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 "qsgdefaultpainternode_p.h"
5
6#include <QtQuick/private/qquickpainteditem_p.h>
7
8#include <QtQuick/private/qsgdefaultrendercontext_p.h>
9#include <QtQuick/private/qsgcontext_p.h>
10#include <qmath.h>
11#include <qpainter.h>
12
13QT_BEGIN_NAMESPACE
14
15#define QT_MINIMUM_DYNAMIC_FBO_SIZE 64U
16
17QSGPainterTexture::QSGPainterTexture()
18 : QSGPlainTexture(*(new QSGPlainTexturePrivate(this)))
19{
20 m_retain_image = true;
21}
22
23void QSGPainterTexture::commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
24{
25 if (!m_dirty_rect.isNull()) {
26 setImage(m_image);
27 m_dirty_rect = QRect();
28 }
29 QSGPlainTexture::commitTextureOperations(rhi, resourceUpdates);
30}
31
32QSGDefaultPainterNode::QSGDefaultPainterNode(QQuickPaintedItem *item)
33 : QSGPainterNode()
34 , m_preferredRenderTarget(QQuickPaintedItem::Image)
35 , m_actualRenderTarget(QQuickPaintedItem::Image)
36 , m_item(item)
37 , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
38 , m_texture(nullptr)
39 , m_fillColor(Qt::transparent)
40 , m_contentsScale(1.0)
41 , m_dirtyContents(false)
42 , m_opaquePainting(false)
43 , m_linear_filtering(false)
44 , m_mipmapping(false)
45 , m_smoothPainting(false)
46 , m_multisamplingSupported(false)
47 , m_fastFBOResizing(false)
48 , m_dirtyGeometry(false)
49 , m_dirtyRenderTarget(false)
50 , m_dirtyTexture(false)
51{
52 Q_UNUSED(m_multisamplingSupported);
53 m_context = static_cast<QSGDefaultRenderContext *>(static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(o: item))->sceneGraphRenderContext());
54
55 setMaterial(&m_materialO);
56 setOpaqueMaterial(&m_material);
57 setGeometry(&m_geometry);
58
59#ifdef QSG_RUNTIME_DESCRIPTION
60 qsgnode_set_description(node: this, description: QString::fromLatin1(ba: "QQuickPaintedItem(%1):%2").arg(a: QString::fromLatin1(ba: item->metaObject()->className())).arg(a: item->objectName()));
61#endif
62}
63
64QSGDefaultPainterNode::~QSGDefaultPainterNode()
65{
66 delete m_texture;
67}
68
69void QSGDefaultPainterNode::paint()
70{
71 QRect dirtyRect = m_dirtyRect.isNull() ? QRect(0, 0, m_size.width(), m_size.height()) : m_dirtyRect;
72
73 QPainter painter;
74 Q_ASSERT(m_actualRenderTarget == QQuickPaintedItem::Image);
75 if (m_image.isNull())
76 return;
77 painter.begin(&m_image);
78
79 if (m_smoothPainting) {
80 painter.setRenderHints(hints: QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
81 }
82
83 QRect clipRect;
84 QRect dirtyTextureRect;
85
86 if (m_contentsScale == 1) {
87 qreal scaleX = m_textureSize.width() / (qreal) m_size.width();
88 qreal scaleY = m_textureSize.height() / (qreal) m_size.height();
89 painter.scale(sx: scaleX, sy: scaleY);
90 clipRect = dirtyRect;
91 dirtyTextureRect = QRectF(dirtyRect.x() * scaleX,
92 dirtyRect.y() * scaleY,
93 dirtyRect.width() * scaleX,
94 dirtyRect.height() * scaleY).toAlignedRect();
95 } else {
96 painter.scale(sx: m_contentsScale, sy: m_contentsScale);
97 QRect sclip(qFloor(v: dirtyRect.x()/m_contentsScale),
98 qFloor(v: dirtyRect.y()/m_contentsScale),
99 qCeil(v: dirtyRect.width()/m_contentsScale+dirtyRect.x()/m_contentsScale-qFloor(v: dirtyRect.x()/m_contentsScale)),
100 qCeil(v: dirtyRect.height()/m_contentsScale+dirtyRect.y()/m_contentsScale-qFloor(v: dirtyRect.y()/m_contentsScale)));
101 clipRect = sclip;
102 dirtyTextureRect = dirtyRect;
103 }
104
105 // only clip if we were originally updating only a subrect
106 if (!m_dirtyRect.isNull()) {
107 painter.setClipRect(clipRect);
108 }
109
110 painter.setCompositionMode(QPainter::CompositionMode_Source);
111 if (m_fillColor.isValid())
112 painter.fillRect(clipRect, color: m_fillColor);
113 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
114
115 m_item->paint(painter: &painter);
116 painter.end();
117
118 m_texture->setImage(m_image);
119 m_texture->setDirtyRect(dirtyTextureRect);
120
121 m_dirtyRect = QRect();
122}
123
124void QSGDefaultPainterNode::update()
125{
126 if (m_dirtyRenderTarget)
127 updateRenderTarget();
128 if (m_dirtyGeometry)
129 updateGeometry();
130 if (m_dirtyTexture)
131 updateTexture();
132
133 if (m_dirtyContents)
134 paint();
135
136 m_dirtyGeometry = false;
137 m_dirtyRenderTarget = false;
138 m_dirtyTexture = false;
139 m_dirtyContents = false;
140}
141
142void QSGDefaultPainterNode::updateTexture()
143{
144 m_texture->setHasAlphaChannel(!m_opaquePainting);
145 m_material.setTexture(m_texture);
146 m_materialO.setTexture(m_texture);
147
148 markDirty(bits: DirtyMaterial);
149}
150
151void QSGDefaultPainterNode::updateGeometry()
152{
153 QRectF source(0, 0, 1, 1);
154 QRectF dest(0, 0, m_size.width(), m_size.height());
155 if (m_actualRenderTarget == QQuickPaintedItem::InvertedYFramebufferObject)
156 dest = QRectF(QPointF(0, m_size.height()), QPointF(m_size.width(), 0));
157 QSGGeometry::updateTexturedRectGeometry(g: &m_geometry,
158 rect: dest,
159 sourceRect: source);
160 markDirty(bits: DirtyGeometry);
161}
162
163void QSGDefaultPainterNode::updateRenderTarget()
164{
165 m_dirtyContents = true;
166
167 m_actualRenderTarget = QQuickPaintedItem::Image;
168 if (!m_image.isNull() && !m_dirtyGeometry)
169 return;
170
171 m_image = QImage(m_textureSize, QImage::Format_RGBA8888_Premultiplied);
172 m_image.fill(color: Qt::transparent);
173
174 if (!m_texture) {
175 m_texture = new QSGPainterTexture;
176 m_texture->setOwnsTexture(true);
177 }
178 m_texture->setTextureSize(m_textureSize);
179}
180
181void QSGDefaultPainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target)
182{
183 if (m_preferredRenderTarget == target)
184 return;
185
186 m_preferredRenderTarget = target;
187
188 m_dirtyRenderTarget = true;
189 m_dirtyGeometry = true;
190 m_dirtyTexture = true;
191}
192
193void QSGDefaultPainterNode::setSize(const QSize &size)
194{
195 if (size == m_size)
196 return;
197
198 m_size = size;
199 m_dirtyGeometry = true;
200}
201
202void QSGDefaultPainterNode::setTextureSize(const QSize &size)
203{
204 if (size == m_textureSize)
205 return;
206
207 m_textureSize = size;
208 m_dirtyRenderTarget = true;
209 m_dirtyGeometry = true;
210 m_dirtyTexture = true;
211}
212
213void QSGDefaultPainterNode::setDirty(const QRect &dirtyRect)
214{
215 m_dirtyContents = true;
216 m_dirtyRect = dirtyRect;
217
218 if (m_mipmapping)
219 m_dirtyTexture = true;
220
221 markDirty(bits: DirtyMaterial);
222}
223
224void QSGDefaultPainterNode::setOpaquePainting(bool opaque)
225{
226 if (opaque == m_opaquePainting)
227 return;
228
229 m_opaquePainting = opaque;
230 m_dirtyTexture = true;
231}
232
233void QSGDefaultPainterNode::setLinearFiltering(bool linearFiltering)
234{
235 if (linearFiltering == m_linear_filtering)
236 return;
237
238 m_linear_filtering = linearFiltering;
239
240 m_material.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
241 m_materialO.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
242 markDirty(bits: DirtyMaterial);
243}
244
245void QSGDefaultPainterNode::setMipmapping(bool mipmapping)
246{
247 if (mipmapping == m_mipmapping)
248 return;
249
250 m_mipmapping = mipmapping;
251 m_material.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
252 m_materialO.setMipmapFiltering(mipmapping ? QSGTexture::Linear : QSGTexture::None);
253 m_dirtyTexture = true;
254}
255
256void QSGDefaultPainterNode::setSmoothPainting(bool s)
257{
258 if (s == m_smoothPainting)
259 return;
260
261 m_smoothPainting = s;
262 m_dirtyRenderTarget = true;
263}
264
265void QSGDefaultPainterNode::setFillColor(const QColor &c)
266{
267 if (c == m_fillColor)
268 return;
269
270 m_fillColor = c;
271 markDirty(bits: DirtyMaterial);
272}
273
274void QSGDefaultPainterNode::setContentsScale(qreal s)
275{
276 if (s == m_contentsScale)
277 return;
278
279 m_contentsScale = s;
280 markDirty(bits: DirtyMaterial);
281}
282
283void QSGDefaultPainterNode::setFastFBOResizing(bool fastResizing)
284{
285 if (m_fastFBOResizing == fastResizing)
286 return;
287
288 m_fastFBOResizing = fastResizing;
289}
290
291QImage QSGDefaultPainterNode::toImage() const
292{
293 Q_ASSERT(m_actualRenderTarget == QQuickPaintedItem::Image);
294 return m_image;
295}
296
297QT_END_NAMESPACE
298

source code of qtdeclarative/src/quick/scenegraph/util/qsgdefaultpainternode.cpp