Coverage Report

Created: 2024-05-20 07:14

/src/skia/src/pathops/SkPathOpsAsWinding.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2018 Google Inc.
3
 *
4
 * Use of this source code is governed by a BSD-style license that can be
5
 * found in the LICENSE file.
6
 */
7
#include "include/core/SkPath.h"
8
#include "include/core/SkPathBuilder.h"
9
#include "include/core/SkPathTypes.h"
10
#include "include/core/SkPoint.h"
11
#include "include/core/SkRect.h"
12
#include "include/core/SkScalar.h"
13
#include "include/core/SkTypes.h"
14
#include "include/pathops/SkPathOps.h"
15
#include "include/private/base/SkMacros.h"
16
#include "src/core/SkPathPriv.h"
17
#include "src/pathops/SkPathOpsConic.h"
18
#include "src/pathops/SkPathOpsCubic.h"
19
#include "src/pathops/SkPathOpsCurve.h"
20
#include "src/pathops/SkPathOpsPoint.h"
21
#include "src/pathops/SkPathOpsQuad.h"
22
#include "src/pathops/SkPathOpsTypes.h"
23
24
#include <algorithm>
25
#include <vector>
26
27
using std::vector;
28
29
struct Contour {
30
    enum class Direction {  // SkPathDirection doesn't have 'none' state
31
        kCCW = -1,
32
        kNone,
33
        kCW,
34
    };
35
36
    Contour(const SkRect& bounds, int lastStart, int verbStart)
37
        : fBounds(bounds)
38
        , fVerbStart(lastStart)
39
17.9k
        , fVerbEnd(verbStart) {
40
17.9k
    }
41
42
    vector<Contour*> fChildren;
43
    const SkRect fBounds;
44
    SkPoint fMinXY{SK_ScalarMax, SK_ScalarMax};
45
    const int fVerbStart;
46
    const int fVerbEnd;
47
    Direction fDirection{Direction::kNone};
48
    bool fContained{false};
49
    bool fReverse{false};
50
};
51
52
static const int kPtCount[] = { 1, 1, 2, 2, 3, 0 };
53
static const int kPtIndex[] = { 0, 1, 1, 1, 1, 0 };
54
55
4.57k
static Contour::Direction to_direction(SkScalar dy) {
56
4.57k
    return dy > 0 ? Contour::Direction::kCCW : dy < 0 ? Contour::Direction::kCW :
57
2.52k
            Contour::Direction::kNone;
58
4.57k
}
59
60
30.2k
static int contains_edge(SkPoint pts[4], SkPath::Verb verb, SkScalar weight, const SkPoint& edge) {
61
30.2k
    SkRect bounds;
62
30.2k
    bounds.setBounds(pts, kPtCount[verb] + 1);
63
30.2k
    if (bounds.fTop > edge.fY) {
64
3.36k
        return 0;
65
3.36k
    }
66
26.8k
    if (bounds.fBottom <= edge.fY) {  // check to see if y is at line end to avoid double counting
67
5.12k
        return 0;
68
5.12k
    }
69
21.7k
    if (bounds.fLeft >= edge.fX) {
70
15.3k
        return 0;
71
15.3k
    }
72
6.41k
    int winding = 0;
73
6.41k
    double tVals[3];
74
6.41k
    Contour::Direction directions[3];
75
    // must intersect horz ray with curve in case it intersects more than once
76
6.41k
    int count = (*CurveIntercept[verb * 2])(pts, weight, edge.fY, tVals);
77
6.41k
    SkASSERT(between(0, count, 3));
78
    // remove results to the right of edge
79
13.3k
    for (int index = 0; index < count; ) {
80
6.94k
        SkScalar intersectX = (*CurvePointAtT[verb])(pts, weight, tVals[index]).fX;
81
6.94k
        if (intersectX < edge.fX) {
82
4.49k
            ++index;
83
4.49k
            continue;
84
4.49k
        }
85
2.45k
        if (intersectX > edge.fX) {
86
1.54k
            tVals[index] = tVals[--count];
87
1.54k
            continue;
88
1.54k
        }
89
        // if intersect x equals edge x, we need to determine if pts is to the left or right of edge
90
908
        if (pts[0].fX < edge.fX && pts[kPtCount[verb]].fX < edge.fX) {
91
88
            ++index;
92
88
            continue;
93
88
        }
94
        // TODO : other cases need discriminating. need op angle code to figure it out
95
        // example: edge ends 45 degree diagonal going up. If pts is to the left of edge, keep.
96
        // if pts is to the right of edge, discard. With code as is, can't distiguish the two cases.
97
820
        tVals[index] = tVals[--count];
98
820
    }
99
    // use first derivative to determine if intersection is contributing +1 or -1 to winding
100
10.9k
    for (int index = 0; index < count; ++index) {
101
4.57k
        directions[index] = to_direction((*CurveSlopeAtT[verb])(pts, weight, tVals[index]).fY);
102
4.57k
    }
103
10.9k
    for (int index = 0; index < count; ++index) {
104
        // skip intersections that end at edge and go up
105
4.57k
        if (zero_or_one(tVals[index]) && Contour::Direction::kCCW != directions[index]) {
106
1.63k
            continue;
107
1.63k
        }
108
2.94k
        winding += (int) directions[index];
109
2.94k
    }
110
6.41k
    return winding;  // note winding indicates containership, not contour direction
111
21.7k
}
SkPathOpsAsWinding.cpp:contains_edge(SkPoint*, SkPath::Verb, float, SkPoint const&)
Line
Count
Source
60
30.2k
static int contains_edge(SkPoint pts[4], SkPath::Verb verb, SkScalar weight, const SkPoint& edge) {
61
30.2k
    SkRect bounds;
62
30.2k
    bounds.setBounds(pts, kPtCount[verb] + 1);
63
30.2k
    if (bounds.fTop > edge.fY) {
64
3.36k
        return 0;
65
3.36k
    }
66
26.8k
    if (bounds.fBottom <= edge.fY) {  // check to see if y is at line end to avoid double counting
67
5.12k
        return 0;
68
5.12k
    }
69
21.7k
    if (bounds.fLeft >= edge.fX) {
70
15.3k
        return 0;
71
15.3k
    }
72
6.41k
    int winding = 0;
73
6.41k
    double tVals[3];
74
6.41k
    Contour::Direction directions[3];
75
    // must intersect horz ray with curve in case it intersects more than once
76
6.41k
    int count = (*CurveIntercept[verb * 2])(pts, weight, edge.fY, tVals);
77
6.41k
    SkASSERT(between(0, count, 3));
78
    // remove results to the right of edge
79
13.3k
    for (int index = 0; index < count; ) {
80
6.94k
        SkScalar intersectX = (*CurvePointAtT[verb])(pts, weight, tVals[index]).fX;
81
6.94k
        if (intersectX < edge.fX) {
82
4.49k
            ++index;
83
4.49k
            continue;
84
4.49k
        }
85
2.45k
        if (intersectX > edge.fX) {
86
1.54k
            tVals[index] = tVals[--count];
87
1.54k
            continue;
88
1.54k
        }
89
        // if intersect x equals edge x, we need to determine if pts is to the left or right of edge
90
908
        if (pts[0].fX < edge.fX && pts[kPtCount[verb]].fX < edge.fX) {
91
88
            ++index;
92
88
            continue;
93
88
        }
94
        // TODO : other cases need discriminating. need op angle code to figure it out
95
        // example: edge ends 45 degree diagonal going up. If pts is to the left of edge, keep.
96
        // if pts is to the right of edge, discard. With code as is, can't distiguish the two cases.
97
820
        tVals[index] = tVals[--count];
98
820
    }
99
    // use first derivative to determine if intersection is contributing +1 or -1 to winding
100
10.9k
    for (int index = 0; index < count; ++index) {
101
4.57k
        directions[index] = to_direction((*CurveSlopeAtT[verb])(pts, weight, tVals[index]).fY);
102
4.57k
    }
103
10.9k
    for (int index = 0; index < count; ++index) {
104
        // skip intersections that end at edge and go up
105
4.57k
        if (zero_or_one(tVals[index]) && Contour::Direction::kCCW != directions[index]) {
106
1.63k
            continue;
107
1.63k
        }
108
2.94k
        winding += (int) directions[index];
109
2.94k
    }
110
6.41k
    return winding;  // note winding indicates containership, not contour direction
111
21.7k
}
Unexecuted instantiation: SkPathOpsAsWinding.cpp:contains_edge(SkPoint*, SkPath::Verb, float, SkPoint const&)
112
113
41.0k
static SkScalar conic_weight(const SkPath::Iter& iter, SkPath::Verb verb) {
114
41.0k
    return SkPath::kConic_Verb == verb ? iter.conicWeight() : 1;
115
41.0k
}
116
117
10.8k
static SkPoint left_edge(SkPoint pts[4], SkPath::Verb verb, SkScalar weight) {
118
10.8k
    SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb);
119
10.8k
    SkPoint result;
120
10.8k
    double t SK_INIT_TO_AVOID_WARNING;
121
10.8k
    int roots = 0;
122
10.8k
    if (SkPath::kLine_Verb == verb) {
123
6.74k
        result = pts[0].fX < pts[1].fX ? pts[0] : pts[1];
124
6.74k
    } else if (SkPath::kQuad_Verb == verb) {
125
2.05k
        SkDQuad quad;
126
2.05k
        quad.set(pts);
127
2.05k
        if (!quad.monotonicInX()) {
128
928
            roots = SkDQuad::FindExtrema(&quad[0].fX, &t);
129
928
        }
130
2.05k
        if (roots) {
131
905
            result = quad.ptAtT(t).asSkPoint();
132
1.14k
        } else {
133
1.14k
            result = pts[0].fX < pts[2].fX ? pts[0] : pts[2];
134
1.14k
        }
135
2.05k
    } else if (SkPath::kConic_Verb == verb) {
136
954
        SkDConic conic;
137
954
        conic.set(pts, weight);
138
954
        if (!conic.monotonicInX()) {
139
419
            roots = SkDConic::FindExtrema(&conic[0].fX, weight, &t);
140
419
        }
141
954
        if (roots) {
142
316
            result = conic.ptAtT(t).asSkPoint();
143
638
        } else {
144
638
            result = pts[0].fX < pts[2].fX ? pts[0] : pts[2];
145
638
        }
146
1.06k
    } else {
147
1.06k
        SkASSERT(SkPath::kCubic_Verb == verb);
148
1.06k
        SkDCubic cubic;
149
1.06k
        cubic.set(pts);
150
1.06k
        if (!cubic.monotonicInX()) {
151
712
            double tValues[2];
152
712
            roots = SkDCubic::FindExtrema(&cubic[0].fX, tValues);
153
712
            SkASSERT(roots <= 2);
154
1.91k
            for (int index = 0; index < roots; ++index) {
155
1.20k
                SkPoint temp = cubic.ptAtT(tValues[index]).asSkPoint();
156
1.20k
                if (0 == index || result.fX > temp.fX) {
157
1.04k
                    result = temp;
158
1.04k
                }
159
1.20k
            }
160
712
        }
161
1.06k
        if (roots) {
162
689
            result = cubic.ptAtT(t).asSkPoint();
163
689
        } else {
164
377
            result = pts[0].fX < pts[3].fX ? pts[0] : pts[3];
165
377
        }
166
1.06k
    }
167
10.8k
    return result;
168
10.8k
}
169
170
class OpAsWinding {
171
public:
172
    enum class Edge {
173
        kInitial,
174
        kCompare,
175
    };
176
177
    OpAsWinding(const SkPath& path)
178
657
        : fPath(path) {
179
657
    }
180
181
657
    void contourBounds(vector<Contour>* containers) {
182
657
        SkRect bounds;
183
657
        bounds.setEmpty();
184
657
        int lastStart = 0;
185
657
        int verbStart = 0;
186
63.7k
        for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) {
187
63.7k
            if (SkPathVerb::kMove == verb) {
188
20.1k
                if (!bounds.isEmpty()) {
189
16.7k
                    containers->emplace_back(bounds, lastStart, verbStart);
190
16.7k
                    lastStart = verbStart;
191
16.7k
               }
192
20.1k
               bounds.setBounds(&pts[kPtIndex[SkPath::kMove_Verb]], kPtCount[SkPath::kMove_Verb]);
193
20.1k
            }
194
63.7k
            if (SkPathVerb::kLine <= verb && verb <= SkPathVerb::kCubic) {
195
25.0k
                SkRect verbBounds;
196
25.0k
                verbBounds.setBounds(&pts[kPtIndex[(int)verb]], kPtCount[(int)verb]);
197
25.0k
                bounds.joinPossiblyEmptyRect(verbBounds);
198
25.0k
            }
199
63.7k
            ++verbStart;
200
63.7k
        }
201
657
        if (!bounds.isEmpty()) {
202
604
            containers->emplace_back(bounds, lastStart, ++verbStart);
203
604
        }
204
657
    }
