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