aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkPathRef.cpp
diff options
context:
space:
mode:
authorGravatar bsalomon <bsalomon@google.com>2016-05-27 09:17:04 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-05-27 09:17:04 -0700
commit78d58d1084f0390c1c0f9123ac6e48efcd226f39 (patch)
tree8ac2f64454d922b0b4ae8eca262171f2dcae9eb5 /src/core/SkPathRef.cpp
parente5a6cd9832eaa7011dee162283ff6470f82a3fdc (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.cpp100
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) {