Coverage Report

Created: 2025-07-16 07:53

/src/qtbase/src/gui/painting/qstroker.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "private/qstroker_p.h"
5
#include "private/qbezier_p.h"
6
#include "qline.h"
7
#include "qtransform.h"
8
#include <qmath.h>
9
10
QT_BEGIN_NAMESPACE
11
12
// #define QPP_STROKE_DEBUG
13
14
class QSubpathForwardIterator
15
{
16
public:
17
    QSubpathForwardIterator(const QDataBuffer<QStrokerOps::Element> *path)
18
0
        : m_path(path), m_pos(0) { }
19
0
    inline int position() const { return m_pos; }
20
0
    inline bool hasNext() const { return m_pos < m_path->size(); }
21
0
    inline QStrokerOps::Element next() { Q_ASSERT(hasNext()); return m_path->at(m_pos++); }
22
23
private:
24
    const QDataBuffer<QStrokerOps::Element> *m_path;
25
    int m_pos;
26
};
27
28
class QSubpathBackwardIterator
29
{
30
public:
31
    QSubpathBackwardIterator(const QDataBuffer<QStrokerOps::Element> *path)
32
0
        : m_path(path), m_pos(path->size() - 1) { }
33
34
0
    inline int position() const { return m_pos; }
35
36
0
    inline bool hasNext() const { return m_pos >= 0; }
37
38
    inline QStrokerOps::Element next()
39
0
    {
40
0
        Q_ASSERT(hasNext());
41
42
0
        QStrokerOps::Element ce = m_path->at(m_pos);   // current element
43
44
0
        if (m_pos == m_path->size() - 1) {
45
0
            --m_pos;
46
0
            ce.type = QPainterPath::MoveToElement;
47
0
            return ce;
48
0
        }
49
50
0
        const QStrokerOps::Element &pe = m_path->at(m_pos + 1); // previous element
51
52
0
        switch (pe.type) {
53
0
        case QPainterPath::LineToElement:
54
0
            ce.type = QPainterPath::LineToElement;
55
0
            break;
56
0
        case QPainterPath::CurveToDataElement:
57
            // First control point?
58
0
            if (ce.type == QPainterPath::CurveToElement) {
59
0
                ce.type = QPainterPath::CurveToDataElement;
60
0
            } else { // Second control point then
61
0
                ce.type = QPainterPath::CurveToElement;
62
0
            }
63
0
            break;
64
0
        case QPainterPath::CurveToElement:
65
0
            ce.type = QPainterPath::CurveToDataElement;
66
0
            break;
67
0
        default:
68
0
            qWarning("QSubpathReverseIterator::next: Case %d unhandled", ce.type);
69
0
            break;
70
0
        }
71
0
        --m_pos;
72
73
0
        return ce;
74
0
    }
75
76
private:
77
    const QDataBuffer<QStrokerOps::Element> *m_path;
78
    int m_pos;
79
};
80
81
class QSubpathFlatIterator
82
{
83
public:
84
    QSubpathFlatIterator(const QDataBuffer<QStrokerOps::Element> *path, qreal threshold)
85
0
        : m_path(path), m_pos(0), m_curve_index(-1), m_curve_threshold(threshold) { }
86
87
0
    inline bool hasNext() const { return m_curve_index >= 0 || m_pos < m_path->size(); }
88
89
    QStrokerOps::Element next()
90
0
    {
91
0
        Q_ASSERT(hasNext());
92
93
0
        if (m_curve_index >= 0) {
94
0
            QStrokerOps::Element e = { QPainterPath::LineToElement,
95
0
                                       qt_real_to_fixed(m_curve.at(m_curve_index).x()),
96
0
                                       qt_real_to_fixed(m_curve.at(m_curve_index).y())
97
0
                                       };
98
0
            ++m_curve_index;
99
0
            if (m_curve_index >= m_curve.size())
100
0
                m_curve_index = -1;
101
0
            return e;
102
0
        }
103
104
0
        QStrokerOps::Element e = m_path->at(m_pos);
105
0
        if (e.isCurveTo()) {
106
0
            Q_ASSERT(m_pos > 0);
107
0
            Q_ASSERT(m_pos < m_path->size());
108
109
0
            m_curve = QBezier::fromPoints(QPointF(qt_fixed_to_real(m_path->at(m_pos-1).x),
110
0
                                                  qt_fixed_to_real(m_path->at(m_pos-1).y)),
111
0
                                          QPointF(qt_fixed_to_real(e.x),
112
0
                                                  qt_fixed_to_real(e.y)),
113
0
                                          QPointF(qt_fixed_to_real(m_path->at(m_pos+1).x),
114
0
                                                  qt_fixed_to_real(m_path->at(m_pos+1).y)),
115
0
                                          QPointF(qt_fixed_to_real(m_path->at(m_pos+2).x),
116
0
                                                  qt_fixed_to_real(m_path->at(m_pos+2).y))).toPolygon(m_curve_threshold);
117
0
            m_curve_index = 1;
118
0
            e.type = QPainterPath::LineToElement;
119
0
            e.x = m_curve.at(0).x();
120
0
            e.y = m_curve.at(0).y();
121
0
            m_pos += 2;
122
0
        }
123
0
        Q_ASSERT(e.isLineTo() || e.isMoveTo());
124
0
        ++m_pos;
125
0
        return e;
126
0
    }
127
128
private:
129
    const QDataBuffer<QStrokerOps::Element> *m_path;
130
    int m_pos;
131
    QPolygonF m_curve;
132
    int m_curve_index;
133
    qreal m_curve_threshold;
134
};
135
136
template <class Iterator> bool qt_stroke_side(Iterator *it, QStroker *stroker,
137
                                              bool capFirst, QLineF *startTangent);
138
139
/*******************************************************************************
140
 * QLineF::angleTo gives us the angle between two lines with respecting the direction.
141
 * Here we want to identify the line's angle direction on the unit circle.
142
 */
143
static inline qreal adapted_angle_on_x(const QLineF &line)
144
0
{
145
0
    return QLineF(0, 0, 1, 0).angleTo(line);
146
0
}
147
148
QStrokerOps::QStrokerOps()
149
11.2k
    : m_elements(0)
150
11.2k
    , m_curveThreshold(qt_real_to_fixed(0.25))
151
11.2k
    , m_dashThreshold(qt_real_to_fixed(0.25))
152
11.2k
    , m_customData(nullptr)
153
11.2k
    , m_moveTo(nullptr)
154
11.2k
    , m_lineTo(nullptr)
155
11.2k
    , m_cubicTo(nullptr)
