| |
| /* |
| * Copyright 2012 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef SkPathRef_DEFINED |
| #define SkPathRef_DEFINED |
| |
| #include "SkRefCnt.h" |
| #include <stddef.h> // ptrdiff_t |
| |
| // When we're ready to break the picture format. Changes: |
| // * Write genID. |
| // * SkPathRef read/write counts (which will change the field order) |
| // * SkPathRef reads/writes verbs backwards. |
| #define NEW_PICTURE_FORMAT 0 |
| |
| /** |
| * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods |
| * modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an |
| * SkPathRef::Editor object. Installing the editor resets the generation ID. It also performs |
| * copy-on-write if the SkPathRef is shared by multipls SkPaths. The caller passes the Editor's |
| * constructor a SkAutoTUnref, which may be updated to point to a new SkPathRef after the editor's |
| * constructor returns. |
| * |
| * The points and verbs are stored in a single allocation. The points are at the begining of the |
| * allocation while the verbs are stored at end of the allocation, in reverse order. Thus the points |
| * and verbs both grow into the middle of the allocation until the meet. To access verb i in the |
| * verb array use ref.verbs()[~i] (because verbs() returns a pointer just beyond the first |
| * logical verb or the last verb in memory). |
| */ |
| |
| class SkPathRef; |
| |
| // This path ref should never be deleted once it is created. It should not be global but was made |
| // so for checks when SK_DEBUG_PATH_REF is enabled. It we be re-hidden when the debugging code is |
| // reverted. |
| SkPathRef* gEmptyPathRef; |
| |
| // Temporary hackery to try to nail down http://code.google.com/p/chromium/issues/detail?id=148637 |
| #if SK_DEBUG_PATH_REF |
| #define PR_CONTAINER SkPath::PathRefDebugRef |
| #define SkDEBUGCODE_X(code) code |
| #define SkASSERT_X(cond) SK_DEBUGBREAK(cond) |
| // We put the mutex in a factory function to protect against static-initializion order |
| // fiasco when SkPaths are created before main(). |
| static SkMutex* owners_mutex() { |
| static SkMutex* gOwnersMutex; |
| if (!gOwnersMutex) { |
| gOwnersMutex = new SkMutex(); // leak! |
| } |
| return gOwnersMutex; |
| } |
| // We have a static initializer that calls owners_mutex before main() so that |
| // hopefully that we only wind up with one mutex (assuming no threads created |
| // before static initialization is finished.) |
| static const SkMutex* gOwnersMutexForce = owners_mutex(); |
| #else |
| #define PR_CONTAINER SkAutoTUnref<SkPathRef> |
| #define SkDEBUGCODE_X(code) SkDEBUGCODE(code) |
| #define SkASSERT_X(cond) SkASSERT(cond) |
| #endif |
| |
| class SkPathRef : public ::SkRefCnt { |
| public: |
| SK_DECLARE_INST_COUNT(SkPathRef); |
| |
| class Editor { |
| public: |
| Editor(PR_CONTAINER* pathRef, |
| int incReserveVerbs = 0, |
| int incReservePoints = 0) { |
| if (pathRef->get()->getRefCnt() > 1) { |
| SkPathRef* copy = SkNEW(SkPathRef); |
| copy->copy(*pathRef->get(), incReserveVerbs, incReservePoints); |
| pathRef->reset(copy); |
| } else { |
| (*pathRef)->incReserve(incReserveVerbs, incReservePoints); |
| } |
| fPathRef = pathRef->get(); |
| fPathRef->fGenerationID = 0; |
| SkDEBUGCODE_X(sk_atomic_inc(&fPathRef->fEditorsAttached);) |
| } |
| |
| ~Editor() { SkDEBUGCODE_X(sk_atomic_dec(&fPathRef->fEditorsAttached);) } |
| |
| /** |
| * Returns the array of points. |
| */ |
| SkPoint* points() { return fPathRef->fPoints; } |
| |
| /** |
| * Gets the ith point. Shortcut for this->points() + i |
| */ |
| SkPoint* atPoint(int i) { |
| SkASSERT((unsigned) i < (unsigned) fPathRef->fPointCnt); |
| return this->points() + i; |
| }; |
| |
| /** |
| * Adds the verb and allocates space for the number of points indicated by the verb. The |
| * return value is a pointer to where the points for the verb should be written. |
| */ |
| SkPoint* growForVerb(SkPath::Verb verb) { |
| fPathRef->validate(); |
| return fPathRef->growForVerb(verb); |
| } |
| |
| /** |
| * Allocates space for additional verbs and points and returns pointers to the new verbs and |
| * points. verbs will point one beyond the first new verb (index it using [~<i>]). pts points |
| * at the first new point (indexed normally [<i>]). |
| */ |
| void grow(int newVerbs, int newPts, uint8_t** verbs, SkPoint** pts) { |
| SkASSERT(NULL != verbs); |
| SkASSERT(NULL != pts); |
| fPathRef->validate(); |
| int oldVerbCnt = fPathRef->fVerbCnt; |
| int oldPointCnt = fPathRef->fPointCnt; |
| SkASSERT(verbs && pts); |
| fPathRef->grow(newVerbs, newPts); |
| *verbs = fPathRef->fVerbs - oldVerbCnt; |
| *pts = fPathRef->fPoints + oldPointCnt; |
| fPathRef->validate(); |
| } |
| |
| /** |
| * Resets the path ref to a new verb and point count. The new verbs and points are |
| * uninitialized. |
| */ |
| void resetToSize(int newVerbCnt, int newPointCnt) { |
| fPathRef->resetToSize(newVerbCnt, newPointCnt); |
| } |
| /** |
| * Gets the path ref that is wrapped in the Editor. |
| */ |
| SkPathRef* pathRef() { return fPathRef; } |
| |
| private: |
| SkPathRef* fPathRef; |
| }; |
| |
| public: |
| #if SK_DEBUG_PATH_REF |
| void addOwner(SkPath* owner) { |
| SkAutoMutexAcquire ac(owners_mutex()); |
| for (int i = 0; i < fOwners.count(); ++i) { |
| SkASSERT_X(fOwners[i] != owner); |
| } |
| *fOwners.append() = owner; |
| SkASSERT_X((this->getRefCnt() == fOwners.count()) || |
| (this == gEmptyPathRef && this->getRefCnt() == fOwners.count() + 1)); |
| } |
| |
| void removeOwner(SkPath* owner) { |
| SkAutoMutexAcquire ac(owners_mutex()); |
| SkASSERT_X((this->getRefCnt() == fOwners.count()) || |
| (this == gEmptyPathRef && this->getRefCnt() == fOwners.count() + 1)); |
| bool found = false; |
| for (int i = 0; !found && i < fOwners.count(); ++i) { |
| found = (owner == fOwners[i]); |
| if (found) { |
| fOwners.remove(i); |
| } |
| } |
| SkASSERT_X(found); |
| } |
| #endif |
| |
| /** |
| * Gets a path ref with no verbs or points. |
| */ |
| static SkPathRef* CreateEmpty() { |
| if (!gEmptyPathRef) { |
| gEmptyPathRef = SkNEW(SkPathRef); // leak! |
| } |
| return SkRef(gEmptyPathRef); |
| } |
| |
| /** |
| * Transforms a path ref by a matrix, allocating a new one only if necessary. |
| */ |
| static void CreateTransformedCopy(PR_CONTAINER* dst, |
| const SkPathRef& src, |
| const SkMatrix& matrix) { |
| src.validate(); |
| if (matrix.isIdentity()) { |
| if (dst->get() != &src) { |
| src.ref(); |
| dst->reset(const_cast<SkPathRef*>(&src)); |
| (*dst)->validate(); |
| } |
| return; |
| } |
| int32_t rcnt = dst->get()->getRefCnt(); |
| if (&src == dst->get() && 1 == rcnt) { |
| matrix.mapPoints((*dst)->fPoints, (*dst)->fPointCnt); |
| return; |
| } else if (rcnt > 1) { |
| dst->reset(SkNEW(SkPathRef)); |
| } |
| (*dst)->resetToSize(src.fVerbCnt, src.fPointCnt); |
| memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(), src.fVerbCnt * sizeof(uint8_t)); |
| matrix.mapPoints((*dst)->fPoints, src.points(), src.fPointCnt); |
| (*dst)->validate(); |
| } |
| |
| #if NEW_PICTURE_FORMAT |
| static SkPathRef* CreateFromBuffer(SkRBuffer* buffer) { |
| SkPathRef* ref = SkNEW(SkPathRef); |
| ref->fGenerationID = buffer->readU32(); |
| int32_t verbCount = buffer->readS32(); |
| int32_t pointCount = buffer->readS32(); |
| ref->resetToSize(verbCount, pointCount); |
| |
| SkASSERT(verbCount == ref->countVerbs()); |
| SkASSERT(pointCount == ref->countPoints()); |
| buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t)); |
| buffer->read(ref->fPoints, pointCount * sizeof(SkPoint)); |
| return ref; |
| } |
| #else |
| static SkPathRef* CreateFromBuffer(int verbCount, int pointCount, SkRBuffer* buffer) { |
| SkPathRef* ref = SkNEW(SkPathRef); |
| |
| ref->resetToSize(verbCount, pointCount); |
| SkASSERT(verbCount == ref->countVerbs()); |
| SkASSERT(pointCount == ref->countPoints()); |
| buffer->read(ref->fPoints, pointCount * sizeof(SkPoint)); |
| for (int i = 0; i < verbCount; ++i) { |
| ref->fVerbs[~i] = buffer->readU8(); |
| } |
| return ref; |
| } |
| #endif |
| |
| /** |
| * Rollsback a path ref to zero verbs and points with the assumption that the path ref will be |
| * repopulated with approximately the same number of verbs and points. A new path ref is created |
| * only if necessary. |
| */ |
| static void Rewind(PR_CONTAINER* pathRef) { |
| if (1 == (*pathRef)->getRefCnt()) { |
| (*pathRef)->validate(); |
| (*pathRef)->fVerbCnt = 0; |
| (*pathRef)->fPointCnt = 0; |
| (*pathRef)->fFreeSpace = (*pathRef)->currSize(); |
| (*pathRef)->fGenerationID = 0; |
| (*pathRef)->validate(); |
| } else { |
| int oldVCnt = (*pathRef)->countVerbs(); |
| int oldPCnt = (*pathRef)->countPoints(); |
| pathRef->reset(SkNEW(SkPathRef)); |
| (*pathRef)->resetToSize(0, 0, oldVCnt, oldPCnt); |
| } |
| } |
| |
| virtual ~SkPathRef() { |
| SkASSERT_X(this != gEmptyPathRef); |
| #if SK_DEBUG_PATH_REF |
| SkASSERT_X(!fOwners.count()); |
| #endif |
| |
| this->validate(); |
| sk_free(fPoints); |
| |
| SkDEBUGCODE_X(fPoints = NULL;) |
| SkDEBUGCODE_X(fVerbs = NULL;) |
| SkDEBUGCODE_X(fVerbCnt = 0x9999999;) |
| SkDEBUGCODE_X(fPointCnt = 0xAAAAAAA;) |
| SkDEBUGCODE_X(fPointCnt = 0xBBBBBBB;) |
| SkDEBUGCODE_X(fGenerationID = 0xEEEEEEEE;) |
| SkDEBUGCODE_X(fEditorsAttached = 0x7777777;) |
| } |
| |
| int countPoints() const { this->validate(); return fPointCnt; } |
| int countVerbs() const { this->validate(); return fVerbCnt; } |
| |
| /** |
| * Returns a pointer one beyond the first logical verb (last verb in memory order). |
| */ |
| const uint8_t* verbs() const { this->validate(); return fVerbs; } |
| |
| /** |
| * Returns a const pointer to the first verb in memory (which is the last logical verb). |
| */ |
| const uint8_t* verbsMemBegin() const { return this->verbs() - fVerbCnt; } |
| |
| /** |
| * Returns a const pointer to the first point. |
| */ |
| const SkPoint* points() const { this->validate(); return fPoints; } |
| |
| /** |
| * Shortcut for this->points() + this->countPoints() |
| */ |
| const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); } |
| |
| /** |
| * Convenience methods for getting to a verb or point by index. |
| */ |
| uint8_t atVerb(int index) { |
| SkASSERT((unsigned) index < (unsigned) fVerbCnt); |
| return this->verbs()[~index]; |
| } |
| const SkPoint& atPoint(int index) const { |
| SkASSERT((unsigned) index < (unsigned) fPointCnt); |
| return this->points()[index]; |
| } |
| |
| bool operator== (const SkPathRef& ref) const { |
| this->validate(); |
| ref.validate(); |
| bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID; |
| #ifdef SK_RELEASE |
| if (genIDMatch) { |
| return true; |
| } |
| #endif |
| if (fPointCnt != ref.fPointCnt || |
| fVerbCnt != ref.fVerbCnt) { |
| SkASSERT(!genIDMatch); |
| return false; |
| } |
| if (0 != memcmp(this->verbsMemBegin(), |
| ref.verbsMemBegin(), |
| ref.fVerbCnt * sizeof(uint8_t))) { |
| SkASSERT(!genIDMatch); |
| return false; |
| } |
| if (0 != memcmp(this->points(), |
| ref.points(), |
| ref.fPointCnt * sizeof(SkPoint))) { |
| SkASSERT(!genIDMatch); |
| return false; |
| } |
| // We've done the work to determine that these are equal. If either has a zero genID, copy |
| // the other's. If both are 0 then genID() will compute the next ID. |
| if (0 == fGenerationID) { |
| fGenerationID = ref.genID(); |
| } else if (0 == ref.fGenerationID) { |
| ref.fGenerationID = this->genID(); |
| } |
| return true; |
| } |
| |
| /** |
| * Writes the path points and verbs to a buffer. |
| */ |
| #if NEW_PICTURE_FORMAT |
| void writeToBuffer(SkWBuffer* buffer) { |
| this->validate(); |
| SkDEBUGCODE_X(size_t beforePos = buffer->pos();) |
| |
| // TODO: write gen ID here. Problem: We don't know if we're cross process or not from |
| // SkWBuffer. Until this is fixed we write 0. |
| buffer->write32(0); |
| buffer->write32(this->fVerbCnt); |
| buffer->write32(this->fPointCnt); |
| buffer->write(this->verbsMemBegin(), fVerbCnt * sizeof(uint8_t)); |
| buffer->write(fPoints, fPointCnt * sizeof(SkPoint)); |
| |
| SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize()); |
| } |
| |
| /** |
| * Gets the number of bytes that would be written in writeBuffer() |
| */ |
| uint32_t writeSize() { |
| return 3 * sizeof(uint32_t) + fVerbCnt * sizeof(uint8_t) + fPointCnt * sizeof(SkPoint); |
| } |
| #else |
| void writeToBuffer(SkWBuffer* buffer) { |
| this->validate(); |
| buffer->write(fPoints, fPointCnt * sizeof(SkPoint)); |
| for (int i = 0; i < fVerbCnt; ++i) { |
| buffer->write8(fVerbs[~i]); |
| } |
| } |
| #endif |
| |
| private: |
| SkPathRef() { |
| fPointCnt = 0; |
| fVerbCnt = 0; |
| fVerbs = NULL; |
| fPoints = NULL; |
| fFreeSpace = 0; |
| fGenerationID = kEmptyGenID; |
| SkDEBUGCODE_X(fEditorsAttached = 0;) |
| this->validate(); |
| } |
| |
| void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints) { |
| this->validate(); |
| this->resetToSize(ref.fVerbCnt, ref.fPointCnt, |
| additionalReserveVerbs, additionalReservePoints); |
| memcpy(this->verbsMemWritable(), ref.verbsMemBegin(), ref.fVerbCnt * sizeof(uint8_t)); |
| memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint)); |
| // We could call genID() here to force a real ID (instead of 0). However, if we're making |
| // a copy then presumably we intend to make a modification immediately afterwards. |
| fGenerationID = ref.fGenerationID; |
| this->validate(); |
| } |
| |
| /** Makes additional room but does not change the counts or change the genID */ |
| void incReserve(int additionalVerbs, int additionalPoints) { |
| this->validate(); |
| size_t space = additionalVerbs * sizeof(uint8_t) + additionalPoints * sizeof (SkPoint); |
| this->makeSpace(space); |
| this->validate(); |
| } |
| |
| /** Resets the path ref with verbCount verbs and pointCount points, all unitialized. Also |
| * allocates space for reserveVerb additional verbs and reservePoints additional points.*/ |
| void resetToSize(int verbCount, int pointCount, int reserveVerbs = 0, int reservePoints = 0) { |
| this->validate(); |
| fGenerationID = 0; |
| |
| size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount; |
| size_t newReserve = sizeof(uint8_t) * reserveVerbs + sizeof(SkPoint) * reservePoints; |
| size_t minSize = newSize + newReserve; |
| |
| ptrdiff_t sizeDelta = this->currSize() - minSize; |
| |
| if (sizeDelta < 0 || static_cast<size_t>(sizeDelta) >= 3 * minSize) { |
| sk_free(fPoints); |
| fPoints = NULL; |
| fVerbs = NULL; |
| fFreeSpace = 0; |
| fVerbCnt = 0; |
| fPointCnt = 0; |
| this->makeSpace(minSize); |
| fVerbCnt = verbCount; |
| fPointCnt = pointCount; |
| fFreeSpace -= newSize; |
| } else { |
| fPointCnt = pointCount; |
| fVerbCnt = verbCount; |
| fFreeSpace = this->currSize() - minSize; |
| } |
| this->validate(); |
| } |
| |
| /** |
| * Increases the verb count by newVerbs and the point count be newPoints. New verbs and points |
| * are uninitialized. |
| */ |
| void grow(int newVerbs, int newPoints) { |
| this->validate(); |
| size_t space = newVerbs * sizeof(uint8_t) + newPoints * sizeof (SkPoint); |
| this->makeSpace(space); |
| fVerbCnt += newVerbs; |
| fPointCnt += newPoints; |
| fFreeSpace -= space; |
| this->validate(); |
| } |
| |
| /** |
| * Increases the verb count 1, records the new verb, and creates room for the requisite number |
| * of additional points. A pointer to the first point is returned. Any new points are |
| * uninitialized. |
| */ |
| SkPoint* growForVerb(SkPath::Verb verb) { |
| this->validate(); |
| int pCnt; |
| switch (verb) { |
| case SkPath::kMove_Verb: |
| pCnt = 1; |
| break; |
| case SkPath::kLine_Verb: |
| pCnt = 1; |
| break; |
| case SkPath::kQuad_Verb: |
| pCnt = 2; |
| break; |
| case SkPath::kCubic_Verb: |
| pCnt = 3; |
| break; |
| default: |
| pCnt = 0; |
| } |
| size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint); |
| this->makeSpace(space); |
| this->fVerbs[~fVerbCnt] = verb; |
| SkPoint* ret = fPoints + fPointCnt; |
| fVerbCnt += 1; |
| fPointCnt += pCnt; |
| fFreeSpace -= space; |
| this->validate(); |
| return ret; |
| } |
| |
| /** |
| * Ensures that the free space available in the path ref is >= size. The verb and point counts |
| * are not changed. |
| */ |
| void makeSpace(size_t size) { |
| this->validate(); |
| ptrdiff_t growSize = size - fFreeSpace; |
| if (growSize <= 0) { |
| return; |
| } |
| size_t oldSize = this->currSize(); |
| // round to next multiple of 8 bytes |
| growSize = (growSize + 7) & ~static_cast<size_t>(7); |
| // we always at least double the allocation |
| if (static_cast<size_t>(growSize) < oldSize) { |
| growSize = oldSize; |
| } |
| if (growSize < kMinSize) { |
| growSize = kMinSize; |
| } |
| size_t newSize = oldSize + growSize; |
| // Note that realloc could memcpy more than we need. It seems to be a win anyway. TODO: |
| // encapsulate this. |
| fPoints = reinterpret_cast<SkPoint*>(sk_realloc_throw(fPoints, newSize)); |
| size_t oldVerbSize = fVerbCnt * sizeof(uint8_t); |
| void* newVerbsDst = reinterpret_cast<void*>( |
| reinterpret_cast<intptr_t>(fPoints) + newSize - oldVerbSize); |
| void* oldVerbsSrc = reinterpret_cast<void*>( |
| reinterpret_cast<intptr_t>(fPoints) + oldSize - oldVerbSize); |
| memmove(newVerbsDst, oldVerbsSrc, oldVerbSize); |
| fVerbs = reinterpret_cast<uint8_t*>(reinterpret_cast<intptr_t>(fPoints) + newSize); |
| fFreeSpace += growSize; |
| this->validate(); |
| } |
| |
| /** |
| * Private, non-const-ptr version of the public function verbsMemBegin(). |
| */ |
| uint8_t* verbsMemWritable() { |
| this->validate(); |
| return fVerbs - fVerbCnt; |
| } |
| |
| /** |
| * Gets the total amount of space allocated for verbs, points, and reserve. |
| */ |
| size_t currSize() const { |
| return reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints); |
| } |
| |
| /** |
| * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the |
| * same ID then they have the same verbs and points. However, two path refs may have the same |
| * contents but different genIDs. Zero is reserved and means an ID has not yet been determined |
| * for the path ref. |
| */ |
| int32_t genID() const { |
| SkASSERT_X(!fEditorsAttached); |
| if (!fGenerationID) { |
| if (0 == fPointCnt && 0 == fVerbCnt) { |
| fGenerationID = kEmptyGenID; |
| } else { |
| static int32_t gPathRefGenerationID; |
| // do a loop in case our global wraps around, as we never want to return a 0 or the |
| // empty ID |
| do { |
| fGenerationID = sk_atomic_inc(&gPathRefGenerationID) + 1; |
| } while (fGenerationID <= kEmptyGenID); |
| } |
| } |
| return fGenerationID; |
| } |
| |
| void validate() const { |
| SkASSERT(static_cast<ptrdiff_t>(fFreeSpace) >= 0); |
| SkASSERT(reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints) >= 0); |
| SkASSERT((NULL == fPoints) == (NULL == fVerbs)); |
| SkASSERT(!(NULL == fPoints && 0 != fFreeSpace)); |
| SkASSERT(!(NULL == fPoints && 0 != fFreeSpace)); |
| SkASSERT(!(NULL == fPoints && fPointCnt)); |
| SkASSERT(!(NULL == fVerbs && fVerbCnt)); |
| SkASSERT(this->currSize() == |
| fFreeSpace + sizeof(SkPoint) * fPointCnt + sizeof(uint8_t) * fVerbCnt); |
| } |
| |
| enum { |
| kMinSize = 256, |
| }; |
| |
| SkPoint* fPoints; // points to begining of the allocation |
| uint8_t* fVerbs; // points just past the end of the allocation (verbs grow backwards) |
| int fVerbCnt; |
| int fPointCnt; |
| size_t fFreeSpace; // redundant but saves computation |
| enum { |
| kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs. |
| }; |
| mutable int32_t fGenerationID; |
| SkDEBUGCODE_X(int32_t fEditorsAttached;) // assert that only one editor in use at any time. |
| |
| #if SK_DEBUG_PATH_REF |
| SkTDArray<SkPath*> fOwners; |
| #endif |
| |
| typedef SkRefCnt INHERITED; |
| }; |
| |
| SK_DEFINE_INST_COUNT(SkPathRef); |
| |
| #endif |