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