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