xref: /third_party/skia/src/core/SkPath.cpp (revision cb93a386)
1/*
2 * Copyright 2006 The Android Open Source Project
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#include "include/core/SkPath.h"
9
10#include "include/core/SkData.h"
11#include "include/core/SkMath.h"
12#include "include/core/SkPathBuilder.h"
13#include "include/core/SkRRect.h"
14#include "include/private/SkMacros.h"
15#include "include/private/SkPathRef.h"
16#include "include/private/SkTo.h"
17#include "src/core/SkBuffer.h"
18#include "src/core/SkCubicClipper.h"
19#include "src/core/SkGeometry.h"
20#include "src/core/SkMatrixPriv.h"
21#include "src/core/SkPathMakers.h"
22#include "src/core/SkPathPriv.h"
23#include "src/core/SkPointPriv.h"
24#include "src/core/SkSafeMath.h"
25#include "src/core/SkTLazy.h"
26// need SkDVector
27#include "src/pathops/SkPathOpsPoint.h"
28
29#include <cmath>
30#include <utility>
31
32struct SkPath_Storage_Equivalent {
33    void*    fPtr;
34    int32_t  fIndex;
35    uint32_t fFlags;
36};
37
38static_assert(sizeof(SkPath) == sizeof(SkPath_Storage_Equivalent),
39              "Please keep an eye on SkPath packing.");
40
41static float poly_eval(float A, float B, float C, float t) {
42    return (A * t + B) * t + C;
43}
44
45static float poly_eval(float A, float B, float C, float D, float t) {
46    return ((A * t + B) * t + C) * t + D;
47}
48
49////////////////////////////////////////////////////////////////////////////
50
51/**
52 *  Path.bounds is defined to be the bounds of all the control points.
53 *  If we called bounds.join(r) we would skip r if r was empty, which breaks
54 *  our promise. Hence we have a custom joiner that doesn't look at emptiness
55 */
56static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) {
57    dst->fLeft = std::min(dst->fLeft, src.fLeft);
58    dst->fTop = std::min(dst->fTop, src.fTop);
59    dst->fRight = std::max(dst->fRight, src.fRight);
60    dst->fBottom = std::max(dst->fBottom, src.fBottom);
61}
62
63static bool is_degenerate(const SkPath& path) {
64    return (path.countVerbs() - SkPathPriv::LeadingMoveToCount(path)) == 0;
65}
66
67class SkAutoDisableDirectionCheck {
68public:
69    SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) {
70        fSaved = static_cast<SkPathFirstDirection>(fPath->getFirstDirection());
71    }
72
73    ~SkAutoDisableDirectionCheck() {
74        fPath->setFirstDirection(fSaved);
75    }
76
77private:
78    SkPath*                 fPath;
79    SkPathFirstDirection    fSaved;
80};
81
82/*  This class's constructor/destructor bracket a path editing operation. It is
83    used when we know the bounds of the amount we are going to add to the path
84    (usually a new contour, but not required).
85
86    It captures some state about the path up front (i.e. if it already has a
87    cached bounds), and then if it can, it updates the cache bounds explicitly,
88    avoiding the need to revisit all of the points in getBounds().
89
90    It also notes if the path was originally degenerate, and if so, sets
91    isConvex to true. Thus it can only be used if the contour being added is
92    convex.
93 */
94class SkAutoPathBoundsUpdate {
95public:
96    SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fPath(path), fRect(r) {
97        // Cannot use fRect for our bounds unless we know it is sorted
98        fRect.sort();
99        // Mark the path's bounds as dirty if (1) they are, or (2) the path
100        // is non-finite, and therefore its bounds are not meaningful
101        fHasValidBounds = path->hasComputedBounds() && path->isFinite();
102        fEmpty = path->isEmpty();
103        if (fHasValidBounds && !fEmpty) {
104            joinNoEmptyChecks(&fRect, fPath->getBounds());
105        }
106        fDegenerate = is_degenerate(*path);
107    }
108
109    ~SkAutoPathBoundsUpdate() {
110        fPath->setConvexity(fDegenerate ? SkPathConvexity::kConvex
111                                            : SkPathConvexity::kUnknown);
112        if ((fEmpty || fHasValidBounds) && fRect.isFinite()) {
113            fPath->setBounds(fRect);
114        }
115    }
116
117private:
118    SkPath* fPath;
119    SkRect  fRect;
120    bool    fHasValidBounds;
121    bool    fDegenerate;
122    bool    fEmpty;
123};
124
125////////////////////////////////////////////////////////////////////////////
126
127/*
128    Stores the verbs and points as they are given to us, with exceptions:
129    - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic
130    - we insert a Move(0,0) if Line | Quad | Cubic is our first command
131
132    The iterator does more cleanup, especially if forceClose == true
133    1. If we encounter degenerate segments, remove them
134    2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
135    3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2
136    4. if we encounter Line | Quad | Cubic after Close, cons up a Move
137*/
138
139////////////////////////////////////////////////////////////////////////////
140
141// flag to require a moveTo if we begin with something else, like lineTo etc.
142// This will also be the value of lastMoveToIndex for a single contour
143// ending with close, so countVerbs needs to be checked against 0.
144#define INITIAL_LASTMOVETOINDEX_VALUE   ~0
145
146SkPath::SkPath()
147    : fPathRef(SkPathRef::CreateEmpty()) {
148    this->resetFields();
149    fIsVolatile = false;
150}
151
152SkPath::SkPath(sk_sp<SkPathRef> pr, SkPathFillType ft, bool isVolatile, SkPathConvexity ct,
153               SkPathFirstDirection firstDirection)
154    : fPathRef(std::move(pr))
155    , fLastMoveToIndex(INITIAL_LASTMOVETOINDEX_VALUE)
156    , fConvexity((uint8_t)ct)
157    , fFirstDirection((uint8_t)firstDirection)
158    , fFillType((unsigned)ft)
159    , fIsVolatile(isVolatile)
160{}
161
162void SkPath::resetFields() {
163    //fPathRef is assumed to have been emptied by the caller.
164    fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
165    fFillType = SkToU8(SkPathFillType::kWinding);
166    this->setConvexity(SkPathConvexity::kUnknown);
167    this->setFirstDirection(SkPathFirstDirection::kUnknown);
168
169    // We don't touch Android's fSourcePath.  It's used to track texture garbage collection, so we
170    // don't want to muck with it if it's been set to something non-nullptr.
171}
172
173SkPath::SkPath(const SkPath& that)
174    : fPathRef(SkRef(that.fPathRef.get())) {
175    this->copyFields(that);
176    SkDEBUGCODE(that.validate();)
177}
178
179SkPath::~SkPath() {
180    SkDEBUGCODE(this->validate();)
181}
182
183SkPath& SkPath::operator=(const SkPath& that) {
184    SkDEBUGCODE(that.validate();)
185
186    if (this != &that) {
187        fPathRef.reset(SkRef(that.fPathRef.get()));
188        this->copyFields(that);
189    }
190    SkDEBUGCODE(this->validate();)
191    return *this;
192}
193
194void SkPath::copyFields(const SkPath& that) {
195    //fPathRef is assumed to have been set by the caller.
196    fLastMoveToIndex = that.fLastMoveToIndex;
197    fFillType        = that.fFillType;
198    fIsVolatile      = that.fIsVolatile;
199
200    // Non-atomic assignment of atomic values.
201    this->setConvexity(that.getConvexityOrUnknown());
202    this->setFirstDirection(that.getFirstDirection());
203}
204
205bool operator==(const SkPath& a, const SkPath& b) {
206    // note: don't need to look at isConvex or bounds, since just comparing the
207    // raw data is sufficient.
208    return &a == &b ||
209        (a.fFillType == b.fFillType && *a.fPathRef == *b.fPathRef);
210}
211
212void SkPath::swap(SkPath& that) {
213    if (this != &that) {
214        fPathRef.swap(that.fPathRef);
215        std::swap(fLastMoveToIndex, that.fLastMoveToIndex);
216
217        const auto ft = fFillType;
218        fFillType = that.fFillType;
219        that.fFillType = ft;
220
221        const auto iv = fIsVolatile;
222        fIsVolatile = that.fIsVolatile;
223        that.fIsVolatile = iv;
224
225        // Non-atomic swaps of atomic values.
226        SkPathConvexity c = this->getConvexityOrUnknown();
227        this->setConvexity(that.getConvexityOrUnknown());
228        that.setConvexity(c);
229
230        SkPathFirstDirection fd = this->getFirstDirection();
231        this->setFirstDirection(that.getFirstDirection());
232        that.setFirstDirection(fd);
233    }
234}
235
236bool SkPath::isInterpolatable(const SkPath& compare) const {
237    // need the same structure (verbs, conicweights) and same point-count
238    return fPathRef->fPoints.count() == compare.fPathRef->fPoints.count() &&
239           fPathRef->fVerbs == compare.fPathRef->fVerbs &&
240           fPathRef->fConicWeights == compare.fPathRef->fConicWeights;
241}
242
243bool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const {
244    int pointCount = fPathRef->countPoints();
245    if (pointCount != ending.fPathRef->countPoints()) {
246        return false;
247    }
248    if (!pointCount) {
249        return true;
250    }
251    out->reset();
252    out->addPath(*this);
253    fPathRef->interpolate(*ending.fPathRef, weight, out->fPathRef.get());
254    return true;
255}
256
257static inline bool check_edge_against_rect(const SkPoint& p0,
258                                           const SkPoint& p1,
259                                           const SkRect& rect,
260                                           SkPathFirstDirection dir) {
261    const SkPoint* edgeBegin;
262    SkVector v;
263    if (SkPathFirstDirection::kCW == dir) {
264        v = p1 - p0;
265        edgeBegin = &p0;
266    } else {
267        v = p0 - p1;
268        edgeBegin = &p1;
269    }
270    if (v.fX || v.fY) {
271        // check the cross product of v with the vec from edgeBegin to each rect corner
272        SkScalar yL = v.fY * (rect.fLeft - edgeBegin->fX);
273        SkScalar xT = v.fX * (rect.fTop - edgeBegin->fY);
274        SkScalar yR = v.fY * (rect.fRight - edgeBegin->fX);
275        SkScalar xB = v.fX * (rect.fBottom - edgeBegin->fY);
276        if ((xT < yL) || (xT < yR) || (xB < yL) || (xB < yR)) {
277            return false;
278        }
279    }
280    return true;
281}
282
283bool SkPath::conservativelyContainsRect(const SkRect& rect) const {
284    // This only handles non-degenerate convex paths currently.
285    if (!this->isConvex()) {
286        return false;
287    }
288
289    SkPathFirstDirection direction = SkPathPriv::ComputeFirstDirection(*this);
290    if (direction == SkPathFirstDirection::kUnknown) {
291        return false;
292    }
293
294    SkPoint firstPt;
295    SkPoint prevPt;
296    int segmentCount = 0;
297    SkDEBUGCODE(int moveCnt = 0;)
298
299    for (auto [verb, pts, weight] : SkPathPriv::Iterate(*this)) {
300        if (verb == SkPathVerb::kClose || (segmentCount > 0 && verb == SkPathVerb::kMove)) {
301            // Closing the current contour; but since convexity is a precondition, it's the only
302            // contour that matters.
303            SkASSERT(moveCnt);
304            segmentCount++;
305            break;
306        } else if (verb == SkPathVerb::kMove) {
307            // A move at the start of the contour (or multiple leading moves, in which case we
308            // keep the last one before a non-move verb).
309            SkASSERT(!segmentCount);
310            SkDEBUGCODE(++moveCnt);
311            firstPt = prevPt = pts[0];
312        } else {
313            int pointCount = SkPathPriv::PtsInVerb((unsigned) verb);
314            SkASSERT(pointCount > 0);
315
316            if (!SkPathPriv::AllPointsEq(pts, pointCount + 1)) {
317                SkASSERT(moveCnt);
318                int nextPt = pointCount;
319                segmentCount++;
320
321                if (SkPathVerb::kConic == verb) {
322                    SkConic orig;
323                    orig.set(pts, *weight);
324                    SkPoint quadPts[5];
325                    int count = orig.chopIntoQuadsPOW2(quadPts, 1);
326                    SkASSERT_RELEASE(2 == count);
327
328                    if (!check_edge_against_rect(quadPts[0], quadPts[2], rect, direction)) {
329                        return false;
330                    }
331                    if (!check_edge_against_rect(quadPts[2], quadPts[4], rect, direction)) {
332                        return false;
333                    }
334                } else {
335                    if (!check_edge_against_rect(prevPt, pts[nextPt], rect, direction)) {
336                        return false;
337                    }
338                }
339                prevPt = pts[nextPt];
340            }
341        }
342    }
343
344    if (segmentCount) {
345        return check_edge_against_rect(prevPt, firstPt, rect, direction);
346    }
347    return false;
348}
349
350uint32_t SkPath::getGenerationID() const {
351    uint32_t genID = fPathRef->genID();
352#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
353    SkASSERT((unsigned)fFillType < (1 << (32 - SkPathPriv::kPathRefGenIDBitCnt)));
354    genID |= static_cast<uint32_t>(fFillType) << SkPathPriv::kPathRefGenIDBitCnt;
355#endif
356    return genID;
357}
358
359SkPath& SkPath::reset() {
360    SkDEBUGCODE(this->validate();)
361
362    fPathRef.reset(SkPathRef::CreateEmpty());
363    this->resetFields();
364    return *this;
365}
366
367SkPath& SkPath::rewind() {
368    SkDEBUGCODE(this->validate();)
369
370    SkPathRef::Rewind(&fPathRef);
371    this->resetFields();
372    return *this;
373}
374
375bool SkPath::isLastContourClosed() const {
376    int verbCount = fPathRef->countVerbs();
377    if (0 == verbCount) {
378        return false;
379    }
380    return kClose_Verb == fPathRef->atVerb(verbCount - 1);
381}
382
383bool SkPath::isLine(SkPoint line[2]) const {
384    int verbCount = fPathRef->countVerbs();
385
386    if (2 == verbCount) {
387        SkASSERT(kMove_Verb == fPathRef->atVerb(0));
388        if (kLine_Verb == fPathRef->atVerb(1)) {
389            SkASSERT(2 == fPathRef->countPoints());
390            if (line) {
391                const SkPoint* pts = fPathRef->points();
392                line[0] = pts[0];
393                line[1] = pts[1];
394            }
395            return true;
396        }
397    }
398    return false;
399}
400
401/*
402 Determines if path is a rect by keeping track of changes in direction
403 and looking for a loop either clockwise or counterclockwise.
404
405 The direction is computed such that:
406  0: vertical up
407  1: horizontal left
408  2: vertical down
409  3: horizontal right
410
411A rectangle cycles up/right/down/left or up/left/down/right.
412
413The test fails if:
414  The path is closed, and followed by a line.
415  A second move creates a new endpoint.
416  A diagonal line is parsed.
417  There's more than four changes of direction.
418  There's a discontinuity on the line (e.g., a move in the middle)
419  The line reverses direction.
420  The path contains a quadratic or cubic.
421  The path contains fewer than four points.
422 *The rectangle doesn't complete a cycle.
423 *The final point isn't equal to the first point.
424
425  *These last two conditions we relax if we have a 3-edge path that would
426   form a rectangle if it were closed (as we do when we fill a path)
427
428It's OK if the path has:
429  Several colinear line segments composing a rectangle side.
430  Single points on the rectangle side.
431
432The direction takes advantage of the corners found since opposite sides
433must travel in opposite directions.
434
435FIXME: Allow colinear quads and cubics to be treated like lines.
436FIXME: If the API passes fill-only, return true if the filled stroke
437       is a rectangle, though the caller failed to close the path.
438
439 directions values:
440    0x1 is set if the segment is horizontal
441    0x2 is set if the segment is moving to the right or down
442 thus:
443    two directions are opposites iff (dirA ^ dirB) == 0x2
444    two directions are perpendicular iff (dirA ^ dirB) == 0x1
445
446 */
447static int rect_make_dir(SkScalar dx, SkScalar dy) {
448    return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1);
449}
450
451bool SkPath::isRect(SkRect* rect, bool* isClosed, SkPathDirection* direction) const {
452    SkDEBUGCODE(this->validate();)
453    int currVerb = 0;
454    const SkPoint* pts = fPathRef->points();
455    return SkPathPriv::IsRectContour(*this, false, &currVerb, &pts, isClosed, direction, rect);
456}
457
458bool SkPath::isOval(SkRect* bounds) const {
459    return SkPathPriv::IsOval(*this, bounds, nullptr, nullptr);
460}
461
462bool SkPath::isRRect(SkRRect* rrect) const {
463    return SkPathPriv::IsRRect(*this, rrect, nullptr, nullptr);
464}
465
466int SkPath::countPoints() const {
467    return fPathRef->countPoints();
468}
469
470int SkPath::getPoints(SkPoint dst[], int max) const {
471    SkDEBUGCODE(this->validate();)
472
473    SkASSERT(max >= 0);
474    SkASSERT(!max || dst);
475    int count = std::min(max, fPathRef->countPoints());
476    sk_careful_memcpy(dst, fPathRef->points(), count * sizeof(SkPoint));
477    return fPathRef->countPoints();
478}
479
480SkPoint SkPath::getPoint(int index) const {
481    if ((unsigned)index < (unsigned)fPathRef->countPoints()) {
482        return fPathRef->atPoint(index);
483    }
484    return SkPoint::Make(0, 0);
485}
486
487int SkPath::countVerbs() const {
488    return fPathRef->countVerbs();
489}
490
491int SkPath::getVerbs(uint8_t dst[], int max) const {
492    SkDEBUGCODE(this->validate();)
493
494    SkASSERT(max >= 0);
495    SkASSERT(!max || dst);
496    int count = std::min(max, fPathRef->countVerbs());
497    if (count) {
498        memcpy(dst, fPathRef->verbsBegin(), count);
499    }
500    return fPathRef->countVerbs();
501}
502
503size_t SkPath::approximateBytesUsed() const {
504    size_t size = sizeof (SkPath);
505    if (fPathRef != nullptr) {
506        size += fPathRef->approximateBytesUsed();
507    }
508    return size;
509}
510
511bool SkPath::getLastPt(SkPoint* lastPt) const {
512    SkDEBUGCODE(this->validate();)
513
514    int count = fPathRef->countPoints();
515    if (count > 0) {
516        if (lastPt) {
517            *lastPt = fPathRef->atPoint(count - 1);
518        }
519        return true;
520    }
521    if (lastPt) {
522        lastPt->set(0, 0);
523    }
524    return false;
525}
526
527void SkPath::setPt(int index, SkScalar x, SkScalar y) {
528    SkDEBUGCODE(this->validate();)
529
530    int count = fPathRef->countPoints();
531    if (count <= index) {
532        return;
533    } else {
534        SkPathRef::Editor ed(&fPathRef);
535        ed.atPoint(index)->set(x, y);
536    }
537}
538
539void SkPath::setLastPt(SkScalar x, SkScalar y) {
540    SkDEBUGCODE(this->validate();)
541
542    int count = fPathRef->countPoints();
543    if (count == 0) {
544        this->moveTo(x, y);
545    } else {
546        SkPathRef::Editor ed(&fPathRef);
547        ed.atPoint(count-1)->set(x, y);
548    }
549}
550
551// This is the public-facing non-const setConvexity().
552void SkPath::setConvexity(SkPathConvexity c) {
553    fConvexity.store((uint8_t)c, std::memory_order_relaxed);
554}
555
556// Const hooks for working with fConvexity and fFirstDirection from const methods.
557void SkPath::setConvexity(SkPathConvexity c) const {
558    fConvexity.store((uint8_t)c, std::memory_order_relaxed);
559}
560void SkPath::setFirstDirection(SkPathFirstDirection d) const {
561    fFirstDirection.store((uint8_t)d, std::memory_order_relaxed);
562}
563SkPathFirstDirection SkPath::getFirstDirection() const {
564    return (SkPathFirstDirection)fFirstDirection.load(std::memory_order_relaxed);
565}
566
567bool SkPath::isConvexityAccurate() const {
568    SkPathConvexity convexity = this->getConvexityOrUnknown();
569    if (convexity != SkPathConvexity::kUnknown) {
570        auto conv = this->computeConvexity();
571        if (conv != convexity) {
572            SkASSERT(false);
573            return false;
574        }
575    }
576    return true;
577}
578
579SkPathConvexity SkPath::getConvexity() const {
580// Enable once we fix all the bugs
581//    SkDEBUGCODE(this->isConvexityAccurate());
582    SkPathConvexity convexity = this->getConvexityOrUnknown();
583    if (convexity == SkPathConvexity::kUnknown) {
584        convexity = this->computeConvexity();
585    }
586    SkASSERT(convexity != SkPathConvexity::kUnknown);
587    return convexity;
588}
589
590//////////////////////////////////////////////////////////////////////////////
591//  Construction methods
592
593SkPath& SkPath::dirtyAfterEdit() {
594    this->setConvexity(SkPathConvexity::kUnknown);
595    this->setFirstDirection(SkPathFirstDirection::kUnknown);
596
597#ifdef SK_DEBUG
598    // enable this as needed for testing, but it slows down some chrome tests so much
599    // that they don't complete, so we don't enable it by default
600    // e.g. TEST(IdentifiabilityPaintOpDigestTest, MassiveOpSkipped)
601    if (this->countVerbs() < 16) {
602        SkASSERT(fPathRef->dataMatchesVerbs());
603    }
604#endif
605
606    return *this;
607}
608
609void SkPath::incReserve(int inc) {
610    SkDEBUGCODE(this->validate();)
611    if (inc > 0) {
612        SkPathRef::Editor(&fPathRef, inc, inc);
613    }
614    SkDEBUGCODE(this->validate();)
615}
616
617SkPath& SkPath::moveTo(SkScalar x, SkScalar y) {
618    SkDEBUGCODE(this->validate();)
619
620    SkPathRef::Editor ed(&fPathRef);
621
622    // remember our index
623    fLastMoveToIndex = fPathRef->countPoints();
624
625    ed.growForVerb(kMove_Verb)->set(x, y);
626
627    return this->dirtyAfterEdit();
628}
629
630SkPath& SkPath::rMoveTo(SkScalar x, SkScalar y) {
631    SkPoint pt = {0,0};
632    int count = fPathRef->countPoints();
633    if (count > 0) {
634        if (fLastMoveToIndex >= 0) {
635            pt = fPathRef->atPoint(count - 1);
636        } else {
637            pt = fPathRef->atPoint(~fLastMoveToIndex);
638        }
639    }
640    return this->moveTo(pt.fX + x, pt.fY + y);
641}
642
643void SkPath::injectMoveToIfNeeded() {
644    if (fLastMoveToIndex < 0) {
645        SkScalar x, y;
646        if (fPathRef->countVerbs() == 0) {
647            x = y = 0;
648        } else {
649            const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex);
650            x = pt.fX;
651            y = pt.fY;
652        }
653        this->moveTo(x, y);
654    }
655}
656
657SkPath& SkPath::lineTo(SkScalar x, SkScalar y) {
658    SkDEBUGCODE(this->validate();)
659
660    this->injectMoveToIfNeeded();
661
662    SkPathRef::Editor ed(&fPathRef);
663    ed.growForVerb(kLine_Verb)->set(x, y);
664
665    return this->dirtyAfterEdit();
666}
667
668SkPath& SkPath::rLineTo(SkScalar x, SkScalar y) {
669    this->injectMoveToIfNeeded();  // This can change the result of this->getLastPt().
670    SkPoint pt;
671    this->getLastPt(&pt);
672    return this->lineTo(pt.fX + x, pt.fY + y);
673}
674
675SkPath& SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
676    SkDEBUGCODE(this->validate();)
677
678    this->injectMoveToIfNeeded();
679
680    SkPathRef::Editor ed(&fPathRef);
681    SkPoint* pts = ed.growForVerb(kQuad_Verb);
682    pts[0].set(x1, y1);
683    pts[1].set(x2, y2);
684
685    return this->dirtyAfterEdit();
686}
687
688SkPath& SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
689    this->injectMoveToIfNeeded();  // This can change the result of this->getLastPt().
690    SkPoint pt;
691    this->getLastPt(&pt);
692    return this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
693}
694
695SkPath& SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
696                        SkScalar w) {
697    // check for <= 0 or NaN with this test
698    if (!(w > 0)) {
699        this->lineTo(x2, y2);
700    } else if (!SkScalarIsFinite(w)) {
701        this->lineTo(x1, y1);
702        this->lineTo(x2, y2);
703    } else if (SK_Scalar1 == w) {
704        this->quadTo(x1, y1, x2, y2);
705    } else {
706        SkDEBUGCODE(this->validate();)
707
708        this->injectMoveToIfNeeded();
709
710        SkPathRef::Editor ed(&fPathRef);
711        SkPoint* pts = ed.growForVerb(kConic_Verb, w);
712        pts[0].set(x1, y1);
713        pts[1].set(x2, y2);
714
715        (void)this->dirtyAfterEdit();
716    }
717    return *this;
718}
719
720SkPath& SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
721                         SkScalar w) {
722    this->injectMoveToIfNeeded();  // This can change the result of this->getLastPt().
723    SkPoint pt;
724    this->getLastPt(&pt);
725    return this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w);
726}
727
728SkPath& SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
729                        SkScalar x3, SkScalar y3) {
730    SkDEBUGCODE(this->validate();)
731
732    this->injectMoveToIfNeeded();
733
734    SkPathRef::Editor ed(&fPathRef);
735    SkPoint* pts = ed.growForVerb(kCubic_Verb);
736    pts[0].set(x1, y1);
737    pts[1].set(x2, y2);
738    pts[2].set(x3, y3);
739
740    return this->dirtyAfterEdit();
741}
742
743SkPath& SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
744                         SkScalar x3, SkScalar y3) {
745    this->injectMoveToIfNeeded();  // This can change the result of this->getLastPt().
746    SkPoint pt;
747    this->getLastPt(&pt);
748    return this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
749                         pt.fX + x3, pt.fY + y3);
750}
751
752SkPath& SkPath::close() {
753    SkDEBUGCODE(this->validate();)
754
755    int count = fPathRef->countVerbs();
756    if (count > 0) {
757        switch (fPathRef->atVerb(count - 1)) {
758            case kLine_Verb:
759            case kQuad_Verb:
760            case kConic_Verb:
761            case kCubic_Verb:
762            case kMove_Verb: {
763                SkPathRef::Editor ed(&fPathRef);
764                ed.growForVerb(kClose_Verb);
765                break;
766            }
767            case kClose_Verb:
768                // don't add a close if it's the first verb or a repeat
769                break;
770            default:
771                SkDEBUGFAIL("unexpected verb");
772                break;
773        }
774    }
775
776    // signal that we need a moveTo to follow us (unless we're done)
777#if 0
778    if (fLastMoveToIndex >= 0) {
779        fLastMoveToIndex = ~fLastMoveToIndex;
780    }
781#else
782    fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
783#endif
784    return *this;
785}
786
787///////////////////////////////////////////////////////////////////////////////
788
789static void assert_known_direction(SkPathDirection dir) {
790    SkASSERT(SkPathDirection::kCW == dir || SkPathDirection::kCCW == dir);
791}
792
793SkPath& SkPath::addRect(const SkRect &rect, SkPathDirection dir, unsigned startIndex) {
794    assert_known_direction(dir);
795    this->setFirstDirection(this->hasOnlyMoveTos() ? (SkPathFirstDirection)dir
796                                                   : SkPathFirstDirection::kUnknown);
797    SkAutoDisableDirectionCheck addc(this);
798    SkAutoPathBoundsUpdate apbu(this, rect);
799
800    SkDEBUGCODE(int initialVerbCount = this->countVerbs());
801
802    const int kVerbs = 5; // moveTo + 3x lineTo + close
803    this->incReserve(kVerbs);
804
805    SkPath_RectPointIterator iter(rect, dir, startIndex);
806
807    this->moveTo(iter.current());
808    this->lineTo(iter.next());
809    this->lineTo(iter.next());
810    this->lineTo(iter.next());
811    this->close();
812
813    SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
814    return *this;
815}
816
817SkPath& SkPath::addPoly(const SkPoint pts[], int count, bool close) {
818    SkDEBUGCODE(this->validate();)
819    if (count <= 0) {
820        return *this;
821    }
822
823    fLastMoveToIndex = fPathRef->countPoints();
824
825    // +close makes room for the extra kClose_Verb
826    SkPathRef::Editor ed(&fPathRef, count+close, count);
827
828    ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY);
829    if (count > 1) {
830        SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1);
831        memcpy(p, &pts[1], (count-1) * sizeof(SkPoint));
832    }
833
834    if (close) {
835        ed.growForVerb(kClose_Verb);
836        fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
837    }
838
839    (void)this->dirtyAfterEdit();
840    SkDEBUGCODE(this->validate();)
841    return *this;
842}
843
844#include "src/core/SkGeometry.h"
845
846static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
847                              SkPoint* pt) {
848    if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) {
849        // Chrome uses this path to move into and out of ovals. If not
850        // treated as a special case the moves can distort the oval's
851        // bounding box (and break the circle special case).
852        pt->set(oval.fRight, oval.centerY());
853        return true;
854    } else if (0 == oval.width() && 0 == oval.height()) {
855        // Chrome will sometimes create 0 radius round rects. Having degenerate
856        // quad segments in the path prevents the path from being recognized as
857        // a rect.
858        // TODO: optimizing the case where only one of width or height is zero
859        // should also be considered. This case, however, doesn't seem to be
860        // as common as the single point case.
861        pt->set(oval.fRight, oval.fTop);
862        return true;
863    }
864    return false;
865}
866
867// Return the unit vectors pointing at the start/stop points for the given start/sweep angles
868//
869static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle,
870                                   SkVector* startV, SkVector* stopV, SkRotationDirection* dir) {
871    SkScalar startRad = SkDegreesToRadians(startAngle),
872             stopRad  = SkDegreesToRadians(startAngle + sweepAngle);
873
874    startV->fY = SkScalarSinSnapToZero(startRad);
875    startV->fX = SkScalarCosSnapToZero(startRad);
876    stopV->fY = SkScalarSinSnapToZero(stopRad);
877    stopV->fX = SkScalarCosSnapToZero(stopRad);
878
879    /*  If the sweep angle is nearly (but less than) 360, then due to precision
880     loss in radians-conversion and/or sin/cos, we may end up with coincident
881     vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
882     of drawing a nearly complete circle (good).
883     e.g. canvas.drawArc(0, 359.99, ...)
884     -vs- canvas.drawArc(0, 359.9, ...)
885     We try to detect this edge case, and tweak the stop vector
886     */
887    if (*startV == *stopV) {
888        SkScalar sw = SkScalarAbs(sweepAngle);
889        if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
890            // make a guess at a tiny angle (in radians) to tweak by
891            SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
892            // not sure how much will be enough, so we use a loop
893            do {
894                stopRad -= deltaRad;
895                stopV->fY = SkScalarSinSnapToZero(stopRad);
896                stopV->fX = SkScalarCosSnapToZero(stopRad);
897            } while (*startV == *stopV);
898        }
899    }
900    *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection;
901}
902
903/**
904 *  If this returns 0, then the caller should just line-to the singlePt, else it should
905 *  ignore singlePt and append the specified number of conics.
906 */
907static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop,
908                            SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc],
909                            SkPoint* singlePt) {
910    SkMatrix    matrix;
911
912    matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
913    matrix.postTranslate(oval.centerX(), oval.centerY());
914
915    int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics);
916    if (0 == count) {
917        matrix.mapXY(stop.x(), stop.y(), singlePt);
918    }
919    return count;
920}
921
922SkPath& SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
923                          SkPathDirection dir) {
924    SkRRect rrect;
925    rrect.setRectRadii(rect, (const SkVector*) radii);
926    return this->addRRect(rrect, dir);
927}
928
929SkPath& SkPath::addRRect(const SkRRect& rrect, SkPathDirection dir) {
930    // legacy start indices: 6 (CW) and 7(CCW)
931    return this->addRRect(rrect, dir, dir == SkPathDirection::kCW ? 6 : 7);
932}
933
934SkPath& SkPath::addRRect(const SkRRect &rrect, SkPathDirection dir, unsigned startIndex) {
935    assert_known_direction(dir);
936
937    bool isRRect = hasOnlyMoveTos();
938    const SkRect& bounds = rrect.getBounds();
939
940    if (rrect.isRect() || rrect.isEmpty()) {
941        // degenerate(rect) => radii points are collapsing
942        this->addRect(bounds, dir, (startIndex + 1) / 2);
943    } else if (rrect.isOval()) {
944        // degenerate(oval) => line points are collapsing
945        this->addOval(bounds, dir, startIndex / 2);
946    } else {
947        this->setFirstDirection(this->hasOnlyMoveTos() ? (SkPathFirstDirection)dir
948                                                       : SkPathFirstDirection::kUnknown);
949
950        SkAutoPathBoundsUpdate apbu(this, bounds);
951        SkAutoDisableDirectionCheck addc(this);
952
953        // we start with a conic on odd indices when moving CW vs. even indices when moving CCW
954        const bool startsWithConic = ((startIndex & 1) == (dir == SkPathDirection::kCW));
955        const SkScalar weight = SK_ScalarRoot2Over2;
956
957        SkDEBUGCODE(int initialVerbCount = this->countVerbs());
958        const int kVerbs = startsWithConic
959            ? 9   // moveTo + 4x conicTo + 3x lineTo + close
960            : 10; // moveTo + 4x lineTo + 4x conicTo + close
961        this->incReserve(kVerbs);
962
963        SkPath_RRectPointIterator rrectIter(rrect, dir, startIndex);
964        // Corner iterator indices follow the collapsed radii model,
965        // adjusted such that the start pt is "behind" the radii start pt.
966        const unsigned rectStartIndex = startIndex / 2 + (dir == SkPathDirection::kCW ? 0 : 1);
967        SkPath_RectPointIterator rectIter(bounds, dir, rectStartIndex);
968
969        this->moveTo(rrectIter.current());
970        if (startsWithConic) {
971            for (unsigned i = 0; i < 3; ++i) {
972                this->conicTo(rectIter.next(), rrectIter.next(), weight);
973                this->lineTo(rrectIter.next());
974            }
975            this->conicTo(rectIter.next(), rrectIter.next(), weight);
976            // final lineTo handled by close().
977        } else {
978            for (unsigned i = 0; i < 4; ++i) {
979                this->lineTo(rrectIter.next());
980                this->conicTo(rectIter.next(), rrectIter.next(), weight);
981            }
982        }
983        this->close();
984
985        SkPathRef::Editor ed(&fPathRef);
986        ed.setIsRRect(isRRect, dir == SkPathDirection::kCCW, startIndex % 8);
987
988        SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
989    }
990
991    SkDEBUGCODE(fPathRef->validate();)
992    return *this;
993}
994
995bool SkPath::hasOnlyMoveTos() const {
996    int count = fPathRef->countVerbs();
997    const uint8_t* verbs = fPathRef->verbsBegin();
998    for (int i = 0; i < count; ++i) {
999        if (*verbs == kLine_Verb ||
1000            *verbs == kQuad_Verb ||
1001            *verbs == kConic_Verb ||
1002            *verbs == kCubic_Verb) {
1003            return false;
1004        }
1005        ++verbs;
1006    }
1007    return true;
1008}
1009
1010bool SkPath::isZeroLengthSincePoint(int startPtIndex) const {
1011    int count = fPathRef->countPoints() - startPtIndex;
1012    if (count < 2) {
1013        return true;
1014    }
1015    const SkPoint* pts = fPathRef->points() + startPtIndex;
1016    const SkPoint& first = *pts;
1017    for (int index = 1; index < count; ++index) {
1018        if (first != pts[index]) {
1019            return false;
1020        }
1021    }
1022    return true;
1023}
1024
1025SkPath& SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
1026                             SkPathDirection dir) {
1027    assert_known_direction(dir);
1028
1029    if (rx < 0 || ry < 0) {
1030        return *this;
1031    }
1032
1033    SkRRect rrect;
1034    rrect.setRectXY(rect, rx, ry);
1035    return this->addRRect(rrect, dir);
1036}
1037
1038SkPath& SkPath::addOval(const SkRect& oval, SkPathDirection dir) {
1039    // legacy start index: 1
1040    return this->addOval(oval, dir, 1);
1041}
1042
1043SkPath& SkPath::addOval(const SkRect &oval, SkPathDirection dir, unsigned startPointIndex) {
1044    assert_known_direction(dir);
1045
1046    /* If addOval() is called after previous moveTo(),
1047       this path is still marked as an oval. This is used to
1048       fit into WebKit's calling sequences.
1049       We can't simply check isEmpty() in this case, as additional
1050       moveTo() would mark the path non empty.
1051     */
1052    bool isOval = hasOnlyMoveTos();
1053    if (isOval) {
1054        this->setFirstDirection((SkPathFirstDirection)dir);
1055    } else {
1056        this->setFirstDirection(SkPathFirstDirection::kUnknown);
1057    }
1058
1059    SkAutoDisableDirectionCheck addc(this);
1060    SkAutoPathBoundsUpdate apbu(this, oval);
1061
1062    SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1063    const int kVerbs = 6; // moveTo + 4x conicTo + close
1064    this->incReserve(kVerbs);
1065
1066    SkPath_OvalPointIterator ovalIter(oval, dir, startPointIndex);
1067    // The corner iterator pts are tracking "behind" the oval/radii pts.
1068    SkPath_RectPointIterator rectIter(oval, dir, startPointIndex + (dir == SkPathDirection::kCW ? 0 : 1));
1069    const SkScalar weight = SK_ScalarRoot2Over2;
1070
1071    this->moveTo(ovalIter.current());
1072    for (unsigned i = 0; i < 4; ++i) {
1073        this->conicTo(rectIter.next(), ovalIter.next(), weight);
1074    }
1075    this->close();
1076
1077    SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1078
1079    SkPathRef::Editor ed(&fPathRef);
1080
1081    ed.setIsOval(isOval, SkPathDirection::kCCW == dir, startPointIndex % 4);
1082    return *this;
1083}
1084
1085SkPath& SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, SkPathDirection dir) {
1086    if (r > 0) {
1087        this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
1088    }
1089    return *this;
1090}
1091
1092SkPath& SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
1093                      bool forceMoveTo) {
1094    if (oval.width() < 0 || oval.height() < 0) {
1095        return *this;
1096    }
1097
1098    if (fPathRef->countVerbs() == 0) {
1099        forceMoveTo = true;
1100    }
1101
1102    SkPoint lonePt;
1103    if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) {
1104        return forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt);
1105    }
1106
1107    SkVector startV, stopV;
1108    SkRotationDirection dir;
1109    angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir);
1110
1111    SkPoint singlePt;
1112
1113    // Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently
1114    // close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous
1115    // arcs from the same oval.
1116    auto addPt = [&forceMoveTo, this](const SkPoint& pt) {
1117        SkPoint lastPt;
1118        if (forceMoveTo) {
1119            this->moveTo(pt);
1120        } else if (!this->getLastPt(&lastPt) ||
1121                   !SkScalarNearlyEqual(lastPt.fX, pt.fX) ||
1122                   !SkScalarNearlyEqual(lastPt.fY, pt.fY)) {
1123            this->lineTo(pt);
1124        }
1125    };
1126
1127    // At this point, we know that the arc is not a lone point, but startV == stopV
1128    // indicates that the sweepAngle is too small such that angles_to_unit_vectors
1129    // cannot handle it.
1130    if (startV == stopV) {
1131        SkScalar endAngle = SkDegreesToRadians(startAngle + sweepAngle);
1132        SkScalar radiusX = oval.width() / 2;
1133        SkScalar radiusY = oval.height() / 2;
1134        // We do not use SkScalar[Sin|Cos]SnapToZero here. When sin(startAngle) is 0 and sweepAngle
1135        // is very small and radius is huge, the expected behavior here is to draw a line. But
1136        // calling SkScalarSinSnapToZero will make sin(endAngle) be 0 which will then draw a dot.
1137        singlePt.set(oval.centerX() + radiusX * SkScalarCos(endAngle),
1138                     oval.centerY() + radiusY * SkScalarSin(endAngle));
1139        addPt(singlePt);
1140        return *this;
1141    }
1142
1143    SkConic conics[SkConic::kMaxConicsForArc];
1144    int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt);
1145    if (count) {
1146        this->incReserve(count * 2 + 1);
1147        const SkPoint& pt = conics[0].fPts[0];
1148        addPt(pt);
1149        for (int i = 0; i < count; ++i) {
1150            this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
1151        }
1152    } else {
1153        addPt(singlePt);
1154    }
1155    return *this;
1156}
1157
1158// This converts the SVG arc to conics.
1159// Partly adapted from Niko's code in kdelibs/kdecore/svgicons.
1160// Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic()
1161// See also SVG implementation notes:
1162// http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
1163// Note that arcSweep bool value is flipped from the original implementation.
1164SkPath& SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arcLarge,
1165                      SkPathDirection arcSweep, SkScalar x, SkScalar y) {
1166    this->injectMoveToIfNeeded();
1167    SkPoint srcPts[2];
1168    this->getLastPt(&srcPts[0]);
1169    // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto")
1170    // joining the endpoints.
1171    // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
1172    if (!rx || !ry) {
1173        return this->lineTo(x, y);
1174    }
1175    // If the current point and target point for the arc are identical, it should be treated as a
1176    // zero length path. This ensures continuity in animations.
1177    srcPts[1].set(x, y);
1178    if (srcPts[0] == srcPts[1]) {
1179        return this->lineTo(x, y);
1180    }
1181    rx = SkScalarAbs(rx);
1182    ry = SkScalarAbs(ry);
1183    SkVector midPointDistance = srcPts[0] - srcPts[1];
1184    midPointDistance *= 0.5f;
1185
1186    SkMatrix pointTransform;
1187    pointTransform.setRotate(-angle);
1188
1189    SkPoint transformedMidPoint;
1190    pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1);
1191    SkScalar squareRx = rx * rx;
1192    SkScalar squareRy = ry * ry;
1193    SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX;
1194    SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY;
1195
1196    // Check if the radii are big enough to draw the arc, scale radii if not.
1197    // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
1198    SkScalar radiiScale = squareX / squareRx + squareY / squareRy;
1199    if (radiiScale > 1) {
1200        radiiScale = SkScalarSqrt(radiiScale);
1201        rx *= radiiScale;
1202        ry *= radiiScale;
1203    }
1204
1205    pointTransform.setScale(1 / rx, 1 / ry);
1206    pointTransform.preRotate(-angle);
1207
1208    SkPoint unitPts[2];
1209    pointTransform.mapPoints(unitPts, srcPts, (int) SK_ARRAY_COUNT(unitPts));
1210    SkVector delta = unitPts[1] - unitPts[0];
1211
1212    SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY;
1213    SkScalar scaleFactorSquared = std::max(1 / d - 0.25f, 0.f);
1214
1215    SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared);
1216    if ((arcSweep == SkPathDirection::kCCW) != SkToBool(arcLarge)) {  // flipped from the original implementation
1217        scaleFactor = -scaleFactor;
1218    }
1219    delta.scale(scaleFactor);
1220    SkPoint centerPoint = unitPts[0] + unitPts[1];
1221    centerPoint *= 0.5f;
1222    centerPoint.offset(-delta.fY, delta.fX);
1223    unitPts[0] -= centerPoint;
1224    unitPts[1] -= centerPoint;
1225    SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX);
1226    SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX);
1227    SkScalar thetaArc = theta2 - theta1;
1228    if (thetaArc < 0 && (arcSweep == SkPathDirection::kCW)) {  // arcSweep flipped from the original implementation
1229        thetaArc += SK_ScalarPI * 2;
1230    } else if (thetaArc > 0 && (arcSweep != SkPathDirection::kCW)) {  // arcSweep flipped from the original implementation
1231        thetaArc -= SK_ScalarPI * 2;
1232    }
1233
1234    // Very tiny angles cause our subsequent math to go wonky (skbug.com/9272)
1235    // so we do a quick check here. The precise tolerance amount is just made up.
1236    // PI/million happens to fix the bug in 9272, but a larger value is probably
1237    // ok too.
1238    if (SkScalarAbs(thetaArc) < (SK_ScalarPI / (1000 * 1000))) {
1239        return this->lineTo(x, y);
1240    }
1241
1242    pointTransform.setRotate(angle);
1243    pointTransform.preScale(rx, ry);
1244
1245    // the arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd
1246    int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (2 * SK_ScalarPI / 3)));
1247    SkScalar thetaWidth = thetaArc / segments;
1248    SkScalar t = SkScalarTan(0.5f * thetaWidth);
1249    if (!SkScalarIsFinite(t)) {
1250        return *this;
1251    }
1252    SkScalar startTheta = theta1;
1253    SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf);
1254    auto scalar_is_integer = [](SkScalar scalar) -> bool {
1255        return scalar == SkScalarFloorToScalar(scalar);
1256    };
1257    bool expectIntegers = SkScalarNearlyZero(SK_ScalarPI/2 - SkScalarAbs(thetaWidth)) &&
1258        scalar_is_integer(rx) && scalar_is_integer(ry) &&
1259        scalar_is_integer(x) && scalar_is_integer(y);
1260
1261    for (int i = 0; i < segments; ++i) {
1262        SkScalar endTheta    = startTheta + thetaWidth,
1263                 sinEndTheta = SkScalarSinSnapToZero(endTheta),
1264                 cosEndTheta = SkScalarCosSnapToZero(endTheta);
1265
1266        unitPts[1].set(cosEndTheta, sinEndTheta);
1267        unitPts[1] += centerPoint;
1268        unitPts[0] = unitPts[1];
1269        unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta);
1270        SkPoint mapped[2];
1271        pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts));
1272        /*
1273        Computing the arc width introduces rounding errors that cause arcs to start
1274        outside their marks. A round rect may lose convexity as a result. If the input
1275        values are on integers, place the conic on integers as well.
1276         */
1277        if (expectIntegers) {
1278            for (SkPoint& point : mapped) {
1279                point.fX = SkScalarRoundToScalar(point.fX);
1280                point.fY = SkScalarRoundToScalar(point.fY);
1281            }
1282        }
1283        this->conicTo(mapped[0], mapped[1], w);
1284        startTheta = endTheta;
1285    }
1286
1287#ifndef SK_LEGACY_PATH_ARCTO_ENDPOINT
1288    // The final point should match the input point (by definition); replace it to
1289    // ensure that rounding errors in the above math don't cause any problems.
1290    this->setLastPt(x, y);
1291#endif
1292    return *this;
1293}
1294
1295SkPath& SkPath::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, SkPath::ArcSize largeArc,
1296                       SkPathDirection sweep, SkScalar dx, SkScalar dy) {
1297    SkPoint currentPoint;
1298    this->getLastPt(&currentPoint);
1299    return this->arcTo(rx, ry, xAxisRotate, largeArc, sweep,
1300                       currentPoint.fX + dx, currentPoint.fY + dy);
1301}
1302
1303SkPath& SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
1304    if (oval.isEmpty() || 0 == sweepAngle) {
1305        return *this;
1306    }
1307
1308    const SkScalar kFullCircleAngle = SkIntToScalar(360);
1309
1310    if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
1311        // We can treat the arc as an oval if it begins at one of our legal starting positions.
1312        // See SkPath::addOval() docs.
1313        SkScalar startOver90 = startAngle / 90.f;
1314        SkScalar startOver90I = SkScalarRoundToScalar(startOver90);
1315        SkScalar error = startOver90 - startOver90I;
1316        if (SkScalarNearlyEqual(error, 0)) {
1317            // Index 1 is at startAngle == 0.
1318            SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f);
1319            startIndex = startIndex < 0 ? startIndex + 4.f : startIndex;
1320            return this->addOval(oval, sweepAngle > 0 ? SkPathDirection::kCW : SkPathDirection::kCCW,
1321                                 (unsigned) startIndex);
1322        }
1323    }
1324    return this->arcTo(oval, startAngle, sweepAngle, true);
1325}
1326
1327/*
1328    Need to handle the case when the angle is sharp, and our computed end-points
1329    for the arc go behind pt1 and/or p2...
1330*/
1331SkPath& SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) {
1332    this->injectMoveToIfNeeded();
1333
1334    if (radius == 0) {
1335        return this->lineTo(x1, y1);
1336    }
1337
1338    // need to know our prev pt so we can construct tangent vectors
1339    SkPoint start;
1340    this->getLastPt(&start);
1341
1342    // need double precision for these calcs.
1343    SkDVector befored, afterd;
1344    befored.set({x1 - start.fX, y1 - start.fY}).normalize();
1345    afterd.set({x2 - x1, y2 - y1}).normalize();
1346    double cosh = befored.dot(afterd);
1347    double sinh = befored.cross(afterd);
1348
1349    if (!befored.isFinite() || !afterd.isFinite() || SkScalarNearlyZero(SkDoubleToScalar(sinh))) {
1350        return this->lineTo(x1, y1);
1351    }
1352
1353    // safe to convert back to floats now
1354    SkVector before = befored.asSkVector();
1355    SkVector after = afterd.asSkVector();
1356    SkScalar dist = SkScalarAbs(SkDoubleToScalar(radius * (1 - cosh) / sinh));
1357    SkScalar xx = x1 - dist * before.fX;
1358    SkScalar yy = y1 - dist * before.fY;
1359    after.setLength(dist);
1360    this->lineTo(xx, yy);
1361    SkScalar weight = SkScalarSqrt(SkDoubleToScalar(SK_ScalarHalf + cosh * 0.5));
1362    return this->conicTo(x1, y1, x1 + after.fX, y1 + after.fY, weight);
1363}
1364
1365///////////////////////////////////////////////////////////////////////////////
1366
1367SkPath& SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy, AddPathMode mode) {
1368    SkMatrix matrix;
1369
1370    matrix.setTranslate(dx, dy);
1371    return this->addPath(path, matrix, mode);
1372}
1373
1374SkPath& SkPath::addPath(const SkPath& srcPath, const SkMatrix& matrix, AddPathMode mode) {
1375    if (srcPath.isEmpty()) {
1376        return *this;
1377    }
1378
1379    // Detect if we're trying to add ourself
1380    const SkPath* src = &srcPath;
1381    SkTLazy<SkPath> tmp;
1382    if (this == src) {
1383        src = tmp.set(srcPath);
1384    }
1385
1386    if (kAppend_AddPathMode == mode && !matrix.hasPerspective()) {
1387        fLastMoveToIndex = this->countPoints() + src->fLastMoveToIndex;
1388
1389        SkPathRef::Editor ed(&fPathRef);
1390        auto [newPts, newWeights] = ed.growForVerbsInPath(*src->fPathRef);
1391        matrix.mapPoints(newPts, src->fPathRef->points(), src->countPoints());
1392        if (int numWeights = src->fPathRef->countWeights()) {
1393            memcpy(newWeights, src->fPathRef->conicWeights(), numWeights * sizeof(newWeights[0]));
1394        }
1395        // fiddle with fLastMoveToIndex, as we do in SkPath::close()
1396        if ((SkPathVerb)fPathRef->verbsEnd()[-1] == SkPathVerb::kClose) {
1397            fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
1398        }
1399        return this->dirtyAfterEdit();
1400    }
1401
1402    SkMatrixPriv::MapPtsProc mapPtsProc = SkMatrixPriv::GetMapPtsProc(matrix);
1403    bool firstVerb = true;
1404    for (auto [verb, pts, w] : SkPathPriv::Iterate(*src)) {
1405        switch (verb) {
1406            SkPoint mappedPts[3];
1407            case SkPathVerb::kMove:
1408                mapPtsProc(matrix, mappedPts, &pts[0], 1);
1409                if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) {
1410                    injectMoveToIfNeeded(); // In case last contour is closed
1411                    SkPoint lastPt;
1412                    // don't add lineTo if it is degenerate
1413                    if (fLastMoveToIndex < 0 || !this->getLastPt(&lastPt) ||
1414                        lastPt != mappedPts[0]) {
1415                        this->lineTo(mappedPts[0]);
1416                    }
1417                } else {
1418                    this->moveTo(mappedPts[0]);
1419                }
1420                break;
1421            case SkPathVerb::kLine:
1422                mapPtsProc(matrix, mappedPts, &pts[1], 1);
1423                this->lineTo(mappedPts[0]);
1424                break;
1425            case SkPathVerb::kQuad:
1426                mapPtsProc(matrix, mappedPts, &pts[1], 2);
1427                this->quadTo(mappedPts[0], mappedPts[1]);
1428                break;
1429            case SkPathVerb::kConic:
1430                mapPtsProc(matrix, mappedPts, &pts[1], 2);
1431                this->conicTo(mappedPts[0], mappedPts[1], *w);
1432                break;
1433            case SkPathVerb::kCubic:
1434                mapPtsProc(matrix, mappedPts, &pts[1], 3);
1435                this->cubicTo(mappedPts[0], mappedPts[1], mappedPts[2]);
1436                break;
1437            case SkPathVerb::kClose:
1438                this->close();
1439                break;
1440        }
1441        firstVerb = false;
1442    }
1443    return *this;
1444}
1445
1446///////////////////////////////////////////////////////////////////////////////
1447
1448// ignore the last point of the 1st contour
1449SkPath& SkPath::reversePathTo(const SkPath& path) {
1450    if (path.fPathRef->fVerbs.count() == 0) {
1451        return *this;
1452    }
1453
1454    const uint8_t* verbs = path.fPathRef->verbsEnd();
1455    const uint8_t* verbsBegin = path.fPathRef->verbsBegin();
1456    SkASSERT(verbsBegin[0] == kMove_Verb);
1457    const SkPoint*  pts = path.fPathRef->pointsEnd() - 1;
1458    const SkScalar* conicWeights = path.fPathRef->conicWeightsEnd();
1459
1460    while (verbs > verbsBegin) {
1461        uint8_t v = *--verbs;
1462        pts -= SkPathPriv::PtsInVerb(v);
1463        switch (v) {
1464            case kMove_Verb:
1465                // if the path has multiple contours, stop after reversing the last
1466                return *this;
1467            case kLine_Verb:
1468                this->lineTo(pts[0]);
1469                break;
1470            case kQuad_Verb:
1471                this->quadTo(pts[1], pts[0]);
1472                break;
1473            case kConic_Verb:
1474                this->conicTo(pts[1], pts[0], *--conicWeights);
1475                break;
1476            case kCubic_Verb:
1477                this->cubicTo(pts[2], pts[1], pts[0]);
1478                break;
1479            case kClose_Verb:
1480                break;
1481            default:
1482                SkDEBUGFAIL("bad verb");
1483                break;
1484        }
1485    }
1486    return *this;
1487}
1488
1489SkPath& SkPath::reverseAddPath(const SkPath& srcPath) {
1490    // Detect if we're trying to add ourself
1491    const SkPath* src = &srcPath;
1492    SkTLazy<SkPath> tmp;
1493    if (this == src) {
1494        src = tmp.set(srcPath);
1495    }
1496
1497    const uint8_t* verbsBegin = src->fPathRef->verbsBegin();
1498    const uint8_t* verbs = src->fPathRef->verbsEnd();
1499    const SkPoint* pts = src->fPathRef->pointsEnd();
1500    const SkScalar* conicWeights = src->fPathRef->conicWeightsEnd();
1501
1502    bool needMove = true;
1503    bool needClose = false;
1504    while (verbs > verbsBegin) {
1505        uint8_t v = *--verbs;
1506        int n = SkPathPriv::PtsInVerb(v);
1507
1508        if (needMove) {
1509            --pts;
1510            this->moveTo(pts->fX, pts->fY);
1511            needMove = false;
1512        }
1513        pts -= n;
1514        switch (v) {
1515            case kMove_Verb:
1516                if (needClose) {
1517                    this->close();
1518                    needClose = false;
1519                }
1520                needMove = true;
1521                pts += 1;   // so we see the point in "if (needMove)" above
1522                break;
1523            case kLine_Verb:
1524                this->lineTo(pts[0]);
1525                break;
1526            case kQuad_Verb:
1527                this->quadTo(pts[1], pts[0]);
1528                break;
1529            case kConic_Verb:
1530                this->conicTo(pts[1], pts[0], *--conicWeights);
1531                break;
1532            case kCubic_Verb:
1533                this->cubicTo(pts[2], pts[1], pts[0]);
1534                break;
1535            case kClose_Verb:
1536                needClose = true;
1537                break;
1538            default:
1539                SkDEBUGFAIL("unexpected verb");
1540        }
1541    }
1542    return *this;
1543}
1544
1545///////////////////////////////////////////////////////////////////////////////
1546
1547void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
1548    SkMatrix    matrix;
1549
1550    matrix.setTranslate(dx, dy);
1551    this->transform(matrix, dst);
1552}
1553
1554static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
1555                               int level = 2) {
1556    if (--level >= 0) {
1557        SkPoint tmp[7];
1558
1559        SkChopCubicAtHalf(pts, tmp);
1560        subdivide_cubic_to(path, &tmp[0], level);
1561        subdivide_cubic_to(path, &tmp[3], level);
1562    } else {
1563        path->cubicTo(pts[1], pts[2], pts[3]);
1564    }
1565}
1566
1567void SkPath::transform(const SkMatrix& matrix, SkPath* dst, SkApplyPerspectiveClip pc) const {
1568    if (matrix.isIdentity()) {
1569        if (dst != nullptr && dst != this) {
1570            *dst = *this;
1571        }
1572        return;
1573    }
1574
1575    SkDEBUGCODE(this->validate();)
1576    if (dst == nullptr) {
1577        dst = (SkPath*)this;
1578    }
1579
1580    if (matrix.hasPerspective()) {
1581        SkPath  tmp;
1582        tmp.fFillType = fFillType;
1583
1584        SkPath clipped;
1585        const SkPath* src = this;
1586        if (pc == SkApplyPerspectiveClip::kYes &&
1587            SkPathPriv::PerspectiveClip(*this, matrix, &clipped))
1588        {
1589            src = &clipped;
1590        }
1591
1592        SkPath::Iter    iter(*src, false);
1593        SkPoint         pts[4];
1594        SkPath::Verb    verb;
1595
1596        while ((verb = iter.next(pts)) != kDone_Verb) {
1597            switch (verb) {
1598                case kMove_Verb:
1599                    tmp.moveTo(pts[0]);
1600                    break;
1601                case kLine_Verb:
1602                    tmp.lineTo(pts[1]);
1603                    break;
1604                case kQuad_Verb:
1605                    // promote the quad to a conic
1606                    tmp.conicTo(pts[1], pts[2],
1607                                SkConic::TransformW(pts, SK_Scalar1, matrix));
1608                    break;
1609                case kConic_Verb:
1610                    tmp.conicTo(pts[1], pts[2],
1611                                SkConic::TransformW(pts, iter.conicWeight(), matrix));
1612                    break;
1613                case kCubic_Verb:
1614                    subdivide_cubic_to(&tmp, pts);
1615                    break;
1616                case kClose_Verb:
1617                    tmp.close();
1618                    break;
1619                default:
1620                    SkDEBUGFAIL("unknown verb");
1621                    break;
1622            }
1623        }
1624
1625        dst->swap(tmp);
1626        SkPathRef::Editor ed(&dst->fPathRef);
1627        matrix.mapPoints(ed.writablePoints(), ed.pathRef()->countPoints());
1628        dst->setFirstDirection(SkPathFirstDirection::kUnknown);
1629    } else {
1630        SkPathConvexity convexity = this->getConvexityOrUnknown();
1631
1632        SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef, matrix);
1633
1634        if (this != dst) {
1635            dst->fLastMoveToIndex = fLastMoveToIndex;
1636            dst->fFillType = fFillType;
1637            dst->fIsVolatile = fIsVolatile;
1638        }
1639
1640        // Due to finite/fragile float numerics, we can't assume that a convex path remains
1641        // convex after a transformation, so mark it as unknown here.
1642        // However, some transformations are thought to be safe:
1643        //    axis-aligned values under scale/translate.
1644        //
1645        if (convexity == SkPathConvexity::kConvex &&
1646            (!matrix.isScaleTranslate() || !SkPathPriv::IsAxisAligned(*this))) {
1647            // Not safe to still assume we're convex...
1648            convexity = SkPathConvexity::kUnknown;
1649        }
1650        dst->setConvexity(convexity);
1651
1652        if (this->getFirstDirection() == SkPathFirstDirection::kUnknown) {
1653            dst->setFirstDirection(SkPathFirstDirection::kUnknown);
1654        } else {
1655            SkScalar det2x2 =
1656                matrix.get(SkMatrix::kMScaleX) * matrix.get(SkMatrix::kMScaleY) -
1657                matrix.get(SkMatrix::kMSkewX)  * matrix.get(SkMatrix::kMSkewY);
1658            if (det2x2 < 0) {
1659                dst->setFirstDirection(
1660                        SkPathPriv::OppositeFirstDirection(
1661                            (SkPathFirstDirection)this->getFirstDirection()));
1662            } else if (det2x2 > 0) {
1663                dst->setFirstDirection(this->getFirstDirection());
1664            } else {
1665                dst->setFirstDirection(SkPathFirstDirection::kUnknown);
1666            }
1667        }
1668
1669        SkDEBUGCODE(dst->validate();)
1670    }
1671}
1672
1673///////////////////////////////////////////////////////////////////////////////
1674///////////////////////////////////////////////////////////////////////////////
1675
1676SkPath::Iter::Iter() {
1677#ifdef SK_DEBUG
1678    fPts = nullptr;
1679    fConicWeights = nullptr;
1680    fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
1681    fForceClose = fCloseLine = false;
1682#endif
1683    // need to init enough to make next() harmlessly return kDone_Verb
1684    fVerbs = nullptr;
1685    fVerbStop = nullptr;
1686    fNeedClose = false;
1687}
1688
1689SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
1690    this->setPath(path, forceClose);
1691}
1692
1693void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
1694    fPts = path.fPathRef->points();
1695    fVerbs = path.fPathRef->verbsBegin();
1696    fVerbStop = path.fPathRef->verbsEnd();
1697    fConicWeights = path.fPathRef->conicWeights();
1698    if (fConicWeights) {
1699      fConicWeights -= 1;  // begin one behind
1700    }
1701    fLastPt.fX = fLastPt.fY = 0;
1702    fMoveTo.fX = fMoveTo.fY = 0;
1703    fForceClose = SkToU8(forceClose);
1704    fNeedClose = false;
1705}
1706
1707bool SkPath::Iter::isClosedContour() const {
1708    if (fVerbs == nullptr || fVerbs == fVerbStop) {
1709        return false;
1710    }
1711    if (fForceClose) {
1712        return true;
1713    }
1714
1715    const uint8_t* verbs = fVerbs;
1716    const uint8_t* stop = fVerbStop;
1717
1718    if (kMove_Verb == *verbs) {
1719        verbs += 1; // skip the initial moveto
1720    }
1721
1722    while (verbs < stop) {
1723        // verbs points one beyond the current verb, decrement first.
1724        unsigned v = *verbs++;
1725        if (kMove_Verb == v) {
1726            break;
1727        }
1728        if (kClose_Verb == v) {
1729            return true;
1730        }
1731    }
1732    return false;
1733}
1734
1735SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
1736    SkASSERT(pts);
1737    if (fLastPt != fMoveTo) {
1738        // A special case: if both points are NaN, SkPoint::operation== returns
1739        // false, but the iterator expects that they are treated as the same.
1740        // (consider SkPoint is a 2-dimension float point).
1741        if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) ||
1742            SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) {
1743            return kClose_Verb;
1744        }
1745
1746        pts[0] = fLastPt;
1747        pts[1] = fMoveTo;
1748        fLastPt = fMoveTo;
1749        fCloseLine = true;
1750        return kLine_Verb;
1751    } else {
1752        pts[0] = fMoveTo;
1753        return kClose_Verb;
1754    }
1755}
1756
1757SkPath::Verb SkPath::Iter::next(SkPoint ptsParam[4]) {
1758    SkASSERT(ptsParam);
1759
1760    if (fVerbs == fVerbStop) {
1761        // Close the curve if requested and if there is some curve to close
1762        if (fNeedClose) {
1763            if (kLine_Verb == this->autoClose(ptsParam)) {
1764                return kLine_Verb;
1765            }
1766            fNeedClose = false;
1767            return kClose_Verb;
1768        }
1769        return kDone_Verb;
1770    }
1771
1772    unsigned verb = *fVerbs++;
1773    const SkPoint* SK_RESTRICT srcPts = fPts;
1774    SkPoint* SK_RESTRICT       pts = ptsParam;
1775
1776    switch (verb) {
1777        case kMove_Verb:
1778            if (fNeedClose) {
1779                fVerbs--; // move back one verb
1780                verb = this->autoClose(pts);
1781                if (verb == kClose_Verb) {
1782                    fNeedClose = false;
1783                }
1784                return (Verb)verb;
1785            }
1786            if (fVerbs == fVerbStop) {    // might be a trailing moveto
1787                return kDone_Verb;
1788            }
1789            fMoveTo = *srcPts;
1790            pts[0] = *srcPts;
1791            srcPts += 1;
1792            fLastPt = fMoveTo;
1793            fNeedClose = fForceClose;
1794            break;
1795        case kLine_Verb:
1796            pts[0] = fLastPt;
1797            pts[1] = srcPts[0];
1798            fLastPt = srcPts[0];
1799            fCloseLine = false;
1800            srcPts += 1;
1801            break;
1802        case kConic_Verb:
1803            fConicWeights += 1;
1804            [[fallthrough]];
1805        case kQuad_Verb:
1806            pts[0] = fLastPt;
1807            memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
1808            fLastPt = srcPts[1];
1809            srcPts += 2;
1810            break;
1811        case kCubic_Verb:
1812            pts[0] = fLastPt;
1813            memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
1814            fLastPt = srcPts[2];
1815            srcPts += 3;
1816            break;
1817        case kClose_Verb:
1818            verb = this->autoClose(pts);
1819            if (verb == kLine_Verb) {
1820                fVerbs--; // move back one verb
1821            } else {
1822                fNeedClose = false;
1823            }
1824            fLastPt = fMoveTo;
1825            break;
1826    }
1827    fPts = srcPts;
1828    return (Verb)verb;
1829}
1830
1831void SkPath::RawIter::setPath(const SkPath& path) {
1832    SkPathPriv::Iterate iterate(path);
1833    fIter = iterate.begin();
1834    fEnd = iterate.end();
1835}
1836
1837SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
1838    if (!(fIter != fEnd)) {
1839        return kDone_Verb;
1840    }
1841    auto [verb, iterPts, weights] = *fIter;
1842    int numPts;
1843    switch (verb) {
1844        case SkPathVerb::kMove: numPts = 1; break;
1845        case SkPathVerb::kLine: numPts = 2; break;
1846        case SkPathVerb::kQuad: numPts = 3; break;
1847        case SkPathVerb::kConic:
1848            numPts = 3;
1849            fConicWeight = *weights;
1850            break;
1851        case SkPathVerb::kCubic: numPts = 4; break;
1852        case SkPathVerb::kClose: numPts = 0; break;
1853    }
1854    memcpy(pts, iterPts, sizeof(SkPoint) * numPts);
1855    ++fIter;
1856    return (Verb) verb;
1857}
1858
1859///////////////////////////////////////////////////////////////////////////////
1860
1861#include "include/core/SkStream.h"
1862#include "include/core/SkString.h"
1863#include "src/core/SkStringUtils.h"
1864
1865static void append_params(SkString* str, const char label[], const SkPoint pts[],
1866                          int count, SkScalarAsStringType strType, SkScalar conicWeight = -12345) {
1867    str->append(label);
1868    str->append("(");
1869
1870    const SkScalar* values = &pts[0].fX;
1871    count *= 2;
1872
1873    for (int i = 0; i < count; ++i) {
1874        SkAppendScalar(str, values[i], strType);
1875        if (i < count - 1) {
1876            str->append(", ");
1877        }
1878    }
1879    if (conicWeight != -12345) {
1880        str->append(", ");
1881        SkAppendScalar(str, conicWeight, strType);
1882    }
1883    str->append(");");
1884    if (kHex_SkScalarAsStringType == strType) {
1885        str->append("  // ");
1886        for (int i = 0; i < count; ++i) {
1887            SkAppendScalarDec(str, values[i]);
1888            if (i < count - 1) {
1889                str->append(", ");
1890            }
1891        }
1892        if (conicWeight >= 0) {
1893            str->append(", ");
1894            SkAppendScalarDec(str, conicWeight);
1895        }
1896    }
1897    str->append("\n");
1898}
1899
1900void SkPath::dump(SkWStream* wStream, bool dumpAsHex) const {
1901    SkScalarAsStringType asType = dumpAsHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
1902    Iter    iter(*this, false);
1903    SkPoint pts[4];
1904    Verb    verb;
1905
1906    SkString builder;
1907    char const * const gFillTypeStrs[] = {
1908        "Winding",
1909        "EvenOdd",
1910        "InverseWinding",
1911        "InverseEvenOdd",
1912    };
1913    builder.printf("path.setFillType(SkPathFillType::k%s);\n",
1914            gFillTypeStrs[(int) this->getFillType()]);
1915    while ((verb = iter.next(pts)) != kDone_Verb) {
1916        switch (verb) {
1917            case kMove_Verb:
1918                append_params(&builder, "path.moveTo", &pts[0], 1, asType);
1919                break;
1920            case kLine_Verb:
1921                append_params(&builder, "path.lineTo", &pts[1], 1, asType);
1922                break;
1923            case kQuad_Verb:
1924                append_params(&builder, "path.quadTo", &pts[1], 2, asType);
1925                break;
1926            case kConic_Verb:
1927                append_params(&builder, "path.conicTo", &pts[1], 2, asType, iter.conicWeight());
1928                break;
1929            case kCubic_Verb:
1930                append_params(&builder, "path.cubicTo", &pts[1], 3, asType);
1931                break;
1932            case kClose_Verb:
1933                builder.append("path.close();\n");
1934                break;
1935            default:
1936                SkDebugf("  path: UNKNOWN VERB %d, aborting dump...\n", verb);
1937                verb = kDone_Verb;  // stop the loop
1938                break;
1939        }
1940        if (!wStream && builder.size()) {
1941            SkDebugf("%s", builder.c_str());
1942            builder.reset();
1943        }
1944    }
1945    if (wStream) {
1946        wStream->writeText(builder.c_str());
1947    }
1948}
1949
1950void SkPath::dump(std::string& desc, int depth) const {
1951    std::string split(depth, '\t');
1952    desc += split + "\n SkPath:{ \n";
1953    Iter    iter(*this, false);
1954    SkPoint points[4];
1955    Verb    verb;
1956
1957    SkString descSk;
1958    char const * const gFillTypeStrs[] = {
1959        "Winding",
1960        "EvenOdd",
1961        "InverseWinding",
1962        "InverseEvenOdd",
1963    };
1964    descSk.printf("path.setFillType(SkPath::k%s_FillType);\n", gFillTypeStrs[(int) this->getFillType()]);
1965    while ((verb = iter.next(points)) != kDone_Verb) {
1966        switch (verb) {
1967            case kMove_Verb:
1968                append_params(&descSk, "path.moveTo", &points[0], 1, kDec_SkScalarAsStringType);
1969                break;
1970            case kLine_Verb:
1971                append_params(&descSk, "path.lineTo", &points[1], 1, kDec_SkScalarAsStringType);
1972                break;
1973            case kQuad_Verb:
1974                append_params(&descSk, "path.quadTo", &points[1], 2, kDec_SkScalarAsStringType);
1975                break;
1976            case kConic_Verb:
1977                append_params(&descSk, "path.conicTo", &points[1], 2, kDec_SkScalarAsStringType, iter.conicWeight());
1978                break;
1979            case kCubic_Verb:
1980                append_params(&descSk, "path.cubicTo", &points[1], 3, kDec_SkScalarAsStringType);
1981                break;
1982            case kClose_Verb:
1983                descSk.append("path.close();\n");
1984                break;
1985            default:
1986                break;
1987        }
1988        if (descSk.size()) {
1989            desc += split + std::string(descSk.c_str());
1990            descSk.reset();
1991        }
1992    }
1993    desc += split + "}\n";
1994}
1995
1996void SkPath::dumpArrays(SkWStream* wStream, bool dumpAsHex) const {
1997    SkString builder;
1998
1999    auto bool_str = [](bool v) { return v ? "true" : "false"; };
2000
2001    builder.appendf("// fBoundsIsDirty = %s\n", bool_str(fPathRef->fBoundsIsDirty));
2002    builder.appendf("// fGenerationID = %d\n", fPathRef->fGenerationID);
2003    builder.appendf("// fSegmentMask = %d\n", fPathRef->fSegmentMask);
2004    builder.appendf("// fIsOval = %s\n", bool_str(fPathRef->fIsOval));
2005    builder.appendf("// fIsRRect = %s\n", bool_str(fPathRef->fIsRRect));
2006
2007    auto append_scalar = [&](SkScalar v) {
2008        if (dumpAsHex) {
2009            builder.appendf("SkBits2Float(0x%08X) /* %g */", SkFloat2Bits(v), v);
2010        } else {
2011            builder.appendf("%g", v);
2012        }
2013    };
2014
2015    builder.append("const SkPoint path_points[] = {\n");
2016    for (int i = 0; i < this->countPoints(); ++i) {
2017        SkPoint p = this->getPoint(i);
2018        builder.append("    { ");
2019        append_scalar(p.fX);
2020        builder.append(", ");
2021        append_scalar(p.fY);
2022        builder.append(" },\n");
2023    }
2024    builder.append("};\n");
2025
2026    const char* gVerbStrs[] = {
2027        "Move", "Line", "Quad", "Conic", "Cubic", "Close"
2028    };
2029    builder.append("const uint8_t path_verbs[] = {\n    ");
2030    for (auto v = fPathRef->verbsBegin(); v != fPathRef->verbsEnd(); ++v) {
2031        builder.appendf("(uint8_t)SkPathVerb::k%s, ", gVerbStrs[*v]);
2032    }
2033    builder.append("\n};\n");
2034
2035    const int nConics = fPathRef->conicWeightsEnd() - fPathRef->conicWeights();
2036    if (nConics) {
2037        builder.append("const SkScalar path_conics[] = {\n    ");
2038        for (auto c = fPathRef->conicWeights(); c != fPathRef->conicWeightsEnd(); ++c) {
2039            append_scalar(*c);
2040            builder.append(", ");
2041        }
2042        builder.append("\n};\n");
2043    }
2044
2045    char const * const gFillTypeStrs[] = {
2046        "Winding",
2047        "EvenOdd",
2048        "InverseWinding",
2049        "InverseEvenOdd",
2050    };
2051
2052    builder.appendf("SkPath path = SkPath::Make(path_points, %d, path_verbs, %d, %s, %d,\n",
2053                    this->countPoints(), this->countVerbs(),
2054                    nConics ? "path_conics" : "nullptr", nConics);
2055    builder.appendf("                           SkPathFillType::k%s, %s);\n",
2056                    gFillTypeStrs[(int)this->getFillType()],
2057                    bool_str(fIsVolatile));
2058
2059    if (wStream) {
2060        wStream->writeText(builder.c_str());
2061    } else {
2062        SkDebugf("%s\n", builder.c_str());
2063    }
2064}
2065
2066bool SkPath::isValidImpl() const {
2067    if ((fFillType & ~3) != 0) {
2068        return false;
2069    }
2070
2071#ifdef SK_DEBUG_PATH
2072    if (!fBoundsIsDirty) {
2073        SkRect bounds;
2074
2075        bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get());
2076        if (SkToBool(fIsFinite) != isFinite) {
2077            return false;
2078        }
2079
2080        if (fPathRef->countPoints() <= 1) {
2081            // if we're empty, fBounds may be empty but translated, so we can't
2082            // necessarily compare to bounds directly
2083            // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
2084            // be [2, 2, 2, 2]
2085            if (!bounds.isEmpty() || !fBounds.isEmpty()) {
2086                return false;
2087            }
2088        } else {
2089            if (bounds.isEmpty()) {
2090                if (!fBounds.isEmpty()) {
2091                    return false;
2092                }
2093            } else {
2094                if (!fBounds.isEmpty()) {
2095                    if (!fBounds.contains(bounds)) {
2096                        return false;
2097                    }
2098                }
2099            }
2100        }
2101    }
2102#endif // SK_DEBUG_PATH
2103    return true;
2104}
2105
2106///////////////////////////////////////////////////////////////////////////////
2107
2108static int sign(SkScalar x) { return x < 0; }
2109#define kValueNeverReturnedBySign   2
2110
2111enum DirChange {
2112    kUnknown_DirChange,
2113    kLeft_DirChange,
2114    kRight_DirChange,
2115    kStraight_DirChange,
2116    kBackwards_DirChange, // if double back, allow simple lines to be convex
2117    kInvalid_DirChange
2118};
2119
2120// only valid for a single contour
2121struct Convexicator {
2122
2123    /** The direction returned is only valid if the path is determined convex */
2124    SkPathFirstDirection getFirstDirection() const { return fFirstDirection; }
2125
2126    void setMovePt(const SkPoint& pt) {
2127        fFirstPt = fLastPt = pt;
2128        fExpectedDir = kInvalid_DirChange;
2129    }
2130
2131    bool addPt(const SkPoint& pt) {
2132        if (fLastPt == pt) {
2133            return true;
2134        }
2135        // should only be true for first non-zero vector after setMovePt was called.
2136        if (fFirstPt == fLastPt && fExpectedDir == kInvalid_DirChange) {
2137            fLastVec = pt - fLastPt;
2138            fFirstVec = fLastVec;
2139        } else if (!this->addVec(pt - fLastPt)) {
2140            return false;
2141        }
2142        fLastPt = pt;
2143        return true;
2144    }
2145
2146    static SkPathConvexity BySign(const SkPoint points[], int count) {
2147        if (count <= 3) {
2148            // point, line, or triangle are always convex
2149            return SkPathConvexity::kConvex;
2150        }
2151
2152        const SkPoint* last = points + count;
2153        SkPoint currPt = *points++;
2154        SkPoint firstPt = currPt;
2155        int dxes = 0;
2156        int dyes = 0;
2157        int lastSx = kValueNeverReturnedBySign;
2158        int lastSy = kValueNeverReturnedBySign;
2159        for (int outerLoop = 0; outerLoop < 2; ++outerLoop ) {
2160            while (points != last) {
2161                SkVector vec = *points - currPt;
2162                if (!vec.isZero()) {
2163                    // give up if vector construction failed
2164                    if (!vec.isFinite()) {
2165                        return SkPathConvexity::kUnknown;
2166                    }
2167                    int sx = sign(vec.fX);
2168                    int sy = sign(vec.fY);
2169                    dxes += (sx != lastSx);
2170                    dyes += (sy != lastSy);
2171                    if (dxes > 3 || dyes > 3) {
2172                        return SkPathConvexity::kConcave;
2173                    }
2174                    lastSx = sx;
2175                    lastSy = sy;
2176                }
2177                currPt = *points++;
2178                if (outerLoop) {
2179                    break;
2180                }
2181            }
2182            points = &firstPt;
2183        }
2184        return SkPathConvexity::kConvex;  // that is, it may be convex, don't know yet
2185    }
2186
2187    bool close() {
2188        // If this was an explicit close, there was already a lineTo to fFirstPoint, so this
2189        // addPt() is a no-op. Otherwise, the addPt implicitly closes the contour. In either case,
2190        // we have to check the direction change along the first vector in case it is concave.
2191        return this->addPt(fFirstPt) && this->addVec(fFirstVec);
2192    }
2193
2194    bool isFinite() const {
2195        return fIsFinite;
2196    }
2197
2198    int reversals() const {
2199        return fReversals;
2200    }
2201
2202private:
2203    DirChange directionChange(const SkVector& curVec) {
2204        SkScalar cross = SkPoint::CrossProduct(fLastVec, curVec);
2205        if (!SkScalarIsFinite(cross)) {
2206                return kUnknown_DirChange;
2207        }
2208        if (cross == 0) {
2209            return fLastVec.dot(curVec) < 0 ? kBackwards_DirChange : kStraight_DirChange;
2210        }
2211        return 1 == SkScalarSignAsInt(cross) ? kRight_DirChange : kLeft_DirChange;
2212    }
2213
2214    bool addVec(const SkVector& curVec) {
2215        DirChange dir = this->directionChange(curVec);
2216        switch (dir) {
2217            case kLeft_DirChange:       // fall through
2218            case kRight_DirChange:
2219                if (kInvalid_DirChange == fExpectedDir) {
2220                    fExpectedDir = dir;
2221                    fFirstDirection = (kRight_DirChange == dir) ? SkPathFirstDirection::kCW
2222                                                                : SkPathFirstDirection::kCCW;
2223                } else if (dir != fExpectedDir) {
2224                    fFirstDirection = SkPathFirstDirection::kUnknown;
2225                    return false;
2226                }
2227                fLastVec = curVec;
2228                break;
2229            case kStraight_DirChange:
2230                break;
2231            case kBackwards_DirChange:
2232                //  allow path to reverse direction twice
2233                //    Given path.moveTo(0, 0); path.lineTo(1, 1);
2234                //    - 1st reversal: direction change formed by line (0,0 1,1), line (1,1 0,0)
2235                //    - 2nd reversal: direction change formed by line (1,1 0,0), line (0,0 1,1)
2236                fLastVec = curVec;
2237                return ++fReversals < 3;
2238            case kUnknown_DirChange:
2239                return (fIsFinite = false);
2240            case kInvalid_DirChange:
2241                SK_ABORT("Use of invalid direction change flag");
2242                break;
2243        }
2244        return true;
2245    }
2246
2247    SkPoint              fFirstPt {0, 0};  // The first point of the contour, e.g. moveTo(x,y)
2248    SkVector             fFirstVec {0, 0}; // The direction leaving fFirstPt to the next vertex
2249
2250    SkPoint              fLastPt {0, 0};   // The last point passed to addPt()
2251    SkVector             fLastVec {0, 0};  // The direction that brought the path to fLastPt
2252
2253    DirChange            fExpectedDir { kInvalid_DirChange };
2254    SkPathFirstDirection fFirstDirection { SkPathFirstDirection::kUnknown };
2255    int                  fReversals { 0 };
2256    bool                 fIsFinite { true };
2257};
2258
2259SkPathConvexity SkPath::computeConvexity() const {
2260    auto setComputedConvexity = [=](SkPathConvexity convexity){
2261        SkASSERT(SkPathConvexity::kUnknown != convexity);
2262        this->setConvexity(convexity);
2263        return convexity;
2264    };
2265
2266    auto setFail = [=](){
2267        return setComputedConvexity(SkPathConvexity::kConcave);
2268    };
2269
2270    if (!this->isFinite()) {
2271        return setFail();
2272    }
2273
2274    // pointCount potentially includes a block of leading moveTos and trailing moveTos. Convexity
2275    // only cares about the last of the initial moveTos and the verbs before the final moveTos.
2276    int pointCount = this->countPoints();
2277    int skipCount = SkPathPriv::LeadingMoveToCount(*this) - 1;
2278
2279    if (fLastMoveToIndex >= 0) {
2280        if (fLastMoveToIndex == pointCount - 1) {
2281            // Find the last real verb that affects convexity
2282            auto verbs = fPathRef->verbsEnd() - 1;
2283            while(verbs > fPathRef->verbsBegin() && *verbs == Verb::kMove_Verb) {
2284                verbs--;
2285                pointCount--;
2286            }
2287        } else if (fLastMoveToIndex != skipCount) {
2288            // There's an additional moveTo between two blocks of other verbs, so the path must have
2289            // more than one contour and cannot be convex.
2290            return setComputedConvexity(SkPathConvexity::kConcave);
2291        } // else no trailing or intermediate moveTos to worry about
2292    }
2293    const SkPoint* points = fPathRef->points();
2294    if (skipCount > 0) {
2295        points += skipCount;
2296        pointCount -= skipCount;
2297    }
2298
2299    // Check to see if path changes direction more than three times as quick concave test
2300    SkPathConvexity convexity = Convexicator::BySign(points, pointCount);
2301    if (SkPathConvexity::kConvex != convexity) {
2302        return setComputedConvexity(SkPathConvexity::kConcave);
2303    }
2304
2305    int contourCount = 0;
2306    bool needsClose = false;
2307    Convexicator state;
2308
2309    for (auto [verb, pts, wt] : SkPathPriv::Iterate(*this)) {
2310        // Looking for the last moveTo before non-move verbs start
2311        if (contourCount == 0) {
2312            if (verb == SkPathVerb::kMove) {
2313                state.setMovePt(pts[0]);
2314            } else {
2315                // Starting the actual contour, fall through to c=1 to add the points
2316                contourCount++;
2317                needsClose = true;
2318            }
2319        }
2320        // Accumulating points into the Convexicator until we hit a close or another move
2321        if (contourCount == 1) {
2322            if (verb == SkPathVerb::kClose || verb == SkPathVerb::kMove) {
2323                if (!state.close()) {
2324                    return setFail();
2325                }
2326                needsClose = false;
2327                contourCount++;
2328            } else {
2329                // lines add 1 point, cubics add 3, conics and quads add 2
2330                int count = SkPathPriv::PtsInVerb((unsigned) verb);
2331                SkASSERT(count > 0);
2332                for (int i = 1; i <= count; ++i) {
2333                    if (!state.addPt(pts[i])) {
2334                        return setFail();
2335                    }
2336                }
2337            }
2338        } else {
2339            // The first contour has closed and anything other than spurious trailing moves means
2340            // there's multiple contours and the path can't be convex
2341            if (verb != SkPathVerb::kMove) {
2342                return setFail();
2343            }
2344        }
2345    }
2346
2347    // If the path isn't explicitly closed do so implicitly
2348    if (needsClose && !state.close()) {
2349        return setFail();
2350    }
2351
2352    if (this->getFirstDirection() == SkPathFirstDirection::kUnknown) {
2353        if (state.getFirstDirection() == SkPathFirstDirection::kUnknown
2354                && !this->getBounds().isEmpty()) {
2355            return setComputedConvexity(state.reversals() < 3 ?
2356                    SkPathConvexity::kConvex : SkPathConvexity::kConcave);
2357        }
2358        this->setFirstDirection(state.getFirstDirection());
2359    }
2360    return setComputedConvexity(SkPathConvexity::kConvex);
2361}
2362
2363///////////////////////////////////////////////////////////////////////////////
2364
2365class ContourIter {
2366public:
2367    ContourIter(const SkPathRef& pathRef);
2368
2369    bool done() const { return fDone; }
2370    // if !done() then these may be called
2371    int count() const { return fCurrPtCount; }
2372    const SkPoint* pts() const { return fCurrPt; }
2373    void next();
2374
2375private:
2376    int fCurrPtCount;
2377    const SkPoint* fCurrPt;
2378    const uint8_t* fCurrVerb;
2379    const uint8_t* fStopVerbs;
2380    const SkScalar* fCurrConicWeight;
2381    bool fDone;
2382    SkDEBUGCODE(int fContourCounter;)
2383};
2384
2385ContourIter::ContourIter(const SkPathRef& pathRef) {
2386    fStopVerbs = pathRef.verbsEnd();
2387    fDone = false;
2388    fCurrPt = pathRef.points();
2389    fCurrVerb = pathRef.verbsBegin();
2390    fCurrConicWeight = pathRef.conicWeights();
2391    fCurrPtCount = 0;
2392    SkDEBUGCODE(fContourCounter = 0;)
2393    this->next();
2394}
2395
2396void ContourIter::next() {
2397    if (fCurrVerb >= fStopVerbs) {
2398        fDone = true;
2399    }
2400    if (fDone) {
2401        return;
2402    }
2403
2404    // skip pts of prev contour
2405    fCurrPt += fCurrPtCount;
2406
2407    SkASSERT(SkPath::kMove_Verb == fCurrVerb[0]);
2408    int ptCount = 1;    // moveTo
2409    const uint8_t* verbs = fCurrVerb;
2410
2411    for (verbs++; verbs < fStopVerbs; verbs++) {
2412        switch (*verbs) {
2413            case SkPath::kMove_Verb:
2414                goto CONTOUR_END;
2415            case SkPath::kLine_Verb:
2416                ptCount += 1;
2417                break;
2418            case SkPath::kConic_Verb:
2419                fCurrConicWeight += 1;
2420                [[fallthrough]];
2421            case SkPath::kQuad_Verb:
2422                ptCount += 2;
2423                break;
2424            case SkPath::kCubic_Verb:
2425                ptCount += 3;
2426                break;
2427            case SkPath::kClose_Verb:
2428                break;
2429            default:
2430                SkDEBUGFAIL("unexpected verb");
2431                break;
2432        }
2433    }
2434CONTOUR_END:
2435    fCurrPtCount = ptCount;
2436    fCurrVerb = verbs;
2437    SkDEBUGCODE(++fContourCounter;)
2438}
2439
2440// returns cross product of (p1 - p0) and (p2 - p0)
2441static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
2442    SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0);
2443    // We may get 0 when the above subtracts underflow. We expect this to be
2444    // very rare and lazily promote to double.
2445    if (0 == cross) {
2446        double p0x = SkScalarToDouble(p0.fX);
2447        double p0y = SkScalarToDouble(p0.fY);
2448
2449        double p1x = SkScalarToDouble(p1.fX);
2450        double p1y = SkScalarToDouble(p1.fY);
2451
2452        double p2x = SkScalarToDouble(p2.fX);
2453        double p2y = SkScalarToDouble(p2.fY);
2454
2455        cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) -
2456                                 (p1y - p0y) * (p2x - p0x));
2457
2458    }
2459    return cross;
2460}
2461
2462// Returns the first pt with the maximum Y coordinate
2463static int find_max_y(const SkPoint pts[], int count) {
2464    SkASSERT(count > 0);
2465    SkScalar max = pts[0].fY;
2466    int firstIndex = 0;
2467    for (int i = 1; i < count; ++i) {
2468        SkScalar y = pts[i].fY;
2469        if (y > max) {
2470            max = y;
2471            firstIndex = i;
2472        }
2473    }
2474    return firstIndex;
2475}
2476
2477static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) {
2478    int i = index;
2479    for (;;) {
2480        i = (i + inc) % n;
2481        if (i == index) {   // we wrapped around, so abort
2482            break;
2483        }
2484        if (pts[index] != pts[i]) { // found a different point, success!
2485            break;
2486        }
2487    }
2488    return i;
2489}
2490
2491/**
2492 *  Starting at index, and moving forward (incrementing), find the xmin and
2493 *  xmax of the contiguous points that have the same Y.
2494 */
2495static int find_min_max_x_at_y(const SkPoint pts[], int index, int n,
2496                               int* maxIndexPtr) {
2497    const SkScalar y = pts[index].fY;
2498    SkScalar min = pts[index].fX;
2499    SkScalar max = min;
2500    int minIndex = index;
2501    int maxIndex = index;
2502    for (int i = index + 1; i < n; ++i) {
2503        if (pts[i].fY != y) {
2504            break;
2505        }
2506        SkScalar x = pts[i].fX;
2507        if (x < min) {
2508            min = x;
2509            minIndex = i;
2510        } else if (x > max) {
2511            max = x;
2512            maxIndex = i;
2513        }
2514    }
2515    *maxIndexPtr = maxIndex;
2516    return minIndex;
2517}
2518
2519static SkPathFirstDirection crossToDir(SkScalar cross) {
2520    return cross > 0 ? SkPathFirstDirection::kCW : SkPathFirstDirection::kCCW;
2521}
2522
2523/*
2524 *  We loop through all contours, and keep the computed cross-product of the
2525 *  contour that contained the global y-max. If we just look at the first
2526 *  contour, we may find one that is wound the opposite way (correctly) since
2527 *  it is the interior of a hole (e.g. 'o'). Thus we must find the contour
2528 *  that is outer most (or at least has the global y-max) before we can consider
2529 *  its cross product.
2530 */
2531SkPathFirstDirection SkPathPriv::ComputeFirstDirection(const SkPath& path) {
2532    auto d = path.getFirstDirection();
2533    if (d != SkPathFirstDirection::kUnknown) {
2534        return d;
2535    }
2536
2537    // We don't want to pay the cost for computing convexity if it is unknown,
2538    // so we call getConvexityOrUnknown() instead of isConvex().
2539    if (path.getConvexityOrUnknown() == SkPathConvexity::kConvex) {
2540        SkASSERT(d == SkPathFirstDirection::kUnknown);
2541        return d;
2542    }
2543
2544    ContourIter iter(*path.fPathRef);
2545
2546    // initialize with our logical y-min
2547    SkScalar ymax = path.getBounds().fTop;
2548    SkScalar ymaxCross = 0;
2549
2550    for (; !iter.done(); iter.next()) {
2551        int n = iter.count();
2552        if (n < 3) {
2553            continue;
2554        }
2555
2556        const SkPoint* pts = iter.pts();
2557        SkScalar cross = 0;
2558        int index = find_max_y(pts, n);
2559        if (pts[index].fY < ymax) {
2560            continue;
2561        }
2562
2563        // If there is more than 1 distinct point at the y-max, we take the
2564        // x-min and x-max of them and just subtract to compute the dir.
2565        if (pts[(index + 1) % n].fY == pts[index].fY) {
2566            int maxIndex;
2567            int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex);
2568            if (minIndex == maxIndex) {
2569                goto TRY_CROSSPROD;
2570            }
2571            SkASSERT(pts[minIndex].fY == pts[index].fY);
2572            SkASSERT(pts[maxIndex].fY == pts[index].fY);
2573            SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX);
2574            // we just subtract the indices, and let that auto-convert to
2575            // SkScalar, since we just want - or + to signal the direction.
2576            cross = minIndex - maxIndex;
2577        } else {
2578            TRY_CROSSPROD:
2579            // Find a next and prev index to use for the cross-product test,
2580            // but we try to find pts that form non-zero vectors from pts[index]
2581            //
2582            // Its possible that we can't find two non-degenerate vectors, so
2583            // we have to guard our search (e.g. all the pts could be in the
2584            // same place).
2585
2586            // we pass n - 1 instead of -1 so we don't foul up % operator by
2587            // passing it a negative LH argument.
2588            int prev = find_diff_pt(pts, index, n, n - 1);
2589            if (prev == index) {
2590                // completely degenerate, skip to next contour
2591                continue;
2592            }
2593            int next = find_diff_pt(pts, index, n, 1);
2594            SkASSERT(next != index);
2595            cross = cross_prod(pts[prev], pts[index], pts[next]);
2596            // if we get a zero and the points are horizontal, then we look at the spread in
2597            // x-direction. We really should continue to walk away from the degeneracy until
2598            // there is a divergence.
2599            if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) {
2600                // construct the subtract so we get the correct Direction below
2601                cross = pts[index].fX - pts[next].fX;
2602            }
2603        }
2604
2605        if (cross) {
2606            // record our best guess so far
2607            ymax = pts[index].fY;
2608            ymaxCross = cross;
2609        }
2610    }
2611    if (ymaxCross) {
2612        d = crossToDir(ymaxCross);
2613        path.setFirstDirection(d);
2614    }
2615    return d;   // may still be kUnknown
2616}
2617
2618///////////////////////////////////////////////////////////////////////////////
2619
2620static bool between(SkScalar a, SkScalar b, SkScalar c) {
2621    SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0)
2622            || (SkScalarNearlyZero(a) && SkScalarNearlyZero(b) && SkScalarNearlyZero(c)));
2623    return (a - b) * (c - b) <= 0;
2624}
2625
2626static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
2627                               SkScalar t) {
2628    SkScalar A = c3 + 3*(c1 - c2) - c0;
2629    SkScalar B = 3*(c2 - c1 - c1 + c0);
2630    SkScalar C = 3*(c1 - c0);
2631    SkScalar D = c0;
2632    return poly_eval(A, B, C, D, t);
2633}
2634
2635template <size_t N> static void find_minmax(const SkPoint pts[],
2636                                            SkScalar* minPtr, SkScalar* maxPtr) {
2637    SkScalar min, max;
2638    min = max = pts[0].fX;
2639    for (size_t i = 1; i < N; ++i) {
2640        min = std::min(min, pts[i].fX);
2641        max = std::max(max, pts[i].fX);
2642    }
2643    *minPtr = min;
2644    *maxPtr = max;
2645}
2646
2647static bool checkOnCurve(SkScalar x, SkScalar y, const SkPoint& start, const SkPoint& end) {
2648    if (start.fY == end.fY) {
2649        return between(start.fX, x, end.fX) && x != end.fX;
2650    } else {
2651        return x == start.fX && y == start.fY;
2652    }
2653}
2654
2655static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2656    SkScalar y0 = pts[0].fY;
2657    SkScalar y3 = pts[3].fY;
2658
2659    int dir = 1;
2660    if (y0 > y3) {
2661        using std::swap;
2662        swap(y0, y3);
2663        dir = -1;
2664    }
2665    if (y < y0 || y > y3) {
2666        return 0;
2667    }
2668    if (checkOnCurve(x, y, pts[0], pts[3])) {
2669        *onCurveCount += 1;
2670        return 0;
2671    }
2672    if (y == y3) {
2673        return 0;
2674    }
2675
2676    // quickreject or quickaccept
2677    SkScalar min, max;
2678    find_minmax<4>(pts, &min, &max);
2679    if (x < min) {
2680        return 0;
2681    }
2682    if (x > max) {
2683        return dir;
2684    }
2685
2686    // compute the actual x(t) value
2687    SkScalar t;
2688    if (!SkCubicClipper::ChopMonoAtY(pts, y, &t)) {
2689        return 0;
2690    }
2691    SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t);
2692    if (SkScalarNearlyEqual(xt, x)) {
2693        if (x != pts[3].fX || y != pts[3].fY) {  // don't test end points; they're start points
2694            *onCurveCount += 1;
2695            return 0;
2696        }
2697    }
2698    return xt < x ? dir : 0;
2699}
2700
2701static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2702    SkPoint dst[10];
2703    int n = SkChopCubicAtYExtrema(pts, dst);
2704    int w = 0;
2705    for (int i = 0; i <= n; ++i) {
2706        w += winding_mono_cubic(&dst[i * 3], x, y, onCurveCount);
2707    }
2708    return w;
2709}
2710
2711static double conic_eval_numerator(const SkScalar src[], SkScalar w, SkScalar t) {
2712    SkASSERT(src);
2713    SkASSERT(t >= 0 && t <= 1);
2714    SkScalar src2w = src[2] * w;
2715    SkScalar C = src[0];
2716    SkScalar A = src[4] - 2 * src2w + C;
2717    SkScalar B = 2 * (src2w - C);
2718    return poly_eval(A, B, C, t);
2719}
2720
2721
2722static double conic_eval_denominator(SkScalar w, SkScalar t) {
2723    SkScalar B = 2 * (w - 1);
2724    SkScalar C = 1;
2725    SkScalar A = -B;
2726    return poly_eval(A, B, C, t);
2727}
2728
2729static int winding_mono_conic(const SkConic& conic, SkScalar x, SkScalar y, int* onCurveCount) {
2730    const SkPoint* pts = conic.fPts;
2731    SkScalar y0 = pts[0].fY;
2732    SkScalar y2 = pts[2].fY;
2733
2734    int dir = 1;
2735    if (y0 > y2) {
2736        using std::swap;
2737        swap(y0, y2);
2738        dir = -1;
2739    }
2740    if (y < y0 || y > y2) {
2741        return 0;
2742    }
2743    if (checkOnCurve(x, y, pts[0], pts[2])) {
2744        *onCurveCount += 1;
2745        return 0;
2746    }
2747    if (y == y2) {
2748        return 0;
2749    }
2750
2751    SkScalar roots[2];
2752    SkScalar A = pts[2].fY;
2753    SkScalar B = pts[1].fY * conic.fW - y * conic.fW + y;
2754    SkScalar C = pts[0].fY;
2755    A += C - 2 * B;  // A = a + c - 2*(b*w - yCept*w + yCept)
2756    B -= C;  // B = b*w - w * yCept + yCept - a
2757    C -= y;
2758    int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
2759    SkASSERT(n <= 1);
2760    SkScalar xt;
2761    if (0 == n) {
2762        // zero roots are returned only when y0 == y
2763        // Need [0] if dir == 1
2764        // and  [2] if dir == -1
2765        xt = pts[1 - dir].fX;
2766    } else {
2767        SkScalar t = roots[0];
2768        xt = conic_eval_numerator(&pts[0].fX, conic.fW, t) / conic_eval_denominator(conic.fW, t);
2769    }
2770    if (SkScalarNearlyEqual(xt, x)) {
2771        if (x != pts[2].fX || y != pts[2].fY) {  // don't test end points; they're start points
2772            *onCurveCount += 1;
2773            return 0;
2774        }
2775    }
2776    return xt < x ? dir : 0;
2777}
2778
2779static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) {
2780    //    return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0;
2781    if (y0 == y1) {
2782        return true;
2783    }
2784    if (y0 < y1) {
2785        return y1 <= y2;
2786    } else {
2787        return y1 >= y2;
2788    }
2789}
2790
2791static int winding_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar weight,
2792                         int* onCurveCount) {
2793    SkConic conic(pts, weight);
2794    SkConic chopped[2];
2795    // If the data points are very large, the conic may not be monotonic but may also
2796    // fail to chop. Then, the chopper does not split the original conic in two.
2797    bool isMono = is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY) || !conic.chopAtYExtrema(chopped);
2798    int w = winding_mono_conic(isMono ? conic : chopped[0], x, y, onCurveCount);
2799    if (!isMono) {
2800        w += winding_mono_conic(chopped[1], x, y, onCurveCount);
2801    }
2802    return w;
2803}
2804
2805static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2806    SkScalar y0 = pts[0].fY;
2807    SkScalar y2 = pts[2].fY;
2808
2809    int dir = 1;
2810    if (y0 > y2) {
2811        using std::swap;
2812        swap(y0, y2);
2813        dir = -1;
2814    }
2815    if (y < y0 || y > y2) {
2816        return 0;
2817    }
2818    if (checkOnCurve(x, y, pts[0], pts[2])) {
2819        *onCurveCount += 1;
2820        return 0;
2821    }
2822    if (y == y2) {
2823        return 0;
2824    }
2825    // bounds check on X (not required. is it faster?)
2826#if 0
2827    if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) {
2828        return 0;
2829    }
2830#endif
2831
2832    SkScalar roots[2];
2833    int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
2834                                2 * (pts[1].fY - pts[0].fY),
2835                                pts[0].fY - y,
2836                                roots);
2837    SkASSERT(n <= 1);
2838    SkScalar xt;
2839    if (0 == n) {
2840        // zero roots are returned only when y0 == y
2841        // Need [0] if dir == 1
2842        // and  [2] if dir == -1
2843        xt = pts[1 - dir].fX;
2844    } else {
2845        SkScalar t = roots[0];
2846        SkScalar C = pts[0].fX;
2847        SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
2848        SkScalar B = 2 * (pts[1].fX - C);
2849        xt = poly_eval(A, B, C, t);
2850    }
2851    if (SkScalarNearlyEqual(xt, x)) {
2852        if (x != pts[2].fX || y != pts[2].fY) {  // don't test end points; they're start points
2853            *onCurveCount += 1;
2854            return 0;
2855        }
2856    }
2857    return xt < x ? dir : 0;
2858}
2859
2860static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2861    SkPoint dst[5];
2862    int     n = 0;
2863
2864    if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) {
2865        n = SkChopQuadAtYExtrema(pts, dst);
2866        pts = dst;
2867    }
2868    int w = winding_mono_quad(pts, x, y, onCurveCount);
2869    if (n > 0) {
2870        w += winding_mono_quad(&pts[2], x, y, onCurveCount);
2871    }
2872    return w;
2873}
2874
2875static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2876    SkScalar x0 = pts[0].fX;
2877    SkScalar y0 = pts[0].fY;
2878    SkScalar x1 = pts[1].fX;
2879    SkScalar y1 = pts[1].fY;
2880
2881    SkScalar dy = y1 - y0;
2882
2883    int dir = 1;
2884    if (y0 > y1) {
2885        using std::swap;
2886        swap(y0, y1);
2887        dir = -1;
2888    }
2889    if (y < y0 || y > y1) {
2890        return 0;
2891    }
2892    if (checkOnCurve(x, y, pts[0], pts[1])) {
2893        *onCurveCount += 1;
2894        return 0;
2895    }
2896    if (y == y1) {
2897        return 0;
2898    }
2899    SkScalar cross = (x1 - x0) * (y - pts[0].fY) - dy * (x - x0);
2900
2901    if (!cross) {
2902        // zero cross means the point is on the line, and since the case where
2903        // y of the query point is at the end point is handled above, we can be
2904        // sure that we're on the line (excluding the end point) here
2905        if (x != x1 || y != pts[1].fY) {
2906            *onCurveCount += 1;
2907        }
2908        dir = 0;
2909    } else if (SkScalarSignAsInt(cross) == dir) {
2910        dir = 0;
2911    }
2912    return dir;
2913}
2914
2915static void tangent_cubic(const SkPoint pts[], SkScalar x, SkScalar y,
2916        SkTDArray<SkVector>* tangents) {
2917    if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)
2918             && !between(pts[2].fY, y, pts[3].fY)) {
2919        return;
2920    }
2921    if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)
2922             && !between(pts[2].fX, x, pts[3].fX)) {
2923        return;
2924    }
2925    SkPoint dst[10];
2926    int n = SkChopCubicAtYExtrema(pts, dst);
2927    for (int i = 0; i <= n; ++i) {
2928        SkPoint* c = &dst[i * 3];
2929        SkScalar t;
2930        if (!SkCubicClipper::ChopMonoAtY(c, y, &t)) {
2931            continue;
2932        }
2933        SkScalar xt = eval_cubic_pts(c[0].fX, c[1].fX, c[2].fX, c[3].fX, t);
2934        if (!SkScalarNearlyEqual(x, xt)) {
2935            continue;
2936        }
2937        SkVector tangent;
2938        SkEvalCubicAt(c, t, nullptr, &tangent, nullptr);
2939        tangents->push_back(tangent);
2940    }
2941}
2942
2943static void tangent_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar w,
2944            SkTDArray<SkVector>* tangents) {
2945    if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
2946        return;
2947    }
2948    if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
2949        return;
2950    }
2951    SkScalar roots[2];
2952    SkScalar A = pts[2].fY;
2953    SkScalar B = pts[1].fY * w - y * w + y;
2954    SkScalar C = pts[0].fY;
2955    A += C - 2 * B;  // A = a + c - 2*(b*w - yCept*w + yCept)
2956    B -= C;  // B = b*w - w * yCept + yCept - a
2957    C -= y;
2958    int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
2959    for (int index = 0; index < n; ++index) {
2960        SkScalar t = roots[index];
2961        SkScalar xt = conic_eval_numerator(&pts[0].fX, w, t) / conic_eval_denominator(w, t);
2962        if (!SkScalarNearlyEqual(x, xt)) {
2963            continue;
2964        }
2965        SkConic conic(pts, w);
2966        tangents->push_back(conic.evalTangentAt(t));
2967    }
2968}
2969
2970static void tangent_quad(const SkPoint pts[], SkScalar x, SkScalar y,
2971        SkTDArray<SkVector>* tangents) {
2972    if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
2973        return;
2974    }
2975    if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
2976        return;
2977    }
2978    SkScalar roots[2];
2979    int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
2980                                2 * (pts[1].fY - pts[0].fY),
2981                                pts[0].fY - y,
2982                                roots);
2983    for (int index = 0; index < n; ++index) {
2984        SkScalar t = roots[index];
2985        SkScalar C = pts[0].fX;
2986        SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
2987        SkScalar B = 2 * (pts[1].fX - C);
2988        SkScalar xt = poly_eval(A, B, C, t);
2989        if (!SkScalarNearlyEqual(x, xt)) {
2990            continue;
2991        }
2992        tangents->push_back(SkEvalQuadTangentAt(pts, t));
2993    }
2994}
2995
2996static void tangent_line(const SkPoint pts[], SkScalar x, SkScalar y,
2997        SkTDArray<SkVector>* tangents) {
2998    SkScalar y0 = pts[0].fY;
2999    SkScalar y1 = pts[1].fY;
3000    if (!between(y0, y, y1)) {
3001        return;
3002    }
3003    SkScalar x0 = pts[0].fX;
3004    SkScalar x1 = pts[1].fX;
3005    if (!between(x0, x, x1)) {
3006        return;
3007    }
3008    SkScalar dx = x1 - x0;
3009    SkScalar dy = y1 - y0;
3010    if (!SkScalarNearlyEqual((x - x0) * dy, dx * (y - y0))) {
3011        return;
3012    }
3013    SkVector v;
3014    v.set(dx, dy);
3015    tangents->push_back(v);
3016}
3017
3018static bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) {
3019    return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom;
3020}
3021
3022bool SkPath::contains(SkScalar x, SkScalar y) const {
3023    bool isInverse = this->isInverseFillType();
3024    if (this->isEmpty()) {
3025        return isInverse;
3026    }
3027
3028    if (!contains_inclusive(this->getBounds(), x, y)) {
3029        return isInverse;
3030    }
3031
3032    SkPath::Iter iter(*this, true);
3033    bool done = false;
3034    int w = 0;
3035    int onCurveCount = 0;
3036    do {
3037        SkPoint pts[4];
3038        switch (iter.next(pts)) {
3039            case SkPath::kMove_Verb:
3040            case SkPath::kClose_Verb:
3041                break;
3042            case SkPath::kLine_Verb:
3043                w += winding_line(pts, x, y, &onCurveCount);
3044                break;
3045            case SkPath::kQuad_Verb:
3046                w += winding_quad(pts, x, y, &onCurveCount);
3047                break;
3048            case SkPath::kConic_Verb:
3049                w += winding_conic(pts, x, y, iter.conicWeight(), &onCurveCount);
3050                break;
3051            case SkPath::kCubic_Verb:
3052                w += winding_cubic(pts, x, y, &onCurveCount);
3053                break;
3054            case SkPath::kDone_Verb:
3055                done = true;
3056                break;
3057       }
3058    } while (!done);
3059    bool evenOddFill = SkPathFillType::kEvenOdd        == this->getFillType()
3060                    || SkPathFillType::kInverseEvenOdd == this->getFillType();
3061    if (evenOddFill) {
3062        w &= 1;
3063    }
3064    if (w) {
3065        return !isInverse;
3066    }
3067    if (onCurveCount <= 1) {
3068        return SkToBool(onCurveCount) ^ isInverse;
3069    }
3070    if ((onCurveCount & 1) || evenOddFill) {
3071        return SkToBool(onCurveCount & 1) ^ isInverse;
3072    }
3073    // If the point touches an even number of curves, and the fill is winding, check for
3074    // coincidence. Count coincidence as places where the on curve points have identical tangents.
3075    iter.setPath(*this, true);
3076    done = false;
3077    SkTDArray<SkVector> tangents;
3078    do {
3079        SkPoint pts[4];
3080        int oldCount = tangents.count();
3081        switch (iter.next(pts)) {
3082            case SkPath::kMove_Verb:
3083            case SkPath::kClose_Verb:
3084                break;
3085            case SkPath::kLine_Verb:
3086                tangent_line(pts, x, y, &tangents);
3087                break;
3088            case SkPath::kQuad_Verb:
3089                tangent_quad(pts, x, y, &tangents);
3090                break;
3091            case SkPath::kConic_Verb:
3092                tangent_conic(pts, x, y, iter.conicWeight(), &tangents);
3093                break;
3094            case SkPath::kCubic_Verb:
3095                tangent_cubic(pts, x, y, &tangents);
3096                break;
3097            case SkPath::kDone_Verb:
3098                done = true;
3099                break;
3100       }
3101       if (tangents.count() > oldCount) {
3102            int last = tangents.count() - 1;
3103            const SkVector& tangent = tangents[last];
3104            if (SkScalarNearlyZero(SkPointPriv::LengthSqd(tangent))) {
3105                tangents.remove(last);
3106            } else {
3107                for (int index = 0; index < last; ++index) {
3108                    const SkVector& test = tangents[index];
3109                    if (SkScalarNearlyZero(test.cross(tangent))
3110                            && SkScalarSignAsInt(tangent.fX * test.fX) <= 0
3111                            && SkScalarSignAsInt(tangent.fY * test.fY) <= 0) {
3112                        tangents.remove(last);
3113                        tangents.removeShuffle(index);
3114                        break;
3115                    }
3116                }
3117            }
3118        }
3119    } while (!done);
3120    return SkToBool(tangents.count()) ^ isInverse;
3121}
3122
3123int SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
3124                                SkScalar w, SkPoint pts[], int pow2) {
3125    const SkConic conic(p0, p1, p2, w);
3126    return conic.chopIntoQuadsPOW2(pts, pow2);
3127}
3128
3129bool SkPathPriv::IsSimpleRect(const SkPath& path, bool isSimpleFill, SkRect* rect,
3130                              SkPathDirection* direction, unsigned* start) {
3131    if (path.getSegmentMasks() != SkPath::kLine_SegmentMask) {
3132        return false;
3133    }
3134    SkPoint rectPts[5];
3135    int rectPtCnt = 0;
3136    bool needsClose = !isSimpleFill;
3137    for (auto [v, verbPts, w] : SkPathPriv::Iterate(path)) {
3138        switch (v) {
3139            case SkPathVerb::kMove:
3140                if (0 != rectPtCnt) {
3141                    return false;
3142                }
3143                rectPts[0] = verbPts[0];
3144                ++rectPtCnt;
3145                break;
3146            case SkPathVerb::kLine:
3147                if (5 == rectPtCnt) {
3148                    return false;
3149                }
3150                rectPts[rectPtCnt] = verbPts[1];
3151                ++rectPtCnt;
3152                break;
3153            case SkPathVerb::kClose:
3154                if (4 == rectPtCnt) {
3155                    rectPts[4] = rectPts[0];
3156                    rectPtCnt = 5;
3157                }
3158                needsClose = false;
3159                break;
3160            case SkPathVerb::kQuad:
3161            case SkPathVerb::kConic:
3162            case SkPathVerb::kCubic:
3163                return false;
3164        }
3165    }
3166    if (needsClose) {
3167        return false;
3168    }
3169    if (rectPtCnt < 5) {
3170        return false;
3171    }
3172    if (rectPts[0] != rectPts[4]) {
3173        return false;
3174    }
3175    // Check for two cases of rectangles: pts 0 and 3 form a vertical edge or a horizontal edge (
3176    // and pts 1 and 2 the opposite vertical or horizontal edge).
3177    bool vec03IsVertical;
3178    if (rectPts[0].fX == rectPts[3].fX && rectPts[1].fX == rectPts[2].fX &&
3179        rectPts[0].fY == rectPts[1].fY && rectPts[3].fY == rectPts[2].fY) {
3180        // Make sure it has non-zero width and height
3181        if (rectPts[0].fX == rectPts[1].fX || rectPts[0].fY == rectPts[3].fY) {
3182            return false;
3183        }
3184        vec03IsVertical = true;
3185    } else if (rectPts[0].fY == rectPts[3].fY && rectPts[1].fY == rectPts[2].fY &&
3186               rectPts[0].fX == rectPts[1].fX && rectPts[3].fX == rectPts[2].fX) {
3187        // Make sure it has non-zero width and height
3188        if (rectPts[0].fY == rectPts[1].fY || rectPts[0].fX == rectPts[3].fX) {
3189            return false;
3190        }
3191        vec03IsVertical = false;
3192    } else {
3193        return false;
3194    }
3195    // Set sortFlags so that it has the low bit set if pt index 0 is on right edge and second bit
3196    // set if it is on the bottom edge.
3197    unsigned sortFlags =
3198            ((rectPts[0].fX < rectPts[2].fX) ? 0b00 : 0b01) |
3199            ((rectPts[0].fY < rectPts[2].fY) ? 0b00 : 0b10);
3200    switch (sortFlags) {
3201        case 0b00:
3202            rect->setLTRB(rectPts[0].fX, rectPts[0].fY, rectPts[2].fX, rectPts[2].fY);
3203            *direction = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW;
3204            *start = 0;
3205            break;
3206        case 0b01:
3207            rect->setLTRB(rectPts[2].fX, rectPts[0].fY, rectPts[0].fX, rectPts[2].fY);
3208            *direction = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW;
3209            *start = 1;
3210            break;
3211        case 0b10:
3212            rect->setLTRB(rectPts[0].fX, rectPts[2].fY, rectPts[2].fX, rectPts[0].fY);
3213            *direction = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW;
3214            *start = 3;
3215            break;
3216        case 0b11:
3217            rect->setLTRB(rectPts[2].fX, rectPts[2].fY, rectPts[0].fX, rectPts[0].fY);
3218            *direction = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW;
3219            *start = 2;
3220            break;
3221    }
3222    return true;
3223}
3224
3225bool SkPathPriv::DrawArcIsConvex(SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
3226    if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
3227        // This gets converted to an oval.
3228        return true;
3229    }
3230    if (useCenter) {
3231        // This is a pie wedge. It's convex if the angle is <= 180.
3232        return SkScalarAbs(sweepAngle) <= 180.f;
3233    }
3234    // When the angle exceeds 360 this wraps back on top of itself. Otherwise it is a circle clipped
3235    // to a secant, i.e. convex.
3236    return SkScalarAbs(sweepAngle) <= 360.f;
3237}
3238
3239void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle,
3240                                   SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
3241    SkASSERT(!oval.isEmpty());
3242    SkASSERT(sweepAngle);
3243#if defined(SK_BUILD_FOR_FUZZER)
3244    if (sweepAngle > 3600.0f || sweepAngle < -3600.0f) {
3245        return;
3246    }
3247#endif
3248    path->reset();
3249    path->setIsVolatile(true);
3250    path->setFillType(SkPathFillType::kWinding);
3251    if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
3252        path->addOval(oval);
3253        SkASSERT(path->isConvex() && DrawArcIsConvex(sweepAngle, false, isFillNoPathEffect));
3254        return;
3255    }
3256    if (useCenter) {
3257        path->moveTo(oval.centerX(), oval.centerY());
3258    }
3259    auto firstDir =
3260            sweepAngle > 0 ? SkPathFirstDirection::kCW : SkPathFirstDirection::kCCW;
3261    bool convex = DrawArcIsConvex(sweepAngle, useCenter, isFillNoPathEffect);
3262    // Arc to mods at 360 and drawArc is not supposed to.
3263    bool forceMoveTo = !useCenter;
3264    while (sweepAngle <= -360.f) {
3265        path->arcTo(oval, startAngle, -180.f, forceMoveTo);
3266        startAngle -= 180.f;
3267        path->arcTo(oval, startAngle, -180.f, false);
3268        startAngle -= 180.f;
3269        forceMoveTo = false;
3270        sweepAngle += 360.f;
3271    }
3272    while (sweepAngle >= 360.f) {
3273        path->arcTo(oval, startAngle, 180.f, forceMoveTo);
3274        startAngle += 180.f;
3275        path->arcTo(oval, startAngle, 180.f, false);
3276        startAngle += 180.f;
3277        forceMoveTo = false;
3278        sweepAngle -= 360.f;
3279    }
3280    path->arcTo(oval, startAngle, sweepAngle, forceMoveTo);
3281    if (useCenter) {
3282        path->close();
3283    }
3284    path->setConvexity(convex ? SkPathConvexity::kConvex : SkPathConvexity::kConcave);
3285    path->setFirstDirection(firstDir);
3286}
3287
3288///////////////////////////////////////////////////////////////////////////////////////////////////
3289#include "include/private/SkNx.h"
3290
3291static int compute_quad_extremas(const SkPoint src[3], SkPoint extremas[3]) {
3292    SkScalar ts[2];
3293    int n  = SkFindQuadExtrema(src[0].fX, src[1].fX, src[2].fX, ts);
3294        n += SkFindQuadExtrema(src[0].fY, src[1].fY, src[2].fY, &ts[n]);
3295    SkASSERT(n >= 0 && n <= 2);
3296    for (int i = 0; i < n; ++i) {
3297        extremas[i] = SkEvalQuadAt(src, ts[i]);
3298    }
3299    extremas[n] = src[2];
3300    return n + 1;
3301}
3302
3303static int compute_conic_extremas(const SkPoint src[3], SkScalar w, SkPoint extremas[3]) {
3304    SkConic conic(src[0], src[1], src[2], w);
3305    SkScalar ts[2];
3306    int n  = conic.findXExtrema(ts);
3307        n += conic.findYExtrema(&ts[n]);
3308    SkASSERT(n >= 0 && n <= 2);
3309    for (int i = 0; i < n; ++i) {
3310        extremas[i] = conic.evalAt(ts[i]);
3311    }
3312    extremas[n] = src[2];
3313    return n + 1;
3314}
3315
3316static int compute_cubic_extremas(const SkPoint src[4], SkPoint extremas[5]) {
3317    SkScalar ts[4];
3318    int n  = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX, src[3].fX, ts);
3319        n += SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY, src[3].fY, &ts[n]);
3320    SkASSERT(n >= 0 && n <= 4);
3321    for (int i = 0; i < n; ++i) {
3322        SkEvalCubicAt(src, ts[i], &extremas[i], nullptr, nullptr);
3323    }
3324    extremas[n] = src[3];
3325    return n + 1;
3326}
3327
3328SkRect SkPath::computeTightBounds() const {
3329    if (0 == this->countVerbs()) {
3330        return SkRect::MakeEmpty();
3331    }
3332
3333    if (this->getSegmentMasks() == SkPath::kLine_SegmentMask) {
3334        return this->getBounds();
3335    }
3336
3337    SkPoint extremas[5]; // big enough to hold worst-case curve type (cubic) extremas + 1
3338
3339    // initial with the first MoveTo, so we don't have to check inside the switch
3340    Sk2s min, max;
3341    min = max = from_point(this->getPoint(0));
3342    for (auto [verb, pts, w] : SkPathPriv::Iterate(*this)) {
3343        int count = 0;
3344        switch (verb) {
3345            case SkPathVerb::kMove:
3346                extremas[0] = pts[0];
3347                count = 1;
3348                break;
3349            case SkPathVerb::kLine:
3350                extremas[0] = pts[1];
3351                count = 1;
3352                break;
3353            case SkPathVerb::kQuad:
3354                count = compute_quad_extremas(pts, extremas);
3355                break;
3356            case SkPathVerb::kConic:
3357                count = compute_conic_extremas(pts, *w, extremas);
3358                break;
3359            case SkPathVerb::kCubic:
3360                count = compute_cubic_extremas(pts, extremas);
3361                break;
3362            case SkPathVerb::kClose:
3363                break;
3364        }
3365        for (int i = 0; i < count; ++i) {
3366            Sk2s tmp = from_point(extremas[i]);
3367            min = Sk2s::Min(min, tmp);
3368            max = Sk2s::Max(max, tmp);
3369        }
3370    }
3371    SkRect bounds;
3372    min.store((SkPoint*)&bounds.fLeft);
3373    max.store((SkPoint*)&bounds.fRight);
3374    return bounds;
3375}
3376
3377bool SkPath::IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact) {
3378    return exact ? p1 == p2 : SkPointPriv::EqualsWithinTolerance(p1, p2);
3379}
3380
3381bool SkPath::IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
3382                                const SkPoint& p3, bool exact) {
3383    return exact ? p1 == p2 && p2 == p3 : SkPointPriv::EqualsWithinTolerance(p1, p2) &&
3384            SkPointPriv::EqualsWithinTolerance(p2, p3);
3385}
3386
3387bool SkPath::IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
3388                                const SkPoint& p3, const SkPoint& p4, bool exact) {
3389    return exact ? p1 == p2 && p2 == p3 && p3 == p4 :
3390            SkPointPriv::EqualsWithinTolerance(p1, p2) &&
3391            SkPointPriv::EqualsWithinTolerance(p2, p3) &&
3392            SkPointPriv::EqualsWithinTolerance(p3, p4);
3393}
3394
3395//////////////////////////////////////////////////////////////////////////////////////////////////
3396
3397SkPathVerbAnalysis sk_path_analyze_verbs(const uint8_t vbs[], int verbCount) {
3398    SkPathVerbAnalysis info = {false, 0, 0, 0};
3399
3400    bool needMove = true;
3401    bool invalid = false;
3402    for (int i = 0; i < verbCount; ++i) {
3403        switch ((SkPathVerb)vbs[i]) {
3404            case SkPathVerb::kMove:
3405                needMove = false;
3406                info.points += 1;
3407                break;
3408            case SkPathVerb::kLine:
3409                invalid |= needMove;
3410                info.segmentMask |= kLine_SkPathSegmentMask;
3411                info.points += 1;
3412                break;
3413            case SkPathVerb::kQuad:
3414                invalid |= needMove;
3415                info.segmentMask |= kQuad_SkPathSegmentMask;
3416                info.points += 2;
3417                break;
3418            case SkPathVerb::kConic:
3419                invalid |= needMove;
3420                info.segmentMask |= kConic_SkPathSegmentMask;
3421                info.points += 2;
3422                info.weights += 1;
3423                break;
3424            case SkPathVerb::kCubic:
3425                invalid |= needMove;
3426                info.segmentMask |= kCubic_SkPathSegmentMask;
3427                info.points += 3;
3428                break;
3429            case SkPathVerb::kClose:
3430                invalid |= needMove;
3431                needMove = true;
3432                break;
3433            default:
3434                invalid = true;
3435                break;
3436        }
3437    }
3438    info.valid = !invalid;
3439    return info;
3440}
3441
3442SkPath SkPath::Make(const SkPoint pts[], int pointCount,
3443                    const uint8_t vbs[], int verbCount,
3444                    const SkScalar ws[], int wCount,
3445                    SkPathFillType ft, bool isVolatile) {
3446    if (verbCount <= 0) {
3447        return SkPath();
3448    }
3449
3450    const auto info = sk_path_analyze_verbs(vbs, verbCount);
3451    if (!info.valid || info.points > pointCount || info.weights > wCount) {
3452        SkDEBUGFAIL("invalid verbs and number of points/weights");
3453        return SkPath();
3454    }
3455
3456    return SkPath(sk_sp<SkPathRef>(new SkPathRef(SkTDArray<SkPoint>(pts, info.points),
3457                                                 SkTDArray<uint8_t>(vbs, verbCount),
3458                                                 SkTDArray<SkScalar>(ws, info.weights),
3459                                                 info.segmentMask)),
3460                  ft, isVolatile, SkPathConvexity::kUnknown, SkPathFirstDirection::kUnknown);
3461}
3462
3463SkPath SkPath::Rect(const SkRect& r, SkPathDirection dir, unsigned startIndex) {
3464    return SkPathBuilder().addRect(r, dir, startIndex).detach();
3465}
3466
3467SkPath SkPath::Oval(const SkRect& r, SkPathDirection dir) {
3468    return SkPathBuilder().addOval(r, dir).detach();
3469}
3470
3471SkPath SkPath::Oval(const SkRect& r, SkPathDirection dir, unsigned startIndex) {
3472    return SkPathBuilder().addOval(r, dir, startIndex).detach();
3473}
3474
3475SkPath SkPath::Circle(SkScalar x, SkScalar y, SkScalar r, SkPathDirection dir) {
3476    return SkPathBuilder().addCircle(x, y, r, dir).detach();
3477}
3478
3479SkPath SkPath::RRect(const SkRRect& rr, SkPathDirection dir) {
3480    return SkPathBuilder().addRRect(rr, dir).detach();
3481}
3482
3483SkPath SkPath::RRect(const SkRRect& rr, SkPathDirection dir, unsigned startIndex) {
3484    return SkPathBuilder().addRRect(rr, dir, startIndex).detach();
3485}
3486
3487SkPath SkPath::RRect(const SkRect& r, SkScalar rx, SkScalar ry, SkPathDirection dir) {
3488    return SkPathBuilder().addRRect(SkRRect::MakeRectXY(r, rx, ry), dir).detach();
3489}
3490
3491SkPath SkPath::Polygon(const SkPoint pts[], int count, bool isClosed,
3492                       SkPathFillType ft, bool isVolatile) {
3493    return SkPathBuilder().addPolygon(pts, count, isClosed)
3494                          .setFillType(ft)
3495                          .setIsVolatile(isVolatile)
3496                          .detach();
3497}
3498
3499//////////////////////////////////////////////////////////////////////////////////////////////////
3500
3501bool SkPathPriv::IsRectContour(const SkPath& path, bool allowPartial, int* currVerb,
3502                               const SkPoint** ptsPtr, bool* isClosed, SkPathDirection* direction,
3503                               SkRect* rect) {
3504    int corners = 0;
3505    SkPoint closeXY;  // used to determine if final line falls on a diagonal
3506    SkPoint lineStart;  // used to construct line from previous point
3507    const SkPoint* firstPt = nullptr; // first point in the rect (last of first moves)
3508    const SkPoint* lastPt = nullptr;  // last point in the rect (last of lines or first if closed)
3509    SkPoint firstCorner;
3510    SkPoint thirdCorner;
3511    const SkPoint* pts = *ptsPtr;
3512    const SkPoint* savePts = nullptr; // used to allow caller to iterate through a pair of rects
3513    lineStart.set(0, 0);
3514    signed char directions[] = {-1, -1, -1, -1, -1};  // -1 to 3; -1 is uninitialized
3515    bool closedOrMoved = false;
3516    bool autoClose = false;
3517    bool insertClose = false;
3518    int verbCnt = path.fPathRef->countVerbs();
3519    while (*currVerb < verbCnt && (!allowPartial || !autoClose)) {
3520        uint8_t verb = insertClose ? (uint8_t) SkPath::kClose_Verb : path.fPathRef->atVerb(*currVerb);
3521        switch (verb) {
3522            case SkPath::kClose_Verb:
3523                savePts = pts;
3524                autoClose = true;
3525                insertClose = false;
3526                [[fallthrough]];
3527            case SkPath::kLine_Verb: {
3528                if (SkPath::kClose_Verb != verb) {
3529                    lastPt = pts;
3530                }
3531                SkPoint lineEnd = SkPath::kClose_Verb == verb ? *firstPt : *pts++;
3532                SkVector lineDelta = lineEnd - lineStart;
3533                if (lineDelta.fX && lineDelta.fY) {
3534                    return false; // diagonal
3535                }
3536                if (!lineDelta.isFinite()) {
3537                    return false; // path contains infinity or NaN
3538                }
3539                if (lineStart == lineEnd) {
3540                    break; // single point on side OK
3541                }
3542                int nextDirection = rect_make_dir(lineDelta.fX, lineDelta.fY); // 0 to 3
3543                if (0 == corners) {
3544                    directions[0] = nextDirection;
3545                    corners = 1;
3546                    closedOrMoved = false;
3547                    lineStart = lineEnd;
3548                    break;
3549                }
3550                if (closedOrMoved) {
3551                    return false; // closed followed by a line
3552                }
3553                if (autoClose && nextDirection == directions[0]) {
3554                    break; // colinear with first
3555                }
3556                closedOrMoved = autoClose;
3557                if (directions[corners - 1] == nextDirection) {
3558                    if (3 == corners && SkPath::kLine_Verb == verb) {
3559                        thirdCorner = lineEnd;
3560                    }
3561                    lineStart = lineEnd;
3562                    break; // colinear segment
3563                }
3564                directions[corners++] = nextDirection;
3565                // opposite lines must point in opposite directions; xoring them should equal 2
3566                switch (corners) {
3567                    case 2:
3568                        firstCorner = lineStart;
3569                        break;
3570                    case 3:
3571                        if ((directions[0] ^ directions[2]) != 2) {
3572                            return false;
3573                        }
3574                        thirdCorner = lineEnd;
3575                        break;
3576                    case 4:
3577                        if ((directions[1] ^ directions[3]) != 2) {
3578                            return false;
3579                        }
3580                        break;
3581                    default:
3582                        return false; // too many direction changes
3583                }
3584                lineStart = lineEnd;
3585                break;
3586            }
3587            case SkPath::kQuad_Verb:
3588            case SkPath::kConic_Verb:
3589            case SkPath::kCubic_Verb:
3590                return false; // quadratic, cubic not allowed
3591            case SkPath::kMove_Verb:
3592                if (allowPartial && !autoClose && directions[0] >= 0) {
3593                    insertClose = true;
3594                    *currVerb -= 1;  // try move again afterwards
3595                    goto addMissingClose;
3596                }
3597                if (!corners) {
3598                    firstPt = pts;
3599                } else {
3600                    closeXY = *firstPt - *lastPt;
3601                    if (closeXY.fX && closeXY.fY) {
3602                        return false;   // we're diagonal, abort
3603                    }
3604                }
3605                lineStart = *pts++;
3606                closedOrMoved = true;
3607                break;
3608            default:
3609                SkDEBUGFAIL("unexpected verb");
3610                break;
3611        }
3612        *currVerb += 1;
3613    addMissingClose:
3614        ;
3615    }
3616    // Success if 4 corners and first point equals last
3617    if (corners < 3 || corners > 4) {
3618        return false;
3619    }
3620    if (savePts) {
3621        *ptsPtr = savePts;
3622    }
3623    // check if close generates diagonal
3624    closeXY = *firstPt - *lastPt;
3625    if (closeXY.fX && closeXY.fY) {
3626        return false;
3627    }
3628    if (rect) {
3629        rect->set(firstCorner, thirdCorner);
3630    }
3631    if (isClosed) {
3632        *isClosed = autoClose;
3633    }
3634    if (direction) {
3635        *direction = directions[0] == ((directions[1] + 1) & 3) ?
3636                     SkPathDirection::kCW : SkPathDirection::kCCW;
3637    }
3638    return true;
3639}
3640
3641
3642bool SkPathPriv::IsNestedFillRects(const SkPath& path, SkRect rects[2], SkPathDirection dirs[2]) {
3643    SkDEBUGCODE(path.validate();)
3644    int currVerb = 0;
3645    const SkPoint* pts = path.fPathRef->points();
3646    SkPathDirection testDirs[2];
3647    SkRect testRects[2];
3648    if (!IsRectContour(path, true, &currVerb, &pts, nullptr, &testDirs[0], &testRects[0])) {
3649        return false;
3650    }
3651    if (IsRectContour(path, false, &currVerb, &pts, nullptr, &testDirs[1], &testRects[1])) {
3652        if (testRects[0].contains(testRects[1])) {
3653            if (rects) {
3654                rects[0] = testRects[0];
3655                rects[1] = testRects[1];
3656            }
3657            if (dirs) {
3658                dirs[0] = testDirs[0];
3659                dirs[1] = testDirs[1];
3660            }
3661            return true;
3662        }
3663        if (testRects[1].contains(testRects[0])) {
3664            if (rects) {
3665                rects[0] = testRects[1];
3666                rects[1] = testRects[0];
3667            }
3668            if (dirs) {
3669                dirs[0] = testDirs[1];
3670                dirs[1] = testDirs[0];
3671            }
3672            return true;
3673        }
3674    }
3675    return false;
3676}
3677
3678///////////////////////////////////////////////////////////////////////////////////////////////////
3679
3680#include "src/core/SkEdgeClipper.h"
3681
3682struct SkHalfPlane {
3683    SkScalar fA, fB, fC;
3684
3685    SkScalar eval(SkScalar x, SkScalar y) const {
3686        return fA * x + fB * y + fC;
3687    }
3688    SkScalar operator()(SkScalar x, SkScalar y) const { return this->eval(x, y); }
3689
3690    bool normalize() {
3691        double a = fA;
3692        double b = fB;
3693        double c = fC;
3694        double dmag = sqrt(a * a + b * b);
3695        // length of initial plane normal is zero
3696        if (dmag == 0) {
3697           fA = fB = 0;
3698           fC = SK_Scalar1;
3699           return true;
3700        }
3701        double dscale = sk_ieee_double_divide(1.0, dmag);
3702        a *= dscale;
3703        b *= dscale;
3704        c *= dscale;
3705        // check if we're not finite, or normal is zero-length
3706        if (!sk_float_isfinite(a) || !sk_float_isfinite(b) || !sk_float_isfinite(c) ||
3707            (a == 0 && b == 0)) {
3708            fA = fB = 0;
3709            fC = SK_Scalar1;
3710            return false;
3711        }
3712        fA = a;
3713        fB = b;
3714        fC = c;
3715        return true;
3716    }
3717
3718    enum Result {
3719        kAllNegative,
3720        kAllPositive,
3721        kMixed
3722    };
3723    Result test(const SkRect& bounds) const {
3724        // check whether the diagonal aligned with the normal crosses the plane
3725        SkPoint diagMin, diagMax;
3726        if (fA >= 0) {
3727            diagMin.fX = bounds.fLeft;
3728            diagMax.fX = bounds.fRight;
3729        } else {
3730            diagMin.fX = bounds.fRight;
3731            diagMax.fX = bounds.fLeft;
3732        }
3733        if (fB >= 0) {
3734            diagMin.fY = bounds.fTop;
3735            diagMax.fY = bounds.fBottom;
3736        } else {
3737            diagMin.fY = bounds.fBottom;
3738            diagMax.fY = bounds.fTop;
3739        }
3740        SkScalar test = this->eval(diagMin.fX, diagMin.fY);
3741        SkScalar sign = test*this->eval(diagMax.fX, diagMax.fY);
3742        if (sign > 0) {
3743            // the path is either all on one side of the half-plane or the other
3744            if (test < 0) {
3745                return kAllNegative;
3746            } else {
3747                return kAllPositive;
3748            }
3749        }
3750        return kMixed;
3751    }
3752};
3753
3754// assumes plane is pre-normalized
3755// If we fail in our calculations, we return the empty path
3756static SkPath clip(const SkPath& path, const SkHalfPlane& plane) {
3757    SkMatrix mx, inv;
3758    SkPoint p0 = { -plane.fA*plane.fC, -plane.fB*plane.fC };
3759    mx.setAll( plane.fB, plane.fA, p0.fX,
3760              -plane.fA, plane.fB, p0.fY,
3761                      0,        0,     1);
3762    if (!mx.invert(&inv)) {
3763        return SkPath();
3764    }
3765
3766    SkPath rotated;
3767    path.transform(inv, &rotated);
3768    if (!rotated.isFinite()) {
3769        return SkPath();
3770    }
3771
3772    SkScalar big = SK_ScalarMax;
3773    SkRect clip = {-big, 0, big, big };
3774
3775    struct Rec {
3776        SkPathBuilder fResult;
3777        SkPoint       fPrev = {0,0};
3778    } rec;
3779
3780    SkEdgeClipper::ClipPath(rotated, clip, false,
3781                            [](SkEdgeClipper* clipper, bool newCtr, void* ctx) {
3782        Rec* rec = (Rec*)ctx;
3783
3784        bool addLineTo = false;
3785        SkPoint      pts[4];
3786        SkPath::Verb verb;
3787        while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
3788            if (newCtr) {
3789                rec->fResult.moveTo(pts[0]);
3790                rec->fPrev = pts[0];
3791                newCtr = false;
3792            }
3793
3794            if (addLineTo || pts[0] != rec->fPrev) {
3795                rec->fResult.lineTo(pts[0]);
3796            }
3797
3798            switch (verb) {
3799                case SkPath::kLine_Verb:
3800                    rec->fResult.lineTo(pts[1]);
3801                    rec->fPrev = pts[1];
3802                    break;
3803                case SkPath::kQuad_Verb:
3804                    rec->fResult.quadTo(pts[1], pts[2]);
3805                    rec->fPrev = pts[2];
3806                    break;
3807                case SkPath::kCubic_Verb:
3808                    rec->fResult.cubicTo(pts[1], pts[2], pts[3]);
3809                    rec->fPrev = pts[3];
3810                    break;
3811                default: break;
3812            }
3813            addLineTo = true;
3814        }
3815    }, &rec);
3816
3817    rec.fResult.setFillType(path.getFillType());
3818    SkPath result = rec.fResult.detach().makeTransform(mx);
3819    if (!result.isFinite()) {
3820        result = SkPath();
3821    }
3822    return result;
3823}
3824
3825// true means we have written to clippedPath
3826bool SkPathPriv::PerspectiveClip(const SkPath& path, const SkMatrix& matrix, SkPath* clippedPath) {
3827    if (!matrix.hasPerspective()) {
3828        return false;
3829    }
3830
3831    SkHalfPlane plane {
3832        matrix[SkMatrix::kMPersp0],
3833        matrix[SkMatrix::kMPersp1],
3834        matrix[SkMatrix::kMPersp2] - kW0PlaneDistance
3835    };
3836    if (plane.normalize()) {
3837        switch (plane.test(path.getBounds())) {
3838            case SkHalfPlane::kAllPositive:
3839                return false;
3840            case SkHalfPlane::kMixed: {
3841                *clippedPath = clip(path, plane);
3842                return true;
3843            } break;
3844            default: break; // handled outside of the switch
3845        }
3846    }
3847    // clipped out (or failed)
3848    *clippedPath = SkPath();
3849    return true;
3850}
3851
3852int SkPathPriv::GenIDChangeListenersCount(const SkPath& path) {
3853    return path.fPathRef->genIDChangeListenerCount();
3854}
3855
3856bool SkPathPriv::IsAxisAligned(const SkPath& path) {
3857    // Conservative (quick) test to see if all segments are axis-aligned.
3858    // Multiple contours might give a false-negative, but for speed, we ignore that
3859    // and just look at the raw points.
3860
3861    const SkPoint* pts = path.fPathRef->points();
3862    const int count = path.fPathRef->countPoints();
3863
3864    for (int i = 1; i < count; ++i) {
3865        if (pts[i-1].fX != pts[i].fX && pts[i-1].fY != pts[i].fY) {
3866            return false;
3867        }
3868    }
3869    return true;
3870}
3871