From: Samuel Rødal Date: Mon, 29 Apr 2013 13:36:09 +0000 (+0200) Subject: Fixed wrong rendering offset / clipping with shadowBlur in Canvas. X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=030ebca1f74dfb6f858bc4e2c594bd2075ca189c;p=konrad%2Fqtdeclarative.git Fixed wrong rendering offset / clipping with shadowBlur in Canvas. If the item being rendered didn't have a bounding rect starting in the origin the shadow path would be get an additional offset equal to the distance of the item from the origin. Also, the stroke's shadow did not take the pen width into account, causing clipping artifacts. Combine all the common shadow generating code into a single function, and avoid rendering into an intermediate image that is then blitted into a larger image. Task-number: QTBUG-30914 Change-Id: I16c7c15897ab8e2d46f37da835f75f97fe680863 Reviewed-by: Mitch Curtis Reviewed-by: Gunnar Sletta --- diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp index 7c58418..e917623 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(); }