Home | History | Annotate | Download | only in core
      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 "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  */
     35 
     36 class SkPathRef;
     37 
     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.
     41 SkPathRef* gEmptyPathRef;
     42 
     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)
     48     // 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();
     61 #else
     62     #define PR_CONTAINER SkAutoTUnref<SkPathRef>
     63     #define SkDEBUGCODE_X(code) SkDEBUGCODE(code)
     64     #define SkASSERT_X(cond) SkASSERT(cond)
     65 #endif
     66 
     67 class SkPathRef : public ::SkRefCnt {
     68 public:
     69     SK_DECLARE_INST_COUNT(SkPathRef);
     70 
     71     class Editor {
     72     public:
     73         Editor(PR_CONTAINER* pathRef,
     74                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;
     85             SkDEBUGCODE_X(sk_atomic_inc(&fPathRef->fEditorsAttached);)
     86         }
     87 
     88         ~Editor() { SkDEBUGCODE_X(sk_atomic_dec(&fPathRef->fEditorsAttached);) }
     89 
     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 
    146 public:
    147 #if SK_DEBUG_PATH_REF
    148     void addOwner(SkPath* owner) {
    149         SkAutoMutexAcquire ac(owners_mutex());
    150         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));
    156     }
    157 
    158     void removeOwner(SkPath* owner) {
    159         SkAutoMutexAcquire ac(owners_mutex());
    160         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);
    170     }
    171 #endif
    172 
    173     /**
    174      * Gets a path ref with no verbs or points.
    175      */
    176     static SkPathRef* CreateEmpty() {
    177         if (!gEmptyPathRef) {
    178             gEmptyPathRef = SkNEW(SkPathRef); // leak!
    179         }
    180         return SkRef(gEmptyPathRef);
    181     }
    182 
    183     /**
    184      * Transforms a path ref by a matrix, allocating a new one only if necessary.
    185      */
    186     static void CreateTransformedCopy(PR_CONTAINER* dst,
    187                                       const SkPathRef& src,
    188                                       const SkMatrix& matrix) {
    189         src.validate();
    190         if (matrix.isIdentity()) {
    191             if (dst->get() != &src) {
    192                 src.ref();
    193                 dst->reset(const_cast<SkPathRef*>(&src));
    194                 (*dst)->validate();
    195             }
    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      */
    245     static void Rewind(PR_CONTAINER* pathRef) {
    246         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() {
    262         SkASSERT_X(this != gEmptyPathRef);
    263 #if SK_DEBUG_PATH_REF
    264         SkASSERT_X(!fOwners.count());
    265 #endif
    266 
    267         this->validate();
    268         sk_free(fPoints);
    269 
    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;)
    277     }
    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();
    356         SkDEBUGCODE_X(size_t beforePos = buffer->pos();)
    357 
    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 
    385 private:
    386     SkPathRef() {
    387         fPointCnt = 0;
    388         fVerbCnt = 0;
    389         fVerbs = NULL;
    390         fPoints = NULL;
    391         fFreeSpace = 0;
    392         fGenerationID = kEmptyGenID;
    393         SkDEBUGCODE_X(fEditorsAttached = 0;)
    394         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 {
    554         SkASSERT_X(!fEditorsAttached);
    555         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;
    595     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
    600 
    601     typedef SkRefCnt INHERITED;
    602 };
    603 
    604 SK_DEFINE_INST_COUNT(SkPathRef);
    605 
    606 #endif
    607