Use pathbuilder or factories to keep path immutable

Change-Id: I57b0bdcc9e4da67e11ae10efaf48c839a1b6b230
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1061317
Reviewed-by: Kaylee Lubick <kjlubick@google.com>
Reviewed-by: Daniel Dilan <danieldilan@google.com>
Commit-Queue: Mike Reed <mike@reedtribe.org>
Commit-Queue: Kaylee Lubick <kjlubick@google.com>
diff --git a/modules/skottie/src/text/Font.cpp b/modules/skottie/src/text/Font.cpp
index 80537c7..cdcab02 100644
--- a/modules/skottie/src/text/Font.cpp
+++ b/modules/skottie/src/text/Font.cpp
@@ -9,6 +9,7 @@
 
 #include "include/core/SkMatrix.h"
 #include "include/core/SkPath.h"
+#include "include/core/SkPathBuilder.h"
 #include "include/core/SkRect.h"
 #include "include/core/SkSize.h"
 #include "include/core/SkTypeface.h"
@@ -83,7 +84,7 @@
         return false;
     }
 
-    path.transform(SkMatrix::Scale(kPtScale, kPtScale));
+    path = path.makeTransform(SkMatrix::Scale(kPtScale, kPtScale));
 
     fCustomBuilder.setGlyph(glyph_id, advance, path);
 
@@ -117,6 +118,7 @@
         return true;
     }
 
+    SkPathBuilder builder;
     for (const skjson::ObjectValue* jgrp : *jshapes) {
         if (!jgrp) {
             return false;
@@ -142,9 +144,10 @@
                 return false;
             }
 
-            path->addPath(path_node->getPath());
+            builder.addPath(path_node->getPath());
         }
     }
+    *path = builder.detach();
 
     return true;
 }
diff --git a/modules/skottie/src/text/TextAdapter.cpp b/modules/skottie/src/text/TextAdapter.cpp
index 73197ce..949fd09 100644
--- a/modules/skottie/src/text/TextAdapter.cpp
+++ b/modules/skottie/src/text/TextAdapter.cpp
@@ -41,6 +41,7 @@
 #include "modules/sksg/include/SkSGTransform.h"
 #include "modules/sksg/src/SkSGTransformPriv.h"
 #include "modules/skshaper/include/SkShaper_factory.h"
+#include "src/core/SkPathPriv.h"
 
 #include <algorithm>
 #include <cmath>
@@ -211,9 +212,7 @@
             // reinitialize cached contour data
             auto path = static_cast<SkPath>(fPath);
             if (reverse) {
-                SkPath reversed;
-                reversed.reverseAddPath(path);
-                path = reversed;
+                path = SkPathPriv::ReversePath(path);
             }
 
             SkContourMeasureIter iter(path, /*forceClosed = */false);
diff --git a/modules/skparagraph/src/Decorations.cpp b/modules/skparagraph/src/Decorations.cpp
index 0e33508..2ab7d1c 100644
--- a/modules/skparagraph/src/Decorations.cpp
+++ b/modules/skparagraph/src/Decorations.cpp
@@ -51,7 +51,7 @@
         switch (textStyle.getDecorationStyle()) {
           case TextDecorationStyle::kWavy: {
               calculateWaves(textStyle, context.clip);
-              fPath.offset(x, y);
+              fPath = fPath.makeOffset(x, y);
               painter->drawPath(fPath, fDecorStyle);
               break;
           }
diff --git a/modules/skparagraph/src/ParagraphImpl.cpp b/modules/skparagraph/src/ParagraphImpl.cpp
index 7a4b8c1..14645d9 100644
--- a/modules/skparagraph/src/ParagraphImpl.cpp
+++ b/modules/skparagraph/src/ParagraphImpl.cpp
@@ -3,6 +3,7 @@
 #include "include/core/SkFontMetrics.h"
 #include "include/core/SkMatrix.h"
 #include "include/core/SkPath.h"
+#include "include/core/SkPathBuilder.h"
 #include "include/core/SkPictureRecorder.h"
 #include "include/core/SkSpan.h"
 #include "include/core/SkTypeface.h"
@@ -1445,6 +1446,7 @@
 }
 
 int ParagraphImpl::getPath(int lineNumber, SkPath* dest) {
+    SkPathBuilder builder;
     int notConverted = 0;
     auto& line = fLines[lineNumber];
     line.iterateThroughVisualRuns(
@@ -1470,13 +1472,12 @@
                                 line.offset().fY + correctedBaseline);
               SkRect rect = context.clip.makeOffset(offset);
               struct Rec {
-                  SkPath* fPath;
+                  SkPathBuilder* fBuilder;
                   SkPoint fOffset;
                   const SkPoint* fPos;
                   int fNotConverted;
               } rec =
-                  {dest, SkPoint::Make(rect.left(), rect.top()),
-                   &run->positions()[context.pos], 0};
+                  {&builder, SkPoint::Make(rect.left(), rect.top()), &run->positions()[context.pos], 0};
               font.getPaths({&run->glyphs()[context.pos], context.size},
                     [](const SkPath* path, const SkMatrix& mx, void* ctx) {
                         Rec* rec = reinterpret_cast<Rec*>(ctx);
@@ -1484,7 +1485,7 @@
                             SkMatrix total = mx;
                             total.postTranslate(rec->fPos->fX + rec->fOffset.fX,
                                                 rec->fPos->fY + rec->fOffset.fY);
-                            rec->fPath->addPath(*path, total);
+                            rec->fBuilder->addPath(*path, total);
                         } else {
                             rec->fNotConverted++;
                         }
@@ -1492,6 +1493,7 @@
                     }, &rec);
               notConverted += rec.fNotConverted;
           });
+        *dest = builder.detach();
         return true;
     });
 
@@ -1499,12 +1501,12 @@
 }
 
 SkPath Paragraph::GetPath(SkTextBlob* textBlob) {
-    SkPath path;
+    SkPathBuilder builder;
     SkTextBlobRunIterator iter(textBlob);
     while (!iter.done()) {
         SkFont font = iter.font();
-        struct Rec { SkPath* fDst; SkPoint fOffset; const SkPoint* fPos; } rec =
-            {&path, {textBlob->bounds().left(), textBlob->bounds().top()},
+        struct Rec { SkPathBuilder* fBuilder; SkPoint fOffset; const SkPoint* fPos; } rec =
+            {&builder, {textBlob->bounds().left(), textBlob->bounds().top()},
              iter.points()};
         font.getPaths({iter.glyphs(), iter.glyphCount()},
             [](const SkPath* src, const SkMatrix& mx, void* ctx) {
@@ -1513,14 +1515,14 @@
                     SkMatrix tmp(mx);
                     tmp.postTranslate(rec->fPos->fX - rec->fOffset.fX,
                                       rec->fPos->fY - rec->fOffset.fY);
-                    rec->fDst->addPath(*src, tmp);
+                    rec->fBuilder->addPath(*src, tmp);
                 }
                 rec->fPos += 1;
             },
             &rec);
         iter.next();
     }
-    return path;
+    return builder.detach();
 }
 
 bool ParagraphImpl::containsEmoji(SkTextBlob* textBlob) {
diff --git a/modules/sksg/src/SkSGMerge.cpp b/modules/sksg/src/SkSGMerge.cpp
index 4dd2dcb..b206c3c 100644
--- a/modules/sksg/src/SkSGMerge.cpp
+++ b/modules/sksg/src/SkSGMerge.cpp
@@ -71,23 +71,23 @@
     SkASSERT(this->hasInval());
 
     SkOpBuilder builder;
+    SkPathBuilder merger;
 
-    fMerged.reset();
     bool in_builder = false;
 
     auto append = [&](const SkPath& path) {
         if (in_builder) {
             if (auto result = builder.resolve()) {
-                fMerged = *result;
+                merger = *result;
             }
             in_builder = false;
         }
 
-        if (fMerged.isEmpty()) {
+        if (merger.isEmpty()) {
             // First merge path determines the fill type.
-            fMerged = path;
+            merger = path;
         } else {
-            fMerged.addPath(path);
+            merger.addPath(path);
         }
     };
 
@@ -101,7 +101,7 @@
         }
 
         if (!in_builder) {
-            builder.add(fMerged, kUnion_SkPathOp);
+            builder.add(fMerged.snapshot(), kUnion_SkPathOp);
             in_builder = true;
         }
 
@@ -110,10 +110,11 @@
 
     if (in_builder) {
         if (auto result = builder.resolve()) {
-            fMerged = *result;
+            merger = *result;
         }
     }
 
+    fMerged = merger.detach();
     SkPathPriv::ShrinkToFit(&fMerged);
 
     return fMerged.computeTightBounds();
diff --git a/modules/svg/src/SkSVGClipPath.cpp b/modules/svg/src/SkSVGClipPath.cpp
index 90c7fc7..102252b 100644
--- a/modules/svg/src/SkSVGClipPath.cpp
+++ b/modules/svg/src/SkSVGClipPath.cpp
@@ -26,7 +26,5 @@
     const auto obbt = ctx.transformForCurrentOBB(fClipPathUnits);
     const auto m = SkMatrix::Translate(obbt.offset.x, obbt.offset.y)
                  * SkMatrix::Scale(obbt.scale.x, obbt.scale.y);
-    clip.transform(m);
-
-    return clip;
+    return clip.makeTransform(m);
 }
diff --git a/tests/AAClipTest.cpp b/tests/AAClipTest.cpp
index 3f3f479..13a114a 100644
--- a/tests/AAClipTest.cpp
+++ b/tests/AAClipTest.cpp
@@ -308,11 +308,10 @@
     };
     SkMask expected(gExpectedImage, SkIRect::MakeWH(4, 6), 4, SkMask::kA8_Format);
 
-    SkPath path;
-    path.addRect(SkRect::MakeXYWH(0, 0,
-                                  SkIntToScalar(4), SkIntToScalar(2)));
-    path.addRect(SkRect::MakeXYWH(0, SkIntToScalar(4),
-                                  SkIntToScalar(4), SkIntToScalar(2)));
+    SkPath path = SkPathBuilder()
+                  .addRect(SkRect::MakeXYWH(0, 0, 4, 2))
+                  .addRect(SkRect::MakeXYWH(0, 4, 4, 2))
+                  .detach();
 
     {
         skiatest::ReporterContext context(reporter, "noAA");
@@ -342,8 +341,7 @@
     SkRRect rrect;
     rrect.setRectXY(SkRect::MakeWH(100, 100), 5, 5);
 
-    SkPath path;
-    path.addRRect(rrect);
+    SkPath path = SkPath::RRect(rrect);
 
     SkAAClip clip;
     clip.setPath(path, path.getBounds().roundOut(), true);
@@ -420,8 +418,7 @@
 //
 DEF_TEST(AAClip_crbug_422693_AvoidOverflow, reporter) {
     SkRasterClip rc(SkIRect::MakeLTRB(-25000, -25000, 25000, 25000));
-    SkPath path;
-    path.addCircle(50, 50, 50);
+    SkPath path = SkPath::Circle(50, 50, 50);
     REPORTER_ASSERT(reporter, rc.op(path, SkMatrix::I(), SkClipOp::kIntersect, true));
 }
 
diff --git a/tests/BlurTest.cpp b/tests/BlurTest.cpp
index d20013f..970086d 100644
--- a/tests/BlurTest.cpp
+++ b/tests/BlurTest.cpp
@@ -74,15 +74,15 @@
 
 
 struct BlurTest {
-    void (*addPath)(SkPath*);
+    SkPath (*addPath)();
     int viewLen;
     SkIRect views[9];
 };
 
 //Path Draw Procs
 //Beware that paths themselves my draw differently depending on the clip.
-static void draw50x50Rect(SkPath* path) {
-    path->addRect(0, 0, SkIntToScalar(50), SkIntToScalar(50));
+static SkPath draw50x50Rect() {
+    return SkPath::Rect({0, 0, 50, 50});
 }
 
 //Tests
@@ -143,8 +143,7 @@
             paint.setMaskFilter(SkMaskFilter::MakeBlur(blurStyle, sigma, respectCTM));
 
             for (size_t test = 0; test < std::size(tests); ++test) {
-                SkPath path;
-                tests[test].addPath(&path);
+                SkPath path = tests[test].addPath();
                 SkPath strokedPath = skpathutils::FillPathWithPaint(path, paint);
                 SkRect refBound = strokedPath.getBounds();
                 SkIRect iref;
@@ -320,8 +319,7 @@
 
     // The geometry is offset a smidge to trigger:
     // https://code.google.com/p/chromium/issues/detail?id=282418
-    SkPath rectPath;
-    rectPath.addRect(0.3f, 0.3f, 100.3f, 100.3f);
+    SkPath rectPath = SkPath::Rect({0.3f, 0.3f, 100.3f, 100.3f});
 
     SkPoint polyPts[] = {
         { 0.3f, 0.3f },
diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp
index 3111fdd..b88a96b 100644
--- a/tests/CanvasTest.cpp
+++ b/tests/CanvasTest.cpp
@@ -244,9 +244,7 @@
 }
 
 static SkPath make_path_from_rect(SkRect r) {
-    SkPath path;
-    path.addRect(r);
-    return path;
+    return SkPath::Rect(r);
 }
 
 static SkRegion make_region_from_irect(SkIRect r) {
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
index 95319f3..81da862 100644
--- a/tests/ClipStackTest.cpp
+++ b/tests/ClipStackTest.cpp
@@ -82,8 +82,7 @@
     // Test that version constructed with rect-path rather than a rect is still considered equal.
     s.restore();
     s.save();
-    SkPath rp;
-    rp.addRect(r);
+    SkPath rp = SkPath::Rect(r);
     s.clipPath(rp, SkMatrix::I(), SkClipOp::kDifference, doAA);
     REPORTER_ASSERT(reporter, s == copy);
 
@@ -230,10 +229,8 @@
     rrectA.setOval(rectA);
     rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
 
-    SkPath pathA, pathB;
-
-    pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
-    pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
+    SkPath pathA = SkPath::RRect(rectA, 5, 5),
+           pathB = SkPath::RRect(rectB, 5, 5);
 
     SkClipStack stack;
     SkRect devClipBound;
@@ -364,8 +361,7 @@
     // non-intersecting rectangles
     SkRect rect  = SkRect::MakeLTRB(0, 0, 10, 10);
 
-    SkPath path;
-    path.addRect(rect);
+    SkPath path = SkPath::Rect(rect);
     path.toggleInverseFillType();
     SkClipStack stack;
     stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
@@ -460,8 +456,7 @@
         stack->clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, doAA);
     };
     SkRect rect = SkRect::MakeWH(100, 100);
-    SkPath path;
-    path.addCircle(50, 50, 50);
+    SkPath path = SkPath::Circle(50, 50, 50);
 
     // Emulating replace operations with more complex geometry is not atomic, it's a replace
     // with a wide-open rect and then an intersection with the complex geometry. The replace can
@@ -586,14 +581,10 @@
     SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
     SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
 
-    SkPath insideCircle;
-    insideCircle.addCircle(25, 25, 5);
-    SkPath intersectingCircle;
-    intersectingCircle.addCircle(25, 40, 10);
-    SkPath outsideCircle;
-    outsideCircle.addCircle(25, 25, 50);
-    SkPath nonIntersectingCircle;
-    nonIntersectingCircle.addCircle(100, 100, 5);
+    SkPath insideCircle = SkPath::Circle(25, 25, 5);
+    SkPath intersectingCircle = SkPath::Circle(25, 40, 10);
+    SkPath outsideCircle = SkPath::Circle(25, 25, 50);
+    SkPath nonIntersectingCircle = SkPath::Circle(100, 100, 5);
 
     {
         SkClipStack stack;
@@ -689,8 +680,7 @@
     // Intersect Op tests with inverse filled rectangles
     {
         SkClipStack stack;
-        SkPath path;
-        path.addRect(outsideRect);
+        SkPath path = SkPath::Rect(outsideRect);
         path.toggleInverseFillType();
         stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
@@ -698,8 +688,7 @@
 
     {
         SkClipStack stack;
-        SkPath path;
-        path.addRect(insideRect);
+        SkPath path = SkPath::Rect(insideRect);
         path.toggleInverseFillType();
         stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
@@ -707,8 +696,7 @@
 
     {
         SkClipStack stack;
-        SkPath path;
-        path.addRect(intersectingRect);
+        SkPath path = SkPath::Rect(intersectingRect);
         path.toggleInverseFillType();
         stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
@@ -716,8 +704,7 @@
 
     {
         SkClipStack stack;
-        SkPath path;
-        path.addRect(nonIntersectingRect);
+        SkPath path = SkPath::Rect(nonIntersectingRect);
         path.toggleInverseFillType();
         stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
@@ -782,8 +769,7 @@
     SkClipStack stack;
     stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), SkClipOp::kIntersect, false);
 
-    SkPath path;
-    path.addRect({30, 10, 40, 20});
+    SkPath path = SkPath::Rect({30, 10, 40, 20});
     path.setFillType(SkPathFillType::kInverseWinding);
     stack.clipPath(path, SkMatrix::I(), SkClipOp::kDifference, false);
 
diff --git a/tests/DashPathEffectTest.cpp b/tests/DashPathEffectTest.cpp
index e0c021b..f1f067c 100644
--- a/tests/DashPathEffectTest.cpp
+++ b/tests/DashPathEffectTest.cpp
@@ -131,8 +131,7 @@
     SkScalar vals[] = { 98, 94, 2888458849.f, 227, 0, 197 };
 
     SkRect cull = SkRect::MakeXYWH(43,236,57,149);
-    SkPath path;
-    path.addRect(cull);
+    SkPath path = SkPath::Rect(cull);
 
     SkPathBuilder builder;
     SkPaint paint;
diff --git a/tests/DrawPathTest.cpp b/tests/DrawPathTest.cpp
index 8a8d2972..481ed99 100644
--- a/tests/DrawPathTest.cpp
+++ b/tests/DrawPathTest.cpp
@@ -363,8 +363,7 @@
 
     SkPaint paint;
     paint.setAntiAlias(true);
-    SkPath path;
-    path.addOval(SkRect::MakeXYWH(-10, -10, 20 + W, 20 + H));
+    SkPath path = SkPath::Oval(SkRect::MakeXYWH(-10, -10, 20 + W, 20 + H));
     surface->getCanvas()->drawPath(path, paint);
 }
 
diff --git a/tests/GrStyledShapeTest.cpp b/tests/GrStyledShapeTest.cpp
index 831754d..62e6f63 100644
--- a/tests/GrStyledShapeTest.cpp
+++ b/tests/GrStyledShapeTest.cpp
@@ -180,8 +180,8 @@
         REPORTER_ASSERT(r, paths_fill_same(pathA, pathB));
         REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
     } else {
-        SkPath pA = pathA;
-        SkPath pB = pathB;
+        SkPathBuilder pA = pathA;
+        SkPathBuilder pB = pathB;
         REPORTER_ASSERT(r, a.inverseFilled() == pA.isInverseFillType());
         REPORTER_ASSERT(r, b.inverseFilled() == pB.isInverseFillType());
         if (ignoreInversenessDifference) {
@@ -206,7 +206,7 @@
             pA.close();
             pB.close();
         }
-        REPORTER_ASSERT(r, pA == pB);
+        REPORTER_ASSERT(r, pA.detach() == pB.detach());
         REPORTER_ASSERT(r, aIsRRect == bIsRRect);
         if (aIsRRect) {
             REPORTER_ASSERT(r, rrectA == rrectB);
@@ -369,9 +369,7 @@
     RectGeo(const SkRect& rect) : fRect(rect) {}
 
     SkPath path() const override {
-        SkPath path;
-        path.addRect(fRect);
-        return path;
+        return SkPath::Rect(fRect);
     }
 
     GrStyledShape makeShape(const SkPaint& paint) const override {
@@ -399,9 +397,7 @@
     }
 
     SkPath path() const override {
-        SkPath path;
-        path.addRRect(fRRect);
-        return path;
+        return SkPath::RRect(fRRect);
     }
 
     bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
@@ -2061,42 +2057,39 @@
         }
     };
 
-    SkPath pathA;
-    SkPath pathB;
+    // Two identical path/pathbuilder
+    SkPath pathA = SkPathBuilder()
+                   .lineTo(10.f, 10.f)
+                   .conicTo(20.f, 20.f, 20.f, 30.f, 0.7f)
+                   .detach();
 
-    // Two identical paths
-    pathA.lineTo(10.f, 10.f);
-    pathA.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f);
-
-    pathB.lineTo(10.f, 10.f);
-    pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f);
-    compare(pathA, pathB, TestCase::kAllSame_ComparisonExpecation);
+    SkPathBuilder pathB = SkPathBuilder()
+                          .lineTo(10.f, 10.f)
+                          .conicTo(20.f, 20.f, 20.f, 30.f, 0.7f);
+    compare(pathA, pathB.detach(), TestCase::kAllSame_ComparisonExpecation);
 
     // Give path b a different point
-    pathB.reset();
     pathB.lineTo(10.f, 10.f);
     pathB.conicTo(21.f, 20.f, 20.f, 30.f, 0.7f);
-    compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
+    compare(pathA, pathB.detach(), TestCase::kAllDifferent_ComparisonExpecation);
 
     // Give path b a different conic weight
     pathB.reset();
     pathB.lineTo(10.f, 10.f);
     pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.6f);
