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