156
11.2k
{
157
11.2k
}
158
159
QStrokerOps::~QStrokerOps()
160
11.2k
{
161
11.2k
}
162
163
/*!
164
    Prepares the stroker. Call this function once before starting a
165
    stroke by calling moveTo, lineTo or cubicTo.
166
167
    The \a customData is passed back through that callback functions
168
    and can be used by the user to for instance maintain state
169
    information.
170
*/
171
void QStrokerOps::begin(void *customData)
172
0
{
173
0
    m_customData = customData;
174
0
    m_elements.reset();
175
0
}
176
177
178
/*!
179
    Finishes the stroke. Call this function once when an entire
180
    primitive has been stroked.
181
*/
182
void QStrokerOps::end()
183
0
{
184
0
    if (m_elements.size() > 1)
185
0
        processCurrentSubpath();
186
0
    m_customData = nullptr;
187
0
}
188
189
/*!
190
    Convenience function that decomposes \a path into begin(),
191
    moveTo(), lineTo(), curevTo() and end() calls.
192
193
    The \a customData parameter is used in the callback functions
194
195
    The \a matrix is used to transform the points before input to the
196
    stroker.
197
198
    \sa begin()
199
*/
200
void QStrokerOps::strokePath(const QPainterPath &path, void *customData, const QTransform &matrix)
201
0
{
202
0
    if (path.isEmpty())
203
0
        return;
204
205
0
    setCurveThresholdFromTransform(QTransform());
206
0
    begin(customData);
207
0
    int count = path.elementCount();
208
0
    if (matrix.isIdentity()) {
209
0
        for (int i=0; i<count; ++i) {
210
0
            const QPainterPath::Element &e = path.elementAt(i);
211
0
            switch (e.type) {
212
0
            case QPainterPath::MoveToElement:
213
0
                moveTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
214
0
                break;
215
0
            case QPainterPath::LineToElement:
216
0
                lineTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
217
0
                break;
218
0
            case QPainterPath::CurveToElement:
219
0
                {
220
0
                    const QPainterPath::Element &cp2 = path.elementAt(++i);
221
0
                    const QPainterPath::Element &ep = path.elementAt(++i);
222
0
                    cubicTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y),
223
0
                            qt_real_to_fixed(cp2.x), qt_real_to_fixed(cp2.y),
224
0
                            qt_real_to_fixed(ep.x), qt_real_to_fixed(ep.y));
225
0
                }
226
0
                break;
227
0
            default:
228
0
                break;
229
0
            }
230
0
        }
231
0
    } else {
232
0
        for (int i=0; i<count; ++i) {
233
0
            const QPainterPath::Element &e = path.elementAt(i);
234
0
            QPointF pt = QPointF(e.x, e.y) * matrix;
235
0
            switch (e.type) {
236
0
            case QPainterPath::MoveToElement:
237
0
                moveTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
238
0
                break;
239
0
            case QPainterPath::LineToElement:
240
0
                lineTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
241
0
                break;
242
0
            case QPainterPath::CurveToElement:
243
0
                {
244
0
                    QPointF cp2 = ((QPointF) path.elementAt(++i)) * matrix;
245
0
                    QPointF ep = ((QPointF) path.elementAt(++i)) * matrix;
246
0
                    cubicTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()),
247
0
                            qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
248
0
                            qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
249
0
                }
250
0
                break;
251
0
            default:
252
0
                break;
253
0
            }
254
0
        }
255
0
    }
256
0
    end();
257
0
}
258
259
/*!
260
    Convenience function for stroking a polygon of the \a pointCount
261
    first points in \a points. If \a implicit_close is set to true a
262
    line is implicitly drawn between the first and last point in the
263
    polygon. Typically true for polygons and false for polylines.
264
265
    The \a matrix is used to transform the points before they enter the
266
    stroker.
267
268
    \sa begin()
269
*/
270
271
void QStrokerOps::strokePolygon(const QPointF *points, int pointCount, bool implicit_close,
272
                                void *data, const QTransform &matrix)
273
0
{
274
0
    if (!pointCount)
275
0
        return;
276
277
0
    setCurveThresholdFromTransform(QTransform());
278
0
    begin(data);
279
0
    if (matrix.isIdentity()) {
280
0
        moveTo(qt_real_to_fixed(points[0].x()), qt_real_to_fixed(points[0].y()));
281
0
        for (int i=1; i<pointCount; ++i)
282
0
            lineTo(qt_real_to_fixed(points[i].x()),
283
0
                   qt_real_to_fixed(points[i].y()));
284
0
        if (implicit_close)
285
0
            lineTo(qt_real_to_fixed(points[0].x()), qt_real_to_fixed(points[0].y()));
286
0
    } else {
287
0
        QPointF start = points[0] * matrix;
288
0
        moveTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y()));
289
0
        for (int i=1; i<pointCount; ++i) {
290
0
            QPointF pt = points[i] * matrix;
291
0
            lineTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
292
0
        }
293
0
        if (implicit_close)
294
0
            lineTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y()));
295
0
    }
296
0
    end();
297
0
}
298
299
/*!
300
    Convenience function for stroking an ellipse with bounding rect \a
301
    rect. The \a matrix is used to transform the coordinates before
302
    they enter the stroker.
303
*/
304
void QStrokerOps::strokeEllipse(const QRectF &rect, void *data, const QTransform &matrix)
305
0
{
306
0
    int count = 0;
307
0
    QPointF pts[12];
308
0
    QPointF start = qt_curves_for_arc(rect, 0, -360, pts, &count);
309
0
    Q_ASSERT(count == 12); // a perfect circle..
310
311
0
    if (!matrix.isIdentity()) {
312
0
        start = start * matrix;
313
0
        for (int i=0; i<12; ++i) {
314
0
            pts[i] = pts[i] * matrix;
315
0
        }
316
0
    }
317
318
0
    setCurveThresholdFromTransform(QTransform());
319
0
    begin(data);
320
0
    moveTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y()));
321
0
    for (int i=0; i<12; i+=3) {
322
0
        cubicTo(qt_real_to_fixed(pts[i].x()), qt_real_to_fixed(pts[i].y()),
323
0
                qt_real_to_fixed(pts[i+1].x()), qt_real_to_fixed(pts[i+1].y()),
324
0
                qt_real_to_fixed(pts[i+2].x()), qt_real_to_fixed(pts[i+2].y()));
325
0
    }
326
0
    end();
327
0
}
328
329
330
QStroker::QStroker()
331
7.52k
    : m_capStyle(SquareJoin), m_joinStyle(FlatJoin),
332
7.52k
      m_back1X(0), m_back1Y(0),
333
7.52k
      m_back2X(0), m_back2Y(0),
334
7.52k
      m_forceOpen(false)
