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
4#ifndef UTILS_P_H
5#define UTILS_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <private/qquicktranslate_p.h>
19#include <private/qquickitem_p.h>
20#include <private/qsvgnode_p.h>
21
22#include <private/qquadpath_p.h>
23#include <private/qsvgvisitor_p.h>
24
25QT_BEGIN_NAMESPACE
26
27namespace QQuickVectorImageGenerator::Utils
28{
29
30class ViewBoxItem : public QQuickItem
31{
32public:
33 ViewBoxItem(const QRectF viewBox, QQuickItem *parent = nullptr) : QQuickItem(parent), m_viewBox(viewBox) { setXForm(); }
34
35protected:
36 void geometryChange(const QRectF &/*newGeometry*/, const QRectF &/*oldGeometry*/) override
37 {
38 setXForm();
39 }
40
41private:
42 void setXForm()
43 {
44 auto xformProp = transform();
45 xformProp.clear(&xformProp);
46 bool translate = !qFuzzyIsNull(d: m_viewBox.x()) || !qFuzzyIsNull(d: m_viewBox.y());
47 if (translate) {
48 auto *tr = new QQuickTranslate(this);
49 tr->setX(-m_viewBox.x());
50 tr->setY(-m_viewBox.y());
51 xformProp.append(&xformProp, tr);
52 }
53 if (!m_viewBox.isEmpty() && width() && height()) {
54 auto *scale = new QQuickScale(this);
55 qreal sx = width() / m_viewBox.width();
56 qreal sy = height() / m_viewBox.height();
57
58 scale->setXScale(sx);
59 scale->setYScale(sy);
60 xformProp.append(&xformProp, scale);
61 }
62 }
63 QRectF m_viewBox;
64};
65
66inline QPainterPath polygonToPath(const QPolygonF &poly, bool closed)
67{
68 QPainterPath path;
69 if (poly.isEmpty())
70 return path;
71 bool first = true;
72 for (const auto &p : poly) {
73 if (first)
74 path.moveTo(p);
75 else
76 path.lineTo(p);
77 first = false;
78 }
79 if (closed)
80 path.closeSubpath();
81 return path;
82}
83
84inline QString pathHintString(const QQuadPath &qp)
85{
86 QString res;
87 QTextStream str(&res);
88 auto flags = qp.pathHints();
89 if (!flags)
90 return res;
91 str << "pathHints:";
92 bool first = true;
93
94#define CHECK_PATH_HINT(flagName) \
95 if (flags.testFlag(QQuadPath::flagName)) { \
96 if (!first) \
97 str << " |"; \
98 first = false; \
99 str << " ShapePath." #flagName; \
100 }
101
102 CHECK_PATH_HINT(PathLinear)
103 CHECK_PATH_HINT(PathQuadratic)
104 CHECK_PATH_HINT(PathConvex)
105 CHECK_PATH_HINT(PathFillOnRight)
106 CHECK_PATH_HINT(PathSolid)
107 CHECK_PATH_HINT(PathNonIntersecting)
108 CHECK_PATH_HINT(PathNonOverlappingControlPointTriangles)
109
110 return res;
111}
112
113// Find the square that gives the same gradient in QGradient::LogicalMode as
114// objModeRect does in QGradient::ObjectMode
115
116// When the object's bounding box is not square, the stripes that are conceptually
117// perpendicular to the gradient vector within object bounding box space shall render
118// non-perpendicular relative to the gradient vector in user space due to application
119// of the non-uniform scaling transformation from bounding box space to user space.
120inline QRectF mapToQtLogicalMode(const QRectF &objModeRect, const QRectF &boundingRect)
121{
122
123 QRect pixelRect(objModeRect.x() * boundingRect.width() + boundingRect.left(),
124 objModeRect.y() * boundingRect.height() + boundingRect.top(),
125 objModeRect.width() * boundingRect.width(),
126 objModeRect.height() * boundingRect.height());
127
128 if (pixelRect.isEmpty()) // pure horizontal/vertical gradient
129 return pixelRect;
130
131 double w = boundingRect.width();
132 double h = boundingRect.height();
133 double objModeSlope = objModeRect.height() / objModeRect.width();
134 double a = objModeSlope * w / h;
135
136 // do calculation with origin == pixelRect.topLeft
137 double x2 = pixelRect.width();
138 double y2 = pixelRect.height();
139 double x = (x2 + a * y2) / (1 + a * a);
140 double y = y2 - (x - x2)/a;
141
142 return QRectF(pixelRect.topLeft(), QSizeF(x,y));
143}
144
145inline QString toSvgString(const QPainterPath &path)
146{
147 QString svgPathString;
148 QTextStream strm(&svgPathString);
149
150 for (int i = 0; i < path.elementCount(); ++i) {
151 QPainterPath::Element element = path.elementAt(i);
152 if (element.isMoveTo()) {
153 strm << "M " << element.x << " " << element.y << " ";
154 } else if (element.isLineTo()) {
155 strm << "L " << element.x << " " << element.y << " ";
156 } else if (element.isCurveTo()) {
157 QPointF c1(element.x, element.y);
158 ++i;
159 element = path.elementAt(i);
160
161 QPointF c2(element.x, element.y);
162 ++i;
163 element = path.elementAt(i);
164 QPointF ep(element.x, element.y);
165
166 strm << "C "
167 << c1.x() << " "
168 << c1.y() << " "
169 << c2.x() << " "
170 << c2.y() << " "
171 << ep.x() << " "
172 << ep.y() << " ";
173 }
174 }
175
176 return svgPathString;
177}
178
179inline QString toSvgString(const QQuadPath &path)
180{
181 QString svgPathString;
182 QTextStream strm(&svgPathString);
183 path.iterateElements(lambda: [&](const QQuadPath::Element &e, int) {
184 if (e.isSubpathStart())
185 strm << "M " << e.startPoint().x() << " " << e.startPoint().y() << " ";
186 if (e.isLine())
187 strm << "L " << e.endPoint().x() << " " << e.endPoint().y() << " ";
188 else
189 strm << "Q " << e.controlPoint().x() << " " << e.controlPoint().y() << " "
190 << e.endPoint().x() << " " << e.endPoint().y() << " ";
191 });
192
193 return svgPathString;
194}
195
196inline QString strokeCapStyleString(Qt::PenCapStyle strokeCapStyle)
197{
198 QString capStyle;
199 switch (strokeCapStyle) {
200 case Qt::FlatCap:
201 capStyle = QStringLiteral("ShapePath.FlatCap");
202 break;
203 case Qt::SquareCap:
204 capStyle = QStringLiteral("ShapePath.SquareCap");
205 break;
206 case Qt::RoundCap:
207 capStyle = QStringLiteral("ShapePath.RoundCap");
208 break;
209 default:
210 Q_UNREACHABLE();
211 break;
212 }
213
214 return capStyle;
215}
216
217inline QString strokeJoinStyleString(Qt::PenJoinStyle strokeJoinStyle)
218{
219 QString joinStyle;
220 switch (strokeJoinStyle) {
221 case Qt::MiterJoin:
222 joinStyle = QStringLiteral("ShapePath.MiterJoin");
223 break;
224 case Qt::BevelJoin:
225 joinStyle = QStringLiteral("ShapePath.BevelJoin");
226 break;
227 case Qt::RoundJoin:
228 joinStyle = QStringLiteral("ShapePath.RoundJoin");
229 break;
230 default:
231 //TODO: Add support for SvgMiter case
232 Q_UNREACHABLE();
233 break;
234 }
235
236 return joinStyle;
237}
238
239template<typename T>
240inline QString listString(QList<T> list)
241{
242 if (list.isEmpty())
243 return QStringLiteral("[]");
244
245 QString listString;
246 QTextStream stream(&listString);
247 stream << "[";
248
249 if (list.length() > 1) {
250 for (int i = 0; i < list.length() - 1; i++) {
251 T v = list[i];
252 stream << v << ", ";
253 }
254 }
255
256 stream << list.last() << "]";
257 return listString;
258}
259
260}
261
262QT_END_NAMESPACE
263
264#endif // UTILS_P_H
265

source code of qtdeclarative/src/quickvectorimage/generator/utils_p.h