* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
* Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de>
+ * Copyright (C) 2010 Sencha, Inc.
*
* All rights reserved.
*
#include "AffineTransform.h"
#include "Color.h"
+#include "ContextShadow.h"
#include "FloatConversion.h"
#include "Font.h"
#include "GraphicsContextPrivate.h"
#include "Path.h"
#include "Pattern.h"
#include "Pen.h"
+#include "TransparencyLayer.h"
#include <QBrush>
#include <QDebug>
namespace WebCore {
-static inline QPainter::CompositionMode toQtCompositionMode(CompositeOperator op)
+QPainter::CompositionMode GraphicsContext::toQtCompositionMode(CompositeOperator op)
{
switch (op) {
case CompositeClear:
return Qt::OddEvenFill;
}
-struct TransparencyLayer : FastAllocBase {
- TransparencyLayer(const QPainter* p, const QRect &rect)
- : pixmap(rect.width(), rect.height())
- {
- offset = rect.topLeft();
- pixmap.fill(Qt::transparent);
- painter.begin(&pixmap);
- painter.setRenderHint(QPainter::Antialiasing, p->testRenderHint(QPainter::Antialiasing));
- painter.translate(-offset);
- painter.setPen(p->pen());
- painter.setBrush(p->brush());
- painter.setTransform(p->transform(), true);
- painter.setOpacity(p->opacity());
- painter.setFont(p->font());
- if (painter.paintEngine()->hasFeature(QPaintEngine::PorterDuff))
- painter.setCompositionMode(p->compositionMode());
- painter.setClipPath(p->clipPath());
- }
-
- TransparencyLayer()
- {
- }
-
- QPixmap pixmap;
- QPoint offset;
- QPainter painter;
- qreal opacity;
-private:
- TransparencyLayer(const TransparencyLayer &) {}
- TransparencyLayer & operator=(const TransparencyLayer &) { return *this; }
-};
-
class GraphicsContextPlatformPrivate : public Noncopyable {
public:
- GraphicsContextPlatformPrivate(QPainter* painter);
+ GraphicsContextPlatformPrivate(QPainter*, const QColor& initialSolidColor);
~GraphicsContextPlatformPrivate();
- inline QPainter* p()
+ inline QPainter* p() const
{
- if (layers.isEmpty()) {
- if (redirect)
- return redirect;
-
+ if (layers.isEmpty())
return painter;
- }
return &layers.top()->painter;
}
bool antiAliasingForRectsAndLines;
QStack<TransparencyLayer*> layers;
- QPainter* redirect;
+ // Counting real layers. Required by inTransparencyLayer() calls
+ // For example, layers with valid alphaMask are not real layers
+ int layerCount;
+ // reuse this brush for solid color (to prevent expensive QBrush construction)
QBrush solidColor;
InterpolationQuality imageInterpolationQuality;
// Only used by SVG for now.
QPainterPath currentPath;
+ ContextShadow shadow;
+ QStack<ContextShadow> shadowStack;
+
+ bool hasShadow() const
+ {
+ return shadow.m_type != ContextShadow::NoShadow;
+ }
+
+ inline void clearCurrentPath()
+ {
+ if (!currentPath.elementCount())
+ return;
+ currentPath = QPainterPath();
+ }
+
+ QRectF clipBoundingRect() const
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
+ return p()->clipBoundingRect();
+#else
+ return p()->clipRegion().boundingRect();
+#endif
+ }
+
private:
QPainter* painter;
};
-GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p)
+GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p, const QColor& initialSolidColor)
+ : antiAliasingForRectsAndLines(false)
+ , layerCount(0)
+ , solidColor(initialSolidColor)
+ , imageInterpolationQuality(InterpolationDefault)
+ , painter(p)
{
- painter = p;
- redirect = 0;
-
- solidColor = QBrush(Qt::black);
+ if (!painter)
+ return;
- imageInterpolationQuality = InterpolationDefault;
+ // Use the default the QPainter was constructed with.
+ antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing);
- if (painter) {
- // use the default the QPainter was constructed with
- antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing);
- // FIXME: Maybe only enable in SVG mode?
- painter->setRenderHint(QPainter::Antialiasing, true);
- } else
- antiAliasingForRectsAndLines = false;
+ painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, true);
}
GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
{
}
-GraphicsContext::GraphicsContext(PlatformGraphicsContext* context)
+GraphicsContext::GraphicsContext(PlatformGraphicsContext* painter)
: m_common(createGraphicsContextPrivate())
- , m_data(new GraphicsContextPlatformPrivate(context))
+ , m_data(new GraphicsContextPlatformPrivate(painter, fillColor()))
{
- setPaintingDisabled(!context);
- if (context) {
- // Make sure the context starts in sync with our state.
- setPlatformFillColor(fillColor(), DeviceColorSpace);
- setPlatformStrokeColor(strokeColor(), DeviceColorSpace);
- }
+ setPaintingDisabled(!painter);
+
+ if (!painter)
+ return;
+
+ // solidColor is initialized with the fillColor().
+ painter->setBrush(m_data->solidColor);
+
+ QPen pen(painter->pen());
+ pen.setColor(strokeColor());
+ pen.setJoinStyle(toQtLineJoin(MiterJoin));
+ painter->setPen(pen);
}
GraphicsContext::~GraphicsContext()
void GraphicsContext::savePlatformState()
{
+ if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
+ ++m_data->layers.top()->saveCounter;
m_data->p()->save();
+ m_data->shadowStack.push(m_data->shadow);
}
void GraphicsContext::restorePlatformState()
{
+ if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
+ if (!--m_data->layers.top()->saveCounter)
+ endTransparencyLayer();
+
m_data->p()->restore();
if (!m_data->currentPath.isEmpty() && m_common->state.pathTransform.isInvertible()) {
QTransform matrix = m_common->state.pathTransform;
m_data->currentPath = m_data->currentPath * matrix;
}
+
+ if (m_data->shadowStack.isEmpty())
+ m_data->shadow = ContextShadow();
+ else
+ m_data->shadow = m_data->shadowStack.pop();
}
// Draws a filled rectangle with a stroked border.
+// This is only used to draw borders (real fill is done via fillRect), and
+// thus it must not cast any shadow.
void GraphicsContext::drawRect(const IntRect& rect)
{
if (paintingDisabled())
const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
- IntSize shadowSize;
- int shadowBlur;
- Color shadowColor;
- if (getShadow(shadowSize, shadowBlur, shadowColor)) {
- IntRect shadowRect = rect;
- shadowRect.move(shadowSize.width(), shadowSize.height());
- shadowRect.inflate(static_cast<int>(p->pen().widthF()));
- p->fillRect(shadowRect, QColor(shadowColor));
- }
-
p->drawRect(rect);
p->setRenderHint(QPainter::Antialiasing, antiAlias);
}
// This is only used to draw borders.
+// Must not cast any shadow.
void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
{
if (paintingDisabled())
StrokeStyle style = strokeStyle();
Color color = strokeColor();
- if (style == NoStroke || !color.alpha())
+ if (style == NoStroke)
return;
float width = strokeThickness();
p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
adjustLineToPixelBoundaries(p1, p2, width, style);
- IntSize shadowSize;
- int shadowBlur;
- Color shadowColor;
- if (textDrawingMode() == cTextFill && getShadow(shadowSize, shadowBlur, shadowColor)) {
- p->save();
- p->translate(shadowSize.width(), shadowSize.height());
- p->setPen(shadowColor);
- p->drawLine(p1, p2);
- p->restore();
- }
-
int patWidth = 0;
switch (style) {
case NoStroke:
patternOffset = patWidth / 2;
} else {
if (remainder)
- patternOffset = (patWidth - remainder)/2;
+ patternOffset = (patWidth - remainder) / 2;
}
}
void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
{
- if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f || !strokeColor().alpha())
+ if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f)
return;
QPainter* p = m_data->p();
const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::Antialiasing, true);
- IntSize shadowSize;
- int shadowBlur;
- Color shadowColor;
startAngle *= 16;
angleSpan *= 16;
- if (getShadow(shadowSize, shadowBlur, shadowColor)) {
+
+ if (m_data->hasShadow()) {
p->save();
- p->translate(shadowSize.width(), shadowSize.height());
+ p->translate(m_data->shadow.offset());
QPen pen(p->pen());
- pen.setColor(shadowColor);
+ pen.setColor(m_data->shadow.m_color);
p->setPen(pen);
p->drawArc(rect, startAngle, angleSpan);
p->restore();
QPainter* p = m_data->p();
p->save();
p->setRenderHint(QPainter::Antialiasing, shouldAntialias);
- IntSize shadowSize;
- int shadowBlur;
- Color shadowColor;
- if (getShadow(shadowSize, shadowBlur, shadowColor)) {
+ if (m_data->hasShadow()) {
p->save();
- p->translate(shadowSize.width(), shadowSize.height());
+ p->translate(m_data->shadow.offset());
if (p->brush().style() != Qt::NoBrush)
- p->setBrush(QBrush(shadowColor));
+ p->setBrush(QBrush(m_data->shadow.m_color));
QPen pen(p->pen());
if (pen.style() != Qt::NoPen) {
- pen.setColor(shadowColor);
+ pen.setColor(m_data->shadow.m_color);
p->setPen(pen);
}
p->drawConvexPolygon(polygon);
p->restore();
}
-QPen GraphicsContext::pen()
+void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
{
if (paintingDisabled())
- return QPen();
+ 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();
- return p->pen();
-}
-static void inline drawFilledShadowPath(GraphicsContext* context, QPainter* p, const QPainterPath& path)
-{
- IntSize shadowSize;
- int shadowBlur;
- Color shadowColor;
- if (context->getShadow(shadowSize, shadowBlur, shadowColor)) {
- p->translate(shadowSize.width(), shadowSize.height());
- p->fillPath(path, QBrush(shadowColor));
- p->translate(-shadowSize.width(), -shadowSize.height());
- }
+ 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()
return;
QPainter* p = m_data->p();
- QPainterPath path = m_data->currentPath;
+ QPainterPath& path = m_data->currentPath; // Avoid detaching the QPainterPath
path.setFillRule(toQtFillRule(fillRule()));
- if (m_common->state.fillPattern || m_common->state.fillGradient || fillColor().alpha()) {
- drawFilledShadowPath(this, p, path);
- if (m_common->state.fillPattern) {
- AffineTransform affine;
- p->fillPath(path, QBrush(m_common->state.fillPattern->createPlatformPattern(affine)));
- } else if (m_common->state.fillGradient) {
- QBrush brush(*m_common->state.fillGradient->platformGradient());
- brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
- p->fillPath(path, brush);
- } else {
- if (fillColor().alpha())
- p->fillPath(path, p->brush());
- }
+ if (m_data->hasShadow()) {
+ p->translate(m_data->shadow.offset());
+ p->fillPath(path, QColor(m_data->shadow.m_color));
+ p->translate(-m_data->shadow.offset());
}
- m_data->currentPath = QPainterPath();
+ if (m_common->state.fillPattern) {
+ AffineTransform affine;
+ p->fillPath(path, QBrush(m_common->state.fillPattern->createPlatformPattern(affine)));
+ } else if (m_common->state.fillGradient) {
+ QBrush brush(*m_common->state.fillGradient->platformGradient());
+ brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
+ p->fillPath(path, brush);
+ } else
+ p->fillPath(path, p->brush());
+
+ m_data->clearCurrentPath();
}
void GraphicsContext::strokePath()
QPainter* p = m_data->p();
QPen pen(p->pen());
- QPainterPath path = m_data->currentPath;
+ QPainterPath& path = m_data->currentPath; // Avoid detaching the QPainterPath
path.setFillRule(toQtFillRule(fillRule()));
- if (m_common->state.strokePattern || m_common->state.strokeGradient || strokeColor().alpha()) {
- IntSize shadowSize;
- int shadowBlur;
- Color shadowColor;
- if (getShadow(shadowSize, shadowBlur, shadowColor)) {
- QTransform t(p->worldTransform());
- p->translate(shadowSize.width(), shadowSize.height());
- QPen shadowPen(pen);
- shadowPen.setColor(shadowColor);
- p->strokePath(path, shadowPen);
- p->setWorldTransform(t);
- }
- if (m_common->state.strokePattern) {
- AffineTransform affine;
- pen.setBrush(QBrush(m_common->state.strokePattern->createPlatformPattern(affine)));
- p->setPen(pen);
- p->strokePath(path, pen);
- } else if (m_common->state.strokeGradient) {
- QBrush brush(*m_common->state.strokeGradient->platformGradient());
- brush.setTransform(m_common->state.strokeGradient->gradientSpaceTransform());
- pen.setBrush(brush);
- p->setPen(pen);
- p->strokePath(path, pen);
+ if (m_data->hasShadow()) {
+ p->translate(m_data->shadow.offset());
+ QPen shadowPen(pen);
+ shadowPen.setColor(m_data->shadow.m_color);
+ p->strokePath(path, shadowPen);
+ p->translate(-m_data->shadow.offset());
+ }
+ if (m_common->state.strokePattern) {
+ AffineTransform affine;
+ pen.setBrush(QBrush(m_common->state.strokePattern->createPlatformPattern(affine)));
+ p->setPen(pen);
+ p->strokePath(path, pen);
+ } else if (m_common->state.strokeGradient) {
+ QBrush brush(*m_common->state.strokeGradient->platformGradient());
+ brush.setTransform(m_common->state.strokeGradient->gradientSpaceTransform());
+ pen.setBrush(brush);
+ p->setPen(pen);
+ p->strokePath(path, pen);
+ } else
+ p->strokePath(path, pen);
+ m_data->clearCurrentPath();
+}
+
+static inline void drawRepeatPattern(QPainter* p, QPixmap* image, const FloatRect& rect, const bool repeatX, const bool repeatY)
+{
+ // Patterns must be painted so that the top left of the first image is anchored at
+ // the origin of the coordinate space
+ 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()));
+
+ // startX, startY is the coordinate of the first image we need to put on the left-top of the rect
+ if (repeatX && repeatY) {
+ // repeat
+ // startX, startY is at the left top side of the left-top of the rect
+ 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 (strokeColor().alpha())
- p->strokePath(path, pen);
+ if (!repeatX && !repeatY) {
+ // no-repeat
+ // only draw the image once at orgin once, check if need to draw
+ QRect imageRect(0, 0, w, h);
+ if (imageRect.intersects(r)) {
+ startX = 0;
+ startY = 0;
+ } else
+ return;
+ } else if (repeatX && !repeatY) {
+ // repeat-x
+ // startY is fixed, but startX change based on the left-top of the rect
+ 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 {
+ // repeat-y
+ // startX is fixed, but startY change based on the left-top of the rect
+ 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;
+ }
}
- }
- m_data->currentPath = QPainterPath();
-}
-static inline void drawBorderlessRectShadow(GraphicsContext* context, QPainter* p, const FloatRect& rect)
-{
- IntSize shadowSize;
- int shadowBlur;
- Color shadowColor;
- if (context->getShadow(shadowSize, shadowBlur, shadowColor)) {
- FloatRect shadowRect(rect);
- shadowRect.move(shadowSize.width(), shadowSize.height());
- p->fillRect(shadowRect, QColor(shadowColor));
+ int x = startX;
+ int y = startY;
+ do {
+ // repeat Y
+ do {
+ // repeat X
+ 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());
}
}
return;
QPainter* p = m_data->p();
-
- if (m_common->state.fillPattern || m_common->state.fillGradient || fillColor().alpha()) {
- drawBorderlessRectShadow(this, p, rect);
- if (m_common->state.fillPattern) {
- AffineTransform affine;
- p->fillRect(rect, QBrush(m_common->state.fillPattern->createPlatformPattern(affine)));
- } else if (m_common->state.fillGradient) {
- QBrush brush(*m_common->state.fillGradient->platformGradient());
- brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
- p->fillRect(rect, brush);
- } else {
- if (fillColor().alpha())
- p->fillRect(rect, p->brush());
+ QRectF normalizedRect = rect.normalized();
+ ContextShadow* shadow = contextShadow();
+
+ if (m_common->state.fillPattern) {
+ AffineTransform affine;
+ QBrush brush(m_common->state.fillPattern->createPlatformPattern(affine));
+ QPixmap* image = m_common->state.fillPattern->tileImage()->nativeImageForCurrentFrame();
+ QPainter* shadowPainter = m_data->hasShadow() ? shadow->beginShadowLayer(p, normalizedRect) : 0;
+ if (shadowPainter) {
+ drawRepeatPattern(shadowPainter, image, normalizedRect, m_common->state.fillPattern->repeatX(), m_common->state.fillPattern->repeatY());
+ shadowPainter->setCompositionMode(QPainter::CompositionMode_SourceIn);
+ shadowPainter->fillRect(normalizedRect, shadow->m_color);
+ shadow->endShadowLayer(p);
}
+ drawRepeatPattern(p, image, normalizedRect, m_common->state.fillPattern->repeatX(), m_common->state.fillPattern->repeatY());
+ } else if (m_common->state.fillGradient) {
+ QBrush brush(*m_common->state.fillGradient->platformGradient());
+ brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
+ QPainter* shadowPainter = m_data->hasShadow() ? shadow->beginShadowLayer(p, normalizedRect) : 0;
+ if (shadowPainter) {
+ shadowPainter->fillRect(normalizedRect, brush);
+ shadowPainter->setCompositionMode(QPainter::CompositionMode_SourceIn);
+ shadowPainter->fillRect(normalizedRect, shadow->m_color);
+ shadow->endShadowLayer(p);
+ }
+ p->fillRect(normalizedRect, brush);
+ } else {
+ if (m_data->hasShadow()) {
+ if (shadow->m_type == ContextShadow::BlurShadow) {
+ QPainter* shadowPainter = shadow->beginShadowLayer(p, normalizedRect);
+ if (shadowPainter) {
+ shadowPainter->fillRect(normalizedRect, p->brush());
+ shadow->endShadowLayer(p);
+ }
+ } else {
+ // Solid rectangle fill with no blur shadow can be done faster
+ // without using the shadow layer at all.
+ QColor shadowColor = shadow->m_color;
+ shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
+ p->fillRect(normalizedRect.translated(shadow->offset()), shadowColor);
+ }
+ }
+ p->fillRect(normalizedRect, p->brush());
}
}
-void GraphicsContext::fillRect(const FloatRect& rect, const Color& c, ColorSpace colorSpace)
+
+void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
{
- if (paintingDisabled())
+ if (paintingDisabled() || !color.isValid())
return;
- m_data->solidColor.setColor(c);
+ m_data->solidColor.setColor(color);
QPainter* p = m_data->p();
- drawBorderlessRectShadow(this, p, rect);
- p->fillRect(rect, m_data->solidColor);
+ QRectF normalizedRect = rect.normalized();
+
+ if (m_data->hasShadow()) {
+ ContextShadow* shadow = contextShadow();
+
+ if (shadow->m_type != ContextShadow::BlurShadow) {
+ // We do not need any layer for simple shadow.
+ p->fillRect(normalizedRect.translated(shadow->offset()), shadow->m_color);
+ } else {
+ QPainter* shadowPainter = shadow->beginShadowLayer(p, normalizedRect);
+ if (shadowPainter) {
+ shadowPainter->setCompositionMode(QPainter::CompositionMode_Source);
+ shadowPainter->fillRect(normalizedRect, shadow->m_color);
+ shadow->endShadowLayer(p);
+ }
+ }
+ }
+
+ 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.alpha())
+ if (paintingDisabled() || !color.isValid())
return;
- Path path = Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight);
+ Path path;
+ path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight);
QPainter* p = m_data->p();
- drawFilledShadowPath(this, p, path.platformPath());
+ if (m_data->hasShadow()) {
+ ContextShadow* shadow = contextShadow();
+
+ if (shadow->m_type != ContextShadow::BlurShadow) {
+ // We do not need any layer for simple shadow.
+ p->translate(m_data->shadow.offset());
+ p->fillPath(path.platformPath(), QColor(m_data->shadow.m_color));
+ p->translate(-m_data->shadow.offset());
+ } else {
+ QPainter* shadowPainter = shadow->beginShadowLayer(p, rect);
+ if (shadowPainter) {
+ shadowPainter->setCompositionMode(QPainter::CompositionMode_Source);
+ shadowPainter->fillPath(path.platformPath(), QColor(m_data->shadow.m_color));
+ shadow->endShadowLayer(p);
+ }
+ }
+ }
p->fillPath(path.platformPath(), QColor(color));
}
void GraphicsContext::beginPath()
{
- m_data->currentPath = QPainterPath();
+ m_data->clearCurrentPath();
}
void GraphicsContext::addPath(const Path& path)
{
- QPainterPath newPath = m_data->currentPath;
- newPath.addPath(path.platformPath());
- m_data->currentPath = newPath;
+ if (!m_data->currentPath.elementCount()) {
+ m_data->currentPath = path.platformPath();
+ return;
+ }
+ m_data->currentPath.addPath(path.platformPath());
}
bool GraphicsContext::inTransparencyLayer() const
{
- return !m_data->layers.isEmpty();
+ return m_data->layerCount;
}
PlatformPath* GraphicsContext::currentPath()
return &m_data->currentPath;
}
+ContextShadow* GraphicsContext::contextShadow()
+{
+ return &m_data->shadow;
+}
+
void GraphicsContext::clip(const FloatRect& rect)
{
if (paintingDisabled())
QPainter* p = m_data->p();
QPainterPath newPath = m_data->currentPath;
newPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill);
- p->setClipPath(newPath);
+ p->setClipPath(newPath, Qt::IntersectClip);
}
void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
*/
void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color)
{
- if (paintingDisabled())
+ if (paintingDisabled() || !color.isValid())
return;
unsigned rectCount = rects.size();
if (paintingDisabled())
return;
+ IntPoint startPoint = origin;
IntPoint endPoint = origin + IntSize(width, 0);
- drawLine(origin, endPoint);
+
+ // If paintengine type is X11 to avoid artifacts
+ // like bug https://bugs.webkit.org/show_bug.cgi?id=42248
+#if defined(Q_WS_X11)
+ QPainter* p = m_data->p();
+ if (p->paintEngine()->type() == QPaintEngine::X11) {
+ // If stroke thickness is odd we need decrease Y coordinate by 1 pixel,
+ // because inside method adjustLineToPixelBoundaries(...), which
+ // called from drawLine(...), Y coordinate will be increased by 0.5f
+ // and then inside Qt painting engine will be rounded to next greater
+ // integer value.
+ 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(startPoint, endPoint);
}
-void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint&, int, bool)
+void GraphicsContext::drawLineForTextChecking(const IntPoint&, int, TextCheckingLineStyle)
{
if (paintingDisabled())
return;
FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect)
{
- QRectF rect(frect);
- rect = m_data->p()->deviceMatrix().mapRect(rect);
+ // It is not enough just to round to pixels in device space. The rotation part of the
+ // affine transform matrix to device space can mess with this conversion if we have a
+ // rotating image like the hands of the world clock widget. We just need the scale, so
+ // we get the affine transform matrix and extract the scale.
+ 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.right() * deviceScaleX, frect.bottom() * deviceScaleY);
+
+ // Don't let the height or width round to 0 unless either was originally 0
+ if (deviceOrigin.y() == deviceLowerRight.y() && frect.height())
+ deviceLowerRight.setY(deviceLowerRight.y() + 1);
+ if (deviceOrigin.x() == deviceLowerRight.x() && frect.width())
+ deviceLowerRight.setX(deviceLowerRight.x() + 1);
- QRect result = rect.toRect(); //round it
- return FloatRect(QRectF(result));
+ 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 IntSize& size, int, const Color&, ColorSpace)
+void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace)
{
// Qt doesn't support shadows natively, they are drawn manually in the draw*
// functions
if (m_common->state.shadowsIgnoreTransforms) {
// Meaning that this graphics context is associated with a CanvasRenderingContext
// We flip the height since CG and HTML5 Canvas have opposite Y axis
- m_common->state.shadowSize = IntSize(size.width(), -size.height());
+ m_common->state.shadowOffset = FloatSize(size.width(), -size.height());
+ m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), -size.height()));
+ } else {
+ m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), size.height()));
}
}
void GraphicsContext::clearPlatformShadow()
{
- // Qt doesn't support shadows natively, they are drawn manually in the draw*
- // functions
+ m_data->shadow.clear();
+}
+
+void GraphicsContext::pushTransparencyLayerInternal(const QRect &rect, qreal opacity, QPixmap& alphaMask)
+{
+ m_data->layers.push(new TransparencyLayer(m_data->p(), m_data->p()->transform().mapRect(rect), 1.0, alphaMask));
}
void GraphicsContext::beginTransparencyLayer(float opacity)
w = device->width();
h = device->height();
- QRectF clip = p->clipPath().boundingRect();
+ 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);
- TransparencyLayer * layer = new TransparencyLayer(m_data->p(), QRect(x, y, w, h));
-
- layer->opacity = opacity;
- m_data->layers.push(layer);
+ QPixmap emptyAlphaMask;
+ m_data->layers.push(new TransparencyLayer(m_data->p(), QRect(x, y, w, h), opacity, emptyAlphaMask));
+ ++m_data->layerCount;
}
void GraphicsContext::endTransparencyLayer()
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; // see the comment for layerCount
layer->painter.end();
QPainter* p = m_data->p();
pattern.append(dashes[i % dashLength] / penWidth);
pen.setDashPattern(pattern);
- pen.setDashOffset(dashOffset);
- }
+ pen.setDashOffset(dashOffset / penWidth);
+ } else
+ pen.setStyle(Qt::SolidLine);
p->setPen(pen);
}
void GraphicsContext::canvasClip(const Path& path)
{
- clip(path);
+ if (paintingDisabled())
+ return;
+
+ QPainterPath clipPath = path.platformPath();
+ clipPath.setFillRule(Qt::WindingFill);
+ m_data->p()->setClipPath(clipPath, Qt::IntersectClip);
}
void GraphicsContext::clipOut(const Path& path)
QPainterPath newClip;
newClip.setFillRule(Qt::OddEvenFill);
if (p->hasClipping()) {
- newClip.addRect(p->clipRegion().boundingRect());
+ newClip.addRect(m_data->clipBoundingRect());
newClip.addPath(clippedOut);
p->setClipPath(newClip, Qt::IntersectClip);
} else {
- newClip.addRect(p->window());
+ QRect windowRect = p->transform().inverted().mapRect(p->window());
+ newClip.addRect(windowRect);
newClip.addPath(clippedOut.intersected(newClip));
p->setClipPath(newClip);
}
}
}
-IntPoint GraphicsContext::origin()
-{
- if (paintingDisabled())
- return IntPoint();
- const QTransform &transform = m_data->p()->transform();
- return IntPoint(qRound(transform.dx()), qRound(transform.dy()));
-}
-
void GraphicsContext::rotate(float radians)
{
if (paintingDisabled())
return;
- m_data->p()->rotate(180/M_PI*radians);
+ m_data->p()->rotate(180 / M_PI*radians);
if (!m_data->currentPath.isEmpty()) {
QTransform matrix;
- m_data->currentPath = m_data->currentPath * matrix.rotate(-180/M_PI*radians);
+ m_data->currentPath = m_data->currentPath * matrix.rotate(-180 / M_PI*radians);
m_common->state.pathTransform.rotate(radians);
}
}
QPainterPath newClip;
newClip.setFillRule(Qt::OddEvenFill);
if (p->hasClipping()) {
- newClip.addRect(p->clipRegion().boundingRect());
+ newClip.addRect(m_data->clipBoundingRect());
newClip.addRect(QRect(rect));
p->setClipPath(newClip, Qt::IntersectClip);
} else {
QRect clipOutRect(rect);
- QRect window(p->window());
+ QRect window = p->transform().inverted().mapRect(p->window());
clipOutRect &= window;
newClip.addRect(window);
newClip.addRect(clipOutRect);
}
}
-void GraphicsContext::clipOutEllipseInRect(const IntRect& rect)
-{
- if (paintingDisabled())
- return;
-
- QPainter* p = m_data->p();
- QPainterPath newClip;
- newClip.setFillRule(Qt::OddEvenFill);
- if (p->hasClipping()) {
- newClip.addRect(p->clipRegion().boundingRect());
- newClip.addEllipse(QRect(rect));
- p->setClipPath(newClip, Qt::IntersectClip);
- } else {
- QRect clipOutRect(rect);
- QRect window(p->window());
- clipOutRect &= window;
- newClip.addRect(window);
- newClip.addEllipse(clipOutRect);
- p->setClipPath(newClip);
- }
-}
-
-void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*)
-{
- notImplemented();
-}
-
void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect,
int thickness)
{
void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
{
- if (paintingDisabled())
+ if (paintingDisabled() || !color.isValid())
return;
+
QPainter* p = m_data->p();
QPen newPen(p->pen());
- newPen.setColor(color);
+ m_data->solidColor.setColor(color);
+ newPen.setBrush(m_data->solidColor);
p->setPen(newPen);
}
void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
{
- if (paintingDisabled())
+ if (paintingDisabled() || !color.isValid())
return;
+
m_data->solidColor.setColor(color);
m_data->p()->setBrush(m_data->solidColor);
}
m_data->imageInterpolationQuality = quality;
switch (quality) {
- case InterpolationDefault:
case InterpolationNone:
case InterpolationLow:
// use nearest-neigbor
m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, false);
break;
+ case InterpolationDefault:
case InterpolationMedium:
case InterpolationHigh:
default: