diff options
author | bsalomon <bsalomon@google.com> | 2016-05-27 09:17:04 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-05-27 09:17:04 -0700 |
commit | 78d58d1084f0390c1c0f9123ac6e48efcd226f39 (patch) | |
tree | 8ac2f64454d922b0b4ae8eca262171f2dcae9eb5 /src/core/SkPathRef.cpp | |
parent | e5a6cd9832eaa7011dee162283ff6470f82a3fdc (diff) |
Make SkPath::isOval() and SkPath::isRRect return the orientation and starting index.
These are tracked in SkPathRef.
Unit tests are updated to test that the returned values are correct.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2012233002
Review-Url: https://codereview.chromium.org/2012233002
Diffstat (limited to 'src/core/SkPathRef.cpp')
-rw-r--r-- | src/core/SkPathRef.cpp | 100 |
1 files changed, 90 insertions, 10 deletions
diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp index 2fc2de935c..74f3a220ce 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) { |