Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquickshapecurverenderer.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
7#if QT_CONFIG(thread)
8#include <QtCore/qthreadpool.h>
9#endif
10
11#include <QtGui/qvector2d.h>
12#include <QtGui/qvector4d.h>
13#include <QtGui/private/qtriangulator_p.h>
14#include <QtGui/private/qtriangulatingstroker_p.h>
15#include <QtGui/private/qrhi_p.h>
16
17#include <QtQuick/private/qsgcurvefillnode_p.h>
18#include <QtQuick/private/qsgcurvestrokenode_p.h>
19#include <QtQuick/private/qquadpath_p.h>
20#include <QtQuick/private/qsgcurveprocessor_p.h>
21#include <QtQuick/qsgmaterial.h>
22
24
25Q_LOGGING_CATEGORY(lcShapeCurveRenderer, "qt.shape.curverenderer");
26
27namespace {
28
29class QQuickShapeWireFrameMaterialShader : public QSGMaterialShader
30{
31public:
32 QQuickShapeWireFrameMaterialShader(int viewCount)
33 {
34 setShaderFileName(VertexStage,
35 QStringLiteral(":/qt-project.org/shapes/shaders_ng/wireframe.vert.qsb"), viewCount);
36 setShaderFileName(FragmentStage,
37 QStringLiteral(":/qt-project.org/shapes/shaders_ng/wireframe.frag.qsb"), viewCount);
38 }
39
40 bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *) override
41 {
42 bool changed = false;
43 QByteArray *buf = state.uniformData();
44 Q_ASSERT(buf->size() >= 64);
45 const int matrixCount = qMin(state.projectionMatrixCount(), newMaterial->viewCount());
46
47 for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
48 if (state.isMatrixDirty()) {
49 const QMatrix4x4 m = state.combinedMatrix(viewIndex);
50 memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
51 changed = true;
52 }
53 }
54
55 return changed;
56 }
57};
58
59class QQuickShapeWireFrameMaterial : public QSGMaterial
60{
61public:
62 QQuickShapeWireFrameMaterial()
63 {
64 setFlag(Blending, true);
65 }
66
67 int compare(const QSGMaterial *other) const override
68 {
69 return (type() - other->type());
70 }
71
72protected:
73 QSGMaterialType *type() const override
74 {
75 static QSGMaterialType t;
76 return &t;
77 }
78 QSGMaterialShader *createShader(QSGRendererInterface::RenderMode) const override
79 {
80 return new QQuickShapeWireFrameMaterialShader(viewCount());
81 }
82
83};
84
85class QQuickShapeWireFrameNode : public QSGCurveAbstractNode
86{
87public:
88 struct WireFrameVertex
89 {
90 float x, y, u, v, w;
91 };
92
93 QQuickShapeWireFrameNode()
94 {
95 setFlag(OwnsGeometry, true);
96 setGeometry(new QSGGeometry(attributes(), 0, 0));
97 activateMaterial();
98 }
99
100 void setColor(QColor col) override
101 {
102 Q_UNUSED(col);
103 }
104
105 void activateMaterial()
106 {
107 m_material.reset(new QQuickShapeWireFrameMaterial);
108 setMaterial(m_material.data());
109 }
110
111 static const QSGGeometry::AttributeSet &attributes()
112 {
113 static QSGGeometry::Attribute data[] = {
116 };
117 static QSGGeometry::AttributeSet attrs = { 2, sizeof(WireFrameVertex), data };
118 return attrs;
119 }
120
121 void cookGeometry() override
122 {
123 // Intentionally empty
124 }
125
126protected:
127 QScopedPointer<QQuickShapeWireFrameMaterial> m_material;
128};
129}
130
132{
133 for (const PathData &pd : std::as_const(m_paths)) {
134 if (pd.currentRunner)
135 pd.currentRunner->orphaned = true;
136 }
137}
138
139void QQuickShapeCurveRenderer::beginSync(int totalCount, bool *countChanged)
140{
141 if (countChanged != nullptr && totalCount != m_paths.size())
142 *countChanged = true;
143 m_paths.resize(totalCount);
144}
145
147{
148 constexpr QQuickShapePath::PathHints noHints;
149 const auto *shapePath = qobject_cast<const QQuickShapePath *>(path);
150 setPath(index, path->path(), shapePath ? shapePath->pathHints() : noHints);
151}
152
153void QQuickShapeCurveRenderer::setPath(int index, const QPainterPath &path, QQuickShapePath::PathHints pathHints)
154{
155 auto &pathData = m_paths[index];
156 pathData.originalPath = path;
157 pathData.pathHints = pathHints;
158 pathData.m_dirty |= PathDirty;
159}
160
162{
163 auto &pathData = m_paths[index];
164 const bool wasVisible = pathData.isStrokeVisible();
165 pathData.pen.setColor(color);
166 if (pathData.isStrokeVisible() != wasVisible)
167 pathData.m_dirty |= StrokeDirty;
168 else
169 pathData.m_dirty |= UniformsDirty;
170}
171
173{
174 auto &pathData = m_paths[index];
175 if (w > 0) {
176 pathData.validPenWidth = true;
177 pathData.pen.setWidthF(w);
178 } else {
179 pathData.validPenWidth = false;
180 }
181 pathData.m_dirty |= StrokeDirty;
182}
183
185{
186 auto &pathData = m_paths[index];
187 const bool wasVisible = pathData.isFillVisible();
188 pathData.fillColor = color;
189 if (pathData.isFillVisible() != wasVisible)
190 pathData.m_dirty |= FillDirty;
191 else
192 pathData.m_dirty |= UniformsDirty;
193}
194
196{
197 auto &pathData = m_paths[index];
198 pathData.fillRule = Qt::FillRule(fillRule);
199 pathData.m_dirty |= PathDirty;
200}
201
204 int miterLimit)
205{
206 auto &pathData = m_paths[index];
207 pathData.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
208 pathData.pen.setMiterLimit(miterLimit);
209 pathData.m_dirty |= StrokeDirty;
210}
211
213{
214 auto &pathData = m_paths[index];
215 pathData.pen.setCapStyle(Qt::PenCapStyle(capStyle));
216 pathData.m_dirty |= StrokeDirty;
217}
218
221 qreal dashOffset,
222 const QVector<qreal> &dashPattern)
223{
224 auto &pathData = m_paths[index];
225 pathData.pen.setStyle(Qt::PenStyle(strokeStyle));
226 if (strokeStyle == QQuickShapePath::DashLine) {
227 pathData.pen.setDashPattern(dashPattern);
228 pathData.pen.setDashOffset(dashOffset);
229 }
230 pathData.m_dirty |= StrokeDirty;
231}
232
234{
235 PathData &pd(m_paths[index]);
236 pd.gradientType = QGradient::NoGradient;
237 if (QQuickShapeLinearGradient *g = qobject_cast<QQuickShapeLinearGradient *>(gradient)) {
238 pd.gradientType = QGradient::LinearGradient;
239 pd.gradient.stops = gradient->gradientStops();
240 pd.gradient.spread = QGradient::Spread(gradient->spread());
241 pd.gradient.a = QPointF(g->x1(), g->y1());
242 pd.gradient.b = QPointF(g->x2(), g->y2());
243 } else if (QQuickShapeRadialGradient *g = qobject_cast<QQuickShapeRadialGradient *>(gradient)) {
244 pd.gradientType = QGradient::RadialGradient;
245 pd.gradient.a = QPointF(g->centerX(), g->centerY());
246 pd.gradient.b = QPointF(g->focalX(), g->focalY());
247 pd.gradient.v0 = g->centerRadius();
248 pd.gradient.v1 = g->focalRadius();
249 } else if (QQuickShapeConicalGradient *g = qobject_cast<QQuickShapeConicalGradient *>(gradient)) {
250 pd.gradientType = QGradient::ConicalGradient;
251 pd.gradient.a = QPointF(g->centerX(), g->centerY());
252 pd.gradient.v0 = g->angle();
253 } else
254 if (gradient != nullptr) {
255 static bool warned = false;
256 if (!warned) {
257 warned = true;
258 qCWarning(lcShapeCurveRenderer) << "Unsupported gradient fill";
259 }
260 }
261
262 if (pd.gradientType != QGradient::NoGradient) {
263 pd.gradient.stops = gradient->gradientStops();
264 pd.gradient.spread = QGradient::Spread(gradient->spread());
265 }
266
267 pd.m_dirty |= FillDirty;
268}
269
271{
272 auto &pathData = m_paths[index];
273 pathData.fillTransform = transform;
274 pathData.m_dirty |= FillDirty;
275}
276
277void QQuickShapeCurveRenderer::setAsyncCallback(void (*callback)(void *), void *data)
278{
279 m_asyncCallback = callback;
280 m_asyncCallbackData = data;
281}
282
284{
285 bool didKickOffAsync = false;
286
287 for (PathData &pathData : m_paths) {
288 if (!pathData.m_dirty)
289 continue;
290
291 if (pathData.m_dirty == UniformsDirty) {
292 // Requires no curve node computation, gets handled directly in updateNode()
293 continue;
294 }
295
296 if (pathData.currentRunner) {
297 // Already performing async computing. New dirty flags will be handled in the next sync
298 // after the current computation is done and the item is updated
299 continue;
300 }
301
302 createRunner(&pathData);
303
304#if QT_CONFIG(thread)
305 if (async) {
306 pathData.currentRunner->isAsync = true;
307 QThreadPool::globalInstance()->start(pathData.currentRunner);
308 didKickOffAsync = true;
309 } else
310#endif
311 {
312 pathData.currentRunner->run();
313 }
314 }
315
316 if (async && !didKickOffAsync && m_asyncCallback)
317 m_asyncCallback(m_asyncCallbackData);
318}
319
320void QQuickShapeCurveRenderer::createRunner(PathData *pathData)
321{
322 Q_ASSERT(!pathData->currentRunner);
324 runner->setAutoDelete(false);
325 runner->pathData = *pathData;
326 runner->pathData.fillNodes.clear();
327 runner->pathData.strokeNodes.clear();
328 runner->pathData.currentRunner = nullptr;
329
330 pathData->currentRunner = runner;
331 pathData->m_dirty = 0;
333 [this](QQuickShapeCurveRunnable *r) {
334 r->isDone = true;
335 if (r->orphaned) {
336 r->deleteLater(); // Renderer was destroyed
337 } else if (r->isAsync) {
338 maybeUpdateAsyncItem();
339 }
340 });
341}
342
343void QQuickShapeCurveRenderer::maybeUpdateAsyncItem()
344{
345 for (const PathData &pd : std::as_const(m_paths)) {
346 if (pd.currentRunner && !pd.currentRunner->isDone)
347 return;
348 }
349 if (m_item)
350 m_item->update();
351 if (m_asyncCallback)
352 m_asyncCallback(m_asyncCallbackData);
353}
354
356{
357 QQuickShapeCurveRenderer::processPath(&pathData);
358 emit done(this);
359}
360
362{
363 if (!m_rootNode)
364 return;
365
366 auto updateUniforms = [](const PathData &pathData) {
367 for (auto &pathNode : std::as_const(pathData.fillNodes))
368 pathNode->setColor(pathData.fillColor);
369 for (auto &strokeNode : std::as_const(pathData.strokeNodes))
370 strokeNode->setColor(pathData.pen.color());
371 };
372
373 NodeList toBeDeleted;
374
375 for (int i = 0; i < m_paths.size(); i++) {
376 PathData &pathData = m_paths[i];
377 if (pathData.currentRunner) {
378 if (!pathData.currentRunner->isDone)
379 continue;
380 // Find insertion point for new nodes. Default is the first stroke node of this path
381 QSGNode *nextNode = pathData.strokeNodes.value(0);
382 // If that is 0, use the first node (stroke or fill) of later paths, if any
383 for (int j = i + 1; !nextNode && j < m_paths.size(); j++) {
384 const PathData &pd = m_paths[j];
385 nextNode = pd.fillNodes.isEmpty() ? pd.strokeNodes.value(0) : pd.fillNodes.value(0);
386 }
387
388 const PathData &newData = pathData.currentRunner->pathData;
389 if (newData.m_dirty & PathDirty)
390 pathData.path = newData.path;
391 if (newData.m_dirty & FillDirty) {
392 pathData.fillPath = newData.fillPath;
393 for (auto *node : std::as_const(newData.fillNodes)) {
394 if (nextNode)
395 m_rootNode->insertChildNodeBefore(node, nextNode);
396 else
397 m_rootNode->appendChildNode(node);
398 }
399 toBeDeleted += pathData.fillNodes;
400 pathData.fillNodes = newData.fillNodes;
401 }
402 if (newData.m_dirty & StrokeDirty) {
403 for (auto *node : std::as_const(newData.strokeNodes)) {
404 if (nextNode)
405 m_rootNode->insertChildNodeBefore(node, nextNode);
406 else
407 m_rootNode->appendChildNode(node);
408 }
409 toBeDeleted += pathData.strokeNodes;
410 pathData.strokeNodes = newData.strokeNodes;
411 }
412
413 if (newData.m_dirty & UniformsDirty)
414 updateUniforms(pathData);
415
416 // if (pathData.m_dirty && pathData.m_dirty != UniformsDirty && currentRunner.isAsync)
417 // qDebug("### should enqueue a new sync?");
418
419 pathData.currentRunner->deleteLater();
420 pathData.currentRunner = nullptr;
421 }
422
423 if (pathData.m_dirty == UniformsDirty) {
424 // Simple case so no runner was created in endSync(); handle it directly here
425 updateUniforms(pathData);
426 pathData.m_dirty = 0;
427 }
428 }
429 qDeleteAll(toBeDeleted); // also removes them from m_rootNode's child list
430}
431
432void QQuickShapeCurveRenderer::processPath(PathData *pathData)
433{
434 static const bool doOverlapSolving = !qEnvironmentVariableIntValue("QT_QUICKSHAPES_DISABLE_OVERLAP_SOLVER");
435 static const bool doIntersetionSolving = !qEnvironmentVariableIntValue("QT_QUICKSHAPES_DISABLE_INTERSECTION_SOLVER");
436 static const bool useTriangulatingStroker = qEnvironmentVariableIntValue("QT_QUICKSHAPES_TRIANGULATING_STROKER");
437 static const bool simplifyPath = qEnvironmentVariableIntValue("QT_QUICKSHAPES_SIMPLIFY_PATHS");
438
439 int &dirtyFlags = pathData->m_dirty;
440
441 if (dirtyFlags & PathDirty) {
442 if (simplifyPath)
444 else
445 pathData->path = QQuadPath::fromPainterPath(pathData->originalPath, QQuadPath::PathHints(int(pathData->pathHints)));
446 pathData->path.setFillRule(pathData->fillRule);
447 pathData->fillPath = {};
448 dirtyFlags |= (FillDirty | StrokeDirty);
449 }
450
451 if (dirtyFlags & FillDirty) {
452 if (pathData->isFillVisible()) {
453 if (pathData->fillPath.isEmpty()) {
454 pathData->fillPath = pathData->path.subPathsClosed();
455 if (doIntersetionSolving)
456 QSGCurveProcessor::solveIntersections(pathData->fillPath);
457 pathData->fillPath.addCurvatureData();
458 if (doOverlapSolving)
459 QSGCurveProcessor::solveOverlaps(pathData->fillPath);
460 }
461 pathData->fillNodes = addFillNodes(*pathData);
462 dirtyFlags |= StrokeDirty;
463 }
464 }
465
466 if (dirtyFlags & StrokeDirty) {
467 if (pathData->isStrokeVisible()) {
468 const QPen &pen = pathData->pen;
469 if (pen.style() == Qt::SolidLine)
470 pathData->strokePath = pathData->path;
471 else
472 pathData->strokePath = pathData->path.dashed(pen.widthF(), pen.dashPattern(), pen.dashOffset());
473
474 if (useTriangulatingStroker)
475 pathData->strokeNodes = addTriangulatingStrokerNodes(*pathData);
476 else
477 pathData->strokeNodes = addCurveStrokeNodes(*pathData);
478 }
479 }
480}
481
482QQuickShapeCurveRenderer::NodeList QQuickShapeCurveRenderer::addFillNodes(const PathData &pathData)
483{
484 auto *node = new QSGCurveFillNode;
485 node->setGradientType(pathData.gradientType);
486 const qsizetype approxDataCount = 20 * pathData.fillPath.elementCount();
487 node->reserve(approxDataCount);
488
490 const QColor &color = pathData.fillColor;
491 QPainterPath internalHull;
492 internalHull.setFillRule(pathData.fillPath.fillRule());
493
494 bool visualizeDebug = debugVisualization() & DebugCurves;
495 const float dbg = visualizeDebug ? 0.5f : 0.0f;
496 node->setDebug(dbg);
497
498 QVector<QQuickShapeWireFrameNode::WireFrameVertex> wfVertices;
499 wfVertices.reserve(approxDataCount);
500
501 QSGCurveProcessor::processFill(pathData.fillPath,
502 pathData.fillRule,
503 [&wfVertices, &node](const std::array<QVector2D, 3> &v,
504 const std::array<QVector2D, 3> &n,
506 {
507 node->appendTriangle(v, n, uvForPoint);
508
509 wfVertices.append({v.at(0).x(), v.at(0).y(), 1.0f, 0.0f, 0.0f}); // 0
510 wfVertices.append({v.at(1).x(), v.at(1).y(), 0.0f, 1.0f, 0.0f}); // 1
511 wfVertices.append({v.at(2).x(), v.at(2).y(), 0.0f, 0.0f, 1.0f}); // 2
512 });
513
514 QVector<quint32> indices = node->uncookedIndexes();
515 if (indices.size() > 0) {
516 node->setColor(color);
517 node->setFillTransform(pathData.fillTransform);
518 node->setFillGradient(pathData.gradient);
519
520 node->cookGeometry();
521 ret.append(node);
522 }
523
524 const bool wireFrame = debugVisualization() & DebugWireframe;
525 if (wireFrame) {
526 QQuickShapeWireFrameNode *wfNode = new QQuickShapeWireFrameNode;
527 QSGGeometry *wfg = new QSGGeometry(QQuickShapeWireFrameNode::attributes(),
528 wfVertices.size(),
529 indices.size(),
531 wfNode->setGeometry(wfg);
532
533 wfg->setDrawingMode(QSGGeometry::DrawTriangles);
534 memcpy(wfg->indexData(),
535 indices.data(),
536 indices.size() * wfg->sizeOfIndex());
537 memcpy(wfg->vertexData(),
538 wfVertices.data(),
539 wfg->vertexCount() * wfg->sizeOfVertex());
540
541 ret.append(wfNode);
542 }
543
544 return ret;
545}
546
547QQuickShapeCurveRenderer::NodeList QQuickShapeCurveRenderer::addTriangulatingStrokerNodes(const PathData &pathData)
548{
550 const QColor &color = pathData.pen.color();
551
552 QVector<QQuickShapeWireFrameNode::WireFrameVertex> wfVertices;
553
554 QTriangulatingStroker stroker;
555 const auto painterPath = pathData.strokePath.toPainterPath();
556 const QVectorPath &vp = qtVectorPathForPath(painterPath);
557 QPen pen = pathData.pen;
558 stroker.process(vp, pen, {}, {});
559
560 auto *node = new QSGCurveFillNode;
561 node->setGradientType(pathData.gradientType);
562
563 auto uvForPoint = [](QVector2D v1, QVector2D v2, QVector2D p)
564 {
565 double divisor = v1.x() * v2.y() - v2.x() * v1.y();
566
567 float u = (p.x() * v2.y() - p.y() * v2.x()) / divisor;
568 float v = (p.y() * v1.x() - p.x() * v1.y()) / divisor;
569
570 return QVector2D(u, v);
571 };
572
573 // Find uv coordinates for the point p, for a quadratic curve from p0 to p2 with control point p1
574 // also works for a line from p0 to p2, where p1 is on the inside of the path relative to the line
575 auto curveUv = [uvForPoint](QVector2D p0, QVector2D p1, QVector2D p2, QVector2D p)
576 {
577 QVector2D v1 = 2 * (p1 - p0);
578 QVector2D v2 = p2 - v1 - p0;
579 return uvForPoint(v1, v2, p - p0);
580 };
581
582 auto findPointOtherSide = [](const QVector2D &startPoint, const QVector2D &endPoint, const QVector2D &referencePoint){
583
584 QVector2D baseLine = endPoint - startPoint;
585 QVector2D insideVector = referencePoint - startPoint;
586 QVector2D normal = QVector2D(-baseLine.y(), baseLine.x()); // TODO: limit size of triangle
587
588 bool swap = QVector2D::dotProduct(insideVector, normal) < 0;
589
590 return swap ? startPoint + normal : startPoint - normal;
591 };
592
593 static bool disableExtraTriangles = qEnvironmentVariableIntValue("QT_QUICKSHAPES_WIP_DISABLE_EXTRA_STROKE_TRIANGLES");
594
595 auto addStrokeTriangle = [&](const QVector2D &p1, const QVector2D &p2, const QVector2D &p3, bool){
596 if (p1 == p2 || p2 == p3) {
597 return;
598 }
599
600 auto uvForPoint = [&p1, &p2, &p3, curveUv](QVector2D p) {
601 auto uv = curveUv(p1, p2, p3, p);
602 return QVector3D(uv.x(), uv.y(), 0.0f); // Line
603 };
604
605 node->appendTriangle(p1, p2, p3, uvForPoint);
606
607
608 wfVertices.append({p1.x(), p1.y(), 1.0f, 0.0f, 0.0f}); // 0
609 wfVertices.append({p2.x(), p2.y(), 0.0f, 0.1f, 0.0f}); // 1
610 wfVertices.append({p3.x(), p3.y(), 0.0f, 0.0f, 1.0f}); // 2
611
612 if (!disableExtraTriangles) {
613 // Add a triangle on the outer side of the line to get some more AA
614 // The new point replaces p2 (currentVertex+1)
615 QVector2D op = findPointOtherSide(p1, p3, p2);
616 node->appendTriangle(p1, op, p3, uvForPoint);
617
618 wfVertices.append({p1.x(), p1.y(), 1.0f, 0.0f, 0.0f});
619 wfVertices.append({op.x(), op.y(), 0.0f, 1.0f, 0.0f}); // replacing p2
620 wfVertices.append({p3.x(), p3.y(), 0.0f, 0.0f, 1.0f});
621 }
622 };
623
624 const int vertCount = stroker.vertexCount() / 2;
625 const float *verts = stroker.vertices();
626 for (int i = 0; i < vertCount - 2; ++i) {
627 QVector2D p[3];
628 for (int j = 0; j < 3; ++j) {
629 p[j] = QVector2D(verts[(i+j)*2], verts[(i+j)*2 + 1]);
630 }
631 bool isOdd = i % 2;
632 addStrokeTriangle(p[0], p[1], p[2], isOdd);
633 }
634
635 QVector<quint32> indices = node->uncookedIndexes();
636 if (indices.size() > 0) {
637 node->setColor(color);
638 node->setFillGradient(pathData.gradient);
639
640 node->cookGeometry();
641 ret.append(node);
642 }
643 const bool wireFrame = debugVisualization() & DebugWireframe;
644 if (wireFrame) {
645 QQuickShapeWireFrameNode *wfNode = new QQuickShapeWireFrameNode;
646 QSGGeometry *wfg = new QSGGeometry(QQuickShapeWireFrameNode::attributes(),
647 wfVertices.size(),
648 indices.size(),
650 wfNode->setGeometry(wfg);
651
652 wfg->setDrawingMode(QSGGeometry::DrawTriangles);
653 memcpy(wfg->indexData(),
654 indices.data(),
655 indices.size() * wfg->sizeOfIndex());
656 memcpy(wfg->vertexData(),
657 wfVertices.data(),
658 wfg->vertexCount() * wfg->sizeOfVertex());
659
660 ret.append(wfNode);
661 }
662
663 return ret;
664}
665
667{
668 m_rootNode = node;
669}
670
671int QQuickShapeCurveRenderer::debugVisualizationFlags = QQuickShapeCurveRenderer::NoDebug;
672
674{
675 static const int envFlags = qEnvironmentVariableIntValue("QT_QUICKSHAPES_DEBUG");
676 return debugVisualizationFlags | envFlags;
677}
678
680{
681 if (debugVisualizationFlags == options)
682 return;
683 debugVisualizationFlags = options;
684}
685
686QQuickShapeCurveRenderer::NodeList QQuickShapeCurveRenderer::addCurveStrokeNodes(const PathData &pathData)
687{
689 const QColor &color = pathData.pen.color();
690
691 const bool debug = debugVisualization() & DebugCurves;
692 auto *node = new QSGCurveStrokeNode;
693 node->setDebug(0.2f * debug);
694 QVector<QQuickShapeWireFrameNode::WireFrameVertex> wfVertices;
695
696 const float miterLimit = pathData.pen.miterLimit();
697 const float penWidth = pathData.pen.widthF();
698
699 static const int subdivisions = qEnvironmentVariable("QT_QUICKSHAPES_STROKE_SUBDIVISIONS", QStringLiteral("3")).toInt();
700
701 QSGCurveProcessor::processStroke(pathData.strokePath,
702 miterLimit,
703 penWidth,
704 pathData.pen.joinStyle(),
705 pathData.pen.capStyle(),
706 [&wfVertices, &node](const std::array<QVector2D, 3> &s,
707 const std::array<QVector2D, 3> &p,
708 const std::array<QVector2D, 3> &n,
709 bool isLine)
710 {
711 const QVector2D &p0 = s.at(0);
712 const QVector2D &p1 = s.at(1);
713 const QVector2D &p2 = s.at(2);
714 if (isLine)
715 node->appendTriangle(s, std::array<QVector2D, 2>{p.at(0), p.at(2)}, n);
716 else
717 node->appendTriangle(s, p, n);
718
719 wfVertices.append({p0.x(), p0.y(), 1.0f, 0.0f, 0.0f});
720 wfVertices.append({p1.x(), p1.y(), 0.0f, 1.0f, 0.0f});
721 wfVertices.append({p2.x(), p2.y(), 0.0f, 0.0f, 1.0f});
722 },
723 subdivisions);
724
725 auto indexCopy = node->uncookedIndexes(); // uncookedIndexes get delete on cooking
726
727 node->setColor(color);
728 node->setStrokeWidth(pathData.pen.widthF());
729 node->cookGeometry();
730 ret.append(node);
731
732 const bool wireFrame = debugVisualization() & DebugWireframe;
733 if (wireFrame) {
734 QQuickShapeWireFrameNode *wfNode = new QQuickShapeWireFrameNode;
735
736 QSGGeometry *wfg = new QSGGeometry(QQuickShapeWireFrameNode::attributes(),
737 wfVertices.size(),
738 indexCopy.size(),
740 wfNode->setGeometry(wfg);
741
742 wfg->setDrawingMode(QSGGeometry::DrawTriangles);
743 memcpy(wfg->indexData(),
744 indexCopy.data(),
745 indexCopy.size() * wfg->sizeOfIndex());
746 memcpy(wfg->vertexData(),
747 wfVertices.data(),
748 wfg->vertexCount() * wfg->sizeOfVertex());
749
750 ret.append(wfNode);
751 }
752
753 return ret;
754}
755
\inmodule QtCore
Definition qbytearray.h:57
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
Spread
Specifies how the area outside the gradient area should be filled.
Definition qbrush.h:146
@ LinearGradient
Definition qbrush.h:139
@ NoGradient
Definition qbrush.h:142
@ ConicalGradient
Definition qbrush.h:141
@ RadialGradient
Definition qbrush.h:140
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:2960
void deleteLater()
\threadsafe
Definition qobject.cpp:2435
\inmodule QtGui
void setFillRule(Qt::FillRule fillRule)
Sets the fill rule of the painter path to the given fillRule.
\inmodule QtGui
Definition qpen.h:28
qreal widthF() const
Returns the pen width with floating point precision.
Definition qpen.cpp:572
QList< qreal > dashPattern() const
Returns the dash pattern of this pen.
Definition qpen.cpp:400
qreal dashOffset() const
Returns the dash offset for the pen.
Definition qpen.cpp:484
Qt::PenStyle style() const
Returns the pen style.
Definition qpen.cpp:366
\inmodule QtCore\reentrant
Definition qpoint.h:217
@ PathNonOverlappingControlPointTriangles
Definition qquadpath_p.h:38
@ PathNonIntersecting
Definition qquadpath_p.h:37
static QQuadPath fromPainterPath(const QPainterPath &path, PathHints hints={})
QGradientStops gradientStops() const
void update()
Schedules a call to updatePaintNode() for this item.
QVector< QSGCurveAbstractNode * > NodeList
void beginSync(int totalCount, bool *countChanged) override
static void setDebugVisualization(int options)
void setFillGradient(int index, QQuickShapeGradient *gradient) override
void endSync(bool async) override
void setStrokeWidth(int index, qreal w) override
void setFillTransform(int index, const QSGTransform &transform) override
void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override
void setStrokeColor(int index, const QColor &color) override
void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override
void setFillColor(int index, const QColor &color) override
void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, qreal dashOffset, const QVector< qreal > &dashPattern) override
void setPath(int index, const QQuickPath *path) override
void setAsyncCallback(void(*)(void *), void *) override
void setFillRule(int index, QQuickShapePath::FillRule fillRule) override
QQuickShapeCurveRenderer::PathData pathData
void done(QQuickShapeCurveRunnable *self)
void run() override
Implement this pure virtual function in your subclass.
void setGradientType(QGradient::Type type)
static void processStroke(const QQuadPath &strokePath, float miterLimit, float penWidth, Qt::PenJoinStyle joinStyle, Qt::PenCapStyle capStyle, addStrokeTriangleCallback addTriangle, int subdivisions=3)
std::function< QVector3D(QVector2D)> uvForPointCallback
static bool solveOverlaps(QQuadPath &path)
static bool solveIntersections(QQuadPath &path, bool removeNestedPaths=true)
static void processFill(const QQuadPath &path, Qt::FillRule fillRule, addTriangleCallback addTriangle)
void setDebug(float newDebug)
The QSGGeometry class provides low-level storage for graphics primitives in the \l{Qt Quick Scene Gra...
Definition qsggeometry.h:15
The QSGMaterialShader class represents a graphics API independent shader program.
The QSGMaterial class encapsulates rendering state for a shader program.
Definition qsgmaterial.h:15
\group qtquick-scenegraph-nodes \title Qt Quick Scene Graph Node classes
Definition qsgnode.h:37
void appendChildNode(QSGNode *node)
Appends node to this node's list of children.
Definition qsgnode.cpp:398
void insertChildNodeBefore(QSGNode *node, QSGNode *before)
Inserts node to this node's list of children before the node specified with before.
Definition qsgnode.cpp:431
RenderMode
\value RenderMode2D Normal 2D rendering \value RenderMode2DNoDepthBuffer Normal 2D rendering with dep...
int toInt(bool *ok=nullptr, int base=10) const
Returns the string converted to an int using base base, which is 10 by default and must be between 2 ...
Definition qstring.h:731
static QThreadPool * globalInstance()
Returns the global QThreadPool instance.
const float * vertices() const
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
static constexpr float dotProduct(QVector2D v1, QVector2D v2) noexcept
Returns the dot product of v1 and v2.
Definition qvectornd.h:604
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
QPixmap p2
QPixmap p1
[0]
qDeleteAll(list.begin(), list.end())
else opt state
[0]
Combined button and popup list for selecting options.
void updateUniformData(QByteArray *dst, const QVideoFrameFormat &format, const QVideoFrame &frame, const QMatrix4x4 &transform, float opacity, float maxNits)
@ SolidLine
PenJoinStyle
PenCapStyle
#define qApp
static struct AttrInfo attrs[]
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLint GLfloat GLfloat GLfloat v2
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLuint divisor
GLuint index
[2]
GLboolean r
[2]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint color
[2]
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLint GLfloat GLfloat v1
GLboolean GLboolean g
GLfloat n
GLsizei GLenum const void * indices
GLint y
GLuint GLenum GLenum transform
GLdouble s
[6]
Definition qopenglext.h:235
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
const QVectorPath & qtVectorPathForPath(const QPainterPath &path)
static bool isLine(const QBezier &bezier)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QStringLiteral(str)
#define v1
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define emit
#define Q_UNUSED(x)
static int compare(quint64 a, quint64 b)
ptrdiff_t qsizetype
Definition qtypes.h:165
double qreal
Definition qtypes.h:187
if(qFloatDistance(a, b)<(1<< 7))
[0]
QSharedPointer< T > other(t)
[5]
this swap(other)
myFilter setColor(QColor(128, 0, 0))
The QSGGeometry::AttributeSet describes how the vertices in a QSGGeometry are built up.
Definition qsggeometry.h:73
The QSGGeometry::Attribute describes a single vertex attribute in a QSGGeometry.
Definition qsggeometry.h:58
static Attribute createWithAttributeType(int pos, int tupleSize, int primitiveType, AttributeType attributeType)
Creates a new QSGGeometry::Attribute for attribute register pos with tupleSize.
The QSGMaterialType class is used as a unique type token in combination with QSGMaterial.