205
206
18.2k
    Contour::Direction getDirection(Contour& contour) {
207
18.2k
        SkPath::Iter iter(fPath, true);
208
18.2k
        int verbCount = -1;
209
18.2k
        SkPath::Verb verb;
210
18.2k
        SkPoint pts[4];
211
212
18.2k
        SkScalar total_signed_area = 0;
213
10.1M
        do {
214
10.1M
            verb = iter.next(pts);
215
10.1M
            if (++verbCount < contour.fVerbStart) {
216
3.70M
                continue;
217
3.70M
            }
218
6.42M
            if (verbCount >= contour.fVerbEnd) {
219
6.35M
                continue;
220
6.35M
            }
221
71.8k
            if (SkPath::kLine_Verb > verb || verb > SkPath::kCubic_Verb) {
222
32.8k
                continue;
223
32.8k
            }
224
225
39.0k
            switch (verb)
226
39.0k
            {
227
31.2k
                case SkPath::kLine_Verb:
228
31.2k
                    total_signed_area += (pts[0].fY - pts[1].fY) * (pts[0].fX + pts[1].fX);
229
31.2k
                    break;
230
3.78k
                case SkPath::kQuad_Verb:
231
5.89k
                case SkPath::kConic_Verb:
232
5.89k
                    total_signed_area += (pts[0].fY - pts[2].fY) * (pts[0].fX + pts[2].fX);
233
5.89k
                    break;
234
1.81k
                case SkPath::kCubic_Verb:
235
1.81k
                    total_signed_area += (pts[0].fY - pts[3].fY) * (pts[0].fX + pts[3].fX);
236
1.81k
                    break;
237
0
                default:
238
0
                    break;
239
39.0k
            }
240
10.1M
        } while (SkPath::kDone_Verb != verb);
241
242
18.2k
        return total_signed_area < 0 ? Contour::Direction::kCCW: Contour::Direction::kCW;
243
18.2k
    }
244
245
19.0k
    int nextEdge(Contour& contour, Edge edge) {
246
19.0k
        SkPath::Iter iter(fPath, true);
247
19.0k
        SkPoint pts[4];
248
19.0k
        SkPath::Verb verb;
249
19.0k
        int verbCount = -1;
250
19.0k
        int winding = 0;
251
10.3M
        do {
252
10.3M
            verb = iter.next(pts);
253
10.3M
            if (++verbCount < contour.fVerbStart) {
254
3.74M
                continue;
255
3.74M
            }
256
6.57M
            if (verbCount >= contour.fVerbEnd) {
257
6.49M
                continue;
258
6.49M
            }
259
76.7k
            if (SkPath::kLine_Verb > verb || verb > SkPath::kCubic_Verb) {
260
33.8k
                continue;
261
33.8k
            }
262
42.8k
            bool horizontal = true;
263
46.6k
            for (int index = 1; index <= kPtCount[verb]; ++index) {
264
44.8k
                if (pts[0].fY != pts[index].fY) {
265
41.0k
                    horizontal = false;
266
41.0k
                    break;
267
41.0k
                }
268
44.8k
            }
269
42.8k
            if (horizontal) {
270
1.80k
                continue;
271
1.80k
            }
272
41.0k
            if (edge == Edge::kCompare) {
273
30.2k
                winding += contains_edge(pts, verb, conic_weight(iter, verb), contour.fMinXY);
274
30.2k
                continue;
275
30.2k
            }
276
10.8k
            SkASSERT(edge == Edge::kInitial);
277
10.8k
            SkPoint minXY = left_edge(pts, verb, conic_weight(iter, verb));
278
10.8k
            if (minXY.fX > contour.fMinXY.fX) {
279
3.79k
                continue;
280
3.79k
            }
281
7.02k
            if (minXY.fX == contour.fMinXY.fX) {
282
3.03k
                if (minXY.fY != contour.fMinXY.fY) {
283
605
                    continue;
284
605
                }
285
3.03k
            }
286
6.42k
            contour.fMinXY = minXY;
287
10.3M
        } while (SkPath::kDone_Verb != verb);
288
0
        return winding;
289
19.0k
    }
OpAsWinding::nextEdge(Contour&, OpAsWinding::Edge)
Line
Count
Source
245
19.0k
    int nextEdge(Contour& contour, Edge edge) {
246
19.0k
        SkPath::Iter iter(fPath, true);
247
19.0k
        SkPoint pts[4];
248
19.0k
        SkPath::Verb verb;
249
19.0k
        int verbCount = -1;
250
19.0k
        int winding = 0;
251
10.3M
        do {
252
10.3M
            verb = iter.next(pts);
253
10.3M
            if (++verbCount < contour.fVerbStart) {
254
3.74M
                continue;
255
3.74M
            }
256
6.57M
            if (verbCount >= contour.fVerbEnd) {
257
6.49M
                continue;
258
6.49M
            }
259
76.7k
            if (SkPath::kLine_Verb > verb || verb > SkPath::kCubic_Verb) {
260
33.8k
                continue;
261
33.8k
            }
262
42.8k
            bool horizontal = true;
263
46.6k
            for (int index = 1; index <= kPtCount[verb]; ++index) {
264
44.8k
                if (pts[0].fY != pts[index].fY) {
265
41.0k
                    horizontal = false;
266
41.0k
                    break;
267
41.0k
                }
268
44.8k
            }
269
42.8k
            if (horizontal) {
270
1.80k
                continue;
271
1.80k
            }
272
41.0k
            if (edge == Edge::kCompare) {
273
30.2k
                winding += contains_edge(pts, verb, conic_weight(iter, verb), contour.fMinXY);
274
30.2k
                continue;
275
30.2k
            }
276
10.8k
            SkASSERT(edge == Edge::kInitial);
277
10.8k
            SkPoint minXY = left_edge(pts, verb, conic_weight(iter, verb));
278
10.8k
            if (minXY.fX > contour.fMinXY.fX) {
279
3.79k
                continue;
280
3.79k
            }
281
7.02k
            if (minXY.fX == contour.fMinXY.fX) {
282
3.03k
                if (minXY.fY != contour.fMinXY.fY) {
283
605
                    continue;
284
605
                }
285
3.03k
            }
286
6.42k
            contour.fMinXY = minXY;
287
10.3M
        } while (SkPath::kDone_Verb != verb);
288
0
        return winding;
289
19.0k
    }
Unexecuted instantiation: OpAsWinding::nextEdge(Contour&, OpAsWinding::Edge)
290
291
15.4k
    bool containerContains(Contour& contour, Contour& test) {
292
        // find outside point on lesser contour
293
        // arbitrarily, choose non-horizontal edge where point <= bounds left
294
        // note that if leftmost point is control point, may need tight bounds
295
        // to find edge with minimum-x
296
15.4k
        if (SK_ScalarMax == test.fMinXY.fX) {
297
2.04k
            this->nextEdge(test, Edge::kInitial);
298
2.04k
        }
299
        // find all edges on greater equal or to the left of one on lesser
300
15.4k
        contour.fMinXY = test.fMinXY;
301
15.4k
        int winding = this->nextEdge(contour, Edge::kCompare);
302
        // if edge is up, mark contour cw, otherwise, ccw
303
        // sum of greater edges direction should be cw, 0, ccw
304
15.4k
        test.fContained = winding != 0;
305
15.4k
        return -1 <= winding && winding <= 1;
306
15.4k
    }
307
308
953k
    void inParent(Contour& contour, Contour& parent) {
309
        // move contour into sibling list contained by parent
310
974k
        for (auto test : parent.fChildren) {
311
974k
            if (test->fBounds.contains(contour.fBounds)) {
312
936k
                inParent(contour, *test);
313
936k
                return;
314
936k
            }
315
974k
        }
316
        // move parent's children into contour's children if contained by contour
317
35.4k
        for (auto iter = parent.fChildren.begin(); iter != parent.fChildren.end(); ) {
318
18.1k
            if (contour.fBounds.contains((*iter)->fBounds)) {
319
10.9k
                contour.fChildren.push_back(*iter);
320
10.9k
                iter = parent.fChildren.erase(iter);
321
10.9k
                continue;
322
10.9k
            }
323
7.22k
            ++iter;
324
7.22k
        }
325
17.3k
        parent.fChildren.push_back(&contour);
326
17.3k
    }
