• 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/SkArc.h"
12 #include "include/core/SkPath.h"
13 #include "include/core/SkPathBuilder.h"
14 #include "include/core/SkPathTypes.h"
15 #include "include/core/SkPoint.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkScalar.h"
19 #include "include/core/SkTypes.h"
20 #include "include/private/SkIDChangeListener.h"
21 #include "include/private/SkPathRef.h"
22 #include "include/private/base/SkDebug.h"
23 #include "src/core/SkPathEnums.h"
24 
25 #include <cstdint>
26 #include <iterator>
27 #include <utility>
28 
29 class SkMatrix;
30 class SkRRect;
31 
32 static_assert(0 == static_cast<int>(SkPathFillType::kWinding), "fill_type_mismatch");
33 static_assert(1 == static_cast<int>(SkPathFillType::kEvenOdd), "fill_type_mismatch");
34 static_assert(2 == static_cast<int>(SkPathFillType::kInverseWinding), "fill_type_mismatch");
35 static_assert(3 == static_cast<int>(SkPathFillType::kInverseEvenOdd), "fill_type_mismatch");
36 
37 class SkPathPriv {
38 public:
39     // skbug.com/9906: Not a perfect solution for W plane clipping, but 1/16384 is a
40     // reasonable limit (roughly 5e-5)
41     inline static constexpr SkScalar kW0PlaneDistance = 1.f / (1 << 14);
42 
AsFirstDirection(SkPathDirection dir)43     static SkPathFirstDirection AsFirstDirection(SkPathDirection dir) {
44         // since we agree numerically for the values in Direction, we can just cast.
45         return (SkPathFirstDirection)dir;
46     }
47 
48     /**
49      *  Return the opposite of the specified direction. kUnknown is its own
50      *  opposite.
51      */
OppositeFirstDirection(SkPathFirstDirection dir)52     static SkPathFirstDirection OppositeFirstDirection(SkPathFirstDirection dir) {
53         static const SkPathFirstDirection gOppositeDir[] = {
54             SkPathFirstDirection::kCCW, SkPathFirstDirection::kCW, SkPathFirstDirection::kUnknown,
55         };
56         return gOppositeDir[(unsigned)dir];
57     }
58 
59     /**
60      *  Tries to compute the direction of the outer-most non-degenerate
61      *  contour. If it can be computed, return that direction. If it cannot be determined,
62      *  or the contour is known to be convex, return kUnknown. If the direction was determined,
63      *  it is cached to make subsequent calls return quickly.
64      */
65     static SkPathFirstDirection ComputeFirstDirection(const SkPath&);
66 
IsClosedSingleContour(const SkPath & path)67     static bool IsClosedSingleContour(const SkPath& path) {
68         int verbCount = path.countVerbs();
69         if (verbCount == 0)
70             return false;
71         int moveCount = 0;
72         auto verbs = path.fPathRef->verbsBegin();
73         for (int i = 0; i < verbCount; i++) {
74             switch (verbs[i]) {
75                 case SkPath::Verb::kMove_Verb:
76                     moveCount += 1;
77                     if (moveCount > 1) {
78                         return false;
79                     }
80                     break;
81                 case SkPath::Verb::kClose_Verb:
82                     if (i == verbCount - 1) {
83                         return true;
84                     }
85                     return false;
86                 default: break;
87             }
88         }
89         return false;
90     }
91 
92     // In some scenarios (e.g. fill or convexity checking all but the last leading move to are
93     // irrelevant to behavior). SkPath::injectMoveToIfNeeded should ensure that this is always at
94     // least 1.
LeadingMoveToCount(const SkPath & path)95     static int LeadingMoveToCount(const SkPath& path) {
96         int verbCount = path.countVerbs();
97         auto verbs = path.fPathRef->verbsBegin();
98         for (int i = 0; i < verbCount; i++) {
99             if (verbs[i] != SkPath::Verb::kMove_Verb) {
100                 return i;
101             }
102         }
103         return verbCount; // path is all move verbs
104     }
105 
AddGenIDChangeListener(const SkPath & path,sk_sp<SkIDChangeListener> listener)106     static void AddGenIDChangeListener(const SkPath& path, sk_sp<SkIDChangeListener> listener) {
107         path.fPathRef->addGenIDChangeListener(std::move(listener));
108     }
109 
110     /**
111      * This returns true for a rect that has a move followed by 3 or 4 lines and a close. If
112      * 'isSimpleFill' is true, an uncloseed rect will also be accepted as long as it starts and
113      * ends at the same corner. This does not permit degenerate line or point rectangles.
114      */
115     static bool IsSimpleRect(const SkPath& path, bool isSimpleFill, SkRect* rect,
116                              SkPathDirection* direction, unsigned* start);
117 
118     /**
119      * Creates a path from arc params using the semantics of SkCanvas::drawArc. This function
120      * assumes empty ovals and zero sweeps have already been filtered out.
121      */
122     static void CreateDrawArcPath(SkPath* path, const SkArc& arc, 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, SkArc::Type arcType, 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 
MakePath(const SkPathVerbAnalysis & analysis,const SkPoint points[],const uint8_t verbs[],int verbCount,const SkScalar conics[],SkPathFillType fillType,bool isVolatile)425     static SkPath MakePath(const SkPathVerbAnalysis& analysis,
426                            const SkPoint points[],
427                            const uint8_t verbs[],
428                            int verbCount,
429                            const SkScalar conics[],
430                            SkPathFillType fillType,
431                            bool isVolatile) {
432         return SkPath::MakeInternal(analysis, points, verbs, verbCount, conics, fillType,
433                                     isVolatile);
434     }
435 };
436 
437 // Lightweight variant of SkPath::Iter that only returns segments (e.g. lines/conics).
438 // Does not return kMove or kClose.
439 // Always "auto-closes" each contour.
440 // Roughly the same as SkPath::Iter(path, true), but does not return moves or closes
441 //
442 class SkPathEdgeIter {
443     const uint8_t*  fVerbs;
444     const uint8_t*  fVerbsStop;
445     const SkPoint*  fPts;
446     const SkPoint*  fMoveToPtr;
447     const SkScalar* fConicWeights;
448     SkPoint         fScratch[2];    // for auto-close lines
449     bool            fNeedsCloseLine;
450     bool            fNextIsNewContour;
451     SkDEBUGCODE(bool fIsConic;)
452 
453     enum {
454         kIllegalEdgeValue = 99
455     };
456 
457 public:
458     SkPathEdgeIter(const SkPath& path);
459 
conicWeight()460     SkScalar conicWeight() const {
461         SkASSERT(fIsConic);
462         return *fConicWeights;
463     }
464 
465     enum class Edge {
466         kLine  = SkPath::kLine_Verb,
467         kQuad  = SkPath::kQuad_Verb,
468         kConic = SkPath::kConic_Verb,
469         kCubic = SkPath::kCubic_Verb,
470     };
471 
EdgeToVerb(Edge e)472     static SkPath::Verb EdgeToVerb(Edge e) {
473         return SkPath::Verb(e);
474     }
475 
476     struct Result {
477         const SkPoint*  fPts;   // points for the segment, or null if done
478         Edge            fEdge;
479         bool            fIsNewContour;
480 
481         // Returns true when it holds an Edge, false when the path is done.
482         explicit operator bool() { return fPts != nullptr; }
483     };
484 
next()485     Result next() {
486         auto closeline = [&]() {
487             fScratch[0] = fPts[-1];
488             fScratch[1] = *fMoveToPtr;
489             fNeedsCloseLine = false;
490             fNextIsNewContour = true;
491             return Result{ fScratch, Edge::kLine, false };
492         };
493 
494         for (;;) {
495             SkASSERT(fVerbs <= fVerbsStop);
496             if (fVerbs == fVerbsStop) {
497                 return fNeedsCloseLine
498                     ? closeline()
499                     : Result{ nullptr, Edge(kIllegalEdgeValue), false };
500             }
501 
502             SkDEBUGCODE(fIsConic = false;)
503 
504             const auto v = *fVerbs++;
505             switch (v) {
506                 case SkPath::kMove_Verb: {
507                     if (fNeedsCloseLine) {
508                         auto res = closeline();
509                         fMoveToPtr = fPts++;
510                         return res;
511                     }
512                     fMoveToPtr = fPts++;
513                     fNextIsNewContour = true;
514                 } break;
515                 case SkPath::kClose_Verb:
516                     if (fNeedsCloseLine) return closeline();
517                     break;
518                 default: {
519                     // Actual edge.
520                     const int pts_count = (v+2) / 2,
521                               cws_count = (v & (v-1)) / 2;
522                     SkASSERT(pts_count == SkPathPriv::PtsInIter(v) - 1);
523 
524                     fNeedsCloseLine = true;
525                     fPts           += pts_count;
526                     fConicWeights  += cws_count;
527 
528                     SkDEBUGCODE(fIsConic = (v == SkPath::kConic_Verb);)
529                     SkASSERT(fIsConic == (cws_count > 0));
530 
531                     bool isNewContour = fNextIsNewContour;
532                     fNextIsNewContour = false;
533                     return { &fPts[-(pts_count + 1)], Edge(v), isNewContour };
534                 }
535             }
536         }
537     }
538 };
539 
540 #endif
541