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