335
7.52k
{
336
7.52k
    m_strokeWidth = qt_real_to_fixed(1);
337
7.52k
    m_miterLimit = qt_real_to_fixed(2);
338
7.52k
}
339
340
QStroker::~QStroker()
341
{
342
}
343
344
Qt::PenCapStyle QStroker::capForJoinMode(LineJoinMode mode)
345
0
{
346
0
    if (mode == FlatJoin) return Qt::FlatCap;
347
0
    else if (mode == SquareJoin) return Qt::SquareCap;
348
0
    else return Qt::RoundCap;
349
0
}
350
351
QStroker::LineJoinMode QStroker::joinModeForCap(Qt::PenCapStyle style)
352
0
{
353
0
    if (style == Qt::FlatCap) return FlatJoin;
354
0
    else if (style == Qt::SquareCap) return SquareJoin;
355
0
    else return RoundCap;
356
0
}
357
358
Qt::PenJoinStyle QStroker::joinForJoinMode(LineJoinMode mode)
359
0
{
360
0
    if (mode == FlatJoin) return Qt::BevelJoin;
361
0
    else if (mode == MiterJoin) return Qt::MiterJoin;
362
0
    else if (mode == SvgMiterJoin) return Qt::SvgMiterJoin;
363
0
    else return Qt::RoundJoin;
364
0
}
365
366
QStroker::LineJoinMode QStroker::joinModeForJoin(Qt::PenJoinStyle joinStyle)
367
0
{
368
0
    if (joinStyle == Qt::BevelJoin) return FlatJoin;
369
0
    else if (joinStyle == Qt::MiterJoin) return MiterJoin;
370
0
    else if (joinStyle == Qt::SvgMiterJoin) return SvgMiterJoin;
371
0
    else return RoundJoin;
372
0
}
373
374
375
/*!
376
    This function is called to stroke the currently built up
377
    subpath. The subpath is cleared when the function completes.
378
*/
379
void QStroker::processCurrentSubpath()
380
0
{
381
0
    Q_ASSERT(!m_elements.isEmpty());
382
0
    Q_ASSERT(m_elements.first().type == QPainterPath::MoveToElement);
383
0
    Q_ASSERT(m_elements.size() > 1);
384
385
0
    QSubpathForwardIterator fwit(&m_elements);
386
0
    QSubpathBackwardIterator bwit(&m_elements);
387
388
0
    QLineF fwStartTangent, bwStartTangent;
389
390
0
    bool fwclosed = qt_stroke_side(&fwit, this, false, &fwStartTangent);
391
0
    bool bwclosed = qt_stroke_side(&bwit, this, !fwclosed, &bwStartTangent);
392
393
0
    if (!bwclosed && !fwStartTangent.isNull())
394
0
        joinPoints(m_elements.at(0).x, m_elements.at(0).y, fwStartTangent, m_capStyle);
395
0
}
396
397
398
/*!
399
    \internal
400
*/
401
void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine, LineJoinMode join)
402
0
{
403
#ifdef QPP_STROKE_DEBUG
404
    printf(" -----> joinPoints: around=(%.0f, %.0f), next_p1=(%.0f, %.f) next_p2=(%.0f, %.f)\n",
405
           qt_fixed_to_real(focal_x),
406
           qt_fixed_to_real(focal_y),
407
           nextLine.x1(), nextLine.y1(), nextLine.x2(), nextLine.y2());
408
#endif
409
    // points connected already, don't join
410
411
0
#if !defined (QFIXED_26_6) && !defined (Q_FIXED_32_32)
412
0
    if (qFuzzyCompare(m_back1X, nextLine.x1()) && qFuzzyCompare(m_back1Y, nextLine.y1()))
413
0
        return;
414
#else
415
    if (m_back1X == qt_real_to_fixed(nextLine.x1())
416
        && m_back1Y == qt_real_to_fixed(nextLine.y1())) {
417
        return;
418
    }
419
#endif
420
0
    QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y),
421
0
                    qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y));
422
0
    QPointF isect;
423
0
    QLineF::IntersectionType type = prevLine.intersects(nextLine, &isect);
424
425
0
    if (join == FlatJoin) {
426
0
        QLineF shortCut(prevLine.p2(), nextLine.p1());
427
0
        qreal angle = shortCut.angleTo(prevLine);
428
0
        if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
429
0
            emitLineTo(focal_x, focal_y);
430
0
            emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
431
0
            return;
432
0
        }
433
0
        emitLineTo(qt_real_to_fixed(nextLine.x1()),
434
0
                   qt_real_to_fixed(nextLine.y1()));
435
436
0
    } else {
437
0
        if (join == MiterJoin) {
438
0
            qreal appliedMiterLimit = qt_fixed_to_real(m_strokeWidth * m_miterLimit);
439
440
            // If we are on the inside, do the short cut...
441
0
            QLineF shortCut(prevLine.p2(), nextLine.p1());
442
0
            qreal angle = shortCut.angleTo(prevLine);
443
0
            if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
444
0
                emitLineTo(focal_x, focal_y);
445
0
                emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
446
0
                return;
447
0
            }
448
0
            QLineF miterLine(QPointF(qt_fixed_to_real(m_back1X),
449
0
                                     qt_fixed_to_real(m_back1Y)), isect);
450
0
            if (type == QLineF::NoIntersection || miterLine.length() > appliedMiterLimit) {
451
0
                QLineF l1(prevLine);
452
0
                l1.setLength(appliedMiterLimit);
453
0
                l1.translate(prevLine.dx(), prevLine.dy());
454
455
0
                QLineF l2(nextLine);
456
0
                l2.setLength(appliedMiterLimit);
457
0
                l2.translate(-l2.dx(), -l2.dy());
458
459
0
                emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
460
0
                emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
461
0
                emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
462
0
            } else {
463
0
                emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
464
0
                emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
465
0
            }
466
467
0
        } else if (join == SquareJoin) {
468
0
            qfixed offset = m_strokeWidth / 2;
469
470
0
            QLineF l1(prevLine);
471
0
            qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy()));
472
0
            if (dp > 0)  // same direction, means that prevLine is from a bezier that has been "reversed" by shifting
473
0
                l1 = QLineF(prevLine.p2(), prevLine.p1());
474
0
            else
475
0
                l1.translate(l1.dx(), l1.dy());
476
0
            l1.setLength(qt_fixed_to_real(offset));
477
0
            QLineF l2(nextLine.p2(), nextLine.p1());
478
0
            l2.translate(l2.dx(), l2.dy());
479
0
            l2.setLength(qt_fixed_to_real(offset));
480
0
            emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
481
0
            emitLineTo(qt_real_to_fixed(l2.x2()), qt_real_to_fixed(l2.y2()));
482
0
            emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