-    compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
+    compare(pathA, pathB.detach(), TestCase::kAllDifferent_ComparisonExpecation);
 
     // Give path b an extra lineTo verb
-    pathB.reset();
     pathB.lineTo(10.f, 10.f);
     pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.6f);
     pathB.lineTo(50.f, 50.f);
-    compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
+    compare(pathA, pathB.detach(), TestCase::kAllDifferent_ComparisonExpecation);
 
     // Give path b a close
-    pathB.reset();
     pathB.lineTo(10.f, 10.f);
     pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f);
     pathB.close();
-    compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
+    compare(pathA, pathB.detach(), TestCase::kAllDifferent_ComparisonExpecation);
 }
 
 DEF_TEST(GrStyledShape, reporter) {
@@ -2108,8 +2101,7 @@
                     SkRect::MakeWH(-10, 20),
                     SkRect::MakeWH(10, -20)}) {
         geos.emplace_back(new RectGeo(r));
-        SkPath rectPath;
-        rectPath.addRect(r);
+        SkPath rectPath = SkPath::Rect(r);
         geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
                                            PathGeo::Invert::kNo));
         geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
@@ -2122,8 +2114,7 @@
                      SkRRect::MakeOval(SkRect::MakeWH(20, 20))}) {
         geos.emplace_back(new RRectGeo(rr));
         test_rrect(reporter, rr);
-        SkPath rectPath;
-        rectPath.addRRect(rr);
+        SkPath rectPath = SkPath::RRect(rr);
         geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes,
                                            PathGeo::Invert::kNo));
         geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes,
@@ -2336,10 +2327,13 @@
 }
 
 DEF_TEST(GrShapeInversion, r) {
-    SkPath path;
-    SkScalar radii[] = {10.f, 10.f, 10.f, 10.f,
-                        10.f, 10.f, 10.f, 10.f};
-    path.addRoundRect(SkRect::MakeWH(50, 50), radii);
+    const SkVector radii[] = {
+        {10.f, 10.f}, {10.f, 10.f},
+        {10.f, 10.f}, {10.f, 10.f}
+    };
+    SkRRect rr;
+    rr.setRectRadii(SkRect::MakeWH(50, 50), radii);
+    SkPath path = SkPath::RRect(rr);
     path.toggleInverseFillType();
 
     GrShape inverseRRect(path);
diff --git a/tests/LazyStencilAttachmentTest.cpp b/tests/LazyStencilAttachmentTest.cpp
index d419af3..c6d1ed4 100644
--- a/tests/LazyStencilAttachmentTest.cpp
+++ b/tests/LazyStencilAttachmentTest.cpp
@@ -13,6 +13,7 @@
 #include "include/core/SkImageInfo.h"
 #include "include/core/SkPaint.h"
 #include "include/core/SkPath.h"
+#include "include/core/SkPathBuilder.h"
 #include "include/core/SkPathTypes.h"
 #include "include/core/SkRect.h"
 #include "include/core/SkRefCnt.h"
@@ -55,9 +56,10 @@
     }
 
     auto drawWithStencilClip = [&](SkSurface& surf, SkColor color) {
-        SkPath clip;
-        clip.addCircle(50, 50, 50);
-        clip.addCircle(50, 50, 10, SkPathDirection::kCCW);
+        SkPath clip = SkPathBuilder()
+                      .addCircle(50, 50, 50)
+                      .addCircle(50, 50, 10, SkPathDirection::kCCW)
+                      .detach();
         SkPaint paint;
         paint.setColor(color);
         surf.getCanvas()->clipPath(clip, false);
diff --git a/tests/MultiPictureDocumentTest.cpp b/tests/MultiPictureDocumentTest.cpp
index 89215b3..423b7d4 100644
--- a/tests/MultiPictureDocumentTest.cpp
+++ b/tests/MultiPictureDocumentTest.cpp
@@ -16,6 +16,7 @@
 #include "include/core/SkImageInfo.h"
 #include "include/core/SkPaint.h"
 #include "include/core/SkPath.h"
+#include "include/core/SkPathBuilder.h"
 #include "include/core/SkPicture.h"
 #include "include/core/SkPictureRecorder.h"
 #include "include/core/SkRRect.h"
@@ -64,8 +65,9 @@
     paint.setColor(SK_ColorYELLOW);
     canvas->drawRoundRect(rect, 10, 10, paint);
 
-    SkPath path;
-    path.cubicTo(768, 0, -512, 256, 256, 256);
+    SkPath path = SkPathBuilder()
+                  .cubicTo(768, 0, -512, 256, 256, 256)
+                  .detach();
     paint.setColor(SK_ColorGREEN);
     canvas->drawPath(path, paint);
 
diff --git a/tests/ParsePathTest.cpp b/tests/ParsePathTest.cpp
index ddf8a7e..28785b1 100644
--- a/tests/ParsePathTest.cpp
+++ b/tests/ParsePathTest.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "include/core/SkPath.h"
+#include "include/core/SkPathBuilder.h"
 #include "include/core/SkRect.h"
 #include "include/core/SkScalar.h"
 #include "include/core/SkString.h"
@@ -59,13 +60,13 @@
 
     SkRect r;
     r.setLTRB(0, 0, 10, 10.5f);
-    SkPath p;
+    SkPathBuilder p;
     p.addRect(r);
-    test_to_from(reporter, p);
+    test_to_from(reporter, p.snapshot());
     p.addOval(r);
-    test_to_from(reporter, p);
-    p.addRoundRect(r, 4, 4.5f);
-    test_to_from(reporter, p);
+    test_to_from(reporter, p.snapshot());
+    p.addRRect(SkRRect::MakeRectXY(r, 4, 4.5f));
+    test_to_from(reporter, p.snapshot());
 }
 
 static void testInvalidPath(skiatest::Reporter* reporter, const std::string& name,
diff --git a/tests/PathBuilderTest.cpp b/tests/PathBuilderTest.cpp
index c57014f..abac7f9 100644
--- a/tests/PathBuilderTest.cpp
+++ b/tests/PathBuilderTest.cpp
@@ -147,8 +147,7 @@
             REPORTER_ASSERT(reporter, closed);
             REPORTER_ASSERT(reporter, dir == dir2);
 
-            SkPath p;
-            p.addRect(r, dir, i);
+            SkPath p = SkPath::Rect(r, dir, i);
             REPORTER_ASSERT(reporter, p == bp);
 
             // do it again, after the detach
@@ -211,8 +210,7 @@
     for (auto dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
         for (int i = 0; i < 4; ++i) {
             auto bp = SkPathBuilder().addOval(r, dir, i).detach();
-            SkPath p;
-            p.addOval(r, dir, i);
+            SkPath p = SkPath::Oval(r, dir, i);
             REPORTER_ASSERT(reporter, is_eq(p, bp));
 
             SkRect bounds;
@@ -222,8 +220,7 @@
             REPORTER_ASSERT(reporter, bp.isConvex());
         }
         auto bp = SkPathBuilder().addOval(r, dir).detach();
-        SkPath p;
-        p.addOval(r, dir);
+        SkPath p = SkPath::Oval(r, dir);
         REPORTER_ASSERT(reporter, is_eq(p, bp));
 
         // test negative case -- can't have any other segments
@@ -243,13 +240,11 @@
             b.addRRect(rr, dir, i);
             auto bp = b.detach();
 
-            SkPath p;
-            p.addRRect(rr, dir, i);
+            SkPath p = SkPath::RRect(rr, dir, i);
             REPORTER_ASSERT(reporter, is_eq(p, bp));
         }
         auto bp = SkPathBuilder().addRRect(rr, dir).detach();
-        SkPath p;
-        p.addRRect(rr, dir);
+        SkPath p = SkPath::RRect(rr, dir);
         REPORTER_ASSERT(reporter, is_eq(p, bp));
 
         // test negative case -- can't have any other segments
@@ -509,11 +504,11 @@
     };
 
     for (auto e : expectations) {
+        SkPath path;
 #ifndef SK_HIDE_PATH_EDIT_METHODS
-        auto path = path_add(e.fStartWithMove, e.fMode);
+        path = path_add(e.fStartWithMove, e.fMode);
         REPORTER_ASSERT(reporter, path.isConvex() == e.fShouldBeConvex);
 #endif
-
         path = builder_add(e.fStartWithMove, e.fMode);
         REPORTER_ASSERT(reporter, path.isConvex() == e.fShouldBeConvex);
     }
