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
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// Qt-Security score:significant reason:default
4
6
13#if QT_CONFIG(quick_sprite)
14#include "qsgsoftwarespritenode_p.h"
15#endif
16
17#include <qsgsimplerectnode.h>
18#include <qsgsimpletexturenode.h>
19#include <private/qsgrendernode_p.h>
20#include <private/qsgplaintexture_p.h>
21
22#include <qmath.h>
23
24Q_STATIC_LOGGING_CATEGORY(lcRenderable, "qt.scenegraph.softwarecontext.renderable")
25
26QT_BEGIN_NAMESPACE
27
28// Largest subrectangle with integer coordinates
29inline QRect toRectMin(const QRectF & r)
30{
31 int x1 = qCeil(r.left());
32 int x2 = qFloor(r.right());
33 int y1 = qCeil(r.top());
34 int y2 = qFloor(r.bottom());
35 return QRect(x1, y1, x2 - x1, y2 - y1);
36}
37
38// Smallest superrectangle with integer coordinates
39inline QRect toRectMax(const QRectF & r)
40{
41 return r.toAlignedRect();
42}
43
44QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *node)
45 : m_nodeType(type)
46 , m_isOpaque(true)
47 , m_isDirty(true)
48 , m_hasClipRegion(false)
49 , m_opacity(1.0f)
50{
51 switch (m_nodeType) {
52 case QSGSoftwareRenderableNode::SimpleRect:
53 m_handle.simpleRectNode = static_cast<QSGSimpleRectNode*>(node);
54 break;
55 case QSGSoftwareRenderableNode::SimpleTexture:
56 m_handle.simpleTextureNode = static_cast<QSGSimpleTextureNode*>(node);
57 break;
58 case QSGSoftwareRenderableNode::Image:
59 m_handle.imageNode = static_cast<QSGSoftwareInternalImageNode*>(node);
60 break;
61 case QSGSoftwareRenderableNode::Painter:
62 m_handle.painterNode = static_cast<QSGSoftwarePainterNode*>(node);
63 break;
64 case QSGSoftwareRenderableNode::Rectangle:
65 m_handle.rectangleNode = static_cast<QSGSoftwareInternalRectangleNode*>(node);
66 break;
67 case QSGSoftwareRenderableNode::Glyph:
68 m_handle.glpyhNode = static_cast<QSGSoftwareGlyphNode*>(node);
69 break;
70 case QSGSoftwareRenderableNode::NinePatch:
71 m_handle.ninePatchNode = static_cast<QSGSoftwareNinePatchNode*>(node);
72 break;
73 case QSGSoftwareRenderableNode::SimpleRectangle:
74 m_handle.simpleRectangleNode = static_cast<QSGRectangleNode*>(node);
75 break;
76 case QSGSoftwareRenderableNode::SimpleImage:
77 m_handle.simpleImageNode = static_cast<QSGImageNode*>(node);
78 break;
79#if QT_CONFIG(quick_sprite)
80 case QSGSoftwareRenderableNode::SpriteNode:
81 m_handle.spriteNode = static_cast<QSGSoftwareSpriteNode*>(node);
82 break;
83#endif
84 case QSGSoftwareRenderableNode::RenderNode:
85 m_handle.renderNode = static_cast<QSGRenderNode*>(node);
86 break;
87 case QSGSoftwareRenderableNode::Invalid:
88 m_handle.simpleRectNode = nullptr;
89 break;
90 }
91}
92
93QSGSoftwareRenderableNode::~QSGSoftwareRenderableNode()
94{
95
96}
97
98void QSGSoftwareRenderableNode::update()
99{
100 // Update the Node properties
101 m_isDirty = true;
102 m_isOpaque = false;
103
104 QRectF boundingRect;
105
106 switch (m_nodeType) {
107 case QSGSoftwareRenderableNode::SimpleRect:
108 if (m_handle.simpleRectNode->color().alpha() == 255)
109 m_isOpaque = true;
110
111 boundingRect = m_handle.simpleRectNode->rect();
112 break;
113 case QSGSoftwareRenderableNode::SimpleTexture:
114 if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel())
115 m_isOpaque = true;
116
117 boundingRect = m_handle.simpleTextureNode->rect();
118 break;
119 case QSGSoftwareRenderableNode::Image:
120 m_isOpaque = !m_handle.imageNode->pixmap().hasAlphaChannel();
121
122 boundingRect = m_handle.imageNode->rect().toRect();
123 break;
124 case QSGSoftwareRenderableNode::Painter:
125 if (m_handle.painterNode->opaquePainting())
126 m_isOpaque = true;
127
128 boundingRect = QRectF(0, 0, m_handle.painterNode->size().width(), m_handle.painterNode->size().height());
129 break;
130 case QSGSoftwareRenderableNode::Rectangle:
131 if (m_handle.rectangleNode->isOpaque())
132 m_isOpaque = true;
133
134 boundingRect = m_handle.rectangleNode->rect();
135 break;
136 case QSGSoftwareRenderableNode::Glyph:
137 // Always has alpha
138 boundingRect = m_handle.glpyhNode->boundingRect();
139 break;
140 case QSGSoftwareRenderableNode::NinePatch:
141 m_isOpaque = m_handle.ninePatchNode->isOpaque();
142
143 boundingRect = m_handle.ninePatchNode->bounds();
144 break;
145 case QSGSoftwareRenderableNode::SimpleRectangle:
146 if (m_handle.simpleRectangleNode->color().alpha() == 255)
147 m_isOpaque = true;
148
149 boundingRect = m_handle.simpleRectangleNode->rect();
150 break;
151 case QSGSoftwareRenderableNode::SimpleImage:
152 if (!m_handle.simpleImageNode->texture()->hasAlphaChannel())
153 m_isOpaque = true;
154
155 boundingRect = m_handle.simpleImageNode->rect();
156 break;
157#if QT_CONFIG(quick_sprite)
158 case QSGSoftwareRenderableNode::SpriteNode:
159 m_isOpaque = m_handle.spriteNode->isOpaque();
160 boundingRect = m_handle.spriteNode->rect();
161 break;
162#endif
163 case QSGSoftwareRenderableNode::RenderNode:
164 if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering))
165 m_isOpaque = true;
166
167 boundingRect = m_handle.renderNode->rect();
168 break;
169 default:
170 break;
171 }
172
173 if (m_transform.isRotating())
174 m_isOpaque = false;
175
176 const QRectF transformedRect = m_transform.mapRect(boundingRect);
177 m_boundingRectMin = toRectMin(transformedRect);
178 m_boundingRectMax = toRectMax(transformedRect);
179
180 if (m_hasClipRegion && m_clipRegion.rectCount() <= 1) {
181 // If there is a clipRegion, and it is empty, the item wont be rendered
182 if (m_clipRegion.isEmpty()) {
183 m_boundingRectMin = QRect();
184 m_boundingRectMax = QRect();
185 } else {
186 const auto rects = m_clipRegion.begin();
187 m_boundingRectMin = m_boundingRectMin.intersected(rects[0]);
188 m_boundingRectMax = m_boundingRectMax.intersected(rects[0]);
189 }
190 }
191
192 // Overrides
193 if (m_opacity < 1.0f)
194 m_isOpaque = false;
195
196 m_dirtyRegion = QRegion(m_boundingRectMax);
197}
198
200{
201 const QMatrix4x4 *projectionMatrix() const override { return &ident; }
202 QRect scissorRect() const override { return QRect(); }
203 bool scissorEnabled() const override { return false; }
204 int stencilValue() const override { return 0; }
205 bool stencilEnabled() const override { return false; }
206 const QRegion *clipRegion() const override { return &cr; }
209};
210
211QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaquePainting)
212{
213 Q_ASSERT(painter);
214
215 // Check for don't paint conditions
216 if (m_nodeType != RenderNode) {
217 if (!m_isDirty || qFuzzyIsNull(m_opacity) || m_dirtyRegion.isEmpty()) {
218 m_isDirty = false;
219 m_dirtyRegion = QRegion();
220 return QRegion();
221 }
222 } else {
223 if (!m_isDirty || qFuzzyIsNull(m_opacity)) {
224 m_isDirty = false;
225 m_dirtyRegion = QRegion();
226 return QRegion();
227 } else {
228 QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(m_handle.renderNode);
229 rd->m_localMatrix = m_transform;
230 rd->m_matrix = &rd->m_localMatrix;
231 rd->m_opacity = m_opacity;
232
233 // all the clip region below is in world coordinates, taking m_transform into account already
234 QRegion cr = m_dirtyRegion;
235 if (m_clipRegion.rectCount() > 1)
236 cr &= m_clipRegion;
237
238 painter->save();
239 RenderNodeState rs;
240 rs.cr = cr;
241 m_handle.renderNode->render(&rs);
242 painter->restore();
243
244 const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering)
245 ? m_boundingRectMax // already mapped to world
246 : QRect(0, 0, painter->device()->width(), painter->device()->height());
247 m_previousDirtyRegion = QRegion(br);
248 m_isDirty = false;
249 m_dirtyRegion = QRegion();
250 return br;
251 }
252 }
253
254 painter->save();
255 painter->setOpacity(m_opacity);
256
257 // Set clipRegion to m_dirtyRegion (in world coordinates, so must be done before the setTransform below)
258 // as m_dirtyRegion already accounts for clipRegion
259 painter->setClipRegion(m_dirtyRegion, Qt::ReplaceClip);
260 if (m_clipRegion.rectCount() > 1)
261 painter->setClipRegion(m_clipRegion, Qt::IntersectClip);
262
263 painter->setTransform(m_transform, false); //precalculated worldTransform
264 if (forceOpaquePainting || m_isOpaque)
265 painter->setCompositionMode(QPainter::CompositionMode_Source);
266
267 switch (m_nodeType) {
268 case QSGSoftwareRenderableNode::SimpleRect:
269 painter->fillRect(m_handle.simpleRectNode->rect(), m_handle.simpleRectNode->color());
270 break;
271 case QSGSoftwareRenderableNode::SimpleTexture:
272 {
273 QSGTexture *texture = m_handle.simpleTextureNode->texture();
274 if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(texture)) {
275 const QPixmap &pm = pt->pixmap();
276 painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, m_handle.simpleTextureNode->sourceRect());
277 } else if (QSGPlainTexture *pt = qobject_cast<QSGPlainTexture *>(texture)) {
278 const QImage &im = pt->image();
279 painter->drawImage(m_handle.simpleTextureNode->rect(), im, m_handle.simpleTextureNode->sourceRect());
280 }
281 }
282 break;
283 case QSGSoftwareRenderableNode::Image:
284 m_handle.imageNode->paint(painter);
285 break;
286 case QSGSoftwareRenderableNode::Painter:
287 m_handle.painterNode->paint(painter);
288 break;
289 case QSGSoftwareRenderableNode::Rectangle:
290 m_handle.rectangleNode->paint(painter);
291 break;
292 case QSGSoftwareRenderableNode::Glyph:
293 m_handle.glpyhNode->paint(painter);
294 break;
295 case QSGSoftwareRenderableNode::NinePatch:
296 m_handle.ninePatchNode->paint(painter);
297 break;
298 case QSGSoftwareRenderableNode::SimpleRectangle:
299 static_cast<QSGSoftwareRectangleNode *>(m_handle.simpleRectangleNode)->paint(painter);
300 break;
301 case QSGSoftwareRenderableNode::SimpleImage:
302 static_cast<QSGSoftwareImageNode *>(m_handle.simpleImageNode)->paint(painter);
303 break;
304#if QT_CONFIG(quick_sprite)
305 case QSGSoftwareRenderableNode::SpriteNode:
306 static_cast<QSGSoftwareSpriteNode *>(m_handle.spriteNode)->paint(painter);
307 break;
308#endif
309 default:
310 break;
311 }
312
313 painter->restore();
314
315 QRegion areaToBeFlushed = m_dirtyRegion;
316 m_previousDirtyRegion = QRegion(m_boundingRectMax);
317 m_isDirty = false;
318 m_dirtyRegion = QRegion();
319
320 return areaToBeFlushed;
321}
322
323bool QSGSoftwareRenderableNode::isDirtyRegionEmpty() const
324{
325 return m_dirtyRegion.isEmpty();
326}
327
328void QSGSoftwareRenderableNode::setTransform(const QTransform &transform)
329{
330 if (m_transform == transform)
331 return;
332 m_transform = transform;
333 update();
334}
335
336void QSGSoftwareRenderableNode::setClipRegion(const QRegion &clipRect, bool hasClipRegion)
337{
338 if (m_clipRegion == clipRect && m_hasClipRegion == hasClipRegion)
339 return;
340
341 m_clipRegion = clipRect;
342 m_hasClipRegion = hasClipRegion;
343 update();
344}
345
346void QSGSoftwareRenderableNode::setOpacity(float opacity)
347{
348 if (qFuzzyCompare(m_opacity, opacity))
349 return;
350
351 m_opacity = opacity;
352 update();
353}
354
355void QSGSoftwareRenderableNode::markGeometryDirty()
356{
357 update();
358}
359
360void QSGSoftwareRenderableNode::markMaterialDirty()
361{
362 update();
363}
364
365void QSGSoftwareRenderableNode::addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty)
366{
367 // Check if the dirty region applies to this node
368 QRegion prev = m_dirtyRegion;
369 if (dirtyRegion.intersects(m_boundingRectMax)) {
370 if (forceDirty)
371 m_isDirty = true;
372 m_dirtyRegion += dirtyRegion.intersected(m_boundingRectMax);
373 }
374 qCDebug(lcRenderable) << "addDirtyRegion: " << dirtyRegion << "old dirtyRegion: " << prev << "new dirtyRegion: " << m_dirtyRegion;
375}
376
377void QSGSoftwareRenderableNode::subtractDirtyRegion(const QRegion &dirtyRegion)
378{
379 QRegion prev = m_dirtyRegion;
380 if (m_isDirty) {
381 // Check if this rect concerns us
382 if (dirtyRegion.intersects(m_boundingRectMax)) {
383 m_dirtyRegion -= dirtyRegion;
384 if (m_dirtyRegion.isEmpty())
385 m_isDirty = false;
386 }
387 }
388 qCDebug(lcRenderable) << "subtractDirtyRegion: " << dirtyRegion << "old dirtyRegion" << prev << "new dirtyRegion: " << m_dirtyRegion;
389}
390
391QRegion QSGSoftwareRenderableNode::previousDirtyRegion(bool wasRemoved) const
392{
393 // When removing a node, the boundingRect shouldn't be subtracted
394 // because a deleted node has no valid boundingRect
395 if (wasRemoved)
396 return m_previousDirtyRegion;
397
398 return m_previousDirtyRegion.subtracted(QRegion(m_boundingRectMax));
399}
400
401QRegion QSGSoftwareRenderableNode::dirtyRegion() const
402{
403 return m_dirtyRegion;
404}
405
406QT_END_NAMESPACE
QRect toRectMax(const QRectF &r)
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