diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp index 7c5841810b999c8570c6387b3ab12124a281baf1..e917623cb0df42df40582dbd74b5fd3da6ff5d03 100644 --- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp +++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp @@ -54,86 +54,136 @@ QT_BEGIN_NAMESPACE void qt_image_boxblur(QImage& image, int radius, bool quality); -static QImage makeShadowImage(const QImage& image, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) -{ - QImage shadowImg(image.width() + blur + qAbs(offsetX), - image.height() + blur + qAbs(offsetY), - QImage::Format_ARGB32_Premultiplied); - shadowImg.fill(0); - QPainter tmpPainter(&shadowImg); - tmpPainter.setCompositionMode(QPainter::CompositionMode_Source); - qreal shadowX = offsetX > 0? offsetX : 0; - qreal shadowY = offsetY > 0? offsetY : 0; - - tmpPainter.drawImage(shadowX, shadowY, image); - tmpPainter.end(); - - if (blur > 0) - qt_image_boxblur(shadowImg, blur/2, true); - - // blacken the image with shadow color... - tmpPainter.begin(&shadowImg); - tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); - tmpPainter.fillRect(shadowImg.rect(), color); - tmpPainter.end(); - return shadowImg; -} +namespace { + class ShadowImageMaker + { + public: + virtual ~ShadowImageMaker() {} -static void fillRectShadow(QPainter* p, QRectF shadowRect, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) -{ - QRectF r = shadowRect; - r.moveTo(0, 0); + void paintShapeAndShadow(QPainter *p, qreal offsetX, qreal offsetY, qreal blur, const QColor &color) + { + QRectF bounds = boundingRect().translated(offsetX, offsetY).adjusted(-2*blur, -2*blur, 2*blur, 2*blur); + QRect boundsAligned = bounds.toAlignedRect(); + + QImage shadowImage(boundsAligned.size(), QImage::Format_ARGB32_Premultiplied); + shadowImage.fill(0); + + QPainter shadowPainter(&shadowImage); + shadowPainter.setRenderHints(p->renderHints()); + shadowPainter.translate(offsetX - boundsAligned.left(), offsetY - boundsAligned.top()); + paint(&shadowPainter); + shadowPainter.end(); + + if (blur > 0) + qt_image_boxblur(shadowImage, blur/2, true); + + // blacken the image with shadow color... + shadowPainter.begin(&shadowImage); + shadowPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + shadowPainter.fillRect(shadowImage.rect(), color); + shadowPainter.end(); + + p->drawImage(boundsAligned.topLeft(), shadowImage); + paint(p); + } - QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32_Premultiplied); - QPainter tp; - tp.begin(&shadowImage); - tp.fillRect(r, p->brush()); - tp.end(); - shadowImage = makeShadowImage(shadowImage, offsetX, offsetY, blur, color); + virtual void paint(QPainter *p) const = 0; + virtual QRectF boundingRect() const = 0; + }; - qreal dx = shadowRect.left() + (offsetX < 0? offsetX:0); - qreal dy = shadowRect.top() + (offsetY < 0? offsetY:0); + class FillRectShadow : public ShadowImageMaker + { + public: + FillRectShadow(const QRectF &rect, const QBrush &brush) + : m_rect(rect.normalized()) + , m_brush(brush) + { + } + + void paint(QPainter *p) const { p->fillRect(m_rect, m_brush); } + QRectF boundingRect() const { return m_rect; } - p->drawImage(dx, dy, shadowImage); - p->fillRect(shadowRect, p->brush()); + private: + QRectF m_rect; + QBrush m_brush; + }; + + class FillPathShadow : public ShadowImageMaker + { + public: + FillPathShadow(const QPainterPath &path, const QBrush &brush) + : m_path(path) + , m_brush(brush) + { + } + + void paint(QPainter *p) const { p->fillPath(m_path, m_brush); } + QRectF boundingRect() const { return m_path.boundingRect(); } + + private: + QPainterPath m_path; + QBrush m_brush; + }; + + class StrokePathShadow : public ShadowImageMaker + { + public: + StrokePathShadow(const QPainterPath &path, const QPen &pen) + : m_path(path) + , m_pen(pen) + { + } + + void paint(QPainter *p) const { p->strokePath(m_path, m_pen); } + + QRectF boundingRect() const + { + qreal d = qMax(qreal(1), m_pen.widthF()); + return m_path.boundingRect().adjusted(-d, -d, d, d); + } + + private: + QPainterPath m_path; + QPen m_pen; + }; + + class DrawImageShadow : public ShadowImageMaker + { + public: + DrawImageShadow(const QImage &image, const QPointF &offset) + : m_image(image) + , m_offset(offset) + { + } + + void paint(QPainter *p) const { p->drawImage(m_offset, m_image); } + + QRectF boundingRect() const { return QRectF(m_image.rect()).translated(m_offset); } + + private: + QImage m_image; + QPointF m_offset; + }; +} + +static void fillRectShadow(QPainter* p, QRectF shadowRect, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) +{ + FillRectShadow shadowMaker(shadowRect, p->brush()); + shadowMaker.paintShapeAndShadow(p, offsetX, offsetY, blur, color); } static void fillShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) { - QRectF r = path.boundingRect(); - QImage img(r.size().width() + r.left() + 1, - r.size().height() + r.top() + 1, - QImage::Format_ARGB32_Premultiplied); - img.fill(0); - QPainter tp(&img); - tp.fillPath(path.translated(0, 0), p->brush()); - tp.end(); - - QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color); - qreal dx = r.left() + (offsetX < 0? offsetX:0); - qreal dy = r.top() + (offsetY < 0? offsetY:0); - - p->drawImage(dx, dy, shadowImage); - p->fillPath(path, p->brush()); + FillPathShadow shadowMaker(path, p->brush()); + shadowMaker.paintShapeAndShadow(p, offsetX, offsetY, blur, color); } static void strokeShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) { - QRectF r = path.boundingRect(); - QImage img(r.size().width() + r.left() + 1, - r.size().height() + r.top() + 1, - QImage::Format_ARGB32_Premultiplied); - img.fill(0); - QPainter tp(&img); - tp.strokePath(path, p->pen()); - tp.end(); - - QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color); - qreal dx = r.left() + (offsetX < 0? offsetX:0); - qreal dy = r.top() + (offsetY < 0? offsetY:0); - p->drawImage(dx, dy, shadowImage); - p->strokePath(path, p->pen()); + StrokePathShadow shadowMaker(path, p->pen()); + shadowMaker.paintShapeAndShadow(p, offsetX, offsetY, blur, color); } + static inline void drawRepeatPattern(QPainter* p, const QImage& image, const QRectF& rect, const bool repeatX, const bool repeatY) { // Patterns must be painted so that the top left of the first image is anchored at @@ -259,15 +309,16 @@ static void qt_drawImage(QPainter *p, QQuickContext2D::State& state, QImage imag if (sw != dw || sh != dh) image = image.scaled(dw, dh); - if (shadow) { - QImage shadow = makeShadowImage(image, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); - qreal shadow_dx = dx + (state.shadowOffsetX < 0? state.shadowOffsetY:0); - qreal shadow_dy = dy + (state.shadowOffsetX < 0? state.shadowOffsetY:0); - p->drawImage(shadow_dx, shadow_dy, shadow); - } //Strange OpenGL painting behavior here, without beginNativePainting/endNativePainting, only the first image is painted. p->beginNativePainting(); - p->drawImage(dx, dy, image); + + if (shadow) { + DrawImageShadow shadowMaker(image, QPointF(dx, dy)); + shadowMaker.paintShapeAndShadow(p, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); + } else { + p->drawImage(dx, dy, image); + } + p->endNativePainting(); }