483
484
0
        } else if (join == RoundJoin) {
485
0
            qfixed offset = m_strokeWidth / 2;
486
487
0
            QLineF shortCut(prevLine.p2(), nextLine.p1());
488
0
            qreal angle = shortCut.angleTo(prevLine);
489
0
            if ((type == QLineF::BoundedIntersection || (angle > qreal(90.01))) && nextLine.length() > offset) {
490
0
                emitLineTo(focal_x, focal_y);
491
0
                emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
492
0
                return;
493
0
            }
494
0
            qreal l1_on_x = adapted_angle_on_x(prevLine);
495
0
            qreal l2_on_x = adapted_angle_on_x(nextLine);
496
497
0
            qreal sweepLength = qAbs(l2_on_x - l1_on_x);
498
499
0
            int point_count;
500
0
            QPointF curves[15];
501
502
0
            QPointF curve_start =
503
0
                qt_curves_for_arc(QRectF(qt_fixed_to_real(focal_x - offset),
504
0
                                         qt_fixed_to_real(focal_y - offset),
505
0
                                         qt_fixed_to_real(offset * 2),
506
0
                                         qt_fixed_to_real(offset * 2)),
507
0
                                  l1_on_x + 90, -sweepLength,
508
0
                                  curves, &point_count);
509
510
//             // line to the beginning of the arc segment, (should not be needed).
511
//             emitLineTo(qt_real_to_fixed(curve_start.x()), qt_real_to_fixed(curve_start.y()));
512
0
            Q_UNUSED(curve_start);
513
514
0
            for (int i=0; i<point_count; i+=3) {
515
0
                emitCubicTo(qt_real_to_fixed(curves[i].x()),
516
0
                            qt_real_to_fixed(curves[i].y()),
517
0
                            qt_real_to_fixed(curves[i+1].x()),
518
0
                            qt_real_to_fixed(curves[i+1].y()),
519
0
                            qt_real_to_fixed(curves[i+2].x()),
520
0
                            qt_real_to_fixed(curves[i+2].y()));
521
0
            }
522
523
            // line to the end of the arc segment, (should also not be needed).
524
0
            emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
525
526
        // Same as round join except we know its 180 degrees. Can also optimize this
527
        // later based on the addEllipse logic
528
0
        } else if (join == RoundCap) {
529
0
            qfixed offset = m_strokeWidth / 2;
530
531
            // first control line
532
0
            QLineF l1 = prevLine;
533
0
            qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy()));
534
0
            if (dp > 0)  // same direction, means that prevLine is from a bezier that has been "reversed" by shifting
535
0
                l1 = QLineF(prevLine.p2(), prevLine.p1());
536
0
            else
537
0
                l1.translate(l1.dx(), l1.dy());
538
0
            l1.setLength(QT_PATH_KAPPA * offset);
539
540
            // second control line, find through normal between prevLine and focal.
541
0
            QLineF l2(qt_fixed_to_real(focal_x), qt_fixed_to_real(focal_y),
542
0
                      prevLine.x2(), prevLine.y2());
543
0
            l2.translate(-l2.dy(), l2.dx());
544
0
            l2.setLength(QT_PATH_KAPPA * offset);
545
546
0
            emitCubicTo(qt_real_to_fixed(l1.x2()),
547
0
                        qt_real_to_fixed(l1.y2()),
548
0
                        qt_real_to_fixed(l2.x2()),
549
0
                        qt_real_to_fixed(l2.y2()),
550
0
                        qt_real_to_fixed(l2.x1()),
551
0
                        qt_real_to_fixed(l2.y1()));
552
553
            // move so that it matches
554
0
            l2 = QLineF(l2.x1(), l2.y1(), l2.x1()-l2.dx(), l2.y1()-l2.dy());
555
556
            // last line is parallel to l1 so just shift it down.
557
0
            l1.translate(nextLine.x1() - l1.x1(), nextLine.y1() - l1.y1());
558
559
0
            emitCubicTo(qt_real_to_fixed(l2.x2()),
560
0
                        qt_real_to_fixed(l2.y2()),
561
0
                        qt_real_to_fixed(l1.x2()),
562
0
                        qt_real_to_fixed(l1.y2()),
563
0
                        qt_real_to_fixed(l1.x1()),
564
0
                        qt_real_to_fixed(l1.y1()));
565
0
        } else if (join == SvgMiterJoin) {
566
0
            QLineF shortCut(prevLine.p2(), nextLine.p1());
567
0
            qreal angle = shortCut.angleTo(prevLine);
568
0
            if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
569
0
                emitLineTo(focal_x, focal_y);
570
0
                emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
571
0
                return;
572
0
            }
573
0
            QLineF miterLine(QPointF(qt_fixed_to_real(focal_x),
574
0
                                     qt_fixed_to_real(focal_y)), isect);
575
0
            if (type == QLineF::NoIntersection || miterLine.length() > qt_fixed_to_real(m_strokeWidth * m_miterLimit) / 2) {
576
0
                emitLineTo(qt_real_to_fixed(nextLine.x1()),
577
0
                           qt_real_to_fixed(nextLine.y1()));
578
0
            } else {
579
0
                emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
580
0
                emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
581
0
            }
582
0
        } else {
583
0
            Q_ASSERT(!"QStroker::joinPoints(), bad join style...");
584
0
        }
585
0
    }
586
0
}
587
588
589
/*
590
   Strokes a subpath side using the \a it as source. Results are put into
591
   \a stroke. The function returns \c true if the subpath side was closed.
592
   If \a capFirst is true, we will use capPoints instead of joinPoints to
593
   connect the first segment, other segments will be joined using joinPoints.
594
   This is to put capping in order...
595
*/
596
template <class Iterator> bool qt_stroke_side(Iterator *it,
597
                                              QStroker *stroker,
598
                                              bool capFirst,
599
                                              QLineF *startTangent)
