Coverage Report

Created: 2026-05-31 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/gui/painting/qpathclipper.cpp
Line
Count
Source
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
// Qt-Security score:significant reason:default
4
5
#include "qpathclipper_p.h"
6
7
#include <private/qbezier_p.h>
8
#include <private/qdatabuffer_p.h>
9
#include <private/qnumeric_p.h>
10
#include <qmath.h>
11
#include <algorithm>
12
13
/**
14
  The algorithm is as follows:
15
16
  1. Find all intersections between the two paths (including self-intersections),
17
     and build a winged edge structure of non-intersecting parts.
18
  2. While there are more unhandled edges:
19
    3. Pick a y-coordinate from an unhandled edge.
20
    4. Intersect the horizontal line at y-coordinate with all edges.
21
    5. Traverse intersections left to right deciding whether each subpath should be added or not.
22
    6. If the subpath should be added, traverse the winged-edge structure and add the edges to
23
       a separate winged edge structure.
24
    7. Mark all edges in subpaths crossing the horizontal line as handled.
25
 8. (Optional) Simplify the resulting winged edge structure by merging shared edges.
26
 9. Convert the resulting winged edge structure to a painter path.
27
 */
28
29
#include <qdebug.h>
30
31
QT_BEGIN_NAMESPACE
32
33
static inline bool fuzzyIsNull(qreal d)
34
0
{
35
0
    if (sizeof(qreal) == sizeof(double))
36
0
        return qAbs(d) <= 1e-12;
37
0
    else
38
0
        return qAbs(d) <= 1e-5f;
39
0
}
40
41
static inline bool comparePoints(const QPointF &a, const QPointF &b)
42
0
{
43
0
    return fuzzyIsNull(a.x() - b.x())
44
0
           && fuzzyIsNull(a.y() - b.y());
45
0
}
46
47
//#define QDEBUG_CLIPPER
48
static qreal dot(const QPointF &a, const QPointF &b)
49
0
{
50
0
    return a.x() * b.x() + a.y() * b.y();
51
0
}
52
53
static void normalize(double &x, double &y)
54
0
{
55
0
    double reciprocal = 1 / qSqrt(x * x + y * y);
56
0
    x *= reciprocal;
57
0
    y *= reciprocal;
58
0
}
59
60
struct QIntersection
61
{
62
    qreal alphaA;
63
    qreal alphaB;
64
65
    QPointF pos;
66
};
67
Q_DECLARE_TYPEINFO(QIntersection, Q_PRIMITIVE_TYPE);
68
69
class QIntersectionFinder
70
{
71
public:
72
    void produceIntersections(QPathSegments &segments);
73
    bool hasIntersections(const QPathSegments &a, const QPathSegments &b) const;
74
75
private:
76
    bool linesIntersect(const QLineF &a, const QLineF &b) const;
77
};
78
79
bool QIntersectionFinder::linesIntersect(const QLineF &a, const QLineF &b) const
80
0
{
81
0
    const QPointF p1 = a.p1();
82
0
    const QPointF p2 = a.p2();
83
84
0
    const QPointF q1 = b.p1();
85
0
    const QPointF q2 = b.p2();
86
87
0
    if (comparePoints(p1, p2) || comparePoints(q1, q2))
88
0
        return false;
89
90
0
    const bool p1_equals_q1 = comparePoints(p1, q1);
91
0
    const bool p2_equals_q2 = comparePoints(p2, q2);
92
93
0
    if (p1_equals_q1 && p2_equals_q2)
94
0
        return true;
95
96
0
    const bool p1_equals_q2 = comparePoints(p1, q2);
97
0
    const bool p2_equals_q1 = comparePoints(p2, q1);
98
99
0
    if (p1_equals_q2 && p2_equals_q1)
100
0
        return true;
101
102
0
    const QPointF pDelta = p2 - p1;
103
0
    const QPointF qDelta = q2 - q1;
104
105
0
    const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x();
106
107
0
    if (qFuzzyIsNull(par)) {
108
0
        const QPointF normal(-pDelta.y(), pDelta.x());
109
110
        // coinciding?
111
0
        if (qFuzzyIsNull(dot(normal, q1 - p1))) {
112
0
            const qreal dp = dot(pDelta, pDelta);
113
114
0
            const qreal tq1 = dot(pDelta, q1 - p1);
115
0
            const qreal tq2 = dot(pDelta, q2 - p1);
116
117
0
            if ((tq1 > 0 && tq1 < dp) || (tq2 > 0 && tq2 < dp))
118
0
                return true;
119
120
0
            const qreal dq = dot(qDelta, qDelta);
121
122
0
            const qreal tp1 = dot(qDelta, p1 - q1);
123
0
            const qreal tp2 = dot(qDelta, p2 - q1);
124
125
0
            if ((tp1 > 0 && tp1 < dq) || (tp2 > 0 && tp2 < dq))
126
0
                return true;
127
0
        }
128
129
0
        return false;
130
0
    }
131
132
0
    const qreal invPar = 1 / par;
133
134
0
    const qreal tp = (qDelta.y() * (q1.x() - p1.x()) -
135
0
                      qDelta.x() * (q1.y() - p1.y())) * invPar;
136
137
0
    if (tp < 0 || tp > 1)
138
0
        return false;
139
140
0
    const qreal tq = (pDelta.y() * (q1.x() - p1.x()) -
141
0
                      pDelta.x() * (q1.y() - p1.y())) * invPar;
142
143
0
    return tq >= 0 && tq <= 1;
144
0
}
145
146
bool QIntersectionFinder::hasIntersections(const QPathSegments &a, const QPathSegments &b) const
147
0
{
148
0
    if (a.segments() == 0 || b.segments() == 0)
149
0
        return false;
150
151
0
    const QRectF &rb0 = b.elementBounds(0);
152
153
0
    qreal minX = rb0.left();
154
0
    qreal minY = rb0.top();
155
0
    qreal maxX = rb0.right();
156
0
    qreal maxY = rb0.bottom();
157
158
0
    for (int i = 1; i < b.segments(); ++i) {
159
0
        const QRectF &r = b.elementBounds(i);
160
0
        minX = qMin(minX, r.left());
161
0
        minY = qMin(minY, r.top());
162
0
        maxX = qMax(maxX, r.right());
163
0
        maxY = qMax(maxY, r.bottom());
164
0
    }
165
166
0
    QRectF rb(minX, minY, maxX - minX, maxY - minY);
167
168
0
    for (int i = 0; i < a.segments(); ++i) {
169
0
        const QRectF &r1 = a.elementBounds(i);
170
171
0
        if (r1.left() > rb.right() || rb.left() > r1.right())
172
0
            continue;
173
0
        if (r1.top() > rb.bottom() || rb.top() > r1.bottom())
174
0
            continue;
175
176
0
        for (int j = 0; j < b.segments(); ++j) {
177
0
            const QRectF &r2 = b.elementBounds(j);
178
179
0
            if (r1.left() > r2.right() || r2.left() > r1.right())
180
0
                continue;
181
0
            if (r1.top() > r2.bottom() || r2.top() > r1.bottom())
182
0
                continue;
183
184
0
            if (linesIntersect(a.lineAt(i), b.lineAt(j)))
185
0
                return true;
186
0
        }
187
0
    }
188
189
0
    return false;
190
0
}
191
192
namespace {
193
struct TreeNode
194
{
195
    qreal splitLeft;
196
    qreal splitRight;
197
    bool leaf;
198
199
    int lowestLeftIndex;
200
    int lowestRightIndex;
201
202
    union {
203
        struct {
204
            int first;
205
            int last;
206
        } interval;
207
        struct {
208
            int left;
209
            int right;
210
        } children;
211
    } index;
212
};
213
214
struct RectF
215
{
216
    qreal x1;
217
    qreal y1;
218
    qreal x2;
219
    qreal y2;
220
};
221
222
class SegmentTree
223
{
224
public:
225
    SegmentTree(QPathSegments &segments);
226
227
    void produceIntersections(int segment);
228
229
private:
230
    TreeNode buildTree(int first, int last, int depth, const RectF &bounds);
231
232
    void produceIntersectionsLeaf(const TreeNode &node, int segment);
233
    void produceIntersections(const TreeNode &node, int segment, const RectF &segmentBounds, const RectF &nodeBounds, int axis);
234
    void intersectLines(const QLineF &a, const QLineF &b, QDataBuffer<QIntersection> &intersections);
235
236
    QPathSegments &m_segments;
237
    QList<int> m_index;
238
239
    RectF m_bounds;
240
241
    QList<TreeNode> m_tree;
242
    QDataBuffer<QIntersection> m_intersections;
243
};
244
245
SegmentTree::SegmentTree(QPathSegments &segments)
246
0
    : m_segments(segments),
247
0
      m_intersections(0)
248
0
{
249
0
    m_bounds.x1 = qt_inf();
250
0
    m_bounds.y1 = qt_inf();
251
0
    m_bounds.x2 = -qt_inf();
252
0
    m_bounds.y2 = -qt_inf();
253
254
0
    m_index.resize(m_segments.segments());
255
256
0
    for (int i = 0; i < m_index.size(); ++i) {
257
0
        m_index[i] = i;
258
259
0
        const QRectF &segmentBounds = m_segments.elementBounds(i);
260
261
0
        if (segmentBounds.left() < m_bounds.x1)
262
0
            m_bounds.x1 = segmentBounds.left();
263
0
        if (segmentBounds.top() < m_bounds.y1)
264
0
            m_bounds.y1 = segmentBounds.top();
265
0
        if (segmentBounds.right() > m_bounds.x2)
266
0
            m_bounds.x2 = segmentBounds.right();
267
0
        if (segmentBounds.bottom() > m_bounds.y2)
268
0
            m_bounds.y2 = segmentBounds.bottom();
269
0
    }
270
271
0
    m_tree.resize(1);
272
273
0
    TreeNode root = buildTree(0, m_index.size(), 0, m_bounds);
274
0
    m_tree[0] = root;
275
0
}
276
277
static inline qreal coordinate(const QPointF &pos, int axis)
278
0
{
279
0
    return axis == 0 ? pos.x() : pos.y();
280
0
}
281
282
TreeNode SegmentTree::buildTree(int first, int last, int depth, const RectF &bounds)
283
0
{
284
0
    if (depth >= 24 || (last - first) <= 10) {
285
0
        TreeNode node = {};
286
0
        node.leaf = true;
287
0
        node.index.interval.first = first;
288
0
        node.index.interval.last = last;
289
290
0
        return node;
291
0
    }
292
293
0
    int splitAxis = (depth & 1);
294
295
0
    TreeNode node;
296
0
    node.leaf = false;
297
298
0
    qreal split = 0.5f * ((&bounds.x1)[splitAxis] + (&bounds.x2)[splitAxis]);
299
300
0
    node.splitLeft = (&bounds.x1)[splitAxis];
301
0
    node.splitRight = (&bounds.x2)[splitAxis];
302
303
0
    node.lowestLeftIndex = INT_MAX;
304
0
    node.lowestRightIndex = INT_MAX;
305
306
0
    const int treeSize = m_tree.size();
307
308
0
    node.index.children.left = treeSize;
309
0
    node.index.children.right = treeSize + 1;
310
311
0
    m_tree.resize(treeSize + 2);
312
313
0
    int l = first;
314
0
    int r = last - 1;
315
316
    // partition into left and right sets
317
0
    while (l <= r) {
318
0
        const int index = m_index.at(l);
319
0
        const QRectF &segmentBounds = m_segments.elementBounds(index);
320
321
0
        qreal lowCoordinate = coordinate(segmentBounds.topLeft(), splitAxis);
322
323
0
        if (coordinate(segmentBounds.center(), splitAxis) < split) {
324
0
            qreal highCoordinate = coordinate(segmentBounds.bottomRight(), splitAxis);
325
0
            if (highCoordinate > node.splitLeft)
326
0
                node.splitLeft = highCoordinate;
327
0
            if (index < node.lowestLeftIndex)
328
0
                node.lowestLeftIndex = index;
329
0
            ++l;
330
0
        } else {
331
0
            if (lowCoordinate < node.splitRight)
332
0
                node.splitRight = lowCoordinate;
333
0
            if (index < node.lowestRightIndex)
334
0
                node.lowestRightIndex = index;
335
0
            qSwap(m_index[l], m_index[r]);
336
0
            --r;
337
0
        }
338
0
    }
339
340
0
    RectF lbounds = bounds;
341
0
    (&lbounds.x2)[splitAxis] = node.splitLeft;
342
343
0
    RectF rbounds = bounds;
344
0
    (&rbounds.x1)[splitAxis] = node.splitRight;
345
346
0
    TreeNode left = buildTree(first, l, depth + 1, lbounds);
347
0
    m_tree[node.index.children.left] = left;
348
349
0
    TreeNode right = buildTree(l, last, depth + 1, rbounds);
350
0
    m_tree[node.index.children.right] = right;
351
352
0
    return node;
353
0
}
354
355
void SegmentTree::intersectLines(const QLineF &a, const QLineF &b, QDataBuffer<QIntersection> &intersections)
356
0
{
357
0
    const QPointF p1 = a.p1();
358
0
    const QPointF p2 = a.p2();
359
360
0
    const QPointF q1 = b.p1();
361
0
    const QPointF q2 = b.p2();
362
363
0
    if (comparePoints(p1, p2) || comparePoints(q1, q2))
364
0
        return;
365
366
0
    const bool p1_equals_q1 = comparePoints(p1, q1);
367
0
    const bool p2_equals_q2 = comparePoints(p2, q2);
368
369
0
    if (p1_equals_q1 && p2_equals_q2)
370
0
        return;
371
372
0
    const bool p1_equals_q2 = comparePoints(p1, q2);
373
0
    const bool p2_equals_q1 = comparePoints(p2, q1);
374
375
0
    if (p1_equals_q2 && p2_equals_q1)
376
0
        return;
377
378
0
    const QPointF pDelta = p2 - p1;
379
0
    const QPointF qDelta = q2 - q1;
380
381
0
    const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x();
382
383
0
    if (qFuzzyIsNull(par)) {
384
0
        const QPointF normal(-pDelta.y(), pDelta.x());
385
386
        // coinciding?
387
0
        if (qFuzzyIsNull(dot(normal, q1 - p1))) {
388
0
            const qreal invDp = 1 / dot(pDelta, pDelta);
389
390
0
            const qreal tq1 = dot(pDelta, q1 - p1) * invDp;
391
0
            const qreal tq2 = dot(pDelta, q2 - p1) * invDp;
392
393
0
            if (tq1 > 0 && tq1 < 1) {
394
0
                QIntersection intersection;
395
0
                intersection.alphaA = tq1;
396
0
                intersection.alphaB = 0;
397
0
                intersection.pos = q1;
398
0
                intersections.add(intersection);
399
0
            }
400
401
0
            if (tq2 > 0 && tq2 < 1) {
402
0
                QIntersection intersection;
403
0
                intersection.alphaA = tq2;
404
0
                intersection.alphaB = 1;
405
0
                intersection.pos = q2;
406
0
                intersections.add(intersection);
407
0
            }
408
409
0
            const qreal invDq = 1 / dot(qDelta, qDelta);
410
411
0
            const qreal tp1 = dot(qDelta, p1 - q1) * invDq;
412
0
            const qreal tp2 = dot(qDelta, p2 - q1) * invDq;
413
414
0
            if (tp1 > 0 && tp1 < 1) {
415
0
                QIntersection intersection;
416
0
                intersection.alphaA = 0;
417
0
                intersection.alphaB = tp1;
418
0
                intersection.pos = p1;
419
0
                intersections.add(intersection);
420
0
            }
421
422
0
            if (tp2 > 0 && tp2 < 1) {
423
0
                QIntersection intersection;
424
0
                intersection.alphaA = 1;
425
0
                intersection.alphaB = tp2;
426
0
                intersection.pos = p2;
427
0
                intersections.add(intersection);
428
0
            }
429
0
        }
430
431
0
        return;
432
0
    }
433
434
    // if the lines are not parallel and share a common end point, then they
435
    // don't intersect
436
0
    if (p1_equals_q1 || p1_equals_q2 || p2_equals_q1 || p2_equals_q2)
437
0
        return;
438
439
440
0
    const qreal tp = (qDelta.y() * (q1.x() - p1.x()) -
441
0
                      qDelta.x() * (q1.y() - p1.y())) / par;
442
0
    const qreal tq = (pDelta.y() * (q1.x() - p1.x()) -
443
0
                      pDelta.x() * (q1.y() - p1.y())) / par;
444
445
0
    if (tp<0 || tp>1 || tq<0 || tq>1)
446
0
        return;
447
448
0
    const bool p_zero = qFuzzyIsNull(tp);
449
0
    const bool p_one = qFuzzyIsNull(tp - 1);
450
451
0
    const bool q_zero = qFuzzyIsNull(tq);
452
0
    const bool q_one = qFuzzyIsNull(tq - 1);
453
454
0
    if ((q_zero || q_one) && (p_zero || p_one))
455
0
        return;
456
457
0
    QPointF pt;
458
0
    if (p_zero) {
459
0
        pt = p1;
460
0
    } else if (p_one) {
461
0
        pt = p2;
462
0
    } else if (q_zero) {
463
0
        pt = q1;
464
0
    } else if (q_one) {
465
0
        pt = q2;
466
0
    } else {
467
0
        pt = q1 + (q2 - q1) * tq;
468
0
    }
469
470
0
    QIntersection intersection;
471
0
    intersection.alphaA = tp;
472
0
    intersection.alphaB = tq;
473
0
    intersection.pos = pt;
474
0
    intersections.add(intersection);
475
0
}
476
477
void SegmentTree::produceIntersections(int segment)
478
0
{
479
0
    const QRectF &segmentBounds = m_segments.elementBounds(segment);
480
481
0
    RectF sbounds;
482
0
    sbounds.x1 = segmentBounds.left();
483
0
    sbounds.y1 = segmentBounds.top();
484
0
    sbounds.x2 = segmentBounds.right();
485
0
    sbounds.y2 = segmentBounds.bottom();
486
487
0
    produceIntersections(m_tree.at(0), segment, sbounds, m_bounds, 0);
488
0
}
489
490
void SegmentTree::produceIntersectionsLeaf(const TreeNode &node, int segment)
491
0
{
492
0
    const QRectF &r1 = m_segments.elementBounds(segment);
493
0
    const QLineF lineA = m_segments.lineAt(segment);
494
495
0
    for (int i = node.index.interval.first; i < node.index.interval.last; ++i) {
496
0
        const int other = m_index.at(i);
497
0
        if (other >= segment)
498
0
            continue;
499
500
0
        const QRectF &r2 = m_segments.elementBounds(other);
501
502
0
        if (r1.left() > r2.right() || r2.left() > r1.right())
503
0
            continue;
504
0
        if (r1.top() > r2.bottom() || r2.top() > r1.bottom())
505
0
            continue;
506
507
0
        m_intersections.reset();
508
509
0
        const QLineF lineB = m_segments.lineAt(other);
510
511
0
        intersectLines(lineA, lineB, m_intersections);
512
513
0
        for (int k = 0; k < m_intersections.size(); ++k) {
514
0
            QPathSegments::Intersection i_isect, j_isect;
515
0
            i_isect.t = m_intersections.at(k).alphaA;
516
0
            j_isect.t = m_intersections.at(k).alphaB;
517
518
0
            i_isect.vertex = j_isect.vertex = m_segments.addPoint(m_intersections.at(k).pos);
519
520
0
            i_isect.next = 0;
521
0
            j_isect.next = 0;
522
523
0
            m_segments.addIntersection(segment, i_isect);
524
0
            m_segments.addIntersection(other, j_isect);
525
0
        }
526
0
    }
527
0
}
528
529
void SegmentTree::produceIntersections(const TreeNode &node, int segment, const RectF &segmentBounds, const RectF &nodeBounds, int axis)
530
0
{
531
0
    if (node.leaf) {
532
0
        produceIntersectionsLeaf(node, segment);
533
0
        return;
534
0
    }
535
536
0
    RectF lbounds = nodeBounds;
537
0
    (&lbounds.x2)[axis] = node.splitLeft;
538
539
0
    RectF rbounds = nodeBounds;
540
0
    (&rbounds.x1)[axis] = node.splitRight;
541
542
0
    if (segment > node.lowestLeftIndex && (&segmentBounds.x1)[axis] <= node.splitLeft)
543
0
        produceIntersections(m_tree.at(node.index.children.left), segment, segmentBounds, lbounds, !axis);
544
545
0
    if (segment > node.lowestRightIndex && (&segmentBounds.x2)[axis] >= node.splitRight)
546
0
        produceIntersections(m_tree.at(node.index.children.right), segment, segmentBounds, rbounds, !axis);
547
0
}
548
549
}
550
551
void QIntersectionFinder::produceIntersections(QPathSegments &segments)
552
0
{
553
0
    SegmentTree tree(segments);
554
555
0
    for (int i = 0; i < segments.segments(); ++i)
556
0
        tree.produceIntersections(i);
557
0
}
558
559
class QKdPointTree
560
{
561
public:
562
    enum Traversal {
563
        TraverseBoth,
564
        TraverseLeft,
565
        TraverseRight,
566
        TraverseNone
567
    };
568
569
    struct Node {
570
        int point;
571
        int id;
572
573
        Node *left;
574
        Node *right;
575
    };
576
577
    QKdPointTree(const QPathSegments &segments)
578
0
        : m_segments(&segments)
579
0
        , m_nodes(m_segments->points())
580
0
        , m_id(0)
581
0
    {
582
0
        m_nodes.resize(m_segments->points());
583
584
0
        for (int i = 0; i < m_nodes.size(); ++i) {
585
0
            m_nodes.at(i).point = i;
586
0
            m_nodes.at(i).id = -1;
587
0
        }
588
589
0
        m_rootNode = build(0, m_nodes.size());
590
0
    }
591
592
    int build(int begin, int end, int depth = 0);
593
594
    Node *rootNode()
595
0
    {
596
0
        return &m_nodes.at(m_rootNode);
597
0
    }
598
599
    inline int nextId()
600
0
    {
601
0
        return m_id++;
602
0
    }
603
604
private:
605
    const QPathSegments *m_segments;
606
    QDataBuffer<Node> m_nodes;
607
608
    int m_rootNode;
609
    int m_id;
610
};
611
612
template <typename T>
613
void qTraverseKdPointTree(QKdPointTree::Node &node, T &t, int depth = 0)
614
0
{
615
0
    QKdPointTree::Traversal status = t(node, depth);
616
617
0
    const bool traverseRight = (status == QKdPointTree::TraverseBoth || status == QKdPointTree::TraverseRight);
618
0
    const bool traverseLeft = (status == QKdPointTree::TraverseBoth || status == QKdPointTree::TraverseLeft);
619
620
0
    if (traverseLeft && node.left)
621
0
        QT_PREPEND_NAMESPACE(qTraverseKdPointTree<T>)(*node.left, t, depth + 1);
622
623
0
    if (traverseRight && node.right)
624
0
        QT_PREPEND_NAMESPACE(qTraverseKdPointTree<T>)(*node.right, t, depth + 1);
625
0
}
626
627
static inline qreal component(const QPointF &point, unsigned int i)
628
0
{
629
0
    Q_ASSERT(i < 2);
630
0
    const qreal components[] = { point.x(), point.y() };
631
0
    return components[i];
632
0
}
633
634
int QKdPointTree::build(int begin, int end, int depth)
635
0
{
636
0
    Q_ASSERT(end > begin);
637
638
0
    const qreal pivot = component(m_segments->pointAt(m_nodes.at(begin).point), depth & 1);
639
640
0
    int first = begin + 1;
641
0
    int last = end - 1;
642
643
0
    while (first <= last) {
644
0
        const qreal value = component(m_segments->pointAt(m_nodes.at(first).point), depth & 1);
645
646
0
        if (value < pivot)
647
0
            ++first;
648
0
        else {
649
0
            qSwap(m_nodes.at(first), m_nodes.at(last));
650
0
            --last;
651
0
        }
652
0
    }
653
654
0
    if (last != begin)
655
0
        qSwap(m_nodes.at(last), m_nodes.at(begin));
656
657
0
    if (last > begin)
658
0
        m_nodes.at(last).left = &m_nodes.at(build(begin, last, depth + 1));
659
0
    else
660
0
        m_nodes.at(last).left = nullptr;
661
662
0
    if (last + 1 < end)
663
0
        m_nodes.at(last).right = &m_nodes.at(build(last + 1, end, depth + 1));
664
0
    else
665
0
        m_nodes.at(last).right = nullptr;
666
667
0
    return last;
668
0
}
669
670
class QKdPointFinder
671
{
672
public:
673
    QKdPointFinder(int point, const QPathSegments &segments, QKdPointTree &tree)
674
0
        : m_result(-1)
675
0
        , m_segments(&segments)
676
0
        , m_tree(&tree)
677
0
    {
678
0
        pointComponents[0] = segments.pointAt(point).x();
679
0
        pointComponents[1] = segments.pointAt(point).y();
680
0
    }
681
682
    inline QKdPointTree::Traversal operator()(QKdPointTree::Node &node, int depth)
683
0
    {
684
0
        if (m_result != -1)
685
0
            return QKdPointTree::TraverseNone;
686
687
0
        const QPointF &nodePoint = m_segments->pointAt(node.point);
688
689
0
        const qreal pivotComponents[] = { nodePoint.x(), nodePoint.y() };
690
691
0
        const qreal pivot = pivotComponents[depth & 1];
692
0
        const qreal value = pointComponents[depth & 1];
693
694
0
        if (fuzzyIsNull(pivot - value)) {
695
0
            const qreal pivot2 = pivotComponents[(depth + 1) & 1];
696
0
            const qreal value2 = pointComponents[(depth + 1) & 1];
697
698
0
            if (fuzzyIsNull(pivot2 - value2)) {
699
0
                if (node.id < 0)
700
0
                    node.id = m_tree->nextId();
701
702
0
                m_result = node.id;
703
0
                return QKdPointTree::TraverseNone;
704
0
            } else
705
0
                return QKdPointTree::TraverseBoth;
706
0
        } else if (value < pivot) {
707
0
            return QKdPointTree::TraverseLeft;
708
0
        } else {
709
0
            return QKdPointTree::TraverseRight;
710
0
        }
711
0
    }
712
713
    int result() const
714
0
    {
715
0
        return m_result;
716
0
    }
717
718
private:
719
    qreal pointComponents[2];
720
    int m_result;
721
    const QPathSegments *m_segments;
722
    QKdPointTree *m_tree;
723
};
724
725
// merge all points that are within qFuzzyCompare range of each other
726
void QPathSegments::mergePoints()
727
0
{
728
0
    QKdPointTree tree(*this);
729
730
0
    if (tree.rootNode()) {
731
0
        QDataBuffer<QPointF> mergedPoints(points());
732
0
        QDataBuffer<int> pointIndices(points());
733
734
0
        for (int i = 0; i < points(); ++i) {
735
0
            QKdPointFinder finder(i, *this, tree);
736
0
            QT_PREPEND_NAMESPACE(qTraverseKdPointTree<QKdPointFinder>)(*tree.rootNode(), finder);
737
738
0
            Q_ASSERT(finder.result() != -1);
739
740
0
            if (finder.result() >= mergedPoints.size())
741
0
                mergedPoints << m_points.at(i);
742
743
0
            pointIndices << finder.result();
744
0
        }
745
746
0
        for (int i = 0; i < m_segments.size(); ++i) {
747
0
            m_segments.at(i).va = pointIndices.at(m_segments.at(i).va);
748
0
            m_segments.at(i).vb = pointIndices.at(m_segments.at(i).vb);
749
0
        }
750
751
0
        for (int i = 0; i < m_intersections.size(); ++i)
752
0
            m_intersections.at(i).vertex = pointIndices.at(m_intersections.at(i).vertex);
753
754
0
        m_points.swap(mergedPoints);
755
0
    }
756
0
}
757
758
void QWingedEdge::intersectAndAdd()
759
0
{
760
0
    QIntersectionFinder finder;
761
0
    finder.produceIntersections(m_segments);
762
763
0
    m_segments.mergePoints();
764
765
0
    for (int i = 0; i < m_segments.points(); ++i)
766
0
        addVertex(m_segments.pointAt(i));
767
768
0
    QDataBuffer<QPathSegments::Intersection> intersections(m_segments.segments());
769
0
    for (int i = 0; i < m_segments.segments(); ++i) {
770
0
        intersections.reset();
771
772
0
        int pathId = m_segments.pathId(i);
773
774
0
        const QPathSegments::Intersection *isect = m_segments.intersectionAt(i);
775
0
        while (isect) {
776
0
            intersections << *isect;
777
778
0
            if (isect->next) {
779
0
                isect += isect->next;
780
0
            } else {
781
0
                isect = nullptr;
782
0
            }
783
0
        }
784
785
0
        std::sort(intersections.data(), intersections.data() + intersections.size());
786
787
0
        int first = m_segments.segmentAt(i).va;
788
0
        int second = m_segments.segmentAt(i).vb;
789
790
0
        int last = first;
791
0
        for (int j = 0; j < intersections.size(); ++j) {
792
0
            const QPathSegments::Intersection &isect = intersections.at(j);
793
794
0
            QPathEdge *ep = edge(addEdge(last, isect.vertex));
795
796
0
            if (ep) {
797
0
                const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(isect.vertex).y() ? 1 : -1;
798
0
                if (pathId == 0)
799
0
                    ep->windingA += dir;
800
0
                else
801
0
                    ep->windingB += dir;
802
0
            }
803
804
0
            last = isect.vertex;
805
0
        }
806
807
0
        QPathEdge *ep = edge(addEdge(last, second));
808
809
0
        if (ep) {
810
0
            const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(second).y() ? 1 : -1;
811
0
            if (pathId == 0)
812
0
                ep->windingA += dir;
813
0
            else
814
0
                ep->windingB += dir;
815
0
        }
816
0
    }
817
0
}
818
819
QWingedEdge::QWingedEdge() :
820
0
    m_edges(0),
821
0
    m_vertices(0),
822
0
    m_segments(0)
823
0
{
824
0
}
825
826
QWingedEdge::QWingedEdge(const QPainterPath &subject, const QPainterPath &clip) :
827
0
    m_edges(subject.elementCount()),
828
0
    m_vertices(subject.elementCount()),
829
0
    m_segments(subject.elementCount())
830
0
{
831
0
    m_segments.setPath(subject);
832
0
    m_segments.addPath(clip);
833
834
0
    intersectAndAdd();
835
0
}
836
837
QWingedEdge::TraversalStatus QWingedEdge::next(const QWingedEdge::TraversalStatus &status) const
838
0
{
839
0
    const QPathEdge *sp = edge(status.edge);
840
0
    Q_ASSERT(sp);
841
842
0
    TraversalStatus result;
843
0
    result.edge = sp->next(status.traversal, status.direction);
844
0
    result.traversal = status.traversal;
845
0
    result.direction = status.direction;
846
847
0
    const QPathEdge *rp = edge(result.edge);
848
0
    Q_ASSERT(rp);
849
850
0
    if (sp->vertex(status.direction) == rp->vertex(status.direction))
851
0
        result.flip();
852
853
0
    return result;
854
0
}
855
856
static bool isLine(const QBezier &bezier)
857
0
{
858
0
    const bool equal_1_2 = comparePoints(bezier.pt1(), bezier.pt2());
859
0
    const bool equal_2_3 = comparePoints(bezier.pt2(), bezier.pt3());
860
0
    const bool equal_3_4 = comparePoints(bezier.pt3(), bezier.pt4());
861
862
    // point?
863
0
    if (equal_1_2 && equal_2_3 && equal_3_4)
864
0
        return true;
865
866
0
    if (comparePoints(bezier.pt1(), bezier.pt4()))
867
0
        return equal_1_2 || equal_3_4;
868
869
0
    return (equal_1_2 && equal_3_4) || (equal_1_2 && equal_2_3) || (equal_2_3 && equal_3_4);
870
0
}
871
872
void QPathSegments::setPath(const QPainterPath &path)
873
0
{
874
0
    m_points.reset();
875
0
    m_intersections.reset();
876
0
    m_segments.reset();
877
878
0
    m_pathId = 0;
879
880
0
    addPath(path);
881
0
}
882
883
void QPathSegments::addPath(const QPainterPath &path)
884
0
{
885
0
    int firstSegment = m_segments.size();
886
887
0
    bool hasMoveTo = false;
888
0
    int lastMoveTo = 0;
889
0
    int last = 0;
890
0
    for (int i = 0; i < path.elementCount(); ++i) {
891
0
        int current = m_points.size();
892
893
0
        QPointF currentPoint;
894
0
        if (path.elementAt(i).type == QPainterPath::CurveToElement)
895
0
            currentPoint = path.elementAt(i+2);
896
0
        else
897
0
            currentPoint = path.elementAt(i);
898
899
0
        if (i > 0 && comparePoints(m_points.at(lastMoveTo), currentPoint))
900
0
            current = lastMoveTo;
901
0
        else
902
0
            m_points << currentPoint;
903
904
0
        switch (path.elementAt(i).type) {
905
0
        case QPainterPath::MoveToElement:
906
0
            if (hasMoveTo && last != lastMoveTo && !comparePoints(m_points.at(last), m_points.at(lastMoveTo)))
907
0
                m_segments << Segment(m_pathId, last, lastMoveTo);
908
0
            hasMoveTo = true;
909
0
            last = lastMoveTo = current;
910
0
            break;
911
0
        case QPainterPath::LineToElement:
912
0
            m_segments << Segment(m_pathId, last, current);
913
0
            last = current;
914
0
            break;
915
0
        case QPainterPath::CurveToElement:
916
0
            {
917
0
                QBezier bezier = QBezier::fromPoints(m_points.at(last), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2));
918
0
                if (isLine(bezier)) {
919
0
                    m_segments << Segment(m_pathId, last, current);
920
0
                } else {
921
0
                    QRectF bounds = bezier.bounds();
922
923
                    // threshold based on similar algorithm as in qtriangulatingstroker.cpp
924
0
                    int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * (2 * qreal(3.14) / 6));
925
926
0
                    if (threshold < 3) threshold = 3;
927
0
                    qreal one_over_threshold_minus_1 = qreal(1) / (threshold - 1);
928
929
0
                    for (int t = 1; t < threshold - 1; ++t) {
930
0
                        currentPoint = bezier.pointAt(t * one_over_threshold_minus_1);
931
932
0
                        int index = m_points.size();
933
0
                        m_segments << Segment(m_pathId, last, index);
934
0
                        last = index;
935
936
0
                        m_points << currentPoint;
937
0
                    }
938
939
0
                    m_segments << Segment(m_pathId, last, current);
940
0
                }
941
0
            }
942
0
            last = current;
943
0
            i += 2;
944
0
            break;
945
0
        default:
946
0
            Q_ASSERT(false);
947
0
            break;
948
0
        }
