Merge remote-tracking branch 'origin/master' into api_changes
[profile/ivi/qtdeclarative.git] / src / quick / items / context2d / qquickcontext2dcommandbuffer.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickcontext2dcommandbuffer_p.h"
43 #include "qquickcanvasitem_p.h"
44 #include <qqml.h>
45 #include <QtCore/QMutex>
46
47 #define HAS_SHADOW(offsetX, offsetY, blur, color) (color.isValid() && color.alpha() && (blur || offsetX || offsetY))
48
49 QT_BEGIN_NAMESPACE
50
51 void qt_image_boxblur(QImage& image, int radius, bool quality);
52
53 static QImage makeShadowImage(const QImage& image, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
54 {
55     QImage shadowImg(image.width() + blur + qAbs(offsetX),
56                      image.height() + blur + qAbs(offsetY),
57                      QImage::Format_ARGB32_Premultiplied);
58     shadowImg.fill(0);
59     QPainter tmpPainter(&shadowImg);
60     tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
61     qreal shadowX = offsetX > 0? offsetX : 0;
62     qreal shadowY = offsetY > 0? offsetY : 0;
63
64     tmpPainter.drawImage(shadowX, shadowY, image);
65     tmpPainter.end();
66
67     if (blur > 0)
68         qt_image_boxblur(shadowImg, blur/2, true);
69
70     // blacken the image with shadow color...
71     tmpPainter.begin(&shadowImg);
72     tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
73     tmpPainter.fillRect(shadowImg.rect(), color);
74     tmpPainter.end();
75     return shadowImg;
76 }
77
78 static void fillRectShadow(QPainter* p, QRectF shadowRect, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
79 {
80     QRectF r = shadowRect;
81     r.moveTo(0, 0);
82
83     QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32_Premultiplied);
84     QPainter tp;
85     tp.begin(&shadowImage);
86     tp.fillRect(r, p->brush());
87     tp.end();
88     shadowImage = makeShadowImage(shadowImage, offsetX, offsetY, blur, color);
89
90     qreal dx = shadowRect.left() + (offsetX < 0? offsetX:0);
91     qreal dy = shadowRect.top() + (offsetY < 0? offsetY:0);
92
93     p->drawImage(dx, dy, shadowImage);
94     p->fillRect(shadowRect, p->brush());
95 }
96
97 static void fillShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
98 {
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);
103     img.fill(0);
104     QPainter tp(&img);
105     tp.fillPath(path.translated(0, 0), p->brush());
106     tp.end();
107
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);
111
112     p->drawImage(dx, dy, shadowImage);
113     p->fillPath(path, p->brush());
114 }
115
116 static void strokeShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
117 {
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);
122     img.fill(0);
123     QPainter tp(&img);
124     tp.strokePath(path, p->pen());
125     tp.end();
126
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());
132 }
133 static inline void drawRepeatPattern(QPainter* p, const QImage& image, const QRectF& rect, const bool repeatX, const bool repeatY)
134 {
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();
140         int startX, startY;
141         QRect r(static_cast<int>(rect.x()), static_cast<int>(rect.y()), static_cast<int>(rect.width()), static_cast<int>(rect.height()));
142
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) {
145             // repeat
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);
149         } else {
150            if (!repeatX && !repeatY) {
151                // no-repeat
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)) {
155                    startX = 0;
156                    startY = 0;
157                } else
158                    return;
159            } else if (repeatX && !repeatY) {
160                // repeat-x
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);
165                    startY = 0;
166                } else
167                    return;
168            } else {
169                // repeat-y
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)) {
173                    startX = 0;
174                    startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
175                } else
176                    return;
177            }
178         }
179
180         int x = startX;
181         int y = startY;
182         do {
183             // repeat Y
184             do {
185                 // repeat X
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());
190
191                 p->drawImage(destStart, image, sourceRect);
192                 x += w;
193             } while (repeatX && x < r.x() + r.width());
194             x = startX;
195             y += h;
196         } while (repeatY && y < r.y() + r.height());
197     }
198 }
199
200 QPen QQuickContext2DCommandBuffer::makePen(const QQuickContext2D::State& state)
201 {
202     QPen pen;
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);
208     return pen;
209 }
210
211 void QQuickContext2DCommandBuffer::setPainterState(QPainter* p, const QQuickContext2D::State& state, const QPen& pen)
212 {
213    p->setTransform(state.matrix * p->transform());
214
215    if (pen != p->pen())
216        p->setPen(pen);
217
218    if (state.fillStyle != p->brush())
219        p->setBrush(state.fillStyle);
220
221    if (state.font != p->font())
222        p->setFont(state.font);
223
224    if (state.globalAlpha != p->opacity()) {
225        p->setOpacity(state.globalAlpha);
226    }
227
228    if (state.globalCompositeOperation != p->compositionMode())
229        p->setCompositionMode(state.globalCompositeOperation);
230 }
231
232 void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& state)
233 {
234     if (!p)
235         return;
236
237     reset();
238
239     QTransform originMatrix = p->worldTransform();
240
241     QPen pen = makePen(state);
242     setPainterState(p, state, pen);
243
244     while (hasNext()) {
245         QQuickContext2D::PaintCommand cmd = takeNextCommand();
246         switch (cmd) {
247         case QQuickContext2D::UpdateMatrix:
248         {
249             state.matrix = takeMatrix();
250             p->setWorldTransform(state.matrix * originMatrix);
251             break;
252         }
253         case QQuickContext2D::ClearRect:
254         {
255             QPainter::CompositionMode  cm = p->compositionMode();
256             qreal alpha = p->opacity();
257             p->setCompositionMode(QPainter::CompositionMode_Source);
258             p->setOpacity(0);
259             p->fillRect(takeRect(), QColor(qRgba(0, 0, 0, 0)));
260             p->setCompositionMode(cm);
261             p->setOpacity(alpha);
262             break;
263         }
264         case QQuickContext2D::FillRect:
265         {
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);
269             else
270                 p->fillRect(r, p->brush());
271             break;
272         }
273         case QQuickContext2D::ShadowColor:
274         {
275             state.shadowColor = takeColor();
276             break;
277         }
278         case QQuickContext2D::ShadowBlur:
279         {
280             state.shadowBlur = takeShadowBlur();
281             break;
282         }
283         case QQuickContext2D::ShadowOffsetX:
284         {
285             state.shadowOffsetX = takeShadowOffsetX();
286             break;
287         }
288         case QQuickContext2D::ShadowOffsetY:
289         {
290             state.shadowOffsetY = takeShadowOffsetY();
291             break;
292         }
293         case QQuickContext2D::FillStyle:
294         {
295             state.fillStyle = takeFillStyle();
296             state.fillPatternRepeatX = takeBool();
297             state.fillPatternRepeatY = takeBool();
298             p->setBrush(state.fillStyle);
299             break;
300         }
301         case QQuickContext2D::StrokeStyle:
302         {
303             state.strokeStyle = takeStrokeStyle();
304             state.strokePatternRepeatX = takeBool();
305             state.strokePatternRepeatY = takeBool();
306             QPen nPen = p->pen();
307             nPen.setBrush(state.strokeStyle);
308             p->setPen(nPen);
309             break;
310         }
311         case QQuickContext2D::LineWidth:
312         {
313             state.lineWidth = takeLineWidth();
314             QPen nPen = p->pen();
315
316             nPen.setWidthF(state.lineWidth);
317             p->setPen(nPen);
318             break;
319         }
320         case QQuickContext2D::LineCap:
321         {
322             state.lineCap = takeLineCap();
323             QPen nPen = p->pen();
324             nPen.setCapStyle(state.lineCap);
325             p->setPen(nPen);
326             break;
327         }
328         case QQuickContext2D::LineJoin:
329         {
330             state.lineJoin = takeLineJoin();
331             QPen nPen = p->pen();
332             nPen.setJoinStyle(state.lineJoin);
333             p->setPen(nPen);
334             break;
335         }
336         case QQuickContext2D::MiterLimit:
337         {
338             state.miterLimit = takeMiterLimit();
339             QPen nPen = p->pen();
340             nPen.setMiterLimit(state.miterLimit);
341             p->setPen(nPen);
342             break;
343         }
344         case QQuickContext2D::TextAlign:
345         case QQuickContext2D::TextBaseline:
346             break;
347         case QQuickContext2D::Fill:
348         {
349             QPainterPath path = takePath();
350             path.closeSubpath();
351             if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
352                 fillShadowPath(p,path, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
353             else
354                 p->fillPath(path, p->brush());
355             break;
356         }
357         case QQuickContext2D::Stroke:
358         {
359             if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
360                 strokeShadowPath(p,takePath(), state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
361             else
362                 p->strokePath(takePath(), p->pen());
363             break;
364         }
365         case QQuickContext2D::Clip:
366         {
367             state.clipPath = takePath();
368             p->setClipping(true);
369             p->setClipPath(state.clipPath);
370             break;
371         }
372         case QQuickContext2D::GlobalAlpha:
373         {
374             state.globalAlpha = takeGlobalAlpha();
375             p->setOpacity(state.globalAlpha);
376             break;
377         }
378         case QQuickContext2D::GlobalCompositeOperation:
379         {
380             state.globalCompositeOperation = takeGlobalCompositeOperation();
381             p->setCompositionMode(state.globalCompositeOperation);
382             break;
383         }
384         case QQuickContext2D::DrawImage:
385         {
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();
395
396             if (!image.isNull()) {
397                 if (sw == -1 || sh == -1) {
398                     sw = image.width();
399                     sh = image.height();
400                 }
401                 if (sx != 0 || sy != 0 || sw != image.width() || sh != image.height())
402                     image = image.copy(sx, sy, sw, sh);
403
404                 image = image.scaled(dw, dh);
405
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);
411                 }
412                 p->drawImage(dx, dy, image);
413             }
414             break;
415         }
416         case QQuickContext2D::GetImageData:
417         {
418             //TODO:
419             break;
420         }
421         default:
422             break;
423         }
424     }
425
426     p->end();
427 }
428
429 QQuickContext2DCommandBuffer::QQuickContext2DCommandBuffer()
430     : cmdIdx(0)
431     , intIdx(0)
432     , boolIdx(0)
433     , realIdx(0)
434     , colorIdx(0)
435     , matrixIdx(0)
436     , brushIdx(0)
437     , pathIdx(0)
438     , imageIdx(0)
439 {
440 }
441
442
443 QQuickContext2DCommandBuffer::~QQuickContext2DCommandBuffer()
444 {
445 }
446
447 void QQuickContext2DCommandBuffer::clear()
448 {
449     commands.clear();
450     ints.clear();
451     bools.clear();
452     reals.clear();
453     colors.clear();
454     matrixes.clear();
455     brushes.clear();
456     pathes.clear();
457     images.clear();
458     reset();
459 }
460
461 void QQuickContext2DCommandBuffer::reset()
462 {
463     cmdIdx = 0;
464     intIdx = 0;
465     boolIdx = 0;
466     realIdx = 0;
467     colorIdx = 0;
468     matrixIdx = 0;
469     brushIdx = 0;
470     pathIdx = 0;
471     imageIdx = 0;
472 }
473
474 QT_END_NAMESPACE
475