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 "qquickshapecurvenode_p_p.h"
5#include "qquickshapecurvenode_p.h"
6
7#include "qquickshapegenericrenderer_p.h"
8
9QT_BEGIN_NAMESPACE
10
11namespace {
12
13 class QQuickShapeCurveMaterialShader : public QSGMaterialShader
14 {
15 public:
16 QQuickShapeCurveMaterialShader(QQuickAbstractPathRenderer::FillGradientType gradientType,
17 bool includeStroke,
18 bool useDerivatives);
19
20 bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
21 void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
22 QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
23 };
24
25 QQuickShapeCurveMaterialShader::QQuickShapeCurveMaterialShader(QQuickAbstractPathRenderer::FillGradientType gradientType,
26 bool includeStroke,
27 bool useDerivatives)
28 {
29 QString baseName = QStringLiteral(":/qt-project.org/shapes/shaders_ng/shapecurve");
30
31 if (gradientType == QQuickAbstractPathRenderer::LinearGradient) {
32 baseName += QStringLiteral("_lg");
33 } else if (gradientType == QQuickAbstractPathRenderer::RadialGradient) {
34 baseName += QStringLiteral("_rg");
35 } else if (gradientType == QQuickAbstractPathRenderer::ConicalGradient) {
36 baseName += QStringLiteral("_cg");
37 }
38
39 if (includeStroke)
40 baseName += QStringLiteral("_stroke");
41
42 if (useDerivatives)
43 baseName += QStringLiteral("_derivatives");
44
45 setShaderFileName(stage: VertexStage, filename: baseName + QStringLiteral(".vert.qsb"));
46 setShaderFileName(stage: FragmentStage, filename: baseName + QStringLiteral(".frag.qsb"));
47 }
48
49 void QQuickShapeCurveMaterialShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
50 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
51 {
52 Q_UNUSED(oldMaterial);
53 const QQuickShapeCurveMaterial *m = static_cast<QQuickShapeCurveMaterial *>(newMaterial);
54 const QQuickShapeCurveNode *node = m->node();
55 if (binding != 1 || node->gradientType() == QQuickAbstractPathRenderer::NoGradient)
56 return;
57
58 const QQuickShapeGradientCacheKey cacheKey(node->fillGradient().stops,
59 node->fillGradient().spread);
60 QSGTexture *t = QQuickShapeGradientCache::cacheForRhi(rhi: state.rhi())->get(grad: cacheKey);
61 t->commitTextureOperations(rhi: state.rhi(), resourceUpdates: state.resourceUpdateBatch());
62 *texture = t;
63 }
64
65 bool QQuickShapeCurveMaterialShader::updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
66 {
67 bool changed = false;
68 QByteArray *buf = state.uniformData();
69 Q_ASSERT(buf->size() >= 80);
70
71 int offset = 0;
72 float matrixScale = 0.0f;
73 if (state.isMatrixDirty()) {
74 const QMatrix4x4 m = state.combinedMatrix();
75
76 memcpy(dest: buf->data() + offset, src: m.constData(), n: 64);
77
78 matrixScale = qSqrt(v: qAbs(t: state.determinant()));
79 memcpy(dest: buf->data() + offset + 64, src: &matrixScale, n: 4);
80
81 changed = true;
82 }
83 offset += 68;
84
85 if (state.isOpacityDirty()) {
86 const float opacity = state.opacity();
87 memcpy(dest: buf->data() + offset, src: &opacity, n: 4);
88 changed = true;
89 }
90 offset += 4;
91
92 QQuickShapeCurveMaterial *newMaterial = static_cast<QQuickShapeCurveMaterial *>(newEffect);
93 QQuickShapeCurveMaterial *oldMaterial = static_cast<QQuickShapeCurveMaterial *>(oldEffect);
94
95 QQuickShapeCurveNode *newNode = newMaterial != nullptr ? newMaterial->node() : nullptr;
96 QQuickShapeCurveNode *oldNode = oldMaterial != nullptr ? oldMaterial->node() : nullptr;
97
98 if (newNode == nullptr)
99 return changed;
100
101 if (oldNode == nullptr || oldNode->debug() != newNode->debug()) {
102 float debug = newNode->debug();
103 memcpy(dest: buf->data() + offset, src: &debug, n: 4);
104 changed = true;
105 }
106 offset += 8;
107
108 if (newNode->hasStroke()) {
109 Q_ASSERT(buf->size() >= offset + 32);
110 QVector4D newStrokeColor(newNode->strokeColor().redF(),
111 newNode->strokeColor().greenF(),
112 newNode->strokeColor().blueF(),
113 newNode->strokeColor().alphaF());
114 QVector4D oldStrokeColor = oldNode != nullptr
115 ? QVector4D(oldNode->strokeColor().redF(),
116 oldNode->strokeColor().greenF(),
117 oldNode->strokeColor().blueF(),
118 oldNode->strokeColor().alphaF())
119 : QVector4D{};
120
121 if (oldNode == nullptr || oldStrokeColor != newStrokeColor) {
122 memcpy(dest: buf->data() + offset, src: &newStrokeColor, n: 16);
123 changed = true;
124 }
125 offset += 16;
126
127 if (oldNode == nullptr
128 || !qFuzzyCompare(p1: newNode->strokeWidth(), p2: oldNode->strokeWidth())
129 || (state.isMatrixDirty() && newNode->strokeWidth() > 0.0f)) {
130 float w = newNode->strokeWidth() * matrixScale; // matrixScale calculated earlier
131 memcpy(dest: buf->data() + offset, src: &w, n: 4);
132 changed = true;
133 }
134 offset += 16;
135 }
136
137 if (newNode->gradientType() == QQuickAbstractPathRenderer::NoGradient) {
138 Q_ASSERT(buf->size() >= offset + 16);
139
140 QVector4D newColor = QVector4D(newNode->color().redF(),
141 newNode->color().greenF(),
142 newNode->color().blueF(),
143 newNode->color().alphaF());
144 QVector4D oldColor = oldNode != nullptr
145 ? QVector4D(oldNode->color().redF(),
146 oldNode->color().greenF(),
147 oldNode->color().blueF(),
148 oldNode->color().alphaF())
149 : QVector4D{};
150
151 if (oldNode == nullptr || oldColor != newColor) {
152 memcpy(dest: buf->data() + offset, src: &newColor, n: 16);
153 changed = true;
154 }
155
156 offset += 16;
157 } else if (newNode->gradientType() == QQuickAbstractPathRenderer::LinearGradient) {
158 Q_ASSERT(buf->size() >= offset + 8 + 8);
159
160 QVector2D newGradientStart = QVector2D(newNode->fillGradient().a);
161 QVector2D oldGradientStart = oldNode != nullptr
162 ? QVector2D(oldNode->fillGradient().a)
163 : QVector2D{};
164
165 if (newGradientStart != oldGradientStart || oldEffect == nullptr) {
166 memcpy(dest: buf->data() + offset, src: &newGradientStart, n: 8);
167 changed = true;
168 }
169 offset += 8;
170
171 QVector2D newGradientEnd = QVector2D(newNode->fillGradient().b);
172 QVector2D oldGradientEnd = oldNode!= nullptr
173 ? QVector2D(oldNode->fillGradient().b)
174 : QVector2D{};
175
176 if (newGradientEnd != oldGradientEnd || oldEffect == nullptr) {
177 memcpy(dest: buf->data() + offset, src: &newGradientEnd, n: 8);
178 changed = true;
179 }
180
181 offset += 8;
182 } else if (newNode->gradientType() == QQuickAbstractPathRenderer::RadialGradient) {
183 Q_ASSERT(buf->size() >= offset + 8 + 8 + 4 + 4);
184
185 QVector2D newFocalPoint = QVector2D(newNode->fillGradient().b);
186 QVector2D oldFocalPoint = oldNode != nullptr
187 ? QVector2D(oldNode->fillGradient().b)
188 : QVector2D{};
189 if (oldNode == nullptr || newFocalPoint != oldFocalPoint) {
190 memcpy(dest: buf->data() + offset, src: &newFocalPoint, n: 8);
191 changed = true;
192 }
193 offset += 8;
194
195 QVector2D newCenterPoint = QVector2D(newNode->fillGradient().a);
196 QVector2D oldCenterPoint = oldNode != nullptr
197 ? QVector2D(oldNode->fillGradient().a)
198 : QVector2D{};
199
200 QVector2D newCenterToFocal = newCenterPoint - newFocalPoint;
201 QVector2D oldCenterToFocal = oldCenterPoint - oldFocalPoint;
202 if (oldNode == nullptr || newCenterToFocal != oldCenterToFocal) {
203 memcpy(dest: buf->data() + offset, src: &newCenterToFocal, n: 8);
204 changed = true;
205 }
206 offset += 8;
207
208 float newCenterRadius = newNode->fillGradient().v0;
209 float oldCenterRadius = oldNode != nullptr
210 ? oldNode->fillGradient().v0
211 : 0.0f;
212 if (oldNode == nullptr || !qFuzzyCompare(p1: newCenterRadius, p2: oldCenterRadius)) {
213 memcpy(dest: buf->data() + offset, src: &newCenterRadius, n: 4);
214 changed = true;
215 }
216 offset += 4;
217
218 float newFocalRadius = newNode->fillGradient().v1;
219 float oldFocalRadius = oldNode != nullptr
220 ? oldNode->fillGradient().v1
221 : 0.0f;
222 if (oldNode == nullptr || !qFuzzyCompare(p1: newFocalRadius, p2: oldFocalRadius)) {
223 memcpy(dest: buf->data() + offset, src: &newFocalRadius, n: 4);
224 changed = true;
225 }
226 offset += 4;
227
228 } else if (newNode->gradientType() == QQuickAbstractPathRenderer::ConicalGradient) {
229 Q_ASSERT(buf->size() >= offset + 8 + 4);
230
231 QVector2D newFocalPoint = QVector2D(newNode->fillGradient().a);
232 QVector2D oldFocalPoint = oldNode != nullptr
233 ? QVector2D(oldNode->fillGradient().a)
234 : QVector2D{};
235 if (oldNode == nullptr || newFocalPoint != oldFocalPoint) {
236 memcpy(dest: buf->data() + offset, src: &newFocalPoint, n: 8);
237 changed = true;
238 }
239 offset += 8;
240
241 float newAngle = newNode->fillGradient().v0;
242 float oldAngle = oldNode != nullptr
243 ? oldNode->fillGradient().v0
244 : 0.0f;
245 if (oldNode == nullptr || !qFuzzyCompare(p1: newAngle, p2: oldAngle)) {
246 newAngle = -qDegreesToRadians(degrees: newAngle);
247 memcpy(dest: buf->data() + offset, src: &newAngle, n: 4);
248 changed = true;
249 }
250 offset += 4;
251 }
252
253 return changed;
254 }
255}
256
257QQuickShapeCurveMaterial::QQuickShapeCurveMaterial(QQuickShapeCurveNode *node)
258 : m_node(node)
259{
260 setFlag(flags: Blending, on: true);
261 setFlag(flags: RequiresDeterminant, on: true);
262}
263
264int QQuickShapeCurveMaterial::compare(const QSGMaterial *other) const
265{
266 if (other->type() != type())
267 return (type() - other->type());
268
269 const QQuickShapeCurveMaterial *otherMaterial =
270 static_cast<const QQuickShapeCurveMaterial *>(other);
271
272 QQuickShapeCurveNode *a = node();
273 QQuickShapeCurveNode *b = otherMaterial->node();
274 if (a == b)
275 return 0;
276
277 if (int d = a->strokeColor().rgba() - b->strokeColor().rgba())
278 return d;
279
280 if (a->gradientType() == QQuickAbstractPathRenderer::NoGradient) {
281 if (int d = a->color().red() - b->color().red())
282 return d;
283 if (int d = a->color().green() - b->color().green())
284 return d;
285 if (int d = a->color().blue() - b->color().blue())
286 return d;
287 if (int d = a->color().alpha() - b->color().alpha())
288 return d;
289 } else {
290 const QQuickAbstractPathRenderer::GradientDesc &ga = a->fillGradient();
291 const QQuickAbstractPathRenderer::GradientDesc &gb = b->fillGradient();
292
293 if (int d = ga.a.x() - gb.a.x())
294 return d;
295 if (int d = ga.a.y() - gb.a.y())
296 return d;
297 if (int d = ga.b.x() - gb.b.x())
298 return d;
299 if (int d = ga.b.y() - gb.b.y())
300 return d;
301
302 if (int d = ga.v0 - gb.v0)
303 return d;
304 if (int d = ga.v1 - gb.v1)
305 return d;
306
307 if (int d = ga.spread - gb.spread)
308 return d;
309
310 if (int d = ga.stops.size() - gb.stops.size())
311 return d;
312
313 for (int i = 0; i < ga.stops.size(); ++i) {
314 if (int d = ga.stops[i].first - gb.stops[i].first)
315 return d;
316 if (int d = ga.stops[i].second.rgba() - gb.stops[i].second.rgba())
317 return d;
318 }
319 }
320
321 return 0;
322}
323
324QSGMaterialType *QQuickShapeCurveMaterial::type() const
325{
326 static QSGMaterialType type[8];
327 uint index = node()->gradientType();
328 Q_ASSERT((index & ~3) == 0); // Only two first bits for gradient type
329
330 if (node()->hasStroke())
331 index |= 4;
332
333 return &type[index];
334}
335
336QSGMaterialShader *QQuickShapeCurveMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
337{
338 return new QQuickShapeCurveMaterialShader(node()->gradientType(),
339 node()->hasStroke(),
340 renderMode == QSGRendererInterface::RenderMode3D);
341}
342
343
344QT_END_NAMESPACE
345

source code of qtdeclarative/src/quickshapes/qquickshapecurvenode_p.cpp