X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=WebCore%2Fplatform%2Fgraphics%2Fqt%2FGraphicsContextQt.cpp;h=4f68577b74fc56a0c569ab1fe151495806f5aacc;hb=28040489d744e0c5d475a88663056c9040ed5320;hp=105d866ecf4f41c1da30c9c9e1356097455c4dbd;hpb=ec92ec7cd8fe4ad6c8137865ec2d6b43c1a56ee5;p=android-x86%2Fexternal-webkit.git diff --git a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index 105d866ec..4f68577b7 100644 --- a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -8,6 +8,7 @@ * 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 + * Copyright (C) 2010 Sencha, Inc. * * All rights reserved. * @@ -42,6 +43,7 @@ #include "AffineTransform.h" #include "Color.h" +#include "ContextShadow.h" #include "FloatConversion.h" #include "Font.h" #include "GraphicsContextPrivate.h" @@ -50,6 +52,7 @@ #include "Path.h" #include "Pattern.h" #include "Pen.h" +#include "TransparencyLayer.h" #include #include @@ -69,7 +72,7 @@ namespace WebCore { -static inline QPainter::CompositionMode toQtCompositionMode(CompositeOperator op) +QPainter::CompositionMode GraphicsContext::toQtCompositionMode(CompositeOperator op) { switch (op) { case CompositeClear: @@ -166,59 +169,26 @@ static inline Qt::FillRule toQtFillRule(WindRule rule) 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 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; @@ -226,43 +196,71 @@ public: // Only used by SVG for now. QPainterPath currentPath; + ContextShadow shadow; + QStack 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() @@ -288,20 +286,34 @@ AffineTransform GraphicsContext::getCTM() const 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()) @@ -311,22 +323,13 @@ void GraphicsContext::drawRect(const IntRect& rect) 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(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()) @@ -334,7 +337,7 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) StrokeStyle style = strokeStyle(); Color color = strokeColor(); - if (style == NoStroke || !color.alpha()) + if (style == NoStroke) return; float width = strokeThickness(); @@ -348,17 +351,6 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) 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: @@ -409,7 +401,7 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) patternOffset = patWidth / 2; } else { if (remainder) - patternOffset = (patWidth - remainder)/2; + patternOffset = (patWidth - remainder) / 2; } } @@ -443,23 +435,21 @@ void GraphicsContext::drawEllipse(const IntRect& rect) 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(); @@ -485,17 +475,14 @@ void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points 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); @@ -505,25 +492,30 @@ void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points 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() @@ -532,24 +524,25 @@ 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() @@ -559,49 +552,96 @@ 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(rect.x()), static_cast(rect.y()), static_cast(rect.width()), static_cast(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()); } } @@ -611,60 +651,126 @@ void GraphicsContext::fillRect(const FloatRect& rect) 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() @@ -672,6 +778,11 @@ PlatformPath* GraphicsContext::currentPath() return &m_data->currentPath; } +ContextShadow* GraphicsContext::contextShadow() +{ + return &m_data->shadow; +} + void GraphicsContext::clip(const FloatRect& rect) { if (paintingDisabled()) @@ -688,7 +799,7 @@ void GraphicsContext::clipPath(WindRule clipRule) 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& paths, int width, int offset, const Color& color) @@ -703,7 +814,7 @@ void GraphicsContext::drawFocusRing(const Vector& paths, int width, int of */ void GraphicsContext::drawFocusRing(const Vector& rects, int /* width */, int /* offset */, const Color& color) { - if (paintingDisabled()) + if (paintingDisabled() || !color.isValid()) return; unsigned rectCount = rects.size(); @@ -746,11 +857,31 @@ void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool) 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(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; @@ -760,14 +891,33 @@ void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint&, int, b 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 @@ -775,14 +925,21 @@ void GraphicsContext::setPlatformShadow(const IntSize& size, int, const Color&, 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) @@ -797,17 +954,16 @@ 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() @@ -816,6 +972,12 @@ 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(); @@ -882,8 +1044,9 @@ void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) pattern.append(dashes[i % dashLength] / penWidth); pen.setDashPattern(pattern); - pen.setDashOffset(dashOffset); - } + pen.setDashOffset(dashOffset / penWidth); + } else + pen.setStyle(Qt::SolidLine); p->setPen(pen); } @@ -936,7 +1099,12 @@ void GraphicsContext::clip(const Path& path) 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) @@ -949,11 +1117,12 @@ 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); } @@ -973,24 +1142,16 @@ void GraphicsContext::translate(float x, float y) } } -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); } } @@ -1018,12 +1179,12 @@ void GraphicsContext::clipOut(const IntRect& rect) 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); @@ -1031,33 +1192,6 @@ void GraphicsContext::clipOut(const IntRect& rect) } } -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) { @@ -1108,11 +1242,13 @@ void GraphicsContext::setURLForRect(const KURL&, const IntRect&) 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); } @@ -1138,8 +1274,9 @@ void GraphicsContext::setPlatformStrokeThickness(float thickness) 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); } @@ -1242,13 +1379,13 @@ void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality) 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: