• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef SkPathPriv_DEFINED
9 #define SkPathPriv_DEFINED
10 
11 #include "include/core/SkPath.h"
12 
13 class SkPathPriv {
14 public:
15 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
16     static const int kPathRefGenIDBitCnt = 30; // leave room for the fill type (skbug.com/1762)
17 #else
18     static const int kPathRefGenIDBitCnt = 32;
19 #endif
20 
21     enum FirstDirection : int {
22         kCW_FirstDirection,         // == SkPath::kCW_Direction
23         kCCW_FirstDirection,        // == SkPath::kCCW_Direction
24         kUnknown_FirstDirection,
25     };
26 
AsFirstDirection(SkPath::Direction dir)27     static FirstDirection AsFirstDirection(SkPath::Direction dir) {
28         // since we agree numerically for the values in Direction, we can just cast.
29         return (FirstDirection)dir;
30     }
31 
32     /**
33      *  Return the opposite of the specified direction. kUnknown is its own
34      *  opposite.
35      */
OppositeFirstDirection(FirstDirection dir)36     static FirstDirection OppositeFirstDirection(FirstDirection dir) {
37         static const FirstDirection gOppositeDir[] = {
38             kCCW_FirstDirection, kCW_FirstDirection, kUnknown_FirstDirection,
39         };
40         return gOppositeDir[dir];
41     }
42 
43     /**
44      *  Tries to quickly compute the direction of the first non-degenerate
45      *  contour. If it can be computed, return true and set dir to that
46      *  direction. If it cannot be (quickly) determined, return false and ignore
47      *  the dir parameter. If the direction was determined, it is cached to make
48      *  subsequent calls return quickly.
49      */
50     static bool CheapComputeFirstDirection(const SkPath&, FirstDirection* dir);
51 
52     /**
53      *  Returns true if the path's direction can be computed via
54      *  cheapComputDirection() and if that computed direction matches the
55      *  specified direction. If dir is kUnknown, returns true if the direction
56      *  cannot be computed.
57      */
CheapIsFirstDirection(const SkPath & path,FirstDirection dir)58     static bool CheapIsFirstDirection(const SkPath& path, FirstDirection dir) {
59         FirstDirection computedDir = kUnknown_FirstDirection;
60         (void)CheapComputeFirstDirection(path, &computedDir);
61         return computedDir == dir;
62     }
63 
IsClosedSingleContour(const SkPath & path)64     static bool IsClosedSingleContour(const SkPath& path) {
65         int verbCount = path.countVerbs();
66         if (verbCount == 0)
67             return false;
68         int moveCount = 0;
69         auto verbs = path.fPathRef->verbs();
70         for (int i = 0; i < verbCount; i++) {
71             switch (verbs[~i]) { // verbs are stored backwards; we use [~i] to get the i'th verb
72                 case SkPath::Verb::kMove_Verb:
73                     moveCount += 1;
74                     if (moveCount > 1) {
75                         return false;
76                     }
77                     break;
78                 case SkPath::Verb::kClose_Verb:
79                     if (i == verbCount - 1) {
80                         return true;
81                     }
82                     return false;
83                 default: break;
84             }
85         }
86         return false;
87     }
88 
AddGenIDChangeListener(const SkPath & path,sk_sp<SkPathRef::GenIDChangeListener> listener)89     static void AddGenIDChangeListener(const SkPath& path,
90                                        sk_sp<SkPathRef::GenIDChangeListener> listener) {
91         path.fPathRef->addGenIDChangeListener(std::move(listener));
92     }
93 
94     /**
95      * This returns true for a rect that begins and ends at the same corner and has either a move
96      * followed by four lines or a move followed by 3 lines and a close. None of the parameters are
97      * optional. This does not permit degenerate line or point rectangles.
98      */
99     static bool IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPath::Direction* direction,
100                                    unsigned* start);
101 
102     /**
103      * Creates a path from arc params using the semantics of SkCanvas::drawArc. This function
104      * assumes empty ovals and zero sweeps have already been filtered out.
105      */
106     static void CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle,
107                                   SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect);
108 
109     /**
110      * Determines whether an arc produced by CreateDrawArcPath will be convex. Assumes a non-empty
111      * oval.
112      */
113     static bool DrawArcIsConvex(SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect);
114 
115     /**
116      * Returns a C++11-iterable object that traverses a path's verbs in order. e.g:
117      *
118      *   for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
119      *       ...
120      *   }
121      */
122     struct Verbs {
123     public:
VerbsVerbs124         Verbs(const SkPath& path) : fPathRef(path.fPathRef.get()) {}
125         struct Iter {
126             void operator++() { --fVerb; } // verbs are laid out backwards in memory.
127             bool operator!=(const Iter& b) { return fVerb != b.fVerb; }
128             SkPath::Verb operator*() { return static_cast<SkPath::Verb>(*fVerb); }
129             const uint8_t* fVerb;
130         };
beginVerbs131         Iter begin() { return Iter{fPathRef->verbs() - 1}; }
endVerbs132         Iter end() { return Iter{fPathRef->verbs() - fPathRef->countVerbs() - 1}; }
133     private:
134         Verbs(const Verbs&) = delete;
135         Verbs& operator=(const Verbs&) = delete;
136         SkPathRef* fPathRef;
137     };
138 
139     /**
140      * Returns a pointer to the verb data. Note that the verbs are stored backwards in memory and
141      * thus the returned pointer is the last verb.
142      */
VerbData(const SkPath & path)143     static const uint8_t* VerbData(const SkPath& path) {
144         return path.fPathRef->verbsMemBegin();
145     }
146 
147     /** Returns a raw pointer to the path points */
PointData(const SkPath & path)148     static const SkPoint* PointData(const SkPath& path) {
149         return path.fPathRef->points();
150     }
151 
152     /** Returns the number of conic weights in the path */
ConicWeightCnt(const SkPath & path)153     static int ConicWeightCnt(const SkPath& path) {
154         return path.fPathRef->countWeights();
155     }
156 
157     /** Returns a raw pointer to the path conic weights. */
ConicWeightData(const SkPath & path)158     static const SkScalar* ConicWeightData(const SkPath& path) {
159         return path.fPathRef->conicWeights();
160     }
161 
162 #ifndef SK_LEGACY_PATH_CONVEXITY
163     /** Returns true if path formed by pts is convex.
164 
165         @param pts    SkPoint array of path
166         @param count  number of entries in array
167 
168         @return       true if pts represent a convex geometry
169     */
170     static bool IsConvex(const SkPoint pts[], int count);
171 #endif
172 
173     /** Returns true if the underlying SkPathRef has one single owner. */
TestingOnly_unique(const SkPath & path)174     static bool TestingOnly_unique(const SkPath& path) {
175         return path.fPathRef->unique();
176     }
177 
178     /** Returns true if constructed by addCircle(), addOval(); and in some cases,
179      addRoundRect(), addRRect(). SkPath constructed with conicTo() or rConicTo() will not
180      return true though SkPath draws oval.
181 
182      rect receives bounds of oval.
183      dir receives SkPath::Direction of oval: kCW_Direction if clockwise, kCCW_Direction if
184      counterclockwise.
185      start receives start of oval: 0 for top, 1 for right, 2 for bottom, 3 for left.
186 
187      rect, dir, and start are unmodified if oval is not found.
188 
189      Triggers performance optimizations on some GPU surface implementations.
190 
191      @param rect   storage for bounding SkRect of oval; may be nullptr
192      @param dir    storage for SkPath::Direction; may be nullptr
193      @param start  storage for start of oval; may be nullptr
194      @return       true if SkPath was constructed by method that reduces to oval
195      */
IsOval(const SkPath & path,SkRect * rect,SkPath::Direction * dir,unsigned * start)196     static bool IsOval(const SkPath& path, SkRect* rect, SkPath::Direction* dir, unsigned* start) {
197         bool isCCW = false;
198         bool result = path.fPathRef->isOval(rect, &isCCW, start);
199         if (dir && result) {
200             *dir = isCCW ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
201         }
202         return result;
203     }
204 
205     /** Returns true if constructed by addRoundRect(), addRRect(); and if construction
206      is not empty, not SkRect, and not oval. SkPath constructed with other calls
207      will not return true though SkPath draws SkRRect.
208 
209      rrect receives bounds of SkRRect.
210      dir receives SkPath::Direction of oval: kCW_Direction if clockwise, kCCW_Direction if
211      counterclockwise.
212      start receives start of SkRRect: 0 for top, 1 for right, 2 for bottom, 3 for left.
213 
214      rrect, dir, and start are unmodified if SkRRect is not found.
215 
216      Triggers performance optimizations on some GPU surface implementations.
217 
218      @param rrect  storage for bounding SkRect of SkRRect; may be nullptr
219      @param dir    storage for SkPath::Direction; may be nullptr
220      @param start  storage for start of SkRRect; may be nullptr
221      @return       true if SkPath contains only SkRRect
222      */
IsRRect(const SkPath & path,SkRRect * rrect,SkPath::Direction * dir,unsigned * start)223     static bool IsRRect(const SkPath& path, SkRRect* rrect, SkPath::Direction* dir,
224                         unsigned* start) {
225         bool isCCW = false;
226         bool result = path.fPathRef->isRRect(rrect, &isCCW, start);
227         if (dir && result) {
228             *dir = isCCW ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
229         }
230         return result;
231     }
232 
233     /**
234      *  Sometimes in the drawing pipeline, we have to perform math on path coordinates, even after
235      *  the path is in device-coordinates. Tessellation and clipping are two examples. Usually this
236      *  is pretty modest, but it can involve subtracting/adding coordinates, or multiplying by
237      *  small constants (e.g. 2,3,4). To try to preflight issues where these optionations could turn
238      *  finite path values into infinities (or NaNs), we allow the upper drawing code to reject
239      *  the path if its bounds (in device coordinates) is too close to max float.
240      */
TooBigForMath(const SkRect & bounds)241     static bool TooBigForMath(const SkRect& bounds) {
242         // This value is just a guess. smaller is safer, but we don't want to reject largish paths
243         // that we don't have to.
244         constexpr SkScalar scale_down_to_allow_for_small_multiplies = 0.25f;
245         constexpr SkScalar max = SK_ScalarMax * scale_down_to_allow_for_small_multiplies;
246 
247         // use ! expression so we return true if bounds contains NaN
248         return !(bounds.fLeft >= -max && bounds.fTop >= -max &&
249                  bounds.fRight <= max && bounds.fBottom <= max);
250     }
TooBigForMath(const SkPath & path)251     static bool TooBigForMath(const SkPath& path) {
252         return TooBigForMath(path.getBounds());
253     }
254 
255     // Returns number of valid points for each SkPath::Iter verb
PtsInIter(unsigned verb)256     static int PtsInIter(unsigned verb) {
257         static const uint8_t gPtsInVerb[] = {
258             1,  // kMove    pts[0]
259             2,  // kLine    pts[0..1]
260             3,  // kQuad    pts[0..2]
261             3,  // kConic   pts[0..2]
262             4,  // kCubic   pts[0..3]
263             0,  // kClose
264             0   // kDone
265         };
266 
267         SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb));
268         return gPtsInVerb[verb];
269     }
270 
IsAxisAligned(const SkPath & path)271     static bool IsAxisAligned(const SkPath& path) {
272         SkRect tmp;
273         return (path.fPathRef->fIsRRect | path.fPathRef->fIsOval) || path.isRect(&tmp);
274     }
275 
AllPointsEq(const SkPoint pts[],int count)276     static bool AllPointsEq(const SkPoint pts[], int count) {
277         for (int i = 1; i < count; ++i) {
278             if (pts[0] != pts[i]) {
279                 return false;
280             }
281         }
282         return true;
283     }
284 };
285 
286 #endif
287