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