1// Copyright (C) 2023 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 "qsgcurvefillnode_p_p.h"
5#include "qsgcurvefillnode_p.h"
6#include "util/qsggradientcache_p.h"
7
8#include <private/qsgtexture_p.h>
9#include <private/qsgplaintexture_p.h>
10
11QT_BEGIN_NAMESPACE
12
13namespace {
14
15 class QSGCurveFillMaterialShader : public QSGMaterialShader
16 {
17 public:
18 QSGCurveFillMaterialShader(QGradient::Type gradientType,
19 bool useTextureFill,
20 bool useDerivatives,
21 int viewCount);
22
23 bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
24 void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
25 QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
26 };
27
28 QSGCurveFillMaterialShader::QSGCurveFillMaterialShader(QGradient::Type gradientType,
29 bool useTextureFill,
30 bool useDerivatives,
31 int viewCount)
32 {
33 QString baseName = QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shapecurve");
34
35 if (gradientType == QGradient::LinearGradient) {
36 baseName += QStringLiteral("_lg");
37 } else if (gradientType == QGradient::RadialGradient) {
38 baseName += QStringLiteral("_rg");
39 } else if (gradientType == QGradient::ConicalGradient) {
40 baseName += QStringLiteral("_cg");
41 } else if (useTextureFill) {
42 baseName += QStringLiteral("_tf");
43 }
44
45 if (useDerivatives)
46 baseName += QStringLiteral("_derivatives");
47
48 setShaderFileName(stage: VertexStage, filename: baseName + QStringLiteral(".vert.qsb"), viewCount);
49 setShaderFileName(stage: FragmentStage, filename: baseName + QStringLiteral(".frag.qsb"), viewCount);
50 }
51
52 void QSGCurveFillMaterialShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
53 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
54 {
55 Q_UNUSED(oldMaterial);
56 QSGCurveFillMaterial *m = static_cast<QSGCurveFillMaterial *>(newMaterial);
57 const QSGCurveFillNode *node = m->node();
58 if (binding != 1
59 || (node->gradientType() == QGradient::NoGradient && node->fillTextureProvider() == nullptr)) {
60 return;
61 }
62
63 QSGTexture *t = nullptr;
64 if (node->gradientType() != QGradient::NoGradient) {
65 const QSGGradientCacheKey cacheKey(node->fillGradient()->stops,
66 node->fillGradient()->spread);
67 t = QSGGradientCache::cacheForRhi(rhi: state.rhi())->get(grad: cacheKey);
68 } else if (node->fillTextureProvider() != nullptr) {
69 t = node->fillTextureProvider()->texture();
70 if (t != nullptr && t->isAtlasTexture()) {
71 // Create a non-atlas copy to make texture coordinate wrapping work. This
72 // texture copy is owned by the QSGTexture so memory is managed with the original
73 // texture provider.
74 QSGTexture *newTexture = t->removedFromAtlas(resourceUpdates: state.resourceUpdateBatch());
75 if (newTexture != nullptr)
76 t = newTexture;
77 }
78
79 }
80
81 if (t != nullptr) {
82 t->commitTextureOperations(rhi: state.rhi(), resourceUpdates: state.resourceUpdateBatch());
83 } else {
84 if (m->dummyTexture() == nullptr) {
85 QSGPlainTexture *dummyTexture = new QSGPlainTexture;
86 dummyTexture->setFiltering(QSGTexture::Nearest);
87 dummyTexture->setHorizontalWrapMode(QSGTexture::Repeat);
88 dummyTexture->setVerticalWrapMode(QSGTexture::Repeat);
89 QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
90 img.fill(pixel: 0);
91 dummyTexture->setImage(img);
92 dummyTexture->commitTextureOperations(rhi: state.rhi(), resourceUpdates: state.resourceUpdateBatch());
93
94 m->setDummyTexture(dummyTexture);
95 }
96
97 t = m->dummyTexture();
98 }
99
100 *texture = t;
101 }
102
103 bool QSGCurveFillMaterialShader::updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
104 {
105 bool changed = false;
106 QByteArray *buf = state.uniformData();
107 Q_ASSERT(buf->size() >= 80);
108 const int matrixCount = qMin(a: state.projectionMatrixCount(), b: newEffect->viewCount());
109
110 int offset = 0;
111 float matrixScale = 0.0f;
112 if (state.isMatrixDirty()) {
113 for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
114 const QMatrix4x4 m = state.combinedMatrix(index: viewIndex);
115 memcpy(dest: buf->data() + offset + viewIndex * 64, src: m.constData(), n: 64);
116 }
117
118 matrixScale = qSqrt(v: qAbs(t: state.determinant()));
119 memcpy(dest: buf->data() + offset + newEffect->viewCount() * 64, src: &matrixScale, n: 4);
120
121 changed = true;
122 }
123 offset += newEffect->viewCount() * 64 + 4;
124
125 if (state.isOpacityDirty()) {
126 const float opacity = state.opacity();
127 memcpy(dest: buf->data() + offset, src: &opacity, n: 4);
128 changed = true;
129 }
130 offset += 4;
131
132 QSGCurveFillMaterial *newMaterial = static_cast<QSGCurveFillMaterial *>(newEffect);
133 QSGCurveFillMaterial *oldMaterial = static_cast<QSGCurveFillMaterial *>(oldEffect);
134
135 QSGCurveFillNode *newNode = newMaterial != nullptr ? newMaterial->node() : nullptr;
136 QSGCurveFillNode *oldNode = oldMaterial != nullptr ? oldMaterial->node() : nullptr;
137
138 if (newNode == nullptr)
139 return changed;
140
141 if (oldNode == nullptr || oldNode->debug() != newNode->debug()) {
142 float debug = newNode->debug();
143 memcpy(dest: buf->data() + offset, src: &debug, n: 4);
144 changed = true;
145 }
146 offset += 8;
147
148 if (newNode->gradientType() == QGradient::NoGradient
149 && newNode->fillTextureProvider() == nullptr) {
150 Q_ASSERT(buf->size() >= offset + 16);
151
152 QVector4D newColor = QVector4D(newNode->color().redF(),
153 newNode->color().greenF(),
154 newNode->color().blueF(),
155 newNode->color().alphaF());
156 QVector4D oldColor = oldNode != nullptr
157 ? QVector4D(oldNode->color().redF(),
158 oldNode->color().greenF(),
159 oldNode->color().blueF(),
160 oldNode->color().alphaF())
161 : QVector4D{};
162
163 if (oldNode == nullptr || oldColor != newColor) {
164 memcpy(dest: buf->data() + offset, src: &newColor, n: 16);
165 changed = true;
166 }
167
168 offset += 16;
169 } else {
170 Q_ASSERT(buf->size() >= offset + 64);
171
172 if (!oldNode || *oldNode->fillTransform() != *newNode->fillTransform()) {
173 memcpy(dest: buf->data() + offset, src: newNode->fillTransform()->invertedData(), n: 64);
174 changed = true;
175 }
176
177 offset += 64;
178 }
179
180 if (newNode->gradientType() == QGradient::NoGradient
181 && newNode->fillTextureProvider() != nullptr) {
182 Q_ASSERT(buf->size() >= offset + 8);
183 const QSizeF newTextureSize = newNode->fillTextureProvider()->texture() != nullptr
184 ? newNode->fillTextureProvider()->texture()->textureSize()
185 : QSizeF(0, 0);
186 const QVector2D newBoundsSize(newTextureSize.width() / state.devicePixelRatio(),
187 newTextureSize.height() / state.devicePixelRatio());
188 const QVector2D oldBoundsSize = oldNode != nullptr
189 ? oldNode->boundsSize()
190 : QVector2D{};
191
192 if (oldEffect == nullptr || newBoundsSize != oldBoundsSize) {
193 newNode->setBoundsSize(newBoundsSize);
194 memcpy(dest: buf->data() + offset, src: &newBoundsSize, n: 8);
195 changed = true;
196 }
197 offset += 8;
198
199 } else if (newNode->gradientType() == QGradient::LinearGradient) {
200 Q_ASSERT(buf->size() >= offset + 8 + 8);
201
202 QVector2D newGradientStart = QVector2D(newNode->fillGradient()->a);
203 QVector2D oldGradientStart = oldNode != nullptr
204 ? QVector2D(oldNode->fillGradient()->a)
205 : QVector2D{};
206
207 if (newGradientStart != oldGradientStart || oldEffect == nullptr) {
208 memcpy(dest: buf->data() + offset, src: &newGradientStart, n: 8);
209 changed = true;
210 }
211 offset += 8;
212
213 QVector2D newGradientEnd = QVector2D(newNode->fillGradient()->b);
214 QVector2D oldGradientEnd = oldNode!= nullptr
215 ? QVector2D(oldNode->fillGradient()->b)
216 : QVector2D{};
217
218 if (newGradientEnd != oldGradientEnd || oldEffect == nullptr) {
219 memcpy(dest: buf->data() + offset, src: &newGradientEnd, n: 8);
220 changed = true;
221 }
222
223 offset += 8;
224 } else if (newNode->gradientType() == QGradient::RadialGradient) {
225 Q_ASSERT(buf->size() >= offset + 8 + 8 + 4 + 4);
226
227 QVector2D newFocalPoint = QVector2D(newNode->fillGradient()->b);
228 QVector2D oldFocalPoint = oldNode != nullptr
229 ? QVector2D(oldNode->fillGradient()->b)
230 : QVector2D{};
231 if (oldNode == nullptr || newFocalPoint != oldFocalPoint) {
232 memcpy(dest: buf->data() + offset, src: &newFocalPoint, n: 8);
233 changed = true;
234 }
235 offset += 8;
236
237 QVector2D newCenterPoint = QVector2D(newNode->fillGradient()->a);
238 QVector2D oldCenterPoint = oldNode != nullptr
239 ? QVector2D(oldNode->fillGradient()->a)
240 : QVector2D{};
241
242 QVector2D newCenterToFocal = newCenterPoint - newFocalPoint;
243 QVector2D oldCenterToFocal = oldCenterPoint - oldFocalPoint;
244 if (oldNode == nullptr || newCenterToFocal != oldCenterToFocal) {
245 memcpy(dest: buf->data() + offset, src: &newCenterToFocal, n: 8);
246 changed = true;
247 }
248 offset += 8;
249
250 float newCenterRadius = newNode->fillGradient()->v0;
251 float oldCenterRadius = oldNode != nullptr
252 ? oldNode->fillGradient()->v0
253 : 0.0f;
254 if (oldNode == nullptr || !qFuzzyCompare(p1: newCenterRadius, p2: oldCenterRadius)) {
255 memcpy(dest: buf->data() + offset, src: &newCenterRadius, n: 4);
256 changed = true;
257 }
258 offset += 4;
259
260 float newFocalRadius = newNode->fillGradient()->v1;
261 float oldFocalRadius = oldNode != nullptr
262 ? oldNode->fillGradient()->v1
263 : 0.0f;
264 if (oldNode == nullptr || !qFuzzyCompare(p1: newFocalRadius, p2: oldFocalRadius)) {
265 memcpy(dest: buf->data() + offset, src: &newFocalRadius, n: 4);
266 changed = true;
267 }
268 offset += 4;
269
270 } else if (newNode->gradientType() == QGradient::ConicalGradient) {
271 Q_ASSERT(buf->size() >= offset + 8 + 4);
272
273 QVector2D newFocalPoint = QVector2D(newNode->fillGradient()->a);
274 QVector2D oldFocalPoint = oldNode != nullptr
275 ? QVector2D(oldNode->fillGradient()->a)
276 : QVector2D{};
277 if (oldNode == nullptr || newFocalPoint != oldFocalPoint) {
278 memcpy(dest: buf->data() + offset, src: &newFocalPoint, n: 8);
279 changed = true;
280 }
281 offset += 8;
282
283 float newAngle = newNode->fillGradient()->v0;
284 float oldAngle = oldNode != nullptr
285 ? oldNode->fillGradient()->v0
286 : 0.0f;
287 if (oldNode == nullptr || !qFuzzyCompare(p1: newAngle, p2: oldAngle)) {
288 newAngle = -qDegreesToRadians(degrees: newAngle);
289 memcpy(dest: buf->data() + offset, src: &newAngle, n: 4);
290 changed = true;
291 }
292 offset += 4;
293 }
294
295 return changed;
296 }
297
298}
299
300QSGCurveFillMaterial::QSGCurveFillMaterial(QSGCurveFillNode *node)
301 : m_node(node)
302{
303 setFlag(flags: Blending, on: true);
304 setFlag(flags: RequiresDeterminant, on: true);
305}
306
307QSGCurveFillMaterial::~QSGCurveFillMaterial()
308{
309 delete m_dummyTexture;
310}
311
312int QSGCurveFillMaterial::compare(const QSGMaterial *other) const
313{
314 if (other->type() != type())
315 return (type() - other->type());
316
317 const QSGCurveFillMaterial *otherMaterial =
318 static_cast<const QSGCurveFillMaterial *>(other);
319
320 QSGCurveFillNode *a = node();
321 QSGCurveFillNode *b = otherMaterial->node();
322 if (a == b)
323 return 0;
324
325 if (a->gradientType() == QGradient::NoGradient && a->fillTextureProvider() == nullptr) {
326 if (int d = a->color().red() - b->color().red())
327 return d;
328 if (int d = a->color().green() - b->color().green())
329 return d;
330 if (int d = a->color().blue() - b->color().blue())
331 return d;
332 if (int d = a->color().alpha() - b->color().alpha())
333 return d;
334 } else {
335 if (a->gradientType() != QGradient::NoGradient) {
336 const QSGGradientCache::GradientDesc &ga = *a->fillGradient();
337 const QSGGradientCache::GradientDesc &gb = *b->fillGradient();
338
339 if (int d = ga.a.x() - gb.a.x())
340 return d;
341 if (int d = ga.a.y() - gb.a.y())
342 return d;
343 if (int d = ga.b.x() - gb.b.x())
344 return d;
345 if (int d = ga.b.y() - gb.b.y())
346 return d;
347
348 if (int d = ga.v0 - gb.v0)
349 return d;
350 if (int d = ga.v1 - gb.v1)
351 return d;
352
353 if (int d = ga.spread - gb.spread)
354 return d;
355
356 if (int d = ga.stops.size() - gb.stops.size())
357 return d;
358
359 for (int i = 0; i < ga.stops.size(); ++i) {
360 if (int d = ga.stops[i].first - gb.stops[i].first)
361 return d;
362 if (int d = ga.stops[i].second.rgba() - gb.stops[i].second.rgba())
363 return d;
364 }
365 }
366
367 if (int d = a->fillTransform()->compareTo(other: *b->fillTransform()))
368 return d;
369 }
370
371 const qintptr diff = qintptr(a->fillTextureProvider()) - qintptr(b->fillTextureProvider());
372 return diff < 0 ? -1 : (diff > 0 ? 1 : 0);
373}
374
375QSGMaterialType *QSGCurveFillMaterial::type() const
376{
377 static QSGMaterialType type[5];
378 uint index = node()->gradientType();
379 Q_ASSERT((index & ~3) == 0); // Only two first bits for gradient type
380
381 if (node()->gradientType() == QGradient::NoGradient && node()->fillTextureProvider() != nullptr)
382 index = 5;
383
384 return &type[index];
385}
386
387QSGMaterialShader *QSGCurveFillMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
388{
389 return new QSGCurveFillMaterialShader(node()->gradientType(),
390 node()->gradientType() == QGradient::NoGradient
391 && node()->fillTextureProvider() != nullptr,
392 renderMode == QSGRendererInterface::RenderMode3D,
393 viewCount());
394}
395
396QT_END_NAMESPACE
397

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtdeclarative/src/quick/scenegraph/qsgcurvefillnode_p.cpp