Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
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();
288 state.fillPatternRepeatX = takeBool();
289 state.fillPatternRepeatY = takeBool();
290 p->setBrush(state.fillStyle);
291 break;
292 }
294 {
295 state.strokeStyle = takeStrokeStyle();
296 state.strokePatternRepeatX = takeBool();
297 state.strokePatternRepeatY = takeBool();
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)
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 {
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
\inmodule QtGui
Definition qbrush.h:30
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
\inmodule QtGui
Definition qimage.h:37
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
\inmodule QtGui
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
void strokePath(const QPainterPath &path, const QPen &pen)
Draws the outline (strokes) the path path with the pen specified by pen.
void drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect, Qt::ImageConversionFlags flags=Qt::AutoColor)
Draws the rectangular portion source of the given image into the target rectangle in the paint device...
void fillPath(const QPainterPath &path, const QBrush &brush)
Fills the given path using the given brush.
CompositionMode
Defines the modes supported for digital image compositing.
Definition qpainter.h:97
@ CompositionMode_Clear
Definition qpainter.h:100
@ CompositionMode_SourceIn
Definition qpainter.h:103
void fillRect(const QRectF &, const QBrush &)
Fills the given rectangle with the brush specified.
\inmodule QtGui
Definition qpen.h:28
void setCapStyle(Qt::PenCapStyle pcs)
Sets the pen's cap style to the given style.
Definition qpen.cpp:650
void setWidthF(qreal width)
Sets the pen width to the given width in pixels with floating point precision.
Definition qpen.cpp:618
void setBrush(const QBrush &brush)
Sets the brush used to fill strokes generated with this pen to the given brush.
Definition qpen.cpp:726
void setJoinStyle(Qt::PenJoinStyle pcs)
Sets the pen's join style to the given style.
Definition qpen.cpp:677
void setDashOffset(qreal doffset)
Sets the dash offset (the starting point on the dash pattern) for this pen to the offset specified.
Definition qpen.cpp:506
void setMiterLimit(qreal limit)
Sets the miter limit of this pen to the given limit.
Definition qpen.cpp:545
void setDashPattern(const QList< qreal > &pattern)
Sets the dash pattern for this pen to the given pattern.
Definition qpen.cpp:463
bool isNull() const
Returns true if this is a null pixmap; otherwise returns false.
Definition qpixmap.cpp:456
\inmodule QtCore\reentrant
Definition qpoint.h:217
QQuickContext2D::PaintCommand takeNextCommand()
void replay(QPainter *painter, QQuickContext2D::State &state, const QVector2D &scaleFactor)
QQmlRefPointer< QQuickCanvasPixmap > takePixmap()
QPainter::CompositionMode takeGlobalCompositeOperation()
\inmodule QtCore\reentrant
Definition qrect.h:484
QRect toAlignedRect() const noexcept
Definition qrect.cpp:2338
constexpr qreal y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:672
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:732
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:729
constexpr qreal x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:669
constexpr QRectF translated(qreal dx, qreal dy) const noexcept
Returns a copy of the rectangle that is translated dx along the x axis and dy along the y axis,...
Definition qrect.h:762
\inmodule QtCore\reentrant
Definition qrect.h:30
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:502
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:501
EGLint EGLint EGLint EGLint int int * ints
QPainter paint
rect
[4]
QPixmap pix
else opt state
[0]
Combined button and popup list for selecting options.
@ white
Definition qnamespace.h:31
@ SolidLine
Definition brush.cpp:5
Definition image.cpp:4
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean r
[2]
GLenum GLenum GLsizei count
GLsizei const GLubyte * commands
GLuint color
[2]
GLenum GLuint GLintptr offset
GLsizei const GLchar *const * path
GLint GLenum GLboolean normalized
Definition qopenglext.h:752
GLfloat GLfloat p
[1]
GLubyte * pattern
static const QRectF boundingRect(const QPointF *points, int pointCount)
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)
QT_BEGIN_NAMESPACE void qt_image_boxblur(QImage &image, int radius, bool quality)
static void qt_drawImage(QPainter *p, QQuickContext2D::State &state, QImage image, const QRectF &sr, const QRectF &dr, bool shadow=false)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
unsigned int uint
Definition qtypes.h:34
double qreal
Definition qtypes.h:187