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
diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp
index a02df30..a57e2f4 100644
--- a/src/core/SkPathRef.cpp
+++ b/src/core/SkPathRef.cpp
@@ -28,13 +28,6 @@
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 @@
(*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* ref = SkNEW(SkPathRef);
bool isOval;
+ uint8_t segmentMask;
int32_t packed;
if (!buffer->readS32(&packed)) {
@@ -130,9 +126,11 @@
#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 @@
return NULL;
}
ref->fBoundsIsDirty = false;
+
+ // resetToSize clears fSegmentMask and fIsOval
+ ref->fSegmentMask = segmentMask;
ref->fIsOval = isOval;
return ref;
}
@@ -172,6 +173,7 @@
(*pathRef)->fFreeSpace = (*pathRef)->currSize();
(*pathRef)->fGenerationID = 0;
(*pathRef)->fConicWeights.rewind();
+ (*pathRef)->fSegmentMask = 0;
(*pathRef)->fIsOval = false;
SkDEBUGCODE((*pathRef)->validate();)
} else {
@@ -185,6 +187,14 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
if (dirtyAfterEdit) {
fIsOval = false;
}
+
+ if (SkPath::kConic_Verb == verb) {
+ *fConicWeights.append() = weight;
+ }
+
SkDEBUGCODE(this->validate();)
return ret;
}
@@ -369,5 +470,36 @@
}
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