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 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | #define QT_MINIMUM_DYNAMIC_FBO_SIZE 64U |
16 | |
17 | QSGPainterTexture::QSGPainterTexture() |
18 | : QSGPlainTexture(*(new QSGPlainTexturePrivate(this))) |
19 | { |
20 | m_retain_image = true; |
21 | } |
22 | |
23 | void 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 | |
32 | QSGDefaultPainterNode::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 | |
64 | QSGDefaultPainterNode::~QSGDefaultPainterNode() |
65 | { |
66 | delete m_texture; |
67 | } |
68 | |
69 | void 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 | |
124 | void 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 | |
142 | void 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 | |
151 | void 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 | |
163 | void 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 | |
181 | void 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 | |
193 | void QSGDefaultPainterNode::setSize(const QSize &size) |
194 | { |
195 | if (size == m_size) |
196 | return; |
197 | |
198 | m_size = size; |
199 | m_dirtyGeometry = true; |
200 | } |
201 | |
202 | void 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 | |
213 | void 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 | |
224 | void QSGDefaultPainterNode::setOpaquePainting(bool opaque) |
225 | { |
226 | if (opaque == m_opaquePainting) |
227 | return; |
228 | |
229 | m_opaquePainting = opaque; |
230 | m_dirtyTexture = true; |
231 | } |
232 | |
233 | void 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 | |
245 | void 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 | |
256 | void QSGDefaultPainterNode::setSmoothPainting(bool s) |
257 | { |
258 | if (s == m_smoothPainting) |
259 | return; |
260 | |
261 | m_smoothPainting = s; |
262 | m_dirtyRenderTarget = true; |
263 | } |
264 | |
265 | void QSGDefaultPainterNode::setFillColor(const QColor &c) |
266 | { |
267 | if (c == m_fillColor) |
268 | return; |
269 | |
270 | m_fillColor = c; |
271 | markDirty(bits: DirtyMaterial); |
272 | } |
273 | |
274 | void QSGDefaultPainterNode::setContentsScale(qreal s) |
275 | { |
276 | if (s == m_contentsScale) |
277 | return; |
278 | |
279 | m_contentsScale = s; |
280 | markDirty(bits: DirtyMaterial); |
281 | } |
282 | |
283 | void QSGDefaultPainterNode::setFastFBOResizing(bool fastResizing) |
284 | { |
285 | if (m_fastFBOResizing == fastResizing) |
286 | return; |
287 | |
288 | m_fastFBOResizing = fastResizing; |
289 | } |
290 | |
291 | QImage QSGDefaultPainterNode::toImage() const |
292 | { |
293 | Q_ASSERT(m_actualRenderTarget == QQuickPaintedItem::Image); |
294 | return m_image; |
295 | } |
296 | |
297 | QT_END_NAMESPACE |
298 | |