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