Chromium Code Reviews| Index: src/core/SkPathRef.cpp |
| diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp |
| index 2fc2de935c149031342cdcdc538535bf50dcb49b..d09494c0e32d05b4635568c7ff06122179d7a40c 100644 |
| --- a/src/core/SkPathRef.cpp |
| +++ b/src/core/SkPathRef.cpp |
| @@ -56,6 +56,60 @@ 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. |
|
robertphillips
2016/05/26 15:57:58
// The oval indices mark the cardinal directions w
bsalomon
2016/05/26 18:25:43
Done (sort of)
|
| + 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 +144,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 +174,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 +198,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 +236,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 +318,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 +365,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 +685,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) { |