600
0
{
601
    // Used in CurveToElement section below.
602
0
    const int MAX_OFFSET = 16;
603
0
    QBezier offsetCurves[MAX_OFFSET];
604
605
0
    Q_ASSERT(it->hasNext()); // The initaial move to
606
0
    QStrokerOps::Element first_element = it->next();
607
0
    Q_ASSERT(first_element.isMoveTo());
608
609
0
    qfixed2d start = first_element;
610
611
#ifdef QPP_STROKE_DEBUG
612
    qDebug(" -> (side) [%.2f, %.2f], startPos=%d",
613
           qt_fixed_to_real(start.x),
614
           qt_fixed_to_real(start.y));
615
#endif
616
617
0
    qfixed2d prev = start;
618
619
0
    bool first = true;
620
621
0
    qfixed offset = stroker->strokeWidth() / 2;
622
623
0
    while (it->hasNext()) {
624
0
        QStrokerOps::Element e = it->next();
625
626
        // LineToElement
627
0
        if (e.isLineTo()) {
628
#ifdef QPP_STROKE_DEBUG
629
            qDebug("\n ---> (side) lineto [%.2f, %.2f]", e.x, e.y);
630
#endif
631
0
            QLineF line(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y),
632
0
                        qt_fixed_to_real(e.x), qt_fixed_to_real(e.y));
633
0
            if (line.p1() != line.p2()) {
634
0
                QLineF normal = line.normalVector();
635
0
                normal.setLength(offset);
636
0
                line.translate(normal.dx(), normal.dy());
637
638
                // If we are starting a new subpath, move to correct starting point.
639
0
                if (first) {
640
0
                    if (capFirst)
641
0
                        stroker->joinPoints(prev.x, prev.y, line, stroker->capStyleMode());
642
0
                    else
643
0
                        stroker->emitMoveTo(qt_real_to_fixed(line.x1()), qt_real_to_fixed(line.y1()));
644
0
                    *startTangent = line;
645
0
                    first = false;
646
0
                } else {
647
0
                    stroker->joinPoints(prev.x, prev.y, line, stroker->joinStyleMode());
648
0
                }
649
650
                // Add the stroke for this line.
651
0
                stroker->emitLineTo(qt_real_to_fixed(line.x2()),
652
0
                                    qt_real_to_fixed(line.y2()));
653
0
                prev = e;
654
0
            }
655
656
        // CurveToElement
657
0
        } else if (e.isCurveTo()) {
658
0
            QStrokerOps::Element cp2 = it->next(); // control point 2
659
0
            QStrokerOps::Element ep = it->next();  // end point
660
661
#ifdef QPP_STROKE_DEBUG
662
            qDebug("\n ---> (side) cubicTo [%.2f, %.2f]",
663
                   qt_fixed_to_real(ep.x),
664
                   qt_fixed_to_real(ep.y));
665
#endif
666
667
0
            QBezier bezier =
668
0
                QBezier::fromPoints(QPointF(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y)),
669
0
                                    QPointF(qt_fixed_to_real(e.x), qt_fixed_to_real(e.y)),
670
0
                                    QPointF(qt_fixed_to_real(cp2.x), qt_fixed_to_real(cp2.y)),
671
0
                                    QPointF(qt_fixed_to_real(ep.x), qt_fixed_to_real(ep.y)));
672
0
            int count = bezier.shifted(offsetCurves,
673
0
                                       MAX_OFFSET,
674
0
                                       offset,
675
0
                                       stroker->curveThreshold());
676
677
0
            if (count) {
678
                // If we are starting a new subpath, move to correct starting point
679
0
                QLineF tangent = bezier.startTangent();
680
0
                tangent.translate(offsetCurves[0].pt1() - bezier.pt1());
681
0
                if (first) {
682
0
                    QPointF pt = offsetCurves[0].pt1();
683
0
                    if (capFirst) {
684
0
                        stroker->joinPoints(prev.x, prev.y,
685
0
                                            tangent,
686
0
                                            stroker->capStyleMode());
687
0
                    } else {
688
0
                        stroker->emitMoveTo(qt_real_to_fixed(pt.x()),
689
0
                                            qt_real_to_fixed(pt.y()));
690
0
                    }
691
0
                    *startTangent = tangent;
692
0
                    first = false;
693
0
                } else {
694
0
                    stroker->joinPoints(prev.x, prev.y,
695
0
                                        tangent,
696
0
                                        stroker->joinStyleMode());
697
0
                }
698
699
                // Add these beziers
700
0
                for (int i=0; i<count; ++i) {
701
0
                    QPointF cp1 = offsetCurves[i].pt2();
702
0
                    QPointF cp2 = offsetCurves[i].pt3();
703
0
                    QPointF ep = offsetCurves[i].pt4();
704
0
                    stroker->emitCubicTo(qt_real_to_fixed(cp1.x()), qt_real_to_fixed(cp1.y()),
705
0
                                         qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
706
0
                                         qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
707
0
                }
708
0
            }
709
710
0
            prev = ep;
711
0
        }
712
0
    }
713
714
0
    if (start == prev && !stroker->forceOpen()) {
715
        // closed subpath, join first and last point
716
#ifdef QPP_STROKE_DEBUG
717
        qDebug("\n ---> (side) closed subpath");
718
#endif
719
        // don't join empty subpaths
720
0
        if (!first)
721
0
            stroker->joinPoints(prev.x, prev.y, *startTangent, stroker->joinStyleMode());
722
0
        return true;
723
0
    } else {
724
#ifdef QPP_STROKE_DEBUG
725
        qDebug("\n ---> (side) open subpath");
726
#endif
727
0
        return false;
728
0
    }
729
0
}
Unexecuted instantiation: bool qt_stroke_side<QSubpathForwardIterator>(QSubpathForwardIterator*, QStroker*, bool, QLineF*)
Unexecuted instantiation: bool qt_stroke_side<QSubpathBackwardIterator>(QSubpathBackwardIterator*, QStroker*, bool, QLineF*)
730
731
/*!
732
    \internal
733
734
    For a given angle in the range [0 .. 90], finds the corresponding parameter t
735
    of the prototype cubic bezier arc segment
736
    b = fromPoints(QPointF(1, 0), QPointF(1, KAPPA), QPointF(KAPPA, 1), QPointF(0, 1));
737
738
    From the bezier equation:
739
    b.pointAt(t).x() = (1-t)^3 + t*(1-t)^2 + t^2*(1-t)*KAPPA
740
    b.pointAt(t).y() = t*(1-t)^2 * KAPPA + t^2*(1-t) + t^3
741
742
    Third degree coefficients:
743
    b.pointAt(t).x() = at^3 + bt^2 + ct + d
744
    where a = 2-3*KAPPA, b = 3*(KAPPA-1), c = 0, d = 1
745
746
    b.pointAt(t).y() = at^3 + bt^2 + ct + d
747
    where a = 3*KAPPA-2, b = 6*KAPPA+3, c = 3*KAPPA, d = 0
748
749
    Newton's method to find the zero of a function:
750
    given a function f(x) and initial guess x_0
751
    x_1 = f(x_0) / f'(x_0)
752
    x_2 = f(x_1) / f'(x_1)
753
    etc...
754
*/
755
756
qreal qt_t_for_arc_angle(qreal angle)
757
0
{
758
0
    if (qFuzzyIsNull(angle))
759
0
        return 0;
760
761
0
    if (qFuzzyCompare(angle, qreal(90)))
762
0
        return 1;
763
764
0
    qreal radians = qDegreesToRadians(angle);
765
0
    qreal cosAngle = qCos(radians);
766
0
    qreal sinAngle = qSin(radians);
767
768
    // initial guess
769
0
    qreal tc = angle / 90;
770
    // do some iterations of newton's method to approximate cosAngle
771
    // finds the zero of the function b.pointAt(tc).x() - cosAngle
772
0
    tc -= ((((2-3*QT_PATH_KAPPA) * tc + 3*(QT_PATH_KAPPA-1)) * tc) * tc + 1 - cosAngle) // value
773
0
         / (((6-9*QT_PATH_KAPPA) * tc + 6*(QT_PATH_KAPPA-1)) * tc); // derivative
774
0
    tc -= ((((2-3*QT_PATH_KAPPA) * tc + 3*(QT_PATH_KAPPA-1)) * tc) * tc + 1 - cosAngle) // value
775
0
         / (((6-9*QT_PATH_KAPPA) * tc + 6*(QT_PATH_KAPPA-1)) * tc); // derivative
776
777
    // initial guess
778
0
    qreal ts = tc;
779
    // do some iterations of newton's method to approximate sinAngle
780
    // finds the zero of the function b.pointAt(tc).y() - sinAngle
781
0
    ts -= ((((3*QT_PATH_KAPPA-2) * ts -  6*QT_PATH_KAPPA + 3) * ts + 3*QT_PATH_KAPPA) * ts - sinAngle)
782
0
         / (((9*QT_PATH_KAPPA-6) * ts + 12*QT_PATH_KAPPA - 6) * ts + 3*QT_PATH_KAPPA);
783
0
    ts -= ((((3*QT_PATH_KAPPA-2) * ts -  6*QT_PATH_KAPPA + 3) * ts + 3*QT_PATH_KAPPA) * ts - sinAngle)
784
0
         / (((9*QT_PATH_KAPPA-6) * ts + 12*QT_PATH_KAPPA - 6) * ts + 3*QT_PATH_KAPPA);
785
786
    // use the average of the t that best approximates cosAngle
787
    // and the t that best approximates sinAngle
788
0
    qreal t = 0.5 * (tc + ts);
789
790
#if 0
791
    printf("angle: %f, t: %f\n", angle, t);
792
    qreal a, b, c, d;
793
    bezierCoefficients(t, a, b, c, d);
794
    printf("cosAngle: %.10f, value: %.10f\n", cosAngle, a + b + c * QT_PATH_KAPPA);
795
    printf("sinAngle: %.10f, value: %.10f\n", sinAngle, b * QT_PATH_KAPPA + c + d);
796
#endif
797
798
0
    return t;
799
0
}
800
801
Q_GUI_EXPORT void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length,
802
                            QPointF* startPoint, QPointF *endPoint);
