Coverage Report

Created: 2024-09-14 07:19

/src/skia/src/core/SkPathPriv.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2015 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
8
#ifndef SkPathPriv_DEFINED
9
#define SkPathPriv_DEFINED
10
11
#include "include/core/SkArc.h"
12
#include "include/core/SkPath.h"
13
#include "include/core/SkPathBuilder.h"
14
#include "include/core/SkPathTypes.h"
15
#include "include/core/SkPoint.h"
16
#include "include/core/SkRect.h"
17
#include "include/core/SkRefCnt.h"
18
#include "include/core/SkScalar.h"
19
#include "include/core/SkTypes.h"
20
#include "include/private/SkIDChangeListener.h"
21
#include "include/private/SkPathRef.h"
22
#include "include/private/base/SkDebug.h"
23
#include "src/core/SkPathEnums.h"
24
25
#include <cstdint>
26
#include <iterator>
27
#include <utility>
28
29
class SkMatrix;
30
class SkRRect;
31
32
static_assert(0 == static_cast<int>(SkPathFillType::kWinding), "fill_type_mismatch");
33
static_assert(1 == static_cast<int>(SkPathFillType::kEvenOdd), "fill_type_mismatch");
34
static_assert(2 == static_cast<int>(SkPathFillType::kInverseWinding), "fill_type_mismatch");
35
static_assert(3 == static_cast<int>(SkPathFillType::kInverseEvenOdd), "fill_type_mismatch");
36
37
class SkPathPriv {
38
public:
39
    // skbug.com/9906: Not a perfect solution for W plane clipping, but 1/16384 is a
40
    // reasonable limit (roughly 5e-5)
41
    inline static constexpr SkScalar kW0PlaneDistance = 1.f / (1 << 14);
42
43
0
    static SkPathFirstDirection AsFirstDirection(SkPathDirection dir) {
44
0
        // since we agree numerically for the values in Direction, we can just cast.
45
0
        return (SkPathFirstDirection)dir;
46
0
    }
47
48
    /**
49
     *  Return the opposite of the specified direction. kUnknown is its own
50
     *  opposite.
51
     */
52
1.08k
    static SkPathFirstDirection OppositeFirstDirection(SkPathFirstDirection dir) {
53
1.08k
        static const SkPathFirstDirection gOppositeDir[] = {
54
1.08k
            SkPathFirstDirection::kCCW, SkPathFirstDirection::kCW, SkPathFirstDirection::kUnknown,
55
1.08k
        };
56
1.08k
        return gOppositeDir[(unsigned)dir];
57
1.08k
    }
58
59
    /**
60
     *  Tries to compute the direction of the outer-most non-degenerate
61
     *  contour. If it can be computed, return that direction. If it cannot be determined,
62
     *  or the contour is known to be convex, return kUnknown. If the direction was determined,
63
     *  it is cached to make subsequent calls return quickly.
64
     */
65
    static SkPathFirstDirection ComputeFirstDirection(const SkPath&);
66
67
343
    static bool IsClosedSingleContour(const SkPath& path) {
68
343
        int verbCount = path.countVerbs();
69
343
        if (verbCount == 0)
70
0
            return false;
71
343
        int moveCount = 0;
72
343
        auto verbs = path.fPathRef->verbsBegin();
73
4.15k
        for (int i = 0; i < verbCount; i++) {
74
4.14k
            switch (verbs[i]) {
75
611
                case SkPath::Verb::kMove_Verb:
76
611
                    moveCount += 1;
77
611
                    if (moveCount > 1) {
78
268
                        return false;
79
268
                    }
80
343
                    break;
81
343
                case SkPath::Verb::kClose_Verb:
82
57
                    if (i == verbCount - 1) {
83
0
                        return true;
84
0
                    }
85
57
                    return false;
86
3.47k
                default: break;
87
4.14k
            }
88
4.14k
        }
89
18
        return false;
90
343
    }
91
92
    // In some scenarios (e.g. fill or convexity checking all but the last leading move to are
93
    // irrelevant to behavior). SkPath::injectMoveToIfNeeded should ensure that this is always at
94
    // least 1.
95
14.0M
    static int LeadingMoveToCount(const SkPath& path) {
96
14.0M
        int verbCount = path.countVerbs();
97
14.0M
        auto verbs = path.fPathRef->verbsBegin();
98
41.1M
        for (int i = 0; i < verbCount; i++) {
99
40.8M
            if (verbs[i] != SkPath::Verb::kMove_Verb) {
100
13.7M
                return i;
101
13.7M
            }
102
40.8M
        }
103
285k
        return verbCount; // path is all move verbs
104
14.0M
    }
105
106
361
    static void AddGenIDChangeListener(const SkPath& path, sk_sp<SkIDChangeListener> listener) {
107
361
        path.fPathRef->addGenIDChangeListener(std::move(listener));
108
361
    }
109
110
    /**
111
     * This returns true for a rect that has a move followed by 3 or 4 lines and a close. If
112
     * 'isSimpleFill' is true, an uncloseed rect will also be accepted as long as it starts and
113
     * ends at the same corner. This does not permit degenerate line or point rectangles.
114
     */
115
    static bool IsSimpleRect(const SkPath& path, bool isSimpleFill, SkRect* rect,
116
                             SkPathDirection* direction, unsigned* start);
117
118
    /**
119
     * Creates a path from arc params using the semantics of SkCanvas::drawArc. This function
120
     * assumes empty ovals and zero sweeps have already been filtered out.
121
     */
122
    static void CreateDrawArcPath(SkPath* path, const SkArc& arc, bool isFillNoPathEffect);
123
124
    /**
125
     * Determines whether an arc produced by CreateDrawArcPath will be convex. Assumes a non-empty
126
     * oval.
127
     */
128
    static bool DrawArcIsConvex(SkScalar sweepAngle, SkArc::Type arcType, bool isFillNoPathEffect);
129
130
6.17M
    static void ShrinkToFit(SkPath* path) {
131
6.17M
        path->shrinkToFit();
132
6.17M
    }
133
134
    /**
135
     * Returns a C++11-iterable object that traverses a path's verbs in order. e.g:
136
     *
137
     *   for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
138
     *       ...
139
     *   }
140
     */
141
    struct Verbs {
142
    public:
143
0
        Verbs(const SkPath& path) : fPathRef(path.fPathRef.get()) {}
144
        struct Iter {
145
0
            void operator++() { fVerb++; }
146
0
            bool operator!=(const Iter& b) { return fVerb != b.fVerb; }
147
0
            SkPath::Verb operator*() { return static_cast<SkPath::Verb>(*fVerb); }
148
            const uint8_t* fVerb;
149
        };
150
0
        Iter begin() { return Iter{fPathRef->verbsBegin()}; }
151
0
        Iter end() { return Iter{fPathRef->verbsEnd()}; }
152
    private:
153
        Verbs(const Verbs&) = delete;
154
        Verbs& operator=(const Verbs&) = delete;
155
        SkPathRef* fPathRef;
156
    };
157
158
    /**
159
      * Iterates through a raw range of path verbs, points, and conics. All values are returned
160
      * unaltered.
161
      *
162
      * NOTE: This class's definition will be moved into SkPathPriv once RangeIter is removed.
163
    */
164
    using RangeIter = SkPath::RangeIter;
165
166
    /**
167
     * Iterable object for traversing verbs, points, and conic weights in a path:
168
     *
169
     *   for (auto [verb, pts, weights] : SkPathPriv::Iterate(skPath)) {
170
     *       ...
171
     *   }
172
     */
173
    struct Iterate {
174
    public:
175
        Iterate(const SkPath& path)
176
                : Iterate(path.fPathRef->verbsBegin(),
177
                          // Don't allow iteration through non-finite points.
178
                          (!path.isFinite()) ? path.fPathRef->verbsBegin()
179
                                             : path.fPathRef->verbsEnd(),
180
20.6M
                          path.fPathRef->points(), path.fPathRef->conicWeights()) {
181
20.6M
        }
182
        Iterate(const uint8_t* verbsBegin, const uint8_t* verbsEnd, const SkPoint* points,
183
                const SkScalar* weights)
184
20.6M
                : fVerbsBegin(verbsBegin), fVerbsEnd(verbsEnd), fPoints(points), fWeights(weights) {
185
20.6M
        }
186
12.9M
        SkPath::RangeIter begin() { return {fVerbsBegin, fPoints, fWeights}; }
187
19.0M
        SkPath::RangeIter end() { return {fVerbsEnd, nullptr, nullptr}; }
188
    private:
189
        const uint8_t* fVerbsBegin;
190
        const uint8_t* fVerbsEnd;
191
        const SkPoint* fPoints;
192
        const SkScalar* fWeights;
193
    };
194
195
    /**
196
     * Returns a pointer to the verb data.
197
     */
198
9.26k
    static const uint8_t* VerbData(const SkPath& path) {
199
9.26k
        return path.fPathRef->verbsBegin();
200
9.26k
    }
201
202
    /** Returns a raw pointer to the path points */
203
9.26k
    static const SkPoint* PointData(const SkPath& path) {
204
9.26k
        return path.fPathRef->points();
205
9.26k
    }
206
207
    /** Returns the number of conic weights in the path */
208
54.8k
    static int ConicWeightCnt(const SkPath& path) {
209
54.8k
        return path.fPathRef->countWeights();
210
54.8k
    }
211
212
    /** Returns a raw pointer to the path conic weights. */
213
9.26k
    static const SkScalar* ConicWeightData(const SkPath& path) {
214
9.26k
        return path.fPathRef->conicWeights();
215
9.26k
    }
216
217
    /** Returns true if the underlying SkPathRef has one single owner. */
218
0
    static bool TestingOnly_unique(const SkPath& path) {
219
0
        return path.fPathRef->unique();
220
0
    }
221
222
    // Won't be needed once we can make path's immutable (with their bounds always computed)
223
0
    static bool HasComputedBounds(const SkPath& path) {
224
0
        return path.hasComputedBounds();
225
0
    }
226
227
    /** Returns true if constructed by addCircle(), addOval(); and in some cases,
228
     addRoundRect(), addRRect(). SkPath constructed with conicTo() or rConicTo() will not
229
     return true though SkPath draws oval.
230
231
     rect receives bounds of oval.
232
     dir receives SkPathDirection of oval: kCW_Direction if clockwise, kCCW_Direction if
233
     counterclockwise.
234
     start receives start of oval: 0 for top, 1 for right, 2 for bottom, 3 for left.
235
236
     rect, dir, and start are unmodified if oval is not found.
237
238
     Triggers performance optimizations on some GPU surface implementations.
239
240
     @param rect   storage for bounding SkRect of oval; may be nullptr
241
     @param dir    storage for SkPathDirection; may be nullptr
242
     @param start  storage for start of oval; may be nullptr
243
     @return       true if SkPath was constructed by method that reduces to oval
244
     */
245
165k
    static bool IsOval(const SkPath& path, SkRect* rect, SkPathDirection* dir, unsigned* start) {
246
165k
        bool isCCW = false;
247
165k
        bool result = path.fPathRef->isOval(rect, &isCCW, start);
248
165k
        if (dir && result) {
249
19
            *dir = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW;
250
19
        }
251
165k
        return result;
252
165k
    }
253
254
    /** Returns true if constructed by addRoundRect(), addRRect(); and if construction
255
     is not empty, not SkRect, and not oval. SkPath constructed with other calls
256
     will not return true though SkPath draws SkRRect.
257
258
     rrect receives bounds of SkRRect.
259
     dir receives SkPathDirection of oval: kCW_Direction if clockwise, kCCW_Direction if
260
     counterclockwise.
261
     start receives start of SkRRect: 0 for top, 1 for right, 2 for bottom, 3 for left.
262
263
     rrect, dir, and start are unmodified if SkRRect is not found.
264
265
     Triggers performance optimizations on some GPU surface implementations.
266
267
     @param rrect  storage for bounding SkRect of SkRRect; may be nullptr
268
     @param dir    storage for SkPathDirection; may be nullptr
269
     @param start  storage for start of SkRRect; may be nullptr
270
     @return       true if SkPath contains only SkRRect
271
     */
272
    static bool IsRRect(const SkPath& path, SkRRect* rrect, SkPathDirection* dir,
273
165k
                        unsigned* start) {
274
165k
        bool isCCW = false;
275
165k
        bool result = path.fPathRef->isRRect(rrect, &isCCW, start);
276
165k
        if (dir && result) {
277
446
            *dir = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW;
278
446
        }
279
165k
        return result;
280
165k
    }
281
282
    /**
283
     *  Sometimes in the drawing pipeline, we have to perform math on path coordinates, even after
284
     *  the path is in device-coordinates. Tessellation and clipping are two examples. Usually this
285
     *  is pretty modest, but it can involve subtracting/adding coordinates, or multiplying by
286
     *  small constants (e.g. 2,3,4). To try to preflight issues where these optionations could turn
287
     *  finite path values into infinities (or NaNs), we allow the upper drawing code to reject
288
     *  the path if its bounds (in device coordinates) is too close to max float.
289
     */
290
349k
    static bool TooBigForMath(const SkRect& bounds) {
291
        // This value is just a guess. smaller is safer, but we don't want to reject largish paths
292
        // that we don't have to.
293
349k
        constexpr SkScalar scale_down_to_allow_for_small_multiplies = 0.25f;
294
349k
        constexpr SkScalar max = SK_ScalarMax * scale_down_to_allow_for_small_multiplies;
295
296
        // use ! expression so we return true if bounds contains NaN
297
349k
        return !(bounds.fLeft >= -max && bounds.fTop >= -max &&
298
349k
                 bounds.fRight <= max && bounds.fBottom <= max);
299
349k
    }
300
327k
    static bool TooBigForMath(const SkPath& path) {
301
327k
        return TooBigForMath(path.getBounds());
302
327k
    }
303
304
    // Returns number of valid points for each SkPath::Iter verb
305
119M
    static int PtsInIter(unsigned verb) {
306
119M
        static const uint8_t gPtsInVerb[] = {
307
119M
            1,  // kMove    pts[0]
308
119M
            2,  // kLine    pts[0..1]
309
119M
            3,  // kQuad    pts[0..2]
310
119M
            3,  // kConic   pts[0..2]
311
119M
            4,  // kCubic   pts[0..3]
312
119M
            0,  // kClose
313
119M
            0   // kDone
314
119M
        };
315
316
119M
        SkASSERT(verb < std::size(gPtsInVerb));
317
119M
        return gPtsInVerb[verb];
318
119M
    }
319
320
    // Returns number of valid points for each verb, not including the "starter"
321
    // point that the Iterator adds for line/quad/conic/cubic
322
108M
    static int PtsInVerb(unsigned verb) {
323
108M
        static const uint8_t gPtsInVerb[] = {
324
108M
            1,  // kMove    pts[0]
325
108M
            1,  // kLine    pts[0..1]
326
108M
            2,  // kQuad    pts[0..2]
327
108M
            2,  // kConic   pts[0..2]
328
108M
            3,  // kCubic   pts[0..3]
329
108M
            0,  // kClose
330
108M
            0   // kDone
331
108M
        };
332
333
108M
        SkASSERT(verb < std::size(gPtsInVerb));
334
108M
        return gPtsInVerb[verb];
335
108M
    }
336
337
    static bool IsAxisAligned(const SkPath& path);
338
339
14.1k
    static bool AllPointsEq(const SkPoint pts[], int count) {
340
19.3k
        for (int i = 1; i < count; ++i) {
341
17.4k
            if (pts[0] != pts[i]) {
342
12.2k
                return false;
343
12.2k
            }
344
17.4k
        }
345
1.91k
        return true;
346
14.1k
    }
347
348
0
    static int LastMoveToIndex(const SkPath& path) { return path.fLastMoveToIndex; }
349
350
    static bool IsRectContour(const SkPath&, bool allowPartial, int* currVerb,
351
                              const SkPoint** ptsPtr, bool* isClosed, SkPathDirection* direction,
352
                              SkRect* rect);
353
354
    /** Returns true if SkPath is equivalent to nested SkRect pair when filled.
355
     If false, rect and dirs are unchanged.
356
     If true, rect and dirs are written to if not nullptr:
357
     setting rect[0] to outer SkRect, and rect[1] to inner SkRect;
358
     setting dirs[0] to SkPathDirection of outer SkRect, and dirs[1] to SkPathDirection of
359
     inner SkRect.
360
361
     @param rect  storage for SkRect pair; may be nullptr
362
     @param dirs  storage for SkPathDirection pair; may be nullptr
363
     @return      true if SkPath contains nested SkRect pair
364
     */
365
    static bool IsNestedFillRects(const SkPath&, SkRect rect[2],
366
                                  SkPathDirection dirs[2] = nullptr);
367
368
0
    static bool IsInverseFillType(SkPathFillType fill) {
369
0
        return (static_cast<int>(fill) & 2) != 0;
370
0
    }
371
372
    /** Returns equivalent SkPath::FillType representing SkPath fill inside its bounds.
373
     .
374
375
     @param fill  one of: kWinding_FillType, kEvenOdd_FillType,
376
     kInverseWinding_FillType, kInverseEvenOdd_FillType
377
     @return      fill, or kWinding_FillType or kEvenOdd_FillType if fill is inverted
378
     */
379
0
    static SkPathFillType ConvertToNonInverseFillType(SkPathFillType fill) {
380
0
        return (SkPathFillType)(static_cast<int>(fill) & 1);
381
0
    }
382
383
    /**
384
     *  If needed (to not blow-up under a perspective matrix), clip the path, returning the
385
     *  answer in "result", and return true.
386
     *
387
     *  Note result might be empty (if the path was completely clipped out).
388
     *
389
     *  If no clipping is needed, returns false and "result" is left unchanged.
390
     */
391
    static bool PerspectiveClip(const SkPath& src, const SkMatrix&, SkPath* result);
392
393
    /**
394
     * Gets the number of GenIDChangeListeners. If another thread has access to this path then
395
     * this may be stale before return and only indicates that the count was the return value
396
     * at some point during the execution of the function.
397
     */
398
    static int GenIDChangeListenersCount(const SkPath&);
399
400
0
    static void UpdatePathPoint(SkPath* path, int index, const SkPoint& pt) {
401
0
        SkASSERT(index < path->countPoints());
402
0
        SkPathRef::Editor ed(&path->fPathRef);
403
0
        ed.writablePoints()[index] = pt;
404
0
        path->dirtyAfterEdit();
405
0
    }
406
407
0
    static SkPathConvexity GetConvexity(const SkPath& path) {
408
0
        return path.getConvexity();
409
0
    }
410
0
    static SkPathConvexity GetConvexityOrUnknown(const SkPath& path) {
411
0
        return path.getConvexityOrUnknown();
412
0
    }
413
7.57k
    static void SetConvexity(const SkPath& path, SkPathConvexity c) {
414
7.57k
        path.setConvexity(c);
415
7.57k
    }
416
0
    static void ForceComputeConvexity(const SkPath& path) {
417
0
        path.setConvexity(SkPathConvexity::kUnknown);
418
0
        (void)path.isConvex();
419
0
    }
420
421
2.32k
    static void ReverseAddPath(SkPathBuilder* builder, const SkPath& reverseMe) {
422
2.32k
        builder->privateReverseAddPath(reverseMe);
423
2.32k
    }
424
425
    static SkPath MakePath(const SkPathVerbAnalysis& analysis,
426
                           const SkPoint points[],
427
                           const uint8_t verbs[],
428
                           int verbCount,
429
                           const SkScalar conics[],
430
                           SkPathFillType fillType,
431
1.98k
                           bool isVolatile) {
432
1.98k
        return SkPath::MakeInternal(analysis, points, verbs, verbCount, conics, fillType,
433
1.98k
                                    isVolatile);
434
1.98k
    }
435
};
436
437
// Lightweight variant of SkPath::Iter that only returns segments (e.g. lines/conics).
438
// Does not return kMove or kClose.
439
// Always "auto-closes" each contour.
440
// Roughly the same as SkPath::Iter(path, true), but does not return moves or closes
441
//
442
class SkPathEdgeIter {
443
    const uint8_t*  fVerbs;
444
    const uint8_t*  fVerbsStop;
445
    const SkPoint*  fPts;
446
    const SkPoint*  fMoveToPtr;
447
    const SkScalar* fConicWeights;
448
    SkPoint         fScratch[2];    // for auto-close lines
449
    bool            fNeedsCloseLine;
450
    bool            fNextIsNewContour;
451
    SkDEBUGCODE(bool fIsConic;)
452
453
    enum {
454
        kIllegalEdgeValue = 99
455
    };
456
457
public:
458
    SkPathEdgeIter(const SkPath& path);
459
460
10.3M
    SkScalar conicWeight() const {
461
10.3M
        SkASSERT(fIsConic);
462
10.3M
        return *fConicWeights;
463
10.3M
    }
464
465
    enum class Edge {
466
        kLine  = SkPath::kLine_Verb,
467
        kQuad  = SkPath::kQuad_Verb,
468
        kConic = SkPath::kConic_Verb,
469
        kCubic = SkPath::kCubic_Verb,
470
    };
471
472
0
    static SkPath::Verb EdgeToVerb(Edge e) {
473
0
        return SkPath::Verb(e);
474
0
    }
475
476
    struct Result {
477
        const SkPoint*  fPts;   // points for the segment, or null if done
478
        Edge            fEdge;
479
        bool            fIsNewContour;
480
481
        // Returns true when it holds an Edge, false when the path is done.
482
194M
        explicit operator bool() { return fPts != nullptr; }
483
    };
484
485
194M
    Result next() {
486
194M
        auto closeline = [&]() {
487
45.1M
            fScratch[0] = fPts[-1];
488
45.1M
            fScratch[1] = *fMoveToPtr;
489
45.1M
            fNeedsCloseLine = false;
490
45.1M
            fNextIsNewContour = true;
491
45.1M
            return Result{ fScratch, Edge::kLine, false };
492
45.1M
        };
493
494
442M
        for (;;) {
495
442M
            SkASSERT(fVerbs <= fVerbsStop);
496
442M
            if (fVerbs == fVerbsStop) {
497
23.3M
                return fNeedsCloseLine
498
23.3M
                    ? closeline()
499
23.3M
                    : Result{ nullptr, Edge(kIllegalEdgeValue), false };
500
23.3M
            }
501
502
0
            SkDEBUGCODE(fIsConic = false;)
503
504
419M
            const auto v = *fVerbs++;
505
419M
            switch (v) {
506
274M
                case SkPath::kMove_Verb: {
507
274M
                    if (fNeedsCloseLine) {
508
27.3M
                        auto res = closeline();
509
27.3M
                        fMoveToPtr = fPts++;
510
27.3M
                        return res;
511
27.3M
                    }
512
247M
                    fMoveToPtr = fPts++;
513
247M
                    fNextIsNewContour = true;
514
247M
                } break;
515
9.33M
                case SkPath::kClose_Verb:
516
9.33M
                    if (fNeedsCloseLine) return closeline();
517
1.10M
                    break;
518
135M
                default: {
519
                    // Actual edge.
520
135M
                    const int pts_count = (v+2) / 2,
521
135M
                              cws_count = (v & (v-1)) / 2;
522
135M
                    SkASSERT(pts_count == SkPathPriv::PtsInIter(v) - 1);
523
524
135M
                    fNeedsCloseLine = true;
525
135M
                    fPts           += pts_count;
526
135M
                    fConicWeights  += cws_count;
527
528
135M
                    SkDEBUGCODE(fIsConic = (v == SkPath::kConic_Verb);)
529
135M
                    SkASSERT(fIsConic == (cws_count > 0));
530
531
135M
                    bool isNewContour = fNextIsNewContour;
532
135M
                    fNextIsNewContour = false;
533
135M
                    return { &fPts[-(pts_count + 1)], Edge(v), isNewContour };
534
9.33M
                }
535
419M
            }
536
419M
        }
537
194M
    }
SkPathEdgeIter::next()
Line
Count
Source
485
194M
    Result next() {
486
194M
        auto closeline = [&]() {
487
194M
            fScratch[0] = fPts[-1];
488
194M
            fScratch[1] = *fMoveToPtr;
489
194M
            fNeedsCloseLine = false;
490
194M
            fNextIsNewContour = true;
491
194M
            return Result{ fScratch, Edge::kLine, false };
492
194M
        };
493
494
442M
        for (;;) {
495
442M
            SkASSERT(fVerbs <= fVerbsStop);
496
442M
            if (fVerbs == fVerbsStop) {
497
23.3M
                return fNeedsCloseLine
498
23.3M
                    ? closeline()
499
23.3M
                    : Result{ nullptr, Edge(kIllegalEdgeValue), false };
500
23.3M
            }
501
502
419M
            SkDEBUGCODE(fIsConic = false;)
503
504
419M
            const auto v = *fVerbs++;
505
419M
            switch (v) {
506
274M
                case SkPath::kMove_Verb: {
507
274M
                    if (fNeedsCloseLine) {
508
27.3M
                        auto res = closeline();
509
27.3M
                        fMoveToPtr = fPts++;
510
27.3M
                        return res;
511
27.3M
                    }
512
247M
                    fMoveToPtr = fPts++;
513
247M
                    fNextIsNewContour = true;
514
247M
                } break;
515
9.33M
                case SkPath::kClose_Verb:
516
9.33M
                    if (fNeedsCloseLine) return closeline();
517
1.10M
                    break;
518
135M
                default: {
519
                    // Actual edge.
520
135M
                    const int pts_count = (v+2) / 2,
521
135M
                              cws_count = (v & (v-1)) / 2;
522
135M
                    SkASSERT(pts_count == SkPathPriv::PtsInIter(v) - 1);
523
524
135M
                    fNeedsCloseLine = true;
525
135M
                    fPts           += pts_count;
526
135M
                    fConicWeights  += cws_count;
527
528
135M
                    SkDEBUGCODE(fIsConic = (v == SkPath::kConic_Verb);)
529
135M
                    SkASSERT(fIsConic == (cws_count > 0));
530
531
135M
                    bool isNewContour = fNextIsNewContour;
532
135M
                    fNextIsNewContour = false;
533
135M
                    return { &fPts[-(pts_count + 1)], Edge(v), isNewContour };
534
9.33M
                }
535
419M
            }
536
419M
        }
537
194M
    }
Unexecuted instantiation: SkPathEdgeIter::next()
538
};
539
540
#endif