327
328
17.1k
    bool checkContainerChildren(Contour* parent, Contour* child) {
329
17.1k
        for (auto grandChild : child->fChildren) {
330
15.6k
            if (!checkContainerChildren(child, grandChild)) {
331
271
                return false;
332
271
            }
333
15.6k
        }
334
16.9k
        if (parent) {
335
15.4k
            if (!containerContains(*parent, *child)) {
336
32
                return false;
337
32
            }
338
15.4k
        }
339
16.8k
        return true;
340
16.9k
    }
341
342
16.7k
    bool markReverse(Contour* parent, Contour* child) {
343
16.7k
        bool reversed = false;
344
16.7k
        for (auto grandChild : child->fChildren) {
345
15.2k
            reversed |= markReverse(grandChild->fContained ? child : parent, grandChild);
346
15.2k
        }
347
348
16.7k
        child->fDirection = getDirection(*child);
349
16.7k
        if (parent && parent->fDirection == child->fDirection) {
350
2.37k
            child->fReverse = true;
351
2.37k
            child->fDirection = (Contour::Direction) -(int) child->fDirection;
352
2.37k
            return true;
353
2.37k
        }
354
14.3k
        return reversed;
355
16.7k
    }
356
357
254
    SkPath reverseMarkedContours(vector<Contour>& contours, SkPathFillType fillType) {
358
254
        SkPathPriv::Iterate iterate(fPath);
359
254
        auto iter = iterate.begin();
360
254
        int verbCount = 0;
361
362
254
        SkPathBuilder result;
363
254
        result.setFillType(fillType);
364
9.11k
        for (const Contour& contour : contours) {
365
9.11k
            SkPathBuilder reverse;
366
9.11k
            SkPathBuilder* temp = contour.fReverse ? &reverse : &result;
367
42.3k
            for (; iter != iterate.end() && verbCount < contour.fVerbEnd; ++iter, ++verbCount) {
368
33.2k
                auto [verb, pts, w] = *iter;
369
33.2k
                switch (verb) {
370
10.5k
                    case SkPathVerb::kMove:
371
10.5k
                        temp->moveTo(pts[0]);
372
10.5k
                        break;
373
9.83k
                    case SkPathVerb::kLine:
374
9.83k
                        temp->lineTo(pts[1]);
375
9.83k
                        break;
376
1.41k
                    case SkPathVerb::kQuad:
377
1.41k
                        temp->quadTo(pts[1], pts[2]);
378
1.41k
                        break;
379
1.04k
                    case SkPathVerb::kConic:
380
1.04k
                        temp->conicTo(pts[1], pts[2], *w);
381
1.04k
                        break;
382
771
                    case SkPathVerb::kCubic:
383
771
                        temp->cubicTo(pts[1], pts[2], pts[3]);
384
771
                        break;
385
9.66k
                    case SkPathVerb::kClose:
386
9.66k
                        temp->close();
387
9.66k
                        break;
388
33.2k
                }
389
33.2k
            }
390
9.11k
            if (contour.fReverse) {
391
2.37k
                SkASSERT(temp == &reverse);
392
2.37k
                SkPathPriv::ReverseAddPath(&result, reverse.detach());
393
2.37k
            }
394
9.11k
        }
395
254
        return result.detach();
396
254
    }
