Index: src/core/SkPathRef.cpp |
diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp |
index 2fc2de935c149031342cdcdc538535bf50dcb49b..74f3a220ce538bbe5e07873161ed0acf9acd4214 100644 |
--- a/src/core/SkPathRef.cpp |
+++ b/src/core/SkPathRef.cpp |
@@ -56,6 +56,61 @@ SkPathRef* SkPathRef::CreateEmpty() { |
return SkRef(gEmpty); |
} |
+static void transform_dir_and_start(const SkMatrix& matrix, bool isRRect, bool* isCCW, |
+ unsigned* start) { |
+ int inStart = *start; |
+ int rm = 0; |
+ if (isRRect) { |
+ // Degenerate rrect indices to oval indices and remember the remainder. |
+ // Ovals have one index per side whereas rrects have two. |
+ rm = inStart & 0b1; |
+ inStart /= 2; |
+ } |
+ // Is the antidiagonal non-zero (otherwise the diagonal is zero) |
+ int antiDiag; |
+ // Is the non-zero value in the top row (either kMScaleX or kMSkewX) negative |
+ int topNeg; |
+ // Are the two non-zero diagonal or antidiagonal values the same sign. |
+ int sameSign; |
+ if (matrix.get(SkMatrix::kMScaleX) != 0) { |
+ antiDiag = 0b00; |
+ if (matrix.get(SkMatrix::kMScaleX) > 0) { |
+ topNeg = 0b00; |
+ sameSign = matrix.get(SkMatrix::kMScaleY) > 0 ? 0b01 : 0b00; |
+ } else { |
+ topNeg = 0b10; |
+ sameSign = matrix.get(SkMatrix::kMScaleY) > 0 ? 0b00 : 0b01; |
+ } |
+ } else { |
+ antiDiag = 0b01; |
+ if (matrix.get(SkMatrix::kMSkewX) > 0) { |
+ topNeg = 0b00; |
+ sameSign = matrix.get(SkMatrix::kMSkewY) > 0 ? 0b01 : 0b00; |
+ } else { |
+ topNeg = 0b10; |
+ sameSign = matrix.get(SkMatrix::kMSkewY) > 0 ? 0b00 : 0b01; |
+ } |
+ } |
+ if (sameSign != antiDiag) { |
+ // This is a rotation (and maybe scale). The direction is unchanged. |
+ // Trust me on the start computation (or draw yourself some pictures) |
+ *start = (inStart + 4 - (topNeg | antiDiag)) % 4; |
+ SkASSERT(*start < 4); |
+ if (isRRect) { |
+ *start = 2 * *start + rm; |
+ } |
+ } else { |
+ // This is a mirror (and maybe scale). The direction is reversed. |
+ *isCCW = !*isCCW; |
+ // Trust me on the start computation (or draw yourself some pictures) |
+ *start = (6 + (topNeg | antiDiag) - inStart) % 4; |
+ SkASSERT(*start < 4); |
+ if (isRRect) { |
+ *start = 2 * *start + (rm ? 0 : 1); |
+ } |
+ } |
+} |
+ |
void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst, |
const SkPathRef& src, |
const SkMatrix& matrix) { |
@@ -90,15 +145,15 @@ void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst, |
matrix.mapPoints((*dst)->fPoints, src.points(), src.fPointCnt); |
/* |
- * Here we optimize the bounds computation, by noting if the bounds are |
- * already known, and if so, we just transform those as well and mark |
- * them as "known", rather than force the transformed path to have to |
- * recompute them. |
- * |
- * Special gotchas if the path is effectively empty (<= 1 point) or |
- * if it is non-finite. In those cases bounds need to stay empty, |
- * regardless of the matrix. |
- */ |
+ * Here we optimize the bounds computation, by noting if the bounds are |
+ * already known, and if so, we just transform those as well and mark |
+ * them as "known", rather than force the transformed path to have to |
+ * recompute them. |
+ * |
+ * Special gotchas if the path is effectively empty (<= 1 point) or |
+ * if it is non-finite. In those cases bounds need to stay empty, |
+ * regardless of the matrix. |
+ */ |
if (canXformBounds) { |
(*dst)->fBoundsIsDirty = false; |
if (src.fIsFinite) { |
@@ -120,6 +175,13 @@ void SkPathRef::CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst, |
bool rectStaysRect = matrix.rectStaysRect(); |
(*dst)->fIsOval = src.fIsOval && rectStaysRect; |
(*dst)->fIsRRect = src.fIsRRect && rectStaysRect; |
+ if ((*dst)->fIsOval || (*dst)->fIsRRect) { |
+ unsigned start = src.fRRectOrOvalStartIdx; |
+ bool isCCW = SkToBool(src.fRRectOrOvalIsCCW); |
+ transform_dir_and_start(matrix, (*dst)->fIsRRect, &isCCW, &start); |
+ (*dst)->fRRectOrOvalIsCCW = isCCW; |
+ (*dst)->fRRectOrOvalStartIdx = start; |
+ } |
SkDEBUGCODE((*dst)->validate();) |
} |
@@ -137,6 +199,8 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer) { |
uint8_t segmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF; |
bool isOval = (packed >> kIsOval_SerializationShift) & 1; |
bool isRRect = (packed >> kIsRRect_SerializationShift) & 1; |
+ bool rrectOrOvalIsCCW = (packed >> kRRectOrOvalIsCCW_SerializationShift) & 1; |
+ unsigned rrectOrOvalStartIdx = (packed >> kRRectOrOvalStartIdx_SerializationShift) & 0x7; |
int32_t verbCount, pointCount, conicCount; |
ptrdiff_t maxPtrDiff = std::numeric_limits<ptrdiff_t>::max(); |
@@ -173,6 +237,8 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer) { |
ref->fSegmentMask = segmentMask; |
ref->fIsOval = isOval; |
ref->fIsRRect = isRRect; |
+ ref->fRRectOrOvalIsCCW = rrectOrOvalIsCCW; |
+ ref->fRRectOrOvalStartIdx = rrectOrOvalStartIdx; |
return ref; |
} |
@@ -253,7 +319,9 @@ void SkPathRef::writeToBuffer(SkWBuffer* buffer) const { |
// and fIsFinite are computed. |
const SkRect& bounds = this->getBounds(); |
- int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift) | |
+ int32_t packed = ((fRRectOrOvalStartIdx & 7) << kRRectOrOvalStartIdx_SerializationShift) | |
+ ((fRRectOrOvalIsCCW & 1) << kRRectOrOvalIsCCW_SerializationShift) | |
+ ((fIsFinite & 1) << kIsFinite_SerializationShift) | |
((fIsOval & 1) << kIsOval_SerializationShift) | |
((fIsRRect & 1) << kIsRRect_SerializationShift) | |
(fSegmentMask << kSegmentMask_SerializationShift); |
@@ -298,6 +366,8 @@ void SkPathRef::copy(const SkPathRef& ref, |
fSegmentMask = ref.fSegmentMask; |
fIsOval = ref.fIsOval; |
fIsRRect = ref.fIsRRect; |
+ fRRectOrOvalIsCCW = ref.fRRectOrOvalIsCCW; |
+ fRRectOrOvalStartIdx = ref.fRRectOrOvalStartIdx; |
SkDEBUGCODE(this->validate();) |
} |
@@ -616,6 +686,16 @@ void SkPathRef::validate() const { |
SkASSERT(this->currSize() == |
fFreeSpace + sizeof(SkPoint) * fPointCnt + sizeof(uint8_t) * fVerbCnt); |
+ if (fIsOval || fIsRRect) { |
+ // Currently we don't allow both of these to be set, even though ovals are round rects. |
+ SkASSERT(fIsOval != fIsRRect); |
+ if (fIsOval) { |
+ SkASSERT(fRRectOrOvalStartIdx < 4); |
+ } else { |
+ SkASSERT(fRRectOrOvalStartIdx < 8); |
+ } |
+ } |
+ |
if (!fBoundsIsDirty && !fBounds.isEmpty()) { |
bool isFinite = true; |
for (int i = 0; i < fPointCnt; ++i) { |