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