OpAsWinding::reverseMarkedContours(std::__1::vector<Contour, std::__1::allocator<Contour> >&, SkPathFillType)
Line
Count
Source
357
254
    SkPath reverseMarkedContours(vector<Contour>& contours, SkPathFillType fillType) {
358
254
        SkPathPriv::Iterate iterate(fPath);
359
254
        auto iter = iterate.begin();
360
254
        int verbCount = 0;
361
362
254
        SkPathBuilder result;
363
254
        result.setFillType(fillType);
364
9.11k
        for (const Contour& contour : contours) {
365
9.11k
            SkPathBuilder reverse;
366
9.11k
            SkPathBuilder* temp = contour.fReverse ? &reverse : &result;
367
42.3k
            for (; iter != iterate.end() && verbCount < contour.fVerbEnd; ++iter, ++verbCount) {
368
33.2k
                auto [verb, pts, w] = *iter;
369
33.2k
                switch (verb) {
370
10.5k
                    case SkPathVerb::kMove:
371
10.5k
                        temp->moveTo(pts[0]);
372
10.5k
                        break;
373
9.83k
                    case SkPathVerb::kLine:
374
9.83k
                        temp->lineTo(pts[1]);
375
9.83k
                        break;
376
1.41k
                    case SkPathVerb::kQuad:
377
1.41k
                        temp->quadTo(pts[1], pts[2]);
378
1.41k
                        break;
379
1.04k
                    case SkPathVerb::kConic:
380
1.04k
                        temp->conicTo(pts[1], pts[2], *w);
381
1.04k
                        break;
382
771
                    case SkPathVerb::kCubic:
383
771
                        temp->cubicTo(pts[1], pts[2], pts[3]);
384
771
                        break;
385
9.66k
                    case SkPathVerb::kClose:
386
9.66k
                        temp->close();
387
9.66k
                        break;
388
33.2k
                }
389
33.2k
            }
390
9.11k
            if (contour.fReverse) {
391
2.37k
                SkASSERT(temp == &reverse);
392
2.37k
                SkPathPriv::ReverseAddPath(&result, reverse.detach());
393
2.37k
            }
394
9.11k
        }
395
254
        return result.detach();
396
254
    }
Unexecuted instantiation: OpAsWinding::reverseMarkedContours(std::__1::vector<Contour, std::__1::allocator<Contour> >&, SkPathFillType)
397
398
private:
399
    const SkPath& fPath;
400
};
401
402
438
static bool set_result_path(SkPath* result, const SkPath& path, SkPathFillType fillType) {
403
438
    *result = path;
404
438
    result->setFillType(fillType);
405
438
    return true;
406
438
}
407
408
726
bool AsWinding(const SkPath& path, SkPath* result) {
409
726
    if (!path.isFinite()) {
410
2
        return false;
411
2
    }
412
724
    SkPathFillType fillType = path.getFillType();
413
724
    if (fillType == SkPathFillType::kWinding
414
724
            || fillType == SkPathFillType::kInverseWinding ) {
415
38
        return set_result_path(result, path, fillType);
416
38
    }
417
686
    fillType = path.isInverseFillType() ? SkPathFillType::kInverseWinding :
418
686
            SkPathFillType::kWinding;
419
686
    if (path.isEmpty() || path.isConvex()) {
420
29
        return set_result_path(result, path, fillType);
421
29
    }
422
    // count contours
423
657
    vector<Contour> contours;   // one per contour
424
657
    OpAsWinding winder(path);
425
657
    winder.contourBounds(&contours);
426
657
    if (contours.size() <= 1) {
427
19
        return set_result_path(result, path, fillType);
428
19
    }
429
    // create contour bounding box tree
430
638
    Contour sorted(SkRect(), 0, 0);
431
17.3k
    for (auto& contour : contours) {
432
17.3k
        winder.inParent(contour, sorted);
433
17.3k
    }
434
    // if sorted has no grandchildren, no child has to fix its children's winding
435
638
    if (std::all_of(sorted.fChildren.begin(), sorted.fChildren.end(),
436
841
            [](const Contour* contour) -> bool { return contour->fChildren.empty(); } )) {
437
14
        return set_result_path(result, path, fillType);
438
14
    }
439
    // starting with outermost and moving inward, see if one path contains another
440
1.51k
    for (auto contour : sorted.fChildren) {
441
1.51k
        winder.nextEdge(*contour, OpAsWinding::Edge::kInitial);
442
1.51k
        contour->fDirection = winder.getDirection(*contour);
443
1.51k
        if (!winder.checkContainerChildren(nullptr, contour)) {
444
32
            return false;
445
32
        }
446
1.51k
    }
447
    // starting with outermost and moving inward, mark paths to reverse
448
592
    bool reversed = false;
449
1.47k
    for (auto contour : sorted.fChildren) {
450
1.47k
        reversed |= winder.markReverse(nullptr, contour);
451
1.47k
    }
452
592
    if (!reversed) {
453
338
        return set_result_path(result, path, fillType);
454
338
    }
455
254
    *result = winder.reverseMarkedContours(contours, fillType);
456
254
    return true;
457
592
}