949
0
    }
950
951
0
    if (hasMoveTo && last != lastMoveTo && !comparePoints(m_points.at(last), m_points.at(lastMoveTo)))
952
0
        m_segments << Segment(m_pathId, last, lastMoveTo);
953
954
0
    for (int i = firstSegment; i < m_segments.size(); ++i) {
955
0
        const QLineF line = lineAt(i);
956
957
0
        qreal x1 = line.p1().x();
958
0
        qreal y1 = line.p1().y();
959
0
        qreal x2 = line.p2().x();
960
0
        qreal y2 = line.p2().y();
961
962
0
        if (x2 < x1)
963
0
            qSwap(x1, x2);
964
0
        if (y2 < y1)
965
0
            qSwap(y1, y2);
966
967
0
        m_segments.at(i).bounds = QRectF(x1, y1, x2 - x1, y2 - y1);
968
0
    }
969
970
0
    ++m_pathId;
971
0
}
972
973
qreal QWingedEdge::delta(int vertex, int a, int b) const
974
0
{
975
0
    const QPathEdge *ap = edge(a);
976
0
    const QPathEdge *bp = edge(b);
977
978
0
    double a_angle = ap->angle;
979
0
    double b_angle = bp->angle;
980
981
0
    if (vertex == ap->second)
982
0
        a_angle = ap->invAngle;
983
984
0
    if (vertex == bp->second)
985
0
        b_angle = bp->invAngle;
986
987
0
    double result = b_angle - a_angle;
988
989
0
    if (result >= 128.)
990
0
        return result - 128.;
991
0
    else if (result < 0)
992
0
        return result + 128.;
993
0
    else
994
0
        return result;
995
0
}
996
997
QWingedEdge::TraversalStatus QWingedEdge::findInsertStatus(int vi, int ei) const
998
0
{
999
0
    const QPathVertex *vp = vertex(vi);
1000
1001
0
    Q_ASSERT(vp);
1002
0
    Q_ASSERT(ei >= 0);
1003
0
    Q_ASSERT(vp->edge >= 0);
1004
1005
0
    int position = vp->edge;
1006
0
    qreal d = 128.;
1007
1008
0
    TraversalStatus status;
1009
0
    status.direction = edge(vp->edge)->directionTo(vi);
1010
0
    status.traversal = QPathEdge::RightTraversal;
1011
0
    status.edge = vp->edge;
1012
1013
#ifdef QDEBUG_CLIPPER
1014
    const QPathEdge *ep = edge(ei);
1015
    qDebug() << "Finding insert status for edge" << ei << "at vertex" << QPointF(*vp) << ", angles: " << ep->angle << ep->invAngle;
1016
#endif
1017
1018
0
    do {
1019
0
        status = next(status);
1020
0
        status.flip();
1021
1022
0
        Q_ASSERT(edge(status.edge)->vertex(status.direction) == vi);
1023
0
        qreal d2 = delta(vi, ei, status.edge);
1024
1025
#ifdef QDEBUG_CLIPPER
1026
        const QPathEdge *op = edge(status.edge);
1027
        qDebug() << "Delta to edge" << status.edge << d2 << ", angles: " << op->angle << op->invAngle;
1028
#endif
1029
1030
0
        if (d2 < d) {
1031
0
            position = status.edge;
1032
0
            d = d2;
1033
0
        }
1034
0
    } while (status.edge != vp->edge);
1035
1036
0
    status.traversal = QPathEdge::LeftTraversal;
1037
0
    status.direction = QPathEdge::Forward;
1038
0
    status.edge = position;
1039
1040
0
    if (edge(status.edge)->vertex(status.direction) != vi)
1041
0
        status.flip();
1042
1043
#ifdef QDEBUG_CLIPPER
1044
    qDebug() << "Inserting edge" << ei << "to" << (status.traversal == QPathEdge::LeftTraversal ? "left" : "right") << "of edge" << status.edge;
1045
#endif
1046
1047
0
    Q_ASSERT(edge(status.edge)->vertex(status.direction) == vi);
1048
1049
0
    return status;
1050
0
}
1051
1052
void QWingedEdge::removeEdge(int ei)
1053
0
{
1054
0
    QPathEdge *ep = edge(ei);
1055
1056
0
    TraversalStatus status;
1057
0
    status.direction = QPathEdge::Forward;
1058
0
    status.traversal = QPathEdge::RightTraversal;
1059
0
    status.edge = ei;
1060
1061
0
    TraversalStatus forwardRight = next(status);
1062
0
    forwardRight.flipDirection();
1063
1064
0
    status.traversal = QPathEdge::LeftTraversal;
1065
0
    TraversalStatus forwardLeft = next(status);
1066
0
    forwardLeft.flipDirection();
1067
1068
0
    status.direction = QPathEdge::Backward;
1069
0
    TraversalStatus backwardLeft = next(status);
1070
0
    backwardLeft.flipDirection();
1071
1072
0
    status.traversal = QPathEdge::RightTraversal;
1073
0
    TraversalStatus backwardRight = next(status);
1074
0
    backwardRight.flipDirection();
1075
1076
0
    edge(forwardRight.edge)->setNext(forwardRight.traversal, forwardRight.direction, forwardLeft.edge);
1077
0
    edge(forwardLeft.edge)->setNext(forwardLeft.traversal, forwardLeft.direction, forwardRight.edge);
1078
1079
0
    edge(backwardRight.edge)->setNext(backwardRight.traversal, backwardRight.direction, backwardLeft.edge);
1080
0
    edge(backwardLeft.edge)->setNext(backwardLeft.traversal, backwardLeft.direction, backwardRight.edge);
1081
1082
0
    ep->setNext(QPathEdge::Forward, ei);
1083
0
    ep->setNext(QPathEdge::Backward, ei);
1084
1085
0
    QPathVertex *a = vertex(ep->first);
1086
0
    QPathVertex *b = vertex(ep->second);
1087
1088
0
    a->edge = backwardRight.edge;
1089
0
    b->edge = forwardRight.edge;
1090
0
}
1091
1092
static int commonEdge(const QWingedEdge &list, int a, int b)
1093
0
{
1094
0
    const QPathVertex *ap = list.vertex(a);
1095
0
    Q_ASSERT(ap);
1096
1097
0
    const QPathVertex *bp = list.vertex(b);
1098
0
    Q_ASSERT(bp);
1099
1100
0
    if (ap->edge < 0 || bp->edge < 0)
1101
0
        return -1;
1102
1103
0
    QWingedEdge::TraversalStatus status;
1104
0
    status.edge = ap->edge;
1105
0
    status.direction = list.edge(status.edge)->directionTo(a);
1106
0
    status.traversal = QPathEdge::RightTraversal;
1107
1108
0
    do {
1109
0
        const QPathEdge *ep = list.edge(status.edge);
1110
1111
0
        if ((ep->first == a && ep->second == b)
1112
0
            || (ep->first == b && ep->second == a))
1113
0
            return status.edge;
1114
1115
0
        status = list.next(status);
1116
0
        status.flip();
1117
0
    } while (status.edge != ap->edge);
1118
1119
0
    return -1;
1120
0
}
1121
1122
static double computeAngle(const QPointF &v)
1123
0
{
1124
0
#if 1
1125
0
    if (v.x() == 0) {
1126
0
        return v.y() <= 0 ? 0 : 64.;
1127
0
    } else if (v.y() == 0) {
1128
0
        return v.x() <= 0 ? 32. : 96.;
1129
0
    }
1130
1131
0
    double vx = v.x();
1132
0
    double vy = v.y();
1133
0
    normalize(vx, vy);
1134
0
    if (vy < 0) {
1135
0
        if (vx < 0) { // 0 - 32
1136
0
            return -32. * vx;
1137
0
        } else { // 96 - 128
1138
0
            return 128. - 32. * vx;
1139
0
        }
1140
0
    } else { // 32 - 96
1141
0
        return 64. + 32. * vx;
1142
0
    }
1143
#else
1144
    // doesn't seem to be robust enough
1145
    return qAtan2(v.x(), v.y()) + Q_PI;
1146
#endif
1147
0
}
1148
1149
int QWingedEdge::addEdge(const QPointF &a, const QPointF &b)
1150
0
{
1151
0
    int fi = insert(a);
1152
0
    int si = insert(b);
1153
1154
0
    return addEdge(fi, si);
1155
0
}
1156
1157
int QWingedEdge::addEdge(int fi, int si)
1158
0
{
1159
0
    if (fi == si)
1160
0
        return -1;
1161
1162
0
    int common = commonEdge(*this, fi, si);
1163
0
    if (common >= 0)
1164
0
        return common;
1165
1166
0
    m_edges << QPathEdge(fi, si);
1167
1168
0
    int ei = m_edges.size() - 1;
1169
1170
0
    QPathVertex *fp = vertex(fi);
1171
0
    QPathVertex *sp = vertex(si);
1172
1173
0
    QPathEdge *ep = edge(ei);
1174
1175
0
    const QPointF tangent = QPointF(*sp) - QPointF(*fp);
1176
0
    ep->angle = computeAngle(tangent);
1177
0
    ep->invAngle = ep->angle + 64;
1178
0
    if (ep->invAngle >= 128)
1179
0
        ep->invAngle -= 128;
1180
1181
0
    QPathVertex *vertices[2] = { fp, sp };
1182
0
    QPathEdge::Direction dirs[2] = { QPathEdge::Backward, QPathEdge::Forward };
1183
1184
#ifdef QDEBUG_CLIPPER
1185
    printf("** Adding edge %d / vertices: %.07f %.07f, %.07f %.07f\n", ei, fp->x, fp->y, sp->x, sp->y);
1186
#endif
1187
1188
0
    for (int i = 0; i < 2; ++i) {
1189
0
        QPathVertex *vp = vertices[i];
1190
0
        if (vp->edge < 0) {
1191
0
            vp->edge = ei;
1192
0
            ep->setNext(dirs[i], ei);
1193
0
        } else {
1194
0
            int vi = ep->vertex(dirs[i]);
1195
0
            Q_ASSERT(vertex(vi) == vertices[i]);
1196
1197
0
            TraversalStatus os = findInsertStatus(vi, ei);
1198
0
            QPathEdge *op = edge(os.edge);
1199
1200
0
            Q_ASSERT(vertex(op->vertex(os.direction)) == vertices[i]);
1201
1202
0
            TraversalStatus ns = next(os);
1203
0
            ns.flipDirection();
1204
0
            QPathEdge *np = edge(ns.edge);
1205
1206
0
            op->setNext(os.traversal, os.direction, ei);
1207
0
            np->setNext(ns.traversal, ns.direction, ei);
1208
1209
0
            int oe = os.edge;
1210
0
            int ne = ns.edge;
1211
1212
0
            os = next(os);
1213
0
            ns = next(ns);
1214
1215
0
            os.flipDirection();
1216
0
            ns.flipDirection();
1217
1218
0
            Q_ASSERT(os.edge == ei);
1219
0
            Q_ASSERT(ns.edge == ei);
1220
1221
0
            ep->setNext(os.traversal, os.direction, oe);
1222
0
            ep->setNext(ns.traversal, ns.direction, ne);
1223
0
        }
1224
0
    }
1225
1226
0
    Q_ASSERT(ep->next(QPathEdge::RightTraversal, QPathEdge::Forward) >= 0);
1227
0
    Q_ASSERT(ep->next(QPathEdge::RightTraversal, QPathEdge::Backward) >= 0);
1228
0
    Q_ASSERT(ep->next(QPathEdge::LeftTraversal, QPathEdge::Forward) >= 0);
1229
0
    Q_ASSERT(ep->next(QPathEdge::LeftTraversal, QPathEdge::Backward) >= 0);
1230
1231
0
    return ei;
1232
0
}
1233
1234
int QWingedEdge::insert(const QPathVertex &vertex)
1235
0
{
1236
0
    if (!m_vertices.isEmpty()) {
1237
0
        const QPathVertex &last = m_vertices.last();
1238
0
        if (vertex.x == last.x && vertex.y == last.y)
1239
0
            return m_vertices.size() - 1;
1240
1241
0
        for (int i = 0; i < m_vertices.size(); ++i) {
1242
0
            const QPathVertex &v = m_vertices.at(i);
1243
0
            if (qFuzzyCompare(v.x, vertex.x) && qFuzzyCompare(v.y, vertex.y)) {
1244
0
                return i;
1245
0
            }
1246
0
        }
1247
0
    }
1248
1249
0
    m_vertices << vertex;
1250
0
    return m_vertices.size() - 1;
1251
0
}
1252
1253
static void addLineTo(QPainterPath &path, const QPointF &point)
1254
0
{
1255
0
    const int elementCount = path.elementCount();
1256
0
    if (elementCount >= 2) {
1257
0
        const QPainterPath::Element &middle = path.elementAt(elementCount - 1);
1258
0
        if (middle.type == QPainterPath::LineToElement) {
1259
0
            const QPointF first = path.elementAt(elementCount - 2);
1260
0
            const QPointF d1 = point - first;
1261
0
            const QPointF d2 = middle - first;
1262
1263
0
            const QPointF p(-d1.y(), d1.x());
1264
1265
0
            if (qFuzzyIsNull(dot(p, d2))) {
1266
0
                path.setElementPositionAt(elementCount - 1, point.x(), point.y());
1267
0
                return;
1268
0
            }
1269
0
        }
1270
0
    }
1271
1272
0
    path.lineTo(point);
1273
0
}
1274
1275
static void add(QPainterPath &path, const QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
1276
0
{
1277
0
    QWingedEdge::TraversalStatus status;
1278
0
    status.edge = edge;
1279
0
    status.traversal = traversal;
1280
0
    status.direction = QPathEdge::Forward;
1281
1282
0
    path.moveTo(*list.vertex(list.edge(edge)->first));
1283
1284
0
    do {
1285
0
        const QPathEdge *ep = list.edge(status.edge);
1286
1287
0
        addLineTo(path, *list.vertex(ep->vertex(status.direction)));
1288
1289
0
        if (status.traversal == QPathEdge::LeftTraversal)
1290
0
            ep->flag &= ~16;
1291
0
        else
1292
0
            ep->flag &= ~32;
1293
1294
0
        status = list.next(status);
1295
0
    } while (status.edge != edge);
1296
0
}
1297
1298
void QWingedEdge::simplify()
1299
0
{
1300
0
    for (int i = 0; i < edgeCount(); ++i) {
1301
0
        const QPathEdge *ep = edge(i);
1302
1303
        // if both sides are part of the inside then we can collapse the edge
1304
0
        int flag = 0x3 << 4;
1305
0
        if ((ep->flag & flag) == flag) {
1306
0
            removeEdge(i);
1307
1308
0
            ep->flag &= ~flag;
1309
0
        }
1310
0
    }
1311
0
}
1312
1313
QPainterPath QWingedEdge::toPath() const
1314
0
{
1315
0
    QPainterPath path;
1316
1317
0
    for (int i = 0; i < edgeCount(); ++i) {
1318
0
        const QPathEdge *ep = edge(i);
1319
1320
0
        if (ep->flag & 16) {
1321
0
            add(path, *this, i, QPathEdge::LeftTraversal);
1322
0
        }
1323
1324
0
        if (ep->flag & 32)
1325
0
            add(path, *this, i, QPathEdge::RightTraversal);
1326
0
    }
1327
1328
0
    return path;
1329
0
}
1330
1331
bool QPathClipper::intersect()
1332
0
{
1333
0
    if (subjectPath == clipPath)
1334
0
        return true;
1335
1336
0
    QRectF r1 = subjectPath.controlPointRect();
1337
0
    QRectF r2 = clipPath.controlPointRect();
1338
0
    if (qMax(r1.x(), r2.x()) > qMin(r1.x() + r1.width(), r2.x() + r2.width()) ||
1339
0
        qMax(r1.y(), r2.y()) > qMin(r1.y() + r1.height(), r2.y() + r2.height())) {
1340
        // no way we could intersect
1341
0
        return false;
1342
0
    }
1343
1344
0
    bool subjectIsRect = pathToRect(subjectPath);
1345
0
    bool clipIsRect = pathToRect(clipPath);
1346
1347
0
    if (subjectIsRect && clipIsRect)
1348
0
        return true;
1349
0
    else if (subjectIsRect)
1350
0
        return clipPath.intersects(r1);
1351
0
    else if (clipIsRect)
1352
0
        return subjectPath.intersects(r2);
1353
1354
0
    QPathSegments a(subjectPath.elementCount());
1355
0
    a.setPath(subjectPath);
1356
0
    QPathSegments b(clipPath.elementCount());
1357
0
    b.setPath(clipPath);
1358
1359
0
    QIntersectionFinder finder;
1360
0
    if (finder.hasIntersections(a, b))
1361
0
        return true;
1362
1363
0
    for (int i = 0; i < clipPath.elementCount(); ++i) {
1364
0
        if (clipPath.elementAt(i).type == QPainterPath::MoveToElement) {
1365
0
            const QPointF point = clipPath.elementAt(i);
1366
0
            if (r1.contains(point) && subjectPath.contains(point))
1367
0
                return true;
1368
0
        }
1369
0
    }
1370
1371
0
    for (int i = 0; i < subjectPath.elementCount(); ++i) {
1372
0
        if (subjectPath.elementAt(i).type == QPainterPath::MoveToElement) {
1373
0
            const QPointF point = subjectPath.elementAt(i);
1374
0
            if (r2.contains(point) && clipPath.contains(point))
1375
0
                return true;
1376
0
        }
1377
0
    }
1378
1379
0
    return false;
1380
0
}
1381
1382
bool QPathClipper::contains()
1383
0
{
1384
0
    if (subjectPath == clipPath)
1385
0
        return false;
1386
1387
0
    QRectF r1 = subjectPath.controlPointRect();
1388
0
    QRectF r2 = clipPath.controlPointRect();
1389
0
    if (qMax(r1.x(), r2.x()) > qMin(r1.x() + r1.width(), r2.x() + r2.width()) ||
1390
0
        qMax(r1.y(), r2.y()) > qMin(r1.y() + r1.height(), r2.y() + r2.height())) {
1391
        // no intersection -> not contained
1392
0
        return false;
1393
0
    }
1394
1395
0
    bool clipIsRect = pathToRect(clipPath);
1396
0
    if (clipIsRect)
1397
0
        return subjectPath.contains(r2);
1398
1399
0
    QPathSegments a(subjectPath.elementCount());
1400
0
    a.setPath(subjectPath);
1401
0
    QPathSegments b(clipPath.elementCount());
1402
0
    b.setPath(clipPath);
1403
1404
0
    QIntersectionFinder finder;
1405
0
    if (finder.hasIntersections(a, b))
1406
0
        return false;
1407
1408
0
    for (int i = 0; i < clipPath.elementCount(); ++i) {
1409
0
        if (clipPath.elementAt(i).type == QPainterPath::MoveToElement) {
1410
0
            const QPointF point = clipPath.elementAt(i);
1411
0
            if (!r1.contains(point) || !subjectPath.contains(point))
1412
0
                return false;
1413
0
        }
1414
0
    }
1415
1416
0
    return true;
1417
0
}
1418
1419
QPathClipper::QPathClipper(const QPainterPath &subject,
1420
                           const QPainterPath &clip)
1421
12.0k
    : subjectPath(subject)
1422
12.0k
    , clipPath(clip)
1423
12.0k
{
1424
12.0k
    aMask = subjectPath.fillRule() == Qt::WindingFill ? ~0x0 : 0x1;
1425
12.0k
    bMask = clipPath.fillRule() == Qt::WindingFill ? ~0x0 : 0x1;
1426
12.0k
}
1427
1428
static void clear(QWingedEdge& list, int edge, QPathEdge::Traversal traversal)
1429
0
{
1430
0
    QWingedEdge::TraversalStatus status;
1431
0
    status.edge = edge;
1432
0
    status.traversal = traversal;
1433
0
    status.direction = QPathEdge::Forward;
1434
1435
0
    do {
1436
0
        if (status.traversal == QPathEdge::LeftTraversal)
1437
0
            list.edge(status.edge)->flag |= 1;
1438
0
        else
1439
0
            list.edge(status.edge)->flag |= 2;
1440
1441
0
        status = list.next(status);
1442
0
    } while (status.edge != edge);
1443
0
}
1444
1445
template <typename InputIterator>
1446
InputIterator qFuzzyFind(InputIterator first, InputIterator last, qreal val)
1447
0
{
1448
0
    while (first != last && !QT_PREPEND_NAMESPACE(qFuzzyCompare)(qreal(*first), qreal(val)))
1449
0
        ++first;
1450
0
    return first;
1451
0
}
1452
1453
static bool fuzzyCompare(qreal a, qreal b)
1454
0
{
1455
0
    return qFuzzyCompare(a, b);
1456
0
}
1457
1458
bool QPathClipper::pathToRect(const QPainterPath &path, QRectF *rect)
1459
24.1k
{
1460
24.1k
    if (path.elementCount() != 5)
1461
10.1k
        return false;
1462
1463
14.0k
    const bool mightBeRect = path.elementAt(0).isMoveTo()
1464
14.0k
        && path.elementAt(1).isLineTo()
1465
14.0k
        && path.elementAt(2).isLineTo()
1466
14.0k
        && path.elementAt(3).isLineTo()
1467
14.0k
        && path.elementAt(4).isLineTo();
1468
1469
14.0k
    if (!mightBeRect)
1470
0
        return false;
1471
1472
14.0k
    const qreal x1 = path.elementAt(0).x;
1473
14.0k
    const qreal y1 = path.elementAt(0).y;
1474
1475
14.0k
    const qreal x2 = path.elementAt(1).x;
1476
14.0k
    const qreal y2 = path.elementAt(2).y;
1477
1478
14.0k
    if (path.elementAt(1).y != y1)
1479
634
        return false;
1480
1481
13.3k
    if (path.elementAt(2).x != x2)
1482
189
        return false;
1483
1484
13.1k
    if (path.elementAt(3).x != x1 || path.elementAt(3).y != y2)
1485
187
        return false;
1486
1487
13.0k
    if (path.elementAt(4).x != x1 || path.elementAt(4).y != y1)
1488
0
        return false;
1489
1490
13.0k
    if (rect)
1491
0
        rect->setCoords(x1, y1, x2, y2);
1492
1493
13.0k
    return true;
1494
13.0k
}
1495
1496
1497
QPainterPath QPathClipper::clip(Operation operation)
1498
12.0k
{
1499
12.0k
    op = operation;
1500
1501
12.0k
    if (op != Simplify) {
1502
12.0k
        if (subjectPath == clipPath)
1503
0
            return op == BoolSub ? QPainterPath() : subjectPath;
1504
1505
12.0k
        bool subjectIsRect = pathToRect(subjectPath, nullptr);
1506
12.0k
        bool clipIsRect = pathToRect(clipPath, nullptr);
1507
1508
12.0k
        const QRectF clipBounds = clipPath.boundingRect();
1509
12.0k
        const QRectF subjectBounds = subjectPath.boundingRect();
1510
1511
12.0k
        if (!clipBounds.intersects(subjectBounds)) {
1512
507
            switch (op) {
1513
0
            case BoolSub:
1514
0
                return subjectPath;
1515
507
            case BoolAnd:
1516
507
                return QPainterPath();
1517
0
            case BoolOr: {
1518
0
                QPainterPath result = subjectPath;
1519
0
                if (result.fillRule() == clipPath.fillRule()) {
1520
0
                    result.addPath(clipPath);
1521
0
                } else if (result.fillRule() == Qt::WindingFill) {
1522
0
                    result = result.simplified();
1523
0
                    result.addPath(clipPath);
1524
0
                } else {
1525
0
                    result.addPath(clipPath.simplified());
1526
0
                }
1527
0
                return result;
1528
0
             }
1529
0
            default:
1530
0
                break;
1531
507
            }
1532
507
        }
1533
1534
11.5k
        if (clipBounds.contains(subjectBounds)) {
1535
0
            if (clipIsRect) {
1536
0
                switch (op) {
1537
0
                case BoolSub:
1538
0
                    return QPainterPath();
1539
0
                case BoolAnd:
1540
0
                    return subjectPath;
1541
0
                case BoolOr:
1542
0
                    return clipPath;
1543
0
                default:
1544
0
                    break;
1545
0
                }
1546
0
            }
1547
11.5k
        } else if (subjectBounds.contains(clipBounds)) {
1548
3.27k
            if (subjectIsRect) {
1549
318
                switch (op) {
1550
0
                case BoolSub:
1551
0
                    if (clipPath.fillRule() == Qt::OddEvenFill) {
1552
0
                        QPainterPath result = clipPath;
1553
0
                        result.addRect(subjectBounds);
1554
0
                        return result;
1555
0
                    } else {
1556
0
                        QPainterPath result = clipPath.simplified();
1557
0
                        result.addRect(subjectBounds);
1558
0
                        return result;
1559
0
                    }
1560
318
                case BoolAnd:
1561
318
                    return clipPath;
1562
0
                case BoolOr:
1563
0
                    return subjectPath;
1564
0
                default:
1565
0
                    break;
1566
318
                }
1567
318
            }
1568
3.27k
        }
1569
1570
11.2k
        if (op == BoolAnd) {
1571
11.2k
            if (subjectIsRect)
1572
597
                return intersect(clipPath, subjectBounds);
1573
10.6k
            else if (clipIsRect)
1574
10.6k
                return intersect(subjectPath, clipBounds);
1575
11.2k
        }
1576
11.2k
    }
1577
1578
0
    QWingedEdge list(subjectPath, clipPath);
1579
1580
0
    doClip(list, ClipMode);
1581
1582
0
    QPainterPath path = list.toPath();
1583
0
    return path;
1584
12.0k
}
1585
1586
bool QPathClipper::doClip(QWingedEdge &list, ClipperMode mode)
1587
0
{
1588
0
    QList<qreal> y_coords;
1589
0
    y_coords.reserve(list.vertexCount());
1590
0
    for (int i = 0; i < list.vertexCount(); ++i)
1591
0
        y_coords << list.vertex(i)->y;
1592
1593
0
    std::sort(y_coords.begin(), y_coords.end());
1594
0
    y_coords.erase(std::unique(y_coords.begin(), y_coords.end(), fuzzyCompare), y_coords.end());
1595
1596
#ifdef QDEBUG_CLIPPER
1597
    printf("sorted y coords:\n");
1598
    for (int i = 0; i < y_coords.size(); ++i) {
1599
        printf("%.9f\n", y_coords.at(i));
1600
    }
1601
#endif
1602
1603
0
    bool found;
1604
0
    do {
1605
0
        found = false;
1606
0
        int index = 0;
1607
0
        qreal maxHeight = 0;
1608
0
        for (int i = 0; i < list.edgeCount(); ++i) {
1609
0
            QPathEdge *edge = list.edge(i);
1610
1611
            // have both sides of this edge already been handled?
1612
0
            if ((edge->flag & 0x3) == 0x3)
1613
0
                continue;
1614
1615
0
            QPathVertex *a = list.vertex(edge->first);
1616
0
            QPathVertex *b = list.vertex(edge->second);
1617
1618
0
            if (qFuzzyCompare(a->y, b->y))
1619
0
                continue;
1620
1621
0
            found = true;
1622
1623
0
            qreal height = qAbs(a->y - b->y);
1624
0
            if (height > maxHeight) {
1625
0
                index = i;
1626
0
                maxHeight = height;
1627
0
            }
1628
0
        }
1629
1630
0
        if (found) {
1631
0
            QPathEdge *edge = list.edge(index);
1632
1633
0
            QPathVertex *a = list.vertex(edge->first);
1634
0
            QPathVertex *b = list.vertex(edge->second);
1635
1636
            // FIXME: this can be optimized by using binary search
1637
0
            const int first = qFuzzyFind(y_coords.cbegin(), y_coords.cend(), qMin(a->y, b->y)) - y_coords.cbegin();
1638
0
            const int last = qFuzzyFind(y_coords.cbegin() + first, y_coords.cend(), qMax(a->y, b->y)) - y_coords.cbegin();
1639
1640
0
            Q_ASSERT(first < y_coords.size() - 1);
1641
0
            Q_ASSERT(last < y_coords.size());
1642
1643
0
            qreal biggestGap = y_coords.at(first + 1) - y_coords.at(first);
1644
0
            int bestIdx = first;
1645
0
            for (int i = first + 1; i < last; ++i) {
1646
0
                qreal gap = y_coords.at(i + 1) - y_coords.at(i);
1647
1648
0
                if (gap > biggestGap) {
1649
0
                    bestIdx = i;
1650
0
                    biggestGap = gap;
1651
0
                }
1652
0
            }
1653
0
            const qreal bestY = 0.5 * (y_coords.at(bestIdx) + y_coords.at(bestIdx + 1));
1654
1655
#ifdef QDEBUG_CLIPPER
1656
            printf("y: %.9f, gap: %.9f\n", bestY, biggestGap);
1657
#endif
1658
1659
0
            if (handleCrossingEdges(list, bestY, mode) && mode == CheckMode)
1660
0
                return true;
1661
1662
0
            edge->flag |= 0x3;
1663
0
        }
1664
0
    } while (found);
1665
1666
0
    if (mode == ClipMode)
1667
0
        list.simplify();
1668
1669
0
    return false;
1670
0
}
1671
1672
static void traverse(QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
1673
0
{
1674
0
    QWingedEdge::TraversalStatus status;
1675
0
    status.edge = edge;
1676
0
    status.traversal = traversal;
1677
0
    status.direction = QPathEdge::Forward;
1678
1679
0
    do {
1680
0
        int flag = status.traversal == QPathEdge::LeftTraversal ? 1 : 2;
1681
1682
0
        QPathEdge *ep = list.edge(status.edge);
1683
1684
0
        ep->flag |= (flag | (flag << 4));
1685
1686
#ifdef QDEBUG_CLIPPER
1687
        qDebug() << "traverse: adding edge " << status.edge << ", mask:" << (flag << 4) <<ep->flag;
1688
#endif
1689
1690
0
        status = list.next(status);
1691
0
    } while (status.edge != edge);
1692
0
}
1693
1694
struct QCrossingEdge
1695
{
1696
    int edge;
1697
    qreal x;
1698
1699
    bool operator<(const QCrossingEdge &edge) const
1700
0
    {
1701
0
        return x < edge.x;
1702
0
    }
1703
};
1704
Q_DECLARE_TYPEINFO(QCrossingEdge, Q_PRIMITIVE_TYPE);
1705
1706
static bool bool_op(bool a, bool b, QPathClipper::Operation op)
1707
0
{
1708
0
    switch (op) {
1709
0
    case QPathClipper::BoolAnd:
1710
0
        return a && b;
1711
0
    case QPathClipper::BoolOr:
1712
0
    case QPathClipper::Simplify:
1713
0
        return a || b;
1714
0
    case QPathClipper::BoolSub:
1715
0
        return a && !b;
1716
0
    default:
1717
0
        Q_ASSERT(false);
1718
0
        return false;
1719
0
    }
1720
0
}
1721
1722
bool QWingedEdge::isInside(qreal x, qreal y) const
1723
0
{
1724
0
    int winding = 0;
1725
0
    for (int i = 0; i < edgeCount(); ++i) {
1726
0
        const QPathEdge *ep = edge(i);
1727
1728
        // left xor right
1729
0
        int w = ((ep->flag >> 4) ^ (ep->flag >> 5)) & 1;
1730
1731
0
        if (!w)
1732
0
            continue;
1733
1734
0
        QPointF a = *vertex(ep->first);
1735
0
        QPointF b = *vertex(ep->second);
1736
1737
0
        if ((a.y() < y && b.y() > y) || (a.y() > y && b.y() < y)) {
1738
0
            qreal intersectionX = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y());
1739
1740
0
            if (intersectionX > x)
1741
0
                winding += w;
1742
0
        }
1743
0
    }
1744
1745
0
    return winding & 1;
1746
0
}
1747
1748
static QList<QCrossingEdge> findCrossings(const QWingedEdge &list, qreal y)
1749
0
{
1750
0
    QList<QCrossingEdge> crossings;
1751
0
    for (int i = 0; i < list.edgeCount(); ++i) {
1752
0
        const QPathEdge *edge = list.edge(i);
1753
0
        QPointF a = *list.vertex(edge->first);
1754
0
        QPointF b = *list.vertex(edge->second);
1755
1756
0
        if ((a.y() < y && b.y() > y) || (a.y() > y && b.y() < y)) {
1757
0
            const qreal intersection = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y());
1758
0
            const QCrossingEdge edge = { i, intersection };
1759
0
            crossings << edge;
1760
0
        }
1761
0
    }
1762
0
    return crossings;
1763
0
}
1764
1765
bool QPathClipper::handleCrossingEdges(QWingedEdge &list, qreal y, ClipperMode mode)
1766
0
{
1767
0
    QList<QCrossingEdge> crossings = findCrossings(list, y);
1768
1769
0
    Q_ASSERT(!crossings.isEmpty());
1770
0
    std::sort(crossings.begin(), crossings.end());
1771
1772
0
    int windingA = 0;
1773
0
    int windingB = 0;
1774
1775
0
    int windingD = 0;
1776
1777
#ifdef QDEBUG_CLIPPER
1778
    qDebug() << "crossings:" << crossings.size();
1779
#endif
1780
0
    for (int i = 0; i < crossings.size() - 1; ++i) {
1781
0
        int ei = crossings.at(i).edge;
1782
0
        const QPathEdge *edge = list.edge(ei);
1783
1784
0
        windingA += edge->windingA;
1785
0
        windingB += edge->windingB;
1786
1787
0
        const bool hasLeft = (edge->flag >> 4) & 1;
1788
0
        const bool hasRight = (edge->flag >> 4) & 2;
1789
1790
0
        windingD += hasLeft ^ hasRight;
1791
1792
0
        const bool inA = (windingA & aMask) != 0;
1793
0
        const bool inB = (windingB & bMask) != 0;
1794
0
        const bool inD = (windingD & 0x1) != 0;
1795
1796
0
        const bool inside = bool_op(inA, inB, op);
1797
0
        const bool add = inD ^ inside;
1798
1799
#ifdef QDEBUG_CLIPPER
1800
        printf("y %f, x %f, inA: %d, inB: %d, inD: %d, inside: %d, flag: %x, bezier: %p, edge: %d\n", y, crossings.at(i).x, inA, inB, inD, inside, edge->flag, edge->bezier, ei);
1801
#endif
1802
1803
0
        if (add) {
1804
0
            if (mode == CheckMode)
1805
0
                return true;
1806
1807
0
            qreal y0 = list.vertex(edge->first)->y;
1808
0
            qreal y1 = list.vertex(edge->second)->y;
1809
1810
0
            if (y0 < y1) {
1811
0
                if (!(edge->flag & 1))
1812
0
                    traverse(list, ei, QPathEdge::LeftTraversal);
1813
1814
0
                if (!(edge->flag & 2))
1815
0
                    clear(list, ei, QPathEdge::RightTraversal);
1816
0
            } else {
1817
0
                if (!(edge->flag & 1))
1818
0
                    clear(list, ei, QPathEdge::LeftTraversal);
1819
1820
0
                if (!(edge->flag & 2))
1821
0
                    traverse(list, ei, QPathEdge::RightTraversal);
1822
0
            }
1823
1824
0
            ++windingD;
1825
0
        } else {
1826
0
            if (!(edge->flag & 1))
1827
0
                clear(list, ei, QPathEdge::LeftTraversal);
1828
1829
0
            if (!(edge->flag & 2))
1830
0
                clear(list, ei, QPathEdge::RightTraversal);
1831
0
        }
1832
0
    }
1833
1834
0
    return false;
1835
0
}
1836
1837
namespace {
1838
1839
QList<QPainterPath> toSubpaths(const QPainterPath &path)
1840
11.2k
{
1841
1842
11.2k
    QList<QPainterPath> subpaths;
1843
11.2k
    if (path.isEmpty())
1844
0
        return subpaths;
1845
1846
11.2k
    QPainterPath current;
1847
19.7M
    for (int i = 0; i < path.elementCount(); ++i) {
1848
19.7M
        const QPainterPath::Element &e = path.elementAt(i);
1849
19.7M
        switch (e.type) {
1850
40.5k
        case QPainterPath::MoveToElement:
1851
40.5k
            if (current.elementCount() > 1)
1852
29.2k
                subpaths += current;
1853
40.5k
            current = QPainterPath();
1854
40.5k
            current.moveTo(e);
1855
40.5k
            break;
1856
19.7M
        case QPainterPath::LineToElement:
1857
19.7M
            current.lineTo(e);
1858
19.7M
            break;
1859
0
        case QPainterPath::CurveToElement: {
1860
0
            current.cubicTo(e, path.elementAt(i + 1), path.elementAt(i + 2));
1861
0
            i+=2;
1862
0
            break;
1863
0
        }
1864
0
        case QPainterPath::CurveToDataElement:
1865
0
            Q_ASSERT(!"toSubpaths(), bad element type");
1866
0
            break;
1867
19.7M
        }
1868
19.7M
    }
1869
1870
11.2k
    if (current.elementCount() > 1)
1871
11.2k
        subpaths << current;
1872
1873
11.2k
    return subpaths;
1874
11.2k
}
1875
1876
enum Edge
1877
{
1878
    Left, Top, Right, Bottom
1879
};
1880
1881
static bool isVertical(Edge edge)
1882
0
{
1883
0
    return edge == Left || edge == Right;
1884
0
}
1885
1886
template <Edge edge>
1887
bool compare(const QPointF &p, qreal t)
1888
72.9M
{
1889
72.9M
    switch (edge)
1890
72.9M
    {
1891
33.9M
    case Left:
1892
33.9M
        return p.x() < t;
1893
17.5M
    case Right:
1894
17.5M
        return p.x() > t;
1895
17.2M
    case Top:
1896
17.2M
        return p.y() < t;
1897
4.15M
    default:
1898
4.15M
        return p.y() > t;
1899
72.9M
    }
1900
72.9M
}
qpathclipper.cpp:bool (anonymous namespace)::compare<((anonymous namespace)::Edge)0>(QPointF const&, double)
Line
Count
Source
1888
33.9M
{
1889
33.9M
    switch (edge)
1890
33.9M
    {
1891
33.9M
    case Left:
1892
33.9M
        return p.x() < t;
1893
0
    case Right:
1894
0
        return p.x() > t;
1895
0
    case Top:
1896
0
        return p.y() < t;
1897
0
    default:
1898
0
        return p.y() > t;
1899
33.9M
    }
1900
33.9M
}
qpathclipper.cpp:bool (anonymous namespace)::compare<((anonymous namespace)::Edge)2>(QPointF const&, double)
Line
Count
Source
1888
17.5M
{
1889
17.5M
    switch (edge)
1890
17.5M
    {
1891
0
    case Left:
1892
0
        return p.x() < t;
1893
17.5M
    case Right:
1894
17.5M
        return p.x() > t;
1895
0
    case Top:
1896
0
        return p.y() < t;
1897
0
    default:
1898
0
        return p.y() > t;
1899
17.5M
    }
1900
17.5M
}
qpathclipper.cpp:bool (anonymous namespace)::compare<((anonymous namespace)::Edge)1>(QPointF const&, double)
Line
Count
Source
1888
17.2M
{
1889
17.2M
    switch (edge)
1890
17.2M
    {
1891
0
    case Left:
1892
0
        return p.x() < t;
1893
0
    case Right:
1894
0
        return p.x() > t;
1895
17.2M
    case Top:
1896
17.2M
        return p.y() < t;
1897
0
    default:
1898
0
        return p.y() > t;
1899
17.2M
    }
1900
17.2M
}
qpathclipper.cpp:bool (anonymous namespace)::compare<((anonymous namespace)::Edge)3>(QPointF const&, double)
Line
Count
Source
1888
4.15M
{
1889
4.15M
    switch (edge)
1890
4.15M
    {
1891
0
    case Left:
1892
0
        return p.x() < t;
1893
0
    case Right:
1894
0
        return p.x() > t;
1895
0
    case Top:
1896
0
        return p.y() < t;
1897
4.15M
    default:
1898
4.15M
        return p.y() > t;
1899
4.15M
    }
1900
4.15M
}
1901
1902
template <Edge edge>
1903
QPointF intersectLine(const QPointF &a, const QPointF &b, qreal t)
1904
277k
{
1905
277k
    QLineF line(a, b);
1906
277k
    switch (edge) {
1907
133k
    case Left:
1908
194k
    case Right:
1909
194k
        return line.pointAt((t - a.x()) / (b.x() - a.x()));
1910
82.2k
    default:
1911
82.2k
        return line.pointAt((t - a.y()) / (b.y() - a.y()));
1912
277k
    }
1913
277k
}
qpathclipper.cpp:QPointF (anonymous namespace)::intersectLine<((anonymous namespace)::Edge)0>(QPointF const&, QPointF const&, double)
Line
Count
Source
1904
133k
{
1905
133k
    QLineF line(a, b);
1906
133k
    switch (edge) {
1907
133k
    case Left:
1908
133k
    case Right:
1909
133k
        return line.pointAt((t - a.x()) / (b.x() - a.x()));
1910
0
    default:
1911
0
        return line.pointAt((t - a.y()) / (b.y() - a.y()));
1912
133k
    }
1913
133k
}
qpathclipper.cpp:QPointF (anonymous namespace)::intersectLine<((anonymous namespace)::Edge)2>(QPointF const&, QPointF const&, double)
Line
Count
Source
1904
61.0k
{
1905
61.0k
    QLineF line(a, b);
1906
61.0k
    switch (edge) {
1907
0
    case Left:
1908
61.0k
    case Right:
1909
61.0k
        return line.pointAt((t - a.x()) / (b.x() - a.x()));
1910
0
    default:
1911
0
        return line.pointAt((t - a.y()) / (b.y() - a.y()));
1912
61.0k
    }
1913
61.0k
}
qpathclipper.cpp:QPointF (anonymous namespace)::intersectLine<((anonymous namespace)::Edge)1>(QPointF const&, QPointF const&, double)
Line
Count
Source
1904
53.0k
{
1905
53.0k
    QLineF line(a, b);
1906
53.0k
    switch (edge) {
1907
0
    case Left:
1908
0
    case Right:
1909
0
        return line.pointAt((t - a.x()) / (b.x() - a.x()));
1910
53.0k
    default:
1911
53.0k
        return line.pointAt((t - a.y()) / (b.y() - a.y()));
1912
53.0k
    }
1913
53.0k
}
qpathclipper.cpp:QPointF (anonymous namespace)::intersectLine<((anonymous namespace)::Edge)3>(QPointF const&, QPointF const&, double)
Line
Count
Source
1904
29.2k
{
1905
29.2k
    QLineF line(a, b);
1906
29.2k
    switch (edge) {
1907
0
    case Left:
1908
0
    case Right:
1909
0
        return line.pointAt((t - a.x()) / (b.x() - a.x()));
1910
29.2k
    default:
1911
29.2k
        return line.pointAt((t - a.y()) / (b.y() - a.y()));
1912
29.2k
    }
1913
29.2k
}
1914
1915
void addLine(QPainterPath &path, const QLineF &line)
1916
23.4M
{
1917
23.4M
    if (path.elementCount() > 0)
1918
23.4M
        path.lineTo(line.p1());
1919
54.6k
    else
1920
54.6k
        path.moveTo(line.p1());
1921
1922
23.4M
    path.lineTo(line.p2());
1923
23.4M
}
1924
1925
template <Edge edge>
1926
void clipLine(const QPointF &a, const QPointF &b, qreal t, QPainterPath &result)
1927
36.4M
{
1928
36.4M
    bool outA = compare<edge>(a, t);
1929
36.4M
    bool outB = compare<edge>(b, t);
1930
36.4M
    if (outA && outB)
1931
13.0M
        return;
1932
1933
23.4M
    if (outA)
1934
138k
        addLine(result, QLineF(intersectLine<edge>(a, b, t), b));
1935
23.3M
    else if (outB)
1936
138k
        addLine(result, QLineF(a, intersectLine<edge>(a, b, t)));
1937
23.1M
    else
1938
23.1M
        addLine(result, QLineF(a, b));
1939
23.4M
}
qpathclipper.cpp:void (anonymous namespace)::clipLine<((anonymous namespace)::Edge)0>(QPointF const&, QPointF const&, double, QPainterPath&)
Line
Count
Source
1927
16.9M
{
1928
16.9M
    bool outA = compare<edge>(a, t);
1929
16.9M
    bool outB = compare<edge>(b, t);
1930
16.9M
    if (outA && outB)
1931
5.33M
        return;
1932
1933
11.6M
    if (outA)
1934
66.8k
        addLine(result, QLineF(intersectLine<edge>(a, b, t), b));
1935
11.5M
    else if (outB)
1936
66.9k
        addLine(result, QLineF(a, intersectLine<edge>(a, b, t)));
1937
11.5M
    else
1938
11.5M
        addLine(result, QLineF(a, b));
1939
11.6M
}
qpathclipper.cpp:void (anonymous namespace)::clipLine<((anonymous namespace)::Edge)2>(QPointF const&, QPointF const&, double, QPainterPath&)
Line
Count
Source
1927
8.78M
{
1928
8.78M
    bool outA = compare<edge>(a, t);
1929
8.78M
    bool outB = compare<edge>(b, t);
1930
8.78M
    if (outA && outB)
1931
4.35M
        return;
1932
1933
4.42M
    if (outA)
1934
30.5k
        addLine(result, QLineF(intersectLine<edge>(a, b, t), b));
1935
4.39M
    else if (outB)
1936
30.5k
        addLine(result, QLineF(a, intersectLine<edge>(a, b, t)));
1937
4.36M
    else
1938
4.36M
        addLine(result, QLineF(a, b));
1939
4.42M
}
qpathclipper.cpp:void (anonymous namespace)::clipLine<((anonymous namespace)::Edge)1>(QPointF const&, QPointF const&, double, QPainterPath&)
Line
Count
Source
1927
8.64M
{
1928
8.64M
    bool outA = compare<edge>(a, t);
1929
8.64M
    bool outB = compare<edge>(b, t);
1930
8.64M
    if (outA && outB)
1931
2.08M
        return;
1932
1933
6.55M
    if (outA)
1934
26.4k
        addLine(result, QLineF(intersectLine<edge>(a, b, t), b));
1935
6.53M
    else if (outB)
1936
26.5k
        addLine(result, QLineF(a, intersectLine<edge>(a, b, t)));
1937
6.50M
    else
1938
6.50M
        addLine(result, QLineF(a, b));
1939
6.55M
}
qpathclipper.cpp:void (anonymous namespace)::clipLine<((anonymous namespace)::Edge)3>(QPointF const&, QPointF const&, double, QPainterPath&)
Line
Count
Source
1927
2.07M
{
1928
2.07M
    bool outA = compare<edge>(a, t);
1929
2.07M
    bool outB = compare<edge>(b, t);
1930
2.07M
    if (outA && outB)
1931
1.24M
        return;
1932
1933
833k
    if (outA)
1934
14.6k
        addLine(result, QLineF(intersectLine<edge>(a, b, t), b));
1935
818k
    else if (outB)
1936
14.6k
        addLine(result, QLineF(a, intersectLine<edge>(a, b, t)));
1937
804k
    else
1938
804k
        addLine(result, QLineF(a, b));
1939
833k
}
1940
1941
void addBezier(QPainterPath &path, const QBezier &bezier)
1942
0
{
1943
0
    if (path.elementCount() > 0)
1944
0
        path.lineTo(bezier.pt1());
1945
0
    else
1946
0
        path.moveTo(bezier.pt1());
1947
1948
0
    path.cubicTo(bezier.pt2(), bezier.pt3(), bezier.pt4());
1949
0
}
1950
1951
template <Edge edge>
1952
void clipBezier(const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, qreal t, QPainterPath &result)
1953
0
{
1954
0
    QBezier bezier = QBezier::fromPoints(a, b, c, d);
1955
1956
0
    bool outA = compare<edge>(a, t);
1957
0
    bool outB = compare<edge>(b, t);
1958
0
    bool outC = compare<edge>(c, t);
1959
0
    bool outD = compare<edge>(d, t);
1960
1961
0
    int outCount = int(outA) + int(outB) + int(outC) + int(outD);
1962
1963
0
    if (outCount == 4)
1964
0
        return;
1965
1966
0
    if (outCount == 0) {
1967
0
        addBezier(result, bezier);
1968
0
        return;
1969
0
    }
1970
1971
0
    QTransform flip = isVertical(edge) ? QTransform(0, 1, 1, 0, 0, 0) : QTransform();
1972
0
    QBezier unflipped = bezier;
1973
0
    QBezier flipped = bezier.mapBy(flip);
1974
1975
0
    qreal t0 = 0, t1 = 1;
1976
0
    int stationary = flipped.stationaryYPoints(t0, t1);
1977
1978
0
    qreal segments[4];
1979
0
    QPointF points[4];
1980
0
    points[0] = unflipped.pt1();
1981
0
    segments[0] = 0;
1982
1983
0
    int segmentCount = 0;
1984
0
    if (stationary > 0) {
1985
0
        ++segmentCount;
1986
0
        segments[segmentCount] = t0;
1987
0
        points[segmentCount] = unflipped.pointAt(t0);
1988
0
    }
1989
0
    if (stationary > 1) {
1990
0
        ++segmentCount;
1991
0
        segments[segmentCount] = t1;
1992
0
        points[segmentCount] = unflipped.pointAt(t1);
1993
0
    }
1994
0
    ++segmentCount;
1995
0
    segments[segmentCount] = 1;
1996
0
    points[segmentCount] = unflipped.pt4();
1997
1998
0
    qreal lastIntersection = 0;
1999
0
    for (int i = 0; i < segmentCount; ++i) {
2000
0
        outA = compare<edge>(points[i], t);
2001
0
        outB = compare<edge>(points[i+1], t);
2002
2003
0
        if (outA != outB) {
2004
0
            qreal intersection = flipped.tForY(segments[i], segments[i+1], t);
2005
2006
0
            if (outB)
2007
0
                addBezier(result, unflipped.getSubRange(lastIntersection, intersection));
2008
2009
0
            lastIntersection = intersection;
2010
0
        }
2011
0
    }
2012
2013
0
    if (!outB)
2014
0
        addBezier(result, unflipped.getSubRange(lastIntersection, 1));
2015
0
}
Unexecuted instantiation: qpathclipper.cpp:void (anonymous namespace)::clipBezier<((anonymous namespace)::Edge)0>(QPointF const&, QPointF const&, QPointF const&, QPointF const&, double, QPainterPath&)
Unexecuted instantiation: qpathclipper.cpp:void (anonymous namespace)::clipBezier<((anonymous namespace)::Edge)2>(QPointF const&, QPointF const&, QPointF const&, QPointF const&, double, QPainterPath&)
Unexecuted instantiation: qpathclipper.cpp:void (anonymous namespace)::clipBezier<((anonymous namespace)::Edge)1>(QPointF const&, QPointF const&, QPointF const&, QPointF const&, double, QPainterPath&)
Unexecuted instantiation: qpathclipper.cpp:void (anonymous namespace)::clipBezier<((anonymous namespace)::Edge)3>(QPointF const&, QPointF const&, QPointF const&, QPointF const&, double, QPainterPath&)
2016
2017
// clips a single subpath against a single edge
2018
template <Edge edge>
2019
QPainterPath clip(const QPainterPath &path, qreal t)
2020
56.5k
{
2021
56.5k
    QPainterPath result;
2022
36.5M
    for (int i = 1; i < path.elementCount(); ++i) {
2023
36.4M
        const QPainterPath::Element &element = path.elementAt(i);
2024
36.4M
        Q_ASSERT(!element.isMoveTo());
2025
36.4M
        if (element.isLineTo()) {
2026
36.4M
            clipLine<edge>(path.elementAt(i-1), path.elementAt(i), t, result);
2027
36.4M
        } else {
2028
0
            clipBezier<edge>(path.elementAt(i-1), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), t, result);
2029
0
            i += 2;
2030
0
        }
2031
36.4M
    }
2032
2033
56.5k
    int last = path.elementCount() - 1;
2034
56.5k
    if (QPointF(path.elementAt(last)) != QPointF(path.elementAt(0)))
2035
12.8k
        clipLine<edge>(path.elementAt(last), path.elementAt(0), t, result);
2036
2037
56.5k
    return result;
2038
56.5k
}
qpathclipper.cpp:QPainterPath (anonymous namespace)::clip<((anonymous namespace)::Edge)0>(QPainterPath const&, double)
Line
Count
Source
2020
18.7k
{
2021
18.7k
    QPainterPath result;
2022
16.9M
    for (int i = 1; i < path.elementCount(); ++i) {
2023
16.9M
        const QPainterPath::Element &element = path.elementAt(i);
2024
16.9M
        Q_ASSERT(!element.isMoveTo());
2025
16.9M
        if (element.isLineTo()) {
2026
16.9M
            clipLine<edge>(path.elementAt(i-1), path.elementAt(i), t, result);
2027
16.9M
        } else {
2028
0
            clipBezier<edge>(path.elementAt(i-1), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), t, result);
2029
0
            i += 2;
2030
0
        }
2031
16.9M
    }
