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
23QT_BEGIN_NAMESPACE
24
25// #define QFIXED_IS_26_6
26
27#if defined QFIXED_IS_26_6
28typedef 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)
33struct 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
41typedef 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))
44struct 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
52typedef 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))
55struct 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
63typedef qreal qfixed;
64#define qt_real_to_fixed(real) qfixed(real)
65#define qt_fixed_to_real(fixed) fixed
66struct 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
79QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength,
80 QPointF *controlPoints, int *point_count);
81
82qreal qt_t_for_arc_angle(qreal angle);
83
84typedef void (*qStrokerMoveToHook)(qfixed x, qfixed y, void *data);
85typedef void (*qStrokerLineToHook)(qfixed x, qfixed y, void *data);
86typedef void (*qStrokerCubicToHook)(qfixed c1x, qfixed c1y,
87 qfixed c2x, qfixed c2y,
88 qfixed ex, qfixed ey,
89 void *data);
90
91// qtransform.cpp
92Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
93
94class Q_GUI_EXPORT QStrokerOps
95{
96public:
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
141protected:
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
160class Q_GUI_EXPORT QStroker : public QStrokerOps
161{
162public:
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
202protected:
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
226class Q_GUI_EXPORT QDashStroker : public QStrokerOps
227{
228public:
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
249protected:
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
265inline void QStrokerOps::emitMoveTo(qfixed x, qfixed y)
266{
267 Q_ASSERT(m_moveTo);
268 m_moveTo(x, y, m_customData);
269}
270
271inline void QStrokerOps::emitLineTo(qfixed x, qfixed y)
272{
273 Q_ASSERT(m_lineTo);
274 m_lineTo(x, y, m_customData);
275}
276
277inline 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
283inline 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
292inline 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
298inline 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 */
311inline 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
320inline 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
329inline 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 */
353inline void QDashStroker::begin(void *data)
354{
355 if (m_stroker)
356 m_stroker->begin(customData: data);
357 QStrokerOps::begin(customData: data);
358}
359
360inline void QDashStroker::end()
361{
362 QStrokerOps::end();
363 if (m_stroker)
364 m_stroker->end();
365}
366
367QT_END_NAMESPACE
368
369#endif // QSTROKER_P_H
370

source code of qtbase/src/gui/painting/qstroker_p.h