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 | |
4 | #ifndef QSTROKER_P_H |
5 | #define QSTROKER_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 <QtGui/private/qtguiglobal_p.h> |
19 | #include "QtGui/qpainterpath.h" |
20 | #include "private/qdatabuffer_p.h" |
21 | #include "private/qnumeric_p.h" |
22 | |
23 | QT_BEGIN_NAMESPACE |
24 | |
25 | // #define QFIXED_IS_26_6 |
26 | |
27 | #if defined QFIXED_IS_26_6 |
28 | typedef int qfixed; |
29 | #define qt_real_to_fixed(real) qfixed(real * 64) |
30 | #define qt_int_to_fixed(real) qfixed(int(real) << 6) |
31 | #define qt_fixed_to_real(fixed) qreal(fixed / qreal(64)) |
32 | #define qt_fixed_to_int(fixed) int(fixed >> 6) |
33 | struct qfixed2d |
34 | { |
35 | qfixed x; |
36 | qfixed y; |
37 | |
38 | bool operator==(const qfixed2d &other) const { return x == other.x && y == other.y; } |
39 | }; |
40 | #elif defined QFIXED_IS_32_32 |
41 | typedef qint64 qfixed; |
42 | #define qt_real_to_fixed(real) qfixed(real * double(qint64(1) << 32)) |
43 | #define qt_fixed_to_real(fixed) qreal(fixed / double(qint64(1) << 32)) |
44 | struct qfixed2d |
45 | { |
46 | qfixed x; |
47 | qfixed y; |
48 | |
49 | bool operator==(const qfixed2d &other) const { return x == other.x && y == other.y; } |
50 | }; |
51 | #elif defined QFIXED_IS_16_16 |
52 | typedef int qfixed; |
53 | #define qt_real_to_fixed(real) qfixed(real * qreal(1 << 16)) |
54 | #define qt_fixed_to_real(fixed) qreal(fixed / qreal(1 << 16)) |
55 | struct qfixed2d |
56 | { |
57 | qfixed x; |
58 | qfixed y; |
59 | |
60 | bool operator==(const qfixed2d &other) const { return x == other.x && y == other.y; } |
61 | }; |
62 | #else |
63 | typedef qreal qfixed; |
64 | #define qt_real_to_fixed(real) qfixed(real) |
65 | #define qt_fixed_to_real(fixed) fixed |
66 | struct qfixed2d |
67 | { |
68 | qfixed x; |
69 | qfixed y; |
70 | |
71 | bool isFinite() { return qIsFinite(d: x) && qIsFinite(d: y); } |
72 | bool operator==(const qfixed2d &other) const { return qFuzzyCompare(p1: x, p2: other.x) |
73 | && qFuzzyCompare(p1: y, p2: other.y); } |
74 | }; |
75 | #endif |
76 | |
77 | #define QT_PATH_KAPPA 0.5522847498 |
78 | |
79 | QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength, |
80 | QPointF *controlPoints, int *point_count); |
81 | |
82 | qreal qt_t_for_arc_angle(qreal angle); |
83 | |
84 | typedef void (*qStrokerMoveToHook)(qfixed x, qfixed y, void *data); |
85 | typedef void (*qStrokerLineToHook)(qfixed x, qfixed y, void *data); |
86 | typedef void (*qStrokerCubicToHook)(qfixed c1x, qfixed c1y, |
87 | qfixed c2x, qfixed c2y, |
88 | qfixed ex, qfixed ey, |
89 | void *data); |
90 | |
91 | // qtransform.cpp |
92 | Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); |
93 | |
94 | class Q_GUI_EXPORT QStrokerOps |
95 | { |
96 | public: |
97 | struct Element { |
98 | QPainterPath::ElementType type; |
99 | qfixed x; |
100 | qfixed y; |
101 | |
102 | inline bool isMoveTo() const { return type == QPainterPath::MoveToElement; } |
103 | inline bool isLineTo() const { return type == QPainterPath::LineToElement; } |
104 | inline bool isCurveTo() const { return type == QPainterPath::CurveToElement; } |
105 | |
106 | operator qfixed2d () { qfixed2d pt = { .x: x, .y: y }; return pt; } |
107 | }; |
108 | |
109 | QStrokerOps(); |
110 | virtual ~QStrokerOps(); |
111 | |
112 | void setMoveToHook(qStrokerMoveToHook moveToHook) { m_moveTo = moveToHook; } |
113 | void setLineToHook(qStrokerLineToHook lineToHook) { m_lineTo = lineToHook; } |
114 | void setCubicToHook(qStrokerCubicToHook cubicToHook) { m_cubicTo = cubicToHook; } |
115 | |
116 | virtual void begin(void *customData); |
117 | virtual void end(); |
118 | |
119 | inline void moveTo(qfixed x, qfixed y); |
120 | inline void lineTo(qfixed x, qfixed y); |
121 | inline void cubicTo(qfixed x1, qfixed y1, qfixed x2, qfixed y2, qfixed ex, qfixed ey); |
122 | |
123 | void strokePath(const QPainterPath &path, void *data, const QTransform &matrix); |
124 | void strokePolygon(const QPointF *points, int pointCount, bool implicit_close, |
125 | void *data, const QTransform &matrix); |
126 | void strokeEllipse(const QRectF &ellipse, void *data, const QTransform &matrix); |
127 | |
128 | QRectF clipRect() const { return m_clip_rect; } |
129 | void setClipRect(const QRectF &clip) { m_clip_rect = clip; } |
130 | |
131 | void setCurveThresholdFromTransform(const QTransform &transform) |
132 | { |
133 | qreal scale; |
134 | qt_scaleForTransform(transform, scale: &scale); |
135 | m_dashThreshold = scale == 0 ? qreal(0.5) : (qreal(0.5) / scale); |
136 | } |
137 | |
138 | void setCurveThreshold(qfixed threshold) { m_curveThreshold = threshold; } |
139 | qfixed curveThreshold() const { return m_curveThreshold; } |
140 | |
141 | protected: |
142 | inline void emitMoveTo(qfixed x, qfixed y); |
143 | inline void emitLineTo(qfixed x, qfixed y); |
144 | inline void emitCubicTo(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey); |
145 | |
146 | virtual void processCurrentSubpath() = 0; |
147 | QDataBuffer<Element> m_elements; |
148 | |
149 | QRectF m_clip_rect; |
150 | qfixed m_curveThreshold; |
151 | qfixed m_dashThreshold; |
152 | |
153 | void *m_customData; |
154 | qStrokerMoveToHook m_moveTo; |
155 | qStrokerLineToHook m_lineTo; |
156 | qStrokerCubicToHook m_cubicTo; |
157 | |
158 | }; |
159 | |
160 | class Q_GUI_EXPORT QStroker : public QStrokerOps |
161 | { |
162 | public: |
163 | |
164 | enum LineJoinMode { |
165 | FlatJoin, |
166 | SquareJoin, |
167 | MiterJoin, |
168 | RoundJoin, |
169 | RoundCap, |
170 | SvgMiterJoin |
171 | }; |
172 | |
173 | QStroker(); |
174 | ~QStroker(); |
175 | |
176 | void setStrokeWidth(qfixed width) |
177 | { |
178 | m_strokeWidth = width; |
179 | m_curveThreshold = qt_real_to_fixed(qBound(0.00025, 1.0 / qt_fixed_to_real(width), 0.25)); |
180 | } |
181 | qfixed strokeWidth() const { return m_strokeWidth; } |
182 | |
183 | void setCapStyle(Qt::PenCapStyle capStyle) { m_capStyle = joinModeForCap(capStyle); } |
184 | Qt::PenCapStyle capStyle() const { return capForJoinMode(mode: m_capStyle); } |
185 | LineJoinMode capStyleMode() const { return m_capStyle; } |
186 | |
187 | void setJoinStyle(Qt::PenJoinStyle style) { m_joinStyle = joinModeForJoin(joinStyle: style); } |
188 | Qt::PenJoinStyle joinStyle() const { return joinForJoinMode(mode: m_joinStyle); } |
189 | LineJoinMode joinStyleMode() const { return m_joinStyle; } |
190 | |
191 | void setMiterLimit(qfixed length) { m_miterLimit = length; } |
192 | qfixed miterLimit() const { return m_miterLimit; } |
193 | |
194 | void setForceOpen(bool state) { m_forceOpen = state; } |
195 | bool forceOpen() const { return m_forceOpen; } |
196 | |
197 | void joinPoints(qfixed x, qfixed y, const QLineF &nextLine, LineJoinMode join); |
198 | inline void emitMoveTo(qfixed x, qfixed y); |
199 | inline void emitLineTo(qfixed x, qfixed y); |
200 | inline void emitCubicTo(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey); |
201 | |
202 | protected: |
203 | static Qt::PenCapStyle capForJoinMode(LineJoinMode mode); |
204 | static LineJoinMode joinModeForCap(Qt::PenCapStyle); |
205 | |
206 | static Qt::PenJoinStyle joinForJoinMode(LineJoinMode mode); |
207 | static LineJoinMode joinModeForJoin(Qt::PenJoinStyle joinStyle); |
208 | |
209 | void processCurrentSubpath() override; |
210 | |
211 | qfixed m_strokeWidth; |
212 | qfixed m_miterLimit; |
213 | |
214 | LineJoinMode m_capStyle; |
215 | LineJoinMode m_joinStyle; |
216 | |
217 | qfixed m_back1X; |
218 | qfixed m_back1Y; |
219 | |
220 | qfixed m_back2X; |
221 | qfixed m_back2Y; |
222 | |
223 | bool m_forceOpen; |
224 | }; |
225 | |
226 | class Q_GUI_EXPORT QDashStroker : public QStrokerOps |
227 | { |
228 | public: |
229 | QDashStroker(QStroker *stroker); |
230 | ~QDashStroker(); |
231 | |
232 | QStroker *stroker() const { return m_stroker; } |
233 | |
234 | static QList<qfixed> patternForStyle(Qt::PenStyle style); |
235 | static int repetitionLimit() { return 10000; } |
236 | |
237 | void setDashPattern(const QList<qfixed> &dashPattern) { m_dashPattern = dashPattern; } |
238 | QList<qfixed> dashPattern() const { return m_dashPattern; } |
239 | |
240 | void setDashOffset(qreal offset) { m_dashOffset = offset; } |
241 | qreal dashOffset() const { return m_dashOffset; } |
242 | |
243 | void begin(void *data) override; |
244 | void end() override; |
245 | |
246 | inline void setStrokeWidth(qreal width) { m_stroke_width = width; } |
247 | inline void setMiterLimit(qreal limit) { m_miter_limit = limit; } |
248 | |
249 | protected: |
250 | void processCurrentSubpath() override; |
251 | |
252 | QStroker *m_stroker; |
253 | QList<qfixed> m_dashPattern; |
254 | qreal m_dashOffset; |
255 | |
256 | qreal m_stroke_width; |
257 | qreal m_miter_limit; |
258 | }; |
259 | |
260 | |
261 | /******************************************************************************* |
262 | * QStrokerOps inline membmers |
263 | */ |
264 | |
265 | inline void QStrokerOps::emitMoveTo(qfixed x, qfixed y) |
266 | { |
267 | Q_ASSERT(m_moveTo); |
268 | m_moveTo(x, y, m_customData); |
269 | } |
270 | |
271 | inline void QStrokerOps::emitLineTo(qfixed x, qfixed y) |
272 | { |
273 | Q_ASSERT(m_lineTo); |
274 | m_lineTo(x, y, m_customData); |
275 | } |
276 | |
277 | inline void QStrokerOps::emitCubicTo(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey) |
278 | { |
279 | Q_ASSERT(m_cubicTo); |
280 | m_cubicTo(c1x, c1y, c2x, c2y, ex, ey, m_customData); |
281 | } |
282 | |
283 | inline void QStrokerOps::moveTo(qfixed x, qfixed y) |
284 | { |
285 | if (m_elements.size()>1) |
286 | processCurrentSubpath(); |
287 | m_elements.reset(); |
288 | Element e = { .type: QPainterPath::MoveToElement, .x: x, .y: y }; |
289 | m_elements.add(t: e); |
290 | } |
291 | |
292 | inline void QStrokerOps::lineTo(qfixed x, qfixed y) |
293 | { |
294 | Element e = { .type: QPainterPath::LineToElement, .x: x, .y: y }; |
295 | m_elements.add(t: e); |
296 | } |
297 | |
298 | inline void QStrokerOps::cubicTo(qfixed x1, qfixed y1, qfixed x2, qfixed y2, qfixed ex, qfixed ey) |
299 | { |
300 | Element c1 = { .type: QPainterPath::CurveToElement, .x: x1, .y: y1 }; |
301 | Element c2 = { .type: QPainterPath::CurveToDataElement, .x: x2, .y: y2 }; |
302 | Element e = { .type: QPainterPath::CurveToDataElement, .x: ex, .y: ey }; |
303 | m_elements.add(t: c1); |
304 | m_elements.add(t: c2); |
305 | m_elements.add(t: e); |
306 | } |
307 | |
308 | /******************************************************************************* |
309 | * QStroker inline members |
310 | */ |
311 | inline void QStroker::emitMoveTo(qfixed x, qfixed y) |
312 | { |
313 | m_back2X = m_back1X; |
314 | m_back2Y = m_back1Y; |
315 | m_back1X = x; |
316 | m_back1Y = y; |
317 | QStrokerOps::emitMoveTo(x, y); |
318 | } |
319 | |
320 | inline void QStroker::emitLineTo(qfixed x, qfixed y) |
321 | { |
322 | m_back2X = m_back1X; |
323 | m_back2Y = m_back1Y; |
324 | m_back1X = x; |
325 | m_back1Y = y; |
326 | QStrokerOps::emitLineTo(x, y); |
327 | } |
328 | |
329 | inline void QStroker::emitCubicTo(qfixed c1x, qfixed c1y, |
330 | qfixed c2x, qfixed c2y, |
331 | qfixed ex, qfixed ey) |
332 | { |
333 | if (c2x == ex && c2y == ey) { |
334 | if (c1x == ex && c1y == ey) { |
335 | m_back2X = m_back1X; |
336 | m_back2Y = m_back1Y; |
337 | } else { |
338 | m_back2X = c1x; |
339 | m_back2Y = c1y; |
340 | } |
341 | } else { |
342 | m_back2X = c2x; |
343 | m_back2Y = c2y; |
344 | } |
345 | m_back1X = ex; |
346 | m_back1Y = ey; |
347 | QStrokerOps::emitCubicTo(c1x, c1y, c2x, c2y, ex, ey); |
348 | } |
349 | |
350 | /******************************************************************************* |
351 | * QDashStroker inline members |
352 | */ |
353 | inline void QDashStroker::begin(void *data) |
354 | { |
355 | if (m_stroker) |
356 | m_stroker->begin(customData: data); |
357 | QStrokerOps::begin(customData: data); |
358 | } |
359 | |
360 | inline void QDashStroker::end() |
361 | { |
362 | QStrokerOps::end(); |
363 | if (m_stroker) |
364 | m_stroker->end(); |
365 | } |
366 | |
367 | QT_END_NAMESPACE |
368 | |
369 | #endif // QSTROKER_P_H |
370 | |