2032
2033
18.7k
    int last = path.elementCount() - 1;
2034
18.7k
    if (QPointF(path.elementAt(last)) != QPointF(path.elementAt(0)))
2035
0
        clipLine<edge>(path.elementAt(last), path.elementAt(0), t, result);
2036
2037
18.7k
    return result;
2038
18.7k
}
qpathclipper.cpp:QPainterPath (anonymous namespace)::clip<((anonymous namespace)::Edge)2>(QPainterPath const&, double)
Line
Count
Source
2020
11.5k
{
2021
11.5k
    QPainterPath result;
2022
8.78M
    for (int i = 1; i < path.elementCount(); ++i) {
2023
8.77M
        const QPainterPath::Element &element = path.elementAt(i);
2024
8.77M
        Q_ASSERT(!element.isMoveTo());
2025
8.77M
        if (element.isLineTo()) {
2026
8.77M
            clipLine<edge>(path.elementAt(i-1), path.elementAt(i), t, result);
2027
8.77M
        } else {
2028
0
            clipBezier<edge>(path.elementAt(i-1), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), t, result);
2029
0
            i += 2;
2030
0
        }
2031
8.77M
    }
2032
2033
11.5k
    int last = path.elementCount() - 1;
2034
11.5k
    if (QPointF(path.elementAt(last)) != QPointF(path.elementAt(0)))
