Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qquickshapegenericrenderer.cpp
Go to the documentation of this file.
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
5#include <QtGui/private/qtriangulator_p.h>
6#include <QtGui/private/qtriangulatingstroker_p.h>
7#include <rhi/qrhi.h>
8#include <QSGVertexColorMaterial>
9
10#if QT_CONFIG(thread)
11#include <QThreadPool>
12#endif
13
15
16struct ColoredVertex // must match QSGGeometry::ColoredPoint2D
17{
18 float x, y;
20 void set(float nx, float ny, QQuickShapeGenericRenderer::Color4ub ncolor)
21 {
22 x = nx; y = ny; color = ncolor;
23 }
24};
25
27{
28 float r, g, b, a;
29 c.getRgbF(&r, &g, &b, &a);
31 uchar(qRound(r * a * 255)),
32 uchar(qRound(g * a * 255)),
33 uchar(qRound(b * a * 255)),
34 uchar(qRound(a * 255))
35 };
36 return color;
37}
38
40 : m_material(nullptr)
41{
45#ifdef QSG_RUNTIME_DESCRIPTION
46 qsgnode_set_description(this, QLatin1String("stroke-fill"));
47#endif
48}
49
51{
52 switch (m) {
53 case MatSolidColor:
54 // Use vertexcolor material. Items with different colors remain batchable
55 // this way, at the expense of having to provide per-vertex color values.
57 break;
60 break;
63 break;
66 break;
67 default:
68 qWarning("Unknown material %d", m);
69 return;
70 }
71
72 if (material() != m_material.data())
73 setMaterial(m_material.data());
74}
75
77{
78 for (ShapePathData &d : m_sp) {
79 if (d.pendingFill)
80 d.pendingFill->orphaned = true;
81 if (d.pendingStroke)
82 d.pendingStroke->orphaned = true;
83 }
84}
85
86// sync, and so triangulation too, happens on the gui thread
87// - except when async is set, in which case triangulation is moved to worker threads
88
89void QQuickShapeGenericRenderer::beginSync(int totalCount, bool *countChanged)
90{
91 if (m_sp.size() != totalCount) {
92 m_sp.resize(totalCount);
93 m_accDirty |= DirtyList;
94 *countChanged = true;
95 } else {
96 *countChanged = false;
97 }
98 for (ShapePathData &d : m_sp)
99 d.syncDirty = 0;
100}
101
103{
104 ShapePathData &d(m_sp[index]);
105 d.path = path ? path->path() : QPainterPath();
106 d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom;
107}
108
110{
111 ShapePathData &d(m_sp[index]);
112 const bool wasTransparent = d.strokeColor.a == 0;
113 d.strokeColor = colorToColor4ub(color);
114 const bool isTransparent = d.strokeColor.a == 0;
115 d.syncDirty |= DirtyColor;
116 if (wasTransparent && !isTransparent)
117 d.syncDirty |= DirtyStrokeGeom;
118}
119
121{
122 ShapePathData &d(m_sp[index]);
123 d.strokeWidth = w;
124 if (w >= 0.0f)
125 d.pen.setWidthF(w);
126 d.syncDirty |= DirtyStrokeGeom;
127}
128
130{
131 ShapePathData &d(m_sp[index]);
132 const bool wasTransparent = d.fillColor.a == 0;
133 d.fillColor = colorToColor4ub(color);
134 const bool isTransparent = d.fillColor.a == 0;
135 d.syncDirty |= DirtyColor;
136 if (wasTransparent && !isTransparent)
137 d.syncDirty |= DirtyFillGeom;
138}
139
141{
142 ShapePathData &d(m_sp[index]);
143 d.fillRule = Qt::FillRule(fillRule);
144 d.syncDirty |= DirtyFillGeom;
145}
146
148{
149 ShapePathData &d(m_sp[index]);
150 d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
151 d.pen.setMiterLimit(miterLimit);
152 d.syncDirty |= DirtyStrokeGeom;
153}
154
156{
157 ShapePathData &d(m_sp[index]);
158 d.pen.setCapStyle(Qt::PenCapStyle(capStyle));
159 d.syncDirty |= DirtyStrokeGeom;
160}
161
163 qreal dashOffset, const QVector<qreal> &dashPattern)
164{
165 ShapePathData &d(m_sp[index]);
166 d.pen.setStyle(Qt::PenStyle(strokeStyle));
167 if (strokeStyle == QQuickShapePath::DashLine) {
168 d.pen.setDashPattern(dashPattern);
169 d.pen.setDashOffset(dashOffset);
170 }
171 d.syncDirty |= DirtyStrokeGeom;
172}
173
175{
176 ShapePathData &d(m_sp[index]);
177 if (gradient) {
178 d.fillGradient.stops = gradient->gradientStops(); // sorted
179 d.fillGradient.spread = gradient->spread();
180 if (QQuickShapeLinearGradient *g = qobject_cast<QQuickShapeLinearGradient *>(gradient)) {
181 d.fillGradientActive = LinearGradient;
182 d.fillGradient.a = QPointF(g->x1(), g->y1());
183 d.fillGradient.b = QPointF(g->x2(), g->y2());
184 } else if (QQuickShapeRadialGradient *g = qobject_cast<QQuickShapeRadialGradient *>(gradient)) {
185 d.fillGradientActive = RadialGradient;
186 d.fillGradient.a = QPointF(g->centerX(), g->centerY());
187 d.fillGradient.b = QPointF(g->focalX(), g->focalY());
188 d.fillGradient.v0 = g->centerRadius();
189 d.fillGradient.v1 = g->focalRadius();
190 } else if (QQuickShapeConicalGradient *g = qobject_cast<QQuickShapeConicalGradient *>(gradient)) {
191 d.fillGradientActive = ConicalGradient;
192 d.fillGradient.a = QPointF(g->centerX(), g->centerY());
193 d.fillGradient.v0 = g->angle();
194 } else {
195 Q_UNREACHABLE();
196 }
197 } else {
198 d.fillGradientActive = NoGradient;
199 }
200 d.syncDirty |= DirtyFillGradient;
201}
202
204{
205 // No dirty, this is called at the start of every sync. Just store the value.
206 m_triangulationScale = scale;
207}
208
210{
213 emit done(this);
214}
215
217{
219 emit done(this);
220}
221
222void QQuickShapeGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data)
223{
224 m_asyncCallback = callback;
225 m_asyncCallbackData = data;
226}
227
228#if QT_CONFIG(thread)
229static QThreadPool *pathWorkThreadPool = nullptr;
230
231static void deletePathWorkThreadPool()
232{
233 delete pathWorkThreadPool;
234 pathWorkThreadPool = nullptr;
235}
236#endif
237
239{
240#if !QT_CONFIG(thread)
241 // Force synchronous mode for the no-thread configuration due
242 // to lack of QThreadPool.
243 async = false;
244#endif
245
246 bool didKickOffAsync = false;
247
248 for (int i = 0; i < m_sp.size(); ++i) {
249 ShapePathData &d(m_sp[i]);
250 if (!d.syncDirty)
251 continue;
252
253 m_accDirty |= d.syncDirty;
254
255 // Use a shadow dirty flag in order to avoid losing state in case there are
256 // multiple syncs with different dirty flags before we get to updateNode()
257 // on the render thread (with the gui thread blocked). For our purposes
258 // here syncDirty is still required since geometry regeneration must only
259 // happen when there was an actual change in this particular sync round.
260 d.effectiveDirty |= d.syncDirty;
261
262 if (d.path.isEmpty()) {
263 d.fillVertices.clear();
264 d.fillIndices.clear();
265 d.strokeVertices.clear();
266 continue;
267 }
268
269#if QT_CONFIG(thread)
270 if (async && !pathWorkThreadPool) {
271 qAddPostRoutine(deletePathWorkThreadPool);
272 pathWorkThreadPool = new QThreadPool;
273 const int idealCount = QThread::idealThreadCount();
274 pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4);
275 }
276#endif
277 auto testFeatureIndexUint = [](QQuickItem *item) -> bool {
278 if (auto *w = item->window()) {
279 if (auto *rhi = QQuickWindowPrivate::get(w)->rhi)
280 return rhi->isFeatureSupported(QRhi::ElementIndexUint);
281 }
282 return true;
283 };
284 static bool supportsElementIndexUint = testFeatureIndexUint(m_item);
285 if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) {
286 d.path.setFillRule(d.fillRule);
288 m_api = m_item->window()->rendererInterface()->graphicsApi();
289 if (async) {
291 r->setAutoDelete(false);
292 if (d.pendingFill)
293 d.pendingFill->orphaned = true;
294 d.pendingFill = r;
295 r->path = d.path;
296 r->fillColor = d.fillColor;
297 r->supportsElementIndexUint = supportsElementIndexUint;
298 r->triangulationScale = m_triangulationScale;
299 // Unlikely in practice but in theory m_sp could be
300 // resized. Therefore, capture 'i' instead of 'd'.
302 // Bail out when orphaned (meaning either another run was
303 // started after this one, or the renderer got destroyed).
304 if (!r->orphaned && i < m_sp.size()) {
305 ShapePathData &d(m_sp[i]);
306 d.fillVertices = r->fillVertices;
307 d.fillIndices = r->fillIndices;
308 d.indexType = r->indexType;
309 d.pendingFill = nullptr;
310 d.effectiveDirty |= DirtyFillGeom;
311 maybeUpdateAsyncItem();
312 }
313 r->deleteLater();
314 });
315 didKickOffAsync = true;
316#if QT_CONFIG(thread)
317 // qtVectorPathForPath() initializes a unique_ptr without locking.
318 // Do that before starting the threads as otherwise we get a race condition.
319 qtVectorPathForPath(r->path);
320 pathWorkThreadPool->start(r);
321#endif
322 } else {
323 triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType,
324 supportsElementIndexUint,
325 m_triangulationScale);
326 }
327 }
328
329 if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth >= 0.0f && d.strokeColor.a) {
330 if (async) {
332 r->setAutoDelete(false);
333 if (d.pendingStroke)
334 d.pendingStroke->orphaned = true;
335 d.pendingStroke = r;
336 r->path = d.path;
337 r->pen = d.pen;
338 r->strokeColor = d.strokeColor;
339 r->clipSize = QSize(m_item->width(), m_item->height());
340 r->triangulationScale = m_triangulationScale;
342 if (!r->orphaned && i < m_sp.size()) {
343 ShapePathData &d(m_sp[i]);
344 d.strokeVertices = r->strokeVertices;
345 d.pendingStroke = nullptr;
346 d.effectiveDirty |= DirtyStrokeGeom;
347 maybeUpdateAsyncItem();
348 }
349 r->deleteLater();
350 });
351 didKickOffAsync = true;
352#if QT_CONFIG(thread)
353 // qtVectorPathForPath() initializes a unique_ptr without locking.
354 // Do that before starting the threads as otherwise we get a race condition.
355 qtVectorPathForPath(r->path);
356 pathWorkThreadPool->start(r);
357#endif
358 } else {
359 triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices,
360 QSize(m_item->width(), m_item->height()), m_triangulationScale);
361 }
362 }
363 }
364
365 if (!didKickOffAsync && async && m_asyncCallback)
366 m_asyncCallback(m_asyncCallbackData);
367}
368
369void QQuickShapeGenericRenderer::maybeUpdateAsyncItem()
370{
371 for (const ShapePathData &d : std::as_const(m_sp)) {
372 if (d.pendingFill || d.pendingStroke)
373 return;
374 }
375 m_accDirty |= DirtyFillGeom | DirtyStrokeGeom;
376 m_item->update();
377 if (m_asyncCallback)
378 m_asyncCallback(m_asyncCallbackData);
379}
380
381// the stroke/fill triangulation functions may be invoked either on the gui
382// thread or some worker thread and must thus be self-contained.
384 const Color4ub &fillColor,
385 VertexContainerType *fillVertices,
386 IndexContainerType *fillIndices,
387 QSGGeometry::Type *indexType,
388 bool supportsElementIndexUint,
389 qreal triangulationScale)
390{
392
393 QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(triangulationScale, triangulationScale), 1, supportsElementIndexUint);
394 const int vertexCount = ts.vertices.size() / 2; // just a qreal vector with x,y hence the / 2
395 fillVertices->resize(vertexCount);
396 ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(fillVertices->data());
397 const qreal *vsrc = ts.vertices.constData();
398 for (int i = 0; i < vertexCount; ++i)
399 vdst[i].set(vsrc[i * 2] / triangulationScale, vsrc[i * 2 + 1] / triangulationScale, fillColor);
400
401 size_t indexByteSize;
404 // fillIndices is still QVector<quint32>. Just resize to N/2 and pack
405 // the N quint16s into it.
406 fillIndices->resize(ts.indices.size() / 2);
407 indexByteSize = ts.indices.size() * sizeof(quint16);
408 } else {
409 *indexType = QSGGeometry::UnsignedIntType;
410 fillIndices->resize(ts.indices.size());
411 indexByteSize = ts.indices.size() * sizeof(quint32);
412 }
413 memcpy(fillIndices->data(), ts.indices.data(), indexByteSize);
414}
415
417 const QPen &pen,
418 const Color4ub &strokeColor,
419 VertexContainerType *strokeVertices,
420 const QSize &clipSize,
421 qreal triangulationScale)
422{
424 const QRectF clip(QPointF(0, 0), clipSize);
425 const qreal inverseScale = 1.0 / triangulationScale;
426
427 QTriangulatingStroker stroker;
428 stroker.setInvScale(inverseScale);
429
430 if (pen.style() == Qt::SolidLine) {
431 stroker.process(vp, pen, clip, {});
432 } else {
433 QDashedStrokeProcessor dashStroker;
434 dashStroker.setInvScale(inverseScale);
435 dashStroker.process(vp, pen, clip, {});
436 QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(),
437 dashStroker.elementTypes(), 0);
438 stroker.process(dashStroke, pen, clip, {});
439 }
440
441 if (!stroker.vertexCount()) {
442 strokeVertices->clear();
443 return;
444 }
445
446 const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2
447 strokeVertices->resize(vertexCount);
448 ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(strokeVertices->data());
449 const float *vsrc = stroker.vertices();
450 for (int i = 0; i < vertexCount; ++i)
451 vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor);
452}
453
455{
456 if (m_rootNode != node) {
457 m_rootNode = node;
458 m_accDirty |= DirtyList;
459 }
460}
461
462// on the render thread with gui blocked
464{
465 if (!m_rootNode || !m_accDirty)
466 return;
467
468// [ m_rootNode ]
469// / / /
470// #0 [ fill ] [ stroke ] [ next ]
471// / / |
472// #1 [ fill ] [ stroke ] [ next ]
473// / / |
474// #2 [ fill ] [ stroke ] [ next ]
475// ...
476// ...
477
478 QQuickShapeGenericNode **nodePtr = &m_rootNode;
479 QQuickShapeGenericNode *prevNode = nullptr;
480
481 for (ShapePathData &d : m_sp) {
482 if (!*nodePtr) {
483 Q_ASSERT(prevNode);
484 *nodePtr = new QQuickShapeGenericNode;
485 prevNode->m_next = *nodePtr;
486 prevNode->appendChildNode(*nodePtr);
487 }
488
489 QQuickShapeGenericNode *node = *nodePtr;
490
491 if (m_accDirty & DirtyList)
493
494 if (!d.effectiveDirty) {
495 prevNode = node;
496 nodePtr = &node->m_next;
497 continue;
498 }
499
500 if (d.fillColor.a == 0) {
501 delete node->m_fillNode;
502 node->m_fillNode = nullptr;
503 } else if (!node->m_fillNode) {
505 if (node->m_strokeNode)
506 node->removeChildNode(node->m_strokeNode);
507 node->appendChildNode(node->m_fillNode);
508 if (node->m_strokeNode)
509 node->appendChildNode(node->m_strokeNode);
510 d.effectiveDirty |= DirtyFillGeom;
511 }
512
513 if (d.strokeWidth < 0.0f || d.strokeColor.a == 0) {
514 delete node->m_strokeNode;
515 node->m_strokeNode = nullptr;
516 } else if (!node->m_strokeNode) {
518 node->appendChildNode(node->m_strokeNode);
519 d.effectiveDirty |= DirtyStrokeGeom;
520 }
521
522 updateFillNode(&d, node);
523 updateStrokeNode(&d, node);
524
525 d.effectiveDirty = 0;
526
527 prevNode = node;
528 nodePtr = &node->m_next;
529 }
530
531 if (*nodePtr && prevNode) {
532 prevNode->removeChildNode(*nodePtr);
533 delete *nodePtr;
534 *nodePtr = nullptr;
535 }
536
537 m_accDirty = 0;
538}
539
540void QQuickShapeGenericRenderer::updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n)
541{
542 if (d->fillGradientActive) {
543 if (d->effectiveDirty & DirtyFillGradient)
544 n->m_fillGradient = d->fillGradient;
545 }
546}
547
548void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGenericNode *node)
549{
550 if (!node->m_fillNode)
551 return;
552 if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient)))
553 return;
554
555 // Make a copy of the data that will be accessed by the material on
556 // the render thread. This must be done even when we bail out below.
558 updateShadowDataInNode(d, n);
559
560 QSGGeometry *g = n->geometry();
561 if (d->fillVertices.isEmpty()) {
562 if (g->vertexCount() || g->indexCount()) {
563 g->allocate(0, 0);
564 n->markDirty(QSGNode::DirtyGeometry);
565 }
566 return;
567 }
568
569 if (d->fillGradientActive) {
571 switch (d->fillGradientActive) {
572 case LinearGradient:
574 break;
575 case RadialGradient:
577 break;
578 case ConicalGradient:
580 break;
581 default:
582 Q_UNREACHABLE_RETURN();
583 }
584 n->activateMaterial(m_item->window(), gradMat);
585 if (d->effectiveDirty & DirtyFillGradient) {
586 // Gradients are implemented via a texture-based material.
587 n->markDirty(QSGNode::DirtyMaterial);
588 // stop here if only the gradient changed; no need to touch the geometry
589 if (!(d->effectiveDirty & DirtyFillGeom))
590 return;
591 }
592 } else {
593 n->activateMaterial(m_item->window(), QQuickShapeGenericStrokeFillNode::MatSolidColor);
594 // fast path for updating only color values when no change in vertex positions
595 if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) {
596 ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData());
597 for (int i = 0; i < g->vertexCount(); ++i)
598 vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor);
599 n->markDirty(QSGNode::DirtyGeometry);
600 return;
601 }
602 }
603
604 const int indexCount = d->indexType == QSGGeometry::UnsignedShortType
605 ? d->fillIndices.size() * 2 : d->fillIndices.size();
606 if (g->indexType() != d->indexType) {
608 d->fillVertices.size(), indexCount, d->indexType);
609 n->setGeometry(g);
610 } else {
611 g->allocate(d->fillVertices.size(), indexCount);
612 }
613 g->setDrawingMode(QSGGeometry::DrawTriangles);
614 memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex());
615 memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex());
616
617 n->markDirty(QSGNode::DirtyGeometry);
618}
619
620void QQuickShapeGenericRenderer::updateStrokeNode(ShapePathData *d, QQuickShapeGenericNode *node)
621{
622 if (!node->m_strokeNode)
623 return;
624 if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor)))
625 return;
626
628 QSGGeometry *g = n->geometry();
629 if (d->strokeVertices.isEmpty()) {
630 if (g->vertexCount() || g->indexCount()) {
631 g->allocate(0, 0);
632 n->markDirty(QSGNode::DirtyGeometry);
633 }
634 return;
635 }
636
637 n->markDirty(QSGNode::DirtyGeometry);
638
639 // Async loading runs update once, bails out above, then updates again once
640 // ready. Set the material dirty then. This is in-line with fill where the
641 // first activateMaterial() achieves the same.
642 if (!g->vertexCount())
643 n->markDirty(QSGNode::DirtyMaterial);
644
645 if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) {
646 ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData());
647 for (int i = 0; i < g->vertexCount(); ++i)
648 vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor);
649 return;
650 }
651
652 g->allocate(d->strokeVertices.size(), 0);
653 g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
654 memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex());
655}
656
658{
659 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
660
662 return new QSGVertexColorMaterial;
663
664 qWarning("Vertex-color material: Unsupported graphics API %d", api);
665 return nullptr;
666}
667
670{
671 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
672
674 return new QQuickShapeLinearGradientMaterial(node);
675
676 qWarning("Linear gradient material: Unsupported graphics API %d", api);
677 return nullptr;
678}
679
682{
683 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
684
686 return new QQuickShapeRadialGradientMaterial(node);
687
688 qWarning("Radial gradient material: Unsupported graphics API %d", api);
689 return nullptr;
690}
691
694{
695 QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
696
698 return new QQuickShapeConicalGradientMaterial(node);
699
700 qWarning("Conical gradient material: Unsupported graphics API %d", api);
701 return nullptr;
702}
703
705{
706 setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/lineargradient.vert.qsb"));
707 setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/lineargradient.frag.qsb"));
708}
709
711 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
712{
713 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
715 bool changed = false;
716 QByteArray *buf = state.uniformData();
717 Q_ASSERT(buf->size() >= 84);
718
719 if (state.isMatrixDirty()) {
720 const QMatrix4x4 m = state.combinedMatrix();
721 memcpy(buf->data(), m.constData(), 64);
722 changed = true;
723 }
724
725 QQuickShapeGenericStrokeFillNode *node = m->node();
726
727 if (!oldMaterial || m_gradA.x() != node->m_fillGradient.a.x() || m_gradA.y() != node->m_fillGradient.a.y()) {
728 m_gradA = QVector2D(node->m_fillGradient.a.x(), node->m_fillGradient.a.y());
729 Q_ASSERT(sizeof(m_gradA) == 8);
730 memcpy(buf->data() + 64, &m_gradA, 8);
731 changed = true;
732 }
733
734 if (!oldMaterial || m_gradB.x() != node->m_fillGradient.b.x() || m_gradB.y() != node->m_fillGradient.b.y()) {
735 m_gradB = QVector2D(node->m_fillGradient.b.x(), node->m_fillGradient.b.y());
736 memcpy(buf->data() + 72, &m_gradB, 8);
737 changed = true;
738 }
739
740 if (state.isOpacityDirty()) {
741 const float opacity = state.opacity();
742 memcpy(buf->data() + 80, &opacity, 4);
743 changed = true;
744 }
745
746 return changed;
747}
748
750 QSGMaterial *newMaterial, QSGMaterial *)
751{
752 if (binding != 1)
753 return;
754
756 QQuickShapeGenericStrokeFillNode *node = m->node();
759 t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
760 *texture = t;
761}
762
764{
765 static QSGMaterialType type;
766 return &type;
767}
768
770{
771 Q_ASSERT(other && type() == other->type());
773
776 Q_ASSERT(a && b);
777 if (a == b)
778 return 0;
779
780 const QQuickAbstractPathRenderer::GradientDesc *ga = &a->m_fillGradient;
781 const QQuickAbstractPathRenderer::GradientDesc *gb = &b->m_fillGradient;
782
783 if (int d = ga->spread - gb->spread)
784 return d;
785
786 if (int d = ga->a.x() - gb->a.x())
787 return d;
788 if (int d = ga->a.y() - gb->a.y())
789 return d;
790 if (int d = ga->b.x() - gb->b.x())
791 return d;
792 if (int d = ga->b.y() - gb->b.y())
793 return d;
794
795 if (int d = ga->stops.size() - gb->stops.size())
796 return d;
797
798 for (int i = 0; i < ga->stops.size(); ++i) {
799 if (int d = ga->stops[i].first - gb->stops[i].first)
800 return d;
801 if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba())
802 return d;
803 }
804
805 return 0;
806}
807
809{
810 Q_UNUSED(renderMode);
812}
813
815{
816 setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/radialgradient.vert.qsb"));
817 setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/radialgradient.frag.qsb"));
818}
819
821 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
822{
823 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
825 bool changed = false;
826 QByteArray *buf = state.uniformData();
827 Q_ASSERT(buf->size() >= 92);
828
829 if (state.isMatrixDirty()) {
830 const QMatrix4x4 m = state.combinedMatrix();
831 memcpy(buf->data(), m.constData(), 64);
832 changed = true;
833 }
834
835 QQuickShapeGenericStrokeFillNode *node = m->node();
836
837 const QPointF centerPoint = node->m_fillGradient.a;
838 const QPointF focalPoint = node->m_fillGradient.b;
839 const QPointF focalToCenter = centerPoint - focalPoint;
840 const float centerRadius = node->m_fillGradient.v0;
841 const float focalRadius = node->m_fillGradient.v1;
842
843 if (!oldMaterial || m_focalPoint.x() != focalPoint.x() || m_focalPoint.y() != focalPoint.y()) {
844 m_focalPoint = QVector2D(focalPoint.x(), focalPoint.y());
845 Q_ASSERT(sizeof(m_focalPoint) == 8);
846 memcpy(buf->data() + 64, &m_focalPoint, 8);
847 changed = true;
848 }
849
850 if (!oldMaterial || m_focalToCenter.x() != focalToCenter.x() || m_focalToCenter.y() != focalToCenter.y()) {
851 m_focalToCenter = QVector2D(focalToCenter.x(), focalToCenter.y());
852 Q_ASSERT(sizeof(m_focalToCenter) == 8);
853 memcpy(buf->data() + 72, &m_focalToCenter, 8);
854 changed = true;
855 }
856
857 if (!oldMaterial || m_centerRadius != centerRadius) {
858 m_centerRadius = centerRadius;
859 memcpy(buf->data() + 80, &m_centerRadius, 4);
860 changed = true;
861 }
862
863 if (!oldMaterial || m_focalRadius != focalRadius) {
864 m_focalRadius = focalRadius;
865 memcpy(buf->data() + 84, &m_focalRadius, 4);
866 changed = true;
867 }
868
869 if (state.isOpacityDirty()) {
870 const float opacity = state.opacity();
871 memcpy(buf->data() + 88, &opacity, 4);
872 changed = true;
873 }
874
875 return changed;
876}
877
879 QSGMaterial *newMaterial, QSGMaterial *)
880{
881 if (binding != 1)
882 return;
883
885 QQuickShapeGenericStrokeFillNode *node = m->node();
888 t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
889 *texture = t;
890}
891
893{
894 static QSGMaterialType type;
895 return &type;
896}
897
899{
900 Q_ASSERT(other && type() == other->type());
902
905 Q_ASSERT(a && b);
906 if (a == b)
907 return 0;
908
909 const QQuickAbstractPathRenderer::GradientDesc *ga = &a->m_fillGradient;
910 const QQuickAbstractPathRenderer::GradientDesc *gb = &b->m_fillGradient;
911
912 if (int d = ga->spread - gb->spread)
913 return d;
914
915 if (int d = ga->a.x() - gb->a.x())
916 return d;
917 if (int d = ga->a.y() - gb->a.y())
918 return d;
919 if (int d = ga->b.x() - gb->b.x())
920 return d;
921 if (int d = ga->b.y() - gb->b.y())
922 return d;
923
924 if (int d = ga->v0 - gb->v0)
925 return d;
926 if (int d = ga->v1 - gb->v1)
927 return d;
928
929 if (int d = ga->stops.size() - gb->stops.size())
930 return d;
931
932 for (int i = 0; i < ga->stops.size(); ++i) {
933 if (int d = ga->stops[i].first - gb->stops[i].first)
934 return d;
935 if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba())
936 return d;
937 }
938
939 return 0;
940}
941
943{
944 Q_UNUSED(renderMode);
946}
947
949{
950 setShaderFileName(VertexStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/conicalgradient.vert.qsb"));
951 setShaderFileName(FragmentStage, QStringLiteral(":/qt-project.org/shapes/shaders_ng/conicalgradient.frag.qsb"));
952}
953
955 QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
956{
957 Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
959 bool changed = false;
960 QByteArray *buf = state.uniformData();
961 Q_ASSERT(buf->size() >= 80);
962
963 if (state.isMatrixDirty()) {
964 const QMatrix4x4 m = state.combinedMatrix();
965 memcpy(buf->data(), m.constData(), 64);
966 changed = true;
967 }
968
969 QQuickShapeGenericStrokeFillNode *node = m->node();
970
971 const QPointF centerPoint = node->m_fillGradient.a;
972 const float angle = -qDegreesToRadians(node->m_fillGradient.v0);
973
974 if (!oldMaterial || m_centerPoint.x() != centerPoint.x() || m_centerPoint.y() != centerPoint.y()) {
975 m_centerPoint = QVector2D(centerPoint.x(), centerPoint.y());
976 Q_ASSERT(sizeof(m_centerPoint) == 8);
977 memcpy(buf->data() + 64, &m_centerPoint, 8);
978 changed = true;
979 }
980
981 if (!oldMaterial || m_angle != angle) {
982 m_angle = angle;
983 memcpy(buf->data() + 72, &m_angle, 4);
984 changed = true;
985 }
986
987 if (state.isOpacityDirty()) {
988 const float opacity = state.opacity();
989 memcpy(buf->data() + 76, &opacity, 4);
990 changed = true;
991 }
992
993 return changed;
994}
995
997 QSGMaterial *newMaterial, QSGMaterial *)
998{
999 if (binding != 1)
1000 return;
1001
1003 QQuickShapeGenericStrokeFillNode *node = m->node();
1006 t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
1007 *texture = t;
1008}
1009
1011{
1012 static QSGMaterialType type;
1013 return &type;
1014}
1015
1017{
1018 Q_ASSERT(other && type() == other->type());
1020
1023 Q_ASSERT(a && b);
1024 if (a == b)
1025 return 0;
1026
1027 const QQuickAbstractPathRenderer::GradientDesc *ga = &a->m_fillGradient;
1028 const QQuickAbstractPathRenderer::GradientDesc *gb = &b->m_fillGradient;
1029
1030 if (int d = ga->a.x() - gb->a.x())
1031 return d;
1032 if (int d = ga->a.y() - gb->a.y())
1033 return d;
1034
1035 if (int d = ga->v0 - gb->v0)
1036 return d;
1037
1038 if (int d = ga->stops.size() - gb->stops.size())
1039 return d;
1040
1041 for (int i = 0; i < ga->stops.size(); ++i) {
1042 if (int d = ga->stops[i].first - gb->stops[i].first)
1043 return d;
1044 if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba())
1045 return d;
1046 }
1047
1048 return 0;
1049}
1050
1052{
1053 Q_UNUSED(renderMode);
1055}
1056
1058
1059#include "moc_qquickshapegenericrenderer_p.cpp"
\inmodule QtCore
Definition qbytearray.h:57
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
QPainterPath::ElementType * elementTypes() const
void process(const QVectorPath &path, const QPen &pen, const QRectF &clip, QPainter::RenderHints hints)
void setInvScale(qreal invScale)
QGraphicsWidget * window() const
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_pointer constData() const noexcept
Definition qlist.h:416
T & first()
Definition qlist.h:628
pointer data()
Definition qlist.h:414
void resize(qsizetype size)
Definition qlist.h:392
void clear()
Definition qlist.h:417
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
\inmodule QtGui
\inmodule QtGui
Definition qpen.h:25
Qt::PenStyle style() const
Returns the pen style.
Definition qpen.cpp:385
\inmodule QtCore\reentrant
Definition qpoint.h:214
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:333
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:338
QGradientStops gradientStops() const
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:64
QQuickWindow * window() const
Returns the window in which this item is rendered.
qreal width
This property holds the width of this item.
Definition qquickitem.h:76
qreal height
This property holds the height of this item.
Definition qquickitem.h:77
void update()
Schedules a call to updatePaintNode() for this item.
QSGMaterialType * type() const override
This function is called by the scene graph to query an identifier that is unique to the QSGMaterialSh...
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
void done(QQuickShapeFillRunnable *self)
void run() override
Implement this pure virtual function in your subclass.
static QSGMaterial * createLinearGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node)
static QSGMaterial * createConicalGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node)
static QSGMaterial * createVertexColor(QQuickWindow *window)
static QSGMaterial * createRadialGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node)
QQuickShapeGenericStrokeFillNode * m_fillNode
QQuickShapeGenericStrokeFillNode * m_strokeNode
void setAsyncCallback(void(*)(void *), void *) override
void setTriangulationScale(qreal scale) override
void setFillGradient(int index, QQuickShapeGradient *gradient) override
static void triangulateFill(const QPainterPath &path, const Color4ub &fillColor, VertexContainerType *fillVertices, IndexContainerType *fillIndices, QSGGeometry::Type *indexType, bool supportsElementIndexUint, qreal triangulationScale)
void setStrokeColor(int index, const QColor &color) override
void setFillRule(int index, QQuickShapePath::FillRule fillRule) override
void setFillColor(int index, const QColor &color) override
void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override
void setStrokeWidth(int index, qreal w) override
void setRootNode(QQuickShapeGenericNode *node)
void beginSync(int totalCount, bool *countChanged) override
void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override
static void triangulateStroke(const QPainterPath &path, const QPen &pen, const Color4ub &strokeColor, VertexContainerType *strokeVertices, const QSize &clipSize, qreal triangulationScale)
void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, qreal dashOffset, const QVector< qreal > &dashPattern) override
void setPath(int index, const QQuickPath *path) override
QQuickAbstractPathRenderer::GradientDesc m_fillGradient
void activateMaterial(QQuickWindow *window, Material m)
QSGTexture * get(const QQuickShapeGradientCacheKey &grad)
static QQuickShapeGradientCache * cacheForRhi(QRhi *rhi)
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
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
void done(QQuickShapeStrokeRunnable *self)
void run() override
Implement this pure virtual function in your subclass.
static QQuickWindowPrivate * get(QQuickWindow *c)
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
\inmodule QtCore\reentrant
Definition qrect.h:483
@ ElementIndexUint
Definition qrhi.h:1805
void setAutoDelete(bool autoDelete)
Enables auto-deletion if autoDelete is true; otherwise auto-deletion is disabled.
Definition qrunnable.h:38
void setGeometry(QSGGeometry *geometry)
Sets the geometry of this node to geometry.
Definition qsgnode.cpp:762
QSGMaterial * material() const
Returns the material of the QSGGeometryNode.
Definition qsgnode.h:197
void setMaterial(QSGMaterial *material)
Sets the material of this geometry node to material.
Definition qsgnode.cpp:925
The QSGGeometry class provides low-level storage for graphics primitives in the \l{Qt Quick Scene Gra...
Definition qsggeometry.h:15
static const AttributeSet & defaultAttributes_ColoredPoint2D()
Convenience function which returns attributes to be used for per vertex colored 2D drawing.
void allocate(int vertexCount, int indexCount=0)
Resizes the vertex and index data of this geometry object to fit vertexCount vertices and indexCount ...
Type
Specifies the component type in the vertex data.
Definition qsggeometry.h:43
Encapsulates the current rendering state during a call to QSGMaterialShader::updateUniformData() and ...
The QSGMaterialShader class represents a graphics API independent shader program.
void setShaderFileName(Stage stage, const QString &filename)
Sets the filename for the shader for the specified stage.
The QSGMaterial class encapsulates rendering state for a shader program.
Definition qsgmaterial.h:15
virtual QSGMaterialType * type() const =0
This function is called by the scene graph to query an identifier that is unique to the QSGMaterialSh...
void removeChildNode(QSGNode *node)
Removes node from this node's list of children.
Definition qsgnode.cpp:498
@ DirtyMaterial
Definition qsgnode.h:75
@ DirtyGeometry
Definition qsgnode.h:74
@ OwnsGeometry
Definition qsgnode.h:57
void appendChildNode(QSGNode *node)
Appends node to this node's list of children.
Definition qsgnode.cpp:396
void setFlag(Flag, bool=true)
Sets the flag f on this node if enabled is true; otherwise clears the flag.
Definition qsgnode.cpp:584
RenderMode
\value RenderMode2D Normal 2D rendering \value RenderMode2DNoDepthBuffer Normal 2D rendering with dep...
static bool isApiRhiBased(GraphicsApi api)
virtual GraphicsApi graphicsApi() const =0
Returns the graphics API that is in use by the Qt Quick scenegraph.
GraphicsApi
\value Unknown An unknown graphics API is in use \value Software The Qt Quick 2D Renderer is in use \...
\inmodule QtQuick
Definition qsgtexture.h:20
virtual void commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
Call this function to enqueue image upload operations to resourceUpdates, in case there are any pendi...
The QSGVertexColorMaterial class provides a convenient way of rendering per-vertex colored geometry i...
T * data() const noexcept
Returns the value of the pointer referenced by this object.
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
\inmodule QtCore
Definition qsize.h:25
\inmodule QtCore
Definition qthreadpool.h:20
void start(QRunnable *runnable, int priority=0)
Reserves a thread and uses it to run runnable, unless this thread will make the current thread count ...
void setMaxThreadCount(int maxThreadCount)
static int idealThreadCount() noexcept
Definition qthread.cpp:971
static QTransform fromScale(qreal dx, qreal dy)
Creates a matrix which corresponds to a scaling of sx horizontally and sy vertically.
const float * vertices() const
void setInvScale(qreal invScale)
void process(const QVectorPath &path, const QPen &pen, const QRectF &clip, QPainter::RenderHints hints)
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:502
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:501
const void * data() const
else opt state
[0]
Combined button and popup list for selecting options.
@ SolidLine
PenJoinStyle
PenCapStyle
void qAddPostRoutine(QtCleanUpFunction p)
#define qApp
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:281
#define qWarning
Definition qlogging.h:162
constexpr float qDegreesToRadians(float degrees)
Definition qmath.h:260
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint focalPoint
GLfloat angle
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum GLuint texture
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLboolean GLboolean g
GLfloat n
GLint y
GLbyte nx
const GLubyte * c
GLfixed ny
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLenum GLenum GLenum GLenum GLenum scale
const QVectorPath & qtVectorPathForPath(const QPainterPath &path)
static QQuickShapeGenericRenderer::Color4ub colorToColor4ub(const QColor &c)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
void qsgnode_set_description(QSGNode *node, const QString &description)
Definition qsgnode.cpp:639
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define emit
#define Q_UNUSED(x)
Q_GUI_EXPORT QTriangleSet qTriangulate(const qreal *polygon, int count, uint hint, const QTransform &matrix, bool allowUintIndices)
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
unsigned short quint16
Definition qtypes.h:43
double qreal
Definition qtypes.h:92
QFuture< QSet< QChar > > set
[10]
QObject::connect nullptr
QSharedPointer< T > other(t)
[5]
QGraphicsItem * item
aWidget window() -> setWindowTitle("New Window Title")
[2]
void set(float nx, float ny, QQuickShapeGenericRenderer::Color4ub ncolor)
QQuickShapeGenericRenderer::Color4ub color
QQuickShapeGradient::SpreadMode spread
The QSGMaterialType class is used as a unique type token in combination with QSGMaterial.
QList< qreal > vertices
QVertexIndexVector indices