@@ -579,6 +574,7 @@
  *  either the classic mutable apis, or via SkPathBuilder (SkPath::Polygon uses builder).
  */
 DEF_TEST(pathbuilder_lastmoveindex, reporter) {
+#ifndef SK_HIDE_PATH_EDIT_METHODS
     const SkPoint pts[] = {
         {0, 1}, {2, 3}, {4, 5},
     };
@@ -606,6 +602,7 @@
             REPORTER_ASSERT(reporter, b_last == expected);
         }
     }
+#endif
 }
 
 static void assertIsMoveTo(skiatest::Reporter* reporter, SkPathPriv::RangeIter* iter,
@@ -830,7 +827,7 @@
 }
 
 DEF_TEST(SkPathBuilder_Path_arcTo, reporter) {
-
+#ifndef SK_HIDE_PATH_EDIT_METHODS
     auto check_both_methods = [reporter](const SkRect& r, float start, float sweep) {
         SkPath path;
         path.arcTo(r, start, sweep, true);
@@ -855,6 +852,7 @@
         sweep = rand.nextSScalar1() * 1000;
         check_both_methods(r, start, sweep);
     }
+#endif
 }
 
 DEF_TEST(SkPathBuilder_cleaning, reporter) {
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index a39989a..e36be94 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -69,9 +69,8 @@
     rrect.setRectRadii(bounds, radii);
     REPORTER_ASSERT(reporter, bounds == rrect.rect());
 
-    SkPath path;
     // this line should not assert in the debug build (from validate)
-    path.addRRect(rrect);
+    SkPath path = SkPath::RRect(rrect);
     REPORTER_ASSERT(reporter, bounds == path.getBounds());
 }
 
