blob: 5f85b7b896ad0ba61f5ec2053de82c8c7d89bdd8 [file] [log] [blame]
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +00001
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 "SkRefCnt.h"
13#include <stddef.h> // ptrdiff_t
14
15// When we're ready to break the picture format. Changes:
16// * Write genID.
17// * SkPathRef read/write counts (which will change the field order)
18// * SkPathRef reads/writes verbs backwards.
19#define NEW_PICTURE_FORMAT 0
20
21/**
22 * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods
23 * modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an
24 * SkPathRef::Editor object. Installing the editor resets the generation ID. It also performs
25 * copy-on-write if the SkPathRef is shared by multipls SkPaths. The caller passes the Editor's
26 * constructor a SkAutoTUnref, which may be updated to point to a new SkPathRef after the editor's
27 * constructor returns.
28 *
29 * The points and verbs are stored in a single allocation. The points are at the begining of the
30 * allocation while the verbs are stored at end of the allocation, in reverse order. Thus the points
31 * and verbs both grow into the middle of the allocation until the meet. To access verb i in the
32 * verb array use ref.verbs()[~i] (because verbs() returns a pointer just beyond the first
33 * logical verb or the last verb in memory).
34 */
bsalomon@google.comae09f2d2012-10-03 19:57:01 +000035
36class SkPathRef;
bsalomon@google.com3a809912012-10-03 21:37:30 +000037
38// This path ref should never be deleted once it is created. It should not be global but was made
39// so for checks when SK_DEBUG_PATH_REF is enabled. It we be re-hidden when the debugging code is
40// reverted.
skia.committer@gmail.com7cc7f492012-10-04 02:01:34 +000041SkPathRef* gEmptyPathRef;
bsalomon@google.comae09f2d2012-10-03 19:57:01 +000042
43// Temporary hackery to try to nail down http://code.google.com/p/chromium/issues/detail?id=148637
44#if SK_DEBUG_PATH_REF
45 #define PR_CONTAINER SkPath::PathRefDebugRef
46 #define SkDEBUGCODE_X(code) code
47 #define SkASSERT_X(cond) SK_DEBUGBREAK(cond)
bsalomon@google.com3a809912012-10-03 21:37:30 +000048 // We put the mutex in a factory function to protect against static-initializion order
49 // fiasco when SkPaths are created before main().
50 static SkMutex* owners_mutex() {
51 static SkMutex* gOwnersMutex;
52 if (!gOwnersMutex) {
53 gOwnersMutex = new SkMutex(); // leak!
54 }
55 return gOwnersMutex;
56 }
57 // We have a static initializer that calls owners_mutex before main() so that
58 // hopefully that we only wind up with one mutex (assuming no threads created
59 // before static initialization is finished.)
60 static const SkMutex* gOwnersMutexForce = owners_mutex();
bsalomon@google.comae09f2d2012-10-03 19:57:01 +000061#else
62 #define PR_CONTAINER SkAutoTUnref<SkPathRef>
63 #define SkDEBUGCODE_X(code) SkDEBUGCODE(code)
64 #define SkASSERT_X(cond) SkASSERT(cond)
65#endif
66
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +000067class SkPathRef : public ::SkRefCnt {
68public:
69 SK_DECLARE_INST_COUNT(SkPathRef);
70
71 class Editor {
72 public:
bsalomon@google.comae09f2d2012-10-03 19:57:01 +000073 Editor(PR_CONTAINER* pathRef,
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +000074 int incReserveVerbs = 0,
75 int incReservePoints = 0) {
76 if (pathRef->get()->getRefCnt() > 1) {
77 SkPathRef* copy = SkNEW(SkPathRef);
78 copy->copy(*pathRef->get(), incReserveVerbs, incReservePoints);
79 pathRef->reset(copy);
80 } else {
81 (*pathRef)->incReserve(incReserveVerbs, incReservePoints);
82 }
83 fPathRef = pathRef->get();
84 fPathRef->fGenerationID = 0;
bsalomon@google.comae09f2d2012-10-03 19:57:01 +000085 SkDEBUGCODE_X(sk_atomic_inc(&fPathRef->fEditorsAttached);)
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +000086 }
87
bsalomon@google.comae09f2d2012-10-03 19:57:01 +000088 ~Editor() { SkDEBUGCODE_X(sk_atomic_dec(&fPathRef->fEditorsAttached);) }
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +000089
90 /**
91 * Returns the array of points.
92 */
93 SkPoint* points() { return fPathRef->fPoints; }
94
95 /**
96 * Gets the ith point. Shortcut for this->points() + i
97 */
98 SkPoint* atPoint(int i) {
99 SkASSERT((unsigned) i < (unsigned) fPathRef->fPointCnt);
100 return this->points() + i;
101 };
102
103 /**
104 * Adds the verb and allocates space for the number of points indicated by the verb. The
105 * return value is a pointer to where the points for the verb should be written.
106 */
107 SkPoint* growForVerb(SkPath::Verb verb) {
108 fPathRef->validate();
109 return fPathRef->growForVerb(verb);
110 }
111
112 /**
113 * Allocates space for additional verbs and points and returns pointers to the new verbs and
114 * points. verbs will point one beyond the first new verb (index it using [~<i>]). pts points
115 * at the first new point (indexed normally [<i>]).
116 */
117 void grow(int newVerbs, int newPts, uint8_t** verbs, SkPoint** pts) {
118 SkASSERT(NULL != verbs);
119 SkASSERT(NULL != pts);
120 fPathRef->validate();
121 int oldVerbCnt = fPathRef->fVerbCnt;
122 int oldPointCnt = fPathRef->fPointCnt;
123 SkASSERT(verbs && pts);
124 fPathRef->grow(newVerbs, newPts);
125 *verbs = fPathRef->fVerbs - oldVerbCnt;
126 *pts = fPathRef->fPoints + oldPointCnt;
127 fPathRef->validate();
128 }
129
130 /**
131 * Resets the path ref to a new verb and point count. The new verbs and points are
132 * uninitialized.
133 */
134 void resetToSize(int newVerbCnt, int newPointCnt) {
135 fPathRef->resetToSize(newVerbCnt, newPointCnt);
136 }
137 /**
138 * Gets the path ref that is wrapped in the Editor.
139 */
140 SkPathRef* pathRef() { return fPathRef; }
141
142 private:
143 SkPathRef* fPathRef;
144 };
145
146public:
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000147#if SK_DEBUG_PATH_REF
148 void addOwner(SkPath* owner) {
bsalomon@google.com3a809912012-10-03 21:37:30 +0000149 SkAutoMutexAcquire ac(owners_mutex());
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000150 for (int i = 0; i < fOwners.count(); ++i) {
151 SkASSERT_X(fOwners[i] != owner);
152 }
153 *fOwners.append() = owner;
154 SkASSERT_X((this->getRefCnt() == fOwners.count()) ||
155 (this == gEmptyPathRef && this->getRefCnt() == fOwners.count() + 1));
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000156 }
157
158 void removeOwner(SkPath* owner) {
bsalomon@google.com3a809912012-10-03 21:37:30 +0000159 SkAutoMutexAcquire ac(owners_mutex());
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000160 SkASSERT_X((this->getRefCnt() == fOwners.count()) ||
161 (this == gEmptyPathRef && this->getRefCnt() == fOwners.count() + 1));
162 bool found = false;
163 for (int i = 0; !found && i < fOwners.count(); ++i) {
164 found = (owner == fOwners[i]);
165 if (found) {
166 fOwners.remove(i);
167 }
168 }
169 SkASSERT_X(found);
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000170 }
171#endif
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000172
173 /**
174 * Gets a path ref with no verbs or points.
175 */
176 static SkPathRef* CreateEmpty() {
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000177 if (!gEmptyPathRef) {
178 gEmptyPathRef = SkNEW(SkPathRef); // leak!
179 }
180 return SkRef(gEmptyPathRef);
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000181 }
182
183 /**
184 * Transforms a path ref by a matrix, allocating a new one only if necessary.
185 */
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000186 static void CreateTransformedCopy(PR_CONTAINER* dst,
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000187 const SkPathRef& src,
188 const SkMatrix& matrix) {
189 src.validate();
190 if (matrix.isIdentity()) {
191 if (dst->get() != &src) {
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000192 src.ref();
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000193 dst->reset(const_cast<SkPathRef*>(&src));
194 (*dst)->validate();
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000195 }
196 return;
197 }
198 int32_t rcnt = dst->get()->getRefCnt();
199 if (&src == dst->get() && 1 == rcnt) {
200 matrix.mapPoints((*dst)->fPoints, (*dst)->fPointCnt);
201 return;
202 } else if (rcnt > 1) {
203 dst->reset(SkNEW(SkPathRef));
204 }
205 (*dst)->resetToSize(src.fVerbCnt, src.fPointCnt);
206 memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(), src.fVerbCnt * sizeof(uint8_t));
207 matrix.mapPoints((*dst)->fPoints, src.points(), src.fPointCnt);
208 (*dst)->validate();
209 }
210
211#if NEW_PICTURE_FORMAT
212 static SkPathRef* CreateFromBuffer(SkRBuffer* buffer) {
213 SkPathRef* ref = SkNEW(SkPathRef);
214 ref->fGenerationID = buffer->readU32();
215 int32_t verbCount = buffer->readS32();
216 int32_t pointCount = buffer->readS32();
217 ref->resetToSize(verbCount, pointCount);
218
219 SkASSERT(verbCount == ref->countVerbs());
220 SkASSERT(pointCount == ref->countPoints());
221 buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t));
222 buffer->read(ref->fPoints, pointCount * sizeof(SkPoint));
223 return ref;
224 }
225#else
226 static SkPathRef* CreateFromBuffer(int verbCount, int pointCount, SkRBuffer* buffer) {
227 SkPathRef* ref = SkNEW(SkPathRef);
228
229 ref->resetToSize(verbCount, pointCount);
230 SkASSERT(verbCount == ref->countVerbs());
231 SkASSERT(pointCount == ref->countPoints());
232 buffer->read(ref->fPoints, pointCount * sizeof(SkPoint));
233 for (int i = 0; i < verbCount; ++i) {
234 ref->fVerbs[~i] = buffer->readU8();
235 }
236 return ref;
237 }
238#endif
239
240 /**
241 * Rollsback a path ref to zero verbs and points with the assumption that the path ref will be
242 * repopulated with approximately the same number of verbs and points. A new path ref is created
243 * only if necessary.
244 */
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000245 static void Rewind(PR_CONTAINER* pathRef) {
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000246 if (1 == (*pathRef)->getRefCnt()) {
247 (*pathRef)->validate();
248 (*pathRef)->fVerbCnt = 0;
249 (*pathRef)->fPointCnt = 0;
250 (*pathRef)->fFreeSpace = (*pathRef)->currSize();
251 (*pathRef)->fGenerationID = 0;
252 (*pathRef)->validate();
253 } else {
254 int oldVCnt = (*pathRef)->countVerbs();
255 int oldPCnt = (*pathRef)->countPoints();
256 pathRef->reset(SkNEW(SkPathRef));
257 (*pathRef)->resetToSize(0, 0, oldVCnt, oldPCnt);
258 }
259 }
260
261 virtual ~SkPathRef() {
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000262 SkASSERT_X(this != gEmptyPathRef);
263#if SK_DEBUG_PATH_REF
264 SkASSERT_X(!fOwners.count());
265#endif
266
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000267 this->validate();
268 sk_free(fPoints);
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000269
270 SkDEBUGCODE_X(fPoints = NULL;)
271 SkDEBUGCODE_X(fVerbs = NULL;)
272 SkDEBUGCODE_X(fVerbCnt = 0x9999999;)
273 SkDEBUGCODE_X(fPointCnt = 0xAAAAAAA;)
274 SkDEBUGCODE_X(fPointCnt = 0xBBBBBBB;)
275 SkDEBUGCODE_X(fGenerationID = 0xEEEEEEEE;)
276 SkDEBUGCODE_X(fEditorsAttached = 0x7777777;)
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000277 }
278
279 int countPoints() const { this->validate(); return fPointCnt; }
280 int countVerbs() const { this->validate(); return fVerbCnt; }
281
282 /**
283 * Returns a pointer one beyond the first logical verb (last verb in memory order).
284 */
285 const uint8_t* verbs() const { this->validate(); return fVerbs; }
286
287 /**
288 * Returns a const pointer to the first verb in memory (which is the last logical verb).
289 */
290 const uint8_t* verbsMemBegin() const { return this->verbs() - fVerbCnt; }
291
292 /**
293 * Returns a const pointer to the first point.
294 */
295 const SkPoint* points() const { this->validate(); return fPoints; }
296
297 /**
298 * Shortcut for this->points() + this->countPoints()
299 */
300 const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); }
301
302 /**
303 * Convenience methods for getting to a verb or point by index.
304 */
305 uint8_t atVerb(int index) {
306 SkASSERT((unsigned) index < (unsigned) fVerbCnt);
307 return this->verbs()[~index];
308 }
309 const SkPoint& atPoint(int index) const {
310 SkASSERT((unsigned) index < (unsigned) fPointCnt);
311 return this->points()[index];
312 }
313
314 bool operator== (const SkPathRef& ref) const {
315 this->validate();
316 ref.validate();
317 bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID;
318#ifdef SK_RELEASE
319 if (genIDMatch) {
320 return true;
321 }
322#endif
323 if (fPointCnt != ref.fPointCnt ||
324 fVerbCnt != ref.fVerbCnt) {
325 SkASSERT(!genIDMatch);
326 return false;
327 }
328 if (0 != memcmp(this->verbsMemBegin(),
329 ref.verbsMemBegin(),
330 ref.fVerbCnt * sizeof(uint8_t))) {
331 SkASSERT(!genIDMatch);
332 return false;
333 }
334 if (0 != memcmp(this->points(),
335 ref.points(),
336 ref.fPointCnt * sizeof(SkPoint))) {
337 SkASSERT(!genIDMatch);
338 return false;
339 }
340 // We've done the work to determine that these are equal. If either has a zero genID, copy
341 // the other's. If both are 0 then genID() will compute the next ID.
342 if (0 == fGenerationID) {
343 fGenerationID = ref.genID();
344 } else if (0 == ref.fGenerationID) {
345 ref.fGenerationID = this->genID();
346 }
347 return true;
348 }
349
350 /**
351 * Writes the path points and verbs to a buffer.
352 */
353#if NEW_PICTURE_FORMAT
354 void writeToBuffer(SkWBuffer* buffer) {
355 this->validate();
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000356 SkDEBUGCODE_X(size_t beforePos = buffer->pos();)
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000357
358 // TODO: write gen ID here. Problem: We don't know if we're cross process or not from
359 // SkWBuffer. Until this is fixed we write 0.
360 buffer->write32(0);
361 buffer->write32(this->fVerbCnt);
362 buffer->write32(this->fPointCnt);
363 buffer->write(this->verbsMemBegin(), fVerbCnt * sizeof(uint8_t));
364 buffer->write(fPoints, fPointCnt * sizeof(SkPoint));
365
366 SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize());
367 }
368
369 /**
370 * Gets the number of bytes that would be written in writeBuffer()
371 */
372 uint32_t writeSize() {
373 return 3 * sizeof(uint32_t) + fVerbCnt * sizeof(uint8_t) + fPointCnt * sizeof(SkPoint);
374 }
375#else
376 void writeToBuffer(SkWBuffer* buffer) {
377 this->validate();
378 buffer->write(fPoints, fPointCnt * sizeof(SkPoint));
379 for (int i = 0; i < fVerbCnt; ++i) {
380 buffer->write8(fVerbs[~i]);
381 }
382 }
383#endif
384
385private:
386 SkPathRef() {
387 fPointCnt = 0;
388 fVerbCnt = 0;
389 fVerbs = NULL;
390 fPoints = NULL;
391 fFreeSpace = 0;
392 fGenerationID = kEmptyGenID;
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000393 SkDEBUGCODE_X(fEditorsAttached = 0;)
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000394 this->validate();
395 }
396
397 void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints) {
398 this->validate();
399 this->resetToSize(ref.fVerbCnt, ref.fPointCnt,
400 additionalReserveVerbs, additionalReservePoints);
401 memcpy(this->verbsMemWritable(), ref.verbsMemBegin(), ref.fVerbCnt * sizeof(uint8_t));
402 memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint));
403 // We could call genID() here to force a real ID (instead of 0). However, if we're making
404 // a copy then presumably we intend to make a modification immediately afterwards.
405 fGenerationID = ref.fGenerationID;
406 this->validate();
407 }
408
409 /** Makes additional room but does not change the counts or change the genID */
410 void incReserve(int additionalVerbs, int additionalPoints) {
411 this->validate();
412 size_t space = additionalVerbs * sizeof(uint8_t) + additionalPoints * sizeof (SkPoint);
413 this->makeSpace(space);
414 this->validate();
415 }
416
417 /** Resets the path ref with verbCount verbs and pointCount points, all unitialized. Also
418 * allocates space for reserveVerb additional verbs and reservePoints additional points.*/
419 void resetToSize(int verbCount, int pointCount, int reserveVerbs = 0, int reservePoints = 0) {
420 this->validate();
421 fGenerationID = 0;
422
423 size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount;
424 size_t newReserve = sizeof(uint8_t) * reserveVerbs + sizeof(SkPoint) * reservePoints;
425 size_t minSize = newSize + newReserve;
426
427 ptrdiff_t sizeDelta = this->currSize() - minSize;
428
429 if (sizeDelta < 0 || static_cast<size_t>(sizeDelta) >= 3 * minSize) {
430 sk_free(fPoints);
431 fPoints = NULL;
432 fVerbs = NULL;
433 fFreeSpace = 0;
434 fVerbCnt = 0;
435 fPointCnt = 0;
436 this->makeSpace(minSize);
437 fVerbCnt = verbCount;
438 fPointCnt = pointCount;
439 fFreeSpace -= newSize;
440 } else {
441 fPointCnt = pointCount;
442 fVerbCnt = verbCount;
443 fFreeSpace = this->currSize() - minSize;
444 }
445 this->validate();
446 }
447
448 /**
449 * Increases the verb count by newVerbs and the point count be newPoints. New verbs and points
450 * are uninitialized.
451 */
452 void grow(int newVerbs, int newPoints) {
453 this->validate();
454 size_t space = newVerbs * sizeof(uint8_t) + newPoints * sizeof (SkPoint);
455 this->makeSpace(space);
456 fVerbCnt += newVerbs;
457 fPointCnt += newPoints;
458 fFreeSpace -= space;
459 this->validate();
460 }
461
462 /**
463 * Increases the verb count 1, records the new verb, and creates room for the requisite number
464 * of additional points. A pointer to the first point is returned. Any new points are
465 * uninitialized.
466 */
467 SkPoint* growForVerb(SkPath::Verb verb) {
468 this->validate();
469 int pCnt;
470 switch (verb) {
471 case SkPath::kMove_Verb:
472 pCnt = 1;
473 break;
474 case SkPath::kLine_Verb:
475 pCnt = 1;
476 break;
477 case SkPath::kQuad_Verb:
478 pCnt = 2;
479 break;
480 case SkPath::kCubic_Verb:
481 pCnt = 3;
482 break;
483 default:
484 pCnt = 0;
485 }
486 size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint);
487 this->makeSpace(space);
488 this->fVerbs[~fVerbCnt] = verb;
489 SkPoint* ret = fPoints + fPointCnt;
490 fVerbCnt += 1;
491 fPointCnt += pCnt;
492 fFreeSpace -= space;
493 this->validate();
494 return ret;
495 }
496
497 /**
498 * Ensures that the free space available in the path ref is >= size. The verb and point counts
499 * are not changed.
500 */
501 void makeSpace(size_t size) {
502 this->validate();
503 ptrdiff_t growSize = size - fFreeSpace;
504 if (growSize <= 0) {
505 return;
506 }
507 size_t oldSize = this->currSize();
508 // round to next multiple of 8 bytes
509 growSize = (growSize + 7) & ~static_cast<size_t>(7);
510 // we always at least double the allocation
511 if (static_cast<size_t>(growSize) < oldSize) {
512 growSize = oldSize;
513 }
514 if (growSize < kMinSize) {
515 growSize = kMinSize;
516 }
517 size_t newSize = oldSize + growSize;
518 // Note that realloc could memcpy more than we need. It seems to be a win anyway. TODO:
519 // encapsulate this.
520 fPoints = reinterpret_cast<SkPoint*>(sk_realloc_throw(fPoints, newSize));
521 size_t oldVerbSize = fVerbCnt * sizeof(uint8_t);
522 void* newVerbsDst = reinterpret_cast<void*>(
523 reinterpret_cast<intptr_t>(fPoints) + newSize - oldVerbSize);
524 void* oldVerbsSrc = reinterpret_cast<void*>(
525 reinterpret_cast<intptr_t>(fPoints) + oldSize - oldVerbSize);
526 memmove(newVerbsDst, oldVerbsSrc, oldVerbSize);
527 fVerbs = reinterpret_cast<uint8_t*>(reinterpret_cast<intptr_t>(fPoints) + newSize);
528 fFreeSpace += growSize;
529 this->validate();
530 }
531
532 /**
533 * Private, non-const-ptr version of the public function verbsMemBegin().
534 */
535 uint8_t* verbsMemWritable() {
536 this->validate();
537 return fVerbs - fVerbCnt;
538 }
539
540 /**
541 * Gets the total amount of space allocated for verbs, points, and reserve.
542 */
543 size_t currSize() const {
544 return reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints);
545 }
546
547 /**
548 * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the
549 * same ID then they have the same verbs and points. However, two path refs may have the same
550 * contents but different genIDs. Zero is reserved and means an ID has not yet been determined
551 * for the path ref.
552 */
553 int32_t genID() const {
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000554 SkASSERT_X(!fEditorsAttached);
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000555 if (!fGenerationID) {
556 if (0 == fPointCnt && 0 == fVerbCnt) {
557 fGenerationID = kEmptyGenID;
558 } else {
559 static int32_t gPathRefGenerationID;
560 // do a loop in case our global wraps around, as we never want to return a 0 or the
561 // empty ID
562 do {
563 fGenerationID = sk_atomic_inc(&gPathRefGenerationID) + 1;
564 } while (fGenerationID <= kEmptyGenID);
565 }
566 }
567 return fGenerationID;
568 }
569
570 void validate() const {
571 SkASSERT(static_cast<ptrdiff_t>(fFreeSpace) >= 0);
572 SkASSERT(reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints) >= 0);
573 SkASSERT((NULL == fPoints) == (NULL == fVerbs));
574 SkASSERT(!(NULL == fPoints && 0 != fFreeSpace));
575 SkASSERT(!(NULL == fPoints && 0 != fFreeSpace));
576 SkASSERT(!(NULL == fPoints && fPointCnt));
577 SkASSERT(!(NULL == fVerbs && fVerbCnt));
578 SkASSERT(this->currSize() ==
579 fFreeSpace + sizeof(SkPoint) * fPointCnt + sizeof(uint8_t) * fVerbCnt);
580 }
581
582 enum {
583 kMinSize = 256,
584 };
585
586 SkPoint* fPoints; // points to begining of the allocation
587 uint8_t* fVerbs; // points just past the end of the allocation (verbs grow backwards)
588 int fVerbCnt;
589 int fPointCnt;
590 size_t fFreeSpace; // redundant but saves computation
591 enum {
592 kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs.
593 };
594 mutable int32_t fGenerationID;
bsalomon@google.comae09f2d2012-10-03 19:57:01 +0000595 SkDEBUGCODE_X(int32_t fEditorsAttached;) // assert that only one editor in use at any time.
596
597#if SK_DEBUG_PATH_REF
598 SkTDArray<SkPath*> fOwners;
599#endif
bsalomon@google.com1dfe88e2012-10-03 13:46:20 +0000600
601 typedef SkRefCnt INHERITED;
602};
603
604SK_DEFINE_INST_COUNT(SkPathRef);
605
606#endif