2035
5.91k
        clipLine<edge>(path.elementAt(last), path.elementAt(0), t, result);
2036
2037
11.5k
    return result;
2038
11.5k
}
qpathclipper.cpp:QPainterPath (anonymous namespace)::clip<((anonymous namespace)::Edge)1>(QPainterPath const&, double)
Line
Count
Source
2020
13.3k
{
2021
13.3k
    QPainterPath result;
2022
8.65M
    for (int i = 1; i < path.elementCount(); ++i) {
2023
8.64M
        const QPainterPath::Element &element = path.elementAt(i);
2024
8.64M
        Q_ASSERT(!element.isMoveTo());
2025
8.64M
        if (element.isLineTo()) {
2026
8.64M
            clipLine<edge>(path.elementAt(i-1), path.elementAt(i), t, result);
2027
8.64M
        } else {
2028
0
            clipBezier<edge>(path.elementAt(i-1), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), t, result);
2029
0
            i += 2;
2030
0
        }
2031
8.64M
    }
2032
2033
13.3k
    int last = path.elementCount() - 1;
2034
13.3k
    if (QPointF(path.elementAt(last)) != QPointF(path.elementAt(0)))
2035
2.48k
        clipLine<edge>(path.elementAt(last), path.elementAt(0), t, result);
2036
2037
13.3k
    return result;
