Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qsgsoftwarerenderablenode.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
5
12#if QT_CONFIG(quick_sprite)
14#endif
15
16#include <qsgsimplerectnode.h>
18#include <private/qsgrendernode_p.h>
19#include <private/qsgplaintexture_p.h>
20
21#include <qmath.h>
22
23Q_LOGGING_CATEGORY(lcRenderable, "qt.scenegraph.softwarecontext.renderable")
24
26
27// Largest subrectangle with integer coordinates
28inline QRect toRectMin(const QRectF & r)
29{
30 int x1 = qCeil(r.left());
31 int x2 = qFloor(r.right());
32 int y1 = qCeil(r.top());
33 int y2 = qFloor(r.bottom());
34 return QRect(x1, y1, x2 - x1, y2 - y1);
35}
36
37// Smallest superrectangle with integer coordinates
38inline QRect toRectMax(const QRectF & r)
39{
40 return r.toAlignedRect();
41}
42
44 : m_nodeType(type)
45 , m_isOpaque(true)
46 , m_isDirty(true)
47 , m_hasClipRegion(false)
48 , m_opacity(1.0f)
49{
50 switch (m_nodeType) {
52 m_handle.simpleRectNode = static_cast<QSGSimpleRectNode*>(node);
53 break;
55 m_handle.simpleTextureNode = static_cast<QSGSimpleTextureNode*>(node);
56 break;
58 m_handle.imageNode = static_cast<QSGSoftwareInternalImageNode*>(node);
59 break;
61 m_handle.painterNode = static_cast<QSGSoftwarePainterNode*>(node);
62 break;
64 m_handle.rectangleNode = static_cast<QSGSoftwareInternalRectangleNode*>(node);
65 break;
67 m_handle.glpyhNode = static_cast<QSGSoftwareGlyphNode*>(node);
68 break;
70 m_handle.ninePatchNode = static_cast<QSGSoftwareNinePatchNode*>(node);
71 break;
73 m_handle.simpleRectangleNode = static_cast<QSGRectangleNode*>(node);
74 break;
76 m_handle.simpleImageNode = static_cast<QSGImageNode*>(node);
77 break;
78#if QT_CONFIG(quick_sprite)
79 case QSGSoftwareRenderableNode::SpriteNode:
80 m_handle.spriteNode = static_cast<QSGSoftwareSpriteNode*>(node);
81 break;
82#endif
84 m_handle.renderNode = static_cast<QSGRenderNode*>(node);
85 break;
87 m_handle.simpleRectNode = nullptr;
88 break;
89 }
90}
91
93{
94
95}
96
98{
99 // Update the Node properties
100 m_isDirty = true;
101 m_isOpaque = false;
102
104
105 switch (m_nodeType) {
107 if (m_handle.simpleRectNode->color().alpha() == 255)
108 m_isOpaque = true;
109
110 boundingRect = m_handle.simpleRectNode->rect();
111 break;
113 if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel())
114 m_isOpaque = true;
115
116 boundingRect = m_handle.simpleTextureNode->rect();
117 break;
119 m_isOpaque = !m_handle.imageNode->pixmap().hasAlphaChannel();
120
121 boundingRect = m_handle.imageNode->rect().toRect();
122 break;
124 if (m_handle.painterNode->opaquePainting())
125 m_isOpaque = true;
126
127 boundingRect = QRectF(0, 0, m_handle.painterNode->size().width(), m_handle.painterNode->size().height());
128 break;
130 if (m_handle.rectangleNode->isOpaque())
131 m_isOpaque = true;
132
133 boundingRect = m_handle.rectangleNode->rect();
134 break;
136 // Always has alpha
137 boundingRect = m_handle.glpyhNode->boundingRect();
138 break;
140 m_isOpaque = m_handle.ninePatchNode->isOpaque();
141
142 boundingRect = m_handle.ninePatchNode->bounds();
143 break;
145 if (m_handle.simpleRectangleNode->color().alpha() == 255)
146 m_isOpaque = true;
147
148 boundingRect = m_handle.simpleRectangleNode->rect();
149 break;
151 if (!m_handle.simpleImageNode->texture()->hasAlphaChannel())
152 m_isOpaque = true;
153
154 boundingRect = m_handle.simpleImageNode->rect();
155 break;
156#if QT_CONFIG(quick_sprite)
157 case QSGSoftwareRenderableNode::SpriteNode:
158 m_isOpaque = m_handle.spriteNode->isOpaque();
159 boundingRect = m_handle.spriteNode->rect();
160 break;
161#endif
163 if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering))
164 m_isOpaque = true;
165
166 boundingRect = m_handle.renderNode->rect();
167 break;
168 default:
169 break;
170 }
171
172 if (m_transform.isRotating())
173 m_isOpaque = false;
174
175 const QRectF transformedRect = m_transform.mapRect(boundingRect);
176 m_boundingRectMin = toRectMin(transformedRect);
177 m_boundingRectMax = toRectMax(transformedRect);
178
179 if (m_hasClipRegion && m_clipRegion.rectCount() <= 1) {
180 // If there is a clipRegion, and it is empty, the item wont be rendered
181 if (m_clipRegion.isEmpty()) {
182 m_boundingRectMin = QRect();
183 m_boundingRectMax = QRect();
184 } else {
185 const auto rects = m_clipRegion.begin();
186 m_boundingRectMin = m_boundingRectMin.intersected(rects[0]);
187 m_boundingRectMax = m_boundingRectMax.intersected(rects[0]);
188 }
189 }
190
191 // Overrides
192 if (m_opacity < 1.0f)
193 m_isOpaque = false;
194
195 m_dirtyRegion = QRegion(m_boundingRectMax);
196}
197
199{
200 const QMatrix4x4 *projectionMatrix() const override { return &ident; }
201 QRect scissorRect() const override { return QRect(); }
202 bool scissorEnabled() const override { return false; }
203 int stencilValue() const override { return 0; }
204 bool stencilEnabled() const override { return false; }
205 const QRegion *clipRegion() const override { return &cr; }
208};
209
211{
213
214 // Check for don't paint conditions
215 if (m_nodeType != RenderNode) {
216 if (!m_isDirty || qFuzzyIsNull(m_opacity) || m_dirtyRegion.isEmpty()) {
217 m_isDirty = false;
218 m_dirtyRegion = QRegion();
219 return QRegion();
220 }
221 } else {
222 if (!m_isDirty || qFuzzyIsNull(m_opacity)) {
223 m_isDirty = false;
224 m_dirtyRegion = QRegion();
225 return QRegion();
226 } else {
227 QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(m_handle.renderNode);
228 rd->m_localMatrix = m_transform;
229 rd->m_matrix = &rd->m_localMatrix;
230 rd->m_opacity = m_opacity;
231
232 // all the clip region below is in world coordinates, taking m_transform into account already
233 QRegion cr = m_dirtyRegion;
234 if (m_clipRegion.rectCount() > 1)
235 cr &= m_clipRegion;
236
237 painter->save();
239 rs.cr = cr;
240 m_handle.renderNode->render(&rs);
241 painter->restore();
242
243 const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering)
244 ? m_boundingRectMax // already mapped to world
245 : QRect(0, 0, painter->device()->width(), painter->device()->height());
246 m_previousDirtyRegion = QRegion(br);
247 m_isDirty = false;
248 m_dirtyRegion = QRegion();
249 return br;
250 }
251 }
252
253 painter->save();
254 painter->setOpacity(m_opacity);
255
256 // Set clipRegion to m_dirtyRegion (in world coordinates, so must be done before the setTransform below)
257 // as m_dirtyRegion already accounts for clipRegion
258 painter->setClipRegion(m_dirtyRegion, Qt::ReplaceClip);
259 if (m_clipRegion.rectCount() > 1)
261
262 painter->setTransform(m_transform, false); //precalculated worldTransform
263 if (forceOpaquePainting || m_isOpaque)
265
266 switch (m_nodeType) {
268 painter->fillRect(m_handle.simpleRectNode->rect(), m_handle.simpleRectNode->color());
269 break;
271 {
272 QSGTexture *texture = m_handle.simpleTextureNode->texture();
273 if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(texture)) {
274 const QPixmap &pm = pt->pixmap();
275 painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, m_handle.simpleTextureNode->sourceRect());
276 } else if (QSGPlainTexture *pt = qobject_cast<QSGPlainTexture *>(texture)) {
277 const QImage &im = pt->image();
278 painter->drawImage(m_handle.simpleTextureNode->rect(), im, m_handle.simpleTextureNode->sourceRect());
279 }
280 }
281 break;
283 m_handle.imageNode->paint(painter);
284 break;
286 m_handle.painterNode->paint(painter);
287 break;
289 m_handle.rectangleNode->paint(painter);
290 break;
292 m_handle.glpyhNode->paint(painter);
293 break;
295 m_handle.ninePatchNode->paint(painter);
296 break;
298 static_cast<QSGSoftwareRectangleNode *>(m_handle.simpleRectangleNode)->paint(painter);
299 break;
301 static_cast<QSGSoftwareImageNode *>(m_handle.simpleImageNode)->paint(painter);
302 break;
303#if QT_CONFIG(quick_sprite)
304 case QSGSoftwareRenderableNode::SpriteNode:
305 static_cast<QSGSoftwareSpriteNode *>(m_handle.spriteNode)->paint(painter);
306 break;
307#endif
308 default:
309 break;
310 }
311
312 painter->restore();
313
314 QRegion areaToBeFlushed = m_dirtyRegion;
315 m_previousDirtyRegion = QRegion(m_boundingRectMax);
316 m_isDirty = false;
317 m_dirtyRegion = QRegion();
318
319 return areaToBeFlushed;
320}
321
323{
324 return m_dirtyRegion.isEmpty();
325}
326
328{
329 if (m_transform == transform)
330 return;
331 m_transform = transform;
332 update();
333}
334
335void QSGSoftwareRenderableNode::setClipRegion(const QRegion &clipRect, bool hasClipRegion)
336{
337 if (m_clipRegion == clipRect && m_hasClipRegion == hasClipRegion)
338 return;
339
340 m_clipRegion = clipRect;
341 m_hasClipRegion = hasClipRegion;
342 update();
343}
344
346{
347 if (qFuzzyCompare(m_opacity, opacity))
348 return;
349
350 m_opacity = opacity;
351 update();
352}
353
355{
356 update();
357}
358
360{
361 update();
362}
363
364void QSGSoftwareRenderableNode::addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty)
365{
366 // Check if the dirty region applies to this node
367 QRegion prev = m_dirtyRegion;
368 if (dirtyRegion.intersects(m_boundingRectMax)) {
369 if (forceDirty)
370 m_isDirty = true;
371 m_dirtyRegion += dirtyRegion.intersected(m_boundingRectMax);
372 }
373 qCDebug(lcRenderable) << "addDirtyRegion: " << dirtyRegion << "old dirtyRegion: " << prev << "new dirtyRegion: " << m_dirtyRegion;
374}
375
377{
378 QRegion prev = m_dirtyRegion;
379 if (m_isDirty) {
380 // Check if this rect concerns us
381 if (dirtyRegion.intersects(m_boundingRectMax)) {
382 m_dirtyRegion -= dirtyRegion;
383 if (m_dirtyRegion.isEmpty())
384 m_isDirty = false;
385 }
386 }
387 qCDebug(lcRenderable) << "subtractDirtyRegion: " << dirtyRegion << "old dirtyRegion" << prev << "new dirtyRegion: " << m_dirtyRegion;
388}
389
391{
392 // When removing a node, the boundingRect shouldn't be subtracted
393 // because a deleted node has no valid boundingRect
394 if (wasRemoved)
395 return m_previousDirtyRegion;
396
397 return m_previousDirtyRegion.subtracted(QRegion(m_boundingRectMax));
398}
399
401{
402 return m_dirtyRegion;
403}
404
\inmodule QtGui
Definition qimage.h:37
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
int width() const
int height() const
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
QPaintDevice * device() const
Returns the paint device on which this painter is currently painting, or \nullptr if the painter is n...
void restore()
Restores the current painter state (pops a saved state off the stack).
void setOpacity(qreal opacity)
void setCompositionMode(CompositionMode mode)
Sets the composition mode to the given mode.
void save()
Saves the current painter state (pushes the state onto a stack).
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 drawPixmap(const QRectF &targetRect, const QPixmap &pixmap, const QRectF &sourceRect)
Draws the rectangular portion source of the given pixmap into the given target in the paint device.
@ CompositionMode_Source
Definition qpainter.h:101
void fillRect(const QRectF &, const QBrush &)
Fills the given rectangle with the brush specified.
void setClipRegion(const QRegion &, Qt::ClipOperation op=Qt::ReplaceClip)
Sets the clip region to the given region using the specified clip operation.
void setTransform(const QTransform &transform, bool combine=false)
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
\inmodule QtCore\reentrant
Definition qrect.h:483
\inmodule QtCore\reentrant
Definition qrect.h:30
QRect intersected(const QRect &other) const noexcept
Definition qrect.h:414
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
int rectCount() const noexcept
bool intersects(const QRegion &r) const
Definition qregion.cpp:613
bool isEmpty() const
Returns true if the region is empty; otherwise returns false.
const_iterator begin() const noexcept
QRegion intersected(const QRegion &r) const
QRegion subtracted(const QRegion &r) const
The QSGImageNode class is provided for convenience to easily draw textured content using the QML scen...
\group qtquick-scenegraph-nodes \title Qt Quick Scene Graph Node classes
Definition qsgnode.h:37
The QSGRectangleNode class is a convenience class for drawing solid filled rectangles using scenegrap...
static QSGRenderNodePrivate * get(QSGRenderNode *node)
The QSGRenderNode class represents a set of custom rendering commands targeting the graphics API that...
The QSGSimpleRectNode class is a convenience class for drawing solid filled rectangles using scenegra...
The QSGSimpleTextureNode class is provided for convenience to easily draw textured content using the ...
void setClipRegion(const QRegion &clipRegion, bool hasClipRegion=true)
void setTransform(const QTransform &transform)
QSGSoftwareRenderableNode(NodeType type, QSGNode *node)
void subtractDirtyRegion(const QRegion &dirtyRegion)
QRegion renderNode(QPainter *painter, bool forceOpaquePainting=false)
QRegion previousDirtyRegion(bool wasRemoved=false) const
void addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty=true)
\inmodule QtQuick
Definition qsgtexture.h:20
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
bool isRotating() const
Returns true if the matrix represents some kind of a rotating transformation, otherwise returns false...
Definition qtransform.h:183
QRect mapRect(const QRect &) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QPainter paint
Combined button and popup list for selecting options.
@ ReplaceClip
@ IntersectClip
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:303
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
int qFloor(T v)
Definition qmath.h:42
int qCeil(T v)
Definition qmath.h:36
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLboolean r
[2]
GLuint GLfloat GLfloat GLfloat x1
GLenum type
GLenum GLuint texture
GLuint GLenum GLenum transform
GLfixed GLfixed GLfixed y2
GLfixed GLfixed x2
static const QRectF boundingRect(const QPointF *points, int pointCount)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QT_BEGIN_NAMESPACE QRect toRectMin(const QRectF &r)
QRect toRectMax(const QRectF &r)
QRandomGenerator64 rd
[10]
QPainter painter(this)
[7]
const QRegion * clipRegion() const override
bool scissorEnabled() const override
const QMatrix4x4 * projectionMatrix() const override
bool stencilEnabled() const override
QRect scissorRect() const override
int stencilValue() const override