From 6b8dbb668f1f069270d35a47cfe98decd059c625 Mon Sep 17 00:00:00 2001 From: "robertphillips@google.com" Date: Thu, 12 Dec 2013 23:03:51 +0000 Subject: [PATCH] Move segment mask from SkPath to SkPathRef https://codereview.chromium.org/105083003/ git-svn-id: http://skia.googlecode.com/svn/trunk@12660 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/core/SkPath.h | 17 +++-- include/core/SkPathRef.h | 67 +++++++++-------- src/core/SkPath.cpp | 99 +++++++------------------ src/core/SkPathRef.cpp | 156 ++++++++++++++++++++++++++++++++++++--- tests/PathTest.cpp | 73 ++++++++++++++++++ 5 files changed, 284 insertions(+), 128 deletions(-) diff --git a/include/core/SkPath.h b/include/core/SkPath.h index 0b2ea61b9..1526461f9 100644 --- a/include/core/SkPath.h +++ b/include/core/SkPath.h @@ -83,7 +83,7 @@ public: */ void toggleInverseFillType() { fFillType ^= 2; - } + } enum Convexity { kUnknown_Convexity, @@ -446,8 +446,8 @@ public: @param dy3 The amount to add to the y-coordinate of the last point on this contour, to specify the end point of a cubic curve */ - void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, - SkScalar x3, SkScalar y3); + void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, + SkScalar x3, SkScalar y3); /** Append the specified arc to the path as a new contour. If the start of the path is different from the path's current last point, then an @@ -461,8 +461,8 @@ public: treated mod 360. @param forceMoveTo If true, always begin a new contour with the arc */ - void arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, - bool forceMoveTo); + void arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, + bool forceMoveTo); /** Append a line and arc to the current path. This is the same as the PostScript call "arct". @@ -778,7 +778,7 @@ public: * set if the path contains 1 or more segments of that type. * Returns 0 for an empty path (no segments). */ - uint32_t getSegmentMasks() const { return fSegmentMask; } + uint32_t getSegmentMasks() const { return fPathRef->getSegmentMasks(); } enum Verb { kMove_Verb, //!< iter.next returns 1 point @@ -942,14 +942,15 @@ private: #endif kConvexity_SerializationShift = 16, // requires 8 bits kFillType_SerializationShift = 8, // requires 8 bits - kSegmentMask_SerializationShift = 0 // requires 4 bits +#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO + kOldSegmentMask_SerializationShift = 0 // requires 4 bits +#endif }; SkAutoTUnref fPathRef; int fLastMoveToIndex; uint8_t fFillType; - uint8_t fSegmentMask; mutable uint8_t fConvexity; mutable uint8_t fDirection; #ifdef SK_BUILD_FOR_ANDROID diff --git a/include/core/SkPathRef.h b/include/core/SkPathRef.h index 5d55c72ce..99fd90a9c 100644 --- a/include/core/SkPathRef.h +++ b/include/core/SkPathRef.h @@ -62,30 +62,24 @@ public: /** * 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. + * 'weight' is only used if 'verb' is kConic_Verb */ - SkPoint* growForVerb(int /*SkPath::Verb*/ verb) { + SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight = 0) { SkDEBUGCODE(fPathRef->validate();) - return fPathRef->growForVerb(verb); + return fPathRef->growForVerb(verb, weight); } - SkPoint* growForConic(SkScalar w); - /** - * 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 [~]). pts points - * at the first new point (indexed normally []). + * Allocates space for multiple instances of a particular verb and the + * requisite points & weights. + * The return pointer points at the first new point (indexed normally []). + * If 'verb' is kConic_Verb, 'weights' will return a pointer to the + * space for the conic weights (indexed normally). */ - void grow(int newVerbs, int newPts, uint8_t** verbs, SkPoint** pts) { - SkASSERT(NULL != verbs); - SkASSERT(NULL != pts); - SkDEBUGCODE(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; - SkDEBUGCODE(fPathRef->validate();) + SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, + int numVbs, + SkScalar** weights = NULL) { + return fPathRef->growForRepeatedVerb(verb, numVbs, weights); } /** @@ -123,6 +117,13 @@ public: return SkToBool(fIsFinite); } + /** + * Returns a mask, where each bit corresponding to a SegmentMask is + * set if the path contains 1 or more segments of that type. + * Returns 0 for an empty path (no segments). + */ + uint32_t getSegmentMasks() const { return fSegmentMask; } + /** Returns true if the path is an oval. * * @param rect returns the bounding rect of this oval. It's a circle @@ -199,6 +200,7 @@ public: int countPoints() const { SkDEBUGCODE(this->validate();) return fPointCnt; } int countVerbs() const { SkDEBUGCODE(this->validate();) return fVerbCnt; } + int countWeights() const { SkDEBUGCODE(this->validate();) return fConicWeights.count(); } /** * Returns a pointer one beyond the first logical verb (last verb in memory order). @@ -226,7 +228,7 @@ public: /** * Convenience methods for getting to a verb or point by index. */ - uint8_t atVerb(int index) { + uint8_t atVerb(int index) const { SkASSERT((unsigned) index < (unsigned) fVerbCnt); return this->verbs()[~index]; } @@ -240,12 +242,12 @@ public: /** * Writes the path points and verbs to a buffer. */ - void writeToBuffer(SkWBuffer* buffer); + void writeToBuffer(SkWBuffer* buffer) const; /** * Gets the number of bytes that would be written in writeBuffer() */ - uint32_t writeSize(); + uint32_t writeSize() const; /** * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the @@ -258,6 +260,7 @@ private: enum SerializationOffsets { kIsFinite_SerializationShift = 25, // requires 1 bit kIsOval_SerializationShift = 24, // requires 1 bit + kSegmentMask_SerializationShift = 0 // requires 4 bits }; SkPathRef() { @@ -268,6 +271,7 @@ private: fPoints = NULL; fFreeSpace = 0; fGenerationID = kEmptyGenID; + fSegmentMask = 0; fIsOval = false; SkDEBUGCODE(fEditorsAttached = 0;) SkDEBUGCODE(this->validate();) @@ -311,6 +315,7 @@ private: fBoundsIsDirty = true; // this also invalidates fIsFinite fGenerationID = 0; + fSegmentMask = 0; fIsOval = false; size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount; @@ -340,26 +345,19 @@ private: } /** - * Increases the verb count by newVerbs and the point count be newPoints. New verbs and points - * are uninitialized. + * Increases the verb count by numVbs and point count by the required amount. + * The new points are uninitialized. All the new verbs are set to the specified + * verb. If 'verb' is kConic_Verb, 'weights' will return a pointer to the + * uninitialized conic weights. */ - void grow(int newVerbs, int newPoints) { - SkDEBUGCODE(this->validate();) - size_t space = newVerbs * sizeof(uint8_t) + newPoints * sizeof (SkPoint); - this->makeSpace(space); - fVerbCnt += newVerbs; - fPointCnt += newPoints; - fFreeSpace -= space; - fBoundsIsDirty = true; // this also invalidates fIsFinite - SkDEBUGCODE(this->validate();) - } + SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, int numVbs, SkScalar** weights); /** * 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(int /*SkPath::Verb*/ verb); + SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight); /** * Ensures that the free space available in the path ref is >= size. The verb and point counts @@ -425,6 +423,7 @@ private: }; mutable SkRect fBounds; + uint8_t fSegmentMask; mutable uint8_t fBoundsIsDirty; mutable SkBool8 fIsFinite; // only meaningful if bounds are valid mutable SkBool8 fIsOval; diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp index eaa6c93de..af8b1aa56 100644 --- a/src/core/SkPath.cpp +++ b/src/core/SkPath.cpp @@ -15,11 +15,6 @@ #include "SkRRect.h" #include "SkThread.h" -// This value is just made-up for now. When count is 4, calling memset was much -// slower than just writing the loop. This seems odd, and hopefully in the -// future this we appear to have been a fluke... -#define MIN_COUNT_FOR_MEMSET_TO_BE_FAST 16 - //////////////////////////////////////////////////////////////////////////// /** @@ -143,7 +138,6 @@ void SkPath::resetFields() { //fPathRef is assumed to have been emptied by the caller. fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE; fFillType = kWinding_FillType; - fSegmentMask = 0; fConvexity = kUnknown_Convexity; fDirection = kUnknown_Direction; @@ -182,7 +176,6 @@ void SkPath::copyFields(const SkPath& that) { //fPathRef is assumed to have been set by the caller. fLastMoveToIndex = that.fLastMoveToIndex; fFillType = that.fFillType; - fSegmentMask = that.fSegmentMask; fConvexity = that.fConvexity; fDirection = that.fDirection; } @@ -190,14 +183,8 @@ void SkPath::copyFields(const SkPath& that) { bool operator==(const SkPath& a, const SkPath& b) { // note: don't need to look at isConvex or bounds, since just comparing the // raw data is sufficient. - - // We explicitly check fSegmentMask as a quick-reject. We could skip it, - // since it is only a cache of info in the fVerbs, but its a fast way to - // notice a difference - return &a == &b || - (a.fFillType == b.fFillType && a.fSegmentMask == b.fSegmentMask && - *a.fPathRef.get() == *b.fPathRef.get()); + (a.fFillType == b.fFillType && *a.fPathRef.get() == *b.fPathRef.get()); } void SkPath::swap(SkPath& that) { @@ -207,7 +194,6 @@ void SkPath::swap(SkPath& that) { fPathRef.swap(&that.fPathRef); SkTSwap(fLastMoveToIndex, that.fLastMoveToIndex); SkTSwap(fFillType, that.fFillType); - SkTSwap(fSegmentMask, that.fSegmentMask); SkTSwap(fConvexity, that.fConvexity); SkTSwap(fDirection, that.fDirection); #ifdef SK_BUILD_FOR_ANDROID @@ -674,7 +660,6 @@ void SkPath::lineTo(SkScalar x, SkScalar y) { SkPathRef::Editor ed(&fPathRef); ed.growForVerb(kLine_Verb)->set(x, y); - fSegmentMask |= kLine_SegmentMask; DIRTY_AFTER_EDIT; } @@ -695,7 +680,6 @@ void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { SkPoint* pts = ed.growForVerb(kQuad_Verb); pts[0].set(x1, y1); pts[1].set(x2, y2); - fSegmentMask |= kQuad_SegmentMask; DIRTY_AFTER_EDIT; } @@ -723,10 +707,9 @@ void SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, this->injectMoveToIfNeeded(); SkPathRef::Editor ed(&fPathRef); - SkPoint* pts = ed.growForConic(w); + SkPoint* pts = ed.growForVerb(kConic_Verb, w); pts[0].set(x1, y1); pts[1].set(x2, y2); - fSegmentMask |= kConic_SegmentMask; DIRTY_AFTER_EDIT; } @@ -751,7 +734,6 @@ void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, pts[0].set(x1, y1); pts[1].set(x2, y2); pts[2].set(x3, y3); - fSegmentMask |= kCubic_SegmentMask; DIRTY_AFTER_EDIT; } @@ -838,29 +820,19 @@ void SkPath::addPoly(const SkPoint pts[], int count, bool close) { return; } - SkPathRef::Editor ed(&fPathRef); - fLastMoveToIndex = ed.pathRef()->countPoints(); - uint8_t* vb; - SkPoint* p; - // +close makes room for the extra kClose_Verb - ed.grow(count + close, count, &vb, &p); + fLastMoveToIndex = fPathRef->countPoints(); - memcpy(p, pts, count * sizeof(SkPoint)); - vb[~0] = kMove_Verb; + // +close makes room for the extra kClose_Verb + SkPathRef::Editor ed(&fPathRef, count+close, count); + + ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY); if (count > 1) { - // cast to unsigned, so if MIN_COUNT_FOR_MEMSET_TO_BE_FAST is defined to - // be 0, the compiler will remove the test/branch entirely. - if ((unsigned)count >= MIN_COUNT_FOR_MEMSET_TO_BE_FAST) { - memset(vb - count, kLine_Verb, count - 1); - } else { - for (int i = 1; i < count; ++i) { - vb[~i] = kLine_Verb; - } - } - fSegmentMask |= kLine_SegmentMask; + SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1); + memcpy(p, &pts[1], (count-1) * sizeof(SkPoint)); } + if (close) { - vb[~count] = kClose_Verb; + ed.growForVerb(kClose_Verb); } DIRTY_AFTER_EDIT; @@ -1343,11 +1315,21 @@ void SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle SkPoint pts[kSkBuildQuadArcStorage]; int count = build_arc_points(oval, startAngle, sweepAngle, pts); - this->incReserve(count); - this->moveTo(pts[0]); - for (int i = 1; i < count; i += 2) { - this->quadTo(pts[i], pts[i+1]); + SkDEBUGCODE(this->validate();) + SkASSERT(count & 1); + + fLastMoveToIndex = fPathRef->countPoints(); + + SkPathRef::Editor ed(&fPathRef, 1+(count-1)/2, count); + + ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY); + if (count > 1) { + SkPoint* p = ed.growForRepeatedVerb(kQuad_Verb, (count-1)/2); + memcpy(p, &pts[1], (count-1) * sizeof(SkPoint)); } + + DIRTY_AFTER_EDIT; + SkDEBUGCODE(this->validate();) } /* @@ -1671,7 +1653,6 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { if (this != dst) { dst->fFillType = fFillType; - dst->fSegmentMask = fSegmentMask; dst->fConvexity = fConvexity; } @@ -2045,7 +2026,6 @@ size_t SkPath::writeToMemory(void* storage) const { int32_t packed = (fConvexity << kConvexity_SerializationShift) | (fFillType << kFillType_SerializationShift) | - (fSegmentMask << kSegmentMask_SerializationShift) | (fDirection << kDirection_SerializationShift) #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO | (0x1 << kNewFormat_SerializationShift) @@ -2070,7 +2050,6 @@ size_t SkPath::readFromMemory(const void* storage, size_t length) { fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF; fFillType = (packed >> kFillType_SerializationShift) & 0xFF; - fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF; fDirection = (packed >> kDirection_SerializationShift) & 0x3; #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO bool newFormat = (packed >> kNewFormat_SerializationShift) & 1; @@ -2201,34 +2180,6 @@ void SkPath::validate() const { } } } - - uint32_t mask = 0; - const uint8_t* verbs = const_cast(fPathRef.get())->verbs(); - for (int i = 0; i < fPathRef->countVerbs(); i++) { - switch (verbs[~i]) { - case kLine_Verb: - mask |= kLine_SegmentMask; - break; - case kQuad_Verb: - mask |= kQuad_SegmentMask; - break; - case kConic_Verb: - mask |= kConic_SegmentMask; - break; - case kCubic_Verb: - mask |= kCubic_SegmentMask; - case kMove_Verb: // these verbs aren't included in the segment mask. - case kClose_Verb: - break; - case kDone_Verb: - SkDEBUGFAIL("Done verb shouldn't be recorded."); - break; - default: - SkDEBUGFAIL("Unknown Verb"); - break; - } - } - SkASSERT(mask == fSegmentMask); #endif // SK_DEBUG_PATH } #endif // SK_DEBUG diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp index a02df3024..a57e2f479 100644 --- a/src/core/SkPathRef.cpp +++ b/src/core/SkPathRef.cpp @@ -28,13 +28,6 @@ SkPathRef::Editor::Editor(SkAutoTUnref* pathRef, SkDEBUGCODE(sk_atomic_inc(&fPathRef->fEditorsAttached);) } -SkPoint* SkPathRef::Editor::growForConic(SkScalar w) { - SkDEBUGCODE(fPathRef->validate();) - SkPoint* pts = fPathRef->growForVerb(SkPath::kConic_Verb); - *fPathRef->fConicWeights.append() = w; - return pts; -} - ////////////////////////////////////////////////////////////////////////////// void SkPathRef::CreateEmptyImpl(SkPathRef** empty) { *empty = SkNEW(SkPathRef); @@ -105,6 +98,8 @@ void SkPathRef::CreateTransformedCopy(SkAutoTUnref* dst, (*dst)->fBoundsIsDirty = true; } + (*dst)->fSegmentMask = src.fSegmentMask; + // It's an oval only if it stays a rect. (*dst)->fIsOval = src.fIsOval && matrix.rectStaysRect(); @@ -118,6 +113,7 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer ) { SkPathRef* ref = SkNEW(SkPathRef); bool isOval; + uint8_t segmentMask; int32_t packed; if (!buffer->readS32(&packed)) { @@ -130,9 +126,11 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO if (newFormat) { #endif + segmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF; isOval = (packed >> kIsOval_SerializationShift) & 1; #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO } else { + segmentMask = (oldPacked >> SkPath::kOldSegmentMask_SerializationShift) & 0xF; isOval = (oldPacked >> SkPath::kOldIsOval_SerializationShift) & 1; } #endif @@ -159,6 +157,9 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer return NULL; } ref->fBoundsIsDirty = false; + + // resetToSize clears fSegmentMask and fIsOval + ref->fSegmentMask = segmentMask; ref->fIsOval = isOval; return ref; } @@ -172,6 +173,7 @@ void SkPathRef::Rewind(SkAutoTUnref* pathRef) { (*pathRef)->fFreeSpace = (*pathRef)->currSize(); (*pathRef)->fGenerationID = 0; (*pathRef)->fConicWeights.rewind(); + (*pathRef)->fSegmentMask = 0; (*pathRef)->fIsOval = false; SkDEBUGCODE((*pathRef)->validate();) } else { @@ -185,6 +187,14 @@ void SkPathRef::Rewind(SkAutoTUnref* pathRef) { bool SkPathRef::operator== (const SkPathRef& ref) const { SkDEBUGCODE(this->validate();) SkDEBUGCODE(ref.validate();) + + // We explicitly check fSegmentMask as a quick-reject. We could skip it, + // since it is only a cache of info in the fVerbs, but its a fast way to + // notice a difference + if (fSegmentMask != ref.fSegmentMask) { + return false; + } + bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID; #ifdef SK_RELEASE if (genIDMatch) { @@ -222,7 +232,7 @@ bool SkPathRef::operator== (const SkPathRef& ref) const { return true; } -void SkPathRef::writeToBuffer(SkWBuffer* buffer) { +void SkPathRef::writeToBuffer(SkWBuffer* buffer) const { SkDEBUGCODE(this->validate();) SkDEBUGCODE(size_t beforePos = buffer->pos();) @@ -231,7 +241,8 @@ void SkPathRef::writeToBuffer(SkWBuffer* buffer) { const SkRect& bounds = this->getBounds(); int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift) | - ((fIsOval & 1) << kIsOval_SerializationShift); + ((fIsOval & 1) << kIsOval_SerializationShift) | + (fSegmentMask << kSegmentMask_SerializationShift); buffer->write32(packed); // TODO: write gen ID here. Problem: We don't know if we're cross process or not from @@ -248,7 +259,7 @@ void SkPathRef::writeToBuffer(SkWBuffer* buffer) { SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize()); } -uint32_t SkPathRef::writeSize() { +uint32_t SkPathRef::writeSize() const { return uint32_t(5 * sizeof(uint32_t) + fVerbCnt * sizeof(uint8_t) + fPointCnt * sizeof(SkPoint) + @@ -273,11 +284,91 @@ void SkPathRef::copy(const SkPathRef& ref, fBounds = ref.fBounds; fIsFinite = ref.fIsFinite; } + fSegmentMask = ref.fSegmentMask; fIsOval = ref.fIsOval; SkDEBUGCODE(this->validate();) } -SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) { +SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb, + int numVbs, + SkScalar** weights) { + // This value is just made-up for now. When count is 4, calling memset was much + // slower than just writing the loop. This seems odd, and hopefully in the + // future this will appear to have been a fluke... + static const unsigned int kMIN_COUNT_FOR_MEMSET_TO_BE_FAST = 16; + + SkDEBUGCODE(this->validate();) + int pCnt; + bool dirtyAfterEdit = true; + switch (verb) { + case SkPath::kMove_Verb: + pCnt = numVbs; + dirtyAfterEdit = false; + break; + case SkPath::kLine_Verb: + fSegmentMask |= SkPath::kLine_SegmentMask; + pCnt = numVbs; + break; + case SkPath::kQuad_Verb: + fSegmentMask |= SkPath::kQuad_SegmentMask; + pCnt = 2 * numVbs; + break; + case SkPath::kConic_Verb: + fSegmentMask |= SkPath::kConic_SegmentMask; + pCnt = 2 * numVbs; + break; + case SkPath::kCubic_Verb: + fSegmentMask |= SkPath::kCubic_SegmentMask; + pCnt = 3 * numVbs; + break; + case SkPath::kClose_Verb: + SkDEBUGFAIL("growForRepeatedVerb called for kClose_Verb"); + pCnt = 0; + dirtyAfterEdit = false; + break; + case SkPath::kDone_Verb: + SkDEBUGFAIL("growForRepeatedVerb called for kDone"); + // fall through + default: + SkDEBUGFAIL("default should not be reached"); + pCnt = 0; + dirtyAfterEdit = false; + } + + size_t space = numVbs * sizeof(uint8_t) + pCnt * sizeof (SkPoint); + this->makeSpace(space); + + SkPoint* ret = fPoints + fPointCnt; + uint8_t* vb = fVerbs - fVerbCnt; + + // cast to unsigned, so if kMIN_COUNT_FOR_MEMSET_TO_BE_FAST is defined to + // be 0, the compiler will remove the test/branch entirely. + if ((unsigned)numVbs >= kMIN_COUNT_FOR_MEMSET_TO_BE_FAST) { + memset(vb - numVbs, verb, numVbs); + } else { + for (int i = 0; i < numVbs; ++i) { + vb[~i] = verb; + } + } + + fVerbCnt += numVbs; + fPointCnt += pCnt; + fFreeSpace -= space; + fBoundsIsDirty = true; // this also invalidates fIsFinite + if (dirtyAfterEdit) { + fIsOval = false; + } + + if (SkPath::kConic_Verb == verb) { + SkASSERT(NULL != weights); + *weights = fConicWeights.append(numVbs); + } + + SkDEBUGCODE(this->validate();) + return ret; +} + +SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb, SkScalar weight) { SkDEBUGCODE(this->validate();) int pCnt; bool dirtyAfterEdit = true; @@ -287,14 +378,19 @@ SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) { dirtyAfterEdit = false; break; case SkPath::kLine_Verb: + fSegmentMask |= SkPath::kLine_SegmentMask; pCnt = 1; break; case SkPath::kQuad_Verb: - // fall through + fSegmentMask |= SkPath::kQuad_SegmentMask; + pCnt = 2; + break; case SkPath::kConic_Verb: + fSegmentMask |= SkPath::kConic_SegmentMask; pCnt = 2; break; case SkPath::kCubic_Verb: + fSegmentMask |= SkPath::kCubic_SegmentMask; pCnt = 3; break; case SkPath::kClose_Verb: @@ -320,6 +416,11 @@ SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) { if (dirtyAfterEdit) { fIsOval = false; } + + if (SkPath::kConic_Verb == verb) { + *fConicWeights.append() = weight; + } + SkDEBUGCODE(this->validate();) return ret; } @@ -369,5 +470,36 @@ void SkPathRef::validate() const { } SkASSERT(SkToBool(fIsFinite) == isFinite); } + +#ifdef SK_DEBUG_PATH + uint32_t mask = 0; + for (int i = 0; i < fVerbCnt; ++i) { + switch (fVerbs[~i]) { + case SkPath::kMove_Verb: + break; + case SkPath::kLine_Verb: + mask |= SkPath::kLine_SegmentMask; + break; + case SkPath::kQuad_Verb: + mask |= SkPath::kQuad_SegmentMask; + break; + case SkPath::kConic_Verb: + mask |= SkPath::kConic_SegmentMask; + break; + case SkPath::kCubic_Verb: + mask |= SkPath::kCubic_SegmentMask; + break; + case SkPath::kClose_Verb: + break; + case SkPath::kDone_Verb: + SkDEBUGFAIL("Done verb shouldn't be recorded."); + break; + default: + SkDEBUGFAIL("Unknown Verb"); + break; + } + } + SkASSERT(mask == fSegmentMask); +#endif // SK_DEBUG_PATH } #endif diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp index bea3ea5ab..991b4fd36 100644 --- a/tests/PathTest.cpp +++ b/tests/PathTest.cpp @@ -3138,6 +3138,78 @@ static void test_contains(skiatest::Reporter* reporter) { } } +static void test_pathref(skiatest::Reporter* reporter) { + static const int kRepeatCnt = 10; + + SkPathRef* pathRef = SkPathRef::CreateEmpty(); + SkAutoTUnref pathRef2(SkPathRef::CreateEmpty()); + SkMatrix mat; + + mat.setTranslate(10, 10); + + SkPathRef::CreateTransformedCopy(&pathRef2, *pathRef, mat); + + SkPathRef::Editor ed(&pathRef2); + + { + ed.growForRepeatedVerb(SkPath::kMove_Verb, kRepeatCnt); + REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countVerbs()); + REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countPoints()); + REPORTER_ASSERT(reporter, 0 == pathRef2->getSegmentMasks()); + for (int i = 0; i < kRepeatCnt; ++i) { + REPORTER_ASSERT(reporter, SkPath::kMove_Verb == pathRef2->atVerb(i)); + } + ed.resetToSize(0, 0, 0); + } + + { + ed.growForRepeatedVerb(SkPath::kLine_Verb, kRepeatCnt); + REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countVerbs()); + REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countPoints()); + REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == pathRef2->getSegmentMasks()); + for (int i = 0; i < kRepeatCnt; ++i) { + REPORTER_ASSERT(reporter, SkPath::kLine_Verb == pathRef2->atVerb(i)); + } + ed.resetToSize(0, 0, 0); + } + + { + ed.growForRepeatedVerb(SkPath::kQuad_Verb, kRepeatCnt); + REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countVerbs()); + REPORTER_ASSERT(reporter, 2*kRepeatCnt == pathRef2->countPoints()); + REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == pathRef2->getSegmentMasks()); + for (int i = 0; i < kRepeatCnt; ++i) { + REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == pathRef2->atVerb(i)); + } + ed.resetToSize(0, 0, 0); + } + + { + SkScalar* weights = NULL; + ed.growForRepeatedVerb(SkPath::kConic_Verb, kRepeatCnt, &weights); + REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countVerbs()); + REPORTER_ASSERT(reporter, 2*kRepeatCnt == pathRef2->countPoints()); + REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countWeights()); + REPORTER_ASSERT(reporter, SkPath::kConic_SegmentMask == pathRef2->getSegmentMasks()); + REPORTER_ASSERT(reporter, NULL != weights); + for (int i = 0; i < kRepeatCnt; ++i) { + REPORTER_ASSERT(reporter, SkPath::kConic_Verb == pathRef2->atVerb(i)); + } + ed.resetToSize(0, 0, 0); + } + + { + ed.growForRepeatedVerb(SkPath::kCubic_Verb, kRepeatCnt); + REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countVerbs()); + REPORTER_ASSERT(reporter, 3*kRepeatCnt == pathRef2->countPoints()); + REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == pathRef2->getSegmentMasks()); + for (int i = 0; i < kRepeatCnt; ++i) { + REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == pathRef2->atVerb(i)); + } + ed.resetToSize(0, 0, 0); + } +} + static void test_operatorEqual(skiatest::Reporter* reporter) { SkPath a; SkPath b; @@ -3297,5 +3369,6 @@ DEF_TEST(Path, reporter) { test_conicTo_special_case(reporter); test_get_point(reporter); test_contains(reporter); + test_pathref(reporter); PathTest_Private::TestPathTo(reporter); }