GraphicsContextQt.cpp [plain text]
#include "config.h"
#include "GraphicsContext.h"
#ifdef Q_WS_WIN
#include <windows.h>
#endif
#include "AffineTransform.h"
#include "Color.h"
#include "FloatConversion.h"
#include "Font.h"
#include "ImageBuffer.h"
#include "NotImplemented.h"
#include "Path.h"
#include "Pattern.h"
#include "ShadowBlur.h"
#include "TransparencyLayer.h"
#include <QBrush>
#include <QGradient>
#include <QPaintDevice>
#include <QPaintEngine>
#include <QPainter>
#include <QPainterPath>
#include <QPixmap>
#include <QPolygonF>
#include <QStack>
#include <QVector>
#include <wtf/MathExtras.h>
namespace WebCore {
static inline QPainter::CompositionMode toQtCompositionMode(CompositeOperator op)
{
switch (op) {
case CompositeClear:
return QPainter::CompositionMode_Clear;
case CompositeCopy:
return QPainter::CompositionMode_Source;
case CompositeSourceOver:
return QPainter::CompositionMode_SourceOver;
case CompositeSourceIn:
return QPainter::CompositionMode_SourceIn;
case CompositeSourceOut:
return QPainter::CompositionMode_SourceOut;
case CompositeSourceAtop:
return QPainter::CompositionMode_SourceAtop;
case CompositeDestinationOver:
return QPainter::CompositionMode_DestinationOver;
case CompositeDestinationIn:
return QPainter::CompositionMode_DestinationIn;
case CompositeDestinationOut:
return QPainter::CompositionMode_DestinationOut;
case CompositeDestinationAtop:
return QPainter::CompositionMode_DestinationAtop;
case CompositeXOR:
return QPainter::CompositionMode_Xor;
case CompositePlusDarker:
return QPainter::CompositionMode_Darken;
case CompositePlusLighter:
return QPainter::CompositionMode_Plus;
default:
ASSERT_NOT_REACHED();
}
return QPainter::CompositionMode_SourceOver;
}
static inline Qt::PenCapStyle toQtLineCap(LineCap lc)
{
switch (lc) {
case ButtCap:
return Qt::FlatCap;
case RoundCap:
return Qt::RoundCap;
case SquareCap:
return Qt::SquareCap;
default:
ASSERT_NOT_REACHED();
}
return Qt::FlatCap;
}
static inline Qt::PenJoinStyle toQtLineJoin(LineJoin lj)
{
switch (lj) {
case MiterJoin:
return Qt::SvgMiterJoin;
case RoundJoin:
return Qt::RoundJoin;
case BevelJoin:
return Qt::BevelJoin;
default:
ASSERT_NOT_REACHED();
}
return Qt::SvgMiterJoin;
}
static Qt::PenStyle toQPenStyle(StrokeStyle style)
{
switch (style) {
case NoStroke:
return Qt::NoPen;
break;
case SolidStroke:
return Qt::SolidLine;
break;
case DottedStroke:
return Qt::DotLine;
break;
case DashedStroke:
return Qt::DashLine;
break;
default:
ASSERT_NOT_REACHED();
}
return Qt::NoPen;
}
static inline Qt::FillRule toQtFillRule(WindRule rule)
{
switch (rule) {
case RULE_EVENODD:
return Qt::OddEvenFill;
case RULE_NONZERO:
return Qt::WindingFill;
default:
ASSERT_NOT_REACHED();
}
return Qt::OddEvenFill;
}
class GraphicsContextPlatformPrivate {
WTF_MAKE_NONCOPYABLE(GraphicsContextPlatformPrivate); WTF_MAKE_FAST_ALLOCATED;
public:
GraphicsContextPlatformPrivate(QPainter*, const QColor& initialSolidColor);
~GraphicsContextPlatformPrivate();
inline QPainter* p() const
{
if (layers.isEmpty())
return painter;
return &layers.top()->painter;
}
bool antiAliasingForRectsAndLines;
QStack<TransparencyLayer*> layers;
int layerCount;
QBrush solidColor;
InterpolationQuality imageInterpolationQuality;
bool initialSmoothPixmapTransformHint;
ShadowBlur* shadow;
QRectF clipBoundingRect() const
{
#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
return p()->clipBoundingRect();
#else
return p()->clipRegion().boundingRect();
#endif
}
void takeOwnershipOfPlatformContext() { platformContextIsOwned = true; }
private:
QPainter* painter;
bool platformContextIsOwned;
};
GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p, const QColor& initialSolidColor)
: antiAliasingForRectsAndLines(false)
, layerCount(0)
, solidColor(initialSolidColor)
, imageInterpolationQuality(InterpolationDefault)
, initialSmoothPixmapTransformHint(false)
, shadow(new ShadowBlur())
, painter(p)
, platformContextIsOwned(false)
{
if (!painter)
return;
antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing);
initialSmoothPixmapTransformHint = painter->testRenderHint(QPainter::SmoothPixmapTransform);
painter->setRenderHint(QPainter::Antialiasing, true);
}
GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
{
if (!platformContextIsOwned)
return;
QPaintDevice* device = painter->device();
painter->end();
delete shadow;
delete painter;
delete device;
}
void GraphicsContext::platformInit(PlatformGraphicsContext* painter)
{
m_data = new GraphicsContextPlatformPrivate(painter, fillColor());
setPaintingDisabled(!painter);
if (!painter)
return;
painter->setBrush(m_data->solidColor);
QPen pen(painter->pen());
pen.setColor(strokeColor());
pen.setJoinStyle(toQtLineJoin(MiterJoin));
painter->setPen(pen);
}
void GraphicsContext::platformDestroy()
{
while (!m_data->layers.isEmpty())
endTransparencyLayer();
delete m_data;
}
PlatformGraphicsContext* GraphicsContext::platformContext() const
{
return m_data->p();
}
AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const
{
if (paintingDisabled())
return AffineTransform();
const QTransform& matrix = platformContext()->combinedTransform();
return AffineTransform(matrix.m11(), matrix.m12(), matrix.m21(),
matrix.m22(), matrix.dx(), matrix.dy());
}
void GraphicsContext::savePlatformState()
{
if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
++m_data->layers.top()->saveCounter;
m_data->p()->save();
}
void GraphicsContext::restorePlatformState()
{
if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
if (!--m_data->layers.top()->saveCounter)
endPlatformTransparencyLayer();
m_data->p()->restore();
m_data->shadow->setShadowValues(FloatSize(m_state.shadowBlur, m_state.shadowBlur), m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace, m_state.shadowsIgnoreTransforms);
}
void GraphicsContext::drawRect(const IntRect& rect)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
p->drawRect(rect);
p->setRenderHint(QPainter::Antialiasing, antiAlias);
}
void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
{
if (paintingDisabled())
return;
StrokeStyle style = strokeStyle();
Color color = strokeColor();
if (style == NoStroke)
return;
float width = strokeThickness();
FloatPoint p1 = point1;
FloatPoint p2 = point2;
bool isVerticalLine = (p1.x() == p2.x());
QPainter* p = m_data->p();
const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
adjustLineToPixelBoundaries(p1, p2, width, style);
int patWidth = 0;
switch (style) {
case NoStroke:
case SolidStroke:
break;
case DottedStroke:
patWidth = static_cast<int>(width);
break;
case DashedStroke:
patWidth = 3 * static_cast<int>(width);
break;
}
if (patWidth) {
p->save();
if (isVerticalLine) {
p->fillRect(FloatRect(p1.x() - width / 2, p1.y() - width, width, width), QColor(color));
p->fillRect(FloatRect(p2.x() - width / 2, p2.y(), width, width), QColor(color));
} else {
p->fillRect(FloatRect(p1.x() - width, p1.y() - width / 2, width, width), QColor(color));
p->fillRect(FloatRect(p2.x(), p2.y() - width / 2, width, width), QColor(color));
}
int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
int remainder = distance % patWidth;
int coverage = distance - remainder;
int numSegments = coverage / patWidth;
float patternOffset = 0.0f;
if (patWidth == 1)
patternOffset = 1.0f;
else {
bool evenNumberOfSegments = !(numSegments % 2);
if (remainder)
evenNumberOfSegments = !evenNumberOfSegments;
if (evenNumberOfSegments) {
if (remainder) {
patternOffset += patWidth - remainder;
patternOffset += remainder / 2;
} else
patternOffset = patWidth / 2;
} else {
if (remainder)
patternOffset = (patWidth - remainder) / 2;
}
}
QVector<qreal> dashes;
dashes << qreal(patWidth) / width << qreal(patWidth) / width;
QPen pen = p->pen();
pen.setWidthF(width);
pen.setCapStyle(Qt::FlatCap);
pen.setDashPattern(dashes);
pen.setDashOffset(patternOffset / width);
p->setPen(pen);
}
p->drawLine(p1, p2);
if (patWidth)
p->restore();
p->setRenderHint(QPainter::Antialiasing, antiAlias);
}
void GraphicsContext::drawEllipse(const IntRect& rect)
{
if (paintingDisabled())
return;
m_data->p()->drawEllipse(rect);
}
void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
{
if (paintingDisabled())
return;
if (npoints <= 1)
return;
QPolygonF polygon(npoints);
for (size_t i = 0; i < npoints; i++)
polygon[i] = points[i];
QPainter* p = m_data->p();
const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::Antialiasing, shouldAntialias);
p->drawConvexPolygon(polygon);
p->setRenderHint(QPainter::Antialiasing, antiAlias);
}
void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
{
if (paintingDisabled())
return;
if (numPoints <= 1)
return;
QPainterPath path(points[0]);
for (size_t i = 1; i < numPoints; ++i)
path.lineTo(points[i]);
path.setFillRule(Qt::WindingFill);
QPainter* p = m_data->p();
bool painterWasAntialiased = p->testRenderHint(QPainter::Antialiasing);
if (painterWasAntialiased != antialiased)
p->setRenderHint(QPainter::Antialiasing, antialiased);
p->setClipPath(path, Qt::IntersectClip);
if (painterWasAntialiased != antialiased)
p->setRenderHint(QPainter::Antialiasing, painterWasAntialiased);
}
void GraphicsContext::fillPath(const Path& path)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPainterPath platformPath = path.platformPath();
platformPath.setFillRule(toQtFillRule(fillRule()));
if (hasShadow()) {
ShadowBlur* shadow = shadowBlur();
if (shadow->mustUseShadowBlur(this) || m_state.fillPattern || m_state.fillGradient)
{
GraphicsContext* shadowContext = shadow->beginShadowLayer(this, platformPath.controlPointRect());
if (shadowContext) {
QPainter* shadowPainter = shadowContext->platformContext();
if (m_state.fillPattern) {
AffineTransform affine;
shadowPainter->fillPath(platformPath, QBrush(m_state.fillPattern->createPlatformPattern(affine)));
} else if (m_state.fillGradient) {
QBrush brush(*m_state.fillGradient->platformGradient());
brush.setTransform(m_state.fillGradient->gradientSpaceTransform());
shadowPainter->fillPath(platformPath, brush);
} else {
shadowPainter->fillPath(platformPath, p->brush());
}
shadow->endShadowLayer(this);
}
} else {
QPointF offset(m_state.shadowOffset.width(), m_state.shadowOffset.height());
p->translate(offset);
QColor shadowColor = m_state.shadowColor;
shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
p->fillPath(platformPath, shadowColor);
p->translate(-offset);
}
}
if (m_state.fillPattern) {
AffineTransform affine;
p->fillPath(platformPath, QBrush(m_state.fillPattern->createPlatformPattern(affine)));
} else if (m_state.fillGradient) {
QBrush brush(*m_state.fillGradient->platformGradient());
brush.setTransform(m_state.fillGradient->gradientSpaceTransform());
p->fillPath(platformPath, brush);
} else
p->fillPath(platformPath, p->brush());
}
void GraphicsContext::strokePath(const Path& path)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPen pen(p->pen());
QPainterPath platformPath = path.platformPath();
platformPath.setFillRule(toQtFillRule(fillRule()));
if (hasShadow()) {
ShadowBlur* shadow = shadowBlur();
if (shadow->mustUseShadowBlur(this) || m_state.strokePattern || m_state.strokeGradient)
{
FloatRect boundingRect = platformPath.controlPointRect();
boundingRect.inflate(pen.miterLimit() + pen.widthF());
GraphicsContext* shadowContext = shadow->beginShadowLayer(this, boundingRect);
if (shadowContext) {
QPainter* shadowPainter = shadowContext->platformContext();
if (m_state.strokeGradient) {
QBrush brush(*m_state.strokeGradient->platformGradient());
brush.setTransform(m_state.strokeGradient->gradientSpaceTransform());
QPen shadowPen(pen);
shadowPen.setBrush(brush);
shadowPainter->strokePath(platformPath, shadowPen);
} else {
shadowPainter->strokePath(platformPath, pen);
}
shadow->endShadowLayer(this);
}
} else {
QPointF offset(m_state.shadowOffset.width(), m_state.shadowOffset.height());
p->translate(offset);
QColor shadowColor = m_state.shadowColor;
shadowColor.setAlphaF(shadowColor.alphaF() * pen.color().alphaF());
QPen shadowPen(pen);
shadowPen.setColor(shadowColor);
p->strokePath(platformPath, shadowPen);
p->translate(-offset);
}
}
if (m_state.strokePattern) {
AffineTransform affine;
pen.setBrush(QBrush(m_state.strokePattern->createPlatformPattern(affine)));
p->setPen(pen);
p->strokePath(platformPath, pen);
} else if (m_state.strokeGradient) {
QBrush brush(*m_state.strokeGradient->platformGradient());
brush.setTransform(m_state.strokeGradient->gradientSpaceTransform());
pen.setBrush(brush);
p->setPen(pen);
p->strokePath(platformPath, pen);
} else
p->strokePath(platformPath, pen);
}
static inline void drawRepeatPattern(QPainter* p, QPixmap* image, const FloatRect& rect, const bool repeatX, const bool repeatY)
{
if (image) {
int w = image->width();
int h = image->height();
int startX, startY;
QRect r(static_cast<int>(rect.x()), static_cast<int>(rect.y()), static_cast<int>(rect.width()), static_cast<int>(rect.height()));
if (repeatX && repeatY) {
startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
} else {
if (!repeatX && !repeatY) {
QRect imageRect(0, 0, w, h);
if (imageRect.intersects(r)) {
startX = 0;
startY = 0;
} else
return;
} else if (repeatX && !repeatY) {
QRect imageRect(r.x(), 0, r.width(), h);
if (imageRect.intersects(r)) {
startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
startY = 0;
} else
return;
} else {
QRect imageRect(0, r.y(), w, r.height());
if (imageRect.intersects(r)) {
startX = 0;
startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
} else
return;
}
}
int x = startX;
int y = startY;
do {
do {
QRect imageRect(x, y, w, h);
QRect intersectRect = imageRect.intersected(r);
QPoint destStart(intersectRect.x(), intersectRect.y());
QRect sourceRect(intersectRect.x() - imageRect.x(), intersectRect.y() - imageRect.y(), intersectRect.width(), intersectRect.height());
p->drawPixmap(destStart, *image, sourceRect);
x += w;
} while (repeatX && x < r.x() + r.width());
x = startX;
y += h;
} while (repeatY && y < r.y() + r.height());
}
}
void GraphicsContext::fillRect(const FloatRect& rect)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QRectF normalizedRect = rect.normalized();
ShadowBlur* shadow = shadowBlur();
if (m_state.fillPattern) {
QPixmap* image = m_state.fillPattern->tileImage()->nativeImageForCurrentFrame();
GraphicsContext* shadowContext = hasShadow() ? shadow->beginShadowLayer(this, normalizedRect) : 0;
if (shadowContext) {
QPainter* shadowPainter = shadowContext->platformContext();
drawRepeatPattern(shadowPainter, image, normalizedRect, m_state.fillPattern->repeatX(), m_state.fillPattern->repeatY());
shadow->endShadowLayer(this);
}
drawRepeatPattern(p, image, normalizedRect, m_state.fillPattern->repeatX(), m_state.fillPattern->repeatY());
} else if (m_state.fillGradient) {
QBrush brush(*m_state.fillGradient->platformGradient());
brush.setTransform(m_state.fillGradient->gradientSpaceTransform());
GraphicsContext* shadowContext = hasShadow() ? shadow->beginShadowLayer(this, normalizedRect) : 0;
if (shadowContext) {
QPainter* shadowPainter = shadowContext->platformContext();
shadowPainter->fillRect(normalizedRect, brush);
shadow->endShadowLayer(this);
}
p->fillRect(normalizedRect, brush);
} else {
if (hasShadow()) {
if (shadow->mustUseShadowBlur(this)) {
GraphicsContext* shadowContext = shadow->beginShadowLayer(this, normalizedRect);
if (shadowContext) {
QPainter* shadowPainter = shadowContext->platformContext();
shadowPainter->fillRect(normalizedRect, p->brush());
shadow->endShadowLayer(this);
}
} else {
QColor shadowColor = m_state.shadowColor;
shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
p->fillRect(normalizedRect.translated(QPointF(m_state.shadowOffset.width(), m_state.shadowOffset.height())), shadowColor);
}
}
p->fillRect(normalizedRect, p->brush());
}
}
void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
{
if (paintingDisabled() || !color.isValid())
return;
m_data->solidColor.setColor(color);
QPainter* p = m_data->p();
QRectF normalizedRect = rect.normalized();
if (hasShadow()) {
ShadowBlur* shadow = shadowBlur();
if (shadow->mustUseShadowBlur(this)) {
GraphicsContext* shadowContext = shadow->beginShadowLayer(this, normalizedRect);
if (shadowContext) {
QPainter* shadowPainter = shadowContext->platformContext();
shadowPainter->setCompositionMode(QPainter::CompositionMode_Source);
shadowPainter->fillRect(normalizedRect, m_state.shadowColor);
shadow->endShadowLayer(this);
}
} else {
QColor shadowColor = m_state.shadowColor;
shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
p->fillRect(normalizedRect.translated(QPointF(m_state.shadowOffset.width(), m_state.shadowOffset.height())), shadowColor);
}
}
p->fillRect(normalizedRect, m_data->solidColor);
}
void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
{
if (paintingDisabled() || !color.isValid())
return;
Path path;
path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight);
QPainter* p = m_data->p();
if (hasShadow()) {
ShadowBlur* shadow = shadowBlur();
if (shadow->mustUseShadowBlur(this)) {
GraphicsContext* shadowContext = shadow->beginShadowLayer(this, rect);
if (shadowContext) {
QPainter* shadowPainter = shadowContext->platformContext();
shadowPainter->setCompositionMode(QPainter::CompositionMode_Source);
shadowPainter->fillPath(path.platformPath(), QColor(m_state.shadowColor));
shadow->endShadowLayer(this);
}
} else {
const QPointF shadowOffset(m_state.shadowOffset.width(), m_state.shadowOffset.height());
p->translate(shadowOffset);
p->fillPath(path.platformPath(), QColor(m_state.shadowColor));
p->translate(-shadowOffset);
}
}
p->fillPath(path.platformPath(), QColor(color));
}
bool GraphicsContext::isInTransparencyLayer() const
{
return m_data->layerCount;
}
ShadowBlur* GraphicsContext::shadowBlur()
{
return m_data->shadow;
}
void GraphicsContext::clip(const IntRect& rect)
{
if (paintingDisabled())
return;
m_data->p()->setClipRect(rect, Qt::IntersectClip);
}
void GraphicsContext::clip(const FloatRect& rect)
{
if (paintingDisabled())
return;
m_data->p()->setClipRect(rect, Qt::IntersectClip);
}
IntRect GraphicsContext::clipBounds() const
{
QPainter* p = m_data->p();
QRectF clipRect;
if (p->hasClipping())
clipRect = m_data->clipBoundingRect();
else
clipRect = p->transform().inverted().mapRect(p->window());
return enclosingIntRect(clipRect);
}
void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPainterPath platformPath = path.platformPath();
platformPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill);
p->setClipPath(platformPath, Qt::IntersectClip);
}
void drawFocusRingForPath(QPainter* p, const QPainterPath& path, const Color& color, bool antiAliasing)
{
const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::Antialiasing, antiAliasing);
const QPen oldPen = p->pen();
const QBrush oldBrush = p->brush();
QPen nPen = p->pen();
nPen.setColor(color);
p->setBrush(Qt::NoBrush);
nPen.setStyle(Qt::DotLine);
p->strokePath(path, nPen);
p->setBrush(oldBrush);
p->setPen(oldPen);
p->setRenderHint(QPainter::Antialiasing, antiAlias);
}
void GraphicsContext::drawFocusRing(const Path& path, int , int offset, const Color& color)
{
if (paintingDisabled() || !color.isValid())
return;
drawFocusRingForPath(m_data->p(), path.platformPath(), color, m_data->antiAliasingForRectsAndLines);
}
void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
{
if (paintingDisabled() || !color.isValid())
return;
unsigned rectCount = rects.size();
if (!rects.size())
return;
int radius = (width - 1) / 2;
QPainterPath path;
for (unsigned i = 0; i < rectCount; ++i) {
QRect rect = QRect((rects[i])).adjusted(-offset - radius, -offset - radius, offset + radius, offset + radius);
QPainterPath tmpPath;
tmpPath.addRoundedRect(rect, radius, radius);
path = path.united(tmpPath);
}
drawFocusRingForPath(m_data->p(), path, color, m_data->antiAliasingForRectsAndLines);
}
void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool)
{
if (paintingDisabled())
return;
FloatPoint startPoint = origin;
FloatPoint endPoint = origin + FloatSize(width, 0);
#if defined(Q_WS_X11)
QPainter* p = m_data->p();
if (p->paintEngine()->type() == QPaintEngine::X11) {
float strokeWidth = strokeThickness();
if (static_cast<int>(strokeWidth) % 2) {
startPoint.setY(startPoint.y() - 1);
endPoint.setY(endPoint.y() - 1);
}
}
#endif // defined(Q_WS_X11)
drawLine(IntPoint(startPoint.x(), startPoint.y()), IntPoint(endPoint.x(), endPoint.y()));
}
static void drawErrorUnderline(QPainter *painter, qreal x, qreal y, qreal width, qreal height)
{
const qreal heightSquares = 2.5;
qreal square = height / heightSquares;
qreal halfSquare = 0.5 * square;
qreal unitWidth = (heightSquares - 1.0) * square;
int widthUnits = static_cast<int>((width + 0.5 * unitWidth) / unitWidth);
x += 0.5 * (width - widthUnits * unitWidth);
width = widthUnits * unitWidth;
qreal bottom = y + height;
qreal top = y;
QPainterPath path;
path.moveTo(x - halfSquare, top + halfSquare);
int i = 0;
for (i = 0; i < widthUnits; i += 2) {
qreal middle = x + (i + 1) * unitWidth;
qreal right = x + (i + 2) * unitWidth;
path.lineTo(middle, bottom);
if (i + 2 == widthUnits)
path.lineTo(right + halfSquare, top + halfSquare); else if (i + 1 != widthUnits)
path.lineTo(right, top + square); }
for (i -= 2; i >= 0; i -= 2) {
qreal left = x + i * unitWidth;
qreal middle = x + (i + 1) * unitWidth;
qreal right = x + (i + 2) * unitWidth;
if (i + 1 == widthUnits)
path.lineTo(middle + halfSquare, bottom - halfSquare); else {
if (i + 2 == widthUnits)
path.lineTo(right, top);
path.lineTo(middle, bottom - halfSquare); }
path.lineTo(left, top); }
painter->drawPath(path);
}
void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& origin, float width, DocumentMarkerLineStyle style)
{
if (paintingDisabled())
return;
QPainter* painter = platformContext();
const QPen originalPen = painter->pen();
switch (style) {
case DocumentMarkerSpellingLineStyle:
painter->setPen(Qt::red);
break;
case DocumentMarkerGrammarLineStyle:
painter->setPen(Qt::green);
break;
default:
return;
}
drawErrorUnderline(painter, origin.x(), origin.y(), width, cMisspellingLineThickness);
painter->setPen(originalPen);
}
FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode)
{
QPainter* painter = platformContext();
QTransform deviceTransform = painter->deviceTransform();
if (deviceTransform.isIdentity())
return frect;
qreal deviceScaleX = sqrtf(deviceTransform.m11() * deviceTransform.m11() + deviceTransform.m12() * deviceTransform.m12());
qreal deviceScaleY = sqrtf(deviceTransform.m21() * deviceTransform.m21() + deviceTransform.m22() * deviceTransform.m22());
QPoint deviceOrigin(frect.x() * deviceScaleX, frect.y() * deviceScaleY);
QPoint deviceLowerRight(frect.maxX() * deviceScaleX, frect.maxY() * deviceScaleY);
if (deviceOrigin.y() == deviceLowerRight.y() && frect.height())
deviceLowerRight.setY(deviceLowerRight.y() + 1);
if (deviceOrigin.x() == deviceLowerRight.x() && frect.width())
deviceLowerRight.setX(deviceLowerRight.x() + 1);
FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x() / deviceScaleX, deviceOrigin.y() / deviceScaleY);
FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x() / deviceScaleX, deviceLowerRight.y() / deviceScaleY);
return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
}
void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace colorSpace)
{
if (m_state.shadowsIgnoreTransforms) {
m_state.shadowOffset = FloatSize(size.width(), -size.height());
}
m_data->shadow->setShadowValues(FloatSize(m_state.shadowBlur, m_state.shadowBlur), m_state.shadowOffset, color, colorSpace, m_state.shadowsIgnoreTransforms);
}
void GraphicsContext::clearPlatformShadow()
{
m_data->shadow->clear();
}
void GraphicsContext::pushTransparencyLayerInternal(const QRect &rect, qreal opacity, QPixmap& alphaMask)
{
QPainter* p = m_data->p();
QRect deviceClip = p->transform().mapRect(rect);
if (alphaMask.width() != deviceClip.width() || alphaMask.height() != deviceClip.height())
alphaMask = alphaMask.scaled(deviceClip.width(), deviceClip.height());
m_data->layers.push(new TransparencyLayer(p, deviceClip, 1.0, alphaMask));
}
void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
{
if (paintingDisabled())
return;
int x, y, w, h;
x = y = 0;
QPainter* p = m_data->p();
const QPaintDevice* device = p->device();
w = device->width();
h = device->height();
if (p->hasClipping()) {
QRectF clip = m_data->clipBoundingRect();
QRectF deviceClip = p->transform().mapRect(clip);
x = int(qBound(qreal(0), deviceClip.x(), (qreal)w));
y = int(qBound(qreal(0), deviceClip.y(), (qreal)h));
w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2);
h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2);
}
QPixmap emptyAlphaMask;
m_data->layers.push(new TransparencyLayer(p, QRect(x, y, w, h), opacity, emptyAlphaMask));
++m_data->layerCount;
}
void GraphicsContext::endPlatformTransparencyLayer()
{
if (paintingDisabled())
return;
TransparencyLayer* layer = m_data->layers.pop();
if (!layer->alphaMask.isNull()) {
layer->painter.resetTransform();
layer->painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
layer->painter.drawPixmap(QPoint(), layer->alphaMask);
} else
--m_data->layerCount; layer->painter.end();
QPainter* p = m_data->p();
p->save();
p->resetTransform();
p->setOpacity(layer->opacity);
p->drawPixmap(layer->offset, layer->pixmap);
p->restore();
delete layer;
}
bool GraphicsContext::supportsTransparencyLayers()
{
return true;
}
void GraphicsContext::clearRect(const FloatRect& rect)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPainter::CompositionMode currentCompositionMode = p->compositionMode();
p->setCompositionMode(QPainter::CompositionMode_Source);
p->fillRect(rect, Qt::transparent);
p->setCompositionMode(currentCompositionMode);
}
void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
{
if (paintingDisabled())
return;
Path path;
path.addRect(rect);
float previousStrokeThickness = strokeThickness();
if (lineWidth != previousStrokeThickness)
setStrokeThickness(lineWidth);
strokePath(path);
if (lineWidth != previousStrokeThickness)
setStrokeThickness(previousStrokeThickness);
}
void GraphicsContext::setLineCap(LineCap lc)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPen nPen = p->pen();
nPen.setCapStyle(toQtLineCap(lc));
p->setPen(nPen);
}
void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
{
QPainter* p = m_data->p();
QPen pen = p->pen();
unsigned dashLength = dashes.size();
if (dashLength) {
QVector<qreal> pattern;
unsigned count = dashLength;
if (dashLength % 2)
count *= 2;
float penWidth = narrowPrecisionToFloat(double(pen.widthF()));
for (unsigned i = 0; i < count; i++)
pattern.append(dashes[i % dashLength] / penWidth);
pen.setDashPattern(pattern);
pen.setDashOffset(dashOffset / penWidth);
} else
pen.setStyle(Qt::SolidLine);
p->setPen(pen);
}
void GraphicsContext::setLineJoin(LineJoin lj)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPen nPen = p->pen();
nPen.setJoinStyle(toQtLineJoin(lj));
p->setPen(nPen);
}
void GraphicsContext::setMiterLimit(float limit)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPen nPen = p->pen();
nPen.setMiterLimit(limit);
p->setPen(nPen);
}
void GraphicsContext::setAlpha(float opacity)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
p->setOpacity(opacity);
}
void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
{
if (paintingDisabled())
return;
m_data->p()->setCompositionMode(toQtCompositionMode(op));
}
void GraphicsContext::clip(const Path& path)
{
if (paintingDisabled())
return;
QPainterPath clipPath = path.platformPath();
clipPath.setFillRule(Qt::WindingFill);
m_data->p()->setClipPath(clipPath, Qt::IntersectClip);
}
void GraphicsContext::canvasClip(const Path& path)
{
clip(path);
}
void GraphicsContext::clipOut(const Path& path)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPainterPath clippedOut = path.platformPath();
QPainterPath newClip;
newClip.setFillRule(Qt::OddEvenFill);
if (p->hasClipping()) {
newClip.addRect(m_data->clipBoundingRect());
newClip.addPath(clippedOut);
p->setClipPath(newClip, Qt::IntersectClip);
} else {
QRect windowRect = p->transform().inverted().mapRect(p->window());
newClip.addRect(windowRect);
newClip.addPath(clippedOut.intersected(newClip));
p->setClipPath(newClip);
}
}
void GraphicsContext::translate(float x, float y)
{
if (paintingDisabled())
return;
m_data->p()->translate(x, y);
}
void GraphicsContext::rotate(float radians)
{
if (paintingDisabled())
return;
m_data->p()->rotate(rad2deg(qreal(radians)));
}
void GraphicsContext::scale(const FloatSize& s)
{
if (paintingDisabled())
return;
m_data->p()->scale(s.width(), s.height());
}
void GraphicsContext::clipOut(const IntRect& rect)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPainterPath newClip;
newClip.setFillRule(Qt::OddEvenFill);
if (p->hasClipping()) {
newClip.addRect(m_data->clipBoundingRect());
newClip.addRect(QRect(rect));
p->setClipPath(newClip, Qt::IntersectClip);
} else {
QRect clipOutRect(rect);
QRect window = p->transform().inverted().mapRect(p->window());
clipOutRect &= window;
newClip.addRect(window);
newClip.addRect(clipOutRect);
p->setClipPath(newClip);
}
}
void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect,
int thickness)
{
if (paintingDisabled())
return;
clip(rect);
QPainterPath path;
path.addEllipse(QRectF(rect.x(), rect.y(), rect.width(), rect.height()));
path.addEllipse(QRectF(rect.x() + thickness, rect.y() + thickness,
rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
path.setFillRule(Qt::OddEvenFill);
QPainter* p = m_data->p();
const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::Antialiasing, true);
p->setClipPath(path, Qt::IntersectClip);
p->setRenderHint(QPainter::Antialiasing, antiAlias);
}
void GraphicsContext::concatCTM(const AffineTransform& transform)
{
if (paintingDisabled())
return;
m_data->p()->setWorldTransform(transform, true);
}
void GraphicsContext::setCTM(const AffineTransform& transform)
{
if (paintingDisabled())
return;
m_data->p()->setWorldTransform(transform);
}
#if ENABLE(3D_RENDERING) && USE(TEXTURE_MAPPER)
TransformationMatrix GraphicsContext::get3DTransform() const
{
if (paintingDisabled())
return TransformationMatrix();
return platformContext()->combinedTransform();
}
void GraphicsContext::concat3DTransform(const TransformationMatrix& transform)
{
if (paintingDisabled())
return;
m_data->p()->setWorldTransform(transform, true);
}
void GraphicsContext::set3DTransform(const TransformationMatrix& transform)
{
if (paintingDisabled())
return;
m_data->p()->setWorldTransform(transform, false);
}
#endif
void GraphicsContext::setURLForRect(const KURL&, const IntRect&)
{
notImplemented();
}
void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
{
if (paintingDisabled() || !color.isValid())
return;
QPainter* p = m_data->p();
QPen newPen(p->pen());
m_data->solidColor.setColor(color);
newPen.setBrush(m_data->solidColor);
p->setPen(newPen);
}
void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPen newPen(p->pen());
newPen.setStyle(toQPenStyle(strokeStyle));
p->setPen(newPen);
}
void GraphicsContext::setPlatformStrokeThickness(float thickness)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPen newPen(p->pen());
newPen.setWidthF(thickness);
p->setPen(newPen);
}
void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
{
if (paintingDisabled() || !color.isValid())
return;
m_data->solidColor.setColor(color);
m_data->p()->setBrush(m_data->solidColor);
}
void GraphicsContext::setPlatformShouldAntialias(bool enable)
{
if (paintingDisabled())
return;
m_data->p()->setRenderHint(QPainter::Antialiasing, enable);
}
#ifdef Q_WS_WIN
HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
{
Q_ASSERT(mayCreateBitmap);
if (dstRect.isEmpty())
return 0;
BITMAPINFO bitmapInfo;
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = dstRect.width();
bitmapInfo.bmiHeader.biHeight = dstRect.height();
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = 0;
bitmapInfo.bmiHeader.biXPelsPerMeter = 0;
bitmapInfo.bmiHeader.biYPelsPerMeter = 0;
bitmapInfo.bmiHeader.biClrUsed = 0;
bitmapInfo.bmiHeader.biClrImportant = 0;
void* pixels = 0;
HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
if (!bitmap)
return 0;
HDC displayDC = ::GetDC(0);
HDC bitmapDC = ::CreateCompatibleDC(displayDC);
::ReleaseDC(0, displayDC);
::SelectObject(bitmapDC, bitmap);
if (supportAlphaBlend) {
BITMAP bmpInfo;
GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
memset(bmpInfo.bmBits, 0, bufferSize);
}
#if !OS(WINCE)
SetGraphicsMode(bitmapDC, GM_ADVANCED);
XFORM xform;
xform.eM11 = 1.0f;
xform.eM12 = 0.0f;
xform.eM21 = 0.0f;
xform.eM22 = 1.0f;
xform.eDx = -dstRect.x();
xform.eDy = -dstRect.y();
::SetWorldTransform(bitmapDC, &xform);
#endif
return bitmapDC;
}
void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
{
Q_ASSERT(mayCreateBitmap);
if (hdc) {
if (!dstRect.isEmpty()) {
HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
BITMAP info;
GetObject(bitmap, sizeof(info), &info);
ASSERT(info.bmBitsPixel == 32);
QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap, supportAlphaBlend ? QPixmap::PremultipliedAlpha : QPixmap::NoAlpha);
m_data->p()->drawPixmap(dstRect, pixmap);
::DeleteObject(bitmap);
}
::DeleteDC(hdc);
}
}
#endif
void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality)
{
m_data->imageInterpolationQuality = quality;
switch (quality) {
case InterpolationNone:
case InterpolationLow:
m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, false);
break;
case InterpolationMedium:
case InterpolationHigh:
m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, true);
break;
case InterpolationDefault:
default:
m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, m_data->initialSmoothPixmapTransformHint);
break;
};
}
InterpolationQuality GraphicsContext::imageInterpolationQuality() const
{
return m_data->imageInterpolationQuality;
}
void GraphicsContext::takeOwnershipOfPlatformContext()
{
m_data->takeOwnershipOfPlatformContext();
}
}