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