@@ -373,51 +372,57 @@
 static void test_path_close_issue1474(skiatest::Reporter* reporter) {
     // This test checks that r{Line,Quad,Conic,Cubic}To following a close()
     // are relative to the point we close to, not relative to the point we close from.
-    SkPath path;
-    SkPoint last;
 
     // Test rLineTo().
-    path.rLineTo(0, 100);
-    path.rLineTo(100, 0);
-    path.close();          // Returns us back to 0,0.
-    path.rLineTo(50, 50);  // This should go to 50,50.
-
-    path.getLastPt(&last);
-    REPORTER_ASSERT(reporter, 50 == last.fX);
-    REPORTER_ASSERT(reporter, 50 == last.fY);
+    SkPath path = SkPathBuilder()
+                  .rLineTo(0, 100)
+                  .rLineTo(100, 0)
+                  .close()          // Returns us back to 0,0.
+                  .rLineTo(50, 50)  // This should go to 50,50.
+                  .detach();
+    auto last = path.getLastPt();
+    REPORTER_ASSERT(reporter, last.has_value());
+    REPORTER_ASSERT(reporter, 50 == last->fX);
+    REPORTER_ASSERT(reporter, 50 == last->fY);
 
     // Test rQuadTo().
-    path.rewind();
-    path.rLineTo(0, 100);
-    path.rLineTo(100, 0);
-    path.close();
-    path.rQuadTo(50, 50, 75, 75);
+    path = SkPathBuilder()
+           .rLineTo(0, 100)
+           .rLineTo(100, 0)
+           .close()
+           .rQuadTo(50, 50, 75, 75)
+           .detach();
 
-    path.getLastPt(&last);
-    REPORTER_ASSERT(reporter, 75 == last.fX);
-    REPORTER_ASSERT(reporter, 75 == last.fY);
+    last = path.getLastPt();
+    REPORTER_ASSERT(reporter, last.has_value());
+    REPORTER_ASSERT(reporter, 75 == last->fX);
+    REPORTER_ASSERT(reporter, 75 == last->fY);
 
     // Test rConicTo().
-    path.rewind();
-    path.rLineTo(0, 100);
-    path.rLineTo(100, 0);
-    path.close();
-    path.rConicTo(50, 50, 85, 85, 2);
+    path = SkPathBuilder()
+           .rLineTo(0, 100)
+           .rLineTo(100, 0)
+           .close()
+           .rConicTo(50, 50, 85, 85, 2)
+           .detach();
 
-    path.getLastPt(&last);
-    REPORTER_ASSERT(reporter, 85 == last.fX);
-    REPORTER_ASSERT(reporter, 85 == last.fY);
+    last = path.getLastPt();
+    REPORTER_ASSERT(reporter, last.has_value());
+    REPORTER_ASSERT(reporter, 85 == last->fX);
+    REPORTER_ASSERT(reporter, 85 == last->fY);
 
     // Test rCubicTo().
-    path.rewind();
-    path.rLineTo(0, 100);
-    path.rLineTo(100, 0);
-    path.close();
-    path.rCubicTo(50, 50, 85, 85, 95, 95);
+    path = SkPathBuilder()
+           .rLineTo(0, 100)
+           .rLineTo(100, 0)
+           .close()
+           .rCubicTo(50, 50, 85, 85, 95, 95)
+           .detach();
 
-    path.getLastPt(&last);
-    REPORTER_ASSERT(reporter, 95 == last.fX);
-    REPORTER_ASSERT(reporter, 95 == last.fY);
+    last = path.getLastPt();
+    REPORTER_ASSERT(reporter, last.has_value());
+    REPORTER_ASSERT(reporter, 95 == last->fX);
+    REPORTER_ASSERT(reporter, 95 == last->fY);
 }
 
 static void test_gen_id(skiatest::Reporter* reporter) {
@@ -667,10 +672,11 @@
 }
 
 static void test_crbug_613918() {
-    SkPath path;
-    path.conicTo(-6.62478e-08f, 4.13885e-08f, -6.36935e-08f, 3.97927e-08f, 0.729058f);
-    path.quadTo(2.28206e-09f, -1.42572e-09f, 3.91919e-09f, -2.44852e-09f);
-    path.cubicTo(-16752.2f, -26792.9f, -21.4673f, 10.9347f, -8.57322f, -7.22739f);
+    SkPath path = SkPathBuilder()
+                  .conicTo(-6.62478e-08f, 4.13885e-08f, -6.36935e-08f, 3.97927e-08f, 0.729058f)
+                  .quadTo(2.28206e-09f, -1.42572e-09f, 3.91919e-09f, -2.44852e-09f)
+                  .cubicTo(-16752.2f, -26792.9f, -21.4673f, 10.9347f, -8.57322f, -7.22739f)
+                  .detach();
 
     // This call could lead to an assert or uninitialized read due to a failure
     // to check the return value from SkCubicClipper::ChopMonoAtY.
@@ -678,29 +684,34 @@
 }
 
 static void test_addrect(skiatest::Reporter* reporter) {
-    SkPath path;
-    path.lineTo(0, 0);
-    path.addRect(SkRect::MakeWH(50, 100));
+    SkPath path = SkPathBuilder()
+                  .lineTo(0, 0)
+                  .addRect(SkRect::MakeWH(50, 100))
+                  .detach();
     REPORTER_ASSERT(reporter, path.isRect(nullptr));
 
-    path.reset();
-    path.lineTo(FLT_EPSILON, FLT_EPSILON);
-    path.addRect(SkRect::MakeWH(50, 100));
+    path = SkPathBuilder()
+           .lineTo(FLT_EPSILON, FLT_EPSILON)
+           .addRect(SkRect::MakeWH(50, 100))
+           .detach();
     REPORTER_ASSERT(reporter, !path.isRect(nullptr));
 
-    path.reset();
-    path.quadTo(0, 0, 0, 0);
-    path.addRect(SkRect::MakeWH(50, 100));
+    path = SkPathBuilder()
+           .quadTo(0, 0, 0, 0)
+           .addRect(SkRect::MakeWH(50, 100))
+           .detach();
     REPORTER_ASSERT(reporter, !path.isRect(nullptr));
 
-    path.reset();
-    path.conicTo(0, 0, 0, 0, 0.5f);
-    path.addRect(SkRect::MakeWH(50, 100));
+    path = SkPathBuilder()
+           .conicTo(0, 0, 0, 0, 0.5f)
+           .addRect(SkRect::MakeWH(50, 100))
+           .detach();
     REPORTER_ASSERT(reporter, !path.isRect(nullptr));
 
-    path.reset();
-    path.cubicTo(0, 0, 0, 0, 0, 0);
-    path.addRect(SkRect::MakeWH(50, 100));
+    path = SkPathBuilder()
+           .cubicTo(0, 0, 0, 0, 0, 0)
+           .addRect(SkRect::MakeWH(50, 100))
+           .detach();
     REPORTER_ASSERT(reporter, !path.isRect(nullptr));
 }
 
@@ -827,9 +838,10 @@
 // Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
 //
 static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
-    SkPath path;
-    path.quadTo(157, 366, 286, 208);
-    path.arcTo(37, 442, 315, 163, 957494590897113.0f);
+    SkPath path = SkPathBuilder()
+                  .quadTo(157, 366, 286, 208)
+                  .arcTo({37, 442}, {315, 163}, 957494590897113.0f)
+                  .detach();
 
     SkMatrix matrix;
     matrix.setScale(1000*1000, 1000*1000);
@@ -841,18 +853,18 @@
     while (path.isFinite()) {
         REPORTER_ASSERT(reporter, path.getBounds().isFinite());
         REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
-        path.transform(matrix);
+        path = path.makeTransform(matrix);
     }
     REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
 
     matrix.setTranslate(SK_Scalar1, SK_Scalar1);
-    path.transform(matrix);
+    path = path.makeTransform(matrix);
     // we need to still be non-finite
     REPORTER_ASSERT(reporter, !path.isFinite());
     REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
 }
 
-static void add_corner_arc(SkPath* path, const SkRect& rect,
+static void add_corner_arc(SkPathBuilder* builder, const SkRect& rect,
                            SkScalar xIn, SkScalar yIn,
                            int startAngle)
 {
@@ -879,17 +891,18 @@
         break;
     }
 
-    path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
+    builder->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
 }
 
-static void make_arb_round_rect(SkPath* path, const SkRect& r,
-                                SkScalar xCorner, SkScalar yCorner) {
+static SkPath make_arb_round_rect(const SkRect& r, SkScalar xCorner, SkScalar yCorner) {
+    SkPathBuilder builder;
     // we are lazy here and use the same x & y for each corner
-    add_corner_arc(path, r, xCorner, yCorner, 270);
-    add_corner_arc(path, r, xCorner, yCorner, 0);
-    add_corner_arc(path, r, xCorner, yCorner, 90);
-    add_corner_arc(path, r, xCorner, yCorner, 180);
-    path->close();
+    add_corner_arc(&builder, r, xCorner, yCorner, 270);
+    add_corner_arc(&builder, r, xCorner, yCorner, 0);
+    add_corner_arc(&builder, r, xCorner, yCorner, 90);
+    add_corner_arc(&builder, r, xCorner, yCorner, 180);
+    builder.close();
+    return builder.detach();
 }
 
 // Chrome creates its own round rects with each corner possibly being different.
@@ -911,9 +924,7 @@
         r.fRight =  r.fLeft + 2 * size;
         r.fBottom = r.fTop + 2 * size;
 
-        SkPath temp;
-
-        make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
+        SkPath temp = make_arb_round_rect(r, r.width() / 10, r.height() / 15);
 
         REPORTER_ASSERT(reporter, temp.isConvex());
     }
@@ -938,9 +949,7 @@
         r.fRight =  r.fLeft + 2 * size;
         r.fBottom = r.fTop + 2 * size;
 
-        SkPath temp;
-
-        make_arb_round_rect(&temp, r, 0, 0);
+        SkPath temp = make_arb_round_rect(r, 0, 0);
 
         SkRect result;
         REPORTER_ASSERT(reporter, temp.isRect(&result));
@@ -1840,10 +1849,10 @@
 }
 
 static void test_isLine(skiatest::Reporter* reporter) {
-    SkPath path;
     SkPoint pts[2];
     const SkScalar value = SkIntToScalar(5);
 
+    SkPath path;
     REPORTER_ASSERT(reporter, !path.isLine(nullptr));
 
     // set some non-zero values
@@ -1858,7 +1867,9 @@
     const SkScalar moveY = SkIntToScalar(2);
     REPORTER_ASSERT(reporter, value != moveX && value != moveY);
 
-    path.moveTo(moveX, moveY);
+    SkPathBuilder builder;
+    builder.moveTo(moveX, moveY);
+    path = builder.snapshot();
     REPORTER_ASSERT(reporter, !path.isLine(nullptr));
     REPORTER_ASSERT(reporter, !path.isLine(pts));
     // check that pts was untouched
@@ -1869,7 +1880,8 @@
     const SkScalar lineY = SkIntToScalar(2);
     REPORTER_ASSERT(reporter, value != lineX && value != lineY);
 
-    path.lineTo(lineX, lineY);
+    builder.lineTo(lineX, lineY);
+    path = builder.snapshot();
     REPORTER_ASSERT(reporter, path.isLine(nullptr));
 
     REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
@@ -1878,20 +1890,20 @@
     REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
     REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
 
-    path.lineTo(0, 0);  // too many points/verbs
+    builder.lineTo(0, 0);  // too many points/verbs
+    path = builder.snapshot();
     REPORTER_ASSERT(reporter, !path.isLine(nullptr));
     REPORTER_ASSERT(reporter, !path.isLine(pts));
     REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
     REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
 
-    path.reset();
-    path.quadTo(1, 1, 2, 2);
+    builder.reset();
+    builder.quadTo(1, 1, 2, 2);
+    path = builder.snapshot();
     REPORTER_ASSERT(reporter, !path.isLine(nullptr));
 }
 
 static void test_conservativelyContains(skiatest::Reporter* reporter) {
-    SkPath path;
-
     // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
     static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
 
@@ -1992,51 +2004,51 @@
             }
             for (int d = 0; d < 2; ++d) {
                 SkPathDirection dir = d ? SkPathDirection::kCCW : SkPathDirection::kCW;
-                path.reset();
-                path.addRect(kBaseRect, dir);
+                SkPath path = SkPath::Rect(kBaseRect, dir);
                 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
                                           path.conservativelyContainsRect(qRect));
 
-                path.reset();
-                path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
+                path = SkPath::Circle(kCircleC.fX, kCircleC.fY, circleR, dir);
                 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
                                           path.conservativelyContainsRect(qRect));
 
-                path.reset();
-                path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
+                path = SkPath::RRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
                 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
                                           path.conservativelyContainsRect(qRect));
 
-                path.reset();
-                path.moveTo(kBaseRect.fLeft + kRRRadii[0], kBaseRect.fTop);
-                path.cubicTo(kBaseRect.fLeft + kRRRadii[0] / 2, kBaseRect.fTop,
-                             kBaseRect.fLeft, kBaseRect.fTop + kRRRadii[1] / 2,
-                             kBaseRect.fLeft, kBaseRect.fTop + kRRRadii[1]);
-                path.lineTo(kBaseRect.fLeft, kBaseRect.fBottom);
-                path.lineTo(kBaseRect.fRight, kBaseRect.fBottom);
-                path.lineTo(kBaseRect.fRight, kBaseRect.fTop);
-                path.close();
+                path = SkPathBuilder()
+                       .moveTo(kBaseRect.fLeft + kRRRadii[0], kBaseRect.fTop)
+                       .cubicTo(kBaseRect.fLeft + kRRRadii[0] / 2, kBaseRect.fTop,
+                                kBaseRect.fLeft, kBaseRect.fTop + kRRRadii[1] / 2,
+                                kBaseRect.fLeft, kBaseRect.fTop + kRRRadii[1])
+                       .lineTo(kBaseRect.fLeft, kBaseRect.fBottom)
+                       .lineTo(kBaseRect.fRight, kBaseRect.fBottom)
+                       .lineTo(kBaseRect.fRight, kBaseRect.fTop)
+                       .close()
+                       .detach();
                 REPORTER_ASSERT(reporter, kQueries[q].fInCubicRR ==
                                           path.conservativelyContainsRect(qRect));
 
             }
             // Slightly non-convex shape, shouldn't contain any rects.
-            path.reset();
-            path.moveTo(0, 0);
-            path.lineTo(SkIntToScalar(50), 0.05f);
-            path.lineTo(SkIntToScalar(100), 0);
-            path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
-            path.lineTo(0, SkIntToScalar(100));
-            path.close();
+            SkPath path = SkPathBuilder()
+                          .moveTo(0, 0)
+                          .lineTo(SkIntToScalar(50), 0.05f)
+                          .lineTo(SkIntToScalar(100), 0)
+                          .lineTo(SkIntToScalar(100), SkIntToScalar(100))
+                          .lineTo(0, SkIntToScalar(100))
+                          .close()
+                          .detach();
             REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
         }
     }
 
     // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
-    path.reset();
-    path.moveTo(0, 0);
-    path.lineTo(SkIntToScalar(100), 0);
-    path.lineTo(0, SkIntToScalar(100));
+    SkPathBuilder builder;
+    builder.moveTo(0, 0);
+    builder.lineTo(SkIntToScalar(100), 0);
+    builder.lineTo(0, SkIntToScalar(100));
+    SkPath path = builder.snapshot();
 
     // inside, on along top edge
     REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
@@ -2062,17 +2074,19 @@
 
 
     // Test that multiple move commands do not cause asserts.
-    path.moveTo(SkIntToScalar(100), SkIntToScalar(100));
+    builder.moveTo(SkIntToScalar(100), SkIntToScalar(100));
+    path = builder.detach();
     REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
                                                                                SkIntToScalar(10),
                                                                                SkIntToScalar(10))));
 
     // Same as above path and first test but with an extra moveTo.
