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
qquickshapegenericrenderer.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 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
6#include <QtGui/private/qtriangulator_p.h>
7#include <QtGui/private/qtriangulatingstroker_p.h>
8#include <rhi/qrhi.h>
9#include <QSGVertexColorMaterial>
10#include <QSGTextureProvider>
11#include <private/qsgplaintexture_p.h>
12
13#include <QtQuick/private/qsggradientcache_p.h>
14
15#if QT_CONFIG(thread)
16#include <QThreadPool>
17#endif
18
20
21struct ColoredVertex // must match QSGGeometry::ColoredPoint2D
22{
23 float x, y;
25 void set(float nx, float ny, QQuickShapeGenericRenderer::Color4ub ncolor)
26 {
27 x = nx; y = ny; color = ncolor;
28 }
29};
30
32{
33 float r, g, b, a;
34 c.getRgbF(&r, &g, &b, &a);
36 uchar(qRound(r * a * 255)),
37 uchar(qRound(g * a * 255)),
38 uchar(qRound(b * a * 255)),
39 uchar(qRound(a * 255))
40 };
41 return color;
42}
43
44QQuickShapeGenericStrokeFillNode::QQuickShapeGenericStrokeFillNode(QQuickWindow *window)
45 : m_material(nullptr)
46{
47 setFlag(QSGNode::OwnsGeometry, true);
48 setFlag(QSGNode::UsePreprocess, true);
49 setGeometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0));
50 activateMaterial(window, MatSolidColor);
52 qsgnode_set_description(this, QLatin1String("stroke-fill"));
53#endif
54}
55
57{
58 switch (m) {
59 case MatSolidColor:
60 // Use vertexcolor material. Items with different colors remain batchable
61 // this way, at the expense of having to provide per-vertex color values.
62 m_material.reset(QQuickShapeGenericMaterialFactory::createVertexColor(window));
63 break;
64 case MatLinearGradient:
65 m_material.reset(QQuickShapeGenericMaterialFactory::createLinearGradient(window, this));
66 break;
67 case MatRadialGradient:
68 m_material.reset(QQuickShapeGenericMaterialFactory::createRadialGradient(window, this));
69 break;
70 case MatConicalGradient:
71 m_material.reset(QQuickShapeGenericMaterialFactory::createConicalGradient(window, this));
72 break;
73 case MatTextureFill:
74 m_material.reset(QQuickShapeGenericMaterialFactory::createTextureFill(window, this));
75 break;
76 default:
77 qWarning("Unknown material %d", m);
78 return;
79 }
80
81 if (material() != m_material.data())
82 setMaterial(m_material.data());
83}
84
86{
87 if (m_fillTextureProvider != nullptr) {
88 if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(m_fillTextureProvider->texture()))
89 texture->updateTexture();
90 }
91}
92
93void QQuickShapeGenericStrokeFillNode::handleTextureChanged()
94{
95 markDirty(QSGNode::DirtyMaterial);
96}
97
98void QQuickShapeGenericStrokeFillNode::handleTextureProviderDestroyed()
99{
100 m_fillTextureProvider = nullptr;
101 markDirty(QSGNode::DirtyMaterial);
102}
103
105{
106 for (ShapePathData &d : m_sp) {
107 if (d.pendingFill)
108 d.pendingFill->orphaned = true;
109 if (d.pendingStroke)
110 d.pendingStroke->orphaned = true;
111 }
112}
113
114// sync, and so triangulation too, happens on the gui thread
115// - except when async is set, in which case triangulation is moved to worker threads
116
117void QQuickShapeGenericRenderer::beginSync(int totalCount, bool *countChanged)
118{
119 for (int i = totalCount; i < m_sp.size(); i++) // Handle removal of paths
120 setFillTextureProvider(i, nullptr); // deref window
121
122 if (m_sp.size() != totalCount) {
123 m_sp.resize(totalCount);
124 m_accDirty |= DirtyList;
125 *countChanged = true;
126 } else {
127 *countChanged = false;
128 }
129 for (ShapePathData &d : m_sp)
130 d.syncDirty = 0;
131}
132
133void QQuickShapeGenericRenderer::setPath(int index, const QPainterPath &path, QQuickShapePath::PathHints)
134{
135 ShapePathData &d(m_sp[index]);
136 d.path = path;
137 d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom;
138}
139
140void QQuickShapeGenericRenderer::setStrokeColor(int index, const QColor &color)
141{
142 ShapePathData &d(m_sp[index]);
143 const bool wasTransparent = d.strokeColor.a == 0;
144 d.strokeColor = colorToColor4ub(color);
145 const bool isTransparent = d.strokeColor.a == 0;
146 d.syncDirty |= DirtyColor;
147 if (wasTransparent && !isTransparent)
148 d.syncDirty |= DirtyStrokeGeom;
149}
150
152{
153 ShapePathData &d(m_sp[index]);
154 d.strokeWidth = w;
155 if (w > 0.0f)
156 d.pen.setWidthF(w);
157 d.syncDirty |= DirtyStrokeGeom;
158}
159
161{
162 ShapePathData &d(m_sp[index]);
163 d.pen.setCosmetic(c);
164 d.syncDirty |= DirtyStrokeGeom;
165 // as long as the stroke is cosmetic,
166 // QQuickShape::itemChange triggers re-triangulation whenever scale changes
167}
168
169void QQuickShapeGenericRenderer::setFillColor(int index, const QColor &color)
170{
171 ShapePathData &d(m_sp[index]);
172 const bool wasTransparent = d.fillColor.a == 0;
173 d.fillColor = colorToColor4ub(color);
174 const bool isTransparent = d.fillColor.a == 0;
175 d.syncDirty |= DirtyColor;
176 if (wasTransparent && !isTransparent)
177 d.syncDirty |= DirtyFillGeom;
178}
179
180void QQuickShapeGenericRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule)
181{
182 ShapePathData &d(m_sp[index]);
183 d.fillRule = Qt::FillRule(fillRule);
184 d.syncDirty |= DirtyFillGeom;
185}
186
187void QQuickShapeGenericRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit)
188{
189 ShapePathData &d(m_sp[index]);
190 d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
191 d.pen.setMiterLimit(miterLimit);
192 d.syncDirty |= DirtyStrokeGeom;
193}
194
195void QQuickShapeGenericRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle)
196{
197 ShapePathData &d(m_sp[index]);
198 d.pen.setCapStyle(Qt::PenCapStyle(capStyle));
199 d.syncDirty |= DirtyStrokeGeom;
200}
201
202void QQuickShapeGenericRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
203 qreal dashOffset, const QList<qreal> &dashPattern)
204{
205 ShapePathData &d(m_sp[index]);
206 d.pen.setStyle(Qt::PenStyle(strokeStyle));
207 if (strokeStyle == QQuickShapePath::DashLine) {
208 d.pen.setDashPattern(dashPattern);
209 d.pen.setDashOffset(dashOffset);
210 }
211 d.syncDirty |= DirtyStrokeGeom;
212}
213
215 copyGenericGradient(const QQuickShapeGradient *gradient, QSGGradientCache::GradientDesc *dst)
216{
217 Q_ASSERT(dst != nullptr);
218 if (gradient == nullptr)
219 return QQuickAbstractPathRenderer::NoGradient;
220
221 dst->stops = gradient->gradientStops(); // sorted
222 dst->spread = QGradient::Spread(gradient->spread());
223 if (const QQuickShapeLinearGradient *g = qobject_cast<const QQuickShapeLinearGradient *>(gradient)) {
224 dst->a = QPointF(g->x1(), g->y1());
225 dst->b = QPointF(g->x2(), g->y2());
226
227 return QQuickAbstractPathRenderer::LinearGradient;
228 } else if (const QQuickShapeRadialGradient *g = qobject_cast<const QQuickShapeRadialGradient *>(gradient)) {
229 dst->a = QPointF(g->centerX(), g->centerY());
230 dst->b = QPointF(g->focalX(), g->focalY());
231 dst->v0 = g->centerRadius();
232 dst->v1 = g->focalRadius();
233
234 return QQuickAbstractPathRenderer::RadialGradient;
235 } else if (const QQuickShapeConicalGradient *g = qobject_cast<const QQuickShapeConicalGradient *>(gradient)) {
236 dst->a = QPointF(g->centerX(), g->centerY());
237 dst->v0 = g->angle();
238
239 return QQuickAbstractPathRenderer::ConicalGradient;
240 }
241
242 Q_UNREACHABLE_RETURN(QQuickAbstractPathRenderer::NoGradient);
243}
244
245void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient *gradient)
246{
247 ShapePathData &d(m_sp[index]);
248 d.fillGradientActive = copyGenericGradient(gradient, &d.fillGradient);
249 d.syncDirty |= DirtyFillGradient;
250}
251
252void QQuickShapeGenericRenderer::setStrokeGradient(int index, QQuickShapeGradient *gradient)
253{
254 ShapePathData &d(m_sp[index]);
255 d.strokeGradientActive = copyGenericGradient(gradient, &d.strokeGradient);
256 d.syncDirty |= DirtyStrokeGradient;
257}
258
259void QQuickShapeGenericRenderer::setFillTextureProvider(int index, QQuickItem *textureProviderItem)
260{
261 ShapePathData &d(m_sp[index]);
262 if ((d.fillTextureProviderItem == nullptr) != (textureProviderItem == nullptr))
263 d.syncDirty |= DirtyFillGeom;
264 if (d.fillTextureProviderItem != nullptr)
265 QQuickItemPrivate::get(d.fillTextureProviderItem)->derefWindow();
266 d.fillTextureProviderItem = textureProviderItem;
267 if (d.fillTextureProviderItem != nullptr)
268 QQuickItemPrivate::get(d.fillTextureProviderItem)->refWindow(m_item->window());
269 d.syncDirty |= DirtyFillTexture;
270}
271
273{
274 for (auto &pathData : m_sp) {
275 if (pathData.fillTextureProviderItem != nullptr) {
276 if (window == nullptr)
277 QQuickItemPrivate::get(pathData.fillTextureProviderItem)->derefWindow();
278 else
279 QQuickItemPrivate::get(pathData.fillTextureProviderItem)->refWindow(window);
280 }
281 }
282}
283
284
285void QQuickShapeGenericRenderer::setFillTransform(int index, const QSGTransform &transform)
286{
287 ShapePathData &d(m_sp[index]);
288 d.fillTransform = transform;
289 d.syncDirty |= DirtyFillTransform;
290}
291
293{
294 ShapePathData &d(m_sp[index]);
295 d.triangulationScale = scale;
296 d.syncDirty |= DirtyStrokeGeom;
297}
298
300{
301 QQuickShapeGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType,
302 supportsElementIndexUint, triangulationScale);
303 emit done(this);
304}
305
307{
308 QQuickShapeGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize, triangulationScale);
309 emit done(this);
310}
311
312void QQuickShapeGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data)
313{
314 m_asyncCallback = callback;
315 m_asyncCallbackData = data;
316}
317
318#if QT_CONFIG(thread)
319static QThreadPool *pathWorkThreadPool = nullptr;
320
321static void deletePathWorkThreadPool()
322{
323 delete pathWorkThreadPool;
324 pathWorkThreadPool = nullptr;
325}
326#endif
327
329{
330#if !QT_CONFIG(thread)
331 // Force synchronous mode for the no-thread configuration due
332 // to lack of QThreadPool.
333 async = false;
334#endif
335
336 bool didKickOffAsync = false;
337
338 for (int i = 0; i < m_sp.size(); ++i) {
339 ShapePathData &d(m_sp[i]);
340 if (!d.syncDirty)
341 continue;
342
343 m_accDirty |= d.syncDirty;
344
345 // Use a shadow dirty flag in order to avoid losing state in case there are
346 // multiple syncs with different dirty flags before we get to updateNode()
347 // on the render thread (with the gui thread blocked). For our purposes
348 // here syncDirty is still required since geometry regeneration must only
349 // happen when there was an actual change in this particular sync round.
350 d.effectiveDirty |= d.syncDirty;
351
352 if (d.path.isEmpty()) {
353 d.fillVertices.clear();
354 d.fillIndices.clear();
355 d.strokeVertices.clear();
356 continue;
357 }
358
359#if QT_CONFIG(thread)
360 if (async && !pathWorkThreadPool) {
361 qAddPostRoutine(deletePathWorkThreadPool);
362 pathWorkThreadPool = new QThreadPool;
363 const int idealCount = QThread::idealThreadCount();
364 pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4);
365 }
366#endif
367 auto testFeatureIndexUint = [](QQuickItem *item) -> bool {
368 if (auto *w = item->window()) {
369 if (auto *rhi = QQuickWindowPrivate::get(w)->rhi)
370 return rhi->isFeatureSupported(QRhi::ElementIndexUint);
371 }
372 return true;
373 };
374 static bool supportsElementIndexUint = testFeatureIndexUint(m_item);
375 if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) {
376 d.path.setFillRule(d.fillRule);
377 if (m_api == QSGRendererInterface::Unknown)
378 m_api = m_item->window()->rendererInterface()->graphicsApi();
379 if (async) {
381 r->setAutoDelete(false);
382 if (d.pendingFill)
383 d.pendingFill->orphaned = true;
384 d.pendingFill = r;
385 r->path = d.path;
386 r->fillColor = d.fillColor;
387 r->supportsElementIndexUint = supportsElementIndexUint;
388 r->triangulationScale = d.triangulationScale;
389 // Unlikely in practice but in theory m_sp could be
390 // resized. Therefore, capture 'i' instead of 'd'.
391 QObject::connect(r, &QQuickShapeFillRunnable::done, qApp, [this, i](QQuickShapeFillRunnable *r) {
392 // Bail out when orphaned (meaning either another run was
393 // started after this one, or the renderer got destroyed).
394 if (!r->orphaned && i < m_sp.size()) {
395 ShapePathData &d(m_sp[i]);
396 d.fillVertices = r->fillVertices;
397 d.fillIndices = r->fillIndices;
398 d.indexType = r->indexType;
399 d.pendingFill = nullptr;
400 d.effectiveDirty |= DirtyFillGeom;
401 maybeUpdateAsyncItem();
402 }
403 r->deleteLater();
404 });
405 didKickOffAsync = true;
406#if QT_CONFIG(thread)
407 // qtVectorPathForPath() initializes a unique_ptr without locking.
408 // Do that before starting the threads as otherwise we get a race condition.
409 qtVectorPathForPath(r->path);
410 pathWorkThreadPool->start(r);
411#endif
412 } else {
413 triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType,
414 supportsElementIndexUint,
415 d.triangulationScale);
416 }
417 }
418
419 if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth > 0.0f && (d.strokeColor.a || d.strokeGradientActive)) {
420 if (async) {
422 r->setAutoDelete(false);
423 if (d.pendingStroke)
424 d.pendingStroke->orphaned = true;
425 d.pendingStroke = r;
426 r->path = d.path;
427 r->pen = d.pen;
428 r->strokeColor = d.strokeColor;
429 r->clipSize = QSize(m_item->width(), m_item->height());
430 r->triangulationScale = d.triangulationScale;
431 QObject::connect(r, &QQuickShapeStrokeRunnable::done, qApp, [this, i](QQuickShapeStrokeRunnable *r) {
432 if (!r->orphaned && i < m_sp.size()) {
433 ShapePathData &d(m_sp[i]);
434 d.strokeVertices = r->strokeVertices;
435 d.pendingStroke = nullptr;
436 d.effectiveDirty |= DirtyStrokeGeom;
437 maybeUpdateAsyncItem();
438 }
439 r->deleteLater();
440 });
441 didKickOffAsync = true;
442#if QT_CONFIG(thread)
443 // qtVectorPathForPath() initializes a unique_ptr without locking.
444 // Do that before starting the threads as otherwise we get a race condition.
445 qtVectorPathForPath(r->path);
446 pathWorkThreadPool->start(r);
447#endif
448 } else {
449 triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices,
450 QSize(m_item->width(), m_item->height()), d.triangulationScale);
451 }
452 }
453 }
454
455 if (!didKickOffAsync && async && m_asyncCallback)
456 m_asyncCallback(m_asyncCallbackData);
457}
458
459void QQuickShapeGenericRenderer::maybeUpdateAsyncItem()
460{
461 for (const ShapePathData &d : std::as_const(m_sp)) {
462 if (d.pendingFill || d.pendingStroke)
463 return;
464 }
465 m_accDirty |= DirtyFillGeom | DirtyStrokeGeom;
466 m_item->update();
467 if (m_asyncCallback)
468 m_asyncCallback(m_asyncCallbackData);
469}
470
471// the stroke/fill triangulation functions may be invoked either on the gui
472// thread or some worker thread and must thus be self-contained.
473void QQuickShapeGenericRenderer::triangulateFill(const QPainterPath &path,
474 const Color4ub &fillColor,
475 VertexContainerType *fillVertices,
476 IndexContainerType *fillIndices,
477 QSGGeometry::Type *indexType,
478 bool supportsElementIndexUint,
479 qreal triangulationScale)
480{
481 const QVectorPath &vp = qtVectorPathForPath(path);
482
483 QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(triangulationScale, triangulationScale), 1, supportsElementIndexUint);
484 const int vertexCount = ts.vertices.size() / 2; // just a qreal vector with x,y hence the / 2
485 fillVertices->resize(vertexCount);
486 ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(fillVertices->data());
487 const qreal *vsrc = ts.vertices.constData();
488 for (int i = 0; i < vertexCount; ++i)
489 vdst[i].set(vsrc[i * 2] / triangulationScale, vsrc[i * 2 + 1] / triangulationScale, fillColor);
490
491 size_t indexByteSize;
492 if (ts.indices.type() == QVertexIndexVector::UnsignedShort) {
493 *indexType = QSGGeometry::UnsignedShortType;
494 // fillIndices is still QList<quint32>. Just resize to N/2 and pack
495 // the N quint16s into it.
496 fillIndices->resize(ts.indices.size() / 2);
497 indexByteSize = ts.indices.size() * sizeof(quint16);
498 } else {
499 *indexType = QSGGeometry::UnsignedIntType;
500 fillIndices->resize(ts.indices.size());
501 indexByteSize = ts.indices.size() * sizeof(quint32);
502 }
503 memcpy(fillIndices->data(), ts.indices.data(), indexByteSize);
504}
505
506void QQuickShapeGenericRenderer::triangulateStroke(const QPainterPath &path,
507 const QPen &pen,
508 const Color4ub &strokeColor,
509 VertexContainerType *strokeVertices,
510 const QSize &clipSize,
511 qreal triangulationScale)
512{
513 const QVectorPath &vp = qtVectorPathForPath(path);
514 const QRectF clip(QPointF(0, 0), clipSize);
515 const qreal inverseScale = 1.0 / triangulationScale;
516
517 QTriangulatingStroker stroker;
518 stroker.setInvScale(inverseScale);
519
520 if (pen.style() == Qt::SolidLine) {
521 stroker.process(vp, pen, clip, {});
522 } else {
523 QDashedStrokeProcessor dashStroker;
524 dashStroker.setInvScale(inverseScale);
525 dashStroker.process(vp, pen, clip, {});
526 QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(),
527 dashStroker.elementTypes(), 0);
528 stroker.process(dashStroke, pen, clip, {});
529 }
530
531 if (!stroker.vertexCount()) {
532 strokeVertices->clear();
533 return;
534 }
535
536 const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2
537 strokeVertices->resize(vertexCount);
538 ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(strokeVertices->data());
539 const float *vsrc = stroker.vertices();
540 for (int i = 0; i < vertexCount; ++i)
541 vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor);
542}
543
545{
546 if (m_rootNode != node) {
547 m_rootNode = node;
548 m_accDirty |= DirtyList;
549 }
550}
551
552// on the render thread with gui blocked
554{
555 if (!m_rootNode || !m_accDirty)
556 return;
557
558// [ m_rootNode ]
559// / / /
560// #0 [ fill ] [ stroke ] [ next ]
561// / / |
562// #1 [ fill ] [ stroke ] [ next ]
563// / / |
564// #2 [ fill ] [ stroke ] [ next ]
565// ...
566// ...
567
568 QQuickShapeGenericNode **nodePtr = &m_rootNode;
569 QQuickShapeGenericNode *prevNode = nullptr;
570
571 for (ShapePathData &d : m_sp) {
572 if (!*nodePtr) {
573 Q_ASSERT(prevNode);
574 *nodePtr = new QQuickShapeGenericNode;
575 prevNode->m_next = *nodePtr;
576 prevNode->appendChildNode(*nodePtr);
577 }
578
579 QQuickShapeGenericNode *node = *nodePtr;
580
581 if (m_accDirty & DirtyList) {
582 d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient
583 | DirtyFillTransform | DirtyFillTexture | DirtyStrokeGradient;
584 }
585
586 if (!d.effectiveDirty) {
587 prevNode = node;
588 nodePtr = &node->m_next;
589 continue;
590 }
591
592 if (d.fillColor.a == 0) {
593 delete node->m_fillNode;
594 node->m_fillNode = nullptr;
595 } else if (!node->m_fillNode) {
596 node->m_fillNode = new QQuickShapeGenericStrokeFillNode(m_item->window());
597 if (node->m_strokeNode)
598 node->removeChildNode(node->m_strokeNode);
599 node->appendChildNode(node->m_fillNode);
600 if (node->m_strokeNode)
601 node->appendChildNode(node->m_strokeNode);
602 d.effectiveDirty |= DirtyFillGeom;
603 }
604
605 if (d.strokeWidth <= 0.0f || d.strokeColor.a == 0) {
606 delete node->m_strokeNode;
607 node->m_strokeNode = nullptr;
608 } else if (!node->m_strokeNode) {
609 node->m_strokeNode = new QQuickShapeGenericStrokeFillNode(m_item->window());
610 node->appendChildNode(node->m_strokeNode);
611 d.effectiveDirty |= DirtyStrokeGeom;
612 }
613
614 updateFillNode(&d, node);
615 updateStrokeNode(&d, node);
616
617 d.effectiveDirty = 0;
618
619 prevNode = node;
620 nodePtr = &node->m_next;
621 }
622
623 if (*nodePtr && prevNode) {
624 prevNode->removeChildNode(*nodePtr);
625 delete *nodePtr;
626 *nodePtr = nullptr;
627 }
628
629 if (m_sp.isEmpty()) {
630 delete m_rootNode->m_fillNode;
631 m_rootNode->m_fillNode = nullptr;
632 delete m_rootNode->m_strokeNode;
633 m_rootNode->m_strokeNode = nullptr;
634 delete m_rootNode->m_next;
635 m_rootNode->m_next = nullptr;
636 }
637
638 m_accDirty = 0;
639}
640
641void QQuickShapeGenericRenderer::updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n)
642{
643 if (d->fillGradientActive) {
644 if (d->effectiveDirty & DirtyFillGradient)
645 n->m_gradient = d->fillGradient;
646 }
647
648 if (d->effectiveDirty & DirtyFillTexture) {
649 bool needsUpdate = d->fillTextureProviderItem == nullptr && n->m_fillTextureProvider != nullptr;
650 if (!needsUpdate
651 && d->fillTextureProviderItem != nullptr
652 && n->m_fillTextureProvider != d->fillTextureProviderItem->textureProvider()) {
653 needsUpdate = true;
654 }
655
656 if (needsUpdate) {
657 if (n->m_fillTextureProvider != nullptr) {
658 QObject::disconnect(n->m_fillTextureProvider, &QSGTextureProvider::textureChanged,
659 n, &QQuickShapeGenericStrokeFillNode::handleTextureChanged);
660 QObject::disconnect(n->m_fillTextureProvider, &QSGTextureProvider::destroyed,
661 n, &QQuickShapeGenericStrokeFillNode::handleTextureProviderDestroyed);
662 }
663
664 n->m_fillTextureProvider = d->fillTextureProviderItem == nullptr
665 ? nullptr
666 : d->fillTextureProviderItem->textureProvider();
667
668 if (n->m_fillTextureProvider != nullptr) {
669 QObject::connect(n->m_fillTextureProvider, &QSGTextureProvider::textureChanged,
670 n, &QQuickShapeGenericStrokeFillNode::handleTextureChanged);
671 QObject::connect(n->m_fillTextureProvider, &QSGTextureProvider::destroyed,
672 n, &QQuickShapeGenericStrokeFillNode::handleTextureProviderDestroyed);
673 }
674 }
675 }
676 if (d->effectiveDirty & DirtyFillTransform)
677 n->m_fillTransform = d->fillTransform;
678}
679
680void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGenericNode *node)
681{
682 if (!node->m_fillNode)
683 return;
685 return;
686
687 // Make a copy of the data that will be accessed by the material on
688 // the render thread. This must be done even when we bail out below.
690 updateShadowDataInNode(d, n);
691
692 QSGGeometry *g = n->geometry();
693 if (d->fillVertices.isEmpty()) {
694 if (g->vertexCount() || g->indexCount()) {
695 g->allocate(0, 0);
696 n->markDirty(QSGNode::DirtyGeometry);
697 }
698 return;
699 }
700
701 if (d->fillGradientActive) {
703 switch (d->fillGradientActive) {
704 case LinearGradient:
706 break;
707 case RadialGradient:
709 break;
710 case ConicalGradient:
712 break;
713 default:
714 Q_UNREACHABLE_RETURN();
715 }
716 n->activateMaterial(m_item->window(), gradMat);
717 if (d->effectiveDirty & (DirtyFillGradient | DirtyFillTransform)) {
718 // Gradients are implemented via a texture-based material.
719 n->markDirty(QSGNode::DirtyMaterial);
720 // stop here if only the gradient or filltransform changed; no need to touch the geometry
721 if (!(d->effectiveDirty & DirtyFillGeom))
722 return;
723 }
724 } else if (d->fillTextureProviderItem != nullptr) {
725 n->activateMaterial(m_item->window(), QQuickShapeGenericStrokeFillNode::MatTextureFill);
726 if (d->effectiveDirty & DirtyFillTexture)
727 n->markDirty(QSGNode::DirtyMaterial);
728 } else {
729 n->activateMaterial(m_item->window(), QQuickShapeGenericStrokeFillNode::MatSolidColor);
730 // fast path for updating only color values when no change in vertex positions
731 if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom) && d->fillTextureProviderItem == nullptr) {
732 ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData());
733 for (int i = 0; i < g->vertexCount(); ++i)
734 vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor);
735 n->markDirty(QSGNode::DirtyGeometry);
736 return;
737 }
738 }
739
740 const int indexCount = d->indexType == QSGGeometry::UnsignedShortType
741 ? d->fillIndices.size() * 2 : d->fillIndices.size();
742 if (g->indexType() != d->indexType) {
743 g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(),
744 d->fillVertices.size(), indexCount, d->indexType);
745 n->setGeometry(g);
746 } else {
747 g->allocate(d->fillVertices.size(), indexCount);
748 }
749 g->setDrawingMode(QSGGeometry::DrawTriangles);
750
751 memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex());
752 memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex());
753
754 n->markDirty(QSGNode::DirtyGeometry);
755}
756
757void QQuickShapeGenericRenderer::updateStrokeNode(ShapePathData *d, QQuickShapeGenericNode *node)
758{
759 if (!node->m_strokeNode)
760 return;
761 if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor | DirtyStrokeGradient)))
762 return;
763
765 if (d->strokeGradientActive) {
766 if (d->effectiveDirty & DirtyStrokeGradient)
767 n->m_gradient = d->strokeGradient;
768 }
769
770 QSGGeometry *g = n->geometry();
771 if (d->strokeVertices.isEmpty()) {
772 if (g->vertexCount() || g->indexCount()) {
773 g->allocate(0, 0);
774 n->markDirty(QSGNode::DirtyGeometry);
775 }
776 return;
777 }
778
779 n->markDirty(QSGNode::DirtyGeometry);
780
781 // Async loading runs update once, bails out above, then updates again once
782 // ready. Set the material dirty then. This is in-line with fill where the
783 // first activateMaterial() achieves the same.
784 if (!g->vertexCount())
785 n->markDirty(QSGNode::DirtyMaterial);
786
787 if (d->strokeGradientActive) {
789 switch (d->strokeGradientActive) {
790 case LinearGradient:
792 break;
793 case RadialGradient:
795 break;
796 case ConicalGradient:
798 break;
799 default:
800 Q_UNREACHABLE_RETURN();
801 }
802 n->activateMaterial(m_item->window(), gradMat);
803 if (d->effectiveDirty & DirtyStrokeGradient) {
804 // Gradients are implemented via a texture-based material.
805 n->markDirty(QSGNode::DirtyMaterial);
806 // stop here if only the gradient changed; no need to touch the geometry
807 if (!(d->effectiveDirty & DirtyStrokeGeom))
808 return;
809 }
810 } else {
811 n->activateMaterial(m_item->window(), QQuickShapeGenericStrokeFillNode::MatSolidColor);
812 if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) {
813 ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData());
814 for (int i = 0; i < g->vertexCount(); ++i)
815 vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor);
816 return;
817 }
818 }
819
820 g->allocate(d->strokeVertices.size(), 0);
821 g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
822 memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex());
823}
824
825QSGMaterial *QQuickShapeGenericMaterialFactory::createVertexColor(QQuickWindow *window)
826{
827 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
828
829 if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
830 return new QSGVertexColorMaterial;
831
832 qWarning("Vertex-color material: Unsupported graphics API %d", api);
833 return nullptr;
834}
835
836QSGMaterial *QQuickShapeGenericMaterialFactory::createLinearGradient(QQuickWindow *window,
838{
839 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
840
841 if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
843
844 qWarning("Linear gradient material: Unsupported graphics API %d", api);
845 return nullptr;
846}
847
848QSGMaterial *QQuickShapeGenericMaterialFactory::createRadialGradient(QQuickWindow *window,
850{
851 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
852
853 if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
855
856 qWarning("Radial gradient material: Unsupported graphics API %d", api);
857 return nullptr;
858}
859
860QSGMaterial *QQuickShapeGenericMaterialFactory::createConicalGradient(QQuickWindow *window,
862{
863 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
864
865 if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
867
868 qWarning("Conical gradient material: Unsupported graphics API %d", api);
869 return nullptr;
870}
871
872QSGMaterial *QQuickShapeGenericMaterialFactory::createTextureFill(QQuickWindow *window,
874{
875 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
876
877 if (api == QSGRendererInterface::OpenGL || QSGRendererInterface::isApiRhiBased(api))
879
880 qWarning("Texture fill material: Unsupported graphics API %d", api);
881 return nullptr;
882}
883
885{
886 setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/lineargradient.vert.qsb"), viewCount);
887 setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/lineargradient.frag.qsb"), viewCount);
888}
889
891 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
892{
893 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
894 QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(newMaterial);
895 bool changed = false;
896 QByteArray *buf = state.uniformData();
897 Q_ASSERT(buf->size() >= 84 + 64);
898 const int shaderMatrixCount = newMaterial->viewCount();
899 const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
900
901 if (state.isMatrixDirty()) {
902 for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
903 const QMatrix4x4 m = state.combinedMatrix();
904 memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
905 changed = true;
906 }
907 }
908
910
911 if (!oldMaterial || m_fillTransform != node->m_fillTransform) {
912 memcpy(buf->data() + 64 * shaderMatrixCount, node->m_fillTransform.invertedData(), 64);
913 m_fillTransform = node->m_fillTransform;
914 changed = true;
915 }
916
917 if (!oldMaterial || m_gradA.x() != node->m_gradient.a.x() || m_gradA.y() != node->m_gradient.a.y()) {
918 m_gradA = QVector2D(node->m_gradient.a.x(), node->m_gradient.a.y());
919 Q_ASSERT(sizeof(m_gradA) == 8);
920 memcpy(buf->data() + 64 * shaderMatrixCount + 64, &m_gradA, 8);
921 changed = true;
922 }
923
924 if (!oldMaterial || m_gradB.x() != node->m_gradient.b.x() || m_gradB.y() != node->m_gradient.b.y()) {
925 m_gradB = QVector2D(node->m_gradient.b.x(), node->m_gradient.b.y());
926 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8, &m_gradB, 8);
927 changed = true;
928 }
929
930 if (state.isOpacityDirty()) {
931 const float opacity = state.opacity();
932 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 8, &opacity, 4);
933 changed = true;
934 }
935
936 return changed;
937}
938
939void QQuickShapeLinearGradientRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
940 QSGMaterial *newMaterial, QSGMaterial *)
941{
942 if (binding != 1)
943 return;
944
945 QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(newMaterial);
947 const QSGGradientCacheKey cacheKey(node->m_gradient.stops, QGradient::Spread(node->m_gradient.spread));
948 QSGTexture *t = QSGGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
949 t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
950 *texture = t;
951}
952
954{
955 static QSGMaterialType type;
956 return &type;
957}
958
959int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const
960{
961 Q_ASSERT(other && type() == other->type());
962 const QQuickShapeLinearGradientMaterial *m = static_cast<const QQuickShapeLinearGradientMaterial *>(other);
963
966 Q_ASSERT(a && b);
967 if (a == b)
968 return 0;
969
970 const QSGGradientCache::GradientDesc *ga = &a->m_gradient;
971 const QSGGradientCache::GradientDesc *gb = &b->m_gradient;
972
973 if (int d = ga->spread - gb->spread)
974 return d;
975
976 if (int d = ga->a.x() - gb->a.x())
977 return d;
978 if (int d = ga->a.y() - gb->a.y())
979 return d;
980 if (int d = ga->b.x() - gb->b.x())
981 return d;
982 if (int d = ga->b.y() - gb->b.y())
983 return d;
984
985 if (int d = ga->stops.size() - gb->stops.size())
986 return d;
987
988 for (int i = 0; i < ga->stops.size(); ++i) {
989 if (int d = ga->stops[i].first - gb->stops[i].first)
990 return d;
991 if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba())
992 return d;
993 }
994
995 if (int d = a->m_fillTransform.compareTo(b->m_fillTransform))
996 return d;
997
998 return 0;
999}
1000
1001QSGMaterialShader *QQuickShapeLinearGradientMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
1002{
1003 Q_UNUSED(renderMode);
1004 return new QQuickShapeLinearGradientRhiShader(viewCount());
1005}
1006
1008{
1009 setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/radialgradient.vert.qsb"), viewCount);
1010 setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/radialgradient.frag.qsb"), viewCount);
1011}
1012
1014 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
1015{
1016 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
1017 QQuickShapeRadialGradientMaterial *m = static_cast<QQuickShapeRadialGradientMaterial *>(newMaterial);
1018 bool changed = false;
1019 QByteArray *buf = state.uniformData();
1020 Q_ASSERT(buf->size() >= 92 + 64);
1021 const int shaderMatrixCount = newMaterial->viewCount();
1022 const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
1023
1024 if (state.isMatrixDirty()) {
1025 for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
1026 const QMatrix4x4 m = state.combinedMatrix();
1027 memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
1028 changed = true;
1029 }
1030 }
1031
1033
1034 if (!oldMaterial || m_fillTransform != node->m_fillTransform) {
1035 memcpy(buf->data() + 64 * shaderMatrixCount, node->m_fillTransform.invertedData(), 64);
1036 m_fillTransform = node->m_fillTransform;
1037 changed = true;
1038 }
1039
1040 const QPointF centerPoint = node->m_gradient.a;
1041 const QPointF focalPoint = node->m_gradient.b;
1042 const QPointF focalToCenter = centerPoint - focalPoint;
1043 const float centerRadius = node->m_gradient.v0;
1044 const float focalRadius = node->m_gradient.v1;
1045
1046 if (!oldMaterial || m_focalPoint.x() != focalPoint.x() || m_focalPoint.y() != focalPoint.y()) {
1047 m_focalPoint = QVector2D(focalPoint.x(), focalPoint.y());
1048 Q_ASSERT(sizeof(m_focalPoint) == 8);
1049 memcpy(buf->data() + 64 * shaderMatrixCount + 64, &m_focalPoint, 8);
1050 changed = true;
1051 }
1052
1053 if (!oldMaterial || m_focalToCenter.x() != focalToCenter.x() || m_focalToCenter.y() != focalToCenter.y()) {
1054 m_focalToCenter = QVector2D(focalToCenter.x(), focalToCenter.y());
1055 Q_ASSERT(sizeof(m_focalToCenter) == 8);
1056 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8, &m_focalToCenter, 8);
1057 changed = true;
1058 }
1059
1060 if (!oldMaterial || m_centerRadius != centerRadius) {
1061 m_centerRadius = centerRadius;
1062 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 8, &m_centerRadius, 4);
1063 changed = true;
1064 }
1065
1066 if (!oldMaterial || m_focalRadius != focalRadius) {
1067 m_focalRadius = focalRadius;
1068 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 8 + 4, &m_focalRadius, 4);
1069 changed = true;
1070 }
1071
1072 if (state.isOpacityDirty()) {
1073 const float opacity = state.opacity();
1074 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 8 + 4 + 4, &opacity, 4);
1075 changed = true;
1076 }
1077
1078 return changed;
1079}
1080
1081void QQuickShapeRadialGradientRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
1082 QSGMaterial *newMaterial, QSGMaterial *)
1083{
1084 if (binding != 1)
1085 return;
1086
1087 QQuickShapeRadialGradientMaterial *m = static_cast<QQuickShapeRadialGradientMaterial *>(newMaterial);
1089 const QSGGradientCacheKey cacheKey(node->m_gradient.stops, QGradient::Spread(node->m_gradient.spread));
1090 QSGTexture *t = QSGGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
1091 t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
1092 *texture = t;
1093}
1094
1096{
1097 static QSGMaterialType type;
1098 return &type;
1099}
1100
1101int QQuickShapeRadialGradientMaterial::compare(const QSGMaterial *other) const
1102{
1103 Q_ASSERT(other && type() == other->type());
1104 const QQuickShapeRadialGradientMaterial *m = static_cast<const QQuickShapeRadialGradientMaterial *>(other);
1105
1108 Q_ASSERT(a && b);
1109 if (a == b)
1110 return 0;
1111
1112 const QSGGradientCache::GradientDesc *ga = &a->m_gradient;
1113 const QSGGradientCache::GradientDesc *gb = &b->m_gradient;
1114
1115 if (int d = ga->spread - gb->spread)
1116 return d;
1117
1118 if (int d = ga->a.x() - gb->a.x())
1119 return d;
1120 if (int d = ga->a.y() - gb->a.y())
1121 return d;
1122 if (int d = ga->b.x() - gb->b.x())
1123 return d;
1124 if (int d = ga->b.y() - gb->b.y())
1125 return d;
1126
1127 if (int d = ga->v0 - gb->v0)
1128 return d;
1129 if (int d = ga->v1 - gb->v1)
1130 return d;
1131
1132 if (int d = ga->stops.size() - gb->stops.size())
1133 return d;
1134
1135 for (int i = 0; i < ga->stops.size(); ++i) {
1136 if (int d = ga->stops[i].first - gb->stops[i].first)
1137 return d;
1138 if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba())
1139 return d;
1140 }
1141
1142 if (int d = a->m_fillTransform.compareTo(b->m_fillTransform))
1143 return d;
1144
1145 return 0;
1146}
1147
1148QSGMaterialShader *QQuickShapeRadialGradientMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
1149{
1150 Q_UNUSED(renderMode);
1151 return new QQuickShapeRadialGradientRhiShader(viewCount());
1152}
1153
1155{
1156 setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/conicalgradient.vert.qsb"), viewCount);
1157 setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/conicalgradient.frag.qsb"), viewCount);
1158}
1159
1161 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
1162{
1163 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
1164 QQuickShapeConicalGradientMaterial *m = static_cast<QQuickShapeConicalGradientMaterial *>(newMaterial);
1165 bool changed = false;
1166 QByteArray *buf = state.uniformData();
1167 Q_ASSERT(buf->size() >= 80 + 64);
1168 const int shaderMatrixCount = newMaterial->viewCount();
1169 const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
1170
1171 if (state.isMatrixDirty()) {
1172 for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
1173 const QMatrix4x4 m = state.combinedMatrix();
1174 memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
1175 changed = true;
1176 }
1177 }
1178
1180
1181 if (!oldMaterial || m_fillTransform != node->m_fillTransform) {
1182 memcpy(buf->data() + 64 * shaderMatrixCount, node->m_fillTransform.invertedData(), 64);
1183 m_fillTransform = node->m_fillTransform;
1184 changed = true;
1185 }
1186
1187 const QPointF centerPoint = node->m_gradient.a;
1188 const float angle = -qDegreesToRadians(node->m_gradient.v0);
1189
1190 if (!oldMaterial || m_centerPoint.x() != centerPoint.x() || m_centerPoint.y() != centerPoint.y()) {
1191 m_centerPoint = QVector2D(centerPoint.x(), centerPoint.y());
1192 Q_ASSERT(sizeof(m_centerPoint) == 8);
1193 memcpy(buf->data() + 64 * shaderMatrixCount + 64, &m_centerPoint, 8);
1194 changed = true;
1195 }
1196
1197 if (!oldMaterial || m_angle != angle) {
1198 m_angle = angle;
1199 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8, &m_angle, 4);
1200 changed = true;
1201 }
1202
1203 if (state.isOpacityDirty()) {
1204 const float opacity = state.opacity();
1205 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8 + 4, &opacity, 4);
1206 changed = true;
1207 }
1208
1209 return changed;
1210}
1211
1212void QQuickShapeConicalGradientRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
1213 QSGMaterial *newMaterial, QSGMaterial *)
1214{
1215 if (binding != 1)
1216 return;
1217
1218 QQuickShapeConicalGradientMaterial *m = static_cast<QQuickShapeConicalGradientMaterial *>(newMaterial);
1220 const QSGGradientCacheKey cacheKey(node->m_gradient.stops, QGradient::Spread(node->m_gradient.spread));
1221 QSGTexture *t = QSGGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
1222 t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
1223 *texture = t;
1224}
1225
1227{
1228 static QSGMaterialType type;
1229 return &type;
1230}
1231
1232int QQuickShapeConicalGradientMaterial::compare(const QSGMaterial *other) const
1233{
1234 Q_ASSERT(other && type() == other->type());
1235 const QQuickShapeConicalGradientMaterial *m = static_cast<const QQuickShapeConicalGradientMaterial *>(other);
1236
1239 Q_ASSERT(a && b);
1240 if (a == b)
1241 return 0;
1242
1243 const QSGGradientCache::GradientDesc *ga = &a->m_gradient;
1244 const QSGGradientCache::GradientDesc *gb = &b->m_gradient;
1245
1246 if (int d = ga->a.x() - gb->a.x())
1247 return d;
1248 if (int d = ga->a.y() - gb->a.y())
1249 return d;
1250
1251 if (int d = ga->v0 - gb->v0)
1252 return d;
1253
1254 if (int d = ga->stops.size() - gb->stops.size())
1255 return d;
1256
1257 for (int i = 0; i < ga->stops.size(); ++i) {
1258 if (int d = ga->stops[i].first - gb->stops[i].first)
1259 return d;
1260 if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba())
1261 return d;
1262 }
1263
1264 if (int d = a->m_fillTransform.compareTo(b->m_fillTransform))
1265 return d;
1266
1267 return 0;
1268}
1269
1270QSGMaterialShader *QQuickShapeConicalGradientMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
1271{
1272 Q_UNUSED(renderMode);
1273 return new QQuickShapeConicalGradientRhiShader(viewCount());
1274}
1275
1277{
1278 setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/texturefill.vert.qsb"), viewCount);
1279 setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/texturefill.frag.qsb"), viewCount);
1280}
1281
1283 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
1284{
1285 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
1286 QQuickShapeTextureFillMaterial *m = static_cast<QQuickShapeTextureFillMaterial *>(newMaterial);
1287 bool changed = false;
1288 QByteArray *buf = state.uniformData();
1289 const int shaderMatrixCount = newMaterial->viewCount();
1290 const int matrixCount = qMin(state.projectionMatrixCount(), shaderMatrixCount);
1291 Q_ASSERT(buf->size() >= 64 * shaderMatrixCount + 64 + 8 + 4);
1292
1293 if (state.isMatrixDirty()) {
1294 for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
1295 const QMatrix4x4 m = state.combinedMatrix();
1296 memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
1297 changed = true;
1298 }
1299 }
1300
1302
1303 if (!oldMaterial || m_fillTransform != node->m_fillTransform) {
1304 memcpy(buf->data() + 64 * shaderMatrixCount, node->m_fillTransform.invertedData(), 64);
1305 m_fillTransform = node->m_fillTransform;
1306 changed = true;
1307 }
1308
1309 const QSizeF boundsSize = node->m_fillTextureProvider != nullptr && node->m_fillTextureProvider->texture() != nullptr
1310 ? node->m_fillTextureProvider->texture()->textureSize()
1311 : QSizeF(0, 0);
1312
1313
1314 const QVector2D boundsVector(boundsSize.width() / state.devicePixelRatio(),
1315 boundsSize.height() / state.devicePixelRatio());
1316 if (!oldMaterial || m_boundsSize != boundsVector) {
1317 m_boundsSize = boundsVector;
1318 Q_ASSERT(sizeof(m_boundsSize) == 8);
1319 memcpy(buf->data() + 64 * shaderMatrixCount + 64, &m_boundsSize, 8);
1320 changed = true;
1321 }
1322
1323 if (state.isOpacityDirty()) {
1324 const float opacity = state.opacity();
1325 memcpy(buf->data() + 64 * shaderMatrixCount + 64 + 8, &opacity, 4);
1326 changed = true;
1327 }
1328
1329 return changed;
1330}
1331
1332void QQuickShapeTextureFillRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
1333 QSGMaterial *newMaterial, QSGMaterial *)
1334{
1335 if (binding != 1)
1336 return;
1337
1338 QQuickShapeTextureFillMaterial *m = static_cast<QQuickShapeTextureFillMaterial *>(newMaterial);
1340 if (node->m_fillTextureProvider != nullptr) {
1341 QSGTexture *providedTexture = node->m_fillTextureProvider->texture();
1342 if (providedTexture != nullptr) {
1343 if (providedTexture->isAtlasTexture()) {
1344 // Create a non-atlas copy to make texture coordinate wrapping work. This
1345 // texture copy is owned by the QSGTexture so memory is managed with the original
1346 // texture provider.
1347 QSGTexture *newTexture = providedTexture->removedFromAtlas(state.resourceUpdateBatch());
1348 if (newTexture != nullptr)
1349 providedTexture = newTexture;
1350 }
1351
1352 providedTexture->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
1353 *texture = providedTexture;
1354 return;
1355 }
1356 }
1357
1358 if (m->dummyTexture() == nullptr) {
1359 QSGPlainTexture *dummyTexture = new QSGPlainTexture;
1360 dummyTexture->setFiltering(QSGTexture::Nearest);
1361 dummyTexture->setHorizontalWrapMode(QSGTexture::Repeat);
1362 dummyTexture->setVerticalWrapMode(QSGTexture::Repeat);
1363 QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
1364 img.fill(0);
1365 dummyTexture->setImage(img);
1366 dummyTexture->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
1367
1368 m->setDummyTexture(dummyTexture);
1369 }
1370
1371 *texture = m->dummyTexture();
1372}
1373
1375{
1376 delete m_dummyTexture;
1377}
1378
1380{
1381 static QSGMaterialType type;
1382 return &type;
1383}
1384
1385int QQuickShapeTextureFillMaterial::compare(const QSGMaterial *other) const
1386{
1387 Q_ASSERT(other && type() == other->type());
1388 const QQuickShapeTextureFillMaterial *m = static_cast<const QQuickShapeTextureFillMaterial *>(other);
1389
1392 Q_ASSERT(a && b);
1393 if (a == b)
1394 return 0;
1395
1396 if (int d = a->m_fillTransform.compareTo(b->m_fillTransform))
1397 return d;
1398
1399 const qintptr diff = qintptr(a->m_fillTextureProvider) - qintptr(b->m_fillTextureProvider);
1400 return diff < 0 ? -1 : (diff > 0 ? 1 : 0);
1401}
1402
1403QSGMaterialShader *QQuickShapeTextureFillMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
1404{
1405 Q_UNUSED(renderMode);
1406 return new QQuickShapeTextureFillRhiShader(viewCount());
1407}
1408
1409QT_END_NAMESPACE
1410
1411#include "moc_qquickshapegenericrenderer_p.cpp"
QSGMaterialType * type() const override
This function is called by the scene graph to query an identifier that is unique to the QSGMaterialSh...
QQuickShapeConicalGradientMaterial(QQuickShapeGenericStrokeFillNode *node)
int compare(const QSGMaterial *other) const override
Compares this material to other and returns 0 if they are equal; -1 if this material should sort befo...
QSGMaterialShader * createShader(QSGRendererInterface::RenderMode renderMode) const override
This function returns a new instance of a the QSGMaterialShader implementation used to render geometr...
QQuickShapeGenericStrokeFillNode * node() const
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,...
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to get the contents of the shader program's uniform buffer...
QQuickShapeGenericRenderer::Color4ub fillColor
QQuickShapeGenericRenderer::VertexContainerType fillVertices
QQuickShapeGenericRenderer::IndexContainerType fillIndices
QQuickShapeGenericStrokeFillNode * m_fillNode
QQuickShapeGenericStrokeFillNode * m_strokeNode
void setAsyncCallback(void(*)(void *), void *) override
void setFillGradient(int index, QQuickShapeGradient *gradient) override
void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, qreal dashOffset, const QList< qreal > &dashPattern) override
void setFillTransform(int index, const QSGTransform &transform) override
void setCosmeticStroke(int index, bool c) override
void setStrokeGradient(int index, QQuickShapeGradient *gradient) override
QList< QSGGeometry::ColoredPoint2D > VertexContainerType
void setPath(int index, const QPainterPath &path, QQuickShapePath::PathHints pathHints={}) override
void setStrokeColor(int index, const QColor &color) override
void setFillRule(int index, QQuickShapePath::FillRule fillRule) override
void setFillTextureProvider(int index, QQuickItem *textureProviderItem) override
void setFillColor(int index, const QColor &color) override
void setTriangulationScale(int index, qreal scale) override
void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override
void setStrokeWidth(int index, qreal w) override
void setRootNode(QQuickShapeGenericNode *node)
void handleSceneChange(QQuickWindow *window) override
void beginSync(int totalCount, bool *countChanged) override
void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override
void preprocess() override
Override this function to do processing on the node before it is rendered.
void activateMaterial(QQuickWindow *window, Material m)
QQuickShapeLinearGradientMaterial(QQuickShapeGenericStrokeFillNode *node)
QSGMaterialShader * createShader(QSGRendererInterface::RenderMode renderMode) const override
This function returns a new instance of a the QSGMaterialShader implementation used to render geometr...
int compare(const QSGMaterial *other) const override
Compares this material to other and returns 0 if they are equal; -1 if this material should sort befo...
QSGMaterialType * type() const override
This function is called by the scene graph to query an identifier that is unique to the QSGMaterialSh...
QQuickShapeGenericStrokeFillNode * node() const
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,...
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to get the contents of the shader program's uniform buffer...
QQuickShapeGenericStrokeFillNode * node() const
QQuickShapeRadialGradientMaterial(QQuickShapeGenericStrokeFillNode *node)
QSGMaterialType * type() const override
This function is called by the scene graph to query an identifier that is unique to the QSGMaterialSh...
QSGMaterialShader * createShader(QSGRendererInterface::RenderMode renderMode) const override
This function returns a new instance of a the QSGMaterialShader implementation used to render geometr...
int compare(const QSGMaterial *other) const override
Compares this material to other and returns 0 if they are equal; -1 if this material should sort befo...
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to get the contents of the shader program's uniform buffer...
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,...
QQuickShapeGenericRenderer::Color4ub strokeColor
QQuickShapeGenericRenderer::VertexContainerType strokeVertices
QQuickShapeTextureFillMaterial(QQuickShapeGenericStrokeFillNode *node)
QSGMaterialShader * createShader(QSGRendererInterface::RenderMode renderMode) const override
This function returns a new instance of a the QSGMaterialShader implementation used to render geometr...
int compare(const QSGMaterial *other) const override
Compares this material to other and returns 0 if they are equal; -1 if this material should sort befo...
QQuickShapeGenericStrokeFillNode * node() const
QSGMaterialType * type() const override
This function is called by the scene graph to query an identifier that is unique to the QSGMaterialSh...
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override
This function is called by the scene graph to get the contents of the shader program's uniform buffer...
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,...
Combined button and popup list for selecting options.
static QQuickShapeGenericRenderer::Color4ub colorToColor4ub(const QColor &c)
static QQuickAbstractPathRenderer::FillGradientType copyGenericGradient(const QQuickShapeGradient *gradient, QSGGradientCache::GradientDesc *dst)
#define QSG_RUNTIME_DESCRIPTION
Definition qsgnode.h:17
void set(float nx, float ny, QQuickShapeGenericRenderer::Color4ub ncolor)