Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickcontext2dcommandbuffer.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6#include <qqml.h>
7#include <QtCore/QMutex>
8#include <QtQuick/qsgtexture.h>
9#include <QtGui/QPaintEngine>
10
11#define HAS_SHADOW(offsetX, offsetY, blur, color) (color.isValid() && color.alpha() && (blur || offsetX || offsetY))
12
14
15void qt_image_boxblur(QImage& image, int radius, bool quality);
16
17namespace {
18 class ShadowImageMaker
19 {
20 public:
21 virtual ~ShadowImageMaker() {}
22
23 void paintShapeAndShadow(QPainter *p, qreal offsetX, qreal offsetY, qreal blur, const QColor &color)
24 {
25 QRectF bounds = boundingRect().translated(offsetX, offsetY).adjusted(-2*blur, -2*blur, 2*blur, 2*blur);
26 QRect boundsAligned = bounds.toAlignedRect();
27
28 QImage shadowImage(boundsAligned.size(), QImage::Format_ARGB32_Premultiplied);
29 shadowImage.fill(0);
30
31 QPainter shadowPainter(&shadowImage);
32 shadowPainter.setRenderHints(p->renderHints());
33 shadowPainter.translate(offsetX - boundsAligned.left(), offsetY - boundsAligned.top());
34 paint(&shadowPainter);
35 shadowPainter.end();
36
37 if (blur > 0)
38 qt_image_boxblur(shadowImage, qMax(1, qRound(blur / 2)), true);
39
40 // blacken the image with shadow color...
41 shadowPainter.begin(&shadowImage);
42 shadowPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
43 shadowPainter.fillRect(shadowImage.rect(), color);
44 shadowPainter.end();
45
46 p->drawImage(boundsAligned.topLeft(), shadowImage);
47 paint(p);
48 }
49
50 virtual void paint(QPainter *p) const = 0;
51 virtual QRectF boundingRect() const = 0;
52 };
53
54 class FillRectShadow : public ShadowImageMaker
55 {
56 public:
57 FillRectShadow(const QRectF &rect, const QBrush &brush)
58 : m_rect(rect.normalized())
59 , m_brush(brush)
60 {
61 }
62
63 void paint(QPainter *p) const override { p->fillRect(m_rect, m_brush); }
64 QRectF boundingRect() const override { return m_rect; }
65
66 private:
67 QRectF m_rect;
68 QBrush m_brush;
69 };
70
71 class FillPathShadow : public ShadowImageMaker
72 {
73 public:
74 FillPathShadow(const QPainterPath &path, const QBrush &brush)
75 : m_path(path)
76 , m_brush(brush)
77 {
78 }
79
80 void paint(QPainter *p) const override { p->fillPath(m_path, m_brush); }
81 QRectF boundingRect() const override { return m_path.boundingRect(); }
82
83 private:
84 QPainterPath m_path;
85 QBrush m_brush;
86 };
87
88 class StrokePathShadow : public ShadowImageMaker
89 {
90 public:
91 StrokePathShadow(const QPainterPath &path, const QPen &pen)
92 : m_path(path)
93 , m_pen(pen)
94 {
95 }
96
97 void paint(QPainter *p) const override { p->strokePath(m_path, m_pen); }
98
99 QRectF boundingRect() const override
100 {
101 qreal d = qMax(qreal(1), m_pen.widthF());
102 return m_path.boundingRect().adjusted(-d, -d, d, d);
103 }
104
105 private:
106 QPainterPath m_path;
107 QPen m_pen;
108 };
109
110 class DrawImageShadow : public ShadowImageMaker
111 {
112 public:
113 DrawImageShadow(const QImage &image, const QPointF &offset)
114 : m_image(image)
115 , m_offset(offset)
116 {
117 }
118
119 void paint(QPainter *p) const override { p->drawImage(m_offset, m_image); }
120
121 QRectF boundingRect() const override { return QRectF(m_image.rect()).translated(m_offset); }
122
123 private:
124 QImage m_image;
125 QPointF m_offset;
126 };
127}
128
129static void fillRectShadow(QPainter* p, QRectF shadowRect, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
130{
131 FillRectShadow shadowMaker(shadowRect, p->brush());
132 shadowMaker.paintShapeAndShadow(p, offsetX, offsetY, blur, color);
133}
134
135static void fillShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
136{
137 FillPathShadow shadowMaker(path, p->brush());
138 shadowMaker.paintShapeAndShadow(p, offsetX, offsetY, blur, color);
139}
140
141static void strokeShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
142{
143 StrokePathShadow shadowMaker(path, p->pen());
144 shadowMaker.paintShapeAndShadow(p, offsetX, offsetY, blur, color);
145}
146
147QPen QQuickContext2DCommandBuffer::makePen(const QQuickContext2D::State& state)
148{
149 QPen pen;
150 pen.setWidthF(state.lineWidth);
151 pen.setCapStyle(state.lineCap);
152 pen.setJoinStyle(state.lineJoin);
153 pen.setMiterLimit(state.miterLimit);
154 pen.setBrush(state.strokeStyle);
155 if (!state.lineDash.isEmpty()) {
156 pen.setDashPattern(state.lineDash);
157 }
158 pen.setDashOffset(state.lineDashOffset);
159 return pen;
160}
161
162void QQuickContext2DCommandBuffer::setPainterState(QPainter* p, const QQuickContext2D::State& state, const QPen& pen)
163{
164 p->setTransform(state.matrix * p->transform());
165
166 if (pen != p->pen())
167 p->setPen(pen);
168
169 if (state.fillStyle != p->brush())
170 p->setBrush(state.fillStyle);
171
172 if (state.font != p->font())
173 p->setFont(state.font);
174
175 if (state.globalAlpha != p->opacity()) {
176 p->setOpacity(state.globalAlpha);
177 }
178
179 if (state.globalCompositeOperation != p->compositionMode())
180 p->setCompositionMode(state.globalCompositeOperation);
181
182 p->setClipping(state.clip);
183 if (state.clip)
184 p->setClipPath(state.clipPath);
185}
186
187static void qt_drawImage(QPainter *p, QQuickContext2D::State& state, QImage image, const QRectF& sr, const QRectF& dr, bool shadow = false)
188{
189 Q_ASSERT(p);
190
191 if (image.isNull())
192 return;
193
194 qreal sx = sr.x();
195 qreal sy = sr.y();
196 qreal sw = sr.width();
197 qreal sh = sr.height();
198 qreal dx = dr.x();
199 qreal dy = dr.y();
200 qreal dw = dr.width();
201 qreal dh = dr.height();
202
203 if (sw == -1 || sh == -1) {
204 sw = image.width();
205 sh = image.height();
206 }
207 if (sx != 0 || sy != 0 || sw != image.width() || sh != image.height())
208 image = image.copy(sx, sy, sw, sh);
209
210 if (sw != dw || sh != dh)
211 image = image.scaled(dw, dh);
212
213 //Strange OpenGL painting behavior here, without beginNativePainting/endNativePainting, only the first image is painted.
214 p->beginNativePainting();
215
216 if (shadow) {
217 DrawImageShadow shadowMaker(image, QPointF(dx, dy));
218 shadowMaker.paintShapeAndShadow(p, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
219 } else {
220 p->drawImage(dx, dy, image);
221 }
222
223 p->endNativePainting();
224}
225
226void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& state, const QVector2D &scaleFactor)
227{
228 if (!p)
229 return;
230
231 reset();
232
233 p->scale(scaleFactor.x(), scaleFactor.y());
234 QTransform originMatrix = p->worldTransform();
235
236 QPen pen = makePen(state);
237 setPainterState(p, state, pen);
238
239 while (hasNext()) {
241 switch (cmd) {
243 {
244 state.matrix = takeMatrix();
245 p->setWorldTransform(state.matrix * originMatrix);
246 break;
247 }
249 {
250 QPainter::CompositionMode cm = p->compositionMode();
251 p->setCompositionMode(QPainter::CompositionMode_Clear);
252 p->fillRect(takeRect(), Qt::white);
253 p->setCompositionMode(cm);
254 break;
255 }
257 {
258 QRectF r = takeRect();
259 if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
260 fillRectShadow(p, r, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
261 else
262 p->fillRect(r, p->brush());
263 break;
264 }
266 {
267 state.shadowColor = takeColor();
268 break;
269 }
271 {
272 state.shadowBlur = takeShadowBlur();
273 break;
274 }
276 {
277 state.shadowOffsetX = takeShadowOffsetX();
278 break;
279 }
281 {
282 state.shadowOffsetY = takeShadowOffsetY();
283 break;
284 }
286 {
287 state.fillStyle = takeFillStyle();
290 p->setBrush(state.fillStyle);
291 break;
292 }
294 {
295 state.strokeStyle = takeStrokeStyle();
298 QPen nPen = p->pen();
299 nPen.setBrush(state.strokeStyle);
300 p->setPen(nPen);
301 break;
302 }
304 {
305 state.lineWidth = takeLineWidth();
306 QPen nPen = p->pen();
307
308 nPen.setWidthF(state.lineWidth);
309 p->setPen(nPen);
310 break;
311 }
313 {
314 state.lineCap = takeLineCap();
315 QPen nPen = p->pen();
316 nPen.setCapStyle(state.lineCap);
317 p->setPen(nPen);
318 break;
319 }
321 {
322 state.lineJoin = takeLineJoin();
323 QPen nPen = p->pen();
324 nPen.setJoinStyle(state.lineJoin);
325 p->setPen(nPen);
326 break;
327 }
329 {
330 const qreal count = takeReal();
331 QVector<qreal> pattern;
332 pattern.reserve(count);
333 for (uint i = 0; i < count; i++) {
334 pattern.append(takeReal());
335 }
336 state.lineDash = pattern;
337 QPen nPen = p->pen();
338 if (count > 0)
339 nPen.setDashPattern(pattern);
340 else
341 nPen.setStyle(Qt::SolidLine);
342 p->setPen(nPen);
343 break;
344 }
346 {
347 state.lineDashOffset = takeReal();
348 QPen nPen = p->pen();
349 nPen.setDashOffset(state.lineDashOffset);
350 p->setPen(nPen);
351 break;
352 }
354 {
355 state.miterLimit = takeMiterLimit();
356 QPen nPen = p->pen();
357 nPen.setMiterLimit(state.miterLimit);
358 p->setPen(nPen);
359 break;
360 }
363 break;
365 {
366 QPainterPath path = takePath();
367 path.closeSubpath();
368 if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
369 fillShadowPath(p,path, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
370 else
371 p->fillPath(path, p->brush());
372 break;
373 }
375 {
376 if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
377 strokeShadowPath(p,takePath(), state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
378 else
379 p->strokePath(takePath(), p->pen());
380 break;
381 }
383 {
384 state.clip = takeBool();
385 state.clipPath = takePath();
386 p->setClipping(state.clip);
387 if (state.clip)
388 p->setClipPath(state.clipPath);
389 break;
390 }
392 {
393 state.globalAlpha = takeGlobalAlpha();
394 p->setOpacity(state.globalAlpha);
395 break;
396 }
398 {
399 state.globalCompositeOperation = takeGlobalCompositeOperation();
400 p->setCompositionMode(state.globalCompositeOperation);
401 break;
402 }
404 {
405 QRectF sr = takeRect();
406 QRectF dr = takeRect();
407 qt_drawImage(p, state, takeImage(), sr, dr, HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor));
408 break;
409 }
411 {
412 QRectF sr = takeRect();
413 QRectF dr = takeRect();
414
415 QQmlRefPointer<QQuickCanvasPixmap> pix = takePixmap();
416 Q_ASSERT(!pix.isNull());
417
418 const bool hasShadow = HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
419 //TODO: generate shadow blur with shaders
420 qt_drawImage(p, state, pix->image(), sr, dr, hasShadow);
421 break;
422 }
424 {
425 //TODO:
426 break;
427 }
428 default:
429 break;
430 }
431 }
432
433 p->end();
434}
435
437 : cmdIdx(0)
438 , intIdx(0)
439 , boolIdx(0)
440 , realIdx(0)
441 , rectIdx(0)
442 , colorIdx(0)
443 , matrixIdx(0)
444 , brushIdx(0)
445 , pathIdx(0)
446 , imageIdx(0)
447 , pixmapIdx(0)
448{
449 static bool registered = false;
450 if (!registered) {
451 qRegisterMetaType<QQuickContext2DCommandBuffer*>("QQuickContext2DCommandBuffer*");
452 registered = true;
453 }
454}
455
456
460
462{
463 commands.clear();
464 ints.clear();
465 bools.clear();
466 reals.clear();
467 rects.clear();
468 colors.clear();
469 matrixes.clear();
470 brushes.clear();
471 pathes.clear();
472 images.clear();
473 pixmaps.clear();
474 reset();
475}
476
478{
479 cmdIdx = 0;
480 intIdx = 0;
481 boolIdx = 0;
482 realIdx = 0;
483 rectIdx = 0;
484 colorIdx = 0;
485 matrixIdx = 0;
486 brushIdx = 0;
487 pathIdx = 0;
488 imageIdx = 0;
489 pixmapIdx = 0;
490}
491
492QT_END_NAMESPACE
QQuickContext2D::PaintCommand takeNextCommand()
void replay(QPainter *painter, QQuickContext2D::State &state, const QVector2D &scaleFactor)
Combined button and popup list for selecting options.
void qt_image_boxblur(QImage &image, int radius, bool quality)
static void strokeShadowPath(QPainter *p, const QPainterPath &path, qreal offsetX, qreal offsetY, qreal blur, const QColor &color)
#define HAS_SHADOW(offsetX, offsetY, blur, color)
static void fillShadowPath(QPainter *p, const QPainterPath &path, qreal offsetX, qreal offsetY, qreal blur, const QColor &color)
static void fillRectShadow(QPainter *p, QRectF shadowRect, qreal offsetX, qreal offsetY, qreal blur, const QColor &color)
static void qt_drawImage(QPainter *p, QQuickContext2D::State &state, QImage image, const QRectF &sr, const QRectF &dr, bool shadow=false)