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

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