8eb95134335bb43a794bad14db0aeb98d0aae784
[profile/ivi/qtdeclarative.git] / src / declarative / items / context2d / qsgcontext2dcommandbuffer.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgcontext2dcommandbuffer_p.h"
43 #include "qsgcanvasitem_p.h"
44 #include "qdeclarative.h"
45 #include <QtGui/QApplication>
46 #include <QtCore/QMutex>
47
48 #define HAS_SHADOW(offsetX, offsetY, blur, color) (color.isValid() && color.alpha() && (blur || offsetX || offsetY))
49 static QImage makeShadowImage(const QImage& image, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
50 {
51     QImage shadowImg(image.width() + blur * 2 + qAbs(offsetX),
52                      image.height() + blur *2 + qAbs(offsetY),
53                      QImage::Format_ARGB32);
54     shadowImg.fill(0);
55     QPainter tmpPainter(&shadowImg);
56     tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
57     qreal shadowX = offsetX > 0? offsetX : 0;
58     qreal shadowY = offsetY > 0? offsetY : 0;
59
60     tmpPainter.drawImage(shadowX, shadowY, image);
61     tmpPainter.end();
62
63     // blur the alpha channel
64     if (blur > 0) {
65         QImage blurred(shadowImg.size(), QImage::Format_ARGB32);
66         blurred.fill(0);
67         QPainter blurPainter(&blurred);
68         qt_blurImage(&blurPainter, shadowImg, blur, true, false);
69         blurPainter.end();
70         shadowImg = blurred;
71     }
72
73     // blacken the image with shadow color...
74     tmpPainter.begin(&shadowImg);
75     tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
76     tmpPainter.fillRect(shadowImg.rect(), color);
77     tmpPainter.end();
78     return shadowImg;
79 }
80
81 static void fillRectShadow(QPainter* p, QRectF shadowRect, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
82 {
83     QRectF r = shadowRect;
84     r.moveTo(0, 0);
85
86     QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32);
87     QPainter tp;
88     tp.begin(&shadowImage);
89     tp.fillRect(r, p->brush());
90     tp.end();
91     shadowImage = makeShadowImage(shadowImage, offsetX, offsetY, blur, color);
92
93     qreal dx = shadowRect.left() + (offsetX < 0? offsetX:0);
94     qreal dy = shadowRect.top() + (offsetY < 0? offsetY:0);
95
96     p->drawImage(dx, dy, shadowImage);
97     p->fillRect(shadowRect, p->brush());
98 }
99
100 static void fillShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
101 {
102     QRectF r = path.boundingRect();
103     QImage img(r.size().width() + r.left() + 1,
104                r.size().height() + r.top() + 1,
105                QImage::Format_ARGB32);
106     img.fill(0);
107     QPainter tp(&img);
108     tp.fillPath(path.translated(0, 0), p->brush());
109     tp.end();
110
111     QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color);
112     qreal dx = r.left() + (offsetX < 0? offsetX:0);
113     qreal dy = r.top() + (offsetY < 0? offsetY:0);
114
115     p->drawImage(dx, dy, shadowImage);
116     p->fillPath(path, p->brush());
117 }
118
119 static void strokeShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
120 {
121     QRectF r = path.boundingRect();
122     QImage img(r.size().width() + r.left() + 1,
123                r.size().height() + r.top() + 1,
124                QImage::Format_ARGB32);
125     img.fill(0);
126     QPainter tp(&img);
127     tp.strokePath(path, p->pen());
128     tp.end();
129
130     QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color);
131     qreal dx = r.left() + (offsetX < 0? offsetX:0);
132     qreal dy = r.top() + (offsetY < 0? offsetY:0);
133     p->drawImage(dx, dy, shadowImage);
134     p->strokePath(path, p->pen());
135 }
136
137 QPen QSGContext2DCommandBuffer::makePen(QSGContext2D::State state)
138 {
139     QPen pen;
140     pen.setWidthF(state.lineWidth);
141     pen.setCapStyle(state.lineCap);
142     pen.setJoinStyle(state.lineJoin);
143     pen.setMiterLimit(state.miterLimit);
144     pen.setBrush(state.strokeStyle);
145     return pen;
146 }
147
148 void QSGContext2DCommandBuffer::setPainterState(QPainter* p, QSGContext2D::State state, const QPen& pen)
149 {
150    p->setTransform(state.matrix * p->transform());
151
152    if (pen != p->pen())
153        p->setPen(pen);
154
155    if (state.fillStyle != p->brush())
156        p->setBrush(state.fillStyle);
157
158    if (state.font != p->font())
159        p->setFont(state.font);
160
161    if (state.globalAlpha != p->opacity()) {
162        p->setOpacity(state.globalAlpha);
163    }
164
165    if (state.globalCompositeOperation != p->compositionMode())
166        p->setCompositionMode(state.globalCompositeOperation);
167 }
168
169 QSGContext2D::State QSGContext2DCommandBuffer::replay(QPainter* p, QSGContext2D::State state)
170 {
171     if (!p)
172         return state;
173
174     reset();
175
176     QTransform originMatrix = p->transform();
177
178     QPen pen = makePen(state);
179     setPainterState(p, state, pen);
180
181     while (hasNext()) {
182         QSGContext2D::PaintCommand cmd = takeNextCommand();
183         switch (cmd) {
184         case QSGContext2D::UpdateMatrix:
185         {
186             state.matrix = takeMatrix();
187             p->setTransform(state.matrix * originMatrix);
188             break;
189         }
190         case QSGContext2D::ClearRect:
191         {
192             p->eraseRect(takeRect());
193             break;
194         }
195         case QSGContext2D::FillRect:
196         {
197             QRectF r = takeRect();
198             if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
199                 fillRectShadow(p, r, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
200             else
201                 p->fillRect(r, p->brush());
202             break;
203         }
204         case QSGContext2D::ShadowColor:
205         {
206             state.shadowColor = takeColor();
207             break;
208         }
209         case QSGContext2D::ShadowBlur:
210         {
211             state.shadowBlur = takeShadowBlur();
212             break;
213         }
214         case QSGContext2D::ShadowOffsetX:
215         {
216             state.shadowOffsetX = takeShadowOffsetX();
217             break;
218         }
219         case QSGContext2D::ShadowOffsetY:
220         {
221             state.shadowOffsetY = takeShadowOffsetY();
222             break;
223         }
224         case QSGContext2D::FillStyle:
225         {
226             state.fillStyle = takeFillStyle();
227             p->setBrush(state.fillStyle);
228             break;
229         }
230         case QSGContext2D::StrokeStyle:
231         {
232             state.strokeStyle = takeStrokeStyle();
233             pen.setBrush(state.strokeStyle);
234             p->setPen(pen);
235             break;
236         }
237         case QSGContext2D::LineWidth:
238         {
239             state.lineWidth = takeLineWidth();
240             pen.setWidth(state.lineWidth);
241             p->setPen(pen);
242             break;
243         }
244         case QSGContext2D::LineCap:
245         {
246             state.lineCap = takeLineCap();
247             pen.setCapStyle(state.lineCap);
248             p->setPen(pen);
249             break;
250         }
251         case QSGContext2D::LineJoin:
252         {
253             state.lineJoin = takeLineJoin();
254             pen.setJoinStyle(state.lineJoin);
255             p->setPen(pen);
256             break;
257         }
258         case QSGContext2D::MiterLimit:
259         {
260             state.miterLimit = takeMiterLimit();
261             pen.setMiterLimit(state.miterLimit);
262             p->setPen(pen);
263             break;
264         }
265         case QSGContext2D::TextAlign:
266         case QSGContext2D::TextBaseline:
267             break;
268         case QSGContext2D::Fill:
269         {
270             if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
271                 fillShadowPath(p,takePath(), state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
272             else
273                 p->fillPath(takePath(), p->brush());
274             break;
275         }
276         case QSGContext2D::Stroke:
277         {
278             if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
279                 strokeShadowPath(p,takePath(), state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
280             else
281                 p->strokePath(takePath(), p->pen());
282             break;
283         }
284         case QSGContext2D::Clip:
285         {
286             QPainterPath clipPath = takePath();
287             clipPath.closeSubpath();
288             state.clipPath = state.clipPath.intersected(clipPath);
289             if (!p->clipPath().isEmpty())
290                 clipPath = clipPath.intersected(p->clipPath());
291             p->setClipping(true);
292             p->setClipPath(clipPath);
293             break;
294         }
295         case QSGContext2D::UpdateBrush:
296         {
297             state.fillStyle = takeBrush();
298             p->setBrush(state.fillStyle);
299             break;
300         }
301
302         case QSGContext2D::GlobalAlpha:
303         {
304             state.globalAlpha = takeGlobalAlpha();
305             p->setOpacity(state.globalAlpha);
306             break;
307         }
308         case QSGContext2D::GlobalCompositeOperation:
309         {
310             state.globalCompositeOperation = takeGlobalCompositeOperation();
311             p->setCompositionMode(state.globalCompositeOperation);
312             break;
313         }
314         case QSGContext2D::DrawImage:
315         {
316             qreal sx = takeReal();
317             qreal sy = takeReal();
318             qreal sw = takeReal();
319             qreal sh = takeReal();
320             qreal dx = takeReal();
321             qreal dy = takeReal();
322             qreal dw = takeReal();
323             qreal dh = takeReal();
324             QImage image = takeImage();
325
326             if (!image.isNull()) {
327                 if (sw == -1 || sh == -1) {
328                     sw = image.width();
329                     sh = image.height();
330                 }
331                 if (sx != 0 || sy != 0 || sw != image.width() || sh != image.height())
332                     image = image.copy(sx, sy, sw, sh);
333
334                 image = image.scaled(dw, dh);
335
336                 if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) {
337                     QImage shadow = makeShadowImage(image, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
338                     qreal shadow_dx = dx + (state.shadowOffsetX < 0? state.shadowOffsetY:0);
339                     qreal shadow_dy = dy + (state.shadowOffsetX < 0? state.shadowOffsetY:0);
340                     p->drawImage(shadow_dx, shadow_dy, shadow);
341                 }
342                 p->drawImage(dx, dy, image);
343             }
344             break;
345         }
346         case QSGContext2D::GetImageData:
347         {
348             //TODO:
349             break;
350         }
351         default:
352             break;
353         }
354     }
355
356     p->end();
357     return state;
358 }
359
360 QSGContext2DCommandBuffer::QSGContext2DCommandBuffer()
361     : cmdIdx(0)
362     , intIdx(0)
363     , realIdx(0)
364     , colorIdx(0)
365     , matrixIdx(0)
366     , brushIdx(0)
367     , pathIdx(0)
368     , imageIdx(0)
369 {
370 }
371
372
373 QSGContext2DCommandBuffer::~QSGContext2DCommandBuffer()
374 {
375 }
376
377 void QSGContext2DCommandBuffer::clear()
378 {
379     commands.clear();
380     ints.clear();
381     reals.clear();
382     colors.clear();
383     matrixes.clear();
384     brushes.clear();
385     pathes.clear();
386     images.clear();
387     reset();
388 }
389
390 void QSGContext2DCommandBuffer::reset()
391 {
392     cmdIdx = 0;
393     intIdx = 0;
394     realIdx = 0;
395     colorIdx = 0;
396     matrixIdx = 0;
397     brushIdx = 0;
398     pathIdx = 0;
399     imageIdx = 0;
400 }
401
402