-    path.reset();
-    path.moveTo(100, 100);
-    path.moveTo(0, 0);
-    path.lineTo(SkIntToScalar(100), 0);
-    path.lineTo(0, SkIntToScalar(100));
+    path = SkPathBuilder()
+           .moveTo(100, 100)
+           .moveTo(0, 0)
+           .lineTo(SkIntToScalar(100), 0)
+           .lineTo(0, SkIntToScalar(100))
+           .detach();
     // Convexity logic treats a path as filled and closed, so that multiple (non-trailing) moveTos
     // have no effect on convexity
     REPORTER_ASSERT(reporter, path.conservativelyContainsRect(
@@ -2082,11 +2096,12 @@
 
     // Same as above path and first test but with the extra moveTo making a degenerate sub-path
     // following the non-empty sub-path. Verifies that this does not trigger assertions.
-    path.reset();
-    path.moveTo(0, 0);
-    path.lineTo(SkIntToScalar(100), 0);
-    path.lineTo(0, SkIntToScalar(100));
-    path.moveTo(100, 100);
+    path = SkPathBuilder()
+           .moveTo(0, 0)
+           .lineTo(SkIntToScalar(100), 0)
+           .lineTo(0, SkIntToScalar(100))
+           .moveTo(100, 100)
+           .detach();
 
     REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
                                                                                SkIntToScalar(10),
@@ -2094,52 +2109,54 @@
 
     // Test that multiple move commands do not cause asserts and that the function
     // is not confused by the multiple moves.
-    path.reset();
-    path.moveTo(0, 0);
-    path.lineTo(SkIntToScalar(100), 0);
-    path.lineTo(0, SkIntToScalar(100));
-    path.moveTo(0, SkIntToScalar(200));
-    path.lineTo(SkIntToScalar(100), SkIntToScalar(200));
-    path.lineTo(0, SkIntToScalar(300));
+    path = SkPathBuilder()
+           .moveTo(0, 0)
+           .lineTo(SkIntToScalar(100), 0)
+           .lineTo(0, SkIntToScalar(100))
+           .moveTo(0, SkIntToScalar(200))
+           .lineTo(SkIntToScalar(100), SkIntToScalar(200))
+           .lineTo(0, SkIntToScalar(300))
+           .detach();
 
     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
                                                             SkRect::MakeXYWH(SkIntToScalar(50), 0,
                                                                              SkIntToScalar(10),
                                                                              SkIntToScalar(10))));
 
-    path.reset();
-    path.lineTo(100, 100);
+    path = SkPathBuilder().lineTo(100, 100).detach();
     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(0, 0, 1, 1)));
 
     // An empty path should not contain any rectangle. It's questionable whether an empty path
     // contains an empty rectangle. However, since it is a conservative test it is ok to
     // return false.
-    path.reset();
+    path = SkPath();
     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeWH(1,1)));
     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeWH(0,0)));
 
-    path.reset();
-    path.moveTo(50, 50);
-    path.cubicTo(0, 0, 100, 0, 50, 50);
+    path = SkPathBuilder()
+           .moveTo(50, 50)
+           .cubicTo(0, 0, 100, 0, 50, 50)
+           .detach();
     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeWH(100, 100)));
     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeWH(30, 30)));
     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeWH(1,1)));
     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeWH(0,0)));
 
-    path.reset();
-    path.moveTo(50, 50);
-    path.quadTo(100, 100, 50, 50);
+    path = SkPathBuilder()
+           .moveTo(50, 50)
+           .quadTo(100, 100, 50, 50)
+           .detach();
     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeWH(1,1)));
     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeWH(0,0)));
 }
 
 static void test_isRect_open_close(skiatest::Reporter* reporter) {
-    SkPath path;
+    SkPath path = SkPathBuilder()
+                  .moveTo(0, 0).lineTo(1, 0).lineTo(1, 1).lineTo(0, 1)
+                  .close()
+                  .detach();
+
     bool isClosed;
-
-    path.moveTo(0, 0); path.lineTo(1, 0); path.lineTo(1, 1); path.lineTo(0, 1);
-    path.close();
-
     REPORTER_ASSERT(reporter, path.isRect(nullptr, &isClosed, nullptr));
     REPORTER_ASSERT(reporter, isClosed);
 }
@@ -2236,14 +2253,15 @@
     const size_t testCount = std::size(tests);
     int index;
     for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
-        SkPath path;
-        path.moveTo(tests[testIndex].fPoints[0].fX, tests[testIndex].fPoints[0].fY);
+        SkPathBuilder builder;
+        builder.moveTo(tests[testIndex].fPoints[0].fX, tests[testIndex].fPoints[0].fY);
         for (index = 1; index < tests[testIndex].fPointCount; ++index) {
-            path.lineTo(tests[testIndex].fPoints[index].fX, tests[testIndex].fPoints[index].fY);
+            builder.lineTo(tests[testIndex].fPoints[index].fX, tests[testIndex].fPoints[index].fY);
         }
         if (tests[testIndex].fClose) {
-            path.close();
+            builder.close();
         }
+        SkPath path = builder.detach();
         REPORTER_ASSERT(reporter, tests[testIndex].fIsRect == path.isRect(nullptr));
 
         if (tests[testIndex].fIsRect) {
@@ -2275,58 +2293,59 @@
     }
 
     // fail, close then line
-    SkPath path1;
-    path1.moveTo(r1[0].fX, r1[0].fY);
+    SkPathBuilder builder;
+    builder.moveTo(r1[0].fX, r1[0].fY);
     for (index = 1; index < SkToInt(std::size(r1)); ++index) {
-        path1.lineTo(r1[index].fX, r1[index].fY);
+        builder.lineTo(r1[index].fX, r1[index].fY);
     }
-    path1.close();
-    path1.lineTo(1, 0);
+    builder.close();
+    builder.lineTo(1, 0);
+    SkPath path1 = builder.detach();
     REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
 
     // fail, move in the middle
-    path1.reset();
-    path1.moveTo(r1[0].fX, r1[0].fY);
+    builder.moveTo(r1[0].fX, r1[0].fY);
     for (index = 1; index < SkToInt(std::size(r1)); ++index) {
         if (index == 2) {
-            path1.moveTo(1, .5f);
+            builder.moveTo(1, .5f);
         }
-        path1.lineTo(r1[index].fX, r1[index].fY);
+        builder.lineTo(r1[index].fX, r1[index].fY);
     }
-    path1.close();
+    builder.close();
+    path1 = builder.detach();
     REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
 
     // fail, move on the edge
-    path1.reset();
     for (index = 1; index < SkToInt(std::size(r1)); ++index) {
-        path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
-        path1.lineTo(r1[index].fX, r1[index].fY);
+        builder.moveTo(r1[index - 1].fX, r1[index - 1].fY);
+        builder.lineTo(r1[index].fX, r1[index].fY);
     }
-    path1.close();
+    builder.close();
+    path1 = builder.detach();
     REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
 
     // fail, quad
-    path1.reset();
-    path1.moveTo(r1[0].fX, r1[0].fY);
+    builder.moveTo(r1[0].fX, r1[0].fY);
     for (index = 1; index < SkToInt(std::size(r1)); ++index) {
         if (index == 2) {
-            path1.quadTo(1, .5f, 1, .5f);
+            builder.quadTo(1, .5f, 1, .5f);
         }
-        path1.lineTo(r1[index].fX, r1[index].fY);
+        builder.lineTo(r1[index].fX, r1[index].fY);
     }
-    path1.close();
+    builder.close();
+    path1 = builder.detach();
     REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
 
     // fail, cubic
-    path1.reset();
-    path1.moveTo(r1[0].fX, r1[0].fY);
+    builder.moveTo(r1[0].fX, r1[0].fY);
     for (index = 1; index < SkToInt(std::size(r1)); ++index) {
         if (index == 2) {
-            path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
+            builder.cubicTo(1, .5f, 1, .5f, 1, .5f);
         }
-        path1.lineTo(r1[index].fX, r1[index].fY);
+        builder.lineTo(r1[index].fX, r1[index].fY);
     }
-    path1.close();
+    builder.close();
+    path1 = builder.detach();
     REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
 }