803
804
/*!
805
    \internal
806
807
    Creates a number of curves for a given arc definition. The arc is
808
    defined an arc along the ellipses that fits into \a rect starting
809
    at \a startAngle and an arc length of \a sweepLength.
810
811
    The function has three out parameters. The return value is the
812
    starting point of the arc. The \a curves array represents the list
813
    of cubicTo elements up to a maximum of \a point_count. There are of course
814
    3 points pr curve.
815
*/
816
QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength,
817
                       QPointF *curves, int *point_count)
818
0
{
819
0
    Q_ASSERT(point_count);
820
0
    Q_ASSERT(curves);
821
822
0
    *point_count = 0;
823
0
    if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height())
824
0
        || qt_is_nan(startAngle) || qt_is_nan(sweepLength)) {
825
0
        qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined");
826
0
        return QPointF();
827
0
    }
828
829
0
    if (rect.isNull()) {
830
0
        return QPointF();
831
0
    }
832
833
0
    qreal x = rect.x();
834
0
    qreal y = rect.y();
835
836
0
    qreal w = rect.width();
837
0
    qreal w2 = rect.width() / 2;
838
0
    qreal w2k = w2 * QT_PATH_KAPPA;
839
840
0
    qreal h = rect.height();
841
0
    qreal h2 = rect.height() / 2;
842
0
    qreal h2k = h2 * QT_PATH_KAPPA;
843
844
0
    QPointF points[16] =
845
0
    {
846
        // start point
847
0
        QPointF(x + w, y + h2),
848
849
        // 0 -> 270 degrees
850
0
        QPointF(x + w, y + h2 + h2k),
851
0
        QPointF(x + w2 + w2k, y + h),
852
0
        QPointF(x + w2, y + h),
853
854
        // 270 -> 180 degrees
855
0
        QPointF(x + w2 - w2k, y + h),
856
0
        QPointF(x, y + h2 + h2k),
857
0
        QPointF(x, y + h2),
858
859
        // 180 -> 90 degrees
860
0
        QPointF(x, y + h2 - h2k),
861
0
        QPointF(x + w2 - w2k, y),
862
0
        QPointF(x + w2, y),
863
864
        // 90 -> 0 degrees
865
0
        QPointF(x + w2 + w2k, y),
866
0
        QPointF(x + w, y + h2 - h2k),
867
0
        QPointF(x + w, y + h2)
868
0
    };
869
870
0
    if (sweepLength > 360) sweepLength = 360;
871
0
    else if (sweepLength < -360) sweepLength = -360;
872
873
    // Special case fast paths
874
0
    if (startAngle == 0.0) {
875
0
        if (sweepLength == 360.0) {
876
0
            for (int i = 11; i >= 0; --i)
877
0
                curves[(*point_count)++] = points[i];
878
0
            return points[12];
879
0
        } else if (sweepLength == -360.0) {
880
0
            for (int i = 1; i <= 12; ++i)
881
0
                curves[(*point_count)++] = points[i];
882
0
            return points[0];
883
0
        }
884
0
    }
885
886
0
    int startSegment = int(qFloor(startAngle / 90));
887
0
    int endSegment = int(qFloor((startAngle + sweepLength) / 90));
888
889
0
    qreal startT = (startAngle - startSegment * 90) / 90;
890
0
    qreal endT = (startAngle + sweepLength - endSegment * 90) / 90;
891
892
0
    int delta = sweepLength > 0 ? 1 : -1;
893
0
    if (delta < 0) {
894
0
        startT = 1 - startT;
895
0
        endT = 1 - endT;
896
0
    }
897
898
    // avoid empty start segment
899
0
    if (qFuzzyIsNull(startT - qreal(1))) {
900
0
        startT = 0;
901
0
        startSegment += delta;
902
0
    }
903
904
    // avoid empty end segment
905
0
    if (qFuzzyIsNull(endT)) {
906
0
        endT = 1;
907
0
        endSegment -= delta;
908
0
    }
909
910
0
    startT = qt_t_for_arc_angle(startT * 90);
911
0
    endT = qt_t_for_arc_angle(endT * 90);
912
913
0
    const bool splitAtStart = !qFuzzyIsNull(startT);
914
0
    const bool splitAtEnd = !qFuzzyIsNull(endT - qreal(1));
915
916
0
    const int end = endSegment + delta;
917
918
    // empty arc?
919
0
    if (startSegment == end) {
920
0
        const int quadrant = 3 - ((startSegment % 4) + 4) % 4;
921
0
        const int j = 3 * quadrant;
922
0
        return delta > 0 ? points[j + 3] : points[j];
923
0
    }
924
925
0
    QPointF startPoint, endPoint;
926
0
    qt_find_ellipse_coords(rect, startAngle, sweepLength, &startPoint, &endPoint);
