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
4#include "qsgsoftwarerenderablenode_p.h"
5
6#include "qsgsoftwareinternalimagenode_p.h"
7#include "qsgsoftwareinternalrectanglenode_p.h"
8#include "qsgsoftwareglyphnode_p.h"
9#include "qsgsoftwarepublicnodes_p.h"
10#include "qsgsoftwarepainternode_p.h"
11#include "qsgsoftwarepixmaptexture_p.h"
12#if QT_CONFIG(quick_sprite)
13#include "qsgsoftwarespritenode_p.h"
14#endif
15
16#include <qsgsimplerectnode.h>
17#include <qsgsimpletexturenode.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
25QT_BEGIN_NAMESPACE
26
27// Largest subrectangle with integer coordinates
28inline QRect toRectMin(const QRectF & r)
29{
30 int x1 = qCeil(v: r.left());
31 int x2 = qFloor(v: r.right());
32 int y1 = qCeil(v: r.top());
33 int y2 = qFloor(v: 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
43QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *node)
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) {
51 case QSGSoftwareRenderableNode::SimpleRect:
52 m_handle.simpleRectNode = static_cast<QSGSimpleRectNode*>(node);
53 break;
54 case QSGSoftwareRenderableNode::SimpleTexture:
55 m_handle.simpleTextureNode = static_cast<QSGSimpleTextureNode*>(node);
56 break;
57 case QSGSoftwareRenderableNode::Image:
58 m_handle.imageNode = static_cast<QSGSoftwareInternalImageNode*>(node);
59 break;
60 case QSGSoftwareRenderableNode::Painter:
61 m_handle.painterNode = static_cast<QSGSoftwarePainterNode*>(node);
62 break;
63 case QSGSoftwareRenderableNode::Rectangle:
64 m_handle.rectangleNode = static_cast<QSGSoftwareInternalRectangleNode*>(node);
65 break;
66 case QSGSoftwareRenderableNode::Glyph:
67 m_handle.glpyhNode = static_cast<QSGSoftwareGlyphNode*>(node);
68 break;
69 case QSGSoftwareRenderableNode::NinePatch:
70 m_handle.ninePatchNode = static_cast<QSGSoftwareNinePatchNode*>(node);
71 break;
72 case QSGSoftwareRenderableNode::SimpleRectangle:
73 m_handle.simpleRectangleNode = static_cast<QSGRectangleNode*>(node);
74 break;
75 case QSGSoftwareRenderableNode::SimpleImage:
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
83 case QSGSoftwareRenderableNode::RenderNode:
84 m_handle.renderNode = static_cast<QSGRenderNode*>(node);
85 break;
86 case QSGSoftwareRenderableNode::Invalid:
87 m_handle.simpleRectNode = nullptr;
88 break;
89 }
90}
91
92QSGSoftwareRenderableNode::~QSGSoftwareRenderableNode()
93{
94
95}
96
97void QSGSoftwareRenderableNode::update()
98{
99 // Update the Node properties
100 m_isDirty = true;
101 m_isOpaque = false;
102
103 QRectF boundingRect;
104
105 switch (m_nodeType) {
106 case QSGSoftwareRenderableNode::SimpleRect:
107 if (m_handle.simpleRectNode->color().alpha() == 255)
108 m_isOpaque = true;
109
110 boundingRect = m_handle.simpleRectNode->rect();
111 break;
112 case QSGSoftwareRenderableNode::SimpleTexture:
113 if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel())
114 m_isOpaque = true;
115
116 boundingRect = m_handle.simpleTextureNode->rect();
117 break;
118 case QSGSoftwareRenderableNode::Image:
119 m_isOpaque = !m_handle.imageNode->pixmap().hasAlphaChannel();
120
121 boundingRect = m_handle.imageNode->rect().toRect();
122 break;
123 case QSGSoftwareRenderableNode::Painter:
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;
129 case QSGSoftwareRenderableNode::Rectangle:
130 if (m_handle.rectangleNode->isOpaque())
131 m_isOpaque = true;
132
133 boundingRect = m_handle.rectangleNode->rect();
134 break;
135 case QSGSoftwareRenderableNode::Glyph:
136 // Always has alpha
137 boundingRect = m_handle.glpyhNode->boundingRect();
138 break;
139 case QSGSoftwareRenderableNode::NinePatch:
140 m_isOpaque = m_handle.ninePatchNode->isOpaque();
141
142 boundingRect = m_handle.ninePatchNode->bounds();
143 break;
144 case QSGSoftwareRenderableNode::SimpleRectangle:
145 if (m_handle.simpleRectangleNode->color().alpha() == 255)
146 m_isOpaque = true;
147
148 boundingRect = m_handle.simpleRectangleNode->rect();
149 break;
150 case QSGSoftwareRenderableNode::SimpleImage:
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
162 case QSGSoftwareRenderableNode::RenderNode:
163 if (m_handle.renderNode->flags().testFlag(flag: 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(r: transformedRect);
177 m_boundingRectMax = toRectMax(r: 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(other: rects[0]);
187 m_boundingRectMax = m_boundingRectMax.intersected(other: 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
198struct RenderNodeState : public QSGRenderNode::RenderState
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; }
206 QMatrix4x4 ident;
207 QRegion cr;
208};
209
210QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaquePainting)
211{
212 Q_ASSERT(painter);
213
214 // Check for don't paint conditions
215 if (m_nodeType != RenderNode) {
216 if (!m_isDirty || qFuzzyIsNull(f: m_opacity) || m_dirtyRegion.isEmpty()) {
217 m_isDirty = false;
218 m_dirtyRegion = QRegion();
219 return QRegion();
220 }
221 } else {
222 if (!m_isDirty || qFuzzyIsNull(f: m_opacity)) {
223 m_isDirty = false;
224 m_dirtyRegion = QRegion();
225 return QRegion();
226 } else {
227 QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(node: 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();
238 RenderNodeState rs;
239 rs.cr = cr;
240 m_handle.renderNode->render(state: &rs);
241 painter->restore();
242
243 const QRect br = m_handle.renderNode->flags().testFlag(flag: 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, op: Qt::ReplaceClip);
259 if (m_clipRegion.rectCount() > 1)
260 painter->setClipRegion(m_clipRegion, op: Qt::IntersectClip);
261
262 painter->setTransform(transform: m_transform, combine: false); //precalculated worldTransform
263 if (forceOpaquePainting || m_isOpaque)
264 painter->setCompositionMode(QPainter::CompositionMode_Source);
265
266 switch (m_nodeType) {
267 case QSGSoftwareRenderableNode::SimpleRect:
268 painter->fillRect(m_handle.simpleRectNode->rect(), color: m_handle.simpleRectNode->color());
269 break;
270 case QSGSoftwareRenderableNode::SimpleTexture:
271 {
272 QSGTexture *texture = m_handle.simpleTextureNode->texture();
273 if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(object: texture)) {
274 const QPixmap &pm = pt->pixmap();
275 painter->drawPixmap(targetRect: m_handle.simpleTextureNode->rect(), pixmap: pm, sourceRect: m_handle.simpleTextureNode->sourceRect());
276 } else if (QSGPlainTexture *pt = qobject_cast<QSGPlainTexture *>(object: texture)) {
277 const QImage &im = pt->image();
278 painter->drawImage(targetRect: m_handle.simpleTextureNode->rect(), image: im, sourceRect: m_handle.simpleTextureNode->sourceRect());
279 }
280 }
281 break;
282 case QSGSoftwareRenderableNode::Image:
283 m_handle.imageNode->paint(painter);
284 break;
285 case QSGSoftwareRenderableNode::Painter:
286 m_handle.painterNode->paint(painter);
287 break;
288 case QSGSoftwareRenderableNode::Rectangle:
289 m_handle.rectangleNode->paint(painter);
290 break;
291 case QSGSoftwareRenderableNode::Glyph:
292 m_handle.glpyhNode->paint(painter);
293 break;
294 case QSGSoftwareRenderableNode::NinePatch:
295 m_handle.ninePatchNode->paint(painter);
296 break;
297 case QSGSoftwareRenderableNode::SimpleRectangle:
298 static_cast<QSGSoftwareRectangleNode *>(m_handle.simpleRectangleNode)->paint(painter);
299 break;
300 case QSGSoftwareRenderableNode::SimpleImage:
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
322bool QSGSoftwareRenderableNode::isDirtyRegionEmpty() const
323{
324 return m_dirtyRegion.isEmpty();
325}
326
327void QSGSoftwareRenderableNode::setTransform(const QTransform &transform)
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
345void QSGSoftwareRenderableNode::setOpacity(float opacity)
346{
347 if (qFuzzyCompare(p1: m_opacity, p2: opacity))
348 return;
349
350 m_opacity = opacity;
351 update();
352}
353
354void QSGSoftwareRenderableNode::markGeometryDirty()
355{
356 update();
357}
358
359void QSGSoftwareRenderableNode::markMaterialDirty()
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(r: m_boundingRectMax)) {
369 if (forceDirty)
370 m_isDirty = true;
371 m_dirtyRegion += dirtyRegion.intersected(r: m_boundingRectMax);
372 }
373 qCDebug(lcRenderable) << "addDirtyRegion: " << dirtyRegion << "old dirtyRegion: " << prev << "new dirtyRegion: " << m_dirtyRegion;
374}
375
376void QSGSoftwareRenderableNode::subtractDirtyRegion(const QRegion &dirtyRegion)
377{
378 QRegion prev = m_dirtyRegion;
379 if (m_isDirty) {
380 // Check if this rect concerns us
381 if (dirtyRegion.intersects(r: 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
390QRegion QSGSoftwareRenderableNode::previousDirtyRegion(bool wasRemoved) const
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(r: QRegion(m_boundingRectMax));
398}
399
400QRegion QSGSoftwareRenderableNode::dirtyRegion() const
401{
402 return m_dirtyRegion;
403}
404
405QT_END_NAMESPACE
406

source code of qtdeclarative/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp