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
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index adebada..6738a0e 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -80,7 +80,7 @@
 
     /** 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 8df7633..4d99fdd 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -26,6 +26,13 @@
 */
 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 @@
      *
      * @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 @@
         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 @@
     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 7b662b0..0002b59 100644
--- a/include/core/SkPathRef.h
+++ b/include/core/SkPathRef.h
@@ -100,9 +100,13 @@
          */
         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 @@
      *
      * @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 @@
 
 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 @@
         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 @@
      */
     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 @@
 
     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 fbda7e8..03bc317 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -1161,7 +1161,7 @@
             this->close();
 
             SkPathRef::Editor ed(&fPathRef);
-            ed.setIsRRect(isRRect);
+            ed.setIsRRect(isRRect, dir, startIndex % 8);
 
             SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
         }
@@ -1259,7 +1259,7 @@
 
     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 2fc2de9..74f3a22 100644
--- a/src/core/SkPathRef.cpp
+++ b/src/core/SkPathRef.cpp
@@ -56,6 +56,61 @@
     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 @@
     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 @@
     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 @@
     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 @@
     ref->fSegmentMask = segmentMask;
     ref->fIsOval = isOval;
     ref->fIsRRect = isRRect;
+    ref->fRRectOrOvalIsCCW = rrectOrOvalIsCCW;
+    ref->fRRectOrOvalStartIdx = rrectOrOvalStartIdx;
     return ref;
 }
 
@@ -253,7 +319,9 @@
     // 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 @@
     fSegmentMask = ref.fSegmentMask;
     fIsOval = ref.fIsOval;
     fIsRRect = ref.fIsRRect;
+    fRRectOrOvalIsCCW = ref.fRRectOrOvalIsCCW;
+    fRRectOrOvalStartIdx = ref.fRRectOrOvalStartIdx;
     SkDEBUGCODE(this->validate();)
 }
 
@@ -616,6 +686,16 @@
     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 dd6f41c..36e9307 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -2215,8 +2215,22 @@
     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 @@
                              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 @@
     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 @@
     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 @@
     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 @@
     // circle + empty (translate)
     path = circle;
     path.addPath(empty, translate);
+
     check_for_circle(reporter, path, true, SkPathPriv::AsFirstDirection(kCircleDir));
 
     // test reverseAddPath
@@ -3102,17 +3135,23 @@
     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 @@
     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 c6b1d9c..5b7d17d 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 @@
             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);
-    }
+    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);
+            {
+                // 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);
+    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);
+            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 };
+    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);
+            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);
+            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);
+            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);
-    rr.inset(20, 20, &rr2);
-    path_contains_rrect_check(reporter, rr);
+            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 @@
                               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);
-    }
+    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);
-    } 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, 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) {
+    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+        for (int start = 0; start < 8; ++start) {
+            //----
+            SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
 
-    //----
-    SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
+            SkRRect rr1;
+            rr1.setRect(rect);
+            path_contains_rrect_check(reporter, rr1, dir, start);
 
-    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, 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);
 
-    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, dir, start);
 
-    //----
-    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, 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);
 
-    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, dir, start);
 
-    //----
-    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, 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);
 
-    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_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 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);
+            }
 
-        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));
+    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);
+            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 @@
     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) {