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