2038
13.3k
}
qpathclipper.cpp:QPainterPath (anonymous namespace)::clip<((anonymous namespace)::Edge)3>(QPainterPath const&, double)
Line
Count
Source
2020
12.8k
{
2021
12.8k
    QPainterPath result;
2022
2.08M
    for (int i = 1; i < path.elementCount(); ++i) {
2023
2.07M
        const QPainterPath::Element &element = path.elementAt(i);
2024
2.07M
        Q_ASSERT(!element.isMoveTo());
2025
2.07M
        if (element.isLineTo()) {
2026
2.07M
            clipLine<edge>(path.elementAt(i-1), path.elementAt(i), t, result);
2027
2.07M
        } else {
2028
0
            clipBezier<edge>(path.elementAt(i-1), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), t, result);
2029
0
            i += 2;
2030
0
        }
2031
2.07M
    }
2032
2033
12.8k
    int last = path.elementCount() - 1;
2034
12.8k
    if (QPointF(path.elementAt(last)) != QPointF(path.elementAt(0)))
2035
4.48k
        clipLine<edge>(path.elementAt(last), path.elementAt(0), t, result);
2036
2037
12.8k
    return result;
2038
12.8k
}
2039
2040
QPainterPath intersectPath(const QPainterPath &path, const QRectF &rect)
2041
11.2k
{
2042
11.2k
    QList<QPainterPath> subpaths = toSubpaths(path);
2043
2044
11.2k
    QPainterPath result;
2045
11.2k
    result.setFillRule(path.fillRule());
2046
51.7k
    for (int i = 0; i < subpaths.size(); ++i) {
2047
40.5k
        QPainterPath subPath = subpaths.at(i);
2048
40.5k
        QRectF bounds = subPath.boundingRect();
2049
40.5k
        if (bounds.intersects(rect)) {
2050
26.3k
            if (bounds.left() < rect.left())
2051
18.7k
                subPath = clip<Left>(subPath, rect.left());
2052
26.3k
            if (bounds.right() > rect.right())
2053
11.5k
                subPath = clip<Right>(subPath, rect.right());
2054
2055
26.3k
            bounds = subPath.boundingRect();
2056
2057
26.3k
            if (bounds.top() < rect.top())
2058
13.3k
                subPath = clip<Top>(subPath, rect.top());
2059
26.3k
            if (bounds.bottom() > rect.bottom())
2060
12.8k
                subPath = clip<Bottom>(subPath, rect.bottom());
2061
2062
26.3k
            if (subPath.elementCount() > 1)
2063
24.4k
                result.addPath(subPath);
2064
26.3k
        }
2065
40.5k
    }
2066
    // The algorithm above might return one side of \a rect if there was no intersection,
2067
    // so only return intersections that are not empty rectangles.
2068
11.2k
    if (result.boundingRect().isEmpty())
2069
351
        return QPainterPath();
2070
10.9k
    else
2071
10.9k
        return result;
2072
11.2k
}
2073
2074
}
2075
2076
QPainterPath QPathClipper::intersect(const QPainterPath &path, const QRectF &rect)
2077
11.2k
{
2078
11.2k
    return intersectPath(path, rect);
2079
11.2k
}
2080
2081
QT_END_NAMESPACE