1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickcontext2dcommandbuffer_p.h"
43 #include "qquickcanvasitem_p.h"
45 #include <QtCore/QMutex>
47 #define HAS_SHADOW(offsetX, offsetY, blur, color) (color.isValid() && color.alpha() && (blur || offsetX || offsetY))
51 void qt_image_boxblur(QImage& image, int radius, bool quality);
53 static QImage makeShadowImage(const QImage& image, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
55 QImage shadowImg(image.width() + blur + qAbs(offsetX),
56 image.height() + blur + qAbs(offsetY),
57 QImage::Format_ARGB32_Premultiplied);
59 QPainter tmpPainter(&shadowImg);
60 tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
61 qreal shadowX = offsetX > 0? offsetX : 0;
62 qreal shadowY = offsetY > 0? offsetY : 0;
64 tmpPainter.drawImage(shadowX, shadowY, image);
68 qt_image_boxblur(shadowImg, blur/2, true);
70 // blacken the image with shadow color...
71 tmpPainter.begin(&shadowImg);
72 tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
73 tmpPainter.fillRect(shadowImg.rect(), color);
78 static void fillRectShadow(QPainter* p, QRectF shadowRect, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
80 QRectF r = shadowRect;
83 QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32_Premultiplied);
85 tp.begin(&shadowImage);
86 tp.fillRect(r, p->brush());
88 shadowImage = makeShadowImage(shadowImage, offsetX, offsetY, blur, color);
90 qreal dx = shadowRect.left() + (offsetX < 0? offsetX:0);
91 qreal dy = shadowRect.top() + (offsetY < 0? offsetY:0);
93 p->drawImage(dx, dy, shadowImage);
94 p->fillRect(shadowRect, p->brush());
97 static void fillShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
99 QRectF r = path.boundingRect();
100 QImage img(r.size().width() + r.left() + 1,
101 r.size().height() + r.top() + 1,
102 QImage::Format_ARGB32_Premultiplied);
105 tp.fillPath(path.translated(0, 0), p->brush());
108 QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color);
109 qreal dx = r.left() + (offsetX < 0? offsetX:0);
110 qreal dy = r.top() + (offsetY < 0? offsetY:0);
112 p->drawImage(dx, dy, shadowImage);
113 p->fillPath(path, p->brush());
116 static void strokeShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
118 QRectF r = path.boundingRect();
119 QImage img(r.size().width() + r.left() + 1,
120 r.size().height() + r.top() + 1,
121 QImage::Format_ARGB32_Premultiplied);
124 tp.strokePath(path, p->pen());
127 QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color);
128 qreal dx = r.left() + (offsetX < 0? offsetX:0);
129 qreal dy = r.top() + (offsetY < 0? offsetY:0);
130 p->drawImage(dx, dy, shadowImage);
131 p->strokePath(path, p->pen());
133 static inline void drawRepeatPattern(QPainter* p, const QImage& image, const QRectF& rect, const bool repeatX, const bool repeatY)
135 // Patterns must be painted so that the top left of the first image is anchored at
136 // the origin of the coordinate space
137 if (!image.isNull()) {
138 int w = image.width();
139 int h = image.height();
141 QRect r(static_cast<int>(rect.x()), static_cast<int>(rect.y()), static_cast<int>(rect.width()), static_cast<int>(rect.height()));
143 // startX, startY is the coordinate of the first image we need to put on the left-top of the rect
144 if (repeatX && repeatY) {
146 // startX, startY is at the left top side of the left-top of the rect
147 startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
148 startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
150 if (!repeatX && !repeatY) {
152 // only draw the image once at orgin once, check if need to draw
153 QRect imageRect(0, 0, w, h);
154 if (imageRect.intersects(r)) {
159 } else if (repeatX && !repeatY) {
161 // startY is fixed, but startX change based on the left-top of the rect
162 QRect imageRect(r.x(), 0, r.width(), h);
163 if (imageRect.intersects(r)) {
164 startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
170 // startX is fixed, but startY change based on the left-top of the rect
171 QRect imageRect(0, r.y(), w, r.height());
172 if (imageRect.intersects(r)) {
174 startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
186 QRect imageRect(x, y, w, h);
187 QRect intersectRect = imageRect.intersected(r);
188 QPoint destStart(intersectRect.x(), intersectRect.y());
189 QRect sourceRect(intersectRect.x() - imageRect.x(), intersectRect.y() - imageRect.y(), intersectRect.width(), intersectRect.height());
191 p->drawImage(destStart, image, sourceRect);
193 } while (repeatX && x < r.x() + r.width());
196 } while (repeatY && y < r.y() + r.height());
200 QPen QQuickContext2DCommandBuffer::makePen(const QQuickContext2D::State& state)
203 pen.setWidthF(state.lineWidth);
204 pen.setCapStyle(state.lineCap);
205 pen.setJoinStyle(state.lineJoin);
206 pen.setMiterLimit(state.miterLimit);
207 pen.setBrush(state.strokeStyle);
211 void QQuickContext2DCommandBuffer::setPainterState(QPainter* p, const QQuickContext2D::State& state, const QPen& pen)
213 p->setTransform(state.matrix * p->transform());
218 if (state.fillStyle != p->brush())
219 p->setBrush(state.fillStyle);
221 if (state.font != p->font())
222 p->setFont(state.font);
224 if (state.globalAlpha != p->opacity()) {
225 p->setOpacity(state.globalAlpha);
228 if (state.globalCompositeOperation != p->compositionMode())
229 p->setCompositionMode(state.globalCompositeOperation);
232 void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& state)
239 QTransform originMatrix = p->worldTransform();
241 QPen pen = makePen(state);
242 setPainterState(p, state, pen);
245 QQuickContext2D::PaintCommand cmd = takeNextCommand();
247 case QQuickContext2D::UpdateMatrix:
249 state.matrix = takeMatrix();
250 p->setWorldTransform(state.matrix * originMatrix);
253 case QQuickContext2D::ClearRect:
255 QPainter::CompositionMode cm = p->compositionMode();
256 qreal alpha = p->opacity();
257 p->setCompositionMode(QPainter::CompositionMode_Source);
259 p->fillRect(takeRect(), QColor(qRgba(0, 0, 0, 0)));
260 p->setCompositionMode(cm);
261 p->setOpacity(alpha);
264 case QQuickContext2D::FillRect:
266 QRectF r = takeRect();
267 if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
268 fillRectShadow(p, r, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
270 p->fillRect(r, p->brush());
273 case QQuickContext2D::ShadowColor:
275 state.shadowColor = takeColor();
278 case QQuickContext2D::ShadowBlur:
280 state.shadowBlur = takeShadowBlur();
283 case QQuickContext2D::ShadowOffsetX:
285 state.shadowOffsetX = takeShadowOffsetX();
288 case QQuickContext2D::ShadowOffsetY:
290 state.shadowOffsetY = takeShadowOffsetY();
293 case QQuickContext2D::FillStyle:
295 state.fillStyle = takeFillStyle();
296 state.fillPatternRepeatX = takeBool();
297 state.fillPatternRepeatY = takeBool();
298 p->setBrush(state.fillStyle);
301 case QQuickContext2D::StrokeStyle:
303 state.strokeStyle = takeStrokeStyle();
304 state.strokePatternRepeatX = takeBool();
305 state.strokePatternRepeatY = takeBool();
306 QPen nPen = p->pen();
307 nPen.setBrush(state.strokeStyle);
311 case QQuickContext2D::LineWidth:
313 state.lineWidth = takeLineWidth();
314 QPen nPen = p->pen();
316 nPen.setWidthF(state.lineWidth);
320 case QQuickContext2D::LineCap:
322 state.lineCap = takeLineCap();
323 QPen nPen = p->pen();
324 nPen.setCapStyle(state.lineCap);
328 case QQuickContext2D::LineJoin:
330 state.lineJoin = takeLineJoin();
331 QPen nPen = p->pen();
332 nPen.setJoinStyle(state.lineJoin);
336 case QQuickContext2D::MiterLimit:
338 state.miterLimit = takeMiterLimit();
339 QPen nPen = p->pen();
340 nPen.setMiterLimit(state.miterLimit);
344 case QQuickContext2D::TextAlign:
345 case QQuickContext2D::TextBaseline:
347 case QQuickContext2D::Fill:
349 QPainterPath path = takePath();
351 if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
352 fillShadowPath(p,path, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
354 p->fillPath(path, p->brush());
357 case QQuickContext2D::Stroke:
359 if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
360 strokeShadowPath(p,takePath(), state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
362 p->strokePath(takePath(), p->pen());
365 case QQuickContext2D::Clip:
367 state.clipPath = takePath();
368 p->setClipping(true);
369 p->setClipPath(state.clipPath);
372 case QQuickContext2D::GlobalAlpha:
374 state.globalAlpha = takeGlobalAlpha();
375 p->setOpacity(state.globalAlpha);
378 case QQuickContext2D::GlobalCompositeOperation:
380 state.globalCompositeOperation = takeGlobalCompositeOperation();
381 p->setCompositionMode(state.globalCompositeOperation);
384 case QQuickContext2D::DrawImage:
386 qreal sx = takeReal();
387 qreal sy = takeReal();
388 qreal sw = takeReal();
389 qreal sh = takeReal();
390 qreal dx = takeReal();
391 qreal dy = takeReal();
392 qreal dw = takeReal();
393 qreal dh = takeReal();
394 QImage image = takeImage();
396 if (!image.isNull()) {
397 if (sw == -1 || sh == -1) {
401 if (sx != 0 || sy != 0 || sw != image.width() || sh != image.height())
402 image = image.copy(sx, sy, sw, sh);
404 image = image.scaled(dw, dh);
406 if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) {
407 QImage shadow = makeShadowImage(image, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
408 qreal shadow_dx = dx + (state.shadowOffsetX < 0? state.shadowOffsetY:0);
409 qreal shadow_dy = dy + (state.shadowOffsetX < 0? state.shadowOffsetY:0);
410 p->drawImage(shadow_dx, shadow_dy, shadow);
412 p->drawImage(dx, dy, image);
416 case QQuickContext2D::GetImageData:
429 QQuickContext2DCommandBuffer::QQuickContext2DCommandBuffer()
443 QQuickContext2DCommandBuffer::~QQuickContext2DCommandBuffer()
447 void QQuickContext2DCommandBuffer::clear()
461 void QQuickContext2DCommandBuffer::reset()