1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtGui module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #ifndef QSTROKER_P_H |
41 | #define QSTROKER_P_H |
42 | |
43 | // |
44 | // W A R N I N G |
45 | // ------------- |
46 | // |
47 | // This file is not part of the Qt API. It exists purely as an |
48 | // implementation detail. This header file may change from version to |
49 | // version without notice, or even be removed. |
50 | // |
51 | // We mean it. |
52 | // |
53 | |
54 | #include <QtGui/private/qtguiglobal_p.h> |
55 | #include "QtGui/qpainterpath.h" |
56 | #include "private/qdatabuffer_p.h" |
57 | #include "private/qnumeric_p.h" |
58 | |
59 | QT_BEGIN_NAMESPACE |
60 | |
61 | // #define QFIXED_IS_26_6 |
62 | |
63 | #if defined QFIXED_IS_26_6 |
64 | typedef int qfixed; |
65 | #define qt_real_to_fixed(real) qfixed(real * 64) |
66 | #define qt_int_to_fixed(real) qfixed(int(real) << 6) |
67 | #define qt_fixed_to_real(fixed) qreal(fixed / qreal(64)) |
68 | #define qt_fixed_to_int(fixed) int(fixed >> 6) |
69 | struct qfixed2d |
70 | { |
71 | qfixed x; |
72 | qfixed y; |
73 | |
74 | bool operator==(const qfixed2d &other) const { return x == other.x && y == other.y; } |
75 | }; |
76 | #elif defined QFIXED_IS_32_32 |
77 | typedef qint64 qfixed; |
78 | #define qt_real_to_fixed(real) qfixed(real * double(qint64(1) << 32)) |
79 | #define qt_fixed_to_real(fixed) qreal(fixed / double(qint64(1) << 32)) |
80 | struct qfixed2d |
81 | { |
82 | qfixed x; |
83 | qfixed y; |
84 | |
85 | bool operator==(const qfixed2d &other) const { return x == other.x && y == other.y; } |
86 | }; |
87 | #elif defined QFIXED_IS_16_16 |
88 | typedef int qfixed; |
89 | #define qt_real_to_fixed(real) qfixed(real * qreal(1 << 16)) |
90 | #define qt_fixed_to_real(fixed) qreal(fixed / qreal(1 << 16)) |
91 | struct qfixed2d |
92 | { |
93 | qfixed x; |
94 | qfixed y; |
95 | |
96 | bool operator==(const qfixed2d &other) const { return x == other.x && y == other.y; } |
97 | }; |
98 | #else |
99 | typedef qreal qfixed; |
100 | #define qt_real_to_fixed(real) qfixed(real) |
101 | #define qt_fixed_to_real(fixed) fixed |
102 | struct qfixed2d |
103 | { |
104 | qfixed x; |
105 | qfixed y; |
106 | |
107 | bool isFinite() { return qIsFinite(d: x) && qIsFinite(d: y); } |
108 | bool operator==(const qfixed2d &other) const { return qFuzzyCompare(p1: x, p2: other.x) |
109 | && qFuzzyCompare(p1: y, p2: other.y); } |
110 | }; |
111 | #endif |
112 | |
113 | #define QT_PATH_KAPPA 0.5522847498 |
114 | |
115 | QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength, |
116 | QPointF *controlPoints, int *point_count); |
117 | |
118 | qreal qt_t_for_arc_angle(qreal angle); |
119 | |
120 | typedef void (*qStrokerMoveToHook)(qfixed x, qfixed y, void *data); |
121 | typedef void (*qStrokerLineToHook)(qfixed x, qfixed y, void *data); |
122 | typedef void (*qStrokerCubicToHook)(qfixed c1x, qfixed c1y, |
123 | qfixed c2x, qfixed c2y, |
124 | qfixed ex, qfixed ey, |
125 | void *data); |
126 | |
127 | // qtransform.cpp |
128 | Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); |
129 | |
130 | class Q_GUI_EXPORT QStrokerOps |
131 | { |
132 | public: |
133 | struct Element { |
134 | QPainterPath::ElementType type; |
135 | qfixed x; |
136 | qfixed y; |
137 | |
138 | inline bool isMoveTo() const { return type == QPainterPath::MoveToElement; } |
139 | inline bool isLineTo() const { return type == QPainterPath::LineToElement; } |
140 | inline bool isCurveTo() const { return type == QPainterPath::CurveToElement; } |
141 | |
142 | operator qfixed2d () { qfixed2d pt = { .x: x, .y: y }; return pt; } |
143 | }; |
144 | |
145 | QStrokerOps(); |
146 | virtual ~QStrokerOps(); |
147 | |
148 | void setMoveToHook(qStrokerMoveToHook moveToHook) { m_moveTo = moveToHook; } |
149 | void setLineToHook(qStrokerLineToHook lineToHook) { m_lineTo = lineToHook; } |
150 | void setCubicToHook(qStrokerCubicToHook cubicToHook) { m_cubicTo = cubicToHook; } |
151 | |
152 | virtual void begin(void *customData); |
153 | virtual void end(); |
154 | |
155 | inline void moveTo(qfixed x, qfixed y); |
156 | inline void lineTo(qfixed x, qfixed y); |
157 | inline void cubicTo(qfixed x1, qfixed y1, qfixed x2, qfixed y2, qfixed ex, qfixed ey); |
158 | |
159 | void strokePath(const QPainterPath &path, void *data, const QTransform &matrix); |
160 | void strokePolygon(const QPointF *points, int pointCount, bool implicit_close, |
161 | void *data, const QTransform &matrix); |
162 | void strokeEllipse(const QRectF &ellipse, void *data, const QTransform &matrix); |
163 | |
164 | QRectF clipRect() const { return m_clip_rect; } |
165 | void setClipRect(const QRectF &clip) { m_clip_rect = clip; } |
166 | |
167 | void setCurveThresholdFromTransform(const QTransform &transform) |
168 | { |
169 | qreal scale; |
170 | qt_scaleForTransform(transform, scale: &scale); |
171 | m_dashThreshold = scale == 0 ? qreal(0.5) : (qreal(0.5) / scale); |
172 | } |
173 | |
174 | void setCurveThreshold(qfixed threshold) { m_curveThreshold = threshold; } |
175 | qfixed curveThreshold() const { return m_curveThreshold; } |
176 | |
177 | protected: |
178 | inline void emitMoveTo(qfixed x, qfixed y); |
179 | inline void emitLineTo(qfixed x, qfixed y); |
180 | inline void emitCubicTo(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey); |
181 | |
182 | virtual void processCurrentSubpath() = 0; |
183 | QDataBuffer<Element> m_elements; |
184 | |
185 | QRectF m_clip_rect; |
186 | qfixed m_curveThreshold; |
187 | qfixed m_dashThreshold; |
188 | |
189 | void *m_customData; |
190 | qStrokerMoveToHook m_moveTo; |
191 | qStrokerLineToHook m_lineTo; |
192 | qStrokerCubicToHook m_cubicTo; |
193 | |
194 | }; |
195 | |
196 | class Q_GUI_EXPORT QStroker : public QStrokerOps |
197 | { |
198 | public: |
199 | |
200 | enum LineJoinMode { |
201 | FlatJoin, |
202 | SquareJoin, |
203 | MiterJoin, |
204 | RoundJoin, |
205 | RoundCap, |
206 | SvgMiterJoin |
207 | }; |
208 | |
209 | QStroker(); |
210 | ~QStroker(); |
211 | |
212 | void setStrokeWidth(qfixed width) |
213 | { |
214 | m_strokeWidth = width; |
215 | m_curveThreshold = qt_real_to_fixed(qBound(0.00025, 1.0 / qt_fixed_to_real(width), 0.25)); |
216 | } |
217 | qfixed strokeWidth() const { return m_strokeWidth; } |
218 | |
219 | void setCapStyle(Qt::PenCapStyle capStyle) { m_capStyle = joinModeForCap(capStyle); } |
220 | Qt::PenCapStyle capStyle() const { return capForJoinMode(mode: m_capStyle); } |
221 | LineJoinMode capStyleMode() const { return m_capStyle; } |
222 | |
223 | void setJoinStyle(Qt::PenJoinStyle style) { m_joinStyle = joinModeForJoin(joinStyle: style); } |
224 | Qt::PenJoinStyle joinStyle() const { return joinForJoinMode(mode: m_joinStyle); } |
225 | LineJoinMode joinStyleMode() const { return m_joinStyle; } |
226 | |
227 | void setMiterLimit(qfixed length) { m_miterLimit = length; } |
228 | qfixed miterLimit() const { return m_miterLimit; } |
229 | |
230 | void setForceOpen(bool state) { m_forceOpen = state; } |
231 | bool forceOpen() { return m_forceOpen; } |
232 | |
233 | void joinPoints(qfixed x, qfixed y, const QLineF &nextLine, LineJoinMode join); |
234 | inline void emitMoveTo(qfixed x, qfixed y); |
235 | inline void emitLineTo(qfixed x, qfixed y); |
236 | inline void emitCubicTo(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey); |
237 | |
238 | protected: |
239 | static Qt::PenCapStyle capForJoinMode(LineJoinMode mode); |
240 | static LineJoinMode joinModeForCap(Qt::PenCapStyle); |
241 | |
242 | static Qt::PenJoinStyle joinForJoinMode(LineJoinMode mode); |
243 | static LineJoinMode joinModeForJoin(Qt::PenJoinStyle joinStyle); |
244 | |
245 | void processCurrentSubpath() override; |
246 | |
247 | qfixed m_strokeWidth; |
248 | qfixed m_miterLimit; |
249 | |
250 | LineJoinMode m_capStyle; |
251 | LineJoinMode m_joinStyle; |
252 | |
253 | qfixed m_back1X; |
254 | qfixed m_back1Y; |
255 | |
256 | qfixed m_back2X; |
257 | qfixed m_back2Y; |
258 | |
259 | bool m_forceOpen; |
260 | }; |
261 | |
262 | class Q_GUI_EXPORT QDashStroker : public QStrokerOps |
263 | { |
264 | public: |
265 | QDashStroker(QStroker *stroker); |
266 | ~QDashStroker(); |
267 | |
268 | QStroker *stroker() const { return m_stroker; } |
269 | |
270 | static QVector<qfixed> patternForStyle(Qt::PenStyle style); |
271 | static int repetitionLimit() { return 10000; } |
272 | |
273 | void setDashPattern(const QVector<qfixed> &dashPattern) { m_dashPattern = dashPattern; } |
274 | QVector<qfixed> dashPattern() const { return m_dashPattern; } |
275 | |
276 | void setDashOffset(qreal offset) { m_dashOffset = offset; } |
277 | qreal dashOffset() const { return m_dashOffset; } |
278 | |
279 | void begin(void *data) override; |
280 | void end() override; |
281 | |
282 | inline void setStrokeWidth(qreal width) { m_stroke_width = width; } |
283 | inline void setMiterLimit(qreal limit) { m_miter_limit = limit; } |
284 | |
285 | protected: |
286 | void processCurrentSubpath() override; |
287 | |
288 | QStroker *m_stroker; |
289 | QVector<qfixed> m_dashPattern; |
290 | qreal m_dashOffset; |
291 | |
292 | qreal m_stroke_width; |
293 | qreal m_miter_limit; |
294 | }; |
295 | |
296 | |
297 | /******************************************************************************* |
298 | * QStrokerOps inline membmers |
299 | */ |
300 | |
301 | inline void QStrokerOps::emitMoveTo(qfixed x, qfixed y) |
302 | { |
303 | Q_ASSERT(m_moveTo); |
304 | m_moveTo(x, y, m_customData); |
305 | } |
306 | |
307 | inline void QStrokerOps::emitLineTo(qfixed x, qfixed y) |
308 | { |
309 | Q_ASSERT(m_lineTo); |
310 | m_lineTo(x, y, m_customData); |
311 | } |
312 | |
313 | inline void QStrokerOps::emitCubicTo(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey) |
314 | { |
315 | Q_ASSERT(m_cubicTo); |
316 | m_cubicTo(c1x, c1y, c2x, c2y, ex, ey, m_customData); |
317 | } |
318 | |
319 | inline void QStrokerOps::moveTo(qfixed x, qfixed y) |
320 | { |
321 | if (m_elements.size()>1) |
322 | processCurrentSubpath(); |
323 | m_elements.reset(); |
324 | Element e = { .type: QPainterPath::MoveToElement, .x: x, .y: y }; |
325 | m_elements.add(t: e); |
326 | } |
327 | |
328 | inline void QStrokerOps::lineTo(qfixed x, qfixed y) |
329 | { |
330 | Element e = { .type: QPainterPath::LineToElement, .x: x, .y: y }; |
331 | m_elements.add(t: e); |
332 | } |
333 | |
334 | inline void QStrokerOps::cubicTo(qfixed x1, qfixed y1, qfixed x2, qfixed y2, qfixed ex, qfixed ey) |
335 | { |
336 | Element c1 = { .type: QPainterPath::CurveToElement, .x: x1, .y: y1 }; |
337 | Element c2 = { .type: QPainterPath::CurveToDataElement, .x: x2, .y: y2 }; |
338 | Element e = { .type: QPainterPath::CurveToDataElement, .x: ex, .y: ey }; |
339 | m_elements.add(t: c1); |
340 | m_elements.add(t: c2); |
341 | m_elements.add(t: e); |
342 | } |
343 | |
344 | /******************************************************************************* |
345 | * QStroker inline members |
346 | */ |
347 | inline void QStroker::emitMoveTo(qfixed x, qfixed y) |
348 | { |
349 | m_back2X = m_back1X; |
350 | m_back2Y = m_back1Y; |
351 | m_back1X = x; |
352 | m_back1Y = y; |
353 | QStrokerOps::emitMoveTo(x, y); |
354 | } |
355 | |
356 | inline void QStroker::emitLineTo(qfixed x, qfixed y) |
357 | { |
358 | m_back2X = m_back1X; |
359 | m_back2Y = m_back1Y; |
360 | m_back1X = x; |
361 | m_back1Y = y; |
362 | QStrokerOps::emitLineTo(x, y); |
363 | } |
364 | |
365 | inline void QStroker::emitCubicTo(qfixed c1x, qfixed c1y, |
366 | qfixed c2x, qfixed c2y, |
367 | qfixed ex, qfixed ey) |
368 | { |
369 | if (c2x == ex && c2y == ey) { |
370 | if (c1x == ex && c1y == ey) { |
371 | m_back2X = m_back1X; |
372 | m_back2Y = m_back1Y; |
373 | } else { |
374 | m_back2X = c1x; |
375 | m_back2Y = c1y; |
376 | } |
377 | } else { |
378 | m_back2X = c2x; |
379 | m_back2Y = c2y; |
380 | } |
381 | m_back1X = ex; |
382 | m_back1Y = ey; |
383 | QStrokerOps::emitCubicTo(c1x, c1y, c2x, c2y, ex, ey); |
384 | } |
385 | |
386 | /******************************************************************************* |
387 | * QDashStroker inline members |
388 | */ |
389 | inline void QDashStroker::begin(void *data) |
390 | { |
391 | if (m_stroker) |
392 | m_stroker->begin(customData: data); |
393 | QStrokerOps::begin(customData: data); |
394 | } |
395 | |
396 | inline void QDashStroker::end() |
397 | { |
398 | QStrokerOps::end(); |
399 | if (m_stroker) |
400 | m_stroker->end(); |
401 | } |
402 | |
403 | QT_END_NAMESPACE |
404 | |
405 | #endif // QSTROKER_P_H |
406 | |