• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 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 SkPathRef_DEFINED
9 #define SkPathRef_DEFINED
10 
11 #include "include/core/SkPoint.h"
12 #include "include/core/SkRect.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkScalar.h"
15 #include "include/core/SkTypes.h"
16 #include "include/private/SkIDChangeListener.h"
17 #include "include/private/base/SkDebug.h"
18 #include "include/private/base/SkTArray.h"
19 #include "include/private/base/SkTo.h"
20 
21 #include <atomic>
22 #include <cstddef>
23 #include <cstdint>
24 #include <tuple>
25 #include <utility>
26 
27 class SkMatrix;
28 class SkRRect;
29 
30 // These are computed from a stream of verbs
31 struct SkPathVerbAnalysis {
32     bool     valid;
33     int      points, weights;
34     unsigned segmentMask;
35 };
36 SkPathVerbAnalysis sk_path_analyze_verbs(const uint8_t verbs[], int count);
37 
38 
39 /**
40  * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods
41  * modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an
42  * SkPathRef::Editor object. Installing the editor resets the generation ID. It also performs
43  * copy-on-write if the SkPathRef is shared by multiple SkPaths. The caller passes the Editor's
44  * constructor a pointer to a sk_sp<SkPathRef>, which may be updated to point to a new SkPathRef
45  * after the editor's constructor returns.
46  *
47  * The points and verbs are stored in a single allocation. The points are at the begining of the
48  * allocation while the verbs are stored at end of the allocation, in reverse order. Thus the points
49  * and verbs both grow into the middle of the allocation until the meet. To access verb i in the
50  * verb array use ref.verbs()[~i] (because verbs() returns a pointer just beyond the first
51  * logical verb or the last verb in memory).
52  */
53 
54 class SK_API SkPathRef final : public SkNVRefCnt<SkPathRef> {
55 public:
56     // See https://bugs.chromium.org/p/skia/issues/detail?id=13817 for how these sizes were
57     // determined.
58     using PointsArray = SkSTArray<4, SkPoint>;
59     using VerbsArray = SkSTArray<4, uint8_t>;
60     using ConicWeightsArray = SkSTArray<2, SkScalar>;
61 
SkPathRef(PointsArray points,VerbsArray verbs,ConicWeightsArray weights,unsigned segmentMask)62     SkPathRef(PointsArray points, VerbsArray verbs, ConicWeightsArray weights,
63               unsigned segmentMask)
64         : fPoints(std::move(points))
65         , fVerbs(std::move(verbs))
66         , fConicWeights(std::move(weights))
67     {
68         fBoundsIsDirty = true;    // this also invalidates fIsFinite
69         fGenerationID = 0;        // recompute
70         fSegmentMask = segmentMask;
71         fIsOval = false;
72         fIsRRect = false;
73         // The next two values don't matter unless fIsOval or fIsRRect are true.
74         fRRectOrOvalIsCCW = false;
75         fRRectOrOvalStartIdx = 0xAC;
76         SkDEBUGCODE(fEditorsAttached.store(0);)
77 
78         this->computeBounds();  // do this now, before we worry about multiple owners/threads
79         SkDEBUGCODE(this->validate();)
80     }
81 
82     class Editor {
83     public:
84         Editor(sk_sp<SkPathRef>* pathRef,
85                int incReserveVerbs = 0,
86                int incReservePoints = 0);
87 
~Editor()88         ~Editor() { SkDEBUGCODE(fPathRef->fEditorsAttached--;) }
89 
90         /**
91          * Returns the array of points.
92          */
writablePoints()93         SkPoint* writablePoints() { return fPathRef->getWritablePoints(); }
points()94         const SkPoint* points() const { return fPathRef->points(); }
95 
96         /**
97          * Gets the ith point. Shortcut for this->points() + i
98          */
atPoint(int i)99         SkPoint* atPoint(int i) { return fPathRef->getWritablePoints() + i; }
atPoint(int i)100         const SkPoint* atPoint(int i) const { return &fPathRef->fPoints[i]; }
101 
102         /**
103          * Adds the verb and allocates space for the number of points indicated by the verb. The
104          * return value is a pointer to where the points for the verb should be written.
105          * 'weight' is only used if 'verb' is kConic_Verb
106          */
107         SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight = 0) {
108             SkDEBUGCODE(fPathRef->validate();)
109             return fPathRef->growForVerb(verb, weight);
110         }
111 
112         /**
113          * Allocates space for multiple instances of a particular verb and the
114          * requisite points & weights.
115          * The return pointer points at the first new point (indexed normally [<i>]).
116          * If 'verb' is kConic_Verb, 'weights' will return a pointer to the
117          * space for the conic weights (indexed normally).
118          */
119         SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb,
120                                      int numVbs,
121                                      SkScalar** weights = nullptr) {
122             return fPathRef->growForRepeatedVerb(verb, numVbs, weights);
123         }
124 
125         /**
126          * Concatenates all verbs from 'path' onto the pathRef's verbs array. Increases the point
127          * count by the number of points in 'path', and the conic weight count by the number of
128          * conics in 'path'.
129          *
130          * Returns pointers to the uninitialized points and conic weights data.
131          */
growForVerbsInPath(const SkPathRef & path)132         std::tuple<SkPoint*, SkScalar*> growForVerbsInPath(const SkPathRef& path) {
133             return fPathRef->growForVerbsInPath(path);
134         }
135 
136         /**
137          * Resets the path ref to a new verb and point count. The new verbs and points are
138          * uninitialized.
139          */
resetToSize(int newVerbCnt,int newPointCnt,int newConicCount)140         void resetToSize(int newVerbCnt, int newPointCnt, int newConicCount) {
141             fPathRef->resetToSize(newVerbCnt, newPointCnt, newConicCount);
142         }
143 
144         /**
145          * Gets the path ref that is wrapped in the Editor.
146          */
pathRef()147         SkPathRef* pathRef() { return fPathRef; }
148 
setIsOval(bool isOval,bool isCCW,unsigned start)149         void setIsOval(bool isOval, bool isCCW, unsigned start) {
150             fPathRef->setIsOval(isOval, isCCW, start);
151         }
152 
setIsRRect(bool isRRect,bool isCCW,unsigned start)153         void setIsRRect(bool isRRect, bool isCCW, unsigned start) {
154             fPathRef->setIsRRect(isRRect, isCCW, start);
155         }
156 
setBounds(const SkRect & rect)157         void setBounds(const SkRect& rect) { fPathRef->setBounds(rect); }
158 
159     private:
160         SkPathRef* fPathRef;
161     };
162 
163     class SK_API Iter {
164     public:
165         Iter();
166         Iter(const SkPathRef&);
167 
168         void setPathRef(const SkPathRef&);
169 
170         /** Return the next verb in this iteration of the path. When all
171             segments have been visited, return kDone_Verb.
172 
173             If any point in the path is non-finite, return kDone_Verb immediately.
174 
175             @param  pts The points representing the current verb and/or segment
176                         This must not be NULL.
177             @return The verb for the current segment
178         */
179         uint8_t next(SkPoint pts[4]);
180         uint8_t peek() const;
181 
conicWeight()182         SkScalar conicWeight() const { return *fConicWeights; }
183 
184     private:
185         const SkPoint*  fPts;
186         const uint8_t*  fVerbs;
187         const uint8_t*  fVerbStop;
188         const SkScalar* fConicWeights;
189     };
190 
191 public:
192     /**
193      * Gets a path ref with no verbs or points.
194      */
195     static SkPathRef* CreateEmpty();
196 
197     /**
198      *  Returns true if all of the points in this path are finite, meaning there
199      *  are no infinities and no NaNs.
200      */
isFinite()201     bool isFinite() const {
202         if (fBoundsIsDirty) {
203             this->computeBounds();
204         }
205         return SkToBool(fIsFinite);
206     }
207 
208     /**
209      *  Returns a mask, where each bit corresponding to a SegmentMask is
210      *  set if the path contains 1 or more segments of that type.
211      *  Returns 0 for an empty path (no segments).
212      */
getSegmentMasks()213     uint32_t getSegmentMasks() const { return fSegmentMask; }
214 
215     /** Returns true if the path is an oval.
216      *
217      * @param rect      returns the bounding rect of this oval. It's a circle
218      *                  if the height and width are the same.
219      * @param isCCW     is the oval CCW (or CW if false).
220      * @param start     indicates where the contour starts on the oval (see
221      *                  SkPath::addOval for intepretation of the index).
222      *
223      * @return true if this path is an oval.
224      *              Tracking whether a path is an oval is considered an
225      *              optimization for performance and so some paths that are in
226      *              fact ovals can report false.
227      */
isOval(SkRect * rect,bool * isCCW,unsigned * start)228     bool isOval(SkRect* rect, bool* isCCW, unsigned* start) const {
229         if (fIsOval) {
230             if (rect) {
231                 *rect = this->getBounds();
232             }
233             if (isCCW) {
234                 *isCCW = SkToBool(fRRectOrOvalIsCCW);
235             }
236             if (start) {
237                 *start = fRRectOrOvalStartIdx;
238             }
239         }
240 
241         return SkToBool(fIsOval);
242     }
243 
244     bool isRRect(SkRRect* rrect, bool* isCCW, unsigned* start) const;
245 
hasComputedBounds()246     bool hasComputedBounds() const {
247         return !fBoundsIsDirty;
248     }
249 
250     /** Returns the bounds of the path's points. If the path contains 0 or 1
251         points, the bounds is set to (0,0,0,0), and isEmpty() will return true.
252         Note: this bounds may be larger than the actual shape, since curves
253         do not extend as far as their control points.
254     */
getBounds()255     const SkRect& getBounds() const {
256         if (fBoundsIsDirty) {
257             this->computeBounds();
258         }
259         return fBounds;
260     }
261 
262     SkRRect getRRect() const;
263 
264     /**
265      * Transforms a path ref by a matrix, allocating a new one only if necessary.
266      */
267     static void CreateTransformedCopy(sk_sp<SkPathRef>* dst,
268                                       const SkPathRef& src,
269                                       const SkMatrix& matrix);
270 
271   //  static SkPathRef* CreateFromBuffer(SkRBuffer* buffer);
272 
273     /**
274      * Rollsback a path ref to zero verbs and points with the assumption that the path ref will be
275      * repopulated with approximately the same number of verbs and points. A new path ref is created
276      * only if necessary.
277      */
278     static void Rewind(sk_sp<SkPathRef>* pathRef);
279 
280     ~SkPathRef();
countPoints()281     int countPoints() const { return fPoints.size(); }
countVerbs()282     int countVerbs() const { return fVerbs.size(); }
countWeights()283     int countWeights() const { return fConicWeights.size(); }
284 
285     size_t approximateBytesUsed() const;
286 
287     /**
288      * Returns a pointer one beyond the first logical verb (last verb in memory order).
289      */
verbsBegin()290     const uint8_t* verbsBegin() const { return fVerbs.begin(); }
291 
292     /**
293      * Returns a const pointer to the first verb in memory (which is the last logical verb).
294      */
verbsEnd()295     const uint8_t* verbsEnd() const { return fVerbs.end(); }
296 
297     /**
298      * Returns a const pointer to the first point.
299      */
points()300     const SkPoint* points() const { return fPoints.begin(); }
301 
302     /**
303      * Shortcut for this->points() + this->countPoints()
304      */
pointsEnd()305     const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); }
306 
conicWeights()307     const SkScalar* conicWeights() const { return fConicWeights.begin(); }
conicWeightsEnd()308     const SkScalar* conicWeightsEnd() const { return fConicWeights.end(); }
309 
310     /**
311      * Convenience methods for getting to a verb or point by index.
312      */
atVerb(int index)313     uint8_t atVerb(int index) const { return fVerbs[index]; }
atPoint(int index)314     const SkPoint& atPoint(int index) const { return fPoints[index]; }
315 
316     bool operator== (const SkPathRef& ref) const;
317 
318     void interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const;
319 
320     /**
321      * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the
322      * same ID then they have the same verbs and points. However, two path refs may have the same
323      * contents but different genIDs.
324      * skbug.com/1762 for background on why fillType is necessary (for now).
325      */
326     uint32_t genID(uint8_t fillType) const;
327 
328     void addGenIDChangeListener(sk_sp<SkIDChangeListener>);   // Threadsafe.
329     int genIDChangeListenerCount();                           // Threadsafe
330 
331     bool dataMatchesVerbs() const;
332     bool isValid() const;
333     SkDEBUGCODE(void validate() const { SkASSERT(this->isValid()); } )
334 
335     /**
336      * Resets this SkPathRef to a clean state.
337      */
338     void reset();
339 
isInitialEmptyPathRef()340     bool isInitialEmptyPathRef() const {
341         return fGenerationID == kEmptyGenID;
342     }
343 
344 private:
345     enum SerializationOffsets {
346         kLegacyRRectOrOvalStartIdx_SerializationShift = 28, // requires 3 bits, ignored.
347         kLegacyRRectOrOvalIsCCW_SerializationShift = 27,    // requires 1 bit, ignored.
348         kLegacyIsRRect_SerializationShift = 26,             // requires 1 bit, ignored.
349         kIsFinite_SerializationShift = 25,                  // requires 1 bit
350         kLegacyIsOval_SerializationShift = 24,              // requires 1 bit, ignored.
351         kSegmentMask_SerializationShift = 0                 // requires 4 bits (deprecated)
352     };
353 
354     SkPathRef(int numVerbs = 0, int numPoints = 0) {
355         fBoundsIsDirty = true;    // this also invalidates fIsFinite
356         fGenerationID = kEmptyGenID;
357         fSegmentMask = 0;
358         fIsOval = false;
359         fIsRRect = false;
360         // The next two values don't matter unless fIsOval or fIsRRect are true.
361         fRRectOrOvalIsCCW = false;
362         fRRectOrOvalStartIdx = 0xAC;
363         if (numPoints > 0)
364             fPoints.reserve_back(numPoints);
365         if (numVerbs > 0)
366             fVerbs.reserve_back(numVerbs);
367         SkDEBUGCODE(fEditorsAttached.store(0);)
368         SkDEBUGCODE(this->validate();)
369     }
370 
371     void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints);
372 
373     // Return true if the computed bounds are finite.
ComputePtBounds(SkRect * bounds,const SkPathRef & ref)374     static bool ComputePtBounds(SkRect* bounds, const SkPathRef& ref) {
375         return bounds->setBoundsCheck(ref.points(), ref.countPoints());
376     }
377 
378     // called, if dirty, by getBounds()
computeBounds()379     void computeBounds() const {
380         SkDEBUGCODE(this->validate();)
381         // TODO(mtklein): remove fBoundsIsDirty and fIsFinite,
382         // using an inverted rect instead of fBoundsIsDirty and always recalculating fIsFinite.
383         SkASSERT(fBoundsIsDirty);
384 
385         fIsFinite = ComputePtBounds(&fBounds, *this);
386         fBoundsIsDirty = false;
387     }
388 
setBounds(const SkRect & rect)389     void setBounds(const SkRect& rect) {
390         SkASSERT(rect.fLeft <= rect.fRight && rect.fTop <= rect.fBottom);
391         fBounds = rect;
392         fBoundsIsDirty = false;
393         fIsFinite = fBounds.isFinite();
394     }
395 
396     /** Makes additional room but does not change the counts or change the genID */
incReserve(int additionalVerbs,int additionalPoints)397     void incReserve(int additionalVerbs, int additionalPoints) {
398         SkDEBUGCODE(this->validate();)
399         // Use reserve() so that if there is not enough space, the array will grow with some
400         // additional space. This ensures repeated calls to grow won't always allocate.
401         if (additionalPoints > 0)
402             fPoints.reserve(fPoints.size() + additionalPoints);
403         if (additionalVerbs > 0)
404             fVerbs.reserve(fVerbs.size() + additionalVerbs);
405         SkDEBUGCODE(this->validate();)
406     }
407 
408     /**
409      * Resets all state except that of the verbs, points, and conic-weights.
410      * Intended to be called from other functions that reset state.
411      */
commonReset()412     void commonReset() {
413         SkDEBUGCODE(this->validate();)
414         this->callGenIDChangeListeners();
415         fBoundsIsDirty = true;      // this also invalidates fIsFinite
416         fGenerationID = 0;
417 
418         fSegmentMask = 0;
419         fIsOval = false;
420         fIsRRect = false;
421     }
422 
423     /** Resets the path ref with verbCount verbs and pointCount points, all uninitialized. Also
424      *  allocates space for reserveVerb additional verbs and reservePoints additional points.*/
425     void resetToSize(int verbCount, int pointCount, int conicCount,
426                      int reserveVerbs = 0, int reservePoints = 0) {
427         commonReset();
428         // Use reserve_back() so the arrays are sized to exactly fit the data.
429         const int pointDelta = pointCount + reservePoints - fPoints.size();
430         if (pointDelta > 0) {
431             fPoints.reserve_back(pointDelta);
432         }
433         fPoints.resize_back(pointCount);
434         const int verbDelta = verbCount + reserveVerbs - fVerbs.size();
435         if (verbDelta > 0) {
436             fVerbs.reserve_back(verbDelta);
437         }
438         fVerbs.resize_back(verbCount);
439         fConicWeights.resize_back(conicCount);
440         SkDEBUGCODE(this->validate();)
441     }
442 
443     /**
444      * Increases the verb count by numVbs and point count by the required amount.
445      * The new points are uninitialized. All the new verbs are set to the specified
446      * verb. If 'verb' is kConic_Verb, 'weights' will return a pointer to the
447      * uninitialized conic weights.
448      */
449     SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, int numVbs, SkScalar** weights);
450 
451     /**
452      * Increases the verb count 1, records the new verb, and creates room for the requisite number
453      * of additional points. A pointer to the first point is returned. Any new points are
454      * uninitialized.
455      */
456     SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight);
457 
458     /**
459      * Concatenates all verbs from 'path' onto our own verbs array. Increases the point count by the
460      * number of points in 'path', and the conic weight count by the number of conics in 'path'.
461      *
462      * Returns pointers to the uninitialized points and conic weights data.
463      */
464     std::tuple<SkPoint*, SkScalar*> growForVerbsInPath(const SkPathRef& path);
465 
466     /**
467      * Private, non-const-ptr version of the public function verbsMemBegin().
468      */
verbsBeginWritable()469     uint8_t* verbsBeginWritable() { return fVerbs.begin(); }
470 
471     /**
472      * Called the first time someone calls CreateEmpty to actually create the singleton.
473      */
474     friend SkPathRef* sk_create_empty_pathref();
475 
setIsOval(bool isOval,bool isCCW,unsigned start)476     void setIsOval(bool isOval, bool isCCW, unsigned start) {
477         fIsOval = isOval;
478         fRRectOrOvalIsCCW = isCCW;
479         fRRectOrOvalStartIdx = SkToU8(start);
480     }
481 
setIsRRect(bool isRRect,bool isCCW,unsigned start)482     void setIsRRect(bool isRRect, bool isCCW, unsigned start) {
483         fIsRRect = isRRect;
484         fRRectOrOvalIsCCW = isCCW;
485         fRRectOrOvalStartIdx = SkToU8(start);
486     }
487 
488     // called only by the editor. Note that this is not a const function.
getWritablePoints()489     SkPoint* getWritablePoints() {
490         SkDEBUGCODE(this->validate();)
491         fIsOval = false;
492         fIsRRect = false;
493         return fPoints.begin();
494     }
495 
getPoints()496     const SkPoint* getPoints() const {
497         SkDEBUGCODE(this->validate();)
498         return fPoints.begin();
499     }
500 
501     void callGenIDChangeListeners();
502 
503     enum {
504         kMinSize = 256,
505     };
506 
507     mutable SkRect   fBounds;
508 
509     PointsArray fPoints;
510     VerbsArray fVerbs;
511     ConicWeightsArray fConicWeights;
512 
513     enum {
514         kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs.
515     };
516     mutable uint32_t    fGenerationID;
517     SkDEBUGCODE(std::atomic<int> fEditorsAttached;) // assert only one editor in use at any time.
518 
519     SkIDChangeListener::List fGenIDChangeListeners;
520 
521     mutable uint8_t  fBoundsIsDirty;
522     mutable bool     fIsFinite;    // only meaningful if bounds are valid
523 
524     bool     fIsOval;
525     bool     fIsRRect;
526     // Both the circle and rrect special cases have a notion of direction and starting point
527     // The next two variables store that information for either.
528     bool     fRRectOrOvalIsCCW;
529     uint8_t  fRRectOrOvalStartIdx;
530     uint8_t  fSegmentMask;
531 
532     friend class PathRefTest_Private;
533     friend class ForceIsRRect_Private; // unit test isRRect
534     friend class SkPath;
535     friend class SkPathBuilder;
536     friend class SkPathPriv;
537 };
538 
539 #endif
540