|
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 "SkMatrix.h" |
|
13 #include "SkPoint.h" |
|
14 #include "SkRect.h" |
|
15 #include "SkRefCnt.h" |
|
16 #include "SkTDArray.h" |
|
17 #include <stddef.h> // ptrdiff_t |
|
18 |
|
19 class SkRBuffer; |
|
20 class SkWBuffer; |
|
21 |
|
22 /** |
|
23 * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods |
|
24 * modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an |
|
25 * SkPathRef::Editor object. Installing the editor resets the generation ID. It also performs |
|
26 * copy-on-write if the SkPathRef is shared by multiple SkPaths. The caller passes the Editor's |
|
27 * constructor a SkAutoTUnref, which may be updated to point to a new SkPathRef after the editor's |
|
28 * constructor returns. |
|
29 * |
|
30 * The points and verbs are stored in a single allocation. The points are at the begining of the |
|
31 * allocation while the verbs are stored at end of the allocation, in reverse order. Thus the points |
|
32 * and verbs both grow into the middle of the allocation until the meet. To access verb i in the |
|
33 * verb array use ref.verbs()[~i] (because verbs() returns a pointer just beyond the first |
|
34 * logical verb or the last verb in memory). |
|
35 */ |
|
36 |
|
37 class SK_API SkPathRef : public ::SkRefCnt { |
|
38 public: |
|
39 SK_DECLARE_INST_COUNT(SkPathRef); |
|
40 |
|
41 class Editor { |
|
42 public: |
|
43 Editor(SkAutoTUnref<SkPathRef>* pathRef, |
|
44 int incReserveVerbs = 0, |
|
45 int incReservePoints = 0); |
|
46 |
|
47 ~Editor() { SkDEBUGCODE(sk_atomic_dec(&fPathRef->fEditorsAttached);) } |
|
48 |
|
49 /** |
|
50 * Returns the array of points. |
|
51 */ |
|
52 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 SkPoint* atPoint(int i) { |
|
59 SkASSERT((unsigned) i < (unsigned) fPathRef->fPointCnt); |
|
60 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 SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight = 0) { |
|
73 SkDEBUGCODE(fPathRef->validate();) |
|
74 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 SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, |
|
85 int numVbs, |
|
86 SkScalar** weights = NULL) { |
|
87 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 SkPathRef* pathRef() { return fPathRef; } |
|
102 |
|
103 void setIsOval(bool isOval) { fPathRef->setIsOval(isOval); } |
|
104 |
|
105 void setBounds(const SkRect& rect) { fPathRef->setBounds(rect); } |
|
106 |
|
107 private: |
|
108 SkPathRef* fPathRef; |
|
109 }; |
|
110 |
|
111 public: |
|
112 /** |
|
113 * Gets a path ref with no verbs or points. |
|
114 */ |
|
115 static SkPathRef* CreateEmpty(); |
|
116 |
|
117 /** |
|
118 * Returns true if all of the points in this path are finite, meaning there |
|
119 * are no infinities and no NaNs. |
|
120 */ |
|
121 bool isFinite() const { |
|
122 if (fBoundsIsDirty) { |
|
123 this->computeBounds(); |
|
124 } |
|
125 return SkToBool(fIsFinite); |
|
126 } |
|
127 |
|
128 /** |
|
129 * Returns a mask, where each bit corresponding to a SegmentMask is |
|
130 * set if the path contains 1 or more segments of that type. |
|
131 * Returns 0 for an empty path (no segments). |
|
132 */ |
|
133 uint32_t getSegmentMasks() const { return fSegmentMask; } |
|
134 |
|
135 /** Returns true if the path is an oval. |
|
136 * |
|
137 * @param rect returns the bounding rect of this oval. It's a circle |
|
138 * if the height and width are the same. |
|
139 * |
|
140 * @return true if this path is an oval. |
|
141 * Tracking whether a path is an oval is considered an |
|
142 * optimization for performance and so some paths that are in |
|
143 * fact ovals can report false. |
|
144 */ |
|
145 bool isOval(SkRect* rect) const { |
|
146 if (fIsOval && NULL != rect) { |
|
147 *rect = getBounds(); |
|
148 } |
|
149 |
|
150 return SkToBool(fIsOval); |
|
151 } |
|
152 |
|
153 bool hasComputedBounds() const { |
|
154 return !fBoundsIsDirty; |
|
155 } |
|
156 |
|
157 /** Returns the bounds of the path's points. If the path contains 0 or 1 |
|
158 points, the bounds is set to (0,0,0,0), and isEmpty() will return true. |
|
159 Note: this bounds may be larger than the actual shape, since curves |
|
160 do not extend as far as their control points. |
|
161 */ |
|
162 const SkRect& getBounds() const { |
|
163 if (fBoundsIsDirty) { |
|
164 this->computeBounds(); |
|
165 } |
|
166 return fBounds; |
|
167 } |
|
168 |
|
169 /** |
|
170 * Transforms a path ref by a matrix, allocating a new one only if necessary. |
|
171 */ |
|
172 static void CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst, |
|
173 const SkPathRef& src, |
|
174 const SkMatrix& matrix); |
|
175 |
|
176 static SkPathRef* CreateFromBuffer(SkRBuffer* buffer); |
|
177 |
|
178 /** |
|
179 * Rollsback a path ref to zero verbs and points with the assumption that the path ref will be |
|
180 * repopulated with approximately the same number of verbs and points. A new path ref is created |
|
181 * only if necessary. |
|
182 */ |
|
183 static void Rewind(SkAutoTUnref<SkPathRef>* pathRef); |
|
184 |
|
185 virtual ~SkPathRef() { |
|
186 SkDEBUGCODE(this->validate();) |
|
187 sk_free(fPoints); |
|
188 |
|
189 SkDEBUGCODE(fPoints = NULL;) |
|
190 SkDEBUGCODE(fVerbs = NULL;) |
|
191 SkDEBUGCODE(fVerbCnt = 0x9999999;) |
|
192 SkDEBUGCODE(fPointCnt = 0xAAAAAAA;) |
|
193 SkDEBUGCODE(fPointCnt = 0xBBBBBBB;) |
|
194 SkDEBUGCODE(fGenerationID = 0xEEEEEEEE;) |
|
195 SkDEBUGCODE(fEditorsAttached = 0x7777777;) |
|
196 } |
|
197 |
|
198 int countPoints() const { SkDEBUGCODE(this->validate();) return fPointCnt; } |
|
199 int countVerbs() const { SkDEBUGCODE(this->validate();) return fVerbCnt; } |
|
200 int countWeights() const { SkDEBUGCODE(this->validate();) return fConicWeights.count(); } |
|
201 |
|
202 /** |
|
203 * Returns a pointer one beyond the first logical verb (last verb in memory order). |
|
204 */ |
|
205 const uint8_t* verbs() const { SkDEBUGCODE(this->validate();) return fVerbs; } |
|
206 |
|
207 /** |
|
208 * Returns a const pointer to the first verb in memory (which is the last logical verb). |
|
209 */ |
|
210 const uint8_t* verbsMemBegin() const { return this->verbs() - fVerbCnt; } |
|
211 |
|
212 /** |
|
213 * Returns a const pointer to the first point. |
|
214 */ |
|
215 const SkPoint* points() const { SkDEBUGCODE(this->validate();) return fPoints; } |
|
216 |
|
217 /** |
|
218 * Shortcut for this->points() + this->countPoints() |
|
219 */ |
|
220 const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); } |
|
221 |
|
222 const SkScalar* conicWeights() const { SkDEBUGCODE(this->validate();) return fConicWeights.begin(); } |
|
223 const SkScalar* conicWeightsEnd() const { SkDEBUGCODE(this->validate();) return fConicWeights.end(); } |
|
224 |
|
225 /** |
|
226 * Convenience methods for getting to a verb or point by index. |
|
227 */ |
|
228 uint8_t atVerb(int index) const { |
|
229 SkASSERT((unsigned) index < (unsigned) fVerbCnt); |
|
230 return this->verbs()[~index]; |
|
231 } |
|
232 const SkPoint& atPoint(int index) const { |
|
233 SkASSERT((unsigned) index < (unsigned) fPointCnt); |
|
234 return this->points()[index]; |
|
235 } |
|
236 |
|
237 bool operator== (const SkPathRef& ref) const; |
|
238 |
|
239 /** |
|
240 * Writes the path points and verbs to a buffer. |
|
241 */ |
|
242 void writeToBuffer(SkWBuffer* buffer) const; |
|
243 |
|
244 /** |
|
245 * Gets the number of bytes that would be written in writeBuffer() |
|
246 */ |
|
247 uint32_t writeSize() const; |
|
248 |
|
249 /** |
|
250 * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the |
|
251 * same ID then they have the same verbs and points. However, two path refs may have the same |
|
252 * contents but different genIDs. |
|
253 */ |
|
254 uint32_t genID() const; |
|
255 |
|
256 private: |
|
257 enum SerializationOffsets { |
|
258 kIsFinite_SerializationShift = 25, // requires 1 bit |
|
259 kIsOval_SerializationShift = 24, // requires 1 bit |
|
260 kSegmentMask_SerializationShift = 0 // requires 4 bits |
|
261 }; |
|
262 |
|
263 SkPathRef() { |
|
264 fBoundsIsDirty = true; // this also invalidates fIsFinite |
|
265 fPointCnt = 0; |
|
266 fVerbCnt = 0; |
|
267 fVerbs = NULL; |
|
268 fPoints = NULL; |
|
269 fFreeSpace = 0; |
|
270 fGenerationID = kEmptyGenID; |
|
271 fSegmentMask = 0; |
|
272 fIsOval = false; |
|
273 SkDEBUGCODE(fEditorsAttached = 0;) |
|
274 SkDEBUGCODE(this->validate();) |
|
275 } |
|
276 |
|
277 void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints); |
|
278 |
|
279 // Return true if the computed bounds are finite. |
|
280 static bool ComputePtBounds(SkRect* bounds, const SkPathRef& ref) { |
|
281 int count = ref.countPoints(); |
|
282 if (count <= 1) { // we ignore just 1 point (moveto) |
|
283 bounds->setEmpty(); |
|
284 return count ? ref.points()->isFinite() : true; |
|
285 } else { |
|
286 return bounds->setBoundsCheck(ref.points(), count); |
|
287 } |
|
288 } |
|
289 |
|
290 // called, if dirty, by getBounds() |
|
291 void computeBounds() const { |
|
292 SkDEBUGCODE(this->validate();) |
|
293 SkASSERT(fBoundsIsDirty); |
|
294 |
|
295 fIsFinite = ComputePtBounds(&fBounds, *this); |
|
296 fBoundsIsDirty = false; |
|
297 } |
|
298 |
|
299 void setBounds(const SkRect& rect) { |
|
300 SkASSERT(rect.fLeft <= rect.fRight && rect.fTop <= rect.fBottom); |
|
301 fBounds = rect; |
|
302 fBoundsIsDirty = false; |
|
303 fIsFinite = fBounds.isFinite(); |
|
304 } |
|
305 |
|
306 /** Makes additional room but does not change the counts or change the genID */ |
|
307 void incReserve(int additionalVerbs, int additionalPoints) { |
|
308 SkDEBUGCODE(this->validate();) |
|
309 size_t space = additionalVerbs * sizeof(uint8_t) + additionalPoints * sizeof (SkPoint); |
|
310 this->makeSpace(space); |
|
311 SkDEBUGCODE(this->validate();) |
|
312 } |
|
313 |
|
314 /** Resets the path ref with verbCount verbs and pointCount points, all uninitialized. Also |
|
315 * allocates space for reserveVerb additional verbs and reservePoints additional points.*/ |
|
316 void resetToSize(int verbCount, int pointCount, int conicCount, |
|
317 int reserveVerbs = 0, int reservePoints = 0) { |
|
318 SkDEBUGCODE(this->validate();) |
|
319 fBoundsIsDirty = true; // this also invalidates fIsFinite |
|
320 fGenerationID = 0; |
|
321 |
|
322 fSegmentMask = 0; |
|
323 fIsOval = false; |
|
324 |
|
325 size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount; |
|
326 size_t newReserve = sizeof(uint8_t) * reserveVerbs + sizeof(SkPoint) * reservePoints; |
|
327 size_t minSize = newSize + newReserve; |
|
328 |
|
329 ptrdiff_t sizeDelta = this->currSize() - minSize; |
|
330 |
|
331 if (sizeDelta < 0 || static_cast<size_t>(sizeDelta) >= 3 * minSize) { |
|
332 sk_free(fPoints); |
|
333 fPoints = NULL; |
|
334 fVerbs = NULL; |
|
335 fFreeSpace = 0; |
|
336 fVerbCnt = 0; |
|
337 fPointCnt = 0; |
|
338 this->makeSpace(minSize); |
|
339 fVerbCnt = verbCount; |
|
340 fPointCnt = pointCount; |
|
341 fFreeSpace -= newSize; |
|
342 } else { |
|
343 fPointCnt = pointCount; |
|
344 fVerbCnt = verbCount; |
|
345 fFreeSpace = this->currSize() - minSize; |
|
346 } |
|
347 fConicWeights.setCount(conicCount); |
|
348 SkDEBUGCODE(this->validate();) |
|
349 } |
|
350 |
|
351 /** |
|
352 * Increases the verb count by numVbs and point count by the required amount. |
|
353 * The new points are uninitialized. All the new verbs are set to the specified |
|
354 * verb. If 'verb' is kConic_Verb, 'weights' will return a pointer to the |
|
355 * uninitialized conic weights. |
|
356 */ |
|
357 SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, int numVbs, SkScalar** weights); |
|
358 |
|
359 /** |
|
360 * Increases the verb count 1, records the new verb, and creates room for the requisite number |
|
361 * of additional points. A pointer to the first point is returned. Any new points are |
|
362 * uninitialized. |
|
363 */ |
|
364 SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight); |
|
365 |
|
366 /** |
|
367 * Ensures that the free space available in the path ref is >= size. The verb and point counts |
|
368 * are not changed. |
|
369 */ |
|
370 void makeSpace(size_t size) { |
|
371 SkDEBUGCODE(this->validate();) |
|
372 ptrdiff_t growSize = size - fFreeSpace; |
|
373 if (growSize <= 0) { |
|
374 return; |
|
375 } |
|
376 size_t oldSize = this->currSize(); |
|
377 // round to next multiple of 8 bytes |
|
378 growSize = (growSize + 7) & ~static_cast<size_t>(7); |
|
379 // we always at least double the allocation |
|
380 if (static_cast<size_t>(growSize) < oldSize) { |
|
381 growSize = oldSize; |
|
382 } |
|
383 if (growSize < kMinSize) { |
|
384 growSize = kMinSize; |
|
385 } |
|
386 size_t newSize = oldSize + growSize; |
|
387 // Note that realloc could memcpy more than we need. It seems to be a win anyway. TODO: |
|
388 // encapsulate this. |
|
389 fPoints = reinterpret_cast<SkPoint*>(sk_realloc_throw(fPoints, newSize)); |
|
390 size_t oldVerbSize = fVerbCnt * sizeof(uint8_t); |
|
391 void* newVerbsDst = reinterpret_cast<void*>( |
|
392 reinterpret_cast<intptr_t>(fPoints) + newSize - oldVerbSize); |
|
393 void* oldVerbsSrc = reinterpret_cast<void*>( |
|
394 reinterpret_cast<intptr_t>(fPoints) + oldSize - oldVerbSize); |
|
395 memmove(newVerbsDst, oldVerbsSrc, oldVerbSize); |
|
396 fVerbs = reinterpret_cast<uint8_t*>(reinterpret_cast<intptr_t>(fPoints) + newSize); |
|
397 fFreeSpace += growSize; |
|
398 SkDEBUGCODE(this->validate();) |
|
399 } |
|
400 |
|
401 /** |
|
402 * Private, non-const-ptr version of the public function verbsMemBegin(). |
|
403 */ |
|
404 uint8_t* verbsMemWritable() { |
|
405 SkDEBUGCODE(this->validate();) |
|
406 return fVerbs - fVerbCnt; |
|
407 } |
|
408 |
|
409 /** |
|
410 * Gets the total amount of space allocated for verbs, points, and reserve. |
|
411 */ |
|
412 size_t currSize() const { |
|
413 return reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints); |
|
414 } |
|
415 |
|
416 SkDEBUGCODE(void validate() const;) |
|
417 |
|
418 /** |
|
419 * Called the first time someone calls CreateEmpty to actually create the singleton. |
|
420 */ |
|
421 static void CreateEmptyImpl(int/*unused*/); |
|
422 |
|
423 void setIsOval(bool isOval) { fIsOval = isOval; } |
|
424 |
|
425 SkPoint* getPoints() { |
|
426 SkDEBUGCODE(this->validate();) |
|
427 fIsOval = false; |
|
428 return fPoints; |
|
429 } |
|
430 |
|
431 enum { |
|
432 kMinSize = 256, |
|
433 }; |
|
434 |
|
435 mutable SkRect fBounds; |
|
436 uint8_t fSegmentMask; |
|
437 mutable uint8_t fBoundsIsDirty; |
|
438 mutable SkBool8 fIsFinite; // only meaningful if bounds are valid |
|
439 mutable SkBool8 fIsOval; |
|
440 |
|
441 SkPoint* fPoints; // points to begining of the allocation |
|
442 uint8_t* fVerbs; // points just past the end of the allocation (verbs grow backwards) |
|
443 int fVerbCnt; |
|
444 int fPointCnt; |
|
445 size_t fFreeSpace; // redundant but saves computation |
|
446 SkTDArray<SkScalar> fConicWeights; |
|
447 |
|
448 enum { |
|
449 kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs. |
|
450 }; |
|
451 mutable uint32_t fGenerationID; |
|
452 SkDEBUGCODE(int32_t fEditorsAttached;) // assert that only one editor in use at any time. |
|
453 |
|
454 friend class PathRefTest_Private; |
|
455 typedef SkRefCnt INHERITED; |
|
456 }; |
|
457 |
|
458 #endif |