927
928
0
    for (int i = startSegment; i != end; i += delta) {
929
0
        const int quadrant = 3 - ((i % 4) + 4) % 4;
930
0
        const int j = 3 * quadrant;
931
932
0
        QBezier b;
933
0
        if (delta > 0)
934
0
            b = QBezier::fromPoints(points[j + 3], points[j + 2], points[j + 1], points[j]);
935
0
        else
936
0
            b = QBezier::fromPoints(points[j], points[j + 1], points[j + 2], points[j + 3]);
937
938
        // empty arc?
939
0
        if (startSegment == endSegment && qFuzzyCompare(startT, endT))
940
0
            return startPoint;
941
942
0
        if (i == startSegment) {
943
0
            if (i == endSegment && splitAtEnd)
944
0
                b = b.bezierOnInterval(startT, endT);
945
0
            else if (splitAtStart)
946
0
                b = b.bezierOnInterval(startT, 1);
947
0
        } else if (i == endSegment && splitAtEnd) {
948
0
            b = b.bezierOnInterval(0, endT);
949
0
        }
950
951
        // push control points
952
0
        curves[(*point_count)++] = b.pt2();
953
0
        curves[(*point_count)++] = b.pt3();
954
0
        curves[(*point_count)++] = b.pt4();
955
0
    }
956
957
0
    Q_ASSERT(*point_count > 0);
958
0
    curves[*(point_count)-1] = endPoint;
959
960
0
    return startPoint;
961
0
}
962
963
964
0
static inline void qdashstroker_moveTo(qfixed x, qfixed y, void *data) {
965
0
    ((QStroker *) data)->moveTo(x, y);
966
0
}
967
968
0
static inline void qdashstroker_lineTo(qfixed x, qfixed y, void *data) {
969
0
    ((QStroker *) data)->lineTo(x, y);
970
0
}
971
972
0
static inline void qdashstroker_cubicTo(qfixed, qfixed, qfixed, qfixed, qfixed, qfixed, void *) {
973
0
    Q_ASSERT(0);
974
//     ((QStroker *) data)->cubicTo(c1x, c1y, c2x, c2y, ex, ey);
975
0
}
976
977
978
/*******************************************************************************
979
 * QDashStroker members
980
 */
981
QDashStroker::QDashStroker(QStroker *stroker)
982
3.76k
    : m_stroker(stroker), m_dashOffset(0), m_stroke_width(1), m_miter_limit(1)
