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 | |
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
-rw-r--r-- | include/core/SkMatrix.h | 2 | ||||
-rw-r--r-- | include/core/SkPath.h | 44 | ||||
-rw-r--r-- | include/core/SkPathRef.h | 68 | ||||
-rw-r--r-- | src/core/SkPath.cpp | 4 | ||||
-rw-r--r-- | src/core/SkPathRef.cpp | 100 | ||||
-rw-r--r-- | tests/PathTest.cpp | 59 | ||||
-rw-r--r-- | tests/RRectInPathTest.cpp | 518 |
7 files changed, 531 insertions, 264 deletions
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h index adebada445..6738a0eb69 100644 --- a/include/core/SkMatrix.h +++ b/include/core/SkMatrix.h @@ -80,7 +80,7 @@ public: /** Returns true if will map a rectangle to another rectangle. This can be true if the matrix is identity, scale-only, or rotates a multiple of - 90 degrees. + 90 degrees, or mirrors in x or y. */ bool rectStaysRect() const { if (fTypeMask & kUnknown_Mask) { diff --git a/include/core/SkPath.h b/include/core/SkPath.h index 8df7633d42..4d99fdd0eb 100644 --- a/include/core/SkPath.h +++ b/include/core/SkPath.h @@ -26,6 +26,13 @@ class SkWStream; */ class SK_API SkPath { public: + enum Direction { + /** clockwise direction for adding closed contours */ + kCW_Direction, + /** counter-clockwise direction for adding closed contours */ + kCCW_Direction, + }; + SkPath(); SkPath(const SkPath&); ~SkPath(); @@ -166,24 +173,45 @@ public: * * @param rect returns the bounding rect of this oval. It's a circle * if the height and width are the same. - * + * @param dir is the oval CCW (or CW if false). + * @param start indicates where the contour starts on the oval (see + * SkPath::addOval for intepretation of the index). * @return true if this path is an oval. * Tracking whether a path is an oval is considered an * optimization for performance and so some paths that are in * fact ovals can report false. */ - bool isOval(SkRect* rect) const { return fPathRef->isOval(rect); } + bool isOval(SkRect* rect, Direction* dir = nullptr, + unsigned* start = nullptr) const { + bool isCCW; + bool result = fPathRef->isOval(rect, &isCCW, start); + if (dir && result) { + *dir = isCCW ? kCCW_Direction : kCW_Direction; + } + return result; + } /** Returns true if the path is a round rect. * * @param rrect Returns the bounding rect and radii of this round rect. + * @param dir is the rrect CCW (or CW if false). + * @param start indicates where the contour starts on the rrect (see + * SkPath::addRRect for intepretation of the index). * * @return true if this path is a round rect. * Tracking whether a path is a round rect is considered an * optimization for performance and so some paths that are in * fact round rects can report false. */ - bool isRRect(SkRRect* rrect) const { return fPathRef->isRRect(rrect); } + bool isRRect(SkRRect* rrect, Direction* dir = nullptr, + unsigned* start = nullptr) const { + bool isCCW; + bool result = fPathRef->isRRect(rrect, &isCCW, start); + if (dir && result) { + *dir = isCCW ? kCCW_Direction : kCW_Direction; + } + return result; + } /** Clear any lines and curves from the path, making it empty. This frees up internal storage associated with those segments. @@ -526,13 +554,6 @@ public: kLarge_ArcSize, }; - enum Direction { - /** clockwise direction for adding closed contours */ - kCW_Direction, - /** counter-clockwise direction for adding closed contours */ - kCCW_Direction, - }; - /** * Append an elliptical arc from the current point in the format used by SVG. * The center of the ellipse is computed to satisfy the constraints below. @@ -717,7 +738,8 @@ public: void addOval(const SkRect& oval, Direction dir, unsigned start); /** - * Add a closed circle contour to the path + * Add a closed circle contour to the path. The circle contour begins at + * the right-most point (as though 1 were passed to addOval's 'start' param). * * @param x The x-coordinate of the center of a circle to add as a * closed contour to the path diff --git a/include/core/SkPathRef.h b/include/core/SkPathRef.h index 7b662b0a73..0002b594b4 100644 --- a/include/core/SkPathRef.h +++ b/include/core/SkPathRef.h @@ -100,9 +100,13 @@ public: */ SkPathRef* pathRef() { return fPathRef; } - void setIsOval(bool isOval) { fPathRef->setIsOval(isOval); } + void setIsOval(bool isOval, bool isCCW, unsigned start) { + fPathRef->setIsOval(isOval, isCCW, start); + } - void setIsRRect(bool isRRect) { fPathRef->setIsRRect(isRRect); } + void setIsRRect(bool isRRect, bool isCCW, unsigned start) { + fPathRef->setIsRRect(isRRect, isCCW, start); + } void setBounds(const SkRect& rect) { fPathRef->setBounds(rect); } @@ -164,23 +168,42 @@ public: * * @param rect returns the bounding rect of this oval. It's a circle * if the height and width are the same. + * @param isCCW is the oval CCW (or CW if false). + * @param start indicates where the contour starts on the oval (see + * SkPath::addOval for intepretation of the index). * * @return true if this path is an oval. * Tracking whether a path is an oval is considered an * optimization for performance and so some paths that are in * fact ovals can report false. */ - bool isOval(SkRect* rect) const { - if (fIsOval && rect) { - *rect = this->getBounds(); + bool isOval(SkRect* rect, bool* isCCW, unsigned* start) const { + if (fIsOval) { + if (rect) { + *rect = this->getBounds(); + } + if (isCCW) { + *isCCW = SkToBool(fRRectOrOvalIsCCW); + } + if (start) { + *start = fRRectOrOvalStartIdx; + } } return SkToBool(fIsOval); } - bool isRRect(SkRRect* rrect) const { - if (fIsRRect && rrect) { - *rrect = this->getRRect(); + bool isRRect(SkRRect* rrect, bool* isCCW, unsigned* start) const { + if (fIsRRect) { + if (rrect) { + *rrect = this->getRRect(); + } + if (isCCW) { + *isCCW = SkToBool(fRRectOrOvalIsCCW); + } + if (start) { + *start = fRRectOrOvalStartIdx; + } } return SkToBool(fIsRRect); } @@ -292,10 +315,12 @@ public: private: enum SerializationOffsets { - kIsRRect_SerializationShift = 26, // requires 1 bit - kIsFinite_SerializationShift = 25, // requires 1 bit - kIsOval_SerializationShift = 24, // requires 1 bit - kSegmentMask_SerializationShift = 0 // requires 4 bits + kRRectOrOvalStartIdx_SerializationShift = 28, // requires 3 bits + kRRectOrOvalIsCCW_SerializationShift = 27, // requires 1 bit + kIsRRect_SerializationShift = 26, // requires 1 bit + kIsFinite_SerializationShift = 25, // requires 1 bit + kIsOval_SerializationShift = 24, // requires 1 bit + kSegmentMask_SerializationShift = 0 // requires 4 bits }; SkPathRef() { @@ -309,6 +334,9 @@ private: fSegmentMask = 0; fIsOval = false; fIsRRect = false; + // The next two values don't matter unless fIsOval or fIsRRect are true. + SkDEBUGCODE(fRRectOrOvalIsCCW = false); + SkDEBUGCODE(fRRectOrOvalStartIdx = 0xAC); SkDEBUGCODE(fEditorsAttached = 0;) SkDEBUGCODE(this->validate();) } @@ -454,9 +482,17 @@ private: */ friend SkPathRef* sk_create_empty_pathref(); - void setIsOval(bool isOval) { fIsOval = isOval; } + void setIsOval(bool isOval, bool isCCW, unsigned start) { + fIsOval = isOval; + fRRectOrOvalIsCCW = isCCW; + fRRectOrOvalStartIdx = start; + } - void setIsRRect(bool isRRect) { fIsRRect = isRRect; } + void setIsRRect(bool isRRect, bool isCCW, unsigned start) { + fIsRRect = isRRect; + fRRectOrOvalIsCCW = isCCW; + fRRectOrOvalStartIdx = start; + } // called only by the editor. Note that this is not a const function. SkPoint* getPoints() { @@ -499,6 +535,10 @@ private: SkBool8 fIsOval; SkBool8 fIsRRect; + // Both the circle and rrect special cases have a notion of direction and starting point + // The next two variables store that information for either. + SkBool8 fRRectOrOvalIsCCW; + uint8_t fRRectOrOvalStartIdx; uint8_t fSegmentMask; friend class PathRefTest_Private; diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp index fbda7e8772..03bc3170d8 100644 --- a/src/core/SkPath.cpp +++ b/src/core/SkPath.cpp @@ -1161,7 +1161,7 @@ void SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex) this->close(); SkPathRef::Editor ed(&fPathRef); - ed.setIsRRect(isRRect); + ed.setIsRRect(isRRect, dir, startIndex % 8); SkASSERT(this->countVerbs() == initialVerbCount + kVerbs); } @@ -1259,7 +1259,7 @@ void SkPath::addOval(const SkRect &oval, Direction dir, unsigned startPointIndex SkPathRef::Editor ed(&fPathRef); - ed.setIsOval(isOval); + ed.setIsOval(isOval, kCCW_Direction == dir, startPointIndex % 4); } void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) { 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) { diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp index dd6f41cbfa..36e93074ef 100644 --- a/tests/PathTest.cpp +++ b/tests/PathTest.cpp @@ -2215,8 +2215,22 @@ static void write_and_read_back(skiatest::Reporter* reporter, REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() == p.getConvexityOrUnknown()); + SkRect oval0, oval1; + SkPath::Direction dir0, dir1; + unsigned start0, start1; REPORTER_ASSERT(reporter, readBack.isOval(nullptr) == p.isOval(nullptr)); - + if (p.isOval(&oval0, &dir0, &start0) && readBack.isOval(&oval1, &dir1, &start1)) { + REPORTER_ASSERT(reporter, oval0 == oval1); + REPORTER_ASSERT(reporter, dir0 == dir1); + REPORTER_ASSERT(reporter, start0 == start1); + } + REPORTER_ASSERT(reporter, readBack.isRRect(nullptr) == p.isRRect(nullptr)); + SkRRect rrect0, rrect1; + if (p.isRRect(&rrect0, &dir0, &start0) && readBack.isRRect(&rrect1, &dir1, &start1)) { + REPORTER_ASSERT(reporter, rrect0 == rrect1); + REPORTER_ASSERT(reporter, dir0 == dir1); + REPORTER_ASSERT(reporter, start0 == start1); + } const SkRect& origBounds = p.getBounds(); const SkRect& readBackBounds = readBack.getBounds(); @@ -2890,11 +2904,16 @@ static void check_for_circle(skiatest::Reporter* reporter, SkPathPriv::FirstDirection expectedDir) { SkRect rect = SkRect::MakeEmpty(); REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle); - REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, expectedDir)); - - if (expectedCircle) { + SkPath::Direction isOvalDir; + unsigned isOvalStart; + if (path.isOval(&rect, &isOvalDir, &isOvalStart)) { REPORTER_ASSERT(reporter, rect.height() == rect.width()); + REPORTER_ASSERT(reporter, SkPathPriv::AsFirstDirection(isOvalDir) == expectedDir); + SkPath tmpPath; + tmpPath.addOval(rect, isOvalDir, isOvalStart); + REPORTER_ASSERT(reporter, path == tmpPath); } + REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, expectedDir)); } static void test_circle_skew(skiatest::Reporter* reporter, @@ -2963,14 +2982,12 @@ static void test_circle_mirror_x(skiatest::Reporter* reporter, m.reset(); m.setScaleX(-SK_Scalar1); path.transform(m, &tmp); - if (SkPathPriv::kCW_FirstDirection == dir) { dir = SkPathPriv::kCCW_FirstDirection; } else { REPORTER_ASSERT(reporter, SkPathPriv::kCCW_FirstDirection == dir); dir = SkPathPriv::kCW_FirstDirection; } - check_for_circle(reporter, tmp, true, dir); } @@ -3018,6 +3035,9 @@ static void test_circle_with_direction(skiatest::Reporter* reporter, test_circle_rotate(reporter, path, dir); test_circle_translate(reporter, path, dir); test_circle_skew(reporter, path, dir); + test_circle_mirror_x(reporter, path, dir); + test_circle_mirror_y(reporter, path, dir); + test_circle_mirror_xy(reporter, path, dir); // circle at an offset at (10, 10) path.reset(); @@ -3031,6 +3051,18 @@ static void test_circle_with_direction(skiatest::Reporter* reporter, test_circle_mirror_x(reporter, path, dir); test_circle_mirror_y(reporter, path, dir); test_circle_mirror_xy(reporter, path, dir); + + // Try different starting points for the contour. + for (unsigned start = 0; start < 4; ++start) { + path.reset(); + path.addOval(SkRect::MakeXYWH(20, 10, 5, 5), inDir, start); + test_circle_rotate(reporter, path, dir); + test_circle_translate(reporter, path, dir); + test_circle_skew(reporter, path, dir); + test_circle_mirror_x(reporter, path, dir); + test_circle_mirror_y(reporter, path, dir); + test_circle_mirror_xy(reporter, path, dir); + } } static void test_circle_with_add_paths(skiatest::Reporter* reporter) { @@ -3060,6 +3092,7 @@ static void test_circle_with_add_paths(skiatest::Reporter* reporter) { // circle + empty (translate) path = circle; path.addPath(empty, translate); + check_for_circle(reporter, path, true, SkPathPriv::AsFirstDirection(kCircleDir)); // test reverseAddPath @@ -3102,17 +3135,23 @@ static void test_oval(skiatest::Reporter* reporter) { SkRect rect; SkMatrix m; SkPath path; + unsigned start = 0; + SkPath::Direction dir = SkPath::kCCW_Direction; rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50)); path.addOval(rect); + // Defaults to dir = CW and start = 1 REPORTER_ASSERT(reporter, path.isOval(nullptr)); m.setRotate(SkIntToScalar(90)); SkPath tmp; path.transform(m, &tmp); - // an oval rotated 90 degrees is still an oval. - REPORTER_ASSERT(reporter, tmp.isOval(nullptr)); + // an oval rotated 90 degrees is still an oval. The start index changes from 1 to 2. Direction + // is unchanged. + REPORTER_ASSERT(reporter, tmp.isOval(nullptr, &dir, &start)); + REPORTER_ASSERT(reporter, 2 == start); + REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir); m.reset(); m.setRotate(SkIntToScalar(30)); @@ -3150,7 +3189,9 @@ static void test_oval(skiatest::Reporter* reporter) { tmp.reset(); tmp.addOval(rect); path = tmp; - REPORTER_ASSERT(reporter, path.isOval(nullptr)); + REPORTER_ASSERT(reporter, path.isOval(nullptr, &dir, &start)); + REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir); + REPORTER_ASSERT(reporter, 1 == start); } static void test_empty(skiatest::Reporter* reporter, const SkPath& p) { diff --git a/tests/RRectInPathTest.cpp b/tests/RRectInPathTest.cpp index c6b1d9c15e..5b7d17d9ce 100644 --- a/tests/RRectInPathTest.cpp +++ b/tests/RRectInPathTest.cpp @@ -8,24 +8,40 @@ #include "SkMatrix.h" #include "SkPath.h" #include "SkPathRef.h" -#include "SkPathOps.h" #include "SkRRect.h" #include "Test.h" -static SkRRect path_contains_rrect(skiatest::Reporter* reporter, const SkPath& path) { +static SkRRect path_contains_rrect(skiatest::Reporter* reporter, const SkPath& path, + SkPath::Direction* dir, unsigned* start) { SkRRect out; - REPORTER_ASSERT(reporter, path.isRRect(&out)); - SkPath path2, xorBoth; - path2.addRRect(out); - if (path == path2) { - return out; + REPORTER_ASSERT(reporter, path.isRRect(&out, dir, start)); + SkPath recreatedPath; + recreatedPath.addRRect(out, *dir, *start); + REPORTER_ASSERT(reporter, path == recreatedPath); + // Test that rotations/mirrors of the rrect path are still rrect paths and the returned + // parameters for the transformed paths are correct. + static const SkMatrix kMatrices[] = { + SkMatrix::MakeScale(1, 1), + SkMatrix::MakeScale(-1, 1), + SkMatrix::MakeScale(1, -1), + SkMatrix::MakeScale(-1, -1), + }; + for (auto& m : kMatrices) { + SkPath xformed; + path.transform(m, &xformed); + SkRRect xrr = SkRRect::MakeRect(SkRect::MakeEmpty()); + SkPath::Direction xd = SkPath::kCCW_Direction; + unsigned xs = ~0U; + REPORTER_ASSERT(reporter, xformed.isRRect(&xrr, &xd, &xs)); + recreatedPath.reset(); + recreatedPath.addRRect(xrr, xd, xs); + REPORTER_ASSERT(reporter, recreatedPath == xformed); } - Op(path, path2, SkPathOp::kXOR_SkPathOp, &xorBoth); - REPORTER_ASSERT(reporter, xorBoth.isEmpty()); return out; } -static SkRRect inner_path_contains_rrect(skiatest::Reporter* reporter, const SkRRect& in) { +static SkRRect inner_path_contains_rrect(skiatest::Reporter* reporter, const SkRRect& in, + SkPath::Direction dir, unsigned start) { switch (in.getType()) { case SkRRect::kEmpty_Type: case SkRRect::kRect_Type: @@ -35,157 +51,192 @@ static SkRRect inner_path_contains_rrect(skiatest::Reporter* reporter, const SkR break; } SkPath path; - path.addRRect(in); - return path_contains_rrect(reporter, path); + path.addRRect(in, dir, start); + SkPath::Direction outDir; + unsigned outStart; + SkRRect rrect = path_contains_rrect(reporter, path, &outDir, &outStart); + REPORTER_ASSERT(reporter, outDir == dir && outStart == start); + return rrect; } -static void path_contains_rrect_check(skiatest::Reporter* reporter, const SkRRect& in) { - SkRRect out = inner_path_contains_rrect(reporter, in); +static void path_contains_rrect_check(skiatest::Reporter* reporter, const SkRRect& in, + SkPath::Direction dir, unsigned start) { + SkRRect out = inner_path_contains_rrect(reporter, in, dir, start); if (in != out) { SkDebugf(""); } REPORTER_ASSERT(reporter, in == out); } -static void path_contains_rrect_nocheck(skiatest::Reporter* reporter, const SkRRect& in) { - SkRRect out = inner_path_contains_rrect(reporter, in); +static void path_contains_rrect_nocheck(skiatest::Reporter* reporter, const SkRRect& in, + SkPath::Direction dir, unsigned start) { + SkRRect out = inner_path_contains_rrect(reporter, in, dir, start); if (in == out) { SkDebugf(""); } } static void path_contains_rrect_check(skiatest::Reporter* reporter, const SkRect& r, - SkVector v[4]) { + SkVector v[4], SkPath::Direction dir, unsigned start) { SkRRect rrect; rrect.setRectRadii(r, v); - path_contains_rrect_check(reporter, rrect); + path_contains_rrect_check(reporter, rrect, dir, start); } class ForceIsRRect_Private { public: - ForceIsRRect_Private(SkPath* path) { - path->fPathRef->setIsRRect(true); + ForceIsRRect_Private(SkPath* path, SkPath::Direction dir, unsigned start) { + path->fPathRef->setIsRRect(true, dir == SkPath::kCCW_Direction, start); } }; -static void force_path_contains_rrect(skiatest::Reporter* reporter, SkPath& path) { - ForceIsRRect_Private force_rrect(&path); - path_contains_rrect(reporter, path); +static void force_path_contains_rrect(skiatest::Reporter* reporter, SkPath& path, + SkPath::Direction dir, unsigned start) { + ForceIsRRect_Private force_rrect(&path, dir, start); + SkPath::Direction outDir; + unsigned outStart; + path_contains_rrect(reporter, path, &outDir, &outStart); + REPORTER_ASSERT(reporter, outDir == dir && outStart == start); } static void test_undetected_paths(skiatest::Reporter* reporter) { + // We use a dummy path to get the exact conic weight used by SkPath for a circular arc. This + // allows our local, hand-crafted, artisanal round rect paths below to exactly match the + // factory made corporate paths produced by SkPath. + SkPath dummyPath; + dummyPath.addCircle(0, 0, 10); + SkPath::RawIter iter(dummyPath); + SkPoint dummyPts[4]; + SkPath::Verb v = iter.next(dummyPts); + REPORTER_ASSERT(reporter, SkPath::kMove_Verb == v); + v = iter.next(dummyPts); + REPORTER_ASSERT(reporter, SkPath::kConic_Verb == v); + const SkScalar weight = iter.conicWeight(); + SkPath path; path.moveTo(0, 62.5f); path.lineTo(0, 3.5f); - path.conicTo(0, 0, 3.5f, 0, 0.70710677f); + path.conicTo(0, 0, 3.5f, 0, weight); path.lineTo(196.5f, 0); - path.conicTo(200, 0, 200, 3.5f, 0.70710677f); + path.conicTo(200, 0, 200, 3.5f, weight); path.lineTo(200, 62.5f); - path.conicTo(200, 66, 196.5f, 66, 0.70710677f); + path.conicTo(200, 66, 196.5f, 66, weight); path.lineTo(3.5f, 66); - path.conicTo(0, 66, 0, 62.5, 0.70710677f); + path.conicTo(0, 66, 0, 62.5, weight); path.close(); - force_path_contains_rrect(reporter, path); + force_path_contains_rrect(reporter, path, SkPath::kCW_Direction, 6); path.reset(); path.moveTo(0, 81.5f); path.lineTo(0, 3.5f); - path.conicTo(0, 0, 3.5f, 0, 0.70710677f); + path.conicTo(0, 0, 3.5f, 0, weight); path.lineTo(149.5, 0); - path.conicTo(153, 0, 153, 3.5f, 0.70710677f); + path.conicTo(153, 0, 153, 3.5f, weight); path.lineTo(153, 81.5f); - path.conicTo(153, 85, 149.5f, 85, 0.70710677f); + path.conicTo(153, 85, 149.5f, 85, weight); path.lineTo(3.5f, 85); - path.conicTo(0, 85, 0, 81.5f, 0.70710677f); + path.conicTo(0, 85, 0, 81.5f, weight); path.close(); - force_path_contains_rrect(reporter, path); + force_path_contains_rrect(reporter, path, SkPath::kCW_Direction, 6); path.reset(); path.moveTo(14, 1189); path.lineTo(14, 21); - path.conicTo(14, 14, 21, 14, 0.70710677f); + path.conicTo(14, 14, 21, 14, weight); path.lineTo(1363, 14); - path.conicTo(1370, 14, 1370, 21, 0.70710677f); + path.conicTo(1370, 14, 1370, 21, weight); path.lineTo(1370, 1189); - path.conicTo(1370, 1196, 1363, 1196, 0.70710677f); + path.conicTo(1370, 1196, 1363, 1196, weight); path.lineTo(21, 1196); - path.conicTo(14, 1196, 14, 1189, 0.70710677f); + path.conicTo(14, 1196, 14, 1189, weight); path.close(); - force_path_contains_rrect(reporter, path); + force_path_contains_rrect(reporter, path, SkPath::kCW_Direction, 6); path.reset(); path.moveTo(14, 1743); path.lineTo(14, 21); - path.conicTo(14, 14, 21, 14, 0.70710677f); + path.conicTo(14, 14, 21, 14, weight); path.lineTo(1363, 14); - path.conicTo(1370, 14, 1370, 21, 0.70710677f); + path.conicTo(1370, 14, 1370, 21, weight); path.lineTo(1370, 1743); - path.conicTo(1370, 1750, 1363, 1750, 0.70710677f); + path.conicTo(1370, 1750, 1363, 1750, weight); path.lineTo(21, 1750); - path.conicTo(14, 1750, 14, 1743, 0.70710677f); + path.conicTo(14, 1750, 14, 1743, weight); path.close(); - force_path_contains_rrect(reporter, path); + force_path_contains_rrect(reporter, path, SkPath::kCW_Direction, 6); } static const SkScalar kWidth = 100.0f; static const SkScalar kHeight = 100.0f; static void test_tricky_radii(skiatest::Reporter* reporter) { - { - // crbug.com/458522 - SkRRect rr; - const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 }; - const SkScalar rad = 12814; - const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } }; - rr.setRectRadii(bounds, vec); - path_contains_rrect_check(reporter, rr); - } - - { - // crbug.com//463920 - SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0); - SkVector radii[4] = { - { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f } - }; - SkRRect rr; - rr.setRectRadii(r, radii); - path_contains_rrect_nocheck(reporter, rr); + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + { + // crbug.com/458522 + SkRRect rr; + const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 }; + const SkScalar rad = 12814; + const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } }; + rr.setRectRadii(bounds, vec); + path_contains_rrect_check(reporter, rr, dir, start); + } + + { + // crbug.com//463920 + SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0); + SkVector radii[4] = { + { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f } + }; + SkRRect rr; + rr.setRectRadii(r, radii); + path_contains_rrect_nocheck(reporter, rr, dir, start); + } + } } } static void test_empty_crbug_458524(skiatest::Reporter* reporter) { - SkRRect rr; - const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 }; - const SkScalar rad = 40; - rr.setRectXY(bounds, rad, rad); - path_contains_rrect_check(reporter, rr); - - SkRRect other; - SkMatrix matrix; - matrix.setScale(0, 1); - rr.transform(matrix, &other); - path_contains_rrect_check(reporter, rr); + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + SkRRect rr; + const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 }; + const SkScalar rad = 40; + rr.setRectXY(bounds, rad, rad); + path_contains_rrect_check(reporter, rr, dir, start); + + SkRRect other; + SkMatrix matrix; + matrix.setScale(0, 1); + rr.transform(matrix, &other); + path_contains_rrect_check(reporter, rr, dir, start); + } + } } static void test_inset(skiatest::Reporter* reporter) { - SkRRect rr, rr2; - SkRect r = { 0, 0, 100, 100 }; - - rr.setRect(r); - rr.inset(-20, -20, &rr2); - path_contains_rrect_check(reporter, rr); - - rr.inset(20, 20, &rr2); - path_contains_rrect_check(reporter, rr); - - rr.inset(r.width()/2, r.height()/2, &rr2); - path_contains_rrect_check(reporter, rr); - - rr.setRectXY(r, 20, 20); - rr.inset(19, 19, &rr2); - path_contains_rrect_check(reporter, rr); - rr.inset(20, 20, &rr2); - path_contains_rrect_check(reporter, rr); + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + SkRRect rr, rr2; + SkRect r = { 0, 0, 100, 100 }; + + rr.setRect(r); + rr.inset(-20, -20, &rr2); + path_contains_rrect_check(reporter, rr, dir, start); + + rr.inset(20, 20, &rr2); + path_contains_rrect_check(reporter, rr, dir, start); + + rr.inset(r.width()/2, r.height()/2, &rr2); + path_contains_rrect_check(reporter, rr, dir, start); + + rr.setRectXY(r, 20, 20); + rr.inset(19, 19, &rr2); + path_contains_rrect_check(reporter, rr, dir, start); + rr.inset(20, 20, &rr2); + path_contains_rrect_check(reporter, rr, dir, start); + } + } } @@ -193,152 +244,177 @@ static void test_9patch_rrect(skiatest::Reporter* reporter, const SkRect& rect, SkScalar l, SkScalar t, SkScalar r, SkScalar b, bool checkRadii) { - SkRRect rr; - rr.setNinePatch(rect, l, t, r, b); - if (checkRadii) { - path_contains_rrect_check(reporter, rr); - } else { - path_contains_rrect_nocheck(reporter, rr); - } - - SkRRect rr2; // construct the same RR using the most general set function - SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } }; - rr2.setRectRadii(rect, radii); - if (checkRadii) { - path_contains_rrect_check(reporter, rr); - } else { - path_contains_rrect_nocheck(reporter, rr); + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + SkRRect rr; + rr.setNinePatch(rect, l, t, r, b); + if (checkRadii) { + path_contains_rrect_check(reporter, rr, dir, start); + } else { + path_contains_rrect_nocheck(reporter, rr, dir, start); + } + + SkRRect rr2; // construct the same RR using the most general set function + SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } }; + rr2.setRectRadii(rect, radii); + if (checkRadii) { + path_contains_rrect_check(reporter, rr, dir, start); + } else { + path_contains_rrect_nocheck(reporter, rr, dir, start); + } + } } } // Test out the basic API entry points static void test_round_rect_basic(skiatest::Reporter* reporter) { - - //---- - SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); - - SkRRect rr1; - rr1.setRect(rect); - path_contains_rrect_check(reporter, rr1); - - SkRRect rr1_2; // construct the same RR using the most general set function - SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; - rr1_2.setRectRadii(rect, rr1_2_radii); - path_contains_rrect_check(reporter, rr1_2); - SkRRect rr1_3; // construct the same RR using the nine patch set function - rr1_3.setNinePatch(rect, 0, 0, 0, 0); - path_contains_rrect_check(reporter, rr1_2); - - //---- - SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) }; - SkRRect rr2; - rr2.setOval(rect); - path_contains_rrect_check(reporter, rr2); - - SkRRect rr2_2; // construct the same RR using the most general set function - SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY }, - { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY } }; - rr2_2.setRectRadii(rect, rr2_2_radii); - path_contains_rrect_check(reporter, rr2_2); - SkRRect rr2_3; // construct the same RR using the nine patch set function - rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY); - path_contains_rrect_check(reporter, rr2_3); - - //---- - SkPoint p = { 5, 5 }; - SkRRect rr3; - rr3.setRectXY(rect, p.fX, p.fY); - path_contains_rrect_check(reporter, rr3); - - SkRRect rr3_2; // construct the same RR using the most general set function - SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } }; - rr3_2.setRectRadii(rect, rr3_2_radii); - path_contains_rrect_check(reporter, rr3_2); - SkRRect rr3_3; // construct the same RR using the nine patch set function - rr3_3.setNinePatch(rect, 5, 5, 5, 5); - path_contains_rrect_check(reporter, rr3_3); - - //---- - test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true); - - { - // Test out the rrect from skia:3466 - SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f); - - test_9patch_rrect(reporter, - rect2, - 0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f, - false); + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + //---- + SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); + + SkRRect rr1; + rr1.setRect(rect); + path_contains_rrect_check(reporter, rr1, dir, start); + + SkRRect rr1_2; // construct the same RR using the most general set function + SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; + rr1_2.setRectRadii(rect, rr1_2_radii); + path_contains_rrect_check(reporter, rr1_2, dir, start); + SkRRect rr1_3; // construct the same RR using the nine patch set function + rr1_3.setNinePatch(rect, 0, 0, 0, 0); + path_contains_rrect_check(reporter, rr1_2, dir, start); + + //---- + SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) }; + SkRRect rr2; + rr2.setOval(rect); + path_contains_rrect_check(reporter, rr2, dir, start); + + SkRRect rr2_2; // construct the same RR using the most general set function + SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, + { halfPoint.fX, halfPoint.fY }, + { halfPoint.fX, halfPoint.fY }, + { halfPoint.fX, halfPoint.fY } }; + rr2_2.setRectRadii(rect, rr2_2_radii); + path_contains_rrect_check(reporter, rr2_2, dir, start); + SkRRect rr2_3; // construct the same RR using the nine patch set function + rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY); + path_contains_rrect_check(reporter, rr2_3, dir, start); + + //---- + SkPoint p = { 5, 5 }; + SkRRect rr3; + rr3.setRectXY(rect, p.fX, p.fY); + path_contains_rrect_check(reporter, rr3, dir, start); + + SkRRect rr3_2; // construct the same RR using the most general set function + SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } }; + rr3_2.setRectRadii(rect, rr3_2_radii); + path_contains_rrect_check(reporter, rr3_2, dir, start); + SkRRect rr3_3; // construct the same RR using the nine patch set function + rr3_3.setNinePatch(rect, 5, 5, 5, 5); + path_contains_rrect_check(reporter, rr3_3, dir, start); + + //---- + test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true); + + { + // Test out the rrect from skia:3466 + SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, + 0.806214333f); + + test_9patch_rrect(reporter, + rect2, + 0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f, + false); + } + + //---- + SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } }; + + SkRRect rr5; + rr5.setRectRadii(rect, radii2); + path_contains_rrect_check(reporter, rr5, dir, start); + } } - - //---- - SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } }; - - SkRRect rr5; - rr5.setRectRadii(rect, radii2); - path_contains_rrect_check(reporter, rr5); } // Test out the cases when the RR degenerates to a rect static void test_round_rect_rects(skiatest::Reporter* reporter) { + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + //---- + SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); + SkRRect rr1; + rr1.setRectXY(rect, 0, 0); - //---- - SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); - SkRRect rr1; - rr1.setRectXY(rect, 0, 0); + path_contains_rrect_check(reporter, rr1, dir, start); - path_contains_rrect_check(reporter, rr1); + //---- + SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; - //---- - SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; + SkRRect rr2; + rr2.setRectRadii(rect, radii); - SkRRect rr2; - rr2.setRectRadii(rect, radii); + path_contains_rrect_check(reporter, rr2, dir, start); - path_contains_rrect_check(reporter, rr2); + //---- + SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } }; - //---- - SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } }; - - SkRRect rr3; - rr3.setRectRadii(rect, radii2); - path_contains_rrect_check(reporter, rr3); + SkRRect rr3; + rr3.setRectRadii(rect, radii2); + path_contains_rrect_check(reporter, rr3, dir, start); + } + } } // Test out the cases when the RR degenerates to an oval static void test_round_rect_ovals(skiatest::Reporter* reporter) { - //---- - SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); - SkRRect rr1; - rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight)); - - path_contains_rrect_check(reporter, rr1); + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + //---- + SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); + SkRRect rr1; + rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight)); + + path_contains_rrect_check(reporter, rr1, dir, start); + } + } } // Test out the non-degenerate RR cases static void test_round_rect_general(skiatest::Reporter* reporter) { - //---- - SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); - SkRRect rr1; - rr1.setRectXY(rect, 20, 20); + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + //---- + SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); + SkRRect rr1; + rr1.setRectXY(rect, 20, 20); - path_contains_rrect_check(reporter, rr1); + path_contains_rrect_check(reporter, rr1, dir, start); - //---- - SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } }; + //---- + SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } }; - SkRRect rr2; - rr2.setRectRadii(rect, radii); + SkRRect rr2; + rr2.setRectRadii(rect, radii); - path_contains_rrect_check(reporter, rr2); + path_contains_rrect_check(reporter, rr2, dir, start); + } + } } static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) { - SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); - SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } }; - SkRRect rr1; - rr1.setRectRadii(rect, radii); - path_contains_rrect_nocheck(reporter, rr1); + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); + SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } }; + SkRRect rr1; + rr1.setRectRadii(rect, radii); + path_contains_rrect_nocheck(reporter, rr1, dir, start); + } + } } static void set_radii(SkVector radii[4], int index, float rad) { @@ -356,21 +432,29 @@ static void test_skbug_3239(skiatest::Reporter* reporter) { const SkRect rectx = SkRect::MakeLTRB(min, min, max, big); const SkRect recty = SkRect::MakeLTRB(min, min, big, max); - SkVector radii[4]; - for (int i = 0; i < 4; ++i) { - set_radii(radii, i, rad); - path_contains_rrect_check(reporter, rectx, radii); - path_contains_rrect_check(reporter, recty, radii); + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + SkVector radii[4]; + for (int i = 0; i < 4; ++i) { + set_radii(radii, i, rad); + path_contains_rrect_check(reporter, rectx, radii, dir, start); + path_contains_rrect_check(reporter, recty, radii, dir, start); + } + } } } static void test_mix(skiatest::Reporter* reporter) { - // Test out mixed degenerate and non-degenerate geometry with Conics - const SkVector radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 100, 100 } }; - SkRect r = SkRect::MakeWH(100, 100); - SkRRect rr; - rr.setRectRadii(r, radii); - path_contains_rrect_check(reporter, rr); + for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) { + for (int start = 0; start < 8; ++start) { + // Test out mixed degenerate and non-degenerate geometry with Conics + const SkVector radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 100, 100 } }; + SkRect r = SkRect::MakeWH(100, 100); + SkRRect rr; + rr.setRectRadii(r, radii); + path_contains_rrect_check(reporter, rr, dir, start); + } + } } DEF_TEST(RoundRectInPath, reporter) { |