| 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) {
|
|
|