983
3.76k
{
984
3.76k
    if (m_stroker) {
985
3.76k
        setMoveToHook(qdashstroker_moveTo);
986
3.76k
        setLineToHook(qdashstroker_lineTo);
987
3.76k
        setCubicToHook(qdashstroker_cubicTo);
988
3.76k
    }
989
3.76k
}
990
991
QDashStroker::~QDashStroker()
992
3.76k
{
993
3.76k
}
994
995
QList<qfixed> QDashStroker::patternForStyle(Qt::PenStyle style)
996
0
{
997
0
    const qfixed space = 2;
998
0
    const qfixed dot = 1;
999
0
    const qfixed dash = 4;
1000
1001
0
    QList<qfixed> pattern;
1002
1003
0
    switch (style) {
1004
0
    case Qt::DashLine:
1005
0
        pattern << dash << space;
1006
0
        break;
1007
0
    case Qt::DotLine:
1008
0
        pattern << dot << space;
1009
0
        break;
1010
0
    case Qt::DashDotLine:
1011
0
        pattern << dash << space << dot << space;
1012
0
        break;
1013
0
    case Qt::DashDotDotLine:
1014
0
        pattern << dash << space << dot << space << dot << space;
1015
0
        break;
1016
0
    default:
1017
0
        break;
1018
0
    }
1019
1020
0
    return pattern;
1021
0
}
1022
1023
static inline bool lineRectIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
1024
0
{
1025
0
    return ((p1.x > tl.x || p2.x > tl.x) && (p1.x < br.x || p2.x < br.x)
1026
0
        && (p1.y > tl.y || p2.y > tl.y) && (p1.y < br.y || p2.y < br.y));
1027
0
}
1028
1029
// If the line intersects the rectangle, this function will return true.
1030
static bool lineIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
1031
0
{
1032
0
    if (!lineRectIntersectsRect(p1, p2, tl, br))
1033
0
        return false;
1034
0
    if (p1.x == p2.x || p1.y == p2.y)
1035
0
        return true;
1036
1037
0
    if (p1.y > p2.y)
1038
0
        qSwap(p1, p2); // make p1 above p2
1039
0
    qfixed2d u;
1040
0
    qfixed2d v;
1041
0
    qfixed2d w = {p2.x - p1.x, p2.y - p1.y};
1042
0
    if (p1.x < p2.x) {
1043
        // backslash
1044
0
        u.x = tl.x - p1.x; u.y = br.y - p1.y;
1045
0
        v.x = br.x - p1.x; v.y = tl.y - p1.y;
1046
0
    } else {
1047
        // slash
1048
0
        u.x = tl.x - p1.x; u.y = tl.y - p1.y;
1049
0
        v.x = br.x - p1.x; v.y = br.y - p1.y;
1050
0
    }
1051
#if defined(QFIXED_IS_26_6) || defined(QFIXED_IS_16_16)
1052
    qint64 val1 = qint64(u.x) * qint64(w.y) - qint64(u.y) * qint64(w.x);
1053
    qint64 val2 = qint64(v.x) * qint64(w.y) - qint64(v.y) * qint64(w.x);
1054
    return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
1055
#elif defined(QFIXED_IS_32_32)
1056
    // Cannot do proper test because it may overflow.
1057
    return true;
1058
#else
1059
0
    qreal val1 = u.x * w.y - u.y * w.x;
1060
0
    qreal val2 = v.x * w.y - v.y * w.x;
1061
0
    return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
1062
0
#endif
1063
0
}
1064
1065
void QDashStroker::processCurrentSubpath()
1066
0
{
1067
0
    int dashCount = qMin(m_dashPattern.size(), 32);
1068
0
    qfixed dashes[32];
1069
1070
0
    if (m_stroker) {
1071
0
        m_customData = m_stroker;
1072
0
        m_stroke_width = m_stroker->strokeWidth();
1073
0
        m_miter_limit = m_stroker->miterLimit();
1074
0
    }
1075
1076
0
    qreal longestLength = 0;
1077
0
    qreal sumLength = 0;
1078
0
    for (int i=0; i<dashCount; ++i) {
1079
0
        dashes[i] = qMax(m_dashPattern.at(i), qreal(0)) * m_stroke_width;
1080
0
        sumLength += dashes[i];
1081
0
        if (dashes[i] > longestLength)
1082
0
            longestLength = dashes[i];
1083
0
    }
1084
1085
0
    if (qFuzzyIsNull(sumLength))
1086
0
        return;
1087
1088
0
    qreal invSumLength = qreal(1) / sumLength;
1089
1090
0
    Q_ASSERT(dashCount > 0);
1091
1092
0
    dashCount = dashCount & -2; // Round down to even number
1093
1094
0
    int idash = 0; // Index to current dash
1095
0
    qreal pos = 0; // The position on the curve, 0 <= pos <= path.length
1096
0
    qreal elen = 0; // element length
1097
0
    qreal doffset = m_dashOffset * m_stroke_width;
1098
1099
    // make sure doffset is in range [0..sumLength)
1100
0
    doffset = std::fmod(doffset, sumLength);
1101
0
    if (doffset < 0)
1102
0
        doffset += sumLength;
1103
1104
0
    while (doffset >= dashes[idash]) {
1105
0
        doffset -= dashes[idash];
1106
0
        if (++idash >= dashCount)
1107
0
            idash = 0;
1108
0
    }
1109
1110
0
    qreal estart = 0; // The elements starting position
1111
0
    qreal estop = 0; // The element stop position
1112
1113
0
    QLineF cline;
1114
1115
0
    QSubpathFlatIterator it(&m_elements, m_dashThreshold);
1116
0
    qfixed2d prev = it.next();
1117
0
    if (!prev.isFinite())
1118
0
        return;
1119
1120
0
    bool clipping = !m_clip_rect.isEmpty();
1121
0
    qfixed2d move_to_pos = prev;
1122
0
    qfixed2d line_to_pos;
1123
1124
    // Pad to avoid clipping the borders of thick pens.
1125
0
    qfixed padding = qt_real_to_fixed(qMax(m_stroke_width, m_miter_limit) * longestLength);
1126
0
    qfixed2d clip_tl = { qt_real_to_fixed(m_clip_rect.left()) - padding,
1127
0
                         qt_real_to_fixed(m_clip_rect.top()) - padding };
1128
0
    qfixed2d clip_br = { qt_real_to_fixed(m_clip_rect.right()) + padding ,
1129
0
                         qt_real_to_fixed(m_clip_rect.bottom()) + padding };
1130
1131
0
    bool hasMoveTo = false;
1132
0
    while (it.hasNext()) {
1133
0
        QStrokerOps::Element e = it.next();
1134
0
        if (!qfixed2d(e).isFinite())
1135
0
            continue;
1136
1137
0
        Q_ASSERT(e.isLineTo());
1138
0
        cline = QLineF(qt_fixed_to_real(prev.x),
1139
0
                       qt_fixed_to_real(prev.y),
1140
0
                       qt_fixed_to_real(e.x),
1141
0
                       qt_fixed_to_real(e.y));
1142
0
        elen = cline.length();
1143
1144
0
        estop = estart + elen;
1145
1146
0
        bool done = pos >= estop;
1147
1148
        // Check if the entire line should be clipped away or simplified
1149
0
        bool clipIt = clipping && !lineIntersectsRect(prev, e, clip_tl, clip_br);
1150
0
        bool skipDashing = elen * invSumLength > repetitionLimit();
1151
0
        int maxDashes = dashCount;
1152
0
        if (skipDashing || clipIt) {
1153
            // Cut away full dash sequences.
1154
0
            elen -= std::floor(elen * invSumLength) * sumLength;
1155
            // Update dash offset.
1156
0
            while (!done) {
1157
0
                qreal dpos = pos + dashes[idash] - doffset - estart;
1158
1159
0
                Q_ASSERT(dpos >= 0);
1160
1161
0
                if (dpos > elen) { // dash extends this line
1162
0
                    doffset = dashes[idash] - (dpos - elen); // subtract the part already used
1163
0
                    pos = estop; // move pos to next path element
1164
0
                    done = true;
1165
0
                } else { // Dash is on this line
1166
0
                    pos = --maxDashes > 0 ? dpos + estart : estop;
1167
0
                    done = pos >= estop;
1168
0
                    if (++idash >= dashCount)
1169
0
                        idash = 0;
1170
0
                    doffset = 0; // full segment so no offset on next.
1171
0
                }
1172
0
            }
1173
0
            if (clipIt) {
1174
0
                hasMoveTo = false;
1175
0
            } else {
1176
                // skip costly dashing, just draw solid line
1177
0
                if (!hasMoveTo) {
1178
0
                    emitMoveTo(move_to_pos.x, move_to_pos.y);
1179
0
                    hasMoveTo = true;
1180
0
                }
1181
0
                emitLineTo(e.x, e.y);
1182
0
            }
1183
0
            move_to_pos = e;
1184
0
        }
1185
1186
        // Dash away...
1187
0
        while (!done) {
1188
0
            QPointF p2;
1189
1190
0
            bool has_offset = doffset > 0;
1191
0
            bool evenDash = (idash & 1) == 0;
1192
0
            qreal dpos = pos + dashes[idash] - doffset - estart;
1193
1194
0
            Q_ASSERT(dpos >= 0);
1195
1196
0
            if (dpos > elen) { // dash extends this line
1197
0
                doffset = dashes[idash] - (dpos - elen); // subtract the part already used
1198
0
                pos = estop; // move pos to next path element
1199
0
                done = true;
1200
0
                p2 = cline.p2();
1201
0
            } else { // Dash is on this line
1202
0
                p2 = cline.pointAt(dpos/elen);
1203
0
                pos = dpos + estart;
1204
0
                done = pos >= estop;
1205
0
                if (++idash >= dashCount)
1206
0
                    idash = 0;
1207
0
                doffset = 0; // full segment so no offset on next.
1208
0
            }
1209
1210
0
            if (evenDash) {
1211
0
                line_to_pos.x = qt_real_to_fixed(p2.x());
1212
0
                line_to_pos.y = qt_real_to_fixed(p2.y());
1213
1214
0
                if (!clipping
1215
0
                    || lineRectIntersectsRect(move_to_pos, line_to_pos, clip_tl, clip_br))
1216
0
                {
1217
                    // If we have an offset, we're continuing a dash
1218
                    // from a previous element and should only
1219
                    // continue the current dash, without starting a
1220
                    // new subpath.
1221
0
                    if (!has_offset || !hasMoveTo) {
1222
0
                        emitMoveTo(move_to_pos.x, move_to_pos.y);
1223
0
                        hasMoveTo = true;
1224
0
                    }
1225
1226
0
                    emitLineTo(line_to_pos.x, line_to_pos.y);
1227
0
                } else {
1228
0
                    hasMoveTo = false;
1229
0
                }
1230
0
                move_to_pos = line_to_pos;
1231
0
            } else {
1232
0
                move_to_pos.x = qt_real_to_fixed(p2.x());
1233
0
                move_to_pos.y = qt_real_to_fixed(p2.y());
1234
0
            }
1235
0
        }
1236
1237
        // Shuffle to the next cycle...
1238
0
        estart = estop;
1239
0
        prev = e;
1240
0
    }
1241
1242
0
}
1243
1244
QT_END_NAMESPACE