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
qsgcurvestrokenode.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7
8QT_BEGIN_NAMESPACE
9
10/*!
11 \class QSGCurveStrokeNode
12 \inmodule QtQuick
13 \internal
14*/
15QSGCurveStrokeNode::QSGCurveStrokeNode()
16{
17 setFlag(OwnsGeometry, true);
18 qsgnode_set_description(this, QLatin1StringView("curve stroke"));
19 setGeometry(new QSGGeometry(attributes(), 0, 0));
20 // defer updateMaterial() until later
21}
22
23void QSGCurveStrokeNode::QSGCurveStrokeNode::updateMaterial()
24{
25 const bool expandingInVertexShader = m_cosmetic || expandingStrokeEnabled();
26 m_material.reset(new QSGCurveStrokeMaterial(this, expandingInVertexShader));
27 setMaterial(m_material.data());
28}
29
30// Take the start, control and end point of a curve and return the points A, B, C
31// representing the curve as Q(s) = A*s*s + B*s + C
32std::array<QVector2D, 3> QSGCurveStrokeNode::curveABC(const std::array<QVector2D, 3> &p)
33{
34 QVector2D a = p[0] - 2*p[1] + p[2];
35 QVector2D b = 2*p[1] - 2*p[0];
36 QVector2D c = p[0];
37
38 return {a, b, c};
39}
40
41/*!
42 Append a triangle with \a vtx corners within which the fragment shader will
43 draw the visible part of a quadratic curve from ctl[0] to ctl[2] with
44 control point ctl[1] (AKA a quadratic Bézier curve with 3 control points).
45 The \a normal vectors are used in the vertex shader to expand the triangle
46 according to its stroke width: therefore, it's ok for the triangle to be
47 degenerate, and get expanded to size in the vertex shader. Normals are
48 usually unit vectors, but it's also ok for some to have larger magnitudes,
49 to handle the case when miter corners need to be extended proportionally
50 farther as stroke width increases.
51*/
52void QSGCurveStrokeNode::appendTriangle(const std::array<QVector2D, 3> &vtx,
53 const std::array<QVector2D, 3> &ctl,
54 const std::array<QVector2D, 3> &normal,
55 const std::array<float, 3> &extrusions)
56{
57 auto abc = curveABC(ctl);
58
59 int currentVertex = m_uncookedVertexes.count();
60
61 for (int i = 0; i < 3; ++i) {
62 m_uncookedVertexes.append( { vtx[i].x(), vtx[i].y(),
63 abc[0].x(), abc[0].y(), abc[1].x(), abc[1].y(), abc[2].x(), abc[2].y(),
64 normal[i].x(), normal[i].y(), extrusions[i] } );
65 }
66 m_uncookedIndexes << currentVertex << currentVertex + 1 << currentVertex + 2;
67}
68
69/*!
70 Append a triangle with \a vtx corners within which the fragment shader will
71 draw the visible part of a line from ctl[0] to ctl[2].
72 The \a normal vectors are used in the vertex shader to expand the triangle
73 according to its stroke width: therefore, it's ok for the triangle to be
74 degenerate, and get expanded to size in the vertex shader. Normals are
75 usually unit vectors, but it's also ok for some to have larger magnitudes,
76 to handle the case when miter corners need to be extended proportionally
77 farther as stroke width increases.
78*/
79void QSGCurveStrokeNode::appendTriangle(const std::array<QVector2D, 3> &vtx,
80 const std::array<QVector2D, 2> &ctl,
81 const std::array<QVector2D, 3> &normal,
82 const std::array<float, 3> &extrusions)
83{
84 // We could reduce this to a linear equation by setting A to (0,0).
85 // However, then we cannot use the cubic solution and need an additional
86 // code path in the shader. The following formulation looks more complicated
87 // but allows to always use the cubic solution.
88 auto A = ctl[1] - ctl[0];
89 auto B = QVector2D(0., 0.);
90 auto C = ctl[0];
91
92 int currentVertex = m_uncookedVertexes.count();
93
94 for (int i = 0; i < 3; ++i) {
95 m_uncookedVertexes.append( { vtx[i].x(), vtx[i].y(),
96 A.x(), A.y(), B.x(), B.y(), C.x(), C.y(),
97 normal[i].x(), normal[i].y(), extrusions[i] } );
98 }
99 m_uncookedIndexes << currentVertex << currentVertex + 1 << currentVertex + 2;
100}
101
102void QSGCurveStrokeNode::cookGeometry()
103{
104 updateMaterial(); // by now, setCosmeticStroke has been called if necessary
105 QSGGeometry *g = geometry();
106 if (g->indexType() != QSGGeometry::UnsignedIntType) {
107 g = new QSGGeometry(attributes(),
108 m_uncookedVertexes.size(),
109 m_uncookedIndexes.size(),
110 QSGGeometry::UnsignedIntType);
111 setGeometry(g);
112 } else {
113 g->allocate(m_uncookedVertexes.size(), m_uncookedIndexes.size());
114 }
115
116 g->setDrawingMode(QSGGeometry::DrawTriangles);
117 memcpy(g->vertexData(),
118 m_uncookedVertexes.constData(),
119 g->vertexCount() * g->sizeOfVertex());
120 memcpy(g->indexData(),
121 m_uncookedIndexes.constData(),
122 g->indexCount() * g->sizeOfIndex());
123
124 m_uncookedIndexes.clear();
125 m_uncookedIndexes.squeeze();
126 m_uncookedVertexes.clear();
127 m_uncookedVertexes.squeeze();
128}
129
130const QSGGeometry::AttributeSet &QSGCurveStrokeNode::attributes()
131{
132 static QSGGeometry::Attribute data[] = {
133 QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute), //vertexCoord
134 QSGGeometry::Attribute::createWithAttributeType(1, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //A
135 QSGGeometry::Attribute::createWithAttributeType(2, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //B
136 QSGGeometry::Attribute::createWithAttributeType(3, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //C
137 QSGGeometry::Attribute::createWithAttributeType(4, 3, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //normalExt
138 };
139 static QSGGeometry::AttributeSet attrs = { 5, sizeof(StrokeVertex), data };
140 return attrs;
141}
142
143// TODO remove when we consider the expanding-stroke shader to be stable for full-time use
144bool QSGCurveStrokeNode::expandingStrokeEnabled()
145{
146 static const bool ret = qEnvironmentVariableIntValue("QT_QUICKSHAPES_STROKE_EXPANDING");
147 return ret;
148}
149
150QT_END_NAMESPACE