Import Cobalt 16.136005

Change-Id: I2b9198982828ad841d50902b145910c0aeb689d3
diff --git a/src/third_party/skia/tests/AAClipTest.cpp b/src/third_party/skia/tests/AAClipTest.cpp
index 64e3784..5b156a4 100644
--- a/src/third_party/skia/tests/AAClipTest.cpp
+++ b/src/third_party/skia/tests/AAClipTest.cpp
@@ -10,6 +10,8 @@
 #include "SkMask.h"
 #include "SkPath.h"
 #include "SkRandom.h"
+#include "SkRasterClip.h"
+#include "SkRRect.h"
 #include "Test.h"
 
 static bool operator==(const SkMask& a, const SkMask& b) {
@@ -34,7 +36,6 @@
         case SkMask::kLCD16_Format:
             wbytes <<= 1;
             break;
-        case SkMask::kLCD32_Format:
         case SkMask::kARGB32_Format:
             wbytes <<= 2;
             break;
@@ -62,7 +63,7 @@
     if (rgn.isEmpty()) {
         mask->fBounds.setEmpty();
         mask->fRowBytes = 0;
-        mask->fImage = NULL;
+        mask->fImage = nullptr;
         return;
     }
 
@@ -185,7 +186,7 @@
 
     path.addOval(SkRect::MakeWH(sheight, sheight));
     REPORTER_ASSERT(reporter, sheight == path.getBounds().height());
-    clip.setPath(path, NULL, true);
+    clip.setPath(path, nullptr, true);
     REPORTER_ASSERT(reporter, height == clip.getBounds().height());
 
     // this is the trimmed height of this cubic (with aa). The critical thing
@@ -199,7 +200,7 @@
     imoveTo(path, 0, 20);
     icubicTo(path, 40, 40, 40, 0, 0, 20);
     REPORTER_ASSERT(reporter, sheight == path.getBounds().height());
-    clip.setPath(path, NULL, true);
+    clip.setPath(path, nullptr, true);
     REPORTER_ASSERT(reporter, teardrop_height == clip.getBounds().height());
 }
 
@@ -228,9 +229,8 @@
     REPORTER_ASSERT(reporter, clip1 == clip0);
 
     SkMask mask;
-    mask.fImage = NULL;
     clip0.copyToMask(&mask);
-    REPORTER_ASSERT(reporter, NULL == mask.fImage);
+    REPORTER_ASSERT(reporter, nullptr == mask.fImage);
     REPORTER_ASSERT(reporter, mask.fBounds.isEmpty());
 }
 
@@ -263,17 +263,18 @@
             bool nonEmptyAA = clip2.op(clip0, clip1, op);
             bool nonEmptyBW = rgn2.op(rgn0, rgn1, op);
             if (nonEmptyAA != nonEmptyBW || clip2.getBounds() != rgn2.getBounds()) {
-                SkDebugf("[%d %d %d %d] %s [%d %d %d %d] = BW:[%d %d %d %d] AA:[%d %d %d %d]\n",
-                         r0.fLeft, r0.fTop, r0.right(), r0.bottom(),
-                         gRgnOpNames[j],
-                         r1.fLeft, r1.fTop, r1.right(), r1.bottom(),
-                         rgn2.getBounds().fLeft, rgn2.getBounds().fTop,
-                         rgn2.getBounds().right(), rgn2.getBounds().bottom(),
-                         clip2.getBounds().fLeft, clip2.getBounds().fTop,
-                         clip2.getBounds().right(), clip2.getBounds().bottom());
+                ERRORF(reporter, "%s %s "
+                       "[%d %d %d %d] %s [%d %d %d %d] = BW:[%d %d %d %d] AA:[%d %d %d %d]\n",
+                       nonEmptyAA == nonEmptyBW ? "true" : "false",
+                       clip2.getBounds() == rgn2.getBounds() ? "true" : "false",
+                       r0.fLeft, r0.fTop, r0.right(), r0.bottom(),
+                       gRgnOpNames[j],
+                       r1.fLeft, r1.fTop, r1.right(), r1.bottom(),
+                       rgn2.getBounds().fLeft, rgn2.getBounds().fTop,
+                       rgn2.getBounds().right(), rgn2.getBounds().bottom(),
+                       clip2.getBounds().fLeft, clip2.getBounds().fTop,
+                       clip2.getBounds().right(), clip2.getBounds().bottom());
             }
-            REPORTER_ASSERT(reporter, nonEmptyAA == nonEmptyBW);
-            REPORTER_ASSERT(reporter, clip2.getBounds() == rgn2.getBounds());
 
             SkMask maskBW, maskAA;
             copyToMask(rgn2, &maskBW);
@@ -308,7 +309,7 @@
 
     for (int i = 0; i < 2; ++i) {
         SkAAClip clip;
-        clip.setPath(path, NULL, 1 == i);
+        clip.setPath(path, nullptr, 1 == i);
 
         SkMask mask;
         clip.copyToMask(&mask);
@@ -334,7 +335,7 @@
     // This rect should intersect the clip, but slice-out all of the "soft" parts,
     // leaving just a rect.
     const SkIRect ir = SkIRect::MakeLTRB(10, -10, 50, 90);
-    
+
     clip.op(ir, SkRegion::kIntersect_Op);
 
     REPORTER_ASSERT(reporter, clip.getBounds() == SkIRect::MakeLTRB(10, 0, 50, 90));
@@ -342,36 +343,9 @@
     REPORTER_ASSERT(reporter, clip.isRect());
 }
 
-#include "SkRasterClip.h"
-
-static void copyToMask(const SkRasterClip& rc, SkMask* mask) {
-    if (rc.isAA()) {
-        rc.aaRgn().copyToMask(mask);
-    } else {
-        copyToMask(rc.bwRgn(), mask);
-    }
-}
-
-static bool operator==(const SkRasterClip& a, const SkRasterClip& b) {
-    if (a.isEmpty()) {
-        return b.isEmpty();
-    }
-    if (b.isEmpty()) {
-        return false;
-    }
-
-    SkMask ma, mb;
-    copyToMask(a, &ma);
-    copyToMask(b, &mb);
-    SkAutoMaskFreeImage aCleanUp(ma.fImage);
-    SkAutoMaskFreeImage bCleanUp(mb.fImage);
-
-    return ma == mb;
-}
-
 static void did_dx_affect(skiatest::Reporter* reporter, const SkScalar dx[],
                           size_t count, bool changed) {
-    const SkISize baseSize = SkISize::Make(10, 10);
+    const SkIRect baseBounds = SkIRect::MakeXYWH(0, 0, 10, 10);
     SkIRect ir = { 0, 0, 10, 10 };
 
     for (size_t i = 0; i < count; ++i) {
@@ -382,11 +356,11 @@
         SkRasterClip rc1(ir);
         SkRasterClip rc2(ir);
 
-        rc0.op(r, baseSize, SkRegion::kIntersect_Op, false);
+        rc0.op(r, SkMatrix::I(), baseBounds, SkRegion::kIntersect_Op, false);
         r.offset(dx[i], 0);
-        rc1.op(r, baseSize, SkRegion::kIntersect_Op, true);
+        rc1.op(r, SkMatrix::I(), baseBounds, SkRegion::kIntersect_Op, true);
         r.offset(-2*dx[i], 0);
-        rc2.op(r, baseSize, SkRegion::kIntersect_Op, true);
+        rc2.op(r, SkMatrix::I(), baseBounds, SkRegion::kIntersect_Op, true);
 
         REPORTER_ASSERT(reporter, changed != (rc0 == rc1));
         REPORTER_ASSERT(reporter, changed != (rc0 == rc2));
@@ -421,6 +395,20 @@
     }
 }
 
+// Building aaclip meant aa-scan-convert a path into a huge clip.
+// the old algorithm sized the supersampler to the size of the clip, which overflowed
+// its internal 16bit coordinates. The fix was to intersect the clip+path_bounds before
+// sizing the supersampler.
+//
+// Before the fix, the following code would assert in debug builds.
+//
+static void test_crbug_422693(skiatest::Reporter* reporter) {
+    SkRasterClip rc(SkIRect::MakeLTRB(-25000, -25000, 25000, 25000));
+    SkPath path;
+    path.addCircle(50, 50, 50);
+    rc.op(path, SkMatrix::I(), rc.getBounds(), SkRegion::kIntersect_Op, true);
+}
+
 DEF_TEST(AAClip, reporter) {
     test_empty(reporter);
     test_path_bounds(reporter);
@@ -430,4 +418,5 @@
     test_regressions();
     test_nearly_integral(reporter);
     test_really_a_rect(reporter);
+    test_crbug_422693(reporter);
 }
diff --git a/src/third_party/skia/tests/ARGBImageEncoderTest.cpp b/src/third_party/skia/tests/ARGBImageEncoderTest.cpp
deleted file mode 100644
index 4d16f4c..0000000
--- a/src/third_party/skia/tests/ARGBImageEncoderTest.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkImageEncoder.h"
-
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkStream.h"
-#include "Test.h"
-
-static SkColorType gColorTypes[] = {
-    kRGB_565_SkColorType,
-    kN32_SkColorType,
-};
-
-DEF_TEST(ARGBImageEncoder, reporter) {
-    // Bytes we expect to get:
-    const int kWidth = 3;
-    const int kHeight = 5;
-    const unsigned char comparisonBuffer[] = {
-        // kHeight rows, each with kWidth pixels, premultiplied ARGB for each pixel
-        0xff,0xff,0x00,0x00, 0xff,0xff,0x00,0x00, 0xff,0xff,0x00,0x00, // red
-        0xff,0x00,0xff,0x00, 0xff,0x00,0xff,0x00, 0xff,0x00,0xff,0x00, // green
-        0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, // blue
-        0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, // blue
-        0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, // blue
-    };
-
-    SkAutoTDelete<SkImageEncoder> enc(CreateARGBImageEncoder());
-    for (size_t ctIndex = 0; ctIndex < SK_ARRAY_COUNT(gColorTypes); ++ctIndex) {
-        // A bitmap that should generate the above bytes:
-        SkBitmap bitmap;
-        {
-            bitmap.allocPixels(SkImageInfo::Make(kWidth, kHeight, gColorTypes[ctIndex],
-                                                 kOpaque_SkAlphaType));
-            bitmap.eraseColor(SK_ColorBLUE);
-            // Change rows [0,1] from blue to [red,green].
-            SkCanvas canvas(bitmap);
-            SkPaint paint;
-            paint.setColor(SK_ColorRED);
-            canvas.drawIRect(SkIRect::MakeLTRB(0, 0, kWidth, 1), paint);
-            paint.setColor(SK_ColorGREEN);
-            canvas.drawIRect(SkIRect::MakeLTRB(0, 1, kWidth, 2), paint);
-        }
-
-        // Transform the bitmap.
-        int bufferSize = bitmap.width() * bitmap.height() * 4;
-        SkAutoMalloc pixelBufferManager(bufferSize);
-        char *pixelBuffer = static_cast<char *>(pixelBufferManager.get());
-        SkMemoryWStream out(pixelBuffer, bufferSize);
-        REPORTER_ASSERT(reporter, enc->encodeStream(&out, bitmap, SkImageEncoder::kDefaultQuality));
-
-        // Confirm we got the expected results.
-        REPORTER_ASSERT(reporter, bufferSize == sizeof(comparisonBuffer));
-        REPORTER_ASSERT(reporter, memcmp(pixelBuffer, comparisonBuffer, bufferSize) == 0);
-    }
-}
diff --git a/src/third_party/skia/tests/AnnotationTest.cpp b/src/third_party/skia/tests/AnnotationTest.cpp
index 274d53e..cc2fd1f 100644
--- a/src/third_party/skia/tests/AnnotationTest.cpp
+++ b/src/third_party/skia/tests/AnnotationTest.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -8,8 +7,8 @@
 #include "SkAnnotation.h"
 #include "SkCanvas.h"
 #include "SkData.h"
-#include "SkPDFDevice.h"
-#include "SkPDFDocument.h"
+#include "SkDocument.h"
+#include "SkStream.h"
 #include "Test.h"
 
 /** Returns true if data (may contain null characters) contains needle (null
@@ -32,62 +31,45 @@
     SkCanvas canvas(bm);
     SkRect r = SkRect::MakeWH(SkIntToScalar(10), SkIntToScalar(10));
 
-    SkAutoDataUnref data(SkData::NewWithCString("http://www.gooogle.com"));
+    sk_sp<SkData> data(SkData::MakeWithCString("http://www.gooogle.com"));
 
     REPORTER_ASSERT(reporter, 0 == *bm.getAddr32(0, 0));
     SkAnnotateRectWithURL(&canvas, r, data.get());
     REPORTER_ASSERT(reporter, 0 == *bm.getAddr32(0, 0));
 }
 
-struct testCase {
-    SkPDFDocument::Flags flags;
-    bool expectAnnotations;
-};
-
 DEF_TEST(Annotation_PdfLink, reporter) {
-    SkISize size = SkISize::Make(612, 792);
-    SkMatrix initialTransform;
-    initialTransform.reset();
-    SkPDFDevice device(size, size, initialTransform);
-    SkCanvas canvas(&device);
+    REQUIRE_PDF_DOCUMENT(Annotation_PdfLink, reporter);
+    SkDynamicMemoryWStream outStream;
+    sk_sp<SkDocument> doc(SkDocument::MakePDF(&outStream));
+    SkCanvas* canvas = doc->beginPage(612.0f, 792.0f);
+    REPORTER_ASSERT(reporter, canvas);
 
     SkRect r = SkRect::MakeXYWH(SkIntToScalar(72), SkIntToScalar(72),
                                 SkIntToScalar(288), SkIntToScalar(72));
-    SkAutoDataUnref data(SkData::NewWithCString("http://www.gooogle.com"));
-    SkAnnotateRectWithURL(&canvas, r, data.get());
+    sk_sp<SkData> data(SkData::MakeWithCString("http://www.gooogle.com"));
+    SkAnnotateRectWithURL(canvas, r, data.get());
 
-    testCase tests[] = {{(SkPDFDocument::Flags)0, true},
-                        {SkPDFDocument::kNoLinks_Flags, false}};
-    for (size_t testNum = 0; testNum < SK_ARRAY_COUNT(tests); testNum++) {
-        SkPDFDocument doc(tests[testNum].flags);
-        doc.appendPage(&device);
-        SkDynamicMemoryWStream outStream;
-        doc.emitPDF(&outStream);
-        SkAutoDataUnref out(outStream.copyToData());
-        const char* rawOutput = (const char*)out->data();
+    doc->close();
+    sk_sp<SkData> out = outStream.detachAsData();
+    const char* rawOutput = (const char*)out->data();
 
-        REPORTER_ASSERT(reporter,
-            ContainsString(rawOutput, out->size(), "/Annots ")
-            == tests[testNum].expectAnnotations);
-    }
+    REPORTER_ASSERT(reporter, ContainsString(rawOutput, out->size(), "/Annots "));
 }
 
 DEF_TEST(Annotation_NamedDestination, reporter) {
-    SkISize size = SkISize::Make(612, 792);
-    SkMatrix initialTransform;
-    initialTransform.reset();
-    SkPDFDevice device(size, size, initialTransform);
-    SkCanvas canvas(&device);
+    REQUIRE_PDF_DOCUMENT(Annotation_NamedDestination, reporter);
+    SkDynamicMemoryWStream outStream;
+    sk_sp<SkDocument> doc(SkDocument::MakePDF(&outStream));
+    SkCanvas* canvas = doc->beginPage(612.0f, 792.0f);
+    REPORTER_ASSERT(reporter, canvas);
 
     SkPoint p = SkPoint::Make(SkIntToScalar(72), SkIntToScalar(72));
-    SkAutoDataUnref data(SkData::NewWithCString("example"));
-    SkAnnotateNamedDestination(&canvas, p, data.get());
+    sk_sp<SkData> data(SkData::MakeWithCString("example"));
+    SkAnnotateNamedDestination(canvas, p, data.get());
 
-    SkPDFDocument doc;
-    doc.appendPage(&device);
-    SkDynamicMemoryWStream outStream;
-    doc.emitPDF(&outStream);
-    SkAutoDataUnref out(outStream.copyToData());
+    doc->close();
+    sk_sp<SkData> out = outStream.detachAsData();
     const char* rawOutput = (const char*)out->data();
 
     REPORTER_ASSERT(reporter,
diff --git a/src/third_party/skia/tests/ApplyGammaTest.cpp b/src/third_party/skia/tests/ApplyGammaTest.cpp
new file mode 100644
index 0000000..83b5968
--- /dev/null
+++ b/src/third_party/skia/tests/ApplyGammaTest.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <initializer_list>
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+
+#include "SkCanvas.h"
+#include "SkColorFilter.h"
+#include "SkSurface.h"
+#include "SkUtils.h"
+
+/** convert 0..1 linear value to 0..1 srgb */
+static float linear_to_srgb(float linear) {
+    if (linear <= 0.0031308) {
+        return linear * 12.92f;
+    } else {
+        return 1.055f * powf(linear, 1.f / 2.4f) - 0.055f;
+    }
+}
+
+/** convert 0..1 srgb value to 0..1 linear */
+static float srgb_to_linear(float srgb) {
+    if (srgb <= 0.04045f) {
+        return srgb / 12.92f;
+    } else {
+        return powf((srgb + 0.055f) / 1.055f, 2.4f);
+    }
+}
+
+bool check_gamma(uint32_t src, uint32_t dst, bool toSRGB, float error,
+                 uint32_t* expected) {
+    bool result = true;
+    uint32_t expectedColor = src & 0xff000000;
+
+    // Alpha should always be exactly preserved.
+    if ((src & 0xff000000) != (dst & 0xff000000)) {
+        result = false;
+    }
+
+    // need to unpremul before we can perform srgb magic
+    float invScale = 0;
+    float alpha = SkGetPackedA32(src);
+    if (alpha) {
+        invScale = 255.0f / alpha;
+    }
+
+    for (int c = 0; c < 3; ++c) {
+        float srcComponent = ((src & (0xff << (c * 8))) >> (c * 8)) * invScale;
+        float lower = SkTMax(0.f, srcComponent - error);
+        float upper = SkTMin(255.f, srcComponent + error);
+        if (toSRGB) {
+            lower = linear_to_srgb(lower / 255.f);
+            upper = linear_to_srgb(upper / 255.f);
+        } else {
+            lower = srgb_to_linear(lower / 255.f);
+            upper = srgb_to_linear(upper / 255.f);
+        }
+        lower *= alpha;
+        upper *= alpha;
+        SkASSERT(lower >= 0.f && lower <= 255.f);
+        SkASSERT(upper >= 0.f && upper <= 255.f);
+        uint8_t dstComponent = (dst & (0xff << (c * 8))) >> (c * 8);
+        if (dstComponent < SkScalarFloorToInt(lower) ||
+            dstComponent > SkScalarCeilToInt(upper)) {
+            result = false;
+        }
+        uint8_t expectedComponent = SkScalarRoundToInt((lower + upper) * 0.5f);
+        expectedColor |= expectedComponent << (c * 8);
+    }
+
+    *expected = expectedColor;
+    return result;
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ApplyGamma, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    static const int kW = 256;
+    static const int kH = 256;
+    static const size_t kRowBytes = sizeof(uint32_t) * kW;
+
+    GrSurfaceDesc baseDesc;
+    baseDesc.fConfig = kRGBA_8888_GrPixelConfig;
+    baseDesc.fWidth = kW;
+    baseDesc.fHeight = kH;
+
+    const SkImageInfo ii = SkImageInfo::MakeN32Premul(kW, kH);
+
+    SkAutoTMalloc<uint32_t> srcPixels(kW * kH);
+    for (int y = 0; y < kH; ++y) {
+        for (int x = 0; x < kW; ++x) {
+            srcPixels.get()[y*kW+x] = SkPreMultiplyARGB(x, y, x, 0xFF);
+        }
+    }
+
+    SkBitmap bm;
+    bm.installPixels(ii, srcPixels.get(), kRowBytes);
+
+    SkAutoTMalloc<uint32_t> read(kW * kH);
+
+    // We allow more error on GPUs with lower precision shader variables.
+    float error = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.2f : 0.5f;
+
+    for (auto toSRGB : { false, true }) {
+        sk_sp<SkSurface> dst(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii));
+
+        if (!dst) {
+            ERRORF(reporter, "Could not create surfaces for copy surface test.");
+            continue;
+        }
+
+        SkCanvas* dstCanvas = dst->getCanvas();
+
+        dstCanvas->clear(SK_ColorRED);
+        dstCanvas->flush();
+
+        SkPaint gammaPaint;
+        gammaPaint.setBlendMode(SkBlendMode::kSrc);
+        gammaPaint.setColorFilter(toSRGB ? SkColorFilter::MakeLinearToSRGBGamma()
+                                         : SkColorFilter::MakeSRGBToLinearGamma());
+
+        dstCanvas->drawBitmap(bm, 0, 0, &gammaPaint);
+        dstCanvas->flush();
+
+        sk_memset32(read.get(), 0, kW * kH);
+        if (!dstCanvas->readPixels(ii, read.get(), kRowBytes, 0, 0)) {
+            ERRORF(reporter, "Error calling readPixels");
+            continue;
+        }
+
+        bool abort = false;
+        // Validate that pixels were copied/transformed correctly.
+        for (int y = 0; y < kH && !abort; ++y) {
+            for (int x = 0; x < kW && !abort; ++x) {
+                uint32_t r = read.get()[y * kW + x];
+                uint32_t s = srcPixels.get()[y * kW + x];
+                uint32_t expected;
+                if (!check_gamma(s, r, toSRGB, error, &expected)) {
+                    ERRORF(reporter, "Expected dst %d,%d to contain 0x%08x "
+                           "from src 0x%08x and mode %s. Got %08x", x, y, expected, s,
+                           toSRGB ? "ToSRGB" : "ToLinear", r);
+                    abort = true;
+                    break;
+                }
+            }
+        }
+    }
+}
+#endif
diff --git a/src/third_party/skia/tests/ArenaAllocTest.cpp b/src/third_party/skia/tests/ArenaAllocTest.cpp
new file mode 100644
index 0000000..137e60e
--- /dev/null
+++ b/src/third_party/skia/tests/ArenaAllocTest.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "SkArenaAlloc.h"
+#include "SkRefCnt.h"
+
+namespace {
+
+    static int created, destroyed;
+
+    struct Foo {
+        Foo() : x(-2), y(-3.0f) { created++; }
+        Foo(int X, float Y) : x(X), y(Y) { created++; }
+        ~Foo() { destroyed++; }
+
+        int x;
+        float y;
+    };
+
+    struct Big {
+        Big() {}
+        uint32_t array[128];
+    };
+
+    struct Node {
+        Node(Node* n) : next(n) { created++; }
+        ~Node() {
+            destroyed++;
+            if (next) {
+                next->~Node();
+            }
+        }
+        Node *next;
+    };
+
+    struct Start {
+        ~Start() {
+            if (start) {
+                start->~Node();
+            }
+        }
+        Node* start;
+    };
+
+    struct FooRefCnt : public SkRefCnt {
+        FooRefCnt() : x(-2), y(-3.0f) { created++; }
+        FooRefCnt(int X, float Y) : x(X), y(Y) { created++; }
+        ~FooRefCnt() { destroyed++; }
+
+        int x;
+        float y;
+    };
+
+}
+
+struct WithDtor {
+    ~WithDtor() { }
+};
+
+DEF_TEST(ArenaAlloc, r) {
+
+    {
+        created = 0;
+        destroyed = 0;
+
+        SkArenaAlloc arena{0};
+        REPORTER_ASSERT(r, *arena.make<int>(3) == 3);
+        Foo* foo = arena.make<Foo>(3, 4.0f);
+        REPORTER_ASSERT(r, foo->x == 3);
+        REPORTER_ASSERT(r, foo->y == 4.0f);
+        REPORTER_ASSERT(r, created == 1);
+        REPORTER_ASSERT(r, destroyed == 0);
+        arena.makeArrayDefault<int>(10);
+        int* zeroed = arena.makeArray<int>(10);
+        for (int i = 0; i < 10; i++) {
+            REPORTER_ASSERT(r, zeroed[i] == 0);
+        }
+        Foo* fooArray = arena.makeArrayDefault<Foo>(10);
+        REPORTER_ASSERT(r, fooArray[3].x == -2);
+        REPORTER_ASSERT(r, fooArray[4].y == -3.0f);
+        REPORTER_ASSERT(r, created == 11);
+        REPORTER_ASSERT(r, destroyed == 0);
+        arena.make<typename std::aligned_storage<10,8>::type>();
+    }
+    REPORTER_ASSERT(r, created == 11);
+    REPORTER_ASSERT(r, destroyed == 11);
+
+    {
+        created = 0;
+        destroyed = 0;
+        SkSTArenaAlloc<64> arena;
+
+        REPORTER_ASSERT(r, *arena.make<int>(3) == 3);
+        Foo* foo = arena.make<Foo>(3, 4.0f);
+        REPORTER_ASSERT(r, foo->x == 3);
+        REPORTER_ASSERT(r, foo->y == 4.0f);
+        REPORTER_ASSERT(r, created == 1);
+        REPORTER_ASSERT(r, destroyed == 0);
+        arena.makeArrayDefault<int>(10);
+        int* zeroed = arena.makeArray<int>(10);
+        for (int i = 0; i < 10; i++) {
+            REPORTER_ASSERT(r, zeroed[i] == 0);
+        }
+        Foo* fooArray = arena.makeArrayDefault<Foo>(10);
+        REPORTER_ASSERT(r, fooArray[3].x == -2);
+        REPORTER_ASSERT(r, fooArray[4].y == -3.0f);
+        REPORTER_ASSERT(r, created == 11);
+        REPORTER_ASSERT(r, destroyed == 0);
+        arena.make<typename std::aligned_storage<10,8>::type>();
+    }
+    REPORTER_ASSERT(r, created == 11);
+    REPORTER_ASSERT(r, destroyed == 11);
+
+    {
+        created = 0;
+        destroyed = 0;
+        std::unique_ptr<char[]> block{new char[1024]};
+        SkArenaAlloc arena{block.get(), 1024, 0};
+
+        REPORTER_ASSERT(r, *arena.make<int>(3) == 3);
+        Foo* foo = arena.make<Foo>(3, 4.0f);
+        REPORTER_ASSERT(r, foo->x == 3);
+        REPORTER_ASSERT(r, foo->y == 4.0f);
+        REPORTER_ASSERT(r, created == 1);
+        REPORTER_ASSERT(r, destroyed == 0);
+        arena.makeArrayDefault<int>(10);
+        int* zeroed = arena.makeArray<int>(10);
+        for (int i = 0; i < 10; i++) {
+            REPORTER_ASSERT(r, zeroed[i] == 0);
+        }
+        Foo* fooArray = arena.makeArrayDefault<Foo>(10);
+        REPORTER_ASSERT(r, fooArray[3].x == -2);
+        REPORTER_ASSERT(r, fooArray[4].y == -3.0f);
+        REPORTER_ASSERT(r, created == 11);
+        REPORTER_ASSERT(r, destroyed == 0);
+        arena.make<typename std::aligned_storage<10,8>::type>();
+    }
+    REPORTER_ASSERT(r, created == 11);
+    REPORTER_ASSERT(r, destroyed == 11);
+
+    {
+        SkSTArenaAlloc<64> arena;
+        arena.makeArrayDefault<char>(256);
+        arena.reset();
+        arena.reset();
+    }
+
+    {
+        created = 0;
+        destroyed = 0;
+        SkSTArenaAlloc<64> arena;
+
+        Start start;
+        Node* current = nullptr;
+        for (int i = 0; i < 128; i++) {
+            uint64_t* temp = arena.makeArrayDefault<uint64_t>(sizeof(Node) / sizeof(Node*));
+            current = new (temp)Node(current);
+        }
+        start.start = current;
+    }
+
+    REPORTER_ASSERT(r, created == 128);
+    REPORTER_ASSERT(r, destroyed == 128);
+
+    {
+        created = 0;
+        destroyed = 0;
+        SkSTArenaAlloc<64> arena;
+
+        sk_sp<FooRefCnt> f = arena.makeSkSp<FooRefCnt>(4, 5.0f);
+        REPORTER_ASSERT(r, f->x == 4);
+        REPORTER_ASSERT(r, f->y == 5.0f);
+        REPORTER_ASSERT(r, created == 1);
+        REPORTER_ASSERT(r, destroyed == 0);
+    }
+    REPORTER_ASSERT(r, created == 1);
+    REPORTER_ASSERT(r, destroyed == 1);
+}
diff --git a/src/third_party/skia/tests/AsADashTest.cpp b/src/third_party/skia/tests/AsADashTest.cpp
index 47f1971..2077b66 100644
--- a/src/third_party/skia/tests/AsADashTest.cpp
+++ b/src/third_party/skia/tests/AsADashTest.cpp
@@ -12,7 +12,7 @@
 #include "SkCornerPathEffect.h"
 
 DEF_TEST(AsADashTest_noneDash, reporter) {
-    SkAutoTUnref<SkCornerPathEffect> pe(SkCornerPathEffect::Create(1.0));
+    sk_sp<SkPathEffect> pe(SkCornerPathEffect::Make(1.0));
     SkPathEffect::DashInfo info;
 
     SkPathEffect::DashType dashType = pe->asADash(&info);
@@ -22,9 +22,9 @@
 DEF_TEST(AsADashTest_nullInfo, reporter) {
     SkScalar inIntervals[] = { 4.0, 2.0, 1.0, 3.0 };
     const SkScalar phase = 2.0;
-    SkAutoTUnref<SkDashPathEffect> pe(SkDashPathEffect::Create(inIntervals, 4, phase));
+    sk_sp<SkPathEffect> pe(SkDashPathEffect::Make(inIntervals, 4, phase));
 
-    SkPathEffect::DashType dashType = pe->asADash(NULL);
+    SkPathEffect::DashType dashType = pe->asADash(nullptr);
     REPORTER_ASSERT(reporter, SkPathEffect::kDash_DashType == dashType);
 }
 
@@ -33,7 +33,7 @@
     SkScalar totalIntSum = 10.0;
     const SkScalar phase = 2.0;
 
-    SkAutoTUnref<SkDashPathEffect> pe(SkDashPathEffect::Create(inIntervals, 4, phase));
+    sk_sp<SkPathEffect> pe(SkDashPathEffect::Make(inIntervals, 4, phase));
 
     SkPathEffect::DashInfo info;
 
diff --git a/src/third_party/skia/tests/AtomicTest.cpp b/src/third_party/skia/tests/AtomicTest.cpp
deleted file mode 100644
index 8b5ac9b..0000000
--- a/src/third_party/skia/tests/AtomicTest.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkThread.h"
-#include "SkThreadUtils.h"
-#include "SkTypes.h"
-#include "Test.h"
-
-struct AddInfo {
-    int32_t valueToAdd;
-    int timesToAdd;
-    unsigned int processorAffinity;
-};
-
-static int32_t base = 0;
-
-static AddInfo gAdds[] = {
-    { 3, 100, 23 },
-    { 2, 200, 2 },
-    { 7, 150, 17 },
-};
-
-static void addABunchOfTimes(void* data) {
-    AddInfo* addInfo = static_cast<AddInfo*>(data);
-    for (int i = 0; i < addInfo->timesToAdd; i++) {
-        sk_atomic_add(&base, addInfo->valueToAdd);
-    }
-}
-
-DEF_TEST(Atomic, reporter) {
-    int32_t total = base;
-    SkThread* threads[SK_ARRAY_COUNT(gAdds)];
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gAdds); i++) {
-        total += gAdds[i].valueToAdd * gAdds[i].timesToAdd;
-    }
-    // Start the threads
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gAdds); i++) {
-        threads[i] = new SkThread(addABunchOfTimes, &gAdds[i]);
-        threads[i]->setProcessorAffinity(gAdds[i].processorAffinity);
-        threads[i]->start();
-    }
-
-    // Now end the threads
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gAdds); i++) {
-        threads[i]->join();
-        delete threads[i];
-    }
-    REPORTER_ASSERT(reporter, total == base);
-    // Ensure that the returned value from sk_atomic_add is correct.
-    int32_t valueToModify = 3;
-    const int32_t originalValue = valueToModify;
-    REPORTER_ASSERT(reporter, originalValue == sk_atomic_add(&valueToModify, 7));
-}
diff --git a/src/third_party/skia/tests/BBoxHierarchyTest.cpp b/src/third_party/skia/tests/BBoxHierarchyTest.cpp
deleted file mode 100644
index 71b9699..0000000
--- a/src/third_party/skia/tests/BBoxHierarchyTest.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Test.h"
-#include "SkRandom.h"
-#include "SkRTree.h"
-#include "SkTSort.h"
-
-static const size_t RTREE_MIN_CHILDREN = 6;
-static const size_t RTREE_MAX_CHILDREN = 11;
-
-static const int NUM_RECTS = 200;
-static const size_t NUM_ITERATIONS = 100;
-static const size_t NUM_QUERIES = 50;
-
-static const SkScalar MAX_SIZE = 1000.0f;
-
-struct DataRect {
-    SkRect rect;
-    void* data;
-};
-
-static SkRect random_rect(SkRandom& rand) {
-    SkRect rect = {0,0,0,0};
-    while (rect.isEmpty()) {
-        rect.fLeft   = rand.nextRangeF(0, MAX_SIZE);
-        rect.fRight  = rand.nextRangeF(0, MAX_SIZE);
-        rect.fTop    = rand.nextRangeF(0, MAX_SIZE);
-        rect.fBottom = rand.nextRangeF(0, MAX_SIZE);
-        rect.sort();
-    }
-    return rect;
-}
-
-static void random_data_rects(SkRandom& rand, DataRect out[], int n) {
-    for (int i = 0; i < n; ++i) {
-        out[i].rect = random_rect(rand);
-        out[i].data = reinterpret_cast<void*>(i);
-    }
-}
-
-static bool verify_query(SkRect query, DataRect rects[],
-                         SkTDArray<void*>& found) {
-    // TODO(mtklein): no need to do this after everything's SkRects
-    query.roundOut();
-
-    SkTDArray<void*> expected;
-    // manually intersect with every rectangle
-    for (int i = 0; i < NUM_RECTS; ++i) {
-        if (SkRect::Intersects(query, rects[i].rect)) {
-            expected.push(rects[i].data);
-        }
-    }
-
-    if (expected.count() != found.count()) {
-        return false;
-    }
-
-    if (0 == expected.count()) {
-        return true;
-    }
-
-    // Just cast to long since sorting by the value of the void*'s was being problematic...
-    SkTQSort(reinterpret_cast<long*>(expected.begin()),
-             reinterpret_cast<long*>(expected.end() - 1));
-    SkTQSort(reinterpret_cast<long*>(found.begin()),
-             reinterpret_cast<long*>(found.end() - 1));
-    return found == expected;
-}
-
-static void run_queries(skiatest::Reporter* reporter, SkRandom& rand, DataRect rects[],
-                        SkBBoxHierarchy& tree) {
-    for (size_t i = 0; i < NUM_QUERIES; ++i) {
-        SkTDArray<void*> hits;
-        SkRect query = random_rect(rand);
-        tree.search(query, &hits);
-        REPORTER_ASSERT(reporter, verify_query(query, rects, hits));
-    }
-}
-
-static void tree_test_main(SkBBoxHierarchy* tree, int minChildren, int maxChildren,
-                           skiatest::Reporter* reporter) {
-    DataRect rects[NUM_RECTS];
-    SkRandom rand;
-    REPORTER_ASSERT(reporter, tree);
-
-    int expectedDepthMin = -1;
-    int expectedDepthMax = -1;
-
-    int tmp = NUM_RECTS;
-    if (maxChildren > 0) {
-        while (tmp > 0) {
-            tmp -= static_cast<int>(pow(static_cast<double>(maxChildren),
-                                    static_cast<double>(expectedDepthMin + 1)));
-            ++expectedDepthMin;
-        }
-    }
-
-    tmp = NUM_RECTS;
-    if (minChildren > 0) {
-        while (tmp > 0) {
-            tmp -= static_cast<int>(pow(static_cast<double>(minChildren),
-                                    static_cast<double>(expectedDepthMax + 1)));
-            ++expectedDepthMax;
-        }
-    }
-
-    for (size_t i = 0; i < NUM_ITERATIONS; ++i) {
-        random_data_rects(rand, rects, NUM_RECTS);
-
-        // First try bulk-loaded inserts
-        for (int i = 0; i < NUM_RECTS; ++i) {
-            tree->insert(rects[i].data, rects[i].rect, true);
-        }
-        tree->flushDeferredInserts();
-        run_queries(reporter, rand, rects, *tree);
-        REPORTER_ASSERT(reporter, NUM_RECTS == tree->getCount());
-        REPORTER_ASSERT(reporter,
-            ((expectedDepthMin <= 0) || (expectedDepthMin <= tree->getDepth())) &&
-            ((expectedDepthMax <= 0) || (expectedDepthMax >= tree->getDepth())));
-        tree->clear();
-        REPORTER_ASSERT(reporter, 0 == tree->getCount());
-
-        // Then try immediate inserts
-        tree->insert(rects[0].data, rects[0].rect);
-        tree->flushDeferredInserts();
-        for (int i = 1; i < NUM_RECTS; ++i) {
-            tree->insert(rects[i].data, rects[i].rect);
-        }
-        run_queries(reporter, rand, rects, *tree);
-        REPORTER_ASSERT(reporter, NUM_RECTS == tree->getCount());
-        REPORTER_ASSERT(reporter,
-            ((expectedDepthMin <= 0) || (expectedDepthMin <= tree->getDepth())) &&
-            ((expectedDepthMax <= 0) || (expectedDepthMax >= tree->getDepth())));
-        tree->clear();
-        REPORTER_ASSERT(reporter, 0 == tree->getCount());
-
-        // And for good measure try immediate inserts, but in reversed order
-        tree->insert(rects[NUM_RECTS - 1].data, rects[NUM_RECTS - 1].rect);
-        tree->flushDeferredInserts();
-        for (int i = NUM_RECTS - 2; i >= 0; --i) {
-            tree->insert(rects[i].data, rects[i].rect);
-        }
-        run_queries(reporter, rand, rects, *tree);
-        REPORTER_ASSERT(reporter, NUM_RECTS == tree->getCount());
-        REPORTER_ASSERT(reporter,
-            ((expectedDepthMin < 0) || (expectedDepthMin <= tree->getDepth())) &&
-            ((expectedDepthMax < 0) || (expectedDepthMax >= tree->getDepth())));
-        tree->clear();
-        REPORTER_ASSERT(reporter, 0 == tree->getCount());
-    }
-}
-
-DEF_TEST(BBoxHierarchy, reporter) {
-    // RTree
-    {
-        SkRTree* rtree = SkRTree::Create(RTREE_MIN_CHILDREN, RTREE_MAX_CHILDREN);
-        SkAutoUnref au(rtree);
-        tree_test_main(rtree, RTREE_MIN_CHILDREN, RTREE_MAX_CHILDREN, reporter);
-
-        // Rtree that orders input rectangles on deferred insert.
-        SkRTree* unsortedRtree = SkRTree::Create(RTREE_MIN_CHILDREN, RTREE_MAX_CHILDREN, 1, false);
-        SkAutoUnref auo(unsortedRtree);
-        tree_test_main(unsortedRtree, RTREE_MIN_CHILDREN, RTREE_MAX_CHILDREN, reporter);
-    }
-}
diff --git a/src/third_party/skia/tests/BadIcoTest.cpp b/src/third_party/skia/tests/BadIcoTest.cpp
new file mode 100644
index 0000000..670c2ac
--- /dev/null
+++ b/src/third_party/skia/tests/BadIcoTest.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Resources.h"
+#include "Test.h"
+
+#include "SkBitmap.h"
+#include "SkCodec.h"
+#include "SkOSFile.h"
+#include "SkOSPath.h"
+#include "SkStream.h"
+
+DEF_TEST(BadImage, reporter) {
+    const char* const badImages [] = {
+        "sigabort_favicon.ico",
+        "sigsegv_favicon.ico",
+        "sigsegv_favicon_2.ico",
+        "ico_leak01.ico",
+        "ico_fuzz0.ico",
+        "ico_fuzz1.ico",
+        "skbug3442.webp",
+        "skbug3429.webp",
+        "b38116746.ico",
+    };
+
+    const char* badImagesFolder = "invalid_images";
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(badImages); ++i) {
+        SkString resourcePath = SkOSPath::Join(badImagesFolder, badImages[i]);
+        std::unique_ptr<SkStream> stream(GetResourceAsStream(resourcePath.c_str()));
+        std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
+
+        // These images are corrupt.  It's not important whether we succeed/fail in codec
+        // creation or decoding.  We just want to make sure that we don't crash.
+        if (codec) {
+            SkBitmap bm;
+            bm.allocPixels(codec->getInfo());
+            codec->getPixels(codec->getInfo(), bm.getPixels(),
+                    bm.rowBytes());
+        }
+    }
+}
diff --git a/src/third_party/skia/tests/BitSetTest.cpp b/src/third_party/skia/tests/BitSetTest.cpp
index da02376..6fc8351 100644
--- a/src/third_party/skia/tests/BitSetTest.cpp
+++ b/src/third_party/skia/tests/BitSetTest.cpp
@@ -10,67 +10,30 @@
 
 DEF_TEST(BitSet, reporter) {
     SkBitSet set0(65536);
-    REPORTER_ASSERT(reporter, set0.isBitSet(0) == false);
-    REPORTER_ASSERT(reporter, set0.isBitSet(32767) == false);
-    REPORTER_ASSERT(reporter, set0.isBitSet(65535) == false);
+    REPORTER_ASSERT(reporter, set0.has(0) == false);
+    REPORTER_ASSERT(reporter, set0.has(32767) == false);
+    REPORTER_ASSERT(reporter, set0.has(65535) == false);
 
-    SkBitSet set1(65536);
-    REPORTER_ASSERT(reporter, set0 == set1);
-
-    set0.setBit(22, true);
-    REPORTER_ASSERT(reporter, set0.isBitSet(22) == true);
-    set0.setBit(24, true);
-    REPORTER_ASSERT(reporter, set0.isBitSet(24) == true);
-    set0.setBit(35, true);  // on a different DWORD
-    REPORTER_ASSERT(reporter, set0.isBitSet(35) == true);
-    set0.setBit(22, false);
-    REPORTER_ASSERT(reporter, set0.isBitSet(22) == false);
-    REPORTER_ASSERT(reporter, set0.isBitSet(24) == true);
-    REPORTER_ASSERT(reporter, set0.isBitSet(35) == true);
+    set0.set(22);
+    REPORTER_ASSERT(reporter, set0.has(22) == true);
+    set0.set(24);
+    REPORTER_ASSERT(reporter, set0.has(24) == true);
+    set0.set(35);  // on a different DWORD
+    REPORTER_ASSERT(reporter, set0.has(35) == true);
+    REPORTER_ASSERT(reporter, set0.has(24) == true);
+    REPORTER_ASSERT(reporter, set0.has(35) == true);
 
     SkTDArray<unsigned int> data;
     set0.exportTo(&data);
-    REPORTER_ASSERT(reporter, data.count() == 2);
-    REPORTER_ASSERT(reporter, data[0] == 24);
-    REPORTER_ASSERT(reporter, data[1] == 35);
+    REPORTER_ASSERT(reporter, data.count() == 3);
+    REPORTER_ASSERT(reporter, data[0] == 22);
+    REPORTER_ASSERT(reporter, data[1] == 24);
+    REPORTER_ASSERT(reporter, data[2] == 35);
 
-    set1.setBit(12345, true);
-    set1.orBits(set0);
-    REPORTER_ASSERT(reporter, set0.isBitSet(12345) == false);
-    REPORTER_ASSERT(reporter, set1.isBitSet(12345) == true);
-    REPORTER_ASSERT(reporter, set1.isBitSet(22) == false);
-    REPORTER_ASSERT(reporter, set1.isBitSet(24) == true);
-    REPORTER_ASSERT(reporter, set0.isBitSet(35) == true);
-    REPORTER_ASSERT(reporter, set1 != set0);
-
-    set1.clearAll();
-    REPORTER_ASSERT(reporter, set0.isBitSet(12345) == false);
-    REPORTER_ASSERT(reporter, set1.isBitSet(12345) == false);
-    REPORTER_ASSERT(reporter, set1.isBitSet(22) == false);
-    REPORTER_ASSERT(reporter, set1.isBitSet(24) == false);
-    REPORTER_ASSERT(reporter, set1.isBitSet(35) == false);
-
-    set1.orBits(set0);
-    REPORTER_ASSERT(reporter, set1 == set0);
-
-    SkBitSet set2(1);
-    SkBitSet set3(1);
-    SkBitSet set4(4);
-    SkBitSet set5(33);
-
-    REPORTER_ASSERT(reporter, set2 == set3);
-    REPORTER_ASSERT(reporter, set2 != set4);
-    REPORTER_ASSERT(reporter, set2 != set5);
-
-    set2.setBit(0, true);
-    REPORTER_ASSERT(reporter, set2 != set5);
-    set5.setBit(0, true);
-    REPORTER_ASSERT(reporter, set2 != set5);
-    REPORTER_ASSERT(reporter, set2 != set3);
-    set3.setBit(0, true);
-    REPORTER_ASSERT(reporter, set2 == set3);
-    set3.clearAll();
-    set3 = set2;
-    set2 = set2;
-    REPORTER_ASSERT(reporter, set2 == set3);
+    SkBitSet set1(65536);
+    set1.set(12345);
+    REPORTER_ASSERT(reporter, set0.has(12345) == false);
+    REPORTER_ASSERT(reporter, set1.has(12345) == true);
+    REPORTER_ASSERT(reporter, set1.has(22) == false);
+    REPORTER_ASSERT(reporter, set0.has(35) == true);
 }
diff --git a/src/third_party/skia/tests/BitmapCopyTest.cpp b/src/third_party/skia/tests/BitmapCopyTest.cpp
index 3923846..09e44ce 100644
--- a/src/third_party/skia/tests/BitmapCopyTest.cpp
+++ b/src/third_party/skia/tests/BitmapCopyTest.cpp
@@ -7,67 +7,16 @@
 
 #include "SkBitmap.h"
 #include "SkRect.h"
+#include "SkTemplates.h"
 #include "Test.h"
-
-static const char* boolStr(bool value) {
-    return value ? "true" : "false";
-}
-
-// these are in the same order as the SkColorType enum
-static const char* gColorTypeName[] = {
-    "None", "A8", "565", "4444", "RGBA", "BGRA", "Index8"
-};
-
-static void report_opaqueness(skiatest::Reporter* reporter, const SkBitmap& src,
-                              const SkBitmap& dst) {
-    ERRORF(reporter, "src %s opaque:%d, dst %s opaque:%d",
-           gColorTypeName[src.colorType()], src.isOpaque(),
-           gColorTypeName[dst.colorType()], dst.isOpaque());
-}
-
-static bool canHaveAlpha(SkColorType ct) {
-    return kRGB_565_SkColorType != ct;
-}
-
-// copyTo() should preserve isOpaque when it makes sense
-static void test_isOpaque(skiatest::Reporter* reporter,
-                          const SkBitmap& srcOpaque, const SkBitmap& srcPremul,
-                          SkColorType dstColorType) {
-    SkBitmap dst;
-
-    if (canHaveAlpha(srcPremul.colorType()) && canHaveAlpha(dstColorType)) {
-        REPORTER_ASSERT(reporter, srcPremul.copyTo(&dst, dstColorType));
-        REPORTER_ASSERT(reporter, dst.colorType() == dstColorType);
-        if (srcPremul.isOpaque() != dst.isOpaque()) {
-            report_opaqueness(reporter, srcPremul, dst);
-        }
-    }
-
-    REPORTER_ASSERT(reporter, srcOpaque.copyTo(&dst, dstColorType));
-    REPORTER_ASSERT(reporter, dst.colorType() == dstColorType);
-    if (srcOpaque.isOpaque() != dst.isOpaque()) {
-        report_opaqueness(reporter, srcOpaque, dst);
-    }
-}
+#include "sk_tool_utils.h"
 
 static void init_src(const SkBitmap& bitmap) {
-    SkAutoLockPixels lock(bitmap);
     if (bitmap.getPixels()) {
-        if (bitmap.getColorTable()) {
-            sk_bzero(bitmap.getPixels(), bitmap.getSize());
-        } else {
-            bitmap.eraseColor(SK_ColorWHITE);
-        }
+        bitmap.eraseColor(SK_ColorWHITE);
     }
 }
 
-static SkColorTable* init_ctable(SkAlphaType alphaType) {
-    static const SkColor colors[] = {
-        SK_ColorBLACK, SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE
-    };
-    return new SkColorTable(colors, SK_ARRAY_COUNT(colors), alphaType);
-}
-
 struct Pair {
     SkColorType fColorType;
     const char* fValid;
@@ -81,60 +30,6 @@
 // reportCopyVerification()
 // writeCoordPixels()
 
-// Utility function to read the value of a given pixel in bm. All
-// values converted to uint32_t for simplification of comparisons.
-static uint32_t getPixel(int x, int y, const SkBitmap& bm) {
-    uint32_t val = 0;
-    uint16_t val16;
-    uint8_t val8;
-    SkAutoLockPixels lock(bm);
-    const void* rawAddr = bm.getAddr(x,y);
-
-    switch (bm.bytesPerPixel()) {
-        case 4:
-            memcpy(&val, rawAddr, sizeof(uint32_t));
-            break;
-        case 2:
-            memcpy(&val16, rawAddr, sizeof(uint16_t));
-            val = val16;
-            break;
-        case 1:
-            memcpy(&val8, rawAddr, sizeof(uint8_t));
-            val = val8;
-            break;
-        default:
-            break;
-    }
-    return val;
-}
-
-// Utility function to set value of any pixel in bm.
-// bm.getConfig() specifies what format 'val' must be
-// converted to, but at present uint32_t can handle all formats.
-static void setPixel(int x, int y, uint32_t val, SkBitmap& bm) {
-    uint16_t val16;
-    uint8_t val8;
-    SkAutoLockPixels lock(bm);
-    void* rawAddr = bm.getAddr(x,y);
-
-    switch (bm.bytesPerPixel()) {
-        case 4:
-            memcpy(rawAddr, &val, sizeof(uint32_t));
-            break;
-        case 2:
-            val16 = val & 0xFFFF;
-            memcpy(rawAddr, &val16, sizeof(uint16_t));
-            break;
-        case 1:
-            val8 = val & 0xFF;
-            memcpy(rawAddr, &val8, sizeof(uint8_t));
-            break;
-        default:
-            // Ignore.
-            break;
-    }
-}
-
 // Helper struct to contain pixel locations, while avoiding need for STL.
 struct Coordinates {
 
@@ -154,41 +49,13 @@
     }
 };
 
-// A function to verify that two bitmaps contain the same pixel values
-// at all coordinates indicated by coords. Simplifies verification of
-// copied bitmaps.
-static void reportCopyVerification(const SkBitmap& bm1, const SkBitmap& bm2,
-                            Coordinates& coords,
-                            const char* msg,
-                            skiatest::Reporter* reporter){
-    bool success = true;
-
-    // Confirm all pixels in the list match.
-    for (int i = 0; i < coords.length; ++i) {
-        success = success &&
-                  (getPixel(coords[i]->fX, coords[i]->fY, bm1) ==
-                   getPixel(coords[i]->fX, coords[i]->fY, bm2));
-    }
-
-    if (!success) {
-        ERRORF(reporter, "%s [colortype = %s]", msg,
-               gColorTypeName[bm1.colorType()]);
-    }
-}
-
-// Writes unique pixel values at locations specified by coords.
-static void writeCoordPixels(SkBitmap& bm, const Coordinates& coords) {
-    for (int i = 0; i < coords.length; ++i)
-        setPixel(coords[i]->fX, coords[i]->fY, i, bm);
-}
-
 static const Pair gPairs[] = {
-    { kUnknown_SkColorType,     "000000"  },
-    { kAlpha_8_SkColorType,     "010101"  },
-    { kIndex_8_SkColorType,     "011111"  },
-    { kRGB_565_SkColorType,     "010101"  },
-    { kARGB_4444_SkColorType,   "010111"  },
-    { kN32_SkColorType,         "010111"  },
+    { kUnknown_SkColorType,     "0000000"  },
+    { kAlpha_8_SkColorType,     "0100000"  },
+    { kRGB_565_SkColorType,     "0101011"  },
+    { kARGB_4444_SkColorType,   "0101111"  },
+    { kN32_SkColorType,         "0101111"  },
+    { kRGBA_F16_SkColorType,    "0101011"  },
 };
 
 static const int W = 20;
@@ -196,19 +63,13 @@
 
 static void setup_src_bitmaps(SkBitmap* srcOpaque, SkBitmap* srcPremul,
                               SkColorType ct) {
-    SkColorTable* ctOpaque = NULL;
-    SkColorTable* ctPremul = NULL;
-    if (kIndex_8_SkColorType == ct) {
-        ctOpaque = init_ctable(kOpaque_SkAlphaType);
-        ctPremul = init_ctable(kPremul_SkAlphaType);
+    sk_sp<SkColorSpace> colorSpace = nullptr;
+    if (kRGBA_F16_SkColorType == ct) {
+        colorSpace = SkColorSpace::MakeSRGBLinear();
     }
 
-    srcOpaque->allocPixels(SkImageInfo::Make(W, H, ct, kOpaque_SkAlphaType),
-                           NULL, ctOpaque);
-    srcPremul->allocPixels(SkImageInfo::Make(W, H, ct, kPremul_SkAlphaType),
-                           NULL, ctPremul);
-    SkSafeUnref(ctOpaque);
-    SkSafeUnref(ctPremul);
+    srcOpaque->allocPixels(SkImageInfo::Make(W, H, ct, kOpaque_SkAlphaType, colorSpace));
+    srcPremul->allocPixels(SkImageInfo::Make(W, H, ct, kPremul_SkAlphaType, colorSpace));
     init_src(*srcOpaque);
     init_src(*srcPremul);
 }
@@ -235,7 +96,7 @@
             // Test copying an extracted subset.
             for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) {
                 SkBitmap copy;
-                bool success = subset.copyTo(&copy, gPairs[j].fColorType);
+                bool success = sk_tool_utils::copy_to(&copy, gPairs[j].fColorType, subset);
                 if (!success) {
                     // Skip checking that success matches fValid, which is redundant
                     // with the code below.
@@ -249,14 +110,6 @@
 
                 REPORTER_ASSERT(reporter, copy.width() == W);
                 REPORTER_ASSERT(reporter, copy.height() == 2);
-
-                if (gPairs[i].fColorType == gPairs[j].fColorType) {
-                    SkAutoLockPixels alp0(subset);
-                    SkAutoLockPixels alp1(copy);
-                    // they should both have, or both not-have, a colortable
-                    bool hasCT = subset.getColorTable() != NULL;
-                    REPORTER_ASSERT(reporter, (copy.getColorTable() != NULL) == hasCT);
-                }
             }
         }
 
@@ -269,282 +122,6 @@
     }
 }
 
-DEF_TEST(BitmapCopy, reporter) {
-    static const bool isExtracted[] = {
-        false, true
-    };
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
-        SkBitmap srcOpaque, srcPremul;
-        setup_src_bitmaps(&srcOpaque, &srcPremul, gPairs[i].fColorType);
-
-        for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) {
-            SkBitmap dst;
-
-            bool success = srcPremul.copyTo(&dst, gPairs[j].fColorType);
-            bool expected = gPairs[i].fValid[j] != '0';
-            if (success != expected) {
-                ERRORF(reporter, "SkBitmap::copyTo from %s to %s. expected %s "
-                       "returned %s", gColorTypeName[i], gColorTypeName[j],
-                       boolStr(expected), boolStr(success));
-            }
-
-            bool canSucceed = srcPremul.canCopyTo(gPairs[j].fColorType);
-            if (success != canSucceed) {
-                ERRORF(reporter, "SkBitmap::copyTo from %s to %s. returned %s "
-                       "canCopyTo %s", gColorTypeName[i], gColorTypeName[j],
-                       boolStr(success), boolStr(canSucceed));
-            }
-
-            if (success) {
-                REPORTER_ASSERT(reporter, srcPremul.width() == dst.width());
-                REPORTER_ASSERT(reporter, srcPremul.height() == dst.height());
-                REPORTER_ASSERT(reporter, dst.colorType() == gPairs[j].fColorType);
-                test_isOpaque(reporter, srcOpaque, srcPremul, dst.colorType());
-                if (srcPremul.colorType() == dst.colorType()) {
-                    SkAutoLockPixels srcLock(srcPremul);
-                    SkAutoLockPixels dstLock(dst);
-                    REPORTER_ASSERT(reporter, srcPremul.readyToDraw());
-                    REPORTER_ASSERT(reporter, dst.readyToDraw());
-                    const char* srcP = (const char*)srcPremul.getAddr(0, 0);
-                    const char* dstP = (const char*)dst.getAddr(0, 0);
-                    REPORTER_ASSERT(reporter, srcP != dstP);
-                    REPORTER_ASSERT(reporter, !memcmp(srcP, dstP,
-                                                      srcPremul.getSize()));
-                    REPORTER_ASSERT(reporter, srcPremul.getGenerationID() == dst.getGenerationID());
-                } else {
-                    REPORTER_ASSERT(reporter, srcPremul.getGenerationID() != dst.getGenerationID());
-                }
-            } else {
-                // dst should be unchanged from its initial state
-                REPORTER_ASSERT(reporter, dst.colorType() == kUnknown_SkColorType);
-                REPORTER_ASSERT(reporter, dst.width() == 0);
-                REPORTER_ASSERT(reporter, dst.height() == 0);
-            }
-        } // for (size_t j = ...
-
-        // Tests for getSafeSize(), getSafeSize64(), copyPixelsTo(),
-        // copyPixelsFrom().
-        //
-        for (size_t copyCase = 0; copyCase < SK_ARRAY_COUNT(isExtracted);
-             ++copyCase) {
-            // Test copying to/from external buffer.
-            // Note: the tests below have hard-coded values ---
-            //       Please take care if modifying.
-
-            // Tests for getSafeSize64().
-            // Test with a very large configuration without pixel buffer
-            // attached.
-            SkBitmap tstSafeSize;
-            tstSafeSize.setInfo(SkImageInfo::Make(100000000U, 100000000U,
-                                                  gPairs[i].fColorType, kPremul_SkAlphaType));
-            int64_t safeSize = tstSafeSize.computeSafeSize64();
-            if (safeSize < 0) {
-                ERRORF(reporter, "getSafeSize64() negative: %s",
-                       gColorTypeName[tstSafeSize.colorType()]);
-            }
-            bool sizeFail = false;
-            // Compare against hand-computed values.
-            switch (gPairs[i].fColorType) {
-                case kUnknown_SkColorType:
-                    break;
-
-                case kAlpha_8_SkColorType:
-                case kIndex_8_SkColorType:
-                    if (safeSize != 0x2386F26FC10000LL) {
-                        sizeFail = true;
-                    }
-                    break;
-
-                case kRGB_565_SkColorType:
-                case kARGB_4444_SkColorType:
-                    if (safeSize != 0x470DE4DF820000LL) {
-                        sizeFail = true;
-                    }
-                    break;
-
-                case kN32_SkColorType:
-                    if (safeSize != 0x8E1BC9BF040000LL) {
-                        sizeFail = true;
-                    }
-                    break;
-
-                default:
-                    break;
-            }
-            if (sizeFail) {
-                ERRORF(reporter, "computeSafeSize64() wrong size: %s",
-                       gColorTypeName[tstSafeSize.colorType()]);
-            }
-
-            int subW = 2;
-            int subH = 2;
-
-            // Create bitmap to act as source for copies and subsets.
-            SkBitmap src, subset;
-            SkColorTable* ct = NULL;
-            if (kIndex_8_SkColorType == src.colorType()) {
-                ct = init_ctable(kPremul_SkAlphaType);
-            }
-
-            int localSubW;
-            if (isExtracted[copyCase]) { // A larger image to extract from.
-                localSubW = 2 * subW + 1;
-            } else { // Tests expect a 2x2 bitmap, so make smaller.
-                localSubW = subW;
-            }
-            // could fail if we pass kIndex_8 for the colortype
-            if (src.tryAllocPixels(SkImageInfo::Make(localSubW, subH, gPairs[i].fColorType,
-                                                     kPremul_SkAlphaType))) {
-                // failure is fine, as we will notice later on
-            }
-            SkSafeUnref(ct);
-
-            // Either copy src or extract into 'subset', which is used
-            // for subsequent calls to copyPixelsTo/From.
-            bool srcReady = false;
-            // Test relies on older behavior that extractSubset will fail on
-            // kUnknown_SkColorType
-            if (kUnknown_SkColorType != src.colorType() &&
-                isExtracted[copyCase]) {
-                // The extractedSubset() test case allows us to test copy-
-                // ing when src and dst mave possibly different strides.
-                SkIRect r;
-                r.set(1, 0, 1 + subW, subH); // 2x2 extracted bitmap
-
-                srcReady = src.extractSubset(&subset, r);
-            } else {
-                srcReady = src.copyTo(&subset);
-            }
-
-            // Not all configurations will generate a valid 'subset'.
-            if (srcReady) {
-
-                // Allocate our target buffer 'buf' for all copies.
-                // To simplify verifying correctness of copies attach
-                // buf to a SkBitmap, but copies are done using the
-                // raw buffer pointer.
-                const size_t bufSize = subH *
-                    SkColorTypeMinRowBytes(src.colorType(), subW) * 2;
-                SkAutoMalloc autoBuf (bufSize);
-                uint8_t* buf = static_cast<uint8_t*>(autoBuf.get());
-
-                SkBitmap bufBm; // Attach buf to this bitmap.
-                bool successExpected;
-
-                // Set up values for each pixel being copied.
-                Coordinates coords(subW * subH);
-                for (int x = 0; x < subW; ++x)
-                    for (int y = 0; y < subH; ++y)
-                    {
-                        int index = y * subW + x;
-                        SkASSERT(index < coords.length);
-                        coords[index]->fX = x;
-                        coords[index]->fY = y;
-                    }
-
-                writeCoordPixels(subset, coords);
-
-                // Test #1 ////////////////////////////////////////////
-
-                const SkImageInfo info = SkImageInfo::Make(subW, subH,
-                                                           gPairs[i].fColorType,
-                                                           kPremul_SkAlphaType);
-                // Before/after comparisons easier if we attach buf
-                // to an appropriately configured SkBitmap.
-                memset(buf, 0xFF, bufSize);
-                // Config with stride greater than src but that fits in buf.
-                bufBm.installPixels(info, buf, info.minRowBytes() * 2);
-                successExpected = false;
-                // Then attempt to copy with a stride that is too large
-                // to fit in the buffer.
-                REPORTER_ASSERT(reporter,
-                    subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes() * 3)
-                    == successExpected);
-
-                if (successExpected)
-                    reportCopyVerification(subset, bufBm, coords,
-                        "copyPixelsTo(buf, bufSize, 1.5*maxRowBytes)",
-                        reporter);
-
-                // Test #2 ////////////////////////////////////////////
-                // This test should always succeed, but in the case
-                // of extracted bitmaps only because we handle the
-                // issue of getSafeSize(). Without getSafeSize()
-                // buffer overrun/read would occur.
-                memset(buf, 0xFF, bufSize);
-                bufBm.installPixels(info, buf, subset.rowBytes());
-                successExpected = subset.getSafeSize() <= bufSize;
-                REPORTER_ASSERT(reporter,
-                    subset.copyPixelsTo(buf, bufSize) ==
-                        successExpected);
-                if (successExpected)
-                    reportCopyVerification(subset, bufBm, coords,
-                    "copyPixelsTo(buf, bufSize)", reporter);
-
-                // Test #3 ////////////////////////////////////////////
-                // Copy with different stride between src and dst.
-                memset(buf, 0xFF, bufSize);
-                bufBm.installPixels(info, buf, subset.rowBytes()+1);
-                successExpected = true; // Should always work.
-                REPORTER_ASSERT(reporter,
-                        subset.copyPixelsTo(buf, bufSize,
-                            subset.rowBytes()+1) == successExpected);
-                if (successExpected)
-                    reportCopyVerification(subset, bufBm, coords,
-                    "copyPixelsTo(buf, bufSize, rowBytes+1)", reporter);
-
-                // Test #4 ////////////////////////////////////////////
-                // Test copy with stride too small.
-                memset(buf, 0xFF, bufSize);
-                bufBm.installPixels(info, buf, info.minRowBytes());
-                successExpected = false;
-                // Request copy with stride too small.
-                REPORTER_ASSERT(reporter,
-                    subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes()-1)
-                        == successExpected);
-                if (successExpected)
-                    reportCopyVerification(subset, bufBm, coords,
-                    "copyPixelsTo(buf, bufSize, rowBytes()-1)", reporter);
-
-#if 0   // copyPixelsFrom is gone
-                // Test #5 ////////////////////////////////////////////
-                // Tests the case where the source stride is too small
-                // for the source configuration.
-                memset(buf, 0xFF, bufSize);
-                bufBm.installPixels(info, buf, info.minRowBytes());
-                writeCoordPixels(bufBm, coords);
-                REPORTER_ASSERT(reporter,
-                    subset.copyPixelsFrom(buf, bufSize, 1) == false);
-
-                // Test #6 ///////////////////////////////////////////
-                // Tests basic copy from an external buffer to the bitmap.
-                // If the bitmap is "extracted", this also tests the case
-                // where the source stride is different from the dest.
-                // stride.
-                // We've made the buffer large enough to always succeed.
-                bufBm.installPixels(info, buf, info.minRowBytes());
-                writeCoordPixels(bufBm, coords);
-                REPORTER_ASSERT(reporter,
-                    subset.copyPixelsFrom(buf, bufSize, bufBm.rowBytes()) ==
-                        true);
-                reportCopyVerification(bufBm, subset, coords,
-                    "copyPixelsFrom(buf, bufSize)",
-                    reporter);
-
-                // Test #7 ////////////////////////////////////////////
-                // Tests the case where the source buffer is too small
-                // for the transfer.
-                REPORTER_ASSERT(reporter,
-                    subset.copyPixelsFrom(buf, 1, subset.rowBytes()) ==
-                        false);
-
-#endif
-            }
-        } // for (size_t copyCase ...
-    }
-}
-
 #include "SkColorPriv.h"
 #include "SkUtils.h"
 
@@ -613,7 +190,7 @@
                                  gRec[i].fRequestedDstSize.height());
         bool success = srcBM.readPixels(dstInfo, dstPixels, rowBytes,
                                         gRec[i].fRequestedSrcLoc.x(), gRec[i].fRequestedSrcLoc.y());
-        
+
         REPORTER_ASSERT(reporter, gRec[i].fExpectedSuccess == success);
         if (success) {
             const SkIRect srcR = gRec[i].fExpectedSrcR;
@@ -636,4 +213,3 @@
         }
     }
 }
-
diff --git a/src/third_party/skia/tests/BitmapHasherTest.cpp b/src/third_party/skia/tests/BitmapHasherTest.cpp
deleted file mode 100644
index 3b51706..0000000
--- a/src/third_party/skia/tests/BitmapHasherTest.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBitmapHasher.h"
-
-#include "SkBitmap.h"
-#include "SkColor.h"
-#include "Test.h"
-
-// Word size that is large enough to hold results of any checksum type.
-typedef uint64_t checksum_result;
-
-// Fill in bitmap with test data.
-static void CreateTestBitmap(SkBitmap* bitmap, int width, int height,
-                             SkColor color, skiatest::Reporter* reporter) {
-    bitmap->allocN32Pixels(width, height, kOpaque_SkAlphaType);
-    bitmap->eraseColor(color);
-}
-
-DEF_TEST(BitmapHasher, reporter) {
-    // Test SkBitmapHasher
-    SkBitmap bitmap;
-    uint64_t digest;
-    // initial test case
-    CreateTestBitmap(&bitmap, 333, 555, SK_ColorBLUE, reporter);
-    REPORTER_ASSERT(reporter, SkBitmapHasher::ComputeDigest(bitmap, &digest));
-    REPORTER_ASSERT(reporter, digest == 0xfb2903562766ef87ULL);
-    // same pixel data but different dimensions should yield a different checksum
-    CreateTestBitmap(&bitmap, 555, 333, SK_ColorBLUE, reporter);
-    REPORTER_ASSERT(reporter, SkBitmapHasher::ComputeDigest(bitmap, &digest));
-    REPORTER_ASSERT(reporter, digest == 0xfe04023fb97d0f61ULL);
-    // same dimensions but different color should yield a different checksum
-    CreateTestBitmap(&bitmap, 555, 333, SK_ColorGREEN, reporter);
-    REPORTER_ASSERT(reporter, SkBitmapHasher::ComputeDigest(bitmap, &digest));
-    REPORTER_ASSERT(reporter, digest == 0x2423c51cad6d1edcULL);
-}
diff --git a/src/third_party/skia/tests/BitmapHeapTest.cpp b/src/third_party/skia/tests/BitmapHeapTest.cpp
deleted file mode 100644
index dc9905e..0000000
--- a/src/third_party/skia/tests/BitmapHeapTest.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBitmap.h"
-#include "SkBitmapHeap.h"
-#include "SkColor.h"
-#include "SkFlattenable.h"
-#include "SkWriteBuffer.h"
-#include "SkPictureFlat.h"
-#include "SkRefCnt.h"
-#include "SkShader.h"
-#include "Test.h"
-
-struct SkShaderTraits {
-    static void Flatten(SkWriteBuffer& buffer, const SkShader& shader) {
-        buffer.writeFlattenable(&shader);
-    }
-};
-typedef SkFlatDictionary<SkShader, SkShaderTraits> FlatDictionary;
-
-class SkBitmapHeapTester {
-
-public:
-    static int32_t GetRefCount(const SkBitmapHeapEntry* entry) {
-        return entry->fRefCount;
-    }
-};
-
-DEF_TEST(BitmapHeap, reporter) {
-    // Create a bitmap shader.
-    SkBitmap bm;
-    bm.allocN32Pixels(2, 2);
-    bm.eraseColor(SK_ColorRED);
-    uint32_t* pixel = bm.getAddr32(1,0);
-    *pixel = SK_ColorBLUE;
-
-    SkShader* bitmapShader = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
-                                                          SkShader::kRepeat_TileMode);
-    SkAutoTUnref<SkShader> aur(bitmapShader);
-
-    // Flatten, storing it in the bitmap heap.
-    SkBitmapHeap heap(1, 1);
-    SkChunkFlatController controller(1024);
-    controller.setBitmapStorage(&heap);
-    FlatDictionary dictionary(&controller);
-
-    // Dictionary and heap start off empty.
-    REPORTER_ASSERT(reporter, heap.count() == 0);
-    REPORTER_ASSERT(reporter, dictionary.count() == 0);
-
-    heap.deferAddingOwners();
-    int index = dictionary.find(*bitmapShader);
-    heap.endAddingOwnersDeferral(true);
-
-    // The dictionary and heap should now each have one entry.
-    REPORTER_ASSERT(reporter, 1 == index);
-    REPORTER_ASSERT(reporter, heap.count() == 1);
-    REPORTER_ASSERT(reporter, dictionary.count() == 1);
-
-    // The bitmap entry's refcount should be 1, then 0 after release.
-    SkBitmapHeapEntry* entry = heap.getEntry(0);
-    REPORTER_ASSERT(reporter, SkBitmapHeapTester::GetRefCount(entry) == 1);
-
-    entry->releaseRef();
-    REPORTER_ASSERT(reporter, SkBitmapHeapTester::GetRefCount(entry) == 0);
-
-    // Now clear out the heap, after which it should be empty.
-    heap.freeMemoryIfPossible(~0U);
-    REPORTER_ASSERT(reporter, heap.count() == 0);
-
-    // Now attempt to flatten the shader again.
-    heap.deferAddingOwners();
-    index = dictionary.find(*bitmapShader);
-    heap.endAddingOwnersDeferral(false);
-
-    // The dictionary should report the same index since the new entry is identical.
-    // The bitmap heap should contain the bitmap, but with no references.
-    REPORTER_ASSERT(reporter, 1 == index);
-    REPORTER_ASSERT(reporter, heap.count() == 1);
-    REPORTER_ASSERT(reporter, SkBitmapHeapTester::GetRefCount(heap.getEntry(0)) == 0);
-}
diff --git a/src/third_party/skia/tests/BitmapTest.cpp b/src/third_party/skia/tests/BitmapTest.cpp
index ef69531..ddc770f 100644
--- a/src/third_party/skia/tests/BitmapTest.cpp
+++ b/src/third_party/skia/tests/BitmapTest.cpp
@@ -6,8 +6,47 @@
  */
 
 #include "SkBitmap.h"
-
+#include "SkMallocPixelRef.h"
+#include "SkRandom.h"
 #include "Test.h"
+#include "sk_tool_utils.h"
+
+static void test_peekpixels(skiatest::Reporter* reporter) {
+    const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
+
+    SkPixmap pmap;
+    SkBitmap bm;
+
+    // empty should return false
+    REPORTER_ASSERT(reporter, !bm.peekPixels(nullptr));
+    REPORTER_ASSERT(reporter, !bm.peekPixels(&pmap));
+
+    // no pixels should return false
+    bm.setInfo(SkImageInfo::MakeN32Premul(10, 10));
+    REPORTER_ASSERT(reporter, !bm.peekPixels(nullptr));
+    REPORTER_ASSERT(reporter, !bm.peekPixels(&pmap));
+
+    // real pixels should return true
+    bm.allocPixels(info);
+    REPORTER_ASSERT(reporter, bm.peekPixels(nullptr));
+    REPORTER_ASSERT(reporter, bm.peekPixels(&pmap));
+    REPORTER_ASSERT(reporter, pmap.info() == bm.info());
+    REPORTER_ASSERT(reporter, pmap.addr() == bm.getPixels());
+    REPORTER_ASSERT(reporter, pmap.rowBytes() == bm.rowBytes());
+}
+
+// https://code.google.com/p/chromium/issues/detail?id=446164
+static void test_bigalloc(skiatest::Reporter* reporter) {
+    const int width = 0x40000001;
+    const int height = 0x00000096;
+    const SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
+
+    SkBitmap bm;
+    REPORTER_ASSERT(reporter, !bm.tryAllocPixels(info));
+
+    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, info.minRowBytes());
+    REPORTER_ASSERT(reporter, !pr);
+}
 
 static void test_allocpixels(skiatest::Reporter* reporter) {
     const int width = 10;
@@ -81,4 +120,101 @@
 
     test_bigwidth(reporter);
     test_allocpixels(reporter);
+    test_bigalloc(reporter);
+    test_peekpixels(reporter);
 }
+
+/**
+ *  This test checks that getColor works for both swizzles.
+ */
+DEF_TEST(Bitmap_getColor_Swizzle, r) {
+    SkBitmap source;
+    source.allocN32Pixels(1,1);
+    source.eraseColor(SK_ColorRED);
+    SkColorType colorTypes[] = {
+        kRGBA_8888_SkColorType,
+        kBGRA_8888_SkColorType,
+    };
+    for (SkColorType ct : colorTypes) {
+        SkBitmap copy;
+        if (!sk_tool_utils::copy_to(&copy, ct, source)) {
+            ERRORF(r, "SkBitmap::copy failed %d", (int)ct);
+            continue;
+        }
+        REPORTER_ASSERT(r, source.getColor(0, 0) == copy.getColor(0, 0));
+    }
+}
+
+static void test_erasecolor_premul(skiatest::Reporter* reporter, SkColorType ct, SkColor input,
+                                   SkColor expected) {
+  SkBitmap bm;
+  bm.allocPixels(SkImageInfo::Make(1, 1, ct, kPremul_SkAlphaType));
+  bm.eraseColor(input);
+  INFOF(reporter, "expected: %x actual: %x\n", expected, bm.getColor(0, 0));
+  REPORTER_ASSERT(reporter, bm.getColor(0, 0) == expected);
+}
+
+/**
+ *  This test checks that eraseColor premultiplies the color correctly.
+ */
+DEF_TEST(Bitmap_eraseColor_Premul, r) {
+    SkColor color = 0x80FF0080;
+    test_erasecolor_premul(r, kAlpha_8_SkColorType, color, 0x80000000);
+    test_erasecolor_premul(r, kRGB_565_SkColorType, color, 0xFF840042);
+    test_erasecolor_premul(r, kARGB_4444_SkColorType, color, 0x88FF0080);
+    test_erasecolor_premul(r, kRGBA_8888_SkColorType, color, color);
+    test_erasecolor_premul(r, kBGRA_8888_SkColorType, color, color);
+}
+
+// Test that SkBitmap::ComputeOpaque() is correct for various colortypes.
+DEF_TEST(Bitmap_compute_is_opaque, r) {
+    struct {
+        SkColorType fCT;
+        SkAlphaType fAT;
+    } types[] = {
+        { kGray_8_SkColorType,    kOpaque_SkAlphaType },
+        { kAlpha_8_SkColorType,   kPremul_SkAlphaType },
+        { kARGB_4444_SkColorType, kPremul_SkAlphaType },
+        { kRGB_565_SkColorType,   kOpaque_SkAlphaType },
+        { kBGRA_8888_SkColorType, kPremul_SkAlphaType },
+        { kRGBA_8888_SkColorType, kPremul_SkAlphaType },
+        { kRGBA_F16_SkColorType,  kPremul_SkAlphaType },
+    };
+    for (auto type : types) {
+        SkBitmap bm;
+        REPORTER_ASSERT(r, !SkBitmap::ComputeIsOpaque(bm));
+
+        bm.allocPixels(SkImageInfo::Make(13, 17, type.fCT, type.fAT));
+        bm.eraseColor(SkColorSetARGB(255, 10, 20, 30));
+        REPORTER_ASSERT(r, SkBitmap::ComputeIsOpaque(bm));
+
+        bm.eraseColor(SkColorSetARGB(128, 255, 255, 255));
+        bool isOpaque = SkBitmap::ComputeIsOpaque(bm);
+        bool shouldBeOpaque = (type.fAT == kOpaque_SkAlphaType);
+        REPORTER_ASSERT(r, isOpaque == shouldBeOpaque);
+    }
+}
+
+// Test that erase+getColor round trips with RGBA_F16 pixels.
+DEF_TEST(Bitmap_erase_f16_erase_getColor, r) {
+    SkRandom random;
+    SkPixmap pm;
+    SkBitmap bm;
+    bm.allocPixels(SkImageInfo::Make(1, 1, kRGBA_F16_SkColorType, kPremul_SkAlphaType));
+    REPORTER_ASSERT(r, bm.peekPixels(&pm));
+    for (unsigned i = 0; i < 0x100; ++i) {
+        // Test all possible values of blue component.
+        SkColor color1 = (SkColor)((random.nextU() & 0xFFFFFF00) | i);
+        // Test all possible values of alpha component.
+        SkColor color2 = (SkColor)((random.nextU() & 0x00FFFFFF) | (i << 24));
+        for (SkColor color : {color1, color2}) {
+            pm.erase(color);
+            if (SkColorGetA(color) != 0) {
+                REPORTER_ASSERT(r, color == pm.getColor(0, 0));
+            } else {
+                REPORTER_ASSERT(r, 0 == SkColorGetA(pm.getColor(0, 0)));
+            }
+        }
+    }
+}
+
diff --git a/src/third_party/skia/tests/BlendTest.cpp b/src/third_party/skia/tests/BlendTest.cpp
index a0a84d7..20ea6e0 100644
--- a/src/third_party/skia/tests/BlendTest.cpp
+++ b/src/third_party/skia/tests/BlendTest.cpp
@@ -1,258 +1,206 @@
-#include "Test.h"
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <functional>
+#include "SkBitmap.h"
+#include "SkCanvas.h"
 #include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkSurface.h"
+#include "SkTaskGroup.h"
+#include "SkUtils.h"
+#include "Test.h"
 
-#define ASSERT(x) REPORTER_ASSERT(r, x)
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrResourceProvider.h"
+#include "GrSurfaceContext.h"
+#include "GrSurfaceProxy.h"
+#include "GrTest.h"
+#include "GrTexture.h"
+#endif
 
-static uint8_t double_to_u8(double d) {
-    SkASSERT(d >= 0);
-    SkASSERT(d < 256);
-    return uint8_t(d);
+struct Results { int diffs, diffs_0x00, diffs_0xff, diffs_by_1; };
+
+static bool acceptable(const Results& r) {
+#if 0
+    SkDebugf("%d diffs, %d at 0x00, %d at 0xff, %d off by 1, all out of 65536\n",
+             r.diffs, r.diffs_0x00, r.diffs_0xff, r.diffs_by_1);
+#endif
+    return r.diffs_by_1 == r.diffs   // never off by more than 1
+        && r.diffs_0x00 == 0         // transparent must stay transparent
+        && r.diffs_0xff == 0;        // opaque must stay opaque
 }
 
-// All algorithms we're testing have this interface.
-// We want a single channel blend, src over dst, assuming src is premultiplied by srcAlpha.
-typedef uint8_t(*Blend)(uint8_t dst, uint8_t src, uint8_t srcAlpha);
-
-// This is our golden algorithm.
-static uint8_t blend_double_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
-    SkASSERT(src <= srcAlpha);
-    return double_to_u8(0.5 + src + dst * (255.0 - srcAlpha) / 255.0);
+template <typename Fn>
+static Results test(Fn&& multiply) {
+    Results r = { 0,0,0,0 };
+    for (int x = 0; x < 256; x++) {
+    for (int y = 0; y < 256; y++) {
+        int p = multiply(x, y),
+            ideal = (x*y+127)/255;
+        if (p != ideal) {
+            r.diffs++;
+            if (x == 0x00 || y == 0x00) { r.diffs_0x00++; }
+            if (x == 0xff || y == 0xff) { r.diffs_0xff++; }
+            if (SkTAbs(ideal - p) == 1) { r.diffs_by_1++; }
+        }
+    }}
+    return r;
 }
 
-static uint8_t abs_diff(uint8_t a, uint8_t b) {
-    const int diff = a - b;
-    return diff > 0 ? diff : -diff;
+DEF_TEST(Blend_byte_multiply, r) {
+    // These are all temptingly close but fundamentally broken.
+    int (*broken[])(int, int) = {
+        [](int x, int y) { return (x*y)>>8; },
+        [](int x, int y) { return (x*y+128)>>8; },
+        [](int x, int y) { y += y>>7; return (x*y)>>8; },
+    };
+    for (auto multiply : broken) { REPORTER_ASSERT(r, !acceptable(test(multiply))); }
+
+    // These are fine to use, but not perfect.
+    int (*fine[])(int, int) = {
+        [](int x, int y) { return (x*y+x)>>8; },
+        [](int x, int y) { return (x*y+y)>>8; },
+        [](int x, int y) { return (x*y+255)>>8; },
+        [](int x, int y) { y += y>>7; return (x*y+128)>>8; },
+    };
+    for (auto multiply : fine) { REPORTER_ASSERT(r, acceptable(test(multiply))); }
+
+    // These are pefect.
+    int (*perfect[])(int, int) = {
+        [](int x, int y) { return (x*y+127)/255; },  // Duh.
+        [](int x, int y) { int p = (x*y+128); return (p+(p>>8))>>8; },
+        [](int x, int y) { return ((x*y+128)*257)>>16; },
+    };
+    for (auto multiply : perfect) { REPORTER_ASSERT(r, test(multiply).diffs == 0); }
 }
 
-static void test(skiatest::Reporter* r, int maxDiff, Blend algorithm,
-                 uint8_t dst, uint8_t src, uint8_t alpha) {
-    const uint8_t golden = blend_double_round(dst, src, alpha);
-    const uint8_t  blend =          algorithm(dst, src, alpha);
-    if (abs_diff(blend, golden) > maxDiff) {
-        SkDebugf("dst %02x, src %02x, alpha %02x, |%02x - %02x| > %d\n",
-                 dst, src, alpha, blend, golden, maxDiff);
-        ASSERT(abs_diff(blend, golden) <= maxDiff);
+#if SK_SUPPORT_GPU
+namespace {
+static sk_sp<SkSurface> create_gpu_surface_backend_texture_as_render_target(
+        GrContext* context, int sampleCnt, int width, int height, GrPixelConfig config,
+        GrSurfaceOrigin origin,
+        sk_sp<GrTexture>* backingSurface) {
+    GrSurfaceDesc backingDesc;
+    backingDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    backingDesc.fOrigin = origin;
+    backingDesc.fWidth = width;
+    backingDesc.fHeight = height;
+    backingDesc.fConfig = config;
+    backingDesc.fSampleCnt = sampleCnt;
+
+    *backingSurface = context->resourceProvider()->createTexture(backingDesc, SkBudgeted::kNo);
+    if (!(*backingSurface)) {
+        return nullptr;
     }
+
+    GrBackendTexture backendTex =
+            GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                         width,
+                                         height,
+                                         config,
+                                         (*backingSurface)->getTextureHandle());
+    sk_sp<SkSurface> surface =
+            SkSurface::MakeFromBackendTextureAsRenderTarget(context, backendTex, origin,
+                                                            sampleCnt, nullptr, nullptr);
+
+    return surface;
+}
 }
 
-// Exhaustively compare an algorithm against our golden, for a given alpha.
-static void test_alpha(skiatest::Reporter* r, uint8_t alpha, int maxDiff, Blend algorithm) {
-    SkASSERT(maxDiff >= 0);
+// Tests blending to a surface with no texture available.
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ES2BlendWithNoTexture, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    const int kWidth = 10;
+    const int kHeight = 10;
+    const GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig;
+    const SkColorType kColorType = kRGBA_8888_SkColorType;
 
-    for (unsigned src = 0; src <= alpha; src++) {
-        for (unsigned dst = 0; dst < 256; dst++) {
-            test(r, maxDiff, algorithm, dst, src, alpha);
+    // Build our test cases:
+    struct RectAndSamplePoint {
+        SkRect rect;
+        SkIPoint outPoint;
+        SkIPoint inPoint;
+    } allRectsAndPoints[3] = {
+            {SkRect::MakeXYWH(0, 0, 5, 5), SkIPoint::Make(7, 7), SkIPoint::Make(2, 2)},
+            {SkRect::MakeXYWH(2, 2, 5, 5), SkIPoint::Make(1, 1), SkIPoint::Make(4, 4)},
+            {SkRect::MakeXYWH(5, 5, 5, 5), SkIPoint::Make(2, 2), SkIPoint::Make(7, 7)},
+    };
+
+    struct TestCase {
+        RectAndSamplePoint fRectAndPoints;
+        SkRect             fClip;
+        int                fSampleCnt;
+        GrSurfaceOrigin    fOrigin;
+    };
+    std::vector<TestCase> testCases;
+
+    for (auto origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
+        for (int sampleCnt : {0, 4}) {
+            for (auto rectAndPoints : allRectsAndPoints) {
+                for (auto clip : {SkRect::MakeXYWH(0, 0, 10, 10), SkRect::MakeXYWH(1, 1, 8, 8)}) {
+                    testCases.push_back({rectAndPoints, clip, sampleCnt, origin});
+                }
+            }
         }
     }
-}
 
-// Exhaustively compare an algorithm against our golden, for a given dst.
-static void test_dst(skiatest::Reporter* r, uint8_t dst, int maxDiff, Blend algorithm) {
-    SkASSERT(maxDiff >= 0);
+    // Run each test case:
+    for (auto testCase : testCases) {
+        int sampleCnt = testCase.fSampleCnt;
+        SkRect paintRect = testCase.fRectAndPoints.rect;
+        SkIPoint outPoint = testCase.fRectAndPoints.outPoint;
+        SkIPoint inPoint = testCase.fRectAndPoints.inPoint;
+        GrSurfaceOrigin origin = testCase.fOrigin;
 
-    for (unsigned alpha = 0; alpha < 256; alpha++) {
-        for (unsigned src = 0; src <= alpha; src++) {
-            test(r, maxDiff, algorithm, dst, src, alpha);
+        sk_sp<GrTexture> backingSurface;
+        // BGRA forces a framebuffer blit on ES2.
+        sk_sp<SkSurface> surface = create_gpu_surface_backend_texture_as_render_target(
+                context, sampleCnt, kWidth, kHeight, kConfig, origin, &backingSurface);
+
+        if (!surface && sampleCnt > 0) {
+            // Some platforms don't support MSAA.
+            continue;
         }
+        REPORTER_ASSERT(reporter, !!surface);
+
+        // Fill our canvas with 0xFFFF80
+        SkCanvas* canvas = surface->getCanvas();
+        canvas->clipRect(testCase.fClip, false);
+        SkPaint black_paint;
+        black_paint.setColor(SkColorSetRGB(0xFF, 0xFF, 0x80));
+        canvas->drawRect(SkRect::MakeXYWH(0, 0, kWidth, kHeight), black_paint);
+
+        // Blend 2x2 pixels at 5,5 with 0x80FFFF. Use multiply blend mode as this will trigger
+        // a copy of the destination.
+        SkPaint white_paint;
+        white_paint.setColor(SkColorSetRGB(0x80, 0xFF, 0xFF));
+        white_paint.setBlendMode(SkBlendMode::kMultiply);
+        canvas->drawRect(paintRect, white_paint);
+
+        // Read the result into a bitmap.
+        SkBitmap bitmap;
+        REPORTER_ASSERT(reporter, bitmap.tryAllocPixels(SkImageInfo::Make(
+                                          kWidth, kHeight, kColorType, kPremul_SkAlphaType)));
+        REPORTER_ASSERT(
+                reporter,
+                surface->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0));
+
+        // Check the in/out pixels.
+        REPORTER_ASSERT(reporter, bitmap.getColor(outPoint.x(), outPoint.y()) ==
+                                          SkColorSetRGB(0xFF, 0xFF, 0x80));
+        REPORTER_ASSERT(reporter, bitmap.getColor(inPoint.x(), inPoint.y()) ==
+                                          SkColorSetRGB(0x80, 0xFF, 0x80));
+
+        // Clean up - surface depends on backingSurface and must be released first.
+        surface.reset();
+        backingSurface.reset();
     }
 }
-
-static uint8_t blend_double_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
-    return double_to_u8(src + dst * (255.0 - srcAlpha) / 255.0);
-}
-
-static uint8_t blend_float_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
-    return double_to_u8(src + dst * (255.0f - srcAlpha) / 255.0f);
-}
-
-static uint8_t blend_float_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
-    return double_to_u8(0.5f + src + dst * (255.0f - srcAlpha) / 255.0f);
-}
-
-static uint8_t blend_255_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
-    const uint16_t invAlpha = 255 - srcAlpha;
-    const uint16_t product = dst * invAlpha;
-    return src + (product >> 8);
-}
-
-static uint8_t blend_255_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
-    const uint16_t invAlpha = 255 - srcAlpha;
-    const uint16_t product = dst * invAlpha + 128;
-    return src + (product >> 8);
-}
-
-static uint8_t blend_256_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
-    const uint16_t invAlpha = 256 - (srcAlpha + (srcAlpha >> 7));
-    const uint16_t product = dst * invAlpha;
-    return src + (product >> 8);
-}
-
-static uint8_t blend_256_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
-    const uint16_t invAlpha = 256 - (srcAlpha + (srcAlpha >> 7));
-    const uint16_t product = dst * invAlpha + 128;
-    return src + (product >> 8);
-}
-
-static uint8_t blend_256_round_alt(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
-    const uint8_t invAlpha8 = 255 - srcAlpha;
-    const uint16_t invAlpha = invAlpha8 + (invAlpha8 >> 7);
-    const uint16_t product = dst * invAlpha + 128;
-    return src + (product >> 8);
-}
-
-static uint8_t blend_256_plus1_trunc(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
-    const uint16_t invAlpha = 256 - (srcAlpha + 1);
-    const uint16_t product = dst * invAlpha;
-    return src + (product >> 8);
-}
-
-static uint8_t blend_256_plus1_round(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
-    const uint16_t invAlpha = 256 - (srcAlpha + 1);
-    const uint16_t product = dst * invAlpha + 128;
-    return src + (product >> 8);
-}
-
-static uint8_t blend_perfect(uint8_t dst, uint8_t src, uint8_t srcAlpha) {
-    const uint8_t invAlpha = 255 - srcAlpha;
-    const uint16_t product = dst * invAlpha + 128;
-    return src + ((product + (product >> 8)) >> 8);
-}
-
-
-// We want 0 diff whenever src is fully transparent.
-DEF_TEST(Blend_alpha_0x00, r) {
-    const uint8_t alpha = 0x00;
-
-    // GOOD
-    test_alpha(r, alpha, 0, blend_256_round);
-    test_alpha(r, alpha, 0, blend_256_round_alt);
-    test_alpha(r, alpha, 0, blend_256_trunc);
-    test_alpha(r, alpha, 0, blend_double_trunc);
-    test_alpha(r, alpha, 0, blend_float_round);
-    test_alpha(r, alpha, 0, blend_float_trunc);
-    test_alpha(r, alpha, 0, blend_perfect);
-
-    // BAD
-    test_alpha(r, alpha, 1, blend_255_round);
-    test_alpha(r, alpha, 1, blend_255_trunc);
-    test_alpha(r, alpha, 1, blend_256_plus1_round);
-    test_alpha(r, alpha, 1, blend_256_plus1_trunc);
-}
-
-// We want 0 diff whenever dst is 0.
-DEF_TEST(Blend_dst_0x00, r) {
-    const uint8_t dst = 0x00;
-
-    // GOOD
-    test_dst(r, dst, 0, blend_255_round);
-    test_dst(r, dst, 0, blend_255_trunc);
-    test_dst(r, dst, 0, blend_256_plus1_round);
-    test_dst(r, dst, 0, blend_256_plus1_trunc);
-    test_dst(r, dst, 0, blend_256_round);
-    test_dst(r, dst, 0, blend_256_round_alt);
-    test_dst(r, dst, 0, blend_256_trunc);
-    test_dst(r, dst, 0, blend_double_trunc);
-    test_dst(r, dst, 0, blend_float_round);
-    test_dst(r, dst, 0, blend_float_trunc);
-    test_dst(r, dst, 0, blend_perfect);
-
-    // BAD
-}
-
-// We want 0 diff whenever src is fully opaque.
-DEF_TEST(Blend_alpha_0xFF, r) {
-    const uint8_t alpha = 0xFF;
-
-    // GOOD
-    test_alpha(r, alpha, 0, blend_255_round);
-    test_alpha(r, alpha, 0, blend_255_trunc);
-    test_alpha(r, alpha, 0, blend_256_plus1_round);
-    test_alpha(r, alpha, 0, blend_256_plus1_trunc);
-    test_alpha(r, alpha, 0, blend_256_round);
-    test_alpha(r, alpha, 0, blend_256_round_alt);
-    test_alpha(r, alpha, 0, blend_256_trunc);
-    test_alpha(r, alpha, 0, blend_double_trunc);
-    test_alpha(r, alpha, 0, blend_float_round);
-    test_alpha(r, alpha, 0, blend_float_trunc);
-    test_alpha(r, alpha, 0, blend_perfect);
-
-    // BAD
-}
-
-// We want 0 diff whenever dst is 0xFF.
-DEF_TEST(Blend_dst_0xFF, r) {
-    const uint8_t dst = 0xFF;
-
-    // GOOD
-    test_dst(r, dst, 0, blend_256_round);
-    test_dst(r, dst, 0, blend_256_round_alt);
-    test_dst(r, dst, 0, blend_double_trunc);
-    test_dst(r, dst, 0, blend_float_round);
-    test_dst(r, dst, 0, blend_float_trunc);
-    test_dst(r, dst, 0, blend_perfect);
-
-    // BAD
-    test_dst(r, dst, 1, blend_255_round);
-    test_dst(r, dst, 1, blend_255_trunc);
-    test_dst(r, dst, 1, blend_256_plus1_round);
-    test_dst(r, dst, 1, blend_256_plus1_trunc);
-    test_dst(r, dst, 1, blend_256_trunc);
-}
-
-// We'd like diff <= 1 everywhere.
-DEF_TEST(Blend_alpha_Exhaustive, r) {
-    for (unsigned alpha = 0; alpha < 256; alpha++) {
-        // PERFECT
-        test_alpha(r, alpha, 0, blend_float_round);
-        test_alpha(r, alpha, 0, blend_perfect);
-
-        // GOOD
-        test_alpha(r, alpha, 1, blend_255_round);
-        test_alpha(r, alpha, 1, blend_256_plus1_round);
-        test_alpha(r, alpha, 1, blend_256_round);
-        test_alpha(r, alpha, 1, blend_256_round_alt);
-        test_alpha(r, alpha, 1, blend_256_trunc);
-        test_alpha(r, alpha, 1, blend_double_trunc);
-        test_alpha(r, alpha, 1, blend_float_trunc);
-
-        // BAD
-        test_alpha(r, alpha, 2, blend_255_trunc);
-        test_alpha(r, alpha, 2, blend_256_plus1_trunc);
-    }
-}
-
-// We'd like diff <= 1 everywhere.
-DEF_TEST(Blend_dst_Exhaustive, r) {
-    for (unsigned dst = 0; dst < 256; dst++) {
-        // PERFECT
-        test_dst(r, dst, 0, blend_float_round);
-        test_dst(r, dst, 0, blend_perfect);
-
-        // GOOD
-        test_dst(r, dst, 1, blend_255_round);
-        test_dst(r, dst, 1, blend_256_plus1_round);
-        test_dst(r, dst, 1, blend_256_round);
-        test_dst(r, dst, 1, blend_256_round_alt);
-        test_dst(r, dst, 1, blend_256_trunc);
-        test_dst(r, dst, 1, blend_double_trunc);
-        test_dst(r, dst, 1, blend_float_trunc);
-
-        // BAD
-        test_dst(r, dst, 2, blend_255_trunc);
-        test_dst(r, dst, 2, blend_256_plus1_trunc);
-    }
-}
-// Overall summary:
-// PERFECT
-//  blend_double_round
-//  blend_float_round
-//  blend_perfect
-// GOOD ENOUGH
-//  blend_double_trunc
-//  blend_float_trunc
-//  blend_256_round
-//  blend_256_round_alt
-// NOT GOOD ENOUGH
-//  all others
-//
-//  Algorithms that make sense to use in Skia: blend_256_round, blend_256_round_alt, blend_perfect
+#endif
diff --git a/src/third_party/skia/tests/BlitMaskClip.cpp b/src/third_party/skia/tests/BlitMaskClip.cpp
new file mode 100644
index 0000000..21fe562
--- /dev/null
+++ b/src/third_party/skia/tests/BlitMaskClip.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBlitter.h"
+#include "SkMask.h"
+#include "Test.h"
+#include <string.h>
+
+class TestBlitter : public SkBlitter {
+public:
+    TestBlitter(SkIRect bounds, skiatest::Reporter* reporter)
+        : fBounds(bounds)
+        , fReporter(reporter) { }
+
+    void blitH(int x, int y, int width) override {
+
+        REPORTER_ASSERT(fReporter, x >= fBounds.fLeft && x < fBounds.fRight);
+        REPORTER_ASSERT(fReporter, y >= fBounds.fTop && y < fBounds.fBottom);
+        int right = x + width;
+        REPORTER_ASSERT(fReporter, right > fBounds.fLeft && right <= fBounds.fRight);
+    }
+
+    void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override {
+        SkDEBUGFAIL("blitAntiH not implemented");
+    }
+
+private:
+    SkIRect fBounds;
+    skiatest::Reporter* fReporter;
+};
+
+// Exercise all clips compared with different widths of bitMask. Make sure that no buffer
+// overruns happen.
+DEF_TEST(BlitAndClip, reporter) {
+    const int originX = 100;
+    const int originY = 100;
+    for (int width = 1; width <= 32; width++) {
+        const int height = 2;
+        int rowBytes = (width + 7) >> 3;
+        uint8_t* bits = new uint8_t[rowBytes * height];
+        memset(bits, 0xAA, rowBytes * height);
+
+        SkIRect b = {originX, originY, originX + width, originY + height};
+
+        SkMask mask;
+        mask.fFormat = SkMask::kBW_Format;
+        mask.fBounds = b;
+        mask.fImage = (uint8_t*)bits;
+        mask.fRowBytes = rowBytes;
+
+        TestBlitter tb(mask.fBounds, reporter);
+
+        for (int top = b.fTop; top < b.fBottom; top++) {
+            for (int bottom = top + 1; bottom <= b.fBottom; bottom++) {
+                for (int left = b.fLeft; left < b.fRight; left++) {
+                    for (int right = left + 1; right <= b.fRight; right++) {
+                        SkIRect clipRect = {left, top, right, bottom};
+                        tb.blitMask(mask, clipRect);
+                    }
+                }
+            }
+        }
+
+        delete [] bits;
+    }
+}
diff --git a/src/third_party/skia/tests/BlitRowTest.cpp b/src/third_party/skia/tests/BlitRowTest.cpp
deleted file mode 100644
index 4689a30..0000000
--- a/src/third_party/skia/tests/BlitRowTest.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkColorPriv.h"
-#include "SkGradientShader.h"
-#include "SkRect.h"
-#include "Test.h"
-
-// these are in the same order as the SkColorType enum
-static const char* gColorTypeName[] = {
-    "None", "A8", "565", "4444", "RGBA", "BGRA", "Index8"
-};
-
-/** Returns -1 on success, else the x coord of the first bad pixel, return its
-    value in bad
- */
-typedef int (*Proc)(const void*, int width, uint32_t expected, uint32_t* bad);
-
-static int proc_32(const void* ptr, int w, uint32_t expected, uint32_t* bad) {
-    const SkPMColor* addr = static_cast<const SkPMColor*>(ptr);
-    for (int x = 0; x < w; x++) {
-        if (addr[x] != expected) {
-            *bad = addr[x];
-            return x;
-        }
-    }
-    return -1;
-}
-
-static int proc_16(const void* ptr, int w, uint32_t expected, uint32_t* bad) {
-    const uint16_t* addr = static_cast<const uint16_t*>(ptr);
-    for (int x = 0; x < w; x++) {
-        if (addr[x] != expected) {
-            *bad = addr[x];
-            return x;
-        }
-    }
-    return -1;
-}
-
-static int proc_8(const void* ptr, int w, uint32_t expected, uint32_t* bad) {
-    const SkPMColor* addr = static_cast<const SkPMColor*>(ptr);
-    for (int x = 0; x < w; x++) {
-        if (SkGetPackedA32(addr[x]) != expected) {
-            *bad = SkGetPackedA32(addr[x]);
-            return x;
-        }
-    }
-    return -1;
-}
-
-static int proc_bad(const void*, int, uint32_t, uint32_t* bad) {
-    *bad = 0;
-    return 0;
-}
-
-static Proc find_proc(const SkBitmap& bm, SkPMColor expect32, uint16_t expect16,
-                      uint8_t expect8, uint32_t* expect) {
-    switch (bm.colorType()) {
-        case kN32_SkColorType:
-            *expect = expect32;
-            return proc_32;
-        case kARGB_4444_SkColorType:
-        case kRGB_565_SkColorType:
-            *expect = expect16;
-            return proc_16;
-        case kAlpha_8_SkColorType:
-            *expect = expect8;
-            return proc_8;
-        default:
-            *expect = 0;
-            return proc_bad;
-    }
-}
-
-static bool check_color(const SkBitmap& bm, SkPMColor expect32,
-                        uint16_t expect16, uint8_t expect8,
-                        skiatest::Reporter* reporter) {
-    uint32_t expect;
-    Proc proc = find_proc(bm, expect32, expect16, expect8, &expect);
-    for (int y = 0; y < bm.height(); y++) {
-        uint32_t bad;
-        int x = proc(bm.getAddr(0, y), bm.width(), expect, &bad);
-        if (x >= 0) {
-            ERRORF(reporter, "BlitRow colortype=%s [%d %d] expected %x got %x",
-                   gColorTypeName[bm.colorType()], x, y, expect, bad);
-            return false;
-        }
-    }
-    return true;
-}
-
-// Make sure our blits always map src==0 to a noop, and src==FF to full opaque
-static void test_00_FF(skiatest::Reporter* reporter) {
-    static const int W = 256;
-
-    static const SkColorType gDstColorType[] = {
-        kN32_SkColorType,
-        kRGB_565_SkColorType,
-    };
-
-    static const struct {
-        SkColor     fSrc;
-        SkColor     fDst;
-        SkPMColor   fResult32;
-        uint16_t    fResult16;
-        uint8_t     fResult8;
-    } gSrcRec[] = {
-        { 0,            0,          0,                                    0,      0 },
-        { 0,            0xFFFFFFFF, SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
-        { 0xFFFFFFFF,   0,          SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
-        { 0xFFFFFFFF,   0xFFFFFFFF, SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
-    };
-
-    SkPaint paint;
-
-    SkBitmap srcBM;
-    srcBM.allocN32Pixels(W, 1);
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gDstColorType); i++) {
-        SkImageInfo info = SkImageInfo::Make(W, 1, gDstColorType[i],
-                                             kPremul_SkAlphaType);
-        SkBitmap dstBM;
-        dstBM.allocPixels(info);
-
-        SkCanvas canvas(dstBM);
-        for (size_t j = 0; j < SK_ARRAY_COUNT(gSrcRec); j++) {
-            srcBM.eraseColor(gSrcRec[j].fSrc);
-            dstBM.eraseColor(gSrcRec[j].fDst);
-
-            for (int k = 0; k < 4; k++) {
-                bool dither = (k & 1) != 0;
-                bool blend = (k & 2) != 0;
-                if (gSrcRec[j].fSrc != 0 && blend) {
-                    // can't make a numerical promise about blending anything
-                    // but 0
-                 //   continue;
-                }
-                paint.setDither(dither);
-                paint.setAlpha(blend ? 0x80 : 0xFF);
-                canvas.drawBitmap(srcBM, 0, 0, &paint);
-                if (!check_color(dstBM, gSrcRec[j].fResult32, gSrcRec[j].fResult16,
-                                 gSrcRec[j].fResult8, reporter)) {
-                    SkDebugf("--- src index %d dither %d blend %d\n", j, dither, blend);
-                }
-            }
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-struct Mesh {
-    SkPoint     fPts[4];
-
-    Mesh(const SkBitmap& bm, SkPaint* paint) {
-        const SkScalar w = SkIntToScalar(bm.width());
-        const SkScalar h = SkIntToScalar(bm.height());
-        fPts[0].set(0, 0);
-        fPts[1].set(w, 0);
-        fPts[2].set(w, h);
-        fPts[3].set(0, h);
-        SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode,
-                                                   SkShader::kClamp_TileMode);
-        paint->setShader(s)->unref();
-
-    }
-
-    void draw(SkCanvas* canvas, SkPaint* paint) {
-        canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode, 4, fPts, fPts,
-                             NULL, NULL, NULL, 0, *paint);
-    }
-};
-
-#include "SkImageEncoder.h"
-static void save_bm(const SkBitmap& bm, const char name[]) {
-    SkImageEncoder::EncodeFile(name, bm, SkImageEncoder::kPNG_Type, 100);
-}
-
-static bool gOnce;
-
-// Make sure our blits are invariant with the width of the blit (i.e. that
-// special case for 8 at a time have the same results as narrower blits)
-static void test_diagonal(skiatest::Reporter* reporter) {
-    static const int W = 64;
-    static const int H = W;
-
-    static const SkColorType gDstColorType[] = {
-        kN32_SkColorType,
-        kRGB_565_SkColorType,
-    };
-
-    static const SkColor gDstBG[] = { 0, 0xFFFFFFFF };
-
-    SkPaint paint;
-
-    SkBitmap srcBM;
-    srcBM.allocN32Pixels(W, H);
-    SkRect srcR = {
-        0, 0, SkIntToScalar(srcBM.width()), SkIntToScalar(srcBM.height()) };
-
-    // cons up a mesh to draw the bitmap with
-    Mesh mesh(srcBM, &paint);
-
-    SkImageInfo info = SkImageInfo::Make(W, H, kUnknown_SkColorType,
-                                         kPremul_SkAlphaType);
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gDstColorType); i++) {
-        info = info.makeColorType(gDstColorType[i]);
-
-        SkBitmap dstBM0, dstBM1;
-        dstBM0.allocPixels(info);
-        dstBM1.allocPixels(info);
-
-        SkCanvas canvas0(dstBM0);
-        SkCanvas canvas1(dstBM1);
-        SkColor bgColor;
-
-        for (size_t j = 0; j < SK_ARRAY_COUNT(gDstBG); j++) {
-            bgColor = gDstBG[j];
-
-            for (int c = 0; c <= 0xFF; c++) {
-                srcBM.eraseARGB(0xFF, c, c, c);
-
-                for (int k = 0; k < 4; k++) {
-                    bool dither = (k & 1) != 0;
-                    uint8_t alpha = (k & 2) ? 0x80 : 0xFF;
-                    paint.setDither(dither);
-                    paint.setAlpha(alpha);
-
-                    dstBM0.eraseColor(bgColor);
-                    dstBM1.eraseColor(bgColor);
-
-                    canvas0.drawRect(srcR, paint);
-                    mesh.draw(&canvas1, &paint);
-
-                    if (!gOnce && false) {
-                        save_bm(dstBM0, "drawBitmap.png");
-                        save_bm(dstBM1, "drawMesh.png");
-                        gOnce = true;
-                    }
-
-                    if (memcmp(dstBM0.getPixels(), dstBM1.getPixels(), dstBM0.getSize())) {
-                        ERRORF(reporter, "Diagonal colortype=%s bg=0x%x dither=%d"
-                               " alpha=0x%x src=0x%x",
-                               gColorTypeName[gDstColorType[i]], bgColor, dither,
-                               alpha, c);
-                    }
-                }
-            }
-        }
-    }
-}
-
-DEF_TEST(BlitRow, reporter) {
-    test_00_FF(reporter);
-    test_diagonal(reporter);
-}
diff --git a/src/third_party/skia/tests/BlurTest.cpp b/src/third_party/skia/tests/BlurTest.cpp
index 143d777..5d95718 100644
--- a/src/third_party/skia/tests/BlurTest.cpp
+++ b/src/third_party/skia/tests/BlurTest.cpp
@@ -8,11 +8,13 @@
 #include "SkBlurMask.h"
 #include "SkBlurMaskFilter.h"
 #include "SkBlurDrawLooper.h"
-#include "SkLayerDrawLooper.h"
-#include "SkEmbossMaskFilter.h"
 #include "SkCanvas.h"
+#include "SkColorFilter.h"
+#include "SkEmbossMaskFilter.h"
+#include "SkLayerDrawLooper.h"
 #include "SkMath.h"
 #include "SkPaint.h"
+#include "SkPath.h"
 #include "Test.h"
 
 #if SK_SUPPORT_GPU
@@ -74,9 +76,6 @@
     const int xOff = itest.fLeft - iref.fLeft;
     const int yOff = itest.fTop - iref.fTop;
 
-    SkAutoLockPixels alpRef(ref);
-    SkAutoLockPixels alpTest(test);
-
     for (int y = 0; y < test.height(); ++y) {
         for (int x = 0; x < test.width(); ++x) {
             SkColor testColor = test.getColor(x, y);
@@ -98,8 +97,7 @@
     return true;
 }
 
-static void test_blur_drawing(skiatest::Reporter* reporter) {
-
+DEF_TEST(BlurDrawing, reporter) {
     SkPaint paint;
     paint.setColor(SK_ColorGRAY);
     paint.setStyle(SkPaint::kStroke_Style);
@@ -111,11 +109,7 @@
 
         const uint32_t flagPermutations = SkBlurMaskFilter::kAll_BlurFlag;
         for (uint32_t flags = 0; flags < flagPermutations; ++flags) {
-            SkMaskFilter* filter;
-            filter = SkBlurMaskFilter::Create(blurStyle, sigma, flags);
-
-            paint.setMaskFilter(filter);
-            filter->unref();
+            paint.setMaskFilter(SkBlurMaskFilter::Make(blurStyle, sigma, flags));
 
             for (size_t test = 0; test < SK_ARRAY_COUNT(tests); ++test) {
                 SkPath path;
@@ -170,8 +164,9 @@
 
     memset(src.fImage, 0xff, src.computeTotalImageSize());
 
-    dst.fImage = NULL;
-    SkBlurMask::BlurGroundTruth(sigma, &dst, src, kNormal_SkBlurStyle);
+    if (!SkBlurMask::BlurGroundTruth(sigma, &dst, src, kNormal_SkBlurStyle)) {
+        return;
+    }
 
     int midX = dst.fBounds.centerX();
     int midY = dst.fBounds.centerY();
@@ -233,10 +228,8 @@
 
     SkPaint blurPaint;
     blurPaint.setColor(SK_ColorWHITE);
-    SkMaskFilter* filter = SkBlurMaskFilter::Create(kNormal_SkBlurStyle,
-                                                    gaussianSigma,
-                                                    SkBlurMaskFilter::kHighQuality_BlurFlag);
-    blurPaint.setMaskFilter(filter)->unref();
+    blurPaint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, gaussianSigma,
+                                                   SkBlurMaskFilter::kHighQuality_BlurFlag));
 
     canvas->drawColor(SK_ColorBLACK);
     canvas->drawPath(path, blurPaint);
@@ -246,12 +239,8 @@
 static void readback(SkCanvas* canvas, int* result, int resultCount) {
     SkBitmap readback;
     readback.allocN32Pixels(resultCount, 30);
+    canvas->readPixels(readback, 0, 0);
 
-    SkIRect readBackRect = { 0, 0, resultCount, 30 };
-
-    canvas->readPixels(readBackRect, &readback);
-
-    readback.lockPixels();
     SkPMColor* pixels = (SkPMColor*) readback.getAddr32(0, 15);
 
     for (int i = 0; i < resultCount; ++i) {
@@ -275,24 +264,18 @@
 #if SK_SUPPORT_GPU
 #if 0
 // temporary disable; see below for explanation
-static bool gpu_blur_path(GrContextFactory* factory, const SkPath& path,
+static bool gpu_blur_path(GrContext* context, const SkPath& path,
                           SkScalar gaussianSigma,
                           int* result, int resultCount) {
-
-    GrContext* grContext = factory->get(GrContextFactory::kNative_GLContextType);
-    if (NULL == grContext) {
-        return false;
-    }
-
-    GrTextureDesc desc;
+    GrSurfaceDesc desc;
     desc.fConfig = kSkia8888_GrPixelConfig;
-    desc.fFlags = kRenderTarget_GrTextureFlagBit;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
     desc.fWidth = resultCount;
     desc.fHeight = 30;
     desc.fSampleCnt = 0;
 
-    SkAutoTUnref<GrTexture> texture(grContext->createUncachedTexture(desc, NULL, 0));
-    SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice, (grContext, texture.get())));
+    sk_sp<GrTexture> texture(grContext->createTexture(desc, false, nullptr, 0));
+    sk_sp<SkGpuDevice> device(new SkGpuDevice(grContext, texture.get()));
     SkCanvas canvas(device.get());
 
     blur_path(&canvas, path, gaussianSigma);
@@ -325,8 +308,7 @@
 }
 
 // Test out the normal blur style with a wide range of sigmas
-static void test_sigma_range(skiatest::Reporter* reporter, GrContextFactory* factory) {
-
+DEF_TEST(BlurSigmaRange, reporter) {
     static const int kSize = 100;
 
     // The geometry is offset a smidge to trigger:
@@ -364,7 +346,7 @@
 #if SK_SUPPORT_GPU
 #if 0
         int gpuResult[kSize];
-        bool haveGPUResult = gpu_blur_path(factory, rectPath, sigma, gpuResult, kSize);
+        bool haveGPUResult = gpu_blur_path(context, rectPath, sigma, gpuResult, kSize);
         // Disabling this test for now -- I don't think it's a legit comparison.
         // Will continue to investigate this.
         if (haveGPUResult) {
@@ -394,24 +376,6 @@
             kHigh_SkBlurQuality : kLow_SkBlurQuality;
 }
 
-static uint32_t blurMaskFilterFlags_to_blurDrawLooperFlags(uint32_t bmf) {
-    const struct {
-        uint32_t fBlurMaskFilterFlag;
-        uint32_t fBlurDrawLooperFlag;
-    } pairs[] = {
-        { SkBlurMaskFilter::kIgnoreTransform_BlurFlag, SkBlurDrawLooper::kIgnoreTransform_BlurFlag },
-        { SkBlurMaskFilter::kHighQuality_BlurFlag,     SkBlurDrawLooper::kHighQuality_BlurFlag },
-    };
-
-    uint32_t bdl = 0;
-    for (size_t i = 0; i < SK_ARRAY_COUNT(pairs); ++i) {
-        if (bmf & pairs[i].fBlurMaskFilterFlag) {
-            bdl |= pairs[i].fBlurDrawLooperFlag;
-        }
-    }
-    return bdl;
-}
-
 static void test_blurDrawLooper(skiatest::Reporter* reporter, SkScalar sigma,
                                 SkBlurStyle style, uint32_t blurMaskFilterFlags) {
     if (kNormal_SkBlurStyle != style) {
@@ -421,15 +385,10 @@
     const SkColor color = 0xFF335577;
     const SkScalar dx = 10;
     const SkScalar dy = -5;
-    const SkBlurQuality quality = blurMaskFilterFlags_as_quality(blurMaskFilterFlags);
-    uint32_t flags = blurMaskFilterFlags_to_blurDrawLooperFlags(blurMaskFilterFlags);
+    sk_sp<SkDrawLooper> lp(SkBlurDrawLooper::Make(color, sigma, dx, dy));
+    const bool expectSuccess = sigma > 0;
 
-    SkAutoTUnref<SkDrawLooper> lp(SkBlurDrawLooper::Create(color, sigma, dx, dy, flags));
-
-    const bool expectSuccess = sigma > 0 &&
-                               0 == (flags & SkBlurDrawLooper::kIgnoreTransform_BlurFlag);
-
-    if (NULL == lp.get()) {
+    if (nullptr == lp) {
         REPORTER_ASSERT(reporter, sigma <= 0);
     } else {
         SkDrawLooper::BlurShadowRec rec;
@@ -441,13 +400,13 @@
             REPORTER_ASSERT(reporter, rec.fOffset.y() == dy);
             REPORTER_ASSERT(reporter, rec.fColor == color);
             REPORTER_ASSERT(reporter, rec.fStyle == style);
-            REPORTER_ASSERT(reporter, rec.fQuality == quality);
+            REPORTER_ASSERT(reporter, rec.fQuality == kLow_SkBlurQuality);
         }
     }
 }
 
-static void test_delete_looper(skiatest::Reporter* reporter, SkDrawLooper* lp, SkScalar sigma,
-                               SkBlurStyle style, SkBlurQuality quality, bool expectSuccess) {
+static void test_looper(skiatest::Reporter* reporter, sk_sp<SkDrawLooper> lp, SkScalar sigma,
+                        SkBlurStyle style, SkBlurQuality quality, bool expectSuccess) {
     SkDrawLooper::BlurShadowRec rec;
     bool success = lp->asABlurShadow(&rec);
     REPORTER_ASSERT(reporter, success == expectSuccess);
@@ -459,54 +418,54 @@
         REPORTER_ASSERT(reporter, rec.fStyle == style);
         REPORTER_ASSERT(reporter, rec.fQuality == quality);
     }
-    lp->unref();
 }
 
 static void make_noop_layer(SkLayerDrawLooper::Builder* builder) {
     SkLayerDrawLooper::LayerInfo info;
 
     info.fPaintBits = 0;
-    info.fColorMode = SkXfermode::kDst_Mode;
+    info.fColorMode = SkBlendMode::kDst;
     builder->addLayer(info);
 }
 
-static void make_blur_layer(SkLayerDrawLooper::Builder* builder, SkMaskFilter* mf) {
+static void make_blur_layer(SkLayerDrawLooper::Builder* builder, sk_sp<SkMaskFilter> mf) {
     SkLayerDrawLooper::LayerInfo info;
 
     info.fPaintBits = SkLayerDrawLooper::kMaskFilter_Bit;
-    info.fColorMode = SkXfermode::kSrc_Mode;
+    info.fColorMode = SkBlendMode::kSrc;
     SkPaint* paint = builder->addLayer(info);
-    paint->setMaskFilter(mf);
+    paint->setMaskFilter(std::move(mf));
 }
 
-static void test_layerDrawLooper(skiatest::Reporter* reporter, SkMaskFilter* mf, SkScalar sigma,
-                                 SkBlurStyle style, SkBlurQuality quality, bool expectSuccess) {
+static void test_layerDrawLooper(skiatest::Reporter* reporter, sk_sp<SkMaskFilter> mf,
+                                 SkScalar sigma, SkBlurStyle style, SkBlurQuality quality,
+                                 bool expectSuccess) {
 
     SkLayerDrawLooper::LayerInfo info;
     SkLayerDrawLooper::Builder builder;
 
     // 1 layer is too few
     make_noop_layer(&builder);
-    test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, false);
+    test_looper(reporter, builder.detach(), sigma, style, quality, false);
 
     // 2 layers is good, but need blur
     make_noop_layer(&builder);
     make_noop_layer(&builder);
-    test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, false);
+    test_looper(reporter, builder.detach(), sigma, style, quality, false);
 
     // 2 layers is just right
     make_noop_layer(&builder);
     make_blur_layer(&builder, mf);
-    test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, expectSuccess);
+    test_looper(reporter, builder.detach(), sigma, style, quality, expectSuccess);
 
     // 3 layers is too many
     make_noop_layer(&builder);
     make_blur_layer(&builder, mf);
     make_noop_layer(&builder);
-    test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, false);
+    test_looper(reporter, builder.detach(), sigma, style, quality, false);
 }
 
-static void test_asABlur(skiatest::Reporter* reporter) {
+DEF_TEST(BlurAsABlur, reporter) {
     const SkBlurStyle styles[] = {
         kNormal_SkBlurStyle, kSolid_SkBlurStyle, kOuter_SkBlurStyle, kInner_SkBlurStyle
     };
@@ -518,14 +477,14 @@
     // Test asABlur for SkBlurMaskFilter
     //
     for (size_t i = 0; i < SK_ARRAY_COUNT(styles); ++i) {
-        const SkBlurStyle style = (SkBlurStyle)styles[i];
+        const SkBlurStyle style = styles[i];
         for (size_t j = 0; j < SK_ARRAY_COUNT(sigmas); ++j) {
             const SkScalar sigma = sigmas[j];
             for (int flags = 0; flags <= SkBlurMaskFilter::kAll_BlurFlag; ++flags) {
                 const SkBlurQuality quality = blurMaskFilterFlags_as_quality(flags);
 
-                SkAutoTUnref<SkMaskFilter> mf(SkBlurMaskFilter::Create(style, sigma, flags));
-                if (NULL == mf.get()) {
+                sk_sp<SkMaskFilter> mf(SkBlurMaskFilter::Make(style, sigma, flags));
+                if (nullptr == mf.get()) {
                     REPORTER_ASSERT(reporter, sigma <= 0);
                 } else {
                     REPORTER_ASSERT(reporter, sigma > 0);
@@ -539,7 +498,7 @@
                         REPORTER_ASSERT(reporter, rec.fStyle == style);
                         REPORTER_ASSERT(reporter, rec.fQuality == quality);
                     }
-                    test_layerDrawLooper(reporter, mf, sigma, style, quality, success);
+                    test_layerDrawLooper(reporter, std::move(mf), sigma, style, quality, success);
                 }
                 test_blurDrawLooper(reporter, sigma, style, flags);
             }
@@ -554,8 +513,8 @@
         };
         for (size_t j = 0; j < SK_ARRAY_COUNT(sigmas); ++j) {
             const SkScalar sigma = sigmas[j];
-            SkAutoTUnref<SkMaskFilter> mf(SkEmbossMaskFilter::Create(sigma, light));
-            if (mf.get()) {
+            auto mf(SkEmbossMaskFilter::Make(sigma, light));
+            if (mf) {
                 SkMaskFilter::BlurRec rec;
                 bool success = mf->asABlur(&rec);
                 REPORTER_ASSERT(reporter, !success);
@@ -564,10 +523,166 @@
     }
 }
 
-///////////////////////////////////////////////////////////////////////////////////////////
+#if SK_SUPPORT_GPU
 
-DEF_GPUTEST(Blur, reporter, factory) {
-    test_blur_drawing(reporter);
-    test_sigma_range(reporter, factory);
-    test_asABlur(reporter);
+// This exercises the problem discovered in crbug.com/570232. The return value from
+// SkBlurMask::BoxBlur wasn't being checked in SkBlurMaskFilter.cpp::GrRRectBlurEffect::Create
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SmallBoxBlurBug, reporter, ctxInfo) {
+
+    SkImageInfo info = SkImageInfo::MakeN32Premul(128, 128);
+    auto surface(SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo, info));
+    SkCanvas* canvas = surface->getCanvas();
+
+    SkRect r = SkRect::MakeXYWH(10, 10, 100, 100);
+    SkRRect rr = SkRRect::MakeRectXY(r, 10, 10);
+
+    SkPaint p;
+    p.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, 0.01f));
+
+    canvas->drawRRect(rr, p);
 }
+
+#endif
+
+
+DEF_TEST(BlurredRRectNinePatchComputation, reporter) {
+    const SkRect r = SkRect::MakeXYWH(10, 10, 100, 100);
+    static const SkScalar kBlurRad = 3.0f;
+
+    bool ninePatchable;
+    SkRRect rrectToDraw;
+    SkISize size;
+    SkScalar rectXs[SkBlurMaskFilter::kMaxDivisions], rectYs[SkBlurMaskFilter::kMaxDivisions];
+    SkScalar texXs[SkBlurMaskFilter::kMaxDivisions], texYs[SkBlurMaskFilter::kMaxDivisions];
+    int numX, numY;
+    uint32_t skipMask;
+
+    // not nine-patchable
+    {
+        SkVector radii[4] = { { 100, 100 }, { 0, 0 }, { 100, 100 }, { 0, 0 } };
+
+        SkRRect rr;
+        rr.setRectRadii(r, radii);
+
+        ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, rr, SkRect::MakeEmpty(),
+                                                                    kBlurRad, kBlurRad,
+                                                                    &rrectToDraw, &size,
+                                                                    rectXs, rectYs, texXs, texYs,
+                                                                    &numX, &numY, &skipMask);   
+        REPORTER_ASSERT(reporter, !ninePatchable);
+    }
+
+    // simple circular
+    {
+        static const SkScalar kCornerRad = 10.0f;
+        SkRRect rr;
+        rr.setRectXY(r, kCornerRad, kCornerRad);
+
+        ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, rr, SkRect::MakeEmpty(),
+                                                                    kBlurRad, kBlurRad,
+                                                                    &rrectToDraw, &size,
+                                                                    rectXs, rectYs, texXs, texYs,
+                                                                    &numX, &numY, &skipMask);
+
+        static const SkScalar kAns = 12.0f * kBlurRad + 2.0f * kCornerRad + 1.0f;
+        REPORTER_ASSERT(reporter, ninePatchable);
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kAns));
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kAns));
+        REPORTER_ASSERT(reporter, 4 == numX && 4 == numY);
+        REPORTER_ASSERT(reporter, !skipMask);
+    }
+
+    // simple elliptical
+    {
+        static const SkScalar kXCornerRad = 2.0f;
+        static const SkScalar kYCornerRad = 10.0f;
+        SkRRect rr;
+        rr.setRectXY(r, kXCornerRad, kYCornerRad);
+
+        ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, rr, SkRect::MakeEmpty(),
+                                                                    kBlurRad, kBlurRad,
+                                                                    &rrectToDraw, &size,
+                                                                    rectXs, rectYs, texXs, texYs,
+                                                                    &numX, &numY, &skipMask);
+
+        static const SkScalar kXAns = 12.0f * kBlurRad + 2.0f * kXCornerRad + 1.0f;
+        static const SkScalar kYAns = 12.0f * kBlurRad + 2.0f * kYCornerRad + 1.0f;
+
+        REPORTER_ASSERT(reporter, ninePatchable);
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kXAns));
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kYAns));
+        REPORTER_ASSERT(reporter, 4 == numX && 4 == numY);
+        REPORTER_ASSERT(reporter, !skipMask);
+    }
+
+    // test-out occlusion
+    {
+        static const SkScalar kCornerRad = 10.0f;
+        SkRRect rr;
+        rr.setRectXY(r, kCornerRad, kCornerRad);
+
+        // The rectXs & rectYs should be { 1, 29, 91, 119 }. Add two more points around each.
+        SkScalar testLocs[] = {
+             -18.0f, -9.0f,
+               1.0f,
+               9.0f, 18.0f, 
+              29.0f, 
+              39.0f, 49.0f,
+              91.0f,
+             109.0f, 118.0f,
+             119.0f,
+             139.0f, 149.0f
+        };
+
+        for (int minY = 0; minY < (int)SK_ARRAY_COUNT(testLocs); ++minY) {
+            for (int maxY = minY+1; maxY < (int)SK_ARRAY_COUNT(testLocs); ++maxY) {
+                for (int minX = 0; minX < (int)SK_ARRAY_COUNT(testLocs); ++minX) {
+                    for (int maxX = minX+1; maxX < (int)SK_ARRAY_COUNT(testLocs); ++maxX) {
+                        SkRect occluder = SkRect::MakeLTRB(testLocs[minX], testLocs[minY],
+                                                           testLocs[maxX], testLocs[maxY]);
+                        if (occluder.isEmpty()) {
+                            continue;
+                        }
+
+                        ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(
+                                                                    rr, rr, occluder,
+                                                                    kBlurRad, kBlurRad,
+                                                                    &rrectToDraw, &size,
+                                                                    rectXs, rectYs, texXs, texYs,
+                                                                    &numX, &numY, &skipMask);     
+
+                        static const SkScalar kAns = 12.0f * kBlurRad + 2.0f * kCornerRad + 1.0f;
+                        REPORTER_ASSERT(reporter, ninePatchable);
+                        REPORTER_ASSERT(reporter,
+                                            SkScalarNearlyEqual(SkIntToScalar(size.fWidth), kAns));
+                        REPORTER_ASSERT(reporter,
+                                            SkScalarNearlyEqual(SkIntToScalar(size.fHeight), kAns));
+
+                        int checkBit = 0x1;
+                        for (int y = 0; y < numY-1; ++y) {
+                            for (int x = 0; x < numX-1; ++x) {
+                                SkRect cell = SkRect::MakeLTRB(rectXs[x], rectYs[y],
+                                                               rectXs[x+1], rectYs[y+1]);
+                                REPORTER_ASSERT(reporter,
+                                                    SkToBool(skipMask & checkBit) ==
+                                                    (cell.isEmpty() || occluder.contains(cell)));
+
+                                REPORTER_ASSERT(reporter, texXs[x] >= 0 &&
+                                                          texXs[x] <= size.fWidth);
+                                REPORTER_ASSERT(reporter, texYs[y] >= 0 &&
+                                                          texXs[y] <= size.fHeight);
+
+                                checkBit <<= 1;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+
+    }
+
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/third_party/skia/tests/CPlusPlusEleven.cpp b/src/third_party/skia/tests/CPlusPlusEleven.cpp
new file mode 100644
index 0000000..bbd8a12
--- /dev/null
+++ b/src/third_party/skia/tests/CPlusPlusEleven.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Test.h"
+#include "SkTemplates.h"
+#include "SkScopeExit.h"
+#include <utility>
+
+namespace {
+class Moveable {
+public:
+    Moveable() {}
+    Moveable(Moveable&&) {}
+    Moveable& operator=(Moveable&&) { return *this; }
+private:
+    Moveable(const Moveable&);
+    Moveable& operator=(const Moveable&);
+};
+template <typename T> void deleter(T*) { }
+template <typename T> struct Deleter {
+    void operator()(T* t) { delete static_cast<const Moveable*>(t); }
+};
+} // namespace
+
+DEF_TEST(CPlusPlusEleven_RvalueAndMove, r) {
+    Moveable src1; Moveable dst1(std::move(src1));
+    Moveable src2, dst2; dst2 = std::move(src2);
+}
+
+DEF_TEST(CPlusPlusEleven_constexpr, r) {
+    static constexpr int x = Sk32ToBool(50);
+    REPORTER_ASSERT(r, x == 1);
+    static constexpr int y = SkTPin<int>(100, 0, 10);
+    REPORTER_ASSERT(r, y == 10);
+}
+
+namespace {
+struct MoveableCopyable {
+    bool fCopied;
+    MoveableCopyable() : fCopied(false) {}
+    MoveableCopyable(const MoveableCopyable &o) : fCopied(true) {}
+    MoveableCopyable(MoveableCopyable &&o) : fCopied(o.fCopied) {}
+};
+struct TestClass {
+    MoveableCopyable fFoo;
+};
+}  // namespace
+
+DEF_TEST(CPlusPlusEleven_default_move, r) {
+    TestClass a;
+    TestClass b(a);
+    TestClass c(std::move(a));
+    REPORTER_ASSERT(r, b.fFoo.fCopied);
+    REPORTER_ASSERT(r, !c.fFoo.fCopied);
+}
+
+DEF_TEST(SkAtScopeExit, r) {
+    int x = 5;
+    {
+        SK_AT_SCOPE_EXIT(x--);
+        REPORTER_ASSERT(r, x == 5);
+    }
+    REPORTER_ASSERT(r, x == 4);
+}
diff --git a/src/third_party/skia/tests/CTest.cpp b/src/third_party/skia/tests/CTest.cpp
new file mode 100644
index 0000000..1f301ef
--- /dev/null
+++ b/src/third_party/skia/tests/CTest.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#include "sk_canvas.h"
+#include "sk_paint.h"
+#include "sk_surface.h"
+#include "sk_shader.h"
+
+static void shader_test(skiatest::Reporter* reporter) {
+    sk_imageinfo_t info =
+        {64, 64, sk_colortype_get_default_8888(), PREMUL_SK_ALPHATYPE};
+    sk_surface_t* surface  = sk_surface_new_raster(&info, nullptr);
+    sk_canvas_t* canvas = sk_surface_get_canvas(surface);
+    sk_paint_t* paint = sk_paint_new();
+
+    sk_shader_tilemode_t tilemode = CLAMP_SK_SHADER_TILEMODE;
+    sk_point_t point = {0.0f, 0.0f};
+    sk_point_t point2 = {30.0f, 40.0f};
+    sk_color_t colors[] = {
+        (sk_color_t)sk_color_set_argb(0xFF, 0x00, 0x00, 0xFF),
+        (sk_color_t)sk_color_set_argb(0xFF, 0x00, 0xFF, 0x00)
+    };
+    sk_shader_t* shader;
+
+    shader = sk_shader_new_radial_gradient(
+            &point, 1.0f, colors, nullptr, 2, tilemode, nullptr);
+    REPORTER_ASSERT(reporter, shader != nullptr);
+    sk_paint_set_shader(paint, shader);
+    sk_shader_unref(shader);
+    sk_canvas_draw_paint(canvas, paint);
+
+    shader = sk_shader_new_sweep_gradient(&point, colors, nullptr, 2, nullptr);
+    REPORTER_ASSERT(reporter, shader != nullptr);
+    sk_paint_set_shader(paint, shader);
+    sk_shader_unref(shader);
+    sk_canvas_draw_paint(canvas, paint);
+
+    shader = sk_shader_new_two_point_conical_gradient(
+            &point, 10.0f,  &point2, 50.0f, colors, nullptr, 2, tilemode, nullptr);
+    REPORTER_ASSERT(reporter, shader != nullptr);
+    sk_paint_set_shader(paint, shader);
+    sk_shader_unref(shader);
+    sk_canvas_draw_paint(canvas, paint);
+
+    sk_paint_delete(paint);
+    sk_surface_unref(surface);
+}
+
+static void test_c(skiatest::Reporter* reporter) {
+    sk_colortype_t ct = sk_colortype_get_default_8888();
+
+    sk_imageinfo_t info = {
+        1, 1, ct, PREMUL_SK_ALPHATYPE
+    };
+    uint32_t pixel[1] = { 0 };
+    sk_surfaceprops_t surfaceProps = { UNKNOWN_SK_PIXELGEOMETRY };
+
+    sk_surface_t* surface = sk_surface_new_raster_direct(&info, pixel, sizeof(uint32_t),
+                                                         &surfaceProps);
+    sk_paint_t* paint = sk_paint_new();
+
+    sk_canvas_t* canvas = sk_surface_get_canvas(surface);
+    sk_canvas_draw_paint(canvas, paint);
+    REPORTER_ASSERT(reporter, 0xFF000000 == pixel[0]);
+
+    sk_paint_set_color(paint, sk_color_set_argb(0xFF, 0xFF, 0xFF, 0xFF));
+    sk_canvas_draw_paint(canvas, paint);
+    REPORTER_ASSERT(reporter, 0xFFFFFFFF == pixel[0]);
+
+    sk_paint_set_xfermode_mode(paint, SRC_SK_XFERMODE_MODE);
+    sk_paint_set_color(paint, sk_color_set_argb(0x80, 0x80, 0x80, 0x80));
+    sk_canvas_draw_paint(canvas, paint);
+    REPORTER_ASSERT(reporter, 0x80404040 == pixel[0]);
+
+    sk_paint_delete(paint);
+    sk_surface_unref(surface);
+}
+
+DEF_TEST(C_API, reporter) {
+    test_c(reporter);
+    shader_test(reporter);
+}
diff --git a/src/third_party/skia/tests/CachedDataTest.cpp b/src/third_party/skia/tests/CachedDataTest.cpp
new file mode 100644
index 0000000..fe7bb5f
--- /dev/null
+++ b/src/third_party/skia/tests/CachedDataTest.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCachedData.h"
+#include "SkDiscardableMemoryPool.h"
+#include "Test.h"
+
+enum LockedState {
+    kUnlocked,
+    kLocked,
+};
+
+enum CachedState {
+    kNotInCache,
+    kInCache,
+};
+
+static void check_data(skiatest::Reporter* reporter, SkCachedData* data,
+                       int refcnt, CachedState cacheState, LockedState lockedState) {
+    REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt);
+    REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState));
+    REPORTER_ASSERT(reporter, data->testing_only_isLocked() == (lockedState == kLocked));
+}
+
+static SkCachedData* make_data(size_t size, SkDiscardableMemoryPool* pool) {
+    if (pool) {
+        SkDiscardableMemory* dm = pool->create(size);
+        // the pool "can" return null, but it shouldn't in these controlled conditions
+        SkASSERT_RELEASE(dm);
+        return new SkCachedData(size, dm);
+    } else {
+        return new SkCachedData(sk_malloc_throw(size), size);
+    }
+}
+
+// returns with the data locked by client and cache
+static SkCachedData* test_locking(skiatest::Reporter* reporter,
+                                  size_t size, SkDiscardableMemoryPool* pool) {
+    SkCachedData* data = make_data(size, pool);
+
+    memset(data->writable_data(), 0x80, size);  // just to use writable_data()
+
+    check_data(reporter, data, 1, kNotInCache, kLocked);
+
+    data->ref();
+    check_data(reporter, data, 2, kNotInCache, kLocked);
+    data->unref();
+    check_data(reporter, data, 1, kNotInCache, kLocked);
+
+    data->attachToCacheAndRef();
+    check_data(reporter, data, 2, kInCache, kLocked);
+
+    data->unref();
+    check_data(reporter, data, 1, kInCache, kUnlocked);
+
+    data->ref();
+    check_data(reporter, data, 2, kInCache, kLocked);
+
+    return data;
+}
+
+/*
+ *  SkCachedData behaves differently (regarding its locked/unlocked state) depending on
+ *  when it is in the cache or not. Being in the cache is signaled by calling attachToCacheAndRef()
+ *  instead of ref(). (and balanced by detachFromCacheAndUnref).
+ *
+ *  Thus, among other things, we test the end-of-life behavior when the client is the last owner
+ *  and when the cache is.
+ */
+DEF_TEST(CachedData, reporter) {
+    sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(1000));
+
+    for (int useDiscardable = 0; useDiscardable <= 1; ++useDiscardable) {
+        const size_t size = 100;
+
+        // test with client as last owner
+        SkCachedData* data = test_locking(reporter, size, useDiscardable ? pool.get() : nullptr);
+        check_data(reporter, data, 2, kInCache, kLocked);
+        data->detachFromCacheAndUnref();
+        check_data(reporter, data, 1, kNotInCache, kLocked);
+        data->unref();
+
+        // test with cache as last owner
+        data = test_locking(reporter, size, useDiscardable ? pool.get() : nullptr);
+        check_data(reporter, data, 2, kInCache, kLocked);
+        data->unref();
+        check_data(reporter, data, 1, kInCache, kUnlocked);
+        data->detachFromCacheAndUnref();
+    }
+}
diff --git a/src/third_party/skia/tests/CachedDecodingPixelRefTest.cpp b/src/third_party/skia/tests/CachedDecodingPixelRefTest.cpp
index b3eb532..0eecdbb 100644
--- a/src/third_party/skia/tests/CachedDecodingPixelRefTest.cpp
+++ b/src/third_party/skia/tests/CachedDecodingPixelRefTest.cpp
@@ -6,209 +6,73 @@
  */
 
 #include "SkBitmap.h"
-#include "SkCachingPixelRef.h"
 #include "SkCanvas.h"
 #include "SkData.h"
-#include "SkDecodingImageGenerator.h"
 #include "SkDiscardableMemoryPool.h"
-#include "SkImageDecoder.h"
-#include "SkImageGeneratorPriv.h"
+#include "SkImage.h"
+#include "SkImageEncoder.h"
+#include "SkImageGenerator.h"
+#include "SkMakeUnique.h"
 #include "SkResourceCache.h"
 #include "SkStream.h"
 #include "SkUtils.h"
 
 #include "Test.h"
 
-/**
- * Fill this bitmap with some color.
- */
-static void make_test_image(SkBitmap* bm) {
-    const int W = 50, H = 50;
-    bm->allocN32Pixels(W, H);
-    bm->eraseColor(SK_ColorBLACK);
-    SkCanvas canvas(*bm);
-    SkPaint paint;
-    paint.setColor(SK_ColorBLUE);
-    canvas.drawRectCoords(0, 0, SkIntToScalar(W/2),
-                          SkIntToScalar(H/2), paint);
-    paint.setColor(SK_ColorWHITE);
-    canvas.drawRectCoords(SkIntToScalar(W/2), SkIntToScalar(H/2),
-                          SkIntToScalar(W), SkIntToScalar(H), paint);
-}
-
-/**
- * encode this bitmap into some data via SkImageEncoder
- */
-static SkData* create_data_from_bitmap(const SkBitmap& bm,
-                                       SkImageEncoder::Type type) {
-    SkDynamicMemoryWStream stream;
-    if (SkImageEncoder::EncodeStream(&stream, bm, type, 100)) {
-        return stream.copyToData();
-    }
-    return NULL;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-static void compare_bitmaps(skiatest::Reporter* reporter,
-                            const SkBitmap& b1, const SkBitmap& b2,
-                            bool pixelPerfect = true) {
-    REPORTER_ASSERT(reporter, b1.empty() == b2.empty());
-    REPORTER_ASSERT(reporter, b1.width() == b2.width());
-    REPORTER_ASSERT(reporter, b1.height() == b2.height());
-    REPORTER_ASSERT(reporter, b1.isNull() == b2.isNull());
-    SkAutoLockPixels autoLockPixels1(b1);
-    SkAutoLockPixels autoLockPixels2(b2);
-    REPORTER_ASSERT(reporter, b1.isNull() == b2.isNull());
-    if (b1.isNull() || b1.empty()) {
-        return;
-    }
-    REPORTER_ASSERT(reporter, b1.getPixels());
-    REPORTER_ASSERT(reporter, b2.getPixels());
-    if ((!(b1.getPixels())) || (!(b2.getPixels()))) {
-        return;
-    }
-    if ((b1.width() != b2.width()) ||
-        (b1.height() != b2.height())) {
-        return;
-    }
-    if (!pixelPerfect) {
-        return;
-    }
-
-    int pixelErrors = 0;
-    for (int y = 0; y < b2.height(); ++y) {
-        for (int x = 0; x < b2.width(); ++x) {
-            if (b1.getColor(x, y) != b2.getColor(x, y)) {
-                ++pixelErrors;
-            }
-        }
-    }
-    REPORTER_ASSERT(reporter, 0 == pixelErrors);
-}
-
-typedef bool (*InstallEncoded)(SkData* encoded, SkBitmap* dst);
-
-/**
-   This function tests three differently encoded images against the
-   original bitmap */
-static void test_three_encodings(skiatest::Reporter* reporter,
-                                 InstallEncoded install) {
-    SkBitmap original;
-    make_test_image(&original);
-    REPORTER_ASSERT(reporter, !original.empty());
-    REPORTER_ASSERT(reporter, !original.isNull());
-    if (original.empty() || original.isNull()) {
-        return;
-    }
-    static const SkImageEncoder::Type types[] = {
-        SkImageEncoder::kPNG_Type,
-        SkImageEncoder::kJPEG_Type,
-        SkImageEncoder::kWEBP_Type
-    };
-    for (size_t i = 0; i < SK_ARRAY_COUNT(types); i++) {
-        SkImageEncoder::Type type = types[i];
-        SkAutoDataUnref encoded(create_data_from_bitmap(original, type));
-        REPORTER_ASSERT(reporter, encoded.get() != NULL);
-        if (NULL == encoded.get()) {
-            continue;
-        }
-        SkBitmap lazy;
-        bool installSuccess = install(encoded.get(), &lazy);
-        REPORTER_ASSERT(reporter, installSuccess);
-        if (!installSuccess) {
-            continue;
-        }
-        REPORTER_ASSERT(reporter, NULL == lazy.getPixels());
-        {
-            SkAutoLockPixels autoLockPixels(lazy);  // now pixels are good.
-            REPORTER_ASSERT(reporter, lazy.getPixels());
-            if (NULL == lazy.getPixels()) {
-                continue;
-            }
-        }
-        // pixels should be gone!
-        REPORTER_ASSERT(reporter, NULL == lazy.getPixels());
-        {
-            SkAutoLockPixels autoLockPixels(lazy);  // now pixels are good.
-            REPORTER_ASSERT(reporter, lazy.getPixels());
-            if (NULL == lazy.getPixels()) {
-                continue;
-            }
-        }
-        bool comparePixels = (SkImageEncoder::kPNG_Type == type);
-        compare_bitmaps(reporter, original, lazy, comparePixels);
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-static bool install_skCachingPixelRef(SkData* encoded, SkBitmap* dst) {
-    return SkCachingPixelRef::Install(
-        SkDecodingImageGenerator::Create(
-            encoded, SkDecodingImageGenerator::Options()), dst);
-}
-static bool install_skDiscardablePixelRef(SkData* encoded, SkBitmap* dst) {
-    // Use system-default discardable memory.
-    return SkInstallDiscardablePixelRef(
-        SkDecodingImageGenerator::Create(
-            encoded, SkDecodingImageGenerator::Options()), dst);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/**
- *  This checks to see that a SkCachingPixelRef and a
- *  SkDiscardablePixelRef works as advertised with a
- *  SkDecodingImageGenerator.
- */
-DEF_TEST(DecodingImageGenerator, reporter) {
-    test_three_encodings(reporter, install_skCachingPixelRef);
-    test_three_encodings(reporter, install_skDiscardablePixelRef);
-}
-
 class TestImageGenerator : public SkImageGenerator {
 public:
     enum TestType {
-        kFailGetInfo_TestType,
         kFailGetPixels_TestType,
         kSucceedGetPixels_TestType,
         kLast_TestType = kSucceedGetPixels_TestType
     };
     static int Width() { return 10; }
     static int Height() { return 10; }
-    static uint32_t Color() { return 0xff123456; }
-    TestImageGenerator(TestType type, skiatest::Reporter* reporter)
-        : fType(type), fReporter(reporter) {
+    // value choosen so that there is no loss when converting to to RGB565 and back
+    static SkColor Color() { return 0xff10355a; }
+    static SkPMColor PMColor() { return SkPreMultiplyColor(Color()); }
+
+    TestImageGenerator(TestType type, skiatest::Reporter* reporter,
+                       SkColorType colorType = kN32_SkColorType)
+    : INHERITED(GetMyInfo(colorType)), fType(type), fReporter(reporter) {
         SkASSERT((fType <= kLast_TestType) && (fType >= 0));
     }
-    virtual ~TestImageGenerator() { }
+    ~TestImageGenerator() override {}
 
 protected:
-    virtual bool onGetInfo(SkImageInfo* info) SK_OVERRIDE {
-        REPORTER_ASSERT(fReporter, info);
-        if ((NULL == info) || (kFailGetInfo_TestType == fType)) {
-            return false;
-        }
-        *info = SkImageInfo::MakeN32(TestImageGenerator::Width(),
-                                     TestImageGenerator::Height(),
-                                     kOpaque_SkAlphaType);
-        return true;
+    static SkImageInfo GetMyInfo(SkColorType colorType) {
+        return SkImageInfo::Make(TestImageGenerator::Width(), TestImageGenerator::Height(),
+                                 colorType, kOpaque_SkAlphaType);
     }
 
-    virtual bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
-                             SkPMColor ctable[], int* ctableCount) SK_OVERRIDE {
-        REPORTER_ASSERT(fReporter, pixels != NULL);
-        size_t minRowBytes = static_cast<size_t>(info.width() * info.bytesPerPixel());
-        REPORTER_ASSERT(fReporter, rowBytes >= minRowBytes);
-        if ((NULL == pixels)
-            || (fType != kSucceedGetPixels_TestType)
-            || (info.colorType() != kN32_SkColorType)) {
+    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
+                     const Options& options) override {
+        REPORTER_ASSERT(fReporter, pixels != nullptr);
+        REPORTER_ASSERT(fReporter, rowBytes >= info.minRowBytes());
+        if (fType != kSucceedGetPixels_TestType) {
+            return false;
+        }
+        if (info.colorType() != kN32_SkColorType && info.colorType() != getInfo().colorType()) {
             return false;
         }
         char* bytePtr = static_cast<char*>(pixels);
-        for (int y = 0; y < info.height(); ++y) {
-            sk_memset32(reinterpret_cast<SkColor*>(bytePtr),
-                        TestImageGenerator::Color(), info.width());
-            bytePtr += rowBytes;
+        switch (info.colorType()) {
+            case kN32_SkColorType:
+                for (int y = 0; y < info.height(); ++y) {
+                    sk_memset32((uint32_t*)bytePtr,
+                                TestImageGenerator::PMColor(), info.width());
+                    bytePtr += rowBytes;
+                }
+                break;
+            case kRGB_565_SkColorType:
+                for (int y = 0; y < info.height(); ++y) {
+                    sk_memset16((uint16_t*)bytePtr,
+                        SkPixel32ToPixel16(TestImageGenerator::PMColor()), info.width());
+                    bytePtr += rowBytes;
+                }
+                break;
+            default:
+                return false;
         }
         return true;
     }
@@ -216,146 +80,48 @@
 private:
     const TestType fType;
     skiatest::Reporter* const fReporter;
+
+    typedef SkImageGenerator INHERITED;
 };
 
-static void check_test_image_generator_bitmap(skiatest::Reporter* reporter,
-                                              const SkBitmap& bm) {
-    REPORTER_ASSERT(reporter, TestImageGenerator::Width() == bm.width());
-    REPORTER_ASSERT(reporter, TestImageGenerator::Height() == bm.height());
-    SkAutoLockPixels autoLockPixels(bm);
-    REPORTER_ASSERT(reporter, bm.getPixels());
-    if (NULL == bm.getPixels()) {
-        return;
-    }
-    int errors = 0;
-    for (int y = 0; y < bm.height(); ++y) {
-        for (int x = 0; x < bm.width(); ++x) {
-            if (TestImageGenerator::Color() != *bm.getAddr32(x, y)) {
-                ++errors;
-            }
-        }
-    }
-    REPORTER_ASSERT(reporter, 0 == errors);
-}
-
-enum PixelRefType {
-    kSkCaching_PixelRefType,
-    kSkDiscardable_PixelRefType,
-    kLast_PixelRefType = kSkDiscardable_PixelRefType
-};
-
-static void check_pixelref(TestImageGenerator::TestType type,
-                           skiatest::Reporter* reporter,
-                           PixelRefType pixelRefType,
-                           SkDiscardableMemory::Factory* factory) {
-    SkASSERT((pixelRefType >= 0) && (pixelRefType <= kLast_PixelRefType));
-    SkAutoTDelete<SkImageGenerator> gen(SkNEW_ARGS(TestImageGenerator,
-                                                   (type, reporter)));
-    REPORTER_ASSERT(reporter, gen.get() != NULL);
-    SkBitmap lazy;
-    bool success;
-    if (kSkCaching_PixelRefType == pixelRefType) {
-        // Ignore factory; use global cache.
-        success = SkCachingPixelRef::Install(gen.detach(), &lazy);
-    } else {
-        success = SkInstallDiscardablePixelRef(gen.detach(), &lazy, factory);
-    }
-    REPORTER_ASSERT(reporter, success
-                    == (TestImageGenerator::kFailGetInfo_TestType != type));
-    if (TestImageGenerator::kSucceedGetPixels_TestType == type) {
-        check_test_image_generator_bitmap(reporter, lazy);
-    } else if (TestImageGenerator::kFailGetPixels_TestType == type) {
-        SkAutoLockPixels autoLockPixels(lazy);
-        REPORTER_ASSERT(reporter, NULL == lazy.getPixels());
-    }
-}
-
-// new/lock/delete is an odd pattern for a pixelref, but it needs to not assert
-static void test_newlockdelete(skiatest::Reporter* reporter) {
-    SkBitmap bm;
-    SkImageGenerator* ig = new TestImageGenerator(
-        TestImageGenerator::kSucceedGetPixels_TestType, reporter);
-    SkInstallDiscardablePixelRef(ig, &bm);
-    bm.pixelRef()->lockPixels();
-}
-
-/**
- *  This tests the basic functionality of SkDiscardablePixelRef with a
- *  basic SkImageGenerator implementation and several
- *  SkDiscardableMemory::Factory choices.
- */
-DEF_TEST(DiscardableAndCachingPixelRef, reporter) {
-    test_newlockdelete(reporter);
-
-    check_pixelref(TestImageGenerator::kFailGetInfo_TestType,
-                   reporter, kSkCaching_PixelRefType, NULL);
-    check_pixelref(TestImageGenerator::kFailGetPixels_TestType,
-                   reporter, kSkCaching_PixelRefType, NULL);
-    check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType,
-                   reporter, kSkCaching_PixelRefType, NULL);
-
-    check_pixelref(TestImageGenerator::kFailGetInfo_TestType,
-                   reporter, kSkDiscardable_PixelRefType, NULL);
-    check_pixelref(TestImageGenerator::kFailGetPixels_TestType,
-                   reporter, kSkDiscardable_PixelRefType, NULL);
-    check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType,
-                   reporter, kSkDiscardable_PixelRefType, NULL);
-
-    SkAutoTUnref<SkDiscardableMemoryPool> pool(
-        SkDiscardableMemoryPool::Create(1, NULL));
-    REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed());
-    check_pixelref(TestImageGenerator::kFailGetPixels_TestType,
-                   reporter, kSkDiscardable_PixelRefType, pool);
-    REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed());
-    check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType,
-                   reporter, kSkDiscardable_PixelRefType, pool);
-    REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed());
-
-    SkDiscardableMemoryPool* globalPool = SkGetGlobalDiscardableMemoryPool();
-    // Only acts differently from NULL on a platform that has a
-    // default discardable memory implementation that differs from the
-    // global DM pool.
-    check_pixelref(TestImageGenerator::kFailGetPixels_TestType,
-                   reporter, kSkDiscardable_PixelRefType, globalPool);
-    check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType,
-                   reporter, kSkDiscardable_PixelRefType, globalPool);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
 DEF_TEST(Image_NewFromGenerator, r) {
-    TestImageGenerator::TestType testTypes[] = {
-        TestImageGenerator::kFailGetInfo_TestType,
+    const TestImageGenerator::TestType testTypes[] = {
         TestImageGenerator::kFailGetPixels_TestType,
         TestImageGenerator::kSucceedGetPixels_TestType,
     };
+    const SkColorType testColorTypes[] = {
+        kN32_SkColorType,
+        kRGB_565_SkColorType
+    };
     for (size_t i = 0; i < SK_ARRAY_COUNT(testTypes); ++i) {
         TestImageGenerator::TestType test = testTypes[i];
-        SkImageGenerator* gen = SkNEW_ARGS(TestImageGenerator, (test, r));
-        SkAutoTUnref<SkImage> image(SkImage::NewFromGenerator(gen));
-        if (TestImageGenerator::kFailGetInfo_TestType == test) {
-            REPORTER_ASSERT(r, NULL == image.get());
-            continue;
-        }
-        if (NULL == image.get()) {
-            ERRORF(r, "SkImage::NewFromGenerator unexpecedly failed ["
-                   SK_SIZE_T_SPECIFIER "]", i);
-            continue;
-        }
-        REPORTER_ASSERT(r, TestImageGenerator::Width() == image->width());
-        REPORTER_ASSERT(r, TestImageGenerator::Height() == image->height());
+        for (const SkColorType testColorType : testColorTypes) {
+            auto gen = skstd::make_unique<TestImageGenerator>(test, r, testColorType);
+            sk_sp<SkImage> image(SkImage::MakeFromGenerator(std::move(gen)));
+            if (nullptr == image) {
+                ERRORF(r, "SkImage::NewFromGenerator unexpecedly failed ["
+                    SK_SIZE_T_SPECIFIER "]", i);
+                continue;
+            }
+            REPORTER_ASSERT(r, TestImageGenerator::Width() == image->width());
+            REPORTER_ASSERT(r, TestImageGenerator::Height() == image->height());
+            REPORTER_ASSERT(r, image->isLazyGenerated());
 
-        SkBitmap bitmap;
-        bitmap.allocN32Pixels(TestImageGenerator::Width(), TestImageGenerator::Height());
-        SkCanvas canvas(bitmap);
-        const SkColor kDefaultColor = 0xffabcdef;
-        canvas.clear(kDefaultColor);
-        canvas.drawImage(image, 0, 0, NULL);
-        if (TestImageGenerator::kSucceedGetPixels_TestType == test) {
-            REPORTER_ASSERT(
-                    r, TestImageGenerator::Color() == *bitmap.getAddr32(0, 0));
-        } else {
-            REPORTER_ASSERT(r, kDefaultColor == bitmap.getColor(0,0));
+            SkBitmap bitmap;
+            bitmap.allocN32Pixels(TestImageGenerator::Width(), TestImageGenerator::Height());
+            SkCanvas canvas(bitmap);
+            const SkColor kDefaultColor = 0xffabcdef;
+            canvas.clear(kDefaultColor);
+            canvas.drawImage(image, 0, 0, nullptr);
+            if (TestImageGenerator::kSucceedGetPixels_TestType == test) {
+                REPORTER_ASSERT(
+                    r, TestImageGenerator::Color() == bitmap.getColor(0, 0));
+            }
+            else {
+                REPORTER_ASSERT(r, kDefaultColor == bitmap.getColor(0, 0));
+            }
         }
     }
 }
diff --git a/src/third_party/skia/tests/CanvasStateHelpers.cpp b/src/third_party/skia/tests/CanvasStateHelpers.cpp
index 96972b8..1547d2f 100644
--- a/src/third_party/skia/tests/CanvasStateHelpers.cpp
+++ b/src/third_party/skia/tests/CanvasStateHelpers.cpp
@@ -27,12 +27,11 @@
 
 extern "C" bool complex_layers_draw_from_canvas_state(SkCanvasState* state,
         float left, float top, float right, float bottom, int32_t spacer) {
-    SkCanvas* canvas = SkCanvasStateUtils::CreateFromCanvasState(state);
+    std::unique_ptr<SkCanvas> canvas = SkCanvasStateUtils::MakeFromCanvasState(state);
     if (!canvas) {
         return false;
     }
-    complex_layers_draw(canvas, left, top, right, bottom, spacer);
-    canvas->unref();
+    complex_layers_draw(canvas.get(), left, top, right, bottom, spacer);
     return true;
 }
 
@@ -45,14 +44,14 @@
     canvas->drawColor(SK_ColorBLUE);
     canvas->restore();
 
-    canvas->clipRegion(localRegion, (SkRegion::Op) clipOp);
+    canvas->clipRegion(localRegion, (SkClipOp) clipOp);
     canvas->drawColor(SK_ColorBLUE);
 }
 
 extern "C" bool complex_clips_draw_from_canvas_state(SkCanvasState* state,
         int32_t left, int32_t top, int32_t right, int32_t bottom, int32_t clipOp,
         int32_t regionRects, int32_t* rectCoords) {
-    SkCanvas* canvas = SkCanvasStateUtils::CreateFromCanvasState(state);
+    std::unique_ptr<SkCanvas> canvas = SkCanvasStateUtils::MakeFromCanvasState(state);
     if (!canvas) {
         return false;
     }
@@ -64,8 +63,7 @@
         rectCoords += 4;
     }
 
-    complex_clips_draw(canvas, left, top, right, bottom, clipOp, localRegion);
-    canvas->unref();
+    complex_clips_draw(canvas.get(), left, top, right, bottom, clipOp, localRegion);
     return true;
 }
 #endif // SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
diff --git a/src/third_party/skia/tests/CanvasStateTest.cpp b/src/third_party/skia/tests/CanvasStateTest.cpp
index 8364a4b..ae417d4 100644
--- a/src/third_party/skia/tests/CanvasStateTest.cpp
+++ b/src/third_party/skia/tests/CanvasStateTest.cpp
@@ -6,14 +6,17 @@
  */
 
 #include "CanvasStateHelpers.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
+#include "SkClipOpPriv.h"
 #include "SkCanvasStateUtils.h"
 #include "SkCommandLineFlags.h"
 #include "SkDrawFilter.h"
-#include "SkError.h"
 #include "SkPaint.h"
+#include "SkRegion.h"
 #include "SkRRect.h"
 #include "SkRect.h"
+#include "SkTLazy.h"
 #include "Test.h"
 
 // dlopen and the library flag are only used for tests which require this flag.
@@ -37,9 +40,9 @@
     OpenLibResult(skiatest::Reporter* reporter) {
         if (FLAGS_library.count() == 1) {
             fHandle = dlopen(FLAGS_library[0], RTLD_LAZY | RTLD_LOCAL);
-            REPORTER_ASSERT_MESSAGE(reporter, fHandle != NULL, "Failed to open library!");
+            REPORTER_ASSERT_MESSAGE(reporter, fHandle != nullptr, "Failed to open library!");
         } else {
-            fHandle = NULL;
+            fHandle = nullptr;
         }
     }
 
@@ -71,9 +74,10 @@
     };
 
     const int layerAlpha[] = { 255, 255, 0 };
-    const SkCanvas::SaveFlags flags[] = { SkCanvas::kARGB_NoClipLayer_SaveFlag,
-                                          SkCanvas::kARGB_ClipLayer_SaveFlag,
-                                          SkCanvas::kARGB_NoClipLayer_SaveFlag
+    const SkCanvas::SaveLayerFlags flags[] = {
+        static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag),
+        0,
+        static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag),
     };
     REPORTER_ASSERT(reporter, sizeof(layerAlpha) == sizeof(flags));
 
@@ -81,7 +85,7 @@
                    float r, float b, int32_t s);
 
     OpenLibResult openLibResult(reporter);
-    if (openLibResult.handle() != NULL) {
+    if (openLibResult.handle() != nullptr) {
         *(void**) (&drawFn) = dlsym(openLibResult.handle(),
                                     "complex_layers_draw_from_canvas_state");
     } else {
@@ -105,8 +109,13 @@
             canvas.drawColor(SK_ColorRED);
 
             for (size_t k = 0; k < SK_ARRAY_COUNT(layerAlpha); ++k) {
+                SkTLazy<SkPaint> paint;
+                if (layerAlpha[k] != 0xFF) {
+                    paint.init()->setAlpha(layerAlpha[k]);
+                }
+
                 // draw a rect within the layer's bounds and again outside the layer's bounds
-                canvas.saveLayerAlpha(&rect, layerAlpha[k], flags[k]);
+                canvas.saveLayer(SkCanvas::SaveLayerRec(&rect, paint.getMaybeNull(), flags[k]));
 
                 if (j) {
                     // Capture from the first Skia.
@@ -172,9 +181,10 @@
                                      SkRegion::kIntersect_Op,
                                      SkRegion::kReplace_Op,
     };
-    const SkCanvas::SaveFlags flags[] = { SkCanvas::kARGB_NoClipLayer_SaveFlag,
-                                          SkCanvas::kARGB_ClipLayer_SaveFlag,
-                                          SkCanvas::kARGB_NoClipLayer_SaveFlag,
+    const SkCanvas::SaveLayerFlags flags[] = {
+        static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag),
+        0,
+        static_cast<SkCanvas::SaveLayerFlags>(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag),
     };
     REPORTER_ASSERT(reporter, sizeof(clipOps) == sizeof(flags));
 
@@ -183,7 +193,7 @@
                    int32_t regionRects, int32_t* rectCoords);
 
     OpenLibResult openLibResult(reporter);
-    if (openLibResult.handle() != NULL) {
+    if (openLibResult.handle() != nullptr) {
         *(void**) (&drawFn) = dlsym(openLibResult.handle(),
                                     "complex_clips_draw_from_canvas_state");
     } else {
@@ -205,9 +215,11 @@
 
         SkRegion localRegion = clipRegion;
 
+        SkPaint paint;
+        paint.setAlpha(128);
         for (size_t j = 0; j < SK_ARRAY_COUNT(flags); ++j) {
             SkRect layerBounds = SkRect::Make(layerRect);
-            canvas.saveLayerAlpha(&layerBounds, 128, flags[j]);
+            canvas.saveLayer(SkCanvas::SaveLayerRec(&layerBounds, &paint, flags[j]));
 
             if (i) {
                 SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
@@ -252,9 +264,11 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
+
 class TestDrawFilter : public SkDrawFilter {
 public:
-    virtual bool filter(SkPaint*, Type) SK_OVERRIDE { return true; }
+    bool filter(SkPaint*, Type) override { return true; }
 };
 
 DEF_TEST(CanvasState_test_draw_filters, reporter) {
@@ -267,20 +281,18 @@
 
     SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
     REPORTER_ASSERT(reporter, state);
-    SkCanvas* tmpCanvas = SkCanvasStateUtils::CreateFromCanvasState(state);
+    std::unique_ptr<SkCanvas> tmpCanvas = SkCanvasStateUtils::MakeFromCanvasState(state);
     REPORTER_ASSERT(reporter, tmpCanvas);
 
     REPORTER_ASSERT(reporter, canvas.getDrawFilter());
-    REPORTER_ASSERT(reporter, NULL == tmpCanvas->getDrawFilter());
+    REPORTER_ASSERT(reporter, nullptr == tmpCanvas->getDrawFilter());
 
-    tmpCanvas->unref();
     SkCanvasStateUtils::ReleaseCanvasState(state);
 }
 
-////////////////////////////////////////////////////////////////////////////////
+#endif
 
-// we need this function to prevent SkError from printing to stdout
-static void error_callback(SkError code, void* ctx) {}
+////////////////////////////////////////////////////////////////////////////////
 
 DEF_TEST(CanvasState_test_soft_clips, reporter) {
     SkBitmap bitmap;
@@ -290,19 +302,17 @@
     SkRRect roundRect;
     roundRect.setOval(SkRect::MakeWH(5, 5));
 
-    canvas.clipRRect(roundRect, SkRegion::kIntersect_Op, true);
-
-    SkSetErrorCallback(error_callback, NULL);
+    canvas.clipRRect(roundRect, kIntersect_SkClipOp, true);
 
     SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
     REPORTER_ASSERT(reporter, !state);
-
-    REPORTER_ASSERT(reporter, kInvalidOperation_SkError == SkGetLastError());
-    SkClearLastError();
 }
 
-#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
 DEF_TEST(CanvasState_test_saveLayer_clip, reporter) {
+    const uint32_t dontSaveFlag = 1 << 31;    // secret flag for don't save
+#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
+    static_assert(SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag == dontSaveFlag, "");
+#endif
     const int WIDTH = 100;
     const int HEIGHT = 100;
     const int LAYER_WIDTH = 50;
@@ -315,23 +325,21 @@
     SkRect bounds = SkRect::MakeWH(SkIntToScalar(LAYER_WIDTH), SkIntToScalar(LAYER_HEIGHT));
     canvas.clipRect(SkRect::MakeWH(SkIntToScalar(WIDTH), SkIntToScalar(HEIGHT)));
 
-    // Check that saveLayer without the kClipToLayer_SaveFlag leaves the
-    // clip stack unchanged.
-    canvas.saveLayer(&bounds, NULL, SkCanvas::kARGB_NoClipLayer_SaveFlag);
-    SkRect clipStackBounds;
-    SkClipStack::BoundsType boundsType;
-    canvas.getClipStack()->getBounds(&clipStackBounds, &boundsType);
-    REPORTER_ASSERT(reporter, clipStackBounds.width() == WIDTH);
-    REPORTER_ASSERT(reporter, clipStackBounds.height() == HEIGHT);
+    SkIRect devClip;
+    // Check that saveLayer without the kClipToLayer_SaveFlag leaves the clip unchanged.
+    canvas.saveLayer(SkCanvas::SaveLayerRec(&bounds, nullptr, dontSaveFlag));
+    devClip = canvas.getDeviceClipBounds();
+    REPORTER_ASSERT(reporter, canvas.isClipRect());
+    REPORTER_ASSERT(reporter, devClip.width() == WIDTH);
+    REPORTER_ASSERT(reporter, devClip.height() == HEIGHT);
     canvas.restore();
 
     // Check that saveLayer with the kClipToLayer_SaveFlag sets the clip
     // stack to the layer bounds.
-    canvas.saveLayer(&bounds, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
-    canvas.getClipStack()->getBounds(&clipStackBounds, &boundsType);
-    REPORTER_ASSERT(reporter, clipStackBounds.width() == LAYER_WIDTH);
-    REPORTER_ASSERT(reporter, clipStackBounds.height() == LAYER_HEIGHT);
-
+    canvas.saveLayer(&bounds, nullptr);
+    devClip = canvas.getDeviceClipBounds();
+    REPORTER_ASSERT(reporter, canvas.isClipRect());
+    REPORTER_ASSERT(reporter, devClip.width() == LAYER_WIDTH);
+    REPORTER_ASSERT(reporter, devClip.height() == LAYER_HEIGHT);
     canvas.restore();
 }
-#endif
diff --git a/src/third_party/skia/tests/CanvasTest.cpp b/src/third_party/skia/tests/CanvasTest.cpp
index 216a408..e64f5e9 100644
--- a/src/third_party/skia/tests/CanvasTest.cpp
+++ b/src/third_party/skia/tests/CanvasTest.cpp
@@ -18,6 +18,7 @@
  *      function of the form:
  *
  *          static void MyTestStepFunction(SkCanvas* canvas,
+ *                                         const TestData& d,
  *                                         skiatest::Reporter* reporter,
  *                                         CanvasTestStep* testStep)
  *          {
@@ -43,75 +44,238 @@
  *      works the same way as SIMPLE_TEST_STEP, and additionally verifies
  *      that the invoked method returns a non-zero value.
  */
+
 #include "SkBitmap.h"
 #include "SkCanvas.h"
-#include "SkDeferredCanvas.h"
-#include "SkDevice.h"
+#include "SkClipStack.h"
+#include "SkDocument.h"
 #include "SkMatrix.h"
 #include "SkNWayCanvas.h"
-#include "SkPDFDevice.h"
-#include "SkPDFDocument.h"
 #include "SkPaint.h"
+#include "SkPaintFilterCanvas.h"
 #include "SkPath.h"
 #include "SkPicture.h"
 #include "SkPictureRecord.h"
 #include "SkPictureRecorder.h"
-#include "SkProxyCanvas.h"
+#include "SkRasterClip.h"
 #include "SkRect.h"
 #include "SkRegion.h"
 #include "SkShader.h"
 #include "SkStream.h"
 #include "SkSurface.h"
+#include "SkTemplates.h"
 #include "SkTDArray.h"
+#include "SkVertices.h"
 #include "Test.h"
 
-static bool equal_clips(const SkCanvas& a, const SkCanvas& b) {
-    if (a.isClipEmpty()) {
-        return b.isClipEmpty();
+DEF_TEST(canvas_clipbounds, reporter) {
+    SkCanvas canvas(10, 10);
+    SkIRect irect, irect2;
+    SkRect rect, rect2;
+
+    irect = canvas.getDeviceClipBounds();
+    REPORTER_ASSERT(reporter, irect == SkIRect::MakeWH(10, 10));
+    REPORTER_ASSERT(reporter, canvas.getDeviceClipBounds(&irect2));
+    REPORTER_ASSERT(reporter, irect == irect2);
+
+    // local bounds are always too big today -- can we trim them?
+    rect = canvas.getLocalClipBounds();
+    REPORTER_ASSERT(reporter, rect.contains(SkRect::MakeWH(10, 10)));
+    REPORTER_ASSERT(reporter, canvas.getLocalClipBounds(&rect2));
+    REPORTER_ASSERT(reporter, rect == rect2);
+
+    canvas.clipRect(SkRect::MakeEmpty());
+
+    irect = canvas.getDeviceClipBounds();
+    REPORTER_ASSERT(reporter, irect == SkIRect::MakeEmpty());
+    REPORTER_ASSERT(reporter, !canvas.getDeviceClipBounds(&irect2));
+    REPORTER_ASSERT(reporter, irect == irect2);
+
+    rect = canvas.getLocalClipBounds();
+    REPORTER_ASSERT(reporter, rect == SkRect::MakeEmpty());
+    REPORTER_ASSERT(reporter, !canvas.getLocalClipBounds(&rect2));
+    REPORTER_ASSERT(reporter, rect == rect2);
+
+    // Test for wacky sizes that we (historically) have guarded against
+    {
+        SkCanvas c(-10, -20);
+        REPORTER_ASSERT(reporter, c.getBaseLayerSize() == SkISize::MakeEmpty());
+
+        SkPictureRecorder().beginRecording({ 5, 5, 4, 4 });
     }
-    if (!a.isClipRect()) {
-        // this is liberally true, since we don't expose a way to know this exactly (for non-rects)
-        return !b.isClipRect();
-    }
-    SkIRect ar, br;
-    a.getClipDeviceBounds(&ar);
-    b.getClipDeviceBounds(&br);
-    return ar == br;
 }
 
-class Canvas2CanvasClipVisitor : public SkCanvas::ClipVisitor {
-public:
-    Canvas2CanvasClipVisitor(SkCanvas* target) : fTarget(target) {}
+// Will call proc with multiple styles of canse (recording, raster, pdf)
+//
+template <typename F> static void multi_canvas_driver(int w, int h, F proc) {
+    proc(SkPictureRecorder().beginRecording(SkRect::MakeIWH(w, h)));
 
-    virtual void clipRect(const SkRect& r, SkRegion::Op op, bool aa) SK_OVERRIDE {
-        fTarget->clipRect(r, op, aa);
-    }
-    virtual void clipRRect(const SkRRect& r, SkRegion::Op op, bool aa) SK_OVERRIDE {
-        fTarget->clipRRect(r, op, aa);
-    }
-    virtual void clipPath(const SkPath& p, SkRegion::Op op, bool aa) SK_OVERRIDE {
-        fTarget->clipPath(p, op, aa);
-    }
+    SkNullWStream stream;
+    proc(SkDocument::MakePDF(&stream)->beginPage(SkIntToScalar(w), SkIntToScalar(h)));
 
-private:
-    SkCanvas* fTarget;
+    proc(SkSurface::MakeRasterN32Premul(w, h, nullptr)->getCanvas());
+}
+
+
+const SkIRect gBaseRestrictedR = { 0, 0, 10, 10 };
+
+static void test_restriction(skiatest::Reporter* reporter, SkCanvas* canvas) {
+    REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == gBaseRestrictedR);
+
+    const SkIRect restrictionR = { 2, 2, 8, 8 };
+    canvas->androidFramework_setDeviceClipRestriction(restrictionR);
+    REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == restrictionR);
+
+    const SkIRect clipR = { 4, 4, 6, 6 };
+    canvas->clipRect(SkRect::Make(clipR), SkClipOp::kIntersect);
+    REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == clipR);
+
+#ifdef SK_SUPPORT_DEPRECATED_CLIPOPS
+    // now test that expanding clipops can't exceed the restriction
+    const SkClipOp expanders[] = {
+        SkClipOp::kUnion_deprecated,
+        SkClipOp::kXOR_deprecated,
+        SkClipOp::kReverseDifference_deprecated,
+        SkClipOp::kReplace_deprecated,
+    };
+
+    const SkRect expandR = { 0, 0, 5, 9 };
+    SkASSERT(!SkRect::Make(restrictionR).contains(expandR));
+
+    for (SkClipOp op : expanders) {
+        canvas->save();
+        canvas->clipRect(expandR, op);
+        REPORTER_ASSERT(reporter, gBaseRestrictedR.contains(canvas->getDeviceClipBounds()));
+        canvas->restore();
+    }
+#endif
+}
+
+/**
+ *  Clip restriction logic exists in the canvas itself, and in various kinds of devices.
+ *
+ *  This test explicitly tries to exercise that variety:
+ *  - picture : empty device but exercises canvas itself
+ *  - pdf : uses SkClipStack in its device (as does SVG and GPU)
+ *  - raster : uses SkRasterClip in its device
+ */
+DEF_TEST(canvas_clip_restriction, reporter) {
+    multi_canvas_driver(gBaseRestrictedR.width(), gBaseRestrictedR.height(),
+                        [reporter](SkCanvas* canvas) { test_restriction(reporter, canvas); });
+}
+
+DEF_TEST(canvas_empty_clip, reporter) {
+    multi_canvas_driver(50, 50, [reporter](SkCanvas* canvas) {
+        canvas->save();
+        canvas->clipRect({0, 0, 20, 40 });
+        REPORTER_ASSERT(reporter, !canvas->isClipEmpty());
+        canvas->clipRect({30, 0, 50, 40 });
+        REPORTER_ASSERT(reporter, canvas->isClipEmpty());
+    });
+}
+
+static const int kWidth = 2, kHeight = 2;
+
+static void createBitmap(SkBitmap* bm, SkColor color) {
+    bm->allocN32Pixels(kWidth, kHeight);
+    bm->eraseColor(color);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Constants used by test steps
+const SkPoint kTestPoints[] = {
+    {SkIntToScalar(0), SkIntToScalar(0)},
+    {SkIntToScalar(2), SkIntToScalar(1)},
+    {SkIntToScalar(0), SkIntToScalar(2)}
+};
+const SkPoint kTestPoints2[] = {
+    { SkIntToScalar(0), SkIntToScalar(1) },
+    { SkIntToScalar(1), SkIntToScalar(1) },
+    { SkIntToScalar(2), SkIntToScalar(1) },
+    { SkIntToScalar(3), SkIntToScalar(1) },
+    { SkIntToScalar(4), SkIntToScalar(1) },
+    { SkIntToScalar(5), SkIntToScalar(1) },
+    { SkIntToScalar(6), SkIntToScalar(1) },
+    { SkIntToScalar(7), SkIntToScalar(1) },
+    { SkIntToScalar(8), SkIntToScalar(1) },
+    { SkIntToScalar(9), SkIntToScalar(1) },
+    { SkIntToScalar(10), SkIntToScalar(1) }
 };
 
-static void test_clipVisitor(skiatest::Reporter* reporter, SkCanvas* canvas) {
-    SkISize size = canvas->getDeviceSize();
+struct TestData {
+public:
+    TestData()
+    : fRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
+                                 SkIntToScalar(2), SkIntToScalar(1)))
+    , fMatrix(TestMatrix())
+    , fPath(TestPath())
+    , fNearlyZeroLengthPath(TestNearlyZeroLengthPath())
+    , fIRect(SkIRect::MakeXYWH(0, 0, 2, 1))
+    , fRegion(TestRegion())
+    , fColor(0x01020304)
+    , fPoints(kTestPoints)
+    , fPointCount(3)
+    , fWidth(2)
+    , fHeight(2)
+    , fText("Hello World")
+    , fPoints2(kTestPoints2)
+    , fBitmap(TestBitmap())
+    { }
 
-    SkBitmap bm;
-    bm.setInfo(SkImageInfo::MakeN32Premul(size.width(), size.height()));
-    SkCanvas c(bm);
+    SkRect fRect;
+    SkMatrix fMatrix;
+    SkPath fPath;
+    SkPath fNearlyZeroLengthPath;
+    SkIRect fIRect;
+    SkRegion fRegion;
+    SkColor fColor;
+    SkPaint fPaint;
+    const SkPoint* fPoints;
+    size_t fPointCount;
+    int fWidth;
+    int fHeight;
+    SkString fText;
+    const SkPoint* fPoints2;
+    SkBitmap fBitmap;
 
-    Canvas2CanvasClipVisitor visitor(&c);
-    canvas->replayClips(&visitor);
+private:
+    static SkMatrix TestMatrix() {
+        SkMatrix matrix;
+        matrix.reset();
+        matrix.setScale(SkIntToScalar(2), SkIntToScalar(3));
 
-    REPORTER_ASSERT(reporter, equal_clips(c, *canvas));
-}
-
-static const int kWidth = 2;
-static const int kHeight = 2;
+        return matrix;
+    }
+    static SkPath TestPath() {
+        SkPath path;
+        path.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
+                                      SkIntToScalar(2), SkIntToScalar(1)));
+        return path;
+    }
+    static SkPath TestNearlyZeroLengthPath() {
+        SkPath path;
+        SkPoint pt1 = { 0, 0 };
+        SkPoint pt2 = { 0, SK_ScalarNearlyZero };
+        SkPoint pt3 = { SkIntToScalar(1), 0 };
+        SkPoint pt4 = { SkIntToScalar(1), SK_ScalarNearlyZero/2 };
+        path.moveTo(pt1);
+        path.lineTo(pt2);
+        path.lineTo(pt3);
+        path.lineTo(pt4);
+        return path;
+    }
+    static SkRegion TestRegion() {
+        SkRegion region;
+        SkIRect rect = SkIRect::MakeXYWH(0, 0, 2, 1);
+        region.setRect(rect);
+        return region;
+    }
+    static SkBitmap TestBitmap() {
+        SkBitmap bitmap;
+        createBitmap(&bitmap, 0x05060708);
+        return bitmap;
+    }
+};
 
 // Format strings that describe the test context.  The %s token is where
 // the name of the test step is inserted.  The context is required for
@@ -121,48 +285,9 @@
 static const char* const kDefaultAssertMessageFormat = "%s";
 static const char* const kCanvasDrawAssertMessageFormat =
     "Drawing test step %s with SkCanvas";
-static const char* const kPictureDrawAssertMessageFormat =
-    "Drawing test step %s with SkPicture";
-static const char* const kPictureSecondDrawAssertMessageFormat =
-    "Duplicate draw of test step %s with SkPicture";
-static const char* const kDeferredDrawAssertMessageFormat =
-    "Drawing test step %s with SkDeferredCanvas";
-static const char* const kProxyDrawAssertMessageFormat =
-    "Drawing test step %s with SkProxyCanvas";
-static const char* const kNWayDrawAssertMessageFormat =
-    "Drawing test step %s with SkNWayCanvas";
-static const char* const kDeferredPreFlushAssertMessageFormat =
-    "test step %s, SkDeferredCanvas state consistency before flush";
-static const char* const kDeferredPostFlushPlaybackAssertMessageFormat =
-    "test step %s, SkDeferredCanvas playback canvas state consistency after flush";
-static const char* const kDeferredPostSilentFlushPlaybackAssertMessageFormat =
-    "test step %s, SkDeferredCanvas playback canvas state consistency after silent flush";
-static const char* const kPictureResourceReuseMessageFormat =
-    "test step %s, SkPicture duplicate flattened object test";
-static const char* const kProxyStateAssertMessageFormat =
-    "test step %s, SkProxyCanvas state consistency";
-static const char* const kProxyIndirectStateAssertMessageFormat =
-    "test step %s, SkProxyCanvas indirect canvas state consistency";
-static const char* const kNWayStateAssertMessageFormat =
-    "test step %s, SkNWayCanvas state consistency";
-static const char* const kNWayIndirect1StateAssertMessageFormat =
-    "test step %s, SkNWayCanvas indirect canvas 1 state consistency";
-static const char* const kNWayIndirect2StateAssertMessageFormat =
-    "test step %s, SkNWayCanvas indirect canvas 2 state consistency";
 static const char* const kPdfAssertMessageFormat =
     "PDF sanity check failed %s";
 
-static void createBitmap(SkBitmap* bm, SkColor color) {
-    bm->allocN32Pixels(kWidth, kHeight);
-    bm->eraseColor(color);
-}
-
-static SkSurface* createSurface(SkColor color) {
-    SkSurface* surface = SkSurface::NewRasterPMColor(kWidth, kHeight);
-    surface->getCanvas()->clear(color);
-    return surface;
-}
-
 class CanvasTestStep;
 static SkTDArray<CanvasTestStep*>& testStepArray() {
     static SkTDArray<CanvasTestStep*> theTests;
@@ -178,7 +303,7 @@
     }
     virtual ~CanvasTestStep() { }
 
-    virtual void draw(SkCanvas*, skiatest::Reporter*) = 0;
+    virtual void draw(SkCanvas*, const TestData&, skiatest::Reporter*) = 0;
     virtual const char* name() const = 0;
 
     const char* assertMessage() {
@@ -199,110 +324,41 @@
 };
 
 ///////////////////////////////////////////////////////////////////////////////
-// Constants used by test steps
-
-const SkRect kTestRect =
-    SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
-                     SkIntToScalar(2), SkIntToScalar(1));
-static SkMatrix testMatrix() {
-    SkMatrix matrix;
-    matrix.reset();
-    matrix.setScale(SkIntToScalar(2), SkIntToScalar(3));
-    return matrix;
-}
-const SkMatrix kTestMatrix = testMatrix();
-static SkPath test_path() {
-    SkPath path;
-    path.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
-                                  SkIntToScalar(2), SkIntToScalar(1)));
-    return path;
-}
-const SkPath kTestPath = test_path();
-static SkPath test_nearly_zero_length_path() {
-    SkPath path;
-    SkPoint pt1 = { 0, 0 };
-    SkPoint pt2 = { 0, SK_ScalarNearlyZero };
-    SkPoint pt3 = { SkIntToScalar(1), 0 };
-    SkPoint pt4 = { SkIntToScalar(1), SK_ScalarNearlyZero/2 };
-    path.moveTo(pt1);
-    path.lineTo(pt2);
-    path.lineTo(pt3);
-    path.lineTo(pt4);
-    return path;
-}
-const SkPath kNearlyZeroLengthPath = test_nearly_zero_length_path();
-static SkRegion testRegion() {
-    SkRegion region;
-    SkIRect rect = SkIRect::MakeXYWH(0, 0, 2, 1);
-    region.setRect(rect);
-    return region;
-}
-const SkIRect kTestIRect = SkIRect::MakeXYWH(0, 0, 2, 1);
-const SkRegion kTestRegion = testRegion();
-const SkColor kTestColor = 0x01020304;
-const SkPaint kTestPaint;
-const SkPoint kTestPoints[3] = {
-    {SkIntToScalar(0), SkIntToScalar(0)},
-    {SkIntToScalar(2), SkIntToScalar(1)},
-    {SkIntToScalar(0), SkIntToScalar(2)}
-};
-const size_t kTestPointCount = 3;
-static SkBitmap testBitmap() {
-    SkBitmap bitmap;
-    createBitmap(&bitmap, 0x05060708);
-    return bitmap;
-}
-SkBitmap kTestBitmap; // cannot be created during static init
-SkString kTestText("Hello World");
-SkPoint kTestPoints2[] = {
-  { SkIntToScalar(0), SkIntToScalar(1) },
-  { SkIntToScalar(1), SkIntToScalar(1) },
-  { SkIntToScalar(2), SkIntToScalar(1) },
-  { SkIntToScalar(3), SkIntToScalar(1) },
-  { SkIntToScalar(4), SkIntToScalar(1) },
-  { SkIntToScalar(5), SkIntToScalar(1) },
-  { SkIntToScalar(6), SkIntToScalar(1) },
-  { SkIntToScalar(7), SkIntToScalar(1) },
-  { SkIntToScalar(8), SkIntToScalar(1) },
-  { SkIntToScalar(9), SkIntToScalar(1) },
-  { SkIntToScalar(10), SkIntToScalar(1) },
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
 // Macros for defining test steps
 
 #define TEST_STEP(NAME, FUNCTION)                                       \
 class NAME##_TestStep : public CanvasTestStep{                          \
 public:                                                                 \
-    virtual void draw(SkCanvas* canvas, skiatest::Reporter* reporter) { \
-        FUNCTION (canvas, reporter, this);                              \
+    virtual void draw(SkCanvas* canvas, const TestData& d,       \
+        skiatest::Reporter* reporter) {                                 \
+        FUNCTION (canvas, d, reporter, this);                    \
     }                                                                   \
     virtual const char* name() const {return #NAME ;}                   \
 };                                                                      \
 static NAME##_TestStep NAME##_TestStepInstance;
 
-#define TEST_STEP_NO_PDF(NAME, FUNCTION)                                       \
+#define TEST_STEP_NO_PDF(NAME, FUNCTION)                                \
 class NAME##_TestStep : public CanvasTestStep{                          \
 public:                                                                 \
     NAME##_TestStep() : CanvasTestStep(false) {}                        \
-    virtual void draw(SkCanvas* canvas, skiatest::Reporter* reporter) { \
-        FUNCTION (canvas, reporter, this);                              \
+    virtual void draw(SkCanvas* canvas, const TestData& d,       \
+        skiatest::Reporter* reporter) {                                 \
+        FUNCTION (canvas, d, reporter, this);                    \
     }                                                                   \
     virtual const char* name() const {return #NAME ;}                   \
 };                                                                      \
 static NAME##_TestStep NAME##_TestStepInstance;
 
-#define SIMPLE_TEST_STEP(NAME, CALL)                              \
-static void NAME##TestStep(SkCanvas* canvas, skiatest::Reporter*, \
-    CanvasTestStep*) {                                            \
-    canvas-> CALL ;                                               \
-}                                                                 \
+#define SIMPLE_TEST_STEP(NAME, CALL)                                    \
+static void NAME##TestStep(SkCanvas* canvas, const TestData& d,  \
+    skiatest::Reporter*, CanvasTestStep*) {                             \
+    canvas-> CALL ;                                                     \
+}                                                                       \
 TEST_STEP(NAME, NAME##TestStep )
 
 #define SIMPLE_TEST_STEP_WITH_ASSERT(NAME, CALL)                           \
-static void NAME##TestStep(SkCanvas* canvas, skiatest::Reporter* reporter, \
-    CanvasTestStep* testStep) {                                            \
+static void NAME##TestStep(SkCanvas* canvas, const TestData& d,     \
+    skiatest::Reporter*, CanvasTestStep* testStep) {                       \
     REPORTER_ASSERT_MESSAGE(reporter, canvas-> CALL ,                      \
         testStep->assertMessage());                                        \
 }                                                                          \
@@ -317,63 +373,22 @@
 SIMPLE_TEST_STEP(Scale, scale(SkIntToScalar(1), SkIntToScalar(2)));
 SIMPLE_TEST_STEP(Rotate, rotate(SkIntToScalar(1)));
 SIMPLE_TEST_STEP(Skew, skew(SkIntToScalar(1), SkIntToScalar(2)));
-SIMPLE_TEST_STEP(Concat, concat(kTestMatrix));
-SIMPLE_TEST_STEP(SetMatrix, setMatrix(kTestMatrix));
-SIMPLE_TEST_STEP(ClipRect, clipRect(kTestRect));
-SIMPLE_TEST_STEP(ClipPath, clipPath(kTestPath));
-SIMPLE_TEST_STEP(ClipRegion,
-    clipRegion(kTestRegion, SkRegion::kReplace_Op));
-SIMPLE_TEST_STEP(Clear, clear(kTestColor));
-SIMPLE_TEST_STEP(DrawPaint, drawPaint(kTestPaint));
-SIMPLE_TEST_STEP(DrawPointsPoints, drawPoints(SkCanvas::kPoints_PointMode,
-    kTestPointCount, kTestPoints, kTestPaint));
-SIMPLE_TEST_STEP(DrawPointsLiness, drawPoints(SkCanvas::kLines_PointMode,
-    kTestPointCount, kTestPoints, kTestPaint));
-SIMPLE_TEST_STEP(DrawPointsPolygon, drawPoints(SkCanvas::kPolygon_PointMode,
-    kTestPointCount, kTestPoints, kTestPaint));
-SIMPLE_TEST_STEP(DrawRect, drawRect(kTestRect, kTestPaint));
-SIMPLE_TEST_STEP(DrawPath, drawPath(kTestPath, kTestPaint));
-SIMPLE_TEST_STEP(DrawBitmap, drawBitmap(kTestBitmap, 0, 0));
-SIMPLE_TEST_STEP(DrawBitmapPaint, drawBitmap(kTestBitmap, 0, 0, &kTestPaint));
-SIMPLE_TEST_STEP(DrawBitmapRect, drawBitmapRect(kTestBitmap, NULL, kTestRect,
-    NULL));
-SIMPLE_TEST_STEP(DrawBitmapRectSrcRect, drawBitmapRect(kTestBitmap,
-    &kTestIRect, kTestRect, NULL));
-SIMPLE_TEST_STEP(DrawBitmapRectPaint, drawBitmapRect(kTestBitmap, NULL,
-    kTestRect, &kTestPaint));
-SIMPLE_TEST_STEP(DrawBitmapMatrix, drawBitmapMatrix(kTestBitmap, kTestMatrix,
-    NULL));
-SIMPLE_TEST_STEP(DrawBitmapMatrixPaint, drawBitmapMatrix(kTestBitmap,
-    kTestMatrix, &kTestPaint));
-SIMPLE_TEST_STEP(DrawBitmapNine, drawBitmapNine(kTestBitmap, kTestIRect,
-    kTestRect, NULL));
-SIMPLE_TEST_STEP(DrawBitmapNinePaint, drawBitmapNine(kTestBitmap, kTestIRect,
-    kTestRect, &kTestPaint));
-SIMPLE_TEST_STEP(DrawSprite, drawSprite(kTestBitmap, 0, 0, NULL));
-SIMPLE_TEST_STEP(DrawSpritePaint, drawSprite(kTestBitmap, 0, 0, &kTestPaint));
-SIMPLE_TEST_STEP(DrawText, drawText(kTestText.c_str(), kTestText.size(),
-    0, 1, kTestPaint));
-SIMPLE_TEST_STEP(DrawPosText, drawPosText(kTestText.c_str(),
-    kTestText.size(), kTestPoints2, kTestPaint));
-SIMPLE_TEST_STEP(DrawTextOnPath, drawTextOnPath(kTestText.c_str(),
-    kTestText.size(), kTestPath, NULL, kTestPaint));
-SIMPLE_TEST_STEP(DrawTextOnPathMatrix, drawTextOnPath(kTestText.c_str(),
-    kTestText.size(), kTestPath, &kTestMatrix, kTestPaint));
-SIMPLE_TEST_STEP(DrawData, drawData(kTestText.c_str(), kTestText.size()));
-SIMPLE_TEST_STEP(BeginGroup, beginCommentGroup(kTestText.c_str()));
-SIMPLE_TEST_STEP(AddComment, addComment(kTestText.c_str(), kTestText.c_str()));
-SIMPLE_TEST_STEP(EndGroup, endCommentGroup());
+SIMPLE_TEST_STEP(Concat, concat(d.fMatrix));
+SIMPLE_TEST_STEP(SetMatrix, setMatrix(d.fMatrix));
+SIMPLE_TEST_STEP(ClipRect, clipRect(d.fRect));
+SIMPLE_TEST_STEP(ClipPath, clipPath(d.fPath));
+SIMPLE_TEST_STEP(ClipRegion, clipRegion(d.fRegion, kReplace_SkClipOp));
+SIMPLE_TEST_STEP(Clear, clear(d.fColor));
 
 ///////////////////////////////////////////////////////////////////////////////
 // Complex test steps
 
-static void SaveMatrixClipStep(SkCanvas* canvas,
-                               skiatest::Reporter* reporter,
-                               CanvasTestStep* testStep) {
+static void SaveMatrixClipStep(SkCanvas* canvas, const TestData& d,
+                               skiatest::Reporter* reporter, CanvasTestStep* testStep) {
     int saveCount = canvas->getSaveCount();
     canvas->save();
     canvas->translate(SkIntToScalar(1), SkIntToScalar(2));
-    canvas->clipRegion(kTestRegion);
+    canvas->clipRegion(d.fRegion);
     canvas->restore();
     REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
         testStep->assertMessage());
@@ -383,100 +398,91 @@
 }
 TEST_STEP(SaveMatrixClip, SaveMatrixClipStep);
 
-static void SaveLayerStep(SkCanvas* canvas,
-                          skiatest::Reporter* reporter,
-                          CanvasTestStep* testStep) {
+static void SaveLayerStep(SkCanvas* canvas, const TestData& d,
+                          skiatest::Reporter* reporter, CanvasTestStep* testStep) {
     int saveCount = canvas->getSaveCount();
-    canvas->saveLayer(NULL, NULL);
+    canvas->saveLayer(nullptr, nullptr);
     canvas->restore();
     REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
         testStep->assertMessage());
 }
 TEST_STEP(SaveLayer, SaveLayerStep);
 
-static void BoundedSaveLayerStep(SkCanvas* canvas,
-                          skiatest::Reporter* reporter,
-                          CanvasTestStep* testStep) {
+static void BoundedSaveLayerStep(SkCanvas* canvas, const TestData& d,
+                                 skiatest::Reporter* reporter, CanvasTestStep* testStep) {
     int saveCount = canvas->getSaveCount();
-    canvas->saveLayer(&kTestRect, NULL);
+    canvas->saveLayer(&d.fRect, nullptr);
     canvas->restore();
     REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
         testStep->assertMessage());
 }
 TEST_STEP(BoundedSaveLayer, BoundedSaveLayerStep);
 
-static void PaintSaveLayerStep(SkCanvas* canvas,
-                          skiatest::Reporter* reporter,
-                          CanvasTestStep* testStep) {
+static void PaintSaveLayerStep(SkCanvas* canvas, const TestData& d,
+                               skiatest::Reporter* reporter, CanvasTestStep* testStep) {
     int saveCount = canvas->getSaveCount();
-    canvas->saveLayer(NULL, &kTestPaint);
+    canvas->saveLayer(nullptr, &d.fPaint);
     canvas->restore();
     REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
         testStep->assertMessage());
 }
 TEST_STEP(PaintSaveLayer, PaintSaveLayerStep);
 
-static void TwoClipOpsStep(SkCanvas* canvas,
-                           skiatest::Reporter*,
-                           CanvasTestStep*) {
+static void TwoClipOpsStep(SkCanvas* canvas, const TestData& d,
+                           skiatest::Reporter*, CanvasTestStep*) {
     // This test exercises a functionality in SkPicture that leads to the
     // recording of restore offset placeholders.  This test will trigger an
     // assertion at playback time if the placeholders are not properly
     // filled when the recording ends.
-    canvas->clipRect(kTestRect);
-    canvas->clipRegion(kTestRegion);
+    canvas->clipRect(d.fRect);
+    canvas->clipRegion(d.fRegion);
 }
 TEST_STEP(TwoClipOps, TwoClipOpsStep);
 
 // exercise fix for http://code.google.com/p/skia/issues/detail?id=560
 // ('SkPathStroker::lineTo() fails for line with length SK_ScalarNearlyZero')
-static void DrawNearlyZeroLengthPathTestStep(SkCanvas* canvas,
-                                             skiatest::Reporter*,
-                                             CanvasTestStep*) {
+static void DrawNearlyZeroLengthPathTestStep(SkCanvas* canvas, const TestData& d,
+                                             skiatest::Reporter*, CanvasTestStep*) {
     SkPaint paint;
     paint.setStrokeWidth(SkIntToScalar(1));
     paint.setStyle(SkPaint::kStroke_Style);
 
-    canvas->drawPath(kNearlyZeroLengthPath, paint);
+    canvas->drawPath(d.fNearlyZeroLengthPath, paint);
 }
 TEST_STEP(DrawNearlyZeroLengthPath, DrawNearlyZeroLengthPathTestStep);
 
-static void DrawVerticesShaderTestStep(SkCanvas* canvas,
-                                       skiatest::Reporter*,
-                                       CanvasTestStep*) {
+static void DrawVerticesShaderTestStep(SkCanvas* canvas, const TestData& d,
+                                       skiatest::Reporter*, CanvasTestStep*) {
     SkPoint pts[4];
     pts[0].set(0, 0);
-    pts[1].set(SkIntToScalar(kWidth), 0);
-    pts[2].set(SkIntToScalar(kWidth), SkIntToScalar(kHeight));
-    pts[3].set(0, SkIntToScalar(kHeight));
+    pts[1].set(SkIntToScalar(d.fWidth), 0);
+    pts[2].set(SkIntToScalar(d.fWidth), SkIntToScalar(d.fHeight));
+    pts[3].set(0, SkIntToScalar(d.fHeight));
     SkPaint paint;
-    SkShader* shader = SkShader::CreateBitmapShader(kTestBitmap,
-        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
-    paint.setShader(shader)->unref();
-    canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode, 4, pts, pts,
-                         NULL, NULL, NULL, 0, paint);
+    paint.setShader(SkShader::MakeBitmapShader(d.fBitmap, SkShader::kClamp_TileMode,
+                                               SkShader::kClamp_TileMode));
+    canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4, pts, pts,
+                                              nullptr),
+                         SkBlendMode::kModulate, paint);
 }
 // NYI: issue 240.
 TEST_STEP_NO_PDF(DrawVerticesShader, DrawVerticesShaderTestStep);
 
-static void DrawPictureTestStep(SkCanvas* canvas,
-                                skiatest::Reporter*,
-                                CanvasTestStep*) {
+static void DrawPictureTestStep(SkCanvas* canvas, const TestData& d,
+                                skiatest::Reporter*, CanvasTestStep*) {
     SkPictureRecorder recorder;
-    SkCanvas* testCanvas = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight), 
-                                                   NULL, 0);
+    SkCanvas* testCanvas = recorder.beginRecording(SkIntToScalar(d.fWidth), SkIntToScalar(d.fHeight),
+                                                   nullptr, 0);
     testCanvas->scale(SkIntToScalar(2), SkIntToScalar(1));
-    testCanvas->clipRect(kTestRect);
-    testCanvas->drawRect(kTestRect, kTestPaint);
-    SkAutoTUnref<SkPicture> testPicture(recorder.endRecording());
+    testCanvas->clipRect(d.fRect);
+    testCanvas->drawRect(d.fRect, d.fPaint);
 
-    canvas->drawPicture(testPicture);
+    canvas->drawPicture(recorder.finishRecordingAsPicture());
 }
 TEST_STEP(DrawPicture, DrawPictureTestStep);
 
-static void SaveRestoreTestStep(SkCanvas* canvas,
-                                skiatest::Reporter* reporter,
-                                CanvasTestStep* testStep) {
+static void SaveRestoreTestStep(SkCanvas* canvas, const TestData& d,
+                                skiatest::Reporter* reporter, CanvasTestStep* testStep) {
     int baseSaveCount = canvas->getSaveCount();
     int n = canvas->save();
     REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount == n, testStep->assertMessage());
@@ -497,43 +503,8 @@
 }
 TEST_STEP(SaveRestore, SaveRestoreTestStep);
 
-static void DrawLayerTestStep(SkCanvas* canvas,
-                              skiatest::Reporter* reporter,
-                              CanvasTestStep* testStep) {
-    REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(),
-        testStep->assertMessage());
-    canvas->save();
-    REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(),
-        testStep->assertMessage());
-    canvas->restore();
-
-    const SkRect* bounds = NULL;    // null means include entire bounds
-    const SkPaint* paint = NULL;
-
-    canvas->saveLayer(bounds, paint);
-    REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(),
-        testStep->assertMessage());
-    canvas->restore();
-    REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(),
-        testStep->assertMessage());
-
-    canvas->saveLayer(bounds, paint);
-    canvas->saveLayer(bounds, paint);
-    REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(),
-        testStep->assertMessage());
-    canvas->restore();
-    REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(),
-        testStep->assertMessage());
-    canvas->restore();
-    // now layer count should be 0
-    REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(),
-        testStep->assertMessage());
-}
-TEST_STEP(DrawLayer, DrawLayerTestStep);
-
-static void NestedSaveRestoreWithSolidPaintTestStep(SkCanvas* canvas,
-                                      skiatest::Reporter*,
-                                      CanvasTestStep*) {
+static void NestedSaveRestoreWithSolidPaintTestStep(SkCanvas* canvas, const TestData& d,
+                                                    skiatest::Reporter*, CanvasTestStep*) {
     // This test step challenges the TestDeferredCanvasStateConsistency
     // test cases because the opaque paint can trigger an optimization
     // that discards previously recorded commands. The challenge is to maintain
@@ -553,9 +524,8 @@
 TEST_STEP(NestedSaveRestoreWithSolidPaint, \
     NestedSaveRestoreWithSolidPaintTestStep);
 
-static void NestedSaveRestoreWithFlushTestStep(SkCanvas* canvas,
-                                      skiatest::Reporter*,
-                                      CanvasTestStep*) {
+static void NestedSaveRestoreWithFlushTestStep(SkCanvas* canvas, const TestData& d,
+                                               skiatest::Reporter*, CanvasTestStep*) {
     // This test step challenges the TestDeferredCanvasStateConsistency
     // test case because the canvas flush on a deferred canvas will
     // reset the recording session. The challenge is to maintain correct
@@ -566,262 +536,25 @@
     canvas->translate(SkIntToScalar(2), SkIntToScalar(1));
     canvas->save();
     canvas->scale(SkIntToScalar(3), SkIntToScalar(3));
-    canvas->drawRect(kTestRect,kTestPaint);
+    canvas->drawRect(d.fRect,d.fPaint);
     canvas->flush();
     canvas->restore();
     canvas->restore();
 }
-TEST_STEP(NestedSaveRestoreWithFlush, \
-    NestedSaveRestoreWithFlushTestStep);
+TEST_STEP(NestedSaveRestoreWithFlush, NestedSaveRestoreWithFlushTestStep);
 
-static void AssertCanvasStatesEqual(skiatest::Reporter* reporter,
-                                    const SkCanvas* canvas1,
-                                    const SkCanvas* canvas2,
-                                    CanvasTestStep* testStep) {
-    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getDeviceSize() ==
-        canvas2->getDeviceSize(), testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getSaveCount() ==
-        canvas2->getSaveCount(), testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter, canvas1->isDrawingToLayer() ==
-        canvas2->isDrawingToLayer(), testStep->assertMessage());
-
-    SkRect bounds1, bounds2;
-    REPORTER_ASSERT_MESSAGE(reporter,
-        canvas1->getClipBounds(&bounds1) == canvas2->getClipBounds(&bounds2),
-        testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter, bounds1 == bounds2,
-                            testStep->assertMessage());
-
-    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getDrawFilter() ==
-        canvas2->getDrawFilter(), testStep->assertMessage());
-    SkIRect deviceBounds1, deviceBounds2;
-    REPORTER_ASSERT_MESSAGE(reporter,
-        canvas1->getClipDeviceBounds(&deviceBounds1) ==
-        canvas2->getClipDeviceBounds(&deviceBounds2),
-        testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter, deviceBounds1 == deviceBounds2, testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getTotalMatrix() ==
-        canvas2->getTotalMatrix(), testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter, equal_clips(*canvas1, *canvas2), testStep->assertMessage());
-
-    // The following test code is commented out because the test fails when
-    // the canvas is an SkPictureRecord or SkDeferredCanvas
-    // Issue: http://code.google.com/p/skia/issues/detail?id=498
-    // Also, creating a LayerIter on an SkProxyCanvas crashes
-    // Issue: http://code.google.com/p/skia/issues/detail?id=499
-    /*
-    SkCanvas::LayerIter layerIter1(const_cast<SkCanvas*>(canvas1), false);
-    SkCanvas::LayerIter layerIter2(const_cast<SkCanvas*>(canvas2), false);
-    while (!layerIter1.done() && !layerIter2.done()) {
-        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.matrix() ==
-            layerIter2.matrix(), testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.clip() ==
-            layerIter2.clip(), testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.paint() ==
-            layerIter2.paint(), testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.x() ==
-            layerIter2.x(), testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.y() ==
-            layerIter2.y(), testStep->assertMessage());
-        layerIter1.next();
-        layerIter2.next();
+static void TestPdfDevice(skiatest::Reporter* reporter, const TestData& d, CanvasTestStep* step) {
+    SkDynamicMemoryWStream outStream;
+    sk_sp<SkDocument> doc(SkDocument::MakePDF(&outStream));
+    REPORTER_ASSERT(reporter, doc);
+    if (!doc) {
+        return;
     }
-    REPORTER_ASSERT_MESSAGE(reporter, layerIter1.done(),
-        testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter, layerIter2.done(),
-        testStep->assertMessage());
-    */
-}
-
-// The following class groups static functions that need to access
-// the privates members of SkPictureRecord
-class SkPictureTester {
-private:
-    static int EQ(const SkFlatData* a, const SkFlatData* b) {
-        return *a == *b;
-    }
-
-    static void AssertFlattenedObjectsEqual(
-        SkPictureRecord* referenceRecord,
-        SkPictureRecord* testRecord,
-        skiatest::Reporter* reporter,
-        CanvasTestStep* testStep) {
-
-        REPORTER_ASSERT_MESSAGE(reporter,
-            referenceRecord->fBitmapHeap->count() ==
-            testRecord->fBitmapHeap->count(), testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter,
-            referenceRecord->fPaints.count() ==
-            testRecord->fPaints.count(), testStep->assertMessage());
-        for (int i = 0; i < referenceRecord->fPaints.count(); ++i) {
-            REPORTER_ASSERT_MESSAGE(reporter,
-                EQ(referenceRecord->fPaints[i], testRecord->fPaints[i]),
-                                    testStep->assertMessage());
-        }
-        REPORTER_ASSERT_MESSAGE(reporter,
-            !referenceRecord->fPathHeap == !testRecord->fPathHeap,
-            testStep->assertMessage());
-        // The following tests are commented out because they currently
-        // fail. Issue: http://code.google.com/p/skia/issues/detail?id=507
-        /*
-        if (referenceRecord->fPathHeap) {
-            REPORTER_ASSERT_MESSAGE(reporter,
-                referenceRecord->fPathHeap->count() ==
-                testRecord->fPathHeap->count(),
-                testStep->assertMessage());
-            for (int i = 0; i < referenceRecord->fPathHeap->count(); ++i) {
-                REPORTER_ASSERT_MESSAGE(reporter,
-                    (*referenceRecord->fPathHeap)[i] ==
-                    (*testRecord->fPathHeap)[i], testStep->assertMessage());
-            }
-        }
-        */
-
-    }
-
-public:
-
-    static void TestPictureFlattenedObjectReuse(skiatest::Reporter* reporter,
-                                                CanvasTestStep* testStep,
-                                                uint32_t recordFlags) {
-        // Verify that when a test step is executed twice, no extra resources
-        // are flattened during the second execution
-        testStep->setAssertMessageFormat(kPictureDrawAssertMessageFormat);
-        SkPictureRecorder referenceRecorder;
-        SkCanvas* referenceCanvas =
-            referenceRecorder.DEPRECATED_beginRecording(SkIntToScalar(kWidth), 
-                                                        SkIntToScalar(kHeight), 
-                                                        NULL, recordFlags);
-        testStep->draw(referenceCanvas, reporter);
-
-        SkPictureRecorder testRecorder;
-        SkCanvas* testCanvas =
-            testRecorder.DEPRECATED_beginRecording(SkIntToScalar(kWidth), 
-                                                   SkIntToScalar(kHeight), 
-                                                   NULL, recordFlags);
-        testStep->draw(testCanvas, reporter);
-        testStep->setAssertMessageFormat(kPictureSecondDrawAssertMessageFormat);
-        testStep->draw(testCanvas, reporter);
-
-        SkPictureRecord* referenceRecord = static_cast<SkPictureRecord*>(referenceCanvas);
-        SkPictureRecord* testRecord = static_cast<SkPictureRecord*>(testCanvas);
-        testStep->setAssertMessageFormat(kPictureResourceReuseMessageFormat);
-        AssertFlattenedObjectsEqual(referenceRecord, testRecord,
-                                    reporter, testStep);
-    }
-};
-
-static void TestPdfDevice(skiatest::Reporter* reporter,
-                          CanvasTestStep* testStep) {
-    SkISize pageSize = SkISize::Make(kWidth, kHeight);
-    SkPDFDevice device(pageSize, pageSize, SkMatrix::I());
-    SkCanvas canvas(&device);
-    testStep->setAssertMessageFormat(kPdfAssertMessageFormat);
-    testStep->draw(&canvas, reporter);
-    SkPDFDocument doc;
-    doc.appendPage(&device);
-    SkDynamicMemoryWStream stream;
-    doc.emitPDF(&stream);
-}
-
-// The following class groups static functions that need to access
-// the privates members of SkDeferredCanvas
-class SkDeferredCanvasTester {
-public:
-    static void TestDeferredCanvasStateConsistency(
-        skiatest::Reporter* reporter,
-        CanvasTestStep* testStep,
-        const SkCanvas& referenceCanvas, bool silent) {
-
-        SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF));
-        SkAutoTUnref<SkDeferredCanvas> deferredCanvas(SkDeferredCanvas::Create(surface.get()));
-
-        testStep->setAssertMessageFormat(kDeferredDrawAssertMessageFormat);
-        testStep->draw(deferredCanvas, reporter);
-        testStep->setAssertMessageFormat(kDeferredPreFlushAssertMessageFormat);
-        AssertCanvasStatesEqual(reporter, deferredCanvas, &referenceCanvas,
-            testStep);
-
-        if (silent) {
-            deferredCanvas->silentFlush();
-        } else {
-            deferredCanvas->flush();
-        }
-
-        testStep->setAssertMessageFormat(
-            silent ? kDeferredPostSilentFlushPlaybackAssertMessageFormat :
-            kDeferredPostFlushPlaybackAssertMessageFormat);
-        AssertCanvasStatesEqual(reporter,
-            deferredCanvas->immediateCanvas(),
-            &referenceCanvas, testStep);
-
-        // Verified that deferred canvas state is not affected by flushing
-        // pending draw operations
-
-        // The following test code is commented out because it currently fails.
-        // Issue: http://code.google.com/p/skia/issues/detail?id=496
-        /*
-        testStep->setAssertMessageFormat(kDeferredPostFlushAssertMessageFormat);
-        AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas,
-            testStep);
-        */
-    }
-};
-
-// unused
-static void TestProxyCanvasStateConsistency(
-    skiatest::Reporter* reporter,
-    CanvasTestStep* testStep,
-    const SkCanvas& referenceCanvas) {
-
-    SkBitmap indirectStore;
-    createBitmap(&indirectStore, 0xFFFFFFFF);
-    SkCanvas indirectCanvas(indirectStore);
-    SkProxyCanvas proxyCanvas(&indirectCanvas);
-    testStep->setAssertMessageFormat(kProxyDrawAssertMessageFormat);
-    testStep->draw(&proxyCanvas, reporter);
-    // Verify that the SkProxyCanvas reports consitent state
-    testStep->setAssertMessageFormat(kProxyStateAssertMessageFormat);
-    AssertCanvasStatesEqual(reporter, &proxyCanvas, &referenceCanvas,
-        testStep);
-    // Verify that the indirect canvas reports consitent state
-    testStep->setAssertMessageFormat(kProxyIndirectStateAssertMessageFormat);
-    AssertCanvasStatesEqual(reporter, &indirectCanvas, &referenceCanvas,
-        testStep);
-}
-
-// unused
-static void TestNWayCanvasStateConsistency(
-    skiatest::Reporter* reporter,
-    CanvasTestStep* testStep,
-    const SkCanvas& referenceCanvas) {
-
-    SkBitmap indirectStore1;
-    createBitmap(&indirectStore1, 0xFFFFFFFF);
-    SkCanvas indirectCanvas1(indirectStore1);
-
-    SkBitmap indirectStore2;
-    createBitmap(&indirectStore2, 0xFFFFFFFF);
-    SkCanvas indirectCanvas2(indirectStore2);
-
-    SkISize canvasSize = referenceCanvas.getDeviceSize();
-    SkNWayCanvas nWayCanvas(canvasSize.width(), canvasSize.height());
-    nWayCanvas.addCanvas(&indirectCanvas1);
-    nWayCanvas.addCanvas(&indirectCanvas2);
-
-    testStep->setAssertMessageFormat(kNWayDrawAssertMessageFormat);
-    testStep->draw(&nWayCanvas, reporter);
-    // Verify that the SkProxyCanvas reports consitent state
-    testStep->setAssertMessageFormat(kNWayStateAssertMessageFormat);
-    AssertCanvasStatesEqual(reporter, &nWayCanvas, &referenceCanvas,
-        testStep);
-    // Verify that the indirect canvases report consitent state
-    testStep->setAssertMessageFormat(kNWayIndirect1StateAssertMessageFormat);
-    AssertCanvasStatesEqual(reporter, &indirectCanvas1, &referenceCanvas,
-        testStep);
-    testStep->setAssertMessageFormat(kNWayIndirect2StateAssertMessageFormat);
-    AssertCanvasStatesEqual(reporter, &indirectCanvas2, &referenceCanvas,
-        testStep);
+    SkCanvas* canvas = doc->beginPage(SkIntToScalar(d.fWidth),
+                                      SkIntToScalar(d.fHeight));
+    REPORTER_ASSERT(reporter, canvas);
+    step->setAssertMessageFormat(kPdfAssertMessageFormat);
+    step->draw(canvas, d, reporter);
 }
 
 /*
@@ -830,93 +563,254 @@
  * that the all canvas derivatives report the same state as an SkCanvas
  * after having executed the test step.
  */
-static void TestOverrideStateConsistency(skiatest::Reporter* reporter,
+static void TestOverrideStateConsistency(skiatest::Reporter* reporter, const TestData& d,
                                          CanvasTestStep* testStep) {
     SkBitmap referenceStore;
     createBitmap(&referenceStore, 0xFFFFFFFF);
     SkCanvas referenceCanvas(referenceStore);
     testStep->setAssertMessageFormat(kCanvasDrawAssertMessageFormat);
-    testStep->draw(&referenceCanvas, reporter);
-
-    SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas, false);
-
-    SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas, true);
-
-    // The following test code is disabled because SkProxyCanvas is
-    // missing a lot of virtual overrides on get* methods, which are used
-    // to verify canvas state.
-    // Issue: http://code.google.com/p/skia/issues/detail?id=500
-
-    if (false) { // avoid bit rot, suppress warning
-        TestProxyCanvasStateConsistency(reporter, testStep, referenceCanvas);
-    }
-
-    // The following test code is disabled because SkNWayCanvas does not
-    // report correct clipping and device bounds information
-    // Issue: http://code.google.com/p/skia/issues/detail?id=501
-
-    if (false) { // avoid bit rot, suppress warning
-        TestNWayCanvasStateConsistency(reporter, testStep, referenceCanvas);
-    }
-
-    if (false) { // avoid bit rot, suppress warning
-        test_clipVisitor(reporter, &referenceCanvas);
-    }
+    testStep->draw(&referenceCanvas, d, reporter);
 }
 
 static void test_newraster(skiatest::Reporter* reporter) {
     SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
-    SkCanvas* canvas = SkCanvas::NewRaster(info);
+    const size_t minRowBytes = info.minRowBytes();
+    const size_t size = info.getSafeSize(minRowBytes);
+    SkAutoTMalloc<SkPMColor> storage(size);
+    SkPMColor* baseAddr = storage.get();
+    sk_bzero(baseAddr, size);
+
+    std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes);
     REPORTER_ASSERT(reporter, canvas);
 
-    SkImageInfo info2;
-    size_t rowBytes;
-    const SkPMColor* addr = (const SkPMColor*)canvas->peekPixels(&info2, &rowBytes);
+    SkPixmap pmap;
+    const SkPMColor* addr = canvas->peekPixels(&pmap) ? pmap.addr32() : nullptr;
     REPORTER_ASSERT(reporter, addr);
-    REPORTER_ASSERT(reporter, info == info2);
+    REPORTER_ASSERT(reporter, info == pmap.info());
+    REPORTER_ASSERT(reporter, minRowBytes == pmap.rowBytes());
     for (int y = 0; y < info.height(); ++y) {
         for (int x = 0; x < info.width(); ++x) {
             REPORTER_ASSERT(reporter, 0 == addr[x]);
         }
-        addr = (const SkPMColor*)((const char*)addr + rowBytes);
+        addr = (const SkPMColor*)((const char*)addr + pmap.rowBytes());
     }
-    SkDELETE(canvas);
 
     // now try a deliberately bad info
     info = info.makeWH(-1, info.height());
-    REPORTER_ASSERT(reporter, NULL == SkCanvas::NewRaster(info));
+    REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
 
     // too big
     info = info.makeWH(1 << 30, 1 << 30);
-    REPORTER_ASSERT(reporter, NULL == SkCanvas::NewRaster(info));
+    REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
 
     // not a valid pixel type
     info = SkImageInfo::Make(10, 10, kUnknown_SkColorType, info.alphaType());
-    REPORTER_ASSERT(reporter, NULL == SkCanvas::NewRaster(info));
+    REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
 
     // We should succeed with a zero-sized valid info
     info = SkImageInfo::MakeN32Premul(0, 0);
-    canvas = SkCanvas::NewRaster(info);
+    canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes);
     REPORTER_ASSERT(reporter, canvas);
-    SkDELETE(canvas);
 }
 
 DEF_TEST(Canvas, reporter) {
-    // Init global here because bitmap pixels cannot be alocated during
-    // static initialization
-    kTestBitmap = testBitmap();
+    TestData d;
 
     for (int testStep = 0; testStep < testStepArray().count(); testStep++) {
-        TestOverrideStateConsistency(reporter, testStepArray()[testStep]);
-        SkPictureTester::TestPictureFlattenedObjectReuse(reporter,
-            testStepArray()[testStep], 0);
+        TestOverrideStateConsistency(reporter, d, testStepArray()[testStep]);
         if (testStepArray()[testStep]->enablePdfTesting()) {
-            TestPdfDevice(reporter, testStepArray()[testStep]);
+            TestPdfDevice(reporter, d, testStepArray()[testStep]);
         }
     }
 
-    // Explicitly call reset(), so we don't leak the pixels (since kTestBitmap is a global)
-    kTestBitmap.reset();
-
     test_newraster(reporter);
 }
+
+DEF_TEST(Canvas_SaveState, reporter) {
+    SkCanvas canvas(10, 10);
+    REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
+
+    int n = canvas.save();
+    REPORTER_ASSERT(reporter, 1 == n);
+    REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount());
+
+    n = canvas.saveLayer(nullptr, nullptr);
+    REPORTER_ASSERT(reporter, 2 == n);
+    REPORTER_ASSERT(reporter, 3 == canvas.getSaveCount());
+
+    canvas.restore();
+    REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount());
+    canvas.restore();
+    REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
+}
+
+DEF_TEST(Canvas_ClipEmptyPath, reporter) {
+    SkCanvas canvas(10, 10);
+    canvas.save();
+    SkPath path;
+    canvas.clipPath(path);
+    canvas.restore();
+    canvas.save();
+    path.moveTo(5, 5);
+    canvas.clipPath(path);
+    canvas.restore();
+    canvas.save();
+    path.moveTo(7, 7);
+    canvas.clipPath(path);  // should not assert here
+    canvas.restore();
+}
+
+namespace {
+
+class MockFilterCanvas : public SkPaintFilterCanvas {
+public:
+    MockFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) { }
+
+protected:
+    bool onFilter(SkTCopyOnFirstWrite<SkPaint>*, Type) const override { return true; }
+
+private:
+    typedef SkPaintFilterCanvas INHERITED;
+};
+
+} // anonymous namespace
+
+// SkPaintFilterCanvas should inherit the initial target canvas state.
+DEF_TEST(PaintFilterCanvas_ConsistentState, reporter) {
+    SkCanvas canvas(100, 100);
+    canvas.clipRect(SkRect::MakeXYWH(12.7f, 12.7f, 75, 75));
+    canvas.scale(0.5f, 0.75f);
+
+    MockFilterCanvas filterCanvas(&canvas);
+    REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix());
+    REPORTER_ASSERT(reporter, canvas.getLocalClipBounds() == filterCanvas.getLocalClipBounds());
+
+    filterCanvas.clipRect(SkRect::MakeXYWH(30.5f, 30.7f, 100, 100));
+    filterCanvas.scale(0.75f, 0.5f);
+    REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix());
+    REPORTER_ASSERT(reporter, filterCanvas.getLocalClipBounds().contains(canvas.getLocalClipBounds()));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkCanvasStack.h"
+#include "SkNWayCanvas.h"
+
+// Subclass that takes a bool*, which it updates in its construct (true) and destructor (false)
+// to allow the caller to know how long the object is alive.
+class LifeLineCanvas : public SkCanvas {
+    bool*   fLifeLine;
+public:
+    LifeLineCanvas(int w, int h, bool* lifeline) : SkCanvas(w, h), fLifeLine(lifeline) {
+        *fLifeLine = true;
+    }
+    ~LifeLineCanvas() {
+        *fLifeLine = false;
+    }
+};
+
+// Check that NWayCanvas does NOT try to manage the lifetime of its sub-canvases
+DEF_TEST(NWayCanvas, r) {
+    const int w = 10;
+    const int h = 10;
+    bool life[2];
+    {
+        LifeLineCanvas c0(w, h, &life[0]);
+        REPORTER_ASSERT(r, life[0]);
+    }
+    REPORTER_ASSERT(r, !life[0]);
+
+
+    std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0]));
+    std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1]));
+    REPORTER_ASSERT(r, life[0]);
+    REPORTER_ASSERT(r, life[1]);
+
+    {
+        SkNWayCanvas nway(w, h);
+        nway.addCanvas(c0.get());
+        nway.addCanvas(c1.get());
+        REPORTER_ASSERT(r, life[0]);
+        REPORTER_ASSERT(r, life[1]);
+    }
+    // Now assert that the death of the nway has NOT also killed the sub-canvases
+    REPORTER_ASSERT(r, life[0]);
+    REPORTER_ASSERT(r, life[1]);
+}
+
+// Check that CanvasStack DOES manage the lifetime of its sub-canvases
+DEF_TEST(CanvasStack, r) {
+    const int w = 10;
+    const int h = 10;
+    bool life[2];
+    std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0]));
+    std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1]));
+    REPORTER_ASSERT(r, life[0]);
+    REPORTER_ASSERT(r, life[1]);
+
+    {
+        SkCanvasStack stack(w, h);
+        stack.pushCanvas(std::move(c0), {0,0});
+        stack.pushCanvas(std::move(c1), {0,0});
+        REPORTER_ASSERT(r, life[0]);
+        REPORTER_ASSERT(r, life[1]);
+    }
+    // Now assert that the death of the canvasstack has also killed the sub-canvases
+    REPORTER_ASSERT(r, !life[0]);
+    REPORTER_ASSERT(r, !life[1]);
+}
+
+static void test_cliptype(SkCanvas* canvas, skiatest::Reporter* r) {
+    REPORTER_ASSERT(r, !canvas->isClipEmpty());
+    REPORTER_ASSERT(r, canvas->isClipRect());
+
+    canvas->save();
+    canvas->clipRect({0, 0, 0, 0});
+    REPORTER_ASSERT(r, canvas->isClipEmpty());
+    REPORTER_ASSERT(r, !canvas->isClipRect());
+    canvas->restore();
+
+    canvas->save();
+    canvas->clipRect({2, 2, 6, 6});
+    REPORTER_ASSERT(r, !canvas->isClipEmpty());
+    REPORTER_ASSERT(r, canvas->isClipRect());
+    canvas->restore();
+
+    canvas->save();
+    canvas->clipRect({2, 2, 6, 6}, SkClipOp::kDifference);  // punch a hole in the clip
+    REPORTER_ASSERT(r, !canvas->isClipEmpty());
+    REPORTER_ASSERT(r, !canvas->isClipRect());
+    canvas->restore();
+
+    REPORTER_ASSERT(r, !canvas->isClipEmpty());
+    REPORTER_ASSERT(r, canvas->isClipRect());
+}
+
+DEF_TEST(CanvasClipType, r) {
+    // test rasterclip backend
+    test_cliptype(SkSurface::MakeRasterN32Premul(10, 10)->getCanvas(), r);
+
+    // test clipstack backend
+    SkDynamicMemoryWStream stream;
+    test_cliptype(SkDocument::MakePDF(&stream)->beginPage(100, 100), r);
+}
+
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+DEF_TEST(Canvas_LegacyColorBehavior, r) {
+    sk_sp<SkColorSpace> cs = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                                   SkColorSpace::kAdobeRGB_Gamut);
+
+    // Make a Adobe RGB bitmap.
+    SkBitmap bitmap;
+    bitmap.allocPixels(SkImageInfo::MakeN32(1, 1, kOpaque_SkAlphaType, cs));
+    bitmap.eraseColor(0xFF000000);
+
+    // Wrap it in a legacy canvas.  Test that the canvas behaves like a legacy canvas.
+    SkCanvas canvas(bitmap, SkCanvas::ColorBehavior::kLegacy);
+    REPORTER_ASSERT(r, !canvas.imageInfo().colorSpace());
+    SkPaint p;
+    p.setColor(SK_ColorRED);
+    canvas.drawIRect(SkIRect::MakeWH(1, 1), p);
+    REPORTER_ASSERT(r, SK_ColorRED == SkSwizzle_BGRA_to_PMColor(*bitmap.getAddr32(0, 0)));
+}
+#endif
diff --git a/src/third_party/skia/tests/ChecksumTest.cpp b/src/third_party/skia/tests/ChecksumTest.cpp
index 6248678..1882235 100644
--- a/src/third_party/skia/tests/ChecksumTest.cpp
+++ b/src/third_party/skia/tests/ChecksumTest.cpp
@@ -6,20 +6,11 @@
  */
 
 #include "SkChecksum.h"
+#include "SkOpts.h"
 #include "SkRandom.h"
 #include "Test.h"
 
-
-// Murmur3 has an optional third seed argument, so we wrap it to fit a uniform type.
-static uint32_t murmur_noseed(const uint32_t* d, size_t l) { return SkChecksum::Murmur3(d, l); }
-
-#define ASSERT(x) REPORTER_ASSERT(r, x)
-
 DEF_TEST(Checksum, r) {
-    // Algorithms to test.  They're currently all uint32_t(const uint32_t*, size_t).
-    typedef uint32_t(*algorithmProc)(const uint32_t*, size_t);
-    const algorithmProc kAlgorithms[] = { &SkChecksum::Compute, &murmur_noseed };
-
     // Put 128 random bytes into two identical buffers.  Any multiple of 4 will do.
     const size_t kBytes = SkAlign4(128);
     SkRandom rand;
@@ -28,25 +19,26 @@
         data[i] = tweaked[i] = rand.nextU();
     }
 
-    // Test each algorithm.
-    for (size_t i = 0; i < SK_ARRAY_COUNT(kAlgorithms); ++i) {
-        const algorithmProc algorithm = kAlgorithms[i];
+    // Hash of nullptr is always 0.
+    REPORTER_ASSERT(r, SkOpts::hash(nullptr, 0) == 0);
 
-        // Hash of NULL is always 0.
-        ASSERT(algorithm(NULL, 0) == 0);
+    const uint32_t hash = SkOpts::hash(data, kBytes);
+    // Should be deterministic.
+    REPORTER_ASSERT(r, hash == SkOpts::hash(data, kBytes));
 
-        const uint32_t hash = algorithm(data, kBytes);
-        // Should be deterministic.
-        ASSERT(hash == algorithm(data, kBytes));
-
-        // Changing any single element should change the hash.
-        for (size_t j = 0; j < SK_ARRAY_COUNT(tweaked); ++j) {
-            const uint32_t saved = tweaked[j];
-            tweaked[j] = rand.nextU();
-            const uint32_t tweakedHash = algorithm(tweaked, kBytes);
-            ASSERT(tweakedHash != hash);
-            ASSERT(tweakedHash == algorithm(tweaked, kBytes));
-            tweaked[j] = saved;
-        }
+    // Changing any single element should change the hash.
+    for (size_t j = 0; j < SK_ARRAY_COUNT(tweaked); ++j) {
+        const uint32_t saved = tweaked[j];
+        tweaked[j] = rand.nextU();
+        const uint32_t tweakedHash = SkOpts::hash(tweaked, kBytes);
+        REPORTER_ASSERT(r, tweakedHash != hash);
+        REPORTER_ASSERT(r, tweakedHash == SkOpts::hash(tweaked, kBytes));
+        tweaked[j] = saved;
     }
 }
+
+DEF_TEST(GoodHash, r) {
+    // 4 bytes --> hits SkChecksum::Mix fast path.
+    REPORTER_ASSERT(r, SkGoodHash()(( int32_t)4) ==  614249093);
+    REPORTER_ASSERT(r, SkGoodHash()((uint32_t)4) ==  614249093);
+}
diff --git a/src/third_party/skia/tests/ClampRangeTest.cpp b/src/third_party/skia/tests/ClampRangeTest.cpp
index bf7c95f..9027e9b 100644
--- a/src/third_party/skia/tests/ClampRangeTest.cpp
+++ b/src/third_party/skia/tests/ClampRangeTest.cpp
@@ -21,9 +21,9 @@
 static void check_value(int64_t bigfx, int expected) {
     if (bigfx < 0) {
         R_ASSERT(expected == kV0);
-    } else if (bigfx > 0xFFFF) {
+    } else if (bigfx > kFracMax_SkGradFixed) {
         R_ASSERT(expected == kV1);
-    } else if (bigfx == 0xFFFF) {
+    } else if (bigfx == kFracMax_SkGradFixed) {
         // Either one is fine (and we do see both).
         R_ASSERT(expected == kV1 || expected == kRamp);
     } else {
@@ -32,7 +32,7 @@
 }
 
 static void slow_check(const SkClampRange& range,
-                       const SkFixed fx, SkFixed dx, int count) {
+                       const SkGradFixed fx, SkGradFixed dx, int count) {
     SkASSERT(range.fCount0 + range.fCount1 + range.fCount2 == count);
 
     // If dx is large, fx will overflow if updated naively.  So we use more bits.
@@ -56,9 +56,12 @@
 
 
 static void test_range(SkFixed fx, SkFixed dx, int count) {
+    const SkGradFixed gfx = SkFixedToGradFixed(fx);
+    const SkGradFixed gdx = SkFixedToGradFixed(dx);
+
     SkClampRange range;
-    range.init(fx, dx, count, kV0, kV1);
-    slow_check(range, fx, dx, count);
+    range.init(gfx, gdx, count, kV0, kV1);
+    slow_check(range, gfx, gdx, count);
 }
 
 #define ff(x)   SkIntToFixed(x)
@@ -79,8 +82,7 @@
     test_range(ff(1),  ff(16384),  100);
     test_range(ff(-1), ff(-16384), 100);
     test_range(ff(1)/2, ff(16384), 100);
-    // TODO(reed): skia:2481, fix whatever bug this is, then uncomment
-    //test_range(ff(1)/2, ff(-16384), 100);
+    test_range(ff(1)/2, ff(-16384), 100);
 
     SkRandom rand;
 
diff --git a/src/third_party/skia/tests/ClearTest.cpp b/src/third_party/skia/tests/ClearTest.cpp
new file mode 100644
index 0000000..bcd0899
--- /dev/null
+++ b/src/third_party/skia/tests/ClearTest.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrRenderTargetContext.h"
+
+static bool check_rect(GrRenderTargetContext* rtc, const SkIRect& rect, uint32_t expectedValue,
+                       uint32_t* actualValue, int* failX, int* failY) {
+    int w = rect.width();
+    int h = rect.height();
+    std::unique_ptr<uint32_t[]> pixels(new uint32_t[w * h]);
+    memset(pixels.get(), ~expectedValue, sizeof(uint32_t) * w * h);
+
+    SkImageInfo dstInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+
+    if (!rtc->readPixels(dstInfo, pixels.get(), 0, rect.fLeft, rect.fTop)) {
+        return false;
+    }
+
+    for (int y = 0; y < h; ++y) {
+        for (int x = 0; x < w; ++x) {
+            uint32_t pixel = pixels.get()[y * w + x];
+            if (pixel != expectedValue) {
+                *actualValue = pixel;
+                *failX = x + rect.fLeft;
+                *failY = y + rect.fTop;
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+sk_sp<GrRenderTargetContext> newRTC(GrContext* context, int w, int h) {
+    return context->makeDeferredRenderTargetContext(SkBackingFit::kExact, w, h,
+                                                    kRGBA_8888_GrPixelConfig, nullptr);
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ClearOp, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    static const int kW = 10;
+    static const int kH = 10;
+
+    SkIRect fullRect = SkIRect::MakeWH(kW, kH);
+    sk_sp<GrRenderTargetContext> rtContext;
+
+    // A rectangle that is inset by one on all sides and the 1-pixel wide rectangles that surround
+    // it.
+    SkIRect mid1Rect = SkIRect::MakeXYWH(1, 1, kW-2, kH-2);
+    SkIRect outerLeftEdge = SkIRect::MakeXYWH(0, 0, 1, kH);
+    SkIRect outerTopEdge = SkIRect::MakeXYWH(0, 0, kW, 1);
+    SkIRect outerRightEdge = SkIRect::MakeXYWH(kW-1, 0, 1, kH);
+    SkIRect outerBottomEdge = SkIRect::MakeXYWH(0, kH-1, kW, 1);
+
+    // A rectangle that is inset by two on all sides and the 1-pixel wide rectangles that surround
+    // it.
+    SkIRect mid2Rect = SkIRect::MakeXYWH(2, 2, kW-4, kH-4);
+    SkIRect innerLeftEdge = SkIRect::MakeXYWH(1, 1, 1, kH-2);
+    SkIRect innerTopEdge = SkIRect::MakeXYWH(1, 1, kW-2, 1);
+    SkIRect innerRightEdge = SkIRect::MakeXYWH(kW-2, 1, 1, kH-2);
+    SkIRect innerBottomEdge = SkIRect::MakeXYWH(1, kH-2, kW-2, 1);
+
+    uint32_t actualValue;
+    int failX, failY;
+
+    static const GrColor kColor1 = 0xABCDEF01;
+    static const GrColor kColor2 = ~kColor1;
+
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
+    // Check a full clear
+    rtContext->clear(&fullRect, kColor1, false);
+    if (!check_rect(rtContext.get(), fullRect, kColor1, &actualValue, &failX, &failY)) {
+        ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
+               failX, failY);
+    }
+
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
+    // Check two full clears, same color
+    rtContext->clear(&fullRect, kColor1, false);
+    rtContext->clear(&fullRect, kColor1, false);
+    if (!check_rect(rtContext.get(), fullRect, kColor1, &actualValue, &failX, &failY)) {
+        ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
+               failX, failY);
+    }
+
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
+    // Check two full clears, different colors
+    rtContext->clear(&fullRect, kColor1, false);
+    rtContext->clear(&fullRect, kColor2, false);
+    if (!check_rect(rtContext.get(), fullRect, kColor2, &actualValue, &failX, &failY)) {
+        ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor2, actualValue,
+               failX, failY);
+    }
+
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
+    // Test a full clear followed by a same color inset clear
+    rtContext->clear(&fullRect, kColor1, false);
+    rtContext->clear(&mid1Rect, kColor1, false);
+    if (!check_rect(rtContext.get(), fullRect, kColor1, &actualValue, &failX, &failY)) {
+        ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
+               failX, failY);
+    }
+
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
+    // Test a inset clear followed by same color full clear
+    rtContext->clear(&mid1Rect, kColor1, false);
+    rtContext->clear(&fullRect, kColor1, false);
+    if (!check_rect(rtContext.get(), fullRect, kColor1, &actualValue, &failX, &failY)) {
+        ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
+               failX, failY);
+    }
+
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
+    // Test a full clear followed by a different color inset clear
+    rtContext->clear(&fullRect, kColor1, false);
+    rtContext->clear(&mid1Rect, kColor2, false);
+    if (!check_rect(rtContext.get(), mid1Rect, kColor2, &actualValue, &failX, &failY)) {
+        ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor2, actualValue,
+               failX, failY);
+    }
+    if (!check_rect(rtContext.get(), outerLeftEdge, kColor1, &actualValue, &failX, &failY) ||
+        !check_rect(rtContext.get(), outerTopEdge, kColor1, &actualValue, &failX, &failY) ||
+        !check_rect(rtContext.get(), outerRightEdge, kColor1, &actualValue, &failX, &failY) ||
+        !check_rect(rtContext.get(), outerBottomEdge, kColor1, &actualValue, &failX, &failY)) {
+        ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
+               failX, failY);
+    }
+
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
+    // Test a inset clear followed by a different full clear
+    rtContext->clear(&mid1Rect, kColor2, false);
+    rtContext->clear(&fullRect, kColor1, false);
+    if (!check_rect(rtContext.get(), fullRect, kColor1, &actualValue, &failX, &failY)) {
+        ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
+               failX, failY);
+    }
+
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
+    // Check three nested clears from largest to smallest where outermost and innermost are same
+    // color.
+    rtContext->clear(&fullRect, kColor1, false);
+    rtContext->clear(&mid1Rect, kColor2, false);
+    rtContext->clear(&mid2Rect, kColor1, false);
+    if (!check_rect(rtContext.get(), mid2Rect, kColor1, &actualValue, &failX, &failY)) {
+        ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
+               failX, failY);
+    }
+    if (!check_rect(rtContext.get(), innerLeftEdge, kColor2, &actualValue, &failX, &failY) ||
+        !check_rect(rtContext.get(), innerTopEdge, kColor2, &actualValue, &failX, &failY) ||
+        !check_rect(rtContext.get(), innerRightEdge, kColor2, &actualValue, &failX, &failY) ||
+        !check_rect(rtContext.get(), innerBottomEdge, kColor2, &actualValue, &failX, &failY)) {
+        ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor2, actualValue,
+               failX, failY);
+    }
+    if (!check_rect(rtContext.get(), outerLeftEdge, kColor1, &actualValue, &failX, &failY) ||
+        !check_rect(rtContext.get(), outerTopEdge, kColor1, &actualValue, &failX, &failY) ||
+        !check_rect(rtContext.get(), outerRightEdge, kColor1, &actualValue, &failX, &failY) ||
+        !check_rect(rtContext.get(), outerBottomEdge, kColor1, &actualValue, &failX, &failY)) {
+        ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
+               failX, failY);
+    }
+
+    rtContext = newRTC(context, kW, kH);
+    SkASSERT(rtContext);
+
+    // Swap the order of the second two clears in the above test.
+    rtContext->clear(&fullRect, kColor1, false);
+    rtContext->clear(&mid2Rect, kColor1, false);
+    rtContext->clear(&mid1Rect, kColor2, false);
+    if (!check_rect(rtContext.get(), mid1Rect, kColor2, &actualValue, &failX, &failY)) {
+        ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor2, actualValue,
+               failX, failY);
+    }
+    if (!check_rect(rtContext.get(), outerLeftEdge, kColor1, &actualValue, &failX, &failY) ||
+        !check_rect(rtContext.get(), outerTopEdge, kColor1, &actualValue, &failX, &failY) ||
+        !check_rect(rtContext.get(), outerRightEdge, kColor1, &actualValue, &failX, &failY) ||
+        !check_rect(rtContext.get(), outerBottomEdge, kColor1, &actualValue, &failX, &failY)) {
+        ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d).", kColor1, actualValue,
+               failX, failY);
+    }
+}
+#endif
diff --git a/src/third_party/skia/tests/ClipBoundsTest.cpp b/src/third_party/skia/tests/ClipBoundsTest.cpp
new file mode 100644
index 0000000..e8aae38
--- /dev/null
+++ b/src/third_party/skia/tests/ClipBoundsTest.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+// This is a GR test
+#if SK_SUPPORT_GPU
+#include "GrClipStackClip.h"
+#include "GrContext.h"
+
+// Ensure that the 'getConservativeBounds' calls are returning bounds clamped
+// to the render target
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrClipBounds, reporter, ctxInfo) {
+    static const int kXSize = 100;
+    static const int kYSize = 100;
+
+    const SkIRect intScreen = SkIRect::MakeWH(kXSize, kYSize);
+    const SkRect screen = SkRect::Make(intScreen);
+
+    SkRect clipRect(screen);
+    clipRect.outset(10, 10);
+
+    // create a clip stack that will (trivially) reduce to a single rect that
+    // is larger than the screen
+    SkClipStack stack;
+    stack.clipRect(clipRect, SkMatrix::I(), kReplace_SkClipOp, false);
+
+    bool isIntersectionOfRects = true;
+    SkRect devStackBounds;
+
+    stack.getConservativeBounds(0, 0, kXSize, kYSize,
+                                &devStackBounds,
+                                &isIntersectionOfRects);
+
+    // make sure that the SkClipStack is behaving itself
+    REPORTER_ASSERT(reporter, screen == devStackBounds);
+    REPORTER_ASSERT(reporter, isIntersectionOfRects);
+
+    // wrap the SkClipStack in a GrClip
+    GrClipStackClip clipData(&stack);
+
+    SkIRect devGrClipBound;
+    clipData.getConservativeBounds(kXSize, kYSize,
+                                   &devGrClipBound,
+                                   &isIntersectionOfRects);
+
+    // make sure that GrClip is behaving itself
+    REPORTER_ASSERT(reporter, intScreen == devGrClipBound);
+    REPORTER_ASSERT(reporter, isIntersectionOfRects);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/ClipCacheTest.cpp b/src/third_party/skia/tests/ClipCacheTest.cpp
deleted file mode 100644
index d9f3b57..0000000
--- a/src/third_party/skia/tests/ClipCacheTest.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Test.h"
-// This is a GR test
-#if SK_SUPPORT_GPU
-#include "../../src/gpu/GrClipMaskManager.h"
-#include "GrContextFactory.h"
-#include "SkGpuDevice.h"
-
-static const int X_SIZE = 12;
-static const int Y_SIZE = 12;
-
-////////////////////////////////////////////////////////////////////////////////
-// note: this is unused
-static GrTexture* createTexture(GrContext* context) {
-    unsigned char textureData[X_SIZE][Y_SIZE][4];
-
-    memset(textureData, 0, 4* X_SIZE * Y_SIZE);
-
-    GrTextureDesc desc;
-
-    // let Skia know we will be using this texture as a render target
-    desc.fFlags     = kRenderTarget_GrTextureFlagBit;
-    desc.fConfig    = kSkia8888_GrPixelConfig;
-    desc.fWidth     = X_SIZE;
-    desc.fHeight    = Y_SIZE;
-
-    // We are initializing the texture with zeros here
-    GrTexture* texture = context->createUncachedTexture(desc, textureData, 0);
-    if (!texture) {
-        return NULL;
-    }
-
-    return texture;
-}
-
-// Ensure that the 'getConservativeBounds' calls are returning bounds clamped
-// to the render target
-static void test_clip_bounds(skiatest::Reporter* reporter, GrContext* context) {
-
-    static const int kXSize = 100;
-    static const int kYSize = 100;
-
-    GrTextureDesc desc;
-    desc.fFlags     = kRenderTarget_GrTextureFlagBit;
-    desc.fConfig    = kAlpha_8_GrPixelConfig;
-    desc.fWidth     = kXSize;
-    desc.fHeight    = kYSize;
-
-    GrTexture* texture = context->createUncachedTexture(desc, NULL, 0);
-    if (!texture) {
-        return;
-    }
-
-    SkAutoTUnref<GrTexture> au(texture);
-
-    SkIRect intScreen = SkIRect::MakeWH(kXSize, kYSize);
-    SkRect screen;
-
-    screen = SkRect::MakeWH(SkIntToScalar(kXSize),
-                            SkIntToScalar(kYSize));
-
-    SkRect clipRect(screen);
-    clipRect.outset(10, 10);
-
-    // create a clip stack that will (trivially) reduce to a single rect that
-    // is larger than the screen
-    SkClipStack stack;
-    stack.clipDevRect(clipRect, SkRegion::kReplace_Op, false);
-
-    bool isIntersectionOfRects = true;
-    SkRect devStackBounds;
-
-    stack.getConservativeBounds(0, 0, kXSize, kYSize,
-                                &devStackBounds,
-                                &isIntersectionOfRects);
-
-    // make sure that the SkClipStack is behaving itself
-    REPORTER_ASSERT(reporter, screen == devStackBounds);
-    REPORTER_ASSERT(reporter, isIntersectionOfRects);
-
-    // wrap the SkClipStack in a GrClipData
-    GrClipData clipData;
-    clipData.fClipStack = &stack;
-
-    SkIRect devGrClipDataBound;
-    clipData.getConservativeBounds(texture,
-                                   &devGrClipDataBound,
-                                   &isIntersectionOfRects);
-
-    // make sure that GrClipData is behaving itself
-    REPORTER_ASSERT(reporter, intScreen == devGrClipDataBound);
-    REPORTER_ASSERT(reporter, isIntersectionOfRects);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// verify that the top state of the stack matches the passed in state
-static void check_state(skiatest::Reporter* reporter,
-                        const GrClipMaskCache& cache,
-                        const SkClipStack& clip,
-                        GrTexture* mask,
-                        const SkIRect& bound) {
-    REPORTER_ASSERT(reporter, clip.getTopmostGenID() == cache.getLastClipGenID());
-
-    REPORTER_ASSERT(reporter, mask == cache.getLastMask());
-
-    SkIRect cacheBound;
-    cache.getLastBound(&cacheBound);
-    REPORTER_ASSERT(reporter, bound == cacheBound);
-}
-
-static void check_empty_state(skiatest::Reporter* reporter,
-                              const GrClipMaskCache& cache) {
-    REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID == cache.getLastClipGenID());
-    REPORTER_ASSERT(reporter, NULL == cache.getLastMask());
-
-    SkIRect emptyBound;
-    emptyBound.setEmpty();
-
-    SkIRect cacheBound;
-    cache.getLastBound(&cacheBound);
-    REPORTER_ASSERT(reporter, emptyBound == cacheBound);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// basic test of the cache's base functionality:
-//  push, pop, set, canReuse & getters
-static void test_cache(skiatest::Reporter* reporter, GrContext* context) {
-
-    if (false) { // avoid bit rot, suppress warning
-        createTexture(context);
-    }
-    GrClipMaskCache cache;
-
-    cache.setContext(context);
-
-    // check initial state
-    check_empty_state(reporter, cache);
-
-    // set the current state
-    SkIRect bound1;
-    bound1.set(0, 0, 100, 100);
-
-    SkClipStack clip1(bound1);
-
-    GrTextureDesc desc;
-    desc.fFlags = kRenderTarget_GrTextureFlagBit;
-    desc.fWidth = X_SIZE;
-    desc.fHeight = Y_SIZE;
-    desc.fConfig = kSkia8888_GrPixelConfig;
-
-    cache.acquireMask(clip1.getTopmostGenID(), desc, bound1);
-
-    GrTexture* texture1 = cache.getLastMask();
-    REPORTER_ASSERT(reporter, texture1);
-    if (NULL == texture1) {
-        return;
-    }
-
-    // check that the set took
-    check_state(reporter, cache, clip1, texture1, bound1);
-
-    // push the state
-    cache.push();
-
-    // verify that the pushed state is initially empty
-    check_empty_state(reporter, cache);
-
-    // modify the new state
-    SkIRect bound2;
-    bound2.set(-10, -10, 10, 10);
-
-    SkClipStack clip2(bound2);
-
-    cache.acquireMask(clip2.getTopmostGenID(), desc, bound2);
-
-    GrTexture* texture2 = cache.getLastMask();
-    REPORTER_ASSERT(reporter, texture2);
-    if (NULL == texture2) {
-        return;
-    }
-
-    // check that the changes took
-    check_state(reporter, cache, clip2, texture2, bound2);
-
-    // check to make sure canReuse works
-    REPORTER_ASSERT(reporter, cache.canReuse(clip2.getTopmostGenID(), bound2));
-    REPORTER_ASSERT(reporter, !cache.canReuse(clip1.getTopmostGenID(), bound1));
-
-    // pop the state
-    cache.pop();
-
-    // verify that the old state is restored
-    check_state(reporter, cache, clip1, texture1, bound1);
-
-    // manually clear the state
-    cache.reset();
-
-    // verify it is now empty
-    check_empty_state(reporter, cache);
-
-    // pop again - so there is no state
-    cache.pop();
-
-#if !defined(SK_DEBUG)
-    // verify that the getters don't crash
-    // only do in release since it generates asserts in debug
-    check_empty_state(reporter, cache);
-#endif
-}
-
-DEF_GPUTEST(ClipCache, reporter, factory) {
-    for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
-        GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type);
-        if (!GrContextFactory::IsRenderingGLContext(glType)) {
-            continue;
-        }
-        GrContext* context = factory->get(glType);
-        if (NULL == context) {
-            continue;
-        }
-
-        test_cache(reporter, context);
-        test_clip_bounds(reporter, context);
-    }
-}
-
-#endif
diff --git a/src/third_party/skia/tests/ClipCubicTest.cpp b/src/third_party/skia/tests/ClipCubicTest.cpp
index 31b38f2..854ac32 100644
--- a/src/third_party/skia/tests/ClipCubicTest.cpp
+++ b/src/third_party/skia/tests/ClipCubicTest.cpp
@@ -5,10 +5,12 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkCubicClipper.h"
 #include "SkGeometry.h"
 #include "SkPaint.h"
+#include "SkPath.h"
 #include "Test.h"
 
 // Currently the supersampler blitter uses int16_t for its index into an array
@@ -161,3 +163,44 @@
 
     test_giantClip();
 }
+
+#include "SkSurface.h"
+
+DEF_TEST(test_fuzz_crbug_698714, reporter) {
+    auto surface(SkSurface::MakeRasterN32Premul(500, 500));
+    SkCanvas* canvas = surface->getCanvas();
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    SkPath path;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0,0
+    path.lineTo(SkBits2Float(0x43434343), SkBits2Float(0x43430143));  //195.263f, 195.005f
+    path.lineTo(SkBits2Float(0x43434343), SkBits2Float(0x43434343));  //195.263f, 195.263f
+    path.lineTo(SkBits2Float(0xb5434343), SkBits2Float(0x434300be));  //-7.2741e-07f, 195.003f
+    // 195.263f, 195.263f, -1.16387e-05f, 3.58641e-38f, 3.85088e-29f,1.86082e-39f
+    path.cubicTo(SkBits2Float(0x43434343), SkBits2Float(0x43434341),
+            SkBits2Float(0xb74343bd), SkBits2Float(0x01434343),
+            SkBits2Float(0x10434343), SkBits2Float(0x00144332));
+    // 4.11823e-38f, 195.263f, 195.263f, 195.263f, -7.2741e-07f, 195.263f
+    path.cubicTo(SkBits2Float(0x016037c0), SkBits2Float(0x43434343),
+            SkBits2Float(0x43434343), SkBits2Float(0x43434343),
+            SkBits2Float(0xb5434343), SkBits2Float(0x43434343));
+    // 195.263f, 195.263f, -1.16387e-05f, 3.58641e-38f, 195.263f, -2
+    path.cubicTo(SkBits2Float(0x43434344), SkBits2Float(0x43434341),
+            SkBits2Float(0xb74343bd), SkBits2Float(0x01434343),
+            SkBits2Float(0x43434343), SkBits2Float(0xc0000014));
+    // -5.87228e+06f, 3.7773e-07f, 3.60231e-13f, -6.64511e+06f,2.77692e-15f, 2.48803e-15f
+    path.cubicTo(SkBits2Float(0xcab33535), SkBits2Float(0x34cacaca),
+            SkBits2Float(0x2acacaca), SkBits2Float(0xcacacae3),
+            SkBits2Float(0x27481927), SkBits2Float(0x27334805));
+    path.lineTo(SkBits2Float(0xb5434343), SkBits2Float(0x43434343));  //-7.2741e-07f, 195.263f
+    // 195.263f, 195.263f, -1.16387e-05f, 195.212f, 195.263f, -2
+    path.cubicTo(SkBits2Float(0x43434343), SkBits2Float(0x43434341),
+            SkBits2Float(0xb74343b9), SkBits2Float(0x43433643),
+            SkBits2Float(0x43434343), SkBits2Float(0xc0000014));
+    path.lineTo(SkBits2Float(0xc7004343), SkBits2Float(0x27480527));  //-32835.3f, 2.77584e-15f
+    path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0,0
+    path.close();
+    canvas->clipRect({0, 0, 65, 202});
+    canvas->drawPath(path, paint);
+}
diff --git a/src/third_party/skia/tests/ClipStackTest.cpp b/src/third_party/skia/tests/ClipStackTest.cpp
index 662e680..6f24908 100644
--- a/src/third_party/skia/tests/ClipStackTest.cpp
+++ b/src/third_party/skia/tests/ClipStackTest.cpp
@@ -6,15 +6,23 @@
  */
 
 #include "Test.h"
-#if SK_SUPPORT_GPU
-    #include "GrReducedClip.h"
-#endif
 #include "SkClipStack.h"
 #include "SkPath.h"
 #include "SkRandom.h"
 #include "SkRect.h"
 #include "SkRegion.h"
 
+#if SK_SUPPORT_GPU
+#include "GrClipStackClip.h"
+#include "GrReducedClip.h"
+#include "GrResourceCache.h"
+#include "GrSurfaceProxyPriv.h"
+#include "GrTexture.h"
+#include "GrTextureProxy.h"
+typedef GrReducedClip::ElementList ElementList;
+typedef GrReducedClip::InitialState InitialState;
+#endif
+
 static void test_assign_and_comparison(skiatest::Reporter* reporter) {
     SkClipStack s;
     bool doAA = false;
@@ -30,21 +38,21 @@
     p.lineTo(7, 8);
     p.lineTo(5, 9);
     p.close();
-    s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
+    s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
 
     s.save();
     REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
 
     SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
-    s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
+    s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
     r = SkRect::MakeLTRB(10, 11, 12, 13);
-    s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
+    s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
 
     s.save();
     REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
 
     r = SkRect::MakeLTRB(14, 15, 16, 17);
-    s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
+    s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
 
     // Test that assignment works.
     SkClipStack copy = s;
@@ -59,7 +67,7 @@
     s.save();
     REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
     r = SkRect::MakeLTRB(14, 15, 16, 17);
-    s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
+    s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
     REPORTER_ASSERT(reporter, s == copy);
 
     // Test that a different op on one level triggers not equal.
@@ -68,7 +76,7 @@
     s.save();
     REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
     r = SkRect::MakeLTRB(14, 15, 16, 17);
-    s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
+    s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
     REPORTER_ASSERT(reporter, s != copy);
 
     // Test that version constructed with rect-path rather than a rect is still considered equal.
@@ -76,7 +84,7 @@
     s.save();
     SkPath rp;
     rp.addRect(r);
-    s.clipDevPath(rp, SkRegion::kUnion_Op, doAA);
+    s.clipPath(rp, SkMatrix::I(), kUnion_SkClipOp, doAA);
     REPORTER_ASSERT(reporter, s == copy);
 
     // Test that different rects triggers not equal.
@@ -86,7 +94,7 @@
     REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
 
     r = SkRect::MakeLTRB(24, 25, 26, 27);
-    s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
+    s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
     REPORTER_ASSERT(reporter, s != copy);
 
     // Sanity check
@@ -109,7 +117,7 @@
     REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
 
     p.addRect(r);
-    s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
+    s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
     REPORTER_ASSERT(reporter, s != copy);
 }
 
@@ -137,14 +145,14 @@
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
         // the union op will prevent these from being fused together
-        stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false);
+        stack.clipRect(gRects[i], SkMatrix::I(), kUnion_SkClipOp, false);
     }
 
     assert_count(reporter, stack, 4);
 
     // bottom to top iteration
     {
-        const SkClipStack::Element* element = NULL;
+        const SkClipStack::Element* element = nullptr;
 
         SkClipStack::B2TIter iter(stack);
         int i;
@@ -159,7 +167,7 @@
 
     // top to bottom iteration
     {
-        const SkClipStack::Element* element = NULL;
+        const SkClipStack::Element* element = nullptr;
 
         SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
         int i;
@@ -174,11 +182,11 @@
 
     // skipToTopmost
     {
-        const SkClipStack::Element* element = NULL;
+        const SkClipStack::Element* element = nullptr;
 
         SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
 
-        element = iter.skipToTopmost(SkRegion::kUnion_Op);
+        element = iter.skipToTopmost(kUnion_SkClipOp);
         REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
         REPORTER_ASSERT(reporter, element->getRect() == gRects[3]);
     }
@@ -217,12 +225,12 @@
         { 10, 10, 50, 50 },
     };
 
-    static const SkRegion::Op gOps[] = {
-        SkRegion::kIntersect_Op,
-        SkRegion::kDifference_Op,
-        SkRegion::kUnion_Op,
-        SkRegion::kXOR_Op,
-        SkRegion::kReverseDifference_Op
+    static const SkClipOp gOps[] = {
+        kIntersect_SkClipOp,
+        kDifference_SkClipOp,
+        kUnion_SkClipOp,
+        kXOR_SkClipOp,
+        kReverseDifference_SkClipOp
     };
 
     SkRect rectA, rectB;
@@ -262,16 +270,16 @@
                     SkDEBUGFAIL("Don't call this with kEmpty.");
                     break;
                 case SkClipStack::Element::kRect_Type:
-                    stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false);
-                    stack.clipDevRect(rectB, gOps[op], false);
+                    stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
+                    stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
                     break;
                 case SkClipStack::Element::kRRect_Type:
-                    stack.clipDevRRect(rrectA, SkRegion::kIntersect_Op, false);
-                    stack.clipDevRRect(rrectB, gOps[op], false);
+                    stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
+                    stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
                     break;
                 case SkClipStack::Element::kPath_Type:
-                    stack.clipDevPath(pathA, SkRegion::kIntersect_Op, false);
-                    stack.clipDevPath(pathB, gOps[op], false);
+                    stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
+                    stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
                     break;
             }
 
@@ -283,7 +291,7 @@
 
             if (SkClipStack::Element::kRect_Type == primType) {
                 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
-                        (gOps[op] == SkRegion::kIntersect_Op));
+                        (gOps[op] == kIntersect_SkClipOp));
             } else {
                 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
             }
@@ -331,8 +339,8 @@
         clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
         clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
 
-        stack.clipDevPath(clipA, SkRegion::kReplace_Op, false);
-        stack.clipDevPath(clipB, SkRegion::kUnion_Op, false);
+        stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
+        stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
 
         REPORTER_ASSERT(reporter, stack.isWideOpen());
         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
@@ -342,7 +350,7 @@
     {
         SkClipStack stack;
 
-        stack.clipDevRect(rectA, SkRegion::kUnion_Op, false);
+        stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false);
 
         REPORTER_ASSERT(reporter, stack.isWideOpen());
         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
@@ -355,7 +363,7 @@
         SkRect emptyRect;
         emptyRect.setEmpty();
 
-        stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false);
+        stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false);
 
         REPORTER_ASSERT(reporter, stack.isWideOpen());
         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
@@ -367,7 +375,7 @@
 
         stack.save();
 
-        stack.clipDevRect(rectA, SkRegion::kReplace_Op, false);
+        stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false);
 
         REPORTER_ASSERT(reporter, !stack.isWideOpen());
         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
@@ -383,7 +391,7 @@
 
     SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
 
-    const SkClipStack::Element* element = NULL;
+    const SkClipStack::Element* element = nullptr;
     int count = 0;
 
     for (element = iter.prev(); element; element = iter.prev(), ++count) {
@@ -401,7 +409,7 @@
     path.addRect(rect);
     path.toggleInverseFillType();
     SkClipStack stack;
-    stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+    stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
 
     SkRect bounds;
     SkClipStack::BoundsType boundsType;
@@ -423,9 +431,9 @@
     {
         SkClipStack stack;
         REPORTER_ASSERT(reporter, 0 == count(stack));
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
         REPORTER_ASSERT(reporter, 1 == count(stack));
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
         REPORTER_ASSERT(reporter, 1 == count(stack));
     }
 
@@ -434,9 +442,9 @@
     {
         SkClipStack stack;
         REPORTER_ASSERT(reporter, 0 == count(stack));
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
         REPORTER_ASSERT(reporter, 1 == count(stack));
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
         REPORTER_ASSERT(reporter, 1 == count(stack));
     }
 
@@ -445,23 +453,23 @@
     {
         SkClipStack stack;
         REPORTER_ASSERT(reporter, 0 == count(stack));
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
         REPORTER_ASSERT(reporter, 1 == count(stack));
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
         REPORTER_ASSERT(reporter, 1 == count(stack));
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
         REPORTER_ASSERT(reporter, 1 == count(stack));
     }
 
     // Make sure replace clip rects don't collapse too much.
     {
         SkClipStack stack;
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
-        stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
+        stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, 1 == count(stack));
 
         stack.save();
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
         REPORTER_ASSERT(reporter, 2 == count(stack));
         stack.getBounds(&bound, &type, &isIntersectionOfRects);
         REPORTER_ASSERT(reporter, bound == rect);
@@ -469,16 +477,16 @@
         REPORTER_ASSERT(reporter, 1 == count(stack));
 
         stack.save();
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
         REPORTER_ASSERT(reporter, 2 == count(stack));
         stack.restore();
         REPORTER_ASSERT(reporter, 1 == count(stack));
 
         stack.save();
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
-        stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
+        stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
         REPORTER_ASSERT(reporter, 2 == count(stack));
         stack.restore();
         REPORTER_ASSERT(reporter, 1 == count(stack));
@@ -495,18 +503,18 @@
     {
         SkClipStack stack;
         REPORTER_ASSERT(reporter, 0 == count(stack));
-        stack.clipDevPath(path, SkRegion::kReplace_Op, false);
+        stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
         REPORTER_ASSERT(reporter, 1 == count(stack));
-        stack.clipDevPath(path, SkRegion::kReplace_Op, false);
+        stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
         REPORTER_ASSERT(reporter, 1 == count(stack));
     }
 
     // Replacing rect with path.
     {
         SkClipStack stack;
-        stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
+        stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
         REPORTER_ASSERT(reporter, 1 == count(stack));
-        stack.clipDevPath(path, SkRegion::kReplace_Op, true);
+        stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
         REPORTER_ASSERT(reporter, 1 == count(stack));
     }
 }
@@ -529,9 +537,9 @@
     {
         SkClipStack stack;
 
-        stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false);
+        stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
 
-        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
+        stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
 
         REPORTER_ASSERT(reporter, 1 == count(stack));
 
@@ -544,9 +552,9 @@
     {
         SkClipStack stack;
 
-        stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
+        stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
 
-        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true);
+        stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true);
 
         REPORTER_ASSERT(reporter, 1 == count(stack));
 
@@ -559,9 +567,9 @@
     {
         SkClipStack stack;
 
-        stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
+        stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
 
-        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
+        stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
 
         REPORTER_ASSERT(reporter, 2 == count(stack));
 
@@ -574,9 +582,9 @@
     {
         SkClipStack stack;
 
-        stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true);
+        stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
 
-        stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false);
+        stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
 
         REPORTER_ASSERT(reporter, 1 == count(stack));
 
@@ -589,9 +597,9 @@
     {
         SkClipStack stack;
 
-        stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false);
+        stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
 
-        stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true);
+        stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
 
         REPORTER_ASSERT(reporter, 1 == count(stack));
 
@@ -604,9 +612,9 @@
     {
         SkClipStack stack;
 
-        stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false);
+        stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
 
-        stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true);
+        stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
 
         REPORTER_ASSERT(reporter, 2 == count(stack));
 
@@ -634,32 +642,32 @@
 
     {
         SkClipStack stack;
-        stack.clipDevRect(outsideRect, SkRegion::kDifference_Op, false);
-        // return false because quickContains currently does not care for kDifference_Op
+        stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
+        // return false because quickContains currently does not care for kDifference_SkClipOp
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
     // Replace Op tests
     {
         SkClipStack stack;
-        stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
+        stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
     }
 
     {
         SkClipStack stack;
-        stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
+        stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
         stack.save(); // To prevent in-place substitution by replace OP
-        stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
+        stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
         stack.restore();
     }
 
     {
         SkClipStack stack;
-        stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
+        stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
         stack.save(); // To prevent in-place substitution by replace OP
-        stack.clipDevRect(insideRect, SkRegion::kReplace_Op, false);
+        stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
         stack.restore();
     }
@@ -667,59 +675,59 @@
     // Verify proper traversal of multi-element clip
     {
         SkClipStack stack;
-        stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
+        stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
         // Use a path for second clip to prevent in-place intersection
-        stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
+        stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
     // Intersect Op tests with rectangles
     {
         SkClipStack stack;
-        stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
+        stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
     }
 
     {
         SkClipStack stack;
-        stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
+        stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
     {
         SkClipStack stack;
-        stack.clipDevRect(intersectingRect, SkRegion::kIntersect_Op, false);
+        stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
     {
         SkClipStack stack;
-        stack.clipDevRect(nonIntersectingRect, SkRegion::kIntersect_Op, false);
+        stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
     // Intersect Op tests with circle paths
     {
         SkClipStack stack;
-        stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
+        stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
     }
 
     {
         SkClipStack stack;
-        stack.clipDevPath(insideCircle, SkRegion::kIntersect_Op, false);
+        stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
     {
         SkClipStack stack;
-        stack.clipDevPath(intersectingCircle, SkRegion::kIntersect_Op, false);
+        stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
     {
         SkClipStack stack;
-        stack.clipDevPath(nonIntersectingCircle, SkRegion::kIntersect_Op, false);
+        stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
@@ -729,7 +737,7 @@
         SkPath path;
         path.addRect(outsideRect);
         path.toggleInverseFillType();
-        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
@@ -738,7 +746,7 @@
         SkPath path;
         path.addRect(insideRect);
         path.toggleInverseFillType();
-        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
@@ -747,7 +755,7 @@
         SkPath path;
         path.addRect(intersectingRect);
         path.toggleInverseFillType();
-        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
@@ -756,7 +764,7 @@
         SkPath path;
         path.addRect(nonIntersectingRect);
         path.toggleInverseFillType();
-        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
     }
 
@@ -765,7 +773,7 @@
         SkClipStack stack;
         SkPath path = outsideCircle;
         path.toggleInverseFillType();
-        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
@@ -773,7 +781,7 @@
         SkClipStack stack;
         SkPath path = insideCircle;
         path.toggleInverseFillType();
-        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
@@ -781,7 +789,7 @@
         SkClipStack stack;
         SkPath path = intersectingCircle;
         path.toggleInverseFillType();
-        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
     }
 
@@ -789,11 +797,56 @@
         SkClipStack stack;
         SkPath path = nonIntersectingCircle;
         path.toggleInverseFillType();
-        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
     }
 }
 
+static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
+    region->setRect(bounds);
+    SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
+    while (const SkClipStack::Element *element = iter.next()) {
+        SkRegion elemRegion;
+        SkRegion boundsRgn(bounds);
+        SkPath path;
+
+        switch (element->getType()) {
+            case SkClipStack::Element::kEmpty_Type:
+                elemRegion.setEmpty();
+                break;
+            default:
+                element->asPath(&path);
+                elemRegion.setPath(path, boundsRgn);
+                break;
+        }
+        region->op(elemRegion, (SkRegion::Op)element->getOp());
+    }
+}
+
+static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
+    SkClipStack stack;
+    stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
+
+    SkPath path;
+    path.addRect({30, 10, 40, 20});
+    path.setFillType(SkPath::kInverseWinding_FillType);
+    stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
+
+    REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
+
+    SkRect stackBounds;
+    SkClipStack::BoundsType stackBoundsType;
+    stack.getBounds(&stackBounds, &stackBoundsType);
+
+    REPORTER_ASSERT(reporter, stackBounds.isEmpty());
+    REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
+
+    SkRegion region;
+    set_region_to_stack(stack, {0, 0, 50, 30}, &region);
+
+    REPORTER_ASSERT(reporter, region.isEmpty());
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #if SK_SUPPORT_GPU
@@ -803,54 +856,58 @@
 // reduced stack.
 typedef void (*AddElementFunc) (const SkRect& rect,
                                 bool invert,
-                                SkRegion::Op op,
-                                SkClipStack* stack);
+                                SkClipOp op,
+                                SkClipStack* stack,
+                                bool doAA);
 
-static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
+static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
+                           bool doAA) {
     SkScalar rx = rect.width() / 10;
     SkScalar ry = rect.height() / 20;
     if (invert) {
         SkPath path;
         path.addRoundRect(rect, rx, ry);
         path.setFillType(SkPath::kInverseWinding_FillType);
-        stack->clipDevPath(path, op, false);
+        stack->clipPath(path, SkMatrix::I(), op, doAA);
     } else {
         SkRRect rrect;
         rrect.setRectXY(rect, rx, ry);
-        stack->clipDevRRect(rrect, op, false);
+        stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
     }
 };
 
-static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
+static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
+                     bool doAA) {
     if (invert) {
         SkPath path;
         path.addRect(rect);
         path.setFillType(SkPath::kInverseWinding_FillType);
-        stack->clipDevPath(path, op, false);
+        stack->clipPath(path, SkMatrix::I(), op, doAA);
     } else {
-        stack->clipDevRect(rect, op, false);
+        stack->clipRect(rect, SkMatrix::I(), op, doAA);
     }
 };
 
-static void add_oval(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
+static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
+                     bool doAA) {
     SkPath path;
     path.addOval(rect);
     if (invert) {
         path.setFillType(SkPath::kInverseWinding_FillType);
     }
-    stack->clipDevPath(path, op, false);
+    stack->clipPath(path, SkMatrix::I(), op, doAA);
 };
 
 static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
     switch (element.getType()) {
         case SkClipStack::Element::kRect_Type:
-            stack->clipDevRect(element.getRect(), element.getOp(), element.isAA());
+            stack->clipRect(element.getRect(), SkMatrix::I(), element.getOp(), element.isAA());
             break;
         case SkClipStack::Element::kRRect_Type:
-            stack->clipDevRRect(element.getRRect(), element.getOp(), element.isAA());
+            stack->clipRRect(element.getRRect(), SkMatrix::I(), element.getOp(), element.isAA());
             break;
         case SkClipStack::Element::kPath_Type:
-            stack->clipDevPath(element.getPath(), element.getOp(), element.isAA());
+            stack->clipPath(element.getPath(), SkMatrix::I(), element.getOp(), element.isAA());
             break;
         case SkClipStack::Element::kEmpty_Type:
             SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
@@ -859,34 +916,16 @@
     }
 }
 
-static void add_elem_to_region(const SkClipStack::Element& element,
-                               const SkIRect& bounds,
-                               SkRegion* region) {
-    SkRegion elemRegion;
-    SkRegion boundsRgn(bounds);
-    SkPath path;
-
-    switch (element.getType()) {
-        case SkClipStack::Element::kEmpty_Type:
-            elemRegion.setEmpty();
-            break;
-        default:
-            element.asPath(&path);
-            elemRegion.setPath(path, boundsRgn);
-            break;
-    }
-    region->op(elemRegion, element.getOp());
-}
-
 static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
     // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
     // they are equal.
 
     // All the clip elements will be contained within these bounds.
-    static const SkRect kBounds = SkRect::MakeWH(100, 100);
+    static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
+    static const SkRect kBounds = SkRect::Make(kIBounds);
 
     enum {
-        kNumTests = 200,
+        kNumTests = 250,
         kMinElemsPerTest = 1,
         kMaxElemsPerTest = 50,
     };
@@ -895,13 +934,13 @@
     static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
     static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
 
-    static const SkRegion::Op kOps[] = {
-        SkRegion::kDifference_Op,
-        SkRegion::kIntersect_Op,
-        SkRegion::kUnion_Op,
-        SkRegion::kXOR_Op,
-        SkRegion::kReverseDifference_Op,
-        SkRegion::kReplace_Op,
+    static const SkClipOp kOps[] = {
+        kDifference_SkClipOp,
+        kIntersect_SkClipOp,
+        kUnion_SkClipOp,
+        kXOR_SkClipOp,
+        kReverseDifference_SkClipOp,
+        kReplace_SkClipOp,
     };
 
     // Replace operations short-circuit the optimizer. We want to make sure that we test this code
@@ -912,6 +951,8 @@
     // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
     static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
 
+    static const SkScalar kFractionAntialiased = 0.25;
+
     static const AddElementFunc kElementFuncs[] = {
         add_rect,
         add_round_rect,
@@ -921,12 +962,16 @@
     SkRandom r;
 
     for (int i = 0; i < kNumTests; ++i) {
+        SkString testCase;
+        testCase.printf("Iteration %d", i);
+
         // Randomly generate a clip stack.
         SkClipStack stack;
         int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
+        bool doAA = r.nextBiasedBool(kFractionAntialiased);
         for (int e = 0; e < numElems; ++e) {
-            SkRegion::Op op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
-            if (op == SkRegion::kReplace_Op) {
+            SkClipOp op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
+            if (op == kReplace_SkClipOp) {
                 if (r.nextU() % kReplaceDiv) {
                     --e;
                     continue;
@@ -937,81 +982,97 @@
             bool doSave = r.nextBool();
 
             SkSize size = SkSize::Make(
-                SkScalarFloorToScalar(SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))),
-                SkScalarFloorToScalar(SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))));
+                kBounds.width()  * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac),
+                kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac));
 
-            SkPoint xy = {SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth)),
-                          SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight))};
+            SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
+                          r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
 
-            SkRect rect = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
+            SkRect rect;
+            if (doAA) {
+                rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
+                if (GrClip::IsPixelAligned(rect)) {
+                    // Don't create an element that may accidentally become not antialiased.
+                    rect.outset(0.5f, 0.5f);
+                }
+                SkASSERT(!GrClip::IsPixelAligned(rect));
+            } else {
+                rect.setXYWH(SkScalarFloorToScalar(xy.fX),
+                             SkScalarFloorToScalar(xy.fY),
+                             SkScalarCeilToScalar(size.fWidth),
+                             SkScalarCeilToScalar(size.fHeight));
+            }
 
             bool invert = r.nextBiasedBool(kFractionInverted);
 
-            kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack);
+            kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack,
+                                                                          doAA);
             if (doSave) {
                 stack.save();
             }
         }
 
-        SkRect inflatedBounds = kBounds;
-        inflatedBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
-        SkIRect inflatedIBounds;
-        inflatedBounds.roundOut(&inflatedIBounds);
+        // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
+        // will be kInvalidGenID if left uninitialized.
+        SkAlignedSTStorage<1, GrReducedClip> storage;
+        memset(storage.get(), 0, sizeof(GrReducedClip));
+        GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
 
-        typedef GrReducedClip::ElementList ElementList;
         // Get the reduced version of the stack.
-        ElementList reducedClips;
-        int32_t reducedGenID;
-        GrReducedClip::InitialState initial;
-        SkIRect tBounds(inflatedIBounds);
-        SkIRect* tightBounds = r.nextBool() ? &tBounds : NULL;
-        GrReducedClip::ReduceClipStack(stack,
-                                       inflatedIBounds,
-                                       &reducedClips,
-                                       &reducedGenID,
-                                       &initial,
-                                       tightBounds);
+        SkRect queryBounds = kBounds;
+        queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
+        const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds);
 
-        REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID);
+        REPORTER_ASSERT_MESSAGE(reporter,
+                                reduced->elements().isEmpty() ||
+                                SkClipStack::kInvalidGenID != reduced->elementsGenID(),
+                                testCase.c_str());
+
+        if (!reduced->elements().isEmpty()) {
+            REPORTER_ASSERT_MESSAGE(reporter, reduced->hasIBounds(), testCase.c_str());
+            SkRect stackBounds;
+            SkClipStack::BoundsType stackBoundsType;
+            stack.getBounds(&stackBounds, &stackBoundsType);
+            if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
+                // Unless GrReducedClip starts doing some heroic tightening of the clip bounds, this
+                // will be true since the stack bounds are completely contained inside the query.
+                REPORTER_ASSERT_MESSAGE(reporter,
+                                        GrClip::IsInsideClip(reduced->ibounds(), stackBounds),
+                                        testCase.c_str());
+            }
+            REPORTER_ASSERT_MESSAGE(reporter, reduced->requiresAA() == doAA, testCase.c_str());
+        }
 
         // Build a new clip stack based on the reduced clip elements
         SkClipStack reducedStack;
-        if (GrReducedClip::kAllOut_InitialState == initial) {
+        if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
             // whether the result is bounded or not, the whole plane should start outside the clip.
             reducedStack.clipEmpty();
         }
-        for (ElementList::Iter iter = reducedClips.headIter(); iter.get(); iter.next()) {
+        for (ElementList::Iter iter(reduced->elements()); iter.get(); iter.next()) {
             add_elem_to_stack(*iter.get(), &reducedStack);
         }
 
+        SkIRect ibounds = reduced->hasIBounds() ? reduced->ibounds() : kIBounds;
+
         // GrReducedClipStack assumes that the final result is clipped to the returned bounds
-        if (tightBounds) {
-            reducedStack.clipDevRect(*tightBounds, SkRegion::kIntersect_Op);
-        }
+        reducedStack.clipDevRect(ibounds, kIntersect_SkClipOp);
+        stack.clipDevRect(ibounds, kIntersect_SkClipOp);
 
         // convert both the original stack and reduced stack to SkRegions and see if they're equal
         SkRegion region;
+        set_region_to_stack(stack, ibounds, &region);
+
         SkRegion reducedRegion;
+        set_region_to_stack(reducedStack, ibounds, &reducedRegion);
 
-        region.setRect(inflatedIBounds);
-        const SkClipStack::Element* element;
-        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
-        while ((element = iter.next())) {
-            add_elem_to_region(*element, inflatedIBounds, &region);
-        }
-
-        reducedRegion.setRect(inflatedIBounds);
-        iter.reset(reducedStack, SkClipStack::Iter::kBottom_IterStart);
-        while ((element = iter.next())) {
-            add_elem_to_region(*element, inflatedIBounds, &reducedRegion);
-        }
-        SkString testCase;
-        testCase.printf("Iteration %d", i);
         REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str());
+
+        reduced->~GrReducedClip();
     }
 }
 
-#if defined(WIN32)
+#ifdef SK_BUILD_FOR_WIN
     #define SUPPRESS_VISIBILITY_WARNING
 #else
     #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
@@ -1020,25 +1081,22 @@
 static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
     {
         SkClipStack stack;
-        stack.clipDevRect(SkRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op, true);
-        stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkRegion::kReplace_Op, true);
-        SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100);
+        stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
+                       true);
+        stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
+                       kReplace_SkClipOp, true);
+        SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
 
-        GrReducedClip::ElementList reducedClips;
-        int32_t reducedGenID;
-        GrReducedClip::InitialState initial;
-        SkIRect tightBounds;
+        SkAlignedSTStorage<1, GrReducedClip> storage;
+        memset(storage.get(), 0, sizeof(GrReducedClip));
+        GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
+        const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds);
 
-        GrReducedClip::ReduceClipStack(stack,
-                                       inflatedIBounds,
-                                       &reducedClips,
-                                       &reducedGenID,
-                                       &initial,
-                                       &tightBounds);
-
-        REPORTER_ASSERT(reporter, reducedClips.count() == 1);
+        REPORTER_ASSERT(reporter, reduced->elements().count() == 1);
         // Clips will be cached based on the generation id. Make sure the gen id is valid.
-        REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID);
+        REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->elementsGenID());
+
+        reduced->~GrReducedClip();
     }
     {
         SkClipStack stack;
@@ -1047,21 +1105,24 @@
         //  A  B
         //  C  D
 
-        stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kReplace_Op, true);
-        int32_t genIDA = stack.getTopmostGenID();
-        stack.clipDevRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
-        int32_t genIDB = stack.getTopmostGenID();
-        stack.clipDevRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
-        int32_t genIDC = stack.getTopmostGenID();
-        stack.clipDevRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
-        int32_t genIDD = stack.getTopmostGenID();
+        stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
+                       kReplace_SkClipOp, true);
+        uint32_t genIDA = stack.getTopmostGenID();
+        stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
+                       kUnion_SkClipOp, true);
+        uint32_t genIDB = stack.getTopmostGenID();
+        stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
+                       kUnion_SkClipOp, true);
+        uint32_t genIDC = stack.getTopmostGenID();
+        stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
+                       kUnion_SkClipOp, true);
+        uint32_t genIDD = stack.getTopmostGenID();
 
 
-#define XYWH SkIRect::MakeXYWH
+#define IXYWH SkIRect::MakeXYWH
+#define XYWH SkRect::MakeXYWH
 
-        SkIRect unused;
-        unused.setEmpty();
-        SkIRect stackBounds = XYWH(0, 0, 76, 76);
+        SkIRect stackBounds = IXYWH(0, 0, 76, 76);
 
         // The base test is to test each rect in two ways:
         // 1) The box dimensions. (Should reduce to "all in", no elements).
@@ -1072,99 +1133,259 @@
 
         // Not passing in tighter bounds is tested for consistency.
         static const struct SUPPRESS_VISIBILITY_WARNING {
-            SkIRect testBounds;
+            SkRect testBounds;
             int reducedClipCount;
-            int32_t reducedGenID;
-            GrReducedClip::InitialState initialState;
-            SkIRect tighterBounds; // If this is empty, the query will not pass tighter bounds
+            uint32_t reducedGenID;
+            InitialState initialState;
+            SkIRect clipIRect;
             // parameter.
         } testCases[] = {
+
             // Rect A.
-            { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 0, 25, 25) },
-            { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
-            { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, XYWH(0, 0, 27, 27)},
-            { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, unused },
+            { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
+            { XYWH(0.1f, 0.1f, 25.1f, 25.1f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 26, 26) },
+            { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 27, 27)},
 
             // Rect B.
-            { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 0, 25, 25) },
-            { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
-            { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, XYWH(50, 0, 26, 27) },
-            { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, unused },
+            { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
+            { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
+            { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
 
             // Rect C.
-            { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 50, 25, 25) },
-            { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
-            { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, XYWH(0, 50, 27, 26) },
-            { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, unused },
+            { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
+            { XYWH(0.2f, 50.1f, 25.1f, 25.2f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 26, 26) },
+            { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
 
             // Rect D.
-            { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
-            { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 50, 25, 25)},
-            { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState, unused },
-            { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState,  XYWH(50, 50, 26, 26)},
+            { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
+            { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
+            { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut,  IXYWH(50, 50, 26, 26)},
 
             // Other tests:
-            { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused },
-            { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, stackBounds },
+            { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
 
             // Rect in the middle, touches none.
-            { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, unused },
-            { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, XYWH(26, 26, 24, 24) },
+            { XYWH(26, 26, 24, 24), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllOut, IXYWH(26, 26, 24, 24) },
 
             // Rect in the middle, touches all the rects. GenID is the last rect.
-            { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused },
-            { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, XYWH(24, 24, 27, 27) },
+            { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
         };
 
 #undef XYWH
+#undef IXYWH
 
         for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
-            GrReducedClip::ElementList reducedClips;
-            int32_t reducedGenID;
-            GrReducedClip::InitialState initial;
-            SkIRect tightBounds;
-
-            GrReducedClip::ReduceClipStack(stack,
-                                           testCases[i].testBounds,
-                                           &reducedClips,
-                                           &reducedGenID,
-                                           &initial,
-                                           testCases[i].tighterBounds.isEmpty() ? NULL : &tightBounds);
-
-            REPORTER_ASSERT(reporter, reducedClips.count() == testCases[i].reducedClipCount);
-            SkASSERT(reducedClips.count() == testCases[i].reducedClipCount);
-            REPORTER_ASSERT(reporter, reducedGenID == testCases[i].reducedGenID);
-            SkASSERT(reducedGenID == testCases[i].reducedGenID);
-            REPORTER_ASSERT(reporter, initial == testCases[i].initialState);
-            SkASSERT(initial == testCases[i].initialState);
-            if (!testCases[i].tighterBounds.isEmpty()) {
-                REPORTER_ASSERT(reporter, tightBounds == testCases[i].tighterBounds);
-                SkASSERT(tightBounds == testCases[i].tighterBounds);
+            const GrReducedClip reduced(stack, testCases[i].testBounds);
+            REPORTER_ASSERT(reporter, reduced.elements().count() == testCases[i].reducedClipCount);
+            SkASSERT(reduced.elements().count() == testCases[i].reducedClipCount);
+            if (reduced.elements().count()) {
+                REPORTER_ASSERT(reporter, reduced.elementsGenID() == testCases[i].reducedGenID);
+                SkASSERT(reduced.elementsGenID() == testCases[i].reducedGenID);
             }
+            REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
+            SkASSERT(reduced.initialState() == testCases[i].initialState);
+            REPORTER_ASSERT(reporter, reduced.hasIBounds());
+            SkASSERT(reduced.hasIBounds());
+            REPORTER_ASSERT(reporter, reduced.ibounds() == testCases[i].clipIRect);
+            SkASSERT(reduced.ibounds() == testCases[i].clipIRect);
         }
     }
 }
 
 static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
     SkClipStack stack;
-    stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op);
-    stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), SkRegion::kReplace_Op);
-    SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100);
-
-    GrReducedClip::ElementList reducedClips;
-    int32_t reducedGenID;
-    GrReducedClip::InitialState initial;
-    SkIRect tightBounds;
+    stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
+    stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
+    SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
 
     // At the time, this would crash.
-    GrReducedClip::ReduceClipStack(stack,
-                                   inflatedIBounds,
-                                   &reducedClips,
-                                   &reducedGenID,
-                                   &initial,
-                                   &tightBounds);
+    const GrReducedClip reduced(stack, bounds);
+    REPORTER_ASSERT(reporter, reduced.elements().isEmpty());
+}
 
-    REPORTER_ASSERT(reporter, 0 == reducedClips.count());
+enum class ClipMethod {
+    kSkipDraw,
+    kIgnoreClip,
+    kScissor,
+    kAAElements
+};
+
+static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
+                          const SkClipStack& stack, const SkMatrix& queryXform,
+                          const SkRect& preXformQuery, ClipMethod expectedMethod,
+                          int numExpectedElems = 0) {
+    SkRect queryBounds;
+    queryXform.mapRect(&queryBounds, preXformQuery);
+    const GrReducedClip reduced(stack, queryBounds);
+
+    SkClipStack::BoundsType stackBoundsType;
+    SkRect stackBounds;
+    stack.getBounds(&stackBounds, &stackBoundsType);
+
+    switch (expectedMethod) {
+        case ClipMethod::kSkipDraw:
+            SkASSERT(0 == numExpectedElems);
+            REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
+            REPORTER_ASSERT_MESSAGE(reporter,
+                                    GrReducedClip::InitialState::kAllOut == reduced.initialState(),
+                                    testName.c_str());
+            return;
+        case ClipMethod::kIgnoreClip:
+            SkASSERT(0 == numExpectedElems);
+            REPORTER_ASSERT_MESSAGE(reporter,
+                                    !reduced.hasIBounds() ||
+                                    GrClip::IsInsideClip(reduced.ibounds(), queryBounds),
+                                    testName.c_str());
+            REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
+            REPORTER_ASSERT_MESSAGE(reporter,
+                                    GrReducedClip::InitialState::kAllIn == reduced.initialState(),
+                                    testName.c_str());
+            return;
+        case ClipMethod::kScissor: {
+            SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
+            SkASSERT(0 == numExpectedElems);
+            SkIRect expectedScissor;
+            stackBounds.round(&expectedScissor);
+            REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
+            REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
+            REPORTER_ASSERT_MESSAGE(reporter, expectedScissor == reduced.ibounds(),
+                                    testName.c_str());
+            REPORTER_ASSERT_MESSAGE(reporter,
+                                    GrReducedClip::InitialState::kAllIn == reduced.initialState(),
+                                    testName.c_str());
+            return;
+        }
+        case ClipMethod::kAAElements: {
+            SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
+            if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
+                SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
+            }
+            REPORTER_ASSERT_MESSAGE(reporter, numExpectedElems == reduced.elements().count(),
+                                    testName.c_str());
+            REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
+            REPORTER_ASSERT_MESSAGE(reporter, expectedClipIBounds == reduced.ibounds(),
+                                    testName.c_str());
+            REPORTER_ASSERT_MESSAGE(reporter, reduced.requiresAA() == !reduced.elements().isEmpty(),
+                                    testName.c_str());
+            break;
+        }
+    }
+}
+
+static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
+    constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7;         // Pixel aligned rect.
+    constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
+    constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
+
+    SkRect alignedRect = {IL, IT, IR, IB};
+    SkRect rect = {L, T, R, B};
+    SkRect innerRect = {l, t, r, b};
+
+    SkMatrix m;
+    m.setIdentity();
+
+    constexpr SkScalar kMinScale = 2.0001f;
+    constexpr SkScalar kMaxScale = 3;
+    constexpr int kNumIters = 8;
+
+    SkString name;
+    SkRandom rand;
+
+    for (int i = 0; i < kNumIters; ++i) {
+        // Pixel-aligned rect (iior=true).
+        name.printf("Pixel-aligned rect test, iter %i", i);
+        SkClipStack stack;
+        stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
+        test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
+        test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
+        test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
+
+        // Rect (iior=true).
+        name.printf("Rect test, iter %i", i);
+        stack.reset();
+        stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
+        test_aa_query(reporter, name, stack, m, {L, T,  R, B}, ClipMethod::kIgnoreClip);
+        test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
+        test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
+
+        // Difference rect (iior=false, inside-out bounds).
+        name.printf("Difference rect test, iter %i", i);
+        stack.reset();
+        stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
+        test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
+        test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
+        test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
+
+        // Complex clip (iior=false, normal bounds).
+        name.printf("Complex clip test, iter %i", i);
+        stack.reset();
+        stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
+        stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
+        test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
+        test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
+        test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
+        test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
+        test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
+        test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
+
+        // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
+        name.printf("Aligned Complex clip test, iter %i", i);
+        stack.reset();
+        stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
+        stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
+        test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
+        test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
+        test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
+        test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
+        test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
+        test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
+
+        // Apply random transforms and try again. This ensures the clip stack reduction is hardened
+        // against FP rounding error.
+        SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
+        sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
+        SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
+        sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
+        SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
+        SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
+
+        SkMatrix xform = SkMatrix::MakeScale(sx, sy);
+        xform.postTranslate(tx, ty);
+        xform.mapRect(&alignedRect);
+        xform.mapRect(&rect);
+        xform.mapRect(&innerRect);
+        m.postConcat(xform);
+    }
+}
+
+static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) {
+    // https://bugs.chromium.org/p/skia/issues/detail?id=5990
+    const SkRect clipBounds = SkRect::MakeXYWH(1.5f, 100, 1000, 1000);
+
+    SkClipStack rectStack;
+    rectStack.clipRect(clipBounds, SkMatrix::I(), kIntersect_SkClipOp, true);
+
+    SkPath clipPath;
+    clipPath.moveTo(clipBounds.left(), clipBounds.top());
+    clipPath.quadTo(clipBounds.right(), clipBounds.top(),
+                    clipBounds.right(), clipBounds.bottom());
+    clipPath.quadTo(clipBounds.left(), clipBounds.bottom(),
+                    clipBounds.left(), clipBounds.top());
+    SkClipStack pathStack;
+    pathStack.clipPath(clipPath, SkMatrix::I(), kIntersect_SkClipOp, true);
+
+    for (const SkClipStack& stack : {rectStack, pathStack}) {
+        for (SkRect queryBounds : {SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance, 1000),
+                                   SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance/2, 1000),
+                                   SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance),
+                                   SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance/2)}) {
+            const GrReducedClip reduced(stack, queryBounds);
+            REPORTER_ASSERT(reporter, !reduced.hasIBounds());
+            REPORTER_ASSERT(reporter, reduced.elements().isEmpty());
+            REPORTER_ASSERT(reporter,
+                            GrReducedClip::InitialState::kAllOut == reduced.initialState());
+        }
+    }
 }
 
 #endif
@@ -1182,7 +1403,7 @@
         { 0, 0, 75, 75 }
     };
     for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
-        stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op);
+        stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
     }
 
     // all of the above rects should have been intersected, leaving only 1 rect
@@ -1193,7 +1414,7 @@
 
     REPORTER_ASSERT(reporter, element);
     REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
-    REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == element->getOp());
+    REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
     REPORTER_ASSERT(reporter, element->getRect() == answer);
     // now check that we only had one in our iterator
     REPORTER_ASSERT(reporter, !iter.next());
@@ -1213,9 +1434,91 @@
     test_rect_inverse_fill(reporter);
     test_path_replace(reporter);
     test_quickContains(reporter);
+    test_invfill_diff_bug(reporter);
 #if SK_SUPPORT_GPU
     test_reduced_clip_stack(reporter);
     test_reduced_clip_stack_genid(reporter);
     test_reduced_clip_stack_no_aa_crash(reporter);
+    test_reduced_clip_stack_aa(reporter);
+    test_tiny_query_bounds_assertion_bug(reporter);
 #endif
 }
+
+//////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(GrContext* context) const {
+    const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), 0);
+    return this->createSoftwareClipMask(context, reducedClip);
+}
+
+// Verify that clip masks are freed up when the clip state that generated them goes away.
+DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
+    // This test uses resource key tags which only function in debug builds.
+#ifdef SK_DEBUG
+    GrContext* context = ctxInfo.grContext();
+    SkClipStack stack;
+
+    SkPath path;
+    path.addCircle(10, 10, 8);
+    path.addCircle(15, 15, 8);
+    path.setFillType(SkPath::kEvenOdd_FillType);
+
+    static const char* kTag = GrClipStackClip::kMaskTestTag;
+    GrResourceCache* cache = context->getResourceCache();
+
+    static constexpr int kN = 5;
+
+    for (int i = 0; i < kN; ++i) {
+        SkMatrix m;
+        m.setTranslate(0.5, 0.5);
+        stack.save();
+        stack.clipPath(path, m, SkClipOp::kIntersect, true);
+        sk_sp<GrTextureProxy> mask = GrClipStackClip(&stack).testingOnly_createClipMask(context);
+        mask->instantiate(context->resourceProvider());
+        GrTexture* tex = mask->priv().peekTexture();
+        REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
+        // Make sure mask isn't pinned in cache.
+        mask.reset(nullptr);
+        context->flush();
+        REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
+    }
+
+    for (int i = 0; i < kN; ++i) {
+        stack.restore();
+        cache->purgeAsNeeded();
+        REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
+    }
+#endif
+}
+
+#include "SkSurface.h"
+DEF_GPUTEST_FOR_ALL_CONTEXTS(canvas_private_clipRgn, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+
+    const int w = 10;
+    const int h = 10;
+    SkImageInfo info = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+    sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
+    SkCanvas* canvas = surf->getCanvas();
+    SkRegion rgn;
+
+    canvas->temporary_internal_getRgnClip(&rgn);
+    REPORTER_ASSERT(reporter, rgn.isRect());
+    REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
+
+    canvas->save();
+    canvas->clipRect(SkRect::MakeWH(5, 5), kDifference_SkClipOp);
+    canvas->temporary_internal_getRgnClip(&rgn);
+    REPORTER_ASSERT(reporter, rgn.isComplex());
+    REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
+    canvas->restore();
+
+    canvas->save();
+    canvas->clipRRect(SkRRect::MakeOval(SkRect::MakeLTRB(3, 3, 7, 7)));
+    canvas->temporary_internal_getRgnClip(&rgn);
+    REPORTER_ASSERT(reporter, rgn.isComplex());
+    REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeLTRB(3, 3, 7, 7));
+    canvas->restore();
+}
+#endif
diff --git a/src/third_party/skia/tests/ClipperTest.cpp b/src/third_party/skia/tests/ClipperTest.cpp
index 00b6229..641ed01 100644
--- a/src/third_party/skia/tests/ClipperTest.cpp
+++ b/src/third_party/skia/tests/ClipperTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkEdgeClipper.h"
 #include "SkLineClipper.h"
@@ -48,7 +49,7 @@
 }
 
 static void test_edgeclipper() {
-    SkEdgeClipper clipper;
+    SkEdgeClipper clipper(false);
 
     const SkPoint pts[] = {
         { 3.0995476e+010f,  42.929779f },
diff --git a/src/third_party/skia/tests/CodecAnimTest.cpp b/src/third_party/skia/tests/CodecAnimTest.cpp
new file mode 100644
index 0000000..84dc9fa
--- /dev/null
+++ b/src/third_party/skia/tests/CodecAnimTest.cpp
@@ -0,0 +1,389 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkCodec.h"
+#include "SkCommonFlags.h"
+#include "SkImageEncoder.h"
+#include "SkOSPath.h"
+#include "SkStream.h"
+
+#include "Resources.h"
+#include "Test.h"
+#include "sk_tool_utils.h"
+
+#include <initializer_list>
+#include <vector>
+
+static void write_bm(const char* name, const SkBitmap& bm) {
+    if (FLAGS_writePath.isEmpty()) {
+        return;
+    }
+
+    SkString filename = SkOSPath::Join(FLAGS_writePath[0], name);
+    filename.appendf(".png");
+    SkFILEWStream file(filename.c_str());
+    if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) {
+        SkDebugf("failed to write '%s'\n", filename.c_str());
+    }
+}
+
+DEF_TEST(Codec_trunc, r) {
+    sk_sp<SkData> data(GetResourceAsData("box.gif"));
+    if (!data) {
+        return;
+    }
+    data = SkData::MakeSubset(data.get(), 0, 23);
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
+    codec->getFrameInfo();
+}
+
+// 565 does not support alpha, but there is no reason for it not to support an
+// animated image with a frame that has alpha but then blends onto an opaque
+// frame making the result opaque. Test that we can decode such a frame.
+DEF_TEST(Codec_565, r) {
+    sk_sp<SkData> data(GetResourceAsData("blendBG.webp"));
+    if (!data) {
+        return;
+    }
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(std::move(data)));
+    auto info = codec->getInfo().makeColorType(kRGB_565_SkColorType);
+    SkBitmap bm;
+    bm.allocPixels(info);
+
+    SkCodec::Options options;
+    options.fFrameIndex = 1;
+    options.fPriorFrame = SkCodec::kNone;
+
+    const auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(),
+                                         &options);
+    REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+}
+
+static bool restore_previous(const SkCodec::FrameInfo& info) {
+    return info.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious;
+}
+
+DEF_TEST(Codec_frames, r) {
+    #define kOpaque         kOpaque_SkAlphaType
+    #define kUnpremul       kUnpremul_SkAlphaType
+    #define kKeep           SkCodecAnimation::DisposalMethod::kKeep
+    #define kRestoreBG      SkCodecAnimation::DisposalMethod::kRestoreBGColor
+    #define kRestorePrev    SkCodecAnimation::DisposalMethod::kRestorePrevious
+    static const struct {
+        const char*                                   fName;
+        int                                           fFrameCount;
+        // One less than fFramecount, since the first frame is always
+        // independent.
+        std::vector<int>                              fRequiredFrames;
+        // Same, since the first frame should match getInfo.
+        std::vector<SkAlphaType>                      fAlphaTypes;
+        // The size of this one should match fFrameCount for animated, empty
+        // otherwise.
+        std::vector<int>                              fDurations;
+        int                                           fRepetitionCount;
+        std::vector<SkCodecAnimation::DisposalMethod> fDisposalMethods;
+    } gRecs[] = {
+        { "required.gif", 7,
+            { 0, 1, 1, SkCodec::kNone, 4, 4 },
+            { kOpaque, kUnpremul, kUnpremul, kOpaque, kOpaque, kOpaque },
+            { 100, 100, 100, 100, 100, 100, 100 },
+            0,
+            { kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep } },
+        { "alphabetAnim.gif", 13,
+            { SkCodec::kNone, 0, 0, 0, 0, 5, 6, SkCodec::kNone,
+              SkCodec::kNone, SkCodec::kNone, 10, 11 },
+            { kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul,
+              kUnpremul, kUnpremul, kUnpremul, kOpaque, kOpaque, kUnpremul },
+            { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 },
+            0,
+            { kKeep, kRestorePrev, kRestorePrev, kRestorePrev, kRestorePrev,
+              kRestoreBG, kKeep, kRestoreBG, kRestoreBG, kKeep, kKeep,
+              kRestoreBG, kKeep } },
+        { "randPixelsAnim2.gif", 4,
+            // required frames
+            { 0, 0, 1 },
+            // alphas
+            { kOpaque, kOpaque, kOpaque },
+            // durations
+            { 0, 1000, 170, 40 },
+            // repetition count
+            0,
+            { kKeep, kKeep, kRestorePrev, kKeep } },
+        { "randPixelsAnim.gif", 13,
+            // required frames
+            { SkCodec::kNone, 1, 2, 3, 4, 3, 6, 7, 7, 7, 9, 9 },
+            { kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul,
+              kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul },
+            // durations
+            { 0, 1000, 170, 40, 220, 7770, 90, 90, 90, 90, 90, 90, 90 },
+            // repetition count
+            0,
+            { kKeep, kKeep, kKeep, kKeep, kRestoreBG, kRestoreBG, kRestoreBG,
+              kRestoreBG, kRestorePrev, kRestoreBG, kRestorePrev, kRestorePrev,
+              kRestorePrev,  } },
+        { "box.gif", 1, {}, {}, {}, 0, { kKeep } },
+        { "color_wheel.gif", 1, {}, {}, {}, 0, { kKeep } },
+        { "test640x479.gif", 4, { 0, 1, 2 },
+                { kOpaque, kOpaque, kOpaque },
+                { 200, 200, 200, 200 },
+                SkCodec::kRepetitionCountInfinite,
+                { kKeep, kKeep, kKeep, kKeep } },
+        { "colorTables.gif", 2, { 0 }, { kOpaque }, { 1000, 1000 }, 5,
+                { kKeep, kKeep } },
+
+        { "arrow.png",  1, {}, {}, {}, 0, {} },
+        { "google_chrome.ico", 1, {}, {}, {}, 0, {} },
+        { "brickwork-texture.jpg", 1, {}, {}, {}, 0, {} },
+#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
+        { "dng_with_preview.dng", 1, {}, {}, {}, 0, {} },
+#endif
+        { "mandrill.wbmp", 1, {}, {}, {}, 0, {} },
+        { "randPixels.bmp", 1, {}, {}, {}, 0, {} },
+        { "yellow_rose.webp", 1, {}, {}, {}, 0, {} },
+        { "webp-animated.webp", 3, { 0, 1 }, { kOpaque, kOpaque },
+            { 1000, 500, 1000 }, SkCodec::kRepetitionCountInfinite,
+            { kKeep, kKeep, kKeep } },
+        { "blendBG.webp", 7, { 0, SkCodec::kNone, SkCodec::kNone, SkCodec::kNone,
+                               4, 4 },
+            { kOpaque, kOpaque, kUnpremul, kOpaque, kUnpremul, kUnpremul },
+            { 525, 500, 525, 437, 609, 729, 444 }, 7,
+            { kKeep, kKeep, kKeep, kKeep, kKeep, kKeep, kKeep } },
+        { "required.webp", 7,
+            { 0, 1, 1, SkCodec::kNone, 4, 4 },
+            { kOpaque, kUnpremul, kUnpremul, kOpaque, kOpaque, kOpaque },
+            { 100, 100, 100, 100, 100, 100, 100 },
+            1,
+            { kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep } },
+    };
+    #undef kOpaque
+    #undef kUnpremul
+    #undef kKeep
+    #undef kRestorePrev
+    #undef kRestoreBG
+
+    for (const auto& rec : gRecs) {
+        sk_sp<SkData> data(GetResourceAsData(rec.fName));
+        if (!data) {
+            // Useful error statement, but sometimes people run tests without
+            // resources, and they do not want to see these messages.
+            //ERRORF(r, "Missing resources? Could not find '%s'", rec.fName);
+            continue;
+        }
+
+        std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
+        if (!codec) {
+            ERRORF(r, "Failed to create an SkCodec from '%s'", rec.fName);
+            continue;
+        }
+
+        {
+            SkCodec::FrameInfo frameInfo;
+            REPORTER_ASSERT(r, !codec->getFrameInfo(0, &frameInfo));
+        }
+
+        const int repetitionCount = codec->getRepetitionCount();
+        if (repetitionCount != rec.fRepetitionCount) {
+            ERRORF(r, "%s repetition count does not match! expected: %i\tactual: %i",
+                      rec.fName, rec.fRepetitionCount, repetitionCount);
+        }
+
+        const int expected = rec.fFrameCount;
+        if (rec.fRequiredFrames.size() + 1 != static_cast<size_t>(expected)) {
+            ERRORF(r, "'%s' has wrong number entries in fRequiredFrames; expected: %i\tactual: %i",
+                   rec.fName, expected - 1, rec.fRequiredFrames.size());
+            continue;
+        }
+
+        if (expected > 1) {
+            if (rec.fDurations.size() != static_cast<size_t>(expected)) {
+                ERRORF(r, "'%s' has wrong number entries in fDurations; expected: %i\tactual: %i",
+                       rec.fName, expected, rec.fDurations.size());
+                continue;
+            }
+
+            if (rec.fAlphaTypes.size() + 1 != static_cast<size_t>(expected)) {
+                ERRORF(r, "'%s' has wrong number entries in fAlphaTypes; expected: %i\tactual: %i",
+                       rec.fName, expected - 1, rec.fAlphaTypes.size());
+                continue;
+            }
+
+            if (rec.fDisposalMethods.size() != static_cast<size_t>(expected)) {
+                ERRORF(r, "'%s' has wrong number entries in fDisposalMethods; "
+                       "expected %i\tactual: %i",
+                       rec.fName, expected, rec.fDisposalMethods.size());
+                continue;
+            }
+        }
+
+        enum class TestMode {
+            kVector,
+            kIndividual,
+        };
+
+        for (auto mode : { TestMode::kVector, TestMode::kIndividual }) {
+            // Re-create the codec to reset state and test parsing.
+            codec.reset(SkCodec::NewFromData(data));
+
+            int frameCount;
+            std::vector<SkCodec::FrameInfo> frameInfos;
+            switch (mode) {
+                case TestMode::kVector:
+                    frameInfos = codec->getFrameInfo();
+                    // getFrameInfo returns empty set for non-animated.
+                    frameCount = frameInfos.empty() ? 1 : frameInfos.size();
+                    break;
+                case TestMode::kIndividual:
+                    frameCount = codec->getFrameCount();
+                    break;
+            }
+
+            if (frameCount != expected) {
+                ERRORF(r, "'%s' expected frame count: %i\tactual: %i",
+                       rec.fName, expected, frameCount);
+                continue;
+            }
+
+            // From here on, we are only concerned with animated images.
+            if (1 == frameCount) {
+                continue;
+            }
+
+            for (int i = 0; i < frameCount; i++) {
+                SkCodec::FrameInfo frameInfo;
+                switch (mode) {
+                    case TestMode::kVector:
+                        frameInfo = frameInfos[i];
+                        break;
+                    case TestMode::kIndividual:
+                        REPORTER_ASSERT(r, codec->getFrameInfo(i, nullptr));
+                        REPORTER_ASSERT(r, codec->getFrameInfo(i, &frameInfo));
+                        break;
+                }
+
+                if (rec.fDurations[i] != frameInfo.fDuration) {
+                    ERRORF(r, "%s frame %i's durations do not match! expected: %i\tactual: %i",
+                           rec.fName, i, rec.fDurations[i], frameInfo.fDuration);
+                }
+
+                auto to_string = [](SkAlphaType type) {
+                    switch (type) {
+                        case kUnpremul_SkAlphaType:
+                            return "unpremul";
+                        case kOpaque_SkAlphaType:
+                            return "opaque";
+                        default:
+                            return "other";
+                    }
+                };
+
+                auto expectedAlpha = 0 == i ? codec->getInfo().alphaType() : rec.fAlphaTypes[i-1];
+                auto alpha = frameInfo.fAlphaType;
+                if (expectedAlpha != alpha) {
+                    ERRORF(r, "%s's frame %i has wrong alpha type! expected: %s\tactual: %s",
+                           rec.fName, i, to_string(expectedAlpha), to_string(alpha));
+                }
+
+                if (0 == i) {
+                    REPORTER_ASSERT(r, frameInfo.fRequiredFrame == SkCodec::kNone);
+                } else if (rec.fRequiredFrames[i-1] != frameInfo.fRequiredFrame) {
+                    ERRORF(r, "%s's frame %i has wrong dependency! expected: %i\tactual: %i",
+                           rec.fName, i, rec.fRequiredFrames[i-1], frameInfo.fRequiredFrame);
+                }
+
+                REPORTER_ASSERT(r, frameInfo.fDisposalMethod == rec.fDisposalMethods[i]);
+            }
+
+            if (TestMode::kIndividual == mode) {
+                // No need to test decoding twice.
+                continue;
+            }
+
+            // Compare decoding in multiple ways:
+            // - Start from scratch for each frame. |codec| will have to decode the required frame
+            //   (and any it depends on) to decode. This is stored in |cachedFrames|.
+            // - Provide the frame that a frame depends on, so |codec| just has to blend.
+            // - Provide a frame after the required frame, which will be covered up by the newest
+            //   frame.
+            // All should look the same.
+            std::vector<SkBitmap> cachedFrames(frameCount);
+            const auto info = codec->getInfo().makeColorType(kN32_SkColorType);
+
+            auto decode = [&](SkBitmap* bm, int index, int cachedIndex) {
+                auto decodeInfo = info;
+                if (index > 0) {
+                    decodeInfo = info.makeAlphaType(frameInfos[index].fAlphaType);
+                }
+                bm->allocPixels(decodeInfo);
+                if (cachedIndex != SkCodec::kNone) {
+                    // First copy the pixels from the cached frame
+                    const bool success = sk_tool_utils::copy_to(bm, kN32_SkColorType,
+                            cachedFrames[cachedIndex]);
+                    REPORTER_ASSERT(r, success);
+                }
+                SkCodec::Options opts;
+                opts.fFrameIndex = index;
+                opts.fPriorFrame = cachedIndex;
+                const auto result = codec->getPixels(decodeInfo, bm->getPixels(), bm->rowBytes(),
+                                                     &opts);
+                if (cachedIndex != SkCodec::kNone && restore_previous(frameInfos[cachedIndex])) {
+                    if (result == SkCodec::kInvalidParameters) {
+                        return true;
+                    }
+                    ERRORF(r, "Using a kRestorePrevious frame as fPriorFrame should fail");
+                    return false;
+                }
+                if (result != SkCodec::kSuccess) {
+                    ERRORF(r, "Failed to decode frame %i from %s when providing prior frame %i, "
+                              "error %i", index, rec.fName, cachedIndex, result);
+                }
+                return result == SkCodec::kSuccess;
+            };
+
+            for (int i = 0; i < frameCount; i++) {
+                SkBitmap& cachedFrame = cachedFrames[i];
+                if (!decode(&cachedFrame, i, SkCodec::kNone)) {
+                    continue;
+                }
+                const auto reqFrame = frameInfos[i].fRequiredFrame;
+                if (reqFrame == SkCodec::kNone) {
+                    // Nothing to compare against.
+                    continue;
+                }
+                for (int j = reqFrame; j < i; j++) {
+                    SkBitmap frame;
+                    if (restore_previous(frameInfos[j])) {
+                        (void) decode(&frame, i, j);
+                        continue;
+                    }
+                    if (!decode(&frame, i, j)) {
+                        continue;
+                    }
+
+                    // Now verify they're equal.
+                    const size_t rowLen = info.bytesPerPixel() * info.width();
+                    for (int y = 0; y < info.height(); y++) {
+                        const void* cachedAddr = cachedFrame.getAddr(0, y);
+                        SkASSERT(cachedAddr != nullptr);
+                        const void* addr = frame.getAddr(0, y);
+                        SkASSERT(addr != nullptr);
+                        const bool lineMatches = memcmp(cachedAddr, addr, rowLen) == 0;
+                        if (!lineMatches) {
+                            SkString name = SkStringPrintf("cached_%i", i);
+                            write_bm(name.c_str(), cachedFrame);
+                            name = SkStringPrintf("frame_%i", i);
+                            write_bm(name.c_str(), frame);
+                            ERRORF(r, "%s's frame %i is different (starting from line %i) when "
+                                      "providing prior frame %i!", rec.fName, i, y, j);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/third_party/skia/tests/CodecExactReadTest.cpp b/src/third_party/skia/tests/CodecExactReadTest.cpp
new file mode 100644
index 0000000..9b6acc8
--- /dev/null
+++ b/src/third_party/skia/tests/CodecExactReadTest.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Resources.h"
+#include "Test.h"
+
+#include "SkBitmap.h"
+#include "SkCodec.h"
+#include "SkData.h"
+#include "SkStream.h"
+
+namespace {
+// This class wraps another SkStream. It does not own the underlying stream, so
+// that the underlying stream can be reused starting from where the first
+// client left off. This mimics Android's JavaInputStreamAdaptor.
+class UnowningStream : public SkStream {
+public:
+    explicit UnowningStream(SkStream* stream)
+        : fStream(stream)
+    {}
+
+    size_t read(void* buf, size_t bytes) override {
+        return fStream->read(buf, bytes);
+    }
+
+    bool rewind() override {
+        return fStream->rewind();
+    }
+
+    bool isAtEnd() const override {
+        return fStream->isAtEnd();
+    }
+private:
+    SkStream* fStream; // Unowned.
+};
+} // namespace
+
+// Test that some SkCodecs do not attempt to read input beyond the logical
+// end of the data. Some other SkCodecs do, but some Android apps rely on not
+// doing so for PNGs. Test on other formats that work.
+DEF_TEST(Codec_end, r) {
+    for (const char* path : { "plane.png",
+                              "yellow_rose.png",
+                              "plane_interlaced.png",
+                              "google_chrome.ico",
+                              "color_wheel.ico",
+                              "mandrill.wbmp",
+                              "randPixels.bmp",
+                              }) {
+        sk_sp<SkData> data = GetResourceAsData(path);
+        if (!data) {
+            continue;
+        }
+
+        const int kNumImages = 2;
+        const size_t size = data->size();
+        sk_sp<SkData> multiData = SkData::MakeUninitialized(size * kNumImages);
+        void* dst = multiData->writable_data();
+        for (int i = 0; i < kNumImages; i++) {
+            memcpy(SkTAddOffset<void>(dst, size * i), data->data(), size);
+        }
+        data.reset();
+
+        SkMemoryStream stream(std::move(multiData));
+        for (int i = 0; i < kNumImages; ++i) {
+            std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(new UnowningStream(&stream)));
+            if (!codec) {
+                ERRORF(r, "Failed to create a codec from %s, iteration %i", path, i);
+                continue;
+            }
+
+            auto info = codec->getInfo().makeColorType(kN32_SkColorType);
+            SkBitmap bm;
+            bm.allocPixels(info);
+
+            auto result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes());
+            if (result != SkCodec::kSuccess) {
+                ERRORF(r, "Failed to getPixels from %s, iteration %i error %i", path, i, result);
+                continue;
+            }
+        }
+    }
+}
diff --git a/src/third_party/skia/tests/CodecPartialTest.cpp b/src/third_party/skia/tests/CodecPartialTest.cpp
new file mode 100644
index 0000000..4a56f46
--- /dev/null
+++ b/src/third_party/skia/tests/CodecPartialTest.cpp
@@ -0,0 +1,456 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkCodec.h"
+#include "SkData.h"
+#include "SkImageInfo.h"
+#include "SkRWBuffer.h"
+#include "SkString.h"
+
+#include "FakeStreams.h"
+#include "Resources.h"
+#include "Test.h"
+
+static SkImageInfo standardize_info(SkCodec* codec) {
+    SkImageInfo defaultInfo = codec->getInfo();
+    // Note: This drops the SkColorSpace, allowing the equality check between two
+    // different codecs created from the same file to have the same SkImageInfo.
+    return SkImageInfo::MakeN32Premul(defaultInfo.width(), defaultInfo.height());
+}
+
+static bool create_truth(sk_sp<SkData> data, SkBitmap* dst) {
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(std::move(data)));
+    if (!codec) {
+        return false;
+    }
+
+    const SkImageInfo info = standardize_info(codec.get());
+    dst->allocPixels(info);
+    return SkCodec::kSuccess == codec->getPixels(info, dst->getPixels(), dst->rowBytes());
+}
+
+static void compare_bitmaps(skiatest::Reporter* r, const SkBitmap& bm1, const SkBitmap& bm2) {
+    const SkImageInfo& info = bm1.info();
+    if (info != bm2.info()) {
+        ERRORF(r, "Bitmaps have different image infos!");
+        return;
+    }
+    const size_t rowBytes = info.minRowBytes();
+    for (int i = 0; i < info.height(); i++) {
+        REPORTER_ASSERT(r, !memcmp(bm1.getAddr(0, 0), bm2.getAddr(0, 0), rowBytes));
+    }
+}
+
+static void test_partial(skiatest::Reporter* r, const char* name, size_t minBytes = 0) {
+    sk_sp<SkData> file = GetResourceAsData(name);
+    if (!file) {
+        SkDebugf("missing resource %s\n", name);
+        return;
+    }
+
+    SkBitmap truth;
+    if (!create_truth(file, &truth)) {
+        ERRORF(r, "Failed to decode %s\n", name);
+        return;
+    }
+
+    // Now decode part of the file
+    HaltingStream* stream = new HaltingStream(file, SkTMax(file->size() / 2, minBytes));
+
+    // Note that we cheat and hold on to a pointer to stream, though it is owned by
+    // partialCodec.
+    std::unique_ptr<SkCodec> partialCodec(SkCodec::NewFromStream(stream));
+    if (!partialCodec) {
+        // Technically, this could be a small file where half the file is not
+        // enough.
+        ERRORF(r, "Failed to create codec for %s", name);
+        return;
+    }
+
+    const SkImageInfo info = standardize_info(partialCodec.get());
+    SkASSERT(info == truth.info());
+    SkBitmap incremental;
+    incremental.allocPixels(info);
+
+    while (true) {
+        const SkCodec::Result startResult = partialCodec->startIncrementalDecode(info,
+                incremental.getPixels(), incremental.rowBytes());
+        if (startResult == SkCodec::kSuccess) {
+            break;
+        }
+
+        if (stream->isAllDataReceived()) {
+            ERRORF(r, "Failed to start incremental decode\n");
+            return;
+        }
+
+        // Append some data. The size is arbitrary, but deliberately different from
+        // the buffer size used by SkPngCodec.
+        stream->addNewData(1000);
+    }
+
+    while (true) {
+        const SkCodec::Result result = partialCodec->incrementalDecode();
+
+        if (result == SkCodec::kSuccess) {
+            break;
+        }
+
+        REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
+
+        if (stream->isAllDataReceived()) {
+            ERRORF(r, "Failed to completely decode %s", name);
+            return;
+        }
+
+        // Append some data. The size is arbitrary, but deliberately different from
+        // the buffer size used by SkPngCodec.
+        stream->addNewData(1000);
+    }
+
+    // compare to original
+    compare_bitmaps(r, truth, incremental);
+}
+
+DEF_TEST(Codec_partial, r) {
+#if 0
+    // FIXME (scroggo): SkPngCodec needs to use SkStreamBuffer in order to
+    // support incremental decoding.
+    test_partial(r, "plane.png");
+    test_partial(r, "plane_interlaced.png");
+    test_partial(r, "yellow_rose.png");
+    test_partial(r, "index8.png");
+    test_partial(r, "color_wheel.png");
+    test_partial(r, "mandrill_256.png");
+    test_partial(r, "mandrill_32.png");
+    test_partial(r, "arrow.png");
+    test_partial(r, "randPixels.png");
+    test_partial(r, "baby_tux.png");
+#endif
+    test_partial(r, "box.gif");
+    test_partial(r, "randPixels.gif", 215);
+    test_partial(r, "color_wheel.gif");
+}
+
+// Verify that when decoding an animated gif byte by byte we report the correct
+// fRequiredFrame as soon as getFrameInfo reports the frame.
+DEF_TEST(Codec_requiredFrame, r) {
+    auto path = "colorTables.gif";
+    sk_sp<SkData> file = GetResourceAsData(path);
+    if (!file) {
+        return;
+    }
+
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(file));
+    if (!codec) {
+        ERRORF(r, "Failed to create codec from %s", path);
+        return;
+    }
+
+    auto frameInfo = codec->getFrameInfo();
+    if (frameInfo.size() <= 1) {
+        ERRORF(r, "Test is uninteresting with 0 or 1 frames");
+        return;
+    }
+
+    HaltingStream* stream(nullptr);
+    std::unique_ptr<SkCodec> partialCodec(nullptr);
+    for (size_t i = 0; !partialCodec; i++) {
+        if (file->size() == i) {
+            ERRORF(r, "Should have created a partial codec for %s", path);
+            return;
+        }
+        stream = new HaltingStream(file, i);
+        partialCodec.reset(SkCodec::NewFromStream(stream));
+    }
+
+    std::vector<SkCodec::FrameInfo> partialInfo;
+    size_t frameToCompare = 0;
+    for (; stream->getLength() <= file->size(); stream->addNewData(1)) {
+        partialInfo = partialCodec->getFrameInfo();
+        for (; frameToCompare < partialInfo.size(); frameToCompare++) {
+            REPORTER_ASSERT(r, partialInfo[frameToCompare].fRequiredFrame
+                                == frameInfo[frameToCompare].fRequiredFrame);
+        }
+
+        if (frameToCompare == frameInfo.size()) {
+            break;
+        }
+    }
+}
+
+DEF_TEST(Codec_partialAnim, r) {
+    auto path = "test640x479.gif";
+    sk_sp<SkData> file = GetResourceAsData(path);
+    if (!file) {
+        return;
+    }
+
+    // This stream will be owned by fullCodec, but we hang on to the pointer
+    // to determine frame offsets.
+    SkStream* stream = new SkMemoryStream(file);
+    std::unique_ptr<SkCodec> fullCodec(SkCodec::NewFromStream(stream));
+    const auto info = standardize_info(fullCodec.get());
+
+    // frameByteCounts stores the number of bytes to decode a particular frame.
+    // - [0] is the number of bytes for the header
+    // - frames[i] requires frameByteCounts[i+1] bytes to decode
+    const std::vector<size_t> frameByteCounts = { 455, 69350, 1344, 1346, 1327 };
+    std::vector<SkBitmap> frames;
+    for (size_t i = 0; true; i++) {
+        SkBitmap frame;
+        frame.allocPixels(info);
+
+        SkCodec::Options opts;
+        opts.fFrameIndex = i;
+        const SkCodec::Result result = fullCodec->getPixels(info, frame.getPixels(),
+                frame.rowBytes(), &opts);
+
+        if (result == SkCodec::kIncompleteInput || result == SkCodec::kInvalidInput) {
+            // We need to distinguish between a partial frame and no more frames.
+            // getFrameInfo lets us do this, since it tells the number of frames
+            // not considering whether they are complete.
+            // FIXME: Should we use a different Result?
+            if (fullCodec->getFrameInfo().size() > i) {
+                // This is a partial frame.
+                frames.push_back(frame);
+            }
+            break;
+        }
+
+        if (result != SkCodec::kSuccess) {
+            ERRORF(r, "Failed to decode frame %i from %s", i, path);
+            return;
+        }
+
+        frames.push_back(frame);
+    }
+
+    // Now decode frames partially, then completely, and compare to the original.
+    HaltingStream* haltingStream = new HaltingStream(file, frameByteCounts[0]);
+    std::unique_ptr<SkCodec> partialCodec(SkCodec::NewFromStream(haltingStream));
+    if (!partialCodec) {
+        ERRORF(r, "Failed to create a partial codec from %s with %i bytes out of %i",
+               path, frameByteCounts[0], file->size());
+        return;
+    }
+
+    SkASSERT(frameByteCounts.size() > frames.size());
+    for (size_t i = 0; i < frames.size(); i++) {
+        const size_t fullFrameBytes = frameByteCounts[i + 1];
+        const size_t firstHalf = fullFrameBytes / 2;
+        const size_t secondHalf = fullFrameBytes - firstHalf;
+
+        haltingStream->addNewData(firstHalf);
+        auto frameInfo = partialCodec->getFrameInfo();
+        REPORTER_ASSERT(r, frameInfo.size() == i + 1);
+        REPORTER_ASSERT(r, !frameInfo[i].fFullyReceived);
+
+        SkBitmap frame;
+        frame.allocPixels(info);
+
+        SkCodec::Options opts;
+        opts.fFrameIndex = i;
+        SkCodec::Result result = partialCodec->startIncrementalDecode(info,
+                frame.getPixels(), frame.rowBytes(), &opts);
+        if (result != SkCodec::kSuccess) {
+            ERRORF(r, "Failed to start incremental decode for %s on frame %i",
+                   path, i);
+            return;
+        }
+
+        result = partialCodec->incrementalDecode();
+        REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
+
+        haltingStream->addNewData(secondHalf);
+        result = partialCodec->incrementalDecode();
+        REPORTER_ASSERT(r, SkCodec::kSuccess == result);
+
+        frameInfo = partialCodec->getFrameInfo();
+        REPORTER_ASSERT(r, frameInfo.size() == i + 1);
+        REPORTER_ASSERT(r, frameInfo[i].fFullyReceived);
+        compare_bitmaps(r, frames[i], frame);
+    }
+}
+
+// Test that calling getPixels when an incremental decode has been
+// started (but not finished) makes the next call to incrementalDecode
+// require a call to startIncrementalDecode.
+static void test_interleaved(skiatest::Reporter* r, const char* name) {
+    sk_sp<SkData> file = GetResourceAsData(name);
+    if (!file) {
+        return;
+    }
+    const size_t halfSize = file->size() / 2;
+    std::unique_ptr<SkCodec> partialCodec(SkCodec::NewFromStream(
+            new HaltingStream(std::move(file), halfSize)));
+    if (!partialCodec) {
+        ERRORF(r, "Failed to create codec for %s", name);
+        return;
+    }
+
+    const SkImageInfo info = standardize_info(partialCodec.get());
+    SkBitmap incremental;
+    incremental.allocPixels(info);
+
+    const SkCodec::Result startResult = partialCodec->startIncrementalDecode(info,
+            incremental.getPixels(), incremental.rowBytes());
+    if (startResult != SkCodec::kSuccess) {
+        ERRORF(r, "Failed to start incremental decode\n");
+        return;
+    }
+
+    SkCodec::Result result = partialCodec->incrementalDecode();
+    REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
+
+    SkBitmap full;
+    full.allocPixels(info);
+    result = partialCodec->getPixels(info, full.getPixels(), full.rowBytes());
+    REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
+
+    // Now incremental decode will fail
+    result = partialCodec->incrementalDecode();
+    REPORTER_ASSERT(r, result == SkCodec::kInvalidParameters);
+}
+
+DEF_TEST(Codec_rewind, r) {
+    test_interleaved(r, "plane.png");
+    test_interleaved(r, "plane_interlaced.png");
+    test_interleaved(r, "box.gif");
+}
+
+// Modified version of the giflib logo, from
+// http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html
+// The global color map has been replaced with a local color map.
+static unsigned char gNoGlobalColorMap[] = {
+  // Header
+  0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
+
+  // Logical screen descriptor
+  0x0A, 0x00, 0x0A, 0x00, 0x11, 0x00, 0x00,
+
+  // Image descriptor
+  0x2C, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x81,
+
+  // Local color table
+  0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
+
+  // Image data
+  0x02, 0x16, 0x8C, 0x2D, 0x99, 0x87, 0x2A, 0x1C, 0xDC, 0x33, 0xA0, 0x02, 0x75,
+  0xEC, 0x95, 0xFA, 0xA8, 0xDE, 0x60, 0x8C, 0x04, 0x91, 0x4C, 0x01, 0x00,
+
+  // Trailer
+  0x3B,
+};
+
+// Test that a gif file truncated before its local color map behaves as expected.
+DEF_TEST(Codec_GifPreMap, r) {
+    sk_sp<SkData> data = SkData::MakeWithoutCopy(gNoGlobalColorMap, sizeof(gNoGlobalColorMap));
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
+    if (!codec) {
+        ERRORF(r, "failed to create codec");
+        return;
+    }
+
+    SkBitmap truth;
+    auto info = standardize_info(codec.get());
+    truth.allocPixels(info);
+
+    auto result = codec->getPixels(info, truth.getPixels(), truth.rowBytes());
+    REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+    // Truncate to 23 bytes, just before the color map. This should fail to decode.
+    codec.reset(SkCodec::NewFromData(SkData::MakeWithoutCopy(gNoGlobalColorMap, 23)));
+    REPORTER_ASSERT(r, codec);
+    if (codec) {
+        SkBitmap bm;
+        bm.allocPixels(info);
+        result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
+        REPORTER_ASSERT(r, result == SkCodec::kInvalidInput);
+    }
+
+    // Again, truncate to 23 bytes, this time for an incremental decode. We
+    // cannot start an incremental decode until we have more data. If we did,
+    // we would be using the wrong color table.
+    HaltingStream* stream = new HaltingStream(data, 23);
+    codec.reset(SkCodec::NewFromStream(stream));
+    REPORTER_ASSERT(r, codec);
+    if (codec) {
+        SkBitmap bm;
+        bm.allocPixels(info);
+        result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
+        REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
+
+        stream->addNewData(data->size());
+        result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
+        REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+        result = codec->incrementalDecode();
+        REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+        compare_bitmaps(r, truth, bm);
+    }
+}
+
+DEF_TEST(Codec_emptyIDAT, r) {
+    const char* name = "baby_tux.png";
+    sk_sp<SkData> file = GetResourceAsData(name);
+    if (!file) {
+        return;
+    }
+
+    // Truncate to the beginning of the IDAT, immediately after the IDAT tag.
+    file = SkData::MakeSubset(file.get(), 0, 80);
+
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(std::move(file)));
+    if (!codec) {
+        ERRORF(r, "Failed to create a codec for %s", name);
+        return;
+    }
+
+    SkBitmap bm;
+    const auto info = standardize_info(codec.get());
+    bm.allocPixels(info);
+
+    const auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
+    REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
+}
+
+DEF_TEST(Codec_incomplete, r) {
+    for (const char* name : { "baby_tux.png",
+                              "baby_tux.webp",
+                              "CMYK.jpg",
+                              "color_wheel.gif",
+                              "google_chrome.ico",
+                              "rle.bmp",
+                              "mandrill.wbmp",
+                              }) {
+        sk_sp<SkData> file = GetResourceAsData(name);
+        if (!name) {
+            continue;
+        }
+
+        for (size_t len = 14; len <= file->size(); len += 5) {
+            SkCodec::Result result;
+            auto* stream = new SkMemoryStream(file->data(), len);
+            std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream, &result));
+            if (codec) {
+                if (result != SkCodec::kSuccess) {
+                    ERRORF(r, "Created an SkCodec for %s with %lu bytes, but "
+                              "reported an error %i", name, len, result);
+                }
+                break;
+            }
+
+            if (SkCodec::kIncompleteInput != result) {
+                ERRORF(r, "Reported error %i for %s with %lu bytes",
+                       result, name, len);
+                break;
+            }
+        }
+    }
+}
diff --git a/src/third_party/skia/tests/CodecPriv.h b/src/third_party/skia/tests/CodecPriv.h
new file mode 100644
index 0000000..d80bb69
--- /dev/null
+++ b/src/third_party/skia/tests/CodecPriv.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef CodecPriv_DEFINED
+#define CodecPriv_DEFINED
+
+#include "SkBitmap.h"
+#include "SkCodec.h"
+#include "SkData.h"
+
+inline bool decode_memory(const void* mem, size_t size, SkBitmap* bm) {
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(SkData::MakeWithoutCopy(mem, size)));
+    if (!codec) {
+        return false;
+    }
+
+    bm->allocPixels(codec->getInfo());
+    const SkCodec::Result result = codec->getPixels(codec->getInfo(), bm->getPixels(),
+            bm->rowBytes());
+    return result == SkCodec::kSuccess || result == SkCodec::kIncompleteInput;
+}
+#endif  // CodecPriv_DEFINED
diff --git a/src/third_party/skia/tests/CodecTest.cpp b/src/third_party/skia/tests/CodecTest.cpp
new file mode 100644
index 0000000..ac4afd1
--- /dev/null
+++ b/src/third_party/skia/tests/CodecTest.cpp
@@ -0,0 +1,1526 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "FakeStreams.h"
+#include "Resources.h"
+#include "SkAndroidCodec.h"
+#include "SkAutoMalloc.h"
+#include "SkBitmap.h"
+#include "SkCodec.h"
+#include "SkCodecImageGenerator.h"
+#include "SkColorSpace_XYZ.h"
+#include "SkColorSpacePriv.h"
+#include "SkData.h"
+#include "SkFrontBufferedStream.h"
+#include "SkImageEncoder.h"
+#include "SkImageEncoderPriv.h"
+#include "SkMD5.h"
+#include "SkOSPath.h"
+#include "SkJpegEncoder.h"
+#include "SkPngChunkReader.h"
+#include "SkPngEncoder.h"
+#include "SkRandom.h"
+#include "SkStream.h"
+#include "SkStreamPriv.h"
+#include "SkWebpEncoder.h"
+#include "Test.h"
+
+#include "png.h"
+
+#include "sk_tool_utils.h"
+
+#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR < 5
+    // FIXME (scroggo): Google3 needs to be updated to use a newer version of libpng. In
+    // the meantime, we had to break some pieces of SkPngCodec in order to support Google3.
+    // The parts that are broken are likely not used by Google3.
+    #define SK_PNG_DISABLE_TESTS
+#endif
+
+static void md5(const SkBitmap& bm, SkMD5::Digest* digest) {
+    SkASSERT(bm.getPixels());
+    SkMD5 md5;
+    size_t rowLen = bm.info().bytesPerPixel() * bm.width();
+    for (int y = 0; y < bm.height(); ++y) {
+        md5.write(bm.getAddr(0, y), rowLen);
+    }
+    md5.finish(*digest);
+}
+
+/**
+ *  Compute the digest for bm and compare it to a known good digest.
+ *  @param r Reporter to assert that bm's digest matches goodDigest.
+ *  @param goodDigest The known good digest to compare to.
+ *  @param bm The bitmap to test.
+ */
+static void compare_to_good_digest(skiatest::Reporter* r, const SkMD5::Digest& goodDigest,
+                           const SkBitmap& bm) {
+    SkMD5::Digest digest;
+    md5(bm, &digest);
+    REPORTER_ASSERT(r, digest == goodDigest);
+}
+
+/**
+ *  Test decoding an SkCodec to a particular SkImageInfo.
+ *
+ *  Calling getPixels(info) should return expectedResult, and if goodDigest is non nullptr,
+ *  the resulting decode should match.
+ */
+template<typename Codec>
+static void test_info(skiatest::Reporter* r, Codec* codec, const SkImageInfo& info,
+                      SkCodec::Result expectedResult, const SkMD5::Digest* goodDigest) {
+    SkBitmap bm;
+    bm.allocPixels(info);
+
+    SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
+    REPORTER_ASSERT(r, result == expectedResult);
+
+    if (goodDigest) {
+        compare_to_good_digest(r, *goodDigest, bm);
+    }
+}
+
+SkIRect generate_random_subset(SkRandom* rand, int w, int h) {
+    SkIRect rect;
+    do {
+        rect.fLeft = rand->nextRangeU(0, w);
+        rect.fTop = rand->nextRangeU(0, h);
+        rect.fRight = rand->nextRangeU(0, w);
+        rect.fBottom = rand->nextRangeU(0, h);
+        rect.sort();
+    } while (rect.isEmpty());
+    return rect;
+}
+
+static void test_incremental_decode(skiatest::Reporter* r, SkCodec* codec, const SkImageInfo& info,
+        const SkMD5::Digest& goodDigest) {
+    SkBitmap bm;
+    bm.allocPixels(info);
+
+    REPORTER_ASSERT(r, SkCodec::kSuccess == codec->startIncrementalDecode(info, bm.getPixels(),
+                                                                          bm.rowBytes()));
+
+    REPORTER_ASSERT(r, SkCodec::kSuccess == codec->incrementalDecode());
+
+    compare_to_good_digest(r, goodDigest, bm);
+}
+
+// Test in stripes, similar to DM's kStripe_Mode
+static void test_in_stripes(skiatest::Reporter* r, SkCodec* codec, const SkImageInfo& info,
+                            const SkMD5::Digest& goodDigest) {
+    SkBitmap bm;
+    bm.allocPixels(info);
+    bm.eraseColor(SK_ColorYELLOW);
+
+    const int height = info.height();
+    // Note that if numStripes does not evenly divide height there will be an extra
+    // stripe.
+    const int numStripes = 4;
+
+    if (numStripes > height) {
+        // Image is too small.
+        return;
+    }
+
+    const int stripeHeight = height / numStripes;
+
+    // Iterate through the image twice. Once to decode odd stripes, and once for even.
+    for (int oddEven = 1; oddEven >= 0; oddEven--) {
+        for (int y = oddEven * stripeHeight; y < height; y += 2 * stripeHeight) {
+            SkIRect subset = SkIRect::MakeLTRB(0, y, info.width(),
+                                               SkTMin(y + stripeHeight, height));
+            SkCodec::Options options;
+            options.fSubset = &subset;
+            if (SkCodec::kSuccess != codec->startIncrementalDecode(info, bm.getAddr(0, y),
+                        bm.rowBytes(), &options)) {
+                ERRORF(r, "failed to start incremental decode!\ttop: %i\tbottom%i\n",
+                       subset.top(), subset.bottom());
+                return;
+            }
+            if (SkCodec::kSuccess != codec->incrementalDecode()) {
+                ERRORF(r, "failed incremental decode starting from line %i\n", y);
+                return;
+            }
+        }
+    }
+
+    compare_to_good_digest(r, goodDigest, bm);
+}
+
+template<typename Codec>
+static void test_codec(skiatest::Reporter* r, Codec* codec, SkBitmap& bm, const SkImageInfo& info,
+        const SkISize& size, SkCodec::Result expectedResult, SkMD5::Digest* digest,
+        const SkMD5::Digest* goodDigest) {
+
+    REPORTER_ASSERT(r, info.dimensions() == size);
+    bm.allocPixels(info);
+
+    SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
+    REPORTER_ASSERT(r, result == expectedResult);
+
+    md5(bm, digest);
+    if (goodDigest) {
+        REPORTER_ASSERT(r, *digest == *goodDigest);
+    }
+
+    {
+        // Test decoding to 565
+        SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType);
+        if (info.alphaType() == kOpaque_SkAlphaType) {
+            // Decoding to 565 should succeed.
+            SkBitmap bm565;
+            bm565.allocPixels(info565);
+
+            // This will allow comparison even if the image is incomplete.
+            bm565.eraseColor(SK_ColorBLACK);
+
+            REPORTER_ASSERT(r, expectedResult == codec->getPixels(info565,
+                    bm565.getPixels(), bm565.rowBytes()));
+
+            SkMD5::Digest digest565;
+            md5(bm565, &digest565);
+
+            // A dumb client's request for non-opaque should also succeed.
+            for (auto alpha : { kPremul_SkAlphaType, kUnpremul_SkAlphaType }) {
+                info565 = info565.makeAlphaType(alpha);
+                test_info(r, codec, info565, expectedResult, &digest565);
+            }
+        } else {
+            test_info(r, codec, info565, SkCodec::kInvalidConversion, nullptr);
+        }
+    }
+
+    if (codec->getInfo().colorType() == kGray_8_SkColorType) {
+        SkImageInfo grayInfo = codec->getInfo();
+        SkBitmap grayBm;
+        grayBm.allocPixels(grayInfo);
+
+        grayBm.eraseColor(SK_ColorBLACK);
+
+        REPORTER_ASSERT(r, expectedResult == codec->getPixels(grayInfo,
+                grayBm.getPixels(), grayBm.rowBytes()));
+
+        SkMD5::Digest grayDigest;
+        md5(grayBm, &grayDigest);
+
+        for (auto alpha : { kPremul_SkAlphaType, kUnpremul_SkAlphaType }) {
+            grayInfo = grayInfo.makeAlphaType(alpha);
+            test_info(r, codec, grayInfo, expectedResult, &grayDigest);
+        }
+    }
+
+    // Verify that re-decoding gives the same result.  It is interesting to check this after
+    // a decode to 565, since choosing to decode to 565 may result in some of the decode
+    // options being modified.  These options should return to their defaults on another
+    // decode to kN32, so the new digest should match the old digest.
+    test_info(r, codec, info, expectedResult, digest);
+
+    {
+        // Check alpha type conversions
+        if (info.alphaType() == kOpaque_SkAlphaType) {
+            test_info(r, codec, info.makeAlphaType(kUnpremul_SkAlphaType),
+                      expectedResult, digest);
+            test_info(r, codec, info.makeAlphaType(kPremul_SkAlphaType),
+                      expectedResult, digest);
+        } else {
+            // Decoding to opaque should fail
+            test_info(r, codec, info.makeAlphaType(kOpaque_SkAlphaType),
+                      SkCodec::kInvalidConversion, nullptr);
+            SkAlphaType otherAt = info.alphaType();
+            if (kPremul_SkAlphaType == otherAt) {
+                otherAt = kUnpremul_SkAlphaType;
+            } else {
+                otherAt = kPremul_SkAlphaType;
+            }
+            // The other non-opaque alpha type should always succeed, but not match.
+            test_info(r, codec, info.makeAlphaType(otherAt), expectedResult, nullptr);
+        }
+    }
+}
+
+static bool supports_partial_scanlines(const char path[]) {
+    static const char* const exts[] = {
+        "jpg", "jpeg", "png", "webp"
+        "JPG", "JPEG", "PNG", "WEBP"
+    };
+
+    for (uint32_t i = 0; i < SK_ARRAY_COUNT(exts); i++) {
+        if (SkStrEndsWith(path, exts[i])) {
+            return true;
+        }
+    }
+    return false;
+}
+
+// FIXME: Break up this giant function
+static void check(skiatest::Reporter* r,
+                  const char path[],
+                  SkISize size,
+                  bool supportsScanlineDecoding,
+                  bool supportsSubsetDecoding,
+                  bool supportsIncomplete,
+                  bool supportsNewScanlineDecoding = false) {
+
+    std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
+    if (!stream) {
+        return;
+    }
+
+    std::unique_ptr<SkCodec> codec(nullptr);
+    bool isIncomplete = supportsIncomplete;
+    if (isIncomplete) {
+        size_t size = stream->getLength();
+        sk_sp<SkData> data((SkData::MakeFromStream(stream.get(), 2 * size / 3)));
+        codec.reset(SkCodec::NewFromData(data));
+    } else {
+        codec.reset(SkCodec::NewFromStream(stream.release()));
+    }
+    if (!codec) {
+        ERRORF(r, "Unable to decode '%s'", path);
+        return;
+    }
+
+    // Test full image decodes with SkCodec
+    SkMD5::Digest codecDigest;
+    const SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
+    SkBitmap bm;
+    SkCodec::Result expectedResult = isIncomplete ? SkCodec::kIncompleteInput : SkCodec::kSuccess;
+    test_codec(r, codec.get(), bm, info, size, expectedResult, &codecDigest, nullptr);
+
+    // Scanline decoding follows.
+
+    if (supportsNewScanlineDecoding && !isIncomplete) {
+        test_incremental_decode(r, codec.get(), info, codecDigest);
+        // This is only supported by codecs that use incremental decoding to
+        // support subset decodes - png and jpeg (once SkJpegCodec is
+        // converted).
+        if (SkStrEndsWith(path, "png") || SkStrEndsWith(path, "PNG")) {
+            test_in_stripes(r, codec.get(), info, codecDigest);
+        }
+    }
+
+    // Need to call startScanlineDecode() first.
+    REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) == 0);
+    REPORTER_ASSERT(r, !codec->skipScanlines(1));
+    const SkCodec::Result startResult = codec->startScanlineDecode(info);
+    if (supportsScanlineDecoding) {
+        bm.eraseColor(SK_ColorYELLOW);
+
+        REPORTER_ASSERT(r, startResult == SkCodec::kSuccess);
+
+        for (int y = 0; y < info.height(); y++) {
+            const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0);
+            if (!isIncomplete) {
+                REPORTER_ASSERT(r, 1 == lines);
+            }
+        }
+        // verify that scanline decoding gives the same result.
+        if (SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder()) {
+            compare_to_good_digest(r, codecDigest, bm);
+        }
+
+        // Cannot continue to decode scanlines beyond the end
+        REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
+                == 0);
+
+        // Interrupting a scanline decode with a full decode starts from
+        // scratch
+        REPORTER_ASSERT(r, codec->startScanlineDecode(info) == SkCodec::kSuccess);
+        const int lines = codec->getScanlines(bm.getAddr(0, 0), 1, 0);
+        if (!isIncomplete) {
+            REPORTER_ASSERT(r, lines == 1);
+        }
+        REPORTER_ASSERT(r, codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes())
+                == expectedResult);
+        REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
+                == 0);
+        REPORTER_ASSERT(r, codec->skipScanlines(1)
+                == 0);
+
+        // Test partial scanline decodes
+        if (supports_partial_scanlines(path) && info.width() >= 3) {
+            SkCodec::Options options;
+            int width = info.width();
+            int height = info.height();
+            SkIRect subset = SkIRect::MakeXYWH(2 * (width / 3), 0, width / 3, height);
+            options.fSubset = &subset;
+
+            const auto partialStartResult = codec->startScanlineDecode(info, &options);
+            REPORTER_ASSERT(r, partialStartResult == SkCodec::kSuccess);
+
+            for (int y = 0; y < height; y++) {
+                const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0);
+                if (!isIncomplete) {
+                    REPORTER_ASSERT(r, 1 == lines);
+                }
+            }
+        }
+    } else {
+        REPORTER_ASSERT(r, startResult == SkCodec::kUnimplemented);
+    }
+
+    // The rest of this function tests decoding subsets, and will decode an arbitrary number of
+    // random subsets.
+    // Do not attempt to decode subsets of an image of only once pixel, since there is no
+    // meaningful subset.
+    if (size.width() * size.height() == 1) {
+        return;
+    }
+
+    SkRandom rand;
+    SkIRect subset;
+    SkCodec::Options opts;
+    opts.fSubset = &subset;
+    for (int i = 0; i < 5; i++) {
+        subset = generate_random_subset(&rand, size.width(), size.height());
+        SkASSERT(!subset.isEmpty());
+        const bool supported = codec->getValidSubset(&subset);
+        REPORTER_ASSERT(r, supported == supportsSubsetDecoding);
+
+        SkImageInfo subsetInfo = info.makeWH(subset.width(), subset.height());
+        SkBitmap bm;
+        bm.allocPixels(subsetInfo);
+        const auto result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(), &opts);
+
+        if (supportsSubsetDecoding) {
+            if (expectedResult == SkCodec::kSuccess) {
+                REPORTER_ASSERT(r, result == expectedResult);
+            } else {
+                SkASSERT(expectedResult == SkCodec::kIncompleteInput);
+                REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput
+                                || result == SkCodec::kSuccess);
+            }
+            // Webp is the only codec that supports subsets, and it will have modified the subset
+            // to have even left/top.
+            REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop));
+        } else {
+            // No subsets will work.
+            REPORTER_ASSERT(r, result == SkCodec::kUnimplemented);
+        }
+    }
+
+    // SkAndroidCodec tests
+    if (supportsScanlineDecoding || supportsSubsetDecoding || supportsNewScanlineDecoding) {
+
+        std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
+        if (!stream) {
+            return;
+        }
+
+        std::unique_ptr<SkAndroidCodec> androidCodec(nullptr);
+        if (isIncomplete) {
+            size_t size = stream->getLength();
+            sk_sp<SkData> data((SkData::MakeFromStream(stream.get(), 2 * size / 3)));
+            androidCodec.reset(SkAndroidCodec::NewFromData(data));
+        } else {
+            androidCodec.reset(SkAndroidCodec::NewFromStream(stream.release()));
+        }
+        if (!androidCodec) {
+            ERRORF(r, "Unable to decode '%s'", path);
+            return;
+        }
+
+        SkBitmap bm;
+        SkMD5::Digest androidCodecDigest;
+        test_codec(r, androidCodec.get(), bm, info, size, expectedResult, &androidCodecDigest,
+                   &codecDigest);
+    }
+
+    if (!isIncomplete) {
+        // Test SkCodecImageGenerator
+        std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
+        sk_sp<SkData> fullData(SkData::MakeFromStream(stream.get(), stream->getLength()));
+        std::unique_ptr<SkImageGenerator> gen(
+                SkCodecImageGenerator::MakeFromEncodedCodec(fullData));
+        SkBitmap bm;
+        bm.allocPixels(info);
+        REPORTER_ASSERT(r, gen->getPixels(info, bm.getPixels(), bm.rowBytes()));
+        compare_to_good_digest(r, codecDigest, bm);
+
+#ifndef SK_PNG_DISABLE_TESTS
+        // Test using SkFrontBufferedStream, as Android does
+        SkStream* bufferedStream = SkFrontBufferedStream::Create(
+                new SkMemoryStream(std::move(fullData)), SkCodec::MinBufferedBytesNeeded());
+        REPORTER_ASSERT(r, bufferedStream);
+        codec.reset(SkCodec::NewFromStream(bufferedStream));
+        REPORTER_ASSERT(r, codec);
+        if (codec) {
+            test_info(r, codec.get(), info, SkCodec::kSuccess, &codecDigest);
+        }
+#endif
+    }
+
+    // If we've just tested incomplete decodes, let's run the same test again on full decodes.
+    if (isIncomplete) {
+        check(r, path, size, supportsScanlineDecoding, supportsSubsetDecoding, false,
+              supportsNewScanlineDecoding);
+    }
+}
+
+DEF_TEST(Codec_wbmp, r) {
+    check(r, "mandrill.wbmp", SkISize::Make(512, 512), true, false, true);
+}
+
+DEF_TEST(Codec_webp, r) {
+    check(r, "baby_tux.webp", SkISize::Make(386, 395), false, true, true);
+    check(r, "color_wheel.webp", SkISize::Make(128, 128), false, true, true);
+    check(r, "yellow_rose.webp", SkISize::Make(400, 301), false, true, true);
+}
+
+DEF_TEST(Codec_bmp, r) {
+    check(r, "randPixels.bmp", SkISize::Make(8, 8), true, false, true);
+    check(r, "rle.bmp", SkISize::Make(320, 240), true, false, true);
+}
+
+DEF_TEST(Codec_ico, r) {
+    // FIXME: We are not ready to test incomplete ICOs
+    // These two tests examine interestingly different behavior:
+    // Decodes an embedded BMP image
+    check(r, "color_wheel.ico", SkISize::Make(128, 128), true, false, false);
+    // Decodes an embedded PNG image
+    check(r, "google_chrome.ico", SkISize::Make(256, 256), false, false, false, true);
+}
+
+DEF_TEST(Codec_gif, r) {
+    check(r, "box.gif", SkISize::Make(200, 55), false, false, true, true);
+    check(r, "color_wheel.gif", SkISize::Make(128, 128), false, false, true, true);
+    // randPixels.gif is too small to test incomplete
+    check(r, "randPixels.gif", SkISize::Make(8, 8), false, false, false, true);
+}
+
+DEF_TEST(Codec_jpg, r) {
+    check(r, "CMYK.jpg", SkISize::Make(642, 516), true, false, true);
+    check(r, "color_wheel.jpg", SkISize::Make(128, 128), true, false, true);
+    // grayscale.jpg is too small to test incomplete
+    check(r, "grayscale.jpg", SkISize::Make(128, 128), true, false, false);
+    check(r, "mandrill_512_q075.jpg", SkISize::Make(512, 512), true, false, true);
+    // randPixels.jpg is too small to test incomplete
+    check(r, "randPixels.jpg", SkISize::Make(8, 8), true, false, false);
+}
+
+DEF_TEST(Codec_png, r) {
+    check(r, "arrow.png", SkISize::Make(187, 312), false, false, true, true);
+    check(r, "baby_tux.png", SkISize::Make(240, 246), false, false, true, true);
+    check(r, "color_wheel.png", SkISize::Make(128, 128), false, false, true, true);
+    // half-transparent-white-pixel.png is too small to test incomplete
+    check(r, "half-transparent-white-pixel.png", SkISize::Make(1, 1), false, false, false, true);
+    check(r, "mandrill_128.png", SkISize::Make(128, 128), false, false, true, true);
+    check(r, "mandrill_16.png", SkISize::Make(16, 16), false, false, true, true);
+    check(r, "mandrill_256.png", SkISize::Make(256, 256), false, false, true, true);
+    check(r, "mandrill_32.png", SkISize::Make(32, 32), false, false, true, true);
+    check(r, "mandrill_512.png", SkISize::Make(512, 512), false, false, true, true);
+    check(r, "mandrill_64.png", SkISize::Make(64, 64), false, false, true, true);
+    check(r, "plane.png", SkISize::Make(250, 126), false, false, true, true);
+    check(r, "plane_interlaced.png", SkISize::Make(250, 126), false, false, true, true);
+    check(r, "randPixels.png", SkISize::Make(8, 8), false, false, true, true);
+    check(r, "yellow_rose.png", SkISize::Make(400, 301), false, false, true, true);
+}
+
+// Disable RAW tests for Win32.
+#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
+DEF_TEST(Codec_raw, r) {
+    check(r, "sample_1mp.dng", SkISize::Make(600, 338), false, false, false);
+    check(r, "sample_1mp_rotated.dng", SkISize::Make(600, 338), false, false, false);
+    check(r, "dng_with_preview.dng", SkISize::Make(600, 338), true, false, false);
+}
+#endif
+
+static void test_invalid_stream(skiatest::Reporter* r, const void* stream, size_t len) {
+    // Neither of these calls should return a codec. Bots should catch us if we leaked anything.
+    SkCodec* codec = SkCodec::NewFromStream(new SkMemoryStream(stream, len, false));
+    REPORTER_ASSERT(r, !codec);
+
+    SkAndroidCodec* androidCodec =
+            SkAndroidCodec::NewFromStream(new SkMemoryStream(stream, len, false));
+    REPORTER_ASSERT(r, !androidCodec);
+}
+
+// Ensure that SkCodec::NewFromStream handles freeing the passed in SkStream,
+// even on failure. Test some bad streams.
+DEF_TEST(Codec_leaks, r) {
+    // No codec should claim this as their format, so this tests SkCodec::NewFromStream.
+    const char nonSupportedStream[] = "hello world";
+    // The other strings should look like the beginning of a file type, so we'll call some
+    // internal version of NewFromStream, which must also delete the stream on failure.
+    const unsigned char emptyPng[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
+    const unsigned char emptyJpeg[] = { 0xFF, 0xD8, 0xFF };
+    const char emptyWebp[] = "RIFF1234WEBPVP";
+    const char emptyBmp[] = { 'B', 'M' };
+    const char emptyIco[] = { '\x00', '\x00', '\x01', '\x00' };
+    const char emptyGif[] = "GIFVER";
+
+    test_invalid_stream(r, nonSupportedStream, sizeof(nonSupportedStream));
+    test_invalid_stream(r, emptyPng, sizeof(emptyPng));
+    test_invalid_stream(r, emptyJpeg, sizeof(emptyJpeg));
+    test_invalid_stream(r, emptyWebp, sizeof(emptyWebp));
+    test_invalid_stream(r, emptyBmp, sizeof(emptyBmp));
+    test_invalid_stream(r, emptyIco, sizeof(emptyIco));
+    test_invalid_stream(r, emptyGif, sizeof(emptyGif));
+}
+
+DEF_TEST(Codec_null, r) {
+    // Attempting to create an SkCodec or an SkAndroidCodec with null should not
+    // crash.
+    SkCodec* codec = SkCodec::NewFromStream(nullptr);
+    REPORTER_ASSERT(r, !codec);
+
+    SkAndroidCodec* androidCodec = SkAndroidCodec::NewFromStream(nullptr);
+    REPORTER_ASSERT(r, !androidCodec);
+}
+
+static void test_dimensions(skiatest::Reporter* r, const char path[]) {
+    // Create the codec from the resource file
+    std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
+    if (!stream) {
+        return;
+    }
+    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(stream.release()));
+    if (!codec) {
+        ERRORF(r, "Unable to create codec '%s'", path);
+        return;
+    }
+
+    // Check that the decode is successful for a variety of scales
+    for (int sampleSize = 1; sampleSize < 32; sampleSize++) {
+        // Scale the output dimensions
+        SkISize scaledDims = codec->getSampledDimensions(sampleSize);
+        SkImageInfo scaledInfo = codec->getInfo()
+                .makeWH(scaledDims.width(), scaledDims.height())
+                .makeColorType(kN32_SkColorType);
+
+        // Set up for the decode
+        size_t rowBytes = scaledDims.width() * sizeof(SkPMColor);
+        size_t totalBytes = scaledInfo.getSafeSize(rowBytes);
+        SkAutoTMalloc<SkPMColor> pixels(totalBytes);
+
+        SkAndroidCodec::AndroidOptions options;
+        options.fSampleSize = sampleSize;
+        SkCodec::Result result =
+                codec->getAndroidPixels(scaledInfo, pixels.get(), rowBytes, &options);
+        REPORTER_ASSERT(r, SkCodec::kSuccess == result);
+    }
+}
+
+// Ensure that onGetScaledDimensions returns valid image dimensions to use for decodes
+DEF_TEST(Codec_Dimensions, r) {
+    // JPG
+    test_dimensions(r, "CMYK.jpg");
+    test_dimensions(r, "color_wheel.jpg");
+    test_dimensions(r, "grayscale.jpg");
+    test_dimensions(r, "mandrill_512_q075.jpg");
+    test_dimensions(r, "randPixels.jpg");
+
+    // Decoding small images with very large scaling factors is a potential
+    // source of bugs and crashes.  We disable these tests in Gold because
+    // tiny images are not very useful to look at.
+    // Here we make sure that we do not crash or access illegal memory when
+    // performing scaled decodes on small images.
+    test_dimensions(r, "1x1.png");
+    test_dimensions(r, "2x2.png");
+    test_dimensions(r, "3x3.png");
+    test_dimensions(r, "3x1.png");
+    test_dimensions(r, "1x1.png");
+    test_dimensions(r, "16x1.png");
+    test_dimensions(r, "1x16.png");
+    test_dimensions(r, "mandrill_16.png");
+
+    // RAW
+// Disable RAW tests for Win32.
+#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
+    test_dimensions(r, "sample_1mp.dng");
+    test_dimensions(r, "sample_1mp_rotated.dng");
+    test_dimensions(r, "dng_with_preview.dng");
+#endif
+}
+
+static void test_invalid(skiatest::Reporter* r, const char path[]) {
+    std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
+    if (!stream) {
+        return;
+    }
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
+    REPORTER_ASSERT(r, nullptr == codec);
+}
+
+DEF_TEST(Codec_Empty, r) {
+    // Test images that should not be able to create a codec
+    test_invalid(r, "empty_images/zero-dims.gif");
+    test_invalid(r, "empty_images/zero-embedded.ico");
+    test_invalid(r, "empty_images/zero-width.bmp");
+    test_invalid(r, "empty_images/zero-height.bmp");
+    test_invalid(r, "empty_images/zero-width.jpg");
+    test_invalid(r, "empty_images/zero-height.jpg");
+    test_invalid(r, "empty_images/zero-width.png");
+    test_invalid(r, "empty_images/zero-height.png");
+    test_invalid(r, "empty_images/zero-width.wbmp");
+    test_invalid(r, "empty_images/zero-height.wbmp");
+    // This image is an ico with an embedded mask-bmp.  This is illegal.
+    test_invalid(r, "invalid_images/mask-bmp-ico.ico");
+    // It is illegal for a webp frame to not be fully contained by the canvas.
+    test_invalid(r, "invalid_images/invalid-offset.webp");
+#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
+    test_invalid(r, "empty_images/zero_height.tiff");
+#endif
+    test_invalid(r, "invalid_images/b37623797.ico");
+}
+
+#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+
+#ifndef SK_PNG_DISABLE_TESTS   // reading chunks does not work properly with older versions.
+                               // It does not appear that anyone in Google3 is reading chunks.
+
+static void codex_test_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
+    SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
+    if (!sk_stream->write(data, len)) {
+        png_error(png_ptr, "sk_write_fn Error!");
+    }
+}
+
+DEF_TEST(Codec_pngChunkReader, r) {
+    // Create a dummy bitmap. Use unpremul RGBA for libpng.
+    SkBitmap bm;
+    const int w = 1;
+    const int h = 1;
+    const SkImageInfo bmInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType,
+                                                 kUnpremul_SkAlphaType);
+    bm.setInfo(bmInfo);
+    bm.allocPixels();
+    bm.eraseColor(SK_ColorBLUE);
+    SkMD5::Digest goodDigest;
+    md5(bm, &goodDigest);
+
+    // Write to a png file.
+    png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+    REPORTER_ASSERT(r, png);
+    if (!png) {
+        return;
+    }
+
+    png_infop info = png_create_info_struct(png);
+    REPORTER_ASSERT(r, info);
+    if (!info) {
+        png_destroy_write_struct(&png, nullptr);
+        return;
+    }
+
+    if (setjmp(png_jmpbuf(png))) {
+        ERRORF(r, "failed writing png");
+        png_destroy_write_struct(&png, &info);
+        return;
+    }
+
+    SkDynamicMemoryWStream wStream;
+    png_set_write_fn(png, (void*) (&wStream), codex_test_write_fn, nullptr);
+
+    png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8,
+                 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
+                 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+    // Create some chunks that match the Android framework's use.
+    static png_unknown_chunk gUnknowns[] = {
+        { "npOl", (png_byte*)"outline", sizeof("outline"), PNG_HAVE_IHDR },
+        { "npLb", (png_byte*)"layoutBounds", sizeof("layoutBounds"), PNG_HAVE_IHDR },
+        { "npTc", (png_byte*)"ninePatchData", sizeof("ninePatchData"), PNG_HAVE_IHDR },
+    };
+
+    png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"npOl\0npLb\0npTc\0", 3);
+    png_set_unknown_chunks(png, info, gUnknowns, SK_ARRAY_COUNT(gUnknowns));
+#if PNG_LIBPNG_VER < 10600
+    /* Deal with unknown chunk location bug in 1.5.x and earlier */
+    png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_IHDR);
+    png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_IHDR);
+#endif
+
+    png_write_info(png, info);
+
+    for (int j = 0; j < h; j++) {
+        png_bytep row = (png_bytep)(bm.getAddr(0, j));
+        png_write_rows(png, &row, 1);
+    }
+    png_write_end(png, info);
+    png_destroy_write_struct(&png, &info);
+
+    class ChunkReader : public SkPngChunkReader {
+    public:
+        ChunkReader(skiatest::Reporter* r)
+            : fReporter(r)
+        {
+            this->reset();
+        }
+
+        bool readChunk(const char tag[], const void* data, size_t length) override {
+            for (size_t i = 0; i < SK_ARRAY_COUNT(gUnknowns); ++i) {
+                if (!strcmp(tag, (const char*) gUnknowns[i].name)) {
+                    // Tag matches. This should have been the first time we see it.
+                    REPORTER_ASSERT(fReporter, !fSeen[i]);
+                    fSeen[i] = true;
+
+                    // Data and length should match
+                    REPORTER_ASSERT(fReporter, length == gUnknowns[i].size);
+                    REPORTER_ASSERT(fReporter, !strcmp((const char*) data,
+                                                       (const char*) gUnknowns[i].data));
+                    return true;
+                }
+            }
+            ERRORF(fReporter, "Saw an unexpected unknown chunk.");
+            return true;
+        }
+
+        bool allHaveBeenSeen() {
+            bool ret = true;
+            for (auto seen : fSeen) {
+                ret &= seen;
+            }
+            return ret;
+        }
+
+        void reset() {
+            sk_bzero(fSeen, sizeof(fSeen));
+        }
+
+    private:
+        skiatest::Reporter* fReporter;  // Unowned
+        bool fSeen[3];
+    };
+
+    ChunkReader chunkReader(r);
+
+    // Now read the file with SkCodec.
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(wStream.detachAsData(), &chunkReader));
+    REPORTER_ASSERT(r, codec);
+    if (!codec) {
+        return;
+    }
+
+    // Now compare to the original.
+    SkBitmap decodedBm;
+    decodedBm.setInfo(codec->getInfo());
+    decodedBm.allocPixels();
+    SkCodec::Result result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(),
+                                              decodedBm.rowBytes());
+    REPORTER_ASSERT(r, SkCodec::kSuccess == result);
+
+    if (decodedBm.colorType() != bm.colorType()) {
+        SkBitmap tmp;
+        bool success = sk_tool_utils::copy_to(&tmp, bm.colorType(), decodedBm);
+        REPORTER_ASSERT(r, success);
+        if (!success) {
+            return;
+        }
+
+        tmp.swap(decodedBm);
+    }
+
+    compare_to_good_digest(r, goodDigest, decodedBm);
+    REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen());
+
+    // Decoding again will read the chunks again.
+    chunkReader.reset();
+    REPORTER_ASSERT(r, !chunkReader.allHaveBeenSeen());
+    result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), decodedBm.rowBytes());
+    REPORTER_ASSERT(r, SkCodec::kSuccess == result);
+    REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen());
+}
+#endif // SK_PNG_DISABLE_TESTS
+#endif // PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+
+// Stream that can only peek up to a limit
+class LimitedPeekingMemStream : public SkStream {
+public:
+    LimitedPeekingMemStream(sk_sp<SkData> data, size_t limit)
+        : fStream(std::move(data))
+        , fLimit(limit) {}
+
+    size_t peek(void* buf, size_t bytes) const override {
+        return fStream.peek(buf, SkTMin(bytes, fLimit));
+    }
+    size_t read(void* buf, size_t bytes) override {
+        return fStream.read(buf, bytes);
+    }
+    bool rewind() override {
+        return fStream.rewind();
+    }
+    bool isAtEnd() const override {
+        return fStream.isAtEnd();
+    }
+private:
+    SkMemoryStream fStream;
+    const size_t   fLimit;
+};
+
+// Disable RAW tests for Win32.
+#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
+// Test that the RawCodec works also for not asset stream. This will test the code path using
+// SkRawBufferedStream instead of SkRawAssetStream.
+DEF_TEST(Codec_raw_notseekable, r) {
+    const char* path = "dng_with_preview.dng";
+    SkString fullPath(GetResourcePath(path));
+    sk_sp<SkData> data(SkData::MakeFromFileName(fullPath.c_str()));
+    if (!data) {
+        SkDebugf("Missing resource '%s'\n", path);
+        return;
+    }
+
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(new NotAssetMemStream(std::move(data))));
+    REPORTER_ASSERT(r, codec);
+
+    test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
+}
+#endif
+
+// Test that even if webp_parse_header fails to peek enough, it will fall back to read()
+// + rewind() and succeed.
+DEF_TEST(Codec_webp_peek, r) {
+    const char* path = "baby_tux.webp";
+    SkString fullPath(GetResourcePath(path));
+    auto data = SkData::MakeFromFileName(fullPath.c_str());
+    if (!data) {
+        SkDebugf("Missing resource '%s'\n", path);
+        return;
+    }
+
+    // The limit is less than webp needs to peek or read.
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(
+                                 new LimitedPeekingMemStream(data, 25)));
+    REPORTER_ASSERT(r, codec);
+
+    test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
+
+    // Similarly, a stream which does not peek should still succeed.
+    codec.reset(SkCodec::NewFromStream(new LimitedPeekingMemStream(data, 0)));
+    REPORTER_ASSERT(r, codec);
+
+    test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
+}
+
+// SkCodec's wbmp decoder was initially unnecessarily restrictive.
+// It required the second byte to be zero. The wbmp specification allows
+// a couple of bits to be 1 (so long as they do not overlap with 0x9F).
+// Test that SkCodec now supports an image with these bits set.
+DEF_TEST(Codec_wbmp_restrictive, r) {
+    const char* path = "mandrill.wbmp";
+    std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
+    if (!stream) {
+        return;
+    }
+
+    // Modify the stream to contain a second byte with some bits set.
+    auto data = SkCopyStreamToData(stream.get());
+    uint8_t* writeableData = static_cast<uint8_t*>(data->writable_data());
+    writeableData[1] = static_cast<uint8_t>(~0x9F);
+
+    // SkCodec should support this.
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
+    REPORTER_ASSERT(r, codec);
+    if (!codec) {
+        return;
+    }
+    test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
+}
+
+// wbmp images have a header that can be arbitrarily large, depending on the
+// size of the image. We cap the size at 65535, meaning we only need to look at
+// 8 bytes to determine whether we can read the image. This is important
+// because SkCodec only passes 14 bytes to SkWbmpCodec to determine whether the
+// image is a wbmp.
+DEF_TEST(Codec_wbmp_max_size, r) {
+    const unsigned char maxSizeWbmp[] = { 0x00, 0x00,           // Header
+                                          0x83, 0xFF, 0x7F,     // W: 65535
+                                          0x83, 0xFF, 0x7F };   // H: 65535
+    std::unique_ptr<SkStream> stream(new SkMemoryStream(maxSizeWbmp, sizeof(maxSizeWbmp), false));
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
+
+    REPORTER_ASSERT(r, codec);
+    if (!codec) return;
+
+    REPORTER_ASSERT(r, codec->getInfo().width() == 65535);
+    REPORTER_ASSERT(r, codec->getInfo().height() == 65535);
+
+    // Now test an image which is too big. Any image with a larger header (i.e.
+    // has bigger width/height) is also too big.
+    const unsigned char tooBigWbmp[] = { 0x00, 0x00,           // Header
+                                         0x84, 0x80, 0x00,     // W: 65536
+                                         0x84, 0x80, 0x00 };   // H: 65536
+    stream.reset(new SkMemoryStream(tooBigWbmp, sizeof(tooBigWbmp), false));
+    codec.reset(SkCodec::NewFromStream(stream.release()));
+
+    REPORTER_ASSERT(r, !codec);
+}
+
+DEF_TEST(Codec_jpeg_rewind, r) {
+    const char* path = "mandrill_512_q075.jpg";
+    sk_sp<SkData> data(GetResourceAsData(path));
+    if (!data) {
+        return;
+    }
+
+    data = SkData::MakeSubset(data.get(), 0, data->size() / 2);
+    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromData(data));
+    if (!codec) {
+        ERRORF(r, "Unable to create codec '%s'.", path);
+        return;
+    }
+
+    const int width = codec->getInfo().width();
+    const int height = codec->getInfo().height();
+    size_t rowBytes = sizeof(SkPMColor) * width;
+    SkAutoMalloc pixelStorage(height * rowBytes);
+
+    // Perform a sampled decode.
+    SkAndroidCodec::AndroidOptions opts;
+    opts.fSampleSize = 12;
+    auto sampledInfo = codec->getInfo().makeWH(width / 12, height / 12);
+    auto result = codec->getAndroidPixels(sampledInfo, pixelStorage.get(), rowBytes, &opts);
+    REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
+
+    // Rewind the codec and perform a full image decode.
+    result = codec->getPixels(codec->getInfo(), pixelStorage.get(), rowBytes);
+    REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
+
+    // Now perform a subset decode.
+    {
+        opts.fSampleSize = 1;
+        SkIRect subset = SkIRect::MakeWH(100, 100);
+        opts.fSubset = &subset;
+        result = codec->getAndroidPixels(codec->getInfo().makeWH(100, 100), pixelStorage.get(),
+                                         rowBytes, &opts);
+        // Though we only have half the data, it is enough to decode this subset.
+        REPORTER_ASSERT(r, SkCodec::kSuccess == result);
+    }
+
+    // Perform another full image decode.  ASAN will detect if we look at the subset when it is
+    // out of scope.  This would happen if we depend on the old state in the codec.
+    // This tests two layers of bugs: both SkJpegCodec::readRows and SkCodec::fillIncompleteImage
+    // used to look at the old subset.
+    opts.fSubset = nullptr;
+    result = codec->getAndroidPixels(codec->getInfo(), pixelStorage.get(), rowBytes, &opts);
+    REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
+}
+
+static void check_color_xform(skiatest::Reporter* r, const char* path) {
+    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(GetResourceAsStream(path)));
+
+    SkAndroidCodec::AndroidOptions opts;
+    opts.fSampleSize = 3;
+    const int subsetWidth = codec->getInfo().width() / 2;
+    const int subsetHeight = codec->getInfo().height() / 2;
+    SkIRect subset = SkIRect::MakeWH(subsetWidth, subsetHeight);
+    opts.fSubset = &subset;
+
+    const int dstWidth = subsetWidth / opts.fSampleSize;
+    const int dstHeight = subsetHeight / opts.fSampleSize;
+    sk_sp<SkData> data = SkData::MakeFromFileName(
+            GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str());
+    sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeICC(data->data(), data->size());
+    SkImageInfo dstInfo = codec->getInfo().makeWH(dstWidth, dstHeight)
+                                          .makeColorType(kN32_SkColorType)
+                                          .makeColorSpace(colorSpace);
+
+    size_t rowBytes = dstInfo.minRowBytes();
+    SkAutoMalloc pixelStorage(dstInfo.getSafeSize(rowBytes));
+    SkCodec::Result result = codec->getAndroidPixels(dstInfo, pixelStorage.get(), rowBytes, &opts);
+    REPORTER_ASSERT(r, SkCodec::kSuccess == result);
+}
+
+DEF_TEST(Codec_ColorXform, r) {
+    check_color_xform(r, "mandrill_512_q075.jpg");
+    check_color_xform(r, "mandrill_512.png");
+}
+
+static bool color_type_match(SkColorType origColorType, SkColorType codecColorType) {
+    switch (origColorType) {
+        case kRGBA_8888_SkColorType:
+        case kBGRA_8888_SkColorType:
+            return kRGBA_8888_SkColorType == codecColorType ||
+                   kBGRA_8888_SkColorType == codecColorType;
+        default:
+            return origColorType == codecColorType;
+    }
+}
+
+static bool alpha_type_match(SkAlphaType origAlphaType, SkAlphaType codecAlphaType) {
+    switch (origAlphaType) {
+        case kUnpremul_SkAlphaType:
+        case kPremul_SkAlphaType:
+            return kUnpremul_SkAlphaType == codecAlphaType ||
+                    kPremul_SkAlphaType == codecAlphaType;
+        default:
+            return origAlphaType == codecAlphaType;
+    }
+}
+
+static void check_round_trip(skiatest::Reporter* r, SkCodec* origCodec, const SkImageInfo& info) {
+    SkBitmap bm1;
+    bm1.allocPixels(info);
+    SkCodec::Result result = origCodec->getPixels(info, bm1.getPixels(), bm1.rowBytes());
+    REPORTER_ASSERT(r, SkCodec::kSuccess == result);
+
+    // Encode the image to png.
+    sk_sp<SkData> data =
+            sk_sp<SkData>(sk_tool_utils::EncodeImageToData(bm1, SkEncodedImageFormat::kPNG, 100));
+
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
+    REPORTER_ASSERT(r, color_type_match(info.colorType(), codec->getInfo().colorType()));
+    REPORTER_ASSERT(r, alpha_type_match(info.alphaType(), codec->getInfo().alphaType()));
+
+    SkBitmap bm2;
+    bm2.allocPixels(info);
+    result = codec->getPixels(info, bm2.getPixels(), bm2.rowBytes());
+    REPORTER_ASSERT(r, SkCodec::kSuccess == result);
+
+    SkMD5::Digest d1, d2;
+    md5(bm1, &d1);
+    md5(bm2, &d2);
+    REPORTER_ASSERT(r, d1 == d2);
+}
+
+DEF_TEST(Codec_PngRoundTrip, r) {
+    const char* path = "mandrill_512_q075.jpg";
+    std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
+
+    SkColorType colorTypesOpaque[] = {
+            kRGB_565_SkColorType, kRGBA_8888_SkColorType, kBGRA_8888_SkColorType
+    };
+    for (SkColorType colorType : colorTypesOpaque) {
+        SkImageInfo newInfo = codec->getInfo().makeColorType(colorType);
+        check_round_trip(r, codec.get(), newInfo);
+    }
+
+    path = "grayscale.jpg";
+    stream.reset(GetResourceAsStream(path));
+    codec.reset(SkCodec::NewFromStream(stream.release()));
+    check_round_trip(r, codec.get(), codec->getInfo());
+
+    path = "yellow_rose.png";
+    stream.reset(GetResourceAsStream(path));
+    codec.reset(SkCodec::NewFromStream(stream.release()));
+
+    SkColorType colorTypesWithAlpha[] = {
+            kRGBA_8888_SkColorType, kBGRA_8888_SkColorType
+    };
+    SkAlphaType alphaTypes[] = {
+            kUnpremul_SkAlphaType, kPremul_SkAlphaType
+    };
+    for (SkColorType colorType : colorTypesWithAlpha) {
+        for (SkAlphaType alphaType : alphaTypes) {
+            // Set color space to nullptr because color correct premultiplies do not round trip.
+            SkImageInfo newInfo = codec->getInfo().makeColorType(colorType)
+                                                  .makeAlphaType(alphaType)
+                                                  .makeColorSpace(nullptr);
+            check_round_trip(r, codec.get(), newInfo);
+        }
+    }
+
+    path = "index8.png";
+    stream.reset(GetResourceAsStream(path));
+    codec.reset(SkCodec::NewFromStream(stream.release()));
+
+    for (SkAlphaType alphaType : alphaTypes) {
+        SkImageInfo newInfo = codec->getInfo().makeAlphaType(alphaType)
+                                              .makeColorSpace(nullptr);
+        check_round_trip(r, codec.get(), newInfo);
+    }
+}
+
+static void test_conversion_possible(skiatest::Reporter* r, const char* path,
+                                     bool supportsScanlineDecoder,
+                                     bool supportsIncrementalDecoder) {
+    std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
+    if (!stream) {
+        return;
+    }
+
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
+    if (!codec) {
+        ERRORF(r, "failed to create a codec for %s", path);
+        return;
+    }
+
+    SkImageInfo infoF16 = codec->getInfo().makeColorType(kRGBA_F16_SkColorType);
+
+    SkBitmap bm;
+    bm.allocPixels(infoF16);
+    SkCodec::Result result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes());
+    REPORTER_ASSERT(r, SkCodec::kInvalidConversion == result);
+
+    result = codec->startScanlineDecode(infoF16);
+    if (supportsScanlineDecoder) {
+        REPORTER_ASSERT(r, SkCodec::kInvalidConversion == result);
+    } else {
+        REPORTER_ASSERT(r, SkCodec::kUnimplemented == result);
+    }
+
+    result = codec->startIncrementalDecode(infoF16, bm.getPixels(), bm.rowBytes());
+    if (supportsIncrementalDecoder) {
+        REPORTER_ASSERT(r, SkCodec::kInvalidConversion == result);
+    } else {
+        REPORTER_ASSERT(r, SkCodec::kUnimplemented == result);
+    }
+
+    SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(infoF16.colorSpace())->type());
+    SkColorSpace_XYZ* csXYZ = static_cast<SkColorSpace_XYZ*>(infoF16.colorSpace());
+    infoF16 = infoF16.makeColorSpace(csXYZ->makeLinearGamma());
+    result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes());
+    REPORTER_ASSERT(r, SkCodec::kSuccess == result);
+    result = codec->startScanlineDecode(infoF16);
+    if (supportsScanlineDecoder) {
+        REPORTER_ASSERT(r, SkCodec::kSuccess == result);
+    } else {
+        REPORTER_ASSERT(r, SkCodec::kUnimplemented == result);
+    }
+
+    result = codec->startIncrementalDecode(infoF16, bm.getPixels(), bm.rowBytes());
+    if (supportsIncrementalDecoder) {
+        REPORTER_ASSERT(r, SkCodec::kSuccess == result);
+    } else {
+        REPORTER_ASSERT(r, SkCodec::kUnimplemented == result);
+    }
+}
+
+DEF_TEST(Codec_F16ConversionPossible, r) {
+    test_conversion_possible(r, "color_wheel.webp", false, false);
+    test_conversion_possible(r, "mandrill_512_q075.jpg", true, false);
+    test_conversion_possible(r, "yellow_rose.png", false, true);
+}
+
+static void decode_frame(skiatest::Reporter* r, SkCodec* codec, size_t frame) {
+    SkBitmap bm;
+    auto info = codec->getInfo().makeColorType(kN32_SkColorType);
+    bm.allocPixels(info);
+
+    SkCodec::Options opts;
+    opts.fFrameIndex = frame;
+    REPORTER_ASSERT(r, SkCodec::kSuccess == codec->getPixels(info,
+            bm.getPixels(), bm.rowBytes(), &opts));
+}
+
+// For an animated GIF, we should only read enough to decode frame 0 if the
+// client never calls getFrameInfo and only decodes frame 0.
+DEF_TEST(Codec_skipFullParse, r) {
+    auto path = "test640x479.gif";
+    SkStream* stream(GetResourceAsStream(path));
+    if (!stream) {
+        return;
+    }
+
+    // Note that we cheat and hold on to the stream pointer, but SkCodec will
+    // take ownership. We will not refer to the stream after the SkCodec
+    // deletes it.
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream));
+    if (!codec) {
+        ERRORF(r, "Failed to create codec for %s", path);
+        return;
+    }
+
+    REPORTER_ASSERT(r, stream->hasPosition());
+    const size_t sizePosition = stream->getPosition();
+    REPORTER_ASSERT(r, stream->hasLength() && sizePosition < stream->getLength());
+
+    // This should read more of the stream, but not the whole stream.
+    decode_frame(r, codec.get(), 0);
+    const size_t positionAfterFirstFrame = stream->getPosition();
+    REPORTER_ASSERT(r, positionAfterFirstFrame > sizePosition
+                       && positionAfterFirstFrame < stream->getLength());
+
+    // There is more data in the stream.
+    auto frameInfo = codec->getFrameInfo();
+    REPORTER_ASSERT(r, frameInfo.size() == 4);
+    REPORTER_ASSERT(r, stream->getPosition() > positionAfterFirstFrame);
+}
+
+// Only rewinds up to a limit.
+class LimitedRewindingStream : public SkStream {
+public:
+    static SkStream* Make(const char path[], size_t limit) {
+        SkStream* stream = GetResourceAsStream(path);
+        if (!stream) {
+            return nullptr;
+        }
+        return new LimitedRewindingStream(stream, limit);
+    }
+
+    size_t read(void* buffer, size_t size) override {
+        const size_t bytes = fStream->read(buffer, size);
+        fPosition += bytes;
+        return bytes;
+    }
+
+    bool isAtEnd() const override {
+        return fStream->isAtEnd();
+    }
+
+    bool rewind() override {
+        if (fPosition <= fLimit && fStream->rewind()) {
+            fPosition = 0;
+            return true;
+        }
+
+        return false;
+    }
+
+private:
+    std::unique_ptr<SkStream> fStream;
+    const size_t              fLimit;
+    size_t                    fPosition;
+
+    LimitedRewindingStream(SkStream* stream, size_t limit)
+        : fStream(stream)
+        , fLimit(limit)
+        , fPosition(0)
+    {
+        SkASSERT(fStream);
+    }
+};
+
+DEF_TEST(Codec_fallBack, r) {
+    // SkAndroidCodec needs to be able to fall back to scanline decoding
+    // if incremental decoding does not work. Make sure this does not
+    // require a rewind.
+
+    // Formats that currently do not support incremental decoding
+    auto files = {
+            "CMYK.jpg",
+            "color_wheel.ico",
+            "mandrill.wbmp",
+            "randPixels.bmp",
+            };
+    for (auto file : files) {
+        SkStream* stream = LimitedRewindingStream::Make(file, 14);
+        if (!stream) {
+            SkDebugf("Missing resources (%s). Set --resourcePath.\n", file);
+            return;
+        }
+
+        std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream));
+        if (!codec) {
+            ERRORF(r, "Failed to create codec for %s,", file);
+            continue;
+        }
+
+        SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
+        SkBitmap bm;
+        bm.allocPixels(info);
+
+        if (SkCodec::kUnimplemented != codec->startIncrementalDecode(info, bm.getPixels(),
+                bm.rowBytes())) {
+            ERRORF(r, "Is scanline decoding now implemented for %s?", file);
+            continue;
+        }
+
+        // Scanline decoding should not require a rewind.
+        SkCodec::Result result = codec->startScanlineDecode(info);
+        if (SkCodec::kSuccess != result) {
+            ERRORF(r, "Scanline decoding failed for %s with %i", file, result);
+        }
+    }
+}
+
+// This test verifies that we fixed an assert statement that fired when reusing a png codec
+// after scaling.
+DEF_TEST(Codec_reusePng, r) {
+    std::unique_ptr<SkStream> stream(GetResourceAsStream("plane.png"));
+    if (!stream) {
+        return;
+    }
+
+    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(stream.release()));
+    if (!codec) {
+        ERRORF(r, "Failed to create codec\n");
+        return;
+    }
+
+    SkAndroidCodec::AndroidOptions opts;
+    opts.fSampleSize = 5;
+    auto size = codec->getSampledDimensions(opts.fSampleSize);
+    auto info = codec->getInfo().makeWH(size.fWidth, size.fHeight).makeColorType(kN32_SkColorType);
+    SkBitmap bm;
+    bm.allocPixels(info);
+    auto result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &opts);
+    REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+    info = codec->getInfo().makeColorType(kN32_SkColorType);
+    bm.allocPixels(info);
+    opts.fSampleSize = 1;
+    result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &opts);
+    REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+}
+
+DEF_TEST(Codec_rowsDecoded, r) {
+    auto file = "plane_interlaced.png";
+    std::unique_ptr<SkStream> stream(GetResourceAsStream(file));
+    if (!stream) {
+        return;
+    }
+
+    // This is enough to read the header etc, but no rows.
+    auto data = SkData::MakeFromStream(stream.get(), 99);
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
+    if (!codec) {
+        ERRORF(r, "Failed to create codec\n");
+        return;
+    }
+
+    auto info = codec->getInfo().makeColorType(kN32_SkColorType);
+    SkBitmap bm;
+    bm.allocPixels(info);
+    auto result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
+    REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+    // This is an arbitrary value. The important fact is that it is not zero, and rowsDecoded
+    // should get set to zero by incrementalDecode.
+    int rowsDecoded = 77;
+    result = codec->incrementalDecode(&rowsDecoded);
+    REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
+    REPORTER_ASSERT(r, rowsDecoded == 0);
+}
+
+static void test_invalid_images(skiatest::Reporter* r, const char* path,
+                                SkCodec::Result expectedResult) {
+    auto* stream = GetResourceAsStream(path);
+    if (!stream) {
+        return;
+    }
+
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream));
+    REPORTER_ASSERT(r, codec);
+
+    test_info(r, codec.get(), codec->getInfo().makeColorType(kN32_SkColorType), expectedResult,
+              nullptr);
+}
+
+DEF_TEST(Codec_InvalidImages, r) {
+    // ASAN will complain if there is an issue.
+    test_invalid_images(r, "invalid_images/skbug5887.gif", SkCodec::kErrorInInput);
+    test_invalid_images(r, "invalid_images/many-progressive-scans.jpg", SkCodec::kInvalidInput);
+    test_invalid_images(r, "invalid_images/b33251605.bmp", SkCodec::kIncompleteInput);
+    test_invalid_images(r, "invalid_images/bad_palette.png", SkCodec::kInvalidInput);
+}
+
+static void test_invalid_header(skiatest::Reporter* r, const char* path) {
+    SkString resourcePath = GetResourcePath(path);
+    std::unique_ptr<SkFILEStream> stream(new SkFILEStream(resourcePath.c_str()));
+    if (!stream->isValid()) {
+        return;
+    }
+
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
+    REPORTER_ASSERT(r, !codec);
+}
+
+DEF_TEST(Codec_InvalidHeader, r) {
+    test_invalid_header(r, "invalid_images/int_overflow.ico");
+
+    // These files report values that have caused problems with SkFILEStreams.
+    // They are invalid, and should not create SkCodecs.
+    test_invalid_header(r, "invalid_images/b33651913.bmp");
+    test_invalid_header(r, "invalid_images/b34778578.bmp");
+}
+
+DEF_TEST(Codec_InvalidAnimated, r) {
+    // ASAN will complain if there is an issue.
+    auto path = "invalid_images/skbug6046.gif";
+    auto* stream = GetResourceAsStream(path);
+    if (!stream) {
+        return;
+    }
+
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream));
+    REPORTER_ASSERT(r, codec);
+    if (!codec) {
+        return;
+    }
+
+    const auto info = codec->getInfo().makeColorType(kN32_SkColorType);
+    SkBitmap bm;
+    bm.allocPixels(info);
+
+    auto frameInfos = codec->getFrameInfo();
+    SkCodec::Options opts;
+    for (int i = 0; static_cast<size_t>(i) < frameInfos.size(); i++) {
+        opts.fFrameIndex = i;
+        const auto reqFrame = frameInfos[i].fRequiredFrame;
+        opts.fPriorFrame = reqFrame == i - 1 ? reqFrame : SkCodec::kNone;
+        auto result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes(), &opts);
+        if (result != SkCodec::kSuccess) {
+            ERRORF(r, "Failed to start decoding frame %i (out of %i) with error %i\n", i,
+                   frameInfos.size(), result);
+            continue;
+        }
+
+        codec->incrementalDecode();
+    }
+}
+
+static void encode_format(SkDynamicMemoryWStream* stream, const SkPixmap& pixmap,
+                          SkTransferFunctionBehavior unpremulBehavior,
+                          SkEncodedImageFormat format) {
+    SkPngEncoder::Options pngOptions;
+    SkWebpEncoder::Options webpOptions;
+    pngOptions.fUnpremulBehavior = unpremulBehavior;
+    webpOptions.fUnpremulBehavior = unpremulBehavior;
+    switch (format) {
+        case SkEncodedImageFormat::kPNG:
+            SkPngEncoder::Encode(stream, pixmap, pngOptions);
+            break;
+        case SkEncodedImageFormat::kJPEG:
+            SkJpegEncoder::Encode(stream, pixmap, SkJpegEncoder::Options());
+            break;
+        case SkEncodedImageFormat::kWEBP:
+            SkWebpEncoder::Encode(stream, pixmap, webpOptions);
+            break;
+        default:
+            SkASSERT(false);
+            break;
+    }
+}
+
+static void test_encode_icc(skiatest::Reporter* r, SkEncodedImageFormat format,
+                            SkTransferFunctionBehavior unpremulBehavior) {
+    // Test with sRGB color space.
+    SkBitmap srgbBitmap;
+    SkImageInfo srgbInfo = SkImageInfo::MakeS32(1, 1, kOpaque_SkAlphaType);
+    srgbBitmap.allocPixels(srgbInfo);
+    *srgbBitmap.getAddr32(0, 0) = 0;
+    SkPixmap pixmap;
+    srgbBitmap.peekPixels(&pixmap);
+    SkDynamicMemoryWStream srgbBuf;
+    encode_format(&srgbBuf, pixmap, unpremulBehavior, format);
+    sk_sp<SkData> srgbData = srgbBuf.detachAsData();
+    std::unique_ptr<SkCodec> srgbCodec(SkCodec::NewFromData(srgbData));
+    REPORTER_ASSERT(r, srgbCodec->getInfo().colorSpace() == SkColorSpace::MakeSRGB().get());
+
+    // Test with P3 color space.
+    SkDynamicMemoryWStream p3Buf;
+    sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                                   SkColorSpace::kDCIP3_D65_Gamut);
+    pixmap.setColorSpace(p3);
+    encode_format(&p3Buf, pixmap, unpremulBehavior, format);
+    sk_sp<SkData> p3Data = p3Buf.detachAsData();
+    std::unique_ptr<SkCodec> p3Codec(SkCodec::NewFromData(p3Data));
+    REPORTER_ASSERT(r, p3Codec->getInfo().colorSpace()->gammaCloseToSRGB());
+    SkMatrix44 mat0(SkMatrix44::kUninitialized_Constructor);
+    SkMatrix44 mat1(SkMatrix44::kUninitialized_Constructor);
+    bool success = p3->toXYZD50(&mat0);
+    REPORTER_ASSERT(r, success);
+    success = p3Codec->getInfo().colorSpace()->toXYZD50(&mat1);
+    REPORTER_ASSERT(r, success);
+
+    for (int i = 0; i < 4; i++) {
+        for (int j = 0; j < 4; j++) {
+            REPORTER_ASSERT(r, color_space_almost_equal(mat0.get(i, j), mat1.get(i, j)));
+        }
+    }
+}
+
+DEF_TEST(Codec_EncodeICC, r) {
+    test_encode_icc(r, SkEncodedImageFormat::kPNG, SkTransferFunctionBehavior::kRespect);
+    test_encode_icc(r, SkEncodedImageFormat::kJPEG, SkTransferFunctionBehavior::kRespect);
+    test_encode_icc(r, SkEncodedImageFormat::kWEBP, SkTransferFunctionBehavior::kRespect);
+    test_encode_icc(r, SkEncodedImageFormat::kPNG, SkTransferFunctionBehavior::kIgnore);
+    test_encode_icc(r, SkEncodedImageFormat::kJPEG, SkTransferFunctionBehavior::kIgnore);
+    test_encode_icc(r, SkEncodedImageFormat::kWEBP, SkTransferFunctionBehavior::kIgnore);
+}
diff --git a/src/third_party/skia/tests/ColorFilterTest.cpp b/src/third_party/skia/tests/ColorFilterTest.cpp
index f3f6a0a..c502a70 100644
--- a/src/third_party/skia/tests/ColorFilterTest.cpp
+++ b/src/third_party/skia/tests/ColorFilterTest.cpp
@@ -5,18 +5,19 @@
  * found in the LICENSE file.
  */
 
+#include "SkAutoMalloc.h"
 #include "SkColor.h"
 #include "SkColorFilter.h"
 #include "SkColorPriv.h"
 #include "SkLumaColorFilter.h"
+#include "SkRandom.h"
 #include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
 #include "SkRandom.h"
-#include "SkXfermode.h"
 #include "Test.h"
 
-static SkColorFilter* reincarnate_colorfilter(SkFlattenable* obj) {
-    SkWriteBuffer wb;
+static sk_sp<SkColorFilter> reincarnate_colorfilter(SkFlattenable* obj) {
+    SkBinaryWriteBuffer wb;
     wb.writeFlattenable(obj);
 
     size_t size = wb.bytesWritten();
@@ -30,45 +31,63 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#define ILLEGAL_MODE    ((SkXfermode::Mode)-1)
+static sk_sp<SkColorFilter> make_filter() {
+    // pick a filter that cannot compose with itself via newComposed()
+    return SkColorFilter::MakeModeFilter(SK_ColorRED, SkBlendMode::kColorBurn);
+}
+
+static void test_composecolorfilter_limit(skiatest::Reporter* reporter) {
+    // Test that CreateComposeFilter() has some finite limit (i.e. that the factory can return null)
+    const int way_too_many = 100;
+    auto parent(make_filter());
+    for (int i = 2; i < way_too_many; ++i) {
+        auto filter(make_filter());
+        parent = SkColorFilter::MakeComposeFilter(parent, filter);
+        if (nullptr == parent) {
+            REPORTER_ASSERT(reporter, i > 2); // we need to have succeeded at least once!
+            return;
+        }
+    }
+    REPORTER_ASSERT(reporter, false); // we never saw a nullptr :(
+}
+
+#define ILLEGAL_MODE    ((SkBlendMode)-1)
 
 DEF_TEST(ColorFilter, reporter) {
     SkRandom rand;
 
-    for (int mode = 0; mode <= SkXfermode::kLastMode; mode++) {
+    for (int mode = 0; mode <= (int)SkBlendMode::kLastMode; mode++) {
         SkColor color = rand.nextU();
 
         // ensure we always get a filter, by avoiding the possibility of a
-        // special case that would return NULL (if color's alpha is 0 or 0xFF)
+        // special case that would return nullptr (if color's alpha is 0 or 0xFF)
         color = SkColorSetA(color, 0x7F);
 
-        SkColorFilter* cf = SkColorFilter::CreateModeFilter(color,
-                                                        (SkXfermode::Mode)mode);
+        auto cf = SkColorFilter::MakeModeFilter(color, (SkBlendMode)mode);
 
         // allow for no filter if we're in Dst mode (its a no op)
-        if (SkXfermode::kDst_Mode == mode && NULL == cf) {
+        if (SkBlendMode::kDst == (SkBlendMode)mode && nullptr == cf) {
             continue;
         }
 
-        SkAutoUnref aur(cf);
         REPORTER_ASSERT(reporter, cf);
 
         SkColor c = ~color;
-        SkXfermode::Mode m = ILLEGAL_MODE;
+        SkBlendMode m = ILLEGAL_MODE;
 
         SkColor expectedColor = color;
-        SkXfermode::Mode expectedMode = (SkXfermode::Mode)mode;
+        SkBlendMode expectedMode = (SkBlendMode)mode;
 
 //        SkDebugf("--- mc [%d %x] ", mode, color);
 
-        REPORTER_ASSERT(reporter, cf->asColorMode(&c, &m));
+        REPORTER_ASSERT(reporter, cf->asColorMode(&c, (SkBlendMode*)&m));
         // handle special-case folding by the factory
-        if (SkXfermode::kClear_Mode == mode) {
+        if (SkBlendMode::kClear == (SkBlendMode)mode) {
             if (c != expectedColor) {
                 expectedColor = 0;
             }
             if (m != expectedMode) {
-                expectedMode = SkXfermode::kSrc_Mode;
+                expectedMode = SkBlendMode::kSrc;
             }
         }
 
@@ -78,50 +97,16 @@
         REPORTER_ASSERT(reporter, m == expectedMode);
 
         {
-            SkColorFilter* cf2 = reincarnate_colorfilter(cf);
-            SkAutoUnref aur2(cf2);
+            auto cf2 = reincarnate_colorfilter(cf.get());
             REPORTER_ASSERT(reporter, cf2);
 
             SkColor c2 = ~color;
-            SkXfermode::Mode m2 = ILLEGAL_MODE;
-            REPORTER_ASSERT(reporter, cf2->asColorMode(&c2, &m2));
+            SkBlendMode m2 = ILLEGAL_MODE;
+            REPORTER_ASSERT(reporter, cf2->asColorMode(&c2, (SkBlendMode*)&m2));
             REPORTER_ASSERT(reporter, c2 == expectedColor);
             REPORTER_ASSERT(reporter, m2 == expectedMode);
         }
     }
-}
 
-///////////////////////////////////////////////////////////////////////////////
-
-DEF_TEST(LumaColorFilter, reporter) {
-    SkPMColor in, out;
-    SkAutoTUnref<SkColorFilter> lf(SkLumaColorFilter::Create());
-
-    // Applying luma to white produces black with the same transparency.
-    for (unsigned i = 0; i < 256; ++i) {
-        in = SkPackARGB32(i, i, i, i);
-        lf->filterSpan(&in, 1, &out);
-        REPORTER_ASSERT(reporter, SkGetPackedA32(out) == i);
-        REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0);
-        REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0);
-        REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0);
-    }
-
-    // Applying luma to black yields transparent black (luminance(black) == 0)
-    for (unsigned i = 0; i < 256; ++i) {
-        in = SkPackARGB32(i, 0, 0, 0);
-        lf->filterSpan(&in, 1, &out);
-        REPORTER_ASSERT(reporter, out == SK_ColorTRANSPARENT);
-    }
-
-    // For general colors, a luma filter generates black with an attenuated alpha channel.
-    for (unsigned i = 1; i < 256; ++i) {
-        in = SkPackARGB32(i, i, i / 2, i / 3);
-        lf->filterSpan(&in, 1, &out);
-        REPORTER_ASSERT(reporter, out != in);
-        REPORTER_ASSERT(reporter, SkGetPackedA32(out) <= i);
-        REPORTER_ASSERT(reporter, SkGetPackedR32(out) == 0);
-        REPORTER_ASSERT(reporter, SkGetPackedG32(out) == 0);
-        REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0);
-    }
+    test_composecolorfilter_limit(reporter);
 }
diff --git a/src/third_party/skia/tests/ColorMatrixTest.cpp b/src/third_party/skia/tests/ColorMatrixTest.cpp
new file mode 100644
index 0000000..7919b40
--- /dev/null
+++ b/src/third_party/skia/tests/ColorMatrixTest.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkColorMatrixFilter.h"
+#include "SkPaint.h"
+
+#include <stdlib.h>
+
+static inline void assert_color(skiatest::Reporter* reporter,
+                                SkColor expected, SkColor actual, int tolerance) {
+    REPORTER_ASSERT(reporter, abs((int)(SkColorGetA(expected) - SkColorGetA(actual))) <= tolerance);
+    REPORTER_ASSERT(reporter, abs((int)(SkColorGetR(expected) - SkColorGetR(actual))) <= tolerance);
+    REPORTER_ASSERT(reporter, abs((int)(SkColorGetG(expected) - SkColorGetG(actual))) <= tolerance);
+    REPORTER_ASSERT(reporter, abs((int)(SkColorGetB(expected) - SkColorGetB(actual))) <= tolerance);
+}
+
+static inline void assert_color(skiatest::Reporter* reporter, SkColor expected, SkColor actual) {
+    const int TOLERANCE = 1;
+    assert_color(reporter, expected, actual, TOLERANCE);
+}
+
+/**
+ * This test case is a mirror of the Android CTS tests for MatrixColorFilter
+ * found in the android.graphics.ColorMatrixColorFilterTest class.
+ */
+static inline void test_colorMatrixCTS(skiatest::Reporter* reporter) {
+
+    SkBitmap bitmap;
+    bitmap.allocN32Pixels(1,1);
+
+    SkCanvas canvas(bitmap);
+    SkPaint paint;
+
+    SkScalar blueToCyan[20] = {
+            1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+            0.0f, 1.0f, 1.0f, 0.0f, 0.0f,
+            0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+            0.0f, 0.0f, 0.0f, 1.0f, 0.0f };
+    paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(blueToCyan));
+
+    paint.setColor(SK_ColorBLUE);
+    canvas.drawPoint(0, 0, paint);
+    assert_color(reporter, SK_ColorCYAN, bitmap.getColor(0, 0));
+
+    paint.setColor(SK_ColorGREEN);
+    canvas.drawPoint(0, 0, paint);
+    assert_color(reporter, SK_ColorGREEN, bitmap.getColor(0, 0));
+
+    paint.setColor(SK_ColorRED);
+    canvas.drawPoint(0, 0, paint);
+    assert_color(reporter, SK_ColorRED, bitmap.getColor(0, 0));
+
+    // color components are clipped, not scaled
+    paint.setColor(SK_ColorMAGENTA);
+    canvas.drawPoint(0, 0, paint);
+    assert_color(reporter, SK_ColorWHITE, bitmap.getColor(0, 0));
+
+    SkScalar transparentRedAddBlue[20] = {
+            1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+            0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
+            0.0f, 0.0f, 1.0f, 0.0f, 64.0f,
+           -0.5f, 0.0f, 0.0f, 1.0f, 0.0f
+    };
+    paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(transparentRedAddBlue));
+    bitmap.eraseColor(SK_ColorTRANSPARENT);
+
+    paint.setColor(SK_ColorRED);
+    canvas.drawPoint(0, 0, paint);
+    assert_color(reporter, SkColorSetARGB(128, 255, 0, 64), bitmap.getColor(0, 0), 2);
+
+    paint.setColor(SK_ColorCYAN);
+    canvas.drawPoint(0, 0, paint);
+    // blue gets clipped
+    assert_color(reporter, SK_ColorCYAN, bitmap.getColor(0, 0));
+
+    // change array to filter out green
+    REPORTER_ASSERT(reporter, 1.0f == transparentRedAddBlue[6]);
+    transparentRedAddBlue[6] = 0.0f;
+
+    // check that changing the array has no effect
+    canvas.drawPoint(0, 0, paint);
+    assert_color(reporter, SK_ColorCYAN, bitmap.getColor(0, 0));
+
+    // create a new filter with the changed matrix
+    paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(transparentRedAddBlue));
+    canvas.drawPoint(0, 0, paint);
+    assert_color(reporter, SK_ColorBLUE, bitmap.getColor(0, 0));
+}
+
+DEF_TEST(ColorMatrix, reporter) {
+    test_colorMatrixCTS(reporter);
+}
diff --git a/src/third_party/skia/tests/ColorSpaceTest.cpp b/src/third_party/skia/tests/ColorSpaceTest.cpp
new file mode 100644
index 0000000..9b2e1b3
--- /dev/null
+++ b/src/third_party/skia/tests/ColorSpaceTest.cpp
@@ -0,0 +1,501 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Resources.h"
+#include "SkCodec.h"
+#include "SkColorSpace.h"
+#include "SkColorSpace_Base.h"
+#include "SkColorSpace_XYZ.h"
+#include "SkColorSpacePriv.h"
+#include "Test.h"
+
+#include "png.h"
+
+static bool almost_equal(float a, float b) {
+    return SkTAbs(a - b) < 0.001f;
+}
+
+static void test_space(skiatest::Reporter* r, SkColorSpace* space,
+                       const float red[], const float green[], const float blue[],
+                       const SkGammaNamed expectedGamma) {
+
+    REPORTER_ASSERT(r, nullptr != space);
+    SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(space)->type());
+    SkColorSpace_XYZ* csXYZ = static_cast<SkColorSpace_XYZ*>(space);
+    REPORTER_ASSERT(r, expectedGamma == csXYZ->gammaNamed());
+
+    const SkMatrix44& mat = *csXYZ->toXYZD50();
+    const float src[] = {
+        1, 0, 0, 1,
+        0, 1, 0, 1,
+        0, 0, 1, 1,
+    };
+    const float* ref[3] = { red, green, blue };
+    float dst[4];
+    for (int i = 0; i < 3; ++i) {
+        mat.mapScalars(&src[i*4], dst);
+        REPORTER_ASSERT(r, almost_equal(ref[i][0], dst[0]));
+        REPORTER_ASSERT(r, almost_equal(ref[i][1], dst[1]));
+        REPORTER_ASSERT(r, almost_equal(ref[i][2], dst[2]));
+    }
+}
+
+static void test_path(skiatest::Reporter* r, const char* path,
+                      const float red[], const float green[], const float blue[],
+                      const SkGammaNamed expectedGamma) {
+    std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
+    REPORTER_ASSERT(r, nullptr != stream);
+    if (!stream) {
+        return;
+    }
+
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
+    REPORTER_ASSERT(r, nullptr != codec);
+    if (!codec) {
+        return;
+    }
+
+    SkColorSpace* colorSpace = codec->getInfo().colorSpace();
+    test_space(r, colorSpace, red, green, blue, expectedGamma);
+}
+
+static constexpr float g_sRGB_XYZ[]{
+    0.4358f, 0.3853f, 0.1430f,    // Rx, Gx, Bx
+    0.2224f, 0.7170f, 0.0606f,    // Ry, Gy, Gz
+    0.0139f, 0.0971f, 0.7139f,    // Rz, Gz, Bz
+};
+
+static constexpr float g_sRGB_R[]{ 0.4358f, 0.2224f, 0.0139f };
+static constexpr float g_sRGB_G[]{ 0.3853f, 0.7170f, 0.0971f };
+static constexpr float g_sRGB_B[]{ 0.1430f, 0.0606f, 0.7139f };
+
+DEF_TEST(ColorSpace_sRGB, r) {
+    test_space(r, SkColorSpace::MakeSRGB().get(),
+               g_sRGB_R, g_sRGB_G, g_sRGB_B, kSRGB_SkGammaNamed);
+
+}
+
+DEF_TEST(ColorSpaceParseICCProfiles, r) {
+
+#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
+    test_path(r, "color_wheel_with_profile.png", g_sRGB_R, g_sRGB_G, g_sRGB_B,
+              kSRGB_SkGammaNamed);
+#endif
+
+    const float red[] = { 0.385117f, 0.716904f, 0.0970612f };
+    const float green[] = { 0.143051f, 0.0606079f, 0.713913f };
+    const float blue[] = { 0.436035f, 0.222488f, 0.013916f };
+    test_path(r, "icc-v2-gbr.jpg", red, green, blue, k2Dot2Curve_SkGammaNamed);
+
+    test_path(r, "webp-color-profile-crash.webp",
+            red, green, blue, kNonStandard_SkGammaNamed);
+    test_path(r, "webp-color-profile-lossless.webp",
+            red, green, blue, kNonStandard_SkGammaNamed);
+    test_path(r, "webp-color-profile-lossy.webp",
+            red, green, blue, kNonStandard_SkGammaNamed);
+    test_path(r, "webp-color-profile-lossy-alpha.webp",
+            red, green, blue, kNonStandard_SkGammaNamed);
+}
+
+DEF_TEST(ColorSpaceSRGBCompare, r) {
+    // Create an sRGB color space by name
+    sk_sp<SkColorSpace> namedColorSpace = SkColorSpace::MakeSRGB();
+
+    // Create an sRGB color space by value
+    SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor);
+    srgbToxyzD50.set3x3RowMajorf(g_sRGB_XYZ);
+    sk_sp<SkColorSpace> rgbColorSpace =
+            SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, srgbToxyzD50);
+    REPORTER_ASSERT(r, rgbColorSpace == namedColorSpace);
+
+    SkColorSpaceTransferFn srgbFn;
+    srgbFn.fA = (1.0f / 1.055f);
+    srgbFn.fB = (0.055f / 1.055f);
+    srgbFn.fC = (1.0f / 12.92f);
+    srgbFn.fD = 0.04045f;
+    srgbFn.fE = 0.0f;
+    srgbFn.fF = 0.0f;
+    srgbFn.fG = 2.4f;
+    sk_sp<SkColorSpace> rgbColorSpace2 = SkColorSpace::MakeRGB(srgbFn, srgbToxyzD50);
+    REPORTER_ASSERT(r, rgbColorSpace2 == namedColorSpace);
+
+    // Change a single value from the sRGB matrix
+    srgbToxyzD50.set(2, 2, 0.5f);
+    sk_sp<SkColorSpace> strangeColorSpace =
+            SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, srgbToxyzD50);
+    REPORTER_ASSERT(r, strangeColorSpace != namedColorSpace);
+}
+
+DEF_TEST(ColorSpaceSRGBLinearCompare, r) {
+    // Create the linear sRGB color space by name
+    sk_sp<SkColorSpace> namedColorSpace = SkColorSpace::MakeSRGBLinear();
+
+    // Create the linear sRGB color space via the sRGB color space's makeLinearGamma()
+    auto srgb = SkColorSpace::MakeSRGB();
+    auto srgbXYZ = static_cast<SkColorSpace_XYZ*>(srgb.get());
+    sk_sp<SkColorSpace> viaSrgbColorSpace = srgbXYZ->makeLinearGamma();
+    REPORTER_ASSERT(r, namedColorSpace == viaSrgbColorSpace);
+
+    // Create a linear sRGB color space by value
+    SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor);
+    srgbToxyzD50.set3x3RowMajorf(g_sRGB_XYZ);
+    sk_sp<SkColorSpace> rgbColorSpace =
+        SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, srgbToxyzD50);
+    REPORTER_ASSERT(r, rgbColorSpace == namedColorSpace);
+
+    SkColorSpaceTransferFn linearExpFn;
+    linearExpFn.fA = 1.0f;
+    linearExpFn.fB = 0.0f;
+    linearExpFn.fC = 0.0f;
+    linearExpFn.fD = 0.0f;
+    linearExpFn.fE = 0.0f;
+    linearExpFn.fF = 0.0f;
+    linearExpFn.fG = 1.0f;
+    sk_sp<SkColorSpace> rgbColorSpace2 = SkColorSpace::MakeRGB(linearExpFn, srgbToxyzD50);
+    REPORTER_ASSERT(r, rgbColorSpace2 == namedColorSpace);
+
+    SkColorSpaceTransferFn linearFn;
+    linearFn.fA = 0.0f;
+    linearFn.fB = 0.0f;
+    linearFn.fC = 1.0f;
+    linearFn.fD = 1.0f;
+    linearFn.fE = 0.0f;
+    linearFn.fF = 0.0f;
+    linearFn.fG = 0.0f;
+    sk_sp<SkColorSpace> rgbColorSpace3 = SkColorSpace::MakeRGB(linearFn, srgbToxyzD50);
+    REPORTER_ASSERT(r, rgbColorSpace3 == namedColorSpace);
+
+    // Change a single value from the sRGB matrix
+    srgbToxyzD50.set(2, 2, 0.5f);
+    sk_sp<SkColorSpace> strangeColorSpace =
+        SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, srgbToxyzD50);
+    REPORTER_ASSERT(r, strangeColorSpace != namedColorSpace);
+}
+
+DEF_TEST(ColorSpaceAdobeCompare, r) {
+    // Create an sRGB color space by name
+    sk_sp<SkColorSpace> namedColorSpace =
+            SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named);
+
+    // Create an sRGB color space by value
+    SkMatrix44 adobeToxyzD50(SkMatrix44::kUninitialized_Constructor);
+    adobeToxyzD50.set3x3RowMajorf(gAdobeRGB_toXYZD50);
+
+    SkColorSpaceTransferFn fn;
+    fn.fA = 1.0f;
+    fn.fB = 0.0f;
+    fn.fC = 0.0f;
+    fn.fD = 0.0f;
+    fn.fE = 0.0f;
+    fn.fF = 0.0f;
+    fn.fG = 2.2f;
+    sk_sp<SkColorSpace> rgbColorSpace = SkColorSpace::MakeRGB(fn, adobeToxyzD50);
+    REPORTER_ASSERT(r, rgbColorSpace == namedColorSpace);
+}
+
+DEF_TEST(ColorSpace_Named, r) {
+    const struct {
+        SkColorSpace_Base::Named fNamed;
+        SkGammaNamed             fExpectedGamma;
+    } recs[] {
+        { SkColorSpace_Base::kSRGB_Named,       kSRGB_SkGammaNamed },
+        { SkColorSpace_Base::kAdobeRGB_Named,   k2Dot2Curve_SkGammaNamed },
+        { SkColorSpace_Base::kSRGBLinear_Named, kLinear_SkGammaNamed },
+    };
+
+    for (auto rec : recs) {
+        auto cs = SkColorSpace_Base::MakeNamed(rec.fNamed);
+        REPORTER_ASSERT(r, cs);
+        if (cs) {
+            SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(cs)->type());
+            SkColorSpace_XYZ* csXYZ = static_cast<SkColorSpace_XYZ*>(cs.get());
+            REPORTER_ASSERT(r, rec.fExpectedGamma == csXYZ->gammaNamed());
+        }
+    }
+
+    SkImageInfo info = SkImageInfo::MakeS32(10, 10, kPremul_SkAlphaType);
+    REPORTER_ASSERT(r, info.gammaCloseToSRGB());
+}
+
+static void test_serialize(skiatest::Reporter* r, SkColorSpace* space, bool isNamed) {
+    sk_sp<SkData> data1 = space->serialize();
+
+    size_t bytes = space->writeToMemory(nullptr);
+    sk_sp<SkData> data2 = SkData::MakeUninitialized(bytes);
+    space->writeToMemory(data2->writable_data());
+
+    sk_sp<SkColorSpace> newSpace1 = SkColorSpace::Deserialize(data1->data(), data1->size());
+    sk_sp<SkColorSpace> newSpace2 = SkColorSpace::Deserialize(data2->data(), data2->size());
+
+    if (isNamed) {
+        REPORTER_ASSERT(r, space == newSpace1.get());
+        REPORTER_ASSERT(r, space == newSpace2.get());
+    } else {
+        REPORTER_ASSERT(r, SkColorSpace::Equals(space, newSpace1.get()));
+        REPORTER_ASSERT(r, SkColorSpace::Equals(space, newSpace2.get()));
+    }
+}
+
+DEF_TEST(ColorSpace_Serialize, r) {
+    test_serialize(r, SkColorSpace::MakeSRGB().get(), true);
+    test_serialize(r, SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named).get(), true);
+    test_serialize(r, SkColorSpace::MakeSRGBLinear().get(), true);
+
+    sk_sp<SkData> monitorData = SkData::MakeFromFileName(
+            GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str());
+    test_serialize(r, SkColorSpace::MakeICC(monitorData->data(), monitorData->size()).get(), false);
+    monitorData = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str());
+    test_serialize(r, SkColorSpace::MakeICC(monitorData->data(), monitorData->size()).get(), false);
+    monitorData = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str());
+    test_serialize(r, SkColorSpace::MakeICC(monitorData->data(), monitorData->size()).get(), false);
+    monitorData = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str());
+    test_serialize(r, SkColorSpace::MakeICC(monitorData->data(), monitorData->size()).get(), false);
+
+    SkColorSpaceTransferFn fn;
+    fn.fA = 1.0f;
+    fn.fB = 0.0f;
+    fn.fC = 1.0f;
+    fn.fD = 0.5f;
+    fn.fE = 0.0f;
+    fn.fF = 0.0f;
+    fn.fG = 1.0f;
+    SkMatrix44 toXYZ(SkMatrix44::kIdentity_Constructor);
+    test_serialize(r, SkColorSpace::MakeRGB(fn, toXYZ).get(), false);
+}
+
+DEF_TEST(ColorSpace_Equals, r) {
+    sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
+    sk_sp<SkColorSpace> adobe = SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named);
+    sk_sp<SkData> data = SkData::MakeFromFileName(
+            GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str());
+    sk_sp<SkColorSpace> z30 = SkColorSpace::MakeICC(data->data(), data->size());
+    data = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str());
+    sk_sp<SkColorSpace> z32 = SkColorSpace::MakeICC(data->data(), data->size());
+    data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str());
+    sk_sp<SkColorSpace> upperLeft = SkColorSpace::MakeICC(data->data(), data->size());
+    data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str());
+    sk_sp<SkColorSpace> upperRight = SkColorSpace::MakeICC(data->data(), data->size());
+
+    SkColorSpaceTransferFn fn;
+    fn.fA = 1.0f;
+    fn.fB = 0.0f;
+    fn.fC = 1.0f;
+    fn.fD = 0.5f;
+    fn.fE = 0.0f;
+    fn.fF = 0.0f;
+    fn.fG = 1.0f;
+    SkMatrix44 toXYZ(SkMatrix44::kIdentity_Constructor);
+    sk_sp<SkColorSpace> rgb4 = SkColorSpace::MakeRGB(fn, toXYZ);
+
+    REPORTER_ASSERT(r, SkColorSpace::Equals(nullptr, nullptr));
+    REPORTER_ASSERT(r, SkColorSpace::Equals(srgb.get(), srgb.get()));
+    REPORTER_ASSERT(r, SkColorSpace::Equals(adobe.get(), adobe.get()));
+    REPORTER_ASSERT(r, SkColorSpace::Equals(z30.get(), z30.get()));
+    REPORTER_ASSERT(r, SkColorSpace::Equals(z32.get(), z32.get()));
+    REPORTER_ASSERT(r, SkColorSpace::Equals(upperLeft.get(), upperLeft.get()));
+    REPORTER_ASSERT(r, SkColorSpace::Equals(upperRight.get(), upperRight.get()));
+    REPORTER_ASSERT(r, SkColorSpace::Equals(rgb4.get(), rgb4.get()));
+
+    REPORTER_ASSERT(r, !SkColorSpace::Equals(nullptr, srgb.get()));
+    REPORTER_ASSERT(r, !SkColorSpace::Equals(srgb.get(), nullptr));
+    REPORTER_ASSERT(r, !SkColorSpace::Equals(adobe.get(), srgb.get()));
+    REPORTER_ASSERT(r, !SkColorSpace::Equals(z30.get(), srgb.get()));
+    REPORTER_ASSERT(r, !SkColorSpace::Equals(z32.get(), z30.get()));
+    REPORTER_ASSERT(r, !SkColorSpace::Equals(upperLeft.get(), srgb.get()));
+    REPORTER_ASSERT(r, !SkColorSpace::Equals(upperLeft.get(), upperRight.get()));
+    REPORTER_ASSERT(r, !SkColorSpace::Equals(z30.get(), upperRight.get()));
+    REPORTER_ASSERT(r, !SkColorSpace::Equals(upperRight.get(), adobe.get()));
+    REPORTER_ASSERT(r, !SkColorSpace::Equals(z30.get(), rgb4.get()));
+    REPORTER_ASSERT(r, !SkColorSpace::Equals(srgb.get(), rgb4.get()));
+}
+
+static inline bool matrix_almost_equal(const SkMatrix44& a, const SkMatrix44& b) {
+    return almost_equal(a.get(0, 0), b.get(0, 0)) &&
+           almost_equal(a.get(0, 1), b.get(0, 1)) &&
+           almost_equal(a.get(0, 2), b.get(0, 2)) &&
+           almost_equal(a.get(0, 3), b.get(0, 3)) &&
+           almost_equal(a.get(1, 0), b.get(1, 0)) &&
+           almost_equal(a.get(1, 1), b.get(1, 1)) &&
+           almost_equal(a.get(1, 2), b.get(1, 2)) &&
+           almost_equal(a.get(1, 3), b.get(1, 3)) &&
+           almost_equal(a.get(2, 0), b.get(2, 0)) &&
+           almost_equal(a.get(2, 1), b.get(2, 1)) &&
+           almost_equal(a.get(2, 2), b.get(2, 2)) &&
+           almost_equal(a.get(2, 3), b.get(2, 3)) &&
+           almost_equal(a.get(3, 0), b.get(3, 0)) &&
+           almost_equal(a.get(3, 1), b.get(3, 1)) &&
+           almost_equal(a.get(3, 2), b.get(3, 2)) &&
+           almost_equal(a.get(3, 3), b.get(3, 3));
+}
+
+static inline void check_primaries(skiatest::Reporter* r, const SkColorSpacePrimaries& primaries,
+                                   const SkMatrix44& reference) {
+    SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor);
+    bool result = primaries.toXYZD50(&toXYZ);
+    REPORTER_ASSERT(r, result);
+    REPORTER_ASSERT(r, matrix_almost_equal(toXYZ, reference));
+}
+
+DEF_TEST(ColorSpace_Primaries, r) {
+    // sRGB primaries (D65)
+    SkColorSpacePrimaries srgb;
+    srgb.fRX = 0.64f;
+    srgb.fRY = 0.33f;
+    srgb.fGX = 0.30f;
+    srgb.fGY = 0.60f;
+    srgb.fBX = 0.15f;
+    srgb.fBY = 0.06f;
+    srgb.fWX = 0.3127f;
+    srgb.fWY = 0.3290f;
+    SkMatrix44 srgbToXYZ(SkMatrix44::kUninitialized_Constructor);
+    bool result = srgb.toXYZD50(&srgbToXYZ);
+    REPORTER_ASSERT(r, result);
+
+    sk_sp<SkColorSpace> space = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                                      srgbToXYZ);
+    REPORTER_ASSERT(r, SkColorSpace::MakeSRGB() == space);
+
+    // AdobeRGB primaries (D65)
+    SkColorSpacePrimaries adobe;
+    adobe.fRX = 0.64f;
+    adobe.fRY = 0.33f;
+    adobe.fGX = 0.21f;
+    adobe.fGY = 0.71f;
+    adobe.fBX = 0.15f;
+    adobe.fBY = 0.06f;
+    adobe.fWX = 0.3127f;
+    adobe.fWY = 0.3290f;
+    SkMatrix44 adobeToXYZ(SkMatrix44::kUninitialized_Constructor);
+    result = adobe.toXYZD50(&adobeToXYZ);
+    REPORTER_ASSERT(r, result);
+
+    SkColorSpaceTransferFn fn;
+    fn.fA = 1.0f;
+    fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f;
+    fn.fG = 2.2f;
+    space = SkColorSpace::MakeRGB(fn, adobeToXYZ);
+    REPORTER_ASSERT(r, SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named) == space);
+
+    // ProPhoto (D50)
+    SkColorSpacePrimaries proPhoto;
+    proPhoto.fRX = 0.7347f;
+    proPhoto.fRY = 0.2653f;
+    proPhoto.fGX = 0.1596f;
+    proPhoto.fGY = 0.8404f;
+    proPhoto.fBX = 0.0366f;
+    proPhoto.fBY = 0.0001f;
+    proPhoto.fWX = 0.34567f;
+    proPhoto.fWY = 0.35850f;
+    SkMatrix44 proToXYZ(SkMatrix44::kUninitialized_Constructor);
+    proToXYZ.set3x3(0.7976749f, 0.2880402f, 0.0000000f,
+                    0.1351917f, 0.7118741f, 0.0000000f,
+                    0.0313534f, 0.0000857f, 0.8252100f);
+    check_primaries(r, proPhoto, proToXYZ);
+
+    // NTSC (C)
+    SkColorSpacePrimaries ntsc;
+    ntsc.fRX = 0.67f;
+    ntsc.fRY = 0.33f;
+    ntsc.fGX = 0.21f;
+    ntsc.fGY = 0.71f;
+    ntsc.fBX = 0.14f;
+    ntsc.fBY = 0.08f;
+    ntsc.fWX = 0.31006f;
+    ntsc.fWY = 0.31616f;
+    SkMatrix44 ntscToXYZ(SkMatrix44::kUninitialized_Constructor);
+    ntscToXYZ.set3x3(0.6343706f, 0.3109496f, -0.0011817f,
+                     0.1852204f, 0.5915984f, 0.0555518f,
+                     0.1446290f, 0.0974520f, 0.7708399f);
+    check_primaries(r, ntsc, ntscToXYZ);
+
+    // DCI P3 (D65)
+    SkColorSpacePrimaries p3;
+    p3.fRX = 0.680f;
+    p3.fRY = 0.320f;
+    p3.fGX = 0.265f;
+    p3.fGY = 0.690f;
+    p3.fBX = 0.150f;
+    p3.fBY = 0.060f;
+    p3.fWX = 0.3127f;
+    p3.fWY = 0.3290f;
+    space = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                  SkColorSpace::kDCIP3_D65_Gamut);
+    SkMatrix44 reference(SkMatrix44::kUninitialized_Constructor);
+    SkAssertResult(space->toXYZD50(&reference));
+    check_primaries(r, p3, reference);
+
+    // Rec 2020 (D65)
+    SkColorSpacePrimaries rec2020;
+    rec2020.fRX = 0.708f;
+    rec2020.fRY = 0.292f;
+    rec2020.fGX = 0.170f;
+    rec2020.fGY = 0.797f;
+    rec2020.fBX = 0.131f;
+    rec2020.fBY = 0.046f;
+    rec2020.fWX = 0.3127f;
+    rec2020.fWY = 0.3290f;
+    space = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                  SkColorSpace::kRec2020_Gamut);
+    SkAssertResult(space->toXYZD50(&reference));
+    check_primaries(r, rec2020, reference);
+}
+
+DEF_TEST(ColorSpace_InvalidICC, r) {
+    // This color space has a matrix that is not D50.
+    sk_sp<SkData> data = GetResourceAsData("icc_profiles/SM2333SW.icc");
+    if (!data) {
+        return;
+    }
+    sk_sp<SkColorSpace> cs = SkColorSpace::MakeICC(data->data(), data->size());
+    REPORTER_ASSERT(r, !cs);
+
+    // The color space has a color lut with only one entry in each dimension.
+    data = GetResourceAsData("icc_profiles/invalid_color_lut.icc");
+    if (!data) {
+        return;
+    }
+
+    cs = SkColorSpace::MakeICC(data->data(), data->size());
+    REPORTER_ASSERT(r, !cs);
+}
+
+DEF_TEST(ColorSpace_MatrixHash, r) {
+    sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
+
+    SkColorSpaceTransferFn fn;
+    fn.fA = 1.0f;
+    fn.fB = 0.0f;
+    fn.fC = 0.0f;
+    fn.fD = 0.0f;
+    fn.fE = 0.0f;
+    fn.fF = 0.0f;
+    fn.fG = 3.0f;
+
+    SkMatrix44 srgbMat(SkMatrix44::kUninitialized_Constructor);
+    srgbMat.set3x3RowMajorf(gSRGB_toXYZD50);
+    sk_sp<SkColorSpace> strange = SkColorSpace::MakeRGB(fn, srgbMat);
+
+    REPORTER_ASSERT(r, *as_CSB(srgb)->toXYZD50() == *as_CSB(strange)->toXYZD50());
+    REPORTER_ASSERT(r, as_CSB(srgb)->toXYZD50Hash() == as_CSB(strange)->toXYZD50Hash());
+}
+
+DEF_TEST(ColorSpace_IsSRGB, r) {
+    sk_sp<SkColorSpace> srgb0 = SkColorSpace::MakeSRGB();
+
+    SkColorSpaceTransferFn fn;
+    fn.fA = 1.0f;
+    fn.fB = 0.0f;
+    fn.fC = 0.0f;
+    fn.fD = 0.0f;
+    fn.fE = 0.0f;
+    fn.fF = 0.0f;
+    fn.fG = 2.2f;
+    sk_sp<SkColorSpace> twoDotTwo = SkColorSpace::MakeRGB(fn, SkColorSpace::kSRGB_Gamut);
+
+    REPORTER_ASSERT(r, srgb0->isSRGB());
+    REPORTER_ASSERT(r, !twoDotTwo->isSRGB());
+}
diff --git a/src/third_party/skia/tests/ColorSpaceXformTest.cpp b/src/third_party/skia/tests/ColorSpaceXformTest.cpp
new file mode 100644
index 0000000..83317d9
--- /dev/null
+++ b/src/third_party/skia/tests/ColorSpaceXformTest.cpp
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Resources.h"
+#include "SkCodec.h"
+#include "SkCodecPriv.h"
+#include "SkColorPriv.h"
+#include "SkColorSpace.h"
+#include "SkColorSpace_A2B.h"
+#include "SkColorSpace_Base.h"
+#include "SkColorSpace_XYZ.h"
+#include "SkColorSpaceXform_Base.h"
+#include "Test.h"
+
+static constexpr int kChannels = 3;
+
+class ColorSpaceXformTest {
+public:
+    static std::unique_ptr<SkColorSpaceXform> CreateIdentityXform(const sk_sp<SkGammas>& gammas) {
+        // Logically we can pass any matrix here.  For simplicty, pass I(), i.e. D50 XYZ gamut.
+        sk_sp<SkColorSpace> space(new SkColorSpace_XYZ(
+                kNonStandard_SkGammaNamed, gammas, SkMatrix::I(), nullptr));
+
+        // Use special testing entry point, so we don't skip the xform, even though src == dst.
+        return SlowIdentityXform(static_cast<SkColorSpace_XYZ*>(space.get()));
+    }
+
+    static std::unique_ptr<SkColorSpaceXform> CreateIdentityXform_A2B(
+            SkGammaNamed gammaNamed, const sk_sp<SkGammas>& gammas) {
+        std::vector<SkColorSpace_A2B::Element> srcElements;
+        // sRGB
+        const float values[16] = {
+            0.4358f, 0.3853f, 0.1430f, 0.0f,
+            0.2224f, 0.7170f, 0.0606f, 0.0f,
+            0.0139f, 0.0971f, 0.7139f, 0.0f,
+            0.0000f, 0.0000f, 0.0000f, 1.0f
+        };
+        SkMatrix44 arbitraryMatrix{SkMatrix44::kUninitialized_Constructor};
+        arbitraryMatrix.setRowMajorf(values);
+        if (kNonStandard_SkGammaNamed == gammaNamed) {
+            SkASSERT(gammas);
+            srcElements.push_back(SkColorSpace_A2B::Element(gammas));
+        } else {
+            srcElements.push_back(SkColorSpace_A2B::Element(gammaNamed, kChannels));
+        }
+        srcElements.push_back(SkColorSpace_A2B::Element(arbitraryMatrix));
+        auto srcSpace =
+                ColorSpaceXformTest::CreateA2BSpace(SkColorSpace_A2B::PCS::kXYZ,
+                                                    SkColorSpace_Base::kRGB_ICCTypeFlag,
+                                                    std::move(srcElements));
+        sk_sp<SkColorSpace> dstSpace(new SkColorSpace_XYZ(gammaNamed, gammas, arbitraryMatrix,
+                                                          nullptr));
+
+        return SkColorSpaceXform::New(static_cast<SkColorSpace_A2B*>(srcSpace.get()),
+                                      static_cast<SkColorSpace_XYZ*>(dstSpace.get()));
+    }
+
+    static sk_sp<SkColorSpace> CreateA2BSpace(SkColorSpace_A2B::PCS pcs,
+                                              SkColorSpace_Base::ICCTypeFlag iccType,
+                                              std::vector<SkColorSpace_A2B::Element> elements) {
+        return sk_sp<SkColorSpace>(new SkColorSpace_A2B(iccType, std::move(elements),
+                                                        pcs, nullptr));
+    }
+};
+
+static bool almost_equal(int x, int y, int tol=1) {
+    return SkTAbs(x-y) <= tol;
+}
+
+static void test_identity_xform(skiatest::Reporter* r, const sk_sp<SkGammas>& gammas,
+                                bool repeat) {
+    // Arbitrary set of 10 pixels
+    constexpr int width = 10;
+    constexpr uint32_t srcPixels[width] = {
+            0xFFABCDEF, 0xFF146829, 0xFF382759, 0xFF184968, 0xFFDE8271,
+            0xFF32AB52, 0xFF0383BC, 0xFF000102, 0xFFFFFFFF, 0xFFDDEEFF, };
+    uint32_t dstPixels[width];
+
+    // Create and perform an identity xform.
+    std::unique_ptr<SkColorSpaceXform> xform = ColorSpaceXformTest::CreateIdentityXform(gammas);
+    bool result = xform->apply(select_xform_format(kN32_SkColorType), dstPixels,
+                               SkColorSpaceXform::kBGRA_8888_ColorFormat, srcPixels, width,
+                               kOpaque_SkAlphaType);
+    REPORTER_ASSERT(r, result);
+
+    // Since the src->dst matrix is the identity, and the gamma curves match,
+    // the pixels should be unchanged.
+    for (int i = 0; i < width; i++) {
+        REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >>  0) & 0xFF),
+                                        SkGetPackedB32(dstPixels[i])));
+        REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >>  8) & 0xFF),
+                                        SkGetPackedG32(dstPixels[i])));
+        REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 16) & 0xFF),
+                                        SkGetPackedR32(dstPixels[i])));
+        REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 24) & 0xFF),
+                                        SkGetPackedA32(dstPixels[i])));
+    }
+
+    if (repeat) {
+        // We should cache part of the transform after the run.  So it is interesting
+        // to make sure it still runs correctly the second time.
+        test_identity_xform(r, gammas, false);
+    }
+}
+
+static void test_identity_xform_A2B(skiatest::Reporter* r, SkGammaNamed gammaNamed,
+                                    const sk_sp<SkGammas>& gammas, int tol=1) {
+    // Arbitrary set of 10 pixels
+    constexpr int width = 10;
+    constexpr uint32_t srcPixels[width] = {
+            0xFFABCDEF, 0xFF146829, 0xFF382759, 0xFF184968, 0xFFDE8271,
+            0xFF32AB52, 0xFF0383BC, 0xFF000102, 0xFFFFFFFF, 0xFFDDEEFF, };
+    uint32_t dstPixels[width];
+
+    // Create and perform an identity xform.
+    auto xform = ColorSpaceXformTest::CreateIdentityXform_A2B(gammaNamed, gammas);
+    bool result = xform->apply(select_xform_format(kN32_SkColorType), dstPixels,
+                               SkColorSpaceXform::kBGRA_8888_ColorFormat, srcPixels, width,
+                               kOpaque_SkAlphaType);
+    REPORTER_ASSERT(r, result);
+
+    // Since the src->dst matrix is the identity, and the gamma curves match,
+    // the pixels should be ~unchanged.
+    for (int i = 0; i < width; i++) {
+        REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >>  0) & 0xFF),
+                                        SkGetPackedB32(dstPixels[i]), tol));
+        REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >>  8) & 0xFF),
+                                        SkGetPackedG32(dstPixels[i]), tol));
+        REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 16) & 0xFF),
+                                        SkGetPackedR32(dstPixels[i]), tol));
+        REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 24) & 0xFF),
+                                        SkGetPackedA32(dstPixels[i]), tol));
+    }
+}
+
+DEF_TEST(ColorSpaceXform_TableGamma, r) {
+    // Lookup-table based gamma curves
+    constexpr size_t tableSize = 10;
+    void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize);
+    sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(kChannels));
+    for (int i = 0; i < kChannels; ++i) {
+        gammas->fType[i] = SkGammas::Type::kTable_Type;
+        gammas->fData[i].fTable.fSize = tableSize;
+        gammas->fData[i].fTable.fOffset = 0;
+    }
+
+    float* table = SkTAddOffset<float>(memory, sizeof(SkGammas));
+    table[0] = 0.00f;
+    table[1] = 0.05f;
+    table[2] = 0.10f;
+    table[3] = 0.15f;
+    table[4] = 0.25f;
+    table[5] = 0.35f;
+    table[6] = 0.45f;
+    table[7] = 0.60f;
+    table[8] = 0.75f;
+    table[9] = 1.00f;
+    // This table's pretty small compared to real ones in the wild (think 256),
+    // so we give test_identity_xform_A2B a wide tolerance.
+    // This lets us implement table transfer functions with a single lookup.
+    const int tolerance = 13;
+
+    test_identity_xform(r, gammas, true);
+    test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, tolerance);
+}
+
+DEF_TEST(ColorSpaceXform_ParametricGamma, r) {
+    // Parametric gamma curves
+    void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(SkColorSpaceTransferFn));
+    sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(kChannels));
+    for (int i = 0; i < kChannels; ++i) {
+        gammas->fType[i] = SkGammas::Type::kParam_Type;
+        gammas->fData[i].fParamOffset = 0;
+    }
+
+    SkColorSpaceTransferFn* params = SkTAddOffset<SkColorSpaceTransferFn>
+            (memory, sizeof(SkGammas));
+
+    // Interval.
+    params->fD = 0.04045f;
+
+    // First equation:
+    params->fC = 1.0f / 12.92f;
+    params->fF = 0.0f;
+
+    // Second equation:
+    // Note that the function is continuous (it's actually sRGB).
+    params->fA = 1.0f / 1.055f;
+    params->fB = 0.055f / 1.055f;
+    params->fE = 0.0f;
+    params->fG = 2.4f;
+    test_identity_xform(r, gammas, true);
+    test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas);
+}
+
+DEF_TEST(ColorSpaceXform_ExponentialGamma, r) {
+    // Exponential gamma curves
+    sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas(kChannels));
+    for (int i = 0; i < kChannels; ++i) {
+        gammas->fType[i] = SkGammas::Type::kValue_Type;
+        gammas->fData[i].fValue = 1.4f;
+    }
+    test_identity_xform(r, gammas, true);
+    test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas);
+}
+
+DEF_TEST(ColorSpaceXform_NamedGamma, r) {
+    sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas(kChannels));
+    gammas->fType[0] = gammas->fType[1] = gammas->fType[2] = SkGammas::Type::kNamed_Type;
+    gammas->fData[0].fNamed = kSRGB_SkGammaNamed;
+    gammas->fData[1].fNamed = k2Dot2Curve_SkGammaNamed;
+    gammas->fData[2].fNamed = kLinear_SkGammaNamed;
+    test_identity_xform(r, gammas, true);
+    test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas);
+    test_identity_xform_A2B(r, kSRGB_SkGammaNamed, nullptr);
+    test_identity_xform_A2B(r, k2Dot2Curve_SkGammaNamed, nullptr);
+    test_identity_xform_A2B(r, kLinear_SkGammaNamed, nullptr);
+}
+
+DEF_TEST(ColorSpaceXform_NonMatchingGamma, r) {
+    constexpr size_t tableSize = 10;
+    void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize +
+                                   sizeof(SkColorSpaceTransferFn));
+    sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(kChannels));
+
+    float* table = SkTAddOffset<float>(memory, sizeof(SkGammas));
+    table[0] = 0.00f;
+    table[1] = 0.15f;
+    table[2] = 0.20f;
+    table[3] = 0.25f;
+    table[4] = 0.35f;
+    table[5] = 0.45f;
+    table[6] = 0.55f;
+    table[7] = 0.70f;
+    table[8] = 0.85f;
+    table[9] = 1.00f;
+
+    SkColorSpaceTransferFn* params = SkTAddOffset<SkColorSpaceTransferFn>(memory,
+            sizeof(SkGammas) + sizeof(float) * tableSize);
+    params->fA = 1.0f / 1.055f;
+    params->fB = 0.055f / 1.055f;
+    params->fC = 1.0f / 12.92f;
+    params->fD = 0.04045f;
+    params->fE = 0.0f;
+    params->fF = 0.0f;
+    params->fG = 2.4f;
+
+    gammas->fType[0] = SkGammas::Type::kValue_Type;
+    gammas->fData[0].fValue = 1.2f;
+
+    // See ColorSpaceXform_TableGamma... we've decided to allow some tolerance
+    // for SkJumper's implementation of tables.
+    const int tolerance = 12;
+    gammas->fType[1] = SkGammas::Type::kTable_Type;
+    gammas->fData[1].fTable.fSize = tableSize;
+    gammas->fData[1].fTable.fOffset = 0;
+
+    gammas->fType[2] = SkGammas::Type::kParam_Type;
+    gammas->fData[2].fParamOffset = sizeof(float) * tableSize;
+
+    test_identity_xform(r, gammas, true);
+    test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, tolerance);
+}
+
+DEF_TEST(ColorSpaceXform_A2BCLUT, r) {
+    constexpr int inputChannels = 3;
+    constexpr int gp            = 4; // # grid points
+
+    constexpr int numEntries    = gp*gp*gp*3;
+    const uint8_t gridPoints[3] = {gp, gp, gp};
+    void* memory = sk_malloc_throw(sizeof(SkColorLookUpTable) + sizeof(float) * numEntries);
+    sk_sp<SkColorLookUpTable> colorLUT(new (memory) SkColorLookUpTable(inputChannels, gridPoints));
+    // make a CLUT that rotates R, G, and B ie R->G, G->B, B->R
+    float* table = SkTAddOffset<float>(memory, sizeof(SkColorLookUpTable));
+    for (int r = 0; r < gp; ++r) {
+        for (int g = 0; g < gp; ++g) {
+            for (int b = 0; b < gp; ++b) {
+                table[3*(gp*gp*r + gp*g + b) + 0] = g * (1.f / (gp - 1.f));
+                table[3*(gp*gp*r + gp*g + b) + 1] = b * (1.f / (gp - 1.f));
+                table[3*(gp*gp*r + gp*g + b) + 2] = r * (1.f / (gp - 1.f));
+            }
+        }
+    }
+
+    // build an even distribution of pixels every (7 / 255) steps
+    // to test the xform on
+    constexpr int pixelgp   = 7;
+    constexpr int numPixels = pixelgp*pixelgp*pixelgp;
+    SkAutoTMalloc<uint32_t> srcPixels(numPixels);
+    int srcIndex = 0;
+    for (int r = 0; r < pixelgp; ++r) {
+        for (int g = 0; g < pixelgp; ++g) {
+            for (int b = 0; b < pixelgp; ++b) {
+                const int red   = (int) (r * (255.f / (pixelgp - 1.f)));
+                const int green = (int) (g * (255.f / (pixelgp - 1.f)));
+                const int blue  = (int) (b * (255.f / (pixelgp - 1.f)));
+                srcPixels[srcIndex] = SkColorSetRGB(red, green, blue);
+                ++srcIndex;
+            }
+        }
+    }
+    SkAutoTMalloc<uint32_t> dstPixels(numPixels);
+
+    // src space is identity besides CLUT
+    std::vector<SkColorSpace_A2B::Element> srcElements;
+    srcElements.push_back(SkColorSpace_A2B::Element(std::move(colorLUT)));
+    auto srcSpace = ColorSpaceXformTest::CreateA2BSpace(SkColorSpace_A2B::PCS::kXYZ,
+                                                        SkColorSpace_Base::kRGB_ICCTypeFlag,
+                                                        std::move(srcElements));
+    // dst space is entirely identity
+    auto dstSpace = SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, SkMatrix44::I());
+    auto xform = SkColorSpaceXform::New(srcSpace.get(), dstSpace.get());
+    bool result = xform->apply(SkColorSpaceXform::kRGBA_8888_ColorFormat, dstPixels.get(),
+                               SkColorSpaceXform::kRGBA_8888_ColorFormat, srcPixels.get(),
+                               numPixels, kOpaque_SkAlphaType);
+    REPORTER_ASSERT(r, result);
+
+    for (int i = 0; i < numPixels; ++i) {
+        REPORTER_ASSERT(r, almost_equal(SkColorGetR(srcPixels[i]),
+                                        SkColorGetG(dstPixels[i])));
+        REPORTER_ASSERT(r, almost_equal(SkColorGetG(srcPixels[i]),
+                                        SkColorGetB(dstPixels[i])));
+        REPORTER_ASSERT(r, almost_equal(SkColorGetB(srcPixels[i]),
+                                        SkColorGetR(dstPixels[i])));
+    }
+}
+
+DEF_TEST(SkColorSpaceXform_LoadTail, r) {
+    std::unique_ptr<uint64_t[]> srcPixel(new uint64_t[1]);
+    srcPixel[0] = 0;
+    uint32_t dstPixel;
+    sk_sp<SkColorSpace> adobe = SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named);
+    sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
+    std::unique_ptr<SkColorSpaceXform> xform = SkColorSpaceXform::New(adobe.get(), srgb.get());
+
+    // ASAN will catch us if we read past the tail.
+    bool success = xform->apply(SkColorSpaceXform::kRGBA_8888_ColorFormat, &dstPixel,
+                                SkColorSpaceXform::kRGBA_U16_BE_ColorFormat, srcPixel.get(), 1,
+                                kUnpremul_SkAlphaType);
+    REPORTER_ASSERT(r, success);
+}
+
diff --git a/src/third_party/skia/tests/CopySurfaceTest.cpp b/src/third_party/skia/tests/CopySurfaceTest.cpp
new file mode 100644
index 0000000..8dfca0c
--- /dev/null
+++ b/src/third_party/skia/tests/CopySurfaceTest.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <initializer_list>
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrResourceProvider.h"
+#include "GrSurfaceContext.h"
+#include "GrSurfaceProxy.h"
+#include "GrTextureProxy.h"
+
+#include "SkUtils.h"
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CopySurface, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    static const int kW = 10;
+    static const int kH = 10;
+    static const size_t kRowBytes = sizeof(uint32_t) * kW;
+
+    GrSurfaceDesc baseDesc;
+    baseDesc.fConfig = kRGBA_8888_GrPixelConfig;
+    baseDesc.fWidth = kW;
+    baseDesc.fHeight = kH;
+
+    SkAutoTMalloc<uint32_t> srcPixels(kW * kH);
+    for (int i = 0; i < kW * kH; ++i) {
+        srcPixels.get()[i] = i;
+    }
+
+    SkAutoTMalloc<uint32_t> dstPixels(kW * kH);
+    for (int i = 0; i < kW * kH; ++i) {
+        dstPixels.get()[i] = ~i;
+    }
+
+    static const SkIRect kSrcRects[] {
+        { 0,  0, kW  , kH  },
+        {-1, -1, kW+1, kH+1},
+        { 1,  1, kW-1, kH-1},
+        { 5,  5, 6   , 6   },
+    };
+
+    static const SkIPoint kDstPoints[] {
+        { 0   ,  0   },
+        { 1   ,  1   },
+        { kW/2,  kH/4},
+        { kW-1,  kH-1},
+        { kW  ,  kH  },
+        { kW+1,  kH+2},
+        {-1   , -1   },
+    };
+
+    const SkImageInfo ii = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+
+    SkAutoTMalloc<uint32_t> read(kW * kH);
+
+    for (auto sOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) {
+        for (auto dOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) {
+            for (auto sFlags: {kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags}) {
+                for (auto dFlags: {kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags}) {
+                    for (auto srcRect : kSrcRects) {
+                        for (auto dstPoint : kDstPoints) {
+                            GrSurfaceDesc srcDesc = baseDesc;
+                            srcDesc.fOrigin = sOrigin;
+                            srcDesc.fFlags = sFlags;
+                            GrSurfaceDesc dstDesc = baseDesc;
+                            dstDesc.fOrigin = dOrigin;
+                            dstDesc.fFlags = dFlags;
+
+                            sk_sp<GrTextureProxy> src(GrSurfaceProxy::MakeDeferred(
+                                                                    context->resourceProvider(),
+                                                                    srcDesc, SkBudgeted::kNo,
+                                                                    srcPixels.get(),
+                                                                    kRowBytes));
+
+                            sk_sp<GrTextureProxy> dst(GrSurfaceProxy::MakeDeferred(
+                                                                    context->resourceProvider(),
+                                                                    dstDesc, SkBudgeted::kNo,
+                                                                    dstPixels.get(),
+                                                                    kRowBytes));
+                            if (!src || !dst) {
+                                ERRORF(reporter,
+                                       "Could not create surfaces for copy surface test.");
+                                continue;
+                            }
+
+                            sk_sp<GrSurfaceContext> dstContext =
+                                   context->contextPriv().makeWrappedSurfaceContext(std::move(dst),
+                                                                                    nullptr);
+
+                            bool result = dstContext->copy(src.get(), srcRect, dstPoint);
+
+                            bool expectedResult = true;
+                            SkIPoint dstOffset = { dstPoint.fX - srcRect.fLeft,
+                                                   dstPoint.fY - srcRect.fTop };
+                            SkIRect copiedDstRect = SkIRect::MakeXYWH(dstPoint.fX,
+                                                                      dstPoint.fY,
+                                                                      srcRect.width(),
+                                                                      srcRect.height());
+
+                            SkIRect copiedSrcRect;
+                            if (!copiedSrcRect.intersect(srcRect, SkIRect::MakeWH(kW, kH))) {
+                                expectedResult = false;
+                            } else {
+                                // If the src rect was clipped, apply same clipping to each side of
+                                // copied dst rect.
+                                copiedDstRect.fLeft += copiedSrcRect.fLeft - srcRect.fLeft;
+                                copiedDstRect.fTop += copiedSrcRect.fTop - srcRect.fTop;
+                                copiedDstRect.fRight -= copiedSrcRect.fRight - srcRect.fRight;
+                                copiedDstRect.fBottom -= copiedSrcRect.fBottom - srcRect.fBottom;
+                            }
+                            if (copiedDstRect.isEmpty() ||
+                                !copiedDstRect.intersect(SkIRect::MakeWH(kW, kH))) {
+                                expectedResult = false;
+                            }
+                            // To make the copied src rect correct we would apply any dst clipping
+                            // back to the src rect, but we don't use it again so don't bother.
+                            if (expectedResult != result) {
+                                ERRORF(reporter, "Expected return value %d from copySurface, got "
+                                       "%d.", expectedResult, result);
+                                continue;
+                            }
+
+                            if (!expectedResult || !result) {
+                                continue;
+                            }
+
+                            sk_memset32(read.get(), 0, kW * kH);
+                            if (!dstContext->readPixels(ii, read.get(), kRowBytes, 0, 0)) {
+                                ERRORF(reporter, "Error calling readPixels");
+                                continue;
+                            }
+
+                            bool abort = false;
+                            // Validate that pixels inside copiedDstRect received the correct value
+                            // from src and that those outside were not modified.
+                            for (int y = 0; y < kH && !abort; ++y) {
+                                for (int x = 0; x < kW; ++x) {
+                                    uint32_t r = read.get()[y * kW + x];
+                                    if (copiedDstRect.contains(x, y)) {
+                                        int sx = x - dstOffset.fX;
+                                        int sy = y - dstOffset.fY;
+                                        uint32_t s = srcPixels.get()[sy * kW + sx];
+                                        if (s != r) {
+                                            ERRORF(reporter, "Expected dst %d,%d to contain "
+                                                   "0x%08x copied from src location %d,%d. Got "
+                                                   "0x%08x", x, y, s, sx, sy, r);
+                                            abort = true;
+                                            break;
+                                        }
+                                    } else {
+                                        uint32_t d = dstPixels.get()[y * kW + x];
+                                        if (d != r) {
+                                            ERRORF(reporter, "Expected dst %d,%d to be unmodified ("
+                                                   "0x%08x). Got 0x%08x", x, y, d, r);
+                                            abort = true;
+                                            break;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+#endif
diff --git a/src/third_party/skia/tests/DFPathRendererTest.cpp b/src/third_party/skia/tests/DFPathRendererTest.cpp
new file mode 100644
index 0000000..653db82
--- /dev/null
+++ b/src/third_party/skia/tests/DFPathRendererTest.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#include "SkPath.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "ops/GrSmallPathRenderer.h"
+
+#if 0
+// This test case including path coords and matrix taken from crbug.com/627443.
+// Because of inaccuracies in large floating point values this causes the
+// the path renderer to attempt to add a path DF to its atlas that is larger
+// than the plot size which used to crash rather than fail gracefully.
+static void test_far_from_origin(GrContext* ctx, GrRenderTargetContext* renderTargetContext,
+                                 GrPathRenderer* pr) {
+    SkPath path;
+    path.lineTo(49.0255089839f, 0.473541f);
+    // This extra line wasn't in the original bug but was added to fake out GrShape's special
+    // handling of single line segments.
+    path.rLineTo(0.015f, 0.015f);
+    static constexpr SkScalar mvals[] = {14.0348252854f, 2.13026182736f,
+                                         13.6122547187f, 118.309922702f,
+                                         1912337682.09f, 2105391889.87f};
+    SkMatrix matrix;
+    matrix.setAffine(mvals);
+    SkMatrix inverse;
+    SkAssertResult(matrix.invert(&inverse));
+    path.transform(inverse);
+
+    SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
+    rec.setStrokeStyle(1.f);
+    rec.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 1.f);
+    GrStyle style(rec, nullptr);
+
+    GrShape shape(path, style);
+    shape = shape.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, 1.f);
+
+    GrPaint paint;
+    paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
+
+    GrNoClip noClip;
+    GrPathRenderer::DrawPathArgs args{ctx,
+                                      std::move(paint),
+                                      &GrUserStencilSettings::kUnused,
+                                      renderTargetContext,
+                                      &noClip,
+                                      &matrix,
+                                      &shape,
+                                      GrAAType::kCoverage,
+                                      false};
+    pr->drawPath(args);
+}
+
+DEF_GPUTEST_FOR_ALL_GL_CONTEXTS(SmallPathRenderer, reporter, ctxInfo) {
+    GrContext* ctx = ctxInfo.grContext();
+    // The DF PR only works with contexts that support derivatives
+    if (!ctx->caps()->shaderCaps()->shaderDerivativeSupport()) {
+        return;
+    }
+    sk_sp<GrRenderTargetContext> rtc(ctx->makeRenderTargetContext(SkBackingFit::kApprox,
+                                                                  800, 800,
+                                                                  kRGBA_8888_GrPixelConfig,
+                                                                  nullptr,
+                                                                  0,
+                                                                  kTopLeft_GrSurfaceOrigin));
+    if (!rtc) {
+        return;
+    }
+
+    GrSmallPathRenderer spr;
+
+    ctx->flush();
+    test_far_from_origin(ctx, rtc.get(), &spr);
+    ctx->flush();
+}
+#endif
+#endif
diff --git a/src/third_party/skia/tests/DashPathEffectTest.cpp b/src/third_party/skia/tests/DashPathEffectTest.cpp
index 1e53058..e3a3804 100644
--- a/src/third_party/skia/tests/DashPathEffectTest.cpp
+++ b/src/third_party/skia/tests/DashPathEffectTest.cpp
@@ -1,21 +1,117 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #include "Test.h"
 
 #include "SkDashPathEffect.h"
 #include "SkWriteBuffer.h"
+#include "SkStrokeRec.h"
+#include "SkCanvas.h"
+#include "SkSurface.h"
 
 // crbug.com/348821 was rooted in SkDashPathEffect refusing to flatten and unflatten itself when
-// fInitialDashLength < 0 (a signal the effect is nonsense).  Here we test that it flattens.
+// the effect is nonsense.  Here we test that it fails when passed nonsense parameters.
 
 DEF_TEST(DashPathEffectTest_crbug_348821, r) {
     SkScalar intervals[] = { 1.76934361e+36f, 2.80259693e-45f };  // Values from bug.
     const int count = 2;
-    SkScalar phase = SK_ScalarInfinity;  // Used to force the bad fInitialDashLength = -1 path.
-    SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, count, phase));
+    SkScalar phase = SK_ScalarInfinity;  // Used to force a nonsense effect.
+    sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, count, phase));
 
-    // NULL -> refuses to work with flattening framework.
-    REPORTER_ASSERT(r, dash->getFactory() != NULL);
+    REPORTER_ASSERT(r, dash == nullptr);
+}
 
-    SkWriteBuffer buffer;
-    buffer.writeFlattenable(dash);
-    REPORTER_ASSERT(r, buffer.bytesWritten() > 12);  // We'd write 12 if broken, >=40 if not.
+// Test out the asPoint culling behavior.
+DEF_TEST(DashPathEffectTest_asPoints, r) {
+
+    const SkScalar intervals[] = { 1.0f, 1.0f };
+    const int count = 2;
+    sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, count, 0.0f));
+
+    SkRect cull = SkRect::MakeWH(1.0f, 1.0f);
+
+    const struct {
+        SkPoint fPts[2];
+        bool    fExpectedResult;
+    } testCases[] = {
+        { { { -5.0f,  0.5f }, { -4.0f,  0.5f } }, false },   // off to the left
+        { { {  4.0f,  0.5f }, {  5.0f,  0.5f } }, false },   // off to the right
+        { { {  0.5f,  4.0f }, {  0.5f,  5.0f } }, false },   // off the bottom
+        { { {  0.5f, -5.0f }, {  0.5f, -4.0f } }, false },   // off the top
+        { { {  0.5f,  0.2f }, {  0.5f,  0.8f } }, true  },   // entirely inside vertical
+        { { {  0.2f,  0.5f }, {  0.8f,  0.5f } }, true  },   // entirely inside horizontal
+        { { {  0.5f, -5.0f }, {  0.5f,  5.0f } }, true  },   // straddles both sides vertically
+        { { { -5.0f,  0.5f }, {  5.0f,  0.5f } }, true  },   // straddles both sides horizontally
+        { { {  0.5f, -5.0f }, {  0.5f,  0.5f } }, true  },   // straddles top
+        { { {  0.5f,  5.0f }, {  0.5f,  0.5f } }, true  },   // straddles bottom
+        { { { -5.0f,  0.5f }, {  0.5f,  0.5f } }, true  },   // straddles left
+        { { {  5.0f,  0.5f }, {  0.5f,  0.5f } }, true  },   // straddles right
+        { { {  0.5f,  0.5f }, {  0.5f,  0.5f } }, false },   // zero length
+    };
+
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(1.0f);
+    SkStrokeRec rec(paint);
+
+    static const int kNumMats = 3;
+    SkMatrix mats[kNumMats];
+    mats[0].reset();
+    mats[1].setRotate(90, 0.5f, 0.5f);
+    mats[2].setTranslate(10.0f, 10.0f);
+
+    for (int i = 0; i < kNumMats; ++i) {
+        for (int j = 0; j < (int)SK_ARRAY_COUNT(testCases); ++j) {
+            for (int k = 0; k < 2; ++k) {  // exercise alternating endpoints
+                SkPathEffect::PointData results;
+                SkPath src;
+
+                src.moveTo(testCases[j].fPts[k]);
+                src.lineTo(testCases[j].fPts[(k+1)%2]);
+
+                bool actualResult = dash->asPoints(&results, src, rec, mats[i], &cull);
+                if (i < 2) {
+                    REPORTER_ASSERT(r, actualResult == testCases[j].fExpectedResult);
+                } else {
+                    // On the third pass all the lines should be outside the translated cull rect
+                    REPORTER_ASSERT(r, !actualResult);
+                }
+            }
+        }
+    }
+}
+
+DEF_TEST(DashPath_bug4871, r) {
+    SkPath path;
+    path.moveTo(30, 24);
+    path.cubicTo(30.002f, 24, 30, 24, 30, 24);
+    path.close();
+
+    SkScalar intervals[2] = { 1, 1 };
+    sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0));
+
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setPathEffect(dash);
+
+    SkPath fill;
+    paint.getFillPath(path, &fill);
+}
+
+// Verify that long lines with many dashes don't cause overflows/OOMs.
+DEF_TEST(DashPathEffectTest_asPoints_limit, r) {
+    sk_sp<SkSurface> surface(SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(256, 256)));
+    SkCanvas* canvas = surface->getCanvas();
+
+    SkPaint p;
+    p.setStyle(SkPaint::kStroke_Style);
+    // force the bounds to outset by a large amount
+    p.setStrokeWidth(5.0e10f);
+    const SkScalar intervals[] = { 1, 1 };
+    p.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0));
+    canvas->drawLine(1, 1, 1, 5.0e10f, p);
 }
diff --git a/src/third_party/skia/tests/DataRefTest.cpp b/src/third_party/skia/tests/DataRefTest.cpp
index 6a58c16..74d511c 100644
--- a/src/third_party/skia/tests/DataRefTest.cpp
+++ b/src/third_party/skia/tests/DataRefTest.cpp
@@ -8,9 +8,11 @@
 #include "SkData.h"
 #include "SkDataTable.h"
 #include "SkOSFile.h"
+#include "SkOSPath.h"
 #include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
 #include "SkStream.h"
+#include "SkTArray.h"
 #include "Test.h"
 
 static void test_is_equal(skiatest::Reporter* reporter,
@@ -25,35 +27,31 @@
     }
 }
 
-static void test_datatable_is_empty(skiatest::Reporter* reporter,
-                                    SkDataTable* table) {
+static void test_datatable_is_empty(skiatest::Reporter* reporter, SkDataTable* table) {
     REPORTER_ASSERT(reporter, table->isEmpty());
     REPORTER_ASSERT(reporter, 0 == table->count());
 }
 
 static void test_emptytable(skiatest::Reporter* reporter) {
-    SkAutoTUnref<SkDataTable> table0(SkDataTable::NewEmpty());
-    SkAutoTUnref<SkDataTable> table1(SkDataTable::NewCopyArrays(NULL, NULL, 0));
-    SkAutoTUnref<SkDataTable> table2(SkDataTable::NewCopyArray(NULL, 0, 0));
-    SkAutoTUnref<SkDataTable> table3(SkDataTable::NewArrayProc(NULL, 0, 0,
-                                                               NULL, NULL));
+    sk_sp<SkDataTable> table0(SkDataTable::MakeEmpty());
+    sk_sp<SkDataTable> table1(SkDataTable::MakeCopyArrays(nullptr, nullptr, 0));
+    sk_sp<SkDataTable> table2(SkDataTable::MakeCopyArray(nullptr, 0, 0));
+    sk_sp<SkDataTable> table3(SkDataTable::MakeArrayProc(nullptr, 0, 0, nullptr, nullptr));
 
-    test_datatable_is_empty(reporter, table0);
-    test_datatable_is_empty(reporter, table1);
-    test_datatable_is_empty(reporter, table2);
-    test_datatable_is_empty(reporter, table3);
+    test_datatable_is_empty(reporter, table0.get());
+    test_datatable_is_empty(reporter, table1.get());
+    test_datatable_is_empty(reporter, table2.get());
+    test_datatable_is_empty(reporter, table3.get());
 
-    test_is_equal(reporter, table0, table1);
-    test_is_equal(reporter, table0, table2);
-    test_is_equal(reporter, table0, table3);
+    test_is_equal(reporter, table0.get(), table1.get());
+    test_is_equal(reporter, table0.get(), table2.get());
+    test_is_equal(reporter, table0.get(), table3.get());
 }
 
 static void test_simpletable(skiatest::Reporter* reporter) {
     const int idata[] = { 1, 4, 9, 16, 25, 63 };
     int icount = SK_ARRAY_COUNT(idata);
-    SkAutoTUnref<SkDataTable> itable(SkDataTable::NewCopyArray(idata,
-                                                               sizeof(idata[0]),
-                                                               icount));
+    sk_sp<SkDataTable> itable(SkDataTable::MakeCopyArray(idata, sizeof(idata[0]), icount));
     REPORTER_ASSERT(reporter, itable->count() == icount);
     for (int i = 0; i < icount; ++i) {
         size_t size;
@@ -73,8 +71,7 @@
         sizes[i] = strlen(str[i]) + 1;
     }
 
-    SkAutoTUnref<SkDataTable> table(SkDataTable::NewCopyArrays(
-                                        (const void*const*)str, sizes, count));
+    sk_sp<SkDataTable> table(SkDataTable::MakeCopyArrays((const void*const*)str, sizes, count));
 
     REPORTER_ASSERT(reporter, table->count() == count);
     for (int i = 0; i < count; ++i) {
@@ -89,40 +86,14 @@
     }
 }
 
-static void test_tablebuilder(skiatest::Reporter* reporter) {
-    const char* str[] = {
-        "", "a", "be", "see", "deigh", "ef", "ggggggggggggggggggggggggggg"
-    };
-    int count = SK_ARRAY_COUNT(str);
-
-    SkDataTableBuilder builder(16);
-
-    for (int i = 0; i < count; ++i) {
-        builder.append(str[i], strlen(str[i]) + 1);
-    }
-    SkAutoTUnref<SkDataTable> table(builder.detachDataTable());
-
-    REPORTER_ASSERT(reporter, table->count() == count);
-    for (int i = 0; i < count; ++i) {
-        size_t size;
-        REPORTER_ASSERT(reporter, table->atSize(i) == strlen(str[i]) + 1);
-        REPORTER_ASSERT(reporter, !strcmp(table->atT<const char>(i, &size),
-                                          str[i]));
-        REPORTER_ASSERT(reporter, size == strlen(str[i]) + 1);
-
-        const char* s = table->atStr(i);
-        REPORTER_ASSERT(reporter, strlen(s) == strlen(str[i]));
-    }
-}
-
 static void test_globaltable(skiatest::Reporter* reporter) {
     static const int gData[] = {
         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
     };
     int count = SK_ARRAY_COUNT(gData);
 
-    SkAutoTUnref<SkDataTable> table(SkDataTable::NewArrayProc(gData,
-                                          sizeof(gData[0]), count, NULL, NULL));
+    sk_sp<SkDataTable> table(
+        SkDataTable::MakeArrayProc(gData, sizeof(gData[0]), count, nullptr, nullptr));
 
     REPORTER_ASSERT(reporter, table->count() == count);
     for (int i = 0; i < count; ++i) {
@@ -137,23 +108,22 @@
     test_emptytable(reporter);
     test_simpletable(reporter);
     test_vartable(reporter);
-    test_tablebuilder(reporter);
     test_globaltable(reporter);
 }
 
 static void* gGlobal;
 
-static void delete_int_proc(const void* ptr, size_t len, void* context) {
+static void delete_int_proc(const void* ptr, void* context) {
     int* data = (int*)ptr;
     SkASSERT(context == gGlobal);
     delete[] data;
 }
 
-static void assert_len(skiatest::Reporter* reporter, SkData* ref, size_t len) {
+static void assert_len(skiatest::Reporter* reporter, const sk_sp<SkData>& ref, size_t len) {
     REPORTER_ASSERT(reporter, ref->size() == len);
 }
 
-static void assert_data(skiatest::Reporter* reporter, SkData* ref,
+static void assert_data(skiatest::Reporter* reporter, const sk_sp<SkData>& ref,
                         const void* data, size_t len) {
     REPORTER_ASSERT(reporter, ref->size() == len);
     REPORTER_ASSERT(reporter, !memcmp(ref->data(), data, len));
@@ -163,18 +133,18 @@
     const char str[] = "Hello world";
     size_t     len = strlen(str);
 
-    SkAutoTUnref<SkData> r0(SkData::NewWithCopy(str, len + 1));
-    SkAutoTUnref<SkData> r1(SkData::NewWithCString(str));
+    sk_sp<SkData> r0(SkData::MakeWithCopy(str, len + 1));
+    sk_sp<SkData> r1(SkData::MakeWithCString(str));
 
-    REPORTER_ASSERT(reporter, r0->equals(r1));
+    REPORTER_ASSERT(reporter, r0->equals(r1.get()));
 
-    SkAutoTUnref<SkData> r2(SkData::NewWithCString(NULL));
+    sk_sp<SkData> r2(SkData::MakeWithCString(nullptr));
     REPORTER_ASSERT(reporter, 1 == r2->size());
     REPORTER_ASSERT(reporter, 0 == *r2->bytes());
 }
 
 static void test_files(skiatest::Reporter* reporter) {
-    SkString tmpDir = skiatest::Test::GetTmpDir();
+    SkString tmpDir = skiatest::GetTmpDir();
     if (tmpDir.isEmpty()) {
         return;
     }
@@ -191,15 +161,15 @@
         writer.write(s, 26);
     }
 
-    SkFILE* file = sk_fopen(path.c_str(), kRead_SkFILE_Flag);
-    SkAutoTUnref<SkData> r1(SkData::NewFromFILE(file));
-    REPORTER_ASSERT(reporter, r1.get() != NULL);
+    FILE* file = sk_fopen(path.c_str(), kRead_SkFILE_Flag);
+    sk_sp<SkData> r1(SkData::MakeFromFILE(file));
+    REPORTER_ASSERT(reporter, r1.get() != nullptr);
     REPORTER_ASSERT(reporter, r1->size() == 26);
     REPORTER_ASSERT(reporter, strncmp(static_cast<const char*>(r1->data()), s, 26) == 0);
 
     int fd = sk_fileno(file);
-    SkAutoTUnref<SkData> r2(SkData::NewFromFD(fd));
-    REPORTER_ASSERT(reporter, r2.get() != NULL);
+    sk_sp<SkData> r2(SkData::MakeFromFD(fd));
+    REPORTER_ASSERT(reporter, r2.get() != nullptr);
     REPORTER_ASSERT(reporter, r2->size() == 26);
     REPORTER_ASSERT(reporter, strncmp(static_cast<const char*>(r2->data()), s, 26) == 0);
 }
@@ -208,11 +178,10 @@
     const char* str = "We the people, in order to form a more perfect union.";
     const int N = 10;
 
-    SkAutoTUnref<SkData> r0(SkData::NewEmpty());
-    SkAutoTUnref<SkData> r1(SkData::NewWithCopy(str, strlen(str)));
-    SkAutoTUnref<SkData> r2(SkData::NewWithProc(new int[N], N*sizeof(int),
-                                           delete_int_proc, gGlobal));
-    SkAutoTUnref<SkData> r3(SkData::NewSubset(r1, 7, 6));
+    sk_sp<SkData> r0(SkData::MakeEmpty());
+    sk_sp<SkData> r1(SkData::MakeWithCopy(str, strlen(str)));
+    sk_sp<SkData> r2(SkData::MakeWithProc(new int[N], N*sizeof(int), delete_int_proc, gGlobal));
+    sk_sp<SkData> r3(SkData::MakeSubset(r1.get(), 7, 6));
 
     assert_len(reporter, r0, 0);
     assert_len(reporter, r1, strlen(str));
@@ -222,13 +191,158 @@
     assert_data(reporter, r1, str, strlen(str));
     assert_data(reporter, r3, "people", 6);
 
-    SkData* tmp = SkData::NewSubset(r1, strlen(str), 10);
+    sk_sp<SkData> tmp(SkData::MakeSubset(r1.get(), strlen(str), 10));
     assert_len(reporter, tmp, 0);
-    tmp->unref();
-    tmp = SkData::NewSubset(r1, 0, 0);
+    tmp = SkData::MakeSubset(r1.get(), 0, 0);
     assert_len(reporter, tmp, 0);
-    tmp->unref();
 
     test_cstring(reporter);
     test_files(reporter);
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#include "SkRWBuffer.h"
+
+const char gABC[] = "abcdefghijklmnopqrstuvwxyz";
+
+static void check_abcs(skiatest::Reporter* reporter, const char buffer[], size_t size) {
+    REPORTER_ASSERT(reporter, size % 26 == 0);
+    for (size_t offset = 0; offset < size; offset += 26) {
+        REPORTER_ASSERT(reporter, !memcmp(&buffer[offset], gABC, 26));
+    }
+}
+
+// stream should contain an integral number of copies of gABC.
+static void check_alphabet_stream(skiatest::Reporter* reporter, SkStream* stream) {
+    REPORTER_ASSERT(reporter, stream->hasLength());
+    size_t size = stream->getLength();
+    REPORTER_ASSERT(reporter, size % 26 == 0);
+
+    SkAutoTMalloc<char> storage(size);
+    char* array = storage.get();
+    size_t bytesRead = stream->read(array, size);
+    REPORTER_ASSERT(reporter, bytesRead == size);
+    check_abcs(reporter, array, size);
+
+    // try checking backwards
+    for (size_t offset = size; offset > 0; offset -= 26) {
+        REPORTER_ASSERT(reporter, stream->seek(offset - 26));
+        REPORTER_ASSERT(reporter, stream->getPosition() == offset - 26);
+        REPORTER_ASSERT(reporter, stream->read(array, 26) == 26);
+        check_abcs(reporter, array, 26);
+        REPORTER_ASSERT(reporter, stream->getPosition() == offset);
+    }
+}
+
+// reader should contains an integral number of copies of gABC.
+static void check_alphabet_buffer(skiatest::Reporter* reporter, const SkROBuffer* reader) {
+    size_t size = reader->size();
+    REPORTER_ASSERT(reporter, size % 26 == 0);
+
+    SkAutoTMalloc<char> storage(size);
+    SkROBuffer::Iter iter(reader);
+    size_t offset = 0;
+    do {
+        SkASSERT(offset + iter.size() <= size);
+        memcpy(storage.get() + offset, iter.data(), iter.size());
+        offset += iter.size();
+    } while (iter.next());
+    REPORTER_ASSERT(reporter, offset == size);
+    check_abcs(reporter, storage.get(), size);
+}
+
+#include "SkTaskGroup.h"
+
+DEF_TEST(RWBuffer, reporter) {
+    // Knowing that the default capacity is 4096, choose N large enough so we force it to use
+    // multiple buffers internally.
+    static constexpr int N = 1000;
+    SkSTArray<N, sk_sp<SkROBuffer>> readers;
+    SkSTArray<N, std::unique_ptr<SkStream>> streams;
+
+    {
+        SkRWBuffer buffer;
+        for (int i = 0; i < N; ++i) {
+            buffer.append(gABC, 26);
+            readers.push_back(buffer.makeROBufferSnapshot());
+            streams.push_back(buffer.makeStreamSnapshot());
+        }
+        REPORTER_ASSERT(reporter, N*26 == buffer.size());
+    }
+
+    // Verify that although the SkRWBuffer's destructor has run, the readers are still valid.
+    for (int i = 0; i < N; ++i) {
+        REPORTER_ASSERT(reporter, (i + 1) * 26U == readers[i]->size());
+        check_alphabet_buffer(reporter, readers[i].get());
+        check_alphabet_stream(reporter, streams[i].get());
+    }
+}
+
+DEF_TEST(RWBuffer_threaded, reporter) {
+    // Knowing that the default capacity is 4096, choose N large enough so we force it to use
+    // multiple buffers internally.
+    const int N = 1000;
+    SkTaskGroup tasks;
+    SkRWBuffer buffer;
+    for (int i = 0; i < N; ++i) {
+        buffer.append(gABC, 26);
+        sk_sp<SkROBuffer> reader = buffer.makeROBufferSnapshot();
+        SkStream* stream = buffer.makeStreamSnapshot().release();
+        REPORTER_ASSERT(reporter, reader->size() == buffer.size());
+        REPORTER_ASSERT(reporter, stream->getLength() == buffer.size());
+
+        // reader's copy constructor will ref the SkROBuffer, which will be unreffed
+        // when the task ends.
+        // Ownership of stream is passed to the task, which will delete it.
+        tasks.add([reporter, i, reader, stream] {
+            REPORTER_ASSERT(reporter, (i + 1) * 26U == reader->size());
+            REPORTER_ASSERT(reporter, stream->getLength() == reader->size());
+            check_alphabet_buffer(reporter, reader.get());
+            check_alphabet_stream(reporter, stream);
+            REPORTER_ASSERT(reporter, stream->rewind());
+            delete stream;
+        });
+    }
+    REPORTER_ASSERT(reporter, N*26 == buffer.size());
+    tasks.wait();
+}
+
+// Tests that it is safe to call SkROBuffer::Iter::size() when exhausted.
+DEF_TEST(RWBuffer_size, r) {
+    SkRWBuffer buffer;
+    buffer.append(gABC, 26);
+
+    sk_sp<SkROBuffer> roBuffer(buffer.makeROBufferSnapshot());
+    SkROBuffer::Iter iter(roBuffer.get());
+    REPORTER_ASSERT(r, iter.data());
+    REPORTER_ASSERT(r, iter.size() == 26);
+
+    // There is only one block in this buffer.
+    REPORTER_ASSERT(r, !iter.next());
+    REPORTER_ASSERT(r, 0 == iter.size());
+}
+
+// Tests that operations (including the destructor) are safe on an SkRWBuffer
+// without any data appended.
+DEF_TEST(RWBuffer_noAppend, r) {
+    SkRWBuffer buffer;
+    REPORTER_ASSERT(r, 0 == buffer.size());
+
+    sk_sp<SkROBuffer> roBuffer = buffer.makeROBufferSnapshot();
+    REPORTER_ASSERT(r, roBuffer);
+    if (roBuffer) {
+        REPORTER_ASSERT(r, roBuffer->size() == 0);
+        SkROBuffer::Iter iter(roBuffer.get());
+        REPORTER_ASSERT(r, iter.size() == 0);
+        REPORTER_ASSERT(r, !iter.data());
+        REPORTER_ASSERT(r, !iter.next());
+    }
+
+    std::unique_ptr<SkStream> stream(buffer.makeStreamSnapshot());
+    REPORTER_ASSERT(r, stream);
+    if (stream) {
+        REPORTER_ASSERT(r, stream->hasLength());
+        REPORTER_ASSERT(r, stream->getLength() == 0);
+        REPORTER_ASSERT(r, stream->skip(10) == 0);
+    }
+}
diff --git a/src/third_party/skia/tests/DeferredCanvasTest.cpp b/src/third_party/skia/tests/DeferredCanvasTest.cpp
deleted file mode 100644
index d234c8c..0000000
--- a/src/third_party/skia/tests/DeferredCanvasTest.cpp
+++ /dev/null
@@ -1,916 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "../src/image/SkImagePriv.h"
-#include "../src/image/SkSurface_Base.h"
-#include "SkBitmap.h"
-#include "SkBitmapDevice.h"
-#include "SkBitmapProcShader.h"
-#include "SkDeferredCanvas.h"
-#include "SkGradientShader.h"
-#include "SkShader.h"
-#include "SkSurface.h"
-#include "Test.h"
-#include "sk_tool_utils.h"
-
-#if SK_SUPPORT_GPU
-#include "GrContextFactory.h"
-#else
-class GrContextFactory;
-#endif
-
-static const int gWidth = 2;
-static const int gHeight = 2;
-
-static void create(SkBitmap* bm, SkColor color) {
-    bm->allocN32Pixels(gWidth, gHeight);
-    bm->eraseColor(color);
-}
-
-static SkSurface* createSurface(SkColor color) {
-    SkSurface* surface = SkSurface::NewRasterPMColor(gWidth, gHeight);
-    surface->getCanvas()->clear(color);
-    return surface;
-}
-
-static SkPMColor read_pixel(SkSurface* surface, int x, int y) {
-    SkPMColor pixel = 0;
-    SkBitmap bitmap;
-    bitmap.installPixels(SkImageInfo::MakeN32Premul(1, 1), &pixel, 4);
-    SkCanvas canvas(bitmap);
-
-    SkPaint paint;
-    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-    surface->draw(&canvas, -SkIntToScalar(x), -SkIntToScalar(y), &paint);
-    return pixel;
-}
-
-class MockSurface : public SkSurface_Base {
-public:
-    MockSurface(int width, int height) : SkSurface_Base(width, height, NULL) {
-        clearCounts();
-        fBitmap.allocN32Pixels(width, height);
-    }
-
-    virtual SkCanvas* onNewCanvas() SK_OVERRIDE {
-        return SkNEW_ARGS(SkCanvas, (fBitmap));
-    }
-
-    virtual SkSurface* onNewSurface(const SkImageInfo&) SK_OVERRIDE {
-        return NULL;
-    }
-
-    virtual SkImage* onNewImageSnapshot() SK_OVERRIDE {
-        return SkNewImageFromBitmap(fBitmap, true);
-    }
-
-    virtual void onCopyOnWrite(ContentChangeMode mode) SK_OVERRIDE {
-        if (mode == SkSurface::kDiscard_ContentChangeMode) {
-            fDiscardCount++;
-        } else {
-            fRetainCount++;
-        }
-    }
-
-    void clearCounts() {
-        fDiscardCount = 0;
-        fRetainCount = 0;
-    }
-
-    int fDiscardCount, fRetainCount;
-    SkBitmap fBitmap;
-};
-
-static void TestDeferredCanvasWritePixelsToSurface(skiatest::Reporter* reporter) {
-    SkAutoTUnref<MockSurface> surface(SkNEW_ARGS(MockSurface, (10, 10)));
-    SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
-
-    SkBitmap srcBitmap;
-    srcBitmap.allocPixels(SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType));
-    srcBitmap.eraseColor(SK_ColorGREEN);
-    // Tests below depend on this bitmap being recognized as opaque
-
-    // Preliminary sanity check: no copy on write if no active snapshot
-    surface->clearCounts();
-    canvas->clear(SK_ColorWHITE);
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->flush();
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    // Case 1: Discard notification happens upon flushing
-    // with an Image attached.
-    surface->clearCounts();
-    SkAutoTUnref<SkImage> image1(canvas->newImageSnapshot());
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->clear(SK_ColorWHITE);
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->flush();
-    REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    // Case 2: Opaque writePixels
-    surface->clearCounts();
-    SkAutoTUnref<SkImage> image2(canvas->newImageSnapshot());
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    // Case 3: writePixels that partially covers the canvas
-    surface->clearCounts();
-    SkAutoTUnref<SkImage> image3(canvas->newImageSnapshot());
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    // Case 4: unpremultiplied opaque writePixels that entirely
-    // covers the canvas
-    surface->clearCounts();
-    SkAutoTUnref<SkImage> image4(canvas->newImageSnapshot());
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->writePixels(srcBitmap, 0, 0);
-    REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->flush();
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    // Case 5: unpremultiplied opaque writePixels that partially
-    // covers the canvas
-    surface->clearCounts();
-    SkAutoTUnref<SkImage> image5(canvas->newImageSnapshot());
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->writePixels(srcBitmap, 5, 0);
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 1 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->flush();
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    // Case 6: unpremultiplied opaque writePixels that entirely
-    // covers the canvas, preceded by clear
-    surface->clearCounts();
-    SkAutoTUnref<SkImage> image6(canvas->newImageSnapshot());
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->clear(SK_ColorWHITE);
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->writePixels(srcBitmap, 0, 0);
-    REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->flush();
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    // Case 7: unpremultiplied opaque writePixels that partially
-    // covers the canvas, preceeded by a clear
-    surface->clearCounts();
-    SkAutoTUnref<SkImage> image7(canvas->newImageSnapshot());
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->clear(SK_ColorWHITE);
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->writePixels(srcBitmap, 5, 0);
-    REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount); // because of the clear
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->flush();
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    // Case 8: unpremultiplied opaque writePixels that partially
-    // covers the canvas, preceeded by a drawREct that partially
-    // covers the canvas
-    surface->clearCounts();
-    SkAutoTUnref<SkImage> image8(canvas->newImageSnapshot());
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    SkPaint paint;
-    canvas->drawRect(SkRect::MakeLTRB(0, 0, 5, 5), paint);
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->writePixels(srcBitmap, 5, 0);
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 1 == surface->fRetainCount);
-
-    surface->clearCounts();
-    canvas->flush();
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
-}
-
-static void TestDeferredCanvasFlush(skiatest::Reporter* reporter) {
-    SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF));
-    SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
-
-    canvas->clear(0x00000000);
-
-    // verify that clear was deferred
-    REPORTER_ASSERT(reporter, 0xFFFFFFFF == read_pixel(surface, 0, 0));
-
-    canvas->flush();
-
-    // verify that clear was executed
-    REPORTER_ASSERT(reporter, 0 == read_pixel(surface, 0, 0));
-}
-
-static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
-    SkRect fullRect;
-    fullRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth),
-        SkIntToScalar(gHeight));
-    SkRect partialRect;
-    partialRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0),
-        SkIntToScalar(1), SkIntToScalar(1));
-
-    SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF));
-    SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
-
-    // verify that frame is intially fresh
-    REPORTER_ASSERT(reporter, canvas->isFreshFrame());
-    // no clearing op since last call to isFreshFrame -> not fresh
-    REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
-
-    // Verify that clear triggers a fresh frame
-    canvas->clear(0x00000000);
-    REPORTER_ASSERT(reporter, canvas->isFreshFrame());
-
-    // Verify that clear with saved state triggers a fresh frame
-    canvas->save();
-    canvas->clear(0x00000000);
-    canvas->restore();
-    REPORTER_ASSERT(reporter, canvas->isFreshFrame());
-
-    // Verify that clear within a layer does NOT trigger a fresh frame
-    canvas->saveLayer(NULL, NULL);
-    canvas->clear(0x00000000);
-    canvas->restore();
-    REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
-
-    // Verify that a clear with clipping triggers a fresh frame
-    // (clear is not affected by clipping)
-    canvas->save();
-    canvas->clipRect(partialRect, SkRegion::kIntersect_Op, false);
-    canvas->clear(0x00000000);
-    canvas->restore();
-    REPORTER_ASSERT(reporter, canvas->isFreshFrame());
-
-    // Verify that full frame rects with different forms of opaque paint
-    // trigger frames to be marked as fresh
-    {
-        SkPaint paint;
-        paint.setStyle(SkPaint::kFill_Style);
-        paint.setAlpha(255);
-        canvas->drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, canvas->isFreshFrame());
-    }
-    {
-        SkPaint paint;
-        paint.setStyle(SkPaint::kFill_Style);
-        paint.setAlpha(255);
-        paint.setXfermodeMode(SkXfermode::kSrcIn_Mode);
-        canvas->drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
-    }
-    {
-        SkPaint paint;
-        paint.setStyle(SkPaint::kFill_Style);
-        SkBitmap bmp;
-        create(&bmp, 0xFFFFFFFF);
-        bmp.setAlphaType(kOpaque_SkAlphaType);
-        SkShader* shader = SkShader::CreateBitmapShader(bmp,
-            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
-        paint.setShader(shader)->unref();
-        canvas->drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, canvas->isFreshFrame());
-    }
-
-    // Verify that full frame rects with different forms of non-opaque paint
-    // do not trigger frames to be marked as fresh
-    {
-        SkPaint paint;
-        paint.setStyle(SkPaint::kFill_Style);
-        paint.setAlpha(254);
-        canvas->drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
-    }
-    {
-        SkPaint paint;
-        paint.setStyle(SkPaint::kFill_Style);
-        // Defining a cone that partially overlaps the canvas
-        const SkPoint pt1 = SkPoint::Make(SkIntToScalar(0), SkIntToScalar(0));
-        const SkScalar r1 = SkIntToScalar(1);
-        const SkPoint pt2 = SkPoint::Make(SkIntToScalar(10), SkIntToScalar(0));
-        const SkScalar r2 = SkIntToScalar(5);
-        const SkColor colors[2] = {SK_ColorWHITE, SK_ColorWHITE};
-        const SkScalar pos[2] = {0, SK_Scalar1};
-        SkShader* shader = SkGradientShader::CreateTwoPointConical(
-            pt1, r1, pt2, r2, colors, pos, 2, SkShader::kClamp_TileMode);
-        paint.setShader(shader)->unref();
-        canvas->drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
-    }
-    {
-        SkPaint paint;
-        paint.setStyle(SkPaint::kFill_Style);
-        SkBitmap bmp;
-        create(&bmp, 0xFFFFFFFF);
-        bmp.setAlphaType(kPremul_SkAlphaType);
-        SkShader* shader = SkShader::CreateBitmapShader(bmp,
-            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
-        paint.setShader(shader)->unref();
-        canvas->drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
-    }
-
-    // Verify that incomplete coverage does not trigger a fresh frame
-    {
-        SkPaint paint;
-        paint.setStyle(SkPaint::kFill_Style);
-        paint.setAlpha(255);
-        canvas->drawRect(partialRect, paint);
-        REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
-    }
-
-    // Verify that incomplete coverage due to clipping does not trigger a fresh
-    // frame
-    {
-        canvas->save();
-        canvas->clipRect(partialRect, SkRegion::kIntersect_Op, false);
-        SkPaint paint;
-        paint.setStyle(SkPaint::kFill_Style);
-        paint.setAlpha(255);
-        canvas->drawRect(fullRect, paint);
-        canvas->restore();
-        REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
-    }
-    {
-        canvas->save();
-        SkPaint paint;
-        paint.setStyle(SkPaint::kFill_Style);
-        paint.setAlpha(255);
-        SkPath path;
-        path.addCircle(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(2));
-        canvas->clipPath(path, SkRegion::kIntersect_Op, false);
-        canvas->drawRect(fullRect, paint);
-        canvas->restore();
-        REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
-    }
-
-    // Verify that stroked rect does not trigger a fresh frame
-    {
-        SkPaint paint;
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setAlpha(255);
-        canvas->drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, !canvas->isFreshFrame());
-    }
-
-    // Verify kSrcMode triggers a fresh frame even with transparent color
-    {
-        SkPaint paint;
-        paint.setStyle(SkPaint::kFill_Style);
-        paint.setAlpha(100);
-        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-        canvas->drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, canvas->isFreshFrame());
-    }
-}
-
-class MockDevice : public SkBitmapDevice {
-public:
-    MockDevice(const SkBitmap& bm) : SkBitmapDevice(bm) {
-        fDrawBitmapCallCount = 0;
-    }
-    virtual void drawBitmap(const SkDraw&, const SkBitmap&,
-                            const SkMatrix&, const SkPaint&) SK_OVERRIDE {
-        fDrawBitmapCallCount++;
-    }
-
-    int fDrawBitmapCallCount;
-};
-
-class NotificationCounter : public SkDeferredCanvas::NotificationClient {
-public:
-    NotificationCounter() {
-        fPrepareForDrawCount = fStorageAllocatedChangedCount =
-            fFlushedDrawCommandsCount = fSkippedPendingDrawCommandsCount = 0;
-    }
-
-    virtual void prepareForDraw() SK_OVERRIDE {
-        fPrepareForDrawCount++;
-    }
-    virtual void storageAllocatedForRecordingChanged(size_t) SK_OVERRIDE {
-        fStorageAllocatedChangedCount++;
-    }
-    virtual void flushedDrawCommands() SK_OVERRIDE {
-        fFlushedDrawCommandsCount++;
-    }
-    virtual void skippedPendingDrawCommands() SK_OVERRIDE {
-        fSkippedPendingDrawCommandsCount++;
-    }
-
-    int fPrepareForDrawCount;
-    int fStorageAllocatedChangedCount;
-    int fFlushedDrawCommandsCount;
-    int fSkippedPendingDrawCommandsCount;
-
-private:
-    typedef SkDeferredCanvas::NotificationClient INHERITED;
-};
-
-// Verifies that the deferred canvas triggers a flush when its memory
-// limit is exceeded
-static void TestDeferredCanvasMemoryLimit(skiatest::Reporter* reporter) {
-    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100));
-    SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
-
-    NotificationCounter notificationCounter;
-    canvas->setNotificationClient(&notificationCounter);
-
-    canvas->setMaxRecordingStorage(160000);
-
-    SkBitmap sourceImage;
-    // 100 by 100 image, takes 40,000 bytes in memory
-    sourceImage.allocN32Pixels(100, 100);
-    sourceImage.eraseColor(SK_ColorGREEN);
-
-    for (int i = 0; i < 5; i++) {
-        sourceImage.notifyPixelsChanged(); // to force re-serialization
-        canvas->drawBitmap(sourceImage, 0, 0, NULL);
-    }
-
-    REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
-}
-
-static void TestDeferredCanvasSilentFlush(skiatest::Reporter* reporter) {
-    SkAutoTUnref<SkSurface> surface(createSurface(0));
-    SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
-
-    NotificationCounter notificationCounter;
-    canvas->setNotificationClient(&notificationCounter);
-
-    canvas->silentFlush(); // will skip the initial clear that was recorded in createSurface
-
-    REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount);
-    REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount);
-}
-
-static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) {
-    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100));
-    SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
-
-    NotificationCounter notificationCounter;
-    canvas->setNotificationClient(&notificationCounter);
-
-    const int imageCount = 2;
-    SkBitmap sourceImages[imageCount];
-    for (int i = 0; i < imageCount; i++) {
-        sourceImages[i].allocN32Pixels(100, 100);
-        sourceImages[i].eraseColor(SK_ColorGREEN);
-    }
-
-    size_t bitmapSize = sourceImages[0].getSize();
-
-    canvas->drawBitmap(sourceImages[0], 0, 0, NULL);
-    REPORTER_ASSERT(reporter, 1 == notificationCounter.fStorageAllocatedChangedCount);
-    // stored bitmap + drawBitmap command
-    REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() > bitmapSize);
-
-    // verify that nothing can be freed at this point
-    REPORTER_ASSERT(reporter, 0 == canvas->freeMemoryIfPossible(~0U));
-
-    // verify that flush leaves image in cache
-    REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount);
-    REPORTER_ASSERT(reporter, 0 == notificationCounter.fPrepareForDrawCount);
-    canvas->flush();
-    REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
-    REPORTER_ASSERT(reporter, 1 == notificationCounter.fPrepareForDrawCount);
-    REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() >= bitmapSize);
-
-    // verify that after a flush, cached image can be freed
-    REPORTER_ASSERT(reporter, canvas->freeMemoryIfPossible(~0U) >= bitmapSize);
-
-    // Verify that caching works for avoiding multiple copies of the same bitmap
-    canvas->drawBitmap(sourceImages[0], 0, 0, NULL);
-    REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount);
-    canvas->drawBitmap(sourceImages[0], 0, 0, NULL);
-    REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount);
-    REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
-    REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() < 2 * bitmapSize);
-
-    // Verify partial eviction based on bytesToFree
-    canvas->drawBitmap(sourceImages[1], 0, 0, NULL);
-    REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
-    canvas->flush();
-    REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
-    REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() > 2 * bitmapSize);
-    size_t bytesFreed = canvas->freeMemoryIfPossible(1);
-    REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
-    REPORTER_ASSERT(reporter,  bytesFreed >= bitmapSize);
-    REPORTER_ASSERT(reporter,  bytesFreed < 2*bitmapSize);
-
-    // Verifiy that partial purge works, image zero is in cache but not reffed by
-    // a pending draw, while image 1 is locked-in.
-    canvas->freeMemoryIfPossible(~0U);
-    REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
-    canvas->drawBitmap(sourceImages[0], 0, 0, NULL);
-    canvas->flush();
-    canvas->drawBitmap(sourceImages[1], 0, 0, NULL);
-    bytesFreed = canvas->freeMemoryIfPossible(~0U);
-    // only one bitmap should have been freed.
-    REPORTER_ASSERT(reporter,  bytesFreed >= bitmapSize);
-    REPORTER_ASSERT(reporter,  bytesFreed < 2*bitmapSize);
-    // Clear for next test
-    canvas->flush();
-    canvas->freeMemoryIfPossible(~0U);
-    REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() < bitmapSize);
-
-    // Verify the image cache is sensitive to genID bumps
-    canvas->drawBitmap(sourceImages[1], 0, 0, NULL);
-    sourceImages[1].notifyPixelsChanged();
-    canvas->drawBitmap(sourceImages[1], 0, 0, NULL);
-    REPORTER_ASSERT(reporter, canvas->storageAllocatedForRecording() > 2*bitmapSize);
-
-    // Verify that nothing in this test caused commands to be skipped
-    REPORTER_ASSERT(reporter, 0 == notificationCounter.fSkippedPendingDrawCommandsCount);
-}
-
-static void TestDeferredCanvasSkip(skiatest::Reporter* reporter) {
-    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100));
-    SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
-
-    NotificationCounter notificationCounter;
-    canvas->setNotificationClient(&notificationCounter);
-    canvas->clear(0x0);
-    REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount);
-    REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount);
-    canvas->flush();
-    REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount);
-    REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
-
-}
-
-static void TestDeferredCanvasBitmapShaderNoLeak(skiatest::Reporter* reporter) {
-    // This is a regression test for crbug.com/155875
-    // This test covers a code path that inserts bitmaps into the bitmap heap through the
-    // flattening of SkBitmapProcShaders. The refcount in the bitmap heap is maintained through
-    // the flattening and unflattening of the shader.
-    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100));
-    SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
-    // test will fail if nbIterations is not in sync with
-    // BITMAPS_TO_KEEP in SkGPipeWrite.cpp
-    const int nbIterations = 5;
-    size_t bytesAllocated = 0;
-    for(int pass = 0; pass < 2; ++pass) {
-        for(int i = 0; i < nbIterations; ++i) {
-            SkPaint paint;
-            SkBitmap paintPattern;
-            paintPattern.allocN32Pixels(10, 10);
-            paintPattern.eraseColor(SK_ColorGREEN);
-            paint.setShader(SkNEW_ARGS(SkBitmapProcShader,
-                (paintPattern, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)))->unref();
-            canvas->drawPaint(paint);
-            canvas->flush();
-
-            // In the first pass, memory allocation should be monotonically increasing as
-            // the bitmap heap slots fill up.  In the second pass memory allocation should be
-            // stable as bitmap heap slots get recycled.
-            size_t newBytesAllocated = canvas->storageAllocatedForRecording();
-            if (pass == 0) {
-                REPORTER_ASSERT(reporter, newBytesAllocated > bytesAllocated);
-                bytesAllocated = newBytesAllocated;
-            } else {
-                REPORTER_ASSERT(reporter, newBytesAllocated == bytesAllocated);
-            }
-        }
-    }
-    // All cached resources should be evictable since last canvas call was flush()
-    canvas->freeMemoryIfPossible(~0U);
-    REPORTER_ASSERT(reporter, 0 == canvas->storageAllocatedForRecording());
-}
-
-static void TestDeferredCanvasBitmapSizeThreshold(skiatest::Reporter* reporter) {
-    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100));
-
-    SkBitmap sourceImage;
-    // 100 by 100 image, takes 40,000 bytes in memory
-    sourceImage.allocN32Pixels(100, 100);
-    sourceImage.eraseColor(SK_ColorGREEN);
-
-    // 1 under : should not store the image
-    {
-        SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
-        canvas->setBitmapSizeThreshold(39999);
-        canvas->drawBitmap(sourceImage, 0, 0, NULL);
-        size_t newBytesAllocated = canvas->storageAllocatedForRecording();
-        REPORTER_ASSERT(reporter, newBytesAllocated == 0);
-    }
-
-    // exact value : should store the image
-    {
-        SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
-        canvas->setBitmapSizeThreshold(40000);
-        canvas->drawBitmap(sourceImage, 0, 0, NULL);
-        size_t newBytesAllocated = canvas->storageAllocatedForRecording();
-        REPORTER_ASSERT(reporter, newBytesAllocated > 0);
-    }
-
-    // 1 over : should still store the image
-    {
-        SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
-        canvas->setBitmapSizeThreshold(40001);
-        canvas->drawBitmap(sourceImage, 0, 0, NULL);
-        size_t newBytesAllocated = canvas->storageAllocatedForRecording();
-        REPORTER_ASSERT(reporter, newBytesAllocated > 0);
-    }
-}
-
-
-typedef const void* PixelPtr;
-// Returns an opaque pointer which, either points to a GrTexture or RAM pixel
-// buffer. Used to test pointer equality do determine whether a surface points
-// to the same pixel data storage as before.
-static PixelPtr get_surface_ptr(SkSurface* surface, bool useGpu) {
-#if SK_SUPPORT_GPU
-    if (useGpu) {
-        return surface->getCanvas()->internal_private_accessTopLayerRenderTarget()->asTexture();
-    } else
-#endif
-    {
-        return surface->peekPixels(NULL, NULL);
-    }
-}
-
-static void TestDeferredCanvasSurface(skiatest::Reporter* reporter, GrContextFactory* factory) {
-    SkImageInfo imageSpec = SkImageInfo::MakeN32Premul(10, 10);
-    bool useGpu = SkToBool(factory);
-    int cnt;
-#if SK_SUPPORT_GPU
-    if (useGpu) {
-        cnt = GrContextFactory::kGLContextTypeCnt;
-    } else {
-        cnt = 1;
-    }
-#else
-    SkASSERT(!useGpu);
-    cnt = 1;
-#endif
-    for (int i = 0; i < cnt; ++i) {
-        SkSurface* surface;
-#if SK_SUPPORT_GPU
-        if (useGpu) {
-            GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
-            if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
-                continue;
-            }
-            GrContext* context = factory->get(glCtxType);
-            if (NULL == context) {
-                return;
-            }
-
-            surface = SkSurface::NewRenderTarget(context, imageSpec, 0, NULL);
-        } else
-#endif
-        {
-           surface = SkSurface::NewRaster(imageSpec);
-        }
-        SkASSERT(surface);
-        SkAutoTUnref<SkSurface> aur(surface);
-        SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface));
-
-        SkImage* image1 = canvas->newImageSnapshot();
-        SkAutoTUnref<SkImage> aur_i1(image1);
-        PixelPtr pixels1 = get_surface_ptr(surface, useGpu);
-        // The following clear would normally trigger a copy on write, but
-        // it won't because rendering is deferred.
-        canvas->clear(SK_ColorBLACK);
-        // Obtaining a snapshot directly from the surface (as opposed to the
-        // SkDeferredCanvas) will not trigger a flush of deferred draw operations
-        // and will therefore return the same image as the previous snapshot.
-        SkImage* image2 = surface->newImageSnapshot();
-        SkAutoTUnref<SkImage> aur_i2(image2);
-        // Images identical because of deferral
-        REPORTER_ASSERT(reporter, image1->uniqueID() == image2->uniqueID());
-        // Now we obtain a snpshot via the deferred canvas, which triggers a flush.
-        // Because there is a pending clear, this will generate a different image.
-        SkImage* image3 = canvas->newImageSnapshot();
-        SkAutoTUnref<SkImage> aur_i3(image3);
-        REPORTER_ASSERT(reporter, image1->uniqueID() != image3->uniqueID());
-        // Verify that backing store is now a different buffer because of copy on
-        // write
-        PixelPtr pixels2 = get_surface_ptr(surface, useGpu);
-        REPORTER_ASSERT(reporter, pixels1 != pixels2);
-        // Verify copy-on write with a draw operation that gets deferred by
-        // the in order draw buffer.
-        SkPaint paint;
-        canvas->drawPaint(paint);
-        SkImage* image4 = canvas->newImageSnapshot();  // implicit flush
-        SkAutoTUnref<SkImage> aur_i4(image4);
-        REPORTER_ASSERT(reporter, image4->uniqueID() != image3->uniqueID());
-        PixelPtr pixels3 = get_surface_ptr(surface, useGpu);
-        REPORTER_ASSERT(reporter, pixels2 != pixels3);
-        // Verify that a direct canvas flush with a pending draw does not trigger
-        // a copy on write when the surface is not sharing its buffer with an
-        // SkImage.
-        canvas->clear(SK_ColorWHITE);
-        canvas->flush();
-        PixelPtr pixels4 = get_surface_ptr(surface, useGpu);
-        canvas->drawPaint(paint);
-        canvas->flush();
-        PixelPtr pixels5 = get_surface_ptr(surface, useGpu);
-        REPORTER_ASSERT(reporter, pixels4 == pixels5);
-    }
-}
-
-static void TestDeferredCanvasSetSurface(skiatest::Reporter* reporter, GrContextFactory* factory) {
-    SkImageInfo imageSpec = SkImageInfo::MakeN32Premul(10, 10);
-    SkSurface* surface;
-    SkSurface* alternateSurface;
-    bool useGpu = SkToBool(factory);
-    int cnt;
-#if SK_SUPPORT_GPU
-    if (useGpu) {
-        cnt = GrContextFactory::kGLContextTypeCnt;
-    } else {
-        cnt = 1;
-    }
-#else
-    SkASSERT(!useGpu);
-    cnt = 1;
-#endif
-
-    for (int i = 0; i < cnt; ++i) {
-#if SK_SUPPORT_GPU
-        if (useGpu) {
-            GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
-            if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
-                continue;
-            }
-            GrContext* context = factory->get(glCtxType);
-            if (NULL == context) {
-                continue;
-            }
-            surface = SkSurface::NewRenderTarget(context, imageSpec, 0, NULL);
-            alternateSurface = SkSurface::NewRenderTarget(context, imageSpec, 0, NULL);
-        } else
-#endif
-        {
-            surface = SkSurface::NewRaster(imageSpec);
-            alternateSurface = SkSurface::NewRaster(imageSpec);
-        }
-        SkASSERT(surface);
-        SkASSERT(alternateSurface);
-        SkAutoTUnref<SkSurface> aur1(surface);
-        SkAutoTUnref<SkSurface> aur2(alternateSurface);
-        PixelPtr pixels1 = get_surface_ptr(surface, useGpu);
-        PixelPtr pixels2 = get_surface_ptr(alternateSurface, useGpu);
-        SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface));
-        SkAutoTUnref<SkImage> image1(canvas->newImageSnapshot());
-        canvas->setSurface(alternateSurface);
-        SkAutoTUnref<SkImage> image2(canvas->newImageSnapshot());
-        REPORTER_ASSERT(reporter, image1->uniqueID() != image2->uniqueID());
-        // Verify that none of the above operations triggered a surface copy on write.
-        REPORTER_ASSERT(reporter, get_surface_ptr(surface, useGpu) == pixels1);
-        REPORTER_ASSERT(reporter, get_surface_ptr(alternateSurface, useGpu) == pixels2);
-        // Verify that a flushed draw command will trigger a copy on write on alternateSurface.
-        canvas->clear(SK_ColorWHITE);
-        canvas->flush();
-        REPORTER_ASSERT(reporter, get_surface_ptr(surface, useGpu) == pixels1);
-        REPORTER_ASSERT(reporter, get_surface_ptr(alternateSurface, useGpu) != pixels2);
-    }
-}
-
-static void TestDeferredCanvasCreateCompatibleDevice(skiatest::Reporter* reporter) {
-    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(100, 100));
-    SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
-
-    NotificationCounter notificationCounter;
-    canvas->setNotificationClient(&notificationCounter);
-
-    SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
-    SkAutoTUnref<SkSurface> secondarySurface(canvas->newSurface(info));
-
-    SkRect rect = SkRect::MakeWH(5, 5);
-    SkPaint paint;
-    // After spawning a compatible canvas:
-    // 1) Verify that secondary canvas is usable and does not report to the notification client.
-    surface->getCanvas()->drawRect(rect, paint);
-    REPORTER_ASSERT(reporter, notificationCounter.fStorageAllocatedChangedCount == 0);
-    // 2) Verify that original canvas is usable and still reports to the notification client.
-    canvas->drawRect(rect, paint);
-    REPORTER_ASSERT(reporter, notificationCounter.fStorageAllocatedChangedCount == 1);
-}
-
-static void TestDeferredCanvasGetCanvasSize(skiatest::Reporter* reporter) {
-    SkRect rect;
-    rect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth), SkIntToScalar(gHeight));
-    SkRect clip;
-    clip.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(1), SkIntToScalar(1));
-
-    SkPaint paint;
-    SkISize size = SkISize::Make(gWidth, gHeight);
-
-    SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF));
-    SkAutoTUnref<SkDeferredCanvas> canvas(SkDeferredCanvas::Create(surface.get()));
-    SkSurface* newSurface = SkSurface::NewRasterPMColor(4, 4);
-    SkAutoTUnref<SkSurface> aur(newSurface);
-
-    for (int i = 0; i < 2; ++i) {
-        if (i == 1) {
-            canvas->setSurface(newSurface);
-            size = SkISize::Make(4, 4);
-        }
-
-        // verify that canvas size is correctly initialized or set
-        REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
-
-        // Verify that clear, clip and draw the canvas will not change its size
-        canvas->clear(0x00000000);
-        canvas->clipRect(clip, SkRegion::kIntersect_Op, false);
-        canvas->drawRect(rect, paint);
-        REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
-
-        // Verify that flush the canvas will not change its size
-        canvas->flush();
-        REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
-
-        // Verify that clear canvas with saved state will not change its size
-        canvas->save();
-        canvas->clear(0xFFFFFFFF);
-        REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
-
-        // Verify that restore canvas state will not change its size
-        canvas->restore();
-        REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
-
-        // Verify that clear within a layer will not change canvas size
-        canvas->saveLayer(&clip, &paint);
-        canvas->clear(0x00000000);
-        REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
-
-        // Verify that restore from a layer will not change canvas size
-        canvas->restore();
-        REPORTER_ASSERT(reporter, size == canvas->getCanvasSize());
-    }
-}
-
-DEF_TEST(DeferredCanvas_CPU, reporter) {
-    TestDeferredCanvasFlush(reporter);
-    TestDeferredCanvasSilentFlush(reporter);
-    TestDeferredCanvasFreshFrame(reporter);
-    TestDeferredCanvasMemoryLimit(reporter);
-    TestDeferredCanvasBitmapCaching(reporter);
-    TestDeferredCanvasSkip(reporter);
-    TestDeferredCanvasBitmapShaderNoLeak(reporter);
-    TestDeferredCanvasBitmapSizeThreshold(reporter);
-    TestDeferredCanvasCreateCompatibleDevice(reporter);
-    TestDeferredCanvasWritePixelsToSurface(reporter);
-    TestDeferredCanvasGetCanvasSize(reporter);
-    TestDeferredCanvasSurface(reporter, NULL);
-    TestDeferredCanvasSetSurface(reporter, NULL);
-}
-
-DEF_GPUTEST(DeferredCanvas_GPU, reporter, factory) {
-    if (factory != NULL) {
-        TestDeferredCanvasSurface(reporter, factory);
-        TestDeferredCanvasSetSurface(reporter, factory);
-    }
-}
diff --git a/src/third_party/skia/tests/DequeTest.cpp b/src/third_party/skia/tests/DequeTest.cpp
index 04b1f40..b41262f 100644
--- a/src/third_party/skia/tests/DequeTest.cpp
+++ b/src/third_party/skia/tests/DequeTest.cpp
@@ -13,8 +13,8 @@
         REPORTER_ASSERT(reporter, deq.empty());
         REPORTER_ASSERT(reporter, 0 == deq.count());
         REPORTER_ASSERT(reporter, sizeof(int) == deq.elemSize());
-        REPORTER_ASSERT(reporter, NULL == deq.front());
-        REPORTER_ASSERT(reporter, NULL == deq.back());
+        REPORTER_ASSERT(reporter, nullptr == deq.front());
+        REPORTER_ASSERT(reporter, nullptr == deq.back());
     } else {
         REPORTER_ASSERT(reporter, !deq.empty());
         REPORTER_ASSERT(reporter, count == deq.count());
diff --git a/src/third_party/skia/tests/DetermineDomainModeTest.cpp b/src/third_party/skia/tests/DetermineDomainModeTest.cpp
new file mode 100644
index 0000000..add746f
--- /dev/null
+++ b/src/third_party/skia/tests/DetermineDomainModeTest.cpp
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrSurfaceProxy.h"
+#include "GrTextureProducer.h"
+#include "GrTextureProxy.h"
+
+// For DetermineDomainMode (in the MDB world) we have 4 rects:
+//      1) the final instantiated backing storage (i.e., the actual GrTexture's extent)
+//      2) the proxy's extent, which may or may not match the GrTexture's extent
+//      3) the content rect, which can be a subset of the proxy's extent or null
+//      4) the constraint rect, which can optionally be hard or soft
+// This test "fuzzes" all the combinations of these rects.
+class GrTextureProducer_TestAccess {
+public:
+    using DomainMode = GrTextureProducer::DomainMode;
+
+    static DomainMode DetermineDomainMode(
+                                const SkRect& constraintRect,
+                                GrTextureProducer::FilterConstraint filterConstraint,
+                                bool coordsLimitedToConstraintRect,
+                                GrTextureProxy* proxy,
+                                const SkIRect* textureContentArea,
+                                const GrSamplerParams::FilterMode* filterModeOrNullForBicubic,
+                                SkRect* domainRect) {
+        return GrTextureProducer::DetermineDomainMode(constraintRect,
+                                                      filterConstraint,
+                                                      coordsLimitedToConstraintRect,
+                                                      proxy,
+                                                      textureContentArea,
+                                                      filterModeOrNullForBicubic,
+                                                      domainRect);
+    }
+};
+
+using DomainMode = GrTextureProducer_TestAccess::DomainMode;
+
+#ifdef SK_DEBUG
+static bool is_irect(const SkRect& r) {
+    return SkScalarIsInt(r.fLeft)  && SkScalarIsInt(r.fTop) &&
+           SkScalarIsInt(r.fRight) && SkScalarIsInt(r.fBottom);
+}
+#endif
+
+static SkIRect to_irect(const SkRect& r) {
+    SkASSERT(is_irect(r));
+    return SkIRect::MakeLTRB(SkScalarRoundToInt(r.fLeft),
+                             SkScalarRoundToInt(r.fTop),
+                             SkScalarRoundToInt(r.fRight),
+                             SkScalarRoundToInt(r.fBottom));
+}
+
+
+class RectInfo {
+public:
+    enum Side { kLeft = 0, kTop = 1, kRight = 2, kBot = 3 };
+
+    enum EdgeType {
+        kSoft = 0,   // there is data on the other side of this edge that we are allowed to sample
+        kHard = 1,   // the backing resource ends at this edge
+        kBad  = 2    // we can't sample across this edge
+    };
+
+    void set(const SkRect& rect, EdgeType left, EdgeType top, EdgeType right, EdgeType bot,
+             const char* name) {
+        fRect = rect;
+        fTypes[kLeft]  = left;
+        fTypes[kTop]   = top;
+        fTypes[kRight] = right;
+        fTypes[kBot]   = bot;
+        fName = name;
+    }
+
+    const SkRect& rect() const { return fRect; }
+    EdgeType edgeType(Side side) const { return fTypes[side]; }
+    const char* name() const { return fName; }
+
+#ifdef SK_DEBUG
+    bool isHardOrBadAllAround() const {
+        for (int i = 0; i < 4; ++i) {
+            if (kHard != fTypes[i] && kBad != fTypes[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+#endif
+
+    bool hasABad() const {
+        for (int i = 0; i < 4; ++i) {
+            if (kBad == fTypes[i]) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+#ifdef SK_DEBUG
+    void print(const char* label) const {
+        SkDebugf("%s: %s (%.1f, %.1f, %.1f, %.1f), L: %s T: %s R: %s B: %s\n",
+                 label, fName,
+                 fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
+                 ToStr(fTypes[kLeft]), ToStr(fTypes[kTop]),
+                 ToStr(fTypes[kRight]), ToStr(fTypes[kBot]));
+    }
+#endif
+
+private:
+#ifdef SK_DEBUG
+    static const char* ToStr(EdgeType type) {
+        static const char* names[] = { "soft", "hard", "bad" };
+        return names[type];
+    }
+#endif
+
+    RectInfo operator=(const RectInfo& other); // disallow
+
+    SkRect      fRect;
+    EdgeType    fTypes[4];
+    const char* fName;
+
+};
+
+static sk_sp<GrTextureProxy> create_proxy(GrResourceProvider* resourceProvider,
+                                          bool isPowerOfTwo,
+                                          bool isExact,
+                                          RectInfo* rect) {
+    int size = isPowerOfTwo ? 128 : 100;
+    SkBackingFit fit = isExact ? SkBackingFit::kExact : SkBackingFit::kApprox;
+
+    GrSurfaceDesc desc;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fWidth = size;
+    desc.fHeight = size;
+
+    static const char* name = "proxy";
+
+    // Proxies are always hard on the left and top but can be bad on the right and bottom
+    rect->set(SkRect::MakeWH(size, size),
+              RectInfo::kHard,
+              RectInfo::kHard,
+              (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
+              (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
+              name);
+
+    sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(resourceProvider,
+                                                               desc, fit,
+                                                               SkBudgeted::kYes);
+    return proxy;
+}
+
+static RectInfo::EdgeType compute_inset_edgetype(RectInfo::EdgeType previous,
+                                                 bool isInsetHard, bool coordsAreLimitedToRect,
+                                                 float insetAmount, float halfFilterWidth) {
+    if (isInsetHard) {
+        if (coordsAreLimitedToRect) {
+            SkASSERT(halfFilterWidth >= 0.0f);
+            if (0.0f == halfFilterWidth) {
+                return RectInfo::kSoft;
+            }
+        }
+
+        if (0.0f == insetAmount && RectInfo::kHard == previous) {
+            return RectInfo::kHard;
+        }
+
+        return RectInfo::kBad;
+    }
+
+    if (RectInfo::kHard == previous) {
+        return RectInfo::kHard;
+    }
+
+    if (coordsAreLimitedToRect) {
+        SkASSERT(halfFilterWidth >= 0.0f);
+        if (0.0 == halfFilterWidth || insetAmount > halfFilterWidth) {
+            return RectInfo::kSoft;
+        }
+    }
+
+    return previous;
+}
+
+static const int kInsetLeft_Flag  = 0x1;
+static const int kInsetTop_Flag   = 0x2;
+static const int kInsetRight_Flag = 0x4;
+static const int kInsetBot_Flag   = 0x8;
+
+// If 'isInsetHard' is true we can't sample across the inset boundary.
+// If 'areCoordsLimitedToRect' is true the client promises to never sample outside the inset.
+static const SkRect* generic_inset(const RectInfo& enclosing,
+                                   RectInfo* result,
+                                   bool isInsetHard,
+                                   bool areCoordsLimitedToRect,
+                                   float insetAmount,
+                                   float halfFilterWidth,
+                                   uint32_t flags,
+                                   const char* name) {
+    SkRect newR = enclosing.rect();
+
+    RectInfo::EdgeType left = enclosing.edgeType(RectInfo::kLeft);
+    if (flags & kInsetLeft_Flag) {
+        newR.fLeft += insetAmount;
+        left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
+                                      insetAmount, halfFilterWidth);
+    } else {
+        left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
+                                      0.0f, halfFilterWidth);
+    }
+
+    RectInfo::EdgeType top = enclosing.edgeType(RectInfo::kTop);
+    if (flags & kInsetTop_Flag) {
+        newR.fTop += insetAmount;
+        top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
+                                     insetAmount, halfFilterWidth);
+    } else {
+        top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
+                                     0.0f, halfFilterWidth);
+    }
+
+    RectInfo::EdgeType right = enclosing.edgeType(RectInfo::kRight);
+    if (flags & kInsetRight_Flag) {
+        newR.fRight -= insetAmount;
+        right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
+                                       insetAmount, halfFilterWidth);
+    } else {
+        right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
+                                       0.0f, halfFilterWidth);
+    }
+
+    RectInfo::EdgeType bot = enclosing.edgeType(RectInfo::kBot);
+    if (flags & kInsetBot_Flag) {
+        newR.fBottom -= insetAmount;
+        bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
+                                     insetAmount, halfFilterWidth);
+    } else {
+        bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
+                                     0.0f, halfFilterWidth);
+    }
+
+    result->set(newR, left, top, right, bot, name);
+    return &result->rect();
+}
+
+// Make a rect that only touches the enclosing rect on the left.
+static const SkRect* left_only(const RectInfo& enclosing,
+                               RectInfo* result,
+                               bool isInsetHard,
+                               bool areCoordsLimitedToRect,
+                               float insetAmount,
+                               float halfFilterWidth) {
+    static const char* name = "left";
+    return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
+                         insetAmount, halfFilterWidth,
+                         kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
+}
+
+// Make a rect that only touches the enclosing rect on the top.
+static const SkRect* top_only(const RectInfo& enclosing,
+                               RectInfo* result,
+                               bool isInsetHard,
+                               bool areCoordsLimitedToRect,
+                               float insetAmount,
+                               float halfFilterWidth) {
+    static const char* name = "top";
+    return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
+                         insetAmount, halfFilterWidth,
+                         kInsetLeft_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
+}
+
+// Make a rect that only touches the enclosing rect on the right.
+static const SkRect* right_only(const RectInfo& enclosing,
+                                RectInfo* result,
+                                bool isInsetHard,
+                                bool areCoordsLimitedToRect,
+                                float insetAmount,
+                                float halfFilterWidth) {
+    static const char* name = "right";
+    return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
+                         insetAmount, halfFilterWidth,
+                         kInsetLeft_Flag|kInsetTop_Flag|kInsetBot_Flag, name);
+}
+
+// Make a rect that only touches the enclosing rect on the bottom.
+static const SkRect* bot_only(const RectInfo& enclosing,
+                              RectInfo* result,
+                              bool isInsetHard,
+                              bool areCoordsLimitedToRect,
+                              float insetAmount,
+                              float halfFilterWidth) {
+    static const char* name = "bot";
+    return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
+                         insetAmount, halfFilterWidth,
+                         kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag, name);
+}
+
+// Make a rect that is inset all around.
+static const SkRect* full_inset(const RectInfo& enclosing,
+                                RectInfo* result,
+                                bool isInsetHard,
+                                bool areCoordsLimitedToRect,
+                                float insetAmount,
+                                float halfFilterWidth) {
+    static const char* name = "all";
+    return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
+                         insetAmount, halfFilterWidth,
+                         kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
+}
+
+// This is only used for content rect creation. We ensure 'result' is correct but
+// return null to indicate no content area (other than what the proxy specifies).
+static const SkRect* null_rect(const RectInfo& enclosing,
+                               RectInfo* result,
+                               bool isInsetHard,
+                               bool areCoordsLimitedToRect,
+                               float insetAmount,
+                               float halfFilterWidth) {
+    static const char* name = "null";
+    generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
+                  insetAmount, halfFilterWidth, 0, name);
+    return nullptr;
+}
+
+// Make a rect with no inset. This is only used for constraint rect creation.
+static const SkRect* no_inset(const RectInfo& enclosing,
+                              RectInfo* result,
+                              bool isInsetHard,
+                              bool areCoordsLimitedToRect,
+                              float insetAmount,
+                              float halfFilterWidth) {
+    static const char* name = "none";
+    return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
+                         insetAmount, halfFilterWidth, 0, name);
+}
+
+static void proxy_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider) {
+    GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode;
+    SkRect actualDomainRect;
+
+    static const GrSamplerParams::FilterMode gModes[] = {
+        GrSamplerParams::kNone_FilterMode,
+        GrSamplerParams::kBilerp_FilterMode,
+        GrSamplerParams::kMipMap_FilterMode,
+    };
+
+    static const GrSamplerParams::FilterMode* gModePtrs[] = {
+        &gModes[0], &gModes[1], nullptr, &gModes[2]
+    };
+
+    static const float gHalfFilterWidth[] = { 0.0f, 0.5f, 1.5f, 10000.0f };
+
+    for (auto isPowerOfTwoSized : { true, false }) {
+        for (auto isExact : { true, false }) {
+            RectInfo outermost;
+
+            sk_sp<GrTextureProxy> proxy = create_proxy(resourceProvider, isPowerOfTwoSized,
+                                                       isExact, &outermost);
+            SkASSERT(outermost.isHardOrBadAllAround());
+
+            for (auto contentRectMaker : { left_only, top_only, right_only,
+                                           bot_only, full_inset, null_rect}) {
+                RectInfo contentRectStorage;
+                const SkRect* contentRect = (*contentRectMaker)(outermost,
+                                                                &contentRectStorage,
+                                                                true, false, 5.0f, -1.0f);
+                if (contentRect) {
+                    // We only have content rects if they actually reduce the extent of the content
+                    SkASSERT(!contentRect->contains(outermost.rect()));
+                    SkASSERT(outermost.rect().contains(*contentRect));
+                    SkASSERT(is_irect(*contentRect));
+                }
+                SkASSERT(contentRectStorage.isHardOrBadAllAround());
+
+                for (auto isConstraintRectHard : { true, false }) {
+                    for (auto areCoordsLimitedToConstraintRect : { true, false }) {
+                        for (int filterMode = 0; filterMode < 4; ++filterMode) {
+                            for (auto constraintRectMaker : { left_only, top_only, right_only,
+                                                              bot_only, full_inset, no_inset }) {
+                                for (auto insetAmt : { 0.25f, 0.75f, 1.25f, 1.75f, 5.0f }) {
+                                    RectInfo constraintRectStorage;
+                                    const SkRect* constraintRect = (*constraintRectMaker)(
+                                                    contentRect ? contentRectStorage : outermost,
+                                                    &constraintRectStorage,
+                                                    isConstraintRectHard,
+                                                    areCoordsLimitedToConstraintRect,
+                                                    insetAmt,
+                                                    gHalfFilterWidth[filterMode]);
+                                    SkASSERT(constraintRect); // always need one of these
+                                    if (contentRect) {
+                                        SkASSERT(contentRect->contains(*constraintRect));
+                                    } else {
+                                        SkASSERT(outermost.rect().contains(*constraintRect));
+                                    }
+
+                                    SkIRect contentIRect;
+                                    if (contentRect) {
+                                        contentIRect = to_irect(*contentRect);
+                                    }
+
+                                    actualMode = GrTextureProducer_TestAccess::DetermineDomainMode(
+                                                    *constraintRect,
+                                                    isConstraintRectHard
+                                                        ? GrTextureProducer::kYes_FilterConstraint
+                                                        : GrTextureProducer::kNo_FilterConstraint,
+                                                    areCoordsLimitedToConstraintRect,
+                                                    proxy.get(),
+                                                    contentRect ? &contentIRect : nullptr,
+                                                    gModePtrs[filterMode],
+                                                    &actualDomainRect);
+
+                                    expectedMode = DomainMode::kNoDomain_DomainMode;
+                                    if (constraintRectStorage.hasABad()) {
+                                        if (3 == filterMode) {
+                                            expectedMode = DomainMode::kTightCopy_DomainMode;
+                                        } else {
+                                            expectedMode = DomainMode::kDomain_DomainMode;
+                                        }
+                                    }
+
+                                    REPORTER_ASSERT(reporter, expectedMode == actualMode);
+                                    // TODO: add a check that the returned domain rect is correct
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+
+    proxy_test(reporter, context->resourceProvider());
+}
+
+#endif
diff --git a/src/third_party/skia/tests/DeviceLooperTest.cpp b/src/third_party/skia/tests/DeviceLooperTest.cpp
index 5735043..c8eb3c7 100644
--- a/src/third_party/skia/tests/DeviceLooperTest.cpp
+++ b/src/third_party/skia/tests/DeviceLooperTest.cpp
@@ -5,13 +5,13 @@
  * found in the LICENSE file.
  */
 
+#include "SkAutoPixmapStorage.h"
 #include "SkDeviceLooper.h"
 #include "SkRasterClip.h"
 #include "Test.h"
 
-static void make_bm(SkBitmap* bm, int w, int h) {
-    bm->allocPixels(SkImageInfo::Make(w, h, kAlpha_8_SkColorType,
-                                      kPremul_SkAlphaType));
+static void make_pm(SkAutoPixmapStorage* pixmap, int w, int h) {
+    pixmap->alloc(SkImageInfo::Make(w, h, kAlpha_8_SkColorType, kPremul_SkAlphaType));
 }
 
 static bool equal(const SkRasterClip& a, const SkRasterClip& b) {
@@ -40,19 +40,19 @@
 static void test_simple(skiatest::Reporter* reporter) {
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
-        SkBitmap bitmap;
-        make_bm(&bitmap, gRec[i].fDevSize.width(), gRec[i].fDevSize.height());
+        SkAutoPixmapStorage pmap;
+        make_pm(&pmap, gRec[i].fDevSize.width(), gRec[i].fDevSize.height());
 
         SkRasterClip rc(gRec[i].fRCBounds);
 
         for (int aa = 0; aa <= 1; ++aa) {
-            SkDeviceLooper looper(bitmap, rc, gRec[i].fRect, SkToBool(aa));
+            SkDeviceLooper looper(pmap, rc, gRec[i].fRect, SkToBool(aa));
 
             bool valid = looper.next();
             REPORTER_ASSERT(reporter, valid);
             if (valid) {
-                REPORTER_ASSERT(reporter, looper.getBitmap().width() == bitmap.width());
-                REPORTER_ASSERT(reporter, looper.getBitmap().height() == bitmap.height());
+                REPORTER_ASSERT(reporter, looper.getPixmap().width() == pmap.width());
+                REPORTER_ASSERT(reporter, looper.getPixmap().height() == pmap.height());
                 REPORTER_ASSERT(reporter, equal(looper.getRC(), rc));
 
                 REPORTER_ASSERT(reporter, !looper.next());
@@ -62,7 +62,7 @@
         {
             SkIRect r = rc.getBounds();
             r.offset(r.width(), 0);
-            SkDeviceLooper looper(bitmap, rc, r, false);
+            SkDeviceLooper looper(pmap, rc, r, false);
             REPORTER_ASSERT(reporter, !looper.next());
         }
     }
@@ -109,8 +109,8 @@
         const int w = gRec[i].fSize.width();
         const int h = gRec[i].fSize.height();
 
-        SkBitmap bitmap;
-        make_bm(&bitmap, w, h);
+        SkAutoPixmapStorage pmap;
+        make_pm(&pmap, w, h);
 
         const SkIRect rect = SkIRect::MakeWH(w, h);
 
@@ -125,7 +125,7 @@
             SkRasterClip rc;
             rc.op(rgn, SkRegion::kReplace_Op);
 
-            SkDeviceLooper looper(bitmap, rc, rect, gRec[i].fAA);
+            SkDeviceLooper looper(pmap, rc, rect, gRec[i].fAA);
             while (looper.next()) {
                 REPORTER_ASSERT(reporter, !looper.getRC().isEmpty());
             }
diff --git a/src/third_party/skia/tests/DeviceTest.cpp b/src/third_party/skia/tests/DeviceTest.cpp
new file mode 100644
index 0000000..55c9421
--- /dev/null
+++ b/src/third_party/skia/tests/DeviceTest.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmapDevice.h"
+#include "SkDevice.h"
+#include "SkSpecialImage.h"
+
+#if SK_SUPPORT_GPU
+#include "SkGpuDevice.h"
+#endif
+
+#include "Test.h"
+
+class DeviceTestingAccess {
+public:
+    static sk_sp<SkSpecialImage> MakeSpecial(SkBaseDevice* dev, const SkBitmap& bm) {
+        return dev->makeSpecial(bm);
+    }
+
+    static sk_sp<SkSpecialImage> MakeSpecial(SkBaseDevice* dev, SkImage* img) {
+        return dev->makeSpecial(img);
+    }
+
+    static sk_sp<SkSpecialImage> SnapSpecial(SkBaseDevice* dev) {
+        return dev->snapSpecial();
+    }
+};
+
+// TODO: re-enable this when Raster methods are implemented
+#if 0
+DEF_TEST(SpecialImage_BitmapDevice, reporter) {
+    static const int kWidth = 100;
+    static const int kHeight = 90;
+
+    SkImageInfo ii = SkImageInfo::MakeN32Premul(2*kWidth, 2*kHeight);
+
+    sk_sp<SkBaseDevice> bmDev(SkBitmapDevice::Create(ii));
+
+    SkBitmap bm;
+    bm.tryAllocN32Pixels(kWidth, kHeight);
+
+    // Create a raster-backed special image from a raster-backed SkBitmap
+    sk_sp<SkSpecialImage> special = DeviceTestingAccess::MakeSpecial(bmDev.get(), bm);
+    SkASSERT(!special->isTextureBacked());
+    SkASSERT(kWidth == special->width());
+    SkASSERT(kHeight == special->height());
+    SkASSERT(bm.getGenerationID() == special->uniqueID());
+    SkASSERT(SkIRect::MakeWH(kWidth, kHeight) == special->subset());
+
+    // Create a raster-backed special image from a raster-backed SkImage
+    sk_sp<SkImage> image(SkImage::MakeFromBitmap(bm));
+    special = DeviceTestingAccess::MakeSpecial(bmDev.get(), image.get());
+    SkASSERT(!special->isTextureBacked());
+    SkASSERT(kWidth == special->width());
+    SkASSERT(kHeight == special->height());
+    SkASSERT(bm.getGenerationID() == special->uniqueID());
+    SkASSERT(SkIRect::MakeWH(kWidth, kHeight) == special->subset());
+
+    // Snap the device as a raster-backed special image
+    special = DeviceTestingAccess::SnapSpecial(bmDev.get());
+    SkASSERT(!special->isTextureBacked());
+    SkASSERT(2*kWidth == special->width());
+    SkASSERT(2*kHeight == special->height());
+    SkASSERT(SkIRect::MakeWH(2*kWidth, 2*kHeight) == special->subset());
+}
+#endif
+
+
+#if SK_SUPPORT_GPU
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_GPUDevice, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+
+    static const int kWidth = 100;
+    static const int kHeight = 90;
+
+    SkImageInfo ii = SkImageInfo::MakeN32Premul(2*kWidth, 2*kHeight);
+
+    sk_sp<SkBaseDevice> gpuDev(SkGpuDevice::Make(context, SkBudgeted::kNo, ii,
+                                                 0, kBottomLeft_GrSurfaceOrigin, nullptr,
+                                                 SkGpuDevice::kClear_InitContents));
+
+    SkBitmap bm;
+    SkAssertResult(bm.tryAllocN32Pixels(kWidth, kHeight));
+
+    // Create a gpu-backed special image from a raster-backed SkBitmap
+    sk_sp<SkSpecialImage> special = DeviceTestingAccess::MakeSpecial(gpuDev.get(), bm);
+    SkASSERT(special->isTextureBacked());
+    SkASSERT(kWidth == special->width());
+    SkASSERT(kHeight == special->height());
+    SkASSERT(bm.getGenerationID() == special->uniqueID());
+    SkASSERT(SkIRect::MakeWH(kWidth, kHeight) == special->subset());
+
+    // Create a gpu-backed special image from a raster-backed SkImage
+    sk_sp<SkImage> image(SkImage::MakeFromBitmap(bm));
+    special = DeviceTestingAccess::MakeSpecial(gpuDev.get(), image.get());
+    SkASSERT(special->isTextureBacked());
+    SkASSERT(kWidth == special->width());
+    SkASSERT(kHeight == special->height());
+    // TODO: Hmmm, this is a bit unexpected
+    SkASSERT(image->uniqueID() != special->uniqueID());
+    SkASSERT(SkIRect::MakeWH(kWidth, kHeight) == special->subset());
+
+    // Create a gpu-backed special image from a gpu-backed SkImage
+    SkColorSpace* legacyColorSpace = nullptr;
+    image = image->makeTextureImage(context, legacyColorSpace);
+    special = DeviceTestingAccess::MakeSpecial(gpuDev.get(), image.get());
+    SkASSERT(special->isTextureBacked());
+    SkASSERT(kWidth == special->width());
+    SkASSERT(kHeight == special->height());
+    SkASSERT(image->uniqueID() == special->uniqueID());
+    SkASSERT(SkIRect::MakeWH(kWidth, kHeight) == special->subset());
+
+    // Snap the device as a gpu-backed special image
+    special = DeviceTestingAccess::SnapSpecial(gpuDev.get());
+    SkASSERT(special->isTextureBacked());
+    SkASSERT(2*kWidth == special->width());
+    SkASSERT(2*kHeight == special->height());
+    SkASSERT(SkIRect::MakeWH(2*kWidth, 2*kHeight) == special->subset());
+}
+
+#endif
diff --git a/src/third_party/skia/tests/DiscardableMemoryPoolTest.cpp b/src/third_party/skia/tests/DiscardableMemoryPoolTest.cpp
index e0145bc..c91e827 100644
--- a/src/third_party/skia/tests/DiscardableMemoryPoolTest.cpp
+++ b/src/third_party/skia/tests/DiscardableMemoryPoolTest.cpp
@@ -9,20 +9,19 @@
 #include "Test.h"
 
 DEF_TEST(DiscardableMemoryPool, reporter) {
-    SkAutoTUnref<SkDiscardableMemoryPool> pool(
-        SkDiscardableMemoryPool::Create(1, NULL));
+    sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(1));
     pool->setRAMBudget(3);
     REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed());
 
-    SkAutoTDelete<SkDiscardableMemory> dm1(pool->create(100));
-    REPORTER_ASSERT(reporter, dm1->data() != NULL);
+    std::unique_ptr<SkDiscardableMemory> dm1(pool->create(100));
+    REPORTER_ASSERT(reporter, dm1->data() != nullptr);
     REPORTER_ASSERT(reporter, 100 == pool->getRAMUsed());
     dm1->unlock();
     REPORTER_ASSERT(reporter, 0 == pool->getRAMUsed());
     REPORTER_ASSERT(reporter, !dm1->lock());
 
 
-    SkAutoTDelete<SkDiscardableMemory> dm2(pool->create(200));
+    std::unique_ptr<SkDiscardableMemory> dm2(pool->create(200));
     REPORTER_ASSERT(reporter, 200 == pool->getRAMUsed());
     pool->setRAMBudget(400);
     dm2->unlock();
diff --git a/src/third_party/skia/tests/DiscardableMemoryTest.cpp b/src/third_party/skia/tests/DiscardableMemoryTest.cpp
index da36ffd..818fbea 100644
--- a/src/third_party/skia/tests/DiscardableMemoryTest.cpp
+++ b/src/third_party/skia/tests/DiscardableMemoryTest.cpp
@@ -5,28 +5,56 @@
  * found in the LICENSE file.
  */
 
-#include "SkDiscardableMemory.h"
+#include "SkDiscardableMemoryPool.h"
 
 #include "Test.h"
 
-DEF_TEST(DiscardableMemory, reporter) {
-    const char testString[] = "HELLO, WORLD!";
-    const size_t len = sizeof(testString);
-    SkAutoTDelete<SkDiscardableMemory> dm(SkDiscardableMemory::Create(len));
-    REPORTER_ASSERT(reporter, dm.get() != NULL);
-    if (NULL == dm.get()) {
+namespace {
+constexpr char kTestString[] = "HELLO, WORLD!";
+constexpr size_t kTestStringLength = sizeof(kTestString);
+}
+
+static void test_dm(skiatest::Reporter* reporter,
+                    SkDiscardableMemory* dm,
+                    bool assertRelock) {
+    REPORTER_ASSERT(reporter, dm);
+    if (!dm) {
         return;
     }
     void* ptr = dm->data();
-    REPORTER_ASSERT(reporter, ptr != NULL);
-    memcpy(ptr, testString, sizeof(testString));
+    REPORTER_ASSERT(reporter, ptr);
+    if (!ptr) {
+        return;
+    }
+    memcpy(ptr, kTestString, sizeof(kTestString));
     dm->unlock();
-    bool success = dm->lock();
-    REPORTER_ASSERT(reporter, success);
-    if (!success) {
+    bool relockSuccess = dm->lock();
+    if (assertRelock) {
+        REPORTER_ASSERT(reporter, relockSuccess);
+    }
+    if (!relockSuccess) {
         return;
     }
     ptr = dm->data();
-    REPORTER_ASSERT(reporter, 0 == memcmp(ptr, testString, len));
+    REPORTER_ASSERT(reporter, ptr);
+    if (!ptr) {
+        return;
+    }
+    REPORTER_ASSERT(reporter, 0 == memcmp(ptr, kTestString, kTestStringLength));
     dm->unlock();
 }
+
+DEF_TEST(DiscardableMemory_global, reporter) {
+    std::unique_ptr<SkDiscardableMemory> dm(SkDiscardableMemory::Create(kTestStringLength));
+    // lock() test is allowed to fail, since other threads could be
+    // using global pool.
+    test_dm(reporter, dm.get(), false);
+}
+
+DEF_TEST(DiscardableMemory_nonglobal, reporter) {
+    sk_sp<SkDiscardableMemoryPool> pool(
+        SkDiscardableMemoryPool::Make(1024));
+    std::unique_ptr<SkDiscardableMemory> dm(pool->create(kTestStringLength));
+    test_dm(reporter, dm.get(), true);
+}
+    
diff --git a/src/third_party/skia/tests/DocumentTest.cpp b/src/third_party/skia/tests/DocumentTest.cpp
deleted file mode 100644
index 97aefcb..0000000
--- a/src/third_party/skia/tests/DocumentTest.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-#include "Test.h"
-
-#include "SkCanvas.h"
-#include "SkDocument.h"
-#include "SkOSFile.h"
-#include "SkStream.h"
-
-static void test_empty(skiatest::Reporter* reporter) {
-    SkDynamicMemoryWStream stream;
-
-    SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream));
-
-    doc->close();
-
-    REPORTER_ASSERT(reporter, stream.bytesWritten() == 0);
-}
-
-static void test_abort(skiatest::Reporter* reporter) {
-    SkDynamicMemoryWStream stream;
-    SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream));
-
-    SkCanvas* canvas = doc->beginPage(100, 100);
-    canvas->drawColor(SK_ColorRED);
-    doc->endPage();
-
-    doc->abort();
-
-    REPORTER_ASSERT(reporter, stream.bytesWritten() == 0);
-}
-
-static void test_abortWithFile(skiatest::Reporter* reporter) {
-    SkString tmpDir = skiatest::Test::GetTmpDir();
-
-    if (tmpDir.isEmpty()) {
-        return;  // TODO(edisonn): unfortunatelly this pattern is used in other
-                 // tests, but if GetTmpDir() starts returning and empty dir
-                 // allways, then all these tests will be disabled.
-    }
-
-    SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf");
-
-    // Make sure doc's destructor is called to flush.
-    {
-        SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(path.c_str()));
-
-        SkCanvas* canvas = doc->beginPage(100, 100);
-        canvas->drawColor(SK_ColorRED);
-        doc->endPage();
-
-        doc->abort();
-    }
-
-    FILE* file = fopen(path.c_str(), "r");
-    // The created file should be empty.
-    char buffer[100];
-    REPORTER_ASSERT(reporter, fread(buffer, 1, 1, file) == 0);
-    fclose(file);
-}
-
-static void test_file(skiatest::Reporter* reporter) {
-    SkString tmpDir = skiatest::Test::GetTmpDir();
-    if (tmpDir.isEmpty()) {
-        return;  // TODO(edisonn): unfortunatelly this pattern is used in other
-                 // tests, but if GetTmpDir() starts returning and empty dir
-                 // allways, then all these tests will be disabled.
-    }
-
-    SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf");
-
-    SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(path.c_str()));
-
-    SkCanvas* canvas = doc->beginPage(100, 100);
-
-    canvas->drawColor(SK_ColorRED);
-    doc->endPage();
-    doc->close();
-
-    FILE* file = fopen(path.c_str(), "r");
-    REPORTER_ASSERT(reporter, file != NULL);
-    char header[100];
-    REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0);
-    REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0);
-    fclose(file);
-}
-
-static void test_close(skiatest::Reporter* reporter) {
-    SkDynamicMemoryWStream stream;
-    SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream));
-
-    SkCanvas* canvas = doc->beginPage(100, 100);
-    canvas->drawColor(SK_ColorRED);
-    doc->endPage();
-
-    doc->close();
-
-    REPORTER_ASSERT(reporter, stream.bytesWritten() != 0);
-}
-
-DEF_TEST(document_tests, reporter) {
-    test_empty(reporter);
-    test_abort(reporter);
-    test_abortWithFile(reporter);
-    test_file(reporter);
-    test_close(reporter);
-}
diff --git a/src/third_party/skia/tests/DrawBitmapRectTest.cpp b/src/third_party/skia/tests/DrawBitmapRectTest.cpp
index 71ad2cf..9912901 100644
--- a/src/third_party/skia/tests/DrawBitmapRectTest.cpp
+++ b/src/third_party/skia/tests/DrawBitmapRectTest.cpp
@@ -9,58 +9,16 @@
 #include "SkCanvas.h"
 #include "SkData.h"
 #include "SkDiscardableMemoryPool.h"
-#include "SkImageGeneratorPriv.h"
+#include "SkImageGenerator.h"
 #include "SkMatrixUtils.h"
 #include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPixelRef.h"
 #include "SkRandom.h"
 #include "SkShader.h"
 #include "SkSurface.h"
 #include "Test.h"
 
-// A BitmapFactory that always fails when asked to return pixels.
-class FailureImageGenerator : public SkImageGenerator {
-public:
-    FailureImageGenerator() { }
-    virtual ~FailureImageGenerator() { }
-
-protected:
-    virtual bool onGetInfo(SkImageInfo* info) SK_OVERRIDE {
-        *info = SkImageInfo::MakeN32Premul(100, 100);
-        return true;
-    }
-    // default onGetPixels() returns false, which is what we want.
-};
-
-// crbug.com/295895
-// Crashing in skia when a pixelref fails in lockPixels
-//
-static void test_faulty_pixelref(skiatest::Reporter* reporter) {
-    // need a cache, but don't expect to use it, so the budget is not critical
-    SkAutoTUnref<SkDiscardableMemoryPool> pool(
-        SkDiscardableMemoryPool::Create(10 * 1000, NULL));
-    SkBitmap bm;
-    bool installSuccess = SkInstallDiscardablePixelRef(SkNEW(FailureImageGenerator), &bm, pool);
-    REPORTER_ASSERT(reporter, installSuccess);
-    // now our bitmap has a pixelref, but we know it will fail to lock
-
-    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(200, 200));
-    SkCanvas* canvas = surface->getCanvas();
-
-    const SkPaint::FilterLevel levels[] = {
-        SkPaint::kNone_FilterLevel,
-        SkPaint::kLow_FilterLevel,
-        SkPaint::kMedium_FilterLevel,
-        SkPaint::kHigh_FilterLevel,
-    };
-
-    SkPaint paint;
-    canvas->scale(2, 2);    // need a scale, otherwise we may ignore filtering
-    for (size_t i = 0; i < SK_ARRAY_COUNT(levels); ++i) {
-        paint.setFilterLevel(levels[i]);
-        canvas->drawBitmap(bm, 0, 0, &paint);
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 static void rand_matrix(SkMatrix* mat, SkRandom& rand, unsigned mask) {
@@ -84,24 +42,22 @@
     size->set(rand.nextU() & 0xFFFF, rand.nextU() & 0xFFFF);
 }
 
-static bool treat_as_sprite(const SkMatrix& mat, const SkISize& size,
-                            unsigned bits) {
-    return SkTreatAsSprite(mat, size.width(), size.height(), bits);
-}
-
 static void test_treatAsSprite(skiatest::Reporter* reporter) {
-    const unsigned bilerBits = kSkSubPixelBitsForBilerp;
 
     SkMatrix mat;
     SkISize  size;
     SkRandom rand;
 
-    // assert: translate-only no-filter can always be treated as sprite
+    SkPaint noaaPaint;
+    SkPaint aaPaint;
+    aaPaint.setAntiAlias(true);
+
+    // assert: translate-only no-aa can always be treated as sprite
     for (int i = 0; i < 1000; ++i) {
         rand_matrix(&mat, rand, SkMatrix::kTranslate_Mask);
         for (int j = 0; j < 1000; ++j) {
             rand_size(&size, rand);
-            REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, 0));
+            REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, noaaPaint));
         }
     }
 
@@ -110,8 +66,8 @@
         rand_matrix(&mat, rand, SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask);
         for (int j = 0; j < 1000; ++j) {
             rand_size(&size, rand);
-            REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, 0));
-            REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+            REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, noaaPaint));
+            REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint));
         }
     }
 
@@ -119,33 +75,33 @@
 
     const SkScalar tooMuchSubpixel = 100.1f;
     mat.setTranslate(tooMuchSubpixel, 0);
-    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+    REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint));
     mat.setTranslate(0, tooMuchSubpixel);
-    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+    REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint));
 
     const SkScalar tinySubPixel = 100.02f;
     mat.setTranslate(tinySubPixel, 0);
-    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits));
+    REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, aaPaint));
     mat.setTranslate(0, tinySubPixel);
-    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits));
+    REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, aaPaint));
 
     const SkScalar twoThirds = SK_Scalar1 * 2 / 3;
-    const SkScalar bigScale = SkScalarDiv(size.width() + twoThirds, size.width());
+    const SkScalar bigScale = (size.width() + twoThirds) / size.width();
     mat.setScale(bigScale, bigScale);
-    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, false));
-    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+    REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, noaaPaint));
+    REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint));
 
     const SkScalar oneThird = SK_Scalar1 / 3;
-    const SkScalar smallScale = SkScalarDiv(size.width() + oneThird, size.width());
+    const SkScalar smallScale = (size.width() + oneThird) / size.width();
     mat.setScale(smallScale, smallScale);
-    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, false));
-    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+    REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, noaaPaint));
+    REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint));
 
     const SkScalar oneFortyth = SK_Scalar1 / 40;
-    const SkScalar tinyScale = SkScalarDiv(size.width() + oneFortyth, size.width());
+    const SkScalar tinyScale = (size.width() + oneFortyth) / size.width();
     mat.setScale(tinyScale, tinyScale);
-    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, false));
-    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits));
+    REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, noaaPaint));
+    REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, aaPaint));
 }
 
 static void assert_ifDrawnTo(skiatest::Reporter* reporter,
@@ -199,11 +155,9 @@
                   0.0078740157f,
                   SkIntToScalar(239),
                   0, 0, SK_Scalar1);
-    SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
-                                               SkShader::kRepeat_TileMode, &matrix);
-
     SkPaint paint;
-    paint.setShader(s)->unref();
+    paint.setShader(SkShader::MakeBitmapShader(bm, SkShader::kRepeat_TileMode,
+                                               SkShader::kRepeat_TileMode, &matrix));
 
     SkRect r = SkRect::MakeXYWH(681, 239, 695, 253);
     c.drawRect(r, paint);
@@ -272,8 +226,6 @@
 }
 
 static bool check_for_all_zeros(const SkBitmap& bm) {
-    SkAutoLockPixels alp(bm);
-
     size_t count = bm.width() * bm.bytesPerPixel();
     for (int y = 0; y < bm.height(); y++) {
         const uint8_t* ptr = reinterpret_cast<const uint8_t*>(bm.getAddr(0, y));
@@ -305,7 +257,7 @@
     SkIRect srcR = { gWidth, 0, gWidth + 16, 16 };
     SkRect  dstR = { 0, 0, SkIntToScalar(16), SkIntToScalar(16) };
 
-    canvas.drawBitmapRect(src, &srcR, dstR, NULL);
+    canvas.drawBitmapRect(src, srcR, dstR, nullptr);
 
     // ensure that we draw nothing if srcR does not intersect the bitmap
     REPORTER_ASSERT(reporter, check_for_all_zeros(dst));
@@ -314,5 +266,4 @@
     test_giantrepeat_crbug118018(reporter);
 
     test_treatAsSprite(reporter);
-    test_faulty_pixelref(reporter);
 }
diff --git a/src/third_party/skia/tests/DrawFilterTest.cpp b/src/third_party/skia/tests/DrawFilterTest.cpp
new file mode 100644
index 0000000..27c9659
--- /dev/null
+++ b/src/third_party/skia/tests/DrawFilterTest.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCanvas.h"
+#include "SkDrawFilter.h"
+#include "SkSurface.h"
+#include "Test.h"
+
+#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
+
+namespace {
+class TestFilter : public SkDrawFilter {
+public:
+    bool filter(SkPaint* p, Type) override {
+        return true;
+    }
+};
+}
+
+/**
+ *  canvas.setDrawFilter is defined to be local to the save/restore block, such that if you
+ *  do the following: save / modify-drawfilter / restore, the current drawfilter should be what
+ *  it was before the save.
+ */
+static void test_saverestore(skiatest::Reporter* reporter) {
+    auto surface(SkSurface::MakeRasterN32Premul(10, 10));
+    SkCanvas* canvas = surface->getCanvas();
+
+    sk_sp<TestFilter> df(new TestFilter);
+
+    REPORTER_ASSERT(reporter, nullptr == canvas->getDrawFilter());
+
+    canvas->save();
+    canvas->setDrawFilter(df.get());
+    REPORTER_ASSERT(reporter, nullptr != canvas->getDrawFilter());
+    canvas->restore();
+
+    REPORTER_ASSERT(reporter, nullptr == canvas->getDrawFilter());
+}
+
+DEF_TEST(DrawFilter, reporter) {
+    test_saverestore(reporter);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/DrawPathTest.cpp b/src/third_party/skia/tests/DrawPathTest.cpp
index 2af4224..2434bdf 100644
--- a/src/third_party/skia/tests/DrawPathTest.cpp
+++ b/src/third_party/skia/tests/DrawPathTest.cpp
@@ -8,6 +8,7 @@
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkDashPathEffect.h"
+#include "SkStrokeRec.h"
 #include "SkSurface.h"
 #include "Test.h"
 
@@ -17,7 +18,7 @@
     SkPMColor pixel[1];
     output.installPixels(SkImageInfo::MakeN32Premul(1, 1), pixel, 4);
 
-    SkSurface* surf = SkSurface::NewRasterPMColor(300, 33300);
+    auto surf = SkSurface::MakeRasterN32Premul(300, 33300);
     SkCanvas* canvas = surf->getCanvas();
 
     SkRect r = { 0, 33000, 300, 33300 };
@@ -25,7 +26,7 @@
     int y = SkScalarRoundToInt(r.top());
 
     // check that the pixel in question starts as transparent (by the surface)
-    if (canvas->readPixels(&output, x, y)) {
+    if (canvas->readPixels(output, x, y)) {
         REPORTER_ASSERT(reporter, 0 == pixel[0]);
     } else {
         REPORTER_ASSERT_MESSAGE(reporter, false, "readPixels failed");
@@ -38,14 +39,13 @@
     canvas->drawRect(r, paint);
 
     // Now check that it is BLACK
-    if (canvas->readPixels(&output, x, y)) {
+    if (canvas->readPixels(output, x, y)) {
         // don't know what swizzling PMColor did, but white should always
         // appear the same.
         REPORTER_ASSERT(reporter, 0xFFFFFFFF == pixel[0]);
     } else {
         REPORTER_ASSERT_MESSAGE(reporter, false, "readPixels failed");
     }
-    surf->unref();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -94,11 +94,11 @@
     moveToH(&path, &data[0]);
     cubicToH(&path, &data[2]);
 
-    SkAutoTUnref<SkCanvas> canvas(SkCanvas::NewRasterN32(640, 480));
+    auto surface(SkSurface::MakeRasterN32Premul(640, 480));
 
     SkPaint paint;
     paint.setAntiAlias(true);
-    canvas->drawPath(path, paint);
+    surface->getCanvas()->drawPath(path, paint);
 }
 
 // This used to assert in debug builds (and crash writing bad memory in release)
@@ -125,38 +125,36 @@
 static void test_inversepathwithclip() {
     SkPath path;
 
-    path.moveTo(0, SkIntToScalar(20));
-    path.quadTo(SkIntToScalar(10), SkIntToScalar(10),
-                SkIntToScalar(20), SkIntToScalar(20));
+    path.moveTo(0, 20);
+    path.quadTo(10, 10, 20, 20);
     path.toggleInverseFillType();
 
     SkPaint paint;
 
-    SkAutoTUnref<SkCanvas> canvas(SkCanvas::NewRasterN32(640, 480));
-    canvas.get()->save();
-    canvas.get()->clipRect(SkRect::MakeWH(SkIntToScalar(19), SkIntToScalar(11)));
+    auto surface(SkSurface::MakeRasterN32Premul(640, 480));
+    SkCanvas* canvas = surface->getCanvas();
+    canvas->save();
+    canvas->clipRect(SkRect::MakeWH(19, 11));
 
     paint.setAntiAlias(false);
-    canvas.get()->drawPath(path, paint);
+    canvas->drawPath(path, paint);
     paint.setAntiAlias(true);
-    canvas.get()->drawPath(path, paint);
+    canvas->drawPath(path, paint);
 
-    canvas.get()->restore();
+    canvas->restore();
 
     // Now do the test again, with the path flipped, so we only draw in the
     // top half of our bounds, and have the clip intersect our bounds at the
     // bottom.
     path.reset();   // preserves our filltype
-    path.moveTo(0, SkIntToScalar(10));
-    path.quadTo(SkIntToScalar(10), SkIntToScalar(20),
-                SkIntToScalar(20), SkIntToScalar(10));
-    canvas.get()->clipRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(19),
-                                            SkIntToScalar(19), SkIntToScalar(11)));
+    path.moveTo(0, 10);
+    path.quadTo(10, 20, 20, 10);
+    canvas->clipRect(SkRect::MakeXYWH(0, 19, 19, 11));
 
     paint.setAntiAlias(false);
-    canvas.get()->drawPath(path, paint);
+    canvas->drawPath(path, paint);
     paint.setAntiAlias(true);
-    canvas.get()->drawPath(path, paint);
+    canvas->drawPath(path, paint);
 }
 
 static void test_bug533() {
@@ -172,8 +170,8 @@
     SkPaint paint;
     paint.setAntiAlias(true);
 
-    SkAutoTUnref<SkCanvas> canvas(SkCanvas::NewRasterN32(640, 480));
-    canvas.get()->drawPath(path, paint);
+    auto surface(SkSurface::MakeRasterN32Premul(640, 480));
+    surface->getCanvas()->drawPath(path, paint);
 }
 
 static void test_crbug_140642() {
@@ -193,7 +191,7 @@
      */
 
     const SkScalar vals[] = { 27734, 35660, 2157846850.0f, 247 };
-    SkAutoTUnref<SkDashPathEffect> dontAssert(SkDashPathEffect::Create(vals, 4, -248.135982067f));
+    auto dontAssert = SkDashPathEffect::Make(vals, 4, -248.135982067f);
 }
 
 static void test_crbug_124652() {
@@ -203,7 +201,7 @@
         large values can "swamp" small ones.
      */
     SkScalar intervals[2] = {837099584, 33450};
-    SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, -10));
+    auto dontAssert = SkDashPathEffect::Make(intervals, 2, -10);
 }
 
 static void test_bigcubic() {
@@ -214,8 +212,47 @@
     SkPaint paint;
     paint.setAntiAlias(true);
 
-    SkAutoTUnref<SkCanvas> canvas(SkCanvas::NewRasterN32(640, 480));
-    canvas.get()->drawPath(path, paint);
+    auto surface(SkSurface::MakeRasterN32Premul(640, 480));
+    surface->getCanvas()->drawPath(path, paint);
+}
+
+// asserts if halfway case is not handled
+static void test_halfway() {
+    SkPaint paint;
+    SkPath path;
+    path.moveTo(16365.5f, 1394);
+    path.lineTo(16365.5f, 1387.5f);
+    path.quadTo(16365.5f, 1385.43f, 16367, 1383.96f);
+    path.quadTo(16368.4f, 1382.5f, 16370.5f, 1382.5f);
+    path.lineTo(16465.5f, 1382.5f);
+    path.quadTo(16467.6f, 1382.5f, 16469, 1383.96f);
+    path.quadTo(16470.5f, 1385.43f, 16470.5f, 1387.5f);
+    path.lineTo(16470.5f, 1394);
+    path.quadTo(16470.5f, 1396.07f, 16469, 1397.54f);
+    path.quadTo(16467.6f, 1399, 16465.5f, 1399);
+    path.lineTo(16370.5f, 1399);
+    path.quadTo(16368.4f, 1399, 16367, 1397.54f);
+    path.quadTo(16365.5f, 1396.07f, 16365.5f, 1394);
+    path.close();
+    SkPath p2;
+    SkMatrix m;
+    m.reset();
+    m.postTranslate(0.001f, 0.001f);
+    path.transform(m, &p2);
+
+    auto surface(SkSurface::MakeRasterN32Premul(640, 480));
+    SkCanvas* canvas = surface->getCanvas();
+    canvas->translate(-16366, -1383);
+    canvas->drawPath(p2, paint);
+
+    m.reset();
+    m.postTranslate(-0.001f, -0.001f);
+    path.transform(m, &p2);
+    canvas->drawPath(p2, paint);
+
+    m.reset();
+    path.transform(m, &p2);
+    canvas->drawPath(p2, paint);
 }
 
 // we used to assert if the bounds of the device (clip) was larger than 32K
@@ -224,13 +261,13 @@
 static void test_giantaa() {
     const int W = 400;
     const int H = 400;
-    SkAutoTUnref<SkCanvas> canvas(SkCanvas::NewRasterN32(33000, 10));
+    auto surface(SkSurface::MakeRasterN32Premul(33000, 10));
 
     SkPaint paint;
     paint.setAntiAlias(true);
     SkPath path;
     path.addOval(SkRect::MakeXYWH(-10, -10, 20 + W, 20 + H));
-    canvas.get()->drawPath(path, paint);
+    surface->getCanvas()->drawPath(path, paint);
 }
 
 // Extremely large path_length/dash_length ratios may cause infinite looping
@@ -243,7 +280,7 @@
     path.lineTo(5000000, 0);
 
     SkScalar intervals[] = { 0.2f, 0.2f };
-    SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0));
+    sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0));
 
     SkPath filteredPath;
     SkPaint paint;
@@ -263,7 +300,7 @@
     path.lineTo(10000000, 0);
 
     SkScalar intervals[] = { 0.5f, 0.5f };
-    SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0));
+    sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0));
 
     SkPaint paint;
     paint.setStyle(SkPaint::kStroke_Style);
@@ -271,10 +308,43 @@
 
     SkPath filteredPath;
     SkStrokeRec rec(paint);
-    REPORTER_ASSERT(reporter, !dash->filterPath(&filteredPath, path, &rec, NULL));
+    REPORTER_ASSERT(reporter, !dash->filterPath(&filteredPath, path, &rec, nullptr));
     REPORTER_ASSERT(reporter, filteredPath.isEmpty());
 }
 
+// http://crbug.com/472147
+// This is a simplified version from the bug. RRect radii not properly scaled.
+static void test_crbug_472147_simple(skiatest::Reporter* reporter) {
+    auto surface(SkSurface::MakeRasterN32Premul(1000, 1000));
+    SkCanvas* canvas = surface->getCanvas();
+    SkPaint p;
+    SkRect r = SkRect::MakeLTRB(-246.0f, 33.0f, 848.0f, 33554464.0f);
+    SkVector radii[4] = {
+        { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554430.0f }, { 120.0f, 5.0f }
+    };
+    SkRRect rr;
+    rr.setRectRadii(r, radii);
+    canvas->drawRRect(rr, p);
+}
+
+// http://crbug.com/472147
+// RRect radii not properly scaled.
+static void test_crbug_472147_actual(skiatest::Reporter* reporter) {
+    auto surface(SkSurface::MakeRasterN32Premul(1000, 1000));
+    SkCanvas* canvas = surface->getCanvas();
+    SkPaint p;
+    SkRect r = SkRect::MakeLTRB(-246.0f, 33.0f, 848.0f, 33554464.0f);
+    SkVector radii[4] = {
+        { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554430.0f }, { 120.0f, 5.0f }
+    };
+    SkRRect rr;
+    rr.setRectRadii(r, radii);
+    canvas->clipRRect(rr);
+
+    SkRect r2 = SkRect::MakeLTRB(0, 33, 1102, 33554464);
+    canvas->drawRect(r2, p);
+}
+
 DEF_TEST(DrawPath, reporter) {
     test_giantaa();
     test_bug533();
@@ -287,5 +357,8 @@
     if (false) test_crbug131181();
     test_infinite_dash(reporter);
     test_crbug_165432(reporter);
+    test_crbug_472147_simple(reporter);
+    test_crbug_472147_actual(reporter);
     test_big_aa_rect(reporter);
+    test_halfway();
 }
diff --git a/src/third_party/skia/tests/DrawTextTest.cpp b/src/third_party/skia/tests/DrawTextTest.cpp
index 3a96c4f..0134d05 100644
--- a/src/third_party/skia/tests/DrawTextTest.cpp
+++ b/src/third_party/skia/tests/DrawTextTest.cpp
@@ -11,8 +11,10 @@
 #include "SkPaint.h"
 #include "SkPoint.h"
 #include "SkRect.h"
+#include "SkSurface.h"
 #include "SkTypes.h"
 #include "Test.h"
+#include <math.h>
 
 static const SkColor bgColor = SK_ColorWHITE;
 
@@ -35,9 +37,6 @@
     const int xOff = itest.fLeft - iref.fLeft;
     const int yOff = itest.fTop - iref.fTop;
 
-    SkAutoLockPixels alpRef(ref);
-    SkAutoLockPixels alpTest(test);
-
     for (int y = 0; y < test.height(); ++y) {
         for (int x = 0; x < test.width(); ++x) {
             SkColor testColor = test.getColor(x, y);
@@ -74,35 +73,96 @@
     create(&drawPosTextBitmap, drawPosTextRect);
     SkCanvas drawPosTextCanvas(drawPosTextBitmap);
 
-    for (float offsetY = 0.0f; offsetY < 1.0f; offsetY += (1.0f / 16.0f)) {
-        for (float offsetX = 0.0f; offsetX < 1.0f; offsetX += (1.0f / 16.0f)) {
-            SkPoint point = SkPoint::Make(25.0f + offsetX,
-                                          25.0f + offsetY);
+    // Two test cases "A" for the normal path through the code, and " " to check the
+    // early return path.
+    const char* cases[] = {"A", " "};
+    for (auto c : cases) {
+        for (float offsetY = 0.0f; offsetY < 1.0f; offsetY += (1.0f / 16.0f)) {
+            for (float offsetX = 0.0f; offsetX < 1.0f; offsetX += (1.0f / 16.0f)) {
+                SkPoint point = SkPoint::Make(25.0f + offsetX,
+                                              25.0f + offsetY);
 
-            for (int align = 0; align < SkPaint::kAlignCount; ++align) {
-                paint.setTextAlign(static_cast<SkPaint::Align>(align));
+                for (int align = 0; align < SkPaint::kAlignCount; ++align) {
+                    paint.setTextAlign(static_cast<SkPaint::Align>(align));
 
-                for (unsigned int flags = 0; flags < (1 << 3); ++flags) {
-                    static const unsigned int antiAliasFlag = 1;
-                    static const unsigned int subpixelFlag = 1 << 1;
-                    static const unsigned int lcdFlag = 1 << 2;
+                    for (unsigned int flags = 0; flags < (1 << 3); ++flags) {
+                        static const unsigned int antiAliasFlag = 1;
+                        static const unsigned int subpixelFlag = 1 << 1;
+                        static const unsigned int lcdFlag = 1 << 2;
 
-                    paint.setAntiAlias(SkToBool(flags & antiAliasFlag));
-                    paint.setSubpixelText(SkToBool(flags & subpixelFlag));
-                    paint.setLCDRenderText(SkToBool(flags & lcdFlag));
+                        paint.setAntiAlias(SkToBool(flags & antiAliasFlag));
+                        paint.setSubpixelText(SkToBool(flags & subpixelFlag));
+                        paint.setLCDRenderText(SkToBool(flags & lcdFlag));
 
-                    // Test: drawText and drawPosText draw the same.
-                    drawBG(&drawTextCanvas);
-                    drawTextCanvas.drawText("A", 1, point.fX, point.fY, paint);
+                        // Test: drawText and drawPosText draw the same.
+                        drawBG(&drawTextCanvas);
+                        drawTextCanvas.drawText(c, 1, point.fX, point.fY, paint);
 
-                    drawBG(&drawPosTextCanvas);
-                    drawPosTextCanvas.drawPosText("A", 1, &point, paint);
+                        drawBG(&drawPosTextCanvas);
+                        drawPosTextCanvas.drawPosText(c, 1, &point, paint);
 
-                    REPORTER_ASSERT(reporter,
-                        compare(drawTextBitmap, drawTextRect,
-                                drawPosTextBitmap, drawPosTextRect));
+                        REPORTER_ASSERT(reporter,
+                                        compare(drawTextBitmap, drawTextRect,
+                                                drawPosTextBitmap, drawPosTextRect));
+                    }
                 }
             }
         }
     }
 }
+
+// Test drawing text at some unusual coordinates.
+// We measure success by not crashing or asserting.
+DEF_TEST(DrawText_weirdCoordinates, r) {
+    auto surface = SkSurface::MakeRasterN32Premul(10,10);
+    auto canvas = surface->getCanvas();
+
+    SkScalar oddballs[] = { 0.0f, (float)INFINITY, (float)NAN, 34359738368.0f };
+
+    for (auto x : oddballs) {
+        canvas->drawString("a", +x, 0.0f, SkPaint());
+        canvas->drawString("a", -x, 0.0f, SkPaint());
+    }
+    for (auto y : oddballs) {
+        canvas->drawString("a", 0.0f, +y, SkPaint());
+        canvas->drawString("a", 0.0f, -y, SkPaint());
+    }
+}
+
+// Test drawing text with some unusual matricies.
+// We measure success by not crashing or asserting.
+DEF_TEST(DrawText_weirdMatricies, r) {
+    auto surface = SkSurface::MakeRasterN32Premul(100,100);
+    auto canvas = surface->getCanvas();
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setLCDRenderText(true);
+
+    struct {
+        SkScalar textSize;
+        SkScalar matrix[9];
+    } testCases[] = {
+        // 2x2 singular
+        {10, { 0,  0,  0,  0,  0,  0,  0,  0,  1}},
+        {10, { 0,  0,  0,  0,  1,  0,  0,  0,  1}},
+        {10, { 0,  0,  0,  1,  0,  0,  0,  0,  1}},
+        {10, { 0,  0,  0,  1,  1,  0,  0,  0,  1}},
+        {10, { 0,  1,  0,  0,  1,  0,  0,  0,  1}},
+        {10, { 1,  0,  0,  0,  0,  0,  0,  0,  1}},
+        {10, { 1,  0,  0,  1,  0,  0,  0,  0,  1}},
+        {10, { 1,  1,  0,  0,  0,  0,  0,  0,  1}},
+        {10, { 1,  1,  0,  1,  1,  0,  0,  0,  1}},
+        // See https://bugzilla.mozilla.org/show_bug.cgi?id=1305085 .
+        { 1, {10, 20,  0, 20, 40,  0,  0,  0,  1}},
+    };
+
+    for (const auto& testCase : testCases) {
+        paint.setTextSize(testCase.textSize);
+        const SkScalar(&m)[9] = testCase.matrix;
+        SkMatrix mat;
+        mat.setAll(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+        canvas->setMatrix(mat);
+        canvas->drawString("Hamburgefons", 10, 10, paint);
+    }
+}
diff --git a/src/third_party/skia/tests/DynamicHashTest.cpp b/src/third_party/skia/tests/DynamicHashTest.cpp
index 4a5bb85..289b332 100644
--- a/src/third_party/skia/tests/DynamicHashTest.cpp
+++ b/src/third_party/skia/tests/DynamicHashTest.cpp
@@ -100,14 +100,14 @@
     ASSERT(hash.countCollisions(9) == 2);
 
     // We can find our data right?
-    ASSERT(hash.find(1) != NULL);
+    ASSERT(hash.find(1) != nullptr);
     ASSERT(hash.find(1)->value == 2.0);
-    ASSERT(hash.find(5) != NULL);
+    ASSERT(hash.find(5) != nullptr);
     ASSERT(hash.find(5)->value == 3.0);
 
     // These aren't in the hash.
-    ASSERT(hash.find(2) == NULL);
-    ASSERT(hash.find(9) == NULL);
+    ASSERT(hash.find(2) == nullptr);
+    ASSERT(hash.find(9) == nullptr);
 }
 
 DEF_TEST(DynamicHash_remove, reporter) {
@@ -123,16 +123,16 @@
     hash.remove(1);
     // a should be marked deleted, and b should still be findable.
 
-    ASSERT(hash.find(1) == NULL);
-    ASSERT(hash.find(5) != NULL);
+    ASSERT(hash.find(1) == nullptr);
+    ASSERT(hash.find(5) != nullptr);
     ASSERT(hash.find(5)->value == 3.0);
 
     // This will go in the same slot as 'a' did before.
     ASSERT(hash.countCollisions(9) == 0);
     hash.add(&c);
-    ASSERT(hash.find(9) != NULL);
+    ASSERT(hash.find(9) != nullptr);
     ASSERT(hash.find(9)->value == 4.0);
-    ASSERT(hash.find(5) != NULL);
+    ASSERT(hash.find(5) != nullptr);
     ASSERT(hash.find(5)->value == 3.0);
 }
 
@@ -161,7 +161,7 @@
     for (T iter(&hash); !iter.done(); ++iter) {
         int key = (*iter).key;
         keys[count] = key;
-        ASSERT(hash.find(key) != NULL);
+        ASSERT(hash.find(key) != nullptr);
         ++count;
     }
     ASSERT(3 == count);
@@ -177,7 +177,7 @@
         int key = (*iter).key;
         keys[count] = key;
         ASSERT(key != 1);
-        ASSERT(hash.find(key) != NULL);
+        ASSERT(hash.find(key) != nullptr);
         ++count;
     }
     ASSERT(2 == count);
@@ -215,8 +215,8 @@
     ASSERT(hash.count() == 2);
     ASSERT(hash.capacity() == 4);
 
-    ASSERT(hash.find(1) != NULL);
-    ASSERT(hash.find(2) != NULL);
+    ASSERT(hash.find(1) != nullptr);
+    ASSERT(hash.find(2) != nullptr);
 }
 
 DEF_TEST(DynamicHash_reset, reporter) {
diff --git a/src/third_party/skia/tests/EGLImageTest.cpp b/src/third_party/skia/tests/EGLImageTest.cpp
new file mode 100644
index 0000000..84a6bfd
--- /dev/null
+++ b/src/third_party/skia/tests/EGLImageTest.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "TestUtils.h"
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrContextFactory.h"
+#include "GrContextPriv.h"
+#include "GrRenderTargetContext.h"
+#include "GrShaderCaps.h"
+#include "GrTest.h"
+#include "GrTextureContext.h"
+#include "gl/GLTestContext.h"
+#include "gl/GrGLGpu.h"
+#include "gl/GrGLUtil.h"
+
+using sk_gpu_test::GLTestContext;
+
+static void cleanup(GLTestContext* glctx0, GrGLuint texID0, GLTestContext* glctx1, GrContext* grctx1,
+                    const GrGLTextureInfo* grbackendtex1, GrEGLImage image1) {
+    if (glctx1) {
+        glctx1->makeCurrent();
+        if (grctx1) {
+            if (grbackendtex1) {
+                GrGLGpu* gpu1 = static_cast<GrGLGpu*>(grctx1->getGpu());
+                GrBackendObject handle = reinterpret_cast<GrBackendObject>(grbackendtex1);
+                gpu1->deleteTestingOnlyBackendTexture(handle, false);
+            }
+            grctx1->unref();
+        }
+        if (GR_EGL_NO_IMAGE != image1) {
+            glctx1->destroyEGLImage(image1);
+        }
+    }
+
+    glctx0->makeCurrent();
+    if (texID0) {
+        GR_GL_CALL(glctx0->gl(), DeleteTextures(1, &texID0));
+    }
+}
+
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(EGLImageTest, reporter, ctxInfo) {
+    GrContext* context0 = ctxInfo.grContext();
+    sk_gpu_test::GLTestContext* glCtx0 = ctxInfo.glContext();
+
+    // Try to create a second GL context and then check if the contexts have necessary
+    // extensions to run this test.
+
+    if (kGLES_GrGLStandard != glCtx0->gl()->fStandard) {
+        return;
+    }
+    GrGLGpu* gpu0 = static_cast<GrGLGpu*>(context0->getGpu());
+    if (!gpu0->glCaps().shaderCaps()->externalTextureSupport()) {
+        return;
+    }
+
+    std::unique_ptr<GLTestContext> glCtx1 = glCtx0->makeNew();
+    if (!glCtx1) {
+        return;
+    }
+    GrContext* context1 = GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)glCtx1->gl());
+    const GrGLTextureInfo* backendTexture1 = nullptr;
+    GrEGLImage image = GR_EGL_NO_IMAGE;
+    GrGLTextureInfo externalTexture;
+    externalTexture.fID = 0;
+
+    if (!context1) {
+        cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image);
+        return;
+    }
+
+    if (!glCtx1->gl()->hasExtension("EGL_KHR_image") ||
+        !glCtx1->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) {
+        cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image);
+        return;
+    }
+
+    ///////////////////////////////// CONTEXT 1 ///////////////////////////////////
+
+    // Use GL Context 1 to create a texture unknown to GrContext.
+    context1->flush();
+    GrGpu* gpu1 = context1->getGpu();
+    static const int kSize = 100;
+    backendTexture1 = reinterpret_cast<const GrGLTextureInfo*>(
+        gpu1->createTestingOnlyBackendTexture(nullptr, kSize, kSize, kRGBA_8888_GrPixelConfig));
+    if (!backendTexture1 || !backendTexture1->fID) {
+        ERRORF(reporter, "Error creating texture for EGL Image");
+        cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image);
+        return;
+    }
+    if (GR_GL_TEXTURE_2D != backendTexture1->fTarget) {
+        ERRORF(reporter, "Expected backend texture to be 2D");
+        cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image);
+        return;
+    }
+
+    // Wrap the texture in an EGLImage
+    image = glCtx1->texture2DToEGLImage(backendTexture1->fID);
+    if (GR_EGL_NO_IMAGE == image) {
+        ERRORF(reporter, "Error creating EGL Image from texture");
+        cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image);
+        return;
+    }
+
+    // Populate the texture using GL context 1. Important to use TexSubImage as TexImage orphans
+    // the EGL image. Also, this must be done after creating the EGLImage as the texture
+    // contents may not be preserved when the image is created.
+    SkAutoTMalloc<uint32_t> pixels(kSize * kSize);
+    for (int i = 0; i < kSize*kSize; ++i) {
+        pixels.get()[i] = 0xDDAABBCC;
+    }
+    GR_GL_CALL(glCtx1->gl(), ActiveTexture(GR_GL_TEXTURE0));
+    GR_GL_CALL(glCtx1->gl(), BindTexture(backendTexture1->fTarget, backendTexture1->fID));
+    GR_GL_CALL(glCtx1->gl(), TexSubImage2D(backendTexture1->fTarget, 0, 0, 0, kSize, kSize,
+                                           GR_GL_RGBA, GR_GL_UNSIGNED_BYTE, pixels.get()));
+    GR_GL_CALL(glCtx1->gl(), Finish());
+    // We've been making direct GL calls in GL context 1, let GrContext 1 know its internal
+    // state is invalid.
+    context1->resetContext();
+
+    ///////////////////////////////// CONTEXT 0 ///////////////////////////////////
+
+    // Make a new texture ID in GL Context 0 from the EGL Image
+    glCtx0->makeCurrent();
+    externalTexture.fTarget = GR_GL_TEXTURE_EXTERNAL;
+    externalTexture.fID = glCtx0->eglImageToExternalTexture(image);
+    if (0 == externalTexture.fID) {
+        ERRORF(reporter, "Error converting EGL Image back to texture");
+        cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image);
+        return;
+    }
+
+    // Wrap this texture ID in a GrTexture
+    GrBackendTexture backendTex(kSize, kSize, kRGBA_8888_GrPixelConfig, externalTexture);
+
+    // TODO: If I make this TopLeft origin to match resolve_origin calls for kDefault, this test
+    // fails on the Nexus5. Why?
+    sk_sp<GrTextureContext> surfaceContext = context0->contextPriv().makeBackendTextureContext(
+            backendTex, kBottomLeft_GrSurfaceOrigin, nullptr);
+
+    if (!surfaceContext) {
+        ERRORF(reporter, "Error wrapping external texture in GrSurfaceContext.");
+        cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image);
+        return;
+    }
+
+    // Should not be able to wrap as a RT
+    {
+        sk_sp<GrRenderTargetContext> temp =
+                context0->contextPriv().makeBackendTextureRenderTargetContext(
+                        backendTex, kBottomLeft_GrSurfaceOrigin, 0, nullptr);
+        if (temp) {
+            ERRORF(reporter, "Should not be able to wrap an EXTERNAL texture as a RT.");
+        }
+    }
+
+    test_read_pixels(reporter, surfaceContext.get(), pixels.get(), "EGLImageTest-read");
+
+    // We should not be able to write to a EXTERNAL texture
+    test_write_pixels(reporter, surfaceContext.get(), false, "EGLImageTest-write");
+
+    // Only test RT-config
+    // TODO: why do we always need to draw to copy from an external texture?
+    test_copy_from_surface(reporter, context0, surfaceContext->asSurfaceProxy(),
+                           pixels.get(), true, "EGLImageTest-copy");
+
+    cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, backendTexture1, image);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/EmptyPathTest.cpp b/src/third_party/skia/tests/EmptyPathTest.cpp
index c4f011a..5b857a0 100644
--- a/src/third_party/skia/tests/EmptyPathTest.cpp
+++ b/src/third_party/skia/tests/EmptyPathTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkPath.h"
 #include "Test.h"
@@ -54,7 +55,13 @@
     }
 }
 
-static void iter_paint(skiatest::Reporter* reporter, const SkPath& path, bool shouldDraw) {
+enum DrawCaps {
+    kDontDrawCaps,
+    kDrawCaps
+};
+
+static void iter_paint(skiatest::Reporter* reporter, const SkPath& path, bool shouldDraw,
+                       DrawCaps drawCaps) {
     static const SkPaint::Cap gCaps[] = {
         SkPaint::kButt_Cap,
         SkPaint::kRound_Cap,
@@ -73,6 +80,11 @@
     for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
         for (size_t join = 0; join < SK_ARRAY_COUNT(gJoins); ++join) {
             for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
+                if (drawCaps && SkPaint::kButt_Cap != gCaps[cap]
+                        && SkPaint::kFill_Style != gStyles[style]) {
+                    continue;
+                }
+
                 SkPaint paint;
                 paint.setStrokeWidth(SkIntToScalar(10));
 
@@ -127,10 +139,14 @@
             if (doClose) {
                 path.close();
             }
+            /* zero length segments and close following moves draw round and square caps */
+            bool allowCaps = make_L == gMakeProc[i] || make_Q == gMakeProc[i]
+                    || make_C == gMakeProc[i] || make_MZM == gMakeProc[i];
+            allowCaps |= SkToBool(doClose);
             for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
                 path.setFillType(gFills[fill]);
                 bool shouldDraw = path.isInverseFillType();
-                iter_paint(reporter, path, shouldDraw);
+                iter_paint(reporter, path, shouldDraw, allowCaps ? kDrawCaps : kDontDrawCaps);
             }
         }
     }
diff --git a/src/third_party/skia/tests/EncodeTest.cpp b/src/third_party/skia/tests/EncodeTest.cpp
new file mode 100644
index 0000000..6638c1e
--- /dev/null
+++ b/src/third_party/skia/tests/EncodeTest.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Resources.h"
+#include "Test.h"
+
+#include "SkBitmap.h"
+#include "SkEncodedImageFormat.h"
+#include "SkImage.h"
+#include "SkJpegEncoder.h"
+#include "SkPngEncoder.h"
+#include "SkStream.h"
+#include "SkWebpEncoder.h"
+
+#include "png.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+static bool encode(SkEncodedImageFormat format, SkWStream* dst, const SkPixmap& src) {
+    switch (format) {
+        case SkEncodedImageFormat::kJPEG:
+            return SkJpegEncoder::Encode(dst, src, SkJpegEncoder::Options());
+        case SkEncodedImageFormat::kPNG:
+            return SkPngEncoder::Encode(dst, src, SkPngEncoder::Options());
+        default:
+            return false;
+    }
+}
+
+static std::unique_ptr<SkEncoder> make(SkEncodedImageFormat format, SkWStream* dst,
+                                       const SkPixmap& src) {
+    switch (format) {
+        case SkEncodedImageFormat::kJPEG:
+            return SkJpegEncoder::Make(dst, src, SkJpegEncoder::Options());
+        case SkEncodedImageFormat::kPNG:
+            return SkPngEncoder::Make(dst, src, SkPngEncoder::Options());
+        default:
+            return nullptr;
+    }
+}
+
+static void test_encode(skiatest::Reporter* r, SkEncodedImageFormat format) {
+    SkBitmap bitmap;
+    bool success = GetResourceAsBitmap("mandrill_128.png", &bitmap);
+    if (!success) {
+        return;
+    }
+
+    SkPixmap src;
+    success = bitmap.peekPixels(&src);
+    REPORTER_ASSERT(r, success);
+    if (!success) {
+        return;
+    }
+
+    SkDynamicMemoryWStream dst0, dst1, dst2, dst3;
+    success = encode(format, &dst0, src);
+    REPORTER_ASSERT(r, success);
+
+    auto encoder1 = make(format, &dst1, src);
+    for (int i = 0; i < src.height(); i++) {
+        success = encoder1->encodeRows(1);
+        REPORTER_ASSERT(r, success);
+    }
+
+    auto encoder2 = make(format, &dst2, src);
+    for (int i = 0; i < src.height(); i+=3) {
+        success = encoder2->encodeRows(3);
+        REPORTER_ASSERT(r, success);
+    }
+
+    auto encoder3 = make(format, &dst3, src);
+    success = encoder3->encodeRows(200);
+    REPORTER_ASSERT(r, success);
+
+    sk_sp<SkData> data0 = dst0.detachAsData();
+    sk_sp<SkData> data1 = dst1.detachAsData();
+    sk_sp<SkData> data2 = dst2.detachAsData();
+    sk_sp<SkData> data3 = dst3.detachAsData();
+    REPORTER_ASSERT(r, data0->equals(data1.get()));
+    REPORTER_ASSERT(r, data0->equals(data2.get()));
+    REPORTER_ASSERT(r, data0->equals(data3.get()));
+}
+
+DEF_TEST(Encode, r) {
+    test_encode(r, SkEncodedImageFormat::kJPEG);
+    test_encode(r, SkEncodedImageFormat::kPNG);
+}
+
+static inline bool almost_equals(SkPMColor a, SkPMColor b, int tolerance) {
+    if (SkTAbs((int)SkGetPackedR32(a) - (int)SkGetPackedR32(b)) > tolerance) {
+        return false;
+    }
+
+    if (SkTAbs((int)SkGetPackedG32(a) - (int)SkGetPackedG32(b)) > tolerance) {
+        return false;
+    }
+
+    if (SkTAbs((int)SkGetPackedB32(a) - (int)SkGetPackedB32(b)) > tolerance) {
+        return false;
+    }
+
+    if (SkTAbs((int)SkGetPackedA32(a) - (int)SkGetPackedA32(b)) > tolerance) {
+        return false;
+    }
+
+    return true;
+}
+
+static inline bool almost_equals(const SkBitmap& a, const SkBitmap& b, int tolerance) {
+    if (a.info() != b.info()) {
+        return false;
+    }
+
+    SkASSERT(kN32_SkColorType == a.colorType());
+    for (int y = 0; y < a.height(); y++) {
+        for (int x = 0; x < a.width(); x++) {
+            if (!almost_equals(*a.getAddr32(x, y), *b.getAddr32(x, y), tolerance)) {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+DEF_TEST(Encode_JpegDownsample, r) {
+    SkBitmap bitmap;
+    bool success = GetResourceAsBitmap("mandrill_128.png", &bitmap);
+    if (!success) {
+        return;
+    }
+
+    SkPixmap src;
+    success = bitmap.peekPixels(&src);
+    REPORTER_ASSERT(r, success);
+    if (!success) {
+        return;
+    }
+
+    SkDynamicMemoryWStream dst0, dst1, dst2;
+    SkJpegEncoder::Options options;
+    success = SkJpegEncoder::Encode(&dst0, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fDownsample = SkJpegEncoder::Downsample::k422;
+    success = SkJpegEncoder::Encode(&dst1, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fDownsample = SkJpegEncoder::Downsample::k444;
+    success = SkJpegEncoder::Encode(&dst2, src, options);
+    REPORTER_ASSERT(r, success);
+
+    sk_sp<SkData> data0 = dst0.detachAsData();
+    sk_sp<SkData> data1 = dst1.detachAsData();
+    sk_sp<SkData> data2 = dst2.detachAsData();
+    REPORTER_ASSERT(r, data0->size() < data1->size());
+    REPORTER_ASSERT(r, data1->size() < data2->size());
+
+    SkBitmap bm0, bm1, bm2;
+    SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2, SkImage::kRO_LegacyBitmapMode);
+    REPORTER_ASSERT(r, almost_equals(bm0, bm1, 60));
+    REPORTER_ASSERT(r, almost_equals(bm1, bm2, 60));
+}
+
+static inline void pushComment(
+        std::vector<std::string>& comments, const char* keyword, const char* text) {
+    comments.push_back(keyword);
+    comments.push_back(text);
+}
+
+static void testPngComments(const SkPixmap& src, SkPngEncoder::Options& options,
+        skiatest::Reporter* r) {
+    std::vector<std::string> commentStrings;
+    pushComment(commentStrings, "key", "text");
+    pushComment(commentStrings, "test", "something");
+    pushComment(commentStrings, "have some", "spaces in both");
+
+    std::string longKey(PNG_KEYWORD_MAX_LENGTH, 'x');
+#ifdef SK_DEBUG
+    commentStrings.push_back(longKey);
+#else
+    // We call SkDEBUGFAILF it the key is too long so we'll only test this in release mode.
+    commentStrings.push_back(longKey + "x");
+#endif
+    commentStrings.push_back("");
+
+    std::vector<const char*> commentPointers;
+    std::vector<size_t> commentSizes;
+    for(auto& str : commentStrings) {
+        commentPointers.push_back(str.c_str());
+        commentSizes.push_back(str.length() + 1);
+    }
+
+    options.fComments = SkDataTable::MakeCopyArrays((void const *const *)commentPointers.data(),
+            commentSizes.data(), commentStrings.size());
+
+
+    SkDynamicMemoryWStream dst;
+    bool success = SkPngEncoder::Encode(&dst, src, options);
+    REPORTER_ASSERT(r, success);
+
+    std::vector<char> output(dst.bytesWritten());
+    dst.copyTo(output.data());
+
+    // Each chunk is of the form length (4 bytes), chunk type (tEXt), data,
+    // checksum (4 bytes).  Make sure we find all of them in the encoded
+    // results.
+    const char kExpected1[] =
+        "\x00\x00\x00\x08tEXtkey\x00text\x9e\xe7\x66\x51";
+    const char kExpected2[] =
+        "\x00\x00\x00\x0etEXttest\x00something\x29\xba\xef\xac";
+    const char kExpected3[] =
+        "\x00\x00\x00\x18tEXthave some\x00spaces in both\x8d\x69\x34\x2d";
+    std::string longKeyRecord = "tEXt" + longKey; // A snippet of our long key comment
+    std::string tooLongRecord = "tExt" + longKey + "x"; // A snippet whose key is too long
+
+    auto search1 = std::search(output.begin(), output.end(),
+            kExpected1, kExpected1 + sizeof(kExpected1));
+    auto search2 = std::search(output.begin(), output.end(),
+            kExpected2, kExpected2 + sizeof(kExpected2));
+    auto search3 = std::search(output.begin(), output.end(),
+            kExpected3, kExpected3 + sizeof(kExpected3));
+    auto search4 = std::search(output.begin(), output.end(),
+            longKeyRecord.begin(), longKeyRecord.end());
+    auto search5 = std::search(output.begin(), output.end(),
+            tooLongRecord.begin(), tooLongRecord.end());
+
+    REPORTER_ASSERT(r, search1 != output.end());
+    REPORTER_ASSERT(r, search2 != output.end());
+    REPORTER_ASSERT(r, search3 != output.end());
+    REPORTER_ASSERT(r, search4 != output.end());
+    REPORTER_ASSERT(r, search5 == output.end());
+    // Comments test ends
+}
+
+DEF_TEST(Encode_PngOptions, r) {
+    SkBitmap bitmap;
+    bool success = GetResourceAsBitmap("mandrill_128.png", &bitmap);
+    if (!success) {
+        return;
+    }
+
+    SkPixmap src;
+    success = bitmap.peekPixels(&src);
+    REPORTER_ASSERT(r, success);
+    if (!success) {
+        return;
+    }
+
+    SkDynamicMemoryWStream dst0, dst1, dst2;
+    SkPngEncoder::Options options;
+    success = SkPngEncoder::Encode(&dst0, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fFilterFlags = SkPngEncoder::FilterFlag::kUp;
+    success = SkPngEncoder::Encode(&dst1, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fZLibLevel = 3;
+    success = SkPngEncoder::Encode(&dst2, src, options);
+    REPORTER_ASSERT(r, success);
+
+    testPngComments(src, options, r);
+
+    sk_sp<SkData> data0 = dst0.detachAsData();
+    sk_sp<SkData> data1 = dst1.detachAsData();
+    sk_sp<SkData> data2 = dst2.detachAsData();
+    REPORTER_ASSERT(r, data0->size() < data1->size());
+    REPORTER_ASSERT(r, data1->size() < data2->size());
+
+    SkBitmap bm0, bm1, bm2;
+    SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2, SkImage::kRO_LegacyBitmapMode);
+    REPORTER_ASSERT(r, almost_equals(bm0, bm1, 0));
+    REPORTER_ASSERT(r, almost_equals(bm0, bm2, 0));
+}
+
+DEF_TEST(Encode_WebpOptions, r) {
+    SkBitmap bitmap;
+    bool success = GetResourceAsBitmap("google_chrome.ico", &bitmap);
+    if (!success) {
+        return;
+    }
+
+    SkPixmap src;
+    success = bitmap.peekPixels(&src);
+    REPORTER_ASSERT(r, success);
+    if (!success) {
+        return;
+    }
+
+    SkDynamicMemoryWStream dst0, dst1, dst2, dst3;
+    SkWebpEncoder::Options options;
+    options.fCompression = SkWebpEncoder::Compression::kLossless;
+    options.fQuality = 0.0f;
+    success = SkWebpEncoder::Encode(&dst0, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fQuality = 100.0f;
+    success = SkWebpEncoder::Encode(&dst1, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fCompression = SkWebpEncoder::Compression::kLossy;
+    options.fQuality = 100.0f;
+    success = SkWebpEncoder::Encode(&dst2, src, options);
+    REPORTER_ASSERT(r, success);
+
+    options.fCompression = SkWebpEncoder::Compression::kLossy;
+    options.fQuality = 50.0f;
+    success = SkWebpEncoder::Encode(&dst3, src, options);
+    REPORTER_ASSERT(r, success);
+
+    sk_sp<SkData> data0 = dst0.detachAsData();
+    sk_sp<SkData> data1 = dst1.detachAsData();
+    sk_sp<SkData> data2 = dst2.detachAsData();
+    sk_sp<SkData> data3 = dst3.detachAsData();
+    REPORTER_ASSERT(r, data0->size() > data1->size());
+    REPORTER_ASSERT(r, data1->size() > data2->size());
+    REPORTER_ASSERT(r, data2->size() > data3->size());
+
+    SkBitmap bm0, bm1, bm2, bm3;
+    SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2, SkImage::kRO_LegacyBitmapMode);
+    SkImage::MakeFromEncoded(data3)->asLegacyBitmap(&bm3, SkImage::kRO_LegacyBitmapMode);
+    REPORTER_ASSERT(r, almost_equals(bm0, bm1, 0));
+    REPORTER_ASSERT(r, almost_equals(bm0, bm2, 90));
+    REPORTER_ASSERT(r, almost_equals(bm2, bm3, 45));
+}
diff --git a/src/third_party/skia/tests/ErrorTest.cpp b/src/third_party/skia/tests/ErrorTest.cpp
deleted file mode 100644
index 4802d53..0000000
--- a/src/third_party/skia/tests/ErrorTest.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkError.h"
-#include "SkPath.h"
-#include "SkRect.h"
-#include "Test.h"
-
-typedef struct {
-    skiatest::Reporter *fReporter;
-    unsigned int *fIntPointer;
-} ErrorContext;
-
-#define CHECK(errcode)                                                        \
-  REPORTER_ASSERT( reporter, (err = SkGetLastError()) == errcode);            \
-  if (err != kNoError_SkError)                                                \
-  {                                                                           \
-     SkClearLastError();                                                      \
-  }
-
-static void cb(SkError err, void *context) {
-    ErrorContext *context_ptr = static_cast<ErrorContext *>(context);
-    REPORTER_ASSERT( context_ptr->fReporter, (*(context_ptr->fIntPointer) == 0xdeadbeef) );
-}
-
-DEF_TEST(Error, reporter) {
-    // Some previous user of this thread may have left an error laying around.
-    SkClearLastError();
-
-    SkError err;
-
-    unsigned int test_value = 0xdeadbeef;
-    ErrorContext context;
-    context.fReporter = reporter;
-    context.fIntPointer = &test_value;
-
-    SkSetErrorCallback(cb, &context);
-
-    CHECK(kNoError_SkError);
-
-    SkRect r = SkRect::MakeWH(50, 100);
-    CHECK(kNoError_SkError);
-
-    SkPath path;
-    path.addRect(r);
-    CHECK(kNoError_SkError);
-
-    path.addRoundRect(r, 10, 10);
-    CHECK(kNoError_SkError);
-
-    // should trigger the default error callback, which just prints to the screen.
-    path.addRoundRect(r, -10, -10);
-    CHECK(kInvalidArgument_SkError);
-    CHECK(kNoError_SkError);
-
-    // should trigger *our* callback.
-    path.addRoundRect(r, -10, -10);
-    CHECK(kInvalidArgument_SkError);
-    CHECK(kNoError_SkError);
-}
diff --git a/src/third_party/skia/tests/ExifTest.cpp b/src/third_party/skia/tests/ExifTest.cpp
new file mode 100644
index 0000000..4fed2b0
--- /dev/null
+++ b/src/third_party/skia/tests/ExifTest.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Resources.h"
+#include "SkCodec.h"
+#include "Test.h"
+
+DEF_TEST(ExifOrientation, r) {
+    std::unique_ptr<SkStream> stream(GetResourceAsStream("exif-orientation-2-ur.jpg"));
+    REPORTER_ASSERT(r, nullptr != stream);
+    if (!stream) {
+        return;
+    }
+
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
+    REPORTER_ASSERT(r, nullptr != codec);
+    SkCodec::Origin origin = codec->getOrigin();
+    REPORTER_ASSERT(r, SkCodec::kTopRight_Origin == origin);
+
+    stream.reset(GetResourceAsStream("mandrill_512_q075.jpg"));
+    codec.reset(SkCodec::NewFromStream(stream.release()));
+    REPORTER_ASSERT(r, nullptr != codec);
+    origin = codec->getOrigin();
+    REPORTER_ASSERT(r, SkCodec::kTopLeft_Origin == origin);
+}
diff --git a/src/third_party/skia/tests/F16StagesTest.cpp b/src/third_party/skia/tests/F16StagesTest.cpp
new file mode 100644
index 0000000..51cb861
--- /dev/null
+++ b/src/third_party/skia/tests/F16StagesTest.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkRasterPipeline.h"
+#include "Test.h"
+
+DEF_TEST(F16Stages, r) {
+    // Make sure SkRasterPipeline::load_f16 and store_f16 can handle a range of
+    // ordinary (0<=x<=1) and interesting (x<0, x>1) values.
+    float floats[16] = {
+        0.0f, 0.25f, 0.5f, 1.0f,
+        -1.25f, -0.5f, 1.25f, 2.0f,
+        0,0,0,0, 0,0,0,0,  // pad a bit to make sure we qualify for platform-specific code
+    };
+    uint16_t halfs[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
+
+    float*    f32 = floats;
+    uint16_t* f16 = halfs;
+
+    {
+        SkRasterPipeline_<256> p;
+        p.append(SkRasterPipeline:: load_f32, &f32);
+        p.append(SkRasterPipeline::store_f16, &f16);
+        p.run(0,0,16/4);
+    }
+    REPORTER_ASSERT(r, f16[0] == 0x0000);
+    REPORTER_ASSERT(r, f16[1] == 0x3400);
+    REPORTER_ASSERT(r, f16[2] == 0x3800);
+    REPORTER_ASSERT(r, f16[3] == 0x3c00);
+    REPORTER_ASSERT(r, f16[4] == 0xbd00);
+    REPORTER_ASSERT(r, f16[5] == 0xb800);
+    REPORTER_ASSERT(r, f16[6] == 0x3d00);
+    REPORTER_ASSERT(r, f16[7] == 0x4000);
+
+    {
+        SkRasterPipeline_<256> p;
+        p.append(SkRasterPipeline:: load_f16, &f16);
+        p.append(SkRasterPipeline::store_f32, &f32);
+        p.run(0,0,16/4);
+    }
+    REPORTER_ASSERT(r, f32[0] ==  0.00f);
+    REPORTER_ASSERT(r, f32[1] ==  0.25f);
+    REPORTER_ASSERT(r, f32[2] ==  0.50f);
+    REPORTER_ASSERT(r, f32[3] ==  1.00f);
+    REPORTER_ASSERT(r, f32[4] == -1.25f);
+    REPORTER_ASSERT(r, f32[5] == -0.50f);
+    REPORTER_ASSERT(r, f32[6] ==  1.25f);
+    REPORTER_ASSERT(r, f32[7] ==  2.00f);
+}
diff --git a/src/third_party/skia/tests/FakeStreams.h b/src/third_party/skia/tests/FakeStreams.h
new file mode 100644
index 0000000..6b510b8
--- /dev/null
+++ b/src/third_party/skia/tests/FakeStreams.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkData.h"
+#include "SkStream.h"
+
+#ifndef FakeStreams_DEFINED
+#define FakeStreams_DEFINED
+
+// Stream that is not an asset stream (!hasPosition() or !hasLength())
+class NotAssetMemStream : public SkStream {
+public:
+    NotAssetMemStream(sk_sp<SkData> data) : fStream(std::move(data)) {}
+
+    bool hasPosition() const override {
+        return false;
+    }
+
+    bool hasLength() const override {
+        return false;
+    }
+
+    size_t peek(void* buf, size_t bytes) const override {
+        return fStream.peek(buf, bytes);
+    }
+    size_t read(void* buf, size_t bytes) override {
+        return fStream.read(buf, bytes);
+    }
+    bool rewind() override {
+        return fStream.rewind();
+    }
+    bool isAtEnd() const override {
+        return fStream.isAtEnd();
+    }
+private:
+    SkMemoryStream fStream;
+};
+
+/*
+ *  Represents a stream without all of its data.
+ */
+class HaltingStream : public SkStream {
+public:
+    HaltingStream(sk_sp<SkData> data, size_t initialLimit)
+        : fTotalSize(data->size())
+        , fLimit(initialLimit)
+        , fStream(std::move(data))
+    {}
+
+    void addNewData(size_t extra) {
+        fLimit = SkTMin(fTotalSize, fLimit + extra);
+    }
+
+    size_t read(void* buffer, size_t size) override {
+        if (fStream.getPosition() + size > fLimit) {
+            size = fLimit - fStream.getPosition();
+        }
+
+        return fStream.read(buffer, size);
+    }
+
+    bool isAtEnd() const override {
+        return fStream.isAtEnd();
+    }
+
+    bool hasLength() const override { return true; }
+    size_t getLength() const override { return fLimit; }
+
+    bool hasPosition() const override { return true; }
+    size_t getPosition() const override { return fStream.getPosition(); }
+    bool rewind() override { return fStream.rewind(); }
+    bool move(long offset) override { return fStream.move(offset); }
+    bool seek(size_t position) override { return fStream.seek(position); }
+
+    bool isAllDataReceived() const { return fLimit == fTotalSize; }
+
+private:
+    const size_t    fTotalSize;
+    size_t          fLimit;
+    SkMemoryStream  fStream;
+};
+#endif // FakeStreams_DEFINED
diff --git a/src/third_party/skia/tests/FillPathTest.cpp b/src/third_party/skia/tests/FillPathTest.cpp
index cc8d329..1f14d4f 100644
--- a/src/third_party/skia/tests/FillPathTest.cpp
+++ b/src/third_party/skia/tests/FillPathTest.cpp
@@ -12,34 +12,37 @@
 #include "Test.h"
 
 struct FakeBlitter : public SkBlitter {
-  FakeBlitter()
-      : m_blitCount(0)
-  {}
+    FakeBlitter()
+        : m_blitCount(0) { }
 
-  virtual void blitH(int x, int y, int width) {
-    m_blitCount++;
-  }
+    void blitH(int x, int y, int width) override {
+        m_blitCount++;
+    }
 
-  int m_blitCount;
+    void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override {
+      SkDEBUGFAIL("blitAntiH not implemented");
+    }
+
+    int m_blitCount;
 };
 
 // http://code.google.com/p/skia/issues/detail?id=87
 // Lines which is not clipped by boundary based clipping,
 // but skipped after tessellation, should be cleared by the blitter.
 DEF_TEST(FillPathInverse, reporter) {
-  FakeBlitter blitter;
-  SkIRect clip;
-  SkPath path;
-  int height = 100;
-  int width  = 200;
-  int expected_lines = 5;
-  clip.set(0, height - expected_lines, width, height);
-  path.moveTo(0.0f, 0.0f);
-  path.quadTo(SkIntToScalar(width/2), SkIntToScalar(height),
+    FakeBlitter blitter;
+    SkIRect clip;
+    SkPath path;
+    int height = 100;
+    int width  = 200;
+    int expected_lines = 5;
+    clip.set(0, height - expected_lines, width, height);
+    path.moveTo(0.0f, 0.0f);
+    path.quadTo(SkIntToScalar(width/2), SkIntToScalar(height),
               SkIntToScalar(width), 0.0f);
-  path.close();
-  path.setFillType(SkPath::kInverseWinding_FillType);
-  SkScan::FillPath(path, clip, &blitter);
+    path.close();
+    path.setFillType(SkPath::kInverseWinding_FillType);
+    SkScan::FillPath(path, clip, &blitter);
 
-  REPORTER_ASSERT(reporter, blitter.m_blitCount == expected_lines);
+    REPORTER_ASSERT(reporter, blitter.m_blitCount == expected_lines);
 }
diff --git a/src/third_party/skia/tests/FlatDataTest.cpp b/src/third_party/skia/tests/FlatDataTest.cpp
deleted file mode 100644
index 2be92b6..0000000
--- a/src/third_party/skia/tests/FlatDataTest.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkColor.h"
-#include "SkColorFilter.h"
-#include "SkGradientShader.h"
-#include "SkPaint.h"
-#include "SkPictureFlat.h"
-#include "SkShader.h"
-#include "SkXfermode.h"
-#include "Test.h"
-
-struct SkFlattenableTraits {
-    static void Flatten(SkWriteBuffer& buffer, const SkFlattenable& flattenable) {
-        buffer.writeFlattenable(&flattenable);
-    }
-};
-
-class Controller : public SkChunkFlatController {
-public:
-    Controller() : INHERITED(1024) {
-        this->INHERITED::setNamedFactorySet(SkNEW(SkNamedFactorySet))->unref();
-    }
-private:
-    typedef SkChunkFlatController INHERITED;
-};
-
-/**
- * Verify that two SkFlatData objects that created from the same object are
- * identical when using an SkNamedFactorySet.
- * @param reporter Object to report failures.
- * @param obj Flattenable object to be flattened.
- * @param flattenProc Function that flattens objects with the same type as obj.
- */
-template <typename Traits, typename T>
-static void testCreate(skiatest::Reporter* reporter, const T& obj) {
-    Controller controller;
-    // No need to delete data because that will be taken care of by the controller.
-    SkFlatData* data1 = SkFlatData::Create<Traits>(&controller, obj, 0);
-    SkFlatData* data2 = SkFlatData::Create<Traits>(&controller, obj, 1);
-    REPORTER_ASSERT(reporter, *data1 == *data2);
-}
-
-DEF_TEST(FlatData, reporter) {
-    // Test flattening SkShader
-    SkPoint points[2];
-    points[0].set(0, 0);
-    points[1].set(SkIntToScalar(20), SkIntToScalar(20));
-    SkColor colors[2];
-    colors[0] = SK_ColorRED;
-    colors[1] = SK_ColorBLUE;
-
-    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(points, colors, NULL, 2,
-                                                                 SkShader::kRepeat_TileMode));
-    testCreate<SkFlattenableTraits>(reporter, *shader);
-
-    // Test SkColorFilter
-    SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateLightingFilter(SK_ColorBLUE, SK_ColorRED));
-    testCreate<SkFlattenableTraits>(reporter, *cf);
-
-    // Test SkXfermode
-    SkAutoTUnref<SkXfermode> xfer(SkXfermode::Create(SkXfermode::kDstOver_Mode));
-    testCreate<SkFlattenableTraits>(reporter, *xfer);
-}
diff --git a/src/third_party/skia/tests/FlateTest.cpp b/src/third_party/skia/tests/FlateTest.cpp
deleted file mode 100644
index 75c6f3f..0000000
--- a/src/third_party/skia/tests/FlateTest.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkData.h"
-#include "SkFlate.h"
-#include "SkStream.h"
-#include "Test.h"
-
-// A memory stream that reports zero size with the standard call, like
-// an unseekable file stream would.
-class SkZeroSizeMemStream : public SkMemoryStream {
-public:
-    virtual size_t read(void* buffer, size_t size) {
-        if (buffer == NULL && size == 0)
-            return 0;
-        if (buffer == NULL && size == kGetSizeKey)
-            size = 0;
-        return SkMemoryStream::read(buffer, size);
-    }
-
-    static const size_t kGetSizeKey = 0xDEADBEEF;
-};
-
-// Returns a deterministic data of the given size that should be
-// very compressible.
-static SkData* new_test_data(size_t dataSize) {
-    SkAutoTMalloc<uint8_t> testBuffer(dataSize);
-    for (size_t i = 0; i < dataSize; ++i) {
-        testBuffer[i] = i % 64;
-    }
-    return SkData::NewFromMalloc(testBuffer.detach(), dataSize);
-}
-
-static void TestFlate(skiatest::Reporter* reporter, SkMemoryStream* testStream,
-                      size_t dataSize) {
-    SkASSERT(testStream != NULL);
-
-    SkAutoDataUnref testData(new_test_data(dataSize));
-    SkASSERT(testData->size() == dataSize);
-
-    testStream->setMemory(testData->data(), dataSize, /*copyData=*/ true);
-    SkDynamicMemoryWStream compressed;
-    bool deflateSuccess = SkFlate::Deflate(testStream, &compressed);
-    REPORTER_ASSERT(reporter, deflateSuccess);
-
-    // Check that the input data wasn't changed.
-    size_t inputSize = testStream->getLength();
-    if (inputSize == 0) {
-        inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey);
-    }
-    REPORTER_ASSERT(reporter, dataSize == inputSize);
-    if (dataSize == inputSize) {
-        REPORTER_ASSERT(reporter, memcmp(testData->data(),
-                                         testStream->getMemoryBase(),
-                                         dataSize) == 0);
-    }
-
-    size_t compressedSize = compressed.getOffset();
-
-    SkAutoDataUnref compressedData(compressed.copyToData());
-    testStream->setData(compressedData.get());
-
-    SkDynamicMemoryWStream uncompressed;
-    bool inflateSuccess = SkFlate::Inflate(testStream, &uncompressed);
-    REPORTER_ASSERT(reporter, inflateSuccess);
-
-    // Check that the input data wasn't changed.
-    inputSize = testStream->getLength();
-    if (inputSize == 0) {
-        inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey);
-    }
-    REPORTER_ASSERT(reporter,  compressedSize == inputSize);
-    if (compressedData->size() == inputSize) {
-        REPORTER_ASSERT(reporter, memcmp(testStream->getMemoryBase(),
-                                         compressedData->data(),
-                                         compressedData->size()) == 0);
-    }
-
-    // Check that the uncompressed data matches the source data.
-    SkAutoDataUnref uncompressedData(uncompressed.copyToData());
-    REPORTER_ASSERT(reporter, dataSize == uncompressedData->size());
-    if (dataSize == uncompressedData->size()) {
-        REPORTER_ASSERT(reporter, memcmp(testData->data(),
-                                         uncompressedData->data(),
-                                         dataSize) == 0);
-    }
-
-    if (compressedSize < 1) { return; }
-
-    double compressionRatio = static_cast<double>(dataSize) / compressedSize;
-    // Assert that some compression took place.
-    REPORTER_ASSERT(reporter, compressionRatio > 1.2);
-
-    if (reporter->verbose()) {
-        SkDebugf("Flate Test: \t input size: " SK_SIZE_T_SPECIFIER
-                 "\tcompressed size: " SK_SIZE_T_SPECIFIER
-                 "\tratio: %.4g\n",
-                 dataSize, compressedSize, compressionRatio);
-    }
-}
-
-DEF_TEST(Flate, reporter) {
-#ifdef SK_HAS_ZLIB
-    REPORTER_ASSERT(reporter, SkFlate::HaveFlate());
-#endif
-    if (SkFlate::HaveFlate()) {
-        SkMemoryStream memStream;
-        TestFlate(reporter, &memStream, 512);
-        TestFlate(reporter, &memStream, 10240);
-
-        SkZeroSizeMemStream fileStream;
-        TestFlate(reporter, &fileStream, 512);
-        TestFlate(reporter, &fileStream, 10240);
-    }
-}
diff --git a/src/third_party/skia/tests/FlattenDrawableTest.cpp b/src/third_party/skia/tests/FlattenDrawableTest.cpp
new file mode 100644
index 0000000..9df8ba0
--- /dev/null
+++ b/src/third_party/skia/tests/FlattenDrawableTest.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCanvas.h"
+#include "SkDrawable.h"
+#include "SkOnce.h"
+#include "SkPictureRecorder.h"
+#include "SkReadBuffer.h"
+#include "SkRect.h"
+#include "SkStream.h"
+#include "SkWriteBuffer.h"
+#include "Test.h"
+
+class IntDrawable : public SkDrawable {
+public:
+    IntDrawable(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
+        : fA(a)
+        , fB(b)
+        , fC(c)
+        , fD(d)
+    {}
+
+    void flatten(SkWriteBuffer& buffer) const override {
+        buffer.writeUInt(fA);
+        buffer.writeUInt(fB);
+        buffer.writeUInt(fC);
+        buffer.writeUInt(fD);
+    }
+
+    static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
+        uint32_t a = buffer.readUInt();
+        uint32_t b = buffer.readUInt();
+        uint32_t c = buffer.readUInt();
+        uint32_t d = buffer.readUInt();
+        return sk_sp<IntDrawable>(new IntDrawable(a, b, c, d));
+    }
+
+    Factory getFactory() const override { return CreateProc; }
+
+    uint32_t a() const { return fA; }
+    uint32_t b() const { return fB; }
+    uint32_t c() const { return fC; }
+    uint32_t d() const { return fD; }
+
+    const char* getTypeName() const override { return "IntDrawable"; }
+
+protected:
+    SkRect onGetBounds() override { return SkRect::MakeEmpty(); }
+    void onDraw(SkCanvas*) override {}
+
+private:
+    uint32_t fA;
+    uint32_t fB;
+    uint32_t fC;
+    uint32_t fD;
+};
+
+class PaintDrawable : public SkDrawable {
+public:
+    PaintDrawable(const SkPaint& paint)
+        : fPaint(paint)
+    {}
+
+    void flatten(SkWriteBuffer& buffer) const override {
+        buffer.writePaint(fPaint);
+    }
+
+    static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
+        SkPaint paint;
+        buffer.readPaint(&paint);
+        return sk_sp<PaintDrawable>(new PaintDrawable(paint));
+    }
+
+    Factory getFactory() const override { return CreateProc; }
+
+    const SkPaint& paint() const { return fPaint; }
+
+    const char* getTypeName() const override { return "PaintDrawable"; }
+
+protected:
+    SkRect onGetBounds() override { return SkRect::MakeEmpty(); }
+    void onDraw(SkCanvas*) override {}
+
+private:
+    SkPaint fPaint;
+};
+
+class CompoundDrawable : public SkDrawable {
+public:
+    CompoundDrawable(uint32_t a, uint32_t b, uint32_t c, uint32_t d, const SkPaint& paint)
+        : fIntDrawable(new IntDrawable(a, b, c, d))
+        , fPaintDrawable(new PaintDrawable(paint))
+    {}
+
+    CompoundDrawable(IntDrawable* intDrawable, PaintDrawable* paintDrawable)
+        : fIntDrawable(SkRef(intDrawable))
+        , fPaintDrawable(SkRef(paintDrawable))
+    {}
+
+    void flatten(SkWriteBuffer& buffer) const override {
+        buffer.writeFlattenable(fIntDrawable.get());
+        buffer.writeFlattenable(fPaintDrawable.get());
+    }
+
+    static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
+        sk_sp<SkFlattenable> intDrawable(
+                buffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+        SkASSERT(intDrawable);
+        SkASSERT(!strcmp("IntDrawable", intDrawable->getTypeName()));
+
+        sk_sp<SkFlattenable> paintDrawable(
+                buffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+        SkASSERT(paintDrawable);
+        SkASSERT(!strcmp("PaintDrawable", paintDrawable->getTypeName()));
+
+        return sk_sp<CompoundDrawable>(new CompoundDrawable((IntDrawable*) intDrawable.get(),
+                                                            (PaintDrawable*) paintDrawable.get()));
+    }
+
+    Factory getFactory() const override { return CreateProc; }
+
+    IntDrawable* intDrawable() const { return fIntDrawable.get(); }
+    PaintDrawable* paintDrawable() const { return fPaintDrawable.get(); }
+
+    const char* getTypeName() const override { return "CompoundDrawable"; }
+
+protected:
+    SkRect onGetBounds() override { return SkRect::MakeEmpty(); }
+    void onDraw(SkCanvas*) override {}
+
+private:
+    sk_sp<IntDrawable>   fIntDrawable;
+    sk_sp<PaintDrawable> fPaintDrawable;
+};
+
+class RootDrawable : public SkDrawable {
+public:
+    RootDrawable(uint32_t a, uint32_t b, uint32_t c, uint32_t d, const SkPaint& paint,
+                   uint32_t e, uint32_t f, uint32_t g, uint32_t h, SkDrawable* drawable)
+        : fCompoundDrawable(new CompoundDrawable(a, b, c, d, paint))
+        , fIntDrawable(new IntDrawable(e, f, g, h))
+        , fDrawable(SkRef(drawable))
+    {}
+
+    RootDrawable(CompoundDrawable* compoundDrawable, IntDrawable* intDrawable,
+            SkDrawable* drawable)
+        : fCompoundDrawable(SkRef(compoundDrawable))
+        , fIntDrawable(SkRef(intDrawable))
+        , fDrawable(SkRef(drawable))
+    {}
+
+    void flatten(SkWriteBuffer& buffer) const override {
+        buffer.writeFlattenable(fCompoundDrawable.get());
+        buffer.writeFlattenable(fIntDrawable.get());
+        buffer.writeFlattenable(fDrawable.get());
+    }
+
+    static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
+        sk_sp<SkFlattenable> compoundDrawable(
+                buffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+        SkASSERT(compoundDrawable);
+        SkASSERT(!strcmp("CompoundDrawable", compoundDrawable->getTypeName()));
+
+        sk_sp<SkFlattenable> intDrawable(
+                buffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+        SkASSERT(intDrawable);
+        SkASSERT(!strcmp("IntDrawable", intDrawable->getTypeName()));
+
+        sk_sp<SkFlattenable> drawable(
+                buffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+        SkASSERT(drawable);
+
+        return sk_sp<RootDrawable>(new RootDrawable((CompoundDrawable*) compoundDrawable.get(),
+                                                    (IntDrawable*) intDrawable.get(),
+                                                    (SkDrawable*) drawable.get()));
+    }
+
+    Factory getFactory() const override { return CreateProc; }
+
+    CompoundDrawable* compoundDrawable() const { return fCompoundDrawable.get(); }
+    IntDrawable* intDrawable() const { return fIntDrawable.get(); }
+    SkDrawable* drawable() const { return fDrawable.get(); }
+
+    const char* getTypeName() const override { return "RootDrawable"; }
+
+protected:
+    SkRect onGetBounds() override { return SkRect::MakeEmpty(); }
+    void onDraw(SkCanvas*) override {}
+
+private:
+    sk_sp<CompoundDrawable> fCompoundDrawable;
+    sk_sp<IntDrawable>      fIntDrawable;
+    sk_sp<SkDrawable>       fDrawable;
+};
+
+static void register_test_drawables(SkReadBuffer& buffer) {
+    buffer.setCustomFactory(SkString("IntDrawable"), IntDrawable::CreateProc);
+    buffer.setCustomFactory(SkString("PaintDrawable"), PaintDrawable::CreateProc);
+    buffer.setCustomFactory(SkString("CompoundDrawable"), CompoundDrawable::CreateProc);
+    buffer.setCustomFactory(SkString("RootDrawable"), RootDrawable::CreateProc);
+}
+
+DEF_TEST(FlattenDrawable, r) {
+    // Create and serialize the test drawable
+    sk_sp<SkDrawable> drawable(new IntDrawable(1, 2, 3, 4));
+    SkPaint paint;
+    paint.setColor(SK_ColorBLUE);
+    sk_sp<RootDrawable> root(new RootDrawable(5, 6, 7, 8, paint, 9, 10, 11, 12, drawable.get()));
+    SkBinaryWriteBuffer writeBuffer;
+    writeBuffer.writeFlattenable(root.get());
+
+    // Copy the contents of the write buffer into a read buffer
+    sk_sp<SkData> data = SkData::MakeUninitialized(writeBuffer.bytesWritten());
+    writeBuffer.writeToMemory(data->writable_data());
+    SkReadBuffer readBuffer(data->data(), data->size());
+    register_test_drawables(readBuffer);
+
+    // Deserialize and verify the drawable
+    sk_sp<SkDrawable> out((SkDrawable*)readBuffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+    REPORTER_ASSERT(r, out);
+    REPORTER_ASSERT(r, !strcmp("RootDrawable", out->getTypeName()));
+
+    RootDrawable* rootOut = (RootDrawable*) out.get();
+    REPORTER_ASSERT(r, 5 == rootOut->compoundDrawable()->intDrawable()->a());
+    REPORTER_ASSERT(r, 6 == rootOut->compoundDrawable()->intDrawable()->b());
+    REPORTER_ASSERT(r, 7 == rootOut->compoundDrawable()->intDrawable()->c());
+    REPORTER_ASSERT(r, 8 == rootOut->compoundDrawable()->intDrawable()->d());
+    REPORTER_ASSERT(r, SK_ColorBLUE ==
+            rootOut->compoundDrawable()->paintDrawable()->paint().getColor());
+    REPORTER_ASSERT(r, 9 == rootOut->intDrawable()->a());
+    REPORTER_ASSERT(r, 10 == rootOut->intDrawable()->b());
+    REPORTER_ASSERT(r, 11 == rootOut->intDrawable()->c());
+    REPORTER_ASSERT(r, 12 == rootOut->intDrawable()->d());
+
+    // Note that we can still recognize the generic drawable as an IntDrawable
+    SkDrawable* generic = rootOut->drawable();
+    REPORTER_ASSERT(r, !strcmp("IntDrawable", generic->getTypeName()));
+    IntDrawable* integer = (IntDrawable*) generic;
+    REPORTER_ASSERT(r, 1 == integer->a());
+    REPORTER_ASSERT(r, 2 == integer->b());
+    REPORTER_ASSERT(r, 3 == integer->c());
+    REPORTER_ASSERT(r, 4 == integer->d());
+}
+
+DEF_TEST(FlattenRecordedDrawable, r) {
+    // Record a set of canvas draw commands
+    SkPictureRecorder recorder;
+    SkCanvas* canvas = recorder.beginRecording(1000.0f, 1000.0f);
+    SkPaint paint;
+    paint.setColor(SK_ColorGREEN);
+    canvas->drawPoint(42.0f, 17.0f, paint);
+    paint.setColor(SK_ColorRED);
+    canvas->drawPaint(paint);
+    SkPaint textPaint;
+    textPaint.setColor(SK_ColorBLUE);
+    canvas->drawString("TEXT", 467.0f, 100.0f, textPaint);
+
+    // Draw some drawables as well
+    sk_sp<SkDrawable> drawable(new IntDrawable(1, 2, 3, 4));
+    sk_sp<RootDrawable> root(new RootDrawable(5, 6, 7, 8, paint, 9, 10, 11, 12, drawable.get()));
+    canvas->drawDrawable(root.get(), 747.0f, 242.0f);
+    sk_sp<PaintDrawable> paintDrawable(new PaintDrawable(paint));
+    canvas->drawDrawable(paintDrawable.get(), 500.0, 500.0f);
+    sk_sp<CompoundDrawable> comDrawable(new CompoundDrawable(13, 14, 15, 16, textPaint));
+    canvas->drawDrawable(comDrawable.get(), 10.0f, 10.0f);
+
+    // Serialize the recorded drawable
+    sk_sp<SkDrawable> recordedDrawable = recorder.finishRecordingAsDrawable();
+    SkBinaryWriteBuffer writeBuffer;
+    writeBuffer.writeFlattenable(recordedDrawable.get());
+
+    // Copy the contents of the write buffer into a read buffer
+    sk_sp<SkData> data = SkData::MakeUninitialized(writeBuffer.bytesWritten());
+    writeBuffer.writeToMemory(data->writable_data());
+    SkReadBuffer readBuffer(data->data(), data->size());
+    register_test_drawables(readBuffer);
+
+    // Deserialize and verify the drawable
+    sk_sp<SkDrawable> out((SkDrawable*)readBuffer.readFlattenable(SkFlattenable::kSkDrawable_Type));
+    REPORTER_ASSERT(r, out);
+    REPORTER_ASSERT(r, !strcmp("SkRecordedDrawable", out->getTypeName()));
+}
diff --git a/src/third_party/skia/tests/FlattenableCustomFactory.cpp b/src/third_party/skia/tests/FlattenableCustomFactory.cpp
new file mode 100644
index 0000000..e83311b
--- /dev/null
+++ b/src/third_party/skia/tests/FlattenableCustomFactory.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkFlattenable.h"
+#include "SkReadBuffer.h"
+#include "SkWriteBuffer.h"
+#include "Test.h"
+
+class IntFlattenable : public SkFlattenable {
+public:
+    IntFlattenable(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
+        : fA(a)
+        , fB(b)
+        , fC(c)
+        , fD(d)
+    {}
+
+    void flatten(SkWriteBuffer& buffer) const override {
+        buffer.writeUInt(fA);
+        buffer.writeUInt(fB);
+        buffer.writeUInt(fC);
+        buffer.writeUInt(fD);
+    }
+
+    Factory getFactory() const override { return nullptr; }
+
+    uint32_t a() const { return fA; }
+    uint32_t b() const { return fB; }
+    uint32_t c() const { return fC; }
+    uint32_t d() const { return fD; }
+
+    const char* getTypeName() const override { return "IntFlattenable"; }
+
+private:
+    uint32_t fA;
+    uint32_t fB;
+    uint32_t fC;
+    uint32_t fD;
+};
+
+static sk_sp<SkFlattenable> custom_create_proc(SkReadBuffer& buffer) {
+    uint32_t a = buffer.readUInt();
+    uint32_t b = buffer.readUInt();
+    uint32_t c = buffer.readUInt();
+    uint32_t d = buffer.readUInt();
+    return sk_sp<SkFlattenable>(new IntFlattenable(a + 1, b + 1, c + 1, d + 1));
+}
+
+DEF_TEST(UnflattenWithCustomFactory, r) {
+    // Create and flatten the test flattenable
+    SkBinaryWriteBuffer writeBuffer;
+    sk_sp<SkFlattenable> flattenable1(new IntFlattenable(1, 2, 3, 4));
+    writeBuffer.writeFlattenable(flattenable1.get());
+    sk_sp<SkFlattenable> flattenable2(new IntFlattenable(2, 3, 4, 5));
+    writeBuffer.writeFlattenable(flattenable2.get());
+    sk_sp<SkFlattenable> flattenable3(new IntFlattenable(3, 4, 5, 6));
+    writeBuffer.writeFlattenable(flattenable3.get());
+
+    // Copy the contents of the write buffer into a read buffer
+    sk_sp<SkData> data = SkData::MakeUninitialized(writeBuffer.bytesWritten());
+    writeBuffer.writeToMemory(data->writable_data());
+    SkReadBuffer readBuffer(data->data(), data->size());
+
+    // Register a custom factory with the read buffer
+    readBuffer.setCustomFactory(SkString("IntFlattenable"), &custom_create_proc);
+
+    // Unflatten and verify the flattenables
+    sk_sp<IntFlattenable> out1((IntFlattenable*) readBuffer.readFlattenable(
+            SkFlattenable::kSkUnused_Type));
+    REPORTER_ASSERT(r, out1);
+    REPORTER_ASSERT(r, 2 == out1->a());
+    REPORTER_ASSERT(r, 3 == out1->b());
+    REPORTER_ASSERT(r, 4 == out1->c());
+    REPORTER_ASSERT(r, 5 == out1->d());
+
+    sk_sp<IntFlattenable> out2((IntFlattenable*) readBuffer.readFlattenable(
+            SkFlattenable::kSkUnused_Type));
+    REPORTER_ASSERT(r, out2);
+    REPORTER_ASSERT(r, 3 == out2->a());
+    REPORTER_ASSERT(r, 4 == out2->b());
+    REPORTER_ASSERT(r, 5 == out2->c());
+    REPORTER_ASSERT(r, 6 == out2->d());
+
+    sk_sp<IntFlattenable> out3((IntFlattenable*) readBuffer.readFlattenable(
+            SkFlattenable::kSkUnused_Type));
+    REPORTER_ASSERT(r, out3);
+    REPORTER_ASSERT(r, 4 == out3->a());
+    REPORTER_ASSERT(r, 5 == out3->b());
+    REPORTER_ASSERT(r, 6 == out3->c());
+    REPORTER_ASSERT(r, 7 == out3->d());
+}
diff --git a/src/third_party/skia/tests/FlattenableFactoryToName.cpp b/src/third_party/skia/tests/FlattenableFactoryToName.cpp
new file mode 100644
index 0000000..fc47dfc
--- /dev/null
+++ b/src/third_party/skia/tests/FlattenableFactoryToName.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkAlphaThresholdFilter.h"
+#include "SkImage.h"
+#include "SkRegion.h"
+#include "Test.h"
+
+static void test_flattenable(skiatest::Reporter* r,
+                             const SkFlattenable* f,
+                             const char* desc) {
+    if (f) {
+        SkFlattenable::Factory factory = f->getFactory();
+        REPORTER_ASSERT(r, factory);
+        if (factory) {
+            if (!SkFlattenable::FactoryToName(factory)) {
+                ERRORF(r, "SkFlattenable::FactoryToName() fails with %s.", desc);
+            }
+        }
+    }
+}
+
+DEF_TEST(FlattenableFactoryToName, r) {
+    SkIRect rects[2];
+    rects[0] = SkIRect::MakeXYWH(0, 150, 500, 200);
+    rects[1] = SkIRect::MakeXYWH(150, 0, 200, 500);
+    SkRegion region;
+    region.setRects(rects, 2);
+    sk_sp<SkImageFilter> filter(SkAlphaThresholdFilter::Make(region, 0.2f, 0.7f, nullptr));
+    test_flattenable(r, filter.get(), "SkAlphaThresholdFilter()");
+
+    SkBitmap bm;
+    bm.allocN32Pixels(8, 8);
+    bm.eraseColor(SK_ColorCYAN);
+    sk_sp<SkImage> image(SkImage::MakeFromBitmap(bm));
+    test_flattenable(r, image->makeShader().get(), "SkImage::newShader()");
+}
diff --git a/src/third_party/skia/tests/Float16Test.cpp b/src/third_party/skia/tests/Float16Test.cpp
new file mode 100644
index 0000000..64873c3
--- /dev/null
+++ b/src/third_party/skia/tests/Float16Test.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "SkAutoPixmapStorage.h"
+#include "SkColor.h"
+#include "SkHalf.h"
+#include "SkOpts.h"
+#include "SkPixmap.h"
+#include "SkPM4f.h"
+#include "SkRandom.h"
+
+#include <cmath>
+
+static bool eq_within_half_float(float a, float b) {
+    const float kTolerance = 1.0f / (1 << (8 + 10));
+
+    SkHalf ha = SkFloatToHalf(a);
+    SkHalf hb = SkFloatToHalf(b);
+    float a2 = SkHalfToFloat(ha);
+    float b2 = SkHalfToFloat(hb);
+    return fabsf(a2 - b2) <= kTolerance;
+}
+
+static bool eq_within_half_float(const SkPM4f& a, const SkPM4f& b) {
+    for (int i = 0; i < 4; ++i) {
+        if (!eq_within_half_float(a.fVec[i], b.fVec[i])) {
+            return false;
+        }
+    }
+    return true;
+}
+
+DEF_TEST(color_half_float, reporter) {
+    const int w = 100;
+    const int h = 100;
+
+    SkImageInfo info = SkImageInfo::Make(w, h, kRGBA_F16_SkColorType, kPremul_SkAlphaType);
+
+    SkAutoPixmapStorage pm;
+    pm.alloc(info);
+    REPORTER_ASSERT(reporter, pm.getSafeSize() == SkToSizeT(w * h * sizeof(uint64_t)));
+
+    SkColor4f c4 { 1, 0.5f, 0.25f, 0.5f };
+    pm.erase(c4);
+
+    SkPM4f origpm4 = c4.premul();
+    for (int y = 0; y < pm.height(); ++y) {
+        for (int x = 0; x < pm.width(); ++x) {
+            SkPM4f pm4 = SkPM4f::FromF16(pm.addrF16(x, y));
+            REPORTER_ASSERT(reporter, eq_within_half_float(origpm4, pm4));
+        }
+    }
+}
+
+static bool is_denorm(uint16_t h) {
+    return (h & 0x7fff) < 0x0400;
+}
+
+static bool is_finite(uint16_t h) {
+    return (h & 0x7c00) != 0x7c00;
+}
+
+DEF_TEST(SkHalfToFloat_finite_ftz, r) {
+    for (uint32_t h = 0; h <= 0xffff; h++) {
+        if (!is_finite(h)) {
+            // _finite_ftz() only works for values that can be represented as a finite half float.
+            continue;
+        }
+
+        // _finite_ftz() may flush denorms to zero.  0.0f will compare == with both +0.0f and -0.0f.
+        float expected  = SkHalfToFloat(h),
+              alternate = is_denorm(h) ? 0.0f : expected;
+
+        float actual = SkHalfToFloat_finite_ftz(h)[0];
+
+        REPORTER_ASSERT(r, actual == expected || actual == alternate);
+    }
+}
+
+DEF_TEST(SkFloatToHalf_finite_ftz, r) {
+#if 0
+    for (uint64_t bits = 0; bits <= 0xffffffff; bits++) {
+#else
+    SkRandom rand;
+    for (int i = 0; i < 1000000; i++) {
+        uint32_t bits = rand.nextU();
+#endif
+        float f;
+        memcpy(&f, &bits, 4);
+
+        uint16_t expected = SkFloatToHalf(f);
+        if (!is_finite(expected)) {
+            // _finite_ftz() only works for values that can be represented as a finite half float.
+            continue;
+        }
+
+        uint16_t alternate = expected;
+        if (is_denorm(expected)) {
+            // _finite_ftz() may flush denorms to zero, and happens to keep the sign bit.
+            alternate = std::signbit(f) ? 0x8000 : 0x0000;
+        }
+
+        uint16_t actual = SkFloatToHalf_finite_ftz(Sk4f{f})[0];
+        // _finite_ftz() may truncate instead of rounding, so it may be one too small.
+        REPORTER_ASSERT(r, actual == expected  || actual == expected  - 1 ||
+                           actual == alternate || actual == alternate - 1);
+    }
+}
diff --git a/src/third_party/skia/tests/FloatingPointTextureTest.cpp b/src/third_party/skia/tests/FloatingPointTextureTest.cpp
index d367659..06a99a5 100644
--- a/src/third_party/skia/tests/FloatingPointTextureTest.cpp
+++ b/src/third_party/skia/tests/FloatingPointTextureTest.cpp
@@ -11,71 +11,101 @@
  * 32 bit floating point textures, and indeed floating point test values
  * have been selected to require 32 bits of precision and full IEEE conformance
  */
-#if SK_SUPPORT_GPU
+
 #include <float.h>
 #include "Test.h"
+
+#if SK_SUPPORT_GPU
 #include "GrContext.h"
-#include "GrTexture.h"
-#include "GrContextFactory.h"
-#include "SkGpuDevice.h"
+#include "GrContextPriv.h"
+#include "GrResourceProvider.h"
+#include "GrTextureProxy.h"
+#include "SkHalf.h"
 
 static const int DEV_W = 100, DEV_H = 100;
-static const int FP_CONTROL_ARRAY_SIZE = DEV_W * DEV_H * sizeof(float);
-static const float kMaxIntegerRepresentableInSPFloatingPoint = 16777216;  // 2 ^ 24
-
 static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
 
-DEF_GPUTEST(FloatingPointTextureTest, reporter, factory) {
-    float controlPixelData[FP_CONTROL_ARRAY_SIZE];
-    float readBuffer[FP_CONTROL_ARRAY_SIZE];
-    for (int i = 0; i < FP_CONTROL_ARRAY_SIZE; i += 4) {
-        controlPixelData[i] = FLT_MIN;
-        controlPixelData[i + 1] = FLT_MAX;
-        controlPixelData[i + 2] = FLT_EPSILON;
-        controlPixelData[i + 3] = kMaxIntegerRepresentableInSPFloatingPoint;
+template <typename T>
+void runFPTest(skiatest::Reporter* reporter, GrContext* context,
+               T min, T max, T epsilon, T maxInt, int arraySize, GrPixelConfig config) {
+    if (0 != arraySize % 4) {
+        REPORT_FAILURE(reporter, "(0 != arraySize % 4)",
+                       SkString("arraySize must be divisible by 4."));
+        return;
+    }
+
+    SkTDArray<T> controlPixelData, readBuffer;
+    controlPixelData.setCount(arraySize);
+    readBuffer.setCount(arraySize);
+
+    for (int i = 0; i < arraySize; i += 4) {
+        controlPixelData[i + 0] = min;
+        controlPixelData[i + 1] = max;
+        controlPixelData[i + 2] = epsilon;
+        controlPixelData[i + 3] = maxInt;
     }
 
     for (int origin = 0; origin < 2; ++origin) {
-        int glCtxTypeCnt = 1;
-        glCtxTypeCnt = GrContextFactory::kGLContextTypeCnt;
-        for (int glCtxType = 0; glCtxType < glCtxTypeCnt; ++glCtxType) {
-            GrTextureDesc desc;
-            desc.fFlags = kRenderTarget_GrTextureFlagBit;
-            desc.fWidth = DEV_W;
-            desc.fHeight = DEV_H;
-            desc.fConfig = kRGBA_float_GrPixelConfig;
-            desc.fOrigin = 0 == origin ?
-                kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
-
-            GrContext* context = NULL;
-            GrContextFactory::GLContextType type =
-                    static_cast<GrContextFactory::GLContextType>(glCtxType);
-            if (!GrContextFactory::IsRenderingGLContext(type)) {
-                continue;
-            }
-            context = factory->get(type);
-            if (NULL == context){
-                continue;
-            }
-
-            SkAutoTUnref<GrTexture> fpTexture(context->createUncachedTexture(desc,
-                                                                             NULL,
-                                                                             0));
-
-            // Floating point textures are NOT supported everywhere
-            if (NULL == fpTexture) {
-                continue;
-            }
-
-            // write square
-            context->writeTexturePixels(fpTexture, 0, 0, DEV_W, DEV_H, desc.fConfig,
-                    controlPixelData, 0);
-            context->readTexturePixels(fpTexture, 0, 0, DEV_W, DEV_H, desc.fConfig, readBuffer, 0);
-            for (int j = 0; j < FP_CONTROL_ARRAY_SIZE; ++j) {
-                REPORTER_ASSERT(reporter, readBuffer[j] == controlPixelData[j]);
-            }
+        GrSurfaceDesc desc;
+        desc.fFlags = kRenderTarget_GrSurfaceFlag;
+        desc.fWidth = DEV_W;
+        desc.fHeight = DEV_H;
+        desc.fConfig = config;
+        desc.fOrigin = 0 == origin ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
+        sk_sp<GrTextureProxy> fpProxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                     desc, SkBudgeted::kNo,
+                                                                     controlPixelData.begin(), 0);
+        // Floating point textures are NOT supported everywhere
+        if (!fpProxy) {
+            continue;
         }
+
+        sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                    std::move(fpProxy), nullptr);
+        REPORTER_ASSERT(reporter, sContext);
+
+        bool result = context->contextPriv().readSurfacePixels(sContext.get(),
+                                                               0, 0, DEV_W, DEV_H,
+                                                               desc.fConfig, nullptr,
+                                                               readBuffer.begin(), 0);
+        REPORTER_ASSERT(reporter, result);
+        REPORTER_ASSERT(reporter,
+                        0 == memcmp(readBuffer.begin(), controlPixelData.begin(), readBuffer.bytes()));
     }
 }
 
+static const int RGBA32F_CONTROL_ARRAY_SIZE = DEV_W * DEV_H * 4;
+static const float kMaxIntegerRepresentableInSPFloatingPoint = 16777216;  // 2 ^ 24
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(FloatingPointTextureTest, reporter, ctxInfo) {
+    runFPTest<float>(reporter, ctxInfo.grContext(), FLT_MIN, FLT_MAX, FLT_EPSILON,
+                     kMaxIntegerRepresentableInSPFloatingPoint,
+                     RGBA32F_CONTROL_ARRAY_SIZE, kRGBA_float_GrPixelConfig);
+}
+
+static const int RG32F_CONTROL_ARRAY_SIZE = DEV_W * DEV_H * 2;
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(FloatingPointTextureTest_RG, reporter, ctxInfo) {
+    runFPTest<float>(reporter, ctxInfo.grContext(), FLT_MIN, FLT_MAX, FLT_EPSILON,
+                     kMaxIntegerRepresentableInSPFloatingPoint,
+                     RG32F_CONTROL_ARRAY_SIZE, kRG_float_GrPixelConfig);
+}
+
+static const int HALF_ALPHA_CONTROL_ARRAY_SIZE = DEV_W * DEV_H * 1 /*alpha-only*/;
+static const SkHalf kMaxIntegerRepresentableInHalfFloatingPoint = 0x6800;  // 2 ^ 11
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(HalfFloatAlphaTextureTest, reporter, ctxInfo) {
+    runFPTest<SkHalf>(reporter, ctxInfo.grContext(), SK_HalfMin, SK_HalfMax, SK_HalfEpsilon,
+        kMaxIntegerRepresentableInHalfFloatingPoint,
+        HALF_ALPHA_CONTROL_ARRAY_SIZE, kAlpha_half_GrPixelConfig);
+}
+
+static const int HALF_RGBA_CONTROL_ARRAY_SIZE = DEV_W * DEV_H * 4 /*RGBA*/;
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(HalfFloatRGBATextureTest, reporter, ctxInfo) {
+    runFPTest<SkHalf>(reporter, ctxInfo.grContext(), SK_HalfMin, SK_HalfMax, SK_HalfEpsilon,
+        kMaxIntegerRepresentableInHalfFloatingPoint,
+        HALF_RGBA_CONTROL_ARRAY_SIZE, kRGBA_half_GrPixelConfig);
+}
+
 #endif
diff --git a/src/third_party/skia/tests/FontConfigParser.cpp b/src/third_party/skia/tests/FontConfigParser.cpp
deleted file mode 100644
index 86b2b1d..0000000
--- a/src/third_party/skia/tests/FontConfigParser.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Resources.h"
-#include "SkFontConfigParser_android.h"
-#include "Test.h"
-
-int CountFallbacks(SkTDArray<FontFamily*> fontFamilies) {
-    int countOfFallbackFonts = 0;
-    for (int i = 0; i < fontFamilies.count(); i++) {
-        if (fontFamilies[i]->fIsFallbackFont) {
-            countOfFallbackFonts++;
-        }
-    }
-    return countOfFallbackFonts;
-}
-
-void ValidateLoadedFonts(SkTDArray<FontFamily*> fontFamilies,
-                         skiatest::Reporter* reporter) {
-    REPORTER_ASSERT(reporter, fontFamilies[0]->fNames.count() == 5);
-    REPORTER_ASSERT(reporter, !strcmp(fontFamilies[0]->fNames[0].c_str(), "sans-serif"));
-    REPORTER_ASSERT(reporter,
-                    !strcmp(fontFamilies[0]->fFonts[0].fFileName.c_str(),
-                            "Roboto-Regular.ttf"));
-    REPORTER_ASSERT(reporter, !fontFamilies[0]->fIsFallbackFont);
-}
-
-void DumpLoadedFonts(SkTDArray<FontFamily*> fontFamilies) {
-#if SK_DEBUG_FONTS
-    for (int i = 0; i < fontFamilies.count(); ++i) {
-        SkDebugf("Family %d:\n", i);
-        switch(fontFamilies[i]->fVariant) {
-            case SkPaintOptionsAndroid::kElegant_Variant: SkDebugf("  elegant"); break;
-            case SkPaintOptionsAndroid::kCompact_Variant: SkDebugf("  compact"); break;
-            default: break;
-        }
-        if (!fontFamilies[i]->fLanguage.getTag().isEmpty()) {
-            SkDebugf("  language: %s", fontFamilies[i]->fLanguage.getTag().c_str());
-        }
-        for (int j = 0; j < fontFamilies[i]->fNames.count(); ++j) {
-            SkDebugf("  name %s\n", fontFamilies[i]->fNames[j].c_str());
-        }
-        for (int j = 0; j < fontFamilies[i]->fFonts.count(); ++j) {
-            const FontFileInfo& ffi = fontFamilies[i]->fFonts[j];
-            SkDebugf("  file (%d %s %d) %s\n",
-                     ffi.fWeight,
-                     ffi.fPaintOptions.getLanguage().getTag().isEmpty() ? "" :
-                         ffi.fPaintOptions.getLanguage().getTag().c_str(),
-                     ffi.fPaintOptions.getFontVariant(),
-                     ffi.fFileName.c_str());
-        }
-    }
-#endif // SK_DEBUG_FONTS
-}
-
-DEF_TEST(FontConfigParserAndroid, reporter) {
-
-    bool resourcesMissing = false;
-
-    SkTDArray<FontFamily*> preV17FontFamilies;
-    SkFontConfigParser::GetTestFontFamilies(preV17FontFamilies,
-        GetResourcePath("android_fonts/pre_v17/system_fonts.xml").c_str(),
-        GetResourcePath("android_fonts/pre_v17/fallback_fonts.xml").c_str());
-
-    if (preV17FontFamilies.count() > 0) {
-        REPORTER_ASSERT(reporter, preV17FontFamilies.count() == 14);
-        REPORTER_ASSERT(reporter, CountFallbacks(preV17FontFamilies) == 10);
-
-        DumpLoadedFonts(preV17FontFamilies);
-        ValidateLoadedFonts(preV17FontFamilies, reporter);
-    } else {
-        resourcesMissing = true;
-    }
-
-
-    SkTDArray<FontFamily*> v17FontFamilies;
-    SkFontConfigParser::GetTestFontFamilies(v17FontFamilies,
-        GetResourcePath("android_fonts/v17/system_fonts.xml").c_str(),
-        GetResourcePath("android_fonts/v17/fallback_fonts.xml").c_str());
-
-    if (v17FontFamilies.count() > 0) {
-        REPORTER_ASSERT(reporter, v17FontFamilies.count() == 41);
-        REPORTER_ASSERT(reporter, CountFallbacks(v17FontFamilies) == 31);
-
-        DumpLoadedFonts(v17FontFamilies);
-        ValidateLoadedFonts(v17FontFamilies, reporter);
-    } else {
-        resourcesMissing = true;
-    }
-
-
-    SkTDArray<FontFamily*> v22FontFamilies;
-    SkFontConfigParser::GetTestFontFamilies(v22FontFamilies,
-        GetResourcePath("android_fonts/v22/fonts.xml").c_str(),
-        NULL);
-
-    if (v22FontFamilies.count() > 0) {
-        REPORTER_ASSERT(reporter, v22FontFamilies.count() == 53);
-        REPORTER_ASSERT(reporter, CountFallbacks(v22FontFamilies) == 42);
-
-        DumpLoadedFonts(v22FontFamilies);
-        ValidateLoadedFonts(v22FontFamilies, reporter);
-    } else {
-        resourcesMissing = true;
-    }
-
-    if (resourcesMissing) {
-        SkDebugf("---- Resource files missing for FontConfigParser test\n");
-    }
-}
-
diff --git a/src/third_party/skia/tests/FontHostStreamTest.cpp b/src/third_party/skia/tests/FontHostStreamTest.cpp
index d4a4924..c3bc878 100644
--- a/src/third_party/skia/tests/FontHostStreamTest.cpp
+++ b/src/third_party/skia/tests/FontHostStreamTest.cpp
@@ -9,7 +9,6 @@
 #include "SkCanvas.h"
 #include "SkColor.h"
 #include "SkFontDescriptor.h"
-#include "SkFontHost.h"
 #include "SkGraphics.h"
 #include "SkPaint.h"
 #include "SkPoint.h"
@@ -40,9 +39,6 @@
     const int xOff = itest.fLeft - iref.fLeft;
     const int yOff = itest.fTop - iref.fTop;
 
-    SkAutoLockPixels alpRef(ref);
-    SkAutoLockPixels alpTest(test);
-
     for (int y = 0; y < test.height(); ++y) {
         for (int x = 0; x < test.width(); ++x) {
             SkColor testColor = test.getColor(x, y);
@@ -70,9 +66,7 @@
         paint.setColor(SK_ColorGRAY);
         paint.setTextSize(SkIntToScalar(30));
 
-        SkTypeface* fTypeface = SkTypeface::CreateFromName("Georgia",
-                                                           SkTypeface::kNormal);
-        SkSafeUnref(paint.setTypeface(fTypeface));
+        paint.setTypeface(SkTypeface::MakeFromName("Georgia", SkFontStyle()));
 
         SkIRect origRect = SkIRect::MakeWH(64, 64);
         SkBitmap origBitmap;
@@ -88,24 +82,20 @@
 
         // Test: origTypeface and streamTypeface from orig data draw the same
         drawBG(&origCanvas);
-        origCanvas.drawText("A", 1, point.fX, point.fY, paint);
+        origCanvas.drawString("A", point.fX, point.fY, paint);
 
-        SkTypeface* origTypeface = paint.getTypeface();
-        SkAutoTUnref<SkTypeface> aur;
-        if (NULL == origTypeface) {
-            origTypeface = aur.reset(SkTypeface::RefDefault());
-        }
-
+        sk_sp<SkTypeface> typeface(paint.getTypeface() ? paint.refTypeface()
+                                                       : SkTypeface::MakeDefault());
         int ttcIndex;
-        SkAutoTUnref<SkStreamAsset> fontData(origTypeface->openStream(&ttcIndex));
-        SkTypeface* streamTypeface = SkTypeface::CreateFromStream(fontData);
+        std::unique_ptr<SkStreamAsset> fontData(typeface->openStream(&ttcIndex));
+        sk_sp<SkTypeface> streamTypeface(SkTypeface::MakeFromStream(fontData.release()));
 
         SkFontDescriptor desc;
         bool isLocalStream = false;
         streamTypeface->getFontDescriptor(&desc, &isLocalStream);
         REPORTER_ASSERT(reporter, isLocalStream);
 
-        SkSafeUnref(paint.setTypeface(streamTypeface));
+        paint.setTypeface(streamTypeface);
         drawBG(&streamCanvas);
         streamCanvas.drawPosText("A", 1, &point, paint);
 
diff --git a/src/third_party/skia/tests/FontHostTest.cpp b/src/third_party/skia/tests/FontHostTest.cpp
index 249fe7b..3d54c57 100644
--- a/src/third_party/skia/tests/FontHostTest.cpp
+++ b/src/third_party/skia/tests/FontHostTest.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "Resources.h"
+#include "SkAutoMalloc.h"
 #include "SkEndian.h"
 #include "SkFontStream.h"
 #include "SkOSFile.h"
@@ -31,7 +32,7 @@
 
 // Test that getUnitsPerEm() agrees with a direct lookup in the 'head' table
 // (if that table is available).
-static void test_unitsPerEm(skiatest::Reporter* reporter, SkTypeface* face) {
+static void test_unitsPerEm(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) {
     int nativeUPEM = face->getUnitsPerEm();
 
     int tableUPEM = -1;
@@ -50,7 +51,7 @@
 
 // Test that countGlyphs() agrees with a direct lookup in the 'maxp' table
 // (if that table is available).
-static void test_countGlyphs(skiatest::Reporter* reporter, SkTypeface* face) {
+static void test_countGlyphs(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) {
     int nativeGlyphs = face->countGlyphs();
 
     int tableGlyphs = -1;
@@ -86,7 +87,7 @@
 };
 
 // Test that SkPaint::textToGlyphs agrees with SkTypeface::charsToGlyphs.
-static void test_charsToGlyphs(skiatest::Reporter* reporter, SkTypeface* face) {
+static void test_charsToGlyphs(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) {
     uint16_t paintGlyphIds[256];
     uint16_t faceGlyphIds[256];
 
@@ -111,9 +112,8 @@
     }
 }
 
-static void test_fontstream(skiatest::Reporter* reporter,
-                            SkStream* stream, int ttcIndex) {
-    int n = SkFontStream::GetTableTags(stream, ttcIndex, NULL);
+static void test_fontstream(skiatest::Reporter* reporter, SkStream* stream, int ttcIndex) {
+    int n = SkFontStream::GetTableTags(stream, ttcIndex, nullptr);
     SkAutoTArray<SkFontTableTag> array(n);
 
     int n2 = SkFontStream::GetTableTags(stream, ttcIndex, array.get());
@@ -138,34 +138,39 @@
     }
 }
 
-static void test_fontstream(skiatest::Reporter* reporter, SkStream* stream) {
-    int count = SkFontStream::CountTTCEntries(stream);
+static void test_fontstream(skiatest::Reporter* reporter) {
+    std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("/fonts/test.ttc"));
+    if (!stream) {
+        SkDebugf("Skipping FontHostTest::test_fontstream\n");
+        return;
+    }
+
+    int count = SkFontStream::CountTTCEntries(stream.get());
 #ifdef DUMP_TTC_TABLES
     SkDebugf("CountTTCEntries %d\n", count);
 #endif
     for (int i = 0; i < count; ++i) {
-        test_fontstream(reporter, stream, i);
+        test_fontstream(reporter, stream.get(), i);
     }
 }
 
-static void test_fontstream(skiatest::Reporter* reporter) {
-    // This test cannot run if there is no resource path.
-    SkString resourcePath = GetResourcePath();
-    if (resourcePath.isEmpty()) {
-        SkDebugf("Could not run fontstream test because resourcePath not specified.");
+static void test_symbolfont(skiatest::Reporter* reporter) {
+    SkUnichar c = 0xf021;
+    uint16_t g;
+    SkPaint paint;
+    paint.setTypeface(MakeResourceAsTypeface("/fonts/SpiderSymbol.ttf"));
+    paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
+    paint.textToGlyphs(&c, 4, &g);
+
+    if (!paint.getTypeface()) {
+        SkDebugf("Skipping FontHostTest::test_symbolfont\n");
         return;
     }
-    SkString filename = SkOSPath::Join(resourcePath.c_str(), "test.ttc");
 
-    SkFILEStream stream(filename.c_str());
-    if (stream.isValid()) {
-        test_fontstream(reporter, &stream);
-    } else {
-        SkDebugf("Could not run fontstream test because test.ttc not found.");
-    }
+    REPORTER_ASSERT(reporter, g == 3);
 }
 
-static void test_tables(skiatest::Reporter* reporter, SkTypeface* face) {
+static void test_tables(skiatest::Reporter* reporter, const sk_sp<SkTypeface>& face) {
     if (false) { // avoid bit rot, suppress warning
         SkFontID fontID = face->uniqueID();
         REPORTER_ASSERT(reporter, fontID);
@@ -210,7 +215,7 @@
 
 static void test_tables(skiatest::Reporter* reporter) {
     static const char* const gNames[] = {
-        NULL,   // default font
+        nullptr,   // default font
         "Helvetica", "Arial",
         "Times", "Times New Roman",
         "Courier", "Courier New",
@@ -219,7 +224,7 @@
     };
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); ++i) {
-        SkAutoTUnref<SkTypeface> face(SkTypeface::CreateFromName(gNames[i], SkTypeface::kNormal));
+        sk_sp<SkTypeface> face(SkTypeface::MakeFromName(gNames[i], SkFontStyle()));
         if (face) {
 #ifdef DUMP_TABLES
             SkDebugf("%s\n", gNames[i]);
@@ -238,7 +243,7 @@
  */
 static void test_advances(skiatest::Reporter* reporter) {
     static const char* const faces[] = {
-        NULL,   // default font
+        nullptr,   // default font
         "Arial", "Times", "Times New Roman", "Helvetica", "Courier",
         "Courier New", "Verdana", "monospace",
     };
@@ -273,8 +278,7 @@
     char txt[] = "long.text.with.lots.of.dots.";
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(faces); i++) {
-        SkAutoTUnref<SkTypeface> face(SkTypeface::CreateFromName(faces[i], SkTypeface::kNormal));
-        paint.setTypeface(face);
+        paint.setTypeface(SkTypeface::MakeFromName(faces[i], SkFontStyle()));
 
         for (size_t j = 0; j  < SK_ARRAY_COUNT(settings); j++) {
             paint.setHinting(settings[j].hinting);
@@ -307,6 +311,7 @@
     test_tables(reporter);
     test_fontstream(reporter);
     test_advances(reporter);
+    test_symbolfont(reporter);
 }
 
 // need tests for SkStrSearch
diff --git a/src/third_party/skia/tests/FontMgrAndroidParserTest.cpp b/src/third_party/skia/tests/FontMgrAndroidParserTest.cpp
new file mode 100644
index 0000000..cbcfb3b
--- /dev/null
+++ b/src/third_party/skia/tests/FontMgrAndroidParserTest.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Resources.h"
+#include "SkCommandLineFlags.h"
+#include "SkFixed.h"
+#include "SkFontMgr_android_parser.h"
+#include "Test.h"
+
+#include <cmath>
+#include <cstdio>
+
+DECLARE_bool(verboseFontMgr);
+
+int CountFallbacks(SkTDArray<FontFamily*> fontFamilies) {
+    int countOfFallbackFonts = 0;
+    for (int i = 0; i < fontFamilies.count(); i++) {
+        if (fontFamilies[i]->fIsFallbackFont) {
+            countOfFallbackFonts++;
+        }
+    }
+    return countOfFallbackFonts;
+}
+
+//https://tools.ietf.org/html/rfc5234#appendix-B.1
+static bool isALPHA(int c) {
+    return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
+}
+
+//https://tools.ietf.org/html/rfc5234#appendix-B.1
+static bool isDIGIT(int c) {
+    return ('0' <= c && c <= '9');
+}
+
+void ValidateLoadedFonts(SkTDArray<FontFamily*> fontFamilies, const char* firstExpectedFile,
+                         skiatest::Reporter* reporter) {
+    REPORTER_ASSERT(reporter, fontFamilies[0]->fNames.count() == 5);
+    REPORTER_ASSERT(reporter, !strcmp(fontFamilies[0]->fNames[0].c_str(), "sans-serif"));
+    REPORTER_ASSERT(reporter,
+                    !strcmp(fontFamilies[0]->fFonts[0].fFileName.c_str(), firstExpectedFile));
+    REPORTER_ASSERT(reporter, !fontFamilies[0]->fIsFallbackFont);
+
+    // Check that the languages are all sane.
+    for (int i = 0; i < fontFamilies.count(); ++i) {
+        const SkString& lang = fontFamilies[i]->fLanguage.getTag();
+        for (size_t j = 0; j < lang.size(); ++j) {
+            int c = lang[j];
+            REPORTER_ASSERT(reporter, isALPHA(c) || isDIGIT(c) || '-' == c);
+        }
+    }
+
+    // All file names in the test configuration files start with a capital letter.
+    // This is not a general requirement, but it is true of all the test configuration data.
+    // Verifying ensures the filenames have been read sanely and have not been 'sliced'.
+    for (int i = 0; i < fontFamilies.count(); ++i) {
+        FontFamily& family = *fontFamilies[i];
+        for (int j = 0; j < family.fFonts.count(); ++j) {
+            FontFileInfo& file = family.fFonts[j];
+            REPORTER_ASSERT(reporter, !file.fFileName.isEmpty() &&
+                                      file.fFileName[0] >= 'A' &&
+                                      file.fFileName[0] <= 'Z');
+        }
+    }
+}
+
+void DumpLoadedFonts(SkTDArray<FontFamily*> fontFamilies, const char* label) {
+    if (!FLAGS_verboseFontMgr) {
+        return;
+    }
+
+    SkDebugf("\n--- Dumping %s\n", label);
+    for (int i = 0; i < fontFamilies.count(); ++i) {
+        SkDebugf("Family %d:\n", i);
+        switch(fontFamilies[i]->fVariant) {
+            case kElegant_FontVariant: SkDebugf("  elegant\n"); break;
+            case kCompact_FontVariant: SkDebugf("  compact\n"); break;
+            default: break;
+        }
+        SkDebugf("  basePath %s\n", fontFamilies[i]->fBasePath.c_str());
+        if (!fontFamilies[i]->fLanguage.getTag().isEmpty()) {
+            SkDebugf("  language %s\n", fontFamilies[i]->fLanguage.getTag().c_str());
+        }
+        for (int j = 0; j < fontFamilies[i]->fNames.count(); ++j) {
+            SkDebugf("  name %s\n", fontFamilies[i]->fNames[j].c_str());
+        }
+        for (int j = 0; j < fontFamilies[i]->fFonts.count(); ++j) {
+            const FontFileInfo& ffi = fontFamilies[i]->fFonts[j];
+            SkDebugf("  file (%d) %s#%d", ffi.fWeight, ffi.fFileName.c_str(), ffi.fIndex);
+            for (const auto& coordinate : ffi.fVariationDesignPosition) {
+                SkDebugf(" @'%c%c%c%c'=%f",
+                         (coordinate.axis >> 24) & 0xFF,
+                         (coordinate.axis >> 16) & 0xFF,
+                         (coordinate.axis >>  8) & 0xFF,
+                         (coordinate.axis      ) & 0xFF,
+                         coordinate.value);
+            }
+            SkDebugf("\n");
+        }
+    }
+    SkDebugf("\n\n");
+}
+
+template <int N, typename T> static double test_parse_fixed_r(skiatest::Reporter* reporter,
+                                                              double low, double high, double inc)
+{
+    double SK_FixedMax_double = nextafter(1 << (sizeof(T) * CHAR_BIT - N - 1), 0.0);
+    double SK_FixedEpsilon_double = (1.0 / (1 << N));
+    double maxError = 0;
+    char buffer[64];
+    for (double f = low; f < high; f += inc) {
+        SkString s;
+        // 'sprintf' formatting as expected depends on the current locale being "C".
+        // We currently expect tests and tools to run in the "C" locale.
+        sprintf(buffer, "%.20f", f);
+        T fix;
+        bool b = parse_fixed<N>(buffer, &fix);
+        if (b) {
+            double f2 = fix * SK_FixedEpsilon_double;
+            double error = fabs(f - f2);
+            REPORTER_ASSERT(reporter,  error <= SK_FixedEpsilon_double);
+            maxError = SkTMax(maxError, error);
+        } else {
+            REPORTER_ASSERT(reporter, f < -SK_FixedMax_double || SK_FixedMax_double < f);
+        }
+    }
+
+    //SkDebugf("maxError: %.20f\n", maxError);
+    return maxError;
+}
+
+static void test_parse_fixed(skiatest::Reporter* reporter) {
+    test_parse_fixed_r<27, int32_t>(reporter, -8.1, -7.9, 0.000001);
+    test_parse_fixed_r<27, int32_t>(reporter, -0.1, 0.1, 0.000001);
+    test_parse_fixed_r<27, int32_t>(reporter, 7.9, 8.1, 0.000001);
+    test_parse_fixed_r<16, int32_t>(reporter, -0.125, 0.125, 1.0 / (1 << 19));
+    test_parse_fixed_r<16, int32_t>(reporter, -32768.125, -32766.875, 1.0 / (1 << 17));
+    test_parse_fixed_r<16, int32_t>(reporter, 32766.875, 32768.125, 1.0 / (1 << 17));
+    test_parse_fixed_r<16, int32_t>(reporter, -1.1, 1.1, 0.0001);
+
+    SkFixed fix;
+    REPORTER_ASSERT(reporter, !parse_fixed<27>("-17.1", &fix));
+    REPORTER_ASSERT(reporter, !parse_fixed<16>("32768", &fix));
+    REPORTER_ASSERT(reporter, !parse_fixed<16>("", &fix));
+    REPORTER_ASSERT(reporter, !parse_fixed<16>(".", &fix));
+    REPORTER_ASSERT(reporter, !parse_fixed<16>("123.", &fix));
+    REPORTER_ASSERT(reporter, !parse_fixed<16>("a", &fix));
+    REPORTER_ASSERT(reporter, !parse_fixed<16>(".123a", &fix));
+}
+
+DEF_TEST(FontMgrAndroidParser, reporter) {
+    test_parse_fixed(reporter);
+
+    bool resourcesMissing = false;
+
+    SkTDArray<FontFamily*> preV17FontFamilies;
+    SkFontMgr_Android_Parser::GetCustomFontFamilies(preV17FontFamilies,
+        SkString("/custom/font/path/"),
+        GetResourcePath("android_fonts/pre_v17/system_fonts.xml").c_str(),
+        GetResourcePath("android_fonts/pre_v17/fallback_fonts.xml").c_str());
+
+    if (preV17FontFamilies.count() > 0) {
+        REPORTER_ASSERT(reporter, preV17FontFamilies.count() == 14);
+        REPORTER_ASSERT(reporter, CountFallbacks(preV17FontFamilies) == 10);
+
+        DumpLoadedFonts(preV17FontFamilies, "pre version 17");
+        ValidateLoadedFonts(preV17FontFamilies, "Roboto-Regular.ttf", reporter);
+    } else {
+        resourcesMissing = true;
+    }
+    preV17FontFamilies.deleteAll();
+
+
+    SkTDArray<FontFamily*> v17FontFamilies;
+    SkFontMgr_Android_Parser::GetCustomFontFamilies(v17FontFamilies,
+        SkString("/custom/font/path/"),
+        GetResourcePath("android_fonts/v17/system_fonts.xml").c_str(),
+        GetResourcePath("android_fonts/v17/fallback_fonts.xml").c_str(),
+        GetResourcePath("android_fonts/v17").c_str());
+
+    if (v17FontFamilies.count() > 0) {
+        REPORTER_ASSERT(reporter, v17FontFamilies.count() == 56);
+        REPORTER_ASSERT(reporter, CountFallbacks(v17FontFamilies) == 46);
+
+        DumpLoadedFonts(v17FontFamilies, "version 17");
+        ValidateLoadedFonts(v17FontFamilies, "Roboto-Regular.ttf", reporter);
+    } else {
+        resourcesMissing = true;
+    }
+    v17FontFamilies.deleteAll();
+
+
+    SkTDArray<FontFamily*> v22FontFamilies;
+    SkFontMgr_Android_Parser::GetCustomFontFamilies(v22FontFamilies,
+        SkString("/custom/font/path/"),
+        GetResourcePath("android_fonts/v22/fonts.xml").c_str(),
+        nullptr);
+
+    if (v22FontFamilies.count() > 0) {
+        REPORTER_ASSERT(reporter, v22FontFamilies.count() == 54);
+        REPORTER_ASSERT(reporter, CountFallbacks(v22FontFamilies) == 42);
+
+        DumpLoadedFonts(v22FontFamilies, "version 22");
+        ValidateLoadedFonts(v22FontFamilies, "Roboto-Thin.ttf", reporter);
+    } else {
+        resourcesMissing = true;
+    }
+    v22FontFamilies.deleteAll();
+
+    if (resourcesMissing) {
+        SkDebugf("---- Resource files missing for FontConfigParser test\n");
+    }
+}
diff --git a/src/third_party/skia/tests/FontMgrTest.cpp b/src/third_party/skia/tests/FontMgrTest.cpp
index a1dc9dc..ef420f3 100644
--- a/src/third_party/skia/tests/FontMgrTest.cpp
+++ b/src/third_party/skia/tests/FontMgrTest.cpp
@@ -5,16 +5,21 @@
  * found in the LICENSE file.
  */
 
+#include "SkAdvancedTypefaceMetrics.h"
 #include "SkCommandLineFlags.h"
+#include "SkFont.h"
 #include "SkFontMgr.h"
+#include "SkPaint.h"
 #include "SkTypeface.h"
 #include "Test.h"
 
-#include "SkFont.h"
-#include "SkPaint.h"
+#include <initializer_list>
+#include <limits>
+#include <vector>
+
 static void test_font(skiatest::Reporter* reporter) {
     uint32_t flags = 0;
-    SkAutoTUnref<SkFont> font(SkFont::Create(NULL, 24, SkFont::kA8_MaskType, flags));
+    sk_sp<SkFont> font(SkFont::Make(nullptr, 24, SkFont::kA8_MaskType, flags));
 
     REPORTER_ASSERT(reporter, font->getTypeface());
     REPORTER_ASSERT(reporter, 24 == font->getSize());
@@ -34,7 +39,7 @@
     REPORTER_ASSERT(reporter, glyphs[0] != glyphs[1]); // 'h' != 'e'
     REPORTER_ASSERT(reporter, glyphs[2] == glyphs[3]); // 'l' == 'l'
 
-    SkAutoTUnref<SkFont> newFont(font->cloneWithSize(36));
+    sk_sp<SkFont> newFont(font->makeWithSize(36));
     REPORTER_ASSERT(reporter, newFont.get());
     REPORTER_ASSERT(reporter, font->getTypeface() == newFont->getTypeface());
     REPORTER_ASSERT(reporter, 36 == newFont->getSize());   // double check we haven't changed
@@ -42,7 +47,7 @@
 
     SkPaint paint;
     paint.setTextSize(18);
-    font.reset(SkFont::Testing_CreateFromPaint(paint));
+    font = SkFont::Testing_CreateFromPaint(paint);
     REPORTER_ASSERT(reporter, font.get());
     REPORTER_ASSERT(reporter, font->getSize() == paint.getTextSize());
     REPORTER_ASSERT(reporter, SkFont::kBW_MaskType == font->getMaskType());
@@ -59,14 +64,12 @@
     };
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(inNames); ++i) {
-        SkAutoTUnref<SkTypeface> first(SkTypeface::CreateFromName(inNames[i],
-                                                          SkTypeface::kNormal));
-        if (NULL == first.get()) {
+        sk_sp<SkTypeface> first(SkTypeface::MakeFromName(inNames[i], SkFontStyle()));
+        if (nullptr == first.get()) {
             continue;
         }
         for (int j = 0; j < 10; ++j) {
-            SkAutoTUnref<SkTypeface> face(SkTypeface::CreateFromName(inNames[i],
-                                                         SkTypeface::kNormal));
+            sk_sp<SkTypeface> face(SkTypeface::MakeFromName(inNames[i], SkFontStyle()));
     #if 0
             SkString name;
             face->getFamilyName(&name);
@@ -79,15 +82,15 @@
 }
 
 static void test_fontiter(skiatest::Reporter* reporter, bool verbose) {
-    SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
+    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
     int count = fm->countFamilies();
 
     for (int i = 0; i < count; ++i) {
         SkString fname;
         fm->getFamilyName(i, &fname);
 
-        SkAutoTUnref<SkFontStyleSet> fnset(fm->matchFamily(fname.c_str()));
-        SkAutoTUnref<SkFontStyleSet> set(fm->createStyleSet(i));
+        sk_sp<SkFontStyleSet> fnset(fm->matchFamily(fname.c_str()));
+        sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
         REPORTER_ASSERT(reporter, fnset->count() == set->count());
 
         if (verbose) {
@@ -100,12 +103,611 @@
             set->getStyle(j, &fs, &sname);
 //            REPORTER_ASSERT(reporter, sname.size() > 0);
 
-            SkAutoTUnref<SkTypeface> face(set->createTypeface(j));
+            sk_sp<SkTypeface> face(set->createTypeface(j));
 //            REPORTER_ASSERT(reporter, face.get());
 
             if (verbose) {
                 SkDebugf("\t[%d] %s [%3d %d %d]\n", j, sname.c_str(),
-                         fs.weight(), fs.width(), fs.isItalic());
+                         fs.weight(), fs.width(), fs.slant());
+            }
+        }
+    }
+}
+
+static void test_match(skiatest::Reporter* reporter) {
+    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+    sk_sp<SkFontStyleSet> styleSet(fm->matchFamily(nullptr));
+    REPORTER_ASSERT(reporter, styleSet);
+}
+
+static void test_matchStyleCSS3(skiatest::Reporter* reporter) {
+    static const SkFontStyle invalidFontStyle(101, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
+
+    class TestTypeface : public SkTypeface {
+    public:
+        TestTypeface(const SkFontStyle& fontStyle) : SkTypeface(fontStyle, false){}
+    protected:
+        SkStreamAsset* onOpenStream(int* ttcIndex) const override { return nullptr; }
+        SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
+                                               const SkDescriptor*) const override {
+            return nullptr;
+        }
+        void onFilterRec(SkScalerContextRec*) const override { }
+        std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override {
+            return nullptr;
+        }
+        void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { }
+        virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
+            uint16_t glyphs[], int glyphCount) const override {
+            if (glyphs && glyphCount > 0) {
+                sk_bzero(glyphs, glyphCount * sizeof(glyphs[0]));
+            }
+            return 0;
+        }
+        int onCountGlyphs() const override { return 0; }
+        int onGetUPEM() const override { return 0; }
+        class EmptyLocalizedStrings : public SkTypeface::LocalizedStrings {
+        public:
+            bool next(SkTypeface::LocalizedString*) override { return false; }
+        };
+        void onGetFamilyName(SkString* familyName) const override {
+            familyName->reset();
+        }
+        SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override {
+            return new EmptyLocalizedStrings;
+        }
+        int onGetVariationDesignPosition(
+                SkFontArguments::VariationPosition::Coordinate coordinates[],
+                int coordinateCount) const override
+        {
+            return 0;
+        }
+        int onGetTableTags(SkFontTableTag tags[]) const override { return 0; }
+        size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override {
+            return 0;
+        }
+    };
+
+    class TestFontStyleSet : public SkFontStyleSet {
+    public:
+        TestFontStyleSet(std::initializer_list<SkFontStyle> styles) : fStyles(styles) {}
+        int count() override { return static_cast<int>(fStyles.size()); }
+        void getStyle(int index, SkFontStyle* style, SkString*) override {
+            if (style) {
+                *style = fStyles[index];
+            }
+        }
+        SkTypeface* createTypeface(int index) override {
+            if (index < 0 || this->count() <= index) {
+                return new TestTypeface(invalidFontStyle);
+            }
+            return new TestTypeface(fStyles[index]);
+        }
+        SkTypeface* matchStyle(const SkFontStyle& pattern) override {
+            return this->matchStyleCSS3(pattern);
+        }
+    private:
+        std::vector<SkFontStyle> fStyles;
+    };
+
+    SkFontStyle condensed_normal_100(SkFontStyle::kThin_Weight,  SkFontStyle::kCondensed_Width, SkFontStyle::kUpright_Slant);
+    SkFontStyle condensed_normal_900(SkFontStyle::kBlack_Weight, SkFontStyle::kCondensed_Width, SkFontStyle::kUpright_Slant);
+    SkFontStyle condensed_italic_100(SkFontStyle::kThin_Weight,  SkFontStyle::kCondensed_Width, SkFontStyle::kItalic_Slant);
+    SkFontStyle condensed_italic_900(SkFontStyle::kBlack_Weight, SkFontStyle::kCondensed_Width, SkFontStyle::kItalic_Slant);
+    SkFontStyle condensed_obliqu_100(SkFontStyle::kThin_Weight,  SkFontStyle::kCondensed_Width, SkFontStyle::kOblique_Slant);
+    SkFontStyle condensed_obliqu_900(SkFontStyle::kBlack_Weight, SkFontStyle::kCondensed_Width, SkFontStyle::kOblique_Slant);
+    SkFontStyle  expanded_normal_100(SkFontStyle::kThin_Weight,  SkFontStyle::kExpanded_Width,  SkFontStyle::kUpright_Slant);
+    SkFontStyle  expanded_normal_900(SkFontStyle::kBlack_Weight, SkFontStyle::kExpanded_Width,  SkFontStyle::kUpright_Slant);
+    SkFontStyle  expanded_italic_100(SkFontStyle::kThin_Weight,  SkFontStyle::kExpanded_Width,  SkFontStyle::kItalic_Slant);
+    SkFontStyle  expanded_italic_900(SkFontStyle::kBlack_Weight, SkFontStyle::kExpanded_Width,  SkFontStyle::kItalic_Slant);
+    SkFontStyle  expanded_obliqu_100(SkFontStyle::kThin_Weight,  SkFontStyle::kExpanded_Width,  SkFontStyle::kOblique_Slant);
+    SkFontStyle  expanded_obliqu_900(SkFontStyle::kBlack_Weight, SkFontStyle::kExpanded_Width,  SkFontStyle::kOblique_Slant);
+
+    SkFontStyle normal_normal_100(SkFontStyle::kThin_Weight,       SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
+    SkFontStyle normal_normal_200(SkFontStyle::kExtraLight_Weight, SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
+    SkFontStyle normal_normal_300(SkFontStyle::kLight_Weight,      SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
+    SkFontStyle normal_normal_400(SkFontStyle::kNormal_Weight,     SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
+    SkFontStyle normal_normal_500(SkFontStyle::kMedium_Weight,     SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
+    SkFontStyle normal_normal_600(SkFontStyle::kSemiBold_Weight,   SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
+    SkFontStyle normal_normal_700(SkFontStyle::kBold_Weight,       SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
+    SkFontStyle normal_normal_800(SkFontStyle::kExtraBold_Weight,  SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
+    SkFontStyle normal_normal_900(SkFontStyle::kBlack_Weight,      SkFontStyle::kNormal_Width, SkFontStyle::kUpright_Slant);
+
+    struct StyleSetTest {
+        TestFontStyleSet styleSet;
+        struct Case {
+            SkFontStyle pattern;
+            SkFontStyle expectedResult;
+        };
+        std::vector<Case> cases;
+    } tests[] = {
+        {
+            { condensed_normal_100,condensed_normal_900,condensed_italic_100,condensed_italic_900,
+               expanded_normal_100, expanded_normal_900, expanded_italic_100, expanded_italic_900 },
+            {
+                { condensed_normal_100, condensed_normal_100 },
+                { condensed_normal_900, condensed_normal_900 },
+                { condensed_italic_100, condensed_italic_100 },
+                { condensed_italic_900, condensed_italic_900 },
+                { expanded_normal_100, expanded_normal_100 },
+                { expanded_normal_900, expanded_normal_900 },
+                { expanded_italic_100, expanded_italic_100 },
+                { expanded_italic_900, expanded_italic_900 },
+            },
+        },
+
+        {
+            { condensed_normal_100,condensed_italic_100,expanded_normal_100,expanded_italic_100 },
+            {
+                { condensed_normal_100, condensed_normal_100 },
+                { condensed_normal_900, condensed_normal_100 },
+                { condensed_italic_100, condensed_italic_100 },
+                { condensed_italic_900, condensed_italic_100 },
+                { expanded_normal_100, expanded_normal_100 },
+                { expanded_normal_900, expanded_normal_100 },
+                { expanded_italic_100, expanded_italic_100 },
+                { expanded_italic_900, expanded_italic_100 },
+            },
+        },
+
+        {
+            { condensed_normal_900,condensed_italic_900,expanded_normal_900,expanded_italic_900 },
+            {
+                { condensed_normal_100, condensed_normal_900 },
+                { condensed_normal_900, condensed_normal_900 },
+                { condensed_italic_100, condensed_italic_900 },
+                { condensed_italic_900, condensed_italic_900 },
+                { expanded_normal_100, expanded_normal_900 },
+                { expanded_normal_900, expanded_normal_900 },
+                { expanded_italic_100, expanded_italic_900 },
+                { expanded_italic_900, expanded_italic_900 },
+            },
+        },
+
+        {
+            { condensed_normal_100,condensed_normal_900,expanded_normal_100,expanded_normal_900 },
+            {
+                { condensed_normal_100, condensed_normal_100 },
+                { condensed_normal_900, condensed_normal_900 },
+                { condensed_italic_100, condensed_normal_100 },
+                { condensed_italic_900, condensed_normal_900 },
+                { expanded_normal_100, expanded_normal_100 },
+                { expanded_normal_900, expanded_normal_900 },
+                { expanded_italic_100, expanded_normal_100 },
+                { expanded_italic_900, expanded_normal_900 },
+            },
+        },
+
+        {
+            { condensed_normal_100,expanded_normal_100 },
+            {
+                { condensed_normal_100, condensed_normal_100 },
+                { condensed_normal_900, condensed_normal_100 },
+                { condensed_italic_100, condensed_normal_100 },
+                { condensed_italic_900, condensed_normal_100 },
+                { expanded_normal_100, expanded_normal_100 },
+                { expanded_normal_900, expanded_normal_100 },
+                { expanded_italic_100, expanded_normal_100 },
+                { expanded_italic_900, expanded_normal_100 },
+            },
+        },
+
+        {
+            { condensed_normal_900,expanded_normal_900 },
+            {
+                { condensed_normal_100, condensed_normal_900 },
+                { condensed_normal_900, condensed_normal_900 },
+                { condensed_italic_100, condensed_normal_900 },
+                { condensed_italic_900, condensed_normal_900 },
+                { expanded_normal_100, expanded_normal_900 },
+                { expanded_normal_900, expanded_normal_900 },
+                { expanded_italic_100, expanded_normal_900 },
+                { expanded_italic_900, expanded_normal_900 },
+            },
+        },
+
+        {
+            { condensed_italic_100,condensed_italic_900,expanded_italic_100,expanded_italic_900 },
+            {
+                { condensed_normal_100, condensed_italic_100 },
+                { condensed_normal_900, condensed_italic_900 },
+                { condensed_italic_100, condensed_italic_100 },
+                { condensed_italic_900, condensed_italic_900 },
+                { expanded_normal_100, expanded_italic_100 },
+                { expanded_normal_900, expanded_italic_900 },
+                { expanded_italic_100, expanded_italic_100 },
+                { expanded_italic_900, expanded_italic_900 },
+            },
+        },
+
+        {
+            { condensed_italic_100,expanded_italic_100 },
+            {
+                { condensed_normal_100, condensed_italic_100 },
+                { condensed_normal_900, condensed_italic_100 },
+                { condensed_italic_100, condensed_italic_100 },
+                { condensed_italic_900, condensed_italic_100 },
+                { expanded_normal_100, expanded_italic_100 },
+                { expanded_normal_900, expanded_italic_100 },
+                { expanded_italic_100, expanded_italic_100 },
+                { expanded_italic_900, expanded_italic_100 },
+            },
+        },
+
+        {
+            { condensed_italic_900,expanded_italic_900 },
+            {
+                { condensed_normal_100, condensed_italic_900 },
+                { condensed_normal_900, condensed_italic_900 },
+                { condensed_italic_100, condensed_italic_900 },
+                { condensed_italic_900, condensed_italic_900 },
+                { expanded_normal_100, expanded_italic_900 },
+                { expanded_normal_900, expanded_italic_900 },
+                { expanded_italic_100, expanded_italic_900 },
+                { expanded_italic_900, expanded_italic_900 },
+            },
+        },
+
+        {
+            { condensed_normal_100,condensed_normal_900,condensed_italic_100,condensed_italic_900 },
+            {
+                { condensed_normal_100, condensed_normal_100 },
+                { condensed_normal_900, condensed_normal_900 },
+                { condensed_italic_100, condensed_italic_100 },
+                { condensed_italic_900, condensed_italic_900 },
+                { expanded_normal_100, condensed_normal_100 },
+                { expanded_normal_900, condensed_normal_900 },
+                { expanded_italic_100, condensed_italic_100 },
+                { expanded_italic_900, condensed_italic_900 },
+            },
+        },
+
+        {
+            { condensed_normal_100,condensed_italic_100 },
+            {
+                { condensed_normal_100, condensed_normal_100 },
+                { condensed_normal_900, condensed_normal_100 },
+                { condensed_italic_100, condensed_italic_100 },
+                { condensed_italic_900, condensed_italic_100 },
+                { expanded_normal_100, condensed_normal_100 },
+                { expanded_normal_900, condensed_normal_100 },
+                { expanded_italic_100, condensed_italic_100 },
+                { expanded_italic_900, condensed_italic_100 },
+            },
+        },
+
+        {
+            { condensed_normal_900,condensed_italic_900 },
+            {
+                { condensed_normal_100, condensed_normal_900 },
+                { condensed_normal_900, condensed_normal_900 },
+                { condensed_italic_100, condensed_italic_900 },
+                { condensed_italic_900, condensed_italic_900 },
+                { expanded_normal_100, condensed_normal_900 },
+                { expanded_normal_900, condensed_normal_900 },
+                { expanded_italic_100, condensed_italic_900 },
+                { expanded_italic_900, condensed_italic_900 },
+            },
+        },
+
+        {
+            { condensed_normal_100,condensed_normal_900 },
+            {
+                { condensed_normal_100, condensed_normal_100 },
+                { condensed_normal_900, condensed_normal_900 },
+                { condensed_italic_100, condensed_normal_100 },
+                { condensed_italic_900, condensed_normal_900 },
+                { expanded_normal_100, condensed_normal_100 },
+                { expanded_normal_900, condensed_normal_900 },
+                { expanded_italic_100, condensed_normal_100 },
+                { expanded_italic_900, condensed_normal_900 },
+            },
+        },
+
+        {
+            { condensed_normal_100 },
+            {
+                { condensed_normal_100, condensed_normal_100 },
+                { condensed_normal_900, condensed_normal_100 },
+                { condensed_italic_100, condensed_normal_100 },
+                { condensed_italic_900, condensed_normal_100 },
+                { expanded_normal_100, condensed_normal_100 },
+                { expanded_normal_900, condensed_normal_100 },
+                { expanded_italic_100, condensed_normal_100 },
+                { expanded_italic_900, condensed_normal_100 },
+            },
+        },
+
+        {
+            { condensed_normal_900 },
+            {
+                { condensed_normal_100, condensed_normal_900 },
+                { condensed_normal_900, condensed_normal_900 },
+                { condensed_italic_100, condensed_normal_900 },
+                { condensed_italic_900, condensed_normal_900 },
+                { expanded_normal_100, condensed_normal_900 },
+                { expanded_normal_900, condensed_normal_900 },
+                { expanded_italic_100, condensed_normal_900 },
+                { expanded_italic_900, condensed_normal_900 },
+            },
+        },
+
+        {
+            { condensed_italic_100,condensed_italic_900 },
+            {
+                { condensed_normal_100, condensed_italic_100 },
+                { condensed_normal_900, condensed_italic_900 },
+                { condensed_italic_100, condensed_italic_100 },
+                { condensed_italic_900, condensed_italic_900 },
+                { expanded_normal_100, condensed_italic_100 },
+                { expanded_normal_900, condensed_italic_900 },
+                { expanded_italic_100, condensed_italic_100 },
+                { expanded_italic_900, condensed_italic_900 },
+            },
+        },
+
+        {
+            { condensed_italic_100 },
+            {
+                { condensed_normal_100, condensed_italic_100 },
+                { condensed_normal_900, condensed_italic_100 },
+                { condensed_italic_100, condensed_italic_100 },
+                { condensed_italic_900, condensed_italic_100 },
+                { expanded_normal_100, condensed_italic_100 },
+                { expanded_normal_900, condensed_italic_100 },
+                { expanded_italic_100, condensed_italic_100 },
+                { expanded_italic_900, condensed_italic_100 },
+            },
+        },
+
+        {
+            { condensed_italic_900 },
+            {
+                { condensed_normal_100, condensed_italic_900 },
+                { condensed_normal_900, condensed_italic_900 },
+                { condensed_italic_100, condensed_italic_900 },
+                { condensed_italic_900, condensed_italic_900 },
+                { expanded_normal_100, condensed_italic_900 },
+                { expanded_normal_900, condensed_italic_900 },
+                { expanded_italic_100, condensed_italic_900 },
+                { expanded_italic_900, condensed_italic_900 },
+            },
+        },
+
+        {
+            { expanded_normal_100,expanded_normal_900,
+              expanded_italic_100,expanded_italic_900 },
+            {
+                { condensed_normal_100, expanded_normal_100 },
+                { condensed_normal_900, expanded_normal_900 },
+                { condensed_italic_100, expanded_italic_100 },
+                { condensed_italic_900, expanded_italic_900 },
+                { condensed_obliqu_100, expanded_italic_100 },
+                { condensed_obliqu_900, expanded_italic_900 },
+                { expanded_normal_100, expanded_normal_100 },
+                { expanded_normal_900, expanded_normal_900 },
+                { expanded_italic_100, expanded_italic_100 },
+                { expanded_italic_900, expanded_italic_900 },
+                { expanded_obliqu_100, expanded_italic_100 },
+                { expanded_obliqu_900, expanded_italic_900 },
+            },
+        },
+
+        {
+            { expanded_normal_100,expanded_italic_100 },
+            {
+                { condensed_normal_100, expanded_normal_100 },
+                { condensed_normal_900, expanded_normal_100 },
+                { condensed_italic_100, expanded_italic_100 },
+                { condensed_italic_900, expanded_italic_100 },
+                { expanded_normal_100, expanded_normal_100 },
+                { expanded_normal_900, expanded_normal_100 },
+                { expanded_italic_100, expanded_italic_100 },
+                { expanded_italic_900, expanded_italic_100 },
+            },
+        },
+
+        {
+            { expanded_normal_900,expanded_italic_900 },
+            {
+                { condensed_normal_100, expanded_normal_900 },
+                { condensed_normal_900, expanded_normal_900 },
+                { condensed_italic_100, expanded_italic_900 },
+                { condensed_italic_900, expanded_italic_900 },
+                { expanded_normal_100, expanded_normal_900 },
+                { expanded_normal_900, expanded_normal_900 },
+                { expanded_italic_100, expanded_italic_900 },
+                { expanded_italic_900, expanded_italic_900 },
+            },
+        },
+
+        {
+            { expanded_normal_100,expanded_normal_900 },
+            {
+                { condensed_normal_100, expanded_normal_100 },
+                { condensed_normal_900, expanded_normal_900 },
+                { condensed_italic_100, expanded_normal_100 },
+                { condensed_italic_900, expanded_normal_900 },
+                { expanded_normal_100, expanded_normal_100 },
+                { expanded_normal_900, expanded_normal_900 },
+                { expanded_italic_100, expanded_normal_100 },
+                { expanded_italic_900, expanded_normal_900 },
+            },
+        },
+
+        {
+            { expanded_normal_100 },
+            {
+                { condensed_normal_100, expanded_normal_100 },
+                { condensed_normal_900, expanded_normal_100 },
+                { condensed_italic_100, expanded_normal_100 },
+                { condensed_italic_900, expanded_normal_100 },
+                { expanded_normal_100, expanded_normal_100 },
+                { expanded_normal_900, expanded_normal_100 },
+                { expanded_italic_100, expanded_normal_100 },
+                { expanded_italic_900, expanded_normal_100 },
+            },
+        },
+
+        {
+            { expanded_normal_900 },
+            {
+                { condensed_normal_100, expanded_normal_900 },
+                { condensed_normal_900, expanded_normal_900 },
+                { condensed_italic_100, expanded_normal_900 },
+                { condensed_italic_900, expanded_normal_900 },
+                { expanded_normal_100, expanded_normal_900 },
+                { expanded_normal_900, expanded_normal_900 },
+                { expanded_italic_100, expanded_normal_900 },
+                { expanded_italic_900, expanded_normal_900 },
+            },
+        },
+
+        {
+            { expanded_italic_100,expanded_italic_900 },
+            {
+                { condensed_normal_100, expanded_italic_100 },
+                { condensed_normal_900, expanded_italic_900 },
+                { condensed_italic_100, expanded_italic_100 },
+                { condensed_italic_900, expanded_italic_900 },
+                { expanded_normal_100, expanded_italic_100 },
+                { expanded_normal_900, expanded_italic_900 },
+                { expanded_italic_100, expanded_italic_100 },
+                { expanded_italic_900, expanded_italic_900 },
+            },
+        },
+
+        {
+            { expanded_italic_100 },
+            {
+                { condensed_normal_100, expanded_italic_100 },
+                { condensed_normal_900, expanded_italic_100 },
+                { condensed_italic_100, expanded_italic_100 },
+                { condensed_italic_900, expanded_italic_100 },
+                { expanded_normal_100, expanded_italic_100 },
+                { expanded_normal_900, expanded_italic_100 },
+                { expanded_italic_100, expanded_italic_100 },
+                { expanded_italic_900, expanded_italic_100 },
+            },
+        },
+
+        {
+            { expanded_italic_900 },
+            {
+                { condensed_normal_100, expanded_italic_900 },
+                { condensed_normal_900, expanded_italic_900 },
+                { condensed_italic_100, expanded_italic_900 },
+                { condensed_italic_900, expanded_italic_900 },
+                { expanded_normal_100, expanded_italic_900 },
+                { expanded_normal_900, expanded_italic_900 },
+                { expanded_italic_100, expanded_italic_900 },
+                { expanded_italic_900, expanded_italic_900 },
+            },
+        },
+
+        {
+            { normal_normal_100, normal_normal_900 },
+            {
+                { normal_normal_300, normal_normal_100 },
+                { normal_normal_400, normal_normal_100 },
+                { normal_normal_500, normal_normal_100 },
+                { normal_normal_600, normal_normal_900 },
+            },
+        },
+
+        {
+            { normal_normal_100, normal_normal_400, normal_normal_900 },
+            {
+                { normal_normal_300, normal_normal_100 },
+                { normal_normal_400, normal_normal_400 },
+                { normal_normal_500, normal_normal_400 },
+                { normal_normal_600, normal_normal_900 },
+            },
+        },
+
+        {
+            { normal_normal_100, normal_normal_500, normal_normal_900 },
+            {
+                { normal_normal_300, normal_normal_100 },
+                { normal_normal_400, normal_normal_500 },
+                { normal_normal_500, normal_normal_500 },
+                { normal_normal_600, normal_normal_900 },
+            },
+        },
+
+        {
+            { },
+            {
+                { normal_normal_300, invalidFontStyle },
+                { normal_normal_400, invalidFontStyle },
+                { normal_normal_500, invalidFontStyle },
+                { normal_normal_600, invalidFontStyle },
+            },
+        },
+        {
+            { expanded_normal_100,expanded_normal_900,
+              expanded_italic_100,expanded_italic_900,
+              expanded_obliqu_100,expanded_obliqu_900, },
+            {
+                { condensed_normal_100, expanded_normal_100 },
+                { condensed_normal_900, expanded_normal_900 },
+                { condensed_italic_100, expanded_italic_100 },
+                { condensed_italic_900, expanded_italic_900 },
+                { condensed_obliqu_100, expanded_obliqu_100 },
+                { condensed_obliqu_900, expanded_obliqu_900 },
+                { expanded_normal_100, expanded_normal_100 },
+                { expanded_normal_900, expanded_normal_900 },
+                { expanded_italic_100, expanded_italic_100 },
+                { expanded_italic_900, expanded_italic_900 },
+                { expanded_obliqu_100, expanded_obliqu_100 },
+                { expanded_obliqu_900, expanded_obliqu_900 },
+            },
+        },
+        {
+            { expanded_normal_100,expanded_normal_900,
+              expanded_obliqu_100,expanded_obliqu_900, },
+            {
+                { condensed_normal_100, expanded_normal_100 },
+                { condensed_normal_900, expanded_normal_900 },
+                { condensed_italic_100, expanded_obliqu_100 },
+                { condensed_italic_900, expanded_obliqu_900 },
+                { condensed_obliqu_100, expanded_obliqu_100 },
+                { condensed_obliqu_900, expanded_obliqu_900 },
+                { expanded_normal_100, expanded_normal_100 },
+                { expanded_normal_900, expanded_normal_900 },
+                { expanded_italic_100, expanded_obliqu_100 },
+                { expanded_italic_900, expanded_obliqu_900 },
+                { expanded_obliqu_100, expanded_obliqu_100 },
+                { expanded_obliqu_900, expanded_obliqu_900 },
+            },
+        },
+        {
+            { expanded_italic_100,expanded_italic_900,
+              expanded_obliqu_100,expanded_obliqu_900, },
+            {
+                { condensed_normal_100, expanded_obliqu_100 },
+                { condensed_normal_900, expanded_obliqu_900 },
+                { condensed_italic_100, expanded_italic_100 },
+                { condensed_italic_900, expanded_italic_900 },
+                { condensed_obliqu_100, expanded_obliqu_100 },
+                { condensed_obliqu_900, expanded_obliqu_900 },
+                { expanded_normal_100, expanded_obliqu_100 },
+                { expanded_normal_900, expanded_obliqu_900 },
+                { expanded_italic_100, expanded_italic_100 },
+                { expanded_italic_900, expanded_italic_900 },
+                { expanded_obliqu_100, expanded_obliqu_100 },
+                { expanded_obliqu_900, expanded_obliqu_900 },
+            },
+        },
+    };
+
+    for (StyleSetTest& test : tests) {
+        for (const StyleSetTest::Case& testCase : test.cases) {
+            sk_sp<SkTypeface> typeface(test.styleSet.matchStyle(testCase.pattern));
+            if (typeface) {
+                REPORTER_ASSERT(reporter, typeface->fontStyle() == testCase.expectedResult);
+            } else {
+                REPORTER_ASSERT(reporter, invalidFontStyle == testCase.expectedResult);
             }
         }
     }
@@ -114,6 +716,8 @@
 DEFINE_bool(verboseFontMgr, false, "run verbose fontmgr tests.");
 
 DEF_TEST(FontMgr, reporter) {
+    test_match(reporter);
+    test_matchStyleCSS3(reporter);
     test_fontiter(reporter, FLAGS_verboseFontMgr);
     test_alias_names(reporter);
     test_font(reporter);
diff --git a/src/third_party/skia/tests/FontNamesTest.cpp b/src/third_party/skia/tests/FontNamesTest.cpp
index 5c314e4..e5dce32 100644
--- a/src/third_party/skia/tests/FontNamesTest.cpp
+++ b/src/third_party/skia/tests/FontNamesTest.cpp
@@ -142,16 +142,16 @@
 static void test_systemfonts(skiatest::Reporter* reporter, bool verbose) {
     static const SkFontTableTag nameTag = SkSetFourByteTag('n','a','m','e');
 
-    SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
+    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
     int count = SkMin32(fm->countFamilies(), MAX_FAMILIES);
     for (int i = 0; i < count; ++i) {
-        SkAutoTUnref<SkFontStyleSet> set(fm->createStyleSet(i));
+        sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
         for (int j = 0; j < set->count(); ++j) {
             SkString sname;
             SkFontStyle fs;
             set->getStyle(j, &fs, &sname);
 
-            SkAutoTUnref<SkTypeface> typeface(set->createTypeface(j));
+            sk_sp<SkTypeface> typeface(set->createTypeface(j));
 
             SkString familyName;
             typeface->getFamilyName(&familyName);
@@ -159,7 +159,7 @@
                 SkDebugf("[%s]\n", familyName.c_str());
             }
 
-            SkAutoTUnref<SkTypeface::LocalizedStrings> familyNamesIter(
+            sk_sp<SkTypeface::LocalizedStrings> familyNamesIter(
                 typeface->createFamilyNameIterator());
             SkTypeface::LocalizedString familyNameLocalized;
             while (familyNamesIter->next(&familyNameLocalized)) {
diff --git a/src/third_party/skia/tests/FontObjTest.cpp b/src/third_party/skia/tests/FontObjTest.cpp
index c2d8f5d..44f799e 100644
--- a/src/third_party/skia/tests/FontObjTest.cpp
+++ b/src/third_party/skia/tests/FontObjTest.cpp
@@ -23,7 +23,7 @@
 }
 
 static void test_cachedfont(skiatest::Reporter* reporter, const SkPaint& paint) {
-    SkAutoTUnref<SkFont> font(SkFont::Testing_CreateFromPaint(paint));
+    sk_sp<SkFont> font(SkFont::Testing_CreateFromPaint(paint));
 
     // Currently SkFont resolves null into the default, so only test if paint's is not null
     if (paint.getTypeface()) {
@@ -43,7 +43,7 @@
 
 static void test_cachedfont(skiatest::Reporter* reporter) {
     static const char* const faces[] = {
-        NULL,   // default font
+        nullptr,   // default font
         "Arial", "Times", "Times New Roman", "Helvetica", "Courier",
         "Courier New", "Verdana", "monospace",
     };
@@ -78,8 +78,7 @@
     char txt[] = "long.text.with.lots.of.dots.";
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(faces); i++) {
-        SkAutoTUnref<SkTypeface> face(SkTypeface::CreateFromName(faces[i], SkTypeface::kNormal));
-        paint.setTypeface(face);
+        paint.setTypeface(SkTypeface::MakeFromName(faces[i], SkFontStyle()));
 
         for (size_t j = 0; j  < SK_ARRAY_COUNT(settings); j++) {
             paint.setHinting(settings[j].hinting);
@@ -103,7 +102,7 @@
 
                 REPORTER_ASSERT(reporter, width1 == width2);
 
-                SkAutoTUnref<SkFont> font(SkFont::Testing_CreateFromPaint(paint));
+                sk_sp<SkFont> font(SkFont::Testing_CreateFromPaint(paint));
                 SkScalar font_width1 = font->measureText(txt, strlen(txt), kUTF8_SkTextEncoding);
                 // measureText not yet implemented...
                 REPORTER_ASSERT(reporter, font_width1 == -1);
diff --git a/src/third_party/skia/tests/FrontBufferedStreamTest.cpp b/src/third_party/skia/tests/FrontBufferedStreamTest.cpp
index cb11b12..69ff488 100644
--- a/src/third_party/skia/tests/FrontBufferedStreamTest.cpp
+++ b/src/third_party/skia/tests/FrontBufferedStreamTest.cpp
@@ -5,10 +5,12 @@
  * found in the LICENSE file.
  */
 
+#include "SkAutoMalloc.h"
+#include "SkBitmap.h"
+#include "SkCodec.h"
 #include "SkFrontBufferedStream.h"
 #include "SkRefCnt.h"
 #include "SkStream.h"
-#include "SkTypes.h"
 #include "Test.h"
 
 static void test_read(skiatest::Reporter* reporter, SkStream* bufferedStream,
@@ -49,72 +51,75 @@
 // Tests reading the stream across boundaries of what has been buffered so far and what
 // the total buffer size is.
 static void test_incremental_buffering(skiatest::Reporter* reporter, size_t bufferSize) {
-    SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
+    // NOTE: For this and other tests in this file, we cheat and continue to refer to the
+    // wrapped stream, but that's okay because we know the wrapping stream has not been
+    // deleted yet (and we only call const methods in it).
+    SkMemoryStream* memStream = new SkMemoryStream(gAbcs, strlen(gAbcs), false);
 
-    SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize));
-    test_hasLength(reporter, *bufferedStream.get(), memStream);
+    std::unique_ptr<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize));
+    test_hasLength(reporter, *bufferedStream, *memStream);
 
     // First, test reading less than the max buffer size.
-    test_read(reporter, bufferedStream, gAbcs, bufferSize / 2);
+    test_read(reporter, bufferedStream.get(), gAbcs, bufferSize / 2);
 
     // Now test rewinding back to the beginning and reading less than what was
     // already buffered.
-    test_rewind(reporter, bufferedStream, true);
-    test_read(reporter, bufferedStream, gAbcs, bufferSize / 4);
+    test_rewind(reporter, bufferedStream.get(), true);
+    test_read(reporter, bufferedStream.get(), gAbcs, bufferSize / 4);
 
     // Now test reading part of what was buffered, and buffering new data.
-    test_read(reporter, bufferedStream, gAbcs + bufferedStream->getPosition(), bufferSize / 2);
+    test_read(reporter, bufferedStream.get(), gAbcs + bufferSize / 4, bufferSize / 2);
 
     // Now test reading what was buffered, buffering new data, and
     // reading directly from the stream.
-    test_rewind(reporter, bufferedStream, true);
-    test_read(reporter, bufferedStream, gAbcs, bufferSize << 1);
+    test_rewind(reporter, bufferedStream.get(), true);
+    test_read(reporter, bufferedStream.get(), gAbcs, bufferSize << 1);
 
     // We have reached the end of the buffer, so rewinding will fail.
     // This test assumes that the stream is larger than the buffer; otherwise the
     // result of rewind should be true.
-    test_rewind(reporter, bufferedStream, false);
+    test_rewind(reporter, bufferedStream.get(), false);
 }
 
 static void test_perfectly_sized_buffer(skiatest::Reporter* reporter, size_t bufferSize) {
-    SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
-    SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize));
-    test_hasLength(reporter, *bufferedStream.get(), memStream);
+    SkMemoryStream* memStream = new SkMemoryStream(gAbcs, strlen(gAbcs), false);
+    std::unique_ptr<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize));
+    test_hasLength(reporter, *bufferedStream, *memStream);
 
     // Read exactly the amount that fits in the buffer.
-    test_read(reporter, bufferedStream, gAbcs, bufferSize);
+    test_read(reporter, bufferedStream.get(), gAbcs, bufferSize);
 
     // Rewinding should succeed.
-    test_rewind(reporter, bufferedStream, true);
+    test_rewind(reporter, bufferedStream.get(), true);
 
     // Once again reading buffered info should succeed
-    test_read(reporter, bufferedStream, gAbcs, bufferSize);
+    test_read(reporter, bufferedStream.get(), gAbcs, bufferSize);
 
     // Read past the size of the buffer. At this point, we cannot return.
-    test_read(reporter, bufferedStream, gAbcs + bufferedStream->getPosition(), 1);
-    test_rewind(reporter, bufferedStream, false);
+    test_read(reporter, bufferedStream.get(), gAbcs + memStream->getPosition(), 1);
+    test_rewind(reporter, bufferedStream.get(), false);
 }
 
 static void test_skipping(skiatest::Reporter* reporter, size_t bufferSize) {
-    SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
-    SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize));
-    test_hasLength(reporter, *bufferedStream.get(), memStream);
+    SkMemoryStream* memStream = new SkMemoryStream(gAbcs, strlen(gAbcs), false);
+    std::unique_ptr<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize));
+    test_hasLength(reporter, *bufferedStream, *memStream);
 
     // Skip half the buffer.
     bufferedStream->skip(bufferSize / 2);
 
     // Rewind, then read part of the buffer, which should have been read.
-    test_rewind(reporter, bufferedStream, true);
-    test_read(reporter, bufferedStream, gAbcs, bufferSize / 4);
+    test_rewind(reporter, bufferedStream.get(), true);
+    test_read(reporter, bufferedStream.get(), gAbcs, bufferSize / 4);
 
     // Now skip beyond the buffered piece, but still within the total buffer.
     bufferedStream->skip(bufferSize / 2);
 
     // Test that reading will still work.
-    test_read(reporter, bufferedStream, gAbcs + bufferedStream->getPosition(), bufferSize / 4);
+    test_read(reporter, bufferedStream.get(), gAbcs + memStream->getPosition(), bufferSize / 4);
 
-    test_rewind(reporter, bufferedStream, true);
-    test_read(reporter, bufferedStream, gAbcs, bufferSize);
+    test_rewind(reporter, bufferedStream.get(), true);
+    test_read(reporter, bufferedStream.get(), gAbcs, bufferSize);
 }
 
 // A custom class whose isAtEnd behaves the way Android's stream does - since it is an adaptor to a
@@ -126,7 +131,7 @@
         : INHERITED(data, size, ownMemory)
         , fIsAtEnd(false) {}
 
-    size_t read(void* dst, size_t requested) SK_OVERRIDE {
+    size_t read(void* dst, size_t requested) override {
         size_t bytesRead = this->INHERITED::read(dst, requested);
         if (bytesRead < requested) {
             fIsAtEnd = true;
@@ -134,7 +139,7 @@
         return bytesRead;
     }
 
-    bool isAtEnd() const SK_OVERRIDE {
+    bool isAtEnd() const override {
         return fIsAtEnd;
     }
 
@@ -147,18 +152,19 @@
 // does not invalidate the buffer.
 static void test_read_beyond_buffer(skiatest::Reporter* reporter, size_t bufferSize) {
     // Use a stream that behaves like Android's stream.
-    AndroidLikeMemoryStream memStream((void*)gAbcs, bufferSize, false);
+    AndroidLikeMemoryStream* memStream =
+            new AndroidLikeMemoryStream((void*)gAbcs, bufferSize, false);
 
     // Create a buffer that matches the length of the stream.
-    SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize));
-    test_hasLength(reporter, *bufferedStream.get(), memStream);
+    std::unique_ptr<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize));
+    test_hasLength(reporter, *bufferedStream.get(), *memStream);
 
     // Attempt to read one more than the bufferSize
     test_read(reporter, bufferedStream.get(), gAbcs, bufferSize + 1);
     test_rewind(reporter, bufferedStream.get(), true);
 
     // Ensure that the initial read did not invalidate the buffer.
-    test_read(reporter, bufferedStream, gAbcs, bufferSize);
+    test_read(reporter, bufferedStream.get(), gAbcs, bufferSize);
 }
 
 // Dummy stream that optionally has a length and/or position. Tests that FrontBufferedStream's
@@ -170,19 +176,19 @@
         , fHasPosition(hasPosition)
     {}
 
-    virtual bool hasLength() const SK_OVERRIDE {
+    bool hasLength() const override {
         return fHasLength;
     }
 
-    virtual bool hasPosition() const SK_OVERRIDE {
+    bool hasPosition() const override {
         return fHasPosition;
     }
 
-    virtual size_t read(void*, size_t) SK_OVERRIDE {
+    size_t read(void*, size_t) override {
         return 0;
     }
 
-    virtual bool isAtEnd() const SK_OVERRIDE {
+    bool isAtEnd() const override {
         return true;
     }
 
@@ -195,40 +201,40 @@
 static void test_length_combos(skiatest::Reporter* reporter, size_t bufferSize) {
     for (int hasLen = 0; hasLen <= 1; hasLen++) {
         for (int hasPos = 0; hasPos <= 1; hasPos++) {
-            LengthOptionalStream stream(SkToBool(hasLen), SkToBool(hasPos));
-            SkAutoTUnref<SkStream> buffered(SkFrontBufferedStream::Create(&stream, bufferSize));
-            test_hasLength(reporter, *buffered.get(), stream);
+            LengthOptionalStream* stream =
+                    new LengthOptionalStream(SkToBool(hasLen), SkToBool(hasPos));
+            std::unique_ptr<SkStream> buffered(SkFrontBufferedStream::Create(stream, bufferSize));
+            test_hasLength(reporter, *buffered.get(), *stream);
         }
     }
 }
 
 // Test using a stream with an initial offset.
 static void test_initial_offset(skiatest::Reporter* reporter, size_t bufferSize) {
-    SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
+    SkMemoryStream* memStream = new SkMemoryStream(gAbcs, strlen(gAbcs), false);
 
     // Skip a few characters into the memStream, so that bufferedStream represents an offset into
     // the stream it wraps.
     const size_t arbitraryOffset = 17;
-    memStream.skip(arbitraryOffset);
-    SkAutoTUnref<SkStream> bufferedStream(SkFrontBufferedStream::Create(&memStream, bufferSize));
+    memStream->skip(arbitraryOffset);
+    std::unique_ptr<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize));
 
-    // Since SkMemoryStream has a length and a position, bufferedStream must also.
+    // Since SkMemoryStream has a length, bufferedStream must also.
     REPORTER_ASSERT(reporter, bufferedStream->hasLength());
 
     const size_t amountToRead = 10;
     const size_t bufferedLength = bufferedStream->getLength();
-    size_t currentPosition = bufferedStream->getPosition();
-    REPORTER_ASSERT(reporter, 0 == currentPosition);
+    size_t currentPosition = 0;
 
     // Read the stream in chunks. After each read, the position must match currentPosition,
     // which sums the amount attempted to read, unless the end of the stream has been reached.
     // Importantly, the end should not have been reached until currentPosition == bufferedLength.
     while (currentPosition < bufferedLength) {
         REPORTER_ASSERT(reporter, !bufferedStream->isAtEnd());
-        test_read(reporter, bufferedStream, gAbcs + arbitraryOffset + currentPosition,
+        test_read(reporter, bufferedStream.get(), gAbcs + arbitraryOffset + currentPosition,
                   amountToRead);
         currentPosition = SkTMin(currentPosition + amountToRead, bufferedLength);
-        REPORTER_ASSERT(reporter, bufferedStream->getPosition() == currentPosition);
+        REPORTER_ASSERT(reporter, memStream->getPosition() - arbitraryOffset == currentPosition);
     }
     REPORTER_ASSERT(reporter, bufferedStream->isAtEnd());
     REPORTER_ASSERT(reporter, bufferedLength == currentPosition);
@@ -249,3 +255,35 @@
     test_buffers(reporter, 15);
     test_buffers(reporter, 64);
 }
+
+// Test that a FrontBufferedStream does not allow reading after the end of a stream.
+// This class is a dummy SkStream which reports that it is at the end on the first
+// read (simulating a failure). Then it tracks whether someone calls read() again.
+class FailingStream : public SkStream {
+public:
+    FailingStream()
+    : fAtEnd(false)
+    {}
+
+    size_t read(void* buffer, size_t size) override {
+        SkASSERT(!fAtEnd);
+        fAtEnd = true;
+        return 0;
+    }
+
+    bool isAtEnd() const override {
+        return fAtEnd;
+    }
+
+private:
+    bool fAtEnd;
+};
+
+DEF_TEST(ShortFrontBufferedStream, reporter) {
+    FailingStream* failingStream = new FailingStream;
+    std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Create(failingStream, 64));
+
+    // This will fail to create a codec.  However, what we really want to test is that we
+    // won't read past the end of the stream.
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
+}
diff --git a/src/third_party/skia/tests/GLInterfaceValidationTest.cpp b/src/third_party/skia/tests/GLInterfaceValidationTest.cpp
deleted file mode 100755
index 797ba72..0000000
--- a/src/third_party/skia/tests/GLInterfaceValidationTest.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Test.h"
-
-// This is a GPU-backend specific test
-#if SK_SUPPORT_GPU
-
-#include "GrContextFactory.h"
-
-DEF_GPUTEST(GLInterfaceValidation, reporter, factory) {
-    for (int i = 0; i <= GrContextFactory::kLastGLContextType; ++i) {
-        GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType)i;
-        // this forces the factory to make the context if it hasn't yet
-        factory->get(glCtxType);
-        SkGLContextHelper* glCtxHelper = factory->getGLContext(glCtxType);
-
-        // We're supposed to fail the NVPR context type when we the native context that does not
-        // support the NVPR extension.
-        if (GrContextFactory::kNVPR_GLContextType == glCtxType &&
-            factory->getGLContext(GrContextFactory::kNative_GLContextType) &&
-            !factory->getGLContext(GrContextFactory::kNative_GLContextType)->hasExtension("GL_NV_path_rendering")) {
-            REPORTER_ASSERT(reporter, NULL == glCtxHelper);
-            continue;
-        }
-
-        REPORTER_ASSERT(reporter, glCtxHelper);
-        if (glCtxHelper) {
-            const GrGLInterface* interface = glCtxHelper->gl();
-            REPORTER_ASSERT(reporter, interface->validate());
-        }
-    }
-}
-
-#endif
diff --git a/src/third_party/skia/tests/GLProgramsTest.cpp b/src/third_party/skia/tests/GLProgramsTest.cpp
index a16173b..f8cb6ef 100644
--- a/src/third_party/skia/tests/GLProgramsTest.cpp
+++ b/src/third_party/skia/tests/GLProgramsTest.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -12,384 +11,410 @@
 
 #if SK_SUPPORT_GPU && SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
 
-#include "GrBackendProcessorFactory.h"
+#include "GrAutoLocaleSetter.h"
 #include "GrContextFactory.h"
-#include "GrOptDrawState.h"
-#include "effects/GrConfigConversionEffect.h"
-#include "gl/GrGLPathRendering.h"
-#include "gl/GrGpuGL.h"
+#include "GrContextPriv.h"
+#include "GrDrawOpTest.h"
+#include "GrDrawingManager.h"
+#include "GrPipeline.h"
+#include "GrRenderTargetContextPriv.h"
+#include "GrTest.h"
+#include "GrXferProcessor.h"
 #include "SkChecksum.h"
 #include "SkRandom.h"
 #include "Test.h"
 
-static void get_stage_stats(const GrFragmentStage stage, bool* readsDst,
-                            bool* readsFragPosition, bool* requiresVertexShader) {
-    if (stage.getFragmentProcessor()->willReadDstColor()) {
-        *readsDst = true;
-    }
-    if (stage.getProcessor()->willReadFragmentPosition()) {
-        *readsFragPosition = true;
-    }
-}
+#include "ops/GrDrawOp.h"
 
-bool GrGLProgramDesc::setRandom(SkRandom* random,
-                                GrGpuGL* gpu,
-                                const GrRenderTarget* dstRenderTarget,
-                                const GrTexture* dstCopyTexture,
-                                const GrGeometryStage* geometryProcessor,
-                                const GrFragmentStage* stages[],
-                                int numColorStages,
-                                int numCoverageStages,
-                                int currAttribIndex,
-                                GrGpu::DrawType drawType) {
-    bool isPathRendering = GrGpu::IsPathRenderingDrawType(drawType);
-    bool useLocalCoords = !isPathRendering &&
-                          random->nextBool() &&
-                          currAttribIndex < GrDrawState::kMaxVertexAttribCnt;
+#include "effects/GrConfigConversionEffect.h"
+#include "effects/GrPorterDuffXferProcessor.h"
+#include "effects/GrXfermodeFragmentProcessor.h"
 
-    int numStages = numColorStages + numCoverageStages;
-    fKey.reset();
-
-    GR_STATIC_ASSERT(0 == kEffectKeyOffsetsAndLengthOffset % sizeof(uint32_t));
-
-    // Make room for everything up to and including the array of offsets to effect keys.
-    fKey.push_back_n(kEffectKeyOffsetsAndLengthOffset + 2 * sizeof(uint16_t) * (numStages +
-            (geometryProcessor ? 1 : 0)));
-
-    bool dstRead = false;
-    bool fragPos = false;
-    bool vertexShader = SkToBool(geometryProcessor);
-    int offset = 0;
-    if (geometryProcessor) {
-        const GrGeometryStage* stage = geometryProcessor;
-        uint16_t* offsetAndSize = reinterpret_cast<uint16_t*>(fKey.begin() +
-                                                              kEffectKeyOffsetsAndLengthOffset +
-                                                              offset * 2 * sizeof(uint16_t));
-        uint32_t effectKeyOffset = fKey.count();
-        if (effectKeyOffset > SK_MaxU16) {
-            fKey.reset();
-            return false;
-        }
-        GrProcessorKeyBuilder b(&fKey);
-        uint16_t effectKeySize;
-        if (!GetProcessorKey(*stage, gpu->glCaps(), useLocalCoords, &b, &effectKeySize)) {
-            fKey.reset();
-            return false;
-        }
-        vertexShader = true;
-        fragPos = stage->getProcessor()->willReadFragmentPosition();
-        offsetAndSize[0] = effectKeyOffset;
-        offsetAndSize[1] = effectKeySize;
-        offset++;
-    }
-
-    for (int s = 0; s < numStages; ++s, ++offset) {
-        const GrFragmentStage* stage = stages[s];
-        uint16_t* offsetAndSize = reinterpret_cast<uint16_t*>(fKey.begin() +
-                                                              kEffectKeyOffsetsAndLengthOffset +
-                                                              offset * 2 * sizeof(uint16_t));
-        uint32_t effectKeyOffset = fKey.count();
-        if (effectKeyOffset > SK_MaxU16) {
-            fKey.reset();
-            return false;
-        }
-        GrProcessorKeyBuilder b(&fKey);
-        uint16_t effectKeySize;
-        if (!GetProcessorKey(*stages[s], gpu->glCaps(), useLocalCoords, &b, &effectKeySize)) {
-            fKey.reset();
-            return false;
-        }
-        get_stage_stats(*stage, &dstRead, &fragPos, &vertexShader);
-        offsetAndSize[0] = effectKeyOffset;
-        offsetAndSize[1] = effectKeySize;
-    }
-
-    KeyHeader* header = this->header();
-    memset(header, 0, kHeaderSize);
-    header->fEmitsPointSize = random->nextBool();
-
-    header->fPositionAttributeIndex = 0;
-
-    // if the effects have used up all off the available attributes,
-    // don't try to use color or coverage attributes as input
-    do {
-        header->fColorInput = static_cast<GrGLProgramDesc::ColorInput>(
-                                     random->nextULessThan(kColorInputCnt));
-    } while ((GrDrawState::kMaxVertexAttribCnt <= currAttribIndex || isPathRendering) &&
-             kAttribute_ColorInput == header->fColorInput);
-    header->fColorAttributeIndex = (header->fColorInput == kAttribute_ColorInput) ?
-                                        currAttribIndex++ :
-                                        -1;
-
-    do {
-        header->fCoverageInput = static_cast<GrGLProgramDesc::ColorInput>(
-                                     random->nextULessThan(kColorInputCnt));
-    } while ((GrDrawState::kMaxVertexAttribCnt <= currAttribIndex || isPathRendering)  &&
-             kAttribute_ColorInput == header->fCoverageInput);
-    header->fCoverageAttributeIndex = (header->fCoverageInput == kAttribute_ColorInput) ?
-                                        currAttribIndex++ :
-                                        -1;
-    bool useGS = random->nextBool();
-#if GR_GL_EXPERIMENTAL_GS
-    header->fExperimentalGS = gpu->caps()->geometryShaderSupport() && useGS;
-#else
-    (void) useGS;
-#endif
-
-    header->fLocalCoordAttributeIndex = useLocalCoords ? currAttribIndex++ : -1;
-
-    header->fColorEffectCnt = numColorStages;
-    header->fCoverageEffectCnt = numCoverageStages;
-
-    if (dstRead) {
-        header->fDstReadKey = SkToU8(GrGLFragmentShaderBuilder::KeyForDstRead(dstCopyTexture,
-                                                                      gpu->glCaps()));
-    } else {
-        header->fDstReadKey = 0;
-    }
-    if (fragPos) {
-        header->fFragPosKey = SkToU8(GrGLFragmentShaderBuilder::KeyForFragmentPosition(dstRenderTarget,
-                                                                               gpu->glCaps()));
-    } else {
-        header->fFragPosKey = 0;
-    }
-
-    header->fUseFragShaderOnly = isPathRendering && gpu->glPathRendering()->texturingMode() ==
-                                                    GrGLPathRendering::FixedFunction_TexturingMode;
-    header->fHasGeometryProcessor = vertexShader;
-
-    GrOptDrawState::PrimaryOutputType primaryOutput;
-    GrOptDrawState::SecondaryOutputType secondaryOutput;
-    if (!dstRead) {
-        primaryOutput = GrOptDrawState::kModulate_PrimaryOutputType;
-    } else {
-        primaryOutput = static_cast<GrOptDrawState::PrimaryOutputType>(
-            random->nextULessThan(GrOptDrawState::kPrimaryOutputTypeCnt));
-    }
-
-    if (GrOptDrawState::kCombineWithDst_PrimaryOutputType == primaryOutput ||
-        !gpu->caps()->dualSourceBlendingSupport()) {
-        secondaryOutput = GrOptDrawState::kNone_SecondaryOutputType;
-    } else {
-        secondaryOutput = static_cast<GrOptDrawState::SecondaryOutputType>(
-            random->nextULessThan(GrOptDrawState::kSecondaryOutputTypeCnt));
-    }
-
-    header->fPrimaryOutputType = primaryOutput;
-    header->fSecondaryOutputType = secondaryOutput;
-
-    this->finalize();
-    return true;
-}
-
-// TODO clean this up, we have to do this to test geometry processors but there has got to be
-// a better way.  In the mean time, we actually fill out these generic vertex attribs below with
-// the correct vertex attribs from the GP.  We have to ensure, however, we don't try to add more
-// than two attributes.
-GrVertexAttrib genericVertexAttribs[] = {
-    { kVec2f_GrVertexAttribType, 0,   kPosition_GrVertexAttribBinding },
-    { kVec2f_GrVertexAttribType, 0,   kGeometryProcessor_GrVertexAttribBinding },
-    { kVec2f_GrVertexAttribType, 0,   kGeometryProcessor_GrVertexAttribBinding }
-};
+#include "gl/GrGLGpu.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLProgramBuilder.h"
 
 /*
- * convert sl type to vertexattrib type, not a complete implementation, only use for debugging
+ * A dummy processor which just tries to insert a massive key and verify that it can retrieve the
+ * whole thing correctly
  */
-GrVertexAttribType convert_sltype_to_attribtype(GrSLType type) {
-    switch (type) {
-        case kFloat_GrSLType:
-            return kFloat_GrVertexAttribType;
-        case kVec2f_GrSLType:
-            return kVec2f_GrVertexAttribType;
-        case kVec3f_GrSLType:
-            return kVec3f_GrVertexAttribType;
-        case kVec4f_GrSLType:
-            return kVec4f_GrVertexAttribType;
-        default:
-            SkFAIL("Type isn't convertible");
-            return kFloat_GrVertexAttribType;
+static const uint32_t kMaxKeySize = 1024;
+
+class GLBigKeyProcessor : public GrGLSLFragmentProcessor {
+public:
+    void emitCode(EmitArgs& args) override {
+        // pass through
+        GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
+        if (args.fInputColor) {
+            fragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, args.fInputColor);
+        } else {
+            fragBuilder->codeAppendf("%s = vec4(1.0);\n", args.fOutputColor);
+        }
+    }
+
+    static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
+        for (uint32_t i = 0; i < kMaxKeySize; i++) {
+            b->add32(i);
+        }
+    }
+
+private:
+    typedef GrGLSLFragmentProcessor INHERITED;
+};
+
+class BigKeyProcessor : public GrFragmentProcessor {
+public:
+    static sk_sp<GrFragmentProcessor> Make() {
+        return sk_sp<GrFragmentProcessor>(new BigKeyProcessor);
+    }
+
+    const char* name() const override { return "Big Ole Key"; }
+
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
+        return new GLBigKeyProcessor;
+    }
+
+private:
+    BigKeyProcessor() : INHERITED(kNone_OptimizationFlags) { this->initClassID<BigKeyProcessor>(); }
+    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                       GrProcessorKeyBuilder* b) const override {
+        GLBigKeyProcessor::GenKey(*this, caps, b);
+    }
+    bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+
+    typedef GrFragmentProcessor INHERITED;
+};
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(BigKeyProcessor);
+
+#if GR_TEST_UTILS
+sk_sp<GrFragmentProcessor> BigKeyProcessor::TestCreate(GrProcessorTestData*) {
+    return BigKeyProcessor::Make();
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+
+class BlockInputFragmentProcessor : public GrFragmentProcessor {
+public:
+    static sk_sp<GrFragmentProcessor> Make(sk_sp<GrFragmentProcessor> fp) {
+        return sk_sp<GrFragmentProcessor>(new BlockInputFragmentProcessor(fp));
+    }
+
+    const char* name() const override { return "Block Input"; }
+
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLFP; }
+
+private:
+    class GLFP : public GrGLSLFragmentProcessor {
+    public:
+        void emitCode(EmitArgs& args) override {
+            this->emitChild(0, args);
+        }
+
+    private:
+        typedef GrGLSLFragmentProcessor INHERITED;
+    };
+
+    BlockInputFragmentProcessor(sk_sp<GrFragmentProcessor> child)
+            : INHERITED(kNone_OptimizationFlags) {
+        this->initClassID<BlockInputFragmentProcessor>();
+        this->registerChildProcessor(std::move(child));
+    }
+
+    void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {}
+
+    bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
+
+    typedef GrFragmentProcessor INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Begin test code
+ */
+static const int kRenderTargetHeight = 1;
+static const int kRenderTargetWidth = 1;
+
+static sk_sp<GrRenderTargetContext> random_render_target_context(GrContext* context,
+                                                                 SkRandom* random,
+                                                                 const GrCaps* caps) {
+    GrSurfaceOrigin origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin
+                                                : kBottomLeft_GrSurfaceOrigin;
+    int sampleCnt = random->nextBool() ? caps->getSampleCount(4, kRGBA_8888_GrPixelConfig) : 0;
+
+    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext(
+                                                                           SkBackingFit::kExact,
+                                                                           kRenderTargetWidth,
+                                                                           kRenderTargetHeight,
+                                                                           kRGBA_8888_GrPixelConfig,
+                                                                           nullptr,
+                                                                           sampleCnt,
+                                                                           origin));
+    return renderTargetContext;
+}
+
+#if GR_TEST_UTILS
+static void set_random_xpf(GrPaint* paint, GrProcessorTestData* d) {
+    paint->setXPFactory(GrXPFactoryTestFactory::Get(d));
+}
+
+static sk_sp<GrFragmentProcessor> create_random_proc_tree(GrProcessorTestData* d,
+                                                          int minLevels, int maxLevels) {
+    SkASSERT(1 <= minLevels);
+    SkASSERT(minLevels <= maxLevels);
+
+    // Return a leaf node if maxLevels is 1 or if we randomly chose to terminate.
+    // If returning a leaf node, make sure that it doesn't have children (e.g. another
+    // GrComposeEffect)
+    const float terminateProbability = 0.3f;
+    if (1 == minLevels) {
+        bool terminate = (1 == maxLevels) || (d->fRandom->nextF() < terminateProbability);
+        if (terminate) {
+            sk_sp<GrFragmentProcessor> fp;
+            while (true) {
+                fp = GrProcessorTestFactory<GrFragmentProcessor>::Make(d);
+                SkASSERT(fp);
+                if (0 == fp->numChildProcessors()) {
+                    break;
+                }
+            }
+            return fp;
+        }
+    }
+    // If we didn't terminate, choose either the left or right subtree to fulfill
+    // the minLevels requirement of this tree; the other child can have as few levels as it wants.
+    // Also choose a random xfer mode.
+    if (minLevels > 1) {
+        --minLevels;
+    }
+    sk_sp<GrFragmentProcessor> minLevelsChild(create_random_proc_tree(d, minLevels, maxLevels - 1));
+    sk_sp<GrFragmentProcessor> otherChild(create_random_proc_tree(d, 1, maxLevels - 1));
+    SkBlendMode mode = static_cast<SkBlendMode>(d->fRandom->nextRangeU(0,
+                                                               (int)SkBlendMode::kLastMode));
+    sk_sp<GrFragmentProcessor> fp;
+    if (d->fRandom->nextF() < 0.5f) {
+        fp = GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(minLevelsChild),
+                                                                std::move(otherChild), mode);
+        SkASSERT(fp);
+    } else {
+        fp = GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(otherChild),
+                                                                std::move(minLevelsChild), mode);
+        SkASSERT(fp);
+    }
+    return fp;
+}
+
+static void set_random_color_coverage_stages(GrPaint* paint,
+                                             GrProcessorTestData* d,
+                                             int maxStages,
+                                             int maxTreeLevels) {
+    // Randomly choose to either create a linear pipeline of procs or create one proc tree
+    const float procTreeProbability = 0.5f;
+    if (d->fRandom->nextF() < procTreeProbability) {
+        sk_sp<GrFragmentProcessor> fp(create_random_proc_tree(d, 2, maxTreeLevels));
+        if (fp) {
+            paint->addColorFragmentProcessor(std::move(fp));
+        }
+    } else {
+        int numProcs = d->fRandom->nextULessThan(maxStages + 1);
+        int numColorProcs = d->fRandom->nextULessThan(numProcs + 1);
+
+        for (int s = 0; s < numProcs;) {
+            sk_sp<GrFragmentProcessor> fp(GrProcessorTestFactory<GrFragmentProcessor>::Make(d));
+            SkASSERT(fp);
+
+            // finally add the stage to the correct pipeline in the drawstate
+            if (s < numColorProcs) {
+                paint->addColorFragmentProcessor(std::move(fp));
+            } else {
+                paint->addCoverageFragmentProcessor(std::move(fp));
+            }
+            ++s;
+        }
     }
 }
-// TODO end test hack
 
+static void set_random_state(GrPaint* paint, SkRandom* random) {
+    if (random->nextBool()) {
+        paint->setDisableOutputConversionToSRGB(true);
+    }
+    if (random->nextBool()) {
+        paint->setAllowSRGBInputs(true);
+    }
+}
 
-bool GrGpuGL::programUnitTest(int maxStages) {
+#endif
 
-    GrTextureDesc dummyDesc;
-    dummyDesc.fFlags = kRenderTarget_GrTextureFlagBit;
-    dummyDesc.fConfig = kSkia8888_GrPixelConfig;
+#if !GR_TEST_UTILS
+bool GrDrawingManager::ProgramUnitTest(GrContext*, int) { return true; }
+#else
+bool GrDrawingManager::ProgramUnitTest(GrContext* context, int maxStages, int maxLevels) {
+    GrDrawingManager* drawingManager = context->contextPriv().drawingManager();
+
+    sk_sp<GrTextureProxy> proxies[2];
+
+    // setup dummy textures
+    GrSurfaceDesc dummyDesc;
+    dummyDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    dummyDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+    dummyDesc.fConfig = kRGBA_8888_GrPixelConfig;
     dummyDesc.fWidth = 34;
     dummyDesc.fHeight = 18;
-    SkAutoTUnref<GrTexture> dummyTexture1(this->createTexture(dummyDesc, NULL, 0));
-    dummyDesc.fFlags = kNone_GrTextureFlags;
+    proxies[0] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                              dummyDesc, SkBudgeted::kNo, nullptr, 0);
+    dummyDesc.fFlags = kNone_GrSurfaceFlags;
+    dummyDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
     dummyDesc.fConfig = kAlpha_8_GrPixelConfig;
     dummyDesc.fWidth = 16;
     dummyDesc.fHeight = 22;
-    SkAutoTUnref<GrTexture> dummyTexture2(this->createTexture(dummyDesc, NULL, 0));
+    proxies[1] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                              dummyDesc, SkBudgeted::kNo, nullptr, 0);
 
-    if (!dummyTexture1 || ! dummyTexture2) {
+    if (!proxies[0] || !proxies[1]) {
+        SkDebugf("Could not allocate dummy textures");
         return false;
     }
 
-    static const int NUM_TESTS = 512;
+    // dummy scissor state
+    GrScissorState scissor;
 
     SkRandom random;
-    for (int t = 0; t < NUM_TESTS; ++t) {
-
-#if 0
-        GrPrintf("\nTest Program %d\n-------------\n", t);
-        static const int stop = -1;
-        if (t == stop) {
-            int breakpointhere = 9;
-        }
-#endif
-
-        GrGLProgramDesc pdesc;
-
-        int currAttribIndex = 1;  // we need to always leave room for position
-        int currTextureCoordSet = 0;
-        GrTexture* dummyTextures[] = {dummyTexture1.get(), dummyTexture2.get()};
-
-        int numStages = random.nextULessThan(maxStages + 1);
-        int numColorStages = random.nextULessThan(numStages + 1);
-        int numCoverageStages = numStages - numColorStages;
-
-        SkAutoSTMalloc<8, const GrFragmentStage*> stages(numStages);
-
-        bool usePathRendering = this->glCaps().pathRenderingSupport() && random.nextBool();
-
-        GrGpu::DrawType drawType = usePathRendering ? GrGpu::kDrawPath_DrawType :
-                                                      GrGpu::kDrawPoints_DrawType;
-
-        SkAutoTDelete<GrGeometryStage> geometryProcessor;
-        bool hasGeometryProcessor = usePathRendering ? false : random.nextBool();
-        if (hasGeometryProcessor) {
-            while (true) {
-                SkAutoTUnref<const GrGeometryProcessor> effect(
-                        GrProcessorTestFactory<GrGeometryProcessor>::CreateStage(&random, this->getContext(), *this->caps(),
-                                                         dummyTextures));
-                SkASSERT(effect);
-                // Only geometryProcessor can use vertex shader
-                GrGeometryStage* stage = SkNEW_ARGS(GrGeometryStage, (effect.get()));
-                geometryProcessor.reset(stage);
-
-                // we have to set dummy vertex attribs
-                const GrGeometryProcessor::VertexAttribArray& v = effect->getVertexAttribs();
-                int numVertexAttribs = v.count();
-
-                SkASSERT(GrGeometryProcessor::kMaxVertexAttribs == 2 &&
-                         GrGeometryProcessor::kMaxVertexAttribs >= numVertexAttribs);
-                size_t runningStride = GrVertexAttribTypeSize(genericVertexAttribs[0].fType);
-                for (int i = 0; i < numVertexAttribs; i++) {
-                    genericVertexAttribs[i + 1].fOffset = runningStride;
-                    genericVertexAttribs[i + 1].fType =
-                            convert_sltype_to_attribtype(v[i].getType());
-                    runningStride += GrVertexAttribTypeSize(genericVertexAttribs[i + 1].fType);
-                }
-
-                // update the vertex attributes with the ds
-                GrDrawState* ds = this->drawState();
-                ds->setVertexAttribs<genericVertexAttribs>(numVertexAttribs + 1, runningStride);
-                currAttribIndex = numVertexAttribs + 1;
-                break;
-            }
-        }
-        for (int s = 0; s < numStages;) {
-            SkAutoTUnref<const GrFragmentProcessor> effect(
-                    GrProcessorTestFactory<GrFragmentProcessor>::CreateStage(
-                                                                            &random,
-                                                                            this->getContext(),
-                                                                            *this->caps(),
-                                                                            dummyTextures));
-            SkASSERT(effect);
-
-            // If adding this effect would exceed the max texture coord set count then generate a
-            // new random effect.
-            if (usePathRendering && this->glPathRendering()->texturingMode() ==
-                                    GrGLPathRendering::FixedFunction_TexturingMode) {;
-                int numTransforms = effect->numTransforms();
-                if (currTextureCoordSet + numTransforms > this->glCaps().maxFixedFunctionTextureCoords()) {
-                    continue;
-                }
-                currTextureCoordSet += numTransforms;
-            }
-            GrFragmentStage* stage = SkNEW_ARGS(GrFragmentStage, (effect.get()));
-
-            stages[s] = stage;
-            ++s;
-        }
-        const GrTexture* dstTexture = random.nextBool() ? dummyTextures[0] : dummyTextures[1];
-        if (!pdesc.setRandom(&random,
-                             this,
-                             dummyTextures[0]->asRenderTarget(),
-                             dstTexture,
-                             geometryProcessor.get(),
-                             stages.get(),
-                             numColorStages,
-                             numCoverageStages,
-                             currAttribIndex,
-                             drawType)) {
+    static const int NUM_TESTS = 1024;
+    for (int t = 0; t < NUM_TESTS; t++) {
+        // setup random render target(can fail)
+        sk_sp<GrRenderTargetContext> renderTargetContext(random_render_target_context(
+            context, &random, context->caps()));
+        if (!renderTargetContext) {
+            SkDebugf("Could not allocate renderTargetContext");
             return false;
         }
 
-        SkAutoTUnref<GrGLProgram> program(GrGLProgram::Create(this,
-                                                              pdesc,
-                                                              geometryProcessor.get(),
-                                                              stages,
-                                                              stages + numColorStages));
-        for (int s = 0; s < numStages; ++s) {
-            SkDELETE(stages[s]);
-        }
-        if (NULL == program.get()) {
-            return false;
-        }
-
-        // We have to reset the drawstate because we might have added a gp
-        this->drawState()->reset();
+        GrPaint paint;
+        GrProcessorTestData ptd(&random, context, renderTargetContext.get(), proxies);
+        set_random_color_coverage_stages(&paint, &ptd, maxStages, maxLevels);
+        set_random_xpf(&paint, &ptd);
+        set_random_state(&paint, &random);
+        GrDrawRandomOp(&random, renderTargetContext.get(), std::move(paint));
     }
+    // Flush everything, test passes if flush is successful(ie, no asserts are hit, no crashes)
+    drawingManager->flush(nullptr);
+
+    // Validate that GrFPs work correctly without an input.
+    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext(
+                                                                           SkBackingFit::kExact,
+                                                                           kRenderTargetWidth,
+                                                                           kRenderTargetHeight,
+                                                                           kRGBA_8888_GrPixelConfig,
+                                                                           nullptr));
+    if (!renderTargetContext) {
+        SkDebugf("Could not allocate a renderTargetContext");
+        return false;
+    }
+
+    int fpFactoryCnt = GrProcessorTestFactory<GrFragmentProcessor>::Count();
+    for (int i = 0; i < fpFactoryCnt; ++i) {
+        // Since FP factories internally randomize, call each 10 times.
+        for (int j = 0; j < 10; ++j) {
+            GrProcessorTestData ptd(&random, context, renderTargetContext.get(), proxies);
+
+            GrPaint paint;
+            paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
+            sk_sp<GrFragmentProcessor> fp(
+                GrProcessorTestFactory<GrFragmentProcessor>::MakeIdx(i, &ptd));
+            sk_sp<GrFragmentProcessor> blockFP(
+                BlockInputFragmentProcessor::Make(std::move(fp)));
+            paint.addColorFragmentProcessor(std::move(blockFP));
+            GrDrawRandomOp(&random, renderTargetContext.get(), std::move(paint));
+            drawingManager->flush(nullptr);
+        }
+    }
+
     return true;
 }
-
-DEF_GPUTEST(GLPrograms, reporter, factory) {
-    for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
-        GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(type));
-        if (context) {
-            GrGpuGL* gpu = static_cast<GrGpuGL*>(context->getGpu());
-            int maxStages = 6;
-#if SK_ANGLE
-            // Some long shaders run out of temporary registers in the D3D compiler on ANGLE.
-            if (type == GrContextFactory::kANGLE_GLContextType) {
-                maxStages = 3;
-            }
 #endif
-            REPORTER_ASSERT(reporter, gpu->programUnitTest(maxStages));
+
+static int get_glprograms_max_stages(const sk_gpu_test::ContextInfo& ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    GrGLGpu* gpu = static_cast<GrGLGpu*>(context->getGpu());
+    int maxStages = 6;
+    if (kGLES_GrGLStandard == gpu->glStandard()) {
+    // We've had issues with driver crashes and HW limits being exceeded with many effects on
+    // Android devices. We have passes on ARM devices with the default number of stages.
+    // TODO When we run ES 3.00 GLSL in more places, test again
+#ifdef SK_BUILD_FOR_ANDROID
+        if (kARM_GrGLVendor != gpu->ctxInfo().vendor()) {
+            maxStages = 1;
         }
+#endif
+    // On iOS we can exceed the maximum number of varyings. http://skbug.com/6627.
+#ifdef SK_BUILD_FOR_IOS
+        maxStages = 3;
+#endif
     }
+    if (ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D9_ES2_ContextType ||
+        ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D11_ES2_ContextType) {
+        // On Angle D3D we will hit a limit of out variables if we use too many stages.
+        maxStages = 3;
+    }
+    return maxStages;
 }
 
-// This is evil evil evil. The linker may throw away whole translation units as dead code if it
-// thinks none of the functions are called. It will do this even if there are static initializers
-// in the unit that could pass pointers to functions from the unit out to other translation units!
-// We force some of the effects that would otherwise be discarded to link here.
+static int get_glprograms_max_levels(const sk_gpu_test::ContextInfo& ctxInfo) {
+    // A full tree with 5 levels (31 nodes) may cause a program that exceeds shader limits
+    // (e.g. uniform or varying limits); maxTreeLevels should be a number from 1 to 4 inclusive.
+    int maxTreeLevels = 4;
+    // On iOS we can exceed the maximum number of varyings. http://skbug.com/6627.
+#ifdef SK_BUILD_FOR_IOS
+    maxTreeLevels = 2;
+#endif
+    if (ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D9_ES2_ContextType ||
+        ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D11_ES2_ContextType) {
+        // On Angle D3D we will hit a limit of out variables if we use too many stages.
+        maxTreeLevels = 2;
+    }
+    return maxTreeLevels;
+}
 
-#include "SkAlphaThresholdFilter.h"
-#include "SkColorMatrixFilter.h"
-#include "SkLightingImageFilter.h"
-#include "SkMagnifierImageFilter.h"
+static void test_glprograms(skiatest::Reporter* reporter, const sk_gpu_test::ContextInfo& ctxInfo) {
+    int maxStages = get_glprograms_max_stages(ctxInfo);
+    if (maxStages == 0) {
+        return;
+    }
+    int maxLevels = get_glprograms_max_levels(ctxInfo);
+    if (maxLevels == 0) {
+        return;
+    }
 
-void forceLinking();
+    // Disable this test on ANGLE D3D9 configurations. We keep hitting a D3D compiler bug.
+    // See skbug.com/6842 and anglebug.com/2098
+    if (sk_gpu_test::GrContextFactory::kANGLE_D3D9_ES2_ContextType == ctxInfo.type()) {
+        return;
+    }
 
-void forceLinking() {
-    SkLightingImageFilter::CreateDistantLitDiffuse(SkPoint3(0,0,0), 0, 0, 0);
-    SkAlphaThresholdFilter::Create(SkRegion(), .5f, .5f);
-    SkAutoTUnref<SkImageFilter> mag(SkMagnifierImageFilter::Create(
-        SkRect::MakeWH(SK_Scalar1, SK_Scalar1), SK_Scalar1));
-    GrConfigConversionEffect::Create(NULL,
-                                     false,
-                                     GrConfigConversionEffect::kNone_PMConversion,
-                                     SkMatrix::I());
-    SkScalar matrix[20];
-    SkAutoTUnref<SkColorMatrixFilter> cmf(SkColorMatrixFilter::Create(matrix));
+    REPORTER_ASSERT(reporter, GrDrawingManager::ProgramUnitTest(ctxInfo.grContext(), maxStages,
+                                                                maxLevels));
+}
+
+DEF_GPUTEST(GLPrograms, reporter, /*factory*/) {
+    // Set a locale that would cause shader compilation to fail because of , as decimal separator.
+    // skbug 3330
+#ifdef SK_BUILD_FOR_WIN
+    GrAutoLocaleSetter als("sv-SE");
+#else
+    GrAutoLocaleSetter als("sv_SE.UTF-8");
+#endif
+
+    // We suppress prints to avoid spew
+    GrContextOptions opts;
+    opts.fSuppressPrints = true;
+    sk_gpu_test::GrContextFactory debugFactory(opts);
+    skiatest::RunWithGPUTestContexts(test_glprograms, &skiatest::IsRenderingGLContextType, reporter,
+                                     &debugFactory);
 }
 
 #endif
diff --git a/src/third_party/skia/tests/GeometryTest.cpp b/src/third_party/skia/tests/GeometryTest.cpp
index 5151b70..4d955b5 100644
--- a/src/third_party/skia/tests/GeometryTest.cpp
+++ b/src/third_party/skia/tests/GeometryTest.cpp
@@ -7,6 +7,8 @@
 
 #include "SkGeometry.h"
 #include "Test.h"
+#include "SkRandom.h"
+#include <array>
 
 static bool nearly_equal(const SkPoint& a, const SkPoint& b) {
     return SkScalarNearlyEqual(a.fX, b.fX) && SkScalarNearlyEqual(a.fY, b.fY);
@@ -33,6 +35,195 @@
     }
 }
 
+static void check_pairs(skiatest::Reporter* reporter, int index, SkScalar t, const char name[],
+                        SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1) {
+    bool eq = SkScalarNearlyEqual(x0, x1) && SkScalarNearlyEqual(y0, y1);
+    if (!eq) {
+        SkDebugf("%s [%d %g] p0 [%10.8f %10.8f] p1 [%10.8f %10.8f]\n",
+                 name, index, t, x0, y0, x1, y1);
+        REPORTER_ASSERT(reporter, eq);
+    }
+}
+
+static void test_evalquadat(skiatest::Reporter* reporter) {
+    SkRandom rand;
+    for (int i = 0; i < 1000; ++i) {
+        SkPoint pts[3];
+        for (int j = 0; j < 3; ++j) {
+            pts[j].set(rand.nextSScalar1() * 100, rand.nextSScalar1() * 100);
+        }
+        const SkScalar dt = SK_Scalar1 / 128;
+        SkScalar t = dt;
+        for (int j = 1; j < 128; ++j) {
+            SkPoint r0;
+            SkEvalQuadAt(pts, t, &r0);
+            SkPoint r1 = SkEvalQuadAt(pts, t);
+            check_pairs(reporter, i, t, "quad-pos", r0.fX, r0.fY, r1.fX, r1.fY);
+
+            SkVector v0;
+            SkEvalQuadAt(pts, t, nullptr, &v0);
+            SkVector v1 = SkEvalQuadTangentAt(pts, t);
+            check_pairs(reporter, i, t, "quad-tan", v0.fX, v0.fY, v1.fX, v1.fY);
+
+            t += dt;
+        }
+    }
+}
+
+static void test_conic_eval_pos(skiatest::Reporter* reporter, const SkConic& conic, SkScalar t) {
+    SkPoint p0, p1;
+    conic.evalAt(t, &p0, nullptr);
+    p1 = conic.evalAt(t);
+    check_pairs(reporter, 0, t, "conic-pos", p0.fX, p0.fY, p1.fX, p1.fY);
+}
+
+static void test_conic_eval_tan(skiatest::Reporter* reporter, const SkConic& conic, SkScalar t) {
+    SkVector v0, v1;
+    conic.evalAt(t, nullptr, &v0);
+    v1 = conic.evalTangentAt(t);
+    check_pairs(reporter, 0, t, "conic-tan", v0.fX, v0.fY, v1.fX, v1.fY);
+}
+
+static void test_conic(skiatest::Reporter* reporter) {
+    SkRandom rand;
+    for (int i = 0; i < 1000; ++i) {
+        SkPoint pts[3];
+        for (int j = 0; j < 3; ++j) {
+            pts[j].set(rand.nextSScalar1() * 100, rand.nextSScalar1() * 100);
+        }
+        for (int k = 0; k < 10; ++k) {
+            SkScalar w = rand.nextUScalar1() * 2;
+            SkConic conic(pts, w);
+
+            const SkScalar dt = SK_Scalar1 / 128;
+            SkScalar t = dt;
+            for (int j = 1; j < 128; ++j) {
+                test_conic_eval_pos(reporter, conic, t);
+                test_conic_eval_tan(reporter, conic, t);
+                t += dt;
+            }
+        }
+    }
+}
+
+static void test_quad_tangents(skiatest::Reporter* reporter) {
+    SkPoint pts[] = {
+        {10, 20}, {10, 20}, {20, 30},
+        {10, 20}, {15, 25}, {20, 30},
+        {10, 20}, {20, 30}, {20, 30},
+    };
+    int count = (int) SK_ARRAY_COUNT(pts) / 3;
+    for (int index = 0; index < count; ++index) {
+        SkConic conic(&pts[index * 3], 0.707f);
+        SkVector start = SkEvalQuadTangentAt(&pts[index * 3], 0);
+        SkVector mid = SkEvalQuadTangentAt(&pts[index * 3], .5f);
+        SkVector end = SkEvalQuadTangentAt(&pts[index * 3], 1);
+        REPORTER_ASSERT(reporter, start.fX && start.fY);
+        REPORTER_ASSERT(reporter, mid.fX && mid.fY);
+        REPORTER_ASSERT(reporter, end.fX && end.fY);
+        REPORTER_ASSERT(reporter, SkScalarNearlyZero(start.cross(mid)));
+        REPORTER_ASSERT(reporter, SkScalarNearlyZero(mid.cross(end)));
+    }
+}
+
+static void test_conic_tangents(skiatest::Reporter* reporter) {
+    SkPoint pts[] = {
+        { 10, 20}, {10, 20}, {20, 30},
+        { 10, 20}, {15, 25}, {20, 30},
+        { 10, 20}, {20, 30}, {20, 30}
+    };
+    int count = (int) SK_ARRAY_COUNT(pts) / 3;
+    for (int index = 0; index < count; ++index) {
+        SkConic conic(&pts[index * 3], 0.707f);
+        SkVector start = conic.evalTangentAt(0);
+        SkVector mid = conic.evalTangentAt(.5f);
+        SkVector end = conic.evalTangentAt(1);
+        REPORTER_ASSERT(reporter, start.fX && start.fY);
+        REPORTER_ASSERT(reporter, mid.fX && mid.fY);
+        REPORTER_ASSERT(reporter, end.fX && end.fY);
+        REPORTER_ASSERT(reporter, SkScalarNearlyZero(start.cross(mid)));
+        REPORTER_ASSERT(reporter, SkScalarNearlyZero(mid.cross(end)));
+    }
+}
+
+static void test_this_conic_to_quad(skiatest::Reporter* r, const SkPoint pts[3], SkScalar w) {
+    SkAutoConicToQuads quadder;
+    const SkPoint* qpts = quadder.computeQuads(pts, w, 0.25);
+    const int qcount = quadder.countQuads();
+    const int pcount = qcount * 2 + 1;
+
+    REPORTER_ASSERT(r, SkPointsAreFinite(qpts, pcount));
+}
+
+/**
+ *  We need to ensure that when a conic is approximated by quads, that we always return finite
+ *  values in the quads.
+ *
+ *  Inspired by crbug_627414
+ */
+static void test_conic_to_quads(skiatest::Reporter* reporter) {
+    const SkPoint triples[] = {
+        { 0, 0 }, { 1, 0 }, { 1, 1 },
+        { 0, 0 }, { 3.58732e-43f, 2.72084f }, { 3.00392f, 3.00392f },
+        { 0, 0 }, { 100000, 0 }, { 100000, 100000 },
+        { 0, 0 }, { 1e30f, 0 }, { 1e30f, 1e30f },
+    };
+    const int N = sizeof(triples) / sizeof(SkPoint);
+
+    for (int i = 0; i < N; i += 3) {
+        const SkPoint* pts = &triples[i];
+
+        SkRect bounds;
+        bounds.set(pts, 3);
+
+        SkScalar w = 1e30f;
+        do {
+            w *= 2;
+            test_this_conic_to_quad(reporter, pts, w);
+        } while (SkScalarIsFinite(w));
+        test_this_conic_to_quad(reporter, pts, SK_ScalarNaN);
+    }
+}
+
+static void test_cubic_tangents(skiatest::Reporter* reporter) {
+    SkPoint pts[] = {
+        { 10, 20}, {10, 20}, {20, 30}, {30, 40},
+        { 10, 20}, {15, 25}, {20, 30}, {30, 40},
+        { 10, 20}, {20, 30}, {30, 40}, {30, 40},
+    };
+    int count = (int) SK_ARRAY_COUNT(pts) / 4;
+    for (int index = 0; index < count; ++index) {
+        SkConic conic(&pts[index * 3], 0.707f);
+        SkVector start, mid, end;
+        SkEvalCubicAt(&pts[index * 4], 0, nullptr, &start, nullptr);
+        SkEvalCubicAt(&pts[index * 4], .5f, nullptr, &mid, nullptr);
+        SkEvalCubicAt(&pts[index * 4], 1, nullptr, &end, nullptr);
+        REPORTER_ASSERT(reporter, start.fX && start.fY);
+        REPORTER_ASSERT(reporter, mid.fX && mid.fY);
+        REPORTER_ASSERT(reporter, end.fX && end.fY);
+        REPORTER_ASSERT(reporter, SkScalarNearlyZero(start.cross(mid)));
+        REPORTER_ASSERT(reporter, SkScalarNearlyZero(mid.cross(end)));
+    }
+}
+
+static void check_cubic_type(skiatest::Reporter* reporter,
+                             const std::array<SkPoint, 4>& bezierPoints, SkCubicType expectedType) {
+    SkCubicType actualType = SkClassifyCubic(bezierPoints.data());
+    REPORTER_ASSERT(reporter, actualType == expectedType);
+}
+
+static void test_classify_cubic(skiatest::Reporter* reporter) {
+    check_cubic_type(reporter, {{{149.325f, 107.705f}, {149.325f, 103.783f},
+                                 {151.638f, 100.127f}, {156.263f, 96.736f}}},
+                     SkCubicType::kQuadratic);
+    check_cubic_type(reporter, {{{225.694f, 223.15f}, {209.831f, 224.837f},
+                                 {195.994f, 230.237f}, {184.181f, 239.35f}}},
+                     SkCubicType::kQuadratic);
+    check_cubic_type(reporter, {{{4.873f, 5.581f}, {5.083f, 5.2783f},
+                                 {5.182f, 4.8593f}, {5.177f, 4.3242f}}},
+                     SkCubicType::kSerpentine);
+}
+
 DEF_TEST(Geometry, reporter) {
     SkPoint pts[3], dst[5];
 
@@ -44,18 +235,22 @@
     REPORTER_ASSERT(reporter, count == 1 || count == 2);
 
     pts[0].set(0, 0);
-    pts[1].set(SkIntToScalar(3), 0);
-    pts[2].set(SkIntToScalar(3), SkIntToScalar(3));
+    pts[1].set(3, 0);
+    pts[2].set(3, 3);
     SkConvertQuadToCubic(pts, dst);
     const SkPoint cubic[] = {
-        { 0, 0, },
-        { SkIntToScalar(2), 0, },
-        { SkIntToScalar(3), SkIntToScalar(1), },
-        { SkIntToScalar(3), SkIntToScalar(3) },
+        { 0, 0, }, { 2, 0, }, { 3, 1, }, { 3, 3 },
     };
     for (int i = 0; i < 4; ++i) {
         REPORTER_ASSERT(reporter, nearly_equal(cubic[i], dst[i]));
     }
 
     testChopCubic(reporter);
+    test_evalquadat(reporter);
+    test_conic(reporter);
+    test_cubic_tangents(reporter);
+    test_quad_tangents(reporter);
+    test_conic_tangents(reporter);
+    test_conic_to_quads(reporter);
+    test_classify_cubic(reporter);
 }
diff --git a/src/third_party/skia/tests/GifTest.cpp b/src/third_party/skia/tests/GifTest.cpp
index e2e0384..5749df1 100644
--- a/src/third_party/skia/tests/GifTest.cpp
+++ b/src/third_party/skia/tests/GifTest.cpp
@@ -5,22 +5,16 @@
  * found in the LICENSE file.
  */
 
-// This tests out GIF decoder (SkImageDecoder_libgif.cpp)
-// It is not used on these platforms:
-#if (!defined(SK_BUILD_FOR_WIN32)) &&           \
-    (!defined(SK_BUILD_FOR_IOS)) &&             \
-    (!defined(SK_BUILD_FOR_MAC))
-
+#include "CodecPriv.h"
+#include "Resources.h"
+#include "SkAndroidCodec.h"
 #include "SkBitmap.h"
 #include "SkData.h"
-#include "SkForceLinking.h"
 #include "SkImage.h"
-#include "SkImageDecoder.h"
 #include "SkStream.h"
+#include "SkTypes.h"
 #include "Test.h"
 
-__SK_FORCE_IMAGE_DECODER_LINKING;
-
 static unsigned char gGIFData[] = {
   0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x03, 0x00, 0x03, 0x00, 0xe3, 0x08,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00,
@@ -32,9 +26,18 @@
 };
 
 static unsigned char gGIFDataNoColormap[] = {
-  0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-  0x21, 0xf9, 0x04, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
-  0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x4c, 0x01, 0x00, 0x3b
+  // Header
+  0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
+  // Screen descriptor
+  0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  // Graphics control extension
+  0x21, 0xf9, 0x04, 0x01, 0x0a, 0x00, 0x01, 0x00,
+  // Image descriptor
+  0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+  // Image data
+  0x02, 0x02, 0x4c, 0x01, 0x00,
+  // Trailer
+  0x3b
 };
 
 static unsigned char gInterlacedGIF[] = {
@@ -52,8 +55,7 @@
                                       void* data,
                                       size_t size) {
     SkBitmap bm;
-    bool imageDecodeSuccess = SkImageDecoder::DecodeMemory(
-        data, size, &bm);
+    bool imageDecodeSuccess = decode_memory(data, size, &bm);
     REPORTER_ASSERT(r, imageDecodeSuccess);
     REPORTER_ASSERT(r, bm.width() == 1);
     REPORTER_ASSERT(r, bm.height() == 1);
@@ -64,8 +66,7 @@
 }
 static void test_gif_data(skiatest::Reporter* r, void* data, size_t size) {
     SkBitmap bm;
-    bool imageDecodeSuccess = SkImageDecoder::DecodeMemory(
-        data, size, &bm);
+    bool imageDecodeSuccess = decode_memory(data, size, &bm);
     REPORTER_ASSERT(r, imageDecodeSuccess);
     REPORTER_ASSERT(r, bm.width() == 3);
     REPORTER_ASSERT(r, bm.height() == 3);
@@ -82,12 +83,20 @@
         REPORTER_ASSERT(r, bm.getColor(2, 2) == 0xff0000ff);
     }
 }
+static void test_gif_data_dims(skiatest::Reporter* r, void* data, size_t size, int width,
+        int height) {
+    SkBitmap bm;
+    bool imageDecodeSuccess = decode_memory(data, size, &bm);
+    REPORTER_ASSERT(r, imageDecodeSuccess);
+    REPORTER_ASSERT(r, bm.width() == width);
+    REPORTER_ASSERT(r, bm.height() == height);
+    REPORTER_ASSERT(r, !(bm.empty()));
+}
 static void test_interlaced_gif_data(skiatest::Reporter* r,
                                      void* data,
                                      size_t size) {
     SkBitmap bm;
-    bool imageDecodeSuccess = SkImageDecoder::DecodeMemory(
-        data, size, &bm);
+    bool imageDecodeSuccess = decode_memory(data, size, &bm);
     REPORTER_ASSERT(r, imageDecodeSuccess);
     REPORTER_ASSERT(r, bm.width() == 9);
     REPORTER_ASSERT(r, bm.height() == 9);
@@ -119,8 +128,7 @@
                                 void* data,
                                 size_t size) {
     SkBitmap bm;
-    bool imageDecodeSuccess = SkImageDecoder::DecodeMemory(
-        data, size, &bm);
+    bool imageDecodeSuccess = decode_memory(data, size, &bm);
     REPORTER_ASSERT(r, imageDecodeSuccess);
     REPORTER_ASSERT(r, bm.width() == 3);
     REPORTER_ASSERT(r, bm.height() == 3);
@@ -136,7 +144,7 @@
 }
 
 /**
-  This test will test the ability of the SkImageDecoder to deal with
+  This test will test the ability of the SkCodec to deal with
   GIF files which have been mangled somehow.  We want to display as
   much of the GIF as possible.
 */
@@ -148,10 +156,6 @@
 
     unsigned char badData[sizeof(gGIFData)];
 
-    /* If you set the environment variable
-       skia_images_gif_suppressDecoderWarnings to 'false', you will
-       see warnings on stderr.  This is a feature.  */
-
     memcpy(badData, gGIFData, sizeof(gGIFData));
     badData[6] = 0x01;  // image too wide
     test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData));
@@ -164,29 +168,50 @@
 
     memcpy(badData, gGIFData, sizeof(gGIFData));
     badData[62] = 0x01;  // image shifted right
-    test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData));
-    // "libgif warning [shifting image left to fit]"
+    test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 4, 3);
 
     memcpy(badData, gGIFData, sizeof(gGIFData));
     badData[64] = 0x01;  // image shifted down
-    test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData));
-    // "libgif warning [shifting image up to fit]"
+    test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3, 4);
 
     memcpy(badData, gGIFData, sizeof(gGIFData));
-    badData[62] = 0xff;  // image shifted left
-    badData[63] = 0xff;  // 2's complement -1 short
-    test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData));
-    // "libgif warning [shifting image left to fit]"
+    badData[62] = 0xff;  // image shifted right
+    badData[63] = 0xff;
+    test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3 + 0xFFFF, 3);
 
     memcpy(badData, gGIFData, sizeof(gGIFData));
-    badData[64] = 0xff;  // image shifted up
-    badData[65] = 0xff;  // 2's complement -1 short
-    test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData));
-    // "libgif warning [shifting image up to fit]"
+    badData[64] = 0xff;  // image shifted down
+    badData[65] = 0xff;
+    test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3, 3 + 0xFFFF);
 
     test_gif_data_no_colormap(reporter, static_cast<void *>(gGIFDataNoColormap),
                               sizeof(gGIFDataNoColormap));
-    // "libgif warning [missing colormap]"
+
+    // Since there is no color map, we do not even need to parse the image data
+    // to know that we should draw transparent. Truncate the file before the
+    // data. This should still succeed.
+    test_gif_data_no_colormap(reporter, static_cast<void *>(gGIFDataNoColormap), 31);
+
+    // Likewise, incremental decoding should succeed here.
+    {
+        sk_sp<SkData> data = SkData::MakeWithoutCopy(gGIFDataNoColormap, 31);
+        std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
+        REPORTER_ASSERT(reporter, codec);
+        if (codec) {
+            auto info = codec->getInfo().makeColorType(kN32_SkColorType);
+            SkBitmap bm;
+            bm.allocPixels(info);
+            REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->startIncrementalDecode(
+                    info, bm.getPixels(), bm.rowBytes()));
+            REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->incrementalDecode());
+            REPORTER_ASSERT(reporter, bm.width() == 1);
+            REPORTER_ASSERT(reporter, bm.height() == 1);
+            REPORTER_ASSERT(reporter, !(bm.empty()));
+            if (!(bm.empty())) {
+                REPORTER_ASSERT(reporter, bm.getColor(0, 0) == 0x00000000);
+            }
+        }
+    }
 
     // test short Gif.  80 is missing a few bytes.
     test_gif_data_short(reporter, static_cast<void *>(gGIFData), 80);
@@ -197,4 +222,63 @@
     // "libgif warning [interlace DGifGetLine]"
 }
 
-#endif  // !(SK_BUILD_FOR_WIN32||SK_BUILD_FOR_IOS||SK_BUILD_FOR_MAC)
+// Regression test for decoding a gif image with sampleSize of 4, which was
+// previously crashing.
+DEF_TEST(Gif_Sampled, r) {
+    std::unique_ptr<SkFILEStream> stream(
+            new SkFILEStream(GetResourcePath("test640x479.gif").c_str()));
+    REPORTER_ASSERT(r, stream->isValid());
+    if (!stream->isValid()) {
+        return;
+    }
+
+    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(stream.release()));
+    REPORTER_ASSERT(r, codec);
+    if (!codec) {
+        return;
+    }
+
+    SkAndroidCodec::AndroidOptions options;
+    options.fSampleSize = 4;
+
+    SkBitmap bm;
+    bm.allocPixels(codec->getInfo());
+    const SkCodec::Result result = codec->getAndroidPixels(codec->getInfo(), bm.getPixels(),
+            bm.rowBytes(), &options);
+    REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+}
+
+// If a GIF file is truncated before the header for the first image is defined,
+// we should not create an SkCodec.
+DEF_TEST(Codec_GifTruncated, r) {
+    sk_sp<SkData> data(GetResourceAsData("test640x479.gif"));
+    if (!data) {
+        return;
+    }
+
+    // This is right before the header for the first image.
+    data = SkData::MakeSubset(data.get(), 0, 446);
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
+    REPORTER_ASSERT(r, !codec);
+}
+
+DEF_TEST(Codec_GifTruncated2, r) {
+    sk_sp<SkData> data(GetResourceAsData("box.gif"));
+    if (!data) {
+        return;
+    }
+
+    // This is after the header, but before the color table.
+    data = SkData::MakeSubset(data.get(), 0, 23);
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
+    if (!codec) {
+        ERRORF(r, "Failed to create codec with partial data");
+        return;
+    }
+
+    // Although we correctly created a codec, no frame is
+    // complete enough that it has its metadata. Returning 0
+    // ensures that Chromium will not try to create a frame
+    // too early.
+    REPORTER_ASSERT(r, codec->getFrameCount() == 0);
+}
diff --git a/src/third_party/skia/tests/GpuColorFilterTest.cpp b/src/third_party/skia/tests/GpuColorFilterTest.cpp
deleted file mode 100644
index 202756b..0000000
--- a/src/third_party/skia/tests/GpuColorFilterTest.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#if SK_SUPPORT_GPU
-
-#include "GrContext.h"
-#include "GrContextFactory.h"
-#include "GrProcessor.h"
-#include "SkColorFilter.h"
-#include "SkGr.h"
-#include "Test.h"
-
-static GrColor filterColor(const GrColor& color, uint32_t flags)  {
-    uint32_t mask = 0;
-    if (flags & kR_GrColorComponentFlag) {
-        mask = 0xFF << GrColor_SHIFT_R;
-    }
-    if (flags & kG_GrColorComponentFlag) {
-        mask |= 0xFF << GrColor_SHIFT_G;
-    }
-    if (flags & kB_GrColorComponentFlag) {
-        mask |= 0xFF << GrColor_SHIFT_B;
-    }
-    if (flags & kA_GrColorComponentFlag) {
-        mask |= 0xFF << GrColor_SHIFT_A;
-    }
-    return color & mask;
-}
-
-static void test_getConstantColorComponents(skiatest::Reporter* reporter, GrContext* grContext) {
-    struct GetConstantComponentTestCase {
-        // "Shape drawn with"
-        uint32_t inputComponents; // "rgb of", "red of", "alpha of", ...
-        GrColor inputColor;       // "[color]"
-
-        SkColor filterColor;      // "with filter color [color]"
-        SkXfermode::Mode filterMode; // "in mode [mode]"
-
-        // "produces"
-        uint32_t outputComponents; // "rgb of", "red of", "alpha of", ...
-        GrColor outputColor;       // "[color]"
-    };
-
-    // Shorthands.
-    enum {
-        kR = kR_GrColorComponentFlag,
-        kG = kG_GrColorComponentFlag,
-        kB = kB_GrColorComponentFlag,
-        kA = kA_GrColorComponentFlag,
-        kRGB = kRGB_GrColorComponentFlags,
-        kRGBA = kRGBA_GrColorComponentFlags
-    };
-
-    // Note: below, SkColors are non-premultiplied, where as GrColors are premultiplied.
-
-    const SkColor c1 = SkColorSetARGB(200, 200, 200, 200);
-    const SkColor c2 = SkColorSetARGB(60, 60, 60, 60);
-    const GrColor gr_c1 = SkColor2GrColor(c1);
-    const GrColor gr_c2 = SkColor2GrColor(c2);
-
-    const GrColor gr_black = GrColorPackRGBA(0, 0, 0, 0);
-    const GrColor gr_white = GrColorPackRGBA(255, 255, 255, 255);
-    const GrColor gr_whiteTrans = GrColorPackRGBA(128, 128, 128, 128);
-
-    GetConstantComponentTestCase filterTests[] = {
-        // A color filtered with Clear produces black.
-        { kRGBA, gr_white, SK_ColorBLACK, SkXfermode::kClear_Mode, kRGBA, gr_black },
-        { kRGBA, gr_c1,    SK_ColorWHITE, SkXfermode::kClear_Mode, kRGBA, gr_black },
-        { kR,    gr_white, c1,            SkXfermode::kClear_Mode, kRGBA, gr_black },
-
-        // A color filtered with a color in mode Src, produces the filter color.
-        { kRGBA, gr_c2, c1, SkXfermode::kSrc_Mode, kRGBA, gr_c1 },
-        { kA,    gr_c1, c1, SkXfermode::kSrc_Mode, kRGBA, gr_c1 },
-
-        // A color filtered with SrcOver produces a color.
-        { kRGBA, gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kSrcOver_Mode, kRGBA, GrColorPackRGBA(164, 164, 164, 192)},
-        // An unknown color with known alpha filtered with SrcOver produces an unknown color with known alpha.
-        { kA   , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kSrcOver_Mode, kA   , GrColorPackRGBA(0, 0, 0, 192)},
-        // A color with unknown alpha filtered with SrcOver produces a color with unknown alpha.
-        { kRGB , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kSrcOver_Mode, kRGB, GrColorPackRGBA(164, 164, 164, 0)},
-
-        // A color filtered with DstOver produces a color.
-        { kRGBA, gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kDstOver_Mode, kRGBA, GrColorPackRGBA(178, 178, 178, 192)},
-        // An unknown color with known alpha filtered with DstOver produces an unknown color with known alpha.
-        { kA   , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kDstOver_Mode, kA   , GrColorPackRGBA(0, 0, 0, 192)},
-        // A color with unknown alpha filtered with DstOver produces an unknown color.
-        { kRGB , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kDstOver_Mode, 0    , gr_black},
-
-        // An unknown color with known alpha and red component filtered with Multiply produces an unknown color with known red and alpha.
-        { kR|kA , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kModulate_Mode, kR|kA, GrColorPackRGBA(50, 0, 0, 64) }
-    };
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(filterTests); ++i) {
-        const GetConstantComponentTestCase& test = filterTests[i];
-        SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(test.filterColor, test.filterMode));
-        SkAutoTUnref<GrFragmentProcessor> effect(cf->asFragmentProcessor(grContext));
-        GrColor color = test.inputColor;
-        uint32_t components = test.inputComponents;
-        effect->getConstantColorComponents(&color, &components);
-
-        REPORTER_ASSERT(reporter, filterColor(color, components) == test.outputColor);
-        REPORTER_ASSERT(reporter, test.outputComponents == components);
-    }
-}
-
-DEF_GPUTEST(GpuColorFilter, reporter, factory) {
-    for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
-        GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type);
-
-        GrContext* grContext = factory->get(glType);
-        if (NULL == grContext) {
-            continue;
-        }
-
-        test_getConstantColorComponents(reporter, grContext);
-    }
-}
-
-#endif
diff --git a/src/third_party/skia/tests/GpuDrawPathTest.cpp b/src/third_party/skia/tests/GpuDrawPathTest.cpp
index 3e47a05..83641cb 100644
--- a/src/third_party/skia/tests/GpuDrawPathTest.cpp
+++ b/src/third_party/skia/tests/GpuDrawPathTest.cpp
@@ -5,27 +5,35 @@
  * found in the LICENSE file.
  */
 
+#include "SkTypes.h"
+
 #if SK_SUPPORT_GPU
 
 #include "GrContext.h"
-#include "GrContextFactory.h"
+#include "GrPath.h"
+#include "GrShape.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkColor.h"
 #include "SkPaint.h"
+#include "SkPath.h"
+#include "SkDashPathEffect.h"
 #include "SkRRect.h"
 #include "SkRect.h"
 #include "SkSurface.h"
 #include "Test.h"
 
+#include <initializer_list>
+
 static void test_drawPathEmpty(skiatest::Reporter*, SkCanvas* canvas) {
     // Filling an empty path should not crash.
     SkPaint paint;
-    canvas->drawRect(SkRect(), paint);
+    SkRect emptyRect = SkRect::MakeEmpty();
+    canvas->drawRect(emptyRect, paint);
     canvas->drawPath(SkPath(), paint);
-    canvas->drawOval(SkRect(), paint);
-    canvas->drawRect(SkRect(), paint);
-    canvas->drawRRect(SkRRect(), paint);
+    canvas->drawOval(emptyRect, paint);
+    canvas->drawRect(emptyRect, paint);
+    canvas->drawRRect(SkRRect::MakeRect(emptyRect), paint);
 
     // Stroking an empty path should not crash.
     paint.setAntiAlias(true);
@@ -33,32 +41,125 @@
     paint.setColor(SK_ColorGRAY);
     paint.setStrokeWidth(SkIntToScalar(20));
     paint.setStrokeJoin(SkPaint::kRound_Join);
-    canvas->drawRect(SkRect(), paint);
+    canvas->drawRect(emptyRect, paint);
     canvas->drawPath(SkPath(), paint);
-    canvas->drawOval(SkRect(), paint);
-    canvas->drawRect(SkRect(), paint);
-    canvas->drawRRect(SkRRect(), paint);
+    canvas->drawOval(emptyRect, paint);
+    canvas->drawRect(emptyRect, paint);
+    canvas->drawRRect(SkRRect::MakeRect(emptyRect), paint);
 }
 
+static void fill_and_stroke(SkCanvas* canvas, const SkPath& p1, const SkPath& p2,
+                            sk_sp<SkPathEffect> effect) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setPathEffect(effect);
 
-DEF_GPUTEST(GpuDrawPath, reporter, factory) {
-    return;
+    canvas->drawPath(p1, paint);
+    canvas->drawPath(p2, paint);
 
-    for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
-        GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type);
+    paint.setStyle(SkPaint::kStroke_Style);
+    canvas->drawPath(p1, paint);
+    canvas->drawPath(p2, paint);
+}
 
-        GrContext* grContext = factory->get(glType);
-        if (NULL == grContext) {
-            continue;
-        }
-        static const int sampleCounts[] = { 0, 4, 16 };
+static void test_drawSameRectOvals(skiatest::Reporter*, SkCanvas* canvas) {
+    // Drawing ovals with similar bounds but different points order should not crash.
 
-        for (size_t i = 0; i < SK_ARRAY_COUNT(sampleCounts); ++i) {
+    SkPath oval1, oval2;
+    const SkRect rect = SkRect::MakeWH(100, 50);
+    oval1.addOval(rect, SkPath::kCW_Direction);
+    oval2.addOval(rect, SkPath::kCCW_Direction);
+
+    fill_and_stroke(canvas, oval1, oval2, nullptr);
+
+    const SkScalar intervals[] = { 1, 1 };
+    fill_and_stroke(canvas, oval1, oval2, SkDashPathEffect::Make(intervals, 2, 0));
+}
+
+DEF_GPUTEST_FOR_ALL_GL_CONTEXTS(GpuDrawPath, reporter, ctxInfo) {
+    for (auto& test_func : { &test_drawPathEmpty, &test_drawSameRectOvals }) {
+        for (auto& sampleCount : {0, 4, 16}) {
             SkImageInfo info = SkImageInfo::MakeN32Premul(255, 255);
-            
-            SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(grContext, info,
-                                                                       sampleCounts[i], NULL));
-            test_drawPathEmpty(reporter, surface->getCanvas());
+            auto surface(
+                SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo, info,
+                                            sampleCount, nullptr));
+            if (!surface) {
+                continue;
+            }
+            test_func(reporter, surface->getCanvas());
+        }
+    }
+}
+
+DEF_GPUTEST(GrPathKeys, reporter, /*factory*/) {
+    SkPaint strokePaint;
+    strokePaint.setStyle(SkPaint::kStroke_Style);
+    strokePaint.setStrokeWidth(10.f);
+    GrStyle styles[] = {
+        GrStyle::SimpleFill(),
+        GrStyle::SimpleHairline(),
+        GrStyle(strokePaint)
+    };
+
+    for (const GrStyle& style : styles) {
+        // Keys should not ignore conic weights.
+        SkPath path1, path2;
+        SkPoint p0 = SkPoint::Make(100, 0);
+        SkPoint p1 = SkPoint::Make(100, 100);
+
+        path1.conicTo(p0, p1, .5f);
+        path2.conicTo(p0, p1, .7f);
+
+        GrUniqueKey key1, key2;
+        // We expect these small paths to be keyed based on their data.
+        bool isVolatile;
+        GrPath::ComputeKey(GrShape(path1, GrStyle::SimpleFill()), &key1, &isVolatile);
+        REPORTER_ASSERT(reporter, !isVolatile);
+        REPORTER_ASSERT(reporter, key1.isValid());
+        GrPath::ComputeKey(GrShape(path2, GrStyle::SimpleFill()), &key2, &isVolatile);
+        REPORTER_ASSERT(reporter, !isVolatile);
+        REPORTER_ASSERT(reporter, key1.isValid());
+        REPORTER_ASSERT(reporter, key1 != key2);
+        {
+            GrUniqueKey tempKey;
+            path1.setIsVolatile(true);
+            GrPath::ComputeKey(GrShape(path1, style), &key1, &isVolatile);
+            REPORTER_ASSERT(reporter, isVolatile);
+            REPORTER_ASSERT(reporter, !tempKey.isValid());
+        }
+
+        // Ensure that recreating the GrShape doesn't change the key.
+        {
+            GrUniqueKey tempKey;
+            GrPath::ComputeKey(GrShape(path2, GrStyle::SimpleFill()), &tempKey, &isVolatile);
+            REPORTER_ASSERT(reporter, key2 == tempKey);
+        }
+
+        // Try a large path that is too big to be keyed off its data.
+        SkPath path3;
+        SkPath path4;
+        for (int i = 0; i < 1000; ++i) {
+            SkScalar s = SkIntToScalar(i);
+            path3.conicTo(s, 3.f * s / 4, s + 1.f, s, 0.5f + s / 2000.f);
+            path4.conicTo(s, 3.f * s / 4, s + 1.f, s, 0.3f + s / 2000.f);
+        }
+
+        GrUniqueKey key3, key4;
+        // These aren't marked volatile and so should have keys
+        GrPath::ComputeKey(GrShape(path3, style), &key3, &isVolatile);
+        REPORTER_ASSERT(reporter, !isVolatile);
+        REPORTER_ASSERT(reporter, key3.isValid());
+        GrPath::ComputeKey(GrShape(path4, style), &key4, &isVolatile);
+        REPORTER_ASSERT(reporter, !isVolatile);
+        REPORTER_ASSERT(reporter, key4.isValid());
+        REPORTER_ASSERT(reporter, key3 != key4);
+
+        {
+            GrUniqueKey tempKey;
+            path3.setIsVolatile(true);
+            GrPath::ComputeKey(GrShape(path3, style), &key1, &isVolatile);
+            REPORTER_ASSERT(reporter, isVolatile);
+            REPORTER_ASSERT(reporter, !tempKey.isValid());
         }
     }
 }
diff --git a/src/third_party/skia/tests/GpuLayerCacheTest.cpp b/src/third_party/skia/tests/GpuLayerCacheTest.cpp
index c9b88f5..7419f17 100644
--- a/src/third_party/skia/tests/GpuLayerCacheTest.cpp
+++ b/src/third_party/skia/tests/GpuLayerCacheTest.cpp
@@ -5,22 +5,46 @@
 * found in the LICENSE file.
 */
 
+// Disabling this test since it is for the layer hoister which is current disabled.
+// The test fails when we add a discard to a newly created render target.
+#if 0
+
 #if SK_SUPPORT_GPU
 
 #include "GrContext.h"
-#include "GrContextFactory.h"
 #include "GrLayerCache.h"
+#include "GrResourceCache.h"
 #include "SkPictureRecorder.h"
 #include "Test.h"
 
 class TestingAccess {
 public:
+    static int NumPlots() {
+        return GrLayerCache::kNumPlotsX * GrLayerCache::kNumPlotsY;
+    }
+    static SkISize PlotSize() {
+        return SkISize::Make(GrLayerCache::kAtlasTextureWidth / GrLayerCache::kNumPlotsX,
+                             GrLayerCache::kAtlasTextureHeight / GrLayerCache::kNumPlotsY);
+    }
+
+    static GrTexture* GetBackingTexture(GrLayerCache* cache) {
+        return cache->fAtlas->getTextureOrNull();
+    }
+
     static int NumLayers(GrLayerCache* cache) {
         return cache->numLayers();
     }
     static void Purge(GrLayerCache* cache, uint32_t pictureID) {
         cache->purge(pictureID);
     }
+    static int Uses(GrCachedLayer* layer) {
+        return layer->uses();
+    }
+    static GrCachedLayer* Find(GrLayerCache* cache, uint32_t pictureID,
+                               const SkMatrix& initialMat,
+                               const int* key, int keySize) {
+        return cache->findLayer(pictureID, initialMat, key, keySize);
+    }
 };
 
 // Add several layers to the cache
@@ -31,14 +55,17 @@
                           int idOffset) {
 
     for (int i = 0; i < numToAdd; ++i) {
-        GrCachedLayer* layer = cache->findLayerOrCreate(picture.uniqueID(), 
-                                                        idOffset+i+1, idOffset+i+2, 
-                                                        SkIPoint::Make(0, 0),
+        int key[1] = { idOffset+i+1 };
+        GrCachedLayer* layer = cache->findLayerOrCreate(picture.uniqueID(),
+                                                        idOffset+i+1, idOffset+i+2,
+                                                        SkIRect::MakeEmpty(),
+                                                        SkIRect::MakeEmpty(),
                                                         SkMatrix::I(),
-                                                        NULL);
+                                                        key, 1,
+                                                        nullptr);
         REPORTER_ASSERT(reporter, layer);
-        GrCachedLayer* temp = cache->findLayer(picture.uniqueID(), idOffset+i+1, idOffset+i+2, 
-                                               SkIPoint::Make(0, 0), SkMatrix::I());
+        GrCachedLayer* temp = TestingAccess::Find(cache, picture.uniqueID(), SkMatrix::I(),
+                                                  key, 1);
         REPORTER_ASSERT(reporter, temp == layer);
 
         REPORTER_ASSERT(reporter, TestingAccess::NumLayers(cache) == idOffset + i + 1);
@@ -46,169 +73,302 @@
         REPORTER_ASSERT(reporter, picture.uniqueID() == layer->pictureID());
         REPORTER_ASSERT(reporter, layer->start() == idOffset + i + 1);
         REPORTER_ASSERT(reporter, layer->stop() == idOffset + i + 2);
-        REPORTER_ASSERT(reporter, layer->ctm() == SkMatrix::I());
-        REPORTER_ASSERT(reporter, NULL == layer->texture());
-        REPORTER_ASSERT(reporter, NULL == layer->paint());
+        REPORTER_ASSERT(reporter, !layer->texture());
+        REPORTER_ASSERT(reporter, !layer->paint());
         REPORTER_ASSERT(reporter, !layer->isAtlased());
     }
-
-    cache->trackPicture(&picture);
 }
 
 static void lock_layer(skiatest::Reporter* reporter,
                        GrLayerCache* cache,
                        GrCachedLayer* layer) {
-    // Make the layer 512x512 (so it can be atlased)
-    GrTextureDesc desc;
-    desc.fWidth = 512;
-    desc.fHeight = 512;
+    // Make each layer big enough to consume one whole plot in the atlas
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fWidth = TestingAccess::PlotSize().fWidth;
+    desc.fHeight = TestingAccess::PlotSize().fHeight;
     desc.fConfig = kSkia8888_GrPixelConfig;
 
-    bool needsRerendering = cache->lock(layer, desc, false);
+    bool needsRerendering;
+    bool inAtlas = cache->tryToAtlas(layer, desc, &needsRerendering);
+    if (!inAtlas) {
+        cache->lock(layer, desc, &needsRerendering);
+    }
     REPORTER_ASSERT(reporter, needsRerendering);
 
-    needsRerendering = cache->lock(layer, desc, false);
+    cache->lock(layer, desc, &needsRerendering);
     REPORTER_ASSERT(reporter, !needsRerendering);
 
     REPORTER_ASSERT(reporter, layer->texture());
     REPORTER_ASSERT(reporter, layer->locked());
+
+    cache->addUse(layer);
+
+    REPORTER_ASSERT(reporter, 1 == TestingAccess::Uses(layer));
 }
 
 // This test case exercises the public API of the GrLayerCache class.
 // In particular it checks its interaction with the resource cache (w.r.t.
 // locking & unlocking textures).
 // TODO: need to add checks on VRAM usage!
-DEF_GPUTEST(GpuLayerCache, reporter, factory) {
-    static const int kInitialNumLayers = 5;
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GpuLayerCache, reporter, ctxInfo) {
+    // Add one more layer than can fit in the atlas
+    static const int kInitialNumLayers = TestingAccess::NumPlots() + 1;
 
-    for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
-        GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
+#if GR_CACHE_STATS
+    GrResourceCache::Stats stats;
+#endif
 
-        if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
-            continue;
-        }
+    sk_sp<SkPicture> picture;
 
-        GrContext* context = factory->get(glCtxType);
-
-        if (NULL == context) {
-            continue;
-        }
-
+    {
         SkPictureRecorder recorder;
-        recorder.beginRecording(1, 1);
-        SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
-
-        GrLayerCache cache(context);
-
-        create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
-
-        for (int i = 0; i < kInitialNumLayers; ++i) {
-            GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, i+2, 
-                                                   SkIPoint::Make(0, 0), SkMatrix::I());
-            REPORTER_ASSERT(reporter, layer);
-
-            lock_layer(reporter, &cache, layer);
-
-            // The first 4 layers should be in the atlas (and thus have non-empty
-            // rects)
-            if (i < 4) {
-                REPORTER_ASSERT(reporter, layer->isAtlased());
-            } else {
-                // The 5th layer couldn't fit in the atlas
-                REPORTER_ASSERT(reporter, !layer->isAtlased());
-            }
-        }
-
-        // Unlock the textures
-        for (int i = 0; i < kInitialNumLayers; ++i) {
-            GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, i+2, 
-                                                   SkIPoint::Make(0, 0), SkMatrix::I());
-            REPORTER_ASSERT(reporter, layer);
-            cache.unlock(layer);
-        }
-
-        for (int i = 0; i < kInitialNumLayers; ++i) {
-            GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, i+2, 
-                                                   SkIPoint::Make(0, 0), SkMatrix::I());
-            REPORTER_ASSERT(reporter, layer);
-
-            REPORTER_ASSERT(reporter, !layer->locked());
-            // The first 4 layers should still be in the atlas.
-            if (i < 4) {
-                REPORTER_ASSERT(reporter, layer->texture());
-                REPORTER_ASSERT(reporter, layer->isAtlased());
-            } else {
-                // The final layer should be unlocked.
-                REPORTER_ASSERT(reporter, NULL == layer->texture());
-                REPORTER_ASSERT(reporter, !layer->isAtlased());
-            }
-        }
-
-        {
-            // Add an additional layer. Since all the layers are unlocked this 
-            // will force out the first atlased layer
-            create_layers(reporter, &cache, *picture, 1, kInitialNumLayers);
-            GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), 
-                                                   kInitialNumLayers+1, kInitialNumLayers+2, 
-                                                   SkIPoint::Make(0, 0), SkMatrix::I());
-            REPORTER_ASSERT(reporter, layer);
-
-            lock_layer(reporter, &cache, layer);
-            cache.unlock(layer);
-        }
-
-        for (int i = 0; i < kInitialNumLayers+1; ++i) {
-            GrCachedLayer* layer = cache.findLayer(picture->uniqueID(), i+1, i+2, 
-                                                   SkIPoint::Make(0, 0), SkMatrix::I());
-            // 3 old layers plus the new one should be in the atlas.
-            if (1 == i || 2 == i || 3 == i || 5 == i) {
-                REPORTER_ASSERT(reporter, layer);
-                REPORTER_ASSERT(reporter, !layer->locked());
-                REPORTER_ASSERT(reporter, layer->texture());
-                REPORTER_ASSERT(reporter, layer->isAtlased());
-            } else if (4 == i) {
-                // The one that was never atlased should still be around
-                REPORTER_ASSERT(reporter, layer);
-
-                REPORTER_ASSERT(reporter, NULL == layer->texture());
-                REPORTER_ASSERT(reporter, !layer->isAtlased());
-            } else {
-                // The one bumped out of the atlas (i.e., 0) should be gone
-                REPORTER_ASSERT(reporter, NULL == layer);
-            }
-        }
-
-        //--------------------------------------------------------------------
-        // Free them all SkGpuDevice-style. This will not free up the
-        // atlas' texture but will eliminate all the layers.
-        TestingAccess::Purge(&cache, picture->uniqueID());
-
-        REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
-        // TODO: add VRAM/resource cache check here
-
-        //--------------------------------------------------------------------
-        // Test out the GrContext-style purge. This should remove all the layers
-        // and the atlas.
-        // Re-create the layers
-        create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
-
-        // Free them again GrContext-style. This should free up everything.
-        cache.freeAll();
-
-        REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
-        // TODO: add VRAM/resource cache check here
-
-        //--------------------------------------------------------------------
-        // Test out the MessageBus-style purge. This will not free the atlas
-        // but should eliminate the free-floating layers.
-        create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
-
-        picture.reset(NULL);
-        cache.processDeletedPictures();
-
-        REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
-        // TODO: add VRAM/resource cache check here
+        SkCanvas* c = recorder.beginRecording(1, 1);
+        // Draw something, anything, to prevent an empty-picture optimization,
+        // which is a singleton and never purged.
+        c->drawRect(SkRect::MakeWH(1,1), SkPaint());
+        picture = recorder.finishRecordingAsPicture();
     }
+
+    GrResourceCache* resourceCache = ctxInfo.grContext()->getResourceCache();
+
+    GrLayerCache cache(ctxInfo.grContext());
+
+    create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
+
+    for (int i = 0; i < kInitialNumLayers; ++i) {
+        int key[1] = { i + 1 };
+        GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
+                                                   key, 1);
+        REPORTER_ASSERT(reporter, layer);
+
+        lock_layer(reporter, &cache, layer);
+
+#if GR_CACHE_STATS
+        resourceCache->getStats(&stats);
+#endif
+
+        // The first 4 layers should be in the atlas (and thus have non-empty rects)
+        if (i < TestingAccess::NumPlots()) {
+            REPORTER_ASSERT(reporter, layer->isAtlased());
+#if GR_CACHE_STATS
+            REPORTER_ASSERT(reporter, 1 == stats.fTotal);
+#endif
+        } else {
+            // The 5th layer couldn't fit in the atlas
+            REPORTER_ASSERT(reporter, !layer->isAtlased());
+#if GR_CACHE_STATS
+            REPORTER_ASSERT(reporter, 2 == stats.fTotal);
+#endif
+        }
+    }
+
+    // Unlock the textures
+    for (int i = 0; i < kInitialNumLayers; ++i) {
+        int key[1] = { i+1 };
+
+        GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
+                                                   key, 1);
+        REPORTER_ASSERT(reporter, layer);
+        cache.removeUse(layer);
+    }
+
+#if GR_CACHE_STATS
+    resourceCache->getStats(&stats);
+    REPORTER_ASSERT(reporter, 2 == stats.fTotal);
+    // The floating layer is purgeable the cache is not
+    REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable);
+    REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable);
+#endif
+
+    for (int i = 0; i < kInitialNumLayers; ++i) {
+        int key[1] = { i+1 };
+
+        GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
+                                                   key, 1);
+        REPORTER_ASSERT(reporter, layer);
+
+        // All the layers should be unlocked
+        REPORTER_ASSERT(reporter, !layer->locked());
+
+        // When hoisted layers aren't cached they are aggressively removed
+        // from the atlas
+#if GR_CACHE_HOISTED_LAYERS
+        // The first 4 layers should still be in the atlas.
+        if (i < 4) {
+            REPORTER_ASSERT(reporter, layer->texture());
+            REPORTER_ASSERT(reporter, layer->isAtlased());
+        } else {
+#endif
+            // The final layer should not be atlased.
+            REPORTER_ASSERT(reporter, !layer->texture());
+            REPORTER_ASSERT(reporter, !layer->isAtlased());
+#if GR_CACHE_HOISTED_LAYERS
+        }
+#endif
+    }
+
+    // Let go of the backing texture
+    cache.end();
+    REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache));
+
+#if GR_CACHE_STATS
+    resourceCache->getStats(&stats);
+    REPORTER_ASSERT(reporter, 2 == stats.fTotal);
+    // Now both the floater and the atlas are purgeable
+    REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable);
+#endif
+
+    // re-attach to the backing texture
+    cache.begin();
+    REPORTER_ASSERT(reporter, TestingAccess::GetBackingTexture(&cache));
+
+#if GR_CACHE_STATS
+    resourceCache->getStats(&stats);
+    REPORTER_ASSERT(reporter, 2 == stats.fTotal);
+    // The atlas is restored to being non-purgeable
+    REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable);
+    REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable);
+#endif
+
+    {
+        int key[1] = { kInitialNumLayers+1 };
+
+        // Add an additional layer. Since all the layers are unlocked this
+        // will force out the first atlased layer
+        create_layers(reporter, &cache, *picture, 1, kInitialNumLayers);
+        GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
+                                                   key, 1);
+        REPORTER_ASSERT(reporter, layer);
+
+        lock_layer(reporter, &cache, layer);
+        cache.removeUse(layer);
+    }
+
+    for (int i = 0; i < kInitialNumLayers+1; ++i) {
+        int key[1] = { i+1 };
+
+        GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
+                                                   key, 1);
+#if GR_CACHE_HOISTED_LAYERS
+        // 3 old layers plus the new one should be in the atlas.
+        if (1 == i || 2 == i || 3 == i || 5 == i) {
+            REPORTER_ASSERT(reporter, layer);
+            REPORTER_ASSERT(reporter, !layer->locked());
+            REPORTER_ASSERT(reporter, layer->texture());
+            REPORTER_ASSERT(reporter, layer->isAtlased());
+        } else if (4 == i) {
+#endif
+            // The one that was never atlased should still be around
+            REPORTER_ASSERT(reporter, layer);
+
+            REPORTER_ASSERT(reporter, !layer->texture());
+            REPORTER_ASSERT(reporter, !layer->isAtlased());
+#if GR_CACHE_HOISTED_LAYERS
+        } else {
+            // The one bumped out of the atlas (i.e., 0) should be gone
+            REPORTER_ASSERT(reporter, nullptr == layer);
+        }
+#endif
+    }
+
+    //--------------------------------------------------------------------
+    // Free them all SkGpuDevice-style. This will not free up the
+    // atlas' texture but will eliminate all the layers.
+    TestingAccess::Purge(&cache, picture->uniqueID());
+
+    REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
+
+#if GR_CACHE_STATS
+    resourceCache->getStats(&stats);
+    REPORTER_ASSERT(reporter, 2 == stats.fTotal);
+    // Atlas isn't purgeable
+    REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable);
+    REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable);
+#endif
+
+    //--------------------------------------------------------------------
+    // Test out the GrContext-style purge. This should remove all the layers
+    // and the atlas.
+    // Re-create the layers
+    create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
+
+    // Free them again GrContext-style. This should free up everything.
+    cache.freeAll();
+
+    REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
+
+    REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache));
+
+#if GR_CACHE_STATS
+    resourceCache->getStats(&stats);
+    REPORTER_ASSERT(reporter, 2 == stats.fTotal);
+    REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable);
+#endif
+
+    // Purge the resource cache ...
+    resourceCache->purgeAllUnlocked();
+
+#if GR_CACHE_STATS
+    resourceCache->getStats(&stats);
+    REPORTER_ASSERT(reporter, 0 == stats.fTotal);
+#endif
+
+    // and try to re-attach to the backing texture. This should fail
+    cache.begin();
+    REPORTER_ASSERT(reporter, nullptr == TestingAccess::GetBackingTexture(&cache));
+
+    //--------------------------------------------------------------------
+    // Test out the MessageBus-style purge. This will not free the atlas
+    // but should eliminate the free-floating layers.
+    create_layers(reporter, &cache, *picture, kInitialNumLayers, 0);
+
+    // Allocate/use the layers
+    for (int i = 0; i < kInitialNumLayers; ++i) {
+        int key[1] = { i + 1 };
+        GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
+                                                   key, 1);
+        REPORTER_ASSERT(reporter, layer);
+
+        lock_layer(reporter, &cache, layer);
+    }
+
+#if GR_CACHE_STATS
+    resourceCache->getStats(&stats);
+    REPORTER_ASSERT(reporter, 2 == stats.fTotal);
+    REPORTER_ASSERT(reporter, 2 == stats.fNumNonPurgeable);
+#endif
+
+    // Unlock the textures
+    for (int i = 0; i < kInitialNumLayers; ++i) {
+        int key[1] = { i+1 };
+
+        GrCachedLayer* layer = TestingAccess::Find(&cache, picture->uniqueID(), SkMatrix::I(),
+                                                   key, 1);
+        REPORTER_ASSERT(reporter, layer);
+        cache.removeUse(layer);
+    }
+
+    picture.reset(nullptr);
+    cache.processDeletedPictures();
+
+    REPORTER_ASSERT(reporter, TestingAccess::NumLayers(&cache) == 0);
+
+#if GR_CACHE_STATS
+    resourceCache->getStats(&stats);
+    REPORTER_ASSERT(reporter, 2 == stats.fTotal);
+    REPORTER_ASSERT(reporter, 1 == stats.fNumPurgeable);
+    REPORTER_ASSERT(reporter, 1 == stats.fNumNonPurgeable);
+#endif
+
+    cache.end();
+
+#if GR_CACHE_STATS
+    resourceCache->getStats(&stats);
+    REPORTER_ASSERT(reporter, 2 == stats.fTotal);
+    REPORTER_ASSERT(reporter, 2 == stats.fNumPurgeable);
+#endif
 }
 
 #endif
+#endif
diff --git a/src/third_party/skia/tests/GpuSampleLocationsTest.cpp b/src/third_party/skia/tests/GpuSampleLocationsTest.cpp
new file mode 100644
index 0000000..6c5d4a6
--- /dev/null
+++ b/src/third_party/skia/tests/GpuSampleLocationsTest.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+#include "SkPoint.h"
+#include "Test.h"
+#include <vector>
+
+#if SK_SUPPORT_GPU
+
+#include "GrAppliedClip.h"
+#include "GrRenderTargetContext.h"
+#include "GrRenderTargetPriv.h"
+#include "GrTypesPriv.h"
+#include "gl/GrGLGpu.h"
+#include "gl/debug/DebugGLTestContext.h"
+
+typedef std::vector<SkPoint> SamplePattern;
+
+static const SamplePattern kTestPatterns[] = {
+    SamplePattern{ // Intel on mac, msaa8, offscreen.
+        {0.562500, 0.312500},
+        {0.437500, 0.687500},
+        {0.812500, 0.562500},
+        {0.312500, 0.187500},
+        {0.187500, 0.812500},
+        {0.062500, 0.437500},
+        {0.687500, 0.937500},
+        {0.937500, 0.062500}
+    },
+
+    SamplePattern{ // Intel on mac, msaa8, on-screen.
+        {0.562500, 0.687500},
+        {0.437500, 0.312500},
+        {0.812500, 0.437500},
+        {0.312500, 0.812500},
+        {0.187500, 0.187500},
+        {0.062500, 0.562500},
+        {0.687500, 0.062500},
+        {0.937500, 0.937500}
+    },
+
+    SamplePattern{ // NVIDIA, msaa16.
+        {0.062500, 0.000000},
+        {0.250000, 0.125000},
+        {0.187500, 0.375000},
+        {0.437500, 0.312500},
+        {0.500000, 0.062500},
+        {0.687500, 0.187500},
+        {0.750000, 0.437500},
+        {0.937500, 0.250000},
+        {0.000000, 0.500000},
+        {0.312500, 0.625000},
+        {0.125000, 0.750000},
+        {0.375000, 0.875000},
+        {0.562500, 0.562500},
+        {0.812500, 0.687500},
+        {0.625000, 0.812500},
+        {0.875000, 0.937500}
+    },
+
+    SamplePattern{ // NVIDIA, mixed samples, 16:1.
+        {0.250000, 0.125000},
+        {0.625000, 0.812500},
+        {0.500000, 0.062500},
+        {0.812500, 0.687500},
+        {0.187500, 0.375000},
+        {0.875000, 0.937500},
+        {0.125000, 0.750000},
+        {0.750000, 0.437500},
+        {0.937500, 0.250000},
+        {0.312500, 0.625000},
+        {0.437500, 0.312500},
+        {0.000000, 0.500000},
+        {0.375000, 0.875000},
+        {0.687500, 0.187500},
+        {0.062500, 0.000000},
+        {0.562500, 0.562500}
+    }
+};
+constexpr int numTestPatterns = SK_ARRAY_COUNT(kTestPatterns);
+
+class TestSampleLocationsInterface : public SkNoncopyable {
+public:
+    virtual void overrideSamplePattern(const SamplePattern&) = 0;
+    virtual ~TestSampleLocationsInterface() {}
+};
+
+void assert_equal(skiatest::Reporter* reporter, const SamplePattern& pattern,
+                  const GrGpu::MultisampleSpecs& specs, bool flipY) {
+    GrAlwaysAssert(specs.fSampleLocations);
+    if ((int)pattern.size() != specs.fEffectiveSampleCnt) {
+        REPORT_FAILURE(reporter, "", SkString("Sample pattern has wrong number of samples."));
+        return;
+    }
+    for (int i = 0; i < specs.fEffectiveSampleCnt; ++i) {
+        SkPoint expectedLocation = specs.fSampleLocations[i];
+        if (flipY) {
+            expectedLocation.fY = 1 - expectedLocation.fY;
+        }
+        if (pattern[i] != expectedLocation) {
+            REPORT_FAILURE(reporter, "", SkString("Sample pattern has wrong sample location."));
+            return;
+        }
+    }
+}
+
+void test_sampleLocations(skiatest::Reporter* reporter, TestSampleLocationsInterface* testInterface,
+                          GrContext* ctx) {
+    SkRandom rand;
+    sk_sp<GrRenderTargetContext> bottomUps[numTestPatterns];
+    sk_sp<GrRenderTargetContext> topDowns[numTestPatterns];
+    for (int i = 0; i < numTestPatterns; ++i) {
+        int numSamples = (int)kTestPatterns[i].size();
+        GrAlwaysAssert(numSamples > 1 && SkIsPow2(numSamples));
+        bottomUps[i] = ctx->makeDeferredRenderTargetContextWithFallback(
+                           SkBackingFit::kExact, 100, 100, kRGBA_8888_GrPixelConfig, nullptr,
+                           rand.nextRangeU(1 + numSamples / 2, numSamples),
+                           kBottomLeft_GrSurfaceOrigin);
+        topDowns[i] = ctx->makeDeferredRenderTargetContextWithFallback(
+                          SkBackingFit::kExact, 100, 100, kRGBA_8888_GrPixelConfig, nullptr,
+                          rand.nextRangeU(1 + numSamples / 2, numSamples),
+                          kTopLeft_GrSurfaceOrigin);
+    }
+
+    // Ensure all sample locations get queried and/or cached properly.
+    for (int repeat = 0; repeat < 2; ++repeat) {
+        for (int i = 0; i < numTestPatterns; ++i) {
+            testInterface->overrideSamplePattern(kTestPatterns[i]);
+            for (GrRenderTargetContext* rtc : {bottomUps[i].get(), topDowns[i].get()}) {
+                GrPipeline dummyPipeline(rtc->accessRenderTarget(),
+                                         GrPipeline::ScissorState::kDisabled,
+                                         SkBlendMode::kSrcOver);
+                GrRenderTarget* rt = rtc->accessRenderTarget();
+                assert_equal(reporter, kTestPatterns[i],
+                             rt->renderTargetPriv().getMultisampleSpecs(dummyPipeline),
+                             kBottomLeft_GrSurfaceOrigin == rt->origin());
+            }
+        }
+    }
+
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class GLTestSampleLocationsInterface : public TestSampleLocationsInterface, public GrGLInterface {
+public:
+    GLTestSampleLocationsInterface() : fTestContext(sk_gpu_test::CreateDebugGLTestContext()) {
+        fStandard = fTestContext->gl()->fStandard;
+        fExtensions = fTestContext->gl()->fExtensions;
+        fFunctions = fTestContext->gl()->fFunctions;
+
+        fFunctions.fGetIntegerv = [&](GrGLenum pname, GrGLint* params) {
+            GrAlwaysAssert(GR_GL_EFFECTIVE_RASTER_SAMPLES != pname);
+            if (GR_GL_SAMPLES == pname) {
+                GrAlwaysAssert(!fSamplePattern.empty());
+                *params = (int)fSamplePattern.size();
+            } else {
+                fTestContext->gl()->fFunctions.fGetIntegerv(pname, params);
+            }
+        };
+
+        fFunctions.fGetMultisamplefv = [&](GrGLenum pname, GrGLuint index, GrGLfloat* val) {
+            GrAlwaysAssert(GR_GL_SAMPLE_POSITION == pname);
+            val[0] = fSamplePattern[index].fX;
+            val[1] = fSamplePattern[index].fY;
+        };
+    }
+
+    operator GrBackendContext() {
+        return reinterpret_cast<GrBackendContext>(static_cast<GrGLInterface*>(this));
+    }
+
+    void overrideSamplePattern(const SamplePattern& newPattern) override {
+        fSamplePattern = newPattern;
+    }
+
+private:
+    std::unique_ptr<sk_gpu_test::GLTestContext> fTestContext;
+    SamplePattern                               fSamplePattern;
+};
+
+DEF_GPUTEST(GLSampleLocations, reporter, /*factory*/) {
+    GLTestSampleLocationsInterface testInterface;
+    sk_sp<GrContext> ctx(GrContext::Create(kOpenGL_GrBackend, testInterface));
+
+    // This test relies on at least 2 samples.
+    int supportedSample = ctx->caps()->getSampleCount(2, kRGBA_8888_GrPixelConfig);
+    if (supportedSample < 2) {
+        return;
+    }
+    test_sampleLocations(reporter, &testInterface, ctx.get());
+}
+
+#endif
diff --git a/src/third_party/skia/tests/GrBinHashKeyTest.cpp b/src/third_party/skia/tests/GrBinHashKeyTest.cpp
deleted file mode 100644
index 605fd9f..0000000
--- a/src/third_party/skia/tests/GrBinHashKeyTest.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-// This is a GPU-backend specific test
-#if SK_SUPPORT_GPU
-
-#include "GrMurmur3HashKey.h"
-#include "GrBinHashKey.h"
-
-#include "Test.h"
-
-template<typename KeyType> static void TestHash(skiatest::Reporter* reporter) {
-    const char* testStringA_ = "abcdABCD";
-    const char* testStringB_ = "abcdBBCD";
-    const uint32_t* testStringA = reinterpret_cast<const uint32_t*>(testStringA_);
-    const uint32_t* testStringB = reinterpret_cast<const uint32_t*>(testStringB_);
-
-    KeyType keyA;
-    keyA.setKeyData(testStringA);
-    // test copy constructor and comparison
-    KeyType keyA2(keyA);
-    REPORTER_ASSERT(reporter, keyA == keyA2);
-    REPORTER_ASSERT(reporter, keyA.getHash() == keyA2.getHash());
-    // test re-init
-    keyA2.setKeyData(testStringA);
-    REPORTER_ASSERT(reporter, keyA == keyA2);
-    REPORTER_ASSERT(reporter, keyA.getHash() == keyA2.getHash());
-    // test sorting
-    KeyType keyB;
-    keyB.setKeyData(testStringB);
-    REPORTER_ASSERT(reporter, keyA.getHash() != keyB.getHash());
-}
-
-
-DEF_TEST(GrBinHashKey, reporter) {
-    enum {
-        kDataLenUsedForKey = 8
-    };
-
-    TestHash<GrBinHashKey<kDataLenUsedForKey> >(reporter);
-    TestHash<GrMurmur3HashKey<kDataLenUsedForKey> >(reporter);
-}
-
-#endif
diff --git a/src/third_party/skia/tests/GrContextAbandonTest.cpp b/src/third_party/skia/tests/GrContextAbandonTest.cpp
new file mode 100644
index 0000000..03e4512
--- /dev/null
+++ b/src/third_party/skia/tests/GrContextAbandonTest.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContextFactory.h"
+#include "Test.h"
+
+using namespace sk_gpu_test;
+
+DEF_GPUTEST(GrContext_abandonContext, reporter, /*factory*/) {
+    for (int testType = 0; testType < 6; ++testType) {
+        for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
+            GrContextFactory testFactory;
+            GrContextFactory::ContextType ctxType = (GrContextFactory::ContextType) i;
+            ContextInfo info = testFactory.getContextInfo(ctxType);
+            if (GrContext* context = info.grContext()) {
+                switch (testType) {
+                    case 0:
+                        context->abandonContext();
+                        break;
+                    case 1:
+                        context->releaseResourcesAndAbandonContext();
+                        break;
+                    case 2:
+                        context->abandonContext();
+                        context->abandonContext();
+                        break;
+                    case 3:
+                        context->abandonContext();
+                        context->releaseResourcesAndAbandonContext();
+                        break;
+                    case 4:
+                        context->releaseResourcesAndAbandonContext();
+                        context->abandonContext();
+                        break;
+                    case 5:
+                        context->releaseResourcesAndAbandonContext();
+                        context->releaseResourcesAndAbandonContext();
+                        break;
+                }
+            }
+        }
+    }
+}
+
+#endif
diff --git a/src/third_party/skia/tests/GrContextFactoryTest.cpp b/src/third_party/skia/tests/GrContextFactoryTest.cpp
index 1d884ac..d4d5667 100644
--- a/src/third_party/skia/tests/GrContextFactoryTest.cpp
+++ b/src/third_party/skia/tests/GrContextFactoryTest.cpp
@@ -5,28 +5,138 @@
  * found in the LICENSE file.
  */
 
+#include "SkTypes.h"
+
 #if SK_SUPPORT_GPU
 
 #include "GrContextFactory.h"
+#include "GrCaps.h"
 #include "Test.h"
 
-DEF_GPUTEST(GrContextFactory, reporter, factory) {
-    // Reset in case some other test has been using it first.
-    factory->destroyContexts();
+using namespace sk_gpu_test;
 
-    // Before we ask for a context, we expect the GL context to not be there.
-    REPORTER_ASSERT(reporter,
-                    NULL == factory->getGLContext(GrContextFactory::kNull_GLContextType));
+DEF_GPUTEST(GrContextFactory_NVPRContextOptionHasPathRenderingSupport, reporter, /*factory*/) {
+    // Test that if NVPR is requested, the context always has path rendering
+    // or the context creation fails.
+    GrContextFactory testFactory;
+    // Test that if NVPR is possible, caps are in sync.
+    for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
+        GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i);
+        GrContext* context = testFactory.get(ctxType,
+                                           GrContextFactory::ContextOverrides::kRequireNVPRSupport);
+        if (!context) {
+            continue;
+        }
+        REPORTER_ASSERT(
+            reporter,
+            context->caps()->shaderCaps()->pathRenderingSupport());
+    }
+}
 
-    // After we ask for a context, we expect that the GL context to be there.
-    factory->get(GrContextFactory::kNull_GLContextType);
-    REPORTER_ASSERT(reporter,
-                    factory->getGLContext(GrContextFactory::kNull_GLContextType) != NULL);
+DEF_GPUTEST(GrContextFactory_NoPathRenderingIfNVPRDisabled, reporter, /*factory*/) {
+    // Test that if NVPR is explicitly disabled, the context has no path rendering support.
 
-    // If we did not ask for a context with the particular GL context, we would
-    // expect the particular GL context to not be there.
-    REPORTER_ASSERT(reporter,
-                    NULL == factory->getGLContext(GrContextFactory::kDebug_GLContextType));
+    GrContextFactory testFactory;
+    for (int i = 0; i <= GrContextFactory::kLastContextType; ++i) {
+        GrContextFactory::ContextType ctxType = (GrContextFactory::ContextType)i;
+        GrContext* context =
+            testFactory.get(ctxType, GrContextFactory::ContextOverrides::kDisableNVPR);
+        if (context) {
+            REPORTER_ASSERT(
+                reporter,
+                !context->caps()->shaderCaps()->pathRenderingSupport());
+        }
+    }
+}
+
+DEF_GPUTEST(GrContextFactory_RequiredSRGBSupport, reporter, /*factory*/) {
+    // Test that if sRGB support is requested, the context always has that capability
+    // or the context creation fails. Also test that if the creation fails, a context
+    // created without that flag would not have had sRGB support.
+    GrContextFactory testFactory;
+    // Test that if sRGB is requested, caps are in sync.
+    for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
+        GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i);
+        GrContext* context =
+            testFactory.get(ctxType, GrContextFactory::ContextOverrides::kRequireSRGBSupport);
+
+        if (context) {
+            REPORTER_ASSERT(reporter, context->caps()->srgbSupport());
+        } else {
+            context = testFactory.get(ctxType);
+            if (context) {
+                REPORTER_ASSERT(reporter, !context->caps()->srgbSupport());
+            }
+        }
+    }
+}
+
+DEF_GPUTEST(GrContextFactory_abandon, reporter, /*factory*/) {
+    GrContextFactory testFactory;
+    for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
+        GrContextFactory::ContextType ctxType = (GrContextFactory::ContextType) i;
+        ContextInfo info1 = testFactory.getContextInfo(ctxType);
+        if (!info1.grContext()) {
+            continue;
+        }
+        REPORTER_ASSERT(reporter, info1.testContext());
+         // Ref for comparison. The API does not explicitly say that this stays alive.
+        info1.grContext()->ref();
+        testFactory.abandonContexts();
+
+        // Test that we get different context after abandon.
+        ContextInfo info2 = testFactory.getContextInfo(ctxType);
+        REPORTER_ASSERT(reporter, info2.grContext());
+        REPORTER_ASSERT(reporter, info2.testContext());
+
+        REPORTER_ASSERT(reporter, info1.grContext() != info2.grContext());
+        // The GL context should also change, but it also could get the same address.
+
+        info1.grContext()->unref();
+    }
+}
+
+DEF_GPUTEST(GrContextFactory_sharedContexts, reporter, /*factory*/) {
+    GrContextFactory testFactory;
+
+    for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
+        GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i);
+        ContextInfo info1 = testFactory.getContextInfo(ctxType);
+        if (!info1.grContext()) {
+            continue;
+        }
+
+        // Ref for passing in. The API does not explicitly say that this stays alive.
+        info1.grContext()->ref();
+        testFactory.abandonContexts();
+
+        // Test that creating a context in a share group with an abandoned context fails.
+        ContextInfo info2 = testFactory.getSharedContextInfo(info1.grContext());
+        REPORTER_ASSERT(reporter, !info2.grContext());
+        info1.grContext()->unref();
+
+        // Create a new base context
+        ContextInfo info3 = testFactory.getContextInfo(ctxType);
+        if (!info3.grContext()) {
+            // Vulkan NexusPlayer bot fails here. Sigh.
+            continue;
+        }
+
+        // Creating a context in a share group may fail, but should never crash.
+        ContextInfo info4 = testFactory.getSharedContextInfo(info3.grContext());
+        if (!info4.grContext()) {
+            continue;
+        }
+        REPORTER_ASSERT(reporter, info3.grContext() != info4.grContext());
+        REPORTER_ASSERT(reporter, info3.testContext() != info4.testContext());
+
+        // Passing a different index should create a new (unique) context.
+        ContextInfo info5 = testFactory.getSharedContextInfo(info3.grContext(), 1);
+        REPORTER_ASSERT(reporter, info5.grContext());
+        REPORTER_ASSERT(reporter, info5.testContext());
+        REPORTER_ASSERT(reporter, info5.grContext() != info4.grContext());
+        REPORTER_ASSERT(reporter, info5.testContext() != info4.testContext());
+    }
 }
 
 #endif
diff --git a/src/third_party/skia/tests/GrDrawTargetTest.cpp b/src/third_party/skia/tests/GrDrawTargetTest.cpp
index c032d88..c8492bc 100644
--- a/src/third_party/skia/tests/GrDrawTargetTest.cpp
+++ b/src/third_party/skia/tests/GrDrawTargetTest.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2013 Google Inc.
  *
@@ -6,31 +5,19 @@
  * found in the LICENSE file.
  */
 
+#include "Test.h"
 #if SK_SUPPORT_GPU
 
+#include "GrCaps.h"
 #include "GrContext.h"
-#include "GrContextFactory.h"
-#include "GrDrawTargetCaps.h"
 #include "GrGpu.h"
-#include "Test.h"
 
-static void test_print(skiatest::Reporter*, const GrDrawTargetCaps* caps) {
+DEF_GPUTEST_FOR_ALL_CONTEXTS(GrDrawTargetPrint, reporter, ctxInfo) {
     // This used to assert.
-    SkString result = caps->dump();
+    SkString result = ctxInfo.grContext()->caps()->dump();
     SkASSERT(!result.isEmpty());
-}
-
-DEF_GPUTEST(GrDrawTarget, reporter, factory) {
-    for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
-        GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type);
-
-        GrContext* grContext = factory->get(glType);
-        if (NULL == grContext) {
-            continue;
-        }
-
-        test_print(reporter, grContext->getGpu()->caps());
-    }
+    SkString shaderResult = ctxInfo.grContext()->caps()->shaderCaps()->dump();
+    SkASSERT(!shaderResult.isEmpty());
 }
 
 #endif
diff --git a/src/third_party/skia/tests/GrGLSLPrettyPrintTest.cpp b/src/third_party/skia/tests/GrGLSLPrettyPrintTest.cpp
deleted file mode 100644
index 9977488..0000000
--- a/src/third_party/skia/tests/GrGLSLPrettyPrintTest.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#if SK_SUPPORT_GPU
-#include "Test.h"
-#include "gl/GrGLSLPrettyPrint.h"
-
-#define ASSERT(x) REPORTER_ASSERT(r, x)
-
-const SkString input1("#this is not a realshader\nvec4 some stuff;outside of a function;"
-                     "int i(int b, int c) { { some stuff;} fake block; //comments\n return i;}"
-                     "void main()"
-                     "{nowin a function;{indenting;{abit more;dreadedfor((;;)(;)((;;);)){doingstuff"
-                     ";for(;;;){and more stufff;mixed garbage\n\n\t\t\t\t\n/*using this"
-                     " comment\n is"
-                     " dangerous\ndo so at your own\n risk*/;\n\n\t\t\t\n"
-                     "//a comment\n}}a; little ;  love; for   ; leading;  spaces;} "
-                     "an struct = { int a; int b; };"
-                     "int[5] arr = int[5](1,2,3,4,5);} some code at the bottom; for(;;) {} }");
-
-const SkString output1(
-        "   1\t#this is not a realshader\n"
-        "   2\tvec4 some stuff;\n"
-        "   3\toutside of a function;\n"
-        "   4\tint i(int b, int c) \n"
-        "   5\t{\n"
-        "   6\t\t{\n"
-        "   7\t\t\tsome stuff;\n"
-        "   8\t\t}\n"
-        "   9\t\tfake block;\n"
-        "  10\t\t//comments\n"
-        "  11\t\treturn i;\n"
-        "  12\t}\n"
-        "  13\tvoid main()\n"
-        "  14\t{\n"
-        "  15\t\tnowin a function;\n"
-        "  16\t\t{\n"
-        "  17\t\t\tindenting;\n"
-        "  18\t\t\t{\n"
-        "  19\t\t\t\tabit more;\n"
-        "  20\t\t\t\tdreadedfor((;;)(;)((;;);))\n"
-        "  21\t\t\t\t{\n"
-        "  22\t\t\t\t\tdoingstuff;\n"
-        "  23\t\t\t\t\tfor(;;;)\n"
-        "  24\t\t\t\t\t{\n"
-        "  25\t\t\t\t\t\tand more stufff;\n"
-        "  26\t\t\t\t\t\tmixed garbage/*using this comment\n"
-        "  27\t\t\t\t\t\t is dangerous\n"
-        "  28\t\t\t\t\t\tdo so at your own\n"
-        "  29\t\t\t\t\t\t risk*/;\n"
-        "  30\t\t\t\t\t\t//a comment\n"
-        "  31\t\t\t\t\t}\n"
-        "  32\t\t\t\t}\n"
-        "  33\t\t\t\ta;\n"
-        "  34\t\t\t\tlittle ;\n"
-        "  35\t\t\t\tlove;\n"
-        "  36\t\t\t\tfor   ;\n"
-        "  37\t\t\t\tleading;\n"
-        "  38\t\t\t\tspaces;\n"
-        "  39\t\t\t}\n"
-        "  40\t\t\tan struct = \n"
-        "  41\t\t\t{\n"
-        "  42\t\t\t\tint a;\n"
-        "  43\t\t\t\tint b;\n"
-        "  44\t\t\t}\n"
-        "  45\t\t\t;\n"
-        "  46\t\t\tint[5] arr = int[5](1,2,3,4,5);\n"
-        "  47\t\t}\n"
-        "  48\t\tsome code at the bottom;\n"
-        "  49\t\tfor(;;) \n"
-        "  50\t\t{\n"
-        "  51\t\t}\n"
-        "  52\t}\n"
-        "  53\t");
-
-const SkString input2("{;;{{{{;;;{{{{{{{{{{{###\n##\n#####(((((((((((((unbalanced verything;;;"
-        "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"
-        ";;;;;;/////");
-
-DEF_TEST(GrGLSLPrettyPrint, r) {
-    SkString test = GrGLSLPrettyPrint::PrettyPrintGLSL(input1, true);
-    ASSERT(output1 == test);
-
-    // Just test we don't crash with garbage input
-    ASSERT(GrGLSLPrettyPrint::PrettyPrintGLSL(input2, true).c_str() != NULL);
-}
-
-#endif
diff --git a/src/third_party/skia/tests/GrMemoryPoolTest.cpp b/src/third_party/skia/tests/GrMemoryPoolTest.cpp
index 0848d97..4eb5ca7 100644
--- a/src/third_party/skia/tests/GrMemoryPoolTest.cpp
+++ b/src/third_party/skia/tests/GrMemoryPoolTest.cpp
@@ -9,8 +9,8 @@
 // This is a GPU-backend specific test
 #if SK_SUPPORT_GPU
 #include "GrMemoryPool.h"
-#include "SkInstCnt.h"
 #include "SkRandom.h"
+#include "SkTArray.h"
 #include "SkTDArray.h"
 #include "SkTemplates.h"
 
@@ -19,14 +19,14 @@
 // that can be set and checked.
 class A {
 public:
-    A() {};
+    A() {}
     virtual void setValues(int v) {
         fChar = static_cast<char>(v);
     }
     virtual bool checkValues(int v) {
         return fChar == static_cast<char>(v);
     }
-    virtual ~A() {};
+    virtual ~A() {}
 
     void* operator new(size_t size) {
         if (!gPool.get()) {
@@ -44,35 +44,27 @@
         }
     }
 
-    SK_DECLARE_INST_COUNT_ROOT(A);
-
     static A* Create(SkRandom* r);
 
     static void SetAllocator(size_t preallocSize, size_t minAllocSize) {
-#if SK_ENABLE_INST_COUNT
-        SkASSERT(0 == GetInstanceCount());
-#endif
         GrMemoryPool* pool = new GrMemoryPool(preallocSize, minAllocSize);
         gPool.reset(pool);
     }
 
     static void ResetAllocator() {
-#if SK_ENABLE_INST_COUNT
-        SkASSERT(0 == GetInstanceCount());
-#endif
-        gPool.reset(NULL);
+        gPool.reset(nullptr);
     }
 
 private:
-    static SkAutoTDelete<GrMemoryPool> gPool;
+    static std::unique_ptr<GrMemoryPool> gPool;
     char fChar;
 };
 
-SkAutoTDelete<GrMemoryPool> A::gPool;
+std::unique_ptr<GrMemoryPool> A::gPool;
 
 class B : public A {
 public:
-    B() {};
+    B() {}
     virtual void setValues(int v) {
         fDouble = static_cast<double>(v);
         this->INHERITED::setValues(v);
@@ -81,7 +73,7 @@
         return fDouble == static_cast<double>(v) &&
                this->INHERITED::checkValues(v);
     }
-    virtual ~B() {};
+    virtual ~B() {}
 
 private:
     double fDouble;
@@ -91,7 +83,7 @@
 
 class C : public A {
 public:
-    C() {};
+    C() {}
     virtual void setValues(int v) {
         fInt64 = static_cast<int64_t>(v);
         this->INHERITED::setValues(v);
@@ -100,7 +92,7 @@
         return fInt64 == static_cast<int64_t>(v) &&
                this->INHERITED::checkValues(v);
     }
-    virtual ~C() {};
+    virtual ~C() {}
 
 private:
     int64_t fInt64;
@@ -115,12 +107,12 @@
         fB = new B();
     }
     virtual void setValues(int v) {
-        fVoidStar = reinterpret_cast<void*>(v);
+        fVoidStar = reinterpret_cast<void*>(static_cast<intptr_t>(v));
         this->INHERITED::setValues(v);
         fB->setValues(v);
     }
     virtual bool checkValues(int v) {
-        return fVoidStar == reinterpret_cast<void*>(v) &&
+        return fVoidStar == reinterpret_cast<void*>(static_cast<intptr_t>(v)) &&
                fB->checkValues(v) &&
                this->INHERITED::checkValues(v);
     }
@@ -173,7 +165,7 @@
             return new E;
         default:
             // suppress warning
-            return NULL;
+            return nullptr;
     }
 }
 
@@ -232,11 +224,178 @@
                 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
                 delete rec.fInstance;
             }
-#if SK_ENABLE_INST_COUNT
-            REPORTER_ASSERT(reporter, !A::GetInstanceCount());
-#endif
         }
     }
 }
 
+// GrMemoryPool requires that it's empty at the point of destruction. This helps
+// achieving that by releasing all added memory in the destructor.
+class AutoPoolReleaser {
+public:
+    AutoPoolReleaser(GrMemoryPool& pool): fPool(pool) {
+    }
+    ~AutoPoolReleaser() {
+        for (void* ptr: fAllocated) {
+            fPool.release(ptr);
+        }
+    }
+    void add(void* ptr) {
+        fAllocated.push_back(ptr);
+    }
+private:
+    GrMemoryPool& fPool;
+    SkTArray<void*> fAllocated;
+};
+
+DEF_TEST(GrMemoryPoolAPI, reporter) {
+    constexpr size_t kSmallestMinAllocSize = GrMemoryPool::kSmallestMinAllocSize;
+
+    // Allocates memory until pool adds a new block (pool.size() changes).
+    auto allocateMemory = [](GrMemoryPool& pool, AutoPoolReleaser& r) {
+        size_t origPoolSize = pool.size();
+        while (pool.size() == origPoolSize) {
+            r.add(pool.allocate(31));
+        }
+    };
+
+    // Effective prealloc space capacity is >= kSmallestMinAllocSize.
+    {
+        GrMemoryPool pool(0, 0);
+        REPORTER_ASSERT(reporter, pool.preallocSize() == kSmallestMinAllocSize);
+    }
+
+    // Effective prealloc space capacity is >= minAllocSize.
+    {
+        constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2;
+        GrMemoryPool pool(kSmallestMinAllocSize, kMinAllocSize);
+        REPORTER_ASSERT(reporter, pool.preallocSize() == kMinAllocSize);
+    }
+
+    // Effective block size capacity >= kSmallestMinAllocSize.
+    {
+        GrMemoryPool pool(kSmallestMinAllocSize, kSmallestMinAllocSize / 2);
+        AutoPoolReleaser r(pool);
+
+        allocateMemory(pool, r);
+        REPORTER_ASSERT(reporter, pool.size() == kSmallestMinAllocSize);
+    }
+
+    // Pool allocates exactly preallocSize on creation.
+    {
+        constexpr size_t kPreallocSize = kSmallestMinAllocSize * 5;
+        GrMemoryPool pool(kPreallocSize, 0);
+        REPORTER_ASSERT(reporter, pool.preallocSize() == kPreallocSize);
+    }
+
+    // Pool allocates exactly minAllocSize when it expands.
+    {
+        constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 7;
+        GrMemoryPool pool(0, kMinAllocSize);
+        AutoPoolReleaser r(pool);
+
+        allocateMemory(pool, r);
+        REPORTER_ASSERT(reporter, pool.size() == kMinAllocSize);
+
+        allocateMemory(pool, r);
+        REPORTER_ASSERT(reporter, pool.size() == 2 * kMinAllocSize);
+    }
+
+    // When asked to allocate amount > minAllocSize, pool allocates larger block
+    // to accommodate all internal structures.
+    {
+        constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2;
+        GrMemoryPool pool(kSmallestMinAllocSize, kMinAllocSize);
+        AutoPoolReleaser r(pool);
+
+        REPORTER_ASSERT(reporter, pool.size() == 0);
+
+        constexpr size_t hugeSize = 10 * kMinAllocSize;
+        r.add(pool.allocate(hugeSize));
+        REPORTER_ASSERT(reporter, pool.size() > hugeSize);
+
+        // Block size allocated to accommodate huge request doesn't include any extra
+        // space, so next allocation request allocates a new block.
+        size_t hugeBlockSize = pool.size();
+        r.add(pool.allocate(0));
+        REPORTER_ASSERT(reporter, pool.size() == hugeBlockSize + kMinAllocSize);
+    }
+}
+
+DEF_TEST(GrObjectMemoryPoolAPI, reporter) {
+    struct Data {
+        int value[5];
+    };
+    using DataObjectPool = GrObjectMemoryPool<Data>;
+    constexpr size_t kSmallestMinAllocCount = DataObjectPool::kSmallestMinAllocCount;
+
+    // Allocates objects until pool adds a new block (pool.size() changes).
+    // Returns number of objects that fit into the current block (i.e. before pool.size()
+    // changed; newly allocated block always ends up with one object allocated from it).
+    auto allocateObjects = [](DataObjectPool& pool, AutoPoolReleaser& r) -> size_t {
+        size_t count = 0;
+        size_t origPoolSize = pool.size();
+        while (pool.size() == origPoolSize) {
+            r.add(pool.allocate());
+            count++;
+        }
+        return count - 1;
+    };
+
+    // Effective prealloc space capacity is >= kSmallestMinAllocCount.
+    {
+        DataObjectPool pool(kSmallestMinAllocCount / 3, 0);
+        AutoPoolReleaser r(pool);
+
+        size_t preallocCount = allocateObjects(pool, r);
+        REPORTER_ASSERT(reporter, preallocCount == kSmallestMinAllocCount);
+    }
+
+    // Effective prealloc space capacity is >= minAllocCount.
+    {
+        DataObjectPool pool(kSmallestMinAllocCount, 2 * kSmallestMinAllocCount);
+        AutoPoolReleaser r(pool);
+
+        size_t preallocCount = allocateObjects(pool, r);
+        REPORTER_ASSERT(reporter, preallocCount == 2 * kSmallestMinAllocCount);
+    }
+
+    // Effective block capacity is >= kSmallestMinAllocCount.
+    {
+        DataObjectPool pool(kSmallestMinAllocCount, kSmallestMinAllocCount / 2);
+        AutoPoolReleaser r(pool);
+
+        // Fill prealloc space
+        allocateObjects(pool, r);
+
+        size_t minAllocCount = 1 + allocateObjects(pool, r);
+        REPORTER_ASSERT(reporter, minAllocCount == kSmallestMinAllocCount);
+    }
+
+    // Pool allocates space for exactly preallocCount objects on creation.
+    {
+        constexpr size_t kPreallocCount = kSmallestMinAllocCount * 7 / 3;
+        DataObjectPool pool(kPreallocCount, 0);
+        AutoPoolReleaser r(pool);
+
+        size_t preallocCount = allocateObjects(pool, r);
+        REPORTER_ASSERT(reporter, preallocCount == kPreallocCount);
+    }
+
+    // Pool allocates space for minAllocCount objects when it adds a new block.
+    {
+        constexpr size_t kMinAllocCount = kSmallestMinAllocCount * 11 / 3;
+        DataObjectPool pool(0, kMinAllocCount);
+        AutoPoolReleaser r(pool);
+
+        // Fill prealloc space
+        allocateObjects(pool, r);
+
+        size_t firstBlockCount = 1 + allocateObjects(pool, r);
+        REPORTER_ASSERT(reporter, firstBlockCount == kMinAllocCount);
+
+        size_t secondBlockCount = 1 + allocateObjects(pool, r);
+        REPORTER_ASSERT(reporter, secondBlockCount == kMinAllocCount);
+    }
+}
+
 #endif
diff --git a/src/third_party/skia/tests/GrMeshTest.cpp b/src/third_party/skia/tests/GrMeshTest.cpp
new file mode 100644
index 0000000..0e67acf
--- /dev/null
+++ b/src/third_party/skia/tests/GrMeshTest.cpp
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrGeometryProcessor.h"
+#include "GrGpuCommandBuffer.h"
+#include "GrOpFlushState.h"
+#include "GrRenderTargetContext.h"
+#include "GrRenderTargetContextPriv.h"
+#include "GrResourceProvider.h"
+#include "GrResourceKey.h"
+#include "SkMakeUnique.h"
+#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLGeometryProcessor.h"
+#include "glsl/GrGLSLVarying.h"
+#include <array>
+#include <vector>
+
+
+GR_DECLARE_STATIC_UNIQUE_KEY(gIndexBufferKey);
+
+static constexpr int kBoxSize = 2;
+static constexpr int kBoxCountY = 8;
+static constexpr int kBoxCountX = 8;
+static constexpr int kBoxCount = kBoxCountY * kBoxCountX;
+
+static constexpr int kImageWidth = kBoxCountY * kBoxSize;
+static constexpr int kImageHeight = kBoxCountX * kBoxSize;
+
+static constexpr int kIndexPatternRepeatCount = 3;
+constexpr uint16_t kIndexPattern[6] = {0, 1, 2, 1, 2, 3};
+
+
+class DrawMeshHelper {
+public:
+    DrawMeshHelper(GrOpFlushState* state) : fState(state) {}
+
+    sk_sp<const GrBuffer> getIndexBuffer();
+
+    template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const SkTArray<T>& data) {
+        return this->makeVertexBuffer(data.begin(), data.count());
+    }
+    template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const std::vector<T>& data) {
+        return this->makeVertexBuffer(data.data(), data.size());
+    }
+    template<typename T> sk_sp<const GrBuffer> makeVertexBuffer(const T* data, int count);
+
+    void drawMesh(const GrMesh& mesh);
+
+private:
+    GrOpFlushState* fState;
+};
+
+struct Box {
+    float fX, fY;
+    GrColor fColor;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * This is a GPU-backend specific test. It tries to test all possible usecases of GrMesh. The test
+ * works by drawing checkerboards of colored boxes, reading back the pixels, and comparing with
+ * expected results. The boxes are drawn on integer boundaries and the (opaque) colors are chosen
+ * from the set (r,g,b) = (0,255)^3, so the GPU renderings ought to produce exact matches.
+ */
+
+static void run_test(const char* testName, skiatest::Reporter*, const sk_sp<GrRenderTargetContext>&,
+                     const SkBitmap& gold, std::function<void(DrawMeshHelper*)> testFn);
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrMeshTest, reporter, ctxInfo) {
+    GrContext* const context = ctxInfo.grContext();
+
+    sk_sp<GrRenderTargetContext> rtc(
+        context->makeDeferredRenderTargetContext(SkBackingFit::kExact, kImageWidth, kImageHeight,
+                                                 kRGBA_8888_GrPixelConfig, nullptr));
+    if (!rtc) {
+        ERRORF(reporter, "could not create render target context.");
+        return;
+    }
+
+    SkTArray<Box> boxes;
+    SkTArray<std::array<Box, 4>> vertexData;
+    SkBitmap gold;
+
+    // ---- setup ----------
+
+    SkPaint paint;
+    paint.setBlendMode(SkBlendMode::kSrc);
+    gold.allocN32Pixels(kImageWidth, kImageHeight);
+
+    SkCanvas goldCanvas(gold);
+
+    for (int y = 0; y < kBoxCountY; ++y) {
+        for (int x = 0; x < kBoxCountX; ++x) {
+            int c = y + x;
+            int rgb[3] = {-(c & 1) & 0xff, -((c >> 1) & 1) & 0xff, -((c >> 2) & 1) & 0xff};
+
+            const Box box = boxes.push_back() = {
+                float(x * kBoxSize),
+                float(y * kBoxSize),
+                GrColorPackRGBA(rgb[0], rgb[1], rgb[2], 255)
+            };
+
+            std::array<Box, 4>& boxVertices = vertexData.push_back();
+            for (int i = 0; i < 4; ++i) {
+                boxVertices[i] = {
+                    box.fX + (i/2) * kBoxSize,
+                    box.fY + (i%2) * kBoxSize,
+                    box.fColor
+                };
+            }
+
+            paint.setARGB(255, rgb[0], rgb[1], rgb[2]);
+            goldCanvas.drawRect(SkRect::MakeXYWH(box.fX, box.fY, kBoxSize, kBoxSize), paint);
+        }
+    }
+
+    goldCanvas.flush();
+
+    // ---- tests ----------
+
+#define VALIDATE(buff) \
+    if (!buff) { \
+        ERRORF(reporter, #buff " is null."); \
+        return; \
+    }
+
+    run_test("setNonIndexedNonInstanced", reporter, rtc, gold, [&](DrawMeshHelper* helper) {
+        SkTArray<Box> expandedVertexData;
+        for (int i = 0; i < kBoxCount; ++i) {
+            for (int j = 0; j < 6; ++j) {
+                expandedVertexData.push_back(vertexData[i][kIndexPattern[j]]);
+            }
+        }
+
+        // Draw boxes one line at a time to exercise base vertex.
+        auto vbuff = helper->makeVertexBuffer(expandedVertexData);
+        VALIDATE(vbuff);
+        for (int y = 0; y < kBoxCountY; ++y) {
+            GrMesh mesh(GrPrimitiveType::kTriangles);
+            mesh.setNonIndexedNonInstanced(kBoxCountX * 6);
+            mesh.setVertexData(vbuff.get(), y * kBoxCountX * 6);
+            helper->drawMesh(mesh);
+        }
+    });
+
+    run_test("setIndexed", reporter, rtc, gold, [&](DrawMeshHelper* helper) {
+        auto ibuff = helper->getIndexBuffer();
+        VALIDATE(ibuff);
+        auto vbuff = helper->makeVertexBuffer(vertexData);
+        VALIDATE(vbuff);
+        int baseRepetition = 0;
+        int i = 0;
+
+        // Start at various repetitions within the patterned index buffer to exercise base index.
+        while (i < kBoxCount) {
+            GR_STATIC_ASSERT(kIndexPatternRepeatCount >= 3);
+            int repetitionCount = SkTMin(3 - baseRepetition, kBoxCount - i);
+
+            GrMesh mesh(GrPrimitiveType::kTriangles);
+            mesh.setIndexed(ibuff.get(), repetitionCount * 6, baseRepetition * 6,
+                            baseRepetition * 4, (baseRepetition + repetitionCount) * 4 - 1);
+            mesh.setVertexData(vbuff.get(), (i - baseRepetition) * 4);
+            helper->drawMesh(mesh);
+
+            baseRepetition = (baseRepetition + 1) % 3;
+            i += repetitionCount;
+        }
+    });
+
+    run_test("setIndexedPatterned", reporter, rtc, gold, [&](DrawMeshHelper* helper) {
+        auto ibuff = helper->getIndexBuffer();
+        VALIDATE(ibuff);
+        auto vbuff = helper->makeVertexBuffer(vertexData);
+        VALIDATE(vbuff);
+
+        // Draw boxes one line at a time to exercise base vertex. setIndexedPatterned does not
+        // support a base index.
+        for (int y = 0; y < kBoxCountY; ++y) {
+            GrMesh mesh(GrPrimitiveType::kTriangles);
+            mesh.setIndexedPatterned(ibuff.get(), 6, 4, kBoxCountX, kIndexPatternRepeatCount);
+            mesh.setVertexData(vbuff.get(), y * kBoxCountX * 4);
+            helper->drawMesh(mesh);
+        }
+    });
+
+    for (bool indexed : {false, true}) {
+        if (!context->caps()->instanceAttribSupport()) {
+            break;
+        }
+
+        run_test(indexed ? "setIndexedInstanced" : "setInstanced",
+                 reporter, rtc, gold, [&](DrawMeshHelper* helper) {
+            auto idxbuff = indexed ? helper->getIndexBuffer() : nullptr;
+            auto instbuff = helper->makeVertexBuffer(boxes);
+            VALIDATE(instbuff);
+            auto vbuff = helper->makeVertexBuffer(std::vector<float>{0,0, 0,1, 1,0, 1,1});
+            VALIDATE(vbuff);
+            auto vbuff2 = helper->makeVertexBuffer( // for testing base vertex.
+                              std::vector<float>{-1,-1, -1,-1, 0,0, 0,1, 1,0, 1,1});
+            VALIDATE(vbuff2);
+
+            // Draw boxes one line at a time to exercise base instance, base vertex, and null vertex
+            // buffer. setIndexedInstanced intentionally does not support a base index.
+            for (int y = 0; y < kBoxCountY; ++y) {
+                GrMesh mesh(indexed ? GrPrimitiveType::kTriangles
+                                    : GrPrimitiveType::kTriangleStrip);
+                if (indexed) {
+                    VALIDATE(idxbuff);
+                    mesh.setIndexedInstanced(idxbuff.get(), 6,
+                                             instbuff.get(), kBoxCountX, y * kBoxCountX);
+                } else {
+                    mesh.setInstanced(instbuff.get(), kBoxCountX, y * kBoxCountX, 4);
+                }
+                switch (y % 3) {
+                    case 0:
+                        if (context->caps()->shaderCaps()->vertexIDSupport()) {
+                            if (y % 2) {
+                                // We don't need this call because it's the initial state of GrMesh.
+                                mesh.setVertexData(nullptr);
+                            }
+                            break;
+                        }
+                        // Fallthru.
+                    case 1:
+                        mesh.setVertexData(vbuff.get());
+                        break;
+                    case 2:
+                        mesh.setVertexData(vbuff2.get(), 2);
+                        break;
+                }
+                helper->drawMesh(mesh);
+            }
+        });
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class GrMeshTestOp : public GrDrawOp {
+public:
+    DEFINE_OP_CLASS_ID
+
+    GrMeshTestOp(std::function<void(DrawMeshHelper*)> testFn)
+        : INHERITED(ClassID())
+        , fTestFn(testFn) {
+        this->setBounds(SkRect::MakeIWH(kImageWidth, kImageHeight),
+                        HasAABloat::kNo, IsZeroArea::kNo);
+    }
+
+private:
+    const char* name() const override { return "GrMeshTestOp"; }
+    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
+    RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*) override {
+        return RequiresDstTexture::kNo;
+    }
+    bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
+    void onPrepare(GrOpFlushState*) override {}
+    void onExecute(GrOpFlushState* state) override {
+        DrawMeshHelper helper(state);
+        fTestFn(&helper);
+    }
+
+    std::function<void(DrawMeshHelper*)> fTestFn;
+
+    typedef GrDrawOp INHERITED;
+};
+
+class GrMeshTestProcessor : public GrGeometryProcessor {
+public:
+    GrMeshTestProcessor(bool instanced, bool hasVertexBuffer)
+        : fInstanceLocation(nullptr)
+        , fVertex(nullptr)
+        , fColor(nullptr) {
+        if (instanced) {
+            fInstanceLocation = &this->addInstanceAttrib("location", kVec2f_GrVertexAttribType);
+            if (hasVertexBuffer) {
+                fVertex = &this->addVertexAttrib("vertex", kVec2f_GrVertexAttribType);
+            }
+            fColor = &this->addInstanceAttrib("color", kVec4ub_GrVertexAttribType);
+        } else {
+            fVertex = &this->addVertexAttrib("vertex", kVec2f_GrVertexAttribType);
+            fColor = &this->addVertexAttrib("color", kVec4ub_GrVertexAttribType);
+        }
+        this->initClassID<GrMeshTestProcessor>();
+    }
+
+    const char* name() const override { return "GrMeshTest Processor"; }
+
+    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
+        b->add32(SkToBool(fInstanceLocation));
+        b->add32(SkToBool(fVertex));
+    }
+
+    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
+
+protected:
+    const Attribute* fInstanceLocation;
+    const Attribute* fVertex;
+    const Attribute* fColor;
+
+    friend class GLSLMeshTestProcessor;
+    typedef GrGeometryProcessor INHERITED;
+};
+
+class GLSLMeshTestProcessor : public GrGLSLGeometryProcessor {
+    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
+                 FPCoordTransformIter&& transformIter) final {}
+
+    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
+        const GrMeshTestProcessor& mp = args.fGP.cast<GrMeshTestProcessor>();
+
+        GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
+        varyingHandler->emitAttributes(mp);
+        varyingHandler->addPassThroughAttribute(mp.fColor, args.fOutputColor);
+
+        GrGLSLVertexBuilder* v = args.fVertBuilder;
+        if (!mp.fInstanceLocation) {
+            v->codeAppendf("vec2 vertex = %s;", mp.fVertex->fName);
+        } else {
+            if (mp.fVertex) {
+                v->codeAppendf("vec2 offset = %s;", mp.fVertex->fName);
+            } else {
+                v->codeAppend ("vec2 offset = vec2(sk_VertexID / 2, sk_VertexID % 2);");
+            }
+            v->codeAppendf("vec2 vertex = %s + offset * %i;",
+                           mp.fInstanceLocation->fName, kBoxSize);
+        }
+        gpArgs->fPositionVar.set(kVec2f_GrSLType, "vertex");
+
+        GrGLSLPPFragmentBuilder* f = args.fFragBuilder;
+        f->codeAppendf("%s = vec4(1);", args.fOutputCoverage);
+    }
+};
+
+GrGLSLPrimitiveProcessor* GrMeshTestProcessor::createGLSLInstance(const GrShaderCaps&) const {
+    return new GLSLMeshTestProcessor;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template<typename T>
+sk_sp<const GrBuffer> DrawMeshHelper::makeVertexBuffer(const T* data, int count) {
+    return sk_sp<const GrBuffer>(
+        fState->resourceProvider()->createBuffer(
+            count * sizeof(T), kVertex_GrBufferType, kDynamic_GrAccessPattern,
+            GrResourceProvider::kNoPendingIO_Flag |
+            GrResourceProvider::kRequireGpuMemory_Flag, data));
+}
+
+sk_sp<const GrBuffer> DrawMeshHelper::getIndexBuffer() {
+    GR_DEFINE_STATIC_UNIQUE_KEY(gIndexBufferKey);
+    return sk_sp<const GrBuffer>(
+        fState->resourceProvider()->findOrCreatePatternedIndexBuffer(
+            kIndexPattern, 6, kIndexPatternRepeatCount, 4, gIndexBufferKey));
+}
+
+void DrawMeshHelper::drawMesh(const GrMesh& mesh) {
+    GrRenderTarget* rt = fState->drawOpArgs().fRenderTarget;
+    GrPipeline pipeline(rt, GrPipeline::ScissorState::kDisabled, SkBlendMode::kSrc);
+    GrMeshTestProcessor mtp(mesh.isInstanced(), mesh.hasVertexData());
+    fState->commandBuffer()->draw(pipeline, mtp, &mesh, nullptr, 1,
+                                  SkRect::MakeIWH(kImageWidth, kImageHeight));
+}
+
+static void run_test(const char* testName, skiatest::Reporter* reporter,
+                     const sk_sp<GrRenderTargetContext>& rtc, const SkBitmap& gold,
+                     std::function<void(DrawMeshHelper*)> testFn) {
+    const int w = gold.width(), h = gold.height(), rowBytes = gold.rowBytes();
+    const uint32_t* goldPx = reinterpret_cast<const uint32_t*>(gold.getPixels());
+    if (h != rtc->height() || w != rtc->width()) {
+        ERRORF(reporter, "[%s] expectation and rtc not compatible (?).", testName);
+        return;
+    }
+    if (sizeof(uint32_t) * kImageWidth != gold.rowBytes()) {
+        ERRORF(reporter, "unexpected row bytes in gold image.", testName);
+        return;
+    }
+
+    SkAutoSTMalloc<kImageHeight * kImageWidth, uint32_t> resultPx(h * rowBytes);
+    rtc->clear(nullptr, 0xbaaaaaad, true);
+    rtc->priv().testingOnly_addDrawOp(skstd::make_unique<GrMeshTestOp>(testFn));
+    rtc->readPixels(gold.info(), resultPx, rowBytes, 0, 0, 0);
+    for (int y = 0; y < h; ++y) {
+        for (int x = 0; x < w; ++x) {
+            uint32_t expected = goldPx[y * kImageWidth + x];
+            uint32_t actual = resultPx[y * kImageWidth + x];
+            if (expected != actual) {
+                ERRORF(reporter, "[%s] pixel (%i,%i): got 0x%x expected 0x%x",
+                       testName, x, y, actual, expected);
+                return;
+            }
+        }
+    }
+}
+
+#endif
diff --git a/src/third_party/skia/tests/GrOrderedSetTest.cpp b/src/third_party/skia/tests/GrOrderedSetTest.cpp
deleted file mode 100644
index 7b3db9d..0000000
--- a/src/third_party/skia/tests/GrOrderedSetTest.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkRandom.h"
-#include "Test.h"
-// This is a GPU-backend specific test
-#if SK_SUPPORT_GPU
-#include "GrOrderedSet.h"
-
-typedef GrOrderedSet<int> Set;
-typedef GrOrderedSet<const char*, GrStrLess> Set2;
-
-DEF_TEST(GrOrderedSet, reporter) {
-    Set set;
-
-    REPORTER_ASSERT(reporter, set.empty());
-
-    SkRandom r;
-
-    int count[1000] = {0};
-    // add 10K ints
-    for (int i = 0; i < 10000; ++i) {
-        int x = r.nextU() % 1000;
-        Set::Iter xi = set.insert(x);
-        REPORTER_ASSERT(reporter, *xi == x);
-        REPORTER_ASSERT(reporter, !set.empty());
-        count[x] = 1;
-    }
-    set.insert(0);
-    count[0] = 1;
-    set.insert(999);
-    count[999] = 1;
-    int totalCount = 0;
-    for (int i = 0; i < 1000; ++i) {
-        totalCount += count[i];
-    }
-    REPORTER_ASSERT(reporter, *set.begin() == 0);
-    REPORTER_ASSERT(reporter, *set.last() == 999);
-    REPORTER_ASSERT(reporter, --(++set.begin()) == set.begin());
-    REPORTER_ASSERT(reporter, --set.end() == set.last());
-    REPORTER_ASSERT(reporter, set.count() == totalCount);
-
-    int c = 0;
-    // check that we iterate through the correct number of
-    // elements and they are properly sorted.
-    for (Set::Iter a = set.begin(); set.end() != a; ++a) {
-        Set::Iter b = a;
-        ++b;
-        ++c;
-        REPORTER_ASSERT(reporter, b == set.end() || *a <= *b);
-    }
-    REPORTER_ASSERT(reporter, c == set.count());
-
-    // check that the set finds all ints and only ints added to set
-    for (int i = 0; i < 1000; ++i) {
-        bool existsFind = set.find(i) != set.end();
-        bool existsCount = 0 != count[i];
-        REPORTER_ASSERT(reporter, existsFind == existsCount);
-    }
-    // remove all the ints between 100 and 200.
-    for (int i = 100; i < 200; ++i) {
-        set.remove(set.find(i));
-        if (1 == count[i]) {
-            count[i] = 0;
-            --totalCount;
-        }
-        REPORTER_ASSERT(reporter, set.count() == totalCount);
-        REPORTER_ASSERT(reporter, set.find(i) == set.end());
-    }
-    // remove the 0 entry. (tests removing begin())
-    REPORTER_ASSERT(reporter, *set.begin() == 0);
-    REPORTER_ASSERT(reporter, *(--set.end()) == 999);
-    set.remove(set.find(0));
-    count[0] = 0;
-    --totalCount;
-    REPORTER_ASSERT(reporter, set.count() == totalCount);
-    REPORTER_ASSERT(reporter, set.find(0) == set.end());
-    REPORTER_ASSERT(reporter, 0 < *set.begin());
-
-    // remove all the 999 entries (tests removing last()).
-    set.remove(set.find(999));
-    count[999] = 0;
-    --totalCount;
-    REPORTER_ASSERT(reporter, set.count() == totalCount);
-    REPORTER_ASSERT(reporter, set.find(999) == set.end());
-    REPORTER_ASSERT(reporter, 999 > *(--set.end()));
-    REPORTER_ASSERT(reporter, set.last() == --set.end());
-
-    // Make sure iteration still goes through correct number of entries
-    // and is still sorted correctly.
-    c = 0;
-    for (Set::Iter a = set.begin(); set.end() != a; ++a) {
-        Set::Iter b = a;
-        ++b;
-        ++c;
-        REPORTER_ASSERT(reporter, b == set.end() || *a <= *b);
-    }
-    REPORTER_ASSERT(reporter, c == set.count());
-
-    // repeat check that the set finds all ints and only ints added to set
-    for (int i = 0; i < 1000; ++i) {
-        bool existsFind = set.find(i) != set.end();
-        bool existsCount = 0 != count[i];
-        REPORTER_ASSERT(reporter, existsFind == existsCount);
-    }
-
-    // remove all entries
-    while (!set.empty()) {
-        set.remove(set.begin());
-    }
-
-    // test reset on empty set.
-    set.reset();
-    REPORTER_ASSERT(reporter, set.empty());
-
-
-    // test using c strings
-    const char* char1 = "dog";
-    const char* char2 = "cat";
-    const char* char3 = "dog";
-
-    Set2 set2;
-
-    set2.insert("ape");
-    set2.insert(char1);
-    set2.insert(char2);
-    set2.insert(char3);
-    set2.insert("ant");
-    set2.insert("cat");
-
-    REPORTER_ASSERT(reporter, set2.count() == 4);
-    REPORTER_ASSERT(reporter, set2.find("dog") == set2.last());
-    REPORTER_ASSERT(reporter, set2.find("cat") != set2.end());
-    REPORTER_ASSERT(reporter, set2.find("ant") == set2.begin());
-    REPORTER_ASSERT(reporter, set2.find("bug") == set2.end());
-
-    set2.remove(set2.find("ant"));
-    REPORTER_ASSERT(reporter, set2.find("ant") == set2.end());
-    REPORTER_ASSERT(reporter, set2.count() == 3);
-
-    set2.reset();
-    REPORTER_ASSERT(reporter, set2.empty());
-}
-
-#endif
diff --git a/src/third_party/skia/tests/GrPipelineDynamicStateTest.cpp b/src/third_party/skia/tests/GrPipelineDynamicStateTest.cpp
new file mode 100644
index 0000000..007702a
--- /dev/null
+++ b/src/third_party/skia/tests/GrPipelineDynamicStateTest.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrColor.h"
+#include "GrGeometryProcessor.h"
+#include "GrGpuCommandBuffer.h"
+#include "GrOpFlushState.h"
+#include "GrRenderTargetContext.h"
+#include "GrRenderTargetContextPriv.h"
+#include "GrResourceProvider.h"
+#include "SkMakeUnique.h"
+#include "glsl/GrGLSLVertexShaderBuilder.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLGeometryProcessor.h"
+#include "glsl/GrGLSLVarying.h"
+
+/**
+ * This is a GPU-backend specific test for dynamic pipeline state. It draws boxes using dynamic
+ * scissor rectangles then reads back the result to verify a successful test.
+ */
+
+using ScissorState = GrPipeline::ScissorState;
+
+static constexpr int kScreenSize = 6;
+static constexpr int kNumMeshes = 4;
+static constexpr int kScreenSplitX = kScreenSize/2;
+static constexpr int kScreenSplitY = kScreenSize/2;
+
+static const GrPipeline::DynamicState kDynamicStates[kNumMeshes] = {
+    {SkIRect::MakeLTRB(0,              0,              kScreenSplitX,  kScreenSplitY)},
+    {SkIRect::MakeLTRB(0,              kScreenSplitY,  kScreenSplitX,  kScreenSize)},
+    {SkIRect::MakeLTRB(kScreenSplitX,  0,              kScreenSize,    kScreenSplitY)},
+    {SkIRect::MakeLTRB(kScreenSplitX,  kScreenSplitY,  kScreenSize,    kScreenSize)},
+};
+
+static const GrColor kMeshColors[kNumMeshes] {
+    GrColorPackRGBA(255, 0, 0, 255),
+    GrColorPackRGBA(0, 255, 0, 255),
+    GrColorPackRGBA(0, 0, 255, 255),
+    GrColorPackRGBA(0, 0, 0, 255)
+};
+
+struct Vertex {
+    float     fX;
+    float     fY;
+    GrColor   fColor;
+};
+
+class GrPipelineDynamicStateTestProcessor : public GrGeometryProcessor {
+public:
+    GrPipelineDynamicStateTestProcessor()
+        : fVertex(this->addVertexAttrib("vertex", kVec2f_GrVertexAttribType))
+        , fColor(this->addVertexAttrib("color", kVec4ub_GrVertexAttribType)) {
+        this->initClassID<GrPipelineDynamicStateTestProcessor>();
+    }
+
+    const char* name() const override { return "GrPipelineDynamicStateTest Processor"; }
+
+    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
+
+    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
+
+protected:
+    const Attribute& fVertex;
+    const Attribute& fColor;
+
+    friend class GLSLPipelineDynamicStateTestProcessor;
+    typedef GrGeometryProcessor INHERITED;
+};
+
+class GLSLPipelineDynamicStateTestProcessor : public GrGLSLGeometryProcessor {
+    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
+                 FPCoordTransformIter&& transformIter) final {}
+
+    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
+        const GrPipelineDynamicStateTestProcessor& mp =
+            args.fGP.cast<GrPipelineDynamicStateTestProcessor>();
+
+        GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
+        varyingHandler->emitAttributes(mp);
+        varyingHandler->addPassThroughAttribute(&mp.fColor, args.fOutputColor);
+
+        GrGLSLVertexBuilder* v = args.fVertBuilder;
+        v->codeAppendf("vec2 vertex = %s;", mp.fVertex.fName);
+        gpArgs->fPositionVar.set(kVec2f_GrSLType, "vertex");
+
+        GrGLSLPPFragmentBuilder* f = args.fFragBuilder;
+        f->codeAppendf("%s = vec4(1);", args.fOutputCoverage);
+    }
+};
+
+GrGLSLPrimitiveProcessor*
+GrPipelineDynamicStateTestProcessor::createGLSLInstance(const GrShaderCaps&) const {
+    return new GLSLPipelineDynamicStateTestProcessor;
+}
+
+class GrPipelineDynamicStateTestOp : public GrDrawOp {
+public:
+    DEFINE_OP_CLASS_ID
+
+    GrPipelineDynamicStateTestOp(ScissorState scissorState, sk_sp<const GrBuffer> vbuff)
+        : INHERITED(ClassID())
+        , fScissorState(scissorState)
+        , fVertexBuffer(std::move(vbuff)) {
+        this->setBounds(SkRect::MakeIWH(kScreenSize, kScreenSize),
+                        HasAABloat::kNo, IsZeroArea::kNo);
+    }
+
+private:
+    const char* name() const override { return "GrPipelineDynamicStateTestOp"; }
+    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
+    RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*) override {
+        return RequiresDstTexture::kNo;
+    }
+    bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
+    void onPrepare(GrOpFlushState*) override {}
+    void onExecute(GrOpFlushState* state) override {
+        GrRenderTarget* rt = state->drawOpArgs().fRenderTarget;
+        GrPipeline pipeline(rt, fScissorState, SkBlendMode::kSrc);
+        SkSTArray<kNumMeshes, GrMesh> meshes;
+        for (int i = 0; i < kNumMeshes; ++i) {
+            GrMesh& mesh = meshes.emplace_back(GrPrimitiveType::kTriangleStrip);
+            mesh.setNonIndexedNonInstanced(4);
+            mesh.setVertexData(fVertexBuffer.get(), 4 * i);
+        }
+        state->commandBuffer()->draw(pipeline, GrPipelineDynamicStateTestProcessor(),
+                                     meshes.begin(), kDynamicStates, 4,
+                                     SkRect::MakeIWH(kScreenSize, kScreenSize));
+    }
+
+    ScissorState                fScissorState;
+    const sk_sp<const GrBuffer> fVertexBuffer;
+
+    typedef GrDrawOp INHERITED;
+};
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrPipelineDynamicStateTest, reporter, ctxInfo) {
+    GrContext* const context = ctxInfo.grContext();
+    GrResourceProvider* rp = context->resourceProvider();
+
+    sk_sp<GrRenderTargetContext> rtc(
+        context->makeDeferredRenderTargetContext(SkBackingFit::kExact, kScreenSize, kScreenSize,
+                                                 kRGBA_8888_GrPixelConfig, nullptr));
+    if (!rtc) {
+        ERRORF(reporter, "could not create render target context.");
+        return;
+    }
+
+    constexpr float d = (float) kScreenSize;
+    Vertex vdata[kNumMeshes * 4] = {
+        {0, 0, kMeshColors[0]},
+        {0, d, kMeshColors[0]},
+        {d, 0, kMeshColors[0]},
+        {d, d, kMeshColors[0]},
+
+        {0, 0, kMeshColors[1]},
+        {0, d, kMeshColors[1]},
+        {d, 0, kMeshColors[1]},
+        {d, d, kMeshColors[1]},
+
+        {0, 0, kMeshColors[2]},
+        {0, d, kMeshColors[2]},
+        {d, 0, kMeshColors[2]},
+        {d, d, kMeshColors[2]},
+
+        {0, 0, kMeshColors[3]},
+        {0, d, kMeshColors[3]},
+        {d, 0, kMeshColors[3]},
+        {d, d, kMeshColors[3]}
+    };
+
+    sk_sp<const GrBuffer> vbuff(rp->createBuffer(sizeof(vdata), kVertex_GrBufferType,
+                                                 kDynamic_GrAccessPattern,
+                                                 GrResourceProvider::kNoPendingIO_Flag |
+                                                 GrResourceProvider::kRequireGpuMemory_Flag,
+                                                 vdata));
+    if (!vbuff) {
+        ERRORF(reporter, "vbuff is null.");
+        return;
+    }
+
+    uint32_t resultPx[kScreenSize * kScreenSize];
+
+    for (ScissorState scissorState : {ScissorState::kEnabled, ScissorState::kDisabled}) {
+        rtc->clear(nullptr, 0xbaaaaaad, true);
+        rtc->priv().testingOnly_addDrawOp(
+            skstd::make_unique<GrPipelineDynamicStateTestOp>(scissorState, vbuff));
+        rtc->readPixels(SkImageInfo::Make(kScreenSize, kScreenSize,
+                                          kRGBA_8888_SkColorType, kPremul_SkAlphaType),
+                        resultPx, 4 * kScreenSize, 0, 0, 0);
+        for (int y = 0; y < kScreenSize; ++y) {
+            for (int x = 0; x < kScreenSize; ++x) {
+                int expectedColorIdx;
+                if (ScissorState::kEnabled == scissorState) {
+                    expectedColorIdx = (x < kScreenSplitX ? 0 : 2) + (y < kScreenSplitY ? 0 : 1);
+                } else {
+                    expectedColorIdx = kNumMeshes - 1;
+                }
+                uint32_t expected = kMeshColors[expectedColorIdx];
+                uint32_t actual = resultPx[y * kScreenSize + x];
+                if (expected != actual) {
+                    ERRORF(reporter, "[scissor=%s] pixel (%i,%i): got 0x%x expected 0x%x",
+                           ScissorState::kEnabled == scissorState ? "enabled" : "disabled", x, y,
+                           actual, expected);
+                    return;
+                }
+            }
+        }
+    }
+}
+
+#endif
diff --git a/src/third_party/skia/tests/GrPorterDuffTest.cpp b/src/third_party/skia/tests/GrPorterDuffTest.cpp
new file mode 100644
index 0000000..37972d9
--- /dev/null
+++ b/src/third_party/skia/tests/GrPorterDuffTest.cpp
@@ -0,0 +1,1102 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContextFactory.h"
+#include "GrContextOptions.h"
+#include "GrContextPriv.h"
+#include "GrGpu.h"
+#include "GrTest.h"
+#include "GrXferProcessor.h"
+#include "effects/GrPorterDuffXferProcessor.h"
+#include "gl/GrGLCaps.h"
+#include "ops/GrMeshDrawOp.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+static void test_color_unknown_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
+static void test_color_not_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
+static void test_color_opaque_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
+static void test_color_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
+static void test_lcd_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
+static void test_lcd_coverage_fallback_case(skiatest::Reporter* reporter, const GrCaps& caps);
+
+DEF_GPUTEST_FOR_NULLGL_CONTEXT(GrPorterDuff, reporter, ctxInfo) {
+    const GrCaps& caps = *ctxInfo.grContext()->getGpu()->caps();
+    if (!caps.shaderCaps()->dualSourceBlendingSupport()) {
+        SkFAIL("Null context does not support dual source blending.");
+        return;
+    }
+
+    test_color_unknown_with_coverage(reporter, caps);
+    test_color_not_opaque_no_coverage(reporter, caps);
+    test_color_opaque_with_coverage(reporter, caps);
+    test_color_opaque_no_coverage(reporter, caps);
+    test_lcd_coverage(reporter, caps);
+    test_lcd_coverage_fallback_case(reporter, caps);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define TEST_ASSERT(...) REPORTER_ASSERT(reporter, __VA_ARGS__)
+
+enum {
+    kNone_OutputType,
+    kCoverage_OutputType,
+    kModulate_OutputType,
+    kSAModulate_OutputType,
+    kISAModulate_OutputType,
+    kISCModulate_OutputType
+};
+static const int kInvalid_OutputType = -1;
+
+static GrProcessorSet::Analysis do_analysis(const GrXPFactory* xpf,
+                                            const GrProcessorAnalysisColor& colorInput,
+                                            GrProcessorAnalysisCoverage coverageInput,
+                                            const GrCaps& caps) {
+    GrPaint paint;
+    paint.setXPFactory(xpf);
+    GrProcessorSet procs(std::move(paint));
+    GrColor overrideColor;
+    GrProcessorSet::Analysis analysis =
+            procs.finalize(colorInput, coverageInput, nullptr, false, caps, &overrideColor);
+    return analysis;
+}
+
+class GrPorterDuffTest {
+public:
+    struct XPInfo {
+        XPInfo(skiatest::Reporter* reporter, SkBlendMode xfermode, const GrCaps& caps,
+               GrProcessorAnalysisColor inputColor, GrProcessorAnalysisCoverage inputCoverage) {
+            const GrXPFactory* xpf = GrPorterDuffXPFactory::Get(xfermode);
+
+            bool isLCD = GrProcessorAnalysisCoverage::kLCD == inputCoverage;
+
+            GrProcessorSet::Analysis analysis = do_analysis(xpf, inputColor, inputCoverage, caps);
+            fCompatibleWithCoverageAsAlpha = analysis.isCompatibleWithCoverageAsAlpha();
+            fCanCombineOverlappedStencilAndCover = analysis.canCombineOverlappedStencilAndCover();
+            fIgnoresInputColor = analysis.inputColorIsIgnored();
+            sk_sp<const GrXferProcessor> xp(
+                    GrXPFactory::MakeXferProcessor(xpf, inputColor, inputCoverage, false, caps));
+            TEST_ASSERT(!analysis.requiresDstTexture() ||
+                        (isLCD &&
+                         !caps.shaderCaps()->dstReadInShaderSupport() &&
+                         (SkBlendMode::kSrcOver != xfermode ||
+                          !inputColor.isOpaque())));
+            GetXPOutputTypes(xp.get(), &fPrimaryOutputType, &fSecondaryOutputType);
+            xp->getBlendInfo(&fBlendInfo);
+            TEST_ASSERT(!xp->willReadDstColor() ||
+                        (isLCD && (SkBlendMode::kSrcOver != xfermode ||
+                                   !inputColor.isOpaque())));
+            TEST_ASSERT(xp->hasSecondaryOutput() == GrBlendCoeffRefsSrc2(fBlendInfo.fDstBlend));
+        }
+
+        bool fCanCombineOverlappedStencilAndCover;
+        bool fCompatibleWithCoverageAsAlpha;
+        bool fIgnoresInputColor;
+        int fPrimaryOutputType;
+        int fSecondaryOutputType;
+        GrXferProcessor::BlendInfo fBlendInfo;
+    };
+
+    static void GetXPOutputTypes(const GrXferProcessor* xp, int* outPrimary, int* outSecondary) {
+        GrPorterDuffXPFactory::TestGetXPOutputTypes(xp, outPrimary, outSecondary);
+    }
+};
+
+static void test_lcd_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
+    GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kYes;
+    GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kLCD;
+
+    for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
+        SkBlendMode xfermode = static_cast<SkBlendMode>(m);
+        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage);
+        switch (xfermode) {
+            case SkBlendMode::kClear:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrc:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDst:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcOver:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kSAModulate_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kIS2C_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstOver:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcIn:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstIn:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcOut:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstOut:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcATop:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstATop:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kXor:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kPlus:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kModulate:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kScreen:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kInvalid_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            default:
+                ERRORF(reporter, "Invalid xfermode.");
+                break;
+        }
+    }
+}
+static void test_color_unknown_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
+    GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kNo;
+    GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kSingleChannel;
+
+    for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
+        SkBlendMode xfermode = static_cast<SkBlendMode>(m);
+        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage);
+        switch (xfermode) {
+            case SkBlendMode::kClear:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kCoverage_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrc:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kIS2A_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDst:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcOver:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstOver:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcIn:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kIS2A_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstIn:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kISAModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcOut:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kIS2A_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstOut:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcATop:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstATop:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kISAModulate_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kIS2C_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kXor:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kPlus:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kModulate:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kISCModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kScreen:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            default:
+                ERRORF(reporter, "Invalid xfermode.");
+                break;
+        }
+    }
+}
+
+static void test_color_not_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
+    GrProcessorAnalysisColor inputColor(GrColorPackRGBA(229, 0, 154, 240));
+    GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kNone;
+
+    for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
+        SkBlendMode xfermode = static_cast<SkBlendMode>(m);
+        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage);
+        switch (xfermode) {
+            case SkBlendMode::kClear:
+                TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrc:
+                TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDst:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcOver:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstOver:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcIn:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstIn:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kSA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcOut:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstOut:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcATop:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstATop:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kSA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kXor:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kPlus:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kModulate:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kSC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kScreen:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            default:
+                ERRORF(reporter, "Invalid xfermode.");
+                break;
+        }
+    }
+}
+
+static void test_color_opaque_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
+    GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kYes;
+    GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kSingleChannel;
+
+    for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
+        SkBlendMode xfermode = static_cast<SkBlendMode>(m);
+        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage);
+        switch (xfermode) {
+            case SkBlendMode::kClear:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kCoverage_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrc:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDst:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcOver:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstOver:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcIn:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstIn:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcOut:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstOut:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kCoverage_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcATop:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstATop:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kXor:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kPlus:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kModulate:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(kISCModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kScreen:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            default:
+                ERRORF(reporter, "Invalid xfermode.");
+                break;
+        }
+    }
+}
+
+static void test_color_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
+    GrProcessorAnalysisColor inputColor = GrProcessorAnalysisColor::Opaque::kYes;
+    GrProcessorAnalysisCoverage inputCoverage = GrProcessorAnalysisCoverage::kNone;
+
+    for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
+        SkBlendMode xfermode = static_cast<SkBlendMode>(m);
+        const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, inputColor, inputCoverage);
+
+        switch (xfermode) {
+            case SkBlendMode::kClear:
+                TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrc:
+                TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDst:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcOver:
+                // We don't specialize opaque src-over. See note in GrPorterDuffXferProcessor.cpp
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstOver:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcIn:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstIn:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcOut:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstOut:
+                TEST_ASSERT(xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kSrcATop:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kDstATop:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kXor:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kPlus:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kModulate:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(!xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kSC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            case SkBlendMode::kScreen:
+                TEST_ASSERT(!xpi.fCanCombineOverlappedStencilAndCover);
+                TEST_ASSERT(!xpi.fIgnoresInputColor);
+                TEST_ASSERT(xpi.fCompatibleWithCoverageAsAlpha);
+                TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+                TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+                TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+                TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+                TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+                TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+                break;
+            default:
+                ERRORF(reporter, "Invalid xfermode.");
+                break;
+        }
+    }
+}
+
+static void test_lcd_coverage_fallback_case(skiatest::Reporter* reporter, const GrCaps& caps) {
+    const GrXPFactory* xpf = GrPorterDuffXPFactory::Get(SkBlendMode::kSrcOver);
+    GrProcessorAnalysisColor color = GrColorPackRGBA(123, 45, 67, 255);
+    GrProcessorAnalysisCoverage coverage = GrProcessorAnalysisCoverage::kLCD;
+    TEST_ASSERT(!(GrXPFactory::GetAnalysisProperties(xpf, color, coverage, caps) &
+                  GrXPFactory::AnalysisProperties::kRequiresDstTexture));
+    sk_sp<const GrXferProcessor> xp_opaque(
+            GrXPFactory::MakeXferProcessor(xpf, color, coverage, false, caps));
+    if (!xp_opaque) {
+        ERRORF(reporter, "Failed to create an XP with LCD coverage.");
+        return;
+    }
+
+    GrXferProcessor::BlendInfo blendInfo;
+    xp_opaque->getBlendInfo(&blendInfo);
+    TEST_ASSERT(blendInfo.fWriteColor);
+
+    // Test with non-opaque alpha
+    color = GrColorPackRGBA(123, 45, 67, 221);
+    coverage = GrProcessorAnalysisCoverage::kLCD;
+    TEST_ASSERT(!(GrXPFactory::GetAnalysisProperties(xpf, color, coverage, caps) &
+                GrXPFactory::AnalysisProperties::kRequiresDstTexture));
+    sk_sp<const GrXferProcessor> xp(
+            GrXPFactory::MakeXferProcessor(xpf, color, coverage, false, caps));
+    if (!xp) {
+        ERRORF(reporter, "Failed to create an XP with LCD coverage.");
+        return;
+    }
+
+    xp->getBlendInfo(&blendInfo);
+    TEST_ASSERT(blendInfo.fWriteColor);
+}
+
+DEF_GPUTEST(PorterDuffNoDualSourceBlending, reporter, /*factory*/) {
+    GrContextOptions opts;
+    opts.fSuppressDualSourceBlending = true;
+    sk_gpu_test::GrContextFactory mockFactory(opts);
+    GrContext* ctx = mockFactory.get(sk_gpu_test::GrContextFactory::kNullGL_ContextType);
+    if (!ctx) {
+        SkFAIL("Failed to create null context without ARB_blend_func_extended.");
+        return;
+    }
+
+    const GrCaps& caps = *ctx->caps();
+    if (caps.shaderCaps()->dualSourceBlendingSupport()) {
+        SkFAIL("Null context failed to honor request for no ARB_blend_func_extended.");
+        return;
+    }
+
+    GrBackendObject backendTexHandle =
+        ctx->getGpu()->createTestingOnlyBackendTexture(nullptr, 100, 100, kRGBA_8888_GrPixelConfig);
+    GrBackendTexture backendTex = GrTest::CreateBackendTexture(ctx->contextPriv().getBackend(),
+                                                               100,
+                                                               100,
+                                                               kRGBA_8888_GrPixelConfig,
+                                                               backendTexHandle);
+
+    GrXferProcessor::DstProxy fakeDstProxy;
+    {
+        sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeWrappedBackend(ctx, backendTex,
+                                                                         kTopLeft_GrSurfaceOrigin);
+        fakeDstProxy.setProxy(std::move(proxy));
+    }
+
+    static const GrProcessorAnalysisColor colorInputs[] = {
+            GrProcessorAnalysisColor::Opaque::kNo, GrProcessorAnalysisColor::Opaque::kYes,
+            GrProcessorAnalysisColor(GrColorPackRGBA(0, 82, 17, 100)),
+            GrProcessorAnalysisColor(GrColorPackRGBA(0, 82, 17, 255))};
+
+    for (const auto& colorInput : colorInputs) {
+        for (GrProcessorAnalysisCoverage coverageType :
+             {GrProcessorAnalysisCoverage::kSingleChannel, GrProcessorAnalysisCoverage::kNone}) {
+            for (int m = 0; m <= (int)SkBlendMode::kLastCoeffMode; m++) {
+                SkBlendMode xfermode = static_cast<SkBlendMode>(m);
+                const GrXPFactory* xpf = GrPorterDuffXPFactory::Get(xfermode);
+                sk_sp<const GrXferProcessor> xp(
+                        GrXPFactory::MakeXferProcessor(xpf, colorInput, coverageType, false, caps));
+                if (!xp) {
+                    ERRORF(reporter, "Failed to create an XP without dual source blending.");
+                    return;
+                }
+                TEST_ASSERT(!xp->hasSecondaryOutput());
+            }
+        }
+    }
+    ctx->getGpu()->deleteTestingOnlyBackendTexture(backendTexHandle);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/GrRedBlackTreeTest.cpp b/src/third_party/skia/tests/GrRedBlackTreeTest.cpp
deleted file mode 100644
index c517cf2..0000000
--- a/src/third_party/skia/tests/GrRedBlackTreeTest.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-// This is a GPU-backend specific test
-#if SK_SUPPORT_GPU
-
-#include "GrRedBlackTree.h"
-#include "SkRandom.h"
-#include "Test.h"
-
-typedef GrRedBlackTree<int> Tree;
-
-DEF_TEST(GrRedBlackTree, reporter) {
-    Tree tree;
-
-    SkRandom r;
-
-    int count[100] = {0};
-    // add 10K ints
-    for (int i = 0; i < 10000; ++i) {
-        int x = r.nextU() % 100;
-        Tree::Iter xi = tree.insert(x);
-        REPORTER_ASSERT(reporter, *xi == x);
-        ++count[x];
-    }
-
-    tree.insert(0);
-    ++count[0];
-    tree.insert(99);
-    ++count[99];
-    REPORTER_ASSERT(reporter, *tree.begin() == 0);
-    REPORTER_ASSERT(reporter, *tree.last() == 99);
-    REPORTER_ASSERT(reporter, --(++tree.begin()) == tree.begin());
-    REPORTER_ASSERT(reporter, --tree.end() == tree.last());
-    REPORTER_ASSERT(reporter, tree.count() == 10002);
-
-    int c = 0;
-    // check that we iterate through the correct number of
-    // elements and they are properly sorted.
-    for (Tree::Iter a = tree.begin(); tree.end() != a; ++a) {
-        Tree::Iter b = a;
-        ++b;
-        ++c;
-        REPORTER_ASSERT(reporter, b == tree.end() || *a <= *b);
-    }
-    REPORTER_ASSERT(reporter, c == tree.count());
-
-    // check that the tree reports the correct number of each int
-    // and that we can iterate through them correctly both forward
-    // and backward.
-    for (int i = 0; i < 100; ++i) {
-        int c;
-        c = tree.countOf(i);
-        REPORTER_ASSERT(reporter, c == count[i]);
-        c = 0;
-        Tree::Iter iter = tree.findFirst(i);
-        while (iter != tree.end() && *iter == i) {
-            ++c;
-            ++iter;
-        }
-        REPORTER_ASSERT(reporter, count[i] == c);
-        c = 0;
-        iter = tree.findLast(i);
-        if (iter != tree.end()) {
-            do {
-                if (*iter == i) {
-                    ++c;
-                } else {
-                    break;
-                }
-                if (iter != tree.begin()) {
-                    --iter;
-                } else {
-                    break;
-                }
-            } while (true);
-        }
-        REPORTER_ASSERT(reporter, c == count[i]);
-    }
-    // remove all the ints between 25 and 74. Randomly chose to remove
-    // the first, last, or any entry for each.
-    for (int i = 25; i < 75; ++i) {
-        while (0 != tree.countOf(i)) {
-            --count[i];
-            int x = r.nextU() % 3;
-            Tree::Iter iter;
-            switch (x) {
-            case 0:
-                iter = tree.findFirst(i);
-                break;
-            case 1:
-                iter = tree.findLast(i);
-                break;
-            case 2:
-            default:
-                iter = tree.find(i);
-                break;
-            }
-            tree.remove(iter);
-        }
-        REPORTER_ASSERT(reporter, 0 == count[i]);
-        REPORTER_ASSERT(reporter, tree.findFirst(i) == tree.end());
-        REPORTER_ASSERT(reporter, tree.findLast(i) == tree.end());
-        REPORTER_ASSERT(reporter, tree.find(i) == tree.end());
-    }
-    // remove all of the 0 entries. (tests removing begin())
-    REPORTER_ASSERT(reporter, *tree.begin() == 0);
-    REPORTER_ASSERT(reporter, *(--tree.end()) == 99);
-    while (0 != tree.countOf(0)) {
-        --count[0];
-        tree.remove(tree.find(0));
-    }
-    REPORTER_ASSERT(reporter, 0 == count[0]);
-    REPORTER_ASSERT(reporter, tree.findFirst(0) == tree.end());
-    REPORTER_ASSERT(reporter, tree.findLast(0) == tree.end());
-    REPORTER_ASSERT(reporter, tree.find(0) == tree.end());
-    REPORTER_ASSERT(reporter, 0 < *tree.begin());
-
-    // remove all the 99 entries (tests removing last()).
-    while (0 != tree.countOf(99)) {
-        --count[99];
-        tree.remove(tree.find(99));
-    }
-    REPORTER_ASSERT(reporter, 0 == count[99]);
-    REPORTER_ASSERT(reporter, tree.findFirst(99) == tree.end());
-    REPORTER_ASSERT(reporter, tree.findLast(99) == tree.end());
-    REPORTER_ASSERT(reporter, tree.find(99) == tree.end());
-    REPORTER_ASSERT(reporter, 99 > *(--tree.end()));
-    REPORTER_ASSERT(reporter, tree.last() == --tree.end());
-
-    // Make sure iteration still goes through correct number of entries
-    // and is still sorted correctly.
-    c = 0;
-    for (Tree::Iter a = tree.begin(); tree.end() != a; ++a) {
-        Tree::Iter b = a;
-        ++b;
-        ++c;
-        REPORTER_ASSERT(reporter, b == tree.end() || *a <= *b);
-    }
-    REPORTER_ASSERT(reporter, c == tree.count());
-
-    // repeat check that correct number of each entry is in the tree
-    // and iterates correctly both forward and backward.
-    for (int i = 0; i < 100; ++i) {
-        REPORTER_ASSERT(reporter, tree.countOf(i) == count[i]);
-        int c = 0;
-        Tree::Iter iter = tree.findFirst(i);
-        while (iter != tree.end() && *iter == i) {
-            ++c;
-            ++iter;
-        }
-        REPORTER_ASSERT(reporter, count[i] == c);
-        c = 0;
-        iter = tree.findLast(i);
-        if (iter != tree.end()) {
-            do {
-                if (*iter == i) {
-                    ++c;
-                } else {
-                    break;
-                }
-                if (iter != tree.begin()) {
-                    --iter;
-                } else {
-                    break;
-                }
-            } while (true);
-        }
-        REPORTER_ASSERT(reporter, count[i] == c);
-    }
-
-    // remove all entries
-    while (!tree.empty()) {
-        tree.remove(tree.begin());
-    }
-
-    // test reset on empty tree.
-    tree.reset();
-}
-
-#endif
diff --git a/src/third_party/skia/tests/GrSKSLPrettyPrintTest.cpp b/src/third_party/skia/tests/GrSKSLPrettyPrintTest.cpp
new file mode 100644
index 0000000..ec5dddb
--- /dev/null
+++ b/src/third_party/skia/tests/GrSKSLPrettyPrintTest.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrSKSLPrettyPrint.h"
+
+#define ASSERT(x) REPORTER_ASSERT(r, x)
+
+const SkString input1("#this is not a realshader\nvec4 some stuff;outside of a function;"
+                     "int i(int b, int c) { { some stuff;} fake block; //comments\n return i;}"
+                     "void main()");
+const SkString input2("{nowin a function;{indenting;{abit more;dreadedfor((;;)(;)((;;);)){"
+                     "doingstuff"
+                     ";for(;;;){and more stufff;mixed garbage\n\n\t\t\t\t\n/*using this"
+                     " comment\n is");
+const SkString input3(" dangerous\ndo so at your own\n risk*/;\n\n\t\t\t\n"
+                     "//a comment");
+const SkString input4("breaking in comment");
+const SkString input5("continuing the comment");
+const SkString input6("\n}}a; little ;  love; for   ; leading;  spaces;} "
+                     "an struct = { int a; int b; };"
+                     "int[5] arr = int[5](1,2,3,4,5);} some code at the bottom; for(;;) {} }");
+
+const SkString output1(
+        "   1\t#this is not a realshader\n"
+        "   2\tvec4 some stuff;\n"
+        "   3\toutside of a function;\n"
+        "   4\tint i(int b, int c) \n"
+        "   5\t{\n"
+        "   6\t\t{\n"
+        "   7\t\t\tsome stuff;\n"
+        "   8\t\t}\n"
+        "   9\t\tfake block;\n"
+        "  10\t\t//comments\n"
+        "  11\t\treturn i;\n"
+        "  12\t}\n"
+        "  13\tvoid main()\n"
+        "  14\t{\n"
+        "  15\t\tnowin a function;\n"
+        "  16\t\t{\n"
+        "  17\t\t\tindenting;\n"
+        "  18\t\t\t{\n"
+        "  19\t\t\t\tabit more;\n"
+        "  20\t\t\t\tdreadedfor((;;)(;)((;;);))\n"
+        "  21\t\t\t\t{\n"
+        "  22\t\t\t\t\tdoingstuff;\n"
+        "  23\t\t\t\t\tfor(;;;)\n"
+        "  24\t\t\t\t\t{\n"
+        "  25\t\t\t\t\t\tand more stufff;\n"
+        "  26\t\t\t\t\t\tmixed garbage/*using this comment\n"
+        "  27\t\t\t\t\t\t is dangerous\n"
+        "  28\t\t\t\t\t\tdo so at your own\n"
+        "  29\t\t\t\t\t\t risk*/;\n"
+        "  30\t\t\t\t\t\t//a commentbreaking in commentcontinuing the comment\n"
+        "  31\t\t\t\t\t}\n"
+        "  32\t\t\t\t}\n"
+        "  33\t\t\t\ta;\n"
+        "  34\t\t\t\tlittle ;\n"
+        "  35\t\t\t\tlove;\n"
+        "  36\t\t\t\tfor   ;\n"
+        "  37\t\t\t\tleading;\n"
+        "  38\t\t\t\tspaces;\n"
+        "  39\t\t\t}\n"
+        "  40\t\t\tan struct = \n"
+        "  41\t\t\t{\n"
+        "  42\t\t\t\tint a;\n"
+        "  43\t\t\t\tint b;\n"
+        "  44\t\t\t}\n"
+        "  45\t\t\t;\n"
+        "  46\t\t\tint[5] arr = int[5](1,2,3,4,5);\n"
+        "  47\t\t}\n"
+        "  48\t\tsome code at the bottom;\n"
+        "  49\t\tfor(;;) \n"
+        "  50\t\t{\n"
+        "  51\t\t}\n"
+        "  52\t}\n"
+        "  53\t");
+
+const SkString neg1("{;;{{{{;;;{{{{{{{{{{{");
+const SkString neg2("###\n##\n#####(((((((((((((unbalanced verything;;;");
+const SkString neg3("}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"
+        ";;;;;;/////");
+
+DEF_TEST(GrSKSLPrettyPrint, r) {
+    SkTArray<const char*> testStr;
+    SkTArray<int> lengths;
+    testStr.push_back(input1.c_str());
+    lengths.push_back((int)input1.size());
+    testStr.push_back(input2.c_str());
+    lengths.push_back((int)input2.size());
+    testStr.push_back(input3.c_str());
+    lengths.push_back((int)input3.size());
+    testStr.push_back(input4.c_str());
+    lengths.push_back((int)input4.size());
+    testStr.push_back(input5.c_str());
+    lengths.push_back((int)input5.size());
+    testStr.push_back(input6.c_str());
+    lengths.push_back((int)input6.size());
+
+    SkString test = GrSKSLPrettyPrint::PrettyPrint(testStr.begin(), lengths.begin(),
+                                                   testStr.count(), true);
+    ASSERT(output1 == test);
+
+    testStr.reset();
+    lengths.reset();
+    testStr.push_back(neg1.c_str());
+    lengths.push_back((int)neg1.size());
+    testStr.push_back(neg2.c_str());
+    lengths.push_back((int)neg2.size());
+    testStr.push_back(neg3.c_str());
+    lengths.push_back((int)neg3.size());
+
+    // Just test we don't crash with garbage input
+    ASSERT(GrSKSLPrettyPrint::PrettyPrint(testStr.begin(), lengths.begin(), 1,
+                                          true).c_str() != nullptr);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/GrShapeTest.cpp b/src/third_party/skia/tests/GrShapeTest.cpp
new file mode 100644
index 0000000..dae71c1
--- /dev/null
+++ b/src/third_party/skia/tests/GrShapeTest.cpp
@@ -0,0 +1,1971 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <initializer_list>
+#include <functional>
+#include "Test.h"
+#if SK_SUPPORT_GPU
+#include "GrShape.h"
+#include "SkCanvas.h"
+#include "SkDashPathEffect.h"
+#include "SkPath.h"
+#include "SkPathOps.h"
+#include "SkSurface.h"
+#include "SkClipOpPriv.h"
+
+using Key = SkTArray<uint32_t>;
+
+static bool make_key(Key* key, const GrShape& shape) {
+    int size = shape.unstyledKeySize();
+    if (size <= 0) {
+        key->reset(0);
+        return false;
+    }
+    SkASSERT(size);
+    key->reset(size);
+    shape.writeUnstyledKey(key->begin());
+    return true;
+}
+
+static bool paths_fill_same(const SkPath& a, const SkPath& b) {
+    SkPath pathXor;
+    Op(a, b, SkPathOp::kXOR_SkPathOp, &pathXor);
+    return pathXor.isEmpty();
+}
+
+static bool test_bounds_by_rasterizing(const SkPath& path, const SkRect& bounds) {
+    // We test the bounds by rasterizing the path into a kRes by kRes grid. The bounds is
+    // mapped to the range kRes/4 to 3*kRes/4 in x and y. A difference clip is used to avoid
+    // rendering within the bounds (with a tolerance). Then we render the path and check that
+    // everything got clipped out.
+    static constexpr int kRes = 2000;
+    // This tolerance is in units of 1/kRes fractions of the bounds width/height.
+    static constexpr int kTol = 0;
+    GR_STATIC_ASSERT(kRes % 4 == 0);
+    SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes);
+    sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
+    surface->getCanvas()->clear(0x0);
+    SkRect clip = SkRect::MakeXYWH(kRes/4, kRes/4, kRes/2, kRes/2);
+    SkMatrix matrix;
+    matrix.setRectToRect(bounds, clip, SkMatrix::kFill_ScaleToFit);
+    clip.outset(SkIntToScalar(kTol), SkIntToScalar(kTol));
+    surface->getCanvas()->clipRect(clip, kDifference_SkClipOp);
+    surface->getCanvas()->concat(matrix);
+    SkPaint whitePaint;
+    whitePaint.setColor(SK_ColorWHITE);
+    surface->getCanvas()->drawPath(path, whitePaint);
+    SkPixmap pixmap;
+    surface->getCanvas()->peekPixels(&pixmap);
+#if defined(SK_BUILD_FOR_WIN)
+    // The static constexpr version in #else causes cl.exe to crash.
+    const uint8_t* kZeros = reinterpret_cast<uint8_t*>(calloc(kRes, 1));
+#else
+    static constexpr uint8_t kZeros[kRes] = {0};
+#endif
+    for (int y = 0; y < kRes; ++y) {
+        const uint8_t* row = pixmap.addr8(0, y);
+        if (0 != memcmp(kZeros, row, kRes)) {
+            return false;
+        }
+    }
+#ifdef SK_BUILD_FOR_WIN
+    free(const_cast<uint8_t*>(kZeros));
+#endif
+    return true;
+}
+
+namespace {
+/**
+ * Geo is a factory for creating a GrShape from another representation. It also answers some
+ * questions about expected behavior for GrShape given the inputs.
+ */
+class Geo {
+public:
+    virtual ~Geo() {}
+    virtual GrShape makeShape(const SkPaint&) const = 0;
+    virtual SkPath path() const = 0;
+    // These functions allow tests to check for special cases where style gets
+    // applied by GrShape in its constructor (without calling GrShape::applyStyle).
+    // These unfortunately rely on knowing details of GrShape's implementation.
+    // These predicates are factored out here to avoid littering the rest of the
+    // test code with GrShape implementation details.
+    virtual bool fillChangesGeom() const { return false; }
+    virtual bool strokeIsConvertedToFill() const { return false; }
+    virtual bool strokeAndFillIsConvertedToFill(const SkPaint&) const { return false; }
+    // Is this something we expect GrShape to recognize as something simpler than a path.
+    virtual bool isNonPath(const SkPaint& paint) const { return true; }
+};
+
+class RectGeo : public Geo {
+public:
+    RectGeo(const SkRect& rect) : fRect(rect) {}
+
+    SkPath path() const override {
+        SkPath path;
+        path.addRect(fRect);
+        return path;
+    }
+
+    GrShape makeShape(const SkPaint& paint) const override {
+        return GrShape(fRect, paint);
+    }
+
+    bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
+        SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
+        // Converted to an outset rectangle.
+        return paint.getStrokeJoin() == SkPaint::kMiter_Join &&
+               paint.getStrokeMiter() >= SK_ScalarSqrt2;
+    }
+
+private:
+    SkRect fRect;
+};
+
+class RRectGeo : public Geo {
+public:
+    RRectGeo(const SkRRect& rrect) : fRRect(rrect) {}
+
+    GrShape makeShape(const SkPaint& paint) const override {
+        return GrShape(fRRect, paint);
+    }
+
+    SkPath path() const override {
+        SkPath path;
+        path.addRRect(fRRect);
+        return path;
+    }
+
+    bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
+        SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
+        if (fRRect.isRect()) {
+            return RectGeo(fRRect.rect()).strokeAndFillIsConvertedToFill(paint);
+        }
+        return false;
+    }
+
+private:
+    SkRRect fRRect;
+};
+
+class PathGeo : public Geo {
+public:
+    enum class Invert { kNo, kYes };
+
+    PathGeo(const SkPath& path, Invert invert) : fPath(path)  {
+        SkASSERT(!path.isInverseFillType());
+        if (Invert::kYes == invert) {
+            if (fPath.getFillType() == SkPath::kEvenOdd_FillType) {
+                fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
+            } else {
+                SkASSERT(fPath.getFillType() == SkPath::kWinding_FillType);
+                fPath.setFillType(SkPath::kInverseWinding_FillType);
+            }
+        }
+    }
+
+    GrShape makeShape(const SkPaint& paint) const override {
+        return GrShape(fPath, paint);
+    }
+
+    SkPath path() const override { return fPath; }
+
+    bool fillChangesGeom() const override {
+        // unclosed rects get closed. Lines get turned into empty geometry
+        return this->isUnclosedRect() || (fPath.isLine(nullptr) && !fPath.isInverseFillType());
+    }
+
+    bool strokeIsConvertedToFill() const override {
+        return this->isAxisAlignedLine();
+    }
+
+    bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
+        SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
+        if (this->isAxisAlignedLine()) {
+            // The fill is ignored (zero area) and the stroke is converted to a rrect.
+            return true;
+        }
+        SkRect rect;
+        unsigned start;
+        SkPath::Direction dir;
+        if (SkPathPriv::IsSimpleClosedRect(fPath, &rect, &dir, &start)) {
+            return RectGeo(rect).strokeAndFillIsConvertedToFill(paint);
+        }
+        return false;
+    }
+
+    bool isNonPath(const SkPaint& paint) const override {
+        return fPath.isLine(nullptr) || fPath.isEmpty();
+    }
+
+private:
+    bool isAxisAlignedLine() const {
+        SkPoint pts[2];
+        if (!fPath.isLine(pts)) {
+            return false;
+        }
+        return pts[0].fX == pts[1].fX || pts[0].fY == pts[1].fY;
+    }
+
+    bool isUnclosedRect() const {
+        bool closed;
+        return fPath.isRect(nullptr, &closed, nullptr) && !closed;
+    }
+
+    SkPath fPath;
+};
+
+class RRectPathGeo : public PathGeo {
+public:
+    enum class RRectForStroke { kNo, kYes };
+
+    RRectPathGeo(const SkPath& path, const SkRRect& equivalentRRect, RRectForStroke rrectForStroke,
+                 Invert invert)
+            : PathGeo(path, invert)
+            , fRRect(equivalentRRect)
+            , fRRectForStroke(rrectForStroke) {}
+
+    RRectPathGeo(const SkPath& path, const SkRect& equivalentRect, RRectForStroke rrectForStroke,
+                 Invert invert)
+            : RRectPathGeo(path, SkRRect::MakeRect(equivalentRect), rrectForStroke, invert) {}
+
+    bool isNonPath(const SkPaint& paint) const override {
+        if (SkPaint::kFill_Style == paint.getStyle() || RRectForStroke::kYes == fRRectForStroke) {
+            return true;
+        }
+        return false;
+    }
+
+    const SkRRect& rrect() const { return fRRect; }
+
+private:
+    SkRRect         fRRect;
+    RRectForStroke  fRRectForStroke;
+};
+
+class TestCase {
+public:
+    TestCase(const Geo& geo, const SkPaint& paint, skiatest::Reporter* r,
+             SkScalar scale = SK_Scalar1) : fBase(geo.makeShape(paint)) {
+        this->init(r, scale);
+    }
+
+    template<typename... ShapeArgs>
+    TestCase(skiatest::Reporter* r, ShapeArgs... shapeArgs)
+            : fBase(shapeArgs...) {
+        this->init(r, SK_Scalar1);
+    }
+
+    TestCase(const GrShape& shape, skiatest::Reporter* r, SkScalar scale = SK_Scalar1)
+        : fBase(shape) {
+        this->init(r, scale);
+    }
+
+    struct SelfExpectations {
+        bool fPEHasEffect;
+        bool fPEHasValidKey;
+        bool fStrokeApplies;
+    };
+
+    void testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const;
+
+    enum ComparisonExpecation {
+        kAllDifferent_ComparisonExpecation,
+        kSameUpToPE_ComparisonExpecation,
+        kSameUpToStroke_ComparisonExpecation,
+        kAllSame_ComparisonExpecation,
+    };
+
+    void compare(skiatest::Reporter*, const TestCase& that, ComparisonExpecation) const;
+
+    const GrShape& baseShape() const { return fBase; }
+    const GrShape& appliedPathEffectShape() const { return fAppliedPE; }
+    const GrShape& appliedFullStyleShape() const { return fAppliedFull; }
+
+    // The returned array's count will be 0 if the key shape has no key.
+    const Key& baseKey() const { return fBaseKey; }
+    const Key& appliedPathEffectKey() const { return fAppliedPEKey; }
+    const Key& appliedFullStyleKey() const { return fAppliedFullKey; }
+    const Key& appliedPathEffectThenStrokeKey() const { return fAppliedPEThenStrokeKey; }
+
+private:
+    static void CheckBounds(skiatest::Reporter* r, const GrShape& shape, const SkRect& bounds) {
+        SkPath path;
+        shape.asPath(&path);
+        // If the bounds are empty, the path ought to be as well.
+        if (bounds.fLeft > bounds.fRight || bounds.fTop > bounds.fBottom) {
+            REPORTER_ASSERT(r, path.isEmpty());
+            return;
+        }
+        if (path.isEmpty()) {
+            return;
+        }
+        // The bounds API explicitly calls out that it does not consider inverseness.
+        SkPath p = path;
+        p.setFillType(SkPath::ConvertToNonInverseFillType(path.getFillType()));
+        REPORTER_ASSERT(r, test_bounds_by_rasterizing(p, bounds));
+    }
+
+    void init(skiatest::Reporter* r, SkScalar scale) {
+        fAppliedPE           = fBase.applyStyle(GrStyle::Apply::kPathEffectOnly, scale);
+        fAppliedPEThenStroke = fAppliedPE.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec,
+                                                     scale);
+        fAppliedFull         = fBase.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, scale);
+
+        make_key(&fBaseKey, fBase);
+        make_key(&fAppliedPEKey, fAppliedPE);
+        make_key(&fAppliedPEThenStrokeKey, fAppliedPEThenStroke);
+        make_key(&fAppliedFullKey, fAppliedFull);
+
+        // Applying the path effect and then the stroke should always be the same as applying
+        // both in one go.
+        REPORTER_ASSERT(r, fAppliedPEThenStrokeKey == fAppliedFullKey);
+        SkPath a, b;
+        fAppliedPEThenStroke.asPath(&a);
+        fAppliedFull.asPath(&b);
+        // If the output of the path effect is a rrect then it is possible for a and b to be
+        // different paths that fill identically. The reason is that fAppliedFull will do this:
+        // base -> apply path effect -> rrect_as_path -> stroke -> stroked_rrect_as_path
+        // fAppliedPEThenStroke will have converted the rrect_as_path back to a rrect. However,
+        // now that there is no longer a path effect, the direction and starting index get
+        // canonicalized before the stroke.
+        if (fAppliedPE.asRRect(nullptr, nullptr, nullptr, nullptr)) {
+            REPORTER_ASSERT(r, paths_fill_same(a, b));
+        } else {
+            REPORTER_ASSERT(r, a == b);
+        }
+        REPORTER_ASSERT(r, fAppliedFull.isEmpty() == fAppliedPEThenStroke.isEmpty());
+
+        SkPath path;
+        fBase.asPath(&path);
+        REPORTER_ASSERT(r, path.isEmpty() == fBase.isEmpty());
+        REPORTER_ASSERT(r, path.getSegmentMasks() == fBase.segmentMask());
+        fAppliedPE.asPath(&path);
+        REPORTER_ASSERT(r, path.isEmpty() == fAppliedPE.isEmpty());
+        REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedPE.segmentMask());
+        fAppliedFull.asPath(&path);
+        REPORTER_ASSERT(r, path.isEmpty() == fAppliedFull.isEmpty());
+        REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedFull.segmentMask());
+
+        CheckBounds(r, fBase, fBase.bounds());
+        CheckBounds(r, fAppliedPE, fAppliedPE.bounds());
+        CheckBounds(r, fAppliedPEThenStroke, fAppliedPEThenStroke.bounds());
+        CheckBounds(r, fAppliedFull, fAppliedFull.bounds());
+        SkRect styledBounds = fBase.styledBounds();
+        CheckBounds(r, fAppliedFull, styledBounds);
+        styledBounds = fAppliedPE.styledBounds();
+        CheckBounds(r, fAppliedFull, styledBounds);
+
+        // Check that the same path is produced when style is applied by GrShape and GrStyle.
+        SkPath preStyle;
+        SkPath postPathEffect;
+        SkPath postAllStyle;
+
+        fBase.asPath(&preStyle);
+        SkStrokeRec postPEStrokeRec(SkStrokeRec::kFill_InitStyle);
+        if (fBase.style().applyPathEffectToPath(&postPathEffect, &postPEStrokeRec, preStyle,
+                                                scale)) {
+            // run postPathEffect through GrShape to get any geometry reductions that would have
+            // occurred to fAppliedPE.
+            GrShape(postPathEffect, GrStyle(postPEStrokeRec, nullptr)).asPath(&postPathEffect);
+
+            SkPath testPath;
+            fAppliedPE.asPath(&testPath);
+            REPORTER_ASSERT(r, testPath == postPathEffect);
+            REPORTER_ASSERT(r, postPEStrokeRec.hasEqualEffect(fAppliedPE.style().strokeRec()));
+        }
+        SkStrokeRec::InitStyle fillOrHairline;
+        if (fBase.style().applyToPath(&postAllStyle, &fillOrHairline, preStyle, scale)) {
+            SkPath testPath;
+            fAppliedFull.asPath(&testPath);
+            if (fBase.style().hasPathEffect()) {
+                // Because GrShape always does two-stage application when there is a path effect
+                // there may be a reduction/canonicalization step between the path effect and
+                // strokerec not reflected in postAllStyle since it applied both the path effect
+                // and strokerec without analyzing the intermediate path.
+                REPORTER_ASSERT(r, paths_fill_same(postAllStyle, testPath));
+            } else {
+                // Make sure that postAllStyle sees any reductions/canonicalizations that GrShape
+                // would apply.
+                GrShape(postAllStyle, GrStyle(fillOrHairline)).asPath(&postAllStyle);
+                REPORTER_ASSERT(r, testPath == postAllStyle);
+            }
+
+            if (fillOrHairline == SkStrokeRec::kFill_InitStyle) {
+                REPORTER_ASSERT(r, fAppliedFull.style().isSimpleFill());
+            } else {
+                REPORTER_ASSERT(r, fAppliedFull.style().isSimpleHairline());
+            }
+        }
+    }
+
+    GrShape fBase;
+    GrShape fAppliedPE;
+    GrShape fAppliedPEThenStroke;
+    GrShape fAppliedFull;
+
+    Key fBaseKey;
+    Key fAppliedPEKey;
+    Key fAppliedPEThenStrokeKey;
+    Key fAppliedFullKey;
+};
+
+void TestCase::testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const {
+    // The base's key should always be valid (unless the path is volatile)
+    REPORTER_ASSERT(reporter, fBaseKey.count());
+    if (expectations.fPEHasEffect) {
+        REPORTER_ASSERT(reporter, fBaseKey != fAppliedPEKey);
+        REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedPEKey.count()));
+        REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
+        REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedFullKey.count()));
+        if (expectations.fStrokeApplies && expectations.fPEHasValidKey) {
+            REPORTER_ASSERT(reporter, fAppliedPEKey != fAppliedFullKey);
+            REPORTER_ASSERT(reporter, SkToBool(fAppliedFullKey.count()));
+        }
+    } else {
+        REPORTER_ASSERT(reporter, fBaseKey == fAppliedPEKey);
+        SkPath a, b;
+        fBase.asPath(&a);
+        fAppliedPE.asPath(&b);
+        REPORTER_ASSERT(reporter, a == b);
+        if (expectations.fStrokeApplies) {
+            REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
+        } else {
+            REPORTER_ASSERT(reporter, fBaseKey == fAppliedFullKey);
+        }
+    }
+}
+
+static bool can_interchange_winding_and_even_odd_fill(const GrShape& shape) {
+    SkPath path;
+    shape.asPath(&path);
+    if (shape.style().hasNonDashPathEffect()) {
+        return false;
+    }
+    const SkStrokeRec::Style strokeRecStyle = shape.style().strokeRec().getStyle();
+    return strokeRecStyle == SkStrokeRec::kStroke_Style ||
+           strokeRecStyle == SkStrokeRec::kHairline_Style ||
+           (shape.style().isSimpleFill() && path.isConvex());
+}
+
+static void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrShape& b,
+                              const Key& keyA, const Key& keyB) {
+    // GrShape only respects the input winding direction and start point for rrect shapes
+    // when there is a path effect. Thus, if there are two GrShapes representing the same rrect
+    // but one has a path effect in its style and the other doesn't then asPath() and the unstyled
+    // key will differ. GrShape will have canonicalized the direction and start point for the shape
+    // without the path effect. If *both* have path effects then they should have both preserved
+    // the direction and starting point.
+
+    // The asRRect() output params are all initialized just to silence compiler warnings about
+    // uninitialized variables.
+    SkRRect rrectA = SkRRect::MakeEmpty(), rrectB = SkRRect::MakeEmpty();
+    SkPath::Direction dirA = SkPath::kCW_Direction, dirB = SkPath::kCW_Direction;
+    unsigned startA = ~0U, startB = ~0U;
+    bool invertedA = true, invertedB = true;
+
+    bool aIsRRect = a.asRRect(&rrectA, &dirA, &startA, &invertedA);
+    bool bIsRRect = b.asRRect(&rrectB, &dirB, &startB, &invertedB);
+    bool aHasPE = a.style().hasPathEffect();
+    bool bHasPE = b.style().hasPathEffect();
+    bool allowSameRRectButDiffStartAndDir = (aIsRRect && bIsRRect) && (aHasPE != bHasPE);
+    // GrShape will close paths with simple fill style.
+    bool allowedClosednessDiff = (a.style().isSimpleFill() != b.style().isSimpleFill());
+    SkPath pathA, pathB;
+    a.asPath(&pathA);
+    b.asPath(&pathB);
+
+    // Having a dash path effect can allow 'a' but not 'b' to turn a inverse fill type into a
+    // non-inverse fill type  (or vice versa).
+    bool ignoreInversenessDifference = false;
+    if (pathA.isInverseFillType() != pathB.isInverseFillType()) {
+        const GrShape* s1 = pathA.isInverseFillType() ? &a : &b;
+        const GrShape* s2 = pathA.isInverseFillType() ? &b : &a;
+        bool canDropInverse1 = s1->style().isDashed();
+        bool canDropInverse2 = s2->style().isDashed();
+        ignoreInversenessDifference = (canDropInverse1 != canDropInverse2);
+    }
+    bool ignoreWindingVsEvenOdd = false;
+    if (SkPath::ConvertToNonInverseFillType(pathA.getFillType()) !=
+        SkPath::ConvertToNonInverseFillType(pathB.getFillType())) {
+        bool aCanChange = can_interchange_winding_and_even_odd_fill(a);
+        bool bCanChange = can_interchange_winding_and_even_odd_fill(b);
+        if (aCanChange != bCanChange) {
+            ignoreWindingVsEvenOdd = true;
+        }
+    }
+    if (allowSameRRectButDiffStartAndDir) {
+        REPORTER_ASSERT(r, rrectA == rrectB);
+        REPORTER_ASSERT(r, paths_fill_same(pathA, pathB));
+        REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
+    } else {
+        SkPath pA = pathA;
+        SkPath pB = pathB;
+        REPORTER_ASSERT(r, a.inverseFilled() == pA.isInverseFillType());
+        REPORTER_ASSERT(r, b.inverseFilled() == pB.isInverseFillType());
+        if (ignoreInversenessDifference) {
+            pA.setFillType(SkPath::ConvertToNonInverseFillType(pathA.getFillType()));
+            pB.setFillType(SkPath::ConvertToNonInverseFillType(pathB.getFillType()));
+        }
+        if (ignoreWindingVsEvenOdd) {
+            pA.setFillType(pA.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
+                                                  : SkPath::kEvenOdd_FillType);
+            pB.setFillType(pB.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
+                                                  : SkPath::kEvenOdd_FillType);
+        }
+        if (!ignoreInversenessDifference && !ignoreWindingVsEvenOdd) {
+            REPORTER_ASSERT(r, keyA == keyB);
+        } else {
+            REPORTER_ASSERT(r, keyA != keyB);
+        }
+        if (allowedClosednessDiff) {
+            // GrShape will close paths with simple fill style. Make the non-filled path closed
+            // so that the comparision will succeed. Make sure both are closed before comparing.
+            pA.close();
+            pB.close();
+        }
+        REPORTER_ASSERT(r, pA == pB);
+        REPORTER_ASSERT(r, aIsRRect == bIsRRect);
+        if (aIsRRect) {
+            REPORTER_ASSERT(r, rrectA == rrectB);
+            REPORTER_ASSERT(r, dirA == dirB);
+            REPORTER_ASSERT(r, startA == startB);
+            REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
+        }
+    }
+    REPORTER_ASSERT(r, a.isEmpty() == b.isEmpty());
+    REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeClosed() == b.knownToBeClosed());
+    // closedness can affect convexity.
+    REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeConvex() == b.knownToBeConvex());
+    if (a.knownToBeConvex()) {
+        REPORTER_ASSERT(r, pathA.isConvex());
+    }
+    if (b.knownToBeConvex()) {
+        REPORTER_ASSERT(r, pathB.isConvex());
+    }
+    REPORTER_ASSERT(r, a.bounds() == b.bounds());
+    REPORTER_ASSERT(r, a.segmentMask() == b.segmentMask());
+    // Init these to suppress warnings.
+    SkPoint pts[4] {{0, 0,}, {0, 0}, {0, 0}, {0, 0}} ;
+    bool invertedLine[2] {true, true};
+    REPORTER_ASSERT(r, a.asLine(pts, &invertedLine[0]) == b.asLine(pts + 2, &invertedLine[1]));
+    // mayBeInverseFilledAfterStyling() is allowed to differ if one has a arbitrary PE and the other
+    // doesn't (since the PE can set any fill type on its output path).
+    // Moreover, dash style explicitly ignores inverseness. So if one is dashed but not the other
+    // then they may disagree about inverseness.
+    if (a.style().hasNonDashPathEffect() == b.style().hasNonDashPathEffect() &&
+        a.style().isDashed() == b.style().isDashed()) {
+        REPORTER_ASSERT(r, a.mayBeInverseFilledAfterStyling() ==
+                           b.mayBeInverseFilledAfterStyling());
+    }
+    if (a.asLine(nullptr, nullptr)) {
+        REPORTER_ASSERT(r, pts[2] == pts[0] && pts[3] == pts[1]);
+        REPORTER_ASSERT(r, ignoreInversenessDifference || invertedLine[0] == invertedLine[1]);
+        REPORTER_ASSERT(r, invertedLine[0] == a.inverseFilled());
+        REPORTER_ASSERT(r, invertedLine[1] == b.inverseFilled());
+    }
+    REPORTER_ASSERT(r, ignoreInversenessDifference || a.inverseFilled() == b.inverseFilled());
+}
+
+void TestCase::compare(skiatest::Reporter* r, const TestCase& that,
+                       ComparisonExpecation expectation) const {
+    SkPath a, b;
+    switch (expectation) {
+        case kAllDifferent_ComparisonExpecation:
+            REPORTER_ASSERT(r, fBaseKey != that.fBaseKey);
+            REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
+            REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
+            break;
+        case kSameUpToPE_ComparisonExpecation:
+            check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
+            REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
+            REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
+            break;
+        case kSameUpToStroke_ComparisonExpecation:
+            check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
+            check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
+            REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
+            break;
+        case kAllSame_ComparisonExpecation:
+            check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
+            check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
+            check_equivalence(r, fAppliedFull, that.fAppliedFull, fAppliedFullKey,
+                              that.fAppliedFullKey);
+            break;
+    }
+}
+}  // namespace
+
+static sk_sp<SkPathEffect> make_dash() {
+    static const SkScalar kIntervals[] = { 0.25, 3.f, 0.5, 2.f };
+    static const SkScalar kPhase = 0.75;
+    return SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), kPhase);
+}
+
+static sk_sp<SkPathEffect> make_null_dash() {
+    static const SkScalar kNullIntervals[] = {0, 0, 0, 0, 0, 0};
+    return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f);
+}
+
+static void test_basic(skiatest::Reporter* reporter, const Geo& geo) {
+    sk_sp<SkPathEffect> dashPE = make_dash();
+
+    TestCase::SelfExpectations expectations;
+    SkPaint fill;
+
+    TestCase fillCase(geo, fill, reporter);
+    expectations.fPEHasEffect = false;
+    expectations.fPEHasValidKey = false;
+    expectations.fStrokeApplies = false;
+    fillCase.testExpectations(reporter, expectations);
+    // Test that another GrShape instance built from the same primitive is the same.
+    TestCase(geo, fill, reporter).compare(reporter, fillCase,
+                                          TestCase::kAllSame_ComparisonExpecation);
+
+    SkPaint stroke2RoundBevel;
+    stroke2RoundBevel.setStyle(SkPaint::kStroke_Style);
+    stroke2RoundBevel.setStrokeCap(SkPaint::kRound_Cap);
+    stroke2RoundBevel.setStrokeJoin(SkPaint::kBevel_Join);
+    stroke2RoundBevel.setStrokeWidth(2.f);
+    TestCase stroke2RoundBevelCase(geo, stroke2RoundBevel, reporter);
+    expectations.fPEHasValidKey = true;
+    expectations.fPEHasEffect = false;
+    expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
+    stroke2RoundBevelCase.testExpectations(reporter, expectations);
+    TestCase(geo, stroke2RoundBevel, reporter).compare(reporter, stroke2RoundBevelCase,
+                                                       TestCase::kAllSame_ComparisonExpecation);
+
+    SkPaint stroke2RoundBevelDash = stroke2RoundBevel;
+    stroke2RoundBevelDash.setPathEffect(make_dash());
+    TestCase stroke2RoundBevelDashCase(geo, stroke2RoundBevelDash, reporter);
+    expectations.fPEHasValidKey = true;
+    expectations.fPEHasEffect = true;
+    expectations.fStrokeApplies = true;
+    stroke2RoundBevelDashCase.testExpectations(reporter, expectations);
+    TestCase(geo, stroke2RoundBevelDash, reporter).compare(reporter, stroke2RoundBevelDashCase,
+                                                           TestCase::kAllSame_ComparisonExpecation);
+
+    if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) {
+        fillCase.compare(reporter, stroke2RoundBevelCase,
+                         TestCase::kAllDifferent_ComparisonExpecation);
+        fillCase.compare(reporter, stroke2RoundBevelDashCase,
+                         TestCase::kAllDifferent_ComparisonExpecation);
+    } else {
+        fillCase.compare(reporter, stroke2RoundBevelCase,
+                         TestCase::kSameUpToStroke_ComparisonExpecation);
+        fillCase.compare(reporter, stroke2RoundBevelDashCase,
+                         TestCase::kSameUpToPE_ComparisonExpecation);
+    }
+    if (geo.strokeIsConvertedToFill()) {
+        stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
+                                      TestCase::kAllDifferent_ComparisonExpecation);
+    } else {
+        stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
+                                      TestCase::kSameUpToPE_ComparisonExpecation);
+    }
+
+    // Stroke and fill cases
+    SkPaint stroke2RoundBevelAndFill = stroke2RoundBevel;
+    stroke2RoundBevelAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
+    TestCase stroke2RoundBevelAndFillCase(geo, stroke2RoundBevelAndFill, reporter);
+    expectations.fPEHasValidKey = true;
+    expectations.fPEHasEffect = false;
+    expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
+    stroke2RoundBevelAndFillCase.testExpectations(reporter, expectations);
+    TestCase(geo, stroke2RoundBevelAndFill, reporter).compare(reporter,
+            stroke2RoundBevelAndFillCase, TestCase::kAllSame_ComparisonExpecation);
+
+    SkPaint stroke2RoundBevelAndFillDash = stroke2RoundBevelDash;
+    stroke2RoundBevelAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
+    TestCase stroke2RoundBevelAndFillDashCase(geo, stroke2RoundBevelAndFillDash, reporter);
+    expectations.fPEHasValidKey = true;
+    expectations.fPEHasEffect = false;
+    expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
+    stroke2RoundBevelAndFillDashCase.testExpectations(reporter, expectations);
+    TestCase(geo, stroke2RoundBevelAndFillDash, reporter).compare(
+        reporter, stroke2RoundBevelAndFillDashCase, TestCase::kAllSame_ComparisonExpecation);
+    stroke2RoundBevelAndFillDashCase.compare(reporter, stroke2RoundBevelAndFillCase,
+                                             TestCase::kAllSame_ComparisonExpecation);
+
+    SkPaint hairline;
+    hairline.setStyle(SkPaint::kStroke_Style);
+    hairline.setStrokeWidth(0.f);
+    TestCase hairlineCase(geo, hairline, reporter);
+    // Since hairline style doesn't change the SkPath data, it is keyed identically to fill (except
+    // in the line and unclosed rect cases).
+    if (geo.fillChangesGeom()) {
+        hairlineCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation);
+    } else {
+        hairlineCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
+    }
+    REPORTER_ASSERT(reporter, hairlineCase.baseShape().style().isSimpleHairline());
+    REPORTER_ASSERT(reporter, hairlineCase.appliedFullStyleShape().style().isSimpleHairline());
+    REPORTER_ASSERT(reporter, hairlineCase.appliedPathEffectShape().style().isSimpleHairline());
+
+}
+
+static void test_scale(skiatest::Reporter* reporter, const Geo& geo) {
+    sk_sp<SkPathEffect> dashPE = make_dash();
+
+    static const SkScalar kS1 = 1.f;
+    static const SkScalar kS2 = 2.f;
+
+    SkPaint fill;
+    TestCase fillCase1(geo, fill, reporter, kS1);
+    TestCase fillCase2(geo, fill, reporter, kS2);
+    // Scale doesn't affect fills.
+    fillCase1.compare(reporter, fillCase2, TestCase::kAllSame_ComparisonExpecation);
+
+    SkPaint hairline;
+    hairline.setStyle(SkPaint::kStroke_Style);
+    hairline.setStrokeWidth(0.f);
+    TestCase hairlineCase1(geo, hairline, reporter, kS1);
+    TestCase hairlineCase2(geo, hairline, reporter, kS2);
+    // Scale doesn't affect hairlines.
+    hairlineCase1.compare(reporter, hairlineCase2, TestCase::kAllSame_ComparisonExpecation);
+
+    SkPaint stroke;
+    stroke.setStyle(SkPaint::kStroke_Style);
+    stroke.setStrokeWidth(2.f);
+    TestCase strokeCase1(geo, stroke, reporter, kS1);
+    TestCase strokeCase2(geo, stroke, reporter, kS2);
+    // Scale affects the stroke
+    if (geo.strokeIsConvertedToFill()) {
+        REPORTER_ASSERT(reporter, !strokeCase1.baseShape().style().applies());
+        strokeCase1.compare(reporter, strokeCase2, TestCase::kAllSame_ComparisonExpecation);
+    } else {
+        strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation);
+    }
+
+    SkPaint strokeDash = stroke;
+    strokeDash.setPathEffect(make_dash());
+    TestCase strokeDashCase1(geo, strokeDash, reporter, kS1);
+    TestCase strokeDashCase2(geo, strokeDash, reporter, kS2);
+    // Scale affects the dash and the stroke.
+    strokeDashCase1.compare(reporter, strokeDashCase2,
+                            TestCase::kSameUpToPE_ComparisonExpecation);
+
+    // Stroke and fill cases
+    SkPaint strokeAndFill = stroke;
+    strokeAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
+    TestCase strokeAndFillCase1(geo, strokeAndFill, reporter, kS1);
+    TestCase strokeAndFillCase2(geo, strokeAndFill, reporter, kS2);
+    SkPaint strokeAndFillDash = strokeDash;
+    strokeAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
+    // Dash is ignored for stroke and fill
+    TestCase strokeAndFillDashCase1(geo, strokeAndFillDash, reporter, kS1);
+    TestCase strokeAndFillDashCase2(geo, strokeAndFillDash, reporter, kS2);
+    // Scale affects the stroke, but check to make sure this didn't become a simpler shape (e.g.
+    // stroke-and-filled rect can become a rect), in which case the scale shouldn't matter and the
+    // geometries should agree.
+    if (geo.strokeAndFillIsConvertedToFill(strokeAndFillDash)) {
+        REPORTER_ASSERT(reporter, !strokeAndFillCase1.baseShape().style().applies());
+        strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
+                                   TestCase::kAllSame_ComparisonExpecation);
+        strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2,
+                                       TestCase::kAllSame_ComparisonExpecation);
+    } else {
+        strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
+                                   TestCase::kSameUpToStroke_ComparisonExpecation);
+    }
+    strokeAndFillDashCase1.compare(reporter, strokeAndFillCase1,
+                                   TestCase::kAllSame_ComparisonExpecation);
+    strokeAndFillDashCase2.compare(reporter, strokeAndFillCase2,
+                                   TestCase::kAllSame_ComparisonExpecation);
+}
+
+template <typename T>
+static void test_stroke_param_impl(skiatest::Reporter* reporter, const Geo& geo,
+                                   std::function<void(SkPaint*, T)> setter, T a, T b,
+                                   bool paramAffectsStroke,
+                                   bool paramAffectsDashAndStroke) {
+    // Set the stroke width so that we don't get hairline. However, call the setter afterward so
+    // that it can override the stroke width.
+    SkPaint strokeA;
+    strokeA.setStyle(SkPaint::kStroke_Style);
+    strokeA.setStrokeWidth(2.f);
+    setter(&strokeA, a);
+    SkPaint strokeB;
+    strokeB.setStyle(SkPaint::kStroke_Style);
+    strokeB.setStrokeWidth(2.f);
+    setter(&strokeB, b);
+
+    TestCase strokeACase(geo, strokeA, reporter);
+    TestCase strokeBCase(geo, strokeB, reporter);
+    if (paramAffectsStroke) {
+        // If stroking is immediately incorporated into a geometric transformation then the base
+        // shapes will differ.
+        if (geo.strokeIsConvertedToFill()) {
+            strokeACase.compare(reporter, strokeBCase,
+                                TestCase::kAllDifferent_ComparisonExpecation);
+        } else {
+            strokeACase.compare(reporter, strokeBCase,
+                                TestCase::kSameUpToStroke_ComparisonExpecation);
+        }
+    } else {
+        strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation);
+    }
+
+    SkPaint strokeAndFillA = strokeA;
+    SkPaint strokeAndFillB = strokeB;
+    strokeAndFillA.setStyle(SkPaint::kStrokeAndFill_Style);
+    strokeAndFillB.setStyle(SkPaint::kStrokeAndFill_Style);
+    TestCase strokeAndFillACase(geo, strokeAndFillA, reporter);
+    TestCase strokeAndFillBCase(geo, strokeAndFillB, reporter);
+    if (paramAffectsStroke) {
+        // If stroking is immediately incorporated into a geometric transformation then the base
+        // shapes will differ.
+        if (geo.strokeAndFillIsConvertedToFill(strokeAndFillA) ||
+            geo.strokeAndFillIsConvertedToFill(strokeAndFillB)) {
+            strokeAndFillACase.compare(reporter, strokeAndFillBCase,
+                                       TestCase::kAllDifferent_ComparisonExpecation);
+        } else {
+            strokeAndFillACase.compare(reporter, strokeAndFillBCase,
+                                       TestCase::kSameUpToStroke_ComparisonExpecation);
+        }
+    } else {
+        strokeAndFillACase.compare(reporter, strokeAndFillBCase,
+                                   TestCase::kAllSame_ComparisonExpecation);
+    }
+
+    // Make sure stroking params don't affect fill style.
+    SkPaint fillA = strokeA, fillB = strokeB;
+    fillA.setStyle(SkPaint::kFill_Style);
+    fillB.setStyle(SkPaint::kFill_Style);
+    TestCase fillACase(geo, fillA, reporter);
+    TestCase fillBCase(geo, fillB, reporter);
+    fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
+
+    // Make sure just applying the dash but not stroke gives the same key for both stroking
+    // variations.
+    SkPaint dashA = strokeA, dashB = strokeB;
+    dashA.setPathEffect(make_dash());
+    dashB.setPathEffect(make_dash());
+    TestCase dashACase(geo, dashA, reporter);
+    TestCase dashBCase(geo, dashB, reporter);
+    if (paramAffectsDashAndStroke) {
+        dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
+    } else {
+        dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation);
+    }
+}
+
+template <typename T>
+static void test_stroke_param(skiatest::Reporter* reporter, const Geo& geo,
+                              std::function<void(SkPaint*, T)> setter, T a, T b) {
+    test_stroke_param_impl(reporter, geo, setter, a, b, true, true);
+};
+
+static void test_stroke_cap(skiatest::Reporter* reporter, const Geo& geo) {
+    SkPaint hairline;
+    hairline.setStrokeWidth(0);
+    hairline.setStyle(SkPaint::kStroke_Style);
+    GrShape shape = geo.makeShape(hairline);
+    // The cap should only affect shapes that may be open.
+    bool affectsStroke = !shape.knownToBeClosed();
+    // Dashing adds ends that need caps.
+    bool affectsDashAndStroke = true;
+    test_stroke_param_impl<SkPaint::Cap>(
+        reporter,
+        geo,
+        [](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
+        SkPaint::kButt_Cap, SkPaint::kRound_Cap,
+        affectsStroke,
+        affectsDashAndStroke);
+};
+
+static bool shape_known_not_to_have_joins(const GrShape& shape) {
+    return shape.asLine(nullptr, nullptr) || shape.isEmpty();
+}
+
+static void test_stroke_join(skiatest::Reporter* reporter, const Geo& geo) {
+    SkPaint hairline;
+    hairline.setStrokeWidth(0);
+    hairline.setStyle(SkPaint::kStroke_Style);
+    GrShape shape = geo.makeShape(hairline);
+    // GrShape recognizes certain types don't have joins and will prevent the join type from
+    // affecting the style key.
+    // Dashing doesn't add additional joins. However, GrShape currently loses track of this
+    // after applying the dash.
+    bool affectsStroke = !shape_known_not_to_have_joins(shape);
+    test_stroke_param_impl<SkPaint::Join>(
+            reporter,
+            geo,
+            [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
+            SkPaint::kRound_Join, SkPaint::kBevel_Join,
+            affectsStroke, true);
+};
+
+static void test_miter_limit(skiatest::Reporter* reporter, const Geo& geo) {
+    auto setMiterJoinAndLimit = [](SkPaint* p, SkScalar miter) {
+        p->setStrokeJoin(SkPaint::kMiter_Join);
+        p->setStrokeMiter(miter);
+    };
+
+    auto setOtherJoinAndLimit = [](SkPaint* p, SkScalar miter) {
+        p->setStrokeJoin(SkPaint::kRound_Join);
+        p->setStrokeMiter(miter);
+    };
+
+    SkPaint hairline;
+    hairline.setStrokeWidth(0);
+    hairline.setStyle(SkPaint::kStroke_Style);
+    GrShape shape = geo.makeShape(hairline);
+    bool mayHaveJoins = !shape_known_not_to_have_joins(shape);
+
+    // The miter limit should affect stroked and dashed-stroked cases when the join type is
+    // miter.
+    test_stroke_param_impl<SkScalar>(
+        reporter,
+        geo,
+        setMiterJoinAndLimit,
+        0.5f, 0.75f,
+        mayHaveJoins,
+        true);
+
+    // The miter limit should not affect stroked and dashed-stroked cases when the join type is
+    // not miter.
+    test_stroke_param_impl<SkScalar>(
+        reporter,
+        geo,
+        setOtherJoinAndLimit,
+        0.5f, 0.75f,
+        false,
+        false);
+}
+
+static void test_dash_fill(skiatest::Reporter* reporter, const Geo& geo) {
+    // A dash with no stroke should have no effect
+    using DashFactoryFn = sk_sp<SkPathEffect>(*)();
+    for (DashFactoryFn md : {&make_dash, &make_null_dash}) {
+        SkPaint dashFill;
+        dashFill.setPathEffect((*md)());
+        TestCase dashFillCase(geo, dashFill, reporter);
+
+        TestCase fillCase(geo, SkPaint(), reporter);
+        dashFillCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
+    }
+}
+
+void test_null_dash(skiatest::Reporter* reporter, const Geo& geo) {
+    SkPaint fill;
+    SkPaint stroke;
+    stroke.setStyle(SkPaint::kStroke_Style);
+    stroke.setStrokeWidth(1.f);
+    SkPaint dash;
+    dash.setStyle(SkPaint::kStroke_Style);
+    dash.setStrokeWidth(1.f);
+    dash.setPathEffect(make_dash());
+    SkPaint nullDash;
+    nullDash.setStyle(SkPaint::kStroke_Style);
+    nullDash.setStrokeWidth(1.f);
+    nullDash.setPathEffect(make_null_dash());
+
+    TestCase fillCase(geo, fill, reporter);
+    TestCase strokeCase(geo, stroke, reporter);
+    TestCase dashCase(geo, dash, reporter);
+    TestCase nullDashCase(geo, nullDash, reporter);
+
+    // We expect the null dash to be ignored so nullDashCase should match strokeCase, always.
+    nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation);
+    // Check whether the fillCase or strokeCase/nullDashCase would undergo a geometric tranformation
+    // on construction in order to determine how to compare the fill and stroke.
+    if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) {
+        nullDashCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation);
+    } else {
+        nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation);
+    }
+    // In the null dash case we may immediately convert to a fill, but not for the normal dash case.
+    if (geo.strokeIsConvertedToFill()) {
+        nullDashCase.compare(reporter, dashCase, TestCase::kAllDifferent_ComparisonExpecation);
+    } else {
+        nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation);
+    }
+}
+
+void test_path_effect_makes_rrect(skiatest::Reporter* reporter, const Geo& geo) {
+    /**
+     * This path effect takes any input path and turns it into a rrect. It passes through stroke
+     * info.
+     */
+    class RRectPathEffect : SkPathEffect {
+    public:
+        static const SkRRect& RRect() {
+            static const SkRRect kRRect = SkRRect::MakeRectXY(SkRect::MakeWH(12, 12), 3, 5);
+            return kRRect;
+        }
+
+        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
+                        const SkRect* cullR) const override {
+            dst->reset();
+            dst->addRRect(RRect());
+            return true;
+        }
+        void computeFastBounds(SkRect* dst, const SkRect& src) const override {
+            *dst = RRect().getBounds();
+        }
+        static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new RRectPathEffect); }
+        Factory getFactory() const override { return nullptr; }
+        void toString(SkString*) const override {}
+    private:
+        RRectPathEffect() {}
+    };
+
+    SkPaint fill;
+    TestCase fillGeoCase(geo, fill, reporter);
+
+    SkPaint pe;
+    pe.setPathEffect(RRectPathEffect::Make());
+    TestCase geoPECase(geo, pe, reporter);
+
+    SkPaint peStroke;
+    peStroke.setPathEffect(RRectPathEffect::Make());
+    peStroke.setStrokeWidth(2.f);
+    peStroke.setStyle(SkPaint::kStroke_Style);
+    TestCase geoPEStrokeCase(geo, peStroke, reporter);
+
+    // Check whether constructing the filled case would cause the base shape to have a different
+    // geometry (because of a geometric transformation upon initial GrShape construction).
+    if (geo.fillChangesGeom()) {
+        fillGeoCase.compare(reporter, geoPECase, TestCase::kAllDifferent_ComparisonExpecation);
+        fillGeoCase.compare(reporter, geoPEStrokeCase,
+                            TestCase::kAllDifferent_ComparisonExpecation);
+    } else {
+        fillGeoCase.compare(reporter, geoPECase, TestCase::kSameUpToPE_ComparisonExpecation);
+        fillGeoCase.compare(reporter, geoPEStrokeCase, TestCase::kSameUpToPE_ComparisonExpecation);
+    }
+    geoPECase.compare(reporter, geoPEStrokeCase,
+                      TestCase::kSameUpToStroke_ComparisonExpecation);
+
+    TestCase rrectFillCase(reporter, RRectPathEffect::RRect(), fill);
+    SkPaint stroke = peStroke;
+    stroke.setPathEffect(nullptr);
+    TestCase rrectStrokeCase(reporter, RRectPathEffect::RRect(), stroke);
+
+    SkRRect rrect;
+    // Applying the path effect should make a SkRRect shape. There is no further stroking in the
+    // geoPECase, so the full style should be the same as just the PE.
+    REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectShape().asRRect(&rrect, nullptr, nullptr,
+                                                                         nullptr));
+    REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
+    REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectKey() == rrectFillCase.baseKey());
+
+    REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr,
+                                                                        nullptr));
+    REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
+    REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleKey() == rrectFillCase.baseKey());
+
+    // In the PE+stroke case applying the full style should be the same as just stroking the rrect.
+    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().asRRect(&rrect, nullptr,
+                                                                               nullptr, nullptr));
+    REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
+    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == rrectFillCase.baseKey());
+
+    REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedFullStyleShape().asRRect(&rrect, nullptr,
+                                                                               nullptr, nullptr));
+    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() ==
+                              rrectStrokeCase.appliedFullStyleKey());
+}
+
+void test_unknown_path_effect(skiatest::Reporter* reporter, const Geo& geo) {
+    /**
+     * This path effect just adds two lineTos to the input path.
+     */
+    class AddLineTosPathEffect : SkPathEffect {
+    public:
+        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
+                        const SkRect* cullR) const override {
+            *dst = src;
+            // To avoid triggering data-based keying of paths with few verbs we add many segments.
+            for (int i = 0; i < 100; ++i) {
+                dst->lineTo(SkIntToScalar(i), SkIntToScalar(i));
+            }
+            return true;
+        }
+        void computeFastBounds(SkRect* dst, const SkRect& src) const override {
+            *dst = src;
+            dst->growToInclude(0, 0);
+            dst->growToInclude(100, 100);
+        }
+        static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new AddLineTosPathEffect); }
+        Factory getFactory() const override { return nullptr; }
+        void toString(SkString*) const override {}
+    private:
+        AddLineTosPathEffect() {}
+    };
+
+     // This path effect should make the keys invalid when it is applied. We only produce a path
+     // effect key for dash path effects. So the only way another arbitrary path effect can produce
+     // a styled result with a key is to produce a non-path shape that has a purely geometric key.
+    SkPaint peStroke;
+    peStroke.setPathEffect(AddLineTosPathEffect::Make());
+    peStroke.setStrokeWidth(2.f);
+    peStroke.setStyle(SkPaint::kStroke_Style);
+    TestCase geoPEStrokeCase(geo, peStroke, reporter);
+    TestCase::SelfExpectations expectations;
+    expectations.fPEHasEffect = true;
+    expectations.fPEHasValidKey = false;
+    expectations.fStrokeApplies = true;
+    geoPEStrokeCase.testExpectations(reporter, expectations);
+}
+
+void test_make_hairline_path_effect(skiatest::Reporter* reporter, const Geo& geo) {
+    /**
+     * This path effect just changes the stroke rec to hairline.
+     */
+    class MakeHairlinePathEffect : SkPathEffect {
+    public:
+        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* strokeRec,
+                        const SkRect* cullR) const override {
+            *dst = src;
+            strokeRec->setHairlineStyle();
+            return true;
+        }
+        void computeFastBounds(SkRect* dst, const SkRect& src) const override { *dst = src; }
+        static sk_sp<SkPathEffect> Make() {
+            return sk_sp<SkPathEffect>(new MakeHairlinePathEffect);
+        }
+        Factory getFactory() const override { return nullptr; }
+        void toString(SkString*) const override {}
+    private:
+        MakeHairlinePathEffect() {}
+    };
+
+    SkPaint fill;
+    SkPaint pe;
+    pe.setPathEffect(MakeHairlinePathEffect::Make());
+
+    TestCase peCase(geo, pe, reporter);
+
+    SkPath a, b, c;
+    peCase.baseShape().asPath(&a);
+    peCase.appliedPathEffectShape().asPath(&b);
+    peCase.appliedFullStyleShape().asPath(&c);
+    if (geo.isNonPath(pe)) {
+        // RRect types can have a change in start index or direction after the PE is applied. This
+        // is because once the PE is applied, GrShape may canonicalize the dir and index since it
+        // is not germane to the styling any longer.
+        // Instead we just check that the paths would fill the same both before and after styling.
+        REPORTER_ASSERT(reporter, paths_fill_same(a, b));
+        REPORTER_ASSERT(reporter, paths_fill_same(a, c));
+    } else {
+        // The base shape cannot perform canonicalization on the path's fill type because of an
+        // unknown path effect. However, after the path effect is applied the resulting hairline
+        // shape will canonicalize the path fill type since hairlines (and stroking in general)
+        // don't distinguish between even/odd and non-zero winding.
+        a.setFillType(b.getFillType());
+        REPORTER_ASSERT(reporter, a == b);
+        REPORTER_ASSERT(reporter, a == c);
+        // If the resulting path is small enough then it will have a key.
+        REPORTER_ASSERT(reporter, paths_fill_same(a, b));
+        REPORTER_ASSERT(reporter, paths_fill_same(a, c));
+        REPORTER_ASSERT(reporter, peCase.appliedPathEffectKey().empty());
+        REPORTER_ASSERT(reporter, peCase.appliedFullStyleKey().empty());
+    }
+    REPORTER_ASSERT(reporter, peCase.appliedPathEffectShape().style().isSimpleHairline());
+    REPORTER_ASSERT(reporter, peCase.appliedFullStyleShape().style().isSimpleHairline());
+}
+
+void test_volatile_path(skiatest::Reporter* reporter, const Geo& geo) {
+    SkPath vPath = geo.path();
+    vPath.setIsVolatile(true);
+
+    SkPaint dashAndStroke;
+    dashAndStroke.setPathEffect(make_dash());
+    dashAndStroke.setStrokeWidth(2.f);
+    dashAndStroke.setStyle(SkPaint::kStroke_Style);
+    TestCase volatileCase(reporter, vPath, dashAndStroke);
+    // We expect a shape made from a volatile path to have a key iff the shape is recognized
+    // as a specialized geometry.
+    if (geo.isNonPath(dashAndStroke)) {
+        REPORTER_ASSERT(reporter, SkToBool(volatileCase.baseKey().count()));
+        // In this case all the keys should be identical to the non-volatile case.
+        TestCase nonVolatileCase(reporter, geo.path(), dashAndStroke);
+        volatileCase.compare(reporter, nonVolatileCase, TestCase::kAllSame_ComparisonExpecation);
+    } else {
+        // None of the keys should be valid.
+        REPORTER_ASSERT(reporter, !SkToBool(volatileCase.baseKey().count()));
+        REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectKey().count()));
+        REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedFullStyleKey().count()));
+        REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectThenStrokeKey().count()));
+    }
+}
+
+void test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const Geo& geo) {
+    /**
+     * This path effect returns an empty path.
+     */
+    class EmptyPathEffect : SkPathEffect {
+    public:
+        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
+                        const SkRect* cullR) const override {
+            dst->reset();
+            return true;
+        }
+        void computeFastBounds(SkRect* dst, const SkRect& src) const override {
+            dst->setEmpty();
+        }
+        static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new EmptyPathEffect); }
+        Factory getFactory() const override { return nullptr; }
+        void toString(SkString*) const override {}
+    private:
+        EmptyPathEffect() {}
+    };
+
+    SkPath emptyPath;
+    GrShape emptyShape(emptyPath);
+    Key emptyKey;
+    make_key(&emptyKey, emptyShape);
+    REPORTER_ASSERT(reporter, emptyShape.isEmpty());
+
+    SkPaint pe;
+    pe.setPathEffect(EmptyPathEffect::Make());
+    TestCase geoCase(geo, pe, reporter);
+    REPORTER_ASSERT(reporter, geoCase.appliedFullStyleKey() == emptyKey);
+    REPORTER_ASSERT(reporter, geoCase.appliedPathEffectKey() == emptyKey);
+    REPORTER_ASSERT(reporter, geoCase.appliedPathEffectThenStrokeKey() == emptyKey);
+    REPORTER_ASSERT(reporter, geoCase.appliedPathEffectShape().isEmpty());
+    REPORTER_ASSERT(reporter, geoCase.appliedFullStyleShape().isEmpty());
+
+    SkPaint peStroke;
+    peStroke.setPathEffect(EmptyPathEffect::Make());
+    peStroke.setStrokeWidth(2.f);
+    peStroke.setStyle(SkPaint::kStroke_Style);
+    TestCase geoPEStrokeCase(geo, peStroke, reporter);
+    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() == emptyKey);
+    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == emptyKey);
+    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectThenStrokeKey() == emptyKey);
+    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().isEmpty());
+    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleShape().isEmpty());
+}
+
+void test_path_effect_fails(skiatest::Reporter* reporter, const Geo& geo) {
+    /**
+     * This path effect always fails to apply.
+     */
+    class FailurePathEffect : SkPathEffect {
+    public:
+        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
+                        const SkRect* cullR) const override {
+            return false;
+        }
+        void computeFastBounds(SkRect* dst, const SkRect& src) const override {
+            *dst = src;
+        }
+        static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new FailurePathEffect); }
+        Factory getFactory() const override { return nullptr; }
+        void toString(SkString*) const override {}
+    private:
+        FailurePathEffect() {}
+    };
+
+    SkPaint fill;
+    TestCase fillCase(geo, fill, reporter);
+
+    SkPaint pe;
+    pe.setPathEffect(FailurePathEffect::Make());
+    TestCase peCase(geo, pe, reporter);
+
+    SkPaint stroke;
+    stroke.setStrokeWidth(2.f);
+    stroke.setStyle(SkPaint::kStroke_Style);
+    TestCase strokeCase(geo, stroke, reporter);
+
+    SkPaint peStroke = stroke;
+    peStroke.setPathEffect(FailurePathEffect::Make());
+    TestCase peStrokeCase(geo, peStroke, reporter);
+
+    // In general the path effect failure can cause some of the TestCase::compare() tests to fail
+    // for at least two reasons: 1) We will initially treat the shape as unkeyable because of the
+    // path effect, but then when the path effect fails we can key it. 2) GrShape will change its
+    // mind about whether a unclosed rect is actually rect. The path effect initially bars us from
+    // closing it but after the effect fails we can (for the fill+pe case). This causes different
+    // routes through GrShape to have equivalent but different representations of the path (closed
+    // or not) but that fill the same.
+    SkPath a;
+    SkPath b;
+    fillCase.appliedPathEffectShape().asPath(&a);
+    peCase.appliedPathEffectShape().asPath(&b);
+    REPORTER_ASSERT(reporter, paths_fill_same(a, b));
+
+    fillCase.appliedFullStyleShape().asPath(&a);
+    peCase.appliedFullStyleShape().asPath(&b);
+    REPORTER_ASSERT(reporter, paths_fill_same(a, b));
+
+    strokeCase.appliedPathEffectShape().asPath(&a);
+    peStrokeCase.appliedPathEffectShape().asPath(&b);
+    REPORTER_ASSERT(reporter, paths_fill_same(a, b));
+
+    strokeCase.appliedFullStyleShape().asPath(&a);
+    peStrokeCase.appliedFullStyleShape().asPath(&b);
+    REPORTER_ASSERT(reporter, paths_fill_same(a, b));
+}
+
+void test_empty_shape(skiatest::Reporter* reporter) {
+    SkPath emptyPath;
+    SkPaint fill;
+    TestCase fillEmptyCase(reporter, emptyPath, fill);
+    REPORTER_ASSERT(reporter, fillEmptyCase.baseShape().isEmpty());
+    REPORTER_ASSERT(reporter, fillEmptyCase.appliedPathEffectShape().isEmpty());
+    REPORTER_ASSERT(reporter, fillEmptyCase.appliedFullStyleShape().isEmpty());
+
+    Key emptyKey(fillEmptyCase.baseKey());
+    REPORTER_ASSERT(reporter, emptyKey.count());
+    TestCase::SelfExpectations expectations;
+    expectations.fStrokeApplies = false;
+    expectations.fPEHasEffect = false;
+    // This will test whether applying style preserves emptiness
+    fillEmptyCase.testExpectations(reporter, expectations);
+
+    // Stroking an empty path should have no effect
+    SkPath emptyPath2;
+    SkPaint stroke;
+    stroke.setStrokeWidth(2.f);
+    stroke.setStyle(SkPaint::kStroke_Style);
+    TestCase strokeEmptyCase(reporter, emptyPath2, stroke);
+    strokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation);
+
+    // Dashing and stroking an empty path should have no effect
+    SkPath emptyPath3;
+    SkPaint dashAndStroke;
+    dashAndStroke.setPathEffect(make_dash());
+    dashAndStroke.setStrokeWidth(2.f);
+    dashAndStroke.setStyle(SkPaint::kStroke_Style);
+    TestCase dashAndStrokeEmptyCase(reporter, emptyPath3, dashAndStroke);
+    dashAndStrokeEmptyCase.compare(reporter, fillEmptyCase,
+                                   TestCase::kAllSame_ComparisonExpecation);
+
+    // A shape made from an empty rrect should behave the same as an empty path.
+    SkRRect emptyRRect = SkRRect::MakeRect(SkRect::MakeEmpty());
+    REPORTER_ASSERT(reporter, emptyRRect.getType() == SkRRect::kEmpty_Type);
+    TestCase dashAndStrokeEmptyRRectCase(reporter, emptyRRect, dashAndStroke);
+    dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase,
+                                        TestCase::kAllSame_ComparisonExpecation);
+
+    // Same for a rect.
+    SkRect emptyRect = SkRect::MakeEmpty();
+    TestCase dashAndStrokeEmptyRectCase(reporter, emptyRect, dashAndStroke);
+    dashAndStrokeEmptyRectCase.compare(reporter, fillEmptyCase,
+                                       TestCase::kAllSame_ComparisonExpecation);
+}
+
+// rect and oval types have rrect start indices that collapse to the same point. Here we select the
+// canonical point in these cases.
+unsigned canonicalize_rrect_start(int s, const SkRRect& rrect) {
+    switch (rrect.getType()) {
+        case SkRRect::kRect_Type:
+            return (s + 1) & 0b110;
+        case SkRRect::kOval_Type:
+            return s & 0b110;
+        default:
+            return s;
+    }
+}
+
+void test_rrect(skiatest::Reporter* r, const SkRRect& rrect) {
+    enum Style {
+        kFill,
+        kStroke,
+        kHairline,
+        kStrokeAndFill
+    };
+
+    // SkStrokeRec has no default cons., so init with kFill before calling the setters below.
+    SkStrokeRec strokeRecs[4] { SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle,
+                                SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle};
+    strokeRecs[kFill].setFillStyle();
+    strokeRecs[kStroke].setStrokeStyle(2.f);
+    strokeRecs[kHairline].setHairlineStyle();
+    strokeRecs[kStrokeAndFill].setStrokeStyle(3.f, true);
+    // Use a bevel join to avoid complications of stroke+filled rects becoming filled rects before
+    // applyStyle() is called.
+    strokeRecs[kStrokeAndFill].setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 1.f);
+    sk_sp<SkPathEffect> dashEffect = make_dash();
+
+    static constexpr Style kStyleCnt = static_cast<Style>(SK_ARRAY_COUNT(strokeRecs));
+
+    auto index = [](bool inverted,
+                    SkPath::Direction dir,
+                    unsigned start,
+                    Style style,
+                    bool dash) -> int {
+        return inverted * (2 * 8 * kStyleCnt * 2) +
+               dir      * (    8 * kStyleCnt * 2) +
+               start    * (        kStyleCnt * 2) +
+               style    * (                    2) +
+               dash;
+    };
+    static const SkPath::Direction kSecondDirection = static_cast<SkPath::Direction>(1);
+    const int cnt = index(true, kSecondDirection, 7, static_cast<Style>(kStyleCnt - 1), true) + 1;
+    SkAutoTArray<GrShape> shapes(cnt);
+    for (bool inverted : {false, true}) {
+        for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+            for (unsigned start = 0; start < 8; ++start) {
+                for (Style style : {kFill, kStroke, kHairline, kStrokeAndFill}) {
+                    for (bool dash : {false, true}) {
+                        sk_sp<SkPathEffect> pe = dash ? dashEffect : nullptr;
+                        shapes[index(inverted, dir, start, style, dash)] =
+                                GrShape(rrect, dir, start, SkToBool(inverted),
+                                        GrStyle(strokeRecs[style], std::move(pe)));
+                    }
+                }
+            }
+        }
+    }
+
+    // Get the keys for some example shape instances that we'll use for comparision against the
+    // rest.
+    static constexpr SkPath::Direction kExamplesDir = SkPath::kCW_Direction;
+    static constexpr unsigned kExamplesStart = 0;
+    const GrShape& exampleFillCase = shapes[index(false, kExamplesDir, kExamplesStart, kFill,
+                                                  false)];
+    Key exampleFillCaseKey;
+    make_key(&exampleFillCaseKey, exampleFillCase);
+
+    const GrShape& exampleStrokeAndFillCase = shapes[index(false, kExamplesDir, kExamplesStart,
+                                                           kStrokeAndFill, false)];
+    Key exampleStrokeAndFillCaseKey;
+    make_key(&exampleStrokeAndFillCaseKey, exampleStrokeAndFillCase);
+
+    const GrShape& exampleInvFillCase = shapes[index(true, kExamplesDir, kExamplesStart, kFill,
+                                                     false)];
+    Key exampleInvFillCaseKey;
+    make_key(&exampleInvFillCaseKey, exampleInvFillCase);
+
+    const GrShape& exampleInvStrokeAndFillCase = shapes[index(true, kExamplesDir, kExamplesStart,
+                                                              kStrokeAndFill, false)];
+    Key exampleInvStrokeAndFillCaseKey;
+    make_key(&exampleInvStrokeAndFillCaseKey, exampleInvStrokeAndFillCase);
+
+    const GrShape& exampleStrokeCase = shapes[index(false, kExamplesDir, kExamplesStart, kStroke,
+                                                    false)];
+    Key exampleStrokeCaseKey;
+    make_key(&exampleStrokeCaseKey, exampleStrokeCase);
+
+    const GrShape& exampleInvStrokeCase = shapes[index(true, kExamplesDir, kExamplesStart, kStroke,
+                                                       false)];
+    Key exampleInvStrokeCaseKey;
+    make_key(&exampleInvStrokeCaseKey, exampleInvStrokeCase);
+
+    const GrShape& exampleHairlineCase = shapes[index(false, kExamplesDir, kExamplesStart,
+                                                      kHairline, false)];
+    Key exampleHairlineCaseKey;
+    make_key(&exampleHairlineCaseKey, exampleHairlineCase);
+
+    const GrShape& exampleInvHairlineCase = shapes[index(true, kExamplesDir, kExamplesStart,
+                                                         kHairline, false)];
+    Key exampleInvHairlineCaseKey;
+    make_key(&exampleInvHairlineCaseKey, exampleInvHairlineCase);
+
+    // These are dummy initializations to suppress warnings.
+    SkRRect queryRR = SkRRect::MakeEmpty();
+    SkPath::Direction queryDir = SkPath::kCW_Direction;
+    unsigned queryStart = ~0U;
+    bool queryInverted = true;
+
+    REPORTER_ASSERT(r, exampleFillCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
+    REPORTER_ASSERT(r, queryRR == rrect);
+    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, 0 == queryStart);
+    REPORTER_ASSERT(r, !queryInverted);
+
+    REPORTER_ASSERT(r, exampleInvFillCase.asRRect(&queryRR, &queryDir, &queryStart,
+                                                  &queryInverted));
+    REPORTER_ASSERT(r, queryRR == rrect);
+    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, 0 == queryStart);
+    REPORTER_ASSERT(r, queryInverted);
+
+    REPORTER_ASSERT(r, exampleStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
+                                                        &queryInverted));
+    REPORTER_ASSERT(r, queryRR == rrect);
+    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, 0 == queryStart);
+    REPORTER_ASSERT(r, !queryInverted);
+
+    REPORTER_ASSERT(r, exampleInvStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
+                                                           &queryInverted));
+    REPORTER_ASSERT(r, queryRR == rrect);
+    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, 0 == queryStart);
+    REPORTER_ASSERT(r, queryInverted);
+
+    REPORTER_ASSERT(r, exampleHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
+                                                   &queryInverted));
+    REPORTER_ASSERT(r, queryRR == rrect);
+    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, 0 == queryStart);
+    REPORTER_ASSERT(r, !queryInverted);
+
+    REPORTER_ASSERT(r, exampleInvHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
+                                                      &queryInverted));
+    REPORTER_ASSERT(r, queryRR == rrect);
+    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, 0 == queryStart);
+    REPORTER_ASSERT(r, queryInverted);
+
+    REPORTER_ASSERT(r, exampleStrokeCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
+    REPORTER_ASSERT(r, queryRR == rrect);
+    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, 0 == queryStart);
+    REPORTER_ASSERT(r, !queryInverted);
+
+    REPORTER_ASSERT(r, exampleInvStrokeCase.asRRect(&queryRR, &queryDir, &queryStart,
+                                                    &queryInverted));
+    REPORTER_ASSERT(r, queryRR == rrect);
+    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, 0 == queryStart);
+    REPORTER_ASSERT(r, queryInverted);
+
+    // Remember that the key reflects the geometry before styling is applied.
+    REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvFillCaseKey);
+    REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeAndFillCaseKey);
+    REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeAndFillCaseKey);
+    REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeCaseKey);
+    REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeCaseKey);
+    REPORTER_ASSERT(r, exampleFillCaseKey == exampleHairlineCaseKey);
+    REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvHairlineCaseKey);
+    REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvFillCaseKey);
+    REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvStrokeCaseKey);
+    REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvHairlineCaseKey);
+
+    for (bool inverted : {false, true}) {
+        for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+            for (unsigned start = 0; start < 8; ++start) {
+                for (bool dash : {false, true}) {
+                    const GrShape& fillCase = shapes[index(inverted, dir, start, kFill, dash)];
+                    Key fillCaseKey;
+                    make_key(&fillCaseKey, fillCase);
+
+                    const GrShape& strokeAndFillCase = shapes[index(inverted, dir, start,
+                                                                    kStrokeAndFill, dash)];
+                    Key strokeAndFillCaseKey;
+                    make_key(&strokeAndFillCaseKey, strokeAndFillCase);
+
+                    // Both fill and stroke-and-fill shapes must respect the inverseness and both
+                    // ignore dashing.
+                    REPORTER_ASSERT(r, !fillCase.style().pathEffect());
+                    REPORTER_ASSERT(r, !strokeAndFillCase.style().pathEffect());
+                    TestCase a(fillCase, r);
+                    TestCase b(inverted ? exampleInvFillCase : exampleFillCase, r);
+                    TestCase c(strokeAndFillCase, r);
+                    TestCase d(inverted ? exampleInvStrokeAndFillCase
+                                        : exampleStrokeAndFillCase, r);
+                    a.compare(r, b, TestCase::kAllSame_ComparisonExpecation);
+                    c.compare(r, d, TestCase::kAllSame_ComparisonExpecation);
+
+                    const GrShape& strokeCase = shapes[index(inverted, dir, start, kStroke, dash)];
+                    const GrShape& hairlineCase = shapes[index(inverted, dir, start, kHairline,
+                                                               dash)];
+
+                    TestCase e(strokeCase, r);
+                    TestCase g(hairlineCase, r);
+
+                    // Both hairline and stroke shapes must respect the dashing.
+                    if (dash) {
+                        // Dashing always ignores the inverseness. skbug.com/5421
+                        TestCase f(exampleStrokeCase, r);
+                        TestCase h(exampleHairlineCase, r);
+                        unsigned expectedStart = canonicalize_rrect_start(start, rrect);
+                        REPORTER_ASSERT(r, strokeCase.style().pathEffect());
+                        REPORTER_ASSERT(r, hairlineCase.style().pathEffect());
+
+                        REPORTER_ASSERT(r, strokeCase.asRRect(&queryRR, &queryDir, &queryStart,
+                                                              &queryInverted));
+                        REPORTER_ASSERT(r, queryRR == rrect);
+                        REPORTER_ASSERT(r, queryDir == dir);
+                        REPORTER_ASSERT(r, queryStart == expectedStart);
+                        REPORTER_ASSERT(r, !queryInverted);
+                        REPORTER_ASSERT(r, hairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
+                                                                &queryInverted));
+                        REPORTER_ASSERT(r, queryRR == rrect);
+                        REPORTER_ASSERT(r, queryDir == dir);
+                        REPORTER_ASSERT(r, queryStart == expectedStart);
+                        REPORTER_ASSERT(r, !queryInverted);
+
+                        // The pre-style case for the dash will match the non-dash example iff the
+                        // dir and start match (dir=cw, start=0).
+                        if (0 == expectedStart && SkPath::kCW_Direction == dir) {
+                            e.compare(r, f, TestCase::kSameUpToPE_ComparisonExpecation);
+                            g.compare(r, h, TestCase::kSameUpToPE_ComparisonExpecation);
+                        } else {
+                            e.compare(r, f, TestCase::kAllDifferent_ComparisonExpecation);
+                            g.compare(r, h, TestCase::kAllDifferent_ComparisonExpecation);
+                        }
+                    } else {
+                        TestCase f(inverted ? exampleInvStrokeCase : exampleStrokeCase, r);
+                        TestCase h(inverted ? exampleInvHairlineCase : exampleHairlineCase, r);
+                        REPORTER_ASSERT(r, !strokeCase.style().pathEffect());
+                        REPORTER_ASSERT(r, !hairlineCase.style().pathEffect());
+                        e.compare(r, f, TestCase::kAllSame_ComparisonExpecation);
+                        g.compare(r, h, TestCase::kAllSame_ComparisonExpecation);
+                    }
+                }
+            }
+        }
+    }
+}
+
+void test_lines(skiatest::Reporter* r) {
+    static constexpr SkPoint kA { 1,  1};
+    static constexpr SkPoint kB { 5, -9};
+    static constexpr SkPoint kC {-3, 17};
+
+    SkPath lineAB;
+    lineAB.moveTo(kA);
+    lineAB.lineTo(kB);
+
+    SkPath lineBA;
+    lineBA.moveTo(kB);
+    lineBA.lineTo(kA);
+
+    SkPath lineAC;
+    lineAC.moveTo(kB);
+    lineAC.lineTo(kC);
+
+    SkPath invLineAB = lineAB;
+    invLineAB.setFillType(SkPath::kInverseEvenOdd_FillType);
+
+    SkPaint fill;
+    SkPaint stroke;
+    stroke.setStyle(SkPaint::kStroke_Style);
+    stroke.setStrokeWidth(2.f);
+    SkPaint hairline;
+    hairline.setStyle(SkPaint::kStroke_Style);
+    hairline.setStrokeWidth(0.f);
+    SkPaint dash = stroke;
+    dash.setPathEffect(make_dash());
+
+    TestCase fillAB(r, lineAB, fill);
+    TestCase fillEmpty(r, SkPath(), fill);
+    fillAB.compare(r, fillEmpty, TestCase::kAllSame_ComparisonExpecation);
+    REPORTER_ASSERT(r, !fillAB.baseShape().asLine(nullptr, nullptr));
+
+    TestCase strokeAB(r, lineAB, stroke);
+    TestCase strokeBA(r, lineBA, stroke);
+    TestCase strokeAC(r, lineAC, stroke);
+
+    TestCase hairlineAB(r, lineAB, hairline);
+    TestCase hairlineBA(r, lineBA, hairline);
+    TestCase hairlineAC(r, lineAC, hairline);
+
+    TestCase dashAB(r, lineAB, dash);
+    TestCase dashBA(r, lineBA, dash);
+    TestCase dashAC(r, lineAC, dash);
+
+    strokeAB.compare(r, fillAB, TestCase::kAllDifferent_ComparisonExpecation);
+
+    strokeAB.compare(r, strokeBA, TestCase::kAllSame_ComparisonExpecation);
+    strokeAB.compare(r, strokeAC, TestCase::kAllDifferent_ComparisonExpecation);
+
+    hairlineAB.compare(r, hairlineBA, TestCase::kAllSame_ComparisonExpecation);
+    hairlineAB.compare(r, hairlineAC, TestCase::kAllDifferent_ComparisonExpecation);
+
+    dashAB.compare(r, dashBA, TestCase::kAllDifferent_ComparisonExpecation);
+    dashAB.compare(r, dashAC, TestCase::kAllDifferent_ComparisonExpecation);
+
+    strokeAB.compare(r, hairlineAB, TestCase::kSameUpToStroke_ComparisonExpecation);
+
+    // One of dashAB or dashBA should have the same line as strokeAB. It depends upon how
+    // GrShape canonicalizes line endpoints (when it can, i.e. when not dashed).
+    bool canonicalizeAsAB;
+    SkPoint canonicalPts[2] {kA, kB};
+    // Init these to suppress warnings.
+    bool inverted = true;
+    SkPoint pts[2] {{0, 0}, {0, 0}};
+    REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted);
+    if (pts[0] == kA && pts[1] == kB) {
+        canonicalizeAsAB = true;
+    } else if (pts[1] == kA && pts[0] == kB) {
+        canonicalizeAsAB = false;
+        SkTSwap(canonicalPts[0], canonicalPts[1]);
+    } else {
+        ERRORF(r, "Should return pts (a,b) or (b, a)");
+        return;
+    };
+
+    strokeAB.compare(r, canonicalizeAsAB ? dashAB : dashBA,
+                     TestCase::kSameUpToPE_ComparisonExpecation);
+    REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted &&
+                       pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
+    REPORTER_ASSERT(r, hairlineAB.baseShape().asLine(pts, &inverted) && !inverted &&
+                       pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
+    REPORTER_ASSERT(r, dashAB.baseShape().asLine(pts, &inverted) && !inverted &&
+                       pts[0] == kA && pts[1] == kB);
+    REPORTER_ASSERT(r, dashBA.baseShape().asLine(pts, &inverted) && !inverted &&
+                       pts[0] == kB && pts[1] == kA);
+
+
+    TestCase strokeInvAB(r, invLineAB, stroke);
+    TestCase hairlineInvAB(r, invLineAB, hairline);
+    TestCase dashInvAB(r, invLineAB, dash);
+    strokeInvAB.compare(r, strokeAB, TestCase::kAllDifferent_ComparisonExpecation);
+    hairlineInvAB.compare(r, hairlineAB, TestCase::kAllDifferent_ComparisonExpecation);
+    // Dashing ignores inverse.
+    dashInvAB.compare(r, dashAB, TestCase::kAllSame_ComparisonExpecation);
+
+    REPORTER_ASSERT(r, strokeInvAB.baseShape().asLine(pts, &inverted) && inverted &&
+                       pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
+    REPORTER_ASSERT(r, hairlineInvAB.baseShape().asLine(pts, &inverted) && inverted &&
+                       pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
+    // Dashing ignores inverse.
+    REPORTER_ASSERT(r, dashInvAB.baseShape().asLine(pts, &inverted) && !inverted &&
+                       pts[0] == kA && pts[1] == kB);
+
+}
+
+static void test_stroked_lines(skiatest::Reporter* r) {
+    // Paints to try
+    SkPaint buttCap;
+    buttCap.setStyle(SkPaint::kStroke_Style);
+    buttCap.setStrokeWidth(4);
+    buttCap.setStrokeCap(SkPaint::kButt_Cap);
+
+    SkPaint squareCap = buttCap;
+    squareCap.setStrokeCap(SkPaint::kSquare_Cap);
+
+    SkPaint roundCap = buttCap;
+    roundCap.setStrokeCap(SkPaint::kRound_Cap);
+
+    // vertical
+    SkPath linePath;
+    linePath.moveTo(4, 4);
+    linePath.lineTo(4, 5);
+
+    SkPaint fill;
+
+    TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 4, 6, 5), fill),
+                                           TestCase::kAllSame_ComparisonExpecation);
+
+    TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 7), fill),
+                                             TestCase::kAllSame_ComparisonExpecation);
+
+    TestCase(r, linePath, roundCap).compare(r,
+        TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 7), 2, 2), fill),
+        TestCase::kAllSame_ComparisonExpecation);
+
+    // horizontal
+    linePath.reset();
+    linePath.moveTo(4, 4);
+    linePath.lineTo(5, 4);
+
+    TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeLTRB(4, 2, 5, 6), fill),
+                                           TestCase::kAllSame_ComparisonExpecation);
+    TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 7, 6), fill),
+                                             TestCase::kAllSame_ComparisonExpecation);
+    TestCase(r, linePath, roundCap).compare(r,
+         TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 7, 6), 2, 2), fill),
+         TestCase::kAllSame_ComparisonExpecation);
+
+    // point
+    linePath.reset();
+    linePath.moveTo(4, 4);
+    linePath.lineTo(4, 4);
+
+    TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeEmpty(), fill),
+                                           TestCase::kAllSame_ComparisonExpecation);
+    TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 6), fill),
+                                             TestCase::kAllSame_ComparisonExpecation);
+    TestCase(r, linePath, roundCap).compare(r,
+        TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 6), 2, 2), fill),
+        TestCase::kAllSame_ComparisonExpecation);
+}
+
+static void test_short_path_keys(skiatest::Reporter* r) {
+    SkPaint paints[4];
+    paints[1].setStyle(SkPaint::kStroke_Style);
+    paints[1].setStrokeWidth(5.f);
+    paints[2].setStyle(SkPaint::kStroke_Style);
+    paints[2].setStrokeWidth(0.f);
+    paints[3].setStyle(SkPaint::kStrokeAndFill_Style);
+    paints[3].setStrokeWidth(5.f);
+
+    auto compare = [r, &paints] (const SkPath& pathA, const SkPath& pathB,
+                                 TestCase::ComparisonExpecation expectation) {
+        SkPath volatileA = pathA;
+        SkPath volatileB = pathB;
+        volatileA.setIsVolatile(true);
+        volatileB.setIsVolatile(true);
+        for (const SkPaint& paint : paints) {
+            REPORTER_ASSERT(r, !GrShape(volatileA, paint).hasUnstyledKey());
+            REPORTER_ASSERT(r, !GrShape(volatileB, paint).hasUnstyledKey());
+            for (PathGeo::Invert invert : {PathGeo::Invert::kNo, PathGeo::Invert::kYes}) {
+                TestCase caseA(PathGeo(pathA, invert), paint, r);
+                TestCase caseB(PathGeo(pathB, invert), paint, r);
+                caseA.compare(r, caseB, expectation);
+            }
+        }
+    };
+
+    SkPath pathA;
+    SkPath pathB;
+
+    // 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);
+
+    // 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);
+
+    // 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);
+
+    // 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);
+
+    // 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);
+}
+
+DEF_TEST(GrShape, reporter) {
+    SkTArray<std::unique_ptr<Geo>> geos;
+    SkTArray<std::unique_ptr<RRectPathGeo>> rrectPathGeos;
+
+    for (auto r : { SkRect::MakeWH(10, 20),
+                    SkRect::MakeWH(-10, -20),
+                    SkRect::MakeWH(-10, 20),
+                    SkRect::MakeWH(10, -20)}) {
+        geos.emplace_back(new RectGeo(r));
+        SkPath rectPath;
+        rectPath.addRect(r);
+        geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
+                                           PathGeo::Invert::kNo));
+        geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
+                                           PathGeo::Invert::kYes));
+        rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
+                                                    PathGeo::Invert::kNo));
+    }
+    for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)),
+                     SkRRect::MakeRectXY(SkRect::MakeWH(10, 10), 3, 4),
+                     SkRRect::MakeOval(SkRect::MakeWH(20, 20))}) {
+        geos.emplace_back(new RRectGeo(rr));
+        test_rrect(reporter, rr);
+        SkPath rectPath;
+        rectPath.addRRect(rr);
+        geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes,
+                                           PathGeo::Invert::kNo));
+        geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes,
+                                           PathGeo::Invert::kYes));
+        rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, rr,
+                                                    RRectPathGeo::RRectForStroke::kYes,
+                                                    PathGeo::Invert::kNo));
+    }
+
+    SkPath openRectPath;
+    openRectPath.moveTo(0, 0);
+    openRectPath.lineTo(10, 0);
+    openRectPath.lineTo(10, 10);
+    openRectPath.lineTo(0, 10);
+    geos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
+                                       RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kNo));
+    geos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
+                                       RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kYes));
+    rrectPathGeos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
+                                                RRectPathGeo::RRectForStroke::kNo,
+                                                PathGeo::Invert::kNo));
+
+    SkPath quadPath;
+    quadPath.quadTo(10, 10, 5, 8);
+    geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kNo));
+    geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kYes));
+
+    SkPath linePath;
+    linePath.lineTo(10, 10);
+    geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kNo));
+    geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kYes));
+
+    // Horizontal and vertical paths become rrects when stroked.
+    SkPath vLinePath;
+    vLinePath.lineTo(0, 10);
+    geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kNo));
+    geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kYes));
+
+    SkPath hLinePath;
+    hLinePath.lineTo(10, 0);
+    geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kNo));
+    geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kYes));
+
+    for (int i = 0; i < geos.count(); ++i) {
+        test_basic(reporter, *geos[i]);
+        test_scale(reporter, *geos[i]);
+        test_dash_fill(reporter, *geos[i]);
+        test_null_dash(reporter, *geos[i]);
+        // Test modifying various stroke params.
+        test_stroke_param<SkScalar>(
+                reporter, *geos[i],
+                [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
+                SkIntToScalar(2), SkIntToScalar(4));
+        test_stroke_join(reporter, *geos[i]);
+        test_stroke_cap(reporter, *geos[i]);
+        test_miter_limit(reporter, *geos[i]);
+        test_path_effect_makes_rrect(reporter, *geos[i]);
+        test_unknown_path_effect(reporter, *geos[i]);
+        test_path_effect_makes_empty_shape(reporter, *geos[i]);
+        test_path_effect_fails(reporter, *geos[i]);
+        test_make_hairline_path_effect(reporter, *geos[i]);
+        test_volatile_path(reporter, *geos[i]);
+    }
+
+    for (int i = 0; i < rrectPathGeos.count(); ++i) {
+        const RRectPathGeo& rrgeo = *rrectPathGeos[i];
+        SkPaint fillPaint;
+        TestCase fillPathCase(reporter, rrgeo.path(), fillPaint);
+        SkRRect rrect;
+        REPORTER_ASSERT(reporter, rrgeo.isNonPath(fillPaint) ==
+                                  fillPathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
+                                                                   nullptr));
+        if (rrgeo.isNonPath(fillPaint)) {
+            TestCase fillPathCase2(reporter, rrgeo.path(), fillPaint);
+            REPORTER_ASSERT(reporter, rrect == rrgeo.rrect());
+            TestCase fillRRectCase(reporter, rrect, fillPaint);
+            fillPathCase2.compare(reporter, fillRRectCase,
+                                  TestCase::kAllSame_ComparisonExpecation);
+        }
+        SkPaint strokePaint;
+        strokePaint.setStrokeWidth(3.f);
+        strokePaint.setStyle(SkPaint::kStroke_Style);
+        TestCase strokePathCase(reporter, rrgeo.path(), strokePaint);
+        if (rrgeo.isNonPath(strokePaint)) {
+            REPORTER_ASSERT(reporter, strokePathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
+                                                                         nullptr));
+            REPORTER_ASSERT(reporter, rrect == rrgeo.rrect());
+            TestCase strokeRRectCase(reporter, rrect, strokePaint);
+            strokePathCase.compare(reporter, strokeRRectCase,
+                                   TestCase::kAllSame_ComparisonExpecation);
+        }
+    }
+
+    // Test a volatile empty path.
+    test_volatile_path(reporter, PathGeo(SkPath(), PathGeo::Invert::kNo));
+
+    test_empty_shape(reporter);
+
+    test_lines(reporter);
+
+    test_stroked_lines(reporter);
+
+    test_short_path_keys(reporter);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/GrSurfaceTest.cpp b/src/third_party/skia/tests/GrSurfaceTest.cpp
index a2ed629..d67f103 100644
--- a/src/third_party/skia/tests/GrSurfaceTest.cpp
+++ b/src/third_party/skia/tests/GrSurfaceTest.cpp
@@ -5,57 +5,240 @@
  * found in the LICENSE file.
  */
 
+#include "SkTypes.h"
+
 #if SK_SUPPORT_GPU
 
 #include "GrContext.h"
-#include "GrContextFactory.h"
-#include "GrRenderTarget.h"
+#include "GrContextPriv.h"
+#include "GrGpu.h"
+#include "GrResourceProvider.h"
+#include "GrTest.h"
 #include "GrTexture.h"
-#include "SkTypes.h"
+#include "GrSurfacePriv.h"
+#include "SkMipMap.h"
 #include "Test.h"
 
-DEF_GPUTEST(GrSurface, reporter, factory) {
-    GrContext* context = factory->get(GrContextFactory::kNull_GLContextType);
-    if (context) {
-        GrTextureDesc desc;
-        desc.fConfig = kSkia8888_GrPixelConfig;
-        desc.fFlags = kRenderTarget_GrTextureFlagBit;
-        desc.fWidth = 256;
-        desc.fHeight = 256;
-        desc.fSampleCnt = 0;
-        GrSurface* texRT1 = context->createUncachedTexture(desc, NULL, 0);
-        GrSurface* texRT2 = context->createUncachedTexture(desc, NULL, 0);
-        desc.fFlags = kNone_GrTextureFlags;
-        GrSurface* tex1 = context->createUncachedTexture(desc, NULL, 0);
+// Tests that GrSurface::asTexture(), GrSurface::asRenderTarget(), and static upcasting of texture
+// and render targets to GrSurface all work as expected.
+DEF_GPUTEST_FOR_NULLGL_CONTEXT(GrSurface, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    GrSurfaceDesc desc;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fWidth = 256;
+    desc.fHeight = 256;
+    desc.fSampleCnt = 0;
+    sk_sp<GrSurface> texRT1 = context->resourceProvider()->createTexture(desc, SkBudgeted::kNo);
 
-        REPORTER_ASSERT(reporter, texRT1->isSameAs(texRT1));
-        REPORTER_ASSERT(reporter, texRT1->isSameAs(texRT1->asRenderTarget()));
-        REPORTER_ASSERT(reporter, texRT1->asRenderTarget()->isSameAs(texRT1));
-        REPORTER_ASSERT(reporter, !texRT2->isSameAs(texRT1));
-        REPORTER_ASSERT(reporter, !texRT2->asRenderTarget()->isSameAs(texRT1));
-        REPORTER_ASSERT(reporter, !texRT2->isSameAs(texRT1->asRenderTarget()));
-        REPORTER_ASSERT(reporter, !texRT2->isSameAs(tex1));
-        REPORTER_ASSERT(reporter, !texRT2->asRenderTarget()->isSameAs(tex1));
+    REPORTER_ASSERT(reporter, texRT1.get() == texRT1->asRenderTarget());
+    REPORTER_ASSERT(reporter, texRT1.get() == texRT1->asTexture());
+    REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT1->asRenderTarget()) ==
+                    texRT1->asTexture());
+    REPORTER_ASSERT(reporter, texRT1->asRenderTarget() ==
+                    static_cast<GrSurface*>(texRT1->asTexture()));
+    REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT1->asRenderTarget()) ==
+                    static_cast<GrSurface*>(texRT1->asTexture()));
 
-        GrBackendTextureDesc backendDesc;
-        backendDesc.fConfig = kSkia8888_GrPixelConfig;
-        backendDesc.fFlags = kRenderTarget_GrBackendTextureFlag;
-        backendDesc.fWidth = 256;
-        backendDesc.fHeight = 256;
-        backendDesc.fSampleCnt = 0;
-        backendDesc.fTextureHandle = 5;
-        GrSurface* externalTexRT = context->wrapBackendTexture(backendDesc);
-        REPORTER_ASSERT(reporter, externalTexRT->isSameAs(externalTexRT));
-        REPORTER_ASSERT(reporter, externalTexRT->isSameAs(externalTexRT->asRenderTarget()));
-        REPORTER_ASSERT(reporter, externalTexRT->asRenderTarget()->isSameAs(externalTexRT));
-        REPORTER_ASSERT(reporter, !externalTexRT->isSameAs(texRT1));
-        REPORTER_ASSERT(reporter, !externalTexRT->asRenderTarget()->isSameAs(texRT1));
+    desc.fFlags = kNone_GrSurfaceFlags;
+    sk_sp<GrTexture> tex1 = context->resourceProvider()->createTexture(desc, SkBudgeted::kNo);
+    REPORTER_ASSERT(reporter, nullptr == tex1->asRenderTarget());
+    REPORTER_ASSERT(reporter, tex1.get() == tex1->asTexture());
+    REPORTER_ASSERT(reporter, static_cast<GrSurface*>(tex1.get()) == tex1->asTexture());
 
-        texRT1->unref();
-        texRT2->unref();
-        tex1->unref();
-        externalTexRT->unref();
+    GrBackendObject backendTexHandle = context->getGpu()->createTestingOnlyBackendTexture(
+        nullptr, 256, 256, kRGBA_8888_GrPixelConfig);
+    GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                               256,
+                                                               256,
+                                                               kRGBA_8888_GrPixelConfig,
+                                                               backendTexHandle);
+
+    sk_sp<GrSurface> texRT2 = context->resourceProvider()->wrapRenderableBackendTexture(
+            backendTex, kTopLeft_GrSurfaceOrigin, 0, kBorrow_GrWrapOwnership);
+
+    REPORTER_ASSERT(reporter, texRT2.get() == texRT2->asRenderTarget());
+    REPORTER_ASSERT(reporter, texRT2.get() == texRT2->asTexture());
+    REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT2->asRenderTarget()) ==
+                    texRT2->asTexture());
+    REPORTER_ASSERT(reporter, texRT2->asRenderTarget() ==
+                    static_cast<GrSurface*>(texRT2->asTexture()));
+    REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT2->asRenderTarget()) ==
+                    static_cast<GrSurface*>(texRT2->asTexture()));
+
+    context->getGpu()->deleteTestingOnlyBackendTexture(backendTexHandle);
+}
+
+// This test checks that the isConfigTexturable and isConfigRenderable are
+// consistent with createTexture's result.
+DEF_GPUTEST_FOR_ALL_CONTEXTS(GrSurfaceRenderability, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    GrResourceProvider* resourceProvider = context->resourceProvider();
+    const GrCaps* caps = context->caps();
+
+    GrPixelConfig configs[] = {
+        kUnknown_GrPixelConfig,
+        kAlpha_8_GrPixelConfig,
+        kGray_8_GrPixelConfig,
+        kRGB_565_GrPixelConfig,
+        kRGBA_4444_GrPixelConfig,
+        kRGBA_8888_GrPixelConfig,
+        kBGRA_8888_GrPixelConfig,
+        kSRGBA_8888_GrPixelConfig,
+        kSBGRA_8888_GrPixelConfig,
+        kRGBA_8888_sint_GrPixelConfig,
+        kRGBA_float_GrPixelConfig,
+        kRG_float_GrPixelConfig,
+        kAlpha_half_GrPixelConfig,
+        kRGBA_half_GrPixelConfig,
+    };
+    SkASSERT(kGrPixelConfigCnt == SK_ARRAY_COUNT(configs));
+
+    GrSurfaceDesc desc;
+    desc.fWidth = 64;
+    desc.fHeight = 64;
+
+    // Enough space for the first mip of our largest pixel config
+    const size_t pixelBufferSize = desc.fWidth * desc.fHeight *
+                                   GrBytesPerPixel(kRGBA_float_GrPixelConfig);
+    std::unique_ptr<char[]> pixelData(new char[pixelBufferSize]);
+    memset(pixelData.get(), 0, pixelBufferSize);
+
+    // We re-use the same mip level objects (with updated pointers and rowBytes) for each config
+    const int levelCount = SkMipMap::ComputeLevelCount(desc.fWidth, desc.fHeight) + 1;
+    std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[levelCount]);
+
+    for (GrPixelConfig config : configs) {
+        for (GrSurfaceOrigin origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
+            desc.fFlags = kNone_GrSurfaceFlags;
+            desc.fOrigin = origin;
+            desc.fSampleCnt = 0;
+            desc.fConfig = config;
+
+            sk_sp<GrSurface> tex = resourceProvider->createTexture(desc, SkBudgeted::kNo);
+            REPORTER_ASSERT(reporter, SkToBool(tex.get()) == caps->isConfigTexturable(desc.fConfig));
+
+            size_t rowBytes = desc.fWidth * GrBytesPerPixel(desc.fConfig);
+            for (int i = 0; i < levelCount; ++i) {
+                texels[i].fPixels = pixelData.get();
+                texels[i].fRowBytes = rowBytes >> i;
+            }
+            sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferredMipMap(resourceProvider,
+                                                                             desc, SkBudgeted::kNo,
+                                                                             texels.get(),
+                                                                             levelCount);
+            REPORTER_ASSERT(reporter, SkToBool(proxy.get()) ==
+                            (caps->isConfigTexturable(desc.fConfig) &&
+                             caps->mipMapSupport() &&
+                             !GrPixelConfigIsSint(desc.fConfig)));
+
+            desc.fFlags = kRenderTarget_GrSurfaceFlag;
+            tex = resourceProvider->createTexture(desc, SkBudgeted::kNo);
+            REPORTER_ASSERT(reporter, SkToBool(tex.get()) == caps->isConfigRenderable(config, false));
+
+            desc.fSampleCnt = 4;
+            tex = resourceProvider->createTexture(desc, SkBudgeted::kNo);
+            REPORTER_ASSERT(reporter, SkToBool(tex.get()) == caps->isConfigRenderable(config, true));
+        }
     }
 }
 
+#include "GrDrawingManager.h"
+#include "GrSurfaceProxy.h"
+#include "GrTextureContext.h"
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(InitialTextureClear, reporter, context_info) {
+    static constexpr int kSize = 100;
+    GrSurfaceDesc desc;
+    desc.fWidth = desc.fHeight = kSize;
+    std::unique_ptr<uint32_t[]> data(new uint32_t[kSize * kSize]);
+    GrContext* context = context_info.grContext();
+    for (int c = 0; c <= kLast_GrPixelConfig; ++c) {
+        desc.fConfig = static_cast<GrPixelConfig>(c);
+        if (!context_info.grContext()->caps()->isConfigTexturable(desc.fConfig)) {
+            continue;
+        }
+        desc.fFlags = kPerformInitialClear_GrSurfaceFlag;
+        for (bool rt : {false, true}) {
+            if (rt && !context->caps()->isConfigRenderable(desc.fConfig, false)) {
+                continue;
+            }
+            desc.fFlags |= rt ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
+            for (bool mipped : {false, true}) {
+                desc.fIsMipMapped = mipped;
+                for (GrSurfaceOrigin origin :
+                     {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
+                    desc.fOrigin = origin;
+                    for (bool approx : {false, true}) {
+                        auto resourceProvider = context->resourceProvider();
+                        // Try directly creating the texture.
+                        // Do this twice in an attempt to hit the cache on the second time through.
+                        for (int i = 0; i < 2; ++i) {
+                            sk_sp<GrTexture> tex;
+                            if (approx) {
+                                tex = sk_sp<GrTexture>(
+                                        resourceProvider->createApproxTexture(desc, 0));
+                            } else {
+                                tex = resourceProvider->createTexture(desc, SkBudgeted::kYes);
+                            }
+                            if (!tex) {
+                                continue;
+                            }
+                            auto proxy = GrSurfaceProxy::MakeWrapped(std::move(tex));
+                            auto texCtx = context->contextPriv().makeWrappedSurfaceContext(
+                                    std::move(proxy), nullptr);
+                            SkImageInfo info = SkImageInfo::Make(
+                                    kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+                            memset(data.get(), 0xAB, kSize * kSize * sizeof(uint32_t));
+                            if (texCtx->readPixels(info, data.get(), 0, 0, 0)) {
+                                uint32_t cmp = GrPixelConfigIsOpaque(desc.fConfig) ? 0xFF000000 : 0;
+                                for (int i = 0; i < kSize * kSize; ++i) {
+                                    if (cmp != data.get()[i]) {
+                                        ERRORF(reporter, "Failed on config %d", desc.fConfig);
+                                        break;
+                                    }
+                                }
+                            }
+                            memset(data.get(), 0xBC, kSize * kSize * sizeof(uint32_t));
+                            // Here we overwrite the texture so that the second time through we
+                            // test against recycling without reclearing.
+                            if (0 == i) {
+                                texCtx->writePixels(info, data.get(), 0, 0, 0);
+                            }
+                        }
+                        context->purgeAllUnlockedResources();
+
+                        // Try creating the texture as a deferred proxy.
+                        for (int i = 0; i < 2; ++i) {
+                            auto surfCtx = context->contextPriv().makeDeferredSurfaceContext(
+                                    desc, approx ? SkBackingFit::kApprox : SkBackingFit::kExact,
+                                    SkBudgeted::kYes);
+                            if (!surfCtx) {
+                                continue;
+                            }
+                            SkImageInfo info = SkImageInfo::Make(
+                                    kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+                            memset(data.get(), 0xAB, kSize * kSize * sizeof(uint32_t));
+                            if (surfCtx->readPixels(info, data.get(), 0, 0, 0)) {
+                                uint32_t cmp = GrPixelConfigIsOpaque(desc.fConfig) ? 0xFF000000 : 0;
+                                for (int i = 0; i < kSize * kSize; ++i) {
+                                    if (cmp != data.get()[i]) {
+                                        ERRORF(reporter, "Failed on config %d", desc.fConfig);
+                                        break;
+                                    }
+                                }
+                            }
+                            // Here we overwrite the texture so that the second time through we
+                            // test against recycling without reclearing.
+                            if (0 == i) {
+                                surfCtx->writePixels(info, data.get(), 0, 0, 0);
+                            }
+                        }
+                        context->purgeAllUnlockedResources();
+                    }
+                }
+            }
+        }
+    }
+}
 #endif
diff --git a/src/third_party/skia/tests/GrTBSearchTest.cpp b/src/third_party/skia/tests/GrTBSearchTest.cpp
deleted file mode 100644
index d057807..0000000
--- a/src/third_party/skia/tests/GrTBSearchTest.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-// This is a GPU-backend specific test
-#if SK_SUPPORT_GPU
-
-#include "Test.h"
-
-// If we aren't inheriting these as #defines from elsewhere,
-// clang demands they be declared before we #include the template
-// that relies on them.
-static bool LT(const int& elem, int value) {
-    return elem < value;
-}
-static bool EQ(const int& elem, int value) {
-    return elem == value;
-}
-
-#include "GrTBSearch.h"
-
-DEF_TEST(GrTBSearch, reporter) {
-    const int array[] = {
-        1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99
-    };
-
-    for (int n = 0; n < static_cast<int>(SK_ARRAY_COUNT(array)); ++n) {
-        for (int i = 0; i < n; i++) {
-            int index = GrTBSearch<int, int>(array, n, array[i]);
-            REPORTER_ASSERT(reporter, index == (int) i);
-            index = GrTBSearch<int, int>(array, n, -array[i]);
-            REPORTER_ASSERT(reporter, index < 0);
-        }
-    }
-}
-
-#endif
diff --git a/src/third_party/skia/tests/GrTRecorderTest.cpp b/src/third_party/skia/tests/GrTRecorderTest.cpp
new file mode 100644
index 0000000..0be0f68
--- /dev/null
+++ b/src/third_party/skia/tests/GrTRecorderTest.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkMatrix.h"
+#include "SkRandom.h"
+#include "SkString.h"
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrTRecorder.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+static int activeRecorderItems = 0;
+
+class IntWrapper {
+public:
+    IntWrapper() {}
+    IntWrapper(int value) : fValue(value) {}
+    operator int() { return fValue; }
+private:
+    int fValue;
+};
+
+static void test_empty_back_and_pop(skiatest::Reporter* reporter) {
+    SkRandom rand;
+    for (int data = 0; data < 2; ++data) {
+        // Do this with different starting sizes to have different alignment between blocks and pops.
+        // pops. We want to test poping the first guy off, guys in the middle of the block, and the
+        // first guy on a non-head block.
+        for (int j = 0; j < 8; ++j) {
+            GrTRecorder<IntWrapper, int> recorder(j);
+
+            REPORTER_ASSERT(reporter, recorder.empty());
+
+            for (int i = 0; i < 100; ++i) {
+                if (data) {
+                    REPORTER_ASSERT(reporter, i == *GrNEW_APPEND_TO_RECORDER(recorder,
+                                                                             IntWrapper, (i)));
+                } else {
+                    REPORTER_ASSERT(reporter, i ==
+                                    *GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder,
+                                                                        IntWrapper, (i),
+                                                                        rand.nextULessThan(10)));
+                }
+                REPORTER_ASSERT(reporter, !recorder.empty());
+                REPORTER_ASSERT(reporter, i == recorder.back());
+                if (0 == (i % 7)) {
+                    recorder.pop_back();
+                    if (i > 0) {
+                        REPORTER_ASSERT(reporter, !recorder.empty());
+                        REPORTER_ASSERT(reporter, i-1 == recorder.back());
+                    }
+                }
+            }
+
+            REPORTER_ASSERT(reporter, !recorder.empty());
+            recorder.reset();
+            REPORTER_ASSERT(reporter, recorder.empty());
+        }
+    }
+}
+
+struct ExtraData {
+    typedef GrTRecorder<ExtraData, int> Recorder;
+
+    ExtraData(int i) : fData(i) {
+        int* extraData = this->extraData();
+        for (int j = 0; j < i; j++) {
+            extraData[j] = i;
+        }
+        ++activeRecorderItems;
+    }
+    ~ExtraData() {
+        --activeRecorderItems;
+    }
+    int* extraData() {
+        return reinterpret_cast<int*>(Recorder::GetDataForItem(this));
+    }
+    int fData;
+};
+
+static void test_extra_data(skiatest::Reporter* reporter) {
+    ExtraData::Recorder recorder(0);
+    for (int i = 0; i < 100; ++i) {
+        GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, ExtraData, (i), i * sizeof(int));
+    }
+    REPORTER_ASSERT(reporter, 100 == activeRecorderItems);
+
+    ExtraData::Recorder::Iter iter(recorder);
+    for (int i = 0; i < 100; ++i) {
+        REPORTER_ASSERT(reporter, iter.next());
+        REPORTER_ASSERT(reporter, i == iter->fData);
+        for (int j = 0; j < i; j++) {
+            REPORTER_ASSERT(reporter, i == iter->extraData()[j]);
+        }
+    }
+    REPORTER_ASSERT(reporter, !iter.next());
+
+    ExtraData::Recorder::ReverseIter reverseIter(recorder);
+    for (int i = 99; i >= 0; --i) {
+        REPORTER_ASSERT(reporter, i == reverseIter->fData);
+        for (int j = 0; j < i; j++) {
+            REPORTER_ASSERT(reporter, i == reverseIter->extraData()[j]);
+        }
+        REPORTER_ASSERT(reporter, reverseIter.previous() == !!i);
+    }
+
+    recorder.reset();
+    REPORTER_ASSERT(reporter, 0 == activeRecorderItems);
+}
+
+enum ClassType {
+    kBase_ClassType,
+    kSubclass_ClassType,
+    kSubSubclass_ClassType,
+    kSubclassExtraData_ClassType,
+    kSubclassEmpty_ClassType,
+
+    kNumClassTypes
+};
+
+class Base {
+public:
+    typedef GrTRecorder<Base, void*> Recorder;
+
+    Base() {
+        fMatrix.reset();
+        ++activeRecorderItems;
+    }
+
+    virtual ~Base() { --activeRecorderItems; }
+
+    virtual ClassType getType() { return kBase_ClassType; }
+
+    virtual void validate(skiatest::Reporter* reporter) const {
+        REPORTER_ASSERT(reporter, fMatrix.isIdentity());
+    }
+
+private:
+    SkMatrix fMatrix;
+};
+
+class Subclass : public Base {
+public:
+    Subclass() : fString("Lorem ipsum dolor sit amet") {}
+
+    virtual ClassType getType() { return kSubclass_ClassType; }
+
+    virtual void validate(skiatest::Reporter* reporter) const {
+        Base::validate(reporter);
+        REPORTER_ASSERT(reporter, !strcmp("Lorem ipsum dolor sit amet", fString.c_str()));
+    }
+
+private:
+    SkString fString;
+};
+
+class SubSubclass : public Subclass {
+public:
+    SubSubclass() : fInt(1234), fFloat(1.234f) {}
+
+    virtual ClassType getType() { return kSubSubclass_ClassType; }
+
+    virtual void validate(skiatest::Reporter* reporter) const {
+        Subclass::validate(reporter);
+        REPORTER_ASSERT(reporter, 1234 == fInt);
+        REPORTER_ASSERT(reporter, 1.234f == fFloat);
+    }
+
+private:
+    int fInt;
+    float fFloat;
+};
+
+class SubclassExtraData : public Base {
+public:
+    SubclassExtraData(int length) : fLength(length) {
+        int* data = reinterpret_cast<int*>(Recorder::GetDataForItem(this));
+        for (int i = 0; i < fLength; ++i) {
+            data[i] = ValueAt(i);
+        }
+    }
+
+    virtual ClassType getType() { return kSubclassExtraData_ClassType; }
+
+    virtual void validate(skiatest::Reporter* reporter) const {
+        Base::validate(reporter);
+        const int* data = reinterpret_cast<const int*>(Recorder::GetDataForItem(this));
+        for (int i = 0; i < fLength; ++i) {
+            REPORTER_ASSERT(reporter, ValueAt(i) == data[i]);
+        }
+    }
+
+private:
+    static int ValueAt(uint64_t i) { return static_cast<int>(123456789 + 987654321 * i); }
+    int fLength;
+};
+
+class SubclassEmpty : public Base {
+public:
+    virtual ClassType getType() { return kSubclassEmpty_ClassType; }
+};
+
+class Order {
+public:
+    Order() { this->reset(); }
+    void reset() { fCurrent = 0; }
+    ClassType next() {
+        fCurrent = 1664525 * fCurrent + 1013904223;
+        return static_cast<ClassType>(fCurrent % kNumClassTypes);
+    }
+private:
+    uint32_t fCurrent;
+};
+static void test_subclasses_iters(skiatest::Reporter*, Order&, Base::Recorder::Iter&,
+                                  Base::Recorder::ReverseIter&, int = 0);
+static void test_subclasses(skiatest::Reporter* reporter) {
+    Base::Recorder recorder(1024);
+
+    Order order;
+    for (int i = 0; i < 1000; i++) {
+        switch (order.next()) {
+            case kBase_ClassType:
+                GrNEW_APPEND_TO_RECORDER(recorder, Base, ());
+                break;
+
+            case kSubclass_ClassType:
+                GrNEW_APPEND_TO_RECORDER(recorder, Subclass, ());
+                break;
+
+            case kSubSubclass_ClassType:
+                GrNEW_APPEND_TO_RECORDER(recorder, SubSubclass, ());
+                break;
+
+            case kSubclassExtraData_ClassType:
+                GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, SubclassExtraData, (i), sizeof(int) * i);
+                break;
+
+            case kSubclassEmpty_ClassType:
+                GrNEW_APPEND_TO_RECORDER(recorder, SubclassEmpty, ());
+                break;
+
+            default:
+                ERRORF(reporter, "Invalid class type");
+                break;
+        }
+    }
+    REPORTER_ASSERT(reporter, 1000 == activeRecorderItems);
+
+    order.reset();
+    Base::Recorder::Iter iter(recorder);
+    Base::Recorder::ReverseIter reverseIter(recorder);
+
+    test_subclasses_iters(reporter, order, iter, reverseIter);
+
+    REPORTER_ASSERT(reporter, !iter.next());
+
+    // Don't reset the recorder. It should automatically destruct all its items.
+}
+static void test_subclasses_iters(skiatest::Reporter* reporter, Order& order,
+                                  Base::Recorder::Iter& iter,
+                                  Base::Recorder::ReverseIter& reverseIter, int i) {
+    if (i >= 1000) {
+        return;
+    }
+
+    ClassType classType = order.next();
+
+    REPORTER_ASSERT(reporter, iter.next());
+    REPORTER_ASSERT(reporter, classType == iter->getType());
+    iter->validate(reporter);
+
+    test_subclasses_iters(reporter, order, iter, reverseIter, i + 1);
+
+    REPORTER_ASSERT(reporter, classType == reverseIter->getType());
+    reverseIter->validate(reporter);
+    REPORTER_ASSERT(reporter, reverseIter.previous() == !!i);
+}
+
+DEF_GPUTEST(GrTRecorder, reporter, factory) {
+    test_empty_back_and_pop(reporter);
+
+    test_extra_data(reporter);
+    REPORTER_ASSERT(reporter, 0 == activeRecorderItems); // test_extra_data should call reset().
+
+    test_subclasses(reporter);
+    REPORTER_ASSERT(reporter, 0 == activeRecorderItems); // Ensure ~GrTRecorder invokes dtors.
+}
+
+#endif
diff --git a/src/third_party/skia/tests/GrTextureMipMapInvalidationTest.cpp b/src/third_party/skia/tests/GrTextureMipMapInvalidationTest.cpp
new file mode 100644
index 0000000..c9c4d7e
--- /dev/null
+++ b/src/third_party/skia/tests/GrTextureMipMapInvalidationTest.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrTexture.h"
+#include "GrTexturePriv.h"
+#include "SkCanvas.h"
+#include "SkImage_Base.h"
+#include "SkSurface.h"
+#include "Test.h"
+
+// Tests that MIP maps are created and invalidated as expected when drawing to and from GrTextures.
+DEF_GPUTEST_FOR_NULLGL_CONTEXT(GrTextureMipMapInvalidationTest, reporter, ctxInfo) {
+    auto isMipped = [] (SkSurface* surf) {
+        return surf->makeImageSnapshot()->getTexture()->texturePriv().hasMipMaps();
+    };
+
+    auto mipsAreDirty = [] (SkSurface* surf) {
+        return surf->makeImageSnapshot()->getTexture()->texturePriv().mipMapsAreDirty();
+    };
+
+    GrContext* context = ctxInfo.grContext();
+    auto info = SkImageInfo::MakeN32Premul(256, 256);
+    auto surf1 = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info);
+    auto surf2 = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info);
+    // Draw something just in case we ever had a solid color optimization
+    surf1->getCanvas()->drawCircle(128, 128, 50, SkPaint());
+    surf1->getCanvas()->flush();
+
+    // No mipmaps initially
+    REPORTER_ASSERT(reporter, !isMipped(surf1.get()));
+
+    // Painting with downscale and medium filter quality should result in mipmap creation
+    SkPaint paint;
+    paint.setFilterQuality(kMedium_SkFilterQuality);
+    surf2->getCanvas()->scale(0.2f, 0.2f);
+    surf2->getCanvas()->drawImage(surf1->makeImageSnapshot(), 0, 0, &paint);
+    surf2->getCanvas()->flush();
+    REPORTER_ASSERT(reporter, isMipped(surf1.get()));
+    REPORTER_ASSERT(reporter, !mipsAreDirty(surf1.get()));
+
+    // Changing the contents of the surface should invalidate the mipmap, but not de-allocate
+    surf1->getCanvas()->drawCircle(128, 128, 100, SkPaint());
+    surf1->getCanvas()->flush();
+    REPORTER_ASSERT(reporter, isMipped(surf1.get()));
+    REPORTER_ASSERT(reporter, mipsAreDirty(surf1.get()));
+}
+
+#endif
diff --git a/src/third_party/skia/tests/GradientTest.cpp b/src/third_party/skia/tests/GradientTest.cpp
index e6fd7b9..3c62cb9 100644
--- a/src/third_party/skia/tests/GradientTest.cpp
+++ b/src/third_party/skia/tests/GradientTest.cpp
@@ -6,12 +6,39 @@
  */
 
 #include "SkCanvas.h"
+#include "SkColorPriv.h"
 #include "SkColorShader.h"
 #include "SkGradientShader.h"
 #include "SkShader.h"
+#include "SkSurface.h"
 #include "SkTemplates.h"
+#include "SkTLazy.h"
 #include "Test.h"
 
+// https://code.google.com/p/chromium/issues/detail?id=448299
+// Giant (inverse) matrix causes overflow when converting/computing using 32.32
+// Before the fix, we would assert (and then crash).
+static void test_big_grad(skiatest::Reporter* reporter) {
+    const SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
+    const SkPoint pts[] = {{ 15, 14.7112684f }, { 0.709064007f, 12.6108112f }};
+    SkPaint paint;
+    paint.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, 2,
+                                                 SkShader::kClamp_TileMode));
+
+    SkBitmap bm;
+    bm.allocN32Pixels(2000, 1);
+    SkCanvas c(bm);
+
+    const SkScalar affine[] = {
+        1.06608627e-06f, 4.26434525e-07f, 6.2855f, 2.6611f, 273.4393f, 244.0046f
+    };
+    SkMatrix matrix;
+    matrix.setAffine(affine);
+    c.concat(matrix);
+
+    c.drawPaint(paint);
+}
+
 struct GradRec {
     int             fColorCount;
     const SkColor*  fColors;
@@ -20,7 +47,7 @@
     const SkScalar* fRadius; // 2
     SkShader::TileMode fTileMode;
 
-    void gradCheck(skiatest::Reporter* reporter, SkShader* shader,
+    void gradCheck(skiatest::Reporter* reporter, const sk_sp<SkShader>& shader,
                    SkShader::GradientInfo* info,
                    SkShader::GradientType gt) const {
         SkAutoTMalloc<SkColor> colorStorage(fColorCount);
@@ -41,90 +68,70 @@
 };
 
 
-static void none_gradproc(skiatest::Reporter* reporter, const GradRec&) {
-    SkAutoTUnref<SkShader> s(SkShader::CreateEmptyShader());
-    REPORTER_ASSERT(reporter, SkShader::kNone_GradientType == s->asAGradient(NULL));
+static void none_gradproc(skiatest::Reporter* reporter, const GradRec&, const GradRec&) {
+    sk_sp<SkShader> s(SkShader::MakeEmptyShader());
+    REPORTER_ASSERT(reporter, SkShader::kNone_GradientType == s->asAGradient(nullptr));
 }
 
-static void color_gradproc(skiatest::Reporter* reporter, const GradRec& rec) {
-    SkAutoTUnref<SkShader> s(new SkColorShader(rec.fColors[0]));
-    REPORTER_ASSERT(reporter, SkShader::kColor_GradientType == s->asAGradient(NULL));
+static void color_gradproc(skiatest::Reporter* reporter, const GradRec& rec, const GradRec&) {
+    sk_sp<SkShader> s(new SkColorShader(rec.fColors[0]));
+    REPORTER_ASSERT(reporter, SkShader::kColor_GradientType == s->asAGradient(nullptr));
 
     SkShader::GradientInfo info;
-    info.fColors = NULL;
+    info.fColors = nullptr;
     info.fColorCount = 0;
     s->asAGradient(&info);
     REPORTER_ASSERT(reporter, 1 == info.fColorCount);
 }
 
-static void linear_gradproc(skiatest::Reporter* reporter, const GradRec& rec) {
-    SkAutoTUnref<SkShader> s(SkGradientShader::CreateLinear(rec.fPoint,
-                                                            rec.fColors,
-                                                            rec.fPos,
-                                                            rec.fColorCount,
-                                                            rec.fTileMode));
+static void linear_gradproc(skiatest::Reporter* reporter, const GradRec& buildRec,
+                            const GradRec& checkRec) {
+    sk_sp<SkShader> s(SkGradientShader::MakeLinear(buildRec.fPoint, buildRec.fColors, buildRec.fPos,
+                                                   buildRec.fColorCount, buildRec.fTileMode));
 
     SkShader::GradientInfo info;
-    rec.gradCheck(reporter, s, &info, SkShader::kLinear_GradientType);
-    REPORTER_ASSERT(reporter, !memcmp(info.fPoint, rec.fPoint, 2 * sizeof(SkPoint)));
+    checkRec.gradCheck(reporter, s, &info, SkShader::kLinear_GradientType);
+    REPORTER_ASSERT(reporter, !memcmp(info.fPoint, checkRec.fPoint, 2 * sizeof(SkPoint)));
 }
 
-static void radial_gradproc(skiatest::Reporter* reporter, const GradRec& rec) {
-    SkAutoTUnref<SkShader> s(SkGradientShader::CreateRadial(rec.fPoint[0],
-                                                            rec.fRadius[0],
-                                                            rec.fColors,
-                                                            rec.fPos,
-                                                            rec.fColorCount,
-                                                            rec.fTileMode));
+static void radial_gradproc(skiatest::Reporter* reporter, const GradRec& buildRec,
+                            const GradRec& checkRec) {
+    sk_sp<SkShader> s(SkGradientShader::MakeRadial(buildRec.fPoint[0], buildRec.fRadius[0],
+                                                   buildRec.fColors, buildRec.fPos,
+                                                   buildRec.fColorCount, buildRec.fTileMode));
 
     SkShader::GradientInfo info;
-    rec.gradCheck(reporter, s, &info, SkShader::kRadial_GradientType);
-    REPORTER_ASSERT(reporter, info.fPoint[0] == rec.fPoint[0]);
-    REPORTER_ASSERT(reporter, info.fRadius[0] == rec.fRadius[0]);
+    checkRec.gradCheck(reporter, s, &info, SkShader::kRadial_GradientType);
+    REPORTER_ASSERT(reporter, info.fPoint[0] == checkRec.fPoint[0]);
+    REPORTER_ASSERT(reporter, info.fRadius[0] == checkRec.fRadius[0]);
 }
 
-static void radial2_gradproc(skiatest::Reporter* reporter, const GradRec& rec) {
-    SkAutoTUnref<SkShader> s(SkGradientShader::CreateTwoPointRadial(rec.fPoint[0],
-                                                            rec.fRadius[0],
-                                                            rec.fPoint[1],
-                                                            rec.fRadius[1],
-                                                            rec.fColors,
-                                                            rec.fPos,
-                                                            rec.fColorCount,
-                                                            rec.fTileMode));
+static void sweep_gradproc(skiatest::Reporter* reporter, const GradRec& buildRec,
+                           const GradRec& checkRec) {
+    sk_sp<SkShader> s(SkGradientShader::MakeSweep(buildRec.fPoint[0].fX, buildRec.fPoint[0].fY,
+                                                  buildRec.fColors, buildRec.fPos,
+                                                  buildRec.fColorCount));
 
     SkShader::GradientInfo info;
-    rec.gradCheck(reporter, s, &info, SkShader::kRadial2_GradientType);
-    REPORTER_ASSERT(reporter, !memcmp(info.fPoint, rec.fPoint, 2 * sizeof(SkPoint)));
-    REPORTER_ASSERT(reporter, !memcmp(info.fRadius, rec.fRadius, 2 * sizeof(SkScalar)));
+    checkRec.gradCheck(reporter, s, &info, SkShader::kSweep_GradientType);
+    REPORTER_ASSERT(reporter, info.fPoint[0] == checkRec.fPoint[0]);
 }
 
-static void sweep_gradproc(skiatest::Reporter* reporter, const GradRec& rec) {
-    SkAutoTUnref<SkShader> s(SkGradientShader::CreateSweep(rec.fPoint[0].fX,
-                                                           rec.fPoint[0].fY,
-                                                           rec.fColors,
-                                                           rec.fPos,
-                                                           rec.fColorCount));
+static void conical_gradproc(skiatest::Reporter* reporter, const GradRec& buildRec,
+                             const GradRec& checkRec) {
+    sk_sp<SkShader> s(SkGradientShader::MakeTwoPointConical(buildRec.fPoint[0],
+                                                            buildRec.fRadius[0],
+                                                            buildRec.fPoint[1],
+                                                            buildRec.fRadius[1],
+                                                            buildRec.fColors,
+                                                            buildRec.fPos,
+                                                            buildRec.fColorCount,
+                                                            buildRec.fTileMode));
 
     SkShader::GradientInfo info;
-    rec.gradCheck(reporter, s, &info, SkShader::kSweep_GradientType);
-    REPORTER_ASSERT(reporter, info.fPoint[0] == rec.fPoint[0]);
-}
-
-static void conical_gradproc(skiatest::Reporter* reporter, const GradRec& rec) {
-    SkAutoTUnref<SkShader> s(SkGradientShader::CreateTwoPointConical(rec.fPoint[0],
-                                                             rec.fRadius[0],
-                                                             rec.fPoint[1],
-                                                             rec.fRadius[1],
-                                                             rec.fColors,
-                                                             rec.fPos,
-                                                             rec.fColorCount,
-                                                             rec.fTileMode));
-
-    SkShader::GradientInfo info;
-    rec.gradCheck(reporter, s, &info, SkShader::kConical_GradientType);
-    REPORTER_ASSERT(reporter, !memcmp(info.fPoint, rec.fPoint, 2 * sizeof(SkPoint)));
-    REPORTER_ASSERT(reporter, !memcmp(info.fRadius, rec.fRadius, 2 * sizeof(SkScalar)));
+    checkRec.gradCheck(reporter, s, &info, SkShader::kConical_GradientType);
+    REPORTER_ASSERT(reporter, !memcmp(info.fPoint, checkRec.fPoint, 2 * sizeof(SkPoint)));
+    REPORTER_ASSERT(reporter, !memcmp(info.fRadius, checkRec.fRadius, 2 * sizeof(SkScalar)));
 }
 
 // Ensure that repeated color gradients behave like drawing a single color
@@ -135,18 +142,12 @@
     };
     SkColor colors[] = { SK_ColorBLUE, SK_ColorBLUE };
     const SkScalar pos[] = { 0, SK_Scalar1 };
-    SkAutoTUnref<SkShader> s(SkGradientShader::CreateLinear(pts,
-                                                            colors,
-                                                            pos,
-                                                            2,
-                                                            SkShader::kClamp_TileMode));
+    SkPaint paint;
+    paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, SkShader::kClamp_TileMode));
     SkBitmap outBitmap;
     outBitmap.allocN32Pixels(10, 1);
-    SkPaint paint;
-    paint.setShader(s.get());
     SkCanvas canvas(outBitmap);
     canvas.drawPaint(paint);
-    SkAutoLockPixels alp(outBitmap);
     for (int i = 0; i < 10; i++) {
         // The following is commented out because it currently fails
         // Related bug: https://code.google.com/p/skia/issues/detail?id=1098
@@ -155,7 +156,7 @@
     }
 }
 
-typedef void (*GradProc)(skiatest::Reporter* reporter, const GradRec&);
+typedef void (*GradProc)(skiatest::Reporter* reporter, const GradRec&, const GradRec&);
 
 static void TestGradientShaders(skiatest::Reporter* reporter) {
     static const SkColor gColors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
@@ -179,17 +180,371 @@
         color_gradproc,
         linear_gradproc,
         radial_gradproc,
-        radial2_gradproc,
         sweep_gradproc,
         conical_gradproc,
     };
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(gProcs); ++i) {
-        gProcs[i](reporter, rec);
+        gProcs[i](reporter, rec, rec);
+    }
+}
+
+static void TestGradientOptimization(skiatest::Reporter* reporter) {
+    static const struct {
+        GradProc fProc;
+        bool     fIsClampRestricted;
+    } gProcInfo[] = {
+        { linear_gradproc , false },
+        { radial_gradproc , false },
+        { sweep_gradproc  , true  }, // sweep is funky in that it always pretends to be kClamp.
+        { conical_gradproc, false },
+    };
+
+    static const SkColor   gC_00[] = { 0xff000000, 0xff000000 };
+    static const SkColor   gC_01[] = { 0xff000000, 0xffffffff };
+    static const SkColor   gC_11[] = { 0xffffffff, 0xffffffff };
+    static const SkColor  gC_001[] = { 0xff000000, 0xff000000, 0xffffffff };
+    static const SkColor  gC_011[] = { 0xff000000, 0xffffffff, 0xffffffff };
+    static const SkColor gC_0011[] = { 0xff000000, 0xff000000, 0xffffffff, 0xffffffff };
+
+    static const SkScalar   gP_01[] = { 0, 1 };
+    static const SkScalar  gP_001[] = { 0,   0, 1 };
+    static const SkScalar  gP_011[] = { 0,   1, 1 };
+    static const SkScalar  gP_0x1[] = { 0, .5f, 1 };
+    static const SkScalar gP_0011[] = { 0, 0, 1, 1 };
+
+    static const SkPoint    gPts[] = { {0, 0}, {1, 1} };
+    static const SkScalar gRadii[] = { 1, 2 };
+
+    static const struct {
+        const SkColor*  fCol;
+        const SkScalar* fPos;
+        int             fCount;
+
+        const SkColor*  fExpectedCol;
+        const SkScalar* fExpectedPos;
+        int             fExpectedCount;
+        bool            fRequiresNonClamp;
+    } gTests[] = {
+        { gC_001,  gP_001, 3,  gC_01,  gP_01, 2, false },
+        { gC_001,  gP_011, 3,  gC_00,  gP_01, 2, true  },
+        { gC_001,  gP_0x1, 3, gC_001, gP_0x1, 3, false },
+        { gC_001, nullptr, 3, gC_001, gP_0x1, 3, false },
+
+        { gC_011,  gP_001, 3,  gC_11,  gP_01, 2, true  },
+        { gC_011,  gP_011, 3,  gC_01,  gP_01, 2, false },
+        { gC_011,  gP_0x1, 3, gC_011, gP_0x1, 3, false },
+        { gC_011, nullptr, 3, gC_011, gP_0x1, 3, false },
+
+        { gC_0011, gP_0011, 4, gC_0011, gP_0011, 4, false },
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gProcInfo); ++i) {
+        for (int mode = 0; mode < SkShader::kTileModeCount; ++mode) {
+            if (gProcInfo[i].fIsClampRestricted && mode != SkShader::kClamp_TileMode) {
+                continue;
+            }
+
+            for (size_t t = 0; t < SK_ARRAY_COUNT(gTests); ++t) {
+                GradRec rec;
+                rec.fColorCount = gTests[t].fCount;
+                rec.fColors     = gTests[t].fCol;
+                rec.fPos        = gTests[t].fPos;
+                rec.fTileMode   = static_cast<SkShader::TileMode>(mode);
+                rec.fPoint      = gPts;
+                rec.fRadius     = gRadii;
+
+                GradRec expected = rec;
+                if (!gTests[t].fRequiresNonClamp || mode != SkShader::kClamp_TileMode) {
+                    expected.fColorCount = gTests[t].fExpectedCount;
+                    expected.fColors     = gTests[t].fExpectedCol;
+                    expected.fPos        = gTests[t].fExpectedPos;
+                }
+
+                gProcInfo[i].fProc(reporter, rec, expected);
+            }
+        }
+    }
+}
+
+static void test_nearly_vertical(skiatest::Reporter* reporter) {
+    auto surface(SkSurface::MakeRasterN32Premul(200, 200));
+
+    const SkPoint pts[] = {{ 100, 50 }, { 100.0001f, 50000 }};
+    const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
+    const SkScalar pos[] = { 0, 1 };
+    SkPaint paint;
+    paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, SkShader::kClamp_TileMode));
+
+    surface->getCanvas()->drawPaint(paint);
+}
+
+static void test_vertical(skiatest::Reporter* reporter) {
+    auto surface(SkSurface::MakeRasterN32Premul(200, 200));
+
+    const SkPoint pts[] = {{ 100, 50 }, { 100, 50 }};
+    const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
+    const SkScalar pos[] = { 0, 1 };
+    SkPaint paint;
+    paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, SkShader::kClamp_TileMode));
+
+    surface->getCanvas()->drawPaint(paint);
+}
+
+// A linear gradient interval can, due to numerical imprecision (likely in the divide)
+// finish an interval with the final fx not landing outside of [p0...p1].
+// The old code had an assert which this test triggered.
+// We now explicitly clamp the resulting fx value.
+static void test_linear_fuzz(skiatest::Reporter* reporter) {
+    auto surface(SkSurface::MakeRasterN32Premul(1300, 630));
+
+    const SkPoint pts[] = {{ 179.5f, -179.5f }, { 1074.5f, 715.5f }};
+    const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE };
+    const SkScalar pos[] = {0, 0.200000003f, 0.800000012f, 1 };
+
+    SkPaint paint;
+    paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 4, SkShader::kClamp_TileMode));
+
+    SkRect r = {0, 83, 1254, 620};
+    surface->getCanvas()->drawRect(r, paint);
+}
+
+// https://bugs.chromium.org/p/skia/issues/detail?id=5023
+// We should still shade pixels for which the radius is exactly 0.
+static void test_two_point_conical_zero_radius(skiatest::Reporter* reporter) {
+    auto surface(SkSurface::MakeRasterN32Premul(5, 5));
+    surface->getCanvas()->clear(SK_ColorRED);
+
+    const SkColor colors[] = { SK_ColorGREEN, SK_ColorBLUE };
+    SkPaint p;
+    p.setShader(SkGradientShader::MakeTwoPointConical(
+        SkPoint::Make(2.5f, 2.5f), 0,
+        SkPoint::Make(3.0f, 3.0f), 10,
+        colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode));
+    surface->getCanvas()->drawPaint(p);
+
+    // r == 0 for the center pixel.
+    // verify that we draw it (no red bleed)
+    SkPMColor centerPMColor;
+    surface->readPixels(SkImageInfo::MakeN32Premul(1, 1), &centerPMColor, sizeof(SkPMColor), 2, 2);
+    REPORTER_ASSERT(reporter, SkGetPackedR32(centerPMColor) == 0);
+}
+
+// http://crbug.com/599458
+static void test_clamping_overflow(skiatest::Reporter*) {
+    SkPaint p;
+    const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };
+    const SkPoint pts1[] = { SkPoint::Make(1001, 1000001), SkPoint::Make(1000.99f, 1000000) };
+
+    p.setShader(SkGradientShader::MakeLinear(pts1, colors, nullptr, 2, SkShader::kClamp_TileMode));
+
+    sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50));
+    surface->getCanvas()->scale(100, 100);
+    surface->getCanvas()->drawPaint(p);
+
+    const SkPoint pts2[] = { SkPoint::Make(10000.99f, 1000000), SkPoint::Make(10001, 1000001) };
+    p.setShader(SkGradientShader::MakeLinear(pts2, colors, nullptr, 2, SkShader::kClamp_TileMode));
+    surface->getCanvas()->drawPaint(p);
+
+    // Passes if we don't trigger asserts.
+}
+
+// http://crbug.com/636194
+static void test_degenerate_linear(skiatest::Reporter*) {
+    SkPaint p;
+    const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };
+    const SkPoint pts[] = {
+        SkPoint::Make(-46058024627067344430605278824628224.0f, 0),
+        SkPoint::Make(SK_ScalarMax, 0)
+    };
+
+    p.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kClamp_TileMode));
+    sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(50, 50));
+    surface->getCanvas()->drawPaint(p);
+
+    // Passes if we don't trigger asserts.
+}
+
+// "Interesting" fuzzer values.
+static void test_linear_fuzzer(skiatest::Reporter*) {
+    static const SkColor gColors0[] = { 0x30303030, 0x30303030 };
+    static const SkColor gColors1[] = { 0x30303030, 0x30303030, 0x30303030 };
+
+    static const SkScalar gPos1[]   = { 0, 0, 1 };
+
+    static const SkScalar gMatrix0[9] = {
+        6.40969056e-10f, 0              , 6.40969056e-10f,
+        0              , 4.42539023e-39f, 6.40969056e-10f,
+        0              , 0              , 1
+    };
+    static const SkScalar gMatrix1[9] = {
+        -2.75294113f    , 6.40969056e-10f,  6.40969056e-10f,
+         6.40969056e-10f, 6.40969056e-10f, -3.32810161e+24f,
+         6.40969056e-10f, 6.40969056e-10f,  0
+    };
+    static const SkScalar gMatrix2[9] = {
+        7.93481258e+17f, 6.40969056e-10f, 6.40969056e-10f,
+        6.40969056e-10f, 6.40969056e-10f, 6.40969056e-10f,
+        6.40969056e-10f, 6.40969056e-10f, 0.688235283f
+    };
+    static const SkScalar gMatrix3[9] = {
+        1.89180674e+11f,     6.40969056e-10f, 6.40969056e-10f,
+        6.40969056e-10f,     6.40969056e-10f, 6.40969056e-10f,
+        6.40969056e-10f, 11276.0469f        , 8.12524808e+20f
+    };
+
+    static const struct {
+        SkPoint            fPts[2];
+        const SkColor*     fColors;
+        const SkScalar*    fPos;
+        int                fCount;
+        SkShader::TileMode fTileMode;
+        uint32_t           fFlags;
+        const SkScalar*    fLocalMatrix;
+        const SkScalar*    fGlobalMatrix;
+    } gConfigs[] = {
+        {
+            {{0, -2.752941f}, {0, 0}},
+            gColors0,
+            nullptr,
+            SK_ARRAY_COUNT(gColors0),
+            SkShader::kClamp_TileMode,
+            0,
+            gMatrix0,
+            nullptr
+        },
+        {
+            {{4.42539023e-39f, -4.42539023e-39f}, {9.78041162e-15f, 4.42539023e-39f}},
+            gColors1,
+            gPos1,
+            SK_ARRAY_COUNT(gColors1),
+            SkShader::kClamp_TileMode,
+            0,
+            nullptr,
+            gMatrix1
+        },
+        {
+            {{4.42539023e-39f, 6.40969056e-10f}, {6.40969056e-10f, 1.49237238e-19f}},
+            gColors1,
+            gPos1,
+            SK_ARRAY_COUNT(gColors1),
+            SkShader::kClamp_TileMode,
+            0,
+            nullptr,
+            gMatrix2
+        },
+        {
+            {{6.40969056e-10f, 6.40969056e-10f}, {6.40969056e-10f, -0.688235283f}},
+            gColors0,
+            nullptr,
+            SK_ARRAY_COUNT(gColors0),
+            SkShader::kClamp_TileMode,
+            0,
+            gMatrix3,
+            nullptr
+        },
+    };
+
+    sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
+    SkColorSpace* colorSpaces[] = {
+        nullptr,     // hits the legacy gradient impl
+        srgb.get(),  // triggers 4f/raster-pipeline
+    };
+
+    SkPaint paint;
+
+    for (auto colorSpace : colorSpaces) {
+
+        sk_sp<SkSurface> surface = SkSurface::MakeRaster(SkImageInfo::Make(100, 100,
+                                                                           kN32_SkColorType,
+                                                                           kPremul_SkAlphaType,
+                                                                           sk_ref_sp(colorSpace)));
+        SkCanvas* canvas = surface->getCanvas();
+
+        for (const auto& config : gConfigs) {
+            SkAutoCanvasRestore acr(canvas, false);
+            SkTLazy<SkMatrix> localMatrix;
+            if (config.fLocalMatrix) {
+                localMatrix.init();
+                localMatrix.get()->set9(config.fLocalMatrix);
+            }
+
+            paint.setShader(SkGradientShader::MakeLinear(config.fPts,
+                                                         config.fColors,
+                                                         config.fPos,
+                                                         config.fCount,
+                                                         config.fTileMode,
+                                                         config.fFlags,
+                                                         localMatrix.getMaybeNull()));
+            if (config.fGlobalMatrix) {
+                SkMatrix m;
+                m.set9(config.fGlobalMatrix);
+                canvas->save();
+                canvas->concat(m);
+            }
+
+            canvas->drawPaint(paint);
+        }
+    }
+}
+
+static void test_sweep_fuzzer(skiatest::Reporter*) {
+    static const SkColor gColors0[] = { 0x30303030, 0x30303030, 0x30303030 };
+    static const SkScalar   gPos0[] = { -47919293023455565225163489280.0f, 0, 1 };
+    static const SkScalar gMatrix0[9] = {
+        1.12116716e-13f,  0              ,  8.50489682e+16f,
+        4.1917041e-41f ,  3.51369881e-23f, -2.54344271e-26f,
+        9.61111907e+17f, -3.35263808e-29f, -1.35659403e+14f
+    };
+    static const struct {
+        SkPoint            fCenter;
+        const SkColor*     fColors;
+        const SkScalar*    fPos;
+        int                fCount;
+        const SkScalar*    fGlobalMatrix;
+    } gConfigs[] = {
+        {
+            { 0, 0 },
+            gColors0,
+            gPos0,
+            SK_ARRAY_COUNT(gColors0),
+            gMatrix0
+        },
+    };
+
+    sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(100, 100);
+    SkCanvas* canvas = surface->getCanvas();
+    SkPaint paint;
+
+    for (const auto& config : gConfigs) {
+        paint.setShader(SkGradientShader::MakeSweep(config.fCenter.x(),
+                                                    config.fCenter.y(),
+                                                    config.fColors,
+                                                    config.fPos,
+                                                    config.fCount));
+
+        SkAutoCanvasRestore acr(canvas, false);
+        if (config.fGlobalMatrix) {
+            SkMatrix m;
+            m.set9(config.fGlobalMatrix);
+            canvas->save();
+            canvas->concat(m);
+        }
+        canvas->drawPaint(paint);
     }
 }
 
 DEF_TEST(Gradient, reporter) {
     TestGradientShaders(reporter);
+    TestGradientOptimization(reporter);
     TestConstantGradient(reporter);
+    test_big_grad(reporter);
+    test_nearly_vertical(reporter);
+    test_vertical(reporter);
+    test_linear_fuzz(reporter);
+    test_two_point_conical_zero_radius(reporter);
+    test_clamping_overflow(reporter);
+    test_degenerate_linear(reporter);
+    test_linear_fuzzer(reporter);
+    test_sweep_fuzzer(reporter);
 }
diff --git a/src/third_party/skia/tests/HSVRoundTripTest.cpp b/src/third_party/skia/tests/HSVRoundTripTest.cpp
new file mode 100644
index 0000000..4d25895
--- /dev/null
+++ b/src/third_party/skia/tests/HSVRoundTripTest.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#include "SkColor.h"
+
+DEF_TEST(ColorToHSVRoundTrip, reporter) {
+    SkScalar hsv[3];
+    for (U8CPU r = 0; r <= 255; r++) {
+        for (U8CPU g = 0; g <= 255; g++) {
+            for (U8CPU b = 0; b <= 255; b++) {
+                SkColor color = SkColorSetARGBInline(0xFF, r, g, b);
+                SkColorToHSV(color, hsv);
+                SkColor result = SkHSVToColor(0xFF, hsv);
+                if (result != color) {
+                    ERRORF(reporter, "HSV roundtrip mismatch!\n"
+                                     "\toriginal: %X\n"
+                                     "\tHSV: %f, %f, %f\n"
+                                     "\tresult: %X\n",
+                           color, hsv[0], hsv[1], hsv[2], result);
+                }
+            }
+        }
+    }
+}
diff --git a/src/third_party/skia/tests/HashTest.cpp b/src/third_party/skia/tests/HashTest.cpp
new file mode 100644
index 0000000..667f8ea
--- /dev/null
+++ b/src/third_party/skia/tests/HashTest.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkChecksum.h"
+#include "SkRefCnt.h"
+#include "SkString.h"
+#include "SkTHash.h"
+#include "Test.h"
+
+// Tests use of const foreach().  map.count() is of course the better way to do this.
+static int count(const SkTHashMap<int, double>& map) {
+    int n = 0;
+    map.foreach([&n](int, double) { n++; });
+    return n;
+}
+
+DEF_TEST(HashMap, r) {
+    SkTHashMap<int, double> map;
+
+    map.set(3, 4.0);
+    REPORTER_ASSERT(r, map.count() == 1);
+
+    REPORTER_ASSERT(r, map.approxBytesUsed() > 0);
+
+    double* found = map.find(3);
+    REPORTER_ASSERT(r, found);
+    REPORTER_ASSERT(r, *found == 4.0);
+
+    map.foreach([](int key, double* d){ *d = -key; });
+    REPORTER_ASSERT(r, count(map) == 1);
+
+    found = map.find(3);
+    REPORTER_ASSERT(r, found);
+    REPORTER_ASSERT(r, *found == -3.0);
+
+    REPORTER_ASSERT(r, !map.find(2));
+
+    const int N = 20;
+
+    for (int i = 0; i < N; i++) {
+        map.set(i, 2.0*i);
+    }
+    for (int i = 0; i < N; i++) {
+        double* found = map.find(i);
+        REPORTER_ASSERT(r, found);
+        REPORTER_ASSERT(r, *found == i*2.0);
+    }
+    for (int i = N; i < 2*N; i++) {
+        REPORTER_ASSERT(r, !map.find(i));
+    }
+
+    REPORTER_ASSERT(r, map.count() == N);
+
+    for (int i = 0; i < N/2; i++) {
+        map.remove(i);
+    }
+    for (int i = 0; i < N; i++) {
+        double* found = map.find(i);
+        REPORTER_ASSERT(r, (found == nullptr) ==  (i < N/2));
+    }
+    REPORTER_ASSERT(r, map.count() == N/2);
+
+    map.reset();
+    REPORTER_ASSERT(r, map.count() == 0);
+
+    {
+        // Test that we don't leave dangling values in empty slots.
+        SkTHashMap<int, sk_sp<SkRefCnt>> refMap;
+        auto ref = sk_make_sp<SkRefCnt>();
+        REPORTER_ASSERT(r, ref->unique());
+
+        refMap.set(0, ref);
+        REPORTER_ASSERT(r, refMap.count() == 1);
+        REPORTER_ASSERT(r, !ref->unique());
+
+        refMap.remove(0);
+        REPORTER_ASSERT(r, refMap.count() == 0);
+        REPORTER_ASSERT(r, ref->unique());
+    }
+}
+
+DEF_TEST(HashSet, r) {
+    SkTHashSet<SkString> set;
+
+    set.add(SkString("Hello"));
+    set.add(SkString("World"));
+
+    REPORTER_ASSERT(r, set.count() == 2);
+
+    REPORTER_ASSERT(r, set.contains(SkString("Hello")));
+    REPORTER_ASSERT(r, set.contains(SkString("World")));
+    REPORTER_ASSERT(r, !set.contains(SkString("Goodbye")));
+
+    REPORTER_ASSERT(r, set.find(SkString("Hello")));
+    REPORTER_ASSERT(r, *set.find(SkString("Hello")) == SkString("Hello"));
+
+    set.remove(SkString("Hello"));
+    REPORTER_ASSERT(r, !set.contains(SkString("Hello")));
+    REPORTER_ASSERT(r, set.count() == 1);
+
+    set.reset();
+    REPORTER_ASSERT(r, set.count() == 0);
+}
+
+namespace {
+
+class CopyCounter {
+public:
+    CopyCounter() : fID(0), fCounter(nullptr) {}
+
+    CopyCounter(uint32_t id, uint32_t* counter) : fID(id), fCounter(counter) {}
+
+    CopyCounter(const CopyCounter& other)
+        : fID(other.fID)
+        , fCounter(other.fCounter) {
+        SkASSERT(fCounter);
+        *fCounter += 1;
+    }
+
+    void operator=(const CopyCounter& other) {
+        fID = other.fID;
+        fCounter = other.fCounter;
+        *fCounter += 1;
+    }
+
+    CopyCounter(CopyCounter&& other) { *this = std::move(other); }
+    void operator=(CopyCounter&& other) {
+        fID = other.fID;
+        fCounter = other.fCounter;
+    }
+
+
+    bool operator==(const CopyCounter& other) const {
+        return fID == other.fID;
+    }
+
+private:
+    uint32_t  fID;
+    uint32_t* fCounter;
+};
+
+struct HashCopyCounter {
+    uint32_t operator()(const CopyCounter&) const {
+        return 0; // let them collide, what do we care?
+    }
+};
+
+}
+
+DEF_TEST(HashSetCopyCounter, r) {
+    SkTHashSet<CopyCounter, HashCopyCounter> set;
+
+    uint32_t globalCounter = 0;
+    CopyCounter copyCounter1(1, &globalCounter);
+    CopyCounter copyCounter2(2, &globalCounter);
+    REPORTER_ASSERT(r, globalCounter == 0);
+
+    set.add(copyCounter1);
+    REPORTER_ASSERT(r, globalCounter == 1);
+    REPORTER_ASSERT(r, set.contains(copyCounter1));
+    REPORTER_ASSERT(r, globalCounter == 1);
+    set.add(copyCounter1);
+    // We allow copies for same-value adds for now.
+    REPORTER_ASSERT(r, globalCounter == 2);
+
+    set.add(copyCounter2);
+    REPORTER_ASSERT(r, globalCounter == 3);
+    REPORTER_ASSERT(r, set.contains(copyCounter1));
+    REPORTER_ASSERT(r, set.contains(copyCounter2));
+    REPORTER_ASSERT(r, globalCounter == 3);
+    set.add(copyCounter1);
+    set.add(copyCounter2);
+    // We allow copies for same-value adds for now.
+    REPORTER_ASSERT(r, globalCounter == 5);
+}
diff --git a/src/third_party/skia/tests/HighContrastFilterTest.cpp b/src/third_party/skia/tests/HighContrastFilterTest.cpp
new file mode 100644
index 0000000..7c3fc84
--- /dev/null
+++ b/src/third_party/skia/tests/HighContrastFilterTest.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkHighContrastFilter.h"
+#include "Test.h"
+
+DEF_TEST(HighContrastFilter_FilterImage, reporter) {
+    SkHighContrastConfig config;
+    config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
+
+    int w = 10, h = 10;
+    SkBitmap filterResult, paintResult;
+
+    filterResult.allocN32Pixels(w, h);
+    SkCanvas canvasFilter(filterResult);
+    canvasFilter.clear(0x00000000);
+
+    paintResult.allocN32Pixels(w, h);
+    SkCanvas canvasPaint(paintResult);
+    canvasPaint.clear(0x00000000);
+
+    SkPaint paint;
+    paint.setColor(SK_ColorBLUE);
+    SkRect r = SkRect::MakeLTRB(SkIntToScalar(2), SkIntToScalar(2),
+                                SkIntToScalar(8), SkIntToScalar(8));
+    canvasPaint.drawRect(r, paint);
+
+    paint.setColorFilter(SkHighContrastFilter::Make(config));
+    canvasFilter.drawRect(r, paint);
+
+    for (int y = r.top(); y < r.bottom(); ++y) {
+        for (int x = r.left(); x < r.right(); ++x) {
+            SkColor paintColor = paintResult.getColor(x, y);
+            SkColor filterColor = filterResult.getColor(x, y);
+            REPORTER_ASSERT(
+                reporter, filterColor ==
+                paint.getColorFilter()->filterColor(paintColor));
+        }
+    }
+}
+
+DEF_TEST(HighContrastFilter_SanityCheck, reporter) {
+    SkHighContrastConfig config;
+    config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
+    sk_sp<SkColorFilter> filter = SkHighContrastFilter::Make(config);
+
+    SkColor white_inverted = filter->filterColor(SK_ColorWHITE);
+    REPORTER_ASSERT(reporter, white_inverted == SK_ColorBLACK);
+
+    SkColor black_inverted = filter->filterColor(SK_ColorBLACK);
+    REPORTER_ASSERT(reporter, black_inverted == SK_ColorWHITE);
+}
+
+DEF_TEST(HighContrastFilter_InvalidInputs, reporter) {
+    SkHighContrastConfig config;
+    REPORTER_ASSERT(reporter, config.isValid());
+
+    // Valid invert style
+    config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertBrightness;
+    REPORTER_ASSERT(reporter, config.isValid());
+    config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
+    REPORTER_ASSERT(reporter, config.isValid());
+    sk_sp<SkColorFilter> filter = SkHighContrastFilter::Make(config);
+    REPORTER_ASSERT(reporter, filter);
+
+    // Invalid invert style
+    config.fInvertStyle = static_cast<SkHighContrastConfig::InvertStyle>(999);
+    REPORTER_ASSERT(reporter, !config.isValid());
+    filter = SkHighContrastFilter::Make(config);
+    REPORTER_ASSERT(reporter, !filter);
+
+    // Valid contrast
+    config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertBrightness;
+    config.fContrast = 0.5f;
+    REPORTER_ASSERT(reporter, config.isValid());
+    filter = SkHighContrastFilter::Make(config);
+    REPORTER_ASSERT(reporter, filter);
+
+    // Invalid contrast
+    config.fContrast = 1.1f;
+    REPORTER_ASSERT(reporter, !config.isValid());
+    filter = SkHighContrastFilter::Make(config);
+    REPORTER_ASSERT(reporter, !filter);
+}
diff --git a/src/third_party/skia/tests/ICCTest.cpp b/src/third_party/skia/tests/ICCTest.cpp
new file mode 100644
index 0000000..4bff654
--- /dev/null
+++ b/src/third_party/skia/tests/ICCTest.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Resources.h"
+#include "SkColorSpace.h"
+#include "SkColorSpacePriv.h"
+#include "SkColorSpace_XYZ.h"
+#include "SkData.h"
+#include "SkICC.h"
+#include "SkICCPriv.h"
+#include "SkMatrix44.h"
+#include "SkStream.h"
+#include "Test.h"
+
+static bool almost_equal(float a, float b) {
+    return SkTAbs(a - b) < 0.001f;
+}
+
+static inline void test_to_xyz_d50(skiatest::Reporter* r, SkICC* icc, bool shouldSucceed,
+                                   const float* reference) {
+    SkMatrix44 result(SkMatrix44::kUninitialized_Constructor);
+    REPORTER_ASSERT(r, shouldSucceed == icc->toXYZD50(&result));
+    if (shouldSucceed) {
+        float resultVals[16];
+        result.asColMajorf(resultVals);
+        for (int i = 0; i < 16; i++) {
+            REPORTER_ASSERT(r, almost_equal(resultVals[i], reference[i]));
+        }
+    }
+}
+
+DEF_TEST(ICC_ToXYZD50, r) {
+    const float z30Reference[16] = {
+        0.59825f, 0.27103f, 0.00603f, 0.0f, 0.22243f, 0.67447f, 0.07368f, 0.0f, 0.14352f, 0.05449f,
+        0.74519f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
+    };
+
+    sk_sp<SkData> data = SkData::MakeFromFileName(
+            GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str());
+    sk_sp<SkICC> z30 = SkICC::Make(data->data(), data->size());
+    test_to_xyz_d50(r, z30.get(), true, z30Reference);
+
+    const float z32Reference[16] = {
+        0.61583f, 0.28789f, 0.00513f, 0.0f, 0.20428f, 0.66972f, 0.06609f, 0.0f, 0.14409f, 0.04237f,
+        0.75368f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
+    };
+
+    data = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str());
+    sk_sp<SkICC> z32 = SkICC::Make(data->data(), data->size());
+    test_to_xyz_d50(r, z32.get(), true, z32Reference);
+
+    data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str());
+    sk_sp<SkICC> upperLeft = SkICC::Make(data->data(), data->size());
+    test_to_xyz_d50(r, upperLeft.get(), false, z32Reference);
+
+    data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str());
+    sk_sp<SkICC> upperRight = SkICC::Make(data->data(), data->size());
+    test_to_xyz_d50(r, upperRight.get(), false, z32Reference);
+}
+
+static inline void test_is_numerical_transfer_fn(skiatest::Reporter* r, SkICC* icc,
+                                                 bool shouldSucceed,
+                                                 const SkColorSpaceTransferFn& reference) {
+    SkColorSpaceTransferFn result;
+    REPORTER_ASSERT(r, shouldSucceed == icc->isNumericalTransferFn(&result));
+    if (shouldSucceed) {
+        REPORTER_ASSERT(r, 0 == memcmp(&result, &reference, sizeof(SkColorSpaceTransferFn)));
+    }
+}
+
+DEF_TEST(ICC_IsNumericalTransferFn, r) {
+    SkColorSpaceTransferFn referenceFn;
+    referenceFn.fA = 1.0f;
+    referenceFn.fB = 0.0f;
+    referenceFn.fC = 0.0f;
+    referenceFn.fD = 0.0f;
+    referenceFn.fE = 0.0f;
+    referenceFn.fF = 0.0f;
+    referenceFn.fG = 2.2f;
+
+    sk_sp<SkData> data = SkData::MakeFromFileName(
+            GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str());
+    sk_sp<SkICC> z30 = SkICC::Make(data->data(), data->size());
+    test_is_numerical_transfer_fn(r, z30.get(), true, referenceFn);
+
+    data = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str());
+    sk_sp<SkICC> z32 = SkICC::Make(data->data(), data->size());
+    test_is_numerical_transfer_fn(r, z32.get(), true, referenceFn);
+
+    data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str());
+    sk_sp<SkICC> upperLeft = SkICC::Make(data->data(), data->size());
+    test_is_numerical_transfer_fn(r, upperLeft.get(), false, referenceFn);
+
+    data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str());
+    sk_sp<SkICC> upperRight = SkICC::Make(data->data(), data->size());
+    test_is_numerical_transfer_fn(r, upperRight.get(), false, referenceFn);
+}
+
+static inline void test_write_icc(skiatest::Reporter* r, const SkColorSpaceTransferFn& fn,
+                                  const SkMatrix44& toXYZD50, SkColorSpace* reference,
+                                  bool writeToFile) {
+    sk_sp<SkData> profile = SkICC::WriteToICC(fn, toXYZD50);
+    if (writeToFile) {
+        SkFILEWStream stream("out.icc");
+        stream.write(profile->data(), profile->size());
+    }
+
+    sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeICC(profile->data(), profile->size());
+    REPORTER_ASSERT(r, SkColorSpace::Equals(reference, colorSpace.get()));
+}
+
+DEF_TEST(ICC_WriteICC, r) {
+    SkColorSpaceTransferFn adobeFn;
+    adobeFn.fA = 1.0f;
+    adobeFn.fB = 0.0f;
+    adobeFn.fC = 0.0f;
+    adobeFn.fD = 0.0f;
+    adobeFn.fE = 0.0f;
+    adobeFn.fF = 0.0f;
+    adobeFn.fG = 2.2f;
+    SkMatrix44 adobeMatrix(SkMatrix44::kUninitialized_Constructor);
+    adobeMatrix.set3x3RowMajorf(gAdobeRGB_toXYZD50);
+    test_write_icc(r, adobeFn, adobeMatrix,
+                   SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named).get(), false);
+
+    SkColorSpaceTransferFn srgbFn;
+    srgbFn.fA = 1.0f / 1.055f;
+    srgbFn.fB = 0.055f / 1.055f;
+    srgbFn.fC = 1.0f / 12.92f;
+    srgbFn.fD = 0.04045f;
+    srgbFn.fE = 0.0f;
+    srgbFn.fF = 0.0f;
+    srgbFn.fG = 2.4f;
+    SkMatrix44 srgbMatrix(SkMatrix44::kUninitialized_Constructor);
+    srgbMatrix.set3x3RowMajorf(gSRGB_toXYZD50);
+    test_write_icc(r, srgbFn, srgbMatrix, SkColorSpace::MakeSRGB().get(),
+                   false);
+
+    SkString adobeTag = SkICCGetColorProfileTag(adobeFn, adobeMatrix);
+    SkString srgbTag = SkICCGetColorProfileTag(srgbFn, srgbMatrix);
+    REPORTER_ASSERT(r, adobeTag != srgbTag);
+    REPORTER_ASSERT(r, srgbTag.equals("sRGB"));
+    REPORTER_ASSERT(r, adobeTag.equals("AdobeRGB"));
+}
+
+static inline void test_raw_transfer_fn(skiatest::Reporter* r, SkICC* icc) {
+    SkICC::Tables tables;
+    bool result = icc->rawTransferFnData(&tables);
+    REPORTER_ASSERT(r, result);
+
+    REPORTER_ASSERT(r, 0.0f == tables.red()[0]);
+    REPORTER_ASSERT(r, 0.0f == tables.green()[0]);
+    REPORTER_ASSERT(r, 0.0f == tables.blue()[0]);
+    REPORTER_ASSERT(r, 1.0f == tables.red()[tables.fRed.fCount - 1]);
+    REPORTER_ASSERT(r, 1.0f == tables.green()[tables.fGreen.fCount - 1]);
+    REPORTER_ASSERT(r, 1.0f == tables.blue()[tables.fBlue.fCount - 1]);
+}
+
+class ICCTest {
+public:
+    static sk_sp<SkICC> MakeICC(sk_sp<SkColorSpace> space) {
+        return sk_sp<SkICC>(new SkICC(std::move(space)));
+    }
+    static sk_sp<SkICC> MakeICC(sk_sp<SkGammas> gammas) {
+        return MakeICC(sk_sp<SkColorSpace>(new SkColorSpace_XYZ(
+                kNonStandard_SkGammaNamed, std::move(gammas),
+                SkMatrix44(SkMatrix44::kIdentity_Constructor), nullptr)));
+    }
+};
+
+DEF_TEST(ICC_RawTransferFns, r) {
+    sk_sp<SkICC> srgb = ICCTest::MakeICC(SkColorSpace::MakeSRGB());
+    test_raw_transfer_fn(r, srgb.get());
+
+    sk_sp<SkICC> adobe =
+            ICCTest::MakeICC(SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named));
+    test_raw_transfer_fn(r, adobe.get());
+
+    // Lookup-table based gamma curves
+    constexpr size_t tableSize = 10;
+    void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize);
+    sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
+    for (int i = 0; i < 3; ++i) {
+        gammas->fType[i] = SkGammas::Type::kTable_Type;
+        gammas->fData[i].fTable.fSize = tableSize;
+        gammas->fData[i].fTable.fOffset = 0;
+    }
+
+    float* table = SkTAddOffset<float>(memory, sizeof(SkGammas));
+    table[0] = 0.00f;
+    table[1] = 0.05f;
+    table[2] = 0.10f;
+    table[3] = 0.15f;
+    table[4] = 0.25f;
+    table[5] = 0.35f;
+    table[6] = 0.45f;
+    table[7] = 0.60f;
+    table[8] = 0.75f;
+    table[9] = 1.00f;
+    sk_sp<SkICC> tbl = ICCTest::MakeICC(gammas);
+    test_raw_transfer_fn(r, tbl.get());
+
+    // Parametric gamma curves
+    memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(SkColorSpaceTransferFn));
+    gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
+    for (int i = 0; i < 3; ++i) {
+        gammas->fType[i] = SkGammas::Type::kParam_Type;
+        gammas->fData[i].fParamOffset = 0;
+    }
+
+    SkColorSpaceTransferFn* params = SkTAddOffset<SkColorSpaceTransferFn>
+            (memory, sizeof(SkGammas));
+
+    // Interval.
+    params->fD = 0.04045f;
+
+    // First equation:
+    params->fC = 1.0f / 12.92f;
+    params->fF = 0.0f;
+
+    // Second equation:
+    // Note that the function is continuous (it's actually sRGB).
+    params->fA = 1.0f / 1.055f;
+    params->fB = 0.055f / 1.055f;
+    params->fE = 0.0f;
+    params->fG = 2.4f;
+    sk_sp<SkICC> param = ICCTest::MakeICC(gammas);
+    test_raw_transfer_fn(r, param.get());
+
+    // Exponential gamma curves
+    gammas = sk_sp<SkGammas>(new SkGammas(3));
+    for (int i = 0; i < 3; ++i) {
+        gammas->fType[i] = SkGammas::Type::kValue_Type;
+        gammas->fData[i].fValue = 1.4f;
+    }
+    sk_sp<SkICC> exp = ICCTest::MakeICC(gammas);
+    test_raw_transfer_fn(r, exp.get());
+
+    gammas = sk_sp<SkGammas>(new SkGammas(3));
+    gammas->fType[0] = gammas->fType[1] = gammas->fType[2] = SkGammas::Type::kNamed_Type;
+    gammas->fData[0].fNamed = kSRGB_SkGammaNamed;
+    gammas->fData[1].fNamed = k2Dot2Curve_SkGammaNamed;
+    gammas->fData[2].fNamed = kLinear_SkGammaNamed;
+    sk_sp<SkICC> named = ICCTest::MakeICC(gammas);
+    test_raw_transfer_fn(r, named.get());
+
+    memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize +
+                                   sizeof(SkColorSpaceTransferFn));
+    gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
+
+    table = SkTAddOffset<float>(memory, sizeof(SkGammas));
+    table[0] = 0.00f;
+    table[1] = 0.15f;
+    table[2] = 0.20f;
+    table[3] = 0.25f;
+    table[4] = 0.35f;
+    table[5] = 0.45f;
+    table[6] = 0.55f;
+    table[7] = 0.70f;
+    table[8] = 0.85f;
+    table[9] = 1.00f;
+
+    params = SkTAddOffset<SkColorSpaceTransferFn>(memory,
+            sizeof(SkGammas) + sizeof(float) * tableSize);
+    params->fA = 1.0f / 1.055f;
+    params->fB = 0.055f / 1.055f;
+    params->fC = 1.0f / 12.92f;
+    params->fD = 0.04045f;
+    params->fE = 0.0f;
+    params->fF = 0.0f;
+    params->fG = 2.4f;
+
+    gammas->fType[0] = SkGammas::Type::kValue_Type;
+    gammas->fData[0].fValue = 1.2f;
+
+    gammas->fType[1] = SkGammas::Type::kTable_Type;
+    gammas->fData[1].fTable.fSize = tableSize;
+    gammas->fData[1].fTable.fOffset = 0;
+
+    gammas->fType[2] = SkGammas::Type::kParam_Type;
+    gammas->fData[2].fParamOffset = sizeof(float) * tableSize;
+    sk_sp<SkICC> nonstd = ICCTest::MakeICC(gammas);
+    test_raw_transfer_fn(r, nonstd.get());
+
+    // Reverse order of table and exponent
+    gammas->fType[1] = SkGammas::Type::kValue_Type;
+    gammas->fData[1].fValue = 1.2f;
+
+    gammas->fType[0] = SkGammas::Type::kTable_Type;
+    gammas->fData[0].fTable.fSize = tableSize;
+    gammas->fData[0].fTable.fOffset = 0;
+    sk_sp<SkICC> nonstd2 = ICCTest::MakeICC(gammas);
+    test_raw_transfer_fn(r, nonstd2.get());
+}
diff --git a/src/third_party/skia/tests/ImageCacheTest.cpp b/src/third_party/skia/tests/ImageCacheTest.cpp
index 9f893bb..457df91 100644
--- a/src/third_party/skia/tests/ImageCacheTest.cpp
+++ b/src/third_party/skia/tests/ImageCacheTest.cpp
@@ -12,11 +12,10 @@
 namespace {
 static void* gGlobalAddress;
 struct TestingKey : public SkResourceCache::Key {
-    void*       fPtr;
     intptr_t    fValue;
 
-    TestingKey(intptr_t value) : fPtr(&gGlobalAddress), fValue(value) {
-        this->init(sizeof(fPtr) + sizeof(fValue));
+    TestingKey(intptr_t value, uint64_t sharedID = 0) : fValue(value) {
+        this->init(&gGlobalAddress, sharedID, sizeof(fValue));
     }
 };
 struct TestingRec : public SkResourceCache::Rec {
@@ -25,13 +24,15 @@
     TestingKey  fKey;
     intptr_t    fValue;
 
-    virtual const Key& getKey() const SK_OVERRIDE { return fKey; }
-    virtual size_t bytesUsed() const SK_OVERRIDE { return sizeof(fKey) + sizeof(fValue); }
+    const Key& getKey() const override { return fKey; }
+    size_t bytesUsed() const override { return sizeof(fKey) + sizeof(fValue); }
+    const char* getCategory() const override { return "test_cache"; }
+    SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
 
     static bool Visitor(const SkResourceCache::Rec& baseRec, void* context) {
         const TestingRec& rec = static_cast<const TestingRec&>(baseRec);
         intptr_t* result = (intptr_t*)context;
-        
+
         *result = rec.fValue;
         return true;
     }
@@ -49,7 +50,7 @@
         REPORTER_ASSERT(reporter, !cache.find(key, TestingRec::Visitor, &value));
         REPORTER_ASSERT(reporter, -1 == value);
 
-        cache.add(SkNEW_ARGS(TestingRec, (key, i)));
+        cache.add(new TestingRec(key, i));
 
         REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value));
         REPORTER_ASSERT(reporter, i == value);
@@ -57,9 +58,9 @@
 
     if (testPurge) {
         // stress test, should trigger purges
-        for (size_t i = 0; i < COUNT * 100; ++i) {
+        for (int i = 0; i < COUNT * 100; ++i) {
             TestingKey key(i);
-            cache.add(SkNEW_ARGS(TestingRec, (key, i)));
+            cache.add(new TestingRec(key, i));
         }
     }
 
@@ -72,6 +73,38 @@
     cache.setTotalByteLimit(0);
 }
 
+static void test_cache_purge_shared_id(skiatest::Reporter* reporter, SkResourceCache& cache) {
+    for (int i = 0; i < COUNT; ++i) {
+        TestingKey key(i, i & 1);   // every other key will have a 1 for its sharedID
+        cache.add(new TestingRec(key, i));
+    }
+
+    // Ensure that everyone is present
+    for (int i = 0; i < COUNT; ++i) {
+        TestingKey key(i, i & 1);   // every other key will have a 1 for its sharedID
+        intptr_t value = -1;
+
+        REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value));
+        REPORTER_ASSERT(reporter, value == i);
+    }
+
+    // Now purge the ones that had a non-zero sharedID (the odd-indexed ones)
+    cache.purgeSharedID(1);
+
+    // Ensure that only the even ones are still present
+    for (int i = 0; i < COUNT; ++i) {
+        TestingKey key(i, i & 1);   // every other key will have a 1 for its sharedID
+        intptr_t value = -1;
+
+        if (i & 1) {
+            REPORTER_ASSERT(reporter, !cache.find(key, TestingRec::Visitor, &value));
+        } else {
+            REPORTER_ASSERT(reporter, cache.find(key, TestingRec::Visitor, &value));
+            REPORTER_ASSERT(reporter, value == i);
+        }
+    }
+}
+
 #include "SkDiscardableMemoryPool.h"
 
 static SkDiscardableMemoryPool* gPool;
@@ -88,8 +121,7 @@
         test_cache(reporter, cache, true);
     }
     {
-        SkAutoTUnref<SkDiscardableMemoryPool> pool(
-                SkDiscardableMemoryPool::Create(defLimit, NULL));
+        sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(defLimit));
         gPool = pool.get();
         SkResourceCache cache(pool_factory);
         test_cache(reporter, cache, true);
@@ -98,6 +130,10 @@
         SkResourceCache cache(SkDiscardableMemory::Create);
         test_cache(reporter, cache, false);
     }
+    {
+        SkResourceCache cache(defLimit);
+        test_cache_purge_shared_id(reporter, cache);
+    }
 }
 
 DEF_TEST(ImageCache_doubleAdd, r) {
@@ -106,8 +142,8 @@
 
     TestingKey key(1);
 
-    cache.add(SkNEW_ARGS(TestingRec, (key, 2)));
-    cache.add(SkNEW_ARGS(TestingRec, (key, 3)));
+    cache.add(new TestingRec(key, 2));
+    cache.add(new TestingRec(key, 3));
 
     // Lookup can return either value.
     intptr_t value = -1;
diff --git a/src/third_party/skia/tests/ImageDecodingTest.cpp b/src/third_party/skia/tests/ImageDecodingTest.cpp
deleted file mode 100644
index 30665a6..0000000
--- a/src/third_party/skia/tests/ImageDecodingTest.cpp
+++ /dev/null
@@ -1,819 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Resources.h"
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkColor.h"
-#include "SkColorPriv.h"
-#include "SkData.h"
-#include "SkDecodingImageGenerator.h"
-#include "SkDiscardableMemoryPool.h"
-#include "SkForceLinking.h"
-#include "SkGradientShader.h"
-#include "SkImageDecoder.h"
-#include "SkImageEncoder.h"
-#include "SkImageGeneratorPriv.h"
-#include "SkImagePriv.h"
-#include "SkOSFile.h"
-#include "SkPoint.h"
-#include "SkShader.h"
-#include "SkStream.h"
-#include "SkString.h"
-#include "Test.h"
-
-__SK_FORCE_IMAGE_DECODER_LINKING;
-
-/**
- *  Interprets c as an unpremultiplied color, and returns the
- *  premultiplied equivalent.
- */
-static SkPMColor premultiply_unpmcolor(SkPMColor c) {
-    U8CPU a = SkGetPackedA32(c);
-    U8CPU r = SkGetPackedR32(c);
-    U8CPU g = SkGetPackedG32(c);
-    U8CPU b = SkGetPackedB32(c);
-    return SkPreMultiplyARGB(a, r, g, b);
-}
-
-/**
- *  Return true if this stream format should be skipped, due
- *  to do being an opaque format or not a valid format.
- */
-static bool skip_image_format(SkImageDecoder::Format format) {
-    switch (format) {
-        case SkImageDecoder::kPNG_Format:
-        case SkImageDecoder::kWEBP_Format:
-            return false;
-        // Skip unknown since it will not be decoded anyway.
-        case SkImageDecoder::kUnknown_Format:
-        // Technically ICO and BMP supports alpha channels, but our image
-        // decoders do not, so skip them as well.
-        case SkImageDecoder::kICO_Format:
-        case SkImageDecoder::kBMP_Format:
-        // KTX and ASTC are texture formats so it's not particularly clear how to 
-        // decode the alpha from them.
-        case SkImageDecoder::kKTX_Format:
-        case SkImageDecoder::kASTC_Format:
-        // The rest of these are opaque.
-        case SkImageDecoder::kPKM_Format:
-        case SkImageDecoder::kWBMP_Format:
-        case SkImageDecoder::kGIF_Format:
-        case SkImageDecoder::kJPEG_Format:
-            return true;
-    }
-    SkASSERT(false);
-    return true;
-}
-
-/**
- *  Test decoding an image in premultiplied mode and unpremultiplied mode and compare
- *  them.
- */
-static void compare_unpremul(skiatest::Reporter* reporter, const SkString& filename) {
-    // Decode a resource:
-    SkBitmap bm8888;
-    SkBitmap bm8888Unpremul;
-
-    SkFILEStream stream(filename.c_str());
-
-    SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&stream);
-    if (skip_image_format(format)) {
-        return;
-    }
-
-    SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
-    if (NULL == decoder.get()) {
-        SkDebugf("couldn't decode %s\n", filename.c_str());
-        return;
-    }
-
-    bool success = decoder->decode(&stream, &bm8888, kN32_SkColorType,
-                                   SkImageDecoder::kDecodePixels_Mode);
-    if (!success) {
-        return;
-    }
-
-    success = stream.rewind();
-    REPORTER_ASSERT(reporter, success);
-    if (!success) {
-        return;
-    }
-
-    decoder->setRequireUnpremultipliedColors(true);
-    success = decoder->decode(&stream, &bm8888Unpremul, kN32_SkColorType,
-                              SkImageDecoder::kDecodePixels_Mode);
-    if (!success) {
-        return;
-    }
-
-    bool dimensionsMatch = bm8888.width() == bm8888Unpremul.width()
-                           && bm8888.height() == bm8888Unpremul.height();
-    REPORTER_ASSERT(reporter, dimensionsMatch);
-    if (!dimensionsMatch) {
-        return;
-    }
-
-    // Only do the comparison if the two bitmaps are both 8888.
-    if (bm8888.colorType() != kN32_SkColorType || bm8888Unpremul.colorType() != kN32_SkColorType) {
-        return;
-    }
-
-    // Now compare the two bitmaps.
-    for (int i = 0; i < bm8888.width(); ++i) {
-        for (int j = 0; j < bm8888.height(); ++j) {
-            // "c0" is the color of the premultiplied bitmap at (i, j).
-            const SkPMColor c0 = *bm8888.getAddr32(i, j);
-            // "c1" is the result of premultiplying the color of the unpremultiplied
-            // bitmap at (i, j).
-            const SkPMColor c1 = premultiply_unpmcolor(*bm8888Unpremul.getAddr32(i, j));
-            // Compute the difference for each component.
-            int da = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1));
-            int dr = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1));
-            int dg = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1));
-            int db = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1));
-
-            // Alpha component must be exactly the same.
-            REPORTER_ASSERT(reporter, 0 == da);
-
-            // Color components may not match exactly due to rounding error.
-            REPORTER_ASSERT(reporter, dr <= 1);
-            REPORTER_ASSERT(reporter, dg <= 1);
-            REPORTER_ASSERT(reporter, db <= 1);
-        }
-    }
-}
-
-static void test_unpremul(skiatest::Reporter* reporter) {
-    // This test cannot run if there is no resource path.
-    SkString resourcePath = GetResourcePath();
-    if (resourcePath.isEmpty()) {
-        SkDebugf("Could not run unpremul test because resourcePath not specified.");
-        return;
-    }
-    SkOSFile::Iter iter(resourcePath.c_str());
-    SkString basename;
-    if (iter.next(&basename)) {
-        do {
-            SkString filename = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
-            // SkDebugf("about to decode \"%s\"\n", filename.c_str());
-            compare_unpremul(reporter, filename);
-        } while (iter.next(&basename));
-    } else {
-        SkDebugf("Failed to find any files :(\n");
-    }
-}
-
-#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
-// Test that the alpha type is what we expect.
-static void test_alphaType(skiatest::Reporter* reporter, const SkString& filename,
-                           bool requireUnpremul) {
-    SkBitmap bm;
-    SkFILEStream stream(filename.c_str());
-
-    SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
-    if (NULL == decoder.get()) {
-        return;
-    }
-
-    decoder->setRequireUnpremultipliedColors(requireUnpremul);
-
-    // Decode just the bounds. This should always succeed.
-    bool success = decoder->decode(&stream, &bm, kN32_SkColorType,
-                                   SkImageDecoder::kDecodeBounds_Mode);
-    REPORTER_ASSERT(reporter, success);
-    if (!success) {
-        return;
-    }
-
-    // Keep track of the alpha type for testing later. If the full decode
-    // succeeds, the alpha type should be the same, unless the full decode
-    // determined that the alpha type should actually be opaque, which may
-    // not be known when only decoding the bounds.
-    const SkAlphaType boundsAlphaType = bm.alphaType();
-
-    // rewind should always succeed on SkFILEStream.
-    success = stream.rewind();
-    REPORTER_ASSERT(reporter, success);
-    if (!success) {
-        return;
-    }
-
-    success = decoder->decode(&stream, &bm, kN32_SkColorType, SkImageDecoder::kDecodePixels_Mode);
-
-    if (!success) {
-        // When the decoder is set to require unpremul, if it does not support
-        // unpremul it will fail. This is the only reason the decode should
-        // fail (since we know the files we are using to test can be decoded).
-        REPORTER_ASSERT(reporter, requireUnpremul);
-        return;
-    }
-
-    // The bounds decode should return with either the requested
-    // premul/unpremul or opaque, if that value could be determined when only
-    // decoding the bounds.
-    if (requireUnpremul) {
-        REPORTER_ASSERT(reporter, kUnpremul_SkAlphaType == boundsAlphaType
-                                  || kOpaque_SkAlphaType == boundsAlphaType);
-    } else {
-        REPORTER_ASSERT(reporter, kPremul_SkAlphaType == boundsAlphaType
-                                  || kOpaque_SkAlphaType == boundsAlphaType);
-    }
-
-    // When decoding the full image, the alpha type should match the one
-    // returned by the bounds decode, unless the full decode determined that
-    // the alpha type is actually opaque.
-    REPORTER_ASSERT(reporter, bm.alphaType() == boundsAlphaType
-                              || bm.alphaType() == kOpaque_SkAlphaType);
-}
-
-DEF_TEST(ImageDecoding_alphaType, reporter) {
-    SkString resourcePath = GetResourcePath();
-    if (resourcePath.isEmpty()) {
-        SkDebugf("Could not run alphaType test because resourcePath not specified.");
-        return;
-    }
-
-    SkOSFile::Iter iter(resourcePath.c_str());
-    SkString basename;
-    if (iter.next(&basename)) {
-        do {
-            SkString filename = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
-            for (int truth = 0; truth <= 1; ++truth) {
-                test_alphaType(reporter, filename, SkToBool(truth));
-            }
-        } while (iter.next(&basename));
-    } else {
-        SkDebugf("Failed to find any files :(\n");
-    }
-
-}
-
-// Using known images, test that decoding into unpremul and premul behave as expected.
-DEF_TEST(ImageDecoding_unpremul, reporter) {
-    SkString resourcePath = GetResourcePath();
-    if (resourcePath.isEmpty()) {
-        SkDebugf("Could not run unpremul test because resourcePath not specified.");
-        return;
-    }
-    const char* root = "half-transparent-white-pixel";
-    const char* suffixes[] = { ".png", ".webp" };
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(suffixes); ++i) {
-        SkString basename = SkStringPrintf("%s%s", root, suffixes[i]);
-        SkString fullName = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
-
-        SkBitmap bm;
-        SkFILEStream stream(fullName.c_str());
-
-        if (!stream.isValid()) {
-            SkDebugf("file %s missing from resource directoy %s\n",
-                     basename.c_str(), resourcePath.c_str());
-            continue;
-        }
-
-        // This should never fail since we know the images we're decoding.
-        SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
-        REPORTER_ASSERT(reporter, decoder.get());
-        if (NULL == decoder.get()) {
-            continue;
-        }
-
-        // Test unpremultiplied. We know what color this should result in.
-        decoder->setRequireUnpremultipliedColors(true);
-        bool success = decoder->decode(&stream, &bm, kN32_SkColorType,
-                                       SkImageDecoder::kDecodePixels_Mode);
-        REPORTER_ASSERT(reporter, success);
-        if (!success) {
-            continue;
-        }
-
-        REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
-        {
-            SkAutoLockPixels alp(bm);
-            REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7fffffff);
-        }
-
-        success = stream.rewind();
-        REPORTER_ASSERT(reporter, success);
-        if (!success) {
-            continue;
-        }
-
-        // Test premultiplied. Once again, we know which color this should
-        // result in.
-        decoder->setRequireUnpremultipliedColors(false);
-        success = decoder->decode(&stream, &bm, kN32_SkColorType,
-                                  SkImageDecoder::kDecodePixels_Mode);
-        REPORTER_ASSERT(reporter, success);
-        if (!success) {
-            continue;
-        }
-
-        REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
-        {
-            SkAutoLockPixels alp(bm);
-            REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7f7f7f7f);
-        }
-    }
-}
-#endif // SK_BUILD_FOR_UNIX/ANDROID skbug.com/2388
-
-#ifdef SK_DEBUG
-// Create a stream containing a bitmap encoded to Type type.
-static SkMemoryStream* create_image_stream(SkImageEncoder::Type type) {
-    SkBitmap bm;
-    const int size = 50;
-    bm.allocN32Pixels(size, size);
-    SkCanvas canvas(bm);
-    SkPoint points[2] = {
-        { SkIntToScalar(0), SkIntToScalar(0) },
-        { SkIntToScalar(size), SkIntToScalar(size) }
-    };
-    SkColor colors[2] = { SK_ColorWHITE, SK_ColorBLUE };
-    SkShader* shader = SkGradientShader::CreateLinear(points, colors, NULL,
-                                                      SK_ARRAY_COUNT(colors),
-                                                      SkShader::kClamp_TileMode);
-    SkPaint paint;
-    paint.setShader(shader)->unref();
-    canvas.drawPaint(paint);
-    // Now encode it to a stream.
-    SkAutoTUnref<SkData> data(SkImageEncoder::EncodeData(bm, type, 100));
-    if (NULL == data.get()) {
-        return NULL;
-    }
-    return SkNEW_ARGS(SkMemoryStream, (data.get()));
-}
-
-// For every format that supports tile based decoding, ensure that
-// calling decodeSubset will not fail if the caller has unreffed the
-// stream provided in buildTileIndex.
-// Only runs in debug mode since we are testing for a crash.
-static void test_stream_life() {
-    const SkImageEncoder::Type gTypes[] = {
-#ifdef SK_BUILD_FOR_ANDROID
-        SkImageEncoder::kJPEG_Type,
-        SkImageEncoder::kPNG_Type,
-#endif
-        SkImageEncoder::kWEBP_Type,
-    };
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gTypes); ++i) {
-        // SkDebugf("encoding to %i\n", i);
-        SkAutoTUnref<SkMemoryStream> stream(create_image_stream(gTypes[i]));
-        if (NULL == stream.get()) {
-            SkDebugf("no stream\n");
-            continue;
-        }
-        SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
-        if (NULL == decoder.get()) {
-            SkDebugf("no decoder\n");
-            continue;
-        }
-        int width, height;
-        if (!decoder->buildTileIndex(stream.get(), &width, &height)) {
-            SkDebugf("could not build a tile index\n");
-            continue;
-        }
-        // Now unref the stream to make sure it survives
-        stream.reset(NULL);
-        SkBitmap bm;
-        decoder->decodeSubset(&bm, SkIRect::MakeWH(width, height), kN32_SkColorType);
-    }
-}
-
-// Test inside SkScaledBitmapSampler.cpp
-extern void test_row_proc_choice();
-
-#endif  // SK_DEBUG
-
-DEF_TEST(ImageDecoding, reporter) {
-    test_unpremul(reporter);
-#ifdef SK_DEBUG
-    test_stream_life();
-    test_row_proc_choice();
-#endif
-}
-
-// expected output for 8x8 bitmap
-static const int kExpectedWidth = 8;
-static const int kExpectedHeight = 8;
-static const SkColor kExpectedPixels[] = {
-    0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
-    0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
-    0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
-    0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
-    0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
-    0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
-    0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
-    0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
-    0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
-    0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
-    0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
-    0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
-    0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
-    0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
-    0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
-    0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
-};
-SK_COMPILE_ASSERT((kExpectedWidth * kExpectedHeight)
-                  == SK_ARRAY_COUNT(kExpectedPixels), array_size_mismatch);
-
-DEF_TEST(WebP, reporter) {
-    const unsigned char encodedWebP[] = {
-        0x52, 0x49, 0x46, 0x46, 0x2c, 0x01, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
-        0x56, 0x50, 0x38, 0x4c, 0x20, 0x01, 0x00, 0x00, 0x2f, 0x07, 0xc0, 0x01,
-        0x00, 0xff, 0x01, 0x45, 0x03, 0x00, 0xe2, 0xd5, 0xae, 0x60, 0x2b, 0xad,
-        0xd9, 0x68, 0x76, 0xb6, 0x8d, 0x6a, 0x1d, 0xc0, 0xe6, 0x19, 0xd6, 0x16,
-        0xb7, 0xb4, 0xef, 0xcf, 0xc3, 0x15, 0x6c, 0xb3, 0xbd, 0x77, 0x0d, 0x85,
-        0x6d, 0x1b, 0xa9, 0xb1, 0x2b, 0xdc, 0x3d, 0x83, 0xdb, 0x00, 0x00, 0xc8,
-        0x26, 0xe5, 0x01, 0x99, 0x8a, 0xd5, 0xdd, 0xfc, 0x82, 0xcd, 0xcd, 0x9a,
-        0x8c, 0x13, 0xcc, 0x1b, 0xba, 0xf5, 0x05, 0xdb, 0xee, 0x6a, 0xdb, 0x38,
-        0x60, 0xfe, 0x43, 0x2c, 0xd4, 0x6a, 0x99, 0x4d, 0xc6, 0xc0, 0xd3, 0x28,
-        0x1b, 0xc1, 0xb1, 0x17, 0x4e, 0x43, 0x0e, 0x3d, 0x27, 0xe9, 0xe4, 0x84,
-        0x4f, 0x24, 0x62, 0x69, 0x85, 0x43, 0x8d, 0xc2, 0x04, 0x00, 0x07, 0x59,
-        0x60, 0xfd, 0x8b, 0x4d, 0x60, 0x32, 0x72, 0xcf, 0x88, 0x0c, 0x2f, 0x2f,
-        0xad, 0x62, 0xbd, 0x27, 0x09, 0x16, 0x70, 0x78, 0x6c, 0xd9, 0x82, 0xef,
-        0x1a, 0xa2, 0xcc, 0xf0, 0xf1, 0x6f, 0xd8, 0x78, 0x2e, 0x39, 0xa1, 0xcf,
-        0x14, 0x4b, 0x89, 0xb4, 0x1b, 0x48, 0x15, 0x7c, 0x48, 0x6f, 0x8c, 0x20,
-        0xb7, 0x00, 0xcf, 0xfc, 0xdb, 0xd0, 0xe9, 0xe7, 0x42, 0x09, 0xa4, 0x03,
-        0x40, 0xac, 0xda, 0x40, 0x01, 0x00, 0x5f, 0xa1, 0x3d, 0x64, 0xe1, 0xf4,
-        0x03, 0x45, 0x29, 0xe0, 0xe2, 0x4a, 0xc3, 0xa2, 0xe8, 0xe0, 0x25, 0x12,
-        0x74, 0xc6, 0xe8, 0xfb, 0x93, 0x4f, 0x9f, 0x5e, 0xc0, 0xa6, 0x91, 0x1b,
-        0xa4, 0x24, 0x82, 0xc3, 0x61, 0x07, 0x4c, 0x49, 0x4f, 0x53, 0xae, 0x5f,
-        0x5d, 0x39, 0x36, 0xc0, 0x5b, 0x57, 0x54, 0x60, 0x10, 0x00, 0x00, 0xd1,
-        0x68, 0xb6, 0x6d, 0xdb, 0x36, 0x22, 0xfa, 0x1f, 0x35, 0x75, 0x22, 0xec,
-        0x31, 0xbc, 0x5d, 0x8f, 0x87, 0x53, 0xa2, 0x05, 0x8c, 0x2f, 0xcd, 0xa8,
-        0xa7, 0xf3, 0xa3, 0xbd, 0x83, 0x8b, 0x2a, 0xc8, 0x58, 0xf5, 0xac, 0x80,
-        0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
-        0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
-    };
-    SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP,
-                                                sizeof(encodedWebP)));
-    SkBitmap bm;
-
-    bool success = SkInstallDiscardablePixelRef(
-        SkDecodingImageGenerator::Create(encoded,
-            SkDecodingImageGenerator::Options()), &bm);
-
-    REPORTER_ASSERT(reporter, success);
-    if (!success) {
-        return;
-    }
-    SkAutoLockPixels alp(bm);
-
-    bool rightSize = ((kExpectedWidth == bm.width())
-                      && (kExpectedHeight == bm.height()));
-    REPORTER_ASSERT(reporter, rightSize);
-    if (rightSize) {
-        bool error = false;
-        const SkColor* correctPixel = kExpectedPixels;
-        for (int y = 0; y < bm.height(); ++y) {
-            for (int x = 0; x < bm.width(); ++x) {
-                error |= (*correctPixel != bm.getColor(x, y));
-                ++correctPixel;
-            }
-        }
-        REPORTER_ASSERT(reporter, !error);
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-// example of how Android will do this inside their BitmapFactory
-static SkPixelRef* install_pixel_ref(SkBitmap* bitmap,
-                                     SkStreamRewindable* stream,
-                                     int sampleSize, bool ditherImage) {
-    SkASSERT(bitmap != NULL);
-    SkASSERT(stream != NULL);
-    SkASSERT(stream->rewind());
-    SkASSERT(stream->unique());
-    SkColorType colorType = bitmap->colorType();
-    SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
-    if (SkInstallDiscardablePixelRef(
-                SkDecodingImageGenerator::Create(stream, opts), bitmap)) {
-        return bitmap->pixelRef();
-    }
-    return NULL;
-}
-/**
- *  A test for the SkDecodingImageGenerator::Create and
- *  SkInstallDiscardablePixelRef functions.
- */
-DEF_TEST(ImprovedBitmapFactory, reporter) {
-    SkString pngFilename = GetResourcePath("randPixels.png");
-    SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(pngFilename.c_str()));
-    if (sk_exists(pngFilename.c_str())) {
-        SkBitmap bm;
-        SkAssertResult(bm.setInfo(SkImageInfo::MakeN32Premul(1, 1)));
-        REPORTER_ASSERT(reporter,
-            install_pixel_ref(&bm, stream.detach(), 1, true));
-        SkAutoLockPixels alp(bm);
-        REPORTER_ASSERT(reporter, bm.getPixels());
-    }
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
-
-#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
-static inline bool check_rounding(int value, int dividend, int divisor) {
-    // returns true if the value is greater than floor(dividend/divisor)
-    // and less than SkNextPow2(ceil(dividend - divisor))
-    return (((divisor * value) > (dividend - divisor))
-            && value <= SkNextPow2(((dividend - 1) / divisor) + 1));
-}
-#endif  // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
-
-
-#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
-    #define kBackwards_SkColorType kRGBA_8888_SkColorType
-#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
-    #define kBackwards_SkColorType kBGRA_8888_SkColorType
-#else
-    #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
-#endif
-
-static inline const char* SkColorType_to_string(SkColorType colorType) {
-    switch(colorType) {
-        case kAlpha_8_SkColorType:   return "Alpha_8";
-        case kRGB_565_SkColorType:   return "RGB_565";
-        case kARGB_4444_SkColorType: return "ARGB_4444";
-        case kN32_SkColorType:       return "N32";
-        case kBackwards_SkColorType: return "Backwards";
-        case kIndex_8_SkColorType:   return "Index_8";
-        default:                     return "ERROR";
-    }
-}
-
-static inline const char* options_colorType(
-        const SkDecodingImageGenerator::Options& opts) {
-    if (opts.fUseRequestedColorType) {
-        return SkColorType_to_string(opts.fRequestedColorType);
-    } else {
-        return "(none)";
-    }
-}
-
-static inline const char* yn(bool value) {
-    if (value) {
-        return "yes";
-    } else {
-        return "no";
-    }
-}
-
-/**
- * Given either a SkStream or a SkData, try to decode the encoded
- * image using the specified options and report errors.
- */
-static void test_options(skiatest::Reporter* reporter,
-                         const SkDecodingImageGenerator::Options& opts,
-                         SkStreamRewindable* encodedStream,
-                         SkData* encodedData,
-                         bool useData,
-                         const SkString& path) {
-    SkBitmap bm;
-    bool success = false;
-    if (useData) {
-        if (NULL == encodedData) {
-            return;
-        }
-        success = SkInstallDiscardablePixelRef(
-            SkDecodingImageGenerator::Create(encodedData, opts), &bm);
-    } else {
-        if (NULL == encodedStream) {
-            return;
-        }
-        success = SkInstallDiscardablePixelRef(
-            SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts), &bm);
-    }
-    if (!success) {
-        if (opts.fUseRequestedColorType
-            && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
-            return;  // Ignore known conversion inabilities.
-        }
-        // If we get here, it's a failure and we will need more
-        // information about why it failed.
-        ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
-               "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
-               options_colorType(opts), path.c_str());
-        return;
-    }
-    #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
-    // Android is the only system that use Skia's image decoders in
-    // production.  For now, we'll only verify that samplesize works
-    // on systems where it already is known to work.
-    REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
-                                             opts.fSampleSize));
-    REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
-                                             opts.fSampleSize));
-    // The ImageDecoder API doesn't guarantee that SampleSize does
-    // anything at all, but the decoders that this test excercises all
-    // produce an output size in the following range:
-    //    (((sample_size * out_size) > (in_size - sample_size))
-    //     && out_size <= SkNextPow2(((in_size - 1) / sample_size) + 1));
-    #endif  // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
-    SkAutoLockPixels alp(bm);
-    if (bm.getPixels() == NULL) {
-        ERRORF(reporter, "Pixel decode failed [sampleSize=%d dither=%s "
-               "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
-               options_colorType(opts), path.c_str());
-        return;
-    }
-
-    SkColorType requestedColorType = opts.fRequestedColorType;
-    REPORTER_ASSERT(reporter,
-                    (!opts.fUseRequestedColorType)
-                    || (bm.colorType() == requestedColorType));
-
-    // Condition under which we should check the decoding results:
-    if ((kN32_SkColorType == bm.colorType())
-        && (!path.endsWith(".jpg"))  // lossy
-        && (opts.fSampleSize == 1)) {  // scaled
-        const SkColor* correctPixels = kExpectedPixels;
-        SkASSERT(bm.height() == kExpectedHeight);
-        SkASSERT(bm.width() == kExpectedWidth);
-        int pixelErrors = 0;
-        for (int y = 0; y < bm.height(); ++y) {
-            for (int x = 0; x < bm.width(); ++x) {
-                if (*correctPixels != bm.getColor(x, y)) {
-                    ++pixelErrors;
-                }
-                ++correctPixels;
-            }
-        }
-        if (pixelErrors != 0) {
-            ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
-                   "[sampleSize=%d dither=%s colorType=%s %s]",
-                   pixelErrors, kExpectedHeight * kExpectedWidth,
-                   opts.fSampleSize, yn(opts.fDitherImage),
-                   options_colorType(opts), path.c_str());
-        }
-    }
-}
-
-/**
- *  SkDecodingImageGenerator has an Options struct which lets the
- *  client of the generator set sample size, dithering, and bitmap
- *  config.  This test loops through many possible options and tries
- *  them on a set of 5 small encoded images (each in a different
- *  format).  We test both SkData and SkStreamRewindable decoding.
- */
-DEF_TEST(ImageDecoderOptions, reporter) {
-    const char* files[]  = {
-        "randPixels.bmp",
-        "randPixels.jpg",
-        "randPixels.png",
-        "randPixels.webp",
-        #if !defined(SK_BUILD_FOR_WIN)
-        // TODO(halcanary): Find out why this fails sometimes.
-        "randPixels.gif",
-        #endif
-    };
-
-    SkString resourceDir = GetResourcePath();
-    if (!sk_exists(resourceDir.c_str())) {
-        return;
-    }
-
-    int scaleList[] = {1, 2, 3, 4};
-    bool ditherList[] = {true, false};
-    SkColorType colorList[] = {
-        kAlpha_8_SkColorType,
-        kRGB_565_SkColorType,
-        kARGB_4444_SkColorType,  // Most decoders will fail on 4444.
-        kN32_SkColorType
-        // Note that indexed color is left out of the list.  Lazy
-        // decoding doesn't do indexed color.
-    };
-    const bool useDataList[] = {true, false};
-
-    for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
-        SkString path = SkOSPath::Join(resourceDir.c_str(), files[fidx]);
-        if (!sk_exists(path.c_str())) {
-            continue;
-        }
-
-        SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
-        REPORTER_ASSERT(reporter, encodedData.get() != NULL);
-        SkAutoTUnref<SkStreamRewindable> encodedStream(
-            SkStream::NewFromFile(path.c_str()));
-        REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
-
-        for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
-            for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
-                for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
-                    for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
-                        SkDecodingImageGenerator::Options opts(scaleList[i],
-                                                               ditherList[j],
-                                                               colorList[k]);
-                        test_options(reporter, opts, encodedStream, encodedData,
-                                     useDataList[m], path);
-
-                    }
-                    SkDecodingImageGenerator::Options options(scaleList[i],
-                                                              ditherList[j]);
-                    test_options(reporter, options, encodedStream, encodedData,
-                                 useDataList[m], path);
-                }
-            }
-        }
-    }
-}
-
-DEF_TEST(DiscardablePixelRef_SecondLockColorTableCheck, r) {
-    SkString resourceDir = GetResourcePath();
-    SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.gif");
-    if (!sk_exists(path.c_str())) {
-        return;
-    }
-    SkAutoDataUnref encoded(SkData::NewFromFileName(path.c_str()));
-    SkBitmap bitmap;
-    if (!SkInstallDiscardablePixelRef(
-            SkDecodingImageGenerator::Create(
-                    encoded, SkDecodingImageGenerator::Options()), &bitmap)) {
-        #ifndef SK_BUILD_FOR_WIN
-        ERRORF(r, "SkInstallDiscardablePixelRef [randPixels.gif] failed.");
-        #endif
-        return;
-    }
-    if (kIndex_8_SkColorType != bitmap.colorType()) {
-        return;
-    }
-    {
-        SkAutoLockPixels alp(bitmap);
-        REPORTER_ASSERT(r, bitmap.getColorTable() && "first pass");
-    }
-    {
-        SkAutoLockPixels alp(bitmap);
-        REPORTER_ASSERT(r, bitmap.getColorTable() && "second pass");
-    }
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
-namespace {
-class SingleAllocator : public SkBitmap::Allocator {
-public:
-    SingleAllocator(void* p, size_t s) : fPixels(p), fSize(s) { }
-    ~SingleAllocator() {}
-    // If the pixels in fPixels are big enough, use them.
-    virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
-        SkASSERT(bm);
-        if (bm->info().getSafeSize(bm->rowBytes()) <= fSize) {
-            bm->setPixels(fPixels, ct);
-            fPixels = NULL;
-            fSize = 0;
-            return true;
-        }
-        return bm->tryAllocPixels(NULL, ct);
-    }
-    bool ready() { return fPixels != NULL; }
-private:
-    void* fPixels;
-    size_t fSize;
-};
-}  // namespace
-
-/*  This tests for a bug in libjpeg where INT32 is typedefed to long
-    and memory can be written to outside of the array. */
-DEF_TEST(ImageDecoding_JpegOverwrite, r) {
-    SkString resourceDir = GetResourcePath();
-    SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.jpg");
-    SkAutoTUnref<SkStreamAsset> stream(
-            SkStream::NewFromFile(path.c_str()));
-    if (!stream.get()) {
-        SkDebugf("\nPath '%s' missing.\n", path.c_str());
-        return;
-    }
-    SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
-    if (NULL == decoder.get()) {
-        ERRORF(r, "\nSkImageDecoder::Factory failed.\n");
-        return;
-    }
-    SkAssertResult(stream->rewind());
-
-    static const uint16_t sentinal = 0xBEEF;
-    static const int pixelCount = 16;
-    SkAutoTMalloc<uint16_t> pixels(pixelCount + 1);
-    // pixels.get() should be 4-byte aligned.
-    // This is necessary to reproduce the bug.
-
-    pixels[pixelCount] = sentinal;  // This value should not be changed.
-
-    SkAutoTUnref<SingleAllocator> allocator(
-            SkNEW_ARGS(SingleAllocator,
-                       ((void*)pixels.get(), sizeof(uint16_t) * pixelCount)));
-    decoder->setAllocator(allocator);
-    decoder->setSampleSize(2);
-    SkBitmap bitmap;
-    bool success = decoder->decode(stream, &bitmap, kRGB_565_SkColorType,
-                                   SkImageDecoder::kDecodePixels_Mode);
-    REPORTER_ASSERT(r, success);
-    REPORTER_ASSERT(r, !allocator->ready());  // Decoder used correct memory
-    REPORTER_ASSERT(r, sentinal == pixels[pixelCount]);
-}
diff --git a/src/third_party/skia/tests/ImageFilterCacheTest.cpp b/src/third_party/skia/tests/ImageFilterCacheTest.cpp
new file mode 100644
index 0000000..d718181
--- /dev/null
+++ b/src/third_party/skia/tests/ImageFilterCacheTest.cpp
@@ -0,0 +1,278 @@
+ /*
+  * Copyright 2016 Google Inc.
+  *
+  * Use of this source code is governed by a BSD-style license that can be
+  * found in the LICENSE file.
+  */
+
+#include "Test.h"
+
+#include "SkBitmap.h"
+#include "SkImage.h"
+#include "SkImageFilter.h"
+#include "SkImageFilterCache.h"
+#include "SkMatrix.h"
+#include "SkSpecialImage.h"
+
+static const int kSmallerSize = 10;
+static const int kPad = 3;
+static const int kFullSize = kSmallerSize + 2 * kPad;
+
+static SkBitmap create_bm() {
+    SkBitmap bm;
+    bm.allocN32Pixels(kFullSize, kFullSize, true);
+    bm.eraseColor(SK_ColorTRANSPARENT);
+    return bm;
+}
+
+// Ensure the cache can return a cached image
+static void test_find_existing(skiatest::Reporter* reporter,
+                               const sk_sp<SkSpecialImage>& image,
+                               const sk_sp<SkSpecialImage>& subset) {
+    static const size_t kCacheSize = 1000000;
+    sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
+
+    SkIRect clip = SkIRect::MakeWH(100, 100);
+    SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
+    SkImageFilterCacheKey key2(0, SkMatrix::I(), clip, subset->uniqueID(), subset->subset());
+
+    SkIPoint offset = SkIPoint::Make(3, 4);
+    cache->set(key1, image.get(), offset, nullptr);
+
+    SkIPoint foundOffset;
+
+    sk_sp<SkSpecialImage> foundImage = cache->get(key1, &foundOffset);
+    REPORTER_ASSERT(reporter, foundImage);
+    REPORTER_ASSERT(reporter, offset == foundOffset);
+
+    REPORTER_ASSERT(reporter, !cache->get(key2, &foundOffset));
+}
+
+// If either id is different or the clip or the matrix are different the
+// cached image won't be found. Even if it is caching the same bitmap.
+static void test_dont_find_if_diff_key(skiatest::Reporter* reporter,
+                                       const sk_sp<SkSpecialImage>& image,
+                                       const sk_sp<SkSpecialImage>& subset) {
+    static const size_t kCacheSize = 1000000;
+    sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
+
+    SkIRect clip1 = SkIRect::MakeWH(100, 100);
+    SkIRect clip2 = SkIRect::MakeWH(200, 200);
+    SkImageFilterCacheKey key0(0, SkMatrix::I(), clip1, image->uniqueID(), image->subset());
+    SkImageFilterCacheKey key1(1, SkMatrix::I(), clip1, image->uniqueID(), image->subset());
+    SkImageFilterCacheKey key2(0, SkMatrix::MakeTrans(5, 5), clip1,
+                                   image->uniqueID(), image->subset());
+    SkImageFilterCacheKey key3(0, SkMatrix::I(), clip2, image->uniqueID(), image->subset());
+    SkImageFilterCacheKey key4(0, SkMatrix::I(), clip1, subset->uniqueID(), subset->subset());
+
+    SkIPoint offset = SkIPoint::Make(3, 4);
+    cache->set(key0, image.get(), offset, nullptr);
+
+    SkIPoint foundOffset;
+    REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset));
+    REPORTER_ASSERT(reporter, !cache->get(key2, &foundOffset));
+    REPORTER_ASSERT(reporter, !cache->get(key3, &foundOffset));
+    REPORTER_ASSERT(reporter, !cache->get(key4, &foundOffset));
+}
+
+// Test purging when the max cache size is exceeded
+static void test_internal_purge(skiatest::Reporter* reporter, const sk_sp<SkSpecialImage>& image) {
+    SkASSERT(image->getSize());
+    const size_t kCacheSize = image->getSize() + 10;
+    sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
+
+    SkIRect clip = SkIRect::MakeWH(100, 100);
+    SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
+    SkImageFilterCacheKey key2(1, SkMatrix::I(), clip, image->uniqueID(), image->subset());
+
+    SkIPoint offset = SkIPoint::Make(3, 4);
+    cache->set(key1, image.get(), offset, nullptr);
+
+    SkIPoint foundOffset;
+
+    REPORTER_ASSERT(reporter, cache->get(key1, &foundOffset));
+
+    // This should knock the first one out of the cache
+    cache->set(key2, image.get(), offset, nullptr);
+
+    REPORTER_ASSERT(reporter, cache->get(key2, &foundOffset));
+    REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset));
+}
+
+// Exercise the purgeByKey and purge methods
+static void test_explicit_purging(skiatest::Reporter* reporter,
+                                  const sk_sp<SkSpecialImage>& image,
+                                  const sk_sp<SkSpecialImage>& subset) {
+    static const size_t kCacheSize = 1000000;
+    sk_sp<SkImageFilterCache> cache(SkImageFilterCache::Create(kCacheSize));
+
+    SkIRect clip = SkIRect::MakeWH(100, 100);
+    SkImageFilterCacheKey key1(0, SkMatrix::I(), clip, image->uniqueID(), image->subset());
+    SkImageFilterCacheKey key2(1, SkMatrix::I(), clip, subset->uniqueID(), image->subset());
+
+    SkIPoint offset = SkIPoint::Make(3, 4);
+    cache->set(key1, image.get(), offset, nullptr);
+    cache->set(key2, image.get(), offset, nullptr);
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->count());)
+
+    SkIPoint foundOffset;
+
+    REPORTER_ASSERT(reporter, cache->get(key1, &foundOffset));
+    REPORTER_ASSERT(reporter, cache->get(key2, &foundOffset));
+
+    cache->purgeByKeys(&key1, 1);
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->count());)
+
+    REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset));
+    REPORTER_ASSERT(reporter, cache->get(key2, &foundOffset));
+
+    cache->purge();
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->count());)
+
+    REPORTER_ASSERT(reporter, !cache->get(key1, &foundOffset));
+    REPORTER_ASSERT(reporter, !cache->get(key2, &foundOffset));
+}
+
+DEF_TEST(ImageFilterCache_RasterBacked, reporter) {
+    SkBitmap srcBM = create_bm();
+
+    const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
+
+    sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeFromRaster(full, srcBM));
+
+    const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
+
+    sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeFromRaster(subset, srcBM));
+
+    test_find_existing(reporter, fullImg, subsetImg);
+    test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
+    test_internal_purge(reporter, fullImg);
+    test_explicit_purging(reporter, fullImg, subsetImg);
+}
+
+
+// Shared test code for both the raster and gpu-backed image cases
+static void test_image_backed(skiatest::Reporter* reporter, const sk_sp<SkImage>& srcImage) {
+    const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
+    SkColorSpace* legacyColorSpace = nullptr;
+
+    sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeFromImage(full, srcImage, legacyColorSpace));
+
+    const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
+
+    sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeFromImage(subset, srcImage,
+                                                                  legacyColorSpace));
+
+    test_find_existing(reporter, fullImg, subsetImg);
+    test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
+    test_internal_purge(reporter, fullImg);
+    test_explicit_purging(reporter, fullImg, subsetImg);
+}
+
+DEF_TEST(ImageFilterCache_ImageBackedRaster, reporter) {
+    SkBitmap srcBM = create_bm();
+
+    sk_sp<SkImage> srcImage(SkImage::MakeFromBitmap(srcBM));
+
+    test_image_backed(reporter, srcImage);
+}
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrResourceProvider.h"
+#include "GrSurfaceProxyPriv.h"
+#include "GrTest.h"
+#include "GrTexture.h"
+#include "GrTextureProxy.h"
+
+static sk_sp<GrTextureProxy> create_proxy(GrResourceProvider* resourceProvider) {
+    SkBitmap srcBM = create_bm();
+
+    GrSurfaceDesc desc;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fFlags  = kNone_GrSurfaceFlags;
+    desc.fWidth  = kFullSize;
+    desc.fHeight = kFullSize;
+
+    return GrSurfaceProxy::MakeDeferred(resourceProvider,
+                                        desc, SkBudgeted::kYes,
+                                        srcBM.getPixels(),
+                                        srcBM.rowBytes());
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+
+    sk_sp<GrTextureProxy> srcProxy(create_proxy(context->resourceProvider()));
+    if (!srcProxy) {
+        return;
+    }
+
+    if (!srcProxy->instantiate(context->resourceProvider())) {
+        return;
+    }
+    GrTexture* tex = srcProxy->priv().peekTexture();
+
+    GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                               kFullSize,
+                                                               kFullSize,
+                                                               kRGBA_8888_GrPixelConfig,
+                                                               tex->getTextureHandle());
+    GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin;
+    sk_sp<SkImage> srcImage(SkImage::MakeFromTexture(context,
+                                                     backendTex,
+                                                     texOrigin,
+                                                     kPremul_SkAlphaType, nullptr));
+    if (!srcImage) {
+        return;
+    }
+
+    GrSurfaceOrigin readBackOrigin;
+    GrBackendObject readBackHandle = srcImage->getTextureHandle(false, &readBackOrigin);
+    // TODO: Make it so we can check this (see skbug.com/5019)
+#if 0
+    if (readBackHandle != tex->getTextureHandle()) {
+        ERRORF(reporter, "backend mismatch %d %d\n",
+                       (int)readBackHandle, (int)tex->getTextureHandle());
+    }
+    REPORTER_ASSERT(reporter, readBackHandle == tex->getTextureHandle());
+#else
+    REPORTER_ASSERT(reporter, SkToBool(readBackHandle));
+#endif
+    if (readBackOrigin != texOrigin) {
+        ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, texOrigin);
+    }
+    REPORTER_ASSERT(reporter, readBackOrigin == texOrigin);
+
+    test_image_backed(reporter, srcImage);
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_GPUBacked, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+
+    sk_sp<GrTextureProxy> srcProxy(create_proxy(context->resourceProvider()));
+    if (!srcProxy) {
+        return;
+    }
+
+    const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
+
+    sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeDeferredFromGpu(
+                                                              context, full,
+                                                              kNeedNewImageUniqueID_SpecialImage,
+                                                              srcProxy, nullptr));
+
+    const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
+
+    sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeDeferredFromGpu(
+                                                                context, subset,
+                                                                kNeedNewImageUniqueID_SpecialImage,
+                                                                srcProxy, nullptr));
+
+    test_find_existing(reporter, fullImg, subsetImg);
+    test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
+    test_internal_purge(reporter, fullImg);
+    test_explicit_purging(reporter, fullImg, subsetImg);
+}
+#endif
diff --git a/src/third_party/skia/tests/ImageFilterTest.cpp b/src/third_party/skia/tests/ImageFilterTest.cpp
index d0fa93f..69b31f1 100644
--- a/src/third_party/skia/tests/ImageFilterTest.cpp
+++ b/src/third_party/skia/tests/ImageFilterTest.cpp
@@ -6,35 +6,41 @@
  */
 
 #include "SkBitmap.h"
-#include "SkBitmapDevice.h"
-#include "SkBitmapSource.h"
 #include "SkBlurImageFilter.h"
 #include "SkCanvas.h"
 #include "SkColorFilterImageFilter.h"
 #include "SkColorMatrixFilter.h"
-#include "SkDeviceImageFilterProxy.h"
+#include "SkColorSpaceXformer.h"
+#include "SkComposeImageFilter.h"
 #include "SkDisplacementMapEffect.h"
 #include "SkDropShadowImageFilter.h"
 #include "SkFlattenableSerialization.h"
 #include "SkGradientShader.h"
+#include "SkImage.h"
+#include "SkImageSource.h"
 #include "SkLightingImageFilter.h"
 #include "SkMatrixConvolutionImageFilter.h"
-#include "SkMatrixImageFilter.h"
 #include "SkMergeImageFilter.h"
 #include "SkMorphologyImageFilter.h"
 #include "SkOffsetImageFilter.h"
+#include "SkPaintImageFilter.h"
+#include "SkPerlinNoiseShader.h"
 #include "SkPicture.h"
 #include "SkPictureImageFilter.h"
 #include "SkPictureRecorder.h"
+#include "SkPoint3.h"
 #include "SkReadBuffer.h"
 #include "SkRect.h"
+#include "SkSpecialImage.h"
+#include "SkSpecialSurface.h"
+#include "SkSurface.h"
+#include "SkTableColorFilter.h"
 #include "SkTileImageFilter.h"
 #include "SkXfermodeImageFilter.h"
 #include "Test.h"
 
 #if SK_SUPPORT_GPU
-#include "GrContextFactory.h"
-#include "SkGpuDevice.h"
+#include "GrContext.h"
 #endif
 
 static const int kBitmapSize = 4;
@@ -43,53 +49,262 @@
 
 class MatrixTestImageFilter : public SkImageFilter {
 public:
-    MatrixTestImageFilter(skiatest::Reporter* reporter, const SkMatrix& expectedMatrix)
-      : SkImageFilter(0, NULL), fReporter(reporter), fExpectedMatrix(expectedMatrix) {
+    static sk_sp<SkImageFilter> Make(skiatest::Reporter* reporter,
+                                     const SkMatrix& expectedMatrix) {
+        return sk_sp<SkImageFilter>(new MatrixTestImageFilter(reporter, expectedMatrix));
     }
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context& ctx,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE {
-        REPORTER_ASSERT(fReporter, ctx.ctm() == fExpectedMatrix);
-        return true;
-    }
-
+    SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(MatrixTestImageFilter)
 
 protected:
-#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
-    explicit MatrixTestImageFilter(SkReadBuffer& buffer) : SkImageFilter(0, NULL) {
-        fReporter = static_cast<skiatest::Reporter*>(buffer.readFunctionPtr());
-        buffer.readMatrix(&fExpectedMatrix);
+    sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context& ctx,
+                                        SkIPoint* offset) const override {
+        REPORTER_ASSERT(fReporter, ctx.ctm() == fExpectedMatrix);
+        offset->fX = offset->fY = 0;
+        return sk_ref_sp<SkSpecialImage>(source);
     }
-#endif
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override {
+        return sk_ref_sp(const_cast<MatrixTestImageFilter*>(this));
+    }
 
-    virtual void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE {
-        this->INHERITED::flatten(buffer);
-        buffer.writeFunctionPtr(fReporter);
-        buffer.writeMatrix(fExpectedMatrix);
+    void flatten(SkWriteBuffer& buffer) const override {
+        SkDEBUGFAIL("Should never get here");
     }
 
 private:
+    MatrixTestImageFilter(skiatest::Reporter* reporter, const SkMatrix& expectedMatrix)
+        : INHERITED(nullptr, 0, nullptr)
+        , fReporter(reporter)
+        , fExpectedMatrix(expectedMatrix) {
+    }
+
     skiatest::Reporter* fReporter;
     SkMatrix fExpectedMatrix;
-    
+
     typedef SkImageFilter INHERITED;
 };
 
+class FailImageFilter : public SkImageFilter {
+public:
+    FailImageFilter() : SkImageFilter(nullptr, 0, nullptr) { }
+
+    sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source,
+                                        const Context& ctx,
+                                        SkIPoint* offset) const override {
+        return nullptr;
+    }
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override {
+        return nullptr;
+    }
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(FailImageFilter)
+
+private:
+    typedef SkImageFilter INHERITED;
+};
+
+sk_sp<SkFlattenable> FailImageFilter::CreateProc(SkReadBuffer& buffer) {
+    SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
+    return sk_sp<SkFlattenable>(new FailImageFilter());
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void FailImageFilter::toString(SkString* str) const {
+    str->appendf("FailImageFilter: (");
+    str->append(")");
+}
+#endif
+
+void draw_gradient_circle(SkCanvas* canvas, int width, int height) {
+    SkScalar x = SkIntToScalar(width / 2);
+    SkScalar y = SkIntToScalar(height / 2);
+    SkScalar radius = SkMinScalar(x, y) * 0.8f;
+    canvas->clear(0x00000000);
+    SkColor colors[2];
+    colors[0] = SK_ColorWHITE;
+    colors[1] = SK_ColorBLACK;
+    sk_sp<SkShader> shader(
+        SkGradientShader::MakeRadial(SkPoint::Make(x, y), radius, colors, nullptr, 2,
+                                       SkShader::kClamp_TileMode)
+    );
+    SkPaint paint;
+    paint.setShader(shader);
+    canvas->drawCircle(x, y, radius, paint);
+}
+
+SkBitmap make_gradient_circle(int width, int height) {
+    SkBitmap bitmap;
+    bitmap.allocN32Pixels(width, height);
+    SkCanvas canvas(bitmap);
+    draw_gradient_circle(&canvas, width, height);
+    return bitmap;
+}
+
+class FilterList {
+public:
+    FilterList(sk_sp<SkImageFilter> input, const SkImageFilter::CropRect* cropRect = nullptr) {
+        SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
+        const SkScalar five = SkIntToScalar(5);
+
+        {
+            sk_sp<SkColorFilter> cf(SkColorFilter::MakeModeFilter(SK_ColorRED,
+                                                                  SkBlendMode::kSrcIn));
+
+            this->addFilter("color filter",
+                SkColorFilterImageFilter::Make(std::move(cf), input, cropRect));
+        }
+
+        {
+            sk_sp<SkImage> gradientImage(SkImage::MakeFromBitmap(make_gradient_circle(64, 64)));
+            sk_sp<SkImageFilter> gradientSource(SkImageSource::Make(std::move(gradientImage)));
+
+            this->addFilter("displacement map",
+                SkDisplacementMapEffect::Make(SkDisplacementMapEffect::kR_ChannelSelectorType,
+                                              SkDisplacementMapEffect::kB_ChannelSelectorType,
+                                              20.0f,
+                                              std::move(gradientSource), input, cropRect));
+        }
+
+        this->addFilter("blur", SkBlurImageFilter::Make(SK_Scalar1,
+                                                        SK_Scalar1,
+                                                        input,
+                                                        cropRect));
+        this->addFilter("drop shadow", SkDropShadowImageFilter::Make(
+                  SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN,
+                  SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
+                  input, cropRect));
+        this->addFilter("diffuse lighting",
+                  SkLightingImageFilter::MakePointLitDiffuse(location, SK_ColorGREEN, 0, 0,
+                                                             input, cropRect));
+        this->addFilter("specular lighting",
+                  SkLightingImageFilter::MakePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0,
+                                                              input, cropRect));
+        {
+            SkScalar kernel[9] = {
+                SkIntToScalar(1), SkIntToScalar(1), SkIntToScalar(1),
+                SkIntToScalar(1), SkIntToScalar(-7), SkIntToScalar(1),
+                SkIntToScalar(1), SkIntToScalar(1), SkIntToScalar(1),
+            };
+            const SkISize kernelSize = SkISize::Make(3, 3);
+            const SkScalar gain = SK_Scalar1, bias = 0;
+
+            this->addFilter("matrix convolution",
+                  SkMatrixConvolutionImageFilter::Make(
+                      kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1),
+                      SkMatrixConvolutionImageFilter::kRepeat_TileMode, false,
+                      input, cropRect));
+        }
+
+        this->addFilter("merge", SkMergeImageFilter::Make(input, input, cropRect));
+
+        {
+            SkPaint greenColorShaderPaint;
+            greenColorShaderPaint.setShader(SkShader::MakeColorShader(SK_ColorGREEN));
+
+            SkImageFilter::CropRect leftSideCropRect(SkRect::MakeXYWH(0, 0, 32, 64));
+            sk_sp<SkImageFilter> paintFilterLeft(SkPaintImageFilter::Make(greenColorShaderPaint,
+                                                                          &leftSideCropRect));
+            SkImageFilter::CropRect rightSideCropRect(SkRect::MakeXYWH(32, 0, 32, 64));
+            sk_sp<SkImageFilter> paintFilterRight(SkPaintImageFilter::Make(greenColorShaderPaint,
+                                                                           &rightSideCropRect));
+
+
+            this->addFilter("merge with disjoint inputs", SkMergeImageFilter::Make(
+                  std::move(paintFilterLeft), std::move(paintFilterRight), cropRect));
+        }
+
+        this->addFilter("offset",
+                        SkOffsetImageFilter::Make(SK_Scalar1, SK_Scalar1, input,
+                                                  cropRect));
+        this->addFilter("dilate", SkDilateImageFilter::Make(3, 2, input, cropRect));
+        this->addFilter("erode", SkErodeImageFilter::Make(2, 3, input, cropRect));
+        this->addFilter("tile", SkTileImageFilter::Make(
+                                    SkRect::MakeXYWH(0, 0, 50, 50),
+                                    cropRect ? cropRect->rect() : SkRect::MakeXYWH(0, 0, 100, 100),
+                                    input));
+
+        if (!cropRect) {
+            SkMatrix matrix;
+
+            matrix.setTranslate(SK_Scalar1, SK_Scalar1);
+            matrix.postRotate(SkIntToScalar(45), SK_Scalar1, SK_Scalar1);
+
+            this->addFilter("matrix",
+                SkImageFilter::MakeMatrixFilter(matrix, kLow_SkFilterQuality, input));
+        }
+        {
+            sk_sp<SkImageFilter> blur(SkBlurImageFilter::Make(five, five, input));
+
+            this->addFilter("blur and offset", SkOffsetImageFilter::Make(five, five,
+                                                                         std::move(blur),
+                                                                         cropRect));
+        }
+        {
+            SkPictureRecorder recorder;
+            SkCanvas* recordingCanvas = recorder.beginRecording(64, 64);
+
+            SkPaint greenPaint;
+            greenPaint.setColor(SK_ColorGREEN);
+            recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint);
+            sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
+            sk_sp<SkImageFilter> pictureFilter(SkPictureImageFilter::Make(std::move(picture)));
+
+            this->addFilter("picture and blur", SkBlurImageFilter::Make(five, five,
+                                                                        std::move(pictureFilter),
+                                                                        cropRect));
+        }
+        {
+            SkPaint paint;
+            paint.setShader(SkPerlinNoiseShader::MakeTurbulence(SK_Scalar1, SK_Scalar1, 1, 0));
+            sk_sp<SkImageFilter> paintFilter(SkPaintImageFilter::Make(paint));
+
+            this->addFilter("paint and blur", SkBlurImageFilter::Make(five, five,
+                                                                      std::move(paintFilter),
+                                                                      cropRect));
+        }
+        this->addFilter("xfermode", SkXfermodeImageFilter::Make(SkBlendMode::kSrc, input, input,
+                                                                cropRect));
+    }
+    int count() const { return fFilters.count(); }
+    SkImageFilter* getFilter(int index) const { return fFilters[index].fFilter.get(); }
+    const char* getName(int index) const { return fFilters[index].fName; }
+private:
+    struct Filter {
+        Filter() : fName(nullptr) {}
+        Filter(const char* name, sk_sp<SkImageFilter> filter)
+            : fName(name)
+            , fFilter(std::move(filter)) {
+        }
+        const char*                 fName;
+        sk_sp<SkImageFilter>        fFilter;
+    };
+    void addFilter(const char* name, sk_sp<SkImageFilter> filter) {
+        fFilters.push_back(Filter(name, std::move(filter)));
+    }
+
+    SkTArray<Filter> fFilters;
+};
+
 }
 
-SkFlattenable* MatrixTestImageFilter::CreateProc(SkReadBuffer& buffer) {
-    SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
-    skiatest::Reporter* reporter = (skiatest::Reporter*)buffer.readFunctionPtr();
-    SkMatrix matrix;
-    buffer.readMatrix(&matrix);
-    return SkNEW_ARGS(MatrixTestImageFilter, (reporter, matrix));
+sk_sp<SkFlattenable> MatrixTestImageFilter::CreateProc(SkReadBuffer& buffer) {
+    SkDEBUGFAIL("Should never get here");
+    return nullptr;
 }
 
-static void make_small_bitmap(SkBitmap& bitmap) {
-    bitmap.allocN32Pixels(kBitmapSize, kBitmapSize);
-    SkCanvas canvas(bitmap);
-    canvas.clear(0x00000000);
+#ifndef SK_IGNORE_TO_STRING
+void MatrixTestImageFilter::toString(SkString* str) const {
+    str->appendf("MatrixTestImageFilter: (");
+    str->append(")");
+}
+#endif
+
+static sk_sp<SkImage> make_small_image() {
+    auto surface(SkSurface::MakeRasterN32Premul(kBitmapSize, kBitmapSize));
+    SkCanvas* canvas = surface->getCanvas();
+    canvas->clear(0x00000000);
     SkPaint darkPaint;
     darkPaint.setColor(0xFF804020);
     SkPaint lightPaint;
@@ -97,77 +312,158 @@
     const int i = kBitmapSize / 4;
     for (int y = 0; y < kBitmapSize; y += i) {
         for (int x = 0; x < kBitmapSize; x += i) {
-            canvas.save();
-            canvas.translate(SkIntToScalar(x), SkIntToScalar(y));
-            canvas.drawRect(SkRect::MakeXYWH(0, 0,
+            canvas->save();
+            canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
+            canvas->drawRect(SkRect::MakeXYWH(0, 0,
                                              SkIntToScalar(i),
                                              SkIntToScalar(i)), darkPaint);
-            canvas.drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
+            canvas->drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
                                              0,
                                              SkIntToScalar(i),
                                              SkIntToScalar(i)), lightPaint);
-            canvas.drawRect(SkRect::MakeXYWH(0,
+            canvas->drawRect(SkRect::MakeXYWH(0,
                                              SkIntToScalar(i),
                                              SkIntToScalar(i),
                                              SkIntToScalar(i)), lightPaint);
-            canvas.drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
+            canvas->drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
                                              SkIntToScalar(i),
                                              SkIntToScalar(i),
                                              SkIntToScalar(i)), darkPaint);
-            canvas.restore();
+            canvas->restore();
         }
     }
+
+    return surface->makeImageSnapshot();
 }
 
-static SkImageFilter* make_scale(float amount, SkImageFilter* input = NULL) {
+static sk_sp<SkImageFilter> make_scale(float amount, sk_sp<SkImageFilter> input) {
     SkScalar s = amount;
     SkScalar matrix[20] = { s, 0, 0, 0, 0,
                             0, s, 0, 0, 0,
                             0, 0, s, 0, 0,
                             0, 0, 0, s, 0 };
-    SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix));
-    return SkColorFilterImageFilter::Create(filter, input);
+    sk_sp<SkColorFilter> filter(SkColorFilter::MakeMatrixFilterRowMajor255(matrix));
+    return SkColorFilterImageFilter::Make(std::move(filter), std::move(input));
 }
 
-static SkImageFilter* make_grayscale(SkImageFilter* input = NULL, const SkImageFilter::CropRect* cropRect = NULL) {
+static sk_sp<SkImageFilter> make_grayscale(sk_sp<SkImageFilter> input,
+                                           const SkImageFilter::CropRect* cropRect) {
     SkScalar matrix[20];
     memset(matrix, 0, 20 * sizeof(SkScalar));
     matrix[0] = matrix[5] = matrix[10] = 0.2126f;
     matrix[1] = matrix[6] = matrix[11] = 0.7152f;
     matrix[2] = matrix[7] = matrix[12] = 0.0722f;
     matrix[18] = 1.0f;
-    SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix));
-    return SkColorFilterImageFilter::Create(filter, input, cropRect);
+    sk_sp<SkColorFilter> filter(SkColorFilter::MakeMatrixFilterRowMajor255(matrix));
+    return SkColorFilterImageFilter::Make(std::move(filter), std::move(input), cropRect);
 }
 
+static sk_sp<SkImageFilter> make_blue(sk_sp<SkImageFilter> input,
+                                      const SkImageFilter::CropRect* cropRect) {
+    sk_sp<SkColorFilter> filter(SkColorFilter::MakeModeFilter(SK_ColorBLUE,
+                                                              SkBlendMode::kSrcIn));
+    return SkColorFilterImageFilter::Make(std::move(filter), std::move(input), cropRect);
+}
+
+static sk_sp<SkSpecialSurface> create_empty_special_surface(GrContext* context, int widthHeight) {
+#if SK_SUPPORT_GPU
+    if (context) {
+        return SkSpecialSurface::MakeRenderTarget(context,
+                                                  widthHeight, widthHeight,
+                                                  kRGBA_8888_GrPixelConfig, nullptr);
+    } else
+#endif
+    {
+        const SkImageInfo info = SkImageInfo::MakeN32(widthHeight, widthHeight,
+                                                      kOpaque_SkAlphaType);
+        return SkSpecialSurface::MakeRaster(info);
+    }
+}
+
+static sk_sp<SkSurface> create_surface(GrContext* context, int width, int height) {
+    const SkImageInfo info = SkImageInfo::MakeN32(width, height, kOpaque_SkAlphaType);
+#if SK_SUPPORT_GPU
+    if (context) {
+        return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
+    } else
+#endif
+    {
+        return SkSurface::MakeRaster(info);
+    }
+}
+
+static sk_sp<SkSpecialImage> create_empty_special_image(GrContext* context, int widthHeight) {
+    sk_sp<SkSpecialSurface> surf(create_empty_special_surface(context, widthHeight));
+
+    SkASSERT(surf);
+
+    SkCanvas* canvas = surf->getCanvas();
+    SkASSERT(canvas);
+
+    canvas->clear(0x0);
+
+    return surf->makeImageSnapshot();
+}
+
+
 DEF_TEST(ImageFilter, reporter) {
     {
-        // Check that two non-clipping color matrices concatenate into a single filter.
-        SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f));
-        SkAutoTUnref<SkImageFilter> quarterBrightness(make_scale(0.5f, halfBrightness));
-        REPORTER_ASSERT(reporter, NULL == quarterBrightness->getInput(0));
+        // Check that two non-clipping color-matrice-filters concatenate into a single filter.
+        sk_sp<SkImageFilter> halfBrightness(make_scale(0.5f, nullptr));
+        sk_sp<SkImageFilter> quarterBrightness(make_scale(0.5f, std::move(halfBrightness)));
+        REPORTER_ASSERT(reporter, nullptr == quarterBrightness->getInput(0));
+        SkColorFilter* cf;
+        REPORTER_ASSERT(reporter, quarterBrightness->asColorFilter(&cf));
+        REPORTER_ASSERT(reporter, cf->asColorMatrix(nullptr));
+        cf->unref();
     }
 
     {
-        // Check that a clipping color matrix followed by a grayscale does not concatenate into a single filter.
-        SkAutoTUnref<SkImageFilter> doubleBrightness(make_scale(2.0f));
-        SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f, doubleBrightness));
-        REPORTER_ASSERT(reporter, halfBrightness->getInput(0));
+        // Check that a clipping color-matrice-filter followed by a color-matrice-filters
+        // concatenates into a single filter, but not a matrixfilter (due to clamping).
+        sk_sp<SkImageFilter> doubleBrightness(make_scale(2.0f, nullptr));
+        sk_sp<SkImageFilter> halfBrightness(make_scale(0.5f, std::move(doubleBrightness)));
+        REPORTER_ASSERT(reporter, nullptr == halfBrightness->getInput(0));
+        SkColorFilter* cf;
+        REPORTER_ASSERT(reporter, halfBrightness->asColorFilter(&cf));
+        REPORTER_ASSERT(reporter, !cf->asColorMatrix(nullptr));
+        cf->unref();
     }
 
     {
         // Check that a color filter image filter without a crop rect can be
         // expressed as a color filter.
-        SkAutoTUnref<SkImageFilter> gray(make_grayscale());
-        REPORTER_ASSERT(reporter, true == gray->asColorFilter(NULL));
+        sk_sp<SkImageFilter> gray(make_grayscale(nullptr, nullptr));
+        REPORTER_ASSERT(reporter, true == gray->asColorFilter(nullptr));
+    }
+
+    {
+        // Check that a colorfilterimage filter without a crop rect but with an input
+        // that is another colorfilterimage can be expressed as a colorfilter (composed).
+        sk_sp<SkImageFilter> mode(make_blue(nullptr, nullptr));
+        sk_sp<SkImageFilter> gray(make_grayscale(std::move(mode), nullptr));
+        REPORTER_ASSERT(reporter, true == gray->asColorFilter(nullptr));
+    }
+
+    {
+        // Test that if we exceed the limit of what ComposeColorFilter can combine, we still
+        // can build the DAG and won't assert if we call asColorFilter.
+        sk_sp<SkImageFilter> filter(make_blue(nullptr, nullptr));
+        const int kWayTooManyForComposeColorFilter = 100;
+        for (int i = 0; i < kWayTooManyForComposeColorFilter; ++i) {
+            filter = make_blue(filter, nullptr);
+            // the first few of these will succeed, but after we hit the internal limit,
+            // it will then return false.
+            (void)filter->asColorFilter(nullptr);
+        }
     }
 
     {
         // Check that a color filter image filter with a crop rect cannot
         // be expressed as a color filter.
         SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(0, 0, 100, 100));
-        SkAutoTUnref<SkImageFilter> grayWithCrop(make_grayscale(NULL, &cropRect));
-        REPORTER_ASSERT(reporter, false == grayWithCrop->asColorFilter(NULL));
+        sk_sp<SkImageFilter> grayWithCrop(make_grayscale(nullptr, &cropRect));
+        REPORTER_ASSERT(reporter, false == grayWithCrop->asColorFilter(nullptr));
     }
 
     {
@@ -177,17 +473,19 @@
         blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
         SkScalar redToGreenMatrix[20] = { 0 };
         redToGreenMatrix[5] = redToGreenMatrix[18] = SK_Scalar1;
-        SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
-        SkAutoTUnref<SkImageFilter> filter1(SkColorFilterImageFilter::Create(blueToRed.get()));
-        SkAutoTUnref<SkColorFilter> redToGreen(SkColorMatrixFilter::Create(redToGreenMatrix));
-        SkAutoTUnref<SkImageFilter> filter2(SkColorFilterImageFilter::Create(redToGreen.get(), filter1.get()));
+        sk_sp<SkColorFilter> blueToRed(SkColorFilter::MakeMatrixFilterRowMajor255(blueToRedMatrix));
+        sk_sp<SkImageFilter> filter1(SkColorFilterImageFilter::Make(std::move(blueToRed),
+                                                                    nullptr));
+        sk_sp<SkColorFilter> redToGreen(SkColorFilter::MakeMatrixFilterRowMajor255(redToGreenMatrix));
+        sk_sp<SkImageFilter> filter2(SkColorFilterImageFilter::Make(std::move(redToGreen),
+                                                                    std::move(filter1)));
 
         SkBitmap result;
         result.allocN32Pixels(kBitmapSize, kBitmapSize);
 
         SkPaint paint;
         paint.setColor(SK_ColorBLUE);
-        paint.setImageFilter(filter2.get());
+        paint.setImageFilter(std::move(filter2));
         SkCanvas canvas(result);
         canvas.clear(0x0);
         SkRect rect = SkRect::Make(SkIRect::MakeWH(kBitmapSize, kBitmapSize));
@@ -199,25 +497,25 @@
 
     {
         // Tests pass by not asserting
-        SkBitmap bitmap, result;
-        make_small_bitmap(bitmap);
+        sk_sp<SkImage> image(make_small_image());
+        SkBitmap result;
         result.allocN32Pixels(kBitmapSize, kBitmapSize);
 
         {
             // This tests for :
             // 1 ) location at (0,0,1)
-            SkPoint3 location(0, 0, SK_Scalar1);
+            SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
             // 2 ) location and target at same value
-            SkPoint3 target(location.fX, location.fY, location.fZ);
+            SkPoint3 target = SkPoint3::Make(location.fX, location.fY, location.fZ);
             // 3 ) large negative specular exponent value
             SkScalar specularExponent = -1000;
 
-            SkAutoTUnref<SkImageFilter> bmSrc(SkBitmapSource::Create(bitmap));
+            sk_sp<SkImageFilter> bmSrc(SkImageSource::Make(std::move(image)));
             SkPaint paint;
-            paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular(
+            paint.setImageFilter(SkLightingImageFilter::MakeSpotLitSpecular(
                     location, target, specularExponent, 180,
                     0xFFFFFFFF, SK_Scalar1, SK_Scalar1, SK_Scalar1,
-                    bmSrc))->unref();
+                    std::move(bmSrc)));
             SkCanvas canvas(result);
             SkRect r = SkRect::MakeWH(SkIntToScalar(kBitmapSize),
                                       SkIntToScalar(kBitmapSize));
@@ -226,130 +524,97 @@
     }
 }
 
-static void test_crop_rects(SkBaseDevice* device, skiatest::Reporter* reporter) {
+static void test_crop_rects(skiatest::Reporter* reporter,
+                            GrContext* context) {
     // Check that all filters offset to their absolute crop rect,
     // unaffected by the input crop rect.
     // Tests pass by not asserting.
-    SkBitmap bitmap;
-    bitmap.allocN32Pixels(100, 100);
-    bitmap.eraseARGB(0, 0, 0, 0);
-    SkDeviceImageFilterProxy proxy(device);
+    sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 100));
+    SkASSERT(srcImg);
 
     SkImageFilter::CropRect inputCropRect(SkRect::MakeXYWH(8, 13, 80, 80));
     SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(20, 30, 60, 60));
-    SkAutoTUnref<SkImageFilter> input(make_grayscale(NULL, &inputCropRect));
+    sk_sp<SkImageFilter> input(make_grayscale(nullptr, &inputCropRect));
 
-    SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
-    SkPoint3 location(0, 0, SK_Scalar1);
-    SkPoint3 target(SK_Scalar1, SK_Scalar1, SK_Scalar1);
-    SkScalar kernel[9] = {
-        SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
-        SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
-        SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
-    };
-    SkISize kernelSize = SkISize::Make(3, 3);
-    SkScalar gain = SK_Scalar1, bias = 0;
+    FilterList filters(input, &cropRect);
 
-    SkImageFilter* filters[] = {
-        SkColorFilterImageFilter::Create(cf.get(), input.get(), &cropRect),
-        SkDisplacementMapEffect::Create(SkDisplacementMapEffect::kR_ChannelSelectorType,
-                                        SkDisplacementMapEffect::kB_ChannelSelectorType,
-                                        40.0f, input.get(), input.get(), &cropRect),
-        SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
-        SkDropShadowImageFilter::Create(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN, input.get(), &cropRect),
-        SkLightingImageFilter::CreatePointLitDiffuse(location, SK_ColorGREEN, 0, 0, input.get(), &cropRect),
-        SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0, input.get(), &cropRect),
-        SkMatrixConvolutionImageFilter::Create(kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1), SkMatrixConvolutionImageFilter::kRepeat_TileMode, false, input.get(), &cropRect),
-        SkMergeImageFilter::Create(input.get(), input.get(), SkXfermode::kSrcOver_Mode, &cropRect),
-        SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
-        SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
-        SkDilateImageFilter::Create(3, 2, input.get(), &cropRect),
-        SkErodeImageFilter::Create(2, 3, input.get(), &cropRect),
-        SkTileImageFilter::Create(inputCropRect.rect(), cropRect.rect(), input.get()),
-        SkXfermodeImageFilter::Create(SkXfermode::Create(SkXfermode::kSrcOver_Mode), input.get(), input.get(), &cropRect),
-    };
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
-        SkImageFilter* filter = filters[i];
-        SkBitmap result;
+    for (int i = 0; i < filters.count(); ++i) {
+        SkImageFilter* filter = filters.getFilter(i);
         SkIPoint offset;
-        SkString str;
-        str.printf("filter %d", static_cast<int>(i));
-        SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), NULL);
-        REPORTER_ASSERT_MESSAGE(reporter, filter->filterImage(&proxy, bitmap, ctx,
-                                &result, &offset), str.c_str());
-        REPORTER_ASSERT_MESSAGE(reporter, offset.fX == 20 && offset.fY == 30, str.c_str());
-    }
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
-        SkSafeUnref(filters[i]);
+        SkImageFilter::OutputProperties noColorSpace(nullptr);
+        SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace);
+        sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset));
+        REPORTER_ASSERT_MESSAGE(reporter, resultImg, filters.getName(i));
+        REPORTER_ASSERT_MESSAGE(reporter, offset.fX == 20 && offset.fY == 30, filters.getName(i));
     }
 }
 
-static SkBitmap make_gradient_circle(int width, int height) {
-    SkBitmap bitmap;
-    SkScalar x = SkIntToScalar(width / 2);
-    SkScalar y = SkIntToScalar(height / 2);
-    SkScalar radius = SkMinScalar(x, y) * 0.8f;
-    bitmap.allocN32Pixels(width, height);
-    SkCanvas canvas(bitmap);
-    canvas.clear(0x00000000);
-    SkColor colors[2];
-    colors[0] = SK_ColorWHITE;
-    colors[1] = SK_ColorBLACK;
-    SkAutoTUnref<SkShader> shader(
-        SkGradientShader::CreateRadial(SkPoint::Make(x, y), radius, colors, NULL, 2,
-                                       SkShader::kClamp_TileMode)
-    );
-    SkPaint paint;
-    paint.setShader(shader);
-    canvas.drawCircle(x, y, radius, paint);
-    return bitmap;
-}
-
-static void test_negative_blur_sigma(SkBaseDevice* device, skiatest::Reporter* reporter) {
+static void test_negative_blur_sigma(skiatest::Reporter* reporter,
+                                     GrContext* context) {
     // Check that SkBlurImageFilter will accept a negative sigma, either in
     // the given arguments or after CTM application.
-    int width = 32, height = 32;
-    SkDeviceImageFilterProxy proxy(device);
-    SkScalar five = SkIntToScalar(5);
+    const int width = 32, height = 32;
+    const SkScalar five = SkIntToScalar(5);
 
-    SkAutoTUnref<SkBlurImageFilter> positiveFilter(
-        SkBlurImageFilter::Create(five, five)
-    );
-
-    SkAutoTUnref<SkBlurImageFilter> negativeFilter(
-        SkBlurImageFilter::Create(-five, five)
-    );
+    sk_sp<SkImageFilter> positiveFilter(SkBlurImageFilter::Make(five, five, nullptr));
+    sk_sp<SkImageFilter> negativeFilter(SkBlurImageFilter::Make(-five, five, nullptr));
 
     SkBitmap gradient = make_gradient_circle(width, height);
-    SkBitmap positiveResult1, negativeResult1;
-    SkBitmap positiveResult2, negativeResult2;
+    sk_sp<SkSpecialImage> imgSrc(SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(width, height),
+                                                                gradient));
+
     SkIPoint offset;
-    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), NULL);
-    positiveFilter->filterImage(&proxy, gradient, ctx, &positiveResult1, &offset);
-    negativeFilter->filterImage(&proxy, gradient, ctx, &negativeResult1, &offset);
+    SkImageFilter::OutputProperties noColorSpace(nullptr);
+    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(32, 32), nullptr, noColorSpace);
+
+    sk_sp<SkSpecialImage> positiveResult1(positiveFilter->filterImage(imgSrc.get(), ctx, &offset));
+    REPORTER_ASSERT(reporter, positiveResult1);
+
+    sk_sp<SkSpecialImage> negativeResult1(negativeFilter->filterImage(imgSrc.get(), ctx, &offset));
+    REPORTER_ASSERT(reporter, negativeResult1);
+
     SkMatrix negativeScale;
     negativeScale.setScale(-SK_Scalar1, SK_Scalar1);
-    SkImageFilter::Context negativeCTX(negativeScale, SkIRect::MakeLargest(), NULL);
-    positiveFilter->filterImage(&proxy, gradient, negativeCTX, &negativeResult2, &offset);
-    negativeFilter->filterImage(&proxy, gradient, negativeCTX, &positiveResult2, &offset);
-    SkAutoLockPixels lockP1(positiveResult1);
-    SkAutoLockPixels lockP2(positiveResult2);
-    SkAutoLockPixels lockN1(negativeResult1);
-    SkAutoLockPixels lockN2(negativeResult2);
+    SkImageFilter::Context negativeCTX(negativeScale, SkIRect::MakeWH(32, 32), nullptr,
+                                       noColorSpace);
+
+    sk_sp<SkSpecialImage> negativeResult2(positiveFilter->filterImage(imgSrc.get(),
+                                                                      negativeCTX,
+                                                                      &offset));
+    REPORTER_ASSERT(reporter, negativeResult2);
+
+    sk_sp<SkSpecialImage> positiveResult2(negativeFilter->filterImage(imgSrc.get(),
+                                                                      negativeCTX,
+                                                                      &offset));
+    REPORTER_ASSERT(reporter, positiveResult2);
+
+
+    SkBitmap positiveResultBM1, positiveResultBM2;
+    SkBitmap negativeResultBM1, negativeResultBM2;
+
+    REPORTER_ASSERT(reporter, positiveResult1->getROPixels(&positiveResultBM1));
+    REPORTER_ASSERT(reporter, positiveResult2->getROPixels(&positiveResultBM2));
+    REPORTER_ASSERT(reporter, negativeResult1->getROPixels(&negativeResultBM1));
+    REPORTER_ASSERT(reporter, negativeResult2->getROPixels(&negativeResultBM2));
+
     for (int y = 0; y < height; y++) {
-        int diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult1.getAddr32(0, y), positiveResult1.rowBytes());
+        int diffs = memcmp(positiveResultBM1.getAddr32(0, y),
+                           negativeResultBM1.getAddr32(0, y),
+                           positiveResultBM1.rowBytes());
         REPORTER_ASSERT(reporter, !diffs);
         if (diffs) {
             break;
         }
-        diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult2.getAddr32(0, y), positiveResult1.rowBytes());
+        diffs = memcmp(positiveResultBM1.getAddr32(0, y),
+                       negativeResultBM2.getAddr32(0, y),
+                       positiveResultBM1.rowBytes());
         REPORTER_ASSERT(reporter, !diffs);
         if (diffs) {
             break;
         }
-        diffs = memcmp(positiveResult1.getAddr32(0, y), positiveResult2.getAddr32(0, y), positiveResult1.rowBytes());
+        diffs = memcmp(positiveResultBM1.getAddr32(0, y),
+                       positiveResultBM2.getAddr32(0, y),
+                       positiveResultBM1.rowBytes());
         REPORTER_ASSERT(reporter, !diffs);
         if (diffs) {
             break;
@@ -357,80 +622,101 @@
     }
 }
 
-DEF_TEST(TestNegativeBlurSigma, reporter) {
-    SkBitmap temp;
-    temp.allocN32Pixels(100, 100);
-    SkBitmapDevice device(temp);
-    test_negative_blur_sigma(&device, reporter);
+DEF_TEST(ImageFilterNegativeBlurSigma, reporter) {
+    test_negative_blur_sigma(reporter, nullptr);
 }
 
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterNegativeBlurSigma_Gpu, reporter, ctxInfo) {
+    test_negative_blur_sigma(reporter, ctxInfo.grContext());
+}
+#endif
+
+static void test_zero_blur_sigma(skiatest::Reporter* reporter, GrContext* context) {
+    // Check that SkBlurImageFilter with a zero sigma and a non-zero srcOffset works correctly.
+    SkImageFilter::CropRect cropRect(SkRect::Make(SkIRect::MakeXYWH(5, 0, 5, 10)));
+    sk_sp<SkImageFilter> input(SkOffsetImageFilter::Make(0, 0, nullptr, &cropRect));
+    sk_sp<SkImageFilter> filter(SkBlurImageFilter::Make(0, 0, std::move(input), &cropRect));
+
+    sk_sp<SkSpecialSurface> surf(create_empty_special_surface(context, 10));
+    surf->getCanvas()->clear(SK_ColorGREEN);
+    sk_sp<SkSpecialImage> image(surf->makeImageSnapshot());
+
+    SkIPoint offset;
+    SkImageFilter::OutputProperties noColorSpace(nullptr);
+    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(32, 32), nullptr, noColorSpace);
+
+    sk_sp<SkSpecialImage> result(filter->filterImage(image.get(), ctx, &offset));
+    REPORTER_ASSERT(reporter, offset.fX == 5 && offset.fY == 0);
+    REPORTER_ASSERT(reporter, result);
+    REPORTER_ASSERT(reporter, result->width() == 5 && result->height() == 10);
+
+    SkBitmap resultBM;
+
+    REPORTER_ASSERT(reporter, result->getROPixels(&resultBM));
+
+    for (int y = 0; y < resultBM.height(); y++) {
+        for (int x = 0; x < resultBM.width(); x++) {
+            bool diff = *resultBM.getAddr32(x, y) != SK_ColorGREEN;
+            REPORTER_ASSERT(reporter, !diff);
+            if (diff) {
+                break;
+            }
+        }
+    }
+}
+
+DEF_TEST(ImageFilterZeroBlurSigma, reporter) {
+    test_zero_blur_sigma(reporter, nullptr);
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterZeroBlurSigma_Gpu, reporter, ctxInfo) {
+    test_zero_blur_sigma(reporter, ctxInfo.grContext());
+}
+#endif
+
+
+// Tests that, even when an upstream filter has returned null (due to failure or clipping), a
+// downstream filter that affects transparent black still does so even with a nullptr input.
+static void test_fail_affects_transparent_black(skiatest::Reporter* reporter, GrContext* context) {
+    sk_sp<FailImageFilter> failFilter(new FailImageFilter());
+    sk_sp<SkSpecialImage> source(create_empty_special_image(context, 5));
+    SkImageFilter::OutputProperties noColorSpace(nullptr);
+    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(0, 0, 1, 1), nullptr, noColorSpace);
+    sk_sp<SkColorFilter> green(SkColorFilter::MakeModeFilter(SK_ColorGREEN, SkBlendMode::kSrc));
+    SkASSERT(green->affectsTransparentBlack());
+    sk_sp<SkImageFilter> greenFilter(SkColorFilterImageFilter::Make(std::move(green),
+                                                                    std::move(failFilter)));
+    SkIPoint offset;
+    sk_sp<SkSpecialImage> result(greenFilter->filterImage(source.get(), ctx, &offset));
+    REPORTER_ASSERT(reporter, nullptr != result.get());
+    if (result.get()) {
+        SkBitmap resultBM;
+        REPORTER_ASSERT(reporter, result->getROPixels(&resultBM));
+        REPORTER_ASSERT(reporter, *resultBM.getAddr32(0, 0) == SK_ColorGREEN);
+    }
+}
+
+DEF_TEST(ImageFilterFailAffectsTransparentBlack, reporter) {
+    test_fail_affects_transparent_black(reporter, nullptr);
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterFailAffectsTransparentBlack_Gpu, reporter, ctxInfo) {
+    test_fail_affects_transparent_black(reporter, ctxInfo.grContext());
+}
+#endif
+
 DEF_TEST(ImageFilterDrawTiled, reporter) {
     // Check that all filters when drawn tiled (with subsequent clip rects) exactly
     // match the same filters drawn with a single full-canvas bitmap draw.
     // Tests pass by not asserting.
 
-    SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
-    SkPoint3 location(0, 0, SK_Scalar1);
-    SkPoint3 target(SK_Scalar1, SK_Scalar1, SK_Scalar1);
-    SkScalar kernel[9] = {
-        SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
-        SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
-        SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
-    };
-    SkISize kernelSize = SkISize::Make(3, 3);
-    SkScalar gain = SK_Scalar1, bias = 0;
-    SkScalar five = SkIntToScalar(5);
-
-    SkAutoTUnref<SkImageFilter> gradient_source(SkBitmapSource::Create(make_gradient_circle(64, 64)));
-    SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(five, five));
-    SkMatrix matrix;
-
-    matrix.setTranslate(SK_Scalar1, SK_Scalar1);
-    matrix.postRotate(SkIntToScalar(45), SK_Scalar1, SK_Scalar1);
-
-    SkRTreeFactory factory;
-    SkPictureRecorder recorder;
-    SkCanvas* recordingCanvas = recorder.beginRecording(64, 64, &factory, 0);
-
-    SkPaint greenPaint;
-    greenPaint.setColor(SK_ColorGREEN);
-    recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint);
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
-    SkAutoTUnref<SkImageFilter> pictureFilter(SkPictureImageFilter::Create(picture.get()));
-
-    struct {
-        const char*    fName;
-        SkImageFilter* fFilter;
-    } filters[] = {
-        { "color filter", SkColorFilterImageFilter::Create(cf.get()) },
-        { "displacement map", SkDisplacementMapEffect::Create(
-              SkDisplacementMapEffect::kR_ChannelSelectorType,
-              SkDisplacementMapEffect::kB_ChannelSelectorType,
-              20.0f, gradient_source.get()) },
-        { "blur", SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1) },
-        { "drop shadow", SkDropShadowImageFilter::Create(
-              SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN) },
-        { "diffuse lighting", SkLightingImageFilter::CreatePointLitDiffuse(
-              location, SK_ColorGREEN, 0, 0) },
-        { "specular lighting",
-              SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0) },
-        { "matrix convolution",
-              SkMatrixConvolutionImageFilter::Create(
-                  kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1),
-                  SkMatrixConvolutionImageFilter::kRepeat_TileMode, false) },
-        { "merge", SkMergeImageFilter::Create(NULL, NULL, SkXfermode::kSrcOver_Mode) },
-        { "offset", SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1) },
-        { "dilate", SkDilateImageFilter::Create(3, 2) },
-        { "erode", SkErodeImageFilter::Create(2, 3) },
-        { "tile", SkTileImageFilter::Create(SkRect::MakeXYWH(0, 0, 50, 50),
-                                            SkRect::MakeXYWH(0, 0, 100, 100), NULL) },
-        { "matrix", SkMatrixImageFilter::Create(matrix, SkPaint::kLow_FilterLevel) },
-        { "blur and offset", SkOffsetImageFilter::Create(five, five, blur.get()) },
-        { "picture and blur", SkBlurImageFilter::Create(five, five, pictureFilter.get()) },
-    };
+    FilterList filters(nullptr);
 
     SkBitmap untiledResult, tiledResult;
-    int width = 64, height = 64;
+    const int width = 64, height = 64;
     untiledResult.allocN32Pixels(width, height);
     tiledResult.allocN32Pixels(width, height);
     SkCanvas tiledCanvas(tiledResult);
@@ -438,11 +724,11 @@
     int tileSize = 8;
 
     for (int scale = 1; scale <= 2; ++scale) {
-        for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
+        for (int i = 0; i < filters.count(); ++i) {
             tiledCanvas.clear(0);
             untiledCanvas.clear(0);
             SkPaint paint;
-            paint.setImageFilter(filters[i].fFilter);
+            paint.setImageFilter(sk_ref_sp(filters.getFilter(i)));
             paint.setTextSize(SkIntToScalar(height));
             paint.setColor(SK_ColorWHITE);
             SkString str;
@@ -450,14 +736,14 @@
             SkScalar ypos = SkIntToScalar(height);
             untiledCanvas.save();
             untiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
-            untiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
+            untiledCanvas.drawString(text, 0, ypos, paint);
             untiledCanvas.restore();
             for (int y = 0; y < height; y += tileSize) {
                 for (int x = 0; x < width; x += tileSize) {
                     tiledCanvas.save();
                     tiledCanvas.clipRect(SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize)));
                     tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
-                    tiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
+                    tiledCanvas.drawString(text, 0, ypos, paint);
                     tiledCanvas.restore();
                 }
             }
@@ -465,40 +751,38 @@
             tiledCanvas.flush();
             for (int y = 0; y < height; y++) {
                 int diffs = memcmp(untiledResult.getAddr32(0, y), tiledResult.getAddr32(0, y), untiledResult.rowBytes());
-                REPORTER_ASSERT_MESSAGE(reporter, !diffs, filters[i].fName);
+                REPORTER_ASSERT_MESSAGE(reporter, !diffs, filters.getName(i));
                 if (diffs) {
                     break;
                 }
             }
         }
     }
-
-    for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
-        SkSafeUnref(filters[i].fFilter);
-    }
 }
 
-static void draw_saveLayer_picture(int width, int height, int tileSize, 
+static void draw_saveLayer_picture(int width, int height, int tileSize,
                                    SkBBHFactory* factory, SkBitmap* result) {
 
     SkMatrix matrix;
     matrix.setTranslate(SkIntToScalar(50), 0);
 
-    SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorWHITE, SkXfermode::kSrc_Mode));
-    SkAutoTUnref<SkImageFilter> cfif(SkColorFilterImageFilter::Create(cf.get()));
-    SkAutoTUnref<SkImageFilter> imageFilter(SkMatrixImageFilter::Create(matrix, SkPaint::kNone_FilterLevel, cfif.get()));
+    sk_sp<SkColorFilter> cf(SkColorFilter::MakeModeFilter(SK_ColorWHITE, SkBlendMode::kSrc));
+    sk_sp<SkImageFilter> cfif(SkColorFilterImageFilter::Make(std::move(cf), nullptr));
+    sk_sp<SkImageFilter> imageFilter(SkImageFilter::MakeMatrixFilter(matrix,
+                                                                     kNone_SkFilterQuality,
+                                                                     std::move(cfif)));
 
     SkPaint paint;
-    paint.setImageFilter(imageFilter.get());
+    paint.setImageFilter(std::move(imageFilter));
     SkPictureRecorder recorder;
     SkRect bounds = SkRect::Make(SkIRect::MakeXYWH(0, 0, 50, 50));
-    SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width), 
-                                                        SkIntToScalar(height), 
+    SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
+                                                        SkIntToScalar(height),
                                                         factory, 0);
     recordingCanvas->translate(-55, 0);
     recordingCanvas->saveLayer(&bounds, &paint);
     recordingCanvas->restore();
-    SkAutoTUnref<SkPicture> picture1(recorder.endRecording());
+    sk_sp<SkPicture> picture1(recorder.finishRecordingAsPicture());
 
     result->allocN32Pixels(width, height);
     SkCanvas canvas(*result);
@@ -518,7 +802,7 @@
     SkRTreeFactory factory;
 
     draw_saveLayer_picture(width, height, tileSize, &factory, &result1);
-    draw_saveLayer_picture(width, height, tileSize, NULL, &result2);
+    draw_saveLayer_picture(width, height, tileSize, nullptr, &result2);
 
     for (int y = 0; y < height; y++) {
         int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
@@ -529,56 +813,180 @@
     }
 }
 
-static SkImageFilter* makeBlur(SkImageFilter* input = NULL) {
-    return SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input);
+static sk_sp<SkImageFilter> make_blur(sk_sp<SkImageFilter> input) {
+    return SkBlurImageFilter::Make(SK_Scalar1, SK_Scalar1, std::move(input));
 }
 
-static SkImageFilter* makeDropShadow(SkImageFilter* input = NULL) {
-    return SkDropShadowImageFilter::Create(
+static sk_sp<SkImageFilter> make_drop_shadow(sk_sp<SkImageFilter> input) {
+    return SkDropShadowImageFilter::Make(
         SkIntToScalar(100), SkIntToScalar(100),
         SkIntToScalar(10), SkIntToScalar(10),
-        SK_ColorBLUE, input);
+        SK_ColorBLUE, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
+        std::move(input));
 }
 
 DEF_TEST(ImageFilterBlurThenShadowBounds, reporter) {
-    SkAutoTUnref<SkImageFilter> filter1(makeBlur());
-    SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get()));
+    sk_sp<SkImageFilter> filter1(make_blur(nullptr));
+    sk_sp<SkImageFilter> filter2(make_drop_shadow(std::move(filter1)));
 
     SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
     SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
-    filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
+    bounds = filter2->filterBounds(bounds, SkMatrix::I());
 
     REPORTER_ASSERT(reporter, bounds == expectedBounds);
 }
 
 DEF_TEST(ImageFilterShadowThenBlurBounds, reporter) {
-    SkAutoTUnref<SkImageFilter> filter1(makeDropShadow());
-    SkAutoTUnref<SkImageFilter> filter2(makeBlur(filter1.get()));
+    sk_sp<SkImageFilter> filter1(make_drop_shadow(nullptr));
+    sk_sp<SkImageFilter> filter2(make_blur(std::move(filter1)));
 
     SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
     SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
-    filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
+    bounds = filter2->filterBounds(bounds, SkMatrix::I());
 
     REPORTER_ASSERT(reporter, bounds == expectedBounds);
 }
 
 DEF_TEST(ImageFilterDilateThenBlurBounds, reporter) {
-    SkAutoTUnref<SkImageFilter> filter1(SkDilateImageFilter::Create(2, 2));
-    SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get()));
+    sk_sp<SkImageFilter> filter1(SkDilateImageFilter::Make(2, 2, nullptr));
+    sk_sp<SkImageFilter> filter2(make_drop_shadow(std::move(filter1)));
 
     SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
     SkIRect expectedBounds = SkIRect::MakeXYWH(-132, -132, 234, 234);
-    filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
+    bounds = filter2->filterBounds(bounds, SkMatrix::I());
 
     REPORTER_ASSERT(reporter, bounds == expectedBounds);
 }
 
+DEF_TEST(ImageFilterScaledBlurRadius, reporter) {
+    // Each blur should spread 3*sigma, so 3 for the blur and 30 for the shadow
+    // (before the CTM). Bounds should be computed correctly in the presence of
+    // a (possibly negative) scale.
+    sk_sp<SkImageFilter> blur(make_blur(nullptr));
+    sk_sp<SkImageFilter> dropShadow(make_drop_shadow(nullptr));
+    {
+        // Uniform scale by 2.
+        SkMatrix scaleMatrix;
+        scaleMatrix.setScale(2, 2);
+        SkIRect bounds = SkIRect::MakeLTRB(0, 0, 200, 200);
+
+        SkIRect expectedBlurBounds = SkIRect::MakeLTRB(-6, -6, 206, 206);
+        SkIRect blurBounds = blur->filterBounds(
+            bounds, scaleMatrix, SkImageFilter::kForward_MapDirection);
+        REPORTER_ASSERT(reporter, blurBounds == expectedBlurBounds);
+        SkIRect reverseBlurBounds = blur->filterBounds(
+            bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection);
+        REPORTER_ASSERT(reporter, reverseBlurBounds == expectedBlurBounds);
+
+        SkIRect expectedShadowBounds = SkIRect::MakeLTRB(0, 0, 460, 460);
+        SkIRect shadowBounds = dropShadow->filterBounds(
+            bounds, scaleMatrix, SkImageFilter::kForward_MapDirection);
+        REPORTER_ASSERT(reporter, shadowBounds == expectedShadowBounds);
+        SkIRect expectedReverseShadowBounds =
+            SkIRect::MakeLTRB(-260, -260, 200, 200);
+        SkIRect reverseShadowBounds = dropShadow->filterBounds(
+            bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection);
+        REPORTER_ASSERT(reporter,
+            reverseShadowBounds == expectedReverseShadowBounds);
+    }
+    {
+        // Vertical flip.
+        SkMatrix scaleMatrix;
+        scaleMatrix.setScale(1, -1);
+        SkIRect bounds = SkIRect::MakeLTRB(0, -100, 100, 0);
+
+        SkIRect expectedBlurBounds = SkIRect::MakeLTRB(-3, -103, 103, 3);
+        SkIRect blurBounds = blur->filterBounds(
+            bounds, scaleMatrix, SkImageFilter::kForward_MapDirection);
+        REPORTER_ASSERT(reporter, blurBounds == expectedBlurBounds);
+        SkIRect reverseBlurBounds = blur->filterBounds(
+            bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection);
+        REPORTER_ASSERT(reporter, reverseBlurBounds == expectedBlurBounds);
+
+        SkIRect expectedShadowBounds = SkIRect::MakeLTRB(0, -230, 230, 0);
+        SkIRect shadowBounds = dropShadow->filterBounds(
+            bounds, scaleMatrix, SkImageFilter::kForward_MapDirection);
+        REPORTER_ASSERT(reporter, shadowBounds == expectedShadowBounds);
+        SkIRect expectedReverseShadowBounds =
+            SkIRect::MakeLTRB(-130, -100, 100, 130);
+        SkIRect reverseShadowBounds = dropShadow->filterBounds(
+            bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection);
+        REPORTER_ASSERT(reporter,
+            reverseShadowBounds == expectedReverseShadowBounds);
+    }
+}
+
+DEF_TEST(ImageFilterComposedBlurFastBounds, reporter) {
+    sk_sp<SkImageFilter> filter1(make_blur(nullptr));
+    sk_sp<SkImageFilter> filter2(make_blur(nullptr));
+    sk_sp<SkImageFilter> composedFilter(SkComposeImageFilter::Make(std::move(filter1),
+                                                                   std::move(filter2)));
+
+    SkRect boundsSrc = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
+    SkRect expectedBounds = SkRect::MakeXYWH(
+        SkIntToScalar(-6), SkIntToScalar(-6), SkIntToScalar(112), SkIntToScalar(112));
+    SkRect boundsDst = composedFilter->computeFastBounds(boundsSrc);
+
+    REPORTER_ASSERT(reporter, boundsDst == expectedBounds);
+}
+
+DEF_TEST(ImageFilterUnionBounds, reporter) {
+    sk_sp<SkImageFilter> offset(SkOffsetImageFilter::Make(50, 0, nullptr));
+    // Regardless of which order they appear in, the image filter bounds should
+    // be combined correctly.
+    {
+        sk_sp<SkImageFilter> composite(SkXfermodeImageFilter::Make(SkBlendMode::kSrcOver, offset));
+        SkRect bounds = SkRect::MakeWH(100, 100);
+        // Intentionally aliasing here, as that's what the real callers do.
+        bounds = composite->computeFastBounds(bounds);
+        REPORTER_ASSERT(reporter, bounds == SkRect::MakeWH(150, 100));
+    }
+    {
+        sk_sp<SkImageFilter> composite(SkXfermodeImageFilter::Make(SkBlendMode::kSrcOver, nullptr,
+                                                                   offset, nullptr));
+        SkRect bounds = SkRect::MakeWH(100, 100);
+        // Intentionally aliasing here, as that's what the real callers do.
+        bounds = composite->computeFastBounds(bounds);
+        REPORTER_ASSERT(reporter, bounds == SkRect::MakeWH(150, 100));
+    }
+}
+
+static void test_imagefilter_merge_result_size(skiatest::Reporter* reporter, GrContext* context) {
+    SkBitmap greenBM;
+    greenBM.allocN32Pixels(20, 20);
+    greenBM.eraseColor(SK_ColorGREEN);
+    sk_sp<SkImage> greenImage(SkImage::MakeFromBitmap(greenBM));
+    sk_sp<SkImageFilter> source(SkImageSource::Make(std::move(greenImage)));
+    sk_sp<SkImageFilter> merge(SkMergeImageFilter::Make(source, source));
+
+    sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 1));
+
+    SkImageFilter::OutputProperties noColorSpace(nullptr);
+    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(0, 0, 100, 100), nullptr,
+                               noColorSpace);
+    SkIPoint offset;
+
+    sk_sp<SkSpecialImage> resultImg(merge->filterImage(srcImg.get(), ctx, &offset));
+    REPORTER_ASSERT(reporter, resultImg);
+
+    REPORTER_ASSERT(reporter, resultImg->width() == 20 && resultImg->height() == 20);
+}
+
+DEF_TEST(ImageFilterMergeResultSize, reporter) {
+    test_imagefilter_merge_result_size(reporter, nullptr);
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterMergeResultSize_Gpu, reporter, ctxInfo) {
+    test_imagefilter_merge_result_size(reporter, ctxInfo.grContext());
+}
+#endif
+
 static void draw_blurred_rect(SkCanvas* canvas) {
-    SkAutoTUnref<SkImageFilter> filter(SkBlurImageFilter::Create(SkIntToScalar(8), 0));
     SkPaint filterPaint;
     filterPaint.setColor(SK_ColorWHITE);
-    filterPaint.setImageFilter(filter);
-    canvas->saveLayer(NULL, &filterPaint);
+    filterPaint.setImageFilter(SkBlurImageFilter::Make(SkIntToScalar(8), 0, nullptr));
+    canvas->saveLayer(nullptr, &filterPaint);
     SkPaint whitePaint;
     whitePaint.setColor(SK_ColorWHITE);
     canvas->drawRect(SkRect::Make(SkIRect::MakeWH(4, 4)), whitePaint);
@@ -615,21 +1023,21 @@
 
     SkPictureRecorder recorder1, recorder2;
     // The only difference between these two pictures is that one has RTree aceleration.
-    SkCanvas* recordingCanvas1 = recorder1.beginRecording(SkIntToScalar(width), 
-                                                          SkIntToScalar(height), 
-                                                          NULL, 0);
-    SkCanvas* recordingCanvas2 = recorder2.beginRecording(SkIntToScalar(width), 
-                                                          SkIntToScalar(height), 
+    SkCanvas* recordingCanvas1 = recorder1.beginRecording(SkIntToScalar(width),
+                                                          SkIntToScalar(height),
+                                                          nullptr, 0);
+    SkCanvas* recordingCanvas2 = recorder2.beginRecording(SkIntToScalar(width),
+                                                          SkIntToScalar(height),
                                                           &factory, 0);
     draw_blurred_rect(recordingCanvas1);
     draw_blurred_rect(recordingCanvas2);
-    SkAutoTUnref<SkPicture> picture1(recorder1.endRecording());
-    SkAutoTUnref<SkPicture> picture2(recorder2.endRecording());
+    sk_sp<SkPicture> picture1(recorder1.finishRecordingAsPicture());
+    sk_sp<SkPicture> picture2(recorder2.finishRecordingAsPicture());
     for (int y = 0; y < height; y += tileSize) {
         for (int x = 0; x < width; x += tileSize) {
             SkRect tileRect = SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize));
-            draw_picture_clipped(&canvas1, tileRect, picture1);
-            draw_picture_clipped(&canvas2, tileRect, picture2);
+            draw_picture_clipped(&canvas1, tileRect, picture1.get());
+            draw_picture_clipped(&canvas2, tileRect, picture2.get());
         }
     }
     for (int y = 0; y < height; y++) {
@@ -650,10 +1058,11 @@
     SkScalar gain = SK_Scalar1, bias = 0;
     SkIPoint kernelOffset = SkIPoint::Make(0, 0);
 
-    SkAutoTUnref<SkImageFilter> filter(
-        SkMatrixConvolutionImageFilter::Create(
-            kernelSize, kernel, gain, bias, kernelOffset,
-            SkMatrixConvolutionImageFilter::kRepeat_TileMode, false));
+    sk_sp<SkImageFilter> filter(SkMatrixConvolutionImageFilter::Make(
+                                            kernelSize, kernel,
+                                            gain, bias, kernelOffset,
+                                            SkMatrixConvolutionImageFilter::kRepeat_TileMode,
+                                            false, nullptr));
 
     SkBitmap result;
     int width = 16, height = 16;
@@ -662,7 +1071,7 @@
     canvas.clear(0);
 
     SkPaint paint;
-    paint.setImageFilter(filter);
+    paint.setImageFilter(std::move(filter));
     SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
     canvas.drawRect(rect, paint);
 }
@@ -677,10 +1086,10 @@
     SkScalar gain = SK_Scalar1, bias = 0;
     SkIPoint kernelOffset = SkIPoint::Make(2, 0);
 
-    SkAutoTUnref<SkImageFilter> filter(
-        SkMatrixConvolutionImageFilter::Create(
-            kernelSize, kernel, gain, bias, kernelOffset,
-            SkMatrixConvolutionImageFilter::kClamp_TileMode, true));
+    sk_sp<SkImageFilter> filter(SkMatrixConvolutionImageFilter::Make(
+                                            kernelSize, kernel, gain, bias, kernelOffset,
+                                            SkMatrixConvolutionImageFilter::kClamp_TileMode,
+                                            true, nullptr));
 
     SkBitmap result;
 
@@ -690,7 +1099,7 @@
     canvas.clear(0);
 
     SkPaint filterPaint;
-    filterPaint.setImageFilter(filter);
+    filterPaint.setImageFilter(std::move(filter));
     SkRect bounds = SkRect::MakeWH(1, 10);
     SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
     SkPaint rectPaint;
@@ -699,18 +1108,64 @@
     canvas.restore();
 }
 
-DEF_TEST(ImageFilterCropRect, reporter) {
-    SkBitmap temp;
-    temp.allocN32Pixels(100, 100);
-    SkBitmapDevice device(temp);
-    test_crop_rects(&device, reporter);
+static void test_big_kernel(skiatest::Reporter* reporter, GrContext* context) {
+    // Check that a kernel that is too big for the GPU still works
+    SkScalar identityKernel[49] = {
+        0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 1, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0
+    };
+    SkISize kernelSize = SkISize::Make(7, 7);
+    SkScalar gain = SK_Scalar1, bias = 0;
+    SkIPoint kernelOffset = SkIPoint::Make(0, 0);
+
+    sk_sp<SkImageFilter> filter(SkMatrixConvolutionImageFilter::Make(
+                                        kernelSize, identityKernel, gain, bias, kernelOffset,
+                                        SkMatrixConvolutionImageFilter::kClamp_TileMode,
+                                        true, nullptr));
+
+    sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 100));
+    SkASSERT(srcImg);
+
+    SkIPoint offset;
+    SkImageFilter::OutputProperties noColorSpace(nullptr);
+    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace);
+    sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset));
+    REPORTER_ASSERT(reporter, resultImg);
+    REPORTER_ASSERT(reporter, SkToBool(context) == resultImg->isTextureBacked());
+    REPORTER_ASSERT(reporter, resultImg->width() == 100 && resultImg->height() == 100);
+    REPORTER_ASSERT(reporter, offset.fX == 0 && offset.fY == 0);
 }
 
+DEF_TEST(ImageFilterMatrixConvolutionBigKernel, reporter) {
+    test_big_kernel(reporter, nullptr);
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterMatrixConvolutionBigKernel_Gpu,
+                                   reporter, ctxInfo) {
+    test_big_kernel(reporter, ctxInfo.grContext());
+}
+#endif
+
+DEF_TEST(ImageFilterCropRect, reporter) {
+    test_crop_rects(reporter, nullptr);
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCropRect_Gpu, reporter, ctxInfo) {
+    test_crop_rects(reporter, ctxInfo.grContext());
+}
+#endif
+
 DEF_TEST(ImageFilterMatrix, reporter) {
     SkBitmap temp;
     temp.allocN32Pixels(100, 100);
-    SkBitmapDevice device(temp);
-    SkCanvas canvas(&device);
+    SkCanvas canvas(temp);
     canvas.scale(SkIntToScalar(2), SkIntToScalar(2));
 
     SkMatrix expectedMatrix = canvas.getTotalMatrix();
@@ -720,10 +1175,8 @@
     SkCanvas* recordingCanvas = recorder.beginRecording(100, 100, &factory, 0);
 
     SkPaint paint;
-    SkAutoTUnref<MatrixTestImageFilter> imageFilter(
-        new MatrixTestImageFilter(reporter, expectedMatrix));
-    paint.setImageFilter(imageFilter.get());
-    recordingCanvas->saveLayer(NULL, &paint);
+    paint.setImageFilter(MatrixTestImageFilter::Make(reporter, expectedMatrix));
+    recordingCanvas->saveLayer(nullptr, &paint);
     SkPaint solidPaint;
     solidPaint.setColor(0xFFFFFFFF);
     recordingCanvas->save();
@@ -731,9 +1184,8 @@
     recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(100, 100)), solidPaint);
     recordingCanvas->restore(); // scale
     recordingCanvas->restore(); // saveLayer
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
 
-    canvas.drawPicture(picture);
+    canvas.drawPicture(recorder.finishRecordingAsPicture());
 }
 
 DEF_TEST(ImageFilterCrossProcessPictureImageFilter, reporter) {
@@ -745,28 +1197,26 @@
     SkPaint greenPaint;
     greenPaint.setColor(SK_ColorGREEN);
     recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
 
     // Wrap that SkPicture in an SkPictureImageFilter.
-    SkAutoTUnref<SkImageFilter> imageFilter(
-        SkPictureImageFilter::Create(picture.get()));
+    sk_sp<SkImageFilter> imageFilter(SkPictureImageFilter::Make(picture));
 
     // Check that SkPictureImageFilter successfully serializes its contained
     // SkPicture when not in cross-process mode.
     SkPaint paint;
-    paint.setImageFilter(imageFilter.get());
+    paint.setImageFilter(imageFilter);
     SkPictureRecorder outerRecorder;
     SkCanvas* outerCanvas = outerRecorder.beginRecording(1, 1, &factory, 0);
     SkPaint redPaintWithFilter;
     redPaintWithFilter.setColor(SK_ColorRED);
-    redPaintWithFilter.setImageFilter(imageFilter.get());
+    redPaintWithFilter.setImageFilter(imageFilter);
     outerCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
-    SkAutoTUnref<SkPicture> outerPicture(outerRecorder.endRecording());
+    sk_sp<SkPicture> outerPicture(outerRecorder.finishRecordingAsPicture());
 
     SkBitmap bitmap;
     bitmap.allocN32Pixels(1, 1);
-    SkBitmapDevice device(bitmap);
-    SkCanvas canvas(&device);
+    SkCanvas canvas(bitmap);
 
     // The result here should be green, since the filter replaces the primitive's red interior.
     canvas.clear(0x0);
@@ -777,75 +1227,87 @@
     // Check that, for now, SkPictureImageFilter does not serialize or
     // deserialize its contained picture when the filter is serialized
     // cross-process. Do this by "laundering" it through SkValidatingReadBuffer.
-    SkAutoTUnref<SkData> data(SkValidatingSerializeFlattenable(imageFilter.get()));
-    SkAutoTUnref<SkFlattenable> flattenable(SkValidatingDeserializeFlattenable(
-        data->data(), data->size(), SkImageFilter::GetFlattenableType()));
-    SkImageFilter* unflattenedFilter = static_cast<SkImageFilter*>(flattenable.get());
+    sk_sp<SkData> data(SkValidatingSerializeFlattenable(imageFilter.get()));
+    sk_sp<SkImageFilter> unflattenedFilter = SkValidatingDeserializeImageFilter(data->data(),
+                                                                                data->size());
 
     redPaintWithFilter.setImageFilter(unflattenedFilter);
     SkPictureRecorder crossProcessRecorder;
     SkCanvas* crossProcessCanvas = crossProcessRecorder.beginRecording(1, 1, &factory, 0);
     crossProcessCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
-    SkAutoTUnref<SkPicture> crossProcessPicture(crossProcessRecorder.endRecording());
+    sk_sp<SkPicture> crossProcessPicture(crossProcessRecorder.finishRecordingAsPicture());
 
     canvas.clear(0x0);
     canvas.drawPicture(crossProcessPicture);
     pixel = *bitmap.getAddr32(0, 0);
-    // The result here should not be green, since the filter draws nothing.
-    REPORTER_ASSERT(reporter, pixel != SK_ColorGREEN);
+    // If the security precautions are enabled, the result here should not be green, since the
+    // filter draws nothing.
+    REPORTER_ASSERT(reporter, SkPicture::PictureIOSecurityPrecautionsEnabled()
+        ? pixel != SK_ColorGREEN : pixel == SK_ColorGREEN);
+}
+
+static void test_clipped_picture_imagefilter(skiatest::Reporter* reporter, GrContext* context) {
+    sk_sp<SkPicture> picture;
+
+    {
+        SkRTreeFactory factory;
+        SkPictureRecorder recorder;
+        SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
+
+        // Create an SkPicture which simply draws a green 1x1 rectangle.
+        SkPaint greenPaint;
+        greenPaint.setColor(SK_ColorGREEN);
+        recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
+        picture = recorder.finishRecordingAsPicture();
+    }
+
+    sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 2));
+
+    sk_sp<SkImageFilter> imageFilter(SkPictureImageFilter::Make(picture));
+
+    SkIPoint offset;
+    SkImageFilter::OutputProperties noColorSpace(nullptr);
+    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(1, 1, 1, 1), nullptr, noColorSpace);
+
+    sk_sp<SkSpecialImage> resultImage(imageFilter->filterImage(srcImg.get(), ctx, &offset));
+    REPORTER_ASSERT(reporter, !resultImage);
 }
 
 DEF_TEST(ImageFilterClippedPictureImageFilter, reporter) {
-    SkRTreeFactory factory;
-    SkPictureRecorder recorder;
-    SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
-
-    // Create an SkPicture which simply draws a green 1x1 rectangle.
-    SkPaint greenPaint;
-    greenPaint.setColor(SK_ColorGREEN);
-    recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
-
-    SkAutoTUnref<SkImageFilter> imageFilter(
-        SkPictureImageFilter::Create(picture.get()));
-
-    SkBitmap result;
-    SkIPoint offset;
-    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(1, 1, 1, 1), NULL);
-    SkBitmap bitmap;
-    bitmap.allocN32Pixels(2, 2);
-    SkBitmapDevice device(bitmap);
-    SkDeviceImageFilterProxy proxy(&device);
-    REPORTER_ASSERT(reporter, !imageFilter->filterImage(&proxy, bitmap, ctx, &result, &offset));
+    test_clipped_picture_imagefilter(reporter, nullptr);
 }
 
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterClippedPictureImageFilter_Gpu, reporter, ctxInfo) {
+    test_clipped_picture_imagefilter(reporter, ctxInfo.grContext());
+}
+#endif
+
 DEF_TEST(ImageFilterEmptySaveLayer, reporter) {
     // Even when there's an empty saveLayer()/restore(), ensure that an image
     // filter or color filter which affects transparent black still draws.
 
     SkBitmap bitmap;
     bitmap.allocN32Pixels(10, 10);
-    SkBitmapDevice device(bitmap);
-    SkCanvas canvas(&device);
+    SkCanvas canvas(bitmap);
 
     SkRTreeFactory factory;
     SkPictureRecorder recorder;
 
-    SkAutoTUnref<SkColorFilter> green(
-        SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrc_Mode));
-    SkAutoTUnref<SkColorFilterImageFilter> imageFilter(
-        SkColorFilterImageFilter::Create(green.get()));
+    sk_sp<SkColorFilter> green(SkColorFilter::MakeModeFilter(SK_ColorGREEN,
+                                                             SkBlendMode::kSrc));
+    sk_sp<SkImageFilter> imageFilter(SkColorFilterImageFilter::Make(green, nullptr));
     SkPaint imageFilterPaint;
-    imageFilterPaint.setImageFilter(imageFilter.get());
+    imageFilterPaint.setImageFilter(std::move(imageFilter));
     SkPaint colorFilterPaint;
-    colorFilterPaint.setColorFilter(green.get());
+    colorFilterPaint.setColorFilter(green);
 
     SkRect bounds = SkRect::MakeWH(10, 10);
 
     SkCanvas* recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
     recordingCanvas->saveLayer(&bounds, &imageFilterPaint);
     recordingCanvas->restore();
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
 
     canvas.clear(0);
     canvas.drawPicture(picture);
@@ -853,9 +1315,9 @@
     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
 
     recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
-    recordingCanvas->saveLayer(NULL, &imageFilterPaint);
+    recordingCanvas->saveLayer(nullptr, &imageFilterPaint);
     recordingCanvas->restore();
-    SkAutoTUnref<SkPicture> picture2(recorder.endRecording());
+    sk_sp<SkPicture> picture2(recorder.finishRecordingAsPicture());
 
     canvas.clear(0);
     canvas.drawPicture(picture2);
@@ -865,7 +1327,7 @@
     recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
     recordingCanvas->saveLayer(&bounds, &colorFilterPaint);
     recordingCanvas->restore();
-    SkAutoTUnref<SkPicture> picture3(recorder.endRecording());
+    sk_sp<SkPicture> picture3(recorder.finishRecordingAsPicture());
 
     canvas.clear(0);
     canvas.drawPicture(picture3);
@@ -873,133 +1335,131 @@
     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
 }
 
-static void test_huge_blur(SkBaseDevice* device, skiatest::Reporter* reporter) {
-    SkCanvas canvas(device);
-
+static void test_huge_blur(SkCanvas* canvas, skiatest::Reporter* reporter) {
     SkBitmap bitmap;
     bitmap.allocN32Pixels(100, 100);
     bitmap.eraseARGB(0, 0, 0, 0);
 
     // Check that a blur with an insane radius does not crash or assert.
-    SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(SkIntToScalar(1<<30), SkIntToScalar(1<<30)));
-
     SkPaint paint;
-    paint.setImageFilter(blur);
-    canvas.drawSprite(bitmap, 0, 0, &paint);
+    paint.setImageFilter(SkBlurImageFilter::Make(SkIntToScalar(1<<30),
+                                                 SkIntToScalar(1<<30),
+                                                 nullptr));
+    canvas->drawBitmap(bitmap, 0, 0, &paint);
 }
 
 DEF_TEST(HugeBlurImageFilter, reporter) {
     SkBitmap temp;
     temp.allocN32Pixels(100, 100);
-    SkBitmapDevice device(temp);
-    test_huge_blur(&device, reporter);
+    SkCanvas canvas(temp);
+    test_huge_blur(&canvas, reporter);
 }
 
-DEF_TEST(MatrixConvolutionSanityTest, reporter) {
+DEF_TEST(ImageFilterMatrixConvolutionSanityTest, reporter) {
     SkScalar kernel[1] = { 0 };
     SkScalar gain = SK_Scalar1, bias = 0;
     SkIPoint kernelOffset = SkIPoint::Make(1, 1);
 
-    // Check that an enormous (non-allocatable) kernel gives a NULL filter.
-    SkAutoTUnref<SkImageFilter> conv(SkMatrixConvolutionImageFilter::Create(
+    // Check that an enormous (non-allocatable) kernel gives a nullptr filter.
+    sk_sp<SkImageFilter> conv(SkMatrixConvolutionImageFilter::Make(
         SkISize::Make(1<<30, 1<<30),
         kernel,
         gain,
         bias,
         kernelOffset,
         SkMatrixConvolutionImageFilter::kRepeat_TileMode,
-        false));
+        false,
+        nullptr));
 
-    REPORTER_ASSERT(reporter, NULL == conv.get());
+    REPORTER_ASSERT(reporter, nullptr == conv.get());
 
-    // Check that a NULL kernel gives a NULL filter.
-    conv.reset(SkMatrixConvolutionImageFilter::Create(
+    // Check that a nullptr kernel gives a nullptr filter.
+    conv = SkMatrixConvolutionImageFilter::Make(
         SkISize::Make(1, 1),
-        NULL,
+        nullptr,
         gain,
         bias,
         kernelOffset,
         SkMatrixConvolutionImageFilter::kRepeat_TileMode,
-        false));
+        false,
+        nullptr);
 
-    REPORTER_ASSERT(reporter, NULL == conv.get());
+    REPORTER_ASSERT(reporter, nullptr == conv.get());
 
-    // Check that a kernel width < 1 gives a NULL filter.
-    conv.reset(SkMatrixConvolutionImageFilter::Create(
+    // Check that a kernel width < 1 gives a nullptr filter.
+    conv = SkMatrixConvolutionImageFilter::Make(
         SkISize::Make(0, 1),
         kernel,
         gain,
         bias,
         kernelOffset,
         SkMatrixConvolutionImageFilter::kRepeat_TileMode,
-        false));
+        false,
+        nullptr);
 
-    REPORTER_ASSERT(reporter, NULL == conv.get());
+    REPORTER_ASSERT(reporter, nullptr == conv.get());
 
-    // Check that kernel height < 1 gives a NULL filter.
-    conv.reset(SkMatrixConvolutionImageFilter::Create(
+    // Check that kernel height < 1 gives a nullptr filter.
+    conv = SkMatrixConvolutionImageFilter::Make(
         SkISize::Make(1, -1),
         kernel,
         gain,
         bias,
         kernelOffset,
         SkMatrixConvolutionImageFilter::kRepeat_TileMode,
-        false));
+        false,
+        nullptr);
 
-    REPORTER_ASSERT(reporter, NULL == conv.get());
+    REPORTER_ASSERT(reporter, nullptr == conv.get());
 }
 
-static void test_xfermode_cropped_input(SkBaseDevice* device, skiatest::Reporter* reporter) {
-    SkCanvas canvas(device);
-    canvas.clear(0);
+static void test_xfermode_cropped_input(SkCanvas* canvas, skiatest::Reporter* reporter) {
+    canvas->clear(0);
 
     SkBitmap bitmap;
     bitmap.allocN32Pixels(1, 1);
     bitmap.eraseARGB(255, 255, 255, 255);
 
-    SkAutoTUnref<SkColorFilter> green(
-        SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrcIn_Mode));
-    SkAutoTUnref<SkColorFilterImageFilter> greenFilter(
-        SkColorFilterImageFilter::Create(green.get()));
+    sk_sp<SkColorFilter> green(SkColorFilter::MakeModeFilter(SK_ColorGREEN,
+                                                             SkBlendMode::kSrcIn));
+    sk_sp<SkImageFilter> greenFilter(SkColorFilterImageFilter::Make(green, nullptr));
     SkImageFilter::CropRect cropRect(SkRect::MakeEmpty());
-    SkAutoTUnref<SkColorFilterImageFilter> croppedOut(
-        SkColorFilterImageFilter::Create(green.get(), NULL, &cropRect));
+    sk_sp<SkImageFilter> croppedOut(SkColorFilterImageFilter::Make(green, nullptr, &cropRect));
 
     // Check that an xfermode image filter whose input has been cropped out still draws the other
     // input. Also check that drawing with both inputs cropped out doesn't cause a GPU warning.
-    SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrcOver_Mode);
-    SkAutoTUnref<SkImageFilter> xfermodeNoFg(
-        SkXfermodeImageFilter::Create(mode, greenFilter, croppedOut));
-    SkAutoTUnref<SkImageFilter> xfermodeNoBg(
-        SkXfermodeImageFilter::Create(mode, croppedOut, greenFilter));
-    SkAutoTUnref<SkImageFilter> xfermodeNoFgNoBg(
-        SkXfermodeImageFilter::Create(mode, croppedOut, croppedOut));
+    SkBlendMode mode = SkBlendMode::kSrcOver;
+    sk_sp<SkImageFilter> xfermodeNoFg(SkXfermodeImageFilter::Make(mode, greenFilter,
+                                                                  croppedOut, nullptr));
+    sk_sp<SkImageFilter> xfermodeNoBg(SkXfermodeImageFilter::Make(mode, croppedOut,
+                                                                  greenFilter, nullptr));
+    sk_sp<SkImageFilter> xfermodeNoFgNoBg(SkXfermodeImageFilter::Make(mode, croppedOut,
+                                                                      croppedOut, nullptr));
 
     SkPaint paint;
-    paint.setImageFilter(xfermodeNoFg);
-    canvas.drawSprite(bitmap, 0, 0, &paint);
+    paint.setImageFilter(std::move(xfermodeNoFg));
+    canvas->drawBitmap(bitmap, 0, 0, &paint);   // drawSprite
 
     uint32_t pixel;
-    SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
-    canvas.readPixels(info, &pixel, 4, 0, 0);
+    SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType);
+    canvas->readPixels(info, &pixel, 4, 0, 0);
     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
 
-    paint.setImageFilter(xfermodeNoBg);
-    canvas.drawSprite(bitmap, 0, 0, &paint);
-    canvas.readPixels(info, &pixel, 4, 0, 0);
+    paint.setImageFilter(std::move(xfermodeNoBg));
+    canvas->drawBitmap(bitmap, 0, 0, &paint);   // drawSprite
+    canvas->readPixels(info, &pixel, 4, 0, 0);
     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
 
-    paint.setImageFilter(xfermodeNoFgNoBg);
-    canvas.drawSprite(bitmap, 0, 0, &paint);
-    canvas.readPixels(info, &pixel, 4, 0, 0);
+    paint.setImageFilter(std::move(xfermodeNoFgNoBg));
+    canvas->drawBitmap(bitmap, 0, 0, &paint);   // drawSprite
+    canvas->readPixels(info, &pixel, 4, 0, 0);
     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
 }
 
 DEF_TEST(ImageFilterNestedSaveLayer, reporter) {
     SkBitmap temp;
     temp.allocN32Pixels(50, 50);
-    SkBitmapDevice device(temp);
-    SkCanvas canvas(&device);
+    SkCanvas canvas(temp);
     canvas.clear(0x0);
 
     SkBitmap bitmap;
@@ -1009,15 +1469,15 @@
     SkMatrix matrix;
     matrix.setScale(SkIntToScalar(2), SkIntToScalar(2));
     matrix.postTranslate(SkIntToScalar(-20), SkIntToScalar(-20));
-    SkAutoTUnref<SkImageFilter> matrixFilter(
-        SkMatrixImageFilter::Create(matrix, SkPaint::kLow_FilterLevel));
+    sk_sp<SkImageFilter> matrixFilter(
+        SkImageFilter::MakeMatrixFilter(matrix, kLow_SkFilterQuality, nullptr));
 
     // Test that saveLayer() with a filter nested inside another saveLayer() applies the
     // correct offset to the filter matrix.
     SkRect bounds1 = SkRect::MakeXYWH(10, 10, 30, 30);
-    canvas.saveLayer(&bounds1, NULL);
+    canvas.saveLayer(&bounds1, nullptr);
     SkPaint filterPaint;
-    filterPaint.setImageFilter(matrixFilter);
+    filterPaint.setImageFilter(std::move(matrixFilter));
     SkRect bounds2 = SkRect::MakeXYWH(20, 20, 10, 10);
     canvas.saveLayer(&bounds2, &filterPaint);
     SkPaint greenPaint;
@@ -1029,7 +1489,7 @@
     strokePaint.setStyle(SkPaint::kStroke_Style);
     strokePaint.setColor(SK_ColorRED);
 
-    SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
+    SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType);
     uint32_t pixel;
     canvas.readPixels(info, &pixel, 4, 25, 25);
     REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
@@ -1038,8 +1498,8 @@
     // correct offset to the filter matrix.
     canvas.clear(0x0);
     canvas.readPixels(info, &pixel, 4, 25, 25);
-    canvas.saveLayer(&bounds1, NULL);
-    canvas.drawSprite(bitmap, 20, 20, &filterPaint);
+    canvas.saveLayer(&bounds1, nullptr);
+    canvas.drawBitmap(bitmap, 20, 20, &filterPaint);    // drawSprite
     canvas.restore();
 
     canvas.readPixels(info, &pixel, 4, 25, 25);
@@ -1049,46 +1509,416 @@
 DEF_TEST(XfermodeImageFilterCroppedInput, reporter) {
     SkBitmap temp;
     temp.allocN32Pixels(100, 100);
-    SkBitmapDevice device(temp);
-    test_xfermode_cropped_input(&device, reporter);
+    SkCanvas canvas(temp);
+    test_xfermode_cropped_input(&canvas, reporter);
+}
+
+static void test_composed_imagefilter_offset(skiatest::Reporter* reporter, GrContext* context) {
+    sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 100));
+
+    SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(1, 0, 20, 20));
+    sk_sp<SkImageFilter> offsetFilter(SkOffsetImageFilter::Make(0, 0, nullptr, &cropRect));
+    sk_sp<SkImageFilter> blurFilter(SkBlurImageFilter::Make(SK_Scalar1, SK_Scalar1,
+                                                            nullptr, &cropRect));
+    sk_sp<SkImageFilter> composedFilter(SkComposeImageFilter::Make(std::move(blurFilter),
+                                                                   std::move(offsetFilter)));
+    SkIPoint offset;
+    SkImageFilter::OutputProperties noColorSpace(nullptr);
+    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace);
+
+    sk_sp<SkSpecialImage> resultImg(composedFilter->filterImage(srcImg.get(), ctx, &offset));
+    REPORTER_ASSERT(reporter, resultImg);
+    REPORTER_ASSERT(reporter, offset.fX == 1 && offset.fY == 0);
+}
+
+DEF_TEST(ComposedImageFilterOffset, reporter) {
+    test_composed_imagefilter_offset(reporter, nullptr);
 }
 
 #if SK_SUPPORT_GPU
-const SkSurfaceProps gProps = SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
-
-DEF_GPUTEST(ImageFilterCropRectGPU, reporter, factory) {
-    GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
-    SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
-                                                         SkImageInfo::MakeN32Premul(100, 100),
-                                                         gProps,
-                                                         0));
-    test_crop_rects(device, reporter);
-}
-
-DEF_GPUTEST(HugeBlurImageFilterGPU, reporter, factory) {
-    GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
-    SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
-                                                         SkImageInfo::MakeN32Premul(100, 100),
-                                                         gProps,
-                                                         0));
-    test_huge_blur(device, reporter);
-}
-
-DEF_GPUTEST(XfermodeImageFilterCroppedInputGPU, reporter, factory) {
-    GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
-    SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
-                                                         SkImageInfo::MakeN32Premul(1, 1),
-                                                         gProps,
-                                                         0));
-    test_xfermode_cropped_input(device, reporter);
-}
-
-DEF_GPUTEST(TestNegativeBlurSigmaGPU, reporter, factory) {
-    GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
-    SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
-                                                         SkImageInfo::MakeN32Premul(1, 1),
-                                                         gProps,
-                                                         0));
-    test_negative_blur_sigma(device, reporter);
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ComposedImageFilterOffset_Gpu, reporter, ctxInfo) {
+    test_composed_imagefilter_offset(reporter, ctxInfo.grContext());
 }
 #endif
+
+static void test_composed_imagefilter_bounds(skiatest::Reporter* reporter, GrContext* context) {
+    // The bounds passed to the inner filter must be filtered by the outer
+    // filter, so that the inner filter produces the pixels that the outer
+    // filter requires as input. This matters if the outer filter moves pixels.
+    // Here, accounting for the outer offset is necessary so that the green
+    // pixels of the picture are not clipped.
+
+    SkPictureRecorder recorder;
+    SkCanvas* recordingCanvas = recorder.beginRecording(SkRect::MakeWH(200, 100));
+    recordingCanvas->clipRect(SkRect::MakeXYWH(100, 0, 100, 100));
+    recordingCanvas->clear(SK_ColorGREEN);
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
+    sk_sp<SkImageFilter> pictureFilter(SkPictureImageFilter::Make(picture));
+    SkImageFilter::CropRect cropRect(SkRect::MakeWH(100, 100));
+    sk_sp<SkImageFilter> offsetFilter(SkOffsetImageFilter::Make(-100, 0, nullptr, &cropRect));
+    sk_sp<SkImageFilter> composedFilter(SkComposeImageFilter::Make(std::move(offsetFilter),
+                                                                   std::move(pictureFilter)));
+
+    sk_sp<SkSpecialImage> sourceImage(create_empty_special_image(context, 100));
+    SkImageFilter::OutputProperties noColorSpace(nullptr);
+    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace);
+    SkIPoint offset;
+    sk_sp<SkSpecialImage> result(composedFilter->filterImage(sourceImage.get(), ctx, &offset));
+    REPORTER_ASSERT(reporter, offset.isZero());
+    REPORTER_ASSERT(reporter, result);
+    REPORTER_ASSERT(reporter, result->subset().size() == SkISize::Make(100, 100));
+
+    SkBitmap resultBM;
+    REPORTER_ASSERT(reporter, result->getROPixels(&resultBM));
+    REPORTER_ASSERT(reporter, resultBM.getColor(50, 50) == SK_ColorGREEN);
+}
+
+DEF_TEST(ComposedImageFilterBounds, reporter) {
+    test_composed_imagefilter_bounds(reporter, nullptr);
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ComposedImageFilterBounds_Gpu, reporter, ctxInfo) {
+    test_composed_imagefilter_bounds(reporter, ctxInfo.grContext());
+}
+#endif
+
+static void test_partial_crop_rect(skiatest::Reporter* reporter, GrContext* context) {
+    sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, 100));
+
+    SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(100, 0, 20, 30),
+        SkImageFilter::CropRect::kHasWidth_CropEdge | SkImageFilter::CropRect::kHasHeight_CropEdge);
+    sk_sp<SkImageFilter> filter(make_grayscale(nullptr, &cropRect));
+    SkIPoint offset;
+    SkImageFilter::OutputProperties noColorSpace(nullptr);
+    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, noColorSpace);
+
+    sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset));
+    REPORTER_ASSERT(reporter, resultImg);
+
+    REPORTER_ASSERT(reporter, offset.fX == 0);
+    REPORTER_ASSERT(reporter, offset.fY == 0);
+    REPORTER_ASSERT(reporter, resultImg->width() == 20);
+    REPORTER_ASSERT(reporter, resultImg->height() == 30);
+}
+
+DEF_TEST(ImageFilterPartialCropRect, reporter) {
+    test_partial_crop_rect(reporter, nullptr);
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterPartialCropRect_Gpu, reporter, ctxInfo) {
+    test_partial_crop_rect(reporter, ctxInfo.grContext());
+}
+#endif
+
+DEF_TEST(ImageFilterCanComputeFastBounds, reporter) {
+
+    {
+        SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
+        sk_sp<SkImageFilter> lighting(SkLightingImageFilter::MakePointLitDiffuse(location,
+                                                                                 SK_ColorGREEN,
+                                                                                 0, 0, nullptr));
+        REPORTER_ASSERT(reporter, !lighting->canComputeFastBounds());
+    }
+
+    {
+        sk_sp<SkImageFilter> gray(make_grayscale(nullptr, nullptr));
+        REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
+        {
+            SkColorFilter* grayCF;
+            REPORTER_ASSERT(reporter, gray->asAColorFilter(&grayCF));
+            REPORTER_ASSERT(reporter, !grayCF->affectsTransparentBlack());
+            grayCF->unref();
+        }
+        REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
+
+        sk_sp<SkImageFilter> grayBlur(SkBlurImageFilter::Make(SK_Scalar1, SK_Scalar1,
+                                                              std::move(gray)));
+        REPORTER_ASSERT(reporter, grayBlur->canComputeFastBounds());
+    }
+
+    {
+        SkScalar greenMatrix[20] = { 0, 0, 0, 0, 0,
+                                     0, 0, 0, 0, 1,
+                                     0, 0, 0, 0, 0,
+                                     0, 0, 0, 0, 1 };
+        sk_sp<SkColorFilter> greenCF(SkColorFilter::MakeMatrixFilterRowMajor255(greenMatrix));
+        sk_sp<SkImageFilter> green(SkColorFilterImageFilter::Make(greenCF, nullptr));
+
+        REPORTER_ASSERT(reporter, greenCF->affectsTransparentBlack());
+        REPORTER_ASSERT(reporter, !green->canComputeFastBounds());
+
+        sk_sp<SkImageFilter> greenBlur(SkBlurImageFilter::Make(SK_Scalar1, SK_Scalar1,
+                                                               std::move(green)));
+        REPORTER_ASSERT(reporter, !greenBlur->canComputeFastBounds());
+    }
+
+    uint8_t allOne[256], identity[256];
+    for (int i = 0; i < 256; ++i) {
+        identity[i] = i;
+        allOne[i] = 255;
+    }
+
+    sk_sp<SkColorFilter> identityCF(SkTableColorFilter::MakeARGB(identity, identity,
+                                                                 identity, allOne));
+    sk_sp<SkImageFilter> identityFilter(SkColorFilterImageFilter::Make(identityCF, nullptr));
+    REPORTER_ASSERT(reporter, !identityCF->affectsTransparentBlack());
+    REPORTER_ASSERT(reporter, identityFilter->canComputeFastBounds());
+
+    sk_sp<SkColorFilter> forceOpaqueCF(SkTableColorFilter::MakeARGB(allOne, identity,
+                                                                    identity, identity));
+    sk_sp<SkImageFilter> forceOpaque(SkColorFilterImageFilter::Make(forceOpaqueCF, nullptr));
+    REPORTER_ASSERT(reporter, forceOpaqueCF->affectsTransparentBlack());
+    REPORTER_ASSERT(reporter, !forceOpaque->canComputeFastBounds());
+}
+
+// Verify that SkImageSource survives serialization
+DEF_TEST(ImageFilterImageSourceSerialization, reporter) {
+    auto surface(SkSurface::MakeRasterN32Premul(10, 10));
+    surface->getCanvas()->clear(SK_ColorGREEN);
+    sk_sp<SkImage> image(surface->makeImageSnapshot());
+    sk_sp<SkImageFilter> filter(SkImageSource::Make(std::move(image)));
+
+    sk_sp<SkData> data(SkValidatingSerializeFlattenable(filter.get()));
+    sk_sp<SkImageFilter> unflattenedFilter = SkValidatingDeserializeImageFilter(data->data(),
+                                                                                data->size());
+    REPORTER_ASSERT(reporter, unflattenedFilter);
+
+    SkBitmap bm;
+    bm.allocN32Pixels(10, 10);
+    bm.eraseColor(SK_ColorBLUE);
+    SkPaint paint;
+    paint.setColor(SK_ColorRED);
+    paint.setImageFilter(unflattenedFilter);
+
+    SkCanvas canvas(bm);
+    canvas.drawRect(SkRect::MakeWH(10, 10), paint);
+    REPORTER_ASSERT(reporter, *bm.getAddr32(0, 0) == SkPreMultiplyColor(SK_ColorGREEN));
+}
+
+static void test_large_blur_input(skiatest::Reporter* reporter, SkCanvas* canvas) {
+    SkBitmap largeBmp;
+    int largeW = 5000;
+    int largeH = 5000;
+#if SK_SUPPORT_GPU
+    // If we're GPU-backed make the bitmap too large to be converted into a texture.
+    if (GrContext* ctx = canvas->getGrContext()) {
+        largeW = ctx->caps()->maxTextureSize() + 1;
+    }
+#endif
+
+    largeBmp.allocN32Pixels(largeW, largeH);
+    largeBmp.eraseColor(0);
+    if (!largeBmp.getPixels()) {
+        ERRORF(reporter, "Failed to allocate large bmp.");
+        return;
+    }
+
+    sk_sp<SkImage> largeImage(SkImage::MakeFromBitmap(largeBmp));
+    if (!largeImage) {
+        ERRORF(reporter, "Failed to create large image.");
+        return;
+    }
+
+    sk_sp<SkImageFilter> largeSource(SkImageSource::Make(std::move(largeImage)));
+    if (!largeSource) {
+        ERRORF(reporter, "Failed to create large SkImageSource.");
+        return;
+    }
+
+    sk_sp<SkImageFilter> blur(SkBlurImageFilter::Make(10.f, 10.f, std::move(largeSource)));
+    if (!blur) {
+        ERRORF(reporter, "Failed to create SkBlurImageFilter.");
+        return;
+    }
+
+    SkPaint paint;
+    paint.setImageFilter(std::move(blur));
+
+    // This should not crash (http://crbug.com/570479).
+    canvas->drawRect(SkRect::MakeIWH(largeW, largeH), paint);
+}
+
+DEF_TEST(ImageFilterBlurLargeImage, reporter) {
+    auto surface(SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(100, 100)));
+    test_large_blur_input(reporter, surface->getCanvas());
+}
+
+static void test_make_with_filter(skiatest::Reporter* reporter, GrContext* context) {
+    sk_sp<SkSurface> surface(create_surface(context, 192, 128));
+    surface->getCanvas()->clear(SK_ColorRED);
+    SkPaint bluePaint;
+    bluePaint.setColor(SK_ColorBLUE);
+    SkIRect subset = SkIRect::MakeXYWH(25, 20, 50, 50);
+    surface->getCanvas()->drawRect(SkRect::Make(subset), bluePaint);
+    sk_sp<SkImage> sourceImage = surface->makeImageSnapshot();
+
+    sk_sp<SkImageFilter> filter = make_grayscale(nullptr, nullptr);
+    SkIRect clipBounds = SkIRect::MakeXYWH(30, 35, 100, 100);
+    SkIRect outSubset;
+    SkIPoint offset;
+    sk_sp<SkImage> result;
+
+    result = sourceImage->makeWithFilter(nullptr, subset, clipBounds, &outSubset, &offset);
+    REPORTER_ASSERT(reporter, !result);
+
+    result = sourceImage->makeWithFilter(filter.get(), subset, clipBounds, nullptr, &offset);
+    REPORTER_ASSERT(reporter, !result);
+
+    result = sourceImage->makeWithFilter(filter.get(), subset, clipBounds, &outSubset, nullptr);
+    REPORTER_ASSERT(reporter, !result);
+
+    SkIRect bigSubset = SkIRect::MakeXYWH(-10000, -10000, 20000, 20000);
+    result = sourceImage->makeWithFilter(filter.get(), bigSubset, clipBounds, &outSubset, &offset);
+    REPORTER_ASSERT(reporter, !result);
+
+    SkIRect empty = SkIRect::MakeEmpty();
+    result = sourceImage->makeWithFilter(filter.get(), empty, clipBounds, &outSubset, &offset);
+    REPORTER_ASSERT(reporter, !result);
+
+    result = sourceImage->makeWithFilter(filter.get(), subset, empty, &outSubset, &offset);
+    REPORTER_ASSERT(reporter, !result);
+
+    SkIRect leftField = SkIRect::MakeXYWH(-1000, 0, 100, 100);
+    result = sourceImage->makeWithFilter(filter.get(), subset, leftField, &outSubset, &offset);
+    REPORTER_ASSERT(reporter, !result);
+
+    result = sourceImage->makeWithFilter(filter.get(), subset, clipBounds, &outSubset, &offset);
+
+    REPORTER_ASSERT(reporter, result);
+    REPORTER_ASSERT(reporter, result->bounds().contains(outSubset));
+    SkIRect destRect = SkIRect::MakeXYWH(offset.x(), offset.y(),
+                                          outSubset.width(), outSubset.height());
+    REPORTER_ASSERT(reporter, clipBounds.contains(destRect));
+
+    // In GPU-mode, this case creates a special image with a backing size that differs from
+    // the content size
+    {
+        clipBounds.setXYWH(0, 0, 170, 100);
+        subset.setXYWH(0, 0, 160, 90);
+
+        filter = SkXfermodeImageFilter::Make(SkBlendMode::kSrc, nullptr);
+        result = sourceImage->makeWithFilter(filter.get(), subset, clipBounds, &outSubset, &offset);
+        REPORTER_ASSERT(reporter, result);
+    }
+}
+
+DEF_TEST(ImageFilterMakeWithFilter, reporter) {
+    test_make_with_filter(reporter, nullptr);
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterMakeWithFilter_Gpu, reporter, ctxInfo) {
+    test_make_with_filter(reporter, ctxInfo.grContext());
+}
+#endif
+
+#if SK_SUPPORT_GPU
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterHugeBlur_Gpu, reporter, ctxInfo) {
+
+    sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(ctxInfo.grContext(),
+                                                      SkBudgeted::kNo,
+                                                      SkImageInfo::MakeN32Premul(100, 100)));
+
+
+    SkCanvas* canvas = surf->getCanvas();
+
+    test_huge_blur(canvas, reporter);
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(XfermodeImageFilterCroppedInput_Gpu, reporter, ctxInfo) {
+    sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(
+            ctxInfo.grContext(),
+            SkBudgeted::kNo,
+            SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, kPremul_SkAlphaType)));
+
+    SkCanvas* canvas = surf->getCanvas();
+
+    test_xfermode_cropped_input(canvas, reporter);
+}
+
+DEF_GPUTEST_FOR_ALL_CONTEXTS(ImageFilterBlurLargeImage_Gpu, reporter, ctxInfo) {
+    auto surface(SkSurface::MakeRenderTarget(
+            ctxInfo.grContext(), SkBudgeted::kYes,
+            SkImageInfo::Make(100, 100, kRGBA_8888_SkColorType, kPremul_SkAlphaType)));
+    test_large_blur_input(reporter, surface->getCanvas());
+}
+#endif
+
+/*
+ *  Test that colorfilterimagefilter does not require its CTM to be decomposed when it has more
+ *  than just scale/translate, but that other filters do.
+ */
+DEF_TEST(ImageFilterComplexCTM, reporter) {
+    // just need a colorfilter to exercise the corresponding imagefilter
+    sk_sp<SkColorFilter> cf = SkColorFilter::MakeModeFilter(SK_ColorRED, SkBlendMode::kSrcATop);
+    sk_sp<SkImageFilter> cfif = SkColorFilterImageFilter::Make(cf, nullptr);    // can handle
+    sk_sp<SkImageFilter> blif = SkBlurImageFilter::Make(3, 3, nullptr);         // cannot handle
+
+    struct {
+        sk_sp<SkImageFilter> fFilter;
+        bool                 fExpectCanHandle;
+    } recs[] = {
+        { cfif,                                     true  },
+        { SkColorFilterImageFilter::Make(cf, cfif), true  },
+        { SkMergeImageFilter::Make(cfif, cfif),     true  },
+        { SkComposeImageFilter::Make(cfif, cfif),   true  },
+
+        { blif,                                     false },
+        { SkBlurImageFilter::Make(3, 3, cfif),      false },
+        { SkColorFilterImageFilter::Make(cf, blif), false },
+        { SkMergeImageFilter::Make(cfif, blif),     false },
+        { SkComposeImageFilter::Make(blif, cfif),   false },
+    };
+
+    for (const auto& rec : recs) {
+        const bool canHandle = rec.fFilter->canHandleComplexCTM();
+        REPORTER_ASSERT(reporter, canHandle == rec.fExpectCanHandle);
+    }
+}
+
+// Test that transforming the filter DAG doesn't clone shared nodes multiple times.
+DEF_TEST(ImageFilterColorSpaceDAG, reporter) {
+
+    // Helper for counting makeColorSpace() clones.
+    class TestFilter final : public SkImageFilter {
+    public:
+        TestFilter() : INHERITED(nullptr, 0, nullptr) {}
+
+#ifndef SK_IGNORE_TO_STRING
+        void toString(SkString*) const override {}
+#endif
+        Factory getFactory() const override { return nullptr; }
+
+        size_t cloneCount() const { return fCloneCount; }
+
+    protected:
+        sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* src, const Context&,
+                                            SkIPoint* offset) const override {
+            return nullptr;
+        }
+        sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override {
+            fCloneCount++;
+            return sk_ref_sp(const_cast<TestFilter*>(this));
+        }
+
+    private:
+        typedef SkImageFilter INHERITED;
+
+        mutable size_t fCloneCount = 0;
+    };
+
+    auto filter = sk_make_sp<TestFilter>();
+    REPORTER_ASSERT(reporter, filter->cloneCount() == 0u);
+
+    // Build a DAG referencing the filter twice.
+    auto complexFilter = SkMergeImageFilter::Make(filter, SkOffsetImageFilter::Make(1, 1, filter));
+    REPORTER_ASSERT(reporter, filter->cloneCount() == 0u);
+
+    auto xformer = SkColorSpaceXformer::Make(SkColorSpace::MakeSRGB());
+    auto xformedFilter = xformer->apply(complexFilter.get());
+
+    REPORTER_ASSERT(reporter, filter->cloneCount() == 1u);
+}
diff --git a/src/third_party/skia/tests/ImageFrom565Bitmap.cpp b/src/third_party/skia/tests/ImageFrom565Bitmap.cpp
new file mode 100644
index 0000000..53f65df
--- /dev/null
+++ b/src/third_party/skia/tests/ImageFrom565Bitmap.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkImageInfo.h"
+#include "SkImage.h"
+#include "Test.h"
+
+DEF_TEST(ImageFrom565Bitmap, r) {
+    SkBitmap bm;
+    bm.allocPixels(SkImageInfo::Make(
+        5, 7, kRGB_565_SkColorType, kOpaque_SkAlphaType));
+    bm.eraseColor(SK_ColorBLACK);
+    REPORTER_ASSERT(r, SkImage::MakeFromBitmap(bm) != nullptr);
+}
diff --git a/src/third_party/skia/tests/ImageGeneratorTest.cpp b/src/third_party/skia/tests/ImageGeneratorTest.cpp
index 1f960ea..5cfd410 100644
--- a/src/third_party/skia/tests/ImageGeneratorTest.cpp
+++ b/src/third_party/skia/tests/ImageGeneratorTest.cpp
@@ -5,30 +5,151 @@
  * found in the LICENSE file.
  */
 
+#include "SkData.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
 #include "SkImageGenerator.h"
 #include "Test.h"
 
+static bool gMyFactoryWasCalled;
+
+static std::unique_ptr<SkImageGenerator> my_factory(sk_sp<SkData>) {
+    gMyFactoryWasCalled = true;
+    return nullptr;
+}
+
+static void test_imagegenerator_factory(skiatest::Reporter* reporter) {
+    // just need a non-empty data to test things
+    sk_sp<SkData> data(SkData::MakeWithCString("test_imagegenerator_factory"));
+
+    gMyFactoryWasCalled = false;
+
+    REPORTER_ASSERT(reporter, !gMyFactoryWasCalled);
+
+    std::unique_ptr<SkImageGenerator> gen = SkImageGenerator::MakeFromEncoded(data);
+    REPORTER_ASSERT(reporter, nullptr == gen);
+    REPORTER_ASSERT(reporter, !gMyFactoryWasCalled);
+
+    // Test is racy, in that it hopes no other thread is changing this global...
+    auto prev = SkGraphics::SetImageGeneratorFromEncodedDataFactory(my_factory);
+    gen = SkImageGenerator::MakeFromEncoded(data);
+    REPORTER_ASSERT(reporter, nullptr == gen);
+    REPORTER_ASSERT(reporter, gMyFactoryWasCalled);
+    SkGraphics::SetImageGeneratorFromEncodedDataFactory(prev);
+}
+
+class MyImageGenerator : public SkImageGenerator {
+public:
+    MyImageGenerator() : SkImageGenerator(SkImageInfo::MakeN32Premul(0, 0)) {}
+};
+
 DEF_TEST(ImageGenerator, reporter) {
-    SkImageGenerator ig;
-    SkISize sizes[3];
-    sizes[0] = SkISize::Make(200, 200);
-    sizes[1] = SkISize::Make(100, 100);
-    sizes[2] = SkISize::Make( 50,  50);
-    void*   planes[3] = { NULL };
-    size_t  rowBytes[3] = { 0 };
+    MyImageGenerator ig;
+    SkYUVSizeInfo sizeInfo;
+    sizeInfo.fSizes[SkYUVSizeInfo::kY] = SkISize::Make(200, 200);
+    sizeInfo.fSizes[SkYUVSizeInfo::kU] = SkISize::Make(100, 100);
+    sizeInfo.fSizes[SkYUVSizeInfo::kV] = SkISize::Make( 50,  50);
+    sizeInfo.fWidthBytes[SkYUVSizeInfo::kY] = 0;
+    sizeInfo.fWidthBytes[SkYUVSizeInfo::kU] = 0;
+    sizeInfo.fWidthBytes[SkYUVSizeInfo::kV] = 0;
+    void* planes[3] = { nullptr };
     SkYUVColorSpace colorSpace;
 
     // Check that the YUV decoding API does not cause any crashes
-    ig.getYUV8Planes(sizes, NULL, NULL, &colorSpace);
-    ig.getYUV8Planes(sizes, NULL, NULL, NULL);
-    ig.getYUV8Planes(sizes, planes, NULL, NULL);
-    ig.getYUV8Planes(sizes, NULL, rowBytes, NULL);
-    ig.getYUV8Planes(sizes, planes, rowBytes, NULL);
-    ig.getYUV8Planes(sizes, planes, rowBytes, &colorSpace);
-
+    ig.queryYUV8(&sizeInfo, nullptr);
+    ig.queryYUV8(&sizeInfo, &colorSpace);
+    sizeInfo.fWidthBytes[SkYUVSizeInfo::kY] = 250;
+    sizeInfo.fWidthBytes[SkYUVSizeInfo::kU] = 250;
+    sizeInfo.fWidthBytes[SkYUVSizeInfo::kV] = 250;
     int dummy;
-    planes[0] = planes[1] = planes[2] = &dummy;
-    rowBytes[0] = rowBytes[1] = rowBytes[2] = 250;
+    planes[SkYUVSizeInfo::kY] = planes[SkYUVSizeInfo::kU] = planes[SkYUVSizeInfo::kV] = &dummy;
+    ig.getYUV8Planes(sizeInfo, planes);
 
-    ig.getYUV8Planes(sizes, planes, rowBytes, &colorSpace);
+    // Suppressed due to https://code.google.com/p/skia/issues/detail?id=4339
+    if (false) {
+        test_imagegenerator_factory(reporter);
+    }
+}
+
+#include "SkAutoMalloc.h"
+#include "SkPictureRecorder.h"
+
+static sk_sp<SkPicture> make_picture() {
+    SkPictureRecorder recorder;
+    recorder.beginRecording(100, 100)->drawColor(SK_ColorRED);
+    return recorder.finishRecordingAsPicture();
+}
+
+DEF_TEST(PictureImageGenerator, reporter) {
+    const struct {
+        SkColorType fColorType;
+        SkAlphaType fAlphaType;
+        bool        fExpectSuccess;
+    } recs[] = {
+        { kRGBA_8888_SkColorType, kPremul_SkAlphaType, kRGBA_8888_SkColorType == kN32_SkColorType },
+        { kBGRA_8888_SkColorType, kPremul_SkAlphaType, kBGRA_8888_SkColorType == kN32_SkColorType },
+        { kRGBA_F16_SkColorType,  kPremul_SkAlphaType, true },
+
+        { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType, false },
+        { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, false },
+        { kRGBA_F16_SkColorType,  kUnpremul_SkAlphaType, false },
+    };
+
+    auto colorspace = SkColorSpace::MakeSRGB();
+    auto picture = make_picture();
+    auto gen = SkImageGenerator::MakeFromPicture({100, 100}, picture, nullptr, nullptr,
+                                                 SkImage::BitDepth::kU8, colorspace);
+
+    // worst case for all requests
+    SkAutoMalloc storage(100 * 100 * SkColorTypeBytesPerPixel(kRGBA_F16_SkColorType));
+
+    for (const auto& rec : recs) {
+        SkImageInfo info = SkImageInfo::Make(100, 100, rec.fColorType, rec.fAlphaType, colorspace);
+        bool success = gen->getPixels(info, storage.get(), info.minRowBytes());
+        REPORTER_ASSERT(reporter, success == rec.fExpectSuccess);
+    }
+}
+
+#include "SkImagePriv.h"
+
+DEF_TEST(ColorXformGenerator, r) {
+    SkBitmap a, b, c, d, e;
+    SkImageInfo info = SkImageInfo::MakeS32(1, 1, kPremul_SkAlphaType);
+    a.allocPixels(info);
+    b.allocPixels(info.makeColorSpace(nullptr));
+    c.allocPixels(info.makeColorSpace(SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                                            SkColorSpace::kRec2020_Gamut)));
+    d.allocPixels(info.makeColorSpace(SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                                            SkColorSpace::kAdobeRGB_Gamut)));
+    e.allocPixels(info);
+    a.eraseColor(0);
+    b.eraseColor(1);
+    c.eraseColor(2);
+    d.eraseColor(3);
+    e.eraseColor(4);
+
+    sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
+    sk_sp<SkImage> ia = SkMakeImageInColorSpace(a, srgb, 0);
+    sk_sp<SkImage> ib = SkMakeImageInColorSpace(b, srgb, b.getGenerationID());
+    sk_sp<SkImage> ic = SkMakeImageInColorSpace(c, srgb, c.getGenerationID());
+    sk_sp<SkImage> id = SkMakeImageInColorSpace(d, srgb, 0);
+    sk_sp<SkImage> ie = SkMakeImageInColorSpace(e, srgb, e.getGenerationID(),
+                                                kAlways_SkCopyPixelsMode);
+
+    // Equal because sRGB->sRGB is a no-op.
+    REPORTER_ASSERT(r, ia->uniqueID() == a.getGenerationID());
+
+    // Equal because nullptr->sRGB is a no-op (nullptr is treated as sRGB), and because
+    // we pass the explicit id that we want.  In the no-op case, the implementation
+    // actually asserts that if we pass an id, it must match the id on the bitmap.
+    REPORTER_ASSERT(r, ib->uniqueID() == b.getGenerationID());
+
+    // Equal because we pass in an explicit id.
+    REPORTER_ASSERT(r, ic->uniqueID() == c.getGenerationID());
+
+    // Not equal because sRGB->Adobe is not a no-op and we do not pass an explicit id.
+    REPORTER_ASSERT(r, id->uniqueID() != d.getGenerationID());
+
+    // Equal because we pass in an explicit id. Forcing a copy, but still want the id respected.
+    REPORTER_ASSERT(r, ie->uniqueID() == e.getGenerationID());
 }
diff --git a/src/third_party/skia/tests/ImageIsOpaqueTest.cpp b/src/third_party/skia/tests/ImageIsOpaqueTest.cpp
index 3fe5b3d..bf3dea6 100644
--- a/src/third_party/skia/tests/ImageIsOpaqueTest.cpp
+++ b/src/third_party/skia/tests/ImageIsOpaqueTest.cpp
@@ -6,56 +6,134 @@
  */
 
 #include "SkTypes.h"
-#if SK_SUPPORT_GPU
-#include "GrContextFactory.h"
-#endif
-#include "SkImage.h"
-#include "SkSurface.h"
-
+#include "Resources.h"
 #include "Test.h"
 
-static void check_isopaque(skiatest::Reporter* reporter, SkSurface* surface, bool expectedOpaque) {
-    SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#endif
+#include "SkCanvas.h"
+#include "SkColorSpace_Base.h"
+#include "SkImage.h"
+#include "SkSurface.h"
+#include "SkReadBuffer.h"
+#include "SkWriteBuffer.h"
+
+static void test_flatten(skiatest::Reporter* reporter, const SkImageInfo& info) {
+    // Need a safe amount of 4-byte aligned storage.  Note that one of the test ICC profiles
+    // is ~7500 bytes.
+    const size_t storageBytes = 8000;
+    SkAutoTMalloc<uint32_t> storage(storageBytes / sizeof(uint32_t));
+    SkBinaryWriteBuffer wb(storage.get(), storageBytes);
+    info.flatten(wb);
+    SkASSERT(wb.bytesWritten() < storageBytes);
+
+    SkReadBuffer rb(storage.get(), wb.bytesWritten());
+
+    // pick a noisy byte pattern, so we ensure that unflatten sets all of our fields
+    SkImageInfo info2 = SkImageInfo::Make(0xB8, 0xB8, (SkColorType) 0xB8, (SkAlphaType) 0xB8);
+
+    info2.unflatten(rb);
+    REPORTER_ASSERT(reporter, rb.offset() == wb.bytesWritten());
+
+    REPORTER_ASSERT(reporter, info == info2);
+}
+
+DEF_TEST(ImageInfo_flattening, reporter) {
+     sk_sp<SkData> data =
+             SkData::MakeFromFileName(GetResourcePath("icc_profiles/HP_ZR30w.icc").c_str());
+    sk_sp<SkColorSpace> space0 = SkColorSpace::MakeICC(data->data(), data->size());
+    data = SkData::MakeFromFileName( GetResourcePath("icc_profiles/HP_Z32x.icc").c_str());
+    sk_sp<SkColorSpace> space1 = SkColorSpace::MakeICC(data->data(), data->size());
+    data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperLeft.icc").c_str());
+    sk_sp<SkColorSpace> space2 = SkColorSpace::MakeICC(data->data(), data->size());
+    data = SkData::MakeFromFileName(GetResourcePath("icc_profiles/upperRight.icc").c_str());
+    sk_sp<SkColorSpace> space3 = SkColorSpace::MakeICC(data->data(), data->size());
+
+    sk_sp<SkColorSpace> spaces[] = {
+        nullptr,
+        SkColorSpace::MakeSRGB(),
+        SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named),
+        space0,
+        space1,
+        space2,
+        space3,
+    };
+
+    for (int ct = 0; ct <= kLastEnum_SkColorType; ++ct) {
+        for (int at = 0; at <= kLastEnum_SkAlphaType; ++at) {
+            for (auto& cs : spaces) {
+                SkImageInfo info = SkImageInfo::Make(100, 200,
+                                                     static_cast<SkColorType>(ct),
+                                                     static_cast<SkAlphaType>(at),
+                                                     cs);
+                test_flatten(reporter, info);
+            }
+        }
+    }
+}
+
+static void check_isopaque(skiatest::Reporter* reporter, const sk_sp<SkSurface>& surface,
+                           bool expectedOpaque) {
+    sk_sp<SkImage> image(surface->makeImageSnapshot());
     REPORTER_ASSERT(reporter, image->isOpaque() == expectedOpaque);
 }
 
 DEF_TEST(ImageIsOpaqueTest, reporter) {
     SkImageInfo infoTransparent = SkImageInfo::MakeN32Premul(5, 5);
-    SkAutoTUnref<SkSurface> surfaceTransparent(SkSurface::NewRaster(infoTransparent));
+    auto surfaceTransparent(SkSurface::MakeRaster(infoTransparent));
     check_isopaque(reporter, surfaceTransparent, false);
 
     SkImageInfo infoOpaque = SkImageInfo::MakeN32(5, 5, kOpaque_SkAlphaType);
-    SkAutoTUnref<SkSurface> surfaceOpaque(SkSurface::NewRaster(infoOpaque));
+    auto surfaceOpaque(SkSurface::MakeRaster(infoOpaque));
     check_isopaque(reporter, surfaceOpaque, true);
 }
 
 #if SK_SUPPORT_GPU
 
-DEF_GPUTEST(ImageIsOpaqueTest_GPU, reporter, factory) {
-    for (int i = 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
-        GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageIsOpaqueTest_Gpu, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    SkImageInfo infoTransparent = SkImageInfo::MakeN32Premul(5, 5);
+    auto surfaceTransparent(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, infoTransparent));
+    check_isopaque(reporter, surfaceTransparent, false);
 
-        if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
-            continue;
-        }
+    SkImageInfo infoOpaque = SkImageInfo::MakeN32(5, 5, kOpaque_SkAlphaType);
+    auto surfaceOpaque(SkSurface::MakeRenderTarget(context,SkBudgeted::kNo, infoOpaque));
 
-        GrContext* context = factory->get(glCtxType);
-
-        if (NULL == context) {
-            continue;
-        }
-
-        SkImageInfo infoTransparent = SkImageInfo::MakeN32Premul(5, 5);
-        SkAutoTUnref<SkSurface> surfaceTransparent(SkSurface::NewRenderTarget(context, infoTransparent));
-        check_isopaque(reporter, surfaceTransparent, false);
-
-        SkImageInfo infoOpaque = SkImageInfo::MakeN32(5, 5, kOpaque_SkAlphaType);
-        SkAutoTUnref<SkSurface> surfaceOpaque(SkSurface::NewRenderTarget(context, infoOpaque));
-#if 0
-        // this is failing right now : TODO fix me
-        check_isopaque(reporter, surfaceOpaque, true);
-#endif
-    }
+    check_isopaque(reporter, surfaceOpaque, true);
 }
 
 #endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#include "SkPictureRecorder.h"
+
+static sk_sp<SkPicture> make_picture() {
+    SkPictureRecorder recorder;
+    SkCanvas* canvas = recorder.beginRecording({ 0, 0, 10, 10 });
+    canvas->drawColor(SK_ColorRED);
+    return recorder.finishRecordingAsPicture();
+}
+
+DEF_TEST(Image_isAlphaOnly, reporter) {
+    SkPMColor pmColors = 0;
+    SkPixmap pmap = {
+        SkImageInfo::MakeN32Premul(1, 1),
+        &pmColors,
+        sizeof(pmColors)
+    };
+    for (auto& image : {
+        SkImage::MakeRasterCopy(pmap),
+        GetResourceAsImage("mandrill_128.png"),
+        GetResourceAsImage("color_wheel.jpg"),
+        SkImage::MakeFromPicture(make_picture(), { 10, 10 }, nullptr, nullptr,
+                                 SkImage::BitDepth::kU8,
+                                 SkColorSpace::MakeSRGB()),
+    })
+    {
+        REPORTER_ASSERT(reporter, image->isAlphaOnly() == false);
+    }
+
+    REPORTER_ASSERT(reporter, SkImage::MakeRasterCopy({
+        SkImageInfo::MakeA8(1, 1), (uint8_t*)&pmColors, 1})->isAlphaOnly() == true);
+}
diff --git a/src/third_party/skia/tests/ImageNewShaderTest.cpp b/src/third_party/skia/tests/ImageNewShaderTest.cpp
index dfc5a24..fe01f04 100644
--- a/src/third_party/skia/tests/ImageNewShaderTest.cpp
+++ b/src/third_party/skia/tests/ImageNewShaderTest.cpp
@@ -5,26 +5,23 @@
  * found in the LICENSE file.
  */
 
-#include "SkTypes.h"
-#if SK_SUPPORT_GPU
-#include "GrContextFactory.h"
-#endif
 #include "SkCanvas.h"
 #include "SkImage.h"
 #include "SkShader.h"
 #include "SkSurface.h"
-
+#include "SkTypes.h"
 #include "Test.h"
 
-void testBitmapEquality(skiatest::Reporter* reporter, SkBitmap& bm1, SkBitmap& bm2) {
-    SkAutoLockPixels lockBm1(bm1);
-    SkAutoLockPixels lockBm2(bm2);
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#endif
 
+static void test_bitmap_equality(skiatest::Reporter* reporter, SkBitmap& bm1, SkBitmap& bm2) {
     REPORTER_ASSERT(reporter, bm1.getSize() == bm2.getSize());
     REPORTER_ASSERT(reporter, 0 == memcmp(bm1.getPixels(), bm2.getPixels(), bm1.getSize()));
 }
 
-void paintSource(SkSurface* sourceSurface) {
+static void paint_source(SkSurface* sourceSurface) {
     SkCanvas* sourceCanvas = sourceSurface->getCanvas();
     sourceCanvas->clear(0xFFDEDEDE);
 
@@ -41,13 +38,14 @@
     sourceCanvas->drawRect(rect, paintColor);
 }
 
-void runShaderTest(skiatest::Reporter* reporter, SkSurface* sourceSurface, SkSurface* destinationSurface, SkImageInfo& info) {
-    paintSource(sourceSurface);
+static void run_shader_test(skiatest::Reporter* reporter, SkSurface* sourceSurface,
+                            SkSurface* destinationSurface, SkImageInfo& info) {
+    paint_source(sourceSurface);
 
-    SkAutoTUnref<SkImage> sourceImage(sourceSurface->newImageSnapshot());
-    SkAutoTUnref<SkShader> sourceShader(sourceImage->newShader(
+    sk_sp<SkImage> sourceImage(sourceSurface->makeImageSnapshot());
+    sk_sp<SkShader> sourceShader = sourceImage->makeShader(
             SkShader::kRepeat_TileMode,
-            SkShader::kRepeat_TileMode));
+            SkShader::kRepeat_TileMode);
 
     SkPaint paint;
     paint.setShader(sourceShader);
@@ -56,27 +54,25 @@
     destinationCanvas->clear(SK_ColorTRANSPARENT);
     destinationCanvas->drawPaint(paint);
 
-    SkIRect rect = SkIRect::MakeWH(info.width(), info.height());
-
     SkBitmap bmOrig;
-    sourceSurface->getCanvas()->readPixels(rect, &bmOrig);
+    bmOrig.allocN32Pixels(info.width(), info.height());
+    sourceSurface->getCanvas()->readPixels(bmOrig, 0, 0);
 
 
     SkBitmap bm;
-    destinationCanvas->readPixels(rect, &bm);
+    bm.allocN32Pixels(info.width(), info.height());
+    destinationCanvas->readPixels(bm, 0, 0);
 
-    testBitmapEquality(reporter, bmOrig, bm);
-
-
+    test_bitmap_equality(reporter, bmOrig, bm);
 
     // Test with a translated shader
     SkMatrix matrix;
     matrix.setTranslate(SkIntToScalar(-1), SkIntToScalar(0));
 
-    SkAutoTUnref<SkShader> sourceShaderTranslated(sourceImage->newShader(
+    sk_sp<SkShader> sourceShaderTranslated = sourceImage->makeShader(
             SkShader::kRepeat_TileMode,
             SkShader::kRepeat_TileMode,
-            &matrix));
+            &matrix);
 
     destinationCanvas->clear(SK_ColorTRANSPARENT);
 
@@ -86,11 +82,11 @@
     destinationCanvas->drawPaint(paintTranslated);
 
     SkBitmap bmt;
-    destinationCanvas->readPixels(rect, &bmt);
+    bmt.allocN32Pixels(info.width(), info.height());
+    destinationCanvas->readPixels(bmt, 0, 0);
 
     //  Test correctness
     {
-        SkAutoLockPixels lockBm(bmt);
         for (int y = 0; y < info.height(); y++) {
             REPORTER_ASSERT(reporter, 0xFFFF0000 == bmt.getColor(0, y));
 
@@ -104,64 +100,50 @@
 DEF_TEST(ImageNewShader, reporter) {
     SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5);
 
-    SkAutoTUnref<SkSurface> sourceSurface(SkSurface::NewRaster(info));
-    SkAutoTUnref<SkSurface> destinationSurface(SkSurface::NewRaster(info));
+    auto sourceSurface(SkSurface::MakeRaster(info));
+    auto destinationSurface(SkSurface::MakeRaster(info));
 
-    runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info);
+    run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info);
 }
 
 #if SK_SUPPORT_GPU
 
-void gpuToGpu(skiatest::Reporter* reporter, GrContext* context) {
+static void gpu_to_gpu(skiatest::Reporter* reporter, GrContext* context) {
     SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5);
 
-    SkAutoTUnref<SkSurface> sourceSurface(SkSurface::NewRenderTarget(context, info));
-    SkAutoTUnref<SkSurface> destinationSurface(SkSurface::NewRenderTarget(context, info));
+    auto sourceSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
+    auto destinationSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
 
-    runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info);
+    run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info);
 }
 
-void gpuToRaster(skiatest::Reporter* reporter, GrContext* context) {
+static void gpu_to_raster(skiatest::Reporter* reporter, GrContext* context) {
     SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5);
 
-    SkAutoTUnref<SkSurface> sourceSurface(SkSurface::NewRenderTarget(context, info));
-    SkAutoTUnref<SkSurface> destinationSurface(SkSurface::NewRaster(info));
+    auto sourceSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
+    auto destinationSurface(SkSurface::MakeRaster(info));
 
-    runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info);
+    run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info);
 }
 
-void rasterToGpu(skiatest::Reporter* reporter, GrContext* context) {
+static void raster_to_gpu(skiatest::Reporter* reporter, GrContext* context) {
     SkImageInfo info = SkImageInfo::MakeN32Premul(5, 5);
 
-    SkAutoTUnref<SkSurface> sourceSurface(SkSurface::NewRaster(info));
-    SkAutoTUnref<SkSurface> destinationSurface(SkSurface::NewRenderTarget(context, info));
+    auto sourceSurface(SkSurface::MakeRaster(info));
+    auto destinationSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
 
-    runShaderTest(reporter, sourceSurface.get(), destinationSurface.get(), info);
+    run_shader_test(reporter, sourceSurface.get(), destinationSurface.get(), info);
 }
 
-DEF_GPUTEST(ImageNewShader_GPU, reporter, factory) {
-    for (int i = 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
-        GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageNewShader_GPU, reporter, ctxInfo) {
+    //  GPU -> GPU
+    gpu_to_gpu(reporter, ctxInfo.grContext());
 
-        if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
-            continue;
-        }
+    //  GPU -> RASTER
+    gpu_to_raster(reporter, ctxInfo.grContext());
 
-        GrContext* context = factory->get(glCtxType);
-
-        if (NULL == context) {
-            continue;
-        }
-
-        //  GPU -> GPU
-        gpuToGpu(reporter, context);
-
-        //  GPU -> RASTER
-        gpuToRaster(reporter, context);
-
-        //  RASTER -> GPU
-        rasterToGpu(reporter, context);
-    }
+    //  RASTER -> GPU
+    raster_to_gpu(reporter, ctxInfo.grContext());
 }
 
 #endif
diff --git a/src/third_party/skia/tests/ImageStorageTest.cpp b/src/third_party/skia/tests/ImageStorageTest.cpp
new file mode 100644
index 0000000..f55553c
--- /dev/null
+++ b/src/third_party/skia/tests/ImageStorageTest.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrClip.h"
+#include "GrFragmentProcessor.h"
+#include "GrRenderTargetContext.h"
+#include "GrTexture.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageStorageLoad, reporter, ctxInfo) {
+    class TestFP : public GrFragmentProcessor {
+    public:
+        static sk_sp<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
+                                               GrSLMemoryModel mm,
+                                               GrSLRestrict restrict) {
+            return sk_sp<GrFragmentProcessor>(new TestFP(std::move(proxy), mm, restrict));
+        }
+
+        const char* name() const override { return "Image Load Test FP"; }
+
+    private:
+        TestFP(sk_sp<GrTextureProxy> proxy, GrSLMemoryModel mm, GrSLRestrict restrict)
+                : INHERITED(kNone_OptimizationFlags)
+                , fImageStorageAccess(std::move(proxy), kRead_GrIOType, mm, restrict) {
+            this->initClassID<TestFP>();
+            this->addImageStorageAccess(&fImageStorageAccess);
+        }
+
+        void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
+
+        bool onIsEqual(const GrFragmentProcessor& that) const override { return true; }
+
+        GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
+            class GLSLProcessor : public GrGLSLFragmentProcessor {
+            public:
+                GLSLProcessor() = default;
+                void emitCode(EmitArgs& args) override {
+                    const TestFP& tfp = args.fFp.cast<TestFP>();
+                    GrGLSLFPFragmentBuilder* fb = args.fFragBuilder;
+                    SkString imageLoadStr;
+                    fb->codeAppend("highp vec2 coord = sk_FragCoord.xy;");
+                    fb->appendImageStorageLoad(&imageLoadStr, args.fImageStorages[0],
+                                               "ivec2(coord)");
+                    if (GrPixelConfigIsSint(tfp.fImageStorageAccess.peekTexture()->config())) {
+                        // Map the signed bytes so that when then get read back as unorm values they
+                        // will have their original bit pattern.
+                        fb->codeAppendf("highp ivec4 ivals = %s;", imageLoadStr.c_str());
+                        // NV gives a linker error for this:
+                        // fb->codeAppend("ivals +=
+                        //                "mix(ivec4(0), ivec4(256), lessThan(ivals, ivec4(0)));");
+                        fb->codeAppend("if (ivals.r < 0) { ivals.r += 256; }");
+                        fb->codeAppend("if (ivals.g < 0) { ivals.g += 256; }");
+                        fb->codeAppend("if (ivals.b < 0) { ivals.b += 256; }");
+                        fb->codeAppend("if (ivals.a < 0) { ivals.a += 256; }");
+                        fb->codeAppendf("%s = vec4(ivals)/255;", args.fOutputColor);
+                    } else {
+                        fb->codeAppendf("%s = %s;", args.fOutputColor, imageLoadStr.c_str());
+                    }
+                }
+            };
+            return new GLSLProcessor;
+        }
+
+        ImageStorageAccess fImageStorageAccess;
+        typedef GrFragmentProcessor INHERITED;
+    };
+
+    static constexpr int kS = 256;
+    GrContext* context = ctxInfo.grContext();
+    if (context->caps()->shaderCaps()->maxFragmentImageStorages() < 1) {
+        return;
+    }
+
+    std::unique_ptr<uint32_t[]> data(new uint32_t[kS * kS]);
+    for (int j = 0; j < kS; ++j) {
+        for (int i = 0; i < kS; ++i) {
+            data[i + kS * j] = GrColorPackRGBA(i, j, 0, 0);
+        }
+    }
+
+    std::unique_ptr<uint32_t[]> idata(new uint32_t[kS * kS]);
+    for (int j = 0; j < kS; ++j) {
+        for (int i = 0; i < kS; ++i) {
+            int8_t r = i - 128;
+            int8_t g = j - 128;
+            int8_t b = -128;
+            int8_t a = -128;
+            idata[i + kS * j] = ((uint8_t)a << 24) | ((uint8_t)b << 16) |
+                                ((uint8_t)g << 8)  |  (uint8_t)r;
+        }
+    }
+
+    // Currently image accesses always have "top left" semantics.
+    GrSurfaceDesc desc;
+    desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+    desc.fWidth = kS;
+    desc.fHeight = kS;
+    struct {
+        GrPixelConfig fConfig;
+        std::unique_ptr<uint32_t[]> fData;
+    } tests[] = {
+        {
+           kRGBA_8888_GrPixelConfig,
+           std::move(data)
+        },
+        {
+           kRGBA_8888_sint_GrPixelConfig,
+           std::move(idata)
+        },
+    };
+    for (const auto& test : tests) {
+        // This test should work with any memory model and with or without restrict
+        for (auto mm : {GrSLMemoryModel::kNone,
+                        GrSLMemoryModel::kCoherent,
+                        GrSLMemoryModel::kVolatile}) {
+            for (auto restrict : {GrSLRestrict::kNo, GrSLRestrict::kYes}) {
+                if (!context->caps()->canConfigBeImageStorage(test.fConfig)) {
+                    continue;
+                }
+                desc.fConfig = test.fConfig;
+                sk_sp<GrTextureProxy> imageStorageTexture =
+                    GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc,
+                                                 SkBudgeted::kYes, test.fData.get(), 0);
+
+                sk_sp<GrRenderTargetContext> rtContext =
+                    context->makeDeferredRenderTargetContext(SkBackingFit::kExact, kS, kS,
+                                                             kRGBA_8888_GrPixelConfig, nullptr);
+                GrPaint paint;
+                paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+                paint.addColorFragmentProcessor(TestFP::Make(imageStorageTexture, mm, restrict));
+                rtContext->drawPaint(GrNoClip(), std::move(paint), SkMatrix::I());
+                std::unique_ptr<uint32_t[]> readData(new uint32_t[kS * kS]);
+                SkImageInfo info = SkImageInfo::Make(kS, kS, kRGBA_8888_SkColorType,
+                                                     kPremul_SkAlphaType);
+                rtContext->readPixels(info, readData.get(), 0, 0, 0);
+                int failed = false;
+                for (int j = 0; j < kS && !failed; ++j) {
+                    for (int i = 0; i < kS && !failed; ++i) {
+                        uint32_t d = test.fData[j * kS + i];
+                        uint32_t rd = readData[j * kS + i];
+                        if (d != rd) {
+                            failed = true;
+                            ERRORF(reporter, "Expected 0x%08x, got 0x%08x at %d, %d.", d, rd, i, j);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+#endif
diff --git a/src/third_party/skia/tests/ImageTest.cpp b/src/third_party/skia/tests/ImageTest.cpp
new file mode 100644
index 0000000..69b2a22
--- /dev/null
+++ b/src/third_party/skia/tests/ImageTest.cpp
@@ -0,0 +1,1326 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <functional>
+#include <initializer_list>
+#include <vector>
+
+#include "SkAutoPixmapStorage.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColorSpacePriv.h"
+#include "SkData.h"
+#include "SkImageEncoder.h"
+#include "SkImageGenerator.h"
+#include "SkImage_Base.h"
+#include "SkImagePriv.h"
+#include "SkMakeUnique.h"
+#include "SkPicture.h"
+#include "SkPictureRecorder.h"
+#include "SkPixelSerializer.h"
+#include "SkRRect.h"
+#include "SkStream.h"
+#include "SkSurface.h"
+#include "SkUtils.h"
+#include "Test.h"
+
+#include "Resources.h"
+#include "sk_tool_utils.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContextPriv.h"
+#include "GrGpu.h"
+#include "GrResourceCache.h"
+#include "GrTest.h"
+#include "GrTexture.h"
+#endif
+
+using namespace sk_gpu_test;
+
+SkImageInfo read_pixels_info(SkImage* image) {
+    if (as_IB(image)->onImageInfo().colorSpace()) {
+        return SkImageInfo::MakeS32(image->width(), image->height(), image->alphaType());
+    }
+
+    return SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType());
+}
+
+static void assert_equal(skiatest::Reporter* reporter, SkImage* a, const SkIRect* subsetA,
+                         SkImage* b) {
+    const int widthA = subsetA ? subsetA->width() : a->width();
+    const int heightA = subsetA ? subsetA->height() : a->height();
+
+    REPORTER_ASSERT(reporter, widthA == b->width());
+    REPORTER_ASSERT(reporter, heightA == b->height());
+
+    // see https://bug.skia.org/3965
+    //REPORTER_ASSERT(reporter, a->isOpaque() == b->isOpaque());
+
+    SkAutoPixmapStorage pmapA, pmapB;
+    pmapA.alloc(read_pixels_info(a));
+    pmapB.alloc(read_pixels_info(b));
+
+    const int srcX = subsetA ? subsetA->x() : 0;
+    const int srcY = subsetA ? subsetA->y() : 0;
+
+    REPORTER_ASSERT(reporter, a->readPixels(pmapA, srcX, srcY));
+    REPORTER_ASSERT(reporter, b->readPixels(pmapB, 0, 0));
+
+    const size_t widthBytes = widthA * 4;
+    for (int y = 0; y < heightA; ++y) {
+        REPORTER_ASSERT(reporter, !memcmp(pmapA.addr32(0, y), pmapB.addr32(0, y), widthBytes));
+    }
+}
+static void draw_image_test_pattern(SkCanvas* canvas) {
+    canvas->clear(SK_ColorWHITE);
+    SkPaint paint;
+    paint.setColor(SK_ColorBLACK);
+    canvas->drawRect(SkRect::MakeXYWH(5, 5, 10, 10), paint);
+}
+static sk_sp<SkImage> create_image() {
+    const SkImageInfo info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
+    auto surface(SkSurface::MakeRaster(info));
+    draw_image_test_pattern(surface->getCanvas());
+    return surface->makeImageSnapshot();
+}
+static sk_sp<SkData> create_image_data(SkImageInfo* info) {
+    *info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
+    const size_t rowBytes = info->minRowBytes();
+    sk_sp<SkData> data(SkData::MakeUninitialized(rowBytes * info->height()));
+    {
+        SkBitmap bm;
+        bm.installPixels(*info, data->writable_data(), rowBytes);
+        SkCanvas canvas(bm);
+        draw_image_test_pattern(&canvas);
+    }
+    return data;
+}
+static sk_sp<SkImage> create_data_image() {
+    SkImageInfo info;
+    sk_sp<SkData> data(create_image_data(&info));
+    return SkImage::MakeRasterData(info, std::move(data), info.minRowBytes());
+}
+#if SK_SUPPORT_GPU // not gpu-specific but currently only used in GPU tests
+static sk_sp<SkImage> create_image_large(int maxTextureSize) {
+    const SkImageInfo info = SkImageInfo::MakeN32(maxTextureSize + 1, 32, kOpaque_SkAlphaType);
+    auto surface(SkSurface::MakeRaster(info));
+    surface->getCanvas()->clear(SK_ColorWHITE);
+    SkPaint paint;
+    paint.setColor(SK_ColorBLACK);
+    surface->getCanvas()->drawRect(SkRect::MakeXYWH(4000, 2, 28000, 30), paint);
+    return surface->makeImageSnapshot();
+}
+static sk_sp<SkImage> create_picture_image() {
+    SkPictureRecorder recorder;
+    SkCanvas* canvas = recorder.beginRecording(10, 10);
+    canvas->clear(SK_ColorCYAN);
+    return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(), SkISize::Make(10, 10),
+                                    nullptr, nullptr, SkImage::BitDepth::kU8,
+                                    SkColorSpace::MakeSRGB());
+};
+#endif
+// Want to ensure that our Release is called when the owning image is destroyed
+struct RasterDataHolder {
+    RasterDataHolder() : fReleaseCount(0) {}
+    sk_sp<SkData> fData;
+    int fReleaseCount;
+    static void Release(const void* pixels, void* context) {
+        RasterDataHolder* self = static_cast<RasterDataHolder*>(context);
+        self->fReleaseCount++;
+        self->fData.reset();
+    }
+};
+static sk_sp<SkImage> create_rasterproc_image(RasterDataHolder* dataHolder) {
+    SkASSERT(dataHolder);
+    SkImageInfo info;
+    dataHolder->fData = create_image_data(&info);
+    return SkImage::MakeFromRaster(SkPixmap(info, dataHolder->fData->data(), info.minRowBytes()),
+                                   RasterDataHolder::Release, dataHolder);
+}
+static sk_sp<SkImage> create_codec_image() {
+    SkImageInfo info;
+    sk_sp<SkData> data(create_image_data(&info));
+    SkBitmap bitmap;
+    bitmap.installPixels(info, data->writable_data(), info.minRowBytes());
+    sk_sp<SkData> src(sk_tool_utils::EncodeImageToData(bitmap, SkEncodedImageFormat::kPNG, 100));
+    return SkImage::MakeFromEncoded(std::move(src));
+}
+#if SK_SUPPORT_GPU
+static sk_sp<SkImage> create_gpu_image(GrContext* context) {
+    const SkImageInfo info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
+    auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
+    draw_image_test_pattern(surface->getCanvas());
+    return surface->makeImageSnapshot();
+}
+#endif
+
+static void test_encode(skiatest::Reporter* reporter, SkImage* image) {
+    const SkIRect ir = SkIRect::MakeXYWH(5, 5, 10, 10);
+    sk_sp<SkData> origEncoded = image->encodeToData();
+    REPORTER_ASSERT(reporter, origEncoded);
+    REPORTER_ASSERT(reporter, origEncoded->size() > 0);
+
+    sk_sp<SkImage> decoded(SkImage::MakeFromEncoded(origEncoded));
+    if (!decoded) {
+        ERRORF(reporter, "failed to decode image!");
+        return;
+    }
+    REPORTER_ASSERT(reporter, decoded);
+    assert_equal(reporter, image, nullptr, decoded.get());
+
+    // Now see if we can instantiate an image from a subset of the surface/origEncoded
+
+    decoded = SkImage::MakeFromEncoded(origEncoded, &ir);
+    REPORTER_ASSERT(reporter, decoded);
+    assert_equal(reporter, image, &ir, decoded.get());
+}
+
+DEF_TEST(ImageEncode, reporter) {
+    test_encode(reporter, create_image().get());
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageEncode_Gpu, reporter, ctxInfo) {
+    test_encode(reporter, create_gpu_image(ctxInfo.grContext()).get());
+}
+#endif
+
+DEF_TEST(Image_MakeFromRasterBitmap, reporter) {
+    const struct {
+        SkCopyPixelsMode fCPM;
+        bool            fExpectSameAsMutable;
+        bool            fExpectSameAsImmutable;
+    } recs[] = {
+        { kIfMutable_SkCopyPixelsMode,  false,  true },
+        { kAlways_SkCopyPixelsMode,     false,  false },
+        { kNever_SkCopyPixelsMode,      true,   true },
+    };
+    for (auto rec : recs) {
+        SkPixmap pm;
+        SkBitmap bm;
+        bm.allocN32Pixels(100, 100);
+
+        auto img = SkMakeImageFromRasterBitmap(bm, rec.fCPM);
+        REPORTER_ASSERT(reporter, img->peekPixels(&pm));
+        const bool sameMutable = pm.addr32(0, 0) == bm.getAddr32(0, 0);
+        REPORTER_ASSERT(reporter, rec.fExpectSameAsMutable == sameMutable);
+        REPORTER_ASSERT(reporter, (bm.getGenerationID() == img->uniqueID()) == sameMutable);
+
+        bm.notifyPixelsChanged();   // force a new generation ID
+
+        bm.setImmutable();
+        img = SkMakeImageFromRasterBitmap(bm, rec.fCPM);
+        REPORTER_ASSERT(reporter, img->peekPixels(&pm));
+        const bool sameImmutable = pm.addr32(0, 0) == bm.getAddr32(0, 0);
+        REPORTER_ASSERT(reporter, rec.fExpectSameAsImmutable == sameImmutable);
+        REPORTER_ASSERT(reporter, (bm.getGenerationID() == img->uniqueID()) == sameImmutable);
+    }
+}
+
+namespace {
+
+const char* kSerializedData = "serialized";
+
+class MockSerializer : public SkPixelSerializer {
+public:
+    MockSerializer(sk_sp<SkData> (*func)()) : fFunc(func), fDidEncode(false) { }
+
+    bool didEncode() const { return fDidEncode; }
+
+protected:
+    bool onUseEncodedData(const void*, size_t) override {
+        return false;
+    }
+
+    SkData* onEncode(const SkPixmap&) override {
+        fDidEncode = true;
+        return fFunc().release();
+    }
+
+private:
+    sk_sp<SkData> (*fFunc)();
+    bool fDidEncode;
+
+    typedef SkPixelSerializer INHERITED;
+};
+
+} // anonymous namespace
+
+// Test that SkImage encoding observes custom pixel serializers.
+DEF_TEST(Image_Encode_Serializer, reporter) {
+    MockSerializer serializer([]() -> sk_sp<SkData> {
+        return SkData::MakeWithCString(kSerializedData);
+    });
+    sk_sp<SkImage> image(create_image());
+    sk_sp<SkData> encoded = image->encodeToData(&serializer);
+    sk_sp<SkData> reference(SkData::MakeWithCString(kSerializedData));
+
+    REPORTER_ASSERT(reporter, serializer.didEncode());
+    REPORTER_ASSERT(reporter, encoded);
+    REPORTER_ASSERT(reporter, encoded->size() > 0);
+    REPORTER_ASSERT(reporter, encoded->equals(reference.get()));
+}
+
+// Test that image encoding failures do not break picture serialization/deserialization.
+DEF_TEST(Image_Serialize_Encoding_Failure, reporter) {
+    auto surface(SkSurface::MakeRasterN32Premul(100, 100));
+    surface->getCanvas()->clear(SK_ColorGREEN);
+    sk_sp<SkImage> image(surface->makeImageSnapshot());
+    REPORTER_ASSERT(reporter, image);
+
+    SkPictureRecorder recorder;
+    SkCanvas* canvas = recorder.beginRecording(100, 100);
+    canvas->drawImage(image, 0, 0);
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
+    REPORTER_ASSERT(reporter, picture);
+    REPORTER_ASSERT(reporter, picture->approximateOpCount() > 0);
+
+    MockSerializer emptySerializer([]() -> sk_sp<SkData> { return SkData::MakeEmpty(); });
+    MockSerializer nullSerializer([]() -> sk_sp<SkData> { return nullptr; });
+    MockSerializer* serializers[] = { &emptySerializer, &nullSerializer };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(serializers); ++i) {
+        SkDynamicMemoryWStream wstream;
+        REPORTER_ASSERT(reporter, !serializers[i]->didEncode());
+        picture->serialize(&wstream, serializers[i]);
+        REPORTER_ASSERT(reporter, serializers[i]->didEncode());
+
+        std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
+        sk_sp<SkPicture> deserialized(SkPicture::MakeFromStream(rstream.get()));
+        REPORTER_ASSERT(reporter, deserialized);
+        REPORTER_ASSERT(reporter, deserialized->approximateOpCount() > 0);
+    }
+}
+
+// Test that a draw that only partially covers the drawing surface isn't
+// interpreted as covering the entire drawing surface (i.e., exercise one of the
+// conditions of SkCanvas::wouldOverwriteEntireSurface()).
+DEF_TEST(Image_RetainSnapshot, reporter) {
+    const SkPMColor red   = SkPackARGB32(0xFF, 0xFF, 0, 0);
+    const SkPMColor green = SkPackARGB32(0xFF, 0, 0xFF, 0);
+    SkImageInfo info = SkImageInfo::MakeN32Premul(2, 2);
+    auto surface(SkSurface::MakeRaster(info));
+    surface->getCanvas()->clear(0xFF00FF00);
+
+    SkPMColor pixels[4];
+    memset(pixels, 0xFF, sizeof(pixels));   // init with values we don't expect
+    const SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(2, 2);
+    const size_t dstRowBytes = 2 * sizeof(SkPMColor);
+
+    sk_sp<SkImage> image1(surface->makeImageSnapshot());
+    REPORTER_ASSERT(reporter, image1->readPixels(dstInfo, pixels, dstRowBytes, 0, 0));
+    for (size_t i = 0; i < SK_ARRAY_COUNT(pixels); ++i) {
+        REPORTER_ASSERT(reporter, pixels[i] == green);
+    }
+
+    SkPaint paint;
+    paint.setBlendMode(SkBlendMode::kSrc);
+    paint.setColor(SK_ColorRED);
+
+    surface->getCanvas()->drawRect(SkRect::MakeXYWH(1, 1, 1, 1), paint);
+
+    sk_sp<SkImage> image2(surface->makeImageSnapshot());
+    REPORTER_ASSERT(reporter, image2->readPixels(dstInfo, pixels, dstRowBytes, 0, 0));
+    REPORTER_ASSERT(reporter, pixels[0] == green);
+    REPORTER_ASSERT(reporter, pixels[1] == green);
+    REPORTER_ASSERT(reporter, pixels[2] == green);
+    REPORTER_ASSERT(reporter, pixels[3] == red);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void make_bitmap_mutable(SkBitmap* bm) {
+    bm->allocN32Pixels(10, 10);
+}
+
+static void make_bitmap_immutable(SkBitmap* bm) {
+    bm->allocN32Pixels(10, 10);
+    bm->setImmutable();
+}
+
+DEF_TEST(image_newfrombitmap, reporter) {
+    const struct {
+        void (*fMakeProc)(SkBitmap*);
+        bool fExpectPeekSuccess;
+        bool fExpectSharedID;
+        bool fExpectLazy;
+    } rec[] = {
+        { make_bitmap_mutable,      true,   false, false },
+        { make_bitmap_immutable,    true,   true,  false },
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(rec); ++i) {
+        SkBitmap bm;
+        rec[i].fMakeProc(&bm);
+
+        sk_sp<SkImage> image(SkImage::MakeFromBitmap(bm));
+        SkPixmap pmap;
+
+        const bool sharedID = (image->uniqueID() == bm.getGenerationID());
+        REPORTER_ASSERT(reporter, sharedID == rec[i].fExpectSharedID);
+
+        const bool peekSuccess = image->peekPixels(&pmap);
+        REPORTER_ASSERT(reporter, peekSuccess == rec[i].fExpectPeekSuccess);
+
+        const bool lazy = image->isLazyGenerated();
+        REPORTER_ASSERT(reporter, lazy == rec[i].fExpectLazy);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#if SK_SUPPORT_GPU
+
+#include "SkBitmapCache.h"
+
+/*
+ *  This tests the caching (and preemptive purge) of the raster equivalent of a gpu-image.
+ *  We cache it for performance when drawing into a raster surface.
+ *
+ *  A cleaner test would know if each drawImage call triggered a read-back from the gpu,
+ *  but we don't have that facility (at the moment) so we use a little internal knowledge
+ *  of *how* the raster version is cached, and look for that.
+ */
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(c, reporter, ctxInfo) {
+    SkImageInfo info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
+    sk_sp<SkImage> image(create_gpu_image(ctxInfo.grContext()));
+    const uint32_t uniqueID = image->uniqueID();
+    const auto desc = SkBitmapCacheDesc::Make(image.get());
+
+    auto surface(SkSurface::MakeRaster(info));
+
+    // now we can test drawing a gpu-backed image into a cpu-backed surface
+
+    {
+        SkBitmap cachedBitmap;
+        REPORTER_ASSERT(reporter, !SkBitmapCache::Find(desc, &cachedBitmap));
+    }
+
+    surface->getCanvas()->drawImage(image, 0, 0);
+    {
+        SkBitmap cachedBitmap;
+        if (SkBitmapCache::Find(desc, &cachedBitmap)) {
+            REPORTER_ASSERT(reporter, cachedBitmap.getGenerationID() == uniqueID);
+            REPORTER_ASSERT(reporter, cachedBitmap.isImmutable());
+            REPORTER_ASSERT(reporter, cachedBitmap.getPixels());
+        } else {
+            // unexpected, but not really a bug, since the cache is global and this test may be
+            // run w/ other threads competing for its budget.
+            SkDebugf("SkImage_Gpu2Cpu : cachedBitmap was already purged\n");
+        }
+    }
+
+    image.reset(nullptr);
+    {
+        SkBitmap cachedBitmap;
+        REPORTER_ASSERT(reporter, !SkBitmapCache::Find(desc, &cachedBitmap));
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkImage_makeTextureImage, reporter, contextInfo) {
+    GrContext* context = contextInfo.grContext();
+    sk_gpu_test::TestContext* testContext = contextInfo.testContext();
+    GrContextFactory otherFactory;
+    ContextInfo otherContextInfo = otherFactory.getContextInfo(contextInfo.type());
+    testContext->makeCurrent();
+
+    std::function<sk_sp<SkImage>()> imageFactories[] = {
+        create_image,
+        create_codec_image,
+        create_data_image,
+        // Create an image from a picture.
+        create_picture_image,
+        // Create a texture image.
+        [context] { return create_gpu_image(context); },
+        // Create a texture image in a another GrContext.
+        [testContext, otherContextInfo] {
+            otherContextInfo.testContext()->makeCurrent();
+            sk_sp<SkImage> otherContextImage = create_gpu_image(otherContextInfo.grContext());
+            testContext->makeCurrent();
+            return otherContextImage;
+        }
+    };
+
+    sk_sp<SkColorSpace> dstColorSpaces[] ={
+        nullptr,
+        SkColorSpace::MakeSRGB(),
+    };
+
+    for (auto& dstColorSpace : dstColorSpaces) {
+        for (auto factory : imageFactories) {
+            sk_sp<SkImage> image(factory());
+            if (!image) {
+                ERRORF(reporter, "Error creating image.");
+                continue;
+            }
+
+            sk_sp<SkImage> texImage(image->makeTextureImage(context, dstColorSpace.get()));
+            if (!texImage) {
+                GrContext* imageContext = as_IB(image)->context();
+
+                // We expect to fail if image comes from a different GrContext.
+                if (!image->isTextureBacked() || imageContext == context) {
+                    ERRORF(reporter, "makeTextureImage failed.");
+                }
+                continue;
+            }
+            if (!texImage->isTextureBacked()) {
+                ERRORF(reporter, "makeTextureImage returned non-texture image.");
+                continue;
+            }
+            if (image->isTextureBacked()) {
+                GrSurfaceProxy* origProxy = as_IB(image)->peekProxy();
+                GrSurfaceProxy* copyProxy = as_IB(texImage)->peekProxy();
+
+                if (origProxy->underlyingUniqueID() != copyProxy->underlyingUniqueID()) {
+                    ERRORF(reporter, "makeTextureImage made unnecessary texture copy.");
+                }
+            }
+            if (image->width() != texImage->width() || image->height() != texImage->height()) {
+                ERRORF(reporter, "makeTextureImage changed the image size.");
+            }
+            if (image->alphaType() != texImage->alphaType()) {
+                ERRORF(reporter, "makeTextureImage changed image alpha type.");
+            }
+        }
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkImage_makeNonTextureImage, reporter, contextInfo) {
+    GrContext* context = contextInfo.grContext();
+
+    std::function<sk_sp<SkImage>()> imageFactories[] = {
+        create_image,
+        create_codec_image,
+        create_data_image,
+        create_picture_image,
+        [context] { return create_gpu_image(context); },
+    };
+    SkColorSpace* legacyColorSpace = nullptr;
+    for (auto factory : imageFactories) {
+        sk_sp<SkImage> image = factory();
+        if (!image->isTextureBacked()) {
+            REPORTER_ASSERT(reporter, image->makeNonTextureImage().get() == image.get());
+            if (!(image = image->makeTextureImage(context, legacyColorSpace))) {
+                continue;
+            }
+        }
+        auto rasterImage = image->makeNonTextureImage();
+        if (!rasterImage) {
+            ERRORF(reporter, "makeNonTextureImage failed for texture-backed image.");
+        }
+        REPORTER_ASSERT(reporter, !rasterImage->isTextureBacked());
+        assert_equal(reporter, image.get(), nullptr, rasterImage.get());
+    }
+}
+
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SkImage_drawAbandonedGpuImage, reporter, contextInfo) {
+    auto context = contextInfo.grContext();
+    auto image = create_gpu_image(context);
+    auto info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
+    auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
+    image->getTexture()->abandon();
+    surface->getCanvas()->drawImage(image, 0, 0);
+}
+
+#endif
+
+class EmptyGenerator : public SkImageGenerator {
+public:
+    EmptyGenerator() : SkImageGenerator(SkImageInfo::MakeN32Premul(0, 0)) {}
+};
+
+DEF_TEST(ImageEmpty, reporter) {
+    const SkImageInfo info = SkImageInfo::Make(0, 0, kN32_SkColorType, kPremul_SkAlphaType);
+    SkPixmap pmap(info, nullptr, 0);
+    REPORTER_ASSERT(reporter, nullptr == SkImage::MakeRasterCopy(pmap));
+    REPORTER_ASSERT(reporter, nullptr == SkImage::MakeRasterData(info, nullptr, 0));
+    REPORTER_ASSERT(reporter, nullptr == SkImage::MakeFromRaster(pmap, nullptr, nullptr));
+    REPORTER_ASSERT(reporter, nullptr == SkImage::MakeFromGenerator(
+                                                            skstd::make_unique<EmptyGenerator>()));
+}
+
+DEF_TEST(ImageDataRef, reporter) {
+    SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
+    size_t rowBytes = info.minRowBytes();
+    size_t size = info.getSafeSize(rowBytes);
+    sk_sp<SkData> data = SkData::MakeUninitialized(size);
+    REPORTER_ASSERT(reporter, data->unique());
+    sk_sp<SkImage> image = SkImage::MakeRasterData(info, data, rowBytes);
+    REPORTER_ASSERT(reporter, !data->unique());
+    image.reset();
+    REPORTER_ASSERT(reporter, data->unique());
+}
+
+static bool has_pixels(const SkPMColor pixels[], int count, SkPMColor expected) {
+    for (int i = 0; i < count; ++i) {
+        if (pixels[i] != expected) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static void image_test_read_pixels(skiatest::Reporter* reporter, SkImage* image) {
+    if (!image) {
+        ERRORF(reporter, "Failed to create image!");
+        return;
+    }
+    const SkPMColor expected = SkPreMultiplyColor(SK_ColorWHITE);
+    const SkPMColor notExpected = ~expected;
+
+    const int w = 2, h = 2;
+    const size_t rowBytes = w * sizeof(SkPMColor);
+    SkPMColor pixels[w*h];
+
+    SkImageInfo info;
+
+    info = SkImageInfo::MakeUnknown(w, h);
+    REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, 0));
+
+    // out-of-bounds should fail
+    info = SkImageInfo::MakeN32Premul(w, h);
+    REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, -w, 0));
+    REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, -h));
+    REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, image->width(), 0));
+    REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, image->height()));
+
+    // top-left should succeed
+    sk_memset32(pixels, notExpected, w*h);
+    REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, 0, 0));
+    REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected));
+
+    // bottom-right should succeed
+    sk_memset32(pixels, notExpected, w*h);
+    REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes,
+                                                image->width() - w, image->height() - h));
+    REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected));
+
+    // partial top-left should succeed
+    sk_memset32(pixels, notExpected, w*h);
+    REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, -1, -1));
+    REPORTER_ASSERT(reporter, pixels[3] == expected);
+    REPORTER_ASSERT(reporter, has_pixels(pixels, w*h - 1, notExpected));
+
+    // partial bottom-right should succeed
+    sk_memset32(pixels, notExpected, w*h);
+    REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes,
+                                                image->width() - 1, image->height() - 1));
+    REPORTER_ASSERT(reporter, pixels[0] == expected);
+    REPORTER_ASSERT(reporter, has_pixels(&pixels[1], w*h - 1, notExpected));
+}
+DEF_TEST(ImageReadPixels, reporter) {
+    sk_sp<SkImage> image(create_image());
+    image_test_read_pixels(reporter, image.get());
+
+    image = create_data_image();
+    image_test_read_pixels(reporter, image.get());
+
+    RasterDataHolder dataHolder;
+    image = create_rasterproc_image(&dataHolder);
+    image_test_read_pixels(reporter, image.get());
+    image.reset();
+    REPORTER_ASSERT(reporter, 1 == dataHolder.fReleaseCount);
+
+    image = create_codec_image();
+    image_test_read_pixels(reporter, image.get());
+}
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageReadPixels_Gpu, reporter, ctxInfo) {
+    image_test_read_pixels(reporter, create_gpu_image(ctxInfo.grContext()).get());
+}
+#endif
+
+static void check_legacy_bitmap(skiatest::Reporter* reporter, const SkImage* image,
+                                const SkBitmap& bitmap, SkImage::LegacyBitmapMode mode) {
+    REPORTER_ASSERT(reporter, image->width() == bitmap.width());
+    REPORTER_ASSERT(reporter, image->height() == bitmap.height());
+    REPORTER_ASSERT(reporter, image->alphaType() == bitmap.alphaType());
+
+    if (SkImage::kRO_LegacyBitmapMode == mode) {
+        REPORTER_ASSERT(reporter, bitmap.isImmutable());
+    }
+
+    REPORTER_ASSERT(reporter, bitmap.getPixels());
+
+    const SkImageInfo info = SkImageInfo::MakeN32(1, 1, bitmap.alphaType());
+    SkPMColor imageColor;
+    REPORTER_ASSERT(reporter, image->readPixels(info, &imageColor, sizeof(SkPMColor), 0, 0));
+    REPORTER_ASSERT(reporter, imageColor == *bitmap.getAddr32(0, 0));
+}
+
+static void test_legacy_bitmap(skiatest::Reporter* reporter, const SkImage* image, SkImage::LegacyBitmapMode mode) {
+    if (!image) {
+        ERRORF(reporter, "Failed to create image.");
+        return;
+    }
+    SkBitmap bitmap;
+    REPORTER_ASSERT(reporter, image->asLegacyBitmap(&bitmap, mode));
+    check_legacy_bitmap(reporter, image, bitmap, mode);
+
+    // Test subsetting to exercise the rowBytes logic.
+    SkBitmap tmp;
+    REPORTER_ASSERT(reporter, bitmap.extractSubset(&tmp, SkIRect::MakeWH(image->width() / 2,
+                                                                         image->height() / 2)));
+    sk_sp<SkImage> subsetImage(SkImage::MakeFromBitmap(tmp));
+    REPORTER_ASSERT(reporter, subsetImage.get());
+
+    SkBitmap subsetBitmap;
+    REPORTER_ASSERT(reporter, subsetImage->asLegacyBitmap(&subsetBitmap, mode));
+    check_legacy_bitmap(reporter, subsetImage.get(), subsetBitmap, mode);
+}
+DEF_TEST(ImageLegacyBitmap, reporter) {
+    const SkImage::LegacyBitmapMode modes[] = {
+        SkImage::kRO_LegacyBitmapMode,
+        SkImage::kRW_LegacyBitmapMode,
+    };
+    for (auto& mode : modes) {
+        sk_sp<SkImage> image(create_image());
+        test_legacy_bitmap(reporter, image.get(), mode);
+
+        image = create_data_image();
+        test_legacy_bitmap(reporter, image.get(), mode);
+
+        RasterDataHolder dataHolder;
+        image = create_rasterproc_image(&dataHolder);
+        test_legacy_bitmap(reporter, image.get(), mode);
+        image.reset();
+        REPORTER_ASSERT(reporter, 1 == dataHolder.fReleaseCount);
+
+        image = create_codec_image();
+        test_legacy_bitmap(reporter, image.get(), mode);
+    }
+}
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageLegacyBitmap_Gpu, reporter, ctxInfo) {
+    const SkImage::LegacyBitmapMode modes[] = {
+        SkImage::kRO_LegacyBitmapMode,
+        SkImage::kRW_LegacyBitmapMode,
+    };
+    for (auto& mode : modes) {
+        sk_sp<SkImage> image(create_gpu_image(ctxInfo.grContext()));
+        test_legacy_bitmap(reporter, image.get(), mode);
+    }
+}
+#endif
+
+static void test_peek(skiatest::Reporter* reporter, SkImage* image, bool expectPeekSuccess) {
+    if (!image) {
+        ERRORF(reporter, "Failed to create image!");
+        return;
+    }
+    SkPixmap pm;
+    bool success = image->peekPixels(&pm);
+    REPORTER_ASSERT(reporter, expectPeekSuccess == success);
+    if (success) {
+        const SkImageInfo& info = pm.info();
+        REPORTER_ASSERT(reporter, 20 == info.width());
+        REPORTER_ASSERT(reporter, 20 == info.height());
+        REPORTER_ASSERT(reporter, kN32_SkColorType == info.colorType());
+        REPORTER_ASSERT(reporter, kPremul_SkAlphaType == info.alphaType() ||
+                        kOpaque_SkAlphaType == info.alphaType());
+        REPORTER_ASSERT(reporter, info.minRowBytes() <= pm.rowBytes());
+        REPORTER_ASSERT(reporter, SkPreMultiplyColor(SK_ColorWHITE) == *pm.addr32(0, 0));
+    }
+}
+DEF_TEST(ImagePeek, reporter) {
+    sk_sp<SkImage> image(create_image());
+    test_peek(reporter, image.get(), true);
+
+    image = create_data_image();
+    test_peek(reporter, image.get(), true);
+
+    RasterDataHolder dataHolder;
+    image = create_rasterproc_image(&dataHolder);
+    test_peek(reporter, image.get(), true);
+    image.reset();
+    REPORTER_ASSERT(reporter, 1 == dataHolder.fReleaseCount);
+
+    image = create_codec_image();
+    test_peek(reporter, image.get(), false);
+}
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImagePeek_Gpu, reporter, ctxInfo) {
+    sk_sp<SkImage> image(create_gpu_image(ctxInfo.grContext()));
+    test_peek(reporter, image.get(), false);
+}
+#endif
+
+#if SK_SUPPORT_GPU
+struct TextureReleaseChecker {
+    TextureReleaseChecker() : fReleaseCount(0) {}
+    int fReleaseCount;
+    static void Release(void* self) {
+        static_cast<TextureReleaseChecker*>(self)->fReleaseCount++;
+    }
+};
+
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SkImage_NewFromTextureRelease, reporter, ctxInfo) {
+    const int kWidth = 10;
+    const int kHeight = 10;
+    std::unique_ptr<uint32_t[]> pixels(new uint32_t[kWidth * kHeight]);
+
+    GrContext* ctx = ctxInfo.grContext();
+
+    GrBackendObject backendTexHandle =
+            ctxInfo.grContext()->getGpu()->createTestingOnlyBackendTexture(
+                    pixels.get(), kWidth, kHeight, kRGBA_8888_GrPixelConfig, true);
+
+    GrBackendTexture backendTex = GrTest::CreateBackendTexture(ctx->contextPriv().getBackend(),
+                                                               kWidth,
+                                                               kHeight,
+                                                               kRGBA_8888_GrPixelConfig,
+                                                               backendTexHandle);
+
+    TextureReleaseChecker releaseChecker;
+    GrSurfaceOrigin texOrigin = kBottomLeft_GrSurfaceOrigin;
+    sk_sp<SkImage> refImg(
+        SkImage::MakeFromTexture(ctx, backendTex, texOrigin, kPremul_SkAlphaType, nullptr,
+                                 TextureReleaseChecker::Release, &releaseChecker));
+
+    GrSurfaceOrigin readBackOrigin;
+    GrBackendObject readBackHandle = refImg->getTextureHandle(false, &readBackOrigin);
+    // TODO: Make it so we can check this (see skbug.com/5019)
+#if 0
+    if (*readBackHandle != *(backendTexHandle)) {
+        ERRORF(reporter, "backend mismatch %d %d\n",
+                       (int)readBackHandle, (int)backendTexHandle);
+    }
+    REPORTER_ASSERT(reporter, readBackHandle == backendTexHandle);
+#else
+    REPORTER_ASSERT(reporter, SkToBool(readBackHandle));
+#endif
+    if (readBackOrigin != texOrigin) {
+        ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, texOrigin);
+    }
+    REPORTER_ASSERT(reporter, readBackOrigin == texOrigin);
+
+    // Now exercise the release proc
+    REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
+    refImg.reset(nullptr); // force a release of the image
+    REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
+
+    ctxInfo.grContext()->getGpu()->deleteTestingOnlyBackendTexture(backendTexHandle);
+}
+
+DEF_GPUTEST(SkImage_MakeCrossContextRelease, reporter, /*factory*/) {
+    GrContextFactory testFactory;
+
+    sk_sp<SkData> data = GetResourceAsData("mandrill_128.png");
+    SkASSERT(data.get());
+
+    for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
+        GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i);
+        ContextInfo ctxInfo = testFactory.getContextInfo(ctxType);
+        GrContext* ctx = ctxInfo.grContext();
+        if (!ctx) {
+            continue;
+        }
+
+        // If we don't have proper support for this feature, the factory will fallback to returning
+        // codec-backed images. Those will "work", but some of our checks will fail because we
+        // expect the cross-context images not to work on multiple contexts at once.
+        if (!ctx->caps()->crossContextTextureSupport()) {
+            continue;
+        }
+
+        // We test three lifetime patterns for a single context:
+        // 1) Create image, free image
+        // 2) Create image, draw, flush, free image
+        // 3) Create image, draw, free image, flush
+        // ... and then repeat the last two patterns with drawing on a second* context:
+        // 4) Create image, draw*, flush*, free image
+        // 5) Create image, draw*, free iamge, flush*
+
+        // Case #1: Create image, free image
+        {
+            sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+            refImg.reset(nullptr); // force a release of the image
+        }
+
+        SkImageInfo info = SkImageInfo::MakeN32Premul(128, 128);
+        sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info);
+        SkCanvas* canvas = surface->getCanvas();
+
+        // Case #2: Create image, draw, flush, free image
+        {
+            sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+
+            canvas->drawImage(refImg, 0, 0);
+            canvas->flush();
+
+            refImg.reset(nullptr); // force a release of the image
+        }
+
+        // Case #3: Create image, draw, free image, flush
+        {
+            sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+
+            canvas->drawImage(refImg, 0, 0);
+            refImg.reset(nullptr); // force a release of the image
+
+            canvas->flush();
+        }
+
+        // Configure second context
+        sk_gpu_test::TestContext* testContext = ctxInfo.testContext();
+
+        ContextInfo otherContextInfo = testFactory.getSharedContextInfo(ctx);
+        GrContext* otherCtx = otherContextInfo.grContext();
+        sk_gpu_test::TestContext* otherTestContext = otherContextInfo.testContext();
+
+        // Creating a context in a share group may fail
+        if (!otherCtx) {
+            continue;
+        }
+
+        surface = SkSurface::MakeRenderTarget(otherCtx, SkBudgeted::kNo, info);
+        canvas = surface->getCanvas();
+
+        // Case #4: Create image, draw*, flush*, free image
+        {
+            testContext->makeCurrent();
+            sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+
+            otherTestContext->makeCurrent();
+            canvas->drawImage(refImg, 0, 0);
+            canvas->flush();
+
+            testContext->makeCurrent();
+            refImg.reset(nullptr); // force a release of the image
+        }
+
+        // Case #5: Create image, draw*, free image, flush*
+        {
+            testContext->makeCurrent();
+            sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+
+            otherTestContext->makeCurrent();
+            canvas->drawImage(refImg, 0, 0);
+
+            testContext->makeCurrent();
+            refImg.reset(nullptr); // force a release of the image
+
+            otherTestContext->makeCurrent();
+            canvas->flush();
+        }
+
+        // Case #6: Verify that only one context can be using the image at a time
+        {
+            testContext->makeCurrent();
+            sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+
+            // Any context should be able to borrow the texture at this point
+            sk_sp<SkColorSpace> texColorSpace;
+            sk_sp<GrTextureProxy> proxy = as_IB(refImg)->asTextureProxyRef(
+                ctx, GrSamplerParams::ClampNoFilter(), nullptr, &texColorSpace, nullptr);
+            REPORTER_ASSERT(reporter, proxy);
+
+            // But once it's borrowed, no other context should be able to borrow
+            otherTestContext->makeCurrent();
+            sk_sp<GrTextureProxy> otherProxy = as_IB(refImg)->asTextureProxyRef(
+                otherCtx, GrSamplerParams::ClampNoFilter(), nullptr, &texColorSpace, nullptr);
+            REPORTER_ASSERT(reporter, !otherProxy);
+
+            // Original context (that's already borrowing) should be okay
+            testContext->makeCurrent();
+            sk_sp<GrTextureProxy> proxySecondRef = as_IB(refImg)->asTextureProxyRef(
+                ctx, GrSamplerParams::ClampNoFilter(), nullptr, &texColorSpace, nullptr);
+            REPORTER_ASSERT(reporter, proxySecondRef);
+
+            // Releae all refs from the original context
+            proxy.reset(nullptr);
+            proxySecondRef.reset(nullptr);
+
+            // Now we should be able to borrow the texture from the other context
+            otherTestContext->makeCurrent();
+            otherProxy = as_IB(refImg)->asTextureProxyRef(
+                otherCtx, GrSamplerParams::ClampNoFilter(), nullptr, &texColorSpace, nullptr);
+            REPORTER_ASSERT(reporter, otherProxy);
+
+            // Release everything
+            otherProxy.reset(nullptr);
+            refImg.reset(nullptr);
+        }
+    }
+}
+
+static void check_images_same(skiatest::Reporter* reporter, const SkImage* a, const SkImage* b) {
+    if (a->width() != b->width() || a->height() != b->height()) {
+        ERRORF(reporter, "Images must have the same size");
+        return;
+    }
+    if (a->alphaType() != b->alphaType()) {
+        ERRORF(reporter, "Images must have the same alpha type");
+        return;
+    }
+
+    SkImageInfo info = SkImageInfo::MakeN32Premul(a->width(), a->height());
+    SkAutoPixmapStorage apm;
+    SkAutoPixmapStorage bpm;
+
+    apm.alloc(info);
+    bpm.alloc(info);
+
+    if (!a->readPixels(apm, 0, 0)) {
+        ERRORF(reporter, "Could not read image a's pixels");
+        return;
+    }
+    if (!b->readPixels(bpm, 0, 0)) {
+        ERRORF(reporter, "Could not read image b's pixels");
+        return;
+    }
+
+    for (auto y = 0; y < info.height(); ++y) {
+        for (auto x = 0; x < info.width(); ++x) {
+            uint32_t pixelA = *apm.addr32(x, y);
+            uint32_t pixelB = *bpm.addr32(x, y);
+            if (pixelA != pixelB) {
+                ERRORF(reporter, "Expected image pixels to be the same. At %d,%d 0x%08x != 0x%08x",
+                       x, y, pixelA, pixelB);
+                return;
+            }
+        }
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredTextureImage, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    sk_gpu_test::TestContext* testContext = ctxInfo.testContext();
+    sk_sp<GrContextThreadSafeProxy> proxy = context->threadSafeProxy();
+
+    GrContextFactory otherFactory;
+    ContextInfo otherContextInfo = otherFactory.getContextInfo(ctxInfo.type());
+
+    testContext->makeCurrent();
+    REPORTER_ASSERT(reporter, proxy);
+    auto createLarge = [context] {
+        return create_image_large(context->caps()->maxTextureSize());
+    };
+    struct {
+        std::function<sk_sp<SkImage> ()>                      fImageFactory;
+        std::vector<SkImage::DeferredTextureImageUsageParams> fParams;
+        sk_sp<SkColorSpace>                                   fColorSpace;
+        SkColorType                                           fColorType;
+        SkFilterQuality                                       fExpectedQuality;
+        int                                                   fExpectedScaleFactor;
+        bool                                                  fExpectation;
+    } testCases[] = {
+        { create_image,          {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+          nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true },
+        { create_codec_image,    {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+          nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true },
+        { create_data_image,     {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+          nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true },
+        { create_picture_image,  {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+          nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
+        { [context] { return create_gpu_image(context); },
+          {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+          nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
+        // Create a texture image in a another GrContext.
+        { [testContext, otherContextInfo] {
+            otherContextInfo.testContext()->makeCurrent();
+            sk_sp<SkImage> otherContextImage = create_gpu_image(otherContextInfo.grContext());
+            testContext->makeCurrent();
+            return otherContextImage;
+          }, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+          nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
+        // Create an image that is too large to upload.
+        { createLarge, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+          nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
+        // Create an image that is too large, but is scaled to an acceptable size.
+        { createLarge, {{SkMatrix::I(), kMedium_SkFilterQuality, 4}},
+          nullptr, kN32_SkColorType, kMedium_SkFilterQuality, 16, true},
+        // Create an image with multiple low filter qualities, make sure we round up.
+        { createLarge, {{SkMatrix::I(), kNone_SkFilterQuality, 4},
+                        {SkMatrix::I(), kMedium_SkFilterQuality, 4}},
+          nullptr, kN32_SkColorType, kMedium_SkFilterQuality, 16, true},
+        // Create an image with multiple prescale levels, make sure we chose the minimum scale.
+        { createLarge, {{SkMatrix::I(), kMedium_SkFilterQuality, 5},
+                        {SkMatrix::I(), kMedium_SkFilterQuality, 4}},
+          nullptr, kN32_SkColorType, kMedium_SkFilterQuality, 16, true},
+        // Create a images which are decoded to a 4444 backing.
+        { create_image,       {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+          nullptr, kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, true },
+        { create_codec_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+          nullptr, kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, true },
+        { create_data_image,  {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+          nullptr, kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, true },
+        // Valid SkColorSpace and SkColorType.
+        { create_data_image,  {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+          SkColorSpace::MakeSRGB(), kN32_SkColorType, kNone_SkFilterQuality, 1, true },
+        // Invalid SkColorSpace and SkColorType.
+        { create_data_image,  {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+          SkColorSpace::MakeSRGB(), kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, false },
+    };
+
+
+    for (auto testCase : testCases) {
+        sk_sp<SkImage> image(testCase.fImageFactory());
+        if (!image) {
+            ERRORF(reporter, "Failed to create image!");
+            continue;
+        }
+
+        size_t size = image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
+                                                         static_cast<int>(testCase.fParams.size()),
+                                                         nullptr, testCase.fColorSpace.get(),
+                                                         testCase.fColorType);
+        static const char *const kFS[] = { "fail", "succeed" };
+        if (SkToBool(size) != testCase.fExpectation) {
+            ERRORF(reporter,  "This image was expected to %s but did not.",
+                   kFS[testCase.fExpectation]);
+        }
+        if (size) {
+            void* buffer = sk_malloc_throw(size);
+            void* misaligned = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(buffer) + 3);
+            if (image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
+                                                   static_cast<int>(testCase.fParams.size()),
+                                                   misaligned, testCase.fColorSpace.get(),
+                                                   testCase.fColorType)) {
+                ERRORF(reporter, "Should fail when buffer is misaligned.");
+            }
+            if (!image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
+                                                    static_cast<int>(testCase.fParams.size()),
+                                                    buffer, testCase.fColorSpace.get(),
+                                                   testCase.fColorType)) {
+                ERRORF(reporter, "deferred image size succeeded but creation failed.");
+            } else {
+                for (auto budgeted : { SkBudgeted::kNo, SkBudgeted::kYes }) {
+                    sk_sp<SkImage> newImage(
+                        SkImage::MakeFromDeferredTextureImageData(context, buffer, budgeted));
+                    REPORTER_ASSERT(reporter, newImage != nullptr);
+                    if (newImage) {
+                        // Scale the image in software for comparison.
+                        SkImageInfo scaled_info = SkImageInfo::MakeN32(
+                                                    image->width() / testCase.fExpectedScaleFactor,
+                                                    image->height() / testCase.fExpectedScaleFactor,
+                                                    image->alphaType());
+                        SkAutoPixmapStorage scaled;
+                        scaled.alloc(scaled_info);
+                        image->scalePixels(scaled, testCase.fExpectedQuality);
+                        sk_sp<SkImage> scaledImage = SkImage::MakeRasterCopy(scaled);
+                        check_images_same(reporter, scaledImage.get(), newImage.get());
+                    }
+                    // The other context should not be able to create images from texture data
+                    // created by the original context.
+                    sk_sp<SkImage> newImage2(SkImage::MakeFromDeferredTextureImageData(
+                        otherContextInfo.grContext(), buffer, budgeted));
+                    REPORTER_ASSERT(reporter, !newImage2);
+                    testContext->makeCurrent();
+                }
+            }
+            sk_free(buffer);
+        }
+    }
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static sk_sp<SkImage> create_picture_image(sk_sp<SkColorSpace> space) {
+    SkPictureRecorder recorder;
+    SkCanvas* canvas = recorder.beginRecording(10, 10);
+    canvas->clear(SK_ColorCYAN);
+    return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(), SkISize::Make(10, 10),
+                                    nullptr, nullptr, SkImage::BitDepth::kU8, std::move(space));
+};
+
+static inline bool almost_equal(int a, int b) {
+    return SkTAbs(a - b) <= 1;
+}
+
+DEF_TEST(Image_ColorSpace, r) {
+    sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
+    sk_sp<SkImage> image = GetResourceAsImage("mandrill_512_q075.jpg");
+    REPORTER_ASSERT(r, srgb.get() == image->colorSpace());
+
+    image = GetResourceAsImage("webp-color-profile-lossy.webp");
+    SkColorSpaceTransferFn fn;
+    bool success = image->colorSpace()->isNumericalTransferFn(&fn);
+    REPORTER_ASSERT(r, success);
+    REPORTER_ASSERT(r, color_space_almost_equal(1.8f, fn.fG));
+
+    sk_sp<SkColorSpace> rec2020 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                                        SkColorSpace::kRec2020_Gamut);
+    image = create_picture_image(rec2020);
+    REPORTER_ASSERT(r, SkColorSpace::Equals(rec2020.get(), image->colorSpace()));
+
+    SkBitmap bitmap;
+    SkImageInfo info = SkImageInfo::MakeN32(10, 10, kPremul_SkAlphaType, rec2020);
+    bitmap.allocPixels(info);
+    image = SkImage::MakeFromBitmap(bitmap);
+    REPORTER_ASSERT(r, SkColorSpace::Equals(rec2020.get(), image->colorSpace()));
+
+    sk_sp<SkSurface> surface = SkSurface::MakeRaster(
+            SkImageInfo::MakeN32Premul(SkISize::Make(10, 10)));
+    image = surface->makeImageSnapshot();
+    REPORTER_ASSERT(r, nullptr == image->colorSpace());
+
+    surface = SkSurface::MakeRaster(info);
+    image = surface->makeImageSnapshot();
+    REPORTER_ASSERT(r, SkColorSpace::Equals(rec2020.get(), image->colorSpace()));
+}
+
+DEF_TEST(Image_makeColorSpace, r) {
+    sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                                   SkColorSpace::kDCIP3_D65_Gamut);
+    SkColorSpaceTransferFn fn;
+    fn.fA = 1.f; fn.fB = 0.f; fn.fC = 0.f; fn.fD = 0.f; fn.fE = 0.f; fn.fF = 0.f; fn.fG = 1.8f;
+    sk_sp<SkColorSpace> adobeGamut = SkColorSpace::MakeRGB(fn, SkColorSpace::kAdobeRGB_Gamut);
+
+    SkBitmap srgbBitmap;
+    srgbBitmap.allocPixels(SkImageInfo::MakeS32(1, 1, kOpaque_SkAlphaType));
+    *srgbBitmap.getAddr32(0, 0) = SkSwizzle_RGBA_to_PMColor(0xFF604020);
+    srgbBitmap.setImmutable();
+    sk_sp<SkImage> srgbImage = SkImage::MakeFromBitmap(srgbBitmap);
+    sk_sp<SkImage> p3Image = srgbImage->makeColorSpace(p3, SkTransferFunctionBehavior::kIgnore);
+    SkBitmap p3Bitmap;
+    bool success = p3Image->asLegacyBitmap(&p3Bitmap, SkImage::kRO_LegacyBitmapMode);
+    REPORTER_ASSERT(r, success);
+    REPORTER_ASSERT(r, almost_equal(0x28, SkGetPackedR32(*p3Bitmap.getAddr32(0, 0))));
+    REPORTER_ASSERT(r, almost_equal(0x40, SkGetPackedG32(*p3Bitmap.getAddr32(0, 0))));
+    REPORTER_ASSERT(r, almost_equal(0x5E, SkGetPackedB32(*p3Bitmap.getAddr32(0, 0))));
+
+    sk_sp<SkImage> adobeImage = srgbImage->makeColorSpace(adobeGamut,
+                                                          SkTransferFunctionBehavior::kIgnore);
+    SkBitmap adobeBitmap;
+    success = adobeImage->asLegacyBitmap(&adobeBitmap, SkImage::kRO_LegacyBitmapMode);
+    REPORTER_ASSERT(r, success);
+    REPORTER_ASSERT(r, almost_equal(0x21, SkGetPackedR32(*adobeBitmap.getAddr32(0, 0))));
+    REPORTER_ASSERT(r, almost_equal(0x31, SkGetPackedG32(*adobeBitmap.getAddr32(0, 0))));
+    REPORTER_ASSERT(r, almost_equal(0x4C, SkGetPackedB32(*adobeBitmap.getAddr32(0, 0))));
+
+    srgbImage = GetResourceAsImage("1x1.png");
+    p3Image = srgbImage->makeColorSpace(p3, SkTransferFunctionBehavior::kIgnore);
+    success = p3Image->asLegacyBitmap(&p3Bitmap, SkImage::kRO_LegacyBitmapMode);
+    REPORTER_ASSERT(r, success);
+    REPORTER_ASSERT(r, almost_equal(0x8B, SkGetPackedR32(*p3Bitmap.getAddr32(0, 0))));
+    REPORTER_ASSERT(r, almost_equal(0x82, SkGetPackedG32(*p3Bitmap.getAddr32(0, 0))));
+    REPORTER_ASSERT(r, almost_equal(0x77, SkGetPackedB32(*p3Bitmap.getAddr32(0, 0))));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void make_all_premul(SkBitmap* bm) {
+    bm->allocPixels(SkImageInfo::MakeN32(256, 256, kPremul_SkAlphaType));
+    for (int a = 0; a < 256; ++a) {
+        for (int r = 0; r < 256; ++r) {
+            // make all valid premul combinations
+            int c = SkTMin(a, r);
+            *bm->getAddr32(a, r) = SkPackARGB32(a, c, c, c);
+        }
+    }
+}
+
+static bool equal(const SkBitmap& a, const SkBitmap& b) {
+    SkASSERT(a.width() == b.width());
+    SkASSERT(a.height() == b.height());
+    for (int y = 0; y < a.height(); ++y) {
+        for (int x = 0; x < a.width(); ++x) {
+            SkPMColor pa = *a.getAddr32(x, y);
+            SkPMColor pb = *b.getAddr32(x, y);
+            if (pa != pb) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+DEF_TEST(image_roundtrip_encode, reporter) {
+    SkBitmap bm0;
+    make_all_premul(&bm0);
+
+    auto img0 = SkImage::MakeFromBitmap(bm0);
+    sk_sp<SkData> data = img0->encodeToData(SkEncodedImageFormat::kPNG, 100);
+    auto img1 = SkImage::MakeFromEncoded(data);
+
+    SkBitmap bm1;
+    bm1.allocPixels(SkImageInfo::MakeN32(256, 256, kPremul_SkAlphaType));
+    img1->readPixels(bm1.info(), bm1.getPixels(), bm1.rowBytes(), 0, 0);
+
+    REPORTER_ASSERT(reporter, equal(bm0, bm1));
+}
+
+DEF_TEST(image_roundtrip_premul, reporter) {
+    SkBitmap bm0;
+    make_all_premul(&bm0);
+
+    SkBitmap bm1;
+    bm1.allocPixels(SkImageInfo::MakeN32(256, 256, kUnpremul_SkAlphaType));
+    bm0.readPixels(bm1.info(), bm1.getPixels(), bm1.rowBytes(), 0, 0);
+
+    SkBitmap bm2;
+    bm2.allocPixels(SkImageInfo::MakeN32(256, 256, kPremul_SkAlphaType));
+    bm1.readPixels(bm2.info(), bm2.getPixels(), bm2.rowBytes(), 0, 0);
+
+    REPORTER_ASSERT(reporter, equal(bm0, bm2));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void check_scaled_pixels(skiatest::Reporter* reporter, SkPixmap* pmap, uint32_t expected) {
+    // Verify that all pixels contain the original test color
+    for (auto y = 0; y < pmap->height(); ++y) {
+        for (auto x = 0; x < pmap->width(); ++x) {
+            uint32_t pixel = *pmap->addr32(x, y);
+            if (pixel != expected) {
+                ERRORF(reporter, "Expected scaled pixels to be the same. At %d,%d 0x%08x != 0x%08x",
+                       x, y, pixel, expected);
+                return;
+            }
+        }
+    }
+}
+
+static void test_scale_pixels(skiatest::Reporter* reporter, const SkImage* image,
+                              uint32_t expected) {
+    SkImageInfo info = SkImageInfo::MakeN32Premul(image->width() * 2, image->height() * 2);
+
+    // Make sure to test kDisallow first, so we don't just get a cache hit in that case
+    for (auto chint : { SkImage::kDisallow_CachingHint, SkImage::kAllow_CachingHint }) {
+        SkAutoPixmapStorage scaled;
+        scaled.alloc(info);
+        if (!image->scalePixels(scaled, kLow_SkFilterQuality, chint)) {
+            ERRORF(reporter, "Failed to scale image");
+            continue;
+        }
+
+        check_scaled_pixels(reporter, &scaled, expected);
+    }
+}
+
+DEF_TEST(ImageScalePixels, reporter) {
+    const SkPMColor pmRed = SkPackARGB32(0xFF, 0xFF, 0, 0);
+    const SkColor red = SK_ColorRED;
+
+    // Test raster image
+    SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
+    sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
+    surface->getCanvas()->clear(red);
+    sk_sp<SkImage> rasterImage = surface->makeImageSnapshot();
+    test_scale_pixels(reporter, rasterImage.get(), pmRed);
+
+    // Test encoded image
+    sk_sp<SkData> data = rasterImage->encodeToData();
+    sk_sp<SkImage> codecImage = SkImage::MakeFromEncoded(data);
+    test_scale_pixels(reporter, codecImage.get(), pmRed);
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageScalePixels_Gpu, reporter, ctxInfo) {
+    const SkPMColor pmRed = SkPackARGB32(0xFF, 0xFF, 0, 0);
+    const SkColor red = SK_ColorRED;
+
+    SkImageInfo info = SkImageInfo::MakeN32Premul(16, 16);
+    sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo,
+                                                           info);
+    surface->getCanvas()->clear(red);
+    sk_sp<SkImage> gpuImage = surface->makeImageSnapshot();
+    test_scale_pixels(reporter, gpuImage.get(), pmRed);
+}
+#endif
diff --git a/src/third_party/skia/tests/IndexedPngOverflowTest.cpp b/src/third_party/skia/tests/IndexedPngOverflowTest.cpp
new file mode 100644
index 0000000..1a48b64
--- /dev/null
+++ b/src/third_party/skia/tests/IndexedPngOverflowTest.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "CodecPriv.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkImageInfo.h"
+#include "SkSurface.h"
+#include "Test.h"
+
+// A 20x1 image with 8 bits per pixel and a palette size of 2. Pixel values are 255, 254... Run
+// this test with ASAN to make sure we don't try to access before/after any palette-sized buffers.
+unsigned char gPng[] = {
+    0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
+    0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01,
+    0x08, 0x03, 0x00, 0x00, 0x00, 0xe9, 0x4c, 0x7e, 0x17, 0x00, 0x00, 0x00,
+    0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+    0x1c, 0x00, 0x0f, 0x01, 0xb9, 0x8f, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c,
+    0x54, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x67, 0xb9, 0xcf,
+    0x00, 0x00, 0x00, 0x20, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xfd,
+    0x07, 0x01, 0x00, 0x20, 0x08, 0x00, 0x41, 0xbc, 0x5b, 0xe8, 0xdf, 0x97,
+    0x99, 0xe3, 0x92, 0xa0, 0xf2, 0xdf, 0x3d, 0x7b, 0x0d, 0xda, 0x04, 0x1c,
+    0x03, 0xad, 0x00, 0x38, 0x5c, 0x2e, 0xad, 0x12, 0x00, 0x00, 0x00, 0x00,
+    0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
+DEF_TEST(IndexedPngOverflow, reporter) {
+    SkBitmap image;
+    bool success = decode_memory(gPng, sizeof(gPng), &image);
+    REPORTER_ASSERT(reporter, success);
+
+    auto surface(SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(20, 1)));
+    SkCanvas* canvas = surface->getCanvas();
+    SkRect destRect = SkRect::MakeXYWH(0, 0, 20, 1);
+    canvas->drawBitmapRect(image, destRect, nullptr);
+}
diff --git a/src/third_party/skia/tests/InsetConvexPolyTest.cpp b/src/third_party/skia/tests/InsetConvexPolyTest.cpp
new file mode 100644
index 0000000..9c1349c
--- /dev/null
+++ b/src/third_party/skia/tests/InsetConvexPolyTest.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Test.h"
+#include "SkInsetConvexPolygon.h"
+
+static bool is_convex(const SkTDArray<SkPoint>& poly) {
+    if (poly.count() < 3) {
+        return false;
+    }
+
+    SkVector v0 = poly[0] - poly[poly.count() - 1];
+    SkVector v1 = poly[1] - poly[poly.count() - 1];
+    SkScalar winding = v0.cross(v1);
+
+    for (int i = 0; i < poly.count()-1; ++i) {
+        int j = i + 1;
+        int k = (i + 2) % poly.count();
+
+        SkVector v0 = poly[j] - poly[i];
+        SkVector v1 = poly[k] - poly[i];
+        SkScalar perpDot = v0.cross(v1);
+        if (winding*perpDot < 0) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+DEF_TEST(InsetConvexPoly, reporter) {
+    SkTDArray<SkPoint> rrectPoly;
+
+    // round rect
+    *rrectPoly.push() = SkPoint::Make(-100, 55);
+    *rrectPoly.push() = SkPoint::Make(100, 55);
+    *rrectPoly.push() = SkPoint::Make(100 + 2.5f, 50 + 4.330127f);
+    *rrectPoly.push() = SkPoint::Make(100 + 3.535534f, 50 + 3.535534f);
+    *rrectPoly.push() = SkPoint::Make(100 + 4.330127f, 50 + 2.5f);
+    *rrectPoly.push() = SkPoint::Make(105, 50);
+    *rrectPoly.push() = SkPoint::Make(105, -50);
+    *rrectPoly.push() = SkPoint::Make(100 + 4.330127f, -50 - 2.5f);
+    *rrectPoly.push() = SkPoint::Make(100 + 3.535534f, -50 - 3.535534f);
+    *rrectPoly.push() = SkPoint::Make(100 + 2.5f, -50 - 4.330127f);
+    *rrectPoly.push() = SkPoint::Make(100, -55);
+    *rrectPoly.push() = SkPoint::Make(-100, -55);
+    *rrectPoly.push() = SkPoint::Make(-100 - 2.5f, -50 - 4.330127f);
+    *rrectPoly.push() = SkPoint::Make(-100 - 3.535534f, -50 - 3.535534f);
+    *rrectPoly.push() = SkPoint::Make(-100 - 4.330127f, -50 - 2.5f);
+    *rrectPoly.push() = SkPoint::Make(-105, -50);
+    *rrectPoly.push() = SkPoint::Make(-105, 50);
+    *rrectPoly.push() = SkPoint::Make(-100 - 4.330127f, 50 + 2.5f);
+    *rrectPoly.push() = SkPoint::Make(-100 - 3.535534f, 50 + 3.535534f);
+    *rrectPoly.push() = SkPoint::Make(-100 - 2.5f, 50 + 4.330127f);
+    REPORTER_ASSERT(reporter, is_convex(rrectPoly));
+
+    // inset a little
+    SkTDArray<SkPoint> insetPoly;
+    bool result = SkInsetConvexPolygon(&rrectPoly[0], rrectPoly.count(), 3, &insetPoly);
+    REPORTER_ASSERT(reporter, result);
+    REPORTER_ASSERT(reporter, is_convex(insetPoly));
+
+    // inset to rect
+    result = SkInsetConvexPolygon(&rrectPoly[0], rrectPoly.count(), 10, &insetPoly);
+    REPORTER_ASSERT(reporter, result);
+    REPORTER_ASSERT(reporter, is_convex(insetPoly));
+    REPORTER_ASSERT(reporter, insetPoly.count() == 4);
+    if (insetPoly.count() == 4) {
+        REPORTER_ASSERT(reporter, insetPoly[0].equals(-95, 45));
+        REPORTER_ASSERT(reporter, insetPoly[1].equals(95, 45));
+        REPORTER_ASSERT(reporter, insetPoly[2].equals(95, -45));
+        REPORTER_ASSERT(reporter, insetPoly[3].equals(-95, -45));
+    }
+
+    // just to full inset
+    // fails, but outputs a line segment
+    result = SkInsetConvexPolygon(&rrectPoly[0], rrectPoly.count(), 55, &insetPoly);
+    REPORTER_ASSERT(reporter, !result);
+    REPORTER_ASSERT(reporter, !is_convex(insetPoly));
+    REPORTER_ASSERT(reporter, insetPoly.count() == 2);
+    if (insetPoly.count() == 2) {
+        REPORTER_ASSERT(reporter, insetPoly[0].equals(-50, 0));
+        REPORTER_ASSERT(reporter, insetPoly[1].equals(50, 0));
+    }
+
+    // past full inset
+    result = SkInsetConvexPolygon(&rrectPoly[0], rrectPoly.count(), 75, &insetPoly);
+    REPORTER_ASSERT(reporter, !result);
+    REPORTER_ASSERT(reporter, insetPoly.count() == 0);
+
+    // troublesome case
+    SkTDArray<SkPoint> clippedRRectPoly;
+    *clippedRRectPoly.push() = SkPoint::Make(335.928101f, 428.219055f);
+    *clippedRRectPoly.push() = SkPoint::Make(330.414459f, 423.034912f);
+    *clippedRRectPoly.push() = SkPoint::Make(325.749084f, 417.395508f);
+    *clippedRRectPoly.push() = SkPoint::Make(321.931946f, 411.300842f);
+    *clippedRRectPoly.push() = SkPoint::Make(318.963074f, 404.750977f);
+    *clippedRRectPoly.push() = SkPoint::Make(316.842468f, 397.745850f);
+    *clippedRRectPoly.push() = SkPoint::Make(315.570068f, 390.285522f);
+    *clippedRRectPoly.push() = SkPoint::Make(315.145966f, 382.369965f);
+    *clippedRRectPoly.push() = SkPoint::Make(315.570068f, 374.454346f);
+    *clippedRRectPoly.push() = SkPoint::Make(316.842468f, 366.994019f);
+    *clippedRRectPoly.push() = SkPoint::Make(318.963074f, 359.988892f);
+    *clippedRRectPoly.push() = SkPoint::Make(321.931946f, 353.439056f);
+    *clippedRRectPoly.push() = SkPoint::Make(325.749084f, 347.344421f);
+    *clippedRRectPoly.push() = SkPoint::Make(330.414459f, 341.705017f);
+    *clippedRRectPoly.push() = SkPoint::Make(335.928101f, 336.520813f);
+    *clippedRRectPoly.push() = SkPoint::Make(342.289948f, 331.791901f);
+    *clippedRRectPoly.push() = SkPoint::Make(377.312134f, 331.791901f);
+    *clippedRRectPoly.push() = SkPoint::Make(381.195313f, 332.532593f);
+    *clippedRRectPoly.push() = SkPoint::Make(384.464935f, 334.754700f);
+    *clippedRRectPoly.push() = SkPoint::Make(386.687042f, 338.024292f);
+    *clippedRRectPoly.push() = SkPoint::Make(387.427765f, 341.907532f);
+    *clippedRRectPoly.push() = SkPoint::Make(387.427765f, 422.832367f);
+    *clippedRRectPoly.push() = SkPoint::Make(386.687042f, 426.715576f);
+    *clippedRRectPoly.push() = SkPoint::Make(384.464935f, 429.985168f);
+    *clippedRRectPoly.push() = SkPoint::Make(381.195313f, 432.207275f);
+    *clippedRRectPoly.push() = SkPoint::Make(377.312134f, 432.947998f);
+    *clippedRRectPoly.push() = SkPoint::Make(342.289948f, 432.947998f);
+    REPORTER_ASSERT(reporter, is_convex(clippedRRectPoly));
+
+    result = SkInsetConvexPolygon(&clippedRRectPoly[0], clippedRRectPoly.count(), 32.3699417f,
+                                  &insetPoly);
+    REPORTER_ASSERT(reporter, result);
+    REPORTER_ASSERT(reporter, is_convex(insetPoly));
+}
diff --git a/src/third_party/skia/tests/IntTextureTest.cpp b/src/third_party/skia/tests/IntTextureTest.cpp
new file mode 100644
index 0000000..ccdc304
--- /dev/null
+++ b/src/third_party/skia/tests/IntTextureTest.cpp
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrClip.h"
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrRenderTargetContext.h"
+#include "GrResourceProvider.h"
+#include "GrTexture.h"
+#include "effects/GrSimpleTextureEffect.h"
+
+template <typename I>
+static SK_WHEN(std::is_integral<I>::value && 4 == sizeof(I), void)
+check_pixels(skiatest::Reporter* reporter, int w, int h, const I exepctedData[],
+             const I actualData[], const char* testName) {
+    for (int j = 0; j < h; ++j) {
+        for (int i = 0; i < w; ++i) {
+            I expected = exepctedData[j * w + i];
+            I actual = actualData[j * w + i];
+            if (expected != actual) {
+                ERRORF(reporter, "[%s] Expected 0x08%x, got 0x%08x at %d, %d.", testName, expected,
+                       actual, i, j);
+                return;
+            }
+        }
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(IntTexture, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    if (!context->caps()->isConfigTexturable(kRGBA_8888_sint_GrPixelConfig)) {
+        return;
+    }
+    static const int kS = UINT8_MAX + 1;
+    static const size_t kRowBytes = kS * sizeof(int32_t);
+
+    GrSurfaceDesc desc;
+    desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+    desc.fConfig = kRGBA_8888_sint_GrPixelConfig;
+    desc.fWidth = kS;
+    desc.fHeight = kS;
+
+    std::unique_ptr<int32_t[]> testData(new int32_t[kS * kS]);
+    for (int j = 0; j < kS; ++j) {
+        for (int i = 0; i < kS; ++i) {
+            uint32_t r = i - INT8_MIN;
+            uint32_t g = j - INT8_MIN;
+            uint32_t b = INT8_MAX - r;
+            uint32_t a = INT8_MAX - g;
+            testData.get()[j * kS + i] = (a << 24) | (b << 16) | (g << 8) | r;
+        }
+    }
+
+    // Test that attempting to create a integer texture with multiple MIP levels fails.
+    {
+        GrMipLevel levels[2];
+        levels[0].fPixels = testData.get();
+        levels[0].fRowBytes = kRowBytes;
+        levels[1].fPixels = testData.get();
+        levels[1].fRowBytes = (kS / 2) * sizeof(int32_t);
+
+        sk_sp<GrTextureProxy> temp(GrSurfaceProxy::MakeDeferredMipMap(context->resourceProvider(),
+                                                                      desc,
+                                                                      SkBudgeted::kYes,
+                                                                      levels, 2));
+        REPORTER_ASSERT(reporter, !temp);
+    }
+
+    // Test that we can create an integer texture.
+    sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                               desc, SkBudgeted::kYes,
+                                                               testData.get(),
+                                                               kRowBytes);
+    REPORTER_ASSERT(reporter, proxy);
+    if (!proxy) {
+        return;
+    }
+
+    sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                                    std::move(proxy), nullptr);
+    if (!sContext) {
+        return;
+    }
+
+    std::unique_ptr<int32_t[]> readData(new int32_t[kS * kS]);
+    // Test that reading to a non-integer config fails.
+    {
+        bool success = context->contextPriv().readSurfacePixels(sContext.get(),
+                                                                0, 0, kS, kS,
+                                                                kRGBA_8888_GrPixelConfig,
+                                                                nullptr, readData.get());
+        REPORTER_ASSERT(reporter, !success);
+    }
+    {
+        std::unique_ptr<uint16_t[]> halfData(new uint16_t[4 * kS * kS]);
+        bool success = context->contextPriv().readSurfacePixels(sContext.get(),
+                                                                0, 0, kS, kS,
+                                                                kRGBA_half_GrPixelConfig,
+                                                                nullptr, halfData.get());
+        REPORTER_ASSERT(reporter, !success);
+    }
+    {
+        // Can read back as ints. (ES only requires being able to read back into 32bit ints which
+        // we don't support. Right now this test is counting on GR_RGBA_INTEGER/GL_BYTE being the
+        // implementation-dependent second format).
+        sk_bzero(readData.get(), sizeof(int32_t) * kS * kS);
+        bool success = context->contextPriv().readSurfacePixels(sContext.get(),
+                                                                0, 0, kS, kS,
+                                                                kRGBA_8888_sint_GrPixelConfig,
+                                                                nullptr, readData.get());
+        REPORTER_ASSERT(reporter, success);
+        if (success) {
+            check_pixels(reporter, kS, kS, testData.get(), readData.get(), "readPixels");
+        }
+    }
+    {
+        // readPixels should fail if we attempt to use the unpremul flag with an integer texture.
+        bool success = context->contextPriv().readSurfacePixels(
+                                                sContext.get(),
+                                                0, 0, kS, kS,
+                                                kRGBA_8888_sint_GrPixelConfig,
+                                                nullptr, readData.get(), 0,
+                                                GrContextPriv::kUnpremul_PixelOpsFlag);
+        REPORTER_ASSERT(reporter, !success);
+    }
+
+    // Test that copying from one integer texture to another succeeds.
+    {
+        sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, desc,
+                                                                    sContext->asSurfaceProxy()));
+        REPORTER_ASSERT(reporter, dstContext);
+        if (!dstContext || !dstContext->asTextureProxy()) {
+            return;
+        }
+
+        sk_bzero(readData.get(), sizeof(int32_t) * kS * kS);
+        bool success = context->contextPriv().readSurfacePixels(dstContext.get(), 0, 0, kS, kS,
+                                                                kRGBA_8888_sint_GrPixelConfig,
+                                                                nullptr, readData.get());
+        REPORTER_ASSERT(reporter, success);
+        if (success) {
+            check_pixels(reporter, kS, kS, testData.get(), readData.get(), "copyIntegerToInteger");
+        }
+    }
+
+
+    // Test that copying to a non-integer (8888) texture fails.
+    {
+        GrSurfaceDesc nonIntDesc = desc;
+        nonIntDesc.fConfig = kRGBA_8888_GrPixelConfig;
+
+        sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, nonIntDesc,
+                                                                    sContext->asSurfaceProxy()));
+        REPORTER_ASSERT(reporter, !dstContext);
+    }
+
+    // Test that copying to a non-integer (RGBA_half) texture fails.
+    if (context->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
+        GrSurfaceDesc nonIntDesc = desc;
+        nonIntDesc.fConfig = kRGBA_half_GrPixelConfig;
+
+        sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, nonIntDesc,
+                                                                    sContext->asSurfaceProxy()));
+        REPORTER_ASSERT(reporter, !dstContext);
+    }
+
+    // We overwrite the top left quarter of the texture with the bottom right quarter of the
+    // original data.
+    const void* bottomRightQuarter = testData.get() + kS / 2 * kS + kS / 2;
+
+    {
+        // Can't write pixels from a non-int config.
+        bool success = context->contextPriv().writeSurfacePixels(sContext.get(),
+                                                                 0, 0, kS/2, kS/2,
+                                                                 kRGBA_8888_GrPixelConfig, nullptr,
+                                                                 bottomRightQuarter, kRowBytes);
+        REPORTER_ASSERT(reporter, !success);
+    }
+    {
+        // Can't use unpremul flag.
+        bool success = context->contextPriv().writeSurfacePixels(
+                                            sContext.get(),
+                                            0, 0, kS/2, kS/2,
+                                            kRGBA_8888_sint_GrPixelConfig,
+                                            nullptr,
+                                            bottomRightQuarter, kRowBytes,
+                                            GrContextPriv::kUnpremul_PixelOpsFlag);
+        REPORTER_ASSERT(reporter, !success);
+    }
+    {
+        bool success = context->contextPriv().writeSurfacePixels(sContext.get(),
+                                                                 0, 0, kS/2, kS/2,
+                                                                 kRGBA_8888_sint_GrPixelConfig,
+                                                                 nullptr,
+                                                                 bottomRightQuarter, kRowBytes);
+        REPORTER_ASSERT(reporter, success);
+        if (!success) {
+            return;
+        }
+
+        sk_bzero(readData.get(), sizeof(int32_t) * kS * kS);
+        success = context->contextPriv().readSurfacePixels(sContext.get(),
+                                                           0, 0, kS, kS,
+                                                           kRGBA_8888_sint_GrPixelConfig,
+                                                           nullptr, readData.get(), 0);
+        REPORTER_ASSERT(reporter, success);
+        if (!success) {
+            return;
+        }
+        std::unique_ptr<int32_t[]> overwrittenTestData(new int32_t[kS * kS]);
+        memcpy(overwrittenTestData.get(), testData.get(), sizeof(int32_t) * kS * kS);
+        char* dst = (char*)overwrittenTestData.get();
+        char* src = (char*)(testData.get() + kS/2 * kS + kS/2);
+        for (int i = 0; i < kS/2; ++i) {
+            memcpy(dst, src, sizeof(int32_t) * kS/2);
+            dst += kRowBytes;
+            src += kRowBytes;
+        }
+        check_pixels(reporter, kS, kS, overwrittenTestData.get(), readData.get(), "overwrite");
+    }
+
+    // Test drawing from the integer texture to a fixed point texture. To avoid any premul issues
+    // we init the int texture with 0s and 1s and make alpha always be 1. We expect that 1s turn
+    // into 0xffs and zeros stay zero.
+    std::unique_ptr<uint32_t[]> expectedData(new uint32_t[kS * kS]);
+    std::unique_ptr<uint32_t[]> actualData(new uint32_t[kS * kS]);
+    for (int i = 0; i < kS*kS; ++i) {
+        int32_t a = 0x1;
+        int32_t b = ((i & 0x1) ? 1 : 0);
+        int32_t g = ((i & 0x1) ? 0 : 1);
+        int32_t r = ((i & 0x2) ? 1 : 0);
+        testData.get()[i] = (a << 24) | (b << 16) | (g << 8) | r;
+        expectedData.get()[i] = ((0xFF * a) << 24) | ((0xFF * b) << 16) |
+                                ((0xFF * g) << 8) | (0xFF * r);
+    }
+    context->contextPriv().writeSurfacePixels(sContext.get(),
+                                              0, 0, kS, kS,
+                                              kRGBA_8888_sint_GrPixelConfig, nullptr,
+                                              testData.get(), 0);
+
+    sk_sp<GrRenderTargetContext> rtContext = context->makeDeferredRenderTargetContext(
+            SkBackingFit::kExact, kS, kS, kRGBA_8888_GrPixelConfig, nullptr);
+
+    struct {
+        GrSamplerParams::FilterMode fMode;
+        const char* fName;
+    } kNamedFilters[] ={
+        { GrSamplerParams::kNone_FilterMode, "filter-none" },
+        { GrSamplerParams::kBilerp_FilterMode, "filter-bilerp" },
+        { GrSamplerParams::kMipMap_FilterMode, "filter-mipmap" }
+    };
+
+    for (auto filter : kNamedFilters) {
+        sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(sContext->asTextureProxyRef(),
+                                                                  nullptr,
+                                                                  SkMatrix::I(),
+                                                                  filter.fMode));
+        REPORTER_ASSERT(reporter, fp);
+        if (!fp) {
+            return;
+        }
+        rtContext->clear(nullptr, 0xDDAABBCC, true);
+        GrPaint paint;
+        paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+        paint.addColorFragmentProcessor(fp);
+        rtContext->drawPaint(GrNoClip(), std::move(paint), SkMatrix::I());
+        SkImageInfo readInfo = SkImageInfo::Make(kS, kS, kRGBA_8888_SkColorType,
+                                                 kPremul_SkAlphaType);
+        rtContext->readPixels(readInfo, actualData.get(), 0, 0, 0);
+        check_pixels(reporter, kS, kS, expectedData.get(), actualData.get(), filter.fName);
+    }
+
+    {
+        // No rendering to integer textures.
+        GrSurfaceDesc intRTDesc = desc;
+        intRTDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+        sk_sp<GrTexture> temp(context->resourceProvider()->createTexture(intRTDesc,
+                                                                         SkBudgeted::kYes));
+        REPORTER_ASSERT(reporter, !temp);
+    }
+}
+
+#endif
diff --git a/src/third_party/skia/tests/InterpolatorTest.cpp b/src/third_party/skia/tests/InterpolatorTest.cpp
index 3cfd19f..8ba1bda 100644
--- a/src/third_party/skia/tests/InterpolatorTest.cpp
+++ b/src/third_party/skia/tests/InterpolatorTest.cpp
@@ -58,4 +58,34 @@
     result = inter.timeToValues(175, v);
     REPORTER_ASSERT(reporter, result == SkInterpolator::kNormal_Result);
 
+    for (SkScalar val = -0.1f; val <= 1.1f; val += 0.1f) {
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkTPin(0.f, val, 1.f),
+                        SkUnitCubicInterp(val, 1.f/3, 1.f/3, 2.f/3, 2.f/3)));
+    }
+
+    // These numbers come from
+    // http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag.
+    const SkScalar testTransitions[][4] = {
+        { 0.25f, 0.1f, 0.25f, 1 }, // ease
+        { 0.42f, 0,    1,     1 }, // ease in
+        { 0,     0,    0.58f, 1 }, // ease out
+        { 0.42f, 0,    0.58f, 1 }, // ease in out
+    };
+
+    const SkScalar expectedOutput[][5] = {
+        { 0.0947876f, 0.513367f, 0.80249f,  0.940796f, 0.994263f }, // ease
+        { 0.0170288f, 0.129639f, 0.31543f,  0.554749f, 0.839417f }, // ease in
+        { 0.160583f,  0.445251f, 0.684692f, 0.870361f, 0.982971f }, // ease out
+        { 0.0197144f, 0.187439f, 0.500122f, 0.812561f, 0.980286f }, // ease in out
+    };
+
+    int i = 0;
+    for (const SkScalar* t : testTransitions) {
+        int j = 0;
+        for (SkScalar val = 0.1f; val < 1; val += 0.2f) {
+            REPORTER_ASSERT(reporter, SkScalarNearlyEqual(expectedOutput[i][j++],
+                            SkUnitCubicInterp(val, t[0], t[1], t[2], t[3])));
+        }
+        ++i;
+    }
 }
diff --git a/src/third_party/skia/tests/InvalidIndexedPngTest.cpp b/src/third_party/skia/tests/InvalidIndexedPngTest.cpp
new file mode 100644
index 0000000..5d9632d
--- /dev/null
+++ b/src/third_party/skia/tests/InvalidIndexedPngTest.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "CodecPriv.h"
+#include "SkBitmap.h"
+#include "Test.h"
+
+// A valid 1x1 indexed PNG.
+unsigned char gPngData[] = {
+  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
+  0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
+  0x08, 0x03, 0x00, 0x00, 0x00, 0x28, 0xcb, 0x34, 0xbb, 0x00, 0x00, 0x00,
+  0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+  0x1c, 0x00, 0x0f, 0x01, 0xb9, 0x8f, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c,
+  0x54, 0x45, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xd2, 0x87, 0xef, 0x71,
+  0x00, 0x00, 0x00, 0x13, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xfd,
+  0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xf8, 0xaf, 0x16, 0x46, 0x00,
+  0x02, 0x00, 0x01, 0x32, 0x60, 0xf7, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x49,
+  0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
+// Attempt to decode an invalid PNG that has a palette. Mostly we're looking to
+// make sure we don't leak memory since libpng uses setjmp for error handling so
+// it's very easy to accidentally skip destructors when a failure happens.
+// As a result, we do not have any REPORTER_ASSERT statements
+DEF_TEST(InvalidIndexedPng, reporter) {
+    SkBitmap image;
+    // Make our PNG invalid by changing a byte.
+    gPngData[sizeof(gPngData) - 1] = 1;
+
+    decode_memory(gPngData, sizeof(gPngData), &image);
+}
diff --git a/src/third_party/skia/tests/IsClosedSingleContourTest.cpp b/src/third_party/skia/tests/IsClosedSingleContourTest.cpp
new file mode 100644
index 0000000..b550998
--- /dev/null
+++ b/src/third_party/skia/tests/IsClosedSingleContourTest.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Test.h"
+#include "SkPathPriv.h"
+
+DEF_TEST(IsClosedSingleContourTest, reporter) {
+    SkPath p;
+    REPORTER_ASSERT(reporter, !SkPathPriv::IsClosedSingleContour(p));
+
+    p.reset();
+    p.close();
+    REPORTER_ASSERT(reporter, !SkPathPriv::IsClosedSingleContour(p));
+
+    p.reset();
+    p.moveTo(10, 10);
+    p.close();
+    REPORTER_ASSERT(reporter, SkPathPriv::IsClosedSingleContour(p));
+
+    p.reset();
+    p.moveTo(10, 10);
+    p.lineTo(20, 20);
+    p.close();
+    REPORTER_ASSERT(reporter, SkPathPriv::IsClosedSingleContour(p));
+
+    p.reset();
+    p.moveTo(10, 10);
+    p.lineTo(20, 20);
+    p.quadTo(30, 30, 40, 40);
+    p.cubicTo(50, 50, 60, 60, 70, 70);
+    p.conicTo(30, 30, 40, 40, 0.5);
+    p.close();
+    REPORTER_ASSERT(reporter, SkPathPriv::IsClosedSingleContour(p));
+
+    p.reset();
+    p.moveTo(10, 10);
+    p.lineTo(20, 20);
+    p.lineTo(20, 30);
+    REPORTER_ASSERT(reporter, !SkPathPriv::IsClosedSingleContour(p));
+
+    p.reset();
+    p.moveTo(10, 10);
+    p.lineTo(20, 20);
+    p.moveTo(10, 10);
+    p.lineTo(20, 30);
+    p.close();
+    REPORTER_ASSERT(reporter, !SkPathPriv::IsClosedSingleContour(p));
+
+    p.reset();
+    p.moveTo(10, 10);
+    p.lineTo(20, 20);
+    p.close();
+    p.lineTo(20, 30);
+    p.close();
+    REPORTER_ASSERT(reporter, !SkPathPriv::IsClosedSingleContour(p));
+}
diff --git a/src/third_party/skia/tests/JpegTest.cpp b/src/third_party/skia/tests/JpegTest.cpp
deleted file mode 100644
index f8784a2..0000000
--- a/src/third_party/skia/tests/JpegTest.cpp
+++ /dev/null
@@ -1,454 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBitmap.h"
-#include "SkData.h"
-#include "SkForceLinking.h"
-#include "SkImage.h"
-#include "SkImageDecoder.h"
-#include "SkStream.h"
-#include "Test.h"
-
-__SK_FORCE_IMAGE_DECODER_LINKING;
-
-#define JPEG_TEST_WRITE_TO_FILE_FOR_DEBUGGING 0  // do not do this for
-                                                 // normal unit testing.
-static unsigned char goodJpegImage[] = {
-0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46,
-0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x8F,
-0x00, 0x8F, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43,
-0x00, 0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05,
-0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07,
-0x0C, 0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B,
-0x0B, 0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11,
-0x0F, 0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13,
-0x14, 0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18,
-0x1A, 0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17,
-0x22, 0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F,
-0x1E, 0xFF, 0xDB, 0x00, 0x43, 0x01, 0x05, 0x05,
-0x05, 0x07, 0x06, 0x07, 0x0E, 0x08, 0x08, 0x0E,
-0x1E, 0x14, 0x11, 0x14, 0x1E, 0x1E, 0x1E, 0x1E,
-0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
-0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
-0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
-0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
-0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
-0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0xFF, 0xC0,
-0x00, 0x11, 0x08, 0x00, 0x80, 0x00, 0x80, 0x03,
-0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11,
-0x01, 0xFF, 0xC4, 0x00, 0x18, 0x00, 0x01, 0x01,
-0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,
-0x08, 0x06, 0x05, 0xFF, 0xC4, 0x00, 0x4C, 0x10,
-0x00, 0x00, 0x01, 0x07, 0x08, 0x05, 0x08, 0x05,
-0x0A, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x11,
-0x05, 0x08, 0x12, 0x13, 0x14, 0x15, 0x38, 0xB4,
-0x16, 0x17, 0x21, 0x31, 0x84, 0x18, 0x22, 0x23,
-0x24, 0x58, 0xA5, 0xA6, 0xD2, 0x32, 0x51, 0x56,
-0x61, 0xD3, 0x28, 0x33, 0x41, 0x48, 0x67, 0x85,
-0x86, 0xC3, 0xE4, 0xF0, 0x25, 0x49, 0x55, 0x09,
-0x34, 0x35, 0x36, 0x53, 0x68, 0x72, 0x81, 0xA7,
-0xE2, 0xFF, 0xC4, 0x00, 0x14, 0x01, 0x01, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
-0xC4, 0x00, 0x14, 0x11, 0x01, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDA, 0x00,
-0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11,
-0x00, 0x3F, 0x00, 0xD9, 0x62, 0x10, 0x80, 0x40,
-0x65, 0xED, 0x62, 0x75, 0xC8, 0x7D, 0xFF, 0x00,
-0x92, 0x30, 0x33, 0x01, 0x97, 0xB5, 0x89, 0xD7,
-0x21, 0xF7, 0xFE, 0x48, 0xC0, 0x0C, 0xC2, 0x10,
-0x80, 0x40, 0x66, 0x64, 0xB8, 0x62, 0x64, 0x78,
-0xDC, 0xEA, 0x70, 0xCC, 0x06, 0x66, 0x4B, 0x86,
-0x26, 0x47, 0x8D, 0xCE, 0xA7, 0x00, 0xCC, 0x21,
-0x08, 0x04, 0x31, 0x9F, 0xF2, 0xC5, 0xFD, 0xFF,
-0x00, 0x5A, 0x1B, 0x30, 0x63, 0x3F, 0xE5, 0x8B,
-0xFB, 0xFE, 0xB4, 0x03, 0x66, 0x01, 0x99, 0x92,
-0xE1, 0x89, 0x91, 0xE3, 0x73, 0xA9, 0xC3, 0x30,
-0x19, 0x99, 0x2E, 0x18, 0x99, 0x1E, 0x37, 0x3A,
-0x9C, 0x03, 0x30, 0x84, 0x33, 0x33, 0x92, 0x55,
-0x7E, 0xCF, 0x29, 0xD8, 0x49, 0x0D, 0xAE, 0xBD,
-0xAE, 0xAB, 0xC6, 0xBB, 0xAA, 0x68, 0x92, 0x92,
-0x6A, 0xBA, 0xB4, 0xE9, 0x11, 0x7A, 0x7C, 0xD8,
-0xC6, 0x84, 0x77, 0x12, 0x11, 0x87, 0xBC, 0x07,
-0x67, 0xAC, 0x47, 0xED, 0xD9, 0xD3, 0xC6, 0xAA,
-0x5E, 0x51, 0x6B, 0x11, 0xFB, 0x76, 0x74, 0xF1,
-0xAA, 0x97, 0x94, 0x33, 0x08, 0x00, 0xCE, 0xB1,
-0x1F, 0xB7, 0x67, 0x4F, 0x1A, 0xA9, 0x79, 0x41,
-0x9B, 0xC4, 0x6C, 0xDE, 0xC2, 0xCB, 0xF6, 0x75,
-0x92, 0x84, 0xA0, 0xE5, 0xEC, 0x12, 0xB2, 0x9D,
-0xEF, 0x76, 0xC9, 0xBA, 0x50, 0xAA, 0x92, 0xF1,
-0xA6, 0xAA, 0x69, 0x12, 0xF4, 0xA4, 0x36, 0x8A,
-0x2A, 0xB3, 0x60, 0x77, 0x3A, 0x34, 0xA3, 0x02,
-0x6D, 0x1A, 0xC8, 0x0C, 0xBD, 0xAC, 0x4E, 0xB9,
-0x0F, 0xBF, 0xF2, 0x46, 0x00, 0xB5, 0x88, 0xFD,
-0xBB, 0x3A, 0x78, 0xD5, 0x4B, 0xCA, 0x2D, 0x62,
-0x3F, 0x6E, 0xCE, 0x9E, 0x35, 0x52, 0xF2, 0x86,
-0x61, 0x00, 0x19, 0xD6, 0x23, 0xF6, 0xEC, 0xE9,
-0xE3, 0x55, 0x2F, 0x28, 0x33, 0x9A, 0xE3, 0x66,
-0xF6, 0x24, 0x97, 0x12, 0xCE, 0xC9, 0xEC, 0xCB,
-0x97, 0xD2, 0x49, 0x25, 0x15, 0xAA, 0xCF, 0x29,
-0x69, 0x42, 0xAA, 0xA5, 0x7C, 0x56, 0x92, 0x94,
-0xEE, 0x88, 0xF3, 0x4A, 0x71, 0xB4, 0x4E, 0x29,
-0xC6, 0xED, 0xDF, 0x46, 0x3B, 0x8A, 0x35, 0x90,
-0x19, 0x99, 0x2E, 0x18, 0x99, 0x1E, 0x37, 0x3A,
-0x9C, 0x01, 0x9B, 0xE4, 0x79, 0x73, 0x93, 0x59,
-0x69, 0xD9, 0x36, 0x65, 0x99, 0x62, 0x34, 0x1E,
-0x56, 0x95, 0xAD, 0x96, 0x75, 0x7B, 0xD6, 0x4F,
-0x94, 0x6F, 0x1A, 0xA3, 0x0C, 0x3C, 0xEE, 0x71,
-0xE6, 0x51, 0x45, 0x56, 0x6D, 0x22, 0xED, 0x29,
-0x29, 0x53, 0xFA, 0x4A, 0x41, 0xE2, 0xFC, 0xBB,
-0x3F, 0x77, 0x28, 0x66, 0x7B, 0x58, 0x9D, 0x72,
-0x1F, 0x7F, 0xE4, 0x8C, 0x0C, 0xC0, 0x31, 0x9F,
-0xCB, 0xB3, 0xF7, 0x72, 0x8F, 0x19, 0xB6, 0x76,
-0x8F, 0x61, 0x8B, 0x99, 0xDA, 0xDA, 0x16, 0x99,
-0xB7, 0xB0, 0x49, 0x2A, 0x74, 0x2D, 0x0C, 0x9D,
-0xD4, 0xAA, 0x92, 0x85, 0x39, 0x40, 0xD2, 0x9B,
-0xD7, 0x0C, 0x3C, 0xA7, 0x16, 0x27, 0x1C, 0x6A,
-0x5D, 0x91, 0xDF, 0x43, 0x70, 0xDC, 0xA2, 0x01,
-0x8C, 0xF5, 0xC1, 0xFE, 0xF1, 0x3F, 0xF3, 0x4F,
-0xFE, 0x07, 0xB5, 0x35, 0xC6, 0x31, 0xEC, 0x4A,
-0xCE, 0x25, 0x9D, 0x94, 0x19, 0x97, 0xD1, 0xA3,
-0x72, 0x4A, 0x5B, 0x55, 0x9E, 0x4D, 0xD1, 0x75,
-0x55, 0xBA, 0x88, 0x2D, 0x25, 0x21, 0xDD, 0x29,
-0xE7, 0x10, 0xE3, 0xA9, 0x1C, 0x43, 0x8E, 0xDB,
-0xBA, 0x94, 0x37, 0x10, 0x6B, 0x21, 0x00, 0x19,
-0xD5, 0xDB, 0xF6, 0xED, 0x17, 0xE0, 0xA5, 0x2F,
-0x30, 0x33, 0x9A, 0xE3, 0x18, 0xF6, 0x25, 0x67,
-0x12, 0xCE, 0xCA, 0x0C, 0xCB, 0xE8, 0xD1, 0xB9,
-0x25, 0x2D, 0xAA, 0xCF, 0x26, 0xE8, 0xBA, 0xAA,
-0xDD, 0x44, 0x16, 0x92, 0x90, 0xEE, 0x94, 0xF3,
-0x88, 0x71, 0xD4, 0x8E, 0x21, 0xC7, 0x6D, 0xDD,
-0x4A, 0x1B, 0x88, 0x35, 0x90, 0x19, 0x99, 0x2E,
-0x18, 0x99, 0x1E, 0x37, 0x3A, 0x9C, 0x03, 0x30,
-0x80, 0x04, 0xDB, 0x99, 0x69, 0x09, 0x8B, 0x7E,
-0xCF, 0x8D, 0x99, 0x66, 0x54, 0x6C, 0x12, 0x4A,
-0x9D, 0xC7, 0x67, 0x57, 0xAD, 0x3D, 0x25, 0x0A,
-0x6A, 0xA9, 0x4F, 0x3B, 0x9C, 0x79, 0x4A, 0x71,
-0x62, 0x71, 0xC7, 0x17, 0x69, 0x4B, 0xBF, 0xD4,
-0x1F, 0xC0, 0x43, 0x8C, 0x79, 0xAE, 0xB5, 0x84,
-0x79, 0x57, 0x7E, 0x9A, 0xC8, 0x57, 0xAD, 0xDD,
-0x5B, 0x64, 0xEB, 0x69, 0xD0, 0xD5, 0xD6, 0x50,
-0xA7, 0xF3, 0x47, 0x9B, 0x18, 0xD0, 0x33, 0x7C,
-0x61, 0x0D, 0x9F, 0x48, 0xEC, 0xC0, 0x03, 0x12,
-0xFB, 0x5E, 0xC3, 0x68, 0xCC, 0x2A, 0x34, 0xCC,
-0xCB, 0x83, 0xB7, 0xC9, 0x2B, 0x94, 0xEC, 0xEB,
-0x1A, 0x5E, 0xAA, 0x8E, 0x9D, 0x03, 0xCE, 0x30,
-0xEE, 0x69, 0xE8, 0xC8, 0x71, 0x20, 0x71, 0xA7,
-0x13, 0x69, 0x09, 0xBB, 0xD4, 0x03, 0xD9, 0xE4,
-0xB8, 0xE2, 0x7D, 0x86, 0xEF, 0x65, 0xDF, 0x8C,
-0x2E, 0x4B, 0x8E, 0x27, 0xD8, 0x6E, 0xF6, 0x5D,
-0xF8, 0xC2, 0xD6, 0x23, 0xF6, 0xEC, 0xE9, 0xE3,
-0x55, 0x2F, 0x28, 0xB5, 0x88, 0xFD, 0xBB, 0x3A,
-0x78, 0xD5, 0x4B, 0xCA, 0x02, 0xE4, 0xB8, 0xE2,
-0x7D, 0x86, 0xEF, 0x65, 0xDF, 0x8C, 0x0C, 0xE6,
-0xB8, 0xE1, 0x1D, 0x3B, 0x68, 0xE2, 0x59, 0xD6,
-0x99, 0xA6, 0x65, 0x2D, 0xF2, 0xB2, 0xE5, 0xAA,
-0xD0, 0xB1, 0x78, 0x2D, 0x23, 0xA7, 0x41, 0x69,
-0x29, 0x86, 0xF3, 0x4C, 0x48, 0x43, 0x49, 0x03,
-0x4D, 0x34, 0x9B, 0x08, 0x4D, 0xDE, 0xB0, 0x99,
-0xAC, 0x47, 0xED, 0xD9, 0xD3, 0xC6, 0xAA, 0x5E,
-0x50, 0x67, 0x35, 0xC6, 0xCD, 0xEC, 0x49, 0x2E,
-0x25, 0x9D, 0x93, 0xD9, 0x97, 0x2F, 0xA4, 0x92,
-0x4A, 0x2B, 0x55, 0x9E, 0x52, 0xD2, 0x85, 0x55,
-0x4A, 0xF8, 0xAD, 0x25, 0x29, 0xDD, 0x11, 0xE6,
-0x94, 0xE3, 0x68, 0x9C, 0x53, 0x8D, 0xDB, 0xBE,
-0x8C, 0x77, 0x14, 0x04, 0xF1, 0x1C, 0x23, 0xA7,
-0x92, 0x5F, 0xB3, 0xAC, 0x66, 0x64, 0xF6, 0x52,
-0xA6, 0x49, 0x97, 0xAF, 0x7B, 0xC9, 0x5E, 0xF0,
-0x5A, 0x3A, 0xBE, 0xA1, 0x54, 0xD3, 0xD1, 0x73,
-0x8A, 0x90, 0xA7, 0x1B, 0x44, 0xE2, 0x94, 0xBC,
-0xD2, 0x92, 0x3F, 0x4C, 0x48, 0x13, 0x39, 0x2E,
-0x38, 0x9F, 0x61, 0xBB, 0xD9, 0x77, 0xE3, 0x01,
-0x97, 0xF4, 0xF7, 0x1B, 0xB6, 0x51, 0xE7, 0xBB,
-0x76, 0xD5, 0xB5, 0x74, 0xB7, 0x15, 0xCD, 0x7A,
-0x59, 0x15, 0x34, 0x89, 0x02, 0xCD, 0xBA, 0xB9,
-0x02, 0x34, 0x47, 0xF3, 0xD1, 0x18, 0x5A, 0xBA,
-0x14, 0x8C, 0x2E, 0xD2, 0x16, 0x95, 0x28, 0x12,
-0x10, 0x29, 0x46, 0xCC, 0x00, 0x33, 0xC9, 0x71,
-0xC4, 0xFB, 0x0D, 0xDE, 0xCB, 0xBF, 0x18, 0x5C,
-0x97, 0x1C, 0x4F, 0xB0, 0xDD, 0xEC, 0xBB, 0xF1,
-0x83, 0x30, 0x80, 0x0C, 0xF2, 0x5C, 0x71, 0x3E,
-0xC3, 0x77, 0xB2, 0xEF, 0xC6, 0x17, 0x25, 0xC7,
-0x13, 0xEC, 0x37, 0x7B, 0x2E, 0xFC, 0x60, 0xCC,
-0x20, 0x03, 0x3C, 0x97, 0x1C, 0x4F, 0xB0, 0xDD,
-0xEC, 0xBB, 0xF1, 0x81, 0x9C, 0xD7, 0x1C, 0x23,
-0xA7, 0x6D, 0x1C, 0x4B, 0x3A, 0xD3, 0x34, 0xCC,
-0xA5, 0xBE, 0x56, 0x5C, 0xB5, 0x5A, 0x16, 0x2F,
-0x05, 0xA4, 0x74, 0xE8, 0x2D, 0x25, 0x30, 0xDE,
-0x69, 0x89, 0x08, 0x69, 0x20, 0x69, 0xA6, 0x93,
-0x61, 0x09, 0xBB, 0xD6, 0x35, 0x90, 0x19, 0x99,
-0x2E, 0x18, 0x99, 0x1E, 0x37, 0x3A, 0x9C, 0x07,
-0x8D, 0x36, 0xE6, 0xA6, 0x42, 0x6D, 0x1F, 0xB3,
-0xE3, 0x69, 0x99, 0x95, 0xEB, 0x7C, 0x92, 0xB9,
-0x71, 0xD9, 0xD6, 0x2A, 0x8F, 0x47, 0x4E, 0x82,
-0xAA, 0x53, 0x0E, 0xE6, 0x9E, 0x42, 0x1C, 0x48,
-0x1C, 0x69, 0xC4, 0xDA, 0x42, 0x6E, 0xF5, 0x07,
-0xF1, 0x08, 0x00, 0xCB, 0x40, 0xF7, 0x1B, 0xBD,
-0x67, 0xB4, 0xEC, 0x53, 0x14, 0xE9, 0x74, 0xAB,
-0x47, 0x2C, 0x96, 0xB5, 0xBD, 0x22, 0x40, 0xA5,
-0xFD, 0xE1, 0x01, 0x12, 0x99, 0xCC, 0x4A, 0x67,
-0xFC, 0xC9, 0xB0, 0xA5, 0xF4, 0x62, 0x58, 0x44,
-0x84, 0x06, 0x73, 0x5C, 0x6C, 0xDE, 0xC4, 0x92,
-0xE2, 0x59, 0xD9, 0x3D, 0x99, 0x72, 0xFA, 0x49,
-0x24, 0xA2, 0xB5, 0x59, 0xE5, 0x2D, 0x28, 0x55,
-0x54, 0xAF, 0x8A, 0xD2, 0x52, 0x9D, 0xD1, 0x1E,
-0x69, 0x4E, 0x36, 0x89, 0xC5, 0x38, 0xDD, 0xBB,
-0xE8, 0xC7, 0x71, 0x42, 0x63, 0xA5, 0xC4, 0xEB,
-0xEF, 0xFB, 0x83, 0x24, 0x78, 0xA6, 0x4B, 0x86,
-0x26, 0x47, 0x8D, 0xCE, 0xA7, 0x01, 0x6B, 0x11,
-0xFB, 0x76, 0x74, 0xF1, 0xAA, 0x97, 0x94, 0x5A,
-0xC4, 0x7E, 0xDD, 0x9D, 0x3C, 0x6A, 0xA5, 0xE5,
-0x0C, 0xC2, 0x00, 0x33, 0xAC, 0x47, 0xED, 0xD9,
-0xD3, 0xC6, 0xAA, 0x5E, 0x50, 0x67, 0x35, 0xC6,
-0xCD, 0xEC, 0x49, 0x2E, 0x25, 0x9D, 0x93, 0xD9,
-0x97, 0x2F, 0xA4, 0x92, 0x4A, 0x2B, 0x55, 0x9E,
-0x52, 0xD2, 0x85, 0x55, 0x4A, 0xF8, 0xAD, 0x25,
-0x29, 0xDD, 0x11, 0xE6, 0x94, 0xE3, 0x68, 0x9C,
-0x53, 0x8D, 0xDB, 0xBE, 0x8C, 0x77, 0x14, 0x6B,
-0x20, 0x33, 0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E,
-0x75, 0x38, 0x0C, 0xCD, 0x3E, 0x76, 0x89, 0xBB,
-0x97, 0xF4, 0x3B, 0x4D, 0x5D, 0xD6, 0x86, 0xD4,
-0x5B, 0xAC, 0x9F, 0xC6, 0x90, 0x2F, 0xDA, 0xA9,
-0x59, 0xE9, 0xFC, 0xD1, 0x09, 0x42, 0x8C, 0x0C,
-0xDF, 0xBE, 0x9E, 0xCD, 0xC5, 0x1A, 0x67, 0x58,
-0x8F, 0xDB, 0xB3, 0xA7, 0x8D, 0x54, 0xBC, 0xA3,
-0x8C, 0xFE, 0xD0, 0x76, 0x16, 0xFF, 0x00, 0x76,
-0x0A, 0xAD, 0xAD, 0xE9, 0x66, 0xD1, 0x5A, 0x7D,
-0x52, 0xCF, 0x4E, 0xD5, 0x6A, 0x4E, 0xAC, 0x8B,
-0xD3, 0xA4, 0x4A, 0x14, 0x61, 0x1D, 0xC7, 0x47,
-0x76, 0xCD, 0xE2, 0x7D, 0xAA, 0xAF, 0xD9, 0xDA,
-0xBB, 0x09, 0x5D, 0xB5, 0xD7, 0xB5, 0xEB, 0x77,
-0x54, 0xF5, 0x4D, 0x12, 0x52, 0x43, 0x59, 0x58,
-0x9D, 0x1A, 0x2F, 0x4F, 0x9D, 0x08, 0x53, 0x8E,
-0xE2, 0xC6, 0x10, 0xF7, 0x80, 0xEC, 0xF5, 0x88,
-0xFD, 0xBB, 0x3A, 0x78, 0xD5, 0x4B, 0xCA, 0x2D,
-0x62, 0x3F, 0x6E, 0xCE, 0x9E, 0x35, 0x52, 0xF2,
-0x8C, 0x67, 0xCA, 0x8D, 0xFB, 0x7B, 0x73, 0xDD,
-0x2A, 0x5F, 0x04, 0x5C, 0xA8, 0xDF, 0xB7, 0xB7,
-0x3D, 0xD2, 0xA5, 0xF0, 0x40, 0x6C, 0xCD, 0x62,
-0x3F, 0x6E, 0xCE, 0x9E, 0x35, 0x52, 0xF2, 0x8B,
-0x58, 0x8F, 0xDB, 0xB3, 0xA7, 0x8D, 0x54, 0xBC,
-0xA3, 0x33, 0x3B, 0x27, 0xA5, 0x3B, 0x17, 0x95,
-0x78, 0x68, 0x54, 0xBB, 0x7A, 0xDD, 0xD5, 0x56,
-0xBE, 0xA9, 0x25, 0xA1, 0xAB, 0xAC, 0xA7, 0x43,
-0xE7, 0x4C, 0x36, 0x31, 0xA0, 0x7E, 0xE8, 0xC2,
-0x1B, 0x7E, 0x81, 0xD9, 0xFC, 0xBB, 0x3F, 0x77,
-0x28, 0x06, 0x6D, 0x62, 0x3F, 0x6E, 0xCE, 0x9E,
-0x35, 0x52, 0xF2, 0x83, 0x39, 0xAE, 0x36, 0x6F,
-0x62, 0x49, 0x71, 0x2C, 0xEC, 0x9E, 0xCC, 0xB9,
-0x7D, 0x24, 0x92, 0x51, 0x5A, 0xAC, 0xF2, 0x96,
-0x94, 0x2A, 0xAA, 0x57, 0xC5, 0x69, 0x29, 0x4E,
-0xE8, 0x8F, 0x34, 0xA7, 0x1B, 0x44, 0xE2, 0x9C,
-0x6E, 0xDD, 0xF4, 0x63, 0xB8, 0xA3, 0xC5, 0xF9,
-0x76, 0x7E, 0xEE, 0x51, 0xC6, 0x39, 0x2E, 0x56,
-0x3A, 0xB0, 0x92, 0x35, 0x69, 0xFE, 0x53, 0xE9,
-0xAC, 0x1F, 0xE1, 0x7F, 0xEB, 0xA4, 0xAC, 0xF9,
-0xFE, 0x93, 0xE7, 0x2B, 0x3D, 0x2F, 0xFA, 0xD9,
-0x00, 0x1B, 0xFC, 0x42, 0x10, 0x0C, 0x9A, 0xD4,
-0xBE, 0x39, 0x09, 0xCF, 0xBF, 0x67, 0xD5, 0x28,
-0x4A, 0x08, 0x6D, 0xF2, 0xB2, 0xE5, 0xC3, 0x76,
-0xC9, 0xB4, 0x8F, 0x47, 0x6B, 0xA0, 0xAA, 0x42,
-0x25, 0xE9, 0x48, 0x8C, 0xF3, 0x4C, 0xA0, 0x6A,
-0x42, 0x1D, 0xCE, 0x84, 0x61, 0x02, 0x6D, 0xDC,
-0x64, 0xE4, 0xA7, 0x5B, 0xAB, 0x57, 0x61, 0x24,
-0x31, 0x5A, 0x05, 0x7A, 0xDD, 0xD5, 0xDD, 0x6E,
-0xF7, 0xA9, 0xAC, 0xAC, 0x4E, 0x91, 0x2F, 0xA1,
-0x52, 0x74, 0x21, 0x4E, 0x1B, 0xCB, 0x18, 0x47,
-0xDC, 0x34, 0xCC, 0xF6, 0xB0, 0xC4, 0xD7, 0x70,
-0x59, 0xD4, 0x02, 0x99, 0x2E, 0x18, 0x99, 0x1E,
-0x37, 0x3A, 0x9C, 0x00, 0xCF, 0x2E, 0x7F, 0xB2,
-0xEE, 0xFF, 0x00, 0xFD, 0x38, 0xB9, 0x73, 0xFD,
-0x97, 0x77, 0xFF, 0x00, 0xE9, 0xC6, 0xCC, 0x10,
-0x0C, 0x67, 0xCB, 0x9F, 0xEC, 0xBB, 0xBF, 0xFF,
-0x00, 0x4E, 0x38, 0xC7, 0x25, 0x3A, 0xDD, 0x5A,
-0xBB, 0x09, 0x21, 0x8A, 0xD0, 0x2B, 0xD6, 0xEE,
-0xAE, 0xEB, 0x77, 0xBD, 0x4D, 0x65, 0x62, 0x74,
-0x89, 0x7D, 0x0A, 0x93, 0xA1, 0x0A, 0x70, 0xDE,
-0x58, 0xC2, 0x3E, 0xE1, 0xBF, 0xC0, 0xCC, 0xC9,
-0x70, 0xC4, 0xC8, 0xF1, 0xB9, 0xD4, 0xE0, 0x33,
-0x33, 0xED, 0x9D, 0x6E, 0xB2, 0x9D, 0x84, 0xAE,
-0xC5, 0x68, 0x15, 0xD5, 0x78, 0xD4, 0xF5, 0xBB,
-0xDE, 0xBA, 0xAE, 0xAD, 0x3A, 0x34, 0xBE, 0x85,
-0x49, 0xB1, 0x8D, 0x08, 0x6F, 0x24, 0x23, 0x1F,
-0x70, 0x9F, 0x6C, 0xEB, 0x75, 0x94, 0xEC, 0x25,
-0x76, 0x2B, 0x40, 0xAE, 0xAB, 0xC6, 0xA7, 0xAD,
-0xDE, 0xF5, 0xD5, 0x75, 0x69, 0xD1, 0xA5, 0xF4,
-0x2A, 0x4D, 0x8C, 0x68, 0x43, 0x79, 0x21, 0x18,
-0xFB, 0x86, 0x99, 0x9E, 0xD6, 0x18, 0x9A, 0xEE,
-0x0B, 0x3A, 0x80, 0x53, 0xDA, 0xC3, 0x13, 0x5D,
-0xC1, 0x67, 0x50, 0x00, 0xCC, 0xCE, 0x4A, 0x75,
-0xBA, 0xB5, 0x76, 0x12, 0x43, 0x15, 0xA0, 0x57,
-0xAD, 0xDD, 0x5D, 0xD6, 0xEF, 0x7A, 0x9A, 0xCA,
-0xC4, 0xE9, 0x12, 0xFA, 0x15, 0x27, 0x42, 0x14,
-0xE1, 0xBC, 0xB1, 0x84, 0x7D, 0xC3, 0xB3, 0xE5,
-0xCF, 0xF6, 0x5D, 0xDF, 0xFF, 0x00, 0xA7, 0x0C,
-0xD3, 0x25, 0xC3, 0x13, 0x23, 0xC6, 0xE7, 0x53,
-0x86, 0x60, 0x18, 0x01, 0x92, 0x9D, 0x6D, 0xC0,
-0xF3, 0xDB, 0x76, 0xD7, 0x40, 0xAD, 0x3A, 0x55,
-0x60, 0xEA, 0x97, 0xBD, 0x0B, 0x2D, 0x95, 0x01,
-0x51, 0x7A, 0x75, 0x25, 0xA7, 0x4A, 0x31, 0xDC,
-0x6C, 0x37, 0x6D, 0xDE, 0x3B, 0x3E, 0x5C, 0xFF,
-0x00, 0x65, 0xDD, 0xFF, 0x00, 0xFA, 0x70, 0xCC,
-0xE9, 0x71, 0x3A, 0xFB, 0xFE, 0xE0, 0xC9, 0x1E,
-0x19, 0x80, 0x63, 0x3E, 0x5C, 0xFF, 0x00, 0x65,
-0xDD, 0xFF, 0x00, 0xFA, 0x71, 0xC6, 0x39, 0x29,
-0xD6, 0xEA, 0xD5, 0xD8, 0x49, 0x0C, 0x56, 0x81,
-0x5E, 0xB7, 0x75, 0x77, 0x5B, 0xBD, 0xEA, 0x6B,
-0x2B, 0x13, 0xA4, 0x4B, 0xE8, 0x54, 0x9D, 0x08,
-0x53, 0x86, 0xF2, 0xC6, 0x11, 0xF7, 0x0D, 0xFE,
-0x06, 0x66, 0x4B, 0x86, 0x26, 0x47, 0x8D, 0xCE,
-0xA7, 0x00, 0xCC, 0x21, 0x08, 0x00, 0xCC, 0xF6,
-0xB0, 0xC4, 0xD7, 0x70, 0x59, 0xD4, 0x02, 0x99,
-0x2E, 0x18, 0x99, 0x1E, 0x37, 0x3A, 0x9C, 0x53,
-0xDA, 0xC3, 0x13, 0x5D, 0xC1, 0x67, 0x50, 0x0A,
-0x64, 0xB8, 0x62, 0x64, 0x78, 0xDC, 0xEA, 0x70,
-0x0C, 0xC2, 0x10, 0x80, 0x40, 0x66, 0x64, 0xB8,
-0x62, 0x64, 0x78, 0xDC, 0xEA, 0x70, 0xCC, 0x06,
-0x66, 0x4B, 0x86, 0x26, 0x47, 0x8D, 0xCE, 0xA7,
-0x01, 0x4F, 0x6B, 0x0C, 0x4D, 0x77, 0x05, 0x9D,
-0x40, 0x29, 0xED, 0x61, 0x89, 0xAE, 0xE0, 0xB3,
-0xA8, 0x05, 0x3D, 0xAC, 0x31, 0x35, 0xDC, 0x16,
-0x75, 0x00, 0xA7, 0xB5, 0x86, 0x26, 0xBB, 0x82,
-0xCE, 0xA0, 0x01, 0x4C, 0x97, 0x0C, 0x4C, 0x8F,
-0x1B, 0x9D, 0x4E, 0x19, 0x86, 0x4D, 0x9A, 0xE3,
-0xFB, 0x74, 0xEC, 0x5B, 0x89, 0x67, 0x59, 0x96,
-0x99, 0xAB, 0xB0, 0x4A, 0xCA, 0x76, 0xAB, 0x42,
-0xBD, 0xDE, 0xB4, 0x92, 0x85, 0x35, 0xA4, 0xA7,
-0x9B, 0xCE, 0x31, 0x19, 0x4D, 0x2C, 0x4D, 0x38,
-0xD2, 0xEC, 0x29, 0x77, 0xFA, 0xC2, 0x67, 0x2A,
-0x37, 0x13, 0xED, 0xCF, 0x74, 0xAE, 0xFC, 0x10,
-0x03, 0x33, 0x80, 0xFA, 0xCE, 0xFE, 0x13, 0xFC,
-0xB0, 0xCD, 0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E,
-0x75, 0x38, 0x00, 0x79, 0x2D, 0x4C, 0x84, 0xDA,
-0x33, 0x13, 0x91, 0x69, 0x99, 0x95, 0xEB, 0x7C,
-0x92, 0xB9, 0xA2, 0xF6, 0x75, 0x8A, 0xA3, 0xD1,
-0xD3, 0xA0, 0x79, 0xA6, 0x1D, 0xCD, 0x3C, 0x84,
-0x38, 0x90, 0x38, 0xD3, 0x89, 0xB4, 0x84, 0xDD,
-0xEA, 0x0F, 0xF3, 0x25, 0xC3, 0x13, 0x23, 0xC6,
-0xE7, 0x53, 0x80, 0x66, 0x03, 0x33, 0x25, 0xC3,
-0x13, 0x23, 0xC6, 0xE7, 0x53, 0x86, 0x60, 0x33,
-0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E, 0x75, 0x38,
-0x06, 0x61, 0x08, 0x40, 0x06, 0x67, 0xB5, 0x86,
-0x26, 0xBB, 0x82, 0xCE, 0xA0, 0x14, 0xC9, 0x70,
-0xC4, 0xC8, 0xF1, 0xB9, 0xD4, 0xE2, 0x9E, 0xD6,
-0x18, 0x9A, 0xEE, 0x0B, 0x3A, 0x80, 0x53, 0x25,
-0xC3, 0x13, 0x23, 0xC6, 0xE7, 0x53, 0x80, 0x66,
-0x10, 0x84, 0x02, 0x03, 0x33, 0x25, 0xC3, 0x13,
-0x23, 0xC6, 0xE7, 0x53, 0x86, 0x60, 0x33, 0x32,
-0x5C, 0x31, 0x32, 0x3C, 0x6E, 0x75, 0x38, 0x0A,
-0x7B, 0x58, 0x62, 0x6B, 0xB8, 0x2C, 0xEA, 0x01,
-0x4F, 0x6B, 0x0C, 0x4D, 0x77, 0x05, 0x9D, 0x40,
-0x29, 0xED, 0x61, 0x89, 0xAE, 0xE0, 0xB3, 0xA8,
-0x05, 0x3D, 0xAC, 0x31, 0x35, 0xDC, 0x16, 0x75,
-0x00, 0x06, 0x61, 0x08, 0x40, 0x31, 0x9C, 0xEB,
-0x65, 0x86, 0xEE, 0x5F, 0xD7, 0x2C, 0x93, 0xA6,
-0x36, 0x66, 0x4D, 0x95, 0xB8, 0xFF, 0x00, 0x82,
-0xDD, 0x88, 0x0F, 0xB5, 0x5A, 0xAA, 0x4E, 0xF9,
-0xF8, 0x11, 0x21, 0x94, 0x52, 0x12, 0x9E, 0xF3,
-0xA3, 0xBB, 0x61, 0x07, 0xB5, 0x35, 0xC6, 0x31,
-0xEC, 0x4A, 0xCE, 0x25, 0x9D, 0x94, 0x19, 0x97,
-0xD1, 0xA3, 0x72, 0x4A, 0x5B, 0x55, 0x9E, 0x4D,
-0xD1, 0x75, 0x55, 0xBA, 0x88, 0x2D, 0x25, 0x21,
-0xDD, 0x29, 0xE7, 0x10, 0xE3, 0xA9, 0x1C, 0x43,
-0x8E, 0xDB, 0xBA, 0x94, 0x37, 0x10, 0x78, 0xB3,
-0x80, 0xFA, 0xCE, 0xFE, 0x13, 0xFC, 0xB0, 0xCD,
-0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E, 0x75, 0x38,
-0x0B, 0x57, 0x6F, 0xDB, 0xB4, 0x5F, 0x82, 0x94,
-0xBC, 0xC0, 0xCE, 0x6B, 0x8C, 0x63, 0xD8, 0x95,
-0x9C, 0x4B, 0x3B, 0x28, 0x33, 0x2F, 0xA3, 0x46,
-0xE4, 0x94, 0xB6, 0xAB, 0x3C, 0x9B, 0xA2, 0xEA,
-0xAB, 0x75, 0x10, 0x5A, 0x4A, 0x43, 0xBA, 0x53,
-0xCE, 0x21, 0xC7, 0x52, 0x38, 0x87, 0x1D, 0xB7,
-0x75, 0x28, 0x6E, 0x20, 0xD6, 0x40, 0x66, 0x64,
-0xB8, 0x62, 0x64, 0x78, 0xDC, 0xEA, 0x70, 0x16,
-0xB1, 0x1F, 0xB7, 0x67, 0x4F, 0x1A, 0xA9, 0x79,
-0x45, 0xAC, 0x47, 0xED, 0xD9, 0xD3, 0xC6, 0xAA,
-0x5E, 0x50, 0xCC, 0x20, 0x19, 0x36, 0x74, 0x6D,
-0x9B, 0xD8, 0x95, 0x9C, 0x4B, 0x45, 0x27, 0xB4,
-0xCE, 0x5F, 0x46, 0xE4, 0x94, 0xB6, 0x5B, 0x44,
-0xA5, 0xA5, 0x0A, 0xAB, 0x75, 0x10, 0x5A, 0x44,
-0x53, 0x7A, 0x23, 0x0D, 0x21, 0xC7, 0x52, 0x38,
-0x86, 0x9B, 0xB3, 0x75, 0x28, 0xEE, 0x20, 0xA6,
-0xB8, 0xD9, 0xBD, 0x89, 0x25, 0xC4, 0xB3, 0xB2,
-0x7B, 0x32, 0xE5, 0xF4, 0x92, 0x49, 0x45, 0x6A,
-0xB3, 0xCA, 0x5A, 0x50, 0xAA, 0xA9, 0x5F, 0x15,
-0xA4, 0xA5, 0x3B, 0xA2, 0x3C, 0xD2, 0x9C, 0x6D,
-0x13, 0x8A, 0x71, 0xBB, 0x77, 0xD1, 0x8E, 0xE2,
-0x84, 0xC9, 0xED, 0x61, 0x89, 0xAE, 0xE0, 0xB3,
-0xA8, 0x05, 0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E,
-0x75, 0x38, 0x0B, 0x58, 0x8F, 0xDB, 0xB3, 0xA7,
-0x8D, 0x54, 0xBC, 0xA2, 0xD6, 0x23, 0xF6, 0xEC,
-0xE9, 0xE3, 0x55, 0x2F, 0x28, 0x66, 0x10, 0x01,
-0x9D, 0x62, 0x3F, 0x6E, 0xCE, 0x9E, 0x35, 0x52,
-0xF2, 0x8F, 0x6A, 0x6B, 0x8C, 0xB4, 0xBA, 0xC5,
-0xB8, 0x96, 0x75, 0x99, 0x69, 0x94, 0x6C, 0x12,
-0xB2, 0x9D, 0xAA, 0xD0, 0xAF, 0x5A, 0x62, 0x4A,
-0x14, 0xD6, 0x92, 0x9E, 0x6F, 0x38, 0xC2, 0x94,
-0xD2, 0xC4, 0xD3, 0x8D, 0x2E, 0xC2, 0x97, 0x7F,
-0xAC, 0x26, 0x08, 0x00, 0xCC, 0xF6, 0xB0, 0xC4,
-0xD7, 0x70, 0x59, 0xD4, 0x02, 0x9E, 0xD6, 0x18,
-0x9A, 0xEE, 0x0B, 0x3A, 0x80, 0x53, 0xDA, 0xC3,
-0x13, 0x5D, 0xC1, 0x67, 0x50, 0x0A, 0x7B, 0x58,
-0x62, 0x6B, 0xB8, 0x2C, 0xEA, 0x00, 0x0C, 0xC2,
-0x10, 0x80, 0x63, 0x39, 0xC0, 0x7D, 0x67, 0x7F,
-0x09, 0xFE, 0x58, 0x66, 0x99, 0x2E, 0x18, 0x99,
-0x1E, 0x37, 0x3A, 0x9C, 0x0C, 0xCE, 0x03, 0xEB,
-0x3B, 0xF8, 0x4F, 0xF2, 0xC3, 0x34, 0xC9, 0x70,
-0xC4, 0xC8, 0xF1, 0xB9, 0xD4, 0xE0, 0x19, 0x80,
-0xCC, 0xC9, 0x70, 0xC4, 0xC8, 0xF1, 0xB9, 0xD4,
-0xE1, 0x98, 0x0C, 0xCC, 0x97, 0x0C, 0x4C, 0x8F,
-0x1B, 0x9D, 0x4E, 0x03, 0xFF, 0xD9};
-static const int goodJpegImageWidth = 128;
-static const int goodJpegImageHeight = 128;
-
-// https://code.google.com/p/android/issues/detail?id=42382
-// https://code.google.com/p/android/issues/detail?id=9064
-// https://code.google.com/p/skia/issues/detail?id=1649
-
-/**
-  This test will test the ability of the SkImageDecoder to deal with
-  Jpeg files which have been mangled somehow.  We want to display as
-  much of the jpeg as possible.
-*/
-DEF_TEST(Jpeg, reporter) {
-    size_t len = sizeof(goodJpegImage) / 2;
-    // I am explicitly not putting the entire image into the
-    // DecodeMemory.  This simulates a network error.
-
-    SkBitmap bm8888;
-    bool imageDecodeSuccess = SkImageDecoder::DecodeMemory(
-        static_cast<void *>(goodJpegImage), len, &bm8888);
-    REPORTER_ASSERT(reporter, imageDecodeSuccess);
-    REPORTER_ASSERT(reporter, bm8888.width() == goodJpegImageWidth);
-    REPORTER_ASSERT(reporter, bm8888.height() == goodJpegImageHeight);
-    REPORTER_ASSERT(reporter, !(bm8888.empty()));
-
-    // Pick a few pixels and verify that their colors match the colors
-    // we expect (given the original image).
-    REPORTER_ASSERT(reporter, bm8888.getColor(7, 9) == 0xffffffff);
-    REPORTER_ASSERT(reporter, bm8888.getColor(28, 3) == 0xff000000);
-    REPORTER_ASSERT(reporter, bm8888.getColor(27, 34) == 0xffffffff);
-    REPORTER_ASSERT(reporter, bm8888.getColor(71, 18) == 0xff000000);
-
-#ifdef SK_BUILD_FOR_IOS  // the iOS jpeg decoder fills to gray
-    REPORTER_ASSERT(reporter, bm8888.getColor(127, 127) == 0xff808080
-            || bm8888.getColor(127, 127) == SK_ColorWHITE);
-#else
-    // This is the fill color
-    REPORTER_ASSERT(reporter, bm8888.getColor(127, 127) == SK_ColorWHITE);
-#endif
-
-    #if JPEG_TEST_WRITE_TO_FILE_FOR_DEBUGGING
-    // Check to see that the resulting bitmap is nice
-    bool writeSuccess = (!(bm8888.empty())) && SkImageEncoder::EncodeFile(
-        "HalfOfAJpeg.png", bm8888, SkImageEncoder::kPNG_Type, 100);
-    SkASSERT(writeSuccess);
-    #endif
-}
diff --git a/src/third_party/skia/tests/KtxTest.cpp b/src/third_party/skia/tests/KtxTest.cpp
deleted file mode 100644
index 1a61f3a..0000000
--- a/src/third_party/skia/tests/KtxTest.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Resources.h"
-#include "SkBitmap.h"
-#include "SkData.h"
-#include "SkDecodingImageGenerator.h"
-#include "SkForceLinking.h"
-#include "SkImageDecoder.h"
-#include "SkOSFile.h"
-#include "SkRandom.h"
-#include "SkStream.h"
-#include "Test.h"
-
-__SK_FORCE_IMAGE_DECODER_LINKING;
-
-/**
- * First, make sure that writing an 8-bit RGBA KTX file and then
- * reading it produces the same bitmap.
- */
-DEF_TEST(KtxReadWrite, reporter) {
-
-    // Random number generator with explicit seed for reproducibility
-    SkRandom rand(0x1005cbad);
-
-    SkBitmap bm8888;
-    bm8888.allocN32Pixels(128, 128);
-
-    uint8_t *pixels = reinterpret_cast<uint8_t*>(bm8888.getPixels());
-    REPORTER_ASSERT(reporter, pixels);
-
-    if (NULL == pixels) {
-        return;
-    }
-    
-    uint8_t *row = pixels;
-    for (int y = 0; y < bm8888.height(); ++y) {        
-        for (int x = 0; x < bm8888.width(); ++x) {
-            uint8_t a = rand.nextRangeU(0, 255);
-            uint8_t r = rand.nextRangeU(0, 255);
-            uint8_t g = rand.nextRangeU(0, 255);
-            uint8_t b = rand.nextRangeU(0, 255);
-
-            SkPMColor &pixel = *(reinterpret_cast<SkPMColor*>(row + x*sizeof(SkPMColor)));
-            pixel = SkPreMultiplyARGB(a, r, g, b);
-        }
-        row += bm8888.rowBytes();
-    }
-    REPORTER_ASSERT(reporter, !(bm8888.empty()));
-
-    SkAutoDataUnref encodedData(SkImageEncoder::EncodeData(bm8888, SkImageEncoder::kKTX_Type, 0));
-    REPORTER_ASSERT(reporter, encodedData);
-
-    SkAutoTUnref<SkMemoryStream> stream(SkNEW_ARGS(SkMemoryStream, (encodedData)));
-    REPORTER_ASSERT(reporter, stream);
-
-    SkBitmap decodedBitmap;
-    bool imageDecodeSuccess = SkImageDecoder::DecodeStream(stream, &decodedBitmap);
-    REPORTER_ASSERT(reporter, imageDecodeSuccess);
-
-    REPORTER_ASSERT(reporter, decodedBitmap.colorType() == bm8888.colorType());
-    REPORTER_ASSERT(reporter, decodedBitmap.alphaType() == bm8888.alphaType());
-    REPORTER_ASSERT(reporter, decodedBitmap.width() == bm8888.width());
-    REPORTER_ASSERT(reporter, decodedBitmap.height() == bm8888.height());
-    REPORTER_ASSERT(reporter, !(decodedBitmap.empty()));
-
-    uint8_t *decodedPixels = reinterpret_cast<uint8_t*>(decodedBitmap.getPixels());
-    REPORTER_ASSERT(reporter, decodedPixels);
-    REPORTER_ASSERT(reporter, decodedBitmap.getSize() == bm8888.getSize());
-
-    if (NULL == decodedPixels) {
-        return;
-    }
-
-    REPORTER_ASSERT(reporter, memcmp(decodedPixels, pixels, decodedBitmap.getSize()) == 0);
-}
-
-/**
- * Next test is to see whether or not reading an unpremultiplied KTX file accurately
- * creates a premultiplied buffer...
- */
-DEF_TEST(KtxReadUnpremul, reporter) {
-
-    static const uint8_t kHalfWhiteKTX[] = {
-        0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, // First twelve bytes is magic
-        0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A, // KTX identifier string
-        0x01, 0x02, 0x03, 0x04, // Then magic endian specifier
-        0x01, 0x14, 0x00, 0x00, // uint32_t fGLType;
-        0x01, 0x00, 0x00, 0x00, // uint32_t fGLTypeSize;
-        0x08, 0x19, 0x00, 0x00, // uint32_t fGLFormat;
-        0x58, 0x80, 0x00, 0x00, // uint32_t fGLInternalFormat;
-        0x08, 0x19, 0x00, 0x00, // uint32_t fGLBaseInternalFormat;
-        0x02, 0x00, 0x00, 0x00, // uint32_t fPixelWidth;
-        0x02, 0x00, 0x00, 0x00, // uint32_t fPixelHeight;
-        0x00, 0x00, 0x00, 0x00, // uint32_t fPixelDepth;
-        0x00, 0x00, 0x00, 0x00, // uint32_t fNumberOfArrayElements;
-        0x01, 0x00, 0x00, 0x00, // uint32_t fNumberOfFaces;
-        0x01, 0x00, 0x00, 0x00, // uint32_t fNumberOfMipmapLevels;
-        0x00, 0x00, 0x00, 0x00, // uint32_t fBytesOfKeyValueData;
-        0x10, 0x00, 0x00, 0x00, // image size: 2x2 image of RGBA = 4 * 4 = 16 bytes
-        0xFF, 0xFF, 0xFF, 0x80, // Pixel 1
-        0xFF, 0xFF, 0xFF, 0x80, // Pixel 2
-        0xFF, 0xFF, 0xFF, 0x80, // Pixel 3
-        0xFF, 0xFF, 0xFF, 0x80};// Pixel 4
-
-    SkAutoTUnref<SkMemoryStream> stream(
-        SkNEW_ARGS(SkMemoryStream, (kHalfWhiteKTX, sizeof(kHalfWhiteKTX))));
-    REPORTER_ASSERT(reporter, stream);
-
-    SkBitmap decodedBitmap;
-    bool imageDecodeSuccess = SkImageDecoder::DecodeStream(stream, &decodedBitmap);
-    REPORTER_ASSERT(reporter, imageDecodeSuccess);
-
-    REPORTER_ASSERT(reporter, decodedBitmap.colorType() == kN32_SkColorType);
-    REPORTER_ASSERT(reporter, decodedBitmap.alphaType() == kPremul_SkAlphaType);
-    REPORTER_ASSERT(reporter, decodedBitmap.width() == 2);
-    REPORTER_ASSERT(reporter, decodedBitmap.height() == 2);
-    REPORTER_ASSERT(reporter, !(decodedBitmap.empty()));
-
-    uint8_t *decodedPixels = reinterpret_cast<uint8_t*>(decodedBitmap.getPixels());
-    REPORTER_ASSERT(reporter, decodedPixels);
-
-    uint8_t *row = decodedPixels;
-    for (int j = 0; j < decodedBitmap.height(); ++j) {
-        for (int i = 0; i < decodedBitmap.width(); ++i) {
-            SkPMColor pixel = *(reinterpret_cast<SkPMColor*>(row + i*sizeof(SkPMColor)));
-            REPORTER_ASSERT(reporter, SkPreMultiplyARGB(0x80, 0xFF, 0xFF, 0xFF) == pixel);
-        }
-        row += decodedBitmap.rowBytes();
-    }
-}
-
-/**
- * Finally, make sure that if we get ETC1 data from a PKM file that we can then
- * accurately write it out into a KTX file (i.e. transferring the ETC1 data from
- * the PKM to the KTX should produce an identical KTX to the one we have on file)
- */
-DEF_TEST(KtxReexportPKM, reporter) {
-    SkString pkmFilename = GetResourcePath("mandrill_128.pkm");
-
-    // Load PKM file into a bitmap
-    SkBitmap etcBitmap;
-    SkAutoTUnref<SkData> fileData(SkData::NewFromFileName(pkmFilename.c_str()));
-    REPORTER_ASSERT(reporter, fileData);
-    if (NULL == fileData) {
-        return;
-    }
-
-    bool installDiscardablePixelRefSuccess =
-        SkInstallDiscardablePixelRef(
-            SkDecodingImageGenerator::Create(
-                fileData, SkDecodingImageGenerator::Options()), &etcBitmap);
-    REPORTER_ASSERT(reporter, installDiscardablePixelRefSuccess);
-
-    // Write the bitmap out to a KTX file.
-    SkData *ktxDataPtr = SkImageEncoder::EncodeData(etcBitmap, SkImageEncoder::kKTX_Type, 0);
-    SkAutoDataUnref newKtxData(ktxDataPtr);
-    REPORTER_ASSERT(reporter, ktxDataPtr);
-
-    // See is this data is identical to data in existing ktx file.
-    SkString ktxFilename = GetResourcePath("mandrill_128.ktx");
-    SkAutoDataUnref oldKtxData(SkData::NewFromFileName(ktxFilename.c_str()));
-    REPORTER_ASSERT(reporter, oldKtxData->equals(newKtxData));
-}
diff --git a/src/third_party/skia/tests/LListTest.cpp b/src/third_party/skia/tests/LListTest.cpp
index 8fb0117..e28cad9 100644
--- a/src/third_party/skia/tests/LListTest.cpp
+++ b/src/third_party/skia/tests/LListTest.cpp
@@ -16,15 +16,10 @@
     }
     bool operator== (const ListElement& other) { return fID == other.fID; }
 
-#if SK_ENABLE_INST_COUNT
-    // Make the instance count available publicly.
-    static int InstanceCount() { return GetInstanceCount(); }
-#endif
-
     int fID;
 
 private:
-    SK_DECLARE_INST_COUNT_ROOT(ListElement);
+
     SK_DECLARE_INTERNAL_LLIST_INTERFACE(ListElement);
 };
 
@@ -46,7 +41,7 @@
 #endif
 }
 
-static void TestTInternalLList(skiatest::Reporter* reporter) {
+static void test_tinternallist(skiatest::Reporter* reporter) {
     SkTInternalLList<ListElement> list;
     ListElement elements[4] = {
         ListElement(0),
@@ -96,12 +91,12 @@
     check_list(list, reporter, true, 0, false, false, false, false, elements);
 
     // test out methods that add to the middle of the list.
-    list.addAfter(&elements[1], NULL);
+    list.addAfter(&elements[1], nullptr);
     check_list(list, reporter, false, 1, false, true, false, false, elements);
 
     list.remove(&elements[1]);
 
-    list.addBefore(&elements[1], NULL);
+    list.addBefore(&elements[1], nullptr);
     check_list(list, reporter, false, 1, false, true, false, false, elements);
 
     list.addBefore(&elements[0], &elements[1]);
@@ -117,200 +112,211 @@
     for (int i = 0; cur; ++i, cur = iter.next()) {
         REPORTER_ASSERT(reporter, cur->fID == i);
     }
+    while (!list.isEmpty()) {
+        list.remove(list.tail());
+    }
+
+    // test concat.
+    SkTInternalLList<ListElement> listA, listB;
+    listA.concat(std::move(listB));
+    check_list(listA, reporter, true, 0, false, false, false, false, elements);
+    check_list(listB, reporter, true, 0, false, false, false, false, elements);
+
+    listB.addToTail(&elements[0]);
+    listA.concat(std::move(listB));
+    check_list(listA, reporter, false, 1, true, false, false, false, elements);
+    check_list(listB, reporter, true, 0, false, false, false, false, elements);
+
+    listB.addToTail(&elements[1]);
+    listA.concat(std::move(listB));
+    check_list(listA, reporter, false, 2, true, true, false, false, elements);
+    check_list(listB, reporter, true, 0, false, false, false, false, elements);
+
+    listA.concat(std::move(listB));
+    check_list(listA, reporter, false, 2, true, true, false, false, elements);
+    check_list(listB, reporter, true, 0, false, false, false, false, elements);
+
+    listB.addToTail(&elements[2]);
+    listB.addToTail(&elements[3]);
+    listA.concat(std::move(listB));
+    check_list(listA, reporter, false, 4, true, true, true, true, elements);
+    check_list(listB, reporter, true, 0, false, false, false, false, elements);
+
+    cur = iter.init(listA, Iter::kHead_IterStart);
+    for (int i = 0; cur; ++i, cur = iter.next()) {
+        REPORTER_ASSERT(reporter, cur->fID == i);
+    }
 }
 
-static void TestTLList(skiatest::Reporter* reporter) {
-    typedef SkTLList<ListElement> ElList;
-    typedef ElList::Iter Iter;
+template <unsigned int N> static void test_tllist(skiatest::Reporter* reporter) {
+    typedef SkTLList<ListElement, N> ElList;
+    typedef typename ElList::Iter Iter;
     SkRandom random;
 
-    for (int i = 1; i <= 16; i *= 2) {
+    ElList list1;
+    ElList list2;
+    Iter iter1;
+    Iter iter2;
+    Iter iter3;
+    Iter iter4;
 
-        ElList list1(i);
-        ElList list2(i);
-        Iter iter1;
-        Iter iter2;
-        Iter iter3;
-        Iter iter4;
+    REPORTER_ASSERT(reporter, list1.isEmpty());
+    REPORTER_ASSERT(reporter, nullptr == iter1.init(list1, Iter::kHead_IterStart));
+    REPORTER_ASSERT(reporter, nullptr == iter1.init(list1, Iter::kTail_IterStart));
+    // Try popping an empty list
+    list1.popHead();
+    list1.popTail();
+    REPORTER_ASSERT(reporter, list1.isEmpty());
+    REPORTER_ASSERT(reporter, list1 == list2);
 
-#if SK_ENABLE_INST_COUNT
-        SkASSERT(0 == ListElement::InstanceCount());
-#endif
+    // Create two identical lists, one by appending to head and the other to the tail.
+    list1.addToHead(ListElement(1));
+    list2.addToTail(ListElement(1));
+    iter1.init(list1, Iter::kHead_IterStart);
+    iter2.init(list1, Iter::kTail_IterStart);
+    REPORTER_ASSERT(reporter, iter1.get()->fID == iter2.get()->fID);
+    iter3.init(list2, Iter::kHead_IterStart);
+    iter4.init(list2, Iter::kTail_IterStart);
+    REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID);
+    REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID);
+    REPORTER_ASSERT(reporter, list1 == list2);
 
-        REPORTER_ASSERT(reporter, list1.isEmpty());
-        REPORTER_ASSERT(reporter, NULL == iter1.init(list1, Iter::kHead_IterStart));
-        REPORTER_ASSERT(reporter, NULL == iter1.init(list1, Iter::kTail_IterStart));
-        // Try popping an empty list
-        list1.popHead();
-        list1.popTail();
-        REPORTER_ASSERT(reporter, list1.isEmpty());
-        REPORTER_ASSERT(reporter, list1 == list2);
+    list2.reset();
 
-        // Create two identical lists, one by appending to head and the other to the tail.
-        list1.addToHead(ListElement(1));
-        list2.addToTail(ListElement(1));
-#if SK_ENABLE_INST_COUNT
-        SkASSERT(2 == ListElement::InstanceCount());
-#endif
-        iter1.init(list1, Iter::kHead_IterStart);
-        iter2.init(list1, Iter::kTail_IterStart);
-        REPORTER_ASSERT(reporter, iter1.get()->fID == iter2.get()->fID);
-        iter3.init(list2, Iter::kHead_IterStart);
-        iter4.init(list2, Iter::kTail_IterStart);
-        REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID);
-        REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID);
-        REPORTER_ASSERT(reporter, list1 == list2);
+    // use both before/after in-place construction on an empty list
+    list2.addBefore(list2.headIter(), 1);
+    REPORTER_ASSERT(reporter, list2 == list1);
+    list2.reset();
 
-        list2.reset();
+    list2.addAfter(list2.tailIter(), 1);
+    REPORTER_ASSERT(reporter, list2 == list1);
 
-        // use both before/after in-place construction on an empty list
-        SkNEW_INSERT_IN_LLIST_BEFORE(&list2, list2.headIter(), ListElement, (1));
-        REPORTER_ASSERT(reporter, list2 == list1);
-        list2.reset();
+    // add an element to the second list, check that iters are still valid
+    iter3.init(list2, Iter::kHead_IterStart);
+    iter4.init(list2, Iter::kTail_IterStart);
+    list2.addToHead(ListElement(2));
 
-        SkNEW_INSERT_IN_LLIST_AFTER(&list2, list2.tailIter(), ListElement, (1));
-        REPORTER_ASSERT(reporter, list2 == list1);
+    REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID);
+    REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID);
+    REPORTER_ASSERT(reporter, 1 == Iter(list2, Iter::kTail_IterStart).get()->fID);
+    REPORTER_ASSERT(reporter, 2 == Iter(list2, Iter::kHead_IterStart).get()->fID);
+    REPORTER_ASSERT(reporter, list1 != list2);
+    list1.addToHead(ListElement(2));
+    REPORTER_ASSERT(reporter, list1 == list2);
+    REPORTER_ASSERT(reporter, !list1.isEmpty());
 
-        // add an element to the second list, check that iters are still valid
-        iter3.init(list2, Iter::kHead_IterStart);
-        iter4.init(list2, Iter::kTail_IterStart);
-        list2.addToHead(ListElement(2));
+    list1.reset();
+    list2.reset();
+    REPORTER_ASSERT(reporter, list1.isEmpty() && list2.isEmpty());
 
-#if SK_ENABLE_INST_COUNT
-        SkASSERT(3 == ListElement::InstanceCount());
-#endif
+    // randomly perform insertions and deletions on a list and perform tests
+    int count = 0;
+    for (int j = 0; j < 100; ++j) {
+        if (list1.isEmpty() || random.nextBiasedBool(3  * SK_Scalar1 / 4)) {
+            int id = j;
+            // Choose one of three ways to insert a new element: at the head, at the tail,
+            // before a random element, after a random element
+            int numValidMethods = 0 == count ? 2 : 4;
+            int insertionMethod = random.nextULessThan(numValidMethods);
+            switch (insertionMethod) {
+                case 0:
+                    list1.addToHead(ListElement(id));
+                    break;
+                case 1:
+                    list1.addToTail(ListElement(id));
+                    break;
+                case 2: // fallthru to share code that picks random element.
+                case 3: {
+                    int n = random.nextULessThan(list1.count());
+                    Iter iter = list1.headIter();
+                    // remember the elements before/after the insertion point.
+                    while (n--) {
+                        iter.next();
+                    }
+                    Iter prev(iter);
+                    Iter next(iter);
+                    next.next();
+                    prev.prev();
 
-        REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID);
-        REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID);
-        REPORTER_ASSERT(reporter, 1 == Iter(list2, Iter::kTail_IterStart).get()->fID);
-        REPORTER_ASSERT(reporter, 2 == Iter(list2, Iter::kHead_IterStart).get()->fID);
-        REPORTER_ASSERT(reporter, list1 != list2);
-        list1.addToHead(ListElement(2));
-        REPORTER_ASSERT(reporter, list1 == list2);
-#if SK_ENABLE_INST_COUNT
-        SkASSERT(4 == ListElement::InstanceCount());
-#endif
-        REPORTER_ASSERT(reporter, !list1.isEmpty());
+                    SkASSERT(iter.get());
+                    // insert either before or after the iterator, then check that the
+                    // surrounding sequence is correct.
+                    if (2 == insertionMethod) {
+                        list1.addBefore(iter, id);
+                        Iter newItem(iter);
+                        newItem.prev();
+                        REPORTER_ASSERT(reporter, newItem.get()->fID == id);
 
-        list1.reset();
-        list2.reset();
-#if SK_ENABLE_INST_COUNT
-        SkASSERT(0 == ListElement::InstanceCount());
-#endif
-        REPORTER_ASSERT(reporter, list1.isEmpty() && list2.isEmpty());
-
-        // randomly perform insertions and deletions on a list and perform tests
-        int count = 0;
-        for (int j = 0; j < 100; ++j) {
-            if (list1.isEmpty() || random.nextBiasedBool(3  * SK_Scalar1 / 4)) {
-                int id = j;
-                // Choose one of three ways to insert a new element: at the head, at the tail,
-                // before a random element, after a random element
-                int numValidMethods = 0 == count ? 2 : 4;
-                int insertionMethod = random.nextULessThan(numValidMethods);
-                switch (insertionMethod) {
-                    case 0:
-                        list1.addToHead(ListElement(id));
-                        break;
-                    case 1:
-                        list1.addToTail(ListElement(id));
-                        break;
-                    case 2: // fallthru to share code that picks random element.
-                    case 3: {
-                        int n = random.nextULessThan(list1.count());
-                        Iter iter = list1.headIter();
-                        // remember the elements before/after the insertion point.
-                        while (n--) {
-                            iter.next();
+                        if (next.get()) {
+                            REPORTER_ASSERT(reporter, next.prev()->fID == iter.get()->fID);
                         }
-                        Iter prev(iter);
-                        Iter next(iter);
-                        next.next();
-                        prev.prev();
+                        if (prev.get()) {
+                            REPORTER_ASSERT(reporter, prev.next()->fID == id);
+                        }
+                    } else {
+                        list1.addAfter(iter, id);
+                        Iter newItem(iter);
+                        newItem.next();
+                        REPORTER_ASSERT(reporter, newItem.get()->fID == id);
 
-                        SkASSERT(iter.get());
-                        // insert either before or after the iterator, then check that the
-                        // surrounding sequence is correct.
-                        if (2 == insertionMethod) {
-                            SkNEW_INSERT_IN_LLIST_BEFORE(&list1, iter, ListElement, (id));
-                            Iter newItem(iter);
-                            newItem.prev();
-                            REPORTER_ASSERT(reporter, newItem.get()->fID == id);
-
-                            if (next.get()) {
-                                REPORTER_ASSERT(reporter, next.prev()->fID == iter.get()->fID);
-                            }
-                            if (prev.get()) {
-                                REPORTER_ASSERT(reporter, prev.next()->fID == id);
-                            }
-                        } else {
-                            SkNEW_INSERT_IN_LLIST_AFTER(&list1, iter, ListElement, (id));
-                            Iter newItem(iter);
-                            newItem.next();
-                            REPORTER_ASSERT(reporter, newItem.get()->fID == id);
-
-                            if (next.get()) {
-                                REPORTER_ASSERT(reporter, next.prev()->fID == id);
-                            }
-                            if (prev.get()) {
-                                REPORTER_ASSERT(reporter, prev.next()->fID == iter.get()->fID);
-                            }
+                        if (next.get()) {
+                            REPORTER_ASSERT(reporter, next.prev()->fID == id);
+                        }
+                        if (prev.get()) {
+                            REPORTER_ASSERT(reporter, prev.next()->fID == iter.get()->fID);
                         }
                     }
                 }
-                ++count;
-            } else {
-                // walk to a random place either forward or backwards and remove.
-                int n = random.nextULessThan(list1.count());
-                Iter::IterStart start;
-                ListElement* (Iter::*incrFunc)();
-
-                if (random.nextBool()) {
-                    start = Iter::kHead_IterStart;
-                    incrFunc = &Iter::next;
-                } else {
-                    start = Iter::kTail_IterStart;
-                    incrFunc = &Iter::prev;
-                }
-
-                // find the element
-                Iter iter(list1, start);
-                while (n--) {
-                    REPORTER_ASSERT(reporter, iter.get());
-                    (iter.*incrFunc)();
-                }
-                REPORTER_ASSERT(reporter, iter.get());
-
-                // remember the prev and next elements from the element to be removed
-                Iter prev = iter;
-                Iter next = iter;
-                prev.prev();
-                next.next();
-                list1.remove(iter.get());
-
-                // make sure the remembered next/prev iters still work
-                Iter pn = prev; pn.next();
-                Iter np = next; np.prev();
-                // pn should match next unless the target node was the head, in which case prev
-                // walked off the list.
-                REPORTER_ASSERT(reporter, pn.get() == next.get() || NULL == prev.get());
-                // Similarly, np should match prev unless next originally walked off the tail.
-                REPORTER_ASSERT(reporter, np.get() == prev.get() || NULL == next.get());
-                --count;
             }
-            REPORTER_ASSERT(reporter, count == list1.count());
-#if SK_ENABLE_INST_COUNT
-            SkASSERT(count == ListElement::InstanceCount());
-#endif
+            ++count;
+        } else {
+            // walk to a random place either forward or backwards and remove.
+            int n = random.nextULessThan(list1.count());
+            typename Iter::IterStart start;
+            ListElement* (Iter::*incrFunc)();
+
+            if (random.nextBool()) {
+                start = Iter::kHead_IterStart;
+                incrFunc = &Iter::next;
+            } else {
+                start = Iter::kTail_IterStart;
+                incrFunc = &Iter::prev;
+            }
+
+            // find the element
+            Iter iter(list1, start);
+            while (n--) {
+                REPORTER_ASSERT(reporter, iter.get());
+                (iter.*incrFunc)();
+            }
+            REPORTER_ASSERT(reporter, iter.get());
+
+            // remember the prev and next elements from the element to be removed
+            Iter prev = iter;
+            Iter next = iter;
+            prev.prev();
+            next.next();
+            list1.remove(iter.get());
+
+            // make sure the remembered next/prev iters still work
+            Iter pn = prev; pn.next();
+            Iter np = next; np.prev();
+            // pn should match next unless the target node was the head, in which case prev
+            // walked off the list.
+            REPORTER_ASSERT(reporter, pn.get() == next.get() || nullptr == prev.get());
+            // Similarly, np should match prev unless next originally walked off the tail.
+            REPORTER_ASSERT(reporter, np.get() == prev.get() || nullptr == next.get());
+            --count;
         }
-        list1.reset();
-#if SK_ENABLE_INST_COUNT
-        SkASSERT(0 == ListElement::InstanceCount());
-#endif
+        REPORTER_ASSERT(reporter, count == list1.count());
     }
 }
 
 DEF_TEST(LList, reporter) {
-    TestTInternalLList(reporter);
-    TestTLList(reporter);
+    test_tinternallist(reporter);
+    test_tllist<1>(reporter);
+    test_tllist<3>(reporter);
+    test_tllist<8>(reporter);
+    test_tllist<10>(reporter);
+    test_tllist<16>(reporter);
 }
diff --git a/src/third_party/skia/tests/LRUCacheTest.cpp b/src/third_party/skia/tests/LRUCacheTest.cpp
new file mode 100644
index 0000000..6a65e4a
--- /dev/null
+++ b/src/third_party/skia/tests/LRUCacheTest.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkLRUCache.h"
+#include "Test.h"
+
+struct Value {
+    Value(int value, int* counter)
+    : fValue(value)
+    , fCounter(counter) {
+        (*fCounter)++;
+    }
+
+    ~Value() {
+        (*fCounter)--;
+    }
+
+    int fValue;
+    int* fCounter;
+};
+
+DEF_TEST(LRUCacheSequential, r) {
+    int instances = 0;
+    {
+        static const int kSize = 100;
+        SkLRUCache<int, std::unique_ptr<Value>> test(kSize);
+        for (int i = 1; i < kSize * 2; i++) {
+            REPORTER_ASSERT(r, !test.find(i));
+            test.insert(i, std::unique_ptr<Value>(new Value(i * i, &instances)));
+            REPORTER_ASSERT(r, test.find(i));
+            REPORTER_ASSERT(r, i * i == (*test.find(i))->fValue);
+            if (i > kSize) {
+                REPORTER_ASSERT(r, kSize == instances);
+                REPORTER_ASSERT(r, !test.find(i - kSize));
+            } else {
+                REPORTER_ASSERT(r, i == instances);
+            }
+            REPORTER_ASSERT(r, (int) test.count() == instances);
+        }
+    }
+    REPORTER_ASSERT(r, 0 == instances);
+}
+
+DEF_TEST(LRUCacheRandom, r) {
+    int instances = 0;
+    {
+        int seq[] = { 0, 1, 2, 3, 4, 1, 6, 2, 7, 5, 3, 2, 2, 3, 1, 7 };
+        int expected[] = { 7, 1, 3, 2, 5 };
+        static const int kSize = 5;
+        SkLRUCache<int, std::unique_ptr<Value>> test(kSize);
+        for (int i = 0; i < (int) (sizeof(seq) / sizeof(int)); i++) {
+            int k = seq[i];
+            if (!test.find(k)) {
+                test.insert(k, std::unique_ptr<Value>(new Value(k, &instances)));
+            }
+        }
+        REPORTER_ASSERT(r, kSize == instances);
+        REPORTER_ASSERT(r, kSize == test.count());
+        for (int i = 0; i < kSize; i++) {
+            int k = expected[i];
+            REPORTER_ASSERT(r, test.find(k));
+            REPORTER_ASSERT(r, k == (*test.find(k))->fValue);
+        }
+    }
+    REPORTER_ASSERT(r, 0 == instances);
+}
diff --git a/src/third_party/skia/tests/LayerDrawLooperTest.cpp b/src/third_party/skia/tests/LayerDrawLooperTest.cpp
index bc76a02..f3705b7 100644
--- a/src/third_party/skia/tests/LayerDrawLooperTest.cpp
+++ b/src/third_party/skia/tests/LayerDrawLooperTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkArenaAlloc.h"
 #include "SkBitmap.h"
 #include "SkBitmapDevice.h"
 #include "SkCanvas.h"
@@ -15,8 +16,6 @@
 #include "SkRect.h"
 #include "SkRefCnt.h"
 #include "SkScalar.h"
-#include "SkSmallAllocator.h"
-#include "SkXfermode.h"
 #include "Test.h"
 
 static SkBitmap make_bm(int w, int h) {
@@ -25,14 +24,15 @@
     return bm;
 }
 
+// TODO: can this be derived from SkBaseDevice?
 class FakeDevice : public SkBitmapDevice {
 public:
-    FakeDevice() : SkBitmapDevice(make_bm(100, 100)) { }
+    FakeDevice() : INHERITED(make_bm(100, 100), SkSurfaceProps(0, kUnknown_SkPixelGeometry)) {
+    }
 
-    virtual void drawRect(const SkDraw& draw, const SkRect& r,
-                          const SkPaint& paint) SK_OVERRIDE {
-        fLastMatrix = *draw.fMatrix;
-        this->INHERITED::drawRect(draw, r, paint);
+    void drawRect(const SkRect& r, const SkPaint& paint) override {
+        fLastMatrix = this->ctm();
+        this->INHERITED::drawRect(r, paint);
     }
 
     SkMatrix fLastMatrix;
@@ -52,19 +52,18 @@
     layerInfo.fOffset.set(10.0f, 20.0f);
     layerInfo.fPaintBits |= SkLayerDrawLooper::kXfermode_Bit;
     SkPaint* layerPaint = looperBuilder.addLayer(layerInfo);
-    layerPaint->setXfermodeMode(SkXfermode::kSrc_Mode);
+    layerPaint->setBlendMode(SkBlendMode::kSrc);
 
     FakeDevice device;
     SkCanvas canvas(&device);
     SkPaint paint;
-    SkAutoTUnref<SkLayerDrawLooper> looper(looperBuilder.detachLooper());
-    SkSmallAllocator<1, 32> allocator;
-    void* buffer = allocator.reserveT<SkDrawLooper::Context>(looper->contextSize());
-    SkDrawLooper::Context* context = looper->createContext(&canvas, buffer);
+    auto looper(looperBuilder.detach());
+    SkArenaAlloc alloc{48};
+    SkDrawLooper::Context* context = looper->makeContext(&canvas, &alloc);
 
     // The back layer should come first.
     REPORTER_ASSERT(reporter, context->next(&canvas, &paint));
-    REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrc_Mode));
+    REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrc);
     canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint);
     REPORTER_ASSERT(reporter, 10.0f == device.fLastMatrix.getTranslateX());
     REPORTER_ASSERT(reporter, 20.0f == device.fLastMatrix.getTranslateY());
@@ -72,7 +71,7 @@
 
     // Then the front layer.
     REPORTER_ASSERT(reporter, context->next(&canvas, &paint));
-    REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode));
+    REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrcOver);
     canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint);
     REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateX());
     REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateY());
@@ -92,19 +91,18 @@
     layerInfo.fOffset.set(10.0f, 20.0f);
     layerInfo.fPaintBits |= SkLayerDrawLooper::kXfermode_Bit;
     SkPaint* layerPaint = looperBuilder.addLayerOnTop(layerInfo);
-    layerPaint->setXfermodeMode(SkXfermode::kSrc_Mode);
+    layerPaint->setBlendMode(SkBlendMode::kSrc);
 
     FakeDevice device;
     SkCanvas canvas(&device);
     SkPaint paint;
-    SkAutoTUnref<SkLayerDrawLooper> looper(looperBuilder.detachLooper());
-    SkSmallAllocator<1, 32> allocator;
-    void* buffer = allocator.reserveT<SkDrawLooper::Context>(looper->contextSize());
-    SkDrawLooper::Context* context = looper->createContext(&canvas, buffer);
+    auto looper(looperBuilder.detach());
+    SkArenaAlloc alloc{48};
+    SkDrawLooper::Context* context = looper->makeContext(&canvas, &alloc);
 
     // The back layer should come first.
     REPORTER_ASSERT(reporter, context->next(&canvas, &paint));
-    REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode));
+    REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrcOver);
     canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint);
     REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateX());
     REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateY());
@@ -112,7 +110,7 @@
 
     // Then the front layer.
     REPORTER_ASSERT(reporter, context->next(&canvas, &paint));
-    REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrc_Mode));
+    REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrc);
     canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint);
     REPORTER_ASSERT(reporter, 10.0f == device.fLastMatrix.getTranslateX());
     REPORTER_ASSERT(reporter, 20.0f == device.fLastMatrix.getTranslateY());
@@ -132,19 +130,18 @@
     layerInfo.fOffset.set(10.0f, 20.0f);
     layerInfo.fPaintBits |= SkLayerDrawLooper::kXfermode_Bit;
     SkPaint* layerPaint = looperBuilder.addLayerOnTop(layerInfo);
-    layerPaint->setXfermodeMode(SkXfermode::kSrc_Mode);
+    layerPaint->setBlendMode(SkBlendMode::kSrc);
 
     FakeDevice device;
     SkCanvas canvas(&device);
     SkPaint paint;
-    SkAutoTUnref<SkLayerDrawLooper> looper(looperBuilder.detachLooper());
-    SkSmallAllocator<1, 32> allocator;
-    void* buffer = allocator.reserveT<SkDrawLooper::Context>(looper->contextSize());
-    SkDrawLooper::Context* context = looper->createContext(&canvas, buffer);
+    sk_sp<SkDrawLooper> looper(looperBuilder.detach());
+    SkArenaAlloc alloc{48};
+    SkDrawLooper::Context* context = looper->makeContext(&canvas, &alloc);
 
     // The back layer should come first.
     REPORTER_ASSERT(reporter, context->next(&canvas, &paint));
-    REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode));
+    REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrcOver);
     canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint);
     REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateX());
     REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateY());
@@ -152,7 +149,7 @@
 
     // Then the front layer.
     REPORTER_ASSERT(reporter, context->next(&canvas, &paint));
-    REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrc_Mode));
+    REPORTER_ASSERT(reporter, paint.getBlendMode() == SkBlendMode::kSrc);
     canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint);
     REPORTER_ASSERT(reporter, 10.0f == device.fLastMatrix.getTranslateX());
     REPORTER_ASSERT(reporter, 20.0f == device.fLastMatrix.getTranslateY());
diff --git a/src/third_party/skia/tests/LayerRasterizerTest.cpp b/src/third_party/skia/tests/LayerRasterizerTest.cpp
index 4b236ac..310aa3b 100644
--- a/src/third_party/skia/tests/LayerRasterizerTest.cpp
+++ b/src/third_party/skia/tests/LayerRasterizerTest.cpp
@@ -26,14 +26,14 @@
         gCount++;
     }
 
-    ~DummyRasterizer() {
+    ~DummyRasterizer() override {
         // Not threadsafe. Only used in one thread.
         gCount--;
     }
 
     static int GetCount() { return gCount; }
 
-    SK_DECLARE_NOT_FLATTENABLE_PROCS(DummyRasterizer)
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DummyRasterizer)
 
 private:
     static int gCount;
@@ -43,11 +43,15 @@
 
 int DummyRasterizer::gCount;
 
+sk_sp<SkFlattenable> DummyRasterizer::CreateProc(SkReadBuffer&) {
+    return sk_make_sp<DummyRasterizer>();
+}
+
 // Check to make sure that the SkPaint in the layer has its destructor called.
 DEF_TEST(LayerRasterizer_destructor, reporter) {
     {
         SkPaint paint;
-        paint.setRasterizer(SkNEW(DummyRasterizer))->unref();
+        paint.setRasterizer(sk_make_sp<DummyRasterizer>());
         REPORTER_ASSERT(reporter, DummyRasterizer::GetCount() == 1);
 
         SkLayerRasterizer::Builder builder;
@@ -79,7 +83,7 @@
 
 DEF_TEST(LayerRasterizer_copy, reporter) {
     SkLayerRasterizer::Builder builder;
-    REPORTER_ASSERT(reporter, NULL == builder.snapshotRasterizer());
+    REPORTER_ASSERT(reporter, nullptr == builder.snapshot());
     SkPaint paint;
     // Create a bunch of paints with different flags.
     for (uint32_t flags = 0x01; flags < SkPaint::kAllFlags; flags <<= 1) {
@@ -88,14 +92,14 @@
     }
 
     // Create a layer rasterizer with all the existing layers.
-    SkAutoTUnref<SkLayerRasterizer> firstCopy(builder.snapshotRasterizer());
+    sk_sp<SkLayerRasterizer> firstCopy(builder.snapshot());
 
     // Add one more layer.
     paint.setFlags(SkPaint::kAllFlags);
     builder.addLayer(paint);
 
-    SkAutoTUnref<SkLayerRasterizer> oneLarger(builder.snapshotRasterizer());
-    SkAutoTUnref<SkLayerRasterizer> detached(builder.detachRasterizer());
+    sk_sp<SkLayerRasterizer> oneLarger(builder.snapshot());
+    sk_sp<SkLayerRasterizer> detached(builder.detach());
 
     // Check the counts for consistency.
     const int largerCount = LayerRasterizerTester::CountLayers(*oneLarger.get());
@@ -103,9 +107,9 @@
     REPORTER_ASSERT(reporter, largerCount == LayerRasterizerTester::CountLayers(*detached.get()));
     REPORTER_ASSERT(reporter, smallerCount == largerCount - 1);
 
-    const SkLayerRasterizer_Rec* recFirstCopy = NULL;
-    const SkLayerRasterizer_Rec* recOneLarger = NULL;
-    const SkLayerRasterizer_Rec* recDetached = NULL;
+    const SkLayerRasterizer_Rec* recFirstCopy = nullptr;
+    const SkLayerRasterizer_Rec* recOneLarger = nullptr;
+    const SkLayerRasterizer_Rec* recDetached = nullptr;
 
     const SkDeque& layersFirstCopy = LayerRasterizerTester::GetLayers(*firstCopy.get());
     const SkDeque& layersOneLarger = LayerRasterizerTester::GetLayers(*oneLarger.get());
@@ -129,7 +133,7 @@
 
         REPORTER_ASSERT(reporter, equals(*recOneLarger, *recDetached));
         if (smallerCount == i) {
-            REPORTER_ASSERT(reporter, recFirstCopy == NULL);
+            REPORTER_ASSERT(reporter, recFirstCopy == nullptr);
         } else {
             REPORTER_ASSERT(reporter, equals(*recFirstCopy, *recOneLarger));
         }
@@ -138,5 +142,5 @@
 
 DEF_TEST(LayerRasterizer_detachEmpty, reporter) {
     SkLayerRasterizer::Builder builder;
-    REPORTER_ASSERT(reporter, NULL == builder.detachRasterizer());
+    REPORTER_ASSERT(reporter, nullptr == builder.detach());
 }
diff --git a/src/third_party/skia/tests/MD5Test.cpp b/src/third_party/skia/tests/MD5Test.cpp
index efad26d..214a7a8 100644
--- a/src/third_party/skia/tests/MD5Test.cpp
+++ b/src/third_party/skia/tests/MD5Test.cpp
@@ -23,7 +23,7 @@
     // All at once
     {
         SkMD5 context;
-        context.update(reinterpret_cast<const uint8_t*>(string), len);
+        context.write(string, len);
         SkMD5::Digest digest;
         context.finish(digest);
 
@@ -36,7 +36,7 @@
         const uint8_t* data = reinterpret_cast<const uint8_t*>(string);
         const uint8_t* end = reinterpret_cast<const uint8_t*>(string + len);
         for (; data < end; ++data) {
-            context.update(data, 1);
+            context.write(data, 1);
         }
         SkMD5::Digest digest;
         context.finish(digest);
diff --git a/src/third_party/skia/tests/MallocPixelRefTest.cpp b/src/third_party/skia/tests/MallocPixelRefTest.cpp
index e267d6f..957c8b3 100644
--- a/src/third_party/skia/tests/MallocPixelRefTest.cpp
+++ b/src/third_party/skia/tests/MallocPixelRefTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkAutoMalloc.h"
 #include "SkData.h"
 #include "SkMallocPixelRef.h"
 #include "Test.h"
@@ -24,82 +25,91 @@
     REPORTER_ASSERT(reporter, true);
     SkImageInfo info = SkImageInfo::MakeN32Premul(10, 13);
     {
-        SkAutoTUnref<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewAllocate(info, info.minRowBytes() - 1, NULL));
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeAllocate(info, info.minRowBytes() - 1));
         // rowbytes too small.
-        REPORTER_ASSERT(reporter, NULL == pr.get());
+        REPORTER_ASSERT(reporter, nullptr == pr.get());
     }
     {
         size_t rowBytes = info.minRowBytes() - 1;
         size_t size = info.getSafeSize(rowBytes);
-        SkAutoDataUnref data(SkData::NewUninitialized(size));
-        SkAutoTUnref<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewWithData(info, rowBytes, NULL, data));
+        sk_sp<SkData> data(SkData::MakeUninitialized(size));
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeWithData(info, rowBytes, data));
         // rowbytes too small.
-        REPORTER_ASSERT(reporter, NULL == pr.get());
+        REPORTER_ASSERT(reporter, nullptr == pr.get());
     }
     {
         size_t rowBytes = info.minRowBytes() + 2;
         size_t size = info.getSafeSize(rowBytes) - 1;
-        SkAutoDataUnref data(SkData::NewUninitialized(size));
-        SkAutoTUnref<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewWithData(info, rowBytes, NULL, data));
+        sk_sp<SkData> data(SkData::MakeUninitialized(size));
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeWithData(info, rowBytes, data));
         // data too small.
-        REPORTER_ASSERT(reporter, NULL == pr.get());
+        REPORTER_ASSERT(reporter, nullptr == pr.get());
     }
     size_t rowBytes = info.minRowBytes() + 7;
     size_t size = info.getSafeSize(rowBytes) + 9;
     {
         SkAutoMalloc memory(size);
-        SkAutoTUnref<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewDirect(info, memory.get(), rowBytes, NULL));
-        REPORTER_ASSERT(reporter, pr.get() != NULL);
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeDirect(info, memory.get(), rowBytes));
+        REPORTER_ASSERT(reporter, pr.get() != nullptr);
         REPORTER_ASSERT(reporter, memory.get() == pr->pixels());
     }
     {
-        SkAutoTUnref<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewAllocate(info, rowBytes, NULL));
-        REPORTER_ASSERT(reporter, pr.get() != NULL);
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeAllocate(info, rowBytes));
+        REPORTER_ASSERT(reporter, pr.get() != nullptr);
         REPORTER_ASSERT(reporter, pr->pixels());
     }
     {
         void* addr = static_cast<void*>(new uint8_t[size]);
-        SkAutoTUnref<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewWithProc(info, rowBytes, NULL, addr,
-                                          delete_uint8_proc, NULL));
-        REPORTER_ASSERT(reporter, pr.get() != NULL);
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeWithProc(info, rowBytes, addr, delete_uint8_proc, nullptr));
+        REPORTER_ASSERT(reporter, pr.get() != nullptr);
         REPORTER_ASSERT(reporter, addr == pr->pixels());
     }
     {
         int x = 0;
         SkAutoMalloc memory(size);
-        SkAutoTUnref<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewWithProc(info, rowBytes, NULL,
-                                          memory.get(), set_to_one_proc,
-                                          static_cast<void*>(&x)));
-        REPORTER_ASSERT(reporter, pr.get() != NULL);
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeWithProc(info, rowBytes,
+                                           memory.get(), set_to_one_proc,
+                                           static_cast<void*>(&x)));
+        REPORTER_ASSERT(reporter, pr.get() != nullptr);
         REPORTER_ASSERT(reporter, memory.get() == pr->pixels());
         REPORTER_ASSERT(reporter, 0 == x);
-        pr.reset(NULL);
+        pr.reset(nullptr);
+        // make sure that set_to_one_proc was called.
+        REPORTER_ASSERT(reporter, 1 == x);
+    }
+    {
+        int x = 0;
+        SkAutoMalloc memory(size);
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeWithProc(SkImageInfo::MakeN32Premul(-1, -1), rowBytes,
+                                           memory.get(), set_to_one_proc,
+                                           static_cast<void*>(&x)));
+        REPORTER_ASSERT(reporter, pr.get() == nullptr);
         // make sure that set_to_one_proc was called.
         REPORTER_ASSERT(reporter, 1 == x);
     }
     {
         void* addr = static_cast<void*>(new uint8_t[size]);
-        REPORTER_ASSERT(reporter, addr != NULL);
-        SkAutoTUnref<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewWithProc(info, rowBytes, NULL, addr,
-                                          delete_uint8_proc, NULL));
+        REPORTER_ASSERT(reporter, addr != nullptr);
+        sk_sp<SkPixelRef> pr(
+            SkMallocPixelRef::MakeWithProc(info, rowBytes, addr,
+                                           delete_uint8_proc, nullptr));
         REPORTER_ASSERT(reporter, addr == pr->pixels());
     }
     {
-        SkAutoDataUnref data(SkData::NewUninitialized(size));
+        sk_sp<SkData> data(SkData::MakeUninitialized(size));
         SkData* dataPtr = data.get();
         REPORTER_ASSERT(reporter, dataPtr->unique());
-        SkAutoTUnref<SkMallocPixelRef> pr(
-            SkMallocPixelRef::NewWithData(info, rowBytes, NULL, data.get()));
+        sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeWithData(info, rowBytes, data);
         REPORTER_ASSERT(reporter, !(dataPtr->unique()));
-        data.reset(NULL);
+        data.reset(nullptr);
         REPORTER_ASSERT(reporter, dataPtr->unique());
         REPORTER_ASSERT(reporter, dataPtr->data() == pr->pixels());
     }
diff --git a/src/third_party/skia/tests/MaskCacheTest.cpp b/src/third_party/skia/tests/MaskCacheTest.cpp
new file mode 100644
index 0000000..13cd354
--- /dev/null
+++ b/src/third_party/skia/tests/MaskCacheTest.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCachedData.h"
+#include "SkMaskCache.h"
+#include "SkResourceCache.h"
+#include "Test.h"
+
+enum LockedState {
+    kUnlocked,
+    kLocked,
+};
+
+enum CachedState {
+    kNotInCache,
+    kInCache,
+};
+
+static void check_data(skiatest::Reporter* reporter, SkCachedData* data,
+                       int refcnt, CachedState cacheState, LockedState lockedState) {
+    REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt);
+    REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState));
+    bool isLocked = (data->data() != nullptr);
+    REPORTER_ASSERT(reporter, isLocked == (lockedState == kLocked));
+}
+
+DEF_TEST(RRectMaskCache, reporter) {
+    SkResourceCache cache(1024);
+
+    SkScalar sigma = 0.8f;
+    SkRect rect = SkRect::MakeWH(100, 100);
+    SkRRect rrect;
+    rrect.setRectXY(rect, 30, 30);
+    SkBlurStyle style = kNormal_SkBlurStyle;
+    SkBlurQuality quality = kLow_SkBlurQuality;
+    SkMask mask;
+
+    SkCachedData* data = SkMaskCache::FindAndRef(sigma, style, quality, rrect, &mask, &cache);
+    REPORTER_ASSERT(reporter, nullptr == data);
+
+    size_t size = 256;
+    data = cache.newCachedData(size);
+    memset(data->writable_data(), 0xff, size);
+    mask.fBounds.setXYWH(0, 0, 100, 100);
+    mask.fRowBytes = 100;
+    mask.fFormat = SkMask::kBW_Format;
+    SkMaskCache::Add(sigma, style, quality, rrect, mask, data, &cache);
+    check_data(reporter, data, 2, kInCache, kLocked);
+
+    data->unref();
+    check_data(reporter, data, 1, kInCache, kUnlocked);
+
+    sk_bzero(&mask, sizeof(mask));
+    data = SkMaskCache::FindAndRef(sigma, style, quality, rrect, &mask, &cache);
+    REPORTER_ASSERT(reporter, data);
+    REPORTER_ASSERT(reporter, data->size() == size);
+    REPORTER_ASSERT(reporter, mask.fBounds.top() == 0 && mask.fBounds.bottom() == 100);
+    REPORTER_ASSERT(reporter, data->data() == (const void*)mask.fImage);
+    check_data(reporter, data, 2, kInCache, kLocked);
+
+    cache.purgeAll();
+    check_data(reporter, data, 1, kNotInCache, kLocked);
+    data->unref();
+}
+
+DEF_TEST(RectsMaskCache, reporter) {
+    SkResourceCache cache(1024);
+
+    SkScalar sigma = 0.8f;
+    SkRect rect = SkRect::MakeWH(100, 100);
+    SkRect rects[2] = {rect};
+    SkBlurStyle style = kNormal_SkBlurStyle;
+    SkBlurQuality quality = kLow_SkBlurQuality;
+    SkMask mask;
+
+    SkCachedData* data = SkMaskCache::FindAndRef(sigma, style, quality, rects, 1, &mask, &cache);
+    REPORTER_ASSERT(reporter, nullptr == data);
+
+    size_t size = 256;
+    data = cache.newCachedData(size);
+    memset(data->writable_data(), 0xff, size);
+    mask.fBounds.setXYWH(0, 0, 100, 100);
+    mask.fRowBytes = 100;
+    mask.fFormat = SkMask::kBW_Format;
+    SkMaskCache::Add(sigma, style, quality, rects, 1, mask, data, &cache);
+    check_data(reporter, data, 2, kInCache, kLocked);
+
+    data->unref();
+    check_data(reporter, data, 1, kInCache, kUnlocked);
+
+    sk_bzero(&mask, sizeof(mask));
+    data = SkMaskCache::FindAndRef(sigma, style, quality, rects, 1, &mask, &cache);
+    REPORTER_ASSERT(reporter, data);
+    REPORTER_ASSERT(reporter, data->size() == size);
+    REPORTER_ASSERT(reporter, mask.fBounds.top() == 0 && mask.fBounds.bottom() == 100);
+    REPORTER_ASSERT(reporter, data->data() == (const void*)mask.fImage);
+    check_data(reporter, data, 2, kInCache, kLocked);
+
+    cache.purgeAll();
+    check_data(reporter, data, 1, kNotInCache, kLocked);
+    data->unref();
+}
diff --git a/src/third_party/skia/tests/MathTest.cpp b/src/third_party/skia/tests/MathTest.cpp
index 2053936..0bd3cf5 100644
--- a/src/third_party/skia/tests/MathTest.cpp
+++ b/src/third_party/skia/tests/MathTest.cpp
@@ -5,10 +5,15 @@
  * found in the LICENSE file.
  */
 
+#include "float.h"
+
 #include "SkColorPriv.h"
 #include "SkEndian.h"
+#include "SkFDot6.h"
+#include "SkFixed.h"
 #include "SkFloatBits.h"
 #include "SkFloatingPoint.h"
+#include "SkHalf.h"
 #include "SkMathPriv.h"
 #include "SkPoint.h"
 #include "SkRandom.h"
@@ -32,6 +37,38 @@
     }
 }
 
+static void test_quick_div(skiatest::Reporter* reporter) {
+    /*
+    The inverse table is generated by turning on SkDebugf in the following test code
+    */
+    SkFixed storage[kInverseTableSize * 2];
+    SkFixed* table = storage + kInverseTableSize;
+
+    // SkDebugf("static const int gFDot6INVERSE[] = {");
+    for (SkFDot6 i=-kInverseTableSize; i<kInverseTableSize; i++) {
+        if (i != 0) {
+            table[i] = SkFDot6Div(SK_FDot6One, i);
+            REPORTER_ASSERT(reporter, table[i] == gFDot6INVERSE[i + kInverseTableSize]);
+        }
+        // SkDebugf("%d, ", table[i]);
+    }
+    // SkDebugf("}\n");
+
+
+    for (SkFDot6 a = -1024; a <= 1024; a++) {
+        for (SkFDot6 b = -1024; b <= 1024; b++) {
+            if (b != 0) {
+                SkFixed ourAnswer = QuickSkFDot6Div(a, b);
+                SkFixed directAnswer = SkFDot6Div(a, b);
+                REPORTER_ASSERT(reporter,
+                    (directAnswer == 0 && ourAnswer == 0) ||
+                    SkFixedDiv(SkAbs32(directAnswer - ourAnswer), SkAbs32(directAnswer)) <= 1 << 10
+                );
+            }
+        }
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static float sk_fsel(float pred, float result_ge, float result_lt) {
@@ -51,9 +88,10 @@
 static void test_floor_value(skiatest::Reporter* reporter, float value) {
     float fast = fast_floor(value);
     float std = std_floor(value);
-    REPORTER_ASSERT(reporter, std == fast);
-//    SkDebugf("value[%1.9f] std[%g] fast[%g] equal[%d]\n",
-//             value, std, fast, std == fast);
+    if (std != fast) {
+        ERRORF(reporter, "fast_floor(%.9g) == %.9g != %.9g == std_floor(%.9g)",
+               value, fast, std, value);
+    }
 }
 
 static void test_floor(skiatest::Reporter* reporter) {
@@ -149,7 +187,7 @@
 
                 if (r0 != r1 && r0 != r2) {
                     SkDebugf("src:%d dst:%d a:%d result:%d float:%g\n",
-                                  src,   dst, a,        r0,      f);
+                                 src,   dst, a,        r0,      f);
                     failed += 1;
                 }
                 if (r0 > 255) {
@@ -175,11 +213,8 @@
                     float diff = sk_float_abs(f1 - r1);
                     diff = sk_float_abs(diff - 0.5f);
                     if (diff > (1 / 255.f)) {
-#ifdef SK_DEBUG
-                        SkDebugf("src:%d dst:%d a:%d result:%d float:%g\n",
-                                 src, dst, a, r0, f1);
-#endif
-                        REPORTER_ASSERT(reporter, false);
+                        ERRORF(reporter, "src:%d dst:%d a:%d "
+                               "result:%d float:%g\n", src, dst, a, r0, f1);
                     }
                 }
             }
@@ -207,34 +242,29 @@
 /*  returns true if a == b as resulting from (int)x. Since it is undefined
  what to do if the float exceeds 2^32-1, we check for that explicitly.
  */
-static bool equal_float_native_skia(float x, uint32_t ni, uint32_t si) {
-    if (!(x == x)) {    // NAN
-        return ((int32_t)si) == SK_MaxS32 || ((int32_t)si) == SK_MinS32;
+static bool equal_float_native_skia(float x, int32_t ni, int32_t si) {
+    // When the float is out of integer range (NaN, above, below),
+    // the C cast is undefined, but Skia's methods should have clamped.
+    if (!(x == x)) {    // NaN
+        return si == SK_MaxS32 || si == SK_MinS32;
     }
-    // for out of range, C is undefined, but skia always should return NaN32
     if (x > SK_MaxS32) {
-        return ((int32_t)si) == SK_MaxS32;
+        return si == SK_MaxS32;
     }
-    if (x < -SK_MaxS32) {
-        return ((int32_t)si) == SK_MinS32;
+    if (x < SK_MinS32) {
+        return si == SK_MinS32;
     }
     return si == ni;
 }
 
 static void assert_float_equal(skiatest::Reporter* reporter, const char op[],
-                               float x, uint32_t ni, uint32_t si) {
+                               float x, int32_t ni, int32_t si) {
     if (!equal_float_native_skia(x, ni, si)) {
         ERRORF(reporter, "%s float %g bits %x native %x skia %x\n",
                op, x, SkFloat2Bits(x), ni, si);
     }
 }
 
-static void test_float_cast(skiatest::Reporter* reporter, float x) {
-    int ix = (int)x;
-    int iix = SkFloatToIntCast(x);
-    assert_float_equal(reporter, "cast", x, ix, iix);
-}
-
 static void test_float_floor(skiatest::Reporter* reporter, float x) {
     int ix = (int)floor(x);
     int iix = SkFloatToIntFloor(x);
@@ -255,23 +285,17 @@
 }
 
 static void test_float_conversions(skiatest::Reporter* reporter, float x) {
-    test_float_cast(reporter, x);
     test_float_floor(reporter, x);
     test_float_round(reporter, x);
     test_float_ceil(reporter, x);
 }
 
-static void test_int2float(skiatest::Reporter* reporter, int ival) {
-    float x0 = (float)ival;
-    float x1 = SkIntToFloatCast(ival);
-    REPORTER_ASSERT(reporter, x0 == x1);
-}
-
 static void unittest_fastfloat(skiatest::Reporter* reporter) {
     SkRandom rand;
     size_t i;
 
     static const float gFloats[] = {
+        0.f/0.f, -0.f/0.f, 1.f/0.f, -1.f/0.f,
         0.f, 1.f, 0.5f, 0.499999f, 0.5000001f, 1.f/3,
         0.000000001f, 1000000000.f,     // doesn't overflow
         0.0000000001f, 10000000000.f    // does overflow
@@ -287,17 +311,6 @@
             float x = nextFloat(rand);
             test_float_conversions(reporter, x);
         }
-
-        test_int2float(reporter, 0);
-        test_int2float(reporter, 1);
-        test_int2float(reporter, -1);
-        for (i = 0; i < 100000; i++) {
-            // for now only test ints that are 24bits or less, since we don't
-            // round (down) large ints the same as IEEE...
-            int ival = rand.nextU() & 0xFFFFFF;
-            test_int2float(reporter, ival);
-            test_int2float(reporter, -ival);
-        }
     }
 }
 
@@ -326,6 +339,96 @@
     REPORTER_ASSERT(reporter,  SkScalarIsFinite(0));
 }
 
+static void unittest_half(skiatest::Reporter* reporter) {
+    static const float gFloats[] = {
+        0.f, 1.f, 0.5f, 0.499999f, 0.5000001f, 1.f/3,
+        -0.f, -1.f, -0.5f, -0.499999f, -0.5000001f, -1.f/3
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gFloats); ++i) {
+        SkHalf h = SkFloatToHalf(gFloats[i]);
+        float f = SkHalfToFloat(h);
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, gFloats[i]));
+    }
+
+    // check some special values
+    union FloatUnion {
+        uint32_t fU;
+        float    fF;
+    };
+
+    static const FloatUnion largestPositiveHalf = { ((142 << 23) | (1023 << 13)) };
+    SkHalf h = SkFloatToHalf(largestPositiveHalf.fF);
+    float f = SkHalfToFloat(h);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, largestPositiveHalf.fF));
+
+    static const FloatUnion largestNegativeHalf = { (1u << 31) | (142u << 23) | (1023u << 13) };
+    h = SkFloatToHalf(largestNegativeHalf.fF);
+    f = SkHalfToFloat(h);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, largestNegativeHalf.fF));
+
+    static const FloatUnion smallestPositiveHalf = { 102 << 23 };
+    h = SkFloatToHalf(smallestPositiveHalf.fF);
+    f = SkHalfToFloat(h);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, smallestPositiveHalf.fF));
+
+    static const FloatUnion overflowHalf = { ((143 << 23) | (1023 << 13)) };
+    h = SkFloatToHalf(overflowHalf.fF);
+    f = SkHalfToFloat(h);
+    REPORTER_ASSERT(reporter, !SkScalarIsFinite(f) );
+
+    static const FloatUnion underflowHalf = { 101 << 23 };
+    h = SkFloatToHalf(underflowHalf.fF);
+    f = SkHalfToFloat(h);
+    REPORTER_ASSERT(reporter, f == 0.0f );
+
+    static const FloatUnion inf32 = { 255 << 23 };
+    h = SkFloatToHalf(inf32.fF);
+    f = SkHalfToFloat(h);
+    REPORTER_ASSERT(reporter, !SkScalarIsFinite(f) );
+
+    static const FloatUnion nan32 = { 255 << 23 | 1 };
+    h = SkFloatToHalf(nan32.fF);
+    f = SkHalfToFloat(h);
+    REPORTER_ASSERT(reporter, SkScalarIsNaN(f) );
+
+}
+
+template <typename RSqrtFn>
+static void test_rsqrt(skiatest::Reporter* reporter, RSqrtFn rsqrt) {
+    const float maxRelativeError = 6.50196699e-4f;
+
+    // test close to 0 up to 1
+    float input = 0.000001f;
+    for (int i = 0; i < 1000; ++i) {
+        float exact = 1.0f/sk_float_sqrt(input);
+        float estimate = rsqrt(input);
+        float relativeError = sk_float_abs(exact - estimate)/exact;
+        REPORTER_ASSERT(reporter, relativeError <= maxRelativeError);
+        input += 0.001f;
+    }
+
+    // test 1 to ~100
+    input = 1.0f;
+    for (int i = 0; i < 1000; ++i) {
+        float exact = 1.0f/sk_float_sqrt(input);
+        float estimate = rsqrt(input);
+        float relativeError = sk_float_abs(exact - estimate)/exact;
+        REPORTER_ASSERT(reporter, relativeError <= maxRelativeError);
+        input += 0.01f;
+    }
+
+    // test some big numbers
+    input = 1000000.0f;
+    for (int i = 0; i < 100; ++i) {
+        float exact = 1.0f/sk_float_sqrt(input);
+        float estimate = rsqrt(input);
+        float relativeError = sk_float_abs(exact - estimate)/exact;
+        REPORTER_ASSERT(reporter, relativeError <= maxRelativeError);
+        input += 754326.f;
+    }
+}
+
 static void test_muldiv255(skiatest::Reporter* reporter) {
     for (int a = 0; a <= 255; a++) {
         for (int b = 0; b <= 255; b++) {
@@ -460,16 +563,27 @@
         REPORTER_ASSERT(reporter, result == SK_Fixed1);
         result = SkFixedDiv(1, SK_Fixed1);
         REPORTER_ASSERT(reporter, result == 1);
+        result = SkFixedDiv(10 - 1, SK_Fixed1 * 3);
+        REPORTER_ASSERT(reporter, result == 3);
+    }
+
+    {
+        REPORTER_ASSERT(reporter, (SkFixedRoundToFixed(-SK_Fixed1 * 10) >> 1) == -SK_Fixed1 * 5);
+        REPORTER_ASSERT(reporter, (SkFixedFloorToFixed(-SK_Fixed1 * 10) >> 1) == -SK_Fixed1 * 5);
+        REPORTER_ASSERT(reporter, (SkFixedCeilToFixed(-SK_Fixed1 * 10) >> 1) == -SK_Fixed1 * 5);
     }
 
     unittest_fastfloat(reporter);
     unittest_isfinite(reporter);
+    unittest_half(reporter);
+    test_rsqrt(reporter, sk_float_rsqrt);
+    test_rsqrt(reporter, sk_float_rsqrt_portable);
 
     for (i = 0; i < 10000; i++) {
         SkFixed numer = rand.nextS();
         SkFixed denom = rand.nextS();
         SkFixed result = SkFixedDiv(numer, denom);
-        int64_t check = ((int64_t)numer << 16) / denom;
+        int64_t check = SkLeftShift((int64_t)numer, 16) / denom;
 
         (void)SkCLZ(numer);
         (void)SkCLZ(denom);
@@ -480,6 +594,9 @@
         } else if (check < -SK_MaxS32) {
             check = SK_MinS32;
         }
+        if (result != (int32_t)check) {
+            ERRORF(reporter, "\nFixed Divide: %8x / %8x -> %8x %8x\n", numer, denom, result, check);
+        }
         REPORTER_ASSERT(reporter, result == (int32_t)check);
     }
 
@@ -492,6 +609,7 @@
 
     test_muldivround(reporter);
     test_clz(reporter);
+    test_quick_div(reporter);
 }
 
 template <typename T> struct PairRec {
@@ -601,3 +719,41 @@
 DEF_TEST(divmod_s64, r) {
     test_divmod<int64_t>(r);
 }
+
+static void test_nextsizepow2(skiatest::Reporter* r, size_t test, size_t expectedAns) {
+    size_t ans = GrNextSizePow2(test);
+
+    REPORTER_ASSERT(r, ans == expectedAns);
+    //SkDebugf("0x%zx -> 0x%zx (0x%zx)\n", test, ans, expectedAns);
+}
+
+DEF_TEST(GrNextSizePow2, reporter) {
+    constexpr int kNumSizeTBits = 8 * sizeof(size_t);
+
+    size_t test = 0, expectedAns = 1;
+
+    test_nextsizepow2(reporter, test, expectedAns);
+
+    test = 1; expectedAns = 1;
+
+    for (int i = 1; i < kNumSizeTBits; ++i) {
+        test_nextsizepow2(reporter, test, expectedAns);
+
+        test++;
+        expectedAns <<= 1;
+
+        test_nextsizepow2(reporter, test, expectedAns);
+
+        test = expectedAns;
+    }
+
+    // For the remaining three tests there is no higher power (of 2)
+    test = 0x1;
+    test <<= kNumSizeTBits-1;
+    test_nextsizepow2(reporter, test, test);
+
+    test++;
+    test_nextsizepow2(reporter, test, test);
+
+    test_nextsizepow2(reporter, SIZE_MAX, SIZE_MAX);
+}
diff --git a/src/third_party/skia/tests/Matrix44Test.cpp b/src/third_party/skia/tests/Matrix44Test.cpp
index 0bd4a8b..382cd21 100644
--- a/src/third_party/skia/tests/Matrix44Test.cpp
+++ b/src/third_party/skia/tests/Matrix44Test.cpp
@@ -78,7 +78,7 @@
 static void test_constructor(skiatest::Reporter* reporter) {
     // Allocate a matrix on the heap
     SkMatrix44* placeholderMatrix = new SkMatrix44(SkMatrix44::kUninitialized_Constructor);
-    SkAutoTDelete<SkMatrix44> deleteMe(placeholderMatrix);
+    std::unique_ptr<SkMatrix44> deleteMe(placeholderMatrix);
 
     for (int row = 0; row < 4; ++row) {
         for (int col = 0; col < 4; ++col) {
@@ -104,6 +104,14 @@
     REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix);
     REPORTER_ASSERT(reporter, testMatrix->isIdentity());
     REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I());
+
+    // Verify that that constructing from an SkMatrix initializes everything.
+    SkMatrix44 scaleMatrix(SkMatrix44::kUninitialized_Constructor);
+    scaleMatrix.setScale(3, 4, 5);
+    REPORTER_ASSERT(reporter, scaleMatrix.isScale());
+    testMatrix = new(&scaleMatrix) SkMatrix44(SkMatrix::I());
+    REPORTER_ASSERT(reporter, testMatrix->isIdentity());
+    REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I());
 }
 
 static void test_translate(skiatest::Reporter* reporter) {
@@ -192,8 +200,8 @@
     SkMScalar dstA[4], dstB[4];
 
     for (int i = 0; i < 4; ++i) {
-        dstA[i] = 123456789;
-        dstB[i] = 987654321;
+        dstA[i] = SkDoubleToMScalar(123456789);
+        dstB[i] = SkDoubleToMScalar(987654321);
     }
 
     mat.map2(src2, 1, dstA);
@@ -360,15 +368,15 @@
                      0,   0,    0,     1);
 
     SkMatrix44 scaleTranslation(SkMatrix44::kUninitialized_Constructor);
-    scaleTranslation.setScale(10, 100, 1000);
+    scaleTranslation.setScale(32, 128, 1024);
     scaleTranslation.preTranslate(2, 3, 4);
     scaleTranslation.invert(&inverse);
     inverse.asRowMajord(inverseData);
     assert16<double>(reporter, inverseData,
-                     0.1,  0,    0,   -2,
-                     0,   0.01,  0,   -3,
-                     0,    0,  0.001, -4,
-                     0,    0,    0,   1);
+                     0.03125,  0,          0,            -2,
+                     0,        0.0078125,  0,            -3,
+                     0,        0,          0.0009765625, -4,
+                     0,        0,          0,             1);
 
     SkMatrix44 rotation(SkMatrix44::kUninitialized_Constructor);
     rotation.setRotateDegreesAbout(0, 0, 1, 90);
@@ -418,6 +426,31 @@
              0,   0,   -1,    1};
     expected.setRowMajord(expectedInverseAffineAndPerspective);
     REPORTER_ASSERT(reporter, nearly_equal(expected, inverse));
+
+    SkMatrix44 tinyScale(SkMatrix44::kIdentity_Constructor);
+    tinyScale.setDouble(0, 0, 1e-39);
+    REPORTER_ASSERT(reporter, tinyScale.getType() == SkMatrix44::kScale_Mask);
+    REPORTER_ASSERT(reporter, !tinyScale.invert(nullptr));
+    REPORTER_ASSERT(reporter, !tinyScale.invert(&inverse));
+
+    SkMatrix44 tinyScaleTranslate(SkMatrix44::kIdentity_Constructor);
+    tinyScaleTranslate.setDouble(0, 0, 1e-38);
+    REPORTER_ASSERT(reporter, tinyScaleTranslate.invert(nullptr));
+    tinyScaleTranslate.setDouble(0, 3, 10);
+    REPORTER_ASSERT(
+        reporter, tinyScaleTranslate.getType() ==
+                      (SkMatrix44::kScale_Mask | SkMatrix44::kTranslate_Mask));
+    REPORTER_ASSERT(reporter, !tinyScaleTranslate.invert(nullptr));
+    REPORTER_ASSERT(reporter, !tinyScaleTranslate.invert(&inverse));
+
+    SkMatrix44 tinyScalePerspective(SkMatrix44::kIdentity_Constructor);
+    tinyScalePerspective.setDouble(0, 0, 1e-39);
+    tinyScalePerspective.setDouble(3, 2, -1);
+    REPORTER_ASSERT(reporter, (tinyScalePerspective.getType() &
+                               SkMatrix44::kPerspective_Mask) ==
+                                  SkMatrix44::kPerspective_Mask);
+    REPORTER_ASSERT(reporter, !tinyScalePerspective.invert(nullptr));
+    REPORTER_ASSERT(reporter, !tinyScalePerspective.invert(&inverse));
 }
 
 static void test_transpose(skiatest::Reporter* reporter) {
@@ -451,6 +484,23 @@
     }
 }
 
+static void test_set_3x3(skiatest::Reporter* r) {
+    static float vals[9] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, };
+
+    SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
+    mat.set3x3RowMajorf(vals);
+
+    REPORTER_ASSERT(r, 1.0f == mat.getFloat(0, 0));
+    REPORTER_ASSERT(r, 2.0f == mat.getFloat(0, 1));
+    REPORTER_ASSERT(r, 3.0f == mat.getFloat(0, 2));
+    REPORTER_ASSERT(r, 4.0f == mat.getFloat(1, 0));
+    REPORTER_ASSERT(r, 5.0f == mat.getFloat(1, 1));
+    REPORTER_ASSERT(r, 6.0f == mat.getFloat(1, 2));
+    REPORTER_ASSERT(r, 7.0f == mat.getFloat(2, 0));
+    REPORTER_ASSERT(r, 8.0f == mat.getFloat(2, 1));
+    REPORTER_ASSERT(r, 9.0f == mat.getFloat(2, 2));
+}
+
 static void test_set_row_col_major(skiatest::Reporter* reporter) {
     SkMatrix44 a(SkMatrix44::kUninitialized_Constructor);
     SkMatrix44 b(SkMatrix44::kUninitialized_Constructor);
@@ -524,32 +574,259 @@
 static void test_has_perspective(skiatest::Reporter* reporter) {
     SkMatrix44 transform(SkMatrix44::kIdentity_Constructor);
 
-    transform.set(3, 2, -0.1);
+    transform.setDouble(3, 2, -0.1);
     REPORTER_ASSERT(reporter, transform.hasPerspective());
 
     transform.reset();
     REPORTER_ASSERT(reporter, !transform.hasPerspective());
 
-    transform.set(3, 0, -1.0);
+    transform.setDouble(3, 0, -1.0);
     REPORTER_ASSERT(reporter, transform.hasPerspective());
 
     transform.reset();
-    transform.set(3, 1, -1.0);
+    transform.setDouble(3, 1, -1.0);
     REPORTER_ASSERT(reporter, transform.hasPerspective());
 
     transform.reset();
-    transform.set(3, 2, -0.3);
+    transform.setDouble(3, 2, -0.3);
     REPORTER_ASSERT(reporter, transform.hasPerspective());
 
     transform.reset();
-    transform.set(3, 3, 0.5);
+    transform.setDouble(3, 3, 0.5);
     REPORTER_ASSERT(reporter, transform.hasPerspective());
- 
+
     transform.reset();
-    transform.set(3, 3, 0.0);
+    transform.setDouble(3, 3, 0.0);
     REPORTER_ASSERT(reporter, transform.hasPerspective());
 }
 
+static bool is_rectilinear (SkVector4& p1, SkVector4& p2, SkVector4& p3, SkVector4& p4) {
+    return (SkScalarNearlyEqual(p1.fData[0], p2.fData[0]) &&
+            SkScalarNearlyEqual(p2.fData[1], p3.fData[1]) &&
+            SkScalarNearlyEqual(p3.fData[0], p4.fData[0]) &&
+            SkScalarNearlyEqual(p4.fData[1], p1.fData[1])) ||
+           (SkScalarNearlyEqual(p1.fData[1], p2.fData[1]) &&
+            SkScalarNearlyEqual(p2.fData[0], p3.fData[0]) &&
+            SkScalarNearlyEqual(p3.fData[1], p4.fData[1]) &&
+            SkScalarNearlyEqual(p4.fData[0], p1.fData[0]));
+}
+
+static SkVector4 mul_with_persp_divide(const SkMatrix44& transform, const SkVector4& target) {
+    SkVector4 result = transform * target;
+    if (result.fData[3] != 0.0f && result.fData[3] != SK_Scalar1) {
+        float wInverse = SK_Scalar1 / result.fData[3];
+        result.set(result.fData[0] * wInverse,
+                   result.fData[1] * wInverse,
+                   result.fData[2] * wInverse,
+                   SK_Scalar1);
+    }
+    return result;
+}
+
+static bool empirically_preserves_2d_axis_alignment(skiatest::Reporter* reporter,
+                                                    const SkMatrix44& transform) {
+  SkVector4 p1(5.0f, 5.0f, 0.0f);
+  SkVector4 p2(10.0f, 5.0f, 0.0f);
+  SkVector4 p3(10.0f, 20.0f, 0.0f);
+  SkVector4 p4(5.0f, 20.0f, 0.0f);
+
+  REPORTER_ASSERT(reporter, is_rectilinear(p1, p2, p3, p4));
+
+  p1 = mul_with_persp_divide(transform, p1);
+  p2 = mul_with_persp_divide(transform, p2);
+  p3 = mul_with_persp_divide(transform, p3);
+  p4 = mul_with_persp_divide(transform, p4);
+
+  return is_rectilinear(p1, p2, p3, p4);
+}
+
+static void test(bool expected, skiatest::Reporter* reporter, const SkMatrix44& transform) {
+    if (expected) {
+        REPORTER_ASSERT(reporter, empirically_preserves_2d_axis_alignment(reporter, transform));
+        REPORTER_ASSERT(reporter, transform.preserves2dAxisAlignment());
+    } else {
+        REPORTER_ASSERT(reporter, !empirically_preserves_2d_axis_alignment(reporter, transform));
+        REPORTER_ASSERT(reporter, !transform.preserves2dAxisAlignment());
+    }
+}
+
+static void test_preserves_2d_axis_alignment(skiatest::Reporter* reporter) {
+  SkMatrix44 transform(SkMatrix44::kUninitialized_Constructor);
+  SkMatrix44 transform2(SkMatrix44::kUninitialized_Constructor);
+
+  static const struct TestCase {
+    SkMScalar a; // row 1, column 1
+    SkMScalar b; // row 1, column 2
+    SkMScalar c; // row 2, column 1
+    SkMScalar d; // row 2, column 2
+    bool expected;
+  } test_cases[] = {
+    { 3.f, 0.f,
+      0.f, 4.f, true }, // basic case
+    { 0.f, 4.f,
+      3.f, 0.f, true }, // rotate by 90
+    { 0.f, 0.f,
+      0.f, 4.f, true }, // degenerate x
+    { 3.f, 0.f,
+      0.f, 0.f, true }, // degenerate y
+    { 0.f, 0.f,
+      3.f, 0.f, true }, // degenerate x + rotate by 90
+    { 0.f, 4.f,
+      0.f, 0.f, true }, // degenerate y + rotate by 90
+    { 3.f, 4.f,
+      0.f, 0.f, false },
+    { 0.f, 0.f,
+      3.f, 4.f, false },
+    { 0.f, 3.f,
+      0.f, 4.f, false },
+    { 3.f, 0.f,
+      4.f, 0.f, false },
+    { 3.f, 4.f,
+      5.f, 0.f, false },
+    { 3.f, 4.f,
+      0.f, 5.f, false },
+    { 3.f, 0.f,
+      4.f, 5.f, false },
+    { 0.f, 3.f,
+      4.f, 5.f, false },
+    { 2.f, 3.f,
+      4.f, 5.f, false },
+  };
+
+  for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) {
+    const TestCase& value = test_cases[i];
+    transform.setIdentity();
+    transform.set(0, 0, value.a);
+    transform.set(0, 1, value.b);
+    transform.set(1, 0, value.c);
+    transform.set(1, 1, value.d);
+
+    test(value.expected, reporter, transform);
+  }
+
+  // Try the same test cases again, but this time make sure that other matrix
+  // elements (except perspective) have entries, to test that they are ignored.
+  for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) {
+    const TestCase& value = test_cases[i];
+    transform.setIdentity();
+    transform.set(0, 0, value.a);
+    transform.set(0, 1, value.b);
+    transform.set(1, 0, value.c);
+    transform.set(1, 1, value.d);
+
+    transform.set(0, 2, 1.f);
+    transform.set(0, 3, 2.f);
+    transform.set(1, 2, 3.f);
+    transform.set(1, 3, 4.f);
+    transform.set(2, 0, 5.f);
+    transform.set(2, 1, 6.f);
+    transform.set(2, 2, 7.f);
+    transform.set(2, 3, 8.f);
+
+    test(value.expected, reporter, transform);
+  }
+
+  // Try the same test cases again, but this time add perspective which is
+  // always assumed to not-preserve axis alignment.
+  for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) {
+    const TestCase& value = test_cases[i];
+    transform.setIdentity();
+    transform.set(0, 0, value.a);
+    transform.set(0, 1, value.b);
+    transform.set(1, 0, value.c);
+    transform.set(1, 1, value.d);
+
+    transform.set(0, 2, 1.f);
+    transform.set(0, 3, 2.f);
+    transform.set(1, 2, 3.f);
+    transform.set(1, 3, 4.f);
+    transform.set(2, 0, 5.f);
+    transform.set(2, 1, 6.f);
+    transform.set(2, 2, 7.f);
+    transform.set(2, 3, 8.f);
+    transform.set(3, 0, 9.f);
+    transform.set(3, 1, 10.f);
+    transform.set(3, 2, 11.f);
+    transform.set(3, 3, 12.f);
+
+    test(false, reporter, transform);
+  }
+
+  // Try a few more practical situations to check precision
+  // Reuse TestCase (a, b, c, d) as (x, y, z, degrees) axis to rotate about.
+  TestCase rotation_tests[] = {
+    { 0.0, 0.0, 1.0, 90.0, true },
+    { 0.0, 0.0, 1.0, 180.0, true },
+    { 0.0, 0.0, 1.0, 270.0, true },
+    { 0.0, 1.0, 0.0, 90.0, true },
+    { 1.0, 0.0, 0.0, 90.0, true },
+    { 0.0, 0.0, 1.0, 45.0, false },
+    // In 3d these next two are non-preserving, but we're testing in 2d after
+    // orthographic projection, where they are.
+    { 0.0, 1.0, 0.0, 45.0, true },
+    { 1.0, 0.0, 0.0, 45.0, true },
+  };
+
+  for (size_t i = 0; i < sizeof(rotation_tests)/sizeof(TestCase); ++i) {
+    const TestCase& value = rotation_tests[i];
+    transform.setRotateDegreesAbout(value.a, value.b, value.c, value.d);
+    test(value.expected, reporter, transform);
+  }
+
+  static const struct DoubleRotationCase {
+    SkMScalar x1;
+    SkMScalar y1;
+    SkMScalar z1;
+    SkMScalar degrees1;
+    SkMScalar x2;
+    SkMScalar y2;
+    SkMScalar z2;
+    SkMScalar degrees2;
+    bool expected;
+  } double_rotation_tests[] = {
+    { 0.0, 0.0, 1.0, 90.0, 0.0, 1.0, 0.0, 90.0, true },
+    { 0.0, 0.0, 1.0, 90.0, 1.0, 0.0, 0.0, 90.0, true },
+    { 0.0, 1.0, 0.0, 90.0, 0.0, 0.0, 1.0, 90.0, true },
+  };
+
+  for (size_t i = 0; i < sizeof(double_rotation_tests)/sizeof(DoubleRotationCase); ++i) {
+    const DoubleRotationCase& value = double_rotation_tests[i];
+    transform.setRotateDegreesAbout(value.x1, value.y1, value.z1, value.degrees1);
+    transform2.setRotateDegreesAbout(value.x2, value.y2, value.z2, value.degrees2);
+    transform.postConcat(transform2);
+    test(value.expected, reporter, transform);
+  }
+
+  // Perspective cases.
+  transform.setIdentity();
+  transform.setDouble(3, 2, -0.1); // Perspective depth 10
+  transform2.setRotateDegreesAbout(0.0, 1.0, 0.0, 45.0);
+  transform.preConcat(transform2);
+  test(false, reporter, transform);
+
+  transform.setIdentity();
+  transform.setDouble(3, 2, -0.1); // Perspective depth 10
+  transform2.setRotateDegreesAbout(0.0, 0.0, 1.0, 90.0);
+  transform.preConcat(transform2);
+  test(true, reporter, transform);
+}
+
+// just want to exercise the various converters for MScalar
+static void test_toint(skiatest::Reporter* reporter) {
+    SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
+    mat.setScale(3, 3, 3);
+
+    SkMScalar sum = SkMScalarFloor(mat.get(0, 0)) +
+                    SkMScalarRound(mat.get(1, 0)) +
+                    SkMScalarCeil(mat.get(2, 0));
+    int isum =      SkMScalarFloorToInt(mat.get(0, 1)) +
+                    SkMScalarRoundToInt(mat.get(1, 2)) +
+                    SkMScalarCeilToInt(mat.get(2, 3));
+    REPORTER_ASSERT(reporter, sum >= 0);
+    REPORTER_ASSERT(reporter, isum >= 0);
+    REPORTER_ASSERT(reporter, static_cast<SkMScalar>(isum) == SkIntToMScalar(isum));
+}
+
 DEF_TEST(Matrix44, reporter) {
     SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
     SkMatrix44 inverse(SkMatrix44::kUninitialized_Constructor);
@@ -575,7 +852,7 @@
     mat.setScale(3, 3, 3);
     rot.setRotateDegreesAbout(0, 0, -1, 90);
     mat.postConcat(rot);
-    REPORTER_ASSERT(reporter, mat.invert(NULL));
+    REPORTER_ASSERT(reporter, mat.invert(nullptr));
     mat.invert(&inverse);
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
@@ -584,22 +861,27 @@
 
     // test tiny-valued matrix inverse
     mat.reset();
-    mat.setScale(1.0e-12, 1.0e-12, 1.0e-12);
+    auto v = SkDoubleToMScalar(1.0e-12);
+    mat.setScale(v,v,v);
     rot.setRotateDegreesAbout(0, 0, -1, 90);
     mat.postConcat(rot);
-    mat.postTranslate(1.0e-12, 1.0e-12, 1.0e-12);
-    REPORTER_ASSERT(reporter, mat.invert(NULL));
+    mat.postTranslate(v,v,v);
+    REPORTER_ASSERT(reporter, mat.invert(nullptr));
     mat.invert(&inverse);
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
 
     // test mixed-valued matrix inverse
     mat.reset();
-    mat.setScale(1.0e-10, 3.0, 1.0e+10);
+    mat.setScale(SkDoubleToMScalar(1.0e-2),
+                 SkDoubleToMScalar(3.0),
+                 SkDoubleToMScalar(1.0e+2));
     rot.setRotateDegreesAbout(0, 0, -1, 90);
     mat.postConcat(rot);
-    mat.postTranslate(1.0e+10, 3.0, 1.0e-10);
-    REPORTER_ASSERT(reporter, mat.invert(NULL));
+    mat.postTranslate(SkDoubleToMScalar(1.0e+2),
+                      SkDoubleToMScalar(3.0),
+                      SkDoubleToMScalar(1.0e-2));
+    REPORTER_ASSERT(reporter, mat.invert(nullptr));
     mat.invert(&inverse);
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
@@ -607,7 +889,7 @@
     // test degenerate matrix
     mat.reset();
     mat.set3x3(1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0);
-    REPORTER_ASSERT(reporter, !mat.invert(NULL));
+    REPORTER_ASSERT(reporter, !mat.invert(nullptr));
 
     // test rol/col Major getters
     {
@@ -651,9 +933,12 @@
     test_transpose(reporter);
     test_get_set_double(reporter);
     test_set_row_col_major(reporter);
+    test_set_3x3(reporter);
     test_translate(reporter);
     test_scale(reporter);
     test_map2(reporter);
     test_3x3_conversion(reporter);
     test_has_perspective(reporter);
+    test_preserves_2d_axis_alignment(reporter);
+    test_toint(reporter);
 }
diff --git a/src/third_party/skia/tests/MatrixClipCollapseTest.cpp b/src/third_party/skia/tests/MatrixClipCollapseTest.cpp
index c994027..de6af36 100644
--- a/src/third_party/skia/tests/MatrixClipCollapseTest.cpp
+++ b/src/third_party/skia/tests/MatrixClipCollapseTest.cpp
@@ -192,8 +192,8 @@
 }
 
 static void add_clip(ClipType clip, MatType mat, SkTDArray<DrawType>* expected) {
-    if (NULL == expected) {
-        // expected is NULL if this clip will be fused into later clips
+    if (nullptr == expected) {
+        // expected is nullptr if this clip will be fused into later clips
         return;
     }
 
@@ -255,8 +255,8 @@
 }
 
 static void add_mat(MatType mat, SkTDArray<DrawType>* expected) {
-    if (NULL == expected) {
-        // expected is NULL if this matrix call will be fused into later ones
+    if (nullptr == expected) {
+        // expected is nullptr if this matrix call will be fused into later ones
         return;
     }
 
@@ -475,7 +475,7 @@
     (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, accumulatedClips+1);
     *expected->append() = SAVE_LAYER;
     // TODO: widen testing to exercise saveLayer's parameters
-    canvas->saveLayer(NULL, NULL);
+    canvas->saveLayer(nullptr, nullptr);
         if (needsSaveRestore) {
             *expected->append() = SAVE;
         }
@@ -513,14 +513,14 @@
     (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, accumulatedClips+1);
     *expected->append() = SAVE_LAYER;
     // TODO: widen testing to exercise saveLayer's parameters
-    canvas->saveLayer(NULL, NULL);
+    canvas->saveLayer(nullptr, nullptr);
         (*emitMC)(canvas, mat, clip, kSaveLayer_DrawOpType, expected, 1);
         if (kNone_MatType != mat || kNone_ClipType != clip) {
             *expected->append() = SAVE;
         }
         *expected->append() = SAVE_LAYER;
         // TODO: widen testing to exercise saveLayer's parameters
-        canvas->saveLayer(NULL, NULL);
+        canvas->saveLayer(nullptr, nullptr);
             if (needsSaveRestore) {
                 *expected->append() = SAVE;
             }
@@ -569,11 +569,11 @@
                          PFEmitMC emitMC, MatType mat, ClipType clip,
                          PFEmitBody emitBody, DrawOpType draw,
                          SkTDArray<DrawType>* expected) {
-    (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops
+    (*emitMC)(canvas, mat, clip, draw, nullptr, 0); // these get fused into later ops
     canvas->save();
         (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1);
     canvas->restore();
-    (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get removed
+    (*emitMC)(canvas, mat, clip, draw, nullptr, 0); // these will get removed
 }
 
 // Emit:
@@ -592,14 +592,14 @@
                          PFEmitMC emitMC, MatType mat, ClipType clip,
                          PFEmitBody emitBody, DrawOpType draw,
                          SkTDArray<DrawType>* expected) {
-    (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get fused into later ops
+    (*emitMC)(canvas, mat, clip, draw, nullptr, 1); // these will get fused into later ops
     canvas->save();
         (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1);
     canvas->restore();
     canvas->save();
         (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1);
     canvas->restore();
-    (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get removed
+    (*emitMC)(canvas, mat, clip, draw, nullptr, 1); // these will get removed
 }
 
 // Emit:
@@ -621,17 +621,17 @@
                          PFEmitMC emitMC, MatType mat, ClipType clip,
                          PFEmitBody emitBody, DrawOpType draw,
                          SkTDArray<DrawType>* expected) {
-    (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get fused into later ops
+    (*emitMC)(canvas, mat, clip, draw, nullptr, 0); // these will get fused into later ops
     canvas->save();
         (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1);
     canvas->restore();
     canvas->save();
-        (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get fused into later ops
+        (*emitMC)(canvas, mat, clip, draw, nullptr, 1); // these will get fused into later ops
         canvas->save();
             (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 2);
         canvas->restore();
     canvas->restore();
-    (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get removed
+    (*emitMC)(canvas, mat, clip, draw, nullptr, 0); // these will get removed
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/third_party/skia/tests/MatrixTest.cpp b/src/third_party/skia/tests/MatrixTest.cpp
index fc7ac42..da2ae35 100644
--- a/src/third_party/skia/tests/MatrixTest.cpp
+++ b/src/third_party/skia/tests/MatrixTest.cpp
@@ -42,7 +42,7 @@
                 if (0 == aVal && 0 == bVal && aValI != bValI) {
                     foundZeroSignDiff = true;
                 } else {
-                    REPORTER_ASSERT(reporter, aVal == bVal && aValI == aValI);
+                    REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
                 }
             }
             REPORTER_ASSERT(reporter, foundZeroSignDiff);
@@ -71,6 +71,45 @@
     return nearly_equal(m, identity);
 }
 
+static void assert9(skiatest::Reporter* reporter, const SkMatrix& m,
+                    SkScalar a, SkScalar b, SkScalar c,
+                    SkScalar d, SkScalar e, SkScalar f,
+                    SkScalar g, SkScalar h, SkScalar i) {
+    SkScalar buffer[9];
+    m.get9(buffer);
+    REPORTER_ASSERT(reporter, buffer[0] == a);
+    REPORTER_ASSERT(reporter, buffer[1] == b);
+    REPORTER_ASSERT(reporter, buffer[2] == c);
+    REPORTER_ASSERT(reporter, buffer[3] == d);
+    REPORTER_ASSERT(reporter, buffer[4] == e);
+    REPORTER_ASSERT(reporter, buffer[5] == f);
+    REPORTER_ASSERT(reporter, buffer[6] == g);
+    REPORTER_ASSERT(reporter, buffer[7] == h);
+    REPORTER_ASSERT(reporter, buffer[8] == i);
+}
+
+static void test_set9(skiatest::Reporter* reporter) {
+
+    SkMatrix m;
+    m.reset();
+    assert9(reporter, m, 1, 0, 0, 0, 1, 0, 0, 0, 1);
+
+    m.setScale(2, 3);
+    assert9(reporter, m, 2, 0, 0, 0, 3, 0, 0, 0, 1);
+
+    m.postTranslate(4, 5);
+    assert9(reporter, m, 2, 0, 4, 0, 3, 5, 0, 0, 1);
+
+    SkScalar buffer[9];
+    sk_bzero(buffer, sizeof(buffer));
+    buffer[SkMatrix::kMScaleX] = 1;
+    buffer[SkMatrix::kMScaleY] = 1;
+    buffer[SkMatrix::kMPersp2] = 1;
+    REPORTER_ASSERT(reporter, !m.isIdentity());
+    m.set9(buffer);
+    REPORTER_ASSERT(reporter, m.isIdentity());
+}
+
 static void test_matrix_recttorect(skiatest::Reporter* reporter) {
     SkRect src, dst;
     SkMatrix matrix;
@@ -103,7 +142,7 @@
     // add 100 in case we have a bug, I don't want to kill my stack in the test
     static const size_t kBufferSize = SkMatrix::kMaxFlattenSize + 100;
     char buffer[kBufferSize];
-    size_t size1 = m.writeToMemory(NULL);
+    size_t size1 = m.writeToMemory(nullptr);
     size_t size2 = m.writeToMemory(buffer);
     REPORTER_ASSERT(reporter, size1 == size2);
     REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize);
@@ -163,18 +202,31 @@
 
     SkMatrix perspX;
     perspX.reset();
-    perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
+    perspX.setPerspX(SK_Scalar1 / 1000);
     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMinScale());
     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxScale());
-    // Verify that getMinMaxScales() doesn't update the scales array on failure.
-    scales[0] = -5;
-    scales[1] = -5;
     success = perspX.getMinMaxScales(scales);
-    REPORTER_ASSERT(reporter, !success && -5 * SK_Scalar1 == scales[0] && -5 * SK_Scalar1  == scales[1]);
+    REPORTER_ASSERT(reporter, !success);
+
+    // skbug.com/4718
+    SkMatrix big;
+    big.setAll(2.39394089e+36f, 8.85347779e+36f, 9.26526204e+36f,
+               3.9159619e+36f, 1.44823453e+37f, 1.51559342e+37f,
+               0.f, 0.f, 1.f);
+    success = big.getMinMaxScales(scales);
+    REPORTER_ASSERT(reporter, !success);
+
+    // skbug.com/4718
+    SkMatrix givingNegativeNearlyZeros;
+    givingNegativeNearlyZeros.setAll(0.00436534f, 0.114138f, 0.37141f,
+                                     0.00358857f, 0.0936228f, -0.0174198f,
+                                     0.f, 0.f, 1.f);
+    success = givingNegativeNearlyZeros.getMinMaxScales(scales);
+    REPORTER_ASSERT(reporter, success && 0 == scales[0]);
 
     SkMatrix perspY;
     perspY.reset();
-    perspY.setPerspY(SkScalarToPersp(-SK_Scalar1 / 500));
+    perspY.setPerspY(-SK_Scalar1 / 500);
     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMinScale());
     REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxScale());
     scales[0] = -5;
@@ -187,8 +239,8 @@
     SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
     for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
         mats[i] = baseMats[i];
-        bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
-        REPORTER_ASSERT(reporter, invertable);
+        bool invertible = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
+        REPORTER_ASSERT(reporter, invertible);
     }
     SkRandom rand;
     for (int m = 0; m < 1000; ++m) {
@@ -231,8 +283,8 @@
         mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
         for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
             SkScalar d = vectors[i].length();
-            REPORTER_ASSERT(reporter, SkScalarDiv(d, maxScale) < gVectorScaleTol);
-            REPORTER_ASSERT(reporter, SkScalarDiv(minScale, d) < gVectorScaleTol);
+            REPORTER_ASSERT(reporter, d / maxScale < gVectorScaleTol);
+            REPORTER_ASSERT(reporter, minScale / d < gVectorScaleTol);
             if (max < d) {
                 max = d;
             }
@@ -240,8 +292,8 @@
                 min = d;
             }
         }
-        REPORTER_ASSERT(reporter, SkScalarDiv(max, maxScale) >= gCloseScaleTol);
-        REPORTER_ASSERT(reporter, SkScalarDiv(minScale, min) >= gCloseScaleTol);
+        REPORTER_ASSERT(reporter, max / maxScale >= gCloseScaleTol);
+        REPORTER_ASSERT(reporter, minScale / min >= gCloseScaleTol);
     }
 }
 
@@ -319,13 +371,13 @@
 
     // perspective x
     mat.reset();
-    mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
+    mat.setPerspX(SK_Scalar1 / 2);
     REPORTER_ASSERT(reporter, !mat.isSimilarity());
     REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
 
     // perspective y
     mat.reset();
-    mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
+    mat.setPerspY(SK_Scalar1 / 2);
     REPORTER_ASSERT(reporter, !mat.isSimilarity());
     REPORTER_ASSERT(reporter, !mat.preservesRightAngles());
 
@@ -458,7 +510,7 @@
     REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
     REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
     // make sure it doesn't crash if we pass in NULLs
-    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, NULL, NULL, NULL));
+    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, nullptr, nullptr, nullptr));
 
     // rotation only
     mat.setRotate(kRotation0);
@@ -625,8 +677,14 @@
     const float kRotation1 = -50.f;
     const float kScale0 = 5000.f;
 
+#if defined(GOOGLE3)
+    // Stack frame size is limited in GOOGLE3.
+    const int kTripleCount = 100;
+    const int kMatrixCount = 100;
+#else
     const int kTripleCount = 1000;
     const int kMatrixCount = 1000;
+#endif
     SkRandom rand;
 
     SkScalar randTriples[3*kTripleCount];
@@ -672,7 +730,7 @@
 
     // doesn't crash with null dst, src, count == 0
     {
-    mats[0].mapHomogeneousPoints(NULL, NULL, 0);
+    mats[0].mapHomogeneousPoints(nullptr, nullptr, 0);
     }
 
     // uniform scale of point
@@ -732,6 +790,34 @@
 
 }
 
+static bool check_decompScale(const SkMatrix& matrix) {
+    SkSize scale;
+    SkMatrix remaining;
+
+    if (!matrix.decomposeScale(&scale, &remaining)) {
+        return false;
+    }
+    if (scale.width() <= 0 || scale.height() <= 0) {
+        return false;
+    }
+    remaining.preScale(scale.width(), scale.height());
+    return nearly_equal(matrix, remaining);
+}
+
+static void test_decompScale(skiatest::Reporter* reporter) {
+    SkMatrix m;
+
+    m.reset();
+    REPORTER_ASSERT(reporter, check_decompScale(m));
+    m.setScale(2, 3);
+    REPORTER_ASSERT(reporter, check_decompScale(m));
+    m.setRotate(35, 0, 0);
+    REPORTER_ASSERT(reporter, check_decompScale(m));
+
+    m.setScale(1, 0);
+    REPORTER_ASSERT(reporter, !check_decompScale(m));
+}
+
 DEF_TEST(Matrix, reporter) {
     SkMatrix    mat, inverse, iden1, iden2;
 
@@ -755,7 +841,7 @@
 
     mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
     mat.postRotate(SkIntToScalar(25));
-    REPORTER_ASSERT(reporter, mat.invert(NULL));
+    REPORTER_ASSERT(reporter, mat.invert(nullptr));
     REPORTER_ASSERT(reporter, mat.invert(&inverse));
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
@@ -765,10 +851,17 @@
     test_flatten(reporter, iden2);
 
     mat.setScale(0, SK_Scalar1);
-    REPORTER_ASSERT(reporter, !mat.invert(NULL));
+    REPORTER_ASSERT(reporter, !mat.invert(nullptr));
     REPORTER_ASSERT(reporter, !mat.invert(&inverse));
     mat.setScale(SK_Scalar1, 0);
-    REPORTER_ASSERT(reporter, !mat.invert(NULL));
+    REPORTER_ASSERT(reporter, !mat.invert(nullptr));
+    REPORTER_ASSERT(reporter, !mat.invert(&inverse));
+
+    // Inverting this matrix results in a non-finite matrix
+    mat.setAll(0.0f, 1.0f, 2.0f,
+               0.0f, 1.0f, -3.40277175e+38f,
+               1.00003040f, 1.0f, 0.0f);
+    REPORTER_ASSERT(reporter, !mat.invert(nullptr));
     REPORTER_ASSERT(reporter, !mat.invert(&inverse));
 
     // rectStaysRect test
@@ -828,7 +921,7 @@
     REPORTER_ASSERT(reporter, affineEqual(TransY));
     #undef affineEqual
 
-    mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2));
+    mat.set(SkMatrix::kMPersp1, SK_Scalar1 / 2);
     REPORTER_ASSERT(reporter, !mat.asAffine(affine));
 
     SkMatrix mat2;
@@ -849,6 +942,14 @@
     test_matrix_recttorect(reporter);
     test_matrix_decomposition(reporter);
     test_matrix_homogeneous(reporter);
+    test_set9(reporter);
+
+    test_decompScale(reporter);
+
+    mat.setScaleTranslate(2, 3, 1, 4);
+    mat2.setScale(2, 3);
+    mat2.postTranslate(1, 4);
+    REPORTER_ASSERT(reporter, mat == mat2);
 }
 
 DEF_TEST(Matrix_Concat, r) {
@@ -863,3 +964,29 @@
 
     REPORTER_ASSERT(r, expected == SkMatrix::Concat(a, b));
 }
+
+// Test that all variants of maprect are correct.
+DEF_TEST(Matrix_maprects, r) {
+    const SkScalar scale = 1000;
+    
+    SkMatrix mat;
+    mat.setScale(2, 3);
+    mat.postTranslate(1, 4);
+
+    SkRandom rand;
+    for (int i = 0; i < 10000; ++i) {
+        SkRect src = SkRect::MakeLTRB(rand.nextSScalar1() * scale,
+                                      rand.nextSScalar1() * scale,
+                                      rand.nextSScalar1() * scale,
+                                      rand.nextSScalar1() * scale);
+        SkRect dst[3];
+        
+        mat.mapPoints((SkPoint*)&dst[0].fLeft, (SkPoint*)&src.fLeft, 2);
+        dst[0].sort();
+        mat.mapRect(&dst[1], src);
+        mat.mapRectScaleTranslate(&dst[2], src);
+
+        REPORTER_ASSERT(r, dst[0] == dst[1]);
+        REPORTER_ASSERT(r, dst[0] == dst[2]);
+    }
+}
diff --git a/src/third_party/skia/tests/MemsetTest.cpp b/src/third_party/skia/tests/MemsetTest.cpp
index ee6aaea..b68844c 100644
--- a/src/third_party/skia/tests/MemsetTest.cpp
+++ b/src/third_party/skia/tests/MemsetTest.cpp
@@ -5,40 +5,10 @@
  * found in the LICENSE file.
  */
 
-#include "SkChunkAlloc.h"
+#include "SkRandom.h"
 #include "SkUtils.h"
 #include "Test.h"
 
-static void test_chunkalloc(skiatest::Reporter* reporter) {
-    size_t min = 256;
-    SkChunkAlloc alloc(min);
-
-    REPORTER_ASSERT(reporter, 0 == alloc.totalCapacity());
-    REPORTER_ASSERT(reporter, 0 == alloc.totalUsed());
-    REPORTER_ASSERT(reporter, 0 == alloc.blockCount());
-    REPORTER_ASSERT(reporter, !alloc.contains(NULL));
-    REPORTER_ASSERT(reporter, !alloc.contains(reporter));
-
-    alloc.reset();
-    REPORTER_ASSERT(reporter, 0 == alloc.totalCapacity());
-    REPORTER_ASSERT(reporter, 0 == alloc.totalUsed());
-    REPORTER_ASSERT(reporter, 0 == alloc.blockCount());
-
-    size_t size = min >> 1;
-    void* ptr = alloc.allocThrow(size);
-    REPORTER_ASSERT(reporter, alloc.totalCapacity() >= size);
-    REPORTER_ASSERT(reporter, alloc.totalUsed() == size);
-    REPORTER_ASSERT(reporter, alloc.blockCount() > 0);
-    REPORTER_ASSERT(reporter, alloc.contains(ptr));
-
-    alloc.reset();
-    REPORTER_ASSERT(reporter, !alloc.contains(ptr));
-    REPORTER_ASSERT(reporter, 0 == alloc.totalCapacity());
-    REPORTER_ASSERT(reporter, 0 == alloc.totalUsed());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
 static void set_zero(void* dst, size_t bytes) {
     char* ptr = (char*)dst;
     for (size_t i = 0; i < bytes; ++i) {
@@ -54,24 +24,24 @@
 #define VALUE16         0x1234
 #define VALUE32         0x12345678
 
-static bool compare16(const uint16_t base[], uint16_t value, int count) {
+static void compare16(skiatest::Reporter* r, const uint16_t base[],
+                      uint16_t value, int count) {
     for (int i = 0; i < count; ++i) {
         if (base[i] != value) {
-            SkDebugf("[%d] expected %x found %x\n", i, value, base[i]);
-            return false;
+            ERRORF(r, "[%d] expected %x found %x\n", i, value, base[i]);
+            return;
         }
     }
-    return true;
 }
 
-static bool compare32(const uint32_t base[], uint32_t value, int count) {
+static void compare32(skiatest::Reporter* r, const uint32_t base[],
+                      uint32_t value, int count) {
     for (int i = 0; i < count; ++i) {
         if (base[i] != value) {
-            SkDebugf("[%d] expected %x found %x\n", i, value, base[i]);
-            return false;
+            ERRORF(r, "[%d] expected %x found %x\n", i, value, base[i]);
+            return;
         }
     }
-    return true;
 }
 
 static void test_16(skiatest::Reporter* reporter) {
@@ -84,10 +54,9 @@
             uint16_t* base = &buffer[PAD + alignment];
             sk_memset16(base, VALUE16, count);
 
-            REPORTER_ASSERT(reporter,
-                compare16(buffer,       0,       PAD + alignment) &&
-                compare16(base,         VALUE16, count) &&
-                compare16(base + count, 0,       TOTAL - count - PAD - alignment));
+            compare16(reporter, buffer,       0,       PAD + alignment);
+            compare16(reporter, base,         VALUE16, count);
+            compare16(reporter, base + count, 0,       TOTAL - count - PAD - alignment);
         }
     }
 }
@@ -102,10 +71,9 @@
             uint32_t* base = &buffer[PAD + alignment];
             sk_memset32(base, VALUE32, count);
 
-            REPORTER_ASSERT(reporter,
-                compare32(buffer,       0,       PAD + alignment) &&
-                compare32(base,         VALUE32, count) &&
-                compare32(base + count, 0,       TOTAL - count - PAD - alignment));
+            compare32(reporter, buffer,       0,       PAD + alignment);
+            compare32(reporter, base,         VALUE32, count);
+            compare32(reporter, base + count, 0,       TOTAL - count - PAD - alignment);
         }
     }
 }
@@ -118,6 +86,4 @@
 DEF_TEST(Memset, reporter) {
     test_16(reporter);
     test_32(reporter);
-
-    test_chunkalloc(reporter);
 }
diff --git a/src/third_party/skia/tests/MessageBusTest.cpp b/src/third_party/skia/tests/MessageBusTest.cpp
index f7a02b2..163addf 100644
--- a/src/third_party/skia/tests/MessageBusTest.cpp
+++ b/src/third_party/skia/tests/MessageBusTest.cpp
@@ -25,7 +25,7 @@
     SkMessageBus<TestMessage>::Post(m2);
 
     // Make sure we got two.
-    SkTDArray<TestMessage> messages;
+    SkTArray<TestMessage> messages;
     inbox1.poll(&messages);
     REPORTER_ASSERT(r, 2 == messages.count());
     REPORTER_ASSERT(r, 5 == messages[0].x);
diff --git a/src/third_party/skia/tests/MetaDataTest.cpp b/src/third_party/skia/tests/MetaDataTest.cpp
index eb7eae9..2567398 100644
--- a/src/third_party/skia/tests/MetaDataTest.cpp
+++ b/src/third_party/skia/tests/MetaDataTest.cpp
@@ -7,10 +7,11 @@
 
 #include "SkMetaData.h"
 #include "Test.h"
+#include "SkRefCnt.h"
 
 static void test_ptrs(skiatest::Reporter* reporter) {
     SkRefCnt ref;
-    REPORTER_ASSERT(reporter, 1 == ref.getRefCnt());
+    REPORTER_ASSERT(reporter, ref.unique());
 
     {
         SkMetaData md0, md1;
@@ -19,19 +20,19 @@
         md0.setRefCnt(name, &ref);
         REPORTER_ASSERT(reporter, md0.findRefCnt(name));
         REPORTER_ASSERT(reporter, md0.hasRefCnt(name, &ref));
-        REPORTER_ASSERT(reporter, 2 == ref.getRefCnt());
+        REPORTER_ASSERT(reporter, !ref.unique());
 
         md1 = md0;
         REPORTER_ASSERT(reporter, md1.findRefCnt(name));
         REPORTER_ASSERT(reporter, md1.hasRefCnt(name, &ref));
-        REPORTER_ASSERT(reporter, 3 == ref.getRefCnt());
+        REPORTER_ASSERT(reporter, !ref.unique());
 
         REPORTER_ASSERT(reporter, md0.removeRefCnt(name));
         REPORTER_ASSERT(reporter, !md0.findRefCnt(name));
         REPORTER_ASSERT(reporter, !md0.hasRefCnt(name, &ref));
-        REPORTER_ASSERT(reporter, 2 == ref.getRefCnt());
+        REPORTER_ASSERT(reporter, !ref.unique());
     }
-    REPORTER_ASSERT(reporter, 1 == ref.getRefCnt());
+    REPORTER_ASSERT(reporter, ref.unique());
 }
 
 DEF_TEST(MetaData, reporter) {
@@ -83,7 +84,7 @@
     int                 loop = 0;
     int count;
     SkMetaData::Type    t;
-    while ((name = iter.next(&t, &count)) != NULL)
+    while ((name = iter.next(&t, &count)) != nullptr)
     {
         int match = 0;
         for (unsigned i = 0; i < SK_ARRAY_COUNT(gElems); i++)
diff --git a/src/third_party/skia/tests/MiniDataTest.cpp b/src/third_party/skia/tests/MiniDataTest.cpp
deleted file mode 100644
index cb656b3..0000000
--- a/src/third_party/skia/tests/MiniDataTest.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-#include "SkMiniData.h"
-#include "Test.h"
-
-DEF_TEST(MiniData, r) {
-    static const char* s = "abcdefghijklmnopqrstuvwxyz";
-
-    for (size_t len = 0; len <= 26; len++) {
-        SkMiniData md(s, len);
-        REPORTER_ASSERT(r, md.len() == len);
-        REPORTER_ASSERT(r, 0 == memcmp(md.data(), s, len));
-
-        SkMiniData copy(md);
-        REPORTER_ASSERT(r, copy.len() == len);
-        REPORTER_ASSERT(r, 0 == memcmp(copy.data(), s, len));
-    }
-}
diff --git a/src/third_party/skia/tests/MipMapTest.cpp b/src/third_party/skia/tests/MipMapTest.cpp
index 33f4672..7e0a653 100644
--- a/src/third_party/skia/tests/MipMapTest.cpp
+++ b/src/third_party/skia/tests/MipMapTest.cpp
@@ -10,12 +10,8 @@
 #include "SkRandom.h"
 #include "Test.h"
 
-static void make_bitmap(SkBitmap* bm, SkRandom& rand) {
-    // for now, Build needs a min size of 2, otherwise it will return NULL.
-    // should fix that to support 1 X N, where N > 1 to return non-null.
-    int w = 2 + rand.nextU() % 1000;
-    int h = 2 + rand.nextU() % 1000;
-    bm->allocN32Pixels(w, h);
+static void make_bitmap(SkBitmap* bm, int width, int height) {
+    bm->allocN32Pixels(width, height);
     bm->eraseColor(SK_ColorWHITE);
 }
 
@@ -24,11 +20,16 @@
     SkRandom rand;
 
     for (int i = 0; i < 500; ++i) {
-        make_bitmap(&bm, rand);
-        SkAutoTUnref<SkMipMap> mm(SkMipMap::Build(bm));
+        int width = 1 + rand.nextU() % 1000;
+        int height = 1 + rand.nextU() % 1000;
+        make_bitmap(&bm, width, height);
+        sk_sp<SkMipMap> mm(SkMipMap::Build(bm, SkDestinationSurfaceColorMode::kLegacy, nullptr));
 
-        REPORTER_ASSERT(reporter, !mm->extractLevel(SK_Scalar1, NULL));
-        REPORTER_ASSERT(reporter, !mm->extractLevel(SK_Scalar1 * 2, NULL));
+        REPORTER_ASSERT(reporter, mm->countLevels() == SkMipMap::ComputeLevelCount(width, height));
+        REPORTER_ASSERT(reporter, !mm->extractLevel(SkSize::Make(SK_Scalar1, SK_Scalar1),
+                                                    nullptr));
+        REPORTER_ASSERT(reporter, !mm->extractLevel(SkSize::Make(SK_Scalar1 * 2, SK_Scalar1 * 2),
+                                                    nullptr));
 
         SkMipMap::Level prevLevel;
         sk_bzero(&prevLevel, sizeof(prevLevel));
@@ -38,18 +39,166 @@
             scale = scale * 2 / 3;
 
             SkMipMap::Level level;
-            if (mm->extractLevel(scale, &level)) {
-                REPORTER_ASSERT(reporter, level.fPixels);
-                REPORTER_ASSERT(reporter, level.fWidth > 0);
-                REPORTER_ASSERT(reporter, level.fHeight > 0);
-                REPORTER_ASSERT(reporter, level.fRowBytes >= level.fWidth * 4);
+            if (mm->extractLevel(SkSize::Make(scale, scale), &level)) {
+                REPORTER_ASSERT(reporter, level.fPixmap.addr());
+                REPORTER_ASSERT(reporter, level.fPixmap.width() > 0);
+                REPORTER_ASSERT(reporter, level.fPixmap.height() > 0);
+                REPORTER_ASSERT(reporter, (int)level.fPixmap.rowBytes() >= level.fPixmap.width() * 4);
 
-                if (prevLevel.fPixels) {
-                    REPORTER_ASSERT(reporter, level.fWidth <= prevLevel.fWidth);
-                    REPORTER_ASSERT(reporter, level.fHeight <= prevLevel.fHeight);
+                if (prevLevel.fPixmap.addr()) {
+                    REPORTER_ASSERT(reporter, level.fPixmap.width() <= prevLevel.fPixmap.width());
+                    REPORTER_ASSERT(reporter, level.fPixmap.height() <= prevLevel.fPixmap.height());
                 }
                 prevLevel = level;
             }
         }
     }
 }
+
+static void test_mipmap_generation(int width, int height, int expectedMipLevelCount,
+                                   skiatest::Reporter* reporter) {
+    SkBitmap bm;
+    bm.allocN32Pixels(width, height);
+    bm.eraseColor(SK_ColorWHITE);
+    sk_sp<SkMipMap> mm(SkMipMap::Build(bm, SkDestinationSurfaceColorMode::kLegacy, nullptr));
+
+    const int mipLevelCount = mm->countLevels();
+    REPORTER_ASSERT(reporter, mipLevelCount == expectedMipLevelCount);
+    REPORTER_ASSERT(reporter, mipLevelCount == SkMipMap::ComputeLevelCount(width, height));
+    for (int i = 0; i < mipLevelCount; ++i) {
+        SkMipMap::Level level;
+        REPORTER_ASSERT(reporter, mm->getLevel(i, &level));
+        // Make sure the mipmaps contain valid data and that the sizes are correct
+        REPORTER_ASSERT(reporter, level.fPixmap.addr());
+        SkISize size = SkMipMap::ComputeLevelSize(width, height, i);
+        REPORTER_ASSERT(reporter, level.fPixmap.width() == size.width());
+        REPORTER_ASSERT(reporter, level.fPixmap.height() == size.height());
+
+        // + 1 because SkMipMap does not include the base mipmap level.
+        int twoToTheMipLevel = 1 << (i + 1);
+        int currentWidth = width / twoToTheMipLevel;
+        int currentHeight = height / twoToTheMipLevel;
+        REPORTER_ASSERT(reporter, level.fPixmap.width() == currentWidth);
+        REPORTER_ASSERT(reporter, level.fPixmap.height() == currentHeight);
+    }
+}
+
+DEF_TEST(MipMap_DirectLevelAccess, reporter) {
+    // create mipmap with invalid size
+    {
+        // SkMipMap current requires the dimensions be greater than 2x2
+        SkBitmap bm;
+        bm.allocN32Pixels(1, 1);
+        bm.eraseColor(SK_ColorWHITE);
+        sk_sp<SkMipMap> mm(SkMipMap::Build(bm, SkDestinationSurfaceColorMode::kLegacy, nullptr));
+
+        REPORTER_ASSERT(reporter, mm == nullptr);
+    }
+
+    // check small mipmap's count and levels
+    // There should be 5 mipmap levels generated:
+    // 16x16, 8x8, 4x4, 2x2, 1x1
+    test_mipmap_generation(32, 32, 5, reporter);
+
+    // check large mipmap's count and levels
+    // There should be 9 mipmap levels generated:
+    // 500x500, 250x250, 125x125, 62x62, 31x31, 15x15, 7x7, 3x3, 1x1
+    test_mipmap_generation(1000, 1000, 9, reporter);
+}
+
+struct LevelCountScenario {
+    int fWidth;
+    int fHeight;
+    int fExpectedLevelCount;
+};
+
+DEF_TEST(MipMap_ComputeLevelCount, reporter) {
+    const LevelCountScenario tests[] = {
+        // Test mipmaps with negative sizes
+        {-100, 100, 0},
+        {100, -100, 0},
+        {-100, -100, 0},
+
+        // Test mipmaps with 0, 1, 2 as dimensions
+        // (SkMipMap::Build requires a min size of 1)
+        //
+        // 0
+        {0, 100, 0},
+        {100, 0, 0},
+        {0, 0, 0},
+        // 1
+        {1, 100, 6},
+        {100, 1, 6},
+        {1, 1, 0},
+        // 2
+        {2, 100, 6},
+        {100, 2, 6},
+        {2, 2, 1},
+
+        // Test a handful of boundaries such as 63x63 and 64x64
+        {63, 63, 5},
+        {64, 64, 6},
+        {127, 127, 6},
+        {128, 128, 7},
+        {255, 255, 7},
+        {256, 256, 8},
+
+        // Test different dimensions, such as 256x64
+        {64, 129, 7},
+        {255, 32, 7},
+        {500, 1000, 9}
+    };
+
+    for (auto& currentTest : tests) {
+        int levelCount = SkMipMap::ComputeLevelCount(currentTest.fWidth, currentTest.fHeight);
+        REPORTER_ASSERT(reporter, currentTest.fExpectedLevelCount == levelCount);
+    }
+}
+
+struct LevelSizeScenario {
+    int fBaseWidth;
+    int fBaseHeight;
+    int fLevel;
+    SkISize fExpectedMipMapLevelSize;
+};
+
+DEF_TEST(MipMap_ComputeLevelSize, reporter) {
+    const LevelSizeScenario tests[] = {
+        // Test mipmaps with negative sizes
+        {-100, 100, 0, SkISize::Make(0, 0)},
+        {100, -100, 0, SkISize::Make(0, 0)},
+        {-100, -100, 0, SkISize::Make(0, 0)},
+
+        // Test mipmaps with 0, 1, 2 as dimensions
+        // (SkMipMap::Build requires a min size of 1)
+        //
+        // 0
+        {0, 100, 0, SkISize::Make(0, 0)},
+        {100, 0, 0, SkISize::Make(0, 0)},
+        {0, 0, 0, SkISize::Make(0, 0)},
+        // 1
+
+        {1, 100, 0, SkISize::Make(1, 50)},
+        {100, 1, 0, SkISize::Make(50, 1)},
+        {1, 1, 0, SkISize::Make(0, 0)},
+        // 2
+        {2, 100, 0, SkISize::Make(1, 50)},
+        {100, 2, 1, SkISize::Make(25, 1)},
+        {2, 2, 0, SkISize::Make(1, 1)},
+
+        // Test a handful of cases
+        {63, 63, 2, SkISize::Make(7, 7)},
+        {64, 64, 2, SkISize::Make(8, 8)},
+        {127, 127, 2, SkISize::Make(15, 15)},
+        {64, 129, 3, SkISize::Make(4, 8)},
+        {255, 32, 6, SkISize::Make(1, 1)},
+        {500, 1000, 1, SkISize::Make(125, 250)},
+    };
+
+    for (auto& currentTest : tests) {
+        SkISize levelSize = SkMipMap::ComputeLevelSize(currentTest.fBaseWidth,
+                                                       currentTest.fBaseHeight,
+                                                       currentTest.fLevel);
+        REPORTER_ASSERT(reporter, currentTest.fExpectedMipMapLevelSize == levelSize);
+    }
+}
diff --git a/src/third_party/skia/tests/NameAllocatorTest.cpp b/src/third_party/skia/tests/NameAllocatorTest.cpp
deleted file mode 100644
index 86efdb2..0000000
--- a/src/third_party/skia/tests/NameAllocatorTest.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#if SK_SUPPORT_GPU
-
-#include "gl/GrGLNameAllocator.h"
-#include "Test.h"
-
-////////////////////////////////////////////////////////////////////////////////
-
-class NameLeakTest {
-    static const GrGLuint kFirstName = 101;
-    static const GrGLuint kRange = 1013;
-
-public:
-    NameLeakTest(skiatest::Reporter* reporter)
-        : fReporter(reporter),
-          fAllocator(kFirstName, kFirstName + kRange),
-          fAllocatedCount(0),
-          fRandomName(kFirstName + 4 * kRange / 7) {
-        memset(fAllocatedNames, 0, sizeof(fAllocatedNames));
-    }
-
-    bool run() {
-        if (!this->allocateAllRemaining()) {
-            return false;
-        }
-
-        for (GrGLuint freeCount = 1; freeCount <= kRange; ++freeCount) {
-            if (!this->freeRandomNames(freeCount)) {
-                return false;
-            }
-            if (!this->allocateAllRemaining()) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-private:
-    bool isAllocated(GrGLuint name) const {
-        return fAllocatedNames[name - kFirstName];
-    }
-
-    void setAllocated(GrGLuint name, bool allocated) {
-        fAllocatedNames[name - kFirstName] = allocated;
-    }
-
-    bool allocateAllRemaining() {
-        for (; fAllocatedCount < kRange; ++fAllocatedCount) {
-            GrGLuint name = fAllocator.allocateName();
-            if (0 == name) {
-                ERRORF(fReporter,
-                       "Name allocate failed, but there should still be %u free names",
-                       kRange - fAllocatedCount);
-                return false;
-            }
-            if (name < kFirstName || name >= kFirstName + kRange) {
-                ERRORF(fReporter,
-                       "Name allocate returned name %u outside its bounds [%u, %u)",
-                       name, kFirstName, kFirstName + kRange);
-                return false;
-            }
-            if (this->isAllocated(name)) {
-                ERRORF(fReporter, "Name allocate returned name that is already allocated");
-                return false;
-            }
-
-            this->setAllocated(name, true);
-        }
-
-        // Ensure it returns 0 once all the names are allocated.
-        GrGLuint name = fAllocator.allocateName();
-        if (0 != name) {
-            ERRORF(fReporter,
-                   "Name allocate did not fail when all names were already in use");
-            return false;
-        }
-
-        // Ensure every unique name is allocated.
-        for (GrGLuint i = 0; i < kRange; ++i) {
-            if (!this->isAllocated(kFirstName + i)) {
-                ERRORF(fReporter, "Not all unique names are allocated after allocateAllRemaining()");
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    bool freeRandomNames(GrGLuint count) {
-        // The values a and c make up an LCG (pseudo-random generator). These
-        // values must satisfy the Hull-Dobell Theorem (with m=kRange):
-        // http://en.wikipedia.org/wiki/Linear_congruential_generator
-        // We use our own generator to guarantee it hits each unique value
-        // within kRange exactly once before repeating.
-        const GrGLuint seed = (count + fRandomName) / 2;
-        const GrGLuint a = seed * kRange + 1;
-        const GrGLuint c = (seed * 743) % kRange;
-
-        for (GrGLuint i = 0; i < count; ++i) {
-            fRandomName = (a * fRandomName + c) % kRange;
-            const GrGLuint name = kFirstName + fRandomName;
-            if (!this->isAllocated(name)) {
-                ERRORF(fReporter, "Test bug: Should not free a not-allocated name at this point (%u)", i);
-                return false;
-            }
-
-            fAllocator.free(name);
-            this->setAllocated(name, false);
-            --fAllocatedCount;
-        }
-
-        return true;
-    }
-
-    skiatest::Reporter* fReporter;
-    GrGLNameAllocator fAllocator;
-    bool fAllocatedNames[kRange];
-    GrGLuint fAllocatedCount;
-    GrGLuint fRandomName;
-};
-
-DEF_GPUTEST(NameAllocator, reporter, factory) {
-    // Ensure no names are leaked or double-allocated during heavy usage.
-    {
-        NameLeakTest nameLeakTest(reporter);
-        nameLeakTest.run();
-    }
-
-    static const GrGLuint range = 32;
-    GrGLNameAllocator allocator(1, 1 + range);
-    for (GrGLuint i = 1; i <= range; ++i) {
-        allocator.allocateName();
-    }
-    REPORTER_ASSERT(reporter, 0 == allocator.allocateName());
-
-    // Test freeing names out of range.
-    allocator.free(allocator.firstName() - 1);
-    allocator.free(allocator.endName());
-    REPORTER_ASSERT(reporter, 0 == allocator.allocateName());
-
-    // Test freeing not-allocated names.
-    for (GrGLuint i = 1; i <= range/2; i += 2) {
-        allocator.free(i);
-    }
-    for (GrGLuint i = 1; i <= range/2; i += 2) {
-        // None of these names will be allocated.
-        allocator.free(i);
-    }
-    for (GrGLuint i = 1; i <= range/2; ++i) {
-        // Every other name will not be be allocated.
-        allocator.free(i);
-    }
-    for (GrGLuint i = 1; i <= range/2; ++i) {
-        if (0 == allocator.allocateName()) {
-            ERRORF(reporter, "Name allocate failed when there should be free names");
-            break;
-        }
-    }
-    REPORTER_ASSERT(reporter, 0 == allocator.allocateName());
-}
-
-#endif
diff --git a/src/third_party/skia/tests/OSPathTest.cpp b/src/third_party/skia/tests/OSPathTest.cpp
index facc6ad..22deff8 100644
--- a/src/third_party/skia/tests/OSPathTest.cpp
+++ b/src/third_party/skia/tests/OSPathTest.cpp
@@ -5,7 +5,7 @@
  * found in the LICENSE file.
  */
 
-#include "SkOSFile.h"
+#include "SkOSPath.h"
 #include "SkString.h"
 #include "Test.h"
 
@@ -15,24 +15,24 @@
  *  and tests using SkOSPath::Basename on the result.
  *  @param reporter Reporter for test conditions.
  *  @param dir String representing the path to a folder. May or may not
- *      end with SkPATH_SEPARATOR.
+ *      end with SkOSPath::SEPARATOR.
  *  @param filename String representing the basename of a file. Must NOT
- *      contain SkPATH_SEPARATOR.
+ *      contain SkOSPath::SEPARATOR.
  */
 static void test_dir_with_file(skiatest::Reporter* reporter, SkString dir,
                                SkString filename) {
-    // If filename contains SkPATH_SEPARATOR, the tests will fail.
-    SkASSERT(!filename.contains(SkPATH_SEPARATOR));
+    // If filename contains SkOSPath::SEPARATOR, the tests will fail.
+    SkASSERT(!filename.contains(SkOSPath::SEPARATOR));
 
     // Tests for SkOSPath::Join and SkOSPath::Basename
 
-    // fullName should be "dir<SkPATH_SEPARATOR>file"
+    // fullName should be "dir<SkOSPath::SEPARATOR>file"
     SkString fullName = SkOSPath::Join(dir.c_str(), filename.c_str());
 
     // fullName should be the combined size of dir and file, plus one if
     // dir did not include the final path separator.
     size_t expectedSize = dir.size() + filename.size();
-    if (!dir.endsWith(SkPATH_SEPARATOR) && !dir.isEmpty()) {
+    if (!dir.endsWith(SkOSPath::SEPARATOR) && !dir.isEmpty()) {
         expectedSize++;
     }
     REPORTER_ASSERT(reporter, fullName.size() == expectedSize);
@@ -46,7 +46,7 @@
     // dirname should be the same as dir with any trailing seperators removed.
     // Except when the the string is just "/".
     SkString strippedDir = dir;
-    while (strippedDir.size() > 2 && strippedDir[strippedDir.size() - 1] == SkPATH_SEPARATOR) {
+    while (strippedDir.size() > 2 && strippedDir[strippedDir.size() - 1] == SkOSPath::SEPARATOR) {
         strippedDir.remove(strippedDir.size() - 1, 1);
     }
     if (!dirname.equals(strippedDir)) {
@@ -55,7 +55,7 @@
     REPORTER_ASSERT(reporter, dirname.equals(strippedDir));
 
     // basename will not contain a path separator
-    REPORTER_ASSERT(reporter, !basename.contains(SkPATH_SEPARATOR));
+    REPORTER_ASSERT(reporter, !basename.contains(SkOSPath::SEPARATOR));
 
     // Now take the basename of filename, which should be the same as filename.
     basename = SkOSPath::Basename(filename.c_str());
@@ -68,7 +68,7 @@
     test_dir_with_file(reporter, dir, filename);
 
     // Now make sure this works with a path separator at the end of dir.
-    dir.appendUnichar(SkPATH_SEPARATOR);
+    dir.appendUnichar(SkOSPath::SEPARATOR);
     test_dir_with_file(reporter, dir, filename);
 
     // Test using no filename.
@@ -82,16 +82,16 @@
     test_dir_with_file(reporter, dir, filename);
 
     // Basename of a directory with a path separator at the end is empty.
-    dir.appendUnichar(SkPATH_SEPARATOR);
+    dir.appendUnichar(SkOSPath::SEPARATOR);
     SkString baseOfDir = SkOSPath::Basename(dir.c_str());
     REPORTER_ASSERT(reporter, baseOfDir.size() == 0);
 
-    // Basename of NULL is an empty string.
-    SkString empty = SkOSPath::Basename(NULL);
+    // Basename of nullptr is an empty string.
+    SkString empty = SkOSPath::Basename(nullptr);
     REPORTER_ASSERT(reporter, empty.size() == 0);
 
     // File in root dir
-    dir.printf("%c", SkPATH_SEPARATOR);
+    dir.printf("%c", SkOSPath::SEPARATOR);
     filename.set("file");
     test_dir_with_file(reporter, dir, filename);
 
@@ -99,7 +99,7 @@
     filename.reset();
     test_dir_with_file(reporter, dir, filename);
 
-    // Test that NULL can be used for the directory and filename.
-    SkString emptyPath = SkOSPath::Join(NULL, NULL);
+    // Test that nullptr can be used for the directory and filename.
+    SkString emptyPath = SkOSPath::Join(nullptr, nullptr);
     REPORTER_ASSERT(reporter, emptyPath.isEmpty());
 }
diff --git a/src/third_party/skia/tests/ObjectPoolTest.cpp b/src/third_party/skia/tests/ObjectPoolTest.cpp
deleted file mode 100644
index 404448e..0000000
--- a/src/third_party/skia/tests/ObjectPoolTest.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkTObjectPool.h"
-#include "SkTObjectPool.h"
-#include "Test.h"
-
-class PoolEntry {
-public:
-private:
-    SK_DECLARE_INTERNAL_SLIST_INTERFACE(PoolEntry);
-};
-
-static const int kNumItemsPerBlock = 3;
-typedef SkTObjectPool<PoolEntry, kNumItemsPerBlock> ObjectPoolType;
-
-static bool verifyPool(skiatest::Reporter* reporter,
-                       const ObjectPoolType& pool,
-                       const char* stage,
-                       int available, int blocks) {
-    if (available != pool.available()) {
-        ERRORF(reporter, "%s - Pool available is %d not %d",
-               stage, pool.available(), available);
-        return false;
-    }
-    if (blocks != pool.blocks()) {
-        ERRORF(reporter, "%s - Pool blocks is %d not %d",
-               stage, pool.blocks(), blocks);
-        return false;
-    }
-    return true;
-}
-
-static const int kNumToAcquire = kNumItemsPerBlock * 5;
-static void testObjectPool(skiatest::Reporter* reporter) {
-    ObjectPoolType pool;
-    SkTInternalSList<PoolEntry> used;
-    verifyPool(reporter, pool, "empty", 0, 0);
-    for (int index = 0; index < kNumToAcquire; ++index) {
-        used.push(pool.acquire());
-        int blocks = (index / kNumItemsPerBlock) + 1;
-        int available = (blocks * kNumItemsPerBlock) - (index + 1);
-        if (!verifyPool(reporter, pool, "acquire", available, blocks)) {
-            return;
-        }
-    }
-    int available = pool.available();
-    int blocks = pool.blocks();
-    for (int index = 0; index < kNumToAcquire / 2; ++index) {
-        pool.release(used.pop());
-        ++available;
-        if (!verifyPool(reporter, pool, "release", available, blocks)) {
-            return;
-        }
-    }
-    available += used.getCount();
-    pool.releaseAll(&used);
-    REPORTER_ASSERT(reporter, used.isEmpty());
-    verifyPool(reporter, pool, "releaseAll", available, blocks);
-}
-
-DEF_TEST(ObjectPool, reporter) {
-    testObjectPool(reporter);
-}
diff --git a/src/third_party/skia/tests/OnFlushCallbackTest.cpp b/src/third_party/skia/tests/OnFlushCallbackTest.cpp
new file mode 100644
index 0000000..c7503bc
--- /dev/null
+++ b/src/third_party/skia/tests/OnFlushCallbackTest.cpp
@@ -0,0 +1,619 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrBackendSemaphore.h"
+#include "GrClip.h"
+#include "GrContextPriv.h"
+#include "GrDefaultGeoProcFactory.h"
+#include "GrOnFlushResourceProvider.h"
+#include "GrRenderTargetContextPriv.h"
+#include "GrResourceProvider.h"
+#include "GrQuad.h"
+#include "effects/GrSimpleTextureEffect.h"
+#include "ops/GrSimpleMeshDrawOpHelper.h"
+
+namespace {
+// This is a simplified mesh drawing op that can be used in the atlas generation test.
+// Please see AtlasedRectOp below.
+class NonAARectOp : public GrMeshDrawOp {
+protected:
+    using Helper = GrSimpleMeshDrawOpHelper;
+
+public:
+    DEFINE_OP_CLASS_ID
+    const char* name() const override { return "NonAARectOp"; }
+
+    // This creates an instance of a simple non-AA solid color rect-drawing Op
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkRect& r) {
+        return Helper::FactoryHelper<NonAARectOp>(std::move(paint), r, nullptr, ClassID());
+    }
+
+    // This creates an instance of a simple non-AA textured rect-drawing Op
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkRect& r, const SkRect& local) {
+        return Helper::FactoryHelper<NonAARectOp>(std::move(paint), r, &local, ClassID());
+    }
+
+    GrColor color() const { return fColor; }
+
+    NonAARectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkRect& r,
+                const SkRect* localRect, int32_t classID)
+            : INHERITED(classID)
+            , fColor(color)
+            , fHasLocalRect(SkToBool(localRect))
+            , fRect(r)
+            , fHelper(helperArgs, GrAAType::kNone) {
+        if (fHasLocalRect) {
+            fLocalQuad.set(*localRect);
+        }
+        // Choose some conservative values for aa bloat and zero area.
+        this->setBounds(r, HasAABloat::kYes, IsZeroArea::kYes);
+    }
+
+    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
+
+    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip*) override {
+        // Set the color to unknown because the subclass may change the color later.
+        GrProcessorAnalysisColor gpColor;
+        gpColor.setToUnknown();
+        // We ignore the clip so pass this rather than the GrAppliedClip param.
+        static GrAppliedClip kNoClip;
+        return fHelper.xpRequiresDstTexture(caps, &kNoClip, GrProcessorAnalysisCoverage::kNone,
+                                            &gpColor);
+    }
+
+protected:
+    GrColor fColor;
+    bool    fHasLocalRect;
+    GrQuad  fLocalQuad;
+    SkRect  fRect;
+
+private:
+    bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
+
+    void onPrepareDraws(Target* target) const override {
+        using namespace GrDefaultGeoProcFactory;
+
+        // The vertex attrib order is always pos, color, local coords.
+        static const int kColorOffset = sizeof(SkPoint);
+        static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
+
+        sk_sp<GrGeometryProcessor> gp =
+                GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
+                                              Coverage::kSolid_Type,
+                                              fHasLocalRect ? LocalCoords::kHasExplicit_Type
+                                                            : LocalCoords::kUnused_Type,
+                                              SkMatrix::I());
+        if (!gp) {
+            SkDebugf("Couldn't create GrGeometryProcessor for GrAtlasedOp\n");
+            return;
+        }
+
+        size_t vertexStride = gp->getVertexStride();
+
+        SkASSERT(fHasLocalRect
+                    ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
+                    : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
+
+        const GrBuffer* indexBuffer;
+        int firstIndex;
+        uint16_t* indices = target->makeIndexSpace(6, &indexBuffer, &firstIndex);
+        if (!indices) {
+            SkDebugf("Indices could not be allocated for GrAtlasedOp.\n");
+            return;
+        }
+
+        const GrBuffer* vertexBuffer;
+        int firstVertex;
+        void* vertices = target->makeVertexSpace(vertexStride, 4, &vertexBuffer, &firstVertex);
+        if (!vertices) {
+            SkDebugf("Vertices could not be allocated for GrAtlasedOp.\n");
+            return;
+        }
+
+        // Setup indices
+        indices[0] = 0;
+        indices[1] = 1;
+        indices[2] = 2;
+        indices[3] = 0;
+        indices[4] = 2;
+        indices[5] = 3;
+
+        // Setup positions
+        SkPoint* position = (SkPoint*) vertices;
+        position->setRectFan(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, vertexStride);
+
+        // Setup vertex colors
+        GrColor* color = (GrColor*)((intptr_t)vertices + kColorOffset);
+        for (int i = 0; i < 4; ++i) {
+            *color = fColor;
+            color = (GrColor*)((intptr_t)color + vertexStride);
+        }
+
+        // Setup local coords
+        if (fHasLocalRect) {
+            SkPoint* coords = (SkPoint*)((intptr_t) vertices + kLocalOffset);
+            for (int i = 0; i < 4; i++) {
+                *coords = fLocalQuad.point(i);
+                coords = (SkPoint*)((intptr_t) coords + vertexStride);
+            }
+        }
+
+        GrMesh mesh(GrPrimitiveType::kTriangles);
+        mesh.setIndexed(indexBuffer, 6, firstIndex, 0, 3);
+        mesh.setVertexData(vertexBuffer, firstVertex);
+
+        target->draw(gp.get(), fHelper.makePipeline(target), mesh);
+    }
+
+    Helper fHelper;
+
+    typedef GrMeshDrawOp INHERITED;
+};
+
+}  // anonymous namespace
+
+#ifdef SK_DEBUG
+#include "SkImageEncoder.h"
+#include "sk_tool_utils.h"
+
+static void save_bm(const SkBitmap& bm, const char name[]) {
+    bool result = sk_tool_utils::EncodeImageToFile(name, bm, SkEncodedImageFormat::kPNG, 100);
+    SkASSERT(result);
+}
+#endif
+
+static constexpr SkRect kEmptyRect = SkRect::MakeEmpty();
+
+namespace {
+
+/*
+ * Atlased ops just draw themselves as textured rects with the texture pixels being
+ * pulled out of the atlas. Their color is based on their ID.
+ */
+class AtlasedRectOp final : public NonAARectOp {
+public:
+    DEFINE_OP_CLASS_ID
+
+    ~AtlasedRectOp() override {
+        fID = -1;
+    }
+
+    const char* name() const override { return "AtlasedRectOp"; }
+
+    int id() const { return fID; }
+
+    static std::unique_ptr<AtlasedRectOp> Make(GrPaint&& paint, const SkRect& r, int id) {
+        GrDrawOp* op = Helper::FactoryHelper<AtlasedRectOp>(std::move(paint), r, id).release();
+        return std::unique_ptr<AtlasedRectOp>(static_cast<AtlasedRectOp*>(op));
+    }
+
+    // We set the initial color of the NonAARectOp based on the ID.
+    // Note that we force creation of a NonAARectOp that has local coords in anticipation of
+    // pulling from the atlas.
+    AtlasedRectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkRect& r, int id)
+            : INHERITED(helperArgs, kColors[id], r, &kEmptyRect, ClassID())
+            , fID(id)
+            , fNext(nullptr) {
+        SkASSERT(fID < kMaxIDs);
+    }
+
+    void setColor(GrColor color) { fColor = color; }
+    void setLocalRect(const SkRect& localRect) {
+        SkASSERT(fHasLocalRect);    // This should've been created to anticipate this
+        fLocalQuad.set(localRect);
+    }
+
+    AtlasedRectOp* next() const { return fNext; }
+    void setNext(AtlasedRectOp* next) {
+        fNext = next;
+    }
+
+private:
+
+    static const int kMaxIDs = 9;
+    static const SkColor kColors[kMaxIDs];
+
+    int            fID;
+    // The Atlased ops have an internal singly-linked list of ops that land in the same opList
+    AtlasedRectOp* fNext;
+
+    typedef NonAARectOp INHERITED;
+};
+
+}  // anonymous namespace
+
+const GrColor AtlasedRectOp::kColors[kMaxIDs] = {
+    GrColorPackRGBA(255, 0, 0, 255),
+    GrColorPackRGBA(0, 255, 0, 255),
+    GrColorPackRGBA(0, 0, 255, 255),
+    GrColorPackRGBA(0, 255, 255, 255),
+    GrColorPackRGBA(255, 0, 255, 255),
+    GrColorPackRGBA(255, 255, 0, 255),
+    GrColorPackRGBA(0, 0, 0, 255),
+    GrColorPackRGBA(128, 128, 128, 255),
+    GrColorPackRGBA(255, 255, 255, 255)
+};
+
+static const int kDrawnTileSize = 16;
+
+/*
+ * Rather than performing any rect packing, this atlaser just lays out constant-sized
+ * tiles in an Nx1 row
+ */
+static const int kAtlasTileSize = 2;
+
+/*
+ * This class aggregates the op information required for atlasing
+ */
+class AtlasObject final : public GrOnFlushCallbackObject {
+public:
+    AtlasObject() : fDone(false) { }
+
+    ~AtlasObject() override {
+        SkASSERT(fDone);
+    }
+
+    void markAsDone() {
+        fDone = true;
+    }
+
+    // Insert the new op in an internal singly-linked list for 'opListID'
+    void addOp(uint32_t opListID, AtlasedRectOp* op) {
+        LinkedListHeader* header = nullptr;
+        for (int i = 0; i < fOps.count(); ++i) {
+            if (opListID == fOps[i].fID) {
+                header = &(fOps[i]);
+            }
+        }
+
+        if (!header) {
+            fOps.push({opListID, nullptr});
+            header = &(fOps[fOps.count()-1]);
+        }
+
+        op->setNext(header->fHead);
+        header->fHead = op;
+    }
+
+    // For the time being we need to pre-allocate the atlas.
+    void setAtlasDest(sk_sp<GrTextureProxy> atlasDest) {
+        fAtlasDest = atlasDest;
+    }
+
+    void saveRTC(sk_sp<GrRenderTargetContext> rtc) {
+        SkASSERT(!fRTC);
+        fRTC = rtc;
+    }
+
+#ifdef SK_DEBUG
+    void saveAtlasToDisk() {
+        SkBitmap readBack;
+        readBack.allocN32Pixels(fRTC->width(), fRTC->height());
+
+        bool result = fRTC->readPixels(readBack.info(),
+                                       readBack.getPixels(), readBack.rowBytes(), 0, 0);
+        SkASSERT(result);
+        save_bm(readBack, "atlas-real.png");
+    }
+#endif
+
+    /*
+     * This callback back creates the atlas and updates the AtlasedRectOps to read from it
+     */
+    void preFlush(GrOnFlushResourceProvider* resourceProvider,
+                  const uint32_t* opListIDs, int numOpListIDs,
+                  SkTArray<sk_sp<GrRenderTargetContext>>* results) override {
+        SkASSERT(!results->count());
+
+        // Until MDB is landed we will most-likely only have one opList.
+        SkTDArray<LinkedListHeader*> lists;
+        for (int i = 0; i < numOpListIDs; ++i) {
+            if (LinkedListHeader* list = this->getList(opListIDs[i])) {
+                lists.push(list);
+            }
+        }
+
+        if (!lists.count()) {
+            return; // nothing to atlas
+        }
+
+        // TODO: right now we have to pre-allocate the atlas bc the TextureSamplers need a
+        // hard GrTexture
+#if 0
+        GrSurfaceDesc desc;
+        desc.fFlags = kRenderTarget_GrSurfaceFlag;
+        desc.fWidth = this->numOps() * kAtlasTileSize;
+        desc.fHeight = kAtlasTileSize;
+        desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+        sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(desc,
+                                                                                     nullptr,
+                                                                                     nullptr);
+#else
+        // At this point all the GrAtlasedOp's should have lined up to read from 'atlasDest' and
+        // there should either be two writes to clear it or no writes.
+        SkASSERT(9 == fAtlasDest->getPendingReadCnt_TestOnly());
+        SkASSERT(2 == fAtlasDest->getPendingWriteCnt_TestOnly() ||
+                 0 == fAtlasDest->getPendingWriteCnt_TestOnly());
+        sk_sp<GrRenderTargetContext> rtc = resourceProvider->makeRenderTargetContext(
+                                                                           fAtlasDest,
+                                                                           nullptr, nullptr);
+#endif
+
+        rtc->clear(nullptr, 0xFFFFFFFF, true); // clear the atlas
+
+        int blocksInAtlas = 0;
+        for (int i = 0; i < lists.count(); ++i) {
+            for (AtlasedRectOp* op = lists[i]->fHead; op; op = op->next()) {
+                SkIRect r = SkIRect::MakeXYWH(blocksInAtlas*kAtlasTileSize, 0,
+                                              kAtlasTileSize, kAtlasTileSize);
+
+                // For now, we avoid the resource buffer issues and just use clears
+#if 1
+                rtc->clear(&r, op->color(), false);
+#else
+                GrPaint paint;
+                paint.setColor4f(GrColor4f::FromGrColor(op->color()));
+                std::unique_ptr<GrDrawOp> drawOp(NonAARectOp::Make(std::move(paint),
+                                                                   SkRect::Make(r)));
+                rtc->priv().testingOnly_addDrawOp(std::move(drawOp));
+#endif
+                blocksInAtlas++;
+
+                // Set the atlased Op's color to white (so we know we're not using it for
+                // the final draw).
+                op->setColor(0xFFFFFFFF);
+
+                // Set the atlased Op's localRect to point to where it landed in the atlas
+                op->setLocalRect(SkRect::Make(r));
+
+                // TODO: we also need to set the op's GrSuperDeferredSimpleTextureEffect to point
+                // to the rtc's proxy!
+            }
+
+            // We've updated all these ops and we certainly don't want to process them again
+            this->clearOpsFor(lists[i]);
+        }
+
+        // Hide a ref to the RTC in AtlasData so we can check on it later
+        this->saveRTC(rtc);
+
+        results->push_back(std::move(rtc));
+    }
+
+private:
+    typedef struct {
+        uint32_t       fID;
+        AtlasedRectOp* fHead;
+    } LinkedListHeader;
+
+    LinkedListHeader* getList(uint32_t opListID) {
+        for (int i = 0; i < fOps.count(); ++i) {
+            if (opListID == fOps[i].fID) {
+                return &(fOps[i]);
+            }
+        }
+        return nullptr;
+    }
+
+    void clearOpsFor(LinkedListHeader* header) {
+        // The AtlasedRectOps have yet to execute (and this class doesn't own them) so just
+        // forget about them in the laziest way possible.
+        header->fHead = nullptr;
+        header->fID = 0;            // invalid opList ID
+    }
+
+    // Each opList containing AtlasedRectOps gets its own internal singly-linked list
+    SkTDArray<LinkedListHeader>  fOps;
+
+    // The RTC used to create the atlas
+    sk_sp<GrRenderTargetContext> fRTC;
+
+    // For the time being we need to pre-allocate the atlas bc the TextureSamplers require
+    // a GrTexture
+    sk_sp<GrTextureProxy>        fAtlasDest;
+
+    // Set to true when the testing harness expects this object to be no longer used
+    bool                         fDone;
+};
+
+// This creates an off-screen rendertarget whose ops which eventually pull from the atlas.
+static sk_sp<GrTextureProxy> make_upstream_image(GrContext* context, AtlasObject* object, int start,
+                                                 sk_sp<GrTextureProxy> fakeAtlas) {
+    sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext(
+                                                                      SkBackingFit::kApprox,
+                                                                      3*kDrawnTileSize,
+                                                                      kDrawnTileSize,
+                                                                      kRGBA_8888_GrPixelConfig,
+                                                                      nullptr));
+
+    rtc->clear(nullptr, GrColorPackRGBA(255, 0, 0, 255), true);
+
+    for (int i = 0; i < 3; ++i) {
+        SkRect r = SkRect::MakeXYWH(i*kDrawnTileSize, 0, kDrawnTileSize, kDrawnTileSize);
+
+        // TODO: here is the blocker for deferring creation of the atlas. The TextureSamplers
+        // created here currently require a hard GrTexture.
+        sk_sp<GrFragmentProcessor> fp = GrSimpleTextureEffect::Make(fakeAtlas,
+                                                                    nullptr, SkMatrix::I());
+        GrPaint paint;
+        paint.addColorFragmentProcessor(std::move(fp));
+        paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+        std::unique_ptr<AtlasedRectOp> op(AtlasedRectOp::Make(std::move(paint), r, start + i));
+
+        AtlasedRectOp* sparePtr = op.get();
+
+        uint32_t opListID = rtc->priv().testingOnly_addDrawOp(std::move(op));
+
+        object->addOp(opListID, sparePtr);
+    }
+
+    return rtc->asTextureProxyRef();
+}
+
+// Enable this if you want to debug the final draws w/o having the atlasCallback create the
+// atlas
+#if 0
+#include "SkGrPriv.h"
+
+sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) {
+    SkBitmap bm;
+    bm.allocN32Pixels(18, 2, true);
+    bm.erase(SK_ColorRED,     SkIRect::MakeXYWH(0, 0, 2, 2));
+    bm.erase(SK_ColorGREEN,   SkIRect::MakeXYWH(2, 0, 2, 2));
+    bm.erase(SK_ColorBLUE,    SkIRect::MakeXYWH(4, 0, 2, 2));
+    bm.erase(SK_ColorCYAN,    SkIRect::MakeXYWH(6, 0, 2, 2));
+    bm.erase(SK_ColorMAGENTA, SkIRect::MakeXYWH(8, 0, 2, 2));
+    bm.erase(SK_ColorYELLOW,  SkIRect::MakeXYWH(10, 0, 2, 2));
+    bm.erase(SK_ColorBLACK,   SkIRect::MakeXYWH(12, 0, 2, 2));
+    bm.erase(SK_ColorGRAY,    SkIRect::MakeXYWH(14, 0, 2, 2));
+    bm.erase(SK_ColorWHITE,   SkIRect::MakeXYWH(16, 0, 2, 2));
+
+#if 1
+    save_bm(bm, "atlas-fake.png");
+#endif
+
+    GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps());
+    desc.fFlags |= kRenderTarget_GrSurfaceFlag;
+
+    sk_sp<GrSurfaceProxy> tmp = GrSurfaceProxy::MakeDeferred(*context->caps(),
+                                                             context->textureProvider(),
+                                                             desc, SkBudgeted::kYes,
+                                                             bm.getPixels(), bm.rowBytes());
+
+    return sk_ref_sp(tmp->asTextureProxy());
+}
+#else
+// TODO: this is unfortunate and must be removed. We want the atlas to be created later.
+sk_sp<GrTextureProxy> pre_create_atlas(GrContext* context) {
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fConfig = kSkia8888_GrPixelConfig;
+    desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+    desc.fWidth = 32;
+    desc.fHeight = 16;
+    sk_sp<GrSurfaceProxy> atlasDest = GrSurfaceProxy::MakeDeferred(
+                                                            context->resourceProvider(),
+                                                            desc, SkBackingFit::kExact,
+                                                            SkBudgeted::kYes,
+                                                            GrResourceProvider::kNoPendingIO_Flag);
+    return sk_ref_sp(atlasDest->asTextureProxy());
+}
+#endif
+
+static void test_color(skiatest::Reporter* reporter, const SkBitmap& bm, int x, SkColor expected) {
+    SkColor readback = bm.getColor(x, kDrawnTileSize/2);
+    REPORTER_ASSERT(reporter, expected == readback);
+    if (expected != readback) {
+        SkDebugf("Color mismatch: %x %x\n", expected, readback);
+    }
+}
+
+/*
+ * For the atlasing test we make a DAG that looks like:
+ *
+ *    RT1 with ops: 0,1,2       RT2 with ops: 3,4,5       RT3 with ops: 6,7,8
+ *                     \         /
+ *                      \       /
+ *                         RT4
+ * We then flush RT4 and expect only ops 0-5 to be atlased together.
+ * Each op is just a solid colored rect so both the atlas and the final image should appear as:
+ *           R G B C M Y
+ * with the atlas having width = 6*kAtlasTileSize and height = kAtlasTileSize.
+ *
+ * Note: until MDB lands, the atlas will actually have width= 9*kAtlasTileSize and look like:
+ *           R G B C M Y K Grey White
+ */
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(OnFlushCallbackTest, reporter, ctxInfo) {
+    static const int kNumProxies = 3;
+
+    GrContext* context = ctxInfo.grContext();
+
+    if (context->caps()->useDrawInsteadOfClear()) {
+        // TODO: fix the buffer issues so this can run on all devices
+        return;
+    }
+
+    AtlasObject object;
+
+    // For now (until we add a GrSuperDeferredSimpleTextureEffect), we create the final atlas
+    // proxy ahead of time.
+    sk_sp<GrTextureProxy> atlasDest = pre_create_atlas(context);
+
+    object.setAtlasDest(atlasDest);
+
+    context->contextPriv().addOnFlushCallbackObject(&object);
+
+    sk_sp<GrTextureProxy> proxies[kNumProxies];
+    for (int i = 0; i < kNumProxies; ++i) {
+        proxies[i] = make_upstream_image(context, &object, i*3, atlasDest);
+    }
+
+    static const int kFinalWidth = 6*kDrawnTileSize;
+    static const int kFinalHeight = kDrawnTileSize;
+
+    sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext(
+                                                                      SkBackingFit::kApprox,
+                                                                      kFinalWidth,
+                                                                      kFinalHeight,
+                                                                      kRGBA_8888_GrPixelConfig,
+                                                                      nullptr));
+
+    rtc->clear(nullptr, 0xFFFFFFFF, true);
+
+    // Note that this doesn't include the third texture proxy
+    for (int i = 0; i < kNumProxies-1; ++i) {
+        SkRect r = SkRect::MakeXYWH(i*3*kDrawnTileSize, 0, 3*kDrawnTileSize, kDrawnTileSize);
+
+        SkMatrix t = SkMatrix::MakeTrans(-i*3*kDrawnTileSize, 0);
+
+        GrPaint paint;
+        sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(std::move(proxies[i]),
+                                                                  nullptr, t));
+        paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+        paint.addColorFragmentProcessor(std::move(fp));
+
+        rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
+    }
+
+    rtc->prepareForExternalIO(0, nullptr);
+
+    SkBitmap readBack;
+    readBack.allocN32Pixels(kFinalWidth, kFinalHeight);
+
+    SkDEBUGCODE(bool result =) rtc->readPixels(readBack.info(), readBack.getPixels(),
+                                               readBack.rowBytes(), 0, 0);
+    SkASSERT(result);
+
+    context->contextPriv().testingOnly_flushAndRemoveOnFlushCallbackObject(&object);
+
+    object.markAsDone();
+
+#if 0
+    save_bm(readBack, "atlas-final-image.png");
+    data.saveAtlasToDisk();
+#endif
+
+    int x = kDrawnTileSize/2;
+    test_color(reporter, readBack, x, SK_ColorRED);
+    x += kDrawnTileSize;
+    test_color(reporter, readBack, x, SK_ColorGREEN);
+    x += kDrawnTileSize;
+    test_color(reporter, readBack, x, SK_ColorBLUE);
+    x += kDrawnTileSize;
+    test_color(reporter, readBack, x, SK_ColorCYAN);
+    x += kDrawnTileSize;
+    test_color(reporter, readBack, x, SK_ColorMAGENTA);
+    x += kDrawnTileSize;
+    test_color(reporter, readBack, x, SK_ColorYELLOW);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/OnceTest.cpp b/src/third_party/skia/tests/OnceTest.cpp
index 192abaa..ef8d3d9 100644
--- a/src/third_party/skia/tests/OnceTest.cpp
+++ b/src/third_party/skia/tests/OnceTest.cpp
@@ -16,49 +16,25 @@
 DEF_TEST(SkOnce_Singlethreaded, r) {
     int x = 0;
 
-    SK_DECLARE_STATIC_ONCE(once);
     // No matter how many times we do this, x will be 5.
-    SkOnce(&once, add_five, &x);
-    SkOnce(&once, add_five, &x);
-    SkOnce(&once, add_five, &x);
-    SkOnce(&once, add_five, &x);
-    SkOnce(&once, add_five, &x);
+    SkOnce once;
+    once(add_five, &x);
+    once(add_five, &x);
+    once(add_five, &x);
+    once(add_five, &x);
+    once(add_five, &x);
 
     REPORTER_ASSERT(r, 5 == x);
 }
 
-static void add_six(int* x) {
-    *x += 6;
-}
-
-class Racer : public SkRunnable {
-public:
-    SkOnceFlag* once;
-    int* ptr;
-
-    virtual void run() SK_OVERRIDE {
-        SkOnce(once, add_six, ptr);
-    }
-};
-
 DEF_TEST(SkOnce_Multithreaded, r) {
-    const int kTasks = 16;
-
-    // Make a bunch of tasks that will race to be the first to add six to x.
-    Racer racers[kTasks];
-    SK_DECLARE_STATIC_ONCE(once);
     int x = 0;
-    for (int i = 0; i < kTasks; i++) {
-        racers[i].once = &once;
-        racers[i].ptr = &x;
-    }
 
-    // Let them race.
-    SkTaskGroup tg;
-    for (int i = 0; i < kTasks; i++) {
-        tg.add(&racers[i]);
-    }
-    tg.wait();
+    // Run a bunch of tasks to be the first to add six to x.
+    SkOnce once;
+    SkTaskGroup().batch(1021, [&](int) {
+        once([&] { x += 6; });
+    });
 
     // Only one should have done the +=.
     REPORTER_ASSERT(r, 6 == x);
@@ -68,9 +44,9 @@
 static void inc_gX() { gX++; }
 
 DEF_TEST(SkOnce_NoArg, r) {
-    SK_DECLARE_STATIC_ONCE(once);
-    SkOnce(&once, inc_gX);
-    SkOnce(&once, inc_gX);
-    SkOnce(&once, inc_gX);
+    SkOnce once;
+    once(inc_gX);
+    once(inc_gX);
+    once(inc_gX);
     REPORTER_ASSERT(r, 1 == gX);
 }
diff --git a/src/third_party/skia/tests/OverAlignedTest.cpp b/src/third_party/skia/tests/OverAlignedTest.cpp
new file mode 100644
index 0000000..fcc3e16
--- /dev/null
+++ b/src/third_party/skia/tests/OverAlignedTest.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "SkRandom.h"
+
+// Clang seems to think only 32-bit alignment is guaranteed on 32-bit x86 Android.
+// See https://reviews.llvm.org/D8357
+// This is why we have disabled -Wover-aligned there (we allocate 8-byte aligned structs in Ganesh).
+DEF_TEST(OverAligned, r) {
+    SkRandom rand;
+    // Let's test that assertion.  We think it really should be providing 8-byte alignment.
+    for (int i = 0; i < 1000; i++) {
+        void* p = sk_malloc_throw(rand.nextRangeU(0,100));
+        REPORTER_ASSERT(r, SkIsAlign8((uintptr_t)p));
+        sk_free(p);
+    }
+}
diff --git a/src/third_party/skia/tests/PDFDeflateWStreamTest.cpp b/src/third_party/skia/tests/PDFDeflateWStreamTest.cpp
new file mode 100644
index 0000000..3d734c8
--- /dev/null
+++ b/src/third_party/skia/tests/PDFDeflateWStreamTest.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#ifdef SK_SUPPORT_PDF
+
+#include "SkDeflate.h"
+#include "SkRandom.h"
+
+namespace {
+
+#include "zlib.h"
+
+// Different zlib implementations use different T.
+// We've seen size_t and unsigned.
+template <typename T> void* skia_alloc_func(void*, T items, T size) {
+    return sk_calloc_throw(SkToSizeT(items) * SkToSizeT(size));
+}
+
+void skia_free_func(void*, void* address) { sk_free(address); }
+
+/**
+ *  Use the un-deflate compression algorithm to decompress the data in src,
+ *  returning the result.  Returns nullptr if an error occurs.
+ */
+std::unique_ptr<SkStreamAsset> stream_inflate(skiatest::Reporter* reporter, SkStream* src) {
+    SkDynamicMemoryWStream decompressedDynamicMemoryWStream;
+    SkWStream* dst = &decompressedDynamicMemoryWStream;
+
+    static const size_t kBufferSize = 1024;
+    uint8_t inputBuffer[kBufferSize];
+    uint8_t outputBuffer[kBufferSize];
+    z_stream flateData;
+    flateData.zalloc = &skia_alloc_func;
+    flateData.zfree = &skia_free_func;
+    flateData.opaque = nullptr;
+    flateData.next_in = nullptr;
+    flateData.avail_in = 0;
+    flateData.next_out = outputBuffer;
+    flateData.avail_out = kBufferSize;
+    int rc;
+    rc = inflateInit(&flateData);
+    if (rc != Z_OK) {
+        ERRORF(reporter, "Zlib: inflateInit failed");
+        return nullptr;
+    }
+    uint8_t* input = (uint8_t*)src->getMemoryBase();
+    size_t inputLength = src->getLength();
+    if (input == nullptr || inputLength == 0) {
+        input = nullptr;
+        flateData.next_in = inputBuffer;
+        flateData.avail_in = 0;
+    } else {
+        flateData.next_in = input;
+        flateData.avail_in = SkToUInt(inputLength);
+    }
+
+    rc = Z_OK;
+    while (true) {
+        if (flateData.avail_out < kBufferSize) {
+            if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) {
+                rc = Z_BUF_ERROR;
+                break;
+            }
+            flateData.next_out = outputBuffer;
+            flateData.avail_out = kBufferSize;
+        }
+        if (rc != Z_OK)
+            break;
+        if (flateData.avail_in == 0) {
+            if (input != nullptr)
+                break;
+            size_t read = src->read(&inputBuffer, kBufferSize);
+            if (read == 0)
+                break;
+            flateData.next_in = inputBuffer;
+            flateData.avail_in = SkToUInt(read);
+        }
+        rc = inflate(&flateData, Z_NO_FLUSH);
+    }
+    while (rc == Z_OK) {
+        rc = inflate(&flateData, Z_FINISH);
+        if (flateData.avail_out < kBufferSize) {
+            if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) {
+                ERRORF(reporter, "write failed");
+                return nullptr;
+            }
+            flateData.next_out = outputBuffer;
+            flateData.avail_out = kBufferSize;
+        }
+    }
+
+    inflateEnd(&flateData);
+    if (rc != Z_STREAM_END) {
+        ERRORF(reporter, "Zlib: inflateEnd failed");
+        return nullptr;
+    }
+    return decompressedDynamicMemoryWStream.detachAsStream();
+}
+}  // namespace
+
+DEF_TEST(SkPDF_DeflateWStream, r) {
+    SkRandom random(123456);
+    for (int i = 0; i < 50; ++i) {
+        uint32_t size = random.nextULessThan(10000);
+        SkAutoTMalloc<uint8_t> buffer(size);
+        for (uint32_t j = 0; j < size; ++j) {
+            buffer[j] = random.nextU() & 0xff;
+        }
+
+        SkDynamicMemoryWStream dynamicMemoryWStream;
+        {
+            SkDeflateWStream deflateWStream(&dynamicMemoryWStream);
+            uint32_t j = 0;
+            while (j < size) {
+                uint32_t writeSize =
+                        SkTMin(size - j, random.nextRangeU(1, 400));
+                if (!deflateWStream.write(&buffer[j], writeSize)) {
+                    ERRORF(r, "something went wrong.");
+                    return;
+                }
+                j += writeSize;
+            }
+            REPORTER_ASSERT(r, deflateWStream.bytesWritten() == size);
+        }
+        std::unique_ptr<SkStreamAsset> compressed(dynamicMemoryWStream.detachAsStream());
+        std::unique_ptr<SkStreamAsset> decompressed(stream_inflate(r, compressed.get()));
+
+        if (!decompressed) {
+            ERRORF(r, "Decompression failed.");
+            return;
+        }
+        if (decompressed->getLength() != size) {
+            ERRORF(r, "Decompression failed to get right size [%d]."
+                   " %u != %u", i,  (unsigned)(decompressed->getLength()),
+                   (unsigned)size);
+            SkString s = SkStringPrintf("/tmp/deftst_compressed_%d", i);
+            SkFILEWStream o(s.c_str());
+            o.writeStream(compressed.get(), compressed->getLength());
+            compressed->rewind();
+
+            s = SkStringPrintf("/tmp/deftst_input_%d", i);
+            SkFILEWStream o2(s.c_str());
+            o2.write(&buffer[0], size);
+
+            continue;
+        }
+        uint32_t minLength = SkTMin(size,
+                                    (uint32_t)(decompressed->getLength()));
+        for (uint32_t i = 0; i < minLength; ++i) {
+            uint8_t c;
+            SkDEBUGCODE(size_t rb =)decompressed->read(&c, sizeof(uint8_t));
+            SkASSERT(sizeof(uint8_t) == rb);
+            if (buffer[i] != c) {
+                ERRORF(r, "Decompression failed at byte %u.", (unsigned)i);
+                break;
+            }
+        }
+    }
+    SkDeflateWStream emptyDeflateWStream(nullptr);
+    REPORTER_ASSERT(r, !emptyDeflateWStream.writeText("FOO"));
+}
+
+#endif
diff --git a/src/third_party/skia/tests/PDFDocumentTest.cpp b/src/third_party/skia/tests/PDFDocumentTest.cpp
new file mode 100644
index 0000000..f01d0f9
--- /dev/null
+++ b/src/third_party/skia/tests/PDFDocumentTest.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Test.h"
+
+#include "Resources.h"
+#include "SkCanvas.h"
+#include "SkDocument.h"
+#include "SkOSFile.h"
+#include "SkOSPath.h"
+#include "SkStream.h"
+#include "SkPixelSerializer.h"
+
+#include "sk_tool_utils.h"
+
+static void test_empty(skiatest::Reporter* reporter) {
+    SkDynamicMemoryWStream stream;
+
+    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
+
+    doc->close();
+
+    REPORTER_ASSERT(reporter, stream.bytesWritten() == 0);
+}
+
+static void test_abort(skiatest::Reporter* reporter) {
+    SkDynamicMemoryWStream stream;
+    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
+
+    SkCanvas* canvas = doc->beginPage(100, 100);
+    canvas->drawColor(SK_ColorRED);
+    doc->endPage();
+
+    doc->abort();
+
+    // Test that only the header is written, not the full document.
+    REPORTER_ASSERT(reporter, stream.bytesWritten() < 256);
+}
+
+static void test_abortWithFile(skiatest::Reporter* reporter) {
+    SkString tmpDir = skiatest::GetTmpDir();
+
+    if (tmpDir.isEmpty()) {
+        return;  // TODO(edisonn): unfortunatelly this pattern is used in other
+                 // tests, but if GetTmpDir() starts returning and empty dir
+                 // allways, then all these tests will be disabled.
+    }
+
+    SkString path = SkOSPath::Join(tmpDir.c_str(), "aborted.pdf");
+
+    // Make sure doc's destructor is called to flush.
+    {
+        sk_sp<SkDocument> doc(SkDocument::MakePDF(path.c_str()));
+
+        SkCanvas* canvas = doc->beginPage(100, 100);
+        canvas->drawColor(SK_ColorRED);
+        doc->endPage();
+
+        doc->abort();
+    }
+
+    FILE* file = fopen(path.c_str(), "r");
+    // Test that only the header is written, not the full document.
+    char buffer[256];
+    REPORTER_ASSERT(reporter, fread(buffer, 1, sizeof(buffer), file) < sizeof(buffer));
+    fclose(file);
+}
+
+static void test_file(skiatest::Reporter* reporter) {
+    SkString tmpDir = skiatest::GetTmpDir();
+    if (tmpDir.isEmpty()) {
+        return;  // TODO(edisonn): unfortunatelly this pattern is used in other
+                 // tests, but if GetTmpDir() starts returning and empty dir
+                 // allways, then all these tests will be disabled.
+    }
+
+    SkString path = SkOSPath::Join(tmpDir.c_str(), "file.pdf");
+
+    sk_sp<SkDocument> doc(SkDocument::MakePDF(path.c_str()));
+
+    SkCanvas* canvas = doc->beginPage(100, 100);
+
+    canvas->drawColor(SK_ColorRED);
+    doc->endPage();
+    doc->close();
+
+    FILE* file = fopen(path.c_str(), "r");
+    REPORTER_ASSERT(reporter, file != nullptr);
+    char header[100];
+    REPORTER_ASSERT(reporter, fread(header, 4, 1, file) != 0);
+    REPORTER_ASSERT(reporter, strncmp(header, "%PDF", 4) == 0);
+    fclose(file);
+}
+
+static void test_close(skiatest::Reporter* reporter) {
+    SkDynamicMemoryWStream stream;
+    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
+
+    SkCanvas* canvas = doc->beginPage(100, 100);
+    canvas->drawColor(SK_ColorRED);
+    doc->endPage();
+
+    doc->close();
+
+    REPORTER_ASSERT(reporter, stream.bytesWritten() != 0);
+}
+
+DEF_TEST(SkPDF_document_tests, reporter) {
+    REQUIRE_PDF_DOCUMENT(document_tests, reporter);
+    test_empty(reporter);
+    test_abort(reporter);
+    test_abortWithFile(reporter);
+    test_file(reporter);
+    test_close(reporter);
+}
+
+namespace {
+class JPEGSerializer final : public SkPixelSerializer {
+    bool onUseEncodedData(const void*, size_t) override { return true; }
+    SkData* onEncode(const SkPixmap& pixmap) override {
+        return sk_tool_utils::EncodeImageToData(pixmap, SkEncodedImageFormat::kJPEG, 85).release();
+    }
+};
+}  // namespace
+
+size_t count_bytes(const SkBitmap& bm, bool useDCT) {
+    SkDynamicMemoryWStream stream;
+    sk_sp<SkDocument> doc;
+    if (useDCT) {
+        doc = SkDocument::MakePDF(&stream, SK_ScalarDefaultRasterDPI,
+                                  SkDocument::PDFMetadata(),
+                                  sk_make_sp<JPEGSerializer>(), false);
+    } else {
+        doc = SkDocument::MakePDF(&stream);
+    }
+    SkCanvas* canvas = doc->beginPage(64, 64);
+    canvas->drawBitmap(bm, 0, 0);
+    doc->endPage();
+    doc->close();
+    return stream.bytesWritten();
+}
+
+DEF_TEST(SkPDF_document_dct_encoder, r) {
+    REQUIRE_PDF_DOCUMENT(SkPDF_document_dct_encoder, r);
+    SkBitmap bm;
+    if (GetResourceAsBitmap("mandrill_64.png", &bm)) {
+        // Lossy encoding works better on photographs.
+        REPORTER_ASSERT(r, count_bytes(bm, true) < count_bytes(bm, false));
+    }
+}
+
+DEF_TEST(SkPDF_document_skbug_4734, r) {
+    REQUIRE_PDF_DOCUMENT(SkPDF_document_skbug_4734, r);
+    SkDynamicMemoryWStream stream;
+    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
+    SkCanvas* canvas = doc->beginPage(64, 64);
+    canvas->scale(10000.0f, 10000.0f);
+    canvas->translate(20.0f, 10.0f);
+    canvas->rotate(30.0f);
+    const char text[] = "HELLO";
+    canvas->drawString(text, 0, 0, SkPaint());
+}
+
+static bool contains(const uint8_t* result, size_t size, const char expectation[]) {
+    size_t len = strlen(expectation);
+    size_t N = 1 + size - len;
+    for (size_t i = 0; i < N; ++i) {
+        if (0 == memcmp(result + i, expectation, len)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+// verify that the PDFA flag does something.
+DEF_TEST(SkPDF_pdfa_document, r) {
+    REQUIRE_PDF_DOCUMENT(SkPDF_pdfa_document, r);
+
+    SkDocument::PDFMetadata pdfMetadata;
+    pdfMetadata.fTitle = "test document";
+    pdfMetadata.fCreation.fEnabled = true;
+    pdfMetadata.fCreation.fDateTime = {0, 1999, 12, 5, 31, 23, 59, 59};
+
+    SkDynamicMemoryWStream buffer;
+    auto doc = SkDocument::MakePDF(&buffer, SK_ScalarDefaultRasterDPI,
+                                   pdfMetadata, nullptr, /* pdfa = */ true);
+    doc->beginPage(64, 64)->drawColor(SK_ColorRED);
+    doc->close();
+    sk_sp<SkData> data(buffer.detachAsData());
+
+    static const char* expectations[] = {
+        "sRGB IEC61966-2.1",
+        "<dc:title><rdf:Alt><rdf:li xml:lang=\"x-default\">test document",
+        "<xmp:CreateDate>1999-12-31T23:59:59+00:00</xmp:CreateDate>",
+        "/Subtype /XML",
+        "/CreationDate (D:19991231235959+00'00')>>",
+    };
+    for (const char* expectation : expectations) {
+        if (!contains(data->bytes(), data->size(), expectation)) {
+            ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
+        }
+    }
+    pdfMetadata.fProducer = "phoney library";
+    doc = SkDocument::MakePDF(&buffer, SK_ScalarDefaultRasterDPI,
+                              pdfMetadata, nullptr, /* pdfa = */ true);
+    doc->beginPage(64, 64)->drawColor(SK_ColorRED);
+    doc->close();
+    data = buffer.detachAsData();
+
+    static const char* moreExpectations[] = {
+        "/Producer (phoney library)",
+        "/ProductionLibrary (Skia/PDF m",
+        "<!-- <skia:ProductionLibrary>Skia/PDF m",
+        "<pdf:Producer>phoney library</pdf:Producer>",
+    };
+    for (const char* expectation : moreExpectations) {
+        if (!contains(data->bytes(), data->size(), expectation)) {
+            ERRORF(r, "PDFA expectation missing: '%s'.", expectation);
+        }
+    }
+}
diff --git a/src/third_party/skia/tests/ToUnicodeTest.cpp b/src/third_party/skia/tests/PDFGlyphsToUnicodeTest.cpp
similarity index 70%
rename from src/third_party/skia/tests/ToUnicodeTest.cpp
rename to src/third_party/skia/tests/PDFGlyphsToUnicodeTest.cpp
index 919ba11..332520b 100644
--- a/src/third_party/skia/tests/ToUnicodeTest.cpp
+++ b/src/third_party/skia/tests/PDFGlyphsToUnicodeTest.cpp
@@ -5,35 +5,38 @@
  * found in the LICENSE file.
  */
 
-#include "SkData.h"
-#include "SkPDFFont.h"
-#include "SkPDFTypes.h"
-#include "SkStream.h"
 #include "Test.h"
 
+#ifdef SK_SUPPORT_PDF
+
+#include "SkBitSet.h"
+#include "SkData.h"
+#include "SkPDFMakeToUnicodeCmap.h"
+#include "SkStream.h"
+
+static const int kMaximumGlyphCount = SK_MaxU16 + 1;
+
 static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
                           const char* buffer, size_t len) {
-    SkAutoDataUnref data(stream.copyToData());
-    if (offset + len > data->size()) {
-        return false;
-    }
     if (len != strlen(buffer)) {
         return false;
     }
-    return memcmp(data->bytes() + offset, buffer, len) == 0;
+
+    const size_t streamSize = stream.bytesWritten();
+
+    if (offset + len > streamSize) {
+        return false;
+    }
+
+    SkAutoTMalloc<char> data(streamSize);
+    stream.copyTo(data.get());
+    return memcmp(data.get() + offset, buffer, len) == 0;
 }
 
-void append_cmap_sections(const SkTDArray<SkUnichar>& glyphToUnicode,
-                          const SkPDFGlyphSet* subset,
-                          SkDynamicMemoryWStream* cmap,
-                          bool multiByteGlyphs,
-                          uint16_t firstGlypthID,
-                          uint16_t lastGlypthID);
-
-DEF_TEST(ToUnicode, reporter) {
+DEF_TEST(SkPDF_ToUnicode, reporter) {
     SkTDArray<SkUnichar> glyphToUnicode;
     SkTDArray<uint16_t> glyphsInSubset;
-    SkPDFGlyphSet subset;
+    SkBitSet subset(kMaximumGlyphCount);
 
     glyphToUnicode.push(0);  // 0
     glyphToUnicode.push(0);  // 1
@@ -72,8 +75,8 @@
     glyphToUnicode.push(0x1013);
 
     SkDynamicMemoryWStream buffer;
-    subset.set(glyphsInSubset.begin(), glyphsInSubset.count());
-    append_cmap_sections(glyphToUnicode, &subset, &buffer, true, 0, 0xFFFF);
+    subset.setAll(glyphsInSubset.begin(), glyphsInSubset.count());
+    SkPDFAppendCmapSections(glyphToUnicode, &subset, &buffer, true, 0, 0xFFFF);
 
     char expectedResult[] =
 "4 beginbfchar\n\
@@ -90,12 +93,12 @@
 endbfrange\n";
 
     REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
-                                            buffer.getOffset()));
+                                            buffer.bytesWritten()));
 
     // Remove characters and ranges.
     buffer.reset();
 
-    append_cmap_sections(glyphToUnicode, &subset, &buffer, true, 8, 0x00FF);
+    SkPDFAppendCmapSections(glyphToUnicode, &subset, &buffer, true, 8, 0x00FF);
 
     char expectedResultChop1[] =
 "2 beginbfchar\n\
@@ -108,12 +111,12 @@
 endbfrange\n";
 
     REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResultChop1,
-                                            buffer.getOffset()));
+                                            buffer.bytesWritten()));
 
     // Remove characters from range to downdrade it to one char.
     buffer.reset();
 
-    append_cmap_sections(glyphToUnicode, &subset, &buffer, true, 0x00D, 0x00FE);
+    SkPDFAppendCmapSections(glyphToUnicode, &subset, &buffer, true, 0x00D, 0x00FE);
 
     char expectedResultChop2[] =
 "2 beginbfchar\n\
@@ -122,34 +125,34 @@
 endbfchar\n";
 
     REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResultChop2,
-                                            buffer.getOffset()));
+                                            buffer.bytesWritten()));
 
     buffer.reset();
 
-    append_cmap_sections(glyphToUnicode, NULL, &buffer, false, 0xFC, 0x110);
+    SkPDFAppendCmapSections(glyphToUnicode, nullptr, &buffer, false, 0xFC, 0x110);
 
     char expectedResultSingleBytes[] =
 "2 beginbfchar\n\
-<0001> <0000>\n\
-<0002> <0000>\n\
+<01> <0000>\n\
+<02> <0000>\n\
 endbfchar\n\
 1 beginbfrange\n\
-<0003> <0006> <1010>\n\
+<03> <06> <1010>\n\
 endbfrange\n";
 
     REPORTER_ASSERT(reporter, stream_equals(buffer, 0,
                                             expectedResultSingleBytes,
-                                            buffer.getOffset()));
+                                            buffer.bytesWritten()));
 
     glyphToUnicode.reset();
     glyphsInSubset.reset();
-    SkPDFGlyphSet subset2;
+    SkBitSet subset2(kMaximumGlyphCount);
 
     // Test mapping:
     //           I  n  s  t  a  l
     // Glyph id 2c 51 56 57 44 4f
     // Unicode  49 6e 73 74 61 6c
-    for (size_t i = 0; i < 100; ++i) {
+    for (SkUnichar i = 0; i < 100; ++i) {
       glyphToUnicode.push(i + 29);
     }
 
@@ -161,8 +164,8 @@
     glyphsInSubset.push(0x57);
 
     SkDynamicMemoryWStream buffer2;
-    subset2.set(glyphsInSubset.begin(), glyphsInSubset.count());
-    append_cmap_sections(glyphToUnicode, &subset2, &buffer2, true, 0, 0xffff);
+    subset2.setAll(glyphsInSubset.begin(), glyphsInSubset.count());
+    SkPDFAppendCmapSections(glyphToUnicode, &subset2, &buffer2, true, 0, 0xffff);
 
     char expectedResult2[] =
 "4 beginbfchar\n\
@@ -176,5 +179,7 @@
 endbfrange\n";
 
     REPORTER_ASSERT(reporter, stream_equals(buffer2, 0, expectedResult2,
-                                            buffer2.getOffset()));
+                                            buffer2.bytesWritten()));
 }
+
+#endif
diff --git a/src/third_party/skia/tests/PDFJpegEmbedTest.cpp b/src/third_party/skia/tests/PDFJpegEmbedTest.cpp
index c1d0ea8..100c9b3 100644
--- a/src/third_party/skia/tests/PDFJpegEmbedTest.cpp
+++ b/src/third_party/skia/tests/PDFJpegEmbedTest.cpp
@@ -5,26 +5,15 @@
  * found in the LICENSE file.
  */
 
-#include "SkDocument.h"
 #include "SkCanvas.h"
-#include "SkImageGenerator.h"
 #include "SkData.h"
+#include "SkDocument.h"
+#include "SkImageGenerator.h"
 #include "SkStream.h"
-#include "SkDecodingImageGenerator.h"
 
 #include "Resources.h"
 #include "Test.h"
 
-// Returned bitmap is lazy.  Only lazy bitmaps hold onto the original data.
-static SkBitmap bitmap_from_data(SkData* data) {
-    SkASSERT(data);
-    SkBitmap bm;
-    SkInstallDiscardablePixelRef(
-            SkDecodingImageGenerator::Create(
-                    data, SkDecodingImageGenerator::Options()), &bm);
-    return bm;
-}
-
 static bool is_subset_of(SkData* smaller, SkData* larger) {
     SkASSERT(smaller && larger);
     if (smaller->size() > larger->size()) {
@@ -41,15 +30,15 @@
 }
 
 
-static SkData* load_resource(
+static sk_sp<SkData> load_resource(
         skiatest::Reporter* r, const char* test, const char* filename) {
     SkString path(GetResourcePath(filename));
-    SkData* data = SkData::NewFromFileName(path.c_str());
-    if (!data && r->verbose()) {
-        SkDebugf("\n%s: Resource '%s' can not be found.\n",
-                 test, filename);
+    sk_sp<SkData> data(SkData::MakeFromFileName(path.c_str()));
+    if (!data) {
+        INFOF(r, "\n%s: Resource '%s' can not be found.\n",
+              test, filename);
     }
-    return data;  // May return NULL.
+    return data;  // May return nullptr.
 }
 
 /**
@@ -57,45 +46,169 @@
  *  directly embedded into the PDF (without re-encoding) when that
  *  makes sense.
  */
-DEF_TEST(PDFJpegEmbedTest, r) {
-    const char test[] = "PDFJpegEmbedTest";
-    SkAutoTUnref<SkData> mandrillData(
-            load_resource(r, test, "mandrill_512_q075.jpg"));
-    SkAutoTUnref<SkData> cmykData(load_resource(r, test, "CMYK.jpg"));
+DEF_TEST(SkPDF_JpegEmbedTest, r) {
+    REQUIRE_PDF_DOCUMENT(SkPDF_JpegEmbedTest, r);
+    const char test[] = "SkPDF_JpegEmbedTest";
+    sk_sp<SkData> mandrillData(load_resource(r, test, "mandrill_512_q075.jpg"));
+    sk_sp<SkData> cmykData(load_resource(r, test, "CMYK.jpg"));
     if (!mandrillData || !cmykData) {
         return;
     }
-
+    ////////////////////////////////////////////////////////////////////////////
     SkDynamicMemoryWStream pdf;
-    SkAutoTUnref<SkDocument> document(SkDocument::CreatePDF(&pdf));
+    sk_sp<SkDocument> document(SkDocument::MakePDF(&pdf));
     SkCanvas* canvas = document->beginPage(642, 1028);
 
     canvas->clear(SK_ColorLTGRAY);
 
-    SkBitmap bm1(bitmap_from_data(mandrillData));
-    canvas->drawBitmap(bm1, 65.0, 0.0, NULL);
-    SkBitmap bm2(bitmap_from_data(cmykData));
-    canvas->drawBitmap(bm2, 0.0, 512.0, NULL);
+    sk_sp<SkImage> im1(SkImage::MakeFromEncoded(mandrillData));
+    canvas->drawImage(im1.get(), 65.0, 0.0, nullptr);
+    sk_sp<SkImage> im2(SkImage::MakeFromEncoded(cmykData));
+    canvas->drawImage(im2.get(), 0.0, 512.0, nullptr);
 
     canvas->flush();
     document->endPage();
     document->close();
-    SkAutoTUnref<SkData> pdfData(pdf.copyToData());
+    sk_sp<SkData> pdfData = pdf.detachAsData();
     SkASSERT(pdfData);
-    pdf.reset();
 
-    REPORTER_ASSERT(r, is_subset_of(mandrillData, pdfData));
+    REPORTER_ASSERT(r, is_subset_of(mandrillData.get(), pdfData.get()));
 
     // This JPEG uses a nonstandard colorspace - it can not be
     // embedded into the PDF directly.
-    REPORTER_ASSERT(r, !is_subset_of(cmykData, pdfData));
+    REPORTER_ASSERT(r, !is_subset_of(cmykData.get(), pdfData.get()));
+}
 
-    // The following is for debugging purposes only.
-    const char* outputPath = getenv("SKIA_TESTS_PDF_JPEG_EMBED_OUTPUT_PATH");
-    if (outputPath) {
-        SkFILEWStream output(outputPath);
-        if (output.isValid()) {
-            output.write(pdfData->data(), pdfData->size());
+#ifdef SK_SUPPORT_PDF
+
+#include "SkJpegInfo.h"
+
+DEF_TEST(SkPDF_JpegIdentification, r) {
+    static struct {
+        const char* path;
+        bool isJfif;
+        SkJFIFInfo::Type type;
+    } kTests[] = {{"CMYK.jpg", false, SkJFIFInfo::kGrayscale},
+                  {"color_wheel.jpg", true, SkJFIFInfo::kYCbCr},
+                  {"grayscale.jpg", true, SkJFIFInfo::kGrayscale},
+                  {"mandrill_512_q075.jpg", true, SkJFIFInfo::kYCbCr},
+                  {"randPixels.jpg", true, SkJFIFInfo::kYCbCr}};
+    for (size_t i = 0; i < SK_ARRAY_COUNT(kTests); ++i) {
+        sk_sp<SkData> data(load_resource(r, "JpegIdentification", kTests[i].path));
+        if (!data) {
+            continue;
         }
+        SkJFIFInfo info;
+        bool isJfif = SkIsJFIF(data.get(), &info);
+        if (isJfif != kTests[i].isJfif) {
+            ERRORF(r, "%s failed isJfif test", kTests[i].path);
+            continue;
+        }
+        if (!isJfif) {
+            continue;  // not applicable
+        }
+        if (kTests[i].type != info.fType) {
+            ERRORF(r, "%s failed jfif type test", kTests[i].path);
+            continue;
+        }
+        INFOF(r, "\nJpegIdentification: %s [%d x %d]\n", kTests[i].path,
+              info.fSize.width(), info.fSize.height());
+    }
+
+    // Test several malformed jpegs.
+    SkJFIFInfo info;
+    {
+        static const char goodJpeg[] =
+            "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7"
+            "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37"
+            "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t"
+            "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222"
+            "22222222222222\377\300\0\21\10\2\0\2\0\3\1\"\0\2\21\1\3\21\001";
+        size_t goodJpegLength = 177;
+        auto data = SkData::MakeWithoutCopy(goodJpeg, goodJpegLength);
+        REPORTER_ASSERT(r, SkIsJFIF(data.get(), &info));
+        REPORTER_ASSERT(r, info.fSize == SkISize::Make(512, 512));
+        REPORTER_ASSERT(r, info.fType == SkJFIFInfo::kYCbCr);
+
+        // Not long enough to read first (SOI) segment marker.
+        data = SkData::MakeWithoutCopy(goodJpeg, 1);
+        REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
+
+        // Not long enough to read second segment (APP0) marker.
+        data = SkData::MakeWithoutCopy(goodJpeg, 3);
+        REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
+
+        // Not long enough to read second segment's length.
+        data = SkData::MakeWithoutCopy(goodJpeg, 5);
+        REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
+
+        // APP0 segment is truncated.
+        data = SkData::MakeWithoutCopy(goodJpeg, 7);
+        REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
+
+        // Missing SOF segment.
+        data = SkData::MakeWithoutCopy(goodJpeg, 89);
+        REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
+    }
+    {
+        // JFIF tag missing.
+        static const char jpeg[] =
+            "\377\330\377\340\0\20JFIX\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7"
+            "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37"
+            "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t"
+            "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222"
+            "22222222222222\377\300\0\21\10\2\0\2\0\3\1\"\0\2\21\1\3\21\001";
+        size_t jpegLength = 177;
+        auto data = SkData::MakeWithoutCopy(jpeg, jpegLength);
+        REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
+    }
+    {
+        // APP0 segment short (byte 6 changed).
+        static const char jpeg[] =
+            "\377\330\377\340\0\5JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7"
+            "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37"
+            "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t"
+            "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222"
+            "22222222222222\377\300\0\21\10\2\0\2\0\3\1\"\0\2\21\1\3\21\001";
+        size_t jpegLength = 177;
+        auto data = SkData::MakeWithoutCopy(jpeg, jpegLength);
+        REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
+    }
+    {
+        // SOF segment short. ('\21' replaced with '\5')
+        static const char jpeg[] =
+            "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7"
+            "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37"
+            "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t"
+            "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222"
+            "22222222222222\377\300\0\5\10\2\0\2\0\3\1\"\0\2\21\1\3\21\001";
+        size_t jpegLength = 177;
+        auto data = SkData::MakeWithoutCopy(jpeg, jpegLength);
+        REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
+    }
+    {
+        // Unsupported 12-bit components. ('\10' replaced with '\14')
+        static const char jpeg[] =
+            "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7"
+            "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37"
+            "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t"
+            "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222"
+            "22222222222222\377\300\0\21\14\2\0\2\0\3\1\"\0\2\21\1\3\21\001";
+        size_t jpegLength = 177;
+        auto data = SkData::MakeWithoutCopy(jpeg, jpegLength);
+        REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
+    }
+    {
+        // Two color channels.  ('\3' replaced with '\2')
+        static const char jpeg[] =
+            "\377\330\377\340\0\20JFIF\0\1\1\0\0\1\0\1\0\0\377\333\0C\0\10\6\6\7"
+            "\6\5\10\7\7\7\t\t\10\n\14\24\r\14\13\13\14\31\22\23\17\24\35\32\37"
+            "\36\35\32\34\34 $.' \",#\34\34(7),01444\37'9=82<.342\377\333\0C\1\t"
+            "\t\t\14\13\14\30\r\r\0302!\34!222222222222222222222222222222222222"
+            "22222222222222\377\300\0\21\10\2\0\2\0\2\1\"\0\2\21\1\3\21\001";
+        size_t jpegLength = 177;
+        auto data = SkData::MakeWithoutCopy(jpeg, jpegLength);
+        REPORTER_ASSERT(r, !SkIsJFIF(data.get(), &info));
     }
 }
+#endif
diff --git a/src/third_party/skia/tests/PDFMetadataAttributeTest.cpp b/src/third_party/skia/tests/PDFMetadataAttributeTest.cpp
new file mode 100644
index 0000000..01351fa
--- /dev/null
+++ b/src/third_party/skia/tests/PDFMetadataAttributeTest.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkDocument.h"
+#include "SkStream.h"
+#include "SkData.h"
+#include "Test.h"
+
+DEF_TEST(SkPDF_Metadata, r) {
+    REQUIRE_PDF_DOCUMENT(SkPDF_Metadata, r);
+    SkTime::DateTime now;
+    SkTime::GetDateTime(&now);
+    SkDocument::PDFMetadata metadata;
+    metadata.fTitle = "A1";
+    metadata.fAuthor = "A2";
+    metadata.fSubject = "A3";
+    metadata.fKeywords = "A4";
+    metadata.fCreator = "A5";
+    metadata.fCreation.fEnabled = true;
+    metadata.fCreation.fDateTime = now;
+    metadata.fModified.fEnabled = true;
+    metadata.fModified.fDateTime = now;
+
+    SkDynamicMemoryWStream pdf;
+    sk_sp<SkDocument> doc = SkDocument::MakePDF(&pdf, SK_ScalarDefaultRasterDPI,
+                                                metadata, nullptr, false);
+    doc->beginPage(612.0f, 792.0f);
+    doc->close();
+    sk_sp<SkData> data = pdf.detachAsData();
+    static const char* expectations[] = {
+        "/Title (A1)",
+        "/Author (A2)",
+        "/Subject (A3)",
+        "/Keywords (A4)",
+        "/Creator (A5)",
+        "/Producer (Skia/PDF ",
+        "/CreationDate (D:",
+        "/ModDate (D:"
+    };
+    const uint8_t* bytes = data->bytes();
+    for (const char* expectation : expectations) {
+        size_t len = strlen(expectation);
+        bool found = false;
+        size_t N = 1 + data->size() - len;
+        for (size_t i = 0; i < N; ++i) {
+            if (0 == memcmp(bytes + i, expectation, len)) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            ERRORF(r, "expectation missing: '%s'.", expectation);
+        }
+    }
+}
diff --git a/src/third_party/skia/tests/PDFOpaqueSrcModeToSrcOverTest.cpp b/src/third_party/skia/tests/PDFOpaqueSrcModeToSrcOverTest.cpp
new file mode 100644
index 0000000..8b3cfe0
--- /dev/null
+++ b/src/third_party/skia/tests/PDFOpaqueSrcModeToSrcOverTest.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkCanvas.h"
+#include "SkDocument.h"
+#include "SkStream.h"
+#include "Test.h"
+
+static void run_test(SkWStream* out, SkBlendMode mode, U8CPU alpha) {
+    sk_sp<SkDocument> pdfDoc(SkDocument::MakePDF(out));
+    SkCanvas* c = pdfDoc->beginPage(612.0f, 792.0f);
+    SkPaint black;
+    SkPaint background;
+    background.setColor(SK_ColorWHITE);
+    background.setAlpha(alpha);
+    background.setBlendMode(mode);
+    c->drawRect(SkRect::MakeWH(612.0f, 792.0f), background);
+    c->drawRect(SkRect::MakeXYWH(36.0f, 36.0f, 9.0f, 9.0f), black);
+    c->drawRect(SkRect::MakeXYWH(72.0f, 72.0f, 468.0f, 648.0f), background);
+    c->drawRect(SkRect::MakeXYWH(108.0f, 108.0f, 9.0f, 9.0f), black);
+    pdfDoc->close();
+}
+
+// http://crbug.com/473572
+DEF_TEST(SkPDF_OpaqueSrcModeToSrcOver, r) {
+    REQUIRE_PDF_DOCUMENT(SkPDF_OpaqueSrcModeToSrcOver, r);
+    SkDynamicMemoryWStream srcMode;
+    SkDynamicMemoryWStream srcOverMode;
+
+    U8CPU alpha = SK_AlphaOPAQUE;
+    run_test(&srcMode, SkBlendMode::kSrc, alpha);
+    run_test(&srcOverMode, SkBlendMode::kSrcOver, alpha);
+    REPORTER_ASSERT(r, srcMode.bytesWritten() == srcOverMode.bytesWritten());
+    // The two PDFs should be equal because they have an opaque alpha.
+
+    srcMode.reset();
+    srcOverMode.reset();
+
+    alpha = 0x80;
+    run_test(&srcMode, SkBlendMode::kSrc, alpha);
+    run_test(&srcOverMode, SkBlendMode::kSrcOver, alpha);
+    REPORTER_ASSERT(r, srcMode.bytesWritten() > srcOverMode.bytesWritten());
+    // The two PDFs should not be equal because they have a non-opaque alpha.
+}
diff --git a/src/third_party/skia/tests/PDFPrimitivesTest.cpp b/src/third_party/skia/tests/PDFPrimitivesTest.cpp
index 05677cd..766bbef 100644
--- a/src/third_party/skia/tests/PDFPrimitivesTest.cpp
+++ b/src/third_party/skia/tests/PDFPrimitivesTest.cpp
@@ -5,322 +5,154 @@
  * found in the LICENSE file.
  */
 
+#include "Test.h"
+
+#ifdef SK_SUPPORT_PDF
+
+#include "Resources.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkData.h"
-#include "SkFlate.h"
+#include "SkDocument.h"
+#include "SkDeflate.h"
 #include "SkImageEncoder.h"
+#include "SkMakeUnique.h"
 #include "SkMatrix.h"
-#include "SkPDFCatalog.h"
+#include "SkPDFCanon.h"
 #include "SkPDFDevice.h"
-#include "SkPDFStream.h"
+#include "SkPDFFont.h"
 #include "SkPDFTypes.h"
+#include "SkPDFUtils.h"
 #include "SkReadBuffer.h"
 #include "SkScalar.h"
+#include "SkSpecialImage.h"
 #include "SkStream.h"
 #include "SkTypes.h"
-#include "Test.h"
+#include "sk_tool_utils.h"
 
-class SkPDFTestDict : public SkPDFDict {
-public:
-  virtual void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
-                            SkTSet<SkPDFObject*>* newResourceObjects) {
-        for (int i = 0; i < fResources.count(); i++) {
-            newResourceObjects->add(fResources[i]);
-            fResources[i]->ref();
-        }
-    }
-
-    void addResource(SkPDFObject* object) {
-        fResources.append(1, &object);
-    }
-
-private:
-    SkTDArray<SkPDFObject*> fResources;
-};
+#include <cstdlib>
+#include <cmath>
 
 #define DUMMY_TEXT "DCT compessed stream."
 
-static SkData* encode_to_dct_data(size_t* pixelRefOffset, const SkBitmap& bitmap) {
-    *pixelRefOffset = 0;
-    return SkData::NewWithProc(DUMMY_TEXT, sizeof(DUMMY_TEXT) - 1, NULL, NULL);
-}
-
-static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
-                          const void* buffer, size_t len) {
-    SkAutoDataUnref data(stream.copyToData());
-    if (offset + len > data->size()) {
-        return false;
-    }
-    return memcmp(data->bytes() + offset, buffer, len) == 0;
-}
-
-static bool stream_contains(const SkDynamicMemoryWStream& stream,
-                            const char* buffer) {
-    SkAutoDataUnref data(stream.copyToData());
-    int len = strlen(buffer);  // our buffer does not have EOSs.
-
-    for (int offset = 0 ; offset < (int)data->size() - len; offset++) {
-        if (memcmp(data->bytes() + offset, buffer, len) == 0) {
-            return true;
-        }
-    }
-
-    return false;
-}
-
-static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj,
-                              const char* expectedData, size_t expectedSize,
-                              bool indirect, bool compression) {
-    SkPDFDocument::Flags docFlags = (SkPDFDocument::Flags) 0;
-    if (!compression) {
-        docFlags = SkTBitOr(docFlags, SkPDFDocument::kFavorSpeedOverSize_Flags);
-    }
-    SkPDFCatalog catalog(docFlags);
-    size_t directSize = obj->getOutputSize(&catalog, false);
-    REPORTER_ASSERT(reporter, directSize == expectedSize);
-
+template <typename T>
+static SkString emit_to_string(T& obj, SkPDFObjNumMap* catPtr = nullptr) {
+    SkPDFObjNumMap catalog;
     SkDynamicMemoryWStream buffer;
-    obj->emit(&buffer, &catalog, false);
-    REPORTER_ASSERT(reporter, directSize == buffer.getOffset());
-    REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData,
-                                            directSize));
+    if (!catPtr) {
+        catPtr = &catalog;
+    }
+    obj.emitObject(&buffer, *catPtr);
+    SkString tmp(buffer.bytesWritten());
+    buffer.copyTo(tmp.writable_str());
+    return tmp;
+}
 
-    if (indirect) {
-        // Indirect output.
-        static char header[] = "1 0 obj\n";
-        static size_t headerLen = strlen(header);
-        static char footer[] = "\nendobj\n";
-        static size_t footerLen = strlen(footer);
+static bool eq(const SkString& str, const char* strPtr, size_t len) {
+    return len == str.size() && 0 == memcmp(str.c_str(), strPtr, len);
+}
 
-        catalog.addObject(obj, false);
-
-        size_t indirectSize = obj->getOutputSize(&catalog, true);
-        REPORTER_ASSERT(reporter,
-                        indirectSize == directSize + headerLen + footerLen);
-
-        buffer.reset();
-        obj->emit(&buffer, &catalog, true);
-        REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset());
-        REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen));
-        REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData,
-                                                directSize));
-        REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize,
-                                                footer, footerLen));
+static void assert_eql(skiatest::Reporter* reporter,
+                       const SkString& skString,
+                       const char* str,
+                       size_t len) {
+    if (!eq(skString, str, len)) {
+        REPORT_FAILURE(reporter, "", SkStringPrintf(
+                "'%*s' != '%s'", len, str, skString.c_str()));
     }
 }
 
-static void SimpleCheckObjectOutput(skiatest::Reporter* reporter,
-                                    SkPDFObject* obj,
-                                    const char* expectedResult) {
-    CheckObjectOutput(reporter, obj, expectedResult,
-                      strlen(expectedResult), true, false);
+static void assert_eq(skiatest::Reporter* reporter,
+                      const SkString& skString,
+                      const char* str) {
+    assert_eql(reporter, skString, str, strlen(str));
+}
+
+
+template <typename T>
+static void assert_emit_eq(skiatest::Reporter* reporter,
+                           T& object,
+                           const char* string) {
+    SkString result = emit_to_string(object);
+    assert_eq(reporter, result, string);
 }
 
 static void TestPDFStream(skiatest::Reporter* reporter) {
     char streamBytes[] = "Test\nFoo\tBar";
-    SkAutoTUnref<SkMemoryStream> streamData(new SkMemoryStream(
-        streamBytes, strlen(streamBytes), true));
-    SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData.get()));
-    SimpleCheckObjectOutput(
-        reporter, stream.get(),
-        "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream");
-    stream->insert("Attribute", new SkPDFInt(42))->unref();
-    SimpleCheckObjectOutput(reporter, stream.get(),
-                            "<</Length 12\n/Attribute 42\n>> stream\n"
-                                "Test\nFoo\tBar\nendstream");
+    auto streamData = skstd::make_unique<SkMemoryStream>(
+            streamBytes, strlen(streamBytes), true);
+    auto stream = sk_make_sp<SkPDFStream>(std::move(streamData));
+    assert_emit_eq(reporter,
+                   *stream,
+                   "<</Length 12>> stream\nTest\nFoo\tBar\nendstream");
+    stream->dict()->insertInt("Attribute", 42);
+    assert_emit_eq(reporter,
+                   *stream,
+                   "<</Length 12\n/Attribute 42>> stream\n"
+                   "Test\nFoo\tBar\nendstream");
 
-    if (SkFlate::HaveFlate()) {
+    {
         char streamBytes2[] = "This is a longer string, so that compression "
                               "can do something with it. With shorter strings, "
                               "the short circuit logic cuts in and we end up "
                               "with an uncompressed string.";
-        SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2,
-                                                        strlen(streamBytes2)));
-        SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData2.get()));
+        auto stream = sk_make_sp<SkPDFStream>(
+                SkData::MakeWithCopy(streamBytes2, strlen(streamBytes2)));
 
         SkDynamicMemoryWStream compressedByteStream;
-        SkFlate::Deflate(streamData2.get(), &compressedByteStream);
-        SkAutoDataUnref compressedData(compressedByteStream.copyToData());
+        SkDeflateWStream deflateWStream(&compressedByteStream);
+        deflateWStream.write(streamBytes2, strlen(streamBytes2));
+        deflateWStream.finalize();
 
-        // Check first without compression.
-        SkDynamicMemoryWStream expectedResult1;
-        expectedResult1.writeText("<</Length 167\n>> stream\n");
-        expectedResult1.writeText(streamBytes2);
-        expectedResult1.writeText("\nendstream");
-        SkAutoDataUnref expectedResultData1(expectedResult1.copyToData());
-        CheckObjectOutput(reporter, stream.get(),
-                          (const char*) expectedResultData1->data(),
-                          expectedResultData1->size(), true, false);
-
-        // Then again with compression.
-        SkDynamicMemoryWStream expectedResult2;
-        expectedResult2.writeText("<</Filter /FlateDecode\n/Length 116\n"
-                                 ">> stream\n");
-        expectedResult2.write(compressedData->data(), compressedData->size());
-        expectedResult2.writeText("\nendstream");
-        SkAutoDataUnref expectedResultData2(expectedResult2.copyToData());
-        CheckObjectOutput(reporter, stream.get(),
-                          (const char*) expectedResultData2->data(),
-                          expectedResultData2->size(), true, true);
+        SkDynamicMemoryWStream expected;
+        expected.writeText("<</Filter /FlateDecode\n/Length 116>> stream\n");
+        compressedByteStream.writeToStream(&expected);
+        compressedByteStream.reset();
+        expected.writeText("\nendstream");
+        sk_sp<SkData> expectedResultData2(expected.detachAsData());
+        SkString result = emit_to_string(*stream);
+        #ifndef SK_PDF_LESS_COMPRESSION
+        assert_eql(reporter,
+                   result,
+                   (const char*)expectedResultData2->data(),
+                   expectedResultData2->size());
+        #endif
     }
 }
 
-static void TestCatalog(skiatest::Reporter* reporter) {
-    SkPDFCatalog catalog((SkPDFDocument::Flags)0);
-    SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
-    SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
-    SkAutoTUnref<SkPDFInt> int3(new SkPDFInt(3));
-    int1.get()->ref();
-    SkAutoTUnref<SkPDFInt> int1Again(int1.get());
+static void TestObjectNumberMap(skiatest::Reporter* reporter) {
+    SkPDFObjNumMap objNumMap;
+    sk_sp<SkPDFArray> a1(new SkPDFArray);
+    sk_sp<SkPDFArray> a2(new SkPDFArray);
+    sk_sp<SkPDFArray> a3(new SkPDFArray);
 
-    catalog.addObject(int1.get(), false);
-    catalog.addObject(int2.get(), false);
-    catalog.addObject(int3.get(), false);
+    objNumMap.addObject(a1.get());
+    objNumMap.addObject(a2.get());
+    objNumMap.addObject(a3.get());
 
-    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
-    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
-    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int3.get()) == 3);
-
-    SkDynamicMemoryWStream buffer;
-    catalog.emitObjectNumber(&buffer, int1.get());
-    catalog.emitObjectNumber(&buffer, int2.get());
-    catalog.emitObjectNumber(&buffer, int3.get());
-    catalog.emitObjectNumber(&buffer, int1Again.get());
-    char expectedResult[] = "1 02 03 01 0";
-    REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
-                                            strlen(expectedResult)));
+    // The objects should be numbered in the order they are added,
+    // starting with 1.
+    REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a1.get()) == 1);
+    REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a2.get()) == 2);
+    REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a3.get()) == 3);
+    // Assert that repeated calls to get the object number return
+    // consistent result.
+    REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a1.get()) == 1);
 }
 
 static void TestObjectRef(skiatest::Reporter* reporter) {
-    SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
-    SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
-    SkAutoTUnref<SkPDFObjRef> int2ref(new SkPDFObjRef(int2.get()));
+    sk_sp<SkPDFArray> a1(new SkPDFArray);
+    sk_sp<SkPDFArray> a2(new SkPDFArray);
+    a2->appendObjRef(a1);
 
-    SkPDFCatalog catalog((SkPDFDocument::Flags)0);
-    catalog.addObject(int1.get(), false);
-    catalog.addObject(int2.get(), false);
-    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
-    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
+    SkPDFObjNumMap catalog;
+    catalog.addObject(a1.get());
+    REPORTER_ASSERT(reporter, catalog.getObjectNumber(a1.get()) == 1);
 
-    char expectedResult[] = "2 0 R";
-    SkDynamicMemoryWStream buffer;
-    int2ref->emitObject(&buffer, &catalog, false);
-    REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
-    REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
-                                            buffer.getOffset()));
-}
-
-static void TestSubstitute(skiatest::Reporter* reporter) {
-    SkAutoTUnref<SkPDFTestDict> proxy(new SkPDFTestDict());
-    SkAutoTUnref<SkPDFTestDict> stub(new SkPDFTestDict());
-    SkAutoTUnref<SkPDFInt> int33(new SkPDFInt(33));
-    SkAutoTUnref<SkPDFDict> stubResource(new SkPDFDict());
-    SkAutoTUnref<SkPDFInt> int44(new SkPDFInt(44));
-
-    stub->insert("Value", int33.get());
-    stubResource->insert("InnerValue", int44.get());
-    stub->addResource(stubResource.get());
-
-    SkPDFCatalog catalog((SkPDFDocument::Flags)0);
-    catalog.addObject(proxy.get(), false);
-    catalog.setSubstitute(proxy.get(), stub.get());
-
-    SkDynamicMemoryWStream buffer;
-    proxy->emit(&buffer, &catalog, false);
-    catalog.emitSubstituteResources(&buffer, false);
-
-    char objectResult[] = "2 0 obj\n<</Value 33\n>>\nendobj\n";
-    REPORTER_ASSERT(
-        reporter,
-        catalog.setFileOffset(proxy.get(), 0) == strlen(objectResult));
-
-    char expectedResult[] =
-        "<</Value 33\n>>1 0 obj\n<</InnerValue 44\n>>\nendobj\n";
-    REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
-    REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
-                                            buffer.getOffset()));
-}
-
-// Create a bitmap that would be very eficiently compressed in a ZIP.
-static void setup_bitmap(SkBitmap* bitmap, int width, int height) {
-    bitmap->allocN32Pixels(width, height);
-    bitmap->eraseColor(SK_ColorWHITE);
-}
-
-static void TestImage(skiatest::Reporter* reporter, const SkBitmap& bitmap,
-                      const char* expected, bool useDCTEncoder) {
-    SkISize pageSize = SkISize::Make(bitmap.width(), bitmap.height());
-    SkAutoTUnref<SkPDFDevice> dev(new SkPDFDevice(pageSize, pageSize, SkMatrix::I()));
-
-    if (useDCTEncoder) {
-        dev->setDCTEncoder(encode_to_dct_data);
-    }
-
-    SkCanvas c(dev);
-    c.drawBitmap(bitmap, 0, 0, NULL);
-
-    SkPDFDocument doc;
-    doc.appendPage(dev);
-
-    SkDynamicMemoryWStream stream;
-    doc.emitPDF(&stream);
-
-    REPORTER_ASSERT(reporter, stream_contains(stream, expected));
-}
-
-static void TestUncompressed(skiatest::Reporter* reporter) {
-    SkBitmap bitmap;
-    setup_bitmap(&bitmap, 1, 1);
-    TestImage(reporter, bitmap,
-              "/Subtype /Image\n"
-              "/Width 1\n"
-              "/Height 1\n"
-              "/ColorSpace /DeviceRGB\n"
-              "/BitsPerComponent 8\n"
-              "/Length 3\n"
-              ">> stream",
-              true);
-}
-
-static void TestFlateDecode(skiatest::Reporter* reporter) {
-    if (!SkFlate::HaveFlate()) {
-        return;
-    }
-    SkBitmap bitmap;
-    setup_bitmap(&bitmap, 10, 10);
-    TestImage(reporter, bitmap,
-              "/Subtype /Image\n"
-              "/Width 10\n"
-              "/Height 10\n"
-              "/ColorSpace /DeviceRGB\n"
-              "/BitsPerComponent 8\n"
-              "/Filter /FlateDecode\n"
-              "/Length 13\n"
-              ">> stream",
-              false);
-}
-
-static void TestDCTDecode(skiatest::Reporter* reporter) {
-    SkBitmap bitmap;
-    setup_bitmap(&bitmap, 32, 32);
-    TestImage(reporter, bitmap,
-              "/Subtype /Image\n"
-              "/Width 32\n"
-              "/Height 32\n"
-              "/ColorSpace /DeviceRGB\n"
-              "/BitsPerComponent 8\n"
-              "/Filter /DCTDecode\n"
-              "/ColorTransform 0\n"
-              "/Length 21\n"
-              ">> stream",
-              true);
-}
-
-static void TestImages(skiatest::Reporter* reporter) {
-    TestUncompressed(reporter);
-    TestFlateDecode(reporter);
-    TestDCTDecode(reporter);
+    SkString result = emit_to_string(*a2, &catalog);
+    // If appendObjRef misbehaves, then the result would
+    // be [[]], not [1 0 R].
+    assert_eq(reporter, result, "[1 0 R]");
 }
 
 // This test used to assert without the fix submitted for
@@ -328,155 +160,336 @@
 // SKP files might have invalid glyph ids. This test ensures they are ignored,
 // and there is no assert on input data in Debug mode.
 static void test_issue1083() {
-    SkISize pageSize = SkISize::Make(100, 100);
-    SkAutoTUnref<SkPDFDevice> dev(new SkPDFDevice(pageSize, pageSize, SkMatrix::I()));
-
-    SkCanvas c(dev);
+    SkDynamicMemoryWStream outStream;
+    sk_sp<SkDocument> doc(SkDocument::MakePDF(&outStream));
+    SkCanvas* canvas = doc->beginPage(100.0f, 100.0f);
     SkPaint paint;
     paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
     uint16_t glyphID = 65000;
-    c.drawText(&glyphID, 2, 0, 0, paint);
+    canvas->drawText(&glyphID, 2, 0, 0, paint);
 
-    SkPDFDocument doc;
-    doc.appendPage(dev);
-
-    SkDynamicMemoryWStream stream;
-    doc.emitPDF(&stream);
+    doc->close();
 }
 
-DEF_TEST(PDFPrimitives, reporter) {
-    SkAutoTUnref<SkPDFInt> int42(new SkPDFInt(42));
-    SimpleCheckObjectOutput(reporter, int42.get(), "42");
+static void assert_emit_eq_number(skiatest::Reporter* reporter, float number) {
+    SkPDFUnion pdfUnion = SkPDFUnion::Scalar(number);
+    SkString result = emit_to_string(pdfUnion);
+    float value = static_cast<float>(std::atof(result.c_str()));
+    if (value != number) {
+        ERRORF(reporter, "%.9g != %s", number, result.c_str());
+    }
+}
 
-    SkAutoTUnref<SkPDFScalar> realHalf(new SkPDFScalar(SK_ScalarHalf));
-    SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5");
 
-    SkAutoTUnref<SkPDFScalar> bigScalar(new SkPDFScalar(110999.75f));
-#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
-    SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000");
-#else
-    SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75");
+static void TestPDFUnion(skiatest::Reporter* reporter) {
+    SkPDFUnion boolTrue = SkPDFUnion::Bool(true);
+    assert_emit_eq(reporter, boolTrue, "true");
 
-    SkAutoTUnref<SkPDFScalar> biggerScalar(new SkPDFScalar(50000000.1));
-    SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000");
+    SkPDFUnion boolFalse = SkPDFUnion::Bool(false);
+    assert_emit_eq(reporter, boolFalse, "false");
 
-    SkAutoTUnref<SkPDFScalar> smallestScalar(new SkPDFScalar(1.0/65536));
-    SimpleCheckObjectOutput(reporter, smallestScalar.get(), "0.00001526");
-#endif
+    SkPDFUnion int42 = SkPDFUnion::Int(42);
+    assert_emit_eq(reporter, int42, "42");
 
-    SkAutoTUnref<SkPDFString> stringSimple(
-        new SkPDFString("test ) string ( foo"));
-    SimpleCheckObjectOutput(reporter, stringSimple.get(),
-                            "(test \\) string \\( foo)");
-    SkAutoTUnref<SkPDFString> stringComplex(
-        new SkPDFString("\ttest ) string ( foo"));
-    SimpleCheckObjectOutput(reporter, stringComplex.get(),
-                            "<0974657374202920737472696E67202820666F6F>");
+    assert_emit_eq_number(reporter, SK_ScalarHalf);
+    assert_emit_eq_number(reporter, 110999.75f);  // bigScalar
+    assert_emit_eq_number(reporter, 50000000.1f);  // biggerScalar
+    assert_emit_eq_number(reporter, 1.0f / 65536);  // smallScalar
 
-    SkAutoTUnref<SkPDFName> name(new SkPDFName("Test name\twith#tab"));
-    const char expectedResult[] = "/Test#20name#09with#23tab";
-    CheckObjectOutput(reporter, name.get(), expectedResult,
-                      strlen(expectedResult), false, false);
+    SkPDFUnion stringSimple = SkPDFUnion::String("test ) string ( foo");
+    assert_emit_eq(reporter, stringSimple, "(test \\) string \\( foo)");
 
-    SkAutoTUnref<SkPDFName> escapedName(new SkPDFName("A#/%()<>[]{}B"));
-    const char escapedNameExpected[] = "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB";
-    CheckObjectOutput(reporter, escapedName.get(), escapedNameExpected,
-                      strlen(escapedNameExpected), false, false);
+    SkString stringComplexInput("\ttest ) string ( foo");
+    SkPDFUnion stringComplex = SkPDFUnion::String(stringComplexInput);
+    assert_emit_eq(reporter, stringComplex, "(\\011test \\) string \\( foo)");
+
+    SkString binaryStringInput("\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20");
+    SkPDFUnion binaryString = SkPDFUnion::String(binaryStringInput);
+    assert_emit_eq(reporter, binaryString, "<0102030405060708090A0B0C0D0E0F10>");
+
+    SkString nameInput("Test name\twith#tab");
+    SkPDFUnion name = SkPDFUnion::Name(nameInput);
+    assert_emit_eq(reporter, name, "/Test#20name#09with#23tab");
+
+    SkString nameInput2("A#/%()<>[]{}B");
+    SkPDFUnion name2 = SkPDFUnion::Name(nameInput2);
+    assert_emit_eq(reporter, name2, "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB");
+
+    SkPDFUnion name3 = SkPDFUnion::Name("SimpleNameWithOnlyPrintableASCII");
+    assert_emit_eq(reporter, name3, "/SimpleNameWithOnlyPrintableASCII");
 
     // Test that we correctly handle characters with the high-bit set.
-    const unsigned char highBitCString[] = {0xDE, 0xAD, 'b', 'e', 0xEF, 0};
-    SkAutoTUnref<SkPDFName> highBitName(
-        new SkPDFName((const char*)highBitCString));
-    const char highBitExpectedResult[] = "/#DE#ADbe#EF";
-    CheckObjectOutput(reporter, highBitName.get(), highBitExpectedResult,
-                      strlen(highBitExpectedResult), false, false);
+    SkString highBitString("\xDE\xAD" "be\xEF");
+    SkPDFUnion highBitName = SkPDFUnion::Name(highBitString);
+    assert_emit_eq(reporter, highBitName, "/#DE#ADbe#EF");
+}
 
-    SkAutoTUnref<SkPDFArray> array(new SkPDFArray);
-    SimpleCheckObjectOutput(reporter, array.get(), "[]");
-    array->append(int42.get());
-    SimpleCheckObjectOutput(reporter, array.get(), "[42]");
-    array->append(realHalf.get());
-    SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5]");
-    SkAutoTUnref<SkPDFInt> int0(new SkPDFInt(0));
-    array->append(int0.get());
-    SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]");
-    SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
-    array->setAt(0, int1.get());
-    SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]");
+static void TestPDFArray(skiatest::Reporter* reporter) {
+    sk_sp<SkPDFArray> array(new SkPDFArray);
+    assert_emit_eq(reporter, *array, "[]");
 
-    SkAutoTUnref<SkPDFDict> dict(new SkPDFDict);
-    SimpleCheckObjectOutput(reporter, dict.get(), "<<>>");
-    SkAutoTUnref<SkPDFName> n1(new SkPDFName("n1"));
-    dict->insert(n1.get(), int42.get());
-    SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>");
-    SkAutoTUnref<SkPDFName> n2(new SkPDFName("n2"));
-    SkAutoTUnref<SkPDFName> n3(new SkPDFName("n3"));
-    dict->insert(n2.get(), realHalf.get());
-    dict->insert(n3.get(), array.get());
-    SimpleCheckObjectOutput(reporter, dict.get(),
-                            "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>");
+    array->appendInt(42);
+    assert_emit_eq(reporter, *array, "[42]");
 
+    array->appendScalar(SK_ScalarHalf);
+    assert_emit_eq(reporter, *array, "[42 .5]");
+
+    array->appendInt(0);
+    assert_emit_eq(reporter, *array, "[42 .5 0]");
+
+    array->appendBool(true);
+    assert_emit_eq(reporter, *array, "[42 .5 0 true]");
+
+    array->appendName("ThisName");
+    assert_emit_eq(reporter, *array, "[42 .5 0 true /ThisName]");
+
+    array->appendName(SkString("AnotherName"));
+    assert_emit_eq(reporter, *array, "[42 .5 0 true /ThisName /AnotherName]");
+
+    array->appendString("This String");
+    assert_emit_eq(reporter, *array,
+                   "[42 .5 0 true /ThisName /AnotherName (This String)]");
+
+    array->appendString(SkString("Another String"));
+    assert_emit_eq(reporter, *array,
+                   "[42 .5 0 true /ThisName /AnotherName (This String) "
+                   "(Another String)]");
+
+    sk_sp<SkPDFArray> innerArray(new SkPDFArray);
+    innerArray->appendInt(-1);
+    array->appendObject(std::move(innerArray));
+    assert_emit_eq(reporter, *array,
+                   "[42 .5 0 true /ThisName /AnotherName (This String) "
+                   "(Another String) [-1]]");
+
+    sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
+    SkPDFObjNumMap catalog;
+    catalog.addObject(referencedArray.get());
+    REPORTER_ASSERT(reporter, catalog.getObjectNumber(
+                            referencedArray.get()) == 1);
+    array->appendObjRef(std::move(referencedArray));
+
+    SkString result = emit_to_string(*array, &catalog);
+    assert_eq(reporter, result,
+              "[42 .5 0 true /ThisName /AnotherName (This String) "
+              "(Another String) [-1] 1 0 R]");
+}
+
+static void TestPDFDict(skiatest::Reporter* reporter) {
+    sk_sp<SkPDFDict> dict(new SkPDFDict);
+    assert_emit_eq(reporter, *dict, "<<>>");
+
+    dict->insertInt("n1", SkToSizeT(42));
+    assert_emit_eq(reporter, *dict, "<</n1 42>>");
+
+    dict.reset(new SkPDFDict);
+    assert_emit_eq(reporter, *dict, "<<>>");
+
+    dict->insertInt("n1", 42);
+    assert_emit_eq(reporter, *dict, "<</n1 42>>");
+
+    dict->insertScalar("n2", SK_ScalarHalf);
+
+    SkString n3("n3");
+    sk_sp<SkPDFArray> innerArray(new SkPDFArray);
+    innerArray->appendInt(-100);
+    dict->insertObject(n3, std::move(innerArray));
+    assert_emit_eq(reporter, *dict, "<</n1 42\n/n2 .5\n/n3 [-100]>>");
+
+    dict.reset(new SkPDFDict);
+    assert_emit_eq(reporter, *dict, "<<>>");
+
+    dict->insertInt("n1", 24);
+    assert_emit_eq(reporter, *dict, "<</n1 24>>");
+
+    dict->insertInt("n2", SkToSizeT(99));
+    assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99>>");
+
+    dict->insertScalar("n3", SK_ScalarHalf);
+    assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5>>");
+
+    dict->insertName("n4", "AName");
+    assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName>>");
+
+    dict->insertName("n5", SkString("AnotherName"));
+    assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
+                   "/n5 /AnotherName>>");
+
+    dict->insertString("n6", "A String");
+    assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
+                   "/n5 /AnotherName\n/n6 (A String)>>");
+
+    dict->insertString("n7", SkString("Another String"));
+    assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
+                   "/n5 /AnotherName\n/n6 (A String)\n/n7 (Another String)>>");
+
+    dict.reset(new SkPDFDict("DType"));
+    assert_emit_eq(reporter, *dict, "<</Type /DType>>");
+
+    sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
+    SkPDFObjNumMap catalog;
+    catalog.addObject(referencedArray.get());
+    REPORTER_ASSERT(reporter, catalog.getObjectNumber(
+                            referencedArray.get()) == 1);
+    dict->insertObjRef("n1", std::move(referencedArray));
+    SkString result = emit_to_string(*dict, &catalog);
+    assert_eq(reporter, result, "<</Type /DType\n/n1 1 0 R>>");
+}
+
+DEF_TEST(SkPDF_Primitives, reporter) {
+    TestPDFUnion(reporter);
+    TestPDFArray(reporter);
+    TestPDFDict(reporter);
     TestPDFStream(reporter);
-
-    TestCatalog(reporter);
-
+    TestObjectNumberMap(reporter);
     TestObjectRef(reporter);
-
-    TestSubstitute(reporter);
-
     test_issue1083();
-
-    TestImages(reporter);
 }
 
 namespace {
 
 class DummyImageFilter : public SkImageFilter {
 public:
-    DummyImageFilter(bool visited = false) : SkImageFilter(0, NULL), fVisited(visited) {}
-    virtual ~DummyImageFilter() SK_OVERRIDE {}
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* offset) const {
-        fVisited = true;
-        offset->fX = offset->fY = 0;
-        *result = src;
-        return true;
+    static sk_sp<DummyImageFilter> Make(bool visited = false) {
+        return sk_sp<DummyImageFilter>(new DummyImageFilter(visited));
     }
+
+    SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DummyImageFilter)
-#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
-    explicit DummyImageFilter(SkReadBuffer& buffer) : SkImageFilter(0, NULL) {
-        fVisited = buffer.readBool();
-    }
-#endif
     bool visited() const { return fVisited; }
 
+protected:
+    sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
+                                        SkIPoint* offset) const override {
+        fVisited = true;
+        offset->fX = offset->fY = 0;
+        return sk_ref_sp<SkSpecialImage>(source);
+    }
+    sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override {
+        return sk_ref_sp(const_cast<DummyImageFilter*>(this));
+    }
+
 private:
+    DummyImageFilter(bool visited) : INHERITED(nullptr, 0, nullptr), fVisited(visited) {}
+
     mutable bool fVisited;
+
+    typedef SkImageFilter INHERITED;
 };
 
-SkFlattenable* DummyImageFilter::CreateProc(SkReadBuffer& buffer) {
+sk_sp<SkFlattenable> DummyImageFilter::CreateProc(SkReadBuffer& buffer) {
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
     bool visited = buffer.readBool();
-    return SkNEW_ARGS(DummyImageFilter, (visited));
+    return DummyImageFilter::Make(visited);
 }
 
+#ifndef SK_IGNORE_TO_STRING
+void DummyImageFilter::toString(SkString* str) const {
+    str->appendf("DummyImageFilter: (");
+    str->append(")");
+}
+#endif
+
 };
 
 // Check that PDF rendering of image filters successfully falls back to
 // CPU rasterization.
-DEF_TEST(PDFImageFilter, reporter) {
-    SkISize pageSize = SkISize::Make(100, 100);
-    SkAutoTUnref<SkPDFDevice> device(new SkPDFDevice(pageSize, pageSize, SkMatrix::I()));
-    SkCanvas canvas(device.get());
-    SkAutoTUnref<DummyImageFilter> filter(new DummyImageFilter());
+DEF_TEST(SkPDF_ImageFilter, reporter) {
+    REQUIRE_PDF_DOCUMENT(SkPDF_ImageFilter, reporter);
+    SkDynamicMemoryWStream stream;
+    sk_sp<SkDocument> doc(SkDocument::MakePDF(&stream));
+    SkCanvas* canvas = doc->beginPage(100.0f, 100.0f);
+
+    sk_sp<DummyImageFilter> filter(DummyImageFilter::Make());
 
     // Filter just created; should be unvisited.
     REPORTER_ASSERT(reporter, !filter->visited());
     SkPaint paint;
-    paint.setImageFilter(filter.get());
-    canvas.drawRect(SkRect::MakeWH(100, 100), paint);
+    paint.setImageFilter(filter);
+    canvas->drawRect(SkRect::MakeWH(100, 100), paint);
+    doc->close();
 
     // Filter was used in rendering; should be visited.
     REPORTER_ASSERT(reporter, filter->visited());
 }
+
+// Check that PDF rendering of image filters successfully falls back to
+// CPU rasterization.
+DEF_TEST(SkPDF_FontCanEmbedTypeface, reporter) {
+    SkPDFCanon canon;
+
+    const char resource[] = "fonts/Roboto2-Regular_NoEmbed.ttf";
+    sk_sp<SkTypeface> noEmbedTypeface(MakeResourceAsTypeface(resource));
+    if (noEmbedTypeface) {
+        REPORTER_ASSERT(reporter,
+                        !SkPDFFont::CanEmbedTypeface(noEmbedTypeface.get(), &canon));
+    }
+    sk_sp<SkTypeface> portableTypeface(
+            sk_tool_utils::create_portable_typeface(NULL, SkFontStyle()));
+    REPORTER_ASSERT(reporter,
+                    SkPDFFont::CanEmbedTypeface(portableTypeface.get(), &canon));
+}
+
+
+// test to see that all finite scalars round trip via scanf().
+static void check_pdf_scalar_serialization(
+        skiatest::Reporter* reporter, float inputFloat) {
+    char floatString[SkPDFUtils::kMaximumFloatDecimalLength];
+    size_t len = SkPDFUtils::FloatToDecimal(inputFloat, floatString);
+    if (len >= sizeof(floatString)) {
+        ERRORF(reporter, "string too long: %u", (unsigned)len);
+        return;
+    }
+    if (floatString[len] != '\0' || strlen(floatString) != len) {
+        ERRORF(reporter, "terminator misplaced.");
+        return;  // The terminator is needed for sscanf().
+    }
+    if (reporter->verbose()) {
+        SkDebugf("%15.9g = \"%s\"\n", inputFloat, floatString);
+    }
+    float roundTripFloat;
+    if (1 != sscanf(floatString, "%f", &roundTripFloat)) {
+        ERRORF(reporter, "unscannable result: %s", floatString);
+        return;
+    }
+    if (std::isfinite(inputFloat) && roundTripFloat != inputFloat) {
+        ERRORF(reporter, "roundTripFloat (%.9g) != inputFloat (%.9g)",
+               roundTripFloat, inputFloat);
+    }
+}
+
+// Test SkPDFUtils::AppendScalar for accuracy.
+DEF_TEST(SkPDF_Primitives_Scalar, reporter) {
+    SkRandom random(0x5EED);
+    int iterationCount = 512;
+    while (iterationCount-- > 0) {
+        union { uint32_t u; float f; };
+        u = random.nextU();
+        static_assert(sizeof(float) == sizeof(uint32_t), "");
+        check_pdf_scalar_serialization(reporter, f);
+    }
+    float alwaysCheck[] = {
+        0.0f, -0.0f, 1.0f, -1.0f, SK_ScalarPI, 0.1f, FLT_MIN, FLT_MAX,
+        -FLT_MIN, -FLT_MAX, FLT_MIN / 16.0f, -FLT_MIN / 16.0f,
+        SK_FloatNaN, SK_FloatInfinity, SK_FloatNegativeInfinity,
+        -FLT_MIN / 8388608.0
+    };
+    for (float inputFloat: alwaysCheck) {
+        check_pdf_scalar_serialization(reporter, inputFloat);
+    }
+}
+
+// Test SkPDFUtils:: for accuracy.
+DEF_TEST(SkPDF_Primitives_Color, reporter) {
+    char buffer[5];
+    for (int i = 0; i < 256; ++i) {
+        size_t len = SkPDFUtils::ColorToDecimal(i, buffer);
+        REPORTER_ASSERT(reporter, len == strlen(buffer));
+        float f;
+        REPORTER_ASSERT(reporter, 1 == sscanf(buffer, "%f", &f));
+        int roundTrip = (int)(0.5 + f * 255);
+        REPORTER_ASSERT(reporter, roundTrip == i);
+    }
+}
+#endif
diff --git a/src/third_party/skia/tests/PackBitsTest.cpp b/src/third_party/skia/tests/PackBitsTest.cpp
index fdbd9bd..ac9a0ae 100644
--- a/src/third_party/skia/tests/PackBitsTest.cpp
+++ b/src/third_party/skia/tests/PackBitsTest.cpp
@@ -52,7 +52,7 @@
         REPORTER_ASSERT(reporter, match);
     }
 
-    for (size_t size = 1; size <= 512; size += 1) {
+    for (uint32_t size = 1; size <= 512; size += 1) {
         for (int n = 100; n; n--) {
             uint8_t src[600], src2[600];
             uint8_t dst[600];
diff --git a/src/third_party/skia/tests/PackedConfigsTextureTest.cpp b/src/third_party/skia/tests/PackedConfigsTextureTest.cpp
new file mode 100644
index 0000000..f91fc42
--- /dev/null
+++ b/src/third_party/skia/tests/PackedConfigsTextureTest.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * This is a straightforward test of using packed pixel configs (4444, 565).
+ * This test will make sure that these RGBA_4444 and RGB_565 are always supported
+ * as valid texturing configs.
+ */
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrResourceProvider.h"
+#include "GrTextureProxy.h"
+
+static const int DEV_W = 10, DEV_H = 10;
+static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
+static const uint8_t TOL = 0x4;
+
+static void check_component(skiatest::Reporter* reporter, uint8_t control, uint8_t test) {
+    uint8_t diff = 0;
+    if (control >= test) {
+        diff = control - test;
+    } else {
+        diff = test - control;
+    }
+    REPORTER_ASSERT(reporter, diff < TOL);
+}
+
+static uint8_t expand_value(uint8_t original, int sigBits) {
+    SkASSERT(sigBits >= 4);
+    uint8_t inSigBitShift = 8 - sigBits;
+    uint8_t duplBitShift = sigBits - inSigBitShift;
+    return (original << inSigBitShift) + (original >> duplBitShift);
+}
+
+static void check_4444(skiatest::Reporter* reporter,
+                       const SkTDArray<uint16_t>& controlData,
+                       const SkTDArray<uint32_t>& readBuffer) {
+    for (int j = 0; j < DEV_H; ++j) {
+        for (int i = 0; i < DEV_W; ++i) {
+            uint16_t control = controlData[i + j * DEV_H];
+            uint32_t test = readBuffer[i + j * DEV_H];
+
+            // Test alpha component
+            uint8_t ctrlComp = expand_value(control & 0xF, 4);
+            uint8_t testComp = GrColorUnpackA(test);
+            check_component(reporter, ctrlComp, testComp);
+
+            // Test blue component
+            ctrlComp = expand_value((control >> 4) & 0xF, 4);
+            testComp = GrColorUnpackB(test);
+            check_component(reporter, ctrlComp, testComp);
+
+            // Test green component
+            ctrlComp = expand_value((control >> 8) & 0xF, 4);
+            testComp = GrColorUnpackG(test);
+            check_component(reporter, ctrlComp, testComp);
+
+            // Test red component
+            ctrlComp = expand_value((control >> 12) & 0xF, 4);
+            testComp = GrColorUnpackR(test);
+            check_component(reporter, ctrlComp, testComp);
+        }
+    }
+}
+
+static void check_565(skiatest::Reporter* reporter,
+                      const SkTDArray<uint16_t>& controlData,
+                      const SkTDArray<GrColor>& readBuffer) {
+    for (int j = 0; j < DEV_H; ++j) {
+        for (int i = 0; i < DEV_W; ++i) {
+            uint16_t control = controlData[i + j * DEV_H];
+            GrColor test = readBuffer[i + j * DEV_H];
+            // Test blue component (5 bit control)
+            uint8_t ctrlComp = expand_value(control & 0x1F, 5);
+            uint8_t testComp = GrColorUnpackB(test);
+            check_component(reporter, ctrlComp, testComp);
+
+            // Test green component (6 bit control)
+            ctrlComp = expand_value((control >> 5) & 0x3F, 6);
+            testComp = GrColorUnpackG(test);
+            check_component(reporter, ctrlComp, testComp);
+
+            // Test red component (5 bit control)
+            ctrlComp = expand_value((control >> 11) & 0x1F, 5);
+            testComp = GrColorUnpackR(test);
+            check_component(reporter, ctrlComp, testComp);
+        }
+    }
+}
+
+static void run_test(skiatest::Reporter* reporter, GrContext* context,
+                     int arraySize, GrPixelConfig config) {
+    SkTDArray<uint16_t> controlPixelData;
+    // We will read back into an 8888 buffer since 565/4444 read backs aren't supported
+    SkTDArray<GrColor> readBuffer;
+    controlPixelData.setCount(arraySize);
+    readBuffer.setCount(arraySize);
+
+    for (int i = 0; i < arraySize; i += 2) {
+        controlPixelData[i] = 0xFF00;
+        controlPixelData[i + 1] = 0xFA62;
+    }
+
+    const SkImageInfo dstInfo = SkImageInfo::Make(DEV_W, DEV_H,
+                                                  kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
+
+    for (auto origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
+        GrSurfaceDesc desc;
+        desc.fFlags = kNone_GrSurfaceFlags;
+        desc.fWidth = DEV_W;
+        desc.fHeight = DEV_H;
+        desc.fConfig = config;
+        desc.fOrigin = origin;
+
+        sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                   desc, SkBudgeted::kNo,
+                                                                   controlPixelData.begin(), 0);
+        SkASSERT(proxy);
+
+        sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                                        std::move(proxy), nullptr);
+
+        SkAssertResult(sContext->readPixels(dstInfo, readBuffer.begin(), 0, 0, 0));
+
+        if (kRGBA_4444_GrPixelConfig == config) {
+            check_4444(reporter, controlPixelData, readBuffer);
+        } else {
+            SkASSERT(kRGB_565_GrPixelConfig == config);
+            check_565(reporter, controlPixelData, readBuffer);
+        }
+    }
+}
+
+static const int CONTROL_ARRAY_SIZE = DEV_W * DEV_H;
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(RGBA4444TextureTest, reporter, ctxInfo) {
+    run_test(reporter, ctxInfo.grContext(), CONTROL_ARRAY_SIZE, kRGBA_4444_GrPixelConfig);
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(RGB565TextureTest, reporter, ctxInfo) {
+    run_test(reporter, ctxInfo.grContext(), CONTROL_ARRAY_SIZE, kRGB_565_GrPixelConfig);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/PaintBreakTextTest.cpp b/src/third_party/skia/tests/PaintBreakTextTest.cpp
new file mode 100644
index 0000000..b716c60
--- /dev/null
+++ b/src/third_party/skia/tests/PaintBreakTextTest.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2011-2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkAutoMalloc.h"
+#include "SkPaint.h"
+#include "Test.h"
+
+static void test_monotonic(skiatest::Reporter* reporter,
+                           const SkPaint& paint,
+                           const char* msg) {
+    const char* text = "sdfkljAKLDFJKEWkldfjlk#$%&sdfs.dsj";
+    const size_t length = strlen(text);
+    const SkScalar width = paint.measureText(text, length);
+
+    SkScalar mm = 0;
+    size_t nn = 0;
+    const SkScalar step = SkMaxScalar(width / 10, SK_Scalar1);
+    for (SkScalar w = 0; w <= width; w += step) {
+        SkScalar m;
+        const size_t n = paint.breakText(text, length, w, &m);
+
+        REPORTER_ASSERT_MESSAGE(reporter, n <= length, msg);
+        REPORTER_ASSERT_MESSAGE(reporter, m <= width, msg);
+
+        if (n == 0) {
+            REPORTER_ASSERT_MESSAGE(reporter, m == 0, msg);
+        } else {
+            // now assert that we're monotonic
+            if (n == nn) {
+                REPORTER_ASSERT_MESSAGE(reporter, m == mm, msg);
+            } else {
+                REPORTER_ASSERT_MESSAGE(reporter, n > nn, msg);
+                REPORTER_ASSERT_MESSAGE(reporter, m > mm, msg);
+            }
+        }
+        nn = n;
+        mm = m;
+    }
+}
+
+static void test_eq_measure_text(skiatest::Reporter* reporter,
+                                 const SkPaint& paint,
+                                 const char* msg) {
+    const char* text = "The ultimate measure of a man is not where he stands in moments of comfort "
+        "and convenience, but where he stands at times of challenge and controversy.";
+    const size_t length = strlen(text);
+    const SkScalar width = paint.measureText(text, length);
+
+    SkScalar mm;
+    const size_t length2 = paint.breakText(text, length, width, &mm);
+    REPORTER_ASSERT_MESSAGE(reporter, length2 == length, msg);
+    REPORTER_ASSERT_MESSAGE(reporter, mm == width, msg);
+}
+
+static void test_long_text(skiatest::Reporter* reporter,
+                           const SkPaint& paint,
+                           const char* msg) {
+    static const int kSize = 16 * 1024;
+    SkAutoMalloc block(kSize);
+    memset(block.get(), 'a', kSize - 1);
+    char* text = static_cast<char*>(block.get());
+    text[kSize - 1] = '\0';
+    const SkScalar width = paint.measureText(text, kSize);
+
+    SkScalar mm;
+    const size_t length = paint.breakText(text, kSize, width, &mm);
+    REPORTER_ASSERT_MESSAGE(reporter, length == kSize, msg);
+    REPORTER_ASSERT_MESSAGE(reporter, mm == width, msg);
+}
+
+DEF_TEST(PaintBreakText, reporter) {
+    SkPaint paint;
+    test_monotonic(reporter, paint, "default");
+    test_eq_measure_text(reporter, paint, "default");
+    test_long_text(reporter, paint, "default");
+    paint.setTextSize(SkIntToScalar(1 << 17));
+    test_monotonic(reporter, paint, "huge text size");
+    test_eq_measure_text(reporter, paint, "huge text size");
+    paint.setTextSize(0);
+    test_monotonic(reporter, paint, "zero text size");
+}
diff --git a/src/third_party/skia/tests/PaintImageFilterTest.cpp b/src/third_party/skia/tests/PaintImageFilterTest.cpp
new file mode 100644
index 0000000..0365336
--- /dev/null
+++ b/src/third_party/skia/tests/PaintImageFilterTest.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPaintImageFilter.h"
+#include "SkShader.h"
+#include "Test.h"
+
+static void test_unscaled(skiatest::Reporter* reporter) {
+    int w = 10, h = 10;
+    SkRect r = SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h));
+
+    SkBitmap filterResult, paintResult;
+
+    filterResult.allocN32Pixels(w, h);
+    SkCanvas canvasFilter(filterResult);
+    canvasFilter.clear(0x00000000);
+
+    paintResult.allocN32Pixels(w, h);
+    SkCanvas canvasPaint(paintResult);
+    canvasPaint.clear(0x00000000);
+
+    SkPoint center = SkPoint::Make(SkIntToScalar(5), SkIntToScalar(5));
+    SkColor colors[] = {SK_ColorBLUE, SK_ColorRED, SK_ColorGREEN};
+    SkScalar pos[] = {0, SK_ScalarHalf, SK_Scalar1};
+    SkScalar radius = SkIntToScalar(5);
+
+    SkPaint gradientPaint;
+    gradientPaint.setShader(SkGradientShader::MakeRadial(
+        center, radius, colors, pos, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode));
+
+    // Test using the image filter
+    {
+        SkPaint paint;
+        SkImageFilter::CropRect cr(r);
+        paint.setImageFilter(SkPaintImageFilter::Make(gradientPaint, &cr));
+        canvasFilter.drawRect(r, paint);
+    }
+
+    // Test using the paint directly
+    {
+        canvasPaint.drawRect(r, gradientPaint);
+    }
+
+    // Assert that both paths yielded the same result
+    for (int y = 0; y < r.height(); ++y) {
+        const SkPMColor* filterPtr = filterResult.getAddr32(0, y);
+        const SkPMColor* paintPtr = paintResult.getAddr32(0, y);
+        for (int x = 0; x < r.width(); ++x, ++filterPtr, ++paintPtr) {
+            REPORTER_ASSERT(reporter, *filterPtr == *paintPtr);
+        }
+    }
+}
+
+static void test_scaled(skiatest::Reporter* reporter) {
+    int w = 10, h = 10;
+    SkRect r = SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h));
+
+    SkBitmap filterResult, paintResult;
+
+    filterResult.allocN32Pixels(w, h);
+    SkCanvas canvasFilter(filterResult);
+    canvasFilter.clear(0x00000000);
+
+    paintResult.allocN32Pixels(w, h);
+    SkCanvas canvasPaint(paintResult);
+    canvasPaint.clear(0x00000000);
+
+    SkPoint center = SkPoint::Make(SkIntToScalar(5), SkIntToScalar(5));
+    SkColor colors[] = {SK_ColorBLUE, SK_ColorRED, SK_ColorGREEN};
+    SkScalar pos[] = {0, SK_ScalarHalf, SK_Scalar1};
+    SkScalar radius = SkIntToScalar(5);
+
+    SkPaint gradientPaint;
+    gradientPaint.setShader(SkGradientShader::MakeRadial(
+        center, radius, colors, pos, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode));
+
+    // Test using the image filter
+    {
+        SkPaint paint;
+        SkImageFilter::CropRect cr(r);
+        paint.setImageFilter(SkPaintImageFilter::Make(gradientPaint, &cr));
+        canvasFilter.scale(SkIntToScalar(2), SkIntToScalar(2));
+        canvasFilter.drawRect(r, paint);
+    }
+
+    // Test using the paint directly
+    {
+        canvasPaint.scale(SkIntToScalar(2), SkIntToScalar(2));
+        canvasPaint.drawRect(r, gradientPaint);
+    }
+
+    // Assert that both paths yielded the same result
+    for (int y = 0; y < r.height(); ++y) {
+        const SkPMColor* filterPtr = filterResult.getAddr32(0, y);
+        const SkPMColor* paintPtr = paintResult.getAddr32(0, y);
+        for (int x = 0; x < r.width(); ++x, ++filterPtr, ++paintPtr) {
+            REPORTER_ASSERT(reporter, *filterPtr == *paintPtr);
+        }
+    }
+}
+
+DEF_TEST(PaintImageFilter, reporter) {
+    test_unscaled(reporter);
+    test_scaled(reporter);
+}
diff --git a/src/third_party/skia/tests/PaintTest.cpp b/src/third_party/skia/tests/PaintTest.cpp
index 57d0283..7ddcd0d 100644
--- a/src/third_party/skia/tests/PaintTest.cpp
+++ b/src/third_party/skia/tests/PaintTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkAutoMalloc.h"
 #include "SkBlurMask.h"
 #include "SkBlurMaskFilter.h"
 #include "SkLayerDrawLooper.h"
@@ -15,13 +16,12 @@
 #include "SkTypeface.h"
 #include "SkUtils.h"
 #include "SkWriteBuffer.h"
-#include "SkXfermode.h"
 #include "Test.h"
 
 static size_t uni_to_utf8(const SkUnichar src[], void* dst, int count) {
     char* u8 = (char*)dst;
     for (int i = 0; i < count; ++i) {
-        int n = SkUTF8_FromUnichar(src[i], u8);
+        int n = SkToInt(SkUTF8_FromUnichar(src[i], u8));
         u8 += n;
     }
     return u8 - (char*)dst;
@@ -30,7 +30,7 @@
 static size_t uni_to_utf16(const SkUnichar src[], void* dst, int count) {
     uint16_t* u16 = (uint16_t*)dst;
     for (int i = 0; i < count; ++i) {
-        int n = SkUTF16_FromUnichar(src[i], u16);
+        int n = SkToInt(SkUTF16_FromUnichar(src[i], u16));
         u16 += n;
     }
     return (char*)u16 - (char*)dst;
@@ -80,7 +80,7 @@
 
     SkRandom rand;
     SkPaint paint;
-    paint.setTypeface(SkTypeface::RefDefault())->unref();
+    paint.setTypeface(SkTypeface::MakeDefault());
     SkTypeface* face = paint.getTypeface();
 
     for (int i = 0; i < 1000; ++i) {
@@ -116,27 +116,25 @@
 }
 
 // temparary api for bicubic, just be sure we can set/clear it
-DEF_TEST(Paint_filterlevel, reporter) {
+DEF_TEST(Paint_filterQuality, reporter) {
     SkPaint p0, p1;
 
-    REPORTER_ASSERT(reporter,
-                    SkPaint::kNone_FilterLevel == p0.getFilterLevel());
+    REPORTER_ASSERT(reporter, kNone_SkFilterQuality == p0.getFilterQuality());
 
-    static const SkPaint::FilterLevel gLevels[] = {
-        SkPaint::kNone_FilterLevel,
-        SkPaint::kLow_FilterLevel,
-        SkPaint::kMedium_FilterLevel,
-        SkPaint::kHigh_FilterLevel
+    static const SkFilterQuality gQualitys[] = {
+        kNone_SkFilterQuality,
+        kLow_SkFilterQuality,
+        kMedium_SkFilterQuality,
+        kHigh_SkFilterQuality
     };
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gLevels); ++i) {
-        p0.setFilterLevel(gLevels[i]);
-        REPORTER_ASSERT(reporter, gLevels[i] == p0.getFilterLevel());
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) {
+        p0.setFilterQuality(gQualitys[i]);
+        REPORTER_ASSERT(reporter, gQualitys[i] == p0.getFilterQuality());
         p1 = p0;
-        REPORTER_ASSERT(reporter, gLevels[i] == p1.getFilterLevel());
+        REPORTER_ASSERT(reporter, gQualitys[i] == p1.getFilterQuality());
 
         p0.reset();
-        REPORTER_ASSERT(reporter,
-                        SkPaint::kNone_FilterLevel == p0.getFilterLevel());
+        REPORTER_ASSERT(reporter, kNone_SkFilterQuality == p0.getFilterQuality());
     }
 }
 
@@ -148,51 +146,24 @@
     paint.setStrokeWidth(SkIntToScalar(2));
     // set a few pointers
     SkLayerDrawLooper::Builder looperBuilder;
-    SkLayerDrawLooper* looper = looperBuilder.detachLooper();
-    paint.setLooper(looper)->unref();
-    SkMaskFilter* mask = SkBlurMaskFilter::Create(kNormal_SkBlurStyle,
-                                      SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(1)));
-    paint.setMaskFilter(mask)->unref();
+    paint.setLooper(looperBuilder.detach());
+    paint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
+                                               SkBlurMask::ConvertRadiusToSigma(1)));
 
     // copy the paint using the copy constructor and check they are the same
     SkPaint copiedPaint = paint;
     REPORTER_ASSERT(reporter, paint == copiedPaint);
 
-#ifdef SK_BUILD_FOR_ANDROID
-    // the copy constructor should preserve the Generation ID
-    uint32_t paintGenID = paint.getGenerationID();
-    uint32_t copiedPaintGenID = copiedPaint.getGenerationID();
-    REPORTER_ASSERT(reporter, paintGenID == copiedPaintGenID);
-    REPORTER_ASSERT(reporter, paint == copiedPaint);
-#endif
-
     // copy the paint using the equal operator and check they are the same
     copiedPaint = paint;
     REPORTER_ASSERT(reporter, paint == copiedPaint);
 
-#ifdef SK_BUILD_FOR_ANDROID
-    // the equals operator should increment the Generation ID
-    REPORTER_ASSERT(reporter, paint.getGenerationID() == paintGenID);
-    REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID);
-    copiedPaintGenID = copiedPaint.getGenerationID(); // reset to the new value
-    REPORTER_ASSERT(reporter, paint == copiedPaint);  // operator== ignores fGenerationID
-#endif
-
     // clean the paint and check they are back to their initial states
     SkPaint cleanPaint;
     paint.reset();
     copiedPaint.reset();
     REPORTER_ASSERT(reporter, cleanPaint == paint);
     REPORTER_ASSERT(reporter, cleanPaint == copiedPaint);
-
-#ifdef SK_BUILD_FOR_ANDROID
-    // the reset function should increment the Generation ID
-    REPORTER_ASSERT(reporter, paint.getGenerationID() != paintGenID);
-    REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID);
-    // operator== ignores fGenerationID
-    REPORTER_ASSERT(reporter, cleanPaint == paint);
-    REPORTER_ASSERT(reporter, cleanPaint == copiedPaint);
-#endif
 }
 
 // found and fixed for webkit: mishandling when we hit recursion limit on
@@ -221,7 +192,7 @@
     SkRect maxR = fillR;
     SkScalar miter = SkMaxScalar(SK_Scalar1, paint.getStrokeMiter());
     SkScalar inset = paint.getStrokeJoin() == SkPaint::kMiter_Join ?
-                            SkScalarMul(paint.getStrokeWidth(), miter) :
+                            paint.getStrokeWidth() * miter :
                             paint.getStrokeWidth();
     maxR.inset(-inset, -inset);
 
@@ -230,11 +201,11 @@
 }
 
 DEF_TEST(Paint_flattening, reporter) {
-    const SkPaint::FilterLevel levels[] = {
-        SkPaint::kNone_FilterLevel,
-        SkPaint::kLow_FilterLevel,
-        SkPaint::kMedium_FilterLevel,
-        SkPaint::kHigh_FilterLevel,
+    const SkFilterQuality levels[] = {
+        kNone_SkFilterQuality,
+        kLow_SkFilterQuality,
+        kMedium_SkFilterQuality,
+        kHigh_SkFilterQuality,
     };
     const SkPaint::Hinting hinting[] = {
         SkPaint::kNo_Hinting,
@@ -276,7 +247,7 @@
     SkPaint paint;
     paint.setFlags(0x1234);
 
-    FOR_SETUP(i, levels, setFilterLevel)
+    FOR_SETUP(i, levels, setFilterQuality)
     FOR_SETUP(j, hinting, setHinting)
     FOR_SETUP(k, align, setTextAlign)
     FOR_SETUP(l, caps, setStrokeCap)
@@ -284,11 +255,12 @@
     FOR_SETUP(n, encodings, setTextEncoding)
     FOR_SETUP(p, styles, setStyle)
 
-    SkWriteBuffer writer;
+    SkBinaryWriteBuffer writer;
     paint.flatten(writer);
 
-    const uint32_t* written = writer.getWriter32()->contiguousArray();
-    SkReadBuffer reader(written, writer.bytesWritten());
+    SkAutoMalloc buf(writer.bytesWritten());
+    writer.writeToMemory(buf.get());
+    SkReadBuffer reader(buf.get(), writer.bytesWritten());
 
     SkPaint paint2;
     paint2.unflatten(reader);
@@ -315,29 +287,23 @@
 
 #define ASSERT(expr) REPORTER_ASSERT(r, expr)
 
-DEF_TEST(Paint_FlatteningTraits, r) {
+DEF_TEST(Paint_MoreFlattening, r) {
     SkPaint paint;
     paint.setColor(0x00AABBCC);
     paint.setTextScaleX(1.0f);  // Default value, ignored.
     paint.setTextSize(19);
-    paint.setXfermode(SkXfermode::Create(SkXfermode::kModulate_Mode))->unref();
-    paint.setLooper(NULL);  // Default value, ignored.
+    paint.setBlendMode(SkBlendMode::kModulate);
+    paint.setLooper(nullptr);  // Default value, ignored.
 
-    SkWriteBuffer writer;
-    SkPaint::FlatteningTraits::Flatten(writer, paint);
+    SkBinaryWriteBuffer writer;
+    paint.flatten(writer);
 
-    // BEGIN white box asserts: if the impl changes, these asserts may change
-        const size_t expectedBytesWritten = sizeof(void*) == 8 ? 32 : 28;
-        ASSERT(expectedBytesWritten == writer.bytesWritten());
+    SkAutoMalloc buf(writer.bytesWritten());
+    writer.writeToMemory(buf.get());
+    SkReadBuffer reader(buf.get(), writer.bytesWritten());
 
-        const uint32_t* written = writer.getWriter32()->contiguousArray();
-        SkASSERT(written != NULL);
-        ASSERT(*written == ((1<<0) | (1<<1) | (1<<8)));  // Dirty bits for our 3.
-    // END white box asserts
-
-    SkReadBuffer reader(written, writer.bytesWritten());
     SkPaint other;
-    SkPaint::FlatteningTraits::Unflatten(reader, &other);
+    other.unflatten(reader);
     ASSERT(reader.offset() == writer.bytesWritten());
 
     // No matter the encoding, these must always hold.
@@ -345,10 +311,57 @@
     ASSERT(other.getTextScaleX() == paint.getTextScaleX());
     ASSERT(other.getTextSize()   == paint.getTextSize());
     ASSERT(other.getLooper()     == paint.getLooper());
+    ASSERT(other.getBlendMode()  == paint.getBlendMode());
+}
 
-    // We have to be a little looser and compare just the modes.  Pointers might not be the same.
-    SkXfermode::Mode otherMode, paintMode;
-    ASSERT(other.getXfermode()->asMode(&otherMode));
-    ASSERT(paint.getXfermode()->asMode(&paintMode));
-    ASSERT(otherMode == paintMode);
+DEF_TEST(Paint_getHash, r) {
+    // Try not to inspect the actual hash values in here.
+    // We might want to change the hash function.
+
+    SkPaint paint;
+    const uint32_t defaultHash = paint.getHash();
+
+    // Check that some arbitrary field affects the hash.
+    paint.setColor(0xFF00FF00);
+    REPORTER_ASSERT(r, paint.getHash() != defaultHash);
+    paint.setColor(SK_ColorBLACK);  // Reset to default value.
+    REPORTER_ASSERT(r, paint.getHash() == defaultHash);
+
+    // SkTypeface is the first field we hash, so test it specially.
+    paint.setTypeface(SkTypeface::MakeDefault());
+    REPORTER_ASSERT(r, paint.getHash() != defaultHash);
+    paint.setTypeface(nullptr);
+    REPORTER_ASSERT(r, paint.getHash() == defaultHash);
+
+    // This is part of fBitfields, the last field we hash.
+    paint.setHinting(SkPaint::kSlight_Hinting);
+    REPORTER_ASSERT(r, paint.getHash() != defaultHash);
+    paint.setHinting(SkPaint::kNormal_Hinting);
+    REPORTER_ASSERT(r, paint.getHash() == defaultHash);
+}
+
+#include "SkColorMatrixFilter.h"
+
+DEF_TEST(Paint_nothingToDraw, r) {
+    SkPaint paint;
+
+    REPORTER_ASSERT(r, !paint.nothingToDraw());
+    paint.setAlpha(0);
+    REPORTER_ASSERT(r, paint.nothingToDraw());
+
+    paint.setAlpha(0xFF);
+    paint.setBlendMode(SkBlendMode::kDst);
+    REPORTER_ASSERT(r, paint.nothingToDraw());
+
+    paint.setAlpha(0);
+    paint.setBlendMode(SkBlendMode::kSrcOver);
+
+    SkColorMatrix cm;
+    cm.setIdentity();   // does not change alpha
+    paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(cm.fMat));
+    REPORTER_ASSERT(r, paint.nothingToDraw());
+
+    cm.postTranslate(0, 0, 0, 1);    // wacks alpha
+    paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(cm.fMat));
+    REPORTER_ASSERT(r, !paint.nothingToDraw());
 }
diff --git a/src/third_party/skia/tests/ParametricStageTest.cpp b/src/third_party/skia/tests/ParametricStageTest.cpp
new file mode 100644
index 0000000..5b34e16
--- /dev/null
+++ b/src/third_party/skia/tests/ParametricStageTest.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkColorSpace.h"
+#include "SkRasterPipeline.h"
+#include "Test.h"
+
+static void check_error(skiatest::Reporter* r, float limit, SkColorSpaceTransferFn fn) {
+    float in[256], out[256];
+    for (int i = 0; i < 256; i++) {
+        in [i] = i / 255.0f;
+        out[i] = 0.0f;  // Not likely important.  Just being tidy.
+    }
+
+    const float* ip = in;
+    float*       op = out;
+
+    SkRasterPipeline_<256> p;
+    p.append(SkRasterPipeline::load_f32, &ip);
+    p.append(SkRasterPipeline::parametric_r, &fn);
+    p.append(SkRasterPipeline::parametric_g, &fn);
+    p.append(SkRasterPipeline::parametric_b, &fn);
+    p.append(SkRasterPipeline::parametric_a, &fn);
+    p.append(SkRasterPipeline::store_f32, &op);
+
+    p.run(0,0, 256/4);
+
+
+    for (int i = 0; i < 256; i++) {
+        float want = (in[i] <= fn.fD) ? fn.fC * in[i] + fn.fF
+                                      : powf(in[i] * fn.fA + fn.fB, fn.fG) + fn.fE;
+        float err = fabsf(out[i] - want);
+        if (err > limit) {
+            ERRORF(r, "At %d, error was %g (got %g, want %g)", i, err, out[i], want);
+        }
+    }
+}
+
+static void check_error(skiatest::Reporter* r, float limit, float gamma) {
+    SkColorSpaceTransferFn fn = {0,0,0,0,0,0,0};
+    fn.fG = gamma;
+    fn.fA = 1;
+    check_error(r, limit, fn);
+}
+
+DEF_TEST(Parametric_sRGB, r) {
+    // Test our good buddy the sRGB transfer function in resplendent 7-parameter glory.
+    check_error(r, 1/510.0f, {
+        2.4f,
+        1.0f / 1.055f,
+        0.055f / 1.055f,
+        1.0f / 12.92f,
+        0.04045f,
+        0.0f,
+        0.0f,
+    });
+}
+
+// A nice little spread of simple gammas.
+DEF_TEST(Parametric_1dot0, r) { check_error(r, 1/510.0f, 1.0f); }
+
+DEF_TEST(Parametric_1dot2, r) { check_error(r, 1/510.0f, 1.2f); }
+DEF_TEST(Parametric_1dot4, r) { check_error(r, 1/510.0f, 1.4f); }
+DEF_TEST(Parametric_1dot8, r) { check_error(r, 1/510.0f, 1.8f); }
+DEF_TEST(Parametric_2dot0, r) { check_error(r, 1/510.0f, 2.0f); }
+DEF_TEST(Parametric_2dot2, r) { check_error(r, 1/510.0f, 2.2f); }
+DEF_TEST(Parametric_2dot4, r) { check_error(r, 1/510.0f, 2.4f); }
+
+DEF_TEST(Parametric_inv_1dot2, r) { check_error(r, 1/510.0f, 1/1.2f); }
+DEF_TEST(Parametric_inv_1dot4, r) { check_error(r, 1/510.0f, 1/1.4f); }
+DEF_TEST(Parametric_inv_1dot8, r) { check_error(r, 1/510.0f, 1/1.8f); }
+DEF_TEST(Parametric_inv_2dot0, r) { check_error(r, 1/510.0f, 1/2.0f); }
+DEF_TEST(Parametric_inv_2dot2, r) { check_error(r, 1/510.0f, 1/2.2f); }
+DEF_TEST(Parametric_inv_2dot4, r) { check_error(r, 1/510.0f, 1/2.4f); }
diff --git a/src/third_party/skia/tests/ParsePathTest.cpp b/src/third_party/skia/tests/ParsePathTest.cpp
index ff43d14..408f780 100644
--- a/src/third_party/skia/tests/ParsePathTest.cpp
+++ b/src/third_party/skia/tests/ParsePathTest.cpp
@@ -31,6 +31,9 @@
     const char* fStr;
     const SkRect fBounds;
 } gRec[] = {
+    { "M1,1 l-2.58-2.828-3.82-0.113, 1.9-3.3223-1.08-3.6702, 3.75,0.7744,3.16-2.1551,"
+       "0.42,3.8008,3.02,2.3384-3.48,1.574-1.29,3.601z",
+        { -5.39999962f, -10.3142f, 5.77000046f, 1.f } },
     { "", { 0, 0, 0, 0 } },
     { "M0,0L10,10", { 0, 0, SkIntToScalar(10), SkIntToScalar(10) } },
     { "M-5.5,-0.5 Q 0 0 6,6.50",
@@ -60,3 +63,68 @@
     p.addRoundRect(r, 4, 4.5f);
     test_to_from(reporter, p);
 }
+
+DEF_TEST(ParsePath_invalid, r) {
+    SkPath path;
+    // This is an invalid SVG string, but the test verifies that we do not
+    // crash.
+    bool success = SkParsePath::FromSVGString("M 5", &path);
+    REPORTER_ASSERT(r, !success);
+}
+
+#include "random_parse_path.h"
+#include "SkRandom.h"
+
+DEF_TEST(ParsePathRandom, r) {
+    SkRandom rand;
+    for (int index = 0; index < 1000; ++index) {
+        SkPath path, path2;
+        SkString spec;
+        uint32_t count = rand.nextRangeU(0, 10);
+        for (uint32_t i = 0; i < count; ++i) {
+            spec.append(MakeRandomParsePathPiece(&rand));
+        }
+        bool success = SkParsePath::FromSVGString(spec.c_str(), &path);
+        REPORTER_ASSERT(r, success);
+    }
+}
+
+DEF_TEST(ParsePathOptionalCommand, r) {
+    struct {
+        const char* fStr;
+        int         fVerbs;
+        int         fPoints;
+    } gTests[] = {
+        { "", 0, 0 },
+
+        { "H100 200 ", 3, 3 },
+        { "H-100-200", 3, 3 },
+        { "H+100+200", 3, 3 },
+        { "H.10.20"  , 3, 3 },
+        { "H-.10-.20", 3, 3 },
+        { "H+.10+.20", 3, 3 },
+
+        { "L100 100 200 200" , 3, 3 },
+        { "L-100-100-200-200", 3, 3 },
+        { "L+100+100+200+200", 3, 3 },
+        { "L.10.10.20.20"    , 3, 3 },
+        { "L-.10-.10-.20-.20", 3, 3 },
+        { "L+.10+.10+.20+.20", 3, 3 },
+
+        { "C100 100 200 200 300 300 400 400 500 500 600 600" , 3, 7 },
+        { "C100-100-200-200-300-300-400-400-500-500-600-600" , 3, 7 },
+        { "C100+100+200+200+300+300+400+400+500+500+600+600" , 3, 7 },
+        { "C.10.10.20.20.30.30.40.40.50.50.60.60"            , 3, 7 },
+        { "C-.10-.10-.20-.20-.30-.30-.40-.40-.50-.50-.60-.60", 3, 7 },
+        { "C+.10+.10+.20+.20+.30+.30+.40+.40+.50+.50+.60+.60", 3, 7 },
+
+        { "c-1.49.71-2.12 2.5-1.4 4 .71 1.49 2.5 2.12 4 1.4z", 4, 7 },
+    };
+
+    SkPath path;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
+        REPORTER_ASSERT(r, SkParsePath::FromSVGString(gTests[i].fStr, &path));
+        REPORTER_ASSERT(r, path.countVerbs() == gTests[i].fVerbs);
+        REPORTER_ASSERT(r, path.countPoints() == gTests[i].fPoints);
+    }
+}
diff --git a/src/third_party/skia/tests/PathCoverageTest.cpp b/src/third_party/skia/tests/PathCoverageTest.cpp
index 0e4a153..2551563 100644
--- a/src/third_party/skia/tests/PathCoverageTest.cpp
+++ b/src/third_party/skia/tests/PathCoverageTest.cpp
@@ -5,7 +5,7 @@
  * found in the LICENSE file.
  */
 
-#include "SkMath.h"
+#include "SkMathPriv.h"
 #include "SkPoint.h"
 #include "SkScalar.h"
 #include "Test.h"
@@ -60,7 +60,7 @@
     if (d < tol) {
        return 1;
     } else {
-       int temp = SkScalarCeilToInt(SkScalarSqrt(SkScalarDiv(d, tol)));
+       int temp = SkScalarCeilToInt(SkScalarSqrt(d / tol));
        uint32_t count = SkMin32(SkNextPow2(temp), MAX_POINTS_PER_CURVE);
        return count;
     }
diff --git a/src/third_party/skia/tests/PathMeasureTest.cpp b/src/third_party/skia/tests/PathMeasureTest.cpp
index 578f4eb..df66578 100644
--- a/src/third_party/skia/tests/PathMeasureTest.cpp
+++ b/src/third_party/skia/tests/PathMeasureTest.cpp
@@ -201,3 +201,19 @@
     test_small_segment2();
     test_small_segment3();
 }
+
+DEF_TEST(PathMeasureConic, reporter) {
+    SkPoint stdP, hiP, pts[] = {{0,0}, {100,0}, {100,0}};
+    SkPath p;
+    p.moveTo(0, 0);
+    p.conicTo(pts[1], pts[2], 1);
+    SkPathMeasure stdm(p, false);
+    REPORTER_ASSERT(reporter, stdm.getPosTan(20, &stdP, nullptr));
+    p.reset();
+    p.moveTo(0, 0);
+    p.conicTo(pts[1], pts[2], 10);
+    stdm.setPath(&p, false);
+    REPORTER_ASSERT(reporter, stdm.getPosTan(20, &hiP, nullptr));
+    REPORTER_ASSERT(reporter, 19.5f < stdP.fX && stdP.fX < 20.5f);
+    REPORTER_ASSERT(reporter, 19.5f < hiP.fX && hiP.fX < 20.5f);
+}
diff --git a/src/third_party/skia/tests/PathOpsAngleIdeas.cpp b/src/third_party/skia/tests/PathOpsAngleIdeas.cpp
old mode 100755
new mode 100644
index 901cab2..c50260f
--- a/src/third_party/skia/tests/PathOpsAngleIdeas.cpp
+++ b/src/third_party/skia/tests/PathOpsAngleIdeas.cpp
@@ -6,8 +6,8 @@
  */
 #include "PathOpsTestCommon.h"
 #include "SkIntersections.h"
+#include "SkOpContour.h"
 #include "SkOpSegment.h"
-#include "SkPathOpsTriangle.h"
 #include "SkRandom.h"
 #include "SkTArray.h"
 #include "SkTSort.h"
@@ -18,12 +18,12 @@
 
 class PathOpsAngleTester {
 public:
-    static int ConvexHullOverlaps(const SkOpAngle& lh, const SkOpAngle& rh) {
-        return lh.convexHullOverlaps(rh);
+    static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) {
+        return lh.convexHullOverlaps(&rh);
     }
 
-    static int EndsIntersect(const SkOpAngle& lh, const SkOpAngle& rh) {
-        return lh.endsIntersect(rh);
+    static int EndsIntersect(SkOpAngle& lh, SkOpAngle& rh) {
+        return lh.endsIntersect(&rh);
     }
 };
 
@@ -77,7 +77,7 @@
     double s = r * SK_ScalarTanPIOver8;
     double m = r * SK_ScalarRoot2Over2;
     // construct circle from quads
-    const SkDQuad circle[8] = {{{{ r,  0}, { r, -s}, { m, -m}}},
+    const QuadPts circle[8] = {{{{ r,  0}, { r, -s}, { m, -m}}},
                                 {{{ m, -m}, { s, -r}, { 0, -r}}},
                                 {{{ 0, -r}, {-s, -r}, {-m, -m}}},
                                 {{{-m, -m}, {-r, -s}, {-r,  0}}},
@@ -86,7 +86,9 @@
                                 {{{ 0,  r}, { s,  r}, { m,  m}}},
                                 {{{ m,  m}, { r,  s}, { r,  0}}}};
     for (int octant = 0; octant < 8; ++octant) {
-        double t = testArc(reporter, quad, circle[octant], octant);
+        SkDQuad cQuad;
+        cQuad.debugSet(circle[octant].fPts);
+        double t = testArc(reporter, quad, cQuad, octant);
         if (t < 0) {
             continue;
         }
@@ -332,6 +334,9 @@
             rStep /= 2;
         } while (rStep > FLT_EPSILON);
         if (bestCCW < 0) {
+            if (bestR >= maxRadius) {
+                SkDebugf("");
+            }
             REPORTER_ASSERT(reporter, bestR < maxRadius);
             return false;
         }
@@ -406,28 +411,28 @@
     return ccw == upperRange.ccw;
 }
 
-class PathOpsSegmentTester {
-public:
-    static void ConstructQuad(SkOpSegment* segment, SkPoint shortQuad[3]) {
-        segment->debugConstructQuad(shortQuad);
-    }
-};
-
-static void makeSegment(const SkDQuad& quad, SkPoint shortQuad[3], SkOpSegment* result) {
+static void makeSegment(SkOpContour* contour, const SkDQuad& quad, SkPoint shortQuad[3]) {
     shortQuad[0] = quad[0].asSkPoint();
     shortQuad[1] = quad[1].asSkPoint();
     shortQuad[2] = quad[2].asSkPoint();
-    PathOpsSegmentTester::ConstructQuad(result, shortQuad);
+    contour->addQuad(shortQuad);
 }
 
 static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2,
-        int testNo) {
+        int testNo, SkArenaAlloc* allocator) {
     SkPoint shortQuads[2][3];
-    SkOpSegment seg[2];
-    makeSegment(quad1, shortQuads[0], &seg[0]);
-    makeSegment(quad2, shortQuads[1], &seg[1]);
-    int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg[0].debugLastAngle(),
-            *seg[1].debugLastAngle());
+
+    SkOpContourHead contour;
+    SkOpGlobalState state(&contour, allocator  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
+    contour.init(&state, false, false);
+    makeSegment(&contour, quad1, shortQuads[0]);
+    makeSegment(&contour, quad1, shortQuads[1]);
+    SkOpSegment* seg1 = contour.first();
+    seg1->debugAddAngle(0, 1);
+    SkOpSegment* seg2 = seg1->next();
+    seg2->debugAddAngle(0, 1);
+    int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg1->debugLastAngle(),
+            *seg2->debugLastAngle());
     const SkDPoint& origin = quad1[0];
     REPORTER_ASSERT(reporter, origin == quad2[0]);
     double a1s = atan2(origin.fY - quad1[1].fY, quad1[1].fX - origin.fX);
@@ -545,25 +550,30 @@
     }
     if (overlap < 0) {
         SkDEBUGCODE(int realEnds =)
-                PathOpsAngleTester::EndsIntersect(*seg[0].debugLastAngle(),
-                *seg[1].debugLastAngle());
+                PathOpsAngleTester::EndsIntersect(*seg1->debugLastAngle(),
+                *seg2->debugLastAngle());
         SkASSERT(realEnds == (firstInside ? 1 : 0));
     }
     bruteForce(reporter, quad1, quad2, firstInside);
 }
 
 DEF_TEST(PathOpsAngleOverlapHullsOne, reporter) {
+    SkSTArenaAlloc<4096> allocator;
 //    gPathOpsAngleIdeasVerbose = true;
-    const SkDQuad quads[] = {
+    const QuadPts quads[] = {
 {{{939.4808349609375, 914.355224609375}, {-357.7921142578125, 590.842529296875}, {736.8936767578125, -350.717529296875}}},
 {{{939.4808349609375, 914.355224609375}, {-182.85418701171875, 634.4552001953125}, {-509.62615966796875, 576.1182861328125}}}
     };
     for (int index = 0; index < (int) SK_ARRAY_COUNT(quads); index += 2) {
-        testQuadAngles(reporter, quads[index], quads[index + 1], 0);
+        SkDQuad quad0, quad1;
+        quad0.debugSet(quads[index].fPts);
+        quad1.debugSet(quads[index + 1].fPts);
+        testQuadAngles(reporter, quad0, quad1, 0, &allocator);
     }
 }
 
 DEF_TEST(PathOpsAngleOverlapHulls, reporter) {
+    SkSTArenaAlloc<4096> allocator;
     if (!gPathOpsAngleIdeasVerbose) {  // takes a while to run -- so exclude it by default
         return;
     }
@@ -571,23 +581,26 @@
     for (int index = 0; index < 100000; ++index) {
         if (index % 1000 == 999) SkDebugf(".");
         SkDPoint origin = {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)};
-        SkDQuad quad1 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
+        QuadPts quad1 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
             {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}}};
-        if (quad1[0] == quad1[2]) {
+        if (quad1.fPts[0] == quad1.fPts[2]) {
             continue;
         }
-        SkDQuad quad2 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
+        QuadPts quad2 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
             {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}}};
-        if (quad2[0] == quad2[2]) {
+        if (quad2.fPts[0] == quad2.fPts[2]) {
             continue;
         }
         SkIntersections i;
-        i.intersect(quad1, quad2);
+        SkDQuad q1, q2;
+        q1.debugSet(quad1.fPts);
+        q2.debugSet(quad2.fPts);
+        i.intersect(q1, q2);
         REPORTER_ASSERT(reporter, i.used() >= 1);
         if (i.used() > 1) {
             continue;
         }
-        testQuadAngles(reporter, quad1, quad2, index);
+        testQuadAngles(reporter, q1, q2, index, &allocator);
     }
 }
 
@@ -601,29 +614,32 @@
     SkDEBUGCODE(int smallIndex);
     for (int index = 0; index < 100000; ++index) {
         SkDPoint origin = {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)};
-        SkDQuad quad1 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
+        QuadPts quad1 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
             {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}}};
-        if (quad1[0] == quad1[2]) {
+        if (quad1.fPts[0] == quad1.fPts[2]) {
             continue;
         }
-        SkDQuad quad2 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
+        QuadPts quad2 = {{origin, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
             {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}}};
-        if (quad2[0] == quad2[2]) {
+        if (quad2.fPts[0] == quad2.fPts[2]) {
             continue;
         }
+        SkDQuad q1, q2;
+        q1.debugSet(quad1.fPts);
+        q2.debugSet(quad2.fPts);
         SkIntersections i;
-        i.intersect(quad1, quad2);
+        i.intersect(q1, q2);
         REPORTER_ASSERT(reporter, i.used() >= 1);
         if (i.used() > 1) {
             continue;
         }
         TRange lowerRange, upperRange;
-        bool result = bruteMinT(reporter, quad1, quad2, &lowerRange, &upperRange);
+        bool result = bruteMinT(reporter, q1, q2, &lowerRange, &upperRange);
         REPORTER_ASSERT(reporter, result);
         double min = SkTMin(upperRange.t1, upperRange.t2);
         if (smaller > min) {
-            small[0] = quad1;
-            small[1] = quad2;
+            small[0] = q1;
+            small[1] = q2;
             SkDEBUGCODE(smallIndex = index);
             smaller = min;
         }
@@ -635,7 +651,7 @@
 
 DEF_TEST(PathOpsAngleBruteTOne, reporter) {
 //    gPathOpsAngleIdeasVerbose = true;
-    const SkDQuad quads[] = {
+    const QuadPts qPts[] = {
 {{{-770.8492431640625, 948.2369384765625}, {-853.37066650390625, 972.0301513671875}, {-200.62042236328125, -26.7174072265625}}},
 {{{-770.8492431640625, 948.2369384765625}, {513.602783203125, 578.8681640625}, {960.641357421875, -813.69757080078125}}},
 {{{563.8267822265625, -107.4566650390625}, {-44.67724609375, -136.57452392578125}, {492.3856201171875, -268.79644775390625}}},
@@ -644,6 +660,10 @@
 {{{598.857421875, 846.345458984375}, {715.7142333984375, 955.3599853515625}, {-919.9478759765625, 691.611328125}}},
     };
     TRange lowerRange, upperRange;
+    SkDQuad quads[SK_ARRAY_COUNT(qPts)];
+    for (int index = 0; index < (int) SK_ARRAY_COUNT(qPts); ++index) {
+        quads[index].debugSet(qPts[index].fPts);
+    }
     bruteMinT(reporter, quads[0], quads[1], &lowerRange, &upperRange);
     bruteMinT(reporter, quads[2], quads[3], &lowerRange, &upperRange);
     bruteMinT(reporter, quads[4], quads[5], &lowerRange, &upperRange);
@@ -664,7 +684,7 @@
 the largest length to determine how stable the curve is vis-a-vis the initial tangent.
 */
 
-static const SkDQuad extremeTests[][2] = {
+static const QuadPts extremeTests[][2] = {
     {
         {{{-708.0077926931004,-154.61669472244046},
             {-707.9234268635319,-154.30459999551294},
@@ -791,8 +811,11 @@
     }
     double maxR = SK_ScalarMax;
     for (int index = 0; index < (int) SK_ARRAY_COUNT(extremeTests); ++index) {
-        const SkDQuad& quad1 = extremeTests[index][0];
-        const SkDQuad& quad2 = extremeTests[index][1];
+        const QuadPts& qu1 = extremeTests[index][0];
+        const QuadPts& qu2 = extremeTests[index][1];
+        SkDQuad quad1, quad2;
+        quad1.debugSet(qu1.fPts);
+        quad2.debugSet(qu2.fPts);
         if (gPathOpsAngleIdeasVerbose) {
             SkDebugf("%s %d\n", __FUNCTION__, index);
         }
diff --git a/src/third_party/skia/tests/PathOpsAngleTest.cpp b/src/third_party/skia/tests/PathOpsAngleTest.cpp
index faf6158..7f4c04a 100644
--- a/src/third_party/skia/tests/PathOpsAngleTest.cpp
+++ b/src/third_party/skia/tests/PathOpsAngleTest.cpp
@@ -6,10 +6,9 @@
  */
 #include "PathOpsTestCommon.h"
 #include "SkIntersections.h"
+#include "SkOpContour.h"
 #include "SkOpSegment.h"
-#include "SkPathOpsTriangle.h"
 #include "SkRandom.h"
-#include "SkTArray.h"
 #include "SkTSort.h"
 #include "Test.h"
 
@@ -52,7 +51,7 @@
                     float p2 = SkDoubleToScalar(line[1].fY * test.fX);
                     int p1Bits = SkFloatAs2sCompliment(p1);
                     int p2Bits = SkFloatAs2sCompliment(p2);
-                    int epsilon = abs(p1Bits - p2Bits);
+                    int epsilon = SkTAbs(p1Bits - p2Bits);
                     if (maxEpsilon < epsilon) {
                         SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g pt={%1.7g, %1.7g}"
                             " epsilon=%d\n",
@@ -82,7 +81,9 @@
         SkDPoint qPt2 = line.ptAtT(t3);
         qPt.fX += qPt2.fY;
         qPt.fY -= qPt2.fX;
-        SkDQuad quad = {{line[0], dPt, qPt}};
+        QuadPts q = {{line[0], dPt, qPt}};
+        SkDQuad quad;
+        quad.debugSet(q.fPts);
         // binary search for maximum movement of quad[1] towards test that still has 1 intersection
         double moveT = 0.5f;
         double deltaT = moveT / 2;
@@ -105,7 +106,7 @@
         float p2 = SkDoubleToScalar(line[1].fY * last.fX);
         int p1Bits = SkFloatAs2sCompliment(p1);
         int p2Bits = SkFloatAs2sCompliment(p2);
-        int epsilon = abs(p1Bits - p2Bits);
+        int epsilon = SkTAbs(p1Bits - p2Bits);
         if (maxEpsilon < epsilon) {
             SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g/%1.7g/%1.7g moveT=%1.7g"
                     " pt={%1.7g, %1.7g} epsilon=%d\n",
@@ -191,20 +192,24 @@
 
 class PathOpsAngleTester {
 public:
-    static int After(const SkOpAngle& lh, const SkOpAngle& rh) {
+    static int After(SkOpAngle& lh, SkOpAngle& rh) {
         return lh.after(&rh);
     }
 
-    static int ConvexHullOverlaps(const SkOpAngle& lh, const SkOpAngle& rh) {
-        return lh.convexHullOverlaps(rh);
+    static int AllOnOneSide(SkOpAngle& lh, SkOpAngle& rh) {
+        return lh.allOnOneSide(&rh);
     }
 
-    static int Orderable(const SkOpAngle& lh, const SkOpAngle& rh) {
-        return lh.orderable(rh);
+    static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) {
+        return lh.convexHullOverlaps(&rh);
     }
 
-    static int EndsIntersect(const SkOpAngle& lh, const SkOpAngle& rh) {
-        return lh.endsIntersect(rh);
+    static int Orderable(SkOpAngle& lh, SkOpAngle& rh) {
+        return lh.orderable(&rh);
+    }
+
+    static int EndsIntersect(SkOpAngle& lh, SkOpAngle& rh) {
+        return lh.endsIntersect(&rh);
     }
 
     static void SetNext(SkOpAngle& lh, SkOpAngle& rh) {
@@ -214,25 +219,13 @@
 
 class PathOpsSegmentTester {
 public:
-    static void ConstructCubic(SkOpSegment* segment, SkPoint shortCubic[4]) {
-        segment->debugConstructCubic(shortCubic);
-    }
-
-    static void ConstructLine(SkOpSegment* segment, SkPoint shortLine[2]) {
-        segment->debugConstructLine(shortLine);
-    }
-
-    static void ConstructQuad(SkOpSegment* segment, SkPoint shortQuad[3]) {
-        segment->debugConstructQuad(shortQuad);
-    }
-
     static void DebugReset(SkOpSegment* segment) {
         segment->debugReset();
     }
 };
 
 struct CircleData {
-    const SkDCubic fPts;
+    const CubicPts fPts;
     const int fPtCount;
     SkPoint fShortPts[4];
 };
@@ -246,7 +239,10 @@
 static const int circleDataSetSize = (int) SK_ARRAY_COUNT(circleDataSet);
 
 DEF_TEST(PathOpsAngleCircle, reporter) {
-    SkOpSegment segment[2];
+    SkSTArenaAlloc<4096> allocator;
+    SkOpContourHead contour;
+    SkOpGlobalState state(&contour, &allocator  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
+    contour.init(&state, false, false);
     for (int index = 0; index < circleDataSetSize; ++index) {
         CircleData& data = circleDataSet[index];
         for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
@@ -254,21 +250,25 @@
         }
         switch (data.fPtCount) {
             case 2:
-                PathOpsSegmentTester::ConstructLine(&segment[index], data.fShortPts);
+                contour.addLine(data.fShortPts);
                 break;
             case 3:
-                PathOpsSegmentTester::ConstructQuad(&segment[index], data.fShortPts);
+                contour.addQuad(data.fShortPts);
                 break;
             case 4:
-                PathOpsSegmentTester::ConstructCubic(&segment[index], data.fShortPts);
+                contour.addCubic(data.fShortPts);
                 break;
         }
     }
-    PathOpsAngleTester::Orderable(*segment[0].debugLastAngle(), *segment[1].debugLastAngle());
+    SkOpSegment* first = contour.first();
+    first->debugAddAngle(0, 1);
+    SkOpSegment* next = first->next();
+    next->debugAddAngle(0, 1);
+    PathOpsAngleTester::Orderable(*first->debugLastAngle(), *next->debugLastAngle());
 }
 
 struct IntersectData {
-    const SkDCubic fPts;
+    const CubicPts fPts;
     const int fPtCount;
     double fTStart;
     double fTEnd;
@@ -379,11 +379,39 @@
     { {{{5.000,4.000}, {2.000,3.000}}}, 2, 0.5, 0, {} }, // pathops_visualizer.htm:7377
 }; //
 
+// from skpi_gino_com_16
+static IntersectData intersectDataSet17[] = {
+    { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
+        , 3, 0.74590454, 0.547660352, {} },
+    { /*seg=8*/ {{{185, 734}, {252.93103f, 734}, {308, 789.06897f}, {308, 857}}}
+        , 4, 0.12052623, 0, {} },
+    { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
+        , 3, 0.74590454, 1, {} },
+};
+
+static IntersectData intersectDataSet18[] = {
+    { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
+        , 3, 0.74590454, 1, {} },
+    { /*seg=8*/ {{{185, 734}, {252.93103f, 734}, {308, 789.06897f}, {308, 857}}}
+        , 4, 0.12052623, 0.217351928, {} },
+    { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
+        , 3, 0.74590454, 0.547660352, {} },
+};
+
+static IntersectData intersectDataSet19[] = {
+    { /*seg=1*/ {{{0, 1}, {3, 5}, {2, 1}, {3, 1}}}
+        , 4, 0.135148995, 0.134791946, {} },
+    { /*seg=3*/ {{{1, 2}, {1, 2.15061641f}, {1, 2.21049166f}, {1.01366711f, 2.21379328f}}}
+        , 4, 0.956740456, 0.894913214, {} },
+    { /*seg=1*/ {{{0, 1}, {3, 5}, {2, 1}, {3, 1}}}
+        , 4, 0.135148995, 0.551812363, {} },
+};
+
 #define I(x) intersectDataSet##x
 
 static IntersectData* intersectDataSets[] = {
     I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10),
-    I(11), I(12), I(13), I(14), I(15), I(16),
+    I(11), I(12), I(13), I(14), I(15), I(16), I(17), I(18), I(19),
 };
 
 #undef I
@@ -391,56 +419,55 @@
 
 static const int intersectDataSetSizes[] = {
     I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10),
-    I(11), I(12), I(13), I(14), I(15), I(16),
+    I(11), I(12), I(13), I(14), I(15), I(16), I(17), I(18), I(19),
 };
 
 #undef I
 
 static const int intersectDataSetsSize = (int) SK_ARRAY_COUNT(intersectDataSetSizes);
 
+struct FourPoints {
+    SkPoint pts[4];
+};
+
 DEF_TEST(PathOpsAngleAfter, reporter) {
+    SkSTArenaAlloc<4096> allocator;
+    SkOpContourHead contour;
+    SkOpGlobalState state(&contour, &allocator  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
+    contour.init(&state, false, false);
     for (int index = intersectDataSetsSize - 1; index >= 0; --index) {
         IntersectData* dataArray = intersectDataSets[index];
         const int dataSize = intersectDataSetSizes[index];
-        SkOpSegment segment[3];
         for (int index2 = 0; index2 < dataSize - 2; ++index2) {
-            for (int temp = 0; temp < (int) SK_ARRAY_COUNT(segment); ++temp) {
-                PathOpsSegmentTester::DebugReset(&segment[temp]);
-            }
-            for (int index3 = 0; index3 < (int) SK_ARRAY_COUNT(segment); ++index3) {
+            allocator.reset();
+            contour.reset();
+            for (int index3 = 0; index3 < 3; ++index3) {
                 IntersectData& data = dataArray[index2 + index3];
-                SkPoint temp[4];
+                SkPoint* temp = (SkPoint*) allocator.make<FourPoints>();
                 for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
                     temp[idx2] = data.fPts.fPts[idx2].asSkPoint();
                 }
                 switch (data.fPtCount) {
                     case 2: {
-                        SkDLine seg = SkDLine::SubDivide(temp, data.fTStart,
-                                data.fTStart < data.fTEnd ? 1 : 0);
-                        data.fShortPts[0] = seg[0].asSkPoint();
-                        data.fShortPts[1] = seg[1].asSkPoint();
-                        PathOpsSegmentTester::ConstructLine(&segment[index3], data.fShortPts);
+                        contour.addLine(temp);
                         } break;
                     case 3: {
-                        SkDQuad seg = SkDQuad::SubDivide(temp, data.fTStart, data.fTEnd);
-                        data.fShortPts[0] = seg[0].asSkPoint();
-                        data.fShortPts[1] = seg[1].asSkPoint();
-                        data.fShortPts[2] = seg[2].asSkPoint();
-                        PathOpsSegmentTester::ConstructQuad(&segment[index3], data.fShortPts);
+                        contour.addQuad(temp);
                         } break;
                     case 4: {
-                        SkDCubic seg = SkDCubic::SubDivide(temp, data.fTStart, data.fTEnd);
-                        data.fShortPts[0] = seg[0].asSkPoint();
-                        data.fShortPts[1] = seg[1].asSkPoint();
-                        data.fShortPts[2] = seg[2].asSkPoint();
-                        data.fShortPts[3] = seg[3].asSkPoint();
-                        PathOpsSegmentTester::ConstructCubic(&segment[index3], data.fShortPts);
+                        contour.addCubic(temp);
                         } break;
                 }
             }
-            SkOpAngle& angle1 = *const_cast<SkOpAngle*>(segment[0].debugLastAngle());
-            SkOpAngle& angle2 = *const_cast<SkOpAngle*>(segment[1].debugLastAngle());
-            SkOpAngle& angle3 = *const_cast<SkOpAngle*>(segment[2].debugLastAngle());
+            SkOpSegment* seg1 = contour.first();
+            seg1->debugAddAngle(dataArray[index2 + 0].fTStart, dataArray[index2 + 0].fTEnd);
+            SkOpSegment* seg2 = seg1->next();
+            seg2->debugAddAngle(dataArray[index2 + 1].fTStart, dataArray[index2 + 1].fTEnd);
+            SkOpSegment* seg3 = seg2->next();
+            seg3->debugAddAngle(dataArray[index2 + 2].fTStart, dataArray[index2 + 2].fTEnd);
+            SkOpAngle& angle1 = *seg1->debugLastAngle();
+            SkOpAngle& angle2 = *seg2->debugLastAngle();
+            SkOpAngle& angle3 = *seg3->debugLastAngle();
             PathOpsAngleTester::SetNext(angle1, angle3);
        // These data sets are seeded when the set itself fails, so likely the dataset does not
        // match the expected result. The tests above return 1 when first added, but
@@ -451,35 +478,52 @@
     }
 }
 
-void SkOpSegment::debugConstruct() {
-    addStartSpan(1);
-    addEndSpan(1);
-    debugAddAngle(0, 1);
+void SkOpSegment::debugAddAngle(double startT, double endT) {
+    SkOpPtT* startPtT = startT == 0 ? fHead.ptT() : startT == 1 ? fTail.ptT()
+            : this->addT(startT);
+    SkOpPtT* endPtT = endT == 0 ? fHead.ptT() : endT == 1 ? fTail.ptT()
+            : this->addT(endT);
+    SkOpAngle* angle = this->globalState()->allocator()->make<SkOpAngle>();
+    SkOpSpanBase* startSpan = &fHead;
+    while (startSpan->ptT() != startPtT) {
+        startSpan = startSpan->upCast()->next();
+    }
+    SkOpSpanBase* endSpan = &fHead;
+    while (endSpan->ptT() != endPtT) {
+        endSpan = endSpan->upCast()->next();
+    }
+    angle->set(startSpan, endSpan);
+    if (startT < endT) {
+        startSpan->upCast()->setToAngle(angle);
+        endSpan->setFromAngle(angle);
+    } else {
+        endSpan->upCast()->setToAngle(angle);
+        startSpan->setFromAngle(angle);
+    }
 }
 
-void SkOpSegment::debugAddAngle(int start, int end) {
-    SkASSERT(start != end);
-    SkOpAngle& angle = fAngles.push_back();
-    angle.set(this, start, end);
-}
-
-void SkOpSegment::debugConstructCubic(SkPoint shortQuad[4]) {
-    addCubic(shortQuad, false, false);
-    addT(NULL, shortQuad[0], 0);
-    addT(NULL, shortQuad[3], 1);
-    debugConstruct();
-}
-
-void SkOpSegment::debugConstructLine(SkPoint shortQuad[2]) {
-    addLine(shortQuad, false, false);
-    addT(NULL, shortQuad[0], 0);
-    addT(NULL, shortQuad[1], 1);
-    debugConstruct();
-}
-
-void SkOpSegment::debugConstructQuad(SkPoint shortQuad[3]) {
-    addQuad(shortQuad, false, false);
-    addT(NULL, shortQuad[0], 0);
-    addT(NULL, shortQuad[2], 1);
-    debugConstruct();
+DEF_TEST(PathOpsAngleAllOnOneSide, reporter) {
+    SkSTArenaAlloc<4096> allocator;
+    SkOpContourHead contour;
+    SkOpGlobalState state(&contour, &allocator  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
+    contour.init(&state, false, false);
+    SkPoint conicPts[3] = {{494.37100219726562f, 224.66200256347656f},
+        {494.37360910682298f, 224.6729026561527f},
+        {494.37600708007813f, 224.68400573730469f}};
+    SkPoint linePts[2] = {{494.371002f, 224.662003f}, {494.375000f, 224.675995f}};
+    for (int i = 10; i >= 0; --i) {
+        SkPoint modLinePts[2] = { linePts[0], linePts[1] };
+        modLinePts[1].fX += i * .1f;
+        contour.addLine(modLinePts);
+        contour.addQuad(conicPts);
+   //     contour.addConic(conicPts, 0.999935746f, &allocator);
+        SkOpSegment* first = contour.first();
+        first->debugAddAngle(0, 1);
+        SkOpSegment* next = first->next();
+        next->debugAddAngle(0, 1);
+        /* int result = */
+            PathOpsAngleTester::AllOnOneSide(*first->debugLastAngle(), *next->debugLastAngle());
+  //      SkDebugf("i=%d result=%d\n", i , result);
+  //      SkDebugf("");
+    }
 }
diff --git a/src/third_party/skia/tests/PathOpsBattles.cpp b/src/third_party/skia/tests/PathOpsBattles.cpp
index 15fffd5..32ba96c 100644
--- a/src/third_party/skia/tests/PathOpsBattles.cpp
+++ b/src/third_party/skia/tests/PathOpsBattles.cpp
@@ -36,7 +36,7 @@
     path2.cubicTo(-15.5552f, 63.4296f, 12.6591f, 64.0704f, 33.9313f, 49.484f);
     path2.lineTo(46.9383f, 68.4529f);
     path2.close();
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void issue414409b(skiatest::Reporter* reporter, const char* filename) {
@@ -59,8 +59,7 @@
 path2.cubicTo(SkBits2Float(0x42383446), SkBits2Float(0x421ac98f), SkBits2Float(0x4242b98a), SkBits2Float(0x420d5308), SkBits2Float(0x424bbb17), SkBits2Float(0x41fdb8ee));
 path2.lineTo(SkBits2Float(0x428ce9ef), SkBits2Float(0x422f7dc6));
 path2.close();
-// SkOpSegment.cpp:3488: failed assertion "other->fTs[min].fWindSum == oppWinding"
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void issue414409c(skiatest::Reporter* reporter, const char* filename) {
@@ -85,7 +84,7 @@
 path2.lineTo(SkBits2Float(0x3eccef1a), SkBits2Float(0xc2a5ff81));
 path2.close();
 
-testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 // fails to draw correctly
@@ -401,7 +400,7 @@
     SkPath path2(path);
     testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
-// ../../third_party/tcmalloc/chromium/src/free_list.h:118] Memory corruption detected. 
+// ../../third_party/tcmalloc/chromium/src/free_list.h:118] Memory corruption detected.
 
 static void battleOp13(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
@@ -1273,9 +1272,6 @@
 // op end success 1
 
 static void battleOp46(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc26fffff));
@@ -1490,9 +1486,6 @@
 // op end success 1
 
 static void battleOp54(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
@@ -1632,9 +1625,6 @@
 // op end success 1
 
 static void battleOp59(skiatest::Reporter* reporter, const char* filename) {  // hung
-    if (!FLAGS_runFail) {
-        return;
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
@@ -1736,9 +1726,6 @@
 // op end success 1
 
 static void battleOp63(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
@@ -2314,9 +2301,6 @@
 // op end success 1
 
 static void battleOp85(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
@@ -3973,9 +3957,6 @@
 // op end success 1
 
 static void battleOp148(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
@@ -4084,9 +4065,6 @@
 // op end success 1
 
 static void battleOp152(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;  // draws wrong
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
@@ -4229,9 +4207,6 @@
 // op end success 1
 
 static void battleOp157(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;  // draws wrong
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
@@ -4408,9 +4383,6 @@
 // op end success 1
 
 static void battleOp163(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;  // draws wrong
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc2700000));
@@ -4951,9 +4923,6 @@
 // op end success 1
 
 static void battleOp181(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;  // draws wrong
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0xb7060057), SkBits2Float(0xc2700000));
@@ -5014,9 +4983,6 @@
 // op end success 1
 
 static void battleOp183(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;  // draws wrong
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0x36d3ff52), SkBits2Float(0xc2700000));
@@ -5376,9 +5342,6 @@
 // op end success 1
 
 static void battleOp195(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;  // draws wrong
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
@@ -5451,9 +5414,6 @@
 // op end success 1
 
 static void battleOp198(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;  // draws wrong
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0x369bbf59), SkBits2Float(0xc2700000));
@@ -6308,9 +6268,6 @@
 // op end success 1
 
 static void battleOp230(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;  // draws wrong
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
@@ -6505,9 +6462,6 @@
 // op end success 1
 
 static void battleOp237(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;  // draws wrong
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
@@ -7066,9 +7020,6 @@
 // op end success 1
 
 static void battleOp256(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;  // draws wrong
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0xb69400ae), SkBits2Float(0xc2700000));
@@ -7465,9 +7416,6 @@
 // op end success 1
 
 static void battleOp269(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;  // draws wrong
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
@@ -7585,9 +7533,6 @@
 // op end success 1
 
 static void battleOp273(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;  // draws wrong
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
@@ -7891,9 +7836,6 @@
 // op end success 1
 
 static void battleOp283(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;  // draws wrong
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
@@ -10739,7 +10681,7 @@
     testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
-static void (*firstTest)(skiatest::Reporter* , const char* filename) = battleOp6001;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = battleOp183;
 static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static struct TestDesc tests[] = {
@@ -10792,8 +10734,6 @@
     TEST(battleOp43),
     TEST(battleOp44),
     TEST(battleOp45),
-    TEST(battleOp46),  // draws wrong : dropped an outer cubic incorrectly
-    // if assembly rewrite was done, the error would be hidden
     TEST(battleOp47),
     TEST(battleOp48),
     TEST(battleOp49),
@@ -10802,17 +10742,15 @@
     TEST(battleOp51),
     TEST(battleOp52),
     TEST(battleOp53),
-    TEST(battleOp54),  // draws wrong
     TEST(battleOp55),
     TEST(battleOp56),
     TEST(battleOp57),
     TEST(battleOp58),
-    TEST(battleOp59),  // draws wrong
+    TEST(battleOp59),
     TEST(battleOp60),
 
     TEST(battleOp61),
     TEST(battleOp62),
-    TEST(battleOp63),  // draws wrong
     TEST(battleOp64),
     TEST(battleOp65),
     TEST(battleOp66),
@@ -10836,7 +10774,7 @@
     TEST(battleOp82),
     TEST(battleOp83),
     TEST(battleOp84),
-    TEST(battleOp85),  // draws wrong
+    TEST(battleOp85),
     TEST(battleOp86),
     TEST(battleOp87),
     TEST(battleOp88),
@@ -10905,24 +10843,20 @@
     TEST(battleOp145),
     TEST(battleOp146),
     TEST(battleOp147),
-    TEST(battleOp148),  // draws wrong
     TEST(battleOp149),
     TEST(battleOp150),
 
     TEST(battleOp151),
-    TEST(battleOp152),
     TEST(battleOp153),
     TEST(battleOp154),
     TEST(battleOp155),
     TEST(battleOp156),
-    TEST(battleOp157),
     TEST(battleOp158),
     TEST(battleOp159),
     TEST(battleOp160),
 
     TEST(battleOp161),
     TEST(battleOp162),
-    TEST(battleOp163),
     TEST(battleOp164),
     TEST(battleOp165),
     TEST(battleOp166),
@@ -10942,9 +10876,7 @@
     TEST(battleOp179),
     TEST(battleOp180),
 
-    TEST(battleOp181),
     TEST(battleOp182),
-    TEST(battleOp183),
     TEST(battleOp184),
     TEST(battleOp185),
     TEST(battleOp186),
@@ -10957,10 +10889,8 @@
     TEST(battleOp192),
     TEST(battleOp193),
     TEST(battleOp194),
-    TEST(battleOp195),
     TEST(battleOp196),
     TEST(battleOp197),
-    TEST(battleOp198),
     TEST(battleOp199),
     TEST(battleOp200),
 
@@ -10995,7 +10925,6 @@
     TEST(battleOp227),
     TEST(battleOp228),
     TEST(battleOp229),
-    TEST(battleOp230),
 
     TEST(battleOp231),
     TEST(battleOp232),
@@ -11024,7 +10953,6 @@
     TEST(battleOp253),
     TEST(battleOp254),
     TEST(battleOp255),
-    TEST(battleOp256),
     TEST(battleOp257),
     TEST(battleOp258),
     TEST(battleOp259),
@@ -11038,12 +10966,10 @@
     TEST(battleOp266),
     TEST(battleOp267),
     TEST(battleOp268),
-    TEST(battleOp269),
     TEST(battleOp270),
 
     TEST(battleOp271),
     TEST(battleOp272),
-    TEST(battleOp273),
     TEST(battleOp274),
     TEST(battleOp275),
     TEST(battleOp276),
@@ -11054,7 +10980,6 @@
 
     TEST(battleOp281),
     TEST(battleOp282),
-    TEST(battleOp283),
     TEST(battleOp284),
     TEST(battleOp285),
     TEST(battleOp286),
@@ -11168,6 +11093,25 @@
     TEST(issue414409c),
     TEST(issue414409b),
     TEST(issue414409),
+
+    // these draw wrong
+    TEST(battleOp46),  // dropped an outer cubic incorrectly
+                       // if assembly rewrite was done, the error would be hidden
+    TEST(battleOp54),
+    TEST(battleOp63),
+    TEST(battleOp152),
+    TEST(battleOp157),
+    TEST(battleOp163),
+    TEST(battleOp181),
+    TEST(battleOp183),
+    TEST(battleOp195),
+    TEST(battleOp198),
+    TEST(battleOp230),
+    TEST(battleOp256),
+    TEST(battleOp269),
+    TEST(battleOp273),
+    TEST(battleOp148),
+    TEST(battleOp283),
 };
 
 
@@ -11179,5 +11123,5 @@
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
-    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, nullptr, stopTest, runReverse);
 }
diff --git a/src/third_party/skia/tests/PathOpsBoundsTest.cpp b/src/third_party/skia/tests/PathOpsBoundsTest.cpp
index 8683051..573a0ce 100644
--- a/src/third_party/skia/tests/PathOpsBoundsTest.cpp
+++ b/src/third_party/skia/tests/PathOpsBoundsTest.cpp
@@ -6,6 +6,7 @@
  */
 #include "PathOpsTestCommon.h"
 #include "SkPathOpsBounds.h"
+#include "SkPathOpsCurve.h"
 #include "Test.h"
 
 static const SkRect sectTests[][2] = {
@@ -28,24 +29,6 @@
 
 static const size_t noSectTestsCount = SK_ARRAY_COUNT(noSectTests);
 
-static const SkRect reallyEmpty[] = {
-    {0, 0, 0, 0},
-    {1, 1, 1, 0},
-    {1, 1, 0, 1},
-    {1, 1, 0, 0},
-    {1, 2, 3, SK_ScalarNaN},
-};
-
-static const size_t emptyTestsCount = SK_ARRAY_COUNT(reallyEmpty);
-
-static const SkRect notReallyEmpty[] = {
-    {0, 0, 1, 0},
-    {0, 0, 0, 1},
-    {0, 0, 1, 1},
-};
-
-static const size_t notEmptyTestsCount = SK_ARRAY_COUNT(notReallyEmpty);
-
 DEF_TEST(PathOpsBounds, reporter) {
     for (size_t index = 0; index < sectTestsCount; ++index) {
         const SkPathOpsBounds& bounds1 = static_cast<const SkPathOpsBounds&>(sectTests[index][0]);
@@ -74,37 +57,18 @@
     ordinal.set(1, 2, 3, 4);
     bounds.add(ordinal);
     REPORTER_ASSERT(reporter, bounds == expected);
-    SkPoint topLeft = {0, 0};
-    bounds.setPointBounds(topLeft);
-    SkPoint botRight = {3, 4};
+    bounds.setEmpty();
+    SkDPoint botRight = {3, 4};
     bounds.add(botRight);
     REPORTER_ASSERT(reporter, bounds == expected);
-    for (size_t index = 0; index < emptyTestsCount; ++index) {
-        const SkPathOpsBounds& bounds = static_cast<const SkPathOpsBounds&>(reallyEmpty[index]);
-        // SkASSERT(ValidBounds(bounds));  // don't check because test may contain nan
-        bool empty = bounds.isReallyEmpty();
-        REPORTER_ASSERT(reporter, empty);
-    }
-    for (size_t index = 0; index < notEmptyTestsCount; ++index) {
-        const SkPathOpsBounds& bounds = static_cast<const SkPathOpsBounds&>(notReallyEmpty[index]);
-        SkASSERT(ValidBounds(bounds));
-        bool empty = bounds.isReallyEmpty();
-        REPORTER_ASSERT(reporter, !empty);
-    }
     const SkPoint curvePts[] = {{0, 0}, {1, 2}, {3, 4}, {5, 6}};
-    bounds.setLineBounds(curvePts);
-    expected.set(0, 0, 1, 2);
-    REPORTER_ASSERT(reporter, bounds == expected);
-    (bounds.*SetCurveBounds[1])(curvePts);
-    REPORTER_ASSERT(reporter, bounds == expected);
-    bounds.setQuadBounds(curvePts);
+    SkDCurve curve;
+    curve.fQuad.set(curvePts);
+    curve.setQuadBounds(curvePts, 1, 0, 1, &bounds);
     expected.set(0, 0, 3, 4);
     REPORTER_ASSERT(reporter, bounds == expected);
-    (bounds.*SetCurveBounds[2])(curvePts);
-    REPORTER_ASSERT(reporter, bounds == expected);
-    bounds.setCubicBounds(curvePts);
+    curve.fCubic.set(curvePts);
+    curve.setCubicBounds(curvePts, 1, 0, 1, &bounds);
     expected.set(0, 0, 5, 6);
     REPORTER_ASSERT(reporter, bounds == expected);
-    (bounds.*SetCurveBounds[3])(curvePts);
-    REPORTER_ASSERT(reporter, bounds == expected);
 }
diff --git a/src/third_party/skia/tests/PathOpsBuildUseTest.cpp b/src/third_party/skia/tests/PathOpsBuildUseTest.cpp
new file mode 100644
index 0000000..67e4252
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsBuildUseTest.cpp
@@ -0,0 +1,2435 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsExtendedTest.h"
+#include "PathOpsTestCommon.h"
+
+#define TEST(name) { name, #name }
+
+static void build1_1(skiatest::Reporter* reporter, const char* filename) {
+    SkOpBuilder builder;
+    SkPath path;
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x436ae68e), SkBits2Float(0x43adff26));  // 234.901f, 347.993f
+path.quadTo(SkBits2Float(0x436ae68e), SkBits2Float(0x43b32ca2), SkBits2Float(0x4363940a), SkBits2Float(0x43b6d5e4));  // 234.901f, 358.349f, 227.578f, 365.671f
+path.quadTo(SkBits2Float(0x435c4186), SkBits2Float(0x43ba7f26), SkBits2Float(0x4351e68e), SkBits2Float(0x43ba7f26));  // 220.256f, 372.993f, 209.901f, 372.993f
+path.quadTo(SkBits2Float(0x43478b96), SkBits2Float(0x43ba7f26), SkBits2Float(0x43403912), SkBits2Float(0x43b6d5e4));  // 199.545f, 372.993f, 192.223f, 365.671f
+path.quadTo(SkBits2Float(0x4338e68e), SkBits2Float(0x43b32ca2), SkBits2Float(0x4338e68e), SkBits2Float(0x43adff26));  // 184.901f, 358.349f, 184.901f, 347.993f
+path.quadTo(SkBits2Float(0x4338e68e), SkBits2Float(0x43a8d1aa), SkBits2Float(0x43403912), SkBits2Float(0x43a52868));  // 184.901f, 337.638f, 192.223f, 330.316f
+path.quadTo(SkBits2Float(0x43478b96), SkBits2Float(0x43a17f26), SkBits2Float(0x4351e68e), SkBits2Float(0x43a17f26));  // 199.545f, 322.993f, 209.901f, 322.993f
+path.quadTo(SkBits2Float(0x435c4186), SkBits2Float(0x43a17f26), SkBits2Float(0x4363940a), SkBits2Float(0x43a52868));  // 220.256f, 322.993f, 227.578f, 330.316f
+path.quadTo(SkBits2Float(0x436ae68e), SkBits2Float(0x43a8d1aa), SkBits2Float(0x436ae68e), SkBits2Float(0x43adff26));  // 234.901f, 337.638f, 234.901f, 347.993f
+path.close();
+    SkPath path0(path);
+    builder.add(path0, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43ad0aca), SkBits2Float(0x432a0e2c));  // 346.084f, 170.055f
+path.quadTo(SkBits2Float(0x43ad0aca), SkBits2Float(0x43346924), SkBits2Float(0x43a96188), SkBits2Float(0x433bbba8));  // 346.084f, 180.411f, 338.762f, 187.733f
+path.quadTo(SkBits2Float(0x43a5b846), SkBits2Float(0x43430e2c), SkBits2Float(0x43a08aca), SkBits2Float(0x43430e2c));  // 331.44f, 195.055f, 321.084f, 195.055f
+path.quadTo(SkBits2Float(0x439b5d4e), SkBits2Float(0x43430e2c), SkBits2Float(0x4397b40c), SkBits2Float(0x433bbba8));  // 310.729f, 195.055f, 303.407f, 187.733f
+path.quadTo(SkBits2Float(0x43940aca), SkBits2Float(0x43346924), SkBits2Float(0x43940aca), SkBits2Float(0x432a0e2c));  // 296.084f, 180.411f, 296.084f, 170.055f
+path.quadTo(SkBits2Float(0x43940aca), SkBits2Float(0x431fb334), SkBits2Float(0x4397b40c), SkBits2Float(0x431860b0));  // 296.084f, 159.7f, 303.407f, 152.378f
+path.quadTo(SkBits2Float(0x439b5d4e), SkBits2Float(0x43110e2c), SkBits2Float(0x43a08aca), SkBits2Float(0x43110e2c));  // 310.729f, 145.055f, 321.084f, 145.055f
+path.quadTo(SkBits2Float(0x43a5b846), SkBits2Float(0x43110e2c), SkBits2Float(0x43a96188), SkBits2Float(0x431860b0));  // 331.44f, 145.055f, 338.762f, 152.378f
+path.quadTo(SkBits2Float(0x43ad0aca), SkBits2Float(0x431fb334), SkBits2Float(0x43ad0aca), SkBits2Float(0x432a0e2c));  // 346.084f, 159.7f, 346.084f, 170.055f
+path.close();
+    SkPath path1(path);
+    builder.add(path1, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x431f14f9), SkBits2Float(0x433943fd));  // 159.082f, 185.266f
+path.quadTo(SkBits2Float(0x431f14f9), SkBits2Float(0x43439ef4), SkBits2Float(0x4317c275), SkBits2Float(0x434af179));  // 159.082f, 195.621f, 151.76f, 202.943f
+path.quadTo(SkBits2Float(0x43106ff0), SkBits2Float(0x435243fd), SkBits2Float(0x430614f9), SkBits2Float(0x435243fd));  // 144.437f, 210.266f, 134.082f, 210.266f
+path.quadTo(SkBits2Float(0x42f77403), SkBits2Float(0x435243fd), SkBits2Float(0x42e8cefa), SkBits2Float(0x434af179));  // 123.727f, 210.266f, 116.404f, 202.943f
+path.quadTo(SkBits2Float(0x42da29f2), SkBits2Float(0x43439ef4), SkBits2Float(0x42da29f2), SkBits2Float(0x433943fd));  // 109.082f, 195.621f, 109.082f, 185.266f
+path.quadTo(SkBits2Float(0x42da29f2), SkBits2Float(0x432ee906), SkBits2Float(0x42e8cefa), SkBits2Float(0x43279681));  // 109.082f, 174.91f, 116.404f, 167.588f
+path.quadTo(SkBits2Float(0x42f77403), SkBits2Float(0x432043fd), SkBits2Float(0x430614f9), SkBits2Float(0x432043fd));  // 123.727f, 160.266f, 134.082f, 160.266f
+path.quadTo(SkBits2Float(0x43106ff0), SkBits2Float(0x432043fd), SkBits2Float(0x4317c275), SkBits2Float(0x43279681));  // 144.437f, 160.266f, 151.76f, 167.588f
+path.quadTo(SkBits2Float(0x431f14f9), SkBits2Float(0x432ee906), SkBits2Float(0x431f14f9), SkBits2Float(0x433943fd));  // 159.082f, 174.91f, 159.082f, 185.266f
+path.close();
+    SkPath path2(path);
+    builder.add(path2, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x433ad67a), SkBits2Float(0x43abd585));  // 186.838f, 343.668f
+path.quadTo(SkBits2Float(0x433ad67a), SkBits2Float(0x43b10301), SkBits2Float(0x433383f6), SkBits2Float(0x43b4ac43));  // 186.838f, 354.023f, 179.515f, 361.346f
+path.quadTo(SkBits2Float(0x432c3172), SkBits2Float(0x43b85585), SkBits2Float(0x4321d67a), SkBits2Float(0x43b85585));  // 172.193f, 368.668f, 161.838f, 368.668f
+path.quadTo(SkBits2Float(0x43177b82), SkBits2Float(0x43b85585), SkBits2Float(0x431028fe), SkBits2Float(0x43b4ac43));  // 151.482f, 368.668f, 144.16f, 361.346f
+path.quadTo(SkBits2Float(0x4308d67a), SkBits2Float(0x43b10301), SkBits2Float(0x4308d67a), SkBits2Float(0x43abd585));  // 136.838f, 354.023f, 136.838f, 343.668f
+path.quadTo(SkBits2Float(0x4308d67a), SkBits2Float(0x43a6a809), SkBits2Float(0x431028fe), SkBits2Float(0x43a2fec7));  // 136.838f, 333.313f, 144.16f, 325.99f
+path.quadTo(SkBits2Float(0x43177b82), SkBits2Float(0x439f5585), SkBits2Float(0x4321d67a), SkBits2Float(0x439f5585));  // 151.482f, 318.668f, 161.838f, 318.668f
+path.quadTo(SkBits2Float(0x432c3172), SkBits2Float(0x439f5585), SkBits2Float(0x433383f6), SkBits2Float(0x43a2fec7));  // 172.193f, 318.668f, 179.515f, 325.99f
+path.quadTo(SkBits2Float(0x433ad67a), SkBits2Float(0x43a6a809), SkBits2Float(0x433ad67a), SkBits2Float(0x43abd585));  // 186.838f, 333.313f, 186.838f, 343.668f
+path.close();
+    SkPath path3(path);
+    builder.add(path3, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43bff91b), SkBits2Float(0x43973a57));  // 383.946f, 302.456f
+path.quadTo(SkBits2Float(0x43bff91b), SkBits2Float(0x439c67d3), SkBits2Float(0x43bc4fd9), SkBits2Float(0x43a01115));  // 383.946f, 312.811f, 376.624f, 320.133f
+path.quadTo(SkBits2Float(0x43b8a697), SkBits2Float(0x43a3ba57), SkBits2Float(0x43b3791b), SkBits2Float(0x43a3ba57));  // 369.301f, 327.456f, 358.946f, 327.456f
+path.quadTo(SkBits2Float(0x43ae4b9f), SkBits2Float(0x43a3ba57), SkBits2Float(0x43aaa25d), SkBits2Float(0x43a01115));  // 348.591f, 327.456f, 341.268f, 320.133f
+path.quadTo(SkBits2Float(0x43a6f91b), SkBits2Float(0x439c67d3), SkBits2Float(0x43a6f91b), SkBits2Float(0x43973a57));  // 333.946f, 312.811f, 333.946f, 302.456f
+path.quadTo(SkBits2Float(0x43a6f91b), SkBits2Float(0x43920cdb), SkBits2Float(0x43aaa25d), SkBits2Float(0x438e6399));  // 333.946f, 292.1f, 341.268f, 284.778f
+path.quadTo(SkBits2Float(0x43ae4b9f), SkBits2Float(0x438aba57), SkBits2Float(0x43b3791b), SkBits2Float(0x438aba57));  // 348.591f, 277.456f, 358.946f, 277.456f
+path.quadTo(SkBits2Float(0x43b8a697), SkBits2Float(0x438aba57), SkBits2Float(0x43bc4fd9), SkBits2Float(0x438e6399));  // 369.301f, 277.456f, 376.624f, 284.778f
+path.quadTo(SkBits2Float(0x43bff91b), SkBits2Float(0x43920cdb), SkBits2Float(0x43bff91b), SkBits2Float(0x43973a57));  // 383.946f, 292.1f, 383.946f, 302.456f
+path.close();
+    SkPath path4(path);
+    builder.add(path4, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43374c2c), SkBits2Float(0x437e8a30));  // 183.298f, 254.54f
+path.quadTo(SkBits2Float(0x43374c2c), SkBits2Float(0x43847294), SkBits2Float(0x432ff9a8), SkBits2Float(0x43881bd6));  // 183.298f, 264.895f, 175.975f, 272.217f
+path.quadTo(SkBits2Float(0x4328a724), SkBits2Float(0x438bc518), SkBits2Float(0x431e4c2c), SkBits2Float(0x438bc518));  // 168.653f, 279.54f, 158.298f, 279.54f
+path.quadTo(SkBits2Float(0x4313f134), SkBits2Float(0x438bc518), SkBits2Float(0x430c9eb0), SkBits2Float(0x43881bd6));  // 147.942f, 279.54f, 140.62f, 272.217f
+path.quadTo(SkBits2Float(0x43054c2c), SkBits2Float(0x43847294), SkBits2Float(0x43054c2c), SkBits2Float(0x437e8a30));  // 133.298f, 264.895f, 133.298f, 254.54f
+path.quadTo(SkBits2Float(0x43054c2c), SkBits2Float(0x43742f38), SkBits2Float(0x430c9eb0), SkBits2Float(0x436cdcb4));  // 133.298f, 244.184f, 140.62f, 236.862f
+path.quadTo(SkBits2Float(0x4313f134), SkBits2Float(0x43658a30), SkBits2Float(0x431e4c2c), SkBits2Float(0x43658a30));  // 147.942f, 229.54f, 158.298f, 229.54f
+path.quadTo(SkBits2Float(0x4328a724), SkBits2Float(0x43658a30), SkBits2Float(0x432ff9a8), SkBits2Float(0x436cdcb4));  // 168.653f, 229.54f, 175.975f, 236.862f
+path.quadTo(SkBits2Float(0x43374c2c), SkBits2Float(0x43742f38), SkBits2Float(0x43374c2c), SkBits2Float(0x437e8a30));  // 183.298f, 244.184f, 183.298f, 254.54f
+path.close();
+    SkPath path5(path);
+    builder.add(path5, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x430e01e8), SkBits2Float(0x435c8671));  // 142.007f, 220.525f
+path.quadTo(SkBits2Float(0x430e01e8), SkBits2Float(0x4366e168), SkBits2Float(0x4306af64), SkBits2Float(0x436e33ed));  // 142.007f, 230.88f, 134.685f, 238.203f
+path.quadTo(SkBits2Float(0x42feb9bf), SkBits2Float(0x43758671), SkBits2Float(0x42ea03d0), SkBits2Float(0x43758671));  // 127.363f, 245.525f, 117.007f, 245.525f
+path.quadTo(SkBits2Float(0x42d54de1), SkBits2Float(0x43758671), SkBits2Float(0x42c6a8d8), SkBits2Float(0x436e33ed));  // 106.652f, 245.525f, 99.3298f, 238.203f
+path.quadTo(SkBits2Float(0x42b803d0), SkBits2Float(0x4366e168), SkBits2Float(0x42b803d0), SkBits2Float(0x435c8671));  // 92.0074f, 230.88f, 92.0074f, 220.525f
+path.quadTo(SkBits2Float(0x42b803d0), SkBits2Float(0x43522b7a), SkBits2Float(0x42c6a8d8), SkBits2Float(0x434ad8f5));  // 92.0074f, 210.17f, 99.3298f, 202.847f
+path.quadTo(SkBits2Float(0x42d54de1), SkBits2Float(0x43438671), SkBits2Float(0x42ea03d0), SkBits2Float(0x43438671));  // 106.652f, 195.525f, 117.007f, 195.525f
+path.quadTo(SkBits2Float(0x42feb9bf), SkBits2Float(0x43438671), SkBits2Float(0x4306af64), SkBits2Float(0x434ad8f5));  // 127.363f, 195.525f, 134.685f, 202.847f
+path.quadTo(SkBits2Float(0x430e01e8), SkBits2Float(0x43522b7a), SkBits2Float(0x430e01e8), SkBits2Float(0x435c8671));  // 142.007f, 210.17f, 142.007f, 220.525f
+path.close();
+    SkPath path6(path);
+    builder.add(path6, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x438b7062), SkBits2Float(0x42d54bf2));  // 278.878f, 106.648f
+path.quadTo(SkBits2Float(0x438b7062), SkBits2Float(0x42ea01e1), SkBits2Float(0x4387c720), SkBits2Float(0x42f8a6ea));  // 278.878f, 117.004f, 271.556f, 124.326f
+path.quadTo(SkBits2Float(0x43841dde), SkBits2Float(0x4303a5f9), SkBits2Float(0x437de0c4), SkBits2Float(0x4303a5f9));  // 264.233f, 131.648f, 253.878f, 131.648f
+path.quadTo(SkBits2Float(0x437385cc), SkBits2Float(0x4303a5f9), SkBits2Float(0x436c3348), SkBits2Float(0x42f8a6ea));  // 243.523f, 131.648f, 236.2f, 124.326f
+path.quadTo(SkBits2Float(0x4364e0c3), SkBits2Float(0x42ea01e1), SkBits2Float(0x4364e0c3), SkBits2Float(0x42d54bf2));  // 228.878f, 117.004f, 228.878f, 106.648f
+path.quadTo(SkBits2Float(0x4364e0c3), SkBits2Float(0x42c09603), SkBits2Float(0x436c3348), SkBits2Float(0x42b1f0fa));  // 228.878f, 96.293f, 236.2f, 88.9707f
+path.quadTo(SkBits2Float(0x437385cc), SkBits2Float(0x42a34bf2), SkBits2Float(0x437de0c4), SkBits2Float(0x42a34bf2));  // 243.523f, 81.6483f, 253.878f, 81.6483f
+path.quadTo(SkBits2Float(0x43841dde), SkBits2Float(0x42a34bf2), SkBits2Float(0x4387c720), SkBits2Float(0x42b1f0fa));  // 264.233f, 81.6483f, 271.556f, 88.9707f
+path.quadTo(SkBits2Float(0x438b7062), SkBits2Float(0x42c09603), SkBits2Float(0x438b7062), SkBits2Float(0x42d54bf2));  // 278.878f, 96.293f, 278.878f, 106.648f
+path.close();
+    SkPath path7(path);
+    builder.add(path7, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43de3ff6), SkBits2Float(0x43963745));  // 444.5f, 300.432f
+path.quadTo(SkBits2Float(0x43de3ff6), SkBits2Float(0x439b64c1), SkBits2Float(0x43da96b4), SkBits2Float(0x439f0e03));  // 444.5f, 310.787f, 437.177f, 318.109f
+path.quadTo(SkBits2Float(0x43d6ed72), SkBits2Float(0x43a2b745), SkBits2Float(0x43d1bff6), SkBits2Float(0x43a2b745));  // 429.855f, 325.432f, 419.5f, 325.432f
+path.quadTo(SkBits2Float(0x43cc927a), SkBits2Float(0x43a2b745), SkBits2Float(0x43c8e938), SkBits2Float(0x439f0e03));  // 409.144f, 325.432f, 401.822f, 318.109f
+path.quadTo(SkBits2Float(0x43c53ff6), SkBits2Float(0x439b64c1), SkBits2Float(0x43c53ff6), SkBits2Float(0x43963745));  // 394.5f, 310.787f, 394.5f, 300.432f
+path.quadTo(SkBits2Float(0x43c53ff6), SkBits2Float(0x439109c9), SkBits2Float(0x43c8e938), SkBits2Float(0x438d6087));  // 394.5f, 290.076f, 401.822f, 282.754f
+path.quadTo(SkBits2Float(0x43cc927a), SkBits2Float(0x4389b745), SkBits2Float(0x43d1bff6), SkBits2Float(0x4389b745));  // 409.144f, 275.432f, 419.5f, 275.432f
+path.quadTo(SkBits2Float(0x43d6ed72), SkBits2Float(0x4389b745), SkBits2Float(0x43da96b4), SkBits2Float(0x438d6087));  // 429.855f, 275.432f, 437.177f, 282.754f
+path.quadTo(SkBits2Float(0x43de3ff6), SkBits2Float(0x439109c9), SkBits2Float(0x43de3ff6), SkBits2Float(0x43963745));  // 444.5f, 290.076f, 444.5f, 300.432f
+path.close();
+    SkPath path8(path);
+    builder.add(path8, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43aae79c), SkBits2Float(0x438d0cbc));  // 341.809f, 282.099f
+path.quadTo(SkBits2Float(0x43aae79c), SkBits2Float(0x43923a38), SkBits2Float(0x43a73e5a), SkBits2Float(0x4395e37a));  // 341.809f, 292.455f, 334.487f, 299.777f
+path.quadTo(SkBits2Float(0x43a39518), SkBits2Float(0x43998cbc), SkBits2Float(0x439e679c), SkBits2Float(0x43998cbc));  // 327.165f, 307.099f, 316.809f, 307.099f
+path.quadTo(SkBits2Float(0x43993a20), SkBits2Float(0x43998cbc), SkBits2Float(0x439590de), SkBits2Float(0x4395e37a));  // 306.454f, 307.099f, 299.132f, 299.777f
+path.quadTo(SkBits2Float(0x4391e79c), SkBits2Float(0x43923a38), SkBits2Float(0x4391e79c), SkBits2Float(0x438d0cbc));  // 291.809f, 292.455f, 291.809f, 282.099f
+path.quadTo(SkBits2Float(0x4391e79c), SkBits2Float(0x4387df40), SkBits2Float(0x439590de), SkBits2Float(0x438435fe));  // 291.809f, 271.744f, 299.132f, 264.422f
+path.quadTo(SkBits2Float(0x43993a20), SkBits2Float(0x43808cbc), SkBits2Float(0x439e679c), SkBits2Float(0x43808cbc));  // 306.454f, 257.099f, 316.809f, 257.099f
+path.quadTo(SkBits2Float(0x43a39518), SkBits2Float(0x43808cbc), SkBits2Float(0x43a73e5a), SkBits2Float(0x438435fe));  // 327.165f, 257.099f, 334.487f, 264.422f
+path.quadTo(SkBits2Float(0x43aae79c), SkBits2Float(0x4387df40), SkBits2Float(0x43aae79c), SkBits2Float(0x438d0cbc));  // 341.809f, 271.744f, 341.809f, 282.099f
+path.close();
+    SkPath path9(path);
+    builder.add(path9, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4354ce7d), SkBits2Float(0x43842ec9));  // 212.807f, 264.366f
+path.quadTo(SkBits2Float(0x4354ce7d), SkBits2Float(0x43895c45), SkBits2Float(0x434d7bf9), SkBits2Float(0x438d0587));  // 212.807f, 274.721f, 205.484f, 282.043f
+path.quadTo(SkBits2Float(0x43462974), SkBits2Float(0x4390aec9), SkBits2Float(0x433bce7d), SkBits2Float(0x4390aec9));  // 198.162f, 289.366f, 187.807f, 289.366f
+path.quadTo(SkBits2Float(0x43317386), SkBits2Float(0x4390aec9), SkBits2Float(0x432a2101), SkBits2Float(0x438d0587));  // 177.451f, 289.366f, 170.129f, 282.043f
+path.quadTo(SkBits2Float(0x4322ce7d), SkBits2Float(0x43895c45), SkBits2Float(0x4322ce7d), SkBits2Float(0x43842ec9));  // 162.807f, 274.721f, 162.807f, 264.366f
+path.quadTo(SkBits2Float(0x4322ce7d), SkBits2Float(0x437e029a), SkBits2Float(0x432a2101), SkBits2Float(0x4376b016));  // 162.807f, 254.01f, 170.129f, 246.688f
+path.quadTo(SkBits2Float(0x43317386), SkBits2Float(0x436f5d92), SkBits2Float(0x433bce7d), SkBits2Float(0x436f5d92));  // 177.451f, 239.366f, 187.807f, 239.366f
+path.quadTo(SkBits2Float(0x43462974), SkBits2Float(0x436f5d92), SkBits2Float(0x434d7bf9), SkBits2Float(0x4376b016));  // 198.162f, 239.366f, 205.484f, 246.688f
+path.quadTo(SkBits2Float(0x4354ce7d), SkBits2Float(0x437e029a), SkBits2Float(0x4354ce7d), SkBits2Float(0x43842ec9));  // 212.807f, 254.01f, 212.807f, 264.366f
+path.close();
+    SkPath path10(path);
+    builder.add(path10, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43a8c299), SkBits2Float(0x432fce08));  // 337.52f, 175.805f
+path.quadTo(SkBits2Float(0x43a8c299), SkBits2Float(0x433a2900), SkBits2Float(0x43a51957), SkBits2Float(0x43417b84));  // 337.52f, 186.16f, 330.198f, 193.482f
+path.quadTo(SkBits2Float(0x43a17015), SkBits2Float(0x4348ce08), SkBits2Float(0x439c4299), SkBits2Float(0x4348ce08));  // 322.876f, 200.805f, 312.52f, 200.805f
+path.quadTo(SkBits2Float(0x4397151d), SkBits2Float(0x4348ce08), SkBits2Float(0x43936bdb), SkBits2Float(0x43417b84));  // 302.165f, 200.805f, 294.843f, 193.482f
+path.quadTo(SkBits2Float(0x438fc299), SkBits2Float(0x433a2900), SkBits2Float(0x438fc299), SkBits2Float(0x432fce08));  // 287.52f, 186.16f, 287.52f, 175.805f
+path.quadTo(SkBits2Float(0x438fc299), SkBits2Float(0x43257310), SkBits2Float(0x43936bdb), SkBits2Float(0x431e208c));  // 287.52f, 165.449f, 294.843f, 158.127f
+path.quadTo(SkBits2Float(0x4397151d), SkBits2Float(0x4316ce08), SkBits2Float(0x439c4299), SkBits2Float(0x4316ce08));  // 302.165f, 150.805f, 312.52f, 150.805f
+path.quadTo(SkBits2Float(0x43a17015), SkBits2Float(0x4316ce08), SkBits2Float(0x43a51957), SkBits2Float(0x431e208c));  // 322.876f, 150.805f, 330.198f, 158.127f
+path.quadTo(SkBits2Float(0x43a8c299), SkBits2Float(0x43257310), SkBits2Float(0x43a8c299), SkBits2Float(0x432fce08));  // 337.52f, 165.449f, 337.52f, 175.805f
+path.close();
+    SkPath path11(path);
+    builder.add(path11, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43d7486e), SkBits2Float(0x430ebc47));  // 430.566f, 142.735f
+path.quadTo(SkBits2Float(0x43d7486e), SkBits2Float(0x4319173e), SkBits2Float(0x43d39f2c), SkBits2Float(0x432069c3));  // 430.566f, 153.091f, 423.244f, 160.413f
+path.quadTo(SkBits2Float(0x43cff5ea), SkBits2Float(0x4327bc47), SkBits2Float(0x43cac86e), SkBits2Float(0x4327bc47));  // 415.921f, 167.735f, 405.566f, 167.735f
+path.quadTo(SkBits2Float(0x43c59af2), SkBits2Float(0x4327bc47), SkBits2Float(0x43c1f1b0), SkBits2Float(0x432069c3));  // 395.211f, 167.735f, 387.888f, 160.413f
+path.quadTo(SkBits2Float(0x43be486e), SkBits2Float(0x4319173e), SkBits2Float(0x43be486e), SkBits2Float(0x430ebc47));  // 380.566f, 153.091f, 380.566f, 142.735f
+path.quadTo(SkBits2Float(0x43be486e), SkBits2Float(0x43046150), SkBits2Float(0x43c1f1b0), SkBits2Float(0x42fa1d96));  // 380.566f, 132.38f, 387.888f, 125.058f
+path.quadTo(SkBits2Float(0x43c59af2), SkBits2Float(0x42eb788e), SkBits2Float(0x43cac86e), SkBits2Float(0x42eb788e));  // 395.211f, 117.735f, 405.566f, 117.735f
+path.quadTo(SkBits2Float(0x43cff5ea), SkBits2Float(0x42eb788e), SkBits2Float(0x43d39f2c), SkBits2Float(0x42fa1d96));  // 415.921f, 117.735f, 423.244f, 125.058f
+path.quadTo(SkBits2Float(0x43d7486e), SkBits2Float(0x43046150), SkBits2Float(0x43d7486e), SkBits2Float(0x430ebc47));  // 430.566f, 132.38f, 430.566f, 142.735f
+path.close();
+    SkPath path12(path);
+    builder.add(path12, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43484ac4), SkBits2Float(0x43421f09));  // 200.292f, 194.121f
+path.quadTo(SkBits2Float(0x43484ac4), SkBits2Float(0x434c7a00), SkBits2Float(0x4340f840), SkBits2Float(0x4353cc85));  // 200.292f, 204.477f, 192.97f, 211.799f
+path.quadTo(SkBits2Float(0x4339a5bc), SkBits2Float(0x435b1f09), SkBits2Float(0x432f4ac4), SkBits2Float(0x435b1f09));  // 185.647f, 219.121f, 175.292f, 219.121f
+path.quadTo(SkBits2Float(0x4324efcc), SkBits2Float(0x435b1f09), SkBits2Float(0x431d9d48), SkBits2Float(0x4353cc85));  // 164.937f, 219.121f, 157.614f, 211.799f
+path.quadTo(SkBits2Float(0x43164ac4), SkBits2Float(0x434c7a00), SkBits2Float(0x43164ac4), SkBits2Float(0x43421f09));  // 150.292f, 204.477f, 150.292f, 194.121f
+path.quadTo(SkBits2Float(0x43164ac4), SkBits2Float(0x4337c412), SkBits2Float(0x431d9d48), SkBits2Float(0x4330718d));  // 150.292f, 183.766f, 157.614f, 176.444f
+path.quadTo(SkBits2Float(0x4324efcc), SkBits2Float(0x43291f09), SkBits2Float(0x432f4ac4), SkBits2Float(0x43291f09));  // 164.937f, 169.121f, 175.292f, 169.121f
+path.quadTo(SkBits2Float(0x4339a5bc), SkBits2Float(0x43291f09), SkBits2Float(0x4340f840), SkBits2Float(0x4330718d));  // 185.647f, 169.121f, 192.97f, 176.444f
+path.quadTo(SkBits2Float(0x43484ac4), SkBits2Float(0x4337c412), SkBits2Float(0x43484ac4), SkBits2Float(0x43421f09));  // 200.292f, 183.766f, 200.292f, 194.121f
+path.close();
+    SkPath path13(path);
+    builder.add(path13, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4328883e), SkBits2Float(0x42fb0be0));  // 168.532f, 125.523f
+path.quadTo(SkBits2Float(0x4328883e), SkBits2Float(0x4307e0e7), SkBits2Float(0x432135ba), SkBits2Float(0x430f336c));  // 168.532f, 135.879f, 161.21f, 143.201f
+path.quadTo(SkBits2Float(0x4319e336), SkBits2Float(0x431685f0), SkBits2Float(0x430f883e), SkBits2Float(0x431685f0));  // 153.888f, 150.523f, 143.532f, 150.523f
+path.quadTo(SkBits2Float(0x43052d46), SkBits2Float(0x431685f0), SkBits2Float(0x42fbb584), SkBits2Float(0x430f336c));  // 133.177f, 150.523f, 125.855f, 143.201f
+path.quadTo(SkBits2Float(0x42ed107c), SkBits2Float(0x4307e0e7), SkBits2Float(0x42ed107c), SkBits2Float(0x42fb0be0));  // 118.532f, 135.879f, 118.532f, 125.523f
+path.quadTo(SkBits2Float(0x42ed107c), SkBits2Float(0x42e655f1), SkBits2Float(0x42fbb584), SkBits2Float(0x42d7b0e9));  // 118.532f, 115.168f, 125.855f, 107.846f
+path.quadTo(SkBits2Float(0x43052d46), SkBits2Float(0x42c90be1), SkBits2Float(0x430f883e), SkBits2Float(0x42c90be1));  // 133.177f, 100.523f, 143.532f, 100.523f
+path.quadTo(SkBits2Float(0x4319e336), SkBits2Float(0x42c90be1), SkBits2Float(0x432135ba), SkBits2Float(0x42d7b0e9));  // 153.888f, 100.523f, 161.21f, 107.846f
+path.quadTo(SkBits2Float(0x4328883e), SkBits2Float(0x42e655f1), SkBits2Float(0x4328883e), SkBits2Float(0x42fb0be0));  // 168.532f, 115.168f, 168.532f, 125.523f
+path.close();
+    SkPath path14(path);
+    builder.add(path14, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43b2bff8), SkBits2Float(0x439bb140));  // 357.5f, 311.385f
+path.quadTo(SkBits2Float(0x43b2bff8), SkBits2Float(0x43a0debc), SkBits2Float(0x43af16b6), SkBits2Float(0x43a487fe));  // 357.5f, 321.74f, 350.177f, 329.062f
+path.quadTo(SkBits2Float(0x43ab6d74), SkBits2Float(0x43a83140), SkBits2Float(0x43a63ff8), SkBits2Float(0x43a83140));  // 342.855f, 336.385f, 332.5f, 336.385f
+path.quadTo(SkBits2Float(0x43a1127c), SkBits2Float(0x43a83140), SkBits2Float(0x439d693a), SkBits2Float(0x43a487fe));  // 322.144f, 336.385f, 314.822f, 329.062f
+path.quadTo(SkBits2Float(0x4399bff8), SkBits2Float(0x43a0debc), SkBits2Float(0x4399bff8), SkBits2Float(0x439bb140));  // 307.5f, 321.74f, 307.5f, 311.385f
+path.quadTo(SkBits2Float(0x4399bff8), SkBits2Float(0x439683c4), SkBits2Float(0x439d693a), SkBits2Float(0x4392da82));  // 307.5f, 301.029f, 314.822f, 293.707f
+path.quadTo(SkBits2Float(0x43a1127c), SkBits2Float(0x438f3140), SkBits2Float(0x43a63ff8), SkBits2Float(0x438f3140));  // 322.144f, 286.385f, 332.5f, 286.385f
+path.quadTo(SkBits2Float(0x43ab6d74), SkBits2Float(0x438f3140), SkBits2Float(0x43af16b6), SkBits2Float(0x4392da82));  // 342.855f, 286.385f, 350.177f, 293.707f
+path.quadTo(SkBits2Float(0x43b2bff8), SkBits2Float(0x439683c4), SkBits2Float(0x43b2bff8), SkBits2Float(0x439bb140));  // 357.5f, 301.029f, 357.5f, 311.385f
+path.close();
+    SkPath path15(path);
+    builder.add(path15, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x435ae426), SkBits2Float(0x4341f066));  // 218.891f, 193.939f
+path.quadTo(SkBits2Float(0x435ae426), SkBits2Float(0x434c4b5e), SkBits2Float(0x435391a2), SkBits2Float(0x43539de2));  // 218.891f, 204.294f, 211.569f, 211.617f
+path.quadTo(SkBits2Float(0x434c3f1e), SkBits2Float(0x435af066), SkBits2Float(0x4341e426), SkBits2Float(0x435af066));  // 204.247f, 218.939f, 193.891f, 218.939f
+path.quadTo(SkBits2Float(0x4337892e), SkBits2Float(0x435af066), SkBits2Float(0x433036aa), SkBits2Float(0x43539de2));  // 183.536f, 218.939f, 176.214f, 211.617f
+path.quadTo(SkBits2Float(0x4328e426), SkBits2Float(0x434c4b5e), SkBits2Float(0x4328e426), SkBits2Float(0x4341f066));  // 168.891f, 204.294f, 168.891f, 193.939f
+path.quadTo(SkBits2Float(0x4328e426), SkBits2Float(0x4337956e), SkBits2Float(0x433036aa), SkBits2Float(0x433042ea));  // 168.891f, 183.584f, 176.214f, 176.261f
+path.quadTo(SkBits2Float(0x4337892e), SkBits2Float(0x4328f066), SkBits2Float(0x4341e426), SkBits2Float(0x4328f066));  // 183.536f, 168.939f, 193.891f, 168.939f
+path.quadTo(SkBits2Float(0x434c3f1e), SkBits2Float(0x4328f066), SkBits2Float(0x435391a2), SkBits2Float(0x433042ea));  // 204.247f, 168.939f, 211.569f, 176.261f
+path.quadTo(SkBits2Float(0x435ae426), SkBits2Float(0x4337956e), SkBits2Float(0x435ae426), SkBits2Float(0x4341f066));  // 218.891f, 183.584f, 218.891f, 193.939f
+path.close();
+    SkPath path16(path);
+    builder.add(path16, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x439817ba), SkBits2Float(0x42e83ba4));  // 304.185f, 116.116f
+path.quadTo(SkBits2Float(0x439817ba), SkBits2Float(0x42fcf193), SkBits2Float(0x43946e78), SkBits2Float(0x4305cb4e));  // 304.185f, 126.472f, 296.863f, 133.794f
+path.quadTo(SkBits2Float(0x4390c536), SkBits2Float(0x430d1dd2), SkBits2Float(0x438b97ba), SkBits2Float(0x430d1dd2));  // 289.541f, 141.116f, 279.185f, 141.116f
+path.quadTo(SkBits2Float(0x43866a3e), SkBits2Float(0x430d1dd2), SkBits2Float(0x4382c0fc), SkBits2Float(0x4305cb4e));  // 268.83f, 141.116f, 261.508f, 133.794f
+path.quadTo(SkBits2Float(0x437e2f74), SkBits2Float(0x42fcf193), SkBits2Float(0x437e2f74), SkBits2Float(0x42e83ba4));  // 254.185f, 126.472f, 254.185f, 116.116f
+path.quadTo(SkBits2Float(0x437e2f74), SkBits2Float(0x42d385b5), SkBits2Float(0x4382c0fc), SkBits2Float(0x42c4e0ac));  // 254.185f, 105.761f, 261.508f, 98.4388f
+path.quadTo(SkBits2Float(0x43866a3e), SkBits2Float(0x42b63ba4), SkBits2Float(0x438b97ba), SkBits2Float(0x42b63ba4));  // 268.83f, 91.1165f, 279.185f, 91.1165f
+path.quadTo(SkBits2Float(0x4390c536), SkBits2Float(0x42b63ba4), SkBits2Float(0x43946e78), SkBits2Float(0x42c4e0ac));  // 289.541f, 91.1165f, 296.863f, 98.4388f
+path.quadTo(SkBits2Float(0x439817ba), SkBits2Float(0x42d385b5), SkBits2Float(0x439817ba), SkBits2Float(0x42e83ba4));  // 304.185f, 105.761f, 304.185f, 116.116f
+path.close();
+    SkPath path17(path);
+    builder.add(path17, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4350558c), SkBits2Float(0x4382efb0));  // 208.334f, 261.873f
+path.quadTo(SkBits2Float(0x4350558c), SkBits2Float(0x43881d2c), SkBits2Float(0x43490308), SkBits2Float(0x438bc66e));  // 208.334f, 272.228f, 201.012f, 279.55f
+path.quadTo(SkBits2Float(0x4341b084), SkBits2Float(0x438f6fb0), SkBits2Float(0x4337558c), SkBits2Float(0x438f6fb0));  // 193.69f, 286.873f, 183.334f, 286.873f
+path.quadTo(SkBits2Float(0x432cfa94), SkBits2Float(0x438f6fb0), SkBits2Float(0x4325a810), SkBits2Float(0x438bc66e));  // 172.979f, 286.873f, 165.656f, 279.55f
+path.quadTo(SkBits2Float(0x431e558c), SkBits2Float(0x43881d2c), SkBits2Float(0x431e558c), SkBits2Float(0x4382efb0));  // 158.334f, 272.228f, 158.334f, 261.873f
+path.quadTo(SkBits2Float(0x431e558c), SkBits2Float(0x437b8468), SkBits2Float(0x4325a810), SkBits2Float(0x437431e4));  // 158.334f, 251.517f, 165.656f, 244.195f
+path.quadTo(SkBits2Float(0x432cfa94), SkBits2Float(0x436cdf60), SkBits2Float(0x4337558c), SkBits2Float(0x436cdf60));  // 172.979f, 236.873f, 183.334f, 236.873f
+path.quadTo(SkBits2Float(0x4341b084), SkBits2Float(0x436cdf60), SkBits2Float(0x43490308), SkBits2Float(0x437431e4));  // 193.69f, 236.873f, 201.012f, 244.195f
+path.quadTo(SkBits2Float(0x4350558c), SkBits2Float(0x437b8468), SkBits2Float(0x4350558c), SkBits2Float(0x4382efb0));  // 208.334f, 251.517f, 208.334f, 261.873f
+path.close();
+    SkPath path18(path);
+    builder.add(path18, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42a8ec1a), SkBits2Float(0x43a51083));  // 84.4611f, 330.129f
+path.quadTo(SkBits2Float(0x42a8ec1a), SkBits2Float(0x43aa3dff), SkBits2Float(0x429a4711), SkBits2Float(0x43ade741));  // 84.4611f, 340.484f, 77.1388f, 347.807f
+path.quadTo(SkBits2Float(0x428ba209), SkBits2Float(0x43b19083), SkBits2Float(0x426dd834), SkBits2Float(0x43b19083));  // 69.8165f, 355.129f, 59.4611f, 355.129f
+path.quadTo(SkBits2Float(0x42446c56), SkBits2Float(0x43b19083), SkBits2Float(0x42272246), SkBits2Float(0x43ade741));  // 49.1058f, 355.129f, 41.7835f, 347.807f
+path.quadTo(SkBits2Float(0x4209d835), SkBits2Float(0x43aa3dff), SkBits2Float(0x4209d835), SkBits2Float(0x43a51083));  // 34.4611f, 340.484f, 34.4611f, 330.129f
+path.quadTo(SkBits2Float(0x4209d835), SkBits2Float(0x439fe307), SkBits2Float(0x42272246), SkBits2Float(0x439c39c5));  // 34.4611f, 319.774f, 41.7835f, 312.451f
+path.quadTo(SkBits2Float(0x42446c56), SkBits2Float(0x43989083), SkBits2Float(0x426dd834), SkBits2Float(0x43989083));  // 49.1058f, 305.129f, 59.4611f, 305.129f
+path.quadTo(SkBits2Float(0x428ba209), SkBits2Float(0x43989083), SkBits2Float(0x429a4711), SkBits2Float(0x439c39c5));  // 69.8165f, 305.129f, 77.1388f, 312.451f
+path.quadTo(SkBits2Float(0x42a8ec1a), SkBits2Float(0x439fe307), SkBits2Float(0x42a8ec1a), SkBits2Float(0x43a51083));  // 84.4611f, 319.774f, 84.4611f, 330.129f
+path.close();
+    SkPath path19(path);
+    builder.add(path19, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43aca5fa), SkBits2Float(0x438f0f1d));  // 345.297f, 286.118f
+path.quadTo(SkBits2Float(0x43aca5fa), SkBits2Float(0x43943c99), SkBits2Float(0x43a8fcb8), SkBits2Float(0x4397e5db));  // 345.297f, 296.473f, 337.974f, 303.796f
+path.quadTo(SkBits2Float(0x43a55376), SkBits2Float(0x439b8f1d), SkBits2Float(0x43a025fa), SkBits2Float(0x439b8f1d));  // 330.652f, 311.118f, 320.297f, 311.118f
+path.quadTo(SkBits2Float(0x439af87e), SkBits2Float(0x439b8f1d), SkBits2Float(0x43974f3c), SkBits2Float(0x4397e5db));  // 309.941f, 311.118f, 302.619f, 303.796f
+path.quadTo(SkBits2Float(0x4393a5fa), SkBits2Float(0x43943c99), SkBits2Float(0x4393a5fa), SkBits2Float(0x438f0f1d));  // 295.297f, 296.473f, 295.297f, 286.118f
+path.quadTo(SkBits2Float(0x4393a5fa), SkBits2Float(0x4389e1a1), SkBits2Float(0x43974f3c), SkBits2Float(0x4386385f));  // 295.297f, 275.763f, 302.619f, 268.44f
+path.quadTo(SkBits2Float(0x439af87e), SkBits2Float(0x43828f1d), SkBits2Float(0x43a025fa), SkBits2Float(0x43828f1d));  // 309.941f, 261.118f, 320.297f, 261.118f
+path.quadTo(SkBits2Float(0x43a55376), SkBits2Float(0x43828f1d), SkBits2Float(0x43a8fcb8), SkBits2Float(0x4386385f));  // 330.652f, 261.118f, 337.974f, 268.44f
+path.quadTo(SkBits2Float(0x43aca5fa), SkBits2Float(0x4389e1a1), SkBits2Float(0x43aca5fa), SkBits2Float(0x438f0f1d));  // 345.297f, 275.763f, 345.297f, 286.118f
+path.close();
+    SkPath path20(path);
+    builder.add(path20, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43846cfc), SkBits2Float(0x431b61f0));  // 264.851f, 155.383f
+path.quadTo(SkBits2Float(0x43846cfc), SkBits2Float(0x4325bce8), SkBits2Float(0x4380c3ba), SkBits2Float(0x432d0f6c));  // 264.851f, 165.738f, 257.529f, 173.06f
+path.quadTo(SkBits2Float(0x437a34f0), SkBits2Float(0x433461f0), SkBits2Float(0x436fd9f8), SkBits2Float(0x433461f0));  // 250.207f, 180.383f, 239.851f, 180.383f
+path.quadTo(SkBits2Float(0x43657f00), SkBits2Float(0x433461f0), SkBits2Float(0x435e2c7c), SkBits2Float(0x432d0f6c));  // 229.496f, 180.383f, 222.174f, 173.06f
+path.quadTo(SkBits2Float(0x4356d9f7), SkBits2Float(0x4325bce8), SkBits2Float(0x4356d9f7), SkBits2Float(0x431b61f0));  // 214.851f, 165.738f, 214.851f, 155.383f
+path.quadTo(SkBits2Float(0x4356d9f7), SkBits2Float(0x431106f8), SkBits2Float(0x435e2c7c), SkBits2Float(0x4309b474));  // 214.851f, 145.027f, 222.174f, 137.705f
+path.quadTo(SkBits2Float(0x43657f00), SkBits2Float(0x430261f0), SkBits2Float(0x436fd9f8), SkBits2Float(0x430261f0));  // 229.496f, 130.383f, 239.851f, 130.383f
+path.quadTo(SkBits2Float(0x437a34f0), SkBits2Float(0x430261f0), SkBits2Float(0x4380c3ba), SkBits2Float(0x4309b474));  // 250.207f, 130.383f, 257.529f, 137.705f
+path.quadTo(SkBits2Float(0x43846cfc), SkBits2Float(0x431106f8), SkBits2Float(0x43846cfc), SkBits2Float(0x431b61f0));  // 264.851f, 145.027f, 264.851f, 155.383f
+path.close();
+    SkPath path21(path);
+    builder.add(path21, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x439597be), SkBits2Float(0x438dc3e1));  // 299.185f, 283.53f
+path.quadTo(SkBits2Float(0x439597be), SkBits2Float(0x4392f15d), SkBits2Float(0x4391ee7c), SkBits2Float(0x43969a9f));  // 299.185f, 293.886f, 291.863f, 301.208f
+path.quadTo(SkBits2Float(0x438e453a), SkBits2Float(0x439a43e1), SkBits2Float(0x438917be), SkBits2Float(0x439a43e1));  // 284.541f, 308.53f, 274.185f, 308.53f
+path.quadTo(SkBits2Float(0x4383ea42), SkBits2Float(0x439a43e1), SkBits2Float(0x43804100), SkBits2Float(0x43969a9f));  // 263.83f, 308.53f, 256.508f, 301.208f
+path.quadTo(SkBits2Float(0x43792f7c), SkBits2Float(0x4392f15d), SkBits2Float(0x43792f7c), SkBits2Float(0x438dc3e1));  // 249.185f, 293.886f, 249.185f, 283.53f
+path.quadTo(SkBits2Float(0x43792f7c), SkBits2Float(0x43889665), SkBits2Float(0x43804100), SkBits2Float(0x4384ed23));  // 249.185f, 273.175f, 256.508f, 265.853f
+path.quadTo(SkBits2Float(0x4383ea42), SkBits2Float(0x438143e1), SkBits2Float(0x438917be), SkBits2Float(0x438143e1));  // 263.83f, 258.53f, 274.185f, 258.53f
+path.quadTo(SkBits2Float(0x438e453a), SkBits2Float(0x438143e1), SkBits2Float(0x4391ee7c), SkBits2Float(0x4384ed23));  // 284.541f, 258.53f, 291.863f, 265.853f
+path.quadTo(SkBits2Float(0x439597be), SkBits2Float(0x43889665), SkBits2Float(0x439597be), SkBits2Float(0x438dc3e1));  // 299.185f, 273.175f, 299.185f, 283.53f
+path.close();
+    SkPath path22(path);
+    builder.add(path22, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42db7a2b), SkBits2Float(0x43699568));  // 109.739f, 233.584f
+path.quadTo(SkBits2Float(0x42db7a2b), SkBits2Float(0x4373f05f), SkBits2Float(0x42ccd522), SkBits2Float(0x437b42e3));  // 109.739f, 243.939f, 102.416f, 251.261f
+path.quadTo(SkBits2Float(0x42be301a), SkBits2Float(0x43814ab4), SkBits2Float(0x42a97a2b), SkBits2Float(0x43814ab4));  // 95.0939f, 258.584f, 84.7386f, 258.584f
+path.quadTo(SkBits2Float(0x4294c43c), SkBits2Float(0x43814ab4), SkBits2Float(0x42861f34), SkBits2Float(0x437b42e3));  // 74.3833f, 258.584f, 67.0609f, 251.261f
+path.quadTo(SkBits2Float(0x426ef456), SkBits2Float(0x4373f05f), SkBits2Float(0x426ef456), SkBits2Float(0x43699568));  // 59.7386f, 243.939f, 59.7386f, 233.584f
+path.quadTo(SkBits2Float(0x426ef456), SkBits2Float(0x435f3a71), SkBits2Float(0x42861f34), SkBits2Float(0x4357e7ed));  // 59.7386f, 223.228f, 67.0609f, 215.906f
+path.quadTo(SkBits2Float(0x4294c43c), SkBits2Float(0x43509569), SkBits2Float(0x42a97a2b), SkBits2Float(0x43509569));  // 74.3833f, 208.584f, 84.7386f, 208.584f
+path.quadTo(SkBits2Float(0x42be301a), SkBits2Float(0x43509569), SkBits2Float(0x42ccd522), SkBits2Float(0x4357e7ed));  // 95.0939f, 208.584f, 102.416f, 215.906f
+path.quadTo(SkBits2Float(0x42db7a2b), SkBits2Float(0x435f3a71), SkBits2Float(0x42db7a2b), SkBits2Float(0x43699568));  // 109.739f, 223.228f, 109.739f, 233.584f
+path.close();
+    SkPath path23(path);
+    builder.add(path23, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x433bea80), SkBits2Float(0x43861662));  // 187.916f, 268.175f
+path.quadTo(SkBits2Float(0x433bea80), SkBits2Float(0x438b43de), SkBits2Float(0x433497fc), SkBits2Float(0x438eed20));  // 187.916f, 278.53f, 180.594f, 285.853f
+path.quadTo(SkBits2Float(0x432d4578), SkBits2Float(0x43929662), SkBits2Float(0x4322ea80), SkBits2Float(0x43929662));  // 173.271f, 293.175f, 162.916f, 293.175f
+path.quadTo(SkBits2Float(0x43188f88), SkBits2Float(0x43929662), SkBits2Float(0x43113d04), SkBits2Float(0x438eed20));  // 152.561f, 293.175f, 145.238f, 285.853f
+path.quadTo(SkBits2Float(0x4309ea80), SkBits2Float(0x438b43de), SkBits2Float(0x4309ea80), SkBits2Float(0x43861662));  // 137.916f, 278.53f, 137.916f, 268.175f
+path.quadTo(SkBits2Float(0x4309ea80), SkBits2Float(0x4380e8e6), SkBits2Float(0x43113d04), SkBits2Float(0x437a7f48));  // 137.916f, 257.82f, 145.238f, 250.497f
+path.quadTo(SkBits2Float(0x43188f88), SkBits2Float(0x43732cc4), SkBits2Float(0x4322ea80), SkBits2Float(0x43732cc4));  // 152.561f, 243.175f, 162.916f, 243.175f
+path.quadTo(SkBits2Float(0x432d4578), SkBits2Float(0x43732cc4), SkBits2Float(0x433497fc), SkBits2Float(0x437a7f48));  // 173.271f, 243.175f, 180.594f, 250.497f
+path.quadTo(SkBits2Float(0x433bea80), SkBits2Float(0x4380e8e6), SkBits2Float(0x433bea80), SkBits2Float(0x43861662));  // 187.916f, 257.82f, 187.916f, 268.175f
+path.close();
+    SkPath path24(path);
+    builder.add(path24, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4386aaee), SkBits2Float(0x43991356));  // 269.335f, 306.151f
+path.quadTo(SkBits2Float(0x4386aaee), SkBits2Float(0x439e40d2), SkBits2Float(0x438301ac), SkBits2Float(0x43a1ea14));  // 269.335f, 316.506f, 262.013f, 323.829f
+path.quadTo(SkBits2Float(0x437eb0d4), SkBits2Float(0x43a59356), SkBits2Float(0x437455dc), SkBits2Float(0x43a59356));  // 254.691f, 331.151f, 244.335f, 331.151f
+path.quadTo(SkBits2Float(0x4369fae4), SkBits2Float(0x43a59356), SkBits2Float(0x4362a860), SkBits2Float(0x43a1ea14));  // 233.98f, 331.151f, 226.658f, 323.829f
+path.quadTo(SkBits2Float(0x435b55dc), SkBits2Float(0x439e40d2), SkBits2Float(0x435b55dc), SkBits2Float(0x43991356));  // 219.335f, 316.506f, 219.335f, 306.151f
+path.quadTo(SkBits2Float(0x435b55dc), SkBits2Float(0x4393e5da), SkBits2Float(0x4362a860), SkBits2Float(0x43903c98));  // 219.335f, 295.796f, 226.658f, 288.473f
+path.quadTo(SkBits2Float(0x4369fae4), SkBits2Float(0x438c9356), SkBits2Float(0x437455dc), SkBits2Float(0x438c9356));  // 233.98f, 281.151f, 244.335f, 281.151f
+path.quadTo(SkBits2Float(0x437eb0d4), SkBits2Float(0x438c9356), SkBits2Float(0x438301ac), SkBits2Float(0x43903c98));  // 254.691f, 281.151f, 262.013f, 288.473f
+path.quadTo(SkBits2Float(0x4386aaee), SkBits2Float(0x4393e5da), SkBits2Float(0x4386aaee), SkBits2Float(0x43991356));  // 269.335f, 295.796f, 269.335f, 306.151f
+path.close();
+    SkPath path25(path);
+    builder.add(path25, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43b0a7ab), SkBits2Float(0x42d6b25c));  // 353.31f, 107.348f
+path.quadTo(SkBits2Float(0x43b0a7ab), SkBits2Float(0x42eb684b), SkBits2Float(0x43acfe69), SkBits2Float(0x42fa0d53));  // 353.31f, 117.704f, 345.988f, 125.026f
+path.quadTo(SkBits2Float(0x43a95527), SkBits2Float(0x4304592e), SkBits2Float(0x43a427ab), SkBits2Float(0x4304592e));  // 338.665f, 132.348f, 328.31f, 132.348f
+path.quadTo(SkBits2Float(0x439efa2f), SkBits2Float(0x4304592e), SkBits2Float(0x439b50ed), SkBits2Float(0x42fa0d53));  // 317.955f, 132.348f, 310.632f, 125.026f
+path.quadTo(SkBits2Float(0x4397a7ab), SkBits2Float(0x42eb684b), SkBits2Float(0x4397a7ab), SkBits2Float(0x42d6b25c));  // 303.31f, 117.704f, 303.31f, 107.348f
+path.quadTo(SkBits2Float(0x4397a7ab), SkBits2Float(0x42c1fc6d), SkBits2Float(0x439b50ed), SkBits2Float(0x42b35765));  // 303.31f, 96.993f, 310.632f, 89.6707f
+path.quadTo(SkBits2Float(0x439efa2f), SkBits2Float(0x42a4b25d), SkBits2Float(0x43a427ab), SkBits2Float(0x42a4b25d));  // 317.955f, 82.3484f, 328.31f, 82.3484f
+path.quadTo(SkBits2Float(0x43a95527), SkBits2Float(0x42a4b25d), SkBits2Float(0x43acfe69), SkBits2Float(0x42b35765));  // 338.665f, 82.3484f, 345.988f, 89.6707f
+path.quadTo(SkBits2Float(0x43b0a7ab), SkBits2Float(0x42c1fc6d), SkBits2Float(0x43b0a7ab), SkBits2Float(0x42d6b25c));  // 353.31f, 96.993f, 353.31f, 107.348f
+path.close();
+    SkPath path26(path);
+    builder.add(path26, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43d478db), SkBits2Float(0x4301c45a));  // 424.944f, 129.767f
+path.quadTo(SkBits2Float(0x43d478db), SkBits2Float(0x430c1f52), SkBits2Float(0x43d0cf99), SkBits2Float(0x431371d6));  // 424.944f, 140.122f, 417.622f, 147.445f
+path.quadTo(SkBits2Float(0x43cd2657), SkBits2Float(0x431ac45a), SkBits2Float(0x43c7f8db), SkBits2Float(0x431ac45a));  // 410.3f, 154.767f, 399.944f, 154.767f
+path.quadTo(SkBits2Float(0x43c2cb5f), SkBits2Float(0x431ac45a), SkBits2Float(0x43bf221d), SkBits2Float(0x431371d6));  // 389.589f, 154.767f, 382.267f, 147.445f
+path.quadTo(SkBits2Float(0x43bb78db), SkBits2Float(0x430c1f52), SkBits2Float(0x43bb78db), SkBits2Float(0x4301c45a));  // 374.944f, 140.122f, 374.944f, 129.767f
+path.quadTo(SkBits2Float(0x43bb78db), SkBits2Float(0x42eed2c5), SkBits2Float(0x43bf221d), SkBits2Float(0x42e02dbc));  // 374.944f, 119.412f, 382.267f, 112.089f
+path.quadTo(SkBits2Float(0x43c2cb5f), SkBits2Float(0x42d188b4), SkBits2Float(0x43c7f8db), SkBits2Float(0x42d188b4));  // 389.589f, 104.767f, 399.944f, 104.767f
+path.quadTo(SkBits2Float(0x43cd2657), SkBits2Float(0x42d188b4), SkBits2Float(0x43d0cf99), SkBits2Float(0x42e02dbc));  // 410.3f, 104.767f, 417.622f, 112.089f
+path.quadTo(SkBits2Float(0x43d478db), SkBits2Float(0x42eed2c5), SkBits2Float(0x43d478db), SkBits2Float(0x4301c45a));  // 424.944f, 119.412f, 424.944f, 129.767f
+path.close();
+    SkPath path27(path);
+    builder.add(path27, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4370d681), SkBits2Float(0x4375fc44));  // 240.838f, 245.985f
+path.quadTo(SkBits2Float(0x4370d681), SkBits2Float(0x43802b9e), SkBits2Float(0x436983fd), SkBits2Float(0x4383d4e0));  // 240.838f, 256.341f, 233.516f, 263.663f
+path.quadTo(SkBits2Float(0x43623178), SkBits2Float(0x43877e22), SkBits2Float(0x4357d681), SkBits2Float(0x43877e22));  // 226.193f, 270.985f, 215.838f, 270.985f
+path.quadTo(SkBits2Float(0x434d7b8a), SkBits2Float(0x43877e22), SkBits2Float(0x43462905), SkBits2Float(0x4383d4e0));  // 205.483f, 270.985f, 198.16f, 263.663f
+path.quadTo(SkBits2Float(0x433ed681), SkBits2Float(0x43802b9e), SkBits2Float(0x433ed681), SkBits2Float(0x4375fc44));  // 190.838f, 256.341f, 190.838f, 245.985f
+path.quadTo(SkBits2Float(0x433ed681), SkBits2Float(0x436ba14d), SkBits2Float(0x43462905), SkBits2Float(0x43644ec9));  // 190.838f, 235.63f, 198.16f, 228.308f
+path.quadTo(SkBits2Float(0x434d7b8a), SkBits2Float(0x435cfc45), SkBits2Float(0x4357d681), SkBits2Float(0x435cfc45));  // 205.483f, 220.985f, 215.838f, 220.985f
+path.quadTo(SkBits2Float(0x43623178), SkBits2Float(0x435cfc45), SkBits2Float(0x436983fd), SkBits2Float(0x43644ec9));  // 226.193f, 220.985f, 233.516f, 228.308f
+path.quadTo(SkBits2Float(0x4370d681), SkBits2Float(0x436ba14d), SkBits2Float(0x4370d681), SkBits2Float(0x4375fc44));  // 240.838f, 235.63f, 240.838f, 245.985f
+path.close();
+    SkPath path28(path);
+    builder.add(path28, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43883c1d), SkBits2Float(0x438a9227));  // 272.47f, 277.142f
+path.quadTo(SkBits2Float(0x43883c1d), SkBits2Float(0x438fbfa3), SkBits2Float(0x438492db), SkBits2Float(0x439368e5));  // 272.47f, 287.497f, 265.147f, 294.819f
+path.quadTo(SkBits2Float(0x4380e999), SkBits2Float(0x43971227), SkBits2Float(0x4377783a), SkBits2Float(0x43971227));  // 257.825f, 302.142f, 247.47f, 302.142f
+path.quadTo(SkBits2Float(0x436d1d42), SkBits2Float(0x43971227), SkBits2Float(0x4365cabe), SkBits2Float(0x439368e5));  // 237.114f, 302.142f, 229.792f, 294.819f
+path.quadTo(SkBits2Float(0x435e783a), SkBits2Float(0x438fbfa3), SkBits2Float(0x435e783a), SkBits2Float(0x438a9227));  // 222.47f, 287.497f, 222.47f, 277.142f
+path.quadTo(SkBits2Float(0x435e783a), SkBits2Float(0x438564ab), SkBits2Float(0x4365cabe), SkBits2Float(0x4381bb69));  // 222.47f, 266.786f, 229.792f, 259.464f
+path.quadTo(SkBits2Float(0x436d1d42), SkBits2Float(0x437c244e), SkBits2Float(0x4377783a), SkBits2Float(0x437c244e));  // 237.114f, 252.142f, 247.47f, 252.142f
+path.quadTo(SkBits2Float(0x4380e999), SkBits2Float(0x437c244e), SkBits2Float(0x438492db), SkBits2Float(0x4381bb69));  // 257.825f, 252.142f, 265.147f, 259.464f
+path.quadTo(SkBits2Float(0x43883c1d), SkBits2Float(0x438564ab), SkBits2Float(0x43883c1d), SkBits2Float(0x438a9227));  // 272.47f, 266.786f, 272.47f, 277.142f
+path.close();
+    SkPath path29(path);
+    builder.add(path29, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43b5b3a4), SkBits2Float(0x43a2dbb0));  // 363.403f, 325.716f
+path.quadTo(SkBits2Float(0x43b5b3a4), SkBits2Float(0x43a8092c), SkBits2Float(0x43b20a62), SkBits2Float(0x43abb26e));  // 363.403f, 336.072f, 356.081f, 343.394f
+path.quadTo(SkBits2Float(0x43ae6120), SkBits2Float(0x43af5bb0), SkBits2Float(0x43a933a4), SkBits2Float(0x43af5bb0));  // 348.759f, 350.716f, 338.403f, 350.716f
+path.quadTo(SkBits2Float(0x43a40628), SkBits2Float(0x43af5bb0), SkBits2Float(0x43a05ce6), SkBits2Float(0x43abb26e));  // 328.048f, 350.716f, 320.726f, 343.394f
+path.quadTo(SkBits2Float(0x439cb3a4), SkBits2Float(0x43a8092c), SkBits2Float(0x439cb3a4), SkBits2Float(0x43a2dbb0));  // 313.403f, 336.072f, 313.403f, 325.716f
+path.quadTo(SkBits2Float(0x439cb3a4), SkBits2Float(0x439dae34), SkBits2Float(0x43a05ce6), SkBits2Float(0x439a04f2));  // 313.403f, 315.361f, 320.726f, 308.039f
+path.quadTo(SkBits2Float(0x43a40628), SkBits2Float(0x43965bb0), SkBits2Float(0x43a933a4), SkBits2Float(0x43965bb0));  // 328.048f, 300.716f, 338.403f, 300.716f
+path.quadTo(SkBits2Float(0x43ae6120), SkBits2Float(0x43965bb0), SkBits2Float(0x43b20a62), SkBits2Float(0x439a04f2));  // 348.759f, 300.716f, 356.081f, 308.039f
+path.quadTo(SkBits2Float(0x43b5b3a4), SkBits2Float(0x439dae34), SkBits2Float(0x43b5b3a4), SkBits2Float(0x43a2dbb0));  // 363.403f, 315.361f, 363.403f, 325.716f
+path.close();
+    SkPath path30(path);
+    builder.add(path30, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43a81cf4), SkBits2Float(0x431b2abc));  // 336.226f, 155.167f
+path.quadTo(SkBits2Float(0x43a81cf4), SkBits2Float(0x432585b4), SkBits2Float(0x43a473b2), SkBits2Float(0x432cd838));  // 336.226f, 165.522f, 328.904f, 172.845f
+path.quadTo(SkBits2Float(0x43a0ca70), SkBits2Float(0x43342abc), SkBits2Float(0x439b9cf4), SkBits2Float(0x43342abc));  // 321.582f, 180.167f, 311.226f, 180.167f
+path.quadTo(SkBits2Float(0x43966f78), SkBits2Float(0x43342abc), SkBits2Float(0x4392c636), SkBits2Float(0x432cd838));  // 300.871f, 180.167f, 293.549f, 172.845f
+path.quadTo(SkBits2Float(0x438f1cf4), SkBits2Float(0x432585b4), SkBits2Float(0x438f1cf4), SkBits2Float(0x431b2abc));  // 286.226f, 165.522f, 286.226f, 155.167f
+path.quadTo(SkBits2Float(0x438f1cf4), SkBits2Float(0x4310cfc4), SkBits2Float(0x4392c636), SkBits2Float(0x43097d40));  // 286.226f, 144.812f, 293.549f, 137.489f
+path.quadTo(SkBits2Float(0x43966f78), SkBits2Float(0x43022abc), SkBits2Float(0x439b9cf4), SkBits2Float(0x43022abc));  // 300.871f, 130.167f, 311.226f, 130.167f
+path.quadTo(SkBits2Float(0x43a0ca70), SkBits2Float(0x43022abc), SkBits2Float(0x43a473b2), SkBits2Float(0x43097d40));  // 321.582f, 130.167f, 328.904f, 137.489f
+path.quadTo(SkBits2Float(0x43a81cf4), SkBits2Float(0x4310cfc4), SkBits2Float(0x43a81cf4), SkBits2Float(0x431b2abc));  // 336.226f, 144.812f, 336.226f, 155.167f
+path.close();
+    SkPath path31(path);
+    builder.add(path31, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x435e092f), SkBits2Float(0x43698168));  // 222.036f, 233.505f
+path.quadTo(SkBits2Float(0x435e092f), SkBits2Float(0x4373dc5f), SkBits2Float(0x4356b6ab), SkBits2Float(0x437b2ee3));  // 222.036f, 243.861f, 214.714f, 251.183f
+path.quadTo(SkBits2Float(0x434f6426), SkBits2Float(0x438140b4), SkBits2Float(0x4345092f), SkBits2Float(0x438140b4));  // 207.391f, 258.505f, 197.036f, 258.505f
+path.quadTo(SkBits2Float(0x433aae38), SkBits2Float(0x438140b4), SkBits2Float(0x43335bb3), SkBits2Float(0x437b2ee3));  // 186.681f, 258.505f, 179.358f, 251.183f
+path.quadTo(SkBits2Float(0x432c092f), SkBits2Float(0x4373dc5f), SkBits2Float(0x432c092f), SkBits2Float(0x43698168));  // 172.036f, 243.861f, 172.036f, 233.505f
+path.quadTo(SkBits2Float(0x432c092f), SkBits2Float(0x435f2671), SkBits2Float(0x43335bb3), SkBits2Float(0x4357d3ed));  // 172.036f, 223.15f, 179.358f, 215.828f
+path.quadTo(SkBits2Float(0x433aae38), SkBits2Float(0x43508169), SkBits2Float(0x4345092f), SkBits2Float(0x43508169));  // 186.681f, 208.506f, 197.036f, 208.506f
+path.quadTo(SkBits2Float(0x434f6426), SkBits2Float(0x43508169), SkBits2Float(0x4356b6ab), SkBits2Float(0x4357d3ed));  // 207.391f, 208.506f, 214.714f, 215.828f
+path.quadTo(SkBits2Float(0x435e092f), SkBits2Float(0x435f2671), SkBits2Float(0x435e092f), SkBits2Float(0x43698168));  // 222.036f, 223.15f, 222.036f, 233.505f
+path.close();
+    SkPath path32(path);
+    builder.add(path32, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43b29d51), SkBits2Float(0x434f504b));  // 357.229f, 207.314f
+path.quadTo(SkBits2Float(0x43b29d51), SkBits2Float(0x4359ab42), SkBits2Float(0x43aef40f), SkBits2Float(0x4360fdc7));  // 357.229f, 217.669f, 349.907f, 224.991f
+path.quadTo(SkBits2Float(0x43ab4acd), SkBits2Float(0x4368504b), SkBits2Float(0x43a61d51), SkBits2Float(0x4368504b));  // 342.584f, 232.314f, 332.229f, 232.314f
+path.quadTo(SkBits2Float(0x43a0efd5), SkBits2Float(0x4368504b), SkBits2Float(0x439d4693), SkBits2Float(0x4360fdc7));  // 321.874f, 232.314f, 314.551f, 224.991f
+path.quadTo(SkBits2Float(0x43999d51), SkBits2Float(0x4359ab42), SkBits2Float(0x43999d51), SkBits2Float(0x434f504b));  // 307.229f, 217.669f, 307.229f, 207.314f
+path.quadTo(SkBits2Float(0x43999d51), SkBits2Float(0x4344f554), SkBits2Float(0x439d4693), SkBits2Float(0x433da2cf));  // 307.229f, 196.958f, 314.551f, 189.636f
+path.quadTo(SkBits2Float(0x43a0efd5), SkBits2Float(0x4336504b), SkBits2Float(0x43a61d51), SkBits2Float(0x4336504b));  // 321.874f, 182.314f, 332.229f, 182.314f
+path.quadTo(SkBits2Float(0x43ab4acd), SkBits2Float(0x4336504b), SkBits2Float(0x43aef40f), SkBits2Float(0x433da2cf));  // 342.584f, 182.314f, 349.907f, 189.636f
+path.quadTo(SkBits2Float(0x43b29d51), SkBits2Float(0x4344f554), SkBits2Float(0x43b29d51), SkBits2Float(0x434f504b));  // 357.229f, 196.958f, 357.229f, 207.314f
+path.close();
+    SkPath path33(path);
+    builder.add(path33, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x439022b6), SkBits2Float(0x434132a3));  // 288.271f, 193.198f
+path.quadTo(SkBits2Float(0x439022b6), SkBits2Float(0x434b8d9a), SkBits2Float(0x438c7974), SkBits2Float(0x4352e01f));  // 288.271f, 203.553f, 280.949f, 210.875f
+path.quadTo(SkBits2Float(0x4388d032), SkBits2Float(0x435a32a3), SkBits2Float(0x4383a2b6), SkBits2Float(0x435a32a3));  // 273.627f, 218.198f, 263.271f, 218.198f
+path.quadTo(SkBits2Float(0x437cea74), SkBits2Float(0x435a32a3), SkBits2Float(0x437597f0), SkBits2Float(0x4352e01f));  // 252.916f, 218.198f, 245.594f, 210.875f
+path.quadTo(SkBits2Float(0x436e456c), SkBits2Float(0x434b8d9a), SkBits2Float(0x436e456c), SkBits2Float(0x434132a3));  // 238.271f, 203.553f, 238.271f, 193.198f
+path.quadTo(SkBits2Float(0x436e456c), SkBits2Float(0x4336d7ac), SkBits2Float(0x437597f0), SkBits2Float(0x432f8527));  // 238.271f, 182.842f, 245.594f, 175.52f
+path.quadTo(SkBits2Float(0x437cea74), SkBits2Float(0x432832a3), SkBits2Float(0x4383a2b6), SkBits2Float(0x432832a3));  // 252.916f, 168.198f, 263.271f, 168.198f
+path.quadTo(SkBits2Float(0x4388d032), SkBits2Float(0x432832a3), SkBits2Float(0x438c7974), SkBits2Float(0x432f8527));  // 273.627f, 168.198f, 280.949f, 175.52f
+path.quadTo(SkBits2Float(0x439022b6), SkBits2Float(0x4336d7ac), SkBits2Float(0x439022b6), SkBits2Float(0x434132a3));  // 288.271f, 182.842f, 288.271f, 193.198f
+path.close();
+    SkPath path34(path);
+    builder.add(path34, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x434c6e1b), SkBits2Float(0x4386bd38));  // 204.43f, 269.478f
+path.quadTo(SkBits2Float(0x434c6e1b), SkBits2Float(0x438beab4), SkBits2Float(0x43451b97), SkBits2Float(0x438f93f6));  // 204.43f, 279.834f, 197.108f, 287.156f
+path.quadTo(SkBits2Float(0x433dc912), SkBits2Float(0x43933d38), SkBits2Float(0x43336e1b), SkBits2Float(0x43933d38));  // 189.785f, 294.478f, 179.43f, 294.478f
+path.quadTo(SkBits2Float(0x43291324), SkBits2Float(0x43933d38), SkBits2Float(0x4321c09f), SkBits2Float(0x438f93f6));  // 169.075f, 294.478f, 161.752f, 287.156f
+path.quadTo(SkBits2Float(0x431a6e1b), SkBits2Float(0x438beab4), SkBits2Float(0x431a6e1b), SkBits2Float(0x4386bd38));  // 154.43f, 279.834f, 154.43f, 269.478f
+path.quadTo(SkBits2Float(0x431a6e1b), SkBits2Float(0x43818fbc), SkBits2Float(0x4321c09f), SkBits2Float(0x437bccf4));  // 154.43f, 259.123f, 161.752f, 251.801f
+path.quadTo(SkBits2Float(0x43291324), SkBits2Float(0x43747a70), SkBits2Float(0x43336e1b), SkBits2Float(0x43747a70));  // 169.075f, 244.478f, 179.43f, 244.478f
+path.quadTo(SkBits2Float(0x433dc912), SkBits2Float(0x43747a70), SkBits2Float(0x43451b97), SkBits2Float(0x437bccf4));  // 189.785f, 244.478f, 197.108f, 251.801f
+path.quadTo(SkBits2Float(0x434c6e1b), SkBits2Float(0x43818fbc), SkBits2Float(0x434c6e1b), SkBits2Float(0x4386bd38));  // 204.43f, 259.123f, 204.43f, 269.478f
+path.close();
+    SkPath path35(path);
+    builder.add(path35, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43926b36), SkBits2Float(0x43b08773));  // 292.838f, 353.058f
+path.quadTo(SkBits2Float(0x43926b36), SkBits2Float(0x43b5b4ef), SkBits2Float(0x438ec1f4), SkBits2Float(0x43b95e31));  // 292.838f, 363.414f, 285.515f, 370.736f
+path.quadTo(SkBits2Float(0x438b18b2), SkBits2Float(0x43bd0773), SkBits2Float(0x4385eb36), SkBits2Float(0x43bd0773));  // 278.193f, 378.058f, 267.838f, 378.058f
+path.quadTo(SkBits2Float(0x4380bdba), SkBits2Float(0x43bd0773), SkBits2Float(0x437a28f0), SkBits2Float(0x43b95e31));  // 257.482f, 378.058f, 250.16f, 370.736f
+path.quadTo(SkBits2Float(0x4372d66c), SkBits2Float(0x43b5b4ef), SkBits2Float(0x4372d66c), SkBits2Float(0x43b08773));  // 242.838f, 363.414f, 242.838f, 353.058f
+path.quadTo(SkBits2Float(0x4372d66c), SkBits2Float(0x43ab59f7), SkBits2Float(0x437a28f0), SkBits2Float(0x43a7b0b5));  // 242.838f, 342.703f, 250.16f, 335.381f
+path.quadTo(SkBits2Float(0x4380bdba), SkBits2Float(0x43a40773), SkBits2Float(0x4385eb36), SkBits2Float(0x43a40773));  // 257.482f, 328.058f, 267.838f, 328.058f
+path.quadTo(SkBits2Float(0x438b18b2), SkBits2Float(0x43a40773), SkBits2Float(0x438ec1f4), SkBits2Float(0x43a7b0b5));  // 278.193f, 328.058f, 285.515f, 335.381f
+path.quadTo(SkBits2Float(0x43926b36), SkBits2Float(0x43ab59f7), SkBits2Float(0x43926b36), SkBits2Float(0x43b08773));  // 292.838f, 342.703f, 292.838f, 353.058f
+path.close();
+    SkPath path36(path);
+    builder.add(path36, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43ea874d), SkBits2Float(0x4382542c));  // 469.057f, 260.658f
+path.quadTo(SkBits2Float(0x43ea874d), SkBits2Float(0x438781a8), SkBits2Float(0x43e6de0b), SkBits2Float(0x438b2aea));  // 469.057f, 271.013f, 461.735f, 278.335f
+path.quadTo(SkBits2Float(0x43e334c9), SkBits2Float(0x438ed42c), SkBits2Float(0x43de074d), SkBits2Float(0x438ed42c));  // 454.412f, 285.658f, 444.057f, 285.658f
+path.quadTo(SkBits2Float(0x43d8d9d1), SkBits2Float(0x438ed42c), SkBits2Float(0x43d5308f), SkBits2Float(0x438b2aea));  // 433.702f, 285.658f, 426.379f, 278.335f
+path.quadTo(SkBits2Float(0x43d1874d), SkBits2Float(0x438781a8), SkBits2Float(0x43d1874d), SkBits2Float(0x4382542c));  // 419.057f, 271.013f, 419.057f, 260.658f
+path.quadTo(SkBits2Float(0x43d1874d), SkBits2Float(0x437a4d60), SkBits2Float(0x43d5308f), SkBits2Float(0x4372fadc));  // 419.057f, 250.302f, 426.379f, 242.98f
+path.quadTo(SkBits2Float(0x43d8d9d1), SkBits2Float(0x436ba858), SkBits2Float(0x43de074d), SkBits2Float(0x436ba858));  // 433.702f, 235.658f, 444.057f, 235.658f
+path.quadTo(SkBits2Float(0x43e334c9), SkBits2Float(0x436ba858), SkBits2Float(0x43e6de0b), SkBits2Float(0x4372fadc));  // 454.412f, 235.658f, 461.735f, 242.98f
+path.quadTo(SkBits2Float(0x43ea874d), SkBits2Float(0x437a4d60), SkBits2Float(0x43ea874d), SkBits2Float(0x4382542c));  // 469.057f, 250.302f, 469.057f, 260.658f
+path.close();
+    SkPath path37(path);
+    builder.add(path37, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42e7d715), SkBits2Float(0x436cecfc));  // 115.92f, 236.926f
+path.quadTo(SkBits2Float(0x42e7d715), SkBits2Float(0x437747f3), SkBits2Float(0x42d9320c), SkBits2Float(0x437e9a77));  // 115.92f, 247.281f, 108.598f, 254.603f
+path.quadTo(SkBits2Float(0x42ca8d04), SkBits2Float(0x4382f67e), SkBits2Float(0x42b5d715), SkBits2Float(0x4382f67e));  // 101.275f, 261.926f, 90.9201f, 261.926f
+path.quadTo(SkBits2Float(0x42a12126), SkBits2Float(0x4382f67e), SkBits2Float(0x42927c1e), SkBits2Float(0x437e9a77));  // 80.5647f, 261.926f, 73.2424f, 254.603f
+path.quadTo(SkBits2Float(0x4283d715), SkBits2Float(0x437747f3), SkBits2Float(0x4283d715), SkBits2Float(0x436cecfc));  // 65.9201f, 247.281f, 65.9201f, 236.926f
+path.quadTo(SkBits2Float(0x4283d715), SkBits2Float(0x43629205), SkBits2Float(0x42927c1e), SkBits2Float(0x435b3f81));  // 65.9201f, 226.57f, 73.2424f, 219.248f
+path.quadTo(SkBits2Float(0x42a12126), SkBits2Float(0x4353ecfd), SkBits2Float(0x42b5d715), SkBits2Float(0x4353ecfd));  // 80.5647f, 211.926f, 90.9201f, 211.926f
+path.quadTo(SkBits2Float(0x42ca8d04), SkBits2Float(0x4353ecfd), SkBits2Float(0x42d9320c), SkBits2Float(0x435b3f81));  // 101.275f, 211.926f, 108.598f, 219.248f
+path.quadTo(SkBits2Float(0x42e7d715), SkBits2Float(0x43629205), SkBits2Float(0x42e7d715), SkBits2Float(0x436cecfc));  // 115.92f, 226.57f, 115.92f, 236.926f
+path.close();
+    SkPath path38(path);
+    builder.add(path38, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43545413), SkBits2Float(0x4333cfcf));  // 212.328f, 179.812f
+path.quadTo(SkBits2Float(0x43545413), SkBits2Float(0x433e2ac6), SkBits2Float(0x434d018f), SkBits2Float(0x43457d4b));  // 212.328f, 190.167f, 205.006f, 197.489f
+path.quadTo(SkBits2Float(0x4345af0a), SkBits2Float(0x434ccfcf), SkBits2Float(0x433b5413), SkBits2Float(0x434ccfcf));  // 197.684f, 204.812f, 187.328f, 204.812f
+path.quadTo(SkBits2Float(0x4330f91c), SkBits2Float(0x434ccfcf), SkBits2Float(0x4329a697), SkBits2Float(0x43457d4b));  // 176.973f, 204.812f, 169.651f, 197.489f
+path.quadTo(SkBits2Float(0x43225413), SkBits2Float(0x433e2ac6), SkBits2Float(0x43225413), SkBits2Float(0x4333cfcf));  // 162.328f, 190.167f, 162.328f, 179.812f
+path.quadTo(SkBits2Float(0x43225413), SkBits2Float(0x432974d8), SkBits2Float(0x4329a697), SkBits2Float(0x43222253));  // 162.328f, 169.456f, 169.651f, 162.134f
+path.quadTo(SkBits2Float(0x4330f91c), SkBits2Float(0x431acfcf), SkBits2Float(0x433b5413), SkBits2Float(0x431acfcf));  // 176.973f, 154.812f, 187.328f, 154.812f
+path.quadTo(SkBits2Float(0x4345af0a), SkBits2Float(0x431acfcf), SkBits2Float(0x434d018f), SkBits2Float(0x43222253));  // 197.684f, 154.812f, 205.006f, 162.134f
+path.quadTo(SkBits2Float(0x43545413), SkBits2Float(0x432974d8), SkBits2Float(0x43545413), SkBits2Float(0x4333cfcf));  // 212.328f, 169.456f, 212.328f, 179.812f
+path.close();
+    SkPath path39(path);
+    builder.add(path39, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43c9b16c), SkBits2Float(0x41823be6));  // 403.386f, 16.2792f
+path.quadTo(SkBits2Float(0x43c9b16c), SkBits2Float(0x41d513a2), SkBits2Float(0x43c6082a), SkBits2Float(0x4207d3e2));  // 403.386f, 26.6346f, 396.064f, 33.9569f
+path.quadTo(SkBits2Float(0x43c25ee8), SkBits2Float(0x42251df3), SkBits2Float(0x43bd316c), SkBits2Float(0x42251df3));  // 388.741f, 41.2792f, 378.386f, 41.2792f
+path.quadTo(SkBits2Float(0x43b803f0), SkBits2Float(0x42251df3), SkBits2Float(0x43b45aae), SkBits2Float(0x4207d3e2));  // 368.031f, 41.2792f, 360.708f, 33.9569f
+path.quadTo(SkBits2Float(0x43b0b16c), SkBits2Float(0x41d513a2), SkBits2Float(0x43b0b16c), SkBits2Float(0x41823be6));  // 353.386f, 26.6346f, 353.386f, 16.2792f
+path.quadTo(SkBits2Float(0x43b0b16c), SkBits2Float(0x40bd90a8), SkBits2Float(0x43b45aae), SkBits2Float(0xbfb2ff80));  // 353.386f, 5.92391f, 360.708f, -1.39842f
+path.quadTo(SkBits2Float(0x43b803f0), SkBits2Float(0xc10b8834), SkBits2Float(0x43bd316c), SkBits2Float(0xc10b8834));  // 368.031f, -8.72075f, 378.386f, -8.72075f
+path.quadTo(SkBits2Float(0x43c25ee8), SkBits2Float(0xc10b8834), SkBits2Float(0x43c6082a), SkBits2Float(0xbfb2ff80));  // 388.741f, -8.72075f, 396.064f, -1.39842f
+path.quadTo(SkBits2Float(0x43c9b16c), SkBits2Float(0x40bd90a8), SkBits2Float(0x43c9b16c), SkBits2Float(0x41823be6));  // 403.386f, 5.92391f, 403.386f, 16.2792f
+path.close();
+    SkPath path40(path);
+    builder.add(path40, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43747fcb), SkBits2Float(0x43805e9d));  // 244.499f, 256.739f
+path.quadTo(SkBits2Float(0x43747fcb), SkBits2Float(0x43858c19), SkBits2Float(0x436d2d47), SkBits2Float(0x4389355b));  // 244.499f, 267.095f, 237.177f, 274.417f
+path.quadTo(SkBits2Float(0x4365dac2), SkBits2Float(0x438cde9d), SkBits2Float(0x435b7fcb), SkBits2Float(0x438cde9d));  // 229.855f, 281.739f, 219.499f, 281.739f
+path.quadTo(SkBits2Float(0x435124d4), SkBits2Float(0x438cde9d), SkBits2Float(0x4349d24f), SkBits2Float(0x4389355b));  // 209.144f, 281.739f, 201.822f, 274.417f
+path.quadTo(SkBits2Float(0x43427fcb), SkBits2Float(0x43858c19), SkBits2Float(0x43427fcb), SkBits2Float(0x43805e9d));  // 194.499f, 267.095f, 194.499f, 256.739f
+path.quadTo(SkBits2Float(0x43427fcb), SkBits2Float(0x43766242), SkBits2Float(0x4349d24f), SkBits2Float(0x436f0fbe));  // 194.499f, 246.384f, 201.822f, 239.061f
+path.quadTo(SkBits2Float(0x435124d4), SkBits2Float(0x4367bd3a), SkBits2Float(0x435b7fcb), SkBits2Float(0x4367bd3a));  // 209.144f, 231.739f, 219.499f, 231.739f
+path.quadTo(SkBits2Float(0x4365dac2), SkBits2Float(0x4367bd3a), SkBits2Float(0x436d2d47), SkBits2Float(0x436f0fbe));  // 229.855f, 231.739f, 237.177f, 239.061f
+path.quadTo(SkBits2Float(0x43747fcb), SkBits2Float(0x43766242), SkBits2Float(0x43747fcb), SkBits2Float(0x43805e9d));  // 244.499f, 246.384f, 244.499f, 256.739f
+path.close();
+    SkPath path41(path);
+    builder.add(path41, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43910318), SkBits2Float(0x43826a1e));  // 290.024f, 260.829f
+path.quadTo(SkBits2Float(0x43910318), SkBits2Float(0x4387979a), SkBits2Float(0x438d59d6), SkBits2Float(0x438b40dc));  // 290.024f, 271.184f, 282.702f, 278.507f
+path.quadTo(SkBits2Float(0x4389b094), SkBits2Float(0x438eea1e), SkBits2Float(0x43848318), SkBits2Float(0x438eea1e));  // 275.38f, 285.829f, 265.024f, 285.829f
+path.quadTo(SkBits2Float(0x437eab38), SkBits2Float(0x438eea1e), SkBits2Float(0x437758b4), SkBits2Float(0x438b40dc));  // 254.669f, 285.829f, 247.346f, 278.507f
+path.quadTo(SkBits2Float(0x43700630), SkBits2Float(0x4387979a), SkBits2Float(0x43700630), SkBits2Float(0x43826a1e));  // 240.024f, 271.184f, 240.024f, 260.829f
+path.quadTo(SkBits2Float(0x43700630), SkBits2Float(0x437a7944), SkBits2Float(0x437758b4), SkBits2Float(0x437326c0));  // 240.024f, 250.474f, 247.346f, 243.151f
+path.quadTo(SkBits2Float(0x437eab38), SkBits2Float(0x436bd43c), SkBits2Float(0x43848318), SkBits2Float(0x436bd43c));  // 254.669f, 235.829f, 265.024f, 235.829f
+path.quadTo(SkBits2Float(0x4389b094), SkBits2Float(0x436bd43c), SkBits2Float(0x438d59d6), SkBits2Float(0x437326c0));  // 275.38f, 235.829f, 282.702f, 243.151f
+path.quadTo(SkBits2Float(0x43910318), SkBits2Float(0x437a7944), SkBits2Float(0x43910318), SkBits2Float(0x43826a1e));  // 290.024f, 250.474f, 290.024f, 260.829f
+path.close();
+    SkPath path42(path);
+    builder.add(path42, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x41c80000), SkBits2Float(0x436edb04));  // 25, 238.856f
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x437935fb), SkBits2Float(0x418d6bde), SkBits2Float(0x43804440));  // 25, 249.211f, 17.6777f, 256.533f
+path.quadTo(SkBits2Float(0x4125af78), SkBits2Float(0x4383ed82), SkBits2Float(0x00000000), SkBits2Float(0x4383ed82));  // 10.3553f, 263.856f, 0, 263.856f
+path.quadTo(SkBits2Float(0xc125af78), SkBits2Float(0x4383ed82), SkBits2Float(0xc18d6bde), SkBits2Float(0x43804440));  // -10.3553f, 263.856f, -17.6777f, 256.533f
+path.quadTo(SkBits2Float(0xc1c80000), SkBits2Float(0x437935fb), SkBits2Float(0xc1c80000), SkBits2Float(0x436edb04));  // -25, 249.211f, -25, 238.856f
+path.quadTo(SkBits2Float(0xc1c80000), SkBits2Float(0x4364800d), SkBits2Float(0xc18d6bde), SkBits2Float(0x435d2d89));  // -25, 228.5f, -17.6777f, 221.178f
+path.quadTo(SkBits2Float(0xc125af78), SkBits2Float(0x4355db05), SkBits2Float(0x00000000), SkBits2Float(0x4355db05));  // -10.3553f, 213.856f, 0, 213.856f
+path.quadTo(SkBits2Float(0x4125af78), SkBits2Float(0x4355db05), SkBits2Float(0x418d6bde), SkBits2Float(0x435d2d89));  // 10.3553f, 213.856f, 17.6777f, 221.178f
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4364800d), SkBits2Float(0x41c80000), SkBits2Float(0x436edb04));  // 25, 228.5f, 25, 238.856f
+path.close();
+    SkPath path43(path);
+    builder.add(path43, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x435d07bd), SkBits2Float(0x4395fbb5));  // 221.03f, 299.966f
+path.quadTo(SkBits2Float(0x435d07bd), SkBits2Float(0x439b2931), SkBits2Float(0x4355b539), SkBits2Float(0x439ed273));  // 221.03f, 310.322f, 213.708f, 317.644f
+path.quadTo(SkBits2Float(0x434e62b4), SkBits2Float(0x43a27bb5), SkBits2Float(0x434407bd), SkBits2Float(0x43a27bb5));  // 206.386f, 324.966f, 196.03f, 324.966f
+path.quadTo(SkBits2Float(0x4339acc6), SkBits2Float(0x43a27bb5), SkBits2Float(0x43325a41), SkBits2Float(0x439ed273));  // 185.675f, 324.966f, 178.353f, 317.644f
+path.quadTo(SkBits2Float(0x432b07bd), SkBits2Float(0x439b2931), SkBits2Float(0x432b07bd), SkBits2Float(0x4395fbb5));  // 171.03f, 310.322f, 171.03f, 299.966f
+path.quadTo(SkBits2Float(0x432b07bd), SkBits2Float(0x4390ce39), SkBits2Float(0x43325a41), SkBits2Float(0x438d24f7));  // 171.03f, 289.611f, 178.353f, 282.289f
+path.quadTo(SkBits2Float(0x4339acc6), SkBits2Float(0x43897bb5), SkBits2Float(0x434407bd), SkBits2Float(0x43897bb5));  // 185.675f, 274.966f, 196.03f, 274.966f
+path.quadTo(SkBits2Float(0x434e62b4), SkBits2Float(0x43897bb5), SkBits2Float(0x4355b539), SkBits2Float(0x438d24f7));  // 206.386f, 274.966f, 213.708f, 282.289f
+path.quadTo(SkBits2Float(0x435d07bd), SkBits2Float(0x4390ce39), SkBits2Float(0x435d07bd), SkBits2Float(0x4395fbb5));  // 221.03f, 289.611f, 221.03f, 299.966f
+path.close();
+    SkPath path44(path);
+    builder.add(path44, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43a3ec29), SkBits2Float(0x434ac5a3));  // 327.845f, 202.772f
+path.quadTo(SkBits2Float(0x43a3ec29), SkBits2Float(0x4355209a), SkBits2Float(0x43a042e7), SkBits2Float(0x435c731f));  // 327.845f, 213.127f, 320.523f, 220.45f
+path.quadTo(SkBits2Float(0x439c99a5), SkBits2Float(0x4363c5a3), SkBits2Float(0x43976c29), SkBits2Float(0x4363c5a3));  // 313.2f, 227.772f, 302.845f, 227.772f
+path.quadTo(SkBits2Float(0x43923ead), SkBits2Float(0x4363c5a3), SkBits2Float(0x438e956b), SkBits2Float(0x435c731f));  // 292.49f, 227.772f, 285.167f, 220.45f
+path.quadTo(SkBits2Float(0x438aec29), SkBits2Float(0x4355209a), SkBits2Float(0x438aec29), SkBits2Float(0x434ac5a3));  // 277.845f, 213.127f, 277.845f, 202.772f
+path.quadTo(SkBits2Float(0x438aec29), SkBits2Float(0x43406aac), SkBits2Float(0x438e956b), SkBits2Float(0x43391827));  // 277.845f, 192.417f, 285.167f, 185.094f
+path.quadTo(SkBits2Float(0x43923ead), SkBits2Float(0x4331c5a3), SkBits2Float(0x43976c29), SkBits2Float(0x4331c5a3));  // 292.49f, 177.772f, 302.845f, 177.772f
+path.quadTo(SkBits2Float(0x439c99a5), SkBits2Float(0x4331c5a3), SkBits2Float(0x43a042e7), SkBits2Float(0x43391827));  // 313.2f, 177.772f, 320.523f, 185.094f
+path.quadTo(SkBits2Float(0x43a3ec29), SkBits2Float(0x43406aac), SkBits2Float(0x43a3ec29), SkBits2Float(0x434ac5a3));  // 327.845f, 192.417f, 327.845f, 202.772f
+path.close();
+    SkPath path45(path);
+    builder.add(path45, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4380585e), SkBits2Float(0x43199f0e));  // 256.69f, 153.621f
+path.quadTo(SkBits2Float(0x4380585e), SkBits2Float(0x4323fa06), SkBits2Float(0x43795e38), SkBits2Float(0x432b4c8a));  // 256.69f, 163.977f, 249.368f, 171.299f
+path.quadTo(SkBits2Float(0x43720bb4), SkBits2Float(0x43329f0e), SkBits2Float(0x4367b0bc), SkBits2Float(0x43329f0e));  // 242.046f, 178.621f, 231.69f, 178.621f
+path.quadTo(SkBits2Float(0x435d55c4), SkBits2Float(0x43329f0e), SkBits2Float(0x43560340), SkBits2Float(0x432b4c8a));  // 221.335f, 178.621f, 214.013f, 171.299f
+path.quadTo(SkBits2Float(0x434eb0bc), SkBits2Float(0x4323fa06), SkBits2Float(0x434eb0bc), SkBits2Float(0x43199f0e));  // 206.69f, 163.977f, 206.69f, 153.621f
+path.quadTo(SkBits2Float(0x434eb0bc), SkBits2Float(0x430f4416), SkBits2Float(0x43560340), SkBits2Float(0x4307f192));  // 206.69f, 143.266f, 214.013f, 135.944f
+path.quadTo(SkBits2Float(0x435d55c4), SkBits2Float(0x43009f0e), SkBits2Float(0x4367b0bc), SkBits2Float(0x43009f0e));  // 221.335f, 128.621f, 231.69f, 128.621f
+path.quadTo(SkBits2Float(0x43720bb4), SkBits2Float(0x43009f0e), SkBits2Float(0x43795e38), SkBits2Float(0x4307f192));  // 242.046f, 128.621f, 249.368f, 135.944f
+path.quadTo(SkBits2Float(0x4380585e), SkBits2Float(0x430f4416), SkBits2Float(0x4380585e), SkBits2Float(0x43199f0e));  // 256.69f, 143.266f, 256.69f, 153.621f
+path.close();
+    SkPath path46(path);
+    builder.add(path46, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43599e4b), SkBits2Float(0x43c5e452));  // 217.618f, 395.784f
+path.quadTo(SkBits2Float(0x43599e4b), SkBits2Float(0x43cb11ce), SkBits2Float(0x43524bc7), SkBits2Float(0x43cebb10));  // 217.618f, 406.139f, 210.296f, 413.461f
+path.quadTo(SkBits2Float(0x434af942), SkBits2Float(0x43d26452), SkBits2Float(0x43409e4b), SkBits2Float(0x43d26452));  // 202.974f, 420.784f, 192.618f, 420.784f
+path.quadTo(SkBits2Float(0x43364354), SkBits2Float(0x43d26452), SkBits2Float(0x432ef0cf), SkBits2Float(0x43cebb10));  // 182.263f, 420.784f, 174.941f, 413.461f
+path.quadTo(SkBits2Float(0x43279e4b), SkBits2Float(0x43cb11ce), SkBits2Float(0x43279e4b), SkBits2Float(0x43c5e452));  // 167.618f, 406.139f, 167.618f, 395.784f
+path.quadTo(SkBits2Float(0x43279e4b), SkBits2Float(0x43c0b6d6), SkBits2Float(0x432ef0cf), SkBits2Float(0x43bd0d94));  // 167.618f, 385.428f, 174.941f, 378.106f
+path.quadTo(SkBits2Float(0x43364354), SkBits2Float(0x43b96452), SkBits2Float(0x43409e4b), SkBits2Float(0x43b96452));  // 182.263f, 370.784f, 192.618f, 370.784f
+path.quadTo(SkBits2Float(0x434af942), SkBits2Float(0x43b96452), SkBits2Float(0x43524bc7), SkBits2Float(0x43bd0d94));  // 202.974f, 370.784f, 210.296f, 378.106f
+path.quadTo(SkBits2Float(0x43599e4b), SkBits2Float(0x43c0b6d6), SkBits2Float(0x43599e4b), SkBits2Float(0x43c5e452));  // 217.618f, 385.428f, 217.618f, 395.784f
+path.close();
+    SkPath path47(path);
+    builder.add(path47, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x435e28dc), SkBits2Float(0x43a32a69));  // 222.16f, 326.331f
+path.quadTo(SkBits2Float(0x435e28dc), SkBits2Float(0x43a857e5), SkBits2Float(0x4356d658), SkBits2Float(0x43ac0127));  // 222.16f, 336.687f, 214.837f, 344.009f
+path.quadTo(SkBits2Float(0x434f83d4), SkBits2Float(0x43afaa69), SkBits2Float(0x434528dc), SkBits2Float(0x43afaa69));  // 207.515f, 351.331f, 197.16f, 351.331f
+path.quadTo(SkBits2Float(0x433acde4), SkBits2Float(0x43afaa69), SkBits2Float(0x43337b60), SkBits2Float(0x43ac0127));  // 186.804f, 351.331f, 179.482f, 344.009f
+path.quadTo(SkBits2Float(0x432c28dc), SkBits2Float(0x43a857e5), SkBits2Float(0x432c28dc), SkBits2Float(0x43a32a69));  // 172.16f, 336.687f, 172.16f, 326.331f
+path.quadTo(SkBits2Float(0x432c28dc), SkBits2Float(0x439dfced), SkBits2Float(0x43337b60), SkBits2Float(0x439a53ab));  // 172.16f, 315.976f, 179.482f, 308.654f
+path.quadTo(SkBits2Float(0x433acde4), SkBits2Float(0x4396aa69), SkBits2Float(0x434528dc), SkBits2Float(0x4396aa69));  // 186.804f, 301.331f, 197.16f, 301.331f
+path.quadTo(SkBits2Float(0x434f83d4), SkBits2Float(0x4396aa69), SkBits2Float(0x4356d658), SkBits2Float(0x439a53ab));  // 207.515f, 301.331f, 214.837f, 308.654f
+path.quadTo(SkBits2Float(0x435e28dc), SkBits2Float(0x439dfced), SkBits2Float(0x435e28dc), SkBits2Float(0x43a32a69));  // 222.16f, 315.976f, 222.16f, 326.331f
+path.close();
+    SkPath path48(path);
+    builder.add(path48, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x433b4ff9), SkBits2Float(0x438034ac));  // 187.312f, 256.411f
+path.quadTo(SkBits2Float(0x433b4ff9), SkBits2Float(0x43856228), SkBits2Float(0x4333fd75), SkBits2Float(0x43890b6a));  // 187.312f, 266.767f, 179.99f, 274.089f
+path.quadTo(SkBits2Float(0x432caaf0), SkBits2Float(0x438cb4ac), SkBits2Float(0x43224ff9), SkBits2Float(0x438cb4ac));  // 172.668f, 281.411f, 162.312f, 281.411f
+path.quadTo(SkBits2Float(0x4317f502), SkBits2Float(0x438cb4ac), SkBits2Float(0x4310a27d), SkBits2Float(0x43890b6a));  // 151.957f, 281.411f, 144.635f, 274.089f
+path.quadTo(SkBits2Float(0x43094ff9), SkBits2Float(0x43856228), SkBits2Float(0x43094ff9), SkBits2Float(0x438034ac));  // 137.312f, 266.767f, 137.312f, 256.411f
+path.quadTo(SkBits2Float(0x43094ff9), SkBits2Float(0x43760e60), SkBits2Float(0x4310a27d), SkBits2Float(0x436ebbdc));  // 137.312f, 246.056f, 144.635f, 238.734f
+path.quadTo(SkBits2Float(0x4317f502), SkBits2Float(0x43676958), SkBits2Float(0x43224ff9), SkBits2Float(0x43676958));  // 151.957f, 231.411f, 162.312f, 231.411f
+path.quadTo(SkBits2Float(0x432caaf0), SkBits2Float(0x43676958), SkBits2Float(0x4333fd75), SkBits2Float(0x436ebbdc));  // 172.668f, 231.411f, 179.99f, 238.734f
+path.quadTo(SkBits2Float(0x433b4ff9), SkBits2Float(0x43760e60), SkBits2Float(0x433b4ff9), SkBits2Float(0x438034ac));  // 187.312f, 246.056f, 187.312f, 256.411f
+path.close();
+    SkPath path49(path);
+    builder.add(path49, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4346c2ee), SkBits2Float(0x435b284b));  // 198.761f, 219.157f
+path.quadTo(SkBits2Float(0x4346c2ee), SkBits2Float(0x43658342), SkBits2Float(0x433f706a), SkBits2Float(0x436cd5c7));  // 198.761f, 229.513f, 191.439f, 236.835f
+path.quadTo(SkBits2Float(0x43381de6), SkBits2Float(0x4374284b), SkBits2Float(0x432dc2ee), SkBits2Float(0x4374284b));  // 184.117f, 244.157f, 173.761f, 244.157f
+path.quadTo(SkBits2Float(0x432367f6), SkBits2Float(0x4374284b), SkBits2Float(0x431c1572), SkBits2Float(0x436cd5c7));  // 163.406f, 244.157f, 156.084f, 236.835f
+path.quadTo(SkBits2Float(0x4314c2ee), SkBits2Float(0x43658342), SkBits2Float(0x4314c2ee), SkBits2Float(0x435b284b));  // 148.761f, 229.513f, 148.761f, 219.157f
+path.quadTo(SkBits2Float(0x4314c2ee), SkBits2Float(0x4350cd54), SkBits2Float(0x431c1572), SkBits2Float(0x43497acf));  // 148.761f, 208.802f, 156.084f, 201.48f
+path.quadTo(SkBits2Float(0x432367f6), SkBits2Float(0x4342284b), SkBits2Float(0x432dc2ee), SkBits2Float(0x4342284b));  // 163.406f, 194.157f, 173.761f, 194.157f
+path.quadTo(SkBits2Float(0x43381de6), SkBits2Float(0x4342284b), SkBits2Float(0x433f706a), SkBits2Float(0x43497acf));  // 184.117f, 194.157f, 191.439f, 201.48f
+path.quadTo(SkBits2Float(0x4346c2ee), SkBits2Float(0x4350cd54), SkBits2Float(0x4346c2ee), SkBits2Float(0x435b284b));  // 198.761f, 208.802f, 198.761f, 219.157f
+path.close();
+    SkPath path50(path);
+    builder.add(path50, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43fb0cf6), SkBits2Float(0x438812a5));  // 502.101f, 272.146f
+path.quadTo(SkBits2Float(0x43fb0cf6), SkBits2Float(0x438d4021), SkBits2Float(0x43f763b4), SkBits2Float(0x4390e963));  // 502.101f, 282.501f, 494.779f, 289.823f
+path.quadTo(SkBits2Float(0x43f3ba72), SkBits2Float(0x439492a5), SkBits2Float(0x43ee8cf6), SkBits2Float(0x439492a5));  // 487.457f, 297.146f, 477.101f, 297.146f
+path.quadTo(SkBits2Float(0x43e95f7a), SkBits2Float(0x439492a5), SkBits2Float(0x43e5b638), SkBits2Float(0x4390e963));  // 466.746f, 297.146f, 459.424f, 289.823f
+path.quadTo(SkBits2Float(0x43e20cf6), SkBits2Float(0x438d4021), SkBits2Float(0x43e20cf6), SkBits2Float(0x438812a5));  // 452.101f, 282.501f, 452.101f, 272.146f
+path.quadTo(SkBits2Float(0x43e20cf6), SkBits2Float(0x4382e529), SkBits2Float(0x43e5b638), SkBits2Float(0x437e77ce));  // 452.101f, 261.79f, 459.424f, 254.468f
+path.quadTo(SkBits2Float(0x43e95f7a), SkBits2Float(0x4377254a), SkBits2Float(0x43ee8cf6), SkBits2Float(0x4377254a));  // 466.746f, 247.146f, 477.101f, 247.146f
+path.quadTo(SkBits2Float(0x43f3ba72), SkBits2Float(0x4377254a), SkBits2Float(0x43f763b4), SkBits2Float(0x437e77ce));  // 487.457f, 247.146f, 494.779f, 254.468f
+path.quadTo(SkBits2Float(0x43fb0cf6), SkBits2Float(0x4382e529), SkBits2Float(0x43fb0cf6), SkBits2Float(0x438812a5));  // 502.101f, 261.79f, 502.101f, 272.146f
+path.close();
+    SkPath path51(path);
+    builder.add(path51, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x438f42d6), SkBits2Float(0x435a09d4));  // 286.522f, 218.038f
+path.quadTo(SkBits2Float(0x438f42d6), SkBits2Float(0x436464cc), SkBits2Float(0x438b9994), SkBits2Float(0x436bb750));  // 286.522f, 228.394f, 279.2f, 235.716f
+path.quadTo(SkBits2Float(0x4387f052), SkBits2Float(0x437309d4), SkBits2Float(0x4382c2d6), SkBits2Float(0x437309d4));  // 271.878f, 243.038f, 261.522f, 243.038f
+path.quadTo(SkBits2Float(0x437b2ab4), SkBits2Float(0x437309d4), SkBits2Float(0x4373d830), SkBits2Float(0x436bb750));  // 251.167f, 243.038f, 243.844f, 235.716f
+path.quadTo(SkBits2Float(0x436c85ac), SkBits2Float(0x436464cc), SkBits2Float(0x436c85ac), SkBits2Float(0x435a09d4));  // 236.522f, 228.394f, 236.522f, 218.038f
+path.quadTo(SkBits2Float(0x436c85ac), SkBits2Float(0x434faedc), SkBits2Float(0x4373d830), SkBits2Float(0x43485c58));  // 236.522f, 207.683f, 243.844f, 200.361f
+path.quadTo(SkBits2Float(0x437b2ab4), SkBits2Float(0x434109d4), SkBits2Float(0x4382c2d6), SkBits2Float(0x434109d4));  // 251.167f, 193.038f, 261.522f, 193.038f
+path.quadTo(SkBits2Float(0x4387f052), SkBits2Float(0x434109d4), SkBits2Float(0x438b9994), SkBits2Float(0x43485c58));  // 271.878f, 193.038f, 279.2f, 200.361f
+path.quadTo(SkBits2Float(0x438f42d6), SkBits2Float(0x434faedc), SkBits2Float(0x438f42d6), SkBits2Float(0x435a09d4));  // 286.522f, 207.683f, 286.522f, 218.038f
+path.close();
+    SkPath path52(path);
+    builder.add(path52, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43ac18fb), SkBits2Float(0x43378440));  // 344.195f, 183.517f
+path.quadTo(SkBits2Float(0x43ac18fb), SkBits2Float(0x4341df38), SkBits2Float(0x43a86fb9), SkBits2Float(0x434931bc));  // 344.195f, 193.872f, 336.873f, 201.194f
+path.quadTo(SkBits2Float(0x43a4c677), SkBits2Float(0x43508440), SkBits2Float(0x439f98fb), SkBits2Float(0x43508440));  // 329.551f, 208.517f, 319.195f, 208.517f
+path.quadTo(SkBits2Float(0x439a6b7f), SkBits2Float(0x43508440), SkBits2Float(0x4396c23d), SkBits2Float(0x434931bc));  // 308.84f, 208.517f, 301.517f, 201.194f
+path.quadTo(SkBits2Float(0x439318fb), SkBits2Float(0x4341df38), SkBits2Float(0x439318fb), SkBits2Float(0x43378440));  // 294.195f, 193.872f, 294.195f, 183.517f
+path.quadTo(SkBits2Float(0x439318fb), SkBits2Float(0x432d2948), SkBits2Float(0x4396c23d), SkBits2Float(0x4325d6c4));  // 294.195f, 173.161f, 301.517f, 165.839f
+path.quadTo(SkBits2Float(0x439a6b7f), SkBits2Float(0x431e8440), SkBits2Float(0x439f98fb), SkBits2Float(0x431e8440));  // 308.84f, 158.517f, 319.195f, 158.517f
+path.quadTo(SkBits2Float(0x43a4c677), SkBits2Float(0x431e8440), SkBits2Float(0x43a86fb9), SkBits2Float(0x4325d6c4));  // 329.551f, 158.517f, 336.873f, 165.839f
+path.quadTo(SkBits2Float(0x43ac18fb), SkBits2Float(0x432d2948), SkBits2Float(0x43ac18fb), SkBits2Float(0x43378440));  // 344.195f, 173.161f, 344.195f, 183.517f
+path.close();
+    SkPath path53(path);
+    builder.add(path53, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42ef12a3), SkBits2Float(0x430c5faa));  // 119.536f, 140.374f
+path.quadTo(SkBits2Float(0x42ef12a3), SkBits2Float(0x4316baa2), SkBits2Float(0x42e06d9a), SkBits2Float(0x431e0d26));  // 119.536f, 150.729f, 112.214f, 158.051f
+path.quadTo(SkBits2Float(0x42d1c892), SkBits2Float(0x43255faa), SkBits2Float(0x42bd12a3), SkBits2Float(0x43255faa));  // 104.892f, 165.374f, 94.5364f, 165.374f
+path.quadTo(SkBits2Float(0x42a85cb4), SkBits2Float(0x43255faa), SkBits2Float(0x4299b7ac), SkBits2Float(0x431e0d26));  // 84.1811f, 165.374f, 76.8587f, 158.051f
+path.quadTo(SkBits2Float(0x428b12a3), SkBits2Float(0x4316baa2), SkBits2Float(0x428b12a3), SkBits2Float(0x430c5faa));  // 69.5364f, 150.729f, 69.5364f, 140.374f
+path.quadTo(SkBits2Float(0x428b12a3), SkBits2Float(0x430204b2), SkBits2Float(0x4299b7ac), SkBits2Float(0x42f5645c));  // 69.5364f, 130.018f, 76.8587f, 122.696f
+path.quadTo(SkBits2Float(0x42a85cb4), SkBits2Float(0x42e6bf54), SkBits2Float(0x42bd12a3), SkBits2Float(0x42e6bf54));  // 84.1811f, 115.374f, 94.5364f, 115.374f
+path.quadTo(SkBits2Float(0x42d1c892), SkBits2Float(0x42e6bf54), SkBits2Float(0x42e06d9a), SkBits2Float(0x42f5645c));  // 104.892f, 115.374f, 112.214f, 122.696f
+path.quadTo(SkBits2Float(0x42ef12a3), SkBits2Float(0x430204b2), SkBits2Float(0x42ef12a3), SkBits2Float(0x430c5faa));  // 119.536f, 130.018f, 119.536f, 140.374f
+path.close();
+    SkPath path54(path);
+    builder.add(path54, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43f569c1), SkBits2Float(0x43463314));  // 490.826f, 198.2f
+path.quadTo(SkBits2Float(0x43f569c1), SkBits2Float(0x43508e0c), SkBits2Float(0x43f1c07f), SkBits2Float(0x4357e090));  // 490.826f, 208.555f, 483.504f, 215.877f
+path.quadTo(SkBits2Float(0x43ee173d), SkBits2Float(0x435f3314), SkBits2Float(0x43e8e9c1), SkBits2Float(0x435f3314));  // 476.182f, 223.2f, 465.826f, 223.2f
+path.quadTo(SkBits2Float(0x43e3bc45), SkBits2Float(0x435f3314), SkBits2Float(0x43e01303), SkBits2Float(0x4357e090));  // 455.471f, 223.2f, 448.149f, 215.877f
+path.quadTo(SkBits2Float(0x43dc69c1), SkBits2Float(0x43508e0c), SkBits2Float(0x43dc69c1), SkBits2Float(0x43463314));  // 440.826f, 208.555f, 440.826f, 198.2f
+path.quadTo(SkBits2Float(0x43dc69c1), SkBits2Float(0x433bd81c), SkBits2Float(0x43e01303), SkBits2Float(0x43348598));  // 440.826f, 187.844f, 448.149f, 180.522f
+path.quadTo(SkBits2Float(0x43e3bc45), SkBits2Float(0x432d3314), SkBits2Float(0x43e8e9c1), SkBits2Float(0x432d3314));  // 455.471f, 173.2f, 465.826f, 173.2f
+path.quadTo(SkBits2Float(0x43ee173d), SkBits2Float(0x432d3314), SkBits2Float(0x43f1c07f), SkBits2Float(0x43348598));  // 476.182f, 173.2f, 483.504f, 180.522f
+path.quadTo(SkBits2Float(0x43f569c1), SkBits2Float(0x433bd81c), SkBits2Float(0x43f569c1), SkBits2Float(0x43463314));  // 490.826f, 187.844f, 490.826f, 198.2f
+path.close();
+    SkPath path55(path);
+    builder.add(path55, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4346ee50), SkBits2Float(0x4386bdd6));  // 198.931f, 269.483f
+path.quadTo(SkBits2Float(0x4346ee50), SkBits2Float(0x438beb52), SkBits2Float(0x433f9bcc), SkBits2Float(0x438f9494));  // 198.931f, 279.838f, 191.609f, 287.161f
+path.quadTo(SkBits2Float(0x43384948), SkBits2Float(0x43933dd6), SkBits2Float(0x432dee50), SkBits2Float(0x43933dd6));  // 184.286f, 294.483f, 173.931f, 294.483f
+path.quadTo(SkBits2Float(0x43239358), SkBits2Float(0x43933dd6), SkBits2Float(0x431c40d4), SkBits2Float(0x438f9494));  // 163.576f, 294.483f, 156.253f, 287.161f
+path.quadTo(SkBits2Float(0x4314ee50), SkBits2Float(0x438beb52), SkBits2Float(0x4314ee50), SkBits2Float(0x4386bdd6));  // 148.931f, 279.838f, 148.931f, 269.483f
+path.quadTo(SkBits2Float(0x4314ee50), SkBits2Float(0x4381905a), SkBits2Float(0x431c40d4), SkBits2Float(0x437bce30));  // 148.931f, 259.128f, 156.253f, 251.805f
+path.quadTo(SkBits2Float(0x43239358), SkBits2Float(0x43747bac), SkBits2Float(0x432dee50), SkBits2Float(0x43747bac));  // 163.576f, 244.483f, 173.931f, 244.483f
+path.quadTo(SkBits2Float(0x43384948), SkBits2Float(0x43747bac), SkBits2Float(0x433f9bcc), SkBits2Float(0x437bce30));  // 184.286f, 244.483f, 191.609f, 251.805f
+path.quadTo(SkBits2Float(0x4346ee50), SkBits2Float(0x4381905a), SkBits2Float(0x4346ee50), SkBits2Float(0x4386bdd6));  // 198.931f, 259.128f, 198.931f, 269.483f
+path.close();
+    SkPath path56(path);
+    builder.add(path56, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4403bd60), SkBits2Float(0x438666a7));  // 526.959f, 268.802f
+path.quadTo(SkBits2Float(0x4403bd60), SkBits2Float(0x438b9423), SkBits2Float(0x4401e8bf), SkBits2Float(0x438f3d65));  // 526.959f, 279.157f, 519.637f, 286.48f
+path.quadTo(SkBits2Float(0x4400141e), SkBits2Float(0x4392e6a7), SkBits2Float(0x43fafac0), SkBits2Float(0x4392e6a7));  // 512.314f, 293.802f, 501.959f, 293.802f
+path.quadTo(SkBits2Float(0x43f5cd44), SkBits2Float(0x4392e6a7), SkBits2Float(0x43f22402), SkBits2Float(0x438f3d65));  // 491.604f, 293.802f, 484.281f, 286.48f
+path.quadTo(SkBits2Float(0x43ee7ac1), SkBits2Float(0x438b9423), SkBits2Float(0x43ee7ac1), SkBits2Float(0x438666a7));  // 476.959f, 279.157f, 476.959f, 268.802f
+path.quadTo(SkBits2Float(0x43ee7ac1), SkBits2Float(0x4381392b), SkBits2Float(0x43f22402), SkBits2Float(0x437b1fd2));  // 476.959f, 258.447f, 484.281f, 251.124f
+path.quadTo(SkBits2Float(0x43f5cd44), SkBits2Float(0x4373cd4e), SkBits2Float(0x43fafac0), SkBits2Float(0x4373cd4e));  // 491.604f, 243.802f, 501.959f, 243.802f
+path.quadTo(SkBits2Float(0x4400141e), SkBits2Float(0x4373cd4e), SkBits2Float(0x4401e8bf), SkBits2Float(0x437b1fd2));  // 512.314f, 243.802f, 519.637f, 251.124f
+path.quadTo(SkBits2Float(0x4403bd60), SkBits2Float(0x4381392b), SkBits2Float(0x4403bd60), SkBits2Float(0x438666a7));  // 526.959f, 258.447f, 526.959f, 268.802f
+path.close();
+    SkPath path57(path);
+    builder.add(path57, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x433c6aff), SkBits2Float(0x439bf9f9));  // 188.418f, 311.953f
+path.quadTo(SkBits2Float(0x433c6aff), SkBits2Float(0x43a12775), SkBits2Float(0x4335187b), SkBits2Float(0x43a4d0b7));  // 188.418f, 322.308f, 181.096f, 329.631f
+path.quadTo(SkBits2Float(0x432dc5f6), SkBits2Float(0x43a879f9), SkBits2Float(0x43236aff), SkBits2Float(0x43a879f9));  // 173.773f, 336.953f, 163.418f, 336.953f
+path.quadTo(SkBits2Float(0x43191008), SkBits2Float(0x43a879f9), SkBits2Float(0x4311bd83), SkBits2Float(0x43a4d0b7));  // 153.063f, 336.953f, 145.74f, 329.631f
+path.quadTo(SkBits2Float(0x430a6aff), SkBits2Float(0x43a12775), SkBits2Float(0x430a6aff), SkBits2Float(0x439bf9f9));  // 138.418f, 322.308f, 138.418f, 311.953f
+path.quadTo(SkBits2Float(0x430a6aff), SkBits2Float(0x4396cc7d), SkBits2Float(0x4311bd83), SkBits2Float(0x4393233b));  // 138.418f, 301.598f, 145.74f, 294.275f
+path.quadTo(SkBits2Float(0x43191008), SkBits2Float(0x438f79f9), SkBits2Float(0x43236aff), SkBits2Float(0x438f79f9));  // 153.063f, 286.953f, 163.418f, 286.953f
+path.quadTo(SkBits2Float(0x432dc5f6), SkBits2Float(0x438f79f9), SkBits2Float(0x4335187b), SkBits2Float(0x4393233b));  // 173.773f, 286.953f, 181.096f, 294.275f
+path.quadTo(SkBits2Float(0x433c6aff), SkBits2Float(0x4396cc7d), SkBits2Float(0x433c6aff), SkBits2Float(0x439bf9f9));  // 188.418f, 301.598f, 188.418f, 311.953f
+path.close();
+    SkPath path58(path);
+    builder.add(path58, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4397b4f8), SkBits2Float(0x43598b8a));  // 303.414f, 217.545f
+path.quadTo(SkBits2Float(0x4397b4f8), SkBits2Float(0x4363e682), SkBits2Float(0x43940bb6), SkBits2Float(0x436b3906));  // 303.414f, 227.9f, 296.091f, 235.223f
+path.quadTo(SkBits2Float(0x43906274), SkBits2Float(0x43728b8a), SkBits2Float(0x438b34f8), SkBits2Float(0x43728b8a));  // 288.769f, 242.545f, 278.414f, 242.545f
+path.quadTo(SkBits2Float(0x4386077c), SkBits2Float(0x43728b8a), SkBits2Float(0x43825e3a), SkBits2Float(0x436b3906));  // 268.058f, 242.545f, 260.736f, 235.223f
+path.quadTo(SkBits2Float(0x437d69f0), SkBits2Float(0x4363e682), SkBits2Float(0x437d69f0), SkBits2Float(0x43598b8a));  // 253.414f, 227.9f, 253.414f, 217.545f
+path.quadTo(SkBits2Float(0x437d69f0), SkBits2Float(0x434f3092), SkBits2Float(0x43825e3a), SkBits2Float(0x4347de0e));  // 253.414f, 207.19f, 260.736f, 199.867f
+path.quadTo(SkBits2Float(0x4386077c), SkBits2Float(0x43408b8a), SkBits2Float(0x438b34f8), SkBits2Float(0x43408b8a));  // 268.058f, 192.545f, 278.414f, 192.545f
+path.quadTo(SkBits2Float(0x43906274), SkBits2Float(0x43408b8a), SkBits2Float(0x43940bb6), SkBits2Float(0x4347de0e));  // 288.769f, 192.545f, 296.091f, 199.867f
+path.quadTo(SkBits2Float(0x4397b4f8), SkBits2Float(0x434f3092), SkBits2Float(0x4397b4f8), SkBits2Float(0x43598b8a));  // 303.414f, 207.19f, 303.414f, 217.545f
+path.close();
+    SkPath path59(path);
+    builder.add(path59, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x430d7c0c), SkBits2Float(0x435ebbfb));  // 141.485f, 222.734f
+path.quadTo(SkBits2Float(0x430d7c0c), SkBits2Float(0x436916f2), SkBits2Float(0x43062988), SkBits2Float(0x43706977));  // 141.485f, 233.09f, 134.162f, 240.412f
+path.quadTo(SkBits2Float(0x42fdae07), SkBits2Float(0x4377bbfb), SkBits2Float(0x42e8f818), SkBits2Float(0x4377bbfb));  // 126.84f, 247.734f, 116.485f, 247.734f
+path.quadTo(SkBits2Float(0x42d44229), SkBits2Float(0x4377bbfb), SkBits2Float(0x42c59d20), SkBits2Float(0x43706977));  // 106.129f, 247.734f, 98.8069f, 240.412f
+path.quadTo(SkBits2Float(0x42b6f818), SkBits2Float(0x436916f2), SkBits2Float(0x42b6f818), SkBits2Float(0x435ebbfb));  // 91.4846f, 233.09f, 91.4846f, 222.734f
+path.quadTo(SkBits2Float(0x42b6f818), SkBits2Float(0x43546104), SkBits2Float(0x42c59d20), SkBits2Float(0x434d0e7f));  // 91.4846f, 212.379f, 98.8069f, 205.057f
+path.quadTo(SkBits2Float(0x42d44229), SkBits2Float(0x4345bbfb), SkBits2Float(0x42e8f818), SkBits2Float(0x4345bbfb));  // 106.129f, 197.734f, 116.485f, 197.734f
+path.quadTo(SkBits2Float(0x42fdae07), SkBits2Float(0x4345bbfb), SkBits2Float(0x43062988), SkBits2Float(0x434d0e7f));  // 126.84f, 197.734f, 134.162f, 205.057f
+path.quadTo(SkBits2Float(0x430d7c0c), SkBits2Float(0x43546104), SkBits2Float(0x430d7c0c), SkBits2Float(0x435ebbfb));  // 141.485f, 212.379f, 141.485f, 222.734f
+path.close();
+    SkPath path60(path);
+    builder.add(path60, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43b7303b), SkBits2Float(0x42e664c0));  // 366.377f, 115.197f
+path.quadTo(SkBits2Float(0x43b7303b), SkBits2Float(0x42fb1aaf), SkBits2Float(0x43b386f9), SkBits2Float(0x4304dfdc));  // 366.377f, 125.552f, 359.054f, 132.874f
+path.quadTo(SkBits2Float(0x43afddb7), SkBits2Float(0x430c3260), SkBits2Float(0x43aab03b), SkBits2Float(0x430c3260));  // 351.732f, 140.197f, 341.377f, 140.197f
+path.quadTo(SkBits2Float(0x43a582bf), SkBits2Float(0x430c3260), SkBits2Float(0x43a1d97d), SkBits2Float(0x4304dfdc));  // 331.021f, 140.197f, 323.699f, 132.874f
+path.quadTo(SkBits2Float(0x439e303b), SkBits2Float(0x42fb1aaf), SkBits2Float(0x439e303b), SkBits2Float(0x42e664c0));  // 316.377f, 125.552f, 316.377f, 115.197f
+path.quadTo(SkBits2Float(0x439e303b), SkBits2Float(0x42d1aed1), SkBits2Float(0x43a1d97d), SkBits2Float(0x42c309c8));  // 316.377f, 104.841f, 323.699f, 97.5191f
+path.quadTo(SkBits2Float(0x43a582bf), SkBits2Float(0x42b464bf), SkBits2Float(0x43aab03b), SkBits2Float(0x42b464bf));  // 331.021f, 90.1968f, 341.377f, 90.1968f
+path.quadTo(SkBits2Float(0x43afddb7), SkBits2Float(0x42b464bf), SkBits2Float(0x43b386f9), SkBits2Float(0x42c309c8));  // 351.732f, 90.1968f, 359.054f, 97.5191f
+path.quadTo(SkBits2Float(0x43b7303b), SkBits2Float(0x42d1aed1), SkBits2Float(0x43b7303b), SkBits2Float(0x42e664c0));  // 366.377f, 104.841f, 366.377f, 115.197f
+path.close();
+    SkPath path61(path);
+    builder.add(path61, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43ded748), SkBits2Float(0x43786398));  // 445.682f, 248.389f
+path.quadTo(SkBits2Float(0x43ded748), SkBits2Float(0x43815f48), SkBits2Float(0x43db2e06), SkBits2Float(0x4385088a));  // 445.682f, 258.744f, 438.36f, 266.067f
+path.quadTo(SkBits2Float(0x43d784c4), SkBits2Float(0x4388b1cc), SkBits2Float(0x43d25748), SkBits2Float(0x4388b1cc));  // 431.037f, 273.389f, 420.682f, 273.389f
+path.quadTo(SkBits2Float(0x43cd29cc), SkBits2Float(0x4388b1cc), SkBits2Float(0x43c9808a), SkBits2Float(0x4385088a));  // 410.327f, 273.389f, 403.004f, 266.067f
+path.quadTo(SkBits2Float(0x43c5d748), SkBits2Float(0x43815f48), SkBits2Float(0x43c5d748), SkBits2Float(0x43786398));  // 395.682f, 258.744f, 395.682f, 248.389f
+path.quadTo(SkBits2Float(0x43c5d748), SkBits2Float(0x436e08a1), SkBits2Float(0x43c9808a), SkBits2Float(0x4366b61d));  // 395.682f, 238.034f, 403.004f, 230.711f
+path.quadTo(SkBits2Float(0x43cd29cc), SkBits2Float(0x435f6399), SkBits2Float(0x43d25748), SkBits2Float(0x435f6399));  // 410.327f, 223.389f, 420.682f, 223.389f
+path.quadTo(SkBits2Float(0x43d784c4), SkBits2Float(0x435f6399), SkBits2Float(0x43db2e06), SkBits2Float(0x4366b61d));  // 431.037f, 223.389f, 438.36f, 230.711f
+path.quadTo(SkBits2Float(0x43ded748), SkBits2Float(0x436e08a1), SkBits2Float(0x43ded748), SkBits2Float(0x43786398));  // 445.682f, 238.034f, 445.682f, 248.389f
+path.close();
+    SkPath path62(path);
+    builder.add(path62, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43bbf04d), SkBits2Float(0x4397997d));  // 375.877f, 303.199f
+path.quadTo(SkBits2Float(0x43bbf04d), SkBits2Float(0x439cc6f9), SkBits2Float(0x43b8470b), SkBits2Float(0x43a0703b));  // 375.877f, 313.554f, 368.555f, 320.877f
+path.quadTo(SkBits2Float(0x43b49dc9), SkBits2Float(0x43a4197d), SkBits2Float(0x43af704d), SkBits2Float(0x43a4197d));  // 361.233f, 328.199f, 350.877f, 328.199f
+path.quadTo(SkBits2Float(0x43aa42d1), SkBits2Float(0x43a4197d), SkBits2Float(0x43a6998f), SkBits2Float(0x43a0703b));  // 340.522f, 328.199f, 333.2f, 320.877f
+path.quadTo(SkBits2Float(0x43a2f04d), SkBits2Float(0x439cc6f9), SkBits2Float(0x43a2f04d), SkBits2Float(0x4397997d));  // 325.877f, 313.554f, 325.877f, 303.199f
+path.quadTo(SkBits2Float(0x43a2f04d), SkBits2Float(0x43926c01), SkBits2Float(0x43a6998f), SkBits2Float(0x438ec2bf));  // 325.877f, 292.844f, 333.2f, 285.521f
+path.quadTo(SkBits2Float(0x43aa42d1), SkBits2Float(0x438b197d), SkBits2Float(0x43af704d), SkBits2Float(0x438b197d));  // 340.522f, 278.199f, 350.877f, 278.199f
+path.quadTo(SkBits2Float(0x43b49dc9), SkBits2Float(0x438b197d), SkBits2Float(0x43b8470b), SkBits2Float(0x438ec2bf));  // 361.233f, 278.199f, 368.555f, 285.521f
+path.quadTo(SkBits2Float(0x43bbf04d), SkBits2Float(0x43926c01), SkBits2Float(0x43bbf04d), SkBits2Float(0x4397997d));  // 375.877f, 292.844f, 375.877f, 303.199f
+path.close();
+    SkPath path63(path);
+    builder.add(path63, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43b62e97), SkBits2Float(0x4313ec95));  // 364.364f, 147.924f
+path.quadTo(SkBits2Float(0x43b62e97), SkBits2Float(0x431e478c), SkBits2Float(0x43b28555), SkBits2Float(0x43259a11));  // 364.364f, 158.279f, 357.042f, 165.602f
+path.quadTo(SkBits2Float(0x43aedc13), SkBits2Float(0x432cec95), SkBits2Float(0x43a9ae97), SkBits2Float(0x432cec95));  // 349.719f, 172.924f, 339.364f, 172.924f
+path.quadTo(SkBits2Float(0x43a4811b), SkBits2Float(0x432cec95), SkBits2Float(0x43a0d7d9), SkBits2Float(0x43259a11));  // 329.009f, 172.924f, 321.686f, 165.602f
+path.quadTo(SkBits2Float(0x439d2e97), SkBits2Float(0x431e478c), SkBits2Float(0x439d2e97), SkBits2Float(0x4313ec95));  // 314.364f, 158.279f, 314.364f, 147.924f
+path.quadTo(SkBits2Float(0x439d2e97), SkBits2Float(0x4309919e), SkBits2Float(0x43a0d7d9), SkBits2Float(0x43023f19));  // 314.364f, 137.569f, 321.686f, 130.246f
+path.quadTo(SkBits2Float(0x43a4811b), SkBits2Float(0x42f5d92a), SkBits2Float(0x43a9ae97), SkBits2Float(0x42f5d92a));  // 329.009f, 122.924f, 339.364f, 122.924f
+path.quadTo(SkBits2Float(0x43aedc13), SkBits2Float(0x42f5d92a), SkBits2Float(0x43b28555), SkBits2Float(0x43023f19));  // 349.719f, 122.924f, 357.042f, 130.246f
+path.quadTo(SkBits2Float(0x43b62e97), SkBits2Float(0x4309919e), SkBits2Float(0x43b62e97), SkBits2Float(0x4313ec95));  // 364.364f, 137.569f, 364.364f, 147.924f
+path.close();
+    SkPath path64(path);
+    builder.add(path64, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43ec86f7), SkBits2Float(0x43b37791));  // 473.054f, 358.934f
+path.quadTo(SkBits2Float(0x43ec86f7), SkBits2Float(0x43b8a50d), SkBits2Float(0x43e8ddb5), SkBits2Float(0x43bc4e4f));  // 473.054f, 369.289f, 465.732f, 376.612f
+path.quadTo(SkBits2Float(0x43e53473), SkBits2Float(0x43bff791), SkBits2Float(0x43e006f7), SkBits2Float(0x43bff791));  // 458.41f, 383.934f, 448.054f, 383.934f
+path.quadTo(SkBits2Float(0x43dad97b), SkBits2Float(0x43bff791), SkBits2Float(0x43d73039), SkBits2Float(0x43bc4e4f));  // 437.699f, 383.934f, 430.377f, 376.612f
+path.quadTo(SkBits2Float(0x43d386f7), SkBits2Float(0x43b8a50d), SkBits2Float(0x43d386f7), SkBits2Float(0x43b37791));  // 423.054f, 369.289f, 423.054f, 358.934f
+path.quadTo(SkBits2Float(0x43d386f7), SkBits2Float(0x43ae4a15), SkBits2Float(0x43d73039), SkBits2Float(0x43aaa0d3));  // 423.054f, 348.579f, 430.377f, 341.256f
+path.quadTo(SkBits2Float(0x43dad97b), SkBits2Float(0x43a6f791), SkBits2Float(0x43e006f7), SkBits2Float(0x43a6f791));  // 437.699f, 333.934f, 448.054f, 333.934f
+path.quadTo(SkBits2Float(0x43e53473), SkBits2Float(0x43a6f791), SkBits2Float(0x43e8ddb5), SkBits2Float(0x43aaa0d3));  // 458.41f, 333.934f, 465.732f, 341.256f
+path.quadTo(SkBits2Float(0x43ec86f7), SkBits2Float(0x43ae4a15), SkBits2Float(0x43ec86f7), SkBits2Float(0x43b37791));  // 473.054f, 348.579f, 473.054f, 358.934f
+path.close();
+    SkPath path65(path);
+    builder.add(path65, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43840826), SkBits2Float(0x43927b24));  // 264.064f, 292.962f
+path.quadTo(SkBits2Float(0x43840826), SkBits2Float(0x4397a8a0), SkBits2Float(0x43805ee4), SkBits2Float(0x439b51e2));  // 264.064f, 303.317f, 256.741f, 310.64f
+path.quadTo(SkBits2Float(0x43796b44), SkBits2Float(0x439efb24), SkBits2Float(0x436f104c), SkBits2Float(0x439efb24));  // 249.419f, 317.962f, 239.064f, 317.962f
+path.quadTo(SkBits2Float(0x4364b554), SkBits2Float(0x439efb24), SkBits2Float(0x435d62d0), SkBits2Float(0x439b51e2));  // 228.708f, 317.962f, 221.386f, 310.64f
+path.quadTo(SkBits2Float(0x4356104c), SkBits2Float(0x4397a8a0), SkBits2Float(0x4356104c), SkBits2Float(0x43927b24));  // 214.064f, 303.317f, 214.064f, 292.962f
+path.quadTo(SkBits2Float(0x4356104c), SkBits2Float(0x438d4da8), SkBits2Float(0x435d62d0), SkBits2Float(0x4389a466));  // 214.064f, 282.607f, 221.386f, 275.284f
+path.quadTo(SkBits2Float(0x4364b554), SkBits2Float(0x4385fb24), SkBits2Float(0x436f104c), SkBits2Float(0x4385fb24));  // 228.708f, 267.962f, 239.064f, 267.962f
+path.quadTo(SkBits2Float(0x43796b44), SkBits2Float(0x4385fb24), SkBits2Float(0x43805ee4), SkBits2Float(0x4389a466));  // 249.419f, 267.962f, 256.741f, 275.284f
+path.quadTo(SkBits2Float(0x43840826), SkBits2Float(0x438d4da8), SkBits2Float(0x43840826), SkBits2Float(0x43927b24));  // 264.064f, 282.607f, 264.064f, 292.962f
+path.close();
+    SkPath path66(path);
+    builder.add(path66, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4387c794), SkBits2Float(0x43567a78));  // 271.559f, 214.478f
+path.quadTo(SkBits2Float(0x4387c794), SkBits2Float(0x4360d570), SkBits2Float(0x43841e52), SkBits2Float(0x436827f4));  // 271.559f, 224.834f, 264.237f, 232.156f
+path.quadTo(SkBits2Float(0x43807510), SkBits2Float(0x436f7a78), SkBits2Float(0x43768f28), SkBits2Float(0x436f7a78));  // 256.915f, 239.478f, 246.559f, 239.478f
+path.quadTo(SkBits2Float(0x436c3430), SkBits2Float(0x436f7a78), SkBits2Float(0x4364e1ac), SkBits2Float(0x436827f4));  // 236.204f, 239.478f, 228.882f, 232.156f
+path.quadTo(SkBits2Float(0x435d8f27), SkBits2Float(0x4360d570), SkBits2Float(0x435d8f27), SkBits2Float(0x43567a78));  // 221.559f, 224.834f, 221.559f, 214.478f
+path.quadTo(SkBits2Float(0x435d8f27), SkBits2Float(0x434c1f80), SkBits2Float(0x4364e1ac), SkBits2Float(0x4344ccfc));  // 221.559f, 204.123f, 228.882f, 196.801f
+path.quadTo(SkBits2Float(0x436c3430), SkBits2Float(0x433d7a78), SkBits2Float(0x43768f28), SkBits2Float(0x433d7a78));  // 236.204f, 189.478f, 246.559f, 189.478f
+path.quadTo(SkBits2Float(0x43807510), SkBits2Float(0x433d7a78), SkBits2Float(0x43841e52), SkBits2Float(0x4344ccfc));  // 256.915f, 189.478f, 264.237f, 196.801f
+path.quadTo(SkBits2Float(0x4387c794), SkBits2Float(0x434c1f80), SkBits2Float(0x4387c794), SkBits2Float(0x43567a78));  // 271.559f, 204.123f, 271.559f, 214.478f
+path.close();
+    SkPath path67(path);
+    builder.add(path67, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43cfc71f), SkBits2Float(0x4314ea3c));  // 415.556f, 148.915f
+path.quadTo(SkBits2Float(0x43cfc71f), SkBits2Float(0x431f4534), SkBits2Float(0x43cc1ddd), SkBits2Float(0x432697b8));  // 415.556f, 159.27f, 408.233f, 166.593f
+path.quadTo(SkBits2Float(0x43c8749b), SkBits2Float(0x432dea3c), SkBits2Float(0x43c3471f), SkBits2Float(0x432dea3c));  // 400.911f, 173.915f, 390.556f, 173.915f
+path.quadTo(SkBits2Float(0x43be19a3), SkBits2Float(0x432dea3c), SkBits2Float(0x43ba7061), SkBits2Float(0x432697b8));  // 380.2f, 173.915f, 372.878f, 166.593f
+path.quadTo(SkBits2Float(0x43b6c71f), SkBits2Float(0x431f4534), SkBits2Float(0x43b6c71f), SkBits2Float(0x4314ea3c));  // 365.556f, 159.27f, 365.556f, 148.915f
+path.quadTo(SkBits2Float(0x43b6c71f), SkBits2Float(0x430a8f44), SkBits2Float(0x43ba7061), SkBits2Float(0x43033cc0));  // 365.556f, 138.56f, 372.878f, 131.237f
+path.quadTo(SkBits2Float(0x43be19a3), SkBits2Float(0x42f7d478), SkBits2Float(0x43c3471f), SkBits2Float(0x42f7d478));  // 380.2f, 123.915f, 390.556f, 123.915f
+path.quadTo(SkBits2Float(0x43c8749b), SkBits2Float(0x42f7d478), SkBits2Float(0x43cc1ddd), SkBits2Float(0x43033cc0));  // 400.911f, 123.915f, 408.233f, 131.237f
+path.quadTo(SkBits2Float(0x43cfc71f), SkBits2Float(0x430a8f44), SkBits2Float(0x43cfc71f), SkBits2Float(0x4314ea3c));  // 415.556f, 138.56f, 415.556f, 148.915f
+path.close();
+    SkPath path68(path);
+    builder.add(path68, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43a48336), SkBits2Float(0x4336f503));  // 329.025f, 182.957f
+path.quadTo(SkBits2Float(0x43a48336), SkBits2Float(0x43414ffa), SkBits2Float(0x43a0d9f4), SkBits2Float(0x4348a27f));  // 329.025f, 193.312f, 321.703f, 200.635f
+path.quadTo(SkBits2Float(0x439d30b2), SkBits2Float(0x434ff503), SkBits2Float(0x43980336), SkBits2Float(0x434ff503));  // 314.38f, 207.957f, 304.025f, 207.957f
+path.quadTo(SkBits2Float(0x4392d5ba), SkBits2Float(0x434ff503), SkBits2Float(0x438f2c78), SkBits2Float(0x4348a27f));  // 293.67f, 207.957f, 286.347f, 200.635f
+path.quadTo(SkBits2Float(0x438b8336), SkBits2Float(0x43414ffa), SkBits2Float(0x438b8336), SkBits2Float(0x4336f503));  // 279.025f, 193.312f, 279.025f, 182.957f
+path.quadTo(SkBits2Float(0x438b8336), SkBits2Float(0x432c9a0c), SkBits2Float(0x438f2c78), SkBits2Float(0x43254787));  // 279.025f, 172.602f, 286.347f, 165.279f
+path.quadTo(SkBits2Float(0x4392d5ba), SkBits2Float(0x431df503), SkBits2Float(0x43980336), SkBits2Float(0x431df503));  // 293.67f, 157.957f, 304.025f, 157.957f
+path.quadTo(SkBits2Float(0x439d30b2), SkBits2Float(0x431df503), SkBits2Float(0x43a0d9f4), SkBits2Float(0x43254787));  // 314.38f, 157.957f, 321.703f, 165.279f
+path.quadTo(SkBits2Float(0x43a48336), SkBits2Float(0x432c9a0c), SkBits2Float(0x43a48336), SkBits2Float(0x4336f503));  // 329.025f, 172.602f, 329.025f, 182.957f
+path.close();
+    SkPath path69(path);
+    builder.add(path69, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x439a774e), SkBits2Float(0x43a57837));  // 308.932f, 330.939f
+path.quadTo(SkBits2Float(0x439a774e), SkBits2Float(0x43aaa5b3), SkBits2Float(0x4396ce0c), SkBits2Float(0x43ae4ef5));  // 308.932f, 341.295f, 301.61f, 348.617f
+path.quadTo(SkBits2Float(0x439324ca), SkBits2Float(0x43b1f837), SkBits2Float(0x438df74e), SkBits2Float(0x43b1f837));  // 294.287f, 355.939f, 283.932f, 355.939f
+path.quadTo(SkBits2Float(0x4388c9d2), SkBits2Float(0x43b1f837), SkBits2Float(0x43852090), SkBits2Float(0x43ae4ef5));  // 273.577f, 355.939f, 266.254f, 348.617f
+path.quadTo(SkBits2Float(0x4381774e), SkBits2Float(0x43aaa5b3), SkBits2Float(0x4381774e), SkBits2Float(0x43a57837));  // 258.932f, 341.295f, 258.932f, 330.939f
+path.quadTo(SkBits2Float(0x4381774e), SkBits2Float(0x43a04abb), SkBits2Float(0x43852090), SkBits2Float(0x439ca179));  // 258.932f, 320.584f, 266.254f, 313.262f
+path.quadTo(SkBits2Float(0x4388c9d2), SkBits2Float(0x4398f837), SkBits2Float(0x438df74e), SkBits2Float(0x4398f837));  // 273.577f, 305.939f, 283.932f, 305.939f
+path.quadTo(SkBits2Float(0x439324ca), SkBits2Float(0x4398f837), SkBits2Float(0x4396ce0c), SkBits2Float(0x439ca179));  // 294.287f, 305.939f, 301.61f, 313.262f
+path.quadTo(SkBits2Float(0x439a774e), SkBits2Float(0x43a04abb), SkBits2Float(0x439a774e), SkBits2Float(0x43a57837));  // 308.932f, 320.584f, 308.932f, 330.939f
+path.close();
+    SkPath path70(path);
+    builder.add(path70, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x439be624), SkBits2Float(0x438cec6f));  // 311.798f, 281.847f
+path.quadTo(SkBits2Float(0x439be624), SkBits2Float(0x439219eb), SkBits2Float(0x43983ce2), SkBits2Float(0x4395c32d));  // 311.798f, 292.202f, 304.476f, 299.525f
+path.quadTo(SkBits2Float(0x439493a0), SkBits2Float(0x43996c6f), SkBits2Float(0x438f6624), SkBits2Float(0x43996c6f));  // 297.153f, 306.847f, 286.798f, 306.847f
+path.quadTo(SkBits2Float(0x438a38a8), SkBits2Float(0x43996c6f), SkBits2Float(0x43868f66), SkBits2Float(0x4395c32d));  // 276.443f, 306.847f, 269.12f, 299.525f
+path.quadTo(SkBits2Float(0x4382e624), SkBits2Float(0x439219eb), SkBits2Float(0x4382e624), SkBits2Float(0x438cec6f));  // 261.798f, 292.202f, 261.798f, 281.847f
+path.quadTo(SkBits2Float(0x4382e624), SkBits2Float(0x4387bef3), SkBits2Float(0x43868f66), SkBits2Float(0x438415b1));  // 261.798f, 271.492f, 269.12f, 264.169f
+path.quadTo(SkBits2Float(0x438a38a8), SkBits2Float(0x43806c6f), SkBits2Float(0x438f6624), SkBits2Float(0x43806c6f));  // 276.443f, 256.847f, 286.798f, 256.847f
+path.quadTo(SkBits2Float(0x439493a0), SkBits2Float(0x43806c6f), SkBits2Float(0x43983ce2), SkBits2Float(0x438415b1));  // 297.153f, 256.847f, 304.476f, 264.169f
+path.quadTo(SkBits2Float(0x439be624), SkBits2Float(0x4387bef3), SkBits2Float(0x439be624), SkBits2Float(0x438cec6f));  // 311.798f, 271.492f, 311.798f, 281.847f
+path.close();
+    SkPath path71(path);
+    builder.add(path71, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43714851), SkBits2Float(0x43aff2c0));  // 241.282f, 351.896f
+path.quadTo(SkBits2Float(0x43714851), SkBits2Float(0x43b5203c), SkBits2Float(0x4369f5cd), SkBits2Float(0x43b8c97e));  // 241.282f, 362.252f, 233.96f, 369.574f
+path.quadTo(SkBits2Float(0x4362a348), SkBits2Float(0x43bc72c0), SkBits2Float(0x43584851), SkBits2Float(0x43bc72c0));  // 226.638f, 376.896f, 216.282f, 376.896f
+path.quadTo(SkBits2Float(0x434ded5a), SkBits2Float(0x43bc72c0), SkBits2Float(0x43469ad5), SkBits2Float(0x43b8c97e));  // 205.927f, 376.896f, 198.605f, 369.574f
+path.quadTo(SkBits2Float(0x433f4851), SkBits2Float(0x43b5203c), SkBits2Float(0x433f4851), SkBits2Float(0x43aff2c0));  // 191.282f, 362.252f, 191.282f, 351.896f
+path.quadTo(SkBits2Float(0x433f4851), SkBits2Float(0x43aac544), SkBits2Float(0x43469ad5), SkBits2Float(0x43a71c02));  // 191.282f, 341.541f, 198.605f, 334.219f
+path.quadTo(SkBits2Float(0x434ded5a), SkBits2Float(0x43a372c0), SkBits2Float(0x43584851), SkBits2Float(0x43a372c0));  // 205.927f, 326.896f, 216.282f, 326.896f
+path.quadTo(SkBits2Float(0x4362a348), SkBits2Float(0x43a372c0), SkBits2Float(0x4369f5cd), SkBits2Float(0x43a71c02));  // 226.638f, 326.896f, 233.96f, 334.219f
+path.quadTo(SkBits2Float(0x43714851), SkBits2Float(0x43aac544), SkBits2Float(0x43714851), SkBits2Float(0x43aff2c0));  // 241.282f, 341.541f, 241.282f, 351.896f
+path.close();
+    SkPath path72(path);
+    builder.add(path72, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43e644d3), SkBits2Float(0x43041b70));  // 460.538f, 132.107f
+path.quadTo(SkBits2Float(0x43e644d3), SkBits2Float(0x430e7668), SkBits2Float(0x43e29b91), SkBits2Float(0x4315c8ec));  // 460.538f, 142.463f, 453.215f, 149.785f
+path.quadTo(SkBits2Float(0x43def24f), SkBits2Float(0x431d1b70), SkBits2Float(0x43d9c4d3), SkBits2Float(0x431d1b70));  // 445.893f, 157.107f, 435.538f, 157.107f
+path.quadTo(SkBits2Float(0x43d49757), SkBits2Float(0x431d1b70), SkBits2Float(0x43d0ee15), SkBits2Float(0x4315c8ec));  // 425.182f, 157.107f, 417.86f, 149.785f
+path.quadTo(SkBits2Float(0x43cd44d3), SkBits2Float(0x430e7668), SkBits2Float(0x43cd44d3), SkBits2Float(0x43041b70));  // 410.538f, 142.463f, 410.538f, 132.107f
+path.quadTo(SkBits2Float(0x43cd44d3), SkBits2Float(0x42f380f1), SkBits2Float(0x43d0ee15), SkBits2Float(0x42e4dbe8));  // 410.538f, 121.752f, 417.86f, 114.43f
+path.quadTo(SkBits2Float(0x43d49757), SkBits2Float(0x42d636e0), SkBits2Float(0x43d9c4d3), SkBits2Float(0x42d636e0));  // 425.182f, 107.107f, 435.538f, 107.107f
+path.quadTo(SkBits2Float(0x43def24f), SkBits2Float(0x42d636e0), SkBits2Float(0x43e29b91), SkBits2Float(0x42e4dbe8));  // 445.893f, 107.107f, 453.215f, 114.43f
+path.quadTo(SkBits2Float(0x43e644d3), SkBits2Float(0x42f380f1), SkBits2Float(0x43e644d3), SkBits2Float(0x43041b70));  // 460.538f, 121.752f, 460.538f, 132.107f
+path.close();
+    SkPath path73(path);
+    builder.add(path73, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43c29be7), SkBits2Float(0x4366bcd5));  // 389.218f, 230.738f
+path.quadTo(SkBits2Float(0x43c29be7), SkBits2Float(0x437117cc), SkBits2Float(0x43bef2a5), SkBits2Float(0x43786a51));  // 389.218f, 241.093f, 381.896f, 248.415f
+path.quadTo(SkBits2Float(0x43bb4963), SkBits2Float(0x437fbcd5), SkBits2Float(0x43b61be7), SkBits2Float(0x437fbcd5));  // 374.573f, 255.738f, 364.218f, 255.738f
+path.quadTo(SkBits2Float(0x43b0ee6b), SkBits2Float(0x437fbcd5), SkBits2Float(0x43ad4529), SkBits2Float(0x43786a51));  // 353.863f, 255.738f, 346.54f, 248.415f
+path.quadTo(SkBits2Float(0x43a99be7), SkBits2Float(0x437117cc), SkBits2Float(0x43a99be7), SkBits2Float(0x4366bcd5));  // 339.218f, 241.093f, 339.218f, 230.738f
+path.quadTo(SkBits2Float(0x43a99be7), SkBits2Float(0x435c61de), SkBits2Float(0x43ad4529), SkBits2Float(0x43550f59));  // 339.218f, 220.382f, 346.54f, 213.06f
+path.quadTo(SkBits2Float(0x43b0ee6b), SkBits2Float(0x434dbcd5), SkBits2Float(0x43b61be7), SkBits2Float(0x434dbcd5));  // 353.863f, 205.738f, 364.218f, 205.738f
+path.quadTo(SkBits2Float(0x43bb4963), SkBits2Float(0x434dbcd5), SkBits2Float(0x43bef2a5), SkBits2Float(0x43550f59));  // 374.573f, 205.738f, 381.896f, 213.06f
+path.quadTo(SkBits2Float(0x43c29be7), SkBits2Float(0x435c61de), SkBits2Float(0x43c29be7), SkBits2Float(0x4366bcd5));  // 389.218f, 220.382f, 389.218f, 230.738f
+path.close();
+    SkPath path74(path);
+    builder.add(path74, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43b8637d), SkBits2Float(0x435f209d));  // 368.777f, 223.127f
+path.quadTo(SkBits2Float(0x43b8637d), SkBits2Float(0x43697b94), SkBits2Float(0x43b4ba3b), SkBits2Float(0x4370ce19));  // 368.777f, 233.483f, 361.455f, 240.805f
+path.quadTo(SkBits2Float(0x43b110f9), SkBits2Float(0x4378209d), SkBits2Float(0x43abe37d), SkBits2Float(0x4378209d));  // 354.133f, 248.127f, 343.777f, 248.127f
+path.quadTo(SkBits2Float(0x43a6b601), SkBits2Float(0x4378209d), SkBits2Float(0x43a30cbf), SkBits2Float(0x4370ce19));  // 333.422f, 248.127f, 326.1f, 240.805f
+path.quadTo(SkBits2Float(0x439f637d), SkBits2Float(0x43697b94), SkBits2Float(0x439f637d), SkBits2Float(0x435f209d));  // 318.777f, 233.483f, 318.777f, 223.127f
+path.quadTo(SkBits2Float(0x439f637d), SkBits2Float(0x4354c5a6), SkBits2Float(0x43a30cbf), SkBits2Float(0x434d7321));  // 318.777f, 212.772f, 326.1f, 205.45f
+path.quadTo(SkBits2Float(0x43a6b601), SkBits2Float(0x4346209d), SkBits2Float(0x43abe37d), SkBits2Float(0x4346209d));  // 333.422f, 198.127f, 343.777f, 198.127f
+path.quadTo(SkBits2Float(0x43b110f9), SkBits2Float(0x4346209d), SkBits2Float(0x43b4ba3b), SkBits2Float(0x434d7321));  // 354.133f, 198.127f, 361.455f, 205.45f
+path.quadTo(SkBits2Float(0x43b8637d), SkBits2Float(0x4354c5a6), SkBits2Float(0x43b8637d), SkBits2Float(0x435f209d));  // 368.777f, 212.772f, 368.777f, 223.127f
+path.close();
+    SkPath path75(path);
+    builder.add(path75, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43a3ec8f), SkBits2Float(0x435dba35));  // 327.848f, 221.727f
+path.quadTo(SkBits2Float(0x43a3ec8f), SkBits2Float(0x4368152c), SkBits2Float(0x43a0434d), SkBits2Float(0x436f67b1));  // 327.848f, 232.083f, 320.526f, 239.405f
+path.quadTo(SkBits2Float(0x439c9a0b), SkBits2Float(0x4376ba35), SkBits2Float(0x43976c8f), SkBits2Float(0x4376ba35));  // 313.203f, 246.727f, 302.848f, 246.727f
+path.quadTo(SkBits2Float(0x43923f13), SkBits2Float(0x4376ba35), SkBits2Float(0x438e95d1), SkBits2Float(0x436f67b1));  // 292.493f, 246.727f, 285.17f, 239.405f
+path.quadTo(SkBits2Float(0x438aec8f), SkBits2Float(0x4368152c), SkBits2Float(0x438aec8f), SkBits2Float(0x435dba35));  // 277.848f, 232.083f, 277.848f, 221.727f
+path.quadTo(SkBits2Float(0x438aec8f), SkBits2Float(0x43535f3e), SkBits2Float(0x438e95d1), SkBits2Float(0x434c0cb9));  // 277.848f, 211.372f, 285.17f, 204.05f
+path.quadTo(SkBits2Float(0x43923f13), SkBits2Float(0x4344ba35), SkBits2Float(0x43976c8f), SkBits2Float(0x4344ba35));  // 292.493f, 196.727f, 302.848f, 196.727f
+path.quadTo(SkBits2Float(0x439c9a0b), SkBits2Float(0x4344ba35), SkBits2Float(0x43a0434d), SkBits2Float(0x434c0cb9));  // 313.203f, 196.727f, 320.526f, 204.05f
+path.quadTo(SkBits2Float(0x43a3ec8f), SkBits2Float(0x43535f3e), SkBits2Float(0x43a3ec8f), SkBits2Float(0x435dba35));  // 327.848f, 211.372f, 327.848f, 221.727f
+path.close();
+    SkPath path76(path);
+    builder.add(path76, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4408ede5), SkBits2Float(0x436af388));  // 547.717f, 234.951f
+path.quadTo(SkBits2Float(0x4408ede5), SkBits2Float(0x43754e7f), SkBits2Float(0x44071944), SkBits2Float(0x437ca103));  // 547.717f, 245.307f, 540.395f, 252.629f
+path.quadTo(SkBits2Float(0x440544a3), SkBits2Float(0x4381f9c4), SkBits2Float(0x4402ade5), SkBits2Float(0x4381f9c4));  // 533.072f, 259.951f, 522.717f, 259.951f
+path.quadTo(SkBits2Float(0x44001727), SkBits2Float(0x4381f9c4), SkBits2Float(0x43fc850c), SkBits2Float(0x437ca103));  // 512.362f, 259.951f, 505.039f, 252.629f
+path.quadTo(SkBits2Float(0x43f8dbca), SkBits2Float(0x43754e7f), SkBits2Float(0x43f8dbca), SkBits2Float(0x436af388));  // 497.717f, 245.307f, 497.717f, 234.951f
+path.quadTo(SkBits2Float(0x43f8dbca), SkBits2Float(0x43609891), SkBits2Float(0x43fc850c), SkBits2Float(0x4359460d));  // 497.717f, 224.596f, 505.039f, 217.274f
+path.quadTo(SkBits2Float(0x44001727), SkBits2Float(0x4351f389), SkBits2Float(0x4402ade5), SkBits2Float(0x4351f389));  // 512.362f, 209.951f, 522.717f, 209.951f
+path.quadTo(SkBits2Float(0x440544a3), SkBits2Float(0x4351f389), SkBits2Float(0x44071944), SkBits2Float(0x4359460d));  // 533.072f, 209.951f, 540.395f, 217.274f
+path.quadTo(SkBits2Float(0x4408ede5), SkBits2Float(0x43609891), SkBits2Float(0x4408ede5), SkBits2Float(0x436af388));  // 547.717f, 224.596f, 547.717f, 234.951f
+path.close();
+    SkPath path77(path);
+    builder.add(path77, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43a06718), SkBits2Float(0x43848d47));  // 320.805f, 265.104f
+path.quadTo(SkBits2Float(0x43a06718), SkBits2Float(0x4389bac3), SkBits2Float(0x439cbdd6), SkBits2Float(0x438d6405));  // 320.805f, 275.459f, 313.483f, 282.781f
+path.quadTo(SkBits2Float(0x43991494), SkBits2Float(0x43910d47), SkBits2Float(0x4393e718), SkBits2Float(0x43910d47));  // 306.161f, 290.104f, 295.805f, 290.104f
+path.quadTo(SkBits2Float(0x438eb99c), SkBits2Float(0x43910d47), SkBits2Float(0x438b105a), SkBits2Float(0x438d6405));  // 285.45f, 290.104f, 278.128f, 282.781f
+path.quadTo(SkBits2Float(0x43876718), SkBits2Float(0x4389bac3), SkBits2Float(0x43876718), SkBits2Float(0x43848d47));  // 270.805f, 275.459f, 270.805f, 265.104f
+path.quadTo(SkBits2Float(0x43876718), SkBits2Float(0x437ebf96), SkBits2Float(0x438b105a), SkBits2Float(0x43776d12));  // 270.805f, 254.748f, 278.128f, 247.426f
+path.quadTo(SkBits2Float(0x438eb99c), SkBits2Float(0x43701a8e), SkBits2Float(0x4393e718), SkBits2Float(0x43701a8e));  // 285.45f, 240.104f, 295.805f, 240.104f
+path.quadTo(SkBits2Float(0x43991494), SkBits2Float(0x43701a8e), SkBits2Float(0x439cbdd6), SkBits2Float(0x43776d12));  // 306.161f, 240.104f, 313.483f, 247.426f
+path.quadTo(SkBits2Float(0x43a06718), SkBits2Float(0x437ebf96), SkBits2Float(0x43a06718), SkBits2Float(0x43848d47));  // 320.805f, 254.748f, 320.805f, 265.104f
+path.close();
+    SkPath path78(path);
+    builder.add(path78, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43a4f721), SkBits2Float(0x439dea2f));  // 329.931f, 315.83f
+path.quadTo(SkBits2Float(0x43a4f721), SkBits2Float(0x43a317ab), SkBits2Float(0x43a14ddf), SkBits2Float(0x43a6c0ed));  // 329.931f, 326.185f, 322.608f, 333.507f
+path.quadTo(SkBits2Float(0x439da49d), SkBits2Float(0x43aa6a2f), SkBits2Float(0x43987721), SkBits2Float(0x43aa6a2f));  // 315.286f, 340.83f, 304.931f, 340.83f
+path.quadTo(SkBits2Float(0x439349a5), SkBits2Float(0x43aa6a2f), SkBits2Float(0x438fa063), SkBits2Float(0x43a6c0ed));  // 294.575f, 340.83f, 287.253f, 333.507f
+path.quadTo(SkBits2Float(0x438bf721), SkBits2Float(0x43a317ab), SkBits2Float(0x438bf721), SkBits2Float(0x439dea2f));  // 279.931f, 326.185f, 279.931f, 315.83f
+path.quadTo(SkBits2Float(0x438bf721), SkBits2Float(0x4398bcb3), SkBits2Float(0x438fa063), SkBits2Float(0x43951371));  // 279.931f, 305.474f, 287.253f, 298.152f
+path.quadTo(SkBits2Float(0x439349a5), SkBits2Float(0x43916a2f), SkBits2Float(0x43987721), SkBits2Float(0x43916a2f));  // 294.575f, 290.83f, 304.931f, 290.83f
+path.quadTo(SkBits2Float(0x439da49d), SkBits2Float(0x43916a2f), SkBits2Float(0x43a14ddf), SkBits2Float(0x43951371));  // 315.286f, 290.83f, 322.608f, 298.152f
+path.quadTo(SkBits2Float(0x43a4f721), SkBits2Float(0x4398bcb3), SkBits2Float(0x43a4f721), SkBits2Float(0x439dea2f));  // 329.931f, 305.474f, 329.931f, 315.83f
+path.close();
+    SkPath path79(path);
+    builder.add(path79, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4343f4a9), SkBits2Float(0x434a2b20));  // 195.956f, 202.168f
+path.quadTo(SkBits2Float(0x4343f4a9), SkBits2Float(0x43548618), SkBits2Float(0x433ca225), SkBits2Float(0x435bd89c));  // 195.956f, 212.524f, 188.633f, 219.846f
+path.quadTo(SkBits2Float(0x43354fa0), SkBits2Float(0x43632b20), SkBits2Float(0x432af4a9), SkBits2Float(0x43632b20));  // 181.311f, 227.168f, 170.956f, 227.168f
+path.quadTo(SkBits2Float(0x432099b2), SkBits2Float(0x43632b20), SkBits2Float(0x4319472d), SkBits2Float(0x435bd89c));  // 160.6f, 227.168f, 153.278f, 219.846f
+path.quadTo(SkBits2Float(0x4311f4a9), SkBits2Float(0x43548618), SkBits2Float(0x4311f4a9), SkBits2Float(0x434a2b20));  // 145.956f, 212.524f, 145.956f, 202.168f
+path.quadTo(SkBits2Float(0x4311f4a9), SkBits2Float(0x433fd028), SkBits2Float(0x4319472d), SkBits2Float(0x43387da4));  // 145.956f, 191.813f, 153.278f, 184.491f
+path.quadTo(SkBits2Float(0x432099b2), SkBits2Float(0x43312b20), SkBits2Float(0x432af4a9), SkBits2Float(0x43312b20));  // 160.6f, 177.168f, 170.956f, 177.168f
+path.quadTo(SkBits2Float(0x43354fa0), SkBits2Float(0x43312b20), SkBits2Float(0x433ca225), SkBits2Float(0x43387da4));  // 181.311f, 177.168f, 188.633f, 184.491f
+path.quadTo(SkBits2Float(0x4343f4a9), SkBits2Float(0x433fd028), SkBits2Float(0x4343f4a9), SkBits2Float(0x434a2b20));  // 195.956f, 191.813f, 195.956f, 202.168f
+path.close();
+    SkPath path80(path);
+    builder.add(path80, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4281a60e), SkBits2Float(0x42f78d6e));  // 64.8243f, 123.776f
+path.quadTo(SkBits2Float(0x4281a60e), SkBits2Float(0x430621ae), SkBits2Float(0x4266020c), SkBits2Float(0x430d7433));  // 64.8243f, 134.132f, 57.502f, 141.454f
+path.quadTo(SkBits2Float(0x4248b7fa), SkBits2Float(0x4314c6b7), SkBits2Float(0x421f4c1c), SkBits2Float(0x4314c6b7));  // 50.1797f, 148.776f, 39.8243f, 148.776f
+path.quadTo(SkBits2Float(0x41ebc07c), SkBits2Float(0x4314c6b7), SkBits2Float(0x41b12c59), SkBits2Float(0x430d7433));  // 29.469f, 148.776f, 22.1467f, 141.454f
+path.quadTo(SkBits2Float(0x416d306c), SkBits2Float(0x430621ae), SkBits2Float(0x416d306c), SkBits2Float(0x42f78d6e));  // 14.8243f, 134.132f, 14.8243f, 123.776f
+path.quadTo(SkBits2Float(0x416d306c), SkBits2Float(0x42e2d77f), SkBits2Float(0x41b12c59), SkBits2Float(0x42d43276));  // 14.8243f, 113.421f, 22.1467f, 106.099f
+path.quadTo(SkBits2Float(0x41ebc07c), SkBits2Float(0x42c58d6e), SkBits2Float(0x421f4c1c), SkBits2Float(0x42c58d6e));  // 29.469f, 98.7762f, 39.8243f, 98.7762f
+path.quadTo(SkBits2Float(0x4248b7fa), SkBits2Float(0x42c58d6e), SkBits2Float(0x4266020c), SkBits2Float(0x42d43276));  // 50.1797f, 98.7762f, 57.502f, 106.099f
+path.quadTo(SkBits2Float(0x4281a60e), SkBits2Float(0x42e2d77f), SkBits2Float(0x4281a60e), SkBits2Float(0x42f78d6e));  // 64.8243f, 113.421f, 64.8243f, 123.776f
+path.close();
+    SkPath path81(path);
+    builder.add(path81, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43740113), SkBits2Float(0x4363dcf0));  // 244.004f, 227.863f
+path.quadTo(SkBits2Float(0x43740113), SkBits2Float(0x436e37e8), SkBits2Float(0x436cae8f), SkBits2Float(0x43758a6c));  // 244.004f, 238.218f, 236.682f, 245.541f
+path.quadTo(SkBits2Float(0x43655c0a), SkBits2Float(0x437cdcf0), SkBits2Float(0x435b0113), SkBits2Float(0x437cdcf0));  // 229.36f, 252.863f, 219.004f, 252.863f
+path.quadTo(SkBits2Float(0x4350a61c), SkBits2Float(0x437cdcf0), SkBits2Float(0x43495397), SkBits2Float(0x43758a6c));  // 208.649f, 252.863f, 201.327f, 245.541f
+path.quadTo(SkBits2Float(0x43420113), SkBits2Float(0x436e37e8), SkBits2Float(0x43420113), SkBits2Float(0x4363dcf0));  // 194.004f, 238.218f, 194.004f, 227.863f
+path.quadTo(SkBits2Float(0x43420113), SkBits2Float(0x435981f8), SkBits2Float(0x43495397), SkBits2Float(0x43522f74));  // 194.004f, 217.508f, 201.327f, 210.185f
+path.quadTo(SkBits2Float(0x4350a61c), SkBits2Float(0x434adcf0), SkBits2Float(0x435b0113), SkBits2Float(0x434adcf0));  // 208.649f, 202.863f, 219.004f, 202.863f
+path.quadTo(SkBits2Float(0x43655c0a), SkBits2Float(0x434adcf0), SkBits2Float(0x436cae8f), SkBits2Float(0x43522f74));  // 229.36f, 202.863f, 236.682f, 210.185f
+path.quadTo(SkBits2Float(0x43740113), SkBits2Float(0x435981f8), SkBits2Float(0x43740113), SkBits2Float(0x4363dcf0));  // 244.004f, 217.508f, 244.004f, 227.863f
+path.close();
+    SkPath path82(path);
+    builder.add(path82, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43bf0464), SkBits2Float(0x431e17ca));  // 382.034f, 158.093f
+path.quadTo(SkBits2Float(0x43bf0464), SkBits2Float(0x432872c2), SkBits2Float(0x43bb5b22), SkBits2Float(0x432fc546));  // 382.034f, 168.448f, 374.712f, 175.771f
+path.quadTo(SkBits2Float(0x43b7b1e0), SkBits2Float(0x433717ca), SkBits2Float(0x43b28464), SkBits2Float(0x433717ca));  // 367.39f, 183.093f, 357.034f, 183.093f
+path.quadTo(SkBits2Float(0x43ad56e8), SkBits2Float(0x433717ca), SkBits2Float(0x43a9ada6), SkBits2Float(0x432fc546));  // 346.679f, 183.093f, 339.357f, 175.771f
+path.quadTo(SkBits2Float(0x43a60464), SkBits2Float(0x432872c2), SkBits2Float(0x43a60464), SkBits2Float(0x431e17ca));  // 332.034f, 168.448f, 332.034f, 158.093f
+path.quadTo(SkBits2Float(0x43a60464), SkBits2Float(0x4313bcd2), SkBits2Float(0x43a9ada6), SkBits2Float(0x430c6a4e));  // 332.034f, 147.738f, 339.357f, 140.415f
+path.quadTo(SkBits2Float(0x43ad56e8), SkBits2Float(0x430517ca), SkBits2Float(0x43b28464), SkBits2Float(0x430517ca));  // 346.679f, 133.093f, 357.034f, 133.093f
+path.quadTo(SkBits2Float(0x43b7b1e0), SkBits2Float(0x430517ca), SkBits2Float(0x43bb5b22), SkBits2Float(0x430c6a4e));  // 367.39f, 133.093f, 374.712f, 140.415f
+path.quadTo(SkBits2Float(0x43bf0464), SkBits2Float(0x4313bcd2), SkBits2Float(0x43bf0464), SkBits2Float(0x431e17ca));  // 382.034f, 147.738f, 382.034f, 158.093f
+path.close();
+    SkPath path83(path);
+    builder.add(path83, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43a72678), SkBits2Float(0x438273e0));  // 334.301f, 260.905f
+path.quadTo(SkBits2Float(0x43a72678), SkBits2Float(0x4387a15c), SkBits2Float(0x43a37d36), SkBits2Float(0x438b4a9e));  // 334.301f, 271.261f, 326.978f, 278.583f
+path.quadTo(SkBits2Float(0x439fd3f4), SkBits2Float(0x438ef3e0), SkBits2Float(0x439aa678), SkBits2Float(0x438ef3e0));  // 319.656f, 285.905f, 309.301f, 285.905f
+path.quadTo(SkBits2Float(0x439578fc), SkBits2Float(0x438ef3e0), SkBits2Float(0x4391cfba), SkBits2Float(0x438b4a9e));  // 298.945f, 285.905f, 291.623f, 278.583f
+path.quadTo(SkBits2Float(0x438e2678), SkBits2Float(0x4387a15c), SkBits2Float(0x438e2678), SkBits2Float(0x438273e0));  // 284.301f, 271.261f, 284.301f, 260.905f
+path.quadTo(SkBits2Float(0x438e2678), SkBits2Float(0x437a8cc8), SkBits2Float(0x4391cfba), SkBits2Float(0x43733a44));  // 284.301f, 250.55f, 291.623f, 243.228f
+path.quadTo(SkBits2Float(0x439578fc), SkBits2Float(0x436be7c0), SkBits2Float(0x439aa678), SkBits2Float(0x436be7c0));  // 298.945f, 235.905f, 309.301f, 235.905f
+path.quadTo(SkBits2Float(0x439fd3f4), SkBits2Float(0x436be7c0), SkBits2Float(0x43a37d36), SkBits2Float(0x43733a44));  // 319.656f, 235.905f, 326.978f, 243.228f
+path.quadTo(SkBits2Float(0x43a72678), SkBits2Float(0x437a8cc8), SkBits2Float(0x43a72678), SkBits2Float(0x438273e0));  // 334.301f, 250.55f, 334.301f, 260.905f
+path.close();
+    SkPath path84(path);
+    builder.add(path84, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4361bf09), SkBits2Float(0x43464372));  // 225.746f, 198.263f
+path.quadTo(SkBits2Float(0x4361bf09), SkBits2Float(0x43509e6a), SkBits2Float(0x435a6c85), SkBits2Float(0x4357f0ee));  // 225.746f, 208.619f, 218.424f, 215.941f
+path.quadTo(SkBits2Float(0x43531a00), SkBits2Float(0x435f4372), SkBits2Float(0x4348bf09), SkBits2Float(0x435f4372));  // 211.102f, 223.263f, 200.746f, 223.263f
+path.quadTo(SkBits2Float(0x433e6412), SkBits2Float(0x435f4372), SkBits2Float(0x4337118d), SkBits2Float(0x4357f0ee));  // 190.391f, 223.263f, 183.069f, 215.941f
+path.quadTo(SkBits2Float(0x432fbf09), SkBits2Float(0x43509e6a), SkBits2Float(0x432fbf09), SkBits2Float(0x43464372));  // 175.746f, 208.619f, 175.746f, 198.263f
+path.quadTo(SkBits2Float(0x432fbf09), SkBits2Float(0x433be87a), SkBits2Float(0x4337118d), SkBits2Float(0x433495f6));  // 175.746f, 187.908f, 183.069f, 180.586f
+path.quadTo(SkBits2Float(0x433e6412), SkBits2Float(0x432d4372), SkBits2Float(0x4348bf09), SkBits2Float(0x432d4372));  // 190.391f, 173.263f, 200.746f, 173.263f
+path.quadTo(SkBits2Float(0x43531a00), SkBits2Float(0x432d4372), SkBits2Float(0x435a6c85), SkBits2Float(0x433495f6));  // 211.102f, 173.263f, 218.424f, 180.586f
+path.quadTo(SkBits2Float(0x4361bf09), SkBits2Float(0x433be87a), SkBits2Float(0x4361bf09), SkBits2Float(0x43464372));  // 225.746f, 187.908f, 225.746f, 198.263f
+path.close();
+    SkPath path85(path);
+    builder.add(path85, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4377220b), SkBits2Float(0x4392653c));  // 247.133f, 292.791f
+path.quadTo(SkBits2Float(0x4377220b), SkBits2Float(0x439792b8), SkBits2Float(0x436fcf87), SkBits2Float(0x439b3bfa));  // 247.133f, 303.146f, 239.811f, 310.469f
+path.quadTo(SkBits2Float(0x43687d02), SkBits2Float(0x439ee53c), SkBits2Float(0x435e220b), SkBits2Float(0x439ee53c));  // 232.488f, 317.791f, 222.133f, 317.791f
+path.quadTo(SkBits2Float(0x4353c714), SkBits2Float(0x439ee53c), SkBits2Float(0x434c748f), SkBits2Float(0x439b3bfa));  // 211.778f, 317.791f, 204.455f, 310.469f
+path.quadTo(SkBits2Float(0x4345220b), SkBits2Float(0x439792b8), SkBits2Float(0x4345220b), SkBits2Float(0x4392653c));  // 197.133f, 303.146f, 197.133f, 292.791f
+path.quadTo(SkBits2Float(0x4345220b), SkBits2Float(0x438d37c0), SkBits2Float(0x434c748f), SkBits2Float(0x43898e7e));  // 197.133f, 282.436f, 204.455f, 275.113f
+path.quadTo(SkBits2Float(0x4353c714), SkBits2Float(0x4385e53c), SkBits2Float(0x435e220b), SkBits2Float(0x4385e53c));  // 211.778f, 267.791f, 222.133f, 267.791f
+path.quadTo(SkBits2Float(0x43687d02), SkBits2Float(0x4385e53c), SkBits2Float(0x436fcf87), SkBits2Float(0x43898e7e));  // 232.488f, 267.791f, 239.811f, 275.113f
+path.quadTo(SkBits2Float(0x4377220b), SkBits2Float(0x438d37c0), SkBits2Float(0x4377220b), SkBits2Float(0x4392653c));  // 247.133f, 282.436f, 247.133f, 292.791f
+path.close();
+    SkPath path86(path);
+    builder.add(path86, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4402a250), SkBits2Float(0x4331ae72));  // 522.536f, 177.681f
+path.quadTo(SkBits2Float(0x4402a250), SkBits2Float(0x433c096a), SkBits2Float(0x4400cdaf), SkBits2Float(0x43435bee));  // 522.536f, 188.037f, 515.214f, 195.359f
+path.quadTo(SkBits2Float(0x43fdf21c), SkBits2Float(0x434aae72), SkBits2Float(0x43f8c4a0), SkBits2Float(0x434aae72));  // 507.891f, 202.681f, 497.536f, 202.681f
+path.quadTo(SkBits2Float(0x43f39724), SkBits2Float(0x434aae72), SkBits2Float(0x43efede2), SkBits2Float(0x43435bee));  // 487.181f, 202.681f, 479.858f, 195.359f
+path.quadTo(SkBits2Float(0x43ec44a0), SkBits2Float(0x433c096a), SkBits2Float(0x43ec44a0), SkBits2Float(0x4331ae72));  // 472.536f, 188.037f, 472.536f, 177.681f
+path.quadTo(SkBits2Float(0x43ec44a0), SkBits2Float(0x4327537a), SkBits2Float(0x43efede2), SkBits2Float(0x432000f6));  // 472.536f, 167.326f, 479.858f, 160.004f
+path.quadTo(SkBits2Float(0x43f39724), SkBits2Float(0x4318ae72), SkBits2Float(0x43f8c4a0), SkBits2Float(0x4318ae72));  // 487.181f, 152.681f, 497.536f, 152.681f
+path.quadTo(SkBits2Float(0x43fdf21c), SkBits2Float(0x4318ae72), SkBits2Float(0x4400cdaf), SkBits2Float(0x432000f6));  // 507.891f, 152.681f, 515.214f, 160.004f
+path.quadTo(SkBits2Float(0x4402a250), SkBits2Float(0x4327537a), SkBits2Float(0x4402a250), SkBits2Float(0x4331ae72));  // 522.536f, 167.326f, 522.536f, 177.681f
+path.close();
+    SkPath path87(path);
+    builder.add(path87, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x439cfd65), SkBits2Float(0x4331c20a));  // 313.98f, 177.758f
+path.quadTo(SkBits2Float(0x439cfd65), SkBits2Float(0x433c1d02), SkBits2Float(0x43995423), SkBits2Float(0x43436f86));  // 313.98f, 188.113f, 306.657f, 195.436f
+path.quadTo(SkBits2Float(0x4395aae1), SkBits2Float(0x434ac20a), SkBits2Float(0x43907d65), SkBits2Float(0x434ac20a));  // 299.335f, 202.758f, 288.98f, 202.758f
+path.quadTo(SkBits2Float(0x438b4fe9), SkBits2Float(0x434ac20a), SkBits2Float(0x4387a6a7), SkBits2Float(0x43436f86));  // 278.624f, 202.758f, 271.302f, 195.436f
+path.quadTo(SkBits2Float(0x4383fd65), SkBits2Float(0x433c1d02), SkBits2Float(0x4383fd65), SkBits2Float(0x4331c20a));  // 263.98f, 188.113f, 263.98f, 177.758f
+path.quadTo(SkBits2Float(0x4383fd65), SkBits2Float(0x43276712), SkBits2Float(0x4387a6a7), SkBits2Float(0x4320148e));  // 263.98f, 167.403f, 271.302f, 160.08f
+path.quadTo(SkBits2Float(0x438b4fe9), SkBits2Float(0x4318c20a), SkBits2Float(0x43907d65), SkBits2Float(0x4318c20a));  // 278.624f, 152.758f, 288.98f, 152.758f
+path.quadTo(SkBits2Float(0x4395aae1), SkBits2Float(0x4318c20a), SkBits2Float(0x43995423), SkBits2Float(0x4320148e));  // 299.335f, 152.758f, 306.657f, 160.08f
+path.quadTo(SkBits2Float(0x439cfd65), SkBits2Float(0x43276712), SkBits2Float(0x439cfd65), SkBits2Float(0x4331c20a));  // 313.98f, 167.403f, 313.98f, 177.758f
+path.close();
+    SkPath path88(path);
+    builder.add(path88, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43c6cc0f), SkBits2Float(0x430c1343));  // 397.594f, 140.075f
+path.quadTo(SkBits2Float(0x43c6cc0f), SkBits2Float(0x43166e3a), SkBits2Float(0x43c322cd), SkBits2Float(0x431dc0bf));  // 397.594f, 150.431f, 390.272f, 157.753f
+path.quadTo(SkBits2Float(0x43bf798b), SkBits2Float(0x43251343), SkBits2Float(0x43ba4c0f), SkBits2Float(0x43251343));  // 382.95f, 165.075f, 372.594f, 165.075f
+path.quadTo(SkBits2Float(0x43b51e93), SkBits2Float(0x43251343), SkBits2Float(0x43b17551), SkBits2Float(0x431dc0bf));  // 362.239f, 165.075f, 354.917f, 157.753f
+path.quadTo(SkBits2Float(0x43adcc0f), SkBits2Float(0x43166e3a), SkBits2Float(0x43adcc0f), SkBits2Float(0x430c1343));  // 347.594f, 150.431f, 347.594f, 140.075f
+path.quadTo(SkBits2Float(0x43adcc0f), SkBits2Float(0x4301b84c), SkBits2Float(0x43b17551), SkBits2Float(0x42f4cb8e));  // 347.594f, 129.72f, 354.917f, 122.398f
+path.quadTo(SkBits2Float(0x43b51e93), SkBits2Float(0x42e62686), SkBits2Float(0x43ba4c0f), SkBits2Float(0x42e62686));  // 362.239f, 115.075f, 372.594f, 115.075f
+path.quadTo(SkBits2Float(0x43bf798b), SkBits2Float(0x42e62686), SkBits2Float(0x43c322cd), SkBits2Float(0x42f4cb8e));  // 382.95f, 115.075f, 390.272f, 122.398f
+path.quadTo(SkBits2Float(0x43c6cc0f), SkBits2Float(0x4301b84c), SkBits2Float(0x43c6cc0f), SkBits2Float(0x430c1343));  // 397.594f, 129.72f, 397.594f, 140.075f
+path.close();
+    SkPath path89(path);
+    builder.add(path89, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43453a6b), SkBits2Float(0x438ed8e3));  // 197.228f, 285.694f
+path.quadTo(SkBits2Float(0x43453a6b), SkBits2Float(0x4394065f), SkBits2Float(0x433de7e7), SkBits2Float(0x4397afa1));  // 197.228f, 296.05f, 189.906f, 303.372f
+path.quadTo(SkBits2Float(0x43369562), SkBits2Float(0x439b58e3), SkBits2Float(0x432c3a6b), SkBits2Float(0x439b58e3));  // 182.584f, 310.694f, 172.228f, 310.694f
+path.quadTo(SkBits2Float(0x4321df74), SkBits2Float(0x439b58e3), SkBits2Float(0x431a8cef), SkBits2Float(0x4397afa1));  // 161.873f, 310.694f, 154.551f, 303.372f
+path.quadTo(SkBits2Float(0x43133a6b), SkBits2Float(0x4394065f), SkBits2Float(0x43133a6b), SkBits2Float(0x438ed8e3));  // 147.228f, 296.05f, 147.228f, 285.694f
+path.quadTo(SkBits2Float(0x43133a6b), SkBits2Float(0x4389ab67), SkBits2Float(0x431a8cef), SkBits2Float(0x43860225));  // 147.228f, 275.339f, 154.551f, 268.017f
+path.quadTo(SkBits2Float(0x4321df74), SkBits2Float(0x438258e3), SkBits2Float(0x432c3a6b), SkBits2Float(0x438258e3));  // 161.873f, 260.694f, 172.228f, 260.694f
+path.quadTo(SkBits2Float(0x43369562), SkBits2Float(0x438258e3), SkBits2Float(0x433de7e7), SkBits2Float(0x43860225));  // 182.584f, 260.694f, 189.906f, 268.017f
+path.quadTo(SkBits2Float(0x43453a6b), SkBits2Float(0x4389ab67), SkBits2Float(0x43453a6b), SkBits2Float(0x438ed8e3));  // 197.228f, 275.339f, 197.228f, 285.694f
+path.close();
+    SkPath path90(path);
+    builder.add(path90, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43a452ad), SkBits2Float(0x434c38a8));  // 328.646f, 204.221f
+path.quadTo(SkBits2Float(0x43a452ad), SkBits2Float(0x435693a0), SkBits2Float(0x43a0a96b), SkBits2Float(0x435de624));  // 328.646f, 214.577f, 321.324f, 221.899f
+path.quadTo(SkBits2Float(0x439d0029), SkBits2Float(0x436538a8), SkBits2Float(0x4397d2ad), SkBits2Float(0x436538a8));  // 314.001f, 229.221f, 303.646f, 229.221f
+path.quadTo(SkBits2Float(0x4392a531), SkBits2Float(0x436538a8), SkBits2Float(0x438efbef), SkBits2Float(0x435de624));  // 293.291f, 229.221f, 285.968f, 221.899f
+path.quadTo(SkBits2Float(0x438b52ad), SkBits2Float(0x435693a0), SkBits2Float(0x438b52ad), SkBits2Float(0x434c38a8));  // 278.646f, 214.577f, 278.646f, 204.221f
+path.quadTo(SkBits2Float(0x438b52ad), SkBits2Float(0x4341ddb0), SkBits2Float(0x438efbef), SkBits2Float(0x433a8b2c));  // 278.646f, 193.866f, 285.968f, 186.544f
+path.quadTo(SkBits2Float(0x4392a531), SkBits2Float(0x433338a8), SkBits2Float(0x4397d2ad), SkBits2Float(0x433338a8));  // 293.291f, 179.221f, 303.646f, 179.221f
+path.quadTo(SkBits2Float(0x439d0029), SkBits2Float(0x433338a8), SkBits2Float(0x43a0a96b), SkBits2Float(0x433a8b2c));  // 314.001f, 179.221f, 321.324f, 186.544f
+path.quadTo(SkBits2Float(0x43a452ad), SkBits2Float(0x4341ddb0), SkBits2Float(0x43a452ad), SkBits2Float(0x434c38a8));  // 328.646f, 193.866f, 328.646f, 204.221f
+path.close();
+    SkPath path91(path);
+    builder.add(path91, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x44002d54), SkBits2Float(0x4339e9e8));  // 512.708f, 185.914f
+path.quadTo(SkBits2Float(0x44002d54), SkBits2Float(0x434444e0), SkBits2Float(0x43fcb166), SkBits2Float(0x434b9764));  // 512.708f, 196.269f, 505.386f, 203.591f
+path.quadTo(SkBits2Float(0x43f90824), SkBits2Float(0x4352e9e8), SkBits2Float(0x43f3daa8), SkBits2Float(0x4352e9e8));  // 498.064f, 210.914f, 487.708f, 210.914f
+path.quadTo(SkBits2Float(0x43eead2c), SkBits2Float(0x4352e9e8), SkBits2Float(0x43eb03ea), SkBits2Float(0x434b9764));  // 477.353f, 210.914f, 470.031f, 203.591f
+path.quadTo(SkBits2Float(0x43e75aa7), SkBits2Float(0x434444e0), SkBits2Float(0x43e75aa7), SkBits2Float(0x4339e9e8));  // 462.708f, 196.269f, 462.708f, 185.914f
+path.quadTo(SkBits2Float(0x43e75aa7), SkBits2Float(0x432f8ef0), SkBits2Float(0x43eb03ea), SkBits2Float(0x43283c6c));  // 462.708f, 175.558f, 470.031f, 168.236f
+path.quadTo(SkBits2Float(0x43eead2c), SkBits2Float(0x4320e9e8), SkBits2Float(0x43f3daa8), SkBits2Float(0x4320e9e8));  // 477.353f, 160.914f, 487.708f, 160.914f
+path.quadTo(SkBits2Float(0x43f90824), SkBits2Float(0x4320e9e8), SkBits2Float(0x43fcb166), SkBits2Float(0x43283c6c));  // 498.064f, 160.914f, 505.386f, 168.236f
+path.quadTo(SkBits2Float(0x44002d54), SkBits2Float(0x432f8ef0), SkBits2Float(0x44002d54), SkBits2Float(0x4339e9e8));  // 512.708f, 175.558f, 512.708f, 185.914f
+path.close();
+    SkPath path92(path);
+    builder.add(path92, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4385d99d), SkBits2Float(0x4302a8cd));  // 267.7f, 130.659f
+path.quadTo(SkBits2Float(0x4385d99d), SkBits2Float(0x430d03c4), SkBits2Float(0x4382305b), SkBits2Float(0x43145649));  // 267.7f, 141.015f, 260.378f, 148.337f
+path.quadTo(SkBits2Float(0x437d0e32), SkBits2Float(0x431ba8cd), SkBits2Float(0x4372b33a), SkBits2Float(0x431ba8cd));  // 253.055f, 155.659f, 242.7f, 155.659f
+path.quadTo(SkBits2Float(0x43685842), SkBits2Float(0x431ba8cd), SkBits2Float(0x436105be), SkBits2Float(0x43145649));  // 232.345f, 155.659f, 225.022f, 148.337f
+path.quadTo(SkBits2Float(0x4359b33a), SkBits2Float(0x430d03c4), SkBits2Float(0x4359b33a), SkBits2Float(0x4302a8cd));  // 217.7f, 141.015f, 217.7f, 130.659f
+path.quadTo(SkBits2Float(0x4359b33a), SkBits2Float(0x42f09bab), SkBits2Float(0x436105be), SkBits2Float(0x42e1f6a2));  // 217.7f, 120.304f, 225.022f, 112.982f
+path.quadTo(SkBits2Float(0x43685842), SkBits2Float(0x42d3519a), SkBits2Float(0x4372b33a), SkBits2Float(0x42d3519a));  // 232.345f, 105.659f, 242.7f, 105.659f
+path.quadTo(SkBits2Float(0x437d0e32), SkBits2Float(0x42d3519a), SkBits2Float(0x4382305b), SkBits2Float(0x42e1f6a2));  // 253.055f, 105.659f, 260.378f, 112.982f
+path.quadTo(SkBits2Float(0x4385d99d), SkBits2Float(0x42f09bab), SkBits2Float(0x4385d99d), SkBits2Float(0x4302a8cd));  // 267.7f, 120.304f, 267.7f, 130.659f
+path.close();
+    SkPath path93(path);
+    builder.add(path93, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42f92820), SkBits2Float(0x435b1722));  // 124.578f, 219.09f
+path.quadTo(SkBits2Float(0x42f92820), SkBits2Float(0x4365721a), SkBits2Float(0x42ea8318), SkBits2Float(0x436cc49e));  // 124.578f, 229.446f, 117.256f, 236.768f
+path.quadTo(SkBits2Float(0x42dbde0f), SkBits2Float(0x43741722), SkBits2Float(0x42c72820), SkBits2Float(0x43741722));  // 109.934f, 244.09f, 99.5784f, 244.09f
+path.quadTo(SkBits2Float(0x42b27231), SkBits2Float(0x43741722), SkBits2Float(0x42a3cd28), SkBits2Float(0x436cc49e));  // 89.223f, 244.09f, 81.9007f, 236.768f
+path.quadTo(SkBits2Float(0x42952820), SkBits2Float(0x4365721a), SkBits2Float(0x42952820), SkBits2Float(0x435b1722));  // 74.5784f, 229.446f, 74.5784f, 219.09f
+path.quadTo(SkBits2Float(0x42952820), SkBits2Float(0x4350bc2a), SkBits2Float(0x42a3cd28), SkBits2Float(0x434969a6));  // 74.5784f, 208.735f, 81.9007f, 201.413f
+path.quadTo(SkBits2Float(0x42b27231), SkBits2Float(0x43421722), SkBits2Float(0x42c72820), SkBits2Float(0x43421722));  // 89.223f, 194.09f, 99.5784f, 194.09f
+path.quadTo(SkBits2Float(0x42dbde0f), SkBits2Float(0x43421722), SkBits2Float(0x42ea8318), SkBits2Float(0x434969a6));  // 109.934f, 194.09f, 117.256f, 201.413f
+path.quadTo(SkBits2Float(0x42f92820), SkBits2Float(0x4350bc2a), SkBits2Float(0x42f92820), SkBits2Float(0x435b1722));  // 124.578f, 208.735f, 124.578f, 219.09f
+path.close();
+    SkPath path94(path);
+    builder.add(path94, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43b70d86), SkBits2Float(0x435cf621));  // 366.106f, 220.961f
+path.quadTo(SkBits2Float(0x43b70d86), SkBits2Float(0x43675118), SkBits2Float(0x43b36444), SkBits2Float(0x436ea39d));  // 366.106f, 231.317f, 358.783f, 238.639f
+path.quadTo(SkBits2Float(0x43afbb02), SkBits2Float(0x4375f621), SkBits2Float(0x43aa8d86), SkBits2Float(0x4375f621));  // 351.461f, 245.961f, 341.106f, 245.961f
+path.quadTo(SkBits2Float(0x43a5600a), SkBits2Float(0x4375f621), SkBits2Float(0x43a1b6c8), SkBits2Float(0x436ea39d));  // 330.75f, 245.961f, 323.428f, 238.639f
+path.quadTo(SkBits2Float(0x439e0d86), SkBits2Float(0x43675118), SkBits2Float(0x439e0d86), SkBits2Float(0x435cf621));  // 316.106f, 231.317f, 316.106f, 220.961f
+path.quadTo(SkBits2Float(0x439e0d86), SkBits2Float(0x43529b2a), SkBits2Float(0x43a1b6c8), SkBits2Float(0x434b48a5));  // 316.106f, 210.606f, 323.428f, 203.284f
+path.quadTo(SkBits2Float(0x43a5600a), SkBits2Float(0x4343f621), SkBits2Float(0x43aa8d86), SkBits2Float(0x4343f621));  // 330.75f, 195.961f, 341.106f, 195.961f
+path.quadTo(SkBits2Float(0x43afbb02), SkBits2Float(0x4343f621), SkBits2Float(0x43b36444), SkBits2Float(0x434b48a5));  // 351.461f, 195.961f, 358.783f, 203.284f
+path.quadTo(SkBits2Float(0x43b70d86), SkBits2Float(0x43529b2a), SkBits2Float(0x43b70d86), SkBits2Float(0x435cf621));  // 366.106f, 210.606f, 366.106f, 220.961f
+path.close();
+    SkPath path95(path);
+    builder.add(path95, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x436d80e1), SkBits2Float(0x43885efa));  // 237.503f, 272.742f
+path.quadTo(SkBits2Float(0x436d80e1), SkBits2Float(0x438d8c76), SkBits2Float(0x43662e5d), SkBits2Float(0x439135b8));  // 237.503f, 283.097f, 230.181f, 290.42f
+path.quadTo(SkBits2Float(0x435edbd8), SkBits2Float(0x4394defa), SkBits2Float(0x435480e1), SkBits2Float(0x4394defa));  // 222.859f, 297.742f, 212.503f, 297.742f
+path.quadTo(SkBits2Float(0x434a25ea), SkBits2Float(0x4394defa), SkBits2Float(0x4342d365), SkBits2Float(0x439135b8));  // 202.148f, 297.742f, 194.826f, 290.42f
+path.quadTo(SkBits2Float(0x433b80e1), SkBits2Float(0x438d8c76), SkBits2Float(0x433b80e1), SkBits2Float(0x43885efa));  // 187.503f, 283.097f, 187.503f, 272.742f
+path.quadTo(SkBits2Float(0x433b80e1), SkBits2Float(0x4383317e), SkBits2Float(0x4342d365), SkBits2Float(0x437f1078));  // 187.503f, 262.387f, 194.826f, 255.064f
+path.quadTo(SkBits2Float(0x434a25ea), SkBits2Float(0x4377bdf4), SkBits2Float(0x435480e1), SkBits2Float(0x4377bdf4));  // 202.148f, 247.742f, 212.503f, 247.742f
+path.quadTo(SkBits2Float(0x435edbd8), SkBits2Float(0x4377bdf4), SkBits2Float(0x43662e5d), SkBits2Float(0x437f1078));  // 222.859f, 247.742f, 230.181f, 255.064f
+path.quadTo(SkBits2Float(0x436d80e1), SkBits2Float(0x4383317e), SkBits2Float(0x436d80e1), SkBits2Float(0x43885efa));  // 237.503f, 262.387f, 237.503f, 272.742f
+path.close();
+    SkPath path96(path);
+    builder.add(path96, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43e54635), SkBits2Float(0x43794ba0));  // 458.548f, 249.295f
+path.quadTo(SkBits2Float(0x43e54635), SkBits2Float(0x4381d34c), SkBits2Float(0x43e19cf3), SkBits2Float(0x43857c8e));  // 458.548f, 259.651f, 451.226f, 266.973f
+path.quadTo(SkBits2Float(0x43ddf3b1), SkBits2Float(0x438925d0), SkBits2Float(0x43d8c635), SkBits2Float(0x438925d0));  // 443.904f, 274.295f, 433.548f, 274.295f
+path.quadTo(SkBits2Float(0x43d398b9), SkBits2Float(0x438925d0), SkBits2Float(0x43cfef77), SkBits2Float(0x43857c8e));  // 423.193f, 274.295f, 415.871f, 266.973f
+path.quadTo(SkBits2Float(0x43cc4635), SkBits2Float(0x4381d34c), SkBits2Float(0x43cc4635), SkBits2Float(0x43794ba0));  // 408.548f, 259.651f, 408.548f, 249.295f
+path.quadTo(SkBits2Float(0x43cc4635), SkBits2Float(0x436ef0a8), SkBits2Float(0x43cfef77), SkBits2Float(0x43679e24));  // 408.548f, 238.94f, 415.871f, 231.618f
+path.quadTo(SkBits2Float(0x43d398b9), SkBits2Float(0x43604ba0), SkBits2Float(0x43d8c635), SkBits2Float(0x43604ba0));  // 423.193f, 224.295f, 433.548f, 224.295f
+path.quadTo(SkBits2Float(0x43ddf3b1), SkBits2Float(0x43604ba0), SkBits2Float(0x43e19cf3), SkBits2Float(0x43679e24));  // 443.904f, 224.295f, 451.226f, 231.618f
+path.quadTo(SkBits2Float(0x43e54635), SkBits2Float(0x436ef0a8), SkBits2Float(0x43e54635), SkBits2Float(0x43794ba0));  // 458.548f, 238.94f, 458.548f, 249.295f
+path.close();
+    SkPath path97(path);
+    builder.add(path97, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42596ac0), SkBits2Float(0x42f09a24));  // 54.3542f, 120.301f
+path.quadTo(SkBits2Float(0x42596ac0), SkBits2Float(0x4302a809), SkBits2Float(0x423c20af), SkBits2Float(0x4309fa8e));  // 54.3542f, 130.656f, 47.0319f, 137.979f
+path.quadTo(SkBits2Float(0x421ed69e), SkBits2Float(0x43114d12), SkBits2Float(0x41ead580), SkBits2Float(0x43114d12));  // 39.7096f, 145.301f, 29.3542f, 145.301f
+path.quadTo(SkBits2Float(0x4197fdc4), SkBits2Float(0x43114d12), SkBits2Float(0x413ad344), SkBits2Float(0x4309fa8e));  // 18.9989f, 145.301f, 11.6766f, 137.979f
+path.quadTo(SkBits2Float(0x408b5604), SkBits2Float(0x4302a809), SkBits2Float(0x408b5604), SkBits2Float(0x42f09a24));  // 4.35425f, 130.656f, 4.35425f, 120.301f
+path.quadTo(SkBits2Float(0x408b5604), SkBits2Float(0x42dbe435), SkBits2Float(0x413ad344), SkBits2Float(0x42cd3f2d));  // 4.35425f, 109.946f, 11.6766f, 102.623f
+path.quadTo(SkBits2Float(0x4197fdc4), SkBits2Float(0x42be9a25), SkBits2Float(0x41ead580), SkBits2Float(0x42be9a25));  // 18.9989f, 95.3011f, 29.3542f, 95.3011f
+path.quadTo(SkBits2Float(0x421ed69e), SkBits2Float(0x42be9a25), SkBits2Float(0x423c20af), SkBits2Float(0x42cd3f2d));  // 39.7096f, 95.3011f, 47.0319f, 102.623f
+path.quadTo(SkBits2Float(0x42596ac0), SkBits2Float(0x42dbe435), SkBits2Float(0x42596ac0), SkBits2Float(0x42f09a24));  // 54.3542f, 109.946f, 54.3542f, 120.301f
+path.close();
+    SkPath path98(path);
+    builder.add(path98, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43ab48a8), SkBits2Float(0x438703b7));  // 342.568f, 270.029f
+path.quadTo(SkBits2Float(0x43ab48a8), SkBits2Float(0x438c3133), SkBits2Float(0x43a79f66), SkBits2Float(0x438fda75));  // 342.568f, 280.384f, 335.245f, 287.707f
+path.quadTo(SkBits2Float(0x43a3f624), SkBits2Float(0x439383b7), SkBits2Float(0x439ec8a8), SkBits2Float(0x439383b7));  // 327.923f, 295.029f, 317.568f, 295.029f
+path.quadTo(SkBits2Float(0x43999b2c), SkBits2Float(0x439383b7), SkBits2Float(0x4395f1ea), SkBits2Float(0x438fda75));  // 307.212f, 295.029f, 299.89f, 287.707f
+path.quadTo(SkBits2Float(0x439248a8), SkBits2Float(0x438c3133), SkBits2Float(0x439248a8), SkBits2Float(0x438703b7));  // 292.568f, 280.384f, 292.568f, 270.029f
+path.quadTo(SkBits2Float(0x439248a8), SkBits2Float(0x4381d63b), SkBits2Float(0x4395f1ea), SkBits2Float(0x437c59f2));  // 292.568f, 259.674f, 299.89f, 252.351f
+path.quadTo(SkBits2Float(0x43999b2c), SkBits2Float(0x4375076e), SkBits2Float(0x439ec8a8), SkBits2Float(0x4375076e));  // 307.212f, 245.029f, 317.568f, 245.029f
+path.quadTo(SkBits2Float(0x43a3f624), SkBits2Float(0x4375076e), SkBits2Float(0x43a79f66), SkBits2Float(0x437c59f2));  // 327.923f, 245.029f, 335.245f, 252.351f
+path.quadTo(SkBits2Float(0x43ab48a8), SkBits2Float(0x4381d63b), SkBits2Float(0x43ab48a8), SkBits2Float(0x438703b7));  // 342.568f, 259.674f, 342.568f, 270.029f
+path.close();
+    SkPath path99(path);
+    builder.add(path99, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43b3d091), SkBits2Float(0x4394a1fa));  // 359.629f, 297.265f
+path.quadTo(SkBits2Float(0x43b3d091), SkBits2Float(0x4399cf76), SkBits2Float(0x43b0274f), SkBits2Float(0x439d78b8));  // 359.629f, 307.621f, 352.307f, 314.943f
+path.quadTo(SkBits2Float(0x43ac7e0d), SkBits2Float(0x43a121fa), SkBits2Float(0x43a75091), SkBits2Float(0x43a121fa));  // 344.985f, 322.265f, 334.629f, 322.265f
+path.quadTo(SkBits2Float(0x43a22315), SkBits2Float(0x43a121fa), SkBits2Float(0x439e79d3), SkBits2Float(0x439d78b8));  // 324.274f, 322.265f, 316.952f, 314.943f
+path.quadTo(SkBits2Float(0x439ad091), SkBits2Float(0x4399cf76), SkBits2Float(0x439ad091), SkBits2Float(0x4394a1fa));  // 309.629f, 307.621f, 309.629f, 297.265f
+path.quadTo(SkBits2Float(0x439ad091), SkBits2Float(0x438f747e), SkBits2Float(0x439e79d3), SkBits2Float(0x438bcb3c));  // 309.629f, 286.91f, 316.952f, 279.588f
+path.quadTo(SkBits2Float(0x43a22315), SkBits2Float(0x438821fa), SkBits2Float(0x43a75091), SkBits2Float(0x438821fa));  // 324.274f, 272.265f, 334.629f, 272.265f
+path.quadTo(SkBits2Float(0x43ac7e0d), SkBits2Float(0x438821fa), SkBits2Float(0x43b0274f), SkBits2Float(0x438bcb3c));  // 344.985f, 272.265f, 352.307f, 279.588f
+path.quadTo(SkBits2Float(0x43b3d091), SkBits2Float(0x438f747e), SkBits2Float(0x43b3d091), SkBits2Float(0x4394a1fa));  // 359.629f, 286.91f, 359.629f, 297.265f
+path.close();
+    SkPath path100(path);
+    builder.add(path100, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42a153aa), SkBits2Float(0x4387c2b8));  // 80.6634f, 271.521f
+path.quadTo(SkBits2Float(0x42a153aa), SkBits2Float(0x438cf034), SkBits2Float(0x4292aea2), SkBits2Float(0x43909976));  // 80.6634f, 281.877f, 73.3411f, 289.199f
+path.quadTo(SkBits2Float(0x42840999), SkBits2Float(0x439442b8), SkBits2Float(0x425ea754), SkBits2Float(0x439442b8));  // 66.0187f, 296.521f, 55.6634f, 296.521f
+path.quadTo(SkBits2Float(0x42353b76), SkBits2Float(0x439442b8), SkBits2Float(0x4217f165), SkBits2Float(0x43909976));  // 45.3081f, 296.521f, 37.9857f, 289.199f
+path.quadTo(SkBits2Float(0x41f54ea8), SkBits2Float(0x438cf034), SkBits2Float(0x41f54ea8), SkBits2Float(0x4387c2b8));  // 30.6634f, 281.877f, 30.6634f, 271.521f
+path.quadTo(SkBits2Float(0x41f54ea8), SkBits2Float(0x4382953c), SkBits2Float(0x4217f165), SkBits2Float(0x437dd7f4));  // 30.6634f, 261.166f, 37.9857f, 253.844f
+path.quadTo(SkBits2Float(0x42353b76), SkBits2Float(0x43768570), SkBits2Float(0x425ea754), SkBits2Float(0x43768570));  // 45.3081f, 246.521f, 55.6634f, 246.521f
+path.quadTo(SkBits2Float(0x42840999), SkBits2Float(0x43768570), SkBits2Float(0x4292aea2), SkBits2Float(0x437dd7f4));  // 66.0187f, 246.521f, 73.3411f, 253.844f
+path.quadTo(SkBits2Float(0x42a153aa), SkBits2Float(0x4382953c), SkBits2Float(0x42a153aa), SkBits2Float(0x4387c2b8));  // 80.6634f, 261.166f, 80.6634f, 271.521f
+path.close();
+    SkPath path101(path);
+    builder.add(path101, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x436da8c1), SkBits2Float(0x434950e1));  // 237.659f, 201.316f
+path.quadTo(SkBits2Float(0x436da8c1), SkBits2Float(0x4353abd8), SkBits2Float(0x4366563d), SkBits2Float(0x435afe5d));  // 237.659f, 211.671f, 230.337f, 218.994f
+path.quadTo(SkBits2Float(0x435f03b8), SkBits2Float(0x436250e1), SkBits2Float(0x4354a8c1), SkBits2Float(0x436250e1));  // 223.015f, 226.316f, 212.659f, 226.316f
+path.quadTo(SkBits2Float(0x434a4dca), SkBits2Float(0x436250e1), SkBits2Float(0x4342fb45), SkBits2Float(0x435afe5d));  // 202.304f, 226.316f, 194.982f, 218.994f
+path.quadTo(SkBits2Float(0x433ba8c1), SkBits2Float(0x4353abd8), SkBits2Float(0x433ba8c1), SkBits2Float(0x434950e1));  // 187.659f, 211.671f, 187.659f, 201.316f
+path.quadTo(SkBits2Float(0x433ba8c1), SkBits2Float(0x433ef5ea), SkBits2Float(0x4342fb45), SkBits2Float(0x4337a365));  // 187.659f, 190.961f, 194.982f, 183.638f
+path.quadTo(SkBits2Float(0x434a4dca), SkBits2Float(0x433050e1), SkBits2Float(0x4354a8c1), SkBits2Float(0x433050e1));  // 202.304f, 176.316f, 212.659f, 176.316f
+path.quadTo(SkBits2Float(0x435f03b8), SkBits2Float(0x433050e1), SkBits2Float(0x4366563d), SkBits2Float(0x4337a365));  // 223.015f, 176.316f, 230.337f, 183.638f
+path.quadTo(SkBits2Float(0x436da8c1), SkBits2Float(0x433ef5ea), SkBits2Float(0x436da8c1), SkBits2Float(0x434950e1));  // 237.659f, 190.961f, 237.659f, 201.316f
+path.close();
+    SkPath path102(path);
+    builder.add(path102, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43ba530a), SkBits2Float(0x4376baae));  // 372.649f, 246.729f
+path.quadTo(SkBits2Float(0x43ba530a), SkBits2Float(0x43808ad3), SkBits2Float(0x43b6a9c8), SkBits2Float(0x43843415));  // 372.649f, 257.085f, 365.326f, 264.407f
+path.quadTo(SkBits2Float(0x43b30086), SkBits2Float(0x4387dd57), SkBits2Float(0x43add30a), SkBits2Float(0x4387dd57));  // 358.004f, 271.729f, 347.649f, 271.729f
+path.quadTo(SkBits2Float(0x43a8a58e), SkBits2Float(0x4387dd57), SkBits2Float(0x43a4fc4c), SkBits2Float(0x43843415));  // 337.293f, 271.729f, 329.971f, 264.407f
+path.quadTo(SkBits2Float(0x43a1530a), SkBits2Float(0x43808ad3), SkBits2Float(0x43a1530a), SkBits2Float(0x4376baae));  // 322.649f, 257.085f, 322.649f, 246.729f
+path.quadTo(SkBits2Float(0x43a1530a), SkBits2Float(0x436c5fb6), SkBits2Float(0x43a4fc4c), SkBits2Float(0x43650d32));  // 322.649f, 236.374f, 329.971f, 229.052f
+path.quadTo(SkBits2Float(0x43a8a58e), SkBits2Float(0x435dbaae), SkBits2Float(0x43add30a), SkBits2Float(0x435dbaae));  // 337.293f, 221.729f, 347.649f, 221.729f
+path.quadTo(SkBits2Float(0x43b30086), SkBits2Float(0x435dbaae), SkBits2Float(0x43b6a9c8), SkBits2Float(0x43650d32));  // 358.004f, 221.729f, 365.326f, 229.052f
+path.quadTo(SkBits2Float(0x43ba530a), SkBits2Float(0x436c5fb6), SkBits2Float(0x43ba530a), SkBits2Float(0x4376baae));  // 372.649f, 236.374f, 372.649f, 246.729f
+path.close();
+    SkPath path103(path);
+    builder.add(path103, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4399286a), SkBits2Float(0x435887a5));  // 306.316f, 216.53f
+path.quadTo(SkBits2Float(0x4399286a), SkBits2Float(0x4362e29c), SkBits2Float(0x43957f28), SkBits2Float(0x436a3521));  // 306.316f, 226.885f, 298.993f, 234.208f
+path.quadTo(SkBits2Float(0x4391d5e6), SkBits2Float(0x437187a5), SkBits2Float(0x438ca86a), SkBits2Float(0x437187a5));  // 291.671f, 241.53f, 281.316f, 241.53f
+path.quadTo(SkBits2Float(0x43877aee), SkBits2Float(0x437187a5), SkBits2Float(0x4383d1ac), SkBits2Float(0x436a3521));  // 270.96f, 241.53f, 263.638f, 234.208f
+path.quadTo(SkBits2Float(0x4380286a), SkBits2Float(0x4362e29c), SkBits2Float(0x4380286a), SkBits2Float(0x435887a5));  // 256.316f, 226.885f, 256.316f, 216.53f
+path.quadTo(SkBits2Float(0x4380286a), SkBits2Float(0x434e2cae), SkBits2Float(0x4383d1ac), SkBits2Float(0x4346da29));  // 256.316f, 206.175f, 263.638f, 198.852f
+path.quadTo(SkBits2Float(0x43877aee), SkBits2Float(0x433f87a5), SkBits2Float(0x438ca86a), SkBits2Float(0x433f87a5));  // 270.96f, 191.53f, 281.316f, 191.53f
+path.quadTo(SkBits2Float(0x4391d5e6), SkBits2Float(0x433f87a5), SkBits2Float(0x43957f28), SkBits2Float(0x4346da29));  // 291.671f, 191.53f, 298.993f, 198.852f
+path.quadTo(SkBits2Float(0x4399286a), SkBits2Float(0x434e2cae), SkBits2Float(0x4399286a), SkBits2Float(0x435887a5));  // 306.316f, 206.175f, 306.316f, 216.53f
+path.close();
+    SkPath path104(path);
+    builder.add(path104, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43a08755), SkBits2Float(0x43120238));  // 321.057f, 146.009f
+path.quadTo(SkBits2Float(0x43a08755), SkBits2Float(0x431c5d30), SkBits2Float(0x439cde13), SkBits2Float(0x4323afb4));  // 321.057f, 156.364f, 313.735f, 163.686f
+path.quadTo(SkBits2Float(0x439934d1), SkBits2Float(0x432b0238), SkBits2Float(0x43940755), SkBits2Float(0x432b0238));  // 306.413f, 171.009f, 296.057f, 171.009f
+path.quadTo(SkBits2Float(0x438ed9d9), SkBits2Float(0x432b0238), SkBits2Float(0x438b3097), SkBits2Float(0x4323afb4));  // 285.702f, 171.009f, 278.38f, 163.686f
+path.quadTo(SkBits2Float(0x43878755), SkBits2Float(0x431c5d30), SkBits2Float(0x43878755), SkBits2Float(0x43120238));  // 271.057f, 156.364f, 271.057f, 146.009f
+path.quadTo(SkBits2Float(0x43878755), SkBits2Float(0x4307a740), SkBits2Float(0x438b3097), SkBits2Float(0x430054bc));  // 271.057f, 135.653f, 278.38f, 128.331f
+path.quadTo(SkBits2Float(0x438ed9d9), SkBits2Float(0x42f20470), SkBits2Float(0x43940755), SkBits2Float(0x42f20470));  // 285.702f, 121.009f, 296.057f, 121.009f
+path.quadTo(SkBits2Float(0x439934d1), SkBits2Float(0x42f20470), SkBits2Float(0x439cde13), SkBits2Float(0x430054bc));  // 306.413f, 121.009f, 313.735f, 128.331f
+path.quadTo(SkBits2Float(0x43a08755), SkBits2Float(0x4307a740), SkBits2Float(0x43a08755), SkBits2Float(0x43120238));  // 321.057f, 135.653f, 321.057f, 146.009f
+path.close();
+    SkPath path105(path);
+    builder.add(path105, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43e31764), SkBits2Float(0x438e240b));  // 454.183f, 284.282f
+path.quadTo(SkBits2Float(0x43e31764), SkBits2Float(0x43935187), SkBits2Float(0x43df6e22), SkBits2Float(0x4396fac9));  // 454.183f, 294.637f, 446.86f, 301.959f
+path.quadTo(SkBits2Float(0x43dbc4e0), SkBits2Float(0x439aa40b), SkBits2Float(0x43d69764), SkBits2Float(0x439aa40b));  // 439.538f, 309.282f, 429.183f, 309.282f
+path.quadTo(SkBits2Float(0x43d169e8), SkBits2Float(0x439aa40b), SkBits2Float(0x43cdc0a6), SkBits2Float(0x4396fac9));  // 418.827f, 309.282f, 411.505f, 301.959f
+path.quadTo(SkBits2Float(0x43ca1764), SkBits2Float(0x43935187), SkBits2Float(0x43ca1764), SkBits2Float(0x438e240b));  // 404.183f, 294.637f, 404.183f, 284.282f
+path.quadTo(SkBits2Float(0x43ca1764), SkBits2Float(0x4388f68f), SkBits2Float(0x43cdc0a6), SkBits2Float(0x43854d4d));  // 404.183f, 273.926f, 411.505f, 266.604f
+path.quadTo(SkBits2Float(0x43d169e8), SkBits2Float(0x4381a40b), SkBits2Float(0x43d69764), SkBits2Float(0x4381a40b));  // 418.827f, 259.282f, 429.183f, 259.282f
+path.quadTo(SkBits2Float(0x43dbc4e0), SkBits2Float(0x4381a40b), SkBits2Float(0x43df6e22), SkBits2Float(0x43854d4d));  // 439.538f, 259.282f, 446.86f, 266.604f
+path.quadTo(SkBits2Float(0x43e31764), SkBits2Float(0x4388f68f), SkBits2Float(0x43e31764), SkBits2Float(0x438e240b));  // 454.183f, 273.926f, 454.183f, 284.282f
+path.close();
+    SkPath path106(path);
+    builder.add(path106, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43430483), SkBits2Float(0x4345764d));  // 195.018f, 197.462f
+path.quadTo(SkBits2Float(0x43430483), SkBits2Float(0x434fd144), SkBits2Float(0x433bb1ff), SkBits2Float(0x435723c9));  // 195.018f, 207.817f, 187.695f, 215.14f
+path.quadTo(SkBits2Float(0x43345f7a), SkBits2Float(0x435e764d), SkBits2Float(0x432a0483), SkBits2Float(0x435e764d));  // 180.373f, 222.462f, 170.018f, 222.462f
+path.quadTo(SkBits2Float(0x431fa98c), SkBits2Float(0x435e764d), SkBits2Float(0x43185707), SkBits2Float(0x435723c9));  // 159.662f, 222.462f, 152.34f, 215.14f
+path.quadTo(SkBits2Float(0x43110483), SkBits2Float(0x434fd144), SkBits2Float(0x43110483), SkBits2Float(0x4345764d));  // 145.018f, 207.817f, 145.018f, 197.462f
+path.quadTo(SkBits2Float(0x43110483), SkBits2Float(0x433b1b56), SkBits2Float(0x43185707), SkBits2Float(0x4333c8d1));  // 145.018f, 187.107f, 152.34f, 179.784f
+path.quadTo(SkBits2Float(0x431fa98c), SkBits2Float(0x432c764d), SkBits2Float(0x432a0483), SkBits2Float(0x432c764d));  // 159.662f, 172.462f, 170.018f, 172.462f
+path.quadTo(SkBits2Float(0x43345f7a), SkBits2Float(0x432c764d), SkBits2Float(0x433bb1ff), SkBits2Float(0x4333c8d1));  // 180.373f, 172.462f, 187.695f, 179.784f
+path.quadTo(SkBits2Float(0x43430483), SkBits2Float(0x433b1b56), SkBits2Float(0x43430483), SkBits2Float(0x4345764d));  // 195.018f, 187.107f, 195.018f, 197.462f
+path.close();
+    SkPath path107(path);
+    builder.add(path107, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x439a55b2), SkBits2Float(0x4370a1cc));  // 308.669f, 240.632f
+path.quadTo(SkBits2Float(0x439a55b2), SkBits2Float(0x437afcc3), SkBits2Float(0x4396ac70), SkBits2Float(0x438127a4));  // 308.669f, 250.987f, 301.347f, 258.31f
+path.quadTo(SkBits2Float(0x4393032e), SkBits2Float(0x4384d0e6), SkBits2Float(0x438dd5b2), SkBits2Float(0x4384d0e6));  // 294.025f, 265.632f, 283.669f, 265.632f
+path.quadTo(SkBits2Float(0x4388a836), SkBits2Float(0x4384d0e6), SkBits2Float(0x4384fef4), SkBits2Float(0x438127a4));  // 273.314f, 265.632f, 265.992f, 258.31f
+path.quadTo(SkBits2Float(0x438155b2), SkBits2Float(0x437afcc3), SkBits2Float(0x438155b2), SkBits2Float(0x4370a1cc));  // 258.669f, 250.987f, 258.669f, 240.632f
+path.quadTo(SkBits2Float(0x438155b2), SkBits2Float(0x436646d5), SkBits2Float(0x4384fef4), SkBits2Float(0x435ef451));  // 258.669f, 230.277f, 265.992f, 222.954f
+path.quadTo(SkBits2Float(0x4388a836), SkBits2Float(0x4357a1cd), SkBits2Float(0x438dd5b2), SkBits2Float(0x4357a1cd));  // 273.314f, 215.632f, 283.669f, 215.632f
+path.quadTo(SkBits2Float(0x4393032e), SkBits2Float(0x4357a1cd), SkBits2Float(0x4396ac70), SkBits2Float(0x435ef451));  // 294.025f, 215.632f, 301.347f, 222.954f
+path.quadTo(SkBits2Float(0x439a55b2), SkBits2Float(0x436646d5), SkBits2Float(0x439a55b2), SkBits2Float(0x4370a1cc));  // 308.669f, 230.277f, 308.669f, 240.632f
+path.close();
+    SkPath path108(path);
+    builder.add(path108, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42fcf02d), SkBits2Float(0x4327ee31));  // 126.469f, 167.93f
+path.quadTo(SkBits2Float(0x42fcf02d), SkBits2Float(0x43324928), SkBits2Float(0x42ee4b24), SkBits2Float(0x43399bad));  // 126.469f, 178.286f, 119.147f, 185.608f
+path.quadTo(SkBits2Float(0x42dfa61c), SkBits2Float(0x4340ee31), SkBits2Float(0x42caf02d), SkBits2Float(0x4340ee31));  // 111.824f, 192.93f, 101.469f, 192.93f
+path.quadTo(SkBits2Float(0x42b63a3e), SkBits2Float(0x4340ee31), SkBits2Float(0x42a79536), SkBits2Float(0x43399bad));  // 91.1138f, 192.93f, 83.7914f, 185.608f
+path.quadTo(SkBits2Float(0x4298f02d), SkBits2Float(0x43324928), SkBits2Float(0x4298f02d), SkBits2Float(0x4327ee31));  // 76.4691f, 178.286f, 76.4691f, 167.93f
+path.quadTo(SkBits2Float(0x4298f02d), SkBits2Float(0x431d933a), SkBits2Float(0x42a79536), SkBits2Float(0x431640b5));  // 76.4691f, 157.575f, 83.7914f, 150.253f
+path.quadTo(SkBits2Float(0x42b63a3e), SkBits2Float(0x430eee31), SkBits2Float(0x42caf02d), SkBits2Float(0x430eee31));  // 91.1138f, 142.93f, 101.469f, 142.93f
+path.quadTo(SkBits2Float(0x42dfa61c), SkBits2Float(0x430eee31), SkBits2Float(0x42ee4b24), SkBits2Float(0x431640b5));  // 111.824f, 142.93f, 119.147f, 150.253f
+path.quadTo(SkBits2Float(0x42fcf02d), SkBits2Float(0x431d933a), SkBits2Float(0x42fcf02d), SkBits2Float(0x4327ee31));  // 126.469f, 157.575f, 126.469f, 167.93f
+path.close();
+    SkPath path109(path);
+    builder.add(path109, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4408a32e), SkBits2Float(0x438aadd7));  // 546.55f, 277.358f
+path.quadTo(SkBits2Float(0x4408a32e), SkBits2Float(0x438fdb53), SkBits2Float(0x4406ce8d), SkBits2Float(0x43938495));  // 546.55f, 287.713f, 539.227f, 295.036f
+path.quadTo(SkBits2Float(0x4404f9ec), SkBits2Float(0x43972dd7), SkBits2Float(0x4402632e), SkBits2Float(0x43972dd7));  // 531.905f, 302.358f, 521.55f, 302.358f
+path.quadTo(SkBits2Float(0x43ff98e0), SkBits2Float(0x43972dd7), SkBits2Float(0x43fbef9e), SkBits2Float(0x43938495));  // 511.194f, 302.358f, 503.872f, 295.036f
+path.quadTo(SkBits2Float(0x43f8465c), SkBits2Float(0x438fdb53), SkBits2Float(0x43f8465c), SkBits2Float(0x438aadd7));  // 496.55f, 287.713f, 496.55f, 277.358f
+path.quadTo(SkBits2Float(0x43f8465c), SkBits2Float(0x4385805b), SkBits2Float(0x43fbef9e), SkBits2Float(0x4381d719));  // 496.55f, 267.003f, 503.872f, 259.68f
+path.quadTo(SkBits2Float(0x43ff98e0), SkBits2Float(0x437c5bae), SkBits2Float(0x4402632e), SkBits2Float(0x437c5bae));  // 511.194f, 252.358f, 521.55f, 252.358f
+path.quadTo(SkBits2Float(0x4404f9ec), SkBits2Float(0x437c5bae), SkBits2Float(0x4406ce8d), SkBits2Float(0x4381d719));  // 531.905f, 252.358f, 539.227f, 259.68f
+path.quadTo(SkBits2Float(0x4408a32e), SkBits2Float(0x4385805b), SkBits2Float(0x4408a32e), SkBits2Float(0x438aadd7));  // 546.55f, 267.003f, 546.55f, 277.358f
+path.close();
+    SkPath path110(path);
+    builder.add(path110, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43ae6436), SkBits2Float(0x4399c25d));  // 348.783f, 307.518f
+path.quadTo(SkBits2Float(0x43ae6436), SkBits2Float(0x439eefd9), SkBits2Float(0x43aabaf4), SkBits2Float(0x43a2991b));  // 348.783f, 317.874f, 341.461f, 325.196f
+path.quadTo(SkBits2Float(0x43a711b2), SkBits2Float(0x43a6425d), SkBits2Float(0x43a1e436), SkBits2Float(0x43a6425d));  // 334.138f, 332.518f, 323.783f, 332.518f
+path.quadTo(SkBits2Float(0x439cb6ba), SkBits2Float(0x43a6425d), SkBits2Float(0x43990d78), SkBits2Float(0x43a2991b));  // 313.428f, 332.518f, 306.105f, 325.196f
+path.quadTo(SkBits2Float(0x43956436), SkBits2Float(0x439eefd9), SkBits2Float(0x43956436), SkBits2Float(0x4399c25d));  // 298.783f, 317.874f, 298.783f, 307.518f
+path.quadTo(SkBits2Float(0x43956436), SkBits2Float(0x439494e1), SkBits2Float(0x43990d78), SkBits2Float(0x4390eb9f));  // 298.783f, 297.163f, 306.105f, 289.841f
+path.quadTo(SkBits2Float(0x439cb6ba), SkBits2Float(0x438d425d), SkBits2Float(0x43a1e436), SkBits2Float(0x438d425d));  // 313.428f, 282.518f, 323.783f, 282.518f
+path.quadTo(SkBits2Float(0x43a711b2), SkBits2Float(0x438d425d), SkBits2Float(0x43aabaf4), SkBits2Float(0x4390eb9f));  // 334.138f, 282.518f, 341.461f, 289.841f
+path.quadTo(SkBits2Float(0x43ae6436), SkBits2Float(0x439494e1), SkBits2Float(0x43ae6436), SkBits2Float(0x4399c25d));  // 348.783f, 297.163f, 348.783f, 307.518f
+path.close();
+    SkPath path111(path);
+    builder.add(path111, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x437ef735), SkBits2Float(0x43a4d766));  // 254.966f, 329.683f
+path.quadTo(SkBits2Float(0x437ef735), SkBits2Float(0x43aa04e2), SkBits2Float(0x4377a4b1), SkBits2Float(0x43adae24));  // 254.966f, 340.038f, 247.643f, 347.36f
+path.quadTo(SkBits2Float(0x4370522c), SkBits2Float(0x43b15766), SkBits2Float(0x4365f735), SkBits2Float(0x43b15766));  // 240.321f, 354.683f, 229.966f, 354.683f
+path.quadTo(SkBits2Float(0x435b9c3e), SkBits2Float(0x43b15766), SkBits2Float(0x435449b9), SkBits2Float(0x43adae24));  // 219.61f, 354.683f, 212.288f, 347.36f
+path.quadTo(SkBits2Float(0x434cf735), SkBits2Float(0x43aa04e2), SkBits2Float(0x434cf735), SkBits2Float(0x43a4d766));  // 204.966f, 340.038f, 204.966f, 329.683f
+path.quadTo(SkBits2Float(0x434cf735), SkBits2Float(0x439fa9ea), SkBits2Float(0x435449b9), SkBits2Float(0x439c00a8));  // 204.966f, 319.327f, 212.288f, 312.005f
+path.quadTo(SkBits2Float(0x435b9c3e), SkBits2Float(0x43985766), SkBits2Float(0x4365f735), SkBits2Float(0x43985766));  // 219.61f, 304.683f, 229.966f, 304.683f
+path.quadTo(SkBits2Float(0x4370522c), SkBits2Float(0x43985766), SkBits2Float(0x4377a4b1), SkBits2Float(0x439c00a8));  // 240.321f, 304.683f, 247.643f, 312.005f
+path.quadTo(SkBits2Float(0x437ef735), SkBits2Float(0x439fa9ea), SkBits2Float(0x437ef735), SkBits2Float(0x43a4d766));  // 254.966f, 319.327f, 254.966f, 329.683f
+path.close();
+    SkPath path112(path);
+    builder.add(path112, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4323b1dd), SkBits2Float(0x433e57b5));  // 163.695f, 190.343f
+path.quadTo(SkBits2Float(0x4323b1dd), SkBits2Float(0x4348b2ac), SkBits2Float(0x431c5f59), SkBits2Float(0x43500531));  // 163.695f, 200.698f, 156.372f, 208.02f
+path.quadTo(SkBits2Float(0x43150cd4), SkBits2Float(0x435757b5), SkBits2Float(0x430ab1dd), SkBits2Float(0x435757b5));  // 149.05f, 215.343f, 138.695f, 215.343f
+path.quadTo(SkBits2Float(0x430056e6), SkBits2Float(0x435757b5), SkBits2Float(0x42f208c2), SkBits2Float(0x43500531));  // 128.339f, 215.343f, 121.017f, 208.02f
+path.quadTo(SkBits2Float(0x42e363ba), SkBits2Float(0x4348b2ac), SkBits2Float(0x42e363ba), SkBits2Float(0x433e57b5));  // 113.695f, 200.698f, 113.695f, 190.343f
+path.quadTo(SkBits2Float(0x42e363ba), SkBits2Float(0x4333fcbe), SkBits2Float(0x42f208c2), SkBits2Float(0x432caa39));  // 113.695f, 179.987f, 121.017f, 172.665f
+path.quadTo(SkBits2Float(0x430056e6), SkBits2Float(0x432557b5), SkBits2Float(0x430ab1dd), SkBits2Float(0x432557b5));  // 128.339f, 165.343f, 138.695f, 165.343f
+path.quadTo(SkBits2Float(0x43150cd4), SkBits2Float(0x432557b5), SkBits2Float(0x431c5f59), SkBits2Float(0x432caa39));  // 149.05f, 165.343f, 156.372f, 172.665f
+path.quadTo(SkBits2Float(0x4323b1dd), SkBits2Float(0x4333fcbe), SkBits2Float(0x4323b1dd), SkBits2Float(0x433e57b5));  // 163.695f, 179.987f, 163.695f, 190.343f
+path.close();
+    SkPath path113(path);
+    builder.add(path113, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x436e6f43), SkBits2Float(0x435d1aaa));  // 238.435f, 221.104f
+path.quadTo(SkBits2Float(0x436e6f43), SkBits2Float(0x436775a2), SkBits2Float(0x43671cbf), SkBits2Float(0x436ec826));  // 238.435f, 231.46f, 231.112f, 238.782f
+path.quadTo(SkBits2Float(0x435fca3a), SkBits2Float(0x43761aaa), SkBits2Float(0x43556f43), SkBits2Float(0x43761aaa));  // 223.79f, 246.104f, 213.435f, 246.104f
+path.quadTo(SkBits2Float(0x434b144c), SkBits2Float(0x43761aaa), SkBits2Float(0x4343c1c7), SkBits2Float(0x436ec826));  // 203.079f, 246.104f, 195.757f, 238.782f
+path.quadTo(SkBits2Float(0x433c6f43), SkBits2Float(0x436775a2), SkBits2Float(0x433c6f43), SkBits2Float(0x435d1aaa));  // 188.435f, 231.46f, 188.435f, 221.104f
+path.quadTo(SkBits2Float(0x433c6f43), SkBits2Float(0x4352bfb2), SkBits2Float(0x4343c1c7), SkBits2Float(0x434b6d2e));  // 188.435f, 210.749f, 195.757f, 203.426f
+path.quadTo(SkBits2Float(0x434b144c), SkBits2Float(0x43441aaa), SkBits2Float(0x43556f43), SkBits2Float(0x43441aaa));  // 203.079f, 196.104f, 213.435f, 196.104f
+path.quadTo(SkBits2Float(0x435fca3a), SkBits2Float(0x43441aaa), SkBits2Float(0x43671cbf), SkBits2Float(0x434b6d2e));  // 223.79f, 196.104f, 231.112f, 203.426f
+path.quadTo(SkBits2Float(0x436e6f43), SkBits2Float(0x4352bfb2), SkBits2Float(0x436e6f43), SkBits2Float(0x435d1aaa));  // 238.435f, 210.749f, 238.435f, 221.104f
+path.close();
+    SkPath path114(path);
+    builder.add(path114, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43699f20), SkBits2Float(0x43b74967));  // 233.622f, 366.573f
+path.quadTo(SkBits2Float(0x43699f20), SkBits2Float(0x43bc76e3), SkBits2Float(0x43624c9c), SkBits2Float(0x43c02025));  // 233.622f, 376.929f, 226.299f, 384.251f
+path.quadTo(SkBits2Float(0x435afa18), SkBits2Float(0x43c3c967), SkBits2Float(0x43509f20), SkBits2Float(0x43c3c967));  // 218.977f, 391.573f, 208.622f, 391.573f
+path.quadTo(SkBits2Float(0x43464428), SkBits2Float(0x43c3c967), SkBits2Float(0x433ef1a4), SkBits2Float(0x43c02025));  // 198.266f, 391.573f, 190.944f, 384.251f
+path.quadTo(SkBits2Float(0x43379f20), SkBits2Float(0x43bc76e3), SkBits2Float(0x43379f20), SkBits2Float(0x43b74967));  // 183.622f, 376.929f, 183.622f, 366.573f
+path.quadTo(SkBits2Float(0x43379f20), SkBits2Float(0x43b21beb), SkBits2Float(0x433ef1a4), SkBits2Float(0x43ae72a9));  // 183.622f, 356.218f, 190.944f, 348.896f
+path.quadTo(SkBits2Float(0x43464428), SkBits2Float(0x43aac967), SkBits2Float(0x43509f20), SkBits2Float(0x43aac967));  // 198.266f, 341.573f, 208.622f, 341.573f
+path.quadTo(SkBits2Float(0x435afa18), SkBits2Float(0x43aac967), SkBits2Float(0x43624c9c), SkBits2Float(0x43ae72a9));  // 218.977f, 341.573f, 226.299f, 348.896f
+path.quadTo(SkBits2Float(0x43699f20), SkBits2Float(0x43b21beb), SkBits2Float(0x43699f20), SkBits2Float(0x43b74967));  // 233.622f, 356.218f, 233.622f, 366.573f
+path.close();
+    SkPath path115(path);
+    builder.add(path115, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4372b5ce), SkBits2Float(0x434919ea));  // 242.71f, 201.101f
+path.quadTo(SkBits2Float(0x4372b5ce), SkBits2Float(0x435374e2), SkBits2Float(0x436b634a), SkBits2Float(0x435ac766));  // 242.71f, 211.457f, 235.388f, 218.779f
+path.quadTo(SkBits2Float(0x436410c6), SkBits2Float(0x436219ea), SkBits2Float(0x4359b5ce), SkBits2Float(0x436219ea));  // 228.066f, 226.101f, 217.71f, 226.101f
+path.quadTo(SkBits2Float(0x434f5ad6), SkBits2Float(0x436219ea), SkBits2Float(0x43480852), SkBits2Float(0x435ac766));  // 207.355f, 226.101f, 200.033f, 218.779f
+path.quadTo(SkBits2Float(0x4340b5ce), SkBits2Float(0x435374e2), SkBits2Float(0x4340b5ce), SkBits2Float(0x434919ea));  // 192.71f, 211.457f, 192.71f, 201.101f
+path.quadTo(SkBits2Float(0x4340b5ce), SkBits2Float(0x433ebef2), SkBits2Float(0x43480852), SkBits2Float(0x43376c6e));  // 192.71f, 190.746f, 200.033f, 183.424f
+path.quadTo(SkBits2Float(0x434f5ad6), SkBits2Float(0x433019ea), SkBits2Float(0x4359b5ce), SkBits2Float(0x433019ea));  // 207.355f, 176.101f, 217.71f, 176.101f
+path.quadTo(SkBits2Float(0x436410c6), SkBits2Float(0x433019ea), SkBits2Float(0x436b634a), SkBits2Float(0x43376c6e));  // 228.066f, 176.101f, 235.388f, 183.424f
+path.quadTo(SkBits2Float(0x4372b5ce), SkBits2Float(0x433ebef2), SkBits2Float(0x4372b5ce), SkBits2Float(0x434919ea));  // 242.71f, 190.746f, 242.71f, 201.101f
+path.close();
+    SkPath path116(path);
+    builder.add(path116, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43212033), SkBits2Float(0x433c0771));  // 161.126f, 188.029f
+path.quadTo(SkBits2Float(0x43212033), SkBits2Float(0x43466268), SkBits2Float(0x4319cdaf), SkBits2Float(0x434db4ed));  // 161.126f, 198.384f, 153.803f, 205.707f
+path.quadTo(SkBits2Float(0x43127b2a), SkBits2Float(0x43550771), SkBits2Float(0x43082033), SkBits2Float(0x43550771));  // 146.481f, 213.029f, 136.126f, 213.029f
+path.quadTo(SkBits2Float(0x42fb8a77), SkBits2Float(0x43550771), SkBits2Float(0x42ece56e), SkBits2Float(0x434db4ed));  // 125.77f, 213.029f, 118.448f, 205.707f
+path.quadTo(SkBits2Float(0x42de4066), SkBits2Float(0x43466268), SkBits2Float(0x42de4066), SkBits2Float(0x433c0771));  // 111.126f, 198.384f, 111.126f, 188.029f
+path.quadTo(SkBits2Float(0x42de4066), SkBits2Float(0x4331ac7a), SkBits2Float(0x42ece56e), SkBits2Float(0x432a59f5));  // 111.126f, 177.674f, 118.448f, 170.351f
+path.quadTo(SkBits2Float(0x42fb8a77), SkBits2Float(0x43230771), SkBits2Float(0x43082033), SkBits2Float(0x43230771));  // 125.77f, 163.029f, 136.126f, 163.029f
+path.quadTo(SkBits2Float(0x43127b2a), SkBits2Float(0x43230771), SkBits2Float(0x4319cdaf), SkBits2Float(0x432a59f5));  // 146.481f, 163.029f, 153.803f, 170.351f
+path.quadTo(SkBits2Float(0x43212033), SkBits2Float(0x4331ac7a), SkBits2Float(0x43212033), SkBits2Float(0x433c0771));  // 161.126f, 177.674f, 161.126f, 188.029f
+path.close();
+    SkPath path117(path);
+    builder.add(path117, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x434d1191), SkBits2Float(0x431c5cdf));  // 205.069f, 156.363f
+path.quadTo(SkBits2Float(0x434d1191), SkBits2Float(0x4326b7d6), SkBits2Float(0x4345bf0d), SkBits2Float(0x432e0a5b));  // 205.069f, 166.718f, 197.746f, 174.04f
+path.quadTo(SkBits2Float(0x433e6c88), SkBits2Float(0x43355cdf), SkBits2Float(0x43341191), SkBits2Float(0x43355cdf));  // 190.424f, 181.363f, 180.069f, 181.363f
+path.quadTo(SkBits2Float(0x4329b69a), SkBits2Float(0x43355cdf), SkBits2Float(0x43226415), SkBits2Float(0x432e0a5b));  // 169.713f, 181.363f, 162.391f, 174.04f
+path.quadTo(SkBits2Float(0x431b1191), SkBits2Float(0x4326b7d6), SkBits2Float(0x431b1191), SkBits2Float(0x431c5cdf));  // 155.069f, 166.718f, 155.069f, 156.363f
+path.quadTo(SkBits2Float(0x431b1191), SkBits2Float(0x431201e8), SkBits2Float(0x43226415), SkBits2Float(0x430aaf63));  // 155.069f, 146.007f, 162.391f, 138.685f
+path.quadTo(SkBits2Float(0x4329b69a), SkBits2Float(0x43035cdf), SkBits2Float(0x43341191), SkBits2Float(0x43035cdf));  // 169.713f, 131.363f, 180.069f, 131.363f
+path.quadTo(SkBits2Float(0x433e6c88), SkBits2Float(0x43035cdf), SkBits2Float(0x4345bf0d), SkBits2Float(0x430aaf63));  // 190.424f, 131.363f, 197.746f, 138.685f
+path.quadTo(SkBits2Float(0x434d1191), SkBits2Float(0x431201e8), SkBits2Float(0x434d1191), SkBits2Float(0x431c5cdf));  // 205.069f, 146.007f, 205.069f, 156.363f
+path.close();
+    SkPath path118(path);
+    builder.add(path118, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43f7761c), SkBits2Float(0x43ba1964));  // 494.923f, 372.198f
+path.quadTo(SkBits2Float(0x43f7761c), SkBits2Float(0x43bf46e0), SkBits2Float(0x43f3ccda), SkBits2Float(0x43c2f022));  // 494.923f, 382.554f, 487.6f, 389.876f
+path.quadTo(SkBits2Float(0x43f02398), SkBits2Float(0x43c69964), SkBits2Float(0x43eaf61c), SkBits2Float(0x43c69964));  // 480.278f, 397.198f, 469.923f, 397.198f
+path.quadTo(SkBits2Float(0x43e5c8a0), SkBits2Float(0x43c69964), SkBits2Float(0x43e21f5e), SkBits2Float(0x43c2f022));  // 459.567f, 397.198f, 452.245f, 389.876f
+path.quadTo(SkBits2Float(0x43de761c), SkBits2Float(0x43bf46e0), SkBits2Float(0x43de761c), SkBits2Float(0x43ba1964));  // 444.923f, 382.554f, 444.923f, 372.198f
+path.quadTo(SkBits2Float(0x43de761c), SkBits2Float(0x43b4ebe8), SkBits2Float(0x43e21f5e), SkBits2Float(0x43b142a6));  // 444.923f, 361.843f, 452.245f, 354.521f
+path.quadTo(SkBits2Float(0x43e5c8a0), SkBits2Float(0x43ad9964), SkBits2Float(0x43eaf61c), SkBits2Float(0x43ad9964));  // 459.567f, 347.198f, 469.923f, 347.198f
+path.quadTo(SkBits2Float(0x43f02398), SkBits2Float(0x43ad9964), SkBits2Float(0x43f3ccda), SkBits2Float(0x43b142a6));  // 480.278f, 347.198f, 487.6f, 354.521f
+path.quadTo(SkBits2Float(0x43f7761c), SkBits2Float(0x43b4ebe8), SkBits2Float(0x43f7761c), SkBits2Float(0x43ba1964));  // 494.923f, 361.843f, 494.923f, 372.198f
+path.close();
+    SkPath path119(path);
+    builder.add(path119, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4387b4f4), SkBits2Float(0x4382767e));  // 271.414f, 260.926f
+path.quadTo(SkBits2Float(0x4387b4f4), SkBits2Float(0x4387a3fa), SkBits2Float(0x43840bb2), SkBits2Float(0x438b4d3c));  // 271.414f, 271.281f, 264.091f, 278.603f
+path.quadTo(SkBits2Float(0x43806270), SkBits2Float(0x438ef67e), SkBits2Float(0x437669e8), SkBits2Float(0x438ef67e));  // 256.769f, 285.926f, 246.414f, 285.926f
+path.quadTo(SkBits2Float(0x436c0ef1), SkBits2Float(0x438ef67e), SkBits2Float(0x4364bc6d), SkBits2Float(0x438b4d3c));  // 236.058f, 285.926f, 228.736f, 278.603f
+path.quadTo(SkBits2Float(0x435d69e9), SkBits2Float(0x4387a3fa), SkBits2Float(0x435d69e9), SkBits2Float(0x4382767e));  // 221.414f, 271.281f, 221.414f, 260.926f
+path.quadTo(SkBits2Float(0x435d69e9), SkBits2Float(0x437a9204), SkBits2Float(0x4364bc6d), SkBits2Float(0x43733f80));  // 221.414f, 250.57f, 228.736f, 243.248f
+path.quadTo(SkBits2Float(0x436c0ef1), SkBits2Float(0x436becfc), SkBits2Float(0x437669e8), SkBits2Float(0x436becfc));  // 236.058f, 235.926f, 246.414f, 235.926f
+path.quadTo(SkBits2Float(0x43806270), SkBits2Float(0x436becfc), SkBits2Float(0x43840bb2), SkBits2Float(0x43733f80));  // 256.769f, 235.926f, 264.091f, 243.248f
+path.quadTo(SkBits2Float(0x4387b4f4), SkBits2Float(0x437a9204), SkBits2Float(0x4387b4f4), SkBits2Float(0x4382767e));  // 271.414f, 250.57f, 271.414f, 260.926f
+path.close();
+    SkPath path120(path);
+    builder.add(path120, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43f176b1), SkBits2Float(0x437ef3b8));  // 482.927f, 254.952f
+path.quadTo(SkBits2Float(0x43f176b1), SkBits2Float(0x4384a758), SkBits2Float(0x43edcd6f), SkBits2Float(0x4388509a));  // 482.927f, 265.307f, 475.605f, 272.63f
+path.quadTo(SkBits2Float(0x43ea242d), SkBits2Float(0x438bf9dc), SkBits2Float(0x43e4f6b1), SkBits2Float(0x438bf9dc));  // 468.283f, 279.952f, 457.927f, 279.952f
+path.quadTo(SkBits2Float(0x43dfc935), SkBits2Float(0x438bf9dc), SkBits2Float(0x43dc1ff3), SkBits2Float(0x4388509a));  // 447.572f, 279.952f, 440.25f, 272.63f
+path.quadTo(SkBits2Float(0x43d876b1), SkBits2Float(0x4384a758), SkBits2Float(0x43d876b1), SkBits2Float(0x437ef3b8));  // 432.927f, 265.307f, 432.927f, 254.952f
+path.quadTo(SkBits2Float(0x43d876b1), SkBits2Float(0x437498c0), SkBits2Float(0x43dc1ff3), SkBits2Float(0x436d463c));  // 432.927f, 244.597f, 440.25f, 237.274f
+path.quadTo(SkBits2Float(0x43dfc935), SkBits2Float(0x4365f3b8), SkBits2Float(0x43e4f6b1), SkBits2Float(0x4365f3b8));  // 447.572f, 229.952f, 457.927f, 229.952f
+path.quadTo(SkBits2Float(0x43ea242d), SkBits2Float(0x4365f3b8), SkBits2Float(0x43edcd6f), SkBits2Float(0x436d463c));  // 468.283f, 229.952f, 475.605f, 237.274f
+path.quadTo(SkBits2Float(0x43f176b1), SkBits2Float(0x437498c0), SkBits2Float(0x43f176b1), SkBits2Float(0x437ef3b8));  // 482.927f, 244.597f, 482.927f, 254.952f
+path.close();
+    SkPath path121(path);
+    builder.add(path121, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x437bd45b), SkBits2Float(0x4361cf52));  // 251.83f, 225.81f
+path.quadTo(SkBits2Float(0x437bd45b), SkBits2Float(0x436c2a4a), SkBits2Float(0x437481d7), SkBits2Float(0x43737cce));  // 251.83f, 236.165f, 244.507f, 243.488f
+path.quadTo(SkBits2Float(0x436d2f52), SkBits2Float(0x437acf52), SkBits2Float(0x4362d45b), SkBits2Float(0x437acf52));  // 237.185f, 250.81f, 226.83f, 250.81f
+path.quadTo(SkBits2Float(0x43587964), SkBits2Float(0x437acf52), SkBits2Float(0x435126df), SkBits2Float(0x43737cce));  // 216.474f, 250.81f, 209.152f, 243.488f
+path.quadTo(SkBits2Float(0x4349d45b), SkBits2Float(0x436c2a4a), SkBits2Float(0x4349d45b), SkBits2Float(0x4361cf52));  // 201.83f, 236.165f, 201.83f, 225.81f
+path.quadTo(SkBits2Float(0x4349d45b), SkBits2Float(0x4357745a), SkBits2Float(0x435126df), SkBits2Float(0x435021d6));  // 201.83f, 215.454f, 209.152f, 208.132f
+path.quadTo(SkBits2Float(0x43587964), SkBits2Float(0x4348cf52), SkBits2Float(0x4362d45b), SkBits2Float(0x4348cf52));  // 216.474f, 200.81f, 226.83f, 200.81f
+path.quadTo(SkBits2Float(0x436d2f52), SkBits2Float(0x4348cf52), SkBits2Float(0x437481d7), SkBits2Float(0x435021d6));  // 237.185f, 200.81f, 244.507f, 208.132f
+path.quadTo(SkBits2Float(0x437bd45b), SkBits2Float(0x4357745a), SkBits2Float(0x437bd45b), SkBits2Float(0x4361cf52));  // 251.83f, 215.454f, 251.83f, 225.81f
+path.close();
+    SkPath path122(path);
+    builder.add(path122, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43bb783c), SkBits2Float(0x43d20000));  // 374.939f, 420
+path.quadTo(SkBits2Float(0x43bb783c), SkBits2Float(0x43d72d7c), SkBits2Float(0x43b7cefa), SkBits2Float(0x43dad6be));  // 374.939f, 430.355f, 367.617f, 437.678f
+path.quadTo(SkBits2Float(0x43b425b8), SkBits2Float(0x43de8000), SkBits2Float(0x43aef83c), SkBits2Float(0x43de8000));  // 360.295f, 445, 349.939f, 445
+path.quadTo(SkBits2Float(0x43a9cac0), SkBits2Float(0x43de8000), SkBits2Float(0x43a6217e), SkBits2Float(0x43dad6be));  // 339.584f, 445, 332.262f, 437.678f
+path.quadTo(SkBits2Float(0x43a2783c), SkBits2Float(0x43d72d7c), SkBits2Float(0x43a2783c), SkBits2Float(0x43d20000));  // 324.939f, 430.355f, 324.939f, 420
+path.quadTo(SkBits2Float(0x43a2783c), SkBits2Float(0x43ccd284), SkBits2Float(0x43a6217e), SkBits2Float(0x43c92942));  // 324.939f, 409.645f, 332.262f, 402.322f
+path.quadTo(SkBits2Float(0x43a9cac0), SkBits2Float(0x43c58000), SkBits2Float(0x43aef83c), SkBits2Float(0x43c58000));  // 339.584f, 395, 349.939f, 395
+path.quadTo(SkBits2Float(0x43b425b8), SkBits2Float(0x43c58000), SkBits2Float(0x43b7cefa), SkBits2Float(0x43c92942));  // 360.295f, 395, 367.617f, 402.322f
+path.quadTo(SkBits2Float(0x43bb783c), SkBits2Float(0x43ccd284), SkBits2Float(0x43bb783c), SkBits2Float(0x43d20000));  // 374.939f, 409.645f, 374.939f, 420
+path.close();
+    SkPath path123(path);
+    builder.add(path123, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43ca2436), SkBits2Float(0x435899a3));  // 404.283f, 216.6f
+path.quadTo(SkBits2Float(0x43ca2436), SkBits2Float(0x4362f49a), SkBits2Float(0x43c67af4), SkBits2Float(0x436a471f));  // 404.283f, 226.955f, 396.961f, 234.278f
+path.quadTo(SkBits2Float(0x43c2d1b2), SkBits2Float(0x437199a3), SkBits2Float(0x43bda436), SkBits2Float(0x437199a3));  // 389.638f, 241.6f, 379.283f, 241.6f
+path.quadTo(SkBits2Float(0x43b876ba), SkBits2Float(0x437199a3), SkBits2Float(0x43b4cd78), SkBits2Float(0x436a471f));  // 368.928f, 241.6f, 361.605f, 234.278f
+path.quadTo(SkBits2Float(0x43b12436), SkBits2Float(0x4362f49a), SkBits2Float(0x43b12436), SkBits2Float(0x435899a3));  // 354.283f, 226.955f, 354.283f, 216.6f
+path.quadTo(SkBits2Float(0x43b12436), SkBits2Float(0x434e3eac), SkBits2Float(0x43b4cd78), SkBits2Float(0x4346ec27));  // 354.283f, 206.245f, 361.605f, 198.922f
+path.quadTo(SkBits2Float(0x43b876ba), SkBits2Float(0x433f99a3), SkBits2Float(0x43bda436), SkBits2Float(0x433f99a3));  // 368.928f, 191.6f, 379.283f, 191.6f
+path.quadTo(SkBits2Float(0x43c2d1b2), SkBits2Float(0x433f99a3), SkBits2Float(0x43c67af4), SkBits2Float(0x4346ec27));  // 389.638f, 191.6f, 396.961f, 198.922f
+path.quadTo(SkBits2Float(0x43ca2436), SkBits2Float(0x434e3eac), SkBits2Float(0x43ca2436), SkBits2Float(0x435899a3));  // 404.283f, 206.245f, 404.283f, 216.6f
+path.close();
+    SkPath path124(path);
+    builder.add(path124, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x440faed8), SkBits2Float(0x43474968));  // 574.732f, 199.287f
+path.quadTo(SkBits2Float(0x440faed8), SkBits2Float(0x4351a460), SkBits2Float(0x440dda37), SkBits2Float(0x4358f6e4));  // 574.732f, 209.642f, 567.41f, 216.964f
+path.quadTo(SkBits2Float(0x440c0596), SkBits2Float(0x43604968), SkBits2Float(0x44096ed8), SkBits2Float(0x43604968));  // 560.087f, 224.287f, 549.732f, 224.287f
+path.quadTo(SkBits2Float(0x4406d81a), SkBits2Float(0x43604968), SkBits2Float(0x44050379), SkBits2Float(0x4358f6e4));  // 539.377f, 224.287f, 532.054f, 216.964f
+path.quadTo(SkBits2Float(0x44032ed8), SkBits2Float(0x4351a460), SkBits2Float(0x44032ed8), SkBits2Float(0x43474968));  // 524.732f, 209.642f, 524.732f, 199.287f
+path.quadTo(SkBits2Float(0x44032ed8), SkBits2Float(0x433cee70), SkBits2Float(0x44050379), SkBits2Float(0x43359bec));  // 524.732f, 188.931f, 532.054f, 181.609f
+path.quadTo(SkBits2Float(0x4406d81a), SkBits2Float(0x432e4968), SkBits2Float(0x44096ed8), SkBits2Float(0x432e4968));  // 539.377f, 174.287f, 549.732f, 174.287f
+path.quadTo(SkBits2Float(0x440c0596), SkBits2Float(0x432e4968), SkBits2Float(0x440dda37), SkBits2Float(0x43359bec));  // 560.087f, 174.287f, 567.41f, 181.609f
+path.quadTo(SkBits2Float(0x440faed8), SkBits2Float(0x433cee70), SkBits2Float(0x440faed8), SkBits2Float(0x43474968));  // 574.732f, 188.931f, 574.732f, 199.287f
+path.close();
+    SkPath path125(path);
+    builder.add(path125, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x437055b3), SkBits2Float(0x438ae764));  // 240.335f, 277.808f
+path.quadTo(SkBits2Float(0x437055b3), SkBits2Float(0x439014e0), SkBits2Float(0x4369032f), SkBits2Float(0x4393be22));  // 240.335f, 288.163f, 233.012f, 295.485f
+path.quadTo(SkBits2Float(0x4361b0aa), SkBits2Float(0x43976764), SkBits2Float(0x435755b3), SkBits2Float(0x43976764));  // 225.69f, 302.808f, 215.335f, 302.808f
+path.quadTo(SkBits2Float(0x434cfabc), SkBits2Float(0x43976764), SkBits2Float(0x4345a837), SkBits2Float(0x4393be22));  // 204.979f, 302.808f, 197.657f, 295.485f
+path.quadTo(SkBits2Float(0x433e55b3), SkBits2Float(0x439014e0), SkBits2Float(0x433e55b3), SkBits2Float(0x438ae764));  // 190.335f, 288.163f, 190.335f, 277.808f
+path.quadTo(SkBits2Float(0x433e55b3), SkBits2Float(0x4385b9e8), SkBits2Float(0x4345a837), SkBits2Float(0x438210a6));  // 190.335f, 267.452f, 197.657f, 260.13f
+path.quadTo(SkBits2Float(0x434cfabc), SkBits2Float(0x437ccec8), SkBits2Float(0x435755b3), SkBits2Float(0x437ccec8));  // 204.979f, 252.808f, 215.335f, 252.808f
+path.quadTo(SkBits2Float(0x4361b0aa), SkBits2Float(0x437ccec8), SkBits2Float(0x4369032f), SkBits2Float(0x438210a6));  // 225.69f, 252.808f, 233.012f, 260.13f
+path.quadTo(SkBits2Float(0x437055b3), SkBits2Float(0x4385b9e8), SkBits2Float(0x437055b3), SkBits2Float(0x438ae764));  // 240.335f, 267.452f, 240.335f, 277.808f
+path.close();
+    SkPath path126(path);
+    builder.add(path126, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43a201c9), SkBits2Float(0x43546e88));  // 324.014f, 212.432f
+path.quadTo(SkBits2Float(0x43a201c9), SkBits2Float(0x435ec980), SkBits2Float(0x439e5887), SkBits2Float(0x43661c04));  // 324.014f, 222.787f, 316.692f, 230.109f
+path.quadTo(SkBits2Float(0x439aaf45), SkBits2Float(0x436d6e88), SkBits2Float(0x439581c9), SkBits2Float(0x436d6e88));  // 309.369f, 237.432f, 299.014f, 237.432f
+path.quadTo(SkBits2Float(0x4390544d), SkBits2Float(0x436d6e88), SkBits2Float(0x438cab0b), SkBits2Float(0x43661c04));  // 288.659f, 237.432f, 281.336f, 230.109f
+path.quadTo(SkBits2Float(0x438901c9), SkBits2Float(0x435ec980), SkBits2Float(0x438901c9), SkBits2Float(0x43546e88));  // 274.014f, 222.787f, 274.014f, 212.432f
+path.quadTo(SkBits2Float(0x438901c9), SkBits2Float(0x434a1390), SkBits2Float(0x438cab0b), SkBits2Float(0x4342c10c));  // 274.014f, 202.076f, 281.336f, 194.754f
+path.quadTo(SkBits2Float(0x4390544d), SkBits2Float(0x433b6e88), SkBits2Float(0x439581c9), SkBits2Float(0x433b6e88));  // 288.659f, 187.432f, 299.014f, 187.432f
+path.quadTo(SkBits2Float(0x439aaf45), SkBits2Float(0x433b6e88), SkBits2Float(0x439e5887), SkBits2Float(0x4342c10c));  // 309.369f, 187.432f, 316.692f, 194.754f
+path.quadTo(SkBits2Float(0x43a201c9), SkBits2Float(0x434a1390), SkBits2Float(0x43a201c9), SkBits2Float(0x43546e88));  // 324.014f, 202.076f, 324.014f, 212.432f
+path.close();
+    SkPath path127(path);
+    builder.add(path127, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42d10265), SkBits2Float(0x434da766));  // 104.505f, 205.654f
+path.quadTo(SkBits2Float(0x42d10265), SkBits2Float(0x4358025e), SkBits2Float(0x42c25d5c), SkBits2Float(0x435f54e2));  // 104.505f, 216.009f, 97.1823f, 223.332f
+path.quadTo(SkBits2Float(0x42b3b854), SkBits2Float(0x4366a766), SkBits2Float(0x429f0265), SkBits2Float(0x4366a766));  // 89.86f, 230.654f, 79.5047f, 230.654f
+path.quadTo(SkBits2Float(0x428a4c76), SkBits2Float(0x4366a766), SkBits2Float(0x42774edb), SkBits2Float(0x435f54e2));  // 69.1493f, 230.654f, 61.827f, 223.332f
+path.quadTo(SkBits2Float(0x425a04ca), SkBits2Float(0x4358025e), SkBits2Float(0x425a04ca), SkBits2Float(0x434da766));  // 54.5047f, 216.009f, 54.5047f, 205.654f
+path.quadTo(SkBits2Float(0x425a04ca), SkBits2Float(0x43434c6e), SkBits2Float(0x42774edb), SkBits2Float(0x433bf9ea));  // 54.5047f, 195.299f, 61.827f, 187.976f
+path.quadTo(SkBits2Float(0x428a4c76), SkBits2Float(0x4334a766), SkBits2Float(0x429f0265), SkBits2Float(0x4334a766));  // 69.1493f, 180.654f, 79.5047f, 180.654f
+path.quadTo(SkBits2Float(0x42b3b854), SkBits2Float(0x4334a766), SkBits2Float(0x42c25d5c), SkBits2Float(0x433bf9ea));  // 89.86f, 180.654f, 97.1823f, 187.976f
+path.quadTo(SkBits2Float(0x42d10265), SkBits2Float(0x43434c6e), SkBits2Float(0x42d10265), SkBits2Float(0x434da766));  // 104.505f, 195.299f, 104.505f, 205.654f
+path.close();
+    SkPath path128(path);
+    builder.add(path128, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4396e5f4), SkBits2Float(0x432540f2));  // 301.797f, 165.254f
+path.quadTo(SkBits2Float(0x4396e5f4), SkBits2Float(0x432f9bea), SkBits2Float(0x43933cb2), SkBits2Float(0x4336ee6e));  // 301.797f, 175.609f, 294.474f, 182.931f
+path.quadTo(SkBits2Float(0x438f9370), SkBits2Float(0x433e40f2), SkBits2Float(0x438a65f4), SkBits2Float(0x433e40f2));  // 287.152f, 190.254f, 276.797f, 190.254f
+path.quadTo(SkBits2Float(0x43853878), SkBits2Float(0x433e40f2), SkBits2Float(0x43818f36), SkBits2Float(0x4336ee6e));  // 266.441f, 190.254f, 259.119f, 182.931f
+path.quadTo(SkBits2Float(0x437bcbe8), SkBits2Float(0x432f9bea), SkBits2Float(0x437bcbe8), SkBits2Float(0x432540f2));  // 251.797f, 175.609f, 251.797f, 165.254f
+path.quadTo(SkBits2Float(0x437bcbe8), SkBits2Float(0x431ae5fa), SkBits2Float(0x43818f36), SkBits2Float(0x43139376));  // 251.797f, 154.898f, 259.119f, 147.576f
+path.quadTo(SkBits2Float(0x43853878), SkBits2Float(0x430c40f2), SkBits2Float(0x438a65f4), SkBits2Float(0x430c40f2));  // 266.441f, 140.254f, 276.797f, 140.254f
+path.quadTo(SkBits2Float(0x438f9370), SkBits2Float(0x430c40f2), SkBits2Float(0x43933cb2), SkBits2Float(0x43139376));  // 287.152f, 140.254f, 294.474f, 147.576f
+path.quadTo(SkBits2Float(0x4396e5f4), SkBits2Float(0x431ae5fa), SkBits2Float(0x4396e5f4), SkBits2Float(0x432540f2));  // 301.797f, 154.898f, 301.797f, 165.254f
+path.close();
+    SkPath path129(path);
+    builder.add(path129, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43759ab0), SkBits2Float(0x43329c4b));  // 245.604f, 178.611f
+path.quadTo(SkBits2Float(0x43759ab0), SkBits2Float(0x433cf742), SkBits2Float(0x436e482c), SkBits2Float(0x434449c7));  // 245.604f, 188.966f, 238.282f, 196.288f
+path.quadTo(SkBits2Float(0x4366f5a8), SkBits2Float(0x434b9c4b), SkBits2Float(0x435c9ab0), SkBits2Float(0x434b9c4b));  // 230.96f, 203.611f, 220.604f, 203.611f
+path.quadTo(SkBits2Float(0x43523fb8), SkBits2Float(0x434b9c4b), SkBits2Float(0x434aed34), SkBits2Float(0x434449c7));  // 210.249f, 203.611f, 202.927f, 196.288f
+path.quadTo(SkBits2Float(0x43439ab0), SkBits2Float(0x433cf742), SkBits2Float(0x43439ab0), SkBits2Float(0x43329c4b));  // 195.604f, 188.966f, 195.604f, 178.611f
+path.quadTo(SkBits2Float(0x43439ab0), SkBits2Float(0x43284154), SkBits2Float(0x434aed34), SkBits2Float(0x4320eecf));  // 195.604f, 168.255f, 202.927f, 160.933f
+path.quadTo(SkBits2Float(0x43523fb8), SkBits2Float(0x43199c4b), SkBits2Float(0x435c9ab0), SkBits2Float(0x43199c4b));  // 210.249f, 153.611f, 220.604f, 153.611f
+path.quadTo(SkBits2Float(0x4366f5a8), SkBits2Float(0x43199c4b), SkBits2Float(0x436e482c), SkBits2Float(0x4320eecf));  // 230.96f, 153.611f, 238.282f, 160.933f
+path.quadTo(SkBits2Float(0x43759ab0), SkBits2Float(0x43284154), SkBits2Float(0x43759ab0), SkBits2Float(0x43329c4b));  // 245.604f, 168.255f, 245.604f, 178.611f
+path.close();
+    SkPath path130(path);
+    builder.add(path130, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43a6e3cc), SkBits2Float(0x43954ede));  // 333.78f, 298.616f
+path.quadTo(SkBits2Float(0x43a6e3cc), SkBits2Float(0x439a7c5a), SkBits2Float(0x43a33a8a), SkBits2Float(0x439e259c));  // 333.78f, 308.971f, 326.457f, 316.294f
+path.quadTo(SkBits2Float(0x439f9148), SkBits2Float(0x43a1cede), SkBits2Float(0x439a63cc), SkBits2Float(0x43a1cede));  // 319.135f, 323.616f, 308.78f, 323.616f
+path.quadTo(SkBits2Float(0x43953650), SkBits2Float(0x43a1cede), SkBits2Float(0x43918d0e), SkBits2Float(0x439e259c));  // 298.424f, 323.616f, 291.102f, 316.294f
+path.quadTo(SkBits2Float(0x438de3cc), SkBits2Float(0x439a7c5a), SkBits2Float(0x438de3cc), SkBits2Float(0x43954ede));  // 283.78f, 308.971f, 283.78f, 298.616f
+path.quadTo(SkBits2Float(0x438de3cc), SkBits2Float(0x43902162), SkBits2Float(0x43918d0e), SkBits2Float(0x438c7820));  // 283.78f, 288.261f, 291.102f, 280.938f
+path.quadTo(SkBits2Float(0x43953650), SkBits2Float(0x4388cede), SkBits2Float(0x439a63cc), SkBits2Float(0x4388cede));  // 298.424f, 273.616f, 308.78f, 273.616f
+path.quadTo(SkBits2Float(0x439f9148), SkBits2Float(0x4388cede), SkBits2Float(0x43a33a8a), SkBits2Float(0x438c7820));  // 319.135f, 273.616f, 326.457f, 280.938f
+path.quadTo(SkBits2Float(0x43a6e3cc), SkBits2Float(0x43902162), SkBits2Float(0x43a6e3cc), SkBits2Float(0x43954ede));  // 333.78f, 288.261f, 333.78f, 298.616f
+path.close();
+    SkPath path131(path);
+    builder.add(path131, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x428e400e), SkBits2Float(0x43a02b61));  // 71.1251f, 320.339f
+path.quadTo(SkBits2Float(0x428e400e), SkBits2Float(0x43a558dd), SkBits2Float(0x427f360c), SkBits2Float(0x43a9021f));  // 71.1251f, 330.694f, 63.8028f, 338.017f
+path.quadTo(SkBits2Float(0x4261ebfa), SkBits2Float(0x43acab61), SkBits2Float(0x4238801c), SkBits2Float(0x43acab61));  // 56.4804f, 345.339f, 46.1251f, 345.339f
+path.quadTo(SkBits2Float(0x420f143e), SkBits2Float(0x43acab61), SkBits2Float(0x41e39459), SkBits2Float(0x43a9021f));  // 35.7698f, 345.339f, 28.4474f, 338.017f
+path.quadTo(SkBits2Float(0x41a90036), SkBits2Float(0x43a558dd), SkBits2Float(0x41a90036), SkBits2Float(0x43a02b61));  // 21.1251f, 330.694f, 21.1251f, 320.339f
+path.quadTo(SkBits2Float(0x41a90036), SkBits2Float(0x439afde5), SkBits2Float(0x41e39459), SkBits2Float(0x439754a3));  // 21.1251f, 309.984f, 28.4474f, 302.661f
+path.quadTo(SkBits2Float(0x420f143e), SkBits2Float(0x4393ab61), SkBits2Float(0x4238801c), SkBits2Float(0x4393ab61));  // 35.7698f, 295.339f, 46.1251f, 295.339f
+path.quadTo(SkBits2Float(0x4261ebfa), SkBits2Float(0x4393ab61), SkBits2Float(0x427f360c), SkBits2Float(0x439754a3));  // 56.4804f, 295.339f, 63.8028f, 302.661f
+path.quadTo(SkBits2Float(0x428e400e), SkBits2Float(0x439afde5), SkBits2Float(0x428e400e), SkBits2Float(0x43a02b61));  // 71.1251f, 309.984f, 71.1251f, 320.339f
+path.close();
+    SkPath path132(path);
+    builder.add(path132, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4323b27c), SkBits2Float(0x4379f49c));  // 163.697f, 249.956f
+path.quadTo(SkBits2Float(0x4323b27c), SkBits2Float(0x438227ca), SkBits2Float(0x431c5ff8), SkBits2Float(0x4385d10c));  // 163.697f, 260.311f, 156.375f, 267.633f
+path.quadTo(SkBits2Float(0x43150d74), SkBits2Float(0x43897a4e), SkBits2Float(0x430ab27c), SkBits2Float(0x43897a4e));  // 149.053f, 274.956f, 138.697f, 274.956f
+path.quadTo(SkBits2Float(0x43005784), SkBits2Float(0x43897a4e), SkBits2Float(0x42f20a00), SkBits2Float(0x4385d10c));  // 128.342f, 274.956f, 121.02f, 267.633f
+path.quadTo(SkBits2Float(0x42e364f8), SkBits2Float(0x438227ca), SkBits2Float(0x42e364f8), SkBits2Float(0x4379f49c));  // 113.697f, 260.311f, 113.697f, 249.956f
+path.quadTo(SkBits2Float(0x42e364f8), SkBits2Float(0x436f99a4), SkBits2Float(0x42f20a00), SkBits2Float(0x43684720));  // 113.697f, 239.6f, 121.02f, 232.278f
+path.quadTo(SkBits2Float(0x43005784), SkBits2Float(0x4360f49c), SkBits2Float(0x430ab27c), SkBits2Float(0x4360f49c));  // 128.342f, 224.956f, 138.697f, 224.956f
+path.quadTo(SkBits2Float(0x43150d74), SkBits2Float(0x4360f49c), SkBits2Float(0x431c5ff8), SkBits2Float(0x43684720));  // 149.053f, 224.956f, 156.375f, 232.278f
+path.quadTo(SkBits2Float(0x4323b27c), SkBits2Float(0x436f99a4), SkBits2Float(0x4323b27c), SkBits2Float(0x4379f49c));  // 163.697f, 239.6f, 163.697f, 249.956f
+path.close();
+    SkPath path133(path);
+    builder.add(path133, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43629af6), SkBits2Float(0x434de1f1));  // 226.605f, 205.883f
+path.quadTo(SkBits2Float(0x43629af6), SkBits2Float(0x43583ce8), SkBits2Float(0x435b4872), SkBits2Float(0x435f8f6d));  // 226.605f, 216.238f, 219.283f, 223.56f
+path.quadTo(SkBits2Float(0x4353f5ee), SkBits2Float(0x4366e1f1), SkBits2Float(0x43499af6), SkBits2Float(0x4366e1f1));  // 211.961f, 230.883f, 201.605f, 230.883f
+path.quadTo(SkBits2Float(0x433f3ffe), SkBits2Float(0x4366e1f1), SkBits2Float(0x4337ed7a), SkBits2Float(0x435f8f6d));  // 191.25f, 230.883f, 183.928f, 223.56f
+path.quadTo(SkBits2Float(0x43309af6), SkBits2Float(0x43583ce8), SkBits2Float(0x43309af6), SkBits2Float(0x434de1f1));  // 176.605f, 216.238f, 176.605f, 205.883f
+path.quadTo(SkBits2Float(0x43309af6), SkBits2Float(0x434386fa), SkBits2Float(0x4337ed7a), SkBits2Float(0x433c3475));  // 176.605f, 195.527f, 183.928f, 188.205f
+path.quadTo(SkBits2Float(0x433f3ffe), SkBits2Float(0x4334e1f1), SkBits2Float(0x43499af6), SkBits2Float(0x4334e1f1));  // 191.25f, 180.883f, 201.605f, 180.883f
+path.quadTo(SkBits2Float(0x4353f5ee), SkBits2Float(0x4334e1f1), SkBits2Float(0x435b4872), SkBits2Float(0x433c3475));  // 211.961f, 180.883f, 219.283f, 188.205f
+path.quadTo(SkBits2Float(0x43629af6), SkBits2Float(0x434386fa), SkBits2Float(0x43629af6), SkBits2Float(0x434de1f1));  // 226.605f, 195.527f, 226.605f, 205.883f
+path.close();
+    SkPath path134(path);
+    builder.add(path134, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43969933), SkBits2Float(0x00000000));  // 301.197f, 0
+path.quadTo(SkBits2Float(0x43969933), SkBits2Float(0x4125af78), SkBits2Float(0x4392eff1), SkBits2Float(0x418d6bde));  // 301.197f, 10.3553f, 293.875f, 17.6777f
+path.quadTo(SkBits2Float(0x438f46af), SkBits2Float(0x41c80000), SkBits2Float(0x438a1933), SkBits2Float(0x41c80000));  // 286.552f, 25, 276.197f, 25
+path.quadTo(SkBits2Float(0x4384ebb7), SkBits2Float(0x41c80000), SkBits2Float(0x43814275), SkBits2Float(0x418d6bde));  // 265.842f, 25, 258.519f, 17.6777f
+path.quadTo(SkBits2Float(0x437b3266), SkBits2Float(0x4125af78), SkBits2Float(0x437b3266), SkBits2Float(0x00000000));  // 251.197f, 10.3553f, 251.197f, 0
+path.quadTo(SkBits2Float(0x437b3266), SkBits2Float(0xc125af78), SkBits2Float(0x43814275), SkBits2Float(0xc18d6bde));  // 251.197f, -10.3553f, 258.519f, -17.6777f
+path.quadTo(SkBits2Float(0x4384ebb7), SkBits2Float(0xc1c80000), SkBits2Float(0x438a1933), SkBits2Float(0xc1c80000));  // 265.842f, -25, 276.197f, -25
+path.quadTo(SkBits2Float(0x438f46af), SkBits2Float(0xc1c80000), SkBits2Float(0x4392eff1), SkBits2Float(0xc18d6bde));  // 286.552f, -25, 293.875f, -17.6777f
+path.quadTo(SkBits2Float(0x43969933), SkBits2Float(0xc125af78), SkBits2Float(0x43969933), SkBits2Float(0x00000000));  // 301.197f, -10.3553f, 301.197f, 0
+path.close();
+    SkPath path135(path);
+    builder.add(path135, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43310d09), SkBits2Float(0x43ab2014));  // 177.051f, 342.251f
+path.quadTo(SkBits2Float(0x43310d09), SkBits2Float(0x43b04d90), SkBits2Float(0x4329ba85), SkBits2Float(0x43b3f6d2));  // 177.051f, 352.606f, 169.729f, 359.928f
+path.quadTo(SkBits2Float(0x43226800), SkBits2Float(0x43b7a014), SkBits2Float(0x43180d09), SkBits2Float(0x43b7a014));  // 162.406f, 367.251f, 152.051f, 367.251f
+path.quadTo(SkBits2Float(0x430db212), SkBits2Float(0x43b7a014), SkBits2Float(0x43065f8d), SkBits2Float(0x43b3f6d2));  // 141.696f, 367.251f, 134.373f, 359.928f
+path.quadTo(SkBits2Float(0x42fe1a12), SkBits2Float(0x43b04d90), SkBits2Float(0x42fe1a12), SkBits2Float(0x43ab2014));  // 127.051f, 352.606f, 127.051f, 342.251f
+path.quadTo(SkBits2Float(0x42fe1a12), SkBits2Float(0x43a5f298), SkBits2Float(0x43065f8d), SkBits2Float(0x43a24956));  // 127.051f, 331.895f, 134.373f, 324.573f
+path.quadTo(SkBits2Float(0x430db212), SkBits2Float(0x439ea014), SkBits2Float(0x43180d09), SkBits2Float(0x439ea014));  // 141.696f, 317.251f, 152.051f, 317.251f
+path.quadTo(SkBits2Float(0x43226800), SkBits2Float(0x439ea014), SkBits2Float(0x4329ba85), SkBits2Float(0x43a24956));  // 162.406f, 317.251f, 169.729f, 324.573f
+path.quadTo(SkBits2Float(0x43310d09), SkBits2Float(0x43a5f298), SkBits2Float(0x43310d09), SkBits2Float(0x43ab2014));  // 177.051f, 331.895f, 177.051f, 342.251f
+path.close();
+    SkPath path136(path);
+    builder.add(path136, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43c19844), SkBits2Float(0x43818fc9));  // 387.19f, 259.123f
+path.quadTo(SkBits2Float(0x43c19844), SkBits2Float(0x4386bd45), SkBits2Float(0x43bdef02), SkBits2Float(0x438a6687));  // 387.19f, 269.479f, 379.867f, 276.801f
+path.quadTo(SkBits2Float(0x43ba45c0), SkBits2Float(0x438e0fc9), SkBits2Float(0x43b51844), SkBits2Float(0x438e0fc9));  // 372.545f, 284.123f, 362.19f, 284.123f
+path.quadTo(SkBits2Float(0x43afeac8), SkBits2Float(0x438e0fc9), SkBits2Float(0x43ac4186), SkBits2Float(0x438a6687));  // 351.834f, 284.123f, 344.512f, 276.801f
+path.quadTo(SkBits2Float(0x43a89844), SkBits2Float(0x4386bd45), SkBits2Float(0x43a89844), SkBits2Float(0x43818fc9));  // 337.19f, 269.479f, 337.19f, 259.123f
+path.quadTo(SkBits2Float(0x43a89844), SkBits2Float(0x4378c49a), SkBits2Float(0x43ac4186), SkBits2Float(0x43717216));  // 337.19f, 248.768f, 344.512f, 241.446f
+path.quadTo(SkBits2Float(0x43afeac8), SkBits2Float(0x436a1f92), SkBits2Float(0x43b51844), SkBits2Float(0x436a1f92));  // 351.834f, 234.123f, 362.19f, 234.123f
+path.quadTo(SkBits2Float(0x43ba45c0), SkBits2Float(0x436a1f92), SkBits2Float(0x43bdef02), SkBits2Float(0x43717216));  // 372.545f, 234.123f, 379.867f, 241.446f
+path.quadTo(SkBits2Float(0x43c19844), SkBits2Float(0x4378c49a), SkBits2Float(0x43c19844), SkBits2Float(0x43818fc9));  // 387.19f, 248.768f, 387.19f, 259.123f
+path.close();
+    SkPath path137(path);
+    builder.add(path137, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43565ef5), SkBits2Float(0x42de6e20));  // 214.371f, 111.215f
+path.quadTo(SkBits2Float(0x43565ef5), SkBits2Float(0x42f3240f), SkBits2Float(0x434f0c71), SkBits2Float(0x4300e48c));  // 214.371f, 121.57f, 207.049f, 128.893f
+path.quadTo(SkBits2Float(0x4347b9ec), SkBits2Float(0x43083710), SkBits2Float(0x433d5ef5), SkBits2Float(0x43083710));  // 199.726f, 136.215f, 189.371f, 136.215f
+path.quadTo(SkBits2Float(0x433303fe), SkBits2Float(0x43083710), SkBits2Float(0x432bb179), SkBits2Float(0x4300e48c));  // 179.016f, 136.215f, 171.693f, 128.893f
+path.quadTo(SkBits2Float(0x43245ef5), SkBits2Float(0x42f3240f), SkBits2Float(0x43245ef5), SkBits2Float(0x42de6e20));  // 164.371f, 121.57f, 164.371f, 111.215f
+path.quadTo(SkBits2Float(0x43245ef5), SkBits2Float(0x42c9b831), SkBits2Float(0x432bb179), SkBits2Float(0x42bb1329));  // 164.371f, 100.86f, 171.693f, 93.5374f
+path.quadTo(SkBits2Float(0x433303fe), SkBits2Float(0x42ac6e21), SkBits2Float(0x433d5ef5), SkBits2Float(0x42ac6e21));  // 179.016f, 86.2151f, 189.371f, 86.2151f
+path.quadTo(SkBits2Float(0x4347b9ec), SkBits2Float(0x42ac6e21), SkBits2Float(0x434f0c71), SkBits2Float(0x42bb1329));  // 199.726f, 86.2151f, 207.049f, 93.5374f
+path.quadTo(SkBits2Float(0x43565ef5), SkBits2Float(0x42c9b831), SkBits2Float(0x43565ef5), SkBits2Float(0x42de6e20));  // 214.371f, 100.86f, 214.371f, 111.215f
+path.close();
+    SkPath path138(path);
+    builder.add(path138, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43e8a73d), SkBits2Float(0x439f6574));  // 465.307f, 318.793f
+path.quadTo(SkBits2Float(0x43e8a73d), SkBits2Float(0x43a492f0), SkBits2Float(0x43e4fdfb), SkBits2Float(0x43a83c32));  // 465.307f, 329.148f, 457.984f, 336.47f
+path.quadTo(SkBits2Float(0x43e154b9), SkBits2Float(0x43abe574), SkBits2Float(0x43dc273d), SkBits2Float(0x43abe574));  // 450.662f, 343.793f, 440.307f, 343.793f
+path.quadTo(SkBits2Float(0x43d6f9c1), SkBits2Float(0x43abe574), SkBits2Float(0x43d3507f), SkBits2Float(0x43a83c32));  // 429.951f, 343.793f, 422.629f, 336.47f
+path.quadTo(SkBits2Float(0x43cfa73d), SkBits2Float(0x43a492f0), SkBits2Float(0x43cfa73d), SkBits2Float(0x439f6574));  // 415.307f, 329.148f, 415.307f, 318.793f
+path.quadTo(SkBits2Float(0x43cfa73d), SkBits2Float(0x439a37f8), SkBits2Float(0x43d3507f), SkBits2Float(0x43968eb6));  // 415.307f, 308.437f, 422.629f, 301.115f
+path.quadTo(SkBits2Float(0x43d6f9c1), SkBits2Float(0x4392e574), SkBits2Float(0x43dc273d), SkBits2Float(0x4392e574));  // 429.951f, 293.793f, 440.307f, 293.793f
+path.quadTo(SkBits2Float(0x43e154b9), SkBits2Float(0x4392e574), SkBits2Float(0x43e4fdfb), SkBits2Float(0x43968eb6));  // 450.662f, 293.793f, 457.984f, 301.115f
+path.quadTo(SkBits2Float(0x43e8a73d), SkBits2Float(0x439a37f8), SkBits2Float(0x43e8a73d), SkBits2Float(0x439f6574));  // 465.307f, 308.437f, 465.307f, 318.793f
+path.close();
+    SkPath path139(path);
+    builder.add(path139, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43abcc05), SkBits2Float(0x42d3af00));  // 343.594f, 105.842f
+path.quadTo(SkBits2Float(0x43abcc05), SkBits2Float(0x42e864ef), SkBits2Float(0x43a822c3), SkBits2Float(0x42f709f8));  // 343.594f, 116.197f, 336.272f, 123.519f
+path.quadTo(SkBits2Float(0x43a47981), SkBits2Float(0x4302d780), SkBits2Float(0x439f4c05), SkBits2Float(0x4302d780));  // 328.949f, 130.842f, 318.594f, 130.842f
+path.quadTo(SkBits2Float(0x439a1e89), SkBits2Float(0x4302d780), SkBits2Float(0x43967547), SkBits2Float(0x42f709f8));  // 308.239f, 130.842f, 300.916f, 123.519f
+path.quadTo(SkBits2Float(0x4392cc05), SkBits2Float(0x42e864ef), SkBits2Float(0x4392cc05), SkBits2Float(0x42d3af00));  // 293.594f, 116.197f, 293.594f, 105.842f
+path.quadTo(SkBits2Float(0x4392cc05), SkBits2Float(0x42bef911), SkBits2Float(0x43967547), SkBits2Float(0x42b05408));  // 293.594f, 95.4865f, 300.916f, 88.1641f
+path.quadTo(SkBits2Float(0x439a1e89), SkBits2Float(0x42a1aeff), SkBits2Float(0x439f4c05), SkBits2Float(0x42a1aeff));  // 308.239f, 80.8418f, 318.594f, 80.8418f
+path.quadTo(SkBits2Float(0x43a47981), SkBits2Float(0x42a1aeff), SkBits2Float(0x43a822c3), SkBits2Float(0x42b05408));  // 328.949f, 80.8418f, 336.272f, 88.1641f
+path.quadTo(SkBits2Float(0x43abcc05), SkBits2Float(0x42bef911), SkBits2Float(0x43abcc05), SkBits2Float(0x42d3af00));  // 343.594f, 95.4865f, 343.594f, 105.842f
+path.close();
+    SkPath path140(path);
+    builder.add(path140, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x438e8ffa), SkBits2Float(0x438bdd2b));  // 285.125f, 279.728f
+path.quadTo(SkBits2Float(0x438e8ffa), SkBits2Float(0x43910aa7), SkBits2Float(0x438ae6b8), SkBits2Float(0x4394b3e9));  // 285.125f, 290.083f, 277.802f, 297.406f
+path.quadTo(SkBits2Float(0x43873d76), SkBits2Float(0x43985d2b), SkBits2Float(0x43820ffa), SkBits2Float(0x43985d2b));  // 270.48f, 304.728f, 260.125f, 304.728f
+path.quadTo(SkBits2Float(0x4379c4fc), SkBits2Float(0x43985d2b), SkBits2Float(0x43727278), SkBits2Float(0x4394b3e9));  // 249.769f, 304.728f, 242.447f, 297.406f
+path.quadTo(SkBits2Float(0x436b1ff4), SkBits2Float(0x43910aa7), SkBits2Float(0x436b1ff4), SkBits2Float(0x438bdd2b));  // 235.125f, 290.083f, 235.125f, 279.728f
+path.quadTo(SkBits2Float(0x436b1ff4), SkBits2Float(0x4386afaf), SkBits2Float(0x43727278), SkBits2Float(0x4383066d));  // 235.125f, 269.373f, 242.447f, 262.05f
+path.quadTo(SkBits2Float(0x4379c4fc), SkBits2Float(0x437eba56), SkBits2Float(0x43820ffa), SkBits2Float(0x437eba56));  // 249.769f, 254.728f, 260.125f, 254.728f
+path.quadTo(SkBits2Float(0x43873d76), SkBits2Float(0x437eba56), SkBits2Float(0x438ae6b8), SkBits2Float(0x4383066d));  // 270.48f, 254.728f, 277.802f, 262.05f
+path.quadTo(SkBits2Float(0x438e8ffa), SkBits2Float(0x4386afaf), SkBits2Float(0x438e8ffa), SkBits2Float(0x438bdd2b));  // 285.125f, 269.373f, 285.125f, 279.728f
+path.close();
+    SkPath path141(path);
+    builder.add(path141, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x441d8000), SkBits2Float(0x435e8072));  // 630, 222.502f
+path.quadTo(SkBits2Float(0x441d8000), SkBits2Float(0x4368db6a), SkBits2Float(0x441bab5f), SkBits2Float(0x43702dee));  // 630, 232.857f, 622.678f, 240.179f
+path.quadTo(SkBits2Float(0x4419d6be), SkBits2Float(0x43778072), SkBits2Float(0x44174000), SkBits2Float(0x43778072));  // 615.355f, 247.502f, 605, 247.502f
+path.quadTo(SkBits2Float(0x4414a942), SkBits2Float(0x43778072), SkBits2Float(0x4412d4a1), SkBits2Float(0x43702dee));  // 594.645f, 247.502f, 587.322f, 240.179f
+path.quadTo(SkBits2Float(0x44110000), SkBits2Float(0x4368db6a), SkBits2Float(0x44110000), SkBits2Float(0x435e8072));  // 580, 232.857f, 580, 222.502f
+path.quadTo(SkBits2Float(0x44110000), SkBits2Float(0x4354257a), SkBits2Float(0x4412d4a1), SkBits2Float(0x434cd2f6));  // 580, 212.146f, 587.322f, 204.824f
+path.quadTo(SkBits2Float(0x4414a942), SkBits2Float(0x43458072), SkBits2Float(0x44174000), SkBits2Float(0x43458072));  // 594.645f, 197.502f, 605, 197.502f
+path.quadTo(SkBits2Float(0x4419d6be), SkBits2Float(0x43458072), SkBits2Float(0x441bab5f), SkBits2Float(0x434cd2f6));  // 615.355f, 197.502f, 622.678f, 204.824f
+path.quadTo(SkBits2Float(0x441d8000), SkBits2Float(0x4354257a), SkBits2Float(0x441d8000), SkBits2Float(0x435e8072));  // 630, 212.146f, 630, 222.502f
+path.close();
+    SkPath path142(path);
+    builder.add(path142, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43cab007), SkBits2Float(0x4370ffc2));  // 405.375f, 240.999f
+path.quadTo(SkBits2Float(0x43cab007), SkBits2Float(0x437b5aba), SkBits2Float(0x43c706c5), SkBits2Float(0x4381569f));  // 405.375f, 251.354f, 398.053f, 258.677f
+path.quadTo(SkBits2Float(0x43c35d83), SkBits2Float(0x4384ffe1), SkBits2Float(0x43be3007), SkBits2Float(0x4384ffe1));  // 390.731f, 265.999f, 380.375f, 265.999f
+path.quadTo(SkBits2Float(0x43b9028b), SkBits2Float(0x4384ffe1), SkBits2Float(0x43b55949), SkBits2Float(0x4381569f));  // 370.02f, 265.999f, 362.698f, 258.677f
+path.quadTo(SkBits2Float(0x43b1b007), SkBits2Float(0x437b5aba), SkBits2Float(0x43b1b007), SkBits2Float(0x4370ffc2));  // 355.375f, 251.354f, 355.375f, 240.999f
+path.quadTo(SkBits2Float(0x43b1b007), SkBits2Float(0x4366a4ca), SkBits2Float(0x43b55949), SkBits2Float(0x435f5246));  // 355.375f, 230.644f, 362.698f, 223.321f
+path.quadTo(SkBits2Float(0x43b9028b), SkBits2Float(0x4357ffc2), SkBits2Float(0x43be3007), SkBits2Float(0x4357ffc2));  // 370.02f, 215.999f, 380.375f, 215.999f
+path.quadTo(SkBits2Float(0x43c35d83), SkBits2Float(0x4357ffc2), SkBits2Float(0x43c706c5), SkBits2Float(0x435f5246));  // 390.731f, 215.999f, 398.053f, 223.321f
+path.quadTo(SkBits2Float(0x43cab007), SkBits2Float(0x4366a4ca), SkBits2Float(0x43cab007), SkBits2Float(0x4370ffc2));  // 405.375f, 230.644f, 405.375f, 240.999f
+path.close();
+    SkPath path143(path);
+    builder.add(path143, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43ae2f7d), SkBits2Float(0x43587ea0));  // 348.371f, 216.495f
+path.quadTo(SkBits2Float(0x43ae2f7d), SkBits2Float(0x4362d998), SkBits2Float(0x43aa863b), SkBits2Float(0x436a2c1c));  // 348.371f, 226.85f, 341.049f, 234.172f
+path.quadTo(SkBits2Float(0x43a6dcf9), SkBits2Float(0x43717ea0), SkBits2Float(0x43a1af7d), SkBits2Float(0x43717ea0));  // 333.726f, 241.495f, 323.371f, 241.495f
+path.quadTo(SkBits2Float(0x439c8201), SkBits2Float(0x43717ea0), SkBits2Float(0x4398d8bf), SkBits2Float(0x436a2c1c));  // 313.016f, 241.495f, 305.693f, 234.172f
+path.quadTo(SkBits2Float(0x43952f7d), SkBits2Float(0x4362d998), SkBits2Float(0x43952f7d), SkBits2Float(0x43587ea0));  // 298.371f, 226.85f, 298.371f, 216.495f
+path.quadTo(SkBits2Float(0x43952f7d), SkBits2Float(0x434e23a8), SkBits2Float(0x4398d8bf), SkBits2Float(0x4346d124));  // 298.371f, 206.139f, 305.693f, 198.817f
+path.quadTo(SkBits2Float(0x439c8201), SkBits2Float(0x433f7ea0), SkBits2Float(0x43a1af7d), SkBits2Float(0x433f7ea0));  // 313.016f, 191.495f, 323.371f, 191.495f
+path.quadTo(SkBits2Float(0x43a6dcf9), SkBits2Float(0x433f7ea0), SkBits2Float(0x43aa863b), SkBits2Float(0x4346d124));  // 333.726f, 191.495f, 341.049f, 198.817f
+path.quadTo(SkBits2Float(0x43ae2f7d), SkBits2Float(0x434e23a8), SkBits2Float(0x43ae2f7d), SkBits2Float(0x43587ea0));  // 348.371f, 206.139f, 348.371f, 216.495f
+path.close();
+    SkPath path144(path);
+    builder.add(path144, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43add1a9), SkBits2Float(0x436897ac));  // 347.638f, 232.592f
+path.quadTo(SkBits2Float(0x43add1a9), SkBits2Float(0x4372f2a3), SkBits2Float(0x43aa2867), SkBits2Float(0x437a4527));  // 347.638f, 242.948f, 340.316f, 250.27f
+path.quadTo(SkBits2Float(0x43a67f25), SkBits2Float(0x4380cbd6), SkBits2Float(0x43a151a9), SkBits2Float(0x4380cbd6));  // 332.993f, 257.592f, 322.638f, 257.592f
+path.quadTo(SkBits2Float(0x439c242d), SkBits2Float(0x4380cbd6), SkBits2Float(0x43987aeb), SkBits2Float(0x437a4527));  // 312.283f, 257.592f, 304.96f, 250.27f
+path.quadTo(SkBits2Float(0x4394d1a9), SkBits2Float(0x4372f2a3), SkBits2Float(0x4394d1a9), SkBits2Float(0x436897ac));  // 297.638f, 242.948f, 297.638f, 232.592f
+path.quadTo(SkBits2Float(0x4394d1a9), SkBits2Float(0x435e3cb5), SkBits2Float(0x43987aeb), SkBits2Float(0x4356ea31));  // 297.638f, 222.237f, 304.96f, 214.915f
+path.quadTo(SkBits2Float(0x439c242d), SkBits2Float(0x434f97ad), SkBits2Float(0x43a151a9), SkBits2Float(0x434f97ad));  // 312.283f, 207.592f, 322.638f, 207.592f
+path.quadTo(SkBits2Float(0x43a67f25), SkBits2Float(0x434f97ad), SkBits2Float(0x43aa2867), SkBits2Float(0x4356ea31));  // 332.993f, 207.592f, 340.316f, 214.915f
+path.quadTo(SkBits2Float(0x43add1a9), SkBits2Float(0x435e3cb5), SkBits2Float(0x43add1a9), SkBits2Float(0x436897ac));  // 347.638f, 222.237f, 347.638f, 232.592f
+path.close();
+    SkPath path145(path);
+    builder.add(path145, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43f37c5c), SkBits2Float(0x438cffd4));  // 486.972f, 281.999f
+path.quadTo(SkBits2Float(0x43f37c5c), SkBits2Float(0x43922d50), SkBits2Float(0x43efd31a), SkBits2Float(0x4395d692));  // 486.972f, 292.354f, 479.649f, 299.676f
+path.quadTo(SkBits2Float(0x43ec29d8), SkBits2Float(0x43997fd4), SkBits2Float(0x43e6fc5c), SkBits2Float(0x43997fd4));  // 472.327f, 306.999f, 461.972f, 306.999f
+path.quadTo(SkBits2Float(0x43e1cee0), SkBits2Float(0x43997fd4), SkBits2Float(0x43de259e), SkBits2Float(0x4395d692));  // 451.616f, 306.999f, 444.294f, 299.676f
+path.quadTo(SkBits2Float(0x43da7c5c), SkBits2Float(0x43922d50), SkBits2Float(0x43da7c5c), SkBits2Float(0x438cffd4));  // 436.972f, 292.354f, 436.972f, 281.999f
+path.quadTo(SkBits2Float(0x43da7c5c), SkBits2Float(0x4387d258), SkBits2Float(0x43de259e), SkBits2Float(0x43842916));  // 436.972f, 271.643f, 444.294f, 264.321f
+path.quadTo(SkBits2Float(0x43e1cee0), SkBits2Float(0x43807fd4), SkBits2Float(0x43e6fc5c), SkBits2Float(0x43807fd4));  // 451.616f, 256.999f, 461.972f, 256.999f
+path.quadTo(SkBits2Float(0x43ec29d8), SkBits2Float(0x43807fd4), SkBits2Float(0x43efd31a), SkBits2Float(0x43842916));  // 472.327f, 256.999f, 479.649f, 264.321f
+path.quadTo(SkBits2Float(0x43f37c5c), SkBits2Float(0x4387d258), SkBits2Float(0x43f37c5c), SkBits2Float(0x438cffd4));  // 486.972f, 271.643f, 486.972f, 281.999f
+path.close();
+    SkPath path146(path);
+    builder.add(path146, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x434938a6), SkBits2Float(0x439b6682));  // 201.221f, 310.801f
+path.quadTo(SkBits2Float(0x434938a6), SkBits2Float(0x43a093fe), SkBits2Float(0x4341e622), SkBits2Float(0x43a43d40));  // 201.221f, 321.156f, 193.899f, 328.479f
+path.quadTo(SkBits2Float(0x433a939e), SkBits2Float(0x43a7e682), SkBits2Float(0x433038a6), SkBits2Float(0x43a7e682));  // 186.577f, 335.801f, 176.221f, 335.801f
+path.quadTo(SkBits2Float(0x4325ddae), SkBits2Float(0x43a7e682), SkBits2Float(0x431e8b2a), SkBits2Float(0x43a43d40));  // 165.866f, 335.801f, 158.544f, 328.479f
+path.quadTo(SkBits2Float(0x431738a6), SkBits2Float(0x43a093fe), SkBits2Float(0x431738a6), SkBits2Float(0x439b6682));  // 151.221f, 321.156f, 151.221f, 310.801f
+path.quadTo(SkBits2Float(0x431738a6), SkBits2Float(0x43963906), SkBits2Float(0x431e8b2a), SkBits2Float(0x43928fc4));  // 151.221f, 300.445f, 158.544f, 293.123f
+path.quadTo(SkBits2Float(0x4325ddae), SkBits2Float(0x438ee682), SkBits2Float(0x433038a6), SkBits2Float(0x438ee682));  // 165.866f, 285.801f, 176.221f, 285.801f
+path.quadTo(SkBits2Float(0x433a939e), SkBits2Float(0x438ee682), SkBits2Float(0x4341e622), SkBits2Float(0x43928fc4));  // 186.577f, 285.801f, 193.899f, 293.123f
+path.quadTo(SkBits2Float(0x434938a6), SkBits2Float(0x43963906), SkBits2Float(0x434938a6), SkBits2Float(0x439b6682));  // 201.221f, 300.445f, 201.221f, 310.801f
+path.close();
+    SkPath path147(path);
+    builder.add(path147, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43957ad9), SkBits2Float(0x4340d6a9));  // 298.96f, 192.839f
+path.quadTo(SkBits2Float(0x43957ad9), SkBits2Float(0x434b31a0), SkBits2Float(0x4391d197), SkBits2Float(0x43528425));  // 298.96f, 203.194f, 291.637f, 210.516f
+path.quadTo(SkBits2Float(0x438e2855), SkBits2Float(0x4359d6a9), SkBits2Float(0x4388fad9), SkBits2Float(0x4359d6a9));  // 284.315f, 217.839f, 273.96f, 217.839f
+path.quadTo(SkBits2Float(0x4383cd5d), SkBits2Float(0x4359d6a9), SkBits2Float(0x4380241b), SkBits2Float(0x43528425));  // 263.604f, 217.839f, 256.282f, 210.516f
+path.quadTo(SkBits2Float(0x4378f5b2), SkBits2Float(0x434b31a0), SkBits2Float(0x4378f5b2), SkBits2Float(0x4340d6a9));  // 248.96f, 203.194f, 248.96f, 192.839f
+path.quadTo(SkBits2Float(0x4378f5b2), SkBits2Float(0x43367bb2), SkBits2Float(0x4380241b), SkBits2Float(0x432f292d));  // 248.96f, 182.483f, 256.282f, 175.161f
+path.quadTo(SkBits2Float(0x4383cd5d), SkBits2Float(0x4327d6a9), SkBits2Float(0x4388fad9), SkBits2Float(0x4327d6a9));  // 263.604f, 167.839f, 273.96f, 167.839f
+path.quadTo(SkBits2Float(0x438e2855), SkBits2Float(0x4327d6a9), SkBits2Float(0x4391d197), SkBits2Float(0x432f292d));  // 284.315f, 167.839f, 291.637f, 175.161f
+path.quadTo(SkBits2Float(0x43957ad9), SkBits2Float(0x43367bb2), SkBits2Float(0x43957ad9), SkBits2Float(0x4340d6a9));  // 298.96f, 182.483f, 298.96f, 192.839f
+path.close();
+    SkPath path148(path);
+    builder.add(path148, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4359cdc3), SkBits2Float(0x431623e6));  // 217.804f, 150.14f
+path.quadTo(SkBits2Float(0x4359cdc3), SkBits2Float(0x43207ede), SkBits2Float(0x43527b3f), SkBits2Float(0x4327d162));  // 217.804f, 160.496f, 210.481f, 167.818f
+path.quadTo(SkBits2Float(0x434b28ba), SkBits2Float(0x432f23e6), SkBits2Float(0x4340cdc3), SkBits2Float(0x432f23e6));  // 203.159f, 175.14f, 192.804f, 175.14f
+path.quadTo(SkBits2Float(0x433672cc), SkBits2Float(0x432f23e6), SkBits2Float(0x432f2047), SkBits2Float(0x4327d162));  // 182.448f, 175.14f, 175.126f, 167.818f
+path.quadTo(SkBits2Float(0x4327cdc3), SkBits2Float(0x43207ede), SkBits2Float(0x4327cdc3), SkBits2Float(0x431623e6));  // 167.804f, 160.496f, 167.804f, 150.14f
+path.quadTo(SkBits2Float(0x4327cdc3), SkBits2Float(0x430bc8ee), SkBits2Float(0x432f2047), SkBits2Float(0x4304766a));  // 167.804f, 139.785f, 175.126f, 132.463f
+path.quadTo(SkBits2Float(0x433672cc), SkBits2Float(0x42fa47cc), SkBits2Float(0x4340cdc3), SkBits2Float(0x42fa47cc));  // 182.448f, 125.14f, 192.804f, 125.14f
+path.quadTo(SkBits2Float(0x434b28ba), SkBits2Float(0x42fa47cc), SkBits2Float(0x43527b3f), SkBits2Float(0x4304766a));  // 203.159f, 125.14f, 210.481f, 132.463f
+path.quadTo(SkBits2Float(0x4359cdc3), SkBits2Float(0x430bc8ee), SkBits2Float(0x4359cdc3), SkBits2Float(0x431623e6));  // 217.804f, 139.785f, 217.804f, 150.14f
+path.close();
+    SkPath path149(path);
+    builder.add(path149, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4395d72b), SkBits2Float(0x43c5524a));  // 299.681f, 394.643f
+path.quadTo(SkBits2Float(0x4395d72b), SkBits2Float(0x43ca7fc6), SkBits2Float(0x43922de9), SkBits2Float(0x43ce2908));  // 299.681f, 404.998f, 292.359f, 412.321f
+path.quadTo(SkBits2Float(0x438e84a7), SkBits2Float(0x43d1d24a), SkBits2Float(0x4389572b), SkBits2Float(0x43d1d24a));  // 285.036f, 419.643f, 274.681f, 419.643f
+path.quadTo(SkBits2Float(0x438429af), SkBits2Float(0x43d1d24a), SkBits2Float(0x4380806d), SkBits2Float(0x43ce2908));  // 264.326f, 419.643f, 257.003f, 412.321f
+path.quadTo(SkBits2Float(0x4379ae56), SkBits2Float(0x43ca7fc6), SkBits2Float(0x4379ae56), SkBits2Float(0x43c5524a));  // 249.681f, 404.998f, 249.681f, 394.643f
+path.quadTo(SkBits2Float(0x4379ae56), SkBits2Float(0x43c024ce), SkBits2Float(0x4380806d), SkBits2Float(0x43bc7b8c));  // 249.681f, 384.288f, 257.003f, 376.965f
+path.quadTo(SkBits2Float(0x438429af), SkBits2Float(0x43b8d24a), SkBits2Float(0x4389572b), SkBits2Float(0x43b8d24a));  // 264.326f, 369.643f, 274.681f, 369.643f
+path.quadTo(SkBits2Float(0x438e84a7), SkBits2Float(0x43b8d24a), SkBits2Float(0x43922de9), SkBits2Float(0x43bc7b8c));  // 285.036f, 369.643f, 292.359f, 376.965f
+path.quadTo(SkBits2Float(0x4395d72b), SkBits2Float(0x43c024ce), SkBits2Float(0x4395d72b), SkBits2Float(0x43c5524a));  // 299.681f, 384.288f, 299.681f, 394.643f
+path.close();
+    SkPath path150(path);
+    builder.add(path150, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43884f5e), SkBits2Float(0x436c6388));  // 272.62f, 236.389f
+path.quadTo(SkBits2Float(0x43884f5e), SkBits2Float(0x4376be7f), SkBits2Float(0x4384a61c), SkBits2Float(0x437e1103));  // 272.62f, 246.744f, 265.298f, 254.066f
+path.quadTo(SkBits2Float(0x4380fcda), SkBits2Float(0x4382b1c4), SkBits2Float(0x43779ebc), SkBits2Float(0x4382b1c4));  // 257.975f, 261.389f, 247.62f, 261.389f
+path.quadTo(SkBits2Float(0x436d43c5), SkBits2Float(0x4382b1c4), SkBits2Float(0x4365f141), SkBits2Float(0x437e1103));  // 237.265f, 261.389f, 229.942f, 254.066f
+path.quadTo(SkBits2Float(0x435e9ebd), SkBits2Float(0x4376be7f), SkBits2Float(0x435e9ebd), SkBits2Float(0x436c6388));  // 222.62f, 246.744f, 222.62f, 236.389f
+path.quadTo(SkBits2Float(0x435e9ebd), SkBits2Float(0x43620891), SkBits2Float(0x4365f141), SkBits2Float(0x435ab60d));  // 222.62f, 226.033f, 229.942f, 218.711f
+path.quadTo(SkBits2Float(0x436d43c5), SkBits2Float(0x43536389), SkBits2Float(0x43779ebc), SkBits2Float(0x43536389));  // 237.265f, 211.389f, 247.62f, 211.389f
+path.quadTo(SkBits2Float(0x4380fcda), SkBits2Float(0x43536389), SkBits2Float(0x4384a61c), SkBits2Float(0x435ab60d));  // 257.975f, 211.389f, 265.298f, 218.711f
+path.quadTo(SkBits2Float(0x43884f5e), SkBits2Float(0x43620891), SkBits2Float(0x43884f5e), SkBits2Float(0x436c6388));  // 272.62f, 226.033f, 272.62f, 236.389f
+path.close();
+    SkPath path151(path);
+    builder.add(path151, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43ac3331), SkBits2Float(0x4330c484));  // 344.4f, 176.768f
+path.quadTo(SkBits2Float(0x43ac3331), SkBits2Float(0x433b1f7c), SkBits2Float(0x43a889ef), SkBits2Float(0x43427200));  // 344.4f, 187.123f, 337.078f, 194.445f
+path.quadTo(SkBits2Float(0x43a4e0ad), SkBits2Float(0x4349c484), SkBits2Float(0x439fb331), SkBits2Float(0x4349c484));  // 329.755f, 201.768f, 319.4f, 201.768f
+path.quadTo(SkBits2Float(0x439a85b5), SkBits2Float(0x4349c484), SkBits2Float(0x4396dc73), SkBits2Float(0x43427200));  // 309.045f, 201.768f, 301.722f, 194.445f
+path.quadTo(SkBits2Float(0x43933331), SkBits2Float(0x433b1f7c), SkBits2Float(0x43933331), SkBits2Float(0x4330c484));  // 294.4f, 187.123f, 294.4f, 176.768f
+path.quadTo(SkBits2Float(0x43933331), SkBits2Float(0x4326698c), SkBits2Float(0x4396dc73), SkBits2Float(0x431f1708));  // 294.4f, 166.412f, 301.722f, 159.09f
+path.quadTo(SkBits2Float(0x439a85b5), SkBits2Float(0x4317c484), SkBits2Float(0x439fb331), SkBits2Float(0x4317c484));  // 309.045f, 151.768f, 319.4f, 151.768f
+path.quadTo(SkBits2Float(0x43a4e0ad), SkBits2Float(0x4317c484), SkBits2Float(0x43a889ef), SkBits2Float(0x431f1708));  // 329.755f, 151.768f, 337.078f, 159.09f
+path.quadTo(SkBits2Float(0x43ac3331), SkBits2Float(0x4326698c), SkBits2Float(0x43ac3331), SkBits2Float(0x4330c484));  // 344.4f, 166.412f, 344.4f, 176.768f
+path.close();
+    SkPath path152(path);
+    builder.add(path152, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x435659f0), SkBits2Float(0x438a1820));  // 214.351f, 276.188f
+path.quadTo(SkBits2Float(0x435659f0), SkBits2Float(0x438f459c), SkBits2Float(0x434f076c), SkBits2Float(0x4392eede));  // 214.351f, 286.544f, 207.029f, 293.866f
+path.quadTo(SkBits2Float(0x4347b4e8), SkBits2Float(0x43969820), SkBits2Float(0x433d59f0), SkBits2Float(0x43969820));  // 199.707f, 301.188f, 189.351f, 301.188f
+path.quadTo(SkBits2Float(0x4332fef8), SkBits2Float(0x43969820), SkBits2Float(0x432bac74), SkBits2Float(0x4392eede));  // 178.996f, 301.188f, 171.674f, 293.866f
+path.quadTo(SkBits2Float(0x432459f0), SkBits2Float(0x438f459c), SkBits2Float(0x432459f0), SkBits2Float(0x438a1820));  // 164.351f, 286.544f, 164.351f, 276.188f
+path.quadTo(SkBits2Float(0x432459f0), SkBits2Float(0x4384eaa4), SkBits2Float(0x432bac74), SkBits2Float(0x43814162));  // 164.351f, 265.833f, 171.674f, 258.511f
+path.quadTo(SkBits2Float(0x4332fef8), SkBits2Float(0x437b3040), SkBits2Float(0x433d59f0), SkBits2Float(0x437b3040));  // 178.996f, 251.188f, 189.351f, 251.188f
+path.quadTo(SkBits2Float(0x4347b4e8), SkBits2Float(0x437b3040), SkBits2Float(0x434f076c), SkBits2Float(0x43814162));  // 199.707f, 251.188f, 207.029f, 258.511f
+path.quadTo(SkBits2Float(0x435659f0), SkBits2Float(0x4384eaa4), SkBits2Float(0x435659f0), SkBits2Float(0x438a1820));  // 214.351f, 265.833f, 214.351f, 276.188f
+path.close();
+    SkPath path153(path);
+    builder.add(path153, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x437aa20e), SkBits2Float(0x434e714e));  // 250.633f, 206.443f
+path.quadTo(SkBits2Float(0x437aa20e), SkBits2Float(0x4358cc46), SkBits2Float(0x43734f8a), SkBits2Float(0x43601eca));  // 250.633f, 216.798f, 243.311f, 224.12f
+path.quadTo(SkBits2Float(0x436bfd06), SkBits2Float(0x4367714e), SkBits2Float(0x4361a20e), SkBits2Float(0x4367714e));  // 235.988f, 231.443f, 225.633f, 231.443f
+path.quadTo(SkBits2Float(0x43574716), SkBits2Float(0x4367714e), SkBits2Float(0x434ff492), SkBits2Float(0x43601eca));  // 215.278f, 231.443f, 207.955f, 224.12f
+path.quadTo(SkBits2Float(0x4348a20e), SkBits2Float(0x4358cc46), SkBits2Float(0x4348a20e), SkBits2Float(0x434e714e));  // 200.633f, 216.798f, 200.633f, 206.443f
+path.quadTo(SkBits2Float(0x4348a20e), SkBits2Float(0x43441656), SkBits2Float(0x434ff492), SkBits2Float(0x433cc3d2));  // 200.633f, 196.087f, 207.955f, 188.765f
+path.quadTo(SkBits2Float(0x43574716), SkBits2Float(0x4335714e), SkBits2Float(0x4361a20e), SkBits2Float(0x4335714e));  // 215.278f, 181.443f, 225.633f, 181.443f
+path.quadTo(SkBits2Float(0x436bfd06), SkBits2Float(0x4335714e), SkBits2Float(0x43734f8a), SkBits2Float(0x433cc3d2));  // 235.988f, 181.443f, 243.311f, 188.765f
+path.quadTo(SkBits2Float(0x437aa20e), SkBits2Float(0x43441656), SkBits2Float(0x437aa20e), SkBits2Float(0x434e714e));  // 250.633f, 196.087f, 250.633f, 206.443f
+path.close();
+    SkPath path154(path);
+    builder.add(path154, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43ed22e3), SkBits2Float(0x439da644));  // 474.273f, 315.299f
+path.quadTo(SkBits2Float(0x43ed22e3), SkBits2Float(0x43a2d3c0), SkBits2Float(0x43e979a1), SkBits2Float(0x43a67d02));  // 474.273f, 325.654f, 466.95f, 332.977f
+path.quadTo(SkBits2Float(0x43e5d05f), SkBits2Float(0x43aa2644), SkBits2Float(0x43e0a2e3), SkBits2Float(0x43aa2644));  // 459.628f, 340.299f, 449.273f, 340.299f
+path.quadTo(SkBits2Float(0x43db7567), SkBits2Float(0x43aa2644), SkBits2Float(0x43d7cc25), SkBits2Float(0x43a67d02));  // 438.917f, 340.299f, 431.595f, 332.977f
+path.quadTo(SkBits2Float(0x43d422e3), SkBits2Float(0x43a2d3c0), SkBits2Float(0x43d422e3), SkBits2Float(0x439da644));  // 424.273f, 325.654f, 424.273f, 315.299f
+path.quadTo(SkBits2Float(0x43d422e3), SkBits2Float(0x439878c8), SkBits2Float(0x43d7cc25), SkBits2Float(0x4394cf86));  // 424.273f, 304.944f, 431.595f, 297.621f
+path.quadTo(SkBits2Float(0x43db7567), SkBits2Float(0x43912644), SkBits2Float(0x43e0a2e3), SkBits2Float(0x43912644));  // 438.917f, 290.299f, 449.273f, 290.299f
+path.quadTo(SkBits2Float(0x43e5d05f), SkBits2Float(0x43912644), SkBits2Float(0x43e979a1), SkBits2Float(0x4394cf86));  // 459.628f, 290.299f, 466.95f, 297.621f
+path.quadTo(SkBits2Float(0x43ed22e3), SkBits2Float(0x439878c8), SkBits2Float(0x43ed22e3), SkBits2Float(0x439da644));  // 474.273f, 304.944f, 474.273f, 315.299f
+path.close();
+    SkPath path155(path);
+    builder.add(path155, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43cc4428), SkBits2Float(0x43797560));  // 408.532f, 249.458f
+path.quadTo(SkBits2Float(0x43cc4428), SkBits2Float(0x4381e82c), SkBits2Float(0x43c89ae6), SkBits2Float(0x4385916e));  // 408.532f, 259.814f, 401.21f, 267.136f
+path.quadTo(SkBits2Float(0x43c4f1a4), SkBits2Float(0x43893ab0), SkBits2Float(0x43bfc428), SkBits2Float(0x43893ab0));  // 393.888f, 274.458f, 383.532f, 274.458f
+path.quadTo(SkBits2Float(0x43ba96ac), SkBits2Float(0x43893ab0), SkBits2Float(0x43b6ed6a), SkBits2Float(0x4385916e));  // 373.177f, 274.458f, 365.855f, 267.136f
+path.quadTo(SkBits2Float(0x43b34428), SkBits2Float(0x4381e82c), SkBits2Float(0x43b34428), SkBits2Float(0x43797560));  // 358.532f, 259.814f, 358.532f, 249.458f
+path.quadTo(SkBits2Float(0x43b34428), SkBits2Float(0x436f1a69), SkBits2Float(0x43b6ed6a), SkBits2Float(0x4367c7e5));  // 358.532f, 239.103f, 365.855f, 231.781f
+path.quadTo(SkBits2Float(0x43ba96ac), SkBits2Float(0x43607561), SkBits2Float(0x43bfc428), SkBits2Float(0x43607561));  // 373.177f, 224.459f, 383.532f, 224.459f
+path.quadTo(SkBits2Float(0x43c4f1a4), SkBits2Float(0x43607561), SkBits2Float(0x43c89ae6), SkBits2Float(0x4367c7e5));  // 393.888f, 224.459f, 401.21f, 231.781f
+path.quadTo(SkBits2Float(0x43cc4428), SkBits2Float(0x436f1a69), SkBits2Float(0x43cc4428), SkBits2Float(0x43797560));  // 408.532f, 239.103f, 408.532f, 249.458f
+path.close();
+    SkPath path156(path);
+    builder.add(path156, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42e47b81), SkBits2Float(0x43b97683));  // 114.241f, 370.926f
+path.quadTo(SkBits2Float(0x42e47b81), SkBits2Float(0x43bea3ff), SkBits2Float(0x42d5d678), SkBits2Float(0x43c24d41));  // 114.241f, 381.281f, 106.919f, 388.604f
+path.quadTo(SkBits2Float(0x42c73170), SkBits2Float(0x43c5f683), SkBits2Float(0x42b27b81), SkBits2Float(0x43c5f683));  // 99.5966f, 395.926f, 89.2412f, 395.926f
+path.quadTo(SkBits2Float(0x429dc592), SkBits2Float(0x43c5f683), SkBits2Float(0x428f208a), SkBits2Float(0x43c24d41));  // 78.8859f, 395.926f, 71.5636f, 388.604f
+path.quadTo(SkBits2Float(0x42807b81), SkBits2Float(0x43bea3ff), SkBits2Float(0x42807b81), SkBits2Float(0x43b97683));  // 64.2412f, 381.281f, 64.2412f, 370.926f
+path.quadTo(SkBits2Float(0x42807b81), SkBits2Float(0x43b44907), SkBits2Float(0x428f208a), SkBits2Float(0x43b09fc5));  // 64.2412f, 360.571f, 71.5636f, 353.248f
+path.quadTo(SkBits2Float(0x429dc592), SkBits2Float(0x43acf683), SkBits2Float(0x42b27b81), SkBits2Float(0x43acf683));  // 78.8859f, 345.926f, 89.2412f, 345.926f
+path.quadTo(SkBits2Float(0x42c73170), SkBits2Float(0x43acf683), SkBits2Float(0x42d5d678), SkBits2Float(0x43b09fc5));  // 99.5966f, 345.926f, 106.919f, 353.248f
+path.quadTo(SkBits2Float(0x42e47b81), SkBits2Float(0x43b44907), SkBits2Float(0x42e47b81), SkBits2Float(0x43b97683));  // 114.241f, 360.571f, 114.241f, 370.926f
+path.close();
+    SkPath path157(path);
+    builder.add(path157, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4384239e), SkBits2Float(0x42a36f30));  // 264.278f, 81.7172f
+path.quadTo(SkBits2Float(0x4384239e), SkBits2Float(0x42b8251f), SkBits2Float(0x43807a5c), SkBits2Float(0x42c6ca28));  // 264.278f, 92.0725f, 256.956f, 99.3948f
+path.quadTo(SkBits2Float(0x4379a234), SkBits2Float(0x42d56f30), SkBits2Float(0x436f473c), SkBits2Float(0x42d56f30));  // 249.634f, 106.717f, 239.278f, 106.717f
+path.quadTo(SkBits2Float(0x4364ec44), SkBits2Float(0x42d56f30), SkBits2Float(0x435d99c0), SkBits2Float(0x42c6ca28));  // 228.923f, 106.717f, 221.601f, 99.3948f
+path.quadTo(SkBits2Float(0x4356473c), SkBits2Float(0x42b8251f), SkBits2Float(0x4356473c), SkBits2Float(0x42a36f30));  // 214.278f, 92.0725f, 214.278f, 81.7172f
+path.quadTo(SkBits2Float(0x4356473c), SkBits2Float(0x428eb941), SkBits2Float(0x435d99c0), SkBits2Float(0x42801438));  // 214.278f, 71.3618f, 221.601f, 64.0395f
+path.quadTo(SkBits2Float(0x4364ec44), SkBits2Float(0x4262de60), SkBits2Float(0x436f473c), SkBits2Float(0x4262de60));  // 228.923f, 56.7172f, 239.278f, 56.7172f
+path.quadTo(SkBits2Float(0x4379a234), SkBits2Float(0x4262de60), SkBits2Float(0x43807a5c), SkBits2Float(0x42801438));  // 249.634f, 56.7172f, 256.956f, 64.0395f
+path.quadTo(SkBits2Float(0x4384239e), SkBits2Float(0x428eb941), SkBits2Float(0x4384239e), SkBits2Float(0x42a36f30));  // 264.278f, 71.3618f, 264.278f, 81.7172f
+path.close();
+    SkPath path158(path);
+    builder.add(path158, (SkPathOp) 2);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x435be541), SkBits2Float(0x43baaaf6));  // 219.896f, 373.336f
+path.quadTo(SkBits2Float(0x435be541), SkBits2Float(0x43bfd872), SkBits2Float(0x435492bd), SkBits2Float(0x43c381b4));  // 219.896f, 383.691f, 212.573f, 391.013f
+path.quadTo(SkBits2Float(0x434d4038), SkBits2Float(0x43c72af6), SkBits2Float(0x4342e541), SkBits2Float(0x43c72af6));  // 205.251f, 398.336f, 194.896f, 398.336f
+path.quadTo(SkBits2Float(0x43388a4a), SkBits2Float(0x43c72af6), SkBits2Float(0x433137c5), SkBits2Float(0x43c381b4));  // 184.54f, 398.336f, 177.218f, 391.013f
+path.quadTo(SkBits2Float(0x4329e541), SkBits2Float(0x43bfd872), SkBits2Float(0x4329e541), SkBits2Float(0x43baaaf6));  // 169.896f, 383.691f, 169.896f, 373.336f
+path.quadTo(SkBits2Float(0x4329e541), SkBits2Float(0x43b57d7a), SkBits2Float(0x433137c5), SkBits2Float(0x43b1d438));  // 169.896f, 362.98f, 177.218f, 355.658f
+path.quadTo(SkBits2Float(0x43388a4a), SkBits2Float(0x43ae2af6), SkBits2Float(0x4342e541), SkBits2Float(0x43ae2af6));  // 184.54f, 348.336f, 194.896f, 348.336f
+path.quadTo(SkBits2Float(0x434d4038), SkBits2Float(0x43ae2af6), SkBits2Float(0x435492bd), SkBits2Float(0x43b1d438));  // 205.251f, 348.336f, 212.573f, 355.658f
+path.quadTo(SkBits2Float(0x435be541), SkBits2Float(0x43b57d7a), SkBits2Float(0x435be541), SkBits2Float(0x43baaaf6));  // 219.896f, 362.98f, 219.896f, 373.336f
+path.close();
+    SkPath path159(path);
+    builder.add(path159, (SkPathOp) 2);
+
+    builder.resolve(&path);
+}
+
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
+
+static struct TestDesc tests[] = {
+    TEST(build1_1),
+};
+
+
+static const size_t testCount = SK_ARRAY_COUNT(tests);
+
+static bool runReverse = false;
+
+DEF_TEST(PathOpsBuildUse, reporter) {
+#if DEBUG_SHOW_TEST_NAME
+    strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
+#endif
+    RunTestSet(reporter, tests, testCount, firstTest, nullptr, stopTest, runReverse);
+}
diff --git a/src/third_party/skia/tests/PathOpsBuilderConicTest.cpp b/src/third_party/skia/tests/PathOpsBuilderConicTest.cpp
new file mode 100644
index 0000000..e6a6447
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsBuilderConicTest.cpp
@@ -0,0 +1,641 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "PathOpsExtendedTest.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SubsetPath.h"
+
+#define DEBUG_SIMPLIFY_FAILS 0
+
+struct OvalSet {
+    SkRect fBounds;
+    int fColumns;
+    int fRows;
+    int fRotations;
+    SkScalar fXSpacing;
+    SkScalar fYSpacing;
+};
+
+static void testOvalSet(const OvalSet& set, const SkPath& oval, SkOpBuilder* builder,
+        SkRegion* region, SkPath* result) {
+    for (int x = 0; x < set.fColumns; ++x) {
+        for (int y = 0; y < set.fRows; ++y) {
+            for (SkScalar r = 0; r < 360; r += 360.f / set.fRotations) {
+                SkPath rotated;
+                SkMatrix matrix;
+                matrix.reset();
+                matrix.postRotate(r, 0, 0);
+                matrix.postTranslate(x * set.fXSpacing, y * set.fYSpacing);
+                oval.transform(matrix, &rotated);
+                if (builder) {
+                    builder->add(rotated, kUnion_SkPathOp);
+                } else if (!region) {
+                    Op(*result, rotated, kUnion_SkPathOp, result);
+                } else {
+                    SkRegion rgnB, openClip;
+                    openClip.setRect(-16000, -16000, 16000, 16000);
+                    rgnB.setPath(rotated, openClip);
+                    region->op(rgnB, SkRegion::kUnion_Op);
+                }
+            }
+        }
+    }
+    if (builder) {
+        builder->resolve(result);
+    } else if (region) {
+        region->getBoundaryPath(result);
+    }
+}
+
+static void testOne(skiatest::Reporter* reporter, const OvalSet& set) {
+    SkPath oval, regionResult, builderResult, opResult;
+    oval.setFillType(SkPath::kWinding_FillType);
+    oval.addOval(set.fBounds);
+    SkOpBuilder builder;
+    SkRegion region;
+    testOvalSet(set, oval, nullptr, &region, &regionResult);
+    testOvalSet(set, oval, &builder, nullptr, &builderResult);
+    testOvalSet(set, oval, nullptr, nullptr, &opResult);
+    SkBitmap bitmap;
+    comparePaths(reporter, __FUNCTION__  , regionResult, builderResult, bitmap);
+    comparePaths(reporter, __FUNCTION__  , regionResult, opResult, bitmap);
+}
+
+struct OvalSetOneOff {
+    int fCol;
+    int fRow;
+    int fRot;
+    int fTrial;
+} oneOffs[] = {
+    { 2, 2, 9, 73 },
+    { 1, 2, 7, 93 }
+};
+
+static void setupOne(skiatest::Reporter* reporter, int col, int row, int rot, int trial) {
+    const int scale = 10;
+    SkRandom r;
+    r.setSeed(col * 100000000 + row * 10000000 + rot * 1000000 + trial);
+    SkScalar xOffset = r.nextRangeScalar(1, 40) * scale;
+    SkScalar yOffset = r.nextRangeScalar(1, 100) * scale;
+    OvalSet set = {{0, 0, 0, 0}, col, row, rot, xOffset, yOffset};
+    set.fBounds.setXYWH(5, 5,
+        r.nextRangeScalar(5, 50) * scale, r.nextRangeScalar(50, 90) * scale);
+    testOne(reporter, set);
+}
+
+#include "SkCommandLineFlags.h"
+
+DEFINE_int32(processOffset, 0, "Offset the test by this value. This permits multiple processes "
+                          "to exercise the same test in parallel with different test values.");
+DEFINE_int32(processCount, 1, "Test iteration count. This permits multiple "
+                          "processes "
+                          "to exercise the same test in parallel with different test values.");
+DEFINE_int32(trialRuns, 100, "Run this many tests (defaults to 100).");
+
+DEF_TEST(SixtyOvals, reporter) {
+    bool skipOneOffs = false;
+    int trialRuns = FLAGS_trialRuns / FLAGS_processCount;
+    for (int col = 1; col <= 2; ++col) {
+        for (int row = 1; row <= 3; ++row) {
+            for (int rot = 2; rot <= 9; ++rot) {
+                for (int trial = FLAGS_processOffset * trialRuns; --trialRuns >= 0; ++trial) {
+                    if (skipOneOffs) {
+                        for (const OvalSetOneOff& oneOff : oneOffs) {
+                            if (col == oneOff.fCol && row == oneOff.fRow && rot == oneOff.fRot
+                                    && trial == oneOff.fTrial) {
+                                goto skipTest;
+                            }
+                        }
+                    }
+                    setupOne(reporter, col, row, rot, trial);
+            skipTest:
+                    ;
+                }
+            }
+        }
+    }
+}
+
+DEF_TEST(SixtyOvalsOneOff, reporter) {
+    for (const OvalSetOneOff& oneOff : oneOffs) {
+        setupOne(reporter, oneOff.fCol, oneOff.fRow, oneOff.fRot, oneOff.fTrial);
+    }
+}
+
+#if DEBUG_SIMPLIFY_FAILS
+static bool simplify_fails(skiatest::Reporter* reporter, const SkPath& path) {
+    SkPath dummy;
+    bool failed = !Simplify(path, &dummy);
+    if (!failed) {
+        SkBitmap bitmap;
+        failed = !!comparePaths(reporter, __FUNCTION__, path, dummy, bitmap);
+    }
+    return failed;
+}
+
+static SkPath subset_simplify_fail(skiatest::Reporter* reporter, const SkPath& path) {
+    SubsetContours subsetContours(path);
+    bool failed = simplify_fails(reporter, path);
+    SkASSERT(failed);
+    SkPath lastFailed = path;
+    SkPath minimal;
+    while (subsetContours.subset(failed, &minimal)) {
+        failed = simplify_fails(reporter, minimal);
+        SkDebugf(" %s\n", failed ? "failed" : "");
+        if (failed) {
+            lastFailed = minimal;
+        }
+    }
+    failed = simplify_fails(reporter, lastFailed);
+    SkASSERT(failed);
+    SubsetVerbs subsetVerbs(lastFailed);
+    while (subsetVerbs.subset(failed, &minimal)) {
+        failed = simplify_fails(reporter, minimal);
+        SkDebugf(" %s\n", failed ? "failed" : "");
+        if (failed) {
+            lastFailed = minimal;
+        }
+    }
+    return lastFailed;
+}
+#endif
+
+DEF_TEST(SixtyOvals_2_2_9_73, reporter) {
+    SkPath path;
+    path.moveTo(SkBits2Float(0x434d53ca), SkBits2Float(0x43ad6ab0));  // 205.327f, 346.833f
+path.conicTo(SkBits2Float(0x434d53ca), SkBits2Float(0x40a00000), SkBits2Float(0x42d253ca), SkBits2Float(0x40a00000), SkBits2Float(0x3f3504f3));  // 205.327f, 5, 105.164f, 5, 0.707107f
+path.conicTo(SkBits2Float(0x40a00000), SkBits2Float(0x40a00000), SkBits2Float(0x40a00000), SkBits2Float(0x43ad6ab0), SkBits2Float(0x3f3504f3));  // 5, 5, 5, 346.833f, 0.707107f
+path.conicTo(SkBits2Float(0x40a00000), SkBits2Float(0x442c2ab0), SkBits2Float(0x42d253ca), SkBits2Float(0x442c2ab0), SkBits2Float(0x3f3504f3));  // 5, 688.667f, 105.164f, 688.667f, 0.707107f
+path.conicTo(SkBits2Float(0x434d53ca), SkBits2Float(0x442c2ab0), SkBits2Float(0x434d53ca), SkBits2Float(0x43ad6ab0), SkBits2Float(0x3f3504f3));  // 205.327f, 688.667f, 205.327f, 346.833f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc2834d04), SkBits2Float(0x43c6d5fb));  // -65.6504f, 397.672f
+path.conicTo(SkBits2Float(0x431a136e), SkBits2Float(0x4307cfe3), SkBits2Float(0x429ab133), SkBits2Float(0x428edb31), SkBits2Float(0x3f3504f3));  // 154.076f, 135.812f, 77.3461f, 71.4281f, 0.707107f
+path.conicTo(SkBits2Float(0x3f1dc4d0), SkBits2Float(0x40e169c2), SkBits2Float(0xc35b1c2c), SkBits2Float(0x438673b0), SkBits2Float(0x3f3504f3));  // 0.616284f, 7.04416f, -219.11f, 268.904f, 0.707107f
+path.conicTo(SkBits2Float(0xc3db6b0e), SkBits2Float(0x4404b0dc), SkBits2Float(0xc3b50da4), SkBits2Float(0x4414c96f), SkBits2Float(0x3f3504f3));  // -438.836f, 530.763f, -362.107f, 595.147f, 0.707107f
+path.conicTo(SkBits2Float(0xc38eb03a), SkBits2Float(0x4424e202), SkBits2Float(0xc2834d04), SkBits2Float(0x43c6d5fb), SkBits2Float(0x3f3504f3));  // -285.377f, 659.531f, -65.6504f, 397.672f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc398f46d), SkBits2Float(0x438337ac));  // -305.91f, 262.435f
+path.conicTo(SkBits2Float(0x41f5d870), SkBits2Float(0x434b137f), SkBits2Float(0x41556629), SkBits2Float(0x42d0de52), SkBits2Float(0x3f3504f3));  // 30.7307f, 203.076f, 13.3374f, 104.434f, 0.707107f
+path.conicTo(SkBits2Float(0xc081c918), SkBits2Float(0x40b95a5c), SkBits2Float(0xc3aa5918), SkBits2Float(0x42824d58), SkBits2Float(0x3f3504f3));  // -4.0558f, 5.79228f, -340.696f, 65.1511f, 0.707107f
+path.conicTo(SkBits2Float(0xc4295587), SkBits2Float(0x42f9050a), SkBits2Float(0xc424fc5c), SkBits2Float(0x435f26db), SkBits2Float(0x3f3504f3));  // -677.336f, 124.51f, -659.943f, 223.152f, 0.707107f
+path.conicTo(SkBits2Float(0xc420a331), SkBits2Float(0x43a0e598), SkBits2Float(0xc398f46d), SkBits2Float(0x438337ac), SkBits2Float(0x3f3504f3));  // -642.55f, 321.794f, -305.91f, 262.435f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc3c983e0), SkBits2Float(0x408cdc40));  // -403.03f, 4.40189f
+path.conicTo(SkBits2Float(0xc2d5fcd2), SkBits2Float(0x432f5193), SkBits2Float(0xc263a5d9), SkBits2Float(0x42b12617), SkBits2Float(0x3f3504f3));  // -106.994f, 175.319f, -56.912f, 88.5744f, 0.707107f
+path.conicTo(SkBits2Float(0xc0da9066), SkBits2Float(0x3fea4196), SkBits2Float(0xc3976eed), SkBits2Float(0xc329162e), SkBits2Float(0x3f3504f3));  // -6.83013f, 1.83013f, -302.867f, -169.087f, 0.707107f
+path.conicTo(SkBits2Float(0xc415b9cc), SkBits2Float(0xc3aa006f), SkBits2Float(0xc4223f09), SkBits2Float(0xc37d4256), SkBits2Float(0x3f3504f3));  // -598.903f, -340.003f, -648.985f, -253.259f, 0.707107f
+path.conicTo(SkBits2Float(0xc42ec446), SkBits2Float(0xc32683cf), SkBits2Float(0xc3c983e0), SkBits2Float(0x408cdc40), SkBits2Float(0x3f3504f3));  // -699.067f, -166.515f, -403.03f, 4.40189f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc39bc8c8), SkBits2Float(0xc37fb0d7));  // -311.569f, -255.691f
+path.conicTo(SkBits2Float(0xc342a797), SkBits2Float(0x42830e25), SkBits2Float(0xc2c9102e), SkBits2Float(0x41fa2834), SkBits2Float(0x3f3504f3));  // -194.655f, 65.5276f, -100.532f, 31.2696f, 0.707107f
+path.conicTo(SkBits2Float(0xc0cd12f5), SkBits2Float(0xc03f4152), SkBits2Float(0xc2f6a523), SkBits2Float(0xc3a21a77), SkBits2Float(0x3f3504f3));  // -6.40856f, -2.98836f, -123.323f, -324.207f, 0.707107f
+path.conicTo(SkBits2Float(0xc3703c8a), SkBits2Float(0xc4215b37), SkBits2Float(0xc3a72e05), SkBits2Float(0xc418cab4), SkBits2Float(0x3f3504f3));  // -240.236f, -645.425f, -334.36f, -611.167f, 0.707107f
+path.conicTo(SkBits2Float(0xc3d63dc5), SkBits2Float(0xc4103a31), SkBits2Float(0xc39bc8c8), SkBits2Float(0xc37fb0d7), SkBits2Float(0x3f3504f3));  // -428.483f, -576.909f, -311.569f, -255.691f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc294a419), SkBits2Float(0xc3c6124c));  // -74.3205f, -396.143f
+path.conicTo(SkBits2Float(0xc33f3c05), SkBits2Float(0xc295d95d), SkBits2Float(0xc2c2390a), SkBits2Float(0xc222aa8c), SkBits2Float(0x3f3504f3));  // -191.234f, -74.9245f, -97.1114f, -40.6665f, 0.707107f
+path.conicTo(SkBits2Float(0xc03f4154), SkBits2Float(0xc0cd12f4), SkBits2Float(0x42e3d9e6), SkBits2Float(0xc3a3d041), SkBits2Float(0x3f3504f3));  // -2.98836f, -6.40856f, 113.926f, -327.627f, 0.707107f
+path.conicTo(SkBits2Float(0x4366d6ec), SkBits2Float(0xc422361b), SkBits2Float(0x4308b76c), SkBits2Float(0xc42ac69e), SkBits2Float(0x3f3504f3));  // 230.84f, -648.845f, 136.716f, -683.103f, 0.707107f
+path.conicTo(SkBits2Float(0x422a5fb0), SkBits2Float(0xc4335721), SkBits2Float(0xc294a419), SkBits2Float(0xc3c6124c), SkBits2Float(0x3f3504f3));  // 42.5934f, -717.361f, -74.3205f, -396.143f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x4345b3f8), SkBits2Float(0xc3af9e21));  // 197.703f, -351.235f
+path.conicTo(SkBits2Float(0xc2c4aac2), SkBits2Float(0xc3345194), SkBits2Float(0xc24101bb), SkBits2Float(0xc2bb2617), SkBits2Float(0x3f3504f3));  // -98.3335f, -180.319f, -48.2517f, -93.5744f, 0.707107f
+path.conicTo(SkBits2Float(0x3fea41a0), SkBits2Float(0xc0da9066), SkBits2Float(0x4394eeee), SkBits2Float(0xc331bf31), SkBits2Float(0x3f3504f3));  // 1.83013f, -6.83013f, 297.867f, -177.747f, 0.707107f
+path.conicTo(SkBits2Float(0x441479cd), SkBits2Float(0xc3ae54f0), SkBits2Float(0x4407f490), SkBits2Float(0xc3d9b434), SkBits2Float(0x3f3504f3));  // 593.903f, -348.664f, 543.821f, -435.408f, 0.707107f
+path.conicTo(SkBits2Float(0x43f6dea8), SkBits2Float(0xc40289bc), SkBits2Float(0x4345b3f8), SkBits2Float(0xc3af9e21), SkBits2Float(0x3f3504f3));  // 493.74f, -522.152f, 197.703f, -351.235f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x43bc9c08), SkBits2Float(0xc30dfb1e));  // 377.219f, -141.981f
+path.conicTo(SkBits2Float(0x422250a2), SkBits2Float(0xc34956f5), SkBits2Float(0x41b97bee), SkBits2Float(0xc2cd653e), SkBits2Float(0x3f3504f3));  // 40.5787f, -201.34f, 23.1855f, -102.698f, 0.707107f
+path.conicTo(SkBits2Float(0x40b95a5b), SkBits2Float(0xc081c919), SkBits2Float(0x43ab375e), SkBits2Float(0x425d363a), SkBits2Float(0x3f3504f3));  // 5.79228f, -4.0558f, 342.433f, 55.303f, 0.707107f
+path.conicTo(SkBits2Float(0x4429c4a9), SkBits2Float(0x42e552cb), SkBits2Float(0x442e1dd4), SkBits2Float(0x4180287c), SkBits2Float(0x3f3504f3));  // 679.073f, 114.662f, 696.466f, 16.0198f, 0.707107f
+path.conicTo(SkBits2Float(0x443276ff), SkBits2Float(0xc2a53e8d), SkBits2Float(0x43bc9c08), SkBits2Float(0xc30dfb1e), SkBits2Float(0x3f3504f3));  // 713.859f, -82.6222f, 377.219f, -141.981f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x43be1d75), SkBits2Float(0x4305b53c));  // 380.23f, 133.708f
+path.conicTo(SkBits2Float(0x432080f6), SkBits2Float(0xc30026d3), SkBits2Float(0x42a78c44), SkBits2Float(0xc27f121c), SkBits2Float(0x3f3504f3));  // 160.504f, -128.152f, 83.774f, -63.7677f, 0.707107f
+path.conicTo(SkBits2Float(0x40e169c3), SkBits2Float(0x3f1dc4b8), SkBits2Float(0x4362c542), SkBits2Float(0x43833cea), SkBits2Float(0x3f3504f3));  // 7.04416f, 0.616283f, 226.771f, 262.476f, 0.707107f
+path.conicTo(SkBits2Float(0x43df3f9c), SkBits2Float(0x44031579), SkBits2Float(0x4402ce83), SkBits2Float(0x43e5f9cc), SkBits2Float(0x3f3504f3));  // 446.497f, 524.336f, 523.227f, 459.952f, 0.707107f
+path.conicTo(SkBits2Float(0x4415fd38), SkBits2Float(0x43c5c8a6), SkBits2Float(0x43be1d75), SkBits2Float(0x4305b53c), SkBits2Float(0x3f3504f3));  // 599.957f, 395.568f, 380.23f, 133.708f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x434d53ca), SkBits2Float(0x44487cfb));  // 205.327f, 801.953f
+path.conicTo(SkBits2Float(0x434d53ca), SkBits2Float(0x43e60f46), SkBits2Float(0x42d253ca), SkBits2Float(0x43e60f46), SkBits2Float(0x3f3504f3));  // 205.327f, 460.119f, 105.164f, 460.119f, 0.707107f
+path.conicTo(SkBits2Float(0x40a00000), SkBits2Float(0x43e60f46), SkBits2Float(0x40a00000), SkBits2Float(0x44487cfb), SkBits2Float(0x3f3504f3));  // 5, 460.119f, 5, 801.953f, 0.707107f
+path.conicTo(SkBits2Float(0x40a00000), SkBits2Float(0x448ef92a), SkBits2Float(0x42d253ca), SkBits2Float(0x448ef92a), SkBits2Float(0x3f3504f3));  // 5, 1143.79f, 105.164f, 1143.79f, 0.707107f
+path.conicTo(SkBits2Float(0x434d53ca), SkBits2Float(0x448ef92a), SkBits2Float(0x434d53ca), SkBits2Float(0x44487cfb), SkBits2Float(0x3f3504f3));  // 205.327f, 1143.79f, 205.327f, 801.953f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc2834d04), SkBits2Float(0x445532a0));  // -65.6504f, 852.791f
+path.conicTo(SkBits2Float(0x431a136e), SkBits2Float(0x4413bb9c), SkBits2Float(0x429ab133), SkBits2Float(0x4403a309), SkBits2Float(0x3f3504f3));  // 154.076f, 590.931f, 77.3461f, 526.547f, 0.707107f
+path.conicTo(SkBits2Float(0x3f1dc4d0), SkBits2Float(0x43e714ed), SkBits2Float(0xc35b1c2c), SkBits2Float(0x4435017b), SkBits2Float(0x3f3504f3));  // 0.616284f, 462.163f, -219.11f, 724.023f, 0.707107f
+path.conicTo(SkBits2Float(0xc3db6b0e), SkBits2Float(0x4476787f), SkBits2Float(0xc3b50da4), SkBits2Float(0x44834889), SkBits2Float(0x3f3504f3));  // -438.836f, 985.883f, -362.107f, 1050.27f, 0.707107f
+path.conicTo(SkBits2Float(0xc38eb03a), SkBits2Float(0x448b54d2), SkBits2Float(0xc2834d04), SkBits2Float(0x445532a0), SkBits2Float(0x3f3504f3));  // -285.377f, 1114.65f, -65.6504f, 852.791f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc398f46d), SkBits2Float(0x44336379));  // -305.91f, 717.554f
+path.conicTo(SkBits2Float(0x41f5d870), SkBits2Float(0x44248c83), SkBits2Float(0x41556629), SkBits2Float(0x440be36d), SkBits2Float(0x3f3504f3));  // 30.7307f, 658.195f, 13.3374f, 559.554f, 0.707107f
+path.conicTo(SkBits2Float(0xc081c918), SkBits2Float(0x43e674af), SkBits2Float(0xc3aa5918), SkBits2Float(0x4402114e), SkBits2Float(0x3f3504f3));  // -4.0558f, 460.912f, -340.696f, 520.27f, 0.707107f
+path.conicTo(SkBits2Float(0xc4295587), SkBits2Float(0x4410e844), SkBits2Float(0xc424fc5c), SkBits2Float(0x4429915a), SkBits2Float(0x3f3504f3));  // -677.336f, 579.629f, -659.943f, 678.271f, 0.707107f
+path.conicTo(SkBits2Float(0xc420a331), SkBits2Float(0x44423a6f), SkBits2Float(0xc398f46d), SkBits2Float(0x44336379), SkBits2Float(0x3f3504f3));  // -642.55f, 776.913f, -305.91f, 717.554f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc3c983e0), SkBits2Float(0x43e5c2b7));  // -403.03f, 459.521f
+path.conicTo(SkBits2Float(0xc2d5fcd2), SkBits2Float(0x441d9c08), SkBits2Float(0xc263a5d9), SkBits2Float(0x4407ec66), SkBits2Float(0x3f3504f3));  // -106.994f, 630.438f, -56.912f, 543.694f, 0.707107f
+path.conicTo(SkBits2Float(0xc0da9066), SkBits2Float(0x43e47988), SkBits2Float(0xc3976eed), SkBits2Float(0x438f042f), SkBits2Float(0x3f3504f3));  // -6.83013f, 456.949f, -302.867f, 286.033f, 0.707107f
+path.conicTo(SkBits2Float(0xc415b9cc), SkBits2Float(0x42e63b5c), SkBits2Float(0xc4223f09), SkBits2Float(0x4349dc36), SkBits2Float(0x3f3504f3));  // -598.903f, 115.116f, -648.985f, 201.86f, 0.707107f
+path.conicTo(SkBits2Float(0xc42ec446), SkBits2Float(0x43904d5e), SkBits2Float(0xc3c983e0), SkBits2Float(0x43e5c2b7), SkBits2Float(0x3f3504f3));  // -699.067f, 288.604f, -403.03f, 459.521f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc39bc8c8), SkBits2Float(0x43476db5));  // -311.569f, 199.429f
+path.conicTo(SkBits2Float(0xc342a797), SkBits2Float(0x44022968), SkBits2Float(0xc2c9102e), SkBits2Float(0x43f331c9), SkBits2Float(0x3f3504f3));  // -194.655f, 520.647f, -100.532f, 486.389f, 0.707107f
+path.conicTo(SkBits2Float(0xc0cd12f5), SkBits2Float(0x43e210c3), SkBits2Float(0xc2f6a523), SkBits2Float(0x4302e99e), SkBits2Float(0x3f3504f3));  // -6.40856f, 452.131f, -123.323f, 130.913f, 0.707107f
+path.conicTo(SkBits2Float(0xc3703c8a), SkBits2Float(0xc33e4e50), SkBits2Float(0xc3a72e05), SkBits2Float(0xc31c0c44), SkBits2Float(0x3f3504f3));  // -240.236f, -190.306f, -334.36f, -156.048f, 0.707107f
+path.conicTo(SkBits2Float(0xc3d63dc5), SkBits2Float(0xc2f39470), SkBits2Float(0xc39bc8c8), SkBits2Float(0x43476db5), SkBits2Float(0x3f3504f3));  // -428.483f, -121.79f, -311.569f, 199.429f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc294a419), SkBits2Float(0x426be7d0));  // -74.3205f, 58.9764f
+path.conicTo(SkBits2Float(0xc33f3c05), SkBits2Float(0x43be18ef), SkBits2Float(0xc2c2390a), SkBits2Float(0x43cf39f4), SkBits2Float(0x3f3504f3));  // -191.234f, 380.195f, -97.1114f, 414.453f, 0.707107f
+path.conicTo(SkBits2Float(0xc03f4154), SkBits2Float(0x43e05afa), SkBits2Float(0x42e3d9e6), SkBits2Float(0x42fefc14), SkBits2Float(0x3f3504f3));  // -2.98836f, 448.711f, 113.926f, 127.492f, 0.707107f
+path.conicTo(SkBits2Float(0x4366d6ec), SkBits2Float(0xc341b9e0), SkBits2Float(0x4308b76c), SkBits2Float(0xc363fbec), SkBits2Float(0x3f3504f3));  // 230.84f, -193.726f, 136.716f, -227.984f, 0.707107f
+path.conicTo(SkBits2Float(0x422a5fb0), SkBits2Float(0xc3831efc), SkBits2Float(0xc294a419), SkBits2Float(0x426be7d0), SkBits2Float(0x3f3504f3));  // 42.5934f, -262.242f, -74.3205f, 58.9764f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x4345b3f8), SkBits2Float(0x42cfc494));  // 197.703f, 103.884f
+path.conicTo(SkBits2Float(0xc2c4aac2), SkBits2Float(0x4389667c), SkBits2Float(0xc24101bb), SkBits2Float(0x43b4c5c0), SkBits2Float(0x3f3504f3));  // -98.3335f, 274.801f, -48.2517f, 361.545f, 0.707107f
+path.conicTo(SkBits2Float(0x3fea41a0), SkBits2Float(0x43e02504), SkBits2Float(0x4394eeee), SkBits2Float(0x438aafae), SkBits2Float(0x3f3504f3));  // 1.83013f, 448.289f, 297.867f, 277.372f, 0.707107f
+path.conicTo(SkBits2Float(0x441479cd), SkBits2Float(0x42d4e958), SkBits2Float(0x4407f490), SkBits2Float(0x419db120), SkBits2Float(0x3f3504f3));  // 593.903f, 106.456f, 543.821f, 19.7115f, 0.707107f
+path.conicTo(SkBits2Float(0x43f6dea8), SkBits2Float(0xc28610c8), SkBits2Float(0x4345b3f8), SkBits2Float(0x42cfc494), SkBits2Float(0x3f3504f3));  // 493.74f, -67.0328f, 197.703f, 103.884f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x43bc9c08), SkBits2Float(0x439c91b7));  // 377.219f, 313.138f
+path.conicTo(SkBits2Float(0x422250a2), SkBits2Float(0x437dc797), SkBits2Float(0x41b97bee), SkBits2Float(0x43b035f6), SkBits2Float(0x3f3504f3));  // 40.5787f, 253.78f, 23.1855f, 352.422f, 0.707107f
+path.conicTo(SkBits2Float(0x40b95a5b), SkBits2Float(0x43e18822), SkBits2Float(0x43ab375e), SkBits2Float(0x43ff360d), SkBits2Float(0x3f3504f3));  // 5.79228f, 451.064f, 342.433f, 510.422f, 0.707107f
+path.conicTo(SkBits2Float(0x4429c4a9), SkBits2Float(0x440e71fc), SkBits2Float(0x442e1dd4), SkBits2Float(0x43eb91ce), SkBits2Float(0x3f3504f3));  // 679.073f, 569.781f, 696.466f, 471.139f, 0.707107f
+path.conicTo(SkBits2Float(0x443276ff), SkBits2Float(0x43ba3fa3), SkBits2Float(0x43bc9c08), SkBits2Float(0x439c91b7), SkBits2Float(0x3f3504f3));  // 713.859f, 372.497f, 377.219f, 313.138f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x43be1d75), SkBits2Float(0x441334f2));  // 380.23f, 588.827f
+path.conicTo(SkBits2Float(0x432080f6), SkBits2Float(0x43a37bdc), SkBits2Float(0x42a78c44), SkBits2Float(0x43c3ad02), SkBits2Float(0x3f3504f3));  // 160.504f, 326.968f, 83.774f, 391.352f, 0.707107f
+path.conicTo(SkBits2Float(0x40e169c3), SkBits2Float(0x43e3de28), SkBits2Float(0x4362c542), SkBits2Float(0x44336618), SkBits2Float(0x3f3504f3));  // 7.04416f, 455.736f, 226.771f, 717.595f, 0.707107f
+path.conicTo(SkBits2Float(0x43df3f9c), SkBits2Float(0x4474dd1c), SkBits2Float(0x4402ce83), SkBits2Float(0x4464c489), SkBits2Float(0x3f3504f3));  // 446.497f, 979.455f, 523.227f, 915.071f, 0.707107f
+path.conicTo(SkBits2Float(0x4415fd38), SkBits2Float(0x4454abf6), SkBits2Float(0x43be1d75), SkBits2Float(0x441334f2), SkBits2Float(0x3f3504f3));  // 599.957f, 850.687f, 380.23f, 588.827f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x43bb9978), SkBits2Float(0x43ad6ab0));  // 375.199f, 346.833f
+path.conicTo(SkBits2Float(0x43bb9978), SkBits2Float(0x40a00000), SkBits2Float(0x43898486), SkBits2Float(0x40a00000), SkBits2Float(0x3f3504f3));  // 375.199f, 5, 275.035f, 5, 0.707107f
+path.conicTo(SkBits2Float(0x432edf26), SkBits2Float(0x40a00000), SkBits2Float(0x432edf26), SkBits2Float(0x43ad6ab0), SkBits2Float(0x3f3504f3));  // 174.872f, 5, 174.872f, 346.833f, 0.707107f
+path.conicTo(SkBits2Float(0x432edf26), SkBits2Float(0x442c2ab0), SkBits2Float(0x43898486), SkBits2Float(0x442c2ab0), SkBits2Float(0x3f3504f3));  // 174.872f, 688.667f, 275.035f, 688.667f, 0.707107f
+path.conicTo(SkBits2Float(0x43bb9978), SkBits2Float(0x442c2ab0), SkBits2Float(0x43bb9978), SkBits2Float(0x43ad6ab0), SkBits2Float(0x3f3504f3));  // 375.199f, 688.667f, 375.199f, 346.833f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x42d07148), SkBits2Float(0x43c6d5fb));  // 104.221f, 397.672f
+path.conicTo(SkBits2Float(0x43a1f94a), SkBits2Float(0x4307cfe3), SkBits2Float(0x437737c0), SkBits2Float(0x428edb31), SkBits2Float(0x3f3504f3));  // 323.948f, 135.812f, 247.218f, 71.4281f, 0.707107f
+path.conicTo(SkBits2Float(0x432a7ceb), SkBits2Float(0x40e169c2), SkBits2Float(0xc244f418), SkBits2Float(0x438673b0), SkBits2Float(0x3f3504f3));  // 170.488f, 7.04416f, -49.2384f, 268.904f, 0.707107f
+path.conicTo(SkBits2Float(0xc3867b7b), SkBits2Float(0x4404b0dc), SkBits2Float(0xc3403c22), SkBits2Float(0x4414c96f), SkBits2Float(0x3f3504f3));  // -268.965f, 530.763f, -192.235f, 595.147f, 0.707107f
+path.conicTo(SkBits2Float(0xc2e7029c), SkBits2Float(0x4424e202), SkBits2Float(0x42d07148), SkBits2Float(0x43c6d5fb), SkBits2Float(0x3f3504f3));  // -115.505f, 659.531f, 104.221f, 397.672f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc30809b4), SkBits2Float(0x438337ac));  // -136.038f, 262.435f
+path.conicTo(SkBits2Float(0x43489a34), SkBits2Float(0x434b137f), SkBits2Float(0x43373589), SkBits2Float(0x42d0de52), SkBits2Float(0x3f3504f3));  // 200.602f, 203.076f, 183.209f, 104.434f, 0.707107f
+path.conicTo(SkBits2Float(0x4325d0dd), SkBits2Float(0x40b95a5c), SkBits2Float(0xc32ad30a), SkBits2Float(0x42824d58), SkBits2Float(0x3f3504f3));  // 165.816f, 5.79228f, -170.824f, 65.1511f, 0.707107f
+path.conicTo(SkBits2Float(0xc3fdbb7b), SkBits2Float(0x42f9050a), SkBits2Float(0xc3f50925), SkBits2Float(0x435f26db), SkBits2Float(0x3f3504f3));  // -507.465f, 124.51f, -490.071f, 223.152f, 0.707107f
+path.conicTo(SkBits2Float(0xc3ec56cf), SkBits2Float(0x43a0e598), SkBits2Float(0xc30809b4), SkBits2Float(0x438337ac), SkBits2Float(0x3f3504f3));  // -472.678f, 321.794f, -136.038f, 262.435f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc369289a), SkBits2Float(0x408cdc40));  // -233.159f, 4.40189f
+path.conicTo(SkBits2Float(0x427b82f4), SkBits2Float(0x432f5193), SkBits2Float(0x42e1eb60), SkBits2Float(0x42b12617), SkBits2Float(0x3f3504f3));  // 62.8779f, 175.319f, 112.96f, 88.5744f, 0.707107f
+path.conicTo(SkBits2Float(0x43230aa3), SkBits2Float(0x3fea4196), SkBits2Float(0xc304feb4), SkBits2Float(0xc329162e), SkBits2Float(0x3f3504f3));  // 163.042f, 1.83013f, -132.995f, -169.087f, 0.707107f
+path.conicTo(SkBits2Float(0xc3d68405), SkBits2Float(0xc3aa006f), SkBits2Float(0xc3ef8e7f), SkBits2Float(0xc37d4256), SkBits2Float(0x3f3504f3));  // -429.031f, -340.003f, -479.113f, -253.259f, 0.707107f
+path.conicTo(SkBits2Float(0xc4044c7c), SkBits2Float(0xc32683cf), SkBits2Float(0xc369289a), SkBits2Float(0x408cdc40), SkBits2Float(0x3f3504f3));  // -529.195f, -166.515f, -233.159f, 4.40189f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc30db26a), SkBits2Float(0xc37fb0d7));  // -141.697f, -255.691f
+path.conicTo(SkBits2Float(0xc1c64388), SkBits2Float(0x42830e25), SkBits2Float(0x428aae1e), SkBits2Float(0x41fa2834), SkBits2Float(0x3f3504f3));  // -24.783f, 65.5276f, 69.3401f, 31.2696f, 0.707107f
+path.conicTo(SkBits2Float(0x4323768e), SkBits2Float(0xc03f4152), SkBits2Float(0x423a3252), SkBits2Float(0xc3a21a77), SkBits2Float(0x3f3504f3));  // 163.463f, -2.98836f, 46.5491f, -324.207f, 0.707107f
+path.conicTo(SkBits2Float(0xc28cbac8), SkBits2Float(0xc4215b37), SkBits2Float(0xc3247ce4), SkBits2Float(0xc418cab4), SkBits2Float(0x3f3504f3));  // -70.3648f, -645.425f, -164.488f, -611.167f, 0.707107f
+path.conicTo(SkBits2Float(0xc3814e32), SkBits2Float(0xc4103a31), SkBits2Float(0xc30db26a), SkBits2Float(0xc37fb0d7), SkBits2Float(0x3f3504f3));  // -258.611f, -576.909f, -141.697f, -255.691f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x42bf1a33), SkBits2Float(0xc3c6124c));  // 95.5512f, -396.143f
+path.conicTo(SkBits2Float(0xc1aae6f8), SkBits2Float(0xc295d95d), SkBits2Float(0x42918542), SkBits2Float(0xc222aa8c), SkBits2Float(0x3f3504f3));  // -21.3628f, -74.9245f, 72.7603f, -40.6665f, 0.707107f
+path.conicTo(SkBits2Float(0x4326e221), SkBits2Float(0xc0cd12f4), SkBits2Float(0x438de60c), SkBits2Float(0xc3a3d041), SkBits2Float(0x3f3504f3));  // 166.883f, -6.40856f, 283.797f, -327.627f, 0.707107f
+path.conicTo(SkBits2Float(0x43c85b09), SkBits2Float(0xc422361b), SkBits2Float(0x43994b49), SkBits2Float(0xc42ac69e), SkBits2Float(0x3f3504f3));  // 400.711f, -648.845f, 306.588f, -683.103f, 0.707107f
+path.conicTo(SkBits2Float(0x43547712), SkBits2Float(0xc4335721), SkBits2Float(0x42bf1a33), SkBits2Float(0xc3c6124c), SkBits2Float(0x3f3504f3));  // 212.465f, -717.361f, 95.5512f, -396.143f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x43b7c98f), SkBits2Float(0xc3af9e21));  // 367.575f, -351.235f
+path.conicTo(SkBits2Float(0x428f138a), SkBits2Float(0xc3345194), SkBits2Float(0x42f33d6e), SkBits2Float(0xc2bb2617), SkBits2Float(0x3f3504f3));  // 71.5382f, -180.319f, 121.62f, -93.5744f, 0.707107f
+path.conicTo(SkBits2Float(0x432bb3a9), SkBits2Float(0xc0da9066), SkBits2Float(0x43e9de81), SkBits2Float(0xc331bf31), SkBits2Float(0x3f3504f3));  // 171.702f, -6.83013f, 467.738f, -177.747f, 0.707107f
+path.conicTo(SkBits2Float(0x443ef196), SkBits2Float(0xc3ae54f0), SkBits2Float(0x44326c5a), SkBits2Float(0xc3d9b434), SkBits2Float(0x3f3504f3));  // 763.775f, -348.664f, 713.693f, -435.408f, 0.707107f
+path.conicTo(SkBits2Float(0x4425e71e), SkBits2Float(0xc40289bc), SkBits2Float(0x43b7c98f), SkBits2Float(0xc3af9e21), SkBits2Float(0x3f3504f3));  // 663.611f, -522.152f, 367.575f, -351.235f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x4408c5ce), SkBits2Float(0xc30dfb1e));  // 547.091f, -141.981f
+path.conicTo(SkBits2Float(0x4352734e), SkBits2Float(0xc34956f5), SkBits2Float(0x43410ea4), SkBits2Float(0xc2cd653e), SkBits2Float(0x3f3504f3));  // 210.45f, -201.34f, 193.057f, -102.698f, 0.707107f
+path.conicTo(SkBits2Float(0x432fa9f9), SkBits2Float(0xc081c919), SkBits2Float(0x44001378), SkBits2Float(0x425d363a), SkBits2Float(0x3f3504f3));  // 175.664f, -4.0558f, 512.304f, 55.303f, 0.707107f
+path.conicTo(SkBits2Float(0x44543c72), SkBits2Float(0x42e552cb), SkBits2Float(0x4458959e), SkBits2Float(0x4180287c), SkBits2Float(0x3f3504f3));  // 848.944f, 114.662f, 866.338f, 16.0198f, 0.707107f
+path.conicTo(SkBits2Float(0x445ceec8), SkBits2Float(0xc2a53e8d), SkBits2Float(0x4408c5ce), SkBits2Float(0xc30dfb1e), SkBits2Float(0x3f3504f3));  // 883.731f, -82.6222f, 547.091f, -141.981f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x44098684), SkBits2Float(0x4305b53c));  // 550.102f, 133.708f
+path.conicTo(SkBits2Float(0x43a5300e), SkBits2Float(0xc30026d3), SkBits2Float(0x437da548), SkBits2Float(0xc27f121c), SkBits2Float(0x3f3504f3));  // 330.375f, -128.152f, 253.646f, -63.7677f, 0.707107f
+path.conicTo(SkBits2Float(0x4330ea74), SkBits2Float(0x3f1dc4b8), SkBits2Float(0x43c65234), SkBits2Float(0x43833cea), SkBits2Float(0x3f3504f3));  // 176.916f, 0.616283f, 396.642f, 262.476f, 0.707107f
+path.conicTo(SkBits2Float(0x441a1798), SkBits2Float(0x44031579), SkBits2Float(0x442d464c), SkBits2Float(0x43e5f9cc), SkBits2Float(0x3f3504f3));  // 616.369f, 524.336f, 693.098f, 459.952f, 0.707107f
+path.conicTo(SkBits2Float(0x44407502), SkBits2Float(0x43c5c8a6), SkBits2Float(0x44098684), SkBits2Float(0x4305b53c), SkBits2Float(0x3f3504f3));  // 769.828f, 395.568f, 550.102f, 133.708f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x43bb9978), SkBits2Float(0x44487cfb));  // 375.199f, 801.953f
+path.conicTo(SkBits2Float(0x43bb9978), SkBits2Float(0x43e60f46), SkBits2Float(0x43898486), SkBits2Float(0x43e60f46), SkBits2Float(0x3f3504f3));  // 375.199f, 460.119f, 275.035f, 460.119f, 0.707107f
+path.conicTo(SkBits2Float(0x432edf26), SkBits2Float(0x43e60f46), SkBits2Float(0x432edf26), SkBits2Float(0x44487cfb), SkBits2Float(0x3f3504f3));  // 174.872f, 460.119f, 174.872f, 801.953f, 0.707107f
+path.conicTo(SkBits2Float(0x432edf26), SkBits2Float(0x448ef92a), SkBits2Float(0x43898486), SkBits2Float(0x448ef92a), SkBits2Float(0x3f3504f3));  // 174.872f, 1143.79f, 275.035f, 1143.79f, 0.707107f
+path.conicTo(SkBits2Float(0x43bb9978), SkBits2Float(0x448ef92a), SkBits2Float(0x43bb9978), SkBits2Float(0x44487cfb), SkBits2Float(0x3f3504f3));  // 375.199f, 1143.79f, 375.199f, 801.953f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x42d07148), SkBits2Float(0x445532a0));  // 104.221f, 852.791f
+path.conicTo(SkBits2Float(0x43a1f94a), SkBits2Float(0x4413bb9c), SkBits2Float(0x437737c0), SkBits2Float(0x4403a309), SkBits2Float(0x3f3504f3));  // 323.948f, 590.931f, 247.218f, 526.547f, 0.707107f
+path.conicTo(SkBits2Float(0x432a7ceb), SkBits2Float(0x43e714ed), SkBits2Float(0xc244f418), SkBits2Float(0x4435017b), SkBits2Float(0x3f3504f3));  // 170.488f, 462.163f, -49.2384f, 724.023f, 0.707107f
+path.conicTo(SkBits2Float(0xc3867b7b), SkBits2Float(0x4476787f), SkBits2Float(0xc3403c22), SkBits2Float(0x44834889), SkBits2Float(0x3f3504f3));  // -268.965f, 985.883f, -192.235f, 1050.27f, 0.707107f
+path.conicTo(SkBits2Float(0xc2e7029c), SkBits2Float(0x448b54d2), SkBits2Float(0x42d07148), SkBits2Float(0x445532a0), SkBits2Float(0x3f3504f3));  // -115.505f, 1114.65f, 104.221f, 852.791f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc30809b4), SkBits2Float(0x44336379));  // -136.038f, 717.554f
+path.conicTo(SkBits2Float(0x43489a34), SkBits2Float(0x44248c83), SkBits2Float(0x43373589), SkBits2Float(0x440be36d), SkBits2Float(0x3f3504f3));  // 200.602f, 658.195f, 183.209f, 559.554f, 0.707107f
+path.conicTo(SkBits2Float(0x4325d0dd), SkBits2Float(0x43e674af), SkBits2Float(0xc32ad30a), SkBits2Float(0x4402114e), SkBits2Float(0x3f3504f3));  // 165.816f, 460.912f, -170.824f, 520.27f, 0.707107f
+path.conicTo(SkBits2Float(0xc3fdbb7b), SkBits2Float(0x4410e844), SkBits2Float(0xc3f50925), SkBits2Float(0x4429915a), SkBits2Float(0x3f3504f3));  // -507.465f, 579.629f, -490.071f, 678.271f, 0.707107f
+path.conicTo(SkBits2Float(0xc3ec56cf), SkBits2Float(0x44423a6f), SkBits2Float(0xc30809b4), SkBits2Float(0x44336379), SkBits2Float(0x3f3504f3));  // -472.678f, 776.913f, -136.038f, 717.554f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc369289a), SkBits2Float(0x43e5c2b7));  // -233.159f, 459.521f
+path.conicTo(SkBits2Float(0x427b82f4), SkBits2Float(0x441d9c08), SkBits2Float(0x42e1eb60), SkBits2Float(0x4407ec66), SkBits2Float(0x3f3504f3));  // 62.8779f, 630.438f, 112.96f, 543.694f, 0.707107f
+path.conicTo(SkBits2Float(0x43230aa3), SkBits2Float(0x43e47988), SkBits2Float(0xc304feb4), SkBits2Float(0x438f042f), SkBits2Float(0x3f3504f3));  // 163.042f, 456.949f, -132.995f, 286.033f, 0.707107f
+path.conicTo(SkBits2Float(0xc3d68405), SkBits2Float(0x42e63b5c), SkBits2Float(0xc3ef8e7f), SkBits2Float(0x4349dc36), SkBits2Float(0x3f3504f3));  // -429.031f, 115.116f, -479.113f, 201.86f, 0.707107f
+path.conicTo(SkBits2Float(0xc4044c7c), SkBits2Float(0x43904d5e), SkBits2Float(0xc369289a), SkBits2Float(0x43e5c2b7), SkBits2Float(0x3f3504f3));  // -529.195f, 288.604f, -233.159f, 459.521f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0xc30db26a), SkBits2Float(0x43476db5));  // -141.697f, 199.429f
+path.conicTo(SkBits2Float(0xc1c64388), SkBits2Float(0x44022968), SkBits2Float(0x428aae1e), SkBits2Float(0x43f331c9), SkBits2Float(0x3f3504f3));  // -24.783f, 520.647f, 69.3401f, 486.389f, 0.707107f
+path.conicTo(SkBits2Float(0x4323768e), SkBits2Float(0x43e210c3), SkBits2Float(0x423a3252), SkBits2Float(0x4302e99e), SkBits2Float(0x3f3504f3));  // 163.463f, 452.131f, 46.5491f, 130.913f, 0.707107f
+path.conicTo(SkBits2Float(0xc28cbac8), SkBits2Float(0xc33e4e50), SkBits2Float(0xc3247ce4), SkBits2Float(0xc31c0c44), SkBits2Float(0x3f3504f3));  // -70.3648f, -190.306f, -164.488f, -156.048f, 0.707107f
+path.conicTo(SkBits2Float(0xc3814e32), SkBits2Float(0xc2f39470), SkBits2Float(0xc30db26a), SkBits2Float(0x43476db5), SkBits2Float(0x3f3504f3));  // -258.611f, -121.79f, -141.697f, 199.429f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x42bf1a33), SkBits2Float(0x426be7d0));  // 95.5512f, 58.9764f
+path.conicTo(SkBits2Float(0xc1aae6f8), SkBits2Float(0x43be18ef), SkBits2Float(0x42918542), SkBits2Float(0x43cf39f4), SkBits2Float(0x3f3504f3));  // -21.3628f, 380.195f, 72.7603f, 414.453f, 0.707107f
+path.conicTo(SkBits2Float(0x4326e221), SkBits2Float(0x43e05afa), SkBits2Float(0x438de60c), SkBits2Float(0x42fefc14), SkBits2Float(0x3f3504f3));  // 166.883f, 448.711f, 283.797f, 127.492f, 0.707107f
+path.conicTo(SkBits2Float(0x43c85b09), SkBits2Float(0xc341b9e0), SkBits2Float(0x43994b49), SkBits2Float(0xc363fbec), SkBits2Float(0x3f3504f3));  // 400.711f, -193.726f, 306.588f, -227.984f, 0.707107f
+path.conicTo(SkBits2Float(0x43547712), SkBits2Float(0xc3831efc), SkBits2Float(0x42bf1a33), SkBits2Float(0x426be7d0), SkBits2Float(0x3f3504f3));  // 212.465f, -262.242f, 95.5512f, 58.9764f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x43b7c98f), SkBits2Float(0x42cfc494));  // 367.575f, 103.884f
+path.conicTo(SkBits2Float(0x428f138a), SkBits2Float(0x4389667c), SkBits2Float(0x42f33d6e), SkBits2Float(0x43b4c5c0), SkBits2Float(0x3f3504f3));  // 71.5382f, 274.801f, 121.62f, 361.545f, 0.707107f
+path.conicTo(SkBits2Float(0x432bb3a9), SkBits2Float(0x43e02504), SkBits2Float(0x43e9de81), SkBits2Float(0x438aafae), SkBits2Float(0x3f3504f3));  // 171.702f, 448.289f, 467.738f, 277.372f, 0.707107f
+path.conicTo(SkBits2Float(0x443ef196), SkBits2Float(0x42d4e958), SkBits2Float(0x44326c5a), SkBits2Float(0x419db120), SkBits2Float(0x3f3504f3));  // 763.775f, 106.456f, 713.693f, 19.7115f, 0.707107f
+path.conicTo(SkBits2Float(0x4425e71e), SkBits2Float(0xc28610c8), SkBits2Float(0x43b7c98f), SkBits2Float(0x42cfc494), SkBits2Float(0x3f3504f3));  // 663.611f, -67.0328f, 367.575f, 103.884f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x4408c5ce), SkBits2Float(0x439c91b7));  // 547.091f, 313.138f
+path.conicTo(SkBits2Float(0x4352734e), SkBits2Float(0x437dc797), SkBits2Float(0x43410ea4), SkBits2Float(0x43b035f6), SkBits2Float(0x3f3504f3));  // 210.45f, 253.78f, 193.057f, 352.422f, 0.707107f
+path.conicTo(SkBits2Float(0x432fa9f9), SkBits2Float(0x43e18822), SkBits2Float(0x44001378), SkBits2Float(0x43ff360d), SkBits2Float(0x3f3504f3));  // 175.664f, 451.064f, 512.304f, 510.422f, 0.707107f
+path.conicTo(SkBits2Float(0x44543c72), SkBits2Float(0x440e71fc), SkBits2Float(0x4458959e), SkBits2Float(0x43eb91ce), SkBits2Float(0x3f3504f3));  // 848.944f, 569.781f, 866.338f, 471.139f, 0.707107f
+path.conicTo(SkBits2Float(0x445ceec8), SkBits2Float(0x43ba3fa3), SkBits2Float(0x4408c5ce), SkBits2Float(0x439c91b7), SkBits2Float(0x3f3504f3));  // 883.731f, 372.497f, 547.091f, 313.138f, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x44098684), SkBits2Float(0x441334f2));  // 550.102f, 588.827f
+path.conicTo(SkBits2Float(0x43a5300e), SkBits2Float(0x43a37bdc), SkBits2Float(0x437da548), SkBits2Float(0x43c3ad02), SkBits2Float(0x3f3504f3));  // 330.375f, 326.968f, 253.646f, 391.352f, 0.707107f
+path.conicTo(SkBits2Float(0x4330ea74), SkBits2Float(0x43e3de28), SkBits2Float(0x43c65234), SkBits2Float(0x44336618), SkBits2Float(0x3f3504f3));  // 176.916f, 455.736f, 396.642f, 717.595f, 0.707107f
+path.conicTo(SkBits2Float(0x441a1798), SkBits2Float(0x4474dd1c), SkBits2Float(0x442d464c), SkBits2Float(0x4464c489), SkBits2Float(0x3f3504f3));  // 616.369f, 979.455f, 693.098f, 915.071f, 0.707107f
+path.conicTo(SkBits2Float(0x44407502), SkBits2Float(0x4454abf6), SkBits2Float(0x44098684), SkBits2Float(0x441334f2), SkBits2Float(0x3f3504f3));  // 769.828f, 850.687f, 550.102f, 588.827f, 0.707107f
+path.close();
+SkPath lastFailed = path;
+#if DEBUG_SIMPLIFY_FAILS
+    for (;;) {
+        SkPath failed = subset_simplify_fail(reporter, lastFailed);
+        if (failed == lastFailed) {
+            break;
+        }
+        lastFailed = failed;
+    }
+#endif
+    testSimplify(reporter, lastFailed, __FUNCTION__);
+}
+
+DEF_TEST(SixtyOvals_2_2_9_73_reduced, reporter) {
+    SkPath path;
+path.moveTo(377.219f, -141.981f);
+path.conicTo(40.5787f, -201.34f, 23.1855f, -102.698f, 0.707107f);
+path.lineTo(377.219f, -141.981f);
+path.close();
+path.moveTo(306.588f, -227.984f);
+path.conicTo(212.465f, -262.242f, 95.5512f, 58.9764f, 0.707107f);
+path.lineTo(306.588f, -227.984f);
+path.close();
+testSimplify(reporter, path, __FUNCTION__);
+}
+
+DEF_TEST(SixtyOvalsA, reporter) {
+SkPath path;
+path.setFillType(SkPath::kEvenOdd_FillType);
+path.moveTo(11.1722f, -8.10398f);
+path.conicTo(22.9143f, -10.3787f, 23.7764f, -7.72542f, 1.00863f);
+path.conicTo(24.6671f, -4.98406f, 13.8147f, 0.0166066f, 0.973016f);
+path.conicTo(24.6378f, 5.07425f, 23.7764f, 7.72542f, 1.00888f);
+path.conicTo(22.8777f, 10.4915f, 11.1648f, 8.13034f, 0.960143f);
+path.conicTo(16.9503f, 18.5866f, 14.6946f, 20.2254f, 1.00881f);
+path.conicTo(12.4417f, 21.8623f, 4.29722f, 13.1468f, 1.0092f);
+path.conicTo(2.92708f, 25, 0, 25, 0.955692f);
+path.conicTo(-2.79361f, 25, -4.258f, 13.1048f, 1.00818f);
+path.conicTo(-4.27813f, 13.1264f, -4.29822f, 13.1479f, 1.03158f);
+path.conicTo(-12.44f, 21.8635f, -14.6946f, 20.2254f, 1.00811f);
+path.conicTo(-16.9933f, 18.5554f, -11.1722f, 8.10398f, 0.989875f);
+path.conicTo(-22.9143f, 10.3787f, -23.7764f, 7.72542f, 1.00863f);
+path.conicTo(-24.6671f, 4.98406f, -13.8147f, -0.0166066f, 0.973016f);
+path.conicTo(-24.6378f, -5.07425f, -23.7764f, -7.72542f, 1.00888f);
+path.conicTo(-22.8777f, -10.4915f, -11.1648f, -8.13034f, 0.960143f);
+path.conicTo(-16.9503f, -18.5866f, -14.6946f, -20.2254f, 1.00881f);
+path.conicTo(-12.4417f, -21.8623f, -4.29722f, -13.1468f, 1.0092f);
+path.conicTo(-2.92708f, -25, 0, -25, 0.955692f);
+path.conicTo(2.79361f, -25, 4.258f, -13.1048f, 1.00818f);
+path.conicTo(4.27813f, -13.1264f, 4.29822f, -13.1479f, 1.03158f);
+path.conicTo(12.44f, -21.8635f, 14.6946f, -20.2254f, 1.00811f);
+path.conicTo(16.9933f, -18.5554f, 11.1722f, -8.10398f, 0.989875f);
+path.close();
+SkPath one(path);
+path.reset();
+path.setFillType(SkPath::kWinding_FillType);
+path.moveTo(-1.54509f, -4.75528f);
+path.conicTo(22.2313f, -12.4807f, 23.7764f, -7.72543f, 0.707107f);
+path.conicTo(25.3215f, -2.97014f, 1.54509f, 4.75528f, 0.707107f);
+path.conicTo(-22.2313f, 12.4807f, -23.7764f, 7.72543f, 0.707107f);
+path.conicTo(-25.3215f, 2.97014f, -1.54509f, -4.75528f, 0.707107f);
+path.close();
+SkPath two(path);
+SkPath result;
+Op(one, two, kUnion_SkPathOp, &result);
+}
+
+DEF_TEST(SixtyOvalsAX, reporter) {
+SkPath path;
+path.setFillType(SkPath::kEvenOdd_FillType);
+path.moveTo(SkBits2Float(0x4132c174), SkBits2Float(0xc101a9e5));  // 11.1722f, -8.10398f
+path.conicTo(SkBits2Float(0x41b7508a), SkBits2Float(0xc1260efe), SkBits2Float(0x41be3618), SkBits2Float(0xc0f736ad), SkBits2Float(0x3f811abd));  // 22.9143f, -10.3787f, 23.7764f, -7.72542f, 1.00863f
+path.conicTo(SkBits2Float(0x41c5564b), SkBits2Float(0xc09f7d6d), SkBits2Float(0x415d0934), SkBits2Float(0x3c880a93), SkBits2Float(0x3f79179a));  // 24.6671f, -4.98406f, 13.8147f, 0.0166066f, 0.973016f
+path.conicTo(SkBits2Float(0x41c51a48), SkBits2Float(0x40a2603c), SkBits2Float(0x41be3618), SkBits2Float(0x40f736ac), SkBits2Float(0x3f8122f3));  // 24.6378f, 5.07425f, 23.7764f, 7.72542f, 1.00888f
+path.conicTo(SkBits2Float(0x41b7056f), SkBits2Float(0x4127dd49), SkBits2Float(0x4132a328), SkBits2Float(0x410215e1), SkBits2Float(0x3f75cbec));  // 22.8777f, 10.4915f, 11.1648f, 8.13034f, 0.960143f
+path.conicTo(SkBits2Float(0x41879a3b), SkBits2Float(0x4194b151), SkBits2Float(0x416b1d34), SkBits2Float(0x41a1cdac), SkBits2Float(0x3f8120d4));  // 16.9503f, 18.5866f, 14.6946f, 20.2254f, 1.00881f
+path.conicTo(SkBits2Float(0x41471107), SkBits2Float(0x41aee601), SkBits2Float(0x408982d1), SkBits2Float(0x41525939), SkBits2Float(0x3f812d7f));  // 12.4417f, 21.8623f, 4.29722f, 13.1468f, 1.0092f
+path.conicTo(SkBits2Float(0x403b5543), SkBits2Float(0x41c80000), SkBits2Float(0x00000000), SkBits2Float(0x41c80000), SkBits2Float(0x3f74a837));  // 2.92708f, 25, 0, 25, 0.955692f
+path.conicTo(SkBits2Float(0xc032ca93), SkBits2Float(0x41c80000), SkBits2Float(0xc088418e), SkBits2Float(0x4151ad32), SkBits2Float(0x3f810c2d));  // -2.79361f, 25, -4.258f, 13.1048f, 1.00818f
+path.conicTo(SkBits2Float(0xc088e66c), SkBits2Float(0x4152058a), SkBits2Float(0xc0898afc), SkBits2Float(0x41525d9e), SkBits2Float(0x3f840adb));  // -4.27813f, 13.1264f, -4.29822f, 13.1479f, 1.03158f
+path.conicTo(SkBits2Float(0xc1470a56), SkBits2Float(0x41aee870), SkBits2Float(0xc16b1d36), SkBits2Float(0x41a1cdac), SkBits2Float(0x3f81099f));  // -12.44f, 21.8635f, -14.6946f, 20.2254f, 1.00811f
+path.conicTo(SkBits2Float(0xc187f23a), SkBits2Float(0x41947162), SkBits2Float(0xc132c174), SkBits2Float(0x4101a9e5), SkBits2Float(0x3f7d6873));  // -16.9933f, 18.5554f, -11.1722f, 8.10398f, 0.989875f
+path.conicTo(SkBits2Float(0xc1b7508a), SkBits2Float(0x41260efe), SkBits2Float(0xc1be3618), SkBits2Float(0x40f736ad), SkBits2Float(0x3f811abd));  // -22.9143f, 10.3787f, -23.7764f, 7.72542f, 1.00863f
+path.conicTo(SkBits2Float(0xc1c5564b), SkBits2Float(0x409f7d6d), SkBits2Float(0xc15d0934), SkBits2Float(0xbc880a93), SkBits2Float(0x3f79179a));  // -24.6671f, 4.98406f, -13.8147f, -0.0166066f, 0.973016f
+path.conicTo(SkBits2Float(0xc1c51a48), SkBits2Float(0xc0a2603c), SkBits2Float(0xc1be3618), SkBits2Float(0xc0f736ac), SkBits2Float(0x3f8122f3));  // -24.6378f, -5.07425f, -23.7764f, -7.72542f, 1.00888f
+path.conicTo(SkBits2Float(0xc1b7056f), SkBits2Float(0xc127dd49), SkBits2Float(0xc132a328), SkBits2Float(0xc10215e1), SkBits2Float(0x3f75cbec));  // -22.8777f, -10.4915f, -11.1648f, -8.13034f, 0.960143f
+path.conicTo(SkBits2Float(0xc1879a3b), SkBits2Float(0xc194b151), SkBits2Float(0xc16b1d34), SkBits2Float(0xc1a1cdac), SkBits2Float(0x3f8120d4));  // -16.9503f, -18.5866f, -14.6946f, -20.2254f, 1.00881f
+path.conicTo(SkBits2Float(0xc1471107), SkBits2Float(0xc1aee601), SkBits2Float(0xc08982d1), SkBits2Float(0xc1525939), SkBits2Float(0x3f812d7f));  // -12.4417f, -21.8623f, -4.29722f, -13.1468f, 1.0092f
+path.conicTo(SkBits2Float(0xc03b5543), SkBits2Float(0xc1c80000), SkBits2Float(0x00000000), SkBits2Float(0xc1c80000), SkBits2Float(0x3f74a837));  // -2.92708f, -25, 0, -25, 0.955692f
+path.conicTo(SkBits2Float(0x4032ca93), SkBits2Float(0xc1c80000), SkBits2Float(0x4088418e), SkBits2Float(0xc151ad32), SkBits2Float(0x3f810c2d));  // 2.79361f, -25, 4.258f, -13.1048f, 1.00818f
+path.conicTo(SkBits2Float(0x4088e66c), SkBits2Float(0xc152058a), SkBits2Float(0x40898afc), SkBits2Float(0xc1525d9e), SkBits2Float(0x3f840adb));  // 4.27813f, -13.1264f, 4.29822f, -13.1479f, 1.03158f
+path.conicTo(SkBits2Float(0x41470a56), SkBits2Float(0xc1aee870), SkBits2Float(0x416b1d36), SkBits2Float(0xc1a1cdac), SkBits2Float(0x3f81099f));  // 12.44f, -21.8635f, 14.6946f, -20.2254f, 1.00811f
+path.conicTo(SkBits2Float(0x4187f23a), SkBits2Float(0xc1947162), SkBits2Float(0x4132c174), SkBits2Float(0xc101a9e5), SkBits2Float(0x3f7d6873));  // 16.9933f, -18.5554f, 11.1722f, -8.10398f, 0.989875f
+path.close();
+path.close();
+SkPath one(path);
+path.reset();
+path.setFillType(SkPath::kWinding_FillType);
+path.moveTo(SkBits2Float(0xbfc5c55c), SkBits2Float(0xc0982b46));  // -1.54509f, -4.75528f
+path.conicTo(SkBits2Float(0x41b1d9c2), SkBits2Float(0xc147b0fc), SkBits2Float(0x41be3618), SkBits2Float(0xc0f736b3), SkBits2Float(0x3f3504f3));  // 22.2313f, -12.4807f, 23.7764f, -7.72543f, 0.707107f
+path.conicTo(SkBits2Float(0x41ca926e), SkBits2Float(0xc03e16da), SkBits2Float(0x3fc5c55c), SkBits2Float(0x40982b46), SkBits2Float(0x3f3504f3));  // 25.3215f, -2.97014f, 1.54509f, 4.75528f, 0.707107f
+path.conicTo(SkBits2Float(0xc1b1d9c2), SkBits2Float(0x4147b0fc), SkBits2Float(0xc1be3618), SkBits2Float(0x40f736b3), SkBits2Float(0x3f3504f3));  // -22.2313f, 12.4807f, -23.7764f, 7.72543f, 0.707107f
+path.conicTo(SkBits2Float(0xc1ca926e), SkBits2Float(0x403e16da), SkBits2Float(0xbfc5c55c), SkBits2Float(0xc0982b46), SkBits2Float(0x3f3504f3));  // -25.3215f, 2.97014f, -1.54509f, -4.75528f, 0.707107f
+path.close();
+SkPath two(path);
+SkPath result;
+Op(one, two, kUnion_SkPathOp, &result);
+}
+
+const char ovalsAsQuads[] = "M 146.4187316894531 136.5"
+" Q 146.4187316894531 139.8508911132812 146.4066772460938 143.19775390625"
+" Q 146.3946533203125 146.5446166992188 146.3705749511719 149.8793640136719"
+" Q 146.3465270996094 153.214111328125 146.3104858398438 156.5287170410156"
+" Q 146.2744750976562 159.8433227539062 146.2265930175781 163.1298217773438"
+" Q 146.1786804199219 166.4163208007812 146.1190490722656 169.6668090820312"
+" Q 146.0593872070312 172.9172973632812 145.9881286621094 176.1238708496094"
+" Q 145.9168701171875 179.3304443359375 145.8341674804688 182.4854736328125"
+" Q 145.75146484375 185.6404418945312 145.6575317382812 188.7362670898438"
+" Q 145.5635681152344 191.8320922851562 145.4586181640625 194.8612365722656"
+" Q 145.3536682128906 197.8904113769531 145.2379455566406 200.8456420898438"
+" Q 145.1222229003906 203.8008422851562 144.9960021972656 206.6750183105469"
+" Q 144.8698120117188 209.5491638183594 144.7334289550781 212.3353271484375"
+" Q 144.5970458984375 215.1214599609375 144.4508056640625 217.8129272460938"
+" Q 144.3045654296875 220.50439453125 144.1488342285156 223.0946655273438"
+" Q 143.9931030273438 225.6849365234375 143.8282470703125 228.1677856445312"
+" Q 143.6633911132812 230.650634765625 143.4898071289062 233.0200805664062"
+" Q 143.3162231445312 235.3894958496094 143.1343078613281 237.6398315429688"
+" Q 142.9524230957031 239.89013671875 142.7626647949219 242.0159301757812"
+" Q 142.5729064941406 244.1417236328125 142.375732421875 246.1378173828125"
+" Q 142.1785583496094 248.1339111328125 141.9744262695312 249.99560546875"
+" Q 141.7703247070312 251.8572387695312 141.5597534179688 253.5799255371094"
+" Q 141.3492126464844 255.3026428222656 141.1326904296875 256.8822326660156"
+" Q 140.9161987304688 258.4617919921875 140.6942749023438 259.8945007324219"
+" Q 140.4723510742188 261.3271789550781 140.2455749511719 262.6095275878906"
+" Q 140.0187683105469 263.8918762207031 139.7876281738281 265.020751953125"
+" Q 139.5564880371094 266.1496276855469 139.3215637207031 267.1223449707031"
+" Q 139.086669921875 268.0950622558594 138.8485412597656 268.9092712402344"
+" Q 138.6104125976562 269.7234497070312 138.36962890625 270.3771667480469"
+" Q 138.1288757324219 271.0308837890625 137.8860473632812 271.5225830078125"
+" Q 137.6432189941406 272.0142822265625 137.3988952636719 272.3427124023438"
+" Q 137.1546020507812 272.6711730957031 136.9093933105469 272.8355712890625"
+" Q 136.6642150878906 272.9999694824219 136.4187316894531 273"
+" Q 136.1732482910156 272.9999694824219 135.9280700683594 272.8355712890625"
+" Q 135.682861328125 272.6711730957031 135.4385681152344 272.3427124023438"
+" Q 135.1942443847656 272.0142822265625 134.951416015625 271.5225830078125"
+" Q 134.7085876464844 271.0308837890625 134.4678344726562 270.3771667480469"
+" Q 134.22705078125 269.7234497070312 133.9889221191406 268.9092407226562"
+" Q 133.7507934570312 268.0950622558594 133.5158996582031 267.122314453125"
+" Q 133.2809753417969 266.1495971679688 133.0498352050781 265.020751953125"
+" Q 132.8186950683594 263.8918762207031 132.5918884277344 262.6095275878906"
+" Q 132.3651123046875 261.3271789550781 132.1431884765625 259.8945007324219"
+" Q 131.9212646484375 258.4617919921875 131.7047729492188 256.8822326660156"
+" Q 131.4882507324219 255.3026428222656 131.2777099609375 253.5799560546875"
+" Q 131.067138671875 251.8572387695312 130.863037109375 249.99560546875"
+" Q 130.6589050292969 248.1339111328125 130.4617309570312 246.1378173828125"
+" Q 130.2645568847656 244.1417236328125 130.0747985839844 242.0159301757812"
+" Q 129.8850402832031 239.89013671875 129.7031555175781 237.6398315429688"
+" Q 129.521240234375 235.3894958496094 129.34765625 233.0200805664062"
+" Q 129.174072265625 230.650634765625 129.0092163085938 228.1677856445312"
+" Q 128.8443603515625 225.6849365234375 128.6886291503906 223.0946655273438"
+" Q 128.5328979492188 220.50439453125 128.3866577148438 217.8129272460938"
+" Q 128.2404174804688 215.1214599609375 128.1040344238281 212.3353271484375"
+" Q 127.9676513671875 209.5491333007812 127.8414306640625 206.6749877929688"
+" Q 127.7152404785156 203.8008422851562 127.5995178222656 200.8456420898438"
+" Q 127.4837951660156 197.8904113769531 127.3788452148438 194.8612365722656"
+" Q 127.2738647460938 191.8320922851562 127.179931640625 188.7362670898438"
+" Q 127.0859985351562 185.6404418945312 127.0032958984375 182.4854431152344"
+" Q 126.9205932617188 179.3304443359375 126.8493347167969 176.1238708496094"
+" Q 126.778076171875 172.9172973632812 126.7184143066406 169.6668090820312"
+" Q 126.6587829589844 166.4163208007812 126.6108703613281 163.1298217773438"
+" Q 126.56298828125 159.8433227539062 126.5269775390625 156.5287170410156"
+" Q 126.4909362792969 153.214111328125 126.4668884277344 149.8793640136719"
+" Q 126.4428100585938 146.5446166992188 126.4307861328125 143.19775390625"
+" Q 126.4187316894531 139.8508911132812 126.4187316894531 136.5"
+" Q 126.4187316894531 133.1491088867188 126.4307861328125 129.8022613525391"
+" Q 126.4428100585938 126.4554138183594 126.4668884277344 123.1206665039062"
+" Q 126.4909362792969 119.7859039306641 126.5269775390625 116.4712829589844"
+" Q 126.56298828125 113.1566619873047 126.6108703613281 109.8701629638672"
+" Q 126.6587829589844 106.5836639404297 126.7184143066406 103.3331909179688"
+" Q 126.778076171875 100.0827331542969 126.8493347167969 96.87612915039062"
+" Q 126.9205932617188 93.66952514648438 127.0032958984375 90.5145263671875"
+" Q 127.0859985351562 87.35951232910156 127.179931640625 84.26370239257812"
+" Q 127.2738647460938 81.16787719726562 127.3788452148438 78.13871765136719"
+" Q 127.4837951660156 75.10955810546875 127.5995178222656 72.15434265136719"
+" Q 127.7152404785156 69.19912719726562 127.8414306640625 66.32498168945312"
+" Q 127.9676513671875 63.45082092285156 128.1040344238281 60.66465759277344"
+" Q 128.2404174804688 57.87849426269531 128.3866577148438 55.18704223632812"
+" Q 128.5328979492188 52.49559020996094 128.6886291503906 49.90530395507812"
+" Q 128.8443603515625 47.31504821777344 129.0092163085938 44.83219909667969"
+" Q 129.174072265625 42.349365234375 129.34765625 39.97991943359375"
+" Q 129.521240234375 37.61048889160156 129.7031555175781 35.36016845703125"
+" Q 129.8850402832031 33.10984802246094 130.0747985839844 30.98406982421875"
+" Q 130.2645568847656 28.85829162597656 130.4617309570312 26.86216735839844"
+" Q 130.6589050292969 24.86604309082031 130.863037109375 23.00439453125"
+" Q 131.067138671875 21.14274597167969 131.2777099609375 19.4200439453125"
+" Q 131.4882507324219 17.69734191894531 131.7047729492188 16.11775207519531"
+" Q 131.9212646484375 14.53814697265625 132.1431884765625 13.10545349121094"
+" Q 132.3651123046875 11.67277526855469 132.5918884277344 10.39044189453125"
+" Q 132.8186950683594 9.108108520507812 133.0498352050781 7.979232788085938"
+" Q 133.2809753417969 6.850357055664062 133.5158996582031 5.877639770507812"
+" Q 133.7507934570312 4.904937744140625 133.9889221191406 4.090728759765625"
+" Q 134.22705078125 3.276535034179688 134.4678344726562 2.622810363769531"
+" Q 134.7085876464844 1.969085693359375 134.951416015625 1.477409362792969"
+" Q 135.1942443847656 0.9857254028320312 135.4385681152344 0.65728759765625"
+" Q 135.682861328125 0.3288421630859375 135.9280700683594 0.1644210815429688"
+" Q 136.1732482910156 0 136.4187316894531 0"
+" Q 136.6642150878906 0 136.9093933105469 0.1644210815429688"
+" Q 137.1546020507812 0.3288421630859375 137.3988952636719 0.65728759765625"
+" Q 137.6432189941406 0.9857254028320312 137.8860473632812 1.477409362792969"
+" Q 138.1288757324219 1.969085693359375 138.36962890625 2.622810363769531"
+" Q 138.6104125976562 3.276535034179688 138.8485412597656 4.090728759765625"
+" Q 139.086669921875 4.904937744140625 139.3215637207031 5.877639770507812"
+" Q 139.5564880371094 6.850357055664062 139.7876281738281 7.979232788085938"
+" Q 140.0187683105469 9.108108520507812 140.2455749511719 10.39044189453125"
+" Q 140.4723510742188 11.67277526855469 140.6942749023438 13.10545349121094"
+" Q 140.9161987304688 14.53814697265625 141.1326904296875 16.11775207519531"
+" Q 141.3492126464844 17.69734191894531 141.5597534179688 19.4200439453125"
+" Q 141.7703247070312 21.14274597167969 141.9744262695312 23.00439453125"
+" Q 142.1785583496094 24.86604309082031 142.375732421875 26.86216735839844"
+" Q 142.5729064941406 28.85829162597656 142.7626647949219 30.98406982421875"
+" Q 142.9524230957031 33.10984802246094 143.1343078613281 35.36016845703125"
+" Q 143.3162231445312 37.61048889160156 143.4898071289062 39.97991943359375"
+" Q 143.6633911132812 42.349365234375 143.8282470703125 44.83219909667969"
+" Q 143.9931030273438 47.31504821777344 144.1488342285156 49.90531921386719"
+" Q 144.3045654296875 52.49559020996094 144.4508056640625 55.18704223632812"
+" Q 144.5970458984375 57.87849426269531 144.7334289550781 60.66465759277344"
+" Q 144.8698120117188 63.45082092285156 144.9960021972656 66.32498168945312"
+" Q 145.1222229003906 69.19912719726562 145.2379455566406 72.15434265136719"
+" Q 145.3536682128906 75.10955810546875 145.4586181640625 78.13871765136719"
+" Q 145.5635681152344 81.16787719726562 145.6575317382812 84.26370239257812"
+" Q 145.75146484375 87.35951232910156 145.8341674804688 90.5145263671875"
+" Q 145.9168701171875 93.66952514648438 145.9881286621094 96.87614440917969"
+" Q 146.0593872070312 100.0827331542969 146.1190490722656 103.3332061767578"
+" Q 146.1786804199219 106.5836639404297 146.2265930175781 109.8701629638672"
+" Q 146.2744750976562 113.1566619873047 146.3104858398438 116.4712829589844"
+" Q 146.3465270996094 119.785888671875 146.3705749511719 123.1206665039062"
+" Q 146.3946533203125 126.4554138183594 146.4066772460938 129.8022613525391"
+" Q 146.4187316894531 133.1491088867188 146.4187316894531 136.5 Z";
+
+#include "SkParsePath.h"
+
+DEF_TEST(PathOpsOvalsAsQuads, reporter) {
+    return; // don't execute this for now
+    SkPath path;
+    SkParsePath::FromSVGString(ovalsAsQuads, &path);
+    Simplify(path, &path);
+}
+
+DEF_TEST(PathOps64OvalsAsQuads, reporter) {
+    return; // don't execute this for now
+    SkPath path, result;
+    SkOpBuilder builder;
+    SkParsePath::FromSVGString(ovalsAsQuads, &path);
+    OvalSet set = {{0, 0, 0, 0}, 2, 3, 9, 100, 100};
+    testOvalSet(set, path, &builder, nullptr, &result);
+}
diff --git a/src/third_party/skia/tests/PathOpsBuilderTest.cpp b/src/third_party/skia/tests/PathOpsBuilderTest.cpp
new file mode 100644
index 0000000..ffeff5f
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsBuilderTest.cpp
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "PathOpsExtendedTest.h"
+#include "PathOpsTestCommon.h"
+#include "SkBitmap.h"
+#include "Test.h"
+
+DEF_TEST(PathOpsBuilder, reporter) {
+    SkOpBuilder builder;
+    SkPath result;
+    REPORTER_ASSERT(reporter, builder.resolve(&result));
+    REPORTER_ASSERT(reporter, result.isEmpty());
+
+    builder.add(result, kDifference_SkPathOp);
+    REPORTER_ASSERT(reporter, builder.resolve(&result));
+    REPORTER_ASSERT(reporter, result.isEmpty());
+
+    builder.add(result, kUnion_SkPathOp);
+    REPORTER_ASSERT(reporter, builder.resolve(&result));
+    REPORTER_ASSERT(reporter, result.isEmpty());
+
+    SkPath rectPath;
+    rectPath.setFillType(SkPath::kEvenOdd_FillType);
+    rectPath.addRect(0, 1, 2, 3, SkPath::kCW_Direction);
+    builder.add(rectPath, kUnion_SkPathOp);
+    REPORTER_ASSERT(reporter, builder.resolve(&result));
+    bool closed;
+    SkPath::Direction dir;
+    REPORTER_ASSERT(reporter, result.isRect(nullptr, &closed, &dir));
+    REPORTER_ASSERT(reporter, closed);
+    REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction);
+    int pixelDiff = comparePaths(reporter, __FUNCTION__, rectPath, result);
+    REPORTER_ASSERT(reporter, pixelDiff == 0);
+
+    rectPath.reset();
+    rectPath.setFillType(SkPath::kEvenOdd_FillType);
+    rectPath.addRect(0, 1, 2, 3, SkPath::kCCW_Direction);
+    builder.add(rectPath, kUnion_SkPathOp);
+    REPORTER_ASSERT(reporter, builder.resolve(&result));
+    REPORTER_ASSERT(reporter, result.isRect(nullptr, &closed, &dir));
+    REPORTER_ASSERT(reporter, closed);
+    REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction);
+    REPORTER_ASSERT(reporter, rectPath == result);
+
+    builder.add(rectPath, kDifference_SkPathOp);
+    REPORTER_ASSERT(reporter, builder.resolve(&result));
+    REPORTER_ASSERT(reporter, result.isEmpty());
+
+    SkPath rect2, rect3;
+    rect2.addRect(2, 1, 4, 3, SkPath::kCW_Direction);
+    rect3.addRect(4, 1, 5, 3, SkPath::kCCW_Direction);
+    builder.add(rectPath, kUnion_SkPathOp);
+    builder.add(rect2, kUnion_SkPathOp);
+    builder.add(rect3, kUnion_SkPathOp);
+    REPORTER_ASSERT(reporter, builder.resolve(&result));
+    REPORTER_ASSERT(reporter, result.isRect(nullptr, &closed, &dir));
+    REPORTER_ASSERT(reporter, closed);
+    SkRect expected;
+    expected.set(0, 1, 5, 3);
+    REPORTER_ASSERT(reporter, result.getBounds() == expected);
+
+    SkPath circle1, circle2, circle3;
+    circle1.addCircle(5, 6, 4, SkPath::kCW_Direction);
+    circle2.addCircle(7, 4, 8, SkPath::kCCW_Direction);
+    circle3.addCircle(6, 5, 6, SkPath::kCW_Direction);
+    SkPath opCompare;
+    Op(circle1, circle2, kUnion_SkPathOp, &opCompare);
+    Op(opCompare, circle3, kDifference_SkPathOp, &opCompare);
+    builder.add(circle1, kUnion_SkPathOp);
+    builder.add(circle2, kUnion_SkPathOp);
+    builder.add(circle3, kDifference_SkPathOp);
+    REPORTER_ASSERT(reporter, builder.resolve(&result));
+    pixelDiff = comparePaths(reporter, __FUNCTION__, opCompare, result);
+    REPORTER_ASSERT(reporter, pixelDiff == 0);
+}
+
+DEF_TEST(BuilderIssue3838, reporter) {
+    SkPath path;
+    path.moveTo(200, 170);
+    path.lineTo(220, 170);
+    path.lineTo(220, 230);
+    path.lineTo(240, 230);
+    path.lineTo(240, 210);
+    path.lineTo(180, 210);
+    path.lineTo(180, 190);
+    path.lineTo(260, 190);
+    path.lineTo(260, 250);
+    path.lineTo(200, 250);
+    path.lineTo(200, 170);
+    path.close();
+    SkPath path2;
+    SkOpBuilder builder;
+    builder.add(path, kUnion_SkPathOp);
+    builder.resolve(&path2);
+    int pixelDiff = comparePaths(reporter, __FUNCTION__, path, path2);
+    REPORTER_ASSERT(reporter, pixelDiff == 0);
+}
+
+DEF_TEST(BuilderIssue3838_2, reporter) {
+    SkPath path;
+    path.addCircle(100, 100, 50);
+
+    SkOpBuilder builder;
+    builder.add(path, kUnion_SkPathOp);
+    builder.add(path, kUnion_SkPathOp);
+
+    SkPath result;
+    builder.resolve(&result);
+    int pixelDiff = comparePaths(reporter, __FUNCTION__, path, result);
+    REPORTER_ASSERT(reporter, pixelDiff == 0);
+}
+
+DEF_TEST(BuilderIssue3838_3, reporter) {
+    SkPath path;
+    path.moveTo(40, 10);
+    path.lineTo(60, 10);
+    path.lineTo(60, 30);
+    path.lineTo(40, 30);
+    path.lineTo(40, 10);
+    path.moveTo(41, 11);
+    path.lineTo(41, 29);
+    path.lineTo(59, 29);
+    path.lineTo(59, 11);
+    path.lineTo(41, 11);
+
+    SkOpBuilder builder;
+    builder.add(path, kUnion_SkPathOp);
+    SkPath result;
+    builder.resolve(&result);
+    int pixelDiff = comparePaths(reporter, __FUNCTION__, path, result);
+    REPORTER_ASSERT(reporter, pixelDiff == 0);
+}
+
+DEF_TEST(BuilderIssue502792_2, reporter) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
+    path.addRect(2, 2, 3, 3, SkPath::kCW_Direction);
+    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.addRect(3, 3, 4, 4, SkPath::kCW_Direction);
+    pathB.addRect(3, 3, 4, 4, SkPath::kCW_Direction);
+    SkOpBuilder builder;
+    builder.add(path, kUnion_SkPathOp);
+    builder.add(pathB, kDifference_SkPathOp);
+    SkPath result;
+    builder.resolve(&result);
+}
+
+DEF_TEST(Fuzz846, reporter) {
+/*
+<clipPath id="clip-circle">
+    <circle id="circle" cx="60" cy="60" r="50" />
+</clipPath>
+<clipPath id="clip-rect">
+    <clipPath id="clip-rect">
+        <clipPath id="clip-rect">
+            <clipPath id="clip-rect">
+                <rect x="10" y="30" width="0" height="60" />
+                <rect x="10" y="30" width="0" height="60" />
+                <rect x="10" y="30" width="100" height="60" />
+                <rect x="10" y="30" width="32668" />
+                <rect x="10" y="30" width="100" height="18446744073709551615" />
+                <rect x="10" y="255" width="100" height="60" />
+                <rect width="100" height="60" />
+                <rect x="10" y="30" width="100" height="60" />
+                <rect x="10" y="30" width="100" height="4294967236" />
+                <rect x="10" y="30" width="100" height="60" />
+            </clipPath>
+            <rect x="10" y="30" width="0" height="60" />
+            <rect x="10" y="30" width="0" height="0.18093252719929986369568203" />
+            <rect x="10" y="30" width="100" height="60" />
+            <rect x="10" y="30" width="32668" height="60" />
+            <rect x="10" y="30" width="100" height="18446744073709551615" />
+            <rect x="10" y="255" width="100" height="60" />
+            <rect x="2147483649" y="30" width="100" height="60" />
+            <rect x="10" y="30" width="100" height="60" />
+            <rect x="10" y="30" width="100" height="60" />
+            <rect x="10" y="30" width="100" height="60" />
+        </clipPath>
+        <rect x="10" y="30" width="0" height="60" />
+        <rect x="10" y="30" width="0" height="60" />
+        <rect x="10" y="30" width="100" height="60" />
+        <rect x="10" y="30" width="32668" height="60" />
+        <rect x="10" y="30" width="100" height="18446744073709551615" />
+        <rect x="10" y="255" width="100" height="60" />
+        <rect x="2147483649" y="30" width="100" height="60" />
+        <rect x="10" y="30" width="100" height="60" />
+        <rect x="10" y="2879753595" width="100" height="60" />
+        <rect x="10" y="30" width="100" height="60" />
+    </clipPath>
+    <rect x="10" y="30" width="100" height="60" />
+    <rect x="10" y="30" width="0" height="60" />
+    <rect x="10" y="30" width="100" height="60" />
+    <rect x="10" y="30" width="32668" height="60" />
+    <rect x="10" y="30" width="100" height="18446744073709551615" />
+    <rect x="10" y="255" width="100" height="60" />
+    <rect x="2147483649" y="30" width="100" height="60" />
+    <rect x="10" y="30" width="100" height="60" />
+    <rect x="10" y="30" width="100" height="4294967236" />
+    <rect x="10" y="30" width="100" height="4294967236" />
+    <rect x="10" y="30" width="100" height="4294967236" />
+    <rect x="10" y="30" width="100" height="4294967236" />
+    <rect x="10" y="30" width="100" height="60" />
+    <rect x="757798030" y="30" width="100" height="60" />
+*/
+    SkPath clipCircle, clipRect;
+    SkPath inner;
+    clipCircle.addCircle(60, 60, 50);             // <circle id="circle" cx="60" cy="60" r="50" />
+
+    inner.addRect(10, 30, 10+0, 30+60);           // <rect x="10" y="30" width="0" height="60" />
+    inner.addRect(10, 30, 10+0, 30+60);           // <rect x="10" y="30" width="0" height="60" />
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    inner.addRect(10, 30, 10+32668, 30+0);        // <rect x="10" y="30" width="32668" />
+    inner.addRect(10, 30, 10+100, 30+18446744073709551615.f); // <rect x="10" y="30" width="100" height="18446744073709551615" />
+    inner.addRect(10, 255, 10+100, 255+60);       // <rect x="10" y="255" width="100" height="60" />
+    inner.addRect(0, 0, 0+100, 0+60);             //  <rect width="100" height="60" />
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" />
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    clipRect.addPath(inner);
+    inner.reset();
+    inner.addRect(10, 30, 10+0, 30+60);           // <rect x="10" y="30" width="0" height="60" />
+    inner.addRect(10, 30, 10+0, 30+0.18093252719929986369568203f); // <rect x="10" y="30" width="0" height="0.18093252719929986369568203" />
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    inner.addRect(10, 30, 10+32668, 30+60);       // <rect x="10" y="30" width="32668" height="60" />
+    inner.addRect(10, 30, 10+100, 30+18446744073709551615.f); // <rect x="10" y="30" width="100" height="18446744073709551615" />
+    inner.addRect(10, 255, 10+100, 255+60);       // <rect x="10" y="255" width="100" height="60" />
+    inner.addRect(2147483649.f, 30, 2147483649.f+100, 30+60); // <rect x="2147483649" y="30" width="100" height="60" />
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    clipRect.addPath(inner);
+    inner.reset();
+    inner.addRect(10, 30, 10+0, 30+60);           // <rect x="10" y="30" width="0" height="60" />
+    inner.addRect(10, 30, 10+0, 30+60);           // <rect x="10" y="30" width="0" height="60" />
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    inner.addRect(10, 30, 10+32668, 30+60);       // <rect x="10" y="30" width="32668" height="60" />
+    inner.addRect(10, 30, 10+100, 30+18446744073709551615.f); // <rect x="10" y="30" width="100" height="18446744073709551615" />
+    inner.addRect(10, 255, 10+100, 255+60);       // <rect x="10" y="255" width="100" height="60" />
+    inner.addRect(2147483649.f, 30, 2147483649.f+100, 30+60); // <rect x="2147483649" y="30" width="100" height="60" />
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    inner.addRect(10, 2879753595.f, 10+100, 30+2879753595.f); // <rect x="10" y="2879753595" width="100" height="60" />
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    clipRect.addPath(inner);
+    inner.reset();
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    inner.addRect(10, 30, 10+0, 30+60);           // <rect x="10" y="30" width="0" height="60" />
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    inner.addRect(10, 30, 10+32668, 30+60);       // <rect x="10" y="30" width="32668" height="60" />
+    inner.addRect(10, 30, 10+100, 30+18446744073709551615.f); // <rect x="10" y="30" width="100" height="18446744073709551615" />
+    inner.addRect(10, 255, 10+100, 255+60);       // <rect x="10" y="255" width="100" height="60" />
+    inner.addRect(2147483649.f, 30, 2147483649.f+100, 30+60); // <rect x="2147483649" y="30" width="100" height="60" />
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" />
+    inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" />
+    inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" />
+    inner.addRect(10, 30, 10+100, 30+4294967236.f); // <rect x="10" y="30" width="100" height="4294967236" />
+    inner.addRect(10, 30, 10+100, 30+60);         // <rect x="10" y="30" width="100" height="60" />
+    inner.addRect(757798030.f, 30, 757798030.f+100, 30+60); // <rect x="757798030" y="30" width="100" height="60" />
+    clipRect.addPath(inner);
+
+    SkOpBuilder builder;
+    builder.add(clipCircle, kUnion_SkPathOp);
+    builder.add(clipRect, kDifference_SkPathOp);
+    SkPath result;
+    builder.resolve(&result);
+}
+
+DEF_TEST(Issue569540, reporter) {
+    SkPath path1;
+    path1.moveTo(5, -225);
+    path1.lineTo(-225, 7425);
+    path1.lineTo(7425, 7425);
+    path1.lineTo(7425, -225);
+    path1.lineTo(-225, -225);
+    path1.lineTo(5, -225);
+    path1.close();
+
+    SkPath path2;
+    path2.moveTo(5940, 2790);
+    path2.lineTo(5940, 2160);
+    path2.lineTo(5970, 1980);
+    path2.lineTo(5688, 773669888);
+    path2.lineTo(5688, 2160);
+    path2.lineTo(5688, 2430);
+    path2.lineTo(5400, 4590);
+    path2.lineTo(5220, 4590);
+    path2.lineTo(5220, 4920);
+    path2.cubicTo(5182.22900390625f, 4948.328125f, 5160, 4992.78662109375f, 5160, 5040.00048828125f);
+    path2.lineTo(5940, 2790);
+    path2.close();
+
+    SkOpBuilder builder;
+    builder.add(path1, kUnion_SkPathOp);
+    builder.add(path2, kUnion_SkPathOp);
+    SkPath result;
+    builder.resolve(&result);
+}
+
+DEF_TEST(SkOpBuilderFuzz665, reporter) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+path.moveTo(SkBits2Float(0xcc4264a7), SkBits2Float(0x4bb12e50));  // -5.0959e+07f, 2.32235e+07f
+path.lineTo(SkBits2Float(0xcc4264b0), SkBits2Float(0x4bb12e48));  // -5.0959e+07f, 2.32234e+07f
+path.lineTo(SkBits2Float(0xcc4264a7), SkBits2Float(0x4bb12e50));  // -5.0959e+07f, 2.32235e+07f
+path.close();
+    SkPath path1(path);
+    path.reset();
+    path.setFillType(SkPath::kWinding_FillType);
+path.moveTo(SkBits2Float(0x43213333), SkBits2Float(0x43080000));  // 161.2f, 136
+path.lineTo(SkBits2Float(0x43038000), SkBits2Float(0x43080000));  // 131.5f, 136
+path.cubicTo(SkBits2Float(0x43038000), SkBits2Float(0x42f00000), SkBits2Float(0x42f16666), SkBits2Float(0x42d53333), SkBits2Float(0x42d3cccd), SkBits2Float(0x42cd6666));  // 131.5f, 120, 120.7f, 106.6f, 105.9f, 102.7f
+path.lineTo(SkBits2Float(0x42e33333), SkBits2Float(0x42940000));  // 113.6f, 74
+    SkPath path2(path);
+    SkOpBuilder builder;
+    builder.add(path1, kUnion_SkPathOp);
+    builder.add(path2, kUnion_SkPathOp);
+    SkPath result;
+    builder.resolve(&result);
+}
+
+DEF_TEST(SkOpBuilder618991, reporter) {
+    SkPath path0;
+    path0.moveTo(140, 40);
+    path0.lineTo(200, 210);
+    path0.lineTo(40, 100);
+    path0.lineTo(2.22223e+07f, 2.22222e+14f);
+    path0.lineTo(2.22223e+07f, 2.22222e+14f);
+
+    SkPath path1;
+    path1.moveTo(160, 60);
+    path1.lineTo(220, 230);
+    path1.lineTo(60, 120);
+    path1.lineTo(2.22223e+07f, 2.22222e+14f);
+    path1.lineTo(2.22223e+07f, 2.22222e+14f);
+
+    SkOpBuilder builder;
+    builder.add(path0, SkPathOp::kUnion_SkPathOp);
+    builder.add(path1, SkPathOp::kUnion_SkPathOp);
+    builder.resolve(&path0);
+}
+
+DEF_TEST(SkOpBuilderKFuzz1, reporter) {
+    SkPath path;
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x39008001), SkBits2Float(0xd31fbc1d));  // 0.000122547f, -6.86056e+11f
+path.conicTo(SkBits2Float(0x246a205a), SkBits2Float(0x0080d3fb), SkBits2Float(0xce000001), SkBits2Float(0x04d31fbc), SkBits2Float(0x57a82c00));  // 5.07681e-17f, 1.1831e-38f, -5.36871e+08f, 4.9635e-36f, 3.69814e+14f
+    SkPath path0(path);
+    path.reset();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.cubicTo(SkBits2Float(0x80d3f924), SkBits2Float(0xcecece4f), SkBits2Float(0xcececece), SkBits2Float(0xcececece), SkBits2Float(0x9a9a9ace), SkBits2Float(0x9a9a9a9a));  // -1.94667e-38f, -1.73481e+09f, -1.73483e+09f, -1.73483e+09f, -6.3943e-23f, -6.39427e-23f
+path.moveTo(SkBits2Float(0x9a9a019a), SkBits2Float(0xa59a9a9a));  // -6.36955e-23f, -2.68195e-16f
+    SkPath path1(path);
+SkOpBuilder builder;
+    builder.add(path0, SkPathOp::kUnion_SkPathOp);
+    builder.add(path1, SkPathOp::kUnion_SkPathOp);
+    builder.resolve(&path);
+}
diff --git a/src/third_party/skia/tests/PathOpsChalkboardTest.cpp b/src/third_party/skia/tests/PathOpsChalkboardTest.cpp
new file mode 100644
index 0000000..8c74e7f
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsChalkboardTest.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsExtendedTest.h"
+#include "PathOpsThreadedCommon.h"
+#include "SkRandom.h"
+
+#define TEST(name) { name, #name }
+
+static void chalkboard(skiatest::Reporter* reporter, uint64_t testlines) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+uint64_t i = 0;
+path.moveTo(SkBits2Float(0x4470eed9), SkBits2Float(0x439c1ac1));  // 963.732f, 312.209f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4470dde3), SkBits2Float(0x439c63d8));  // 963.467f, 312.78f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470dbd7), SkBits2Float(0x439c3e57), SkBits2Float(0x4470c893), SkBits2Float(0x439c69fd), SkBits2Float(0x4470cfcf), SkBits2Float(0x439c297a));  // 963.435f, 312.487f, 963.134f, 312.828f, 963.247f, 312.324f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470c46b), SkBits2Float(0x439c8149), SkBits2Float(0x4470b137), SkBits2Float(0x439c2938), SkBits2Float(0x4470b5f4), SkBits2Float(0x439ca99b));  // 963.069f, 313.01f, 962.769f, 312.322f, 962.843f, 313.325f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470e842), SkBits2Float(0x439c8335), SkBits2Float(0x447125a2), SkBits2Float(0x439cce78), SkBits2Float(0x44715a2d), SkBits2Float(0x439c61ed));  // 963.629f, 313.025f, 964.588f, 313.613f, 965.409f, 312.765f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447150d5), SkBits2Float(0x439c945c));  // 965.263f, 313.159f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4471546b), SkBits2Float(0x439c87f2), SkBits2Float(0x4471579e), SkBits2Float(0x439c8085), SkBits2Float(0x44715a8f), SkBits2Float(0x439c7c4c));  // 965.319f, 313.062f, 965.369f, 313.004f, 965.415f, 312.971f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715cbc), SkBits2Float(0x439c79dd));  // 965.449f, 312.952f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715dd3), SkBits2Float(0x439c7918));  // 965.466f, 312.946f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715e56), SkBits2Float(0x439c78d6));  // 965.474f, 312.944f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715e77), SkBits2Float(0x439c78b5));  // 965.476f, 312.943f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715e77), SkBits2Float(0x439c78b5));  // 965.476f, 312.943f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715e87), SkBits2Float(0x439c78b5));  // 965.477f, 312.943f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4471a50e), SkBits2Float(0x439d05c3), SkBits2Float(0x4470fe77), SkBits2Float(0x439bb894), SkBits2Float(0x44710f9e), SkBits2Float(0x439bdb03));  // 966.579f, 314.045f, 963.976f, 311.442f, 964.244f, 311.711f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44710fae), SkBits2Float(0x439bdb24));  // 964.245f, 311.712f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44710fbe), SkBits2Float(0x439bdba7));  // 964.246f, 311.716f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44710fce), SkBits2Float(0x439be397));  // 964.247f, 311.778f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44710eb7), SkBits2Float(0x439bedf5), SkBits2Float(0x44710978), SkBits2Float(0x439bf74d), SkBits2Float(0x447105e2), SkBits2Float(0x439c0064));  // 964.23f, 311.859f, 964.148f, 311.932f, 964.092f, 312.003f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470fe86), SkBits2Float(0x439c1270), SkBits2Float(0x4470fd4f), SkBits2Float(0x439c2250), SkBits2Float(0x44712fde), SkBits2Float(0x439c33d9));  // 963.977f, 312.144f, 963.958f, 312.268f, 964.748f, 312.405f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4470fc48), SkBits2Float(0x439c3271));  // 963.942f, 312.394f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470ee13), SkBits2Float(0x439c4c2b), SkBits2Float(0x4471476b), SkBits2Float(0x439c5c0b), SkBits2Float(0x44711177), SkBits2Float(0x439c7a40));  // 963.72f, 312.595f, 965.116f, 312.719f, 964.273f, 312.955f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44712685), SkBits2Float(0x439c7648));  // 964.602f, 312.924f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447126a6), SkBits2Float(0x439c7d31), SkBits2Float(0x44711d2d), SkBits2Float(0x439c8085), SkBits2Float(0x44711d1d), SkBits2Float(0x439c8790));  // 964.604f, 312.978f, 964.456f, 313.004f, 964.455f, 313.059f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44712675), SkBits2Float(0x439c843c));  // 964.601f, 313.033f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44713bd5), SkBits2Float(0x439c94e0), SkBits2Float(0x44713956), SkBits2Float(0x439ca065), SkBits2Float(0x44712b63), SkBits2Float(0x439cb357));  // 964.935f, 313.163f, 964.896f, 313.253f, 964.678f, 313.401f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44711af0), SkBits2Float(0x439cb5a5), SkBits2Float(0x44712459), SkBits2Float(0x439cab47), SkBits2Float(0x44711fad), SkBits2Float(0x439ca607));  // 964.421f, 313.419f, 964.568f, 313.338f, 964.495f, 313.297f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44710f1a), SkBits2Float(0x439caf3e), SkBits2Float(0x4471325d), SkBits2Float(0x439cbb26), SkBits2Float(0x4471326e), SkBits2Float(0x439cc93a));  // 964.236f, 313.369f, 964.787f, 313.462f, 964.788f, 313.572f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44712428), SkBits2Float(0x439cd501), SkBits2Float(0x44711ad0), SkBits2Float(0x439cca82), SkBits2Float(0x447113b6), SkBits2Float(0x439cc95b));  // 964.565f, 313.664f, 964.419f, 313.582f, 964.308f, 313.573f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44712b95), SkBits2Float(0x439cf20f), SkBits2Float(0x4470f550), SkBits2Float(0x439d0790), SkBits2Float(0x4471426e), SkBits2Float(0x439d21ce));  // 964.681f, 313.891f, 963.833f, 314.059f, 965.038f, 314.264f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44715072), SkBits2Float(0x439d241c), SkBits2Float(0x44715c6a), SkBits2Float(0x439d15a5), SkBits2Float(0x44716364), SkBits2Float(0x439d24c0));  // 965.257f, 314.282f, 965.444f, 314.169f, 965.553f, 314.287f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44717b22), SkBits2Float(0x439d0791), SkBits2Float(0x44715cbc), SkBits2Float(0x439cf231), SkBits2Float(0x4471475c), SkBits2Float(0x439cda20));  // 965.924f, 314.059f, 965.449f, 313.892f, 965.115f, 313.704f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471477d), SkBits2Float(0x439ce12a));  // 965.117f, 313.759f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470fc4a), SkBits2Float(0x439cd14b), SkBits2Float(0x44715810), SkBits2Float(0x439cd0e8), SkBits2Float(0x4471372b), SkBits2Float(0x439cb272));  // 963.942f, 313.635f, 965.376f, 313.632f, 964.862f, 313.394f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447155b2), SkBits2Float(0x439cb91a), SkBits2Float(0x44715581), SkBits2Float(0x439cc72e), SkBits2Float(0x447165f4), SkBits2Float(0x439ccbeb));  // 965.339f, 313.446f, 965.336f, 313.556f, 965.593f, 313.593f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44719e77), SkBits2Float(0x439ca2b4), SkBits2Float(0x44713979), SkBits2Float(0x439c993b), SkBits2Float(0x4471821d), SkBits2Float(0x439c7b47));  // 966.476f, 313.271f, 964.898f, 313.197f, 966.033f, 312.963f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471847b), SkBits2Float(0x439c7dd6));  // 966.07f, 312.983f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44718b96), SkBits2Float(0x439c77b1), SkBits2Float(0x44717d81), SkBits2Float(0x439c6ebb), SkBits2Float(0x44717667), SkBits2Float(0x439c66ab));  // 966.181f, 312.935f, 965.961f, 312.865f, 965.85f, 312.802f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44716cff), SkBits2Float(0x439c6a41), SkBits2Float(0x44716842), SkBits2Float(0x439c7315), SkBits2Float(0x44716159), SkBits2Float(0x439c793a));  // 965.703f, 312.83f, 965.629f, 312.899f, 965.521f, 312.947f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44715a0d), SkBits2Float(0x439c712a), SkBits2Float(0x44713938), SkBits2Float(0x439c6f3e), SkBits2Float(0x44712b34), SkBits2Float(0x439c6d73));  // 965.407f, 312.884f, 964.894f, 312.869f, 964.675f, 312.855f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44714c19), SkBits2Float(0x439c614a), SkBits2Float(0x44711af2), SkBits2Float(0x439c61ee), SkBits2Float(0x44712b34), SkBits2Float(0x439c518c));  // 965.189f, 312.76f, 964.421f, 312.765f, 964.675f, 312.637f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447149ab), SkBits2Float(0x439c499c), SkBits2Float(0x4471474d), SkBits2Float(0x439c5c0b), SkBits2Float(0x447157d0), SkBits2Float(0x439c6065));  // 965.151f, 312.575f, 965.114f, 312.719f, 965.372f, 312.753f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447142b1), SkBits2Float(0x439c4fa0));  // 965.042f, 312.622f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44714053), SkBits2Float(0x439c3f1d), SkBits2Float(0x44716396), SkBits2Float(0x439c3c6d), SkBits2Float(0x447173f9), SkBits2Float(0x439c3292));  // 965.005f, 312.493f, 965.556f, 312.472f, 965.812f, 312.395f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44715c7c), SkBits2Float(0x439c2628), SkBits2Float(0x44716397), SkBits2Float(0x439c3c4c), SkBits2Float(0x447142b1), SkBits2Float(0x439c3398));  // 965.445f, 312.298f, 965.556f, 312.471f, 965.042f, 312.403f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44715572), SkBits2Float(0x439c2919), SkBits2Float(0x44715bd8), SkBits2Float(0x439c10a6), SkBits2Float(0x447159bb), SkBits2Float(0x439bf68a));  // 965.335f, 312.321f, 965.435f, 312.13f, 965.402f, 311.926f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715698), SkBits2Float(0x439be2f4));  // 965.353f, 311.773f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447153f8), SkBits2Float(0x439bd95a));  // 965.312f, 311.698f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471526f), SkBits2Float(0x439bd49e));  // 965.288f, 311.661f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471524e), SkBits2Float(0x439bd45c));  // 965.286f, 311.659f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4471523e), SkBits2Float(0x439bd41a));  // 965.285f, 311.657f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44717148), SkBits2Float(0x439c124f), SkBits2Float(0x44715ae2), SkBits2Float(0x439be562), SkBits2Float(0x447161cb), SkBits2Float(0x439bf335));  // 965.77f, 312.143f, 965.42f, 311.792f, 965.528f, 311.9f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447161bb), SkBits2Float(0x439bf356));  // 965.527f, 311.901f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447161bb), SkBits2Float(0x439bf356));  // 965.527f, 311.901f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44716169), SkBits2Float(0x439bf3b8));  // 965.522f, 311.904f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x447160c5), SkBits2Float(0x439bf47d));  // 965.512f, 311.91f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44715f7d), SkBits2Float(0x439bf627));  // 965.492f, 311.923f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447158f6), SkBits2Float(0x439bfeba), SkBits2Float(0x447152e1), SkBits2Float(0x439c0ac3), SkBits2Float(0x44714e15), SkBits2Float(0x439c1919));  // 965.39f, 311.99f, 965.295f, 312.084f, 965.22f, 312.196f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4471548c), SkBits2Float(0x439c10c7), SkBits2Float(0x447151bb), SkBits2Float(0x439bd7f2), SkBits2Float(0x44715927), SkBits2Float(0x439be271));  // 965.321f, 312.131f, 965.277f, 311.687f, 965.393f, 311.769f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x447156b8), SkBits2Float(0x439bd41b), SkBits2Float(0x44714c19), SkBits2Float(0x439bf356), SkBits2Float(0x44714b13), SkBits2Float(0x439c222f));  // 965.355f, 311.657f, 965.189f, 311.901f, 965.173f, 312.267f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44713dd4), SkBits2Float(0x439c4aa2), SkBits2Float(0x44712ea9), SkBits2Float(0x439c2be9), SkBits2Float(0x44712344), SkBits2Float(0x439c0085));  // 964.966f, 312.583f, 964.729f, 312.343f, 964.551f, 312.004f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44712605), SkBits2Float(0x439c2fa0));  // 964.594f, 312.372f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x44711af3), SkBits2Float(0x439c7e9a), SkBits2Float(0x44710de4), SkBits2Float(0x439bf41b), SkBits2Float(0x4470fb65), SkBits2Float(0x439c20c7));  // 964.421f, 312.989f, 964.217f, 311.907f, 963.928f, 312.256f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4470fbb7), SkBits2Float(0x439c220f));  // 963.933f, 312.266f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470f5e4), SkBits2Float(0x439c2bc9), SkBits2Float(0x4470ef5d), SkBits2Float(0x439c9e59), SkBits2Float(0x4470e50f), SkBits2Float(0x439c85e6));  // 963.842f, 312.342f, 963.74f, 313.237f, 963.579f, 313.046f
+if (testlines & (1LL << i++)) path.cubicTo(SkBits2Float(0x4470e8f6), SkBits2Float(0x439c4e35), SkBits2Float(0x4470ee98), SkBits2Float(0x439c5333), SkBits2Float(0x4470eed9), SkBits2Float(0x439c1ac1));  // 963.64f, 312.611f, 963.728f, 312.65f, 963.732f, 312.209f
+SkASSERT(64 == i);
+path.close();
+
+testSimplify(reporter, path, "chalkboard");
+}
+
+static void testChalkboard(PathOpsThreadState* data) {
+    uint64_t testlines = ((uint64_t) data->fB << 32) | (unsigned int) data->fA;
+    chalkboard(data->fReporter, testlines);
+}
+
+static void chalkboard_threaded(skiatest::Reporter* reporter, const char* filename) {
+#if DEBUG_UNDER_DEVELOPMENT
+    return;
+#endif
+    initializeTests(reporter, "chalkboard");
+    PathOpsThreadedTestRunner testRunner(reporter);
+    SkRandom r;
+    for (int samples = 0; samples <= 64; ++samples) {
+        int testCount;
+        int bitCount = samples < 32 ? samples : 64 - samples;
+        int index1 = 63;
+        int index2 = 62;
+        switch (bitCount) {
+            case 0:
+                testCount = 1;
+                break;
+            case 1:
+                testCount = 64;
+                break;
+            case 2:
+                testCount = reporter->allowExtendedTest() ? 63 * 62 / 2 : 100;
+                break;
+            default:
+                testCount = reporter->allowExtendedTest() ? 10000 : 100;
+                break;
+        }
+        for (int test = 0; test < testCount; ++test) {
+            uint64_t testlines;
+            switch (bitCount) {
+                case 0:
+                    testlines = 0;
+                    break;
+                case 1:
+                    testlines = 1LL << test;
+                    break;
+                case 2:
+                    if (reporter->allowExtendedTest()) {
+                        SkASSERT(index1 >= 1);
+                        SkASSERT(index2 >= 0);
+                        testlines = 1LL << index1;
+                        testlines |= 1LL << index2;
+                        if (--index2 < 0) {
+                            --index1;
+                            index2 = index1 - 1;
+                        }
+                        break;
+                    }
+                default:
+                    testlines = 0;
+                    for (int i = 0; i < bitCount; ++i) {
+                        int bit;
+                        do {
+                            bit = r.nextRangeU(0, 63);
+                        } while (testlines & (1LL << bit));
+                        testlines |= 1LL << bit;
+                    }
+            }
+            if (samples >= 32) {
+                testlines ^= 0xFFFFFFFFFFFFFFFFLL;
+            }
+            *testRunner.fRunnables.append() =
+                    new PathOpsThreadedRunnable(&testChalkboard,
+                                                (int) (unsigned) (testlines & 0xFFFFFFFF),
+                                                (int) (unsigned) (testlines >> 32),
+                                                0, 0, &testRunner);
+        }
+    }
+    testRunner.render();
+}
+
+static void chalkboard_1(skiatest::Reporter* reporter, const char* filename) {
+    uint64_t testlines = 0xFFFFFFFFFFFFFFFFLL;
+    chalkboard(reporter, testlines);
+}
+
+static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
+
+static TestDesc tests[] = {
+    TEST(chalkboard_1),
+    TEST(chalkboard_threaded),
+};
+
+static const size_t testCount = SK_ARRAY_COUNT(tests);
+static bool runReverse = false;
+
+DEF_TEST(PathOpsChalkboard, reporter) {
+    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
+}
diff --git a/src/third_party/skia/tests/PathOpsConicIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsConicIntersectionTest.cpp
new file mode 100644
index 0000000..1de1583
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsConicIntersectionTest.cpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsTestCommon.h"
+#include "SkGeometry.h"
+#include "SkIntersections.h"
+#include "Test.h"
+
+/*
+manually compute the intersection of a pair of circles and see if the conic intersection matches
+  given two circles
+    construct a line connecting their centers
+
+ */
+
+static const ConicPts testSet[] = {
+    {{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
+    {{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
+
+    {{{{5.1114602088928223, 628.77813720703125},
+        {10.834027290344238, 988.964111328125},
+        {163.40835571289062, 988.964111328125}}}, 0.72944212f},
+    {{{{163.40835571289062, 988.964111328125},
+        {5, 988.964111328125},
+        {5, 614.7423095703125}}}, 0.707106769f},
+
+    {{{{11.17222976684570312, -8.103978157043457031},
+        {22.91432571411132812, -10.37866020202636719},
+        {23.7764129638671875, -7.725424289703369141}}}, 1.00862849f},
+    {{{{-1.545085430145263672, -4.755282402038574219},
+        {22.23132705688476562, -12.48070907592773438},
+        {23.7764129638671875, -7.725427150726318359}}}, 0.707106769f},
+
+    {{{{-4,1}, {-4,5}, {0,5}}}, 0.707106769f},
+    {{{{-3,4}, {-3,1}, {0,1}}}, 0.707106769f},
+
+    {{{{0, 0}, {0, 1}, {1, 1}}}, 0.5f},
+    {{{{1, 0}, {0, 0}, {0, 1}}}, 0.5f},
+
+};
+
+const int testSetCount = (int) SK_ARRAY_COUNT(testSet);
+
+static void chopCompare(const SkConic chopped[2], const SkDConic dChopped[2]) {
+    SkASSERT(roughly_equal(chopped[0].fW, dChopped[0].fWeight));
+    SkASSERT(roughly_equal(chopped[1].fW, dChopped[1].fWeight));
+    for (int cIndex = 0; cIndex < 2; ++cIndex) {
+        for (int pIndex = 0; pIndex < 3; ++pIndex) {
+            SkDPoint up;
+            up.set(chopped[cIndex].fPts[pIndex]);
+            SkASSERT(dChopped[cIndex].fPts[pIndex].approximatelyEqual(up));
+        }
+    }
+#if DEBUG_VISUALIZE_CONICS
+    dChopped[0].dump();
+    dChopped[1].dump();
+#endif
+}
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkImageEncoder.h"
+#include "SkPathOpsRect.h"
+#include "SkPaint.h"
+#include "SkString.h"
+
+#define DEBUG_VISUALIZE_CONICS 0
+
+#if DEBUG_VISUALIZE_CONICS
+static void writePng(const SkConic& c, const SkConic ch[2], const char* name) {
+    const int scale = 10;
+    SkConic conic, chopped[2];
+    for (int index = 0; index < 3; ++index) {
+        conic.fPts[index].fX = c.fPts[index].fX * scale;
+        conic.fPts[index].fY = c.fPts[index].fY * scale;
+        for (int chIndex = 0; chIndex < 2; ++chIndex) {
+            chopped[chIndex].fPts[index].fX = ch[chIndex].fPts[index].fX * scale;
+            chopped[chIndex].fPts[index].fY = ch[chIndex].fPts[index].fY * scale;
+        }
+    }
+    conic.fW = c.fW;
+    chopped[0].fW = ch[0].fW;
+    chopped[1].fW = ch[1].fW;
+    SkBitmap bitmap;
+    SkRect bounds;
+    conic.computeTightBounds(&bounds);
+    bounds.outset(10, 10);
+    bitmap.tryAllocPixels(SkImageInfo::MakeN32Premul(
+          SkScalarRoundToInt(bounds.width()), SkScalarRoundToInt(bounds.height())));
+    SkCanvas canvas(bitmap);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setStyle(SkPaint::kStroke_Style);
+    canvas.translate(-bounds.fLeft, -bounds.fTop);
+    canvas.drawColor(SK_ColorWHITE);
+    SkPath path;
+    path.moveTo(conic.fPts[0]);
+    path.conicTo(conic.fPts[1], conic.fPts[2], conic.fW);
+    paint.setARGB(0x80, 0xFF, 0, 0);
+    canvas.drawPath(path, paint);
+    path.reset();
+    path.moveTo(chopped[0].fPts[0]);
+    path.conicTo(chopped[0].fPts[1], chopped[0].fPts[2], chopped[0].fW);
+    path.moveTo(chopped[1].fPts[0]);
+    path.conicTo(chopped[1].fPts[1], chopped[1].fPts[2], chopped[1].fW);
+    paint.setARGB(0x80, 0, 0, 0xFF);
+    canvas.drawPath(path, paint);
+    SkString filename("c:\\Users\\caryclark\\Documents\\");
+    filename.appendf("%s.png", name);
+    sk_tool_utils::EncodeImageToFile(filename.c_str(), bitmap,
+            SkEncodedImageFormat::kPNG, 100);
+}
+
+static void writeDPng(const SkDConic& dC, const char* name) {
+    const int scale = 5;
+    SkDConic dConic = {{{ {dC.fPts[0].fX * scale, dC.fPts[0].fY * scale },
+        {dC.fPts[1].fX * scale, dC.fPts[1].fY * scale },
+        {dC.fPts[2].fX * scale, dC.fPts[2].fY * scale }}}, dC.fWeight };
+    SkBitmap bitmap;
+    SkDRect bounds;
+    bounds.setBounds(dConic);
+    bounds.fLeft -= 10;
+    bounds.fTop -= 10;
+    bounds.fRight += 10;
+    bounds.fBottom += 10;
+    bitmap.tryAllocPixels(SkImageInfo::MakeN32Premul(
+          SkScalarRoundToInt(SkDoubleToScalar(bounds.width())),
+          SkScalarRoundToInt(SkDoubleToScalar(bounds.height()))));
+    SkCanvas canvas(bitmap);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setStyle(SkPaint::kStroke_Style);
+    canvas.translate(SkDoubleToScalar(-bounds.fLeft), SkDoubleToScalar(-bounds.fTop));
+    canvas.drawColor(SK_ColorWHITE);
+    SkPath path;
+    path.moveTo(dConic.fPts[0].asSkPoint());
+    path.conicTo(dConic.fPts[1].asSkPoint(), dConic.fPts[2].asSkPoint(), dConic.fWeight);
+    paint.setARGB(0x80, 0xFF, 0, 0);
+    canvas.drawPath(path, paint);
+    path.reset();
+    const int chops = 2;
+    for (int tIndex = 0; tIndex < chops; ++tIndex) {
+        SkDConic chopped = dConic.subDivide(tIndex / (double) chops,
+                (tIndex + 1) / (double) chops);
+        path.moveTo(chopped.fPts[0].asSkPoint());
+        path.conicTo(chopped.fPts[1].asSkPoint(), chopped.fPts[2].asSkPoint(), chopped.fWeight);
+    }
+    paint.setARGB(0x80, 0, 0, 0xFF);
+    canvas.drawPath(path, paint);
+    SkString filename("c:\\Users\\caryclark\\Documents\\");
+    filename.appendf("%s.png", name);
+    sk_tool_utils::EncodeImageToFile(filename.c_str(), bitmap,
+            SkEncodedImageFormat::kPNG, 100);
+}
+#endif
+
+static void chopBothWays(const SkDConic& dConic, double t, const char* name) {
+    SkConic conic;
+    for (int index = 0; index < 3; ++index) {
+        conic.fPts[index] = dConic.fPts[index].asSkPoint();
+    }
+    conic.fW = dConic.fWeight;
+    SkConic chopped[2];
+    SkDConic dChopped[2];
+    if (!conic.chopAt(SkDoubleToScalar(t), chopped)) {
+        return;
+    }
+    dChopped[0] = dConic.subDivide(0, t);
+    dChopped[1] = dConic.subDivide(t, 1);
+#if DEBUG_VISUALIZE_CONICS
+    dConic.dump();
+#endif
+    chopCompare(chopped, dChopped);
+#if DEBUG_VISUALIZE_CONICS
+    writePng(conic, chopped, name);
+#endif
+}
+
+#if DEBUG_VISUALIZE_CONICS
+const SkDConic frame0[] = {
+{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
+{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
+};
+
+const SkDConic frame1[] = {
+{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
+{{{{306.58801299999999, -227.983994}, {212.46499600000001, -262.24200400000001}, {95.551200899999998, 58.976398500000002}}}, 0.707107008f},
+{{{{377.21899400000001, -141.98100299999999}, {237.77799285476553, -166.56830755921084}, {134.08399674208422, -155.06258330544892}}}, 0.788580656f},
+{{{{134.08399674208422, -155.06258330544892}, {30.390000629402859, -143.55685905168704}, {23.185499199999999, -102.697998}}}, 0.923879623f},
+};
+
+const SkDConic frame2[] = {
+{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
+{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
+{{{{205.78973252799028, -158.12538713371103}, {143.97848953841861, -74.076645245042371}, {95.551200899999998, 58.976398500000002}}}, 0.923879623f},
+{{{{377.21899400000001, -141.98100299999999}, {237.77799285476553, -166.56830755921084}, {134.08399674208422, -155.06258330544892}}}, 0.788580656f},
+};
+
+const SkDConic frame3[] = {
+{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
+{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
+{{{{205.78973252799028, -158.12538713371103}, {143.97848953841861, -74.076645245042371}, {95.551200899999998, 58.976398500000002}}}, 0.923879623f},
+{{{{252.08225670812539, -156.90491625851064}, {185.93099479842493, -160.81544543232982}, {134.08399674208422, -155.06258330544892}}}, 0.835816324f},
+};
+
+const SkDConic frame4[] = {
+{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
+{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
+{{{{205.78973252799028, -158.12538713371103}, {174.88411103320448, -116.10101618937664}, {145.19509369736275, -56.857102571363754}}}, 0.871667147f},
+{{{{252.08225670812539, -156.90491625851064}, {185.93099479842493, -160.81544543232982}, {134.08399674208422, -155.06258330544892}}}, 0.835816324f},
+};
+
+const SkDConic frame5[] = {
+{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
+{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
+{{{{205.78973252799028, -158.12538713371103}, {174.88411103320448, -116.10101618937664}, {145.19509369736275, -56.857102571363754}}}, 0.871667147f},
+{{{{252.08225670812539, -156.90491625851064}, {219.70109133058406, -158.81912754088933}, {190.17095392508796, -158.38373974664466}}}, 0.858306944f},
+};
+
+const SkDConic frame6[] = {
+{{{{306.588013,-227.983994}, {212.464996,-262.242004}, {95.5512009,58.9763985}}}, 0.707107008f},
+{{{{377.218994,-141.981003}, {40.578701,-201.339996}, {23.1854992,-102.697998}}}, 0.707107008f},
+{{{{205.78973252799028, -158.12538713371103}, {190.33692178059735, -137.11320166154385}, {174.87004877564593, -111.2132534799228}}}, 0.858117759f},
+{{{{252.08225670812539, -156.90491625851064}, {219.70109133058406, -158.81912754088933}, {190.17095392508796, -158.38373974664466}}}, 0.858306944f},
+};
+
+const SkDConic* frames[] = {
+    frame0, frame1, frame2, frame3, frame4, frame5, frame6
+};
+
+const int frameSizes[] = { (int) SK_ARRAY_COUNT(frame0), (int) SK_ARRAY_COUNT(frame1),
+        (int) SK_ARRAY_COUNT(frame2), (int) SK_ARRAY_COUNT(frame3),
+        (int) SK_ARRAY_COUNT(frame4), (int) SK_ARRAY_COUNT(frame5),
+        (int) SK_ARRAY_COUNT(frame6),
+};
+
+static void writeFrames() {
+    const int scale = 5;
+
+    for (int index = 0; index < (int) SK_ARRAY_COUNT(frameSizes); ++index) {
+        SkDRect bounds;
+        bool boundsSet = false;
+        int frameSize = frameSizes[index];
+        for (int fIndex = 0; fIndex < frameSize; ++fIndex) {
+            const SkDConic& dC = frames[index][fIndex];
+            SkDConic dConic = {{{ {dC.fPts[0].fX * scale, dC.fPts[0].fY * scale },
+                {dC.fPts[1].fX * scale, dC.fPts[1].fY * scale },
+                {dC.fPts[2].fX * scale, dC.fPts[2].fY * scale }}}, dC.fWeight };
+            SkDRect dBounds;
+            dBounds.setBounds(dConic);
+            if (!boundsSet) {
+                bounds = dBounds;
+                boundsSet = true;
+            } else {
+                bounds.add((SkDPoint&) dBounds.fLeft);
+                bounds.add((SkDPoint&) dBounds.fRight);
+            }
+        }
+        bounds.fLeft -= 10;
+        bounds.fTop -= 10;
+        bounds.fRight += 10;
+        bounds.fBottom += 10;
+        SkBitmap bitmap;
+        bitmap.tryAllocPixels(SkImageInfo::MakeN32Premul(
+              SkScalarRoundToInt(SkDoubleToScalar(bounds.width())),
+              SkScalarRoundToInt(SkDoubleToScalar(bounds.height()))));
+        SkCanvas canvas(bitmap);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+        canvas.translate(SkDoubleToScalar(-bounds.fLeft), SkDoubleToScalar(-bounds.fTop));
+        canvas.drawColor(SK_ColorWHITE);
+        for (int fIndex = 0; fIndex < frameSize; ++fIndex) {
+            const SkDConic& dC = frames[index][fIndex];
+            SkDConic dConic = {{{ {dC.fPts[0].fX * scale, dC.fPts[0].fY * scale },
+                {dC.fPts[1].fX * scale, dC.fPts[1].fY * scale },
+                {dC.fPts[2].fX * scale, dC.fPts[2].fY * scale }}}, dC.fWeight };
+            SkPath path;
+            path.moveTo(dConic.fPts[0].asSkPoint());
+            path.conicTo(dConic.fPts[1].asSkPoint(), dConic.fPts[2].asSkPoint(), dConic.fWeight);
+            if (fIndex < 2) {
+                paint.setARGB(0x80, 0xFF, 0, 0);
+            } else {
+                paint.setARGB(0x80, 0, 0, 0xFF);
+            }
+            canvas.drawPath(path, paint);
+        }
+        SkString filename("c:\\Users\\caryclark\\Documents\\");
+        filename.appendf("f%d.png", index);
+        sk_tool_utils::EncodeImageToFile(filename.c_str(), bitmap, SkEncodedImageFormat::kPNG, 100);
+    }
+}
+#endif
+
+static void oneOff(skiatest::Reporter* reporter, const ConicPts& conic1, const ConicPts& conic2,
+        bool coin) {
+#if DEBUG_VISUALIZE_CONICS
+    writeFrames();
+#endif
+    SkDConic c1, c2;
+    c1.debugSet(conic1.fPts.fPts, conic1.fWeight);
+    c2.debugSet(conic2.fPts.fPts, conic2.fWeight);
+    chopBothWays(c1, 0.5, "c1");
+    chopBothWays(c2, 0.5, "c2");
+#if DEBUG_VISUALIZE_CONICS
+    writeDPng(c1, "d1");
+    writeDPng(c2, "d2");
+#endif
+    SkASSERT(ValidConic(c1));
+    SkASSERT(ValidConic(c2));
+    SkIntersections intersections;
+    intersections.intersect(c1, c2);
+    if (coin && intersections.used() != 2) {
+        SkDebugf("");
+    }
+    REPORTER_ASSERT(reporter, !coin || intersections.used() == 2);
+    double tt1, tt2;
+    SkDPoint xy1, xy2;
+    for (int pt3 = 0; pt3 < intersections.used(); ++pt3) {
+        tt1 = intersections[0][pt3];
+        xy1 = c1.ptAtT(tt1);
+        tt2 = intersections[1][pt3];
+        xy2 = c2.ptAtT(tt2);
+        const SkDPoint& iPt = intersections.pt(pt3);
+        REPORTER_ASSERT(reporter, xy1.approximatelyEqual(iPt));
+        REPORTER_ASSERT(reporter, xy2.approximatelyEqual(iPt));
+        REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
+    }
+    reporter->bumpTestCount();
+}
+
+static void oneOff(skiatest::Reporter* reporter, int outer, int inner) {
+    const ConicPts& c1 = testSet[outer];
+    const ConicPts& c2 = testSet[inner];
+    oneOff(reporter, c1, c2, false);
+}
+
+static void oneOffTests(skiatest::Reporter* reporter) {
+    for (int outer = 0; outer < testSetCount - 1; ++outer) {
+        for (int inner = outer + 1; inner < testSetCount; ++inner) {
+            oneOff(reporter, outer, inner);
+        }
+    }
+}
+
+DEF_TEST(PathOpsConicIntersectionOneOff, reporter) {
+    oneOff(reporter, 0, 1);
+}
+
+DEF_TEST(PathOpsConicIntersection, reporter) {
+    oneOffTests(reporter);
+}
diff --git a/src/third_party/skia/tests/PathOpsConicLineIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsConicLineIntersectionTest.cpp
new file mode 100644
index 0000000..c3d4a2a
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsConicLineIntersectionTest.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsExtendedTest.h"
+#include "PathOpsTestCommon.h"
+#include "SkGeometry.h"
+#include "SkIntersections.h"
+#include "SkPathOpsConic.h"
+#include "SkPathOpsLine.h"
+#include "SkReduceOrder.h"
+#include "Test.h"
+
+static struct lineConic {
+    ConicPts conic;
+    SkDLine line;
+    int result;
+    SkDPoint expected[2];
+} lineConicTests[] = {
+    {
+     {{{{30.6499996,25.6499996}, {30.6499996,20.6499996}, {25.6499996,20.6499996}}}, 0.707107008f},
+      {{{25.6499996,20.6499996}, {45.6500015,20.6499996}}},
+          1,
+       {{25.6499996,20.6499996}, {0,0}}
+    },
+};
+
+static size_t lineConicTests_count = SK_ARRAY_COUNT(lineConicTests);
+
+static int doIntersect(SkIntersections& intersections, const SkDConic& conic, const SkDLine& line,
+                       bool& flipped) {
+    int result;
+    flipped = false;
+    if (line[0].fX == line[1].fX) {
+        double top = line[0].fY;
+        double bottom = line[1].fY;
+        flipped = top > bottom;
+        if (flipped) {
+            SkTSwap<double>(top, bottom);
+        }
+        result = intersections.vertical(conic, top, bottom, line[0].fX, flipped);
+    } else if (line[0].fY == line[1].fY) {
+        double left = line[0].fX;
+        double right = line[1].fX;
+        flipped = left > right;
+        if (flipped) {
+            SkTSwap<double>(left, right);
+        }
+        result = intersections.horizontal(conic, left, right, line[0].fY, flipped);
+    } else {
+        intersections.intersect(conic, line);
+        result = intersections.used();
+    }
+    return result;
+}
+
+static struct oneLineConic {
+    ConicPts conic;
+    SkDLine line;
+} oneOffs[] = {
+    {{{{{30.6499996,25.6499996}, {30.6499996,20.6499996}, {25.6499996,20.6499996}}}, 0.707107008f},
+      {{{25.6499996,20.6499996}, {45.6500015,20.6499996}}}}
+};
+
+static size_t oneOffs_count = SK_ARRAY_COUNT(oneOffs);
+
+static void testOneOffs(skiatest::Reporter* reporter) {
+    bool flipped = false;
+    for (size_t index = 0; index < oneOffs_count; ++index) {
+        const ConicPts& c = oneOffs[index].conic;
+        SkDConic  conic;
+        conic.debugSet(c.fPts.fPts, c.fWeight);
+        SkASSERT(ValidConic(conic));
+        const SkDLine& line = oneOffs[index].line;
+        SkASSERT(ValidLine(line));
+        SkIntersections intersections;
+        int result = doIntersect(intersections, conic, line, flipped);
+        for (int inner = 0; inner < result; ++inner) {
+            double conicT = intersections[0][inner];
+            SkDPoint conicXY = conic.ptAtT(conicT);
+            double lineT = intersections[1][inner];
+            SkDPoint lineXY = line.ptAtT(lineT);
+            if (!conicXY.approximatelyEqual(lineXY)) {
+                conicXY.approximatelyEqual(lineXY);
+                SkDebugf("");
+            }
+            REPORTER_ASSERT(reporter, conicXY.approximatelyEqual(lineXY));
+        }
+    }
+}
+
+DEF_TEST(PathOpsConicLineIntersectionOneOff, reporter) {
+    testOneOffs(reporter);
+}
+
+DEF_TEST(PathOpsConicLineIntersection, reporter) {
+    for (size_t index = 0; index < lineConicTests_count; ++index) {
+        int iIndex = static_cast<int>(index);
+        const ConicPts& c = lineConicTests[index].conic;
+        SkDConic conic;
+        conic.debugSet(c.fPts.fPts, c.fWeight);
+        SkASSERT(ValidConic(conic));
+        const SkDLine& line = lineConicTests[index].line;
+        SkASSERT(ValidLine(line));
+        SkReduceOrder reducer;
+        SkPoint pts[3] = { conic.fPts.fPts[0].asSkPoint(), conic.fPts.fPts[1].asSkPoint(),
+            conic.fPts.fPts[2].asSkPoint() };
+        SkPoint reduced[3];
+        SkConic floatConic;
+        floatConic.set(pts, conic.fWeight);
+        SkPath::Verb order1 = SkReduceOrder::Conic(floatConic, reduced);
+        if (order1 != SkPath::kConic_Verb) {
+            SkDebugf("%s [%d] conic verb=%d\n", __FUNCTION__, iIndex, order1);
+            REPORTER_ASSERT(reporter, 0);
+        }
+        int order2 = reducer.reduce(line);
+        if (order2 < 2) {
+            SkDebugf("%s [%d] line order=%d\n", __FUNCTION__, iIndex, order2);
+            REPORTER_ASSERT(reporter, 0);
+        }
+        SkIntersections intersections;
+        bool flipped = false;
+        int result = doIntersect(intersections, conic, line, flipped);
+        REPORTER_ASSERT(reporter, result == lineConicTests[index].result);
+        if (intersections.used() <= 0) {
+            continue;
+        }
+        for (int pt = 0; pt < result; ++pt) {
+            double tt1 = intersections[0][pt];
+            REPORTER_ASSERT(reporter, tt1 >= 0 && tt1 <= 1);
+            SkDPoint t1 = conic.ptAtT(tt1);
+            double tt2 = intersections[1][pt];
+            REPORTER_ASSERT(reporter, tt2 >= 0 && tt2 <= 1);
+            SkDPoint t2 = line.ptAtT(tt2);
+            if (!t1.approximatelyEqual(t2)) {
+                SkDebugf("%s [%d,%d] x!= t1=%1.9g (%1.9g,%1.9g) t2=%1.9g (%1.9g,%1.9g)\n",
+                    __FUNCTION__, iIndex, pt, tt1, t1.fX, t1.fY, tt2, t2.fX, t2.fY);
+                REPORTER_ASSERT(reporter, 0);
+            }
+            if (!t1.approximatelyEqual(lineConicTests[index].expected[0])
+                    && (lineConicTests[index].result == 1
+                    || !t1.approximatelyEqual(lineConicTests[index].expected[1]))) {
+                SkDebugf("%s t1=(%1.9g,%1.9g)\n", __FUNCTION__, t1.fX, t1.fY);
+                REPORTER_ASSERT(reporter, 0);
+            }
+        }
+    }
+}
diff --git a/src/third_party/skia/tests/PathOpsConicQuadIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsConicQuadIntersectionTest.cpp
new file mode 100644
index 0000000..99411dc
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsConicQuadIntersectionTest.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsTestCommon.h"
+#include "SkIntersections.h"
+#include "SkPathOpsConic.h"
+#include "SkPathOpsQuad.h"
+#include "SkReduceOrder.h"
+#include "Test.h"
+
+static struct conicQuad {
+    ConicPts conic;
+    QuadPts quad;
+} conicQuadTests[] = {
+   {{{{{494.348663,224.583771}, {494.365143,224.633194}, {494.376404,224.684067}}}, 0.998645842f},
+    {{{494.30481,224.474213}, {494.334961,224.538284}, {494.355774,224.605927}}}},
+
+   {{{{{494.348663,224.583771}, {494.365143,224.633194}, {494.376404,224.684067}}}, 0.998645842f},
+    {{{494.355774f, 224.605927f}, {494.363708f, 224.631714f}, {494.370148f, 224.657471f}}}},
+};
+
+static const int conicQuadTests_count = (int) SK_ARRAY_COUNT(conicQuadTests);
+
+static void conicQuadIntersection(skiatest::Reporter* reporter, int index) {
+    const ConicPts& c = conicQuadTests[index].conic;
+    SkDConic conic;
+    conic.debugSet(c.fPts.fPts, c.fWeight);
+    SkASSERT(ValidConic(conic));
+    const QuadPts& q = conicQuadTests[index].quad;
+    SkDQuad quad;
+    quad.debugSet(q.fPts);
+    SkASSERT(ValidQuad(quad));
+    SkReduceOrder reduce1;
+    SkReduceOrder reduce2;
+    int order1 = reduce2.reduce(conic.fPts);
+    int order2 = reduce1.reduce(quad);
+    if (order2 != 3) {
+        SkDebugf("[%d] conic order=%d\n", index, order1);
+        REPORTER_ASSERT(reporter, 0);
+    }
+    if (order1 != 3) {
+        SkDebugf("[%d] quad order=%d\n", index, order2);
+        REPORTER_ASSERT(reporter, 0);
+    }
+    SkIntersections i;
+    int roots = i.intersect(conic, quad);
+    for (int pt = 0; pt < roots; ++pt) {
+        double tt1 = i[0][pt];
+        SkDPoint xy1 = conic.ptAtT(tt1);
+        double tt2 = i[1][pt];
+        SkDPoint xy2 = quad.ptAtT(tt2);
+        if (!xy1.approximatelyEqual(xy2)) {
+            SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+                __FUNCTION__, index, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY);
+        }
+        REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
+    }
+    reporter->bumpTestCount();
+}
+
+DEF_TEST(PathOpsConicQuadIntersection, reporter) {
+    for (int index = 0; index < conicQuadTests_count; ++index) {
+        conicQuadIntersection(reporter, index);
+        reporter->bumpTestCount();
+    }
+}
+
+DEF_TEST(PathOpsConicQuadIntersectionOneOff, reporter) {
+    conicQuadIntersection(reporter, 1);
+}
diff --git a/src/third_party/skia/tests/PathOpsCubicConicIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsCubicConicIntersectionTest.cpp
new file mode 100644
index 0000000..d792463
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsCubicConicIntersectionTest.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsTestCommon.h"
+#include "SkIntersections.h"
+#include "SkPathOpsConic.h"
+#include "SkPathOpsCubic.h"
+#include "SkReduceOrder.h"
+#include "Test.h"
+
+static struct cubicConic {
+    CubicPts cubic;
+    ConicPts conic;
+} cubicConicTests[] = {
+    {{{{188.60000610351562, 2041.5999755859375}, {188.60000610351562, 2065.39990234375},
+        {208, 2084.800048828125}, {231.80000305175781, 2084.800048828125}}},
+    {{{{231.80000305175781, 2084.800048828125}, {188.60000610351562, 2084.800048828125},
+        {188.60000610351562, 2041.5999755859375}}}, 0.707107008f}},
+
+    {{{{231.80000305175781, 2084.800048828125}, {255.60000610351562, 2084.800048828125},
+        {275, 2065.39990234375}, {275, 2041.5999755859375}}},
+    {{{{275, 2041.5999755859375}, {275, 2084.800048828125},
+        {231.80000305175781, 2084.800048828125}}}, 0.707107008f}},
+};
+
+static const int cubicConicTests_count = (int) SK_ARRAY_COUNT(cubicConicTests);
+
+static void cubicConicIntersection(skiatest::Reporter* reporter, int index) {
+    const CubicPts& cu = cubicConicTests[index].cubic;
+    SkDCubic cubic;
+    cubic.debugSet(cu.fPts);
+    SkASSERT(ValidCubic(cubic));
+    const ConicPts& co = cubicConicTests[index].conic;
+    SkDConic conic;
+    conic.debugSet(co.fPts.fPts, co.fWeight);
+    SkASSERT(ValidConic(conic));
+    SkReduceOrder reduce1;
+    SkReduceOrder reduce2;
+    int order1 = reduce1.reduce(cubic, SkReduceOrder::kNo_Quadratics);
+    int order2 = reduce2.reduce(conic.fPts);
+    if (order1 != 4) {
+        SkDebugf("[%d] cubic order=%d\n", index, order1);
+        REPORTER_ASSERT(reporter, 0);
+    }
+    if (order2 != 3) {
+        SkDebugf("[%d] conic order=%d\n", index, order2);
+        REPORTER_ASSERT(reporter, 0);
+    }
+    SkIntersections i;
+    int roots = i.intersect(cubic, conic);
+    for (int pt = 0; pt < roots; ++pt) {
+        double tt1 = i[0][pt];
+        SkDPoint xy1 = cubic.ptAtT(tt1);
+        double tt2 = i[1][pt];
+        SkDPoint xy2 = conic.ptAtT(tt2);
+        if (!xy1.approximatelyEqual(xy2)) {
+            SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+                __FUNCTION__, index, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY);
+        }
+        REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
+    }
+    reporter->bumpTestCount();
+}
+
+DEF_TEST(PathOpsCubicConicIntersection, reporter) {
+    for (int index = 0; index < cubicConicTests_count; ++index) {
+        cubicConicIntersection(reporter, index);
+        reporter->bumpTestCount();
+    }
+}
+
+DEF_TEST(PathOpsCubicConicIntersectionOneOff, reporter) {
+    cubicConicIntersection(reporter, 1);
+}
diff --git a/src/third_party/skia/tests/PathOpsCubicIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsCubicIntersectionTest.cpp
index 443e1ae..6eab71d 100644
--- a/src/third_party/skia/tests/PathOpsCubicIntersectionTest.cpp
+++ b/src/third_party/skia/tests/PathOpsCubicIntersectionTest.cpp
@@ -6,21 +6,27 @@
  */
 #include "PathOpsCubicIntersectionTestData.h"
 #include "PathOpsTestCommon.h"
+#include "SkGeometry.h"
 #include "SkIntersections.h"
 #include "SkPathOpsRect.h"
 #include "SkReduceOrder.h"
 #include "Test.h"
 
+#include <stdlib.h>
+
 const int firstCubicIntersectionTest = 9;
 
 static void standardTestCases(skiatest::Reporter* reporter) {
     for (size_t index = firstCubicIntersectionTest; index < tests_count; ++index) {
         int iIndex = static_cast<int>(index);
-        const SkDCubic& cubic1 = tests[index][0];
-        const SkDCubic& cubic2 = tests[index][1];
+        const CubicPts& cubic1 = tests[index][0];
+        const CubicPts& cubic2 = tests[index][1];
+        SkDCubic c1, c2;
+        c1.debugSet(cubic1.fPts);
+        c2.debugSet(cubic2.fPts);
         SkReduceOrder reduce1, reduce2;
-        int order1 = reduce1.reduce(cubic1, SkReduceOrder::kNo_Quadratics);
-        int order2 = reduce2.reduce(cubic2, SkReduceOrder::kNo_Quadratics);
+        int order1 = reduce1.reduce(c1, SkReduceOrder::kNo_Quadratics);
+        int order2 = reduce2.reduce(c2, SkReduceOrder::kNo_Quadratics);
         const bool showSkipped = false;
         if (order1 < 4) {
             if (showSkipped) {
@@ -35,7 +41,7 @@
             continue;
         }
         SkIntersections tIntersections;
-        tIntersections.intersect(cubic1, cubic2);
+        tIntersections.intersect(c1, c2);
         if (!tIntersections.used()) {
             if (showSkipped) {
                 SkDebugf("%s [%d] no intersection\n", __FUNCTION__, iIndex);
@@ -50,9 +56,9 @@
         }
         for (int pt = 0; pt < tIntersections.used(); ++pt) {
             double tt1 = tIntersections[0][pt];
-            SkDPoint xy1 = cubic1.ptAtT(tt1);
+            SkDPoint xy1 = c1.ptAtT(tt1);
             double tt2 = tIntersections[1][pt];
-            SkDPoint xy2 = cubic2.ptAtT(tt2);
+            SkDPoint xy2 = c2.ptAtT(tt2);
             if (!xy1.approximatelyEqual(xy2)) {
                 SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
                     __FUNCTION__, (int)index, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY);
@@ -63,7 +69,7 @@
     }
 }
 
-static const SkDCubic testSet[] = {
+static const CubicPts testSet[] = {
 // FIXME: uncommenting these two will cause this to fail
 // this results in two curves very nearly but not exactly coincident
 #if 0
@@ -161,7 +167,79 @@
 
 const int testSetCount = (int) SK_ARRAY_COUNT(testSet);
 
-static const SkDCubic newTestSet[] = {
+static const CubicPts newTestSet[] = {
+{ { { 130.0427549999999997, 11417.41309999999976 },{ 130.2331240000000037, 11418.3192999999992 },{ 131.0370790000000056, 11419 },{ 132, 11419 } } },
+{ { { 132, 11419 },{ 130.8954319999999996, 11419 },{ 130, 11418.10449999999946 },{ 130, 11417 } } },
+    
+{{{1,3}, {-1.0564518,1.79032254}, {1.45265341,0.229448318}, {1.45381773,0.22913377}}},
+{{{1.45381773,0.22913377}, {1.45425761,0.229014933}, {1.0967741,0.451612949}, {0,1}}},
+
+{{{1.64551306f, 3.57876182f}, {0.298127174f, 3.70454836f}, {-0.809808373f, 6.39524937f}, {-3.66666651f, 13.333334f}}},
+{{{1, 2}, {1, 2}, {-3.66666651f, 13.333334f}, {5, 6}}},
+
+{{{0.0660428554,1.65340209}, {-0.251940489,1.43560803}, {-0.782382965,-0.196299091}, {3.33333325,-0.666666627}}},
+{{{1,3}, {-1.22353387,1.09411383}, {0.319867611,0.12996155}, {0.886705518,0.107543148}}},
+
+{{{-0.13654758,2.10514426}, {-0.585797966,1.89349782}, {-0.807703257,-0.192306399}, {6,-1}}},
+{{{1,4}, {-2.25000453,1.42241001}, {1.1314013,0.0505309105}, {1.87140274,0.0363764353}}},
+
+{{{1.3127951622009277, 2.0637707710266113}, {1.8210518360137939, 1.9148571491241455}, {1.6106204986572266, -0.68700540065765381}, {8.5, -2.5}}},
+{{{3, 4}, {0.33333325386047363, 1.3333332538604736}, {3.6666667461395264, -0.66666674613952637}, {3.6666665077209473, -0.66666656732559204}}},
+
+{{{980.026001,1481.276}, {980.026001,1481.276}, {980.02594,1481.27576}, {980.025879,1481.27527}}},
+{{{980.025879,1481.27527}, {980.025452,1481.27222}, {980.023743,1481.26038}, {980.02179,1481.24072}}},
+
+{{{1.80943513,3.07782435}, {1.66686702,2.16806936}, {1.68301272,0}, {3,0}}},
+{{{0,1}, {0,3}, {3,2}, {5,2}}},
+
+{{{3.4386673,2.66977954}, {4.06668949,2.17046738}, {4.78887367,1.59629118}, {6,2}}},
+{{{1.71985495,3.49467373}, {2.11620402,2.7201426}, {2.91897964,1.15138781}, {6,3}}},
+
+{{{0,1}, {0.392703831,1.78540766}, {0.219947904,2.05676103}, {0.218561709,2.05630541}}},
+{{{0.218561709,2.05630541}, {0.216418028,2.05560064}, {0.624105453,1.40486407}, {4.16666651,1.00000012}}},
+
+{{{0, 1}, {3, 5}, {2, 1}, {3, 1}}},
+{{{1.01366711f, 2.21379328f}, {1.09074128f, 2.23241305f}, {1.60246587f, 0.451849401f}, {5, 3}}},
+
+{{{0, 1}, {0.541499972f, 3.16599989f}, {1.08299994f, 2.69299984f}, {2.10083938f, 1.80391729f}}},
+{{{0.806384504f, 2.85426903f}, {1.52740121f, 1.99355423f}, {2.81689167f, 0.454222918f}, {5, 1}}},
+
+{{{0, 1}, {1.90192389f, 2.90192389f}, {2.59807634f, 2.79422879f}, {3.1076951f, 2.71539044f}}},
+{{{2, 3}, {2.36602545f, 3.36602545f}, {2.330127f, 3.06217766f}, {2.28460979f, 2.67691422f}}},
+
+{{{0, 1}, {1.90192389f, 2.90192389f}, {2.59807634f, 2.79422879f}, {3.1076951f, 2.71539044f}}},
+{{{2.28460979f, 2.67691422f}, {2.20577145f, 2.00961876f}, {2.09807634f, 1.09807622f}, {4, 3}}},
+
+{{{0, 1}, {0.8211091160774231, 2.0948121547698975}, {0.91805583238601685, 2.515404224395752}, {0.91621249914169312, 2.5146586894989014}}},
+{{{0.91621249914169312, 2.5146586894989014}, {0.91132104396820068, 2.5126807689666748}, {0.21079301834106445, -0.45617169141769409}, {10.5, -1.6666665077209473}}},
+
+{{{42.6237564,68.9841232}, {32.449646,81.963089}, {14.7713947,103.565269}, {12.6310005,105.247002}}},
+{{{37.2640038,95.3540039}, {37.2640038,95.3540039}, {11.3710003,83.7339935}, {-25.0779991,124.912003}}},
+
+{{{0,1}, {4,5}, {6,0}, {1,0}}},
+{{{0,6}, {0,1}, {1,0}, {5,4}}},
+
+{{{0,1}, {4,6}, {5,1}, {6,2}}},
+{{{1,5}, {2,6}, {1,0}, {6,4}}},
+
+{{{322, 896.04803466796875}, {314.09201049804687, 833.4376220703125}, {260.24713134765625, 785}, {195, 785}}},
+{{{195, 785}, {265.14016723632812, 785}, {322, 842.30755615234375}, {322, 913}}},
+
+{{{1, 4}, {4, 5}, {3, 2}, {6, 3}}},
+{{{2, 3}, {3, 6}, {4, 1}, {5, 4}}},
+
+{{{67, 913}, {67, 917.388916015625}, {67.224380493164063, 921.72576904296875}, {67.662384033203125, 926}}},
+{{{194, 1041}, {123.85984039306641, 1041}, {67, 983.69244384765625}, {67, 913}}},
+
+{{{1,4}, {1,5}, {6,0}, {5,1}}},
+{{{0,6}, {1,5}, {4,1}, {5,1}}},
+
+{{{0,1}, {4,5}, {6,0}, {1,0}}},
+{{{0,6}, {0,1}, {1,0}, {5,4}}},
+
+{{{0,1}, {4,6}, {2,0}, {2,0}}},
+{{{0,2}, {0,2}, {1,0}, {6,4}}},
+
 {{{980.9000244140625, 1474.3280029296875}, {980.9000244140625, 1474.3280029296875}, {978.89300537109375, 1471.95703125}, {981.791015625, 1469.487060546875}}},
 {{{981.791015625, 1469.487060546875}, {981.791015625, 1469.4859619140625}, {983.3580322265625, 1472.72900390625}, {980.9000244140625, 1474.3280029296875}}},
 
@@ -306,11 +384,13 @@
 };
 
 const int newTestSetCount = (int) SK_ARRAY_COUNT(newTestSet);
-
-static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const SkDCubic& cubic2,
+static void oneOff(skiatest::Reporter* reporter, const CubicPts& cubic1, const CubicPts& cubic2,
         bool coin) {
-    SkASSERT(ValidCubic(cubic1));
-    SkASSERT(ValidCubic(cubic2));
+    SkDCubic c1, c2;
+    c1.debugSet(cubic1.fPts);
+    c2.debugSet(cubic2.fPts);
+    SkASSERT(ValidCubic(c1));
+    SkASSERT(ValidCubic(c2));
 #if ONE_OFF_DEBUG
     SkDebugf("computed quadratics given\n");
     SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
@@ -320,36 +400,27 @@
         cubic2[0].fX, cubic2[0].fY, cubic2[1].fX, cubic2[1].fY,
         cubic2[2].fX, cubic2[2].fY, cubic2[3].fX, cubic2[3].fY);
 #endif
-    SkTArray<SkDQuad, true> quads1;
-    CubicToQuads(cubic1, cubic1.calcPrecision(), quads1);
-#if ONE_OFF_DEBUG
-    SkDebugf("computed quadratics set 1\n");
-    for (int index = 0; index < quads1.count(); ++index) {
-        const SkDQuad& q = quads1[index];
-        SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY,
-                 q[1].fX, q[1].fY,  q[2].fX, q[2].fY);
-    }
-#endif
-    SkTArray<SkDQuad, true> quads2;
-    CubicToQuads(cubic2, cubic2.calcPrecision(), quads2);
-#if ONE_OFF_DEBUG
-    SkDebugf("computed quadratics set 2\n");
-    for (int index = 0; index < quads2.count(); ++index) {
-        const SkDQuad& q = quads2[index];
-        SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY,
-                 q[1].fX, q[1].fY,  q[2].fX, q[2].fY);
-    }
-#endif
     SkIntersections intersections;
-    intersections.intersect(cubic1, cubic2);
-    REPORTER_ASSERT(reporter, !coin || intersections.used() == 2);
+    intersections.intersect(c1, c2);
+#if DEBUG_T_SECT_DUMP == 3
+    SkDebugf("</div>\n\n");
+    SkDebugf("<script type=\"text/javascript\">\n\n");
+    SkDebugf("var testDivs = [\n");
+    for (int index = 1; index <= gDumpTSectNum; ++index) {
+        SkDebugf("sect%d,\n", index);
+    }
+#endif
+    if (coin && intersections.used() < 2) {
+        SkDebugf("");
+    }
+    REPORTER_ASSERT(reporter, !coin || intersections.used() >= 2);
     double tt1, tt2;
     SkDPoint xy1, xy2;
     for (int pt3 = 0; pt3 < intersections.used(); ++pt3) {
         tt1 = intersections[0][pt3];
-        xy1 = cubic1.ptAtT(tt1);
+        xy1 = c1.ptAtT(tt1);
         tt2 = intersections[1][pt3];
-        xy2 = cubic2.ptAtT(tt2);
+        xy2 = c2.ptAtT(tt2);
         const SkDPoint& iPt = intersections.pt(pt3);
 #if ONE_OFF_DEBUG
         SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n",
@@ -364,14 +435,20 @@
 }
 
 static void oneOff(skiatest::Reporter* reporter, int outer, int inner) {
-    const SkDCubic& cubic1 = testSet[outer];
-    const SkDCubic& cubic2 = testSet[inner];
+    const CubicPts& cubic1 = testSet[outer];
+    const CubicPts& cubic2 = testSet[inner];
     oneOff(reporter, cubic1, cubic2, false);
 }
 
 static void newOneOff(skiatest::Reporter* reporter, int outer, int inner) {
-    const SkDCubic& cubic1 = newTestSet[outer];
-    const SkDCubic& cubic2 = newTestSet[inner];
+    const CubicPts& cubic1 = newTestSet[outer];
+    const CubicPts& cubic2 = newTestSet[inner];
+    oneOff(reporter, cubic1, cubic2, false);
+}
+
+static void testsOneOff(skiatest::Reporter* reporter, int index) {
+    const CubicPts& cubic1 = tests[index][0];
+    const CubicPts& cubic2 = tests[index][1];
     oneOff(reporter, cubic1, cubic2, false);
 }
 
@@ -397,12 +474,12 @@
     unsigned seed = 0;
 #endif
     for (int test = 0; test < tests; ++test) {
-        SkDCubic cubic1, cubic2;
+        CubicPts cubic1, cubic2;
         for (int i = 0; i < 4; ++i) {
-            cubic1[i].fX = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100;
-            cubic1[i].fY = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100;
-            cubic2[i].fX = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100;
-            cubic2[i].fY = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100;
+            cubic1.fPts[i].fX = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100;
+            cubic1.fPts[i].fY = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100;
+            cubic2.fPts[i].fX = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100;
+            cubic2.fPts[i].fY = static_cast<double>(SK_RAND(seed)) / RAND_MAX * 100;
         }
     #if DEBUG_CRASH
         char str[1024];
@@ -415,15 +492,18 @@
                 cubic2[3].fX, cubic2[3].fY);
     #endif
         SkDRect rect1, rect2;
-        rect1.setBounds(cubic1);
-        rect2.setBounds(cubic2);
+        SkDCubic c1, c2;
+        c1.debugSet(cubic1.fPts);
+        c2.debugSet(cubic2.fPts);
+        rect1.setBounds(c1);
+        rect2.setBounds(c2);
         bool boundsIntersect = rect1.fLeft <= rect2.fRight && rect2.fLeft <= rect2.fRight
                 && rect1.fTop <= rect2.fBottom && rect2.fTop <= rect1.fBottom;
         if (test == -1) {
             SkDebugf("ready...\n");
         }
         SkIntersections intersections2;
-        int newIntersects = intersections2.intersect(cubic1, cubic2);
+        int newIntersects = intersections2.intersect(c1, c2);
         if (!boundsIntersect && newIntersects) {
     #if DEBUG_CRASH
             SkDebugf("%s %d unexpected intersection boundsIntersect=%d "
@@ -434,9 +514,9 @@
         }
         for (int pt = 0; pt < intersections2.used(); ++pt) {
             double tt1 = intersections2[0][pt];
-            SkDPoint xy1 = cubic1.ptAtT(tt1);
+            SkDPoint xy1 = c1.ptAtT(tt1);
             double tt2 = intersections2[1][pt];
-            SkDPoint xy2 = cubic2.ptAtT(tt2);
+            SkDPoint xy2 = c2.ptAtT(tt2);
             REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
         }
         reporter->bumpTestCount();
@@ -445,17 +525,20 @@
 
 static void intersectionFinder(int index0, int index1, double t1Seed, double t2Seed,
         double t1Step, double t2Step) {
-    const SkDCubic& cubic1 = newTestSet[index0];
-    const SkDCubic& cubic2 = newTestSet[index1];
+    const CubicPts& cubic1 = newTestSet[index0];
+    const CubicPts& cubic2 = newTestSet[index1];
     SkDPoint t1[3], t2[3];
     bool toggle = true;
+    SkDCubic c1, c2;
+    c1.debugSet(cubic1.fPts);
+    c2.debugSet(cubic2.fPts);
     do {
-        t1[0] = cubic1.ptAtT(t1Seed - t1Step);
-        t1[1] = cubic1.ptAtT(t1Seed);
-        t1[2] = cubic1.ptAtT(t1Seed + t1Step);
-        t2[0] = cubic2.ptAtT(t2Seed - t2Step);
-        t2[1] = cubic2.ptAtT(t2Seed);
-        t2[2] = cubic2.ptAtT(t2Seed + t2Step);
+        t1[0] = c1.ptAtT(t1Seed - t1Step);
+        t1[1] = c1.ptAtT(t1Seed);
+        t1[2] = c1.ptAtT(t1Seed + t1Step);
+        t2[0] = c2.ptAtT(t2Seed - t2Step);
+        t2[1] = c2.ptAtT(t2Seed);
+        t2[2] = c2.ptAtT(t2Seed + t2Step);
         double dist[3][3];
         dist[1][1] = t1[1].distance(t2[1]);
         int best_i = 1, best_j = 1;
@@ -496,38 +579,38 @@
     double t22 = t2Seed + t2Step * 2;
     SkDPoint test;
     while (!approximately_zero(t1Step)) {
-        test = cubic1.ptAtT(t10);
+        test = c1.ptAtT(t10);
         t10 += t1[1].approximatelyEqual(test) ? -t1Step : t1Step;
         t1Step /= 2;
     }
     t1Step = 0.1;
     while (!approximately_zero(t1Step)) {
-        test = cubic1.ptAtT(t12);
+        test = c1.ptAtT(t12);
         t12 -= t1[1].approximatelyEqual(test) ? -t1Step : t1Step;
         t1Step /= 2;
     }
     while (!approximately_zero(t2Step)) {
-        test = cubic2.ptAtT(t20);
+        test = c2.ptAtT(t20);
         t20 += t2[1].approximatelyEqual(test) ? -t2Step : t2Step;
         t2Step /= 2;
     }
     t2Step = 0.1;
     while (!approximately_zero(t2Step)) {
-        test = cubic2.ptAtT(t22);
+        test = c2.ptAtT(t22);
         t22 -= t2[1].approximatelyEqual(test) ? -t2Step : t2Step;
         t2Step /= 2;
     }
 #if ONE_OFF_DEBUG
     SkDebugf("%s t1=(%1.9g<%1.9g<%1.9g) t2=(%1.9g<%1.9g<%1.9g)\n", __FUNCTION__,
         t10, t1Seed, t12, t20, t2Seed, t22);
-    SkDPoint p10 = cubic1.ptAtT(t10);
-    SkDPoint p1Seed = cubic1.ptAtT(t1Seed);
-    SkDPoint p12 = cubic1.ptAtT(t12);
+    SkDPoint p10 = c1.ptAtT(t10);
+    SkDPoint p1Seed = c1.ptAtT(t1Seed);
+    SkDPoint p12 = c1.ptAtT(t12);
     SkDebugf("%s p1=(%1.9g,%1.9g)<(%1.9g,%1.9g)<(%1.9g,%1.9g)\n", __FUNCTION__,
         p10.fX, p10.fY, p1Seed.fX, p1Seed.fY, p12.fX, p12.fY);
-    SkDPoint p20 = cubic2.ptAtT(t20);
-    SkDPoint p2Seed = cubic2.ptAtT(t2Seed);
-    SkDPoint p22 = cubic2.ptAtT(t22);
+    SkDPoint p20 = c2.ptAtT(t20);
+    SkDPoint p2Seed = c2.ptAtT(t2Seed);
+    SkDPoint p22 = c2.ptAtT(t22);
     SkDebugf("%s p2=(%1.9g,%1.9g)<(%1.9g,%1.9g)<(%1.9g,%1.9g)\n", __FUNCTION__,
         p20.fX, p20.fY, p2Seed.fX, p2Seed.fY, p22.fX, p22.fY);
 #endif
@@ -543,7 +626,7 @@
     intersectionFinder(0, 1, 0.865213351, 0.865208087, t1Step, t2Step);
 }
 
-static const SkDCubic selfSet[] = {
+static const CubicPts selfSet[] = {
     {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
     {{{3, 6}, {2, 3}, {4, 0}, {3, 2}}},
     {{{0, 2}, {2, 3}, {5, 1}, {3, 2}}},
@@ -557,38 +640,32 @@
 int selfSetCount = (int) SK_ARRAY_COUNT(selfSet);
 
 static void selfOneOff(skiatest::Reporter* reporter, int index) {
-    const SkDCubic& cubic = selfSet[index];
-#if ONE_OFF_DEBUG
-    int idx2;
-    double max[3];
-    int ts = cubic.findMaxCurvature(max);
-    for (idx2 = 0; idx2 < ts; ++idx2) {
-        SkDebugf("%s max[%d]=%1.9g (%1.9g, %1.9g)\n", __FUNCTION__, idx2,
-                max[idx2], cubic.ptAtT(max[idx2]).fX, cubic.ptAtT(max[idx2]).fY);
+    const CubicPts& cubic = selfSet[index];
+    SkPoint c[4];
+    for (int i = 0; i < 4; ++i) {
+        c[i] = cubic.fPts[i].asSkPoint();
     }
-    SkTArray<double, true> ts1;
-    SkTArray<SkDQuad, true> quads1;
-    cubic.toQuadraticTs(cubic.calcPrecision(), &ts1);
-    for (idx2 = 0; idx2 < ts1.count(); ++idx2) {
-        SkDebugf("%s t[%d]=%1.9g\n", __FUNCTION__, idx2, ts1[idx2]);
+    SkScalar loopT[3];
+    SkCubicType cubicType = SkClassifyCubic(c);
+    int breaks = SkDCubic::ComplexBreak(c, loopT);
+    SkASSERT(breaks < 2);
+    if (breaks && cubicType == SkCubicType::kLoop) {
+        SkIntersections i;
+        SkPoint twoCubics[7];
+        SkChopCubicAt(c, twoCubics, loopT[0]);
+        SkDCubic chopped[2];
+        chopped[0].set(&twoCubics[0]);
+        chopped[1].set(&twoCubics[3]);
+        int result = i.intersect(chopped[0], chopped[1]);
+        REPORTER_ASSERT(reporter, result == 2);
+        REPORTER_ASSERT(reporter, i.used() == 2);
+        for (int index = 0; index < result; ++index) {
+            SkDPoint pt1 = chopped[0].ptAtT(i[0][index]);
+            SkDPoint pt2 = chopped[1].ptAtT(i[1][index]);
+            REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2));
+            reporter->bumpTestCount();
+        }
     }
-    CubicToQuads(cubic, cubic.calcPrecision(), quads1);
-    for (idx2 = 0; idx2 < quads1.count(); ++idx2) {
-        const SkDQuad& q = quads1[idx2];
-        SkDebugf("  {{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}},\n",
-                q[0].fX, q[0].fY,  q[1].fX, q[1].fY,  q[2].fX, q[2].fY);
-    }
-    SkDebugf("\n");
-#endif
-    SkIntersections i;
-    int result = i.intersect(cubic);
-    REPORTER_ASSERT(reporter, result == 1);
-    REPORTER_ASSERT(reporter, i.used() == 1);
-    REPORTER_ASSERT(reporter, !approximately_equal(i[0][0], i[1][0]));
-    SkDPoint pt1 = cubic.ptAtT(i[0][0]);
-    SkDPoint pt2 = cubic.ptAtT(i[1][0]);
-    REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2));
-    reporter->bumpTestCount();
 }
 
 static void cubicIntersectionSelfTest(skiatest::Reporter* reporter) {
@@ -598,7 +675,12 @@
     }
 }
 
-static const SkDCubic coinSet[] = {
+static const CubicPts coinSet[] = {
+    {{{72.350448608398438, 27.966041564941406}, {72.58441162109375, 27.861515045166016},
+        {72.818222045898437, 27.756658554077148}, {73.394996643066406, 27.49799919128418}}},
+    {{{73.394996643066406, 27.49799919128418}, {72.818222045898437, 27.756658554077148},
+        {72.58441162109375, 27.861515045166016}, {72.350448608398438, 27.966041564941406}}},
+
     {{{297.04998779296875, 43.928997039794922}, {297.04998779296875, 43.928997039794922},
         {300.69699096679688, 45.391998291015625}, {306.92498779296875, 43.08599853515625}}},
     {{{297.04998779296875, 43.928997039794922}, {297.04998779296875, 43.928997039794922},
@@ -610,16 +692,13 @@
     {{{317, 711}, {322.52285766601562, 711}, {327, 715.4771728515625}, {327, 721}}},
     {{{324.07107543945312, 713.928955078125}, {324.4051513671875, 714.26300048828125},
             {324.71566772460937, 714.62060546875}, {325, 714.9990234375}}},
-
-    {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
-    {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
 };
 
 static int coinSetCount = (int) SK_ARRAY_COUNT(coinSet);
 
 static void coinOneOff(skiatest::Reporter* reporter, int index) {
-    const SkDCubic& cubic1 = coinSet[index];
-    const SkDCubic& cubic2 = coinSet[index + 1];
+    const CubicPts& cubic1 = coinSet[index];
+    const CubicPts& cubic2 = coinSet[index + 1];
     oneOff(reporter, cubic1, cubic2, true);
 }
 
@@ -638,6 +717,10 @@
     newOneOff(reporter, 0, 1);
 }
 
+DEF_TEST(PathOpsCubicIntersectionTestsOneOff, reporter) {
+    testsOneOff(reporter, 10);
+}
+
 DEF_TEST(PathOpsCubicSelfOneOff, reporter) {
     selfOneOff(reporter, 0);
 }
diff --git a/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.cpp b/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.cpp
index 31056d2..58dbfde 100644
--- a/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.cpp
+++ b/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.cpp
@@ -14,7 +14,7 @@
 static const double N = -FLT_EPSILON / 2;
 static const double M = -FLT_EPSILON / 3;
 
-const SkDCubic pointDegenerates[] = {
+const CubicPts pointDegenerates[] = {
     {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}},
     {{{1, 1}, {1, 1}, {1, 1}, {1, 1}}},
     {{{1 + FLT_EPSILON_HALF, 1}, {1, 1 + FLT_EPSILON_HALF}, {1, 1}, {1, 1}}},
@@ -45,16 +45,16 @@
 
 const size_t pointDegenerates_count = SK_ARRAY_COUNT(pointDegenerates);
 
-const SkDCubic notPointDegenerates[] = {
-    {{{1 + FLT_EPSILON * 2, 1}, {1, FLT_EPSILON * 2}, {1, 1}, {1, 1}}},
-    {{{1 + FLT_EPSILON * 2, 1}, {1 - FLT_EPSILON * 2, 1}, {1, 1}, {1, 1}}}
+const CubicPts notPointDegenerates[] = {
+    {{{1 + FLT_EPSILON * 8, 1}, {1, FLT_EPSILON * 8}, {1, 1}, {1, 1}}},
+    {{{1 + FLT_EPSILON * 8, 1}, {1 - FLT_EPSILON * 8, 1}, {1, 1}, {1, 1}}}
 };
 
 const size_t notPointDegenerates_count =
         SK_ARRAY_COUNT(notPointDegenerates);
 
 // from http://www.truetex.com/bezint.htm
-const SkDCubic tests[][2] = {
+const CubicPts tests[][2] = {
     {  // intersects in one place (data gives bezier clip fits
      {{{0, 45},
       {6.0094158284751593, 51.610357411322688},
@@ -109,7 +109,7 @@
 
 const size_t tests_count = SK_ARRAY_COUNT(tests);
 
-const SkDCubic lines[] = {
+const CubicPts lines[] = {
     {{{0, 0}, {0, 0}, {0, 0}, {1, 0}}},  // 0: horizontal
     {{{1, 0}, {0, 0}, {0, 0}, {0, 0}}},
     {{{1, 0}, {2, 0}, {3, 0}, {4, 0}}},
@@ -145,7 +145,7 @@
 const size_t lines_count = SK_ARRAY_COUNT(lines);
 
 // 'not a line' tries to fool the line detection code
-const SkDCubic notLines[] = {
+const CubicPts notLines[] = {
     {{{0, 0}, {0, 0}, {0, 1}, {1, 0}}},
     {{{0, 0}, {0, 1}, {0, 0}, {1, 0}}},
     {{{0, 0}, {0, 1}, {1, 0}, {0, 0}}},
@@ -156,10 +156,10 @@
 
 const size_t notLines_count = SK_ARRAY_COUNT(notLines);
 
-static const double E = FLT_EPSILON * 2;
-static const double F = FLT_EPSILON * 3;
+static const double E = FLT_EPSILON * 8;
+static const double F = FLT_EPSILON * 8;
 
-const SkDCubic modEpsilonLines[] = {
+const CubicPts modEpsilonLines[] = {
     {{{0, E}, {0, 0}, {0, 0}, {1, 0}}},  // horizontal
     {{{0, 0}, {0, E}, {1, 0}, {0, 0}}},
     {{{0, 0}, {1, 0}, {0, E}, {0, 0}}},
@@ -187,8 +187,8 @@
     {{{1, 1}, {2, 2}, {2, 2+E}, {1, 1}}},
     {{{1, 1}, {1, 1+E}, {3, 3}, {3, 3}}},  // first-middle middle-last coincident
     {{{1, 1}, {2+E, 2}, {3, 3}, {4, 4}}},  // no coincident
-    {{{1, 1}, {3, 3}, {2, 2}, {4, 4+F}}},  // INVESTIGATE: why the epsilon is bigger
-    {{{1, 1+F}, {2, 2}, {4, 4}, {3, 3}}},  // INVESTIGATE: why the epsilon is bigger
+    {{{1, 1}, {3, 3}, {2, 2}, {4, 4+F+F}}},  // INVESTIGATE: why the epsilon is bigger
+    {{{1, 1+F+F}, {2, 2}, {4, 4}, {3, 3}}},  // INVESTIGATE: why the epsilon is bigger
     {{{1, 1}, {3, 3}, {4, 4+E}, {2, 2}}},
     {{{1, 1}, {4, 4}, {2, 2}, {3, 3+E}}},
     {{{1, 1}, {4, 4}, {3, 3}, {2+E, 2}}},
@@ -202,7 +202,7 @@
 
 const size_t modEpsilonLines_count = SK_ARRAY_COUNT(modEpsilonLines);
 
-const SkDCubic lessEpsilonLines[] = {
+const CubicPts lessEpsilonLines[] = {
     {{{0, D}, {0, 0}, {0, 0}, {1, 0}}},  // horizontal
     {{{1, 0}, {0, 0}, {0, 0}, {0, D}}},
     {{{1, D}, {2, 0}, {3, 0}, {4, 0}}},
@@ -238,7 +238,7 @@
 
 const size_t lessEpsilonLines_count = SK_ARRAY_COUNT(lessEpsilonLines);
 
-const SkDCubic negEpsilonLines[] = {
+const CubicPts negEpsilonLines[] = {
     {{{0, N}, {0, 0}, {0, 0}, {1, 0}}},  // horizontal
     {{{1, 0}, {0, 0}, {0, 0}, {0, N}}},
     {{{1, N}, {2, 0}, {3, 0}, {4, 0}}},
diff --git a/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.h b/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.h
index e37d571..4c9894c 100644
--- a/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.h
+++ b/src/third_party/skia/tests/PathOpsCubicIntersectionTestData.h
@@ -4,18 +4,18 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#include "SkPathOpsCubic.h"
+#include "PathOpsTestCommon.h"
 
-extern const SkDCubic pointDegenerates[];
-extern const SkDCubic notPointDegenerates[];
-extern const SkDCubic tests[][2];
-extern SkDCubic hexTests[][2];
+extern const CubicPts pointDegenerates[];
+extern const CubicPts notPointDegenerates[];
+extern const CubicPts tests[][2];
+extern CubicPts hexTests[][2];
 
-extern const SkDCubic lines[];
-extern const SkDCubic notLines[];
-extern const SkDCubic modEpsilonLines[];
-extern const SkDCubic lessEpsilonLines[];
-extern const SkDCubic negEpsilonLines[];
+extern const CubicPts lines[];
+extern const CubicPts notLines[];
+extern const CubicPts modEpsilonLines[];
+extern const CubicPts lessEpsilonLines[];
+extern const CubicPts negEpsilonLines[];
 
 extern const size_t pointDegenerates_count;
 extern const size_t notPointDegenerates_count;
diff --git a/src/third_party/skia/tests/PathOpsCubicLineIntersectionIdeas.cpp b/src/third_party/skia/tests/PathOpsCubicLineIntersectionIdeas.cpp
index 6aec8b1..1555475 100644
--- a/src/third_party/skia/tests/PathOpsCubicLineIntersectionIdeas.cpp
+++ b/src/third_party/skia/tests/PathOpsCubicLineIntersectionIdeas.cpp
@@ -16,7 +16,7 @@
 static bool gPathOpsCubicLineIntersectionIdeasVerbose = false;
 
 static struct CubicLineFailures {
-    SkDCubic c;
+    CubicPts c;
     double t;
     SkDPoint p;
 } cubicLineFailures[] = {
@@ -145,13 +145,15 @@
     double largestR2 = 0;
     for (int index = 0; index < 1000000000; ++index) {
         SkDPoint origin = {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)};
-        SkDCubic cubic = {{origin,
+        CubicPts cuPts = {{origin,
                 {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
                 {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
                 {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}
         }};
         // construct a line at a known intersection
         double t = ran.nextRangeF(0, 1);
+        SkDCubic cubic;
+        cubic.debugSet(cuPts.fPts);
         SkDPoint pt = cubic.ptAtT(t);
         // skip answers with no intersections (although note the bug!) or two, or more
         // see if the line / cubic has a fun range of roots
@@ -194,13 +196,13 @@
             if (realRoots == 3) {
                 smallest = SkTMin(smallest, allRoots[2]);
             }
-            SK_ALWAYSBREAK(smallest < 0);
-            SK_ALWAYSBREAK(smallest >= -1);
+            SkASSERT_RELEASE(smallest < 0);
+            SkASSERT_RELEASE(smallest >= -1);
             largeBits = 0;
         } else {
             frexp(largest, &largeBits);
-            SK_ALWAYSBREAK(largeBits >= 0);
-            SK_ALWAYSBREAK(largeBits < 256);
+            SkASSERT_RELEASE(largeBits >= 0);
+            SkASSERT_RELEASE(largeBits < 256);
         }
         double step = 1e-6;
         if (largeBits > 21) {
@@ -222,7 +224,7 @@
                 break;
             }
             step *= 1.5;
-            SK_ALWAYSBREAK(step < 1);
+            SkASSERT_RELEASE(step < 1);
         } while (true);
         worstStep[largeBits] = SkTMax(worstStep[largeBits], diff);
 #if 0
@@ -248,7 +250,9 @@
 }
 
 static double testOneFailure(const CubicLineFailures& failure) {
-    const SkDCubic& cubic = failure.c;
+    const CubicPts& c = failure.c;
+    SkDCubic cubic;
+    cubic.debugSet(c.fPts);
     const SkDPoint& pt = failure.p;
     double A, B, C, D;
     SkDCubic::Coefficients(&cubic[0].fY, &A, &B, &C, &D);
@@ -256,11 +260,11 @@
     double allRoots[3] = {0}, validRoots[3] = {0};
     int realRoots = SkDCubic::RootsReal(A, B, C, D, allRoots);
     int valid = SkDQuad::AddValidTs(allRoots, realRoots, validRoots);
-    SK_ALWAYSBREAK(valid == 1);
-    SK_ALWAYSBREAK(realRoots != 1);
+    SkASSERT_RELEASE(valid == 1);
+    SkASSERT_RELEASE(realRoots != 1);
     double t = validRoots[0];
     SkDPoint calcPt = cubic.ptAtT(t);
-    SK_ALWAYSBREAK(!calcPt.approximatelyEqual(pt));
+    SkASSERT_RELEASE(!calcPt.approximatelyEqual(pt));
     int iters = 0;
     double newT = binary_search(cubic, 0.1, pt, t, &iters);
     return newT;
@@ -271,7 +275,7 @@
     for (int index = 0; index < cubicLineFailuresCount; ++index) {
         const CubicLineFailures& failure = cubicLineFailures[index];
         double newT = testOneFailure(failure);
-        SK_ALWAYSBREAK(newT >= 0);
+        SkASSERT_RELEASE(newT >= 0);
     }
 }
 
@@ -279,5 +283,5 @@
     return;  // disable for now
     const CubicLineFailures& failure = cubicLineFailures[1];
     double newT = testOneFailure(failure);
-    SK_ALWAYSBREAK(newT >= 0);
+    SkASSERT_RELEASE(newT >= 0);
 }
diff --git a/src/third_party/skia/tests/PathOpsCubicLineIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsCubicLineIntersectionTest.cpp
index 234a538..a6ae5e6 100644
--- a/src/third_party/skia/tests/PathOpsCubicLineIntersectionTest.cpp
+++ b/src/third_party/skia/tests/PathOpsCubicLineIntersectionTest.cpp
@@ -12,7 +12,7 @@
 #include "Test.h"
 
 struct lineCubic {
-    SkDCubic cubic;
+    CubicPts cubic;
     SkDLine line;
 };
 
@@ -25,7 +25,9 @@
 static const size_t failLineCubicTests_count = SK_ARRAY_COUNT(failLineCubicTests);
 
 static void testFail(skiatest::Reporter* reporter, int iIndex) {
-    const SkDCubic& cubic = failLineCubicTests[iIndex].cubic;
+    const CubicPts& cuPts = failLineCubicTests[iIndex].cubic;
+    SkDCubic cubic;
+    cubic.debugSet(cuPts.fPts);
     SkASSERT(ValidCubic(cubic));
     const SkDLine& line = failLineCubicTests[iIndex].line;
     SkASSERT(ValidLine(line));
@@ -49,6 +51,12 @@
 }
 
 static lineCubic lineCubicTests[] = {
+    {{{{0, 6}, {1.0851458311080933, 4.3722810745239258}, {1.5815209150314331, 3.038947582244873}, {1.9683018922805786, 1.9999997615814209}}},
+     {{{3,2}, {1,2}}}},
+
+    {{{{0.468027353,4}, {1.06734705,1.33333337}, {1.36700678,0}, {3,0}}},
+    {{{2,1}, {0,1}}}},
+
     {{{{-634.60540771484375, -481.262939453125}, {266.2696533203125, -752.70867919921875},
             {-751.8370361328125, -317.37921142578125}, {-969.7427978515625, 824.7255859375}}},
             {{{-287.9506133720805678, -557.1376476615772617},
@@ -117,7 +125,9 @@
 }
 
 static void testOne(skiatest::Reporter* reporter, int iIndex) {
-    const SkDCubic& cubic = lineCubicTests[iIndex].cubic;
+    const CubicPts& cuPts = lineCubicTests[iIndex].cubic;
+    SkDCubic cubic;
+    cubic.debugSet(cuPts.fPts);
     SkASSERT(ValidCubic(cubic));
     const SkDLine& line = lineCubicTests[iIndex].line;
     SkASSERT(ValidLine(line));
@@ -151,8 +161,6 @@
         double cubicT = i[0][0];
         SkDPoint prev = cubic.ptAtT(cubicT * 2 - 1);
         SkDPoint sect = cubic.ptAtT(cubicT);
-        double left[3] = { line.isLeft(prev), line.isLeft(sect), line.isLeft(cubic[3]) };
-        SkDebugf("cubic=(%1.9g, %1.9g, %1.9g)\n", left[0], left[1], left[2]);
         SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", prev.fX, prev.fY, sect.fX, sect.fY);
         SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", sect.fX, sect.fY, cubic[3].fX, cubic[3].fY);
         SkDPoint prevL = line.ptAtT(i[1][0] - 0.0000007);
@@ -184,7 +192,9 @@
 DEF_TEST(PathOpsCubicLineIntersectionOneOff, reporter) {
     int iIndex = 0;
     testOne(reporter, iIndex);
-    const SkDCubic& cubic = lineCubicTests[iIndex].cubic;
+    const CubicPts& cuPts = lineCubicTests[iIndex].cubic;
+    SkDCubic cubic;
+    cubic.debugSet(cuPts.fPts);
     const SkDLine& line = lineCubicTests[iIndex].line;
     SkIntersections i;
     i.intersect(cubic, line);
diff --git a/src/third_party/skia/tests/PathOpsCubicQuadIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsCubicQuadIntersectionTest.cpp
index 967dfc7..87c7e13 100644
--- a/src/third_party/skia/tests/PathOpsCubicQuadIntersectionTest.cpp
+++ b/src/third_party/skia/tests/PathOpsCubicQuadIntersectionTest.cpp
@@ -13,55 +13,53 @@
 #include "Test.h"
 
 static struct quadCubic {
-    SkDCubic cubic;
-    SkDQuad quad;
-    int answerCount;
-    SkDPoint answers[2];
+    CubicPts cubic;
+    QuadPts quad;
 } quadCubicTests[] = {
-#if 0  // FIXME : this should not fail (root problem behind skpcarrot_is24 )
-    {{{{1020.08099,672.161987}, {1020.08002,630.73999}, {986.502014,597.161987}, {945.080994,597.161987}}},
-     {{{1020,672}, {1020,640.93396}, {998.03302,618.96698}}}, 1,
-      {{1019.421, 662.449}}},
-#endif
+    {{{{945.08099365234375, 747.1619873046875}, {982.5679931640625, 747.1619873046875}, {1013.6290283203125, 719.656005859375}, {1019.1910400390625, 683.72601318359375}}},
+     {{{945, 747}, {976.0660400390625, 747}, {998.03302001953125, 725.03302001953125}}}},
 
     {{{{778, 14089}, {778, 14091.208984375}, {776.20916748046875, 14093}, {774, 14093}}},
-     {{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}, 2,
-     {{778, 14089}, {776.82855609581270,14091.828250841330}}},
+     {{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}},
+
+    {{{{1020.08099,672.161987}, {1020.08002,630.73999}, {986.502014,597.161987}, {945.080994,597.161987}}},
+     {{{1020,672}, {1020,640.93396}, {998.03302,618.96698}}}},
+
+    {{{{778, 14089}, {778, 14091.208984375}, {776.20916748046875, 14093}, {774, 14093}}},
+     {{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}},
 
     {{{{1110, 817}, {1110.55225f, 817}, {1111, 817.447693f}, {1111, 818}}},
-     {{{1110.70715f, 817.292908f}, {1110.41406f, 817.000122f}, {1110, 817}}}, 2,
-      {{1110, 817}, {1110.70715f, 817.292908f}}},
+     {{{1110.70715f, 817.292908f}, {1110.41406f, 817.000122f}, {1110, 817}}}},
 
     {{{{1110, 817}, {1110.55225f, 817}, {1111, 817.447693f}, {1111, 818}}},
-     {{{1111, 818}, {1110.99988f, 817.585876f}, {1110.70715f, 817.292908f}}}, 2,
-      {{1110.70715f, 817.292908f}, {1111, 818}}},
+     {{{1111, 818}, {1110.99988f, 817.585876f}, {1110.70715f, 817.292908f}}}},
 
     {{{{55, 207}, {52.238574981689453, 207}, {50, 204.76142883300781}, {50, 202}}},
      {{{55, 207}, {52.929431915283203, 206.99949645996094},
-       {51.464466094970703, 205.53553771972656}}}, 2,
-      {{55, 207}, {51.464466094970703, 205.53553771972656}}},
+       {51.464466094970703, 205.53553771972656}}}},
 
     {{{{49, 47}, {49, 74.614250183105469}, {26.614250183105469, 97}, {-1, 97}}},
      {{{-8.659739592076221e-015, 96.991401672363281}, {20.065492630004883, 96.645187377929688},
-       {34.355339050292969, 82.355339050292969}}}, 2,
-      {{34.355339050292969,82.355339050292969}, {34.28654835573549, 82.424006509351585}}},
+       {34.355339050292969, 82.355339050292969}}}},
 
     {{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}},
-     {{{18,226}, {14.686291694641113,226}, {12.342399597167969,228.3424072265625}}}, 1,
-      {{18,226}, {0,0}}},
+     {{{18,226}, {14.686291694641113,226}, {12.342399597167969,228.3424072265625}}}},
 
     {{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}},
-     {{{12.342399597167969,228.3424072265625}, {10,230.68629455566406}, {10,234}}}, 1,
-      {{10,234}, {0,0}}},
+     {{{12.342399597167969,228.3424072265625}, {10,230.68629455566406}, {10,234}}}},
 };
 
 static const int quadCubicTests_count = (int) SK_ARRAY_COUNT(quadCubicTests);
 
 static void cubicQuadIntersection(skiatest::Reporter* reporter, int index) {
     int iIndex = static_cast<int>(index);
-    const SkDCubic& cubic = quadCubicTests[index].cubic;
+    const CubicPts& c = quadCubicTests[index].cubic;
+    SkDCubic cubic;
+    cubic.debugSet(c.fPts);
     SkASSERT(ValidCubic(cubic));
-    const SkDQuad& quad = quadCubicTests[index].quad;
+    const QuadPts& q = quadCubicTests[index].quad;
+    SkDQuad quad;
+    quad.debugSet(q.fPts);
     SkASSERT(ValidQuad(quad));
     SkReduceOrder reduce1;
     SkReduceOrder reduce2;
@@ -77,7 +75,6 @@
     }
     SkIntersections i;
     int roots = i.intersect(cubic, quad);
-    SkASSERT(roots == quadCubicTests[index].answerCount);
     for (int pt = 0; pt < roots; ++pt) {
         double tt1 = i[0][pt];
         SkDPoint xy1 = cubic.ptAtT(tt1);
@@ -88,15 +85,6 @@
                 __FUNCTION__, iIndex, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY);
         }
         REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
-        bool found = false;
-        for (int idx2 = 0; idx2 < quadCubicTests[index].answerCount; ++idx2) {
-            found |= quadCubicTests[index].answers[idx2].approximatelyEqual(xy1);
-        }
-        if (!found) {
-            SkDebugf("%s [%d,%d] xy1=(%g,%g) != \n",
-                __FUNCTION__, iIndex, pt, xy1.fX, xy1.fY);
-        }
-        REPORTER_ASSERT(reporter, found);
     }
     reporter->bumpTestCount();
 }
@@ -111,195 +99,3 @@
 DEF_TEST(PathOpsCubicQuadIntersectionOneOff, reporter) {
     cubicQuadIntersection(reporter, 0);
 }
-
-static bool gPathOpCubicQuadSlopVerbose = false;
-static const int kCubicToQuadSubdivisionDepth = 8; // slots reserved for cubic to quads subdivision
-
-// determine that slop required after quad/quad finds a candidate intersection
-// use the cross of the tangents plus the distance from 1 or 0 as knobs
-DEF_TEST(PathOpsCubicQuadSlop, reporter) {
-    // create a random non-selfintersecting cubic
-    // break it into quadratics
-    // offset the quadratic, measuring the slop required to find the intersection
-    if (!gPathOpCubicQuadSlopVerbose) {  // takes a while to run -- so exclude it by default
-        return;
-    }
-    int results[101];
-    sk_bzero(results, sizeof(results));
-    double minCross[101];
-    sk_bzero(minCross, sizeof(minCross));
-    double maxCross[101];
-    sk_bzero(maxCross, sizeof(maxCross));
-    double sumCross[101];
-    sk_bzero(sumCross, sizeof(sumCross));
-    int foundOne = 0;
-    int slopCount = 1;
-    SkRandom ran;
-    for (int index = 0; index < 10000000; ++index) {
-        if (index % 1000 == 999) SkDebugf(".");
-        SkDCubic cubic = {{
-                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
-                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
-                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
-                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}
-        }};
-        SkIntersections i;
-        if (i.intersect(cubic)) {
-            continue;
-        }
-        SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts;
-        cubic.toQuadraticTs(cubic.calcPrecision(), &ts);
-        double tStart = 0;
-        int tsCount = ts.count();
-        for (int i1 = 0; i1 <= tsCount; ++i1) {
-            const double tEnd = i1 < tsCount ? ts[i1] : 1;
-            SkDCubic part = cubic.subDivide(tStart, tEnd);
-            SkDQuad quad = part.toQuad();
-            SkReduceOrder reducer;
-            int order = reducer.reduce(quad);
-            if (order != 3) {
-                continue;
-            }
-            for (int i2 = 0; i2 < 100; ++i2) {
-                SkDPoint endDisplacement = {ran.nextRangeF(-100, 100), ran.nextRangeF(-100, 100)};
-                SkDQuad nearby = {{
-                        {quad[0].fX + endDisplacement.fX, quad[0].fY + endDisplacement.fY},
-                        {quad[1].fX + ran.nextRangeF(-100, 100), quad[1].fY + ran.nextRangeF(-100, 100)},
-                        {quad[2].fX - endDisplacement.fX, quad[2].fY - endDisplacement.fY}
-                }};
-                order = reducer.reduce(nearby);
-                if (order != 3) {
-                    continue;
-                }
-                SkIntersections locals;
-                locals.allowNear(false);
-                locals.intersect(quad, nearby);
-                if (locals.used() != 1) {
-                    continue;
-                }
-                // brute force find actual intersection
-                SkDLine cubicLine = {{ {0, 0}, {cubic[0].fX, cubic[0].fY } }};
-                SkIntersections liner;
-                int i3;
-                int found = -1;
-                int foundErr = true;
-                for (i3 = 1; i3 <= 1000; ++i3) {
-                    cubicLine[0] = cubicLine[1];
-                    cubicLine[1] = cubic.ptAtT(i3 / 1000.);
-                    liner.reset();
-                    liner.allowNear(false);
-                    liner.intersect(nearby, cubicLine);
-                    if (liner.used() == 0) {
-                        continue;
-                    }
-                    if (liner.used() > 1) {
-                        foundErr = true;
-                        break;
-                    }
-                    if (found > 0) {
-                        foundErr = true;
-                        break;
-                    }
-                    foundErr = false;
-                    found = i3;
-                }
-                if (foundErr) {
-                    continue;
-                }
-                SkDVector dist = liner.pt(0) - locals.pt(0);
-                SkDVector qV = nearby.dxdyAtT(locals[0][0]);
-                double cubicT = (found - 1 + liner[1][0]) / 1000.;
-                SkDVector cV = cubic.dxdyAtT(cubicT);
-                double qxc = qV.crossCheck(cV);
-                double qvLen = qV.length();
-                double cvLen = cV.length();
-                double maxLen = SkTMax(qvLen, cvLen);
-                qxc /= maxLen;
-                double quadT = tStart + (tEnd - tStart) * locals[0][0];
-                double diffT = fabs(cubicT - quadT);
-                int diffIdx = (int) (diffT * 100);
-                results[diffIdx]++;
-                double absQxc = fabs(qxc);
-                if (sumCross[diffIdx] == 0) {
-                    minCross[diffIdx] = maxCross[diffIdx] = sumCross[diffIdx] = absQxc;
-                } else {
-                    minCross[diffIdx] = SkTMin(minCross[diffIdx], absQxc);
-                    maxCross[diffIdx] = SkTMax(maxCross[diffIdx], absQxc);
-                    sumCross[diffIdx] +=  absQxc;
-                }
-                if (diffIdx >= 20) {
-#if 01
-                    SkDebugf("cubic={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
-                        " quad={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
-                        " {{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
-                        " qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n",
-                        cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY,
-                        cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY,
-                        nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY,
-                        nearby[2].fX, nearby[2].fY,
-                        liner.pt(0).fX, liner.pt(0).fY,
-                        locals.pt(0).fX, locals.pt(0).fY, quadT, cubicT, dist.length(), qxc);
-#else
-                    SkDebugf("qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n",
-                        quadT, cubicT, dist.length(), qxc);
-                    SkDebugf("<div id=\"slop%d\">\n", ++slopCount);
-                    SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n"
-                        "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n"
-                        "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n",
-                        cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY,
-                        cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY,
-                        nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY,
-                        nearby[2].fX, nearby[2].fY,
-                        liner.pt(0).fX, liner.pt(0).fY,
-                        locals.pt(0).fX, locals.pt(0).fY);
-                    SkDebugf("</div>\n\n");
-#endif
-                }
-                ++foundOne;
-            }
-            tStart = tEnd;
-        }
-        if (++foundOne >= 100000) {
-            break;
-        }
-    }
-#if 01
-    SkDebugf("slopCount=%d\n", slopCount);
-    int max = 100;
-    while (results[max] == 0) {
-        --max;
-    }
-    for (int i = 0; i <= max; ++i) {
-        if (i > 0 && i % 10 == 0) {
-            SkDebugf("\n");
-        }
-        SkDebugf("%d ", results[i]);
-    }
-    SkDebugf("min\n");
-    for (int i = 0; i <= max; ++i) {
-        if (i > 0 && i % 10 == 0) {
-            SkDebugf("\n");
-        }
-        SkDebugf("%1.9g ", minCross[i]);
-    }
-    SkDebugf("max\n");
-    for (int i = 0; i <= max; ++i) {
-        if (i > 0 && i % 10 == 0) {
-            SkDebugf("\n");
-        }
-        SkDebugf("%1.9g ", maxCross[i]);
-    }
-    SkDebugf("avg\n");
-    for (int i = 0; i <= max; ++i) {
-        if (i > 0 && i % 10 == 0) {
-            SkDebugf("\n");
-        }
-        SkDebugf("%1.9g ", sumCross[i] / results[i]);
-    }
-#else
-    for (int i = 1; i < slopCount; ++i) {
-        SkDebugf("        slop%d,\n", i);
-    }
-#endif
-    SkDebugf("\n");
-}
diff --git a/src/third_party/skia/tests/PathOpsCubicReduceOrderTest.cpp b/src/third_party/skia/tests/PathOpsCubicReduceOrderTest.cpp
index dc779b8..89a263c 100644
--- a/src/third_party/skia/tests/PathOpsCubicReduceOrderTest.cpp
+++ b/src/third_party/skia/tests/PathOpsCubicReduceOrderTest.cpp
@@ -105,7 +105,9 @@
             ? firstTestIndex : SK_MaxS32;
 #endif
     for (index = firstPointDegeneratesTest; index < pointDegenerates_count; ++index) {
-        const SkDCubic& cubic = pointDegenerates[index];
+        const CubicPts& c = pointDegenerates[index];
+        SkDCubic cubic;
+        cubic.debugSet(c.fPts);
         SkASSERT(ValidCubic(cubic));
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
         if (order != 1) {
@@ -114,7 +116,9 @@
         }
     }
     for (index = firstNotPointDegeneratesTest; index < notPointDegenerates_count; ++index) {
-        const SkDCubic& cubic = notPointDegenerates[index];
+        const CubicPts& c = notPointDegenerates[index];
+        SkDCubic cubic;
+        cubic.debugSet(c.fPts);
         SkASSERT(ValidCubic(cubic));
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
         if (order == 1) {
@@ -124,7 +128,9 @@
         }
     }
     for (index = firstLinesTest; index < lines_count; ++index) {
-        const SkDCubic& cubic = lines[index];
+        const CubicPts& c = lines[index];
+        SkDCubic cubic;
+        cubic.debugSet(c.fPts);
         SkASSERT(ValidCubic(cubic));
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
         if (order != 2) {
@@ -133,7 +139,9 @@
         }
     }
     for (index = firstNotLinesTest; index < notLines_count; ++index) {
-        const SkDCubic& cubic = notLines[index];
+        const CubicPts& c = notLines[index];
+        SkDCubic cubic;
+        cubic.debugSet(c.fPts);
         SkASSERT(ValidCubic(cubic));
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
         if (order == 2) {
@@ -142,7 +150,9 @@
        }
     }
     for (index = firstModEpsilonTest; index < modEpsilonLines_count; ++index) {
-        const SkDCubic& cubic = modEpsilonLines[index];
+        const CubicPts& c = modEpsilonLines[index];
+        SkDCubic cubic;
+        cubic.debugSet(c.fPts);
         SkASSERT(ValidCubic(cubic));
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
         if (order == 2) {
@@ -151,7 +161,9 @@
         }
     }
     for (index = firstLessEpsilonTest; index < lessEpsilonLines_count; ++index) {
-        const SkDCubic& cubic = lessEpsilonLines[index];
+        const CubicPts& c = lessEpsilonLines[index];
+        SkDCubic cubic;
+        cubic.debugSet(c.fPts);
         SkASSERT(ValidCubic(cubic));
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
         if (order != 2) {
@@ -161,7 +173,9 @@
         }
     }
     for (index = firstNegEpsilonTest; index < negEpsilonLines_count; ++index) {
-        const SkDCubic& cubic = negEpsilonLines[index];
+        const CubicPts& c = negEpsilonLines[index];
+        SkDCubic cubic;
+        cubic.debugSet(c.fPts);
         SkASSERT(ValidCubic(cubic));
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
         if (order != 2) {
@@ -170,9 +184,11 @@
         }
     }
     for (index = firstQuadraticPointTest; index < quadraticPoints_count; ++index) {
-        const SkDQuad& quad = quadraticPoints[index];
+        const QuadPts& q = quadraticPoints[index];
+        SkDQuad quad;
+        quad.debugSet(q.fPts);
         SkASSERT(ValidQuad(quad));
-        SkDCubic cubic = quad.toCubic();
+        SkDCubic cubic = quad.debugToCubic();
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
         if (order != 1) {
             SkDebugf("[%d] point quad order=%d\n", static_cast<int>(index), order);
@@ -180,9 +196,11 @@
         }
     }
     for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) {
-        const SkDQuad& quad = quadraticLines[index];
+        const QuadPts& q = quadraticLines[index];
+        SkDQuad quad;
+        quad.debugSet(q.fPts);
         SkASSERT(ValidQuad(quad));
-        SkDCubic cubic = quad.toCubic();
+        SkDCubic cubic = quad.debugToCubic();
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
         if (order != 2) {
             SkDebugf("[%d] line quad order=%d\n", static_cast<int>(index), order);
@@ -190,9 +208,11 @@
         }
     }
     for (index = firstQuadraticModLineTest; index < quadraticModEpsilonLines_count; ++index) {
-        const SkDQuad& quad = quadraticModEpsilonLines[index];
+        const QuadPts& q = quadraticModEpsilonLines[index];
+        SkDQuad quad;
+        quad.debugSet(q.fPts);
         SkASSERT(ValidQuad(quad));
-        SkDCubic cubic = quad.toCubic();
+        SkDCubic cubic = quad.debugToCubic();
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
         if (order != 3) {
             SkDebugf("[%d] line mod quad order=%d\n", static_cast<int>(index), order);
diff --git a/src/third_party/skia/tests/PathOpsCubicToQuadsTest.cpp b/src/third_party/skia/tests/PathOpsCubicToQuadsTest.cpp
deleted file mode 100644
index ab22a83..0000000
--- a/src/third_party/skia/tests/PathOpsCubicToQuadsTest.cpp
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "PathOpsCubicIntersectionTestData.h"
-#include "PathOpsQuadIntersectionTestData.h"
-#include "PathOpsTestCommon.h"
-#include "SkGeometry.h"
-#include "SkIntersections.h"
-#include "SkPathOpsRect.h"
-#include "SkReduceOrder.h"
-#include "Test.h"
-
-static void test(skiatest::Reporter* reporter, const SkDCubic* cubics, const char* name,
-                 int firstTest, size_t testCount) {
-    for (size_t index = firstTest; index < testCount; ++index) {
-        const SkDCubic& cubic = cubics[index];
-        SkASSERT(ValidCubic(cubic));
-        double precision = cubic.calcPrecision();
-        SkTArray<SkDQuad, true> quads;
-        CubicToQuads(cubic, precision, quads);
-        if (quads.count() != 1 && quads.count() != 2) {
-            SkDebugf("%s [%d] cubic to quadratics failed count=%d\n", name, static_cast<int>(index),
-                    quads.count());
-        }
-        REPORTER_ASSERT(reporter, quads.count() == 1);
-    }
-}
-
-static void test(skiatest::Reporter* reporter, const SkDQuad* quadTests, const char* name,
-                 int firstTest, size_t testCount) {
-    for (size_t index = firstTest; index < testCount; ++index) {
-        const SkDQuad& quad = quadTests[index];
-        SkASSERT(ValidQuad(quad));
-        SkDCubic cubic = quad.toCubic();
-        double precision = cubic.calcPrecision();
-        SkTArray<SkDQuad, true> quads;
-        CubicToQuads(cubic, precision, quads);
-        if (quads.count() != 1 && quads.count() != 2) {
-            SkDebugf("%s [%d] cubic to quadratics failed count=%d\n", name, static_cast<int>(index),
-                    quads.count());
-        }
-        REPORTER_ASSERT(reporter, quads.count() <= 2);
-    }
-}
-
-static void testC(skiatest::Reporter* reporter, const SkDCubic* cubics, const char* name,
-                  int firstTest, size_t testCount) {
-    // test if computed line end points are valid
-    for (size_t index = firstTest; index < testCount; ++index) {
-        const SkDCubic& cubic = cubics[index];
-        SkASSERT(ValidCubic(cubic));
-        double precision = cubic.calcPrecision();
-        SkTArray<SkDQuad, true> quads;
-        CubicToQuads(cubic, precision, quads);
-        if (!AlmostEqualUlps(cubic[0].fX, quads[0][0].fX)
-                || !AlmostEqualUlps(cubic[0].fY, quads[0][0].fY)) {
-            SkDebugf("[%d] unmatched start\n", static_cast<int>(index));
-            REPORTER_ASSERT(reporter, 0);
-        }
-        int last = quads.count() - 1;
-        if (!AlmostEqualUlps(cubic[3].fX, quads[last][2].fX)
-                || !AlmostEqualUlps(cubic[3].fY, quads[last][2].fY)) {
-            SkDebugf("[%d] unmatched end\n", static_cast<int>(index));
-            REPORTER_ASSERT(reporter, 0);
-        }
-    }
-}
-
-static void testC(skiatest::Reporter* reporter, const SkDCubic(* cubics)[2], const char* name,
-                  int firstTest, size_t testCount) {
-    for (size_t index = firstTest; index < testCount; ++index) {
-        for (int idx2 = 0; idx2 < 2; ++idx2) {
-            const SkDCubic& cubic = cubics[index][idx2];
-            SkASSERT(ValidCubic(cubic));
-            double precision = cubic.calcPrecision();
-            SkTArray<SkDQuad, true> quads;
-            CubicToQuads(cubic, precision, quads);
-            if (!AlmostEqualUlps(cubic[0].fX, quads[0][0].fX)
-                    || !AlmostEqualUlps(cubic[0].fY, quads[0][0].fY)) {
-                SkDebugf("[%d][%d] unmatched start\n", static_cast<int>(index), idx2);
-                REPORTER_ASSERT(reporter, 0);
-            }
-            int last = quads.count() - 1;
-            if (!AlmostEqualUlps(cubic[3].fX, quads[last][2].fX)
-                    || !AlmostEqualUlps(cubic[3].fY, quads[last][2].fY)) {
-                SkDebugf("[%d][%d] unmatched end\n", static_cast<int>(index), idx2);
-                REPORTER_ASSERT(reporter, 0);
-            }
-        }
-    }
-}
-
-DEF_TEST(CubicToQuads, reporter) {
-    enum {
-        RunAll,
-        RunPointDegenerates,
-        RunNotPointDegenerates,
-        RunLines,
-        RunNotLines,
-        RunModEpsilonLines,
-        RunLessEpsilonLines,
-        RunNegEpsilonLines,
-        RunQuadraticLines,
-        RunQuadraticModLines,
-        RunComputedLines,
-        RunComputedTests,
-        RunNone
-    } run = RunAll;
-    int firstTestIndex = 0;
-#if 0
-    run = RunComputedLines;
-    firstTestIndex = 18;
-#endif
-    int firstPointDegeneratesTest = run == RunAll ? 0 : run == RunPointDegenerates
-            ? firstTestIndex : SK_MaxS32;
-    int firstNotPointDegeneratesTest = run == RunAll ? 0 : run == RunNotPointDegenerates
-            ? firstTestIndex : SK_MaxS32;
-    int firstLinesTest = run == RunAll ? 0 : run == RunLines ? firstTestIndex : SK_MaxS32;
-    int firstNotLinesTest = run == RunAll ? 0 : run == RunNotLines ? firstTestIndex : SK_MaxS32;
-    int firstModEpsilonTest = run == RunAll ? 0 : run == RunModEpsilonLines
-            ? firstTestIndex : SK_MaxS32;
-    int firstLessEpsilonTest = run == RunAll ? 0 : run == RunLessEpsilonLines
-            ? firstTestIndex : SK_MaxS32;
-    int firstNegEpsilonTest = run == RunAll ? 0 : run == RunNegEpsilonLines
-            ? firstTestIndex : SK_MaxS32;
-    int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines
-            ? firstTestIndex : SK_MaxS32;
-    int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines
-            ? firstTestIndex : SK_MaxS32;
-    int firstComputedLinesTest = run == RunAll ? 0 : run == RunComputedLines
-            ? firstTestIndex : SK_MaxS32;
-    int firstComputedCubicsTest = run == RunAll ? 0 : run == RunComputedTests
-            ? firstTestIndex : SK_MaxS32;
-
-    test(reporter, pointDegenerates, "pointDegenerates", firstPointDegeneratesTest,
-            pointDegenerates_count);
-    testC(reporter, notPointDegenerates, "notPointDegenerates", firstNotPointDegeneratesTest,
-            notPointDegenerates_count);
-    test(reporter, lines, "lines", firstLinesTest, lines_count);
-    testC(reporter, notLines, "notLines", firstNotLinesTest, notLines_count);
-    testC(reporter, modEpsilonLines, "modEpsilonLines", firstModEpsilonTest, modEpsilonLines_count);
-    test(reporter, lessEpsilonLines, "lessEpsilonLines", firstLessEpsilonTest,
-            lessEpsilonLines_count);
-    test(reporter, negEpsilonLines, "negEpsilonLines", firstNegEpsilonTest, negEpsilonLines_count);
-    test(reporter, quadraticLines, "quadraticLines", firstQuadraticLineTest, quadraticLines_count);
-    test(reporter, quadraticModEpsilonLines, "quadraticModEpsilonLines", firstQuadraticModLineTest,
-            quadraticModEpsilonLines_count);
-    testC(reporter, lines, "computed lines", firstComputedLinesTest, lines_count);
-    testC(reporter, tests, "computed tests", firstComputedCubicsTest, tests_count);
-}
-
-static SkDCubic locals[] = {
-    {{{0, 1}, {1.9274705288631189e-19, 1.0000000000000002},
-            {0.0017190297609673323, 0.99828097023903239},
-            {0.0053709083094631276, 0.99505672974365911}}},
-    {{{14.5975863, 41.632436}, {16.3518929, 26.2639684}, {18.5165519, 7.68775139},
-            {8.03767257, 89.1628526}}},
-    {{{69.7292014, 38.6877352}, {24.7648688, 23.1501713}, {84.9283191, 90.2588441},
-            {80.392774, 61.3533852}}},
-    {{{60.776536520932126, 71.249307306133829}, {87.107894191103014, 22.377669868235323},
-            {1.4974754310666936, 68.069569937917208}, {45.261946574441133, 17.536076632112298}}},
-};
-
-static size_t localsCount = SK_ARRAY_COUNT(locals);
-
-#define DEBUG_CRASH 0
-#define TEST_AVERAGE_END_POINTS 0  // must take const off to test
-extern const bool AVERAGE_END_POINTS;
-
-static void oneOff(skiatest::Reporter* reporter, size_t x) {
-    const SkDCubic& cubic = locals[x];
-    SkASSERT(ValidCubic(cubic));
-    const SkPoint skcubic[4] = {
-            {static_cast<float>(cubic[0].fX), static_cast<float>(cubic[0].fY)},
-            {static_cast<float>(cubic[1].fX), static_cast<float>(cubic[1].fY)},
-            {static_cast<float>(cubic[2].fX), static_cast<float>(cubic[2].fY)},
-            {static_cast<float>(cubic[3].fX), static_cast<float>(cubic[3].fY)}};
-    SkScalar skinflect[2];
-    int skin = SkFindCubicInflections(skcubic, skinflect);
-    if (false) SkDebugf("%s %d %1.9g\n", __FUNCTION__, skin, skinflect[0]);
-    SkTArray<SkDQuad, true> quads;
-    double precision = cubic.calcPrecision();
-    CubicToQuads(cubic, precision, quads);
-    if (false) SkDebugf("%s quads=%d\n", __FUNCTION__, quads.count());
-}
-
-DEF_TEST(CubicsToQuadratics_OneOff_Loop, reporter) {
-    for (size_t x = 0; x < localsCount; ++x) {
-        oneOff(reporter, x);
-    }
-}
-
-DEF_TEST(CubicsToQuadratics_OneOff_Single, reporter) {
-    oneOff(reporter, 0);
-}
diff --git a/src/third_party/skia/tests/PathOpsDCubicTest.cpp b/src/third_party/skia/tests/PathOpsDCubicTest.cpp
index 422236d..b8c08df 100644
--- a/src/third_party/skia/tests/PathOpsDCubicTest.cpp
+++ b/src/third_party/skia/tests/PathOpsDCubicTest.cpp
@@ -8,22 +8,18 @@
 #include "SkPathOpsCubic.h"
 #include "Test.h"
 
-static const SkDCubic tests[] = {
-    {{{2, 0}, {3, 1}, {2, 2}, {1, 1}}},
-    {{{3, 1}, {2, 2}, {1, 1}, {2, 0}}},
-    {{{3, 0}, {2, 1}, {3, 2}, {1, 1}}},
+static const CubicPts hullTests[] = {
+{{{2.6250000819563866, 2.3750000223517418}, {2.833333432674408, 2.3333333432674408}, {3.1111112236976624, 2.3333333134651184}, {3.4074075222015381, 2.3333332538604736}}},
 };
 
-static const size_t tests_count = SK_ARRAY_COUNT(tests);
+static const size_t hullTests_count = SK_ARRAY_COUNT(hullTests);
 
-DEF_TEST(PathOpsDCubic, reporter) {
-    for (size_t index = 0; index < tests_count; ++index) {
-        const SkDCubic& cubic = tests[index];
-        SkASSERT(ValidCubic(cubic));
-        bool result = cubic.clockwise();
-        if (!result) {
-            SkDebugf("%s [%d] expected clockwise\n", __FUNCTION__, index);
-            REPORTER_ASSERT(reporter, 0);
-        }
+DEF_TEST(PathOpsCubicHull, reporter) {
+    for (size_t index = 0; index < hullTests_count; ++index) {
+        const CubicPts& c = hullTests[index];
+        SkDCubic cubic;
+        cubic.debugSet(c.fPts);
+        char order[4];
+        cubic.convexHull(order);
     }
 }
diff --git a/src/third_party/skia/tests/PathOpsDLineTest.cpp b/src/third_party/skia/tests/PathOpsDLineTest.cpp
index dd86dd3..1e16002 100644
--- a/src/third_party/skia/tests/PathOpsDLineTest.cpp
+++ b/src/third_party/skia/tests/PathOpsDLineTest.cpp
@@ -17,15 +17,6 @@
     {{{3, 2}, {1, 1}}},
 };
 
-static const SkDPoint left[] = {
-    {2, 1},
-    {1, 0},
-    {1, 1},
-    {1, 2},
-    {2, 0},
-    {2, 1}
-};
-
 static const size_t tests_count = SK_ARRAY_COUNT(tests);
 
 DEF_TEST(PathOpsLineUtilities, reporter) {
@@ -36,17 +27,6 @@
         SkPoint pts[2] = {line[0].asSkPoint(), line[1].asSkPoint()};
         line2.set(pts);
         REPORTER_ASSERT(reporter, line[0] == line2[0] && line[1] == line2[1]);
-        const SkDPoint& pt = left[index];
-        SkASSERT(ValidPoint(pt));
-        double result = line.isLeft(pt);
-        if ((result <= 0 && index >= 1) || (result < 0 && index == 0)) {
-            SkDebugf("%s [%d] expected left\n", __FUNCTION__, index);
-            REPORTER_ASSERT(reporter, 0);
-        }
-        line2 = line.subDivide(1, 0);
-        REPORTER_ASSERT(reporter, line[0] == line2[1] && line[1] == line2[0]);
-        line2 = SkDLine::SubDivide(pts, 1, 0);
-        REPORTER_ASSERT(reporter, line[0] == line2[1] && line[1] == line2[0]);
         SkDPoint mid = line.ptAtT(.5);
         REPORTER_ASSERT(reporter, approximately_equal((line[0].fX + line[1].fX) / 2, mid.fX));
         REPORTER_ASSERT(reporter, approximately_equal((line[0].fY + line[1].fY) / 2, mid.fY));
diff --git a/src/third_party/skia/tests/PathOpsDPointTest.cpp b/src/third_party/skia/tests/PathOpsDPointTest.cpp
index 186d691..e197d5d 100644
--- a/src/third_party/skia/tests/PathOpsDPointTest.cpp
+++ b/src/third_party/skia/tests/PathOpsDPointTest.cpp
@@ -38,7 +38,6 @@
         REPORTER_ASSERT(reporter, p == pt);
         REPORTER_ASSERT(reporter, p.approximatelyEqual(sPt));
         REPORTER_ASSERT(reporter, p.roughlyEqual(pt));
-        REPORTER_ASSERT(reporter, p.moreRoughlyEqual(pt));
         p.fX = p.fY = 0;
         REPORTER_ASSERT(reporter, p.fX == 0 && p.fY == 0);
         REPORTER_ASSERT(reporter, p.approximatelyZero());
diff --git a/src/third_party/skia/tests/PathOpsDQuadTest.cpp b/src/third_party/skia/tests/PathOpsDQuadTest.cpp
deleted file mode 100644
index bd29ff1..0000000
--- a/src/third_party/skia/tests/PathOpsDQuadTest.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "PathOpsTestCommon.h"
-#include "SkPath.h"
-#include "SkPathOpsQuad.h"
-#include "SkRRect.h"
-#include "Test.h"
-
-static const SkDQuad tests[] = {
-    {{{1, 1}, {2, 1}, {0, 2}}},
-    {{{0, 0}, {1, 1}, {3, 1}}},
-    {{{2, 0}, {1, 1}, {2, 2}}},
-    {{{4, 0}, {0, 1}, {4, 2}}},
-    {{{0, 0}, {0, 1}, {1, 1}}},
-};
-
-static const SkDPoint inPoint[]= {
-    {1,   1.2},
-    {1,   0.8},
-    {1.8, 1},
-    {1.5, 1},
-    {0.4999, 0.5},  // was 0.5, 0.5; points on the hull are considered outside
-};
-
-static const SkDPoint outPoint[]= {
-    {1,   1.6},
-    {1,   1.5},
-    {2.2, 1},
-    {1.5, 1.5},
-    {1.1, 0.5},
-};
-
-static const size_t tests_count = SK_ARRAY_COUNT(tests);
-
-DEF_TEST(PathOpsDQuad, reporter) {
-    for (size_t index = 0; index < tests_count; ++index) {
-        const SkDQuad& quad = tests[index];
-        SkASSERT(ValidQuad(quad));
-        bool result = quad.pointInHull(inPoint[index]);
-        if (!result) {
-            SkDebugf("%s [%d] expected in hull\n", __FUNCTION__, index);
-            REPORTER_ASSERT(reporter, 0);
-        }
-        result = quad.pointInHull(outPoint[index]);
-        if (result) {
-            SkDebugf("%s [%d] expected outside hull\n", __FUNCTION__, index);
-            REPORTER_ASSERT(reporter, 0);
-        }
-    }
-}
-
-DEF_TEST(PathOpsRRect, reporter) {
-    SkPath path;
-    SkRRect rRect;
-    SkRect rect = {135, 143, 250, 177};
-    SkVector radii[4] = {{8, 8}, {8, 8}, {0, 0}, {0, 0}};
-    rRect.setRectRadii(rect, radii);
-    path.addRRect(rRect);
-}
diff --git a/src/third_party/skia/tests/PathOpsDRectTest.cpp b/src/third_party/skia/tests/PathOpsDRectTest.cpp
index 874e82b..fa52e78 100644
--- a/src/third_party/skia/tests/PathOpsDRectTest.cpp
+++ b/src/third_party/skia/tests/PathOpsDRectTest.cpp
@@ -11,16 +11,7 @@
 #include "SkPathOpsRect.h"
 #include "Test.h"
 
-static const SkDLine lineTests[] = {
-    {{{2, 1}, {2, 1}}},
-    {{{2, 1}, {1, 1}}},
-    {{{2, 1}, {2, 2}}},
-    {{{1, 1}, {2, 2}}},
-    {{{3, 0}, {2, 1}}},
-    {{{3, 2}, {1, 1}}},
-};
-
-static const SkDQuad quadTests[] = {
+static const QuadPts quadTests[] = {
     {{{1, 1}, {2, 1}, {0, 2}}},
     {{{0, 0}, {1, 1}, {3, 1}}},
     {{{2, 0}, {1, 1}, {2, 2}}},
@@ -28,50 +19,39 @@
     {{{0, 0}, {0, 1}, {1, 1}}},
 };
 
-static const SkDCubic cubicTests[] = {
+static const CubicPts cubicTests[] = {
     {{{2, 0}, {3, 1}, {2, 2}, {1, 1}}},
     {{{3, 1}, {2, 2}, {1, 1}, {2, 0}}},
     {{{3, 0}, {2, 1}, {3, 2}, {1, 1}}},
 };
 
-static const size_t lineTests_count = SK_ARRAY_COUNT(lineTests);
 static const size_t quadTests_count = SK_ARRAY_COUNT(quadTests);
 static const size_t cubicTests_count = SK_ARRAY_COUNT(cubicTests);
 
+static void setRawBounds(const SkDQuad& quad, SkDRect* rect) {
+    rect->set(quad[0]);
+    rect->add(quad[1]);
+    rect->add(quad[2]);
+}
+
+static void setRawBounds(const SkDCubic& cubic, SkDRect* rect) {
+    rect->set(cubic[0]);
+    rect->add(cubic[1]);
+    rect->add(cubic[2]);
+    rect->add(cubic[3]);
+}
+
 DEF_TEST(PathOpsDRect, reporter) {
     size_t index;
     SkDRect rect, rect2;
-    for (index = 0; index < lineTests_count; ++index) {
-        const SkDLine& line = lineTests[index];
-        SkASSERT(ValidLine(line));
-        rect.setBounds(line);
-        REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(line[0].fX, line[1].fX));
-        REPORTER_ASSERT(reporter, rect.fTop == SkTMin(line[0].fY, line[1].fY));
-        REPORTER_ASSERT(reporter, rect.fRight == SkTMax(line[0].fX, line[1].fX));
-        REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(line[0].fY, line[1].fY));
-        rect2.set(line[0]);
-        rect2.add(line[1]);
-        REPORTER_ASSERT(reporter, rect2.fLeft == SkTMin(line[0].fX, line[1].fX));
-        REPORTER_ASSERT(reporter, rect2.fTop == SkTMin(line[0].fY, line[1].fY));
-        REPORTER_ASSERT(reporter, rect2.fRight == SkTMax(line[0].fX, line[1].fX));
-        REPORTER_ASSERT(reporter, rect2.fBottom == SkTMax(line[0].fY, line[1].fY));
-        REPORTER_ASSERT(reporter, rect.contains(line[0]));
-        REPORTER_ASSERT(reporter, rect.intersects(&rect2));
-    }
     for (index = 0; index < quadTests_count; ++index) {
-        const SkDQuad& quad = quadTests[index];
+        const QuadPts& q = quadTests[index];
+        SkDQuad quad;
+        quad.debugSet(q.fPts);
         SkASSERT(ValidQuad(quad));
-        rect.setRawBounds(quad);
-        REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(quad[0].fX,
-                SkTMin(quad[1].fX, quad[2].fX)));
-        REPORTER_ASSERT(reporter, rect.fTop == SkTMin(quad[0].fY,
-                SkTMin(quad[1].fY, quad[2].fY)));
-        REPORTER_ASSERT(reporter, rect.fRight == SkTMax(quad[0].fX,
-                SkTMax(quad[1].fX, quad[2].fX)));
-        REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(quad[0].fY,
-                SkTMax(quad[1].fY, quad[2].fY)));
+        setRawBounds(quad, &rect);
         rect2.setBounds(quad);
-        REPORTER_ASSERT(reporter, rect.intersects(&rect2));
+        REPORTER_ASSERT(reporter, rect.intersects(rect2));
         // FIXME: add a recursive box subdivision method to verify that tight bounds is correct
         SkDPoint leftTop = {rect2.fLeft, rect2.fTop};
         REPORTER_ASSERT(reporter, rect.contains(leftTop));
@@ -79,19 +59,13 @@
         REPORTER_ASSERT(reporter, rect.contains(rightBottom));
     }
     for (index = 0; index < cubicTests_count; ++index) {
-        const SkDCubic& cubic = cubicTests[index];
+        const CubicPts& c = cubicTests[index];
+        SkDCubic cubic;
+        cubic.debugSet(c.fPts);
         SkASSERT(ValidCubic(cubic));
-        rect.setRawBounds(cubic);
-        REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(cubic[0].fX,
-                SkTMin(cubic[1].fX, SkTMin(cubic[2].fX, cubic[3].fX))));
-        REPORTER_ASSERT(reporter, rect.fTop == SkTMin(cubic[0].fY,
-                SkTMin(cubic[1].fY, SkTMin(cubic[2].fY, cubic[3].fY))));
-        REPORTER_ASSERT(reporter, rect.fRight == SkTMax(cubic[0].fX,
-                SkTMax(cubic[1].fX, SkTMax(cubic[2].fX, cubic[3].fX))));
-        REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(cubic[0].fY,
-                SkTMax(cubic[1].fY, SkTMax(cubic[2].fY, cubic[3].fY))));
+        setRawBounds(cubic, &rect);
         rect2.setBounds(cubic);
-        REPORTER_ASSERT(reporter, rect.intersects(&rect2));
+        REPORTER_ASSERT(reporter, rect.intersects(rect2));
         // FIXME: add a recursive box subdivision method to verify that tight bounds is correct
         SkDPoint leftTop = {rect2.fLeft, rect2.fTop};
         REPORTER_ASSERT(reporter, rect.contains(leftTop));
diff --git a/src/third_party/skia/tests/PathOpsDTriangleTest.cpp b/src/third_party/skia/tests/PathOpsDTriangleTest.cpp
deleted file mode 100644
index b5e2d41..0000000
--- a/src/third_party/skia/tests/PathOpsDTriangleTest.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "PathOpsTestCommon.h"
-#include "SkPathOpsTriangle.h"
-#include "Test.h"
-
-static const SkDTriangle tests[] = {
-    {{{2, 0}, {3, 1}, {2, 2}}},
-    {{{3, 1}, {2, 2}, {1, 1}}},
-    {{{3, 0}, {2, 1}, {3, 2}}},
-};
-
-static const SkDPoint inPoint[] = {
-    {2.5, 1},
-    {2, 1.5},
-    {2.5, 1},
-};
-
-static const SkDPoint outPoint[] = {
-    {3, 0},
-    {2.5, 2},
-    {2.5, 2},
-};
-
-static const size_t tests_count = SK_ARRAY_COUNT(tests);
-
-DEF_TEST(PathOpsTriangleUtilities, reporter) {
-    for (size_t index = 0; index < tests_count; ++index) {
-        const SkDTriangle& triangle = tests[index];
-        SkASSERT(ValidTriangle(triangle));
-        bool result = triangle.contains(inPoint[index]);
-        if (!result) {
-            SkDebugf("%s [%d] expected point in triangle\n", __FUNCTION__, index);
-            REPORTER_ASSERT(reporter, 0);
-        }
-        result = triangle.contains(outPoint[index]);
-        if (result) {
-            SkDebugf("%s [%d] expected point outside triangle\n", __FUNCTION__, index);
-            REPORTER_ASSERT(reporter, 0);
-        }
-    }
-}
-
-static const SkDTriangle oneOff[] = {
-    {{{271.03291625750461, 5.0402503630087025e-05}, {275.21652430019037, 3.6997300650817753},
-      {279.25839233398438, 7.7416000366210938}}},
-
-    {{{271.03291625750461, 5.0402503617874572e-05}, {275.21652430019037, 3.6997300650817877},
-      {279.25839233398438, 7.7416000366210938}}}
-};
-
-static const size_t oneOff_count = SK_ARRAY_COUNT(oneOff);
-
-DEF_TEST(PathOpsTriangleOneOff, reporter) {
-    for (size_t index = 0; index < oneOff_count; ++index) {
-        const SkDTriangle& triangle = oneOff[index];
-        SkASSERT(ValidTriangle(triangle));
-        for (int inner = 0; inner < 3; ++inner) {
-            bool result = triangle.contains(triangle.fPts[inner]);
-            if (result) {
-                SkDebugf("%s [%d][%d] point on triangle is not in\n", __FUNCTION__, index, inner);
-                REPORTER_ASSERT(reporter, 0);
-            }
-        }
-    }
-}
diff --git a/src/third_party/skia/tests/PathOpsDVectorTest.cpp b/src/third_party/skia/tests/PathOpsDVectorTest.cpp
index ab291b2..583a868 100644
--- a/src/third_party/skia/tests/PathOpsDVectorTest.cpp
+++ b/src/third_party/skia/tests/PathOpsDVectorTest.cpp
@@ -28,8 +28,6 @@
         SkASSERT(ValidVector(v2));
         v1 += v2;
         REPORTER_ASSERT(reporter, v1.fX == 0 && v1.fY == 0);
-        SkDPoint p = tests[index + 1] + v2;
-        REPORTER_ASSERT(reporter, p == tests[index]);
         v2 -= v2;
         REPORTER_ASSERT(reporter, v2.fX == 0 && v2.fY == 0);
         v1 = tests[index + 1] - tests[index];
diff --git a/src/third_party/skia/tests/PathOpsDebug.cpp b/src/third_party/skia/tests/PathOpsDebug.cpp
old mode 100755
new mode 100644
index 8ac38aa..49f730a
--- a/src/third_party/skia/tests/PathOpsDebug.cpp
+++ b/src/third_party/skia/tests/PathOpsDebug.cpp
@@ -1,5 +1,15 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "PathOpsTSectDebug.h"
+#include "SkOpCoincidence.h"
 #include "SkOpContour.h"
 #include "SkIntersectionHelper.h"
+#include "SkMutex.h"
 #include "SkOpSegment.h"
 #include "SkString.h"
 
@@ -19,137 +29,10 @@
     }
 }
 
-
-#if DEBUG_SHOW_TEST_NAME
-
-static void output_scalar(SkScalar num) {
-    if (num == (int) num) {
-        SkDebugf("%d", (int) num);
-    } else {
-        SkString str;
-        str.printf("%1.9g", num);
-        int width = (int) str.size();
-        const char* cStr = str.c_str();
-        while (cStr[width - 1] == '0') {
-            --width;
-        }
-        str.resize(width);
-        SkDebugf("%sf", str.c_str());
-    }
+inline void DebugDumpHexFloat(float x) {
+    SkDebugf("SkBits2Float(0x%08x)", SkFloat2Bits(x));
 }
 
-static void output_points(const SkPoint* pts, int count) {
-    for (int index = 0; index < count; ++index) {
-        output_scalar(pts[index].fX);
-        SkDebugf(", ");
-        output_scalar(pts[index].fY);
-        if (index + 1 < count) {
-            SkDebugf(", ");
-        }
-    }
-    SkDebugf(");\n");
-}
-
-static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
-    uint8_t verb;
-    SkPoint pts[4];
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-        switch (verb) {
-            case SkPath::kMove_Verb:
-                SkDebugf("    %s.moveTo(", pathName);
-                output_points(&pts[0], 1);
-                continue;
-            case SkPath::kLine_Verb:
-                SkDebugf("    %s.lineTo(", pathName);
-                output_points(&pts[1], 1);
-                break;
-            case SkPath::kQuad_Verb:
-                SkDebugf("    %s.quadTo(", pathName);
-                output_points(&pts[1], 2);
-                break;
-            case SkPath::kCubic_Verb:
-                SkDebugf("    %s.cubicTo(", pathName);
-                output_points(&pts[1], 3);
-                break;
-            case SkPath::kClose_Verb:
-                SkDebugf("    %s.close();\n", pathName);
-                break;
-            default:
-                SkDEBUGFAIL("bad verb");
-                return;
-        }
-    }
-}
-
-static const char* gFillTypeStr[] = {
-    "kWinding_FillType",
-    "kEvenOdd_FillType",
-    "kInverseWinding_FillType",
-    "kInverseEvenOdd_FillType"
-};
-
-void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
-    SkPath::RawIter iter(path);
-#define SUPPORT_RECT_CONTOUR_DETECTION 0
-#if SUPPORT_RECT_CONTOUR_DETECTION
-    int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0;
-    if (rectCount > 0) {
-        SkTDArray<SkRect> rects;
-        SkTDArray<SkPath::Direction> directions;
-        rects.setCount(rectCount);
-        directions.setCount(rectCount);
-        path.rectContours(rects.begin(), directions.begin());
-        for (int contour = 0; contour < rectCount; ++contour) {
-            const SkRect& rect = rects[contour];
-            SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
-                    rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
-                    ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
-        }
-        return;
-    }
-#endif
-    SkPath::FillType fillType = path.getFillType();
-    SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
-    if (includeDeclaration) {
-        SkDebugf("    SkPath %s;\n", name);
-    }
-    SkDebugf("    %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
-    iter.setPath(path);
-    showPathContours(iter, name);
-}
-
-static void show_function_header(const char* functionName) {
-    SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
-    if (strcmp("skphealth_com76", functionName) == 0) {
-        SkDebugf("found it\n");
-    }
-}
-
-static const char* gOpStrs[] = {
-    "kDifference_PathOp",
-    "kIntersect_PathOp",
-    "kUnion_PathOp",
-    "kXor_PathOp",
-    "kReverseDifference_PathOp",
-};
-
-static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
-    SkDebugf("    testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
-    SkDebugf("}\n");
-}
-
-SK_DECLARE_STATIC_MUTEX(gTestMutex);
-
-void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
-        const char* testName) {
-    SkAutoMutexAcquire ac(gTestMutex);
-    show_function_header(testName);
-    ShowOnePath(a, "path", true);
-    ShowOnePath(b, "pathB", true);
-    show_op(shapeOp, "path", "pathB");
-}
-#endif
-
 // if not defined by PathOpsDebug.cpp ...
 #if !defined SK_DEBUG && FORCE_RELEASE
 bool SkPathOpsDebug::ValidWind(int wind) {
@@ -165,236 +48,105 @@
 }
 #endif
 
-void SkOpAngle::dump() const {
-    dumpOne(true);
+static void DumpID(int id) {
+    SkDebugf("} ");
+    if (id >= 0) {
+        SkDebugf("id=%d", id);
+    }
     SkDebugf("\n");
 }
 
-void SkOpAngle::dumpOne(bool functionHeader) const {
-//    fSegment->debugValidate();
-    const SkOpSpan& mSpan = fSegment->span(SkMin32(fStart, fEnd));
-    if (functionHeader) {
-        SkDebugf("%s ", __FUNCTION__);
-    }
-    SkDebugf("[%d", fSegment->debugID());
-    SkDebugf("/%d", debugID());
-    SkDebugf("] next=");
-    if (fNext) {
-        SkDebugf("%d", fNext->fSegment->debugID());
-        SkDebugf("/%d", fNext->debugID());
-    } else {
-        SkDebugf("?");
-    }
-    SkDebugf(" sect=%d/%d ", fSectorStart, fSectorEnd);
-    SkDebugf(" s=%1.9g [%d] e=%1.9g [%d]", fSegment->span(fStart).fT, fStart,
-            fSegment->span(fEnd).fT, fEnd);
-    SkDebugf(" sgn=%d windVal=%d", sign(), mSpan.fWindValue);
-
-    SkDebugf(" windSum=");
-    SkPathOpsDebug::WindingPrintf(mSpan.fWindSum);
-    if (mSpan.fOppValue != 0 || mSpan.fOppSum != SK_MinS32) {
-        SkDebugf(" oppVal=%d", mSpan.fOppValue);
-        SkDebugf(" oppSum=");
-        SkPathOpsDebug::WindingPrintf(mSpan.fOppSum);
-    }
-    if (mSpan.fDone) {
-        SkDebugf(" done");
-    }
-    if (unorderable()) {
-        SkDebugf(" unorderable");
-    }
-    if (small()) {
-        SkDebugf(" small");
-    }
-    if (mSpan.fTiny) {
-        SkDebugf(" tiny");
-    }
-    if (fSegment->operand()) {
-        SkDebugf(" operand");
-    }
-    if (fStop) {
-        SkDebugf(" stop");
-    }
+void SkDConic::dump() const {
+    dumpInner();
+    SkDebugf("},\n");
 }
 
-void SkOpAngle::dumpTo(const SkOpSegment* segment, const SkOpAngle* to) const {
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = this;
-    const char* indent = "";
-    do {
-        SkDebugf("%s", indent);
-        next->dumpOne(false);
-        if (segment == next->fSegment) {
-            if (this == fNext) {
-                SkDebugf(" << from");
-            }
-            if (to == fNext) {
-                SkDebugf(" << to");
-            }
-        }
-        SkDebugf("\n");
-        indent = "           ";
-        next = next->fNext;
-    } while (next && next != first);
+void SkDConic::dumpID(int id) const {
+    dumpInner();
+    DumpID(id);
 }
 
-void SkOpAngle::dumpLoop() const {
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = this;
-    do {
-        next->dumpOne(false);
-        SkDebugf("\n");
-        next = next->fNext;
-    } while (next && next != first);
-}
-
-void SkOpAngle::dumpPartials() const {
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = this;
-    do {
-        next->fCurvePart.dumpNumber();
-        next = next->fNext;
-    } while (next && next != first);
-}
-
-void SkOpAngleSet::dump() const {
-    // FIXME: unimplemented
-/* This requires access to the internal SkChunkAlloc data
-   Defer implementing this until it is needed for debugging
-*/
-    SkASSERT(0);
-}
-
-void SkOpContour::dump() const {
-    int segmentCount = fSegments.count();
-    SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
-    for (int test = 0; test < segmentCount; ++test) {
-        SkDebugf("  [%d] ((SkOpSegment*) 0x%p) [%d]\n", test, &fSegments[test],
-                fSegments[test].debugID());
-    }
-}
-
-void SkOpContour::dumpAngles() const {
-    int segmentCount = fSegments.count();
-    SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
-    for (int test = 0; test < segmentCount; ++test) {
-        SkDebugf("  [%d] ", test);
-        fSegments[test].dumpAngles();
-    }
-}
-
-void SkOpContour::dumpCoincidence(const SkCoincidence& coin) const {
-    int thisIndex = coin.fSegments[0];
-    const SkOpSegment& s1 = fSegments[thisIndex];
-    int otherIndex = coin.fSegments[1];
-    const SkOpSegment& s2 = coin.fOther->fSegments[otherIndex];
-    SkDebugf("((SkOpSegment*) 0x%p) [%d]  ((SkOpSegment*) 0x%p) [%d]\n", &s1, s1.debugID(),
-            &s2, s2.debugID());
-    for (int index = 0; index < 2; ++index) {
-        SkDebugf("    {%1.9gf, %1.9gf}", coin.fPts[0][index].fX, coin.fPts[0][index].fY);
-        if (coin.fNearly[index]) {
-            SkDebugf("    {%1.9gf, %1.9gf}", coin.fPts[1][index].fX, coin.fPts[1][index].fY);
-        }
-        SkDebugf("  seg1t=%1.9g seg2t=%1.9g\n", coin.fTs[0][index], coin.fTs[1][index]);
-    }
-}
-
-void SkOpContour::dumpCoincidences() const {
-    int count = fCoincidences.count();
-    if (count > 0) {
-        SkDebugf("fCoincidences count=%d\n", count);
-        for (int test = 0; test < count; ++test) {
-            dumpCoincidence(fCoincidences[test]);
-        }
-    }
-    count = fPartialCoincidences.count();
-    if (count == 0) {
-        return;
-    }
-    SkDebugf("fPartialCoincidences count=%d\n", count);
-    for (int test = 0; test < count; ++test) {
-        dumpCoincidence(fPartialCoincidences[test]);
-    }
-}
-
-void SkOpContour::dumpPt(int index) const {
-    int segmentCount = fSegments.count();
-    for (int test = 0; test < segmentCount; ++test) {
-        const SkOpSegment& segment = fSegments[test];
-        if (segment.debugID() == index) {
-            fSegments[test].dumpPts();
-        }
-    }
-}
-
-void SkOpContour::dumpPts() const {
-    int segmentCount = fSegments.count();
-    SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
-    for (int test = 0; test < segmentCount; ++test) {
-        SkDebugf("  [%d] ", test);
-        fSegments[test].dumpPts();
-    }
-}
-
-void SkOpContour::dumpSpan(int index) const {
-    int segmentCount = fSegments.count();
-    for (int test = 0; test < segmentCount; ++test) {
-        const SkOpSegment& segment = fSegments[test];
-        if (segment.debugID() == index) {
-            fSegments[test].dumpSpans();
-        }
-    }
-}
-
-void SkOpContour::dumpSpans() const {
-    int segmentCount = fSegments.count();
-    SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
-    for (int test = 0; test < segmentCount; ++test) {
-        SkDebugf("  [%d] ", test);
-        fSegments[test].dumpSpans();
-    }
+void SkDConic::dumpInner() const {
+    SkDebugf("{");
+    fPts.dumpInner();
+    SkDebugf("}}, %1.9gf", fWeight);
 }
 
 void SkDCubic::dump() const {
-    SkDebugf("{{");
-    int index = 0;
-    do {
-        fPts[index].dump();
-        SkDebugf(", ");
-    } while (++index < 3);
-    fPts[index].dump();
-    SkDebugf("}}\n");
+    this->dumpInner();
+    SkDebugf("}},\n");
 }
 
-void SkDCubic::dumpNumber() const {
+void SkDCubic::dumpID(int id) const {
+    this->dumpInner();
+    SkDebugf("}");
+    DumpID(id);
+}
+
+static inline bool double_is_NaN(double x) { return x != x; }
+
+void SkDCubic::dumpInner() const {
     SkDebugf("{{");
     int index = 0;
-    bool dumpedOne = false;
     do {
-        if (!(fPts[index].fX == fPts[index].fX && fPts[index].fY == fPts[index].fY)) {
-            continue;
-        }
-        if (dumpedOne) {
+        if (index != 0) {
+            if (double_is_NaN(fPts[index].fX) && double_is_NaN(fPts[index].fY)) {
+                return;
+            }
             SkDebugf(", ");
         }
         fPts[index].dump();
-        dumpedOne = true;
     } while (++index < 3);
-    if (fPts[index].fX == fPts[index].fX && fPts[index].fY == fPts[index].fY) {
-        if (dumpedOne) {
-            SkDebugf(", ");
-        }
-        fPts[index].dump();
+    if (double_is_NaN(fPts[index].fX) && double_is_NaN(fPts[index].fY)) {
+        return;
     }
-    SkDebugf("}}\n");
+    SkDebugf(", ");
+    fPts[index].dump();
+}
+
+void SkDCurve::dump() const {
+    dumpID(-1);
+}
+
+void SkDCurve::dumpID(int id) const {
+#ifndef SK_RELEASE
+    switch(fVerb) {
+        case SkPath::kLine_Verb:
+            fLine.dumpID(id);
+            break;
+        case SkPath::kQuad_Verb:
+            fQuad.dumpID(id);
+            break;
+        case SkPath::kConic_Verb:
+            fConic.dumpID(id);
+            break;
+        case SkPath::kCubic_Verb:
+            fCubic.dumpID(id);
+            break;
+        default:
+            SkASSERT(0);
+    }
+#else
+    fCubic.dumpID(id);
+#endif
 }
 
 void SkDLine::dump() const {
+    this->dumpInner();
+    SkDebugf("}},\n");
+}
+
+void SkDLine::dumpID(int id) const {
+    this->dumpInner();
+    SkDebugf("}");
+    DumpID(id);
+}
+
+void SkDLine::dumpInner() const {
     SkDebugf("{{");
     fPts[0].dump();
     SkDebugf(", ");
     fPts[1].dump();
-    SkDebugf("}}\n");
 }
 
 void SkDPoint::dump() const {
@@ -413,8 +165,26 @@
     SkDebugf("}");
 }
 
+void SkDPoint::DumpHex(const SkPoint& pt) {
+    SkDebugf("{");
+    DebugDumpHexFloat(pt.fX);
+    SkDebugf(", ");
+    DebugDumpHexFloat(pt.fY);
+    SkDebugf("}");
+}
 
-void SkDQuad::dumpComma(const char* comma) const {
+void SkDQuad::dump() const {
+    dumpInner();
+    SkDebugf("}},\n");
+}
+
+void SkDQuad::dumpID(int id) const {
+    dumpInner();
+    SkDebugf("}");
+    DumpID(id);
+}
+
+void SkDQuad::dumpInner() const {
     SkDebugf("{{");
     int index = 0;
     do {
@@ -422,428 +192,600 @@
         SkDebugf(", ");
     } while (++index < 2);
     fPts[index].dump();
-    SkDebugf("}}%s\n", comma ? comma : "");
 }
 
-void SkDQuad::dump() const {
-    dumpComma("");
-}
-
-void SkIntersectionHelper::dump() const {
-    SkDPoint::Dump(pts()[0]);
-    SkDPoint::Dump(pts()[1]);
-    if (verb() >= SkPath::kQuad_Verb) {
-        SkDPoint::Dump(pts()[2]);
-    }
-    if (verb() >= SkPath::kCubic_Verb) {
-        SkDPoint::Dump(pts()[3]);
-    }
-}
-
-const SkTDArray<SkOpSpan>& SkOpSegment::debugSpans() const {
-    return fTs;
-}
-
-void SkOpSegment::dumpAngles() const {
-    SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", this, debugID());
-    const SkOpAngle* fromAngle = NULL;
-    const SkOpAngle* toAngle = NULL;
-    for (int index = 0; index < count(); ++index) {
-        const SkOpAngle* fAngle = fTs[index].fFromAngle;
-        const SkOpAngle* tAngle = fTs[index].fToAngle;
-        if (fromAngle == fAngle && toAngle == tAngle) {
-            continue;
+void SkIntersections::dump() const {
+    SkDebugf("used=%d of %d", fUsed, fMax);
+    for (int index = 0; index < fUsed; ++index) {
+        SkDebugf(" t=(%s%1.9g,%s%1.9g) pt=(%1.9g,%1.9g)",
+                fIsCoincident[0] & (1 << index) ? "*" : "", fT[0][index],
+                fIsCoincident[1] & (1 << index) ? "*" : "", fT[1][index],
+                fPt[index].fX, fPt[index].fY);
+        if (index < 2 && fNearlySame[index]) {
+            SkDebugf(" pt2=(%1.9g,%1.9g)",fPt2[index].fX, fPt2[index].fY);
         }
-        if (fAngle) {
-            SkDebugf("  [%d] from=%d ", index, fAngle->debugID());
-            fAngle->dumpTo(this, tAngle);
-        }
-        if (tAngle) {
-            SkDebugf("  [%d] to=%d   ", index, tAngle->debugID());
-            tAngle->dumpTo(this, fAngle);
-        }
-        fromAngle = fAngle;
-        toAngle = tAngle;
-    }
-}
-
-void SkOpSegment::dumpContour(int firstID, int lastID) const {
-    if (debugID() < 0) {
-        return;
-    }
-    const SkOpSegment* test = this - (debugID() - 1);
-    test += (firstID - 1);
-    const SkOpSegment* last = test + (lastID - firstID);
-    while (test <= last) {
-        test->dumpSpans();
-        ++test;
-    }
-}
-
-void SkOpSegment::dumpPts() const {
-    int last = SkPathOpsVerbToPoints(fVerb);
-    SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID());
-    int index = 0;
-    do {
-        SkDPoint::Dump(fPts[index]);
-        SkDebugf(", ");
-    } while (++index < last);
-    SkDPoint::Dump(fPts[index]);
-    SkDebugf("}}\n");
-}
-
-void SkOpSegment::dumpDPts() const {
-    int count = SkPathOpsVerbToPoints(fVerb);
-    SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID());
-    int index = 0;
-    do {
-        SkDPoint dPt = {fPts[index].fX, fPts[index].fY};
-        dPt.dump();
-        if (index != count) {
-            SkDebugf(", ");
-        }
-    } while (++index <= count);
-    SkDebugf("}}\n");
-}
-
-void SkOpSegment::dumpSpans() const {
-    int count = this->count();
-    SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", this, debugID());
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan& span = this->span(index);
-        SkDebugf("  [%d] ", index);
-        span.dumpOne();
-    }
-}
-
-void SkPathOpsDebug::DumpCoincidence(const SkTArray<SkOpContour, true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dumpCoincidences();
-    }
-}
-
-void SkPathOpsDebug::DumpCoincidence(const SkTArray<SkOpContour* , true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dumpCoincidences();
-    }
-}
-
-void SkPathOpsDebug::DumpContours(const SkTArray<SkOpContour, true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dump();
-    }
-}
-
-void SkPathOpsDebug::DumpContours(const SkTArray<SkOpContour* , true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dump();
-    }
-}
-
-void SkPathOpsDebug::DumpContourAngles(const SkTArray<SkOpContour, true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dumpAngles();
-    }
-}
-
-void SkPathOpsDebug::DumpContourAngles(const SkTArray<SkOpContour* , true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dumpAngles();
-    }
-}
-
-void SkPathOpsDebug::DumpContourPts(const SkTArray<SkOpContour, true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dumpPts();
-    }
-}
-
-void SkPathOpsDebug::DumpContourPts(const SkTArray<SkOpContour* , true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dumpPts();
-    }
-}
-
-void SkPathOpsDebug::DumpContourPt(const SkTArray<SkOpContour, true>& contours, int segmentID) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dumpPt(segmentID);
-    }
-}
-
-void SkPathOpsDebug::DumpContourPt(const SkTArray<SkOpContour* , true>& contours, int segmentID) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dumpPt(segmentID);
-    }
-}
-
-void SkPathOpsDebug::DumpContourSpans(const SkTArray<SkOpContour, true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dumpSpans();
-    }
-}
-
-void SkPathOpsDebug::DumpContourSpans(const SkTArray<SkOpContour* , true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dumpSpans();
-    }
-}
-
-void SkPathOpsDebug::DumpContourSpan(const SkTArray<SkOpContour, true>& contours, int segmentID) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dumpSpan(segmentID);
-    }
-}
-
-void SkPathOpsDebug::DumpContourSpan(const SkTArray<SkOpContour* , true>& contours, int segmentID) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dumpSpan(segmentID);
-    }
-}
-
-void SkPathOpsDebug::DumpSpans(const SkTDArray<SkOpSpan *>& spans) {
-    int count = spans.count();
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan* span = spans[index];
-        const SkOpSpan& oSpan = span->fOther->span(span->fOtherIndex);
-        const SkOpSegment* segment = oSpan.fOther;
-        SkDebugf("((SkOpSegment*) 0x%p) [%d] ", segment, segment->debugID());
-        SkDebugf("spanIndex:%d ", oSpan.fOtherIndex);
-        span->dumpOne();
-    }
-}
-
-// this does not require that other T index is initialized or correct
-const SkOpSegment* SkOpSpan::debugToSegment(ptrdiff_t* spanIndex) const {
-    if (!fOther) {
-        return NULL;
-    }
-    int oppCount = fOther->count();
-    for (int index = 0; index < oppCount; ++index) {
-        const SkOpSpan& otherSpan = fOther->span(index);
-        double otherTestT = otherSpan.fT;
-        if (otherTestT < fOtherT) {
-            continue;
-        }
-        SkASSERT(otherTestT == fOtherT);
-        const SkOpSegment* candidate = otherSpan.fOther;
-        const SkOpSpan* first = candidate->debugSpans().begin();
-        const SkOpSpan* last = candidate->debugSpans().end() - 1;
-        if (first <= this && this <= last) {
-            if (spanIndex) {
-                *spanIndex = this - first;
-            }
-            return candidate;
-        }
-    }
-    SkASSERT(0);
-    return NULL;
-}
-
-void SkOpSpan::dumpOne() const {
-    SkDebugf("t=");
-    DebugDumpDouble(fT);
-    SkDebugf(" pt=");
-    SkDPoint::Dump(fPt);
-    if (fOther) {
-        SkDebugf(" other.fID=%d", fOther->debugID());
-        SkDebugf(" [%d] otherT=", fOtherIndex);
-        DebugDumpDouble(fOtherT);
-    } else {
-        SkDebugf(" other.fID=? [?] otherT=?");
-    }
-    if (fWindSum != SK_MinS32) {
-        SkDebugf(" windSum=%d", fWindSum);
-    }
-    if (fOppSum != SK_MinS32 && (SkPathOpsDebug::ValidWind(fOppSum) || fOppValue != 0)) {
-        SkDebugf(" oppSum=%d", fOppSum);
-    }
-    SkDebugf(" windValue=%d", fWindValue);
-    if (SkPathOpsDebug::ValidWind(fOppSum) || fOppValue != 0) {
-        SkDebugf(" oppValue=%d", fOppValue);
-    }
-    if (fFromAngle && fFromAngle->debugID()) {
-        SkDebugf(" from=%d", fFromAngle->debugID());
-    }
-    if (fToAngle && fToAngle->debugID()) {
-        SkDebugf(" to=%d", fToAngle->debugID());
-    }
-    if (fChased) {
-        SkDebugf(" chased");
-    }
-    if (fCoincident) {
-        SkDebugf(" coincident");
-    }
-    if (fDone) {
-        SkDebugf(" done");
-    }
-    if (fLoop) {
-        SkDebugf(" loop");
-    }
-    if (fMultiple) {
-        SkDebugf(" multiple");
-    }
-    if (fNear) {
-        SkDebugf(" near");
-    }
-    if (fSmall) {
-        SkDebugf(" small");
-    }
-    if (fTiny) {
-        SkDebugf(" tiny");
     }
     SkDebugf("\n");
 }
 
-void SkOpSpan::dump() const {
-    ptrdiff_t spanIndex;
-    const SkOpSegment* segment = debugToSegment(&spanIndex);
-    if (segment) {
-        SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", segment, segment->debugID());
-        SkDebugf("  [%d] ", spanIndex);
-    } else {
-        SkDebugf("((SkOpSegment*) ?) [?]\n");
-        SkDebugf("  [?] ");
+const SkOpAngle* SkPathOpsDebug::DebugAngleAngle(const SkOpAngle* angle, int id) {
+    return angle->debugAngle(id);
+}
+
+SkOpContour* SkPathOpsDebug::DebugAngleContour(SkOpAngle* angle, int id) {
+    return angle->debugContour(id);
+}
+
+const SkOpPtT* SkPathOpsDebug::DebugAnglePtT(const SkOpAngle* angle, int id) {
+    return angle->debugPtT(id);
+}
+
+const SkOpSegment* SkPathOpsDebug::DebugAngleSegment(const SkOpAngle* angle, int id) {
+    return angle->debugSegment(id);
+}
+
+const SkOpSpanBase* SkPathOpsDebug::DebugAngleSpan(const SkOpAngle* angle, int id) {
+    return angle->debugSpan(id);
+}
+
+const SkOpAngle* SkPathOpsDebug::DebugContourAngle(SkOpContour* contour, int id) {
+    return contour->debugAngle(id);
+}
+
+SkOpContour* SkPathOpsDebug::DebugContourContour(SkOpContour* contour, int id) {
+    return contour->debugContour(id);
+}
+
+const SkOpPtT* SkPathOpsDebug::DebugContourPtT(SkOpContour* contour, int id) {
+    return contour->debugPtT(id);
+}
+
+const SkOpSegment* SkPathOpsDebug::DebugContourSegment(SkOpContour* contour, int id) {
+    return contour->debugSegment(id);
+}
+
+const SkOpSpanBase* SkPathOpsDebug::DebugContourSpan(SkOpContour* contour, int id) {
+    return contour->debugSpan(id);
+}
+
+const SkOpAngle* SkPathOpsDebug::DebugCoincidenceAngle(SkOpCoincidence* coin, int id) {
+    return coin->debugAngle(id);
+}
+
+SkOpContour* SkPathOpsDebug::DebugCoincidenceContour(SkOpCoincidence* coin, int id) {
+    return coin->debugContour(id);
+}
+
+const SkOpPtT* SkPathOpsDebug::DebugCoincidencePtT(SkOpCoincidence* coin, int id) {
+    return coin->debugPtT(id);
+}
+
+const SkOpSegment* SkPathOpsDebug::DebugCoincidenceSegment(SkOpCoincidence* coin, int id) {
+    return coin->debugSegment(id);
+}
+
+const SkOpSpanBase* SkPathOpsDebug::DebugCoincidenceSpan(SkOpCoincidence* coin, int id) {
+    return coin->debugSpan(id);
+}
+
+const SkOpAngle* SkPathOpsDebug::DebugPtTAngle(const SkOpPtT* ptT, int id) {
+    return ptT->debugAngle(id);
+}
+
+SkOpContour* SkPathOpsDebug::DebugPtTContour(SkOpPtT* ptT, int id) {
+    return ptT->debugContour(id);
+}
+
+const SkOpPtT* SkPathOpsDebug::DebugPtTPtT(const SkOpPtT* ptT, int id) {
+    return ptT->debugPtT(id);
+}
+
+const SkOpSegment* SkPathOpsDebug::DebugPtTSegment(const SkOpPtT* ptT, int id) {
+    return ptT->debugSegment(id);
+}
+
+const SkOpSpanBase* SkPathOpsDebug::DebugPtTSpan(const SkOpPtT* ptT, int id) {
+    return ptT->debugSpan(id);
+}
+
+const SkOpAngle* SkPathOpsDebug::DebugSegmentAngle(const SkOpSegment* span, int id) {
+    return span->debugAngle(id);
+}
+
+SkOpContour* SkPathOpsDebug::DebugSegmentContour(SkOpSegment* span, int id) {
+    return span->debugContour(id);
+}
+
+const SkOpPtT* SkPathOpsDebug::DebugSegmentPtT(const SkOpSegment* span, int id) {
+    return span->debugPtT(id);
+}
+
+const SkOpSegment* SkPathOpsDebug::DebugSegmentSegment(const SkOpSegment* span, int id) {
+    return span->debugSegment(id);
+}
+
+const SkOpSpanBase* SkPathOpsDebug::DebugSegmentSpan(const SkOpSegment* span, int id) {
+    return span->debugSpan(id);
+}
+
+const SkOpAngle* SkPathOpsDebug::DebugSpanAngle(const SkOpSpanBase* span, int id) {
+    return span->debugAngle(id);
+}
+
+SkOpContour* SkPathOpsDebug::DebugSpanContour(SkOpSpanBase* span, int id) {
+    return span->debugContour(id);
+}
+
+const SkOpPtT* SkPathOpsDebug::DebugSpanPtT(const SkOpSpanBase* span, int id) {
+    return span->debugPtT(id);
+}
+
+const SkOpSegment* SkPathOpsDebug::DebugSpanSegment(const SkOpSpanBase* span, int id) {
+    return span->debugSegment(id);
+}
+
+const SkOpSpanBase* SkPathOpsDebug::DebugSpanSpan(const SkOpSpanBase* span, int id) {
+    return span->debugSpan(id);
+}
+
+#if DEBUG_COIN
+void SkPathOpsDebug::DumpCoinDict() {
+    gCoinSumChangedDict.dump("unused coin algorithm", false);
+    gCoinSumVisitedDict.dump("visited coin function", true);
+}
+
+void SkPathOpsDebug::CoinDict::dump(const char* str, bool visitCheck) const {
+    int count = fDict.count();
+    for (int index = 0; index < count; ++index) {
+        const auto& entry = fDict[index];
+        if (visitCheck || entry.fGlitchType == kUninitialized_Glitch) {
+            SkDebugf("%s %s : line %d iteration %d", str, entry.fFunctionName,
+                    entry.fLineNumber, entry.fIteration);
+            DumpGlitchType(entry.fGlitchType);
+            SkDebugf("\n");
+        }
     }
-    dumpOne();
+}
+#endif
+
+void SkOpContour::dumpContours() const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dump();
+    } while ((contour = contour->next()));
 }
 
-void Dump(const SkTArray<class SkOpContour, true>& contours) {
-    SkPathOpsDebug::DumpContours(contours);
+void SkOpContour::dumpContoursAll() const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpAll();
+    } while ((contour = contour->next()));
 }
 
-void Dump(const SkTArray<class SkOpContour* , true>& contours) {
-    SkPathOpsDebug::DumpContours(contours);
+void SkOpContour::dumpContoursAngles() const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpAngles();
+    } while ((contour = contour->next()));
 }
 
-void Dump(const SkTArray<class SkOpContour, true>* contours) {
-    SkPathOpsDebug::DumpContours(*contours);
+void SkOpContour::dumpContoursPts() const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpPts();
+    } while ((contour = contour->next()));
 }
 
-void Dump(const SkTArray<class SkOpContour* , true>* contours) {
-    SkPathOpsDebug::DumpContours(*contours);
+void SkOpContour::dumpContoursPt(int segmentID) const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpPt(segmentID);
+    } while ((contour = contour->next()));
 }
 
-void Dump(const SkTDArray<SkOpSpan *>& chase) {
-    SkPathOpsDebug::DumpSpans(chase);
+void SkOpContour::dumpContoursSegment(int segmentID) const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpSegment(segmentID);
+    } while ((contour = contour->next()));
 }
 
-void Dump(const SkTDArray<SkOpSpan *>* chase) {
-    SkPathOpsDebug::DumpSpans(*chase);
+void SkOpContour::dumpContoursSpan(int spanID) const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpSpan(spanID);
+    } while ((contour = contour->next()));
 }
 
-void DumpAngles(const SkTArray<class SkOpContour, true>& contours) {
-    SkPathOpsDebug::DumpContourAngles(contours);
+void SkOpContour::dumpContoursSpans() const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpSpans();
+    } while ((contour = contour->next()));
 }
 
-void DumpAngles(const SkTArray<class SkOpContour* , true>& contours) {
-    SkPathOpsDebug::DumpContourAngles(contours);
+template <typename TCurve, typename OppCurve>
+const SkTSpan<TCurve, OppCurve>* DebugSpan(const SkTSect<TCurve, OppCurve>* sect, int id) {
+    return sect->debugSpan(id);
 }
 
-void DumpAngles(const SkTArray<class SkOpContour, true>* contours) {
-    SkPathOpsDebug::DumpContourAngles(*contours);
+void DontCallDebugSpan(int id);
+void DontCallDebugSpan(int id) {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DebugSpan(&q1q2, id);
+    DebugSpan(&q1k2, id);
+    DebugSpan(&q1c2, id);
+    DebugSpan(&k1q2, id);
+    DebugSpan(&k1k2, id);
+    DebugSpan(&k1c2, id);
+    DebugSpan(&c1q2, id);
+    DebugSpan(&c1k2, id);
+    DebugSpan(&c1c2, id);
 }
 
-void DumpAngles(const SkTArray<class SkOpContour* , true>* contours) {
-    SkPathOpsDebug::DumpContourAngles(*contours);
+template <typename TCurve, typename OppCurve>
+const SkTSpan<TCurve, OppCurve>* DebugT(const SkTSect<TCurve, OppCurve>* sect, double t) {
+    return sect->debugT(t);
 }
 
-void DumpCoin(const SkTArray<class SkOpContour, true>& contours) {
-    SkPathOpsDebug::DumpCoincidence(contours);
+void DontCallDebugT(double t);
+void DontCallDebugT(double t) {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DebugT(&q1q2, t);
+    DebugT(&q1k2, t);
+    DebugT(&q1c2, t);
+    DebugT(&k1q2, t);
+    DebugT(&k1k2, t);
+    DebugT(&k1c2, t);
+    DebugT(&c1q2, t);
+    DebugT(&c1k2, t);
+    DebugT(&c1c2, t);
 }
 
-void DumpCoin(const SkTArray<class SkOpContour* , true>& contours) {
-    SkPathOpsDebug::DumpCoincidence(contours);
+template <typename TCurve, typename OppCurve>
+void Dump(const SkTSect<TCurve, OppCurve>* sect) {
+    sect->dump();
 }
 
-void DumpCoin(const SkTArray<class SkOpContour, true>* contours) {
-    SkPathOpsDebug::DumpCoincidence(*contours);
+void DontCallDumpTSect();
+void DontCallDumpTSect() {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    Dump(&q1q2);
+    Dump(&q1k2);
+    Dump(&q1c2);
+    Dump(&k1q2);
+    Dump(&k1k2);
+    Dump(&k1c2);
+    Dump(&c1q2);
+    Dump(&c1k2);
+    Dump(&c1c2);
 }
 
-void DumpCoin(const SkTArray<class SkOpContour* , true>* contours) {
-    SkPathOpsDebug::DumpCoincidence(*contours);
+template <typename TCurve, typename OppCurve>
+void DumpBoth(SkTSect<TCurve, OppCurve>* sect1, SkTSect<OppCurve, TCurve>* sect2) {
+    sect1->dumpBoth(sect2);
 }
 
-void DumpSpans(const SkTArray<class SkOpContour, true>& contours) {
-    SkPathOpsDebug::DumpContourSpans(contours);
+void DontCallDumpBoth();
+void DontCallDumpBoth() {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DumpBoth(&q1q2, &q1q2);
+    DumpBoth(&q1k2, &k1q2);
+    DumpBoth(&q1c2, &c1q2);
+    DumpBoth(&k1q2, &q1k2);
+    DumpBoth(&k1k2, &k1k2);
+    DumpBoth(&k1c2, &c1k2);
+    DumpBoth(&c1q2, &q1c2);
+    DumpBoth(&c1k2, &k1c2);
+    DumpBoth(&c1c2, &c1c2);
 }
 
-void DumpSpans(const SkTArray<class SkOpContour* , true>& contours) {
-    SkPathOpsDebug::DumpContourSpans(contours);
+template <typename TCurve, typename OppCurve>
+void DumpBounded(SkTSect<TCurve, OppCurve>* sect1, int id) {
+    sect1->dumpBounded(id);
 }
 
-void DumpSpans(const SkTArray<class SkOpContour, true>* contours) {
-    SkPathOpsDebug::DumpContourSpans(*contours);
+void DontCallDumpBounded();
+void DontCallDumpBounded() {
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DumpBounded(&q1q2, 0);
+    DumpBounded(&q1k2, 0);
+    DumpBounded(&q1c2, 0);
+    DumpBounded(&k1q2, 0);
+    DumpBounded(&k1k2, 0);
+    DumpBounded(&k1c2, 0);
+    DumpBounded(&c1q2, 0);
+    DumpBounded(&c1k2, 0);
+    DumpBounded(&c1c2, 0);
 }
 
-void DumpSpans(const SkTArray<class SkOpContour* , true>* contours) {
-    SkPathOpsDebug::DumpContourSpans(*contours);
+template <typename TCurve, typename OppCurve>
+void DumpBounds(SkTSect<TCurve, OppCurve>* sect1) {
+    sect1->dumpBounds();
 }
 
-void DumpSpan(const SkTArray<class SkOpContour, true>& contours, int segmentID) {
-    SkPathOpsDebug::DumpContourSpan(contours, segmentID);
+void DontCallDumpBounds();
+void DontCallDumpBounds() {
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DumpBounds(&q1q2);
+    DumpBounds(&q1k2);
+    DumpBounds(&q1c2);
+    DumpBounds(&k1q2);
+    DumpBounds(&k1k2);
+    DumpBounds(&k1c2);
+    DumpBounds(&c1q2);
+    DumpBounds(&c1k2);
+    DumpBounds(&c1c2);
 }
 
-void DumpSpan(const SkTArray<class SkOpContour* , true>& contours, int segmentID) {
-    SkPathOpsDebug::DumpContourSpan(contours, segmentID);
+template <typename TCurve, typename OppCurve>
+void DumpCoin(SkTSect<TCurve, OppCurve>* sect1) {
+    sect1->dumpCoin();
 }
 
-void DumpSpan(const SkTArray<class SkOpContour, true>* contours, int segmentID) {
-    SkPathOpsDebug::DumpContourSpan(*contours, segmentID);
+void DontCallDumpCoin();
+void DontCallDumpCoin() {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DumpCoin(&q1q2);
+    DumpCoin(&q1k2);
+    DumpCoin(&q1c2);
+    DumpCoin(&k1q2);
+    DumpCoin(&k1k2);
+    DumpCoin(&k1c2);
+    DumpCoin(&c1q2);
+    DumpCoin(&c1k2);
+    DumpCoin(&c1c2);
 }
 
-void DumpSpan(const SkTArray<class SkOpContour* , true>* contours, int segmentID) {
-    SkPathOpsDebug::DumpContourSpan(*contours, segmentID);
+template <typename TCurve, typename OppCurve>
+void DumpCoinCurves(SkTSect<TCurve, OppCurve>* sect1) {
+    sect1->dumpCoinCurves();
 }
 
-void DumpPts(const SkTArray<class SkOpContour, true>& contours) {
-    SkPathOpsDebug::DumpContourPts(contours);
+void DontCallDumpCoinCurves();
+void DontCallDumpCoinCurves() {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DumpCoinCurves(&q1q2);
+    DumpCoinCurves(&q1k2);
+    DumpCoinCurves(&q1c2);
+    DumpCoinCurves(&k1q2);
+    DumpCoinCurves(&k1k2);
+    DumpCoinCurves(&k1c2);
+    DumpCoinCurves(&c1q2);
+    DumpCoinCurves(&c1k2);
+    DumpCoinCurves(&c1c2);
 }
 
-void DumpPts(const SkTArray<class SkOpContour* , true>& contours) {
-    SkPathOpsDebug::DumpContourPts(contours);
+template <typename TCurve, typename OppCurve>
+void DumpCurves(const SkTSect<TCurve, OppCurve>* sect) {
+    sect->dumpCurves();
 }
 
-void DumpPts(const SkTArray<class SkOpContour, true>* contours) {
-    SkPathOpsDebug::DumpContourPts(*contours);
+void DontCallDumpCurves();
+void DontCallDumpCurves() {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic  SkDEBUGPARAMS(nullptr)  PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DumpCurves(&q1q2);
+    DumpCurves(&q1k2);
+    DumpCurves(&q1c2);
+    DumpCurves(&k1q2);
+    DumpCurves(&k1k2);
+    DumpCurves(&k1c2);
+    DumpCurves(&c1q2);
+    DumpCurves(&c1k2);
+    DumpCurves(&c1c2);
 }
 
-void DumpPts(const SkTArray<class SkOpContour* , true>* contours) {
-    SkPathOpsDebug::DumpContourPts(*contours);
+template <typename TCurve, typename OppCurve>
+void Dump(const SkTSpan<TCurve, OppCurve>* span) {
+    span->dump();
 }
 
-void DumpPt(const SkTArray<class SkOpContour, true>& contours, int segmentID) {
-    SkPathOpsDebug::DumpContourPt(contours, segmentID);
+void DontCallDumpTSpan();
+void DontCallDumpTSpan() {  // exists to instantiate the templates
+    SkTSpan<SkDQuad, SkDQuad> q1q2; q1q2.debugInit();
+    SkTSpan<SkDQuad, SkDConic> q1k2; q1k2.debugInit();
+    SkTSpan<SkDQuad, SkDCubic> q1c2; q1c2.debugInit();
+    SkTSpan<SkDConic, SkDQuad> k1q2; k1q2.debugInit();
+    SkTSpan<SkDConic, SkDConic> k1k2; k1k2.debugInit();
+    SkTSpan<SkDConic, SkDCubic> k1c2; k1c2.debugInit();
+    SkTSpan<SkDCubic, SkDQuad> c1q2; c1q2.debugInit();
+    SkTSpan<SkDCubic, SkDConic> c1k2; c1k2.debugInit();
+    SkTSpan<SkDCubic, SkDCubic> c1c2; c1c2.debugInit();
+    Dump(&q1q2);
+    Dump(&q1k2);
+    Dump(&q1c2);
+    Dump(&k1q2);
+    Dump(&k1k2);
+    Dump(&k1c2);
+    Dump(&c1q2);
+    Dump(&c1k2);
+    Dump(&c1c2);
 }
 
-void DumpPt(const SkTArray<class SkOpContour* , true>& contours, int segmentID) {
-    SkPathOpsDebug::DumpContourPt(contours, segmentID);
+template <typename TCurve, typename OppCurve>
+void DumpAll(const SkTSpan<TCurve, OppCurve>* span) {
+    span->dumpAll();
 }
 
-void DumpPt(const SkTArray<class SkOpContour, true>* contours, int segmentID) {
-    SkPathOpsDebug::DumpContourPt(*contours, segmentID);
+void DontCallDumpSpanAll();
+void DontCallDumpSpanAll() {  // exists to instantiate the templates
+    SkTSpan<SkDQuad, SkDQuad> q1q2; q1q2.debugInit();
+    SkTSpan<SkDQuad, SkDConic> q1k2; q1k2.debugInit();
+    SkTSpan<SkDQuad, SkDCubic> q1c2; q1c2.debugInit();
+    SkTSpan<SkDConic, SkDQuad> k1q2; k1q2.debugInit();
+    SkTSpan<SkDConic, SkDConic> k1k2; k1k2.debugInit();
+    SkTSpan<SkDConic, SkDCubic> k1c2; k1c2.debugInit();
+    SkTSpan<SkDCubic, SkDQuad> c1q2; c1q2.debugInit();
+    SkTSpan<SkDCubic, SkDConic> c1k2; c1k2.debugInit();
+    SkTSpan<SkDCubic, SkDCubic> c1c2; c1c2.debugInit();
+    DumpAll(&q1q2);
+    DumpAll(&q1k2);
+    DumpAll(&q1c2);
+    DumpAll(&k1q2);
+    DumpAll(&k1k2);
+    DumpAll(&k1c2);
+    DumpAll(&c1q2);
+    DumpAll(&c1k2);
+    DumpAll(&c1c2);
 }
 
-void DumpPt(const SkTArray<class SkOpContour* , true>* contours, int segmentID) {
-    SkPathOpsDebug::DumpContourPt(*contours, segmentID);
+template <typename TCurve, typename OppCurve>
+void DumpBounded(const SkTSpan<TCurve, OppCurve>* span) {
+    span->dumpBounded(0);
+}
+
+void DontCallDumpSpanBounded();
+void DontCallDumpSpanBounded() {  // exists to instantiate the templates
+    SkTSpan<SkDQuad, SkDQuad> q1q2; q1q2.debugInit();
+    SkTSpan<SkDQuad, SkDConic> q1k2; q1k2.debugInit();
+    SkTSpan<SkDQuad, SkDCubic> q1c2; q1c2.debugInit();
+    SkTSpan<SkDConic, SkDQuad> k1q2; k1q2.debugInit();
+    SkTSpan<SkDConic, SkDConic> k1k2; k1k2.debugInit();
+    SkTSpan<SkDConic, SkDCubic> k1c2; k1c2.debugInit();
+    SkTSpan<SkDCubic, SkDQuad> c1q2; c1q2.debugInit();
+    SkTSpan<SkDCubic, SkDConic> c1k2; c1k2.debugInit();
+    SkTSpan<SkDCubic, SkDCubic> c1c2; c1c2.debugInit();
+    DumpBounded(&q1q2);
+    DumpBounded(&q1k2);
+    DumpBounded(&q1c2);
+    DumpBounded(&k1q2);
+    DumpBounded(&k1k2);
+    DumpBounded(&k1c2);
+    DumpBounded(&c1q2);
+    DumpBounded(&c1k2);
+    DumpBounded(&c1c2);
+}
+
+template <typename TCurve, typename OppCurve>
+void DumpCoin(const SkTSpan<TCurve, OppCurve>* span) {
+    span->dumpCoin();
+}
+
+void DontCallDumpSpanCoin();
+void DontCallDumpSpanCoin() {  // exists to instantiate the templates
+    SkTSpan<SkDQuad, SkDQuad> q1q2; q1q2.debugInit();
+    SkTSpan<SkDQuad, SkDConic> q1k2; q1k2.debugInit();
+    SkTSpan<SkDQuad, SkDCubic> q1c2; q1c2.debugInit();
+    SkTSpan<SkDConic, SkDQuad> k1q2; k1q2.debugInit();
+    SkTSpan<SkDConic, SkDConic> k1k2; k1k2.debugInit();
+    SkTSpan<SkDConic, SkDCubic> k1c2; k1c2.debugInit();
+    SkTSpan<SkDCubic, SkDQuad> c1q2; c1q2.debugInit();
+    SkTSpan<SkDCubic, SkDConic> c1k2; c1k2.debugInit();
+    SkTSpan<SkDCubic, SkDCubic> c1c2; c1c2.debugInit();
+    DumpCoin(&q1q2);
+    DumpCoin(&q1k2);
+    DumpCoin(&q1c2);
+    DumpCoin(&k1q2);
+    DumpCoin(&k1k2);
+    DumpCoin(&k1c2);
+    DumpCoin(&c1q2);
+    DumpCoin(&c1k2);
+    DumpCoin(&c1c2);
 }
 
 static void dumpTestCase(const SkDQuad& quad1, const SkDQuad& quad2, int testNo) {
-    SkDebugf("<div id=\"quad%d\">\n", testNo);
-    quad1.dumpComma(",");
+    SkDebugf("\n<div id=\"quad%d\">\n", testNo);
+    quad1.dumpInner();
+    SkDebugf("}}, ");
     quad2.dump();
     SkDebugf("</div>\n\n");
 }
@@ -873,3 +815,667 @@
     SkDLine line = {{quad.ptAtT(t), quad[0]}};
     line.dump();
 }
+
+const SkOpAngle* SkOpAngle::debugAngle(int id) const {
+    return this->segment()->debugAngle(id);
+}
+
+const SkOpCoincidence* SkOpAngle::debugCoincidence() const {
+    return this->segment()->debugCoincidence();
+}
+
+SkOpContour* SkOpAngle::debugContour(int id) const {
+    return this->segment()->debugContour(id);
+}
+
+const SkOpPtT* SkOpAngle::debugPtT(int id) const {
+    return this->segment()->debugPtT(id);
+}
+
+const SkOpSegment* SkOpAngle::debugSegment(int id) const {
+    return this->segment()->debugSegment(id);
+}
+
+int SkOpAngle::debugSign() const {
+    SkASSERT(fStart->t() != fEnd->t());
+    return fStart->t() < fEnd->t() ? -1 : 1;
+}
+
+const SkOpSpanBase* SkOpAngle::debugSpan(int id) const {
+    return this->segment()->debugSpan(id);
+}
+
+void SkOpAngle::dump() const {
+    dumpOne(true);
+    SkDebugf("\n");
+}
+
+void SkOpAngle::dumpOne(bool functionHeader) const {
+//    fSegment->debugValidate();
+    const SkOpSegment* segment = this->segment();
+    const SkOpSpan& mSpan = *fStart->starter(fEnd);
+    if (functionHeader) {
+        SkDebugf("%s ", __FUNCTION__);
+    }
+    SkDebugf("[%d", segment->debugID());
+    SkDebugf("/%d", debugID());
+    SkDebugf("] next=");
+    if (fNext) {
+        SkDebugf("%d", fNext->fStart->segment()->debugID());
+        SkDebugf("/%d", fNext->debugID());
+    } else {
+        SkDebugf("?");
+    }
+    SkDebugf(" sect=%d/%d ", fSectorStart, fSectorEnd);
+    SkDebugf(" s=%1.9g [%d] e=%1.9g [%d]", fStart->t(), fStart->debugID(),
+                fEnd->t(), fEnd->debugID());
+    SkDebugf(" sgn=%d windVal=%d", this->debugSign(), mSpan.windValue());
+
+    SkDebugf(" windSum=");
+    SkPathOpsDebug::WindingPrintf(mSpan.windSum());
+    if (mSpan.oppValue() != 0 || mSpan.oppSum() != SK_MinS32) {
+        SkDebugf(" oppVal=%d", mSpan.oppValue());
+        SkDebugf(" oppSum=");
+        SkPathOpsDebug::WindingPrintf(mSpan.oppSum());
+    }
+    if (mSpan.done()) {
+        SkDebugf(" done");
+    }
+    if (unorderable()) {
+        SkDebugf(" unorderable");
+    }
+    if (segment->operand()) {
+        SkDebugf(" operand");
+    }
+}
+
+void SkOpAngle::dumpTo(const SkOpSegment* segment, const SkOpAngle* to) const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = this;
+    const char* indent = "";
+    do {
+        SkDebugf("%s", indent);
+        next->dumpOne(false);
+        if (segment == next->fStart->segment()) {
+            if (this == fNext) {
+                SkDebugf(" << from");
+            }
+            if (to == fNext) {
+                SkDebugf(" << to");
+            }
+        }
+        SkDebugf("\n");
+        indent = "           ";
+        next = next->fNext;
+    } while (next && next != first);
+}
+
+void SkOpAngle::dumpCurves() const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = this;
+    do {
+        next->fPart.fCurve.dumpID(next->segment()->debugID());
+        next = next->fNext;
+    } while (next && next != first);
+}
+
+void SkOpAngle::dumpLoop() const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = this;
+    do {
+        next->dumpOne(false);
+        SkDebugf("\n");
+        next = next->fNext;
+    } while (next && next != first);
+}
+
+void SkOpAngle::dumpTest() const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = this;
+    do {
+        SkDebugf("{ ");
+        SkOpSegment* segment = next->segment();
+        segment->dumpPts();
+        SkDebugf(", %d, %1.9g, %1.9g, {} },\n", SkPathOpsVerbToPoints(segment->verb()) + 1,
+                next->start()->t(), next->end()->t());
+        next = next->fNext;
+    } while (next && next != first);
+}
+
+bool SkOpPtT::debugMatchID(int id) const {
+    int limit = this->debugLoopLimit(false);
+    int loop = 0;
+    const SkOpPtT* ptT = this;
+    do {
+        if (ptT->debugID() == id) {
+            return true;
+        }
+    } while ((!limit || ++loop <= limit) && (ptT = ptT->next()) && ptT != this);
+    return false;
+}
+
+const SkOpAngle* SkOpPtT::debugAngle(int id) const {
+    return this->span()->debugAngle(id);
+}
+
+SkOpContour* SkOpPtT::debugContour(int id) const {
+    return this->span()->debugContour(id);
+}
+
+const SkOpCoincidence* SkOpPtT::debugCoincidence() const {
+    return this->span()->debugCoincidence();
+}
+
+const SkOpPtT* SkOpPtT::debugPtT(int id) const {
+    return this->span()->debugPtT(id);
+}
+
+const SkOpSegment* SkOpPtT::debugSegment(int id) const {
+    return this->span()->debugSegment(id);
+}
+
+const SkOpSpanBase* SkOpPtT::debugSpan(int id) const {
+    return this->span()->debugSpan(id);
+}
+
+void SkOpPtT::dump() const {
+    SkDebugf("seg=%d span=%d ptT=%d",
+            this->segment()->debugID(), this->span()->debugID(), this->debugID());
+    this->dumpBase();
+    SkDebugf("\n");
+}
+
+void SkOpPtT::dumpAll() const {
+    contour()->indentDump();
+    const SkOpPtT* next = this;
+    int limit = debugLoopLimit(true);
+    int loop = 0;
+    do {
+        SkDebugf("%.*s", contour()->debugIndent(), "        ");
+        SkDebugf("seg=%d span=%d ptT=%d",
+                next->segment()->debugID(), next->span()->debugID(), next->debugID());
+        next->dumpBase();
+        SkDebugf("\n");
+        if (limit && ++loop >= limit) {
+            SkDebugf("*** abort loop ***\n");
+            break;
+        }
+    } while ((next = next->fNext) && next != this);
+    contour()->outdentDump();
+}
+
+void SkOpPtT::dumpBase() const {
+    SkDebugf(" t=%1.9g pt=(%1.9g,%1.9g)%s%s%s", this->fT, this->fPt.fX, this->fPt.fY,
+            this->fCoincident ? " coin" : "",
+            this->fDuplicatePt ? " dup" : "", this->fDeleted ? " deleted" : "");
+}
+
+const SkOpAngle* SkOpSpanBase::debugAngle(int id) const {
+    return this->segment()->debugAngle(id);
+}
+
+const SkOpCoincidence* SkOpSpanBase::debugCoincidence() const {
+    return this->segment()->debugCoincidence();
+}
+
+SkOpContour* SkOpSpanBase::debugContour(int id) const {
+    return this->segment()->debugContour(id);
+}
+
+const SkOpPtT* SkOpSpanBase::debugPtT(int id) const {
+    return this->segment()->debugPtT(id);
+}
+
+const SkOpSegment* SkOpSpanBase::debugSegment(int id) const {
+    return this->segment()->debugSegment(id);
+}
+
+const SkOpSpanBase* SkOpSpanBase::debugSpan(int id) const {
+    return this->segment()->debugSpan(id);
+}
+
+void SkOpSpanBase::dump() const {
+    this->dumpHead();
+    this->fPtT.dump();
+}
+
+void SkOpSpanBase::dumpHead() const {
+    SkDebugf("%.*s", contour()->debugIndent(), "        ");
+    SkDebugf("seg=%d span=%d", this->segment()->debugID(), this->debugID());
+    this->dumpBase();
+    SkDebugf("\n");
+}
+
+void SkOpSpanBase::dumpAll() const {
+    this->dumpHead();
+    this->fPtT.dumpAll();
+}
+
+void SkOpSpanBase::dumpBase() const {
+    if (this->fAligned) {
+        SkDebugf(" aligned");
+    }
+    if (this->fChased) {
+        SkDebugf(" chased");
+    }
+#ifdef SK_DEBUG
+    if (this->fDebugDeleted) {
+        SkDebugf(" deleted");
+    }
+#endif
+    if (!this->final()) {
+        this->upCast()->dumpSpan();
+    }
+    const SkOpSpanBase* coin = this->coinEnd();
+    if (this != coin) {
+        SkDebugf(" coinEnd seg/span=%d/%d", coin->segment()->debugID(), coin->debugID());
+    } else if (this->final() || !this->upCast()->isCoincident()) {
+        const SkOpPtT* oPt = this->ptT()->next();
+        SkDebugf(" seg/span=%d/%d", oPt->segment()->debugID(), oPt->span()->debugID());
+    }
+    SkDebugf(" adds=%d", fSpanAdds);
+}
+
+void SkOpSpanBase::dumpCoin() const {
+    const SkOpSpan* span = this->upCastable();
+    if (!span) {
+        return;
+    }
+    if (!span->isCoincident()) {
+        return;
+    }
+    span->dumpCoin();
+}
+
+void SkOpSpan::dumpCoin() const {
+    const SkOpSpan* coincident = fCoincident;
+    bool ok = debugCoinLoopCheck();
+    this->dump();
+    int loop = 0;
+    do {
+        coincident->dump();
+        if (!ok && ++loop > 10) {
+            SkDebugf("*** abort loop ***\n");
+            break;
+        }
+    } while ((coincident = coincident->fCoincident) != this);
+}
+
+bool SkOpSpan::dumpSpan() const {
+    SkOpSpan* coin = fCoincident;
+    if (this != coin) {
+        SkDebugf(" coinStart seg/span=%d/%d", coin->segment()->debugID(), coin->debugID());
+    }
+    SkDebugf(" windVal=%d", this->windValue());
+    SkDebugf(" windSum=");
+    SkPathOpsDebug::WindingPrintf(this->windSum());
+    if (this->oppValue() != 0 || this->oppSum() != SK_MinS32) {
+        SkDebugf(" oppVal=%d", this->oppValue());
+        SkDebugf(" oppSum=");
+        SkPathOpsDebug::WindingPrintf(this->oppSum());
+    }
+    if (this->done()) {
+        SkDebugf(" done");
+    }
+    return this != coin;
+}
+
+const SkOpAngle* SkOpSegment::debugAngle(int id) const {
+    return this->contour()->debugAngle(id);
+}
+
+
+const SkOpCoincidence* SkOpSegment::debugCoincidence() const {
+    return this->contour()->debugCoincidence();
+}
+
+SkOpContour* SkOpSegment::debugContour(int id) const {
+    return this->contour()->debugContour(id);
+}
+
+const SkOpPtT* SkOpSegment::debugPtT(int id) const {
+    return this->contour()->debugPtT(id);
+}
+
+const SkOpSegment* SkOpSegment::debugSegment(int id) const {
+    return this->contour()->debugSegment(id);
+}
+
+const SkOpSpanBase* SkOpSegment::debugSpan(int id) const {
+    return this->contour()->debugSpan(id);
+}
+
+void SkOpSegment::dump() const {
+    SkDebugf("%.*s", contour()->debugIndent(), "        ");
+    this->dumpPts();
+    const SkOpSpanBase* span = &fHead;
+    contour()->indentDump();
+    do {
+        SkDebugf("%.*s span=%d ", contour()->debugIndent(), "        ", span->debugID());
+        span->ptT()->dumpBase();
+        span->dumpBase();
+        SkDebugf("\n");
+    } while (!span->final() && (span = span->upCast()->next()));
+    contour()->outdentDump();
+}
+
+void SkOpSegment::dumpAll() const {
+    SkDebugf("%.*s", contour()->debugIndent(), "        ");
+    this->dumpPts();
+    const SkOpSpanBase* span = &fHead;
+    contour()->indentDump();
+    do {
+        span->dumpAll();
+    } while (!span->final() && (span = span->upCast()->next()));
+    contour()->outdentDump();
+}
+
+void SkOpSegment::dumpAngles() const {
+    SkDebugf("seg=%d\n", debugID());
+    const SkOpSpanBase* span = &fHead;
+    do {
+        const SkOpAngle* fAngle = span->fromAngle();
+        const SkOpAngle* tAngle = span->final() ? nullptr : span->upCast()->toAngle();
+        if (fAngle) {
+            SkDebugf("  span=%d from=%d ", span->debugID(), fAngle->debugID());
+            fAngle->dumpTo(this, tAngle);
+        }
+        if (tAngle) {
+            SkDebugf("  span=%d to=%d   ", span->debugID(), tAngle->debugID());
+            tAngle->dumpTo(this, fAngle);
+        }
+    } while (!span->final() && (span = span->upCast()->next()));
+}
+
+void SkOpSegment::dumpCoin() const {
+    const SkOpSpan* span = &fHead;
+    do {
+        span->dumpCoin();
+    } while ((span = span->next()->upCastable()));
+}
+
+void SkOpSegment::dumpPtsInner(const char* prefix) const {
+    int last = SkPathOpsVerbToPoints(fVerb);
+    SkDebugf("%s=%d {{", prefix, this->debugID());
+    if (fVerb == SkPath::kConic_Verb) {
+        SkDebugf("{");
+    }
+    int index = 0;
+    do {
+        SkDPoint::Dump(fPts[index]);
+        SkDebugf(", ");
+    } while (++index < last);
+    SkDPoint::Dump(fPts[index]);
+    SkDebugf("}}");
+    if (fVerb == SkPath::kConic_Verb) {
+        SkDebugf(", %1.9gf}", fWeight);
+    }
+}
+
+void SkOpSegment::dumpPts(const char* prefix) const {
+    dumpPtsInner(prefix);
+    SkDebugf("\n");
+}
+
+void SkCoincidentSpans::dump() const {
+    SkDebugf("- seg=%d span=%d ptT=%d ", fCoinPtTStart->segment()->debugID(),
+        fCoinPtTStart->span()->debugID(), fCoinPtTStart->debugID());
+    fCoinPtTStart->dumpBase();
+    SkDebugf(" span=%d ptT=%d ", fCoinPtTEnd->span()->debugID(), fCoinPtTEnd->debugID());
+    fCoinPtTEnd->dumpBase();
+    if (fCoinPtTStart->segment()->operand()) {
+        SkDebugf(" operand");
+    }
+    if (fCoinPtTStart->segment()->isXor()) {
+        SkDebugf(" xor");
+    }
+    SkDebugf("\n");
+    SkDebugf("+ seg=%d span=%d ptT=%d ", fOppPtTStart->segment()->debugID(),
+        fOppPtTStart->span()->debugID(), fOppPtTStart->debugID());
+    fOppPtTStart->dumpBase();
+    SkDebugf(" span=%d ptT=%d ", fOppPtTEnd->span()->debugID(), fOppPtTEnd->debugID());
+    fOppPtTEnd->dumpBase();
+    if (fOppPtTStart->segment()->operand()) {
+        SkDebugf(" operand");
+    }
+    if (fOppPtTStart->segment()->isXor()) {
+        SkDebugf(" xor");
+    }
+    SkDebugf("\n");
+}
+
+void SkOpCoincidence::dump() const {
+    SkCoincidentSpans* span = fHead;
+    while (span) {
+        span->dump();
+        span = span->next();
+    }
+    if (!fTop || fHead == fTop) {
+        return;
+    }
+    SkDebugf("top:\n");
+    span = fTop;
+    int count = 0;
+    while (span) {
+        span->dump();
+        span = span->next();
+        SkCoincidentSpans* check = fTop;
+        ++count;
+        for (int index = 0; index < count; ++index) {
+            if (span == check) {
+                SkDebugf("(loops to #%d)\n", index);
+                return;
+            }
+            check = check->next();
+        }
+    }
+}
+
+void SkOpContour::dump() const {
+    SkDebugf("contour=%d count=%d op=%d xor=%d\n", this->debugID(), fCount, fOperand, fXor);
+    if (!fCount) {
+        return;
+    }
+    const SkOpSegment* segment = &fHead;
+    SkDEBUGCODE(fDebugIndent = 0);
+    this->indentDump();
+    do {
+        segment->dump();
+    } while ((segment = segment->next()));
+    this->outdentDump();
+}
+
+void SkOpContour::dumpAll() const {
+    SkDebugf("contour=%d count=%d op=%d xor=%d\n", this->debugID(), fCount, fOperand, fXor);
+    if (!fCount) {
+        return;
+    }
+    const SkOpSegment* segment = &fHead;
+    SkDEBUGCODE(fDebugIndent = 0);
+    this->indentDump();
+    do {
+        segment->dumpAll();
+    } while ((segment = segment->next()));
+    this->outdentDump();
+}
+
+
+void SkOpContour::dumpAngles() const {
+    SkDebugf("contour=%d\n", this->debugID());
+    const SkOpSegment* segment = &fHead;
+    do {
+        SkDebugf("  seg=%d ", segment->debugID());
+        segment->dumpAngles();
+    } while ((segment = segment->next()));
+}
+
+void SkOpContour::dumpPt(int index) const {
+    const SkOpSegment* segment = &fHead;
+    do {
+        if (segment->debugID() == index) {
+            segment->dumpPts();
+        }
+    } while ((segment = segment->next()));
+}
+
+void SkOpContour::dumpPts(const char* prefix) const {
+    SkDebugf("contour=%d\n", this->debugID());
+    const SkOpSegment* segment = &fHead;
+    do {
+        SkDebugf("  %s=%d ", prefix, segment->debugID());
+        segment->dumpPts(prefix);
+    } while ((segment = segment->next()));
+}
+
+void SkOpContour::dumpPtsX(const char* prefix) const {
+    if (!this->fCount) {
+        SkDebugf("<empty>\n");
+        return;
+    }
+    const SkOpSegment* segment = &fHead;
+    do {
+        segment->dumpPts(prefix);
+    } while ((segment = segment->next()));
+}
+
+void SkOpContour::dumpSegment(int index) const {
+    debugSegment(index)->dump();
+}
+
+void SkOpContour::dumpSegments(const char* prefix, SkPathOp op) const {
+    bool firstOp = false;
+    const SkOpContour* c = this;
+    do {
+        if (!firstOp && c->operand() && op >= 0) {
+#if DEBUG_ACTIVE_OP
+            SkDebugf("op %s\n", SkPathOpsDebug::kPathOpStr[op]);
+#endif
+            firstOp = true;
+        }
+        c->dumpPtsX(prefix);
+    } while ((c = c->next()));
+}
+
+void SkOpContour::dumpSpan(int index) const {
+    debugSpan(index)->dump();
+}
+
+void SkOpContour::dumpSpans() const {
+    SkDebugf("contour=%d\n", this->debugID());
+    const SkOpSegment* segment = &fHead;
+    do {
+        SkDebugf("  seg=%d ", segment->debugID());
+        segment->dump();
+    } while ((segment = segment->next()));
+}
+
+void SkOpCurve::dump() const {
+    int count = SkPathOpsVerbToPoints(SkDEBUGRELEASE(fVerb, SkPath::kCubic_Verb));
+    SkDebugf("{{");
+    int index;
+    for (index = 0; index <= count - 1; ++index) {
+        SkDebugf("{%1.9gf,%1.9gf}, ", fPts[index].fX, fPts[index].fY);
+    }
+    SkDebugf("{%1.9gf,%1.9gf}}}\n", fPts[index].fX, fPts[index].fY);
+}
+
+#ifdef SK_DEBUG
+const SkOpAngle* SkOpGlobalState::debugAngle(int id) const {
+    const SkOpContour* contour = fContourHead;
+    do {
+        const SkOpSegment* segment = contour->first();
+        while (segment) {
+            const SkOpSpan* span = segment->head();
+            do {
+                SkOpAngle* angle = span->fromAngle();
+                if (angle && angle->debugID() == id) {
+                    return angle;
+                }
+                angle = span->toAngle();
+                if (angle && angle->debugID() == id) {
+                    return angle;
+                }
+            } while ((span = span->next()->upCastable()));
+            const SkOpSpanBase* tail = segment->tail();
+            SkOpAngle* angle = tail->fromAngle();
+            if (angle && angle->debugID() == id) {
+                return angle;
+            }
+            segment = segment->next();
+        }
+    } while ((contour = contour->next()));
+    return nullptr;
+}
+
+SkOpContour* SkOpGlobalState::debugContour(int id) const {
+    SkOpContour* contour = fContourHead;
+    do {
+        if (contour->debugID() == id) {
+            return contour;
+        }
+    } while ((contour = contour->next()));
+    return nullptr;
+}
+
+const SkOpPtT* SkOpGlobalState::debugPtT(int id) const {
+    const SkOpContour* contour = fContourHead;
+    do {
+        const SkOpSegment* segment = contour->first();
+        while (segment) {
+            const SkOpSpan* span = segment->head();
+            do {
+                const SkOpPtT* ptT = span->ptT();
+                if (ptT->debugMatchID(id)) {
+                    return ptT;
+                }
+            } while ((span = span->next()->upCastable()));
+            const SkOpSpanBase* tail = segment->tail();
+            const SkOpPtT* ptT = tail->ptT();
+            if (ptT->debugMatchID(id)) {
+                return ptT;
+            }
+            segment = segment->next();
+        }
+    } while ((contour = contour->next()));
+    return nullptr;
+}
+
+const SkOpSegment* SkOpGlobalState::debugSegment(int id) const {
+    const SkOpContour* contour = fContourHead;
+    do {
+        const SkOpSegment* segment = contour->first();
+        while (segment) {
+            if (segment->debugID() == id) {
+                return segment;
+            }
+            segment = segment->next();
+        }
+    } while ((contour = contour->next()));
+    return nullptr;
+}
+
+const SkOpSpanBase* SkOpGlobalState::debugSpan(int id) const {
+    const SkOpContour* contour = fContourHead;
+    do {
+        const SkOpSegment* segment = contour->first();
+        while (segment) {
+            const SkOpSpan* span = segment->head();
+            do {
+                if (span->debugID() == id) {
+                    return span;
+                }
+            } while ((span = span->next()->upCastable()));
+            const SkOpSpanBase* tail = segment->tail();
+            if (tail->debugID() == id) {
+                return tail;
+            }
+            segment = segment->next();
+        }
+    } while ((contour = contour->next()));
+    return nullptr;
+}
+#endif
+
+#if DEBUG_T_SECT_DUMP > 1
+int gDumpTSectNum;
+#endif
diff --git a/src/third_party/skia/tests/PathOpsExtendedTest.cpp b/src/third_party/skia/tests/PathOpsExtendedTest.cpp
index d808ed7..c668963 100644
--- a/src/third_party/skia/tests/PathOpsExtendedTest.cpp
+++ b/src/third_party/skia/tests/PathOpsExtendedTest.cpp
@@ -9,21 +9,36 @@
 #include "PathOpsThreadedCommon.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
-#include "SkForceLinking.h"
 #include "SkMatrix.h"
+#include "SkMutex.h"
 #include "SkPaint.h"
-#include "SkRTConf.h"
+#include "SkRegion.h"
 #include "SkStream.h"
-#include "SkTaskGroup.h"
-#include "SkThread.h"
+
+#include <stdlib.h>
 
 #ifdef SK_BUILD_FOR_MAC
 #include <sys/sysctl.h>
 #endif
 
-__SK_FORCE_IMAGE_DECODER_LINKING;
+// std::to_string isn't implemented on android
+#include <sstream>
 
-DEFINE_bool2(runFail, f, false, "run tests known to fail.");
+template <typename T>
+std::string std_to_string(T value)
+{
+    std::ostringstream os ;
+    os << value ;
+    return os.str() ;
+}
+
+bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
+             SkDEBUGPARAMS(bool skipAssert)
+             SkDEBUGPARAMS(const char* testName));
+
+bool SimplifyDebug(const SkPath& one, SkPath* result
+                   SkDEBUGPARAMS(bool skipAssert)
+                   SkDEBUGPARAMS(const char* testName));
 
 static const char marker[] =
     "</div>\n"
@@ -33,11 +48,11 @@
     "var testDivs = [\n";
 
 static const char* opStrs[] = {
-    "kDifference_PathOp",
-    "kIntersect_PathOp",
-    "kUnion_PathOp",
-    "kXor_PathOp",
-    "kReverseDifference_PathOp",
+    "kDifference_SkPathOp",
+    "kIntersect_SkPathOp",
+    "kUnion_SkPathOp",
+    "kXOR_PathOp",
+    "kReverseDifference_SkPathOp",
 };
 
 static const char* opSuffixes[] = {
@@ -45,11 +60,25 @@
     "i",
     "u",
     "o",
+    "r",
 };
 
-static bool gShowPath = false;
-static bool gComparePathsAssert = true;
-static bool gPathStrAssert = true;
+enum class ExpectSuccess {
+    kNo,
+    kYes,
+    kFlaky
+};
+
+enum class SkipAssert {
+    kNo,
+    kYes
+};
+
+enum class ExpectMatch {
+    kNo,
+    kYes,
+    kFlaky
+};
 
 #if DEBUG_SHOW_TEST_NAME
 static void showPathData(const SkPath& path) {
@@ -82,6 +111,13 @@
                 lastPt = pts[2];
                 lastPtSet = true;
                 break;
+            case SkPath::kConic_Verb:
+                SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},  //weight=%1.9g\n",
+                        pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
+                        iter.conicWeight());
+                lastPt = pts[2];
+                lastPtSet = true;
+                break;
             case SkPath::kCubic_Verb:
                 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
                         pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
@@ -110,19 +146,19 @@
 
 void showOp(const SkPathOp op) {
     switch (op) {
-        case kDifference_PathOp:
+        case kDifference_SkPathOp:
             SkDebugf("op difference\n");
             break;
-        case kIntersect_PathOp:
+        case kIntersect_SkPathOp:
             SkDebugf("op intersect\n");
             break;
-        case kUnion_PathOp:
+        case kUnion_SkPathOp:
             SkDebugf("op union\n");
             break;
-        case kXOR_PathOp:
+        case kXOR_SkPathOp:
             SkDebugf("op xor\n");
             break;
-        case kReverseDifference_PathOp:
+        case kReverseDifference_SkPathOp:
             SkDebugf("op reverse difference\n");
             break;
         default:
@@ -154,7 +190,7 @@
     state->fSerialNo[8] = '\0';
     SkDebugf("%s\n", state->fSerialNo);
     if (strcmp(state->fSerialNo, state->fKey) == 0) {
-        SkDebugf("%s\n", state->fPathStr);
+        SkDebugf("%s\n", state->fPathStr.c_str());
     }
 #endif
 }
@@ -177,6 +213,15 @@
     SkScalar vScale = (bitHeight - 2) / largerHeight;
     scale.reset();
     scale.preScale(hScale, vScale);
+    larger.fLeft *= hScale;
+    larger.fRight *= hScale;
+    larger.fTop *= vScale;
+    larger.fBottom *= vScale;
+    SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
+            : 16000 < larger.fRight ? 16000 - larger.fRight : 0;
+    SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
+            : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
+    scale.postTranslate(dx, dy);
 }
 
 static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo,
@@ -273,7 +318,7 @@
     return true;
 }
 
-static int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one,
+int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one,
         const SkPath& two, SkBitmap& bitmap) {
     int errors2x2;
     SkPath scaledOne, scaledTwo;
@@ -282,138 +327,122 @@
         return 0;
     }
     const int MAX_ERRORS = 9;
-    REPORTER_ASSERT(reporter, errors2x2 <= MAX_ERRORS || !gComparePathsAssert);
     return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
 }
 
-const int gTestFirst = 4;
-static int gTestNo = gTestFirst;
 static SkTDArray<SkPathOp> gTestOp;
 
 static void showPathOpPath(const char* testName, const SkPath& one, const SkPath& two,
         const SkPath& a, const SkPath& b, const SkPath& scaledOne, const SkPath& scaledTwo,
         const SkPathOp shapeOp, const SkMatrix& scale) {
     SkASSERT((unsigned) shapeOp < SK_ARRAY_COUNT(opStrs));
-    SkString defaultTestName;
     if (!testName) {
-        defaultTestName.printf("xOp%d%s", gTestNo, opSuffixes[shapeOp]);
-        testName = defaultTestName.c_str();
+        testName = "xOp";
     }
-    SkDebugf("static void %s(skiatest::Reporter* reporter, const char* filename) {\n", testName);
+    SkDebugf("static void %s_%s(skiatest::Reporter* reporter, const char* filename) {\n",
+        testName, opSuffixes[shapeOp]);
     *gTestOp.append() = shapeOp;
-    ++gTestNo;
     SkDebugf("    SkPath path, pathB;\n");
-#if DEBUG_SHOW_TEST_NAME
     SkPathOpsDebug::ShowOnePath(a, "path", false);
     SkPathOpsDebug::ShowOnePath(b, "pathB", false);
-#endif
     SkDebugf("    testPathOp(reporter, path, pathB, %s, filename);\n", opStrs[shapeOp]);
     SkDebugf("}\n");
     drawAsciiPaths(scaledOne, scaledTwo, true);
 }
 
-void ShowTestArray() {
-    for (int x = gTestFirst; x < gTestNo; ++x) {
-        SkDebugf("    TEST(xOp%d%s),\n", x, opSuffixes[gTestOp[x - gTestFirst]]);
-    }
-}
-
 SK_DECLARE_STATIC_MUTEX(compareDebugOut3);
-SK_DECLARE_STATIC_MUTEX(compareDebugOut4);
+
 static int comparePaths(skiatest::Reporter* reporter, const char* testName, const SkPath& one,
         const SkPath& scaledOne, const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap,
-        const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const SkMatrix& scale) {
+        const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const SkMatrix& scale,
+        ExpectMatch expectMatch) {
     int errors2x2;
+    const int MAX_ERRORS = 8;
     (void) pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2);
-    if (errors2x2 == 0) {
-        if (gShowPath) {
-            showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
+    if (ExpectMatch::kNo == expectMatch) {
+        if (errors2x2 < MAX_ERRORS) {
+            REPORTER_ASSERT(reporter, 0);
         }
         return 0;
     }
-    const int MAX_ERRORS = 8;
-    if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
-        SkAutoMutexAcquire autoM(compareDebugOut3);
-        SkDebugf("\n*** this test fails ***\n");
-        showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
-        REPORTER_ASSERT(reporter, 0);
-    } else if (gShowPath || errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
-        SkAutoMutexAcquire autoM(compareDebugOut4);
-        showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
+    if (errors2x2 == 0) {
+        return 0;
     }
-    return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
+    if (ExpectMatch::kYes == expectMatch && errors2x2 >= MAX_ERRORS) {
+        SkAutoMutexAcquire autoM(compareDebugOut3);
+        showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
+        SkDebugf("\n/*");
+        REPORTER_ASSERT(reporter, 0);
+        SkDebugf(" */\n");
+    }
+    return errors2x2 >= MAX_ERRORS ? errors2x2 : 0;
 }
 
 // Default values for when reporter->verbose() is false.
 static int testNumber = 55;
 static const char* testName = "pathOpTest";
 
-static void writeTestName(const char* nameSuffix, SkMemoryWStream& outFile) {
-    outFile.writeText(testName);
-    outFile.writeDecAsText(testNumber);
+static void appendTestName(const char* nameSuffix, std::string& out) {
+    out += testName;
+    out += std_to_string(testNumber);
     ++testNumber;
     if (nameSuffix) {
-        outFile.writeText(nameSuffix);
+        out.append(nameSuffix);
     }
 }
 
-static void outputToStream(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
-        const char* testFunction, bool twoPaths, SkMemoryWStream& outFile) {
+static void appendTest(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
+                       const char* testFunction, bool twoPaths, std::string& out) {
 #if 0
-    outFile.writeText("<div id=\"");
-    writeTestName(nameSuffix, outFile);
-    outFile.writeText("\">\n");
+    out.append("\n<div id=\"");
+    appendTestName(nameSuffix, out);
+    out.append("\">\n");
     if (pathPrefix) {
-        outFile.writeText(pathPrefix);
+        out.append(pathPrefix);
     }
-    outFile.writeText(pathStr);
-    outFile.writeText("</div>\n\n");
+    out.append(pathStr);
+    out.append("</div>\n\n");
 
-    outFile.writeText(marker);
-    outFile.writeText("    ");
-    writeTestName(nameSuffix, outFile);
-    outFile.writeText(",\n\n\n");
+    out.append(marker);
+    out.append("    ");
+    appendTestName(nameSuffix, out);
+    out.append(",\n\n\n");
 #endif
-    outFile.writeText("static void ");
-    writeTestName(nameSuffix, outFile);
-    outFile.writeText("(skiatest::Reporter* reporter) {\n    SkPath path");
+    out.append("static void ");
+    appendTestName(nameSuffix, out);
+    out.append("(skiatest::Reporter* reporter) {\n    SkPath path");
     if (twoPaths) {
-        outFile.writeText(", pathB");
+        out.append(", pathB");
     }
-    outFile.writeText(";\n");
+    out.append(";\n");
     if (pathPrefix) {
-        outFile.writeText(pathPrefix);
+        out.append(pathPrefix);
     }
-    outFile.writeText(pathStr);
-    outFile.writeText("    ");
-    outFile.writeText(testFunction);
-    outFile.writeText("\n}\n\n");
+    out += pathStr;
+    out += "    ";
+    out += testFunction;
 #if 0
-    outFile.writeText("static void (*firstTest)() = ");
-    writeTestName(nameSuffix, outFile);
-    outFile.writeText(";\n\n");
+    out.append("static void (*firstTest)() = ");
+    appendTestName(nameSuffix, out);
+    out.append(";\n\n");
 
-    outFile.writeText("static struct {\n");
-    outFile.writeText("    void (*fun)();\n");
-    outFile.writeText("    const char* str;\n");
-    outFile.writeText("} tests[] = {\n");
-    outFile.writeText("    TEST(");
-    writeTestName(nameSuffix, outFile);
-    outFile.writeText("),\n");
+    out.append("static struct {\n");
+    out.append("    void (*fun)();\n");
+    out.append("    const char* str;\n");
+    out.append("} tests[] = {\n");
+    out.append("    TEST(");
+    appendTestName(nameSuffix, out);
+    out.append("),\n");
 #endif
-    outFile.flush();
 }
 
 SK_DECLARE_STATIC_MUTEX(simplifyDebugOut);
+
 bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
                   const char* pathStr) {
     SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
     path.setFillType(fillType);
-#if DEBUG_SHOW_TEST_NAME
-    if (gShowPath) {
-        SkPathOpsDebug::ShowOnePath(path, "path", false);
-    }
-#endif
+    state.fReporter->bumpTestCount();
     if (!Simplify(path, &out)) {
         SkDebugf("%s did not expect failure\n", __FUNCTION__);
         REPORTER_ASSERT(state.fReporter, 0);
@@ -422,44 +451,73 @@
     if (!state.fReporter->verbose()) {
         return true;
     }
-    int result = comparePaths(state.fReporter, NULL, path, out, *state.fBitmap);
-    if (result && gPathStrAssert) {
+    int result = comparePaths(state.fReporter, nullptr, path, out, *state.fBitmap);
+    if (result) {
         SkAutoMutexAcquire autoM(simplifyDebugOut);
-        char temp[8192];
-        sk_bzero(temp, sizeof(temp));
-        SkMemoryWStream stream(temp, sizeof(temp));
-        const char* pathPrefix = NULL;
-        const char* nameSuffix = NULL;
+        std::string str;
+        const char* pathPrefix = nullptr;
+        const char* nameSuffix = nullptr;
         if (fillType == SkPath::kEvenOdd_FillType) {
             pathPrefix = "    path.setFillType(SkPath::kEvenOdd_FillType);\n";
             nameSuffix = "x";
         }
         const char testFunction[] = "testSimplify(reporter, path);";
-        outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, stream);
-        SkDebugf(temp);
+        appendTest(pathStr, pathPrefix, nameSuffix, testFunction, false, str);
+        SkDebugf("%s", str.c_str());
         REPORTER_ASSERT(state.fReporter, 0);
     }
     state.fReporter->bumpTestCount();
     return result == 0;
 }
 
-bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
-#if DEBUG_SHOW_TEST_NAME
+static bool inner_simplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
+        ExpectSuccess expectSuccess, SkipAssert skipAssert, ExpectMatch expectMatch) {
+#if 0 && DEBUG_SHOW_TEST_NAME
     showPathData(path);
 #endif
     SkPath out;
-    if (!Simplify(path, &out)) {
-        SkDebugf("%s did not expect failure\n", __FUNCTION__);
-        REPORTER_ASSERT(reporter, 0);
+    if (!SimplifyDebug(path, &out  SkDEBUGPARAMS(SkipAssert::kYes == skipAssert)
+            SkDEBUGPARAMS(testName))) {
+        if (ExpectSuccess::kYes == expectSuccess) {
+            SkDebugf("%s did not expect %s failure\n", __FUNCTION__, filename);
+            REPORTER_ASSERT(reporter, 0);
+        }
         return false;
+    } else {
+        if (ExpectSuccess::kNo == expectSuccess) {
+            SkDebugf("%s %s unexpected success\n", __FUNCTION__, filename);
+            REPORTER_ASSERT(reporter, 0);
+        }
     }
     SkBitmap bitmap;
-    int result = comparePaths(reporter, filename, path, out, bitmap);
-    if (result && gPathStrAssert) {
+    int errors = comparePaths(reporter, filename, path, out, bitmap);
+    if (ExpectMatch::kNo == expectMatch) {
+        if (!errors) {
+            SkDebugf("%s failing test %s now succeeds\n", __FUNCTION__, filename);
+            REPORTER_ASSERT(reporter, 0);
+            return false;
+        }
+    } else if (ExpectMatch::kYes == expectMatch && errors) {
         REPORTER_ASSERT(reporter, 0);
     }
     reporter->bumpTestCount();
-    return result == 0;
+    return errors == 0;
+}
+
+bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
+    return inner_simplify(reporter, path, filename, ExpectSuccess::kYes, SkipAssert::kNo,
+            ExpectMatch::kYes);
+}
+
+bool testSimplifyFuzz(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
+    return inner_simplify(reporter, path, filename, ExpectSuccess::kFlaky, SkipAssert::kYes,
+            ExpectMatch::kFlaky);
+}
+
+bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
+        bool checkFail) {
+    return inner_simplify(reporter, path, filename, checkFail ?
+            ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo);
 }
 
 #if DEBUG_SHOW_TEST_NAME
@@ -472,17 +530,26 @@
 #endif
 
 static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
-                 const SkPathOp shapeOp, const char* testName, bool threaded) {
-#if DEBUG_SHOW_TEST_NAME
+        const SkPathOp shapeOp, const char* testName, ExpectSuccess expectSuccess,
+        SkipAssert skipAssert, ExpectMatch expectMatch) {
+#if 0 && DEBUG_SHOW_TEST_NAME
     showName(a, b, shapeOp);
 #endif
     SkPath out;
-    if (!Op(a, b, shapeOp, &out) ) {
-        SkDebugf("%s did not expect failure\n", __FUNCTION__);
-        REPORTER_ASSERT(reporter, 0);
+    if (!OpDebug(a, b, shapeOp, &out  SkDEBUGPARAMS(SkipAssert::kYes == skipAssert)
+            SkDEBUGPARAMS(testName))) {
+        if (ExpectSuccess::kYes == expectSuccess) {
+            SkDebugf("%s %s did not expect failure\n", __FUNCTION__, testName);
+            REPORTER_ASSERT(reporter, 0);
+        }
         return false;
+    } else {
+        if (ExpectSuccess::kNo == expectSuccess) {
+                SkDebugf("%s %s unexpected success\n", __FUNCTION__, testName);
+                REPORTER_ASSERT(reporter, 0);
+        }
     }
-    if (threaded && !reporter->verbose()) {
+    if (!reporter->verbose()) {
         return true;
     }
     SkPath pathOut, scaledPathOut;
@@ -510,45 +577,49 @@
     scaledOut.addPath(out, scale);
     scaledOut.setFillType(out.getFillType());
     int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap,
-            a, b, shapeOp, scale);
-    if (result && gPathStrAssert) {
-        REPORTER_ASSERT(reporter, 0);
-    }
+            a, b, shapeOp, scale, expectMatch);
     reporter->bumpTestCount();
     return result == 0;
 }
 
 bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
-                 const SkPathOp shapeOp, const char* testName) {
-    return innerPathOp(reporter, a, b, shapeOp, testName, false);
+        const SkPathOp shapeOp, const char* testName) {
+    return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kYes, SkipAssert::kNo,
+            ExpectMatch::kYes);
 }
 
-bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+        const SkPathOp shapeOp, const char* testName, bool checkFail) {
+    return innerPathOp(reporter, a, b, shapeOp, testName, checkFail ?
+            ExpectSuccess::kYes : ExpectSuccess::kNo, SkipAssert::kNo, ExpectMatch::kNo);
+}
+
+bool testPathOpFuzz(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+        const SkPathOp shapeOp, const char* testName) {
+    return innerPathOp(reporter, a, b, shapeOp, testName, ExpectSuccess::kFlaky, SkipAssert::kYes,
+            ExpectMatch::kFlaky);
+}
+
+bool testPathOpFail(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
                  const SkPathOp shapeOp, const char* testName) {
 #if DEBUG_SHOW_TEST_NAME
     showName(a, b, shapeOp);
 #endif
-    SkPath out;
+    SkPath orig;
+    orig.lineTo(54, 43);
+    SkPath out = orig;
     if (Op(a, b, shapeOp, &out) ) {
         SkDebugf("%s test is expected to fail\n", __FUNCTION__);
         REPORTER_ASSERT(reporter, 0);
         return false;
     }
+    SkASSERT(out == orig);
     return true;
 }
 
-bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
-                 const SkPathOp shapeOp, const char* testName) {
-    return innerPathOp(reporter, a, b, shapeOp, testName, true);
-}
-
 SK_DECLARE_STATIC_MUTEX(gMutex);
 
 void initializeTests(skiatest::Reporter* reporter, const char* test) {
-#if 0  // doesn't work yet
-    SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true);
-    SK_CONF_SET("images.png.suppressDecoderWarnings", true);
-#endif
     if (reporter->verbose()) {
         SkAutoMutexAcquire lock(gMutex);
         testName = test;
@@ -559,7 +630,7 @@
             inData.setCount((int) inFile.getLength());
             size_t inLen = inData.count();
             inFile.read(inData.begin(), inLen);
-            inFile.setPath(NULL);
+            inFile.close();
             char* insert = strstr(inData.begin(), marker);
             if (insert) {
                 insert += sizeof(marker) - 1;
@@ -570,28 +641,27 @@
     }
 }
 
-void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType pathFillType) {
+void PathOpsThreadState::outputProgress(const char* pathStr, SkPath::FillType pathFillType) {
     const char testFunction[] = "testSimplify(path);";
-    const char* pathPrefix = NULL;
-    const char* nameSuffix = NULL;
+    const char* pathPrefix = nullptr;
+    const char* nameSuffix = nullptr;
     if (pathFillType == SkPath::kEvenOdd_FillType) {
         pathPrefix = "    path.setFillType(SkPath::kEvenOdd_FillType);\n";
         nameSuffix = "x";
     }
-    SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
-    outputToStream(pathStr, pathPrefix, nameSuffix, testFunction, false, rRamStream);
+    appendTest(pathStr, pathPrefix, nameSuffix, testFunction, false, fPathStr);
 }
 
-void outputProgress(char* ramStr, const char* pathStr, SkPathOp op) {
+void PathOpsThreadState::outputProgress(const char* pathStr, SkPathOp op) {
     const char testFunction[] = "testOp(path);";
     SkASSERT((size_t) op < SK_ARRAY_COUNT(opSuffixes));
     const char* nameSuffix = opSuffixes[op];
-    SkMemoryWStream rRamStream(ramStr, PATH_STR_SIZE);
-    outputToStream(pathStr, NULL, nameSuffix, testFunction, true, rRamStream);
+    appendTest(pathStr, nullptr, nameSuffix, testFunction, true, fPathStr);
 }
 
 void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
                 void (*firstTest)(skiatest::Reporter* , const char* filename),
+                void (*skipTest)(skiatest::Reporter* , const char* filename),
                 void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse) {
     size_t index;
     if (firstTest) {
@@ -600,8 +670,7 @@
             --index;
         }
 #if DEBUG_SHOW_TEST_NAME
-        SkDebugf("<div id=\"%s\">\n", tests[index].str);
-        SkDebugf("  %s [%s]\n", __FUNCTION__, tests[index].str);
+        SkDebugf("\n<div id=\"%s\">\n", tests[index].str);
 #endif
         (*tests[index].fun)(reporter, tests[index].str);
         if (tests[index].fun == stopTest) {
@@ -610,21 +679,45 @@
     }
     index = reverse ? count - 1 : 0;
     size_t last = reverse ? 0 : count - 1;
+    bool foundSkip = !skipTest;
     do {
-        if (tests[index].fun != firstTest) {
+        if (tests[index].fun == skipTest) {
+            foundSkip = true;
+        }
+        if (foundSkip && tests[index].fun != firstTest) {
     #if DEBUG_SHOW_TEST_NAME
-            SkDebugf("<div id=\"%s\">\n", tests[index].str);
-            SkDebugf("  %s [%s]\n", __FUNCTION__, tests[index].str);
+            SkDebugf("\n<div id=\"%s\">\n", tests[index].str);
     #endif
-            (*tests[index].fun)(reporter, tests[index].str);
+             (*tests[index].fun)(reporter, tests[index].str);
         }
-        if (tests[index].fun == stopTest) {
-            SkDebugf("lastTest\n");
-            break;
-        }
-        if (index == last) {
+        if (tests[index].fun == stopTest || index == last) {
             break;
         }
         index += reverse ? -1 : 1;
     } while (true);
+#if DEBUG_SHOW_TEST_NAME
+    SkDebugf(
+            "\n"
+            "</div>\n"
+            "\n"
+            "<script type=\"text/javascript\">\n"
+            "\n"
+            "var testDivs = [\n"
+    );
+    index = reverse ? count - 1 : 0;
+    last = reverse ? 0 : count - 1;
+    foundSkip = !skipTest;
+    do {
+        if (tests[index].fun == skipTest) {
+            foundSkip = true;
+        }
+        if (foundSkip && tests[index].fun != firstTest) {
+            SkDebugf("    %s,\n", tests[index].str);
+        }
+        if (tests[index].fun == stopTest || index == last) {
+            break;
+        }
+        index += reverse ? -1 : 1;
+    } while (true);
+#endif
 }
diff --git a/src/third_party/skia/tests/PathOpsExtendedTest.h b/src/third_party/skia/tests/PathOpsExtendedTest.h
index 49ac804..9f8b0ae 100644
--- a/src/third_party/skia/tests/PathOpsExtendedTest.h
+++ b/src/third_party/skia/tests/PathOpsExtendedTest.h
@@ -8,16 +8,11 @@
 #define PathOpsExtendedTest_DEFINED
 
 #include "SkBitmap.h"
-#include "SkCommandLineFlags.h"
 #include "SkPath.h"
 #include "SkPathOpsTypes.h"
 #include "SkStream.h"
-#include "SkThread.h"
-#include "SkThreadUtils.h"
 #include "Test.h"
 
-DECLARE_bool(runFail);
-
 struct PathOpsThreadState;
 
 struct TestDesc {
@@ -26,27 +21,39 @@
 };
 
 //extern int comparePaths(const SkPath& one, const SkPath& two);
-extern int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap);
+extern int comparePaths(skiatest::Reporter* reporter, const char* filename,
+                        const SkPath& one, const SkPath& two, SkBitmap& bitmap);
+
+inline int comparePaths(skiatest::Reporter* reporter, const char* filename,
+                        const SkPath& one, const SkPath& two) {
+    SkBitmap bitmap;
+    return comparePaths(reporter, filename, one, two, bitmap);
+}
+
 extern bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths);
 extern void showOp(const SkPathOp op);
 extern bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
-                        const SkPathOp , const char* testName);
-extern bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
-                        const SkPathOp , const char* testName);
-extern bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
-                        const SkPathOp , const char* testName);
+                       const SkPathOp , const char* testName);
+extern bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+                            const SkPathOp , const char* testName, bool checkFail);
+extern bool testPathOpFail(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+                           const SkPathOp, const char* testName);
+extern bool testPathOpFuzz(skiatest::Reporter* reporter, const SkPath& a,
+                           const SkPath& b, const SkPathOp , const char* testName);
 extern bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
                          const char* pathStr);
 extern bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename);
+extern bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path,
+                              const char* filename, bool checkFail);
+extern bool testSimplifyFuzz(skiatest::Reporter* reporter, const SkPath& path,
+                                       const char* filename);
 
 void initializeTests(skiatest::Reporter* reporter, const char* testName);
-void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType );
-void outputProgress(char* ramStr, const char* pathStr, SkPathOp op);
 
 void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
                 void (*firstTest)(skiatest::Reporter* , const char* filename),
+                void (*skipTest)(skiatest::Reporter* , const char* filename),
                 void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse);
-void ShowTestArray();
 void ShowTestName(PathOpsThreadState* data, int a, int b, int c, int d);
 void ShowFunctionHeader(const char* name);
 void ShowPath(const SkPath& path, const char* pathName);
diff --git a/src/third_party/skia/tests/PathOpsFuzz763Test.cpp b/src/third_party/skia/tests/PathOpsFuzz763Test.cpp
new file mode 100644
index 0000000..90d5723
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsFuzz763Test.cpp
@@ -0,0 +1,2444 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsExtendedTest.h"
+#include "PathOpsTestCommon.h"
+
+#define TEST(name) { name, #name }
+
+static void fuzz763_3084(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0x421d76c6), SkBits2Float(0x414d1957));
+path.quadTo(SkBits2Float(0x4229fd05), SkBits2Float(0x413bbdcc), SkBits2Float(0x4235e9b0), SkBits2Float(0x4152e45d));
+path.quadTo(SkBits2Float(0x4241d65c), SkBits2Float(0x416a0aee), SkBits2Float(0x42462d3e), SkBits2Float(0x418e11f4));
+path.quadTo(SkBits2Float(0x424a8421), SkBits2Float(0x41a71e71), SkBits2Float(0x4244ba7d), SkBits2Float(0x41bef7c6));
+path.quadTo(SkBits2Float(0x423ef0da), SkBits2Float(0x41d6d11e), SkBits2Float(0x42326a9b), SkBits2Float(0x41df7ee4));
+path.quadTo(SkBits2Float(0x42273b3e), SkBits2Float(0x41e73f0f), SkBits2Float(0x421c865e), SkBits2Float(0x41ded7e1));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41f0534a), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4209f7d0), SkBits2Float(0x41c80000), SkBits2Float(0x420f2625), SkBits2Float(0x41cbccd7));
+path.quadTo(SkBits2Float(0x420ba850), SkBits2Float(0x41c340da), SkBits2Float(0x4209b422), SkBits2Float(0x41b7f99d));
+path.quadTo(SkBits2Float(0x42055d40), SkBits2Float(0x419eed20), SkBits2Float(0x420b26e4), SkBits2Float(0x418713c8));
+path.quadTo(SkBits2Float(0x4210f088), SkBits2Float(0x415e74e2), SkBits2Float(0x421d76c6), SkBits2Float(0x414d1957));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_1823(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x405eb92c), SkBits2Float(0x422355aa), SkBits2Float(0x3eee625a), SkBits2Float(0x4223f3e8));
+path.quadTo(SkBits2Float(0x3f238500), SkBits2Float(0x4224bba7), SkBits2Float(0x3f4dcc60), SkBits2Float(0x4225921a));
+path.quadTo(SkBits2Float(0x4036c5c0), SkBits2Float(0x422ffa87), SkBits2Float(0x401de138), SkBits2Float(0x423d244e));
+path.quadTo(SkBits2Float(0x4004fcb0), SkBits2Float(0x424a4e17), SkBits2Float(0xbf0628a0), SkBits2Float(0x42528342));
+path.quadTo(SkBits2Float(0xc04810f8), SkBits2Float(0x425ab86c), SkBits2Float(0xc0cd56bc), SkBits2Float(0x42592a22));
+path.quadTo(SkBits2Float(0xc11b5280), SkBits2Float(0x42579bda), SkBits2Float(0xc13c272a), SkBits2Float(0x424d336e));
+path.quadTo(SkBits2Float(0xc15cfbd4), SkBits2Float(0x4242cb00), SkBits2Float(0xc156c2ae), SkBits2Float(0x4235a138));
+path.quadTo(SkBits2Float(0xc150898c), SkBits2Float(0x42287770), SkBits2Float(0xc126e7d8), SkBits2Float(0x42204246));
+path.quadTo(SkBits2Float(0xc1066ae4), SkBits2Float(0x4219da96), SkBits2Float(0xc0be6f82), SkBits2Float(0x42196502));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x42106507), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ed7d86), SkBits2Float(0xc0b504f3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x41c80000), SkBits2Float(0x00000000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x41c80000), SkBits2Float(0x40b504f3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_378(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x41013776), SkBits2Float(0xc25007a8));
+path.quadTo(SkBits2Float(0x412f219e), SkBits2Float(0xc256a86f), SkBits2Float(0x41625842), SkBits2Float(0xc2533a60));
+path.quadTo(SkBits2Float(0x418ac776), SkBits2Float(0xc24fcc52), SkBits2Float(0x41980904), SkBits2Float(0xc24451c8));
+path.quadTo(SkBits2Float(0x41a54a92), SkBits2Float(0xc238d73e), SkBits2Float(0x419e6e72), SkBits2Float(0xc22c0994));
+path.quadTo(SkBits2Float(0x41979256), SkBits2Float(0xc21f3bea), SkBits2Float(0x41809d42), SkBits2Float(0xc2189b23));
+path.quadTo(SkBits2Float(0x4153505c), SkBits2Float(0xc211fa5c), SkBits2Float(0x412019b5), SkBits2Float(0xc215686b));
+path.quadTo(SkBits2Float(0x40d9c61e), SkBits2Float(0xc218d67a), SkBits2Float(0x40a4bfe8), SkBits2Float(0xc2245104));
+path.quadTo(SkBits2Float(0x405f7360), SkBits2Float(0xc22fcb8e), SkBits2Float(0x408b2a24), SkBits2Float(0xc23c9937));
+path.quadTo(SkBits2Float(0x40a69a9c), SkBits2Float(0xc24966e1), SkBits2Float(0x41013776), SkBits2Float(0xc25007a8));
+path.close();
+path.moveTo(SkBits2Float(0xc21aa3d0), SkBits2Float(0xc21a9d6c));
+path.quadTo(SkBits2Float(0xc21144a0), SkBits2Float(0xc223fd00), SkBits2Float(0xc2040363), SkBits2Float(0xc223fd46));
+path.quadTo(SkBits2Float(0xc1ed844d), SkBits2Float(0xc223fd8c), SkBits2Float(0xc1dac526), SkBits2Float(0xc21a9e5c));
+path.quadTo(SkBits2Float(0xc1c80600), SkBits2Float(0xc2113f2c), SkBits2Float(0xc1c80574), SkBits2Float(0xc203fdef));
+path.quadTo(SkBits2Float(0xc1c804e8), SkBits2Float(0xc1ed7964), SkBits2Float(0xc1dac348), SkBits2Float(0xc1daba3e));
+path.quadTo(SkBits2Float(0xc1ed81a8), SkBits2Float(0xc1c7fb18), SkBits2Float(0xc2040211), SkBits2Float(0xc1c7fa8c));
+path.quadTo(SkBits2Float(0xc211434e), SkBits2Float(0xc1c7fa00), SkBits2Float(0xc21aa2e0), SkBits2Float(0xc1dab860));
+path.quadTo(SkBits2Float(0xc2240274), SkBits2Float(0xc1ed76bf), SkBits2Float(0xc22402ba), SkBits2Float(0xc203fc9d));
+path.quadTo(SkBits2Float(0xc2240300), SkBits2Float(0xc2113dda), SkBits2Float(0xc21aa3d0), SkBits2Float(0xc21a9d6c));
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));
+path.quadTo(SkBits2Float(0xc24fcaa2), SkBits2Float(0x418ace05), SkBits2Float(0xc2533929), SkBits2Float(0x41626a5d));
+path.lineTo(SkBits2Float(0xc25338d1), SkBits2Float(0x41626f8c));
+path.quadTo(SkBits2Float(0xc24fca68), SkBits2Float(0x418ad2e8), SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4));
+path.quadTo(SkBits2Float(0xc238d4f6), SkBits2Float(0x41a554c0), SkBits2Float(0xc22c0765), SkBits2Float(0x419e77ee));
+path.quadTo(SkBits2Float(0xc21f39d4), SkBits2Float(0x41979b1c), SkBits2Float(0xc218995e), SkBits2Float(0x4180a5aa));
+path.quadTo(SkBits2Float(0xc211f8e8), SkBits2Float(0x41536071), SkBits2Float(0xc2156751), SkBits2Float(0x41202a2d));
+path.quadTo(SkBits2Float(0xc2156774), SkBits2Float(0x41202819), SkBits2Float(0xc2156798), SkBits2Float(0x41202604));
+path.quadTo(SkBits2Float(0xc2156d3e), SkBits2Float(0x411fd1b7), SkBits2Float(0xc2157321), SkBits2Float(0x411f7b6e));
+path.quadTo(SkBits2Float(0xc218e910), SkBits2Float(0x40d986da), SkBits2Float(0xc2245097), SkBits2Float(0x40a4daf8));
+path.quadTo(SkBits2Float(0xc22fcb44), SkBits2Float(0x405fad48), SkBits2Float(0xc23c98dc), SkBits2Float(0x408b493c));
+path.quadTo(SkBits2Float(0xc2496673), SkBits2Float(0x40a6bbcc), SkBits2Float(0xc25006fe), SkBits2Float(0x4101489a));
+path.quadTo(SkBits2Float(0xc256a729), SkBits2Float(0x412f30b9), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.close();
+path.moveTo(SkBits2Float(0xc2533937), SkBits2Float(0x41626995));
+path.quadTo(SkBits2Float(0xc2533968), SkBits2Float(0x416266bf), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.lineTo(SkBits2Float(0x41dac1c6), SkBits2Float(0x41dabbc0));
+path.quadTo(SkBits2Float(0x41dac044), SkBits2Float(0x41dabd41), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x421aa1af), SkBits2Float(0x421a9f8e), SkBits2Float(0x421aa2bf), SkBits2Float(0x421a9e7d));
+path.quadTo(SkBits2Float(0x42240200), SkBits2Float(0x42113efb), SkBits2Float(0x422401d1), SkBits2Float(0x4203fdbe));
+path.quadTo(SkBits2Float(0x422401a3), SkBits2Float(0x41ed7902), SkBits2Float(0x421aa220), SkBits2Float(0x41daba81));
+path.quadTo(SkBits2Float(0x4211429e), SkBits2Float(0x41c7fc00), SkBits2Float(0x42040161), SkBits2Float(0x41c7fc5d));
+path.quadTo(SkBits2Float(0x41ed8047), SkBits2Float(0x41c7fcbb), SkBits2Float(0x41dac1c6), SkBits2Float(0x41dabbc0));
+path.lineTo(SkBits2Float(0xc2533937), SkBits2Float(0x41626995));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4));
+path.quadTo(SkBits2Float(0xc24fca68), SkBits2Float(0x418ad2e8), SkBits2Float(0xc25338d1), SkBits2Float(0x41626f8c));
+path.quadTo(SkBits2Float(0xc256a73a), SkBits2Float(0x412f3944), SkBits2Float(0xc25006c4), SkBits2Float(0x41014e62));
+path.quadTo(SkBits2Float(0xc249664e), SkBits2Float(0x40a6c6fc), SkBits2Float(0xc23c98bd), SkBits2Float(0x408b53b8));
+path.quadTo(SkBits2Float(0xc22fcb2b), SkBits2Float(0x405fc0d8), SkBits2Float(0xc2245073), SkBits2Float(0x40a4e41c));
+path.quadTo(SkBits2Float(0xc218d5ba), SkBits2Float(0x40d9e7cc), SkBits2Float(0xc2156751), SkBits2Float(0x41202a2d));
+path.quadTo(SkBits2Float(0xc211f8e8), SkBits2Float(0x41536071), SkBits2Float(0xc218995e), SkBits2Float(0x4180a5aa));
+path.quadTo(SkBits2Float(0xc21f39d4), SkBits2Float(0x41979b1c), SkBits2Float(0xc22c0765), SkBits2Float(0x419e77ee));
+path.quadTo(SkBits2Float(0xc238d4f6), SkBits2Float(0x41a554c0), SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4));
+path.close();
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_378b(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(-47.1494f, 4.35143f);
+path.quadTo(-39.8075f, 18.9486f, -43.0083f, 19.8062f);
+path.quadTo(-50.35f, 5.21042f, -52.0068f, 8.08022f);
+path.quadTo(-53.6632f, 10.9494f, -52.8062f, 14.1494f);
+path.quadTo(-53.6639f, 10.9486f, -52.007f, 8.07884f);
+path.quadTo(-50.3502f, 5.20908f, -47.1494f, 4.35143f);
+path.close();
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4));
+path.quadTo(SkBits2Float(0xc24fca68), SkBits2Float(0x418ad2e8), SkBits2Float(0xc25338d1), SkBits2Float(0x41626f8c));
+path.quadTo(SkBits2Float(0xc256a73a), SkBits2Float(0x412f3944), SkBits2Float(0xc25006c4), SkBits2Float(0x41014e62));
+path.quadTo(SkBits2Float(0xc21f39d4), SkBits2Float(0x41979b1c), SkBits2Float(0xc22c0765), SkBits2Float(0x419e77ee));
+path.quadTo(SkBits2Float(0xc238d4f6), SkBits2Float(0x41a554c0), SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4));
+path.close();
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_378c(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+    path.moveTo(-47.1494f, 4.35143f);
+    path.quadTo(-46.208f, 20.6664f, -43.0072f, 19.8086f);
+    path.quadTo(-39.8065f, 18.9507f, -38.1498f, 16.0809f);
+    path.quadTo(-36.4931f, 13.211f, -37.3509f, 10.0103f);
+    path.quadTo(-37.351f, 10.0098f, -37.3512f, 10.0093f);
+    path.close();
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+    path.moveTo(-49.0778f, 19.0097f);
+    path.quadTo(-38.2087f, 6.80955f, -37.3509f, 10.0103f);
+    path.quadTo(-36.4931f, 13.211f, -38.1498f, 16.0809f);
+    path.quadTo(-39.8065f, 18.9507f, -43.0072f, 19.8086f);
+    path.close();
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_378d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(-47.1494f, 4.35143f);
+path.quadTo(-38.2091f, 6.80749f, -37.3514f, 10.0083f);  // required
+path.quadTo(-36.4938f, 13.2091f, -38.1506f, 16.0788f);  // required
+path.close();
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(-49.0778f, 19.0097f);
+path.quadTo(-38.2087f, 6.80955f, -37.3509f, 10.0103f);
+path.quadTo(-36.4931f, 13.211f, -38.1498f, 16.0809f);
+path.close();
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_558(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x41c95d06), SkBits2Float(0xc238e312));
+path.quadTo(SkBits2Float(0x41e37302), SkBits2Float(0xc23b3f66), SkBits2Float(0x41f93bb2), SkBits2Float(0xc233b1b3));
+path.quadTo(SkBits2Float(0x42025d9e), SkBits2Float(0xc22fb50a), SkBits2Float(0x4205bcea), SkBits2Float(0xc22a30db));
+path.quadTo(SkBits2Float(0x420be531), SkBits2Float(0xc22837fe), SkBits2Float(0x421120f1), SkBits2Float(0xc2239353));
+path.quadTo(SkBits2Float(0x421b0b2d), SkBits2Float(0xc21ac757), SkBits2Float(0x421bd594), SkBits2Float(0xc20d8c25));
+path.quadTo(SkBits2Float(0x421c9ffc), SkBits2Float(0xc20050f2), SkBits2Float(0x4213d3fe), SkBits2Float(0xc1eccd6f));
+path.quadTo(SkBits2Float(0x420b0802), SkBits2Float(0xc1d8f8fa), SkBits2Float(0x41fb99a0), SkBits2Float(0xc1d7642b));
+path.quadTo(SkBits2Float(0x41e1233b), SkBits2Float(0xc1d5cf5c), SkBits2Float(0x41cd4ec5), SkBits2Float(0xc1e76755));
+path.quadTo(SkBits2Float(0x41c5ef3d), SkBits2Float(0xc1edf201), SkBits2Float(0x41c11591), SkBits2Float(0xc1f5b68f));
+path.quadTo(SkBits2Float(0x41b863c9), SkBits2Float(0xc1f896c5), SkBits2Float(0x41b04a41), SkBits2Float(0xc1fe34bf));
+path.quadTo(SkBits2Float(0x419a8190), SkBits2Float(0xc206a812), SkBits2Float(0x4195c8e8), SkBits2Float(0xc213b310));
+path.quadTo(SkBits2Float(0x41911040), SkBits2Float(0xc220be0e), SkBits2Float(0x41a02ba6), SkBits2Float(0xc22ba266));
+path.quadTo(SkBits2Float(0x41af470a), SkBits2Float(0xc23686bf), SkBits2Float(0x41c95d06), SkBits2Float(0xc238e312));
+path.close();
+path.moveTo(SkBits2Float(0xc2169738), SkBits2Float(0xc2131d1b));
+path.quadTo(SkBits2Float(0xc2096e21), SkBits2Float(0xc214b131), SkBits2Float(0xc1fe042e), SkBits2Float(0xc20c809d));
+path.quadTo(SkBits2Float(0xc1e92c1a), SkBits2Float(0xc204500a), SkBits2Float(0xc1e603ef), SkBits2Float(0xc1ee4de5));
+path.quadTo(SkBits2Float(0xc1e2dbc3), SkBits2Float(0xc1d3fbb6), SkBits2Float(0xc1f33ce9), SkBits2Float(0xc1bf23a3));
+path.quadTo(SkBits2Float(0xc201cf08), SkBits2Float(0xc1aa4b8f), SkBits2Float(0xc20ef820), SkBits2Float(0xc1a72363));
+path.quadTo(SkBits2Float(0xc21c2138), SkBits2Float(0xc1a3fb38), SkBits2Float(0xc2268d41), SkBits2Float(0xc1b45c5e));
+path.quadTo(SkBits2Float(0xc230f94b), SkBits2Float(0xc1c4bd85), SkBits2Float(0xc2328d61), SkBits2Float(0xc1df0fb4));
+path.quadTo(SkBits2Float(0xc2342177), SkBits2Float(0xc1f961e4), SkBits2Float(0xc22bf0e3), SkBits2Float(0xc2071cfb));
+path.quadTo(SkBits2Float(0xc223c050), SkBits2Float(0xc2118905), SkBits2Float(0xc2169738), SkBits2Float(0xc2131d1b));
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0xbe8f799b), SkBits2Float(0x42240000), SkBits2Float(0xbf0db675), SkBits2Float(0x4223eed6));
+path.quadTo(SkBits2Float(0xc060c2a3), SkBits2Float(0x42233513), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.lineTo(SkBits2Float(0xc0c24f68), SkBits2Float(0x4218d9ff));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x421005d8), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0x42215fd9), SkBits2Float(0xc1c64bb4));
+path.quadTo(SkBits2Float(0x422dad8e), SkBits2Float(0xc1d0284f), SkBits2Float(0x4239dd52), SkBits2Float(0xc1c5bb1d));
+path.quadTo(SkBits2Float(0x42460d14), SkBits2Float(0xc1bb4dea), SkBits2Float(0x424afb61), SkBits2Float(0xc1a2b282));
+path.quadTo(SkBits2Float(0x424fe9af), SkBits2Float(0xc18a1717), SkBits2Float(0x424ab316), SkBits2Float(0xc1636f22));
+path.quadTo(SkBits2Float(0x42457c7c), SkBits2Float(0xc132b016), SkBits2Float(0x42392ec8), SkBits2Float(0xc11ef6e3));
+path.quadTo(SkBits2Float(0x422ce113), SkBits2Float(0xc10b3dad), SkBits2Float(0x4220b150), SkBits2Float(0xc1201812));
+path.quadTo(SkBits2Float(0x4214818d), SkBits2Float(0xc134f276), SkBits2Float(0x420f9340), SkBits2Float(0xc1662949));
+path.quadTo(SkBits2Float(0x420aa4f2), SkBits2Float(0xc18bb00e), SkBits2Float(0x420fdb8c), SkBits2Float(0xc1a40f94));
+path.quadTo(SkBits2Float(0x42151225), SkBits2Float(0xc1bc6f1a), SkBits2Float(0x42215fd9), SkBits2Float(0xc1c64bb4));
+path.close();
+path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xbfe9fe20), SkBits2Float(0x42526568));
+path.quadTo(SkBits2Float(0xc08f5cf4), SkBits2Float(0x425a57e7), SkBits2Float(0xc0f853f0), SkBits2Float(0x4258763b));
+path.quadTo(SkBits2Float(0xc130a57c), SkBits2Float(0x42569490), SkBits2Float(0xc1506f76), SkBits2Float(0x424bf8e3));
+path.quadTo(SkBits2Float(0xc1703970), SkBits2Float(0x42415d36), SkBits2Float(0xc168b2c0), SkBits2Float(0x42343e56));
+path.quadTo(SkBits2Float(0xc1612c17), SkBits2Float(0x42271f76), SkBits2Float(0xc136bd61), SkBits2Float(0x421f2cf7));
+path.quadTo(SkBits2Float(0xc10c4ead), SkBits2Float(0x42173a78), SkBits2Float(0xc0afa654), SkBits2Float(0x42191c24));
+path.quadTo(SkBits2Float(0xc00d5ea8), SkBits2Float(0x421afdcf), SkBits2Float(0xbe636c00), SkBits2Float(0x4225997c));
+path.quadTo(SkBits2Float(0x3fe1e250), SkBits2Float(0x42303529), SkBits2Float(0x3fa5acf0), SkBits2Float(0x423d5409));
+path.quadTo(SkBits2Float(0x3f52ef00), SkBits2Float(0x424a72ea), SkBits2Float(0xbfe9fe20), SkBits2Float(0x42526568));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_378a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x40f4c1fc), SkBits2Float(0xc25049c6));
+path.quadTo(SkBits2Float(0x41281306), SkBits2Float(0xc25702a0), SkBits2Float(0x415b6610), SkBits2Float(0xc253af82));
+path.quadTo(SkBits2Float(0x41875c90), SkBits2Float(0xc2505c66), SkBits2Float(0x4194ce44), SkBits2Float(0xc244efe4));
+path.quadTo(SkBits2Float(0x41a23ff8), SkBits2Float(0xc2398363), SkBits2Float(0x419b99bc), SkBits2Float(0xc22caea0));
+path.quadTo(SkBits2Float(0x4194f385), SkBits2Float(0xc21fd9dc), SkBits2Float(0x417c3502), SkBits2Float(0xc2192102));
+path.quadTo(SkBits2Float(0x414e82fc), SkBits2Float(0xc2126828), SkBits2Float(0x411b2fef), SkBits2Float(0xc215bb45));
+path.quadTo(SkBits2Float(0x40cfb9c4), SkBits2Float(0xc2190e62), SkBits2Float(0x4099f2f4), SkBits2Float(0xc2247ae4));
+path.quadTo(SkBits2Float(0x40485848), SkBits2Float(0xc22fe766), SkBits2Float(0x407d8a18), SkBits2Float(0xc23cbc28));
+path.quadTo(SkBits2Float(0x40995df0), SkBits2Float(0xc24990ec), SkBits2Float(0x40f4c1fc), SkBits2Float(0xc25049c6));
+path.close();
+path.moveTo(SkBits2Float(0xc20605f2), SkBits2Float(0xc22259cd));
+path.quadTo(SkBits2Float(0xc1f189ee), SkBits2Float(0xc22283df), SkBits2Float(0xc1de900b), SkBits2Float(0xc219426c));
+path.quadTo(SkBits2Float(0xc1cb9626), SkBits2Float(0xc21000fa), SkBits2Float(0xc1cb4202), SkBits2Float(0xc202c000));
+path.quadTo(SkBits2Float(0xc1caeddd), SkBits2Float(0xc1eafe0b), SkBits2Float(0xc1dd70c3), SkBits2Float(0xc1d80427));
+path.quadTo(SkBits2Float(0xc1eff3a7), SkBits2Float(0xc1c50a43), SkBits2Float(0xc2053ace), SkBits2Float(0xc1c4b61e));
+path.quadTo(SkBits2Float(0xc2127bc8), SkBits2Float(0xc1c461fa), SkBits2Float(0xc21bf8ba), SkBits2Float(0xc1d6e4df));
+path.quadTo(SkBits2Float(0xc22575ad), SkBits2Float(0xc1e967c4), SkBits2Float(0xc2259fbf), SkBits2Float(0xc201f4dc));
+path.quadTo(SkBits2Float(0xc225c9d1), SkBits2Float(0xc20f35d6), SkBits2Float(0xc21c885e), SkBits2Float(0xc218b2c8));
+path.quadTo(SkBits2Float(0xc21346ec), SkBits2Float(0xc2222fbb), SkBits2Float(0xc20605f2), SkBits2Float(0xc22259cd));
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
+path.quadTo(SkBits2Float(0xc2155d3d), SkBits2Float(0x4120c08f), SkBits2Float(0xc2155303), SkBits2Float(0x41215e9f));
+path.quadTo(SkBits2Float(0xc21547f6), SkBits2Float(0x4121fb98), SkBits2Float(0xc2153d2f), SkBits2Float(0x412299db));
+path.quadTo(SkBits2Float(0xc2153265), SkBits2Float(0x41233845), SkBits2Float(0xc21527fc), SkBits2Float(0x4123d684));
+path.quadTo(SkBits2Float(0xc2151cc4), SkBits2Float(0x41247361), SkBits2Float(0xc21511d9), SkBits2Float(0x41251125));
+path.quadTo(SkBits2Float(0xc211888d), SkBits2Float(0x41582a1e), SkBits2Float(0xc21810d2), SkBits2Float(0x4183262b));
+path.quadTo(SkBits2Float(0xc21e9918), SkBits2Float(0x419a3747), SkBits2Float(0xc22b5f56), SkBits2Float(0x41a149de));
+path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb));
+path.quadTo(SkBits2Float(0xc24f36b0), SkBits2Float(0x418e3b60), SkBits2Float(0xc252bffc), SkBits2Float(0x41695dc8));
+path.quadTo(SkBits2Float(0xc252cf70), SkBits2Float(0x41687e79), SkBits2Float(0xc252de2e), SkBits2Float(0x41679ef4));
+path.quadTo(SkBits2Float(0xc252ee09), SkBits2Float(0x4166c0c4), SkBits2Float(0xc252fd41), SkBits2Float(0x4165e14c));
+path.quadTo(SkBits2Float(0xc2530c80), SkBits2Float(0x41650165), SkBits2Float(0xc2531afd), SkBits2Float(0x416421f9));
+path.quadTo(SkBits2Float(0xc2532a97), SkBits2Float(0x416343e9), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.close();
+path.moveTo(SkBits2Float(0x4204d274), SkBits2Float(0x41c5cf9d));
+path.quadTo(SkBits2Float(0x42121393), SkBits2Float(0x41c59784), SkBits2Float(0x421b86b4), SkBits2Float(0x41d82e73));
+path.quadTo(SkBits2Float(0x4224f9d6), SkBits2Float(0x41eac561), SkBits2Float(0x422515e3), SkBits2Float(0x4202a3d1));
+path.quadTo(SkBits2Float(0x422531ef), SkBits2Float(0x420fe4f0), SkBits2Float(0x421be677), SkBits2Float(0x42195811));
+path.quadTo(SkBits2Float(0x421b94ff), SkBits2Float(0x4219aae4), SkBits2Float(0x421b423a), SkBits2Float(0x4219fb06));
+path.lineTo(SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41db32ee), SkBits2Float(0x41da4a98), SkBits2Float(0x41dba82d), SkBits2Float(0x41d9d952));
+path.quadTo(SkBits2Float(0x41dc1880), SkBits2Float(0x41d9631e), SkBits2Float(0x41dc8bb9), SkBits2Float(0x41d8edf9));
+path.quadTo(SkBits2Float(0x41ef22a8), SkBits2Float(0x41c607b5), SkBits2Float(0x4204d274), SkBits2Float(0x41c5cf9d));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb));
+path.quadTo(SkBits2Float(0xc24f36b0), SkBits2Float(0x418e3b60), SkBits2Float(0xc252bffc), SkBits2Float(0x41695dc8));
+path.quadTo(SkBits2Float(0xc2564948), SkBits2Float(0x413644ce), SkBits2Float(0xc24fc102), SkBits2Float(0x41082296));
+path.quadTo(SkBits2Float(0xc24938bd), SkBits2Float(0x40b400bc), SkBits2Float(0xc23c727f), SkBits2Float(0x4097b660));
+path.quadTo(SkBits2Float(0xc22fac40), SkBits2Float(0x4076d800), SkBits2Float(0xc22423b2), SkBits2Float(0x40afae2c));
+path.quadTo(SkBits2Float(0xc2189b24), SkBits2Float(0x40e3f058), SkBits2Float(0xc21511d9), SkBits2Float(0x41251125));
+path.quadTo(SkBits2Float(0xc211888d), SkBits2Float(0x41582a1e), SkBits2Float(0xc21810d2), SkBits2Float(0x4183262b));
+path.quadTo(SkBits2Float(0xc21e9918), SkBits2Float(0x419a3747), SkBits2Float(0xc22b5f56), SkBits2Float(0x41a149de));
+path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+
+static void fuzz763_378a_1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
+path.quadTo(SkBits2Float(0xc2155d3d), SkBits2Float(0x4120c08f), SkBits2Float(0xc2155303), SkBits2Float(0x41215e9f));
+path.quadTo(SkBits2Float(0xc21547f6), SkBits2Float(0x4121fb98), SkBits2Float(0xc2153d2f), SkBits2Float(0x412299db));
+path.quadTo(SkBits2Float(0xc2153265), SkBits2Float(0x41233845), SkBits2Float(0xc21527fc), SkBits2Float(0x4123d684));
+path.quadTo(SkBits2Float(0xc2151cc4), SkBits2Float(0x41247361), SkBits2Float(0xc21511d9), SkBits2Float(0x41251125));
+path.quadTo(SkBits2Float(0xc211888d), SkBits2Float(0x41582a1e), SkBits2Float(0xc21810d2), SkBits2Float(0x4183262b));
+path.quadTo(SkBits2Float(0xc21e9918), SkBits2Float(0x419a3747), SkBits2Float(0xc22b5f56), SkBits2Float(0x41a149de));
+path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb));
+path.quadTo(SkBits2Float(0xc24f36b0), SkBits2Float(0x418e3b60), SkBits2Float(0xc252bffc), SkBits2Float(0x41695dc8));
+path.quadTo(SkBits2Float(0xc252cf70), SkBits2Float(0x41687e79), SkBits2Float(0xc252de2e), SkBits2Float(0x41679ef4));
+path.quadTo(SkBits2Float(0xc252ee09), SkBits2Float(0x4166c0c4), SkBits2Float(0xc252fd41), SkBits2Float(0x4165e14c));
+path.quadTo(SkBits2Float(0xc2530c80), SkBits2Float(0x41650165), SkBits2Float(0xc2531afd), SkBits2Float(0x416421f9));
+path.quadTo(SkBits2Float(0xc2532a97), SkBits2Float(0x416343e9), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb));
+path.quadTo(SkBits2Float(0xc24f36b0), SkBits2Float(0x418e3b60), SkBits2Float(0xc252bffc), SkBits2Float(0x41695dc8));
+path.quadTo(SkBits2Float(0xc2564948), SkBits2Float(0x413644ce), SkBits2Float(0xc24fc102), SkBits2Float(0x41082296));
+path.quadTo(SkBits2Float(0xc24938bd), SkBits2Float(0x40b400bc), SkBits2Float(0xc23c727f), SkBits2Float(0x4097b660));
+path.quadTo(SkBits2Float(0xc22fac40), SkBits2Float(0x4076d800), SkBits2Float(0xc22423b2), SkBits2Float(0x40afae2c));
+path.quadTo(SkBits2Float(0xc2189b24), SkBits2Float(0x40e3f058), SkBits2Float(0xc21511d9), SkBits2Float(0x41251125));
+path.quadTo(SkBits2Float(0xc211888d), SkBits2Float(0x41582a1e), SkBits2Float(0xc21810d2), SkBits2Float(0x4183262b));
+path.quadTo(SkBits2Float(0xc21e9918), SkBits2Float(0x419a3747), SkBits2Float(0xc22b5f56), SkBits2Float(0x41a149de));
+path.quadTo(SkBits2Float(0xc2382594), SkBits2Float(0x41a85c76), SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_8712(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x40dce520), SkBits2Float(0xc250b45c));
+path.quadTo(SkBits2Float(0x411bc0ec), SkBits2Float(0xc25796e0), SkBits2Float(0x414f4352), SkBits2Float(0xc25472d6));
+path.quadTo(SkBits2Float(0x418162dd), SkBits2Float(0xc2514ece), SkBits2Float(0x418f27e4), SkBits2Float(0xc245fb37));
+path.quadTo(SkBits2Float(0x419cecea), SkBits2Float(0xc23aa7a0), SkBits2Float(0x4196a4d8), SkBits2Float(0xc22dc706));
+path.quadTo(SkBits2Float(0x41905cc8), SkBits2Float(0xc220e66c), SkBits2Float(0x41736b34), SkBits2Float(0xc21a03e9));
+path.quadTo(SkBits2Float(0x41461cda), SkBits2Float(0xc2132166), SkBits2Float(0x41129a71), SkBits2Float(0xc216456f));
+path.quadTo(SkBits2Float(0x40be3010), SkBits2Float(0xc2196978), SkBits2Float(0x40871bf8), SkBits2Float(0xc224bd0e));
+path.quadTo(SkBits2Float(0x40200fb8), SkBits2Float(0xc23010a5), SkBits2Float(0x40525050), SkBits2Float(0xc23cf13e));
+path.quadTo(SkBits2Float(0x4082486c), SkBits2Float(0xc249d1d9), SkBits2Float(0x40dce520), SkBits2Float(0xc250b45c));
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0x422b20ca), SkBits2Float(0xc1a252a8));
+path.quadTo(SkBits2Float(0x4237e448), SkBits2Float(0xc1a97900), SkBits2Float(0x424371e0), SkBits2Float(0xc19c7a4f));
+path.quadTo(SkBits2Float(0x424eff77), SkBits2Float(0xc18f7b9e), SkBits2Float(0x425292a1), SkBits2Float(0xc16be93c));
+path.quadTo(SkBits2Float(0x425625cd), SkBits2Float(0xc138db44), SkBits2Float(0x424fa674), SkBits2Float(0xc10aa4e6));
+path.quadTo(SkBits2Float(0x4249271c), SkBits2Float(0xc0b8dd14), SkBits2Float(0x423c639c), SkBits2Float(0xc09c43bc));
+path.quadTo(SkBits2Float(0x422fa01e), SkBits2Float(0xc07f54c8), SkBits2Float(0x42241287), SkBits2Float(0xc0b3a528));
+path.quadTo(SkBits2Float(0x421884f0), SkBits2Float(0xc0e79fee), SkBits2Float(0x4214f1c4), SkBits2Float(0xc126ddf2));
+path.quadTo(SkBits2Float(0x42115e99), SkBits2Float(0xc159ebed), SkBits2Float(0x4217ddf2), SkBits2Float(0xc1841124));
+path.quadTo(SkBits2Float(0x421e5d4a), SkBits2Float(0xc19b2c54), SkBits2Float(0x422b20ca), SkBits2Float(0xc1a252a8));
+path.close();
+path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
+path.quadTo(SkBits2Float(0xc2152d73), SkBits2Float(0x412389fb), SkBits2Float(0xc214fe6a), SkBits2Float(0x4126ec3a));
+path.quadTo(SkBits2Float(0xc214b621), SkBits2Float(0x412a3217), SkBits2Float(0xc21476d0), SkBits2Float(0x412d948d));
+path.quadTo(SkBits2Float(0xc210bed3), SkBits2Float(0x41607862), SkBits2Float(0xc2171cb0), SkBits2Float(0x41877c8b));
+path.quadTo(SkBits2Float(0xc21d7a8c), SkBits2Float(0x419ebce4), SkBits2Float(0xc22a3381), SkBits2Float(0x41a62cde));
+path.quadTo(SkBits2Float(0xc236ec77), SkBits2Float(0x41ad9cd6), SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e));
+path.quadTo(SkBits2Float(0xc24e2cd0), SkBits2Float(0x41942565), SkBits2Float(0xc251e4cc), SkBits2Float(0x417566f6));
+path.quadTo(SkBits2Float(0xc2523ea2), SkBits2Float(0x41709990), SkBits2Float(0xc252817b), SkBits2Float(0x416bd61f));
+path.quadTo(SkBits2Float(0xc252e6b7), SkBits2Float(0x41673927), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.close();
+path.moveTo(SkBits2Float(0x42074f3a), SkBits2Float(0x41bef2d8));
+path.quadTo(SkBits2Float(0x42148e85), SkBits2Float(0x41be0d1d), SkBits2Float(0x421e3dbf), SkBits2Float(0x41d026ae));
+path.quadTo(SkBits2Float(0x4227ecfa), SkBits2Float(0x41e24040), SkBits2Float(0x42285fd8), SkBits2Float(0x41fcbed6));
+path.quadTo(SkBits2Float(0x4228d2b5), SkBits2Float(0x420b9eb6), SkBits2Float(0x421fc5ec), SkBits2Float(0x42154df0));
+path.quadTo(SkBits2Float(0x421f5958), SkBits2Float(0x4215c221), SkBits2Float(0x421eea62), SkBits2Float(0x42163126));
+path.quadTo(SkBits2Float(0x421e81d1), SkBits2Float(0x4216a62c), SkBits2Float(0x421e13f4), SkBits2Float(0x4217191c));
+path.quadTo(SkBits2Float(0x421d36b1), SkBits2Float(0x42180097), SkBits2Float(0x421c5020), SkBits2Float(0x4218d2e4));
+path.quadTo(SkBits2Float(0x421bb44d), SkBits2Float(0x421985ae), SkBits2Float(0x421b0c17), SkBits2Float(0x421a3367));
+path.lineTo(SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41dbfdf8), SkBits2Float(0x41d97f8e), SkBits2Float(0x41dd45f8), SkBits2Float(0x41d85595));
+path.quadTo(SkBits2Float(0x41de6877), SkBits2Float(0x41d706ef), SkBits2Float(0x41dfa063), SkBits2Float(0x41d5c09b));
+path.quadTo(SkBits2Float(0x41e03b86), SkBits2Float(0x41d51e4d), SkBits2Float(0x41e0d904), SkBits2Float(0x41d48124));
+path.quadTo(SkBits2Float(0x41e16d06), SkBits2Float(0x41d3db0f), SkBits2Float(0x41e2064d), SkBits2Float(0x41d33709));
+path.quadTo(SkBits2Float(0x41f41fdf), SkBits2Float(0x41bfd894), SkBits2Float(0x42074f3a), SkBits2Float(0x41bef2d8));
+path.close();
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e));
+path.quadTo(SkBits2Float(0xc24e2cd0), SkBits2Float(0x41942565), SkBits2Float(0xc251e4cc), SkBits2Float(0x417566f6));
+path.quadTo(SkBits2Float(0xc2559cca), SkBits2Float(0x4142831e), SkBits2Float(0xc24f3eed), SkBits2Float(0x4114026c));
+path.quadTo(SkBits2Float(0xc248e111), SkBits2Float(0x40cb0370), SkBits2Float(0xc23c281b), SkBits2Float(0x40ad4390));
+path.quadTo(SkBits2Float(0xc22f6f26), SkBits2Float(0x408f83a8), SkBits2Float(0xc223cefa), SkBits2Float(0x40c2728a));
+path.quadTo(SkBits2Float(0xc2182ecc), SkBits2Float(0x40f5616e), SkBits2Float(0xc21476d0), SkBits2Float(0x412d948d));
+path.quadTo(SkBits2Float(0xc210bed3), SkBits2Float(0x41607862), SkBits2Float(0xc2171cb0), SkBits2Float(0x41877c8b));
+path.quadTo(SkBits2Float(0xc21d7a8c), SkBits2Float(0x419ebce4), SkBits2Float(0xc22a3381), SkBits2Float(0x41a62cde));
+path.quadTo(SkBits2Float(0xc236ec77), SkBits2Float(0x41ad9cd6), SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_8712a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
+path.quadTo(SkBits2Float(0xc2152d73), SkBits2Float(0x412389fb), SkBits2Float(0xc214fe6a), SkBits2Float(0x4126ec3a));
+path.quadTo(SkBits2Float(0xc214b621), SkBits2Float(0x412a3217), SkBits2Float(0xc21476d0), SkBits2Float(0x412d948d));
+path.quadTo(SkBits2Float(0xc210bed3), SkBits2Float(0x41607862), SkBits2Float(0xc2171cb0), SkBits2Float(0x41877c8b));
+path.quadTo(SkBits2Float(0xc21d7a8c), SkBits2Float(0x419ebce4), SkBits2Float(0xc22a3381), SkBits2Float(0x41a62cde));
+path.quadTo(SkBits2Float(0xc236ec77), SkBits2Float(0x41ad9cd6), SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e));
+path.quadTo(SkBits2Float(0xc24e2cd0), SkBits2Float(0x41942565), SkBits2Float(0xc251e4cc), SkBits2Float(0x417566f6));
+path.quadTo(SkBits2Float(0xc2523ea2), SkBits2Float(0x41709990), SkBits2Float(0xc252817b), SkBits2Float(0x416bd61f));
+path.quadTo(SkBits2Float(0xc252e6b7), SkBits2Float(0x41673927), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.close();
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e));
+path.quadTo(SkBits2Float(0xc24e2cd0), SkBits2Float(0x41942565), SkBits2Float(0xc251e4cc), SkBits2Float(0x417566f6));
+path.quadTo(SkBits2Float(0xc2559cca), SkBits2Float(0x4142831e), SkBits2Float(0xc24f3eed), SkBits2Float(0x4114026c));
+path.quadTo(SkBits2Float(0xc248e111), SkBits2Float(0x40cb0370), SkBits2Float(0xc23c281b), SkBits2Float(0x40ad4390));
+path.quadTo(SkBits2Float(0xc22f6f26), SkBits2Float(0x408f83a8), SkBits2Float(0xc223cefa), SkBits2Float(0x40c2728a));
+path.quadTo(SkBits2Float(0xc2182ecc), SkBits2Float(0x40f5616e), SkBits2Float(0xc21476d0), SkBits2Float(0x412d948d));
+path.quadTo(SkBits2Float(0xc210bed3), SkBits2Float(0x41607862), SkBits2Float(0xc2171cb0), SkBits2Float(0x41877c8b));
+path.quadTo(SkBits2Float(0xc21d7a8c), SkBits2Float(0x419ebce4), SkBits2Float(0xc22a3381), SkBits2Float(0x41a62cde));
+path.quadTo(SkBits2Float(0xc236ec77), SkBits2Float(0x41ad9cd6), SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_4014(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x4126977e), SkBits2Float(0xc24e5cc8));
+path.quadTo(SkBits2Float(0x4155a79e), SkBits2Float(0xc2547762), SkBits2Float(0x41841952), SkBits2Float(0xc250767b));
+path.quadTo(SkBits2Float(0x419d5ed4), SkBits2Float(0xc24c7594), SkBits2Float(0x41a99408), SkBits2Float(0xc240b18c));
+path.quadTo(SkBits2Float(0x41b5c93d), SkBits2Float(0xc234ed84), SkBits2Float(0x41adc770), SkBits2Float(0xc2284ac3));
+path.quadTo(SkBits2Float(0x41a5c5a2), SkBits2Float(0xc21ba802), SkBits2Float(0x418e3d92), SkBits2Float(0xc2158d68));
+path.quadTo(SkBits2Float(0x416d6b02), SkBits2Float(0xc20f72ce), SkBits2Float(0x413adfff), SkBits2Float(0xc21373b4));
+path.quadTo(SkBits2Float(0x410854fa), SkBits2Float(0xc217749a), SkBits2Float(0x40dfd522), SkBits2Float(0xc22338a3));
+path.quadTo(SkBits2Float(0x40af0050), SkBits2Float(0xc22efcab), SkBits2Float(0x40cf0788), SkBits2Float(0xc23b9f6c));
+path.quadTo(SkBits2Float(0x40ef0eb8), SkBits2Float(0xc248422e), SkBits2Float(0x4126977e), SkBits2Float(0xc24e5cc8));
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0x422dc5fa), SkBits2Float(0xc196a9b4));
+path.quadTo(SkBits2Float(0x423aa688), SkBits2Float(0xc19cf222), SkBits2Float(0x4245fa38), SkBits2Float(0xc18f2d6c));
+path.quadTo(SkBits2Float(0x42514de7), SkBits2Float(0xc18168b7), SkBits2Float(0x4254721e), SkBits2Float(0xc14f4f32));
+path.quadTo(SkBits2Float(0x42579654), SkBits2Float(0xc11bccf8), SkBits2Float(0x4250b3fa), SkBits2Float(0xc0dcfc74));
+path.quadTo(SkBits2Float(0x4249d19f), SkBits2Float(0xc0825efc), SkBits2Float(0x423cf110), SkBits2Float(0xc0527a88));
+path.quadTo(SkBits2Float(0x42301082), SkBits2Float(0xc0203718), SkBits2Float(0x4224bcd2), SkBits2Float(0xc0872e60));
+path.quadTo(SkBits2Float(0x42196923), SkBits2Float(0xc0be4136), SkBits2Float(0x421644ec), SkBits2Float(0xc112a2d8));
+path.quadTo(SkBits2Float(0x421320b5), SkBits2Float(0xc1462514), SkBits2Float(0x421a0310), SkBits2Float(0xc17373d0));
+path.quadTo(SkBits2Float(0x4220e56a), SkBits2Float(0xc1906147), SkBits2Float(0x422dc5fa), SkBits2Float(0xc196a9b4));
+path.close();
+path.moveTo(SkBits2Float(0xc23d30a0), SkBits2Float(0x400e5c28));
+path.quadTo(SkBits2Float(0xc2303ecc), SkBits2Float(0x3fc17f10), SkBits2Float(0xc2251387), SkBits2Float(0x4052f2a8));
+path.quadTo(SkBits2Float(0xc219e842), SkBits2Float(0x40a292e2), SkBits2Float(0xc2170e78), SkBits2Float(0x410510c3));
+path.quadTo(SkBits2Float(0xc216ac8b), SkBits2Float(0x410c0373), SkBits2Float(0xc21678a8), SkBits2Float(0x4112d552));
+path.quadTo(SkBits2Float(0xc215ddb7), SkBits2Float(0x411942a8), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.quadTo(SkBits2Float(0xc253e12c), SkBits2Float(0x41589e3d), SkBits2Float(0xc2542b01), SkBits2Float(0x414f09d7));
+path.quadTo(SkBits2Float(0xc25503cd), SkBits2Float(0x414600f6), SkBits2Float(0xc2558f0e), SkBits2Float(0x413c1fa8));
+path.quadTo(SkBits2Float(0xc25868d8), SkBits2Float(0x41085856), SkBits2Float(0xc25145a6), SkBits2Float(0x40b75684));
+path.quadTo(SkBits2Float(0xc24a2274), SkBits2Float(0x403bf8b8), SkBits2Float(0xc23d30a0), SkBits2Float(0x400e5c28));
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4218d6c4), SkBits2Float(0x421c6a78), SkBits2Float(0x4216e8ba), SkBits2Float(0x421ddcf4));
+path.quadTo(SkBits2Float(0x42156061), SkBits2Float(0x421fb9cf), SkBits2Float(0x42138263), SkBits2Float(0x42216e93));
+path.quadTo(SkBits2Float(0x42129692), SkBits2Float(0x4222460e), SkBits2Float(0x4211a2ed), SkBits2Float(0x422307e2));
+path.quadTo(SkBits2Float(0x4210c6f1), SkBits2Float(0x4223e438), SkBits2Float(0x420fd652), SkBits2Float(0x4224b658));
+path.quadTo(SkBits2Float(0x4205da74), SkBits2Float(0x422d6e4b), SkBits2Float(0x41f141e8), SkBits2Float(0x422c893f));
+path.quadTo(SkBits2Float(0x41d6cee9), SkBits2Float(0x422ba432), SkBits2Float(0x41c55f04), SkBits2Float(0x4221a853));
+path.quadTo(SkBits2Float(0x41b3ef1f), SkBits2Float(0x4217ac75), SkBits2Float(0x41b5b938), SkBits2Float(0x420a72f5));
+path.quadTo(SkBits2Float(0x41b78350), SkBits2Float(0x41fa72eb), SkBits2Float(0x41cb7b0e), SkBits2Float(0x41e90306));
+path.quadTo(SkBits2Float(0x41ccce3f), SkBits2Float(0x41e7dad2), SkBits2Float(0x41ce28c1), SkBits2Float(0x41e6c848));
+path.quadTo(SkBits2Float(0x41cf607c), SkBits2Float(0x41e58ed0), SkBits2Float(0x41d0aced), SkBits2Float(0x41e45f0b));
+path.quadTo(SkBits2Float(0x41d34d34), SkBits2Float(0x41e1f8bf), SkBits2Float(0x41d60d52), SkBits2Float(0x41dfea66));
+path.quadTo(SkBits2Float(0x41d83ad5), SkBits2Float(0x41dd42b1), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.close();
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xc2478a00), SkBits2Float(0x418639e0));
+path.quadTo(SkBits2Float(0xc252b546), SkBits2Float(0x416fe6f8), SkBits2Float(0xc2558f0e), SkBits2Float(0x413c1fa8));
+path.quadTo(SkBits2Float(0xc25868d8), SkBits2Float(0x41085856), SkBits2Float(0xc25145a6), SkBits2Float(0x40b75684));
+path.quadTo(SkBits2Float(0xc24a2274), SkBits2Float(0x403bf8b8), SkBits2Float(0xc23d30a0), SkBits2Float(0x400e5c28));
+path.quadTo(SkBits2Float(0xc2303ecc), SkBits2Float(0x3fc17f10), SkBits2Float(0xc2251387), SkBits2Float(0x4052f2a8));
+path.quadTo(SkBits2Float(0xc219e842), SkBits2Float(0x40a292e2), SkBits2Float(0xc2170e78), SkBits2Float(0x410510c3));
+path.quadTo(SkBits2Float(0xc21434af), SkBits2Float(0x4138d815), SkBits2Float(0xc21b57e0), SkBits2Float(0x41658529));
+path.quadTo(SkBits2Float(0xc2227b12), SkBits2Float(0x4189191e), SkBits2Float(0xc22f6ce6), SkBits2Float(0x418eccb0));
+path.quadTo(SkBits2Float(0xc23c5ebc), SkBits2Float(0x41948044), SkBits2Float(0xc2478a00), SkBits2Float(0x418639e0));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_4014a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0xc23d30a0), SkBits2Float(0x400e5c28));  // -47.2975f, 2.22437f
+path.quadTo(SkBits2Float(0xc2303ecc), SkBits2Float(0x3fc17f10), SkBits2Float(0xc2251387), SkBits2Float(0x4052f2a8));  // -44.0613f, 1.51169f, -41.2691f, 3.29606f
+path.quadTo(SkBits2Float(0xc219e842), SkBits2Float(0x40a292e2), SkBits2Float(0xc2170e78), SkBits2Float(0x410510c3));  // -38.4768f, 5.08043f, -37.7641f, 8.31659f
+path.quadTo(SkBits2Float(0xc216ac8b), SkBits2Float(0x410c0373), SkBits2Float(0xc21678a8), SkBits2Float(0x4112d552));  // -37.6685f, 8.75084f, -37.6178f, 9.17708f
+path.quadTo(SkBits2Float(0xc215ddb7), SkBits2Float(0x411942a8), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));  // -37.4665f, 9.57877f, -37.3514f, 10.0083f
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));  // -36.4938f, 13.2091f, -38.1506f, 16.0788f
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));  // -39.8075f, 18.9486f, -43.0083f, 19.8062f
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));  // -46.2091f, 20.6639f, -49.0788f, 19.007f
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));  // -51.9486f, 17.3502f, -52.8062f, 14.1494f
+path.quadTo(SkBits2Float(0xc253e12c), SkBits2Float(0x41589e3d), SkBits2Float(0xc2542b01), SkBits2Float(0x414f09d7));  // -52.9699f, 13.5386f, -53.042f, 12.9399f
+path.quadTo(SkBits2Float(0xc25503cd), SkBits2Float(0x414600f6), SkBits2Float(0xc2558f0e), SkBits2Float(0x413c1fa8));  // -53.2537f, 12.3752f, -53.3897f, 11.7577f
+path.quadTo(SkBits2Float(0xc25868d8), SkBits2Float(0x41085856), SkBits2Float(0xc25145a6), SkBits2Float(0x40b75684));  // -54.1024f, 8.52157f, -52.318f, 5.72931f
+path.quadTo(SkBits2Float(0xc24a2274), SkBits2Float(0x403bf8b8), SkBits2Float(0xc23d30a0), SkBits2Float(0x400e5c28));  // -50.5336f, 2.93706f, -47.2975f, 2.22437f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xc2478a00), SkBits2Float(0x418639e0));  // -49.8848f, 16.7783f
+path.quadTo(SkBits2Float(0xc252b546), SkBits2Float(0x416fe6f8), SkBits2Float(0xc2558f0e), SkBits2Float(0x413c1fa8));  // -52.677f, 14.9939f, -53.3897f, 11.7577f
+path.quadTo(SkBits2Float(0xc25868d8), SkBits2Float(0x41085856), SkBits2Float(0xc25145a6), SkBits2Float(0x40b75684));  // -54.1024f, 8.52157f, -52.318f, 5.72931f
+path.quadTo(SkBits2Float(0xc24a2274), SkBits2Float(0x403bf8b8), SkBits2Float(0xc23d30a0), SkBits2Float(0x400e5c28));  // -50.5336f, 2.93706f, -47.2975f, 2.22437f
+path.quadTo(SkBits2Float(0xc2303ecc), SkBits2Float(0x3fc17f10), SkBits2Float(0xc2251387), SkBits2Float(0x4052f2a8));  // -44.0613f, 1.51169f, -41.2691f, 3.29606f
+path.quadTo(SkBits2Float(0xc219e842), SkBits2Float(0x40a292e2), SkBits2Float(0xc2170e78), SkBits2Float(0x410510c3));  // -38.4768f, 5.08043f, -37.7641f, 8.31659f
+path.quadTo(SkBits2Float(0xc21434af), SkBits2Float(0x4138d815), SkBits2Float(0xc21b57e0), SkBits2Float(0x41658529));  // -37.0514f, 11.5528f, -38.8358f, 14.345f
+path.quadTo(SkBits2Float(0xc2227b12), SkBits2Float(0x4189191e), SkBits2Float(0xc22f6ce6), SkBits2Float(0x418eccb0));  // -40.6202f, 17.1373f, -43.8563f, 17.8499f
+path.quadTo(SkBits2Float(0xc23c5ebc), SkBits2Float(0x41948044), SkBits2Float(0xc2478a00), SkBits2Float(0x418639e0));  // -47.0925f, 18.5626f, -49.8848f, 16.7783f
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_1404(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x419b2e3e), SkBits2Float(0xc243b405));
+path.quadTo(SkBits2Float(0x41b4811d), SkBits2Float(0xc2479f9a), SkBits2Float(0x41cbf476), SkBits2Float(0xc2417131));
+path.quadTo(SkBits2Float(0x41e19882), SkBits2Float(0xc23bbceb), SkBits2Float(0x41e9f15f), SkBits2Float(0xc23083cb));
+path.quadTo(SkBits2Float(0x4200ef06), SkBits2Float(0xc2310b91), SkBits2Float(0x420a6762), SkBits2Float(0xc2294dac));
+path.quadTo(SkBits2Float(0x4214aa80), SkBits2Float(0xc220ea0a), SkBits2Float(0x4215fd8a), SkBits2Float(0xc213b9c8));
+path.quadTo(SkBits2Float(0x42175094), SkBits2Float(0xc2068986), SkBits2Float(0x420eecf0), SkBits2Float(0xc1f88cd3));
+path.quadTo(SkBits2Float(0x4206894d), SkBits2Float(0xc1e4069a), SkBits2Float(0x41f2b216), SkBits2Float(0xc1e16085));
+path.quadTo(SkBits2Float(0x41d85192), SkBits2Float(0xc1deba71), SkBits2Float(0x41c3cb5a), SkBits2Float(0xc1ef81b8));
+path.quadTo(SkBits2Float(0x41b61bc0), SkBits2Float(0xc1fab1e0), SkBits2Float(0x41b05ee8), SkBits2Float(0xc2051350));
+path.quadTo(SkBits2Float(0x419fe690), SkBits2Float(0xc204b4a5), SkBits2Float(0x4190436e), SkBits2Float(0xc208d3d1));
+path.quadTo(SkBits2Float(0x4171a027), SkBits2Float(0xc20f0238), SkBits2Float(0x4161f1d7), SkBits2Float(0xc21baba8));
+path.quadTo(SkBits2Float(0x41524386), SkBits2Float(0xc2285517), SkBits2Float(0x416afd23), SkBits2Float(0xc2340ec3));
+path.quadTo(SkBits2Float(0x4181db5f), SkBits2Float(0xc23fc871), SkBits2Float(0x419b2e3e), SkBits2Float(0xc243b405));
+path.close();
+path.moveTo(SkBits2Float(0xc221f910), SkBits2Float(0xc2067acc));
+path.quadTo(SkBits2Float(0xc214fb93), SkBits2Float(0xc2091d7b), SkBits2Float(0xc209ef07), SkBits2Float(0xc201cb14));
+path.quadTo(SkBits2Float(0xc1fdc4f6), SkBits2Float(0xc1f4f15c), SkBits2Float(0xc1f87f97), SkBits2Float(0xc1daf662));
+path.quadTo(SkBits2Float(0xc1f33a38), SkBits2Float(0xc1c0fb68), SkBits2Float(0xc200ef83), SkBits2Float(0xc1aae250));
+path.quadTo(SkBits2Float(0xc20841e9), SkBits2Float(0xc194c938), SkBits2Float(0xc2153f65), SkBits2Float(0xc18f83d9));
+path.quadTo(SkBits2Float(0xc2223ce2), SkBits2Float(0xc18a3e7a), SkBits2Float(0xc22d496e), SkBits2Float(0xc198e348));
+path.quadTo(SkBits2Float(0xc23855fb), SkBits2Float(0xc1a78814), SkBits2Float(0xc23af8aa), SkBits2Float(0xc1c1830c));
+path.quadTo(SkBits2Float(0xc23d9b5a), SkBits2Float(0xc1db7e06), SkBits2Float(0xc23648f3), SkBits2Float(0xc1f1971e));
+path.quadTo(SkBits2Float(0xc22ef68d), SkBits2Float(0xc203d81c), SkBits2Float(0xc221f910), SkBits2Float(0xc2067acc));
+path.close();
+path.moveTo(SkBits2Float(0x4218d883), SkBits2Float(0xc1dfb2a2));
+path.quadTo(SkBits2Float(0x4224b610), SkBits2Float(0xc1eb836c), SkBits2Float(0x4231475d), SkBits2Float(0xc1e31687));
+path.quadTo(SkBits2Float(0x423dd8aa), SkBits2Float(0xc1daa9a1), SkBits2Float(0x4243c10e), SkBits2Float(0xc1c2ee88));
+path.quadTo(SkBits2Float(0x4249a974), SkBits2Float(0xc1ab336d), SkBits2Float(0x42457300), SkBits2Float(0xc19210d4));
+path.quadTo(SkBits2Float(0x42413c8e), SkBits2Float(0xc171dc76), SkBits2Float(0x42355f01), SkBits2Float(0xc15a3ae1));
+path.quadTo(SkBits2Float(0x42298174), SkBits2Float(0xc142994c), SkBits2Float(0x421cf027), SkBits2Float(0xc1537318));
+path.quadTo(SkBits2Float(0x42105edb), SkBits2Float(0xc1644ce3), SkBits2Float(0x420a7675), SkBits2Float(0xc189e18c));
+path.quadTo(SkBits2Float(0x42048e10), SkBits2Float(0xc1a19ca6), SkBits2Float(0x4208c483), SkBits2Float(0xc1babf40));
+path.quadTo(SkBits2Float(0x420cfaf6), SkBits2Float(0xc1d3e1d8), SkBits2Float(0x4218d883), SkBits2Float(0xc1dfb2a2));
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4087d82a), SkBits2Float(0x42204637), SkBits2Float(0x401ecaaa), SkBits2Float(0x422284dc));
+path.quadTo(SkBits2Float(0x4033f0a5), SkBits2Float(0x4223bc28), SkBits2Float(0x4047ae10), SkBits2Float(0x4225218a));
+path.quadTo(SkBits2Float(0x40aa0f54), SkBits2Float(0x422f1027), SkBits2Float(0x40a38748), SkBits2Float(0x423c4af2));
+path.quadTo(SkBits2Float(0x409cff44), SkBits2Float(0x424985be), SkBits2Float(0x401b14b8), SkBits2Float(0x42524cc7));
+path.quadTo(SkBits2Float(0xbd754800), SkBits2Float(0x425b13d0), SkBits2Float(0xc05781d0), SkBits2Float(0x425a42cf));
+path.quadTo(SkBits2Float(0xc0d59744), SkBits2Float(0x425971cf), SkBits2Float(0xc10de7c8), SkBits2Float(0x424f8332));
+path.quadTo(SkBits2Float(0xc13103ee), SkBits2Float(0x42459494), SkBits2Float(0xc12dbfea), SkBits2Float(0x423859c9));
+path.quadTo(SkBits2Float(0xc12a7be8), SkBits2Float(0x422b1efe), SkBits2Float(0xc102c172), SkBits2Float(0x422257f4));
+path.quadTo(SkBits2Float(0xc0dbff18), SkBits2Float(0x421dc1e9), SkBits2Float(0xc0ab47a0), SkBits2Float(0x421bca58));
+path.quadTo(SkBits2Float(0xc0ad79af), SkBits2Float(0x421b8a4a), SkBits2Float(0xc0afa610), SkBits2Float(0x421b4830));
+path.quadTo(SkBits2Float(0xc0b25ad5), SkBits2Float(0x421af5e2), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x401b14b8), SkBits2Float(0x42524cc7));
+path.quadTo(SkBits2Float(0xbd754800), SkBits2Float(0x425b13d0), SkBits2Float(0xc05781d0), SkBits2Float(0x425a42cf));
+path.quadTo(SkBits2Float(0xc0d59744), SkBits2Float(0x425971cf), SkBits2Float(0xc10de7c8), SkBits2Float(0x424f8332));
+path.quadTo(SkBits2Float(0xc13103ee), SkBits2Float(0x42459494), SkBits2Float(0xc12dbfea), SkBits2Float(0x423859c9));
+path.quadTo(SkBits2Float(0xc12a7be8), SkBits2Float(0x422b1efe), SkBits2Float(0xc102c172), SkBits2Float(0x422257f4));
+path.quadTo(SkBits2Float(0xc0b60dfc), SkBits2Float(0x421990ea), SkBits2Float(0xc0186f48), SkBits2Float(0x421a61ec));
+path.quadTo(SkBits2Float(0x3f6cf5e0), SkBits2Float(0x421b32ec), SkBits2Float(0x4047ae10), SkBits2Float(0x4225218a));
+path.quadTo(SkBits2Float(0x40aa0f54), SkBits2Float(0x422f1027), SkBits2Float(0x40a38748), SkBits2Float(0x423c4af2));
+path.quadTo(SkBits2Float(0x409cff44), SkBits2Float(0x424985be), SkBits2Float(0x401b14b8), SkBits2Float(0x42524cc7));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_4713(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x40f7bc68), SkBits2Float(0xc2503bb0));
+path.quadTo(SkBits2Float(0x41299c84), SkBits2Float(0xc256ef4e), SkBits2Float(0x415ce976), SkBits2Float(0xc2539652));
+path.quadTo(SkBits2Float(0x41881b33), SkBits2Float(0xc2503d58), SkBits2Float(0x41958271), SkBits2Float(0xc244cdc4));
+path.quadTo(SkBits2Float(0x41a2e9af), SkBits2Float(0xc2395e30), SkBits2Float(0x419c37b8), SkBits2Float(0xc22c8af3));
+path.quadTo(SkBits2Float(0x419585c2), SkBits2Float(0xc21fb7b7), SkBits2Float(0x417d4d34), SkBits2Float(0xc2190418));
+path.quadTo(SkBits2Float(0x414f8ee4), SkBits2Float(0xc2125079), SkBits2Float(0x411c41f2), SkBits2Float(0xc215a974));
+path.quadTo(SkBits2Float(0x40d1ea00), SkBits2Float(0xc2190270), SkBits2Float(0x409c4d08), SkBits2Float(0xc2247204));
+path.quadTo(SkBits2Float(0x404d6020), SkBits2Float(0xc22fe198), SkBits2Float(0x408177f0), SkBits2Float(0xc23cb4d4));
+path.quadTo(SkBits2Float(0x409c3fc8), SkBits2Float(0xc2498810), SkBits2Float(0x40f7bc68), SkBits2Float(0xc2503bb0));
+path.close();
+path.moveTo(SkBits2Float(0xc20487d4), SkBits2Float(0xc2239250));
+path.quadTo(SkBits2Float(0xc1ee8d37), SkBits2Float(0xc2239d4e), SkBits2Float(0xc1dbbeef), SkBits2Float(0xc21a45b5));
+path.quadTo(SkBits2Float(0xc1c8f0a7), SkBits2Float(0xc210ee1d), SkBits2Float(0xc1c8daab), SkBits2Float(0xc203ace5));
+path.quadTo(SkBits2Float(0xc1c8c4af), SkBits2Float(0xc1ecd758), SkBits2Float(0xc1db73e0), SkBits2Float(0xc1da0910));
+path.quadTo(SkBits2Float(0xc1ee2310), SkBits2Float(0xc1c73ac7), SkBits2Float(0xc20452c1), SkBits2Float(0xc1c724cb));
+path.quadTo(SkBits2Float(0xc21193f9), SkBits2Float(0xc1c70ecf), SkBits2Float(0xc21afb1d), SkBits2Float(0xc1d9be01));
+path.quadTo(SkBits2Float(0xc2246242), SkBits2Float(0xc1ec6d31), SkBits2Float(0xc2246d40), SkBits2Float(0xc20377d2));
+path.quadTo(SkBits2Float(0xc224783e), SkBits2Float(0xc210b90a), SkBits2Float(0xc21b20a5), SkBits2Float(0xc21a202d));
+path.quadTo(SkBits2Float(0xc211c90c), SkBits2Float(0xc2238752), SkBits2Float(0xc20487d4), SkBits2Float(0xc2239250));
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0xc23c7b18), SkBits2Float(0x40950470));
+path.quadTo(SkBits2Float(0xc22fb33d), SkBits2Float(0x4071d1b0), SkBits2Float(0xc2242dae), SkBits2Float(0x40ad5534));
+path.quadTo(SkBits2Float(0xc218a81f), SkBits2Float(0x40e1c194), SkBits2Float(0xc21524ab), SkBits2Float(0x41240037));
+path.quadTo(SkBits2Float(0xc211a138), SkBits2Float(0x41571fa5), SkBits2Float(0xc2182ec4), SkBits2Float(0x41829af0));
+path.quadTo(SkBits2Float(0xc21ebc50), SkBits2Float(0x4199a610), SkBits2Float(0xc22b842b), SkBits2Float(0x41a0acf4));
+path.quadTo(SkBits2Float(0xc2384c07), SkBits2Float(0x41a7b3dc), SkBits2Float(0xc243d196), SkBits2Float(0x419a98c4));
+path.quadTo(SkBits2Float(0xc24f5726), SkBits2Float(0x418d7dad), SkBits2Float(0xc252da98), SkBits2Float(0x4167dbea));
+path.quadTo(SkBits2Float(0xc2565e0c), SkBits2Float(0x4134bc7e), SkBits2Float(0xc24fd080), SkBits2Float(0x4106a640));
+path.quadTo(SkBits2Float(0xc24942f4), SkBits2Float(0x40b12008), SkBits2Float(0xc23c7b18), SkBits2Float(0x40950470));
+path.close();
+path.moveTo(SkBits2Float(0x4204f72e), SkBits2Float(0x41c56cd2));
+path.quadTo(SkBits2Float(0x42123842), SkBits2Float(0x41c52adf), SkBits2Float(0x421baed7), SkBits2Float(0x41d7bac6));
+path.quadTo(SkBits2Float(0x4225256d), SkBits2Float(0x41ea4aad), SkBits2Float(0x42254667), SkBits2Float(0x4202666b));
+path.quadTo(SkBits2Float(0x42256760), SkBits2Float(0x420fa77f), SkBits2Float(0x421c1f6c), SkBits2Float(0x42191e14));
+path.quadTo(SkBits2Float(0x421bff97), SkBits2Float(0x42193e89), SkBits2Float(0x421bdf6b), SkBits2Float(0x42195eb8));
+path.quadTo(SkBits2Float(0x421bbff6), SkBits2Float(0x42197f32), SkBits2Float(0x421ba03b), SkBits2Float(0x42199f57));
+path.quadTo(SkBits2Float(0x421b605e), SkBits2Float(0x4219e00a), SkBits2Float(0x421b1fa8), SkBits2Float(0x421a1f22));
+path.quadTo(SkBits2Float(0x421ae0f1), SkBits2Float(0x421a604b), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41db19b1), SkBits2Float(0x41da63d5), SkBits2Float(0x41db755b), SkBits2Float(0x41da0a9b));
+path.quadTo(SkBits2Float(0x41dbce01), SkBits2Float(0x41d9ae59), SkBits2Float(0x41dc285e), SkBits2Float(0x41d952ce));
+path.quadTo(SkBits2Float(0x41dc55b6), SkBits2Float(0x41d924df), SkBits2Float(0x41dc82cd), SkBits2Float(0x41d8f7cd));
+path.quadTo(SkBits2Float(0x41dcaf1e), SkBits2Float(0x41d8ca01), SkBits2Float(0x41dcdc4c), SkBits2Float(0x41d89bf0));
+path.quadTo(SkBits2Float(0x41ef6c33), SkBits2Float(0x41c5aec5), SkBits2Float(0x4204f72e), SkBits2Float(0x41c56cd2));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_24588(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x413a5194), SkBits2Float(0xc24d4e33));  // 11.6449f, -51.3264f
+path.quadTo(SkBits2Float(0x4169f3fc), SkBits2Float(0xc2532032), SkBits2Float(0x418e0c8b), SkBits2Float(0xc24ed218));  // 14.6221f, -52.7814f, 17.7561f, -51.7052f
+path.quadTo(SkBits2Float(0x41a71f17), SkBits2Float(0xc24a83ff), SkBits2Float(0x41b2c316), SkBits2Float(0xc23e9b65));  // 20.8902f, -50.6289f, 22.3453f, -47.6518f
+path.quadTo(SkBits2Float(0x41be6714), SkBits2Float(0xc232b2cb), SkBits2Float(0x41b5cae0), SkBits2Float(0xc2262985));  // 23.8003f, -44.6746f, 22.7241f, -41.5405f
+path.quadTo(SkBits2Float(0x41ad2ead), SkBits2Float(0xc219a03f), SkBits2Float(0x41955d79), SkBits2Float(0xc213ce40));  // 21.6478f, -38.4065f, 18.6706f, -36.9514f
+path.quadTo(SkBits2Float(0x417b188a), SkBits2Float(0xc20dfc40), SkBits2Float(0x4148f373), SkBits2Float(0xc2124a5a));  // 15.6935f, -35.4963f, 12.5594f, -36.5726f
+path.quadTo(SkBits2Float(0x4116ce5a), SkBits2Float(0xc2169874), SkBits2Float(0x40ff0cba), SkBits2Float(0xc222810e));  // 9.42538f, -37.6489f, 7.9703f, -40.626f
+path.quadTo(SkBits2Float(0x40d07cc0), SkBits2Float(0xc22e69a8), SkBits2Float(0x40f2ed90), SkBits2Float(0xc23af2ee));  // 6.51523f, -43.6032f, 7.5915f, -46.7372f
+path.quadTo(SkBits2Float(0x410aaf2c), SkBits2Float(0xc2477c34), SkBits2Float(0x413a5194), SkBits2Float(0xc24d4e33));  // 8.66777f, -49.8713f, 11.6449f, -51.3264f
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -19.799f, -19.799f
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));  // -11.598f, -28, 0, -28
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));  // 11.598f, -28, 19.799f, -19.799f
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));  // 28, -11.598f, 28, 0
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));  // 28, 11.598f, 19.799f, 19.799f
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));  // 13.7163f, 25.8817f, 5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));  // 8, 29.7635f, 8, 33
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));  // 8, 36.3137f, 5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));  // 3.31371f, 41, 0, 41
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));  // -3.31371f, 41, -5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));  // -8, 36.3137f, -8, 33
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));  // -8, 29.7635f, -5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));  // -13.7163f, 25.8817f, -19.799f, 19.799f
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));  // -28, 11.598f, -28, 0
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -28, -11.598f, -19.799f, -19.799f
+path.close();
+path.moveTo(SkBits2Float(0xc23d594a), SkBits2Float(0x3f8b9aa0));  // -47.3372f, 1.09066f
+path.quadTo(SkBits2Float(0xc23056ee), SkBits2Float(0x3ee95200), SkBits2Float(0xc2255841), SkBits2Float(0x40139cf0));  // -44.0849f, 0.455704f, -41.3362f, 2.30645f
+path.quadTo(SkBits2Float(0xc21a5994), SkBits2Float(0x408507d0), SkBits2Float(0xc217cf63), SkBits2Float(0x40ed1ab6));  // -38.5875f, 4.1572f, -37.9525f, 7.40951f
+path.quadTo(SkBits2Float(0xc21747fe), SkBits2Float(0x41016369), SkBits2Float(0xc2172ef9), SkBits2Float(0x410bdff7));  // -37.8203f, 8.08677f, -37.7959f, 8.74218f
+path.quadTo(SkBits2Float(0xc2161ebf), SkBits2Float(0x411577cf), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));  // -37.53f, 9.34175f, -37.3514f, 10.0083f
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));  // -36.4938f, 13.2091f, -38.1506f, 16.0788f
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));  // -39.8075f, 18.9486f, -43.0083f, 19.8062f
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));  // -46.2091f, 20.6639f, -49.0788f, 19.007f
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));  // -51.9486f, 17.3502f, -52.8062f, 14.1494f
+path.quadTo(SkBits2Float(0xc2543df8), SkBits2Float(0x415334fe), SkBits2Float(0xc2546005), SkBits2Float(0x41447d40));  // -53.0605f, 13.2004f, -53.0938f, 12.2806f
+path.quadTo(SkBits2Float(0xc255df09), SkBits2Float(0x41370862), SkBits2Float(0xc2569fcc), SkBits2Float(0x41279af0));  // -53.4678f, 11.4395f, -53.6561f, 10.4753f
+path.quadTo(SkBits2Float(0xc25929fe), SkBits2Float(0x40e722fc), SkBits2Float(0xc251c2d2), SkBits2Float(0x408f2d94));  // -54.291f, 7.22302f, -52.4403f, 4.47431f
+path.quadTo(SkBits2Float(0xc24a5ba8), SkBits2Float(0x3fdce0a0), SkBits2Float(0xc23d594a), SkBits2Float(0x3f8b9aa0));  // -50.5895f, 1.72561f, -47.3372f, 1.09066f
+path.close();
+path.moveTo(SkBits2Float(0xc18b14a2), SkBits2Float(0x42164b25));  // -17.3851f, 37.5734f
+path.quadTo(SkBits2Float(0xc1675bab), SkBits2Float(0x421010e4), SkBits2Float(0xc134a62c), SkBits2Float(0x4213efa5));  // -14.4599f, 36.0165f, -11.2906f, 36.984f
+path.quadTo(SkBits2Float(0xc101f0aa), SkBits2Float(0x4217ce66), SkBits2Float(0xc0d20f46), SkBits2Float(0x422381cc));  // -8.12126f, 37.9516f, -6.56436f, 40.8768f
+path.quadTo(SkBits2Float(0xc0a03d38), SkBits2Float(0x422f3532), SkBits2Float(0xc0bf3344), SkBits2Float(0x423be292));  // -5.00747f, 43.8019f, -5.97501f, 46.9713f
+path.quadTo(SkBits2Float(0xc0de294c), SkBits2Float(0x42488ff2), SkBits2Float(0xc11de23e), SkBits2Float(0x424eca34));  // -6.94254f, 50.1406f, -9.86773f, 51.6975f
+path.quadTo(SkBits2Float(0xc14cafd4), SkBits2Float(0x42550476), SkBits2Float(0xc17f6556), SkBits2Float(0x425125b4));  // -12.7929f, 53.2544f, -15.9622f, 52.2868f
+path.quadTo(SkBits2Float(0xc1990d6c), SkBits2Float(0x424d46f3), SkBits2Float(0xc1a581f0), SkBits2Float(0x4241938e));  // -19.1316f, 51.3193f, -20.6884f, 48.3941f
+path.quadTo(SkBits2Float(0xc1b1f673), SkBits2Float(0x4235e028), SkBits2Float(0xc1aa38f0), SkBits2Float(0x422932c8));  // -22.2453f, 45.4689f, -21.2778f, 42.2996f
+path.quadTo(SkBits2Float(0xc1a27b6c), SkBits2Float(0x421c8567), SkBits2Float(0xc18b14a2), SkBits2Float(0x42164b25));  // -20.3103f, 39.1303f, -17.3851f, 37.5734f
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 27.3431f, 27.3431f
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));  // 29.6863f, 25, 33, 25
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));  // 36.3137f, 25, 38.6569f, 27.3431f
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));  // 41, 29.6863f, 41, 33
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));  // 41, 36.3137f, 38.6569f, 38.6569f
+path.quadTo(SkBits2Float(0x4217d943), SkBits2Float(0x421d67f9), SkBits2Float(0x4214ba8d), SkBits2Float(0x421f5c6e));  // 37.9622f, 39.3515f, 37.1822f, 39.8403f
+path.quadTo(SkBits2Float(0x42129039), SkBits2Float(0x422256bd), SkBits2Float(0x420f9986), SkBits2Float(0x4224eb5c));  // 36.6408f, 40.5847f, 35.8999f, 41.2298f
+path.quadTo(SkBits2Float(0x420e25cf), SkBits2Float(0x42262f05), SkBits2Float(0x420ca0a4), SkBits2Float(0x42273ec0));  // 35.5369f, 41.5459f, 35.1569f, 41.8113f
+path.quadTo(SkBits2Float(0x420b5228), SkBits2Float(0x42288f80), SkBits2Float(0x4209d382), SkBits2Float(0x4229c624));  // 34.8302f, 42.1401f, 34.4566f, 42.4435f
+path.quadTo(SkBits2Float(0x41ff1232), SkBits2Float(0x423220d2), SkBits2Float(0x41e4b406), SkBits2Float(0x4230c247));  // 31.8839f, 44.5321f, 28.5879f, 44.1897f
+path.quadTo(SkBits2Float(0x41ca55dc), SkBits2Float(0x422f63bd), SkBits2Float(0x41b9a084), SkBits2Float(0x42251952));  // 25.2919f, 43.8474f, 23.2034f, 41.2747f
+path.quadTo(SkBits2Float(0x41a8eb2b), SkBits2Float(0x421acee9), SkBits2Float(0x41aba840), SkBits2Float(0x420d9fd3));  // 21.1148f, 38.7021f, 21.4572f, 35.4061f
+path.quadTo(SkBits2Float(0x41ae6555), SkBits2Float(0x420070be), SkBits2Float(0x41c2fa28), SkBits2Float(0x41f02c24));  // 21.7995f, 32.1101f, 24.3721f, 30.0216f
+path.quadTo(SkBits2Float(0x41c514db), SkBits2Float(0x41ee76d0), SkBits2Float(0x41c73f0f), SkBits2Float(0x41ecf584));  // 24.6352f, 29.808f, 24.9058f, 29.6199f
+path.quadTo(SkBits2Float(0x41c919c1), SkBits2Float(0x41eb15ab), SkBits2Float(0x41cb250b), SkBits2Float(0x41e94e07));  // 25.1376f, 29.3856f, 25.3931f, 29.1631f
+path.quadTo(SkBits2Float(0x41cf4ed6), SkBits2Float(0x41e5ae04), SkBits2Float(0x41d3c048), SkBits2Float(0x41e2e387));  // 25.9135f, 28.71f, 26.4689f, 28.3611f
+path.quadTo(SkBits2Float(0x41d4d649), SkBits2Float(0x41e1661f), SkBits2Float(0x41d605fb), SkBits2Float(0x41dff35b));  // 26.6046f, 28.1749f, 26.7529f, 27.9938f
+path.quadTo(SkBits2Float(0x41d7094e), SkBits2Float(0x41deb6c2), SkBits2Float(0x41d81f4d), SkBits2Float(0x41dd81fd));  // 26.8795f, 27.8392f, 27.0153f, 27.6885f
+path.lineTo(SkBits2Float(0x41d81f53), SkBits2Float(0x41dd81f7));  // 27.0153f, 27.6885f
+path.quadTo(SkBits2Float(0x41d96269), SkBits2Float(0x41dc1b1d), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 27.1731f, 27.5132f, 27.3431f, 27.3431f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x421a8288), SkBits2Float(0x420efdef));  // 38.6275f, 35.748f
+path.quadTo(SkBits2Float(0x4219989b), SkBits2Float(0x421c3719), SkBits2Float(0x420f9986), SkBits2Float(0x4224eb5c));  // 38.399f, 39.0538f, 35.8999f, 41.2298f
+path.quadTo(SkBits2Float(0x42059a71), SkBits2Float(0x422d9f9f), SkBits2Float(0x41f0c28e), SkBits2Float(0x422cb5b2));  // 33.4008f, 43.4059f, 30.095f, 43.1774f
+path.quadTo(SkBits2Float(0x41d65038), SkBits2Float(0x422bcbc5), SkBits2Float(0x41c4e7b3), SkBits2Float(0x4221ccb0));  // 26.7892f, 42.949f, 24.6131f, 40.4499f
+path.quadTo(SkBits2Float(0x41b37f2c), SkBits2Float(0x4217cd9b), SkBits2Float(0x41b55306), SkBits2Float(0x420a9471));  // 22.4371f, 37.9508f, 22.6655f, 34.645f
+path.quadTo(SkBits2Float(0x41b726e0), SkBits2Float(0x41fab68c), SkBits2Float(0x41cb250b), SkBits2Float(0x41e94e07));  // 22.894f, 31.3391f, 25.3931f, 29.1631f
+path.quadTo(SkBits2Float(0x41df2336), SkBits2Float(0x41d7e580), SkBits2Float(0x41f9958b), SkBits2Float(0x41d9b95a));  // 27.8922f, 26.9871f, 31.198f, 27.2155f
+path.quadTo(SkBits2Float(0x420a03ef), SkBits2Float(0x41db8d34), SkBits2Float(0x4212b832), SkBits2Float(0x41ef8b5f));  // 34.5038f, 27.4439f, 36.6799f, 29.9431f
+path.quadTo(SkBits2Float(0x421b6c75), SkBits2Float(0x4201c4c5), SkBits2Float(0x421a8288), SkBits2Float(0x420efdef));  // 38.8559f, 32.4422f, 38.6275f, 35.748f
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+// SkDQuadIntersection.cpp:594: failed assertion "way_roughly_zero(fT[0][index])
+static void fuzz763_20016(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x41e88c66), SkBits2Float(0xc22f800b));  // 29.0686f, -43.875f
+path.quadTo(SkBits2Float(0x420178e8), SkBits2Float(0xc230b9b9), SkBits2Float(0x420babd1), SkBits2Float(0xc228426b));  // 32.3681f, -44.1814f, 34.9178f, -42.0649f
+path.quadTo(SkBits2Float(0x4215debb), SkBits2Float(0xc21fcb1e), SkBits2Float(0x42171869), SkBits2Float(0xc2129869));  // 37.4675f, -39.9484f, 37.7738f, -36.6488f
+path.quadTo(SkBits2Float(0x42185217), SkBits2Float(0xc20565b3), SkBits2Float(0x420fdac9), SkBits2Float(0xc1f66594));  // 38.0802f, -33.3493f, 35.9637f, -30.7996f
+path.quadTo(SkBits2Float(0x4207637c), SkBits2Float(0xc1e1ffc1), SkBits2Float(0x41f4618e), SkBits2Float(0xc1df8c65));  // 33.8472f, -28.2499f, 30.5476f, -27.9436f
+path.quadTo(SkBits2Float(0x41d9fc22), SkBits2Float(0xc1dd190a), SkBits2Float(0x41c59650), SkBits2Float(0xc1ee07a4));  // 27.2481f, -27.6372f, 24.6984f, -29.7537f
+path.quadTo(SkBits2Float(0x41b1307c), SkBits2Float(0xc1fef63e), SkBits2Float(0x41aebd21), SkBits2Float(0xc20cadd5));  // 22.1487f, -31.8702f, 21.8423f, -35.1698f
+path.quadTo(SkBits2Float(0x41ac49c5), SkBits2Float(0xc219e08a), SkBits2Float(0x41bd3860), SkBits2Float(0xc2241373));  // 21.536f, -38.4693f, 23.6525f, -41.019f
+path.quadTo(SkBits2Float(0x41ce26fa), SkBits2Float(0xc22e465d), SkBits2Float(0x41e88c66), SkBits2Float(0xc22f800b));  // 25.769f, -43.5687f, 29.0686f, -43.875f
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -19.799f, -19.799f
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));  // -11.598f, -28, 0, -28
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));  // 11.598f, -28, 19.799f, -19.799f
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));  // 28, -11.598f, 28, 0
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));  // 28, 11.598f, 19.799f, 19.799f
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));  // 13.7163f, 25.8817f, 5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));  // 8, 29.7635f, 8, 33
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x42100ef9), SkBits2Float(0x40c1f194), SkBits2Float(0x4218e765));  // 8, 36.0146f, 6.06074f, 38.226f
+path.quadTo(SkBits2Float(0x4100db87), SkBits2Float(0x42186e1c), SkBits2Float(0x411cd246), SkBits2Float(0x421bbc51));  // 8.0536f, 38.1075f, 9.80134f, 38.9339f
+path.quadTo(SkBits2Float(0x41326be0), SkBits2Float(0x42146b95), SkBits2Float(0x4156b15b), SkBits2Float(0x421110b0));  // 11.1513f, 37.1051f, 13.4183f, 36.2663f
+path.quadTo(SkBits2Float(0x41843577), SkBits2Float(0x420c7739), SkBits2Float(0x419c4b3e), SkBits2Float(0x421200ec));  // 16.5261f, 35.1164f, 19.5367f, 36.5009f
+path.quadTo(SkBits2Float(0x41b46104), SkBits2Float(0x42178a9f), SkBits2Float(0x41bd93f2), SkBits2Float(0x4223f904));  // 22.5474f, 37.8854f, 23.6972f, 40.9932f
+path.quadTo(SkBits2Float(0x41c6c6e0), SkBits2Float(0x42306768), SkBits2Float(0x41bbb37b), SkBits2Float(0x423c724c));  // 24.8471f, 44.101f, 23.4626f, 47.1116f
+path.quadTo(SkBits2Float(0x41b0a015), SkBits2Float(0x42487d2f), SkBits2Float(0x4197c34c), SkBits2Float(0x424d16a6));  // 22.0782f, 50.1222f, 18.9704f, 51.2721f
+path.quadTo(SkBits2Float(0x417dcd04), SkBits2Float(0x4251b01e), SkBits2Float(0x414da178), SkBits2Float(0x424c266a));  // 15.8626f, 52.422f, 12.8519f, 51.0375f
+path.quadTo(SkBits2Float(0x414d992c), SkBits2Float(0x424c2576), SkBits2Float(0x414d90e0), SkBits2Float(0x424c2481));  // 12.8499f, 51.0366f, 12.8479f, 51.0356f
+path.quadTo(SkBits2Float(0x414d8b5f), SkBits2Float(0x424c2655), SkBits2Float(0x414d85dc), SkBits2Float(0x424c2828));  // 12.8465f, 51.0374f, 12.8452f, 51.0392f
+path.quadTo(SkBits2Float(0x412d952c), SkBits2Float(0x4256bc8e), SkBits2Float(0x40f225c0), SkBits2Float(0x4258923c));  // 10.8489f, 53.6841f, 7.56711f, 54.1428f
+path.quadTo(SkBits2Float(0x4089212c), SkBits2Float(0x425a67eb), SkBits2Float(0x3fd1f7e0), SkBits2Float(0x42526bbf));  // 4.2853f, 54.6015f, 1.64038f, 52.6052f
+path.quadTo(SkBits2Float(0xbf8094f0), SkBits2Float(0x424a6f94), SkBits2Float(0xbfbb4ab0), SkBits2Float(0x423d4f00));  // -1.00455f, 50.609f, -1.46322f, 47.3271f
+path.quadTo(SkBits2Float(0xbff60080), SkBits2Float(0x42302e6e), SkBits2Float(0x3d985000), SkBits2Float(0x42259a07));  // -1.92189f, 44.0453f, 0.0743713f, 41.4004f
+path.quadTo(SkBits2Float(0x3e6fb042), SkBits2Float(0x4224c15b), SkBits2Float(0x3ecdd2ea), SkBits2Float(0x4223f703));  // 0.234071f, 41.1888f, 0.402f, 40.9912f
+path.quadTo(SkBits2Float(0x3e4fb040), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));  // 0.202821f, 41, 0, 41
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));  // -3.31371f, 41, -5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));  // -8, 36.3137f, -8, 33
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));  // -8, 29.7635f, -5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));  // -13.7163f, 25.8817f, -19.799f, 19.799f
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));  // -28, 11.598f, -28, 0
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -28, -11.598f, -19.799f, -19.799f
+path.close();
+path.moveTo(SkBits2Float(0xc24a80c4), SkBits2Float(0xc16637fc));  // -50.6257f, -14.3887f
+path.quadTo(SkBits2Float(0xc23faf8f), SkBits2Float(0xc1826e03), SkBits2Float(0xc2329eca), SkBits2Float(0xc17bee18));  // -47.9214f, -16.3037f, -44.6551f, -15.7456f
+path.quadTo(SkBits2Float(0xc2258e06), SkBits2Float(0xc173002c), SkBits2Float(0xc21de504), SkBits2Float(0xc147bb58));  // -41.3887f, -15.1875f, -39.4736f, -12.4832f
+path.quadTo(SkBits2Float(0xc2163c02), SkBits2Float(0xc11c7684), SkBits2Float(0xc218777c), SkBits2Float(0xc0d066e4));  // -37.5586f, -9.77893f, -38.1167f, -6.51256f
+path.quadTo(SkBits2Float(0xc21ab2f8), SkBits2Float(0xc04fc188), SkBits2Float(0xc225842d), SkBits2Float(0xbfaa62c0));  // -38.6748f, -3.24619f, -41.3791f, -1.33114f
+path.quadTo(SkBits2Float(0xc2305562), SkBits2Float(0x3f157b20), SkBits2Float(0xc23d6626), SkBits2Float(0x3cd38800));  // -44.0834f, 0.58391f, -47.3498f, 0.0258217f
+path.quadTo(SkBits2Float(0xc24a76ea), SkBits2Float(0xbf084280), SkBits2Float(0xc2521fed), SkBits2Float(0xc04f23f0));  // -50.6161f, -0.532265f, -52.5312f, -3.23657f
+path.quadTo(SkBits2Float(0xc259c8f0), SkBits2Float(0xc0be1ba0), SkBits2Float(0xc2578d74), SkBits2Float(0xc11350e2));  // -54.4462f, -5.94087f, -53.8881f, -9.20725f
+path.quadTo(SkBits2Float(0xc25551f9), SkBits2Float(0xc14793f2), SkBits2Float(0xc24a80c4), SkBits2Float(0xc16637fc));  // -53.3301f, -12.4736f, -50.6257f, -14.3887f
+path.close();
+path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));  // -47.1494f, 4.35143f
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));  // -43.9486f, 3.49378f, -41.0788f, 5.15063f
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));  // -38.2091f, 6.80749f, -37.3514f, 10.0083f
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));  // -36.4938f, 13.2091f, -38.1506f, 16.0788f
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));  // -39.8075f, 18.9486f, -43.0083f, 19.8062f
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));  // -46.2091f, 20.6639f, -49.0788f, 19.007f
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));  // -51.9486f, 17.3502f, -52.8062f, 14.1494f
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));  // -53.6639f, 10.9486f, -52.007f, 8.07884f
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));  // -50.3502f, 5.20908f, -47.1494f, 4.35143f
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 27.3431f, 27.3431f
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));  // 29.6863f, 25, 33, 25
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));  // 36.3137f, 25, 38.6569f, 27.3431f
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));  // 41, 29.6863f, 41, 33
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));  // 41, 36.3137f, 38.6569f, 38.6569f
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));  // 36.3137f, 41, 33, 41
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));  // 29.6863f, 41, 27.3431f, 38.6569f
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));  // 25, 36.3137f, 25, 33
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 25, 29.6863f, 27.3431f, 27.3431f
+path.close();
+path.moveTo(SkBits2Float(0xc1beec1e), SkBits2Float(0x4207519a));  // -23.8653f, 33.8297f
+path.quadTo(SkBits2Float(0xc1a5a92e), SkBits2Float(0x42034ca6), SkBits2Float(0xc18e1d36), SkBits2Float(0x42096379));  // -20.7076f, 32.8249f, -17.7643f, 34.3471f
+path.quadTo(SkBits2Float(0xc16d2279), SkBits2Float(0x420f7a4d), SkBits2Float(0xc15d0ea8), SkBits2Float(0x421c1bc5));  // -14.8209f, 35.8694f, -13.8161f, 39.0271f
+path.quadTo(SkBits2Float(0xc14cfad8), SkBits2Float(0x4228bd3d), SkBits2Float(0xc1655627), SkBits2Float(0x42348339));  // -12.8112f, 42.1848f, -14.3335f, 45.1281f
+path.quadTo(SkBits2Float(0xc17db174), SkBits2Float(0x42404936), SkBits2Float(0xc1981baa), SkBits2Float(0x42444e2a));  // -15.8558f, 48.0715f, -19.0135f, 49.0763f
+path.quadTo(SkBits2Float(0xc1b15e9a), SkBits2Float(0x4248531e), SkBits2Float(0xc1c8ea94), SkBits2Float(0x42423c4a));  // -22.1712f, 50.0812f, -25.1145f, 48.5589f
+path.quadTo(SkBits2Float(0xc1e0768c), SkBits2Float(0x423c2577), SkBits2Float(0xc1e88074), SkBits2Float(0x422f83ff));  // -28.0579f, 47.0366f, -29.0627f, 43.8789f
+path.quadTo(SkBits2Float(0xc1f08a5c), SkBits2Float(0x4222e287), SkBits2Float(0xc1e45cb6), SkBits2Float(0x42171c8a));  // -30.0676f, 40.7212f, -28.5453f, 37.7779f
+path.quadTo(SkBits2Float(0xc1d82f0e), SkBits2Float(0x420b568e), SkBits2Float(0xc1beec1e), SkBits2Float(0x4207519a));  // -27.023f, 34.8345f, -23.8653f, 33.8297f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x41bbb37b), SkBits2Float(0x423c724c));  // 23.4626f, 47.1116f
+path.quadTo(SkBits2Float(0x41b0a015), SkBits2Float(0x42487d2f), SkBits2Float(0x4197c34c), SkBits2Float(0x424d16a6));  // 22.0782f, 50.1222f, 18.9704f, 51.2721f
+path.quadTo(SkBits2Float(0x417dcd04), SkBits2Float(0x4251b01e), SkBits2Float(0x414da178), SkBits2Float(0x424c266a));  // 15.8626f, 52.422f, 12.8519f, 51.0375f
+path.quadTo(SkBits2Float(0x411d75ea), SkBits2Float(0x42469cb8), SkBits2Float(0x410b100e), SkBits2Float(0x423a2e53));  // 9.84129f, 49.653f, 8.69142f, 46.5452f
+path.quadTo(SkBits2Float(0x40f15460), SkBits2Float(0x422dbfee), SkBits2Float(0x410ed0fc), SkBits2Float(0x4221b50b));  // 7.54155f, 43.4374f, 8.92602f, 40.4268f
+path.quadTo(SkBits2Float(0x4124f7c7), SkBits2Float(0x4215aa28), SkBits2Float(0x4156b15b), SkBits2Float(0x421110b0));  // 10.3105f, 37.4162f, 13.4183f, 36.2663f
+path.quadTo(SkBits2Float(0x41843577), SkBits2Float(0x420c7739), SkBits2Float(0x419c4b3e), SkBits2Float(0x421200ec));  // 16.5261f, 35.1164f, 19.5367f, 36.5009f
+path.quadTo(SkBits2Float(0x41b46104), SkBits2Float(0x42178a9f), SkBits2Float(0x41bd93f2), SkBits2Float(0x4223f904));  // 22.5474f, 37.8854f, 23.6972f, 40.9932f
+path.quadTo(SkBits2Float(0x41c6c6e0), SkBits2Float(0x42306768), SkBits2Float(0x41bbb37b), SkBits2Float(0x423c724c));  // 24.8471f, 44.101f, 23.4626f, 47.1116f
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_17370(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x41fb8980), SkBits2Float(0xc20d9cf4));  // 31.4421f, -35.4033f
+path.quadTo(SkBits2Float(0x42081e43), SkBits2Float(0xc215e4e6), SkBits2Float(0x42154ac6), SkBits2Float(0xc2146e6f));  // 34.0296f, -37.4735f, 37.323f, -37.1078f
+path.quadTo(SkBits2Float(0x4222774a), SkBits2Float(0xc212f7f8), SkBits2Float(0x422abf3a), SkBits2Float(0xc2089e76));  // 40.6165f, -36.7422f, 42.6867f, -34.1547f
+path.quadTo(SkBits2Float(0x4233072c), SkBits2Float(0xc1fc89e5), SkBits2Float(0x423190b5), SkBits2Float(0xc1e230df));  // 44.757f, -31.5673f, 44.3913f, -28.2739f
+path.quadTo(SkBits2Float(0x42301a3e), SkBits2Float(0xc1c7d7d8), SkBits2Float(0x4225c0bc), SkBits2Float(0xc1b747f6));  // 44.0256f, -24.9804f, 41.4382f, -22.9101f
+path.quadTo(SkBits2Float(0x421b6738), SkBits2Float(0xc1a6b815), SkBits2Float(0x420e3ab6), SkBits2Float(0xc1a9a502));  // 38.8508f, -20.8399f, 35.5573f, -21.2056f
+path.quadTo(SkBits2Float(0x42010e32), SkBits2Float(0xc1ac91ef), SkBits2Float(0x41f18c82), SkBits2Float(0xc1c144f4));  // 32.2639f, -21.5713f, 30.1936f, -24.1587f
+path.quadTo(SkBits2Float(0x41e0fca1), SkBits2Float(0xc1d5f7fa), SkBits2Float(0x41e3e98e), SkBits2Float(0xc1f05101));  // 28.1234f, -26.7461f, 28.489f, -30.0396f
+path.quadTo(SkBits2Float(0x41e6d67b), SkBits2Float(0xc2055504), SkBits2Float(0x41fb8980), SkBits2Float(0xc20d9cf4));  // 28.8547f, -33.333f, 31.4421f, -35.4033f
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -19.799f, -19.799f
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));  // -11.598f, -28, 0, -28
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));  // 11.598f, -28, 19.799f, -19.799f
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));  // 28, -11.598f, 28, 0
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));  // 28, 11.598f, 19.799f, 19.799f
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));  // 13.7163f, 25.8817f, 5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));  // 8, 29.7635f, 8, 33
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x420ff6ba), SkBits2Float(0x40c2ea2a), SkBits2Float(0x4218c3c6));  // 8, 35.9909f, 6.09108f, 38.1912f
+path.quadTo(SkBits2Float(0x41135b8a), SkBits2Float(0x42173bf5), SkBits2Float(0x413c53fb), SkBits2Float(0x421ec49c));  // 9.20985f, 37.8086f, 11.7705f, 39.692f
+path.quadTo(SkBits2Float(0x416709c9), SkBits2Float(0x42269f2a), SkBits2Float(0x416f0674), SkBits2Float(0x4233b9ad));  // 14.4399f, 41.6554f, 14.9391f, 44.9313f
+path.quadTo(SkBits2Float(0x41770320), SkBits2Float(0x4240d431), SkBits2Float(0x415798ee), SkBits2Float(0x424b81a4));  // 15.4383f, 48.2072f, 13.4748f, 50.8766f
+path.quadTo(SkBits2Float(0x41382eba), SkBits2Float(0x42562f18), SkBits2Float(0x4103c4ac), SkBits2Float(0x42582e42));  // 11.5114f, 53.546f, 8.23552f, 54.0452f
+path.quadTo(SkBits2Float(0x409eb53c), SkBits2Float(0x425a2d6e), SkBits2Float(0x40129340), SkBits2Float(0x425252e0));  // 4.95962f, 54.5444f, 2.29024f, 52.5809f
+path.quadTo(SkBits2Float(0x3ee54581), SkBits2Float(0x424ce72c), SkBits2Float(0xbeb8b807), SkBits2Float(0x4244fb36));  // 0.447796f, 51.2258f, -0.360779f, 49.2453f
+path.quadTo(SkBits2Float(0xbf99615c), SkBits2Float(0x424cdad2), SkBits2Float(0xc043dd58), SkBits2Float(0x42522abc));  // -1.19828f, 51.2137f, -3.06038f, 52.5417f
+path.quadTo(SkBits2Float(0xc0b84398), SkBits2Float(0x4259dd06), SkBits2Float(0xc1106c72), SkBits2Float(0x4257acc2));  // -5.75825f, 54.4658f, -9.02648f, 53.9187f
+path.quadTo(SkBits2Float(0xc144b71a), SkBits2Float(0x42557c80), SkBits2Float(0xc163803e), SkBits2Float(0x424ab1e2));  // -12.2947f, 53.3716f, -14.2188f, 50.6737f
+path.quadTo(SkBits2Float(0xc18124b1), SkBits2Float(0x423fe745), SkBits2Float(0xc1798856), SkBits2Float(0x4232d49b));  // -16.1429f, 47.9758f, -15.5958f, 44.7076f
+path.quadTo(SkBits2Float(0xc170c74c), SkBits2Float(0x4225c1f0), SkBits2Float(0xc1459cd6), SkBits2Float(0x421e0fa8));  // -15.0487f, 41.4394f, -12.3508f, 39.5153f
+path.quadTo(SkBits2Float(0xc11a7260), SkBits2Float(0x42165d5e), SkBits2Float(0xc0cc4f70), SkBits2Float(0x42188da2));  // -9.65292f, 37.5912f, -6.3847f, 38.1383f
+path.quadTo(SkBits2Float(0xc0c78c19), SkBits2Float(0x4218a726), SkBits2Float(0xc0c2e01e), SkBits2Float(0x4218c538));  // -6.23585f, 38.1632f, -6.08986f, 38.1926f
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x420ff7b5), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));  // -8, 35.9919f, -8, 33
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));  // -8, 29.7635f, -5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));  // -13.7163f, 25.8817f, -19.799f, 19.799f
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));  // -28, 11.598f, -28, 0
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -28, -11.598f, -19.799f, -19.799f
+path.close();
+path.moveTo(SkBits2Float(0xc23ab9e7), SkBits2Float(0xc1c274dd));  // -46.6815f, -24.3071f
+path.quadTo(SkBits2Float(0xc22e95fe), SkBits2Float(0xc1cd18ca), SkBits2Float(0xc2223d50), SkBits2Float(0xc1c373a5));  // -43.6465f, -25.6371f, -40.5599f, -24.4315f
+path.quadTo(SkBits2Float(0xc215e4a2), SkBits2Float(0xc1b9ce80), SkBits2Float(0xc21092ac), SkBits2Float(0xc1a186ac));  // -37.4733f, -23.2258f, -36.1432f, -20.1908f
+path.quadTo(SkBits2Float(0xc20b40b5), SkBits2Float(0xc1893ed9), SkBits2Float(0xc2101348), SkBits2Float(0xc1611afc));  // -34.8132f, -17.1557f, -36.0188f, -14.0691f
+path.quadTo(SkBits2Float(0xc214e5da), SkBits2Float(0xc12fb844), SkBits2Float(0xc22109c4), SkBits2Float(0xc11a706a));  // -37.2245f, -10.9825f, -40.2595f, -9.65244f
+path.quadTo(SkBits2Float(0xc22d2dae), SkBits2Float(0xc1052890), SkBits2Float(0xc239865c), SkBits2Float(0xc11872dd));  // -43.2946f, -8.3224f, -46.3812f, -9.52804f
+path.quadTo(SkBits2Float(0xc245df09), SkBits2Float(0xc12bbd26), SkBits2Float(0xc24b3100), SkBits2Float(0xc15c4ccc));  // -49.4678f, -10.7337f, -50.7979f, -13.7687f
+path.quadTo(SkBits2Float(0xc25082f6), SkBits2Float(0xc1866e3a), SkBits2Float(0xc24bb063), SkBits2Float(0xc19f1f96));  // -52.1279f, -16.8038f, -50.9223f, -19.8904f
+path.quadTo(SkBits2Float(0xc246ddd1), SkBits2Float(0xc1b7d0f0), SkBits2Float(0xc23ab9e7), SkBits2Float(0xc1c274dd));  // -49.7166f, -22.977f, -46.6815f, -24.3071f
+path.close();
+path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));  // -47.1494f, 4.35143f
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));  // -43.9486f, 3.49378f, -41.0788f, 5.15063f
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));  // -38.2091f, 6.80749f, -37.3514f, 10.0083f
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));  // -36.4938f, 13.2091f, -38.1506f, 16.0788f
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));  // -39.8075f, 18.9486f, -43.0083f, 19.8062f
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));  // -46.2091f, 20.6639f, -49.0788f, 19.007f
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));  // -51.9486f, 17.3502f, -52.8062f, 14.1494f
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));  // -53.6639f, 10.9486f, -52.007f, 8.07884f
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));  // -50.3502f, 5.20908f, -47.1494f, 4.35143f
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 27.3431f, 27.3431f
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));  // 29.6863f, 25, 33, 25
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));  // 36.3137f, 25, 38.6569f, 27.3431f
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));  // 41, 29.6863f, 41, 33
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));  // 41, 36.3137f, 38.6569f, 38.6569f
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));  // 36.3137f, 41, 33, 41
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));  // 29.6863f, 41, 27.3431f, 38.6569f
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));  // 25, 36.3137f, 25, 33
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 25, 29.6863f, 27.3431f, 27.3431f
+path.close();
+path.moveTo(SkBits2Float(0xc1d961c1), SkBits2Float(0x41f9e1da));  // -27.1727f, 31.2353f
+path.quadTo(SkBits2Float(0xc1bf6f78), SkBits2Float(0x41f47252), SkBits2Float(0xc1a93eb6), SkBits2Float(0x42017995));  // -23.9294f, 30.5558f, -21.1556f, 32.3687f
+path.quadTo(SkBits2Float(0xc1930df4), SkBits2Float(0x4208ba01), SkBits2Float(0xc18d9e6c), SkBits2Float(0x4215b325));  // -18.3818f, 34.1816f, -17.7024f, 37.4249f
+path.quadTo(SkBits2Float(0xc1882ee5), SkBits2Float(0x4222ac49), SkBits2Float(0xc196afbd), SkBits2Float(0x422dc4aa));  // -17.0229f, 40.6682f, -18.8358f, 43.4421f
+path.quadTo(SkBits2Float(0xc1a53094), SkBits2Float(0x4238dd0c), SkBits2Float(0xc1bf22dd), SkBits2Float(0x423b94cf));  // -20.6487f, 46.2159f, -23.892f, 46.8953f
+path.quadTo(SkBits2Float(0xc1d91525), SkBits2Float(0x423e4c93), SkBits2Float(0xc1ef45e7), SkBits2Float(0x42370c27));  // -27.1353f, 47.5748f, -29.9091f, 45.7619f
+path.quadTo(SkBits2Float(0xc202bb55), SkBits2Float(0x422fcbbc), SkBits2Float(0xc2057319), SkBits2Float(0x4222d298));  // -32.6829f, 43.949f, -33.3624f, 40.7057f
+path.quadTo(SkBits2Float(0xc2082adc), SkBits2Float(0x4215d973), SkBits2Float(0xc200ea70), SkBits2Float(0x420ac112));  // -34.0419f, 37.4624f, -32.2289f, 34.6885f
+path.quadTo(SkBits2Float(0xc1f35409), SkBits2Float(0x41ff5161), SkBits2Float(0xc1d961c1), SkBits2Float(0x41f9e1da));  // -30.416f, 31.9147f, -27.1727f, 31.2353f
+path.close();
+path.moveTo(SkBits2Float(0xbfccf162), SkBits2Float(0x42236913));  // -1.60112f, 40.8526f
+path.quadTo(SkBits2Float(0xbf54c171), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));  // -0.831077f, 41, 0, 41
+path.quadTo(SkBits2Float(0x3dcda9e6), SkBits2Float(0x42240000), SkBits2Float(0x3e4cbe2e), SkBits2Float(0x4223fdcc));  // 0.100422f, 41, 0.199944f, 40.9978f
+path.quadTo(SkBits2Float(0x3f12dfd9), SkBits2Float(0x4223f586), SkBits2Float(0x3f6f571b), SkBits2Float(0x4223ce2c));  // 0.573728f, 40.9898f, 0.934923f, 40.9513f
+path.quadTo(SkBits2Float(0x3f4168dc), SkBits2Float(0x4224a9bb), SkBits2Float(0x3f15fde0), SkBits2Float(0x422595d9));  // 0.755506f, 41.1658f, 0.585905f, 41.3963f
+path.quadTo(SkBits2Float(0x3d27275b), SkBits2Float(0x42288cb9), SkBits2Float(0xbea10331), SkBits2Float(0x422bb375));  // 0.040809f, 42.1374f, -0.314477f, 42.9253f
+path.quadTo(SkBits2Float(0xbf287eab), SkBits2Float(0x4228877a), SkBits2Float(0xbf989f50), SkBits2Float(0x42258882));  // -0.658183f, 42.1323f, -1.19236f, 41.3833f
+path.quadTo(SkBits2Float(0xbfb1e0f9), SkBits2Float(0x42246d34), SkBits2Float(0xbfccf162), SkBits2Float(0x42236913));  // -1.38968f, 41.1066f, -1.60112f, 40.8526f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x415798ee), SkBits2Float(0x424b81a4));  // 13.4748f, 50.8766f
+path.quadTo(SkBits2Float(0x41382eba), SkBits2Float(0x42562f18), SkBits2Float(0x4103c4ac), SkBits2Float(0x42582e42));  // 11.5114f, 53.546f, 8.23552f, 54.0452f
+path.quadTo(SkBits2Float(0x409eb53c), SkBits2Float(0x425a2d6e), SkBits2Float(0x40129340), SkBits2Float(0x425252e0));  // 4.95962f, 54.5444f, 2.29024f, 52.5809f
+path.quadTo(SkBits2Float(0xbec21f80), SkBits2Float(0x424a7854), SkBits2Float(0xbf60da80), SkBits2Float(0x423d5dd0));  // -0.379147f, 50.6175f, -0.878334f, 47.3416f
+path.quadTo(SkBits2Float(0xbfb052b0), SkBits2Float(0x4230434d), SkBits2Float(0x3f15fde0), SkBits2Float(0x422595d9));  // -1.37752f, 44.0657f, 0.585905f, 41.3963f
+path.quadTo(SkBits2Float(0x40232840), SkBits2Float(0x421ae866), SkBits2Float(0x40ba6840), SkBits2Float(0x4218e93b));  // 2.54933f, 38.727f, 5.82523f, 38.2278f
+path.quadTo(SkBits2Float(0x41119e2f), SkBits2Float(0x4216ea10), SkBits2Float(0x413c53fb), SkBits2Float(0x421ec49c));  // 9.10112f, 37.7286f, 11.7705f, 39.692f
+path.quadTo(SkBits2Float(0x416709c9), SkBits2Float(0x42269f2a), SkBits2Float(0x416f0674), SkBits2Float(0x4233b9ad));  // 14.4399f, 41.6554f, 14.9391f, 44.9313f
+path.quadTo(SkBits2Float(0x41770320), SkBits2Float(0x4240d431), SkBits2Float(0x415798ee), SkBits2Float(0x424b81a4));  // 15.4383f, 48.2072f, 13.4748f, 50.8766f
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+// SkDQuadIntersection.cpp:598: failed assertion "way_roughly_equal(fT[0][index], 1)"
+static void fuzz763_35322(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x41042400), SkBits2Float(0xc24fea42));  // 8.25879f, -51.9788f
+path.quadTo(SkBits2Float(0x413225f2), SkBits2Float(0xc25680b3), SkBits2Float(0x4165502a), SkBits2Float(0xc2530720));  // 11.1343f, -53.6257f, 14.3321f, -52.757f
+path.quadTo(SkBits2Float(0x418c3d32), SkBits2Float(0xc24f8d8e), SkBits2Float(0x41996a13), SkBits2Float(0xc2440d11));  // 17.5299f, -51.8882f, 19.1768f, -49.0128f
+path.quadTo(SkBits2Float(0x41a696f3), SkBits2Float(0xc2388c95), SkBits2Float(0x419fa3cc), SkBits2Float(0xc22bc206));  // 20.8237f, -46.1373f, 19.955f, -42.9395f
+path.quadTo(SkBits2Float(0x4198b0a8), SkBits2Float(0xc21ef778), SkBits2Float(0x4181afaf), SkBits2Float(0xc2186108));  // 19.0863f, -39.7417f, 16.2108f, -38.0948f
+path.quadTo(SkBits2Float(0x41555d6e), SkBits2Float(0xc211ca98), SkBits2Float(0x41223335), SkBits2Float(0xc215442b));  // 13.3353f, -36.4478f, 10.1375f, -37.3166f
+path.quadTo(SkBits2Float(0x40de11f8), SkBits2Float(0xc218bdbe), SkBits2Float(0x40a95e78), SkBits2Float(0xc2243e3a));  // 6.93969f, -38.1853f, 5.29278f, -41.0608f
+path.quadTo(SkBits2Float(0x406955e8), SkBits2Float(0xc22fbeb6), SkBits2Float(0x4090778c), SkBits2Float(0xc23c8944));  // 3.64587f, -43.9362f, 4.51459f, -47.134f
+path.quadTo(SkBits2Float(0x40ac4420), SkBits2Float(0xc24953d2), SkBits2Float(0x41042400), SkBits2Float(0xc24fea42));  // 5.38332f, -50.3319f, 8.25879f, -51.9788f
+path.close();
+path.moveTo(SkBits2Float(0x41cb7543), SkBits2Float(0xc2385013));  // 25.4323f, -46.0782f
+path.quadTo(SkBits2Float(0x41e591fd), SkBits2Float(0xc23a9972), SkBits2Float(0x41fb44a5), SkBits2Float(0xc232fbf5));  // 28.6963f, -46.6498f, 31.4085f, -44.7461f
+path.quadTo(SkBits2Float(0x42087ba8), SkBits2Float(0xc22b5e79), SkBits2Float(0x420ac507), SkBits2Float(0xc21e501c));  // 34.1208f, -42.8423f, 34.6924f, -39.5782f
+path.quadTo(SkBits2Float(0x420d0e67), SkBits2Float(0xc21141bf), SkBits2Float(0x420570ea), SkBits2Float(0xc206686a));  // 35.2641f, -36.3142f, 33.3603f, -33.602f
+path.quadTo(SkBits2Float(0x41fba6dc), SkBits2Float(0xc1f71e2c), SkBits2Float(0x41e18a22), SkBits2Float(0xc1f28b6c));  // 31.4565f, -30.8897f, 28.1924f, -30.3181f
+path.quadTo(SkBits2Float(0x41c76d67), SkBits2Float(0xc1edf8ad), SkBits2Float(0x41b1babe), SkBits2Float(0xc1fd33a6));  // 24.9284f, -29.7464f, 22.2162f, -31.6502f
+path.quadTo(SkBits2Float(0x419c0815), SkBits2Float(0xc2063750), SkBits2Float(0x41977556), SkBits2Float(0xc21345ad));  // 19.5039f, -33.554f, 18.9323f, -36.818f
+path.quadTo(SkBits2Float(0x4192e296), SkBits2Float(0xc220540a), SkBits2Float(0x41a21d8f), SkBits2Float(0xc22b2d5e));  // 18.3606f, -40.0821f, 20.2644f, -42.7943f
+path.quadTo(SkBits2Float(0x41b15888), SkBits2Float(0xc23606b3), SkBits2Float(0x41cb7543), SkBits2Float(0xc2385013));  // 22.1682f, -45.5065f, 25.4323f, -46.0782f
+path.close();
+path.moveTo(SkBits2Float(0x4206de71), SkBits2Float(0xc204f99f));  // 33.7172f, -33.2438f
+path.quadTo(SkBits2Float(0x4211be80), SkBits2Float(0xc20c8d7c), SkBits2Float(0x421ecad2), SkBits2Float(0xc20a388c));  // 36.436f, -35.1382f, 39.6981f, -34.5552f
+path.quadTo(SkBits2Float(0x422bd724), SkBits2Float(0xc207e39b), SkBits2Float(0x42336b00), SkBits2Float(0xc1fa0718));  // 42.9601f, -33.9723f, 44.8545f, -31.2535f
+path.quadTo(SkBits2Float(0x423afedd), SkBits2Float(0xc1e446f9), SkBits2Float(0x4238a9ec), SkBits2Float(0xc1ca2e57));  // 46.7489f, -28.5347f, 46.1659f, -25.2726f
+path.quadTo(SkBits2Float(0x423654fc), SkBits2Float(0xc1b015b3), SkBits2Float(0x422b74ed), SkBits2Float(0xc1a0edfa));  // 45.583f, -22.0106f, 42.8642f, -20.1162f
+path.quadTo(SkBits2Float(0x422094de), SkBits2Float(0xc191c640), SkBits2Float(0x4213888c), SkBits2Float(0xc1967021));  // 40.1454f, -18.2218f, 36.8833f, -18.8048f
+path.quadTo(SkBits2Float(0x42067c3a), SkBits2Float(0xc19b1a02), SkBits2Float(0x41fdd0bc), SkBits2Float(0xc1b0da20));  // 33.6213f, -19.3877f, 31.7269f, -22.1065f
+path.quadTo(SkBits2Float(0x41eea902), SkBits2Float(0xc1c69a3f), SkBits2Float(0x41f352e3), SkBits2Float(0xc1e0b2e3));  // 29.8325f, -24.8253f, 30.4155f, -28.0873f
+path.quadTo(SkBits2Float(0x41f7fcc4), SkBits2Float(0xc1facb85), SkBits2Float(0x4206de71), SkBits2Float(0xc204f99f));  // 30.9984f, -31.3494f, 33.7172f, -33.2438f
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -19.799f, -19.799f
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));  // -11.598f, -28, 0, -28
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));  // 11.598f, -28, 19.799f, -19.799f
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));  // 28, -11.598f, 28, 0
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));  // 28, 11.598f, 19.799f, 19.799f
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));  // 13.7163f, 25.8817f, 5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));  // 8, 29.7635f, 8, 33
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));  // 8, 36.3137f, 5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0x40b4fa00), SkBits2Float(0x421aa1fd), SkBits2Float(0x40b4ef0c), SkBits2Float(0x421aa35b));  // 5.65552f, 38.6582f, 5.65418f, 38.6595f
+path.quadTo(SkBits2Float(0x40f11f68), SkBits2Float(0x421c342c), SkBits2Float(0x4111ddac), SkBits2Float(0x42218978));  // 7.53508f, 39.0509f, 9.11662f, 40.3842f
+path.quadTo(SkBits2Float(0x413a6700), SkBits2Float(0x422a1497), SkBits2Float(0x413ee6c0), SkBits2Float(0x42374996));  // 11.6501f, 42.5201f, 11.9313f, 45.8219f
+path.quadTo(SkBits2Float(0x41436686), SkBits2Float(0x42447e96), SkBits2Float(0x41213a06), SkBits2Float(0x424ea0eb));  // 12.2125f, 49.1236f, 10.0767f, 51.6571f
+path.quadTo(SkBits2Float(0x40fe1b10), SkBits2Float(0x4258c340), SkBits2Float(0x40947314), SkBits2Float(0x4259e330));  // 7.9408f, 54.1907f, 4.63905f, 54.4719f
+path.quadTo(SkBits2Float(0x3fab2c60), SkBits2Float(0x425b0321), SkBits2Float(0xbf991e40), SkBits2Float(0x42527802));  // 1.33729f, 54.7531f, -1.19624f, 52.6172f
+path.quadTo(SkBits2Float(0xc06eb470), SkBits2Float(0x4249ece2), SkBits2Float(0xc08059b8), SkBits2Float(0x423cb7e2));  // -3.72976f, 50.4813f, -4.01095f, 47.1796f
+path.quadTo(SkBits2Float(0xc0895940), SkBits2Float(0x422f82e3), SkBits2Float(0xc00a0088), SkBits2Float(0x4225608e));  // -4.29214f, 43.8778f, -2.15628f, 41.3443f
+path.quadTo(SkBits2Float(0xbff725c6), SkBits2Float(0x42244eb9), SkBits2Float(0xbfd8a182), SkBits2Float(0x4223569a));  // -1.93084f, 41.0769f, -1.69243f, 40.8346f
+path.quadTo(SkBits2Float(0xc07bec59), SkBits2Float(0x42218277), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));  // -3.9363f, 40.3774f, -5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));  // -8, 36.3137f, -8, 33
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));  // -8, 29.7635f, -5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));  // -13.7163f, 25.8817f, -19.799f, 19.799f
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));  // -28, 11.598f, -28, 0
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -28, -11.598f, -19.799f, -19.799f
+path.close();
+path.moveTo(SkBits2Float(0xc233e0fb), SkBits2Float(0xc1dac1ac));  // -44.9697f, -27.3446f
+path.quadTo(SkBits2Float(0xc22769b6), SkBits2Float(0xc1e3c410), SkBits2Float(0xc21b69b6), SkBits2Float(0xc1d881ca));  // -41.8532f, -28.4707f, -38.8532f, -27.0634f
+path.quadTo(SkBits2Float(0xc20f69b6), SkBits2Float(0xc1cd3f84), SkBits2Float(0xc20ae884), SkBits2Float(0xc1b450fa));  // -35.8532f, -25.656f, -34.7271f, -22.5395f
+path.quadTo(SkBits2Float(0xc2066752), SkBits2Float(0xc19b6270), SkBits2Float(0xc20c0875), SkBits2Float(0xc1836270));  // -33.6009f, -19.4231f, -35.0083f, -16.4231f
+path.quadTo(SkBits2Float(0xc211a998), SkBits2Float(0xc156c4e1), SkBits2Float(0xc21e20dd), SkBits2Float(0xc144c018));  // -36.4156f, -13.4231f, -39.5321f, -12.2969f
+path.quadTo(SkBits2Float(0xc22a9822), SkBits2Float(0xc132bb50), SkBits2Float(0xc2369821), SkBits2Float(0xc1493fdc));  // -42.6486f, -11.1707f, -45.6486f, -12.5781f
+path.quadTo(SkBits2Float(0xc2429822), SkBits2Float(0xc15fc467), SkBits2Float(0xc2471954), SkBits2Float(0xc188d0be));  // -48.6486f, -13.9854f, -49.7747f, -17.1019f
+path.quadTo(SkBits2Float(0xc24b9a86), SkBits2Float(0xc1a1bf48), SkBits2Float(0xc245f962), SkBits2Float(0xc1b9bf47));  // -50.9009f, -20.2184f, -49.4935f, -23.2184f
+path.quadTo(SkBits2Float(0xc2405840), SkBits2Float(0xc1d1bf48), SkBits2Float(0xc233e0fb), SkBits2Float(0xc1dac1ac));  // -48.0862f, -26.2184f, -44.9697f, -27.3446f
+path.close();
+path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));  // -47.1494f, 4.35143f
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));  // -43.9486f, 3.49378f, -41.0788f, 5.15063f
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));  // -38.2091f, 6.80749f, -37.3514f, 10.0083f
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));  // -36.4938f, 13.2091f, -38.1506f, 16.0788f
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));  // -39.8075f, 18.9486f, -43.0083f, 19.8062f
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));  // -46.2091f, 20.6639f, -49.0788f, 19.007f
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));  // -51.9486f, 17.3502f, -52.8062f, 14.1494f
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));  // -53.6639f, 10.9486f, -52.007f, 8.07884f
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));  // -50.3502f, 5.20908f, -47.1494f, 4.35143f
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 27.3431f, 27.3431f
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));  // 29.6863f, 25, 33, 25
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));  // 36.3137f, 25, 38.6569f, 27.3431f
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));  // 41, 29.6863f, 41, 33
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));  // 41, 36.3137f, 38.6569f, 38.6569f
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));  // 36.3137f, 41, 33, 41
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));  // 29.6863f, 41, 27.3431f, 38.6569f
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));  // 25, 36.3137f, 25, 33
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 25, 29.6863f, 27.3431f, 27.3431f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x41213a06), SkBits2Float(0x424ea0eb));  // 10.0767f, 51.6571f
+path.quadTo(SkBits2Float(0x40fe1b10), SkBits2Float(0x4258c340), SkBits2Float(0x40947314), SkBits2Float(0x4259e330));  // 7.9408f, 54.1907f, 4.63905f, 54.4719f
+path.quadTo(SkBits2Float(0x3fab2c60), SkBits2Float(0x425b0321), SkBits2Float(0xbf991e40), SkBits2Float(0x42527802));  // 1.33729f, 54.7531f, -1.19624f, 52.6172f
+path.quadTo(SkBits2Float(0xc06eb470), SkBits2Float(0x4249ece2), SkBits2Float(0xc08059b8), SkBits2Float(0x423cb7e2));  // -3.72976f, 50.4813f, -4.01095f, 47.1796f
+path.quadTo(SkBits2Float(0xc0895940), SkBits2Float(0x422f82e3), SkBits2Float(0xc00a0088), SkBits2Float(0x4225608e));  // -4.29214f, 43.8778f, -2.15628f, 41.3443f
+path.quadTo(SkBits2Float(0xbca74400), SkBits2Float(0x421b3e39), SkBits2Float(0x40520168), SkBits2Float(0x421a1e48));  // -0.0204182f, 38.8108f, 3.28134f, 38.5296f
+path.quadTo(SkBits2Float(0x40d2a8b0), SkBits2Float(0x4218fe58), SkBits2Float(0x4111ddac), SkBits2Float(0x42218978));  // 6.58309f, 38.2484f, 9.11662f, 40.3842f
+path.quadTo(SkBits2Float(0x413a6700), SkBits2Float(0x422a1497), SkBits2Float(0x413ee6c0), SkBits2Float(0x42374996));  // 11.6501f, 42.5201f, 11.9313f, 45.8219f
+path.quadTo(SkBits2Float(0x41436686), SkBits2Float(0x42447e96), SkBits2Float(0x41213a06), SkBits2Float(0x424ea0eb));  // 12.2125f, 49.1236f, 10.0767f, 51.6571f
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+// SkPathOpsOp.cpp:52: failed assertion "angle != firstAngle || !loop"
+static void fuzz763_849020(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x411e4374), SkBits2Float(0xc24ec58f));  // 9.89147f, -51.6929f
+path.quadTo(SkBits2Float(0x414d13fa), SkBits2Float(0xc254fe70), SkBits2Float(0x417fc7a6), SkBits2Float(0xc2511e30));  // 12.8174f, -53.2485f, 15.9862f, -52.2795f
+path.quadTo(SkBits2Float(0x41993dab), SkBits2Float(0xc24d3df4), SkBits2Float(0x41a5af6e), SkBits2Float(0xc24189d2));  // 19.1551f, -51.3105f, 20.7107f, -48.3846f
+path.quadTo(SkBits2Float(0x41b22132), SkBits2Float(0xc235d5b1), SkBits2Float(0x41aa60b2), SkBits2Float(0xc22928c4));  // 22.2662f, -45.4587f, 21.2972f, -42.2898f
+path.quadTo(SkBits2Float(0x41a2a038), SkBits2Float(0xc21c7bda), SkBits2Float(0x418b37f4), SkBits2Float(0xc21642f8));  // 20.3282f, -39.1209f, 17.4023f, -37.5654f
+path.quadTo(SkBits2Float(0x41679f65), SkBits2Float(0xc2100a16), SkBits2Float(0x4134ebb5), SkBits2Float(0xc213ea55));  // 14.4764f, -36.0098f, 11.3075f, -36.9788f
+path.quadTo(SkBits2Float(0x41023808), SkBits2Float(0xc217ca94), SkBits2Float(0x40d2a902), SkBits2Float(0xc2237eb5));  // 8.13868f, -37.9478f, 6.58313f, -40.8737f
+path.quadTo(SkBits2Float(0x40a0e1f4), SkBits2Float(0xc22f32d6), SkBits2Float(0x40bfe3ec), SkBits2Float(0xc23bdfc1));  // 5.02758f, -43.7996f, 5.99657f, -46.9685f
+path.quadTo(SkBits2Float(0x40dee5e0), SkBits2Float(0xc2488cad), SkBits2Float(0x411e4374), SkBits2Float(0xc24ec58f));  // 6.96556f, -50.1374f, 9.89147f, -51.6929f
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -19.799f, -19.799f
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));  // -11.598f, -28, 0, -28
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));  // 11.598f, -28, 19.799f, -19.799f
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));  // 28, -11.598f, 28, 0
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));  // 28, 11.598f, 19.799f, 19.799f
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));  // 13.7163f, 25.8817f, 5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));  // 8, 29.7635f, 8, 33
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));  // 8, 36.3137f, 5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));  // 3.31371f, 41, 0, 41
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));  // -3.31371f, 41, -5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));  // -8, 36.3137f, -8, 33
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));  // -8, 29.7635f, -5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));  // -13.7163f, 25.8817f, -19.799f, 19.799f
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));  // -28, 11.598f, -28, 0
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -28, -11.598f, -19.799f, -19.799f
+path.close();
+path.moveTo(SkBits2Float(0xc23d1735), SkBits2Float(0x402cdce8));  // -47.2727f, 2.70098f
+path.quadTo(SkBits2Float(0xc2302ce7), SkBits2Float(0x3ffa54f0), SkBits2Float(0xc224ef5c), SkBits2Float(0x406d8f00));  // -44.0438f, 1.95572f, -41.2337f, 3.71185f
+path.quadTo(SkBits2Float(0xc219b1d2), SkBits2Float(0x40aef9c4), SkBits2Float(0xc216b6ab), SkBits2Float(0x410b261b));  // -38.4237f, 5.46799f, -37.6784f, 8.6968f
+path.quadTo(SkBits2Float(0xc2166795), SkBits2Float(0x411080a8), SkBits2Float(0xc2163402), SkBits2Float(0x4115c8a0));  // -37.6012f, 9.03141f, -37.5508f, 9.36148f
+path.quadTo(SkBits2Float(0xc215c2e1), SkBits2Float(0x411ad33f), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));  // -37.4403f, 9.67657f, -37.3514f, 10.0083f
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));  // -36.4938f, 13.2091f, -38.1506f, 16.0788f
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));  // -39.8075f, 18.9486f, -43.0083f, 19.8062f
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));  // -46.2091f, 20.6639f, -49.0788f, 19.007f
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));  // -51.9486f, 17.3502f, -52.8062f, 14.1494f
+path.quadTo(SkBits2Float(0xc253bae4), SkBits2Float(0x415ad9be), SkBits2Float(0xc2540461), SkBits2Float(0x41536cea));  // -52.9325f, 13.6782f, -53.0043f, 13.2141f
+path.quadTo(SkBits2Float(0xc254a293), SkBits2Float(0x414c5480), SkBits2Float(0xc25512ee), SkBits2Float(0x4144b962));  // -53.1588f, 12.7706f, -53.2685f, 12.2953f
+path.quadTo(SkBits2Float(0xc2580e14), SkBits2Float(0x41111028), SkBits2Float(0xc25107cc), SkBits2Float(0x40c833fc));  // -54.0137f, 9.06644f, -52.2576f, 6.25635f
+path.quadTo(SkBits2Float(0xc24a0183), SkBits2Float(0x405c8f50), SkBits2Float(0xc23d1735), SkBits2Float(0x402cdce8));  // -50.5015f, 3.44625f, -47.2727f, 2.70098f
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 27.3431f, 27.3431f
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));  // 29.6863f, 25, 33, 25
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));  // 36.3137f, 25, 38.6569f, 27.3431f
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));  // 41, 29.6863f, 41, 33
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));  // 41, 36.3137f, 38.6569f, 38.6569f
+path.quadTo(SkBits2Float(0x421a60e3), SkBits2Float(0x421ae059), SkBits2Float(0x421a205f), SkBits2Float(0x421b1e77));  // 38.5946f, 38.7191f, 38.5316f, 38.7798f
+path.lineTo(SkBits2Float(0x421a2049), SkBits2Float(0x421b1e8d));  // 38.5315f, 38.7798f
+path.quadTo(SkBits2Float(0x4218fba8), SkBits2Float(0x421c384c), SkBits2Float(0x4217c88e), SkBits2Float(0x421d2f21));  // 38.2458f, 39.055f, 37.9459f, 39.296f
+path.quadTo(SkBits2Float(0x42168e68), SkBits2Float(0x421e9b27), SkBits2Float(0x42152104), SkBits2Float(0x421fefda));  // 37.6391f, 39.6515f, 37.2822f, 39.9842f
+path.quadTo(SkBits2Float(0x42146c3b), SkBits2Float(0x4220986c), SkBits2Float(0x4213b29d), SkBits2Float(0x42213416));  // 37.1057f, 40.1488f, 36.9244f, 40.3009f
+path.quadTo(SkBits2Float(0x42130756), SkBits2Float(0x4221df6b), SkBits2Float(0x42124f9e), SkBits2Float(0x422284d0));  // 36.7572f, 40.4682f, 36.5778f, 40.6297f
+path.quadTo(SkBits2Float(0x420875c9), SkBits2Float(0x422b6326), SkBits2Float(0x41f6726d), SkBits2Float(0x422ab150));  // 34.115f, 42.8468f, 30.8059f, 42.6732f
+path.quadTo(SkBits2Float(0x41dbf947), SkBits2Float(0x4229ff79), SkBits2Float(0x41ca3c9c), SkBits2Float(0x422025a4));  // 27.4967f, 42.4995f, 25.2796f, 40.0368f
+path.quadTo(SkBits2Float(0x41b87ff1), SkBits2Float(0x42164bcf), SkBits2Float(0x41b9e39e), SkBits2Float(0x42090f3c));  // 23.0625f, 37.574f, 23.2361f, 34.2649f
+path.quadTo(SkBits2Float(0x41bb474a), SkBits2Float(0x41f7a551), SkBits2Float(0x41cefaf4), SkBits2Float(0x41e5e8a6));  // 23.4098f, 30.9557f, 25.8725f, 28.7386f
+path.quadTo(SkBits2Float(0x41cffe18), SkBits2Float(0x41e4ff5a), SkBits2Float(0x41d105e6), SkBits2Float(0x41e422e9));  // 25.9991f, 28.6247f, 26.1279f, 28.517f
+path.quadTo(SkBits2Float(0x41d1f880), SkBits2Float(0x41e32f61), SkBits2Float(0x41d2f77e), SkBits2Float(0x41e2419e));  // 26.2463f, 28.3981f, 26.3708f, 28.282f
+path.quadTo(SkBits2Float(0x41d4f9d5), SkBits2Float(0x41e06208), SkBits2Float(0x41d70fba), SkBits2Float(0x41deb6ad));  // 26.622f, 28.0479f, 26.8827f, 27.8392f
+path.quadTo(SkBits2Float(0x41d8cd7c), SkBits2Float(0x41dcb00a), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 27.1003f, 27.586f, 27.3431f, 27.3431f
+path.close();
+path.moveTo(SkBits2Float(0xc185f1f2), SkBits2Float(0x42177488));  // -16.7431f, 37.8638f
+path.quadTo(SkBits2Float(0xc15d848f), SkBits2Float(0x42110787), SkBits2Float(0xc12a8d7f), SkBits2Float(0x4214aee9));  // -13.8449f, 36.2574f, -10.6595f, 37.1708f
+path.quadTo(SkBits2Float(0xc0ef2cde), SkBits2Float(0x4218564b), SkBits2Float(0xc0bbc4da), SkBits2Float(0x4223ee21));  // -7.47423f, 38.0843f, -5.86778f, 40.9825f
+path.quadTo(SkBits2Float(0xc0885cd4), SkBits2Float(0x422f85f6), SkBits2Float(0xc0a597e8), SkBits2Float(0x423c43ba));  // -4.26133f, 43.8808f, -5.17479f, 47.0661f
+path.quadTo(SkBits2Float(0xc0c2d2f4), SkBits2Float(0x4249017e), SkBits2Float(0xc10fc8d0), SkBits2Float(0x424f6e7e));  // -6.08825f, 50.2515f, -8.98653f, 51.8579f
+path.quadTo(SkBits2Float(0xc13e2826), SkBits2Float(0x4255db7f), SkBits2Float(0xc1711f36), SkBits2Float(0x4252341c));  // -11.8848f, 53.4644f, -15.0701f, 52.5509f
+path.quadTo(SkBits2Float(0xc1920b22), SkBits2Float(0x424e8cbc), SkBits2Float(0xc19ee524), SkBits2Float(0x4242f4e6));  // -18.2554f, 51.6374f, -19.8619f, 48.7392f
+path.quadTo(SkBits2Float(0xc1abbf24), SkBits2Float(0x42375d10), SkBits2Float(0xc1a47060), SkBits2Float(0x422a9f4c));  // -21.4683f, 45.8409f, -20.5549f, 42.6556f
+path.quadTo(SkBits2Float(0xc19d219e), SkBits2Float(0x421de188), SkBits2Float(0xc185f1f2), SkBits2Float(0x42177488));  // -19.6414f, 39.4702f, -16.7431f, 37.8638f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x421f4961), SkBits2Float(0x4209a6a0));  // 39.8217f, 34.4127f
+path.quadTo(SkBits2Float(0x421ed2ca), SkBits2Float(0x4216e5cb), SkBits2Float(0x42152104), SkBits2Float(0x421fefda));  // 39.7058f, 37.7244f, 37.2822f, 39.9842f
+path.quadTo(SkBits2Float(0x420b6f41), SkBits2Float(0x4228f9ea), SkBits2Float(0x41fc602b), SkBits2Float(0x42288353));  // 34.8586f, 42.2441f, 31.547f, 42.1282f
+path.quadTo(SkBits2Float(0x41e1e1d7), SkBits2Float(0x42280cbd), SkBits2Float(0x41cfcdb9), SkBits2Float(0x421e5af7));  // 28.2353f, 42.0124f, 25.9755f, 39.5888f
+path.quadTo(SkBits2Float(0x41bdb999), SkBits2Float(0x4214a933), SkBits2Float(0x41bea6c6), SkBits2Float(0x42076a08));  // 23.7156f, 37.1652f, 23.8314f, 33.8535f
+path.quadTo(SkBits2Float(0x41bf93f3), SkBits2Float(0x41f455bd), SkBits2Float(0x41d2f77e), SkBits2Float(0x41e2419e));  // 23.9472f, 30.5419f, 26.3708f, 28.282f
+path.quadTo(SkBits2Float(0x41e65b07), SkBits2Float(0x41d02d7f), SkBits2Float(0x42006cae), SkBits2Float(0x41d11aac));  // 28.7944f, 26.0222f, 32.1061f, 26.138f
+path.quadTo(SkBits2Float(0x420dabd9), SkBits2Float(0x41d207d9), SkBits2Float(0x4216b5e7), SkBits2Float(0x41e56b63));  // 35.4178f, 26.2538f, 37.6776f, 28.6774f
+path.quadTo(SkBits2Float(0x421fbff7), SkBits2Float(0x41f8ceed), SkBits2Float(0x421f4961), SkBits2Float(0x4209a6a0));  // 39.9375f, 31.101f, 39.8217f, 34.4127f
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_1597464(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x4101092a), SkBits2Float(0xc2500973));  // 8.06474f, -52.0092f
+path.quadTo(SkBits2Float(0x412ef1d8), SkBits2Float(0xc256aade), SkBits2Float(0x41622940), SkBits2Float(0xc2533d84));  // 10.934f, -53.6669f, 14.1351f, -52.8101f
+path.quadTo(SkBits2Float(0x418ab055), SkBits2Float(0xc24fd02d), SkBits2Float(0x4197f32a), SkBits2Float(0xc2445602));  // 17.3361f, -51.9533f, 18.9937f, -49.084f
+path.quadTo(SkBits2Float(0x41a535ff), SkBits2Float(0xc238dbd6), SkBits2Float(0x419e5b4c), SkBits2Float(0xc22c0dfb));  // 20.6514f, -46.2147f, 19.7946f, -43.0137f
+path.quadTo(SkBits2Float(0x4197809e), SkBits2Float(0xc21f4021), SkBits2Float(0x41808c46), SkBits2Float(0xc2189eb6));  // 18.9378f, -39.8126f, 16.0685f, -38.155f
+path.quadTo(SkBits2Float(0x41532fdd), SkBits2Float(0xc211fd4c), SkBits2Float(0x411ff875), SkBits2Float(0xc2156aa5));  // 13.1992f, -36.4974f, 9.99816f, -37.3541f
+path.quadTo(SkBits2Float(0x40d98218), SkBits2Float(0xc218d7fd), SkBits2Float(0x40a476c4), SkBits2Float(0xc2245229));  // 6.79713f, -38.2109f, 5.1395f, -41.0802f
+path.quadTo(SkBits2Float(0x405ed6e0), SkBits2Float(0xc22fcc54), SkBits2Float(0x408ad638), SkBits2Float(0xc23c9a2e));  // 3.48186f, -43.9495f, 4.33865f, -47.1506f
+path.quadTo(SkBits2Float(0x40a640f4), SkBits2Float(0xc2496808), SkBits2Float(0x4101092a), SkBits2Float(0xc2500973));  // 5.19543f, -50.3516f, 8.06474f, -52.0092f
+path.close();
+path.moveTo(SkBits2Float(0xc21ab0b3), SkBits2Float(0xc21a9087));  // -38.6726f, -38.6411f
+path.quadTo(SkBits2Float(0xc211524c), SkBits2Float(0xc223f0e2), SkBits2Float(0xc204110f), SkBits2Float(0xc223f243));  // -36.3304f, -40.9852f, -33.0167f, -40.9866f
+path.quadTo(SkBits2Float(0xc1ed9fa5), SkBits2Float(0xc223f3a4), SkBits2Float(0xc1dadeef), SkBits2Float(0xc21a953c));  // -29.703f, -40.9879f, -27.3589f, -38.6457f
+path.quadTo(SkBits2Float(0xc1c81e38), SkBits2Float(0xc21136d5), SkBits2Float(0xc1c81b76), SkBits2Float(0xc203f598));  // -25.0148f, -36.3035f, -25.0134f, -32.9898f
+path.quadTo(SkBits2Float(0xc1c818b4), SkBits2Float(0xc1ed68b6), SkBits2Float(0xc1dad584), SkBits2Float(0xc1daa800));  // -25.0121f, -29.6761f, -27.3543f, -27.332f
+path.quadTo(SkBits2Float(0xc1ed9254), SkBits2Float(0xc1c7e74a), SkBits2Float(0xc2040a66), SkBits2Float(0xc1c7e488));  // -29.6964f, -24.9879f, -33.0102f, -24.9866f
+path.quadTo(SkBits2Float(0xc2114ba3), SkBits2Float(0xc1c7e1c6), SkBits2Float(0xc21aabfe), SkBits2Float(0xc1da9e95));  // -36.3239f, -24.9852f, -38.668f, -27.3274f
+path.quadTo(SkBits2Float(0xc2240c5a), SkBits2Float(0xc1ed5b65), SkBits2Float(0xc2240dbb), SkBits2Float(0xc203eeef));  // -41.0121f, -29.6696f, -41.0134f, -32.9833f
+path.quadTo(SkBits2Float(0xc2240f1c), SkBits2Float(0xc211302c), SkBits2Float(0xc21ab0b3), SkBits2Float(0xc21a9087));  // -41.0148f, -36.297f, -38.6726f, -38.6411f
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -19.799f, -19.799f
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));  // -11.598f, -28, 0, -28
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));  // 11.598f, -28, 19.799f, -19.799f
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));  // 28, -11.598f, 28, 0
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));  // 28, 11.598f, 19.799f, 19.799f
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));  // 13.7163f, 25.8817f, 5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));  // 8, 29.7635f, 8, 33
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));  // 8, 36.3137f, 5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));  // 3.31371f, 41, 0, 41
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));  // -3.31371f, 41, -5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));  // -8, 36.3137f, -8, 33
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));  // -8, 29.7635f, -5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));  // -13.7163f, 25.8817f, -19.799f, 19.799f
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));  // -28, 11.598f, -28, 0
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -28, -11.598f, -19.799f, -19.799f
+path.close();
+path.moveTo(SkBits2Float(0xc21564da), SkBits2Float(0x41204f08));  // -37.3485f, 10.0193f
+path.quadTo(SkBits2Float(0xc211fc8d), SkBits2Float(0x41536ca0), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));  // -36.4966f, 13.214f, -38.1506f, 16.0788f
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));  // -39.8075f, 18.9486f, -43.0083f, 19.8062f
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));  // -46.2091f, 20.6639f, -49.0788f, 19.007f
+path.quadTo(SkBits2Float(0xc24fc815), SkBits2Float(0x418ad0f8), SkBits2Float(0xc25337a2), SkBits2Float(0x4162812a));  // -51.9454f, 17.352f, -52.8043f, 14.1565f
+path.quadTo(SkBits2Float(0xc25336a6), SkBits2Float(0x41628fdb), SkBits2Float(0xc25335aa), SkBits2Float(0x41629e8a));  // -52.8034f, 14.1601f, -52.8024f, 14.1637f
+path.quadTo(SkBits2Float(0xc24fc68b), SkBits2Float(0x418aea08), SkBits2Float(0xc2444b74), SkBits2Float(0x419829ad));  // -51.9439f, 17.3643f, -49.0737f, 19.0203f
+path.quadTo(SkBits2Float(0xc238d05c), SkBits2Float(0x41a56952), SkBits2Float(0xc22c02fc), SkBits2Float(0x419e8b10));  // -46.2035f, 20.6764f, -43.0029f, 19.8179f
+path.quadTo(SkBits2Float(0xc21f359c), SkBits2Float(0x4197acd2), SkBits2Float(0xc21895c9), SkBits2Float(0x4180b6a4));  // -39.8024f, 18.9594f, -38.1463f, 16.0892f
+path.quadTo(SkBits2Float(0xc211f634), SkBits2Float(0x41538295), SkBits2Float(0xc21564da), SkBits2Float(0x41204f08));  // -36.4904f, 13.2194f, -37.3485f, 10.0193f
+path.close();
+path.moveTo(SkBits2Float(0x41dacdf1), SkBits2Float(0x41daaf93));  // 27.3506f, 27.3357f
+path.quadTo(SkBits2Float(0x41ed8b66), SkBits2Float(0x41c7ef83), SkBits2Float(0x420406f0), SkBits2Float(0x41c7edac));  // 29.6931f, 24.9919f, 33.0068f, 24.9911f
+path.quadTo(SkBits2Float(0x4211482d), SkBits2Float(0x41c7ebd5), SkBits2Float(0x421aa834), SkBits2Float(0x41daa94b));  // 36.3205f, 24.9902f, 38.6643f, 27.3327f
+path.quadTo(SkBits2Float(0x4224083d), SkBits2Float(0x41ed66c1), SkBits2Float(0x42240928), SkBits2Float(0x4203f49d));  // 41.008f, 29.6752f, 41.0089f, 32.9889f
+path.quadTo(SkBits2Float(0x42240a14), SkBits2Float(0x421135da), SkBits2Float(0x421aab58), SkBits2Float(0x421a95e2));  // 41.0098f, 36.3026f, 38.6673f, 38.6464f
+path.quadTo(SkBits2Float(0x421aa5fc), SkBits2Float(0x421a9b42), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));  // 38.6621f, 38.6516f, 38.6569f, 38.6569f
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));  // 36.3137f, 41, 33, 41
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));  // 29.6863f, 41, 27.3431f, 38.6569f
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));  // 25, 36.3137f, 25, 33
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 25, 29.6863f, 27.3431f, 27.3431f
+path.quadTo(SkBits2Float(0x41dac1e5), SkBits2Float(0x41dabba1), SkBits2Float(0x41dac507), SkBits2Float(0x41dab880));  // 27.3447f, 27.3416f, 27.3462f, 27.3401f
+path.lineTo(SkBits2Float(0x41dac551), SkBits2Float(0x41dab836));  // 27.3463f, 27.3399f
+path.quadTo(SkBits2Float(0x41dac9a0), SkBits2Float(0x41dab3e4), SkBits2Float(0x41dacdf1), SkBits2Float(0x41daaf93));  // 27.3484f, 27.3378f, 27.3506f, 27.3357f
+path.close();
+path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));  // -47.1494f, 4.35143f
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));  // -43.9486f, 3.49378f, -41.0788f, 5.15063f
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));  // -38.2091f, 6.80749f, -37.3514f, 10.0083f
+path.quadTo(SkBits2Float(0xc215672b), SkBits2Float(0x41202c49), SkBits2Float(0xc215667a), SkBits2Float(0x412036a3));  // -37.3507f, 10.0108f, -37.3501f, 10.0133f
+path.lineTo(SkBits2Float(0xc2156516), SkBits2Float(0x41204b6b));  // -37.3487f, 10.0184f
+path.lineTo(SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));  // -52.8062f, 14.1494f
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));  // -53.6639f, 10.9486f, -52.007f, 8.07884f
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));  // -50.3502f, 5.20908f, -47.1494f, 4.35143f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xc2444b74), SkBits2Float(0x419829ad));  // -49.0737f, 19.0203f
+path.quadTo(SkBits2Float(0xc24fc68b), SkBits2Float(0x418aea08), SkBits2Float(0xc25335aa), SkBits2Float(0x41629e8a));  // -51.9439f, 17.3643f, -52.8024f, 14.1637f
+path.quadTo(SkBits2Float(0xc256a4ca), SkBits2Float(0x412f6908), SkBits2Float(0xc25004f8), SkBits2Float(0x41017cac));  // -53.6609f, 10.9631f, -52.0049f, 8.09294f
+path.quadTo(SkBits2Float(0xc2496525), SkBits2Float(0x40a7209c), SkBits2Float(0xc23c97c4), SkBits2Float(0x408ba7a8));  // -50.3488f, 5.22273f, -47.1482f, 4.36422f
+path.quadTo(SkBits2Float(0xc22fca64), SkBits2Float(0x40605d58), SkBits2Float(0xc2244f4d), SkBits2Float(0x40a52d40));  // -43.9476f, 3.5057f, -41.0774f, 5.16177f
+path.quadTo(SkBits2Float(0xc218d435), SkBits2Float(0x40da2bd2), SkBits2Float(0xc2156516), SkBits2Float(0x41204b6b));  // -38.2072f, 6.81785f, -37.3487f, 10.0184f
+path.quadTo(SkBits2Float(0xc211f5f7), SkBits2Float(0x415380eb), SkBits2Float(0xc21895c9), SkBits2Float(0x4180b6a4));  // -36.4902f, 13.219f, -38.1463f, 16.0892f
+path.quadTo(SkBits2Float(0xc21f359c), SkBits2Float(0x4197acd2), SkBits2Float(0xc22c02fc), SkBits2Float(0x419e8b10));  // -39.8024f, 18.9594f, -43.0029f, 19.8179f
+path.quadTo(SkBits2Float(0xc238d05c), SkBits2Float(0x41a56952), SkBits2Float(0xc2444b74), SkBits2Float(0x419829ad));  // -46.2035f, 20.6764f, -49.0737f, 19.0203f
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_34974(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+#if 00
+path.moveTo(SkBits2Float(0x41015326), SkBits2Float(0xc2500694));
+path.quadTo(SkBits2Float(0x412f3e30), SkBits2Float(0xc256a6fa), SkBits2Float(0x41627462), SkBits2Float(0xc253387e));
+path.quadTo(SkBits2Float(0x418ad549), SkBits2Float(0xc24fca02), SkBits2Float(0x41981613), SkBits2Float(0xc2444f40));
+path.quadTo(SkBits2Float(0x41a556de), SkBits2Float(0xc238d47d), SkBits2Float(0x419e79e6), SkBits2Float(0xc22c06f0));
+path.quadTo(SkBits2Float(0x41979cee), SkBits2Float(0xc21f3964), SkBits2Float(0x4180a76a), SkBits2Float(0xc21898ff));
+path.quadTo(SkBits2Float(0x415363c9), SkBits2Float(0xc211f89a), SkBits2Float(0x41202d96), SkBits2Float(0xc2156716));
+path.quadTo(SkBits2Float(0x40d9eeca), SkBits2Float(0xc218d592), SkBits2Float(0x40a4eba0), SkBits2Float(0xc2245054));
+path.quadTo(SkBits2Float(0x405fd0f0), SkBits2Float(0xc22fcb17), SkBits2Float(0x408b5c58), SkBits2Float(0xc23c98a3));
+path.quadTo(SkBits2Float(0x40a6d038), SkBits2Float(0xc249662f), SkBits2Float(0x41015326), SkBits2Float(0xc2500694));
+path.close();
+#endif
+#if 000
+path.moveTo(SkBits2Float(0xc21a9c18), SkBits2Float(0xc21aa524));
+path.quadTo(SkBits2Float(0xc2113c71), SkBits2Float(0xc2240440), SkBits2Float(0xc203fb34), SkBits2Float(0xc22403dc));
+path.quadTo(SkBits2Float(0xc1ed73ee), SkBits2Float(0xc2240379), SkBits2Float(0xc1dab5b7), SkBits2Float(0xc21aa3d1));
+path.quadTo(SkBits2Float(0xc1c7f781), SkBits2Float(0xc211442a), SkBits2Float(0xc1c7f847), SkBits2Float(0xc20402ed));
+path.quadTo(SkBits2Float(0xc1c7f90e), SkBits2Float(0xc1ed835f), SkBits2Float(0xc1dab85d), SkBits2Float(0xc1dac529));
+path.quadTo(SkBits2Float(0xc1ed77ad), SkBits2Float(0xc1c806f2), SkBits2Float(0xc203fd13), SkBits2Float(0xc1c807b9));
+path.quadTo(SkBits2Float(0xc2113e50), SkBits2Float(0xc1c8087f), SkBits2Float(0xc21a9d6b), SkBits2Float(0xc1dac7cf));
+path.quadTo(SkBits2Float(0xc223fc87), SkBits2Float(0xc1ed871e), SkBits2Float(0xc223fc24), SkBits2Float(0xc20404cc));
+path.quadTo(SkBits2Float(0xc223fbc0), SkBits2Float(0xc2114609), SkBits2Float(0xc21a9c18), SkBits2Float(0xc21aa524));
+path.close();
+#endif
+#if 00
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+#endif
+#if 01
+path.moveTo(SkBits2Float(0xc2533a24), SkBits2Float(0x41625bba));
+path.lineTo(SkBits2Float(0xc2533ab2), SkBits2Float(0x4162536e));
+path.lineTo(SkBits2Float(0xc2533af7), SkBits2Float(0x41624f68));
+path.quadTo(SkBits2Float(0xc2533a8e), SkBits2Float(0x41625591), SkBits2Float(0xc2533a24), SkBits2Float(0x41625bba));
+path.close();
+#endif
+#if 0
+path.moveTo(SkBits2Float(0x41dac664), SkBits2Float(0x41dab723));
+path.quadTo(SkBits2Float(0x41ed82ea), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.lineTo(SkBits2Float(0x421a9d9a), SkBits2Float(0x421aa3a2));
+path.quadTo(SkBits2Float(0x42113e0a), SkBits2Float(0x422402d5), SkBits2Float(0x4203fccd), SkBits2Float(0x42240293));
+path.quadTo(SkBits2Float(0x41ed7721), SkBits2Float(0x42240251), SkBits2Float(0x41dab8bb), SkBits2Float(0x421aa2c0));
+path.quadTo(SkBits2Float(0x41c7fa56), SkBits2Float(0x42114330), SkBits2Float(0x41c7fada), SkBits2Float(0x420401f3));
+path.quadTo(SkBits2Float(0x41c7fb5f), SkBits2Float(0x41ed9352), SkBits2Float(0x41daa13c), SkBits2Float(0x41dadc57));
+path.quadTo(SkBits2Float(0x41daa91d), SkBits2Float(0x41dad46f), SkBits2Float(0x41dab101), SkBits2Float(0x41dacc89));
+path.quadTo(SkBits2Float(0x41dab5bf), SkBits2Float(0x41dac7c8), SkBits2Float(0x41daba7f), SkBits2Float(0x41dac307));
+path.lineTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41dac293), SkBits2Float(0x41dabaf3), SkBits2Float(0x41dac664), SkBits2Float(0x41dab723));
+path.close();
+#endif
+#if 00001
+path.moveTo(SkBits2Float(0xc23c9951), SkBits2Float(0x408b2180));
+path.quadTo(SkBits2Float(0xc22fcba2), SkBits2Float(0x405f6340), SkBits2Float(0xc2245122), SkBits2Float(0x40a4b85c));
+path.quadTo(SkBits2Float(0xc218dd36), SkBits2Float(0x40d9a0b8), SkBits2Float(0xc2156c96), SkBits2Float(0x411fdb9a));
+path.lineTo(SkBits2Float(0xc2156b9c), SkBits2Float(0x411fea15));
+path.quadTo(SkBits2Float(0xc2156a20), SkBits2Float(0x4120002c), SkBits2Float(0xc21568a5), SkBits2Float(0x41201647));
+path.lineTo(SkBits2Float(0xc21568a3), SkBits2Float(0x41201660));
+path.lineTo(SkBits2Float(0xc2156841), SkBits2Float(0x41201c29));
+path.quadTo(SkBits2Float(0xc215680f), SkBits2Float(0x41201f0a), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
+path.quadTo(SkBits2Float(0xc21562d2), SkBits2Float(0x41206d52), SkBits2Float(0xc2155ca3), SkBits2Float(0x4120cb63));
+path.quadTo(SkBits2Float(0xc212057d), SkBits2Float(0x4153a15f), SkBits2Float(0xc2189adf), SkBits2Float(0x41809e82));
+path.quadTo(SkBits2Float(0xc21f3b9a), SkBits2Float(0x419793a4), SkBits2Float(0xc22c0940), SkBits2Float(0x419e6fdc));
+path.quadTo(SkBits2Float(0xc238d6e6), SkBits2Float(0x41a54c16), SkBits2Float(0xc2445177), SkBits2Float(0x41980aa0));
+path.quadTo(SkBits2Float(0xc24fcb1e), SkBits2Float(0x418aca39), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));
+path.lineTo(SkBits2Float(0xc2533b22), SkBits2Float(0x41624cea));
+path.quadTo(SkBits2Float(0xc256a842), SkBits2Float(0x412f19c8), SkBits2Float(0xc25007d7), SkBits2Float(0x410132b2));
+path.quadTo(SkBits2Float(0xc24966ff), SkBits2Float(0x40a69160), SkBits2Float(0xc23c9951), SkBits2Float(0x408b2180));
+path.close();
+#endif
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+#if 01
+path.moveTo(SkBits2Float(0xc2445236), SkBits2Float(0x419806c2));
+path.quadTo(SkBits2Float(0xc24fccb6), SkBits2Float(0x418ac513), SkBits2Float(0xc2533ab2), SkBits2Float(0x4162536e));
+path.quadTo(SkBits2Float(0xc256a8ae), SkBits2Float(0x412f1cb2), SkBits2Float(0xc25007d7), SkBits2Float(0x410132b2));
+path.quadTo(SkBits2Float(0xc24966ff), SkBits2Float(0x40a69160), SkBits2Float(0xc23c9951), SkBits2Float(0x408b2180));
+path.quadTo(SkBits2Float(0xc22fcba2), SkBits2Float(0x405f6340), SkBits2Float(0xc2245122), SkBits2Float(0x40a4b85c));
+path.quadTo(SkBits2Float(0xc218d6a2), SkBits2Float(0x40d9bf1c), SkBits2Float(0xc21568a5), SkBits2Float(0x41201647));
+path.quadTo(SkBits2Float(0xc211faaa), SkBits2Float(0x41534d02), SkBits2Float(0xc2189b82), SkBits2Float(0x41809b82));
+path.quadTo(SkBits2Float(0xc21f3c59), SkBits2Float(0x41979082), SkBits2Float(0xc22c0a07), SkBits2Float(0x419e6c7a));
+path.quadTo(SkBits2Float(0xc238d7b6), SkBits2Float(0x41a54872), SkBits2Float(0xc2445236), SkBits2Float(0x419806c2));
+path.close();
+#endif
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_2211264(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x41017a68), SkBits2Float(0xc250050e));
+path.quadTo(SkBits2Float(0x412f66b2), SkBits2Float(0xc256a4e9), SkBits2Float(0x41629c3c), SkBits2Float(0xc25335d1));
+path.quadTo(SkBits2Float(0x418ae8e6), SkBits2Float(0xc24fc6bc), SkBits2Float(0x4198289b), SkBits2Float(0xc2444ba9));
+path.quadTo(SkBits2Float(0x41a56850), SkBits2Float(0xc238d096), SkBits2Float(0x419e8a20), SkBits2Float(0xc22c0333));
+path.quadTo(SkBits2Float(0x4197abf4), SkBits2Float(0xc21f35d0), SkBits2Float(0x4180b5d0), SkBits2Float(0xc21895f6));
+path.quadTo(SkBits2Float(0x41537f55), SkBits2Float(0xc211f61c), SkBits2Float(0x412049c9), SkBits2Float(0xc2156532));
+path.quadTo(SkBits2Float(0x40da287c), SkBits2Float(0xc218d449), SkBits2Float(0x40a529a8), SkBits2Float(0xc2244f5b));
+path.quadTo(SkBits2Float(0x406055a8), SkBits2Float(0xc22fca6e), SkBits2Float(0x408ba388), SkBits2Float(0xc23c97d0));
+path.quadTo(SkBits2Float(0x40a71c3c), SkBits2Float(0xc2496534), SkBits2Float(0x41017a68), SkBits2Float(0xc250050e));
+path.close();
+path.moveTo(SkBits2Float(0xc21a9126), SkBits2Float(0xc21ab014));
+path.quadTo(SkBits2Float(0xc21130d5), SkBits2Float(0xc2240e86), SkBits2Float(0xc203ef98), SkBits2Float(0xc2240d33));
+path.quadTo(SkBits2Float(0xc1ed5cb7), SkBits2Float(0xc2240bdf), SkBits2Float(0xc1da9fd4), SkBits2Float(0xc21aab8d));
+path.quadTo(SkBits2Float(0xc1c7e2f1), SkBits2Float(0xc2114b3c), SkBits2Float(0xc1c7e598), SkBits2Float(0xc20409ff));
+path.quadTo(SkBits2Float(0xc1c7e83e), SkBits2Float(0xc1ed9186), SkBits2Float(0xc1daa8e1), SkBits2Float(0xc1dad4a3));
+path.quadTo(SkBits2Float(0xc1ed6984), SkBits2Float(0xc1c817c0), SkBits2Float(0xc203f5ff), SkBits2Float(0xc1c81a66));
+path.quadTo(SkBits2Float(0xc211373c), SkBits2Float(0xc1c81d0d), SkBits2Float(0xc21a95ad), SkBits2Float(0xc1daddb0));
+path.quadTo(SkBits2Float(0xc223f41f), SkBits2Float(0xc1ed9e53), SkBits2Float(0xc223f2cb), SkBits2Float(0xc2041066));
+path.quadTo(SkBits2Float(0xc223f178), SkBits2Float(0xc21151a3), SkBits2Float(0xc21a9126), SkBits2Float(0xc21ab014));
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x421a9e0c), SkBits2Float(0x421aa331), SkBits2Float(0x421a9b79), SkBits2Float(0x421aa5c3));
+path.quadTo(SkBits2Float(0x421a98e4), SkBits2Float(0x421aa858), SkBits2Float(0x421a964e), SkBits2Float(0x421aaaec));
+path.lineTo(SkBits2Float(0x421a943a), SkBits2Float(0x421aad00));
+path.quadTo(SkBits2Float(0x421134d5), SkBits2Float(0x422409ae), SkBits2Float(0x4203f510), SkBits2Float(0x422408cc));
+path.quadTo(SkBits2Float(0x41ed67a7), SkBits2Float(0x422407e9), SkBits2Float(0x41daaa24), SkBits2Float(0x421aa7e8));
+path.quadTo(SkBits2Float(0x41c7eca1), SkBits2Float(0x421147e7), SkBits2Float(0x41c7ee66), SkBits2Float(0x420406aa));
+path.quadTo(SkBits2Float(0x41c7f02a), SkBits2Float(0x41ed8ada), SkBits2Float(0x41dab02f), SkBits2Float(0x41dacd55));
+path.lineTo(SkBits2Float(0x41dab02d), SkBits2Float(0x41dacd57));
+path.quadTo(SkBits2Float(0x41dab3d4), SkBits2Float(0x41dac9b0), SkBits2Float(0x41dab77c), SkBits2Float(0x41dac60a));
+path.quadTo(SkBits2Float(0x41dab83b), SkBits2Float(0x41dac54b), SkBits2Float(0x41dab8fa), SkBits2Float(0x41dac48d));
+path.quadTo(SkBits2Float(0x41dabbde), SkBits2Float(0x41dac1a8), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xc24455cc), SkBits2Float(0x4197f43e));
+path.quadTo(SkBits2Float(0xc24fcffc), SkBits2Float(0x418ab179), SkBits2Float(0xc2533d5e), SkBits2Float(0x41622b92));
+path.quadTo(SkBits2Float(0xc256aac0), SkBits2Float(0x412ef432), SkBits2Float(0xc250095d), SkBits2Float(0x41010b70));
+path.quadTo(SkBits2Float(0xc24967fb), SkBits2Float(0x40a64560), SkBits2Float(0xc23c9a22), SkBits2Float(0x408ada54));
+path.quadTo(SkBits2Float(0xc22fcc4a), SkBits2Float(0x405ede90), SkBits2Float(0xc224521a), SkBits2Float(0x40a47a5c));
+path.quadTo(SkBits2Float(0xc218d7ea), SkBits2Float(0x40d9856e), SkBits2Float(0xc2156a88), SkBits2Float(0x411ffa16));
+path.quadTo(SkBits2Float(0xc211fd27), SkBits2Float(0x41533178), SkBits2Float(0xc2189e8a), SkBits2Float(0x41808d1c));
+path.quadTo(SkBits2Float(0xc21f3fec), SkBits2Float(0x4197817d), SkBits2Float(0xc22c0dc4), SkBits2Float(0x419e5c3f));
+path.quadTo(SkBits2Float(0xc238db9c), SkBits2Float(0x41a53702), SkBits2Float(0xc24455cc), SkBits2Float(0x4197f43e));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_4628016(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x41029678), SkBits2Float(0xc24ff9f4));  // 8.16174f, -51.9941f
+path.quadTo(SkBits2Float(0x41308bcc), SkBits2Float(0xc25695e3), SkBits2Float(0x4163bca4), SkBits2Float(0xc253226d));  // 11.0341f, -53.6464f, 14.2336f, -52.7836f
+path.quadTo(SkBits2Float(0x4164114c), SkBits2Float(0xc2531cb8), SkBits2Float(0x41646770), SkBits2Float(0xc25316ce));  // 14.2542f, -52.778f, 14.2753f, -52.7723f
+path.quadTo(SkBits2Float(0x4164bf2b), SkBits2Float(0xc25310f7), SkBits2Float(0x4165153a), SkBits2Float(0xc2530b20));  // 14.2967f, -52.7666f, 14.3177f, -52.7609f
+path.quadTo(SkBits2Float(0x418c2035), SkBits2Float(0xc24f9272), SkBits2Float(0x41994eb0), SkBits2Float(0xc244126c));  // 17.5157f, -51.893f, 19.1634f, -49.018f
+path.quadTo(SkBits2Float(0x41a67d2c), SkBits2Float(0xc2389265), SkBits2Float(0x419f8bd0), SkBits2Float(0xc22bc798));  // 20.8111f, -46.143f, 19.9433f, -42.9449f
+path.quadTo(SkBits2Float(0x41989a74), SkBits2Float(0xc21efccc), SkBits2Float(0x41819a68), SkBits2Float(0xc218658e));  // 19.0754f, -39.7469f, 16.2004f, -38.0992f
+path.quadTo(SkBits2Float(0x415534b5), SkBits2Float(0xc211ce50), SkBits2Float(0x41220985), SkBits2Float(0xc21546ff));  // 13.3254f, -36.4515f, 10.1273f, -37.3193f
+path.quadTo(SkBits2Float(0x4121cc4c), SkBits2Float(0xc2154b26), SkBits2Float(0x41218f63), SkBits2Float(0xc2154f56));  // 10.1124f, -37.3234f, 10.0975f, -37.3275f
+path.quadTo(SkBits2Float(0x412152b0), SkBits2Float(0xc2155360), SkBits2Float(0x412115c8), SkBits2Float(0xc215577b));  // 10.0827f, -37.3314f, 10.0678f, -37.3354f
+path.quadTo(SkBits2Float(0x40dbc9e2), SkBits2Float(0xc218caf0), SkBits2Float(0x40a6ea6c), SkBits2Float(0xc2244845));  // 6.86839f, -38.1982f, 5.21612f, -41.0706f
+path.quadTo(SkBits2Float(0x406415f0), SkBits2Float(0xc22fc59a), SkBits2Float(0x408da6a4), SkBits2Float(0xc23c91d0));  // 3.56384f, -43.943f, 4.42659f, -47.1424f
+path.quadTo(SkBits2Float(0x40a94248), SkBits2Float(0xc2495e06), SkBits2Float(0x41029678), SkBits2Float(0xc24ff9f4));  // 5.28934f, -50.3418f, 8.16174f, -51.9941f
+path.close();
+path.moveTo(SkBits2Float(0xc219e2da), SkBits2Float(0xc21b5d7a));  // -38.4715f, -38.8413f
+path.quadTo(SkBits2Float(0xc2107806), SkBits2Float(0xc224b15c), SkBits2Float(0xc20336d3), SkBits2Float(0xc224a121));  // -36.1172f, -41.1732f, -32.8035f, -41.1574f
+path.quadTo(SkBits2Float(0xc1ebeb41), SkBits2Float(0xc22490e7), SkBits2Float(0xc1d9437f), SkBits2Float(0xc21b2612));  // -29.4899f, -41.1415f, -27.158f, -38.7872f
+path.quadTo(SkBits2Float(0xc1c69bbd), SkBits2Float(0xc211bb3f), SkBits2Float(0xc1c6bc32), SkBits2Float(0xc2047a0c));  // -24.826f, -36.4329f, -24.8419f, -33.1192f
+path.quadTo(SkBits2Float(0xc1c6dca7), SkBits2Float(0xc1ee71b1), SkBits2Float(0xc1d9b24f), SkBits2Float(0xc1dbc9ef));  // -24.8577f, -29.8055f, -27.2121f, -27.4736f
+path.quadTo(SkBits2Float(0xc1ec87f7), SkBits2Float(0xc1c9222d), SkBits2Float(0xc203852e), SkBits2Float(0xc1c942a2));  // -29.5664f, -25.1417f, -32.8801f, -25.1575f
+path.quadTo(SkBits2Float(0xc210c661), SkBits2Float(0xc1c96317), SkBits2Float(0xc21a1a42), SkBits2Float(0xc1dc38bf));  // -36.1937f, -25.1734f, -38.5256f, -27.5277f
+path.quadTo(SkBits2Float(0xc2236e23), SkBits2Float(0xc1ef0e67), SkBits2Float(0xc2235de9), SkBits2Float(0xc204c867));  // -40.8576f, -29.882f, -40.8417f, -33.1957f
+path.quadTo(SkBits2Float(0xc2234dae), SkBits2Float(0xc212099a), SkBits2Float(0xc219e2da), SkBits2Float(0xc21b5d7a));  // -40.8259f, -36.5094f, -38.4715f, -38.8413f
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -19.799f, -19.799f
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));  // -11.598f, -28, 0, -28
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));  // 11.598f, -28, 19.799f, -19.799f
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));  // 28, -11.598f, 28, 0
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));  // 28, 11.598f, 19.799f, 19.799f
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));  // 13.7163f, 25.8817f, 5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));  // 8, 29.7635f, 8, 33
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));  // 8, 36.3137f, 5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));  // 3.31371f, 41, 0, 41
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));  // -3.31371f, 41, -5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));  // -8, 36.3137f, -8, 33
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));  // -8, 29.7635f, -5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));  // -13.7163f, 25.8817f, -19.799f, 19.799f
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));  // -28, 11.598f, -28, 0
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -28, -11.598f, -19.799f, -19.799f
+path.close();
+path.moveTo(SkBits2Float(0xc23ca6f0), SkBits2Float(0x40866f38));  // -47.163f, 4.20108f
+path.quadTo(SkBits2Float(0xc22fd68a), SkBits2Float(0x4056a1f8), SkBits2Float(0xc2246156), SkBits2Float(0x40a0a0d4));  // -43.9595f, 3.35364f, -41.0951f, 5.01963f
+path.quadTo(SkBits2Float(0xc218ec21), SkBits2Float(0x40d5f0aa), SkBits2Float(0xc2158859), SkBits2Float(0x411e39ec));  // -38.2306f, 6.68563f, -37.3832f, 9.88914f
+path.quadTo(SkBits2Float(0xc2158055), SkBits2Float(0x411eb323), SkBits2Float(0xc2157870), SkBits2Float(0x411f2dc3));  // -37.3753f, 9.91873f, -37.3676f, 9.94867f
+path.quadTo(SkBits2Float(0xc2157002), SkBits2Float(0x411fa855), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));  // -37.3594f, 9.9786f, -37.3514f, 10.0083f
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));  // -36.4938f, 13.2091f, -38.1506f, 16.0788f
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));  // -39.8075f, 18.9486f, -43.0083f, 19.8062f
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));  // -46.2091f, 20.6639f, -49.0788f, 19.007f
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));  // -51.9486f, 17.3502f, -52.8062f, 14.1494f
+path.quadTo(SkBits2Float(0xc2534522), SkBits2Float(0x4161b7a0), SkBits2Float(0xc253504c), SkBits2Float(0x41610a87));  // -52.8175f, 14.1073f, -52.8284f, 14.0651f
+path.quadTo(SkBits2Float(0xc2535c1f), SkBits2Float(0x41605e45), SkBits2Float(0xc2536784), SkBits2Float(0x415fb1f8));  // -52.84f, 14.023f, -52.8511f, 13.9809f
+path.quadTo(SkBits2Float(0xc256cb4c), SkBits2Float(0x412c7060), SkBits2Float(0xc2502151), SkBits2Float(0x40fd371c));  // -53.6985f, 10.7774f, -52.0325f, 7.91298f
+path.quadTo(SkBits2Float(0xc2497756), SkBits2Float(0x40a18d74), SkBits2Float(0xc23ca6f0), SkBits2Float(0x40866f38));  // -50.3665f, 5.04852f, -47.163f, 4.20108f
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 27.3431f, 27.3431f
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));  // 29.6863f, 25, 33, 25
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));  // 36.3137f, 25, 38.6569f, 27.3431f
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));  // 41, 29.6863f, 41, 33
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));  // 41, 36.3137f, 38.6569f, 38.6569f
+path.quadTo(SkBits2Float(0x421a8115), SkBits2Float(0x421ac027), SkBits2Float(0x421a613d), SkBits2Float(0x421adf68));  // 38.6261f, 38.6876f, 38.595f, 38.7182f
+path.quadTo(SkBits2Float(0x421a41da), SkBits2Float(0x421aff30), SkBits2Float(0x421a2234), SkBits2Float(0x421b1ea2));  // 38.5643f, 38.7492f, 38.5334f, 38.7799f
+path.quadTo(SkBits2Float(0x4210bb30), SkBits2Float(0x4224765a), SkBits2Float(0x420379f7), SkBits2Float(0x42246b89));  // 36.1828f, 41.1156f, 32.8691f, 41.105f
+path.quadTo(SkBits2Float(0x41ec717c), SkBits2Float(0x422460b7), SkBits2Float(0x41d9c20c), SkBits2Float(0x421af9b1));  // 29.5554f, 41.0944f, 27.2197f, 38.7438f
+path.quadTo(SkBits2Float(0x41c7129d), SkBits2Float(0x421192ad), SkBits2Float(0x41c72841), SkBits2Float(0x42045174));  // 24.8841f, 36.3932f, 24.8947f, 33.0795f
+path.quadTo(SkBits2Float(0x41c73de4), SkBits2Float(0x41ee2077), SkBits2Float(0x41da0bed), SkBits2Float(0x41db7107));  // 24.9052f, 29.7659f, 27.2558f, 27.4302f
+path.quadTo(SkBits2Float(0x41da38d6), SkBits2Float(0x41db4467), SkBits2Float(0x41da65bd), SkBits2Float(0x41db185f));  // 27.2778f, 27.4084f, 27.2997f, 27.3869f
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41edbcc3), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));  // 25, 29.7172f, 25, 33
+path.lineTo(SkBits2Float(0x41da65bd), SkBits2Float(0x41db185f));  // 27.2997f, 27.3869f
+path.lineTo(SkBits2Float(0x41da65c4), SkBits2Float(0x41db1859));  // 27.2997f, 27.3869f
+path.quadTo(SkBits2Float(0x41da921e), SkBits2Float(0x41daeb68), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 27.3213f, 27.3649f, 27.3431f, 27.3431f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4223940b), SkBits2Float(0x420485b1));  // 40.8946f, 33.1306f
+path.quadTo(SkBits2Float(0x4223893a), SkBits2Float(0x4211c6ea), SkBits2Float(0x421a2234), SkBits2Float(0x421b1ea2));  // 40.884f, 36.4443f, 38.5334f, 38.7799f
+path.quadTo(SkBits2Float(0x4210bb30), SkBits2Float(0x4224765a), SkBits2Float(0x420379f7), SkBits2Float(0x42246b89));  // 36.1828f, 41.1156f, 32.8691f, 41.105f
+path.quadTo(SkBits2Float(0x41ec717c), SkBits2Float(0x422460b7), SkBits2Float(0x41d9c20c), SkBits2Float(0x421af9b1));  // 29.5554f, 41.0944f, 27.2197f, 38.7438f
+path.quadTo(SkBits2Float(0x41c7129d), SkBits2Float(0x421192ad), SkBits2Float(0x41c72841), SkBits2Float(0x42045174));  // 24.8841f, 36.3932f, 24.8947f, 33.0795f
+path.quadTo(SkBits2Float(0x41c73de4), SkBits2Float(0x41ee2077), SkBits2Float(0x41da0bed), SkBits2Float(0x41db7107));  // 24.9052f, 29.7659f, 27.2558f, 27.4302f
+path.quadTo(SkBits2Float(0x41ecd9f7), SkBits2Float(0x41c8c198), SkBits2Float(0x4203ae34), SkBits2Float(0x41c8d73b));  // 29.6064f, 25.0945f, 32.9201f, 25.1051f
+path.quadTo(SkBits2Float(0x4210ef6d), SkBits2Float(0x41c8ecdf), SkBits2Float(0x421a4725), SkBits2Float(0x41dbbae8));  // 36.2338f, 25.1157f, 38.5695f, 27.4663f
+path.quadTo(SkBits2Float(0x42239edd), SkBits2Float(0x41ee88f2), SkBits2Float(0x4223940b), SkBits2Float(0x420485b1));  // 40.9051f, 29.8169f, 40.8946f, 33.1306f
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_6411089(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x410373c2), SkBits2Float(0xc24ff13e));
+path.quadTo(SkBits2Float(0x4131701e), SkBits2Float(0xc2568a1e), SkBits2Float(0x41649d46), SkBits2Float(0xc2531340));
+path.quadTo(SkBits2Float(0x418be539), SkBits2Float(0xc24f9c64), SkBits2Float(0x419916f9), SkBits2Float(0xc2441d4e));
+path.quadTo(SkBits2Float(0x41a648b8), SkBits2Float(0xc2389e37), SkBits2Float(0x419f5afe), SkBits2Float(0xc22bd2ec));
+path.quadTo(SkBits2Float(0x41986d46), SkBits2Float(0xc21f07a2), SkBits2Float(0x41816f18), SkBits2Float(0xc2186ec2));
+path.quadTo(SkBits2Float(0x4154e1d6), SkBits2Float(0xc211d5e2), SkBits2Float(0x4121b4ad), SkBits2Float(0xc2154cbf));
+path.quadTo(SkBits2Float(0x40dd0f06), SkBits2Float(0xc218c39c), SkBits2Float(0x40a84808), SkBits2Float(0xc22442b2));
+path.quadTo(SkBits2Float(0x40670210), SkBits2Float(0xc22fc1c9), SkBits2Float(0x408f37ec), SkBits2Float(0xc23c8d13));
+path.quadTo(SkBits2Float(0x40aaeed0), SkBits2Float(0xc249585e), SkBits2Float(0x410373c2), SkBits2Float(0xc24ff13e));
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0xc23ca48b), SkBits2Float(0x408745a4));
+path.quadTo(SkBits2Float(0xc22fd4a1), SkBits2Float(0x405831a8), SkBits2Float(0xc2245e7a), SkBits2Float(0x40a15ba4));
+path.quadTo(SkBits2Float(0xc218e853), SkBits2Float(0x40d69e76), SkBits2Float(0xc21582b9), SkBits2Float(0x411e8ee7));
+path.quadTo(SkBits2Float(0xc2121d1f), SkBits2Float(0x4151ce91), SkBits2Float(0xc218c57a), SkBits2Float(0x417fa72d));
+path.quadTo(SkBits2Float(0xc21f6dd4), SkBits2Float(0x4196bfe4), SkBits2Float(0xc22c3dbe), SkBits2Float(0x419d8b16));
+path.quadTo(SkBits2Float(0xc2390da9), SkBits2Float(0x41a4564c), SkBits2Float(0xc24483d0), SkBits2Float(0x41970597));
+path.quadTo(SkBits2Float(0xc24ff9f7), SkBits2Float(0x4189b4e3), SkBits2Float(0xc2535f90), SkBits2Float(0x41602a18));
+path.quadTo(SkBits2Float(0xc256c52a), SkBits2Float(0x412cea70), SkBits2Float(0xc2501cd0), SkBits2Float(0x40fe23a8));
+path.quadTo(SkBits2Float(0xc2497476), SkBits2Float(0x40a27270), SkBits2Float(0xc23ca48b), SkBits2Float(0x408745a4));
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x421a8005), SkBits2Float(0x421ac137), SkBits2Float(0x421a5f6b), SkBits2Float(0x421ae132));
+path.lineTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.close();
+path.moveTo(SkBits2Float(0x421a52fa), SkBits2Float(0x421aed60));
+path.quadTo(SkBits2Float(0x421a59d5), SkBits2Float(0x421ae6ac), SkBits2Float(0x421a5f6b), SkBits2Float(0x421ae132));
+path.quadTo(SkBits2Float(0x421a5a2a), SkBits2Float(0x421ae65a), SkBits2Float(0x421a52fa), SkBits2Float(0x421aed60));
+path.close();
+path.moveTo(SkBits2Float(0x421a52fa), SkBits2Float(0x421aed60));
+path.quadTo(SkBits2Float(0x42110a85), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41edcbe0), SkBits2Float(0x41da509a), SkBits2Float(0x41db2dd4));
+path.quadTo(SkBits2Float(0x41da18a0), SkBits2Float(0x41db6476), SkBits2Float(0x41d9e121), SkBits2Float(0x41db9b85));
+path.quadTo(SkBits2Float(0x41c70f73), SkBits2Float(0x41ee474a), SkBits2Float(0x41c6f4a5), SkBits2Float(0x420464db));
+path.quadTo(SkBits2Float(0x41c6d9d6), SkBits2Float(0x4211a612), SkBits2Float(0x41d9859b), SkBits2Float(0x421b0ee8));
+path.quadTo(SkBits2Float(0x41ec315f), SkBits2Float(0x422477bf), SkBits2Float(0x420359e6), SkBits2Float(0x42248527));
+path.quadTo(SkBits2Float(0x42109b1d), SkBits2Float(0x4224928e), SkBits2Float(0x421a03f3), SkBits2Float(0x421b3cab));
+path.quadTo(SkBits2Float(0x421a2b68), SkBits2Float(0x421b1585), SkBits2Float(0x421a5203), SkBits2Float(0x421aee52));
+path.quadTo(SkBits2Float(0x421a527f), SkBits2Float(0x421aedd9), SkBits2Float(0x421a52fa), SkBits2Float(0x421aed60));
+path.close();
+path.moveTo(SkBits2Float(0xc1810850), SkBits2Float(0x4218848a));
+path.quadTo(SkBits2Float(0xc1541d2b), SkBits2Float(0x4211e7ca), SkBits2Float(0xc120eb59), SkBits2Float(0x42155a57));
+path.quadTo(SkBits2Float(0xc0db730e), SkBits2Float(0x4218cce3), SkBits2Float(0xc0a68d10), SkBits2Float(0x422449c0));
+path.quadTo(SkBits2Float(0xc0634e28), SkBits2Float(0x422fc69d), SkBits2Float(0xc08d3b78), SkBits2Float(0x423c9311));
+path.quadTo(SkBits2Float(0xc0a8cfdc), SkBits2Float(0x42495f86), SkBits2Float(0xc1025b62), SkBits2Float(0x424ffc46));
+path.quadTo(SkBits2Float(0xc1304ed4), SkBits2Float(0x42569906), SkBits2Float(0xc16380a6), SkBits2Float(0x42532678));
+path.quadTo(SkBits2Float(0xc18b593d), SkBits2Float(0x424fb3ec), SkBits2Float(0xc19892bc), SkBits2Float(0x42443710));
+path.quadTo(SkBits2Float(0xc1a5cc3c), SkBits2Float(0x4238ba32), SkBits2Float(0xc19ee722), SkBits2Float(0x422bedbe));
+path.quadTo(SkBits2Float(0xc1980208), SkBits2Float(0x421f214a), SkBits2Float(0xc1810850), SkBits2Float(0x4218848a));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_3283699(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x411032d0), SkBits2Float(0xc24f69e5));
+path.quadTo(SkBits2Float(0x413e956c), SkBits2Float(0xc255d56a), SkBits2Float(0x41718a9c), SkBits2Float(0xc2522c67));
+path.quadTo(SkBits2Float(0x41755c61), SkBits2Float(0xc251e62c), SkBits2Float(0x417909ca), SkBits2Float(0xc2519487));
+path.quadTo(SkBits2Float(0x417cd0ce), SkBits2Float(0xc2515870), SkBits2Float(0x41804eae), SkBits2Float(0xc2510dd3));
+path.quadTo(SkBits2Float(0x4199a689), SkBits2Float(0xc24d2a58), SkBits2Float(0x41a61251), SkBits2Float(0xc24174a0));
+path.quadTo(SkBits2Float(0x41b27e19), SkBits2Float(0xc235bee8), SkBits2Float(0x41aab721), SkBits2Float(0xc22912fa));
+path.quadTo(SkBits2Float(0x41a2f02a), SkBits2Float(0xc21c670e), SkBits2Float(0x418b84ba), SkBits2Float(0xc216312a));
+path.quadTo(SkBits2Float(0x41683297), SkBits2Float(0xc20ffb46), SkBits2Float(0x413582e0), SkBits2Float(0xc213dec1));
+path.quadTo(SkBits2Float(0x4132d4e6), SkBits2Float(0xc2141362), SkBits2Float(0x4130395d), SkBits2Float(0xc2144d9c));
+path.quadTo(SkBits2Float(0x412d8b06), SkBits2Float(0xc21477ef), SkBits2Float(0x412ad979), SkBits2Float(0xc214a976));
+path.quadTo(SkBits2Float(0x40efc890), SkBits2Float(0xc2185278), SkBits2Float(0x40bc6c64), SkBits2Float(0xc223eb20));
+path.quadTo(SkBits2Float(0x4089103c), SkBits2Float(0xc22f83c7), SkBits2Float(0x40a65850), SkBits2Float(0xc23c4113));
+path.quadTo(SkBits2Float(0x40c3a064), SkBits2Float(0xc248fe60), SkBits2Float(0x411032d0), SkBits2Float(0xc24f69e5));
+path.close();
+path.moveTo(SkBits2Float(0xc2121147), SkBits2Float(0xc222bcdb));
+path.quadTo(SkBits2Float(0xc208340d), SkBits2Float(0xc22b9769), SkBits2Float(0xc1f5ef7e), SkBits2Float(0xc22ae080));
+path.quadTo(SkBits2Float(0xc1db76e2), SkBits2Float(0xc22a2997), SkBits2Float(0xc1c9c1c6), SkBits2Float(0xc2204c5c));
+path.quadTo(SkBits2Float(0xc1b80ca9), SkBits2Float(0xc2166f21), SkBits2Float(0xc1b97a7c), SkBits2Float(0xc20932d3));
+path.quadTo(SkBits2Float(0xc1bae84e), SkBits2Float(0xc1f7ed0b), SkBits2Float(0xc1cea2c4), SkBits2Float(0xc1e637ee));
+path.quadTo(SkBits2Float(0xc1e25d39), SkBits2Float(0xc1d482d2), SkBits2Float(0xc1fcd5d5), SkBits2Float(0xc1d5f0a4));
+path.quadTo(SkBits2Float(0xc20ba739), SkBits2Float(0xc1d75e77), SkBits2Float(0xc21481c6), SkBits2Float(0xc1eb18ec));
+path.quadTo(SkBits2Float(0xc21d5c55), SkBits2Float(0xc1fed362), SkBits2Float(0xc21ca56c), SkBits2Float(0xc20ca5ff));
+path.quadTo(SkBits2Float(0xc21bee83), SkBits2Float(0xc219e24d), SkBits2Float(0xc2121147), SkBits2Float(0xc222bcdb));
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0xc23d19f2), SkBits2Float(0x4029d790));
+path.quadTo(SkBits2Float(0xc2302ee1), SkBits2Float(0x3ff4b3e0), SkBits2Float(0xc224f322), SkBits2Float(0x406aec68));
+path.quadTo(SkBits2Float(0xc219b764), SkBits2Float(0x40adbf72), SkBits2Float(0xc216bf8a), SkBits2Float(0x410a8bfe));
+path.quadTo(SkBits2Float(0xc213c7b1), SkBits2Float(0x413e3841), SkBits2Float(0xc21ad0d8), SkBits2Float(0x416b273b));
+path.quadTo(SkBits2Float(0xc221da00), SkBits2Float(0x418c0b1a), SkBits2Float(0xc22ec511), SkBits2Float(0x4191facd));
+path.quadTo(SkBits2Float(0xc23bb022), SkBits2Float(0x4197ea80), SkBits2Float(0xc246ebe0), SkBits2Float(0x4189d830));
+path.quadTo(SkBits2Float(0xc252279f), SkBits2Float(0x41778bc2), SkBits2Float(0xc2551f78), SkBits2Float(0x4143df80));
+path.quadTo(SkBits2Float(0xc2581752), SkBits2Float(0x4110333c), SkBits2Float(0xc2510e2a), SkBits2Float(0x40c68884));
+path.quadTo(SkBits2Float(0xc24a0502), SkBits2Float(0x40595520), SkBits2Float(0xc23d19f2), SkBits2Float(0x4029d790));
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x421951ff), SkBits2Float(0x421bef3d), SkBits2Float(0x4217f013), SkBits2Float(0x421d0f2c));
+path.lineTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.close();
+path.moveTo(SkBits2Float(0x4217c478), SkBits2Float(0x421d326a));
+path.quadTo(SkBits2Float(0x4217da16), SkBits2Float(0x421d2110), SkBits2Float(0x4217f013), SkBits2Float(0x421d0f2c));
+path.quadTo(SkBits2Float(0x4217da0a), SkBits2Float(0x421d2119), SkBits2Float(0x4217c478), SkBits2Float(0x421d326a));
+path.close();
+path.moveTo(SkBits2Float(0x4217b2af), SkBits2Float(0x421d40a6));
+path.quadTo(SkBits2Float(0x4217bc6a), SkBits2Float(0x421d38e1), SkBits2Float(0x4217c478), SkBits2Float(0x421d326a));
+path.quadTo(SkBits2Float(0x4217bc41), SkBits2Float(0x421d3902), SkBits2Float(0x4217b2af), SkBits2Float(0x421d40a6));
+path.close();
+path.moveTo(SkBits2Float(0x4217b2af), SkBits2Float(0x421d40a6));
+path.quadTo(SkBits2Float(0x42167086), SkBits2Float(0x421eb7af), SkBits2Float(0x4214f81f), SkBits2Float(0x422015f3));
+path.quadTo(SkBits2Float(0x420b440b), SkBits2Float(0x42291d88), SkBits2Float(0x41fc09ff), SkBits2Float(0x4228a38f));
+path.quadTo(SkBits2Float(0x41e18be8), SkBits2Float(0x42282996), SkBits2Float(0x41cf7cbe), SkBits2Float(0x421e7581));
+path.quadTo(SkBits2Float(0x41bd6d94), SkBits2Float(0x4214c16e), SkBits2Float(0x41be6187), SkBits2Float(0x42078262));
+path.quadTo(SkBits2Float(0x41bf557a), SkBits2Float(0x41f486ac), SkBits2Float(0x41d2bda2), SkBits2Float(0x41e27782));
+path.quadTo(SkBits2Float(0x41d4cf6c), SkBits2Float(0x41e08a82), SkBits2Float(0x41d6f5de), SkBits2Float(0x41ded4d5));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41f0513a), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x420f3f16), SkBits2Float(0x42240000), SkBits2Float(0x4217b2af), SkBits2Float(0x421d40a6));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_1026368(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x4101c02c), SkBits2Float(0xc2500256));  // 8.10942f, -52.0023f
+path.quadTo(SkBits2Float(0x412faeae), SkBits2Float(0xc256a13a), SkBits2Float(0x4162e312), SkBits2Float(0xc2533110));  // 10.9801f, -53.6574f, 14.1804f, -52.7979f
+path.quadTo(SkBits2Float(0x418b0bbc), SkBits2Float(0xc24fc0e8), SkBits2Float(0x41984984), SkBits2Float(0xc2444547));  // 17.3807f, -51.9384f, 19.0359f, -49.0677f
+path.quadTo(SkBits2Float(0x41a5874c), SkBits2Float(0xc238c9a6), SkBits2Float(0x419ea6f8), SkBits2Float(0xc22bfc8c));  // 20.6911f, -46.1969f, 19.8315f, -42.9966f
+path.quadTo(SkBits2Float(0x4197c6a7), SkBits2Float(0xc21f2f74), SkBits2Float(0x4180cf66), SkBits2Float(0xc2189090));  // 18.972f, -39.7963f, 16.1013f, -38.1412f
+path.quadTo(SkBits2Float(0x4153b048), SkBits2Float(0xc211f1ac), SkBits2Float(0x41207be4), SkBits2Float(0xc21561d5));  // 13.2305f, -36.486f, 10.0302f, -37.3455f
+path.quadTo(SkBits2Float(0x40da8f00), SkBits2Float(0xc218d1fe), SkBits2Float(0x40a597e0), SkBits2Float(0xc2244d9f));  // 6.82996f, -38.2051f, 5.17479f, -41.0758f
+path.quadTo(SkBits2Float(0x40614180), SkBits2Float(0xc22fc940), SkBits2Float(0x408c220c), SkBits2Float(0xc23c9658));  // 3.51962f, -43.9465f, 4.37916f, -47.1468f
+path.quadTo(SkBits2Float(0x40a7a350), SkBits2Float(0xc2496372), SkBits2Float(0x4101c02c), SkBits2Float(0xc2500256));  // 5.23869f, -50.3471f, 8.10942f, -52.0023f
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -19.799f, -19.799f
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));  // -11.598f, -28, 0, -28
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));  // 11.598f, -28, 19.799f, -19.799f
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));  // 28, -11.598f, 28, 0
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));  // 28, 11.598f, 19.799f, 19.799f
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));  // 13.7163f, 25.8817f, 5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));  // 8, 29.7635f, 8, 33
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));  // 8, 36.3137f, 5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));  // 3.31371f, 41, 0, 41
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));  // -3.31371f, 41, -5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));  // -8, 36.3137f, -8, 33
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));  // -8, 29.7635f, -5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));  // -13.7163f, 25.8817f, -19.799f, 19.799f
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));  // -28, 11.598f, -28, 0
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -28, -11.598f, -19.799f, -19.799f
+path.close();
+path.moveTo(SkBits2Float(0xc23c9b95), SkBits2Float(0x408a5bd8));  // -47.1519f, 4.32371f
+path.quadTo(SkBits2Float(0xc22fcd74), SkBits2Float(0x405df2c8), SkBits2Float(0xc22453d3), SkBits2Float(0x40a40c28));  // -43.9506f, 3.46794f, -41.0819f, 5.12648f
+path.quadTo(SkBits2Float(0xc218da31), SkBits2Float(0x40d91eee), SkBits2Float(0xc2156de2), SkBits2Float(0x411fc7fd));  // -38.2131f, 6.78503f, -37.3573f, 9.98633f
+path.quadTo(SkBits2Float(0xc2120193), SkBits2Float(0x41530084), SkBits2Float(0xc218a3ec), SkBits2Float(0x41807386));  // -36.5015f, 13.1876f, -38.1601f, 16.0564f
+path.quadTo(SkBits2Float(0xc21f4644), SkBits2Float(0x419766c9), SkBits2Float(0xc22c1466), SkBits2Float(0x419e3f66));  // -39.8186f, 18.9252f, -43.0199f, 19.781f
+path.quadTo(SkBits2Float(0xc238e288), SkBits2Float(0x41a51804), SkBits2Float(0xc2445c2a), SkBits2Float(0x4197d353));  // -46.2212f, 20.6367f, -49.09f, 18.9782f
+path.quadTo(SkBits2Float(0xc24fd5cc), SkBits2Float(0x418a8ea2), SkBits2Float(0xc253421a), SkBits2Float(0x4161e4ba));  // -51.9588f, 17.3196f, -52.8146f, 14.1183f
+path.quadTo(SkBits2Float(0xc256ae69), SkBits2Float(0x412eac36), SkBits2Float(0xc2500c10), SkBits2Float(0x4100c5b0));  // -53.6703f, 10.917f, -52.0118f, 8.04826f
+path.quadTo(SkBits2Float(0xc24969b8), SkBits2Float(0x40a5be50), SkBits2Float(0xc23c9b95), SkBits2Float(0x408a5bd8));  // -50.3532f, 5.17948f, -47.1519f, 4.32371f
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 27.3431f, 27.3431f
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));  // 29.6863f, 25, 33, 25
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));  // 36.3137f, 25, 38.6569f, 27.3431f
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));  // 41, 29.6863f, 41, 33
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));  // 41, 36.3137f, 38.6569f, 38.6569f
+path.quadTo(SkBits2Float(0x421a9aa6), SkBits2Float(0x421aa696), SkBits2Float(0x421a9438), SkBits2Float(0x421aacfe));  // 38.651f, 38.6627f, 38.6447f, 38.6689f
+path.quadTo(SkBits2Float(0x421a8ec8), SkBits2Float(0x421ab270), SkBits2Float(0x421a8956), SkBits2Float(0x421ab7e2));  // 38.6394f, 38.6743f, 38.6341f, 38.6796f
+path.quadTo(SkBits2Float(0x421a8386), SkBits2Float(0x421abdb2), SkBits2Float(0x421a7db3), SkBits2Float(0x421ac381));  // 38.6284f, 38.6852f, 38.6228f, 38.6909f
+path.quadTo(SkBits2Float(0x42111c34), SkBits2Float(0x422420c6), SkBits2Float(0x4203daf7), SkBits2Float(0x42241dc8));  // 36.2775f, 41.032f, 32.9638f, 41.0291f
+path.quadTo(SkBits2Float(0x41ed3376), SkBits2Float(0x42241aca), SkBits2Float(0x41da78ee), SkBits2Float(0x421ab94a));  // 29.6501f, 41.0262f, 27.309f, 38.6809f
+path.quadTo(SkBits2Float(0x41c7be68), SkBits2Float(0x421157cc), SkBits2Float(0x41c7c464), SkBits2Float(0x4204168f));  // 24.968f, 36.3357f, 24.9709f, 33.022f
+path.quadTo(SkBits2Float(0x41c7ca5f), SkBits2Float(0x41edaaa5), SkBits2Float(0x41da8d5d), SkBits2Float(0x41daf01d));  // 24.9738f, 29.7083f, 27.319f, 27.3672f
+path.quadTo(SkBits2Float(0x41da915b), SkBits2Float(0x41daec21), SkBits2Float(0x41da955a), SkBits2Float(0x41dae825));  // 27.321f, 27.3653f, 27.3229f, 27.3634f
+path.quadTo(SkBits2Float(0x41da9997), SkBits2Float(0x41dae3e8), SkBits2Float(0x41da9dd4), SkBits2Float(0x41dadfac));  // 27.325f, 27.3613f, 27.3271f, 27.3592f
+path.quadTo(SkBits2Float(0x41daa667), SkBits2Float(0x41dad71b), SkBits2Float(0x41daaefc), SkBits2Float(0x41dace8e));  // 27.3313f, 27.355f, 27.3354f, 27.3509f
+path.quadTo(SkBits2Float(0x41daaa91), SkBits2Float(0x41dad2fb), SkBits2Float(0x41daa628), SkBits2Float(0x41dad768));  // 27.3333f, 27.353f, 27.3311f, 27.3552f
+path.quadTo(SkBits2Float(0x41dab217), SkBits2Float(0x41dacb89), SkBits2Float(0x41dabedf), SkBits2Float(0x41dabedf));  // 27.337f, 27.3494f, 27.3432f, 27.3432f
+path.lineTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 27.3431f, 27.3431f
+path.close();
+path.moveTo(SkBits2Float(0xc180b86e), SkBits2Float(0x42189568));  // -16.0901f, 38.1459f
+path.quadTo(SkBits2Float(0xc1538459), SkBits2Float(0x4211f5a8), SkBits2Float(0xc1204eec), SkBits2Float(0x421564da));  // -13.2198f, 36.4899f, -10.0193f, 37.3485f
+path.quadTo(SkBits2Float(0xc0da32fc), SkBits2Float(0x4218d40d), SkBits2Float(0xc0a534f4), SkBits2Float(0x42244f2e));  // -6.81872f, 38.2071f, -5.16271f, 41.0773f
+path.quadTo(SkBits2Float(0xc0606dd0), SkBits2Float(0x422fca4f), SkBits2Float(0xc08bb080), SkBits2Float(0x423c97aa));  // -3.5067f, 43.9476f, -4.3653f, 47.1481f
+path.quadTo(SkBits2Float(0xc0a72a14), SkBits2Float(0x42496506), SkBits2Float(0xc101818e), SkBits2Float(0x425004c7));  // -5.22389f, 50.3487f, -8.09413f, 52.0047f
+path.quadTo(SkBits2Float(0xc12f6e12), SkBits2Float(0x4256a488), SkBits2Float(0xc162a37e), SkBits2Float(0x42533554));  // -10.9644f, 53.6607f, -14.1649f, 52.8021f
+path.quadTo(SkBits2Float(0xc18aec77), SkBits2Float(0x424fc622), SkBits2Float(0xc1982bfa), SkBits2Float(0x42444b02));  // -17.3655f, 51.9435f, -19.0215f, 49.0732f
+path.quadTo(SkBits2Float(0xc1a56b7c), SkBits2Float(0x4238cfe0), SkBits2Float(0xc19e8d15), SkBits2Float(0x422c0285));  // -20.6775f, 46.203f, -19.8189f, 43.0025f
+path.quadTo(SkBits2Float(0xc197aeb0), SkBits2Float(0x421f352a), SkBits2Float(0xc180b86e), SkBits2Float(0x42189568));  // -18.9603f, 39.8019f, -16.0901f, 38.1459f
+path.close();
+path.moveTo(SkBits2Float(0x421a8f2f), SkBits2Float(0x421ab201));  // 38.6398f, 38.6738f
+path.lineTo(SkBits2Float(0x421a9433), SkBits2Float(0x421aacf9));  // 38.6447f, 38.6689f
+path.lineTo(SkBits2Float(0x421a9438), SkBits2Float(0x421aacfe));  // 38.6447f, 38.6689f
+path.quadTo(SkBits2Float(0x421a91b4), SkBits2Float(0x421aaf80), SkBits2Float(0x421a8f2f), SkBits2Float(0x421ab201));  // 38.6423f, 38.6714f, 38.6398f, 38.6738f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000));  // 41, 33
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));  // 41, 36.3137f, 38.6569f, 38.6569f
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));  // 36.3137f, 41, 33, 41
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));  // 29.6863f, 41, 27.3431f, 38.6569f
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));  // 25, 36.3137f, 25, 33
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 25, 29.6863f, 27.3431f, 27.3431f
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));  // 29.6863f, 25, 33, 25
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));  // 36.3137f, 25, 38.6569f, 27.3431f
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));  // 41, 29.6863f, 41, 33
+path.close();
+
+    SkPath path2(path);
+    // DEBUG_UNDER_DEVELOPMENT  fuzz763_1026368 disable expectation check for now
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, !SkOpGlobalState::DebugRunFail());
+}
+
+static void fuzz763_5485218(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0xc1b1a434), SkBits2Float(0xc247d348));
+path.quadTo(SkBits2Float(0xc1996ac1), SkBits2Float(0xc24d3588), SkBits2Float(0xc180ac87), SkBits2Float(0xc248738e));
+path.quadTo(SkBits2Float(0xc14fdc9c), SkBits2Float(0xc243b194), SkBits2Float(0xc13a53a0), SkBits2Float(0xc23794da));
+path.quadTo(SkBits2Float(0xc124caa4), SkBits2Float(0xc22b7821), SkBits2Float(0xc137d28c), SkBits2Float(0xc21f1904));
+path.quadTo(SkBits2Float(0xc14ada74), SkBits2Float(0xc212b9e7), SkBits2Float(0xc17b4d59), SkBits2Float(0xc20d57a8));
+path.quadTo(SkBits2Float(0xc195e020), SkBits2Float(0xc207f569), SkBits2Float(0xc1ae9e58), SkBits2Float(0xc20cb763));
+path.quadTo(SkBits2Float(0xc1c75c92), SkBits2Float(0xc211795d), SkBits2Float(0xc1d22110), SkBits2Float(0xc21d9616));
+path.quadTo(SkBits2Float(0xc1dce590), SkBits2Float(0xc229b2d0), SkBits2Float(0xc1d3619c), SkBits2Float(0xc23611ec));
+path.quadTo(SkBits2Float(0xc1c9dda8), SkBits2Float(0xc242710a), SkBits2Float(0xc1b1a434), SkBits2Float(0xc247d348));
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x40b3b5dc), SkBits2Float(0x421aca81), SkBits2Float(0x40b26487), SkBits2Float(0x421af3a3));
+path.quadTo(SkBits2Float(0x40eb2464), SkBits2Float(0x421ca9a9), SkBits2Float(0x410d8414), SkBits2Float(0x4221c755));
+path.quadTo(SkBits2Float(0x4135d252), SkBits2Float(0x422a63dc), SkBits2Float(0x4139f710), SkBits2Float(0x42379ab7));
+path.quadTo(SkBits2Float(0x413e1bd4), SkBits2Float(0x4244d193), SkBits2Float(0x411ba9b8), SkBits2Float(0x424ee522));
+path.quadTo(SkBits2Float(0x40f26f3c), SkBits2Float(0x4258f8b2), SkBits2Float(0x4088b85c), SkBits2Float(0x425a01e2));
+path.quadTo(SkBits2Float(0x3f780c00), SkBits2Float(0x425b0b12), SkBits2Float(0xbfc66bf0), SkBits2Float(0x42526e8c));
+path.quadTo(SkBits2Float(0xc0823778), SkBits2Float(0x4249d205), SkBits2Float(0xc08a80f8), SkBits2Float(0x423c9b29));
+path.quadTo(SkBits2Float(0xc092ca7c), SkBits2Float(0x422f644e), SkBits2Float(0xc01bcc90), SkBits2Float(0x422550be));
+path.quadTo(SkBits2Float(0xc00c0f96), SkBits2Float(0x42242a18), SkBits2Float(0xbff6b9b1), SkBits2Float(0x422321a7));
+path.quadTo(SkBits2Float(0xc080dd2a), SkBits2Float(0x42212597), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0x422e1dec), SkBits2Float(0xc1951160));
+path.quadTo(SkBits2Float(0x423b0222), SkBits2Float(0xc19b3b8f), SkBits2Float(0x42464db6), SkBits2Float(0xc18d5c4c));
+path.quadTo(SkBits2Float(0x42519948), SkBits2Float(0xc17efa12), SkBits2Float(0x4254ae60), SkBits2Float(0xc14b693a));
+path.quadTo(SkBits2Float(0x4257c378), SkBits2Float(0xc117d862), SkBits2Float(0x4250d3d6), SkBits2Float(0xc0d5542c));
+path.quadTo(SkBits2Float(0x4249e434), SkBits2Float(0xc075ef28), SkBits2Float(0x423cfffe), SkBits2Float(0xc0449db8));
+path.quadTo(SkBits2Float(0x423cdcf0), SkBits2Float(0xc044179d), SkBits2Float(0x423cb9cd), SkBits2Float(0xc04395ab));
+path.quadTo(SkBits2Float(0x423cdda4), SkBits2Float(0xc0435d6a), SkBits2Float(0x423d018a), SkBits2Float(0xc04320b0));
+path.quadTo(SkBits2Float(0x424a2ff8), SkBits2Float(0xc02cd470), SkBits2Float(0x425285c7), SkBits2Float(0xbdfdd900));
+path.quadTo(SkBits2Float(0x425adb97), SkBits2Float(0x401cf6e8), SkBits2Float(0x425976d3), SkBits2Float(0x40b7eee0));
+path.quadTo(SkBits2Float(0x42581210), SkBits2Float(0x4110b128), SkBits2Float(0x424dc3b4), SkBits2Float(0x41320868));
+path.quadTo(SkBits2Float(0x4243755a), SkBits2Float(0x41535fa6), SkBits2Float(0x423646ec), SkBits2Float(0x414dcc96));
+path.quadTo(SkBits2Float(0x4229187e), SkBits2Float(0x41483989), SkBits2Float(0x4220c2ae), SkBits2Float(0x411f001d));
+path.quadTo(SkBits2Float(0x42186cde), SkBits2Float(0x40eb8d66), SkBits2Float(0x4219d1a2), SkBits2Float(0x408219f4));
+path.quadTo(SkBits2Float(0x421b3666), SkBits2Float(0x3f453420), SkBits2Float(0x422584c1), SkBits2Float(0xbfa81fe0));
+path.quadTo(SkBits2Float(0x422b4324), SkBits2Float(0xc01e631a), SkBits2Float(0x4231e626), SkBits2Float(0xc0385d42));
+path.quadTo(SkBits2Float(0x422b1bb8), SkBits2Float(0xc0446d07), SkBits2Float(0x4224d036), SkBits2Float(0xc0812328));
+path.quadTo(SkBits2Float(0x421984a2), SkBits2Float(0xc0b8a034), SkBits2Float(0x42166f8b), SkBits2Float(0xc10fe0f3));
+path.quadTo(SkBits2Float(0x42135a74), SkBits2Float(0xc14371ca), SkBits2Float(0x421a4a15), SkBits2Float(0xc170a016));
+path.quadTo(SkBits2Float(0x422139b6), SkBits2Float(0xc18ee731), SkBits2Float(0x422e1dec), SkBits2Float(0xc1951160));
+path.close();
+path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.close();
+path.moveTo(SkBits2Float(0xc1e59c15), SkBits2Float(0x41eeb1f9));
+path.quadTo(SkBits2Float(0xc1cb6c81), SkBits2Float(0x41ea9074), SkBits2Float(0xc1b5fc96), SkBits2Float(0x41fa28d2));
+path.quadTo(SkBits2Float(0xc1a08cac), SkBits2Float(0x4204e098), SkBits2Float(0xc19c6b27), SkBits2Float(0x4211f862));
+path.quadTo(SkBits2Float(0xc19849a3), SkBits2Float(0x421f102b), SkBits2Float(0xc1a7e200), SkBits2Float(0x4229c820));
+path.quadTo(SkBits2Float(0xc1b77a5e), SkBits2Float(0x42348016), SkBits2Float(0xc1d1a9f2), SkBits2Float(0x423690d8));
+path.quadTo(SkBits2Float(0xc1ebd984), SkBits2Float(0x4238a19a), SkBits2Float(0xc200a4b7), SkBits2Float(0x4230d56b));
+path.quadTo(SkBits2Float(0xc20b5cae), SkBits2Float(0x4229093c), SkBits2Float(0xc20d6d70), SkBits2Float(0x421bf173));
+path.quadTo(SkBits2Float(0xc20f7e32), SkBits2Float(0x420ed9a9), SkBits2Float(0xc207b202), SkBits2Float(0x420421b4));
+path.quadTo(SkBits2Float(0xc1ffcba7), SkBits2Float(0x41f2d37d), SkBits2Float(0xc1e59c15), SkBits2Float(0x41eeb1f9));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x425285c7), SkBits2Float(0xbdfdd900));
+path.quadTo(SkBits2Float(0x425adb97), SkBits2Float(0x401cf6e8), SkBits2Float(0x425976d3), SkBits2Float(0x40b7eee0));
+path.quadTo(SkBits2Float(0x42581210), SkBits2Float(0x4110b128), SkBits2Float(0x424dc3b4), SkBits2Float(0x41320868));
+path.quadTo(SkBits2Float(0x4243755a), SkBits2Float(0x41535fa6), SkBits2Float(0x423646ec), SkBits2Float(0x414dcc96));
+path.quadTo(SkBits2Float(0x4229187e), SkBits2Float(0x41483989), SkBits2Float(0x4220c2ae), SkBits2Float(0x411f001d));
+path.quadTo(SkBits2Float(0x42186cde), SkBits2Float(0x40eb8d66), SkBits2Float(0x4219d1a2), SkBits2Float(0x408219f4));
+path.quadTo(SkBits2Float(0x421b3666), SkBits2Float(0x3f453420), SkBits2Float(0x422584c1), SkBits2Float(0xbfa81fe0));
+path.quadTo(SkBits2Float(0x422fd31c), SkBits2Float(0xc0596cf0), SkBits2Float(0x423d018a), SkBits2Float(0xc04320b0));
+path.quadTo(SkBits2Float(0x424a2ff8), SkBits2Float(0xc02cd470), SkBits2Float(0x425285c7), SkBits2Float(0xbdfdd900));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_2674194(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0xbfb16e10), SkBits2Float(0xc252733b));
+path.quadTo(SkBits2Float(0x3f91df50), SkBits2Float(0xc25b07b4), SkBits2Float(0x408e27f4), SkBits2Float(0xc259f3f8));
+path.quadTo(SkBits2Float(0x40f7d814), SkBits2Float(0xc258e03e), SkBits2Float(0x411e3df0), SkBits2Float(0xc24ec5d2));
+path.quadTo(SkBits2Float(0x41408fd4), SkBits2Float(0xc244ab67), SkBits2Float(0x413c40e6), SkBits2Float(0xc2377562));
+path.quadTo(SkBits2Float(0x4137f1f8), SkBits2Float(0xc22a3f5f), SkBits2Float(0x410f884c), SkBits2Float(0xc221aae6));
+path.quadTo(SkBits2Float(0x40ce3d3c), SkBits2Float(0xc219166c), SkBits2Float(0x40491a38), SkBits2Float(0xc21a2a28));
+path.quadTo(SkBits2Float(0xbe246080), SkBits2Float(0xc21b3de4), SkBits2Float(0xc0138d98), SkBits2Float(0xc225584f));
+path.quadTo(SkBits2Float(0xc08e6a98), SkBits2Float(0xc22f72ba), SkBits2Float(0xc085ccbc), SkBits2Float(0xc23ca8be));
+path.quadTo(SkBits2Float(0xc07a5dc0), SkBits2Float(0xc249dec2), SkBits2Float(0xbfb16e10), SkBits2Float(0xc252733b));
+path.close();
+path.moveTo(SkBits2Float(0x41b47dea), SkBits2Float(0xc23e32f4));
+path.quadTo(SkBits2Float(0x41ce465c), SkBits2Float(0xc24147ff), SkBits2Float(0x41e4dd74), SkBits2Float(0xc23a5854));
+path.quadTo(SkBits2Float(0x41fb748e), SkBits2Float(0xc23368a8), SkBits2Float(0x4200cf52), SkBits2Float(0xc226846f));
+path.quadTo(SkBits2Float(0x42022efd), SkBits2Float(0xc220c591), SkBits2Float(0x42019135), SkBits2Float(0xc21b57df));
+path.quadTo(SkBits2Float(0x4206cc04), SkBits2Float(0xc21cec81), SkBits2Float(0x420cb220), SkBits2Float(0xc21c9a86));
+path.quadTo(SkBits2Float(0x4219ee60), SkBits2Float(0xc21be297), SkBits2Float(0x4222c82a), SkBits2Float(0xc21204ac));
+path.quadTo(SkBits2Float(0x422ba1f5), SkBits2Float(0xc20826c2), SkBits2Float(0x422aea06), SkBits2Float(0xc1f5d504));
+path.quadTo(SkBits2Float(0x422a3216), SkBits2Float(0xc1db5c85), SkBits2Float(0x4220542b), SkBits2Float(0xc1c9a8f0));
+path.quadTo(SkBits2Float(0x42167641), SkBits2Float(0xc1b7f55b), SkBits2Float(0x42093a01), SkBits2Float(0xc1b9653a));
+path.quadTo(SkBits2Float(0x41f7fb83), SkBits2Float(0xc1bad519), SkBits2Float(0x41e647ee), SkBits2Float(0xc1ce90ee));
+path.quadTo(SkBits2Float(0x41d49459), SkBits2Float(0xc1e24cc3), SkBits2Float(0x41d60438), SkBits2Float(0xc1fcc542));
+path.quadTo(SkBits2Float(0x41d62223), SkBits2Float(0xc1feec5b), SkBits2Float(0x41d65f09), SkBits2Float(0xc2008251));
+path.quadTo(SkBits2Float(0x41d45a68), SkBits2Float(0xc200343d), SkBits2Float(0x41d2419c), SkBits2Float(0xc1ffe823));
+path.quadTo(SkBits2Float(0x41b8792a), SkBits2Float(0xc1f9be0c), SkBits2Float(0x41a1e211), SkBits2Float(0xc203ceb1));
+path.quadTo(SkBits2Float(0x418b4af8), SkBits2Float(0xc20abe5d), SkBits2Float(0x418520e1), SkBits2Float(0xc217a296));
+path.quadTo(SkBits2Float(0x417ded93), SkBits2Float(0xc22486cf), SkBits2Float(0x418cd620), SkBits2Float(0xc22fd25b));
+path.quadTo(SkBits2Float(0x419ab578), SkBits2Float(0xc23b1de8), SkBits2Float(0x41b47dea), SkBits2Float(0xc23e32f4));
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.lineTo(SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
+path.close();
+path.moveTo(SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x40b3bf9e), SkBits2Float(0x421ac949), SkBits2Float(0x40b278ad), SkBits2Float(0x421af12e));
+path.quadTo(SkBits2Float(0x40d4fa46), SkBits2Float(0x42187664), SkBits2Float(0x40ff010e), SkBits2Float(0x42175b2b));
+path.quadTo(SkBits2Float(0x41336223), SkBits2Float(0x42149fe4), SkBits2Float(0x415fcb81), SkBits2Float(0x421bdd50));
+path.quadTo(SkBits2Float(0x41861a70), SkBits2Float(0x42231aba), SkBits2Float(0x418b90fc), SkBits2Float(0x42301322));
+path.quadTo(SkBits2Float(0x4191078a), SkBits2Float(0x423d0b88), SkBits2Float(0x41828cb4), SkBits2Float(0x424825e0));
+path.quadTo(SkBits2Float(0x416823bc), SkBits2Float(0x42534038), SkBits2Float(0x41344220), SkBits2Float(0x4255fb7e));
+path.quadTo(SkBits2Float(0x41006082), SkBits2Float(0x4258b6c5), SkBits2Float(0x40a7ee48), SkBits2Float(0x4251795a));
+path.quadTo(SkBits2Float(0x401e3718), SkBits2Float(0x424a3bef), SkBits2Float(0x3fe50570), SkBits2Float(0x423d4388));
+path.quadTo(SkBits2Float(0x3f8d9c90), SkBits2Float(0x42304b20), SkBits2Float(0x403aa4f8), SkBits2Float(0x422530c9));
+path.quadTo(SkBits2Float(0x4059e097), SkBits2Float(0x4222326b), SkBits2Float(0x407fc660), SkBits2Float(0x421fcfdc));
+path.lineTo(SkBits2Float(0x407fc672), SkBits2Float(0x421fcfdb));
+path.quadTo(SkBits2Float(0x409c2918), SkBits2Float(0x421dbc1a), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
+path.close();
+path.moveTo(SkBits2Float(0xc23fe7ce), SkBits2Float(0xc1ad1dad));
+path.quadTo(SkBits2Float(0xc2341588), SkBits2Float(0xc1b91b57), SkBits2Float(0xc2277c56), SkBits2Float(0xc1b0de1d));
+path.quadTo(SkBits2Float(0xc21ae326), SkBits2Float(0xc1a8a0e6), SkBits2Float(0xc214e451), SkBits2Float(0xc190fc58));
+path.quadTo(SkBits2Float(0xc20ee57c), SkBits2Float(0xc172af93), SkBits2Float(0xc2130419), SkBits2Float(0xc1404ad0));
+path.quadTo(SkBits2Float(0xc21722b5), SkBits2Float(0xc10de60c), SkBits2Float(0xc222f4fc), SkBits2Float(0xc0ebd570));
+path.quadTo(SkBits2Float(0xc22ec743), SkBits2Float(0xc0bbdec8), SkBits2Float(0xc23b6074), SkBits2Float(0xc0dcd3b4));
+path.quadTo(SkBits2Float(0xc247f9a4), SkBits2Float(0xc0fdc894), SkBits2Float(0xc24df87a), SkBits2Float(0xc12e2d66));
+path.quadTo(SkBits2Float(0xc253f74e), SkBits2Float(0xc15d7682), SkBits2Float(0xc24fd8b1), SkBits2Float(0xc187eda2));
+path.quadTo(SkBits2Float(0xc24bba16), SkBits2Float(0xc1a12003), SkBits2Float(0xc23fe7ce), SkBits2Float(0xc1ad1dad));
+path.close();
+path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));
+path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
+path.quadTo(SkBits2Float(0xc211f9a2), SkBits2Float(0x41535866), SkBits2Float(0xc2189a40), SkBits2Float(0x4180a176));
+path.quadTo(SkBits2Float(0xc21f3ade), SkBits2Float(0x419796b9), SkBits2Float(0xc22c087c), SkBits2Float(0x419e7330));
+path.quadTo(SkBits2Float(0xc238d61a), SkBits2Float(0x41a54fa9), SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));
+path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
+path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));
+path.quadTo(SkBits2Float(0xc2496698), SkBits2Float(0x40a6b0cc), SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
+path.close();
+path.moveTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x41828cb4), SkBits2Float(0x424825e0));
+path.quadTo(SkBits2Float(0x416823bc), SkBits2Float(0x42534038), SkBits2Float(0x41344220), SkBits2Float(0x4255fb7e));
+path.quadTo(SkBits2Float(0x41006082), SkBits2Float(0x4258b6c5), SkBits2Float(0x40a7ee48), SkBits2Float(0x4251795a));
+path.quadTo(SkBits2Float(0x401e3718), SkBits2Float(0x424a3bef), SkBits2Float(0x3fe50570), SkBits2Float(0x423d4388));
+path.quadTo(SkBits2Float(0x3f8d9c90), SkBits2Float(0x42304b20), SkBits2Float(0x403aa4f8), SkBits2Float(0x422530c9));
+path.quadTo(SkBits2Float(0x40973dd4), SkBits2Float(0x421a1671), SkBits2Float(0x40ff010e), SkBits2Float(0x42175b2b));
+path.quadTo(SkBits2Float(0x41336223), SkBits2Float(0x42149fe4), SkBits2Float(0x415fcb81), SkBits2Float(0x421bdd50));
+path.quadTo(SkBits2Float(0x41861a70), SkBits2Float(0x42231aba), SkBits2Float(0x418b90fc), SkBits2Float(0x42301322));
+path.quadTo(SkBits2Float(0x4191078a), SkBits2Float(0x423d0b88), SkBits2Float(0x41828cb4), SkBits2Float(0x424825e0));
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_10022998(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x40f23d54), SkBits2Float(0xc250558c));  // 7.56999f, -52.0835f
+path.quadTo(SkBits2Float(0x4126c646), SkBits2Float(0xc25712d1), SkBits2Float(0x415a1e76), SkBits2Float(0xc253c4aa));  // 10.4234f, -53.7684f, 13.6324f, -52.9421f
+path.quadTo(SkBits2Float(0x4186bb52), SkBits2Float(0xc2507686), SkBits2Float(0x419435db), SkBits2Float(0xc2450c9e));  // 16.8415f, -52.1157f, 18.5263f, -49.2623f
+path.quadTo(SkBits2Float(0x41a1b065), SkBits2Float(0xc239a2b8), SkBits2Float(0x419b1418), SkBits2Float(0xc22cccac));  // 20.2111f, -46.4089f, 19.3848f, -43.1999f
+path.quadTo(SkBits2Float(0x419477ce), SkBits2Float(0xc21ff6a0), SkBits2Float(0x417b47ff), SkBits2Float(0xc219395c));  // 18.5585f, -39.9908f, 15.7051f, -38.306f
+path.quadTo(SkBits2Float(0x414da063), SkBits2Float(0xc2127c16), SkBits2Float(0x411a4835), SkBits2Float(0xc215ca3d));  // 12.8517f, -36.6212f, 9.64263f, -37.4475f
+path.quadTo(SkBits2Float(0x40cde00c), SkBits2Float(0xc2191862), SkBits2Float(0x4097f5e4), SkBits2Float(0xc2248249));  // 6.4336f, -38.2738f, 4.74877f, -41.1272f
+path.quadTo(SkBits2Float(0x40441780), SkBits2Float(0xc22fec30), SkBits2Float(0x4078f9e8), SkBits2Float(0xc23cc23c));  // 3.06393f, -43.9807f, 3.89025f, -47.1897f
+path.quadTo(SkBits2Float(0x4096ee1c), SkBits2Float(0xc2499848), SkBits2Float(0x40f23d54), SkBits2Float(0xc250558c));  // 4.71657f, -50.3987f, 7.56999f, -52.0835f
+path.close();
+path.moveTo(SkBits2Float(0xc2066415), SkBits2Float(0xc2220be8));  // -33.5977f, -40.5116f
+path.quadTo(SkBits2Float(0xc1f2466b), SkBits2Float(0xc2223dac), SkBits2Float(0xc1df41cc), SkBits2Float(0xc21901bc));  // -30.2844f, -40.5602f, -27.9071f, -38.2517f
+path.quadTo(SkBits2Float(0xc1cc3d2d), SkBits2Float(0xc20fc5cd), SkBits2Float(0xc1cbd9a6), SkBits2Float(0xc20284ee));  // -25.5299f, -35.9432f, -25.4813f, -32.6298f
+path.quadTo(SkBits2Float(0xc1cb761f), SkBits2Float(0xc1ea881c), SkBits2Float(0xc1ddedfe), SkBits2Float(0xc1d7837e));  // -25.4327f, -29.3165f, -27.7412f, -26.9392f
+path.quadTo(SkBits2Float(0xc1f065dc), SkBits2Float(0xc1c47edf), SkBits2Float(0xc20573ce), SkBits2Float(0xc1c41b58));  // -30.0497f, -24.5619f, -33.3631f, -24.5134f
+path.quadTo(SkBits2Float(0xc212b4ad), SkBits2Float(0xc1c3b7d1), SkBits2Float(0xc21c36fc), SkBits2Float(0xc1d62fb0));  // -36.6764f, -24.4648f, -39.0537f, -26.7733f
+path.quadTo(SkBits2Float(0xc225b94c), SkBits2Float(0xc1e8a78d), SkBits2Float(0xc225eb10), SkBits2Float(0xc20194a7));  // -41.431f, -29.0818f, -41.4796f, -32.3952f
+path.quadTo(SkBits2Float(0xc2261cd3), SkBits2Float(0xc20ed586), SkBits2Float(0xc21ce0e3), SkBits2Float(0xc21857d5));  // -41.5281f, -35.7085f, -39.2196f, -38.0858f
+path.quadTo(SkBits2Float(0xc213a4f4), SkBits2Float(0xc221da25), SkBits2Float(0xc2066415), SkBits2Float(0xc2220be8));  // -36.9111f, -40.463f, -33.5977f, -40.5116f
+path.close();
+path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -19.799f, -19.799f
+path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));  // -11.598f, -28, 0, -28
+path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));  // 11.598f, -28, 19.799f, -19.799f
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0xc1399153), SkBits2Float(0x41e00000), SkBits2Float(0x00000000));  // 28, -11.598f, 28, 0
+path.quadTo(SkBits2Float(0x41e00000), SkBits2Float(0x41399153), SkBits2Float(0x419e6455), SkBits2Float(0x419e6455));  // 28, 11.598f, 19.799f, 19.799f
+path.quadTo(SkBits2Float(0x415b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0x40b878fc), SkBits2Float(0x41db9f74));  // 13.7163f, 25.8817f, 5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0x41000000), SkBits2Float(0x42040000));  // 8, 29.7635f, 8, 33
+path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));  // 8, 36.3137f, 5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));  // 3.31371f, 41, 0, 41
+path.quadTo(SkBits2Float(0xc05413cd), SkBits2Float(0x42240000), SkBits2Float(0xc0b504f3), SkBits2Float(0x421aa09e));  // -3.31371f, 41, -5.65685f, 38.6569f
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x4211413d), SkBits2Float(0xc1000000), SkBits2Float(0x42040000));  // -8, 36.3137f, -8, 33
+path.quadTo(SkBits2Float(0xc1000000), SkBits2Float(0x41ee1ba4), SkBits2Float(0xc0b878fc), SkBits2Float(0x41db9f74));  // -8, 29.7635f, -5.76477f, 27.4529f
+path.quadTo(SkBits2Float(0xc15b75ce), SkBits2Float(0x41cf0dc3), SkBits2Float(0xc19e6455), SkBits2Float(0x419e6455));  // -13.7163f, 25.8817f, -19.799f, 19.799f
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));  // -28, 11.598f, -28, 0
+path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));  // -28, -11.598f, -19.799f, -19.799f
+path.close();
+path.moveTo(SkBits2Float(0xc23c6b1a), SkBits2Float(0x4099fd84));  // -47.1046f, 4.8122f
+path.quadTo(SkBits2Float(0xc22fa63c), SkBits2Float(0x407b1738), SkBits2Float(0xc2241b28), SkBits2Float(0x40b1aa10));  // -43.9123f, 3.92329f, -41.0265f, 5.55201f
+path.quadTo(SkBits2Float(0xc2189014), SkBits2Float(0x40e5c886), SkBits2Float(0xc21501d7), SkBits2Float(0x4125f7c0));  // -38.1407f, 7.18073f, -37.2518f, 10.373f
+path.quadTo(SkBits2Float(0xc211739a), SkBits2Float(0x41590b3e), SkBits2Float(0xc217f769), SkBits2Float(0x41839bc7));  // -36.3629f, 13.5652f, -37.9916f, 16.4511f
+path.quadTo(SkBits2Float(0xc21e7b38), SkBits2Float(0x419ab1ef), SkBits2Float(0xc22b4016), SkBits2Float(0x41a1ce67));  // -39.6203f, 19.3369f, -42.8126f, 20.2258f
+path.quadTo(SkBits2Float(0xc23804f6), SkBits2Float(0x41a8eae0), SkBits2Float(0xc243900a), SkBits2Float(0x419be343));  // -46.0048f, 21.1147f, -48.8907f, 19.486f
+path.quadTo(SkBits2Float(0xc24f1b1e), SkBits2Float(0x418edba6), SkBits2Float(0xc252a95a), SkBits2Float(0x416aa3d0));  // -51.7765f, 17.8573f, -52.6654f, 14.665f
+path.quadTo(SkBits2Float(0xc2563797), SkBits2Float(0x41379054), SkBits2Float(0xc24fb3c8), SkBits2Float(0x41096404));  // -53.5543f, 11.4727f, -51.9256f, 8.58692f
+path.quadTo(SkBits2Float(0xc2492ffa), SkBits2Float(0x40b66f68), SkBits2Float(0xc23c6b1a), SkBits2Float(0x4099fd84));  // -50.2969f, 5.7011f, -47.1046f, 4.8122f
+path.close();
+path.moveTo(SkBits2Float(0x4204f8ac), SkBits2Float(0x41c568cb));  // 33.2428f, 24.6762f
+path.quadTo(SkBits2Float(0x421239c0), SkBits2Float(0x41c52671), SkBits2Float(0x421bb079), SkBits2Float(0x41d7b610));  // 36.5564f, 24.6438f, 38.9223f, 26.9639f
+path.quadTo(SkBits2Float(0x42252732), SkBits2Float(0x41ea45ad), SkBits2Float(0x4225485f), SkBits2Float(0x420263e9));  // 41.2883f, 29.284f, 41.3207f, 32.5976f
+path.quadTo(SkBits2Float(0x4225698c), SkBits2Float(0x420fa4fd), SkBits2Float(0x421c21bd), SkBits2Float(0x42191bb7));  // 41.3531f, 35.9111f, 39.0329f, 38.2771f
+path.quadTo(SkBits2Float(0x421bf1ce), SkBits2Float(0x42194c98), SkBits2Float(0x421bc136), SkBits2Float(0x42197cbe));  // 38.9861f, 38.3248f, 38.9387f, 38.3718f
+path.quadTo(SkBits2Float(0x421b91ce), SkBits2Float(0x4219ae03), SkBits2Float(0x421b61a7), SkBits2Float(0x4219dea3));  // 38.8924f, 38.4199f, 38.8454f, 38.4674f
+path.quadTo(SkBits2Float(0x421b3158), SkBits2Float(0x421a0f6b), SkBits2Float(0x421b00a6), SkBits2Float(0x421a3f33));  // 38.7982f, 38.5151f, 38.7506f, 38.5617f
+path.quadTo(SkBits2Float(0x421ad122), SkBits2Float(0x421a701a), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));  // 38.7042f, 38.6095f, 38.6569f, 38.6569f
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));  // 36.3137f, 41, 33, 41
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));  // 29.6863f, 41, 27.3431f, 38.6569f
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));  // 25, 36.3137f, 25, 33
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 25, 29.6863f, 27.3431f, 27.3431f
+path.quadTo(SkBits2Float(0x41db0360), SkBits2Float(0x41da7a26), SkBits2Float(0x41db4867), SkBits2Float(0x41da3682));  // 27.3766f, 27.3096f, 27.4104f, 27.2766f
+path.quadTo(SkBits2Float(0x41db8ba6), SkBits2Float(0x41d9f135), SkBits2Float(0x41dbcfd8), SkBits2Float(0x41d9ac58));  // 27.4432f, 27.2428f, 27.4765f, 27.2092f
+path.quadTo(SkBits2Float(0x41dc13ed), SkBits2Float(0x41d96798), SkBits2Float(0x41dc58ba), SkBits2Float(0x41d92383));  // 27.5097f, 27.1756f, 27.5433f, 27.1423f
+path.quadTo(SkBits2Float(0x41dc9bcc), SkBits2Float(0x41d8ddb7), SkBits2Float(0x41dcdf94), SkBits2Float(0x41d89898));  // 27.5761f, 27.1083f, 27.6092f, 27.0745f
+path.quadTo(SkBits2Float(0x41ef6f31), SkBits2Float(0x41c5ab25), SkBits2Float(0x4204f8ac), SkBits2Float(0x41c568cb));  // 29.9293f, 24.7086f, 33.2428f, 24.6762f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000));  // 41, 33
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));  // 41, 36.3137f, 38.6569f, 38.6569f
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));  // 36.3137f, 41, 33, 41
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));  // 29.6863f, 41, 27.3431f, 38.6569f
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));  // 25, 36.3137f, 25, 33
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));  // 25, 29.6863f, 27.3431f, 27.3431f
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));  // 29.6863f, 25, 33, 25
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));  // 36.3137f, 25, 38.6569f, 27.3431f
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));  // 41, 29.6863f, 41, 33
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = fuzz763_1026368;
+static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
+
+static struct TestDesc tests[] = {
+    TEST(fuzz763_10022998),
+    TEST(fuzz763_2674194),
+    TEST(fuzz763_5485218),
+    TEST(fuzz763_1026368),
+    TEST(fuzz763_3283699),
+    TEST(fuzz763_6411089),
+    TEST(fuzz763_4628016),
+    TEST(fuzz763_2211264),
+    TEST(fuzz763_34974),
+    TEST(fuzz763_1597464),
+    TEST(fuzz763_849020),
+    TEST(fuzz763_24588),
+    TEST(fuzz763_20016),
+    TEST(fuzz763_17370),
+    TEST(fuzz763_35322),
+    TEST(fuzz763_8712),
+    TEST(fuzz763_8712a),
+    TEST(fuzz763_4713),
+    TEST(fuzz763_4014),
+    TEST(fuzz763_4014a),
+    TEST(fuzz763_1404),
+    TEST(fuzz763_378),
+    TEST(fuzz763_378b),
+    TEST(fuzz763_378d),
+    TEST(fuzz763_378c),
+    TEST(fuzz763_3084),
+    TEST(fuzz763_1823),
+    TEST(fuzz763_558),
+    TEST(fuzz763_378a),
+    TEST(fuzz763_378a_1),
+};
+
+static const size_t testCount = SK_ARRAY_COUNT(tests);
+
+static bool runReverse = false;
+
+DEF_TEST(PathOpsFuzz763, reporter) {
+#if DEBUG_SHOW_TEST_NAME
+    strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
+#endif
+    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
+}
diff --git a/src/third_party/skia/tests/PathOpsInverseTest.cpp b/src/third_party/skia/tests/PathOpsInverseTest.cpp
index 23dbd1f..929851e 100644
--- a/src/third_party/skia/tests/PathOpsInverseTest.cpp
+++ b/src/third_party/skia/tests/PathOpsInverseTest.cpp
@@ -8,7 +8,7 @@
 
 DEF_TEST(PathOpsInverse, reporter) {
     SkPath one, two;
-    for (int op = kDifference_PathOp; op <= kReverseDifference_PathOp; ++op) {
+    for (int op = kDifference_SkPathOp; op <= kReverseDifference_SkPathOp; ++op) {
         for (int oneFill = SkPath::kWinding_FillType; oneFill <= SkPath::kInverseEvenOdd_FillType;
                     ++oneFill) {
             for (int oneDir = SkPath::kCW_Direction; oneDir != SkPath::kCCW_Direction; ++oneDir) {
diff --git a/src/third_party/skia/tests/PathOpsIssue3651.cpp b/src/third_party/skia/tests/PathOpsIssue3651.cpp
new file mode 100644
index 0000000..bd4ed2d
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsIssue3651.cpp
@@ -0,0 +1,1686 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsExtendedTest.h"
+#include "PathOpsTestCommon.h"
+
+#define TEST(name) { name, #name }
+
+static SkPath path1() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333));  // 157.5f, 65.1f
+path.lineTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333));  // 157.5f, 65.1f
+path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42823333), SkBits2Float(0x431ee666), SkBits2Float(0x4282368d), SkBits2Float(0x431f999a), SkBits2Float(0x42823333));  // 158.2f, 65.1f, 158.9f, 65.1065f, 159.6f, 65.1f
+path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42822fd9), SkBits2Float(0x43210000), SkBits2Float(0x42822861), SkBits2Float(0x4321b333), SkBits2Float(0x42821f17));  // 160.3f, 65.0935f, 161, 65.0789f, 161.7f, 65.0607f
+path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x428215ce), SkBits2Float(0x4323199a), SkBits2Float(0x4282071d), SkBits2Float(0x4323cccd), SkBits2Float(0x4281fb7b));  // 162.4f, 65.0426f, 163.1f, 65.0139f, 163.8f, 64.9912f
+path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x4281efd8), SkBits2Float(0x43253333), SkBits2Float(0x4281e467), SkBits2Float(0x4325e666), SkBits2Float(0x4281d94a));  // 164.5f, 64.9684f, 165.2f, 64.9461f, 165.9f, 64.9244f
+path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x4281ce2c), SkBits2Float(0x43274ccd), SkBits2Float(0x4281c15d), SkBits2Float(0x43280000), SkBits2Float(0x4281b8cb));  // 166.6f, 64.9027f, 167.3f, 64.8777f, 168, 64.8609f
+path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x4281b039), SkBits2Float(0x43296666), SkBits2Float(0x4281a66d), SkBits2Float(0x432a199a), SkBits2Float(0x4281a5dd));  // 168.7f, 64.8442f, 169.4f, 64.8251f, 170.1f, 64.824f
+path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x4281a54c), SkBits2Float(0x432b8000), SkBits2Float(0x4281aecf), SkBits2Float(0x432c3333), SkBits2Float(0x4281b566));  // 170.8f, 64.8228f, 171.5f, 64.8414f, 172.2f, 64.8543f
+path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x4281bbfe), SkBits2Float(0x432d999a), SkBits2Float(0x4281c612), SkBits2Float(0x432e4ccd), SkBits2Float(0x4281cd6b));  // 172.9f, 64.8672f, 173.6f, 64.8869f, 174.3f, 64.9012f
+path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x4281d4c4), SkBits2Float(0x432fb333), SkBits2Float(0x4281dc73), SkBits2Float(0x43306666), SkBits2Float(0x4281e17e));  // 175, 64.9156f, 175.7f, 64.9306f, 176.4f, 64.9404f
+path.cubicTo(SkBits2Float(0x4331199a), SkBits2Float(0x4281e688), SkBits2Float(0x4331cccd), SkBits2Float(0x4281e967), SkBits2Float(0x43328000), SkBits2Float(0x4281ebaa));  // 177.1f, 64.9503f, 177.8f, 64.9559f, 178.5f, 64.9603f
+path.cubicTo(SkBits2Float(0x43333333), SkBits2Float(0x4281eded), SkBits2Float(0x4333e666), SkBits2Float(0x4281eec6), SkBits2Float(0x4334999a), SkBits2Float(0x4281ef0f));  // 179.2f, 64.9647f, 179.9f, 64.9664f, 180.6f, 64.9669f
+path.cubicTo(SkBits2Float(0x43354ccd), SkBits2Float(0x4281ef57), SkBits2Float(0x43360000), SkBits2Float(0x4281eeba), SkBits2Float(0x4336b333), SkBits2Float(0x4281ed5c));  // 181.3f, 64.9675f, 182, 64.9663f, 182.7f, 64.9636f
+path.cubicTo(SkBits2Float(0x43376666), SkBits2Float(0x4281ebfe), SkBits2Float(0x4338199a), SkBits2Float(0x4281e8c9), SkBits2Float(0x4338cccd), SkBits2Float(0x4281e6db));  // 183.4f, 64.9609f, 184.1f, 64.9547f, 184.8f, 64.9509f
+path.cubicTo(SkBits2Float(0x43398000), SkBits2Float(0x4281e4ec), SkBits2Float(0x433a3333), SkBits2Float(0x4281e29d), SkBits2Float(0x433ae666), SkBits2Float(0x4281e1c4));  // 185.5f, 64.9471f, 186.2f, 64.9426f, 186.9f, 64.9409f
+path.cubicTo(SkBits2Float(0x433b999a), SkBits2Float(0x4281e0eb), SkBits2Float(0x433c4ccd), SkBits2Float(0x4281e188), SkBits2Float(0x433d0000), SkBits2Float(0x4281e1c4));  // 187.6f, 64.9393f, 188.3f, 64.9405f, 189, 64.9409f
+path.cubicTo(SkBits2Float(0x433db333), SkBits2Float(0x4281e201), SkBits2Float(0x433e6666), SkBits2Float(0x4281e415), SkBits2Float(0x433f199a), SkBits2Float(0x4281e330));  // 189.7f, 64.9414f, 190.4f, 64.9455f, 191.1f, 64.9437f
+path.cubicTo(SkBits2Float(0x433fcccd), SkBits2Float(0x4281e24b), SkBits2Float(0x43408000), SkBits2Float(0x4281df77), SkBits2Float(0x43413333), SkBits2Float(0x4281dc67));  // 191.8f, 64.942f, 192.5f, 64.9365f, 193.2f, 64.9305f
+path.cubicTo(SkBits2Float(0x4341e666), SkBits2Float(0x4281d957), SkBits2Float(0x4342999a), SkBits2Float(0x4281d35a), SkBits2Float(0x43434ccd), SkBits2Float(0x4281d0cf));  // 193.9f, 64.9245f, 194.6f, 64.9128f, 195.3f, 64.9078f
+path.cubicTo(SkBits2Float(0x43440000), SkBits2Float(0x4281ce44), SkBits2Float(0x4344b333), SkBits2Float(0x4281cd6c), SkBits2Float(0x43456666), SkBits2Float(0x4281cd24));  // 196, 64.9029f, 196.7f, 64.9012f, 197.4f, 64.9007f
+path.cubicTo(SkBits2Float(0x4346199a), SkBits2Float(0x4281ccdc), SkBits2Float(0x4346cccd), SkBits2Float(0x4281cf1d), SkBits2Float(0x43478000), SkBits2Float(0x4281cf1d));  // 198.1f, 64.9001f, 198.8f, 64.9045f, 199.5f, 64.9045f
+path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x4281cf1d), SkBits2Float(0x4348e666), SkBits2Float(0x4281ce8e), SkBits2Float(0x4349999a), SkBits2Float(0x4281cd24));  // 200.2f, 64.9045f, 200.9f, 64.9034f, 201.6f, 64.9007f
+path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x4281cbba), SkBits2Float(0x434b0000), SkBits2Float(0x4281c854), SkBits2Float(0x434bb333), SkBits2Float(0x4281c6a2));  // 202.3f, 64.8979f, 203, 64.8913f, 203.7f, 64.888f
+path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x4281c4f0), SkBits2Float(0x434d199a), SkBits2Float(0x4281c46d), SkBits2Float(0x434dcccd), SkBits2Float(0x4281c2f7));  // 204.4f, 64.8846f, 205.1f, 64.8836f, 205.8f, 64.8808f
+path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x4281c182), SkBits2Float(0x434f3333), SkBits2Float(0x4281bf4b), SkBits2Float(0x434fe666), SkBits2Float(0x4281bde1));  // 206.5f, 64.8779f, 207.2f, 64.8736f, 207.9f, 64.8709f
+path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x4281bc77), SkBits2Float(0x43514ccd), SkBits2Float(0x4281bb92), SkBits2Float(0x43520000), SkBits2Float(0x4281ba7d));  // 208.6f, 64.8681f, 209.3f, 64.8663f, 210, 64.8642f
+path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x4281b967), SkBits2Float(0x43536666), SkBits2Float(0x4281b95a), SkBits2Float(0x4354199a), SkBits2Float(0x4281b75f));  // 210.7f, 64.8621f, 211.4f, 64.862f, 212.1f, 64.8581f
+path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x4281b565), SkBits2Float(0x43558000), SkBits2Float(0x4281b0a4), SkBits2Float(0x43563333), SkBits2Float(0x4281ae9e));  // 212.8f, 64.8543f, 213.5f, 64.845f, 214.2f, 64.841f
+path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x4281ac98), SkBits2Float(0x4357999a), SkBits2Float(0x4281aca3), SkBits2Float(0x43584ccd), SkBits2Float(0x4281ab3a));  // 214.9f, 64.8371f, 215.6f, 64.8372f, 216.3f, 64.8344f
+path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x4281a9d0), SkBits2Float(0x4359b333), SkBits2Float(0x4281a82a), SkBits2Float(0x435a6666), SkBits2Float(0x4281a623));  // 217, 64.8317f, 217.7f, 64.8284f, 218.4f, 64.8245f
+path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x4281a41d), SkBits2Float(0x435bcccd), SkBits2Float(0x4281a157), SkBits2Float(0x435c8000), SkBits2Float(0x42819f14));  // 219.1f, 64.8205f, 219.8f, 64.8151f, 220.5f, 64.8107f
+path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42819cd1), SkBits2Float(0x435de666), SkBits2Float(0x42819a39), SkBits2Float(0x435e999a), SkBits2Float(0x42819892));  // 221.2f, 64.8063f, 221.9f, 64.8012f, 222.6f, 64.798f
+path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x428196ec), SkBits2Float(0x43600000), SkBits2Float(0x42819455), SkBits2Float(0x4360b333), SkBits2Float(0x4281952e));  // 223.3f, 64.7948f, 224, 64.7897f, 224.7f, 64.7914f
+path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42819607), SkBits2Float(0x4362199a), SkBits2Float(0x428198e7), SkBits2Float(0x4362cccd), SkBits2Float(0x42819da9));  // 225.4f, 64.793f, 226.1f, 64.7986f, 226.8f, 64.8079f
+path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x4281a26b), SkBits2Float(0x43643333), SkBits2Float(0x4281ad8a), SkBits2Float(0x4364e666), SkBits2Float(0x4281b1bc));  // 227.5f, 64.8172f, 228.2f, 64.8389f, 228.9f, 64.8471f
+path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x4281b5ed), SkBits2Float(0x43664ccd), SkBits2Float(0x4281b70f), SkBits2Float(0x43670000), SkBits2Float(0x4281b6d2));  // 229.6f, 64.8553f, 230.3f, 64.8575f, 231, 64.8571f
+path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x4281b695), SkBits2Float(0x43686666), SkBits2Float(0x4281b2db), SkBits2Float(0x4369199a), SkBits2Float(0x4281b050));  // 231.7f, 64.8566f, 232.4f, 64.8493f, 233.1f, 64.8444f
+path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x4281adc5), SkBits2Float(0x436a8000), SkBits2Float(0x4281a9e9), SkBits2Float(0x436b3333), SkBits2Float(0x4281a78f));  // 233.8f, 64.8394f, 234.5f, 64.8319f, 235.2f, 64.8273f
+path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x4281a535), SkBits2Float(0x436c999a), SkBits2Float(0x4281a55a), SkBits2Float(0x436d4ccd), SkBits2Float(0x4281a232));  // 235.9f, 64.8227f, 236.6f, 64.823f, 237.3f, 64.8168f
+path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42819f0a), SkBits2Float(0x436eb333), SkBits2Float(0x42819ad9), SkBits2Float(0x436f6666), SkBits2Float(0x428194a1));  // 238, 64.8106f, 238.7f, 64.8024f, 239.4f, 64.7903f
+path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42818e69), SkBits2Float(0x4370cccd), SkBits2Float(0x4281843c), SkBits2Float(0x43718000), SkBits2Float(0x42817ce3));  // 240.1f, 64.7781f, 240.8f, 64.7583f, 241.5f, 64.7439f
+path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x4281758a), SkBits2Float(0x4372e666), SkBits2Float(0x42816c36), SkBits2Float(0x4373999a), SkBits2Float(0x4281688a));  // 242.2f, 64.7296f, 242.9f, 64.7113f, 243.6f, 64.7042f
+path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x428164dd), SkBits2Float(0x43750000), SkBits2Float(0x428167a5), SkBits2Float(0x4375b333), SkBits2Float(0x428166d8));  // 244.3f, 64.697f, 245, 64.7024f, 245.7f, 64.7009f
+path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x4281660a), SkBits2Float(0x4377199a), SkBits2Float(0x42816651), SkBits2Float(0x4377cccd), SkBits2Float(0x428163ba));  // 246.4f, 64.6993f, 247.1f, 64.6998f, 247.8f, 64.6948f
+path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42816123), SkBits2Float(0x43793333), SkBits2Float(0x42815b5b), SkBits2Float(0x4379e666), SkBits2Float(0x4281574e));  // 248.5f, 64.6897f, 249.2f, 64.6784f, 249.9f, 64.6705f
+path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42815342), SkBits2Float(0x437b4ccd), SkBits2Float(0x42814fad), SkBits2Float(0x437c0000), SkBits2Float(0x42814b6f));  // 250.6f, 64.6626f, 251.3f, 64.6556f, 252, 64.6473f
+path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42814732), SkBits2Float(0x437d6666), SkBits2Float(0x42813eb7), SkBits2Float(0x437e199a), SkBits2Float(0x42813dde));  // 252.7f, 64.6391f, 253.4f, 64.6225f, 254.1f, 64.6208f
+path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42813d05), SkBits2Float(0x437f8000), SkBits2Float(0x428137d7), SkBits2Float(0x4380199a), SkBits2Float(0x42814659));  // 254.8f, 64.6192f, 255.5f, 64.6091f, 256.2f, 64.6374f
+path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x428154da), SkBits2Float(0x4380cccd), SkBits2Float(0x42817565), SkBits2Float(0x43812666), SkBits2Float(0x428194e8));  // 256.9f, 64.6657f, 257.6f, 64.7293f, 258.3f, 64.7908f
+path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x4281b46a), SkBits2Float(0x4381d99a), SkBits2Float(0x4281e906), SkBits2Float(0x43823333), SkBits2Float(0x42820368));  // 259, 64.8524f, 259.7f, 64.9551f, 260.4f, 65.0067f
+path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42821dca), SkBits2Float(0x4382e666), SkBits2Float(0x42822b3c), SkBits2Float(0x43834000), SkBits2Float(0x42823333));  // 261.1f, 65.0582f, 261.8f, 65.0844f, 262.5f, 65.1f
+path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42823b2a), SkBits2Float(0x4383f333), SkBits2Float(0x42823333), SkBits2Float(0x43844ccd), SkBits2Float(0x42823333));  // 263.2f, 65.1156f, 263.9f, 65.1f, 264.6f, 65.1f
+path.lineTo(SkBits2Float(0x43844ccd), SkBits2Float(0x42823333));  // 264.6f, 65.1f
+path.lineTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333));  // 157.5f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333));  // 283.5f, 65.1f
+path.lineTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333));  // 283.5f, 65.1f
+path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x428230fb), SkBits2Float(0x438e7333), SkBits2Float(0x4282293a), SkBits2Float(0x438ecccd), SkBits2Float(0x428225e0));  // 284.2f, 65.0957f, 284.9f, 65.0805f, 285.6f, 65.074f
+path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42822286), SkBits2Float(0x438f8000), SkBits2Float(0x42821cde), SkBits2Float(0x438fd99a), SkBits2Float(0x42821f17));  // 286.3f, 65.0674f, 287, 65.0564f, 287.7f, 65.0607f
+path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42822150), SkBits2Float(0x43908ccd), SkBits2Float(0x42822fd9), SkBits2Float(0x4390e666), SkBits2Float(0x42823333));  // 288.4f, 65.0651f, 289.1f, 65.0935f, 289.8f, 65.1f
+path.lineTo(SkBits2Float(0x4390e666), SkBits2Float(0x42823333));  // 289.8f, 65.1f
+path.lineTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333));  // 283.5f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.cubicTo(SkBits2Float(0x4399a666), SkBits2Float(0x42823332), SkBits2Float(0x439a0000), SkBits2Float(0x42823842), SkBits2Float(0x439a599a), SkBits2Float(0x4282332a));  // 307.3f, 65.1f, 308, 65.1099f, 308.7f, 65.0999f
+path.cubicTo(SkBits2Float(0x439ab333), SkBits2Float(0x42822e12), SkBits2Float(0x439b0ccd), SkBits2Float(0x42821e94), SkBits2Float(0x439b6666), SkBits2Float(0x428214a4));  // 309.4f, 65.09f, 310.1f, 65.0597f, 310.8f, 65.0403f
+path.cubicTo(SkBits2Float(0x439bc000), SkBits2Float(0x42820ab4), SkBits2Float(0x439c199a), SkBits2Float(0x42820185), SkBits2Float(0x439c7333), SkBits2Float(0x4281f789));  // 311.5f, 65.0209f, 312.2f, 65.003f, 312.9f, 64.9835f
+path.cubicTo(SkBits2Float(0x439ccccd), SkBits2Float(0x4281ed8d), SkBits2Float(0x439d2666), SkBits2Float(0x4281e391), SkBits2Float(0x439d8000), SkBits2Float(0x4281d8bc));  // 313.6f, 64.964f, 314.3f, 64.9445f, 315, 64.9233f
+path.cubicTo(SkBits2Float(0x439dd99a), SkBits2Float(0x4281cde7), SkBits2Float(0x439e3333), SkBits2Float(0x4281c0c4), SkBits2Float(0x439e8ccd), SkBits2Float(0x4281b68b));  // 315.7f, 64.9022f, 316.4f, 64.8765f, 317.1f, 64.8565f
+path.cubicTo(SkBits2Float(0x439ee666), SkBits2Float(0x4281ac53), SkBits2Float(0x439f4000), SkBits2Float(0x4281a27a), SkBits2Float(0x439f999a), SkBits2Float(0x42819b69));  // 317.8f, 64.8366f, 318.5f, 64.8173f, 319.2f, 64.8035f
+path.cubicTo(SkBits2Float(0x439ff333), SkBits2Float(0x42819459), SkBits2Float(0x43a04ccd), SkBits2Float(0x42818f8b), SkBits2Float(0x43a0a666), SkBits2Float(0x42818c26));  // 319.9f, 64.7897f, 320.6f, 64.7804f, 321.3f, 64.7737f
+path.cubicTo(SkBits2Float(0x43a10000), SkBits2Float(0x428188c2), SkBits2Float(0x43a1599a), SkBits2Float(0x42818795), SkBits2Float(0x43a1b333), SkBits2Float(0x42818710));  // 322, 64.7671f, 322.7f, 64.7648f, 323.4f, 64.7638f
+path.cubicTo(SkBits2Float(0x43a20ccd), SkBits2Float(0x4281868b), SkBits2Float(0x43a26666), SkBits2Float(0x42818824), SkBits2Float(0x43a2c000), SkBits2Float(0x42818909));  // 324.1f, 64.7628f, 324.8f, 64.7659f, 325.5f, 64.7676f
+path.cubicTo(SkBits2Float(0x43a3199a), SkBits2Float(0x428189ee), SkBits2Float(0x43a37333), SkBits2Float(0x42818de2), SkBits2Float(0x43a3cccd), SkBits2Float(0x42818c6d));  // 326.2f, 64.7694f, 326.9f, 64.7771f, 327.6f, 64.7743f
+path.cubicTo(SkBits2Float(0x43a42666), SkBits2Float(0x42818af7), SkBits2Float(0x43a48000), SkBits2Float(0x428185be), SkBits2Float(0x43a4d99a), SkBits2Float(0x42818048));  // 328.3f, 64.7714f, 329, 64.7612f, 329.7f, 64.7505f
+path.cubicTo(SkBits2Float(0x43a53333), SkBits2Float(0x42817ad1), SkBits2Float(0x43a58ccd), SkBits2Float(0x42816e33), SkBits2Float(0x43a5e666), SkBits2Float(0x42816ba7));  // 330.4f, 64.7399f, 331.1f, 64.7152f, 331.8f, 64.7103f
+path.cubicTo(SkBits2Float(0x43a64000), SkBits2Float(0x4281691c), SkBits2Float(0x43a6999a), SkBits2Float(0x42816b46), SkBits2Float(0x43a6f333), SkBits2Float(0x42817104));  // 332.5f, 64.7053f, 333.2f, 64.7095f, 333.9f, 64.7207f
+path.cubicTo(SkBits2Float(0x43a74ccd), SkBits2Float(0x428176c3), SkBits2Float(0x43a7a666), SkBits2Float(0x42817fa9), SkBits2Float(0x43a80000), SkBits2Float(0x42818e1f));  // 334.6f, 64.732f, 335.3f, 64.7493f, 336, 64.7776f
+path.cubicTo(SkBits2Float(0x43a8599a), SkBits2Float(0x42819c95), SkBits2Float(0x43a8b333), SkBits2Float(0x4281b1ec), SkBits2Float(0x43a90ccd), SkBits2Float(0x4281c7c7));  // 336.7f, 64.8058f, 337.4f, 64.8475f, 338.1f, 64.8902f
+path.cubicTo(SkBits2Float(0x43a96666), SkBits2Float(0x4281dda2), SkBits2Float(0x43a9c000), SkBits2Float(0x428209cf), SkBits2Float(0x43aa199a), SkBits2Float(0x42821140));  // 338.8f, 64.9329f, 339.5f, 65.0192f, 340.2f, 65.0337f
+path.cubicTo(SkBits2Float(0x43aa7333), SkBits2Float(0x428218b0), SkBits2Float(0x43aacccd), SkBits2Float(0x42820dff), SkBits2Float(0x43ab2666), SkBits2Float(0x4281f46b));  // 340.9f, 65.0482f, 341.6f, 65.0273f, 342.3f, 64.9774f
+path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x4281dad8), SkBits2Float(0x43abd99a), SkBits2Float(0x42819956), SkBits2Float(0x43ac3333), SkBits2Float(0x428177cd));  // 343, 64.9274f, 343.7f, 64.7995f, 344.4f, 64.734f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42815644), SkBits2Float(0x43ace666), SkBits2Float(0x42813910), SkBits2Float(0x43ad4000), SkBits2Float(0x42812b37));  // 345.1f, 64.6685f, 345.8f, 64.6115f, 346.5f, 64.5844f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42811d5e), SkBits2Float(0x43adf333), SkBits2Float(0x42812394), SkBits2Float(0x43ae4ccd), SkBits2Float(0x428124b5));  // 347.2f, 64.5574f, 347.9f, 64.5695f, 348.6f, 64.5717f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x428125d6), SkBits2Float(0x43af0000), SkBits2Float(0x42812c1c), SkBits2Float(0x43af599a), SkBits2Float(0x428131ff));  // 349.3f, 64.5739f, 350, 64.5862f, 350.7f, 64.5976f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x428137e3), SkBits2Float(0x43b00ccd), SkBits2Float(0x4281417f), SkBits2Float(0x43b06666), SkBits2Float(0x4281480b));  // 351.4f, 64.6092f, 352.1f, 64.6279f, 352.8f, 64.6407f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42814e97), SkBits2Float(0x43b1199a), SkBits2Float(0x4281534c), SkBits2Float(0x43b17333), SkBits2Float(0x42815947));  // 353.5f, 64.6535f, 354.2f, 64.6627f, 354.9f, 64.6744f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42815f42), SkBits2Float(0x43b22666), SkBits2Float(0x428165ff), SkBits2Float(0x43b28000), SkBits2Float(0x42816bee));  // 355.6f, 64.6861f, 356.3f, 64.6992f, 357, 64.7108f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x428171de), SkBits2Float(0x43b33333), SkBits2Float(0x42817af5), SkBits2Float(0x43b38ccd), SkBits2Float(0x42817ce3));  // 357.7f, 64.7224f, 358.4f, 64.7402f, 359.1f, 64.7439f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42817ed2), SkBits2Float(0x43b44000), SkBits2Float(0x42817bcf), SkBits2Float(0x43b4999a), SkBits2Float(0x42817786));  // 359.8f, 64.7477f, 360.5f, 64.7418f, 361.2f, 64.7334f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x4281733d), SkBits2Float(0x43b54ccd), SkBits2Float(0x428167a7), SkBits2Float(0x43b5a666), SkBits2Float(0x4281632d));  // 361.9f, 64.7251f, 362.6f, 64.7024f, 363.3f, 64.6937f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42815eb3), SkBits2Float(0x43b6599a), SkBits2Float(0x42815b7e), SkBits2Float(0x43b6b333), SkBits2Float(0x42815cab));  // 364, 64.685f, 364.7f, 64.6787f, 365.4f, 64.681f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42815dd8), SkBits2Float(0x43b76666), SkBits2Float(0x4281644d), SkBits2Float(0x43b7c000), SkBits2Float(0x42816a3c));  // 366.1f, 64.6833f, 366.8f, 64.6959f, 367.5f, 64.7075f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x4281702b), SkBits2Float(0x43b87333), SkBits2Float(0x428179d3), SkBits2Float(0x43b8cccd), SkBits2Float(0x42818048));  // 368.2f, 64.7191f, 368.9f, 64.7379f, 369.6f, 64.7505f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x428186bc), SkBits2Float(0x43b98000), SkBits2Float(0x42818d4a), SkBits2Float(0x43b9d99a), SkBits2Float(0x428190f6));  // 370.3f, 64.7632f, 371, 64.776f, 371.7f, 64.7831f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x428194a3), SkBits2Float(0x43ba8ccd), SkBits2Float(0x428193b0), SkBits2Float(0x43bae666), SkBits2Float(0x42819653));  // 372.4f, 64.7903f, 373.1f, 64.7885f, 373.8f, 64.7936f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x428198f6), SkBits2Float(0x43bb999a), SkBits2Float(0x42819840), SkBits2Float(0x43bbf333), SkBits2Float(0x4281a0c6));  // 374.5f, 64.7988f, 375.2f, 64.7974f, 375.9f, 64.814f
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x4281a94d), SkBits2Float(0x43bca666), SkBits2Float(0x4281bc0d), SkBits2Float(0x43bd0000), SkBits2Float(0x4281c979));  // 376.6f, 64.8307f, 377.3f, 64.8673f, 378, 64.8935f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x4281d6e5), SkBits2Float(0x43bdb333), SkBits2Float(0x4281e6fe), SkBits2Float(0x43be0ccd), SkBits2Float(0x4281f14e));  // 378.7f, 64.9197f, 379.4f, 64.9512f, 380.1f, 64.9713f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x4281fb9e), SkBits2Float(0x43bec000), SkBits2Float(0x4281fd75), SkBits2Float(0x43bf199a), SkBits2Float(0x42820759));  // 380.8f, 64.9914f, 381.5f, 64.995f, 382.2f, 65.0144f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x4282113e), SkBits2Float(0x43bfcccd), SkBits2Float(0x42822559), SkBits2Float(0x43c02666), SkBits2Float(0x42822ca8));  // 382.9f, 65.0337f, 383.6f, 65.0729f, 384.3f, 65.0872f
+path.lineTo(SkBits2Float(0x43c02666), SkBits2Float(0x42823333));  // 384.3f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333));  // 388.5f, 65.1f
+path.lineTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333));  // 388.5f, 65.1f
+path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42823333), SkBits2Float(0x43c2f333), SkBits2Float(0x428239f1), SkBits2Float(0x43c34ccd), SkBits2Float(0x42823333));  // 389.2f, 65.1f, 389.9f, 65.1132f, 390.6f, 65.1f
+path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42822c75), SkBits2Float(0x43c40000), SkBits2Float(0x42822289), SkBits2Float(0x43c4599a), SkBits2Float(0x42820abe));  // 391.3f, 65.0868f, 392, 65.0675f, 392.7f, 65.021f
+path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x4281f2f3), SkBits2Float(0x43c50ccd), SkBits2Float(0x4281be4d), SkBits2Float(0x43c56666), SkBits2Float(0x4281a471));  // 393.4f, 64.9745f, 394.1f, 64.8717f, 394.8f, 64.8212f
+path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42818a96), SkBits2Float(0x43c6199a), SkBits2Float(0x428177e3), SkBits2Float(0x43c67333), SkBits2Float(0x42816f99));  // 395.5f, 64.7707f, 396.2f, 64.7342f, 396.9f, 64.718f
+path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x4281674f), SkBits2Float(0x43c72666), SkBits2Float(0x42817195), SkBits2Float(0x43c78000), SkBits2Float(0x428172b7));  // 397.6f, 64.7018f, 398.3f, 64.7218f, 399, 64.7241f
+path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x428173d8), SkBits2Float(0x43c83333), SkBits2Float(0x42817528), SkBits2Float(0x43c88ccd), SkBits2Float(0x42817661));  // 399.7f, 64.7263f, 400.4f, 64.7288f, 401.1f, 64.7312f
+path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x4281779a), SkBits2Float(0x43c94000), SkBits2Float(0x4281778d), SkBits2Float(0x43c9999a), SkBits2Float(0x42817a0c));  // 401.8f, 64.7336f, 402.5f, 64.7335f, 403.2f, 64.7384f
+path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42817c8c), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42817f49), SkBits2Float(0x43caa666), SkBits2Float(0x4281855e));  // 403.9f, 64.7433f, 404.6f, 64.7486f, 405.3f, 64.7605f
+path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42818b72), SkBits2Float(0x43cb599a), SkBits2Float(0x4281985b), SkBits2Float(0x43cbb333), SkBits2Float(0x42819e87));  // 406, 64.7724f, 406.7f, 64.7976f, 407.4f, 64.8096f
+path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x4281a4b3), SkBits2Float(0x43cc6666), SkBits2Float(0x4281a6ea), SkBits2Float(0x43ccc000), SkBits2Float(0x4281aa66));  // 408.1f, 64.8217f, 408.8f, 64.826f, 409.5f, 64.8328f
+path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x4281ade2), SkBits2Float(0x43cd7333), SkBits2Float(0x4281aad0), SkBits2Float(0x43cdcccd), SkBits2Float(0x4281b36e));  // 410.2f, 64.8396f, 410.9f, 64.8336f, 411.6f, 64.8504f
+path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x4281bc0c), SkBits2Float(0x43ce8000), SkBits2Float(0x4281d071), SkBits2Float(0x43ced99a), SkBits2Float(0x4281de19));  // 412.3f, 64.8673f, 413, 64.9071f, 413.7f, 64.9338f
+path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x4281ebc2), SkBits2Float(0x43cf8ccd), SkBits2Float(0x4281fb65), SkBits2Float(0x43cfe666), SkBits2Float(0x42820561));  // 414.4f, 64.9605f, 415.1f, 64.991f, 415.8f, 65.0105f
+path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42820f5d), SkBits2Float(0x43d0999a), SkBits2Float(0x428217a6), SkBits2Float(0x43d0f333), SkBits2Float(0x42821a01));  // 416.5f, 65.03f, 417.2f, 65.0462f, 417.9f, 65.0508f
+path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42821c5b), SkBits2Float(0x43d1a666), SkBits2Float(0x42821a47), SkBits2Float(0x43d20000), SkBits2Float(0x4282137f));  // 418.6f, 65.0554f, 419.3f, 65.0513f, 420, 65.0381f
+path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42820cb6), SkBits2Float(0x43d2b333), SkBits2Float(0x4281fcb3), SkBits2Float(0x43d30ccd), SkBits2Float(0x4281f14e));  // 420.7f, 65.0248f, 421.4f, 64.9936f, 422.1f, 64.9713f
+path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x4281e5e8), SkBits2Float(0x43d3c000), SkBits2Float(0x4281d645), SkBits2Float(0x43d4199a), SkBits2Float(0x4281cf1d));  // 422.8f, 64.949f, 423.5f, 64.9185f, 424.2f, 64.9045f
+path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x4281c7f4), SkBits2Float(0x43d4cccd), SkBits2Float(0x4281c5d7), SkBits2Float(0x43d52666), SkBits2Float(0x4281c65c));  // 424.9f, 64.8905f, 425.6f, 64.8864f, 426.3f, 64.8874f
+path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x4281c6e1), SkBits2Float(0x43d5d99a), SkBits2Float(0x4281d040), SkBits2Float(0x43d63333), SkBits2Float(0x4281d23a));  // 427, 64.8884f, 427.7f, 64.9067f, 428.4f, 64.9106f
+path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x4281d435), SkBits2Float(0x43d6e666), SkBits2Float(0x4281d7ed), SkBits2Float(0x43d74000), SkBits2Float(0x4281d23a));  // 429.1f, 64.9145f, 429.8f, 64.9217f, 430.5f, 64.9106f
+path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x4281cc88), SkBits2Float(0x43d7f333), SkBits2Float(0x4281ba4e), SkBits2Float(0x43d84ccd), SkBits2Float(0x4281b009));  // 431.2f, 64.8995f, 431.9f, 64.8639f, 432.6f, 64.8438f
+path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x4281a5c5), SkBits2Float(0x43d90000), SkBits2Float(0x4281997b), SkBits2Float(0x43d9599a), SkBits2Float(0x428194a1));  // 433.3f, 64.8238f, 434, 64.7998f, 434.7f, 64.7903f
+path.cubicTo(SkBits2Float(0x43d9b333), SkBits2Float(0x42818fc7), SkBits2Float(0x43da0ccd), SkBits2Float(0x4281929b), SkBits2Float(0x43da6666), SkBits2Float(0x428192ef));  // 435.4f, 64.7808f, 436.1f, 64.7863f, 436.8f, 64.787f
+path.cubicTo(SkBits2Float(0x43dac000), SkBits2Float(0x42819343), SkBits2Float(0x43db199a), SkBits2Float(0x428194dc), SkBits2Float(0x43db7333), SkBits2Float(0x4281969a));  // 437.5f, 64.7876f, 438.2f, 64.7907f, 438.9f, 64.7941f
+path.cubicTo(SkBits2Float(0x43dbcccd), SkBits2Float(0x42819858), SkBits2Float(0x43dc2666), SkBits2Float(0x42819925), SkBits2Float(0x43dc8000), SkBits2Float(0x42819d62));  // 439.6f, 64.7975f, 440.3f, 64.7991f, 441, 64.8074f
+path.cubicTo(SkBits2Float(0x43dcd99a), SkBits2Float(0x4281a19f), SkBits2Float(0x43dd3333), SkBits2Float(0x4281a9d2), SkBits2Float(0x43dd8ccd), SkBits2Float(0x4281b009));  // 441.7f, 64.8157f, 442.4f, 64.8317f, 443.1f, 64.8438f
+path.cubicTo(SkBits2Float(0x43dde666), SkBits2Float(0x4281b641), SkBits2Float(0x43de4000), SkBits2Float(0x4281be1f), SkBits2Float(0x43de999a), SkBits2Float(0x4281c2b1));  // 443.8f, 64.856f, 444.5f, 64.8713f, 445.2f, 64.8803f
+path.cubicTo(SkBits2Float(0x43def333), SkBits2Float(0x4281c742), SkBits2Float(0x43df4ccd), SkBits2Float(0x4281ca45), SkBits2Float(0x43dfa666), SkBits2Float(0x4281cb72));  // 445.9f, 64.8892f, 446.6f, 64.8951f, 447.3f, 64.8974f
+path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x4281cc9f), SkBits2Float(0x43e0599a), SkBits2Float(0x4281cb72), SkBits2Float(0x43e0b333), SkBits2Float(0x4281c9c0));  // 448, 64.8997f, 448.7f, 64.8974f, 449.4f, 64.894f
+path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x4281c80e), SkBits2Float(0x43e16666), SkBits2Float(0x4281c34c), SkBits2Float(0x43e1c000), SkBits2Float(0x4281c145));  // 450.1f, 64.8907f, 450.8f, 64.8814f, 451.5f, 64.8775f
+path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x4281bf3f), SkBits2Float(0x43e27333), SkBits2Float(0x4281c026), SkBits2Float(0x43e2cccd), SkBits2Float(0x4281bd9a));  // 452.2f, 64.8735f, 452.9f, 64.8753f, 453.6f, 64.8703f
+path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x4281bb0f), SkBits2Float(0x43e38000), SkBits2Float(0x4281b877), SkBits2Float(0x43e3d99a), SkBits2Float(0x4281b202));  // 454.3f, 64.8653f, 455, 64.8603f, 455.7f, 64.8477f
+path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x4281ab8e), SkBits2Float(0x43e48ccd), SkBits2Float(0x4281a1fe), SkBits2Float(0x43e4e666), SkBits2Float(0x428196e0));  // 456.4f, 64.8351f, 457.1f, 64.8164f, 457.8f, 64.7947f
+path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42818bc3), SkBits2Float(0x43e5999a), SkBits2Float(0x42817cb2), SkBits2Float(0x43e5f333), SkBits2Float(0x42816f52));  // 458.5f, 64.773f, 459.2f, 64.7435f, 459.9f, 64.7174f
+path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x428161f2), SkBits2Float(0x43e6a666), SkBits2Float(0x428151a5), SkBits2Float(0x43e70000), SkBits2Float(0x4281469f));  // 460.6f, 64.6913f, 461.3f, 64.6595f, 462, 64.6379f
+path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42813b9a), SkBits2Float(0x43e7b333), SkBits2Float(0x428132d7), SkBits2Float(0x43e80ccd), SkBits2Float(0x42812d30));  // 462.7f, 64.6164f, 463.4f, 64.5993f, 464.1f, 64.5883f
+path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42812789), SkBits2Float(0x43e8c000), SkBits2Float(0x428125d6), SkBits2Float(0x43e9199a), SkBits2Float(0x428124b5));  // 464.8f, 64.5772f, 465.5f, 64.5739f, 466.2f, 64.5717f
+path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42812394), SkBits2Float(0x43e9cccd), SkBits2Float(0x4281258e), SkBits2Float(0x43ea2666), SkBits2Float(0x42812667));  // 466.9f, 64.5695f, 467.6f, 64.5733f, 468.3f, 64.575f
+path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42812740), SkBits2Float(0x43ead99a), SkBits2Float(0x42812819), SkBits2Float(0x43eb3333), SkBits2Float(0x428129cb));  // 469, 64.5767f, 469.7f, 64.5783f, 470.4f, 64.5816f
+path.cubicTo(SkBits2Float(0x43eb8ccd), SkBits2Float(0x42812b7e), SkBits2Float(0x43ebe666), SkBits2Float(0x42812734), SkBits2Float(0x43ec4000), SkBits2Float(0x42813094));  // 471.1f, 64.5849f, 471.8f, 64.5766f, 472.5f, 64.5949f
+path.cubicTo(SkBits2Float(0x43ec999a), SkBits2Float(0x428139f3), SkBits2Float(0x43ecf333), SkBits2Float(0x42814838), SkBits2Float(0x43ed4ccd), SkBits2Float(0x42816208));  // 473.2f, 64.6132f, 473.9f, 64.6411f, 474.6f, 64.6915f
+path.cubicTo(SkBits2Float(0x43eda666), SkBits2Float(0x42817bd8), SkBits2Float(0x43ee0000), SkBits2Float(0x4281a8c8), SkBits2Float(0x43ee599a), SkBits2Float(0x4281cb72));  // 475.3f, 64.7419f, 476, 64.8297f, 476.7f, 64.8974f
+path.cubicTo(SkBits2Float(0x43eeb333), SkBits2Float(0x4281ee1c), SkBits2Float(0x43ef0ccd), SkBits2Float(0x428220ba), SkBits2Float(0x43ef6666), SkBits2Float(0x42823205));  // 477.4f, 64.9651f, 478.1f, 65.0639f, 478.8f, 65.0977f
+path.cubicTo(SkBits2Float(0x43efc000), SkBits2Float(0x42824350), SkBits2Float(0x43f0199a), SkBits2Float(0x42823301), SkBits2Float(0x43f07333), SkBits2Float(0x42823333));  // 479.5f, 65.1315f, 480.2f, 65.0996f, 480.9f, 65.1f
+path.lineTo(SkBits2Float(0x43f07333), SkBits2Float(0x42823333));  // 480.9f, 65.1f
+path.lineTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333));  // 388.5f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333));  // 504, 65.1f
+path.lineTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333));  // 504, 65.1f
+path.cubicTo(SkBits2Float(0x43fc599a), SkBits2Float(0x42823333), SkBits2Float(0x43fcb333), SkBits2Float(0x42823772), SkBits2Float(0x43fd0ccd), SkBits2Float(0x42823333));  // 504.7f, 65.1f, 505.4f, 65.1083f, 506.1f, 65.1f
+path.cubicTo(SkBits2Float(0x43fd6666), SkBits2Float(0x42822ef4), SkBits2Float(0x43fdc000), SkBits2Float(0x428227e9), SkBits2Float(0x43fe199a), SkBits2Float(0x428219ba));  // 506.8f, 65.0917f, 507.5f, 65.0779f, 508.2f, 65.0502f
+path.cubicTo(SkBits2Float(0x43fe7333), SkBits2Float(0x42820b8b), SkBits2Float(0x43fecccd), SkBits2Float(0x4281ebf3), SkBits2Float(0x43ff2666), SkBits2Float(0x4281de19));  // 508.9f, 65.0225f, 509.6f, 64.9608f, 510.3f, 64.9338f
+path.cubicTo(SkBits2Float(0x43ff8000), SkBits2Float(0x4281d040), SkBits2Float(0x43ffd99a), SkBits2Float(0x4281cbde), SkBits2Float(0x4400199a), SkBits2Float(0x4281c6a2));  // 511, 64.9067f, 511.7f, 64.8982f, 512.4f, 64.888f
+path.cubicTo(SkBits2Float(0x44004666), SkBits2Float(0x4281c167), SkBits2Float(0x44007333), SkBits2Float(0x4281bf82), SkBits2Float(0x4400a000), SkBits2Float(0x4281beb5));  // 513.1f, 64.8777f, 513.8f, 64.874f, 514.5f, 64.8725f
+path.cubicTo(SkBits2Float(0x4400cccd), SkBits2Float(0x4281bde8), SkBits2Float(0x4400f99a), SkBits2Float(0x4281bfe4), SkBits2Float(0x44012666), SkBits2Float(0x4281c1d2));  // 515.2f, 64.8709f, 515.9f, 64.8748f, 516.6f, 64.8786f
+path.cubicTo(SkBits2Float(0x44015333), SkBits2Float(0x4281c3c1), SkBits2Float(0x44018000), SkBits2Float(0x4281c822), SkBits2Float(0x4401accd), SkBits2Float(0x4281ca4d));  // 517.3f, 64.8823f, 518, 64.8909f, 518.7f, 64.8951f
+path.cubicTo(SkBits2Float(0x4401d99a), SkBits2Float(0x4281cc78), SkBits2Float(0x44020666), SkBits2Float(0x4281cf4e), SkBits2Float(0x44023333), SkBits2Float(0x4281ced6));  // 519.4f, 64.8994f, 520.1f, 64.9049f, 520.8f, 64.904f
+path.cubicTo(SkBits2Float(0x44026000), SkBits2Float(0x4281ce5f), SkBits2Float(0x44028ccd), SkBits2Float(0x4281cb81), SkBits2Float(0x4402b99a), SkBits2Float(0x4281c781));  // 521.5f, 64.9031f, 522.2f, 64.8975f, 522.9f, 64.8897f
+path.cubicTo(SkBits2Float(0x4402e666), SkBits2Float(0x4281c380), SkBits2Float(0x44031333), SkBits2Float(0x4281babb), SkBits2Float(0x44034000), SkBits2Float(0x4281b6d2));  // 523.6f, 64.8818f, 524.3f, 64.8647f, 525, 64.8571f
+path.cubicTo(SkBits2Float(0x44036ccd), SkBits2Float(0x4281b2e9), SkBits2Float(0x4403999a), SkBits2Float(0x4281b0a6), SkBits2Float(0x4403c666), SkBits2Float(0x4281b009));  // 525.7f, 64.8494f, 526.4f, 64.845f, 527.1f, 64.8438f
+path.cubicTo(SkBits2Float(0x4403f333), SkBits2Float(0x4281af6d), SkBits2Float(0x44042000), SkBits2Float(0x42819d4b), SkBits2Float(0x44044ccd), SkBits2Float(0x4281b327));  // 527.8f, 64.8426f, 528.5f, 64.8072f, 529.2f, 64.8499f
+path.cubicTo(SkBits2Float(0x4404799a), SkBits2Float(0x4281c903), SkBits2Float(0x4404a666), SkBits2Float(0x42821ddc), SkBits2Float(0x4404d333), SkBits2Float(0x42823333));  // 529.9f, 64.8926f, 530.6f, 65.0583f, 531.3f, 65.1f
+path.cubicTo(SkBits2Float(0x44050000), SkBits2Float(0x4282488b), SkBits2Float(0x44052ccd), SkBits2Float(0x42823333), SkBits2Float(0x4405599a), SkBits2Float(0x42823333));  // 532, 65.1417f, 532.7f, 65.1f, 533.4f, 65.1f
+path.lineTo(SkBits2Float(0x4405599a), SkBits2Float(0x42823333));  // 533.4f, 65.1f
+path.lineTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333));  // 504, 65.1f
+path.close();
+    return path;
+}
+
+static SkPath path2() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333));  // 157.5f, 65.1f
+path.lineTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333));  // 157.5f, 65.1f
+path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42823333), SkBits2Float(0x431ee666), SkBits2Float(0x42822fd9), SkBits2Float(0x431f999a), SkBits2Float(0x42823333));  // 158.2f, 65.1f, 158.9f, 65.0935f, 159.6f, 65.1f
+path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x4282368d), SkBits2Float(0x43210000), SkBits2Float(0x42823e05), SkBits2Float(0x4321b333), SkBits2Float(0x4282474f));  // 160.3f, 65.1065f, 161, 65.1211f, 161.7f, 65.1393f
+path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42825098), SkBits2Float(0x4323199a), SkBits2Float(0x42825f49), SkBits2Float(0x4323cccd), SkBits2Float(0x42826aeb));  // 162.4f, 65.1574f, 163.1f, 65.1861f, 163.8f, 65.2088f
+path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x4282768e), SkBits2Float(0x43253333), SkBits2Float(0x428281ff), SkBits2Float(0x4325e666), SkBits2Float(0x42828d1c));  // 164.5f, 65.2316f, 165.2f, 65.2539f, 165.9f, 65.2756f
+path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x4282983a), SkBits2Float(0x43274ccd), SkBits2Float(0x4282a509), SkBits2Float(0x43280000), SkBits2Float(0x4282ad9b));  // 166.6f, 65.2973f, 167.3f, 65.3223f, 168, 65.3391f
+path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x4282b62d), SkBits2Float(0x43296666), SkBits2Float(0x4282bff9), SkBits2Float(0x432a199a), SkBits2Float(0x4282c089));  // 168.7f, 65.3558f, 169.4f, 65.3749f, 170.1f, 65.376f
+path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x4282c11a), SkBits2Float(0x432b8000), SkBits2Float(0x4282b797), SkBits2Float(0x432c3333), SkBits2Float(0x4282b100));  // 170.8f, 65.3772f, 171.5f, 65.3586f, 172.2f, 65.3457f
+path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x4282aa68), SkBits2Float(0x432d999a), SkBits2Float(0x4282a054), SkBits2Float(0x432e4ccd), SkBits2Float(0x428298fb));  // 172.9f, 65.3328f, 173.6f, 65.3131f, 174.3f, 65.2988f
+path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x428291a2), SkBits2Float(0x432fb333), SkBits2Float(0x428289f3), SkBits2Float(0x43306666), SkBits2Float(0x428284e8));  // 175, 65.2844f, 175.7f, 65.2694f, 176.4f, 65.2596f
+path.cubicTo(SkBits2Float(0x4331199a), SkBits2Float(0x42827fde), SkBits2Float(0x4331cccd), SkBits2Float(0x42827cff), SkBits2Float(0x43328000), SkBits2Float(0x42827abc));  // 177.1f, 65.2497f, 177.8f, 65.2441f, 178.5f, 65.2397f
+path.cubicTo(SkBits2Float(0x43333333), SkBits2Float(0x42827879), SkBits2Float(0x4333e666), SkBits2Float(0x428277a0), SkBits2Float(0x4334999a), SkBits2Float(0x42827757));  // 179.2f, 65.2353f, 179.9f, 65.2336f, 180.6f, 65.2331f
+path.cubicTo(SkBits2Float(0x43354ccd), SkBits2Float(0x4282770f), SkBits2Float(0x43360000), SkBits2Float(0x428277ac), SkBits2Float(0x4336b333), SkBits2Float(0x4282790a));  // 181.3f, 65.2325f, 182, 65.2337f, 182.7f, 65.2364f
+path.cubicTo(SkBits2Float(0x43376666), SkBits2Float(0x42827a68), SkBits2Float(0x4338199a), SkBits2Float(0x42827d9d), SkBits2Float(0x4338cccd), SkBits2Float(0x42827f8b));  // 183.4f, 65.2391f, 184.1f, 65.2453f, 184.8f, 65.2491f
+path.cubicTo(SkBits2Float(0x43398000), SkBits2Float(0x4282817a), SkBits2Float(0x433a3333), SkBits2Float(0x428283c9), SkBits2Float(0x433ae666), SkBits2Float(0x428284a2));  // 185.5f, 65.2529f, 186.2f, 65.2574f, 186.9f, 65.259f
+path.cubicTo(SkBits2Float(0x433b999a), SkBits2Float(0x4282857b), SkBits2Float(0x433c4ccd), SkBits2Float(0x428284de), SkBits2Float(0x433d0000), SkBits2Float(0x428284a2));  // 187.6f, 65.2607f, 188.3f, 65.2595f, 189, 65.259f
+path.cubicTo(SkBits2Float(0x433db333), SkBits2Float(0x42828465), SkBits2Float(0x433e6666), SkBits2Float(0x42828251), SkBits2Float(0x433f199a), SkBits2Float(0x42828336));  // 189.7f, 65.2586f, 190.4f, 65.2545f, 191.1f, 65.2563f
+path.cubicTo(SkBits2Float(0x433fcccd), SkBits2Float(0x4282841b), SkBits2Float(0x43408000), SkBits2Float(0x428286ef), SkBits2Float(0x43413333), SkBits2Float(0x428289ff));  // 191.8f, 65.258f, 192.5f, 65.2635f, 193.2f, 65.2695f
+path.cubicTo(SkBits2Float(0x4341e666), SkBits2Float(0x42828d0f), SkBits2Float(0x4342999a), SkBits2Float(0x4282930c), SkBits2Float(0x43434ccd), SkBits2Float(0x42829597));  // 193.9f, 65.2755f, 194.6f, 65.2872f, 195.3f, 65.2922f
+path.cubicTo(SkBits2Float(0x43440000), SkBits2Float(0x42829822), SkBits2Float(0x4344b333), SkBits2Float(0x428298fa), SkBits2Float(0x43456666), SkBits2Float(0x42829942));  // 196, 65.2971f, 196.7f, 65.2988f, 197.4f, 65.2993f
+path.cubicTo(SkBits2Float(0x4346199a), SkBits2Float(0x4282998a), SkBits2Float(0x4346cccd), SkBits2Float(0x42829749), SkBits2Float(0x43478000), SkBits2Float(0x42829749));  // 198.1f, 65.2999f, 198.8f, 65.2955f, 199.5f, 65.2955f
+path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42829749), SkBits2Float(0x4348e666), SkBits2Float(0x428297d8), SkBits2Float(0x4349999a), SkBits2Float(0x42829942));  // 200.2f, 65.2955f, 200.9f, 65.2966f, 201.6f, 65.2993f
+path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42829aac), SkBits2Float(0x434b0000), SkBits2Float(0x42829e12), SkBits2Float(0x434bb333), SkBits2Float(0x42829fc4));  // 202.3f, 65.3021f, 203, 65.3087f, 203.7f, 65.312f
+path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x4282a176), SkBits2Float(0x434d199a), SkBits2Float(0x4282a1f9), SkBits2Float(0x434dcccd), SkBits2Float(0x4282a36f));  // 204.4f, 65.3154f, 205.1f, 65.3164f, 205.8f, 65.3192f
+path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x4282a4e4), SkBits2Float(0x434f3333), SkBits2Float(0x4282a71b), SkBits2Float(0x434fe666), SkBits2Float(0x4282a885));  // 206.5f, 65.3221f, 207.2f, 65.3264f, 207.9f, 65.3291f
+path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x4282a9ef), SkBits2Float(0x43514ccd), SkBits2Float(0x4282aad4), SkBits2Float(0x43520000), SkBits2Float(0x4282abe9));  // 208.6f, 65.3319f, 209.3f, 65.3336f, 210, 65.3358f
+path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x4282acff), SkBits2Float(0x43536666), SkBits2Float(0x4282ad0c), SkBits2Float(0x4354199a), SkBits2Float(0x4282af07));  // 210.7f, 65.3379f, 211.4f, 65.338f, 212.1f, 65.3419f
+path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x4282b101), SkBits2Float(0x43558000), SkBits2Float(0x4282b5c2), SkBits2Float(0x43563333), SkBits2Float(0x4282b7c8));  // 212.8f, 65.3457f, 213.5f, 65.355f, 214.2f, 65.3589f
+path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x4282b9ce), SkBits2Float(0x4357999a), SkBits2Float(0x4282b9c3), SkBits2Float(0x43584ccd), SkBits2Float(0x4282bb2c));  // 214.9f, 65.3629f, 215.6f, 65.3628f, 216.3f, 65.3656f
+path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x4282bc96), SkBits2Float(0x4359b333), SkBits2Float(0x4282be3c), SkBits2Float(0x435a6666), SkBits2Float(0x4282c043));  // 217, 65.3683f, 217.7f, 65.3716f, 218.4f, 65.3755f
+path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x4282c249), SkBits2Float(0x435bcccd), SkBits2Float(0x4282c50f), SkBits2Float(0x435c8000), SkBits2Float(0x4282c752));  // 219.1f, 65.3795f, 219.8f, 65.3849f, 220.5f, 65.3893f
+path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x4282c995), SkBits2Float(0x435de666), SkBits2Float(0x4282cc2d), SkBits2Float(0x435e999a), SkBits2Float(0x4282cdd4));  // 221.2f, 65.3937f, 221.9f, 65.3988f, 222.6f, 65.402f
+path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x4282cf7a), SkBits2Float(0x43600000), SkBits2Float(0x4282d211), SkBits2Float(0x4360b333), SkBits2Float(0x4282d138));  // 223.3f, 65.4052f, 224, 65.4103f, 224.7f, 65.4086f
+path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x4282d05f), SkBits2Float(0x4362199a), SkBits2Float(0x4282cd7f), SkBits2Float(0x4362cccd), SkBits2Float(0x4282c8bd));  // 225.4f, 65.407f, 226.1f, 65.4014f, 226.8f, 65.3921f
+path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x4282c3fb), SkBits2Float(0x43643333), SkBits2Float(0x4282b8dc), SkBits2Float(0x4364e666), SkBits2Float(0x4282b4aa));  // 227.5f, 65.3828f, 228.2f, 65.3611f, 228.9f, 65.3529f
+path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x4282b079), SkBits2Float(0x43664ccd), SkBits2Float(0x4282af57), SkBits2Float(0x43670000), SkBits2Float(0x4282af94));  // 229.6f, 65.3447f, 230.3f, 65.3425f, 231, 65.3429f
+path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x4282afd1), SkBits2Float(0x43686666), SkBits2Float(0x4282b38b), SkBits2Float(0x4369199a), SkBits2Float(0x4282b616));  // 231.7f, 65.3434f, 232.4f, 65.3507f, 233.1f, 65.3556f
+path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x4282b8a1), SkBits2Float(0x436a8000), SkBits2Float(0x4282bc7d), SkBits2Float(0x436b3333), SkBits2Float(0x4282bed7));  // 233.8f, 65.3606f, 234.5f, 65.3681f, 235.2f, 65.3727f
+path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x4282c131), SkBits2Float(0x436c999a), SkBits2Float(0x4282c10c), SkBits2Float(0x436d4ccd), SkBits2Float(0x4282c434));  // 235.9f, 65.3773f, 236.6f, 65.377f, 237.3f, 65.3832f
+path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x4282c75c), SkBits2Float(0x436eb333), SkBits2Float(0x4282cb8d), SkBits2Float(0x436f6666), SkBits2Float(0x4282d1c5));  // 238, 65.3894f, 238.7f, 65.3976f, 239.4f, 65.4097f
+path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x4282d7fd), SkBits2Float(0x4370cccd), SkBits2Float(0x4282e22a), SkBits2Float(0x43718000), SkBits2Float(0x4282e983));  // 240.1f, 65.4219f, 240.8f, 65.4417f, 241.5f, 65.4561f
+path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x4282f0dc), SkBits2Float(0x4372e666), SkBits2Float(0x4282fa30), SkBits2Float(0x4373999a), SkBits2Float(0x4282fddc));  // 242.2f, 65.4704f, 242.9f, 65.4886f, 243.6f, 65.4958f
+path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42830189), SkBits2Float(0x43750000), SkBits2Float(0x4282fec1), SkBits2Float(0x4375b333), SkBits2Float(0x4282ff8e));  // 244.3f, 65.503f, 245, 65.4976f, 245.7f, 65.4991f
+path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x4283005c), SkBits2Float(0x4377199a), SkBits2Float(0x42830015), SkBits2Float(0x4377cccd), SkBits2Float(0x428302ac));  // 246.4f, 65.5007f, 247.1f, 65.5002f, 247.8f, 65.5052f
+path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42830543), SkBits2Float(0x43793333), SkBits2Float(0x42830b0b), SkBits2Float(0x4379e666), SkBits2Float(0x42830f18));  // 248.5f, 65.5103f, 249.2f, 65.5216f, 249.9f, 65.5295f
+path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42831324), SkBits2Float(0x437b4ccd), SkBits2Float(0x428316b9), SkBits2Float(0x437c0000), SkBits2Float(0x42831af7));  // 250.6f, 65.5374f, 251.3f, 65.5444f, 252, 65.5527f
+path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42831f34), SkBits2Float(0x437d6666), SkBits2Float(0x428327af), SkBits2Float(0x437e199a), SkBits2Float(0x42832888));  // 252.7f, 65.5609f, 253.4f, 65.5775f, 254.1f, 65.5792f
+path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42832961), SkBits2Float(0x437f8000), SkBits2Float(0x42832e8f), SkBits2Float(0x4380199a), SkBits2Float(0x4283200d));  // 254.8f, 65.5808f, 255.5f, 65.5909f, 256.2f, 65.5626f
+path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x4283118c), SkBits2Float(0x4380cccd), SkBits2Float(0x4282f101), SkBits2Float(0x43812666), SkBits2Float(0x4282d17e));  // 256.9f, 65.5343f, 257.6f, 65.4707f, 258.3f, 65.4092f
+path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x4282b1fc), SkBits2Float(0x4381d99a), SkBits2Float(0x42827d60), SkBits2Float(0x43823333), SkBits2Float(0x428262fe));  // 259, 65.3476f, 259.7f, 65.2449f, 260.4f, 65.1933f
+path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x4282489c), SkBits2Float(0x4382e666), SkBits2Float(0x42823b2a), SkBits2Float(0x43834000), SkBits2Float(0x42823333));  // 261.1f, 65.1418f, 261.8f, 65.1156f, 262.5f, 65.1f
+path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42822b3c), SkBits2Float(0x4383f333), SkBits2Float(0x42823333), SkBits2Float(0x43844ccd), SkBits2Float(0x42823333));  // 263.2f, 65.0844f, 263.9f, 65.1f, 264.6f, 65.1f
+path.lineTo(SkBits2Float(0x43844ccd), SkBits2Float(0x42823333));  // 264.6f, 65.1f
+path.lineTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333));  // 157.5f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333));  // 283.5f, 65.1f
+path.lineTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333));  // 283.5f, 65.1f
+path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x4282356b), SkBits2Float(0x438e7333), SkBits2Float(0x42823d2c), SkBits2Float(0x438ecccd), SkBits2Float(0x42824086));  // 284.2f, 65.1043f, 284.9f, 65.1195f, 285.6f, 65.126f
+path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x428243e0), SkBits2Float(0x438f8000), SkBits2Float(0x42824988), SkBits2Float(0x438fd99a), SkBits2Float(0x4282474f));  // 286.3f, 65.1326f, 287, 65.1436f, 287.7f, 65.1393f
+path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42824516), SkBits2Float(0x43908ccd), SkBits2Float(0x4282368d), SkBits2Float(0x4390e666), SkBits2Float(0x42823333));  // 288.4f, 65.1349f, 289.1f, 65.1065f, 289.8f, 65.1f
+path.lineTo(SkBits2Float(0x4390e666), SkBits2Float(0x42823333));  // 289.8f, 65.1f
+path.lineTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333));  // 283.5f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.cubicTo(SkBits2Float(0x4399a666), SkBits2Float(0x42823334), SkBits2Float(0x439a0000), SkBits2Float(0x42822e24), SkBits2Float(0x439a599a), SkBits2Float(0x4282333c));  // 307.3f, 65.1f, 308, 65.0901f, 308.7f, 65.1001f
+path.cubicTo(SkBits2Float(0x439ab333), SkBits2Float(0x42823854), SkBits2Float(0x439b0ccd), SkBits2Float(0x428247d2), SkBits2Float(0x439b6666), SkBits2Float(0x428251c2));  // 309.4f, 65.11f, 310.1f, 65.1403f, 310.8f, 65.1597f
+path.cubicTo(SkBits2Float(0x439bc000), SkBits2Float(0x42825bb2), SkBits2Float(0x439c199a), SkBits2Float(0x428264e1), SkBits2Float(0x439c7333), SkBits2Float(0x42826edd));  // 311.5f, 65.1791f, 312.2f, 65.197f, 312.9f, 65.2165f
+path.cubicTo(SkBits2Float(0x439ccccd), SkBits2Float(0x428278d9), SkBits2Float(0x439d2666), SkBits2Float(0x428282d5), SkBits2Float(0x439d8000), SkBits2Float(0x42828daa));  // 313.6f, 65.236f, 314.3f, 65.2555f, 315, 65.2767f
+path.cubicTo(SkBits2Float(0x439dd99a), SkBits2Float(0x4282987f), SkBits2Float(0x439e3333), SkBits2Float(0x4282a5a2), SkBits2Float(0x439e8ccd), SkBits2Float(0x4282afdb));  // 315.7f, 65.2978f, 316.4f, 65.3235f, 317.1f, 65.3435f
+path.cubicTo(SkBits2Float(0x439ee666), SkBits2Float(0x4282ba13), SkBits2Float(0x439f4000), SkBits2Float(0x4282c3ec), SkBits2Float(0x439f999a), SkBits2Float(0x4282cafd));  // 317.8f, 65.3634f, 318.5f, 65.3827f, 319.2f, 65.3965f
+path.cubicTo(SkBits2Float(0x439ff333), SkBits2Float(0x4282d20d), SkBits2Float(0x43a04ccd), SkBits2Float(0x4282d6db), SkBits2Float(0x43a0a666), SkBits2Float(0x4282da40));  // 319.9f, 65.4103f, 320.6f, 65.4196f, 321.3f, 65.4263f
+path.cubicTo(SkBits2Float(0x43a10000), SkBits2Float(0x4282dda4), SkBits2Float(0x43a1599a), SkBits2Float(0x4282ded1), SkBits2Float(0x43a1b333), SkBits2Float(0x4282df56));  // 322, 65.4329f, 322.7f, 65.4352f, 323.4f, 65.4362f
+path.cubicTo(SkBits2Float(0x43a20ccd), SkBits2Float(0x4282dfdb), SkBits2Float(0x43a26666), SkBits2Float(0x4282de42), SkBits2Float(0x43a2c000), SkBits2Float(0x4282dd5d));  // 324.1f, 65.4372f, 324.8f, 65.4341f, 325.5f, 65.4324f
+path.cubicTo(SkBits2Float(0x43a3199a), SkBits2Float(0x4282dc78), SkBits2Float(0x43a37333), SkBits2Float(0x4282d884), SkBits2Float(0x43a3cccd), SkBits2Float(0x4282d9f9));  // 326.2f, 65.4306f, 326.9f, 65.4229f, 327.6f, 65.4257f
+path.cubicTo(SkBits2Float(0x43a42666), SkBits2Float(0x4282db6f), SkBits2Float(0x43a48000), SkBits2Float(0x4282e0a8), SkBits2Float(0x43a4d99a), SkBits2Float(0x4282e61e));  // 328.3f, 65.4286f, 329, 65.4388f, 329.7f, 65.4494f
+path.cubicTo(SkBits2Float(0x43a53333), SkBits2Float(0x4282eb95), SkBits2Float(0x43a58ccd), SkBits2Float(0x4282f833), SkBits2Float(0x43a5e666), SkBits2Float(0x4282fabf));  // 330.4f, 65.4601f, 331.1f, 65.4848f, 331.8f, 65.4897f
+path.cubicTo(SkBits2Float(0x43a64000), SkBits2Float(0x4282fd4a), SkBits2Float(0x43a6999a), SkBits2Float(0x4282fb20), SkBits2Float(0x43a6f333), SkBits2Float(0x4282f562));  // 332.5f, 65.4947f, 333.2f, 65.4905f, 333.9f, 65.4793f
+path.cubicTo(SkBits2Float(0x43a74ccd), SkBits2Float(0x4282efa3), SkBits2Float(0x43a7a666), SkBits2Float(0x4282e6bd), SkBits2Float(0x43a80000), SkBits2Float(0x4282d847));  // 334.6f, 65.468f, 335.3f, 65.4507f, 336, 65.4224f
+path.cubicTo(SkBits2Float(0x43a8599a), SkBits2Float(0x4282c9d1), SkBits2Float(0x43a8b333), SkBits2Float(0x4282b47a), SkBits2Float(0x43a90ccd), SkBits2Float(0x42829e9f));  // 336.7f, 65.3942f, 337.4f, 65.3525f, 338.1f, 65.3098f
+path.cubicTo(SkBits2Float(0x43a96666), SkBits2Float(0x428288c4), SkBits2Float(0x43a9c000), SkBits2Float(0x42825c97), SkBits2Float(0x43aa199a), SkBits2Float(0x42825526));  // 338.8f, 65.2671f, 339.5f, 65.1808f, 340.2f, 65.1663f
+path.cubicTo(SkBits2Float(0x43aa7333), SkBits2Float(0x42824db6), SkBits2Float(0x43aacccd), SkBits2Float(0x42825867), SkBits2Float(0x43ab2666), SkBits2Float(0x428271fb));  // 340.9f, 65.1518f, 341.6f, 65.1727f, 342.3f, 65.2226f
+path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x42828b8e), SkBits2Float(0x43abd99a), SkBits2Float(0x4282cd10), SkBits2Float(0x43ac3333), SkBits2Float(0x4282ee99));  // 343, 65.2726f, 343.7f, 65.4005f, 344.4f, 65.466f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42831022), SkBits2Float(0x43ace666), SkBits2Float(0x42832d56), SkBits2Float(0x43ad4000), SkBits2Float(0x42833b2f));  // 345.1f, 65.5315f, 345.8f, 65.5885f, 346.5f, 65.6156f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42834908), SkBits2Float(0x43adf333), SkBits2Float(0x428342d2), SkBits2Float(0x43ae4ccd), SkBits2Float(0x428341b1));  // 347.2f, 65.6426f, 347.9f, 65.6305f, 348.6f, 65.6283f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42834090), SkBits2Float(0x43af0000), SkBits2Float(0x42833a4a), SkBits2Float(0x43af599a), SkBits2Float(0x42833467));  // 349.3f, 65.6261f, 350, 65.6138f, 350.7f, 65.6023f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42832e83), SkBits2Float(0x43b00ccd), SkBits2Float(0x428324e7), SkBits2Float(0x43b06666), SkBits2Float(0x42831e5b));  // 351.4f, 65.5908f, 352.1f, 65.5721f, 352.8f, 65.5593f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x428317cf), SkBits2Float(0x43b1199a), SkBits2Float(0x4283131a), SkBits2Float(0x43b17333), SkBits2Float(0x42830d1f));  // 353.5f, 65.5465f, 354.2f, 65.5373f, 354.9f, 65.5256f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42830724), SkBits2Float(0x43b22666), SkBits2Float(0x42830067), SkBits2Float(0x43b28000), SkBits2Float(0x4282fa78));  // 355.6f, 65.5139f, 356.3f, 65.5008f, 357, 65.4892f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x4282f488), SkBits2Float(0x43b33333), SkBits2Float(0x4282eb71), SkBits2Float(0x43b38ccd), SkBits2Float(0x4282e983));  // 357.7f, 65.4776f, 358.4f, 65.4598f, 359.1f, 65.4561f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x4282e794), SkBits2Float(0x43b44000), SkBits2Float(0x4282ea97), SkBits2Float(0x43b4999a), SkBits2Float(0x4282eee0));  // 359.8f, 65.4523f, 360.5f, 65.4582f, 361.2f, 65.4666f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x4282f329), SkBits2Float(0x43b54ccd), SkBits2Float(0x4282febf), SkBits2Float(0x43b5a666), SkBits2Float(0x42830339));  // 361.9f, 65.4749f, 362.6f, 65.4976f, 363.3f, 65.5063f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x428307b3), SkBits2Float(0x43b6599a), SkBits2Float(0x42830ae8), SkBits2Float(0x43b6b333), SkBits2Float(0x428309bb));  // 364, 65.515f, 364.7f, 65.5213f, 365.4f, 65.519f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x4283088e), SkBits2Float(0x43b76666), SkBits2Float(0x42830219), SkBits2Float(0x43b7c000), SkBits2Float(0x4282fc2a));  // 366.1f, 65.5167f, 366.8f, 65.5041f, 367.5f, 65.4925f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x4282f63b), SkBits2Float(0x43b87333), SkBits2Float(0x4282ec93), SkBits2Float(0x43b8cccd), SkBits2Float(0x4282e61e));  // 368.2f, 65.4809f, 368.9f, 65.4621f, 369.6f, 65.4494f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x4282dfaa), SkBits2Float(0x43b98000), SkBits2Float(0x4282d91c), SkBits2Float(0x43b9d99a), SkBits2Float(0x4282d570));  // 370.3f, 65.4368f, 371, 65.424f, 371.7f, 65.4169f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x4282d1c3), SkBits2Float(0x43ba8ccd), SkBits2Float(0x4282d2b6), SkBits2Float(0x43bae666), SkBits2Float(0x4282d013));  // 372.4f, 65.4097f, 373.1f, 65.4115f, 373.8f, 65.4064f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x4282cd70), SkBits2Float(0x43bb999a), SkBits2Float(0x4282ce26), SkBits2Float(0x43bbf333), SkBits2Float(0x4282c5a0));  // 374.5f, 65.4012f, 375.2f, 65.4026f, 375.9f, 65.386f
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x4282bd19), SkBits2Float(0x43bca666), SkBits2Float(0x4282aa59), SkBits2Float(0x43bd0000), SkBits2Float(0x42829ced));  // 376.6f, 65.3693f, 377.3f, 65.3327f, 378, 65.3065f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42828f81), SkBits2Float(0x43bdb333), SkBits2Float(0x42827f68), SkBits2Float(0x43be0ccd), SkBits2Float(0x42827518));  // 378.7f, 65.2803f, 379.4f, 65.2488f, 380.1f, 65.2287f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42826ac8), SkBits2Float(0x43bec000), SkBits2Float(0x428268f1), SkBits2Float(0x43bf199a), SkBits2Float(0x42825f0d));  // 380.8f, 65.2086f, 381.5f, 65.205f, 382.2f, 65.1856f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42825528), SkBits2Float(0x43bfcccd), SkBits2Float(0x4282410d), SkBits2Float(0x43c02666), SkBits2Float(0x428239be));  // 382.9f, 65.1663f, 383.6f, 65.1271f, 384.3f, 65.1128f
+path.lineTo(SkBits2Float(0x43c02666), SkBits2Float(0x42823333));  // 384.3f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333));  // 388.5f, 65.1f
+path.lineTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333));  // 388.5f, 65.1f
+path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42823333), SkBits2Float(0x43c2f333), SkBits2Float(0x42822c75), SkBits2Float(0x43c34ccd), SkBits2Float(0x42823333));  // 389.2f, 65.1f, 389.9f, 65.0868f, 390.6f, 65.1f
+path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x428239f1), SkBits2Float(0x43c40000), SkBits2Float(0x428243dd), SkBits2Float(0x43c4599a), SkBits2Float(0x42825ba8));  // 391.3f, 65.1132f, 392, 65.1325f, 392.7f, 65.179f
+path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42827373), SkBits2Float(0x43c50ccd), SkBits2Float(0x4282a819), SkBits2Float(0x43c56666), SkBits2Float(0x4282c1f5));  // 393.4f, 65.2255f, 394.1f, 65.3283f, 394.8f, 65.3788f
+path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x4282dbd0), SkBits2Float(0x43c6199a), SkBits2Float(0x4282ee83), SkBits2Float(0x43c67333), SkBits2Float(0x4282f6cd));  // 395.5f, 65.4293f, 396.2f, 65.4658f, 396.9f, 65.482f
+path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x4282ff17), SkBits2Float(0x43c72666), SkBits2Float(0x4282f4d1), SkBits2Float(0x43c78000), SkBits2Float(0x4282f3af));  // 397.6f, 65.4982f, 398.3f, 65.4782f, 399, 65.4759f
+path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x4282f28e), SkBits2Float(0x43c83333), SkBits2Float(0x4282f13e), SkBits2Float(0x43c88ccd), SkBits2Float(0x4282f005));  // 399.7f, 65.4737f, 400.4f, 65.4712f, 401.1f, 65.4688f
+path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x4282eecc), SkBits2Float(0x43c94000), SkBits2Float(0x4282eed9), SkBits2Float(0x43c9999a), SkBits2Float(0x4282ec5a));  // 401.8f, 65.4664f, 402.5f, 65.4665f, 403.2f, 65.4616f
+path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x4282e9da), SkBits2Float(0x43ca4ccd), SkBits2Float(0x4282e71d), SkBits2Float(0x43caa666), SkBits2Float(0x4282e108));  // 403.9f, 65.4567f, 404.6f, 65.4514f, 405.3f, 65.4395f
+path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x4282daf4), SkBits2Float(0x43cb599a), SkBits2Float(0x4282ce0b), SkBits2Float(0x43cbb333), SkBits2Float(0x4282c7df));  // 406, 65.4276f, 406.7f, 65.4024f, 407.4f, 65.3904f
+path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x4282c1b3), SkBits2Float(0x43cc6666), SkBits2Float(0x4282bf7c), SkBits2Float(0x43ccc000), SkBits2Float(0x4282bc00));  // 408.1f, 65.3783f, 408.8f, 65.374f, 409.5f, 65.3672f
+path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x4282b884), SkBits2Float(0x43cd7333), SkBits2Float(0x4282bb96), SkBits2Float(0x43cdcccd), SkBits2Float(0x4282b2f8));  // 410.2f, 65.3604f, 410.9f, 65.3664f, 411.6f, 65.3495f
+path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x4282aa5a), SkBits2Float(0x43ce8000), SkBits2Float(0x428295f5), SkBits2Float(0x43ced99a), SkBits2Float(0x4282884d));  // 412.3f, 65.3327f, 413, 65.2929f, 413.7f, 65.2662f
+path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42827aa4), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42826b01), SkBits2Float(0x43cfe666), SkBits2Float(0x42826105));  // 414.4f, 65.2395f, 415.1f, 65.209f, 415.8f, 65.1895f
+path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42825709), SkBits2Float(0x43d0999a), SkBits2Float(0x42824ec0), SkBits2Float(0x43d0f333), SkBits2Float(0x42824c65));  // 416.5f, 65.17f, 417.2f, 65.1538f, 417.9f, 65.1492f
+path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42824a0b), SkBits2Float(0x43d1a666), SkBits2Float(0x42824c1f), SkBits2Float(0x43d20000), SkBits2Float(0x428252e7));  // 418.6f, 65.1446f, 419.3f, 65.1487f, 420, 65.1619f
+path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x428259b0), SkBits2Float(0x43d2b333), SkBits2Float(0x428269b3), SkBits2Float(0x43d30ccd), SkBits2Float(0x42827518));  // 420.7f, 65.1752f, 421.4f, 65.2064f, 422.1f, 65.2287f
+path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x4282807e), SkBits2Float(0x43d3c000), SkBits2Float(0x42829021), SkBits2Float(0x43d4199a), SkBits2Float(0x42829749));  // 422.8f, 65.251f, 423.5f, 65.2815f, 424.2f, 65.2955f
+path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42829e72), SkBits2Float(0x43d4cccd), SkBits2Float(0x4282a08f), SkBits2Float(0x43d52666), SkBits2Float(0x4282a00a));  // 424.9f, 65.3095f, 425.6f, 65.3136f, 426.3f, 65.3126f
+path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42829f85), SkBits2Float(0x43d5d99a), SkBits2Float(0x42829626), SkBits2Float(0x43d63333), SkBits2Float(0x4282942c));  // 427, 65.3116f, 427.7f, 65.2933f, 428.4f, 65.2894f
+path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42829231), SkBits2Float(0x43d6e666), SkBits2Float(0x42828e79), SkBits2Float(0x43d74000), SkBits2Float(0x4282942c));  // 429.1f, 65.2855f, 429.8f, 65.2783f, 430.5f, 65.2894f
+path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x428299de), SkBits2Float(0x43d7f333), SkBits2Float(0x4282ac18), SkBits2Float(0x43d84ccd), SkBits2Float(0x4282b65d));  // 431.2f, 65.3005f, 431.9f, 65.3361f, 432.6f, 65.3562f
+path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x4282c0a1), SkBits2Float(0x43d90000), SkBits2Float(0x4282cceb), SkBits2Float(0x43d9599a), SkBits2Float(0x4282d1c5));  // 433.3f, 65.3762f, 434, 65.4002f, 434.7f, 65.4097f
+path.cubicTo(SkBits2Float(0x43d9b333), SkBits2Float(0x4282d69f), SkBits2Float(0x43da0ccd), SkBits2Float(0x4282d3cb), SkBits2Float(0x43da6666), SkBits2Float(0x4282d377));  // 435.4f, 65.4192f, 436.1f, 65.4137f, 436.8f, 65.413f
+path.cubicTo(SkBits2Float(0x43dac000), SkBits2Float(0x4282d323), SkBits2Float(0x43db199a), SkBits2Float(0x4282d18a), SkBits2Float(0x43db7333), SkBits2Float(0x4282cfcc));  // 437.5f, 65.4124f, 438.2f, 65.4093f, 438.9f, 65.4059f
+path.cubicTo(SkBits2Float(0x43dbcccd), SkBits2Float(0x4282ce0e), SkBits2Float(0x43dc2666), SkBits2Float(0x4282cd41), SkBits2Float(0x43dc8000), SkBits2Float(0x4282c904));  // 439.6f, 65.4025f, 440.3f, 65.4009f, 441, 65.3926f
+path.cubicTo(SkBits2Float(0x43dcd99a), SkBits2Float(0x4282c4c7), SkBits2Float(0x43dd3333), SkBits2Float(0x4282bc94), SkBits2Float(0x43dd8ccd), SkBits2Float(0x4282b65d));  // 441.7f, 65.3843f, 442.4f, 65.3683f, 443.1f, 65.3562f
+path.cubicTo(SkBits2Float(0x43dde666), SkBits2Float(0x4282b025), SkBits2Float(0x43de4000), SkBits2Float(0x4282a847), SkBits2Float(0x43de999a), SkBits2Float(0x4282a3b5));  // 443.8f, 65.344f, 444.5f, 65.3287f, 445.2f, 65.3197f
+path.cubicTo(SkBits2Float(0x43def333), SkBits2Float(0x42829f24), SkBits2Float(0x43df4ccd), SkBits2Float(0x42829c21), SkBits2Float(0x43dfa666), SkBits2Float(0x42829af4));  // 445.9f, 65.3108f, 446.6f, 65.3049f, 447.3f, 65.3026f
+path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x428299c7), SkBits2Float(0x43e0599a), SkBits2Float(0x42829af4), SkBits2Float(0x43e0b333), SkBits2Float(0x42829ca6));  // 448, 65.3003f, 448.7f, 65.3026f, 449.4f, 65.306f
+path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42829e58), SkBits2Float(0x43e16666), SkBits2Float(0x4282a31a), SkBits2Float(0x43e1c000), SkBits2Float(0x4282a521));  // 450.1f, 65.3093f, 450.8f, 65.3186f, 451.5f, 65.3225f
+path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x4282a727), SkBits2Float(0x43e27333), SkBits2Float(0x4282a640), SkBits2Float(0x43e2cccd), SkBits2Float(0x4282a8cc));  // 452.2f, 65.3265f, 452.9f, 65.3247f, 453.6f, 65.3297f
+path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x4282ab57), SkBits2Float(0x43e38000), SkBits2Float(0x4282adef), SkBits2Float(0x43e3d99a), SkBits2Float(0x4282b464));  // 454.3f, 65.3346f, 455, 65.3397f, 455.7f, 65.3523f
+path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x4282bad8), SkBits2Float(0x43e48ccd), SkBits2Float(0x4282c468), SkBits2Float(0x43e4e666), SkBits2Float(0x4282cf86));  // 456.4f, 65.3649f, 457.1f, 65.3836f, 457.8f, 65.4053f
+path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x4282daa3), SkBits2Float(0x43e5999a), SkBits2Float(0x4282e9b4), SkBits2Float(0x43e5f333), SkBits2Float(0x4282f714));  // 458.5f, 65.427f, 459.2f, 65.4565f, 459.9f, 65.4826f
+path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42830474), SkBits2Float(0x43e6a666), SkBits2Float(0x428314c1), SkBits2Float(0x43e70000), SkBits2Float(0x42831fc7));  // 460.6f, 65.5087f, 461.3f, 65.5405f, 462, 65.5621f
+path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42832acc), SkBits2Float(0x43e7b333), SkBits2Float(0x4283338f), SkBits2Float(0x43e80ccd), SkBits2Float(0x42833936));  // 462.7f, 65.5836f, 463.4f, 65.6007f, 464.1f, 65.6117f
+path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42833edd), SkBits2Float(0x43e8c000), SkBits2Float(0x42834090), SkBits2Float(0x43e9199a), SkBits2Float(0x428341b1));  // 464.8f, 65.6228f, 465.5f, 65.6261f, 466.2f, 65.6283f
+path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x428342d2), SkBits2Float(0x43e9cccd), SkBits2Float(0x428340d8), SkBits2Float(0x43ea2666), SkBits2Float(0x42833fff));  // 466.9f, 65.6305f, 467.6f, 65.6266f, 468.3f, 65.625f
+path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42833f26), SkBits2Float(0x43ead99a), SkBits2Float(0x42833e4d), SkBits2Float(0x43eb3333), SkBits2Float(0x42833c9b));  // 469, 65.6233f, 469.7f, 65.6217f, 470.4f, 65.6184f
+path.cubicTo(SkBits2Float(0x43eb8ccd), SkBits2Float(0x42833ae8), SkBits2Float(0x43ebe666), SkBits2Float(0x42833f32), SkBits2Float(0x43ec4000), SkBits2Float(0x428335d2));  // 471.1f, 65.6151f, 471.8f, 65.6234f, 472.5f, 65.6051f
+path.cubicTo(SkBits2Float(0x43ec999a), SkBits2Float(0x42832c73), SkBits2Float(0x43ecf333), SkBits2Float(0x42831e2e), SkBits2Float(0x43ed4ccd), SkBits2Float(0x4283045e));  // 473.2f, 65.5868f, 473.9f, 65.5589f, 474.6f, 65.5085f
+path.cubicTo(SkBits2Float(0x43eda666), SkBits2Float(0x4282ea8e), SkBits2Float(0x43ee0000), SkBits2Float(0x4282bd9e), SkBits2Float(0x43ee599a), SkBits2Float(0x42829af4));  // 475.3f, 65.4581f, 476, 65.3703f, 476.7f, 65.3026f
+path.cubicTo(SkBits2Float(0x43eeb333), SkBits2Float(0x4282784a), SkBits2Float(0x43ef0ccd), SkBits2Float(0x428245ac), SkBits2Float(0x43ef6666), SkBits2Float(0x42823461));  // 477.4f, 65.2349f, 478.1f, 65.1361f, 478.8f, 65.1023f
+path.cubicTo(SkBits2Float(0x43efc000), SkBits2Float(0x42822316), SkBits2Float(0x43f0199a), SkBits2Float(0x42823365), SkBits2Float(0x43f07333), SkBits2Float(0x42823333));  // 479.5f, 65.0685f, 480.2f, 65.1004f, 480.9f, 65.1f
+path.lineTo(SkBits2Float(0x43f07333), SkBits2Float(0x42823333));  // 480.9f, 65.1f
+path.lineTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333));  // 388.5f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333));  // 504, 65.1f
+path.lineTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333));  // 504, 65.1f
+path.cubicTo(SkBits2Float(0x43fc599a), SkBits2Float(0x42823333), SkBits2Float(0x43fcb333), SkBits2Float(0x42822ef4), SkBits2Float(0x43fd0ccd), SkBits2Float(0x42823333));  // 504.7f, 65.1f, 505.4f, 65.0917f, 506.1f, 65.1f
+path.cubicTo(SkBits2Float(0x43fd6666), SkBits2Float(0x42823772), SkBits2Float(0x43fdc000), SkBits2Float(0x42823e7d), SkBits2Float(0x43fe199a), SkBits2Float(0x42824cac));  // 506.8f, 65.1083f, 507.5f, 65.122f, 508.2f, 65.1497f
+path.cubicTo(SkBits2Float(0x43fe7333), SkBits2Float(0x42825adb), SkBits2Float(0x43fecccd), SkBits2Float(0x42827a73), SkBits2Float(0x43ff2666), SkBits2Float(0x4282884d));  // 508.9f, 65.1775f, 509.6f, 65.2392f, 510.3f, 65.2662f
+path.cubicTo(SkBits2Float(0x43ff8000), SkBits2Float(0x42829626), SkBits2Float(0x43ffd99a), SkBits2Float(0x42829a88), SkBits2Float(0x4400199a), SkBits2Float(0x42829fc4));  // 511, 65.2933f, 511.7f, 65.3018f, 512.4f, 65.312f
+path.cubicTo(SkBits2Float(0x44004666), SkBits2Float(0x4282a4ff), SkBits2Float(0x44007333), SkBits2Float(0x4282a6e4), SkBits2Float(0x4400a000), SkBits2Float(0x4282a7b1));  // 513.1f, 65.3223f, 513.8f, 65.326f, 514.5f, 65.3275f
+path.cubicTo(SkBits2Float(0x4400cccd), SkBits2Float(0x4282a87e), SkBits2Float(0x4400f99a), SkBits2Float(0x4282a682), SkBits2Float(0x44012666), SkBits2Float(0x4282a494));  // 515.2f, 65.3291f, 515.9f, 65.3252f, 516.6f, 65.3214f
+path.cubicTo(SkBits2Float(0x44015333), SkBits2Float(0x4282a2a5), SkBits2Float(0x44018000), SkBits2Float(0x42829e44), SkBits2Float(0x4401accd), SkBits2Float(0x42829c19));  // 517.3f, 65.3177f, 518, 65.3091f, 518.7f, 65.3049f
+path.cubicTo(SkBits2Float(0x4401d99a), SkBits2Float(0x428299ee), SkBits2Float(0x44020666), SkBits2Float(0x42829718), SkBits2Float(0x44023333), SkBits2Float(0x42829790));  // 519.4f, 65.3006f, 520.1f, 65.2951f, 520.8f, 65.296f
+path.cubicTo(SkBits2Float(0x44026000), SkBits2Float(0x42829807), SkBits2Float(0x44028ccd), SkBits2Float(0x42829ae5), SkBits2Float(0x4402b99a), SkBits2Float(0x42829ee5));  // 521.5f, 65.2969f, 522.2f, 65.3025f, 522.9f, 65.3103f
+path.cubicTo(SkBits2Float(0x4402e666), SkBits2Float(0x4282a2e6), SkBits2Float(0x44031333), SkBits2Float(0x4282abab), SkBits2Float(0x44034000), SkBits2Float(0x4282af94));  // 523.6f, 65.3182f, 524.3f, 65.3353f, 525, 65.3429f
+path.cubicTo(SkBits2Float(0x44036ccd), SkBits2Float(0x4282b37d), SkBits2Float(0x4403999a), SkBits2Float(0x4282b5c0), SkBits2Float(0x4403c666), SkBits2Float(0x4282b65d));  // 525.7f, 65.3506f, 526.4f, 65.355f, 527.1f, 65.3562f
+path.cubicTo(SkBits2Float(0x4403f333), SkBits2Float(0x4282b6f9), SkBits2Float(0x44042000), SkBits2Float(0x4282c91b), SkBits2Float(0x44044ccd), SkBits2Float(0x4282b33f));  // 527.8f, 65.3574f, 528.5f, 65.3928f, 529.2f, 65.3501f
+path.cubicTo(SkBits2Float(0x4404799a), SkBits2Float(0x42829d63), SkBits2Float(0x4404a666), SkBits2Float(0x4282488a), SkBits2Float(0x4404d333), SkBits2Float(0x42823333));  // 529.9f, 65.3074f, 530.6f, 65.1417f, 531.3f, 65.1f
+path.cubicTo(SkBits2Float(0x44050000), SkBits2Float(0x42821ddb), SkBits2Float(0x44052ccd), SkBits2Float(0x42823333), SkBits2Float(0x4405599a), SkBits2Float(0x42823333));  // 532, 65.0583f, 532.7f, 65.1f, 533.4f, 65.1f
+path.lineTo(SkBits2Float(0x4405599a), SkBits2Float(0x42823333));  // 533.4f, 65.1f
+path.lineTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333));  // 504, 65.1f
+path.close();
+    return path;
+}
+
+static SkPath path1_a() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.cubicTo(SkBits2Float(0x4399a666), SkBits2Float(0x42823332), SkBits2Float(0x439a0000), SkBits2Float(0x42823842), SkBits2Float(0x439a599a), SkBits2Float(0x4282332a));  // 307.3f, 65.1f, 308, 65.1099f, 308.7f, 65.0999f
+path.cubicTo(SkBits2Float(0x439ab333), SkBits2Float(0x42822e12), SkBits2Float(0x439b0ccd), SkBits2Float(0x42821e94), SkBits2Float(0x439b6666), SkBits2Float(0x428214a4));  // 309.4f, 65.09f, 310.1f, 65.0597f, 310.8f, 65.0403f
+path.cubicTo(SkBits2Float(0x439bc000), SkBits2Float(0x42820ab4), SkBits2Float(0x439c199a), SkBits2Float(0x42820185), SkBits2Float(0x439c7333), SkBits2Float(0x4281f789));  // 311.5f, 65.0209f, 312.2f, 65.003f, 312.9f, 64.9835f
+#if 0
+path.cubicTo(SkBits2Float(0x439ccccd), SkBits2Float(0x4281ed8d), SkBits2Float(0x439d2666), SkBits2Float(0x4281e391), SkBits2Float(0x439d8000), SkBits2Float(0x4281d8bc));  // 313.6f, 64.964f, 314.3f, 64.9445f, 315, 64.9233f
+#if 0
+path.cubicTo(SkBits2Float(0x439dd99a), SkBits2Float(0x4281cde7), SkBits2Float(0x439e3333), SkBits2Float(0x4281c0c4), SkBits2Float(0x439e8ccd), SkBits2Float(0x4281b68b));  // 315.7f, 64.9022f, 316.4f, 64.8765f, 317.1f, 64.8565f
+path.cubicTo(SkBits2Float(0x439ee666), SkBits2Float(0x4281ac53), SkBits2Float(0x439f4000), SkBits2Float(0x4281a27a), SkBits2Float(0x439f999a), SkBits2Float(0x42819b69));  // 317.8f, 64.8366f, 318.5f, 64.8173f, 319.2f, 64.8035f
+path.cubicTo(SkBits2Float(0x439ff333), SkBits2Float(0x42819459), SkBits2Float(0x43a04ccd), SkBits2Float(0x42818f8b), SkBits2Float(0x43a0a666), SkBits2Float(0x42818c26));  // 319.9f, 64.7897f, 320.6f, 64.7804f, 321.3f, 64.7737f
+path.cubicTo(SkBits2Float(0x43a10000), SkBits2Float(0x428188c2), SkBits2Float(0x43a1599a), SkBits2Float(0x42818795), SkBits2Float(0x43a1b333), SkBits2Float(0x42818710));  // 322, 64.7671f, 322.7f, 64.7648f, 323.4f, 64.7638f
+path.cubicTo(SkBits2Float(0x43a20ccd), SkBits2Float(0x4281868b), SkBits2Float(0x43a26666), SkBits2Float(0x42818824), SkBits2Float(0x43a2c000), SkBits2Float(0x42818909));  // 324.1f, 64.7628f, 324.8f, 64.7659f, 325.5f, 64.7676f
+path.cubicTo(SkBits2Float(0x43a3199a), SkBits2Float(0x428189ee), SkBits2Float(0x43a37333), SkBits2Float(0x42818de2), SkBits2Float(0x43a3cccd), SkBits2Float(0x42818c6d));  // 326.2f, 64.7694f, 326.9f, 64.7771f, 327.6f, 64.7743f
+path.cubicTo(SkBits2Float(0x43a42666), SkBits2Float(0x42818af7), SkBits2Float(0x43a48000), SkBits2Float(0x428185be), SkBits2Float(0x43a4d99a), SkBits2Float(0x42818048));  // 328.3f, 64.7714f, 329, 64.7612f, 329.7f, 64.7505f
+path.cubicTo(SkBits2Float(0x43a53333), SkBits2Float(0x42817ad1), SkBits2Float(0x43a58ccd), SkBits2Float(0x42816e33), SkBits2Float(0x43a5e666), SkBits2Float(0x42816ba7));  // 330.4f, 64.7399f, 331.1f, 64.7152f, 331.8f, 64.7103f
+path.cubicTo(SkBits2Float(0x43a64000), SkBits2Float(0x4281691c), SkBits2Float(0x43a6999a), SkBits2Float(0x42816b46), SkBits2Float(0x43a6f333), SkBits2Float(0x42817104));  // 332.5f, 64.7053f, 333.2f, 64.7095f, 333.9f, 64.7207f
+path.cubicTo(SkBits2Float(0x43a74ccd), SkBits2Float(0x428176c3), SkBits2Float(0x43a7a666), SkBits2Float(0x42817fa9), SkBits2Float(0x43a80000), SkBits2Float(0x42818e1f));  // 334.6f, 64.732f, 335.3f, 64.7493f, 336, 64.7776f
+path.cubicTo(SkBits2Float(0x43a8599a), SkBits2Float(0x42819c95), SkBits2Float(0x43a8b333), SkBits2Float(0x4281b1ec), SkBits2Float(0x43a90ccd), SkBits2Float(0x4281c7c7));  // 336.7f, 64.8058f, 337.4f, 64.8475f, 338.1f, 64.8902f
+path.cubicTo(SkBits2Float(0x43a96666), SkBits2Float(0x4281dda2), SkBits2Float(0x43a9c000), SkBits2Float(0x428209cf), SkBits2Float(0x43aa199a), SkBits2Float(0x42821140));  // 338.8f, 64.9329f, 339.5f, 65.0192f, 340.2f, 65.0337f
+path.cubicTo(SkBits2Float(0x43aa7333), SkBits2Float(0x428218b0), SkBits2Float(0x43aacccd), SkBits2Float(0x42820dff), SkBits2Float(0x43ab2666), SkBits2Float(0x4281f46b));  // 340.9f, 65.0482f, 341.6f, 65.0273f, 342.3f, 64.9774f
+path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x4281dad8), SkBits2Float(0x43abd99a), SkBits2Float(0x42819956), SkBits2Float(0x43ac3333), SkBits2Float(0x428177cd));  // 343, 64.9274f, 343.7f, 64.7995f, 344.4f, 64.734f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42815644), SkBits2Float(0x43ace666), SkBits2Float(0x42813910), SkBits2Float(0x43ad4000), SkBits2Float(0x42812b37));  // 345.1f, 64.6685f, 345.8f, 64.6115f, 346.5f, 64.5844f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42811d5e), SkBits2Float(0x43adf333), SkBits2Float(0x42812394), SkBits2Float(0x43ae4ccd), SkBits2Float(0x428124b5));  // 347.2f, 64.5574f, 347.9f, 64.5695f, 348.6f, 64.5717f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x428125d6), SkBits2Float(0x43af0000), SkBits2Float(0x42812c1c), SkBits2Float(0x43af599a), SkBits2Float(0x428131ff));  // 349.3f, 64.5739f, 350, 64.5862f, 350.7f, 64.5976f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x428137e3), SkBits2Float(0x43b00ccd), SkBits2Float(0x4281417f), SkBits2Float(0x43b06666), SkBits2Float(0x4281480b));  // 351.4f, 64.6092f, 352.1f, 64.6279f, 352.8f, 64.6407f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42814e97), SkBits2Float(0x43b1199a), SkBits2Float(0x4281534c), SkBits2Float(0x43b17333), SkBits2Float(0x42815947));  // 353.5f, 64.6535f, 354.2f, 64.6627f, 354.9f, 64.6744f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42815f42), SkBits2Float(0x43b22666), SkBits2Float(0x428165ff), SkBits2Float(0x43b28000), SkBits2Float(0x42816bee));  // 355.6f, 64.6861f, 356.3f, 64.6992f, 357, 64.7108f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x428171de), SkBits2Float(0x43b33333), SkBits2Float(0x42817af5), SkBits2Float(0x43b38ccd), SkBits2Float(0x42817ce3));  // 357.7f, 64.7224f, 358.4f, 64.7402f, 359.1f, 64.7439f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42817ed2), SkBits2Float(0x43b44000), SkBits2Float(0x42817bcf), SkBits2Float(0x43b4999a), SkBits2Float(0x42817786));  // 359.8f, 64.7477f, 360.5f, 64.7418f, 361.2f, 64.7334f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x4281733d), SkBits2Float(0x43b54ccd), SkBits2Float(0x428167a7), SkBits2Float(0x43b5a666), SkBits2Float(0x4281632d));  // 361.9f, 64.7251f, 362.6f, 64.7024f, 363.3f, 64.6937f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42815eb3), SkBits2Float(0x43b6599a), SkBits2Float(0x42815b7e), SkBits2Float(0x43b6b333), SkBits2Float(0x42815cab));  // 364, 64.685f, 364.7f, 64.6787f, 365.4f, 64.681f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42815dd8), SkBits2Float(0x43b76666), SkBits2Float(0x4281644d), SkBits2Float(0x43b7c000), SkBits2Float(0x42816a3c));  // 366.1f, 64.6833f, 366.8f, 64.6959f, 367.5f, 64.7075f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x4281702b), SkBits2Float(0x43b87333), SkBits2Float(0x428179d3), SkBits2Float(0x43b8cccd), SkBits2Float(0x42818048));  // 368.2f, 64.7191f, 368.9f, 64.7379f, 369.6f, 64.7505f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x428186bc), SkBits2Float(0x43b98000), SkBits2Float(0x42818d4a), SkBits2Float(0x43b9d99a), SkBits2Float(0x428190f6));  // 370.3f, 64.7632f, 371, 64.776f, 371.7f, 64.7831f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x428194a3), SkBits2Float(0x43ba8ccd), SkBits2Float(0x428193b0), SkBits2Float(0x43bae666), SkBits2Float(0x42819653));  // 372.4f, 64.7903f, 373.1f, 64.7885f, 373.8f, 64.7936f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x428198f6), SkBits2Float(0x43bb999a), SkBits2Float(0x42819840), SkBits2Float(0x43bbf333), SkBits2Float(0x4281a0c6));  // 374.5f, 64.7988f, 375.2f, 64.7974f, 375.9f, 64.814f
+#endif
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x4281a94d), SkBits2Float(0x43bca666), SkBits2Float(0x4281bc0d), SkBits2Float(0x43bd0000), SkBits2Float(0x4281c979));  // 376.6f, 64.8307f, 377.3f, 64.8673f, 378, 64.8935f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x4281d6e5), SkBits2Float(0x43bdb333), SkBits2Float(0x4281e6fe), SkBits2Float(0x43be0ccd), SkBits2Float(0x4281f14e));  // 378.7f, 64.9197f, 379.4f, 64.9512f, 380.1f, 64.9713f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x4281fb9e), SkBits2Float(0x43bec000), SkBits2Float(0x4281fd75), SkBits2Float(0x43bf199a), SkBits2Float(0x42820759));  // 380.8f, 64.9914f, 381.5f, 64.995f, 382.2f, 65.0144f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x4282113e), SkBits2Float(0x43bfcccd), SkBits2Float(0x42822559), SkBits2Float(0x43c02666), SkBits2Float(0x42822ca8));  // 382.9f, 65.0337f, 383.6f, 65.0729f, 384.3f, 65.0872f
+#endif
+path.lineTo(SkBits2Float(0x43c02666), SkBits2Float(0x42823333));  // 384.3f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.close();
+    return path;
+}
+
+static SkPath path2_a() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.cubicTo(SkBits2Float(0x4399a666), SkBits2Float(0x42823334), SkBits2Float(0x439a0000), SkBits2Float(0x42822e24), SkBits2Float(0x439a599a), SkBits2Float(0x4282333c));  // 307.3f, 65.1f, 308, 65.0901f, 308.7f, 65.1001f
+path.cubicTo(SkBits2Float(0x439ab333), SkBits2Float(0x42823854), SkBits2Float(0x439b0ccd), SkBits2Float(0x428247d2), SkBits2Float(0x439b6666), SkBits2Float(0x428251c2));  // 309.4f, 65.11f, 310.1f, 65.1403f, 310.8f, 65.1597f
+path.cubicTo(SkBits2Float(0x439bc000), SkBits2Float(0x42825bb2), SkBits2Float(0x439c199a), SkBits2Float(0x428264e1), SkBits2Float(0x439c7333), SkBits2Float(0x42826edd));  // 311.5f, 65.1791f, 312.2f, 65.197f, 312.9f, 65.2165f
+#if 0
+path.cubicTo(SkBits2Float(0x439ccccd), SkBits2Float(0x428278d9), SkBits2Float(0x439d2666), SkBits2Float(0x428282d5), SkBits2Float(0x439d8000), SkBits2Float(0x42828daa));  // 313.6f, 65.236f, 314.3f, 65.2555f, 315, 65.2767f
+#if 0
+path.cubicTo(SkBits2Float(0x439dd99a), SkBits2Float(0x4282987f), SkBits2Float(0x439e3333), SkBits2Float(0x4282a5a2), SkBits2Float(0x439e8ccd), SkBits2Float(0x4282afdb));  // 315.7f, 65.2978f, 316.4f, 65.3235f, 317.1f, 65.3435f
+path.cubicTo(SkBits2Float(0x439ee666), SkBits2Float(0x4282ba13), SkBits2Float(0x439f4000), SkBits2Float(0x4282c3ec), SkBits2Float(0x439f999a), SkBits2Float(0x4282cafd));  // 317.8f, 65.3634f, 318.5f, 65.3827f, 319.2f, 65.3965f
+path.cubicTo(SkBits2Float(0x439ff333), SkBits2Float(0x4282d20d), SkBits2Float(0x43a04ccd), SkBits2Float(0x4282d6db), SkBits2Float(0x43a0a666), SkBits2Float(0x4282da40));  // 319.9f, 65.4103f, 320.6f, 65.4196f, 321.3f, 65.4263f
+path.cubicTo(SkBits2Float(0x43a10000), SkBits2Float(0x4282dda4), SkBits2Float(0x43a1599a), SkBits2Float(0x4282ded1), SkBits2Float(0x43a1b333), SkBits2Float(0x4282df56));  // 322, 65.4329f, 322.7f, 65.4352f, 323.4f, 65.4362f
+path.cubicTo(SkBits2Float(0x43a20ccd), SkBits2Float(0x4282dfdb), SkBits2Float(0x43a26666), SkBits2Float(0x4282de42), SkBits2Float(0x43a2c000), SkBits2Float(0x4282dd5d));  // 324.1f, 65.4372f, 324.8f, 65.4341f, 325.5f, 65.4324f
+path.cubicTo(SkBits2Float(0x43a3199a), SkBits2Float(0x4282dc78), SkBits2Float(0x43a37333), SkBits2Float(0x4282d884), SkBits2Float(0x43a3cccd), SkBits2Float(0x4282d9f9));  // 326.2f, 65.4306f, 326.9f, 65.4229f, 327.6f, 65.4257f
+path.cubicTo(SkBits2Float(0x43a42666), SkBits2Float(0x4282db6f), SkBits2Float(0x43a48000), SkBits2Float(0x4282e0a8), SkBits2Float(0x43a4d99a), SkBits2Float(0x4282e61e));  // 328.3f, 65.4286f, 329, 65.4388f, 329.7f, 65.4494f
+path.cubicTo(SkBits2Float(0x43a53333), SkBits2Float(0x4282eb95), SkBits2Float(0x43a58ccd), SkBits2Float(0x4282f833), SkBits2Float(0x43a5e666), SkBits2Float(0x4282fabf));  // 330.4f, 65.4601f, 331.1f, 65.4848f, 331.8f, 65.4897f
+path.cubicTo(SkBits2Float(0x43a64000), SkBits2Float(0x4282fd4a), SkBits2Float(0x43a6999a), SkBits2Float(0x4282fb20), SkBits2Float(0x43a6f333), SkBits2Float(0x4282f562));  // 332.5f, 65.4947f, 333.2f, 65.4905f, 333.9f, 65.4793f
+path.cubicTo(SkBits2Float(0x43a74ccd), SkBits2Float(0x4282efa3), SkBits2Float(0x43a7a666), SkBits2Float(0x4282e6bd), SkBits2Float(0x43a80000), SkBits2Float(0x4282d847));  // 334.6f, 65.468f, 335.3f, 65.4507f, 336, 65.4224f
+path.cubicTo(SkBits2Float(0x43a8599a), SkBits2Float(0x4282c9d1), SkBits2Float(0x43a8b333), SkBits2Float(0x4282b47a), SkBits2Float(0x43a90ccd), SkBits2Float(0x42829e9f));  // 336.7f, 65.3942f, 337.4f, 65.3525f, 338.1f, 65.3098f
+path.cubicTo(SkBits2Float(0x43a96666), SkBits2Float(0x428288c4), SkBits2Float(0x43a9c000), SkBits2Float(0x42825c97), SkBits2Float(0x43aa199a), SkBits2Float(0x42825526));  // 338.8f, 65.2671f, 339.5f, 65.1808f, 340.2f, 65.1663f
+path.cubicTo(SkBits2Float(0x43aa7333), SkBits2Float(0x42824db6), SkBits2Float(0x43aacccd), SkBits2Float(0x42825867), SkBits2Float(0x43ab2666), SkBits2Float(0x428271fb));  // 340.9f, 65.1518f, 341.6f, 65.1727f, 342.3f, 65.2226f
+path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x42828b8e), SkBits2Float(0x43abd99a), SkBits2Float(0x4282cd10), SkBits2Float(0x43ac3333), SkBits2Float(0x4282ee99));  // 343, 65.2726f, 343.7f, 65.4005f, 344.4f, 65.466f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42831022), SkBits2Float(0x43ace666), SkBits2Float(0x42832d56), SkBits2Float(0x43ad4000), SkBits2Float(0x42833b2f));  // 345.1f, 65.5315f, 345.8f, 65.5885f, 346.5f, 65.6156f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42834908), SkBits2Float(0x43adf333), SkBits2Float(0x428342d2), SkBits2Float(0x43ae4ccd), SkBits2Float(0x428341b1));  // 347.2f, 65.6426f, 347.9f, 65.6305f, 348.6f, 65.6283f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42834090), SkBits2Float(0x43af0000), SkBits2Float(0x42833a4a), SkBits2Float(0x43af599a), SkBits2Float(0x42833467));  // 349.3f, 65.6261f, 350, 65.6138f, 350.7f, 65.6023f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42832e83), SkBits2Float(0x43b00ccd), SkBits2Float(0x428324e7), SkBits2Float(0x43b06666), SkBits2Float(0x42831e5b));  // 351.4f, 65.5908f, 352.1f, 65.5721f, 352.8f, 65.5593f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x428317cf), SkBits2Float(0x43b1199a), SkBits2Float(0x4283131a), SkBits2Float(0x43b17333), SkBits2Float(0x42830d1f));  // 353.5f, 65.5465f, 354.2f, 65.5373f, 354.9f, 65.5256f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42830724), SkBits2Float(0x43b22666), SkBits2Float(0x42830067), SkBits2Float(0x43b28000), SkBits2Float(0x4282fa78));  // 355.6f, 65.5139f, 356.3f, 65.5008f, 357, 65.4892f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x4282f488), SkBits2Float(0x43b33333), SkBits2Float(0x4282eb71), SkBits2Float(0x43b38ccd), SkBits2Float(0x4282e983));  // 357.7f, 65.4776f, 358.4f, 65.4598f, 359.1f, 65.4561f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x4282e794), SkBits2Float(0x43b44000), SkBits2Float(0x4282ea97), SkBits2Float(0x43b4999a), SkBits2Float(0x4282eee0));  // 359.8f, 65.4523f, 360.5f, 65.4582f, 361.2f, 65.4666f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x4282f329), SkBits2Float(0x43b54ccd), SkBits2Float(0x4282febf), SkBits2Float(0x43b5a666), SkBits2Float(0x42830339));  // 361.9f, 65.4749f, 362.6f, 65.4976f, 363.3f, 65.5063f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x428307b3), SkBits2Float(0x43b6599a), SkBits2Float(0x42830ae8), SkBits2Float(0x43b6b333), SkBits2Float(0x428309bb));  // 364, 65.515f, 364.7f, 65.5213f, 365.4f, 65.519f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x4283088e), SkBits2Float(0x43b76666), SkBits2Float(0x42830219), SkBits2Float(0x43b7c000), SkBits2Float(0x4282fc2a));  // 366.1f, 65.5167f, 366.8f, 65.5041f, 367.5f, 65.4925f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x4282f63b), SkBits2Float(0x43b87333), SkBits2Float(0x4282ec93), SkBits2Float(0x43b8cccd), SkBits2Float(0x4282e61e));  // 368.2f, 65.4809f, 368.9f, 65.4621f, 369.6f, 65.4494f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x4282dfaa), SkBits2Float(0x43b98000), SkBits2Float(0x4282d91c), SkBits2Float(0x43b9d99a), SkBits2Float(0x4282d570));  // 370.3f, 65.4368f, 371, 65.424f, 371.7f, 65.4169f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x4282d1c3), SkBits2Float(0x43ba8ccd), SkBits2Float(0x4282d2b6), SkBits2Float(0x43bae666), SkBits2Float(0x4282d013));  // 372.4f, 65.4097f, 373.1f, 65.4115f, 373.8f, 65.4064f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x4282cd70), SkBits2Float(0x43bb999a), SkBits2Float(0x4282ce26), SkBits2Float(0x43bbf333), SkBits2Float(0x4282c5a0));  // 374.5f, 65.4012f, 375.2f, 65.4026f, 375.9f, 65.386f
+#endif
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x4282bd19), SkBits2Float(0x43bca666), SkBits2Float(0x4282aa59), SkBits2Float(0x43bd0000), SkBits2Float(0x42829ced));  // 376.6f, 65.3693f, 377.3f, 65.3327f, 378, 65.3065f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42828f81), SkBits2Float(0x43bdb333), SkBits2Float(0x42827f68), SkBits2Float(0x43be0ccd), SkBits2Float(0x42827518));  // 378.7f, 65.2803f, 379.4f, 65.2488f, 380.1f, 65.2287f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42826ac8), SkBits2Float(0x43bec000), SkBits2Float(0x428268f1), SkBits2Float(0x43bf199a), SkBits2Float(0x42825f0d));  // 380.8f, 65.2086f, 381.5f, 65.205f, 382.2f, 65.1856f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42825528), SkBits2Float(0x43bfcccd), SkBits2Float(0x4282410d), SkBits2Float(0x43c02666), SkBits2Float(0x428239be));  // 382.9f, 65.1663f, 383.6f, 65.1271f, 384.3f, 65.1128f
+#endif
+path.lineTo(SkBits2Float(0x43c02666), SkBits2Float(0x42823333));  // 384.3f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.close();
+    return path;
+}
+
+static void issue3651_1a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path = path1_a();
+    SkPath pathB = path2_a();
+    // DEBUG_UNDER_DEVELOPMENT  issue3651_1a disable expectation check for now
+    testPathOpCheck(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename,
+            !SkOpGlobalState::DebugRunFail());
+}
+
+static SkPath path3() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000));  // 88.2f, 94.5f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000));  // 88.2f, 94.5f
+path.cubicTo(SkBits2Float(0x42b1cccd), SkBits2Float(0x42bd0000), SkBits2Float(0x42b33333), SkBits2Float(0x42bd2573), SkBits2Float(0x42b4999a), SkBits2Float(0x42bd0000));  // 88.9f, 94.5f, 89.6f, 94.5731f, 90.3f, 94.5f
+path.cubicTo(SkBits2Float(0x42b60000), SkBits2Float(0x42bcda8d), SkBits2Float(0x42b76666), SkBits2Float(0x42bc4598), SkBits2Float(0x42b8cccd), SkBits2Float(0x42bc1f4b));  // 91, 94.4269f, 91.7f, 94.1359f, 92.4f, 94.0611f
+path.cubicTo(SkBits2Float(0x42ba3333), SkBits2Float(0x42bbf8ff), SkBits2Float(0x42bb999a), SkBits2Float(0x42bc1b9e), SkBits2Float(0x42bd0000), SkBits2Float(0x42bc1a35));  // 93.1f, 93.9863f, 93.8f, 94.0539f, 94.5f, 94.0512f
+path.cubicTo(SkBits2Float(0x42be6666), SkBits2Float(0x42bc18cb), SkBits2Float(0x42bfcccd), SkBits2Float(0x42bc17f2), SkBits2Float(0x42c13333), SkBits2Float(0x42bc16d0));  // 95.2f, 94.0484f, 95.9f, 94.0468f, 96.6f, 94.0446f
+path.cubicTo(SkBits2Float(0x42c2999a), SkBits2Float(0x42bc15af), SkBits2Float(0x42c40000), SkBits2Float(0x42bc13fd), SkBits2Float(0x42c56666), SkBits2Float(0x42bc136c));  // 97.3f, 94.0424f, 98, 94.039f, 98.7f, 94.0379f
+path.cubicTo(SkBits2Float(0x42c6cccd), SkBits2Float(0x42bc12dc), SkBits2Float(0x42c83333), SkBits2Float(0x42bc12e7), SkBits2Float(0x42c9999a), SkBits2Float(0x42bc136c));  // 99.4f, 94.0368f, 100.1f, 94.0369f, 100.8f, 94.0379f
+path.cubicTo(SkBits2Float(0x42cb0000), SkBits2Float(0x42bc13f1), SkBits2Float(0x42cc6666), SkBits2Float(0x42bc15c8), SkBits2Float(0x42cdcccd), SkBits2Float(0x42bc168a));  // 101.5f, 94.0389f, 102.2f, 94.0425f, 102.9f, 94.044f
+path.cubicTo(SkBits2Float(0x42cf3333), SkBits2Float(0x42bc174b), SkBits2Float(0x42d0999a), SkBits2Float(0x42bc17ad), SkBits2Float(0x42d20000), SkBits2Float(0x42bc17f5));  // 103.6f, 94.0455f, 104.3f, 94.0462f, 105, 94.0468f
+path.cubicTo(SkBits2Float(0x42d36666), SkBits2Float(0x42bc183e), SkBits2Float(0x42d4cccd), SkBits2Float(0x42bc162a), SkBits2Float(0x42d63333), SkBits2Float(0x42bc183c));  // 105.7f, 94.0473f, 106.4f, 94.0433f, 107.1f, 94.0473f
+path.cubicTo(SkBits2Float(0x42d7999a), SkBits2Float(0x42bc1a4e), SkBits2Float(0x42d90000), SkBits2Float(0x42bc1e66), SkBits2Float(0x42da6666), SkBits2Float(0x42bc2461));  // 107.8f, 94.0514f, 108.5f, 94.0594f, 109.2f, 94.0711f
+path.cubicTo(SkBits2Float(0x42dbcccd), SkBits2Float(0x42bc2a5d), SkBits2Float(0x42dd3333), SkBits2Float(0x42bc33f9), SkBits2Float(0x42de999a), SkBits2Float(0x42bc3c1f));  // 109.9f, 94.0827f, 110.6f, 94.1015f, 111.3f, 94.1174f
+path.cubicTo(SkBits2Float(0x42e00000), SkBits2Float(0x42bc4446), SkBits2Float(0x42e16666), SkBits2Float(0x42bc4cce), SkBits2Float(0x42e2cccd), SkBits2Float(0x42bc5548));  // 112, 94.1333f, 112.7f, 94.15f, 113.4f, 94.1666f
+path.cubicTo(SkBits2Float(0x42e43333), SkBits2Float(0x42bc5dc3), SkBits2Float(0x42e5999a), SkBits2Float(0x42bc6472), SkBits2Float(0x42e70000), SkBits2Float(0x42bc6eff));  // 114.1f, 94.1831f, 114.8f, 94.1962f, 115.5f, 94.2168f
+path.cubicTo(SkBits2Float(0x42e86666), SkBits2Float(0x42bc798b), SkBits2Float(0x42e9cccd), SkBits2Float(0x42bc8607), SkBits2Float(0x42eb3333), SkBits2Float(0x42bc9494));  // 116.2f, 94.2374f, 116.9f, 94.2618f, 117.6f, 94.2902f
+path.cubicTo(SkBits2Float(0x42ec999a), SkBits2Float(0x42bca321), SkBits2Float(0x42ee0000), SkBits2Float(0x42bcb9f8), SkBits2Float(0x42ef6666), SkBits2Float(0x42bcc64f));  // 118.3f, 94.3186f, 119, 94.3632f, 119.7f, 94.3873f
+path.cubicTo(SkBits2Float(0x42f0cccd), SkBits2Float(0x42bcd2a5), SkBits2Float(0x42f23333), SkBits2Float(0x42bcdb35), SkBits2Float(0x42f3999a), SkBits2Float(0x42bcde9a));  // 120.4f, 94.4114f, 121.1f, 94.4281f, 121.8f, 94.4348f
+path.cubicTo(SkBits2Float(0x42f50000), SkBits2Float(0x42bce1fe), SkBits2Float(0x42f66666), SkBits2Float(0x42bcdd3f), SkBits2Float(0x42f7cccd), SkBits2Float(0x42bcdaa8));  // 122.5f, 94.4414f, 123.2f, 94.4321f, 123.9f, 94.4271f
+path.cubicTo(SkBits2Float(0x42f93333), SkBits2Float(0x42bcd811), SkBits2Float(0x42fa999a), SkBits2Float(0x42bcd25d), SkBits2Float(0x42fc0000), SkBits2Float(0x42bccf10));  // 124.6f, 94.422f, 125.3f, 94.4109f, 126, 94.4044f
+path.cubicTo(SkBits2Float(0x42fd6666), SkBits2Float(0x42bccbc3), SkBits2Float(0x42fecccd), SkBits2Float(0x42bcc95b), SkBits2Float(0x4300199a), SkBits2Float(0x42bcc6dc));  // 126.7f, 94.398f, 127.4f, 94.3933f, 128.1f, 94.3884f
+path.cubicTo(SkBits2Float(0x4300cccd), SkBits2Float(0x42bcc45c), SkBits2Float(0x43018000), SkBits2Float(0x42bcc0ec), SkBits2Float(0x43023333), SkBits2Float(0x42bcc013));  // 128.8f, 94.3835f, 129.5f, 94.3768f, 130.2f, 94.3751f
+path.cubicTo(SkBits2Float(0x4302e666), SkBits2Float(0x42bcbf3a), SkBits2Float(0x4303999a), SkBits2Float(0x42bcc0b0), SkBits2Float(0x43044ccd), SkBits2Float(0x42bcc1c5));  // 130.9f, 94.3735f, 131.6f, 94.3763f, 132.3f, 94.3785f
+path.cubicTo(SkBits2Float(0x43050000), SkBits2Float(0x42bcc2db), SkBits2Float(0x4305b333), SkBits2Float(0x42bcc5bc), SkBits2Float(0x43066666), SkBits2Float(0x42bcc695));  // 133, 94.3806f, 133.7f, 94.3862f, 134.4f, 94.3879f
+path.cubicTo(SkBits2Float(0x4307199a), SkBits2Float(0x42bcc76e), SkBits2Float(0x4307cccd), SkBits2Float(0x42bcc688), SkBits2Float(0x43088000), SkBits2Float(0x42bcc6dc));  // 135.1f, 94.3895f, 135.8f, 94.3878f, 136.5f, 94.3884f
+path.cubicTo(SkBits2Float(0x43093333), SkBits2Float(0x42bcc730), SkBits2Float(0x4309e666), SkBits2Float(0x42bcc89a), SkBits2Float(0x430a999a), SkBits2Float(0x42bcc88e));  // 137.2f, 94.389f, 137.9f, 94.3918f, 138.6f, 94.3917f
+path.cubicTo(SkBits2Float(0x430b4ccd), SkBits2Float(0x42bcc882), SkBits2Float(0x430c0000), SkBits2Float(0x42bcc76e), SkBits2Float(0x430cb333), SkBits2Float(0x42bcc695));  // 139.3f, 94.3916f, 140, 94.3895f, 140.7f, 94.3879f
+path.cubicTo(SkBits2Float(0x430d6666), SkBits2Float(0x42bcc5bc), SkBits2Float(0x430e199a), SkBits2Float(0x42bcc445), SkBits2Float(0x430ecccd), SkBits2Float(0x42bcc378));  // 141.4f, 94.3862f, 142.1f, 94.3833f, 142.8f, 94.3818f
+path.cubicTo(SkBits2Float(0x430f8000), SkBits2Float(0x42bcc2aa), SkBits2Float(0x43103333), SkBits2Float(0x42bcc32f), SkBits2Float(0x4310e666), SkBits2Float(0x42bcc1c5));  // 143.5f, 94.3802f, 144.2f, 94.3812f, 144.9f, 94.3785f
+path.cubicTo(SkBits2Float(0x4311999a), SkBits2Float(0x42bcc05c), SkBits2Float(0x43124ccd), SkBits2Float(0x42bcbd88), SkBits2Float(0x43130000), SkBits2Float(0x42bcbafd));  // 145.6f, 94.3757f, 146.3f, 94.3702f, 147, 94.3652f
+path.cubicTo(SkBits2Float(0x4313b333), SkBits2Float(0x42bcb872), SkBits2Float(0x43146666), SkBits2Float(0x42bcb50e), SkBits2Float(0x4315199a), SkBits2Float(0x42bcb282));  // 147.7f, 94.3602f, 148.4f, 94.3536f, 149.1f, 94.3486f
+path.cubicTo(SkBits2Float(0x4315cccd), SkBits2Float(0x42bcaff7), SkBits2Float(0x43168000), SkBits2Float(0x42bcac56), SkBits2Float(0x43173333), SkBits2Float(0x42bcabba));  // 149.8f, 94.3437f, 150.5f, 94.3366f, 151.2f, 94.3354f
+path.cubicTo(SkBits2Float(0x4317e666), SkBits2Float(0x42bcab1d), SkBits2Float(0x4318999a), SkBits2Float(0x42bcadfe), SkBits2Float(0x43194ccd), SkBits2Float(0x42bcaed8));  // 151.9f, 94.3342f, 152.6f, 94.3398f, 153.3f, 94.3415f
+path.cubicTo(SkBits2Float(0x431a0000), SkBits2Float(0x42bcafb1), SkBits2Float(0x431ab333), SkBits2Float(0x42bcb034), SkBits2Float(0x431b6666), SkBits2Float(0x42bcb0d0));  // 154, 94.3431f, 154.7f, 94.3441f, 155.4f, 94.3453f
+path.cubicTo(SkBits2Float(0x431c199a), SkBits2Float(0x42bcb16d), SkBits2Float(0x431ccccd), SkBits2Float(0x42bcb119), SkBits2Float(0x431d8000), SkBits2Float(0x42bcb282));  // 156.1f, 94.3465f, 156.8f, 94.3459f, 157.5f, 94.3486f
+path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42bcb3ec), SkBits2Float(0x431ee666), SkBits2Float(0x42bcb708), SkBits2Float(0x431f999a), SkBits2Float(0x42bcb94b));  // 158.2f, 94.3514f, 158.9f, 94.3575f, 159.6f, 94.3619f
+path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42bcbb8e), SkBits2Float(0x43210000), SkBits2Float(0x42bcbef2), SkBits2Float(0x4321b333), SkBits2Float(0x42bcc013));  // 160.3f, 94.3663f, 161, 94.3729f, 161.7f, 94.3751f
+path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42bcc135), SkBits2Float(0x4323199a), SkBits2Float(0x42bcbfe3), SkBits2Float(0x4323cccd), SkBits2Float(0x42bcc013));  // 162.4f, 94.3774f, 163.1f, 94.3748f, 163.8f, 94.3751f
+path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x42bcc044), SkBits2Float(0x43253333), SkBits2Float(0x42bcbf19), SkBits2Float(0x4325e666), SkBits2Float(0x42bcc138));  // 164.5f, 94.3755f, 165.2f, 94.3732f, 165.9f, 94.3774f
+path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x42bcc358), SkBits2Float(0x43274ccd), SkBits2Float(0x42bcc7c6), SkBits2Float(0x43280000), SkBits2Float(0x42bcccd1));  // 166.6f, 94.3815f, 167.3f, 94.3902f, 168, 94.4f
+path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x42bcd1db), SkBits2Float(0x43296666), SkBits2Float(0x42bcd9b9), SkBits2Float(0x432a199a), SkBits2Float(0x42bcdf78));  // 168.7f, 94.4099f, 169.4f, 94.4252f, 170.1f, 94.4365f
+path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x42bce536), SkBits2Float(0x432b8000), SkBits2Float(0x42bceb84), SkBits2Float(0x432c3333), SkBits2Float(0x42bcef48));  // 170.8f, 94.4477f, 171.5f, 94.46f, 172.2f, 94.4673f
+path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x42bcf30c), SkBits2Float(0x432d999a), SkBits2Float(0x42bcf3ce), SkBits2Float(0x432e4ccd), SkBits2Float(0x42bcf611));  // 172.9f, 94.4747f, 173.6f, 94.4762f, 174.3f, 94.4806f
+path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x42bcf853), SkBits2Float(0x432fb333), SkBits2Float(0x42bcfb31), SkBits2Float(0x43306666), SkBits2Float(0x42bcfcd9));  // 175, 94.485f, 175.7f, 94.4906f, 176.4f, 94.4938f
+path.lineTo(SkBits2Float(0x43306666), SkBits2Float(0x42bd0000));  // 176.4f, 94.5f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000));  // 88.2f, 94.5f
+path.close();
+path.moveTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000));  // 193.2f, 94.5f
+path.lineTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000));  // 193.2f, 94.5f
+path.cubicTo(SkBits2Float(0x4341e666), SkBits2Float(0x42bd0000), SkBits2Float(0x4342999a), SkBits2Float(0x42bd0549), SkBits2Float(0x43434ccd), SkBits2Float(0x42bd0000));  // 193.9f, 94.5f, 194.6f, 94.5103f, 195.3f, 94.5f
+path.cubicTo(SkBits2Float(0x43440000), SkBits2Float(0x42bcfab7), SkBits2Float(0x4344b333), SkBits2Float(0x42bcec20), SkBits2Float(0x43456666), SkBits2Float(0x42bce04c));  // 196, 94.4897f, 196.7f, 94.4612f, 197.4f, 94.4381f
+path.cubicTo(SkBits2Float(0x4346199a), SkBits2Float(0x42bcd477), SkBits2Float(0x4346cccd), SkBits2Float(0x42bcc14e), SkBits2Float(0x43478000), SkBits2Float(0x42bcb904));  // 198.1f, 94.415f, 198.8f, 94.3775f, 199.5f, 94.3614f
+path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42bcb0ba), SkBits2Float(0x4348e666), SkBits2Float(0x42bcaed9), SkBits2Float(0x4349999a), SkBits2Float(0x42bcae91));  // 200.2f, 94.3452f, 200.9f, 94.3415f, 201.6f, 94.341f
+path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42bcae49), SkBits2Float(0x434b0000), SkBits2Float(0x42bcb23c), SkBits2Float(0x434bb333), SkBits2Float(0x42bcb752));  // 202.3f, 94.3404f, 203, 94.3481f, 203.7f, 94.358f
+path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x42bcbc69), SkBits2Float(0x434d199a), SkBits2Float(0x42bcc612), SkBits2Float(0x434dcccd), SkBits2Float(0x42bccd17));  // 204.4f, 94.368f, 205.1f, 94.3869f, 205.8f, 94.4006f
+path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x42bcd41c), SkBits2Float(0x434f3333), SkBits2Float(0x42bcdc5a), SkBits2Float(0x434fe666), SkBits2Float(0x42bce171));  // 206.5f, 94.4143f, 207.2f, 94.4304f, 207.9f, 94.4403f
+path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x42bce687), SkBits2Float(0x43514ccd), SkBits2Float(0x42bceb0d), SkBits2Float(0x43520000), SkBits2Float(0x42bceb9d));  // 208.6f, 94.4502f, 209.3f, 94.4591f, 210, 94.4602f
+path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x42bcec2e), SkBits2Float(0x43536666), SkBits2Float(0x42bcea70), SkBits2Float(0x4354199a), SkBits2Float(0x42bce4d5));  // 210.7f, 94.4613f, 211.4f, 94.4579f, 212.1f, 94.4469f
+path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x42bcdf39), SkBits2Float(0x43558000), SkBits2Float(0x42bcd432), SkBits2Float(0x43563333), SkBits2Float(0x42bcc9f9));  // 212.8f, 94.436f, 213.5f, 94.4144f, 214.2f, 94.3945f
+path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x42bcbfc1), SkBits2Float(0x4357999a), SkBits2Float(0x42bcb9bc), SkBits2Float(0x43584ccd), SkBits2Float(0x42bca782));  // 214.9f, 94.3745f, 215.6f, 94.3628f, 216.3f, 94.3272f
+path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x42bc9548), SkBits2Float(0x4359b333), SkBits2Float(0x42bc76fe), SkBits2Float(0x435a6666), SkBits2Float(0x42bc5c9e));  // 217, 94.2916f, 217.7f, 94.2324f, 218.4f, 94.1809f
+path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x42bc423e), SkBits2Float(0x435bcccd), SkBits2Float(0x42bc19b0), SkBits2Float(0x435c8000), SkBits2Float(0x42bc0940));  // 219.1f, 94.1294f, 219.8f, 94.0502f, 220.5f, 94.0181f
+path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42bbf8cf), SkBits2Float(0x435de666), SkBits2Float(0x42bbfbf7), SkBits2Float(0x435e999a), SkBits2Float(0x42bbf9fc));  // 221.2f, 93.986f, 221.9f, 93.9921f, 222.6f, 93.9883f
+path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x42bbf802), SkBits2Float(0x43600000), SkBits2Float(0x42bbfad6), SkBits2Float(0x4360b333), SkBits2Float(0x42bbfd61));  // 223.3f, 93.9844f, 224, 93.9899f, 224.7f, 93.9949f
+path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42bbffec), SkBits2Float(0x4362199a), SkBits2Float(0x42bc06fd), SkBits2Float(0x4362cccd), SkBits2Float(0x42bc0940));  // 225.4f, 93.9998f, 226.1f, 94.0136f, 226.8f, 94.0181f
+path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x42bc0b82), SkBits2Float(0x43643333), SkBits2Float(0x42bc0c13), SkBits2Float(0x4364e666), SkBits2Float(0x42bc0af2));  // 227.5f, 94.0225f, 228.2f, 94.0236f, 228.9f, 94.0214f
+path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x42bc09d0), SkBits2Float(0x43664ccd), SkBits2Float(0x42bc019e), SkBits2Float(0x43670000), SkBits2Float(0x42bc0277));  // 229.6f, 94.0192f, 230.3f, 94.0032f, 231, 94.0048f
+path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x42bc0350), SkBits2Float(0x43686666), SkBits2Float(0x42bc0b3a), SkBits2Float(0x4369199a), SkBits2Float(0x42bc1008));  // 231.7f, 94.0065f, 232.4f, 94.0219f, 233.1f, 94.0313f
+path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x42bc14d6), SkBits2Float(0x436a8000), SkBits2Float(0x42bc1d08), SkBits2Float(0x436b3333), SkBits2Float(0x42bc1f4b));  // 233.8f, 94.0407f, 234.5f, 94.0567f, 235.2f, 94.0611f
+path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x42bc218e), SkBits2Float(0x436c999a), SkBits2Float(0x42bc1e2a), SkBits2Float(0x436d4ccd), SkBits2Float(0x42bc1d99));  // 235.9f, 94.0655f, 236.6f, 94.0589f, 237.3f, 94.0578f
+path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42bc1d08), SkBits2Float(0x436eb333), SkBits2Float(0x42bc1cc0), SkBits2Float(0x436f6666), SkBits2Float(0x42bc1be7));  // 238, 94.0567f, 238.7f, 94.0562f, 239.4f, 94.0545f
+path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42bc1b0e), SkBits2Float(0x4370cccd), SkBits2Float(0x42bc195c), SkBits2Float(0x43718000), SkBits2Float(0x42bc1883));  // 240.1f, 94.0528f, 240.8f, 94.0495f, 241.5f, 94.0479f
+path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x42bc17aa), SkBits2Float(0x4372e666), SkBits2Float(0x42bc1719), SkBits2Float(0x4373999a), SkBits2Float(0x42bc16d0));  // 242.2f, 94.0462f, 242.9f, 94.0451f, 243.6f, 94.0446f
+path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42bc1688), SkBits2Float(0x43750000), SkBits2Float(0x42bc1719), SkBits2Float(0x4375b333), SkBits2Float(0x42bc16d0));  // 244.3f, 94.044f, 245, 94.0451f, 245.7f, 94.0446f
+path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x42bc1688), SkBits2Float(0x4377199a), SkBits2Float(0x42bc1567), SkBits2Float(0x4377cccd), SkBits2Float(0x42bc151e));  // 246.4f, 94.044f, 247.1f, 94.0418f, 247.8f, 94.0412f
+path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42bc14d6), SkBits2Float(0x43793333), SkBits2Float(0x42bc148e), SkBits2Float(0x4379e666), SkBits2Float(0x42bc151e));  // 248.5f, 94.0407f, 249.2f, 94.0401f, 249.9f, 94.0412f
+path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42bc15af), SkBits2Float(0x437b4ccd), SkBits2Float(0x42bc1761), SkBits2Float(0x437c0000), SkBits2Float(0x42bc1883));  // 250.6f, 94.0424f, 251.3f, 94.0457f, 252, 94.0479f
+path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42bc19a4), SkBits2Float(0x437d6666), SkBits2Float(0x42bc1b0e), SkBits2Float(0x437e199a), SkBits2Float(0x42bc1be7));  // 252.7f, 94.0501f, 253.4f, 94.0528f, 254.1f, 94.0545f
+path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42bc1cc0), SkBits2Float(0x437f8000), SkBits2Float(0x42bc1d99), SkBits2Float(0x4380199a), SkBits2Float(0x42bc1d99));  // 254.8f, 94.0562f, 255.5f, 94.0578f, 256.2f, 94.0578f
+path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x42bc1d99), SkBits2Float(0x4380cccd), SkBits2Float(0x42bc1d08), SkBits2Float(0x43812666), SkBits2Float(0x42bc1be7));  // 256.9f, 94.0578f, 257.6f, 94.0567f, 258.3f, 94.0545f
+path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x42bc1ac5), SkBits2Float(0x4381d99a), SkBits2Float(0x42bc183a), SkBits2Float(0x43823333), SkBits2Float(0x42bc16d0));  // 259, 94.0523f, 259.7f, 94.0473f, 260.4f, 94.0446f
+path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42bc1567), SkBits2Float(0x4382e666), SkBits2Float(0x42bc13fd), SkBits2Float(0x43834000), SkBits2Float(0x42bc136c));  // 261.1f, 94.0418f, 261.8f, 94.039f, 262.5f, 94.0379f
+path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42bc12dc), SkBits2Float(0x4383f333), SkBits2Float(0x42bc1324), SkBits2Float(0x43844ccd), SkBits2Float(0x42bc136c));  // 263.2f, 94.0368f, 263.9f, 94.0374f, 264.6f, 94.0379f
+path.cubicTo(SkBits2Float(0x4384a666), SkBits2Float(0x42bc13b5), SkBits2Float(0x43850000), SkBits2Float(0x42bc148e), SkBits2Float(0x4385599a), SkBits2Float(0x42bc151e));  // 265.3f, 94.0385f, 266, 94.0401f, 266.7f, 94.0412f
+path.cubicTo(SkBits2Float(0x4385b333), SkBits2Float(0x42bc15af), SkBits2Float(0x43860ccd), SkBits2Float(0x42bc1688), SkBits2Float(0x43866666), SkBits2Float(0x42bc16d0));  // 267.4f, 94.0424f, 268.1f, 94.044f, 268.8f, 94.0446f
+path.cubicTo(SkBits2Float(0x4386c000), SkBits2Float(0x42bc1719), SkBits2Float(0x4387199a), SkBits2Float(0x42bc1719), SkBits2Float(0x43877333), SkBits2Float(0x42bc16d0));  // 269.5f, 94.0451f, 270.2f, 94.0451f, 270.9f, 94.0446f
+path.cubicTo(SkBits2Float(0x4387cccd), SkBits2Float(0x42bc1688), SkBits2Float(0x43882666), SkBits2Float(0x42bc1567), SkBits2Float(0x43888000), SkBits2Float(0x42bc151e));  // 271.6f, 94.044f, 272.3f, 94.0418f, 273, 94.0412f
+path.cubicTo(SkBits2Float(0x4388d99a), SkBits2Float(0x42bc14d6), SkBits2Float(0x43893333), SkBits2Float(0x42bc151e), SkBits2Float(0x43898ccd), SkBits2Float(0x42bc151e));  // 273.7f, 94.0407f, 274.4f, 94.0412f, 275.1f, 94.0412f
+path.cubicTo(SkBits2Float(0x4389e666), SkBits2Float(0x42bc151e), SkBits2Float(0x438a4000), SkBits2Float(0x42bc1567), SkBits2Float(0x438a999a), SkBits2Float(0x42bc151e));  // 275.8f, 94.0412f, 276.5f, 94.0418f, 277.2f, 94.0412f
+path.cubicTo(SkBits2Float(0x438af333), SkBits2Float(0x42bc14d6), SkBits2Float(0x438b4ccd), SkBits2Float(0x42bc136c), SkBits2Float(0x438ba666), SkBits2Float(0x42bc136c));  // 277.9f, 94.0407f, 278.6f, 94.0379f, 279.3f, 94.0379f
+path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x42bc136c), SkBits2Float(0x438c599a), SkBits2Float(0x42bc152a), SkBits2Float(0x438cb333), SkBits2Float(0x42bc151e));  // 280, 94.0379f, 280.7f, 94.0413f, 281.4f, 94.0412f
+path.cubicTo(SkBits2Float(0x438d0ccd), SkBits2Float(0x42bc1513), SkBits2Float(0x438d6666), SkBits2Float(0x42bc14ef), SkBits2Float(0x438dc000), SkBits2Float(0x42bc1326));  // 282.1f, 94.0412f, 282.8f, 94.0409f, 283.5f, 94.0374f
+path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x42bc115c), SkBits2Float(0x438e7333), SkBits2Float(0x42bc0c17), SkBits2Float(0x438ecccd), SkBits2Float(0x42bc0a64));  // 284.2f, 94.0339f, 284.9f, 94.0236f, 285.6f, 94.0203f
+path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42bc08b2), SkBits2Float(0x438f8000), SkBits2Float(0x42bc0413), SkBits2Float(0x438fd99a), SkBits2Float(0x42bc08f9));  // 286.3f, 94.017f, 287, 94.008f, 287.7f, 94.0175f
+path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42bc0dde), SkBits2Float(0x43908ccd), SkBits2Float(0x42bc1476), SkBits2Float(0x4390e666), SkBits2Float(0x42bc27c6));  // 288.4f, 94.0271f, 289.1f, 94.04f, 289.8f, 94.0777f
+path.cubicTo(SkBits2Float(0x43914000), SkBits2Float(0x42bc3b15), SkBits2Float(0x4391999a), SkBits2Float(0x42bc5916), SkBits2Float(0x4391f333), SkBits2Float(0x42bc7cd6));  // 290.5f, 94.1154f, 291.2f, 94.174f, 291.9f, 94.2438f
+path.cubicTo(SkBits2Float(0x43924ccd), SkBits2Float(0x42bca096), SkBits2Float(0x4392a666), SkBits2Float(0x42bce868), SkBits2Float(0x43930000), SkBits2Float(0x42bcfe45));  // 292.6f, 94.3136f, 293.3f, 94.4539f, 294, 94.4966f
+path.cubicTo(SkBits2Float(0x4393599a), SkBits2Float(0x42bd1421), SkBits2Float(0x4393b333), SkBits2Float(0x42bcffb6), SkBits2Float(0x43940ccd), SkBits2Float(0x42bd0000));  // 294.7f, 94.5393f, 295.4f, 94.4994f, 296.1f, 94.5f
+path.lineTo(SkBits2Float(0x43940ccd), SkBits2Float(0x42bd0000));  // 296.1f, 94.5f
+path.lineTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000));  // 193.2f, 94.5f
+path.close();
+path.moveTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000));  // 344.4f, 94.5f
+path.lineTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000));  // 344.4f, 94.5f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42bd0000), SkBits2Float(0x43ace666), SkBits2Float(0x42bd03a2), SkBits2Float(0x43ad4000), SkBits2Float(0x42bd0000));  // 345.1f, 94.5f, 345.8f, 94.5071f, 346.5f, 94.5f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42bcfc5e), SkBits2Float(0x43adf333), SkBits2Float(0x42bd069a), SkBits2Float(0x43ae4ccd), SkBits2Float(0x42bcea32));  // 347.2f, 94.4929f, 347.9f, 94.5129f, 348.6f, 94.4574f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42bccdca), SkBits2Float(0x43af0000), SkBits2Float(0x42bc7b18), SkBits2Float(0x43af599a), SkBits2Float(0x42bc558f));  // 349.3f, 94.4019f, 350, 94.2404f, 350.7f, 94.1671f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42bc3005), SkBits2Float(0x43b00ccd), SkBits2Float(0x42bc16d2), SkBits2Float(0x43b06666), SkBits2Float(0x42bc08f9));  // 351.4f, 94.0938f, 352.1f, 94.0446f, 352.8f, 94.0175f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42bbfb20), SkBits2Float(0x43b1199a), SkBits2Float(0x42bc03d5), SkBits2Float(0x43b17333), SkBits2Float(0x42bc0277));  // 353.5f, 93.9905f, 354.2f, 94.0075f, 354.9f, 94.0048f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42bc0119), SkBits2Float(0x43b22666), SkBits2Float(0x42bc019e), SkBits2Float(0x43b28000), SkBits2Float(0x42bc00c5));  // 355.6f, 94.0021f, 356.3f, 94.0032f, 357, 94.0015f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x42bbffec), SkBits2Float(0x43b33333), SkBits2Float(0x42bbfdf1), SkBits2Float(0x43b38ccd), SkBits2Float(0x42bbfd61));  // 357.7f, 93.9998f, 358.4f, 93.996f, 359.1f, 93.9949f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42bbfcd0), SkBits2Float(0x43b44000), SkBits2Float(0x42bbfdf1), SkBits2Float(0x43b4999a), SkBits2Float(0x42bbfd61));  // 359.8f, 93.9938f, 360.5f, 93.996f, 361.2f, 93.9949f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x42bbfcd0), SkBits2Float(0x43b54ccd), SkBits2Float(0x42bbfad6), SkBits2Float(0x43b5a666), SkBits2Float(0x42bbf9fc));  // 361.9f, 93.9938f, 362.6f, 93.9899f, 363.3f, 93.9883f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42bbf923), SkBits2Float(0x43b6599a), SkBits2Float(0x42bbf8db), SkBits2Float(0x43b6b333), SkBits2Float(0x42bbf84a));  // 364, 93.9866f, 364.7f, 93.986f, 365.4f, 93.9849f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42bbf7ba), SkBits2Float(0x43b76666), SkBits2Float(0x42bbf729), SkBits2Float(0x43b7c000), SkBits2Float(0x42bbf698));  // 366.1f, 93.9838f, 366.8f, 93.9827f, 367.5f, 93.9816f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x42bbf608), SkBits2Float(0x43b87333), SkBits2Float(0x42bbf52e), SkBits2Float(0x43b8cccd), SkBits2Float(0x42bbf4e6));  // 368.2f, 93.9805f, 368.9f, 93.9789f, 369.6f, 93.9783f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x42bbf49e), SkBits2Float(0x43b98000), SkBits2Float(0x42bbf455), SkBits2Float(0x43b9d99a), SkBits2Float(0x42bbf4e6));  // 370.3f, 93.9778f, 371, 93.9772f, 371.7f, 93.9783f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x42bbf577), SkBits2Float(0x43ba8ccd), SkBits2Float(0x42bbf771), SkBits2Float(0x43bae666), SkBits2Float(0x42bbf84a));  // 372.4f, 93.9794f, 373.1f, 93.9833f, 373.8f, 93.9849f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x42bbf923), SkBits2Float(0x43bb999a), SkBits2Float(0x42bbf9b4), SkBits2Float(0x43bbf333), SkBits2Float(0x42bbf9fc));  // 374.5f, 93.9866f, 375.2f, 93.9877f, 375.9f, 93.9883f
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x42bbfa45), SkBits2Float(0x43bca666), SkBits2Float(0x42bbf9fc), SkBits2Float(0x43bd0000), SkBits2Float(0x42bbf9fc));  // 376.6f, 93.9888f, 377.3f, 93.9883f, 378, 93.9883f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42bbf9fc), SkBits2Float(0x43bdb333), SkBits2Float(0x42bbf9fc), SkBits2Float(0x43be0ccd), SkBits2Float(0x42bbf9fc));  // 378.7f, 93.9883f, 379.4f, 93.9883f, 380.1f, 93.9883f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42bbf9fc), SkBits2Float(0x43bec000), SkBits2Float(0x42bbf8db), SkBits2Float(0x43bf199a), SkBits2Float(0x42bbf9fc));  // 380.8f, 93.9883f, 381.5f, 93.986f, 382.2f, 93.9883f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42bbfb1e), SkBits2Float(0x43bfcccd), SkBits2Float(0x42bbfeca), SkBits2Float(0x43c02666), SkBits2Float(0x42bc00c5));  // 382.9f, 93.9905f, 383.6f, 93.9976f, 384.3f, 94.0015f
+path.cubicTo(SkBits2Float(0x43c08000), SkBits2Float(0x42bc02bf), SkBits2Float(0x43c0d99a), SkBits2Float(0x42bc0593), SkBits2Float(0x43c13333), SkBits2Float(0x42bc05db));  // 385, 94.0054f, 385.7f, 94.0109f, 386.4f, 94.0114f
+path.cubicTo(SkBits2Float(0x43c18ccd), SkBits2Float(0x42bc0624), SkBits2Float(0x43c1e666), SkBits2Float(0x42bc0308), SkBits2Float(0x43c24000), SkBits2Float(0x42bc0277));  // 387.1f, 94.012f, 387.8f, 94.0059f, 388.5f, 94.0048f
+path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42bc01e6), SkBits2Float(0x43c2f333), SkBits2Float(0x42bc022f), SkBits2Float(0x43c34ccd), SkBits2Float(0x42bc0277));  // 389.2f, 94.0037f, 389.9f, 94.0043f, 390.6f, 94.0048f
+path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42bc02bf), SkBits2Float(0x43c40000), SkBits2Float(0x42bc02bf), SkBits2Float(0x43c4599a), SkBits2Float(0x42bc0429));  // 391.3f, 94.0054f, 392, 94.0054f, 392.7f, 94.0081f
+path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42bc0593), SkBits2Float(0x43c50ccd), SkBits2Float(0x42bc08f7), SkBits2Float(0x43c56666), SkBits2Float(0x42bc0af2));  // 393.4f, 94.0109f, 394.1f, 94.0175f, 394.8f, 94.0214f
+path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42bc0cec), SkBits2Float(0x43c6199a), SkBits2Float(0x42bc0f2f), SkBits2Float(0x43c67333), SkBits2Float(0x42bc1008));  // 395.5f, 94.0252f, 396.2f, 94.0297f, 396.9f, 94.0313f
+path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x42bc10e1), SkBits2Float(0x43c72666), SkBits2Float(0x42bc0fc0), SkBits2Float(0x43c78000), SkBits2Float(0x42bc1008));  // 397.6f, 94.033f, 398.3f, 94.0308f, 399, 94.0313f
+path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x42bc1050), SkBits2Float(0x43c83333), SkBits2Float(0x42bc1172), SkBits2Float(0x43c88ccd), SkBits2Float(0x42bc11ba));  // 399.7f, 94.0319f, 400.4f, 94.0341f, 401.1f, 94.0346f
+path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x42bc1202), SkBits2Float(0x43c94000), SkBits2Float(0x42bc11ba), SkBits2Float(0x43c9999a), SkBits2Float(0x42bc11ba));  // 401.8f, 94.0352f, 402.5f, 94.0346f, 403.2f, 94.0346f
+path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42bc11ba), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42bc124b), SkBits2Float(0x43caa666), SkBits2Float(0x42bc11ba));  // 403.9f, 94.0346f, 404.6f, 94.0357f, 405.3f, 94.0346f
+path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42bc1129), SkBits2Float(0x43cb599a), SkBits2Float(0x42bc0f77), SkBits2Float(0x43cbb333), SkBits2Float(0x42bc0e56));  // 406, 94.0335f, 406.7f, 94.0302f, 407.4f, 94.028f
+path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x42bc0d34), SkBits2Float(0x43cc6666), SkBits2Float(0x42bc0b82), SkBits2Float(0x43ccc000), SkBits2Float(0x42bc0af2));  // 408.1f, 94.0258f, 408.8f, 94.0225f, 409.5f, 94.0214f
+path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x42bc0a61), SkBits2Float(0x43cd7333), SkBits2Float(0x42bc0b3a), SkBits2Float(0x43cdcccd), SkBits2Float(0x42bc0af2));  // 410.2f, 94.0203f, 410.9f, 94.0219f, 411.6f, 94.0214f
+path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x42bc0aa9), SkBits2Float(0x43ce8000), SkBits2Float(0x42bc0aa9), SkBits2Float(0x43ced99a), SkBits2Float(0x42bc0940));  // 412.3f, 94.0208f, 413, 94.0208f, 413.7f, 94.0181f
+path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42bc07d6), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42bc0502), SkBits2Float(0x43cfe666), SkBits2Float(0x42bc0277));  // 414.4f, 94.0153f, 415.1f, 94.0098f, 415.8f, 94.0048f
+path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42bbffec), SkBits2Float(0x43d0999a), SkBits2Float(0x42bbfc88), SkBits2Float(0x43d0f333), SkBits2Float(0x42bbf9fc));  // 416.5f, 93.9998f, 417.2f, 93.9932f, 417.9f, 93.9883f
+path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42bbf771), SkBits2Float(0x43d1a666), SkBits2Float(0x42bbf4e6), SkBits2Float(0x43d20000), SkBits2Float(0x42bbf334));  // 418.6f, 93.9833f, 419.3f, 93.9783f, 420, 93.975f
+path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42bbf182), SkBits2Float(0x43d2b333), SkBits2Float(0x42bbee66), SkBits2Float(0x43d30ccd), SkBits2Float(0x42bbefd0));  // 420.7f, 93.9717f, 421.4f, 93.9656f, 422.1f, 93.9684f
+path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x42bbf13a), SkBits2Float(0x43d3c000), SkBits2Float(0x42bbf52e), SkBits2Float(0x43d4199a), SkBits2Float(0x42bbfbaf));  // 422.8f, 93.9711f, 423.5f, 93.9789f, 424.2f, 93.9916f
+path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42bc022f), SkBits2Float(0x43d4cccd), SkBits2Float(0x42bc1014), SkBits2Float(0x43d52666), SkBits2Float(0x42bc16d0));  // 424.9f, 94.0043f, 425.6f, 94.0314f, 426.3f, 94.0446f
+path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42bc1d8d), SkBits2Float(0x43d5d99a), SkBits2Float(0x42bc1de3), SkBits2Float(0x43d63333), SkBits2Float(0x42bc241b));  // 427, 94.0577f, 427.7f, 94.0584f, 428.4f, 94.0705f
+path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42bc2a53), SkBits2Float(0x43d6e666), SkBits2Float(0x42bc30c5), SkBits2Float(0x43d74000), SkBits2Float(0x42bc3c1f));  // 429.1f, 94.0827f, 429.8f, 94.0953f, 430.5f, 94.1174f
+path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x42bc4779), SkBits2Float(0x43d7f333), SkBits2Float(0x42bc6283), SkBits2Float(0x43d84ccd), SkBits2Float(0x42bc6836));  // 431.2f, 94.1396f, 431.9f, 94.1924f, 432.6f, 94.2035f
+path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x42bc6de9), SkBits2Float(0x43d90000), SkBits2Float(0x42bc67e0), SkBits2Float(0x43d9599a), SkBits2Float(0x42bc5e50));  // 433.3f, 94.2147f, 434, 94.2029f, 434.7f, 94.1842f
+path.cubicTo(SkBits2Float(0x43d9b333), SkBits2Float(0x42bc54c0), SkBits2Float(0x43da0ccd), SkBits2Float(0x42bc3a23), SkBits2Float(0x43da6666), SkBits2Float(0x42bc2ed5));  // 435.4f, 94.1655f, 436.1f, 94.1135f, 436.8f, 94.0915f
+path.cubicTo(SkBits2Float(0x43dac000), SkBits2Float(0x42bc2387), SkBits2Float(0x43db199a), SkBits2Float(0x42bc1e34), SkBits2Float(0x43db7333), SkBits2Float(0x42bc1a7b));  // 437.5f, 94.0694f, 438.2f, 94.059f, 438.9f, 94.0517f
+path.cubicTo(SkBits2Float(0x43dbcccd), SkBits2Float(0x42bc16c3), SkBits2Float(0x43dc2666), SkBits2Float(0x42bc18a6), SkBits2Float(0x43dc8000), SkBits2Float(0x42bc1883));  // 439.6f, 94.0445f, 440.3f, 94.0481f, 441, 94.0479f
+path.cubicTo(SkBits2Float(0x43dcd99a), SkBits2Float(0x42bc185f), SkBits2Float(0x43dd3333), SkBits2Float(0x42bc19f0), SkBits2Float(0x43dd8ccd), SkBits2Float(0x42bc19a8));  // 441.7f, 94.0476f, 442.4f, 94.0507f, 443.1f, 94.0501f
+path.cubicTo(SkBits2Float(0x43dde666), SkBits2Float(0x42bc195f), SkBits2Float(0x43de4000), SkBits2Float(0x42bc15d4), SkBits2Float(0x43de999a), SkBits2Float(0x42bc16d0));  // 443.8f, 94.0496f, 444.5f, 94.0426f, 445.2f, 94.0446f
+path.cubicTo(SkBits2Float(0x43def333), SkBits2Float(0x42bc17cd), SkBits2Float(0x43df4ccd), SkBits2Float(0x42bc11a1), SkBits2Float(0x43dfa666), SkBits2Float(0x42bc1f92));  // 445.9f, 94.0465f, 446.6f, 94.0344f, 447.3f, 94.0617f
+path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x42bc2d83), SkBits2Float(0x43e0599a), SkBits2Float(0x42bc478f), SkBits2Float(0x43e0b333), SkBits2Float(0x42bc6a76));  // 448, 94.0889f, 448.7f, 94.1398f, 449.4f, 94.2079f
+path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42bc8d5c), SkBits2Float(0x43e16666), SkBits2Float(0x42bcd80e), SkBits2Float(0x43e1c000), SkBits2Float(0x42bcf0fa));  // 450.1f, 94.2761f, 450.8f, 94.422f, 451.5f, 94.4707f
+path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x42bd09e7), SkBits2Float(0x43e27333), SkBits2Float(0x42bd0449), SkBits2Float(0x43e2cccd), SkBits2Float(0x42bd0000));  // 452.2f, 94.5193f, 452.9f, 94.5084f, 453.6f, 94.5f
+path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x42bcfbb7), SkBits2Float(0x43e38000), SkBits2Float(0x42bcebe7), SkBits2Float(0x43e3d99a), SkBits2Float(0x42bcd744));  // 454.3f, 94.4916f, 455, 94.4607f, 455.7f, 94.4204f
+path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x42bcc2a0), SkBits2Float(0x43e48ccd), SkBits2Float(0x42bc8dc8), SkBits2Float(0x43e4e666), SkBits2Float(0x42bc842c));  // 456.4f, 94.3801f, 457.1f, 94.2769f, 457.8f, 94.2581f
+path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42bc7a90), SkBits2Float(0x43e5999a), SkBits2Float(0x42bc8f32), SkBits2Float(0x43e5f333), SkBits2Float(0x42bc9d9c));  // 458.5f, 94.2394f, 459.2f, 94.2797f, 459.9f, 94.3078f
+path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42bcac06), SkBits2Float(0x43e6a666), SkBits2Float(0x42bcca42), SkBits2Float(0x43e70000), SkBits2Float(0x42bcdaa8));  // 460.6f, 94.336f, 461.3f, 94.395f, 462, 94.4271f
+path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42bceb0e), SkBits2Float(0x43e7b333), SkBits2Float(0x42bcf9c7), SkBits2Float(0x43e80ccd), SkBits2Float(0x42bd0000));  // 462.7f, 94.4591f, 463.4f, 94.4878f, 464.1f, 94.5f
+path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42bd0639), SkBits2Float(0x43e8c000), SkBits2Float(0x42bd010b), SkBits2Float(0x43e9199a), SkBits2Float(0x42bd0000));  // 464.8f, 94.5122f, 465.5f, 94.502f, 466.2f, 94.5f
+path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42bcfef5), SkBits2Float(0x43e9cccd), SkBits2Float(0x42bcf9bb), SkBits2Float(0x43ea2666), SkBits2Float(0x42bcf9bb));  // 466.9f, 94.498f, 467.6f, 94.4878f, 468.3f, 94.4878f
+path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42bcf9bb), SkBits2Float(0x43ead99a), SkBits2Float(0x42bcfef5), SkBits2Float(0x43eb3333), SkBits2Float(0x42bd0000));  // 469, 94.4878f, 469.7f, 94.498f, 470.4f, 94.5f
+path.lineTo(SkBits2Float(0x43eb3333), SkBits2Float(0x42bd0000));  // 470.4f, 94.5f
+path.lineTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000));  // 344.4f, 94.5f
+path.close();
+    return path;
+}
+
+static SkPath path4() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000));  // 88.2f, 94.5f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000));  // 88.2f, 94.5f
+path.cubicTo(SkBits2Float(0x42b1cccd), SkBits2Float(0x42bd0000), SkBits2Float(0x42b33333), SkBits2Float(0x42bcda8d), SkBits2Float(0x42b4999a), SkBits2Float(0x42bd0000));  // 88.9f, 94.5f, 89.6f, 94.4269f, 90.3f, 94.5f
+path.cubicTo(SkBits2Float(0x42b60000), SkBits2Float(0x42bd2573), SkBits2Float(0x42b76666), SkBits2Float(0x42bdba68), SkBits2Float(0x42b8cccd), SkBits2Float(0x42bde0b5));  // 91, 94.5731f, 91.7f, 94.8641f, 92.4f, 94.9389f
+path.cubicTo(SkBits2Float(0x42ba3333), SkBits2Float(0x42be0701), SkBits2Float(0x42bb999a), SkBits2Float(0x42bde462), SkBits2Float(0x42bd0000), SkBits2Float(0x42bde5cb));  // 93.1f, 95.0137f, 93.8f, 94.9461f, 94.5f, 94.9488f
+path.cubicTo(SkBits2Float(0x42be6666), SkBits2Float(0x42bde735), SkBits2Float(0x42bfcccd), SkBits2Float(0x42bde80e), SkBits2Float(0x42c13333), SkBits2Float(0x42bde930));  // 95.2f, 94.9516f, 95.9f, 94.9532f, 96.6f, 94.9554f
+path.cubicTo(SkBits2Float(0x42c2999a), SkBits2Float(0x42bdea51), SkBits2Float(0x42c40000), SkBits2Float(0x42bdec03), SkBits2Float(0x42c56666), SkBits2Float(0x42bdec94));  // 97.3f, 94.9576f, 98, 94.961f, 98.7f, 94.9621f
+path.cubicTo(SkBits2Float(0x42c6cccd), SkBits2Float(0x42bded24), SkBits2Float(0x42c83333), SkBits2Float(0x42bded19), SkBits2Float(0x42c9999a), SkBits2Float(0x42bdec94));  // 99.4f, 94.9632f, 100.1f, 94.9631f, 100.8f, 94.9621f
+path.cubicTo(SkBits2Float(0x42cb0000), SkBits2Float(0x42bdec0f), SkBits2Float(0x42cc6666), SkBits2Float(0x42bdea38), SkBits2Float(0x42cdcccd), SkBits2Float(0x42bde976));  // 101.5f, 94.9611f, 102.2f, 94.9575f, 102.9f, 94.956f
+path.cubicTo(SkBits2Float(0x42cf3333), SkBits2Float(0x42bde8b5), SkBits2Float(0x42d0999a), SkBits2Float(0x42bde853), SkBits2Float(0x42d20000), SkBits2Float(0x42bde80b));  // 103.6f, 94.9545f, 104.3f, 94.9538f, 105, 94.9532f
+path.cubicTo(SkBits2Float(0x42d36666), SkBits2Float(0x42bde7c2), SkBits2Float(0x42d4cccd), SkBits2Float(0x42bde9d6), SkBits2Float(0x42d63333), SkBits2Float(0x42bde7c4));  // 105.7f, 94.9527f, 106.4f, 94.9567f, 107.1f, 94.9527f
+path.cubicTo(SkBits2Float(0x42d7999a), SkBits2Float(0x42bde5b2), SkBits2Float(0x42d90000), SkBits2Float(0x42bde19a), SkBits2Float(0x42da6666), SkBits2Float(0x42bddb9f));  // 107.8f, 94.9486f, 108.5f, 94.9406f, 109.2f, 94.9289f
+path.cubicTo(SkBits2Float(0x42dbcccd), SkBits2Float(0x42bdd5a3), SkBits2Float(0x42dd3333), SkBits2Float(0x42bdcc07), SkBits2Float(0x42de999a), SkBits2Float(0x42bdc3e1));  // 109.9f, 94.9173f, 110.6f, 94.8985f, 111.3f, 94.8826f
+path.cubicTo(SkBits2Float(0x42e00000), SkBits2Float(0x42bdbbba), SkBits2Float(0x42e16666), SkBits2Float(0x42bdb332), SkBits2Float(0x42e2cccd), SkBits2Float(0x42bdaab8));  // 112, 94.8667f, 112.7f, 94.85f, 113.4f, 94.8334f
+path.cubicTo(SkBits2Float(0x42e43333), SkBits2Float(0x42bda23d), SkBits2Float(0x42e5999a), SkBits2Float(0x42bd9b8e), SkBits2Float(0x42e70000), SkBits2Float(0x42bd9101));  // 114.1f, 94.8169f, 114.8f, 94.8038f, 115.5f, 94.7832f
+path.cubicTo(SkBits2Float(0x42e86666), SkBits2Float(0x42bd8675), SkBits2Float(0x42e9cccd), SkBits2Float(0x42bd79f9), SkBits2Float(0x42eb3333), SkBits2Float(0x42bd6b6c));  // 116.2f, 94.7626f, 116.9f, 94.7382f, 117.6f, 94.7098f
+path.cubicTo(SkBits2Float(0x42ec999a), SkBits2Float(0x42bd5cdf), SkBits2Float(0x42ee0000), SkBits2Float(0x42bd4608), SkBits2Float(0x42ef6666), SkBits2Float(0x42bd39b1));  // 118.3f, 94.6814f, 119, 94.6368f, 119.7f, 94.6127f
+path.cubicTo(SkBits2Float(0x42f0cccd), SkBits2Float(0x42bd2d5b), SkBits2Float(0x42f23333), SkBits2Float(0x42bd24cb), SkBits2Float(0x42f3999a), SkBits2Float(0x42bd2166));  // 120.4f, 94.5886f, 121.1f, 94.5719f, 121.8f, 94.5652f
+path.cubicTo(SkBits2Float(0x42f50000), SkBits2Float(0x42bd1e02), SkBits2Float(0x42f66666), SkBits2Float(0x42bd22c1), SkBits2Float(0x42f7cccd), SkBits2Float(0x42bd2558));  // 122.5f, 94.5586f, 123.2f, 94.5679f, 123.9f, 94.5729f
+path.cubicTo(SkBits2Float(0x42f93333), SkBits2Float(0x42bd27ef), SkBits2Float(0x42fa999a), SkBits2Float(0x42bd2da3), SkBits2Float(0x42fc0000), SkBits2Float(0x42bd30f0));  // 124.6f, 94.578f, 125.3f, 94.5891f, 126, 94.5956f
+path.cubicTo(SkBits2Float(0x42fd6666), SkBits2Float(0x42bd343d), SkBits2Float(0x42fecccd), SkBits2Float(0x42bd36a5), SkBits2Float(0x4300199a), SkBits2Float(0x42bd3924));  // 126.7f, 94.602f, 127.4f, 94.6067f, 128.1f, 94.6116f
+path.cubicTo(SkBits2Float(0x4300cccd), SkBits2Float(0x42bd3ba4), SkBits2Float(0x43018000), SkBits2Float(0x42bd3f14), SkBits2Float(0x43023333), SkBits2Float(0x42bd3fed));  // 128.8f, 94.6165f, 129.5f, 94.6232f, 130.2f, 94.6249f
+path.cubicTo(SkBits2Float(0x4302e666), SkBits2Float(0x42bd40c6), SkBits2Float(0x4303999a), SkBits2Float(0x42bd3f50), SkBits2Float(0x43044ccd), SkBits2Float(0x42bd3e3b));  // 130.9f, 94.6265f, 131.6f, 94.6237f, 132.3f, 94.6215f
+path.cubicTo(SkBits2Float(0x43050000), SkBits2Float(0x42bd3d25), SkBits2Float(0x4305b333), SkBits2Float(0x42bd3a44), SkBits2Float(0x43066666), SkBits2Float(0x42bd396b));  // 133, 94.6194f, 133.7f, 94.6138f, 134.4f, 94.6121f
+path.cubicTo(SkBits2Float(0x4307199a), SkBits2Float(0x42bd3892), SkBits2Float(0x4307cccd), SkBits2Float(0x42bd3978), SkBits2Float(0x43088000), SkBits2Float(0x42bd3924));  // 135.1f, 94.6105f, 135.8f, 94.6122f, 136.5f, 94.6116f
+path.cubicTo(SkBits2Float(0x43093333), SkBits2Float(0x42bd38d0), SkBits2Float(0x4309e666), SkBits2Float(0x42bd3766), SkBits2Float(0x430a999a), SkBits2Float(0x42bd3772));  // 137.2f, 94.611f, 137.9f, 94.6082f, 138.6f, 94.6083f
+path.cubicTo(SkBits2Float(0x430b4ccd), SkBits2Float(0x42bd377e), SkBits2Float(0x430c0000), SkBits2Float(0x42bd3892), SkBits2Float(0x430cb333), SkBits2Float(0x42bd396b));  // 139.3f, 94.6084f, 140, 94.6105f, 140.7f, 94.6121f
+path.cubicTo(SkBits2Float(0x430d6666), SkBits2Float(0x42bd3a44), SkBits2Float(0x430e199a), SkBits2Float(0x42bd3bbb), SkBits2Float(0x430ecccd), SkBits2Float(0x42bd3c88));  // 141.4f, 94.6138f, 142.1f, 94.6167f, 142.8f, 94.6182f
+path.cubicTo(SkBits2Float(0x430f8000), SkBits2Float(0x42bd3d56), SkBits2Float(0x43103333), SkBits2Float(0x42bd3cd1), SkBits2Float(0x4310e666), SkBits2Float(0x42bd3e3b));  // 143.5f, 94.6198f, 144.2f, 94.6188f, 144.9f, 94.6215f
+path.cubicTo(SkBits2Float(0x4311999a), SkBits2Float(0x42bd3fa4), SkBits2Float(0x43124ccd), SkBits2Float(0x42bd4278), SkBits2Float(0x43130000), SkBits2Float(0x42bd4503));  // 145.6f, 94.6243f, 146.3f, 94.6298f, 147, 94.6348f
+path.cubicTo(SkBits2Float(0x4313b333), SkBits2Float(0x42bd478e), SkBits2Float(0x43146666), SkBits2Float(0x42bd4af2), SkBits2Float(0x4315199a), SkBits2Float(0x42bd4d7e));  // 147.7f, 94.6398f, 148.4f, 94.6464f, 149.1f, 94.6514f
+path.cubicTo(SkBits2Float(0x4315cccd), SkBits2Float(0x42bd5009), SkBits2Float(0x43168000), SkBits2Float(0x42bd53aa), SkBits2Float(0x43173333), SkBits2Float(0x42bd5446));  // 149.8f, 94.6563f, 150.5f, 94.6634f, 151.2f, 94.6646f
+path.cubicTo(SkBits2Float(0x4317e666), SkBits2Float(0x42bd54e3), SkBits2Float(0x4318999a), SkBits2Float(0x42bd5202), SkBits2Float(0x43194ccd), SkBits2Float(0x42bd5128));  // 151.9f, 94.6658f, 152.6f, 94.6602f, 153.3f, 94.6585f
+path.cubicTo(SkBits2Float(0x431a0000), SkBits2Float(0x42bd504f), SkBits2Float(0x431ab333), SkBits2Float(0x42bd4fcc), SkBits2Float(0x431b6666), SkBits2Float(0x42bd4f30));  // 154, 94.6569f, 154.7f, 94.6559f, 155.4f, 94.6547f
+path.cubicTo(SkBits2Float(0x431c199a), SkBits2Float(0x42bd4e93), SkBits2Float(0x431ccccd), SkBits2Float(0x42bd4ee7), SkBits2Float(0x431d8000), SkBits2Float(0x42bd4d7e));  // 156.1f, 94.6535f, 156.8f, 94.6541f, 157.5f, 94.6514f
+path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42bd4c14), SkBits2Float(0x431ee666), SkBits2Float(0x42bd48f8), SkBits2Float(0x431f999a), SkBits2Float(0x42bd46b5));  // 158.2f, 94.6486f, 158.9f, 94.6425f, 159.6f, 94.6381f
+path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42bd4472), SkBits2Float(0x43210000), SkBits2Float(0x42bd410e), SkBits2Float(0x4321b333), SkBits2Float(0x42bd3fed));  // 160.3f, 94.6337f, 161, 94.6271f, 161.7f, 94.6249f
+path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42bd3ecb), SkBits2Float(0x4323199a), SkBits2Float(0x42bd401d), SkBits2Float(0x4323cccd), SkBits2Float(0x42bd3fed));  // 162.4f, 94.6226f, 163.1f, 94.6252f, 163.8f, 94.6249f
+path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x42bd3fbc), SkBits2Float(0x43253333), SkBits2Float(0x42bd40e7), SkBits2Float(0x4325e666), SkBits2Float(0x42bd3ec8));  // 164.5f, 94.6245f, 165.2f, 94.6268f, 165.9f, 94.6226f
+path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x42bd3ca8), SkBits2Float(0x43274ccd), SkBits2Float(0x42bd383a), SkBits2Float(0x43280000), SkBits2Float(0x42bd332f));  // 166.6f, 94.6185f, 167.3f, 94.6098f, 168, 94.6f
+path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x42bd2e25), SkBits2Float(0x43296666), SkBits2Float(0x42bd2647), SkBits2Float(0x432a199a), SkBits2Float(0x42bd2088));  // 168.7f, 94.5901f, 169.4f, 94.5748f, 170.1f, 94.5635f
+path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x42bd1aca), SkBits2Float(0x432b8000), SkBits2Float(0x42bd147c), SkBits2Float(0x432c3333), SkBits2Float(0x42bd10b8));  // 170.8f, 94.5523f, 171.5f, 94.54f, 172.2f, 94.5327f
+path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x42bd0cf4), SkBits2Float(0x432d999a), SkBits2Float(0x42bd0c32), SkBits2Float(0x432e4ccd), SkBits2Float(0x42bd09ef));  // 172.9f, 94.5253f, 173.6f, 94.5238f, 174.3f, 94.5194f
+path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x42bd07ad), SkBits2Float(0x432fb333), SkBits2Float(0x42bd04cf), SkBits2Float(0x43306666), SkBits2Float(0x42bd0327));  // 175, 94.515f, 175.7f, 94.5094f, 176.4f, 94.5062f
+path.lineTo(SkBits2Float(0x43306666), SkBits2Float(0x42bd0000));  // 176.4f, 94.5f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000));  // 88.2f, 94.5f
+path.close();
+path.moveTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000));  // 193.2f, 94.5f
+path.lineTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000));  // 193.2f, 94.5f
+path.cubicTo(SkBits2Float(0x4341e666), SkBits2Float(0x42bd0000), SkBits2Float(0x4342999a), SkBits2Float(0x42bcfab7), SkBits2Float(0x43434ccd), SkBits2Float(0x42bd0000));  // 193.9f, 94.5f, 194.6f, 94.4897f, 195.3f, 94.5f
+path.cubicTo(SkBits2Float(0x43440000), SkBits2Float(0x42bd0549), SkBits2Float(0x4344b333), SkBits2Float(0x42bd13e0), SkBits2Float(0x43456666), SkBits2Float(0x42bd1fb4));  // 196, 94.5103f, 196.7f, 94.5388f, 197.4f, 94.5619f
+path.cubicTo(SkBits2Float(0x4346199a), SkBits2Float(0x42bd2b89), SkBits2Float(0x4346cccd), SkBits2Float(0x42bd3eb2), SkBits2Float(0x43478000), SkBits2Float(0x42bd46fc));  // 198.1f, 94.585f, 198.8f, 94.6225f, 199.5f, 94.6386f
+path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42bd4f46), SkBits2Float(0x4348e666), SkBits2Float(0x42bd5127), SkBits2Float(0x4349999a), SkBits2Float(0x42bd516f));  // 200.2f, 94.6548f, 200.9f, 94.6585f, 201.6f, 94.659f
+path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42bd51b7), SkBits2Float(0x434b0000), SkBits2Float(0x42bd4dc4), SkBits2Float(0x434bb333), SkBits2Float(0x42bd48ae));  // 202.3f, 94.6596f, 203, 94.6519f, 203.7f, 94.642f
+path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x42bd4397), SkBits2Float(0x434d199a), SkBits2Float(0x42bd39ee), SkBits2Float(0x434dcccd), SkBits2Float(0x42bd32e9));  // 204.4f, 94.632f, 205.1f, 94.6131f, 205.8f, 94.5994f
+path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x42bd2be4), SkBits2Float(0x434f3333), SkBits2Float(0x42bd23a6), SkBits2Float(0x434fe666), SkBits2Float(0x42bd1e8f));  // 206.5f, 94.5857f, 207.2f, 94.5696f, 207.9f, 94.5597f
+path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x42bd1979), SkBits2Float(0x43514ccd), SkBits2Float(0x42bd14f3), SkBits2Float(0x43520000), SkBits2Float(0x42bd1463));  // 208.6f, 94.5498f, 209.3f, 94.5409f, 210, 94.5398f
+path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x42bd13d2), SkBits2Float(0x43536666), SkBits2Float(0x42bd1590), SkBits2Float(0x4354199a), SkBits2Float(0x42bd1b2b));  // 210.7f, 94.5387f, 211.4f, 94.5421f, 212.1f, 94.5531f
+path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x42bd20c7), SkBits2Float(0x43558000), SkBits2Float(0x42bd2bce), SkBits2Float(0x43563333), SkBits2Float(0x42bd3607));  // 212.8f, 94.564f, 213.5f, 94.5856f, 214.2f, 94.6055f
+path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x42bd403f), SkBits2Float(0x4357999a), SkBits2Float(0x42bd4644), SkBits2Float(0x43584ccd), SkBits2Float(0x42bd587e));  // 214.9f, 94.6255f, 215.6f, 94.6372f, 216.3f, 94.6728f
+path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x42bd6ab8), SkBits2Float(0x4359b333), SkBits2Float(0x42bd8902), SkBits2Float(0x435a6666), SkBits2Float(0x42bda362));  // 217, 94.7084f, 217.7f, 94.7676f, 218.4f, 94.8191f
+path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x42bdbdc2), SkBits2Float(0x435bcccd), SkBits2Float(0x42bde650), SkBits2Float(0x435c8000), SkBits2Float(0x42bdf6c0));  // 219.1f, 94.8706f, 219.8f, 94.9498f, 220.5f, 94.9819f
+path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42be0731), SkBits2Float(0x435de666), SkBits2Float(0x42be0409), SkBits2Float(0x435e999a), SkBits2Float(0x42be0604));  // 221.2f, 95.014f, 221.9f, 95.0079f, 222.6f, 95.0117f
+path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x42be07fe), SkBits2Float(0x43600000), SkBits2Float(0x42be052a), SkBits2Float(0x4360b333), SkBits2Float(0x42be029f));  // 223.3f, 95.0156f, 224, 95.0101f, 224.7f, 95.0051f
+path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42be0014), SkBits2Float(0x4362199a), SkBits2Float(0x42bdf903), SkBits2Float(0x4362cccd), SkBits2Float(0x42bdf6c0));  // 225.4f, 95.0002f, 226.1f, 94.9864f, 226.8f, 94.9819f
+path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x42bdf47e), SkBits2Float(0x43643333), SkBits2Float(0x42bdf3ed), SkBits2Float(0x4364e666), SkBits2Float(0x42bdf50e));  // 227.5f, 94.9775f, 228.2f, 94.9764f, 228.9f, 94.9786f
+path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x42bdf630), SkBits2Float(0x43664ccd), SkBits2Float(0x42bdfe62), SkBits2Float(0x43670000), SkBits2Float(0x42bdfd89));  // 229.6f, 94.9808f, 230.3f, 94.9968f, 231, 94.9952f
+path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x42bdfcb0), SkBits2Float(0x43686666), SkBits2Float(0x42bdf4c6), SkBits2Float(0x4369199a), SkBits2Float(0x42bdeff8));  // 231.7f, 94.9935f, 232.4f, 94.9781f, 233.1f, 94.9687f
+path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x42bdeb2a), SkBits2Float(0x436a8000), SkBits2Float(0x42bde2f8), SkBits2Float(0x436b3333), SkBits2Float(0x42bde0b5));  // 233.8f, 94.9593f, 234.5f, 94.9433f, 235.2f, 94.9389f
+path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x42bdde72), SkBits2Float(0x436c999a), SkBits2Float(0x42bde1d6), SkBits2Float(0x436d4ccd), SkBits2Float(0x42bde267));  // 235.9f, 94.9345f, 236.6f, 94.9411f, 237.3f, 94.9422f
+path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42bde2f8), SkBits2Float(0x436eb333), SkBits2Float(0x42bde340), SkBits2Float(0x436f6666), SkBits2Float(0x42bde419));  // 238, 94.9433f, 238.7f, 94.9438f, 239.4f, 94.9455f
+path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42bde4f2), SkBits2Float(0x4370cccd), SkBits2Float(0x42bde6a4), SkBits2Float(0x43718000), SkBits2Float(0x42bde77d));  // 240.1f, 94.9472f, 240.8f, 94.9505f, 241.5f, 94.9521f
+path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x42bde856), SkBits2Float(0x4372e666), SkBits2Float(0x42bde8e7), SkBits2Float(0x4373999a), SkBits2Float(0x42bde930));  // 242.2f, 94.9538f, 242.9f, 94.9549f, 243.6f, 94.9554f
+path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42bde978), SkBits2Float(0x43750000), SkBits2Float(0x42bde8e7), SkBits2Float(0x4375b333), SkBits2Float(0x42bde930));  // 244.3f, 94.956f, 245, 94.9549f, 245.7f, 94.9554f
+path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x42bde978), SkBits2Float(0x4377199a), SkBits2Float(0x42bdea99), SkBits2Float(0x4377cccd), SkBits2Float(0x42bdeae2));  // 246.4f, 94.956f, 247.1f, 94.9582f, 247.8f, 94.9588f
+path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42bdeb2a), SkBits2Float(0x43793333), SkBits2Float(0x42bdeb72), SkBits2Float(0x4379e666), SkBits2Float(0x42bdeae2));  // 248.5f, 94.9593f, 249.2f, 94.9599f, 249.9f, 94.9588f
+path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42bdea51), SkBits2Float(0x437b4ccd), SkBits2Float(0x42bde89f), SkBits2Float(0x437c0000), SkBits2Float(0x42bde77d));  // 250.6f, 94.9576f, 251.3f, 94.9543f, 252, 94.9521f
+path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42bde65c), SkBits2Float(0x437d6666), SkBits2Float(0x42bde4f2), SkBits2Float(0x437e199a), SkBits2Float(0x42bde419));  // 252.7f, 94.9499f, 253.4f, 94.9472f, 254.1f, 94.9455f
+path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42bde340), SkBits2Float(0x437f8000), SkBits2Float(0x42bde267), SkBits2Float(0x4380199a), SkBits2Float(0x42bde267));  // 254.8f, 94.9438f, 255.5f, 94.9422f, 256.2f, 94.9422f
+path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x42bde267), SkBits2Float(0x4380cccd), SkBits2Float(0x42bde2f8), SkBits2Float(0x43812666), SkBits2Float(0x42bde419));  // 256.9f, 94.9422f, 257.6f, 94.9433f, 258.3f, 94.9455f
+path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x42bde53b), SkBits2Float(0x4381d99a), SkBits2Float(0x42bde7c6), SkBits2Float(0x43823333), SkBits2Float(0x42bde930));  // 259, 94.9477f, 259.7f, 94.9527f, 260.4f, 94.9554f
+path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42bdea99), SkBits2Float(0x4382e666), SkBits2Float(0x42bdec03), SkBits2Float(0x43834000), SkBits2Float(0x42bdec94));  // 261.1f, 94.9582f, 261.8f, 94.961f, 262.5f, 94.9621f
+path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42bded24), SkBits2Float(0x4383f333), SkBits2Float(0x42bdecdc), SkBits2Float(0x43844ccd), SkBits2Float(0x42bdec94));  // 263.2f, 94.9632f, 263.9f, 94.9626f, 264.6f, 94.9621f
+path.cubicTo(SkBits2Float(0x4384a666), SkBits2Float(0x42bdec4b), SkBits2Float(0x43850000), SkBits2Float(0x42bdeb72), SkBits2Float(0x4385599a), SkBits2Float(0x42bdeae2));  // 265.3f, 94.9615f, 266, 94.9599f, 266.7f, 94.9588f
+path.cubicTo(SkBits2Float(0x4385b333), SkBits2Float(0x42bdea51), SkBits2Float(0x43860ccd), SkBits2Float(0x42bde978), SkBits2Float(0x43866666), SkBits2Float(0x42bde930));  // 267.4f, 94.9576f, 268.1f, 94.956f, 268.8f, 94.9554f
+path.cubicTo(SkBits2Float(0x4386c000), SkBits2Float(0x42bde8e7), SkBits2Float(0x4387199a), SkBits2Float(0x42bde8e7), SkBits2Float(0x43877333), SkBits2Float(0x42bde930));  // 269.5f, 94.9549f, 270.2f, 94.9549f, 270.9f, 94.9554f
+path.cubicTo(SkBits2Float(0x4387cccd), SkBits2Float(0x42bde978), SkBits2Float(0x43882666), SkBits2Float(0x42bdea99), SkBits2Float(0x43888000), SkBits2Float(0x42bdeae2));  // 271.6f, 94.956f, 272.3f, 94.9582f, 273, 94.9588f
+path.cubicTo(SkBits2Float(0x4388d99a), SkBits2Float(0x42bdeb2a), SkBits2Float(0x43893333), SkBits2Float(0x42bdeae2), SkBits2Float(0x43898ccd), SkBits2Float(0x42bdeae2));  // 273.7f, 94.9593f, 274.4f, 94.9588f, 275.1f, 94.9588f
+path.cubicTo(SkBits2Float(0x4389e666), SkBits2Float(0x42bdeae2), SkBits2Float(0x438a4000), SkBits2Float(0x42bdea99), SkBits2Float(0x438a999a), SkBits2Float(0x42bdeae2));  // 275.8f, 94.9588f, 276.5f, 94.9582f, 277.2f, 94.9588f
+path.cubicTo(SkBits2Float(0x438af333), SkBits2Float(0x42bdeb2a), SkBits2Float(0x438b4ccd), SkBits2Float(0x42bdec94), SkBits2Float(0x438ba666), SkBits2Float(0x42bdec94));  // 277.9f, 94.9593f, 278.6f, 94.9621f, 279.3f, 94.9621f
+path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x42bdec94), SkBits2Float(0x438c599a), SkBits2Float(0x42bdead6), SkBits2Float(0x438cb333), SkBits2Float(0x42bdeae2));  // 280, 94.9621f, 280.7f, 94.9587f, 281.4f, 94.9588f
+path.cubicTo(SkBits2Float(0x438d0ccd), SkBits2Float(0x42bdeaed), SkBits2Float(0x438d6666), SkBits2Float(0x42bdeb11), SkBits2Float(0x438dc000), SkBits2Float(0x42bdecda));  // 282.1f, 94.9588f, 282.8f, 94.9591f, 283.5f, 94.9626f
+path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x42bdeea4), SkBits2Float(0x438e7333), SkBits2Float(0x42bdf3e9), SkBits2Float(0x438ecccd), SkBits2Float(0x42bdf59c));  // 284.2f, 94.9661f, 284.9f, 94.9764f, 285.6f, 94.9797f
+path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42bdf74e), SkBits2Float(0x438f8000), SkBits2Float(0x42bdfbed), SkBits2Float(0x438fd99a), SkBits2Float(0x42bdf707));  // 286.3f, 94.983f, 287, 94.992f, 287.7f, 94.9825f
+path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42bdf222), SkBits2Float(0x43908ccd), SkBits2Float(0x42bdeb8a), SkBits2Float(0x4390e666), SkBits2Float(0x42bdd83a));  // 288.4f, 94.9729f, 289.1f, 94.96f, 289.8f, 94.9223f
+path.cubicTo(SkBits2Float(0x43914000), SkBits2Float(0x42bdc4eb), SkBits2Float(0x4391999a), SkBits2Float(0x42bda6ea), SkBits2Float(0x4391f333), SkBits2Float(0x42bd832a));  // 290.5f, 94.8846f, 291.2f, 94.826f, 291.9f, 94.7562f
+path.cubicTo(SkBits2Float(0x43924ccd), SkBits2Float(0x42bd5f6a), SkBits2Float(0x4392a666), SkBits2Float(0x42bd1798), SkBits2Float(0x43930000), SkBits2Float(0x42bd01bb));  // 292.6f, 94.6864f, 293.3f, 94.5461f, 294, 94.5034f
+path.cubicTo(SkBits2Float(0x4393599a), SkBits2Float(0x42bcebdf), SkBits2Float(0x4393b333), SkBits2Float(0x42bd004a), SkBits2Float(0x43940ccd), SkBits2Float(0x42bd0000));  // 294.7f, 94.4607f, 295.4f, 94.5006f, 296.1f, 94.5f
+path.lineTo(SkBits2Float(0x43940ccd), SkBits2Float(0x42bd0000));  // 296.1f, 94.5f
+path.lineTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000));  // 193.2f, 94.5f
+path.close();
+path.moveTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000));  // 344.4f, 94.5f
+path.lineTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000));  // 344.4f, 94.5f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42bd0000), SkBits2Float(0x43ace666), SkBits2Float(0x42bcfc5e), SkBits2Float(0x43ad4000), SkBits2Float(0x42bd0000));  // 345.1f, 94.5f, 345.8f, 94.4929f, 346.5f, 94.5f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42bd03a2), SkBits2Float(0x43adf333), SkBits2Float(0x42bcf966), SkBits2Float(0x43ae4ccd), SkBits2Float(0x42bd15ce));  // 347.2f, 94.5071f, 347.9f, 94.4871f, 348.6f, 94.5426f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42bd3236), SkBits2Float(0x43af0000), SkBits2Float(0x42bd84e8), SkBits2Float(0x43af599a), SkBits2Float(0x42bdaa71));  // 349.3f, 94.5981f, 350, 94.7596f, 350.7f, 94.8329f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42bdcffb), SkBits2Float(0x43b00ccd), SkBits2Float(0x42bde92e), SkBits2Float(0x43b06666), SkBits2Float(0x42bdf707));  // 351.4f, 94.9062f, 352.1f, 94.9554f, 352.8f, 94.9825f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42be04e0), SkBits2Float(0x43b1199a), SkBits2Float(0x42bdfc2b), SkBits2Float(0x43b17333), SkBits2Float(0x42bdfd89));  // 353.5f, 95.0095f, 354.2f, 94.9925f, 354.9f, 94.9952f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42bdfee7), SkBits2Float(0x43b22666), SkBits2Float(0x42bdfe62), SkBits2Float(0x43b28000), SkBits2Float(0x42bdff3b));  // 355.6f, 94.9979f, 356.3f, 94.9968f, 357, 94.9985f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x42be0014), SkBits2Float(0x43b33333), SkBits2Float(0x42be020f), SkBits2Float(0x43b38ccd), SkBits2Float(0x42be029f));  // 357.7f, 95.0002f, 358.4f, 95.004f, 359.1f, 95.0051f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42be0330), SkBits2Float(0x43b44000), SkBits2Float(0x42be020f), SkBits2Float(0x43b4999a), SkBits2Float(0x42be029f));  // 359.8f, 95.0062f, 360.5f, 95.004f, 361.2f, 95.0051f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x42be0330), SkBits2Float(0x43b54ccd), SkBits2Float(0x42be052a), SkBits2Float(0x43b5a666), SkBits2Float(0x42be0604));  // 361.9f, 95.0062f, 362.6f, 95.0101f, 363.3f, 95.0117f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42be06dd), SkBits2Float(0x43b6599a), SkBits2Float(0x42be0725), SkBits2Float(0x43b6b333), SkBits2Float(0x42be07b6));  // 364, 95.0134f, 364.7f, 95.014f, 365.4f, 95.0151f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42be0846), SkBits2Float(0x43b76666), SkBits2Float(0x42be08d7), SkBits2Float(0x43b7c000), SkBits2Float(0x42be0968));  // 366.1f, 95.0162f, 366.8f, 95.0173f, 367.5f, 95.0184f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x42be09f8), SkBits2Float(0x43b87333), SkBits2Float(0x42be0ad2), SkBits2Float(0x43b8cccd), SkBits2Float(0x42be0b1a));  // 368.2f, 95.0195f, 368.9f, 95.0211f, 369.6f, 95.0217f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x42be0b62), SkBits2Float(0x43b98000), SkBits2Float(0x42be0bab), SkBits2Float(0x43b9d99a), SkBits2Float(0x42be0b1a));  // 370.3f, 95.0222f, 371, 95.0228f, 371.7f, 95.0217f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x42be0a89), SkBits2Float(0x43ba8ccd), SkBits2Float(0x42be088f), SkBits2Float(0x43bae666), SkBits2Float(0x42be07b6));  // 372.4f, 95.0206f, 373.1f, 95.0167f, 373.8f, 95.0151f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x42be06dd), SkBits2Float(0x43bb999a), SkBits2Float(0x42be064c), SkBits2Float(0x43bbf333), SkBits2Float(0x42be0604));  // 374.5f, 95.0134f, 375.2f, 95.0123f, 375.9f, 95.0117f
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x42be05bb), SkBits2Float(0x43bca666), SkBits2Float(0x42be0604), SkBits2Float(0x43bd0000), SkBits2Float(0x42be0604));  // 376.6f, 95.0112f, 377.3f, 95.0117f, 378, 95.0117f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42be0604), SkBits2Float(0x43bdb333), SkBits2Float(0x42be0604), SkBits2Float(0x43be0ccd), SkBits2Float(0x42be0604));  // 378.7f, 95.0117f, 379.4f, 95.0117f, 380.1f, 95.0117f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42be0604), SkBits2Float(0x43bec000), SkBits2Float(0x42be0725), SkBits2Float(0x43bf199a), SkBits2Float(0x42be0604));  // 380.8f, 95.0117f, 381.5f, 95.014f, 382.2f, 95.0117f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42be04e2), SkBits2Float(0x43bfcccd), SkBits2Float(0x42be0136), SkBits2Float(0x43c02666), SkBits2Float(0x42bdff3b));  // 382.9f, 95.0095f, 383.6f, 95.0024f, 384.3f, 94.9985f
+path.cubicTo(SkBits2Float(0x43c08000), SkBits2Float(0x42bdfd41), SkBits2Float(0x43c0d99a), SkBits2Float(0x42bdfa6d), SkBits2Float(0x43c13333), SkBits2Float(0x42bdfa25));  // 385, 94.9946f, 385.7f, 94.9891f, 386.4f, 94.9886f
+path.cubicTo(SkBits2Float(0x43c18ccd), SkBits2Float(0x42bdf9dc), SkBits2Float(0x43c1e666), SkBits2Float(0x42bdfcf8), SkBits2Float(0x43c24000), SkBits2Float(0x42bdfd89));  // 387.1f, 94.988f, 387.8f, 94.9941f, 388.5f, 94.9952f
+path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42bdfe1a), SkBits2Float(0x43c2f333), SkBits2Float(0x42bdfdd1), SkBits2Float(0x43c34ccd), SkBits2Float(0x42bdfd89));  // 389.2f, 94.9963f, 389.9f, 94.9957f, 390.6f, 94.9952f
+path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42bdfd41), SkBits2Float(0x43c40000), SkBits2Float(0x42bdfd41), SkBits2Float(0x43c4599a), SkBits2Float(0x42bdfbd7));  // 391.3f, 94.9946f, 392, 94.9946f, 392.7f, 94.9919f
+path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42bdfa6d), SkBits2Float(0x43c50ccd), SkBits2Float(0x42bdf709), SkBits2Float(0x43c56666), SkBits2Float(0x42bdf50e));  // 393.4f, 94.9891f, 394.1f, 94.9825f, 394.8f, 94.9786f
+path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42bdf314), SkBits2Float(0x43c6199a), SkBits2Float(0x42bdf0d1), SkBits2Float(0x43c67333), SkBits2Float(0x42bdeff8));  // 395.5f, 94.9748f, 396.2f, 94.9703f, 396.9f, 94.9687f
+path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x42bdef1f), SkBits2Float(0x43c72666), SkBits2Float(0x42bdf040), SkBits2Float(0x43c78000), SkBits2Float(0x42bdeff8));  // 397.6f, 94.967f, 398.3f, 94.9692f, 399, 94.9687f
+path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x42bdefb0), SkBits2Float(0x43c83333), SkBits2Float(0x42bdee8e), SkBits2Float(0x43c88ccd), SkBits2Float(0x42bdee46));  // 399.7f, 94.9681f, 400.4f, 94.9659f, 401.1f, 94.9654f
+path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x42bdedfe), SkBits2Float(0x43c94000), SkBits2Float(0x42bdee46), SkBits2Float(0x43c9999a), SkBits2Float(0x42bdee46));  // 401.8f, 94.9648f, 402.5f, 94.9654f, 403.2f, 94.9654f
+path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42bdee46), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42bdedb5), SkBits2Float(0x43caa666), SkBits2Float(0x42bdee46));  // 403.9f, 94.9654f, 404.6f, 94.9643f, 405.3f, 94.9654f
+path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42bdeed7), SkBits2Float(0x43cb599a), SkBits2Float(0x42bdf089), SkBits2Float(0x43cbb333), SkBits2Float(0x42bdf1aa));  // 406, 94.9665f, 406.7f, 94.9698f, 407.4f, 94.972f
+path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x42bdf2cc), SkBits2Float(0x43cc6666), SkBits2Float(0x42bdf47e), SkBits2Float(0x43ccc000), SkBits2Float(0x42bdf50e));  // 408.1f, 94.9742f, 408.8f, 94.9775f, 409.5f, 94.9786f
+path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x42bdf59f), SkBits2Float(0x43cd7333), SkBits2Float(0x42bdf4c6), SkBits2Float(0x43cdcccd), SkBits2Float(0x42bdf50e));  // 410.2f, 94.9797f, 410.9f, 94.9781f, 411.6f, 94.9786f
+path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x42bdf557), SkBits2Float(0x43ce8000), SkBits2Float(0x42bdf557), SkBits2Float(0x43ced99a), SkBits2Float(0x42bdf6c0));  // 412.3f, 94.9792f, 413, 94.9792f, 413.7f, 94.9819f
+path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42bdf82a), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42bdfafe), SkBits2Float(0x43cfe666), SkBits2Float(0x42bdfd89));  // 414.4f, 94.9847f, 415.1f, 94.9902f, 415.8f, 94.9952f
+path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42be0014), SkBits2Float(0x43d0999a), SkBits2Float(0x42be0378), SkBits2Float(0x43d0f333), SkBits2Float(0x42be0604));  // 416.5f, 95.0002f, 417.2f, 95.0068f, 417.9f, 95.0117f
+path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42be088f), SkBits2Float(0x43d1a666), SkBits2Float(0x42be0b1a), SkBits2Float(0x43d20000), SkBits2Float(0x42be0ccc));  // 418.6f, 95.0167f, 419.3f, 95.0217f, 420, 95.025f
+path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42be0e7e), SkBits2Float(0x43d2b333), SkBits2Float(0x42be119a), SkBits2Float(0x43d30ccd), SkBits2Float(0x42be1030));  // 420.7f, 95.0283f, 421.4f, 95.0344f, 422.1f, 95.0316f
+path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x42be0ec6), SkBits2Float(0x43d3c000), SkBits2Float(0x42be0ad2), SkBits2Float(0x43d4199a), SkBits2Float(0x42be0451));  // 422.8f, 95.0289f, 423.5f, 95.0211f, 424.2f, 95.0084f
+path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42bdfdd1), SkBits2Float(0x43d4cccd), SkBits2Float(0x42bdefec), SkBits2Float(0x43d52666), SkBits2Float(0x42bde930));  // 424.9f, 94.9957f, 425.6f, 94.9686f, 426.3f, 94.9554f
+path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42bde273), SkBits2Float(0x43d5d99a), SkBits2Float(0x42bde21d), SkBits2Float(0x43d63333), SkBits2Float(0x42bddbe5));  // 427, 94.9423f, 427.7f, 94.9416f, 428.4f, 94.9295f
+path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42bdd5ad), SkBits2Float(0x43d6e666), SkBits2Float(0x42bdcf3b), SkBits2Float(0x43d74000), SkBits2Float(0x42bdc3e1));  // 429.1f, 94.9173f, 429.8f, 94.9047f, 430.5f, 94.8826f
+path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x42bdb887), SkBits2Float(0x43d7f333), SkBits2Float(0x42bd9d7d), SkBits2Float(0x43d84ccd), SkBits2Float(0x42bd97ca));  // 431.2f, 94.8604f, 431.9f, 94.8076f, 432.6f, 94.7965f
+path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x42bd9217), SkBits2Float(0x43d90000), SkBits2Float(0x42bd9820), SkBits2Float(0x43d9599a), SkBits2Float(0x42bda1b0));  // 433.3f, 94.7853f, 434, 94.7971f, 434.7f, 94.8158f
+path.cubicTo(SkBits2Float(0x43d9b333), SkBits2Float(0x42bdab40), SkBits2Float(0x43da0ccd), SkBits2Float(0x42bdc5dd), SkBits2Float(0x43da6666), SkBits2Float(0x42bdd12b));  // 435.4f, 94.8345f, 436.1f, 94.8865f, 436.8f, 94.9085f
+path.cubicTo(SkBits2Float(0x43dac000), SkBits2Float(0x42bddc79), SkBits2Float(0x43db199a), SkBits2Float(0x42bde1cc), SkBits2Float(0x43db7333), SkBits2Float(0x42bde585));  // 437.5f, 94.9306f, 438.2f, 94.941f, 438.9f, 94.9483f
+path.cubicTo(SkBits2Float(0x43dbcccd), SkBits2Float(0x42bde93d), SkBits2Float(0x43dc2666), SkBits2Float(0x42bde75a), SkBits2Float(0x43dc8000), SkBits2Float(0x42bde77d));  // 439.6f, 94.9555f, 440.3f, 94.9519f, 441, 94.9521f
+path.cubicTo(SkBits2Float(0x43dcd99a), SkBits2Float(0x42bde7a1), SkBits2Float(0x43dd3333), SkBits2Float(0x42bde610), SkBits2Float(0x43dd8ccd), SkBits2Float(0x42bde658));  // 441.7f, 94.9524f, 442.4f, 94.9493f, 443.1f, 94.9499f
+path.cubicTo(SkBits2Float(0x43dde666), SkBits2Float(0x42bde6a1), SkBits2Float(0x43de4000), SkBits2Float(0x42bdea2c), SkBits2Float(0x43de999a), SkBits2Float(0x42bde930));  // 443.8f, 94.9504f, 444.5f, 94.9574f, 445.2f, 94.9554f
+path.cubicTo(SkBits2Float(0x43def333), SkBits2Float(0x42bde833), SkBits2Float(0x43df4ccd), SkBits2Float(0x42bdee5f), SkBits2Float(0x43dfa666), SkBits2Float(0x42bde06e));  // 445.9f, 94.9535f, 446.6f, 94.9656f, 447.3f, 94.9383f
+path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x42bdd27d), SkBits2Float(0x43e0599a), SkBits2Float(0x42bdb871), SkBits2Float(0x43e0b333), SkBits2Float(0x42bd958a));  // 448, 94.9111f, 448.7f, 94.8602f, 449.4f, 94.7921f
+path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42bd72a4), SkBits2Float(0x43e16666), SkBits2Float(0x42bd27f2), SkBits2Float(0x43e1c000), SkBits2Float(0x42bd0f06));  // 450.1f, 94.7239f, 450.8f, 94.578f, 451.5f, 94.5293f
+path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x42bcf619), SkBits2Float(0x43e27333), SkBits2Float(0x42bcfbb7), SkBits2Float(0x43e2cccd), SkBits2Float(0x42bd0000));  // 452.2f, 94.4807f, 452.9f, 94.4916f, 453.6f, 94.5f
+path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x42bd0449), SkBits2Float(0x43e38000), SkBits2Float(0x42bd1419), SkBits2Float(0x43e3d99a), SkBits2Float(0x42bd28bc));  // 454.3f, 94.5084f, 455, 94.5393f, 455.7f, 94.5796f
+path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x42bd3d60), SkBits2Float(0x43e48ccd), SkBits2Float(0x42bd7238), SkBits2Float(0x43e4e666), SkBits2Float(0x42bd7bd4));  // 456.4f, 94.6199f, 457.1f, 94.7231f, 457.8f, 94.7419f
+path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42bd8570), SkBits2Float(0x43e5999a), SkBits2Float(0x42bd70ce), SkBits2Float(0x43e5f333), SkBits2Float(0x42bd6264));  // 458.5f, 94.7606f, 459.2f, 94.7203f, 459.9f, 94.6922f
+path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42bd53fa), SkBits2Float(0x43e6a666), SkBits2Float(0x42bd35be), SkBits2Float(0x43e70000), SkBits2Float(0x42bd2558));  // 460.6f, 94.664f, 461.3f, 94.605f, 462, 94.5729f
+path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42bd14f2), SkBits2Float(0x43e7b333), SkBits2Float(0x42bd0639), SkBits2Float(0x43e80ccd), SkBits2Float(0x42bd0000));  // 462.7f, 94.5409f, 463.4f, 94.5122f, 464.1f, 94.5f
+path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42bcf9c7), SkBits2Float(0x43e8c000), SkBits2Float(0x42bcfef5), SkBits2Float(0x43e9199a), SkBits2Float(0x42bd0000));  // 464.8f, 94.4878f, 465.5f, 94.498f, 466.2f, 94.5f
+path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42bd010b), SkBits2Float(0x43e9cccd), SkBits2Float(0x42bd0645), SkBits2Float(0x43ea2666), SkBits2Float(0x42bd0645));  // 466.9f, 94.502f, 467.6f, 94.5122f, 468.3f, 94.5122f
+path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42bd0645), SkBits2Float(0x43ead99a), SkBits2Float(0x42bd010b), SkBits2Float(0x43eb3333), SkBits2Float(0x42bd0000));  // 469, 94.5122f, 469.7f, 94.502f, 470.4f, 94.5f
+path.lineTo(SkBits2Float(0x43eb3333), SkBits2Float(0x42bd0000));  // 470.4f, 94.5f
+path.lineTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000));  // 344.4f, 94.5f
+path.close();
+    return path;
+}
+
+static SkPath path5() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a));  // 88.2f, 100.8f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a));  // 88.2f, 100.8f
+path.cubicTo(SkBits2Float(0x42b1cccd), SkBits2Float(0x42c9999a), SkBits2Float(0x42b33333), SkBits2Float(0x42c9b407), SkBits2Float(0x42b4999a), SkBits2Float(0x42c9999a));  // 88.9f, 100.8f, 89.6f, 100.852f, 90.3f, 100.8f
+path.cubicTo(SkBits2Float(0x42b60000), SkBits2Float(0x42c97f2c), SkBits2Float(0x42b76666), SkBits2Float(0x42c91521), SkBits2Float(0x42b8cccd), SkBits2Float(0x42c8fb07));  // 91, 100.748f, 91.7f, 100.541f, 92.4f, 100.49f
+path.cubicTo(SkBits2Float(0x42ba3333), SkBits2Float(0x42c8e0ee), SkBits2Float(0x42bb999a), SkBits2Float(0x42c8fd00), SkBits2Float(0x42bd0000), SkBits2Float(0x42c8fd00));  // 93.1f, 100.439f, 93.8f, 100.494f, 94.5f, 100.494f
+path.cubicTo(SkBits2Float(0x42be6666), SkBits2Float(0x42c8fd00), SkBits2Float(0x42bfcccd), SkBits2Float(0x42c8facb), SkBits2Float(0x42c13333), SkBits2Float(0x42c8fb07));  // 95.2f, 100.494f, 95.9f, 100.49f, 96.6f, 100.49f
+path.cubicTo(SkBits2Float(0x42c2999a), SkBits2Float(0x42c8fb44), SkBits2Float(0x42c40000), SkBits2Float(0x42c8fd4a), SkBits2Float(0x42c56666), SkBits2Float(0x42c8fe6c));  // 97.3f, 100.491f, 98, 100.495f, 98.7f, 100.497f
+path.cubicTo(SkBits2Float(0x42c6cccd), SkBits2Float(0x42c8ff8d), SkBits2Float(0x42c83333), SkBits2Float(0x42c90218), SkBits2Float(0x42c9999a), SkBits2Float(0x42c901d0));  // 99.4f, 100.499f, 100.1f, 100.504f, 100.8f, 100.504f
+path.cubicTo(SkBits2Float(0x42cb0000), SkBits2Float(0x42c90187), SkBits2Float(0x42cc6666), SkBits2Float(0x42c8ff39), SkBits2Float(0x42cdcccd), SkBits2Float(0x42c8fcb9));  // 101.5f, 100.503f, 102.2f, 100.498f, 102.9f, 100.494f
+path.cubicTo(SkBits2Float(0x42cf3333), SkBits2Float(0x42c8fa3a), SkBits2Float(0x42d0999a), SkBits2Float(0x42c8f364), SkBits2Float(0x42d20000), SkBits2Float(0x42c8f2d3));  // 103.6f, 100.489f, 104.3f, 100.475f, 105, 100.474f
+path.cubicTo(SkBits2Float(0x42d36666), SkBits2Float(0x42c8f243), SkBits2Float(0x42d4cccd), SkBits2Float(0x42c8f402), SkBits2Float(0x42d63333), SkBits2Float(0x42c8f955));  // 105.7f, 100.473f, 106.4f, 100.477f, 107.1f, 100.487f
+path.cubicTo(SkBits2Float(0x42d7999a), SkBits2Float(0x42c8fea8), SkBits2Float(0x42d90000), SkBits2Float(0x42c90daf), SkBits2Float(0x42da6666), SkBits2Float(0x42c912c5));  // 107.8f, 100.497f, 108.5f, 100.527f, 109.2f, 100.537f
+path.cubicTo(SkBits2Float(0x42dbcccd), SkBits2Float(0x42c917db), SkBits2Float(0x42dd3333), SkBits2Float(0x42c918f1), SkBits2Float(0x42de999a), SkBits2Float(0x42c917db));  // 109.9f, 100.547f, 110.6f, 100.549f, 111.3f, 100.547f
+path.cubicTo(SkBits2Float(0x42e00000), SkBits2Float(0x42c916c6), SkBits2Float(0x42e16666), SkBits2Float(0x42c90ac2), SkBits2Float(0x42e2cccd), SkBits2Float(0x42c90c43));  // 112, 100.544f, 112.7f, 100.521f, 113.4f, 100.524f
+path.cubicTo(SkBits2Float(0x42e43333), SkBits2Float(0x42c90dc4), SkBits2Float(0x42e5999a), SkBits2Float(0x42c91420), SkBits2Float(0x42e70000), SkBits2Float(0x42c920e3));  // 114.1f, 100.527f, 114.8f, 100.539f, 115.5f, 100.564f
+path.cubicTo(SkBits2Float(0x42e86666), SkBits2Float(0x42c92da7), SkBits2Float(0x42e9cccd), SkBits2Float(0x42c946ab), SkBits2Float(0x42eb3333), SkBits2Float(0x42c958d9));  // 116.2f, 100.589f, 116.9f, 100.638f, 117.6f, 100.674f
+path.cubicTo(SkBits2Float(0x42ec999a), SkBits2Float(0x42c96b07), SkBits2Float(0x42ee0000), SkBits2Float(0x42c9832d), SkBits2Float(0x42ef6666), SkBits2Float(0x42c98df8));  // 118.3f, 100.709f, 119, 100.756f, 119.7f, 100.777f
+path.cubicTo(SkBits2Float(0x42f0cccd), SkBits2Float(0x42c998c3), SkBits2Float(0x42f23333), SkBits2Float(0x42c997a9), SkBits2Float(0x42f3999a), SkBits2Float(0x42c9999a));  // 120.4f, 100.798f, 121.1f, 100.796f, 121.8f, 100.8f
+path.lineTo(SkBits2Float(0x42f3999a), SkBits2Float(0x42c9999a));  // 121.8f, 100.8f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a));  // 88.2f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c9999a));  // 128.1f, 100.8f
+path.lineTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c99825));  // 128.1f, 100.797f
+path.cubicTo(SkBits2Float(0x4300cccd), SkBits2Float(0x42c99756), SkBits2Float(0x43018000), SkBits2Float(0x42c99482), SkBits2Float(0x43023333), SkBits2Float(0x42c994c1));  // 128.8f, 100.796f, 129.5f, 100.79f, 130.2f, 100.791f
+path.cubicTo(SkBits2Float(0x4302e666), SkBits2Float(0x42c994ff), SkBits2Float(0x4303999a), SkBits2Float(0x42c998cb), SkBits2Float(0x43044ccd), SkBits2Float(0x42c9999a));  // 130.9f, 100.791f, 131.6f, 100.798f, 132.3f, 100.8f
+path.lineTo(SkBits2Float(0x43044ccd), SkBits2Float(0x42c9999a));  // 132.3f, 100.8f
+path.lineTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c9999a));  // 128.1f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a));  // 155.4f, 100.8f
+path.lineTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a));  // 155.4f, 100.8f
+path.cubicTo(SkBits2Float(0x431c199a), SkBits2Float(0x42c9999a), SkBits2Float(0x431ccccd), SkBits2Float(0x42c99dcd), SkBits2Float(0x431d8000), SkBits2Float(0x42c9999a));  // 156.1f, 100.8f, 156.8f, 100.808f, 157.5f, 100.8f
+path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42c99567), SkBits2Float(0x431ee666), SkBits2Float(0x42c98d21), SkBits2Float(0x431f999a), SkBits2Float(0x42c98067));  // 158.2f, 100.792f, 158.9f, 100.776f, 159.6f, 100.751f
+path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42c973ae), SkBits2Float(0x43210000), SkBits2Float(0x42c95aa1), SkBits2Float(0x4321b333), SkBits2Float(0x42c94d41));  // 160.3f, 100.726f, 161, 100.677f, 161.7f, 100.651f
+path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42c93fe1), SkBits2Float(0x4323199a), SkBits2Float(0x42c935e5), SkBits2Float(0x4323cccd), SkBits2Float(0x42c93026));  // 162.4f, 100.625f, 163.1f, 100.605f, 163.8f, 100.594f
+path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x42c92a68), SkBits2Float(0x43253333), SkBits2Float(0x42c92b66), SkBits2Float(0x4325e666), SkBits2Float(0x42c92ac9));  // 164.5f, 100.583f, 165.2f, 100.585f, 165.9f, 100.584f
+path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x42c92a2d), SkBits2Float(0x43274ccd), SkBits2Float(0x42c92759), SkBits2Float(0x43280000), SkBits2Float(0x42c92c7b));  // 166.6f, 100.582f, 167.3f, 100.577f, 168, 100.587f
+path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x42c9319e), SkBits2Float(0x43296666), SkBits2Float(0x42c93d57), SkBits2Float(0x432a199a), SkBits2Float(0x42c94996));  // 168.7f, 100.597f, 169.4f, 100.62f, 170.1f, 100.644f
+path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x42c955d5), SkBits2Float(0x432b8000), SkBits2Float(0x42c968e8), SkBits2Float(0x432c3333), SkBits2Float(0x42c975f4));  // 170.8f, 100.668f, 171.5f, 100.705f, 172.2f, 100.73f
+path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x42c98300), SkBits2Float(0x432d999a), SkBits2Float(0x42c991ed), SkBits2Float(0x432e4ccd), SkBits2Float(0x42c997de));  // 172.9f, 100.756f, 173.6f, 100.785f, 174.3f, 100.797f
+path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x42c99dcf), SkBits2Float(0x432fb333), SkBits2Float(0x42c99950), SkBits2Float(0x43306666), SkBits2Float(0x42c9999a));  // 175, 100.808f, 175.7f, 100.799f, 176.4f, 100.8f
+path.lineTo(SkBits2Float(0x43306666), SkBits2Float(0x42c9999a));  // 176.4f, 100.8f
+path.lineTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a));  // 155.4f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a));  // 199.5f, 100.8f
+path.lineTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a));  // 199.5f, 100.8f
+path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42c9999a), SkBits2Float(0x4348e666), SkBits2Float(0x42c99cf4), SkBits2Float(0x4349999a), SkBits2Float(0x42c9999a));  // 200.2f, 100.8f, 200.9f, 100.807f, 201.6f, 100.8f
+path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42c99640), SkBits2Float(0x434b0000), SkBits2Float(0x42c98bf3), SkBits2Float(0x434bb333), SkBits2Float(0x42c9857d));  // 202.3f, 100.793f, 203, 100.773f, 203.7f, 100.761f
+path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x42c97f08), SkBits2Float(0x434d199a), SkBits2Float(0x42c9771f), SkBits2Float(0x434dcccd), SkBits2Float(0x42c972d6));  // 204.4f, 100.748f, 205.1f, 100.733f, 205.8f, 100.724f
+path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x42c96e8d), SkBits2Float(0x434f3333), SkBits2Float(0x42c96dc2), SkBits2Float(0x434fe666), SkBits2Float(0x42c96bc7));  // 206.5f, 100.716f, 207.2f, 100.714f, 207.9f, 100.711f
+path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x42c969cd), SkBits2Float(0x43514ccd), SkBits2Float(0x42c967c5), SkBits2Float(0x43520000), SkBits2Float(0x42c966f7));  // 208.6f, 100.707f, 209.3f, 100.703f, 210, 100.701f
+path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x42c9662a), SkBits2Float(0x43536666), SkBits2Float(0x42c966af), SkBits2Float(0x4354199a), SkBits2Float(0x42c966f7));  // 210.7f, 100.7f, 211.4f, 100.701f, 212.1f, 100.701f
+path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x42c96740), SkBits2Float(0x43558000), SkBits2Float(0x42c96b40), SkBits2Float(0x43563333), SkBits2Float(0x42c968a9));  // 212.8f, 100.702f, 213.5f, 100.709f, 214.2f, 100.704f
+path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x42c96612), SkBits2Float(0x4357999a), SkBits2Float(0x42c9624e), SkBits2Float(0x43584ccd), SkBits2Float(0x42c9576e));  // 214.9f, 100.699f, 215.6f, 100.692f, 216.3f, 100.671f
+path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x42c94c8d), SkBits2Float(0x4359b333), SkBits2Float(0x42c935e7), SkBits2Float(0x435a6666), SkBits2Float(0x42c92765));  // 217, 100.65f, 217.7f, 100.605f, 218.4f, 100.577f
+path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x42c918e4), SkBits2Float(0x435bcccd), SkBits2Float(0x42c9097b), SkBits2Float(0x435c8000), SkBits2Float(0x42c90064));  // 219.1f, 100.549f, 219.8f, 100.519f, 220.5f, 100.501f
+path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42c8f74d), SkBits2Float(0x435de666), SkBits2Float(0x42c8f840), SkBits2Float(0x435e999a), SkBits2Float(0x42c8f0db));  // 221.2f, 100.483f, 221.9f, 100.485f, 222.6f, 100.47f
+path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x42c8e976), SkBits2Float(0x43600000), SkBits2Float(0x42c8df18), SkBits2Float(0x4360b333), SkBits2Float(0x42c8d407));  // 223.3f, 100.456f, 224, 100.436f, 224.7f, 100.414f
+path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42c8c8f5), SkBits2Float(0x4362199a), SkBits2Float(0x42c8b92f), SkBits2Float(0x4362cccd), SkBits2Float(0x42c8ae71));  // 225.4f, 100.392f, 226.1f, 100.362f, 226.8f, 100.341f
+path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x42c8a3b4), SkBits2Float(0x43643333), SkBits2Float(0x42c89926), SkBits2Float(0x4364e666), SkBits2Float(0x42c89396));  // 227.5f, 100.32f, 228.2f, 100.299f, 228.9f, 100.288f
+path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x42c88e07), SkBits2Float(0x43664ccd), SkBits2Float(0x42c88baa), SkBits2Float(0x43670000), SkBits2Float(0x42c88d14));  // 229.6f, 100.277f, 230.3f, 100.273f, 231, 100.276f
+path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x42c88e7e), SkBits2Float(0x43686666), SkBits2Float(0x42c89675), SkBits2Float(0x4369199a), SkBits2Float(0x42c89c11));  // 231.7f, 100.278f, 232.4f, 100.294f, 233.1f, 100.305f
+path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x42c8a1ac), SkBits2Float(0x436a8000), SkBits2Float(0x42c8a9ea), SkBits2Float(0x436b3333), SkBits2Float(0x42c8aeb8));  // 233.8f, 100.316f, 234.5f, 100.332f, 235.2f, 100.341f
+path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x42c8b386), SkBits2Float(0x436c999a), SkBits2Float(0x42c8b733), SkBits2Float(0x436d4ccd), SkBits2Float(0x42c8b8e5));  // 235.9f, 100.351f, 236.6f, 100.358f, 237.3f, 100.361f
+path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42c8ba97), SkBits2Float(0x436eb333), SkBits2Float(0x42c8b9be), SkBits2Float(0x436f6666), SkBits2Float(0x42c8b8e5));  // 238, 100.364f, 238.7f, 100.363f, 239.4f, 100.361f
+path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42c8b80c), SkBits2Float(0x4370cccd), SkBits2Float(0x42c8b45f), SkBits2Float(0x43718000), SkBits2Float(0x42c8b3ce));  // 240.1f, 100.359f, 240.8f, 100.352f, 241.5f, 100.351f
+path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x42c8b33e), SkBits2Float(0x4372e666), SkBits2Float(0x42c8b4f0), SkBits2Float(0x4373999a), SkBits2Float(0x42c8b580));  // 242.2f, 100.35f, 242.9f, 100.353f, 243.6f, 100.354f
+path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42c8b611), SkBits2Float(0x43750000), SkBits2Float(0x42c8b6ea), SkBits2Float(0x4375b333), SkBits2Float(0x42c8b733));  // 244.3f, 100.356f, 245, 100.357f, 245.7f, 100.358f
+path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x42c8b77b), SkBits2Float(0x4377199a), SkBits2Float(0x42c8b77b), SkBits2Float(0x4377cccd), SkBits2Float(0x42c8b733));  // 246.4f, 100.358f, 247.1f, 100.358f, 247.8f, 100.358f
+path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42c8b6ea), SkBits2Float(0x43793333), SkBits2Float(0x42c8b5c9), SkBits2Float(0x4379e666), SkBits2Float(0x42c8b580));  // 248.5f, 100.357f, 249.2f, 100.355f, 249.9f, 100.354f
+path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42c8b538), SkBits2Float(0x437b4ccd), SkBits2Float(0x42c8b538), SkBits2Float(0x437c0000), SkBits2Float(0x42c8b580));  // 250.6f, 100.354f, 251.3f, 100.354f, 252, 100.354f
+path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42c8b5c9), SkBits2Float(0x437d6666), SkBits2Float(0x42c8b6ea), SkBits2Float(0x437e199a), SkBits2Float(0x42c8b733));  // 252.7f, 100.355f, 253.4f, 100.357f, 254.1f, 100.358f
+path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42c8b77b), SkBits2Float(0x437f8000), SkBits2Float(0x42c8b77b), SkBits2Float(0x4380199a), SkBits2Float(0x42c8b733));  // 254.8f, 100.358f, 255.5f, 100.358f, 256.2f, 100.358f
+path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x42c8b6ea), SkBits2Float(0x4380cccd), SkBits2Float(0x42c8b5c9), SkBits2Float(0x43812666), SkBits2Float(0x42c8b580));  // 256.9f, 100.357f, 257.6f, 100.355f, 258.3f, 100.354f
+path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x42c8b538), SkBits2Float(0x4381d99a), SkBits2Float(0x42c8b580), SkBits2Float(0x43823333), SkBits2Float(0x42c8b580));  // 259, 100.354f, 259.7f, 100.354f, 260.4f, 100.354f
+path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42c8b580), SkBits2Float(0x4382e666), SkBits2Float(0x42c8b580), SkBits2Float(0x43834000), SkBits2Float(0x42c8b580));  // 261.1f, 100.354f, 261.8f, 100.354f, 262.5f, 100.354f
+path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42c8b580), SkBits2Float(0x4383f333), SkBits2Float(0x42c8b5c9), SkBits2Float(0x43844ccd), SkBits2Float(0x42c8b580));  // 263.2f, 100.354f, 263.9f, 100.355f, 264.6f, 100.354f
+path.cubicTo(SkBits2Float(0x4384a666), SkBits2Float(0x42c8b538), SkBits2Float(0x43850000), SkBits2Float(0x42c8b417), SkBits2Float(0x4385599a), SkBits2Float(0x42c8b3ce));  // 265.3f, 100.354f, 266, 100.352f, 266.7f, 100.351f
+path.cubicTo(SkBits2Float(0x4385b333), SkBits2Float(0x42c8b386), SkBits2Float(0x43860ccd), SkBits2Float(0x42c8b386), SkBits2Float(0x43866666), SkBits2Float(0x42c8b3ce));  // 267.4f, 100.351f, 268.1f, 100.351f, 268.8f, 100.351f
+path.cubicTo(SkBits2Float(0x4386c000), SkBits2Float(0x42c8b417), SkBits2Float(0x4387199a), SkBits2Float(0x42c8b4f0), SkBits2Float(0x43877333), SkBits2Float(0x42c8b580));  // 269.5f, 100.352f, 270.2f, 100.353f, 270.9f, 100.354f
+path.cubicTo(SkBits2Float(0x4387cccd), SkBits2Float(0x42c8b611), SkBits2Float(0x43882666), SkBits2Float(0x42c8b6ea), SkBits2Float(0x43888000), SkBits2Float(0x42c8b733));  // 271.6f, 100.356f, 272.3f, 100.357f, 273, 100.358f
+path.cubicTo(SkBits2Float(0x4388d99a), SkBits2Float(0x42c8b77b), SkBits2Float(0x43893333), SkBits2Float(0x42c8b6a2), SkBits2Float(0x43898ccd), SkBits2Float(0x42c8b733));  // 273.7f, 100.358f, 274.4f, 100.357f, 275.1f, 100.358f
+path.cubicTo(SkBits2Float(0x4389e666), SkBits2Float(0x42c8b7c3), SkBits2Float(0x438a4000), SkBits2Float(0x42c8ba97), SkBits2Float(0x438a999a), SkBits2Float(0x42c8ba97));  // 275.8f, 100.359f, 276.5f, 100.364f, 277.2f, 100.364f
+path.cubicTo(SkBits2Float(0x438af333), SkBits2Float(0x42c8ba97), SkBits2Float(0x438b4ccd), SkBits2Float(0x42c8ba5a), SkBits2Float(0x438ba666), SkBits2Float(0x42c8b733));  // 277.9f, 100.364f, 278.6f, 100.364f, 279.3f, 100.358f
+path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x42c8b40b), SkBits2Float(0x438c599a), SkBits2Float(0x42c8aad1), SkBits2Float(0x438cb333), SkBits2Float(0x42c8a7a9));  // 280, 100.352f, 280.7f, 100.334f, 281.4f, 100.327f
+path.cubicTo(SkBits2Float(0x438d0ccd), SkBits2Float(0x42c8a481), SkBits2Float(0x438d6666), SkBits2Float(0x42c89f23), SkBits2Float(0x438dc000), SkBits2Float(0x42c8a445));  // 282.1f, 100.321f, 282.8f, 100.311f, 283.5f, 100.321f
+path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x42c8a967), SkBits2Float(0x438e7333), SkBits2Float(0x42c8b67f), SkBits2Float(0x438ecccd), SkBits2Float(0x42c8c676));  // 284.2f, 100.331f, 284.9f, 100.356f, 285.6f, 100.388f
+path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42c8d66d), SkBits2Float(0x438f8000), SkBits2Float(0x42c8ecb3), SkBits2Float(0x438fd99a), SkBits2Float(0x42c9040f));  // 286.3f, 100.419f, 287, 100.462f, 287.7f, 100.508f
+path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42c91b6b), SkBits2Float(0x43908ccd), SkBits2Float(0x42c939b1), SkBits2Float(0x4390e666), SkBits2Float(0x42c9529e));  // 288.4f, 100.554f, 289.1f, 100.613f, 289.8f, 100.661f
+path.cubicTo(SkBits2Float(0x43914000), SkBits2Float(0x42c96b8a), SkBits2Float(0x4391999a), SkBits2Float(0x42c98dc5), SkBits2Float(0x4391f333), SkBits2Float(0x42c9999a));  // 290.5f, 100.71f, 291.2f, 100.777f, 291.9f, 100.8f
+path.cubicTo(SkBits2Float(0x43924ccd), SkBits2Float(0x42c9a56e), SkBits2Float(0x4392a666), SkBits2Float(0x42c9999a), SkBits2Float(0x43930000), SkBits2Float(0x42c9999a));  // 292.6f, 100.823f, 293.3f, 100.8f, 294, 100.8f
+path.lineTo(SkBits2Float(0x43930000), SkBits2Float(0x42c9999a));  // 294, 100.8f
+path.lineTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a));  // 199.5f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a));  // 342.3f, 100.8f
+path.lineTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a));  // 342.3f, 100.8f
+path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x42c9999a), SkBits2Float(0x43abd99a), SkBits2Float(0x42c9a526), SkBits2Float(0x43ac3333), SkBits2Float(0x42c9999a));  // 343, 100.8f, 343.7f, 100.823f, 344.4f, 100.8f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42c98e0d), SkBits2Float(0x43ace666), SkBits2Float(0x42c9760b), SkBits2Float(0x43ad4000), SkBits2Float(0x42c95450));  // 345.1f, 100.777f, 345.8f, 100.731f, 346.5f, 100.665f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42c93295), SkBits2Float(0x43adf333), SkBits2Float(0x42c8ebfd), SkBits2Float(0x43ae4ccd), SkBits2Float(0x42c8cf37));  // 347.2f, 100.599f, 347.9f, 100.461f, 348.6f, 100.405f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42c8b270), SkBits2Float(0x43af0000), SkBits2Float(0x42c8afe7), SkBits2Float(0x43af599a), SkBits2Float(0x42c8a7a9));  // 349.3f, 100.349f, 350, 100.344f, 350.7f, 100.327f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42c89f6b), SkBits2Float(0x43b00ccd), SkBits2Float(0x42c8a08b), SkBits2Float(0x43b06666), SkBits2Float(0x42c89dc3));  // 351.4f, 100.311f, 352.1f, 100.314f, 352.8f, 100.308f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42c89afb), SkBits2Float(0x43b1199a), SkBits2Float(0x42c89864), SkBits2Float(0x43b17333), SkBits2Float(0x42c896fa));  // 353.5f, 100.303f, 354.2f, 100.298f, 354.9f, 100.295f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42c89591), SkBits2Float(0x43b22666), SkBits2Float(0x42c89591), SkBits2Float(0x43b28000), SkBits2Float(0x42c89548));  // 355.6f, 100.292f, 356.3f, 100.292f, 357, 100.292f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x42c89500), SkBits2Float(0x43b33333), SkBits2Float(0x42c89500), SkBits2Float(0x43b38ccd), SkBits2Float(0x42c89548));  // 357.7f, 100.291f, 358.4f, 100.291f, 359.1f, 100.292f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42c89591), SkBits2Float(0x43b44000), SkBits2Float(0x42c896b2), SkBits2Float(0x43b4999a), SkBits2Float(0x42c896fa));  // 359.8f, 100.292f, 360.5f, 100.294f, 361.2f, 100.295f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x42c89743), SkBits2Float(0x43b54ccd), SkBits2Float(0x42c896fa), SkBits2Float(0x43b5a666), SkBits2Float(0x42c896fa));  // 361.9f, 100.295f, 362.6f, 100.295f, 363.3f, 100.295f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42c896fa), SkBits2Float(0x43b6599a), SkBits2Float(0x42c89743), SkBits2Float(0x43b6b333), SkBits2Float(0x42c896fa));  // 364, 100.295f, 364.7f, 100.295f, 365.4f, 100.295f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42c896b2), SkBits2Float(0x43b76666), SkBits2Float(0x42c89591), SkBits2Float(0x43b7c000), SkBits2Float(0x42c89548));  // 366.1f, 100.294f, 366.8f, 100.292f, 367.5f, 100.292f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x42c89500), SkBits2Float(0x43b87333), SkBits2Float(0x42c89548), SkBits2Float(0x43b8cccd), SkBits2Float(0x42c89548));  // 368.2f, 100.291f, 368.9f, 100.292f, 369.6f, 100.292f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x42c89548), SkBits2Float(0x43b98000), SkBits2Float(0x42c89548), SkBits2Float(0x43b9d99a), SkBits2Float(0x42c89548));  // 370.3f, 100.292f, 371, 100.292f, 371.7f, 100.292f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x42c89548), SkBits2Float(0x43ba8ccd), SkBits2Float(0x42c894b7), SkBits2Float(0x43bae666), SkBits2Float(0x42c89548));  // 372.4f, 100.292f, 373.1f, 100.29f, 373.8f, 100.292f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x42c895d9), SkBits2Float(0x43bb999a), SkBits2Float(0x42c897d3), SkBits2Float(0x43bbf333), SkBits2Float(0x42c898ac));  // 374.5f, 100.293f, 375.2f, 100.297f, 375.9f, 100.298f
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x42c89985), SkBits2Float(0x43bca666), SkBits2Float(0x42c89a16), SkBits2Float(0x43bd0000), SkBits2Float(0x42c89a5f));  // 376.6f, 100.3f, 377.3f, 100.301f, 378, 100.302f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42c89aa7), SkBits2Float(0x43bdb333), SkBits2Float(0x42c89aa7), SkBits2Float(0x43be0ccd), SkBits2Float(0x42c89a5f));  // 378.7f, 100.302f, 379.4f, 100.302f, 380.1f, 100.302f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42c89a16), SkBits2Float(0x43bec000), SkBits2Float(0x42c8993d), SkBits2Float(0x43bf199a), SkBits2Float(0x42c898ac));  // 380.8f, 100.301f, 381.5f, 100.299f, 382.2f, 100.298f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42c8981c), SkBits2Float(0x43bfcccd), SkBits2Float(0x42c8978b), SkBits2Float(0x43c02666), SkBits2Float(0x42c896fa));  // 382.9f, 100.297f, 383.6f, 100.296f, 384.3f, 100.295f
+path.cubicTo(SkBits2Float(0x43c08000), SkBits2Float(0x42c8966a), SkBits2Float(0x43c0d99a), SkBits2Float(0x42c8946f), SkBits2Float(0x43c13333), SkBits2Float(0x42c89548));  // 385, 100.294f, 385.7f, 100.29f, 386.4f, 100.292f
+path.cubicTo(SkBits2Float(0x43c18ccd), SkBits2Float(0x42c89621), SkBits2Float(0x43c1e666), SkBits2Float(0x42c898f5), SkBits2Float(0x43c24000), SkBits2Float(0x42c89c11));  // 387.1f, 100.293f, 387.8f, 100.299f, 388.5f, 100.305f
+path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42c89f2d), SkBits2Float(0x43c2f333), SkBits2Float(0x42c8a5ad), SkBits2Float(0x43c34ccd), SkBits2Float(0x42c8a7ef));  // 389.2f, 100.311f, 389.9f, 100.324f, 390.6f, 100.328f
+path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42c8aa32), SkBits2Float(0x43c40000), SkBits2Float(0x42c8a9a2), SkBits2Float(0x43c4599a), SkBits2Float(0x42c8a9a2));  // 391.3f, 100.332f, 392, 100.331f, 392.7f, 100.331f
+path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42c8a9a2), SkBits2Float(0x43c50ccd), SkBits2Float(0x42c8a8c9), SkBits2Float(0x43c56666), SkBits2Float(0x42c8a7ef));  // 393.4f, 100.331f, 394.1f, 100.33f, 394.8f, 100.328f
+path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42c8a716), SkBits2Float(0x43c6199a), SkBits2Float(0x42c8a5ad), SkBits2Float(0x43c67333), SkBits2Float(0x42c8a48b));  // 395.5f, 100.326f, 396.2f, 100.324f, 396.9f, 100.321f
+path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x42c8a36a), SkBits2Float(0x43c72666), SkBits2Float(0x42c8a291), SkBits2Float(0x43c78000), SkBits2Float(0x42c8a127));  // 397.6f, 100.319f, 398.3f, 100.318f, 399, 100.315f
+path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x42c89fbd), SkBits2Float(0x43c83333), SkBits2Float(0x42c89dc3), SkBits2Float(0x43c88ccd), SkBits2Float(0x42c89c11));  // 399.7f, 100.312f, 400.4f, 100.308f, 401.1f, 100.305f
+path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x42c89a5f), SkBits2Float(0x43c94000), SkBits2Float(0x42c898ac), SkBits2Float(0x43c9999a), SkBits2Float(0x42c896fa));  // 401.8f, 100.302f, 402.5f, 100.298f, 403.2f, 100.295f
+path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42c89548), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42c89305), SkBits2Float(0x43caa666), SkBits2Float(0x42c891e4));  // 403.9f, 100.292f, 404.6f, 100.287f, 405.3f, 100.285f
+path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42c890c3), SkBits2Float(0x43cb599a), SkBits2Float(0x42c88ec8), SkBits2Float(0x43cbb333), SkBits2Float(0x42c89032));  // 406, 100.283f, 406.7f, 100.279f, 407.4f, 100.282f
+path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x42c8919c), SkBits2Float(0x43cc6666), SkBits2Float(0x42c89864), SkBits2Float(0x43ccc000), SkBits2Float(0x42c89a5f));  // 408.1f, 100.284f, 408.8f, 100.298f, 409.5f, 100.302f
+path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x42c89c59), SkBits2Float(0x43cd7333), SkBits2Float(0x42c89dff), SkBits2Float(0x43cdcccd), SkBits2Float(0x42c89c11));  // 410.2f, 100.305f, 410.9f, 100.309f, 411.6f, 100.305f
+path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x42c89a22), SkBits2Float(0x43ce8000), SkBits2Float(0x42c8919a), SkBits2Float(0x43ced99a), SkBits2Float(0x42c88ec6));  // 412.3f, 100.301f, 413, 100.284f, 413.7f, 100.279f
+path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42c88bf3), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42c88b70), SkBits2Float(0x43cfe666), SkBits2Float(0x42c88b1b));  // 414.4f, 100.273f, 415.1f, 100.272f, 415.8f, 100.272f
+path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42c88ac7), SkBits2Float(0x43d0999a), SkBits2Float(0x42c88bac), SkBits2Float(0x43d0f333), SkBits2Float(0x42c88cce));  // 416.5f, 100.271f, 417.2f, 100.273f, 417.9f, 100.275f
+path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42c88def), SkBits2Float(0x43d1a666), SkBits2Float(0x42c89032), SkBits2Float(0x43d20000), SkBits2Float(0x42c891e4));  // 418.6f, 100.277f, 419.3f, 100.282f, 420, 100.285f
+path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42c89396), SkBits2Float(0x43d2b333), SkBits2Float(0x42c89621), SkBits2Float(0x43d30ccd), SkBits2Float(0x42c896fa));  // 420.7f, 100.288f, 421.4f, 100.293f, 422.1f, 100.295f
+path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x42c897d3), SkBits2Float(0x43d3c000), SkBits2Float(0x42c89810), SkBits2Float(0x43d4199a), SkBits2Float(0x42c896fa));  // 422.8f, 100.297f, 423.5f, 100.297f, 424.2f, 100.295f
+path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42c895e5), SkBits2Float(0x43d4cccd), SkBits2Float(0x42c88d99), SkBits2Float(0x43d52666), SkBits2Float(0x42c89078));  // 424.9f, 100.293f, 425.6f, 100.277f, 426.3f, 100.282f
+path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42c89358), SkBits2Float(0x43d5d99a), SkBits2Float(0x42c88f0f), SkBits2Float(0x43d63333), SkBits2Float(0x42c8a836));  // 427, 100.288f, 427.7f, 100.279f, 428.4f, 100.329f
+path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42c8c15e), SkBits2Float(0x43d6e666), SkBits2Float(0x42c8ff2a), SkBits2Float(0x43d74000), SkBits2Float(0x42c92765));  // 429.1f, 100.378f, 429.8f, 100.498f, 430.5f, 100.577f
+path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x42c94fa0), SkBits2Float(0x43d7f333), SkBits2Float(0x42c98691), SkBits2Float(0x43d84ccd), SkBits2Float(0x42c9999a));  // 431.2f, 100.656f, 431.9f, 100.763f, 432.6f, 100.8f
+path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x42c9aca2), SkBits2Float(0x43d90000), SkBits2Float(0x42c9999a), SkBits2Float(0x43d9599a), SkBits2Float(0x42c9999a));  // 433.3f, 100.837f, 434, 100.8f, 434.7f, 100.8f
+path.lineTo(SkBits2Float(0x43d9599a), SkBits2Float(0x42c9999a));  // 434.7f, 100.8f
+path.lineTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a));  // 342.3f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a));  // 447.3f, 100.8f
+path.lineTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a));  // 447.3f, 100.8f
+path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x42c99877), SkBits2Float(0x43e0599a), SkBits2Float(0x42c99312), SkBits2Float(0x43e0b333), SkBits2Float(0x42c992c8));  // 448, 100.798f, 448.7f, 100.787f, 449.4f, 100.787f
+path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42c9927e), SkBits2Float(0x43e16666), SkBits2Float(0x42c996bb), SkBits2Float(0x43e1c000), SkBits2Float(0x42c997de));  // 450.1f, 100.786f, 450.8f, 100.794f, 451.5f, 100.797f
+path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x42c99901), SkBits2Float(0x43e27333), SkBits2Float(0x42c9a0aa), SkBits2Float(0x43e2cccd), SkBits2Float(0x42c9999a));  // 452.2f, 100.799f, 452.9f, 100.814f, 453.6f, 100.8f
+path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x42c99289), SkBits2Float(0x43e38000), SkBits2Float(0x42c97cb2), SkBits2Float(0x43e3d99a), SkBits2Float(0x42c96d79));  // 454.3f, 100.786f, 455, 100.744f, 455.7f, 100.714f
+path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x42c95e40), SkBits2Float(0x43e48ccd), SkBits2Float(0x42c943a3), SkBits2Float(0x43e4e666), SkBits2Float(0x42c93e44));  // 456.4f, 100.684f, 457.1f, 100.632f, 457.8f, 100.622f
+path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42c938e6), SkBits2Float(0x43e5999a), SkBits2Float(0x42c949f4), SkBits2Float(0x43e5f333), SkBits2Float(0x42c94d41));  // 458.5f, 100.611f, 459.2f, 100.644f, 459.9f, 100.651f
+path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42c9508e), SkBits2Float(0x43e6a666), SkBits2Float(0x42c94fce), SkBits2Float(0x43e70000), SkBits2Float(0x42c95211));  // 460.6f, 100.657f, 461.3f, 100.656f, 462, 100.66f
+path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42c95454), SkBits2Float(0x43e7b333), SkBits2Float(0x42c9595c), SkBits2Float(0x43e80ccd), SkBits2Float(0x42c95ad2));  // 462.7f, 100.665f, 463.4f, 100.675f, 464.1f, 100.677f
+path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42c95c47), SkBits2Float(0x43e8c000), SkBits2Float(0x42c959f9), SkBits2Float(0x43e9199a), SkBits2Float(0x42c95ad2));  // 464.8f, 100.68f, 465.5f, 100.676f, 466.2f, 100.677f
+path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42c95bab), SkBits2Float(0x43e9cccd), SkBits2Float(0x42c95d20), SkBits2Float(0x43ea2666), SkBits2Float(0x42c95fe8));  // 466.9f, 100.679f, 467.6f, 100.682f, 468.3f, 100.687f
+path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42c962b0), SkBits2Float(0x43ead99a), SkBits2Float(0x42c96743), SkBits2Float(0x43eb3333), SkBits2Float(0x42c96b80));  // 469, 100.693f, 469.7f, 100.702f, 470.4f, 100.71f
+path.cubicTo(SkBits2Float(0x43eb8ccd), SkBits2Float(0x42c96fbe), SkBits2Float(0x43ebe666), SkBits2Float(0x42c9754c), SkBits2Float(0x43ec4000), SkBits2Float(0x42c97958));  // 471.1f, 100.718f, 471.8f, 100.729f, 472.5f, 100.737f
+path.cubicTo(SkBits2Float(0x43ec999a), SkBits2Float(0x42c97d65), SkBits2Float(0x43ecf333), SkBits2Float(0x42c97ef2), SkBits2Float(0x43ed4ccd), SkBits2Float(0x42c983cb));  // 473.2f, 100.745f, 473.9f, 100.748f, 474.6f, 100.757f
+path.cubicTo(SkBits2Float(0x43eda666), SkBits2Float(0x42c988a5), SkBits2Float(0x43ee0000), SkBits2Float(0x42c992d0), SkBits2Float(0x43ee599a), SkBits2Float(0x42c99673));  // 475.3f, 100.767f, 476, 100.787f, 476.7f, 100.794f
+path.cubicTo(SkBits2Float(0x43eeb333), SkBits2Float(0x42c99a15), SkBits2Float(0x43ef0ccd), SkBits2Float(0x42c99bdc), SkBits2Float(0x43ef6666), SkBits2Float(0x42c9999a));  // 477.4f, 100.801f, 478.1f, 100.804f, 478.8f, 100.8f
+path.cubicTo(SkBits2Float(0x43efc000), SkBits2Float(0x42c99757), SkBits2Float(0x43f0199a), SkBits2Float(0x42c99426), SkBits2Float(0x43f07333), SkBits2Float(0x42c988e2));  // 479.5f, 100.796f, 480.2f, 100.789f, 480.9f, 100.767f
+path.cubicTo(SkBits2Float(0x43f0cccd), SkBits2Float(0x42c97d9e), SkBits2Float(0x43f12666), SkBits2Float(0x42c96356), SkBits2Float(0x43f18000), SkBits2Float(0x42c95602));  // 481.6f, 100.745f, 482.3f, 100.694f, 483, 100.668f
+path.cubicTo(SkBits2Float(0x43f1d99a), SkBits2Float(0x42c948ae), SkBits2Float(0x43f23333), SkBits2Float(0x42c93ee3), SkBits2Float(0x43f28ccd), SkBits2Float(0x42c938e7));  // 483.7f, 100.642f, 484.4f, 100.623f, 485.1f, 100.611f
+path.cubicTo(SkBits2Float(0x43f2e666), SkBits2Float(0x42c932ec), SkBits2Float(0x43f34000), SkBits2Float(0x42c93741), SkBits2Float(0x43f3999a), SkBits2Float(0x42c9321f));  // 485.8f, 100.599f, 486.5f, 100.608f, 487.2f, 100.598f
+path.cubicTo(SkBits2Float(0x43f3f333), SkBits2Float(0x42c92cfd), SkBits2Float(0x43f44ccd), SkBits2Float(0x42c922f5), SkBits2Float(0x43f4a666), SkBits2Float(0x42c91a1b));  // 487.9f, 100.588f, 488.6f, 100.568f, 489.3f, 100.551f
+path.cubicTo(SkBits2Float(0x43f50000), SkBits2Float(0x42c91140), SkBits2Float(0x43f5599a), SkBits2Float(0x42c904ad), SkBits2Float(0x43f5b333), SkBits2Float(0x42c8fd00));  // 490, 100.534f, 490.7f, 100.509f, 491.4f, 100.494f
+path.cubicTo(SkBits2Float(0x43f60ccd), SkBits2Float(0x42c8f553), SkBits2Float(0x43f66666), SkBits2Float(0x42c8ef7b), SkBits2Float(0x43f6c000), SkBits2Float(0x42c8ec0b));  // 492.1f, 100.479f, 492.8f, 100.468f, 493.5f, 100.461f
+path.cubicTo(SkBits2Float(0x43f7199a), SkBits2Float(0x42c8e89b), SkBits2Float(0x43f77333), SkBits2Float(0x42c8e981), SkBits2Float(0x43f7cccd), SkBits2Float(0x42c8e860));  // 494.2f, 100.454f, 494.9f, 100.456f, 495.6f, 100.454f
+path.cubicTo(SkBits2Float(0x43f82666), SkBits2Float(0x42c8e73f), SkBits2Float(0x43f88000), SkBits2Float(0x42c8e658), SkBits2Float(0x43f8d99a), SkBits2Float(0x42c8e542));  // 496.3f, 100.452f, 497, 100.45f, 497.7f, 100.448f
+path.cubicTo(SkBits2Float(0x43f93333), SkBits2Float(0x42c8e42d), SkBits2Float(0x43f98ccd), SkBits2Float(0x42c8e348), SkBits2Float(0x43f9e666), SkBits2Float(0x42c8e1de));  // 498.4f, 100.446f, 499.1f, 100.444f, 499.8f, 100.441f
+path.cubicTo(SkBits2Float(0x43fa4000), SkBits2Float(0x42c8e074), SkBits2Float(0x43fa999a), SkBits2Float(0x42c8df53), SkBits2Float(0x43faf333), SkBits2Float(0x42c8dcc8));  // 500.5f, 100.438f, 501.2f, 100.436f, 501.9f, 100.431f
+path.cubicTo(SkBits2Float(0x43fb4ccd), SkBits2Float(0x42c8da3d), SkBits2Float(0x43fba666), SkBits2Float(0x42c8d5c3), SkBits2Float(0x43fc0000), SkBits2Float(0x42c8d29b));  // 502.6f, 100.426f, 503.3f, 100.418f, 504, 100.411f
+path.cubicTo(SkBits2Float(0x43fc599a), SkBits2Float(0x42c8cf73), SkBits2Float(0x43fcb333), SkBits2Float(0x42c8ccf6), SkBits2Float(0x43fd0ccd), SkBits2Float(0x42c8c9da));  // 504.7f, 100.405f, 505.4f, 100.4f, 506.1f, 100.394f
+path.cubicTo(SkBits2Float(0x43fd6666), SkBits2Float(0x42c8c6be), SkBits2Float(0x43fdc000), SkBits2Float(0x42c8c310), SkBits2Float(0x43fe199a), SkBits2Float(0x42c8bff4));  // 506.8f, 100.388f, 507.5f, 100.381f, 508.2f, 100.375f
+path.cubicTo(SkBits2Float(0x43fe7333), SkBits2Float(0x42c8bcd8), SkBits2Float(0x43fecccd), SkBits2Float(0x42c8b8f0), SkBits2Float(0x43ff2666), SkBits2Float(0x42c8b733));  // 508.9f, 100.369f, 509.6f, 100.361f, 510.3f, 100.358f
+path.cubicTo(SkBits2Float(0x43ff8000), SkBits2Float(0x42c8b575), SkBits2Float(0x43ffd99a), SkBits2Float(0x42c8b45f), SkBits2Float(0x4400199a), SkBits2Float(0x42c8b580));  // 511, 100.354f, 511.7f, 100.352f, 512.4f, 100.354f
+path.cubicTo(SkBits2Float(0x44004666), SkBits2Float(0x42c8b6a2), SkBits2Float(0x44007333), SkBits2Float(0x42c8bb28), SkBits2Float(0x4400a000), SkBits2Float(0x42c8bdfb));  // 513.1f, 100.357f, 513.8f, 100.366f, 514.5f, 100.371f
+path.cubicTo(SkBits2Float(0x4400cccd), SkBits2Float(0x42c8c0cf), SkBits2Float(0x4400f99a), SkBits2Float(0x42c8c31d), SkBits2Float(0x44012666), SkBits2Float(0x42c8c676));  // 515.2f, 100.377f, 515.9f, 100.381f, 516.6f, 100.388f
+path.cubicTo(SkBits2Float(0x44015333), SkBits2Float(0x42c8c9ce), SkBits2Float(0x44018000), SkBits2Float(0x42c8caa9), SkBits2Float(0x4401accd), SkBits2Float(0x42c8d20e));  // 517.3f, 100.394f, 518, 100.396f, 518.7f, 100.41f
+path.cubicTo(SkBits2Float(0x4401d99a), SkBits2Float(0x42c8d973), SkBits2Float(0x44020666), SkBits2Float(0x42c8e446), SkBits2Float(0x44023333), SkBits2Float(0x42c8f2d3));  // 519.4f, 100.425f, 520.1f, 100.446f, 520.8f, 100.474f
+path.cubicTo(SkBits2Float(0x44026000), SkBits2Float(0x42c90161), SkBits2Float(0x44028ccd), SkBits2Float(0x42c917c0), SkBits2Float(0x4402b99a), SkBits2Float(0x42c9295e));  // 521.5f, 100.503f, 522.2f, 100.546f, 522.9f, 100.581f
+path.cubicTo(SkBits2Float(0x4402e666), SkBits2Float(0x42c93afb), SkBits2Float(0x44031333), SkBits2Float(0x42c94c14), SkBits2Float(0x44034000), SkBits2Float(0x42c95c84));  // 523.6f, 100.615f, 524.3f, 100.649f, 525, 100.681f
+path.cubicTo(SkBits2Float(0x44036ccd), SkBits2Float(0x42c96cf4), SkBits2Float(0x4403999a), SkBits2Float(0x42c981d1), SkBits2Float(0x4403c666), SkBits2Float(0x42c98bff));  // 525.7f, 100.713f, 526.4f, 100.754f, 527.1f, 100.773f
+path.cubicTo(SkBits2Float(0x4403f333), SkBits2Float(0x42c9962e), SkBits2Float(0x44042000), SkBits2Float(0x42c99755), SkBits2Float(0x44044ccd), SkBits2Float(0x42c9999a));  // 527.8f, 100.793f, 528.5f, 100.796f, 529.2f, 100.8f
+path.lineTo(SkBits2Float(0x44044ccd), SkBits2Float(0x42c9999a));  // 529.2f, 100.8f
+path.lineTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a));  // 447.3f, 100.8f
+path.close();
+    return path;
+}
+
+static SkPath path6() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a));  // 88.2f, 100.8f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a));  // 88.2f, 100.8f
+path.cubicTo(SkBits2Float(0x42b1cccd), SkBits2Float(0x42c9999a), SkBits2Float(0x42b33333), SkBits2Float(0x42c97f2d), SkBits2Float(0x42b4999a), SkBits2Float(0x42c9999a));  // 88.9f, 100.8f, 89.6f, 100.748f, 90.3f, 100.8f
+path.cubicTo(SkBits2Float(0x42b60000), SkBits2Float(0x42c9b408), SkBits2Float(0x42b76666), SkBits2Float(0x42ca1e13), SkBits2Float(0x42b8cccd), SkBits2Float(0x42ca382d));  // 91, 100.852f, 91.7f, 101.059f, 92.4f, 101.11f
+path.cubicTo(SkBits2Float(0x42ba3333), SkBits2Float(0x42ca5246), SkBits2Float(0x42bb999a), SkBits2Float(0x42ca3634), SkBits2Float(0x42bd0000), SkBits2Float(0x42ca3634));  // 93.1f, 101.161f, 93.8f, 101.106f, 94.5f, 101.106f
+path.cubicTo(SkBits2Float(0x42be6666), SkBits2Float(0x42ca3634), SkBits2Float(0x42bfcccd), SkBits2Float(0x42ca3869), SkBits2Float(0x42c13333), SkBits2Float(0x42ca382d));  // 95.2f, 101.106f, 95.9f, 101.11f, 96.6f, 101.11f
+path.cubicTo(SkBits2Float(0x42c2999a), SkBits2Float(0x42ca37f0), SkBits2Float(0x42c40000), SkBits2Float(0x42ca35ea), SkBits2Float(0x42c56666), SkBits2Float(0x42ca34c8));  // 97.3f, 101.109f, 98, 101.105f, 98.7f, 101.103f
+path.cubicTo(SkBits2Float(0x42c6cccd), SkBits2Float(0x42ca33a7), SkBits2Float(0x42c83333), SkBits2Float(0x42ca311c), SkBits2Float(0x42c9999a), SkBits2Float(0x42ca3164));  // 99.4f, 101.101f, 100.1f, 101.096f, 100.8f, 101.096f
+path.cubicTo(SkBits2Float(0x42cb0000), SkBits2Float(0x42ca31ad), SkBits2Float(0x42cc6666), SkBits2Float(0x42ca33fb), SkBits2Float(0x42cdcccd), SkBits2Float(0x42ca367b));  // 101.5f, 101.097f, 102.2f, 101.102f, 102.9f, 101.106f
+path.cubicTo(SkBits2Float(0x42cf3333), SkBits2Float(0x42ca38fa), SkBits2Float(0x42d0999a), SkBits2Float(0x42ca3fd0), SkBits2Float(0x42d20000), SkBits2Float(0x42ca4061));  // 103.6f, 101.111f, 104.3f, 101.125f, 105, 101.126f
+path.cubicTo(SkBits2Float(0x42d36666), SkBits2Float(0x42ca40f1), SkBits2Float(0x42d4cccd), SkBits2Float(0x42ca3f32), SkBits2Float(0x42d63333), SkBits2Float(0x42ca39df));  // 105.7f, 101.127f, 106.4f, 101.123f, 107.1f, 101.113f
+path.cubicTo(SkBits2Float(0x42d7999a), SkBits2Float(0x42ca348c), SkBits2Float(0x42d90000), SkBits2Float(0x42ca2585), SkBits2Float(0x42da6666), SkBits2Float(0x42ca206f));  // 107.8f, 101.103f, 108.5f, 101.073f, 109.2f, 101.063f
+path.cubicTo(SkBits2Float(0x42dbcccd), SkBits2Float(0x42ca1b59), SkBits2Float(0x42dd3333), SkBits2Float(0x42ca1a43), SkBits2Float(0x42de999a), SkBits2Float(0x42ca1b59));  // 109.9f, 101.053f, 110.6f, 101.051f, 111.3f, 101.053f
+path.cubicTo(SkBits2Float(0x42e00000), SkBits2Float(0x42ca1c6e), SkBits2Float(0x42e16666), SkBits2Float(0x42ca2872), SkBits2Float(0x42e2cccd), SkBits2Float(0x42ca26f1));  // 112, 101.056f, 112.7f, 101.079f, 113.4f, 101.076f
+path.cubicTo(SkBits2Float(0x42e43333), SkBits2Float(0x42ca2570), SkBits2Float(0x42e5999a), SkBits2Float(0x42ca1f14), SkBits2Float(0x42e70000), SkBits2Float(0x42ca1251));  // 114.1f, 101.073f, 114.8f, 101.061f, 115.5f, 101.036f
+path.cubicTo(SkBits2Float(0x42e86666), SkBits2Float(0x42ca058d), SkBits2Float(0x42e9cccd), SkBits2Float(0x42c9ec89), SkBits2Float(0x42eb3333), SkBits2Float(0x42c9da5b));  // 116.2f, 101.011f, 116.9f, 100.962f, 117.6f, 100.926f
+path.cubicTo(SkBits2Float(0x42ec999a), SkBits2Float(0x42c9c82d), SkBits2Float(0x42ee0000), SkBits2Float(0x42c9b007), SkBits2Float(0x42ef6666), SkBits2Float(0x42c9a53c));  // 118.3f, 100.891f, 119, 100.844f, 119.7f, 100.823f
+path.cubicTo(SkBits2Float(0x42f0cccd), SkBits2Float(0x42c99a71), SkBits2Float(0x42f23333), SkBits2Float(0x42c99b8b), SkBits2Float(0x42f3999a), SkBits2Float(0x42c9999a));  // 120.4f, 100.802f, 121.1f, 100.804f, 121.8f, 100.8f
+path.lineTo(SkBits2Float(0x42f3999a), SkBits2Float(0x42c9999a));  // 121.8f, 100.8f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a));  // 88.2f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c9999a));  // 128.1f, 100.8f
+path.lineTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c99b0f));  // 128.1f, 100.803f
+path.cubicTo(SkBits2Float(0x4300cccd), SkBits2Float(0x42c99bde), SkBits2Float(0x43018000), SkBits2Float(0x42c99eb2), SkBits2Float(0x43023333), SkBits2Float(0x42c99e73));  // 128.8f, 100.804f, 129.5f, 100.81f, 130.2f, 100.809f
+path.cubicTo(SkBits2Float(0x4302e666), SkBits2Float(0x42c99e35), SkBits2Float(0x4303999a), SkBits2Float(0x42c99a69), SkBits2Float(0x43044ccd), SkBits2Float(0x42c9999a));  // 130.9f, 100.809f, 131.6f, 100.802f, 132.3f, 100.8f
+path.lineTo(SkBits2Float(0x43044ccd), SkBits2Float(0x42c9999a));  // 132.3f, 100.8f
+path.lineTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c9999a));  // 128.1f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a));  // 155.4f, 100.8f
+path.lineTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a));  // 155.4f, 100.8f
+path.cubicTo(SkBits2Float(0x431c199a), SkBits2Float(0x42c9999a), SkBits2Float(0x431ccccd), SkBits2Float(0x42c99567), SkBits2Float(0x431d8000), SkBits2Float(0x42c9999a));  // 156.1f, 100.8f, 156.8f, 100.792f, 157.5f, 100.8f
+path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42c99dcd), SkBits2Float(0x431ee666), SkBits2Float(0x42c9a613), SkBits2Float(0x431f999a), SkBits2Float(0x42c9b2cd));  // 158.2f, 100.808f, 158.9f, 100.824f, 159.6f, 100.849f
+path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42c9bf86), SkBits2Float(0x43210000), SkBits2Float(0x42c9d893), SkBits2Float(0x4321b333), SkBits2Float(0x42c9e5f3));  // 160.3f, 100.874f, 161, 100.923f, 161.7f, 100.949f
+path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42c9f353), SkBits2Float(0x4323199a), SkBits2Float(0x42c9fd4f), SkBits2Float(0x4323cccd), SkBits2Float(0x42ca030e));  // 162.4f, 100.975f, 163.1f, 100.995f, 163.8f, 101.006f
+path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x42ca08cc), SkBits2Float(0x43253333), SkBits2Float(0x42ca07ce), SkBits2Float(0x4325e666), SkBits2Float(0x42ca086b));  // 164.5f, 101.017f, 165.2f, 101.015f, 165.9f, 101.016f
+path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x42ca0907), SkBits2Float(0x43274ccd), SkBits2Float(0x42ca0bdb), SkBits2Float(0x43280000), SkBits2Float(0x42ca06b9));  // 166.6f, 101.018f, 167.3f, 101.023f, 168, 101.013f
+path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x42ca0196), SkBits2Float(0x43296666), SkBits2Float(0x42c9f5dd), SkBits2Float(0x432a199a), SkBits2Float(0x42c9e99e));  // 168.7f, 101.003f, 169.4f, 100.98f, 170.1f, 100.956f
+path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x42c9dd5f), SkBits2Float(0x432b8000), SkBits2Float(0x42c9ca4c), SkBits2Float(0x432c3333), SkBits2Float(0x42c9bd40));  // 170.8f, 100.932f, 171.5f, 100.895f, 172.2f, 100.87f
+path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x42c9b034), SkBits2Float(0x432d999a), SkBits2Float(0x42c9a147), SkBits2Float(0x432e4ccd), SkBits2Float(0x42c99b56));  // 172.9f, 100.844f, 173.6f, 100.815f, 174.3f, 100.803f
+path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x42c99565), SkBits2Float(0x432fb333), SkBits2Float(0x42c999e4), SkBits2Float(0x43306666), SkBits2Float(0x42c9999a));  // 175, 100.792f, 175.7f, 100.801f, 176.4f, 100.8f
+path.lineTo(SkBits2Float(0x43306666), SkBits2Float(0x42c9999a));  // 176.4f, 100.8f
+path.lineTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a));  // 155.4f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a));  // 199.5f, 100.8f
+path.lineTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a));  // 199.5f, 100.8f
+path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42c9999a), SkBits2Float(0x4348e666), SkBits2Float(0x42c99640), SkBits2Float(0x4349999a), SkBits2Float(0x42c9999a));  // 200.2f, 100.8f, 200.9f, 100.793f, 201.6f, 100.8f
+path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42c99cf4), SkBits2Float(0x434b0000), SkBits2Float(0x42c9a741), SkBits2Float(0x434bb333), SkBits2Float(0x42c9adb7));  // 202.3f, 100.807f, 203, 100.827f, 203.7f, 100.839f
+path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x42c9b42c), SkBits2Float(0x434d199a), SkBits2Float(0x42c9bc15), SkBits2Float(0x434dcccd), SkBits2Float(0x42c9c05e));  // 204.4f, 100.852f, 205.1f, 100.867f, 205.8f, 100.876f
+path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x42c9c4a7), SkBits2Float(0x434f3333), SkBits2Float(0x42c9c572), SkBits2Float(0x434fe666), SkBits2Float(0x42c9c76d));  // 206.5f, 100.884f, 207.2f, 100.886f, 207.9f, 100.89f
+path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x42c9c967), SkBits2Float(0x43514ccd), SkBits2Float(0x42c9cb6f), SkBits2Float(0x43520000), SkBits2Float(0x42c9cc3d));  // 208.6f, 100.893f, 209.3f, 100.897f, 210, 100.899f
+path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x42c9cd0a), SkBits2Float(0x43536666), SkBits2Float(0x42c9cc85), SkBits2Float(0x4354199a), SkBits2Float(0x42c9cc3d));  // 210.7f, 100.9f, 211.4f, 100.899f, 212.1f, 100.899f
+path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x42c9cbf4), SkBits2Float(0x43558000), SkBits2Float(0x42c9c7f4), SkBits2Float(0x43563333), SkBits2Float(0x42c9ca8b));  // 212.8f, 100.898f, 213.5f, 100.891f, 214.2f, 100.896f
+path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x42c9cd22), SkBits2Float(0x4357999a), SkBits2Float(0x42c9d0e6), SkBits2Float(0x43584ccd), SkBits2Float(0x42c9dbc6));  // 214.9f, 100.901f, 215.6f, 100.908f, 216.3f, 100.929f
+path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x42c9e6a7), SkBits2Float(0x4359b333), SkBits2Float(0x42c9fd4d), SkBits2Float(0x435a6666), SkBits2Float(0x42ca0bcf));  // 217, 100.95f, 217.7f, 100.995f, 218.4f, 101.023f
+path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x42ca1a50), SkBits2Float(0x435bcccd), SkBits2Float(0x42ca29b9), SkBits2Float(0x435c8000), SkBits2Float(0x42ca32d0));  // 219.1f, 101.051f, 219.8f, 101.081f, 220.5f, 101.099f
+path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42ca3be7), SkBits2Float(0x435de666), SkBits2Float(0x42ca3af4), SkBits2Float(0x435e999a), SkBits2Float(0x42ca4259));  // 221.2f, 101.117f, 221.9f, 101.115f, 222.6f, 101.13f
+path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x42ca49be), SkBits2Float(0x43600000), SkBits2Float(0x42ca541c), SkBits2Float(0x4360b333), SkBits2Float(0x42ca5f2d));  // 223.3f, 101.144f, 224, 101.164f, 224.7f, 101.186f
+path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42ca6a3f), SkBits2Float(0x4362199a), SkBits2Float(0x42ca7a05), SkBits2Float(0x4362cccd), SkBits2Float(0x42ca84c3));  // 225.4f, 101.208f, 226.1f, 101.238f, 226.8f, 101.259f
+path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x42ca8f80), SkBits2Float(0x43643333), SkBits2Float(0x42ca9a0e), SkBits2Float(0x4364e666), SkBits2Float(0x42ca9f9e));  // 227.5f, 101.28f, 228.2f, 101.301f, 228.9f, 101.312f
+path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x42caa52d), SkBits2Float(0x43664ccd), SkBits2Float(0x42caa78a), SkBits2Float(0x43670000), SkBits2Float(0x42caa620));  // 229.6f, 101.323f, 230.3f, 101.327f, 231, 101.324f
+path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x42caa4b6), SkBits2Float(0x43686666), SkBits2Float(0x42ca9cbf), SkBits2Float(0x4369199a), SkBits2Float(0x42ca9723));  // 231.7f, 101.322f, 232.4f, 101.306f, 233.1f, 101.295f
+path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x42ca9188), SkBits2Float(0x436a8000), SkBits2Float(0x42ca894a), SkBits2Float(0x436b3333), SkBits2Float(0x42ca847c));  // 233.8f, 101.284f, 234.5f, 101.268f, 235.2f, 101.259f
+path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x42ca7fae), SkBits2Float(0x436c999a), SkBits2Float(0x42ca7c01), SkBits2Float(0x436d4ccd), SkBits2Float(0x42ca7a4f));  // 235.9f, 101.249f, 236.6f, 101.242f, 237.3f, 101.239f
+path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42ca789d), SkBits2Float(0x436eb333), SkBits2Float(0x42ca7976), SkBits2Float(0x436f6666), SkBits2Float(0x42ca7a4f));  // 238, 101.236f, 238.7f, 101.237f, 239.4f, 101.239f
+path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42ca7b28), SkBits2Float(0x4370cccd), SkBits2Float(0x42ca7ed5), SkBits2Float(0x43718000), SkBits2Float(0x42ca7f66));  // 240.1f, 101.241f, 240.8f, 101.248f, 241.5f, 101.249f
+path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x42ca7ff6), SkBits2Float(0x4372e666), SkBits2Float(0x42ca7e44), SkBits2Float(0x4373999a), SkBits2Float(0x42ca7db4));  // 242.2f, 101.25f, 242.9f, 101.247f, 243.6f, 101.246f
+path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42ca7d23), SkBits2Float(0x43750000), SkBits2Float(0x42ca7c4a), SkBits2Float(0x4375b333), SkBits2Float(0x42ca7c01));  // 244.3f, 101.244f, 245, 101.243f, 245.7f, 101.242f
+path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x42ca7bb9), SkBits2Float(0x4377199a), SkBits2Float(0x42ca7bb9), SkBits2Float(0x4377cccd), SkBits2Float(0x42ca7c01));  // 246.4f, 101.242f, 247.1f, 101.242f, 247.8f, 101.242f
+path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42ca7c4a), SkBits2Float(0x43793333), SkBits2Float(0x42ca7d6b), SkBits2Float(0x4379e666), SkBits2Float(0x42ca7db4));  // 248.5f, 101.243f, 249.2f, 101.245f, 249.9f, 101.246f
+path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42ca7dfc), SkBits2Float(0x437b4ccd), SkBits2Float(0x42ca7dfc), SkBits2Float(0x437c0000), SkBits2Float(0x42ca7db4));  // 250.6f, 101.246f, 251.3f, 101.246f, 252, 101.246f
+path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42ca7d6b), SkBits2Float(0x437d6666), SkBits2Float(0x42ca7c4a), SkBits2Float(0x437e199a), SkBits2Float(0x42ca7c01));  // 252.7f, 101.245f, 253.4f, 101.243f, 254.1f, 101.242f
+path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42ca7bb9), SkBits2Float(0x437f8000), SkBits2Float(0x42ca7bb9), SkBits2Float(0x4380199a), SkBits2Float(0x42ca7c01));  // 254.8f, 101.242f, 255.5f, 101.242f, 256.2f, 101.242f
+path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x42ca7c4a), SkBits2Float(0x4380cccd), SkBits2Float(0x42ca7d6b), SkBits2Float(0x43812666), SkBits2Float(0x42ca7db4));  // 256.9f, 101.243f, 257.6f, 101.245f, 258.3f, 101.246f
+path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x42ca7dfc), SkBits2Float(0x4381d99a), SkBits2Float(0x42ca7db4), SkBits2Float(0x43823333), SkBits2Float(0x42ca7db4));  // 259, 101.246f, 259.7f, 101.246f, 260.4f, 101.246f
+path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42ca7db4), SkBits2Float(0x4382e666), SkBits2Float(0x42ca7db4), SkBits2Float(0x43834000), SkBits2Float(0x42ca7db4));  // 261.1f, 101.246f, 261.8f, 101.246f, 262.5f, 101.246f
+path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42ca7db4), SkBits2Float(0x4383f333), SkBits2Float(0x42ca7d6b), SkBits2Float(0x43844ccd), SkBits2Float(0x42ca7db4));  // 263.2f, 101.246f, 263.9f, 101.245f, 264.6f, 101.246f
+path.cubicTo(SkBits2Float(0x4384a666), SkBits2Float(0x42ca7dfc), SkBits2Float(0x43850000), SkBits2Float(0x42ca7f1d), SkBits2Float(0x4385599a), SkBits2Float(0x42ca7f66));  // 265.3f, 101.246f, 266, 101.248f, 266.7f, 101.249f
+path.cubicTo(SkBits2Float(0x4385b333), SkBits2Float(0x42ca7fae), SkBits2Float(0x43860ccd), SkBits2Float(0x42ca7fae), SkBits2Float(0x43866666), SkBits2Float(0x42ca7f66));  // 267.4f, 101.249f, 268.1f, 101.249f, 268.8f, 101.249f
+path.cubicTo(SkBits2Float(0x4386c000), SkBits2Float(0x42ca7f1d), SkBits2Float(0x4387199a), SkBits2Float(0x42ca7e44), SkBits2Float(0x43877333), SkBits2Float(0x42ca7db4));  // 269.5f, 101.248f, 270.2f, 101.247f, 270.9f, 101.246f
+path.cubicTo(SkBits2Float(0x4387cccd), SkBits2Float(0x42ca7d23), SkBits2Float(0x43882666), SkBits2Float(0x42ca7c4a), SkBits2Float(0x43888000), SkBits2Float(0x42ca7c01));  // 271.6f, 101.244f, 272.3f, 101.243f, 273, 101.242f
+path.cubicTo(SkBits2Float(0x4388d99a), SkBits2Float(0x42ca7bb9), SkBits2Float(0x43893333), SkBits2Float(0x42ca7c92), SkBits2Float(0x43898ccd), SkBits2Float(0x42ca7c01));  // 273.7f, 101.242f, 274.4f, 101.243f, 275.1f, 101.242f
+path.cubicTo(SkBits2Float(0x4389e666), SkBits2Float(0x42ca7b71), SkBits2Float(0x438a4000), SkBits2Float(0x42ca789d), SkBits2Float(0x438a999a), SkBits2Float(0x42ca789d));  // 275.8f, 101.241f, 276.5f, 101.236f, 277.2f, 101.236f
+path.cubicTo(SkBits2Float(0x438af333), SkBits2Float(0x42ca789d), SkBits2Float(0x438b4ccd), SkBits2Float(0x42ca78da), SkBits2Float(0x438ba666), SkBits2Float(0x42ca7c01));  // 277.9f, 101.236f, 278.6f, 101.236f, 279.3f, 101.242f
+path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x42ca7f29), SkBits2Float(0x438c599a), SkBits2Float(0x42ca8863), SkBits2Float(0x438cb333), SkBits2Float(0x42ca8b8b));  // 280, 101.248f, 280.7f, 101.266f, 281.4f, 101.273f
+path.cubicTo(SkBits2Float(0x438d0ccd), SkBits2Float(0x42ca8eb3), SkBits2Float(0x438d6666), SkBits2Float(0x42ca9411), SkBits2Float(0x438dc000), SkBits2Float(0x42ca8eef));  // 282.1f, 101.279f, 282.8f, 101.289f, 283.5f, 101.279f
+path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x42ca89cd), SkBits2Float(0x438e7333), SkBits2Float(0x42ca7cb5), SkBits2Float(0x438ecccd), SkBits2Float(0x42ca6cbe));  // 284.2f, 101.269f, 284.9f, 101.244f, 285.6f, 101.212f
+path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42ca5cc7), SkBits2Float(0x438f8000), SkBits2Float(0x42ca4681), SkBits2Float(0x438fd99a), SkBits2Float(0x42ca2f25));  // 286.3f, 101.181f, 287, 101.138f, 287.7f, 101.092f
+path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42ca17c9), SkBits2Float(0x43908ccd), SkBits2Float(0x42c9f983), SkBits2Float(0x4390e666), SkBits2Float(0x42c9e096));  // 288.4f, 101.046f, 289.1f, 100.987f, 289.8f, 100.939f
+path.cubicTo(SkBits2Float(0x43914000), SkBits2Float(0x42c9c7aa), SkBits2Float(0x4391999a), SkBits2Float(0x42c9a56f), SkBits2Float(0x4391f333), SkBits2Float(0x42c9999a));  // 290.5f, 100.89f, 291.2f, 100.823f, 291.9f, 100.8f
+path.cubicTo(SkBits2Float(0x43924ccd), SkBits2Float(0x42c98dc6), SkBits2Float(0x4392a666), SkBits2Float(0x42c9999a), SkBits2Float(0x43930000), SkBits2Float(0x42c9999a));  // 292.6f, 100.777f, 293.3f, 100.8f, 294, 100.8f
+path.lineTo(SkBits2Float(0x43930000), SkBits2Float(0x42c9999a));  // 294, 100.8f
+path.lineTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a));  // 199.5f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a));  // 342.3f, 100.8f
+path.lineTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a));  // 342.3f, 100.8f
+path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x42c9999a), SkBits2Float(0x43abd99a), SkBits2Float(0x42c98e0e), SkBits2Float(0x43ac3333), SkBits2Float(0x42c9999a));  // 343, 100.8f, 343.7f, 100.777f, 344.4f, 100.8f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42c9a527), SkBits2Float(0x43ace666), SkBits2Float(0x42c9bd29), SkBits2Float(0x43ad4000), SkBits2Float(0x42c9dee4));  // 345.1f, 100.823f, 345.8f, 100.869f, 346.5f, 100.935f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42ca009f), SkBits2Float(0x43adf333), SkBits2Float(0x42ca4737), SkBits2Float(0x43ae4ccd), SkBits2Float(0x42ca63fd));  // 347.2f, 101.001f, 347.9f, 101.139f, 348.6f, 101.195f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42ca80c4), SkBits2Float(0x43af0000), SkBits2Float(0x42ca834d), SkBits2Float(0x43af599a), SkBits2Float(0x42ca8b8b));  // 349.3f, 101.251f, 350, 101.256f, 350.7f, 101.273f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42ca93c9), SkBits2Float(0x43b00ccd), SkBits2Float(0x42ca92a9), SkBits2Float(0x43b06666), SkBits2Float(0x42ca9571));  // 351.4f, 101.289f, 352.1f, 101.286f, 352.8f, 101.292f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42ca9839), SkBits2Float(0x43b1199a), SkBits2Float(0x42ca9ad0), SkBits2Float(0x43b17333), SkBits2Float(0x42ca9c3a));  // 353.5f, 101.297f, 354.2f, 101.302f, 354.9f, 101.305f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42ca9da3), SkBits2Float(0x43b22666), SkBits2Float(0x42ca9da3), SkBits2Float(0x43b28000), SkBits2Float(0x42ca9dec));  // 355.6f, 101.308f, 356.3f, 101.308f, 357, 101.308f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x42ca9e34), SkBits2Float(0x43b33333), SkBits2Float(0x42ca9e34), SkBits2Float(0x43b38ccd), SkBits2Float(0x42ca9dec));  // 357.7f, 101.309f, 358.4f, 101.309f, 359.1f, 101.308f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42ca9da3), SkBits2Float(0x43b44000), SkBits2Float(0x42ca9c82), SkBits2Float(0x43b4999a), SkBits2Float(0x42ca9c3a));  // 359.8f, 101.308f, 360.5f, 101.306f, 361.2f, 101.305f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x42ca9bf1), SkBits2Float(0x43b54ccd), SkBits2Float(0x42ca9c3a), SkBits2Float(0x43b5a666), SkBits2Float(0x42ca9c3a));  // 361.9f, 101.305f, 362.6f, 101.305f, 363.3f, 101.305f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42ca9c3a), SkBits2Float(0x43b6599a), SkBits2Float(0x42ca9bf1), SkBits2Float(0x43b6b333), SkBits2Float(0x42ca9c3a));  // 364, 101.305f, 364.7f, 101.305f, 365.4f, 101.305f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42ca9c82), SkBits2Float(0x43b76666), SkBits2Float(0x42ca9da3), SkBits2Float(0x43b7c000), SkBits2Float(0x42ca9dec));  // 366.1f, 101.306f, 366.8f, 101.308f, 367.5f, 101.308f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x42ca9e34), SkBits2Float(0x43b87333), SkBits2Float(0x42ca9dec), SkBits2Float(0x43b8cccd), SkBits2Float(0x42ca9dec));  // 368.2f, 101.309f, 368.9f, 101.308f, 369.6f, 101.308f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x42ca9dec), SkBits2Float(0x43b98000), SkBits2Float(0x42ca9dec), SkBits2Float(0x43b9d99a), SkBits2Float(0x42ca9dec));  // 370.3f, 101.308f, 371, 101.308f, 371.7f, 101.308f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x42ca9dec), SkBits2Float(0x43ba8ccd), SkBits2Float(0x42ca9e7d), SkBits2Float(0x43bae666), SkBits2Float(0x42ca9dec));  // 372.4f, 101.308f, 373.1f, 101.31f, 373.8f, 101.308f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x42ca9d5b), SkBits2Float(0x43bb999a), SkBits2Float(0x42ca9b61), SkBits2Float(0x43bbf333), SkBits2Float(0x42ca9a88));  // 374.5f, 101.307f, 375.2f, 101.303f, 375.9f, 101.302f
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x42ca99af), SkBits2Float(0x43bca666), SkBits2Float(0x42ca991e), SkBits2Float(0x43bd0000), SkBits2Float(0x42ca98d5));  // 376.6f, 101.3f, 377.3f, 101.299f, 378, 101.299f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42ca988d), SkBits2Float(0x43bdb333), SkBits2Float(0x42ca988d), SkBits2Float(0x43be0ccd), SkBits2Float(0x42ca98d5));  // 378.7f, 101.298f, 379.4f, 101.298f, 380.1f, 101.299f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42ca991e), SkBits2Float(0x43bec000), SkBits2Float(0x42ca99f7), SkBits2Float(0x43bf199a), SkBits2Float(0x42ca9a88));  // 380.8f, 101.299f, 381.5f, 101.301f, 382.2f, 101.302f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42ca9b18), SkBits2Float(0x43bfcccd), SkBits2Float(0x42ca9ba9), SkBits2Float(0x43c02666), SkBits2Float(0x42ca9c3a));  // 382.9f, 101.303f, 383.6f, 101.304f, 384.3f, 101.305f
+path.cubicTo(SkBits2Float(0x43c08000), SkBits2Float(0x42ca9cca), SkBits2Float(0x43c0d99a), SkBits2Float(0x42ca9ec5), SkBits2Float(0x43c13333), SkBits2Float(0x42ca9dec));  // 385, 101.306f, 385.7f, 101.31f, 386.4f, 101.308f
+path.cubicTo(SkBits2Float(0x43c18ccd), SkBits2Float(0x42ca9d13), SkBits2Float(0x43c1e666), SkBits2Float(0x42ca9a3f), SkBits2Float(0x43c24000), SkBits2Float(0x42ca9723));  // 387.1f, 101.307f, 387.8f, 101.301f, 388.5f, 101.295f
+path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42ca9407), SkBits2Float(0x43c2f333), SkBits2Float(0x42ca8d87), SkBits2Float(0x43c34ccd), SkBits2Float(0x42ca8b45));  // 389.2f, 101.289f, 389.9f, 101.276f, 390.6f, 101.272f
+path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42ca8902), SkBits2Float(0x43c40000), SkBits2Float(0x42ca8992), SkBits2Float(0x43c4599a), SkBits2Float(0x42ca8992));  // 391.3f, 101.268f, 392, 101.269f, 392.7f, 101.269f
+path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42ca8992), SkBits2Float(0x43c50ccd), SkBits2Float(0x42ca8a6b), SkBits2Float(0x43c56666), SkBits2Float(0x42ca8b45));  // 393.4f, 101.269f, 394.1f, 101.27f, 394.8f, 101.272f
+path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42ca8c1e), SkBits2Float(0x43c6199a), SkBits2Float(0x42ca8d87), SkBits2Float(0x43c67333), SkBits2Float(0x42ca8ea9));  // 395.5f, 101.274f, 396.2f, 101.276f, 396.9f, 101.279f
+path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x42ca8fca), SkBits2Float(0x43c72666), SkBits2Float(0x42ca90a3), SkBits2Float(0x43c78000), SkBits2Float(0x42ca920d));  // 397.6f, 101.281f, 398.3f, 101.282f, 399, 101.285f
+path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x42ca9377), SkBits2Float(0x43c83333), SkBits2Float(0x42ca9571), SkBits2Float(0x43c88ccd), SkBits2Float(0x42ca9723));  // 399.7f, 101.288f, 400.4f, 101.292f, 401.1f, 101.295f
+path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x42ca98d5), SkBits2Float(0x43c94000), SkBits2Float(0x42ca9a88), SkBits2Float(0x43c9999a), SkBits2Float(0x42ca9c3a));  // 401.8f, 101.299f, 402.5f, 101.302f, 403.2f, 101.305f
+path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42ca9dec), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42caa02f), SkBits2Float(0x43caa666), SkBits2Float(0x42caa150));  // 403.9f, 101.308f, 404.6f, 101.313f, 405.3f, 101.315f
+path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42caa271), SkBits2Float(0x43cb599a), SkBits2Float(0x42caa46c), SkBits2Float(0x43cbb333), SkBits2Float(0x42caa302));  // 406, 101.317f, 406.7f, 101.321f, 407.4f, 101.318f
+path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x42caa198), SkBits2Float(0x43cc6666), SkBits2Float(0x42ca9ad0), SkBits2Float(0x43ccc000), SkBits2Float(0x42ca98d5));  // 408.1f, 101.316f, 408.8f, 101.302f, 409.5f, 101.299f
+path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x42ca96db), SkBits2Float(0x43cd7333), SkBits2Float(0x42ca9535), SkBits2Float(0x43cdcccd), SkBits2Float(0x42ca9723));  // 410.2f, 101.295f, 410.9f, 101.291f, 411.6f, 101.295f
+path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x42ca9912), SkBits2Float(0x43ce8000), SkBits2Float(0x42caa19a), SkBits2Float(0x43ced99a), SkBits2Float(0x42caa46e));  // 412.3f, 101.299f, 413, 101.316f, 413.7f, 101.321f
+path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42caa741), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42caa7c4), SkBits2Float(0x43cfe666), SkBits2Float(0x42caa819));  // 414.4f, 101.327f, 415.1f, 101.328f, 415.8f, 101.328f
+path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42caa86d), SkBits2Float(0x43d0999a), SkBits2Float(0x42caa788), SkBits2Float(0x43d0f333), SkBits2Float(0x42caa666));  // 416.5f, 101.329f, 417.2f, 101.327f, 417.9f, 101.325f
+path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42caa545), SkBits2Float(0x43d1a666), SkBits2Float(0x42caa302), SkBits2Float(0x43d20000), SkBits2Float(0x42caa150));  // 418.6f, 101.323f, 419.3f, 101.318f, 420, 101.315f
+path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42ca9f9e), SkBits2Float(0x43d2b333), SkBits2Float(0x42ca9d13), SkBits2Float(0x43d30ccd), SkBits2Float(0x42ca9c3a));  // 420.7f, 101.312f, 421.4f, 101.307f, 422.1f, 101.305f
+path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x42ca9b61), SkBits2Float(0x43d3c000), SkBits2Float(0x42ca9b24), SkBits2Float(0x43d4199a), SkBits2Float(0x42ca9c3a));  // 422.8f, 101.303f, 423.5f, 101.303f, 424.2f, 101.305f
+path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42ca9d4f), SkBits2Float(0x43d4cccd), SkBits2Float(0x42caa59b), SkBits2Float(0x43d52666), SkBits2Float(0x42caa2bc));  // 424.9f, 101.307f, 425.6f, 101.323f, 426.3f, 101.318f
+path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42ca9fdc), SkBits2Float(0x43d5d99a), SkBits2Float(0x42caa425), SkBits2Float(0x43d63333), SkBits2Float(0x42ca8afe));  // 427, 101.312f, 427.7f, 101.321f, 428.4f, 101.271f
+path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42ca71d6), SkBits2Float(0x43d6e666), SkBits2Float(0x42ca340a), SkBits2Float(0x43d74000), SkBits2Float(0x42ca0bcf));  // 429.1f, 101.222f, 429.8f, 101.102f, 430.5f, 101.023f
+path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x42c9e394), SkBits2Float(0x43d7f333), SkBits2Float(0x42c9aca3), SkBits2Float(0x43d84ccd), SkBits2Float(0x42c9999a));  // 431.2f, 100.944f, 431.9f, 100.837f, 432.6f, 100.8f
+path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x42c98692), SkBits2Float(0x43d90000), SkBits2Float(0x42c9999a), SkBits2Float(0x43d9599a), SkBits2Float(0x42c9999a));  // 433.3f, 100.763f, 434, 100.8f, 434.7f, 100.8f
+path.lineTo(SkBits2Float(0x43d9599a), SkBits2Float(0x42c9999a));  // 434.7f, 100.8f
+path.lineTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a));  // 342.3f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a));  // 447.3f, 100.8f
+path.lineTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a));  // 447.3f, 100.8f
+path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x42c99abd), SkBits2Float(0x43e0599a), SkBits2Float(0x42c9a022), SkBits2Float(0x43e0b333), SkBits2Float(0x42c9a06c));  // 448, 100.802f, 448.7f, 100.813f, 449.4f, 100.813f
+path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42c9a0b6), SkBits2Float(0x43e16666), SkBits2Float(0x42c99c79), SkBits2Float(0x43e1c000), SkBits2Float(0x42c99b56));  // 450.1f, 100.814f, 450.8f, 100.806f, 451.5f, 100.803f
+path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x42c99a33), SkBits2Float(0x43e27333), SkBits2Float(0x42c9928a), SkBits2Float(0x43e2cccd), SkBits2Float(0x42c9999a));  // 452.2f, 100.801f, 452.9f, 100.786f, 453.6f, 100.8f
+path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x42c9a0ab), SkBits2Float(0x43e38000), SkBits2Float(0x42c9b682), SkBits2Float(0x43e3d99a), SkBits2Float(0x42c9c5bb));  // 454.3f, 100.814f, 455, 100.856f, 455.7f, 100.886f
+path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x42c9d4f4), SkBits2Float(0x43e48ccd), SkBits2Float(0x42c9ef91), SkBits2Float(0x43e4e666), SkBits2Float(0x42c9f4f0));  // 456.4f, 100.916f, 457.1f, 100.968f, 457.8f, 100.978f
+path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42c9fa4e), SkBits2Float(0x43e5999a), SkBits2Float(0x42c9e940), SkBits2Float(0x43e5f333), SkBits2Float(0x42c9e5f3));  // 458.5f, 100.989f, 459.2f, 100.956f, 459.9f, 100.949f
+path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42c9e2a6), SkBits2Float(0x43e6a666), SkBits2Float(0x42c9e366), SkBits2Float(0x43e70000), SkBits2Float(0x42c9e123));  // 460.6f, 100.943f, 461.3f, 100.944f, 462, 100.94f
+path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42c9dee0), SkBits2Float(0x43e7b333), SkBits2Float(0x42c9d9d8), SkBits2Float(0x43e80ccd), SkBits2Float(0x42c9d862));  // 462.7f, 100.935f, 463.4f, 100.925f, 464.1f, 100.923f
+path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42c9d6ed), SkBits2Float(0x43e8c000), SkBits2Float(0x42c9d93b), SkBits2Float(0x43e9199a), SkBits2Float(0x42c9d862));  // 464.8f, 100.92f, 465.5f, 100.924f, 466.2f, 100.923f
+path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42c9d789), SkBits2Float(0x43e9cccd), SkBits2Float(0x42c9d614), SkBits2Float(0x43ea2666), SkBits2Float(0x42c9d34c));  // 466.9f, 100.921f, 467.6f, 100.918f, 468.3f, 100.913f
+path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42c9d084), SkBits2Float(0x43ead99a), SkBits2Float(0x42c9cbf1), SkBits2Float(0x43eb3333), SkBits2Float(0x42c9c7b4));  // 469, 100.907f, 469.7f, 100.898f, 470.4f, 100.89f
+path.cubicTo(SkBits2Float(0x43eb8ccd), SkBits2Float(0x42c9c376), SkBits2Float(0x43ebe666), SkBits2Float(0x42c9bde8), SkBits2Float(0x43ec4000), SkBits2Float(0x42c9b9dc));  // 471.1f, 100.882f, 471.8f, 100.871f, 472.5f, 100.863f
+path.cubicTo(SkBits2Float(0x43ec999a), SkBits2Float(0x42c9b5cf), SkBits2Float(0x43ecf333), SkBits2Float(0x42c9b442), SkBits2Float(0x43ed4ccd), SkBits2Float(0x42c9af69));  // 473.2f, 100.855f, 473.9f, 100.852f, 474.6f, 100.843f
+path.cubicTo(SkBits2Float(0x43eda666), SkBits2Float(0x42c9aa8f), SkBits2Float(0x43ee0000), SkBits2Float(0x42c9a064), SkBits2Float(0x43ee599a), SkBits2Float(0x42c99cc1));  // 475.3f, 100.833f, 476, 100.813f, 476.7f, 100.806f
+path.cubicTo(SkBits2Float(0x43eeb333), SkBits2Float(0x42c9991f), SkBits2Float(0x43ef0ccd), SkBits2Float(0x42c99758), SkBits2Float(0x43ef6666), SkBits2Float(0x42c9999a));  // 477.4f, 100.799f, 478.1f, 100.796f, 478.8f, 100.8f
+path.cubicTo(SkBits2Float(0x43efc000), SkBits2Float(0x42c99bdd), SkBits2Float(0x43f0199a), SkBits2Float(0x42c99f0e), SkBits2Float(0x43f07333), SkBits2Float(0x42c9aa52));  // 479.5f, 100.804f, 480.2f, 100.811f, 480.9f, 100.833f
+path.cubicTo(SkBits2Float(0x43f0cccd), SkBits2Float(0x42c9b596), SkBits2Float(0x43f12666), SkBits2Float(0x42c9cfde), SkBits2Float(0x43f18000), SkBits2Float(0x42c9dd32));  // 481.6f, 100.855f, 482.3f, 100.906f, 483, 100.932f
+path.cubicTo(SkBits2Float(0x43f1d99a), SkBits2Float(0x42c9ea86), SkBits2Float(0x43f23333), SkBits2Float(0x42c9f451), SkBits2Float(0x43f28ccd), SkBits2Float(0x42c9fa4d));  // 483.7f, 100.958f, 484.4f, 100.977f, 485.1f, 100.989f
+path.cubicTo(SkBits2Float(0x43f2e666), SkBits2Float(0x42ca0048), SkBits2Float(0x43f34000), SkBits2Float(0x42c9fbf3), SkBits2Float(0x43f3999a), SkBits2Float(0x42ca0115));  // 485.8f, 101.001f, 486.5f, 100.992f, 487.2f, 101.002f
+path.cubicTo(SkBits2Float(0x43f3f333), SkBits2Float(0x42ca0637), SkBits2Float(0x43f44ccd), SkBits2Float(0x42ca103f), SkBits2Float(0x43f4a666), SkBits2Float(0x42ca1919));  // 487.9f, 101.012f, 488.6f, 101.032f, 489.3f, 101.049f
+path.cubicTo(SkBits2Float(0x43f50000), SkBits2Float(0x42ca21f4), SkBits2Float(0x43f5599a), SkBits2Float(0x42ca2e87), SkBits2Float(0x43f5b333), SkBits2Float(0x42ca3634));  // 490, 101.066f, 490.7f, 101.091f, 491.4f, 101.106f
+path.cubicTo(SkBits2Float(0x43f60ccd), SkBits2Float(0x42ca3de1), SkBits2Float(0x43f66666), SkBits2Float(0x42ca43b9), SkBits2Float(0x43f6c000), SkBits2Float(0x42ca4729));  // 492.1f, 101.121f, 492.8f, 101.132f, 493.5f, 101.139f
+path.cubicTo(SkBits2Float(0x43f7199a), SkBits2Float(0x42ca4a99), SkBits2Float(0x43f77333), SkBits2Float(0x42ca49b3), SkBits2Float(0x43f7cccd), SkBits2Float(0x42ca4ad4));  // 494.2f, 101.146f, 494.9f, 101.144f, 495.6f, 101.146f
+path.cubicTo(SkBits2Float(0x43f82666), SkBits2Float(0x42ca4bf5), SkBits2Float(0x43f88000), SkBits2Float(0x42ca4cdc), SkBits2Float(0x43f8d99a), SkBits2Float(0x42ca4df2));  // 496.3f, 101.148f, 497, 101.15f, 497.7f, 101.152f
+path.cubicTo(SkBits2Float(0x43f93333), SkBits2Float(0x42ca4f07), SkBits2Float(0x43f98ccd), SkBits2Float(0x42ca4fec), SkBits2Float(0x43f9e666), SkBits2Float(0x42ca5156));  // 498.4f, 101.154f, 499.1f, 101.156f, 499.8f, 101.159f
+path.cubicTo(SkBits2Float(0x43fa4000), SkBits2Float(0x42ca52c0), SkBits2Float(0x43fa999a), SkBits2Float(0x42ca53e1), SkBits2Float(0x43faf333), SkBits2Float(0x42ca566c));  // 500.5f, 101.162f, 501.2f, 101.164f, 501.9f, 101.169f
+path.cubicTo(SkBits2Float(0x43fb4ccd), SkBits2Float(0x42ca58f7), SkBits2Float(0x43fba666), SkBits2Float(0x42ca5d71), SkBits2Float(0x43fc0000), SkBits2Float(0x42ca6099));  // 502.6f, 101.174f, 503.3f, 101.183f, 504, 101.189f
+path.cubicTo(SkBits2Float(0x43fc599a), SkBits2Float(0x42ca63c1), SkBits2Float(0x43fcb333), SkBits2Float(0x42ca663e), SkBits2Float(0x43fd0ccd), SkBits2Float(0x42ca695a));  // 504.7f, 101.195f, 505.4f, 101.2f, 506.1f, 101.206f
+path.cubicTo(SkBits2Float(0x43fd6666), SkBits2Float(0x42ca6c76), SkBits2Float(0x43fdc000), SkBits2Float(0x42ca7024), SkBits2Float(0x43fe199a), SkBits2Float(0x42ca7340));  // 506.8f, 101.212f, 507.5f, 101.219f, 508.2f, 101.225f
+path.cubicTo(SkBits2Float(0x43fe7333), SkBits2Float(0x42ca765c), SkBits2Float(0x43fecccd), SkBits2Float(0x42ca7a44), SkBits2Float(0x43ff2666), SkBits2Float(0x42ca7c01));  // 508.9f, 101.231f, 509.6f, 101.239f, 510.3f, 101.242f
+path.cubicTo(SkBits2Float(0x43ff8000), SkBits2Float(0x42ca7dbf), SkBits2Float(0x43ffd99a), SkBits2Float(0x42ca7ed5), SkBits2Float(0x4400199a), SkBits2Float(0x42ca7db4));  // 511, 101.246f, 511.7f, 101.248f, 512.4f, 101.246f
+path.cubicTo(SkBits2Float(0x44004666), SkBits2Float(0x42ca7c92), SkBits2Float(0x44007333), SkBits2Float(0x42ca780c), SkBits2Float(0x4400a000), SkBits2Float(0x42ca7539));  // 513.1f, 101.243f, 513.8f, 101.234f, 514.5f, 101.229f
+path.cubicTo(SkBits2Float(0x4400cccd), SkBits2Float(0x42ca7265), SkBits2Float(0x4400f99a), SkBits2Float(0x42ca7017), SkBits2Float(0x44012666), SkBits2Float(0x42ca6cbe));  // 515.2f, 101.223f, 515.9f, 101.219f, 516.6f, 101.212f
+path.cubicTo(SkBits2Float(0x44015333), SkBits2Float(0x42ca6966), SkBits2Float(0x44018000), SkBits2Float(0x42ca688b), SkBits2Float(0x4401accd), SkBits2Float(0x42ca6126));  // 517.3f, 101.206f, 518, 101.204f, 518.7f, 101.19f
+path.cubicTo(SkBits2Float(0x4401d99a), SkBits2Float(0x42ca59c1), SkBits2Float(0x44020666), SkBits2Float(0x42ca4eee), SkBits2Float(0x44023333), SkBits2Float(0x42ca4061));  // 519.4f, 101.175f, 520.1f, 101.154f, 520.8f, 101.126f
+path.cubicTo(SkBits2Float(0x44026000), SkBits2Float(0x42ca31d3), SkBits2Float(0x44028ccd), SkBits2Float(0x42ca1b74), SkBits2Float(0x4402b99a), SkBits2Float(0x42ca09d6));  // 521.5f, 101.097f, 522.2f, 101.054f, 522.9f, 101.019f
+path.cubicTo(SkBits2Float(0x4402e666), SkBits2Float(0x42c9f839), SkBits2Float(0x44031333), SkBits2Float(0x42c9e720), SkBits2Float(0x44034000), SkBits2Float(0x42c9d6b0));  // 523.6f, 100.985f, 524.3f, 100.951f, 525, 100.919f
+path.cubicTo(SkBits2Float(0x44036ccd), SkBits2Float(0x42c9c640), SkBits2Float(0x4403999a), SkBits2Float(0x42c9b163), SkBits2Float(0x4403c666), SkBits2Float(0x42c9a735));  // 525.7f, 100.887f, 526.4f, 100.846f, 527.1f, 100.827f
+path.cubicTo(SkBits2Float(0x4403f333), SkBits2Float(0x42c99d06), SkBits2Float(0x44042000), SkBits2Float(0x42c99bdf), SkBits2Float(0x44044ccd), SkBits2Float(0x42c9999a));  // 527.8f, 100.807f, 528.5f, 100.804f, 529.2f, 100.8f
+path.lineTo(SkBits2Float(0x44044ccd), SkBits2Float(0x42c9999a));  // 529.2f, 100.8f
+path.lineTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a));  // 447.3f, 100.8f
+path.close();
+    return path;
+}
+
+static void issue3651_1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path = path1();
+    SkPath pathB = path2();
+    // DEBUG_UNDER_DEVELOPMENT  issue3651_1 disable expectation check for now
+    testPathOpCheck(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename,
+            !SkOpGlobalState::DebugRunFail());
+}
+
+static void issue3651_2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path = path3();
+    SkPath pathB = path4();
+    testPathOp(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename);
+}
+
+static void issue3651_3(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path = path5();
+    SkPath pathB = path6();
+    testPathOp(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename);
+}
+
+static void issue3651_4(skiatest::Reporter* reporter, const char* filename) {
+SkPath path;
+path.moveTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666));  // 32.8f, 180.4f
+path.lineTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666));  // 32.8f, 180.4f
+path.cubicTo(SkBits2Float(0x4205eeef), SkBits2Float(0x43346666), SkBits2Float(0x4208aaab), SkBits2Float(0x4334780f), SkBits2Float(0x420b6666), SkBits2Float(0x43346666));  // 33.4833f, 180.4f, 34.1667f, 180.469f, 34.85f, 180.4f
+path.cubicTo(SkBits2Float(0x420e2222), SkBits2Float(0x433454be), SkBits2Float(0x4210ddde), SkBits2Float(0x43340d56), SkBits2Float(0x4213999a), SkBits2Float(0x4333fc72));  // 35.5333f, 180.331f, 36.2167f, 180.052f, 36.9f, 179.986f
+path.cubicTo(SkBits2Float(0x42165555), SkBits2Float(0x4333eb8e), SkBits2Float(0x42191111), SkBits2Float(0x4333fbf4), SkBits2Float(0x421bcccd), SkBits2Float(0x4334010f));  // 37.5833f, 179.92f, 38.2667f, 179.984f, 38.95f, 180.004f
+path.cubicTo(SkBits2Float(0x421e8889), SkBits2Float(0x4334062b), SkBits2Float(0x42214444), SkBits2Float(0x43341213), SkBits2Float(0x42240000), SkBits2Float(0x43341b17));  // 39.6333f, 180.024f, 40.3167f, 180.071f, 41, 180.106f
+path.cubicTo(SkBits2Float(0x4226bbbc), SkBits2Float(0x4334241b), SkBits2Float(0x42297777), SkBits2Float(0x4334339e), SkBits2Float(0x422c3333), SkBits2Float(0x43343728));  // 41.6833f, 180.141f, 42.3667f, 180.202f, 43.05f, 180.215f
+path.cubicTo(SkBits2Float(0x422eeeef), SkBits2Float(0x43343ab2), SkBits2Float(0x4231aaab), SkBits2Float(0x4334337f), SkBits2Float(0x42346666), SkBits2Float(0x43343054));  // 43.7333f, 180.229f, 44.4167f, 180.201f, 45.1f, 180.189f
+path.cubicTo(SkBits2Float(0x42372222), SkBits2Float(0x43342d28), SkBits2Float(0x4239ddde), SkBits2Float(0x43342281), SkBits2Float(0x423c999a), SkBits2Float(0x43342423));  // 45.7833f, 180.176f, 46.4667f, 180.135f, 47.15f, 180.141f
+path.cubicTo(SkBits2Float(0x423f5555), SkBits2Float(0x433425c5), SkBits2Float(0x42421111), SkBits2Float(0x43343381), SkBits2Float(0x4244cccd), SkBits2Float(0x43343a1f));  // 47.8333f, 180.148f, 48.5167f, 180.201f, 49.2f, 180.227f
+path.cubicTo(SkBits2Float(0x42478889), SkBits2Float(0x433440be), SkBits2Float(0x424a4444), SkBits2Float(0x43344d5d), SkBits2Float(0x424d0000), SkBits2Float(0x43344bdb));  // 49.8833f, 180.253f, 50.5667f, 180.302f, 51.25f, 180.296f
+path.cubicTo(SkBits2Float(0x424fbbbc), SkBits2Float(0x43344a59), SkBits2Float(0x42527777), SkBits2Float(0x43342ca7), SkBits2Float(0x42553333), SkBits2Float(0x43343113));  // 51.9333f, 180.29f, 52.6167f, 180.174f, 53.3f, 180.192f
+path.cubicTo(SkBits2Float(0x4257eeef), SkBits2Float(0x43343580), SkBits2Float(0x425aaaab), SkBits2Float(0x4334654b), SkBits2Float(0x425d6666), SkBits2Float(0x43346666));  // 53.9833f, 180.209f, 54.6667f, 180.396f, 55.35f, 180.4f
+path.cubicTo(SkBits2Float(0x42602222), SkBits2Float(0x43346782), SkBits2Float(0x4262ddde), SkBits2Float(0x43343ee4), SkBits2Float(0x4265999a), SkBits2Float(0x433437ba));  // 56.0333f, 180.404f, 56.7167f, 180.246f, 57.4f, 180.218f
+path.cubicTo(SkBits2Float(0x42685555), SkBits2Float(0x4334308f), SkBits2Float(0x426b1111), SkBits2Float(0x43343ce3), SkBits2Float(0x426dcccd), SkBits2Float(0x43343b69));  // 58.0833f, 180.19f, 58.7667f, 180.238f, 59.45f, 180.232f
+path.cubicTo(SkBits2Float(0x42708889), SkBits2Float(0x433439ef), SkBits2Float(0x42734444), SkBits2Float(0x433437d8), SkBits2Float(0x42760000), SkBits2Float(0x43342edc));  // 60.1333f, 180.226f, 60.8167f, 180.218f, 61.5f, 180.183f
+path.cubicTo(SkBits2Float(0x4278bbbc), SkBits2Float(0x433425df), SkBits2Float(0x427b7777), SkBits2Float(0x43340d4f), SkBits2Float(0x427e3333), SkBits2Float(0x4334057e));  // 62.1833f, 180.148f, 62.8667f, 180.052f, 63.55f, 180.021f
+path.cubicTo(SkBits2Float(0x42807777), SkBits2Float(0x4333fdad), SkBits2Float(0x4281d555), SkBits2Float(0x4333fb55), SkBits2Float(0x42833333), SkBits2Float(0x4333fff3));  // 64.2333f, 179.991f, 64.9167f, 179.982f, 65.6f, 180
+path.cubicTo(SkBits2Float(0x42849111), SkBits2Float(0x43340492), SkBits2Float(0x4285eeef), SkBits2Float(0x43341020), SkBits2Float(0x42874ccd), SkBits2Float(0x43342133));  // 66.2833f, 180.018f, 66.9667f, 180.063f, 67.65f, 180.13f
+path.cubicTo(SkBits2Float(0x4288aaab), SkBits2Float(0x43343246), SkBits2Float(0x428a0889), SkBits2Float(0x43345ade), SkBits2Float(0x428b6666), SkBits2Float(0x43346666));  // 68.3333f, 180.196f, 69.0167f, 180.355f, 69.7f, 180.4f
+path.cubicTo(SkBits2Float(0x428cc444), SkBits2Float(0x433471ef), SkBits2Float(0x428e2222), SkBits2Float(0x433467af), SkBits2Float(0x428f8000), SkBits2Float(0x43346666));  // 70.3833f, 180.445f, 71.0667f, 180.405f, 71.75f, 180.4f
+path.cubicTo(SkBits2Float(0x4290ddde), SkBits2Float(0x4334651e), SkBits2Float(0x42923bbc), SkBits2Float(0x43346442), SkBits2Float(0x4293999a), SkBits2Float(0x43345eb2));  // 72.4333f, 180.395f, 73.1167f, 180.392f, 73.8f, 180.37f
+path.cubicTo(SkBits2Float(0x4294f777), SkBits2Float(0x43345922), SkBits2Float(0x42965555), SkBits2Float(0x43344bb4), SkBits2Float(0x4297b333), SkBits2Float(0x43344506));  // 74.4833f, 180.348f, 75.1667f, 180.296f, 75.85f, 180.27f
+path.cubicTo(SkBits2Float(0x42991111), SkBits2Float(0x43343e58), SkBits2Float(0x429a6eef), SkBits2Float(0x433438a6), SkBits2Float(0x429bcccd), SkBits2Float(0x4334369e));  // 76.5333f, 180.244f, 77.2167f, 180.221f, 77.9f, 180.213f
+path.cubicTo(SkBits2Float(0x429d2aab), SkBits2Float(0x43343496), SkBits2Float(0x429e8889), SkBits2Float(0x43343fb3), SkBits2Float(0x429fe666), SkBits2Float(0x433438d5));  // 78.5833f, 180.205f, 79.2667f, 180.249f, 79.95f, 180.222f
+path.cubicTo(SkBits2Float(0x42a14444), SkBits2Float(0x433431f8), SkBits2Float(0x42a2a222), SkBits2Float(0x433411e5), SkBits2Float(0x42a40000), SkBits2Float(0x43340d6e));  // 80.6333f, 180.195f, 81.3167f, 180.07f, 82, 180.052f
+path.cubicTo(SkBits2Float(0x42a55dde), SkBits2Float(0x433408f8), SkBits2Float(0x42a6bbbc), SkBits2Float(0x43341ae2), SkBits2Float(0x42a8199a), SkBits2Float(0x43341e0e));  // 82.6833f, 180.035f, 83.3667f, 180.105f, 84.05f, 180.117f
+path.cubicTo(SkBits2Float(0x42a97777), SkBits2Float(0x43342139), SkBits2Float(0x42aad555), SkBits2Float(0x433427a8), SkBits2Float(0x42ac3333), SkBits2Float(0x43342073));  // 84.7333f, 180.13f, 85.4167f, 180.155f, 86.1f, 180.127f
+path.cubicTo(SkBits2Float(0x42ad9111), SkBits2Float(0x4334193f), SkBits2Float(0x42aeeeef), SkBits2Float(0x4333fa48), SkBits2Float(0x42b04ccd), SkBits2Float(0x4333f2d5));  // 86.7833f, 180.099f, 87.4667f, 179.978f, 88.15f, 179.949f
+path.cubicTo(SkBits2Float(0x42b1aaab), SkBits2Float(0x4333eb62), SkBits2Float(0x42b30889), SkBits2Float(0x4333f0fd), SkBits2Float(0x42b46666), SkBits2Float(0x4333f3c3));  // 88.8333f, 179.919f, 89.5167f, 179.941f, 90.2f, 179.952f
+path.cubicTo(SkBits2Float(0x42b5c444), SkBits2Float(0x4333f688), SkBits2Float(0x42b72222), SkBits2Float(0x4333f7ca), SkBits2Float(0x42b88000), SkBits2Float(0x43340375));  // 90.8833f, 179.963f, 91.5667f, 179.968f, 92.25f, 180.014f
+path.cubicTo(SkBits2Float(0x42b9ddde), SkBits2Float(0x43340f1f), SkBits2Float(0x42bb3bbc), SkBits2Float(0x43342b53), SkBits2Float(0x42bc999a), SkBits2Float(0x433439c3));  // 92.9333f, 180.059f, 93.6167f, 180.169f, 94.3f, 180.226f
+path.cubicTo(SkBits2Float(0x42bdf777), SkBits2Float(0x43344833), SkBits2Float(0x42bf5555), SkBits2Float(0x43345473), SkBits2Float(0x42c0b333), SkBits2Float(0x43345a15));  // 94.9833f, 180.282f, 95.6667f, 180.33f, 96.35f, 180.352f
+path.cubicTo(SkBits2Float(0x42c21111), SkBits2Float(0x43345fb6), SkBits2Float(0x42c36eef), SkBits2Float(0x433467f5), SkBits2Float(0x42c4cccd), SkBits2Float(0x43345b8d));  // 97.0333f, 180.374f, 97.7167f, 180.406f, 98.4f, 180.358f
+path.cubicTo(SkBits2Float(0x42c62aab), SkBits2Float(0x43344f25), SkBits2Float(0x42c78889), SkBits2Float(0x43342bb0), SkBits2Float(0x42c8e666), SkBits2Float(0x43340fa6));  // 99.0833f, 180.309f, 99.7667f, 180.171f, 100.45f, 180.061f
+path.cubicTo(SkBits2Float(0x42ca4444), SkBits2Float(0x4333f39b), SkBits2Float(0x42cba222), SkBits2Float(0x4333c2e0), SkBits2Float(0x42cd0000), SkBits2Float(0x4333b34d));  // 101.133f, 179.952f, 101.817f, 179.761f, 102.5f, 179.7f
+path.cubicTo(SkBits2Float(0x42ce5dde), SkBits2Float(0x4333a3b9), SkBits2Float(0x42cfbbbc), SkBits2Float(0x4333b115), SkBits2Float(0x42d1199a), SkBits2Float(0x4333b231));  // 103.183f, 179.64f, 103.867f, 179.692f, 104.55f, 179.696f
+path.cubicTo(SkBits2Float(0x42d27777), SkBits2Float(0x4333b34d), SkBits2Float(0x42d3d555), SkBits2Float(0x4333b6a0), SkBits2Float(0x42d53333), SkBits2Float(0x4333b9f3));  // 105.233f, 179.7f, 105.917f, 179.713f, 106.6f, 179.726f
+path.cubicTo(SkBits2Float(0x42d69111), SkBits2Float(0x4333bd46), SkBits2Float(0x42d7eeef), SkBits2Float(0x4333c308), SkBits2Float(0x42d94ccd), SkBits2Float(0x4333c624));  // 107.283f, 179.739f, 107.967f, 179.762f, 108.65f, 179.774f
+path.cubicTo(SkBits2Float(0x42daaaab), SkBits2Float(0x4333c940), SkBits2Float(0x42dc0889), SkBits2Float(0x4333b41c), SkBits2Float(0x42dd6666), SkBits2Float(0x4333cc9c));  // 109.333f, 179.786f, 110.017f, 179.704f, 110.7f, 179.799f
+path.cubicTo(SkBits2Float(0x42dec444), SkBits2Float(0x4333e51d), SkBits2Float(0x42e02222), SkBits2Float(0x43343f85), SkBits2Float(0x42e18000), SkBits2Float(0x43345927));  // 111.383f, 179.895f, 112.067f, 180.248f, 112.75f, 180.348f
+path.cubicTo(SkBits2Float(0x42e2ddde), SkBits2Float(0x433472c9), SkBits2Float(0x42e43bbc), SkBits2Float(0x43346431), SkBits2Float(0x42e5999a), SkBits2Float(0x43346666));  // 113.433f, 180.448f, 114.117f, 180.391f, 114.8f, 180.4f
+path.cubicTo(SkBits2Float(0x42e6f777), SkBits2Float(0x4334689c), SkBits2Float(0x42e85555), SkBits2Float(0x43346666), SkBits2Float(0x42e9b333), SkBits2Float(0x43346666));  // 115.483f, 180.409f, 116.167f, 180.4f, 116.85f, 180.4f
+path.lineTo(SkBits2Float(0x42e9b333), SkBits2Float(0x43346666));  // 116.85f, 180.4f
+path.lineTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666));  // 32.8f, 180.4f
+path.close();
+path.moveTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666));  // 133.25f, 180.4f
+path.lineTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666));  // 133.25f, 180.4f
+path.cubicTo(SkBits2Float(0x4305eeef), SkBits2Float(0x43346666), SkBits2Float(0x43069dde), SkBits2Float(0x43347a6e), SkBits2Float(0x43074ccd), SkBits2Float(0x43346666));  // 133.933f, 180.4f, 134.617f, 180.478f, 135.3f, 180.4f
+path.cubicTo(SkBits2Float(0x4307fbbc), SkBits2Float(0x4334525f), SkBits2Float(0x4308aaab), SkBits2Float(0x43340a40), SkBits2Float(0x4309599a), SkBits2Float(0x4333ee38));  // 135.983f, 180.322f, 136.667f, 180.04f, 137.35f, 179.931f
+path.cubicTo(SkBits2Float(0x430a0889), SkBits2Float(0x4333d230), SkBits2Float(0x430ab777), SkBits2Float(0x4333c68b), SkBits2Float(0x430b6666), SkBits2Float(0x4333be34));  // 138.033f, 179.821f, 138.717f, 179.776f, 139.4f, 179.743f
+path.cubicTo(SkBits2Float(0x430c1555), SkBits2Float(0x4333b5dc), SkBits2Float(0x430cc444), SkBits2Float(0x4333bc82), SkBits2Float(0x430d7333), SkBits2Float(0x4333bc2b));  // 140.083f, 179.71f, 140.767f, 179.736f, 141.45f, 179.735f
+path.cubicTo(SkBits2Float(0x430e2222), SkBits2Float(0x4333bbd4), SkBits2Float(0x430ed111), SkBits2Float(0x4333bd76), SkBits2Float(0x430f8000), SkBits2Float(0x4333bc2b));  // 142.133f, 179.734f, 142.817f, 179.74f, 143.5f, 179.735f
+path.cubicTo(SkBits2Float(0x43102eef), SkBits2Float(0x4333bae0), SkBits2Float(0x4310ddde), SkBits2Float(0x4333b72e), SkBits2Float(0x43118ccd), SkBits2Float(0x4333b469));  // 144.183f, 179.73f, 144.867f, 179.716f, 145.55f, 179.705f
+path.cubicTo(SkBits2Float(0x43123bbc), SkBits2Float(0x4333b1a3), SkBits2Float(0x4312eaab), SkBits2Float(0x4333ad34), SkBits2Float(0x4313999a), SkBits2Float(0x4333ab8b));  // 146.233f, 179.694f, 146.917f, 179.677f, 147.6f, 179.67f
+path.cubicTo(SkBits2Float(0x43144889), SkBits2Float(0x4333a9e1), SkBits2Float(0x4314f777), SkBits2Float(0x4333aa97), SkBits2Float(0x4315a666), SkBits2Float(0x4333aa6f));  // 148.283f, 179.664f, 148.967f, 179.666f, 149.65f, 179.666f
+path.cubicTo(SkBits2Float(0x43165555), SkBits2Float(0x4333aa48), SkBits2Float(0x43170444), SkBits2Float(0x4333aac5), SkBits2Float(0x4317b333), SkBits2Float(0x4333aa9d));  // 150.333f, 179.665f, 151.017f, 179.667f, 151.7f, 179.666f
+path.cubicTo(SkBits2Float(0x43186222), SkBits2Float(0x4333aa76), SkBits2Float(0x43191111), SkBits2Float(0x4333a962), SkBits2Float(0x4319c000), SkBits2Float(0x4333a982));  // 152.383f, 179.666f, 153.067f, 179.662f, 153.75f, 179.662f
+path.cubicTo(SkBits2Float(0x431a6eef), SkBits2Float(0x4333a9a2), SkBits2Float(0x431b1dde), SkBits2Float(0x4333ab0e), SkBits2Float(0x431bcccd), SkBits2Float(0x4333ab5d));  // 154.433f, 179.663f, 155.117f, 179.668f, 155.8f, 179.669f
+path.cubicTo(SkBits2Float(0x431c7bbc), SkBits2Float(0x4333abac), SkBits2Float(0x431d2aab), SkBits2Float(0x4333ab84), SkBits2Float(0x431dd99a), SkBits2Float(0x4333ab5d));  // 156.483f, 179.671f, 157.167f, 179.67f, 157.85f, 179.669f
+path.cubicTo(SkBits2Float(0x431e8889), SkBits2Float(0x4333ab35), SkBits2Float(0x431f3777), SkBits2Float(0x4333aa8f), SkBits2Float(0x431fe666), SkBits2Float(0x4333aa6f));  // 158.533f, 179.669f, 159.217f, 179.666f, 159.9f, 179.666f
+path.cubicTo(SkBits2Float(0x43209555), SkBits2Float(0x4333aa4f), SkBits2Float(0x43214444), SkBits2Float(0x4333a9b1), SkBits2Float(0x4321f333), SkBits2Float(0x4333aa9d));  // 160.583f, 179.665f, 161.267f, 179.663f, 161.95f, 179.666f
+path.cubicTo(SkBits2Float(0x4322a222), SkBits2Float(0x4333ab8a), SkBits2Float(0x43235111), SkBits2Float(0x4333aeb6), SkBits2Float(0x43240000), SkBits2Float(0x4333affa));  // 162.633f, 179.67f, 163.317f, 179.682f, 164, 179.687f
+path.cubicTo(SkBits2Float(0x4324aeef), SkBits2Float(0x4333b13d), SkBits2Float(0x43255dde), SkBits2Float(0x4333b1a3), SkBits2Float(0x43260ccd), SkBits2Float(0x4333b231));  // 164.683f, 179.692f, 165.367f, 179.694f, 166.05f, 179.696f
+path.cubicTo(SkBits2Float(0x4326bbbc), SkBits2Float(0x4333b2bf), SkBits2Float(0x43276aab), SkBits2Float(0x4333b439), SkBits2Float(0x4328199a), SkBits2Float(0x4333b34d));  // 166.733f, 179.698f, 167.417f, 179.704f, 168.1f, 179.7f
+path.cubicTo(SkBits2Float(0x4328c889), SkBits2Float(0x4333b260), SkBits2Float(0x43297777), SkBits2Float(0x4333ae48), SkBits2Float(0x432a2666), SkBits2Float(0x4333aca7));  // 168.783f, 179.697f, 169.467f, 179.681f, 170.15f, 179.674f
+path.cubicTo(SkBits2Float(0x432ad555), SkBits2Float(0x4333ab05), SkBits2Float(0x432b8444), SkBits2Float(0x4333a9d8), SkBits2Float(0x432c3333), SkBits2Float(0x4333a982));  // 170.833f, 179.668f, 171.517f, 179.663f, 172.2f, 179.662f
+path.cubicTo(SkBits2Float(0x432ce222), SkBits2Float(0x4333a92b), SkBits2Float(0x432d9111), SkBits2Float(0x4333a63e), SkBits2Float(0x432e4000), SkBits2Float(0x4333aa9d));  // 172.883f, 179.661f, 173.567f, 179.649f, 174.25f, 179.666f
+path.cubicTo(SkBits2Float(0x432eeeef), SkBits2Float(0x4333aefd), SkBits2Float(0x432f9dde), SkBits2Float(0x4333aacf), SkBits2Float(0x43304ccd), SkBits2Float(0x4333c3bf));  // 174.933f, 179.684f, 175.617f, 179.667f, 176.3f, 179.765f
+path.cubicTo(SkBits2Float(0x4330fbbc), SkBits2Float(0x4333dcae), SkBits2Float(0x4331aaab), SkBits2Float(0x433427ba), SkBits2Float(0x4332599a), SkBits2Float(0x4334403b));  // 176.983f, 179.862f, 177.667f, 180.155f, 178.35f, 180.251f
+path.cubicTo(SkBits2Float(0x43330889), SkBits2Float(0x433458bc), SkBits2Float(0x4333b777), SkBits2Float(0x43345065), SkBits2Float(0x43346666), SkBits2Float(0x433456c2));  // 179.033f, 180.347f, 179.717f, 180.314f, 180.4f, 180.339f
+path.cubicTo(SkBits2Float(0x43351555), SkBits2Float(0x43345d1e), SkBits2Float(0x4335c444), SkBits2Float(0x433463cb), SkBits2Float(0x43367333), SkBits2Float(0x43346666));  // 181.083f, 180.364f, 181.767f, 180.39f, 182.45f, 180.4f
+path.cubicTo(SkBits2Float(0x43372222), SkBits2Float(0x43346902), SkBits2Float(0x4337d111), SkBits2Float(0x43346666), SkBits2Float(0x43388000), SkBits2Float(0x43346666));  // 183.133f, 180.41f, 183.817f, 180.4f, 184.5f, 180.4f
+path.lineTo(SkBits2Float(0x43388000), SkBits2Float(0x43346666));  // 184.5f, 180.4f
+path.lineTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666));  // 133.25f, 180.4f
+path.close();
+path.moveTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666));  // 186.55f, 180.4f
+path.lineTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666));  // 186.55f, 180.4f
+path.cubicTo(SkBits2Float(0x433b3bbc), SkBits2Float(0x433465db), SkBits2Float(0x433beaab), SkBits2Float(0x433463ac), SkBits2Float(0x433c999a), SkBits2Float(0x43346321));  // 187.233f, 180.398f, 187.917f, 180.389f, 188.6f, 180.387f
+path.cubicTo(SkBits2Float(0x433d4889), SkBits2Float(0x43346295), SkBits2Float(0x433df777), SkBits2Float(0x43346295), SkBits2Float(0x433ea666), SkBits2Float(0x43346321));  // 189.283f, 180.385f, 189.967f, 180.385f, 190.65f, 180.387f
+path.cubicTo(SkBits2Float(0x433f5555), SkBits2Float(0x433463ac), SkBits2Float(0x43400444), SkBits2Float(0x433465db), SkBits2Float(0x4340b333), SkBits2Float(0x43346666));  // 191.333f, 180.389f, 192.017f, 180.398f, 192.7f, 180.4f
+path.lineTo(SkBits2Float(0x4340b333), SkBits2Float(0x43346666));  // 192.7f, 180.4f
+path.lineTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666));  // 186.55f, 180.4f
+path.close();
+SkPath pathA = path;
+path.reset();
+path.moveTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666));  // 32.8f, 180.4f
+path.lineTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666));  // 32.8f, 180.4f
+path.cubicTo(SkBits2Float(0x4205eeef), SkBits2Float(0x43346666), SkBits2Float(0x4208aaab), SkBits2Float(0x433454bd), SkBits2Float(0x420b6666), SkBits2Float(0x43346666));  // 33.4833f, 180.4f, 34.1667f, 180.331f, 34.85f, 180.4f
+path.cubicTo(SkBits2Float(0x420e2222), SkBits2Float(0x4334780e), SkBits2Float(0x4210ddde), SkBits2Float(0x4334bf76), SkBits2Float(0x4213999a), SkBits2Float(0x4334d05a));  // 35.5333f, 180.469f, 36.2167f, 180.748f, 36.9f, 180.814f
+path.cubicTo(SkBits2Float(0x42165555), SkBits2Float(0x4334e13e), SkBits2Float(0x42191111), SkBits2Float(0x4334d0d8), SkBits2Float(0x421bcccd), SkBits2Float(0x4334cbbd));  // 37.5833f, 180.88f, 38.2667f, 180.816f, 38.95f, 180.796f
+path.cubicTo(SkBits2Float(0x421e8889), SkBits2Float(0x4334c6a1), SkBits2Float(0x42214444), SkBits2Float(0x4334bab9), SkBits2Float(0x42240000), SkBits2Float(0x4334b1b5));  // 39.6333f, 180.776f, 40.3167f, 180.729f, 41, 180.694f
+path.cubicTo(SkBits2Float(0x4226bbbc), SkBits2Float(0x4334a8b1), SkBits2Float(0x42297777), SkBits2Float(0x4334992e), SkBits2Float(0x422c3333), SkBits2Float(0x433495a4));  // 41.6833f, 180.659f, 42.3667f, 180.598f, 43.05f, 180.585f
+path.cubicTo(SkBits2Float(0x422eeeef), SkBits2Float(0x4334921a), SkBits2Float(0x4231aaab), SkBits2Float(0x4334994d), SkBits2Float(0x42346666), SkBits2Float(0x43349c78));  // 43.7333f, 180.571f, 44.4167f, 180.599f, 45.1f, 180.611f
+path.cubicTo(SkBits2Float(0x42372222), SkBits2Float(0x43349fa4), SkBits2Float(0x4239ddde), SkBits2Float(0x4334aa4b), SkBits2Float(0x423c999a), SkBits2Float(0x4334a8a9));  // 45.7833f, 180.624f, 46.4667f, 180.665f, 47.15f, 180.659f
+path.cubicTo(SkBits2Float(0x423f5555), SkBits2Float(0x4334a707), SkBits2Float(0x42421111), SkBits2Float(0x4334994b), SkBits2Float(0x4244cccd), SkBits2Float(0x433492ad));  // 47.8333f, 180.652f, 48.5167f, 180.599f, 49.2f, 180.573f
+path.cubicTo(SkBits2Float(0x42478889), SkBits2Float(0x43348c0e), SkBits2Float(0x424a4444), SkBits2Float(0x43347f6f), SkBits2Float(0x424d0000), SkBits2Float(0x433480f1));  // 49.8833f, 180.547f, 50.5667f, 180.498f, 51.25f, 180.504f
+path.cubicTo(SkBits2Float(0x424fbbbc), SkBits2Float(0x43348273), SkBits2Float(0x42527777), SkBits2Float(0x4334a025), SkBits2Float(0x42553333), SkBits2Float(0x43349bb9));  // 51.9333f, 180.51f, 52.6167f, 180.626f, 53.3f, 180.608f
+path.cubicTo(SkBits2Float(0x4257eeef), SkBits2Float(0x4334974c), SkBits2Float(0x425aaaab), SkBits2Float(0x43346781), SkBits2Float(0x425d6666), SkBits2Float(0x43346666));  // 53.9833f, 180.591f, 54.6667f, 180.404f, 55.35f, 180.4f
+path.cubicTo(SkBits2Float(0x42602222), SkBits2Float(0x4334654a), SkBits2Float(0x4262ddde), SkBits2Float(0x43348de8), SkBits2Float(0x4265999a), SkBits2Float(0x43349512));  // 56.0333f, 180.396f, 56.7167f, 180.554f, 57.4f, 180.582f
+path.cubicTo(SkBits2Float(0x42685555), SkBits2Float(0x43349c3d), SkBits2Float(0x426b1111), SkBits2Float(0x43348fe9), SkBits2Float(0x426dcccd), SkBits2Float(0x43349163));  // 58.0833f, 180.61f, 58.7667f, 180.562f, 59.45f, 180.568f
+path.cubicTo(SkBits2Float(0x42708889), SkBits2Float(0x433492dd), SkBits2Float(0x42734444), SkBits2Float(0x433494f4), SkBits2Float(0x42760000), SkBits2Float(0x43349df0));  // 60.1333f, 180.574f, 60.8167f, 180.582f, 61.5f, 180.617f
+path.cubicTo(SkBits2Float(0x4278bbbc), SkBits2Float(0x4334a6ed), SkBits2Float(0x427b7777), SkBits2Float(0x4334bf7d), SkBits2Float(0x427e3333), SkBits2Float(0x4334c74e));  // 62.1833f, 180.652f, 62.8667f, 180.748f, 63.55f, 180.779f
+path.cubicTo(SkBits2Float(0x42807777), SkBits2Float(0x4334cf1f), SkBits2Float(0x4281d555), SkBits2Float(0x4334d177), SkBits2Float(0x42833333), SkBits2Float(0x4334ccd9));  // 64.2333f, 180.809f, 64.9167f, 180.818f, 65.6f, 180.8f
+path.cubicTo(SkBits2Float(0x42849111), SkBits2Float(0x4334c83a), SkBits2Float(0x4285eeef), SkBits2Float(0x4334bcac), SkBits2Float(0x42874ccd), SkBits2Float(0x4334ab99));  // 66.2833f, 180.782f, 66.9667f, 180.737f, 67.65f, 180.67f
+path.cubicTo(SkBits2Float(0x4288aaab), SkBits2Float(0x43349a86), SkBits2Float(0x428a0889), SkBits2Float(0x433471ee), SkBits2Float(0x428b6666), SkBits2Float(0x43346666));  // 68.3333f, 180.604f, 69.0167f, 180.445f, 69.7f, 180.4f
+path.cubicTo(SkBits2Float(0x428cc444), SkBits2Float(0x43345add), SkBits2Float(0x428e2222), SkBits2Float(0x4334651d), SkBits2Float(0x428f8000), SkBits2Float(0x43346666));  // 70.3833f, 180.355f, 71.0667f, 180.395f, 71.75f, 180.4f
+path.cubicTo(SkBits2Float(0x4290ddde), SkBits2Float(0x433467ae), SkBits2Float(0x42923bbc), SkBits2Float(0x4334688a), SkBits2Float(0x4293999a), SkBits2Float(0x43346e1a));  // 72.4333f, 180.405f, 73.1167f, 180.408f, 73.8f, 180.43f
+path.cubicTo(SkBits2Float(0x4294f777), SkBits2Float(0x433473aa), SkBits2Float(0x42965555), SkBits2Float(0x43348118), SkBits2Float(0x4297b333), SkBits2Float(0x433487c6));  // 74.4833f, 180.452f, 75.1667f, 180.504f, 75.85f, 180.53f
+path.cubicTo(SkBits2Float(0x42991111), SkBits2Float(0x43348e74), SkBits2Float(0x429a6eef), SkBits2Float(0x43349426), SkBits2Float(0x429bcccd), SkBits2Float(0x4334962e));  // 76.5333f, 180.556f, 77.2167f, 180.579f, 77.9f, 180.587f
+path.cubicTo(SkBits2Float(0x429d2aab), SkBits2Float(0x43349836), SkBits2Float(0x429e8889), SkBits2Float(0x43348d19), SkBits2Float(0x429fe666), SkBits2Float(0x433493f7));  // 78.5833f, 180.595f, 79.2667f, 180.551f, 79.95f, 180.578f
+path.cubicTo(SkBits2Float(0x42a14444), SkBits2Float(0x43349ad4), SkBits2Float(0x42a2a222), SkBits2Float(0x4334bae7), SkBits2Float(0x42a40000), SkBits2Float(0x4334bf5e));  // 80.6333f, 180.605f, 81.3167f, 180.73f, 82, 180.748f
+path.cubicTo(SkBits2Float(0x42a55dde), SkBits2Float(0x4334c3d4), SkBits2Float(0x42a6bbbc), SkBits2Float(0x4334b1ea), SkBits2Float(0x42a8199a), SkBits2Float(0x4334aebe));  // 82.6833f, 180.765f, 83.3667f, 180.695f, 84.05f, 180.683f
+path.cubicTo(SkBits2Float(0x42a97777), SkBits2Float(0x4334ab93), SkBits2Float(0x42aad555), SkBits2Float(0x4334a524), SkBits2Float(0x42ac3333), SkBits2Float(0x4334ac59));  // 84.7333f, 180.67f, 85.4167f, 180.645f, 86.1f, 180.673f
+path.cubicTo(SkBits2Float(0x42ad9111), SkBits2Float(0x4334b38d), SkBits2Float(0x42aeeeef), SkBits2Float(0x4334d284), SkBits2Float(0x42b04ccd), SkBits2Float(0x4334d9f7));  // 86.7833f, 180.701f, 87.4667f, 180.822f, 88.15f, 180.851f
+path.cubicTo(SkBits2Float(0x42b1aaab), SkBits2Float(0x4334e16a), SkBits2Float(0x42b30889), SkBits2Float(0x4334dbcf), SkBits2Float(0x42b46666), SkBits2Float(0x4334d909));  // 88.8333f, 180.881f, 89.5167f, 180.859f, 90.2f, 180.848f
+path.cubicTo(SkBits2Float(0x42b5c444), SkBits2Float(0x4334d644), SkBits2Float(0x42b72222), SkBits2Float(0x4334d502), SkBits2Float(0x42b88000), SkBits2Float(0x4334c957));  // 90.8833f, 180.837f, 91.5667f, 180.832f, 92.25f, 180.786f
+path.cubicTo(SkBits2Float(0x42b9ddde), SkBits2Float(0x4334bdad), SkBits2Float(0x42bb3bbc), SkBits2Float(0x4334a179), SkBits2Float(0x42bc999a), SkBits2Float(0x43349309));  // 92.9333f, 180.741f, 93.6167f, 180.631f, 94.3f, 180.574f
+path.cubicTo(SkBits2Float(0x42bdf777), SkBits2Float(0x43348499), SkBits2Float(0x42bf5555), SkBits2Float(0x43347859), SkBits2Float(0x42c0b333), SkBits2Float(0x433472b7));  // 94.9833f, 180.518f, 95.6667f, 180.47f, 96.35f, 180.448f
+path.cubicTo(SkBits2Float(0x42c21111), SkBits2Float(0x43346d16), SkBits2Float(0x42c36eef), SkBits2Float(0x433464d7), SkBits2Float(0x42c4cccd), SkBits2Float(0x4334713f));  // 97.0333f, 180.426f, 97.7167f, 180.394f, 98.4f, 180.442f
+path.cubicTo(SkBits2Float(0x42c62aab), SkBits2Float(0x43347da7), SkBits2Float(0x42c78889), SkBits2Float(0x4334a11c), SkBits2Float(0x42c8e666), SkBits2Float(0x4334bd26));  // 99.0833f, 180.491f, 99.7667f, 180.629f, 100.45f, 180.739f
+path.cubicTo(SkBits2Float(0x42ca4444), SkBits2Float(0x4334d931), SkBits2Float(0x42cba222), SkBits2Float(0x433509ec), SkBits2Float(0x42cd0000), SkBits2Float(0x4335197f));  // 101.133f, 180.848f, 101.817f, 181.039f, 102.5f, 181.1f
+path.cubicTo(SkBits2Float(0x42ce5dde), SkBits2Float(0x43352913), SkBits2Float(0x42cfbbbc), SkBits2Float(0x43351bb7), SkBits2Float(0x42d1199a), SkBits2Float(0x43351a9b));  // 103.183f, 181.16f, 103.867f, 181.108f, 104.55f, 181.104f
+path.cubicTo(SkBits2Float(0x42d27777), SkBits2Float(0x4335197f), SkBits2Float(0x42d3d555), SkBits2Float(0x4335162c), SkBits2Float(0x42d53333), SkBits2Float(0x433512d9));  // 105.233f, 181.1f, 105.917f, 181.087f, 106.6f, 181.074f
+path.cubicTo(SkBits2Float(0x42d69111), SkBits2Float(0x43350f86), SkBits2Float(0x42d7eeef), SkBits2Float(0x433509c4), SkBits2Float(0x42d94ccd), SkBits2Float(0x433506a8));  // 107.283f, 181.061f, 107.967f, 181.038f, 108.65f, 181.026f
+path.cubicTo(SkBits2Float(0x42daaaab), SkBits2Float(0x4335038c), SkBits2Float(0x42dc0889), SkBits2Float(0x433518b0), SkBits2Float(0x42dd6666), SkBits2Float(0x43350030));  // 109.333f, 181.014f, 110.017f, 181.096f, 110.7f, 181.001f
+path.cubicTo(SkBits2Float(0x42dec444), SkBits2Float(0x4334e7af), SkBits2Float(0x42e02222), SkBits2Float(0x43348d47), SkBits2Float(0x42e18000), SkBits2Float(0x433473a5));  // 111.383f, 180.905f, 112.067f, 180.552f, 112.75f, 180.452f
+path.cubicTo(SkBits2Float(0x42e2ddde), SkBits2Float(0x43345a03), SkBits2Float(0x42e43bbc), SkBits2Float(0x4334689b), SkBits2Float(0x42e5999a), SkBits2Float(0x43346666));  // 113.433f, 180.352f, 114.117f, 180.409f, 114.8f, 180.4f
+path.cubicTo(SkBits2Float(0x42e6f777), SkBits2Float(0x43346430), SkBits2Float(0x42e85555), SkBits2Float(0x43346666), SkBits2Float(0x42e9b333), SkBits2Float(0x43346666));  // 115.483f, 180.391f, 116.167f, 180.4f, 116.85f, 180.4f
+path.lineTo(SkBits2Float(0x42e9b333), SkBits2Float(0x43346666));  // 116.85f, 180.4f
+path.lineTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666));  // 32.8f, 180.4f
+path.close();
+path.moveTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666));  // 133.25f, 180.4f
+path.lineTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666));  // 133.25f, 180.4f
+path.cubicTo(SkBits2Float(0x4305eeef), SkBits2Float(0x43346666), SkBits2Float(0x43069dde), SkBits2Float(0x4334525e), SkBits2Float(0x43074ccd), SkBits2Float(0x43346666));  // 133.933f, 180.4f, 134.617f, 180.322f, 135.3f, 180.4f
+path.cubicTo(SkBits2Float(0x4307fbbc), SkBits2Float(0x43347a6d), SkBits2Float(0x4308aaab), SkBits2Float(0x4334c28c), SkBits2Float(0x4309599a), SkBits2Float(0x4334de94));  // 135.983f, 180.478f, 136.667f, 180.76f, 137.35f, 180.869f
+path.cubicTo(SkBits2Float(0x430a0889), SkBits2Float(0x4334fa9c), SkBits2Float(0x430ab777), SkBits2Float(0x43350641), SkBits2Float(0x430b6666), SkBits2Float(0x43350e98));  // 138.033f, 180.979f, 138.717f, 181.024f, 139.4f, 181.057f
+path.cubicTo(SkBits2Float(0x430c1555), SkBits2Float(0x433516f0), SkBits2Float(0x430cc444), SkBits2Float(0x4335104a), SkBits2Float(0x430d7333), SkBits2Float(0x433510a1));  // 140.083f, 181.09f, 140.767f, 181.064f, 141.45f, 181.065f
+path.cubicTo(SkBits2Float(0x430e2222), SkBits2Float(0x433510f8), SkBits2Float(0x430ed111), SkBits2Float(0x43350f56), SkBits2Float(0x430f8000), SkBits2Float(0x433510a1));  // 142.133f, 181.066f, 142.817f, 181.06f, 143.5f, 181.065f
+path.cubicTo(SkBits2Float(0x43102eef), SkBits2Float(0x433511ec), SkBits2Float(0x4310ddde), SkBits2Float(0x4335159e), SkBits2Float(0x43118ccd), SkBits2Float(0x43351863));  // 144.183f, 181.07f, 144.867f, 181.084f, 145.55f, 181.095f
+path.cubicTo(SkBits2Float(0x43123bbc), SkBits2Float(0x43351b29), SkBits2Float(0x4312eaab), SkBits2Float(0x43351f98), SkBits2Float(0x4313999a), SkBits2Float(0x43352141));  // 146.233f, 181.106f, 146.917f, 181.123f, 147.6f, 181.13f
+path.cubicTo(SkBits2Float(0x43144889), SkBits2Float(0x433522eb), SkBits2Float(0x4314f777), SkBits2Float(0x43352235), SkBits2Float(0x4315a666), SkBits2Float(0x4335225d));  // 148.283f, 181.136f, 148.967f, 181.134f, 149.65f, 181.134f
+path.cubicTo(SkBits2Float(0x43165555), SkBits2Float(0x43352284), SkBits2Float(0x43170444), SkBits2Float(0x43352207), SkBits2Float(0x4317b333), SkBits2Float(0x4335222f));  // 150.333f, 181.135f, 151.017f, 181.133f, 151.7f, 181.134f
+path.cubicTo(SkBits2Float(0x43186222), SkBits2Float(0x43352256), SkBits2Float(0x43191111), SkBits2Float(0x4335236a), SkBits2Float(0x4319c000), SkBits2Float(0x4335234a));  // 152.383f, 181.134f, 153.067f, 181.138f, 153.75f, 181.138f
+path.cubicTo(SkBits2Float(0x431a6eef), SkBits2Float(0x4335232a), SkBits2Float(0x431b1dde), SkBits2Float(0x433521be), SkBits2Float(0x431bcccd), SkBits2Float(0x4335216f));  // 154.433f, 181.137f, 155.117f, 181.132f, 155.8f, 181.131f
+path.cubicTo(SkBits2Float(0x431c7bbc), SkBits2Float(0x43352120), SkBits2Float(0x431d2aab), SkBits2Float(0x43352148), SkBits2Float(0x431dd99a), SkBits2Float(0x4335216f));  // 156.483f, 181.129f, 157.167f, 181.13f, 157.85f, 181.131f
+path.cubicTo(SkBits2Float(0x431e8889), SkBits2Float(0x43352197), SkBits2Float(0x431f3777), SkBits2Float(0x4335223d), SkBits2Float(0x431fe666), SkBits2Float(0x4335225d));  // 158.533f, 181.131f, 159.217f, 181.134f, 159.9f, 181.134f
+path.cubicTo(SkBits2Float(0x43209555), SkBits2Float(0x4335227d), SkBits2Float(0x43214444), SkBits2Float(0x4335231b), SkBits2Float(0x4321f333), SkBits2Float(0x4335222f));  // 160.583f, 181.135f, 161.267f, 181.137f, 161.95f, 181.134f
+path.cubicTo(SkBits2Float(0x4322a222), SkBits2Float(0x43352142), SkBits2Float(0x43235111), SkBits2Float(0x43351e16), SkBits2Float(0x43240000), SkBits2Float(0x43351cd2));  // 162.633f, 181.13f, 163.317f, 181.118f, 164, 181.113f
+path.cubicTo(SkBits2Float(0x4324aeef), SkBits2Float(0x43351b8f), SkBits2Float(0x43255dde), SkBits2Float(0x43351b29), SkBits2Float(0x43260ccd), SkBits2Float(0x43351a9b));  // 164.683f, 181.108f, 165.367f, 181.106f, 166.05f, 181.104f
+path.cubicTo(SkBits2Float(0x4326bbbc), SkBits2Float(0x43351a0d), SkBits2Float(0x43276aab), SkBits2Float(0x43351893), SkBits2Float(0x4328199a), SkBits2Float(0x4335197f));  // 166.733f, 181.102f, 167.417f, 181.096f, 168.1f, 181.1f
+path.cubicTo(SkBits2Float(0x4328c889), SkBits2Float(0x43351a6c), SkBits2Float(0x43297777), SkBits2Float(0x43351e84), SkBits2Float(0x432a2666), SkBits2Float(0x43352025));  // 168.783f, 181.103f, 169.467f, 181.119f, 170.15f, 181.126f
+path.cubicTo(SkBits2Float(0x432ad555), SkBits2Float(0x433521c7), SkBits2Float(0x432b8444), SkBits2Float(0x433522f4), SkBits2Float(0x432c3333), SkBits2Float(0x4335234a));  // 170.833f, 181.132f, 171.517f, 181.137f, 172.2f, 181.138f
+path.cubicTo(SkBits2Float(0x432ce222), SkBits2Float(0x433523a1), SkBits2Float(0x432d9111), SkBits2Float(0x4335268e), SkBits2Float(0x432e4000), SkBits2Float(0x4335222f));  // 172.883f, 181.139f, 173.567f, 181.151f, 174.25f, 181.134f
+path.cubicTo(SkBits2Float(0x432eeeef), SkBits2Float(0x43351dcf), SkBits2Float(0x432f9dde), SkBits2Float(0x433521fd), SkBits2Float(0x43304ccd), SkBits2Float(0x4335090d));  // 174.933f, 181.116f, 175.617f, 181.133f, 176.3f, 181.035f
+path.cubicTo(SkBits2Float(0x4330fbbc), SkBits2Float(0x4334f01e), SkBits2Float(0x4331aaab), SkBits2Float(0x4334a512), SkBits2Float(0x4332599a), SkBits2Float(0x43348c91));  // 176.983f, 180.938f, 177.667f, 180.645f, 178.35f, 180.549f
+path.cubicTo(SkBits2Float(0x43330889), SkBits2Float(0x43347410), SkBits2Float(0x4333b777), SkBits2Float(0x43347c67), SkBits2Float(0x43346666), SkBits2Float(0x4334760a));  // 179.033f, 180.453f, 179.717f, 180.486f, 180.4f, 180.461f
+path.cubicTo(SkBits2Float(0x43351555), SkBits2Float(0x43346fae), SkBits2Float(0x4335c444), SkBits2Float(0x43346901), SkBits2Float(0x43367333), SkBits2Float(0x43346666));  // 181.083f, 180.436f, 181.767f, 180.41f, 182.45f, 180.4f
+path.cubicTo(SkBits2Float(0x43372222), SkBits2Float(0x433463ca), SkBits2Float(0x4337d111), SkBits2Float(0x43346666), SkBits2Float(0x43388000), SkBits2Float(0x43346666));  // 183.133f, 180.39f, 183.817f, 180.4f, 184.5f, 180.4f
+path.lineTo(SkBits2Float(0x43388000), SkBits2Float(0x43346666));  // 184.5f, 180.4f
+path.lineTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666));  // 133.25f, 180.4f
+path.close();
+path.moveTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666));  // 186.55f, 180.4f
+path.lineTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666));  // 186.55f, 180.4f
+path.cubicTo(SkBits2Float(0x433b3bbc), SkBits2Float(0x433466f1), SkBits2Float(0x433beaab), SkBits2Float(0x43346920), SkBits2Float(0x433c999a), SkBits2Float(0x433469ab));  // 187.233f, 180.402f, 187.917f, 180.411f, 188.6f, 180.413f
+path.cubicTo(SkBits2Float(0x433d4889), SkBits2Float(0x43346a37), SkBits2Float(0x433df777), SkBits2Float(0x43346a37), SkBits2Float(0x433ea666), SkBits2Float(0x433469ab));  // 189.283f, 180.415f, 189.967f, 180.415f, 190.65f, 180.413f
+path.cubicTo(SkBits2Float(0x433f5555), SkBits2Float(0x43346920), SkBits2Float(0x43400444), SkBits2Float(0x433466f1), SkBits2Float(0x4340b333), SkBits2Float(0x43346666));  // 191.333f, 180.411f, 192.017f, 180.402f, 192.7f, 180.4f
+path.lineTo(SkBits2Float(0x4340b333), SkBits2Float(0x43346666));  // 192.7f, 180.4f
+path.lineTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666));  // 186.55f, 180.4f
+path.close();
+    testPathOp(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename);
+}
+
+static void issue3651_5(skiatest::Reporter* reporter, const char* filename) {
+SkPath path;
+path.moveTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.cubicTo(SkBits2Float(0x41300000), SkBits2Float(0x4380b333), SkBits2Float(0x4141999a), SkBits2Float(0x4380ba9e), SkBits2Float(0x41533333), SkBits2Float(0x4380b333));  // 11, 257.4f, 12.1f, 257.458f, 13.2f, 257.4f
+path.cubicTo(SkBits2Float(0x4164cccd), SkBits2Float(0x4380abc8), SkBits2Float(0x41766666), SkBits2Float(0x43809a93), SkBits2Float(0x41840000), SkBits2Float(0x438086b0));  // 14.3f, 257.342f, 15.4f, 257.208f, 16.5f, 257.052f
+path.cubicTo(SkBits2Float(0x418ccccd), SkBits2Float(0x438072cc), SkBits2Float(0x4195999a), SkBits2Float(0x4380498b), SkBits2Float(0x419e6666), SkBits2Float(0x43803bdc));  // 17.6f, 256.897f, 18.7f, 256.575f, 19.8f, 256.468f
+path.cubicTo(SkBits2Float(0x41a73333), SkBits2Float(0x43802e2d), SkBits2Float(0x41b00000), SkBits2Float(0x438026f3), SkBits2Float(0x41b8cccd), SkBits2Float(0x43803498));  // 20.9f, 256.361f, 22, 256.304f, 23.1f, 256.411f
+path.cubicTo(SkBits2Float(0x41c1999a), SkBits2Float(0x4380423d), SkBits2Float(0x41ca6666), SkBits2Float(0x4380789f), SkBits2Float(0x41d33333), SkBits2Float(0x43808db9));  // 24.2f, 256.517f, 25.3f, 256.942f, 26.4f, 257.107f
+path.cubicTo(SkBits2Float(0x41dc0000), SkBits2Float(0x4380a2d3), SkBits2Float(0x41e4cccd), SkBits2Float(0x4380b36f), SkBits2Float(0x41ed999a), SkBits2Float(0x4380b333));  // 27.5f, 257.272f, 28.6f, 257.402f, 29.7f, 257.4f
+path.cubicTo(SkBits2Float(0x41f66666), SkBits2Float(0x4380b2f7), SkBits2Float(0x41ff3333), SkBits2Float(0x4380a1a6), SkBits2Float(0x42040000), SkBits2Float(0x43808c51));  // 30.8f, 257.398f, 31.9f, 257.263f, 33, 257.096f
+path.cubicTo(SkBits2Float(0x42086666), SkBits2Float(0x438076fc), SkBits2Float(0x420ccccd), SkBits2Float(0x43803f7a), SkBits2Float(0x42113333), SkBits2Float(0x43803333));  // 34.1f, 256.93f, 35.2f, 256.496f, 36.3f, 256.4f
+path.cubicTo(SkBits2Float(0x4215999a), SkBits2Float(0x438026ed), SkBits2Float(0x421a0000), SkBits2Float(0x43802d56), SkBits2Float(0x421e6666), SkBits2Float(0x438042ab));  // 37.4f, 256.304f, 38.5f, 256.354f, 39.6f, 256.521f
+path.cubicTo(SkBits2Float(0x4222cccd), SkBits2Float(0x43805800), SkBits2Float(0x42273333), SkBits2Float(0x4380a072), SkBits2Float(0x422b999a), SkBits2Float(0x4380b333));  // 40.7f, 256.688f, 41.8f, 257.253f, 42.9f, 257.4f
+path.cubicTo(SkBits2Float(0x42300000), SkBits2Float(0x4380c5f5), SkBits2Float(0x42346666), SkBits2Float(0x4380b333), SkBits2Float(0x4238cccd), SkBits2Float(0x4380b333));  // 44, 257.547f, 45.1f, 257.4f, 46.2f, 257.4f
+path.cubicTo(SkBits2Float(0x423d3333), SkBits2Float(0x4380b333), SkBits2Float(0x4241999a), SkBits2Float(0x4380c1e4), SkBits2Float(0x42460000), SkBits2Float(0x4380b333));  // 47.3f, 257.4f, 48.4f, 257.515f, 49.5f, 257.4f
+path.cubicTo(SkBits2Float(0x424a6666), SkBits2Float(0x4380a482), SkBits2Float(0x424ecccd), SkBits2Float(0x43807063), SkBits2Float(0x42533333), SkBits2Float(0x43805b0e));  // 50.6f, 257.285f, 51.7f, 256.878f, 52.8f, 256.711f
+path.cubicTo(SkBits2Float(0x4257999a), SkBits2Float(0x438045b8), SkBits2Float(0x425c0000), SkBits2Float(0x438039d8), SkBits2Float(0x42606666), SkBits2Float(0x43803333));  // 53.9f, 256.545f, 55, 256.452f, 56.1f, 256.4f
+path.cubicTo(SkBits2Float(0x4264cccd), SkBits2Float(0x43802c8f), SkBits2Float(0x42693333), SkBits2Float(0x43803333), SkBits2Float(0x426d999a), SkBits2Float(0x43803333));  // 57.2f, 256.348f, 58.3f, 256.4f, 59.4f, 256.4f
+path.cubicTo(SkBits2Float(0x42720000), SkBits2Float(0x43803333), SkBits2Float(0x42766666), SkBits2Float(0x43802bdb), SkBits2Float(0x427acccd), SkBits2Float(0x43803333));  // 60.5f, 256.4f, 61.6f, 256.343f, 62.7f, 256.4f
+path.cubicTo(SkBits2Float(0x427f3333), SkBits2Float(0x43803a8c), SkBits2Float(0x4281cccd), SkBits2Float(0x438049f1), SkBits2Float(0x42840000), SkBits2Float(0x43805f47));  // 63.8f, 256.457f, 64.9f, 256.578f, 66, 256.744f
+path.cubicTo(SkBits2Float(0x42863333), SkBits2Float(0x4380749c), SkBits2Float(0x42886666), SkBits2Float(0x4380a536), SkBits2Float(0x428a999a), SkBits2Float(0x4380b333));  // 67.1f, 256.911f, 68.2f, 257.291f, 69.3f, 257.4f
+path.cubicTo(SkBits2Float(0x428ccccd), SkBits2Float(0x4380c130), SkBits2Float(0x428f0000), SkBits2Float(0x4380b333), SkBits2Float(0x42913333), SkBits2Float(0x4380b333));  // 70.4f, 257.509f, 71.5f, 257.4f, 72.6f, 257.4f
+path.cubicTo(SkBits2Float(0x42936666), SkBits2Float(0x4380b333), SkBits2Float(0x4295999a), SkBits2Float(0x4380bb17), SkBits2Float(0x4297cccd), SkBits2Float(0x4380b333));  // 73.7f, 257.4f, 74.8f, 257.462f, 75.9f, 257.4f
+path.cubicTo(SkBits2Float(0x429a0000), SkBits2Float(0x4380ab50), SkBits2Float(0x429c3333), SkBits2Float(0x438083df), SkBits2Float(0x429e6666), SkBits2Float(0x438083df));  // 77, 257.338f, 78.1f, 257.03f, 79.2f, 257.03f
+path.cubicTo(SkBits2Float(0x42a0999a), SkBits2Float(0x438083df), SkBits2Float(0x42a2cccd), SkBits2Float(0x4380ab50), SkBits2Float(0x42a50000), SkBits2Float(0x4380b333));  // 80.3f, 257.03f, 81.4f, 257.338f, 82.5f, 257.4f
+path.cubicTo(SkBits2Float(0x42a73333), SkBits2Float(0x4380bb17), SkBits2Float(0x42a96666), SkBits2Float(0x4380b333), SkBits2Float(0x42ab999a), SkBits2Float(0x4380b333));  // 83.6f, 257.462f, 84.7f, 257.4f, 85.8f, 257.4f
+path.lineTo(SkBits2Float(0x42ab999a), SkBits2Float(0x4380b333));  // 85.8f, 257.4f
+path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.close();
+SkPath pathA = path;
+path.reset();
+path.moveTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.cubicTo(SkBits2Float(0x41300000), SkBits2Float(0x4380b333), SkBits2Float(0x4141999a), SkBits2Float(0x4380abc8), SkBits2Float(0x41533333), SkBits2Float(0x4380b333));  // 11, 257.4f, 12.1f, 257.342f, 13.2f, 257.4f
+path.cubicTo(SkBits2Float(0x4164cccd), SkBits2Float(0x4380ba9e), SkBits2Float(0x41766666), SkBits2Float(0x4380cbd3), SkBits2Float(0x41840000), SkBits2Float(0x4380dfb6));  // 14.3f, 257.458f, 15.4f, 257.592f, 16.5f, 257.748f
+path.cubicTo(SkBits2Float(0x418ccccd), SkBits2Float(0x4380f39a), SkBits2Float(0x4195999a), SkBits2Float(0x43811cdb), SkBits2Float(0x419e6666), SkBits2Float(0x43812a8a));  // 17.6f, 257.903f, 18.7f, 258.225f, 19.8f, 258.332f
+path.cubicTo(SkBits2Float(0x41a73333), SkBits2Float(0x43813839), SkBits2Float(0x41b00000), SkBits2Float(0x43813f73), SkBits2Float(0x41b8cccd), SkBits2Float(0x438131ce));  // 20.9f, 258.439f, 22, 258.496f, 23.1f, 258.389f
+path.cubicTo(SkBits2Float(0x41c1999a), SkBits2Float(0x43812429), SkBits2Float(0x41ca6666), SkBits2Float(0x4380edc7), SkBits2Float(0x41d33333), SkBits2Float(0x4380d8ad));  // 24.2f, 258.283f, 25.3f, 257.858f, 26.4f, 257.693f
+path.cubicTo(SkBits2Float(0x41dc0000), SkBits2Float(0x4380c393), SkBits2Float(0x41e4cccd), SkBits2Float(0x4380b2f7), SkBits2Float(0x41ed999a), SkBits2Float(0x4380b333));  // 27.5f, 257.528f, 28.6f, 257.398f, 29.7f, 257.4f
+path.cubicTo(SkBits2Float(0x41f66666), SkBits2Float(0x4380b36f), SkBits2Float(0x41ff3333), SkBits2Float(0x4380c4c0), SkBits2Float(0x42040000), SkBits2Float(0x4380da15));  // 30.8f, 257.402f, 31.9f, 257.537f, 33, 257.704f
+path.cubicTo(SkBits2Float(0x42086666), SkBits2Float(0x4380ef6a), SkBits2Float(0x420ccccd), SkBits2Float(0x438126ec), SkBits2Float(0x42113333), SkBits2Float(0x43813333));  // 34.1f, 257.87f, 35.2f, 258.304f, 36.3f, 258.4f
+path.cubicTo(SkBits2Float(0x4215999a), SkBits2Float(0x43813f79), SkBits2Float(0x421a0000), SkBits2Float(0x43813910), SkBits2Float(0x421e6666), SkBits2Float(0x438123bb));  // 37.4f, 258.496f, 38.5f, 258.446f, 39.6f, 258.279f
+path.cubicTo(SkBits2Float(0x4222cccd), SkBits2Float(0x43810e66), SkBits2Float(0x42273333), SkBits2Float(0x4380c5f4), SkBits2Float(0x422b999a), SkBits2Float(0x4380b333));  // 40.7f, 258.112f, 41.8f, 257.547f, 42.9f, 257.4f
+path.cubicTo(SkBits2Float(0x42300000), SkBits2Float(0x4380a071), SkBits2Float(0x42346666), SkBits2Float(0x4380b333), SkBits2Float(0x4238cccd), SkBits2Float(0x4380b333));  // 44, 257.253f, 45.1f, 257.4f, 46.2f, 257.4f
+path.cubicTo(SkBits2Float(0x423d3333), SkBits2Float(0x4380b333), SkBits2Float(0x4241999a), SkBits2Float(0x4380a482), SkBits2Float(0x42460000), SkBits2Float(0x4380b333));  // 47.3f, 257.4f, 48.4f, 257.285f, 49.5f, 257.4f
+path.cubicTo(SkBits2Float(0x424a6666), SkBits2Float(0x4380c1e4), SkBits2Float(0x424ecccd), SkBits2Float(0x4380f603), SkBits2Float(0x42533333), SkBits2Float(0x43810b58));  // 50.6f, 257.515f, 51.7f, 257.922f, 52.8f, 258.089f
+path.cubicTo(SkBits2Float(0x4257999a), SkBits2Float(0x438120ae), SkBits2Float(0x425c0000), SkBits2Float(0x43812c8e), SkBits2Float(0x42606666), SkBits2Float(0x43813333));  // 53.9f, 258.255f, 55, 258.348f, 56.1f, 258.4f
+path.cubicTo(SkBits2Float(0x4264cccd), SkBits2Float(0x438139d7), SkBits2Float(0x42693333), SkBits2Float(0x43813333), SkBits2Float(0x426d999a), SkBits2Float(0x43813333));  // 57.2f, 258.452f, 58.3f, 258.4f, 59.4f, 258.4f
+path.cubicTo(SkBits2Float(0x42720000), SkBits2Float(0x43813333), SkBits2Float(0x42766666), SkBits2Float(0x43813a8b), SkBits2Float(0x427acccd), SkBits2Float(0x43813333));  // 60.5f, 258.4f, 61.6f, 258.457f, 62.7f, 258.4f
+path.cubicTo(SkBits2Float(0x427f3333), SkBits2Float(0x43812bda), SkBits2Float(0x4281cccd), SkBits2Float(0x43811c75), SkBits2Float(0x42840000), SkBits2Float(0x4381071f));  // 63.8f, 258.343f, 64.9f, 258.222f, 66, 258.056f
+path.cubicTo(SkBits2Float(0x42863333), SkBits2Float(0x4380f1ca), SkBits2Float(0x42886666), SkBits2Float(0x4380c130), SkBits2Float(0x428a999a), SkBits2Float(0x4380b333));  // 67.1f, 257.889f, 68.2f, 257.509f, 69.3f, 257.4f
+path.cubicTo(SkBits2Float(0x428ccccd), SkBits2Float(0x4380a536), SkBits2Float(0x428f0000), SkBits2Float(0x4380b333), SkBits2Float(0x42913333), SkBits2Float(0x4380b333));  // 70.4f, 257.291f, 71.5f, 257.4f, 72.6f, 257.4f
+path.cubicTo(SkBits2Float(0x42936666), SkBits2Float(0x4380b333), SkBits2Float(0x4295999a), SkBits2Float(0x4380ab4f), SkBits2Float(0x4297cccd), SkBits2Float(0x4380b333));  // 73.7f, 257.4f, 74.8f, 257.338f, 75.9f, 257.4f
+path.cubicTo(SkBits2Float(0x429a0000), SkBits2Float(0x4380bb16), SkBits2Float(0x429c3333), SkBits2Float(0x4380e287), SkBits2Float(0x429e6666), SkBits2Float(0x4380e287));  // 77, 257.462f, 78.1f, 257.77f, 79.2f, 257.77f
+path.cubicTo(SkBits2Float(0x42a0999a), SkBits2Float(0x4380e287), SkBits2Float(0x42a2cccd), SkBits2Float(0x4380bb16), SkBits2Float(0x42a50000), SkBits2Float(0x4380b333));  // 80.3f, 257.77f, 81.4f, 257.462f, 82.5f, 257.4f
+path.cubicTo(SkBits2Float(0x42a73333), SkBits2Float(0x4380ab4f), SkBits2Float(0x42a96666), SkBits2Float(0x4380b333), SkBits2Float(0x42ab999a), SkBits2Float(0x4380b333));  // 83.6f, 257.338f, 84.7f, 257.4f, 85.8f, 257.4f
+path.lineTo(SkBits2Float(0x42ab999a), SkBits2Float(0x4380b333));  // 85.8f, 257.4f
+path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.close();
+    testPathOp(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename);
+}
+
+static void issue3651_6(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.cubicTo(SkBits2Float(0x41c1999a), SkBits2Float(0x4380423d), SkBits2Float(0x41ca6666), SkBits2Float(0x4380789f), SkBits2Float(0x41d33333), SkBits2Float(0x43808db9));  // 24.2f, 256.517f, 25.3f, 256.942f, 26.4f, 257.107f
+path.cubicTo(SkBits2Float(0x41dc0000), SkBits2Float(0x4380a2d3), SkBits2Float(0x41e4cccd), SkBits2Float(0x4380b36f), SkBits2Float(0x41ed999a), SkBits2Float(0x4380b333));  // 27.5f, 257.272f, 28.6f, 257.402f, 29.7f, 257.4f
+path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.close();
+SkPath pathA = path;
+path.reset();
+path.cubicTo(SkBits2Float(0x41c1999a), SkBits2Float(0x43812429), SkBits2Float(0x41ca6666), SkBits2Float(0x4380edc7), SkBits2Float(0x41d33333), SkBits2Float(0x4380d8ad));  // 24.2f, 258.283f, 25.3f, 257.858f, 26.4f, 257.693f
+path.cubicTo(SkBits2Float(0x41dc0000), SkBits2Float(0x4380c393), SkBits2Float(0x41e4cccd), SkBits2Float(0x4380b2f7), SkBits2Float(0x41ed999a), SkBits2Float(0x4380b333));  // 27.5f, 257.528f, 28.6f, 257.398f, 29.7f, 257.4f
+    testPathOp(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename);
+}
+
+static void issue3651_7(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.moveTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a));  // 68.2f, 74.8f
+path.lineTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a));  // 68.2f, 74.8f
+path.cubicTo(SkBits2Float(0x4289ddde), SkBits2Float(0x4295999a), SkBits2Float(0x428b5555), SkBits2Float(0x4295ab91), SkBits2Float(0x428ccccd), SkBits2Float(0x4295999a));  // 68.9333f, 74.8f, 69.6667f, 74.8351f, 70.4f, 74.8f
+path.cubicTo(SkBits2Float(0x428e4444), SkBits2Float(0x429587a2), SkBits2Float(0x428fbbbc), SkBits2Float(0x42954339), SkBits2Float(0x42913333), SkBits2Float(0x42952dcf));  // 71.1333f, 74.7649f, 71.8667f, 74.6313f, 72.6f, 74.5895f
+path.cubicTo(SkBits2Float(0x4292aaab), SkBits2Float(0x42951865), SkBits2Float(0x42942222), SkBits2Float(0x42951dec), SkBits2Float(0x4295999a), SkBits2Float(0x4295191f));  // 73.3333f, 74.5476f, 74.0667f, 74.5584f, 74.8f, 74.5491f
+path.cubicTo(SkBits2Float(0x42971111), SkBits2Float(0x42951452), SkBits2Float(0x42988889), SkBits2Float(0x4294fb9a), SkBits2Float(0x429a0000), SkBits2Float(0x42951101));  // 75.5333f, 74.5397f, 76.2667f, 74.4914f, 77, 74.5332f
+path.cubicTo(SkBits2Float(0x429b7777), SkBits2Float(0x42952667), SkBits2Float(0x429ceeef), SkBits2Float(0x429582c2), SkBits2Float(0x429e6666), SkBits2Float(0x42959986));  // 77.7333f, 74.575f, 78.4667f, 74.7554f, 79.2f, 74.7999f
+path.cubicTo(SkBits2Float(0x429fddde), SkBits2Float(0x4295b04b), SkBits2Float(0x42a15555), SkBits2Float(0x42959996), SkBits2Float(0x42a2cccd), SkBits2Float(0x4295999a));  // 79.9333f, 74.8443f, 80.6667f, 74.8f, 81.4f, 74.8f
+path.cubicTo(SkBits2Float(0x42a44444), SkBits2Float(0x4295999d), SkBits2Float(0x42a5bbbc), SkBits2Float(0x42959e78), SkBits2Float(0x42a73333), SkBits2Float(0x4295999a));  // 82.1333f, 74.8f, 82.8667f, 74.8095f, 83.6f, 74.8f
+path.cubicTo(SkBits2Float(0x42a8aaab), SkBits2Float(0x429594bb), SkBits2Float(0x42aa2222), SkBits2Float(0x42957c62), SkBits2Float(0x42ab999a), SkBits2Float(0x42957c62));  // 84.3333f, 74.7905f, 85.0667f, 74.7429f, 85.8f, 74.7429f
+path.cubicTo(SkBits2Float(0x42ad1111), SkBits2Float(0x42957c62), SkBits2Float(0x42ae8889), SkBits2Float(0x429594bb), SkBits2Float(0x42b00000), SkBits2Float(0x4295999a));  // 86.5333f, 74.7429f, 87.2667f, 74.7905f, 88, 74.8f
+path.cubicTo(SkBits2Float(0x42b17777), SkBits2Float(0x42959e78), SkBits2Float(0x42b2eeef), SkBits2Float(0x4295ac2d), SkBits2Float(0x42b46666), SkBits2Float(0x4295999a));  // 88.7333f, 74.8095f, 89.4667f, 74.8363f, 90.2f, 74.8f
+path.cubicTo(SkBits2Float(0x42b5ddde), SkBits2Float(0x42958706), SkBits2Float(0x42b75555), SkBits2Float(0x42953b44), SkBits2Float(0x42b8cccd), SkBits2Float(0x42952a25));  // 90.9333f, 74.7637f, 91.6667f, 74.6158f, 92.4f, 74.5823f
+path.cubicTo(SkBits2Float(0x42ba4444), SkBits2Float(0x42951906), SkBits2Float(0x42bbbbbc), SkBits2Float(0x42952e4b), SkBits2Float(0x42bd3333), SkBits2Float(0x429532e0));  // 93.1333f, 74.5489f, 93.8667f, 74.5904f, 94.6f, 74.5994f
+path.cubicTo(SkBits2Float(0x42beaaab), SkBits2Float(0x42953775), SkBits2Float(0x42c02222), SkBits2Float(0x42954fda), SkBits2Float(0x42c1999a), SkBits2Float(0x429545a4));  // 95.3333f, 74.6083f, 96.0667f, 74.656f, 96.8f, 74.636f
+path.cubicTo(SkBits2Float(0x42c31111), SkBits2Float(0x42953b6d), SkBits2Float(0x42c48889), SkBits2Float(0x42950c1f), SkBits2Float(0x42c60000), SkBits2Float(0x4294f599));  // 97.5333f, 74.6161f, 98.2667f, 74.5237f, 99, 74.4797f
+path.cubicTo(SkBits2Float(0x42c77777), SkBits2Float(0x4294df14), SkBits2Float(0x42c8eeef), SkBits2Float(0x4294c8f4), SkBits2Float(0x42ca6666), SkBits2Float(0x4294be85));  // 99.7333f, 74.4357f, 100.467f, 74.3925f, 101.2f, 74.3721f
+path.cubicTo(SkBits2Float(0x42cbddde), SkBits2Float(0x4294b417), SkBits2Float(0x42cd5555), SkBits2Float(0x4294ba72), SkBits2Float(0x42cecccd), SkBits2Float(0x4294b703));  // 101.933f, 74.3517f, 102.667f, 74.3642f, 103.4f, 74.3574f
+path.cubicTo(SkBits2Float(0x42d04444), SkBits2Float(0x4294b395), SkBits2Float(0x42d1bbbc), SkBits2Float(0x4294b0ca), SkBits2Float(0x42d33333), SkBits2Float(0x4294a9ec));  // 104.133f, 74.3507f, 104.867f, 74.3453f, 105.6f, 74.3319f
+path.cubicTo(SkBits2Float(0x42d4aaab), SkBits2Float(0x4294a30e), SkBits2Float(0x42d62222), SkBits2Float(0x429493f5), SkBits2Float(0x42d7999a), SkBits2Float(0x42948dd1));  // 106.333f, 74.3185f, 107.067f, 74.289f, 107.8f, 74.277f
+path.cubicTo(SkBits2Float(0x42d91111), SkBits2Float(0x429487ae), SkBits2Float(0x42da8889), SkBits2Float(0x42947f24), SkBits2Float(0x42dc0000), SkBits2Float(0x42948517));  // 108.533f, 74.265f, 109.267f, 74.2483f, 110, 74.2599f
+path.cubicTo(SkBits2Float(0x42dd7777), SkBits2Float(0x42948b0a), SkBits2Float(0x42deeeef), SkBits2Float(0x42949db5), SkBits2Float(0x42e06666), SkBits2Float(0x4294b185));  // 110.733f, 74.2716f, 111.467f, 74.308f, 112.2f, 74.3467f
+path.cubicTo(SkBits2Float(0x42e1ddde), SkBits2Float(0x4294c556), SkBits2Float(0x42e35555), SkBits2Float(0x4294ed7d), SkBits2Float(0x42e4cccd), SkBits2Float(0x4294fbfa));  // 112.933f, 74.3854f, 113.667f, 74.4638f, 114.4f, 74.4921f
+path.cubicTo(SkBits2Float(0x42e64444), SkBits2Float(0x42950a77), SkBits2Float(0x42e7bbbc), SkBits2Float(0x42950d94), SkBits2Float(0x42e93333), SkBits2Float(0x42950875));  // 115.133f, 74.5204f, 115.867f, 74.5265f, 116.6f, 74.5165f
+path.cubicTo(SkBits2Float(0x42eaaaab), SkBits2Float(0x42950356), SkBits2Float(0x42ec2222), SkBits2Float(0x4294e77a), SkBits2Float(0x42ed999a), SkBits2Float(0x4294dd40));  // 117.333f, 74.5065f, 118.067f, 74.4521f, 118.8f, 74.4321f
+path.cubicTo(SkBits2Float(0x42ef1111), SkBits2Float(0x4294d305), SkBits2Float(0x42f08889), SkBits2Float(0x4294c50a), SkBits2Float(0x42f20000), SkBits2Float(0x4294cb18));  // 119.533f, 74.4121f, 120.267f, 74.3848f, 121, 74.3967f
+path.cubicTo(SkBits2Float(0x42f37777), SkBits2Float(0x4294d125), SkBits2Float(0x42f4eeef), SkBits2Float(0x4294ece7), SkBits2Float(0x42f66666), SkBits2Float(0x42950190));  // 121.733f, 74.4085f, 122.467f, 74.4627f, 123.2f, 74.5031f
+path.cubicTo(SkBits2Float(0x42f7ddde), SkBits2Float(0x42951638), SkBits2Float(0x42f95555), SkBits2Float(0x42953e63), SkBits2Float(0x42facccd), SkBits2Float(0x4295470b));  // 123.933f, 74.5434f, 124.667f, 74.6218f, 125.4f, 74.6388f
+path.cubicTo(SkBits2Float(0x42fc4444), SkBits2Float(0x42954fb3), SkBits2Float(0x42fdbbbc), SkBits2Float(0x4295478d), SkBits2Float(0x42ff3333), SkBits2Float(0x4295357f));  // 126.133f, 74.6557f, 126.867f, 74.6397f, 127.6f, 74.6045f
+path.cubicTo(SkBits2Float(0x43005555), SkBits2Float(0x42952371), SkBits2Float(0x43011111), SkBits2Float(0x4294fc35), SkBits2Float(0x4301cccd), SkBits2Float(0x4294dab7));  // 128.333f, 74.5692f, 129.067f, 74.4926f, 129.8f, 74.4272f
+path.cubicTo(SkBits2Float(0x43028889), SkBits2Float(0x4294b93a), SkBits2Float(0x43034444), SkBits2Float(0x429480ff), SkBits2Float(0x43040000), SkBits2Float(0x42946c8f));  // 130.533f, 74.3618f, 131.267f, 74.2519f, 132, 74.212f
+path.cubicTo(SkBits2Float(0x4304bbbc), SkBits2Float(0x4294581e), SkBits2Float(0x43057777), SkBits2Float(0x42945d0d), SkBits2Float(0x43063333), SkBits2Float(0x42946014));  // 132.733f, 74.1721f, 133.467f, 74.1817f, 134.2f, 74.1877f
+path.cubicTo(SkBits2Float(0x4306eeef), SkBits2Float(0x4294631a), SkBits2Float(0x4307aaab), SkBits2Float(0x42947340), SkBits2Float(0x43086666), SkBits2Float(0x42947eb7));  // 134.933f, 74.1936f, 135.667f, 74.2251f, 136.4f, 74.2475f
+path.cubicTo(SkBits2Float(0x43092222), SkBits2Float(0x42948a2d), SkBits2Float(0x4309ddde), SkBits2Float(0x42949abf), SkBits2Float(0x430a999a), SkBits2Float(0x4294a4db));  // 137.133f, 74.2699f, 137.867f, 74.3022f, 138.6f, 74.322f
+path.cubicTo(SkBits2Float(0x430b5555), SkBits2Float(0x4294aef8), SkBits2Float(0x430c1111), SkBits2Float(0x4294bfa8), SkBits2Float(0x430ccccd), SkBits2Float(0x4294bb61));  // 139.333f, 74.3417f, 140.067f, 74.3743f, 140.8f, 74.366f
+path.cubicTo(SkBits2Float(0x430d8889), SkBits2Float(0x4294b71a), SkBits2Float(0x430e4444), SkBits2Float(0x429490b9), SkBits2Float(0x430f0000), SkBits2Float(0x42948b32));  // 141.533f, 74.3576f, 142.267f, 74.2827f, 143, 74.2719f
+path.cubicTo(SkBits2Float(0x430fbbbc), SkBits2Float(0x429485ab), SkBits2Float(0x43107777), SkBits2Float(0x429483e4), SkBits2Float(0x43113333), SkBits2Float(0x42949a35));  // 143.733f, 74.2611f, 144.467f, 74.2576f, 145.2f, 74.3012f
+path.cubicTo(SkBits2Float(0x4311eeef), SkBits2Float(0x4294b086), SkBits2Float(0x4312aaab), SkBits2Float(0x4294f894), SkBits2Float(0x43136666), SkBits2Float(0x42951118));  // 145.933f, 74.3448f, 146.667f, 74.4855f, 147.4f, 74.5334f
+path.cubicTo(SkBits2Float(0x43142222), SkBits2Float(0x4295299b), SkBits2Float(0x4314ddde), SkBits2Float(0x4295342c), SkBits2Float(0x4315999a), SkBits2Float(0x42952d4a));  // 148.133f, 74.5813f, 148.867f, 74.6019f, 149.6f, 74.5885f
+path.cubicTo(SkBits2Float(0x43165555), SkBits2Float(0x42952668), SkBits2Float(0x43171111), SkBits2Float(0x4294f257), SkBits2Float(0x4317cccd), SkBits2Float(0x4294e7cf));  // 150.333f, 74.575f, 151.067f, 74.4733f, 151.8f, 74.4528f
+path.cubicTo(SkBits2Float(0x43188889), SkBits2Float(0x4294dd46), SkBits2Float(0x43194444), SkBits2Float(0x4294e9b3), SkBits2Float(0x431a0000), SkBits2Float(0x4294ee18));  // 152.533f, 74.4322f, 153.267f, 74.4564f, 154, 74.465f
+path.cubicTo(SkBits2Float(0x431abbbc), SkBits2Float(0x4294f27d), SkBits2Float(0x431b7777), SkBits2Float(0x42950422), SkBits2Float(0x431c3333), SkBits2Float(0x4295022c));  // 154.733f, 74.4736f, 155.467f, 74.5081f, 156.2f, 74.5042f
+path.cubicTo(SkBits2Float(0x431ceeef), SkBits2Float(0x42950035), SkBits2Float(0x431daaab), SkBits2Float(0x4294e324), SkBits2Float(0x431e6666), SkBits2Float(0x4294e250));  // 156.933f, 74.5004f, 157.667f, 74.4436f, 158.4f, 74.442f
+path.cubicTo(SkBits2Float(0x431f2222), SkBits2Float(0x4294e17c), SkBits2Float(0x431fddde), SkBits2Float(0x4294e5a1), SkBits2Float(0x4320999a), SkBits2Float(0x4294fd32));  // 159.133f, 74.4404f, 159.867f, 74.4485f, 160.6f, 74.4945f
+path.cubicTo(SkBits2Float(0x43215555), SkBits2Float(0x429514c4), SkBits2Float(0x43221111), SkBits2Float(0x42955c02), SkBits2Float(0x4322cccd), SkBits2Float(0x42956fb8));  // 161.333f, 74.5406f, 162.067f, 74.6797f, 162.8f, 74.7182f
+path.cubicTo(SkBits2Float(0x43238889), SkBits2Float(0x4295836e), SkBits2Float(0x43244444), SkBits2Float(0x429586fb), SkBits2Float(0x43250000), SkBits2Float(0x42957379));  // 163.533f, 74.7567f, 164.267f, 74.7636f, 165, 74.7255f
+path.cubicTo(SkBits2Float(0x4325bbbc), SkBits2Float(0x42955ff7), SkBits2Float(0x43267777), SkBits2Float(0x42950a52), SkBits2Float(0x43273333), SkBits2Float(0x4294faaa));  // 165.733f, 74.6874f, 166.467f, 74.5202f, 167.2f, 74.4896f
+path.cubicTo(SkBits2Float(0x4327eeef), SkBits2Float(0x4294eb03), SkBits2Float(0x4328aaab), SkBits2Float(0x4294fb0f), SkBits2Float(0x43296666), SkBits2Float(0x4295158c));  // 167.933f, 74.459f, 168.667f, 74.4903f, 169.4f, 74.5421f
+path.cubicTo(SkBits2Float(0x432a2222), SkBits2Float(0x4295300a), SkBits2Float(0x432addde), SkBits2Float(0x42958397), SkBits2Float(0x432b999a), SkBits2Float(0x4295999a));  // 170.133f, 74.5938f, 170.867f, 74.757f, 171.6f, 74.8f
+path.cubicTo(SkBits2Float(0x432c5555), SkBits2Float(0x4295af9c), SkBits2Float(0x432d1111), SkBits2Float(0x4295ace7), SkBits2Float(0x432dcccd), SkBits2Float(0x4295999a));  // 172.333f, 74.843f, 173.067f, 74.8377f, 173.8f, 74.8f
+path.cubicTo(SkBits2Float(0x432e8889), SkBits2Float(0x4295864c), SkBits2Float(0x432f4444), SkBits2Float(0x4295555a), SkBits2Float(0x43300000), SkBits2Float(0x429525c8));  // 174.533f, 74.7623f, 175.267f, 74.6667f, 176, 74.5738f
+path.cubicTo(SkBits2Float(0x4330bbbc), SkBits2Float(0x4294f636), SkBits2Float(0x43317777), SkBits2Float(0x42949a53), SkBits2Float(0x43323333), SkBits2Float(0x42947c2e));  // 176.733f, 74.4809f, 177.467f, 74.3014f, 178.2f, 74.2425f
+path.cubicTo(SkBits2Float(0x4332eeef), SkBits2Float(0x42945e0a), SkBits2Float(0x4333aaab), SkBits2Float(0x42946377), SkBits2Float(0x43346666), SkBits2Float(0x429470ec));  // 178.933f, 74.1837f, 179.667f, 74.1943f, 180.4f, 74.2206f
+path.cubicTo(SkBits2Float(0x43352222), SkBits2Float(0x42947e61), SkBits2Float(0x4335ddde), SkBits2Float(0x4294b4b7), SkBits2Float(0x4336999a), SkBits2Float(0x4294ccec));  // 181.133f, 74.2468f, 181.867f, 74.353f, 182.6f, 74.4002f
+path.cubicTo(SkBits2Float(0x43375555), SkBits2Float(0x4294e522), SkBits2Float(0x43381111), SkBits2Float(0x4294f1c6), SkBits2Float(0x4338cccd), SkBits2Float(0x4295022c));  // 183.333f, 74.4475f, 184.067f, 74.4722f, 184.8f, 74.5042f
+path.cubicTo(SkBits2Float(0x43398889), SkBits2Float(0x42951291), SkBits2Float(0x433a4444), SkBits2Float(0x42952241), SkBits2Float(0x433b0000), SkBits2Float(0x42952f4d));  // 185.533f, 74.5363f, 186.267f, 74.5669f, 187, 74.5924f
+path.cubicTo(SkBits2Float(0x433bbbbc), SkBits2Float(0x42953c5a), SkBits2Float(0x433c7777), SkBits2Float(0x42955a4b), SkBits2Float(0x433d3333), SkBits2Float(0x42955079));  // 187.733f, 74.6179f, 188.467f, 74.6764f, 189.2f, 74.6572f
+path.cubicTo(SkBits2Float(0x433deeef), SkBits2Float(0x429546a7), SkBits2Float(0x433eaaab), SkBits2Float(0x4294fc01), SkBits2Float(0x433f6666), SkBits2Float(0x4294f461));  // 189.933f, 74.638f, 190.667f, 74.4922f, 191.4f, 74.4773f
+path.cubicTo(SkBits2Float(0x43402222), SkBits2Float(0x4294ecc1), SkBits2Float(0x4340ddde), SkBits2Float(0x42950f1f), SkBits2Float(0x4341999a), SkBits2Float(0x429522bb));  // 192.133f, 74.4624f, 192.867f, 74.5295f, 193.6f, 74.5678f
+path.cubicTo(SkBits2Float(0x43425555), SkBits2Float(0x42953657), SkBits2Float(0x43431111), SkBits2Float(0x4295616b), SkBits2Float(0x4343cccd), SkBits2Float(0x42956a0b));  // 194.333f, 74.6061f, 195.067f, 74.6903f, 195.8f, 74.7071f
+path.cubicTo(SkBits2Float(0x43448889), SkBits2Float(0x429572ab), SkBits2Float(0x43454444), SkBits2Float(0x4295608a), SkBits2Float(0x43460000), SkBits2Float(0x4295567c));  // 196.533f, 74.724f, 197.267f, 74.6886f, 198, 74.6689f
+path.cubicTo(SkBits2Float(0x4346bbbc), SkBits2Float(0x42954c6e), SkBits2Float(0x43477777), SkBits2Float(0x42953beb), SkBits2Float(0x43483333), SkBits2Float(0x42952db8));  // 198.733f, 74.6493f, 199.467f, 74.617f, 200.2f, 74.5893f
+path.cubicTo(SkBits2Float(0x4348eeef), SkBits2Float(0x42951f85), SkBits2Float(0x4349aaab), SkBits2Float(0x42950a92), SkBits2Float(0x434a6666), SkBits2Float(0x4295014a));  // 200.933f, 74.5616f, 201.667f, 74.5206f, 202.4f, 74.5025f
+path.cubicTo(SkBits2Float(0x434b2222), SkBits2Float(0x4294f802), SkBits2Float(0x434bddde), SkBits2Float(0x4294f9c8), SkBits2Float(0x434c999a), SkBits2Float(0x4294f607));  // 203.133f, 74.4844f, 203.867f, 74.4879f, 204.6f, 74.4805f
+path.cubicTo(SkBits2Float(0x434d5555), SkBits2Float(0x4294f246), SkBits2Float(0x434e1111), SkBits2Float(0x4294f080), SkBits2Float(0x434ecccd), SkBits2Float(0x4294eac5));  // 205.333f, 74.4732f, 206.067f, 74.4697f, 206.8f, 74.4585f
+path.cubicTo(SkBits2Float(0x434f8889), SkBits2Float(0x4294e509), SkBits2Float(0x43504444), SkBits2Float(0x4294dd6d), SkBits2Float(0x43510000), SkBits2Float(0x4294d3a3));  // 207.533f, 74.4473f, 208.267f, 74.4325f, 209, 74.4134f
+path.cubicTo(SkBits2Float(0x4351bbbc), SkBits2Float(0x4294c9d9), SkBits2Float(0x43527777), SkBits2Float(0x4294b931), SkBits2Float(0x43533333), SkBits2Float(0x4294b007));  // 209.733f, 74.3942f, 210.467f, 74.3617f, 211.2f, 74.3438f
+path.cubicTo(SkBits2Float(0x4353eeef), SkBits2Float(0x4294a6dd), SkBits2Float(0x4354aaab), SkBits2Float(0x4294a02f), SkBits2Float(0x43556666), SkBits2Float(0x42949ca6));  // 211.933f, 74.3259f, 212.667f, 74.3129f, 213.4f, 74.306f
+path.cubicTo(SkBits2Float(0x43562222), SkBits2Float(0x4294991d), SkBits2Float(0x4356ddde), SkBits2Float(0x42949775), SkBits2Float(0x4357999a), SkBits2Float(0x42949ad1));  // 214.133f, 74.299f, 214.867f, 74.2958f, 215.6f, 74.3024f
+path.cubicTo(SkBits2Float(0x43585555), SkBits2Float(0x42949e2e), SkBits2Float(0x43591111), SkBits2Float(0x4294a9b4), SkBits2Float(0x4359cccd), SkBits2Float(0x4294b0d2));  // 216.333f, 74.3089f, 217.067f, 74.3315f, 217.8f, 74.3454f
+path.cubicTo(SkBits2Float(0x435a8889), SkBits2Float(0x4294b7ef), SkBits2Float(0x435b4444), SkBits2Float(0x4294d2c3), SkBits2Float(0x435c0000), SkBits2Float(0x4294c582));  // 218.533f, 74.3592f, 219.267f, 74.4116f, 220, 74.3858f
+path.cubicTo(SkBits2Float(0x435cbbbc), SkBits2Float(0x4294b841), SkBits2Float(0x435d7777), SkBits2Float(0x42947919), SkBits2Float(0x435e3333), SkBits2Float(0x4294614c));  // 220.733f, 74.3599f, 221.467f, 74.2365f, 222.2f, 74.19f
+path.cubicTo(SkBits2Float(0x435eeeef), SkBits2Float(0x4294497f), SkBits2Float(0x435faaab), SkBits2Float(0x42943521), SkBits2Float(0x43606666), SkBits2Float(0x429436b3));  // 222.933f, 74.1435f, 223.667f, 74.1038f, 224.4f, 74.1068f
+path.cubicTo(SkBits2Float(0x43612222), SkBits2Float(0x42943845), SkBits2Float(0x4361ddde), SkBits2Float(0x42945996), SkBits2Float(0x4362999a), SkBits2Float(0x42946aba));  // 225.133f, 74.1099f, 225.867f, 74.175f, 226.6f, 74.2085f
+path.cubicTo(SkBits2Float(0x43635555), SkBits2Float(0x42947bdd), SkBits2Float(0x43641111), SkBits2Float(0x4294973f), SkBits2Float(0x4364cccd), SkBits2Float(0x42949d88));  // 227.333f, 74.2419f, 228.067f, 74.2954f, 228.8f, 74.3077f
+path.cubicTo(SkBits2Float(0x43658889), SkBits2Float(0x4294a3d2), SkBits2Float(0x43664444), SkBits2Float(0x429495a6), SkBits2Float(0x43670000), SkBits2Float(0x42949071));  // 229.533f, 74.32f, 230.267f, 74.2923f, 231, 74.2821f
+path.cubicTo(SkBits2Float(0x4367bbbc), SkBits2Float(0x42948b3c), SkBits2Float(0x43687777), SkBits2Float(0x429480b0), SkBits2Float(0x43693333), SkBits2Float(0x42947e49));  // 231.733f, 74.2719f, 232.467f, 74.2513f, 233.2f, 74.2467f
+path.cubicTo(SkBits2Float(0x4369eeef), SkBits2Float(0x42947be3), SkBits2Float(0x436aaaab), SkBits2Float(0x42947a88), SkBits2Float(0x436b6666), SkBits2Float(0x4294820a));  // 233.933f, 74.242f, 234.667f, 74.2393f, 235.4f, 74.254f
+path.cubicTo(SkBits2Float(0x436c2222), SkBits2Float(0x4294898c), SkBits2Float(0x436cddde), SkBits2Float(0x42949eee), SkBits2Float(0x436d999a), SkBits2Float(0x4294ab53));  // 236.133f, 74.2686f, 236.867f, 74.3104f, 237.6f, 74.3346f
+path.cubicTo(SkBits2Float(0x436e5555), SkBits2Float(0x4294b7b8), SkBits2Float(0x436f1111), SkBits2Float(0x4294c2bb), SkBits2Float(0x436fcccd), SkBits2Float(0x4294cc67));  // 238.333f, 74.3588f, 239.067f, 74.3803f, 239.8f, 74.3992f
+path.cubicTo(SkBits2Float(0x43708889), SkBits2Float(0x4294d614), SkBits2Float(0x43714444), SkBits2Float(0x4294d5a0), SkBits2Float(0x43720000), SkBits2Float(0x4294e55e));  // 240.533f, 74.4181f, 241.267f, 74.4172f, 242, 74.448f
+path.cubicTo(SkBits2Float(0x4372bbbc), SkBits2Float(0x4294f51b), SkBits2Float(0x43737777), SkBits2Float(0x429516d0), SkBits2Float(0x43743333), SkBits2Float(0x42952ad9));  // 242.733f, 74.4787f, 243.467f, 74.5446f, 244.2f, 74.5837f
+path.cubicTo(SkBits2Float(0x4374eeef), SkBits2Float(0x42953ee1), SkBits2Float(0x4375aaab), SkBits2Float(0x42954f2d), SkBits2Float(0x43766666), SkBits2Float(0x42955d90));  // 244.933f, 74.6228f, 245.667f, 74.6546f, 246.4f, 74.6827f
+path.cubicTo(SkBits2Float(0x43772222), SkBits2Float(0x42956bf3), SkBits2Float(0x4377ddde), SkBits2Float(0x4295772b), SkBits2Float(0x4378999a), SkBits2Float(0x4295812d));  // 247.133f, 74.7108f, 247.867f, 74.7327f, 248.6f, 74.7523f
+path.cubicTo(SkBits2Float(0x43795555), SkBits2Float(0x42958b2e), SkBits2Float(0x437a1111), SkBits2Float(0x42959587), SkBits2Float(0x437acccd), SkBits2Float(0x4295999a));  // 249.333f, 74.7718f, 250.067f, 74.792f, 250.8f, 74.8f
+path.cubicTo(SkBits2Float(0x437b8889), SkBits2Float(0x42959dac), SkBits2Float(0x437c4444), SkBits2Float(0x4295999a), SkBits2Float(0x437d0000), SkBits2Float(0x4295999a));  // 251.533f, 74.808f, 252.267f, 74.8f, 253, 74.8f
+path.lineTo(SkBits2Float(0x437d0000), SkBits2Float(0x4295999a));  // 253, 74.8f
+path.lineTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a));  // 68.2f, 74.8f
+path.close();
+SkPath pathA = path;
+path.reset();
+
+path.moveTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a));  // 68.2f, 74.8f
+path.lineTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a));  // 68.2f, 74.8f
+path.cubicTo(SkBits2Float(0x4289ddde), SkBits2Float(0x4295999a), SkBits2Float(0x428b5555), SkBits2Float(0x429587a3), SkBits2Float(0x428ccccd), SkBits2Float(0x4295999a));  // 68.9333f, 74.8f, 69.6667f, 74.7649f, 70.4f, 74.8f
+path.cubicTo(SkBits2Float(0x428e4444), SkBits2Float(0x4295ab92), SkBits2Float(0x428fbbbc), SkBits2Float(0x4295effb), SkBits2Float(0x42913333), SkBits2Float(0x42960565));  // 71.1333f, 74.8351f, 71.8667f, 74.9687f, 72.6f, 75.0105f
+path.cubicTo(SkBits2Float(0x4292aaab), SkBits2Float(0x42961acf), SkBits2Float(0x42942222), SkBits2Float(0x42961548), SkBits2Float(0x4295999a), SkBits2Float(0x42961a15));  // 73.3333f, 75.0524f, 74.0667f, 75.0416f, 74.8f, 75.0509f
+path.cubicTo(SkBits2Float(0x42971111), SkBits2Float(0x42961ee2), SkBits2Float(0x42988889), SkBits2Float(0x4296379a), SkBits2Float(0x429a0000), SkBits2Float(0x42962233));  // 75.5333f, 75.0603f, 76.2667f, 75.1086f, 77, 75.0668f
+path.cubicTo(SkBits2Float(0x429b7777), SkBits2Float(0x42960ccd), SkBits2Float(0x429ceeef), SkBits2Float(0x4295b072), SkBits2Float(0x429e6666), SkBits2Float(0x429599ae));  // 77.7333f, 75.025f, 78.4667f, 74.8446f, 79.2f, 74.8002f
+path.cubicTo(SkBits2Float(0x429fddde), SkBits2Float(0x429582e9), SkBits2Float(0x42a15555), SkBits2Float(0x4295999e), SkBits2Float(0x42a2cccd), SkBits2Float(0x4295999a));  // 79.9333f, 74.7557f, 80.6667f, 74.8f, 81.4f, 74.8f
+path.cubicTo(SkBits2Float(0x42a44444), SkBits2Float(0x42959997), SkBits2Float(0x42a5bbbc), SkBits2Float(0x429594bc), SkBits2Float(0x42a73333), SkBits2Float(0x4295999a));  // 82.1333f, 74.8f, 82.8667f, 74.7905f, 83.6f, 74.8f
+path.cubicTo(SkBits2Float(0x42a8aaab), SkBits2Float(0x42959e79), SkBits2Float(0x42aa2222), SkBits2Float(0x4295b6d2), SkBits2Float(0x42ab999a), SkBits2Float(0x4295b6d2));  // 84.3333f, 74.8095f, 85.0667f, 74.8571f, 85.8f, 74.8571f
+path.cubicTo(SkBits2Float(0x42ad1111), SkBits2Float(0x4295b6d2), SkBits2Float(0x42ae8889), SkBits2Float(0x42959e79), SkBits2Float(0x42b00000), SkBits2Float(0x4295999a));  // 86.5333f, 74.8571f, 87.2667f, 74.8095f, 88, 74.8f
+path.cubicTo(SkBits2Float(0x42b17777), SkBits2Float(0x429594bc), SkBits2Float(0x42b2eeef), SkBits2Float(0x42958707), SkBits2Float(0x42b46666), SkBits2Float(0x4295999a));  // 88.7333f, 74.7905f, 89.4667f, 74.7637f, 90.2f, 74.8f
+path.cubicTo(SkBits2Float(0x42b5ddde), SkBits2Float(0x4295ac2e), SkBits2Float(0x42b75555), SkBits2Float(0x4295f7f0), SkBits2Float(0x42b8cccd), SkBits2Float(0x4296090f));  // 90.9333f, 74.8363f, 91.6667f, 74.9843f, 92.4f, 75.0177f
+path.cubicTo(SkBits2Float(0x42ba4444), SkBits2Float(0x42961a2e), SkBits2Float(0x42bbbbbc), SkBits2Float(0x429604e9), SkBits2Float(0x42bd3333), SkBits2Float(0x42960054));  // 93.1333f, 75.0511f, 93.8667f, 75.0096f, 94.6f, 75.0006f
+path.cubicTo(SkBits2Float(0x42beaaab), SkBits2Float(0x4295fbbf), SkBits2Float(0x42c02222), SkBits2Float(0x4295e35a), SkBits2Float(0x42c1999a), SkBits2Float(0x4295ed90));  // 95.3333f, 74.9917f, 96.0667f, 74.944f, 96.8f, 74.964f
+path.cubicTo(SkBits2Float(0x42c31111), SkBits2Float(0x4295f7c7), SkBits2Float(0x42c48889), SkBits2Float(0x42962715), SkBits2Float(0x42c60000), SkBits2Float(0x42963d9b));  // 97.5333f, 74.9839f, 98.2667f, 75.0763f, 99, 75.1203f
+path.cubicTo(SkBits2Float(0x42c77777), SkBits2Float(0x42965420), SkBits2Float(0x42c8eeef), SkBits2Float(0x42966a40), SkBits2Float(0x42ca6666), SkBits2Float(0x429674af));  // 99.7333f, 75.1643f, 100.467f, 75.2075f, 101.2f, 75.2279f
+path.cubicTo(SkBits2Float(0x42cbddde), SkBits2Float(0x42967f1d), SkBits2Float(0x42cd5555), SkBits2Float(0x429678c2), SkBits2Float(0x42cecccd), SkBits2Float(0x42967c31));  // 101.933f, 75.2483f, 102.667f, 75.2359f, 103.4f, 75.2426f
+path.cubicTo(SkBits2Float(0x42d04444), SkBits2Float(0x42967f9f), SkBits2Float(0x42d1bbbc), SkBits2Float(0x4296826a), SkBits2Float(0x42d33333), SkBits2Float(0x42968948));  // 104.133f, 75.2493f, 104.867f, 75.2547f, 105.6f, 75.2681f
+path.cubicTo(SkBits2Float(0x42d4aaab), SkBits2Float(0x42969026), SkBits2Float(0x42d62222), SkBits2Float(0x42969f3f), SkBits2Float(0x42d7999a), SkBits2Float(0x4296a563));  // 106.333f, 75.2815f, 107.067f, 75.311f, 107.8f, 75.323f
+path.cubicTo(SkBits2Float(0x42d91111), SkBits2Float(0x4296ab86), SkBits2Float(0x42da8889), SkBits2Float(0x4296b410), SkBits2Float(0x42dc0000), SkBits2Float(0x4296ae1d));  // 108.533f, 75.335f, 109.267f, 75.3517f, 110, 75.3401f
+path.cubicTo(SkBits2Float(0x42dd7777), SkBits2Float(0x4296a82a), SkBits2Float(0x42deeeef), SkBits2Float(0x4296957f), SkBits2Float(0x42e06666), SkBits2Float(0x429681af));  // 110.733f, 75.3284f, 111.467f, 75.292f, 112.2f, 75.2533f
+path.cubicTo(SkBits2Float(0x42e1ddde), SkBits2Float(0x42966dde), SkBits2Float(0x42e35555), SkBits2Float(0x429645b7), SkBits2Float(0x42e4cccd), SkBits2Float(0x4296373a));  // 112.933f, 75.2146f, 113.667f, 75.1362f, 114.4f, 75.1079f
+path.cubicTo(SkBits2Float(0x42e64444), SkBits2Float(0x429628bd), SkBits2Float(0x42e7bbbc), SkBits2Float(0x429625a0), SkBits2Float(0x42e93333), SkBits2Float(0x42962abf));  // 115.133f, 75.0796f, 115.867f, 75.0735f, 116.6f, 75.0835f
+path.cubicTo(SkBits2Float(0x42eaaaab), SkBits2Float(0x42962fde), SkBits2Float(0x42ec2222), SkBits2Float(0x42964bba), SkBits2Float(0x42ed999a), SkBits2Float(0x429655f4));  // 117.333f, 75.0935f, 118.067f, 75.1479f, 118.8f, 75.1679f
+path.cubicTo(SkBits2Float(0x42ef1111), SkBits2Float(0x4296602f), SkBits2Float(0x42f08889), SkBits2Float(0x42966e2a), SkBits2Float(0x42f20000), SkBits2Float(0x4296681c));  // 119.533f, 75.1879f, 120.267f, 75.2152f, 121, 75.2033f
+path.cubicTo(SkBits2Float(0x42f37777), SkBits2Float(0x4296620f), SkBits2Float(0x42f4eeef), SkBits2Float(0x4296464d), SkBits2Float(0x42f66666), SkBits2Float(0x429631a4));  // 121.733f, 75.1915f, 122.467f, 75.1373f, 123.2f, 75.097f
+path.cubicTo(SkBits2Float(0x42f7ddde), SkBits2Float(0x42961cfc), SkBits2Float(0x42f95555), SkBits2Float(0x4295f4d1), SkBits2Float(0x42facccd), SkBits2Float(0x4295ec29));  // 123.933f, 75.0566f, 124.667f, 74.9782f, 125.4f, 74.9613f
+path.cubicTo(SkBits2Float(0x42fc4444), SkBits2Float(0x4295e381), SkBits2Float(0x42fdbbbc), SkBits2Float(0x4295eba7), SkBits2Float(0x42ff3333), SkBits2Float(0x4295fdb5));  // 126.133f, 74.9443f, 126.867f, 74.9603f, 127.6f, 74.9955f
+path.cubicTo(SkBits2Float(0x43005555), SkBits2Float(0x42960fc3), SkBits2Float(0x43011111), SkBits2Float(0x429636ff), SkBits2Float(0x4301cccd), SkBits2Float(0x4296587d));  // 128.333f, 75.0308f, 129.067f, 75.1074f, 129.8f, 75.1728f
+path.cubicTo(SkBits2Float(0x43028889), SkBits2Float(0x429679fa), SkBits2Float(0x43034444), SkBits2Float(0x4296b235), SkBits2Float(0x43040000), SkBits2Float(0x4296c6a5));  // 130.533f, 75.2382f, 131.267f, 75.3481f, 132, 75.388f
+path.cubicTo(SkBits2Float(0x4304bbbc), SkBits2Float(0x4296db16), SkBits2Float(0x43057777), SkBits2Float(0x4296d627), SkBits2Float(0x43063333), SkBits2Float(0x4296d320));  // 132.733f, 75.4279f, 133.467f, 75.4183f, 134.2f, 75.4124f
+path.cubicTo(SkBits2Float(0x4306eeef), SkBits2Float(0x4296d01a), SkBits2Float(0x4307aaab), SkBits2Float(0x4296bff4), SkBits2Float(0x43086666), SkBits2Float(0x4296b47d));  // 134.933f, 75.4064f, 135.667f, 75.3749f, 136.4f, 75.3525f
+path.cubicTo(SkBits2Float(0x43092222), SkBits2Float(0x4296a907), SkBits2Float(0x4309ddde), SkBits2Float(0x42969875), SkBits2Float(0x430a999a), SkBits2Float(0x42968e59));  // 137.133f, 75.3301f, 137.867f, 75.2978f, 138.6f, 75.278f
+path.cubicTo(SkBits2Float(0x430b5555), SkBits2Float(0x4296843c), SkBits2Float(0x430c1111), SkBits2Float(0x4296738c), SkBits2Float(0x430ccccd), SkBits2Float(0x429677d3));  // 139.333f, 75.2583f, 140.067f, 75.2257f, 140.8f, 75.234f
+path.cubicTo(SkBits2Float(0x430d8889), SkBits2Float(0x42967c1a), SkBits2Float(0x430e4444), SkBits2Float(0x4296a27b), SkBits2Float(0x430f0000), SkBits2Float(0x4296a802));  // 141.533f, 75.2424f, 142.267f, 75.3173f, 143, 75.3281f
+path.cubicTo(SkBits2Float(0x430fbbbc), SkBits2Float(0x4296ad89), SkBits2Float(0x43107777), SkBits2Float(0x4296af50), SkBits2Float(0x43113333), SkBits2Float(0x429698ff));  // 143.733f, 75.3389f, 144.467f, 75.3424f, 145.2f, 75.2988f
+path.cubicTo(SkBits2Float(0x4311eeef), SkBits2Float(0x429682ae), SkBits2Float(0x4312aaab), SkBits2Float(0x42963aa0), SkBits2Float(0x43136666), SkBits2Float(0x4296221c));  // 145.933f, 75.2552f, 146.667f, 75.1145f, 147.4f, 75.0666f
+path.cubicTo(SkBits2Float(0x43142222), SkBits2Float(0x42960999), SkBits2Float(0x4314ddde), SkBits2Float(0x4295ff08), SkBits2Float(0x4315999a), SkBits2Float(0x429605ea));  // 148.133f, 75.0187f, 148.867f, 74.9981f, 149.6f, 75.0116f
+path.cubicTo(SkBits2Float(0x43165555), SkBits2Float(0x42960ccc), SkBits2Float(0x43171111), SkBits2Float(0x429640dd), SkBits2Float(0x4317cccd), SkBits2Float(0x42964b65));  // 150.333f, 75.025f, 151.067f, 75.1267f, 151.8f, 75.1473f
+path.cubicTo(SkBits2Float(0x43188889), SkBits2Float(0x429655ee), SkBits2Float(0x43194444), SkBits2Float(0x42964981), SkBits2Float(0x431a0000), SkBits2Float(0x4296451c));  // 152.533f, 75.1678f, 153.267f, 75.1436f, 154, 75.135f
+path.cubicTo(SkBits2Float(0x431abbbc), SkBits2Float(0x429640b7), SkBits2Float(0x431b7777), SkBits2Float(0x42962f12), SkBits2Float(0x431c3333), SkBits2Float(0x42963108));  // 154.733f, 75.1264f, 155.467f, 75.0919f, 156.2f, 75.0958f
+path.cubicTo(SkBits2Float(0x431ceeef), SkBits2Float(0x429632ff), SkBits2Float(0x431daaab), SkBits2Float(0x42965010), SkBits2Float(0x431e6666), SkBits2Float(0x429650e4));  // 156.933f, 75.0996f, 157.667f, 75.1564f, 158.4f, 75.158f
+path.cubicTo(SkBits2Float(0x431f2222), SkBits2Float(0x429651b8), SkBits2Float(0x431fddde), SkBits2Float(0x42964d93), SkBits2Float(0x4320999a), SkBits2Float(0x42963602));  // 159.133f, 75.1596f, 159.867f, 75.1515f, 160.6f, 75.1055f
+path.cubicTo(SkBits2Float(0x43215555), SkBits2Float(0x42961e70), SkBits2Float(0x43221111), SkBits2Float(0x4295d732), SkBits2Float(0x4322cccd), SkBits2Float(0x4295c37c));  // 161.333f, 75.0594f, 162.067f, 74.9203f, 162.8f, 74.8818f
+path.cubicTo(SkBits2Float(0x43238889), SkBits2Float(0x4295afc6), SkBits2Float(0x43244444), SkBits2Float(0x4295ac39), SkBits2Float(0x43250000), SkBits2Float(0x4295bfbb));  // 163.533f, 74.8433f, 164.267f, 74.8364f, 165, 74.8745f
+path.cubicTo(SkBits2Float(0x4325bbbc), SkBits2Float(0x4295d33d), SkBits2Float(0x43267777), SkBits2Float(0x429628e2), SkBits2Float(0x43273333), SkBits2Float(0x4296388a));  // 165.733f, 74.9126f, 166.467f, 75.0798f, 167.2f, 75.1104f
+path.cubicTo(SkBits2Float(0x4327eeef), SkBits2Float(0x42964831), SkBits2Float(0x4328aaab), SkBits2Float(0x42963825), SkBits2Float(0x43296666), SkBits2Float(0x42961da8));  // 167.933f, 75.141f, 168.667f, 75.1097f, 169.4f, 75.0579f
+path.cubicTo(SkBits2Float(0x432a2222), SkBits2Float(0x4296032a), SkBits2Float(0x432addde), SkBits2Float(0x4295af9d), SkBits2Float(0x432b999a), SkBits2Float(0x4295999a));  // 170.133f, 75.0062f, 170.867f, 74.843f, 171.6f, 74.8f
+path.cubicTo(SkBits2Float(0x432c5555), SkBits2Float(0x42958398), SkBits2Float(0x432d1111), SkBits2Float(0x4295864d), SkBits2Float(0x432dcccd), SkBits2Float(0x4295999a));  // 172.333f, 74.757f, 173.067f, 74.7623f, 173.8f, 74.8f
+path.cubicTo(SkBits2Float(0x432e8889), SkBits2Float(0x4295ace8), SkBits2Float(0x432f4444), SkBits2Float(0x4295ddda), SkBits2Float(0x43300000), SkBits2Float(0x42960d6c));  // 174.533f, 74.8377f, 175.267f, 74.9333f, 176, 75.0262f
+path.cubicTo(SkBits2Float(0x4330bbbc), SkBits2Float(0x42963cfe), SkBits2Float(0x43317777), SkBits2Float(0x429698e1), SkBits2Float(0x43323333), SkBits2Float(0x4296b706));  // 176.733f, 75.1191f, 177.467f, 75.2986f, 178.2f, 75.3575f
+path.cubicTo(SkBits2Float(0x4332eeef), SkBits2Float(0x4296d52a), SkBits2Float(0x4333aaab), SkBits2Float(0x4296cfbd), SkBits2Float(0x43346666), SkBits2Float(0x4296c248));  // 178.933f, 75.4163f, 179.667f, 75.4057f, 180.4f, 75.3795f
+path.cubicTo(SkBits2Float(0x43352222), SkBits2Float(0x4296b4d3), SkBits2Float(0x4335ddde), SkBits2Float(0x42967e7d), SkBits2Float(0x4336999a), SkBits2Float(0x42966648));  // 181.133f, 75.3532f, 181.867f, 75.247f, 182.6f, 75.1998f
+path.cubicTo(SkBits2Float(0x43375555), SkBits2Float(0x42964e12), SkBits2Float(0x43381111), SkBits2Float(0x4296416e), SkBits2Float(0x4338cccd), SkBits2Float(0x42963108));  // 183.333f, 75.1525f, 184.067f, 75.1278f, 184.8f, 75.0958f
+path.cubicTo(SkBits2Float(0x43398889), SkBits2Float(0x429620a3), SkBits2Float(0x433a4444), SkBits2Float(0x429610f3), SkBits2Float(0x433b0000), SkBits2Float(0x429603e7));  // 185.533f, 75.0637f, 186.267f, 75.0331f, 187, 75.0076f
+path.cubicTo(SkBits2Float(0x433bbbbc), SkBits2Float(0x4295f6da), SkBits2Float(0x433c7777), SkBits2Float(0x4295d8e9), SkBits2Float(0x433d3333), SkBits2Float(0x4295e2bb));  // 187.733f, 74.9821f, 188.467f, 74.9237f, 189.2f, 74.9428f
+path.cubicTo(SkBits2Float(0x433deeef), SkBits2Float(0x4295ec8d), SkBits2Float(0x433eaaab), SkBits2Float(0x42963733), SkBits2Float(0x433f6666), SkBits2Float(0x42963ed3));  // 189.933f, 74.962f, 190.667f, 75.1078f, 191.4f, 75.1227f
+path.cubicTo(SkBits2Float(0x43402222), SkBits2Float(0x42964673), SkBits2Float(0x4340ddde), SkBits2Float(0x42962415), SkBits2Float(0x4341999a), SkBits2Float(0x42961079));  // 192.133f, 75.1376f, 192.867f, 75.0705f, 193.6f, 75.0322f
+path.cubicTo(SkBits2Float(0x43425555), SkBits2Float(0x4295fcdd), SkBits2Float(0x43431111), SkBits2Float(0x4295d1c9), SkBits2Float(0x4343cccd), SkBits2Float(0x4295c929));  // 194.333f, 74.9939f, 195.067f, 74.9097f, 195.8f, 74.8929f
+path.cubicTo(SkBits2Float(0x43448889), SkBits2Float(0x4295c089), SkBits2Float(0x43454444), SkBits2Float(0x4295d2aa), SkBits2Float(0x43460000), SkBits2Float(0x4295dcb8));  // 196.533f, 74.876f, 197.267f, 74.9115f, 198, 74.9311f
+path.cubicTo(SkBits2Float(0x4346bbbc), SkBits2Float(0x4295e6c6), SkBits2Float(0x43477777), SkBits2Float(0x4295f749), SkBits2Float(0x43483333), SkBits2Float(0x4296057c));  // 198.733f, 74.9507f, 199.467f, 74.983f, 200.2f, 75.0107f
+path.cubicTo(SkBits2Float(0x4348eeef), SkBits2Float(0x429613af), SkBits2Float(0x4349aaab), SkBits2Float(0x429628a2), SkBits2Float(0x434a6666), SkBits2Float(0x429631ea));  // 200.933f, 75.0384f, 201.667f, 75.0794f, 202.4f, 75.0975f
+path.cubicTo(SkBits2Float(0x434b2222), SkBits2Float(0x42963b32), SkBits2Float(0x434bddde), SkBits2Float(0x4296396c), SkBits2Float(0x434c999a), SkBits2Float(0x42963d2d));  // 203.133f, 75.1156f, 203.867f, 75.1122f, 204.6f, 75.1195f
+path.cubicTo(SkBits2Float(0x434d5555), SkBits2Float(0x429640ee), SkBits2Float(0x434e1111), SkBits2Float(0x429642b4), SkBits2Float(0x434ecccd), SkBits2Float(0x4296486f));  // 205.333f, 75.1268f, 206.067f, 75.1303f, 206.8f, 75.1415f
+path.cubicTo(SkBits2Float(0x434f8889), SkBits2Float(0x42964e2b), SkBits2Float(0x43504444), SkBits2Float(0x429655c7), SkBits2Float(0x43510000), SkBits2Float(0x42965f91));  // 207.533f, 75.1527f, 208.267f, 75.1675f, 209, 75.1867f
+path.cubicTo(SkBits2Float(0x4351bbbc), SkBits2Float(0x4296695b), SkBits2Float(0x43527777), SkBits2Float(0x42967a03), SkBits2Float(0x43533333), SkBits2Float(0x4296832d));  // 209.733f, 75.2058f, 210.467f, 75.2383f, 211.2f, 75.2562f
+path.cubicTo(SkBits2Float(0x4353eeef), SkBits2Float(0x42968c57), SkBits2Float(0x4354aaab), SkBits2Float(0x42969305), SkBits2Float(0x43556666), SkBits2Float(0x4296968e));  // 211.933f, 75.2741f, 212.667f, 75.2871f, 213.4f, 75.2941f
+path.cubicTo(SkBits2Float(0x43562222), SkBits2Float(0x42969a17), SkBits2Float(0x4356ddde), SkBits2Float(0x42969bbf), SkBits2Float(0x4357999a), SkBits2Float(0x42969863));  // 214.133f, 75.301f, 214.867f, 75.3042f, 215.6f, 75.2976f
+path.cubicTo(SkBits2Float(0x43585555), SkBits2Float(0x42969506), SkBits2Float(0x43591111), SkBits2Float(0x42968980), SkBits2Float(0x4359cccd), SkBits2Float(0x42968262));  // 216.333f, 75.2911f, 217.067f, 75.2686f, 217.8f, 75.2547f
+path.cubicTo(SkBits2Float(0x435a8889), SkBits2Float(0x42967b45), SkBits2Float(0x435b4444), SkBits2Float(0x42966071), SkBits2Float(0x435c0000), SkBits2Float(0x42966db2));  // 218.533f, 75.2408f, 219.267f, 75.1884f, 220, 75.2142f
+path.cubicTo(SkBits2Float(0x435cbbbc), SkBits2Float(0x42967af3), SkBits2Float(0x435d7777), SkBits2Float(0x4296ba1b), SkBits2Float(0x435e3333), SkBits2Float(0x4296d1e8));  // 220.733f, 75.2401f, 221.467f, 75.3635f, 222.2f, 75.41f
+path.cubicTo(SkBits2Float(0x435eeeef), SkBits2Float(0x4296e9b5), SkBits2Float(0x435faaab), SkBits2Float(0x4296fe13), SkBits2Float(0x43606666), SkBits2Float(0x4296fc81));  // 222.933f, 75.4565f, 223.667f, 75.4962f, 224.4f, 75.4932f
+path.cubicTo(SkBits2Float(0x43612222), SkBits2Float(0x4296faef), SkBits2Float(0x4361ddde), SkBits2Float(0x4296d99e), SkBits2Float(0x4362999a), SkBits2Float(0x4296c87a));  // 225.133f, 75.4901f, 225.867f, 75.425f, 226.6f, 75.3916f
+path.cubicTo(SkBits2Float(0x43635555), SkBits2Float(0x4296b757), SkBits2Float(0x43641111), SkBits2Float(0x42969bf5), SkBits2Float(0x4364cccd), SkBits2Float(0x429695ac));  // 227.333f, 75.3581f, 228.067f, 75.3046f, 228.8f, 75.2923f
+path.cubicTo(SkBits2Float(0x43658889), SkBits2Float(0x42968f62), SkBits2Float(0x43664444), SkBits2Float(0x42969d8e), SkBits2Float(0x43670000), SkBits2Float(0x4296a2c3));  // 229.533f, 75.28f, 230.267f, 75.3077f, 231, 75.3179f
+path.cubicTo(SkBits2Float(0x4367bbbc), SkBits2Float(0x4296a7f8), SkBits2Float(0x43687777), SkBits2Float(0x4296b284), SkBits2Float(0x43693333), SkBits2Float(0x4296b4eb));  // 231.733f, 75.3281f, 232.467f, 75.3487f, 233.2f, 75.3534f
+path.cubicTo(SkBits2Float(0x4369eeef), SkBits2Float(0x4296b751), SkBits2Float(0x436aaaab), SkBits2Float(0x4296b8ac), SkBits2Float(0x436b6666), SkBits2Float(0x4296b12a));  // 233.933f, 75.358f, 234.667f, 75.3607f, 235.4f, 75.346f
+path.cubicTo(SkBits2Float(0x436c2222), SkBits2Float(0x4296a9a8), SkBits2Float(0x436cddde), SkBits2Float(0x42969446), SkBits2Float(0x436d999a), SkBits2Float(0x429687e1));  // 236.133f, 75.3314f, 236.867f, 75.2896f, 237.6f, 75.2654f
+path.cubicTo(SkBits2Float(0x436e5555), SkBits2Float(0x42967b7c), SkBits2Float(0x436f1111), SkBits2Float(0x42967079), SkBits2Float(0x436fcccd), SkBits2Float(0x429666cd));  // 238.333f, 75.2412f, 239.067f, 75.2197f, 239.8f, 75.2008f
+path.cubicTo(SkBits2Float(0x43708889), SkBits2Float(0x42965d20), SkBits2Float(0x43714444), SkBits2Float(0x42965d94), SkBits2Float(0x43720000), SkBits2Float(0x42964dd6));  // 240.533f, 75.1819f, 241.267f, 75.1828f, 242, 75.152f
+path.cubicTo(SkBits2Float(0x4372bbbc), SkBits2Float(0x42963e19), SkBits2Float(0x43737777), SkBits2Float(0x42961c64), SkBits2Float(0x43743333), SkBits2Float(0x4296085b));  // 242.733f, 75.1213f, 243.467f, 75.0555f, 244.2f, 75.0163f
+path.cubicTo(SkBits2Float(0x4374eeef), SkBits2Float(0x4295f453), SkBits2Float(0x4375aaab), SkBits2Float(0x4295e407), SkBits2Float(0x43766666), SkBits2Float(0x4295d5a4));  // 244.933f, 74.9772f, 245.667f, 74.9454f, 246.4f, 74.9173f
+path.cubicTo(SkBits2Float(0x43772222), SkBits2Float(0x4295c741), SkBits2Float(0x4377ddde), SkBits2Float(0x4295bc09), SkBits2Float(0x4378999a), SkBits2Float(0x4295b207));  // 247.133f, 74.8892f, 247.867f, 74.8673f, 248.6f, 74.8477f
+path.cubicTo(SkBits2Float(0x43795555), SkBits2Float(0x4295a806), SkBits2Float(0x437a1111), SkBits2Float(0x42959dad), SkBits2Float(0x437acccd), SkBits2Float(0x4295999a));  // 249.333f, 74.8282f, 250.067f, 74.808f, 250.8f, 74.8f
+path.cubicTo(SkBits2Float(0x437b8889), SkBits2Float(0x42959588), SkBits2Float(0x437c4444), SkBits2Float(0x4295999a), SkBits2Float(0x437d0000), SkBits2Float(0x4295999a));  // 251.533f, 74.7921f, 252.267f, 74.8f, 253, 74.8f
+path.lineTo(SkBits2Float(0x437d0000), SkBits2Float(0x4295999a));  // 253, 74.8f
+path.lineTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a));  // 68.2f, 74.8f
+path.close();
+    testPathOp(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename);
+}
+
+
+static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = issue3651_1;
+static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
+
+static struct TestDesc tests[] = {
+    TEST(issue3651_6),
+    TEST(issue3651_1a),
+    TEST(issue3651_1),
+    TEST(issue3651_7),
+    TEST(issue3651_5),
+    TEST(issue3651_4),
+    TEST(issue3651_2),
+    TEST(issue3651_3),
+};
+
+static const size_t testCount = SK_ARRAY_COUNT(tests);
+
+static bool runReverse = false;
+
+DEF_TEST(PathOpsIssue3651, reporter) {
+#if DEBUG_SHOW_TEST_NAME
+    strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
+#endif
+    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
+}
diff --git a/src/third_party/skia/tests/PathOpsLineIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsLineIntersectionTest.cpp
index 105187b..bc0259c 100644
--- a/src/third_party/skia/tests/PathOpsLineIntersectionTest.cpp
+++ b/src/third_party/skia/tests/PathOpsLineIntersectionTest.cpp
@@ -11,6 +11,9 @@
 
 // FIXME: add tests for intersecting, non-intersecting, degenerate, coincident
 static const SkDLine tests[][2] = {
+{{{{0.00010360032320022583, 1.0172703415155411}, {0.00014114845544099808, 1.0200891587883234}}},
+ {{{0.00010259449481964111, 1.017270140349865}, {0.00018215179443359375, 1.022890567779541}}}},
+
 #if 0
     // these do intersect at a pair of points, but not close enough for check results liking
     {{{{365.848175,5081.15186}, {368,5103}}}, {{{367.967712,5102.61084}, {368.278717,5105.71045}}}},
@@ -82,10 +85,13 @@
 static const size_t coincidentTests_count = SK_ARRAY_COUNT(coincidentTests);
 
 static void check_results(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2,
-                          const SkIntersections& ts) {
+                          const SkIntersections& ts, bool nearAllowed) {
     for (int i = 0; i < ts.used(); ++i) {
         SkDPoint result1 = line1.ptAtT(ts[0][i]);
         SkDPoint result2 = line2.ptAtT(ts[1][i]);
+        if (nearAllowed && result1.roughlyEqual(result2)) {
+            continue;
+        }
         if (!result1.approximatelyEqual(result2) && !ts.nearlySame(i)) {
             REPORTER_ASSERT(reporter, ts.used() != 1);
             result2 = line2.ptAtT(ts[1][i ^ 1]);
@@ -98,14 +104,16 @@
     }
 }
 
-static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2) {
+static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2,
+        bool nearAllowed) {
     SkASSERT(ValidLine(line1));
     SkASSERT(ValidLine(line2));
     SkIntersections i;
+    i.allowNear(nearAllowed);
     int pts = i.intersect(line1, line2);
     REPORTER_ASSERT(reporter, pts);
     REPORTER_ASSERT(reporter, pts == i.used());
-    check_results(reporter, line1, line2, i);
+    check_results(reporter, line1, line2, i, nearAllowed);
     if (line1[0] == line1[1] || line2[0] == line2[1]) {
         return;
     }
@@ -114,28 +122,28 @@
         double right = SkTMax(line1[0].fX, line1[1].fX);
         SkIntersections ts;
         ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left);
-        check_results(reporter, line2, line1, ts);
+        check_results(reporter, line2, line1, ts, nearAllowed);
     }
     if (line2[0].fY == line2[1].fY) {
         double left = SkTMin(line2[0].fX, line2[1].fX);
         double right = SkTMax(line2[0].fX, line2[1].fX);
         SkIntersections ts;
         ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left);
-        check_results(reporter, line1, line2, ts);
+        check_results(reporter, line1, line2, ts, nearAllowed);
     }
     if (line1[0].fX == line1[1].fX) {
         double top = SkTMin(line1[0].fY, line1[1].fY);
         double bottom = SkTMax(line1[0].fY, line1[1].fY);
         SkIntersections ts;
         ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top);
-        check_results(reporter, line2, line1, ts);
+        check_results(reporter, line2, line1, ts, nearAllowed);
     }
     if (line2[0].fX == line2[1].fX) {
         double top = SkTMin(line2[0].fY, line2[1].fY);
         double bottom = SkTMax(line2[0].fY, line2[1].fY);
         SkIntersections ts;
         ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top);
-        check_results(reporter, line1, line2, ts);
+        check_results(reporter, line1, line2, ts, nearAllowed);
     }
     reporter->bumpTestCount();
 }
@@ -148,7 +156,7 @@
     int pts = ts.intersect(line1, line2);
     REPORTER_ASSERT(reporter, pts == 2);
     REPORTER_ASSERT(reporter, pts == ts.used());
-    check_results(reporter, line1, line2, ts);
+    check_results(reporter, line1, line2, ts, false);
     if (line1[0] == line1[1] || line2[0] == line2[1]) {
         return;
     }
@@ -159,7 +167,7 @@
         ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left);
         REPORTER_ASSERT(reporter, pts == 2);
         REPORTER_ASSERT(reporter, pts == ts.used());
-        check_results(reporter, line2, line1, ts);
+        check_results(reporter, line2, line1, ts, false);
     }
     if (line2[0].fY == line2[1].fY) {
         double left = SkTMin(line2[0].fX, line2[1].fX);
@@ -168,7 +176,7 @@
         ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left);
         REPORTER_ASSERT(reporter, pts == 2);
         REPORTER_ASSERT(reporter, pts == ts.used());
-        check_results(reporter, line1, line2, ts);
+        check_results(reporter, line1, line2, ts, false);
     }
     if (line1[0].fX == line1[1].fX) {
         double top = SkTMin(line1[0].fY, line1[1].fY);
@@ -177,7 +185,7 @@
         ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top);
         REPORTER_ASSERT(reporter, pts == 2);
         REPORTER_ASSERT(reporter, pts == ts.used());
-        check_results(reporter, line2, line1, ts);
+        check_results(reporter, line2, line1, ts, false);
     }
     if (line2[0].fX == line2[1].fX) {
         double top = SkTMin(line2[0].fY, line2[1].fY);
@@ -186,7 +194,7 @@
         ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top);
         REPORTER_ASSERT(reporter, pts == 2);
         REPORTER_ASSERT(reporter, pts == ts.used());
-        check_results(reporter, line1, line2, ts);
+        check_results(reporter, line1, line2, ts, false);
     }
     reporter->bumpTestCount();
 }
@@ -201,7 +209,7 @@
     for (index = 0; index < tests_count; ++index) {
         const SkDLine& line1 = tests[index][0];
         const SkDLine& line2 = tests[index][1];
-        testOne(reporter, line1, line2);
+        testOne(reporter, line1, line2, true);
     }
     for (index = 0; index < noIntersect_count; ++index) {
         const SkDLine& line1 = noIntersect[index][0];
@@ -217,8 +225,13 @@
 DEF_TEST(PathOpsLineIntersectionOneOff, reporter) {
     int index = 0;
     SkASSERT(index < (int) tests_count);
-    testOne(reporter, tests[index][0], tests[index][1]);
-    testOne(reporter, tests[1][0], tests[1][1]);
+    testOne(reporter, tests[index][0], tests[index][1], true);
+}
+
+DEF_TEST(PathOpsLineIntersectionExactOneOff, reporter) {
+    int index = 0;
+    SkASSERT(index < (int) tests_count);
+    testOne(reporter, tests[index][0], tests[index][1], false);
 }
 
 DEF_TEST(PathOpsLineIntersectionOneCoincident, reporter) {
diff --git a/src/third_party/skia/tests/PathOpsLineParametetersTest.cpp b/src/third_party/skia/tests/PathOpsLineParametetersTest.cpp
index aab1f7a..66a4be2 100644
--- a/src/third_party/skia/tests/PathOpsLineParametetersTest.cpp
+++ b/src/third_party/skia/tests/PathOpsLineParametetersTest.cpp
@@ -9,7 +9,7 @@
 #include "Test.h"
 
 // tests to verify that distance calculations are coded correctly
-static const SkDCubic tests[] = {
+static const CubicPts tests[] = {
     {{{0, 0}, {1, 1}, {2, 2}, {0, 3}}},
     {{{0, 0}, {1, 1}, {2, 2}, {3, 0}}},
     {{{0, 0}, {5, 0}, {-2, 4}, {3, 4}}},
@@ -40,7 +40,9 @@
 DEF_TEST(PathOpsLineParameters, reporter) {
     for (size_t index = 0; index < tests_count; ++index) {
         SkLineParameters lineParameters;
-        const SkDCubic& cubic = tests[index];
+        const CubicPts& c = tests[index];
+        SkDCubic cubic;
+        cubic.debugSet(c.fPts);
         SkASSERT(ValidCubic(cubic));
         lineParameters.cubicEndPoints(cubic, 0, 3);
         double denormalizedDistance[2];
diff --git a/src/third_party/skia/tests/PathOpsOpCircleThreadedTest.cpp b/src/third_party/skia/tests/PathOpsOpCircleThreadedTest.cpp
new file mode 100644
index 0000000..7711dd0
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsOpCircleThreadedTest.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsExtendedTest.h"
+#include "PathOpsThreadedCommon.h"
+#include "SkString.h"
+
+static int loopNo = 4;
+
+static void testOpCirclesMain(PathOpsThreadState* data) {
+        SkASSERT(data);
+    PathOpsThreadState& state = *data;
+    SkString pathStr;
+    for (int a = 0 ; a < 6; ++a) {
+        for (int b = a + 1 ; b < 7; ++b) {
+            for (int c = 0 ; c < 6; ++c) {
+                for (int d = c + 1 ; d < 7; ++d) {
+                    for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) {
+    for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f) {
+        SkPath pathA, pathB;
+        pathA.setFillType((SkPath::FillType) e);
+        pathA.addCircle(SkIntToScalar(state.fA), SkIntToScalar(state.fB), SkIntToScalar(state.fC),
+                state.fD ? SkPath::kCW_Direction : SkPath::kCCW_Direction);
+        pathB.setFillType((SkPath::FillType) f);
+        pathB.addCircle(SkIntToScalar(a), SkIntToScalar(b), SkIntToScalar(c),
+                d ? SkPath::kCW_Direction : SkPath::kCCW_Direction);
+        for (int op = 0 ; op <= kXOR_SkPathOp; ++op)    {
+            if (state.fReporter->verbose()) {
+                pathStr.printf("static void circlesOp%d(skiatest::Reporter* reporter,"
+                        " const char* filename) {\n", loopNo);
+                pathStr.appendf("    SkPath path, pathB;\n");
+                pathStr.appendf("    path.setFillType(SkPath::k%s_FillType);\n",
+                        e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
+                        ? "EvenOdd" : "?UNDEFINED");
+                pathStr.appendf("    path.addCircle(%d, %d, %d, %s);\n", state.fA, state.fB,
+                        state.fC, state.fD ? "SkPath::kCW_Direction" : "SkPath::kCCW_Direction");
+                pathStr.appendf("    pathB.setFillType(SkPath::k%s_FillType);\n",
+                        f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
+                        ? "EvenOdd" : "?UNDEFINED");
+                pathStr.appendf("    pathB.addCircle(%d, %d, %d, %s);\n", a, b,
+                        c, d ? "SkPath::kCW_Direction" : "SkPath::kCCW_Direction");
+                pathStr.appendf("    testPathOp(reporter, path, pathB, %s, filename);\n",
+                        SkPathOpsDebug::OpStr((SkPathOp) op));
+                pathStr.appendf("}\n");
+                state.outputProgress(pathStr.c_str(), (SkPathOp) op);
+            }
+            if (!testPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "circles")) {
+                if (state.fReporter->verbose()) {
+                    ++loopNo;
+                    goto skipToNext;
+                }
+            }
+        }
+    }
+                    }
+skipToNext: ;
+                }
+            }
+        }
+    }
+}
+
+DEF_TEST(PathOpsOpCircleThreaded, reporter) {
+    initializeTests(reporter, "circleOp");
+    PathOpsThreadedTestRunner testRunner(reporter);
+    for (int a = 0; a < 6; ++a) {  // outermost
+        for (int b = a + 1; b < 7; ++b) {
+            for (int c = 0 ; c < 6; ++c) {
+                for (int d = 0; d < 2; ++d) {
+                    *testRunner.fRunnables.append() = new PathOpsThreadedRunnable(
+                            &testOpCirclesMain, a, b, c, d, &testRunner);
+                }
+            }
+            if (!reporter->allowExtendedTest()) goto finish;
+        }
+    }
+finish:
+    testRunner.render();
+}
diff --git a/src/third_party/skia/tests/PathOpsOpCubicThreadedTest.cpp b/src/third_party/skia/tests/PathOpsOpCubicThreadedTest.cpp
index 751ccc5..0bb8c19 100644
--- a/src/third_party/skia/tests/PathOpsOpCubicThreadedTest.cpp
+++ b/src/third_party/skia/tests/PathOpsOpCubicThreadedTest.cpp
@@ -6,6 +6,9 @@
  */
 #include "PathOpsExtendedTest.h"
 #include "PathOpsThreadedCommon.h"
+#include "SkString.h"
+
+static int loopNo = 158;
 
 static void testOpCubicsMain(PathOpsThreadState* data) {
 #if DEBUG_SHOW_TEST_NAME
@@ -13,11 +16,7 @@
 #endif
     SkASSERT(data);
     PathOpsThreadState& state = *data;
-    char pathStr[1024];  // gdb: set print elements 400
-    bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter?
-    if (progress) {
-        sk_bzero(pathStr, sizeof(pathStr));
-    }
+    SkString pathStr;
     for (int a = 0 ; a < 6; ++a) {
         for (int b = a + 1 ; b < 7; ++b) {
             for (int c = 0 ; c < 6; ++c) {
@@ -25,23 +24,6 @@
                     for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) {
     for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f) {
         SkPath pathA, pathB;
-        if (progress) {
-            char* str = pathStr;
-            str += sprintf(str, "    path.setFillType(SkPath::k%s_FillType);\n",
-                    e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
-                    ? "EvenOdd" : "?UNDEFINED");
-            str += sprintf(str, "    path.moveTo(%d,%d);\n", state.fA, state.fB);
-            str += sprintf(str, "    path.cubicTo(%d,%d, %d,%d, %d,%d);\n", state.fC, state.fD,
-                    b, a, d, c);
-            str += sprintf(str, "    path.close();\n");
-            str += sprintf(str, "    pathB.setFillType(SkPath::k%s_FillType);\n",
-                    f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
-                    ? "EvenOdd" : "?UNDEFINED");
-            str += sprintf(str, "    pathB.moveTo(%d,%d);\n", a, b);
-            str += sprintf(str, "    pathB.cubicTo(%d,%d, %d,%d, %d,%d);\n", c, d,
-                    state.fB, state.fA, state.fD, state.fC);
-            str += sprintf(str, "    pathB.close();\n");
-        }
         pathA.setFillType((SkPath::FillType) e);
         pathA.moveTo(SkIntToScalar(state.fA), SkIntToScalar(state.fB));
         pathA.cubicTo(SkIntToScalar(state.fC), SkIntToScalar(state.fD), SkIntToScalar(b),
@@ -52,14 +34,40 @@
         pathB.cubicTo(SkIntToScalar(c), SkIntToScalar(d), SkIntToScalar(state.fB),
                 SkIntToScalar(state.fA), SkIntToScalar(state.fD), SkIntToScalar(state.fC));
         pathB.close();
-        for (int op = 0 ; op <= kXOR_PathOp; ++op)    {
-            if (progress) {
-                outputProgress(state.fPathStr, pathStr, (SkPathOp) op);
+        for (int op = 0 ; op <= kXOR_SkPathOp; ++op)    {
+            if (state.fReporter->verbose()) {
+                pathStr.printf("static void cubicOp%d(skiatest::Reporter* reporter,"
+                        " const char* filename) {\n", loopNo);
+                pathStr.appendf("    SkPath path, pathB;\n");
+                pathStr.appendf("    path.setFillType(SkPath::k%s_FillType);\n",
+                        e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
+                        ? "EvenOdd" : "?UNDEFINED");
+                pathStr.appendf("    path.moveTo(%d,%d);\n", state.fA, state.fB);
+                pathStr.appendf("    path.cubicTo(%d,%d, %d,%d, %d,%d);\n", state.fC, state.fD,
+                        b, a, d, c);
+                pathStr.appendf("    path.close();\n");
+                pathStr.appendf("    pathB.setFillType(SkPath::k%s_FillType);\n",
+                        f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
+                        ? "EvenOdd" : "?UNDEFINED");
+                pathStr.appendf("    pathB.moveTo(%d,%d);\n", a, b);
+                pathStr.appendf("    pathB.cubicTo(%d,%d, %d,%d, %d,%d);\n", c, d,
+                        state.fB, state.fA, state.fD, state.fC);
+                pathStr.appendf("    pathB.close();\n");
+                pathStr.appendf("    testPathOp(reporter, path, pathB, %s, filename);\n",
+                        SkPathOpsDebug::OpStr((SkPathOp) op));
+                pathStr.appendf("}\n");
+                state.outputProgress(pathStr.c_str(), (SkPathOp) op);
             }
-            testThreadedPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "cubics");
+            if (!testPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "cubics")) {
+                if (state.fReporter->verbose()) {
+                    ++loopNo;
+                    goto skipToNext;
+                }
+            }
         }
     }
                     }
+skipToNext: ;
                 }
             }
         }
@@ -73,8 +81,8 @@
         for (int b = a + 1; b < 7; ++b) {
             for (int c = 0 ; c < 6; ++c) {
                 for (int d = c + 1; d < 7; ++d) {
-                    *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
-                            (&testOpCubicsMain, a, b, c, d, &testRunner));
+                    *testRunner.fRunnables.append() =
+                            new PathOpsThreadedRunnable(&testOpCubicsMain, a, b, c, d, &testRunner);
                 }
             }
             if (!reporter->allowExtendedTest()) goto finish;
@@ -82,5 +90,4 @@
     }
 finish:
     testRunner.render();
-    ShowTestArray();
 }
diff --git a/src/third_party/skia/tests/PathOpsOpLoopThreadedTest.cpp b/src/third_party/skia/tests/PathOpsOpLoopThreadedTest.cpp
old mode 100755
new mode 100644
index c50e23b..04caac9
--- a/src/third_party/skia/tests/PathOpsOpLoopThreadedTest.cpp
+++ b/src/third_party/skia/tests/PathOpsOpLoopThreadedTest.cpp
@@ -6,6 +6,25 @@
  */
 #include "PathOpsExtendedTest.h"
 #include "PathOpsThreadedCommon.h"
+#include "SkString.h"
+
+static int loopNo = 17;
+
+static void add_point(SkString* str, SkScalar x, SkScalar y) {
+    int asInt = SkScalarRoundToInt(x);
+    if (SkIntToScalar(asInt) == x) {
+        str->appendf("%d", asInt);
+    } else {
+        str->appendf("%1.9gf", x);
+    }
+    str->appendf(",");
+    asInt = SkScalarRoundToInt(y);
+    if (SkIntToScalar(asInt) == y) {
+        str->appendf("%d", asInt);
+    } else {
+        str->appendf("%1.9gf", y);
+    }
+}
 
 static void testOpLoopsMain(PathOpsThreadState* data) {
 #if DEBUG_SHOW_TEST_NAME
@@ -13,11 +32,7 @@
 #endif
     SkASSERT(data);
     PathOpsThreadState& state = *data;
-    char pathStr[1024];  // gdb: set print elements 400
-    bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter?
-    if (progress) {
-        sk_bzero(pathStr, sizeof(pathStr));
-    }
+    SkString pathStr;
     for (int a = 0 ; a < 6; ++a) {
         for (int b = a + 1 ; b < 7; ++b) {
             for (int c = 0 ; c < 6; ++c) {
@@ -33,17 +48,6 @@
         SkPoint endD = { midB.fX - v.fY * state.fD / 3,
                           midB.fY + v.fX * state.fD / 3 };
         SkPath pathA, pathB;
-        if (progress) {
-            char* str = pathStr;
-            str += sprintf(str, "    path.moveTo(%d,%d);\n", a, b);
-            str += sprintf(str, "    path.cubicTo(%d,%d, %1.9gf,%1.9gf, %1.9gf,%1.9gf);\n",
-                    c, d, endC.fX, endC.fY, endD.fX, endD.fY);
-            str += sprintf(str, "    path.close();\n");
-            str += sprintf(str, "    pathB.moveTo(%d,%d);\n", c, d);
-            str += sprintf(str, "    pathB.cubicTo(%1.9gf,%1.9gf, %1.9gf,%1.9gf, %d,%d);\n",
-                    endC.fX, endC.fY, endD.fX, endD.fY, a, b);
-            str += sprintf(str, "    pathB.close();\n");
-        }
         pathA.moveTo(SkIntToScalar(a), SkIntToScalar(b));
         pathA.cubicTo(SkIntToScalar(c), SkIntToScalar(d), endC.fX, endC.fY, endD.fX, endD.fY);
         pathA.close();
@@ -51,10 +55,30 @@
         pathB.cubicTo(endC.fX, endC.fY, endD.fX, endD.fY, SkIntToScalar(a), SkIntToScalar(b));
         pathB.close();
 //        SkDebugf("%s\n", pathStr);
-        if (progress) {
-            outputProgress(state.fPathStr, pathStr, kIntersect_PathOp);
+        if (state.fReporter->verbose()) {
+            pathStr.printf("static void loop%d(skiatest::Reporter* reporter,"
+                    " const char* filename) {\n", loopNo);
+            pathStr.appendf("    SkPath path, pathB;\n");
+            pathStr.appendf("    path.moveTo(%d,%d);\n", a, b);
+            pathStr.appendf("    path.cubicTo(%d,%d, ", c, d);
+            add_point(&pathStr, endC.fX, endC.fY);
+            pathStr.appendf(", ");
+            add_point(&pathStr, endD.fX, endD.fY);
+            pathStr.appendf(");\n");
+            pathStr.appendf("    path.close();\n");
+            pathStr.appendf("    pathB.moveTo(%d,%d);\n", c, d);
+            pathStr.appendf("    pathB.cubicTo(");
+            add_point(&pathStr, endC.fX, endC.fY);
+            pathStr.appendf(", ");
+            add_point(&pathStr, endD.fX, endD.fY);
+            pathStr.appendf(", %d,%d);\n", a, b);
+            pathStr.appendf("    pathB.close();\n");
+            pathStr.appendf("    testPathOp(reporter, path, pathB, kIntersect_SkPathOp,"
+                    " filename);\n");
+            pathStr.appendf("}\n");
+            state.outputProgress(pathStr.c_str(), kIntersect_SkPathOp);
         }
-        testThreadedPathOp(state.fReporter, pathA, pathB, kIntersect_PathOp, "loops");
+        testPathOp(state.fReporter, pathA, pathB, kIntersect_SkPathOp, "loops");
                 }
             }
         }
@@ -62,17 +86,14 @@
 }
 
 DEF_TEST(PathOpsOpLoopsThreaded, reporter) {
-    if (!FLAGS_runFail) {
-        return;
-    }
-    initializeTests(reporter, "cubicOp");
+    initializeTests(reporter, "loopOp");
     PathOpsThreadedTestRunner testRunner(reporter);
     for (int a = 0; a < 6; ++a) {  // outermost
         for (int b = a + 1; b < 7; ++b) {
             for (int c = 0 ; c < 6; ++c) {
                 for (int d = c + 1; d < 7; ++d) {
-                    *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
-                            (&testOpLoopsMain, a, b, c, d, &testRunner));
+                    *testRunner.fRunnables.append() =
+                            new PathOpsThreadedRunnable(&testOpLoopsMain, a, b, c, d, &testRunner);
                 }
             }
             if (!reporter->allowExtendedTest()) goto finish;
@@ -80,30 +101,4 @@
     }
 finish:
     testRunner.render();
-    ShowTestArray();
-}
-
-DEF_TEST(PathOpsOpLoops, reporter) {
-    if (!FLAGS_runFail) {
-        return;
-    }
-    initializeTests(reporter, "cubicOp");
-    PathOpsThreadState state;
-    state.fReporter = reporter;
-    SkBitmap bitmap;
-    state.fBitmap = &bitmap;
-    char pathStr[PATH_STR_SIZE];
-    state.fPathStr = pathStr;
-    for (state.fA = 0; state.fA < 6; ++state.fA) {  // outermost
-        for (state.fB = state.fA + 1; state.fB < 7; ++state.fB) {
-            for (state.fC = 0 ; state.fC < 6; ++state.fC) {
-                for (state.fD = state.fC + 1; state.fD < 7; ++state.fD) {
-                    testOpLoopsMain(&state);
-                }
-            }
-            if (!reporter->allowExtendedTest()) goto finish;
-        }
-    }
-finish:
-    ShowTestArray();
 }
diff --git a/src/third_party/skia/tests/PathOpsOpRectThreadedTest.cpp b/src/third_party/skia/tests/PathOpsOpRectThreadedTest.cpp
index 1b6e4e8..e904a8d 100644
--- a/src/third_party/skia/tests/PathOpsOpRectThreadedTest.cpp
+++ b/src/third_party/skia/tests/PathOpsOpRectThreadedTest.cpp
@@ -6,6 +6,7 @@
  */
 #include "PathOpsExtendedTest.h"
 #include "PathOpsThreadedCommon.h"
+#include "SkString.h"
 
 // four rects, of four sizes
 // for 3 smaller sizes, tall, wide
@@ -14,38 +15,19 @@
 // not included, square, tall, wide (2 bits)
 // cw or ccw (1 bit)
 
+static int loopNo = 6;
+
 static void testPathOpsRectsMain(PathOpsThreadState* data)
 {
     SkASSERT(data);
     PathOpsThreadState& state = *data;
-    char pathStr[1024];  // gdb: set print elements 400
-    bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter?
-    if (progress) {
-        sk_bzero(pathStr, sizeof(pathStr));
-    }
+    SkString pathStr;
     for (int a = 0 ; a < 6; ++a) {
         for (int b = a + 1 ; b < 7; ++b) {
             for (int c = 0 ; c < 6; ++c) {
                 for (int d = c + 1 ; d < 7; ++d) {
                     for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) {
     for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f)   {
-        if (progress) {
-            char* str = pathStr;
-            str += sprintf(str, "    path.setFillType(SkPath::k%s_FillType);\n",
-                    e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
-                    ? "EvenOdd" : "?UNDEFINED");
-            str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
-                    " SkPath::kCW_Direction);\n", state.fA, state.fA, state.fB, state.fB);
-            str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
-                    " SkPath::kCW_Direction);\n", state.fC, state.fC, state.fD, state.fD);
-            str += sprintf(str, "    pathB.setFillType(SkPath::k%s_FillType);\n",
-                    f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
-                    ? "EvenOdd" : "?UNDEFINED");
-            str += sprintf(str, "    pathB.addRect(%d, %d, %d, %d,"
-                    " SkPath::kCW_Direction);\n", a, a, b, b);
-            str += sprintf(str, "    pathB.addRect(%d, %d, %d, %d,"
-                    " SkPath::kCW_Direction);\n", c, c, d, d);
-        }
         SkPath pathA, pathB;
         pathA.setFillType((SkPath::FillType) e);
         pathA.addRect(SkIntToScalar(state.fA), SkIntToScalar(state.fA), SkIntToScalar(state.fB),
@@ -59,14 +41,41 @@
         pathB.addRect(SkIntToScalar(c), SkIntToScalar(c), SkIntToScalar(d),
                 SkIntToScalar(d), SkPath::kCW_Direction);
         pathB.close();
-        for (int op = 0 ; op <= kXOR_PathOp; ++op)    {
-            if (progress) {
-                outputProgress(state.fPathStr, pathStr, (SkPathOp) op);
+        for (int op = 0 ; op <= kXOR_SkPathOp; ++op)    {
+            if (state.fReporter->verbose()) {
+                pathStr.printf(
+                        "static void rects%d(skiatest::Reporter* reporter,"
+                        "const char* filename) {\n", loopNo);
+                pathStr.appendf("    SkPath path, pathB;");
+                pathStr.appendf("    path.setFillType(SkPath::k%s_FillType);\n",
+                        e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
+                        ? "EvenOdd" : "?UNDEFINED");
+                pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
+                        " SkPath::kCW_Direction);\n", state.fA, state.fA, state.fB, state.fB);
+                pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
+                        " SkPath::kCW_Direction);\n", state.fC, state.fC, state.fD, state.fD);
+                pathStr.appendf("    pathB.setFillType(SkPath::k%s_FillType);\n",
+                        f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
+                        ? "EvenOdd" : "?UNDEFINED");
+                pathStr.appendf("    pathB.addRect(%d, %d, %d, %d,"
+                        " SkPath::kCW_Direction);\n", a, a, b, b);
+                pathStr.appendf("    pathB.addRect(%d, %d, %d, %d,"
+                        " SkPath::kCW_Direction);\n", c, c, d, d);
+                pathStr.appendf("    testPathOp(reporter, path, pathB, %s, filename);\n",
+                        SkPathOpsDebug::OpStr((SkPathOp) op));
+                pathStr.appendf("}\n\n");
+                state.outputProgress(pathStr.c_str(), (SkPathOp) op);
             }
-            testThreadedPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "rects");
+            if (!testPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "rects")) {
+                if (state.fReporter->verbose()) {
+                    ++loopNo;
+                    goto skipToNext;
+                }
+            }
         }
     }
                     }
+skipToNext: ;
                 }
             }
         }
@@ -80,8 +89,8 @@
         for (int b = a + 1; b < 7; ++b) {
             for (int c = 0 ; c < 6; ++c) {
                 for (int d = c + 1; d < 7; ++d) {
-                    *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
-                            (&testPathOpsRectsMain, a, b, c, d, &testRunner));
+                    *testRunner.fRunnables.append() = new PathOpsThreadedRunnable(
+                            &testPathOpsRectsMain, a, b, c, d, &testRunner);
                 }
             }
             if (!reporter->allowExtendedTest()) goto finish;
diff --git a/src/third_party/skia/tests/PathOpsOpTest.cpp b/src/third_party/skia/tests/PathOpsOpTest.cpp
index 7ca15e1..6cc9dc7 100644
--- a/src/third_party/skia/tests/PathOpsOpTest.cpp
+++ b/src/third_party/skia/tests/PathOpsOpTest.cpp
@@ -7,7 +7,27 @@
 #include "PathOpsExtendedTest.h"
 #include "PathOpsTestCommon.h"
 
-#define TEST(name) { name, #name }
+class PathTest_Private {
+public:
+    PathTest_Private(SkPath* path)
+        : fPath(path) {}
+
+    void setPt(int index, SkScalar x, SkScalar y) {
+        fPath->setPt(index, x, y);
+    }
+
+    SkPath* fPath;
+};
+
+static void path_edit(const SkPoint& from, const SkPoint& to, SkPath* path) {
+    PathTest_Private testPath(path);
+    for (int index = 0; index < path->countPoints(); ++index) {
+        if (SkDPoint::ApproximatelyEqual(path->getPoint(index), from)) {
+            testPath.setPt(index, to.fX, to.fY);
+            return;
+        }
+    }
+}
 
 static void cubicOp1d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
@@ -19,7 +39,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,1, 1,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp2d(skiatest::Reporter* reporter, const char* filename) {
@@ -32,7 +52,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,1, 2,0, 1,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp3d(skiatest::Reporter* reporter, const char* filename) {
@@ -45,7 +65,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,1, 1,0, 3,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp5d(skiatest::Reporter* reporter, const char* filename) {
@@ -58,7 +78,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,2, 1,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp6d(skiatest::Reporter* reporter, const char* filename) {
@@ -71,7 +91,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,3, 1,0, 6,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp7d(skiatest::Reporter* reporter, const char* filename) {
@@ -84,7 +104,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,3, 1,0, 4,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp8d(skiatest::Reporter* reporter, const char* filename) {
@@ -97,7 +117,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,4, 1,0, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp9d(skiatest::Reporter* reporter, const char* filename) {
@@ -110,7 +130,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(1,2, 1,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void quadOp9d(skiatest::Reporter* reporter, const char* filename) {
@@ -125,7 +145,7 @@
     pathB.quadTo(1,2, 1.4f,1);
     pathB.quadTo(3,0.4f, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void lineOp9d(skiatest::Reporter* reporter, const char* filename) {
@@ -144,7 +164,7 @@
     pathB.lineTo(3,0.4f);
     pathB.lineTo(6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp1i(skiatest::Reporter* reporter, const char* filename) {
@@ -157,7 +177,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(1,2, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp10d(skiatest::Reporter* reporter, const char* filename) {
@@ -170,7 +190,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(1,4, 1,0, 3,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp11d(skiatest::Reporter* reporter, const char* filename) {
@@ -183,7 +203,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(1,5, 1,0, 4,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp12d(skiatest::Reporter* reporter, const char* filename) {
@@ -196,7 +216,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,1, 1,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp13d(skiatest::Reporter* reporter, const char* filename) {
@@ -209,7 +229,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(3,5, 1,0, 5,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp14d(skiatest::Reporter* reporter, const char* filename) {
@@ -222,7 +242,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(1,2, 1,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp15d(skiatest::Reporter* reporter, const char* filename) {
@@ -235,7 +255,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(1,2, 1,0, 6,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp16d(skiatest::Reporter* reporter, const char* filename) {
@@ -248,7 +268,7 @@
     pathB.moveTo(0,3);
     pathB.cubicTo(0,1, 2,0, 1,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp17d(skiatest::Reporter* reporter, const char* filename) {
@@ -261,7 +281,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(1,2, 2,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp18d(skiatest::Reporter* reporter, const char* filename) {
@@ -274,7 +294,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(1,2, 1,0, 5,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp19i(skiatest::Reporter* reporter, const char* filename) {
@@ -287,7 +307,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(2,6, 2,0, 1,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp20d(skiatest::Reporter* reporter, const char* filename) {
@@ -300,7 +320,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(1,2, 1,0, 1,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp21d(skiatest::Reporter* reporter, const char* filename) {
@@ -313,7 +333,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(5,6, 1,0, 1,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp22d(skiatest::Reporter* reporter, const char* filename) {
@@ -326,7 +346,7 @@
     pathB.moveTo(0,3);
     pathB.cubicTo(1,2, 1,0, 3,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp23d(skiatest::Reporter* reporter, const char* filename) {
@@ -339,7 +359,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(1,2, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp24d(skiatest::Reporter* reporter, const char* filename) {
@@ -352,63 +372,63 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(2,3, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testIntersect1(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(3, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kIntersect_PathOp, filename);
+    testPathOp(reporter, one, two, kIntersect_SkPathOp, filename);
 }
 
 static void testUnion1(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(3, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kUnion_PathOp, filename);
+    testPathOp(reporter, one, two, kUnion_SkPathOp, filename);
 }
 
 static void testDiff1(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(3, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kDifference_PathOp, filename);
+    testPathOp(reporter, one, two, kDifference_SkPathOp, filename);
 }
 
 static void testXor1(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(3, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kXOR_PathOp, filename);
+    testPathOp(reporter, one, two, kXOR_SkPathOp, filename);
 }
 
 static void testIntersect2(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(0, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kIntersect_PathOp, filename);
+    testPathOp(reporter, one, two, kIntersect_SkPathOp, filename);
 }
 
 static void testUnion2(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(0, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kUnion_PathOp, filename);
+    testPathOp(reporter, one, two, kUnion_SkPathOp, filename);
 }
 
 static void testDiff2(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(0, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kDifference_PathOp, filename);
+    testPathOp(reporter, one, two, kDifference_SkPathOp, filename);
 }
 
 static void testXor2(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(0, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kXOR_PathOp, filename);
+    testPathOp(reporter, one, two, kXOR_SkPathOp, filename);
 }
 
 static void testOp1d(skiatest::Reporter* reporter, const char* filename) {
@@ -419,7 +439,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp2d(skiatest::Reporter* reporter, const char* filename) {
@@ -430,7 +450,7 @@
     pathB.setFillType(SkPath::kEvenOdd_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp3d(skiatest::Reporter* reporter, const char* filename) {
@@ -441,7 +461,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp1u(skiatest::Reporter* reporter, const char* filename) {
@@ -452,7 +472,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void testOp4d(skiatest::Reporter* reporter, const char* filename) {
@@ -463,7 +483,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp5d(skiatest::Reporter* reporter, const char* filename) {
@@ -474,7 +494,7 @@
     pathB.setFillType(SkPath::kEvenOdd_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp6d(skiatest::Reporter* reporter, const char* filename) {
@@ -485,7 +505,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp7d(skiatest::Reporter* reporter, const char* filename) {
@@ -496,7 +516,7 @@
     pathB.setFillType(SkPath::kEvenOdd_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp2u(skiatest::Reporter* reporter, const char* filename) {
@@ -507,7 +527,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 3, 3, SkPath::kCW_Direction);
     pathB.addRect(1, 1, 2, 2, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void testOp8d(skiatest::Reporter* reporter, const char* filename) {
@@ -516,7 +536,7 @@
     pathB.moveTo(577330, 1971.72f);
     pathB.cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 static void cubicOp25i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
@@ -528,7 +548,7 @@
     pathB.moveTo(0,5);
     pathB.cubicTo(2,3, 1,0, 4,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp26d(skiatest::Reporter* reporter, const char* filename) {
@@ -541,7 +561,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(2,3, 1,0, 4,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp27d(skiatest::Reporter* reporter, const char* filename) {
@@ -554,7 +574,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(2,5, 1,0, 6,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp28u(skiatest::Reporter* reporter, const char* filename) {
@@ -567,7 +587,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(2,3, 1,0, 4,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp29d(skiatest::Reporter* reporter, const char* filename) {
@@ -580,7 +600,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(2,4, 1,0, 5,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp30d(skiatest::Reporter* reporter, const char* filename) {
@@ -593,7 +613,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(3,5, 1,0, 5,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp31d(skiatest::Reporter* reporter, const char* filename) {
@@ -606,7 +626,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(0,4, 2,0, 3,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp31u(skiatest::Reporter* reporter, const char* filename) {
@@ -619,7 +639,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(0,4, 2,0, 3,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp31x(skiatest::Reporter* reporter, const char* filename) {
@@ -632,7 +652,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(0,4, 2,0, 3,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kXOR_PathOp, filename);
+    testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename);
 }
 
 static void cubicOp32d(skiatest::Reporter* reporter, const char* filename) {
@@ -645,7 +665,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(1,3, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp33i(skiatest::Reporter* reporter, const char* filename) {
@@ -658,7 +678,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(1,3, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp34d(skiatest::Reporter* reporter, const char* filename) {
@@ -671,7 +691,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(1,3, 1,0, 5,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp35d(skiatest::Reporter* reporter, const char* filename) {
@@ -684,7 +704,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(0,4, 1,0, 5,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp36u(skiatest::Reporter* reporter, const char* filename) {
@@ -697,7 +717,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(1,5, 1,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp37d(skiatest::Reporter* reporter, const char* filename) {
@@ -710,14 +730,9 @@
     pathB.moveTo(1,6);
     pathB.cubicTo(3,4, 1,0, 6,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// this fails to detect a cubic/cubic intersection
-// the slight overlap is missed when the cubics are approximated by quadratics
-// and the subsequent line/cubic intersection also (correctly) misses the intersection
-// if the line/cubic was a matching line/approx.quadratic then the missing intersection
-// could have been detected
 static void cubicOp38d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
     path.setFillType(SkPath::kWinding_FillType);
@@ -728,7 +743,7 @@
     pathB.moveTo(2,3);
     pathB.cubicTo(1,4, 1,0, 6,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp39d(skiatest::Reporter* reporter, const char* filename) {
@@ -741,7 +756,7 @@
     pathB.moveTo(1,5);
     pathB.cubicTo(3,4, 1,0, 3,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp40d(skiatest::Reporter* reporter, const char* filename) {
@@ -754,7 +769,7 @@
     pathB.moveTo(2,3);
     pathB.cubicTo(2,4, 1,0, 5,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp41i(skiatest::Reporter* reporter, const char* filename) {
@@ -767,7 +782,7 @@
     pathB.moveTo(3,4);
     pathB.cubicTo(4,6, 1,0, 6,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp42d(skiatest::Reporter* reporter, const char* filename) {
@@ -780,7 +795,7 @@
     pathB.moveTo(5,6);
     pathB.cubicTo(4,5, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp43d(skiatest::Reporter* reporter, const char* filename) {
@@ -793,7 +808,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(1,3, 2,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp44d(skiatest::Reporter* reporter, const char* filename) {
@@ -806,7 +821,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(2,3, 2,0, 6,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp45d(skiatest::Reporter* reporter, const char* filename) {
@@ -819,7 +834,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(2,3, 2,0, 4,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp46d(skiatest::Reporter* reporter, const char* filename) {
@@ -832,7 +847,7 @@
     pathB.moveTo(0,5);
     pathB.cubicTo(2,4, 2,0, 5,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp47d(skiatest::Reporter* reporter, const char* filename) {
@@ -845,7 +860,7 @@
     pathB.moveTo(2,6);
     pathB.cubicTo(4,5, 1,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp48d(skiatest::Reporter* reporter, const char* filename) {
@@ -858,7 +873,7 @@
     pathB.moveTo(1,5);
     pathB.cubicTo(2,3, 2,0, 3,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp49d(skiatest::Reporter* reporter, const char* filename) {
@@ -871,7 +886,7 @@
     pathB.moveTo(2,3);
     pathB.cubicTo(1,4, 2,0, 5,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp50d(skiatest::Reporter* reporter, const char* filename) {
@@ -884,7 +899,7 @@
     pathB.moveTo(0,5);
     pathB.cubicTo(1,5, 3,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp51d(skiatest::Reporter* reporter, const char* filename) {
@@ -897,7 +912,7 @@
     pathB.moveTo(1,4);
     pathB.cubicTo(0,6, 3,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp52d(skiatest::Reporter* reporter, const char* filename) {
@@ -910,7 +925,7 @@
     pathB.moveTo(4,5);
     pathB.cubicTo(3,4, 2,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp53d(skiatest::Reporter* reporter, const char* filename) {
@@ -923,7 +938,7 @@
     pathB.moveTo(3,5);
     pathB.cubicTo(1,2, 3,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp54d(skiatest::Reporter* reporter, const char* filename) {
@@ -936,7 +951,7 @@
     pathB.moveTo(4,5);
     pathB.cubicTo(2,4, 4,0, 3,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp55d(skiatest::Reporter* reporter, const char* filename) {
@@ -949,7 +964,7 @@
     pathB.moveTo(2,3);
     pathB.cubicTo(0,5, 5,0, 3,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp56d(skiatest::Reporter* reporter, const char* filename) {
@@ -962,7 +977,7 @@
     pathB.moveTo(0,5);
     pathB.cubicTo(1,2, 1,0, 6,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp57d(skiatest::Reporter* reporter, const char* filename) {
@@ -975,7 +990,7 @@
     pathB.moveTo(4,5);
     pathB.cubicTo(4,6, 5,0, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp58d(skiatest::Reporter* reporter, const char* filename) {
@@ -988,7 +1003,7 @@
     pathB.moveTo(5,6);
     pathB.cubicTo(3,5, 5,0, 4,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp59d(skiatest::Reporter* reporter, const char* filename) {
@@ -1001,7 +1016,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(1,4, 1,0, 6,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp60d(skiatest::Reporter* reporter, const char* filename) {
@@ -1014,7 +1029,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(2,5, 2,0, 6,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp61d(skiatest::Reporter* reporter, const char* filename) {
@@ -1027,7 +1042,7 @@
     pathB.moveTo(2,3);
     pathB.cubicTo(1,6, 2,1, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp62d(skiatest::Reporter* reporter, const char* filename) {
@@ -1040,7 +1055,7 @@
     pathB.moveTo(3,5);
     pathB.cubicTo(4,5, 3,1, 6,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp63d(skiatest::Reporter* reporter, const char* filename) {
@@ -1053,7 +1068,7 @@
     pathB.moveTo(2,3);
     pathB.cubicTo(3,5, 3,2, 4,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp64d(skiatest::Reporter* reporter, const char* filename) {
@@ -1066,7 +1081,7 @@
     pathB.cubicTo(0,3, 1,0, 1,0);
     pathB.lineTo(0,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp65d(skiatest::Reporter* reporter, const char* filename) {
@@ -1079,7 +1094,7 @@
     pathB.cubicTo(0,1, 1,0, 5,1);
     pathB.lineTo(0,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void rectOp1d(skiatest::Reporter* reporter, const char* filename) {
@@ -1092,7 +1107,7 @@
     pathB.cubicTo(0,3, 1,0, 1,0);
     pathB.lineTo(0,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp66u(skiatest::Reporter* reporter, const char* filename) {
@@ -1105,7 +1120,7 @@
     pathB.moveTo(2,4);
     pathB.cubicTo(3,5, 1,0, 6,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp67u(skiatest::Reporter* reporter, const char* filename) {
@@ -1118,7 +1133,7 @@
     pathB.cubicTo(1,3, 5,3, 6,1);
     pathB.lineTo(0,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp68u(skiatest::Reporter* reporter, const char* filename) {
@@ -1129,7 +1144,7 @@
     pathB.moveTo(1,4);
     pathB.cubicTo(0,5, 5,0, 5,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp69d(skiatest::Reporter* reporter, const char* filename) {
@@ -1140,15 +1155,15 @@
     pathB.moveTo(1,3);
     pathB.cubicTo(0,2, 3,1, 1,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 SkPathOp ops[] = {
-    kUnion_PathOp,
-    kXOR_PathOp,
-    kReverseDifference_PathOp,
-    kXOR_PathOp,
-    kReverseDifference_PathOp,
+    kUnion_SkPathOp,
+    kXOR_SkPathOp,
+    kReverseDifference_SkPathOp,
+    kXOR_SkPathOp,
+    kReverseDifference_SkPathOp,
 };
 
 static void rRect1(skiatest::Reporter* reporter, const char* filename) {
@@ -1181,7 +1196,7 @@
     path.setFillType(SkPath::kInverseEvenOdd_FillType);
     for (int index = 0; index < 5; ++index) {
         testPathOp(reporter, path, paths[index], ops[index], filename);
-        Op(path, paths[index], ops[index], &path);
+        REPORTER_ASSERT(reporter, Op(path, paths[index], ops[index], &path));
     }
 }
 
@@ -1216,7 +1231,7 @@
     pathB.lineTo(246,4);
     pathB.lineTo(189,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skp2(skiatest::Reporter* reporter, const char* filename) {
@@ -1236,7 +1251,7 @@
     pathB.lineTo(823.000000f, 1028.00000f);
     pathB.lineTo(258.000000f, 1028.00000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skp3(skiatest::Reporter* reporter, const char* filename) {
@@ -1268,7 +1283,7 @@
     pathB.lineTo(973.000000f, 510.000000f);
     pathB.lineTo(717.000000f, 510.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skp4(skiatest::Reporter* reporter, const char* filename) {
@@ -1300,7 +1315,7 @@
     pathB.lineTo(306.000000f, 617.000000f);
     pathB.lineTo(306.000000f, 590.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skp5(skiatest::Reporter* reporter, const char* filename) {
@@ -1326,7 +1341,7 @@
     pathB.lineTo(10.0000000f, 234.000000f);
     pathB.cubicTo(10.0000000f, 229.581726f, 13.5817204f, 226.000000f, 18.0000000f, 226.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp70d(skiatest::Reporter* reporter, const char* filename) {
@@ -1339,7 +1354,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(0,5, 1,0, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp71d(skiatest::Reporter* reporter, const char* filename) {
@@ -1352,7 +1367,7 @@
     pathB.moveTo(1,4);
     pathB.cubicTo(4,6, 1,0, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp72i(skiatest::Reporter* reporter, const char* filename) {
@@ -1365,7 +1380,7 @@
     pathB.moveTo(2,5);
     pathB.cubicTo(4,5, 1,0, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp73d(skiatest::Reporter* reporter, const char* filename) {
@@ -1380,7 +1395,7 @@
     pathB.cubicTo(4,6, 1,0, 4,3);
     pathB.lineTo(0,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp74d(skiatest::Reporter* reporter, const char* filename) {
@@ -1395,7 +1410,7 @@
     pathB.cubicTo(1,5, 1,0, 5,1);
     pathB.lineTo(1,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp75d(skiatest::Reporter* reporter, const char* filename) {
@@ -1410,7 +1425,7 @@
     pathB.cubicTo(4,6, 1,0, 4,0);
     pathB.lineTo(1,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp76u(skiatest::Reporter* reporter, const char* filename) {
@@ -1423,7 +1438,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(3,5, 1,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp77i(skiatest::Reporter* reporter, const char* filename) {
@@ -1438,7 +1453,7 @@
     pathB.cubicTo(2,3, 1,0, 3,1);
     pathB.lineTo(0,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp78u(skiatest::Reporter* reporter, const char* filename) {
@@ -1453,7 +1468,7 @@
     pathB.cubicTo(1,6, 6,1, 6,1);
     pathB.lineTo(0,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp79u(skiatest::Reporter* reporter, const char* filename) {
@@ -1466,7 +1481,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(4,6, 1,0, 3,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp80i(skiatest::Reporter* reporter, const char* filename) {
@@ -1481,7 +1496,7 @@
     pathB.cubicTo(3,4, 1,0, 3,2);
     pathB.lineTo(1,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp81d(skiatest::Reporter* reporter, const char* filename) {
@@ -1494,7 +1509,7 @@
     pathB.moveTo(3,4);
     pathB.cubicTo(4,5, 1,0, 6,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp82i(skiatest::Reporter* reporter, const char* filename) {
@@ -1509,7 +1524,7 @@
     pathB.cubicTo(0,3, 1,0, 3,2);
     pathB.lineTo(2,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp83i(skiatest::Reporter* reporter, const char* filename) {
@@ -1524,7 +1539,7 @@
     pathB.cubicTo(1,4, 1,0, 3,0);
     pathB.lineTo(1,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp84d(skiatest::Reporter* reporter, const char* filename) {
@@ -1537,7 +1552,7 @@
     pathB.moveTo(3,6);
     pathB.cubicTo(2,3, 4,0, 3,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void skpClip1(skiatest::Reporter* reporter, const char* filename) {
@@ -1567,7 +1582,7 @@
     pathB.lineTo(1247.00000f, 907.000000f);
     pathB.lineTo(1247.00000f, 876.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpClip2(skiatest::Reporter* reporter, const char* filename) {
@@ -1593,7 +1608,7 @@
     pathB.lineTo(130.000000f, 11416.0000f);
     pathB.cubicTo(130.000000f, 11415.4473f, 130.895432f, 11415.0000f, 132.000000f, 11415.0000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skp96prezzi1(skiatest::Reporter* reporter, const char* filename) {
@@ -1623,7 +1638,7 @@
     pathB.lineTo(253.000000f, 669.000000f);
     pathB.lineTo(156.000000f, 669.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpancestry_com1(skiatest::Reporter* reporter, const char* filename) {
@@ -1649,7 +1664,7 @@
     pathB.lineTo(157.000000f, 930.000000f);
     pathB.cubicTo(157.000000f, 927.790833f, 158.790863f, 926.000000f, 161.000000f, 926.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpeldorado_com_ua1(skiatest::Reporter* reporter, const char* filename) {
@@ -1673,7 +1688,7 @@
     pathB.cubicTo(283.840179f, 304.431458f, 300.126587f, 291.000000f, 316.695129f, 291.000000f);
     pathB.lineTo(1006.69513f, 291.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbyte_com1(skiatest::Reporter* reporter, const char* filename) {
@@ -1701,7 +1716,7 @@
     pathB.lineTo(963.000000f, 19.0000000f);
     pathB.cubicTo(963.000000f, 16.2385750f, 965.238586f, 14.0000000f, 968.000000f, 14.0000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skphealth_com76(skiatest::Reporter* reporter, const char* filename) {
@@ -1719,17 +1734,10 @@
     SkPath pathB;
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.moveTo(704.000000f, 3.00000000f);
-#if 0
-    pathB.lineTo(719.500000f, 3.00000000f);
-    pathB.lineTo(705.000000f, 33.0000000f);
-    pathB.lineTo(704.000000f, 33.0000000f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-#else
     pathB.lineTo(704.000000f, 33.0000000f);
     pathB.lineTo(705.000000f, 33.0000000f);
     pathB.lineTo(719.500000f, 3.00000000f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-#endif
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpahrefs_com88(skiatest::Reporter* reporter, const char* filename) {
@@ -1755,7 +1763,7 @@
     pathB.lineTo(1088.00000f, 6.00000000f);
     pathB.lineTo(1088.00000f, 19.0000000f);
     pathB.lineTo(1101.00000f, 32.0000000f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpahrefs_com29(skiatest::Reporter* reporter, const char* filename) {
@@ -1785,7 +1793,7 @@
     pathB.lineTo(1049.00000f, 19.0000000f);
     pathB.lineTo(1073.00000f, 31.0000000f);
     pathB.lineTo(1074.00000f, 32.0000000f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp85d(skiatest::Reporter* reporter, const char* filename) {
@@ -1799,12 +1807,9 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(2,6, 1,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// this fails because the pair of nearly coincident cubics intersect at the ends
-// but the line connected to one of the cubics at the same point does not intersect
-// the other
 static void skpkkiste_to98(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1830,7 +1835,7 @@
     pathB.cubicTo(91, 124.238579f, 93.2385788f, 122, 96, 122);
     pathB.lineTo(258, 122);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void issue1417(skiatest::Reporter* reporter, const char* filename) {
@@ -1941,8 +1946,8 @@
     path2.lineTo(113.232177734375f, 173.5789947509765625f);
     path2.lineTo(113.232177734375f, 173.5789947509765625f);
     path2.close();
-
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    // FIXME : difficult data, circle back later
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void issue1418(skiatest::Reporter* reporter, const char* filename) {
@@ -1968,7 +1973,7 @@
     path2.quadTo(0.79289329051971435547f, 0.50000005960464477539f, 0.64644664525985717773f, 0.35355341434478759766f);
     path2.quadTo(0.50000005960464477539f, 0.20710679888725280762f, 0.50000005960464477539f, 0);
     path2.quadTo(0.50000005960464477539f, -0.20710679888725280762f, 0.64644664525985717773f, -0.35355341434478759766f);
-    testPathOp(reporter, path1, path2, kIntersect_PathOp, filename);
+    testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp85i(skiatest::Reporter* reporter, const char* filename) {
@@ -1981,7 +1986,7 @@
     pathB.moveTo(3, 4);
     pathB.cubicTo(4, 6, 4, 3, 5, 1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void issue1418b(skiatest::Reporter* reporter, const char* filename) {
@@ -2011,7 +2016,7 @@
     path2.quadTo(0.792893291f, 0.50000006f, 1.00000012f, 0.50000006f);
     path2.close();
     path2.setFillType(SkPath::kEvenOdd_FillType);
-    testPathOp(reporter, path1, path2, kIntersect_PathOp, filename);
+    testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
 }
 
 static void rectOp1i(skiatest::Reporter* reporter, const char* filename) {
@@ -2022,7 +2027,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void rectOp2i(skiatest::Reporter* reporter, const char* filename) {
@@ -2033,7 +2038,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void rectOp3x(skiatest::Reporter* reporter, const char* filename) {
@@ -2060,12 +2065,9 @@
     pathB.lineTo(3, 3);
     pathB.lineTo(2, 3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kXOR_PathOp, filename);
+    testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename);
 }
 
-// this fails to generate two interior line segments 
-// an earlier pathops succeeded, but still failed to generate one interior line segment
-// (but was saved by assemble, which works around a single line missing segment)
 static void issue1435(skiatest::Reporter* reporter, const char* filename) {
     SkPath path1;
     path1.moveTo(160, 60);
@@ -2114,7 +2116,7 @@
     path2.lineTo(195.830978f, 161.521133f);
     path2.close();
     path2.setFillType(SkPath::kEvenOdd_FillType);
-    testPathOp(reporter, path1, path2, kIntersect_PathOp, filename);
+    testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
 }
 
 static void skpkkiste_to716(skiatest::Reporter* reporter, const char* filename) {
@@ -2138,7 +2140,7 @@
     pathB.cubicTo(1173, 124.238579f, 1175.23853f, 122, 1178, 122);
     pathB.lineTo(1340, 122);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void loopEdge1(skiatest::Reporter* reporter, const char* filename) {
@@ -2160,7 +2162,7 @@
     pathB.lineTo(2,4);
     pathB.lineTo(1,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void loopEdge2(skiatest::Reporter* reporter, const char* filename) {
@@ -2182,7 +2184,7 @@
     pathB.lineTo(2 - 1e-6f,4);
     pathB.lineTo(1 - 1e-6f,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp86i(skiatest::Reporter* reporter, const char* filename) {
@@ -2195,7 +2197,7 @@
     pathB.moveTo(2, 6);
     pathB.cubicTo(2, 5, 4, 0, 4, 3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp87u(skiatest::Reporter* reporter, const char* filename) {
@@ -2208,7 +2210,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(4,6, 1,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp88u(skiatest::Reporter* reporter, const char* filename) {
@@ -2221,7 +2223,7 @@
     pathB.moveTo(0,5);
     pathB.cubicTo(4,6, 1,0, 5,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp89u(skiatest::Reporter* reporter, const char* filename) {
@@ -2234,7 +2236,7 @@
     pathB.moveTo(0, 5);
     pathB.cubicTo(3, 6, 3, 0, 6, 1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp90u(skiatest::Reporter* reporter, const char* filename) {
@@ -2247,7 +2249,7 @@
     pathB.moveTo(2, 5);
     pathB.cubicTo(1, 4, 5, 0, 2, 1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp91u(skiatest::Reporter* reporter, const char* filename) {
@@ -2260,10 +2262,10 @@
     pathB.moveTo(3, 6);
     pathB.cubicTo(0, 5, 6, 1, 3, 0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
-static void skpaaalgarve_org53(skiatest::Reporter* reporter, const char* filename) {  //  add t cancel
+static void skpaaalgarve_org53(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
    path.moveTo(-1.24344979e-014f, 348);
@@ -2281,10 +2283,10 @@
     pathB.lineTo(258, 348);
     pathB.lineTo(0, 348);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpabcspark_ca103(skiatest::Reporter* reporter, const char* filename) {  //  add t cancel
+static void skpabcspark_ca103(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(1.99840144e-015f, 494);
@@ -2304,10 +2306,10 @@
     pathB.lineTo(105, 494);
     pathB.lineTo(0, 494);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpacesoftech_com47(skiatest::Reporter* reporter, const char* filename) {  // partial coincidence
+static void skpacesoftech_com47(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(670.537415f, 285);
@@ -2330,10 +2332,10 @@
     pathB.quadTo(691.660889f, 337.416199f, 685.173523f, 352.661896f);
     pathB.quadTo(678.686157f, 367.907562f, 663.318542f, 374.100616f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpact_com43(skiatest::Reporter* reporter, const char* filename) {  // bridge op
+static void skpact_com43(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(1.45716772e-016f, 924.336121f);
@@ -2355,10 +2357,10 @@
     pathB.lineTo(0, 920);
     pathB.lineTo(3, 927);
     pathB.lineTo(-1, 927);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpadbox_lt8(skiatest::Reporter* reporter, const char* filename) {  // zero span
+static void skpadbox_lt8(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(320.097229f, 628.573669f);
@@ -2379,10 +2381,10 @@
     pathB.lineTo(334.366943f, 625.145508f);
     pathB.cubicTo(333.773315f, 624.828247f, 333.549286f, 624.089783f, 333.866608f, 623.496155f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpadindex_de4(skiatest::Reporter* reporter, const char* filename) {  // find chase op
+static void skpadindex_de4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(0, 926);
@@ -2398,10 +2400,10 @@
     pathB.lineTo(49, 178);
     pathB.lineTo(49, 312);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter, const char* filename) { // calc common
+static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(205.605804f, 142.334625f);
@@ -2422,10 +2424,10 @@
     pathB.cubicTo(243.829437f, 98.1356659f, 267.195221f, 96.4417267f, 282.651581f, 109.808517f);
     pathB.lineTo(283.407959f, 110.462646f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpadspert_de11(skiatest::Reporter* reporter, const char* filename) {  // mark and chase winding
+static void skpadspert_de11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(-4.4408921e-016f, 682.5f);
@@ -2443,10 +2445,10 @@
     pathB.lineTo(35, 683);
     pathB.lineTo(0, 683);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpaiaigames_com870(skiatest::Reporter* reporter, const char* filename) {  // cubic/cubic intersect
+static void skpaiaigames_com870(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(324.071075f, 845.071045f);
@@ -2473,7 +2475,7 @@
     pathB.cubicTo(145, 715.477173f, 149.477158f, 711, 155, 711);
     pathB.lineTo(317, 711);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp92i(skiatest::Reporter* reporter, const char* filename) {
@@ -2486,7 +2488,7 @@
     pathB.moveTo(1, 4);
     pathB.cubicTo(4, 5, 1, 0, 6, 2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp93d(skiatest::Reporter* reporter, const char* filename) {
@@ -2499,7 +2501,7 @@
     pathB.moveTo(1, 4);
     pathB.cubicTo(3, 4, 1, 0, 6, 1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp94u(skiatest::Reporter* reporter, const char* filename) {
@@ -2512,7 +2514,7 @@
     pathB.moveTo(0, 5);
     pathB.cubicTo(3, 5, 3, 0, 3, 2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void skpadbox_lt15(skiatest::Reporter* reporter, const char* filename) {
@@ -2536,7 +2538,7 @@
     pathB.cubicTo(333.773315f, 624.828247f, 333.549286f, 624.089783f, 333.866608f, 623.496155f);
     pathB.lineTo(613.368042f, 100.585754f);
      pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpadoption_org196(skiatest::Reporter* reporter, const char* filename) {
@@ -2561,7 +2563,7 @@
     pathB.cubicTo(805.238586f, 375, 803, 372.761414f, 803, 370);
     pathB.lineTo(803, 326);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpadspert_net23(skiatest::Reporter* reporter, const char* filename) {
@@ -2588,7 +2590,7 @@
     pathB.lineTo(35, 683);
     pathB.lineTo(0, 683);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpadventistmission_org572(skiatest::Reporter* reporter, const char* filename) {
@@ -2607,7 +2609,7 @@
     pathB.lineTo(1182, 926);
     pathB.lineTo(934, 926);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpagentxsites_com55(skiatest::Reporter* reporter, const char* filename) {
@@ -2635,7 +2637,7 @@
     pathB.cubicTo(924, 27.8954315f, 924.895447f, 27, 926, 27);
     pathB.lineTo(1103, 27);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbakosoft_com10(skiatest::Reporter* reporter, const char* filename) {
@@ -2661,7 +2663,7 @@
     pathB.quadTo(198.284271f, 170, 204.142136f, 175.857864f);
     pathB.quadTo(210, 181.715729f, 210, 190);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbambootheme_com12(skiatest::Reporter* reporter, const char* filename) {
@@ -2685,7 +2687,7 @@
     pathB.lineTo(-51, 47);
     pathB.cubicTo(-51, 19.3857498f, -28.6142502f, -3, -1, -3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpakmmos_ru100(skiatest::Reporter* reporter, const char* filename) {
@@ -2704,7 +2706,7 @@
     pathB.lineTo(693, 926);
     pathB.lineTo(575, 926);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpcarpetplanet_ru22(skiatest::Reporter* reporter, const char* filename) {
@@ -2728,14 +2730,10 @@
     pathB.cubicTo(67, 842.307556f, 123.85984f, 785, 194, 785);
     pathB.lineTo(195, 785);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// this fails because cubic/quad misses an intersection (failure is isolated in c/q int test)
 static void skpcarrot_is24(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;
-    }
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(945, 597);
@@ -2758,7 +2756,7 @@
     pathB.cubicTo(1019.77502f, 679.955017f, 1020.08099f, 676.094971f, 1020.08099f, 672.161987f);
     pathB.cubicTo(1020.08002f, 630.73999f, 986.502014f, 597.161987f, 945.080994f, 597.161987f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbangalorenest_com4(skiatest::Reporter* reporter, const char* filename) {
@@ -2777,7 +2775,7 @@
     pathB.lineTo(30, 146);
     pathB.lineTo(30, 290);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbenzoteh_ru152(skiatest::Reporter* reporter, const char* filename) {
@@ -2804,7 +2802,7 @@
     pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);
     pathB.quadTo(883, 25.0710678f, 883, 23);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbestred_ru37(skiatest::Reporter* reporter, const char* filename) {
@@ -2831,7 +2829,7 @@
     pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);
     pathB.quadTo(883, 25.0710678f, 883, 23);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbingoentertainment_net189(skiatest::Reporter* reporter, const char* filename) {
@@ -2855,7 +2853,7 @@
     pathB.cubicTo(922.567993f, 755.399414f, 920.880615f, 748.474304f, 918.799133f, 748.216003f);
     pathB.lineTo(899.200928f, 745.783997f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpcarrefour_ro62(skiatest::Reporter* reporter, const char* filename) {
@@ -2878,7 +2876,7 @@
     pathB.cubicTo(402.686279f, 666, 400, 663.313721f, 400, 660);
     pathB.lineTo(400, 453);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpcaffelavazzait_com_ua21(skiatest::Reporter* reporter, const char* filename) {
@@ -2905,7 +2903,7 @@
     pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);
     pathB.quadTo(883, 25.0710678f, 883, 23);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpcamcorder_kz21(skiatest::Reporter* reporter, const char* filename) {
@@ -2932,7 +2930,7 @@
     pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);
     pathB.quadTo(883, 25.0710678f, 883, 23);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpcavablar_net563(skiatest::Reporter* reporter, const char* filename) {
@@ -2951,7 +2949,7 @@
     pathB.lineTo(160, 918);
     pathB.lineTo(91, 918);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpinsomnia_gr72(skiatest::Reporter* reporter, const char* filename) {
@@ -2970,7 +2968,7 @@
     pathB.lineTo(1138, 231);
     pathB.lineTo(633, 6101);
     pathB.lineTo(1139, 6607);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp95u(skiatest::Reporter* reporter, const char* filename) {
@@ -2983,7 +2981,7 @@
     pathB.moveTo(1, 5);
     pathB.cubicTo(2, 3, 2, 0, 3, 2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp96d(skiatest::Reporter* reporter, const char* filename) {
@@ -2996,7 +2994,7 @@
     pathB.moveTo(3, 6);
     pathB.cubicTo(0, 5, 6, 1, 3, 0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp97x(skiatest::Reporter* reporter, const char* filename) {
@@ -3009,7 +3007,7 @@
     pathB.moveTo(1, 2);
     pathB.cubicTo(1, 2, 2, 0, 6, 0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kXOR_PathOp, filename);
+    testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename);
 }
 
 static void cubicOp98x(skiatest::Reporter* reporter, const char* filename) {
@@ -3022,7 +3020,7 @@
     pathB.moveTo(1, 4);
     pathB.cubicTo(3, 6, 3, 0, 6, 3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kXOR_PathOp, filename);
+    testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename);
 }
 
 static void cubicOp99(skiatest::Reporter* reporter, const char* filename) {
@@ -3035,7 +3033,7 @@
     pathB.moveTo(5,6);
     pathB.cubicTo(4,5, 6,3, 3,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp100(skiatest::Reporter* reporter, const char* filename) {
@@ -3048,7 +3046,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(2,4, 1,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp101(skiatest::Reporter* reporter, const char* filename) {
@@ -3061,7 +3059,7 @@
     pathB.moveTo(1, 2);
     pathB.cubicTo(3, 5, 1, 0, 3, 2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp102(skiatest::Reporter* reporter, const char* filename) {
@@ -3074,7 +3072,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,3, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp103(skiatest::Reporter* reporter, const char* filename) {
@@ -3087,7 +3085,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(1,2, 1,0, 5,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp104(skiatest::Reporter* reporter, const char* filename) {
@@ -3100,7 +3098,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(1,6, 1,0, 6,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp105(skiatest::Reporter* reporter, const char* filename) {
@@ -3113,7 +3111,7 @@
     pathB.moveTo(5,6);
     pathB.cubicTo(0,2, 1,0, 4,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp106(skiatest::Reporter* reporter, const char* filename) {
@@ -3126,7 +3124,7 @@
     pathB.moveTo(1, 2);
     pathB.cubicTo(0, 2, 1, 0, 6, 4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp107(skiatest::Reporter* reporter, const char* filename) {
@@ -3139,7 +3137,7 @@
     pathB.moveTo(1, 2);
     pathB.cubicTo(0, 2, 1, 0, 6, 4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp108(skiatest::Reporter* reporter, const char* filename) {
@@ -3152,7 +3150,7 @@
     pathB.moveTo(1, 2);
     pathB.cubicTo(0, 2, 1, 0, 6, 4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp109(skiatest::Reporter* reporter, const char* filename) {
@@ -3165,7 +3163,7 @@
     pathB.moveTo(3,6);
     pathB.cubicTo(4,5, 1,0, 5,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp110(skiatest::Reporter* reporter, const char* filename) {
@@ -3176,7 +3174,7 @@
     pathB.setFillType(SkPath::kEvenOdd_FillType);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp111(skiatest::Reporter* reporter, const char* filename) {
@@ -3189,7 +3187,7 @@
     pathB.moveTo(1,4);
     pathB.cubicTo(1,3, 4,1, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void xOp1u(skiatest::Reporter* reporter, const char* filename) {
@@ -3202,7 +3200,7 @@
     pathB.moveTo(2, 3);
     pathB.cubicTo(3, 6, 4, 1, 5, 4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void xOp1i(skiatest::Reporter* reporter, const char* filename) {
@@ -3215,7 +3213,7 @@
     pathB.moveTo(0, 6);
     pathB.cubicTo(1, 5, 4, 1, 5, 1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void xOp2i(skiatest::Reporter* reporter, const char* filename) {
@@ -3228,7 +3226,7 @@
     pathB.moveTo(2, 3);
     pathB.cubicTo(1, 6, 5, 1, 4, 0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void xOp3i(skiatest::Reporter* reporter, const char* filename) {
@@ -3241,7 +3239,7 @@
     pathB.moveTo(1,4);
     pathB.cubicTo(1,3, 4,1, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void findFirst1(skiatest::Reporter* reporter, const char* filename) {
@@ -3254,10 +3252,9 @@
     pathB.moveTo(0,5);
     pathB.cubicTo(1,2, 1,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// triggers addSimpleAngle with non-zero argument
 static void cubicOp112(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
     path.setFillType(SkPath::kWinding_FillType);
@@ -3268,7 +3265,7 @@
     pathB.moveTo(4,6);
     pathB.cubicTo(0,1, 4,2, 3,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp113(skiatest::Reporter* reporter, const char* filename) {
@@ -3279,13 +3276,10 @@
     pathB.moveTo(3,5);
     pathB.cubicTo(2.33333325f,4.33333349f, 3.83333325f,3.83333349f, 2,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp114(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;
-    }
     SkPath path, pathB;
     path.setFillType(SkPath::kWinding_FillType);
     path.moveTo(0, 1);
@@ -3295,7 +3289,7 @@
     pathB.moveTo(1, 3);
     pathB.cubicTo(-1, 2, 3.5f, 1.33333337f, 0, 1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp114asQuad(skiatest::Reporter* reporter, const char* filename) {
@@ -3311,7 +3305,7 @@
     SkPath qPath, qPathB;
     CubicPathToQuads(path, &qPath);
     CubicPathToQuads(pathB, &qPathB);
-    testPathOp(reporter, qPath, qPathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, qPath, qPathB, kIntersect_SkPathOp, filename);
 }
 
 static void quadOp10i(skiatest::Reporter* reporter, const char* filename) {
@@ -3323,7 +3317,7 @@
     pathB.moveTo(0, 0);
     pathB.quadTo(8, 1, 4, 8);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void kari1(skiatest::Reporter* reporter, const char* filename) {
@@ -3342,7 +3336,7 @@
     path2.cubicTo(38.0664443969727f, -0.154893040657043f, 37.1809883117676f, -1.18359375f, 37.52734375, -1.44140625f);
     path2.close();
 
-    testPathOp(reporter, path1, path2, kDifference_PathOp, filename);
+    testPathOp(reporter, path1, path2, kDifference_SkPathOp, filename);
 }
 
 static void issue2504(skiatest::Reporter* reporter, const char* filename) {
@@ -3361,7 +3355,7 @@
                   34.53484344482421875, -5.6777553558349609375,
                   34.53484344482421875, -5.6777553558349609375);
     path2.close();
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void issue2540(skiatest::Reporter* reporter, const char* filename) {
@@ -3381,7 +3375,7 @@
     path2.cubicTo(-16.06999969482421875, 124.66899871826171875, 1.2680000066757202148437500, 91.23999786376953125, 37.264003753662109375, 95.35400390625);
     path2.cubicTo(37.264003753662109375, 95.35400390625, 11.3710002899169921875, 83.7339935302734375, -25.077999114990234375, 124.9120025634765625);
     path2.close();
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void rects1(skiatest::Reporter* reporter, const char* filename) {
@@ -3408,7 +3402,7 @@
     pathB.lineTo(2, 2);
     pathB.lineTo(0, 2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void rects2(skiatest::Reporter* reporter, const char* filename) {
@@ -3435,7 +3429,7 @@
     pathB.lineTo(4, 4);
     pathB.lineTo(3, 4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void rects3(skiatest::Reporter* reporter, const char* filename) {
@@ -3446,7 +3440,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void rects4(skiatest::Reporter* reporter, const char* filename) {
@@ -3457,13 +3451,10 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 3, 3, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void issue2753(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) {
-        return;
-    }
     SkPath path1;
     path1.moveTo(142.701f, 110.568f);
     path1.lineTo(142.957f, 100);
@@ -3480,255 +3471,83 @@
     path2.cubicTo(188.201f, 117.601f, 174.801f, 93, 39, 124.001f);
     path2.close();
 
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void issue2808(skiatest::Reporter* reporter, const char* filename) {
     SkPath path1, path2;
 
-	path1.moveTo(509.20300293f, 385.601989746f);
-	path1.quadTo(509.20300293f, 415.68838501f, 487.928710938f, 436.96270752f);
-	path1.quadTo(466.654388428f, 458.236999512f, 436.567993164f, 458.236999512f);
-	path1.quadTo(406.4815979f, 458.236999512f, 385.207275391f, 436.96270752f);
-	path1.quadTo(363.932983398f, 415.68838501f, 363.932983398f, 385.601989746f);
-	path1.quadTo(363.932983398f, 355.515594482f, 385.207275391f, 334.241271973f);
-	path1.quadTo(406.4815979f, 312.96697998f, 436.567993164f, 312.96697998f);
-	path1.quadTo(466.654388428f, 312.96697998f, 487.928710938f, 334.241271973f);
-	path1.quadTo(509.20300293f, 355.515594482f, 509.20300293f, 385.601989746f);
-	path1.close();
+    path1.moveTo(509.20300293f, 385.601989746f);
+    path1.quadTo(509.20300293f, 415.68838501f, 487.928710938f, 436.96270752f);
+    path1.quadTo(466.654388428f, 458.236999512f, 436.567993164f, 458.236999512f);
+    path1.quadTo(406.4815979f, 458.236999512f, 385.207275391f, 436.96270752f);
+    path1.quadTo(363.932983398f, 415.68838501f, 363.932983398f, 385.601989746f);
+    path1.quadTo(363.932983398f, 355.515594482f, 385.207275391f, 334.241271973f);
+    path1.quadTo(406.4815979f, 312.96697998f, 436.567993164f, 312.96697998f);
+    path1.quadTo(466.654388428f, 312.96697998f, 487.928710938f, 334.241271973f);
+    path1.quadTo(509.20300293f, 355.515594482f, 509.20300293f, 385.601989746f);
+    path1.close();
 
-	path2.moveTo(449.033996582f, 290.87298584f);
-	path2.quadTo(449.033996582f, 301.028259277f, 441.853149414f, 308.209106445f);
-	path2.quadTo(434.672271729f, 315.389984131f, 424.516998291f, 315.389984131f);
-	path2.quadTo(414.361724854f, 315.389984131f, 407.180847168f, 308.209106445f);
-	path2.quadTo(400, 301.028259277f, 400, 290.87298584f);
-	path2.quadTo(400, 280.717712402f, 407.180847168f, 273.536865234f);
-	path2.quadTo(414.361724854f, 266.355987549f, 424.516998291f, 266.355987549f);
-	path2.quadTo(434.672271729f, 266.355987549f, 441.853149414f, 273.536865234f);
-	path2.quadTo(449.033996582f, 280.717712402f, 449.033996582f, 290.87298584f);
-	path2.close();
+    path2.moveTo(449.033996582f, 290.87298584f);
+    path2.quadTo(449.033996582f, 301.028259277f, 441.853149414f, 308.209106445f);
+    path2.quadTo(434.672271729f, 315.389984131f, 424.516998291f, 315.389984131f);
+    path2.quadTo(414.361724854f, 315.389984131f, 407.180847168f, 308.209106445f);
+    path2.quadTo(400, 301.028259277f, 400, 290.87298584f);
+    path2.quadTo(400, 280.717712402f, 407.180847168f, 273.536865234f);
+    path2.quadTo(414.361724854f, 266.355987549f, 424.516998291f, 266.355987549f);
+    path2.quadTo(434.672271729f, 266.355987549f, 441.853149414f, 273.536865234f);
+    path2.quadTo(449.033996582f, 280.717712402f, 449.033996582f, 290.87298584f);
+    path2.close();
 
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
-static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
-static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
+static void cubicOp115(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(3,4, 2,1, 5,3);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1,2);
+    pathB.cubicTo(3,5, 1,0, 4,3);
+    pathB.close();
+    SkPath path2(path);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
 
-static struct TestDesc tests[] = {
-    TEST(issue2753),  // FIXME: pair of cubics miss intersection
-    TEST(cubicOp114),  // FIXME: curve with inflection is ordered the wrong way
-    TEST(issue2808),
-    TEST(cubicOp114asQuad),
-    TEST(rects4),
-    TEST(rects3),
-    TEST(rects2),
-    TEST(rects1),
-    TEST(issue2540),
-    TEST(issue2504),
-    TEST(kari1),
-    TEST(quadOp10i),
-    TEST(cubicOp113),
-    // fails because a cubic/quadratic intersection is missed
-    // the internal quad/quad is far enough away from the real cubic/quad that it is rejected
-    TEST(skpcarrot_is24),
-    TEST(issue1417),
-    TEST(cubicOp112),
-    TEST(skpadspert_net23),
-    TEST(skpadspert_de11),
-    TEST(findFirst1),
-    TEST(xOp2i),
-    TEST(xOp3i),
-    TEST(xOp1u),
-    TEST(xOp1i),
-    TEST(cubicOp111),
-    TEST(cubicOp110),
-    TEST(cubicOp109),
-    TEST(cubicOp108),
-    TEST(cubicOp107),
-    TEST(cubicOp106),
-    TEST(cubicOp105),
-    TEST(cubicOp104),
-    TEST(cubicOp103),
-    TEST(cubicOp102),
-    TEST(cubicOp101),
-    TEST(cubicOp100),
-    TEST(cubicOp99),
-    TEST(issue1435),
-    TEST(cubicOp98x),
-    TEST(cubicOp97x),
-    TEST(skpcarpetplanet_ru22),  // cubic/cubic intersect detects unwanted coincidence
-    TEST(cubicOp96d),
-    TEST(cubicOp95u),
-    TEST(skpadbox_lt15),
-    TEST(skpagentxsites_com55),
-    TEST(skpadventistmission_org572),
-    TEST(skpadoption_org196),
-    TEST(skpbambootheme_com12),
-    TEST(skpbakosoft_com10),
-    TEST(skpakmmos_ru100),
-    TEST(skpbangalorenest_com4),
-    TEST(skpbingoentertainment_net189),
-    TEST(skpbestred_ru37),
-    TEST(skpbenzoteh_ru152),
-    TEST(skpcamcorder_kz21),
-    TEST(skpcaffelavazzait_com_ua21),
-    TEST(skpcarrefour_ro62),
-    TEST(skpcavablar_net563),
-    TEST(skpinsomnia_gr72),
-    TEST(skpadbox_lt8),
-    TEST(skpact_com43),
-    TEST(skpacesoftech_com47),
-    TEST(skpabcspark_ca103),
-    TEST(cubicOp94u),
-    TEST(cubicOp93d),
-    TEST(cubicOp92i),
-    TEST(skpadithya_putr4_blogspot_com551),
-    TEST(skpadindex_de4),
-    TEST(skpaiaigames_com870),
-    TEST(skpaaalgarve_org53),
-    TEST(skpkkiste_to716),
-    TEST(cubicOp91u),
-    TEST(cubicOp90u),
-    TEST(cubicOp89u),
-    TEST(cubicOp88u),
-    TEST(cubicOp87u),
-    TEST(cubicOp86i),
-    TEST(loopEdge2),
-    TEST(loopEdge1),
-    TEST(rectOp3x),
-    TEST(rectOp2i),
-    TEST(rectOp1i),
-    TEST(issue1418b),
-    TEST(cubicOp85i),
-    TEST(issue1418),
-    TEST(skpkkiste_to98),
-    TEST(skpahrefs_com29),
-    TEST(cubicOp85d),
-    TEST(skpahrefs_com88),
-    TEST(skphealth_com76),
-    TEST(skpancestry_com1),
-    TEST(skpbyte_com1),
-    TEST(skpeldorado_com_ua1),
-    TEST(skp96prezzi1),
-    TEST(skpClip2),
-    TEST(skpClip1),
-    TEST(cubicOp84d),
-    TEST(cubicOp83i),
-    TEST(cubicOp82i),
-    TEST(cubicOp81d),
-    TEST(cubicOp80i),
-    TEST(cubicOp79u),
-    TEST(cubicOp78u),
-    TEST(cubicOp77i),
-    TEST(cubicOp76u),
-    TEST(cubicOp75d),
-    TEST(cubicOp74d),
-    TEST(cubicOp73d),
-    TEST(cubicOp72i),
-    TEST(cubicOp71d),
-    TEST(skp5),
-    TEST(skp4),
-    TEST(skp3),
-    TEST(skp2),
-    TEST(skp1),
-    TEST(rRect1),
-    TEST(cubicOp70d),
-    TEST(cubicOp69d),
-    TEST(cubicOp68u),
-    TEST(cubicOp67u),
-    TEST(cubicOp66u),
-    TEST(rectOp1d),
-    TEST(cubicOp65d),
-    TEST(cubicOp64d),
-    TEST(cubicOp63d),
-    TEST(cubicOp62d),
-    TEST(cubicOp61d),
-    TEST(cubicOp60d),
-    TEST(cubicOp59d),
-    TEST(cubicOp58d),
-    TEST(cubicOp57d),
-    TEST(cubicOp56d),
-    TEST(cubicOp55d),
-    TEST(cubicOp54d),
-    TEST(cubicOp53d),
-    TEST(cubicOp52d),
-    TEST(cubicOp51d),
-    TEST(cubicOp50d),
-    TEST(cubicOp49d),
-    TEST(cubicOp48d),
-    TEST(cubicOp47d),
-    TEST(cubicOp46d),
-    TEST(cubicOp45d),
-    TEST(cubicOp44d),
-    TEST(cubicOp43d),
-    TEST(cubicOp42d),
-    TEST(cubicOp41i),
-    TEST(cubicOp40d),
-    TEST(cubicOp39d),
-    TEST(cubicOp38d),
-    TEST(cubicOp37d),
-    TEST(cubicOp36u),
-    TEST(cubicOp35d),
-    TEST(cubicOp34d),
-    TEST(cubicOp33i),
-    TEST(cubicOp32d),
-    TEST(cubicOp31d),
-    TEST(cubicOp31x),
-    TEST(cubicOp31u),
-    TEST(cubicOp30d),
-    TEST(cubicOp29d),
-    TEST(cubicOp28u),
-    TEST(cubicOp27d),
-    TEST(cubicOp26d),
-    TEST(cubicOp25i),
-    TEST(testOp8d),
-    TEST(testDiff1),
-    TEST(testIntersect1),
-    TEST(testUnion1),
-    TEST(testXor1),
-    TEST(testDiff2),
-    TEST(testIntersect2),
-    TEST(testUnion2),
-    TEST(testXor2),
-    TEST(testOp1d),
-    TEST(testOp2d),
-    TEST(testOp3d),
-    TEST(testOp1u),
-    TEST(testOp4d),
-    TEST(testOp5d),
-    TEST(testOp6d),
-    TEST(testOp7d),
-    TEST(testOp2u),
+static void testRect1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, path2;
+    path.addRect(0, 0, 60, 60, SkPath::kCCW_Direction);
+    path.addRect(30, 20, 50, 50, SkPath::kCCW_Direction);
+    path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction);
+//    path.addRect(32, 24, 36, 41, SkPath::kCCW_Direction);
+    testPathOp(reporter, path, path2, kUnion_SkPathOp, filename);
+}
 
-    TEST(cubicOp24d),
-    TEST(cubicOp23d),
-    TEST(cubicOp22d),
-    TEST(cubicOp21d),
-    TEST(cubicOp20d),
-    TEST(cubicOp19i),
-    TEST(cubicOp18d),
-    TEST(cubicOp17d),
-    TEST(cubicOp16d),
-    TEST(cubicOp15d),
-    TEST(cubicOp14d),
-    TEST(cubicOp13d),
-    TEST(cubicOp12d),
-    TEST(cubicOp11d),
-    TEST(cubicOp10d),
-    TEST(cubicOp1i),
-    TEST(cubicOp9d),
-    TEST(quadOp9d),
-    TEST(lineOp9d),
-    TEST(cubicOp8d),
-    TEST(cubicOp7d),
-    TEST(cubicOp6d),
-    TEST(cubicOp5d),
-    TEST(cubicOp3d),
-    TEST(cubicOp2d),
-    TEST(cubicOp1d),
-};
+static void testRect2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
+    path.addRect(4, 4, 5, 5, SkPath::kCW_Direction);
+    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
+    pathB.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
 
-static const size_t testCount = SK_ARRAY_COUNT(tests);
+static void cubicOp116(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(4,6, 2,0, 2,0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,2);
+    pathB.cubicTo(0,2, 1,0, 6,4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
 
 static void cubicOp117(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
@@ -3770,8 +3589,11 @@
 #include "SkPathOpsCubic.h"
 
 static void loop1asQuad(skiatest::Reporter* reporter, const char* filename) {
-    SkDCubic c1 = {{{0,1}, {1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}}};
-    SkDCubic c2 = {{{1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}, {0,1}}};
+    CubicPts cubic1 = {{{0,1}, {1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}}};
+    CubicPts cubic2 = {{{1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}, {0,1}}};
+    SkDCubic c1, c2;
+    c1.debugSet(cubic1.fPts);
+    c2.debugSet(cubic2.fPts);
     double c1InflectionTs[2], c2InflectionTs[2];
     SkDEBUGCODE(int c1InfTCount =) c1.findInflections(c1InflectionTs);
     SkASSERT(c1InfTCount == 2);
@@ -4025,11 +3847,10 @@
 #include "SkGeometry.h"
 
 static void complex_to_quads(const SkPoint pts[], SkPath* path) {
-    SkScalar loopT;
-    SkDCubic::CubicType dType;
-    if (SkDCubic::ComplexBreak(pts, &loopT, &dType)) {
-        SkPoint cubicPair[7]; 
-        SkChopCubicAt(pts, cubicPair, loopT);
+    SkScalar loopT[3];
+    if (SkDCubic::ComplexBreak(pts, loopT)) {
+        SkPoint cubicPair[7];
+        SkChopCubicAt(pts, cubicPair, loopT[0]);
         SkDCubic c1, c2;
         c1.set(cubicPair);
         c2.set(&cubicPair[3]);
@@ -5048,7 +4869,7 @@
 }
 
 /*
-FAILED: d:\cygwin\puregit\tests\pathopsextendedtest.cpp:346	0 */
+FAILED: d:\cygwin\puregit\tests\pathopsextendedtest.cpp:346    0 */
 static void loops47i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
     path.setFillType(SkPath::kWinding_FillType);
@@ -5342,9 +5163,270 @@
     path.lineTo(100.34f, 310.156f);
     path.lineTo(100.34f, 303.312f);
     path.close();
-    testPathOpCheck(reporter, path, pathB, kUnion_SkPathOp, filename, FLAGS_runFail);
+    testPathOpCheck(reporter, path, pathB, kUnion_SkPathOp, filename, true);
 }
 
+static void crbug_526025(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x43b40000), SkBits2Float(0xcf000000));  // 360, -2.14748e+09f
+path.cubicTo(SkBits2Float(0x4e0d628f), SkBits2Float(0xceffffff), SkBits2Float(0x4e800003), SkBits2Float(0xcec6b143), SkBits2Float(0x4e800002), SkBits2Float(0xce7ffffc));  // 5.93012e+08f, -2.14748e+09f, 1.07374e+09f, -1.66675e+09f, 1.07374e+09f, -1.07374e+09f
+path.cubicTo(SkBits2Float(0x4e800002), SkBits2Float(0xcde53aee), SkBits2Float(0x4e0d6292), SkBits2Float(0xc307820e), SkBits2Float(0x44627d00), SkBits2Float(0x437ffff2));  // 1.07374e+09f, -4.80731e+08f, 5.93012e+08f, -135.508f, 905.953f, 256
+path.lineTo(SkBits2Float(0x444bf3bc), SkBits2Float(0x4460537e));  // 815.808f, 897.305f
+path.lineTo(SkBits2Float(0x43553abd), SkBits2Float(0x440f3cbd));  // 213.229f, 572.949f
+path.lineTo(SkBits2Float(0x42000000), SkBits2Float(0x41800000));  // 32, 16
+path.lineTo(SkBits2Float(0x42c80000), SkBits2Float(0x44000000));  // 100, 512
+path.lineTo(SkBits2Float(0x43553abd), SkBits2Float(0x440f3cbd));  // 213.229f, 572.949f
+path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x44800000));  // 360, 1024
+path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x45816000));  // 360, 4140
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42fe0000), SkBits2Float(0x43a08000));  // 127, 321
+path.lineTo(SkBits2Float(0x45d5c000), SkBits2Float(0x43870000));  // 6840, 270
+path.lineTo(SkBits2Float(0xd0a00000), SkBits2Float(0x4cbebc20));  // -2.14748e+10f, 1e+08
+path.lineTo(SkBits2Float(0x451f7000), SkBits2Float(0x42800000));  // 2551, 64
+path.lineTo(SkBits2Float(0x42fe0000), SkBits2Float(0x43a08000));  // 127, 321
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzzX_392(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+path.moveTo(SkBits2Float(0x41e80000), SkBits2Float(0x43bde212));  // 29, 379.766f
+path.lineTo(SkBits2Float(0x41e80000), SkBits2Float(0x43bdc7ef));  // 29, 379.562f
+path.conicTo(SkBits2Float(0x42a5861e), SkBits2Float(0x43c61f86), SkBits2Float(0x430b0610), SkBits2Float(0x43c61f86), SkBits2Float(0x3f7d23f3));  // 82.7619f, 396.246f, 139.024f, 396.246f, 0.98883f
+path.conicTo(SkBits2Float(0x42a58e20), SkBits2Float(0x43c61f86), SkBits2Float(0x41e80000), SkBits2Float(0x43bde212), SkBits2Float(0x3f7d2cf5));  // 82.7776f, 396.246f, 29, 379.766f, 0.988967f
+path.close();
+
+    SkPath path1(path);
+    path.setFillType(SkPath::kWinding_FillType);
+path.moveTo(SkBits2Float(0xc36c7bd8), SkBits2Float(0xc3a31d72));  // -236.484f, -326.23f
+path.lineTo(SkBits2Float(0xc367a4ae), SkBits2Float(0xc3a31d72));  // -231.643f, -326.23f
+path.lineTo(SkBits2Float(0x430b0610), SkBits2Float(0x43c61f86));  // 139.024f, 396.246f
+path.lineTo(SkBits2Float(0xc36c7bd8), SkBits2Float(0x43c61f86));  // -236.484f, 396.246f
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
+}
+
+static void dean2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
+path.cubicTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b), SkBits2Float(0x41531912), SkBits2Float(0x3f130322), SkBits2Float(0x4154a02b), SkBits2Float(0x3f2b74b3)); // 0.669749f, 13.2891f, 13.1936f, 0.574267f, 13.2891f, 0.669749f
+path.cubicTo(SkBits2Float(0x414a835a), SkBits2Float(0x3ec07ba6), SkBits2Float(0x413fcc0d), SkBits2Float(0x3e193319), SkBits2Float(0x4134a02b), SkBits2Float(0x00000000)); // 12.6571f, 0.375943f, 11.9873f, 0.149609f, 11.2891f, 0
+path.lineTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
+path.close();
+    SkPath path1(path);
+
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
+path.cubicTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b), SkBits2Float(0x41531912), SkBits2Float(0x3f130322), SkBits2Float(0x4154a02b), SkBits2Float(0x3f2b74b3)); // 0.669749f, 13.2891f, 13.1936f, 0.574267f, 13.2891f, 0.669749f
+path.lineTo(SkBits2Float(0x417ab74b), SkBits2Float(0x4154a02b)); // 15.6697f, 13.2891f
+path.lineTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
+path.close();
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
+}
+
+static void cubics_d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 1);
+    path.cubicTo(3, 5, 1, 0, 3, 0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 1);
+    pathB.cubicTo(0, 3, 1, 0, 5, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics_d2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 1);
+    path.cubicTo(2, 5, 2, 0, 2, 1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 2);
+    pathB.cubicTo(1, 2, 1, 0, 5, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void loops_i1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 3);
+    path.cubicTo(0, 4, -0.333333343f, 4.66666651f, 3, 5.83333349f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 4);
+    pathB.cubicTo(-0.333333343f, 4.66666651f, 3, 5.83333349f, 2, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops_i2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 4);
+    path.cubicTo(0, 5, -0.333333343f, 5.66666651f, 3, 6.83333302f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 5);
+    pathB.cubicTo(-0.333333343f, 5.66666651f, 3, 6.83333302f, 2, 4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops_i3(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 5);
+    path.cubicTo(0, 6, -0.333333343f, 6.66666651f, 3, 7.83333302f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 6);
+    pathB.cubicTo(-0.333333343f, 6.66666651f, 3, 7.83333302f, 2, 5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops_i4(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(3, 4);
+    path.cubicTo(1, 5, 0.666666627f, 5.66666651f, 4, 6.83333302f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 5);
+    pathB.cubicTo(0.666666627f, 5.66666651f, 4, 6.83333302f, 3, 4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops_i5(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(3, 5);
+    path.cubicTo(1, 6, 0.666666627f, 6.66666651f, 4, 7.83333302f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 6);
+    pathB.cubicTo(0.666666627f, 6.66666651f, 4, 7.83333302f, 3, 5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops_i6(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(4, 5);
+    path.cubicTo(2, 6, 1.66666663f, 6.66666651f, 5, 7.83333302f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2, 6);
+    pathB.cubicTo(1.66666663f, 6.66666651f, 5, 7.83333302f, 4, 5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void cubics_d3(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(3, 4);
+    path.cubicTo(0, 6, 6, 1, 4, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 6);
+    pathB.cubicTo(2, 4, 4, 3, 6, 0);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics_o(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 4);
+    path.cubicTo(2, 6, 5, 0, 5, 3);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 5);
+    pathB.cubicTo(3, 5, 4, 1, 6, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename);
+}
+
+static void cubicOp158(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(2,4, 2,0, 2,0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,2);
+    pathB.cubicTo(0,2, 1,0, 4,2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void loop17(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(1, 2);
+    path.cubicTo(0, 3, -0.333333343f, 3.33333325f, 0.833333373f, 3.5f);
+    path.close();
+    pathB.moveTo(0, 3);
+    pathB.cubicTo(-0.333333343f, 3.33333325f, 0.833333373f, 3.5f, 1, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void circlesOp4(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addCircle(0, 1, 5, SkPath::kCW_Direction);
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.addCircle(0, 1, 0, SkPath::kCW_Direction);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void bug5240(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path;
+path.moveTo(815, 82);
+path.cubicTo(814.4794311523438f, 82.7868881225586f, 814.5330810546875f,
+82.6266555786133f, 814.5291137695312f, 82.6252212524414f);
+path.cubicTo(814.5229492187500f, 82.6230010986328f, 814.3790283203125f,
+83.0008087158203f, 813.8533935546875f, 82.7072601318359f);
+path.close();
+    testPathOp(reporter, path, path, kUnion_SkPathOp, filename);
+}
+
+static void android1(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path, pathB;
+path.moveTo(SkBits2Float(0xc0a00000), SkBits2Float(0x00000000));  // -5, 0

+path.lineTo(SkBits2Float(0x44866000), SkBits2Float(0x00000000));  // 1075, 0

+path.lineTo(SkBits2Float(0x44866000), SkBits2Float(0x43720000));  // 1075, 242

+path.lineTo(SkBits2Float(0xc0a00000), SkBits2Float(0x43720000));  // -5, 242

+path.lineTo(SkBits2Float(0xc0a00000), SkBits2Float(0x00000000));  // -5, 0

+path.close();

+pathB.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0

+pathB.lineTo(SkBits2Float(0x44870000), SkBits2Float(0x00000000));  // 1080, 0

+pathB.lineTo(SkBits2Float(0x44870000), SkBits2Float(0x43720000));  // 1080, 242

+pathB.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x43720000));  // 0, 242

+pathB.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0

+pathB.close();

+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+
 static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
 static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
 static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
@@ -5352,6 +5434,24 @@
 #define TEST(name) { name, #name }
 
 static struct TestDesc tests[] = {
+    TEST(android1),
+    TEST(bug5240),
+    TEST(circlesOp4),
+    TEST(loop17),
+    TEST(cubicOp158),
+    TEST(loops_i1),
+    TEST(loops_i2),
+    TEST(loops_i3),
+    TEST(loops_i4),
+    TEST(loops_i5),
+    TEST(loops_i6),
+    TEST(cubics_d3),
+    TEST(cubics_o),
+    TEST(cubics_d2),
+    TEST(cubics_d),
+    TEST(dean2),
+    TEST(fuzzX_392),
+    TEST(crbug_526025),
     TEST(fuzz38),
     TEST(cubics44d),
     TEST(cubics45u),
@@ -5427,7 +5527,273 @@
     TEST(quadRect6),
     TEST(cubicOp141),
     TEST(cubicOp58d),
+    TEST(loops5i),
+    TEST(cubicOp140),
+    TEST(cubicOp139),
+    TEST(cubics138),
+    TEST(cubics137),
+    TEST(cubicOp136a),
+    TEST(cubicOp136),
+    TEST(cubicOp135),
+    TEST(cubicOp134),
+    TEST(cubicOp133),
+    TEST(loop12),
+    TEST(cubicOp132),
+    TEST(loop11),
+    TEST(loop10),
+    TEST(circlesOp3),
+    TEST(loop9),
+    TEST(loop8),
+    TEST(rects5),
+    TEST(loop7),
+    TEST(cubicOp130a),
+    TEST(rRect1x),
+    TEST(circlesOp2),
+    TEST(circlesOp1),
+    TEST(cubicOp131),
+    TEST(cubicOp130),
+    TEST(cubicOp129),
+    TEST(cubicOp128),
+    TEST(cubicOp127),
+    TEST(cubicOp126),
+    TEST(cubicOp125),
+    TEST(cubicOp124),
+    TEST(loop6),
+    TEST(loop5),
+    TEST(cubicOp123),
+    TEST(cubicOp122),
+    TEST(cubicOp121),
+    TEST(cubicOp120),
+    TEST(cubicOp119),
+    TEST(loop4),
+    TEST(loop3),
+    TEST(loop2),
+    TEST(loop1asQuad),
+    TEST(loop1),
+    TEST(issue3517),
+    TEST(cubicOp118),
+    TEST(cubicOp117),
+    TEST(cubicOp116),
+    TEST(testRect2),
+    TEST(testRect1),
+    TEST(cubicOp115),
+    TEST(issue2753),
+    TEST(cubicOp114),
+    TEST(issue2808),
+    TEST(cubicOp114asQuad),
+    TEST(rects4),
+    TEST(rects3),
+    TEST(rects2),
+    TEST(rects1),
+    TEST(issue2540),
+    TEST(issue2504),
+    TEST(kari1),
+    TEST(quadOp10i),
+    TEST(cubicOp113),
+    TEST(skpcarrot_is24),
+    TEST(issue1417),
+    TEST(cubicOp112),
+    TEST(skpadspert_net23),
+    TEST(skpadspert_de11),
+    TEST(findFirst1),
+    TEST(xOp2i),
+    TEST(xOp3i),
+    TEST(xOp1u),
+    TEST(xOp1i),
+    TEST(cubicOp111),
+    TEST(cubicOp110),
+    TEST(cubicOp109),
+    TEST(cubicOp108),
+    TEST(cubicOp107),
+    TEST(cubicOp106),
+    TEST(cubicOp105),
+    TEST(cubicOp104),
+    TEST(cubicOp103),
+    TEST(cubicOp102),
+    TEST(cubicOp101),
+    TEST(cubicOp100),
+    TEST(cubicOp99),
+    TEST(issue1435),
+    TEST(cubicOp98x),
+    TEST(cubicOp97x),
+    TEST(skpcarpetplanet_ru22),
+    TEST(cubicOp96d),
+    TEST(cubicOp95u),
+    TEST(skpadbox_lt15),
+    TEST(skpagentxsites_com55),
+    TEST(skpadventistmission_org572),
+    TEST(skpadoption_org196),
+    TEST(skpbambootheme_com12),
+    TEST(skpbakosoft_com10),
+    TEST(skpakmmos_ru100),
+    TEST(skpbangalorenest_com4),
+    TEST(skpbingoentertainment_net189),
+    TEST(skpbestred_ru37),
+    TEST(skpbenzoteh_ru152),
+    TEST(skpcamcorder_kz21),
+    TEST(skpcaffelavazzait_com_ua21),
+    TEST(skpcarrefour_ro62),
+    TEST(skpcavablar_net563),
+    TEST(skpinsomnia_gr72),
+    TEST(skpadbox_lt8),
+    TEST(skpact_com43),
+    TEST(skpacesoftech_com47),
+    TEST(skpabcspark_ca103),
+    TEST(cubicOp94u),
+    TEST(cubicOp93d),
+    TEST(cubicOp92i),
+    TEST(skpadithya_putr4_blogspot_com551),
+    TEST(skpadindex_de4),
+    TEST(skpaiaigames_com870),
+    TEST(skpaaalgarve_org53),
+    TEST(skpkkiste_to716),
+    TEST(cubicOp91u),
+    TEST(cubicOp90u),
+    TEST(cubicOp89u),
+    TEST(cubicOp88u),
+    TEST(cubicOp87u),
+    TEST(cubicOp86i),
+    TEST(loopEdge2),
+    TEST(loopEdge1),
+    TEST(rectOp3x),
+    TEST(rectOp2i),
+    TEST(rectOp1i),
+    TEST(issue1418b),
+    TEST(cubicOp85i),
+    TEST(issue1418),
+    TEST(skpkkiste_to98),
+    TEST(skpahrefs_com29),
+    TEST(cubicOp85d),
+    TEST(skpahrefs_com88),
+    TEST(skphealth_com76),
+    TEST(skpancestry_com1),
+    TEST(skpbyte_com1),
+    TEST(skpeldorado_com_ua1),
+    TEST(skp96prezzi1),
+    TEST(skpClip2),
+    TEST(skpClip1),
+    TEST(cubicOp84d),
+    TEST(cubicOp83i),
+    TEST(cubicOp82i),
+    TEST(cubicOp81d),
+    TEST(cubicOp80i),
+    TEST(cubicOp79u),
+    TEST(cubicOp78u),
+    TEST(cubicOp77i),
+    TEST(cubicOp76u),
+    TEST(cubicOp75d),
+    TEST(cubicOp74d),
+    TEST(cubicOp73d),
+    TEST(cubicOp72i),
+    TEST(cubicOp71d),
+    TEST(skp5),
+    TEST(skp4),
+    TEST(skp3),
+    TEST(skp2),
+    TEST(skp1),
+    TEST(rRect1),
+    TEST(cubicOp70d),
+    TEST(cubicOp69d),
+    TEST(cubicOp68u),
+    TEST(cubicOp67u),
+    TEST(cubicOp66u),
+    TEST(rectOp1d),
+    TEST(cubicOp65d),
+    TEST(cubicOp64d),
+    TEST(cubicOp63d),
+    TEST(cubicOp62d),
+    TEST(cubicOp61d),
+    TEST(cubicOp60d),
+    TEST(cubicOp59d),
+    TEST(cubicOp57d),
+    TEST(cubicOp56d),
+    TEST(cubicOp55d),
+    TEST(cubicOp54d),
     TEST(cubicOp53d),
+    TEST(cubicOp52d),
+    TEST(cubicOp51d),
+    TEST(cubicOp50d),
+    TEST(cubicOp49d),
+    TEST(cubicOp48d),
+    TEST(cubicOp47d),
+    TEST(cubicOp46d),
+    TEST(cubicOp45d),
+    TEST(cubicOp44d),
+    TEST(cubicOp43d),
+    TEST(cubicOp42d),
+    TEST(cubicOp41i),
+    TEST(cubicOp40d),
+    TEST(cubicOp39d),
+    TEST(cubicOp38d),
+    TEST(cubicOp37d),
+    TEST(cubicOp36u),
+    TEST(cubicOp35d),
+    TEST(cubicOp34d),
+    TEST(cubicOp33i),
+    TEST(cubicOp32d),
+    TEST(cubicOp31d),
+    TEST(cubicOp31x),
+    TEST(cubicOp31u),
+    TEST(cubicOp30d),
+    TEST(cubicOp29d),
+    TEST(cubicOp28u),
+    TEST(cubicOp27d),
+    TEST(cubicOp26d),
+    TEST(cubicOp25i),
+    TEST(testOp8d),
+    TEST(testDiff1),
+    TEST(testIntersect1),
+    TEST(testUnion1),
+    TEST(testXor1),
+    TEST(testDiff2),
+    TEST(testIntersect2),
+    TEST(testUnion2),
+    TEST(testXor2),
+    TEST(testOp1d),
+    TEST(testOp2d),
+    TEST(testOp3d),
+    TEST(testOp1u),
+    TEST(testOp4d),
+    TEST(testOp5d),
+    TEST(testOp6d),
+    TEST(testOp7d),
+    TEST(testOp2u),
+
+    TEST(cubicOp24d),
+    TEST(cubicOp23d),
+    TEST(cubicOp22d),
+    TEST(cubicOp21d),
+    TEST(cubicOp20d),
+    TEST(cubicOp19i),
+    TEST(cubicOp18d),
+    TEST(cubicOp17d),
+    TEST(cubicOp16d),
+    TEST(cubicOp15d),
+    TEST(cubicOp14d),
+    TEST(cubicOp13d),
+    TEST(cubicOp12d),
+    TEST(cubicOp11d),
+    TEST(cubicOp10d),
+    TEST(cubicOp1i),
+    TEST(cubicOp9d),
+    TEST(quadOp9d),
+    TEST(lineOp9d),
+    TEST(cubicOp8d),
+    TEST(cubicOp7d),
+    TEST(cubicOp6d),
+    TEST(cubicOp5d),
+    TEST(cubicOp3d),
+    TEST(cubicOp2d),
+    TEST(cubicOp1d),
+};
+
+static const size_t testCount = SK_ARRAY_COUNT(tests);
+
+static struct TestDesc subTests[] = {
+    TEST(loops47i),
+    TEST(loops61i),
+    TEST(loops62i),
+    TEST(issue3517),
 };
 
 static const size_t subTestCount = SK_ARRAY_COUNT(subTests);
@@ -5435,7 +5801,7 @@
 static void (*firstSubTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static bool runSubTests = false;
-static bool runSubTestsFirst = false;
+static bool runSubTestsFirst = true;
 static bool runReverse = false;
 
 DEF_TEST(PathOpsOp, reporter) {
@@ -5443,20 +5809,32 @@
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
     if (runSubTests && runSubTestsFirst) {
-        RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
+        RunTestSet(reporter, subTests, subTestCount, firstSubTest, nullptr, stopTest, runReverse);
     }
-    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
     if (runSubTests && !runSubTestsFirst) {
-        RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
+        RunTestSet(reporter, subTests, subTestCount, firstSubTest, nullptr, stopTest, runReverse);
     }
 }
 
+static void fuzz535151(skiatest::Reporter* reporter, const char* filename) {
+    SkPath one;
+    one.setFillType(SkPath::kWinding_FillType);
+    SkPath two;
+    two.setFillType(SkPath::kWinding_FillType);
+    two.moveTo(0, 0);
+    two.lineTo(0, 50);
+    two.lineTo(4.29497e+09f, 50);
+    SkPath dummy;
+    testPathOpFuzz(reporter, one, two, kIntersect_SkPathOp, filename);
+}
+
 static void bufferOverflow(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.addRect(0,0, 300,170141183460469231731687303715884105728.f);
     SkPath pathB;
     pathB.addRect(0,0, 300,16);
-    testPathFailOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOpFuzz(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 // m 100,0 60,170 -160,-110 200,0 -170,11000000000 z
@@ -5476,7 +5854,7 @@
     path2.lineTo(-170 + 20,11000000000.0f + 20);
     path2.close();
 
-    testPathFailOp(reporter, path1, path2, kIntersect_PathOp, filename);
+    testPathOpFuzz(reporter, path1, path2, kIntersect_SkPathOp, filename);
 }
 
 static void fuzz433b(skiatest::Reporter* reporter, const char* filename) {
@@ -5499,7 +5877,7 @@
     path2.lineTo(190, 60);
     path2.close();
 
-    testPathFailOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOpFuzz(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void fuzz487a(skiatest::Reporter* reporter, const char* filename) {
@@ -5545,7 +5923,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz487b(skiatest::Reporter* reporter, const char* filename) {
@@ -5591,14 +5969,2677 @@
 path.close();
 
     SkPath path2(path);
-    testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
+static void fuzz714(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x430c0000), SkBits2Float(0x42200000));
+path.lineTo(SkBits2Float(0x43480000), SkBits2Float(0x43520000));
+path.lineTo(SkBits2Float(0x42200000), SkBits2Float(0x42c80000));
+path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x42c80000));  // 2.22222e+022f
+path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x43520000));  // 2.22222e+022f
+path.lineTo(SkBits2Float(0x430c0000), SkBits2Float(0x42200000));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43200000), SkBits2Float(0x42700000));
+path.lineTo(SkBits2Float(0x435c0000), SkBits2Float(0x43660000));
+path.lineTo(SkBits2Float(0x42700000), SkBits2Float(0x42f00000));
+path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x42f00000));  // 2.22222e+022f
+path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x43660000));  // 2.22222e+022f
+path.lineTo(SkBits2Float(0x43200000), SkBits2Float(0x42700000));
+path.close();
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x7f800000), SkBits2Float(0x7f800000));
+path.quadTo(SkBits2Float(0x7f800000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000));
+path.quadTo(SkBits2Float(0x7f800000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000));
+path.quadTo(SkBits2Float(0xffc00000), SkBits2Float(0x7f800000), SkBits2Float(0xffc00000), SkBits2Float(0x7f800000));
+path.quadTo(SkBits2Float(0xff000001), SkBits2Float(0x7f800000), SkBits2Float(0xff000001), SkBits2Float(0x7f800000));
+path.quadTo(SkBits2Float(0xff000001), SkBits2Float(0xffc00000), SkBits2Float(0xffc00000), SkBits2Float(0xffc00000));
+path.quadTo(SkBits2Float(0xffc00000), SkBits2Float(0xff000001), SkBits2Float(0x7f800000), SkBits2Float(0xff000001));
+path.quadTo(SkBits2Float(0x7f800000), SkBits2Float(0xff000001), SkBits2Float(0x7f800000), SkBits2Float(0xffc00000));
+path.quadTo(SkBits2Float(0x7f800000), SkBits2Float(0xffc00000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+
+static void fuzz753_91(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42910000), SkBits2Float(0x00000000));  // 72.5f, 0
+path.lineTo(SkBits2Float(0x42166668), SkBits2Float(0x00000000));  // 37.6f, 0
+path.cubicTo(SkBits2Float(0x42166668), SkBits2Float(0xc1966668), SkBits2Float(0x41c66668), SkBits2Float(0xc20a6666), SkBits2Float(0x40f00010), SkBits2Float(0xc21ccccd));  // 37.6f, -18.8f, 24.8f, -34.6f, 7.50001f, -39.2f
+path.lineTo(SkBits2Float(0x41840004), SkBits2Float(0xc291cccd));  // 16.5f, -72.9f
+path.lineTo(SkBits2Float(0x42fb6668), SkBits2Float(0x42c73334));  // 125.7f, 99.6f
+path.lineTo(SkBits2Float(0x43646668), SkBits2Float(0x43880ccd));  // 228.4f, 272.1f
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x428bf702), SkBits2Float(0xcf223cbf));  // 69.9824f, -2.72189e+09f
+path.lineTo(SkBits2Float(0x42112d68), SkBits2Float(0xcf223cbf));  // 36.2943f, -2.72189e+09f
+path.cubicTo(SkBits2Float(0x4220d9fc), SkBits2Float(0xcf223cc0), SkBits2Float(0x420ee118), SkBits2Float(0xcf223cc0), SkBits2Float(0x41cef2f8), SkBits2Float(0xcf223cc0));  // 40.2129f, -2.72189e+09f, 35.7198f, -2.72189e+09f, 25.8686f, -2.72189e+09f
+path.lineTo(SkBits2Float(0x424a99e0), SkBits2Float(0xcf223cc0));  // 50.6503f, -2.72189e+09f
+path.cubicTo(SkBits2Float(0x42266e32), SkBits2Float(0xcf223cc0), SkBits2Float(0x41f0fa20), SkBits2Float(0xcf223cc0), SkBits2Float(0x41872ed4), SkBits2Float(0xcf223cc0));  // 41.6076f, -2.72189e+09f, 30.1221f, -2.72189e+09f, 16.8979f, -2.72189e+09f
+path.lineTo(SkBits2Float(0x40f8fbe0), SkBits2Float(0xcf223cc0));  // 7.78075f, -2.72189e+09f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void bug597926_0(skiatest::Reporter* reporter, const char* filename) {
+SkPath path;
+path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43b38000), SkBits2Float(0x433e0000));  // 359, 190
+path.lineTo(SkBits2Float(0x40c00000), SkBits2Float(0x449ce000));  // 6, 1255
+path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x4497a000), SkBits2Float(0x43e40000), SkBits2Float(0x44750000), SkBits2Float(0x41000000), SkBits2Float(0x44aa2000));  // 280, 1213, 456, 980, 8, 1361
+path.moveTo(SkBits2Float(0x43290000), SkBits2Float(0x4431c000));  // 169, 711
+path.lineTo(SkBits2Float(0xd987d6ba), SkBits2Float(0xd93d0ad4));  // -4.7794e+15f, -3.32567e+15f
+path.conicTo(SkBits2Float(0x43cc8000), SkBits2Float(0x445b8000), SkBits2Float(0xd888b096), SkBits2Float(0xd9a1ebfa), SkBits2Float(0x3ebcb199));  // 409, 878, -1.20234e+15f, -5.69712e+15f, 0.368542f
+path.cubicTo(SkBits2Float(0x43c00000), SkBits2Float(0x443a8000), SkBits2Float(0x42380000), SkBits2Float(0x4421c000), SkBits2Float(0x42500000), SkBits2Float(0x448ca000));  // 384, 746, 46, 647, 52, 1125
+path.quadTo(SkBits2Float(0x43948000), SkBits2Float(0x42ac0000), SkBits2Float(0x43880000), SkBits2Float(0x4487e000));  // 297, 86, 272, 1087
+SkPath path1(path);
+path.reset();
+path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xc51d735c), SkBits2Float(0xc49db029));  // -2519.21f, -1261.51f
+path.cubicTo(SkBits2Float(0xc51d1dbd), SkBits2Float(0xc49d7a3f), SkBits2Float(0xc51c524a), SkBits2Float(0xc49d1610), SkBits2Float(0xc51d1a96), SkBits2Float(0xc49d86a6));  // -2513.86f, -1259.82f, -2501.14f, -1256.69f, -2513.66f, -1260.21f
+path.cubicTo(SkBits2Float(0xc51cd471), SkBits2Float(0xc49d54d0), SkBits2Float(0xc51c2e51), SkBits2Float(0xc49d0081), SkBits2Float(0xc51d197b), SkBits2Float(0xc49d7927));  // -2509.28f, -1258.65f, -2498.89f, -1256.02f, -2513.59f, -1259.79f
+path.quadTo(SkBits2Float(0xc51bf7eb), SkBits2Float(0xc49cf010), SkBits2Float(0xc51ba866), SkBits2Float(0xc49cb9e6));  // -2495.49f, -1255.5f, -2490.52f, -1253.81f
+path.cubicTo(SkBits2Float(0xc51bac0d), SkBits2Float(0xc49cc50e), SkBits2Float(0xc51c29eb), SkBits2Float(0xc49cfb01), SkBits2Float(0xc51c5bca), SkBits2Float(0xc49d1fa6));  // -2490.75f, -1254.16f, -2498.62f, -1255.84f, -2501.74f, -1256.99f
+SkPath path2(path);
+testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+static void fuzz1450_0(skiatest::Reporter* reporter, const char* filename) {
+SkPath path;
+path.moveTo(SkBits2Float(0x43b40000), SkBits2Float(0xcf000000));  // 360, -2.14748e+09f
+path.conicTo(SkBits2Float(0x4e800002), SkBits2Float(0xcf000000), SkBits2Float(0x4e800002), SkBits2Float(0xce7ffffe), SkBits2Float(0x3f3504f4));  // 1.07374e+09f, -2.14748e+09f, 1.07374e+09f, -1.07374e+09f, 0.707107f
+path.conicTo(SkBits2Float(0x4e800002), SkBits2Float(0x43800001), SkBits2Float(0x43348000), SkBits2Float(0x43800001), SkBits2Float(0x3f3504f4));  // 1.07374e+09f, 256, 180.5f, 256, 0.707107f
+SkPath path1(path);
+path.reset();
+path.moveTo(SkBits2Float(0x43b40000), SkBits2Float(0x45816000));  // 360, 4140
+path.conicTo(SkBits2Float(0x43b40005), SkBits2Float(0x458a945d), SkBits2Float(0x45610000), SkBits2Float(0x458a945d), SkBits2Float(0x3f3504f3));  // 360, 4434.55f, 3600, 4434.55f, 0.707107f
+path.conicTo(SkBits2Float(0x45d5bfff), SkBits2Float(0x458a945d), SkBits2Float(0x45d5bfff), SkBits2Float(0x45816000), SkBits2Float(0x3f3504f3));  // 6840, 4434.55f, 6840, 4140, 0.707107f
+path.lineTo(SkBits2Float(0x42c80000), SkBits2Float(0x44000000));  // 100, 512
+path.lineTo(SkBits2Float(0x42000000), SkBits2Float(0x41800000));  // 32, 16
+path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x44800000));  // 360, 1024
+path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x45816000));  // 360, 4140
+path.close();
+SkPath path2(path);
+testPathOpFuzz(reporter, path1, path2, kUnion_SkPathOp, filename);
+}
+
+static void fuzz1450_1(skiatest::Reporter* reporter, const char* filename) {
+SkPath path;
+path.setFillType(SkPath::kEvenOdd_FillType);
+path.moveTo(SkBits2Float(0x4e800002), SkBits2Float(0xce7ffffe));  // 1.07374e+09f, -1.07374e+09f
+path.conicTo(SkBits2Float(0x4e800002), SkBits2Float(0xcf000000), SkBits2Float(0x43b40000), SkBits2Float(0xcf000000), SkBits2Float(0x3f3504f4));  // 1.07374e+09f, -2.14748e+09f, 360, -2.14748e+09f, 0.707107f
+path.lineTo(SkBits2Float(0x43348000), SkBits2Float(0x43800001));  // 180.5f, 256
+path.lineTo(SkBits2Float(0x42000000), SkBits2Float(0x41800000));  // 32, 16
+path.lineTo(SkBits2Float(0x42c80000), SkBits2Float(0x44000000));  // 100, 512
+path.lineTo(SkBits2Float(0x43553abd), SkBits2Float(0x440f3cbd));  // 213.229f, 572.949f
+path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x44800000));  // 360, 1024
+path.lineTo(SkBits2Float(0x43b40000), SkBits2Float(0x45816000));  // 360, 4140
+path.conicTo(SkBits2Float(0x43b40005), SkBits2Float(0x458a945d), SkBits2Float(0x45610000), SkBits2Float(0x458a945d), SkBits2Float(0x3f3504f3));  // 360, 4434.55f, 3600, 4434.55f, 0.707107f
+path.conicTo(SkBits2Float(0x45d5bfff), SkBits2Float(0x458a945d), SkBits2Float(0x45d5bfff), SkBits2Float(0x45816000), SkBits2Float(0x3f3504f3));  // 6840, 4434.55f, 6840, 4140, 0.707107f
+path.lineTo(SkBits2Float(0x43553abd), SkBits2Float(0x440f3cbd));  // 213.229f, 572.949f
+path.lineTo(SkBits2Float(0x43348000), SkBits2Float(0x43800001));  // 180.5f, 256
+path.conicTo(SkBits2Float(0x4e800002), SkBits2Float(0x43800001), SkBits2Float(0x4e800002), SkBits2Float(0xce7ffffe), SkBits2Float(0x3f3504f4));  // 1.07374e+09f, 256, 1.07374e+09f, -1.07374e+09f, 0.707107f
+path.close();
+SkPath path1(path);
+path.reset();
+path.moveTo(SkBits2Float(0x42fe0000), SkBits2Float(0x43a08000));  // 127, 321
+path.lineTo(SkBits2Float(0x45d5c000), SkBits2Float(0x43870000));  // 6840, 270
+path.lineTo(SkBits2Float(0xd0a00000), SkBits2Float(0x4cbebc20));  // -2.14748e+10f, 1e+08
+path.lineTo(SkBits2Float(0x451f7000), SkBits2Float(0x42800000));  // 2551, 64
+path.lineTo(SkBits2Float(0x42fe0000), SkBits2Float(0x43a08000));  // 127, 321
+path.close();
+SkPath path2(path);
+testPathOpFuzz(reporter, path1, path2, kUnion_SkPathOp, filename);
+}
+
+static void fuzz763_9(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321), SkBits2Float(0xed7a6a4b), SkBits2Float(0x1f212a8c));  // 2.49282e-13f, 4.78968e-34f, 1.99397e+36f, -4.84373e+27f, 3.41283e-20f
+path.lineTo(SkBits2Float(0x7bc00321), SkBits2Float(0xed7a6a4b));  // 1.99397e+36f, -4.84373e+27f
+path.lineTo(SkBits2Float(0x282a3a21), SkBits2Float(0x3a21df28));  // 9.4495e-15f, 0.000617492f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3), SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6));  // -8.10388e-33f, 0.00148185f, 2.25206e-21f, 5.54035e+21f
+path.moveTo(SkBits2Float(0x29272a81), SkBits2Float(0x2ab03a55));  // 3.71183e-14f, 3.13044e-13f
+path.quadTo(SkBits2Float(0x2720213b), SkBits2Float(0x3a214729), SkBits2Float(0xdf28282a), SkBits2Float(0x8a2f2121));  // 2.22225e-15f, 0.000615227f, -1.2117e+19f, -8.43217e-33f
+path.quadTo(SkBits2Float(0x373b3a27), SkBits2Float(0x201fc4c1), SkBits2Float(0x27576c2a), SkBits2Float(0x5921c25d));  // 1.11596e-05f, 1.35329e-19f, 2.98959e-15f, 2.8457e+15f
+path.quadTo(SkBits2Float(0x2720213b), SkBits2Float(0x3a214729), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21));  // 2.22225e-15f, 0.000615227f, -1.2117e+19f, 0.00105459f
+path.cubicTo(SkBits2Float(0x373b3ac5), SkBits2Float(0x201fc422), SkBits2Float(0x523a702a), SkBits2Float(0x27576c51), SkBits2Float(0x5921c25d), SkBits2Float(0x51523a70));  // 1.11598e-05f, 1.35327e-19f, 2.00186e+11f, 2.9896e-15f, 2.8457e+15f, 5.64327e+10f
+path.quadTo(SkBits2Float(0xd912102a), SkBits2Float(0x284f9a28), SkBits2Float(0xb38a1f30), SkBits2Float(0x3a3ac23a));  // -2.56957e+15f, 1.15242e-14f, -6.4318e-08f, 0.000712428f
+path.lineTo(SkBits2Float(0xc809272a), SkBits2Float(0x29b02829));  // -140445, 7.82294e-14f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+
+static void fuzz763_4(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x555b3a2d), SkBits2Float(0x2a212a8c));  // 1.50652e+13f, 1.43144e-13f
+path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0));  // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f
+path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac2b33a));  // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148544f
+path.cubicTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6), SkBits2Float(0x295b2d2a), SkBits2Float(0x68295b2d), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c275b));  // 2.25206e-21f, 5.54035e+21f, 4.86669e-14f, 3.19905e+24f, 9.6297e-12f, 2.48963e-13f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0x55685b1f), SkBits2Float(0x5b2d2968));  // 1.59674e+13f, 4.87407e+16f
+path.lineTo(SkBits2Float(0x2a212a8c), SkBits2Float(0x2a21081f));  // 1.43144e-13f, 1.43025e-13f
+path.conicTo(SkBits2Float(0xde6a4b7b), SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21), SkBits2Float(0x3a7bc003), SkBits2Float(0x47ed7a6a));  // -4.22068e+18f, 2.50338e-13f, 4.61198e-19f, 0.00096035f, 121589
+path.lineTo(SkBits2Float(0x55685b1f), SkBits2Float(0x5b2d2968));  // 1.59674e+13f, 4.87407e+16f
+path.close();
+path.moveTo(SkBits2Float(0x55685b1f), SkBits2Float(0x5b2d2968));  // 1.59674e+13f, 4.87407e+16f
+path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3));  // -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f
+path.lineTo(SkBits2Float(0x2928088c), SkBits2Float(0x2be61d2a));  // 3.73109e-14f, 1.63506e-12f
+path.conicTo(SkBits2Float(0x2a812a63), SkBits2Float(0x2d292a27), SkBits2Float(0x5568295b), SkBits2Float(0x5b2d2968), SkBits2Float(0x552d6829));  // 2.29444e-13f, 9.6159e-12f, 1.5954e+13f, 4.87407e+16f, 1.19164e+13f
+path.conicTo(SkBits2Float(0x395b2d5b), SkBits2Float(0x68552768), SkBits2Float(0x555b2df0), SkBits2Float(0x1f722a8c), SkBits2Float(0x082a212a));  // 0.000209024f, 4.02636e+24f, 1.50619e+13f, 5.12807e-20f, 5.11965e-34f
+path.lineTo(SkBits2Float(0x55685b1f), SkBits2Float(0x5b2d2968));  // 1.59674e+13f, 4.87407e+16f
+path.close();
+path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2147ed7a), SkBits2Float(0x28282a3a), SkBits2Float(0x21df212a), SkBits2Float(0x033a8a3a));  // 6.14991e+25f, 6.77381e-19f, 9.33503e-15f, 1.51198e-18f, 5.48192e-37f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+static void fuzz763_3(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x555b292d), SkBits2Float(0x2a212a8c));  // 1.50606e+13f, 1.43144e-13f
+path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x295b2d1f), SkBits2Float(0x29685568));  // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.86669e-14f, 5.15884e-14f
+path.conicTo(SkBits2Float(0x8c28295b), SkBits2Float(0x1f21212a), SkBits2Float(0xc0032a08), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced));  // -1.29547e-31f, 3.41205e-20f, -2.04944f, 3.04132e+35f, 5.77848e-19f
+path.moveTo(SkBits2Float(0x25682929), SkBits2Float(0x212a8c5b));  // 2.01367e-16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4a7bc0));  // 4.7323e-37f, 6.11969e+25f
+path.conicTo(SkBits2Float(0x032108ed), SkBits2Float(0x283a7bc0), SkBits2Float(0x47ed7a6a), SkBits2Float(0x282a3a21), SkBits2Float(0x3a21ff28));  // 4.73239e-37f, 1.03519e-14f, 121589, 9.4495e-15f, 0.000617968f
+path.quadTo(SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3), SkBits2Float(0x2a292827), SkBits2Float(0x962be61d));  // -8.10388e-33f, 0.00148185f, 1.50241e-13f, -1.38859e-25f
+path.lineTo(SkBits2Float(0x295b2d2a), SkBits2Float(0x2d296868));  // 4.86669e-14f, 9.62972e-12f
+path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x898ced7a), SkBits2Float(0x21081f21), SkBits2Float(0x3a7bc003), SkBits2Float(0x47ed7a6a));  // 6.14991e+25f, -3.39271e-33f, 4.61198e-19f, 0.00096035f, 121589
+path.lineTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
+path.close();
+path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
+path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0xb38a281a), SkBits2Float(0x29283ac2));  // -1.2117e+19f, 0.00105459f, -6.43342e-08f, 3.73545e-14f
+path.moveTo(SkBits2Float(0x962be61d), SkBits2Float(0x432a2927));  // -1.38859e-25f, 170.161f
+path.conicTo(SkBits2Float(0x3a2a552a), SkBits2Float(0x3b1e2ab0), SkBits2Float(0x29272021), SkBits2Float(0x3b3ac527), SkBits2Float(0x1fc42236));  // 0.000649768f, 0.00241343f, 3.71093e-14f, 0.00284989f, 8.30658e-20f
+path.cubicTo(SkBits2Float(0x27576c2a), SkBits2Float(0x5921c25d), SkBits2Float(0x51503a70), SkBits2Float(0x12102a10), SkBits2Float(0x633a28d9), SkBits2Float(0x29c80927));  // 2.98959e-15f, 2.8457e+15f, 5.58959e+10f, 4.54902e-28f, 3.43404e+21f, 8.88337e-14f
+path.lineTo(SkBits2Float(0x272927b0), SkBits2Float(0x5b392929));  // 2.3475e-15f, 5.21181e+16f
+path.moveTo(SkBits2Float(0x3a1127b4), SkBits2Float(0x2921ee3b));  // 0.000553723f, 3.59558e-14f
+path.cubicTo(SkBits2Float(0x5e215d3b), SkBits2Float(0x7828ee3a), SkBits2Float(0x8e28b03b), SkBits2Float(0x50783be8), SkBits2Float(0x9e0b8a3a), SkBits2Float(0x555b2d68));  // 2.90688e+18f, 1.37053e+34f, -2.07925e-30f, 1.66587e+10f, -7.38718e-21f, 1.50618e+13f
+path.moveTo(SkBits2Float(0x21081f3f), SkBits2Float(0x9fd4e62a));  // 4.61199e-19f, -9.01663e-20f
+path.cubicTo(SkBits2Float(0x3a293a2a), SkBits2Float(0x0e3bf0c5), SkBits2Float(0x3b29d42a), SkBits2Float(0x0f217265), SkBits2Float(0x2d5d2921), SkBits2Float(0x5568295b));  // 0.000645551f, 2.31655e-30f, 0.00259138f, 7.95994e-30f, 1.25715e-11f, 1.5954e+13f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+static void fuzz763_5(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x2a2a8c55));  // 4.76191e+16f, 1.51477e-13f
+path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b79), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0));  // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f
+path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3));  // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f
+path.cubicTo(SkBits2Float(0xe62a2928), SkBits2Float(0x2a63962b), SkBits2Float(0x68295b2d), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c555b), SkBits2Float(0x001f2a21));  // -2.0089e+23f, 2.02138e-13f, 3.19905e+24f, 9.6297e-12f, 2.49282e-13f, 2.86201e-39f
+path.lineTo(SkBits2Float(0x5b292d55), SkBits2Float(0x2a2a8c55));  // 4.76191e+16f, 1.51477e-13f
+path.close();
+path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x2a2a8c55));  // 4.76191e+16f, 1.51477e-13f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21), SkBits2Float(0x3a7bc003), SkBits2Float(0x47ed7a6a));  // 6.14991e+25f, 2.50338e-13f, 4.61198e-19f, 0.00096035f, 121589
+path.lineTo(SkBits2Float(0x5b292d55), SkBits2Float(0x2a2a8c55));  // 4.76191e+16f, 1.51477e-13f
+path.close();
+path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x2a2a8c55));  // 4.76191e+16f, 1.51477e-13f
+path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3b21), SkBits2Float(0x28ee4f9a), SkBits2Float(0x68293b78));  // -1.2117e+19f, 0.00105462f, 2.64578e-14f, 3.19671e+24f
+path.lineTo(SkBits2Float(0x5b2d2968), SkBits2Float(0x5b2d8c55));  // 4.87407e+16f, 4.88495e+16f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x555b292d), SkBits2Float(0x2a212a8c));  // 1.50606e+13f, 1.43144e-13f
+path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0));  // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f
+path.lineTo(SkBits2Float(0x081f2ad7), SkBits2Float(0x7bc00321));  // 4.78977e-34f, 1.99397e+36f
+path.moveTo(SkBits2Float(0x2a3a2147), SkBits2Float(0xdf212828));  // 1.65317e-13f, -1.16126e+19f
+path.quadTo(SkBits2Float(0x4f1a3a8a), SkBits2Float(0x3ab38a28), SkBits2Float(0x29283ac2), SkBits2Float(0x962be62a));  // 2.58753e+09f, 0.00136978f, 3.73545e-14f, -1.38859e-25f
+path.cubicTo(SkBits2Float(0x272a812a), SkBits2Float(0x3a2a5529), SkBits2Float(0x3b1e2ab0), SkBits2Float(0x29272021), SkBits2Float(0x3b3ac527), SkBits2Float(0x1fc42237));  // 2.36623e-15f, 0.000649768f, 0.00241343f, 3.71093e-14f, 0.00284989f, 8.30658e-20f
+path.cubicTo(SkBits2Float(0x27576c2a), SkBits2Float(0x5921c25d), SkBits2Float(0x51523a70), SkBits2Float(0x12102a10), SkBits2Float(0x633a28d9), SkBits2Float(0x29c80927));  // 2.98959e-15f, 2.8457e+15f, 5.64327e+10f, 4.54902e-28f, 3.43404e+21f, 8.88337e-14f
+path.lineTo(SkBits2Float(0x29292727), SkBits2Float(0x21475b3b));  // 3.75595e-14f, 6.75446e-19f
+path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3));  // -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f
+path.cubicTo(SkBits2Float(0x682d2928), SkBits2Float(0x555b6829), SkBits2Float(0x555b292d), SkBits2Float(0x2a212a8c), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 3.27091e+24f, 1.50775e+13f, 1.50606e+13f, 1.43144e-13f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x295b2ded), SkBits2Float(0x29685568), SkBits2Float(0x8c555b2d), SkBits2Float(0xe61d2a2a), SkBits2Float(0x2a63962b));  // 4.86676e-14f, 5.15884e-14f, -1.64364e-31f, -1.85547e+23f, 2.02138e-13f
+path.conicTo(SkBits2Float(0x5568295b), SkBits2Float(0x5b2d2968), SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a), SkBits2Float(0x4b7bc003));  // 1.5954e+13f, 4.87407e+16f, 5.7784e-19f, 4.61198e-19f, 1.64987e+07f
+path.lineTo(SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21));  // 2.50338e-13f, 4.61198e-19f
+path.conicTo(SkBits2Float(0x6a3a7bc0), SkBits2Float(0x2147ed7a), SkBits2Float(0x28282a3a), SkBits2Float(0x8a3a21df), SkBits2Float(0x27b42a3a));  // 5.63611e+25f, 6.77381e-19f, 9.33503e-15f, -8.96194e-33f, 5.00058e-15f
+path.conicTo(SkBits2Float(0x2921217d), SkBits2Float(0x5e3a3b35), SkBits2Float(0x7828ee3a), SkBits2Float(0x8e28b03b), SkBits2Float(0x783be82a));  // 3.57782e-14f, 3.35484e+18f, 1.37053e+34f, -2.07925e-30f, 1.52448e+34f
+path.conicTo(SkBits2Float(0x8e0b8a3a), SkBits2Float(0x279fd4e6), SkBits2Float(0x7a293a2a), SkBits2Float(0x2a0ef0c5), SkBits2Float(0x653b29d4));  // -1.71996e-30f, 4.43622e-15f, 2.19669e+35f, 1.26957e-13f, 5.52409e+22f
+path.quadTo(SkBits2Float(0x29210f21), SkBits2Float(0x282a085d), SkBits2Float(0xc2ab2127), SkBits2Float(0xa6800028));  // 3.57623e-14f, 9.43871e-15f, -85.5648f, -8.88183e-16f
+path.lineTo(SkBits2Float(0x2a3a2147), SkBits2Float(0xdf212828));  // 1.65317e-13f, -1.16126e+19f
+path.close();
+path.moveTo(SkBits2Float(0x2a3a2147), SkBits2Float(0xdf212828));  // 1.65317e-13f, -1.16126e+19f
+path.quadTo(SkBits2Float(0x216a2770), SkBits2Float(0x2ab73b28), SkBits2Float(0x4b28f427), SkBits2Float(0x283b5b28));  // 7.93345e-19f, 3.25484e-13f, 1.10726e+07f, 1.04004e-14f
+path.lineTo(SkBits2Float(0x2a3a2147), SkBits2Float(0xdf212828));  // 1.65317e-13f, -1.16126e+19f
+path.close();
+path.moveTo(SkBits2Float(0x2a3a2147), SkBits2Float(0xdf212828));  // 1.65317e-13f, -1.16126e+19f
+path.conicTo(SkBits2Float(0xf86d273b), SkBits2Float(0x27e523e3), SkBits2Float(0x2927e0f5), SkBits2Float(0x2ac0e729), SkBits2Float(0x6b492128));  // -1.92402e+34f, 6.35992e-15f, 3.72766e-14f, 3.42665e-13f, 2.43151e+26f
+path.cubicTo(SkBits2Float(0x2f273927), SkBits2Float(0xa83a2c21), SkBits2Float(0xd7122121), SkBits2Float(0x21212921), SkBits2Float(0x3be3db3a), SkBits2Float(0xa9deb63b));  // 1.52089e-10f, -1.03346e-14f, -1.60671e+14f, 5.46034e-19f, 0.00695362f, -9.89039e-14f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+// crbug.com/626164
+static void fuzz763_1c(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+    path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+    path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a));  // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
+    path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000));  // 5.96533e-42f, 9.15715e-24f
+    path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a));  // 3.31432e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f
+    path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff));  // 3.31432e+06f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f
+
+    SkPath path2(path);
+    SkPath dummy;
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp)4, filename);
+}
+
+// crbug.com/626186
+static void fuzz763_1b(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+    path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+    path.cubicTo(SkBits2Float(0x0000ff07), SkBits2Float(0xf9f9ff00), SkBits2Float(0xfe0ef9f4), SkBits2Float(0xd9b105fb), SkBits2Float(0x000000f9), SkBits2Float(0xfe11f901));  // 9.14866e-41f, -1.62257e+35f, -4.75121e+37f, -6.22846e+15f, 3.48923e-43f, -4.85077e+37f
+    path.lineTo(SkBits2Float(0xda1905ed), SkBits2Float(0x3c05fbfb));  // -1.0768e+16f, 0.00817775f
+    path.cubicTo(SkBits2Float(0x3c3c3c3c), SkBits2Float(0x3c3c3c3c), SkBits2Float(0x253c7f00), SkBits2Float(0xfa00d3fa), SkBits2Float(0x250025fe), SkBits2Float(0x00000006));  // 0.011489f, 0.011489f, 1.63494e-16f, -1.67228e+35f, 1.11151e-16f, 8.40779e-45f
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+    path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+    path.quadTo(SkBits2Float(0x3c3c3c3c), SkBits2Float(0xfa253c3c), SkBits2Float(0xfefa00d3), SkBits2Float(0x25fad9df));  // 0.011489f, -2.14488e+35f, -1.66156e+38f, 4.35157e-16f
+    path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+    path.close();
+    path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+    path.lineTo(SkBits2Float(0x8dfefa00), SkBits2Float(0xf0f9fad9));  // -1.57141e-30f, -6.1892e+29f
+    path.cubicTo(SkBits2Float(0x20fe58f9), SkBits2Float(0x0525fbed), SkBits2Float(0x1905ffff), SkBits2Float(0x01f9f9f9), SkBits2Float(0xfbfe0ef9), SkBits2Float(0xfb212fff));  // 4.30882e-19f, 7.80453e-36f, 6.92764e-24f, 9.18268e-38f, -2.63829e+36f, -8.36933e+35f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp)2, filename);
+}
+
+static void fuzz763_1a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+    path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+    path.cubicTo(SkBits2Float(0x154be880), SkBits2Float(0x80000640), SkBits2Float(0x5559a419), SkBits2Float(0x59d55928), SkBits2Float(0x80045959), SkBits2Float(0x40154be8));  // 4.11789e-26f, -2.24208e-42f, 1.49562e+13f, 7.50652e+15f, -3.99394e-40f, 2.33276f
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+    path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+    path.quadTo(SkBits2Float(0x5559a419), SkBits2Float(0x59d55928), SkBits2Float(0xbd595959), SkBits2Float(0x3f3f3f09));  // 1.49562e+13f, 7.50652e+15f, -0.0530637f, 0.747056f
+    path.moveTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0x3f3f3f3f));  // 0.747059f, 0.747059f
+    path.moveTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f));  // 0.747059f, -2.54211e+38f
+    path.lineTo(SkBits2Float(0x09090909), SkBits2Float(0x3038d509));  // 1.6495e-33f, 6.72416e-10f
+    path.conicTo(SkBits2Float(0x5947ffff), SkBits2Float(0x40e88004), SkBits2Float(0x00002059), SkBits2Float(0x28555900), SkBits2Float(0x5959d559));  // 3.51844e+15f, 7.26563f, 1.16042e-41f, 1.18432e-14f, 3.83217e+15f
+    path.lineTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f));  // 0.747059f, -2.54211e+38f
+    path.close();
+    path.moveTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f));  // 0.747059f, -2.54211e+38f
+    path.lineTo(SkBits2Float(0x38d57f4b), SkBits2Float(0x59597f4b));  // 0.000101803f, 3.82625e+15f
+    path.lineTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0xff3f3f3f));  // 0.747059f, -2.54211e+38f
+    path.close();
+    path.moveTo(SkBits2Float(0x384700ff), SkBits2Float(0x0108804b));  // 4.74462e-05f, 2.50713e-38f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp)0, filename);
+}
+
+// crbug.com/627780
+static void fuzz763_3a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x555b292d), SkBits2Float(0x2a212a8c));  // 1.50606e+13f, 1.43144e-13f
+path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0));  // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f
+path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3));  // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f
+path.cubicTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6), SkBits2Float(0x272a812a), SkBits2Float(0x295b2d29), SkBits2Float(0x2a685568), SkBits2Float(0x68295b2d));  // 2.25206e-21f, 5.54035e+21f, 2.36623e-15f, 4.86669e-14f, 2.06354e-13f, 3.19905e+24f
+path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321), SkBits2Float(0x7a6a4b77), SkBits2Float(0x3a214726));  // 2.49282e-13f, 4.78968e-34f, 1.99397e+36f, 3.04132e+35f, 0.000615226f
+path.moveTo(SkBits2Float(0x8adf2028), SkBits2Float(0x3a219a3a));  // -2.14862e-32f, 0.000616464f
+path.quadTo(SkBits2Float(0x3ab38e28), SkBits2Float(0x29283ac2), SkBits2Float(0x2be61d2a), SkBits2Float(0x812a4396));  // 0.0013699f, 3.73545e-14f, 1.63506e-12f, -3.12726e-38f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+// crbug.com/627689
+static void fuzz763_5a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x38bd8610), SkBits2Float(0x00000000));  // 9.03719e-05f, 0
+path.conicTo(SkBits2Float(0x4183d871), SkBits2Float(0x41fea321), SkBits2Float(0xb700ff00), SkBits2Float(0x4240b8b8), SkBits2Float(0x3b058283));  // 16.4807f, 31.8297f, -7.68877e-06f, 48.1804f, 0.0020372f
+path.lineTo(SkBits2Float(0x3a3a3ab8), SkBits2Float(0xb8b8b8b8));  // 0.000710409f, -8.80821e-05f
+path.conicTo(SkBits2Float(0x3a455ec8), SkBits2Float(0xb8b8b8b3), SkBits2Float(0x38b2418d), SkBits2Float(0xb730d014), SkBits2Float(0x3f7ffff3));  // 0.000752908f, -8.80821e-05f, 8.49991e-05f, -1.05389e-05f, 0.999999f
+path.quadTo(SkBits2Float(0x3a51246a), SkBits2Float(0xb6da45a3), SkBits2Float(0x38bc5c3c), SkBits2Float(0x00000000));  // 0.000797814f, -6.50501e-06f, 8.98172e-05f, 0
+path.lineTo(SkBits2Float(0x3a3a3ab8), SkBits2Float(0xb8b8b8b8));  // 0.000710409f, -8.80821e-05f
+path.quadTo(SkBits2Float(0x39a32d2d), SkBits2Float(0x00000000), SkBits2Float(0xb8a13a00), SkBits2Float(0x00000000));  // 0.000311234f, 0, -7.68788e-05f, 0
+path.lineTo(SkBits2Float(0x3a3a3ab8), SkBits2Float(0xb8b8b8b8));  // 0.000710409f, -8.80821e-05f
+path.quadTo(SkBits2Float(0x39ba814c), SkBits2Float(0xb838fed2), SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0.00035573f, -4.41063e-05f, 0, 0
+path.lineTo(SkBits2Float(0x38bd8610), SkBits2Float(0x00000000));  // 9.03719e-05f, 0
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+// crbug.com/627401
+static void fuzz763_2a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0x3e484500), SkBits2Float(0x164f3a30), SkBits2Float(0x49484801), SkBits2Float(0x7d0100c8));  // 0.195576f, 1.67397e-25f, 820352, 1.07172e+37f
+path.conicTo(SkBits2Float(0xff7f36fd), SkBits2Float(0x3e647d01), SkBits2Float(0x0c00f430), SkBits2Float(0x486b6448), SkBits2Float(0x00484848));  // -3.39239e+38f, 0.223133f, 9.93424e-32f, 241041, 6.63809e-39f
+path.lineTo(SkBits2Float(0x4f4f557d), SkBits2Float(0x48480112));  // 3.47849e+09f, 204804
+path.lineTo(SkBits2Float(0xf40c01ff), SkBits2Float(0x45008000));  // -4.43702e+31f, 2056
+path.moveTo(SkBits2Float(0x4bfffa00), SkBits2Float(0x7d4ac859));  // 3.35514e+07f, 1.68465e+37f
+path.conicTo(SkBits2Float(0x7d014f3e), SkBits2Float(0x00f4ff01), SkBits2Float(0x6b64480c), SkBits2Float(0x48484848), SkBits2Float(0x557d0100));  // 1.07426e+37f, 2.24993e-38f, 2.75975e+26f, 205089, 1.73863e+13f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+// crbug.com/627761
+static void fuzz763_2b(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x212a8c55));  // 4.76191e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003));  // 0.00245715f, 1.64987e+07f
+path.lineTo(SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21));  // 2.50338e-13f, 4.61198e-19f
+path.conicTo(SkBits2Float(0x6a3a7bc0), SkBits2Float(0x4721ed7a), SkBits2Float(0x282a3a21), SkBits2Float(0x3a21df28), SkBits2Float(0x4f9a3a8a));  // 5.63611e+25f, 41453.5f, 9.4495e-15f, 0.000617492f, 5.17506e+09f
+path.lineTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003));  // 0.00245715f, 1.64987e+07f
+path.close();
+path.moveTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003));  // 0.00245715f, 1.64987e+07f
+path.cubicTo(SkBits2Float(0x273ac23a), SkBits2Float(0x1d2a2928), SkBits2Float(0x63962be6), SkBits2Float(0x272a812a), SkBits2Float(0x295b2d29), SkBits2Float(0x29685568));  // 2.5918e-15f, 2.25206e-21f, 5.54035e+21f, 2.36623e-15f, 4.86669e-14f, 5.15884e-14f
+path.lineTo(SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321));  // 4.78968e-34f, 1.99397e+36f
+path.lineTo(SkBits2Float(0x282a3a21), SkBits2Float(0x3a21df28));  // 9.4495e-15f, 0.000617492f
+path.lineTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003));  // 0.00245715f, 1.64987e+07f
+path.close();
+path.moveTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003));  // 0.00245715f, 1.64987e+07f
+path.quadTo(SkBits2Float(0x8a4fc29a), SkBits2Float(0x3ab3283a), SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6));  // -1.00033e-32f, 0.00136686f, 2.25206e-21f, 300.343f
+path.moveTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829));  // 4.87419e+16f, 3.71718e-14f
+path.conicTo(SkBits2Float(0x1e2ab03a), SkBits2Float(0x2920213b), SkBits2Float(0x3b3ac527), SkBits2Float(0xc422333b), SkBits2Float(0x6c2a9f1f));  // 9.03617e-21f, 3.5556e-14f, 0.00284989f, -648.8f, 8.25075e+26f
+path.quadTo(SkBits2Float(0xc25d2757), SkBits2Float(0x3a705921), SkBits2Float(0x2a105152), SkBits2Float(0x28d91210));  // -55.2884f, 0.000916855f, 1.2818e-13f, 2.40997e-14f
+path.quadTo(SkBits2Float(0x68295b2d), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21));  // 3.19905e+24f, 9.6297e-12f, 2.49282e-13f, 4.78968e-34f
+path.lineTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829));  // 4.87419e+16f, 3.71718e-14f
+path.close();
+path.moveTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829));  // 4.87419e+16f, 3.71718e-14f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21), SkBits2Float(0xcb7bc003), SkBits2Float(0x47ed7a6a));  // 6.14991e+25f, 2.50338e-13f, 4.61198e-19f, -1.64987e+07f, 121589
+path.lineTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829));  // 4.87419e+16f, 3.71718e-14f
+path.close();
+path.moveTo(SkBits2Float(0x5b2d2a81), SkBits2Float(0x29276829));  // 4.87419e+16f, 3.71718e-14f
+path.quadTo(SkBits2Float(0xdf28282a), SkBits2Float(0x2d8a3a21), SkBits2Float(0x5b682b68), SkBits2Float(0x5b292d55));  // -1.2117e+19f, 1.57146e-11f, 6.53499e+16f, 4.76191e+16f
+path.lineTo(SkBits2Float(0x2a212a8c), SkBits2Float(0x0321081f));  // 1.43144e-13f, 4.7323e-37f
+path.conicTo(SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0), SkBits2Float(0x3a21477a));  // 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f, 0.000615231f
+path.moveTo(SkBits2Float(0x21df2828), SkBits2Float(0x9a3a8a3a));  // 1.51217e-18f, -3.85756e-23f
+path.quadTo(SkBits2Float(0x3ab38a28), SkBits2Float(0x28273ac2), SkBits2Float(0xe61d2a29), SkBits2Float(0x2a63962b));  // 0.00136978f, 9.2831e-15f, -1.85547e+23f, 2.02138e-13f
+path.conicTo(SkBits2Float(0x2d29272a), SkBits2Float(0x5568295b), SkBits2Float(0x5b2d2968), SkBits2Float(0x5b2d6829), SkBits2Float(0x212a8c55));  // 9.61523e-12f, 1.5954e+13f, 4.87407e+16f, 4.88097e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3));  // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f
+path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.close();
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_2c(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x36344a4a));  // 0, 2.68653e-06f
+path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x364a4a4a), SkBits2Float(0x364a4a4a), SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0, 3.01436e-06f, 3.01436e-06f, 0, 0
+path.lineTo(SkBits2Float(0x364a4a4a), SkBits2Float(0x00000000));  // 3.01436e-06f, 0
+path.cubicTo(SkBits2Float(0x364a30f0), SkBits2Float(0x344ac7fb), SkBits2Float(0x3656d432), SkBits2Float(0x34cabb48), SkBits2Float(0x367031a9), SkBits2Float(0x351802f1));  // 3.01288e-06f, 1.88855e-07f, 3.2012e-06f, 3.77617e-07f, 3.57917e-06f, 5.66287e-07f
+path.cubicTo(SkBits2Float(0x36a7b150), SkBits2Float(0x35ab09db), SkBits2Float(0x371874ed), SkBits2Float(0x3604f2c7), SkBits2Float(0x3784e0c7), SkBits2Float(0x36344a51));  // 4.99763e-06f, 1.27434e-06f, 9.08713e-06f, 1.98108e-06f, 1.58403e-05f, 2.68653e-06f
+path.cubicTo(SkBits2Float(0x3743dc9a), SkBits2Float(0x36344a4f), SkBits2Float(0x36fbef33), SkBits2Float(0x36344a4e), SkBits2Float(0x36604a35), SkBits2Float(0x36344a4c));  // 1.16743e-05f, 2.68653e-06f, 7.50823e-06f, 2.68653e-06f, 3.34218e-06f, 2.68653e-06f
+path.cubicTo(SkBits2Float(0x36531715), SkBits2Float(0x36344a4c), SkBits2Float(0x3645e3f5), SkBits2Float(0x36344a4b), SkBits2Float(0x3638b0d4), SkBits2Float(0x36344a4b));  // 3.14549e-06f, 2.68653e-06f, 2.9488e-06f, 2.68653e-06f, 2.75211e-06f, 2.68653e-06f
+path.cubicTo(SkBits2Float(0x35f64120), SkBits2Float(0x36344a4b), SkBits2Float(0x35764124), SkBits2Float(0x36344a4a), SkBits2Float(0x00000000), SkBits2Float(0x36344a4a));  // 1.83474e-06f, 2.68653e-06f, 9.17369e-07f, 2.68653e-06f, 0, 2.68653e-06f
+path.close();
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a));  // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
+path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000));  // 5.96533e-42f, 9.15715e-24f
+path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a));  // 3.31432e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f
+path.cubicTo(SkBits2Float(0x544a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff));  // 3.47532e+12f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, kReverseDifference_SkPathOp, filename);
+}
+
+static void fuzz763_6(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f));  // 0, 5.14279e+25f
+path.cubicTo(SkBits2Float(0x68295b2d), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x68556829));  // 3.19905e+24f, 0, 0, 0, 0, 4.03114e+24f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68555b2a));  // 0, 4.03018e+24f
+path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x67d55b2a), SkBits2Float(0x67296a4b), SkBits2Float(0x67555b2a), SkBits2Float(0x677e1f70), SkBits2Float(0x66d55b2a));  // 0, 2.01509e+24f, 8.00041e+23f, 1.00755e+24f, 1.20006e+24f, 5.03773e+23f
+path.cubicTo(SkBits2Float(0x678f0684), SkBits2Float(0x6684f008), SkBits2Float(0x6798f8ea), SkBits2Float(0x6625a942), SkBits2Float(0x67961914), SkBits2Float(0x65ce709a));  // 1.35084e+24f, 3.1389e+23f, 1.44478e+24f, 1.95578e+23f, 1.41764e+24f, 1.21861e+23f
+path.cubicTo(SkBits2Float(0x679174f7), SkBits2Float(0x63199132), SkBits2Float(0x6756c79f), SkBits2Float(0x606478de), SkBits2Float(0x65682bcf), SkBits2Float(0x00000000));  // 1.3738e+24f, 2.83281e+21f, 1.01427e+24f, 6.58526e+19f, 6.85248e+22f, 0
+path.conicTo(SkBits2Float(0x68295b02), SkBits2Float(0x60f7f28b), SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f), SkBits2Float(0x42784f5a));  // 3.19903e+24f, 1.42932e+20f, 0, 5.14279e+25f, 62.0775f
+path.close();
+path.moveTo(SkBits2Float(0x654d6d10), SkBits2Float(0x00000000));  // 6.06311e+22f, 0
+path.lineTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x00000000));  // 6.14991e+25f, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6a4b7bc0));  // 0, 6.14991e+25f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x3ac23a55), SkBits2Float(0x2a292827));  // 0.00148184f, 1.50241e-13f
+path.lineTo(SkBits2Float(0x63962be6), SkBits2Float(0x272a812a));  // 5.54035e+21f, 2.36623e-15f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_7(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x0f2a312a), SkBits2Float(0xc0032108));  // 8.39112e-30f, -2.04889f
+path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a1f2a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76395e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68345b2d), SkBits2Float(0xf0682955));  // 3.40683e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0xef2a8c55), SkBits2Float(0x295b2d2a), SkBits2Float(0x08685568), SkBits2Float(0x7bc00321));  // 5.76397e-19f, -5.27821e+28f, 4.86669e-14f, 6.99154e-34f, 1.99397e+36f
+path.lineTo(SkBits2Float(0x68345b2d), SkBits2Float(0xf0682955));  // 3.40683e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68345b2d), SkBits2Float(0xf0682955));  // 3.40683e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55));  // 4.85282e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.lineTo(SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a281a4f));  // 0.00105461f, -8.09385e-33f
+path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x272a812a), SkBits2Float(0x3a2a5529));  // 2.25206e-21f, 300.343f, 2.36623e-15f, 0.000649768f
+path.lineTo(SkBits2Float(0x213b1e2a), SkBits2Float(0x27292720));  // 6.3398e-19f, 2.34747e-15f
+path.conicTo(SkBits2Float(0xba1f203a), SkBits2Float(0xc422c538), SkBits2Float(0x215d5927), SkBits2Float(0x70ec2ac2), SkBits2Float(0x2a51523a));  // -0.000607017f, -651.082f, 7.49957e-19f, 5.84721e+29f, 1.85915e-13f
+path.quadTo(SkBits2Float(0x633ad912), SkBits2Float(0x29c80927), SkBits2Float(0x272927b0), SkBits2Float(0x683a5b2d));  // 3.44674e+21f, 8.88337e-14f, 2.3475e-15f, 3.52017e+24f
+path.lineTo(SkBits2Float(0x295b2d68), SkBits2Float(0x29685568));  // 4.86672e-14f, 5.15884e-14f
+path.conicTo(SkBits2Float(0xaa8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x5b2d0321), SkBits2Float(0x68556829), SkBits2Float(0x2a552d29));  // -2.49282e-13f, 4.78968e-34f, 4.86986e+16f, 4.03114e+24f, 1.89339e-13f
+path.cubicTo(SkBits2Float(0x21295b2d), SkBits2Float(0x2a688c5b), SkBits2Float(0x68295b2d), SkBits2Float(0x2d296855), SkBits2Float(0x8c08555b), SkBits2Float(0x2a2a29ca));  // 5.73801e-19f, 2.06544e-13f, 3.19905e+24f, 9.6297e-12f, -1.05027e-31f, 1.51135e-13f
+path.quadTo(SkBits2Float(0x68295b21), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21));  // 3.19904e+24f, 9.6297e-12f, 2.49282e-13f, 4.78968e-34f
+path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.close();
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x5b2d6829), SkBits2Float(0x212a8c55), SkBits2Float(0xed7aba1f), SkBits2Float(0x2a212a8c));  // 6.14991e+25f, 4.88097e+16f, 5.7784e-19f, -4.84977e+27f, 1.43144e-13f
+path.moveTo(SkBits2Float(0x2d212d08), SkBits2Float(0x5568295b));  // 9.16179e-12f, 1.5954e+13f
+path.moveTo(SkBits2Float(0x5529685b), SkBits2Float(0x11295b68));  // 1.16416e+13f, 1.33599e-28f
+path.conicTo(SkBits2Float(0x5b782968), SkBits2Float(0x3a292d55), SkBits2Float(0x2a8c555b), SkBits2Float(0x68295a2d), SkBits2Float(0x2d296855));  // 6.98513e+16f, 0.000645359f, 2.49282e-13f, 3.19897e+24f, 9.6297e-12f
+path.moveTo(SkBits2Float(0x555b8c55), SkBits2Float(0x21682929));  // 1.50872e+13f, 7.86591e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0xac2d8ced), SkBits2Float(0x5b682968), SkBits2Float(0x5b292d55), SkBits2Float(0x212a8c55), SkBits2Float(0x081f282a));  // -2.4663e-12f, 6.53477e+16f, 4.76191e+16f, 5.7784e-19f, 4.78945e-34f
+path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.close();
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2a8ced7a), SkBits2Float(0x03081f21), SkBits2Float(0x6a3a7bc0), SkBits2Float(0x2147ed7a));  // 6.14991e+25f, 2.50338e-13f, 4.00025e-37f, 5.63611e+25f, 6.77381e-19f
+path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.close();
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.quadTo(SkBits2Float(0x2d28282a), SkBits2Float(0x5568295b), SkBits2Float(0x3a21df68), SkBits2Float(0x4f9a3a8a));  // 9.55861e-12f, 1.5954e+13f, 0.000617495f, 5.17506e+09f
+path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.close();
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.cubicTo(SkBits2Float(0x5568c23a), SkBits2Float(0x5b2d2968), SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a), SkBits2Float(0x3a7bc003), SkBits2Float(0x294b2827));  // 1.59951e+13f, 4.87407e+16f, 5.7784e-19f, 4.61198e-19f, 0.00096035f, 4.51099e-14f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void kfuzz2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path1;
+    SkPath path;
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xfafadbfa));  // 0, -6.51268e+35f
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xfafadbfa));  // 0, -6.51268e+35f
+path.cubicTo(SkBits2Float(0xe3000000), SkBits2Float(0xf19e92c7), SkBits2Float(0xf17febcb), SkBits2Float(0xff7febcb), SkBits2Float(0x60600100), SkBits2Float(0x0100ff60));  // -2.36118e+21f, -1.57043e+30f, -1.26726e+30f, -3.40177e+38f, 6.45647e+19f, 2.36931e-38f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xfafadbfa));  // 0, -6.51268e+35f
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xfafadbfa));  // 0, -6.51268e+35f
+path.lineTo(SkBits2Float(0x60601a1d), SkBits2Float(0x60606060));  // 6.4593e+19f, 6.46721e+19f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xfafadbfa));  // 0, -6.51268e+35f
+path.close();
+path.moveTo(SkBits2Float(0xe5e2f300), SkBits2Float(0xee244a40));  // -1.33967e+23f, -1.27113e+28f
+path.moveTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a));  // -0.0011673f, -6.51573e+35f
+path.close();
+path.moveTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a));  // -0.0011673f, -6.51573e+35f
+path.lineTo(SkBits2Float(0xfafafafa), SkBits2Float(0xe30000fa));  // -6.51582e+35f, -2.36125e+21f
+path.conicTo(SkBits2Float(0x92e592e5), SkBits2Float(0xfafafafb), SkBits2Float(0xc4fa0000), SkBits2Float(0x6060fafa), SkBits2Float(0x60606060));  // -1.44881e-27f, -6.51582e+35f, -2000, 6.48462e+19f, 6.46721e+19f
+path.lineTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a));  // -0.0011673f, -6.51573e+35f
+path.close();
+path.moveTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a));  // -0.0011673f, -6.51573e+35f
+path.cubicTo(SkBits2Float(0xe3000000), SkBits2Float(0xf19e92c7), SkBits2Float(0xf17febcb), SkBits2Float(0xff7febcb), SkBits2Float(0xfafafa00), SkBits2Float(0xfafafafa));  // -2.36118e+21f, -1.57043e+30f, -1.26726e+30f, -3.40177e+38f, -6.51572e+35f, -6.51582e+35f
+path.lineTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a));  // -0.0011673f, -6.51573e+35f
+path.close();
+path.moveTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a));  // -0.0011673f, -6.51573e+35f
+path.cubicTo(SkBits2Float(0xe3000000), SkBits2Float(0xe39e92c7), SkBits2Float(0xf17febcb), SkBits2Float(0xff7febcb), SkBits2Float(0xeed0ee9a), SkBits2Float(0x9a98ffca));  // -2.36118e+21f, -5.85032e+21f, -1.26726e+30f, -3.40177e+38f, -3.23307e+28f, -6.3279e-23f
+path.lineTo(SkBits2Float(0xba98ffee), SkBits2Float(0xfafafa1a));  // -0.0011673f, -6.51573e+35f
+path.close();
+SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, kXOR_SkPathOp, filename);
+}
+
+static void fuzz763_10(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x68556829));  // 0, 4.03114e+24f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x6a4b7bc4));  // 6.14991e+25f, 0, 0, 6.14991e+25f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68556829));  // 0, 4.03114e+24f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x5b2d2968), SkBits2Float(0x2a8c8f55));  // 4.87407e+16f, 2.49685e-13f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.conicTo(SkBits2Float(0x682d2fed), SkBits2Float(0x755b6829), SkBits2Float(0x5b292d2b), SkBits2Float(0xc92a8c55), SkBits2Float(0x081f2a21));  // 3.27141e+24f, 2.78131e+32f, 4.76189e+16f, -698565, 4.78968e-34f
+path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.close();
+path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21), SkBits2Float(0x3a7bc003), SkBits2Float(0x47ed7a29));  // 6.14991e+25f, 2.50338e-13f, 4.61198e-19f, 0.00096035f, 121588
+path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.close();
+path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.quadTo(SkBits2Float(0x6829682d), SkBits2Float(0x292d555b), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a29));  // 3.20001e+24f, 3.84878e-14f, 2.49282e-13f, 4.78969e-34f
+path.conicTo(SkBits2Float(0x6a497b19), SkBits2Float(0x218ced7a), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0), SkBits2Float(0x47ed3a7a));  // 6.08939e+25f, 9.54963e-19f, 4.7323e-37f, 5.63611e+25f, 121461
+path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.close();
+path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.quadTo(SkBits2Float(0x282a282a), SkBits2Float(0x8a3a21df), SkBits2Float(0x2728282a), SkBits2Float(0x8a3a2129));  // 9.4456e-15f, -8.96194e-33f, 2.33365e-15f, -8.96181e-33f
+path.quadTo(SkBits2Float(0x8a284f9a), SkBits2Float(0x3a3ac2b3), SkBits2Float(0x2a292827), SkBits2Float(0x962be61d));  // -8.10388e-33f, 0.000712435f, 1.50241e-13f, -1.38859e-25f
+path.lineTo(SkBits2Float(0x272a802a), SkBits2Float(0x2a8c2d29));  // 2.36617e-15f, 2.49003e-13f
+path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.close();
+path.moveTo(SkBits2Float(0x4f9a3a29), SkBits2Float(0x3ab38a28));  // 5.17501e+09f, 0.00136978f
+path.quadTo(SkBits2Float(0xc368305b), SkBits2Float(0x5b296855), SkBits2Float(0x2d8c5568), SkBits2Float(0x1f2a2172));  // -232.189f, 4.7684e+16f, 1.59541e-11f, 3.60266e-20f
+path.lineTo(SkBits2Float(0x29c00321), SkBits2Float(0x5b4b7b13));  // 8.52706e-14f, 5.72747e+16f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_11(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x2a0f312a), SkBits2Float(0xc0032108));  // 1.2718e-13f, -2.04889f
+path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a1f2a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76395e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7b21), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d));  // 5.77848e-19f, 4.7323e-37f, 6.14984e+25f, 9.43289e-15f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68385b2d), SkBits2Float(0x70682955));  // 3.48239e+24f, 2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0xef2a8c55), SkBits2Float(0x295b2d2a), SkBits2Float(0x08685568), SkBits2Float(0x7bc00321));  // 5.76397e-19f, -5.27821e+28f, 4.86669e-14f, 6.99154e-34f, 1.99397e+36f
+path.lineTo(SkBits2Float(0x68385b2d), SkBits2Float(0x70682955));  // 3.48239e+24f, 2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68385b2d), SkBits2Float(0x70682955));  // 3.48239e+24f, 2.87402e+29f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55));  // 4.85282e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.lineTo(SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a281a4f));  // 0.00105461f, -8.09385e-33f
+path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x2a812a3b), SkBits2Float(0x2a552927));  // 2.25206e-21f, 300.343f, 2.29443e-13f, 1.89325e-13f
+path.quadTo(SkBits2Float(0x3b1e2ab0), SkBits2Float(0x29272021), SkBits2Float(0x203a3b27), SkBits2Float(0x22c5381f));  // 0.00241343f, 3.71093e-14f, 1.57744e-19f, 5.34564e-18f
+path.moveTo(SkBits2Float(0x5d27ec2a), SkBits2Float(0x705921c2));  // 7.56256e+17f, 2.68796e+29f
+path.quadTo(SkBits2Float(0x102a5152), SkBits2Float(0x5b2dd912), SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 3.35892e-29f, 4.89338e+16f, 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032127));  // 3.60396e-20f, -2.0489f
+path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0x2a8c684b), SkBits2Float(0xf05b272d), SkBits2Float(0x2a1f1555), SkBits2Float(0x21082a21), SkBits2Float(0x6a4b7b03));  // 3.4979e+24f, 2.49414e-13f, -2.71298e+29f, 1.41294e-13f, 4.61343e-19f, 6.14982e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x2a395b2d), SkBits2Float(0xf0682955));  // 1.64629e-13f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0xef2a8c55), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508));  // 5.76397e-19f, -5.27821e+28f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f
+path.lineTo(SkBits2Float(0x2a395b2d), SkBits2Float(0xf0682955));  // 1.64629e-13f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x2a395b2d), SkBits2Float(0xf0682955));  // 1.64629e-13f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x2a21211f));  // 4.85282e+16f, 1.43112e-13f
+path.lineTo(SkBits2Float(0x03552a8c), SkBits2Float(0x6a4f7b28));  // 6.26439e-37f, 6.27073e+25f
+path.conicTo(SkBits2Float(0x2347ed93), SkBits2Float(0x282a3a21), SkBits2Float(0x3adf2128), SkBits2Float(0x4f1a3a8a), SkBits2Float(0x3ab38a28));  // 1.08381e-17f, 9.4495e-15f, 0.00170234f, 2.58753e+09f, 0.00136978f
+path.lineTo(SkBits2Float(0x2a395b2d), SkBits2Float(0xf0682955));  // 1.64629e-13f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x2a395b2d), SkBits2Float(0xf0682955));  // 1.64629e-13f, -2.87402e+29f
+path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x262a812a), SkBits2Float(0x3a2a5529));  // 2.25206e-21f, 300.343f, 5.91556e-16f, 0.000649768f
+path.lineTo(SkBits2Float(0x213b1e2a), SkBits2Float(0x27292720));  // 6.3398e-19f, 2.34747e-15f
+path.conicTo(SkBits2Float(0x371f203a), SkBits2Float(0xc52a22c4), SkBits2Float(0xc25d27ec), SkBits2Float(0x3a705921), SkBits2Float(0x5210513a));  // 9.48464e-06f, -2722.17f, -55.289f, 0.000916855f, 1.5496e+11f
+path.cubicTo(SkBits2Float(0x63102ad9), SkBits2Float(0x29c80927), SkBits2Float(0x633a27b0), SkBits2Float(0x2909c827), SkBits2Float(0x272927b1), SkBits2Float(0x3a685b2d));  // 2.65942e+21f, 8.88337e-14f, 3.43395e+21f, 3.05937e-14f, 2.3475e-15f, 0.000886368f
+path.moveTo(SkBits2Float(0x682d6829), SkBits2Float(0x29685555));  // 3.27556e+24f, 5.15884e-14f
+path.conicTo(SkBits2Float(0xaa8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x5b2d0321), SkBits2Float(0x68556829), SkBits2Float(0x5b2d2729));  // -2.49282e-13f, 4.78968e-34f, 4.86986e+16f, 4.03114e+24f, 4.87382e+16f
+path.quadTo(SkBits2Float(0x2d685568), SkBits2Float(0x5568295b), SkBits2Float(0x2a552d29), SkBits2Float(0x295b2d27));  // 1.32066e-11f, 1.5954e+13f, 1.89339e-13f, 4.86669e-14f
+path.lineTo(SkBits2Float(0x682d6829), SkBits2Float(0x29685555));  // 3.27556e+24f, 5.15884e-14f
+path.close();
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_12(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6a29082a));  // 0, 5.10868e+25f
+path.conicTo(SkBits2Float(0x6a295ac3), SkBits2Float(0x61bb988e), SkBits2Float(0x6829682d), SkBits2Float(0x5f3ba76a), SkBits2Float(0x42730a87));  // 5.11843e+25f, 4.32567e+20f, 3.20001e+24f, 1.35219e+19f, 60.7603f
+path.conicTo(SkBits2Float(0x67aedf99), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x3f801112));  // 1.65163e+24f, 0, 0, 0, 1.00052f
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 6.14991e+25f, 0, 0, 4.03114e+24f, 1.50617e+13f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68555b2a));  // 0, 4.03018e+24f
+path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x67d55b2a), SkBits2Float(0x67296a4b), SkBits2Float(0x67555b2a), SkBits2Float(0x677e1f70), SkBits2Float(0x66d55b2a));  // 0, 2.01509e+24f, 8.00041e+23f, 1.00755e+24f, 1.20006e+24f, 5.03773e+23f
+path.cubicTo(SkBits2Float(0x678f0684), SkBits2Float(0x6684f008), SkBits2Float(0x6798f8ea), SkBits2Float(0x6625a942), SkBits2Float(0x67961914), SkBits2Float(0x65ce709a));  // 1.35084e+24f, 3.1389e+23f, 1.44478e+24f, 1.95578e+23f, 1.41764e+24f, 1.21861e+23f
+path.cubicTo(SkBits2Float(0x679158b0), SkBits2Float(0x00000000), SkBits2Float(0x67531e34), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 1.37276e+24f, 0, 9.96976e+23f, 0, 0, 0
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.conicTo(SkBits2Float(0x21081f21), SkBits2Float(0x4b7bc003), SkBits2Float(0xed237a6a), SkBits2Float(0x2d682967), SkBits2Float(0x2a8c555b));  // 4.61198e-19f, 1.64987e+07f, -3.16213e+27f, 1.31969e-11f, 2.49282e-13f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x3a6821df), SkBits2Float(0x2a8c3a8a));  // 0.000885514f, 2.49096e-13f
+path.moveTo(SkBits2Float(0x29272a1d), SkBits2Float(0xb03a2a55));  // 3.7118e-14f, -6.77266e-10f
+path.moveTo(SkBits2Float(0x20213b1e), SkBits2Float(0xc5272927));  // 1.36568e-19f, -2674.57f
+path.quadTo(SkBits2Float(0xc422373b), SkBits2Float(0xec2a201f), SkBits2Float(0x21c25d27), SkBits2Float(0x523a7059));  // -648.863f, -8.22676e+26f, 1.31706e-18f, 2.00187e+11f
+path.cubicTo(SkBits2Float(0x12102a10), SkBits2Float(0xe73a28d9), SkBits2Float(0xc8092763), SkBits2Float(0x2927b029), SkBits2Float(0x295b2d27), SkBits2Float(0x2d685568));  // 4.54902e-28f, -8.79114e+23f, -140446, 3.72342e-14f, 4.86669e-14f, 1.32066e-11f
+path.moveTo(SkBits2Float(0x68556809), SkBits2Float(0x555b2d29));  // 4.03113e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a212a), SkBits2Float(0x2d032108));  // 3.60263e-20f, 7.45382e-12f
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x2a552d29));  // 4.03114e+24f, 1.89339e-13f
+path.cubicTo(SkBits2Float(0x21295b2d), SkBits2Float(0x2a528c5b), SkBits2Float(0x284f5b2d), SkBits2Float(0x218aa621), SkBits2Float(0x3f2d2db3), SkBits2Float(0x68293a2a));  // 5.73801e-19f, 1.87004e-13f, 1.15106e-14f, 9.39522e-19f, 0.676479f, 3.19661e+24f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_13(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a), SkBits2Float(0x282a3a21), SkBits2Float(0x3adf2128), SkBits2Float(0x4f1a3a8a));  // 6.14991e+25f, 75739, 9.4495e-15f, 0.00170234f, 2.58753e+09f
+path.lineTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
+path.close();
+path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
+path.cubicTo(SkBits2Float(0x3ac2213a), SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x272a8128), SkBits2Float(0x3a2a5529), SkBits2Float(0x3b1e2ab0));  // 0.00148109f, 2.25206e-21f, 300.343f, 2.36623e-15f, 0.000649768f, 0.00241343f
+path.lineTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
+path.close();
+path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
+path.cubicTo(SkBits2Float(0x3b272927), SkBits2Float(0x381f203a), SkBits2Float(0x2ac422c5), SkBits2Float(0xc25d27ec), SkBits2Float(0x3a705921), SkBits2Float(0x2a105152));  // 0.00255067f, 3.79386e-05f, 3.48407e-13f, -55.289f, 0.000916855f, 1.2818e-13f
+path.quadTo(SkBits2Float(0x633ad912), SkBits2Float(0x29c80927), SkBits2Float(0x272927b0), SkBits2Float(0x68295b2d));  // 3.44674e+21f, 8.88337e-14f, 2.3475e-15f, 3.19905e+24f
+path.lineTo(SkBits2Float(0x295b2d68), SkBits2Float(0x29685568));  // 4.86672e-14f, 5.15884e-14f
+path.conicTo(SkBits2Float(0xaa8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x5b2d0321), SkBits2Float(0x68556829), SkBits2Float(0x2a552d29));  // -2.49282e-13f, 4.78968e-34f, 4.86986e+16f, 4.03114e+24f, 1.89339e-13f
+path.cubicTo(SkBits2Float(0x21295b2d), SkBits2Float(0x2a688c5b), SkBits2Float(0x6829292d), SkBits2Float(0x2d296855), SkBits2Float(0x8c08555b), SkBits2Float(0x2a2a291f));  // 5.73801e-19f, 2.06544e-13f, 3.19536e+24f, 9.6297e-12f, -1.05027e-31f, 1.51133e-13f
+path.conicTo(SkBits2Float(0x68295b21), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321));  // 3.19904e+24f, 9.6297e-12f, 2.49282e-13f, 4.78968e-34f, 1.99397e+36f
+path.lineTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
+path.close();
+path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
+path.lineTo(SkBits2Float(0x5b2d6829), SkBits2Float(0x212a8c55));  // 4.88097e+16f, 5.7784e-19f
+path.conicTo(SkBits2Float(0x8ced7aba), SkBits2Float(0x3f2a212a), SkBits2Float(0x2d212d08), SkBits2Float(0x5568295b), SkBits2Float(0x29685b2d));  // -3.65895e-31f, 0.664569f, 9.16179e-12f, 1.5954e+13f, 5.15934e-14f
+path.lineTo(SkBits2Float(0x68295b68), SkBits2Float(0x2d296855));  // 3.19906e+24f, 9.6297e-12f
+path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21), SkBits2Float(0x6aba7b03), SkBits2Float(0x2147ed7a));  // 6.14991e+25f, 2.50338e-13f, 4.61198e-19f, 1.12721e+26f, 6.77381e-19f
+path.quadTo(SkBits2Float(0x6028282a), SkBits2Float(0x68292ddf), SkBits2Float(0x5b2d555b), SkBits2Float(0x68556829));  // 4.84679e+19f, 3.1957e+24f, 4.8789e+16f, 4.03114e+24f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_14(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0xf45b2d29));  // 4.03114e+24f, -6.94598e+31f
+path.moveTo(SkBits2Float(0x1f2a302a), SkBits2Float(0xc8032108));  // 3.60387e-20f, -134276
+path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf0db684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x302a5b25), SkBits2Float(0xf0685568));  // 3.4979e+24f, -5.43226e+29f, -1.64207e-31f, 5.76527e-19f, 6.19752e-10f, -2.87615e+29f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_15(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x212a8c55));  // 4.76191e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a6b7bc4));  // 4.7323e-37f, 7.11705e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x2c6829c0), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a29));  // 5.77848e-19f, 4.7323e-37f, 3.29924e-12f, 2.49282e-13f, 4.78969e-34f
+path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a6b7bc4));  // 4.7323e-37f, 7.11705e+25f
+path.close();
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a6b7bc4));  // 4.7323e-37f, 7.11705e+25f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a), SkBits2Float(0x282a3a21), SkBits2Float(0xdf218a28), SkBits2Float(0x4f1a3a3a));  // 6.14991e+25f, 75739, 9.4495e-15f, -1.16402e+19f, 2.58751e+09f
+path.quadTo(SkBits2Float(0x3ab38a28), SkBits2Float(0x283ac221), SkBits2Float(0xe6432a29), SkBits2Float(0x2a96812b));  // 0.00136978f, 1.03672e-14f, -2.3041e+23f, 2.6735e-13f
+path.lineTo(SkBits2Float(0x5529272a), SkBits2Float(0x1eb03a2a));  // 1.16241e+13f, 1.86588e-20f
+path.conicTo(SkBits2Float(0x2a272021), SkBits2Float(0x3ac52729), SkBits2Float(0xc422313b), SkBits2Float(0xec2a201f), SkBits2Float(0x21c25d27));  // 1.48437e-13f, 0.00150416f, -648.769f, -8.22676e+26f, 1.31706e-18f
+path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a6b7bc4));  // 4.7323e-37f, 7.11705e+25f
+path.close();
+path.moveTo(SkBits2Float(0x1051523a), SkBits2Float(0xd912102a));  // 4.12813e-29f, -2.56957e+15f
+path.close();
+path.moveTo(SkBits2Float(0x1051523a), SkBits2Float(0xd912102a));  // 4.12813e-29f, -2.56957e+15f
+path.quadTo(SkBits2Float(0xc82763e7), SkBits2Float(0x2927b029), SkBits2Float(0x295b2d27), SkBits2Float(0x2d685568));  // -171408, 3.72342e-14f, 4.86669e-14f, 1.32066e-11f
+path.moveTo(SkBits2Float(0x68556809), SkBits2Float(0x8c555b2d));  // 4.03113e+24f, -1.64364e-31f
+path.moveTo(SkBits2Float(0x081f2a21), SkBits2Float(0x252d0321));  // 4.78968e-34f, 1.50064e-16f
+path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x5b2df068));  // 1.59583e+13f, 4.89595e+16f
+path.quadTo(SkBits2Float(0x2a1f2a8c), SkBits2Float(0x21482a21), SkBits2Float(0x4b7bc003), SkBits2Float(0x8ced3a6a));  // 1.41368e-13f, 6.78184e-19f, 1.64987e+07f, -3.65508e-31f
+path.moveTo(SkBits2Float(0x21481f21), SkBits2Float(0x4b7bc003));  // 6.78038e-19f, 1.64987e+07f
+path.conicTo(SkBits2Float(0x6829ed27), SkBits2Float(0x2d155b2d), SkBits2Float(0x5568295b), SkBits2Float(0x5b2d2968), SkBits2Float(0x2a8c8f55));  // 3.20982e+24f, 8.48991e-12f, 1.5954e+13f, 4.87407e+16f, 2.49685e-13f
+path.lineTo(SkBits2Float(0x21481f21), SkBits2Float(0x4b7bc003));  // 6.78038e-19f, 1.64987e+07f
+path.close();
+path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.conicTo(SkBits2Float(0x682d2fed), SkBits2Float(0x755b6829), SkBits2Float(0x5b292d2b), SkBits2Float(0xc92a8c55), SkBits2Float(0x081f2a21));  // 3.27141e+24f, 2.78131e+32f, 4.76189e+16f, -698565, 4.78968e-34f
+path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.close();
+path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x212aed7a), SkBits2Float(0x0321081f), SkBits2Float(0x293a7bc0), SkBits2Float(0x2147ed7a));  // 6.14991e+25f, 5.79125e-19f, 4.7323e-37f, 4.14076e-14f, 6.77381e-19f
+path.quadTo(SkBits2Float(0x6829682d), SkBits2Float(0x292d555b), SkBits2Float(0x292a8c55), SkBits2Float(0x21081f2a));  // 3.20001e+24f, 3.84878e-14f, 3.78693e-14f, 4.61198e-19f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x218ced7a), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0), SkBits2Float(0x47ed3a7a));  // 6.14991e+25f, 9.54963e-19f, 4.7323e-37f, 5.63611e+25f, 121461
+path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.close();
+path.moveTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.quadTo(SkBits2Float(0x282a282a), SkBits2Float(0x8a3a21df), SkBits2Float(0x2728282a), SkBits2Float(0x8a3a21df));  // 9.4456e-15f, -8.96194e-33f, 2.33365e-15f, -8.96194e-33f
+path.quadTo(SkBits2Float(0x8a284f9a), SkBits2Float(0x3a3ac2b3), SkBits2Float(0x2a292827), SkBits2Float(0x962be61d));  // -8.10388e-33f, 0.000712435f, 1.50241e-13f, -1.38859e-25f
+path.lineTo(SkBits2Float(0x272a802a), SkBits2Float(0x2a8c2d29));  // 2.36617e-15f, 2.49003e-13f
+path.lineTo(SkBits2Float(0xc021211f), SkBits2Float(0x6a4b7b03));  // -2.51765f, 6.14982e+25f
+path.close();
+path.moveTo(SkBits2Float(0x4f9a3a29), SkBits2Float(0x3ab38a28));  // 5.17501e+09f, 0.00136978f
+path.quadTo(SkBits2Float(0xc368305b), SkBits2Float(0x5b296855), SkBits2Float(0x2d8c5568), SkBits2Float(0x1f2a2172));  // -232.189f, 4.7684e+16f, 1.59541e-11f, 3.60266e-20f
+path.lineTo(SkBits2Float(0x29c00321), SkBits2Float(0x5b4b7b13));  // 8.52706e-14f, 5.72747e+16f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_16(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68372d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c552775), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.46012e+24f, -2.71613e+29f, -1.64208e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.61207e+25f
+path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0682955));  // 3.35016e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508));  // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f
+path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0682955));  // 3.35016e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0682955));  // 3.35016e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55));  // 4.85282e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x68385b2d), SkBits2Float(0x555bf055), SkBits2Float(0x2a1f2a8c), SkBits2Float(0x03212a21), SkBits2Float(0x5a4b7bc0));  // 3.48239e+24f, 1.51141e+13f, 1.41368e-13f, 4.7362e-37f, 1.43189e+16f
+path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d));  // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x6e2a1f72), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a));  // 2.49282e-13f, 1.31626e+28f, 4.7323e-37f, 6.14991e+25f, 75739
+path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0x2c213ab3));  // 5.69738e-19f, 0.00105461f, -8.10378e-33f, 2.29121e-12f
+path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2a8ced29));  // 2.25206e-21f, 300.343f, 0.000650423f, 2.50336e-13f
+path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x681aed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a));  // 4.73231e-37f, 6.14991e+25f, 2.92648e+24f, 1.46617e+13f, 9.01175e-21f
+path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2769), SkBits2Float(0xc4371f20), SkBits2Float(0xecc52a22), SkBits2Float(0x21512727));  // 2.22225e-15f, 0.000713936f, -732.486f, -1.90686e+27f, 7.08638e-19f
+path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d));  // 3.19839e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a322a), SkBits2Float(0xc0032108));  // 3.60404e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05bd24b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0xed4b7bc0));  // 4.06458e+24f, -2.72126e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, -3.93594e+27f
+path.conicTo(SkBits2Float(0x212a8c6a), SkBits2Float(0x0329081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d));  // 5.77841e-19f, 4.9674e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955));  // 3.33127e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508));  // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f
+path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955));  // 3.33127e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955));  // 3.33127e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x555b6829), SkBits2Float(0x6c212a8c));  // 1.50775e+13f, 7.79352e+26f
+path.conicTo(SkBits2Float(0x084b0321), SkBits2Float(0x6ac07b2a), SkBits2Float(0x395b2d7a), SkBits2Float(0x5bf05568), SkBits2Float(0x212a3a8c));  // 6.10918e-34f, 1.16348e+26f, 0.000209024f, 1.35296e+17f, 5.76757e-19f
+path.lineTo(SkBits2Float(0x8c558c55), SkBits2Float(0x212a1f2a));  // -1.64512e-31f, 5.76395e-19f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_17(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.61207e+25f
+path.moveTo(SkBits2Float(0x6835282d), SkBits2Float(0xf0682955));  // 3.42196e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2aef552a), SkBits2Float(0x68295b2d), SkBits2Float(0x08682103), SkBits2Float(0x4b7bc055));  // 5.76397e-19f, 4.2514e-13f, 3.19905e+24f, 6.98538e-34f, 1.64988e+07f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55));  // 4.85282e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x68395b2d), SkBits2Float(0x555bf055), SkBits2Float(0x2a1f2a8c), SkBits2Float(0x03212a21), SkBits2Float(0x5a4b7bc0));  // 3.50128e+24f, 1.51141e+13f, 1.41368e-13f, 4.7362e-37f, 1.43189e+16f
+path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d));  // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x212a1f72), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x254793ed));  // 2.49282e-13f, 5.76399e-19f, 4.7323e-37f, 6.14991e+25f, 1.73106e-16f
+path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0xc2213ab3));  // 5.69738e-19f, 0.00105461f, -8.10378e-33f, -40.3073f
+path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2a8ced29));  // 2.25206e-21f, 300.343f, 0.000650423f, 2.50336e-13f
+path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x6829ed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a));  // 4.73231e-37f, 6.14991e+25f, 3.20982e+24f, 1.46617e+13f, 9.01175e-21f
+path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2729), SkBits2Float(0xc4371f20), SkBits2Float(0x16c52a22), SkBits2Float(0x515d27ec));  // 2.22225e-15f, 0.000713932f, -732.486f, 3.18537e-25f, 5.93661e+10f
+path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d));  // 3.19839e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 4.06458e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x2a212a1f));  // 2.49282e-13f, 1.43143e-13f
+path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x8c2aed7a), SkBits2Float(0x2a1f08c0));  // -1.31678e-31f, 1.41251e-13f
+path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x2a8cef55), SkBits2Float(0x68295b2d));  // 2.50351e-13f, 3.19905e+24f
+path.conicTo(SkBits2Float(0x55086821), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x5b2c6829), SkBits2Float(0x21218c55), SkBits2Float(0x2a6c1f03));  // 9.3738e+12f, 6.14991e+25f, 4.85282e+16f, 5.47346e-19f, 2.09718e-13f
+path.lineTo(SkBits2Float(0x2a8cef55), SkBits2Float(0x68295b2d));  // 2.50351e-13f, 3.19905e+24f
+path.close();
+path.moveTo(SkBits2Float(0x2a8cef55), SkBits2Float(0x68295b2d));  // 2.50351e-13f, 3.19905e+24f
+path.lineTo(SkBits2Float(0x6ac07b2a), SkBits2Float(0x395b2d7a));  // 1.16348e+26f, 0.000209024f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_18(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.61207e+25f
+path.moveTo(SkBits2Float(0x6835282d), SkBits2Float(0xf0682955));  // 3.42196e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2aef552a), SkBits2Float(0x68295b2d), SkBits2Float(0x08682103), SkBits2Float(0x4b7bc055));  // 5.76397e-19f, 4.2514e-13f, 3.19905e+24f, 6.98538e-34f, 1.64988e+07f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55));  // 4.85282e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x68395b2d), SkBits2Float(0x555bf055), SkBits2Float(0x2a1f2a8c), SkBits2Float(0x03212a21), SkBits2Float(0x5a4b7bc0));  // 3.50128e+24f, 1.51141e+13f, 1.41368e-13f, 4.7362e-37f, 1.43189e+16f
+path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d));  // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x212a1f72), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x254793ed));  // 2.49282e-13f, 5.76399e-19f, 4.7323e-37f, 6.14991e+25f, 1.73106e-16f
+path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0xc2213ab3));  // 5.69738e-19f, 0.00105461f, -8.10378e-33f, -40.3073f
+path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2a8ced29));  // 2.25206e-21f, 300.343f, 0.000650423f, 2.50336e-13f
+path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x6829ed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a));  // 4.73231e-37f, 6.14991e+25f, 3.20982e+24f, 1.46617e+13f, 9.01175e-21f
+path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2729), SkBits2Float(0xc4371f20), SkBits2Float(0x16c52a22), SkBits2Float(0x515d27ec));  // 2.22225e-15f, 0.000713932f, -732.486f, 3.18537e-25f, 5.93661e+10f
+path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d));  // 3.19839e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 4.06458e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x2a212a1f));  // 2.49282e-13f, 1.43143e-13f
+path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x8c2aed7a), SkBits2Float(0x2a1f08c0));  // -1.31678e-31f, 1.41251e-13f
+
+path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d));  // 3.19839e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 4.06458e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x2a8c54ed), SkBits2Float(0x21081f21), SkBits2Float(0x4b7bc003), SkBits2Float(0x29ed846a), SkBits2Float(0x555b2d28));  // 2.49279e-13f, 4.61198e-19f, 1.64987e+07f, 1.05479e-13f, 1.50617e+13f
+path.conicTo(SkBits2Float(0x68392d5b), SkBits2Float(0xf0682955), SkBits2Float(0x2a1f5b2d), SkBits2Float(0xef552a21), SkBits2Float(0x5b2d2a8c));  // 3.4979e+24f, -2.87402e+29f, 1.41537e-13f, -6.59712e+28f, 4.8742e+16f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_19(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x21081f21), SkBits2Float(0x4b7bc003));  // 4.61198e-19f, 1.64987e+07f
+path.lineTo(SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d));  // 9.43289e-15f, 1.61207e+25f
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955));  // 3.33127e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2aef552a), SkBits2Float(0x68295b2d), SkBits2Float(0x08682103), SkBits2Float(0x4b7bc055));  // 5.76397e-19f, 4.2514e-13f, 3.19905e+24f, 6.98538e-34f, 1.64988e+07f
+path.moveTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55));  // 4.85282e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x68395b2d), SkBits2Float(0x8c5bf055), SkBits2Float(0x2a1f2a55), SkBits2Float(0x03212a21), SkBits2Float(0x5a4b7bc0));  // 3.50128e+24f, -1.69435e-31f, 1.41367e-13f, 4.7362e-37f, 1.43189e+16f
+path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d));  // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x212a1f72), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2547937a));  // 2.49282e-13f, 5.76399e-19f, 4.7323e-37f, 6.14991e+25f, 1.73105e-16f
+path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0xc2213ab3));  // 5.69738e-19f, 0.00105461f, -8.10378e-33f, -40.3073f
+path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2a8ced29));  // 2.25206e-21f, 300.343f, 0.000650423f, 2.50336e-13f
+path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x6829ed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a));  // 4.73231e-37f, 6.14991e+25f, 3.20982e+24f, 1.46617e+13f, 9.01175e-21f
+path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2729), SkBits2Float(0xc4371f20), SkBits2Float(0xecc52a22), SkBits2Float(0x21515d27));  // 2.22225e-15f, 0.000713932f, -732.486f, -1.90686e+27f, 7.09352e-19f
+path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d));  // 3.19839e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 4.06458e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508));  // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f
+path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x555b2c29), SkBits2Float(0x6c212a8c));  // 1.50614e+13f, 7.79352e+26f
+path.conicTo(SkBits2Float(0x084b0321), SkBits2Float(0x6ac07b2a), SkBits2Float(0x395b2d7a), SkBits2Float(0xf05b5568), SkBits2Float(0x212a3a8c));  // 6.10918e-34f, 1.16348e+26f, 0.000209024f, -2.71522e+29f, 5.76757e-19f
+path.conicTo(SkBits2Float(0x290321d9), SkBits2Float(0x555b2d68), SkBits2Float(0x2a8c558c), SkBits2Float(0x2abe2a1f), SkBits2Float(0x7bc00321));  // 2.91172e-14f, 1.50618e+13f, 2.49284e-13f, 3.378e-13f, 1.99397e+36f
+path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x8c2aed7a), SkBits2Float(0x1f2128c0));  // -1.31678e-31f, 3.41268e-20f
+path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.close();
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_20(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.61207e+25f
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955));  // 3.33127e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2a8c552a), SkBits2Float(0x68295b2d), SkBits2Float(0x08682103), SkBits2Float(0x4b7bc055));  // 5.76397e-19f, 2.49281e-13f, 3.19905e+24f, 6.98538e-34f, 1.64988e+07f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55));  // 4.85282e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x68395b2d), SkBits2Float(0x555bf055), SkBits2Float(0x2a1f2a8c), SkBits2Float(0x03212a21), SkBits2Float(0x5a4b7bc0));  // 3.50128e+24f, 1.51141e+13f, 1.41368e-13f, 4.7362e-37f, 1.43189e+16f
+path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d));  // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x6e2a1f72), SkBits2Float(0x0321182a), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a));  // 2.49282e-13f, 1.31626e+28f, 4.73414e-37f, 6.14991e+25f, 75739
+path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0x2c213ab3));  // 5.69738e-19f, 0.00105461f, -8.10378e-33f, 2.29121e-12f
+path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2a8ced29));  // 2.25206e-21f, 300.343f, 0.000650423f, 2.50336e-13f
+path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x6829ed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a));  // 4.73231e-37f, 6.14991e+25f, 3.20982e+24f, 1.46617e+13f, 9.01175e-21f
+path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2769), SkBits2Float(0xc4371f20), SkBits2Float(0xecc52a22), SkBits2Float(0x51282727));  // 2.22225e-15f, 0.000713936f, -732.486f, -1.90686e+27f, 4.51382e+10f
+path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d));  // 3.19839e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x8c555b2d));  // 4.03114e+24f, -1.64364e-31f
+path.moveTo(SkBits2Float(0x081f2a31), SkBits2Float(0xc0032921));  // 4.78969e-34f, -2.04939f
+path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05bd24b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0xed4b7bc0));  // 4.06458e+24f, -2.72126e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, -3.93594e+27f
+path.conicTo(SkBits2Float(0x212a8c6a), SkBits2Float(0x4329081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x5b2d2d55));  // 5.77841e-19f, 169.032f, 6.14991e+25f, 9.43289e-15f, 4.8745e+16f
+path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x3a210368), SkBits2Float(0x7bc05508));  // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 0.000614217f, 1.99729e+36f
+path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x555b6829), SkBits2Float(0x6c212a8c));  // 1.50775e+13f, 7.79352e+26f
+path.lineTo(SkBits2Float(0x5b2d7a6a), SkBits2Float(0xf0556830));  // 4.88298e+16f, -2.64185e+29f
+path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x0321d90a), SkBits2Float(0x555b2d68), SkBits2Float(0x2a8c558c), SkBits2Float(0x212a2a1f), SkBits2Float(0x4b7bc003));  // 4.75628e-37f, 1.50618e+13f, 2.49284e-13f, 5.7654e-19f, 1.64987e+07f
+path.lineTo(SkBits2Float(0x8c2aed7a), SkBits2Float(0x212128c0));  // -1.31678e-31f, 5.46029e-19f
+path.lineTo(SkBits2Float(0x68395b2d), SkBits2Float(0xf0682955));  // 3.50128e+24f, -2.87402e+29f
+path.close();
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_21(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x6828c6f9), SkBits2Float(0x6614dc9e));  // 3.18811e+24f, 1.75745e+23f
+path.cubicTo(SkBits2Float(0x68303469), SkBits2Float(0x661f92fc), SkBits2Float(0x6837d3c3), SkBits2Float(0x662b0eb2), SkBits2Float(0x683fa268), SkBits2Float(0x663759e1));  // 3.32841e+24f, 1.88392e+23f, 3.4724e+24f, 2.01949e+23f, 3.61987e+24f, 2.16463e+23f
+path.cubicTo(SkBits2Float(0x68c4391f), SkBits2Float(0x672c5c9f), SkBits2Float(0x688b20ab), SkBits2Float(0x6804b825), SkBits2Float(0x681ddb5e), SkBits2Float(0x6838dc00));  // 7.4131e+24f, 8.13956e+23f, 5.25609e+24f, 2.507e+24f, 2.98183e+24f, 3.49189e+24f
+path.lineTo(SkBits2Float(0x6828c6f9), SkBits2Float(0x6614dc9e));  // 3.18811e+24f, 1.75745e+23f
+path.close();
+path.moveTo(SkBits2Float(0x68226c73), SkBits2Float(0x660bd15e));  // 3.0681e+24f, 1.65068e+23f
+path.cubicTo(SkBits2Float(0x6823b0e1), SkBits2Float(0x660d990f), SkBits2Float(0x6824f6d5), SkBits2Float(0x660f668c), SkBits2Float(0x68263e4e), SkBits2Float(0x66113632));  // 3.09203e+24f, 1.67169e+23f, 3.11609e+24f, 1.69298e+23f, 3.14025e+24f, 1.71436e+23f
+path.cubicTo(SkBits2Float(0x682715e4), SkBits2Float(0x6612676d), SkBits2Float(0x6827ee22), SkBits2Float(0x66139997), SkBits2Float(0x6828c709), SkBits2Float(0x6614cba5));  // 3.15616e+24f, 1.72843e+23f, 3.17211e+24f, 1.74255e+23f, 3.18812e+24f, 1.75667e+23f
+path.lineTo(SkBits2Float(0x6828d720), SkBits2Float(0x6604a1a2));  // 3.1893e+24f, 1.56583e+23f
+path.cubicTo(SkBits2Float(0x68270421), SkBits2Float(0x6601102c), SkBits2Float(0x68252b97), SkBits2Float(0x65fb1edd), SkBits2Float(0x68234ce5), SkBits2Float(0x65f4367f));  // 3.15485e+24f, 1.52371e+23f, 3.11998e+24f, 1.48235e+23f, 3.08466e+24f, 1.44158e+23f
+path.conicTo(SkBits2Float(0x6822e012), SkBits2Float(0x6602acc5), SkBits2Float(0x68226c73), SkBits2Float(0x660bd15e), SkBits2Float(0x3f7ffa04));  // 3.07663e+24f, 1.54274e+23f, 3.0681e+24f, 1.65068e+23f, 0.999909f
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f));  // 0, 5.14279e+25f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68555b2a));  // 0, 4.03018e+24f
+path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x68617414), SkBits2Float(0x66af1c42), SkBits2Float(0x68624f96), SkBits2Float(0x6757755b), SkBits2Float(0x685b93f2));  // 0, 4.25869e+24f, 4.13468e+23f, 4.27489e+24f, 1.01747e+24f, 4.14771e+24f
+path.cubicTo(SkBits2Float(0x67a63a84), SkBits2Float(0x68fe1c37), SkBits2Float(0x67c05eed), SkBits2Float(0x69930962), SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f));  // 1.56998e+24f, 9.60001e+24f, 1.81689e+24f, 2.22196e+25f, 0, 5.14279e+25f
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f));  // 0, 5.14279e+25f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6a4b7bc4));  // 0, 6.14991e+25f
+path.cubicTo(SkBits2Float(0x6a2c8798), SkBits2Float(0x68f7a144), SkBits2Float(0x6951f5ea), SkBits2Float(0x6796ad55), SkBits2Float(0x683fa268), SkBits2Float(0x663759e1));  // 5.21439e+25f, 9.35519e+24f, 1.58642e+25f, 1.4231e+24f, 3.61987e+24f, 2.16463e+23f
+path.cubicTo(SkBits2Float(0x683871e3), SkBits2Float(0x66253b4f), SkBits2Float(0x6830da01), SkBits2Float(0x66144d3e), SkBits2Float(0x6828d720), SkBits2Float(0x6604a1a2));  // 3.48407e+24f, 1.95071e+23f, 3.34063e+24f, 1.75084e+23f, 3.1893e+24f, 1.56583e+23f
+path.conicTo(SkBits2Float(0x68295b21), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x492bb324));  // 3.19904e+24f, 0, 0, 0, 703282
+path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x677b84f0), SkBits2Float(0x00000000), SkBits2Float(0x68226c73), SkBits2Float(0x660bd15e));  // 0, 0, 1.18777e+24f, 0, 3.0681e+24f, 1.65068e+23f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68156829));  // 0, 2.82222e+24f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68555b2a));  // 0, 4.03018e+24f
+path.lineTo(SkBits2Float(0x673918f3), SkBits2Float(0x681b0f5f));  // 8.74098e+23f, 2.929e+24f
+path.lineTo(SkBits2Float(0x67391759), SkBits2Float(0x681b0fae));  // 8.74068e+23f, 2.92902e+24f
+path.cubicTo(SkBits2Float(0x674384e7), SkBits2Float(0x682e2068), SkBits2Float(0x674db698), SkBits2Float(0x6843893b), SkBits2Float(0x6757755b), SkBits2Float(0x685b93f2));  // 9.23313e+23f, 3.28916e+24f, 9.71453e+23f, 3.69357e+24f, 1.01747e+24f, 4.14771e+24f
+path.cubicTo(SkBits2Float(0x67a63484), SkBits2Float(0x68556bdd), SkBits2Float(0x67f18c5f), SkBits2Float(0x6848eb25), SkBits2Float(0x681ddb5e), SkBits2Float(0x6838dc00));  // 1.56976e+24f, 4.03142e+24f, 2.28136e+24f, 3.79524e+24f, 2.98183e+24f, 3.49189e+24f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f));  // 0, 5.14279e+25f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+static void fuzz763_22(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x68295b2d));  // 0, 3.19905e+24f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x6a3a7bc0), SkBits2Float(0x00000000));  // 5.63611e+25f, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6a034b21));  // 0, 3.9681e+25f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68295b2d));  // 0, 3.19905e+24f
+path.close();
+path.moveTo(SkBits2Float(0x6617da56), SkBits2Float(0x00000000));  // 1.79276e+23f, 0
+path.conicTo(SkBits2Float(0x5e704d09), SkBits2Float(0x5e3a4dfd), SkBits2Float(0x00000000), SkBits2Float(0x65eb62ef), SkBits2Float(0x430fa5e6));  // 4.32888e+18f, 3.35617e+18f, 0, 1.38948e+23f, 143.648f
+path.conicTo(SkBits2Float(0x5e798b32), SkBits2Float(0x627a95c0), SkBits2Float(0x61f5014c), SkBits2Float(0x61fba0fd), SkBits2Float(0x40f8a1a1));  // 4.49538e+18f, 1.15562e+21f, 5.64943e+20f, 5.80217e+20f, 7.76973f
+path.conicTo(SkBits2Float(0x62743d2d), SkBits2Float(0x5e49b862), SkBits2Float(0x6617da56), SkBits2Float(0x00000000), SkBits2Float(0x410ef54c));  // 1.12635e+21f, 3.63387e+18f, 1.79276e+23f, 0, 8.93489f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0x4f9a3a8a), SkBits2Float(0xc28a0d28), SkBits2Float(0x273a3ab3), SkBits2Float(0x8b2a2928));  // 5.17506e+09f, -69.0257f, 2.58445e-15f, -3.27718e-32f
+path.lineTo(SkBits2Float(0x63283ae6), SkBits2Float(0x27282a81));  // 3.1033e+21f, 2.33377e-15f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename);
+}
+
+static void fuzz763_23(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x03210c2a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.73276e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.61207e+25f
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955));  // 3.33127e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2aef552a), SkBits2Float(0x29295b2d), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508));  // 5.76397e-19f, 4.2514e-13f, 3.76046e-14f, 3.04146e+24f, 1.99729e+36f
+path.lineTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955));  // 3.33127e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55));  // 4.85282e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x68395b2d), SkBits2Float(0x8c5bf055), SkBits2Float(0x2a1f2a55), SkBits2Float(0x03212a21), SkBits2Float(0x5a4b7bc0));  // 3.50128e+24f, -1.69435e-31f, 1.41367e-13f, 4.7362e-37f, 1.43189e+16f
+path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d));  // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x08211f72), SkBits2Float(0x032a2a21), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2547937a));  // 2.49282e-13f, 4.84861e-34f, 5.00069e-37f, 6.14991e+25f, 1.73105e-16f
+path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0xc2213ab3));  // 5.69738e-19f, 0.00105461f, -8.10378e-33f, -40.3073f
+path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2a8ced29));  // 2.25206e-21f, 300.343f, 0.000650423f, 2.50336e-13f
+path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x6829ed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a));  // 4.73231e-37f, 6.14991e+25f, 3.20982e+24f, 1.46617e+13f, 9.01175e-21f
+path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2729), SkBits2Float(0xc4371f20), SkBits2Float(0xecc52a22), SkBits2Float(0x21515d27));  // 2.22225e-15f, 0.000713932f, -732.486f, -1.90686e+27f, 7.09352e-19f
+path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0685527));  // 3.35016e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d));  // 3.19839e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 4.06458e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x2a8c54ed), SkBits2Float(0x21081f21), SkBits2Float(0x4b7bc003), SkBits2Float(0x29ed846a), SkBits2Float(0x555b2d28));  // 2.49279e-13f, 4.61198e-19f, 1.64987e+07f, 1.05479e-13f, 1.50617e+13f
+path.conicTo(SkBits2Float(0x68392d5b), SkBits2Float(0xf0682955), SkBits2Float(0x2a1f5b2d), SkBits2Float(0xef552a21), SkBits2Float(0x5b2d2a8c));  // 3.4979e+24f, -2.87402e+29f, 1.41537e-13f, -6.59712e+28f, 4.8742e+16f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_24(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xededed02), SkBits2Float(0xedededed));  // -9.20431e+27f, -9.20445e+27f
+path.close();
+path.moveTo(SkBits2Float(0xededed02), SkBits2Float(0xedededed));  // -9.20431e+27f, -9.20445e+27f
+path.quadTo(SkBits2Float(0x9fb9c16e), SkBits2Float(0x27737375), SkBits2Float(0xb7c5ff00), SkBits2Float(0x00ff9908));  // -7.86706e-20f, 3.37856e-15f, -2.3603e-05f, 2.34729e-38f
+path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.cubicTo(SkBits2Float(0x1616ecec), SkBits2Float(0x2c321616), SkBits2Float(0x3516c616), SkBits2Float(0x6e161616), SkBits2Float(0x4c416033), SkBits2Float(0xf6000000));  // 1.21917e-25f, 2.53076e-12f, 5.61676e-07f, 1.16124e+28f, 5.06923e+07f, -6.49037e+32f
+path.quadTo(SkBits2Float(0x04007f41), SkBits2Float(0xecececec), SkBits2Float(0xecececec), SkBits2Float(0xecec41ec));  // 1.51048e-36f, -2.2914e+27f, -2.2914e+27f, -2.28494e+27f
+path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.close();
+path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.quadTo(SkBits2Float(0x000000ec), SkBits2Float(0xececcc00), SkBits2Float(0x48ececec), SkBits2Float(0x0278806e));  // 3.30706e-43f, -2.29016e+27f, 485223, 1.8257e-37f
+path.lineTo(SkBits2Float(0x72ececec), SkBits2Float(0xecec02ec));  // 9.38559e+30f, -2.28256e+27f
+path.quadTo(SkBits2Float(0xec04007f), SkBits2Float(0xecececec), SkBits2Float(0xecececec), SkBits2Float(0xecec0400));  // -6.38322e+26f, -2.2914e+27f, -2.2914e+27f, -2.2826e+27f
+path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.close();
+path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.quadTo(SkBits2Float(0x000040ec), SkBits2Float(0x3a333300), SkBits2Float(0xecec3333), SkBits2Float(0xececdbec));  // 2.32896e-41f, 0.000683591f, -2.28439e+27f, -2.29076e+27f
+path.lineTo(SkBits2Float(0x3300007f), SkBits2Float(0x33d83333));  // 2.98028e-08f, 1.00676e-07f
+path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.close();
+path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.quadTo(SkBits2Float(0x9e9ea900), SkBits2Float(0x33ececec), SkBits2Float(0xececec33), SkBits2Float(0xec336e6e));  // -1.67988e-20f, 1.10327e-07f, -2.29138e+27f, -8.67677e+26f
+path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.close();
+path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.lineTo(SkBits2Float(0xedededed), SkBits2Float(0xedededed));  // -9.20445e+27f, -9.20445e+27f
+path.lineTo(SkBits2Float(0xecececec), SkBits2Float(0xecececec));  // -2.2914e+27f, -2.2914e+27f
+path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.close();
+path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.lineTo(SkBits2Float(0x01003300), SkBits2Float(0x33d83333));  // 2.35465e-38f, 1.00676e-07f
+path.quadTo(SkBits2Float(0xecec3333), SkBits2Float(0x04eeedec), SkBits2Float(0xe0e0e0e0), SkBits2Float(0x9ee0e0e0));  // -2.28439e+27f, 5.6172e-36f, -1.29634e+20f, -2.38099e-20f
+path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.close();
+path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.cubicTo(SkBits2Float(0x299e9e9e), SkBits2Float(0xecececec), SkBits2Float(0xececb6ec), SkBits2Float(0xf0ececec), SkBits2Float(0x0000ecec), SkBits2Float(0x9ebe6e6e));  // 7.04413e-14f, -2.2914e+27f, -2.28936e+27f, -5.86599e+29f, 8.49916e-41f, -2.01627e-20f
+path.cubicTo(SkBits2Float(0x9e9e9e9e), SkBits2Float(0xe8009e9e), SkBits2Float(0x9e9e9e9e), SkBits2Float(0xecec9e9e), SkBits2Float(0xec3333ec), SkBits2Float(0xececf0ec));  // -1.67945e-20f, -2.42956e+24f, -1.67945e-20f, -2.28844e+27f, -8.66572e+26f, -2.29155e+27f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_25(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x6a4b7bc4));  // 0, 6.14991e+25f
+path.conicTo(SkBits2Float(0x653140d9), SkBits2Float(0x6a4b4f74), SkBits2Float(0x65906630), SkBits2Float(0x6a25a070), SkBits2Float(0x3f6728a2));  // 5.23159e+22f, 6.14468e+25f, 8.52382e+22f, 5.00576e+25f, 0.902964f
+path.cubicTo(SkBits2Float(0x68295bc5), SkBits2Float(0x00000000), SkBits2Float(0x682958ff), SkBits2Float(0x00000000), SkBits2Float(0x68286829), SkBits2Float(0x00000000));  // 3.19909e+24f, 0, 3.19889e+24f, 0, 3.18112e+24f, 0
+path.lineTo(SkBits2Float(0x68555b29), SkBits2Float(0x00000000));  // 4.03018e+24f, 0
+path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x682d2927), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x6829686f));  // 0, 3.27091e+24f, 0, 0, 3.20003e+24f
+path.lineTo(SkBits2Float(0xdf218a28), SkBits2Float(0x00000000));  // -1.16402e+19f, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6a4b7bc4));  // 0, 6.14991e+25f
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.conicTo(SkBits2Float(0x6642c40c), SkBits2Float(0x00000000), SkBits2Float(0x65906630), SkBits2Float(0x6a25a070), SkBits2Float(0x3edcd74d));  // 2.29939e+23f, 0, 8.52382e+22f, 5.00576e+25f, 0.43133f
+path.conicTo(SkBits2Float(0x68295afa), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x4277a57b));  // 3.19903e+24f, 0, 0, 0, 61.9116f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+
+static void fuzz763_26(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc003210a));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68372d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.46012e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.61207e+25f
+path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0682955));  // 3.35016e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508));  // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f
+path.lineTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0682955));  // 3.35016e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68315b2d), SkBits2Float(0xf0682955));  // 3.35016e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55));  // 4.85282e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x68385b2d), SkBits2Float(0x555bf055), SkBits2Float(0x2a1f2a8c), SkBits2Float(0x03212121), SkBits2Float(0x5a4b7bc0));  // 3.48239e+24f, 1.51141e+13f, 1.41368e-13f, 4.73517e-37f, 1.43189e+16f
+path.conicTo(SkBits2Float(0xc08c2aed), SkBits2Float(0x211f2108), SkBits2Float(0x6a4b7b03), SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d));  // -4.38024f, 5.3915e-19f, 6.14982e+25f, 3.20982e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68355b2d), SkBits2Float(0xf0685527));  // 3.42572e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x6e2a1f72), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a));  // 2.49282e-13f, 1.31626e+28f, 4.7323e-37f, 6.14991e+25f, 75739
+path.lineTo(SkBits2Float(0x68355b2d), SkBits2Float(0xf0685527));  // 3.42572e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68355b2d), SkBits2Float(0xf0685527));  // 3.42572e+24f, -2.87614e+29f
+path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x3a8a3adf), SkBits2Float(0x8a284f1a), SkBits2Float(0x2c213ab3));  // 5.69738e-19f, 0.00105461f, -8.10378e-33f, 2.29121e-12f
+path.lineTo(SkBits2Float(0x68355b2d), SkBits2Float(0xf0685527));  // 3.42572e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68355b2d), SkBits2Float(0xf0685527));  // 3.42572e+24f, -2.87614e+29f
+path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a2a812a), SkBits2Float(0x2127ed29));  // 2.25206e-21f, 300.343f, 0.000650423f, 5.68957e-19f
+path.conicTo(SkBits2Float(0x03210831), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x6829ed27), SkBits2Float(0x55555b2d), SkBits2Float(0x1e2a3a2a));  // 4.73231e-37f, 6.14991e+25f, 3.20982e+24f, 1.46617e+13f, 9.01175e-21f
+path.conicTo(SkBits2Float(0x27202140), SkBits2Float(0x3a3b2769), SkBits2Float(0xc4371f20), SkBits2Float(0xecc52a22), SkBits2Float(0x21512727));  // 2.22225e-15f, 0.000713936f, -732.486f, -1.90686e+27f, 7.08638e-19f
+path.lineTo(SkBits2Float(0x68355b2d), SkBits2Float(0xf0685527));  // 3.42572e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x6829523a), SkBits2Float(0x2d555b2d));  // 3.19839e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x5b2d5529));  // 4.03114e+24f, 4.87888e+16f
+path.moveTo(SkBits2Float(0x1f2a322a), SkBits2Float(0xc0032108));  // 3.60404e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68572d55), SkBits2Float(0xf05bd24b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0xed4b7bc0));  // 4.06458e+24f, -2.72126e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, -3.93594e+27f
+path.conicTo(SkBits2Float(0x212a8c6a), SkBits2Float(0x0329081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d));  // 5.77841e-19f, 4.9674e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68385b2d), SkBits2Float(0xf0682955));  // 3.48239e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508));  // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f
+path.lineTo(SkBits2Float(0x68385b2d), SkBits2Float(0xf0682955));  // 3.48239e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68385b2d), SkBits2Float(0xf0682955));  // 3.48239e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x555b1b29), SkBits2Float(0x6c212a8c));  // 1.50569e+13f, 7.79352e+26f
+path.conicTo(SkBits2Float(0x084b0321), SkBits2Float(0x6ac07b2a), SkBits2Float(0x395b2d7a), SkBits2Float(0x8c5bf055), SkBits2Float(0x1f212a3a));  // 6.10918e-34f, 1.16348e+26f, 0.000209024f, -1.69435e-31f, 3.4128e-20f
+path.conicTo(SkBits2Float(0x290321d9), SkBits2Float(0x555b2d68), SkBits2Float(0x2a8c558c), SkBits2Float(0x2a212a1f), SkBits2Float(0x7bc00321));  // 2.91172e-14f, 1.50618e+13f, 2.49284e-13f, 1.43143e-13f, 1.99397e+36f
+path.lineTo(SkBits2Float(0x68385b2d), SkBits2Float(0xf0682955));  // 3.48239e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68385b2d), SkBits2Float(0xf0682955));  // 3.48239e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x8c2aed7a), SkBits2Float(0x1f2128c0));  // -1.31678e-31f, 3.41268e-20f
+path.lineTo(SkBits2Float(0x68385b2d), SkBits2Float(0xf0682955));  // 3.48239e+24f, -2.87402e+29f
+path.close();
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_28(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68302d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a1f2a), SkBits2Float(0x0321082a), SkBits2Float(0x6aa37bc0));  // 3.32789e+24f, -2.71613e+29f, -1.64207e-31f, 5.76395e-19f, 4.7323e-37f, 9.88197e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2d28ed84), SkBits2Float(0x5b2d2955));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.60243e-12f, 4.87406e+16f
+path.moveTo(SkBits2Float(0x6c395b2d), SkBits2Float(0xf0682955));  // 8.96327e+26f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2aef8c55), SkBits2Float(0x68295b2d), SkBits2Float(0x21086855), SkBits2Float(0x4b7bc003));  // 5.76397e-19f, 4.25523e-13f, 3.19905e+24f, 4.62167e-19f, 1.64987e+07f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55));  // 4.85282e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.lineTo(SkBits2Float(0x8a283a28), SkBits2Float(0x284f1a3a));  // -8.09984e-33f, 1.14965e-14f
+path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x272a812a), SkBits2Float(0x3a2a5529));  // 2.25206e-21f, 300.343f, 2.36623e-15f, 0.000649768f
+path.lineTo(SkBits2Float(0x213b1e2a), SkBits2Float(0x27292720));  // 6.3398e-19f, 2.34747e-15f
+path.conicTo(SkBits2Float(0x381f203a), SkBits2Float(0x2ac422c5), SkBits2Float(0xc25d27ec), SkBits2Float(0x3a705921), SkBits2Float(0x2a105152));  // 3.79386e-05f, 3.48407e-13f, -55.289f, 0.000916855f, 1.2818e-13f
+path.quadTo(SkBits2Float(0x633ad912), SkBits2Float(0x29c80927), SkBits2Float(0x272927b0), SkBits2Float(0x683a5b2d));  // 3.44674e+21f, 8.88337e-14f, 2.3475e-15f, 3.52017e+24f
+path.lineTo(SkBits2Float(0x295b2d68), SkBits2Float(0x29685568));  // 4.86672e-14f, 5.15884e-14f
+path.conicTo(SkBits2Float(0xaa8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x5b2d0321), SkBits2Float(0x68556829), SkBits2Float(0x2a552d29));  // -2.49282e-13f, 4.78968e-34f, 4.86986e+16f, 4.03114e+24f, 1.89339e-13f
+path.cubicTo(SkBits2Float(0x21295b2d), SkBits2Float(0x2a688c5b), SkBits2Float(0x68295b2d), SkBits2Float(0x2d296855), SkBits2Float(0x8c08555b), SkBits2Float(0x2a2a29ca));  // 5.73801e-19f, 2.06544e-13f, 3.19905e+24f, 9.6297e-12f, -1.05027e-31f, 1.51135e-13f
+path.quadTo(SkBits2Float(0x68295b21), SkBits2Float(0x2d296855), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21));  // 3.19904e+24f, 9.6297e-12f, 2.49282e-13f, 4.78968e-34f
+path.lineTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.close();
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x5b2d6829), SkBits2Float(0x1f212a55), SkBits2Float(0x8ced7aba), SkBits2Float(0x3f2a212a));  // 6.14991e+25f, 4.88097e+16f, 3.41281e-20f, -3.65895e-31f, 0.664569f
+path.lineTo(SkBits2Float(0x5b2d212d), SkBits2Float(0x2d556829));  // 4.87316e+16f, 1.21308e-11f
+path.moveTo(SkBits2Float(0x68552968), SkBits2Float(0x5568295b));  // 4.02651e+24f, 1.5954e+13f
+path.moveTo(SkBits2Float(0x5b2d2968), SkBits2Float(0x212a8c55));  // 4.87407e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0));  // 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0), SkBits2Float(0x2147ed7a), SkBits2Float(0x28282a3a));  // 5.77848e-19f, 4.7323e-37f, 5.63611e+25f, 6.77381e-19f, 9.33503e-15f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_27(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0x30309ab8), SkBits2Float(0x305b3030), SkBits2Float(0x00f53030), SkBits2Float(0x3a3a0000));  // 6.42483e-10f, 7.97402e-10f, 2.2517e-38f, 0.000709534f
+path.quadTo(SkBits2Float(0xb8b8d5b8), SkBits2Float(0x0b0b0b03), SkBits2Float(0x0b0b0b0b), SkBits2Float(0x3a3a0b0b));  // -8.81361e-05f, 2.67787e-32f, 2.67787e-32f, 0.000709698f
+path.quadTo(SkBits2Float(0xb8b8b8b8), SkBits2Float(0x0b1203b8), SkBits2Float(0x0b0b0b0b), SkBits2Float(0x3a3a2110));  // -8.80821e-05f, 2.81214e-32f, 2.67787e-32f, 0.000710026f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_29(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x743e0000));  // 0, 6.02134e+31f
+path.cubicTo(SkBits2Float(0x74083cf1), SkBits2Float(0x74536e73), SkBits2Float(0x742ac4e4), SkBits2Float(0x7415f5be), SkBits2Float(0x7433ee3c), SkBits2Float(0x7405a69a));  // 4.31756e+31f, 6.70053e+31f, 5.41189e+31f, 4.75242e+31f, 5.70223e+31f, 4.23556e+31f
+path.quadTo(SkBits2Float(0x74360ca0), SkBits2Float(0x7401e10c), SkBits2Float(0x7436a382), SkBits2Float(0x7401cc18));  // 5.76937e+31f, 4.11603e+31f, 5.78805e+31f, 4.11344e+31f
+path.cubicTo(SkBits2Float(0x74374a91), SkBits2Float(0x7401ef19), SkBits2Float(0x74375c84), SkBits2Float(0x7404d9b9), SkBits2Float(0x7437868f), SkBits2Float(0x740bae8a));  // 5.80873e+31f, 4.11777e+31f, 5.81095e+31f, 4.2102e+31f, 5.81616e+31f, 4.42669e+31f
+path.cubicTo(SkBits2Float(0x7437d6c1), SkBits2Float(0x7418b629), SkBits2Float(0x74387e9b), SkBits2Float(0x7433fbc5), SkBits2Float(0x743e2ff7), SkBits2Float(0x74655fa2));  // 5.82609e+31f, 4.83962e+31f, 5.84687e+31f, 5.7039e+31f, 6.02728e+31f, 7.26914e+31f
+path.cubicTo(SkBits2Float(0x741ada75), SkBits2Float(0x74745717), SkBits2Float(0x73c106b4), SkBits2Float(0x74744e64), SkBits2Float(0x00000000), SkBits2Float(0x74744006));  // 4.9075e+31f, 7.74345e+31f, 3.05862e+31f, 7.74237e+31f, 0, 7.74059e+31f
+path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x74746c7c), SkBits2Float(0x74244dce), SkBits2Float(0x7474733e), SkBits2Float(0x74400000), SkBits2Float(0x74747445));  // 0, 7.7461e+31f, 5.207e+31f, 7.74693e+31f, 6.08472e+31f, 7.74706e+31f
+path.cubicTo(SkBits2Float(0x743f5854), SkBits2Float(0x746f3659), SkBits2Float(0x743ebe05), SkBits2Float(0x746a3017), SkBits2Float(0x743e2ff7), SkBits2Float(0x74655fa2));  // 6.06397e+31f, 7.58094e+31f, 6.04486e+31f, 7.42171e+31f, 6.02728e+31f, 7.26914e+31f
+path.cubicTo(SkBits2Float(0x7447a582), SkBits2Float(0x74615dee), SkBits2Float(0x744f74f6), SkBits2Float(0x745c4903), SkBits2Float(0x7455e7e6), SkBits2Float(0x7455d751));  // 6.32705e+31f, 7.14216e+31f, 6.57457e+31f, 6.98112e+31f, 6.77895e+31f, 6.77689e+31f
+path.cubicTo(SkBits2Float(0x74747474), SkBits2Float(0x743750a4), SkBits2Float(0x74747474), SkBits2Float(0x73f46f0d), SkBits2Float(0x74747474), SkBits2Float(0x00000000));  // 7.74708e+31f, 5.80948e+31f, 7.74708e+31f, 3.87321e+31f, 7.74708e+31f, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0xf0682955), SkBits2Float(0x211f5b2d));  // -2.87402e+29f, 5.3992e-19f
+path.moveTo(SkBits2Float(0x2d2aff2d), SkBits2Float(0x74747474));  // 9.72004e-12f, 7.74708e+31f
+path.cubicTo(SkBits2Float(0x7474748e), SkBits2Float(0x74747490), SkBits2Float(0x8c722174), SkBits2Float(0x181f0080), SkBits2Float(0x74c0e520), SkBits2Float(0x747d7463));  // 7.7471e+31f, 7.7471e+31f, -1.86531e-31f, 2.05505e-24f, 1.22262e+32f, 8.0323e+31f
+path.cubicTo(SkBits2Float(0x7b005e4b), SkBits2Float(0xdf3a6a3a), SkBits2Float(0x2a3a2848), SkBits2Float(0x2d2d7821), SkBits2Float(0x8c55212d), SkBits2Float(0x2d2d2d24));  // 6.66526e+35f, -1.34326e+19f, 1.65341e-13f, 9.86059e-12f, -1.64189e-31f, 9.84393e-12f
+path.conicTo(SkBits2Float(0xde28804c), SkBits2Float(0x28e03721), SkBits2Float(0x3329df28), SkBits2Float(0x2d291515), SkBits2Float(0x0568295b));  // -3.03545e+18f, 2.48929e-14f, 3.95513e-08f, 9.61122e-12f, 1.09162e-35f
+path.conicTo(SkBits2Float(0x556a2d21), SkBits2Float(0x21088c2a), SkBits2Float(0x3a333303), SkBits2Float(0x5b293a8a), SkBits2Float(0x6855683b));  // 1.60925e+13f, 4.62641e-19f, 0.000683591f, 4.76336e+16f, 4.03115e+24f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_30(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x1f2108c0), SkBits2Float(0x4b7b0321));  // 3.41003e-20f, 1.64503e+07f
+path.lineTo(SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d));  // 3.20982e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x6e2a1f72), SkBits2Float(0x0321082a), SkBits2Float(0x2a4b7bc0), SkBits2Float(0x68295b2d));  // 2.49282e-13f, 1.31626e+28f, 4.7323e-37f, 1.8073e-13f, 3.19905e+24f
+path.lineTo(SkBits2Float(0x5b2d2968), SkBits2Float(0x212a8c55));  // 4.87407e+16f, 5.7784e-19f
+path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x4b7b28c0));  // 4.7323e-37f, 1.646e+07f
+path.lineTo(SkBits2Float(0x2a8ced7a), SkBits2Float(0x2d081f21));  // 2.50338e-13f, 7.73762e-12f
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x69392d55), SkBits2Float(0x2d5b684b), SkBits2Float(0x8c5527f0), SkBits2Float(0x212a1f2a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 1.39916e+25f, 1.24719e-11f, -1.64209e-31f, 5.76395e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0xed7a6a1f), SkBits2Float(0x3a214793), SkBits2Float(0x3328282a), SkBits2Float(0x3a8a3adf));  // 5.77848e-19f, -4.84372e+27f, 0.000615233f, 3.91521e-08f, 0.00105461f
+path.conicTo(SkBits2Float(0x4be80304), SkBits2Float(0xdcdcdc15), SkBits2Float(0xdcdcdcdc), SkBits2Float(0x71dcdcdc), SkBits2Float(0x6c107164));  // 3.04102e+07f, -4.97332e+17f, -4.97339e+17f, 2.18732e+30f, 6.98483e+26f
+path.conicTo(SkBits2Float(0x6c0f1d6c), SkBits2Float(0x8e406c6e), SkBits2Float(0x6c6c0200), SkBits2Float(0x6c6ce46c), SkBits2Float(0x6c6c6c6c));  // 6.92061e+26f, -2.3718e-30f, 1.14126e+27f, 1.14554e+27f, 1.14327e+27f
+path.lineTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.close();
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.quadTo(SkBits2Float(0x3ab38a28), SkBits2Float(0x3ac22c21), SkBits2Float(0x6c401057), SkBits2Float(0x6d6d6b64));  // 0.00136978f, 0.00148142f, 9.28764e+26f, 4.59236e+27f
+path.cubicTo(SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x286d6d6d), SkBits2Float(0x081d2a29), SkBits2Float(0x6d690321), SkBits2Float(0x6b6b026d));  // 4.59251e+27f, 4.59251e+27f, 1.31799e-14f, 4.7295e-34f, 4.50711e+27f, 2.84109e+26f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_31(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0xd72a8c55), SkBits2Float(0x61081f2a));  // -1.8752e+14f, 1.56938e+20f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a), SkBits2Float(0x282a3a21), SkBits2Float(0xdf3a2128), SkBits2Float(0x471ac575));  // 6.14991e+25f, 75739, 9.4495e-15f, -1.3412e+19f, 39621.5f
+path.lineTo(SkBits2Float(0x28404040), SkBits2Float(0x552a298a));  // 1.06721e-14f, 1.16935e+13f
+path.moveTo(SkBits2Float(0x212c685b), SkBits2Float(0x21081f2a));  // 5.8414e-19f, 4.61198e-19f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x80ed7a3a), SkBits2Float(0x2a3a2147), SkBits2Float(0xdf212828), SkBits2Float(0x4f1a3a3a));  // 6.14991e+25f, -2.18089e-38f, 1.65317e-13f, -1.16126e+19f, 2.58751e+09f
+path.lineTo(SkBits2Float(0x212c685b), SkBits2Float(0x21081f2a));  // 5.8414e-19f, 4.61198e-19f
+path.close();
+path.moveTo(SkBits2Float(0x212c685b), SkBits2Float(0x21081f2a));  // 5.8414e-19f, 4.61198e-19f
+path.cubicTo(SkBits2Float(0x3ac2213a), SkBits2Float(0x432a2928), SkBits2Float(0x96812be6), SkBits2Float(0x272a1d2a), SkBits2Float(0x3a2a3529), SkBits2Float(0x3b1e2ab0));  // 0.00148109f, 170.161f, -2.08688e-25f, 2.3608e-15f, 0.000649291f, 0.00241343f
+path.lineTo(SkBits2Float(0x212c685b), SkBits2Float(0x21081f2a));  // 5.8414e-19f, 4.61198e-19f
+path.close();
+path.moveTo(SkBits2Float(0x212c685b), SkBits2Float(0x21081f2a));  // 5.8414e-19f, 4.61198e-19f
+path.cubicTo(SkBits2Float(0xc5272927), SkBits2Float(0x22383b39), SkBits2Float(0x1051523a), SkBits2Float(0x2927b029), SkBits2Float(0x685b2d27), SkBits2Float(0x5b2d6855));  // -2674.57f, 2.4968e-18f, 4.12813e-29f, 3.72342e-14f, 4.14012e+24f, 4.88099e+16f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_33(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x72c185d5), SkBits2Float(0x72c184e8));  // 7.66623e+30f, 7.66608e+30f
+path.quadTo(SkBits2Float(0x724341bf), SkBits2Float(0x72433fc4), SkBits2Float(0x6d757575), SkBits2Float(0x6d6d6d6d));  // 3.86746e+30f, 3.86731e+30f, 4.74786e+27f, 4.59251e+27f
+path.cubicTo(SkBits2Float(0x6d18b5e5), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6cbe03bd), SkBits2Float(0x6d4b455b), SkBits2Float(0x6c6c69d8), SkBits2Float(0x6d20df31));  // 2.95385e+27f, 4.59251e+27f, 1.83771e+27f, 3.93183e+27f, 1.14323e+27f, 3.11171e+27f
+path.conicTo(SkBits2Float(0x6c6c8b72), SkBits2Float(0x00000000), SkBits2Float(0x6c6c6c6c), SkBits2Float(0x00000000), SkBits2Float(0x400812df));  // 1.14386e+27f, 0, 1.14327e+27f, 0, 2.12615f
+path.quadTo(SkBits2Float(0x72432acb), SkBits2Float(0x72432295), SkBits2Float(0x72c185d5), SkBits2Float(0x72c184e8));  // 3.86568e+30f, 3.86505e+30f, 7.66623e+30f, 7.66608e+30f
+path.close();
+path.moveTo(SkBits2Float(0x72c185d5), SkBits2Float(0x72c184e8));  // 7.66623e+30f, 7.66608e+30f
+path.cubicTo(SkBits2Float(0x74f97d76), SkBits2Float(0x74f97d90), SkBits2Float(0x75381628), SkBits2Float(0x7538182c), SkBits2Float(0x7538153b), SkBits2Float(0x75381835));  // 1.58133e+32f, 1.58133e+32f, 2.33357e+32f, 2.33367e+32f, 2.33353e+32f, 2.33368e+32f
+path.cubicTo(SkBits2Float(0x7538144e), SkBits2Float(0x7538183f), SkBits2Float(0x74f9760f), SkBits2Float(0x74f97ddd), SkBits2Float(0x72c185d5), SkBits2Float(0x72c184e8));  // 2.33348e+32f, 2.33368e+32f, 1.58115e+32f, 1.58134e+32f, 7.66623e+30f, 7.66608e+30f
+path.close();
+path.moveTo(SkBits2Float(0x6c6c69d8), SkBits2Float(0x6d20df31));  // 1.14323e+27f, 3.11171e+27f
+path.conicTo(SkBits2Float(0x6c6c55ae), SkBits2Float(0x6d80b520), SkBits2Float(0x6c6c1071), SkBits2Float(0x6e0f1d6c), SkBits2Float(0x3f96e656));  // 1.14284e+27f, 4.97913e+27f, 1.14154e+27f, 1.1073e+28f, 1.1789f
+path.lineTo(SkBits2Float(0x6a674231), SkBits2Float(0x6c0c3394));  // 6.98936e+25f, 6.77973e+26f
+path.cubicTo(SkBits2Float(0x6b12c63f), SkBits2Float(0x6c881439), SkBits2Float(0x6bba4ae5), SkBits2Float(0x6ced1e23), SkBits2Float(0x6c6c69d8), SkBits2Float(0x6d20df31));  // 1.77439e+26f, 1.31608e+27f, 4.50428e+26f, 2.29326e+27f, 1.14323e+27f, 3.11171e+27f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x6c6b6ba7), SkBits2Float(0x886b6b6b));  // 1.13842e+27f, -7.0844e-34f
+path.quadTo(SkBits2Float(0x0000206b), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6d6d6d6d));  // 1.16294e-41f, 4.59251e+27f, 4.59251e+27f, 4.59251e+27f
+path.conicTo(SkBits2Float(0x3e3e3e3e), SkBits2Float(0xafbcad20), SkBits2Float(0x78787878), SkBits2Float(0x78787829), SkBits2Float(0x78787878));  // 0.185784f, -3.432e-10f, 2.01583e+34f, 2.01582e+34f, 2.01583e+34f
+path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x95066b78));  // 2.01583e+34f, -2.71459e-26f
+path.lineTo(SkBits2Float(0x6c6b6ba7), SkBits2Float(0x886b6b6b));  // 1.13842e+27f, -7.0844e-34f
+path.quadTo(SkBits2Float(0x0000206b), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6d6d6d6d));  // 1.16294e-41f, 4.59251e+27f, 4.59251e+27f, 4.59251e+27f
+path.conicTo(SkBits2Float(0x3e3e3e3e), SkBits2Float(0xafbcad20), SkBits2Float(0x78787878), SkBits2Float(0x78787829), SkBits2Float(0x78787878));  // 0.185784f, -3.432e-10f, 2.01583e+34f, 2.01582e+34f, 2.01583e+34f
+path.lineTo(SkBits2Float(0x8787878f), SkBits2Float(0x87878787));  // -2.03922e-34f, -2.03922e-34f
+path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878));  // 2.01583e+34f, 2.01583e+34f
+path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878));  // 2.01583e+34f, 2.01583e+34f
+path.lineTo(SkBits2Float(0x6c105778), SkBits2Float(0x6d406b64));  // 6.97994e+26f, 3.72193e+27f
+path.cubicTo(SkBits2Float(0x7575756d), SkBits2Float(0x75757575), SkBits2Float(0x75757575), SkBits2Float(0x75757575), SkBits2Float(0x6d6d7575), SkBits2Float(0x6d6d6d6d));  // 3.11156e+32f, 3.11156e+32f, 3.11156e+32f, 3.11156e+32f, 4.59312e+27f, 4.59251e+27f
+path.cubicTo(SkBits2Float(0x6d696d6d), SkBits2Float(0x026d6d6d), SkBits2Float(0x80bc6b6b), SkBits2Float(0xaebcdfd0), SkBits2Float(0x7878bcac), SkBits2Float(0x78787878));  // 4.51514e+27f, 1.74434e-37f, -1.73036e-38f, -8.58901e-11f, 2.01799e+34f, 2.01583e+34f
+path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878));  // 2.01583e+34f, 2.01583e+34f
+path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878));  // 2.01583e+34f, 2.01583e+34f
+path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878));  // 2.01583e+34f, 2.01583e+34f
+path.lineTo(SkBits2Float(0xb4bcacbc), SkBits2Float(0xbcadbcbc));  // -3.51434e-07f, -0.0212082f
+path.moveTo(SkBits2Float(0xa03aacbc), SkBits2Float(0x757575a0));  // -1.5812e-19f, 3.11157e+32f
+path.close();
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_32(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.cubicTo(SkBits2Float(0xdedcdcdc), SkBits2Float(0xdcdcdcdc), SkBits2Float(0xdcdcdcdc), SkBits2Float(0xdcdcdcdc), SkBits2Float(0x55dcdcdc), SkBits2Float(0x29407d7f));  // -7.95742e+18f, -4.97339e+17f, -4.97339e+17f, -4.97339e+17f, 3.03551e+13f, 4.27414e-14f
+path.cubicTo(SkBits2Float(0x7b93ed4b), SkBits2Float(0x29521472), SkBits2Float(0xdfc83c28), SkBits2Float(0x1a3a834e), SkBits2Float(0x6855e84f), SkBits2Float(0xf2f22a80));  // 1.53616e+36f, 4.66471e-14f, -2.88569e+19f, 3.857e-23f, 4.0406e+24f, -9.59318e+30f
+path.moveTo(SkBits2Float(0xe0f2f210), SkBits2Float(0xc3f2eef2));  // -1.40049e+20f, -485.867f
+path.cubicTo(SkBits2Float(0x108ced7a), SkBits2Float(0x7bc00308), SkBits2Float(0x287a6a3a), SkBits2Float(0x242847ed), SkBits2Float(0x2bcb302a), SkBits2Float(0xf21003e8));  // 5.55862e-29f, 1.99396e+36f, 1.39008e-14f, 3.64901e-17f, 1.44374e-12f, -2.85252e+30f
+path.moveTo(SkBits2Float(0x556c0010), SkBits2Float(0x002a8768));  // 1.62178e+13f, 3.90567e-39f
+path.quadTo(SkBits2Float(0xf2f22021), SkBits2Float(0xf2f2f56e), SkBits2Float(0xf2f2f2f2), SkBits2Float(0xf22040d9));  // -9.59158e+30f, -9.62459e+30f, -9.6242e+30f, -3.17414e+30f
+path.lineTo(SkBits2Float(0xc013f2f2), SkBits2Float(0x0000294d));  // -2.3117f, 1.48159e-41f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_34(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x63a95a6c), SkBits2Float(0x6cc8e7e2));  // 6.24803e+21f, 1.94304e+27f
+path.quadTo(SkBits2Float(0x63690f37), SkBits2Float(0x6d0a3d9b), SkBits2Float(0x00000000), SkBits2Float(0x6d3e3e3e));  // 4.29919e+21f, 2.67396e+27f, 0, 3.67984e+27f
+path.conicTo(SkBits2Float(0x6b9253fc), SkBits2Float(0x6c956a8b), SkBits2Float(0x6c6ac798), SkBits2Float(0x692a5d27), SkBits2Float(0x3e56eb72));  // 3.538e+26f, 1.44506e+27f, 1.13532e+27f, 1.28723e+25f, 0.209883f
+path.lineTo(SkBits2Float(0x6c6c586c), SkBits2Float(0x00000000));  // 1.1429e+27f, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.conicTo(SkBits2Float(0x6c8c6c6c), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x6cc8e82a), SkBits2Float(0x5b684b68));  // 1.35809e+27f, 0, 0, 1.94305e+27f, 6.53851e+16f
+path.lineTo(SkBits2Float(0x63a95a6c), SkBits2Float(0x6cc8e7e2));  // 6.24803e+21f, 1.94304e+27f
+path.close();
+path.moveTo(SkBits2Float(0x63a95a6c), SkBits2Float(0x6cc8e7e2));  // 6.24803e+21f, 1.94304e+27f
+path.quadTo(SkBits2Float(0x641ae35f), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 1.14287e+22f, 0, 0, 0
+path.lineTo(SkBits2Float(0x6c6c586c), SkBits2Float(0x00000000));  // 1.1429e+27f, 0
+path.conicTo(SkBits2Float(0x6c6ba1fc), SkBits2Float(0x688c9eb1), SkBits2Float(0x6c6ac798), SkBits2Float(0x692a5d27), SkBits2Float(0x3f7fec32));  // 1.13945e+27f, 5.31247e+24f, 1.13532e+27f, 1.28723e+25f, 0.999698f
+path.lineTo(SkBits2Float(0x63a95a6c), SkBits2Float(0x6cc8e7e2));  // 6.24803e+21f, 1.94304e+27f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x6c3e3e3e), SkBits2Float(0x586c79ff));  // 9.19959e+26f, 1.04003e+15f
+path.quadTo(SkBits2Float(0x6c6c4a6c), SkBits2Float(0x6c6c6c6c), SkBits2Float(0xc83e6c6c), SkBits2Float(0x3e313e3e));  // 1.14263e+27f, 1.14327e+27f, -194994, 0.173089f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_36(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
+path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x2d555b2d));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 1.21279e-11f
+path.moveTo(SkBits2Float(0xe8355b2d), SkBits2Float(0xf0682955));  // -3.42572e+24f, -2.87402e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x8cef552a), SkBits2Float(0x295b2d2a), SkBits2Float(0x68210368), SkBits2Float(0x7bc05508));  // 5.76397e-19f, -3.6875e-31f, 4.86669e-14f, 3.04146e+24f, 1.99729e+36f
+path.lineTo(SkBits2Float(0xe8355b2d), SkBits2Float(0xf0682955));  // -3.42572e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0xe8355b2d), SkBits2Float(0xf0682955));  // -3.42572e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55));  // 4.85282e+16f, 5.7784e-19f
+path.conicTo(SkBits2Float(0x212a081f), SkBits2Float(0x4b7bc003), SkBits2Float(0x5b2d7a6a), SkBits2Float(0xf0556839), SkBits2Float(0x2a8c555b));  // 5.7609e-19f, 1.64987e+07f, 4.88298e+16f, -2.64185e+29f, 2.49282e-13f
+path.conicTo(SkBits2Float(0xf42a212a), SkBits2Float(0x4b7bc003), SkBits2Float(0x2aed7a39), SkBits2Float(0x2108c08c), SkBits2Float(0x7b03211f));  // -5.39162e+31f, 1.64987e+07f, 4.21845e-13f, 4.63334e-19f, 6.80863e+35f
+path.lineTo(SkBits2Float(0xe8355b2d), SkBits2Float(0xf0682955));  // -3.42572e+24f, -2.87402e+29f
+path.close();
+path.moveTo(SkBits2Float(0xe8355b2d), SkBits2Float(0xf0682955));  // -3.42572e+24f, -2.87402e+29f
+path.lineTo(SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d));  // 3.20982e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x212a1f72), SkBits2Float(0x03210807), SkBits2Float(0x6a4b7b28), SkBits2Float(0x4793ed7a));  // 2.49282e-13f, 5.76399e-19f, 4.73229e-37f, 6.14984e+25f, 75739
+path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.quadTo(SkBits2Float(0x282a282a), SkBits2Float(0x8a3adf21), SkBits2Float(0x284f1a3a), SkBits2Float(0x213ab38a));  // 9.4456e-15f, -8.99754e-33f, 1.14965e-14f, 6.32569e-19f
+path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.quadTo(SkBits2Float(0x1d2a2928), SkBits2Float(0x43962be6), SkBits2Float(0x3a20002a), SkBits2Float(0x2a8ced29));  // 2.25206e-21f, 300.343f, 0.000610354f, 2.50336e-13f
+path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0xed210830), SkBits2Float(0xc04b6a03), SkBits2Float(0x68297b27), SkBits2Float(0x55555b2d), SkBits2Float(0x2ab03a2a));  // -3.11481e+27f, -3.17835f, 3.20141e+24f, 1.46617e+13f, 3.13042e-13f
+path.quadTo(SkBits2Float(0x2720213b), SkBits2Float(0x3a3b2729), SkBits2Float(0xc4341f20), SkBits2Float(0xecc52a22));  // 2.22225e-15f, 0.000713932f, -720.486f, -1.90686e+27f
+path.cubicTo(SkBits2Float(0x5921c25d), SkBits2Float(0x29523a70), SkBits2Float(0x555b2d68), SkBits2Float(0x1f212a8c), SkBits2Float(0x0321d90a), SkBits2Float(0x5b2d6829));  // 2.8457e+15f, 4.66801e-14f, 1.50618e+13f, 3.41283e-20f, 4.75628e-37f, 4.88097e+16f
+path.lineTo(SkBits2Float(0x1f2a2a8c), SkBits2Float(0x03210821));  // 3.60341e-20f, 4.7323e-37f
+path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x2eed6a7a), SkBits2Float(0x282a3a21), SkBits2Float(0x3a21df28), SkBits2Float(0x4f1a3a8a), SkBits2Float(0x3ab38a28));  // 1.07964e-10f, 9.4495e-15f, 0.000617492f, 2.58753e+09f, 0.00136978f
+path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.quadTo(SkBits2Float(0xe61d2a28), SkBits2Float(0x2a43962b), SkBits2Float(0x29272a81), SkBits2Float(0x2bb02a55));  // -1.85547e+23f, 1.73716e-13f, 3.71183e-14f, 1.25173e-12f
+path.quadTo(SkBits2Float(0x2720213b), SkBits2Float(0x3ac52729), SkBits2Float(0xc4223b32), SkBits2Float(0x6c2a201f));  // 2.22225e-15f, 0.00150416f, -648.925f, 8.22676e+26f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_35(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x2aed2a8c), SkBits2Float(0x03210a1f));  // 4.21292e-13f, 4.73253e-37f
+path.conicTo(SkBits2Float(0x0000007b), SkBits2Float(0x7474747f), SkBits2Float(0x74747474), SkBits2Float(0x747474c4), SkBits2Float(0x74747474));  // 1.7236e-43f, 7.74709e+31f, 7.74708e+31f, 7.74712e+31f, 7.74708e+31f
+path.quadTo(SkBits2Float(0x74747474), SkBits2Float(0x74747474), SkBits2Float(0x20437474), SkBits2Float(0x43a52b02));  // 7.74708e+31f, 7.74708e+31f, 1.65557e-19f, 330.336f
+path.moveTo(SkBits2Float(0x3a214781), SkBits2Float(0x2128282a));  // 0.000615232f, 5.69738e-19f
+path.lineTo(SkBits2Float(0x4b7bd603), SkBits2Float(0x6cf33b6a));  // 1.65043e+07f, 2.3524e+27f
+path.conicTo(SkBits2Float(0x35778caa), SkBits2Float(0x0000002a), SkBits2Float(0x74742164), SkBits2Float(0x2a3a7474), SkBits2Float(0x4cc22157));  // 9.22194e-07f, 5.88545e-44f, 7.7368e+31f, 1.65605e-13f, 1.0178e+08f
+path.cubicTo(SkBits2Float(0x21479321), SkBits2Float(0x23434cc2), SkBits2Float(0x3a214793), SkBits2Float(0x2128282a), SkBits2Float(0x323adf81), SkBits2Float(0x77291a3a));  // 6.76185e-19f, 1.05872e-17f, 0.000615233f, 5.69738e-19f, 1.08774e-08f, 3.42981e+33f
+path.conicTo(SkBits2Float(0x0000002a), SkBits2Float(0x7474743e), SkBits2Float(0x74747474), SkBits2Float(0x74746474), SkBits2Float(0x74747474));  // 5.88545e-44f, 7.74706e+31f, 7.74708e+31f, 7.7451e+31f, 7.74708e+31f
+path.cubicTo(SkBits2Float(0x21e7fc06), SkBits2Float(0x2a212a59), SkBits2Float(0x0321081f), SkBits2Float(0x00002a35), SkBits2Float(0x74744000), SkBits2Float(0x2974e874));  // 1.57199e-18f, 1.43144e-13f, 4.7323e-37f, 1.5141e-41f, 7.74059e+31f, 5.43805e-14f
+path.cubicTo(SkBits2Float(0x74647474), SkBits2Float(0x74747474), SkBits2Float(0x12ec7474), SkBits2Float(0x4cc22147), SkBits2Float(0x47932343), SkBits2Float(0x282a3a21));  // 7.24002e+31f, 7.74708e+31f, 1.49224e-27f, 1.0178e+08f, 75334.5f, 9.4495e-15f
+path.lineTo(SkBits2Float(0x3a214781), SkBits2Float(0x2128282a));  // 0.000615232f, 5.69738e-19f
+path.close();
+path.moveTo(SkBits2Float(0x3a214781), SkBits2Float(0x2128282a));  // 0.000615232f, 5.69738e-19f
+path.conicTo(SkBits2Float(0x3a323adf), SkBits2Float(0x4977291a), SkBits2Float(0x0000002a), SkBits2Float(0x7474743e), SkBits2Float(0x74747474));  // 0.000679893f, 1.01237e+06f, 5.88545e-44f, 7.74706e+31f, 7.74708e+31f
+path.cubicTo(SkBits2Float(0x74747464), SkBits2Float(0x74747474), SkBits2Float(0x21e7fc06), SkBits2Float(0x2a212a59), SkBits2Float(0x0321081f), SkBits2Float(0x00002a35));  // 7.74708e+31f, 7.74708e+31f, 1.57199e-18f, 1.43144e-13f, 4.7323e-37f, 1.5141e-41f
+path.moveTo(SkBits2Float(0x74747440), SkBits2Float(0x742974e8));  // 7.74706e+31f, 5.3703e+31f
+path.cubicTo(SkBits2Float(0x74746474), SkBits2Float(0x74747474), SkBits2Float(0xd912ec74), SkBits2Float(0x553a3728), SkBits2Float(0x29202a8c), SkBits2Float(0x5555201b));  // 7.7451e+31f, 7.74708e+31f, -2.58471e+15f, 1.27966e+13f, 3.5564e-14f, 1.46459e+13f
+path.moveTo(SkBits2Float(0x31292768), SkBits2Float(0x212d2aff));  // 2.46151e-09f, 5.86716e-19f
+path.quadTo(SkBits2Float(0x2128282a), SkBits2Float(0x323adf81), SkBits2Float(0x77291a3a), SkBits2Float(0x00002a49));  // 5.69738e-19f, 1.08774e-08f, 3.42981e+33f, 1.51691e-41f
+path.moveTo(SkBits2Float(0x7474743e), SkBits2Float(0x74747474));  // 7.74706e+31f, 7.74708e+31f
+path.cubicTo(SkBits2Float(0x74747464), SkBits2Float(0x74747474), SkBits2Float(0x21e7fc06), SkBits2Float(0x2a212a59), SkBits2Float(0x0321081f), SkBits2Float(0x00002a35));  // 7.74708e+31f, 7.74708e+31f, 1.57199e-18f, 1.43144e-13f, 4.7323e-37f, 1.5141e-41f
+path.moveTo(SkBits2Float(0x74747440), SkBits2Float(0x74747474));  // 7.74706e+31f, 7.74708e+31f
+path.cubicTo(SkBits2Float(0x74747464), SkBits2Float(0x74747474), SkBits2Float(0x43747474), SkBits2Float(0xa52b0220), SkBits2Float(0x47812a43), SkBits2Float(0x282a3a21));  // 7.74708e+31f, 7.74708e+31f, 244.455f, -1.48326e-16f, 66132.5f, 9.4495e-15f
+path.lineTo(SkBits2Float(0x74747440), SkBits2Float(0x74747474));  // 7.74706e+31f, 7.74708e+31f
+path.close();
+path.moveTo(SkBits2Float(0x74747440), SkBits2Float(0x74747474));  // 7.74706e+31f, 7.74708e+31f
+path.conicTo(SkBits2Float(0x3a323adf), SkBits2Float(0x19433b1a), SkBits2Float(0x5921e7fc), SkBits2Float(0x1f2a212a), SkBits2Float(0x35032108));  // 0.000679893f, 1.00932e-23f, 2.84828e+15f, 3.60263e-20f, 4.88494e-07f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_37(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x5b2d3368));  // 1.59583e+13f, 4.87517e+16f
+path.conicTo(SkBits2Float(0x5b2d555b), SkBits2Float(0x68275b2d), SkBits2Float(0x21685527), SkBits2Float(0x0321082a), SkBits2Float(0x6ab485c0));  // 4.8789e+16f, 3.16127e+24f, 7.87174e-19f, 4.7323e-37f, 1.09119e+26f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x5b2d2d55));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 4.8745e+16f
+path.moveTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5b));  // 3.50084e+24f, -2.87489e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2a8cef2a), SkBits2Float(0x682d2953), SkBits2Float(0xee682103), SkBits2Float(0x4b7bc055));  // 5.76397e-19f, 2.50349e-13f, 3.27093e+24f, -1.79601e+28f, 1.64988e+07f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55));  // 4.85282e+16f, 5.7784e-19f
+path.conicTo(SkBits2Float(0x4b03213b), SkBits2Float(0xc07b2a08), SkBits2Float(0x5b2d7a6a), SkBits2Float(0xf0556830), SkBits2Float(0x2a8c555b));  // 8.59372e+06f, -3.92444f, 4.88298e+16f, -2.64185e+29f, 2.49282e-13f
+path.conicTo(SkBits2Float(0x0321212a), SkBits2Float(0x4b7bd2c0), SkBits2Float(0xed7ac039), SkBits2Float(0x2f218c08), SkBits2Float(0x1f037b2a));  // 4.73517e-37f, 1.65035e+07f, -4.85023e+27f, 1.46926e-10f, 2.78422e-20f
+path.lineTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5b));  // 3.50084e+24f, -2.87489e+29f
+path.close();
+path.moveTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5b));  // 3.50084e+24f, -2.87489e+29f
+path.lineTo(SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d));  // 3.20982e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x721f2a5b), SkBits2Float(0x212a8c55), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7b28), SkBits2Float(0x4793ed7a));  // 3.1526e+30f, 5.7784e-19f, 4.7323e-37f, 6.14984e+25f, 75739
+path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.quadTo(SkBits2Float(0x28282a2a), SkBits2Float(0x2c682921), SkBits2Float(0x8c555bf6), SkBits2Float(0x6d03de30));  // 9.33502e-15f, 3.2992e-12f, -1.64366e-31f, 2.5507e+27f
+path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x081f2a21));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 4.78968e-34f
+path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0xdf93ed7a), SkBits2Float(0x1a3a803a), SkBits2Float(0xb38a294f), SkBits2Float(0x3ac2213a));  // 6.14991e+25f, -2.13186e+19f, 3.85675e-23f, -6.43364e-08f, 0.00148109f
+path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0xe62b291d), SkBits2Float(0x2a812a43), SkBits2Float(0x8ced093a), SkBits2Float(0xb38a5c5c), SkBits2Float(0x3ac2213a));  // -2.02071e+23f, 2.29443e-13f, -3.65212e-31f, -6.44293e-08f, 0.00148109f
+path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.lineTo(SkBits2Float(0x8ced293a), SkBits2Float(0x5c5c5c5c));  // -3.65404e-31f, 2.48104e+17f
+path.moveTo(SkBits2Float(0x21081f21), SkBits2Float(0x4b7bc003));  // 4.61198e-19f, 1.64987e+07f
+path.lineTo(SkBits2Float(0x2829ed84), SkBits2Float(0x5b2d2d55));  // 9.43289e-15f, 4.8745e+16f
+path.moveTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5a));  // 3.50084e+24f, -2.87489e+29f
+path.lineTo(SkBits2Float(0x682d2952), SkBits2Float(0xee682103));  // 3.27093e+24f, -1.79601e+28f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x2a3b0355));  // 4.85282e+16f, 1.66101e-13f
+path.lineTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5a));  // 3.50084e+24f, -2.87489e+29f
+path.close();
+path.moveTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5a));  // 3.50084e+24f, -2.87489e+29f
+path.conicTo(SkBits2Float(0x084b218c), SkBits2Float(0x6ac07b2a), SkBits2Float(0x395b2d7a), SkBits2Float(0x5bf05568), SkBits2Float(0x1f2a8c55));  // 6.11275e-34f, 1.16348e+26f, 0.000209024f, 1.35296e+17f, 3.6115e-20f
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.cubicTo(SkBits2Float(0xbcb4bcac), SkBits2Float(0x000029ff), SkBits2Float(0x010000bc), SkBits2Float(0x00bcbc00), SkBits2Float(0xbebcbcbc), SkBits2Float(0xb6aebcae));  // -0.0220626f, 1.50654e-41f, 2.35104e-38f, 1.73325e-38f, -0.368627f, -5.20757e-06f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_38(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.conicTo(SkBits2Float(0x5b682968), SkBits2Float(0x5b292d11), SkBits2Float(0x212a8c55), SkBits2Float(0x555b2d2d), SkBits2Float(0x52525268));  // 6.53477e+16f, 4.76188e+16f, 5.7784e-19f, 1.50617e+13f, 2.25831e+11f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad));  // -1.43243e-16f, 3.88004e+11f
+path.close();
+path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad));  // -1.43243e-16f, 3.88004e+11f
+path.quadTo(SkBits2Float(0x72727270), SkBits2Float(0x52525272), SkBits2Float(0x2ac05252), SkBits2Float(0x727fb721));  // 4.80216e+30f, 2.25832e+11f, 3.41632e-13f, 5.06496e+30f
+path.lineTo(SkBits2Float(0x73727322), SkBits2Float(0x555b2d29));  // 1.92088e+31f, 1.50617e+13f
+path.lineTo(SkBits2Float(0xab2a212e), SkBits2Float(0x7a27872a));  // -6.04422e-13f, 2.17464e+35f
+path.moveTo(SkBits2Float(0x25fffefb), SkBits2Float(0x7bc00321));  // 4.44082e-16f, 1.99397e+36f
+path.quadTo(SkBits2Float(0x52524852), SkBits2Float(0x72525228), SkBits2Float(0x72727272), SkBits2Float(0x3a727272));  // 2.25789e+11f, 4.16584e+30f, 4.80216e+30f, 0.000924862f
+path.lineTo(SkBits2Float(0x25fffefb), SkBits2Float(0x7bc00321));  // 4.44082e-16f, 1.99397e+36f
+path.close();
+path.moveTo(SkBits2Float(0x25fffefb), SkBits2Float(0x7bc00321));  // 4.44082e-16f, 1.99397e+36f
+path.quadTo(SkBits2Float(0x2a292827), SkBits2Float(0x962b0080), SkBits2Float(0x5252752a), SkBits2Float(0x72725252));  // 1.50241e-13f, -1.38134e-25f, 2.25977e+11f, 4.79967e+30f
+path.quadTo(SkBits2Float(0x72725252), SkBits2Float(0x52525272), SkBits2Float(0x72525252), SkBits2Float(0x72727272));  // 4.79967e+30f, 2.25832e+11f, 4.16585e+30f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0xda000072), SkBits2Float(0x52525ada), SkBits2Float(0x52525252));  // 4.80215e+30f, -9.00732e+15f, 2.25867e+11f, 2.25831e+11f
+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525272), SkBits2Float(0x72525248), SkBits2Float(0x72727272));  // 4.80216e+30f, 2.25832e+11f, 4.16584e+30f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0xda007b72), SkBits2Float(0x52525ada), SkBits2Float(0x52525252));  // 4.80215e+30f, -9.04113e+15f, 2.25867e+11f, 2.25831e+11f
+path.quadTo(SkBits2Float(0x86727272), SkBits2Float(0x5252528d), SkBits2Float(0x72525252), SkBits2Float(0x72727227));  // -4.55992e-35f, 2.25832e+11f, 4.16585e+30f, 4.80214e+30f
+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x29217272), SkBits2Float(0xc003211c), SkBits2Float(0x556a4b7b));  // 4.80216e+30f, 3.58484e-14f, -2.0489f, 1.61006e+13f
+path.moveTo(SkBits2Float(0x72557272), SkBits2Float(0x00727272));  // 4.22775e+30f, 1.05103e-38f
+path.moveTo(SkBits2Float(0x5a61dada), SkBits2Float(0x52525252));  // 1.58931e+16f, 2.25831e+11f
+path.close();
+path.moveTo(SkBits2Float(0x5a61dada), SkBits2Float(0x52525252));  // 1.58931e+16f, 2.25831e+11f
+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x3a727272), SkBits2Float(0x28273ac2), SkBits2Float(0x00802a29));  // 4.80216e+30f, 0.000924862f, 9.2831e-15f, 1.17701e-38f
+path.lineTo(SkBits2Float(0x52752a96), SkBits2Float(0x72525252));  // 2.63245e+11f, 4.16585e+30f
+path.quadTo(SkBits2Float(0x72525272), SkBits2Float(0x52527272), SkBits2Float(0x52525252), SkBits2Float(0x72727272));  // 4.16586e+30f, 2.25966e+11f, 2.25831e+11f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x72725572), SkBits2Float(0x00007272), SkBits2Float(0x525adada), SkBits2Float(0x52525252));  // 4.79991e+30f, 4.10552e-41f, 2.34994e+11f, 2.25831e+11f
+path.lineTo(SkBits2Float(0x5a61dada), SkBits2Float(0x52525252));  // 1.58931e+16f, 2.25831e+11f
+path.close();
+path.moveTo(SkBits2Float(0x5a61dada), SkBits2Float(0x52525252));  // 1.58931e+16f, 2.25831e+11f
+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525272), SkBits2Float(0x72525248), SkBits2Float(0x72727272));  // 4.80216e+30f, 2.25832e+11f, 4.16584e+30f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0xda007b72), SkBits2Float(0x52525ada), SkBits2Float(0x72525252));  // 4.80215e+30f, -9.04113e+15f, 2.25867e+11f, 4.16585e+30f
+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x72727252), SkBits2Float(0xda007b72), SkBits2Float(0x52525ada));  // 4.80216e+30f, 4.80215e+30f, -9.04113e+15f, 2.25867e+11f
+path.lineTo(SkBits2Float(0x5a61dada), SkBits2Float(0x52525252));  // 1.58931e+16f, 2.25831e+11f
+path.close();
+path.moveTo(SkBits2Float(0x5a61dada), SkBits2Float(0x52525252));  // 1.58931e+16f, 2.25831e+11f
+path.quadTo(SkBits2Float(0x86727272), SkBits2Float(0x5252528d), SkBits2Float(0x72525252), SkBits2Float(0x72727227));  // -4.55992e-35f, 2.25832e+11f, 4.16585e+30f, 4.80214e+30f
+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x29217272), SkBits2Float(0xc003211c), SkBits2Float(0x556a4b7b));  // 4.80216e+30f, 3.58484e-14f, -2.0489f, 1.61006e+13f
+path.moveTo(SkBits2Float(0x72557272), SkBits2Float(0x00727272));  // 4.22775e+30f, 1.05103e-38f
+path.moveTo(SkBits2Float(0x525adada), SkBits2Float(0x52525252));  // 2.34994e+11f, 2.25831e+11f
+path.close();
+path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad));  // -1.43243e-16f, 3.88004e+11f
+path.close();
+path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad));  // -1.43243e-16f, 3.88004e+11f
+path.quadTo(SkBits2Float(0x72727270), SkBits2Float(0x52525272), SkBits2Float(0x72525252), SkBits2Float(0x72727272));  // 4.80216e+30f, 2.25832e+11f, 4.16585e+30f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0xda007b72), SkBits2Float(0x52525ada), SkBits2Float(0x52525252));  // 4.80215e+30f, -9.04113e+15f, 2.25867e+11f, 2.25831e+11f
+path.quadTo(SkBits2Float(0x52525272), SkBits2Float(0x3b3b0052), SkBits2Float(0x5b2d553a), SkBits2Float(0x68556829));  // 2.25832e+11f, 0.00285341f, 4.87889e+16f, 4.03114e+24f
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x52528c55), SkBits2Float(0x29215252));  // 2.26074e+11f, 3.58206e-14f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_41(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0x7a057c72), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x055f0089));  // 1.73275e+35f, 4.80216e+30f, 4.34505e+30f, 1.04855e-35f
+path.quadTo(SkBits2Float(0x00057272), SkBits2Float(0x72ff0000), SkBits2Float(0xba405e72), SkBits2Float(0x031b0074));  // 5.00233e-40f, 1.01016e+31f, -0.000733829f, 4.55509e-37f
+path.lineTo(SkBits2Float(0x664af700), SkBits2Float(0x56397d39));  // 2.39619e+23f, 5.09869e+13f
+path.quadTo(SkBits2Float(0x7a057273), SkBits2Float(0x057300e4), SkBits2Float(0x257c0c9f), SkBits2Float(0x72400006));  // 1.73224e+35f, 1.1426e-35f, 2.18618e-16f, 3.80295e+30f
+path.quadTo(SkBits2Float(0xba5b5e72), SkBits2Float(0x030000ff), SkBits2Float(0x74ba00e8), SkBits2Float(0xe8ec4000));  // -0.000836826f, 3.7617e-37f, 1.17894e+32f, -8.92527e+24f
+path.moveTo(SkBits2Float(0x39724aff), SkBits2Float(0x7200397d));  // 0.000231069f, 2.53975e+30f
+path.quadTo(SkBits2Float(0x827a0572), SkBits2Float(0x08727272), SkBits2Float(0x08080808), SkBits2Float(0x08080808));  // -1.83687e-37f, 7.29588e-34f, 4.09355e-34f, 4.09355e-34f
+path.lineTo(SkBits2Float(0x08080808), SkBits2Float(0x08080808));  // 4.09355e-34f, 4.09355e-34f
+path.lineTo(SkBits2Float(0x08080808), SkBits2Float(0x08080808));  // 4.09355e-34f, 4.09355e-34f
+path.conicTo(SkBits2Float(0x72728c08), SkBits2Float(0x5b5e7272), SkBits2Float(0x000074ba), SkBits2Float(0x03f8e300), SkBits2Float(0x5aff00e8));  // 4.80414e+30f, 6.26133e+16f, 4.18736e-41f, 1.46282e-36f, 3.58886e+16f
+path.quadTo(SkBits2Float(0x00800039), SkBits2Float(0x72100039), SkBits2Float(0x727a0572), SkBits2Float(0x7a727272));  // 1.1755e-38f, 2.85223e+30f, 4.95218e+30f, 3.14714e+35f
+path.lineTo(SkBits2Float(0x7272727a), SkBits2Float(0xdb5e6472));  // 4.80216e+30f, -6.25979e+16f
+path.moveTo(SkBits2Float(0x440039fc), SkBits2Float(0x0000f647));  // 512.906f, 8.83477e-41f
+path.lineTo(SkBits2Float(0x666d0100), SkBits2Float(0x726efe62));  // 2.79805e+23f, 4.73376e+30f
+path.lineTo(SkBits2Float(0x440039fc), SkBits2Float(0x0000f647));  // 512.906f, 8.83477e-41f
+path.close();
+path.moveTo(SkBits2Float(0x440039fc), SkBits2Float(0x0000f647));  // 512.906f, 8.83477e-41f
+path.conicTo(SkBits2Float(0x72727272), SkBits2Float(0xf3db5e64), SkBits2Float(0x475afc16), SkBits2Float(0x170100ad), SkBits2Float(0x01008000));  // 4.80216e+30f, -3.47604e+31f, 56060.1f, 4.1683e-25f, 2.36017e-38f
+path.quadTo(SkBits2Float(0x72057272), SkBits2Float(0x8c7a3472), SkBits2Float(0x72727272), SkBits2Float(0x00f6475e));  // 2.64319e+30f, -1.92751e-31f, 4.80216e+30f, 2.26171e-38f
+path.moveTo(SkBits2Float(0x6d106d43), SkBits2Float(0x6efe6266));  // 2.79362e+27f, 3.93641e+28f
+path.quadTo(SkBits2Float(0x72727a05), SkBits2Float(0xba5b7272), SkBits2Float(0x03000074), SkBits2Float(0x5aff00e8));  // 4.80274e+30f, -0.000837124f, 3.76163e-37f, 3.58886e+16f
+path.quadTo(SkBits2Float(0x00da0039), SkBits2Float(0x72100039), SkBits2Float(0x727a0572), SkBits2Float(0x7a727272));  // 2.00202e-38f, 2.85223e+30f, 4.95218e+30f, 3.14714e+35f
+path.lineTo(SkBits2Float(0x7272727a), SkBits2Float(0xdb5e6472));  // 4.80216e+30f, -6.25979e+16f
+path.lineTo(SkBits2Float(0xfc5b97fc), SkBits2Float(0x47440039));  // -4.56078e+36f, 50176.2f
+path.lineTo(SkBits2Float(0x00710000), SkBits2Float(0x62766d01));  // 1.03774e-38f, 1.13644e+21f
+path.quadTo(SkBits2Float(0x7a05726e), SkBits2Float(0x72727272), SkBits2Float(0xf3db5e64), SkBits2Float(0x4a5afc16));  // 1.73224e+35f, 4.80216e+30f, -3.47604e+31f, 3.58785e+06f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_40(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x10190004), SkBits2Float(0x7272727a));  // 3.01739e-29f, 4.80216e+30f
+path.quadTo(SkBits2Float(0xf3db5e64), SkBits2Float(0x5b97fc16), SkBits2Float(0x000039fc), SkBits2Float(0x01008000));  // -3.47604e+31f, 8.55598e+16f, 2.08009e-41f, 2.36017e-38f
+path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x41720089));  // 1.73224e+35f, 4.80216e+30f, 4.34505e+30f, 15.1251f
+path.lineTo(SkBits2Float(0x63636363), SkBits2Float(0x63606363));  // 4.19457e+21f, 4.13923e+21f
+path.lineTo(SkBits2Float(0x01000000), SkBits2Float(0x10010004));  // 2.35099e-38f, 2.54408e-29f
+path.conicTo(SkBits2Float(0x72727272), SkBits2Float(0xf3db5e64), SkBits2Float(0x4a5afc16), SkBits2Float(0x0000d07d), SkBits2Float(0x01008000));  // 4.80216e+30f, -3.47604e+31f, 3.58785e+06f, 7.47915e-41f, 2.36017e-38f
+path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x63720089));  // 1.73224e+35f, 4.80216e+30f, 4.34505e+30f, 4.46415e+21f
+path.lineTo(SkBits2Float(0x63636363), SkBits2Float(0x63606363));  // 4.19457e+21f, 4.13923e+21f
+path.lineTo(SkBits2Float(0x72000000), SkBits2Float(0x5b5e72b4));  // 2.5353e+30f, 6.26136e+16f
+path.quadTo(SkBits2Float(0x05720089), SkBits2Float(0x05727272), SkBits2Float(0x7272727a), SkBits2Float(0x5b5e7272));  // 1.13789e-35f, 1.13998e-35f, 4.80216e+30f, 6.26133e+16f
+path.cubicTo(SkBits2Float(0x03000074), SkBits2Float(0x4aff00e8), SkBits2Float(0x397d3972), SkBits2Float(0x01727200), SkBits2Float(0x72727a00), SkBits2Float(0x5e8d7272));  // 3.76163e-37f, 8.35596e+06f, 0.000241494f, 4.45302e-38f, 4.80274e+30f, 5.09617e+18f
+path.moveTo(SkBits2Float(0x72008972), SkBits2Float(0x458fe705));  // 2.54594e+30f, 4604.88f
+path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0xe8727272), SkBits2Float(0xba5b5e03), SkBits2Float(0x03000074));  // 1.73224e+35f, -4.5797e+24f, -0.00083682f, 3.76163e-37f
+path.lineTo(SkBits2Float(0xf3dbff00), SkBits2Float(0x00397d16));  // -3.48598e+31f, 5.2795e-39f
+path.cubicTo(SkBits2Float(0x7a101900), SkBits2Float(0x72727272), SkBits2Float(0xf3db5e64), SkBits2Float(0x0197fc16), SkBits2Float(0x200c2010), SkBits2Float(0x20203620));  // 1.87049e+35f, 4.80216e+30f, -3.47604e+31f, 5.58304e-38f, 1.18691e-19f, 1.35704e-19f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_39(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0x7a057c72), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x055f0089));  // 1.73275e+35f, 4.80216e+30f, 4.34505e+30f, 1.04855e-35f
+path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0xba405e72), SkBits2Float(0x03000074));  // 1.73224e+35f, 4.80216e+30f, -0.000733829f, 3.76163e-37f
+path.lineTo(SkBits2Float(0x664aff00), SkBits2Float(0x56397d39));  // 2.39655e+23f, 5.09869e+13f
+path.quadTo(SkBits2Float(0x7a057273), SkBits2Float(0x057300ff), SkBits2Float(0x257c0c9f), SkBits2Float(0x72787257));  // 1.73224e+35f, 1.1426e-35f, 2.18618e-16f, 4.92099e+30f
+path.quadTo(SkBits2Float(0xba5b5e72), SkBits2Float(0x03000093), SkBits2Float(0x74ba00e8), SkBits2Float(0xe8ecff00));  // -0.000836826f, 3.76165e-37f, 1.17894e+32f, -8.95346e+24f
+path.moveTo(SkBits2Float(0x39724aff), SkBits2Float(0x7200397d));  // 0.000231069f, 2.53975e+30f
+path.quadTo(SkBits2Float(0x827a0572), SkBits2Float(0x72727272), SkBits2Float(0x724adf00), SkBits2Float(0x00397d39));  // -1.83687e-37f, 4.80216e+30f, 4.01828e+30f, 5.27954e-39f
+path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x16f3abab), SkBits2Float(0xfc5b97fc), SkBits2Float(0x47440039));  // 1.73224e+35f, 3.93671e-25f, -4.56078e+36f, 50176.2f
+path.lineTo(SkBits2Float(0x00710000), SkBits2Float(0x62767201));  // 1.03774e-38f, 1.13653e+21f
+path.quadTo(SkBits2Float(0x7a05726e), SkBits2Float(0x72727272), SkBits2Float(0xf3db5e64), SkBits2Float(0x4a5afc16));  // 1.73224e+35f, 4.80216e+30f, -3.47604e+31f, 3.58785e+06f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+
+static void fuzz763_42(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x05720089));  // 1.73224e+35f, 4.80216e+30f, 4.34505e+30f, 1.13789e-35f
+path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0xba405e72), SkBits2Float(0x03000074));  // 1.73224e+35f, 4.80216e+30f, -0.000733829f, 3.76163e-37f
+path.lineTo(SkBits2Float(0x724aff00), SkBits2Float(0x56397d39));  // 4.02075e+30f, 5.09869e+13f
+path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0xfa8d00ff), SkBits2Float(0x25727272), SkBits2Float(0x7272727a));  // 1.73224e+35f, -3.66067e+35f, 2.10289e-16f, 4.80216e+30f
+path.quadTo(SkBits2Float(0xba5b5e72), SkBits2Float(0x03000093), SkBits2Float(0x74ba00e8), SkBits2Float(0xe8e0ff00));  // -0.000836826f, 3.76165e-37f, 1.17894e+32f, -8.50011e+24f
+path.conicTo(SkBits2Float(0x39724aff), SkBits2Float(0x7200397d), SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0x4aff0072));  // 0.000231069f, 2.53975e+30f, 1.73224e+35f, 4.80216e+30f, 8.3559e+06f
+path.quadTo(SkBits2Float(0x00397d39), SkBits2Float(0x05727272), SkBits2Float(0x7272727a), SkBits2Float(0x385e7272));  // 5.27954e-39f, 1.13998e-35f, 4.80216e+30f, 5.30355e-05f
+path.quadTo(SkBits2Float(0x057200ff), SkBits2Float(0x25727272), SkBits2Float(0x7272727a), SkBits2Float(0x5b5e7272));  // 1.1379e-35f, 2.10289e-16f, 4.80216e+30f, 6.26133e+16f
+path.cubicTo(SkBits2Float(0x03000074), SkBits2Float(0x4aff00e8), SkBits2Float(0x397d3972), SkBits2Float(0x01000400), SkBits2Float(0x72727a10), SkBits2Float(0x5e647272));  // 3.76163e-37f, 8.35596e+06f, 0.000241494f, 2.35128e-38f, 4.80275e+30f, 4.11534e+18f
+path.quadTo(SkBits2Float(0x2b2d16f3), SkBits2Float(0x0039fc4d), SkBits2Float(0x68800000), SkBits2Float(0x0100fafa));  // 6.14938e-13f, 5.32513e-39f, 4.8357e+24f, 2.369e-38f
+path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x63720089));  // 1.73224e+35f, 4.80216e+30f, 4.34505e+30f, 4.46415e+21f
+path.lineTo(SkBits2Float(0x63636363), SkBits2Float(0x63606363));  // 4.19457e+21f, 4.13923e+21f
+path.lineTo(SkBits2Float(0x72720000), SkBits2Float(0xff725b5e));  // 4.7933e+30f, -3.22148e+38f
+path.moveTo(SkBits2Float(0x72720572), SkBits2Float(0x5b5e2572));  // 4.79373e+30f, 6.25286e+16f
+path.quadTo(SkBits2Float(0x05720089), SkBits2Float(0x25727272), SkBits2Float(0x72728c7a), SkBits2Float(0x5b5e7272));  // 1.13789e-35f, 2.10289e-16f, 4.80417e+30f, 6.26133e+16f
+path.cubicTo(SkBits2Float(0x03000074), SkBits2Float(0x4aff00e8), SkBits2Float(0x397d3972), SkBits2Float(0x01000400), SkBits2Float(0x72727a10), SkBits2Float(0x5e827272));  // 3.76163e-37f, 8.35596e+06f, 0.000241494f, 2.35128e-38f, 4.80275e+30f, 4.69985e+18f
+path.quadTo(SkBits2Float(0x97fc16f3), SkBits2Float(0x0039fc5b), SkBits2Float(0x00f6472e), SkBits2Float(0x01008000));  // -1.62909e-24f, 5.32515e-39f, 2.26171e-38f, 2.36017e-38f
+path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0xf3db5e64), SkBits2Float(0x4a5afc16));  // 1.73224e+35f, 4.80216e+30f, -3.47604e+31f, 3.58785e+06f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_43(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x5c386c3a), SkBits2Float(0x4e691a3e));  // 2.07642e+17f, 9.77703e+08f
+path.cubicTo(SkBits2Float(0x6f69f9f5), SkBits2Float(0x18ff8791), SkBits2Float(0x2492263c), SkBits2Float(0xbc6fdb48), SkBits2Float(0xc2f82107), SkBits2Float(0x729a18e1));  // 7.24122e+28f, 6.60528e-24f, 6.33822e-17f, -0.0146397f, -124.065f, 6.10442e+30f
+path.cubicTo(SkBits2Float(0x07d729d1), SkBits2Float(0xdea6db48), SkBits2Float(0xcd1dfb88), SkBits2Float(0x90826769), SkBits2Float(0x1c20e5a4), SkBits2Float(0xa4c3ba9b));  // 3.23742e-34f, -6.01164e+18f, -1.65657e+08f, -5.14353e-29f, 5.32364e-22f, -8.48839e-17f
+path.moveTo(SkBits2Float(0xcc2084b7), SkBits2Float(0x19f68bdb));  // -4.20789e+07f, 2.54923e-23f
+path.close();
+path.moveTo(SkBits2Float(0xcc2084b7), SkBits2Float(0x19f68bdb));  // -4.20789e+07f, 2.54923e-23f
+path.cubicTo(SkBits2Float(0xdeea1d6e), SkBits2Float(0xc7774804), SkBits2Float(0x27cf0dcf), SkBits2Float(0x6ae8b99f), SkBits2Float(0x24ac3260), SkBits2Float(0x062fa93c));  // -8.43488e+18f, -63304, 5.7469e-15f, 1.40674e+26f, 7.46784e-17f, 3.30382e-35f
+path.lineTo(SkBits2Float(0x438a0b9c), SkBits2Float(0x60a1d2c8));  // 276.091f, 9.32848e+19f
+path.quadTo(SkBits2Float(0xe13fb902), SkBits2Float(0x07ee536f), SkBits2Float(0x971d8ac1), SkBits2Float(0x2f9f174b));  // -2.21041e+20f, 3.58593e-34f, -5.09046e-25f, 2.89385e-10f
+path.lineTo(SkBits2Float(0x0f2cf5d8), SkBits2Float(0xe271654c));  // 8.5276e-30f, -1.11324e+21f
+path.lineTo(SkBits2Float(0xe6cf24d2), SkBits2Float(0xd9537742));  // -4.89105e+23f, -3.72015e+15f
+path.cubicTo(SkBits2Float(0x1aaaee04), SkBits2Float(0x9e3b804c), SkBits2Float(0x84cba87d), SkBits2Float(0x4e0e8ccc), SkBits2Float(0x2aec611a), SkBits2Float(0x7ae4b639));  // 7.06949e-23f, -9.92623e-21f, -4.78798e-36f, 5.97898e+08f, 4.19894e-13f, 5.9377e+35f
+path.conicTo(SkBits2Float(0x73357921), SkBits2Float(0x6f163021), SkBits2Float(0x70ea542c), SkBits2Float(0xe008f404), SkBits2Float(0x1f6c5e52));  // 1.43778e+31f, 4.64809e+28f, 5.8017e+29f, -3.94741e+19f, 5.0053e-20f
+path.lineTo(SkBits2Float(0xda45ad4e), SkBits2Float(0xedce4a04));  // -1.39103e+16f, -7.98042e+27f
+path.lineTo(SkBits2Float(0xac0e45da), SkBits2Float(0x8f632841));  // -2.02182e-12f, -1.11997e-29f
+path.lineTo(SkBits2Float(0xcc2084b7), SkBits2Float(0x19f68bdb));  // -4.20789e+07f, 2.54923e-23f
+path.close();
+path.moveTo(SkBits2Float(0xcc2084b7), SkBits2Float(0x19f68bdb));  // -4.20789e+07f, 2.54923e-23f
+path.quadTo(SkBits2Float(0xf35c4ad5), SkBits2Float(0x0692f251), SkBits2Float(0x69632126), SkBits2Float(0xb927af67));  // -1.74534e+31f, 5.52751e-35f, 1.71614e+25f, -0.000159917f
+path.moveTo(SkBits2Float(0x6534bff9), SkBits2Float(0x434a9986));  // 5.3348e+22f, 202.6f
+path.quadTo(SkBits2Float(0x37c603e5), SkBits2Float(0xa0683953), SkBits2Float(0x751915e4), SkBits2Float(0x831c911a));  // 2.36053e-05f, -1.96701e-19f, 1.94059e+32f, -4.60108e-37f
+path.cubicTo(SkBits2Float(0xba4f10f1), SkBits2Float(0x5a7571df), SkBits2Float(0x4ec67459), SkBits2Float(0x33c58827), SkBits2Float(0x10b78ccb), SkBits2Float(0xedbd2748));  // -0.000789895f, 1.72716e+16f, 1.66476e+09f, 9.19829e-08f, 7.23977e-29f, -7.31752e+27f
+path.cubicTo(SkBits2Float(0x6d06f06a), SkBits2Float(0xe30465cf), SkBits2Float(0xc5458fe7), SkBits2Float(0xca488dc4), SkBits2Float(0x38f9021c), SkBits2Float(0x3e8d58db));  // 2.6101e+27f, -2.44231e+21f, -3160.99f, -3.28587e+06f, 0.000118736f, 0.276069f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_44(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x7c223bab), SkBits2Float(0x7cf35966));  // 3.36945e+36f, 1.01083e+37f
+path.quadTo(SkBits2Float(0x00000000), SkBits2Float(0x7ccaca6d), SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 8.4236e+36f, 0, 0
+path.lineTo(SkBits2Float(0x7d7d7d7d), SkBits2Float(0x00000000));  // 2.10591e+37f, 0
+path.quadTo(SkBits2Float(0x7ccacab0), SkBits2Float(0x7d1817f4), SkBits2Float(0x7c223bab), SkBits2Float(0x7cf35966));  // 8.42364e+36f, 1.26354e+37f, 3.36945e+36f, 1.01083e+37f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x109d0000), SkBits2Float(0xff7bc000));  // 6.19256e-29f, -3.34633e+38f
+path.conicTo(SkBits2Float(0x979797ed), SkBits2Float(0x3a214797), SkBits2Float(0x28aa217a), SkBits2Float(0x01007272), SkBits2Float(0x00000072));  // -9.7965e-25f, 0.000615233f, 1.88883e-14f, 2.3592e-38f, 1.59748e-43f
+path.quadTo(SkBits2Float(0x72728302), SkBits2Float(0x8b727272), SkBits2Float(0x72727272), SkBits2Float(0xc00308f6));  // 4.80344e+30f, -4.66936e-32f, 4.80216e+30f, -2.04742f
+path.conicTo(SkBits2Float(0x7f52753a), SkBits2Float(0x8072ffff), SkBits2Float(0x67af2103), SkBits2Float(0x7d2a6847), SkBits2Float(0x7d7d7d7d));  // 2.79747e+38f, -1.05611e-38f, 1.65405e+24f, 1.41569e+37f, 2.10591e+37f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename);
+}
+
+static void fuzz763_45(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.cubicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7a303030), SkBits2Float(0x7a303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 2.28705e+35f, 2.28705e+35f, 6.40969e-10f
+path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x74303030), SkBits2Float(0x74303030), SkBits2Float(0x30303030), SkBits2Float(0x74303030));  // 6.40969e-10f, 5.58363e+31f, 5.58363e+31f, 6.40969e-10f, 5.58363e+31f
+path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f
+path.moveTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.moveTo(SkBits2Float(0x77773030), SkBits2Float(0x30303030));  // 5.01357e+33f, 6.40969e-10f
+path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7a743030), SkBits2Float(0x74303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f, 3.16974e+35f, 5.58363e+31f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x77773030), SkBits2Float(0x30303030));  // 5.01357e+33f, 6.40969e-10f
+path.close();
+path.moveTo(SkBits2Float(0x77773030), SkBits2Float(0x30303030));  // 5.01357e+33f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x7f303030), SkBits2Float(0x7a303030));  // 2.34194e+38f, 2.28705e+35f
+path.conicTo(SkBits2Float(0x77303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0xf9303030), SkBits2Float(0x7a303030));  // 3.57352e+33f, 6.40969e-10f, 6.40969e-10f, -5.71764e+34f, 2.28705e+35f
+path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f
+path.quadTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f
+path.quadTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f
+path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f
+path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7a303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 2.28705e+35f, 6.40969e-10f
+path.cubicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7a303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 2.28705e+35f, 6.40969e-10f
+path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 6.40969e-10f
+path.moveTo(SkBits2Float(0x77303030), SkBits2Float(0xff303030));  // 3.57352e+33f, -2.34194e+38f
+path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7f773030), SkBits2Float(0x7a7a3030), SkBits2Float(0x7a303030));  // 6.40969e-10f, 6.40969e-10f, 3.2857e+38f, 3.24763e+35f, 2.28705e+35f
+path.quadTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x77303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f, 3.57352e+33f, 6.40969e-10f
+path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7b303030), SkBits2Float(0x73303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f, 9.14822e+35f, 1.39591e+31f, 6.40969e-10f
+path.quadTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7a7a3030));  // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 3.24763e+35f
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename);
+}
+
+static void fuzz763_46(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+    path.conicTo(SkBits2Float(0x44444444), SkBits2Float(0x44444444), SkBits2Float(0x44263030), SkBits2Float(0x44304430), SkBits2Float(0x4c444430));  // 785.067f, 785.067f, 664.753f, 705.065f, 5.145e+07f
+path.moveTo(SkBits2Float(0x44444444), SkBits2Float(0x44444444));  // 785.067f, 785.067f
+path.cubicTo(SkBits2Float(0x30303030), SkBits2Float(0x44444444), SkBits2Float(0x30303030), SkBits2Float(0x44444444), SkBits2Float(0x44444444), SkBits2Float(0x4444444c));  // 6.40969e-10f, 785.067f, 6.40969e-10f, 785.067f, 785.067f, 785.067f
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename);
+}
+
+static void fuzz763_47(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.cubicTo(SkBits2Float(0x7272728e), SkBits2Float(0x52527272), SkBits2Float(0x2d555252), SkBits2Float(0x68556829), SkBits2Float(0x555b2d29), SkBits2Float(0x2a212a8c));  // 4.80217e+30f, 2.25966e+11f, 1.21259e-11f, 4.03114e+24f, 1.50617e+13f, 1.43144e-13f
+path.conicTo(SkBits2Float(0x00296808), SkBits2Float(0x00000002), SkBits2Float(0x52525252), SkBits2Float(0x72007272), SkBits2Float(0x52527272));  // 3.80257e-39f, 2.8026e-45f, 2.25831e+11f, 2.54416e+30f, 2.25966e+11f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x2a212a8c), SkBits2Float(0x7272081f));  // 1.43144e-13f, 4.79393e+30f
+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x5974fa80), SkBits2Float(0x00747474), SkBits2Float(0x59585264));  // 4.80216e+30f, 4.30971e+15f, 1.06947e-38f, 3.80557e+15f
+path.cubicTo(SkBits2Float(0x64007474), SkBits2Float(0x088c5852), SkBits2Float(0x80808021), SkBits2Float(0x8c808080), SkBits2Float(0x80802108), SkBits2Float(0x80808080));  // 9.4783e+21f, 8.44671e-34f, -1.18009e-38f, -1.97989e-31f, -1.17668e-38f, -1.1801e-38f
+path.quadTo(SkBits2Float(0x80807d80), SkBits2Float(0x80808080), SkBits2Float(0xff7f0000), SkBits2Float(0x80808080));  // -1.18e-38f, -1.1801e-38f, -3.38953e+38f, -1.1801e-38f
+path.quadTo(SkBits2Float(0x80808080), SkBits2Float(0x80808080), SkBits2Float(0xed842b00), SkBits2Float(0x7252ff6d));  // -1.1801e-38f, -1.1801e-38f, -5.113e+27f, 4.17924e+30f
+path.quadTo(SkBits2Float(0x72577200), SkBits2Float(0x55525352), SkBits2Float(0x2a212a8c), SkBits2Float(0x7272081f));  // 4.26733e+30f, 1.44535e+13f, 1.43144e-13f, 4.79393e+30f
+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x6f740080), SkBits2Float(0x8c556874), SkBits2Float(0x2982ffff));  // 4.80216e+30f, 7.55149e+28f, -1.64404e-31f, 5.81757e-14f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_48(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0xed0081bc), SkBits2Float(0x1b2d8040));  // -2.48568e+27f, 1.43517e-22f
+path.moveTo(SkBits2Float(0x74747403), SkBits2Float(0x29747474));  // 7.74703e+31f, 5.42799e-14f
+path.close();
+path.moveTo(SkBits2Float(0x74747403), SkBits2Float(0x29747474));  // 7.74703e+31f, 5.42799e-14f
+path.conicTo(SkBits2Float(0x662d5576), SkBits2Float(0x2d804066), SkBits2Float(0x8068291b), SkBits2Float(0x740315ff), SkBits2Float(0x74747474));  // 2.04636e+23f, 1.45805e-11f, -9.56564e-39f, 4.15428e+31f, 7.74708e+31f
+path.cubicTo(SkBits2Float(0x762d0529), SkBits2Float(0x72525252), SkBits2Float(0x007b7272), SkBits2Float(0x525adada), SkBits2Float(0x52525252), SkBits2Float(0x52727252));  // 8.77316e+32f, 4.16585e+30f, 1.13368e-38f, 2.34994e+11f, 2.25831e+11f, 2.60325e+11f
+path.lineTo(SkBits2Float(0x74747403), SkBits2Float(0x29747474));  // 7.74703e+31f, 5.42799e-14f
+path.close();
+path.moveTo(SkBits2Float(0xa5252620), SkBits2Float(0x52b4adad));  // -1.43244e-16f, 3.88004e+11f
+path.close();
+path.moveTo(SkBits2Float(0xa5252620), SkBits2Float(0x52b4adad));  // -1.43244e-16f, 3.88004e+11f
+path.quadTo(SkBits2Float(0x72727270), SkBits2Float(0x52524872), SkBits2Float(0x72525252), SkBits2Float(0x72727272));  // 4.80216e+30f, 2.2579e+11f, 4.16585e+30f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0x80406666), SkBits2Float(0x68291b2d), SkBits2Float(0x0315ff80));  // 4.80215e+30f, -5.91421e-39f, 3.19432e+24f, 4.40805e-37f
+path.cubicTo(SkBits2Float(0x74747474), SkBits2Float(0x7b722974), SkBits2Float(0x5adada00), SkBits2Float(0x52525252), SkBits2Float(0x72720052), SkBits2Float(0x72727272));  // 7.74708e+31f, 1.25738e+36f, 3.08006e+16f, 2.25831e+11f, 4.79333e+30f, 4.80216e+30f
+path.lineTo(SkBits2Float(0xa5252620), SkBits2Float(0x52b4adad));  // -1.43244e-16f, 3.88004e+11f
+path.close();
+path.moveTo(SkBits2Float(0xa5252620), SkBits2Float(0x52b4adad));  // -1.43244e-16f, 3.88004e+11f
+path.quadTo(SkBits2Float(0x72727227), SkBits2Float(0x72727272), SkBits2Float(0x74727272), SkBits2Float(0x55747421));  // 4.80214e+30f, 4.80216e+30f, 7.68345e+31f, 1.67987e+13f
+path.lineTo(SkBits2Float(0xa5252620), SkBits2Float(0x52b4adad));  // -1.43244e-16f, 3.88004e+11f
+path.close();
+path.moveTo(SkBits2Float(0x724b0000), SkBits2Float(0x00725f72));  // 4.02083e+30f, 1.05035e-38f
+path.lineTo(SkBits2Float(0x52525252), SkBits2Float(0x72725252));  // 2.25831e+11f, 4.79967e+30f
+path.quadTo(SkBits2Float(0x26727272), SkBits2Float(0x0303a525), SkBits2Float(0x52005c03), SkBits2Float(0x72525252));  // 8.41157e-16f, 3.8687e-37f, 1.37825e+11f, 4.16585e+30f
+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x1ff07255), SkBits2Float(0x2a8c5572), SkBits2Float(0x21082a21));  // 4.80216e+30f, 1.01833e-19f, 2.49283e-13f, 4.61343e-19f
+path.lineTo(SkBits2Float(0x2a2a3a21), SkBits2Float(0x29212828));  // 1.51192e-13f, 3.5784e-14f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+static void fuzz763_49(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x78303030), SkBits2Float(0x78787881), SkBits2Float(0x78787878), SkBits2Float(0x30303030));  // 6.40969e-10f, 1.42941e+34f, 2.01583e+34f, 2.01583e+34f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878));  // 2.01583e+34f, 2.01583e+34f
+path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878));  // 2.01583e+34f, 2.01583e+34f
+path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878));  // 2.01583e+34f, 2.01583e+34f
+path.quadTo(SkBits2Float(0x30303030), SkBits2Float(0x78787878), SkBits2Float(0x78787878), SkBits2Float(0x78787878));  // 6.40969e-10f, 2.01583e+34f, 2.01583e+34f, 2.01583e+34f
+path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f
+path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x7878788d));  // 2.01583e+34f, 2.01584e+34f
+path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x30303030));  // 2.01583e+34f, 6.40969e-10f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename);
+}
+
+static void fuzz763_50(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x70621ede), SkBits2Float(0x00000000));  // 2.79924e+29f, 0
+path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x74fc5b97), SkBits2Float(0x7d458fe4));  // 0, 0, 0, 1.59951e+32f, 1.64128e+37f
+path.lineTo(SkBits2Float(0xefea1ffe), SkBits2Float(0x00000000));  // -1.44916e+29f, 0
+path.lineTo(SkBits2Float(0x70621ede), SkBits2Float(0x00000000));  // 2.79924e+29f, 0
+path.close();
+path.moveTo(SkBits2Float(0xefea1ffe), SkBits2Float(0x00000000));  // -1.44916e+29f, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0xefea1ffe), SkBits2Float(0x00000000));  // -1.44916e+29f, 0
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename);
+}
+
+static void fuzz763_51(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0x868b5aae), SkBits2Float(0x626c45ab), SkBits2Float(0xefea1ffe), SkBits2Float(0x0029fc76));  // -5.24192e-35f, 1.08961e+21f, -1.44916e+29f, 3.85582e-39f
+path.moveTo(SkBits2Float(0xfacbff01), SkBits2Float(0x56fc5b97));  // -5.29604e+35f, 1.38735e+14f
+path.cubicTo(SkBits2Float(0x7d4559c9), SkBits2Float(0xad801c39), SkBits2Float(0xfbe2091a), SkBits2Float(0x7268e394), SkBits2Float(0x7c800079), SkBits2Float(0xa1d75590));  // 1.63953e+37f, -1.45644e-11f, -2.34729e+36f, 4.61284e+30f, 5.31699e+36f, -1.45916e-18f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_52(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0x29ff4bae), SkBits2Float(0xa1d75590), SkBits2Float(0x9fd6f6c3), SkBits2Float(0x70621ede));  // 1.13374e-13f, -1.45916e-18f, -9.10408e-20f, 2.79924e+29f
+path.quadTo(SkBits2Float(0x57a839d3), SkBits2Float(0x1a80d34b), SkBits2Float(0x0147a31b), SkBits2Float(0xff7fffff));  // 3.69933e+14f, 5.32809e-23f, 3.66675e-38f, -3.40282e+38f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0x1ab8e97c), SkBits2Float(0x94fbe3ef));  // 7.64778e-23f, -2.54344e-26f
+path.conicTo(SkBits2Float(0x75757568), SkBits2Float(0x7575755e), SkBits2Float(0x75757575), SkBits2Float(0x75757575), SkBits2Float(0x75756575));  // 3.11156e+32f, 3.11156e+32f, 3.11156e+32f, 3.11156e+32f, 3.11077e+32f
+path.lineTo(SkBits2Float(0x1ab8e97c), SkBits2Float(0x94fbe3ef));  // 7.64778e-23f, -2.54344e-26f
+path.close();
+path.moveTo(SkBits2Float(0x1ab8e97c), SkBits2Float(0x94fbe3ef));  // 7.64778e-23f, -2.54344e-26f
+path.conicTo(SkBits2Float(0x75757575), SkBits2Float(0x75757575), SkBits2Float(0x75757575), SkBits2Float(0x75917575), SkBits2Float(0x75757575));  // 3.11156e+32f, 3.11156e+32f, 3.11156e+32f, 3.68782e+32f, 3.11156e+32f
+path.lineTo(SkBits2Float(0x1ab8e97c), SkBits2Float(0x94fbe3ef));  // 7.64778e-23f, -2.54344e-26f
+path.close();
+path.moveTo(SkBits2Float(0x1ab8e97c), SkBits2Float(0x94fbe3ef));  // 7.64778e-23f, -2.54344e-26f
+path.conicTo(SkBits2Float(0x75757575), SkBits2Float(0x7575758f), SkBits2Float(0x7f757575), SkBits2Float(0x75757575), SkBits2Float(0x75757575));  // 3.11156e+32f, 3.11157e+32f, 3.26271e+38f, 3.11156e+32f, 3.11156e+32f
+path.lineTo(SkBits2Float(0x1ab8e97c), SkBits2Float(0x94fbe3ef));  // 7.64778e-23f, -2.54344e-26f
+path.close();
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz763_53(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x7644b829), SkBits2Float(0x00000000));  // 9.97486e+32f, 0
+path.lineTo(SkBits2Float(0x74fc5b97), SkBits2Float(0x77df944a));  // 1.59951e+32f, 9.06945e+33f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xf8fbe3ff));  // 0, -4.08716e+34f
+path.lineTo(SkBits2Float(0x7644b829), SkBits2Float(0x00000000));  // 9.97486e+32f, 0
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0x45ab86ae), SkBits2Float(0xd6d6626c), SkBits2Float(0xd6d6d6d6), SkBits2Float(0x7644d6d6));  // 5488.83f, -1.17859e+14f, -1.18109e+14f, 9.98093e+32f
+path.moveTo(SkBits2Float(0xd6d6d6d6), SkBits2Float(0xd6d6d6d6));  // -1.18109e+14f, -1.18109e+14f
+path.cubicTo(SkBits2Float(0xd6d6d6d6), SkBits2Float(0x64fed6d6), SkBits2Float(0x7644ef40), SkBits2Float(0x290877fc), SkBits2Float(0x447644b8), SkBits2Float(0x80fafc76));  // -1.18109e+14f, 3.76076e+22f, 9.98577e+32f, 3.03021e-14f, 985.074f, -2.30494e-38f
+path.conicTo(SkBits2Float(0x87808080), SkBits2Float(0x764400ae), SkBits2Float(0x764400fc), SkBits2Float(0x450080fc), SkBits2Float(0x3636366c));  // -1.93348e-34f, 9.93852e+32f, 9.93858e+32f, 2056.06f, 2.71518e-06f
+path.lineTo(SkBits2Float(0xd6d6d6d6), SkBits2Float(0xd6d6d6d6));  // -1.18109e+14f, -1.18109e+14f
+path.close();
+path.moveTo(SkBits2Float(0xef08a412), SkBits2Float(0x5aaeff7f));  // -4.22883e+28f, 2.46288e+16f
+path.conicTo(SkBits2Float(0x7644626c), SkBits2Float(0x088912fc), SkBits2Float(0xae8744ef), SkBits2Float(0x76571f5a), SkBits2Float(0x45ab86fc));  // 9.95788e+32f, 8.24985e-34f, -6.15133e-11f, 1.0908e+33f, 5488.87f
+path.conicTo(SkBits2Float(0x4064fe62), SkBits2Float(0x290877ef), SkBits2Float(0x780080b8), SkBits2Float(0x553c7644), SkBits2Float(0x644eae87));  // 3.57803f, 3.03021e-14f, 1.04254e+34f, 1.2951e+13f, 1.52504e+22f
+path.lineTo(SkBits2Float(0xef08a412), SkBits2Float(0x5aaeff7f));  // -4.22883e+28f, 2.46288e+16f
+path.close();
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+// hangs 654939
+static void fuzz763_54(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.conicTo(SkBits2Float(0x5b682968), SkBits2Float(0xb3b32d11), SkBits2Float(0xb3b3b3b3), SkBits2Float(0x5b29b3b3), SkBits2Float(0x212a8c55));  // 6.53477e+16f, -8.34353e-08f, -8.36802e-08f, 4.77669e+16f, 5.7784e-19f
+path.conicTo(SkBits2Float(0x68555b2d), SkBits2Float(0x28296869), SkBits2Float(0x5b252a08), SkBits2Float(0x5d68392a), SkBits2Float(0x29282780));  // 4.03018e+24f, 9.40402e-15f, 4.64896e+16f, 1.04584e+18f, 3.73378e-14f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.cubicTo(SkBits2Float(0x52727272), SkBits2Float(0x72727252), SkBits2Float(0x525252c7), SkBits2Float(0x72725252), SkBits2Float(0x72727272), SkBits2Float(0x72727255));  // 2.60326e+11f, 4.80215e+30f, 2.25833e+11f, 4.79967e+30f, 4.80216e+30f, 4.80215e+30f
+path.quadTo(SkBits2Float(0xd7da0000), SkBits2Float(0x5252525a), SkBits2Float(0x72525252), SkBits2Float(0x72727272));  // -4.79387e+14f, 2.25831e+11f, 4.16585e+30f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x48525252), SkBits2Float(0x72725252), SkBits2Float(0x72727272), SkBits2Float(0x72727255));  // 215369, 4.79967e+30f, 4.80216e+30f, 4.80215e+30f
+path.quadTo(SkBits2Float(0xdada007b), SkBits2Float(0x5252525a), SkBits2Float(0x72675252), SkBits2Float(0x72727272));  // -3.0681e+16f, 2.25831e+11f, 4.5818e+30f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x52525252), SkBits2Float(0x27725252), SkBits2Float(0x72727272), SkBits2Float(0x72727272));  // 2.25831e+11f, 3.36289e-15f, 4.80216e+30f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x1c292172), SkBits2Float(0x7bc00321), SkBits2Float(0x9aaaaaaa), SkBits2Float(0x8c556a4b));  // 5.59606e-22f, 1.99397e+36f, -7.05861e-23f, -1.64409e-31f
+path.quadTo(SkBits2Float(0x72725572), SkBits2Float(0x00007272), SkBits2Float(0x525adada), SkBits2Float(0x52525252));  // 4.79991e+30f, 4.10552e-41f, 2.34994e+11f, 2.25831e+11f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad));  // -1.43243e-16f, 3.88004e+11f
+path.close();
+path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad));  // -1.43243e-16f, 3.88004e+11f
+path.quadTo(SkBits2Float(0x72725570), SkBits2Float(0x52525272), SkBits2Float(0x72525252), SkBits2Float(0x72727272));  // 4.79991e+30f, 2.25832e+11f, 4.16585e+30f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0x555bb672), SkBits2Float(0x29686968), SkBits2Float(0x252a081f));  // 4.80215e+30f, 1.50985e+13f, 5.16058e-14f, 1.47479e-16f
+path.moveTo(SkBits2Float(0x5d68392a), SkBits2Float(0x01002780));  // 1.04584e+18f, 2.35382e-38f
+path.moveTo(SkBits2Float(0x72727200), SkBits2Float(0x72725252));  // 4.80212e+30f, 4.79967e+30f
+path.quadTo(SkBits2Float(0x5adada00), SkBits2Float(0xa5252652), SkBits2Float(0x727272ad), SkBits2Float(0xda007b72));  // 3.08006e+16f, -1.43245e-16f, 4.80218e+30f, -9.04113e+15f
+path.lineTo(SkBits2Float(0x5252525a), SkBits2Float(0x72525252));  // 2.25831e+11f, 4.16585e+30f
+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525252), SkBits2Float(0x27725252), SkBits2Float(0x72727272));  // 4.80216e+30f, 2.25831e+11f, 3.36289e-15f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x74217472), SkBits2Float(0x005b5574), SkBits2Float(0x72680000));  // 4.80216e+30f, 5.11671e+31f, 8.38768e-39f, 4.59523e+30f
+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525252), SkBits2Float(0x007b7272), SkBits2Float(0x525adada));  // 4.80216e+30f, 2.25831e+11f, 1.13368e-38f, 2.34994e+11f
+path.lineTo(SkBits2Float(0x72727200), SkBits2Float(0x72725252));  // 4.80212e+30f, 4.79967e+30f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
+}
+
+
+// afl crash
+static void fuzz763_55(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x55415500));  // 0, 1.32857e+13f
+path.lineTo(SkBits2Float(0x55555568), SkBits2Float(0x55555555));  // 1.46602e+13f, 1.46602e+13f
+path.lineTo(SkBits2Float(0x98989898), SkBits2Float(0x55989898));  // -3.94452e-24f, 2.09726e+13f
+path.lineTo(SkBits2Float(0xf6f65555), SkBits2Float(0x101006f6));  // -2.49812e+33f, 2.84044e-29f
+path.quadTo(SkBits2Float(0xdca33f10), SkBits2Float(0xf6f6f6f6), SkBits2Float(0xf621f6f6), SkBits2Float(0xf70ff6f6));  // -3.67598e+17f, -2.50452e+33f, -8.21259e+32f, -2.91995e+33f
+path.lineTo(SkBits2Float(0x9400f6f6), SkBits2Float(0x10530000));  // -6.51105e-27f, 4.16124e-29f
+path.quadTo(SkBits2Float(0x0f101010), SkBits2Float(0x00101010), SkBits2Float(0xf610f720), SkBits2Float(0xf6f6f6f6));  // 7.10284e-30f, 1.47513e-39f, -7.35062e+32f, -2.50452e+33f
+path.lineTo(SkBits2Float(0x105352f6), SkBits2Float(0x1cf6ff10));  // 4.16763e-29f, 1.63448e-21f
+path.lineTo(SkBits2Float(0xf6f6220a), SkBits2Float(0x003700f6));  // -2.49608e+33f, 5.0513e-39f
+path.cubicTo(SkBits2Float(0x0000001e), SkBits2Float(0x00fff4f6), SkBits2Float(0xff101064), SkBits2Float(0xf6b6ac7f), SkBits2Float(0xf6f629f6), SkBits2Float(0x10f6f6f6));  // 4.2039e-44f, 2.35059e-38f, -1.91494e+38f, -1.85253e+33f, -2.4964e+33f, 9.74104e-29f
+path.quadTo(SkBits2Float(0x10101007), SkBits2Float(0x10f7fd10), SkBits2Float(0xf6f6f6f6), SkBits2Float(0xf6f645e0));  // 2.84113e-29f, 9.78142e-29f, -2.50452e+33f, -2.4975e+33f
+path.lineTo(SkBits2Float(0xed9ef6f6), SkBits2Float(0x53535353));  // -6.14965e+27f, 9.07636e+11f
+path.lineTo(SkBits2Float(0x53006cf6), SkBits2Float(0x53295353));  // 5.51584e+11f, 7.27247e+11f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x55415500));  // 0, 1.32857e+13f
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x55415500));  // 0, 1.32857e+13f
+path.lineTo(SkBits2Float(0xf6f6f6f6), SkBits2Float(0x5353d9f6));  // -2.50452e+33f, 9.09895e+11f
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename);
+}
+
+// 656149
+static void fuzz763_56(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.conicTo(SkBits2Float(0x5b682968), SkBits2Float(0xb3b32d11), SkBits2Float(0xb3b3b3b3), SkBits2Float(0x5b29b3b3), SkBits2Float(0x72725255));  // 6.53477e+16f, -8.34353e-08f, -8.36802e-08f, 4.77669e+16f, 4.79967e+30f
+path.quadTo(SkBits2Float(0x525252c7), SkBits2Float(0x72725252), SkBits2Float(0x72727272), SkBits2Float(0x72727255));  // 2.25833e+11f, 4.79967e+30f, 4.80216e+30f, 4.80215e+30f
+path.quadTo(SkBits2Float(0xd7da0000), SkBits2Float(0x5adada00), SkBits2Float(0x52525252), SkBits2Float(0x00005252));  // -4.79387e+14f, 3.08006e+16f, 2.25831e+11f, 2.9531e-41f
+path.conicTo(SkBits2Float(0xadada525), SkBits2Float(0x52525ab4), SkBits2Float(0x52525252), SkBits2Float(0x72727272), SkBits2Float(0x52527272));  // -1.97412e-11f, 2.25866e+11f, 2.25831e+11f, 4.80216e+30f, 2.25966e+11f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0x72725252), SkBits2Float(0x72727272), SkBits2Float(0x72727255), SkBits2Float(0xda007b72));  // 4.79967e+30f, 4.80216e+30f, 4.80215e+30f, -9.04113e+15f
+path.lineTo(SkBits2Float(0x5252525a), SkBits2Float(0x72525252));  // 2.25831e+11f, 4.16585e+30f
+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525252), SkBits2Float(0x27725252), SkBits2Float(0x72727272));  // 4.80216e+30f, 2.25831e+11f, 3.36289e-15f, 4.80216e+30f
+path.lineTo(SkBits2Float(0x7bc00321), SkBits2Float(0x9aaaaaaa));  // 1.99397e+36f, -7.05861e-23f
+path.quadTo(SkBits2Float(0x72725572), SkBits2Float(0x00007272), SkBits2Float(0x525adada), SkBits2Float(0x52525252));  // 4.79991e+30f, 4.10552e-41f, 2.34994e+11f, 2.25831e+11f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad));  // -1.43243e-16f, 3.88004e+11f
+path.close();
+path.moveTo(SkBits2Float(0xa5252600), SkBits2Float(0x52b4adad));  // -1.43243e-16f, 3.88004e+11f
+path.quadTo(SkBits2Float(0x72727270), SkBits2Float(0x52525272), SkBits2Float(0x72525252), SkBits2Float(0x72727272));  // 4.80216e+30f, 2.25832e+11f, 4.16585e+30f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x72727255), SkBits2Float(0xda007b72), SkBits2Float(0x26525ada), SkBits2Float(0x72ada525));  // 4.80215e+30f, -9.04113e+15f, 7.29815e-16f, 6.87879e+30f
+path.quadTo(SkBits2Float(0x007b7272), SkBits2Float(0x525adada), SkBits2Float(0x52525252), SkBits2Float(0x72727252));  // 1.13368e-38f, 2.34994e+11f, 2.25831e+11f, 4.80215e+30f
+path.quadTo(SkBits2Float(0x52527272), SkBits2Float(0x52525252), SkBits2Float(0x72722772), SkBits2Float(0x72727272));  // 2.25966e+11f, 2.25831e+11f, 4.79636e+30f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x74727272), SkBits2Float(0x55747421), SkBits2Float(0x0000005b), SkBits2Float(0x72727268));  // 7.68345e+31f, 1.67987e+13f, 1.27518e-43f, 4.80216e+30f
+path.quadTo(SkBits2Float(0x52527272), SkBits2Float(0x52525252), SkBits2Float(0x72727272), SkBits2Float(0x72557272));  // 2.25966e+11f, 2.25831e+11f, 4.80216e+30f, 4.22775e+30f
+path.quadTo(SkBits2Float(0x5adada72), SkBits2Float(0x52525252), SkBits2Float(0x72725252), SkBits2Float(0x72727272));  // 3.08009e+16f, 2.25831e+11f, 4.79967e+30f, 4.80216e+30f
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzz763_57(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x68546829), SkBits2Float(0x555b2d29));  // 4.01225e+24f, 1.50617e+13f
+path.moveTo(SkBits2Float(0x1f2a322a), SkBits2Float(0x4b7b2108));  // 3.60404e-20f, 1.6458e+07f
+path.lineTo(SkBits2Float(0x2829ed84), SkBits2Float(0x5b2d2d55));  // 9.43289e-15f, 4.8745e+16f
+path.moveTo(SkBits2Float(0x6838552d), SkBits2Float(0xf0684f5b));  // 3.48195e+24f, -2.87586e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x2a8cef2a), SkBits2Float(0x682d2953), SkBits2Float(0xce682103), SkBits2Float(0x4b7bc055));  // 5.76397e-19f, 2.50349e-13f, 3.27093e+24f, -9.73619e+08f, 1.64988e+07f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x3b2a8c55));  // 4.85282e+16f, 0.00260236f
+path.lineTo(SkBits2Float(0x6838552d), SkBits2Float(0xf0684f5b));  // 3.48195e+24f, -2.87586e+29f
+path.close();
+path.moveTo(SkBits2Float(0x6838552d), SkBits2Float(0xf0684f5b));  // 3.48195e+24f, -2.87586e+29f
+path.conicTo(SkBits2Float(0xd2c00321), SkBits2Float(0xc0394b7b), SkBits2Float(0x8c08ed7a), SkBits2Float(0x211f2f2a), SkBits2Float(0x704b7b03));  // -4.12343e+11f, -2.89523f, -1.05485e-31f, 5.39337e-19f, 2.51897e+29f
+path.cubicTo(SkBits2Float(0x2d6829ed), SkBits2Float(0x5b2d555b), SkBits2Float(0x68275b2d), SkBits2Float(0x21685527), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 1.3197e-11f, 4.8789e+16f, 3.16127e+24f, 7.87174e-19f, 4.7323e-37f, 6.14991e+25f
+path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x5b2d2d55));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 4.8745e+16f
+path.moveTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5b));  // 3.50084e+24f, -2.87489e+29f
+path.conicTo(SkBits2Float(0x212a1f5b), SkBits2Float(0x228cef2a), SkBits2Float(0x682d2953), SkBits2Float(0xee682103), SkBits2Float(0x287bc055));  // 5.76397e-19f, 3.82003e-18f, 3.27093e+24f, -1.79601e+28f, 1.3975e-14f
+path.lineTo(SkBits2Float(0x5b2c6829), SkBits2Float(0x212a8c55));  // 4.85282e+16f, 5.7784e-19f
+path.conicTo(SkBits2Float(0x4b03213b), SkBits2Float(0xc07b2a08), SkBits2Float(0x5b2d7a6a), SkBits2Float(0xf0556830), SkBits2Float(0x2a8c555b));  // 8.59372e+06f, -3.92444f, 4.88298e+16f, -2.64185e+29f, 2.49282e-13f
+path.conicTo(SkBits2Float(0x0321212a), SkBits2Float(0x4b7bd2c0), SkBits2Float(0xed7ac039), SkBits2Float(0x2f2a8c08), SkBits2Float(0x7b03211f));  // 4.73517e-37f, 1.65035e+07f, -4.85023e+27f, 1.55112e-10f, 6.80863e+35f
+path.lineTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5b));  // 3.50084e+24f, -2.87489e+29f
+path.close();
+path.moveTo(SkBits2Float(0x6839552d), SkBits2Float(0xf0683b5b));  // 3.50084e+24f, -2.87489e+29f
+path.lineTo(SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d));  // 3.20982e+24f, 1.21279e-11f
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.conicTo(SkBits2Float(0x721f2a5b), SkBits2Float(0x212a8c55), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7b28), SkBits2Float(0x4797ed7a));  // 3.1526e+30f, 5.7784e-19f, 4.7323e-37f, 6.14984e+25f, 77787
+path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.quadTo(SkBits2Float(0x2828102a), SkBits2Float(0x2c682921), SkBits2Float(0x8c555bf6), SkBits2Float(0x6d03de30));  // 9.32938e-15f, 3.2992e-12f, -1.64366e-31f, 2.5507e+27f
+path.cubicTo(SkBits2Float(0x683f2d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x211f2a21));  // 3.61123e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 5.39271e-19f
+path.lineTo(SkBits2Float(0x3a803adf), SkBits2Float(0x8a294f1a));  // 0.000978317f, -8.15193e-33f
+path.quadTo(SkBits2Float(0x291d9628), SkBits2Float(0x2a43e62b), SkBits2Float(0x093a2a81), SkBits2Float(0x5c5c8ced));  // 3.49912e-14f, 1.73993e-13f, 2.24089e-33f, 2.48318e+17f
+path.lineTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.close();
+path.moveTo(SkBits2Float(0x68275b2d), SkBits2Float(0xf0685527));  // 3.16127e+24f, -2.87614e+29f
+path.cubicTo(SkBits2Float(0x3ac2213a), SkBits2Float(0x291d9628), SkBits2Float(0x2a43e62b), SkBits2Float(0x293a2a81), SkBits2Float(0x5c5c8ced), SkBits2Float(0x5c5c6e5c));  // 0.00148109f, 3.49912e-14f, 1.73993e-13f, 4.13372e-14f, 2.48318e+17f, 2.48183e+17f
+path.lineTo(SkBits2Float(0x1f212a8c), SkBits2Float(0xc0032108));  // 3.41283e-20f, -2.04889f
+path.lineTo(SkBits2Float(0xed847b4b), SkBits2Float(0x2d552829));  // -5.12513e+27f, 1.21166e-11f
+path.conicTo(SkBits2Float(0x552d5b5b), SkBits2Float(0x3b5a6839), SkBits2Float(0x5b2df068), SkBits2Float(0x2a212a1f), SkBits2Float(0x532a8cef));  // 1.1913e+13f, 0.00333263f, 4.89595e+16f, 1.43143e-13f, 7.32509e+11f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
+}
+
+static void fuzzhang_1(skiatest::Reporter* reporter, const char* filename) {

+    SkPath path;

+    path.setFillType((SkPath::FillType) 1);

+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0

+path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x668ece09), SkBits2Float(0x00000000), SkBits2Float(0x6751c81a), SkBits2Float(0x61c4b0fb));  // 0, 0, 3.37188e+23f, 0, 9.90666e+23f, 4.53539e+20f

+path.conicTo(SkBits2Float(0x66f837a9), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x3f823406));  // 5.86087e+23f, 0, 0, 0, 1.01721f

+path.close();

+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0

+path.quadTo(SkBits2Float(0x675b1bfe), SkBits2Float(0x00000000), SkBits2Float(0x67d76c42), SkBits2Float(0x6292c469));  // 1.03471e+24f, 0, 2.03461e+24f, 1.35369e+21f

+path.cubicTo(SkBits2Float(0x6a16df68), SkBits2Float(0x651a2f15), SkBits2Float(0x6c1e7f31), SkBits2Float(0x67a1f9b4), SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f));  // 4.55985e+25f, 4.55071e+22f, 7.66444e+26f, 1.52981e+24f, 0, 5.14279e+25f

+path.conicTo(SkBits2Float(0x680dcb75), SkBits2Float(0x68dd898d), SkBits2Float(0x681a434a), SkBits2Float(0x6871046b), SkBits2Float(0x3fea0440));  // 2.67843e+24f, 8.36944e+24f, 2.91394e+24f, 4.55269e+24f, 1.82825f

+path.quadTo(SkBits2Float(0x679e1b26), SkBits2Float(0x687703c4), SkBits2Float(0x00000000), SkBits2Float(0x687d2968));  // 1.49327e+24f, 4.66598e+24f, 0, 4.78209e+24f

+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0

+path.close();

+

+    SkPath path1(path);

+    path.reset();

+    path.setFillType((SkPath::FillType) 0);

+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0

+path.cubicTo(SkBits2Float(0x535353ec), SkBits2Float(0x98989898), SkBits2Float(0x98989898), SkBits2Float(0xf207f36e), SkBits2Float(0xf3f2f2f2), SkBits2Float(0xed3a9781));  // 9.07646e+11f, -3.94452e-24f, -3.94452e-24f, -2.69278e+30f, -3.84968e+31f, -3.60921e+27f

+path.quadTo(SkBits2Float(0xf8f8c0ed), SkBits2Float(0xf8f8f8f8), SkBits2Float(0x9f9f9f9f), SkBits2Float(0x3014149f));  // -4.03626e+34f, -4.03981e+34f, -6.76032e-20f, 5.38714e-10f

+

+    SkPath path2(path);

+    testPathOp(reporter, path1, path2, (SkPathOp) 0, filename);

+}

+
+static void release_13(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.setFillType(SkPath::kEvenOdd_FillType);
+path.moveTo(SkBits2Float(0xd4438848), SkBits2Float(0xd488cf64));  // -3.35922e+12f, -4.70076e+12f
+path.lineTo(SkBits2Float(0xd43a056e), SkBits2Float(0xd4851696));  // -3.19582e+12f, -4.57288e+12f
+path.quadTo(SkBits2Float(0xd3d48e79), SkBits2Float(0xd49fb136), SkBits2Float(0x00000000), SkBits2Float(0xd4d4d4d4));  // -1.82585e+12f, -5.48698e+12f, 0, -7.31283e+12f
+path.quadTo(SkBits2Float(0xd3d06670), SkBits2Float(0xd4a0bb38), SkBits2Float(0xd41d628f), SkBits2Float(0xd472c531));  // -1.79014e+12f, -5.52269e+12f, -2.70385e+12f, -4.17076e+12f
+path.lineTo(SkBits2Float(0xd43a0559), SkBits2Float(0xd485168e));  // -3.19581e+12f, -4.57287e+12f
+path.lineTo(SkBits2Float(0xd446958b), SkBits2Float(0xd4810278));  // -3.41165e+12f, -4.43274e+12f
+path.lineTo(SkBits2Float(0xd443884a), SkBits2Float(0xd488cf65));  // -3.35922e+12f, -4.70076e+12f
+path.quadTo(SkBits2Float(0xd47efa09), SkBits2Float(0xd49fd72a), SkBits2Float(0xd4a63f0f), SkBits2Float(0xd4b83ab3));  // -4.38047e+12f, -5.49208e+12f, -5.71218e+12f, -6.33007e+12f
+path.lineTo(SkBits2Float(0xd497ca70), SkBits2Float(0xd4c4d4ae));  // -5.21549e+12f, -6.76305e+12f
+path.lineTo(SkBits2Float(0xd459d4d4), SkBits2Float(0xd4c4d4d4));  // -3.74231e+12f, -6.76307e+12f
+path.lineTo(SkBits2Float(0xd440daf9), SkBits2Float(0xd4c632d3));  // -3.31323e+12f, -6.81005e+12f
+path.lineTo(SkBits2Float(0xd4438848), SkBits2Float(0xd488cf64));  // -3.35922e+12f, -4.70076e+12f
+path.close();
+path.moveTo(SkBits2Float(0xd4767560), SkBits2Float(0xd4d1ca84));  // -4.23412e+12f, -7.20837e+12f
+path.lineTo(SkBits2Float(0xd4422174), SkBits2Float(0xd4d02069));  // -3.33514e+12f, -7.15118e+12f
+path.lineTo(SkBits2Float(0xd440daa3), SkBits2Float(0xd4c632d9));  // -3.31321e+12f, -6.81005e+12f
+path.lineTo(SkBits2Float(0xd41017bc), SkBits2Float(0xd4cb99b6));  // -2.47549e+12f, -6.99566e+12f
+path.lineTo(SkBits2Float(0xd442213b), SkBits2Float(0xd4d02067));  // -3.33512e+12f, -7.15117e+12f
+path.lineTo(SkBits2Float(0xd442d4d4), SkBits2Float(0xd4d4d4d4));  // -3.34718e+12f, -7.31283e+12f
+path.lineTo(SkBits2Float(0xd4767560), SkBits2Float(0xd4d1ca84));  // -4.23412e+12f, -7.20837e+12f
+path.close();
+path.moveTo(SkBits2Float(0xd46c7a11), SkBits2Float(0xd46c7a2e));  // -4.06264e+12f, -4.06265e+12f
+path.lineTo(SkBits2Float(0xd484e02c), SkBits2Float(0xd45fafcd));  // -4.56557e+12f, -3.84291e+12f
+path.lineTo(SkBits2Float(0xd462c867), SkBits2Float(0xd45655f7));  // -3.8961e+12f, -3.68226e+12f
+path.lineTo(SkBits2Float(0xd45ac463), SkBits2Float(0xd45ac505));  // -3.75839e+12f, -3.75843e+12f
+path.lineTo(SkBits2Float(0xd43d2fa9), SkBits2Float(0xd43d2fb5));  // -3.25019e+12f, -3.2502e+12f
+path.lineTo(SkBits2Float(0xd41d6287), SkBits2Float(0xd472c52a));  // -2.70385e+12f, -4.17076e+12f
+path.quadTo(SkBits2Float(0x00000000), SkBits2Float(0xd3db1b95), SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, -1.88212e+12f, 0, 0
+path.quadTo(SkBits2Float(0xd4b7efac), SkBits2Float(0x00000000), SkBits2Float(0xd4d0e88f), SkBits2Float(0xd40b8b46));  // -6.32e+12f, 0, -7.17804e+12f, -2.39735e+12f
+path.lineTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0x00000000));  // -7.31283e+12f, 0
+path.lineTo(SkBits2Float(0xdcdc154b), SkBits2Float(0x00000000));  // -4.95583e+17f, 0
+path.lineTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4c4d477));  // -7.31283e+12f, -6.76303e+12f
+path.lineTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d442));  // -7.31283e+12f, -7.31275e+12f
+path.lineTo(SkBits2Float(0xd4d4a691), SkBits2Float(0xd4d4d442));  // -7.30662e+12f, -7.31275e+12f
+path.lineTo(SkBits2Float(0xd454d4d4), SkBits2Float(0xd4d4aa30));  // -3.65641e+12f, -7.30711e+12f
+path.lineTo(SkBits2Float(0xd4bd9def), SkBits2Float(0xd4d43df0));  // -6.51519e+12f, -7.29258e+12f
+path.lineTo(SkBits2Float(0xd4767560), SkBits2Float(0xd4d1ca84));  // -4.23412e+12f, -7.20837e+12f
+path.lineTo(SkBits2Float(0xd497ca70), SkBits2Float(0xd4c4d4ae));  // -5.21549e+12f, -6.76305e+12f
+path.lineTo(SkBits2Float(0xd4bab953), SkBits2Float(0xd4c4d48e));  // -6.41579e+12f, -6.76304e+12f
+path.lineTo(SkBits2Float(0xd4a63f0f), SkBits2Float(0xd4b83ab3));  // -5.71218e+12f, -6.33007e+12f
+path.lineTo(SkBits2Float(0xd4ae61eb), SkBits2Float(0xd4ae61f4));  // -5.99174e+12f, -5.99174e+12f
+path.lineTo(SkBits2Float(0xd46c7a11), SkBits2Float(0xd46c7a2e));  // -4.06264e+12f, -4.06265e+12f
+path.close();
+path.moveTo(SkBits2Float(0xd46c7a11), SkBits2Float(0xd46c7a2e));  // -4.06264e+12f, -4.06265e+12f
+path.lineTo(SkBits2Float(0xd446965c), SkBits2Float(0xd4810237));  // -3.4117e+12f, -4.4327e+12f
+path.lineTo(SkBits2Float(0xd45ac549), SkBits2Float(0xd45ac55f));  // -3.75845e+12f, -3.75846e+12f
+path.lineTo(SkBits2Float(0xd46c7a11), SkBits2Float(0xd46c7a2e));  // -4.06264e+12f, -4.06265e+12f
+path.close();
+path.moveTo(SkBits2Float(0xd4b46028), SkBits2Float(0xd41e572a));  // -6.19766e+12f, -2.72027e+12f
+path.lineTo(SkBits2Float(0xd4cde20a), SkBits2Float(0xd434bb57));  // -7.07408e+12f, -3.10495e+12f
+path.lineTo(SkBits2Float(0xd4c75ffe), SkBits2Float(0xd46f215d));  // -6.85047e+12f, -4.10823e+12f
+path.lineTo(SkBits2Float(0xd4b46028), SkBits2Float(0xd41e572a));  // -6.19766e+12f, -2.72027e+12f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.setFillType(SkPath::kWinding_FillType);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0x00000000), SkBits2Float(0xa5a50000), SkBits2Float(0xd4d4a5a5), SkBits2Float(0xd4d4d4d4));  // 0, -2.86229e-16f, -7.3065e+12f, -7.31283e+12f
+path.quadTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4cfd4d4), SkBits2Float(0xd4d41dd4));  // -7.31283e+12f, -7.31283e+12f, -7.14103e+12f, -7.28827e+12f
+path.quadTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d432d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4a5a5d4));  // -7.31283e+12f, -7.29109e+12f, -7.31283e+12f, -5.69161e+12f
+path.quadTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0x00000000));  // -7.31283e+12f, -7.31283e+12f, -7.31283e+12f, 0
+path.moveTo(SkBits2Float(0xa5a5a500), SkBits2Float(0xd4d4d4a5));  // -2.87347e-16f, -7.31281e+12f
+path.quadTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0x2ad4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4cfd4d4));  // -7.31283e+12f, 3.78064e-13f, -7.31283e+12f, -7.14103e+12f
+path.quadTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4));  // -7.31283e+12f, -7.31283e+12f, -7.31283e+12f, -7.31283e+12f
+path.quadTo(SkBits2Float(0xd4d40000), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4));  // -7.28426e+12f, -7.31283e+12f, -7.31283e+12f, -7.31283e+12f
+
+    SkPath path2(path);
+    testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzzhang_2(skiatest::Reporter* reporter, const char* filename) {

+    SkPath path;

+    path.setFillType((SkPath::FillType) 0);

+path.setFillType(SkPath::kWinding_FillType);

+path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268));  // 1.59583e+13f, 5.20715e+30f

+path.quadTo(SkBits2Float(0xe0e02972), SkBits2Float(0xe0e060e0), SkBits2Float(0x728e4603), SkBits2Float(0x72727272));  // -1.29221e+20f, -1.29345e+20f, 5.63603e+30f, 4.80216e+30f

+path.lineTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268));  // 1.59583e+13f, 5.20715e+30f

+path.close();

+path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268));  // 1.59583e+13f, 5.20715e+30f

+path.quadTo(SkBits2Float(0x68720052), SkBits2Float(0x52527372), SkBits2Float(0x00527252), SkBits2Float(0x728e4601));  // 4.57127e+24f, 2.2597e+11f, 7.57152e-39f, 5.63603e+30f

+path.quadTo(SkBits2Float(0x52ec7272), SkBits2Float(0x6265527f), SkBits2Float(0x8e460152), SkBits2Float(0x72ff8072));  // 5.07766e+11f, 1.05756e+21f, -2.4406e-30f, 1.01215e+31f

+path.lineTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268));  // 1.59583e+13f, 5.20715e+30f

+path.close();

+path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268));  // 1.59583e+13f, 5.20715e+30f

+path.lineTo(SkBits2Float(0x52626552), SkBits2Float(0x72727272));  // 2.43091e+11f, 4.80216e+30f

+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x62727272), SkBits2Float(0x39393939), SkBits2Float(0x728bc739));  // 4.80216e+30f, 1.11809e+21f, 0.000176643f, 5.53719e+30f

+path.cubicTo(SkBits2Float(0x72728092), SkBits2Float(0x72727260), SkBits2Float(0x4d727272), SkBits2Float(0x5252522a), SkBits2Float(0x72735252), SkBits2Float(0x72707272));  // 4.80325e+30f, 4.80215e+30f, 2.54224e+08f, 2.2583e+11f, 4.81948e+30f, 4.76254e+30f

+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x56727272), SkBits2Float(0x72720152), SkBits2Float(0x72727270));  // 4.80216e+30f, 6.66433e+13f, 4.79341e+30f, 4.80216e+30f

+path.quadTo(SkBits2Float(0x52526172), SkBits2Float(0x8e460300), SkBits2Float(0x72727272), SkBits2Float(0x52525272));  // 2.25894e+11f, -2.44068e-30f, 4.80216e+30f, 2.25832e+11f

+path.conicTo(SkBits2Float(0xb5727272), SkBits2Float(0x7f2b727f), SkBits2Float(0x607272ff), SkBits2Float(0x72727276), SkBits2Float(0x2a527272));  // -9.03186e-07f, 2.27892e+38f, 6.98812e+19f, 4.80216e+30f, 1.86915e-13f

+path.lineTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268));  // 1.59583e+13f, 5.20715e+30f

+path.close();

+path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268));  // 1.59583e+13f, 5.20715e+30f

+path.lineTo(SkBits2Float(0x72727272), SkBits2Float(0x52525f72));  // 4.80216e+30f, 2.25886e+11f

+path.lineTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268));  // 1.59583e+13f, 5.20715e+30f

+path.close();

+path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268));  // 1.59583e+13f, 5.20715e+30f

+path.quadTo(SkBits2Float(0x52727272), SkBits2Float(0x64655252), SkBits2Float(0x72c1c152), SkBits2Float(0x72727272));  // 2.60326e+11f, 1.69209e+22f, 7.67543e+30f, 4.80216e+30f

+

+    SkPath path1(path);

+    path.reset();

+    path.setFillType((SkPath::FillType) 0);

+path.setFillType(SkPath::kWinding_FillType);

+

+    SkPath path2(path);

+    testPathOpFail(reporter, path1, path2, (SkPathOp) 1, filename);

+}

+

+static void fuzzhang_3(skiatest::Reporter* reporter, const char* filename) {

+    SkPath path;

+    path.setFillType((SkPath::FillType) 0);

+path.setFillType(SkPath::kWinding_FillType);

+

+    SkPath path1(path);

+    path.reset();

+    path.setFillType((SkPath::FillType) 0);

+path.setFillType(SkPath::kWinding_FillType);

+path.moveTo(SkBits2Float(0x46090052), SkBits2Float(0x7270726c));  // 8768.08f, 4.76254e+30f

+path.moveTo(SkBits2Float(0xe0437272), SkBits2Float(0x03e0e060));  // -5.63338e+19f, 1.32171e-36f

+path.close();

+path.moveTo(SkBits2Float(0xe0437272), SkBits2Float(0x03e0e060));  // -5.63338e+19f, 1.32171e-36f

+path.lineTo(SkBits2Float(0x77727272), SkBits2Float(0x52520072));  // 4.91741e+33f, 2.25488e+11f

+path.lineTo(SkBits2Float(0x46090052), SkBits2Float(0x727272ce));  // 8768.08f, 4.80219e+30f

+path.quadTo(SkBits2Float(0x725252ec), SkBits2Float(0x72727272), SkBits2Float(0x72727272), SkBits2Float(0x39393962));  // 4.16589e+30f, 4.80216e+30f, 4.80216e+30f, 0.000176644f

+path.lineTo(SkBits2Float(0x6c460900), SkBits2Float(0x72727072));  // 9.57639e+26f, 4.802e+30f

+path.cubicTo(SkBits2Float(0xe0e060e0), SkBits2Float(0x72943603), SkBits2Float(0x72777272), SkBits2Float(0x5c525200), SkBits2Float(0x46090052), SkBits2Float(0x727272ce));  // -1.29345e+20f, 5.87124e+30f, 4.90119e+30f, 2.368e+17f, 8768.08f, 4.80219e+30f

+path.quadTo(SkBits2Float(0x725252ec), SkBits2Float(0x72727272), SkBits2Float(0x72727272), SkBits2Float(0x39393962));  // 4.16589e+30f, 4.80216e+30f, 4.80216e+30f, 0.000176644f

+path.lineTo(SkBits2Float(0x0052ca00), SkBits2Float(0x728e4603));  // 7.60297e-39f, 5.63603e+30f

+path.quadTo(SkBits2Float(0xff727272), SkBits2Float(0x52527272), SkBits2Float(0x39392072), SkBits2Float(0xe0393939));  // -3.22267e+38f, 2.25966e+11f, 0.000176551f, -5.3387e+19f

+path.lineTo(SkBits2Float(0xe0437272), SkBits2Float(0x03e0e060));  // -5.63338e+19f, 1.32171e-36f

+path.close();

+path.moveTo(SkBits2Float(0xe0437272), SkBits2Float(0x03e0e060));  // -5.63338e+19f, 1.32171e-36f

+path.cubicTo(SkBits2Float(0xdada7272), SkBits2Float(0x2dff7272), SkBits2Float(0x767272f0), SkBits2Float(0x72727272), SkBits2Float(0x21727f72), SkBits2Float(0x0b210929));  // -3.07437e+16f, 2.9041e-11f, 1.22936e+33f, 4.80216e+30f, 8.21615e-19f, 3.10144e-32f

+path.cubicTo(SkBits2Float(0xd6d6d6d6), SkBits2Float(0x72a5d6d6), SkBits2Float(0x72553872), SkBits2Float(0xdada7072), SkBits2Float(0x5252525a), SkBits2Float(0x72727252));  // -1.18109e+14f, 6.56957e+30f, 4.22327e+30f, -3.07426e+16f, 2.25831e+11f, 4.80215e+30f

+path.quadTo(SkBits2Float(0x72725572), SkBits2Float(0xdada0072), SkBits2Float(0x52524b5a), SkBits2Float(0x72528000));  // 4.79991e+30f, -3.0681e+16f, 2.25802e+11f, 4.16938e+30f

+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0xca005252), SkBits2Float(0x46030052), SkBits2Float(0x7272728e));  // 4.80216e+30f, -2.10242e+06f, 8384.08f, 4.80217e+30f

+path.quadTo(SkBits2Float(0x7272ff72), SkBits2Float(0x20725252), SkBits2Float(0x39393939), SkBits2Float(0xd76ee039));  // 4.81307e+30f, 2.05254e-19f, 0.000176643f, -2.62647e+14f

+path.cubicTo(SkBits2Float(0xdada7272), SkBits2Float(0x2dff7272), SkBits2Float(0x767272f0), SkBits2Float(0x72727272), SkBits2Float(0x21727f72), SkBits2Float(0x0b210929));  // -3.07437e+16f, 2.9041e-11f, 1.22936e+33f, 4.80216e+30f, 8.21615e-19f, 3.10144e-32f

+path.cubicTo(SkBits2Float(0xd6d6d6d6), SkBits2Float(0x72a5d6d6), SkBits2Float(0x72553872), SkBits2Float(0xdada7072), SkBits2Float(0x5252525a), SkBits2Float(0x72727252));  // -1.18109e+14f, 6.56957e+30f, 4.22327e+30f, -3.07426e+16f, 2.25831e+11f, 4.80215e+30f

+path.quadTo(SkBits2Float(0x72725572), SkBits2Float(0xdada0072), SkBits2Float(0x52524b5a), SkBits2Float(0x72528000));  // 4.79991e+30f, -3.0681e+16f, 2.25802e+11f, 4.16938e+30f

+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525252), SkBits2Float(0x27725252), SkBits2Float(0x72727272));  // 4.80216e+30f, 2.25831e+11f, 3.36289e-15f, 4.80216e+30f

+path.quadTo(SkBits2Float(0x72667254), SkBits2Float(0x00000040), SkBits2Float(0x00a70155), SkBits2Float(0x726800ff));  // 4.56447e+30f, 8.96831e-44f, 1.5337e-38f, 4.59531e+30f

+path.quadTo(SkBits2Float(0x7b727272), SkBits2Float(0xad000c52), SkBits2Float(0x1c10adad), SkBits2Float(0x72728d8a));  // 1.25886e+36f, -7.27869e-12f, 4.78701e-22f, 4.80425e+30f

+path.quadTo(SkBits2Float(0xff056546), SkBits2Float(0x727205ff), SkBits2Float(0x524b5aff), SkBits2Float(0x64005252));  // -1.77313e+38f, 4.79377e+30f, 2.18351e+11f, 9.46846e+21f

+path.quadTo(SkBits2Float(0x72524872), SkBits2Float(0xdada7272), SkBits2Float(0x5252525a), SkBits2Float(0x72727252));  // 4.16508e+30f, -3.07437e+16f, 2.25831e+11f, 4.80215e+30f

+path.quadTo(SkBits2Float(0x72724172), SkBits2Float(0xdad10072), SkBits2Float(0x52524b5a), SkBits2Float(0x725b8000));  // 4.79837e+30f, -2.94144e+16f, 2.25802e+11f, 4.34765e+30f

+path.quadTo(SkBits2Float(0x72727272), SkBits2Float(0x52525252), SkBits2Float(0x27725252), SkBits2Float(0x72727272));  // 4.80216e+30f, 2.25831e+11f, 3.36289e-15f, 4.80216e+30f

+path.quadTo(SkBits2Float(0x72728372), SkBits2Float(0x00000040), SkBits2Float(0xf6a70147), SkBits2Float(0xc2c2c256));  // 4.80347e+30f, 8.96831e-44f, -1.69363e+33f, -97.3796f

+path.lineTo(SkBits2Float(0xe0437272), SkBits2Float(0x03e0e060));  // -5.63338e+19f, 1.32171e-36f

+path.close();

+path.moveTo(SkBits2Float(0x7a787a7a), SkBits2Float(0x7a3a7a7a));  // 3.22543e+35f, 2.42063e+35f

+path.lineTo(SkBits2Float(0x8f4603e0), SkBits2Float(0x72727272));  // -9.7629e-30f, 4.80216e+30f

+path.quadTo(SkBits2Float(0x00807272), SkBits2Float(0x46090052), SkBits2Float(0x7270726c), SkBits2Float(0x60e04372));  // 1.1796e-38f, 8768.08f, 4.76254e+30f, 1.29279e+20f

+path.moveTo(SkBits2Float(0x943603e0), SkBits2Float(0x77727272));  // -9.18942e-27f, 4.91741e+33f

+path.quadTo(SkBits2Float(0x5c525200), SkBits2Float(0x46090052), SkBits2Float(0x727272ce), SkBits2Float(0x5252ec72));  // 2.368e+17f, 8768.08f, 4.80219e+30f, 2.26478e+11f

+

+    SkPath path2(path);

+    testPathOpFail(reporter, path1, path2, (SkPathOp) 3, filename);

+}

+

+
 static struct TestDesc failTests[] = {
+    TEST(fuzzhang_3),
+    TEST(fuzzhang_2),
+    TEST(release_13),
+    TEST(fuzzhang_1),
+    TEST(fuzz763_57),
+    TEST(fuzz763_56),
+    TEST(fuzz763_55),
+    TEST(fuzz763_54),
+    TEST(fuzz763_53),
+    TEST(fuzz763_52),
+    TEST(fuzz763_51),
+    TEST(fuzz763_50),
+    TEST(fuzz763_49),
+    TEST(fuzz763_48),
+    TEST(fuzz763_47),
+    TEST(fuzz763_46),
+    TEST(fuzz763_45),
+    TEST(fuzz763_44),
+    TEST(fuzz763_43),
+    TEST(fuzz763_42),
+    TEST(fuzz763_41),
+    TEST(fuzz763_40),
+    TEST(fuzz763_39),
+    TEST(fuzz763_38),
+    TEST(fuzz763_37),
+    TEST(fuzz763_36),
+    TEST(fuzz763_35),
+    TEST(fuzz763_34),
+    TEST(fuzz763_33),
+    TEST(fuzz763_32),
+    TEST(fuzz763_31),
+    TEST(fuzz763_30),
+    TEST(fuzz763_29),
+    TEST(fuzz763_28),
+    TEST(fuzz763_27),
+    TEST(fuzz763_26),
+    TEST(fuzz763_25),
+    TEST(fuzz763_24),
+    TEST(fuzz763_23),
+    TEST(fuzz763_22),
+    TEST(fuzz763_21),
+    TEST(fuzz763_20),
+    TEST(fuzz763_19),
+    TEST(fuzz763_18),
+    TEST(fuzz763_17),
+    TEST(fuzz763_16),
+    TEST(fuzz763_15),
+    TEST(fuzz763_14),
+    TEST(fuzz763_13),
+    TEST(fuzz763_12),
+    TEST(fuzz763_11),
+    TEST(fuzz763_10),
+    TEST(kfuzz2),
+    TEST(fuzz763_7),
+    TEST(fuzz763_6),
+    TEST(fuzz763_2c),
+    TEST(fuzz763_2b),
+    TEST(fuzz763_2a),
+    TEST(fuzz763_5a),
+    TEST(fuzz763_3a),
+    TEST(fuzz763_1a),
+    TEST(fuzz763_1b),
+    TEST(fuzz763_1c),
+    TEST(fuzz763_2),
+    TEST(fuzz763_5),
+    TEST(fuzz763_3),
+    TEST(fuzz763_4),
+    TEST(fuzz763_9),
+    TEST(fuzz1450_1),
+    TEST(fuzz1450_0),
+    TEST(bug597926_0),
+    TEST(fuzz535151),
+    TEST(fuzz753_91),
+    TEST(fuzz714),
     TEST(fuzz487a),
+    TEST(fuzz433),
+    TEST(fuzz1),
     TEST(fuzz487b),
     TEST(fuzz433b),
-    TEST(fuzz433),
     TEST(bufferOverflow),
 };
 
@@ -5608,5 +8649,14 @@
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
-    RunTestSet(reporter, failTests, failTestCount, 0, 0, false);
+    RunTestSet(reporter, failTests, failTestCount, nullptr, nullptr, nullptr, false);
+}
+
+static struct TestDesc repTests[] = {
+    TEST(fuzz763_5a),
+};
+
+DEF_TEST(PathOpsRepOp, reporter) {
+  for (int index = 0; index < 1; ++index)
+    RunTestSet(reporter, repTests, SK_ARRAY_COUNT(repTests), nullptr, nullptr, nullptr, false);
 }
diff --git a/src/third_party/skia/tests/PathOpsQuadIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsQuadIntersectionTest.cpp
index 1ddbbcc..f5eb32f 100644
--- a/src/third_party/skia/tests/PathOpsQuadIntersectionTest.cpp
+++ b/src/third_party/skia/tests/PathOpsQuadIntersectionTest.cpp
@@ -14,9 +14,13 @@
 static void standardTestCases(skiatest::Reporter* reporter) {
     bool showSkipped = false;
     for (size_t index = 0; index < quadraticTests_count; ++index) {
-        const SkDQuad& quad1 = quadraticTests[index][0];
+        const QuadPts& q1 = quadraticTests[index][0];
+        SkDQuad quad1;
+        quad1.debugSet(q1.fPts);
         SkASSERT(ValidQuad(quad1));
-        const SkDQuad& quad2 = quadraticTests[index][1];
+        const QuadPts& q2 = quadraticTests[index][1];
+        SkDQuad quad2;
+        quad2.debugSet(q2.fPts);
         SkASSERT(ValidQuad(quad2));
         SkReduceOrder reduce1, reduce2;
         int order1 = reduce1.reduce(quad1);
@@ -52,7 +56,71 @@
     }
 }
 
-static const SkDQuad testSet[] = {
+static const QuadPts testSet[] = {
+{{{123.637985f, 102.405312f}, {125.172699f, 104.575714f}, {123.387383f, 106.91227f}}},
+{{{123.388428f, 106.910896f}, {123.365623f, 106.94088f}, {123.320007f, 107.000946f}}},
+
+{{{-0.001019871095195412636, -0.008523519150912761688}, {-0.005396408028900623322, -0.005396373569965362549}, {-0.02855382487177848816, -0.02855364233255386353}}},
+{{{-0.004567248281091451645, -0.01482933573424816132}, {-0.01142475008964538574, -0.01140109263360500336}, {-0.02852955088019371033, -0.02847047336399555206}}},
+
+{{{1, 1}, {0, 2}, {3, 3}}},
+{{{3, 0}, {0, 1}, {1, 2}}},
+
+{{{0.33333333333333326, 0.81481481481481488}, {0.63395173631977997, 0.68744136726313931}, {1.205684411948591, 0.81344322326274499}}},
+{{{0.33333333333333326, 0.81481481481481488}, {0.63396444791444551, 0.68743368362444768}, {1.205732763658403, 0.81345617746834109}}},
+
+{{{4981.9990234375, 1590}, {4981.9990234375, 1617.7523193359375}, {4962.375, 1637.3760986328125}}},
+{{{4962.3759765625, 1637.3760986328125}, {4982, 1617.7523193359375}, {4982, 1590}}},
+
+{{{48.7416f, 7.74160004f}, {96.4831848f, -40}, {164, -40}}},
+{{{56.9671326f, 0}, {52.7835083f, 3.69968891f}, {48.7416f, 7.74160004f}}},
+
+{{{138, 80}, {147.15692138671875, 80}, {155.12803649902344, 82.86279296875}}},
+{{{155.12803649902344, 82.86279296875}, {153.14971923828125, 82.152290344238281}, {151.09841918945312, 81.618133544921875}}},
+
+{{{88, 130}, {88, 131.54483032226562}, {88.081489562988281, 133.0560302734375}}},
+{{{88.081489562988281, 133.0560302734375}, {88, 131.54483032226562}, {88, 130}}},
+
+{{{0.59987992,2.14448452}, {0.775417507,1.95606446}, {1.00564098,1.79310346}}},
+{{{1.00564098,1.79310346}, {1.25936198,1.615623}, {1.35901463,1.46834028}}},
+
+{{{3,0}, {0,1}, {3,2}}},
+{{{2,0}, {1,1}, {2,2}}},
+
+{{{38.656852722167969, 38.656852722167969}, {38.651023864746094, 38.662681579589844}, {38.644744873046875, 38.668937683105469}}},
+{{{38.656852722167969, 38.656852722167969}, {36.313709259033203, 41}, {33, 41}}},
+
+{{{4914.9990234375, 1523}, {4942.75146484375, 1523}, {4962.375, 1542.6239013671875}}},
+{{{4962.3759765625, 1542.6239013671875}, {4942.75244140625, 1523}, {4915, 1523}}},
+
+{{{4867.623046875, 1637.3760986328125}, {4847.9990234375, 1617.7523193359375}, {4847.9990234375, 1590}}},
+{{{4848, 1590}, {4848, 1617.7523193359375}, {4867.6240234375, 1637.3760986328125}}},
+
+{{{102.64466094970703, 165.3553466796875}, {110.79246520996094, 173.50314331054687}, {120.81797790527344, 177.11778259277344}}},
+{{{113.232177734375, 173.57899475097656}, {116.88026428222656, 175.69805908203125}, {120.81797790527344, 177.11778259277344}}},
+
+{{{-37.3484879,10.0192947}, {-36.4966316,13.2140198}, {-38.1506348,16.0788383}}},
+{{{-38.1462746,16.08918}, {-36.4904327,13.2193804}, {-37.3484879,10.0192947}}},
+
+{{{-37.3513985,10.0082998}, {-36.4938011,13.2090998}, {-38.1506004,16.0788002}}},
+{{{-37.3508987,10.0102997}, {-36.4930992,13.2110004}, {-38.1497993,16.0809002}}},
+
+{{{-37.3508987,10.0102997}, {-37.3510017,10.0098}, {-37.3512001,10.0093002}}},
+{{{-49.0778008,19.0097008}, {-38.2086983,6.80954981}, {-37.3508987,10.0102997}}},
+
+{{{SkBits2Float(0xc22423b2), SkBits2Float(0x40afae2c)},
+        {SkBits2Float(0xc2189b24), SkBits2Float(0x40e3f058)},
+        {SkBits2Float(0xc21511d9), SkBits2Float(0x41251125)}}},
+{{{SkBits2Float(0xc2153d2f), SkBits2Float(0x412299db)},
+        {SkBits2Float(0xc2153265), SkBits2Float(0x41233845)},
+        {SkBits2Float(0xc21527fc), SkBits2Float(0x4123d684)}}},
+
+{{{-37.3097496, 10.1625624}, {-37.2992134, 10.2012377}, {-37.2890472, 10.239872}}},
+{{{-41.0348587, 5.49001122}, {-38.1515045, 7.12308884}, {-37.2674294, 10.3166857}}},
+
+{{{-52.8062439,14.1493912}, {-53.6638947,10.948595}, {-52.0070419,8.07883835}}},
+{{{-52.8054848,14.1522331}, {-53.6633072,10.9514809}, {-52.0066071,8.08163643}}},
+
 {{{441.853149, 308.209106}, {434.672272, 315.389984}, {424.516998, 315.389984}}},
 {{{385.207275, 334.241272}, {406.481598, 312.96698}, {436.567993, 312.96698}}},
 
@@ -265,16 +333,20 @@
 const size_t testSetCount = SK_ARRAY_COUNT(testSet);
 
 static void oneOffTest1(skiatest::Reporter* reporter, size_t outer, size_t inner) {
-    const SkDQuad& quad1 = testSet[outer];
+    const QuadPts& q1 = testSet[outer];
+    SkDQuad quad1;
+    quad1.debugSet(q1.fPts);
     SkASSERT(ValidQuad(quad1));
-    const SkDQuad& quad2 = testSet[inner];
+    const QuadPts& q2 = testSet[inner];
+    SkDQuad quad2;
+    quad2.debugSet(q2.fPts);
     SkASSERT(ValidQuad(quad2));
-    SkIntersections intersections2;
-    intersections2.intersect(quad1, quad2);
-    for (int pt = 0; pt < intersections2.used(); ++pt) {
-        double tt1 = intersections2[0][pt];
+    SkIntersections intersections;
+    intersections.intersect(quad1, quad2);
+    for (int pt = 0; pt < intersections.used(); ++pt) {
+        double tt1 = intersections[0][pt];
         SkDPoint xy1 = quad1.ptAtT(tt1);
-        double tt2 = intersections2[1][pt];
+        double tt2 = intersections[1][pt];
         SkDPoint xy2 = quad2.ptAtT(tt2);
         if (!xy1.approximatelyEqual(xy2)) {
             SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
@@ -289,10 +361,6 @@
     }
 }
 
-DEF_TEST(PathOpsQuadIntersectionOneOff, reporter) {
-    oneOffTest1(reporter, 0, 1);
-}
-
 static void oneOffTests(skiatest::Reporter* reporter) {
     for (size_t outer = 0; outer < testSetCount - 1; ++outer) {
         for (size_t inner = outer + 1; inner < testSetCount; ++inner) {
@@ -301,7 +369,9 @@
     }
 }
 
-static const SkDQuad coincidentTestSet[] = {
+static const QuadPts coincidentTestSet[] = {
+    {{{4914.9990234375, 1523}, {4942.75146484375, 1523}, {4962.375, 1542.6239013671875}}},
+    {{{4962.3759765625, 1542.6239013671875}, {4942.75244140625, 1523}, {4915, 1523}}},
 #if 0
     {{{97.9337615966796875,100}, {88,112.94264984130859375}, {88,130}}},
     {{{88,130}, {88,124.80951690673828125}, {88.91983795166015625,120}}},
@@ -315,15 +385,19 @@
 static const int coincidentTestSetCount = (int) SK_ARRAY_COUNT(coincidentTestSet);
 
 static void coincidentTestOne(skiatest::Reporter* reporter, int test1, int test2) {
-    const SkDQuad& quad1 = coincidentTestSet[test1];
+    const QuadPts& q1 = coincidentTestSet[test1];
+    SkDQuad quad1;
+    quad1.debugSet(q1.fPts);
     SkASSERT(ValidQuad(quad1));
-    const SkDQuad& quad2 = coincidentTestSet[test2];
+    const QuadPts& q2 = coincidentTestSet[test2];
+    SkDQuad quad2;
+    quad2.debugSet(q2.fPts);
     SkASSERT(ValidQuad(quad2));
     SkIntersections intersections2;
     intersections2.intersect(quad1, quad2);
-    REPORTER_ASSERT(reporter, intersections2.coincidentUsed() == 2);
-    REPORTER_ASSERT(reporter, intersections2.used() == 2);
-    for (int pt = 0; pt < intersections2.coincidentUsed(); ++pt) {
+    REPORTER_ASSERT(reporter, intersections2.debugCoincidentUsed() >= 2);
+    REPORTER_ASSERT(reporter, intersections2.used() >= 2);
+    for (int pt = 0; pt < intersections2.debugCoincidentUsed(); pt += 2) {
         double tt1 = intersections2[0][pt];
         double tt2 = intersections2[1][pt];
         SkDPoint pt1 = quad1.ptAtT(tt1);
@@ -338,79 +412,12 @@
     }
 }
 
-DEF_TEST(PathOpsQuadIntersectionCoincidenceOneOff, reporter) {
-    coincidentTestOne(reporter, 0, 1);
-}
-
-static int floatSign(double x) {
-    return x < 0 ? -1 : x > 0 ? 1 : 0;
-}
-
-static const SkDQuad pointFinderTestSet[] = {
-                                                                                                                                //>=0.633974464         0.633974846 <=
-{{{1.2071879545809394, 0.82163474041730045}, {1.1534203513372994, 0.52790870069930229},
-        {1.0880000000000001, 0.29599999999999982}}},  //t=0.63155333662549329, 0.80000000000000004
-{{{1.2071879545809394, 0.82163474041730045}, {1.2065040319428038, 0.81766753259119995},
-        {1.2058123269101506, 0.81370135061854221}}},  //t=0.63155333662549329, 0.6339049773632347
-{{{1.2058123269101506, 0.81370135061854221}, {1.152376363978022, 0.5244097415381026},
-        {1.0880000000000001, 0.29599999999999982}}},  //t=0.6339049773632347,  0.80000000000000004
-                                                                                                                                //>=0.633974083         0.633975227 <=
-{{{0.33333333333333326, 0.81481481481481488}, {0.63395173631977997, 0.68744136726313931},
-        {1.205684411948591, 0.81344322326274499}}},   //t=0.33333333333333331, 0.63395173631977986
-{{{0.33333333333333326, 0.81481481481481488}, {0.63396444791444551, 0.68743368362444768},
-        {1.205732763658403, 0.81345617746834109}}},   //t=0.33333333333333331, 0.63396444791444551
-{{{1.205684411948591, 0.81344322326274499}, {1.2057085875611198, 0.81344969999329253},
-        {1.205732763658403, 0.81345617746834109}}},   //t=0.63395173631977986, 0.63396444791444551
-{{{1.205732763658403, 0.81345617746834109}, {1.267928895828891, 0.83008534558465619},
-        {1.3333333333333333, 0.85185185185185175}}},  //t=0.63396444791444551, 0.66666666666666663
-};
-
-static void pointFinder(const SkDQuad& q1, const SkDQuad& q2) {
-    for (int index = 0; index < 3; ++index) {
-        double t = q1.nearestT(q2[index]);
-        SkDPoint onQuad = q1.ptAtT(t);
-        SkDebugf("%s t=%1.9g (%1.9g,%1.9g) dist=%1.9g\n", __FUNCTION__, t, onQuad.fX, onQuad.fY,
-                onQuad.distance(q2[index]));
-        double left[3];
-        left[0] = ((const SkDLine&) q1[0]).isLeft(q2[index]);
-        left[1] = ((const SkDLine&) q1[1]).isLeft(q2[index]);
-        SkDLine diag = {{q1[0], q1[2]}};
-        left[2] = diag.isLeft(q2[index]);
-        SkDebugf("%s left=(%d, %d, %d) inHull=%s\n", __FUNCTION__, floatSign(left[0]),
-                floatSign(left[1]), floatSign(left[2]),
-                q1.pointInHull(q2[index]) ? "true" : "false");
-    }
-    SkDebugf("\n");
-}
-
-static void hullIntersect(const SkDQuad& q1, const SkDQuad& q2) {
-    SkDebugf("%s", __FUNCTION__);
-    SkIntersections ts;
-    for (int i1 = 0; i1 < 3; ++i1) {
-        SkDLine l1 = {{q1[i1], q1[(i1 + 1) % 3]}};
-        for (int i2 = 0; i2 < 3; ++i2) {
-            SkDLine l2 = {{q2[i2], q2[(i2 + 1) % 3]}};
-            if (ts.intersect(l1, l2)) {
-                SkDebugf(" [%d,%d]", i1, i2);
-            }
-        }
-    }
-    SkDebugf("\n");
-}
-
-static void QuadraticIntersection_PointFinder() {
-    pointFinder(pointFinderTestSet[0], pointFinderTestSet[4]);
-    pointFinder(pointFinderTestSet[4], pointFinderTestSet[0]);
-    pointFinder(pointFinderTestSet[0], pointFinderTestSet[6]);
-    pointFinder(pointFinderTestSet[6], pointFinderTestSet[0]);
-    hullIntersect(pointFinderTestSet[0], pointFinderTestSet[4]);
-    hullIntersect(pointFinderTestSet[0], pointFinderTestSet[6]);
-}
-
 static void intersectionFinder(int test1, int test2) {
-    const SkDQuad& quad1 = testSet[test1];
-    const SkDQuad& quad2 = testSet[test2];
-
+    const QuadPts& q1 = testSet[test1];
+    const QuadPts& q2 = testSet[test2];
+    SkDQuad quad1, quad2;
+    quad1.debugSet(q1.fPts);
+    quad2.debugSet(q2.fPts);
     double t1Seed = 0.5;
     double t2Seed = 0.8;
     double t1Step = 0.1;
@@ -505,10 +512,51 @@
     intersectionFinder(0, 1);
 }
 
+DEF_TEST(PathOpsQuadIntersectionOneOff, reporter) {
+    oneOffTest1(reporter, 0, 1);
+}
+
+DEF_TEST(PathOpsQuadIntersectionCoincidenceOneOff, reporter) {
+    coincidentTestOne(reporter, 0, 1);
+}
+
 DEF_TEST(PathOpsQuadIntersection, reporter) {
     oneOffTests(reporter);
     coincidentTest(reporter);
     standardTestCases(reporter);
     if (false) QuadraticIntersection_IntersectionFinder();
-    if (false) QuadraticIntersection_PointFinder();
+}
+
+DEF_TEST(PathOpsQuadBinaryProfile, reporter) {
+    if (!SkPathOpsDebug::gVeryVerbose) {
+            return;
+    }
+    SkIntersections intersections;
+    for (int x = 0; x < 100; ++x) {
+        int outer = 0;
+        int inner = outer + 1;
+        do {
+            const QuadPts& q1 = testSet[outer];
+            SkDQuad quad1;
+            quad1.debugSet(q1.fPts);
+            const QuadPts& q2 = testSet[inner];
+            SkDQuad quad2;
+            quad2.debugSet(q2.fPts);
+            (void) intersections.intersect(quad1, quad2);
+            REPORTER_ASSERT(reporter, intersections.used() >= 0);  // make sure code isn't tossed
+            inner += 2;
+            outer += 2;
+        } while (outer < (int) testSetCount);
+    }
+    for (int x = 0; x < 100; ++x) {
+        for (size_t test = 0; test < quadraticTests_count; ++test) {
+            const QuadPts& q1 = quadraticTests[test][0];
+            const QuadPts& q2 = quadraticTests[test][1];
+            SkDQuad quad1, quad2;
+            quad1.debugSet(q1.fPts);
+            quad2.debugSet(q2.fPts);
+            (void) intersections.intersect(quad1, quad2);
+            REPORTER_ASSERT(reporter, intersections.used() >= 0);  // make sure code isn't tossed
+        }
+    }
 }
diff --git a/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.cpp b/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.cpp
index 0706efc..537b509 100644
--- a/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.cpp
+++ b/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.cpp
@@ -7,7 +7,7 @@
 
 #include "PathOpsQuadIntersectionTestData.h"
 
-const SkDQuad quadraticPoints[] = {
+const QuadPts quadraticPoints[] = {
     {{{0, 0}, {1, 0}, {0, 0}}},
     {{{0, 0}, {0, 1}, {0, 0}}},
     {{{0, 0}, {1, 1}, {0, 0}}},
@@ -16,7 +16,7 @@
 
 const size_t quadraticPoints_count = SK_ARRAY_COUNT(quadraticPoints);
 
-const SkDQuad quadraticLines[] = {
+const QuadPts quadraticLines[] = {
     {{{0, 0}, {0, 0}, {1, 0}}},
     {{{1, 0}, {0, 0}, {0, 0}}},
     {{{1, 0}, {2, 0}, {3, 0}}},
@@ -44,12 +44,12 @@
 
 const size_t quadraticLines_count = SK_ARRAY_COUNT(quadraticLines);
 
-static const double F = FLT_EPSILON * 3;
-static const double H = FLT_EPSILON * 4;
-static const double J = FLT_EPSILON * 5;
-static const double K = FLT_EPSILON * 8;  // INVESTIGATE: why are larger multiples necessary?
+static const double F = FLT_EPSILON * 32;
+static const double H = FLT_EPSILON * 32;
+static const double J = FLT_EPSILON * 32;
+static const double K = FLT_EPSILON * 32;  // INVESTIGATE: why are larger multiples necessary?
 
-const SkDQuad quadraticModEpsilonLines[] = {
+const QuadPts quadraticModEpsilonLines[] = {
     {{{0, F}, {0, 0}, {1, 0}}},
     {{{0, 0}, {1, 0}, {0, F}}},
     {{{1, 0}, {0, F}, {0, 0}}},
@@ -64,7 +64,7 @@
     {{{1, 1+J}, {2, 2}, {3, 3}}},
     {{{1, 1}, {3, 3}, {3+F, 3}}},
     {{{1, 1}, {1+F, 1}, {2, 2}}},
-    {{{1, 1}, {2, 2}, {1, 1+F}}},
+    {{{1, 1}, {2, 2}, {1, 1+K}}},
     {{{1, 1}, {1, 1+F}, {3, 3}}},
     {{{1+H, 1}, {2, 2}, {4, 4}}},  // no coincident
     {{{1, 1+K}, {3, 3}, {4, 4}}},
@@ -82,7 +82,7 @@
 const size_t quadraticModEpsilonLines_count =
         SK_ARRAY_COUNT(quadraticModEpsilonLines);
 
-const SkDQuad quadraticTests[][2] = {
+const QuadPts quadraticTests[][2] = {
     {  // one intersection
      {{{0, 0},
       {0, 1},
diff --git a/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.h b/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.h
index 3090fc9..be46a33 100644
--- a/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.h
+++ b/src/third_party/skia/tests/PathOpsQuadIntersectionTestData.h
@@ -4,12 +4,12 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#include "SkPathOpsQuad.h"
+#include "PathOpsTestCommon.h"
 
-extern const SkDQuad quadraticLines[];
-extern const SkDQuad quadraticPoints[];
-extern const SkDQuad quadraticModEpsilonLines[];
-extern const SkDQuad quadraticTests[][2];
+extern const QuadPts quadraticLines[];
+extern const QuadPts quadraticPoints[];
+extern const QuadPts quadraticModEpsilonLines[];
+extern const QuadPts quadraticTests[][2];
 
 extern const size_t quadraticLines_count;
 extern const size_t quadraticPoints_count;
diff --git a/src/third_party/skia/tests/PathOpsQuadLineIntersectionTest.cpp b/src/third_party/skia/tests/PathOpsQuadLineIntersectionTest.cpp
index 6a9e497..f24b2e4 100644
--- a/src/third_party/skia/tests/PathOpsQuadLineIntersectionTest.cpp
+++ b/src/third_party/skia/tests/PathOpsQuadLineIntersectionTest.cpp
@@ -13,7 +13,7 @@
 #include "Test.h"
 
 static struct lineQuad {
-    SkDQuad quad;
+    QuadPts quad;
     SkDLine line;
     int result;
     SkDPoint expected[2];
@@ -56,7 +56,7 @@
 }
 
 static struct oneLineQuad {
-    SkDQuad quad;
+    QuadPts quad;
     SkDLine line;
 } oneOffs[] = {
     {{{{97.9337616,100}, {88,112.94265}, {88,130}}},
@@ -79,7 +79,9 @@
 static void testOneOffs(skiatest::Reporter* reporter) {
     bool flipped = false;
     for (size_t index = 0; index < oneOffs_count; ++index) {
-        const SkDQuad& quad = oneOffs[index].quad;
+        const QuadPts& q = oneOffs[index].quad;
+        SkDQuad quad;
+        quad.debugSet(q.fPts);
         SkASSERT(ValidQuad(quad));
         const SkDLine& line = oneOffs[index].line;
         SkASSERT(ValidLine(line));
@@ -106,7 +108,9 @@
 DEF_TEST(PathOpsQuadLineIntersection, reporter) {
     for (size_t index = 0; index < lineQuadTests_count; ++index) {
         int iIndex = static_cast<int>(index);
-        const SkDQuad& quad = lineQuadTests[index].quad;
+        const QuadPts& q = lineQuadTests[index].quad;
+        SkDQuad quad;
+        quad.debugSet(q.fPts);
         SkASSERT(ValidQuad(quad));
         const SkDLine& line = lineQuadTests[index].line;
         SkASSERT(ValidLine(line));
diff --git a/src/third_party/skia/tests/PathOpsQuadLineIntersectionThreadedTest.cpp b/src/third_party/skia/tests/PathOpsQuadLineIntersectionThreadedTest.cpp
index 7e33b7b..a82ac28 100644
--- a/src/third_party/skia/tests/PathOpsQuadLineIntersectionThreadedTest.cpp
+++ b/src/third_party/skia/tests/PathOpsQuadLineIntersectionThreadedTest.cpp
@@ -5,11 +5,13 @@
  * found in the LICENSE file.
  */
 #include "PathOpsExtendedTest.h"
+#include "PathOpsTestCommon.h"
 #include "PathOpsThreadedCommon.h"
 #include "SkIntersections.h"
 #include "SkPathOpsLine.h"
 #include "SkPathOpsQuad.h"
 #include "SkReduceOrder.h"
+#include "SkString.h"
 
 static int doIntersect(SkIntersections& intersections, const SkDQuad& quad, const SkDLine& line,
                        bool& flipped) {
@@ -40,14 +42,12 @@
 
 static void testLineIntersect(skiatest::Reporter* reporter, const SkDQuad& quad,
                               const SkDLine& line, const double x, const double y) {
-    char pathStr[1024];
-    sk_bzero(pathStr, sizeof(pathStr));
-    char* str = pathStr;
-    str += sprintf(str, "    path.moveTo(%1.9g, %1.9g);\n", quad[0].fX, quad[0].fY);
-    str += sprintf(str, "    path.quadTo(%1.9g, %1.9g, %1.9g, %1.9g);\n", quad[1].fX,
+    SkString pathStr;
+    pathStr.appendf("    path.moveTo(%1.9g, %1.9g);\n", quad[0].fX, quad[0].fY);
+    pathStr.appendf("    path.quadTo(%1.9g, %1.9g, %1.9g, %1.9g);\n", quad[1].fX,
             quad[1].fY, quad[2].fX, quad[2].fY);
-    str += sprintf(str, "    path.moveTo(%1.9g, %1.9g);\n", line[0].fX, line[0].fY);
-    str += sprintf(str, "    path.lineTo(%1.9g, %1.9g);\n", line[1].fX, line[1].fY);
+    pathStr.appendf("    path.moveTo(%1.9g, %1.9g);\n", line[0].fX, line[0].fY);
+    pathStr.appendf("    path.lineTo(%1.9g, %1.9g);\n", line[1].fX, line[1].fY);
 
     SkIntersections intersections;
     bool flipped = false;
@@ -80,8 +80,10 @@
     int by = state.fB >> 2;
     int cx = state.fC & 0x03;
     int cy = state.fC >> 2;
-    SkDQuad quad = {{{(double) ax, (double) ay}, {(double) bx, (double) by},
+    QuadPts q = {{{(double) ax, (double) ay}, {(double) bx, (double) by},
             {(double) cx, (double) cy}}};
+    SkDQuad quad;
+    quad.debugSet(q.fPts);
     SkReduceOrder reducer;
     int order = reducer.reduce(quad);
     if (order < 3) {
@@ -91,7 +93,7 @@
         SkDPoint xy = quad.ptAtT(tIndex / 4.0);
         for (int h = -2; h <= 2; ++h) {
             for (int v = -2; v <= 2; ++v) {
-                if (h == v && abs(h) != 1) {
+                if (h == v && SkTAbs(h) != 1) {
                     continue;
                 }
                 double x = xy.fX;
@@ -116,8 +118,8 @@
     for (int a = 0; a < 16; ++a) {
         for (int b = 0 ; b < 16; ++b) {
             for (int c = 0 ; c < 16; ++c) {
-                    *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
-                            (&testQuadLineIntersectMain, a, b, c, 0, &testRunner));
+                *testRunner.fRunnables.append() = new PathOpsThreadedRunnable(
+                        &testQuadLineIntersectMain, a, b, c, 0, &testRunner);
             }
             if (!reporter->allowExtendedTest()) goto finish;
         }
diff --git a/src/third_party/skia/tests/PathOpsQuadParameterizationTest.cpp b/src/third_party/skia/tests/PathOpsQuadParameterizationTest.cpp
deleted file mode 100644
index c7a2e87..0000000
--- a/src/third_party/skia/tests/PathOpsQuadParameterizationTest.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SkDQuadImplicit.h"
-#include "SkPathOpsQuad.h"
-#include "Test.h"
-
-static bool point_on_parameterized_curve(const SkDQuad& quad, const SkDPoint& point) {
-    SkDQuadImplicit q(quad);
-    double  xx = q.x2() * point.fX * point.fX;
-    double  xy = q.xy() * point.fX * point.fY;
-    double  yy = q.y2() * point.fY * point.fY;
-    double   x = q.x() * point.fX;
-    double   y = q.y() * point.fY;
-    double   c = q.c();
-    double sum = xx + xy + yy + x + y + c;
-    return approximately_zero(sum);
-}
-
-static const SkDQuad quadratics[] = {
-    {{{0, 0}, {1, 0}, {1, 1}}},
-};
-
-static const int quadratics_count = (int) SK_ARRAY_COUNT(quadratics);
-
-DEF_TEST(PathOpsQuadImplicit, reporter) {
-    // split large quadratic
-    // compare original, parts, to see if the are coincident
-    for (int index = 0; index < quadratics_count; ++index) {
-        const SkDQuad& test = quadratics[index];
-        SkDQuadPair split = test.chopAt(0.5);
-        SkDQuad midThird = test.subDivide(1.0/3, 2.0/3);
-        const SkDQuad* quads[] = {
-            &test, &midThird, &split.first(), &split.second()
-        };
-        int quadsCount = (int) SK_ARRAY_COUNT(quads);
-        for (int one = 0; one < quadsCount; ++one) {
-            for (int two = 0; two < quadsCount; ++two) {
-                for (int inner = 0; inner < 3; inner += 2) {
-                     REPORTER_ASSERT(reporter, point_on_parameterized_curve(*quads[one],
-                            (*quads[two])[inner]));
-                }
-                REPORTER_ASSERT(reporter, SkDQuadImplicit::Match(*quads[one], *quads[two]));
-            }
-        }
-    }
-}
diff --git a/src/third_party/skia/tests/PathOpsQuadReduceOrderTest.cpp b/src/third_party/skia/tests/PathOpsQuadReduceOrderTest.cpp
index 9a0bdcf..4f62026 100644
--- a/src/third_party/skia/tests/PathOpsQuadReduceOrderTest.cpp
+++ b/src/third_party/skia/tests/PathOpsQuadReduceOrderTest.cpp
@@ -10,7 +10,7 @@
 #include "SkReduceOrder.h"
 #include "Test.h"
 
-static const SkDQuad testSet[] = {
+static const QuadPts testSet[] = {
     {{{1, 1}, {2, 2}, {1, 1.000003}}},
     {{{1, 0}, {2, 6}, {3, 0}}}
 };
@@ -19,7 +19,9 @@
 
 static void oneOffTest(skiatest::Reporter* reporter) {
     for (size_t index = 0; index < testSetCount; ++index) {
-        const SkDQuad& quad = testSet[index];
+        const QuadPts& q = testSet[index];
+        SkDQuad quad;
+        quad.debugSet(q.fPts);
         SkReduceOrder reducer;
         SkDEBUGCODE(int result = ) reducer.reduce(quad);
         SkASSERT(result == 3);
@@ -47,16 +49,20 @@
             : SK_MaxS32;
 
     for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) {
-        const SkDQuad& quad = quadraticLines[index];
+        const QuadPts& q = quadraticLines[index];
+        SkDQuad quad;
+        quad.debugSet(q.fPts);
         order = reducer.reduce(quad);
         if (order != 2) {
             SkDebugf("[%d] line quad order=%d\n", (int) index, order);
         }
     }
     for (index = firstQuadraticModLineTest; index < quadraticModEpsilonLines_count; ++index) {
-        const SkDQuad& quad = quadraticModEpsilonLines[index];
+        const QuadPts& q = quadraticModEpsilonLines[index];
+        SkDQuad quad;
+        quad.debugSet(q.fPts);
         order = reducer.reduce(quad);
-        if (order != 3) {
+        if (order != 2 && order != 3) {  // FIXME: data probably is not good
             SkDebugf("[%d] line mod quad order=%d\n", (int) index, order);
         }
     }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyDegenerateThreadedTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyDegenerateThreadedTest.cpp
old mode 100755
new mode 100644
index 8e8c58b..3f49718
--- a/src/third_party/skia/tests/PathOpsSimplifyDegenerateThreadedTest.cpp
+++ b/src/third_party/skia/tests/PathOpsSimplifyDegenerateThreadedTest.cpp
@@ -6,15 +6,11 @@
  */
 #include "PathOpsExtendedTest.h"
 #include "PathOpsThreadedCommon.h"
+#include "SkString.h"
 
 static void testSimplifyDegeneratesMain(PathOpsThreadState* data) {
     SkASSERT(data);
     PathOpsThreadState& state = *data;
-    char pathStr[1024];
-    bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter?
-    if (progress) {
-        sk_bzero(pathStr, sizeof(pathStr));
-    }
     int ax = state.fA & 0x03;
     int ay = state.fA >> 2;
     int bx = state.fB & 0x03;
@@ -34,6 +30,7 @@
                         != (ey - dy) * (fx - dx)) {
                     continue;
                 }
+                SkString pathStr;
                 SkPath path, out;
                 path.setFillType(SkPath::kWinding_FillType);
                 path.moveTo(SkIntToScalar(ax), SkIntToScalar(ay));
@@ -44,24 +41,23 @@
                 path.lineTo(SkIntToScalar(ex), SkIntToScalar(ey));
                 path.lineTo(SkIntToScalar(fx), SkIntToScalar(fy));
                 path.close();
-                if (progress) {
-                    char* str = pathStr;
-                    str += sprintf(str, "    path.moveTo(%d, %d);\n", ax, ay);
-                    str += sprintf(str, "    path.lineTo(%d, %d);\n", bx, by);
-                    str += sprintf(str, "    path.lineTo(%d, %d);\n", cx, cy);
-                    str += sprintf(str, "    path.close();\n");
-                    str += sprintf(str, "    path.moveTo(%d, %d);\n", dx, dy);
-                    str += sprintf(str, "    path.lineTo(%d, %d);\n", ex, ey);
-                    str += sprintf(str, "    path.lineTo(%d, %d);\n", fx, fy);
-                    str += sprintf(str, "    path.close();\n");
-                    outputProgress(state.fPathStr, pathStr, SkPath::kWinding_FillType);
+                if (state.fReporter->verbose()) {
+                    pathStr.appendf("    path.moveTo(%d, %d);\n", ax, ay);
+                    pathStr.appendf("    path.lineTo(%d, %d);\n", bx, by);
+                    pathStr.appendf("    path.lineTo(%d, %d);\n", cx, cy);
+                    pathStr.appendf("    path.close();\n");
+                    pathStr.appendf("    path.moveTo(%d, %d);\n", dx, dy);
+                    pathStr.appendf("    path.lineTo(%d, %d);\n", ex, ey);
+                    pathStr.appendf("    path.lineTo(%d, %d);\n", fx, fy);
+                    pathStr.appendf("    path.close();\n");
+                    state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType);
                 }
-                testSimplify(path, false, out, state, pathStr);
+                testSimplify(path, false, out, state, pathStr.c_str());
                 path.setFillType(SkPath::kEvenOdd_FillType);
-                if (progress) {
-                    outputProgress(state.fPathStr, pathStr, SkPath::kEvenOdd_FillType);
+                if (state.fReporter->verbose()) {
+                    state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType);
                 }
-                testSimplify(path, true, out, state, pathStr);
+                testSimplify(path, true, out, state, pathStr.c_str());
             }
         }
     }
@@ -80,9 +76,8 @@
                 int cx = c & 0x03;
                 int cy = c >> 2;
                 bool abcIsATriangle = (bx - ax) * (cy - ay) != (by - ay) * (cx - ax);
-                *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
-                        (&testSimplifyDegeneratesMain, a, b, c, abcIsATriangle,
-                        &testRunner));
+                *testRunner.fRunnables.append() = new PathOpsThreadedRunnable(
+                        &testSimplifyDegeneratesMain, a, b, c, abcIsATriangle, &testRunner);
             }
             if (!reporter->allowExtendedTest()) goto finish;
         }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyFailTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyFailTest.cpp
index 2a4b0a0..b6f210e 100644
--- a/src/third_party/skia/tests/PathOpsSimplifyFailTest.cpp
+++ b/src/third_party/skia/tests/PathOpsSimplifyFailTest.cpp
@@ -4,6 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+#include "PathOpsExtendedTest.h"
 #include "SkPath.h"
 #include "SkPathOps.h"
 #include "SkPoint.h"
@@ -86,15 +87,142 @@
     SkPath result;
     result.setFillType(SkPath::kWinding_FillType);
     bool success = Simplify(path, &result);
-    // linux 32 debug fails test 13 because the quad is not treated as linear
-    // there's no error in the math that I can find -- it looks like a processor
-    // or compiler bug -- so for now, allow either to work
-    REPORTER_ASSERT(reporter, success || index == 13);
+    REPORTER_ASSERT(reporter, success);
     REPORTER_ASSERT(reporter, result.getFillType() != SkPath::kWinding_FillType);
     reporter->bumpTestCount();
 }
 
+static void fuzz_59(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.moveTo(SkBits2Float(0x430c0000), SkBits2Float(0xce58f41c));  // 140, -9.09969e+08f
+    path.lineTo(SkBits2Float(0x43480000), SkBits2Float(0xce58f419));  // 200, -9.09969e+08f
+    path.lineTo(SkBits2Float(0x42200000), SkBits2Float(0xce58f41b));  // 40, -9.09969e+08f
+    path.lineTo(SkBits2Float(0x43700000), SkBits2Float(0xce58f41b));  // 240, -9.09969e+08f
+    path.lineTo(SkBits2Float(0x428c0000), SkBits2Float(0xce58f419));  // 70, -9.09969e+08f
+    path.lineTo(SkBits2Float(0x430c0000), SkBits2Float(0xce58f41c));  // 140, -9.09969e+08f
+    path.close();
+    testSimplifyFuzz(reporter, path, filename);
+}
+
+static void fuzz_x1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a));  // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
+path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000));  // 5.96533e-42f, 9.15715e-24f
+path.cubicTo(SkBits2Float(0x4a6a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a));  // 3.83861e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f
+path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff));  // 3.31432e+06f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f
+    testSimplify(reporter, path, filename);
+}
+
+static void fuzz_x2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a));  // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
+path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000));  // 5.96533e-42f, 9.15715e-24f
+path.cubicTo(SkBits2Float(0x4a6a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa14a4a4a), SkBits2Float(0x08ff2ba1), SkBits2Float(0x08ff4a4a), SkBits2Float(0x4a344a4a));  // 3.83861e+06f, 3.31432e+06f, -6.85386e-19f, 1.53575e-33f, 1.53647e-33f, 2.95387e+06f
+path.cubicTo(SkBits2Float(0x4a4a4a4a), SkBits2Float(0x4a4a4a4a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4e4a08ff), SkBits2Float(0x4a4a4a4a), SkBits2Float(0xa1a181ff));  // 3.31432e+06f, 3.31432e+06f, 1.14845e-12f, 8.47397e+08f, 3.31432e+06f, -1.09442e-18f
+    testSimplify(reporter, path, filename);
+}
+
+static void fuzz763_1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.cubicTo(SkBits2Float(0xbcb63000), SkBits2Float(0xb6b6b6b7), SkBits2Float(0x38b6b6b6), SkBits2Float(0xafb63a5a), SkBits2Float(0xca000087), SkBits2Float(0xe93ae9e9));  // -0.0222397f, -5.44529e-06f, 8.71247e-05f, -3.31471e-10f, -2.09719e+06f, -1.41228e+25f
+path.quadTo(SkBits2Float(0xb6007fb6), SkBits2Float(0xb69fb6b6), SkBits2Float(0xe9e964b6), SkBits2Float(0xe9e9e9e9));  // -1.91478e-06f, -4.75984e-06f, -3.52694e+25f, -3.5348e+25f
+path.quadTo(SkBits2Float(0xb6b6b8b7), SkBits2Float(0xb60000b6), SkBits2Float(0xb6b6b6b6), SkBits2Float(0xe9e92064));  // -5.44553e-06f, -1.90739e-06f, -5.44529e-06f, -3.52291e+25f
+path.quadTo(SkBits2Float(0x000200e9), SkBits2Float(0xe9e9d100), SkBits2Float(0xe93ae9e9), SkBits2Float(0xe964b6e9));  // 1.83997e-40f, -3.53333e+25f, -1.41228e+25f, -1.72812e+25f
+path.quadTo(SkBits2Float(0x40b6e9e9), SkBits2Float(0xe9b60000), SkBits2Float(0x00b6b8e9), SkBits2Float(0xe9000001));  // 5.71605f, -2.75031e+25f, 1.67804e-38f, -9.67141e+24f
+path.quadTo(SkBits2Float(0xe9d3b6b2), SkBits2Float(0x40404540), SkBits2Float(0x803d4043), SkBits2Float(0xe9e9e9ff));  // -3.19933e+25f, 3.00423f, -5.62502e-39f, -3.53481e+25f
+path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0xe8b3b6b6), SkBits2Float(0xe90a0003), SkBits2Float(0x4040403c), SkBits2Float(0x803d4040), SkBits2Float(0xe9e80900));  // 0, -6.78939e+24f, -1.0427e+25f, 3.00392f, -5.62501e-39f, -3.50642e+25f
+path.quadTo(SkBits2Float(0xe9e910e9), SkBits2Float(0xe9e93ae9), SkBits2Float(0x0000b6b6), SkBits2Float(0xb6b6aab6));  // -3.52199e+25f, -3.52447e+25f, 6.55443e-41f, -5.4439e-06f
+path.moveTo(SkBits2Float(0xe9e92064), SkBits2Float(0xe9e9d106));  // -3.52291e+25f, -3.53334e+25f
+path.quadTo(SkBits2Float(0xe9e93ae9), SkBits2Float(0x0000abb6), SkBits2Float(0xb6b6bdb6), SkBits2Float(0xe92064b6));  // -3.52447e+25f, 6.15983e-41f, -5.44611e-06f, -1.2119e+25f
+path.quadTo(SkBits2Float(0x0000e9e9), SkBits2Float(0xb6b6b6e9), SkBits2Float(0x05ffff05), SkBits2Float(0xe9ea06e9));  // 8.39112e-41f, -5.44532e-06f, 2.40738e-35f, -3.53652e+25f
+path.quadTo(SkBits2Float(0xe93ae9e9), SkBits2Float(0x02007fe9), SkBits2Float(0xb8b7b600), SkBits2Float(0xe9e9b6b6));  // -1.41228e+25f, 9.44066e-38f, -8.76002e-05f, -3.53178e+25f
+path.quadTo(SkBits2Float(0xe9e9e9b6), SkBits2Float(0xedb6b6b6), SkBits2Float(0x5a38a1b6), SkBits2Float(0xe93ae9e9));  // -3.53479e+25f, -7.06839e+27f, 1.29923e+16f, -1.41228e+25f
+path.quadTo(SkBits2Float(0x0000b6b6), SkBits2Float(0xb6b6b6b6), SkBits2Float(0xe9e9e9b6), SkBits2Float(0xe9e9e954));  // 6.55443e-41f, -5.44529e-06f, -3.53479e+25f, -3.53477e+25f
+path.quadTo(SkBits2Float(0xb6e9e93a), SkBits2Float(0x375837ff), SkBits2Float(0xceb6b6b6), SkBits2Float(0x0039e94f));  // -6.97109e-06f, 1.28876e-05f, -1.53271e+09f, 5.31832e-39f
+path.quadTo(SkBits2Float(0xe9e9e9e9), SkBits2Float(0xe9e6e9e9), SkBits2Float(0xb6b641b6), SkBits2Float(0xede9e9e9));  // -3.5348e+25f, -3.48947e+25f, -5.43167e-06f, -9.0491e+27f
+path.moveTo(SkBits2Float(0xb6b6e9e9), SkBits2Float(0xb6b60000));  // -5.45125e-06f, -5.42402e-06f
+path.moveTo(SkBits2Float(0xe9b6b6b6), SkBits2Float(0xe9b6b8e9));  // -2.76109e+25f, -2.76122e+25f
+path.close();
+path.moveTo(SkBits2Float(0xe9b6b6b6), SkBits2Float(0xe9b6b8e9));  // -2.76109e+25f, -2.76122e+25f
+path.quadTo(SkBits2Float(0xe93ae9e9), SkBits2Float(0xe964b6e9), SkBits2Float(0x0000203a), SkBits2Float(0xb6000000));  // -1.41228e+25f, -1.72812e+25f, 1.15607e-41f, -1.90735e-06f
+path.moveTo(SkBits2Float(0x64b6b6b6), SkBits2Float(0xe9e9e900));  // 2.69638e+22f, -3.53475e+25f
+path.quadTo(SkBits2Float(0xb6b6b6e9), SkBits2Float(0xb6b6b6b6), SkBits2Float(0xe9e9b6ce), SkBits2Float(0xe9e93ae9));  // -5.44532e-06f, -5.44529e-06f, -3.53179e+25f, -3.52447e+25f
+
+    testSimplifyFuzz(reporter, path, filename);
+}
+
+static void fuzz763_2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.cubicTo(SkBits2Float(0x76773011), SkBits2Float(0x5d66fe78), SkBits2Float(0xbbeeff66), SkBits2Float(0x637677a2), SkBits2Float(0x205266fe), SkBits2Float(0xec296fdf));  // 1.25339e+33f, 1.0403e+18f, -0.00729363f, 4.54652e+21f, 1.78218e-19f, -8.19347e+26f
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.quadTo(SkBits2Float(0xec4eecec), SkBits2Float(0x6e6f10ec), SkBits2Float(0xb6b6ecf7), SkBits2Float(0xb6b6b6b6));  // -1.00063e+27f, 1.84968e+28f, -5.45161e-06f, -5.44529e-06f
+path.moveTo(SkBits2Float(0x002032b8), SkBits2Float(0xecfeb6b6));  // 2.95693e-39f, -2.46344e+27f
+path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.cubicTo(SkBits2Float(0x1616ece4), SkBits2Float(0xdf020018), SkBits2Float(0x77772965), SkBits2Float(0x1009db73), SkBits2Float(0x80ececec), SkBits2Float(0xf7ffffff));  // 1.21917e-25f, -9.36751e+18f, 5.01303e+33f, 2.71875e-29f, -2.17582e-38f, -1.03846e+34f
+path.lineTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.close();
+path.moveTo(SkBits2Float(0x73737300), SkBits2Float(0x73735273));  // 1.9288e+31f, 1.9278e+31f
+path.conicTo(SkBits2Float(0xec0700ec), SkBits2Float(0xecececec), SkBits2Float(0xececccec), SkBits2Float(0x772965ec), SkBits2Float(0x77777377));  // -6.52837e+26f, -2.2914e+27f, -2.29019e+27f, 3.4358e+33f, 5.0189e+33f
+path.moveTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266));  // -8.60376e+37f, -1.65964e+19f
+path.close();
+path.moveTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266));  // -8.60376e+37f, -1.65964e+19f
+path.quadTo(SkBits2Float(0x29ec02ec), SkBits2Float(0x1009ecec), SkBits2Float(0x80ececec), SkBits2Float(0xf7ffffff));  // 1.0481e-13f, 2.7201e-29f, -2.17582e-38f, -1.03846e+34f
+path.lineTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266));  // -8.60376e+37f, -1.65964e+19f
+path.close();
+path.moveTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266));  // -8.60376e+37f, -1.65964e+19f
+path.conicTo(SkBits2Float(0xff003aff), SkBits2Float(0xdbec2300), SkBits2Float(0xecececec), SkBits2Float(0x6fdf6052), SkBits2Float(0x41ecec29));  // -1.70448e+38f, -1.32933e+17f, -2.2914e+27f, 1.38263e+29f, 29.6153f
+path.lineTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266));  // -8.60376e+37f, -1.65964e+19f
+path.close();
+path.moveTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266));  // -8.60376e+37f, -1.65964e+19f
+path.quadTo(SkBits2Float(0xecf76e6f), SkBits2Float(0xeccfddec), SkBits2Float(0xecececcc), SkBits2Float(0x66000066));  // -2.39301e+27f, -2.01037e+27f, -2.2914e+27f, 1.51118e+23f
+path.lineTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266));  // -8.60376e+37f, -1.65964e+19f
+path.close();
+path.moveTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266));  // -8.60376e+37f, -1.65964e+19f
+path.cubicTo(SkBits2Float(0x772965df), SkBits2Float(0x77777377), SkBits2Float(0x77777876), SkBits2Float(0x665266fe), SkBits2Float(0xecececdf), SkBits2Float(0x0285806e));  // 3.4358e+33f, 5.0189e+33f, 5.0193e+33f, 2.48399e+23f, -2.2914e+27f, 1.96163e-37f
+path.lineTo(SkBits2Float(0xecececeb), SkBits2Float(0xecec0700));  // -2.2914e+27f, -2.28272e+27f
+path.lineTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266));  // -8.60376e+37f, -1.65964e+19f
+path.close();
+path.moveTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266));  // -8.60376e+37f, -1.65964e+19f
+path.lineTo(SkBits2Float(0x65ecfaec), SkBits2Float(0xde777729));  // 1.39888e+23f, -4.45794e+18f
+path.conicTo(SkBits2Float(0x74777777), SkBits2Float(0x66fe7876), SkBits2Float(0xecdf6660), SkBits2Float(0x726eecec), SkBits2Float(0x29d610ec));  // 7.84253e+31f, 6.00852e+23f, -2.16059e+27f, 4.73241e+30f, 9.50644e-14f
+path.lineTo(SkBits2Float(0xfe817477), SkBits2Float(0xdf665266));  // -8.60376e+37f, -1.65964e+19f
+path.close();
+path.moveTo(SkBits2Float(0xd0ecec10), SkBits2Float(0x6e6eecdb));  // -3.17991e+10f, 1.84859e+28f
+path.quadTo(SkBits2Float(0x003affec), SkBits2Float(0xec2300ef), SkBits2Float(0xecececdb), SkBits2Float(0xcfececec));  // 5.41827e-39f, -7.88237e+26f, -2.2914e+27f, -7.9499e+09f
+path.lineTo(SkBits2Float(0xd0ecec10), SkBits2Float(0x6e6eecdb));  // -3.17991e+10f, 1.84859e+28f
+path.close();
+path.moveTo(SkBits2Float(0xd0ecec10), SkBits2Float(0x6e6eecdb));  // -3.17991e+10f, 1.84859e+28f
+path.quadTo(SkBits2Float(0xecccec80), SkBits2Float(0xfa66ecec), SkBits2Float(0x66fa0000), SkBits2Float(0x772965df));  // -1.9819e+27f, -2.99758e+35f, 5.90296e+23f, 3.4358e+33f
+path.moveTo(SkBits2Float(0x77777790), SkBits2Float(0x00807677));  // 5.01923e+33f, 1.17974e-38f
+path.close();
+path.moveTo(SkBits2Float(0x77777790), SkBits2Float(0x00807677));  // 5.01923e+33f, 1.17974e-38f
+path.cubicTo(SkBits2Float(0xecececec), SkBits2Float(0xfe66eaec), SkBits2Float(0xecdf1452), SkBits2Float(0x806eecec), SkBits2Float(0x10ececec), SkBits2Float(0xec000000));  // -2.2914e+27f, -7.67356e+37f, -2.15749e+27f, -1.01869e-38f, 9.34506e-29f, -6.1897e+26f
+path.lineTo(SkBits2Float(0x77777790), SkBits2Float(0x00807677));  // 5.01923e+33f, 1.17974e-38f
+path.close();
+path.moveTo(SkBits2Float(0x77777790), SkBits2Float(0x00807677));  // 5.01923e+33f, 1.17974e-38f
+path.cubicTo(SkBits2Float(0x52668062), SkBits2Float(0x2965df66), SkBits2Float(0x77777377), SkBits2Float(0x76777773), SkBits2Float(0x1697fe78), SkBits2Float(0xeebfff00));  // 2.47499e+11f, 5.1042e-14f, 5.0189e+33f, 1.2548e+33f, 2.4556e-25f, -2.971e+28f
+path.lineTo(SkBits2Float(0x77777790), SkBits2Float(0x00807677));  // 5.01923e+33f, 1.17974e-38f
+path.close();
+
+    testSimplifyFuzz(reporter, path, filename);
+}
+
+#define TEST(test) test(reporter, #test)
+
 DEF_TEST(PathOpsSimplifyFail, reporter) {
+    TEST(fuzz763_2);
+    TEST(fuzz763_1);
+    TEST(fuzz_x2);
+    TEST(fuzz_x1);
+    TEST(fuzz_59);
     for (int index = 0; index < (int) (13 * nonFinitePtsCount * finitePtsCount); ++index) {
         failOne(reporter, index);
     }
@@ -103,12 +231,14 @@
     }
 }
 
+#undef TEST
+
 DEF_TEST(PathOpsSimplifyFailOne, reporter) {
     int index = 0;
     failOne(reporter, index);
 }
 
 DEF_TEST(PathOpsSimplifyDontFailOne, reporter) {
-    int index = 13;
+    int index = 17;
     dontFailOne(reporter, index);
 }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyQuadThreadedTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyQuadThreadedTest.cpp
index 3c92cca..55dc07b 100644
--- a/src/third_party/skia/tests/PathOpsSimplifyQuadThreadedTest.cpp
+++ b/src/third_party/skia/tests/PathOpsSimplifyQuadThreadedTest.cpp
@@ -6,16 +6,15 @@
  */
 #include "PathOpsExtendedTest.h"
 #include "PathOpsThreadedCommon.h"
+#include "SkString.h"
+
+static int quadTest = 66;
 
 static void testSimplifyQuadsMain(PathOpsThreadState* data)
 {
     SkASSERT(data);
     PathOpsThreadState& state = *data;
-    char pathStr[1024];
-    bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter?
-    if (progress) {
-        sk_bzero(pathStr, sizeof(pathStr));
-    }
+    SkString pathStr;
     int ax = state.fA & 0x03;
     int ay = state.fA >> 2;
     int bx = state.fB & 0x03;
@@ -48,25 +47,28 @@
                     path.quadTo(SkIntToScalar(gx), SkIntToScalar(gy),
                             SkIntToScalar(hx), SkIntToScalar(hy));
                     path.close();
-                    if (progress) {
-                        // gdb: set print elements 400
-                        char* str = pathStr;
-                        str += sprintf(str, "    path.moveTo(%d, %d);\n", ax, ay);
-                        str += sprintf(str, "    path.quadTo(%d, %d, %d, %d);\n", bx, by, cx, cy);
-                        str += sprintf(str, "    path.lineTo(%d, %d);\n", dx, dy);
-                        str += sprintf(str, "    path.close();\n");
-                        str += sprintf(str, "    path.moveTo(%d, %d);\n", ex, ey);
-                        str += sprintf(str, "    path.lineTo(%d, %d);\n", fx, fy);
-                        str += sprintf(str, "    path.quadTo(%d, %d, %d, %d);\n", gx, gy, hx, hy);
-                        str += sprintf(str, "    path.close();\n");
-                        outputProgress(state.fPathStr, pathStr, SkPath::kWinding_FillType);
+                    if (state.fReporter->verbose()) {
+                        pathStr.printf("static void testQuads%d(skiatest::Reporter* reporter,"
+                                "const char* filename) {\n", quadTest);
+                        pathStr.appendf("    SkPath path;\n");
+                        pathStr.appendf("    path.moveTo(%d, %d);\n", ax, ay);
+                        pathStr.appendf("    path.quadTo(%d, %d, %d, %d);\n", bx, by, cx, cy);
+                        pathStr.appendf("    path.lineTo(%d, %d);\n", dx, dy);
+                        pathStr.appendf("    path.close();\n");
+                        pathStr.appendf("    path.moveTo(%d, %d);\n", ex, ey);
+                        pathStr.appendf("    path.lineTo(%d, %d);\n", fx, fy);
+                        pathStr.appendf("    path.quadTo(%d, %d, %d, %d);\n", gx, gy, hx, hy);
+                        pathStr.appendf("    path.close();\n");
+                        pathStr.appendf("    testSimplify(reporter, path, filename);\n");
+                        pathStr.appendf("}\n");
+                        state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType);
                     }
-                    testSimplify(path, false, out, state, pathStr);
+                    testSimplify(path, false, out, state, pathStr.c_str());
                     path.setFillType(SkPath::kEvenOdd_FillType);
-                    if (progress) {
-                        outputProgress(state.fPathStr, pathStr, SkPath::kEvenOdd_FillType);
+                    if (state.fReporter->verbose()) {
+                        state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType);
                     }
-                    testSimplify(path, true, out, state, pathStr);
+                    testSimplify(path, true, out, state, pathStr.c_str());
                 }
             }
         }
@@ -81,8 +83,8 @@
         for (int b = a ; b < 16; ++b) {
             for (int c = b ; c < 16; ++c) {
                 for (int d = c; d < 16; ++d) {
-                    *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
-                            (&testSimplifyQuadsMain, a, b, c, d, &testRunner));
+                    *testRunner.fRunnables.append() = new PathOpsThreadedRunnable(
+                            &testSimplifyQuadsMain, a, b, c, d, &testRunner);
                 }
                 if (!reporter->allowExtendedTest()) goto finish;
             }
@@ -90,5 +92,4 @@
     }
 finish:
     testRunner.render();
-    ShowTestArray();
 }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyQuadralateralsThreadedTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyQuadralateralsThreadedTest.cpp
old mode 100755
new mode 100644
index f8e9a6e..6133042
--- a/src/third_party/skia/tests/PathOpsSimplifyQuadralateralsThreadedTest.cpp
+++ b/src/third_party/skia/tests/PathOpsSimplifyQuadralateralsThreadedTest.cpp
@@ -6,16 +6,15 @@
  */
 #include "PathOpsExtendedTest.h"
 #include "PathOpsThreadedCommon.h"
+#include "SkString.h"
+
+static int loopNo = 1;
 
 static void testSimplifyQuadralateralsMain(PathOpsThreadState* data)
 {
     SkASSERT(data);
     PathOpsThreadState& state = *data;
-    char pathStr[1024];
-    bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter?
-    if (progress) {
-        sk_bzero(pathStr, sizeof(pathStr));
-    }
+    SkString pathStr;
     int ax = state.fA & 0x03;
     int ay = state.fA >> 2;
     int bx = state.fB & 0x03;
@@ -48,27 +47,30 @@
                     path.lineTo(SkIntToScalar(gx), SkIntToScalar(gy));
                     path.lineTo(SkIntToScalar(hx), SkIntToScalar(hy));
                     path.close();
-                    if (progress) {
-                       // gdb: set print elements 400
-                        char* str = pathStr;
-                        str += sprintf(str, "    path.moveTo(%d, %d);\n", ax, ay);
-                        str += sprintf(str, "    path.lineTo(%d, %d);\n", bx, by);
-                        str += sprintf(str, "    path.lineTo(%d, %d);\n", cx, cy);
-                        str += sprintf(str, "    path.lineTo(%d, %d);\n", dx, dy);
-                        str += sprintf(str, "    path.close();\n");
-                        str += sprintf(str, "    path.moveTo(%d, %d);\n", ex, ey);
-                        str += sprintf(str, "    path.lineTo(%d, %d);\n", fx, fy);
-                        str += sprintf(str, "    path.lineTo(%d, %d);\n", gx, gy);
-                        str += sprintf(str, "    path.lineTo(%d, %d);\n", hx, hy);
-                        str += sprintf(str, "    path.close();\n");
-                        outputProgress(state.fPathStr, pathStr, SkPath::kWinding_FillType);
+                    if (state.fReporter->verbose()) {
+                        pathStr.printf("static void quadralateralSimplify%d(skiatest::Reporter*"
+                                "reporter, const char* filename) {\n", loopNo);
+                        pathStr.appendf("    SkPath path;\n");
+                        pathStr.appendf("    path.moveTo(%d, %d);\n", ax, ay);
+                        pathStr.appendf("    path.lineTo(%d, %d);\n", bx, by);
+                        pathStr.appendf("    path.lineTo(%d, %d);\n", cx, cy);
+                        pathStr.appendf("    path.lineTo(%d, %d);\n", dx, dy);
+                        pathStr.appendf("    path.close();\n");
+                        pathStr.appendf("    path.moveTo(%d, %d);\n", ex, ey);
+                        pathStr.appendf("    path.lineTo(%d, %d);\n", fx, fy);
+                        pathStr.appendf("    path.lineTo(%d, %d);\n", gx, gy);
+                        pathStr.appendf("    path.lineTo(%d, %d);\n", hx, hy);
+                        pathStr.appendf("    path.close();\n");
+                        pathStr.appendf("    testPathSimplify(reporter, path, filename);\n");
+                        pathStr.appendf("}\n");
+                        state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType);
                     }
-                    testSimplify(path, false, out, state, pathStr);
+                    testSimplify(path, false, out, state, pathStr.c_str());
                     path.setFillType(SkPath::kEvenOdd_FillType);
-                    if (progress) {
-                        outputProgress(state.fPathStr, pathStr, SkPath::kEvenOdd_FillType);
+                    if (state.fReporter->verbose()) {
+                        state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType);
                     }
-                    testSimplify(path, true, out, state, pathStr);
+                    testSimplify(path, true, out, state, pathStr.c_str());
                 }
             }
         }
@@ -82,8 +84,8 @@
         for (int b = a ; b < 16; ++b) {
             for (int c = b ; c < 16; ++c) {
                 for (int d = c; d < 16; ++d) {
-                    *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
-                            (&testSimplifyQuadralateralsMain, a, b, c, d, &testRunner));
+                    *testRunner.fRunnables.append() = new PathOpsThreadedRunnable(
+                            &testSimplifyQuadralateralsMain, a, b, c, d, &testRunner);
                 }
                 if (!reporter->allowExtendedTest()) goto finish;
             }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyRectThreadedTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyRectThreadedTest.cpp
index 52a78ec..384d1b0 100644
--- a/src/third_party/skia/tests/PathOpsSimplifyRectThreadedTest.cpp
+++ b/src/third_party/skia/tests/PathOpsSimplifyRectThreadedTest.cpp
@@ -6,6 +6,7 @@
  */
 #include "PathOpsExtendedTest.h"
 #include "PathOpsThreadedCommon.h"
+#include "SkString.h"
 
 // four rects, of four sizes
 // for 3 smaller sizes, tall, wide
@@ -18,11 +19,6 @@
 {
     SkASSERT(data);
     PathOpsThreadState& state = *data;
-    char pathStr[1024];  // gdb: set print elements 400
-    bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter?
-    if (progress) {
-        sk_bzero(pathStr, sizeof(pathStr));
-    }
     int aShape = state.fA & 0x03;
     SkPath::Direction aCW = state.fA >> 2 ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
     int bShape = state.fB & 0x03;
@@ -39,10 +35,11 @@
                          for (int cYAlign = 0; cYAlign < 5; ++cYAlign) {
                             for (int dXAlign = 0; dXAlign < 5; ++dXAlign) {
     for (int dYAlign = 0; dYAlign < 5; ++dYAlign) {
+        SkString pathStr;
         SkPath path, out;
-        char* str = pathStr;
         path.setFillType(SkPath::kWinding_FillType);
-        int l, t, r, b;
+        int l SK_INIT_TO_AVOID_WARNING, t SK_INIT_TO_AVOID_WARNING,
+            r SK_INIT_TO_AVOID_WARNING, b SK_INIT_TO_AVOID_WARNING;
         if (aShape) {
             switch (aShape) {
                 case 1:  // square
@@ -66,8 +63,8 @@
             }
             path.addRect(SkIntToScalar(l), SkIntToScalar(t), SkIntToScalar(r), SkIntToScalar(b),
                     aCW);
-            if (progress) {
-                str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
+            if (state.fReporter->verbose()) {
+                pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
                         " SkPath::kC%sW_Direction);\n", l, t, r, b, aCW ? "C" : "");
             }
         } else {
@@ -97,8 +94,8 @@
             }
             path.addRect(SkIntToScalar(l), SkIntToScalar(t), SkIntToScalar(r), SkIntToScalar(b),
                     bCW);
-            if (progress) {
-                str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
+            if (state.fReporter->verbose()) {
+                pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
                         " SkPath::kC%sW_Direction);\n", l, t, r, b, bCW ? "C" : "");
             }
         } else {
@@ -128,8 +125,8 @@
             }
             path.addRect(SkIntToScalar(l), SkIntToScalar(t), SkIntToScalar(r), SkIntToScalar(b),
                     cCW);
-            if (progress) {
-                str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
+            if (state.fReporter->verbose()) {
+                pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
                         " SkPath::kC%sW_Direction);\n", l, t, r, b, cCW ? "C" : "");
             }
         } else {
@@ -159,8 +156,8 @@
             }
             path.addRect(SkIntToScalar(l), SkIntToScalar(t), SkIntToScalar(r), SkIntToScalar(b),
                     dCW);
-            if (progress) {
-                str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
+            if (state.fReporter->verbose()) {
+                pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
                         " SkPath::kC%sW_Direction);\n", l, t, r, b, dCW ? "C" : "");
             }
         } else {
@@ -168,14 +165,14 @@
             dYAlign = 5;
         }
         path.close();
-        if (progress) {
-            outputProgress(state.fPathStr, pathStr, SkPath::kWinding_FillType);
+        if (state.fReporter->verbose()) {
+            state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType);
         }
-        testSimplify(path, false, out, state, pathStr);
-        if (progress) {
-            outputProgress(state.fPathStr, pathStr, SkPath::kEvenOdd_FillType);
+        testSimplify(path, false, out, state, pathStr.c_str());
+        if (state.fReporter->verbose()) {
+            state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType);
         }
-        testSimplify(path, true, out, state, pathStr);
+        testSimplify(path, true, out, state, pathStr.c_str());
     }
                             }
                         }
@@ -193,8 +190,8 @@
         for (int b = a ; b < 8; ++b) {
             for (int c = b ; c < 8; ++c) {
                 for (int d = c; d < 8; ++d) {
-                        *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
-                                (&testSimplify4x4RectsMain, a, b, c, d, &testRunner));
+                    *testRunner.fRunnables.append() = new PathOpsThreadedRunnable(
+                            &testSimplify4x4RectsMain, a, b, c, d, &testRunner);
                 }
                 if (!reporter->allowExtendedTest()) goto finish;
             }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyTest.cpp
index 88547a0..2de67ff 100644
--- a/src/third_party/skia/tests/PathOpsSimplifyTest.cpp
+++ b/src/third_party/skia/tests/PathOpsSimplifyTest.cpp
@@ -3649,7 +3649,7 @@
     testSimplify(reporter, path, filename);
 }
 
-// A test this for this case:
+// A test for this case:
 // contourA has two segments that are coincident
 // contourB has two segments that are coincident in the same place
 // each ends up with +2/0 pairs for winding count
@@ -4506,8 +4506,6 @@
     testSimplify(reporter, path, filename);
 }
 
-// this fails because there is a short unorderable segment and the unordered state isn't handled
-// correctly later on.
 static void testQuads46x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -4679,9 +4677,3064 @@
     testSimplify(reporter, path, filename);
 }
 
+static void testRect4(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 30, 60, SkPath::kCCW_Direction);
+    path.addRect(10, 0, 40, 30, SkPath::kCCW_Direction);
+    path.addRect(20, 0, 30, 40, SkPath::kCCW_Direction);
+    path.addRect(32, 0, 36, 41, SkPath::kCCW_Direction);
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads62(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.moveTo(3, 2);
+    path.quadTo(1, 3, 3, 3);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 0);
+    path.quadTo(1, 3, 3, 3);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads63(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(3, 0);
+    path.quadTo(0, 1, 1, 2);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.quadTo(0, 2, 3, 3);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads64(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(3, 0);
+    path.quadTo(0, 1, 1, 2);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 1);
+    path.quadTo(0, 2, 3, 3);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testTriangle1(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 2);
+    path.lineTo(1, 0);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testTriangle2(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(2, 0);
+    path.lineTo(0, 2);
+    path.lineTo(2, 2);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testArc(skiatest::Reporter* reporter,const char* filename) {
+    SkRect r = SkRect::MakeWH(150, 100);
+    SkPath path;
+    path.arcTo(r, 0, 0.0025f, false);
+    testSimplify(reporter, path, filename);
+}
+
+static void testIssue3838(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(220, 170);
+    path.lineTo(200, 170);
+    path.lineTo(200, 190);
+    path.lineTo(180, 190);
+    path.lineTo(180, 210);
+    path.lineTo(200, 210);
+    path.lineTo(200, 250);
+    path.lineTo(260, 250);
+    path.lineTo(260, 190);
+    path.lineTo(220, 190);
+    path.lineTo(220, 170);
+    path.close();
+    path.moveTo(220, 210);
+    path.lineTo(220, 230);
+    path.lineTo(240, 230);
+    path.lineTo(240, 210);
+    path.lineTo(220, 210);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testIssue3838_3(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(40, 10);
+    path.lineTo(60, 10);
+    path.lineTo(60, 30);
+    path.lineTo(40, 30);
+    path.lineTo(40, 10);
+    path.moveTo(41, 11);
+    path.lineTo(41, 29);
+    path.lineTo(59, 29);
+    path.lineTo(59, 11);
+    path.lineTo(41, 11);
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads65(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(1, 2);
+    path.quadTo(3, 2, 0, 3);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 2);
+    path.quadTo(3, 2, 1, 3);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void fuzz864a(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(10, 90);
+    path.lineTo(10, 90);
+    path.lineTo(10, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 90);
+    path.close();
+    path.moveTo(10, 90);
+    path.lineTo(10, 90);
+    path.lineTo(10, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 90);
+    path.close();
+    path.moveTo(10, 90);
+    path.lineTo(110, 90);
+    path.lineTo(110, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 90);
+    path.close();
+    path.moveTo(10, 30);
+    path.lineTo(32678, 30);
+    path.lineTo(32678, 30);
+    path.lineTo(10, 30);
+    path.close();
+    path.moveTo(10, 3.35545e+07f);
+    path.lineTo(110, 3.35545e+07f);
+    path.lineTo(110, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 3.35545e+07f);
+    path.close();
+    path.moveTo(10, 315);
+    path.lineTo(110, 315);
+    path.lineTo(110, 255);
+    path.lineTo(10, 255);
+    path.lineTo(10, 315);
+    path.close();
+    path.moveTo(0, 60);
+    path.lineTo(100, 60);
+    path.lineTo(100, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 60);
+    path.close();
+    path.moveTo(10, 90);
+    path.lineTo(110, 90);
+    path.lineTo(110, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 90);
+    path.close();
+    path.moveTo(10, 3.35545e+07f);
+    path.lineTo(110, 3.35545e+07f);
+    path.lineTo(110, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 3.35545e+07f);
+    path.close();
+    path.moveTo(10, 90);
+    path.lineTo(110, 90);
+    path.lineTo(110, 30);
+    path.lineTo(10, 30);
+    path.lineTo(10, 90);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void cr514118(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+path.moveTo(SkBits2Float(0x42c80000), SkBits2Float(0x42480000));  // 100, 50
+path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3));  // 100, 0, 50, 0, 0.707107f
+path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 0, 0, 0, 50, 0.707107f
+path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3));  // 0, 100, 50, 100, 0.707107f
+path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 100, 100, 100, 50, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x42c80133), SkBits2Float(0x42480000));  // 100.002f, 50
+path.conicTo(SkBits2Float(0x42c80133), SkBits2Float(0x00000000), SkBits2Float(0x42480267), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3));  // 100.002f, 0, 50.0023f, 0, 0.707107f
+path.conicTo(SkBits2Float(0x3b19b530), SkBits2Float(0x00000000), SkBits2Float(0x3b19b530), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 0.00234539f, 0, 0.00234539f, 50, 0.707107f
+path.conicTo(SkBits2Float(0x3b19b530), SkBits2Float(0x42c80000), SkBits2Float(0x42480267), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3));  // 0.00234539f, 100, 50.0023f, 100, 0.707107f
+path.conicTo(SkBits2Float(0x42c80133), SkBits2Float(0x42c80000), SkBits2Float(0x42c80133), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 100.002f, 100, 100.002f, 50, 0.707107f
+path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void fuzz994s_11(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x42b40000));  // 110, 90
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000));  // 110, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x46ff4c00), SkBits2Float(0x41f00000));  // 32678, 30
+path.lineTo(SkBits2Float(0x46ff4c00), SkBits2Float(0x41f00000));  // 32678, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x4c000006));  // 10, 3.35545e+07f
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x4c000006));  // 110, 3.35545e+07f
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000));  // 110, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x4c000006));  // 10, 3.35545e+07f
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x439d8000));  // 10, 315
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x439d8000));  // 110, 315
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x437f0000));  // 110, 255
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x437f0000));  // 10, 255
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x439d8000));  // 10, 315
+path.close();
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x42700000));  // 0, 60
+path.lineTo(SkBits2Float(0x42c80000), SkBits2Float(0x42700000));  // 100, 60
+path.lineTo(SkBits2Float(0x42c80000), SkBits2Float(0x00000000));  // 100, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x42700000));  // 0, 60
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x42b40000));  // 110, 90
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000));  // 110, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x4c000006));  // 10, 3.35545e+07f
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x4c000006));  // 110, 3.35545e+07f
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000));  // 110, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x4c000006));  // 10, 3.35545e+07f
+path.close();
+path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x42b40000));  // 110, 90
+path.lineTo(SkBits2Float(0x42dc0000), SkBits2Float(0x41f00000));  // 110, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
+path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
+path.close();
+
+    testSimplify(reporter, path, filename);
+}
+
+static void fuzz994s_3414(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42c80000), SkBits2Float(0x42480000));  // 100, 50
+path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3));  // 100, 0, 50, 0, 0.707107f
+path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 0, 0, 0, 50, 0.707107f
+path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3));  // 0, 100, 50, 100, 0.707107f
+path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 100, 100, 100, 50, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x42c84964), SkBits2Float(0x42480000));  // 100.143f, 50
+path.conicTo(SkBits2Float(0x42c84964), SkBits2Float(0x00000000), SkBits2Float(0x424892c8), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3));  // 100.143f, 0, 50.1433f, 0, 0.707107f
+path.conicTo(SkBits2Float(0x3e12c788), SkBits2Float(0x00000000), SkBits2Float(0x3e12c788), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 0.143339f, 0, 0.143339f, 50, 0.707107f
+path.conicTo(SkBits2Float(0x3e12c788), SkBits2Float(0x42c80000), SkBits2Float(0x424892c8), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3));  // 0.143339f, 100, 50.1433f, 100, 0.707107f
+path.conicTo(SkBits2Float(0x42c84964), SkBits2Float(0x42c80000), SkBits2Float(0x42c84964), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 100.143f, 100, 100.143f, 50, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x42c80000), SkBits2Float(0x42480000));  // 100, 50
+path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3));  // 100, 0, 50, 0, 0.707107f
+path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 0, 0, 0, 50, 0.707107f
+path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3));  // 0, 100, 50, 100, 0.707107f
+path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42c80000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 100, 100, 100, 50, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x4c00006b), SkBits2Float(0x424c0000));  // 3.35549e+07f, 51
+path.conicTo(SkBits2Float(0x4c00006b), SkBits2Float(0xcbffffe5), SkBits2Float(0x43d6e720), SkBits2Float(0xcbffffe5), SkBits2Float(0x3f3504f3));  // 3.35549e+07f, -3.35544e+07f, 429.806f, -3.35544e+07f, 0.707107f
+path.conicTo(SkBits2Float(0xcbffff28), SkBits2Float(0xcbffffe5), SkBits2Float(0xcbffff28), SkBits2Float(0x424c0000), SkBits2Float(0x3f3504f3));  // -3.3554e+07f, -3.35544e+07f, -3.3554e+07f, 51, 0.707107f
+path.conicTo(SkBits2Float(0xcbffff28), SkBits2Float(0x4c00000c), SkBits2Float(0x43d6e720), SkBits2Float(0x4c00000c), SkBits2Float(0x3f3504f3));  // -3.3554e+07f, 3.35545e+07f, 429.806f, 3.35545e+07f, 0.707107f
+path.conicTo(SkBits2Float(0x4c00006b), SkBits2Float(0x4c00000c), SkBits2Float(0x4c00006b), SkBits2Float(0x424c0000), SkBits2Float(0x3f3504f3));  // 3.35549e+07f, 3.35545e+07f, 3.35549e+07f, 51, 0.707107f
+path.close();
+path.moveTo(SkBits2Float(0x43ef6720), SkBits2Float(0x42480000));  // 478.806f, 50
+path.conicTo(SkBits2Float(0x43ef6720), SkBits2Float(0x00000000), SkBits2Float(0x43d66720), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3));  // 478.806f, 0, 428.806f, 0, 0.707107f
+path.conicTo(SkBits2Float(0x43bd6720), SkBits2Float(0x00000000), SkBits2Float(0x43bd6720), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 378.806f, 0, 378.806f, 50, 0.707107f
+path.conicTo(SkBits2Float(0x43bd6720), SkBits2Float(0x42c80000), SkBits2Float(0x43d66720), SkBits2Float(0x42c80000), SkBits2Float(0x3f3504f3));  // 378.806f, 100, 428.806f, 100, 0.707107f
+path.conicTo(SkBits2Float(0x43ef6720), SkBits2Float(0x42c80000), SkBits2Float(0x43ef6720), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 478.806f, 100, 478.806f, 50, 0.707107f
+path.close();
+
+    testSimplify(reporter, path, filename);
+}
+
+static void fuzz_twister(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(0, 600);
+path.lineTo(3.35544e+07f, 600);
+path.lineTo(3.35544e+07f, 0);
+path.lineTo(0, 0);
+path.lineTo(0, 600);
+path.close();
+path.moveTo(63, 600);
+path.lineTo(3.35545e+07f, 600);
+path.lineTo(3.35545e+07f, 0);
+path.lineTo(63, 0);
+path.lineTo(63, 600);
+path.close();
+path.moveTo(93, 600);
+path.lineTo(3.35545e+07f, 600);
+path.lineTo(3.35545e+07f, 0);
+path.lineTo(93, 0);
+path.lineTo(93, 600);
+path.close();
+path.moveTo(123, 600);
+path.lineTo(3.35546e+07f, 600);
+path.lineTo(3.35546e+07f, 0);
+path.lineTo(123, 0);
+path.lineTo(123, 600);
+path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void fuzz_twister2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x44160000));  // 0, 600
+path.lineTo(SkBits2Float(0x4bfffffe), SkBits2Float(0x44160000));  // 3.35544e+07f, 600
+path.lineTo(SkBits2Float(0x4bfffffe), SkBits2Float(0x00000000));  // 3.35544e+07f, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x44160000));  // 0, 600
+path.close();
+
+path.moveTo(SkBits2Float(0x427c0000), SkBits2Float(0x00000000));  // 63, 0
+path.lineTo(SkBits2Float(0x4c00000f), SkBits2Float(0x00000000));  // 3.35545e+07f, 0
+path.lineTo(SkBits2Float(0x4c00000f), SkBits2Float(0x00000000));  // 3.35545e+07f, 0
+path.lineTo(SkBits2Float(0x427c0000), SkBits2Float(0x00000000));  // 63, 0
+path.close();
+
+path.moveTo(SkBits2Float(0x42ba0000), SkBits2Float(0x00000000));  // 93, 0
+path.lineTo(SkBits2Float(0x4c000016), SkBits2Float(0x00000000));  // 3.35545e+07f, 0
+path.lineTo(SkBits2Float(0x4c000016), SkBits2Float(0x00000000));  // 3.35545e+07f, 0
+path.lineTo(SkBits2Float(0x42ba0000), SkBits2Float(0x00000000));  // 93, 0
+path.close();
+
+path.moveTo(SkBits2Float(0x42f60000), SkBits2Float(0x00000000));  // 123, 0
+path.lineTo(SkBits2Float(0x4c00001e), SkBits2Float(0x00000000));  // 3.35546e+07f, 0
+path.lineTo(SkBits2Float(0x4c00001e), SkBits2Float(0x00000000));  // 3.35546e+07f, 0
+path.lineTo(SkBits2Float(0x42f60000), SkBits2Float(0x00000000));  // 123, 0
+path.close();
+
+    testSimplify(reporter, path, filename);
+}
+
+static void fuzz763_4713_b(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x42040000));
+path.close();
+
+path.moveTo(SkBits2Float(0x4204f72e), SkBits2Float(0x41c56cd2));
+path.quadTo(SkBits2Float(0x42123842), SkBits2Float(0x41c52adf), SkBits2Float(0x421baed7), SkBits2Float(0x41d7bac6));
+path.quadTo(SkBits2Float(0x4225256d), SkBits2Float(0x41ea4aad), SkBits2Float(0x42254667), SkBits2Float(0x4202666b));
+path.quadTo(SkBits2Float(0x42256760), SkBits2Float(0x420fa77f), SkBits2Float(0x421c1f6c), SkBits2Float(0x42191e14));
+path.quadTo(SkBits2Float(0x421bff97), SkBits2Float(0x42193e89), SkBits2Float(0x421bdf6b), SkBits2Float(0x42195eb8));
+path.quadTo(SkBits2Float(0x421bbff6), SkBits2Float(0x42197f32), SkBits2Float(0x421ba03b), SkBits2Float(0x42199f57));
+path.quadTo(SkBits2Float(0x421b605e), SkBits2Float(0x4219e00a), SkBits2Float(0x421b1fa8), SkBits2Float(0x421a1f22));
+path.quadTo(SkBits2Float(0x421ae0f1), SkBits2Float(0x421a604b), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
+path.quadTo(SkBits2Float(0x41ed7d86), SkBits2Float(0x42240000), SkBits2Float(0x41dabec3), SkBits2Float(0x421aa09e));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x42040000));
+path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x41ed7d86), SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
+path.quadTo(SkBits2Float(0x41db19b1), SkBits2Float(0x41da63d5), SkBits2Float(0x41db755b), SkBits2Float(0x41da0a9b));
+path.quadTo(SkBits2Float(0x41dbce01), SkBits2Float(0x41d9ae59), SkBits2Float(0x41dc285e), SkBits2Float(0x41d952ce));
+path.quadTo(SkBits2Float(0x41dc55b6), SkBits2Float(0x41d924df), SkBits2Float(0x41dc82cd), SkBits2Float(0x41d8f7cd));
+path.quadTo(SkBits2Float(0x41dcaf1e), SkBits2Float(0x41d8ca01), SkBits2Float(0x41dcdc4c), SkBits2Float(0x41d89bf0));
+path.quadTo(SkBits2Float(0x41ef6c33), SkBits2Float(0x41c5aec5), SkBits2Float(0x4204f72e), SkBits2Float(0x41c56cd2));
+path.close();
+testSimplify(reporter, path, filename);
+}
+
+static void dean4(skiatest::Reporter* reporter, const char* filename) {
+  SkPath path;
+
+  // start region
+  // start loop, contour: 1
+  // Segment 1145.3381097316742 2017.6783947944641 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2017.0033947825432
+  path.moveTo(1145.3381347656250, 2017.6783447265625);
+  path.lineTo(1145.3381347656250, 2017.0034179687500);
+  // Segment 1145.3381097316742 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6927231521568 2017.0033947825432
+  path.lineTo(1143.6927490234375, 2017.0034179687500);
+  // Segment 1143.6927231521568 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675112890 2018.1589246992417
+  path.lineTo(1144.8640136718750, 2018.1589355468750);
+  // Segment 1144.8640675112890 2018.1589246992417 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2017.6783947944641
+  path.lineTo(1145.3381347656250, 2017.6783447265625);
+  path.close();
+  // start loop, contour: 2
+  // Segment 1145.3381097316742 2016.3216052055359 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675258462 2015.8410752863977
+  path.moveTo(1145.3381347656250, 2016.3216552734375);
+  path.lineTo(1144.8640136718750, 2015.8410644531250);
+  // Segment 1144.8640675258462 2015.8410752863977 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6927230811802 2016.9966052174568
+  path.lineTo(1143.6927490234375, 2016.9965820312500);
+  // Segment 1143.6927230811802 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2016.9966052174568
+  path.lineTo(1145.3381347656250, 2016.9965820312500);
+  // Segment 1145.3381097316742 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2016.3216052055359
+  path.lineTo(1145.3381347656250, 2016.3216552734375);
+  path.close();
+  // start loop, contour: 3
+  // Segment 1147.3323798179626 2014.3542600870132 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220239557 2014.8347900059885
+  path.moveTo(1147.3323974609375, 2014.3542480468750);
+  path.lineTo(1147.8063964843750, 2014.8348388671875);
+  // Segment 1147.8064220239557 2014.8347900059885 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2014.8347899786306
+  path.lineTo(1147.8063964843750, 2014.8348388671875);
+  // Segment 1147.8064220516883 2014.8347899786306 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.3323798179626 2014.3542600870132
+  path.lineTo(1147.3323974609375, 2014.3542480468750);
+  path.close();
+  // start loop, contour: 4
+  // Segment 1146.3696286678314 2013.4045072346926 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708778083 2013.8850371497379
+  path.moveTo(1146.3696289062500, 2013.4045410156250);
+  path.lineTo(1146.8436279296875, 2013.8850097656250);
+  // Segment 1146.8436708778083 2013.8850371497379 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436709015571 2013.8850371263100
+  path.lineTo(1146.8436279296875, 2013.8850097656250);
+  // Segment 1146.8436709015571 2013.8850371263100 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.3696286678314 2013.4045072346926
+  path.lineTo(1146.3696289062500, 2013.4045410156250);
+  path.close();
+  // start loop, contour: 5
+  // Segment 1143.2063037902117 2016.5251235961914 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615802348 2016.0445936811461
+  path.moveTo(1143.2062988281250, 2016.5251464843750);
+  path.lineTo(1142.7322998046875, 2016.0445556640625);
+  // Segment 1142.7322615802348 2016.0445936811461 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615564860 2016.0445937045740
+  path.lineTo(1142.7322998046875, 2016.0445556640625);
+  // Segment 1142.7322615564860 2016.0445937045740 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.2063037902117 2016.5251235961914
+  path.lineTo(1143.2062988281250, 2016.5251464843750);
+  path.close();
+  // start loop, contour: 6
+  // Segment 1143.0687679275870 2016.7286419868469 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.5428101613127 2017.2091718784643
+  path.moveTo(1143.0687255859375, 2016.7286376953125);
+  path.lineTo(1143.5428466796875, 2017.2092285156250);
+  // Segment 1143.5428101613127 2017.2091718784643 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.7437679395080 2017.0109272411960
+  path.lineTo(1143.7437744140625, 2017.0109863281250);
+  // Segment 1143.7437679395080 2017.0109272411960 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.7437679395080 2016.7286419868469
+  path.lineTo(1143.7437744140625, 2016.7286376953125);
+  // Segment 1143.7437679395080 2016.7286419868469 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.0687679275870 2016.7286419868469
+  path.lineTo(1143.0687255859375, 2016.7286376953125);
+  path.close();
+  // start loop, contour: 7
+  // Segment 1143.2063037902117 2017.4748764038086 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615603032 2017.9554062991915
+  path.moveTo(1143.2062988281250, 2017.4748535156250);
+  path.lineTo(1142.7322998046875, 2017.9554443359375);
+  // Segment 1142.7322615603032 2017.9554062991915 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615746241 2017.9554063133189
+  path.lineTo(1142.7322998046875, 2017.9554443359375);
+  // Segment 1142.7322615746241 2017.9554063133189 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.2063037902117 2017.4748764038086
+  path.lineTo(1143.2062988281250, 2017.4748535156250);
+  path.close();
+  // start loop, contour: 8
+  // Segment 1146.3696286678314 2020.5954928398132 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708977399 2020.1149629444303
+  path.moveTo(1146.3696289062500, 2020.5954589843750);
+  path.lineTo(1146.8436279296875, 2020.1149902343750);
+  // Segment 1146.8436708977399 2020.1149629444303 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708834190 2020.1149629303029
+  path.lineTo(1146.8436279296875, 2020.1149902343750);
+  // Segment 1146.8436708834190 2020.1149629303029 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.3696286678314 2020.5954928398132
+  path.lineTo(1146.3696289062500, 2020.5954589843750);
+  path.close();
+  // start loop, contour: 9
+  // Segment 1147.3323798179626 2019.6457400321960 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220484741 2019.1652101374082
+  path.moveTo(1147.3323974609375, 2019.6457519531250);
+  path.lineTo(1147.8063964843750, 2019.1651611328125);
+  // Segment 1147.8064220484741 2019.1652101374082 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220383478 2019.1652101274185
+  path.lineTo(1147.8063964843750, 2019.1651611328125);
+  // Segment 1147.8064220383478 2019.1652101274185 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.3323798179626 2019.6457400321960
+  path.lineTo(1147.3323974609375, 2019.6457519531250);
+  path.close();
+  // start loop, contour: 10
+  // Segment 1145.3381097316742 2018.3533948063850 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2018.3533948063850
+  path.moveTo(1145.3381347656250, 2018.3533935546875);
+  path.lineTo(1156.6848144531250, 2018.3533935546875);
+  // Segment 1156.6848182678223 2018.3533948063850 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2017.0033947825432
+  path.lineTo(1156.6848144531250, 2017.0034179687500);
+  // Segment 1156.6848182678223 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2017.0033947825432
+  path.lineTo(1145.3381347656250, 2017.0034179687500);
+  // Segment 1145.3381097316742 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2018.3533948063850
+  path.lineTo(1145.3381347656250, 2018.3533935546875);
+  path.close();
+  // start loop, contour: 11
+  // Segment 1156.6848182678223 2018.3533948063850 0.3569631313191 0.0000000000000 -0.2645167304388 0.2609454237780 1157.6574279406423 2017.9723661860094
+  path.moveTo(1156.6848144531250, 2018.3533935546875);
+  path.cubicTo(1157.0417480468750, 2018.3533935546875, 1157.3929443359375, 2018.2332763671875, 1157.6574707031250, 2017.9724121093750);
+  // Segment 1157.6574279406423 2017.9723661860094 0.2653344079822 -0.2617520616521 0.0000000000000 0.3596905289350 1158.0474975705147 2017.0000000000000
+  path.cubicTo(1157.9227294921875, 2017.7105712890625, 1158.0474853515625, 2017.3597412109375, 1158.0474853515625, 2017.0000000000000);
+  // Segment 1158.0474975705147 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6974975466728 2017.0000000000000
+  path.lineTo(1156.6975097656250, 2017.0000000000000);
+  // Segment 1156.6974975466728 2017.0000000000000 0.0028009248351 0.0403311981485 0.0118595244351 -0.0220843520393 1156.6941780622435 2017.0325257649940
+  path.cubicTo(1156.7003173828125, 2017.0402832031250, 1156.7060546875000, 2017.0104980468750, 1156.6942138671875, 2017.0324707031250);
+  // Segment 1156.6941780622435 2017.0325257649940 -0.0032637855860 0.0184860248562 0.0120617528380 -0.0065934603083 1156.7093435710913 2017.0113063061967
+  path.cubicTo(1156.6909179687500, 2017.0510253906250, 1156.7214355468750, 2017.0047607421875, 1156.7093505859375, 2017.0113525390625);
+  // split at 0.4496445953846
+  // path.cubicTo(1156.6927490234375, 2017.0407714843750, 1156.6981201171875, 2017.0360107421875, 1156.7033691406250, 2017.0289306640625);
+  // path.cubicTo(1156.7097167968750, 2017.0201416015625, 1156.7159423828125, 2017.0076904296875, 1156.7093505859375, 2017.0113525390625);
+  // Segment 1156.7093435710913 2017.0113063061967 -0.0070717276929 0.0122220954353 0.0203483811973 -0.0039136894418 1156.7268834554304 2016.9985353221975
+  path.cubicTo(1156.7022705078125, 2017.0235595703125, 1156.7471923828125, 2016.9946289062500, 1156.7269287109375, 2016.9985351562500);
+  // Segment 1156.7268834554304 2016.9985353221975 -0.0244396787691 0.0123649140586 0.0433322464027 0.0026558844666 1156.6848182678223 2017.0033947825432
+  path.cubicTo(1156.7023925781250, 2017.0108642578125, 1156.7281494140625, 2017.0061035156250, 1156.6848144531250, 2017.0034179687500);
+  // split at 0.4418420493603
+  // path.cubicTo(1156.7160644531250, 2017.0040283203125, 1156.7150878906250, 2017.0061035156250, 1156.7136230468750, 2017.0065917968750);
+  // path.cubicTo(1156.7116699218750, 2017.0070800781250, 1156.7089843750000, 2017.0048828125000, 1156.6848144531250, 2017.0034179687500);
+  // Segment 1156.6848182678223 2017.0033947825432 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2018.3533948063850
+  path.lineTo(1156.6848144531250, 2018.3533935546875);
+  path.close();
+  // start loop, contour: 12
+  // Segment 1158.0474975705147 2017.0000000000000 0.0000000000000 -0.3596905289350 0.2653344079822 0.2617520616521 1157.6574279406423 2016.0276338139906
+  path.moveTo(1158.0474853515625, 2017.0000000000000);
+  path.cubicTo(1158.0474853515625, 2016.6402587890625, 1157.9227294921875, 2016.2894287109375, 1157.6574707031250, 2016.0275878906250);
+  // Segment 1157.6574279406423 2016.0276338139906 -0.2645167304388 -0.2609454237780 0.3569631313191 0.0000000000000 1156.6848182678223 2015.6466051936150
+  path.cubicTo(1157.3929443359375, 2015.7667236328125, 1157.0417480468750, 2015.6466064453125, 1156.6848144531250, 2015.6466064453125);
+  // split at 0.5481675863266
+  // path.cubicTo(1157.5124511718750, 2015.8846435546875, 1157.3414306640625, 2015.7839355468750, 1157.1577148437500, 2015.7220458984375);
+  // path.cubicTo(1157.0062255859375, 2015.6711425781250, 1156.8460693359375, 2015.6466064453125, 1156.6848144531250, 2015.6466064453125);
+  // Segment 1156.6848182678223 2015.6466051936150 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2016.9966052174568
+  path.lineTo(1156.6848144531250, 2016.9965820312500);
+  // Segment 1156.6848182678223 2016.9966052174568 0.0433322464027 -0.0026558844666 -0.0244396787691 -0.0123649140586 1156.7268834554304 2017.0014646778025
+  path.cubicTo(1156.7281494140625, 2016.9938964843750, 1156.7023925781250, 2016.9891357421875, 1156.7269287109375, 2017.0014648437500);
+  // split at 0.5581579208374
+  // path.cubicTo(1156.7089843750000, 2016.9951171875000, 1156.7116699218750, 2016.9929199218750, 1156.7136230468750, 2016.9934082031250);
+  // path.cubicTo(1156.7150878906250, 2016.9938964843750, 1156.7160644531250, 2016.9959716796875, 1156.7269287109375, 2017.0014648437500);
+  // Segment 1156.7268834554304 2017.0014646778025 0.0203483811973 0.0039136894418 -0.0070717276929 -0.0122220954353 1156.7093435710913 2016.9886936938033
+  path.cubicTo(1156.7471923828125, 2017.0053710937500, 1156.7022705078125, 2016.9764404296875, 1156.7093505859375, 2016.9886474609375);
+  // Segment 1156.7093435710913 2016.9886936938033 0.0120617528380 0.0065934603083 -0.0032637855860 -0.0184860248562 1156.6941780622435 2016.9674742350060
+  path.cubicTo(1156.7214355468750, 2016.9952392578125, 1156.6909179687500, 2016.9489746093750, 1156.6942138671875, 2016.9675292968750);
+  // Segment 1156.6941780622435 2016.9674742350060 0.0118595244351 0.0220843520393 0.0028009248351 -0.0403311981485 1156.6974975466728 2017.0000000000000
+  path.cubicTo(1156.7060546875000, 2016.9895019531250, 1156.7003173828125, 2016.9597167968750, 1156.6975097656250, 2017.0000000000000);
+  // split at 0.4572408795357
+  // path.cubicTo(1156.6995849609375, 2016.9775390625000, 1156.7014160156250, 2016.9768066406250, 1156.7014160156250, 2016.9768066406250);
+  // path.cubicTo(1156.7014160156250, 2016.9769287109375, 1156.6989746093750, 2016.9781494140625, 1156.6975097656250, 2017.0000000000000);
+  // Segment 1156.6974975466728 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1158.0474975705147 2017.0000000000000
+  path.lineTo(1158.0474853515625, 2017.0000000000000);
+  path.close();
+  // start loop, contour: 13
+  // Segment 1156.6848182678223 2015.6466051936150 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2015.6466051936150
+  path.moveTo(1156.6848144531250, 2015.6466064453125);
+  path.lineTo(1145.3381347656250, 2015.6466064453125);
+  // Segment 1145.3381097316742 2015.6466051936150 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.3381097316742 2016.9966052174568
+  path.lineTo(1145.3381347656250, 2016.9965820312500);
+  // Segment 1145.3381097316742 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2016.9966052174568
+  path.lineTo(1156.6848144531250, 2016.9965820312500);
+  // Segment 1156.6848182678223 2016.9966052174568 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1156.6848182678223 2015.6466051936150
+  path.lineTo(1156.6848144531250, 2015.6466064453125);
+  path.close();
+  // start loop, contour: 14
+  // Segment 1145.8121519375022 2016.8021351246741 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220237907 2014.8347900061515
+  path.moveTo(1145.8121337890625, 2016.8021240234375);
+  path.lineTo(1147.8063964843750, 2014.8348388671875);
+  // Segment 1147.8064220237907 2014.8347900061515 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583376121346 2013.8737301678750
+  path.lineTo(1146.8583984375000, 2013.8737792968750);
+  // Segment 1146.8583376121346 2013.8737301678750 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675258462 2015.8410752863977
+  path.lineTo(1144.8640136718750, 2015.8410644531250);
+  // Segment 1144.8640675258462 2015.8410752863977 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8121519375022 2016.8021351246741
+  path.lineTo(1145.8121337890625, 2016.8021240234375);
+  path.close();
+  // start loop, contour: 15
+  // Segment 1147.8064220516883 2014.8347899786306 0.5430154146087 -0.5356841365729 0.5430154146087 0.5356841365729 1147.8064220516883 2012.9239773430752
+  path.moveTo(1147.8063964843750, 2014.8348388671875);
+  path.cubicTo(1148.3494873046875, 2014.2990722656250, 1148.3494873046875, 2013.4597167968750, 1147.8063964843750, 2012.9239501953125);
+  // Segment 1147.8064220516883 2012.9239773430752 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375842370 2013.8850371263100
+  path.lineTo(1146.8583984375000, 2013.8850097656250);
+  // Segment 1146.8583375842370 2013.8850371263100 0.0071280060876 0.0070317705240 0.0071280060876 -0.0070317705240 1146.8583375842370 2013.8737301953959
+  path.cubicTo(1146.8654785156250, 2013.8920898437500, 1146.8654785156250, 2013.8666992187500, 1146.8583984375000, 2013.8737792968750);
+  // Segment 1146.8583375842370 2013.8737301953959 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2014.8347899786306
+  path.lineTo(1147.8063964843750, 2014.8348388671875);
+  path.close();
+  // start loop, contour: 16
+  // Segment 1147.8064220516883 2012.9239773430752 -0.5379138488298 -0.5306514472866 0.5379138488298 -0.5306514472866 1145.8955864341058 2012.9239773430752
+  path.moveTo(1147.8063964843750, 2012.9239501953125);
+  path.cubicTo(1147.2685546875000, 2012.3933105468750, 1146.4334716796875, 2012.3933105468750, 1145.8956298828125, 2012.9239501953125);
+  // Segment 1145.8955864341058 2012.9239773430752 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436709015571 2013.8850371263100
+  path.lineTo(1146.8436279296875, 2013.8850097656250);
+  // Segment 1146.8436709015571 2013.8850371263100 0.0122295718664 -0.0120644598103 -0.0122295718664 -0.0120644598103 1146.8583375842370 2013.8850371263100
+  path.cubicTo(1146.8559570312500, 2013.8729248046875, 1146.8460693359375, 2013.8729248046875, 1146.8583984375000, 2013.8850097656250);
+  // Segment 1146.8583375842370 2013.8850371263100 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2012.9239773430752
+  path.lineTo(1147.8063964843750, 2012.9239501953125);
+  path.close();
+  // start loop, contour: 17
+  // Segment 1145.8955864579798 2012.9239773195236 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615803600 2016.0445936810224
+  path.moveTo(1145.8956298828125, 2012.9239501953125);
+  path.lineTo(1142.7322998046875, 2016.0445556640625);
+  // Segment 1142.7322615803600 2016.0445936810224 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6803460000633 2017.0056535113604
+  path.lineTo(1143.6802978515625, 2017.0056152343750);
+  // Segment 1143.6803460000633 2017.0056535113604 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708776831 2013.8850371498615
+  path.lineTo(1146.8436279296875, 2013.8850097656250);
+  // Segment 1146.8436708776831 2013.8850371498615 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8955864579798 2012.9239773195236
+  path.lineTo(1145.8956298828125, 2012.9239501953125);
+  path.close();
+  // start loop, contour: 18
+  // Segment 1142.7322615564860 2016.0445937045740 -0.0343838913237 0.0339196727021 0.0561572931720 -0.0710493024751 1142.5744069596683 2016.2183613784646
+  path.moveTo(1142.7322998046875, 2016.0445556640625);
+  path.cubicTo(1142.6978759765625, 2016.0784912109375, 1142.6306152343750, 2016.1473388671875, 1142.5744628906250, 2016.2183837890625);
+  // Segment 1142.5744069596683 2016.2183613784646 -0.0547779032556 0.0720510806539 0.0000000000000 -0.2570904015602 1142.3937679156661 2016.7286419868469
+  path.cubicTo(1142.5196533203125, 2016.2904052734375, 1142.3937988281250, 2016.4715576171875, 1142.3937988281250, 2016.7286376953125);
+  // Segment 1142.3937679156661 2016.7286419868469 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.7437679395080 2016.7286419868469
+  path.lineTo(1143.7437744140625, 2016.7286376953125);
+  // Segment 1143.7437679395080 2016.7286419868469 -0.0051909534315 0.0665915567290 0.0133980913650 -0.0361675066532 1143.6976291086639 2016.9514128270803
+  path.cubicTo(1143.7385253906250, 2016.7952880859375, 1143.7110595703125, 2016.9152832031250, 1143.6976318359375, 2016.9514160156250);
+  // Segment 1143.6976291086639 2016.9514128270803 -0.0142876819622 0.0277028472317 0.0040377216094 -0.0063254385208 1143.6490888124401 2017.0354042045738
+  path.cubicTo(1143.6833496093750, 2016.9791259765625, 1143.6530761718750, 2017.0290527343750, 1143.6490478515625, 2017.0354003906250);
+  // Segment 1143.6490888124401 2017.0354042045738 -0.0045813437564 0.0032098513409 -0.0343840362634 0.0339198156850 1143.6803460239373 2017.0056534878088
+  path.cubicTo(1143.6445312500000, 2017.0385742187500, 1143.6459960937500, 2017.0395507812500, 1143.6802978515625, 2017.0056152343750);
+  // Segment 1143.6803460239373 2017.0056534878088 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615564860 2016.0445937045740
+  path.lineTo(1142.7322998046875, 2016.0445556640625);
+  path.close();
+  // start loop, contour: 19
+  // Segment 1142.5947256938614 2016.2481120952295 -0.1857487117715 0.1832409092043 0.0167379373694 -0.0990717748979 1142.3430278987244 2016.7518748698508
+  path.moveTo(1142.5947265625000, 2016.2481689453125);
+  path.cubicTo(1142.4089355468750, 2016.4313964843750, 1142.3597412109375, 2016.6528320312500, 1142.3430175781250, 2016.7518310546875);
+  // Segment 1142.3430278987244 2016.7518748698508 -0.0156657977007 0.1069052535795 0.0000000000000 -0.0339197441936 1142.3249999880791 2017.0000000000000
+  path.cubicTo(1142.3273925781250, 2016.8587646484375, 1142.3249511718750, 2016.9660644531250, 1142.3249511718750, 2017.0000000000000);
+  // Segment 1142.3249999880791 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6750000119209 2017.0000000000000
+  path.lineTo(1143.6750488281250, 2017.0000000000000);
+  // Segment 1143.6750000119209 2017.0000000000000 0.0000000000000 -0.0339197441936 -0.0015261841961 -0.0051459911965 1143.6741640831724 2016.9767671169961
+  path.cubicTo(1143.6750488281250, 2016.9660644531250, 1143.6726074218750, 2016.9716796875000, 1143.6741943359375, 2016.9768066406250);
+  // Segment 1143.6741640831724 2016.9767671169961 -0.0007886982052 0.0013596649622 0.0074114058388 -0.0224954551713 1143.6525251830094 2017.0486861571169
+  path.cubicTo(1143.6733398437500, 2016.9781494140625, 1143.6599121093750, 2017.0262451171875, 1143.6524658203125, 2017.0487060546875);
+  // split at 0.4203657805920
+  // path.cubicTo(1143.6738281250000, 2016.9774169921875, 1143.6712646484375, 2016.9862060546875, 1143.6678466796875, 2016.9979248046875);
+  // path.cubicTo(1143.6630859375000, 2017.0140380859375, 1143.6567382812500, 2017.0356445312500, 1143.6524658203125, 2017.0487060546875);
+  // Segment 1143.6525251830094 2017.0486861571169 -0.0119644334077 0.0236755853369 0.0381324473830 -0.0447670202574 1143.5428101613127 2017.2091718784643
+  path.cubicTo(1143.6405029296875, 2017.0723876953125, 1143.5809326171875, 2017.1644287109375, 1143.5428466796875, 2017.2092285156250);
+  // Segment 1143.5428101613127 2017.2091718784643 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.5947256938614 2016.2481120952295
+  path.lineTo(1142.5947265625000, 2016.2481689453125);
+  path.close();
+  // start loop, contour: 20
+  // Segment 1142.3249999880791 2017.0000000000000 0.0000000000000 0.0339197441936 -0.0156657977007 -0.1069052535795 1142.3430278987244 2017.2481251301492
+  path.moveTo(1142.3249511718750, 2017.0000000000000);
+  path.cubicTo(1142.3249511718750, 2017.0339355468750, 1142.3273925781250, 2017.1412353515625, 1142.3430175781250, 2017.2481689453125);
+  // Segment 1142.3430278987244 2017.2481251301492 0.0167379373694 0.0990717748979 -0.1857487117715 -0.1832409092043 1142.5947256938614 2017.7518879047705
+  path.cubicTo(1142.3597412109375, 2017.3471679687500, 1142.4089355468750, 2017.5686035156250, 1142.5947265625000, 2017.7518310546875);
+  // split at 0.4008532166481
+  // path.cubicTo(1142.3497314453125, 2017.2878417968750, 1142.3616943359375, 2017.3471679687500, 1142.3854980468750, 2017.4158935546875);
+  // path.cubicTo(1142.4211425781250, 2017.5185546875000, 1142.4833984375000, 2017.6420898437500, 1142.5947265625000, 2017.7518310546875);
+  // Segment 1142.5947256938614 2017.7518879047705 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.5428101613127 2016.7908281215357
+  path.lineTo(1143.5428466796875, 2016.7907714843750);
+  // Segment 1143.5428101613127 2016.7908281215357 0.0381324473830 0.0447670202574 -0.0119644334077 -0.0236755853369 1143.6525251830094 2016.9513138428831
+  path.cubicTo(1143.5809326171875, 2016.8355712890625, 1143.6405029296875, 2016.9276123046875, 1143.6524658203125, 2016.9512939453125);
+  // Segment 1143.6525251830094 2016.9513138428831 0.0074114058388 0.0224954551713 -0.0007886982052 -0.0013596649622 1143.6741640831724 2017.0232328830039
+  path.cubicTo(1143.6599121093750, 2016.9737548828125, 1143.6733398437500, 2017.0218505859375, 1143.6741943359375, 2017.0231933593750);
+  // Segment 1143.6741640831724 2017.0232328830039 -0.0015261841961 0.0051459911965 0.0000000000000 0.0339197441936 1143.6750000119209 2017.0000000000000
+  path.cubicTo(1143.6726074218750, 2017.0283203125000, 1143.6750488281250, 2017.0339355468750, 1143.6750488281250, 2017.0000000000000);
+  // Segment 1143.6750000119209 2017.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.3249999880791 2017.0000000000000
+  path.lineTo(1142.3249511718750, 2017.0000000000000);
+  path.close();
+  // start loop, contour: 21
+  // Segment 1142.5947256938614 2017.7518879047705 -0.0799271403989 -0.1522613934208 -0.2174629955730 -0.2879403701950 1142.7322615564860 2017.9554062954260
+  path.moveTo(1142.5947265625000, 2017.7518310546875);
+  path.cubicTo(1142.5147705078125, 2017.5996093750000, 1142.5147705078125, 2017.6674804687500, 1142.7322998046875, 2017.9554443359375);
+  // Segment 1142.7322615564860 2017.9554062954260 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6803460239373 2016.9943465121912
+  path.lineTo(1143.6802978515625, 2016.9943847656250);
+  // Segment 1143.6803460239373 2016.9943465121912 0.0799271403989 0.1522613934208 0.2174629955730 0.2879403701950 1143.5428101613127 2016.7908281215357
+  path.cubicTo(1143.7602539062500, 2017.1466064453125, 1143.7602539062500, 2017.0787353515625, 1143.5428466796875, 2016.7907714843750);
+  // Segment 1143.5428101613127 2016.7908281215357 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.5947256938614 2017.7518879047705
+  path.lineTo(1142.5947265625000, 2017.7518310546875);
+  path.close();
+  // start loop, contour: 22
+  // Segment 1142.7322615746241 2017.9554063133189 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8955864522438 2021.0760227493236
+  path.moveTo(1142.7322998046875, 2017.9554443359375);
+  path.lineTo(1145.8956298828125, 2021.0760498046875);
+  // Segment 1145.8955864522438 2021.0760227493236 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8436708834190 2020.1149629303029
+  path.lineTo(1146.8436279296875, 2020.1149902343750);
+  // Segment 1146.8436708834190 2020.1149629303029 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1143.6803460057993 2016.9943464942983
+  path.lineTo(1143.6802978515625, 2016.9943847656250);
+  // Segment 1143.6803460057993 2016.9943464942983 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1142.7322615746241 2017.9554063133189
+  path.lineTo(1142.7322998046875, 2017.9554443359375);
+  path.close();
+  // start loop, contour: 23
+  // Segment 1145.8955864341058 2021.0760227314306 0.2730164534637 0.2693304447891 -0.3016608168437 0.0000000000000 1146.8510041236877 2021.4740112423897
+  path.moveTo(1145.8956298828125, 2021.0760498046875);
+  path.cubicTo(1146.1685791015625, 2021.3453369140625, 1146.5493164062500, 2021.4739990234375, 1146.8509521484375, 2021.4739990234375);
+  // Segment 1146.8510041236877 2021.4740112423897 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8510041236877 2020.1240112185478
+  path.lineTo(1146.8509521484375, 2020.1240234375000);
+  // Segment 1146.8510041236877 2020.1240112185478 -0.0031276099109 0.0031991747760 0.0281856144058 0.0140930868099 1146.8580791488898 2020.1202473991566
+  path.cubicTo(1146.8479003906250, 2020.1271972656250, 1146.8862304687500, 2020.1343994140625, 1146.8580322265625, 2020.1202392578125);
+  // split at 0.3845077157021
+  // path.cubicTo(1146.8497314453125, 2020.1252441406250, 1146.8547363281250, 2020.1270751953125, 1146.8596191406250, 2020.1280517578125);
+  // path.cubicTo(1146.8675537109375, 2020.1296386718750, 1146.8753662109375, 2020.1289062500000, 1146.8580322265625, 2020.1202392578125);
+  // Segment 1146.8580791488898 2020.1202473991566 -0.0369995545027 -0.0123195805663 0.0067223483810 0.0136883790721 1146.8436709015571 2020.1149629481959
+  path.cubicTo(1146.8210449218750, 2020.1079101562500, 1146.8503417968750, 2020.1286621093750, 1146.8436279296875, 2020.1149902343750);
+  // Segment 1146.8436709015571 2020.1149629481959 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8955864341058 2021.0760227314306
+  path.lineTo(1145.8956298828125, 2021.0760498046875);
+  path.close();
+  // start loop, contour: 24
+  // Segment 1146.8510041236877 2021.4740112423897 0.3016605789999 0.0000000000000 -0.2730166120260 0.2693306012106 1147.8064220516883 2021.0760227314306
+  path.moveTo(1146.8509521484375, 2021.4739990234375);
+  path.cubicTo(1147.1527099609375, 2021.4739990234375, 1147.5334472656250, 2021.3453369140625, 1147.8063964843750, 2021.0760498046875);
+  // Segment 1147.8064220516883 2021.0760227314306 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375842370 2020.1149629481959
+  path.lineTo(1146.8583984375000, 2020.1149902343750);
+  // Segment 1146.8583375842370 2020.1149629481959 -0.0067222671256 0.0136883164611 0.0369996293611 -0.0123196021258 1146.8439293663473 2020.1202473404985
+  path.cubicTo(1146.8515625000000, 2020.1286621093750, 1146.8809814453125, 2020.1079101562500, 1146.8438720703125, 2020.1202392578125);
+  // Segment 1146.8439293663473 2020.1202473404985 -0.0281857033438 0.0140931104690 0.0031276541428 0.0031991704542 1146.8510041236877 2020.1240112185478
+  path.cubicTo(1146.8157958984375, 2020.1343994140625, 1146.8541259765625, 2020.1271972656250, 1146.8509521484375, 2020.1240234375000);
+  // Segment 1146.8510041236877 2020.1240112185478 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8510041236877 2021.4740112423897
+  path.lineTo(1146.8509521484375, 2021.4739990234375);
+  path.close();
+  // start loop, contour: 25
+  // Segment 1147.8064220516883 2021.0760227314306 0.5430154146087 -0.5356841365729 0.5430154146087 0.5356841365729 1147.8064220516883 2019.1652101405787
+  path.moveTo(1147.8063964843750, 2021.0760498046875);
+  path.cubicTo(1148.3494873046875, 2020.5402832031250, 1148.3494873046875, 2019.7009277343750, 1147.8063964843750, 2019.1651611328125);
+  // Segment 1147.8064220516883 2019.1652101405787 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375842370 2020.1262699238134
+  path.lineTo(1146.8583984375000, 2020.1262207031250);
+  // Segment 1146.8583375842370 2020.1262699238134 0.0071280060876 0.0070317705240 0.0071280060876 -0.0070317705240 1146.8583375842370 2020.1149629481959
+  path.cubicTo(1146.8654785156250, 2020.1333007812500, 1146.8654785156250, 2020.1079101562500, 1146.8583984375000, 2020.1149902343750);
+  // Segment 1146.8583375842370 2020.1149629481959 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220516883 2021.0760227314306
+  path.lineTo(1147.8063964843750, 2021.0760498046875);
+  path.close();
+  // start loop, contour: 26
+  // Segment 1147.8064220383478 2019.1652101274185 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1145.8121519520594 2017.1978648896866
+  path.moveTo(1147.8063964843750, 2019.1651611328125);
+  path.lineTo(1145.8121337890625, 2017.1978759765625);
+  // Segment 1145.8121519520594 2017.1978648896866 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1144.8640675112890 2018.1589246992417
+  path.lineTo(1144.8640136718750, 2018.1589355468750);
+  // Segment 1144.8640675112890 2018.1589246992417 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1146.8583375975775 2020.1262699369736
+  path.lineTo(1146.8583984375000, 2020.1262207031250);
+  // Segment 1146.8583375975775 2020.1262699369736 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1147.8064220383478 2019.1652101274185
+  path.lineTo(1147.8063964843750, 2019.1651611328125);
+  path.close();
+
+testSimplify(reporter, path, filename);
+}
+
+static void testQuads66(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(2, 0);
+    path.quadTo(3, 1, 2, 2);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(2, 1);
+    path.lineTo(2, 1);
+    path.quadTo(1, 2, 2, 2);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads67(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(3, 2);
+    path.quadTo(1, 3, 3, 3);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(2, 3, 3, 3);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads68(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(1, 2);
+    path.quadTo(0, 3, 2, 3);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(0, 1);
+    path.quadTo(1, 3, 2, 3);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads69(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(1, 0);
+    path.quadTo(2, 2, 2, 3);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 0);
+    path.quadTo(3, 0, 1, 3);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads70(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.moveTo(1, 1);
+    path.quadTo(2, 3, 3, 3);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(2, 0);
+    path.lineTo(2, 2);
+    path.quadTo(1, 3, 3, 3);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads71(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.moveTo(1, 1);
+    path.quadTo(2, 3, 3, 3);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(2, 2);
+    path.quadTo(1, 3, 3, 3);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads72(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.moveTo(1, 1);
+    path.quadTo(2, 3, 3, 3);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(0, 1);
+    path.lineTo(2, 2);
+    path.quadTo(1, 3, 3, 3);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads73(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 1, 1, 2);
+    path.lineTo(0, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(0, 1, 1, 1);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void bug5169(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x4281c71c));  // 0, 64.8889f
+path.cubicTo(SkBits2Float(0x434e0000), SkBits2Float(0x4281c71c), SkBits2Float(0x00000000), SkBits2Float(0xc2a238e4), SkBits2Float(0x00000000), SkBits2Float(0x4281c71c));  // 206, 64.8889f, 0, -81.1111f, 0, 64.8889f
+path.moveTo(SkBits2Float(0x43300000), SkBits2Float(0x41971c72));  // 176, 18.8889f
+path.cubicTo(SkBits2Float(0xc29e0000), SkBits2Float(0xc25c71c7), SkBits2Float(0x42b20000), SkBits2Float(0x42fbc71c), SkBits2Float(0x43300000), SkBits2Float(0x41971c72));  // -79, -55.1111f, 89, 125.889f, 176, 18.8889f
+    testSimplify(reporter, path, filename);
+}
+
+static void tiger8_393(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42b93333), SkBits2Float(0x43d5a666));  // 92.6f, 427.3f
+path.cubicTo(SkBits2Float(0x42b93333), SkBits2Float(0x43d5a666), SkBits2Float(0x42b5cccd), SkBits2Float(0x43da1999), SkBits2Float(0x42b80000), SkBits2Float(0x43ddf333));  // 92.6f, 427.3f, 90.9f, 436.2f, 92, 443.9f
+path.cubicTo(SkBits2Float(0x42b80000), SkBits2Float(0x43ddf333), SkBits2Float(0x42b30000), SkBits2Float(0x43e17333), SkBits2Float(0x42cf999a), SkBits2Float(0x43e1b333));  // 92, 443.9f, 89.5f, 450.9f, 103.8f, 451.4f
+path.cubicTo(SkBits2Float(0x42ec3334), SkBits2Float(0x43e14ccd), SkBits2Float(0x42e73334), SkBits2Float(0x43ddf333), SkBits2Float(0x42e73334), SkBits2Float(0x43ddf333));  // 118.1f, 450.6f, 115.6f, 443.9f, 115.6f, 443.9f
+path.cubicTo(SkBits2Float(0x42e7999a), SkBits2Float(0x43de8000), SkBits2Float(0x42ea6667), SkBits2Float(0x43db4000), SkBits2Float(0x42e60001), SkBits2Float(0x43d5a666));  // 115.8f, 445, 117.2f, 438.5f, 115, 427.3f
+    testSimplify(reporter, path, filename);
+}
+
+// triggers angle assert from distance field code
+static void carsvg_1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4393d61e), SkBits2Float(0x43e768f9));  // 295.673f, 462.82f
+path.cubicTo(SkBits2Float(0x4396b50e), SkBits2Float(0x43e63c20), SkBits2Float(0x43998931), SkBits2Float(0x43e6c43e), SkBits2Float(0x439cb6a8), SkBits2Float(0x43e70ef9));  // 301.414f, 460.47f, 307.072f, 461.533f, 313.427f, 462.117f
+path.cubicTo(SkBits2Float(0x439dfc1e), SkBits2Float(0x43e72ce0), SkBits2Float(0x439a285c), SkBits2Float(0x43e717fb), SkBits2Float(0x4398e23c), SkBits2Float(0x43e7027c));  // 315.97f, 462.351f, 308.315f, 462.187f, 305.767f, 462.019f
+path.cubicTo(SkBits2Float(0x4398136f), SkBits2Float(0x43e6f4db), SkBits2Float(0x439a7e14), SkBits2Float(0x43e6d390), SkBits2Float(0x439b4ba9), SkBits2Float(0x43e6b956));  // 304.152f, 461.913f, 308.985f, 461.653f, 310.591f, 461.448f
+path.cubicTo(SkBits2Float(0x439c2b19), SkBits2Float(0x43e68603), SkBits2Float(0x43abf4df), SkBits2Float(0x43e9ca9e), SkBits2Float(0x43a1daea), SkBits2Float(0x43e912a5));  // 312.337f, 461.047f, 343.913f, 467.583f, 323.71f, 466.146f
+path.cubicTo(SkBits2Float(0x43a4f45a), SkBits2Float(0x43e78baf), SkBits2Float(0x43a2a391), SkBits2Float(0x43e86a82), SkBits2Float(0x43a946bd), SkBits2Float(0x43e90c56));  // 329.909f, 463.091f, 325.278f, 464.832f, 338.553f, 466.096f
+path.lineTo(SkBits2Float(0x43a4250b), SkBits2Float(0x43e998dc));  // 328.289f, 467.194f
+path.cubicTo(SkBits2Float(0x43a8a9c8), SkBits2Float(0x43e8f06c), SkBits2Float(0x43a95cb5), SkBits2Float(0x43e84ea6), SkBits2Float(0x43a6f7c1), SkBits2Float(0x43e9bdb5));  // 337.326f, 465.878f, 338.724f, 464.614f, 333.936f, 467.482f
+path.cubicTo(SkBits2Float(0x43a59ed0), SkBits2Float(0x43e9d2ca), SkBits2Float(0x4395ea4d), SkBits2Float(0x43e92afe), SkBits2Float(0x43a06569), SkBits2Float(0x43e7773d));  // 331.241f, 467.647f, 299.83f, 466.336f, 320.792f, 462.932f
+path.cubicTo(SkBits2Float(0x438bf0ff), SkBits2Float(0x43ea0fef), SkBits2Float(0x43a0e17a), SkBits2Float(0x43e5f41b), SkBits2Float(0x4398f3fb), SkBits2Float(0x43e804c8));  // 279.883f, 468.124f, 321.762f, 459.907f, 305.906f, 464.037f
+path.lineTo(SkBits2Float(0x4393d61e), SkBits2Float(0x43e768f9));  // 295.673f, 462.82f
+path.close();
+
+    testSimplify(reporter, path, filename);
+}
+
+static void simplifyTest_1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x42bfefd4), SkBits2Float(0x42ef80ef));  // 95.9684f, 119.752f
+path.quadTo(SkBits2Float(0x42c26810), SkBits2Float(0x42e214b8), SkBits2Float(0x42cdcad5), SkBits2Float(0x42d82aa2));  // 97.2032f, 113.04f, 102.896f, 108.083f
+path.lineTo(SkBits2Float(0x42cdcb21), SkBits2Float(0x42d82a61));  // 102.897f, 108.083f
+path.quadTo(SkBits2Float(0x42d5e3c8), SkBits2Float(0x42d12140), SkBits2Float(0x42e20ee8), SkBits2Float(0x42cdc937));  // 106.945f, 104.565f, 113.029f, 102.893f
+path.lineTo(SkBits2Float(0x42e256e3), SkBits2Float(0x42cdbc92));  // 113.17f, 102.868f
+path.lineTo(SkBits2Float(0x42f5eadb), SkBits2Float(0x42cc2cb3));  // 122.959f, 102.087f
+path.lineTo(SkBits2Float(0x42f746a6), SkBits2Float(0x42cccf85));  // 123.638f, 102.405f
+path.quadTo(SkBits2Float(0x42fa586c), SkBits2Float(0x42d126c4), SkBits2Float(0x42f6c657), SkBits2Float(0x42d5d315));  // 125.173f, 104.576f, 123.387f, 106.912f
+path.lineTo(SkBits2Float(0x42f591eb), SkBits2Float(0x42d4e76d));  // 122.785f, 106.452f
+path.lineTo(SkBits2Float(0x42f6c6e0), SkBits2Float(0x42d5d261));  // 123.388f, 106.911f
+path.quadTo(SkBits2Float(0x42f6bb33), SkBits2Float(0x42d5e1bb), SkBits2Float(0x42f6a3d8), SkBits2Float(0x42d6007c));  // 123.366f, 106.941f, 123.32f, 107.001f
+path.quadTo(SkBits2Float(0x42ea3850), SkBits2Float(0x42e65af0), SkBits2Float(0x42d97a6e), SkBits2Float(0x42ed841c));  // 117.11f, 115.178f, 108.739f, 118.758f
+path.lineTo(SkBits2Float(0x42d91d92), SkBits2Float(0x42ed9ec0));  // 108.558f, 118.81f
+path.lineTo(SkBits2Float(0x42c1a959), SkBits2Float(0x42f146b0));  // 96.8308f, 120.638f
+path.lineTo(SkBits2Float(0x42bfefd4), SkBits2Float(0x42ef80f0));  // 95.9684f, 119.752f
+path.lineTo(SkBits2Float(0x42bfefd4), SkBits2Float(0x42ef80ef));  // 95.9684f, 119.752f
+path.close();
+path.moveTo(SkBits2Float(0x42c2eb4e), SkBits2Float(0x42f00d68));  // 97.4596f, 120.026f
+path.lineTo(SkBits2Float(0x42c16d91), SkBits2Float(0x42efc72c));  // 96.714f, 119.889f
+path.lineTo(SkBits2Float(0x42c131c9), SkBits2Float(0x42ee47a8));  // 96.5972f, 119.14f
+path.lineTo(SkBits2Float(0x42d8a602), SkBits2Float(0x42ea9fb8));  // 108.324f, 117.312f
+path.lineTo(SkBits2Float(0x42d8e1ca), SkBits2Float(0x42ec1f3c));  // 108.441f, 118.061f
+path.lineTo(SkBits2Float(0x42d84926), SkBits2Float(0x42eaba5c));  // 108.143f, 117.364f
+path.quadTo(SkBits2Float(0x42e84a40), SkBits2Float(0x42e3e1f0), SkBits2Float(0x42f439a2), SkBits2Float(0x42d42af8));  // 116.145f, 113.941f, 122.113f, 106.084f
+path.quadTo(SkBits2Float(0x42f45121), SkBits2Float(0x42d40c08), SkBits2Float(0x42f45cf6), SkBits2Float(0x42d3fc79));  // 122.158f, 106.023f, 122.182f, 105.993f
+path.lineTo(SkBits2Float(0x42f45d7f), SkBits2Float(0x42d3fbc5));  // 122.183f, 105.992f
+path.quadTo(SkBits2Float(0x42f69510), SkBits2Float(0x42d114f4), SkBits2Float(0x42f4ccce), SkBits2Float(0x42ce8fb7));  // 123.291f, 104.541f, 122.4f, 103.281f
+path.lineTo(SkBits2Float(0x42f609ba), SkBits2Float(0x42cdaf9e));  // 123.019f, 102.843f
+path.lineTo(SkBits2Float(0x42f62899), SkBits2Float(0x42cf3289));  // 123.079f, 103.599f
+path.lineTo(SkBits2Float(0x42e294a1), SkBits2Float(0x42d0c268));  // 113.29f, 104.38f
+path.lineTo(SkBits2Float(0x42e275c2), SkBits2Float(0x42cf3f7d));  // 113.23f, 103.624f
+path.lineTo(SkBits2Float(0x42e2dc9c), SkBits2Float(0x42d0b5c3));  // 113.431f, 104.355f
+path.quadTo(SkBits2Float(0x42d75bb8), SkBits2Float(0x42d3df08), SkBits2Float(0x42cfc853), SkBits2Float(0x42da7457));  // 107.679f, 105.936f, 103.891f, 109.227f
+path.lineTo(SkBits2Float(0x42cec9ba), SkBits2Float(0x42d94f5c));  // 103.394f, 108.655f
+path.lineTo(SkBits2Float(0x42cfc89f), SkBits2Float(0x42da7416));  // 103.892f, 109.227f
+path.quadTo(SkBits2Float(0x42c53268), SkBits2Float(0x42e3ac00), SkBits2Float(0x42c2eb4e), SkBits2Float(0x42f00d67));  // 98.5984f, 113.836f, 97.4596f, 120.026f
+path.lineTo(SkBits2Float(0x42c2eb4e), SkBits2Float(0x42f00d68));  // 97.4596f, 120.026f
+path.close();
+
+    testSimplify(reporter, path, filename);
+}
+

+static void joel_1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(144.859f, 285.172f);

+path.lineTo(144.859f, 285.172f);

+path.lineTo(144.859f, 285.172f);

+path.lineTo(143.132f, 284.617f);

+path.lineTo(144.859f, 285.172f);

+path.close();

+path.moveTo(135.922f, 286.844f);

+path.lineTo(135.922f, 286.844f);

+path.lineTo(135.922f, 286.844f);

+path.lineTo(135.367f, 288.571f);

+path.lineTo(135.922f, 286.844f);

+path.close();

+path.moveTo(135.922f, 286.844f);

+path.cubicTo(137.07f, 287.219f, 138.242f, 287.086f, 139.242f, 286.578f);

+path.cubicTo(140.234f, 286.078f, 141.031f, 285.203f, 141.406f, 284.055f);

+path.lineTo(144.859f, 285.172f);

+path.cubicTo(143.492f, 289.375f, 138.992f, 291.656f, 134.797f, 290.297f);

+path.lineTo(135.922f, 286.844f);

+path.close();

+path.moveTo(129.68f, 280.242f);

+path.lineTo(129.68f, 280.242f);

+path.lineTo(129.68f, 280.242f);

+path.lineTo(131.407f, 280.804f);

+path.lineTo(129.68f, 280.242f);

+path.close();

+path.moveTo(133.133f, 281.367f);

+path.cubicTo(132.758f, 282.508f, 132.883f, 283.687f, 133.391f, 284.679f);

+path.cubicTo(133.907f, 285.679f, 134.774f, 286.468f, 135.922f, 286.843f);

+path.lineTo(134.797f, 290.296f);

+path.cubicTo(130.602f, 288.929f, 128.313f, 284.437f, 129.68f, 280.241f);

+path.lineTo(133.133f, 281.367f);

+path.close();

+path.moveTo(139.742f, 275.117f);

+path.lineTo(139.742f, 275.117f);

+path.lineTo(139.18f, 276.844f);

+path.lineTo(139.742f, 275.117f);

+path.close();

+path.moveTo(138.609f, 278.57f);

+path.cubicTo(137.461f, 278.203f, 136.297f, 278.328f, 135.297f, 278.836f);

+path.cubicTo(134.297f, 279.344f, 133.508f, 280.219f, 133.133f, 281.367f);

+path.lineTo(129.68f, 280.242f);

+path.cubicTo(131.047f, 276.039f, 135.539f, 273.758f, 139.742f, 275.117f);

+path.lineTo(138.609f, 278.57f);

+path.close();

+path.moveTo(141.406f, 284.055f);

+path.cubicTo(141.773f, 282.907f, 141.648f, 281.735f, 141.148f, 280.735f);

+path.cubicTo(140.625f, 279.735f, 139.757f, 278.946f, 138.609f, 278.571f);

+path.lineTo(139.742f, 275.118f);

+path.cubicTo(143.937f, 276.493f, 146.219f, 280.977f, 144.859f, 285.173f);

+path.lineTo(141.406f, 284.055f);

+path.close();

+    testSimplify(reporter, path, filename);
+}

+

+static void joel_2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+

+path.moveTo(403.283f, 497.197f);

+path.cubicTo(403.424f, 497.244f, 391.111f, 495.556f, 391.111f, 495.556f);

+path.lineTo(392.291f, 493.165f);

+path.cubicTo(392.291f, 493.165f, 388.994f, 492.056f, 386.65f, 491.821f);

+path.cubicTo(384.244f, 491.454f, 381.603f, 490.774f, 381.603f, 490.774f);

+path.lineTo(383.392f, 488.383f);

+path.cubicTo(383.392f, 488.383f, 379.119f, 487.453f, 378.939f, 485.695f);

+path.cubicTo(378.791f, 483.57f, 383.064f, 485.25f, 384.877f, 485.843f);

+path.lineTo(387.697f, 484.351f);

+path.cubicTo(382.752f, 483.835f, 376.595f, 482.124f, 374.478f, 480.312f);

+path.lineTo(356.22f, 496.304f);

+path.lineTo(368.095f, 510.499f);

+path.lineTo(373.884f, 510.202f);

+path.lineTo(374.478f, 509.007f);

+path.lineTo(370.916f, 506.913f);

+path.lineTo(371.807f, 506.022f);

+path.cubicTo(371.807f, 506.022f, 374.807f, 507.28f, 377.752f, 507.514f);

+path.cubicTo(380.752f, 507.881f, 387.4f, 508.108f, 387.4f, 508.108f);

+path.lineTo(388.884f, 506.764f);

+path.cubicTo(388.884f, 506.764f, 378.345f, 504.998f, 378.345f, 504.819f);

+path.lineTo(378.04f, 503.03f);

+path.cubicTo(378.04f, 503.03f, 391.415f, 505.796f, 391.399f, 505.866f);

+path.lineTo(386.063f, 502.132f);

+path.lineTo(387.547f, 500.335f);

+path.lineTo(398.375f, 501.976f);

+path.lineTo(403.283f, 497.197f);

+path.lineTo(403.283f, 497.197f);

+path.close();

+    testSimplify(reporter, path, filename);
+}

+

+static void joel_3(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(391.097f, 334.453f);

+path.lineTo(390.761f, 334.617f);

+path.lineTo(390.425f, 333.937f);

+path.lineTo(390.761f, 333.765f);

+path.lineTo(391.097f, 334.453f);

+path.close();

+path.moveTo(391.128f, 334.438f);

+path.lineTo(390.808f, 334.633f);

+path.lineTo(390.402f, 333.992f);

+path.lineTo(390.73f, 333.781f);

+path.lineTo(391.128f, 334.438f);

+path.lineTo(391.128f, 334.438f);

+path.close();

+path.moveTo(455.073f, 302.219f);

+path.lineTo(455.018f, 302.375f);

+path.lineTo(454.87f, 302.453f);

+path.lineTo(454.706f, 302.109f);

+path.lineTo(455.073f, 302.219f);

+path.close();

+path.moveTo(454.87f, 302.453f);

+path.lineTo(391.097f, 334.453f);

+path.lineTo(390.761f, 333.765f);

+path.lineTo(454.534f, 301.765f);

+path.lineTo(454.87f, 302.453f);

+path.close();

+path.moveTo(456.245f, 296.867f);

+path.lineTo(456.659f, 296.953f);

+path.lineTo(456.526f, 297.351f);

+path.lineTo(456.174f, 297.242f);

+path.lineTo(456.245f, 296.867f);

+path.lineTo(456.245f, 296.867f);

+path.close();

+path.moveTo(456.526f, 297.352f);

+path.lineTo(455.073f, 302.219f);

+path.lineTo(454.339f, 302);

+path.lineTo(455.808f, 297.133f);

+path.lineTo(456.526f, 297.352f);

+path.lineTo(456.526f, 297.352f);

+path.close();

+path.moveTo(450.979f, 295.891f);

+path.lineTo(451.112f, 295.813f);

+path.lineTo(451.26f, 295.836f);

+path.lineTo(451.19f, 296.211f);

+path.lineTo(450.979f, 295.891f);

+path.close();

+path.moveTo(451.261f, 295.836f);

+path.lineTo(456.245f, 296.867f);

+path.lineTo(456.089f, 297.617f);

+path.lineTo(451.105f, 296.586f);

+path.lineTo(451.261f, 295.836f);

+path.close();

+path.moveTo(390.729f, 333.781f);

+path.lineTo(450.979f, 295.89f);

+path.lineTo(451.385f, 296.531f);

+path.lineTo(391.127f, 334.437f);

+path.lineTo(390.729f, 333.781f);

+path.close();
+    testSimplify(reporter, path, filename);
+}

+
+static void joel_4(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4199d4fe), SkBits2Float(0x4265ac08));  // 19.229f, 57.418f

+path.cubicTo(SkBits2Float(0x419be979), SkBits2Float(0x426574bc), SkBits2Float(0x419c2b02), SkBits2Float(0x42653c6a), SkBits2Float(0x419af5c3), SkBits2Float(0x42645f3b));  // 19.489f, 57.364f, 19.521f, 57.309f, 19.37f, 57.093f

+path.cubicTo(SkBits2Float(0x419a1894), SkBits2Float(0x4263a3d7), SkBits2Float(0x4198cccd), SkBits2Float(0x4262f2b0), SkBits2Float(0x4197c290), SkBits2Float(0x4262374b));  // 19.262f, 56.91f, 19.1f, 56.737f, 18.97f, 56.554f

+path.cubicTo(SkBits2Float(0x41960832), SkBits2Float(0x42610c49), SkBits2Float(0x41944dd4), SkBits2Float(0x425fd709), SkBits2Float(0x41927cee), SkBits2Float(0x425ea0c4));  // 18.754f, 56.262f, 18.538f, 55.96f, 18.311f, 55.657f

+path.cubicTo(SkBits2Float(0x4191b646), SkBits2Float(0x425e1cab), SkBits2Float(0x418edd30), SkBits2Float(0x425ca4dd), SkBits2Float(0x418f4bc7), SkBits2Float(0x425bdd2e));  // 18.214f, 55.528f, 17.858f, 55.161f, 17.912f, 54.966f

+path.lineTo(SkBits2Float(0x41903f7d), SkBits2Float(0x425b6e96));  // 18.031f, 54.858f

+path.cubicTo(SkBits2Float(0x41921062), SkBits2Float(0x425aa6e8), SkBits2Float(0x4193872b), SkBits2Float(0x425bd1ea), SkBits2Float(0x41947ae1), SkBits2Float(0x425c77cd));  // 18.258f, 54.663f, 18.441f, 54.955f, 18.56f, 55.117f

+path.cubicTo(SkBits2Float(0x4195dd2f), SkBits2Float(0x425d6b83), SkBits2Float(0x4197ae14), SkBits2Float(0x425e1caa), SkBits2Float(0x419924dd), SkBits2Float(0x425ef9d9));  // 18.733f, 55.355f, 18.96f, 55.528f, 19.143f, 55.744f

+path.cubicTo(SkBits2Float(0x419a1893), SkBits2Float(0x425f9479), SkBits2Float(0x419adf3b), SkBits2Float(0x42601997), SkBits2Float(0x419bd2f1), SkBits2Float(0x42609db0));  // 19.262f, 55.895f, 19.359f, 56.025f, 19.478f, 56.154f

+path.cubicTo(SkBits2Float(0x419c147a), SkBits2Float(0x4260c9b8), SkBits2Float(0x419c8312), SkBits2Float(0x4260e03f), SkBits2Float(0x419cb020), SkBits2Float(0x42610104));  // 19.51f, 56.197f, 19.564f, 56.219f, 19.586f, 56.251f

+path.cubicTo(SkBits2Float(0x419d0830), SkBits2Float(0x42613850), SkBits2Float(0x419da3d6), SkBits2Float(0x4261bd6e), SkBits2Float(0x419e126e), SkBits2Float(0x4261d2f0));  // 19.629f, 56.305f, 19.705f, 56.435f, 19.759f, 56.456f

+path.lineTo(SkBits2Float(0x419e28f5), SkBits2Float(0x4261d2f0));  // 19.77f, 56.456f

+path.lineTo(SkBits2Float(0x419e28f5), SkBits2Float(0x4261f4bb));  // 19.77f, 56.489f

+path.cubicTo(SkBits2Float(0x419e3d70), SkBits2Float(0x4261fef8), SkBits2Float(0x419e53f7), SkBits2Float(0x4261f4bb), SkBits2Float(0x419e8105), SkBits2Float(0x4261fef8));  // 19.78f, 56.499f, 19.791f, 56.489f, 19.813f, 56.499f

+path.cubicTo(SkBits2Float(0x419eac07), SkBits2Float(0x426220c3), SkBits2Float(0x419eac07), SkBits2Float(0x42624187), SkBits2Float(0x419eef9d), SkBits2Float(0x4262580f));  // 19.834f, 56.532f, 19.834f, 56.564f, 19.867f, 56.586f

+path.cubicTo(SkBits2Float(0x419fe353), SkBits2Float(0x4262f2af), SkBits2Float(0x41a0eb84), SkBits2Float(0x426377cd), SkBits2Float(0x41a1b22c), SkBits2Float(0x4263fbe6));  // 19.986f, 56.737f, 20.115f, 56.867f, 20.212f, 56.996f

+path.cubicTo(SkBits2Float(0x41a20a3c), SkBits2Float(0x42641db1), SkBits2Float(0x41a2e76b), SkBits2Float(0x4264a1c9), SkBits2Float(0x41a34188), SkBits2Float(0x4264ad0d));  // 20.255f, 57.029f, 20.363f, 57.158f, 20.407f, 57.169f

+path.cubicTo(SkBits2Float(0x41a36c8a), SkBits2Float(0x4264ad0d), SkBits2Float(0x41a3c6a7), SkBits2Float(0x4264a1c9), SkBits2Float(0x41a3f1a9), SkBits2Float(0x4264ad0d));  // 20.428f, 57.169f, 20.472f, 57.158f, 20.493f, 57.169f

+path.cubicTo(SkBits2Float(0x41a3f1a9), SkBits2Float(0x42648c48), SkBits2Float(0x41a41eb7), SkBits2Float(0x42648105), SkBits2Float(0x41a449b9), SkBits2Float(0x426475c1));  // 20.493f, 57.137f, 20.515f, 57.126f, 20.536f, 57.115f

+path.cubicTo(SkBits2Float(0x41a48d4f), SkBits2Float(0x4263f1a8), SkBits2Float(0x41a46040), SkBits2Float(0x42634082), SkBits2Float(0x41a48d4f), SkBits2Float(0x4262bb63));  // 20.569f, 56.986f, 20.547f, 56.813f, 20.569f, 56.683f

+path.cubicTo(SkBits2Float(0x41a51061), SkBits2Float(0x426122d0), SkBits2Float(0x41a63126), SkBits2Float(0x425f51ea), SkBits2Float(0x41a82d0d), SkBits2Float(0x425e0624));  // 20.633f, 56.284f, 20.774f, 55.83f, 21.022f, 55.506f

+path.cubicTo(SkBits2Float(0x41a90a3c), SkBits2Float(0x425d820b), SkBits2Float(0x41aab01f), SkBits2Float(0x425cba5d), SkBits2Float(0x41ab0830), SkBits2Float(0x425c147a));  // 21.13f, 55.377f, 21.336f, 55.182f, 21.379f, 55.02f

+path.cubicTo(SkBits2Float(0x41aa147a), SkBits2Float(0x425bf3b5), SkBits2Float(0x41a8df3a), SkBits2Float(0x425c0936), SkBits2Float(0x41a7d4fd), SkBits2Float(0x425c147a));  // 21.26f, 54.988f, 21.109f, 55.009f, 20.979f, 55.02f

+path.cubicTo(SkBits2Float(0x41a74fde), SkBits2Float(0x425c147a), SkBits2Float(0x41a65e34), SkBits2Float(0x425c4082), SkBits2Float(0x41a5c28e), SkBits2Float(0x425c4082));  // 20.914f, 55.02f, 20.796f, 55.063f, 20.72f, 55.063f

+path.cubicTo(SkBits2Float(0x41a56a7e), SkBits2Float(0x425c353e), SkBits2Float(0x41a4fbe6), SkBits2Float(0x425c147a), SkBits2Float(0x41a4ced8), SkBits2Float(0x425c0936));  // 20.677f, 55.052f, 20.623f, 55.02f, 20.601f, 55.009f

+path.cubicTo(SkBits2Float(0x41a53d70), SkBits2Float(0x425af4bb), SkBits2Float(0x41a5ed90), SkBits2Float(0x425abd6f), SkBits2Float(0x41a85a1c), SkBits2Float(0x425aa6e8));  // 20.655f, 54.739f, 20.741f, 54.685f, 21.044f, 54.663f

+path.cubicTo(SkBits2Float(0x41a920c4), SkBits2Float(0x425a9cab), SkBits2Float(0x41a9d0e5), SkBits2Float(0x425aa6e8), SkBits2Float(0x41aa5603), SkBits2Float(0x425a9167));  // 21.141f, 54.653f, 21.227f, 54.663f, 21.292f, 54.642f

+path.cubicTo(SkBits2Float(0x41aa8311), SkBits2Float(0x425a8623), SkBits2Float(0x41aa9999), SkBits2Float(0x425a655f), SkBits2Float(0x41aab020), SkBits2Float(0x425a655f));  // 21.314f, 54.631f, 21.325f, 54.599f, 21.336f, 54.599f

+path.cubicTo(SkBits2Float(0x41aa3f7c), SkBits2Float(0x42599eb7), SkBits2Float(0x41a9a5e3), SkBits2Float(0x42591998), SkBits2Float(0x41a9374b), SkBits2Float(0x42586871));  // 21.281f, 54.405f, 21.206f, 54.275f, 21.152f, 54.102f

+path.cubicTo(SkBits2Float(0x41a8c8b3), SkBits2Float(0x4257e458), SkBits2Float(0x41a8b22c), SkBits2Float(0x42575f3a), SkBits2Float(0x41a85a1c), SkBits2Float(0x4256c49a));  // 21.098f, 53.973f, 21.087f, 53.843f, 21.044f, 53.692f

+path.cubicTo(SkBits2Float(0x41a76666), SkBits2Float(0x42551479), SkBits2Float(0x41a68937), SkBits2Float(0x4252cabf), SkBits2Float(0x41a74fdf), SkBits2Float(0x4250a1c9));  // 20.925f, 53.27f, 20.817f, 52.698f, 20.914f, 52.158f

+path.cubicTo(SkBits2Float(0x41a77ced), SkBits2Float(0x42500729), SkBits2Float(0x41a870a4), SkBits2Float(0x424e8417), SkBits2Float(0x41a8b22d), SkBits2Float(0x424e4ccb));  // 20.936f, 52.007f, 21.055f, 51.629f, 21.087f, 51.575f

+path.cubicTo(SkBits2Float(0x41a8b22d), SkBits2Float(0x424e4187), SkBits2Float(0x41aa147b), SkBits2Float(0x424cc9b9), SkBits2Float(0x41aab021), SkBits2Float(0x424c2f19));  // 21.087f, 51.564f, 21.26f, 51.197f, 21.336f, 51.046f

+path.cubicTo(SkBits2Float(0x41aac49c), SkBits2Float(0x424c1892), SkBits2Float(0x41ab49bb), SkBits2Float(0x424b9eb7), SkBits2Float(0x41ab8b44), SkBits2Float(0x424b676b));  // 21.346f, 51.024f, 21.411f, 50.905f, 21.443f, 50.851f

+path.cubicTo(SkBits2Float(0x41ac3d71), SkBits2Float(0x424ab644), SkBits2Float(0x41ad45a2), SkBits2Float(0x424a26e8), SkBits2Float(0x41ae22d1), SkBits2Float(0x42498105));  // 21.53f, 50.678f, 21.659f, 50.538f, 21.767f, 50.376f

+path.cubicTo(SkBits2Float(0x41ae6667), SkBits2Float(0x42496b84), SkBits2Float(0x41aeeb85), SkBits2Float(0x42491db1), SkBits2Float(0x41af0000), SkBits2Float(0x4248fbe6));  // 21.8f, 50.355f, 21.865f, 50.279f, 21.875f, 50.246f

+path.cubicTo(SkBits2Float(0x41b0624e), SkBits2Float(0x4248353e), SkBits2Float(0x41b1db23), SkBits2Float(0x424779da), SkBits2Float(0x41b353f8), SkBits2Float(0x4246bd6f));  // 22.048f, 50.052f, 22.232f, 49.869f, 22.416f, 49.685f

+path.cubicTo(SkBits2Float(0x41b3c083), SkBits2Float(0x42468623), SkBits2Float(0x41b445a2), SkBits2Float(0x42464ed7), SkBits2Float(0x41b4cac1), SkBits2Float(0x4246178c));  // 22.469f, 49.631f, 22.534f, 49.577f, 22.599f, 49.523f

+path.cubicTo(SkBits2Float(0x41b56667), SkBits2Float(0x4245c9b9), SkBits2Float(0x41b62d0f), SkBits2Float(0x4245872a), SkBits2Float(0x41b6c8b5), SkBits2Float(0x4245449a));  // 22.675f, 49.447f, 22.772f, 49.382f, 22.848f, 49.317f

+path.cubicTo(SkBits2Float(0x41b7624f), SkBits2Float(0x42450311), SkBits2Float(0x41b7e76d), SkBits2Float(0x4244a9fa), SkBits2Float(0x41b88313), SkBits2Float(0x42445d2d));  // 22.923f, 49.253f, 22.988f, 49.166f, 23.064f, 49.091f

+path.cubicTo(SkBits2Float(0x41b949bb), SkBits2Float(0x4243ee95), SkBits2Float(0x41ba1063), SkBits2Float(0x424374ba), SkBits2Float(0x41baed92), SkBits2Float(0x42431166));  // 23.161f, 48.983f, 23.258f, 48.864f, 23.366f, 48.767f

+path.cubicTo(SkBits2Float(0x41bb45a2), SkBits2Float(0x4242c393), SkBits2Float(0x41bbb43a), SkBits2Float(0x424276c6), SkBits2Float(0x41bc0e57), SkBits2Float(0x424228f3));  // 23.409f, 48.691f, 23.463f, 48.616f, 23.507f, 48.54f

+path.cubicTo(SkBits2Float(0x41bc6667), SkBits2Float(0x4241e664), SkBits2Float(0x41bc7ae2), SkBits2Float(0x4241a4da), SkBits2Float(0x41bcd2f3), SkBits2Float(0x4241624b));  // 23.55f, 48.475f, 23.56f, 48.411f, 23.603f, 48.346f

+path.cubicTo(SkBits2Float(0x41bd0001), SkBits2Float(0x42411478), SkBits2Float(0x41bd0001), SkBits2Float(0x4240c6a5), SkBits2Float(0x41bd1689), SkBits2Float(0x4240851c));  // 23.625f, 48.27f, 23.625f, 48.194f, 23.636f, 48.13f

+path.cubicTo(SkBits2Float(0x41bd2d10), SkBits2Float(0x42404cca), SkBits2Float(0x41bdb023), SkBits2Float(0x423fd3f5), SkBits2Float(0x41bd8521), SkBits2Float(0x423f7adf));  // 23.647f, 48.075f, 23.711f, 47.957f, 23.69f, 47.87f

+path.lineTo(SkBits2Float(0x41bd6e9a), SkBits2Float(0x423f7adf));  // 23.679f, 47.87f

+path.cubicTo(SkBits2Float(0x41bd6e9a), SkBits2Float(0x423f7adf), SkBits2Float(0x41bd5813), SkBits2Float(0x423f4ed7), SkBits2Float(0x41bd168a), SkBits2Float(0x423f4499));  // 23.679f, 47.87f, 23.668f, 47.827f, 23.636f, 47.817f

+path.cubicTo(SkBits2Float(0x41bc916b), SkBits2Float(0x423f22ce), SkBits2Float(0x41bc22d4), SkBits2Float(0x423f3955), SkBits2Float(0x41bb893a), SkBits2Float(0x423f178b));  // 23.571f, 47.784f, 23.517f, 47.806f, 23.442f, 47.773f

+path.cubicTo(SkBits2Float(0x41bb2f1d), SkBits2Float(0x423f0c47), SkBits2Float(0x41bb041b), SkBits2Float(0x423ee03f), SkBits2Float(0x41baac0b), SkBits2Float(0x423ec9b8));  // 23.398f, 47.762f, 23.377f, 47.719f, 23.334f, 47.697f

+path.cubicTo(SkBits2Float(0x41baac0b), SkBits2Float(0x423ebf7b), SkBits2Float(0x41bac086), SkBits2Float(0x423ea8f3), SkBits2Float(0x41bac086), SkBits2Float(0x423e926c));  // 23.334f, 47.687f, 23.344f, 47.665f, 23.344f, 47.643f

+path.cubicTo(SkBits2Float(0x41bb2f1e), SkBits2Float(0x423e882f), SkBits2Float(0x41bc0e59), SkBits2Float(0x423e6664), SkBits2Float(0x41bc916b), SkBits2Float(0x423e5c26));  // 23.398f, 47.633f, 23.507f, 47.6f, 23.571f, 47.59f

+path.cubicTo(SkBits2Float(0x41be4bc9), SkBits2Float(0x423e50e2), SkBits2Float(0x41c53542), SkBits2Float(0x423e926c), SkBits2Float(0x41c5ba61), SkBits2Float(0x423e24da));  // 23.787f, 47.579f, 24.651f, 47.643f, 24.716f, 47.536f

+path.cubicTo(SkBits2Float(0x41c61271), SkBits2Float(0x423de24b), SkBits2Float(0x41c61271), SkBits2Float(0x423d1a9d), SkBits2Float(0x41c63f80), SkBits2Float(0x423ca1c8));  // 24.759f, 47.471f, 24.759f, 47.276f, 24.781f, 47.158f

+path.cubicTo(SkBits2Float(0x41c68109), SkBits2Float(0x423bda1a), SkBits2Float(0x41c6ae18), SkBits2Float(0x423afceb), SkBits2Float(0x41c70628), SkBits2Float(0x423a2aff));  // 24.813f, 46.963f, 24.835f, 46.747f, 24.878f, 46.542f

+path.cubicTo(SkBits2Float(0x41c71caf), SkBits2Float(0x42399ba3), SkBits2Float(0x41c81065), SkBits2Float(0x42379eb5), SkBits2Float(0x41c79fc2), SkBits2Float(0x4237459f));  // 24.889f, 46.402f, 25.008f, 45.905f, 24.953f, 45.818f

+path.cubicTo(SkBits2Float(0x41c70628), SkBits2Float(0x4236e24b), SkBits2Float(0x41c4dd33), SkBits2Float(0x4237459f), SkBits2Float(0x41c45814), SkBits2Float(0x423750e3));  // 24.878f, 45.721f, 24.608f, 45.818f, 24.543f, 45.829f

+path.cubicTo(SkBits2Float(0x41c245a5), SkBits2Float(0x42379eb6), SkBits2Float(0x41bea5e7), SkBits2Float(0x42380d4d), SkBits2Float(0x41bbf5c6), SkBits2Float(0x4237ec89));  // 24.284f, 45.905f, 23.831f, 46.013f, 23.495f, 45.981f

+path.cubicTo(SkBits2Float(0x41b9f9df), SkBits2Float(0x4237e145), SkBits2Float(0x41b7e770), SkBits2Float(0x4237a9fa), SkBits2Float(0x41b62d12), SkBits2Float(0x4237676a));  // 23.247f, 45.97f, 22.988f, 45.916f, 22.772f, 45.851f

+path.cubicTo(SkBits2Float(0x41b4312b), SkBits2Float(0x423724db), SkBits2Float(0x41b1f1ae), SkBits2Float(0x42369fbc), SkBits2Float(0x41af9baa), SkBits2Float(0x423673b4));  // 22.524f, 45.786f, 22.243f, 45.656f, 21.951f, 45.613f

+path.cubicTo(SkBits2Float(0x41ae7ae5), SkBits2Float(0x42366977), SkBits2Float(0x41aced96), SkBits2Float(0x42365d2d), SkBits2Float(0x41ab8b48), SkBits2Float(0x42366977));  // 21.81f, 45.603f, 21.616f, 45.591f, 21.443f, 45.603f

+path.cubicTo(SkBits2Float(0x41a9e771), SkBits2Float(0x42368a3c), SkBits2Float(0x41a82d13), SkBits2Float(0x4236d708), SkBits2Float(0x41a65e3a), SkBits2Float(0x4236b644));  // 21.238f, 45.635f, 21.022f, 45.71f, 20.796f, 45.678f

+path.cubicTo(SkBits2Float(0x41a65e3a), SkBits2Float(0x4236ab00), SkBits2Float(0x41a647b3), SkBits2Float(0x42369fbd), SkBits2Float(0x41a65e3a), SkBits2Float(0x42369479));  // 20.796f, 45.667f, 20.785f, 45.656f, 20.796f, 45.645f

+path.cubicTo(SkBits2Float(0x41a672b5), SkBits2Float(0x42366977), SkBits2Float(0x41a7a7f4), SkBits2Float(0x42363125), SkBits2Float(0x41a81898), SkBits2Float(0x42361ba4));  // 20.806f, 45.603f, 20.957f, 45.548f, 21.012f, 45.527f

+path.cubicTo(SkBits2Float(0x41a85a21), SkBits2Float(0x42361060), SkBits2Float(0x41a8df40), SkBits2Float(0x4235d915), SkBits2Float(0x41a94dd7), SkBits2Float(0x4235cdd1));  // 21.044f, 45.516f, 21.109f, 45.462f, 21.163f, 45.451f

+path.cubicTo(SkBits2Float(0x41ab8b48), SkBits2Float(0x42356a7d), SkBits2Float(0x41af8523), SkBits2Float(0x423575c1), SkBits2Float(0x41b249be), SkBits2Float(0x42359685));  // 21.443f, 45.354f, 21.94f, 45.365f, 22.286f, 45.397f

+path.cubicTo(SkBits2Float(0x41b3d70e), SkBits2Float(0x4235a1c9), SkBits2Float(0x41b6168b), SkBits2Float(0x4235cdd1), SkBits2Float(0x41b7e770), SkBits2Float(0x4235ad0c));  // 22.48f, 45.408f, 22.761f, 45.451f, 22.988f, 45.419f

+path.cubicTo(SkBits2Float(0x41bac087), SkBits2Float(0x42359685), SkBits2Float(0x41bd6e9b), SkBits2Float(0x4234fbe5), SkBits2Float(0x41c03337), SkBits2Float(0x4234af18));  // 23.344f, 45.397f, 23.679f, 45.246f, 24.025f, 45.171f

+path.cubicTo(SkBits2Float(0x41c2cac4), SkBits2Float(0x42346145), SkBits2Float(0x41c56252), SkBits2Float(0x4234820a), SkBits2Float(0x41c81066), SkBits2Float(0x42346145));  // 24.349f, 45.095f, 24.673f, 45.127f, 25.008f, 45.095f

+path.cubicTo(SkBits2Float(0x41c824e1), SkBits2Float(0x42340935), SkBits2Float(0x41c89378), SkBits2Float(0x42330a3b), SkBits2Float(0x41c7b649), SkBits2Float(0x4232fef7));  // 25.018f, 45.009f, 25.072f, 44.76f, 24.964f, 44.749f

+path.cubicTo(SkBits2Float(0x41c6d91a), SkBits2Float(0x4232e976), SkBits2Float(0x41c5a3da), SkBits2Float(0x42338416), SkBits2Float(0x41c51ebc), SkBits2Float(0x4233a4da));  // 24.856f, 44.728f, 24.705f, 44.879f, 24.64f, 44.911f

+path.cubicTo(SkBits2Float(0x41c42b06), SkBits2Float(0x4233bb61), SkBits2Float(0x41c2cac4), SkBits2Float(0x4233d0e2), SkBits2Float(0x41c1d70e), SkBits2Float(0x4233e769));  // 24.521f, 44.933f, 24.349f, 44.954f, 24.23f, 44.976f

+path.cubicTo(SkBits2Float(0x41c08b47), SkBits2Float(0x4233f2ad), SkBits2Float(0x41bf1272), SkBits2Float(0x4233c6a4), SkBits2Float(0x41bdf3ba), SkBits2Float(0x4233bb61));  // 24.068f, 44.987f, 23.884f, 44.944f, 23.744f, 44.933f

+path.cubicTo(SkBits2Float(0x41bcd2f5), SkBits2Float(0x4233b01d), SkBits2Float(0x41bbf5c6), SkBits2Float(0x4233b01d), SkBits2Float(0x41baed95), SkBits2Float(0x4233a4da));  // 23.603f, 44.922f, 23.495f, 44.922f, 23.366f, 44.911f

+path.cubicTo(SkBits2Float(0x41ba26ed), SkBits2Float(0x42338f59), SkBits2Float(0x41b91cb0), SkBits2Float(0x4233580d), SkBits2Float(0x41b83f81), SkBits2Float(0x4233580d));  // 23.269f, 44.89f, 23.139f, 44.836f, 23.031f, 44.836f

+path.cubicTo(SkBits2Float(0x41b4b43d), SkBits2Float(0x42333642), SkBits2Float(0x41b19791), SkBits2Float(0x4233a4da), SkBits2Float(0x41aea7f4), SkBits2Float(0x4233d0e2));  // 22.588f, 44.803f, 22.199f, 44.911f, 21.832f, 44.954f

+path.cubicTo(SkBits2Float(0x41aba1cf), SkBits2Float(0x42340934), SkBits2Float(0x41a7666b), SkBits2Float(0x4233e769), SkBits2Float(0x41a4b856), SkBits2Float(0x42338415));  // 21.454f, 45.009f, 20.925f, 44.976f, 20.59f, 44.879f

+path.cubicTo(SkBits2Float(0x41a46046), SkBits2Float(0x423378d1), SkBits2Float(0x41a3f1ae), SkBits2Float(0x4233580d), SkBits2Float(0x41a3c6ac), SkBits2Float(0x42334cc9));  // 20.547f, 44.868f, 20.493f, 44.836f, 20.472f, 44.825f

+path.cubicTo(SkBits2Float(0x41a28f60), SkBits2Float(0x4233157d), SkBits2Float(0x41a19db6), SkBits2Float(0x42330a3a), SkBits2Float(0x41a0c087), SkBits2Float(0x4232c7aa));  // 20.32f, 44.771f, 20.202f, 44.76f, 20.094f, 44.695f

+path.cubicTo(SkBits2Float(0x41a0eb89), SkBits2Float(0x4232bc66), SkBits2Float(0x41a0eb89), SkBits2Float(0x4232905e), SkBits2Float(0x41a10210), SkBits2Float(0x4232905e));  // 20.115f, 44.684f, 20.115f, 44.641f, 20.126f, 44.641f

+path.cubicTo(SkBits2Float(0x41a19db6), SkBits2Float(0x42325912), SkBits2Float(0x41a2645e), SkBits2Float(0x42326f99), SkBits2Float(0x41a35608), SkBits2Float(0x42326f99));  // 20.202f, 44.587f, 20.299f, 44.609f, 20.417f, 44.609f

+path.cubicTo(SkBits2Float(0x41a476cd), SkBits2Float(0x42324ed4), SkBits2Float(0x41a5ed95), SkBits2Float(0x4232384d), SkBits2Float(0x41a724e1), SkBits2Float(0x42320c45));  // 20.558f, 44.577f, 20.741f, 44.555f, 20.893f, 44.512f

+path.cubicTo(SkBits2Float(0x41a8c8b8), SkBits2Float(0x4231c9b6), SkBits2Float(0x41aa999d), SkBits2Float(0x42316662), SkBits2Float(0x41ac26ed), SkBits2Float(0x4231188f));  // 21.098f, 44.447f, 21.325f, 44.35f, 21.519f, 44.274f

+path.cubicTo(SkBits2Float(0x41af168b), SkBits2Float(0x423072ac), SkBits2Float(0x41b249be), SkBits2Float(0x42300f58), SkBits2Float(0x41b57ae5), SkBits2Float(0x422fe249));  // 21.886f, 44.112f, 22.286f, 44.015f, 22.685f, 43.971f

+path.cubicTo(SkBits2Float(0x41b66e9b), SkBits2Float(0x422fd80c), SkBits2Float(0x41b7d0e9), SkBits2Float(0x422fee93), SkBits2Float(0x41b89791), SkBits2Float(0x422fee93));  // 22.804f, 43.961f, 22.977f, 43.983f, 23.074f, 43.983f

+path.cubicTo(SkBits2Float(0x41bb1897), SkBits2Float(0x42300f58), SkBits2Float(0x41bd2d12), SkBits2Float(0x423024d9), SkBits2Float(0x41bfc49f), SkBits2Float(0x4230301c));  // 23.387f, 44.015f, 23.647f, 44.036f, 23.971f, 44.047f

+path.cubicTo(SkBits2Float(0x41c0e357), SkBits2Float(0x423046a3), SkBits2Float(0x41c245a5), SkBits2Float(0x42305c24), SkBits2Float(0x41c3a7f3), SkBits2Float(0x423051e7));  // 24.111f, 44.069f, 24.284f, 44.09f, 24.457f, 44.08f

+path.cubicTo(SkBits2Float(0x41c50835), SkBits2Float(0x423046a3), SkBits2Float(0x41c69791), SkBits2Float(0x42300f58), SkBits2Float(0x41c79fc2), SkBits2Float(0x422fb641));  // 24.629f, 44.069f, 24.824f, 44.015f, 24.953f, 43.928f

+path.cubicTo(SkBits2Float(0x41c7f9df), SkBits2Float(0x422fa0c0), SkBits2Float(0x41c86876), SkBits2Float(0x422f5e31), SkBits2Float(0x41c8eb89), SkBits2Float(0x422f52ed));  // 24.997f, 43.907f, 25.051f, 43.842f, 25.115f, 43.831f

+path.cubicTo(SkBits2Float(0x41c9b43d), SkBits2Float(0x422f3c66), SkBits2Float(0x41c9df3f), SkBits2Float(0x422fb641), SkBits2Float(0x41c9f5c6), SkBits2Float(0x42300f57));  // 25.213f, 43.809f, 25.234f, 43.928f, 25.245f, 44.015f

+path.cubicTo(SkBits2Float(0x41ca0c4d), SkBits2Float(0x4230e143), SkBits2Float(0x41c9df3f), SkBits2Float(0x42319ca7), SkBits2Float(0x41c9f5c6), SkBits2Float(0x4232384d));  // 25.256f, 44.22f, 25.234f, 44.403f, 25.245f, 44.555f

+path.cubicTo(SkBits2Float(0x41ca395c), SkBits2Float(0x4234fbe2), SkBits2Float(0x41ca22d4), SkBits2Float(0x4237cabc), SkBits2Float(0x41ca7ae5), SkBits2Float(0x423a6d8c));  // 25.278f, 45.246f, 25.267f, 45.948f, 25.31f, 46.607f

+path.cubicTo(SkBits2Float(0x41ca916c), SkBits2Float(0x423b3f78), SkBits2Float(0x41ca645e), SkBits2Float(0x423ca1c5), SkBits2Float(0x41ca916c), SkBits2Float(0x423d9475));  // 25.321f, 46.812f, 25.299f, 47.158f, 25.321f, 47.395f

+path.cubicTo(SkBits2Float(0x41ca916c), SkBits2Float(0x423daafc), SkBits2Float(0x41ca7ae5), SkBits2Float(0x423dd704), SkBits2Float(0x41ca916c), SkBits2Float(0x423dec85));  // 25.321f, 47.417f, 25.31f, 47.46f, 25.321f, 47.481f

+path.cubicTo(SkBits2Float(0x41caa5e7), SkBits2Float(0x423e0e50), SkBits2Float(0x41cb0004), SkBits2Float(0x423e459c), SkBits2Float(0x41cb2b06), SkBits2Float(0x423e50df));  // 25.331f, 47.514f, 25.375f, 47.568f, 25.396f, 47.579f

+path.cubicTo(SkBits2Float(0x41cb6e9c), SkBits2Float(0x423e5c23), SkBits2Float(0x41ce47b2), SkBits2Float(0x423e7ce7), SkBits2Float(0x41ce8b48), SkBits2Float(0x423e6660));  // 25.429f, 47.59f, 25.785f, 47.622f, 25.818f, 47.6f

+path.lineTo(SkBits2Float(0x41ceb64a), SkBits2Float(0x423e5c23));  // 25.839f, 47.59f

+path.cubicTo(SkBits2Float(0x41d1395c), SkBits2Float(0x423e5c23), SkBits2Float(0x41d41273), SkBits2Float(0x423e50df), SkBits2Float(0x41d6666b), SkBits2Float(0x423e6660));  // 26.153f, 47.59f, 26.509f, 47.579f, 26.8f, 47.6f

+path.cubicTo(SkBits2Float(0x41d71898), SkBits2Float(0x423e7ce7), SkBits2Float(0x41d80a42), SkBits2Float(0x423e5c23), SkBits2Float(0x41d8a5e8), SkBits2Float(0x423e7ce7));  // 26.887f, 47.622f, 27.005f, 47.59f, 27.081f, 47.622f

+path.cubicTo(SkBits2Float(0x41d8d2f6), SkBits2Float(0x423e882b), SkBits2Float(0x41d8d2f6), SkBits2Float(0x423e9268), SkBits2Float(0x41d8fdf8), SkBits2Float(0x423e9eb2));  // 27.103f, 47.633f, 27.103f, 47.643f, 27.124f, 47.655f

+path.cubicTo(SkBits2Float(0x41d8e771), SkBits2Float(0x423ebf77), SkBits2Float(0x41d8fdf8), SkBits2Float(0x423ed4f8), SkBits2Float(0x41d8e771), SkBits2Float(0x423eeb7f));  // 27.113f, 47.687f, 27.124f, 47.708f, 27.113f, 47.73f

+path.cubicTo(SkBits2Float(0x41d88f61), SkBits2Float(0x423f4496), SkBits2Float(0x41d71898), SkBits2Float(0x423f4496), SkBits2Float(0x41d6aa00), SkBits2Float(0x423f9162));  // 27.07f, 47.817f, 26.887f, 47.817f, 26.833f, 47.892f

+path.cubicTo(SkBits2Float(0x41d547b2), SkBits2Float(0x42406e91), SkBits2Float(0x41d43d75), SkBits2Float(0x4241ba58), SkBits2Float(0x41d38d54), SkBits2Float(0x4242b952));  // 26.66f, 48.108f, 26.53f, 48.432f, 26.444f, 48.681f

+path.cubicTo(SkBits2Float(0x41d1395c), SkBits2Float(0x4245a8f0), SkBits2Float(0x41d0b231), SkBits2Float(0x42491dac), SkBits2Float(0x41d2147f), SkBits2Float(0x424c2f15));  // 26.153f, 49.415f, 26.087f, 50.279f, 26.26f, 51.046f

+path.cubicTo(SkBits2Float(0x41d2418d), SkBits2Float(0x424c7be2), SkBits2Float(0x41d2999e), SkBits2Float(0x424cc9b5), SkBits2Float(0x41d2b025), SkBits2Float(0x424d0c44));  // 26.282f, 51.121f, 26.325f, 51.197f, 26.336f, 51.262f

+path.cubicTo(SkBits2Float(0x41d33544), SkBits2Float(0x424dc7a8), SkBits2Float(0x41d3a3db), SkBits2Float(0x424e8413), SkBits2Float(0x41d453fc), SkBits2Float(0x424f136f));  // 26.401f, 51.445f, 26.455f, 51.629f, 26.541f, 51.769f

+path.cubicTo(SkBits2Float(0x41d453fc), SkBits2Float(0x424f136f), SkBits2Float(0x41d59fc3), SkBits2Float(0x42506a79), SkBits2Float(0x41d6c087), SkBits2Float(0x4250e454));  // 26.541f, 51.769f, 26.703f, 52.104f, 26.844f, 52.223f

+path.cubicTo(SkBits2Float(0x41d6c087), SkBits2Float(0x4250ef98), SkBits2Float(0x41d6eb89), SkBits2Float(0x4251105c), SkBits2Float(0x41d70210), SkBits2Float(0x4251105c));  // 26.844f, 52.234f, 26.865f, 52.266f, 26.876f, 52.266f

+path.cubicTo(SkBits2Float(0x41d71897), SkBits2Float(0x42511ba0), SkBits2Float(0x41d75a20), SkBits2Float(0x4251105c), SkBits2Float(0x41d7872f), SkBits2Float(0x4251105c));  // 26.887f, 52.277f, 26.919f, 52.266f, 26.941f, 52.266f

+path.cubicTo(SkBits2Float(0x41d87ae5), SkBits2Float(0x42501ca6), SkBits2Float(0x41d9147f), SkBits2Float(0x424f136e), SkBits2Float(0x41da0835), SkBits2Float(0x424e157b));  // 27.06f, 52.028f, 27.135f, 51.769f, 27.254f, 51.521f

+path.cubicTo(SkBits2Float(0x41da1ebc), SkBits2Float(0x424df4b6), SkBits2Float(0x41db1066), SkBits2Float(0x424d0c44), SkBits2Float(0x41db1066), SkBits2Float(0x424d0100));  // 27.265f, 51.489f, 27.383f, 51.262f, 27.383f, 51.251f

+path.cubicTo(SkBits2Float(0x41db3d74), SkBits2Float(0x424cc9b4), SkBits2Float(0x41db9585), SkBits2Float(0x424c8725), SkBits2Float(0x41dbd91a), SkBits2Float(0x424c5b1d));  // 27.405f, 51.197f, 27.448f, 51.132f, 27.481f, 51.089f

+path.cubicTo(SkBits2Float(0x41dc5e39), SkBits2Float(0x424bcbc1), SkBits2Float(0x41dcf7d2), SkBits2Float(0x424b301b), SkBits2Float(0x41dd7cf1), SkBits2Float(0x424aac02));  // 27.546f, 50.949f, 27.621f, 50.797f, 27.686f, 50.668f

+path.cubicTo(SkBits2Float(0x41ddd501), SkBits2Float(0x424a5e2f), SkBits2Float(0x41ddeb89), SkBits2Float(0x424a105c), SkBits2Float(0x41de4399), SkBits2Float(0x4249b84c));  // 27.729f, 50.592f, 27.74f, 50.516f, 27.783f, 50.43f

+path.cubicTo(SkBits2Float(0x41de70a7), SkBits2Float(0x4249a1c5), SkBits2Float(0x41def5c6), SkBits2Float(0x42490725), SkBits2Float(0x41df20c8), SkBits2Float(0x4248e660));  // 27.805f, 50.408f, 27.87f, 50.257f, 27.891f, 50.225f

+path.cubicTo(SkBits2Float(0x41df8f60), SkBits2Float(0x42488206), SkBits2Float(0x41e0c49f), SkBits2Float(0x42474cc6), SkBits2Float(0x41e10835), SkBits2Float(0x42472c02));  // 27.945f, 50.127f, 28.096f, 49.825f, 28.129f, 49.793f

+path.cubicTo(SkBits2Float(0x41e11ebc), SkBits2Float(0x42472c02), SkBits2Float(0x41e13337), SkBits2Float(0x4246fef4), SkBits2Float(0x41e13337), SkBits2Float(0x4246f4b6));  // 28.14f, 49.793f, 28.15f, 49.749f, 28.15f, 49.739f

+path.cubicTo(SkBits2Float(0x41e149be), SkBits2Float(0x4246c7a8), SkBits2Float(0x41e226ed), SkBits2Float(0x42461787), SkBits2Float(0x41e253fc), SkBits2Float(0x4245df35));  // 28.161f, 49.695f, 28.269f, 49.523f, 28.291f, 49.468f

+path.cubicTo(SkBits2Float(0x41e27efe), SkBits2Float(0x4245d3f1), SkBits2Float(0x41e2ac0c), SkBits2Float(0x42459ca6), SkBits2Float(0x41e2ac0c), SkBits2Float(0x42459162));  // 28.312f, 49.457f, 28.334f, 49.403f, 28.334f, 49.392f

+path.cubicTo(SkBits2Float(0x41e372b4), SkBits2Float(0x4244e141), SkBits2Float(0x41e4666a), SkBits2Float(0x42445c23), SkBits2Float(0x41e4eb89), SkBits2Float(0x42437ef3));  // 28.431f, 49.22f, 28.55f, 49.09f, 28.615f, 48.874f

+path.cubicTo(SkBits2Float(0x41e4a7f3), SkBits2Float(0x424373af), SkBits2Float(0x41e47ae5), SkBits2Float(0x42435e2e), SkBits2Float(0x41e4666a), SkBits2Float(0x42435e2e));  // 28.582f, 48.863f, 28.56f, 48.842f, 28.55f, 48.842f

+path.cubicTo(SkBits2Float(0x41e3893b), SkBits2Float(0x42433c63), SkBits2Float(0x41e1fbeb), SkBits2Float(0x4243686b), SkBits2Float(0x41e18b47), SkBits2Float(0x42431b9f));  // 28.442f, 48.809f, 28.248f, 48.852f, 28.193f, 48.777f

+path.cubicTo(SkBits2Float(0x41e16045), SkBits2Float(0x4242f9d4), SkBits2Float(0x41e18b47), SkBits2Float(0x4242ee91), SkBits2Float(0x41e16045), SkBits2Float(0x4242d910));  // 28.172f, 48.744f, 28.193f, 48.733f, 28.172f, 48.712f

+path.cubicTo(SkBits2Float(0x41e1a1ce), SkBits2Float(0x4242b84b), SkBits2Float(0x41e1fbeb), SkBits2Float(0x42429681), SkBits2Float(0x41e226ed), SkBits2Float(0x42429681));  // 28.204f, 48.68f, 28.248f, 48.647f, 28.269f, 48.647f

+path.cubicTo(SkBits2Float(0x41e3cac4), SkBits2Float(0x42425f35), SkBits2Float(0x41e9c087), SkBits2Float(0x4242b84c), SkBits2Float(0x41ea5c2c), SkBits2Float(0x424248ae));  // 28.474f, 48.593f, 29.219f, 48.68f, 29.295f, 48.571f

+path.cubicTo(SkBits2Float(0x41eacac4), SkBits2Float(0x4241fbe1), SkBits2Float(0x41eacac4), SkBits2Float(0x42414aba), SkBits2Float(0x41eaf7d2), SkBits2Float(0x4240d0df));  // 29.349f, 48.496f, 29.349f, 48.323f, 29.371f, 48.204f

+path.cubicTo(SkBits2Float(0x41eb395b), SkBits2Float(0x4240580a), SkBits2Float(0x41eba7f3), SkBits2Float(0x423fb121), SkBits2Float(0x41ebd501), SkBits2Float(0x423f21c4));  // 29.403f, 48.086f, 29.457f, 47.923f, 29.479f, 47.783f

+path.cubicTo(SkBits2Float(0x41ec2d11), SkBits2Float(0x423e4fd8), SkBits2Float(0x41ec5813), SkBits2Float(0x423d936e), SkBits2Float(0x41ecb230), SkBits2Float(0x423cb63f));  // 29.522f, 47.578f, 29.543f, 47.394f, 29.587f, 47.178f

+path.cubicTo(SkBits2Float(0x41ecc8b7), SkBits2Float(0x423c5e2f), SkBits2Float(0x41edba61), SkBits2Float(0x423b332d), SkBits2Float(0x41ed8f5f), SkBits2Float(0x423ac495));  // 29.598f, 47.092f, 29.716f, 46.8f, 29.695f, 46.692f

+path.cubicTo(SkBits2Float(0x41ed6251), SkBits2Float(0x423a8d49), SkBits2Float(0x41ec9ba9), SkBits2Float(0x423a407c), SkBits2Float(0x41ec2d11), SkBits2Float(0x423a3539));  // 29.673f, 46.638f, 29.576f, 46.563f, 29.522f, 46.552f

+path.cubicTo(SkBits2Float(0x41ec0003), SkBits2Float(0x423a29f5), SkBits2Float(0x41ebeb88), SkBits2Float(0x423a3539), SkBits2Float(0x41ebd501), SkBits2Float(0x423a3539));  // 29.5f, 46.541f, 29.49f, 46.552f, 29.479f, 46.552f

+path.cubicTo(SkBits2Float(0x41eb6669), SkBits2Float(0x423a29f5), SkBits2Float(0x41ea72b3), SkBits2Float(0x4239f2aa), SkBits2Float(0x41e9c086), SkBits2Float(0x423a0931));  // 29.425f, 46.541f, 29.306f, 46.487f, 29.219f, 46.509f

+path.cubicTo(SkBits2Float(0x41e99584), SkBits2Float(0x423a0931), SkBits2Float(0x41e96876), SkBits2Float(0x423a29f6), SkBits2Float(0x41e953fb), SkBits2Float(0x423a3539));  // 29.198f, 46.509f, 29.176f, 46.541f, 29.166f, 46.552f

+path.cubicTo(SkBits2Float(0x41e96876), SkBits2Float(0x423a8d49), SkBits2Float(0x41e9c086), SkBits2Float(0x423acfd9), SkBits2Float(0x41e9d70d), SkBits2Float(0x423b28ef));  // 29.176f, 46.638f, 29.219f, 46.703f, 29.23f, 46.79f

+path.cubicTo(SkBits2Float(0x41ea041b), SkBits2Float(0x423bd910), SkBits2Float(0x41e8fbea), SkBits2Float(0x423c73b0), SkBits2Float(0x41e849bd), SkBits2Float(0x423cac01));  // 29.252f, 46.962f, 29.123f, 47.113f, 29.036f, 47.168f

+path.cubicTo(SkBits2Float(0x41e75607), SkBits2Float(0x423cf8ce), SkBits2Float(0x41e5f3b9), SkBits2Float(0x423ced8a), SkBits2Float(0x41e4eb88), SkBits2Float(0x423cd809));  // 28.917f, 47.243f, 28.744f, 47.232f, 28.615f, 47.211f

+path.cubicTo(SkBits2Float(0x41e372b3), SkBits2Float(0x423cb63e), SkBits2Float(0x41e2ac0b), SkBits2Float(0x423c0517), SkBits2Float(0x41e10834), SkBits2Float(0x423c52ea));  // 28.431f, 47.178f, 28.334f, 47.005f, 28.129f, 47.081f

+path.cubicTo(SkBits2Float(0x41e0db26), SkBits2Float(0x423cd809), SkBits2Float(0x41e0999c), SkBits2Float(0x423d46a0), SkBits2Float(0x41dfd0e8), SkBits2Float(0x423d72a8));  // 28.107f, 47.211f, 28.075f, 47.319f, 27.977f, 47.362f

+path.cubicTo(SkBits2Float(0x41deb230), SkBits2Float(0x423dcab8), SkBits2Float(0x41dd3b67), SkBits2Float(0x423d8829), SkBits2Float(0x41dc312a), SkBits2Float(0x423d46a0));  // 27.837f, 47.448f, 27.654f, 47.383f, 27.524f, 47.319f

+path.cubicTo(SkBits2Float(0x41dae563), SkBits2Float(0x423cf8cd), SkBits2Float(0x41d98316), SkBits2Float(0x423cccc5), SkBits2Float(0x41d8645d), SkBits2Float(0x423c6971));  // 27.362f, 47.243f, 27.189f, 47.2f, 27.049f, 47.103f

+path.cubicTo(SkBits2Float(0x41d7df3e), SkBits2Float(0x423c52ea), SkBits2Float(0x41d72d11), SkBits2Float(0x423c311f), SkBits2Float(0x41d6a9ff), SkBits2Float(0x423c0517));  // 26.984f, 47.081f, 26.897f, 47.048f, 26.833f, 47.005f

+path.cubicTo(SkBits2Float(0x41d67cf1), SkBits2Float(0x423bfada), SkBits2Float(0x41d572b3), SkBits2Float(0x423b967f), SkBits2Float(0x41d5893a), SkBits2Float(0x423b967f));  // 26.811f, 46.995f, 26.681f, 46.897f, 26.692f, 46.897f

+path.cubicTo(SkBits2Float(0x41d5893a), SkBits2Float(0x423b967f), SkBits2Float(0x41d5b648), SkBits2Float(0x423b6a77), SkBits2Float(0x41d5ccd0), SkBits2Float(0x423b6a77));  // 26.692f, 46.897f, 26.714f, 46.854f, 26.725f, 46.854f

+path.cubicTo(SkBits2Float(0x41d6eb88), SkBits2Float(0x423b3e6f), SkBits2Float(0x41d8374f), SkBits2Float(0x423b967f), SkBits2Float(0x41d8fdf7), SkBits2Float(0x423bad06));  // 26.865f, 46.811f, 27.027f, 46.897f, 27.124f, 46.919f

+path.cubicTo(SkBits2Float(0x41d9c6ab), SkBits2Float(0x423bb84a), SkBits2Float(0x41da49be), SkBits2Float(0x423bb84a), SkBits2Float(0x41db1066), SkBits2Float(0x423bd90e));  // 27.222f, 46.93f, 27.286f, 46.93f, 27.383f, 46.962f

+path.cubicTo(SkBits2Float(0x41db810a), SkBits2Float(0x423bd90e), SkBits2Float(0x41dc5e39), SkBits2Float(0x423bfad9), SkBits2Float(0x41dcf7d3), SkBits2Float(0x423bef95));  // 27.438f, 46.962f, 27.546f, 46.995f, 27.621f, 46.984f

+path.cubicTo(SkBits2Float(0x41ddd502), SkBits2Float(0x423bc38d), SkBits2Float(0x41dd4fe3), SkBits2Float(0x423b332b), SkBits2Float(0x41dd7cf2), SkBits2Float(0x423ab94f));  // 27.729f, 46.941f, 27.664f, 46.8f, 27.686f, 46.681f

+path.cubicTo(SkBits2Float(0x41dda7f4), SkBits2Float(0x423a77c6), SkBits2Float(0x41de2d13), SkBits2Float(0x423a29f3), SkBits2Float(0x41de70a8), SkBits2Float(0x423a136c));  // 27.707f, 46.617f, 27.772f, 46.541f, 27.805f, 46.519f

+path.cubicTo(SkBits2Float(0x41dfba62), SkBits2Float(0x4239c69f), SkBits2Float(0x41e253fc), SkBits2Float(0x423a092f), SkBits2Float(0x41e372b4), SkBits2Float(0x423a4bbe));  // 27.966f, 46.444f, 28.291f, 46.509f, 28.431f, 46.574f

+path.cubicTo(SkBits2Float(0x41e40e5a), SkBits2Float(0x423a6c83), SkBits2Float(0x41e49379), SkBits2Float(0x423a8d47), SkBits2Float(0x41e55a21), SkBits2Float(0x423ab94f));  // 28.507f, 46.606f, 28.572f, 46.638f, 28.669f, 46.681f

+path.lineTo(SkBits2Float(0x41e58523), SkBits2Float(0x423acfd6));  // 28.69f, 46.703f

+path.cubicTo(SkBits2Float(0x41e5b231), SkBits2Float(0x423acfd6), SkBits2Float(0x41e60a42), SkBits2Float(0x423ac492), SkBits2Float(0x41e66252), SkBits2Float(0x423acfd6));  // 28.712f, 46.703f, 28.755f, 46.692f, 28.798f, 46.703f

+path.cubicTo(SkBits2Float(0x41e66252), SkBits2Float(0x423ab94f), SkBits2Float(0x41e68f60), SkBits2Float(0x423ab94f), SkBits2Float(0x41e6a5e8), SkBits2Float(0x423aae0b));  // 28.798f, 46.681f, 28.82f, 46.681f, 28.831f, 46.67f

+path.cubicTo(SkBits2Float(0x41e6fdf8), SkBits2Float(0x423a136b), SkBits2Float(0x41e5dd34), SkBits2Float(0x423978cc), SkBits2Float(0x41e68f61), SkBits2Float(0x4238fef0));  // 28.874f, 46.519f, 28.733f, 46.368f, 28.82f, 46.249f

+path.cubicTo(SkBits2Float(0x41e72b07), SkBits2Float(0x42389058), SkBits2Float(0x41eaf7d4), SkBits2Float(0x42391577), SkBits2Float(0x41ec5815), SkBits2Float(0x4238f3ac));  // 28.896f, 46.141f, 29.371f, 46.271f, 29.543f, 46.238f

+path.cubicTo(SkBits2Float(0x41ef1cb1), SkBits2Float(0x4238bd66), SkBits2Float(0x41ed6252), SkBits2Float(0x4237d4f4), SkBits2Float(0x41ede771), SkBits2Float(0x42369eae));  // 29.889f, 46.185f, 29.673f, 45.958f, 29.738f, 45.655f

+path.cubicTo(SkBits2Float(0x41ee28fa), SkBits2Float(0x423651e1), SkBits2Float(0x41ee8317), SkBits2Float(0x42366868), SkBits2Float(0x41eedb27), SkBits2Float(0x42365c1f));  // 29.77f, 45.58f, 29.814f, 45.602f, 29.857f, 45.59f

+path.cubicTo(SkBits2Float(0x41ef0629), SkBits2Float(0x4236a9f2), SkBits2Float(0x41ef3337), SkBits2Float(0x42371889), SkBits2Float(0x41ef3337), SkBits2Float(0x42375b19));  // 29.878f, 45.666f, 29.9f, 45.774f, 29.9f, 45.839f

+path.cubicTo(SkBits2Float(0x41ef49be), SkBits2Float(0x4237e038), SkBits2Float(0x41ef3337), SkBits2Float(0x42386450), SkBits2Float(0x41ef49be), SkBits2Float(0x4238d2e8));  // 29.911f, 45.969f, 29.9f, 46.098f, 29.911f, 46.206f

+path.cubicTo(SkBits2Float(0x41ef8b47), SkBits2Float(0x42394cc3), SkBits2Float(0x41eff9df), SkBits2Float(0x4239e763), SkBits2Float(0x41f026ed), SkBits2Float(0x423a613e));  // 29.943f, 46.325f, 29.997f, 46.476f, 30.019f, 46.595f

+path.cubicTo(SkBits2Float(0x41f0ac0c), SkBits2Float(0x423b967d), SkBits2Float(0x41f11897), SkBits2Float(0x423ca0bb), SkBits2Float(0x41f1893b), SkBits2Float(0x423dd5fa));  // 30.084f, 46.897f, 30.137f, 47.157f, 30.192f, 47.459f

+path.cubicTo(SkBits2Float(0x41f19db6), SkBits2Float(0x423e1889), SkBits2Float(0x41f1f5c6), SkBits2Float(0x423e7bdd), SkBits2Float(0x41f20e5a), SkBits2Float(0x423ebe6d));  // 30.202f, 47.524f, 30.245f, 47.621f, 30.257f, 47.686f

+path.cubicTo(SkBits2Float(0x41f27ae5), SkBits2Float(0x423f9059), SkBits2Float(0x41f2be7b), SkBits2Float(0x42406d88), SkBits2Float(0x41f3168b), SkBits2Float(0x424128ec));  // 30.31f, 47.891f, 30.343f, 48.107f, 30.386f, 48.29f

+path.cubicTo(SkBits2Float(0x41f35814), SkBits2Float(0x42418203), SkBits2Float(0x41f35814), SkBits2Float(0x4241e556), SkBits2Float(0x41f38523), SkBits2Float(0x42423329));  // 30.418f, 48.377f, 30.418f, 48.474f, 30.44f, 48.55f

+path.cubicTo(SkBits2Float(0x41f3b025), SkBits2Float(0x424248aa), SkBits2Float(0x41f420c9), SkBits2Float(0x424275b8), SkBits2Float(0x41f46252), SkBits2Float(0x424280fc));  // 30.461f, 48.571f, 30.516f, 48.615f, 30.548f, 48.626f

+path.cubicTo(SkBits2Float(0x41f4fdf8), SkBits2Float(0x4242967d), SkBits2Float(0x41f5db27), SkBits2Float(0x424275b8), SkBits2Float(0x41f674c1), SkBits2Float(0x424280fc));  // 30.624f, 48.647f, 30.732f, 48.615f, 30.807f, 48.626f

+path.cubicTo(SkBits2Float(0x41f8f5c7), SkBits2Float(0x4242967d), SkBits2Float(0x41fc5609), SkBits2Float(0x424280fc), SkBits2Float(0x41feeb8a), SkBits2Float(0x4242a1c1));  // 31.12f, 48.647f, 31.542f, 48.626f, 31.865f, 48.658f

+path.cubicTo(SkBits2Float(0x41ff45a7), SkBits2Float(0x4242a1c1), SkBits2Float(0x41ffdf40), SkBits2Float(0x424280fc), SkBits2Float(0x4200322f), SkBits2Float(0x4242a1c1));  // 31.909f, 48.658f, 31.984f, 48.626f, 32.049f, 48.658f

+path.cubicTo(SkBits2Float(0x420048b6), SkBits2Float(0x4242a1c1), SkBits2Float(0x42005e37), SkBits2Float(0x4242c286), SkBits2Float(0x420074be), SkBits2Float(0x4242d90d));  // 32.071f, 48.658f, 32.092f, 48.69f, 32.114f, 48.712f

+path.cubicTo(SkBits2Float(0x420074be), SkBits2Float(0x4242ee8e), SkBits2Float(0x42008002), SkBits2Float(0x42431b9c), SkBits2Float(0x420074be), SkBits2Float(0x4243311d));  // 32.114f, 48.733f, 32.125f, 48.777f, 32.114f, 48.798f

+path.lineTo(SkBits2Float(0x420052f3), SkBits2Float(0x42433c61));  // 32.081f, 48.809f

+path.cubicTo(SkBits2Float(0x42001cad), SkBits2Float(0x42439fb5), SkBits2Float(0x41ff2f1d), SkBits2Float(0x42436869), SkBits2Float(0x41fe7cf0), SkBits2Float(0x4243aaf9));  // 32.028f, 48.906f, 31.898f, 48.852f, 31.811f, 48.917f

+path.cubicTo(SkBits2Float(0x41fe24e0), SkBits2Float(0x4243cbbe), SkBits2Float(0x41fd3336), SkBits2Float(0x4244cab7), SkBits2Float(0x41fd0627), SkBits2Float(0x42450203));  // 31.768f, 48.949f, 31.65f, 49.198f, 31.628f, 49.252f

+path.cubicTo(SkBits2Float(0x41fcc291), SkBits2Float(0x4245438c), SkBits2Float(0x41fcc291), SkBits2Float(0x42457bde), SkBits2Float(0x41fcae17), SkBits2Float(0x4245be6d));  // 31.595f, 49.316f, 31.595f, 49.371f, 31.585f, 49.436f

+path.cubicTo(SkBits2Float(0x41fc9790), SkBits2Float(0x4245fff6), SkBits2Float(0x41fc28f8), SkBits2Float(0x4246634a), SkBits2Float(0x41fc1271), SkBits2Float(0x4246b11d));  // 31.574f, 49.5f, 31.52f, 49.597f, 31.509f, 49.673f

+path.cubicTo(SkBits2Float(0x41fbba61), SkBits2Float(0x42478e4c), SkBits2Float(0x41fba3d9), SkBits2Float(0x424880fc), SkBits2Float(0x41fbba61), SkBits2Float(0x424974b2));  // 31.466f, 49.889f, 31.455f, 50.126f, 31.466f, 50.364f

+path.cubicTo(SkBits2Float(0x41fbd0e8), SkBits2Float(0x424a7de9), SkBits2Float(0x41fc8109), SkBits2Float(0x424b5b18), SkBits2Float(0x41fd47b1), SkBits2Float(0x424c4ecf));  // 31.477f, 50.623f, 31.563f, 50.839f, 31.66f, 51.077f

+path.cubicTo(SkBits2Float(0x41fd8b47), SkBits2Float(0x424c915e), SkBits2Float(0x41fdccd0), SkBits2Float(0x424cde2b), SkBits2Float(0x41fe3b67), SkBits2Float(0x424d167d));  // 31.693f, 51.142f, 31.725f, 51.217f, 31.779f, 51.272f

+path.cubicTo(SkBits2Float(0x41fe9377), SkBits2Float(0x424d4dc9), SkBits2Float(0x41fec086), SkBits2Float(0x424d8f52), SkBits2Float(0x41ff2f1d), SkBits2Float(0x424dc69e));  // 31.822f, 51.326f, 31.844f, 51.39f, 31.898f, 51.444f

+path.cubicTo(SkBits2Float(0x41ff70a6), SkBits2Float(0x424df3ac), SkBits2Float(0x41ffdf3e), SkBits2Float(0x424e092d), SkBits2Float(0x42000626), SkBits2Float(0x424e3536));  // 31.93f, 51.488f, 31.984f, 51.509f, 32.006f, 51.552f

+path.cubicTo(SkBits2Float(0x42003d72), SkBits2Float(0x424e6c82), SkBits2Float(0x4200c18a), SkBits2Float(0x424f3e6d), SkBits2Float(0x4201041a), SkBits2Float(0x424f49b1));  // 32.06f, 51.606f, 32.189f, 51.811f, 32.254f, 51.822f

+path.cubicTo(SkBits2Float(0x420172b2), SkBits2Float(0x424f6b7c), SkBits2Float(0x4201ec8d), SkBits2Float(0x424e8309), SkBits2Float(0x42020d51), SkBits2Float(0x424e4bbd));  // 32.362f, 51.855f, 32.481f, 51.628f, 32.513f, 51.574f

+path.cubicTo(SkBits2Float(0x4202be78), SkBits2Float(0x424d5807), SkBits2Float(0x42037ae2), SkBits2Float(0x424c6557), SkBits2Float(0x42044cce), SkBits2Float(0x424b9265));  // 32.686f, 51.336f, 32.87f, 51.099f, 33.075f, 50.893f

+path.cubicTo(SkBits2Float(0x42049aa1), SkBits2Float(0x424b4598), SkBits2Float(0x4204e874), SkBits2Float(0x424ae13e), SkBits2Float(0x42054084), SkBits2Float(0x424a9471));  // 33.151f, 50.818f, 33.227f, 50.72f, 33.313f, 50.645f

+path.cubicTo(SkBits2Float(0x42058d51), SkBits2Float(0x424a51e2), SkBits2Float(0x4206ef9f), SkBits2Float(0x4248fad7), SkBits2Float(0x42071063), SkBits2Float(0x4248cecf));  // 33.388f, 50.58f, 33.734f, 50.245f, 33.766f, 50.202f

+path.cubicTo(SkBits2Float(0x42075e36), SkBits2Float(0x424876bf), SkBits2Float(0x4207cccd), SkBits2Float(0x4248342f), SkBits2Float(0x42083021), SkBits2Float(0x4247e65c));  // 33.842f, 50.116f, 33.95f, 50.051f, 34.047f, 49.975f

+path.cubicTo(SkBits2Float(0x42088831), SkBits2Float(0x42478308), SkBits2Float(0x4208f6c9), SkBits2Float(0x4247363b), SkBits2Float(0x420970a4), SkBits2Float(0x4246f3ac));  // 34.133f, 49.878f, 34.241f, 49.803f, 34.36f, 49.738f

+path.cubicTo(SkBits2Float(0x4209f5c3), SkBits2Float(0x42469a95), SkBits2Float(0x420a645a), SkBits2Float(0x42464285), SkBits2Float(0x420add2f), SkBits2Float(0x4245f4b2));  // 34.49f, 49.651f, 34.598f, 49.565f, 34.716f, 49.489f

+path.cubicTo(SkBits2Float(0x420b2b02), SkBits2Float(0x4245be6c), SkBits2Float(0x420bc5a2), SkBits2Float(0x42455a12), SkBits2Float(0x420b8418), SkBits2Float(0x4244eb7b));  // 34.792f, 49.436f, 34.943f, 49.338f, 34.879f, 49.23f

+path.cubicTo(SkBits2Float(0x420b624d), SkBits2Float(0x4244cab6), SkBits2Float(0x420b1fbe), SkBits2Float(0x42449eae), SkBits2Float(0x420b0a3d), SkBits2Float(0x42448827));  // 34.846f, 49.198f, 34.781f, 49.155f, 34.76f, 49.133f

+path.cubicTo(SkBits2Float(0x420abd70), SkBits2Float(0x424450db), SkBits2Float(0x420a9ba5), SkBits2Float(0x42440e4c), SkBits2Float(0x420a5916), SkBits2Float(0x4243d700));  // 34.685f, 49.079f, 34.652f, 49.014f, 34.587f, 48.96f

+path.cubicTo(SkBits2Float(0x420a3851), SkBits2Float(0x4243b63b), SkBits2Float(0x420a21ca), SkBits2Float(0x4243b63b), SkBits2Float(0x4209f5c2), SkBits2Float(0x42439fb4));  // 34.555f, 48.928f, 34.533f, 48.928f, 34.49f, 48.906f

+path.cubicTo(SkBits2Float(0x4209ea7e), SkBits2Float(0x42439470), SkBits2Float(0x4209ea7e), SkBits2Float(0x424373ac), SkBits2Float(0x4209d3f7), SkBits2Float(0x42436868));  // 34.479f, 48.895f, 34.479f, 48.863f, 34.457f, 48.852f

+path.cubicTo(SkBits2Float(0x4209b332), SkBits2Float(0x424352e7), SkBits2Float(0x42099db1), SkBits2Float(0x42435e2b), SkBits2Float(0x42097be7), SkBits2Float(0x424352e7));  // 34.425f, 48.831f, 34.404f, 48.842f, 34.371f, 48.831f

+path.cubicTo(SkBits2Float(0x420970a3), SkBits2Float(0x42433c60), SkBits2Float(0x42096560), SkBits2Float(0x42431b9b), SkBits2Float(0x4209449b), SkBits2Float(0x42431b9b));  // 34.36f, 48.809f, 34.349f, 48.777f, 34.317f, 48.777f

+path.cubicTo(SkBits2Float(0x4208f6c8), SkBits2Float(0x4242e349), SkBits2Float(0x42089eb8), SkBits2Float(0x4242c284), SkBits2Float(0x42083020), SkBits2Float(0x4242a1c0));  // 34.241f, 48.722f, 34.155f, 48.69f, 34.047f, 48.658f

+path.cubicTo(SkBits2Float(0x42080f5b), SkBits2Float(0x4242967c), SkBits2Float(0x4207d810), SkBits2Float(0x42425f31), SkBits2Float(0x4207c188), SkBits2Float(0x42425f31));  // 34.015f, 48.647f, 33.961f, 48.593f, 33.939f, 48.593f

+path.cubicTo(SkBits2Float(0x420748b3), SkBits2Float(0x424227e5), SkBits2Float(0x42066040), SkBits2Float(0x4241fbdd), SkBits2Float(0x4205b957), SkBits2Float(0x42421ca2));  // 33.821f, 48.539f, 33.594f, 48.496f, 33.431f, 48.528f

+path.cubicTo(SkBits2Float(0x4204c6a7), SkBits2Float(0x42423329), SkBits2Float(0x42041580), SkBits2Float(0x4242ad04), SkBits2Float(0x42032d0d), SkBits2Float(0x4242c285));  // 33.194f, 48.55f, 33.021f, 48.669f, 32.794f, 48.69f

+path.cubicTo(SkBits2Float(0x42032d0d), SkBits2Float(0x4242b848), SkBits2Float(0x420322d0), SkBits2Float(0x4242a1c0), SkBits2Float(0x42032d0d), SkBits2Float(0x4242a1c0));  // 32.794f, 48.68f, 32.784f, 48.658f, 32.794f, 48.658f

+path.cubicTo(SkBits2Float(0x42032d0d), SkBits2Float(0x424280fb), SkBits2Float(0x42036459), SkBits2Float(0x424275b8), SkBits2Float(0x42036f9c), SkBits2Float(0x42426a74));  // 32.794f, 48.626f, 32.848f, 48.615f, 32.859f, 48.604f

+path.cubicTo(SkBits2Float(0x4203e977), SkBits2Float(0x4241cfd4), SkBits2Float(0x4204580f), SkBits2Float(0x42418201), SkBits2Float(0x420529fa), SkBits2Float(0x42413f72));  // 32.978f, 48.453f, 33.086f, 48.377f, 33.291f, 48.312f

+path.lineTo(SkBits2Float(0x42054abf), SkBits2Float(0x424128eb));  // 33.323f, 48.29f

+path.cubicTo(SkBits2Float(0x4205cfde), SkBits2Float(0x4240fde9), SkBits2Float(0x420649b9), SkBits2Float(0x4240fde9), SkBits2Float(0x4206b850), SkBits2Float(0x4240b016));  // 33.453f, 48.248f, 33.572f, 48.248f, 33.68f, 48.172f

+path.cubicTo(SkBits2Float(0x4206a1c9), SkBits2Float(0x4240998f), SkBits2Float(0x4206b850), SkBits2Float(0x42408e4b), SkBits2Float(0x4206a1c9), SkBits2Float(0x424078ca));  // 33.658f, 48.15f, 33.68f, 48.139f, 33.658f, 48.118f

+path.cubicTo(SkBits2Float(0x42068104), SkBits2Float(0x4240363b), SkBits2Float(0x42054081), SkBits2Float(0x423fb11c), SkBits2Float(0x4204d1ea), SkBits2Float(0x423f9057));  // 33.626f, 48.053f, 33.313f, 47.923f, 33.205f, 47.891f

+path.cubicTo(SkBits2Float(0x42044ccb), SkBits2Float(0x423f79d0), SkBits2Float(0x42035915), SkBits2Float(0x423f644f), SkBits2Float(0x4202be75), SkBits2Float(0x423f8513));  // 33.075f, 47.869f, 32.837f, 47.848f, 32.686f, 47.88f

+path.cubicTo(SkBits2Float(0x42022f19), SkBits2Float(0x423f9b9a), SkBits2Float(0x4201c081), SkBits2Float(0x423fde2a), SkBits2Float(0x420125e2), SkBits2Float(0x423ff3ab));  // 32.546f, 47.902f, 32.438f, 47.967f, 32.287f, 47.988f

+path.lineTo(SkBits2Float(0x42010f5b), SkBits2Float(0x423fc7a3));  // 32.265f, 47.945f

+path.cubicTo(SkBits2Float(0x4201a9fb), SkBits2Float(0x423f167c), SkBits2Float(0x42036459), SkBits2Float(0x423d5c1e), SkBits2Float(0x4204580f), SkBits2Float(0x423d198f));  // 32.416f, 47.772f, 32.848f, 47.34f, 33.086f, 47.275f

+path.cubicTo(SkBits2Float(0x4205b957), SkBits2Float(0x423cabfe), SkBits2Float(0x4207c188), SkBits2Float(0x423cd806), SkBits2Float(0x42090d4e), SkBits2Float(0x423d24d3));  // 33.431f, 47.168f, 33.939f, 47.211f, 34.263f, 47.286f

+path.cubicTo(SkBits2Float(0x420ae871), SkBits2Float(0x423d936b), SkBits2Float(0x420c9892), SkBits2Float(0x423e7bdd), SkBits2Float(0x420e6871), SkBits2Float(0x423ed3ee));  // 34.727f, 47.394f, 35.149f, 47.621f, 35.602f, 47.707f

+path.cubicTo(SkBits2Float(0x42103956), SkBits2Float(0x423f438c), SkBits2Float(0x42121479), SkBits2Float(0x423f0b3a), SkBits2Float(0x4213c49a), SkBits2Float(0x423e2e0b));  // 36.056f, 47.816f, 36.52f, 47.761f, 36.942f, 47.545f

+path.cubicTo(SkBits2Float(0x4214cdd1), SkBits2Float(0x423db536), SkBits2Float(0x4215c081), SkBits2Float(0x423d24d4), SkBits2Float(0x42169db1), SkBits2Float(0x423c696f));  // 37.201f, 47.427f, 37.438f, 47.286f, 37.654f, 47.103f

+path.cubicTo(SkBits2Float(0x4216eb84), SkBits2Float(0x423c26e0), SkBits2Float(0x4217df3a), SkBits2Float(0x423afbde), SkBits2Float(0x4218580f), SkBits2Float(0x423b75b9));  // 37.73f, 47.038f, 37.968f, 46.746f, 38.086f, 46.865f

+path.cubicTo(SkBits2Float(0x42189a9e), SkBits2Float(0x423bad05), SkBits2Float(0x421820c3), SkBits2Float(0x423c1b9c), SkBits2Float(0x4217ffff), SkBits2Float(0x423c311d));  // 38.151f, 46.919f, 38.032f, 47.027f, 38, 47.048f

+path.cubicTo(SkBits2Float(0x4217a6e8), SkBits2Float(0x423c9577), SkBits2Float(0x42173851), SkBits2Float(0x423ced87), SkBits2Float(0x4216cac0), SkBits2Float(0x423d5c1f));  // 37.913f, 47.146f, 37.805f, 47.232f, 37.698f, 47.34f

+path.cubicTo(SkBits2Float(0x42168831), SkBits2Float(0x423d9eae), SkBits2Float(0x421650e5), SkBits2Float(0x423deb7b), SkBits2Float(0x4215f7ce), SkBits2Float(0x423e23cd));  // 37.633f, 47.405f, 37.579f, 47.48f, 37.492f, 47.535f

+path.lineTo(SkBits2Float(0x4215f7ce), SkBits2Float(0x423e4492));  // 37.492f, 47.567f

+path.cubicTo(SkBits2Float(0x4215ed91), SkBits2Float(0x423e4fd6), SkBits2Float(0x4215d709), SkBits2Float(0x423e4492), SkBits2Float(0x4215cbc6), SkBits2Float(0x423e4fd6));  // 37.482f, 47.578f, 37.46f, 47.567f, 37.449f, 47.578f

+path.cubicTo(SkBits2Float(0x42158937), SkBits2Float(0x423e8722), SkBits2Float(0x42153126), SkBits2Float(0x423f00fd), SkBits2Float(0x4214ee97), SkBits2Float(0x423f3849));  // 37.384f, 47.632f, 37.298f, 47.751f, 37.233f, 47.805f

+path.cubicTo(SkBits2Float(0x4214d810), SkBits2Float(0x423f438d), SkBits2Float(0x4214cdd2), SkBits2Float(0x423f590e), SkBits2Float(0x4214cdd2), SkBits2Float(0x423f590e));  // 37.211f, 47.816f, 37.201f, 47.837f, 37.201f, 47.837f

+path.lineTo(SkBits2Float(0x4214b74b), SkBits2Float(0x423f590e));  // 37.179f, 47.837f

+path.lineTo(SkBits2Float(0x4214b74b), SkBits2Float(0x423f79d3));  // 37.179f, 47.869f

+path.cubicTo(SkBits2Float(0x42147fff), SkBits2Float(0x423f905a), SkBits2Float(0x421474bc), SkBits2Float(0x423fb11f), SkBits2Float(0x421448b3), SkBits2Float(0x423fc7a6));  // 37.125f, 47.891f, 37.114f, 47.923f, 37.071f, 47.945f

+path.lineTo(SkBits2Float(0x421448b3), SkBits2Float(0x423fdd27));  // 37.071f, 47.966f

+path.lineTo(SkBits2Float(0x42143332), SkBits2Float(0x423fdd27));  // 37.05f, 47.966f

+path.lineTo(SkBits2Float(0x4213b957), SkBits2Float(0x424077c7));  // 36.931f, 48.117f

+path.cubicTo(SkBits2Float(0x4213a2d0), SkBits2Float(0x4240830b), SkBits2Float(0x4213b957), SkBits2Float(0x4240988c), SkBits2Float(0x4213b957), SkBits2Float(0x4240988c));  // 36.909f, 48.128f, 36.931f, 48.149f, 36.931f, 48.149f

+path.cubicTo(SkBits2Float(0x4213c49b), SkBits2Float(0x4240988c), SkBits2Float(0x4213b957), SkBits2Float(0x4240ba57), SkBits2Float(0x4213da1c), SkBits2Float(0x4240af13));  // 36.942f, 48.149f, 36.931f, 48.182f, 36.963f, 48.171f

+path.cubicTo(SkBits2Float(0x42141cab), SkBits2Float(0x4240af13), SkBits2Float(0x4214a1ca), SkBits2Float(0x42405703), SkBits2Float(0x4214ee97), SkBits2Float(0x42403538));  // 37.028f, 48.171f, 37.158f, 48.085f, 37.233f, 48.052f

+path.cubicTo(SkBits2Float(0x42153126), SkBits2Float(0x42401473), SkBits2Float(0x42157ef9), SkBits2Float(0x423ffdec), SkBits2Float(0x4215cbc6), SkBits2Float(0x423fd1e4));  // 37.298f, 48.02f, 37.374f, 47.998f, 37.449f, 47.955f

+path.cubicTo(SkBits2Float(0x421650e5), SkBits2Float(0x423f8f55), SkBits2Float(0x4216cac0), SkBits2Float(0x423f4288), SkBits2Float(0x42178624), SkBits2Float(0x423f20bd));  // 37.579f, 47.89f, 37.698f, 47.815f, 37.881f, 47.782f

+path.cubicTo(SkBits2Float(0x42177ae0), SkBits2Float(0x423f8f55), SkBits2Float(0x421770a3), SkBits2Float(0x423fc6a0), SkBits2Float(0x42174395), SkBits2Float(0x423ffdec));  // 37.87f, 47.89f, 37.86f, 47.944f, 37.816f, 47.998f

+path.cubicTo(SkBits2Float(0x4216bf7c), SkBits2Float(0x4240ba56), SkBits2Float(0x4215ab02), SkBits2Float(0x4241332b), SkBits2Float(0x4214f9db), SkBits2Float(0x4241c38e));  // 37.687f, 48.182f, 37.417f, 48.3f, 37.244f, 48.441f

+path.cubicTo(SkBits2Float(0x42143333), SkBits2Float(0x424274b5), SkBits2Float(0x42136b85), SkBits2Float(0x42433019), SkBits2Float(0x4212c5a2), SkBits2Float(0x4243f7c7));  // 37.05f, 48.614f, 36.855f, 48.797f, 36.693f, 48.992f

+path.cubicTo(SkBits2Float(0x42115917), SkBits2Float(0x42459b9e), SkBits2Float(0x421022d1), SkBits2Float(0x42476c83), SkBits2Float(0x420f0313), SkBits2Float(0x4249311f));  // 36.337f, 49.402f, 36.034f, 49.856f, 35.753f, 50.298f

+path.cubicTo(SkBits2Float(0x420e1ba6), SkBits2Float(0x424a936d), SkBits2Float(0x420d75c3), SkBits2Float(0x424c21c3), SkBits2Float(0x420cdb23), SkBits2Float(0x424dba56));  // 35.527f, 50.644f, 35.365f, 51.033f, 35.214f, 51.432f

+path.cubicTo(SkBits2Float(0x420c3f7d), SkBits2Float(0x424f6a77), SkBits2Float(0x420b8419), SkBits2Float(0x42510e4e), SkBits2Float(0x420b1fbf), SkBits2Float(0x4252d3f0));  // 35.062f, 51.854f, 34.879f, 52.264f, 34.781f, 52.707f

+path.cubicTo(SkBits2Float(0x420ad2f2), SkBits2Float(0x42548e4e), SkBits2Float(0x420ab127), SkBits2Float(0x42565e2d), SkBits2Float(0x420a9063), SkBits2Float(0x4258188c));  // 34.706f, 53.139f, 34.673f, 53.592f, 34.641f, 54.024f

+path.cubicTo(SkBits2Float(0x420a7ae2), SkBits2Float(0x4258882a), SkBits2Float(0x420a9ba7), SkBits2Float(0x4258e03a), SkBits2Float(0x420a9ba7), SkBits2Float(0x42594ed2));  // 34.62f, 54.133f, 34.652f, 54.219f, 34.652f, 54.327f

+path.cubicTo(SkBits2Float(0x420aa6eb), SkBits2Float(0x425e301a), SkBits2Float(0x420c820d), SkBits2Float(0x4262c495), SkBits2Float(0x420ecbc8), SkBits2Float(0x4266fff9));  // 34.663f, 55.547f, 35.127f, 56.692f, 35.699f, 57.75f

+path.cubicTo(SkBits2Float(0x420eed93), SkBits2Float(0x426721c4), SkBits2Float(0x420f0e57), SkBits2Float(0x42674dcc), SkBits2Float(0x420f3022), SkBits2Float(0x42676e91));  // 35.732f, 57.783f, 35.764f, 57.826f, 35.797f, 57.858f

+path.cubicTo(SkBits2Float(0x420f7df5), SkBits2Float(0x42680a37), SkBits2Float(0x420fbf7e), SkBits2Float(0x42689993), SkBits2Float(0x42100d51), SkBits2Float(0x42693433));  // 35.873f, 58.01f, 35.937f, 58.15f, 36.013f, 58.301f

+path.cubicTo(SkBits2Float(0x42102e16), SkBits2Float(0x426955fe), SkBits2Float(0x42105a1e), SkBits2Float(0x426976c2), SkBits2Float(0x42106561), SkBits2Float(0x42698d4a));  // 36.045f, 58.334f, 36.088f, 58.366f, 36.099f, 58.388f

+path.cubicTo(SkBits2Float(0x4210872c), SkBits2Float(0x4269e55a), SkBits2Float(0x4210a7f0), SkBits2Float(0x426a3d6b), SkBits2Float(0x4210ea80), SkBits2Float(0x426a6a79));  // 36.132f, 58.474f, 36.164f, 58.56f, 36.229f, 58.604f

+path.cubicTo(SkBits2Float(0x42119aa1), SkBits2Float(0x426acdcd), SkBits2Float(0x42131376), SkBits2Float(0x426a48ae), SkBits2Float(0x4213e561), SkBits2Float(0x426a6a79));  // 36.401f, 58.701f, 36.769f, 58.571f, 36.974f, 58.604f

+path.cubicTo(SkBits2Float(0x4213fae2), SkBits2Float(0x426a75bd), SkBits2Float(0x42141cad), SkBits2Float(0x426a8b3e), SkBits2Float(0x42143d71), SkBits2Float(0x426a8b3e));  // 36.995f, 58.615f, 37.028f, 58.636f, 37.06f, 58.636f

+path.cubicTo(SkBits2Float(0x42141cac), SkBits2Float(0x426acdcd), SkBits2Float(0x42143334), SkBits2Float(0x426aee92), SkBits2Float(0x42141cac), SkBits2Float(0x426b25de));  // 37.028f, 58.701f, 37.05f, 58.733f, 37.028f, 58.787f

+path.cubicTo(SkBits2Float(0x4213e560), SkBits2Float(0x426b9fb9), SkBits2Float(0x4212dc29), SkBits2Float(0x426d0d4b), SkBits2Float(0x4212f1aa), SkBits2Float(0x426da7ea));  // 36.974f, 58.906f, 36.715f, 59.263f, 36.736f, 59.414f

+path.cubicTo(SkBits2Float(0x4212f1aa), SkBits2Float(0x426dfffa), SkBits2Float(0x4213b958), SkBits2Float(0x426ed1e6), SkBits2Float(0x4213c49c), SkBits2Float(0x426edd29));  // 36.736f, 59.5f, 36.931f, 59.705f, 36.942f, 59.716f

+path.cubicTo(SkBits2Float(0x4213e561), SkBits2Float(0x426f1fb8), SkBits2Float(0x42143d71), SkBits2Float(0x426f9993), SkBits2Float(0x421448b5), SkBits2Float(0x426ffce7));  // 36.974f, 59.781f, 37.06f, 59.9f, 37.071f, 59.997f

+path.cubicTo(SkBits2Float(0x421448b5), SkBits2Float(0x427076c2), SkBits2Float(0x4214072c), SkBits2Float(0x4270ef97), SkBits2Float(0x4213fae2), SkBits2Float(0x427148ae));  // 37.071f, 60.116f, 37.007f, 60.234f, 36.995f, 60.321f

+path.cubicTo(SkBits2Float(0x4213e561), SkBits2Float(0x42717ffa), SkBits2Float(0x4213fae2), SkBits2Float(0x42718b3d), SkBits2Float(0x4213e561), SkBits2Float(0x4271b746));  // 36.974f, 60.375f, 36.995f, 60.386f, 36.974f, 60.429f

+path.cubicTo(SkBits2Float(0x4213da1d), SkBits2Float(0x4271ccc7), SkBits2Float(0x4213b959), SkBits2Float(0x42721a9a), SkBits2Float(0x4213a2d2), SkBits2Float(0x42721a9a));  // 36.963f, 60.45f, 36.931f, 60.526f, 36.909f, 60.526f

+path.cubicTo(SkBits2Float(0x42134ac2), SkBits2Float(0x42723c65), SkBits2Float(0x4212d0e6), SkBits2Float(0x427225de), SkBits2Float(0x42126d93), SkBits2Float(0x427225de));  // 36.823f, 60.559f, 36.704f, 60.537f, 36.607f, 60.537f

+path.cubicTo(SkBits2Float(0x42124bc8), SkBits2Float(0x427225de), SkBits2Float(0x4211bc6c), SkBits2Float(0x42723c65), SkBits2Float(0x42119064), SkBits2Float(0x42723c65));  // 36.574f, 60.537f, 36.434f, 60.559f, 36.391f, 60.559f

+path.cubicTo(SkBits2Float(0x4210d3fa), SkBits2Float(0x427246a2), SkBits2Float(0x420ff6ca), SkBits2Float(0x4272301b), SkBits2Float(0x420f676e), SkBits2Float(0x4272686d));  // 36.207f, 60.569f, 35.991f, 60.547f, 35.851f, 60.602f

+path.cubicTo(SkBits2Float(0x420eb647), SkBits2Float(0x4272b53a), SkBits2Float(0x420e52f3), SkBits2Float(0x42737ce8), SkBits2Float(0x420dc291), SkBits2Float(0x4273f5bd));  // 35.678f, 60.677f, 35.581f, 60.872f, 35.44f, 60.99f

+path.cubicTo(SkBits2Float(0x420d116a), SkBits2Float(0x4274861f), SkBits2Float(0x420c5606), SkBits2Float(0x4274e973), SkBits2Float(0x420b999b), SkBits2Float(0x4275580b));  // 35.267f, 61.131f, 35.084f, 61.228f, 34.9f, 61.336f

+path.cubicTo(SkBits2Float(0x420a9ba7), SkBits2Float(0x4275fdee), SkBits2Float(0x4209b335), SkBits2Float(0x42768d4a), SkBits2Float(0x42089eba), SkBits2Float(0x4276f1a5));  // 34.652f, 61.498f, 34.425f, 61.638f, 34.155f, 61.736f

+path.cubicTo(SkBits2Float(0x4207ab04), SkBits2Float(0x42773e72), SkBits2Float(0x4206a1cc), SkBits2Float(0x42778101), SkBits2Float(0x4205b95a), SkBits2Float(0x4277c391));  // 33.917f, 61.811f, 33.658f, 61.876f, 33.431f, 61.941f

+path.cubicTo(SkBits2Float(0x4203bd73), SkBits2Float(0x42786974), SkBits2Float(0x4201cbc9), SkBits2Float(0x42793b60), SkBits2Float(0x4200ac0a), SkBits2Float(0x427af5be));  // 32.935f, 62.103f, 32.449f, 62.308f, 32.168f, 62.74f

+path.cubicTo(SkBits2Float(0x420074be), SkBits2Float(0x427b428b), SkBits2Float(0x41ffb43d), SkBits2Float(0x427c4cc8), SkBits2Float(0x41ff872f), SkBits2Float(0x427ca4d9));  // 32.114f, 62.815f, 31.963f, 63.075f, 31.941f, 63.161f

+path.cubicTo(SkBits2Float(0x41ff872f), SkBits2Float(0x427cbb60), SkBits2Float(0x41ff9db6), SkBits2Float(0x427cd0e1), SkBits2Float(0x41ff872f), SkBits2Float(0x427ce768));  // 31.941f, 63.183f, 31.952f, 63.204f, 31.941f, 63.226f

+path.cubicTo(SkBits2Float(0x41ffb43d), SkBits2Float(0x427cfce9), SkBits2Float(0x41ffb43d), SkBits2Float(0x427cfce9), SkBits2Float(0x41ffdf3f), SkBits2Float(0x427d1370));  // 31.963f, 63.247f, 31.963f, 63.247f, 31.984f, 63.269f

+path.cubicTo(SkBits2Float(0x4200ac0a), SkBits2Float(0x427cfce9), SkBits2Float(0x42010f5e), SkBits2Float(0x427cd0e1), SkBits2Float(0x4201a9fe), SkBits2Float(0x427ca4d8));  // 32.168f, 63.247f, 32.265f, 63.204f, 32.416f, 63.161f

+path.cubicTo(SkBits2Float(0x4201c085), SkBits2Float(0x427c9994), SkBits2Float(0x4201f7d1), SkBits2Float(0x427c78d0), SkBits2Float(0x42020315), SkBits2Float(0x427c78d0));  // 32.438f, 63.15f, 32.492f, 63.118f, 32.503f, 63.118f

+path.cubicTo(SkBits2Float(0x420223da), SkBits2Float(0x427c6249), SkBits2Float(0x42022f1d), SkBits2Float(0x427c78d0), SkBits2Float(0x42023a61), SkBits2Float(0x427c78d0));  // 32.535f, 63.096f, 32.546f, 63.118f, 32.557f, 63.118f

+path.cubicTo(SkBits2Float(0x42025b26), SkBits2Float(0x427c6249), SkBits2Float(0x42028834), SkBits2Float(0x427c4184), SkBits2Float(0x4202a8f9), SkBits2Float(0x427c4184));  // 32.589f, 63.096f, 32.633f, 63.064f, 32.665f, 63.064f

+path.cubicTo(SkBits2Float(0x4203e97c), SkBits2Float(0x427bc7a9), SkBits2Float(0x42061db5), SkBits2Float(0x427ba6e4), SkBits2Float(0x4207b649), SkBits2Float(0x427bfef5));  // 32.978f, 62.945f, 33.529f, 62.913f, 33.928f, 62.999f

+path.cubicTo(SkBits2Float(0x42089ebc), SkBits2Float(0x427c20c0), SkBits2Float(0x420970a7), SkBits2Float(0x427c78d0), SkBits2Float(0x420a21ce), SkBits2Float(0x427cc59d));  // 34.155f, 63.032f, 34.36f, 63.118f, 34.533f, 63.193f

+path.cubicTo(SkBits2Float(0x420a6fa1), SkBits2Float(0x427cdc24), SkBits2Float(0x420ab12a), SkBits2Float(0x427ce768), SkBits2Float(0x420af3ba), SkBits2Float(0x427d1370));  // 34.609f, 63.215f, 34.673f, 63.226f, 34.738f, 63.269f

+path.cubicTo(SkBits2Float(0x420b0a41), SkBits2Float(0x427d1370), SkBits2Float(0x420af3ba), SkBits2Float(0x427d353b), SkBits2Float(0x420b1585), SkBits2Float(0x427d407e));  // 34.76f, 63.269f, 34.738f, 63.302f, 34.771f, 63.313f

+path.cubicTo(SkBits2Float(0x420b0a41), SkBits2Float(0x427d6143), SkBits2Float(0x420b0a41), SkBits2Float(0x427d8207), SkBits2Float(0x420af3ba), SkBits2Float(0x427dae0f));  // 34.76f, 63.345f, 34.76f, 63.377f, 34.738f, 63.42f

+path.cubicTo(SkBits2Float(0x420ad2f5), SkBits2Float(0x427df09e), SkBits2Float(0x420a2d12), SkBits2Float(0x427e54f8), SkBits2Float(0x420a4293), SkBits2Float(0x427ee455));  // 34.706f, 63.485f, 34.544f, 63.583f, 34.565f, 63.723f

+path.cubicTo(SkBits2Float(0x420a591a), SkBits2Float(0x427f051a), SkBits2Float(0x420ad2f5), SkBits2Float(0x427f3122), SkBits2Float(0x420af3ba), SkBits2Float(0x427f47a9));  // 34.587f, 63.755f, 34.706f, 63.798f, 34.738f, 63.82f

+path.cubicTo(SkBits2Float(0x420af3ba), SkBits2Float(0x427f5d2a), SkBits2Float(0x420af3ba), SkBits2Float(0x427f73b1), SkBits2Float(0x420b0a41), SkBits2Float(0x427f7ef5));  // 34.738f, 63.841f, 34.738f, 63.863f, 34.76f, 63.874f

+path.cubicTo(SkBits2Float(0x420add33), SkBits2Float(0x427fccc8), SkBits2Float(0x420a21ce), SkBits2Float(0x42803e74), SkBits2Float(0x420a2d12), SkBits2Float(0x4280701e));  // 34.716f, 63.95f, 34.533f, 64.122f, 34.544f, 64.219f

+path.cubicTo(SkBits2Float(0x420a3856), SkBits2Float(0x42808bc4), SkBits2Float(0x420ad2f5), SkBits2Float(0x4280a7ed), SkBits2Float(0x420ae876), SkBits2Float(0x4280b2ad));  // 34.555f, 64.273f, 34.706f, 64.328f, 34.727f, 64.349f

+path.cubicTo(SkBits2Float(0x420af3ba), SkBits2Float(0x4280bdf1), SkBits2Float(0x420add32), SkBits2Float(0x4280c8b1), SkBits2Float(0x420af3ba), SkBits2Float(0x4280d3f5));  // 34.738f, 64.371f, 34.716f, 64.392f, 34.738f, 64.414f

+path.cubicTo(SkBits2Float(0x420abd74), SkBits2Float(0x4280f53d), SkBits2Float(0x4209f5c6), SkBits2Float(0x4281428d), SkBits2Float(0x420a21ce), SkBits2Float(0x42816e95));  // 34.685f, 64.479f, 34.49f, 64.63f, 34.533f, 64.716f

+path.cubicTo(SkBits2Float(0x420a4293), SkBits2Float(0x4281957e), SkBits2Float(0x420ad2f5), SkBits2Float(0x4281a664), SkBits2Float(0x420ae876), SkBits2Float(0x4281c187));  // 34.565f, 64.792f, 34.706f, 64.825f, 34.727f, 64.878f

+path.cubicTo(SkBits2Float(0x420ae876), SkBits2Float(0x4281c729), SkBits2Float(0x420add32), SkBits2Float(0x4281d26c), SkBits2Float(0x420ae876), SkBits2Float(0x4281d26c));  // 34.727f, 64.889f, 34.716f, 64.911f, 34.727f, 64.911f

+path.cubicTo(SkBits2Float(0x420aa6ed), SkBits2Float(0x4281fe74), SkBits2Float(0x420a591a), SkBits2Float(0x42821a1a), SkBits2Float(0x4209f5c6), SkBits2Float(0x42823b62));  // 34.663f, 64.997f, 34.587f, 65.051f, 34.49f, 65.116f

+path.cubicTo(SkBits2Float(0x420a168b), SkBits2Float(0x42825caa), SkBits2Float(0x420a010a), SkBits2Float(0x4282624b), SkBits2Float(0x420a2d12), SkBits2Float(0x42827850));  // 34.522f, 65.181f, 34.501f, 65.192f, 34.544f, 65.235f

+path.cubicTo(SkBits2Float(0x420a645e), SkBits2Float(0x428288b2), SkBits2Float(0x420a9baa), SkBits2Float(0x428293f6), SkBits2Float(0x420ad2f5), SkBits2Float(0x4282a458));  // 34.598f, 65.267f, 34.652f, 65.289f, 34.706f, 65.321f

+path.cubicTo(SkBits2Float(0x420ad2f5), SkBits2Float(0x4282a458), SkBits2Float(0x420add32), SkBits2Float(0x4282d685), SkBits2Float(0x420abd74), SkBits2Float(0x4282c5a0));  // 34.706f, 65.321f, 34.716f, 65.419f, 34.685f, 65.386f

+path.cubicTo(SkBits2Float(0x420aa6ed), SkBits2Float(0x4282cb42), SkBits2Float(0x420a9066), SkBits2Float(0x4282e146), SkBits2Float(0x420a6fa1), SkBits2Float(0x4282e6e8));  // 34.663f, 65.397f, 34.641f, 65.44f, 34.609f, 65.451f

+path.cubicTo(SkBits2Float(0x4209df3f), SkBits2Float(0x42830830), SkBits2Float(0x4208f6cc), SkBits2Float(0x4282bae0), SkBits2Float(0x42088834), SkBits2Float(0x4282a459));  // 34.468f, 65.516f, 34.241f, 65.365f, 34.133f, 65.321f

+path.cubicTo(SkBits2Float(0x420846ab), SkBits2Float(0x42829915), SkBits2Float(0x42080f5f), SkBits2Float(0x42829915), SkBits2Float(0x4207c18c), SkBits2Float(0x428293f7));  // 34.069f, 65.299f, 34.015f, 65.299f, 33.939f, 65.289f

+path.cubicTo(SkBits2Float(0x42079584), SkBits2Float(0x428288b3), SkBits2Float(0x420748b7), SkBits2Float(0x42826d0e), SkBits2Float(0x42071ba9), SkBits2Float(0x42826d0e));  // 33.896f, 65.267f, 33.821f, 65.213f, 33.777f, 65.213f

+path.cubicTo(SkBits2Float(0x4206cedc), SkBits2Float(0x4282624d), SkBits2Float(0x42068109), SkBits2Float(0x4282624d), SkBits2Float(0x42061272), SkBits2Float(0x4282624d));  // 33.702f, 65.192f, 33.626f, 65.192f, 33.518f, 65.192f

+path.cubicTo(SkBits2Float(0x4205cfe3), SkBits2Float(0x42825cab), SkBits2Float(0x4205614b), SkBits2Float(0x42824bc6), SkBits2Float(0x42051ebc), SkBits2Float(0x42824bc6));  // 33.453f, 65.181f, 33.345f, 65.148f, 33.28f, 65.148f

+path.cubicTo(SkBits2Float(0x42037ae5), SkBits2Float(0x428246a7), SkBits2Float(0x4201cbca), SkBits2Float(0x42829eb8), SkBits2Float(0x4200ac0c), SkBits2Float(0x4282e147));  // 32.87f, 65.138f, 32.449f, 65.31f, 32.168f, 65.44f

+path.cubicTo(SkBits2Float(0x42008b47), SkBits2Float(0x4282e6e9), SkBits2Float(0x42005e39), SkBits2Float(0x4282fced), SkBits2Float(0x42003d74), SkBits2Float(0x4283028f));  // 32.136f, 65.451f, 32.092f, 65.494f, 32.06f, 65.505f

+path.cubicTo(SkBits2Float(0x41fdf9e2), SkBits2Float(0x42833f7d), SkBits2Float(0x41fa4190), SkBits2Float(0x42836041), SkBits2Float(0x41f674c3), SkBits2Float(0x42834fdf));  // 31.747f, 65.624f, 31.282f, 65.688f, 30.807f, 65.656f

+path.cubicTo(SkBits2Float(0x41f59794), SkBits2Float(0x4283451e), SkBits2Float(0x41f48d56), SkBits2Float(0x4283451e), SkBits2Float(0x41f3b027), SkBits2Float(0x428339db));  // 30.699f, 65.635f, 30.569f, 65.635f, 30.461f, 65.613f

+path.cubicTo(SkBits2Float(0x41f32d15), SkBits2Float(0x42832e97), SkBits2Float(0x41f2666d), SkBits2Float(0x428312f2), SkBits2Float(0x41f1b440), SkBits2Float(0x42830831));  // 30.397f, 65.591f, 30.3f, 65.537f, 30.213f, 65.516f

+path.cubicTo(SkBits2Float(0x41f1041f), SkBits2Float(0x4282fced), SkBits2Float(0x41f07f01), SkBits2Float(0x4282f74c), SkBits2Float(0x41efb859), SkBits2Float(0x4282e6e9));  // 30.127f, 65.494f, 30.062f, 65.483f, 29.965f, 65.451f

+path.cubicTo(SkBits2Float(0x41efa1d2), SkBits2Float(0x4282e147), SkBits2Float(0x41ef6049), SkBits2Float(0x4282d687), SkBits2Float(0x41ef49c1), SkBits2Float(0x4282d687));  // 29.954f, 65.44f, 29.922f, 65.419f, 29.911f, 65.419f

+path.cubicTo(SkBits2Float(0x41ef062b), SkBits2Float(0x4282cb43), SkBits2Float(0x41eec4a2), SkBits2Float(0x4282cb43), SkBits2Float(0x41ee560b), SkBits2Float(0x4282c5a2));  // 29.878f, 65.397f, 29.846f, 65.397f, 29.792f, 65.386f

+path.cubicTo(SkBits2Float(0x41ee1275), SkBits2Float(0x4282c000), SkBits2Float(0x41ed8f63), SkBits2Float(0x4282a45a), SkBits2Float(0x41ed3546), SkBits2Float(0x42829eb9));  // 29.759f, 65.375f, 29.695f, 65.321f, 29.651f, 65.31f

+path.cubicTo(SkBits2Float(0x41ebbe7d), SkBits2Float(0x42827d71), SkBits2Float(0x41ea72b7), SkBits2Float(0x42825cad), SkBits2Float(0x41e91069), SkBits2Float(0x42823b65));  // 29.468f, 65.245f, 29.306f, 65.181f, 29.133f, 65.116f

+path.cubicTo(SkBits2Float(0x41e6fdfa), SkBits2Float(0x42820419), SkBits2Float(0x41e4a7f6), SkBits2Float(0x4281ab86), SkBits2Float(0x41e18b4a), SkBits2Float(0x4281ab86));  // 28.874f, 65.008f, 28.582f, 64.835f, 28.193f, 64.835f

+path.cubicTo(SkBits2Float(0x41de9bac), SkBits2Float(0x4281b128), SkBits2Float(0x41dcf7d5), SkBits2Float(0x4281fe78), SkBits2Float(0x41db3d77), SkBits2Float(0x428246a9));  // 27.826f, 64.846f, 27.621f, 64.997f, 27.405f, 65.138f

+path.cubicTo(SkBits2Float(0x41dacedf), SkBits2Float(0x4282570b), SkBits2Float(0x41da76cf), SkBits2Float(0x4282570b), SkBits2Float(0x41da0838), SkBits2Float(0x4282676e));  // 27.351f, 65.17f, 27.308f, 65.17f, 27.254f, 65.202f

+path.cubicTo(SkBits2Float(0x41d9f1b1), SkBits2Float(0x4282676e), SkBits2Float(0x41d9f1b1), SkBits2Float(0x42827853), SkBits2Float(0x41d9db2a), SkBits2Float(0x42827d72));  // 27.243f, 65.202f, 27.243f, 65.235f, 27.232f, 65.245f

+path.cubicTo(SkBits2Float(0x41d96c92), SkBits2Float(0x428288b6), SkBits2Float(0x41d91482), SkBits2Float(0x428288b6), SkBits2Float(0x41d8a5eb), SkBits2Float(0x42829eba));  // 27.178f, 65.267f, 27.135f, 65.267f, 27.081f, 65.31f

+path.lineTo(SkBits2Float(0x41d88f64), SkBits2Float(0x4282a9fe));  // 27.07f, 65.332f

+path.cubicTo(SkBits2Float(0x41d6eb8d), SkBits2Float(0x4282e14a), SkBits2Float(0x41d4ac10), SkBits2Float(0x42830291), SkBits2Float(0x41d25818), SkBits2Float(0x428312f4));  // 26.865f, 65.44f, 26.584f, 65.505f, 26.293f, 65.537f

+path.cubicTo(SkBits2Float(0x41d0b235), SkBits2Float(0x42831896), SkBits2Float(0x41ce74c4), SkBits2Float(0x428312f4), SkBits2Float(0x41cce568), SkBits2Float(0x42830292));  // 26.087f, 65.548f, 25.807f, 65.537f, 25.612f, 65.505f

+path.cubicTo(SkBits2Float(0x41cca3df), SkBits2Float(0x4282fcf0), SkBits2Float(0x41cc1ec0), SkBits2Float(0x4282f1ad), SkBits2Float(0x41cbf3be), SkBits2Float(0x4282f1ad));  // 25.58f, 65.494f, 25.515f, 65.472f, 25.494f, 65.472f

+path.cubicTo(SkBits2Float(0x41ca9170), SkBits2Float(0x4282dba9), SkBits2Float(0x41c99dba), SkBits2Float(0x4282e14b), SkBits2Float(0x41c8687a), SkBits2Float(0x4282cb47));  // 25.321f, 65.429f, 25.202f, 65.44f, 25.051f, 65.397f

+path.cubicTo(SkBits2Float(0x41c7b64d), SkBits2Float(0x4282c003), SkBits2Float(0x41c71cb3), SkBits2Float(0x4282bae5), SkBits2Float(0x41c6560b), SkBits2Float(0x4282a9ff));  // 24.964f, 65.375f, 24.889f, 65.365f, 24.792f, 65.332f

+path.lineTo(SkBits2Float(0x41c628fd), SkBits2Float(0x42829ebb));  // 24.77f, 65.31f

+path.cubicTo(SkBits2Float(0x41c58d57), SkBits2Float(0x428293fa), SkBits2Float(0x41c53547), SkBits2Float(0x42829919), SkBits2Float(0x41c4b028), SkBits2Float(0x428293fa));  // 24.694f, 65.289f, 24.651f, 65.299f, 24.586f, 65.289f

+path.lineTo(SkBits2Float(0x41c46e9f), SkBits2Float(0x42828315));  // 24.554f, 65.256f

+path.cubicTo(SkBits2Float(0x41c1d712), SkBits2Float(0x4282570d), SkBits2Float(0x41be20cc), SkBits2Float(0x428209bd), SkBits2Float(0x41bb0420), SkBits2Float(0x42820f5f));  // 24.23f, 65.17f, 23.766f, 65.019f, 23.377f, 65.03f

+path.cubicTo(SkBits2Float(0x41b9a1d2), SkBits2Float(0x42820f5f), SkBits2Float(0x41b7e774), SkBits2Float(0x42823024), SkBits2Float(0x41b6dd37), SkBits2Float(0x428246ab));  // 23.204f, 65.03f, 22.988f, 65.094f, 22.858f, 65.138f

+path.cubicTo(SkBits2Float(0x41b5eb8d), SkBits2Float(0x4282570d), SkBits2Float(0x41b54fe7), SkBits2Float(0x4282570d), SkBits2Float(0x41b45c31), SkBits2Float(0x42826770));  // 22.74f, 65.17f, 22.664f, 65.17f, 22.545f, 65.202f

+path.cubicTo(SkBits2Float(0x41b3ed99), SkBits2Float(0x42826d12), SkBits2Float(0x41b35400), SkBits2Float(0x428288b8), SkBits2Float(0x41b2fbef), SkBits2Float(0x428293fb));  // 22.491f, 65.213f, 22.416f, 65.267f, 22.373f, 65.289f

+path.cubicTo(SkBits2Float(0x41b274c4), SkBits2Float(0x4282991a), SkBits2Float(0x41b249c2), SkBits2Float(0x428293fb), SkBits2Float(0x41b1c4a3), SkBits2Float(0x42829ebc));  // 22.307f, 65.299f, 22.286f, 65.289f, 22.221f, 65.31f

+path.cubicTo(SkBits2Float(0x41b1560b), SkBits2Float(0x4282a45e), SkBits2Float(0x41b08f64), SkBits2Float(0x4282c004), SkBits2Float(0x41aff3be), SkBits2Float(0x4282cb47));  // 22.167f, 65.321f, 22.07f, 65.375f, 21.994f, 65.397f

+path.cubicTo(SkBits2Float(0x41aea7f7), SkBits2Float(0x4282e14b), SkBits2Float(0x41ad893f), SkBits2Float(0x4282f1ad), SkBits2Float(0x41ac3d78), SkBits2Float(0x42830835));  // 21.832f, 65.44f, 21.692f, 65.472f, 21.53f, 65.516f

+path.cubicTo(SkBits2Float(0x41ac106a), SkBits2Float(0x428312f6), SkBits2Float(0x41aba1d2), SkBits2Float(0x42831e39), SkBits2Float(0x41ab76d0), SkBits2Float(0x428323db));  // 21.508f, 65.537f, 21.454f, 65.559f, 21.433f, 65.57f

+path.cubicTo(SkBits2Float(0x41aac4a3), SkBits2Float(0x4283343d), SkBits2Float(0x41aa560b), SkBits2Float(0x4283343d), SkBits2Float(0x41a9ba66), SkBits2Float(0x42833f81));  // 21.346f, 65.602f, 21.292f, 65.602f, 21.216f, 65.624f

+path.lineTo(SkBits2Float(0x41a98f64), SkBits2Float(0x42834fe3));  // 21.195f, 65.656f

+path.cubicTo(SkBits2Float(0x41a96256), SkBits2Float(0x42834fe3), SkBits2Float(0x41a93754), SkBits2Float(0x42834522), SkBits2Float(0x41a920cc), SkBits2Float(0x42834fe3));  // 21.173f, 65.656f, 21.152f, 65.635f, 21.141f, 65.656f

+path.cubicTo(SkBits2Float(0x41a90a45), SkBits2Float(0x42834fe3), SkBits2Float(0x41a8b234), SkBits2Float(0x42836045), SkBits2Float(0x41a89bad), SkBits2Float(0x42836b89));  // 21.13f, 65.656f, 21.087f, 65.688f, 21.076f, 65.71f

+path.cubicTo(SkBits2Float(0x41a7d505), SkBits2Float(0x42837beb), SkBits2Float(0x41a7666e), SkBits2Float(0x4283818d), SkBits2Float(0x41a6cac8), SkBits2Float(0x42839d33));  // 20.979f, 65.742f, 20.925f, 65.753f, 20.849f, 65.807f

+path.cubicTo(SkBits2Float(0x41a6b64d), SkBits2Float(0x4283a2d5), SkBits2Float(0x41a672b8), SkBits2Float(0x4283b3ba), SkBits2Float(0x41a65e3d), SkBits2Float(0x4283b8d9));  // 20.839f, 65.818f, 20.806f, 65.851f, 20.796f, 65.861f

+path.cubicTo(SkBits2Float(0x41a6312f), SkBits2Float(0x4283be7b), SkBits2Float(0x41a60420), SkBits2Float(0x4283b8d9), SkBits2Float(0x41a5ed99), SkBits2Float(0x4283be7b));  // 20.774f, 65.872f, 20.752f, 65.861f, 20.741f, 65.872f

+path.cubicTo(SkBits2Float(0x41a5810e), SkBits2Float(0x4283cedd), SkBits2Float(0x41a4e568), SkBits2Float(0x428406ac), SkBits2Float(0x41a48d57), SkBits2Float(0x42840bcb));  // 20.688f, 65.904f, 20.612f, 66.013f, 20.569f, 66.023f

+path.lineTo(SkBits2Float(0x41a41ebf), SkBits2Float(0x42840bcb));  // 20.515f, 66.023f

+path.cubicTo(SkBits2Float(0x41a40838), SkBits2Float(0x4283fb69), SkBits2Float(0x41a3f1b1), SkBits2Float(0x428406ac), SkBits2Float(0x41a3f1b1), SkBits2Float(0x4283fb69));  // 20.504f, 65.991f, 20.493f, 66.013f, 20.493f, 65.991f

+path.cubicTo(SkBits2Float(0x41a38319), SkBits2Float(0x4283b8da), SkBits2Float(0x41a4b859), SkBits2Float(0x4282f750), SkBits2Float(0x41a4e567), SkBits2Float(0x4282cb48));  // 20.439f, 65.861f, 20.59f, 65.483f, 20.612f, 65.397f

+path.cubicTo(SkBits2Float(0x41a5ed98), SkBits2Float(0x4281d273), SkBits2Float(0x41a74fe6), SkBits2Float(0x4280ea00), SkBits2Float(0x41a96255), SkBits2Float(0x42802e19));  // 20.741f, 64.911f, 20.914f, 64.457f, 21.173f, 64.09f

+path.cubicTo(SkBits2Float(0x41aa2b09), SkBits2Float(0x427fccd6), SkBits2Float(0x41ab1ebf), SkBits2Float(0x427f6982), SkBits2Float(0x41abfbef), SkBits2Float(0x427eefa7));  // 21.271f, 63.95f, 21.39f, 63.853f, 21.498f, 63.734f

+path.cubicTo(SkBits2Float(0x41ac7f01), SkBits2Float(0x427e9690), SkBits2Float(0x41aced99), SkBits2Float(0x427e49c4), SkBits2Float(0x41ad893f), SkBits2Float(0x427e0734));  // 21.562f, 63.647f, 21.616f, 63.572f, 21.692f, 63.507f

+path.cubicTo(SkBits2Float(0x41aed506), SkBits2Float(0x427d8215), SkBits2Float(0x41b020cc), SkBits2Float(0x427d137e), SkBits2Float(0x41b1831a), SkBits2Float(0x427cbb6d));  // 21.854f, 63.377f, 22.016f, 63.269f, 22.189f, 63.183f

+path.cubicTo(SkBits2Float(0x41b1f1b2), SkBits2Float(0x427c99a2), SkBits2Float(0x41b26049), SkBits2Float(0x427c6256), SkBits2Float(0x41b2cee1), SkBits2Float(0x427c4cd5));  // 22.243f, 63.15f, 22.297f, 63.096f, 22.351f, 63.075f

+path.cubicTo(SkBits2Float(0x41b3106a), SkBits2Float(0x427c2b0a), SkBits2Float(0x41b445aa), SkBits2Float(0x427bff02), SkBits2Float(0x41b49dba), SkBits2Float(0x427bde3d));  // 22.383f, 63.042f, 22.534f, 62.999f, 22.577f, 62.967f

+path.cubicTo(SkBits2Float(0x41b49dba), SkBits2Float(0x427bd2f9), SkBits2Float(0x41b4cac8), SkBits2Float(0x427ba6f1), SkBits2Float(0x41b4cac8), SkBits2Float(0x427ba6f1));  // 22.577f, 62.956f, 22.599f, 62.913f, 22.599f, 62.913f

+path.cubicTo(SkBits2Float(0x41b4cac8), SkBits2Float(0x427b6462), SkBits2Float(0x41b33b6c), SkBits2Float(0x427a4fe7), SkBits2Float(0x41b2fbef), SkBits2Float(0x427a189b));  // 22.599f, 62.848f, 22.404f, 62.578f, 22.373f, 62.524f

+path.cubicTo(SkBits2Float(0x41b2cee1), SkBits2Float(0x4279f7d6), SkBits2Float(0x41b19795), SkBits2Float(0x42796774), SkBits2Float(0x41b1560c), SkBits2Float(0x42795c31));  // 22.351f, 62.492f, 22.199f, 62.351f, 22.167f, 62.34f

+path.cubicTo(SkBits2Float(0x41b0e774), SkBits2Float(0x42793b6c), SkBits2Float(0x41aff3be), SkBits2Float(0x42795c31), SkBits2Float(0x41af70ac), SkBits2Float(0x42795c31));  // 22.113f, 62.308f, 21.994f, 62.34f, 21.93f, 62.34f

+path.cubicTo(SkBits2Float(0x41ae0e5e), SkBits2Float(0x42796775), SkBits2Float(0x41ac9589), SkBits2Float(0x427946b0), SkBits2Float(0x41ab76d1), SkBits2Float(0x42793b6c));  // 21.757f, 62.351f, 21.573f, 62.319f, 21.433f, 62.308f

+path.cubicTo(SkBits2Float(0x41aa3f85), SkBits2Float(0x42793028), SkBits2Float(0x41a94ddb), SkBits2Float(0x42793b6c), SkBits2Float(0x41a82d17), SkBits2Float(0x42793028));  // 21.281f, 62.297f, 21.163f, 62.308f, 21.022f, 62.297f

+path.cubicTo(SkBits2Float(0x41a5c298), SkBits2Float(0x42791aa7), SkBits2Float(0x41a2e775), SkBits2Float(0x4278ed99), SkBits2Float(0x41a07cf6), SkBits2Float(0x4278c190));  // 20.72f, 62.276f, 20.363f, 62.232f, 20.061f, 62.189f

+path.cubicTo(SkBits2Float(0x419f47b7), SkBits2Float(0x4278b753), SkBits2Float(0x419e810f), SkBits2Float(0x4278b753), SkBits2Float(0x419d4bcf), SkBits2Float(0x4278a0cb));  // 19.91f, 62.179f, 19.813f, 62.179f, 19.662f, 62.157f

+path.cubicTo(SkBits2Float(0x419c831b), SkBits2Float(0x42788a44), SkBits2Float(0x419b20cd), SkBits2Float(0x42785e3c), SkBits2Float(0x419a45aa), SkBits2Float(0x427847b4));  // 19.564f, 62.135f, 19.391f, 62.092f, 19.284f, 62.07f

+path.cubicTo(SkBits2Float(0x41949171), SkBits2Float(0x4277e460), SkBits2Float(0x418e5819), SkBits2Float(0x42778c50), SkBits2Float(0x41896a87), SkBits2Float(0x4275dd35));  // 18.571f, 61.973f, 17.793f, 61.887f, 17.177f, 61.466f

+path.cubicTo(SkBits2Float(0x4182efa6), SkBits2Float(0x4273a8fc), SkBits2Float(0x417fd71a), SkBits2Float(0x42703f83), SkBits2Float(0x4180dd37), SkBits2Float(0x426c5b29));  // 16.367f, 60.915f, 15.99f, 60.062f, 16.108f, 59.089f

+path.cubicTo(SkBits2Float(0x41813547), SkBits2Float(0x426b5d35), SkBits2Float(0x41821276), SkBits2Float(0x426a8006), SkBits2Float(0x4182560c), SkBits2Float(0x426976cf));  // 16.151f, 58.841f, 16.259f, 58.625f, 16.292f, 58.366f

+path.cubicTo(SkBits2Float(0x418228fe), SkBits2Float(0x426976cf), SkBits2Float(0x41823f85), SkBits2Float(0x42696b8b), SkBits2Float(0x418228fe), SkBits2Float(0x42694ac7));  // 16.27f, 58.366f, 16.281f, 58.355f, 16.27f, 58.323f

+path.cubicTo(SkBits2Float(0x4181a5ec), SkBits2Float(0x42696b8c), SkBits2Float(0x41813548), SkBits2Float(0x42696b8c), SkBits2Float(0x41809bae), SkBits2Float(0x4269560b));  // 16.206f, 58.355f, 16.151f, 58.355f, 16.076f, 58.334f

+path.cubicTo(SkBits2Float(0x4180b235), SkBits2Float(0x4269560b), SkBits2Float(0x4180439e), SkBits2Float(0x426976d0), SkBits2Float(0x4180168f), SkBits2Float(0x42696b8c));  // 16.087f, 58.334f, 16.033f, 58.366f, 16.011f, 58.355f

+path.cubicTo(SkBits2Float(0x417eccdc), SkBits2Float(0x4269560b), SkBits2Float(0x417e9fce), SkBits2Float(0x4268d0ec), SkBits2Float(0x417f4fef), SkBits2Float(0x42688319));  // 15.925f, 58.334f, 15.914f, 58.204f, 15.957f, 58.128f

+path.cubicTo(SkBits2Float(0x4180168f), SkBits2Float(0x4268364c), SkBits2Float(0x41849589), SkBits2Float(0x4267a5ea), SkBits2Float(0x4185b441), SkBits2Float(0x42679069));  // 16.011f, 58.053f, 16.573f, 57.912f, 16.713f, 57.891f

+path.cubicTo(SkBits2Float(0x41891276), SkBits2Float(0x42674296), SkBits2Float(0x418c9dba), SkBits2Float(0x4266df42), SkBits2Float(0x418fd0ed), SkBits2Float(0x4266916f));  // 17.134f, 57.815f, 17.577f, 57.718f, 17.977f, 57.642f

+path.cubicTo(SkBits2Float(0x4190ae1c), SkBits2Float(0x42668732), SkBits2Float(0x4191333b), SkBits2Float(0x42668732), SkBits2Float(0x4192106a), SkBits2Float(0x426670aa));  // 18.085f, 57.632f, 18.15f, 57.632f, 18.258f, 57.61f

+path.cubicTo(SkBits2Float(0x4193189b), SkBits2Float(0x42665a23), SkBits2Float(0x4194a5eb), SkBits2Float(0x426622d7), SkBits2Float(0x4195dd37), SkBits2Float(0x42660d56));  // 18.387f, 57.588f, 18.581f, 57.534f, 18.733f, 57.513f

+path.cubicTo(SkBits2Float(0x41975400), SkBits2Float(0x4265e254), SkBits2Float(0x41988b4b), SkBits2Float(0x4265c18f), SkBits2Float(0x4199d506), SkBits2Float(0x4265ac0e));  // 18.916f, 57.471f, 19.068f, 57.439f, 19.229f, 57.418f

+path.moveTo(SkBits2Float(0x41a4e568), SkBits2Float(0x4277d0eb));  // 20.612f, 61.954f

+path.cubicTo(SkBits2Float(0x41a4cee1), SkBits2Float(0x4277d0eb), SkBits2Float(0x41a48d58), SkBits2Float(0x4277f1b0), SkBits2Float(0x41a48d58), SkBits2Float(0x4277f1b0));  // 20.601f, 61.954f, 20.569f, 61.986f, 20.569f, 61.986f

+path.cubicTo(SkBits2Float(0x41a3831b), SkBits2Float(0x42781275), SkBits2Float(0x41a0c08b), SkBits2Float(0x4277c5a8), SkBits2Float(0x419fe35c), SkBits2Float(0x4277af21));  // 20.439f, 62.018f, 20.094f, 61.943f, 19.986f, 61.921f

+path.cubicTo(SkBits2Float(0x419dd0ed), SkBits2Float(0x42778319), SkBits2Float(0x419bbc73), SkBits2Float(0x42775711), SkBits2Float(0x4199c08b), SkBits2Float(0x42771481));  // 19.727f, 61.878f, 19.467f, 61.835f, 19.219f, 61.77f

+path.cubicTo(SkBits2Float(0x4199687b), SkBits2Float(0x4277093d), SkBits2Float(0x4198f7d7), SkBits2Float(0x4276f3bc), SkBits2Float(0x4198b64e), SkBits2Float(0x4276dd35));  // 19.176f, 61.759f, 19.121f, 61.738f, 19.089f, 61.716f

+path.cubicTo(SkBits2Float(0x419847b6), SkBits2Float(0x4276d1f1), SkBits2Float(0x4198062d), SkBits2Float(0x4276dd35), SkBits2Float(0x4197ae1d), SkBits2Float(0x4276d1f1));  // 19.035f, 61.705f, 19.003f, 61.716f, 18.96f, 61.705f

+path.cubicTo(SkBits2Float(0x4196fbf0), SkBits2Float(0x4276c6ad), SkBits2Float(0x4196083a), SkBits2Float(0x42768f62), SkBits2Float(0x4195831b), SkBits2Float(0x427679e1));  // 18.873f, 61.694f, 18.754f, 61.64f, 18.689f, 61.619f

+path.cubicTo(SkBits2Float(0x41951690), SkBits2Float(0x4276635a), SkBits2Float(0x41950009), SkBits2Float(0x427679e1), SkBits2Float(0x4194a5ec), SkBits2Float(0x4276635a));  // 18.636f, 61.597f, 18.625f, 61.619f, 18.581f, 61.597f

+path.cubicTo(SkBits2Float(0x41940c52), SkBits2Float(0x42764dd9), SkBits2Float(0x41935a25), SkBits2Float(0x4276168d), SkBits2Float(0x4192c08c), SkBits2Float(0x42760006));  // 18.506f, 61.576f, 18.419f, 61.522f, 18.344f, 61.5f

+path.cubicTo(SkBits2Float(0x4190c298), SkBits2Float(0x42759cb2), SkBits2Float(0x418f6257), SkBits2Float(0x427544a2), SkBits2Float(0x418e2b0b), SkBits2Float(0x42748837));  // 18.095f, 61.403f, 17.923f, 61.317f, 17.771f, 61.133f

+path.cubicTo(SkBits2Float(0x418e1690), SkBits2Float(0x4274666c), SkBits2Float(0x418dd2fb), SkBits2Float(0x4274666c), SkBits2Float(0x418dbe80), SkBits2Float(0x42745c2f));  // 17.761f, 61.1f, 17.728f, 61.1f, 17.718f, 61.09f

+path.cubicTo(SkBits2Float(0x418da7f9), SkBits2Float(0x42742f21), SkBits2Float(0x418da7f9), SkBits2Float(0x42740e5c), SkBits2Float(0x418d6670), SkBits2Float(0x4273ed97));  // 17.707f, 61.046f, 17.707f, 61.014f, 17.675f, 60.982f

+path.cubicTo(SkBits2Float(0x418d22da), SkBits2Float(0x42739fc4), SkBits2Float(0x418ccaca), SkBits2Float(0x427373bc), SkBits2Float(0x418c9dbc), SkBits2Float(0x42731aa5));  // 17.642f, 60.906f, 17.599f, 60.863f, 17.577f, 60.776f

+path.cubicTo(SkBits2Float(0x418bd714), SkBits2Float(0x4271b95d), SkBits2Float(0x418d22db), SkBits2Float(0x4270999f), SkBits2Float(0x418fd0ef), SkBits2Float(0x4270418e));  // 17.48f, 60.431f, 17.642f, 60.15f, 17.977f, 60.064f

+path.cubicTo(SkBits2Float(0x41919fc8), SkBits2Float(0x426ffeff), SkBits2Float(0x4193df45), SkBits2Float(0x42701fc3), SkBits2Float(0x4195f3c0), SkBits2Float(0x4270841d));  // 18.203f, 59.999f, 18.484f, 60.031f, 18.744f, 60.129f

+path.cubicTo(SkBits2Float(0x419847b8), SkBits2Float(0x4270e771), SkBits2Float(0x419a5a26), SkBits2Float(0x42718211), SkBits2Float(0x419bd2fb), SkBits2Float(0x42723231));  // 19.035f, 60.226f, 19.294f, 60.377f, 19.478f, 60.549f

+path.cubicTo(SkBits2Float(0x419be982), SkBits2Float(0x42723e7b), SkBits2Float(0x419be982), SkBits2Float(0x42726a83), SkBits2Float(0x419c1484), SkBits2Float(0x42726a83));  // 19.489f, 60.561f, 19.489f, 60.604f, 19.51f, 60.604f

+path.cubicTo(SkBits2Float(0x419c4192), SkBits2Float(0x42728004), SkBits2Float(0x419c831c), SkBits2Float(0x42728004), SkBits2Float(0x419c99a3), SkBits2Float(0x4272968b));  // 19.532f, 60.625f, 19.564f, 60.625f, 19.575f, 60.647f

+path.cubicTo(SkBits2Float(0x419cdb2c), SkBits2Float(0x4272b750), SkBits2Float(0x419d083b), SkBits2Float(0x4272ee9b), SkBits2Float(0x419d3549), SkBits2Float(0x427325e7));  // 19.607f, 60.679f, 19.629f, 60.733f, 19.651f, 60.787f

+path.cubicTo(SkBits2Float(0x419e28ff), SkBits2Float(0x4273cbca), SkBits2Float(0x419f062e), SkBits2Float(0x4274666a), SkBits2Float(0x419ff7d8), SkBits2Float(0x42750c4d));  // 19.77f, 60.949f, 19.878f, 61.1f, 19.996f, 61.262f

+path.cubicTo(SkBits2Float(0x41a0c08c), SkBits2Float(0x42758628), SkBits2Float(0x41a1f5cc), SkBits2Float(0x4275df3f), SkBits2Float(0x41a2d2fb), SkBits2Float(0x42766357));  // 20.094f, 61.381f, 20.245f, 61.468f, 20.353f, 61.597f

+path.cubicTo(SkBits2Float(0x41a31484), SkBits2Float(0x42769aa3), SkBits2Float(0x41a36c95), SkBits2Float(0x4276f3b9), SkBits2Float(0x41a3db2c), SkBits2Float(0x42771fc1));  // 20.385f, 61.651f, 20.428f, 61.738f, 20.482f, 61.781f

+path.cubicTo(SkBits2Float(0x41a4083a), SkBits2Float(0x42774bc9), SkBits2Float(0x41a4b85b), SkBits2Float(0x42778315), SkBits2Float(0x41a4e569), SkBits2Float(0x4277af1d));  // 20.504f, 61.824f, 20.59f, 61.878f, 20.612f, 61.921f

+path.cubicTo(SkBits2Float(0x41a4e569), SkBits2Float(0x4277ba61), SkBits2Float(0x41a4cee2), SkBits2Float(0x4277c5a4), SkBits2Float(0x41a4e569), SkBits2Float(0x4277d0e8));  // 20.612f, 61.932f, 20.601f, 61.943f, 20.612f, 61.954f

+path.moveTo(SkBits2Float(0x41ad72b9), SkBits2Float(0x42786044));  // 21.681f, 62.094f

+path.cubicTo(SkBits2Float(0x41ac106b), SkBits2Float(0x42788c4c), SkBits2Float(0x41a9d0ee), SkBits2Float(0x4277d0e8), SkBits2Float(0x41a8b236), SkBits2Float(0x42778e58));  // 21.508f, 62.137f, 21.227f, 61.954f, 21.087f, 61.889f

+path.cubicTo(SkBits2Float(0x41a2fdfd), SkBits2Float(0x42761689), SkBits2Float(0x41a10215), SkBits2Float(0x42733c6c), SkBits2Float(0x419fb64f), SkBits2Float(0x42704ccf));  // 20.374f, 61.522f, 20.126f, 60.809f, 19.964f, 60.075f

+path.cubicTo(SkBits2Float(0x419f9fc8), SkBits2Float(0x42700a40), SkBits2Float(0x419f47b7), SkBits2Float(0x426f9ba8), SkBits2Float(0x419f3130), SkBits2Float(0x426f5919));  // 19.953f, 60.01f, 19.91f, 59.902f, 19.899f, 59.837f

+path.cubicTo(SkBits2Float(0x419f3130), SkBits2Float(0x426f0b46), SkBits2Float(0x419f47b7), SkBits2Float(0x426ec9bd), SkBits2Float(0x419f3130), SkBits2Float(0x426e70a6));  // 19.899f, 59.761f, 19.91f, 59.697f, 19.899f, 59.61f

+path.cubicTo(SkBits2Float(0x419f1aa9), SkBits2Float(0x426de14a), SkBits2Float(0x419f062e), SkBits2Float(0x426ced94), SkBits2Float(0x419f3130), SkBits2Float(0x426c5d31));  // 19.888f, 59.47f, 19.878f, 59.232f, 19.899f, 59.091f

+path.cubicTo(SkBits2Float(0x419f72b9), SkBits2Float(0x426befa0), SkBits2Float(0x419fe35d), SkBits2Float(0x426b8108), SkBits2Float(0x41a00e5f), SkBits2Float(0x426b3335));  // 19.931f, 58.984f, 19.986f, 58.876f, 20.007f, 58.8f

+path.cubicTo(SkBits2Float(0x41a0666f), SkBits2Float(0x426acfe1), SkBits2Float(0x41a10215), SkBits2Float(0x4269c6aa), SkBits2Float(0x41a19dbb), SkBits2Float(0x4269bb66));  // 20.05f, 58.703f, 20.126f, 58.444f, 20.202f, 58.433f

+path.cubicTo(SkBits2Float(0x41a220cd), SkBits2Float(0x4269bb66), SkBits2Float(0x41a2a5ec), SkBits2Float(0x4269f2b2), SkBits2Float(0x41a31484), SkBits2Float(0x426a3f7f));  // 20.266f, 58.433f, 20.331f, 58.487f, 20.385f, 58.562f

+path.cubicTo(SkBits2Float(0x41a3c6b1), SkBits2Float(0x426aa3d9), SkBits2Float(0x41a449c3), SkBits2Float(0x426b1cae), SkBits2Float(0x41a476d2), SkBits2Float(0x426b3e79));  // 20.472f, 58.66f, 20.536f, 58.778f, 20.558f, 58.811f

+path.cubicTo(SkBits2Float(0x41a5ac11), SkBits2Float(0x426c0521), SkBits2Float(0x41a6caca), SkBits2Float(0x426ce250), SkBits2Float(0x41a8189d), SkBits2Float(0x426da9fe));  // 20.709f, 59.005f, 20.849f, 59.221f, 21.012f, 59.416f

+path.cubicTo(SkBits2Float(0x41aa3f86), SkBits2Float(0x426f1689), SkBits2Float(0x41ac5401), SkBits2Float(0x4270841b), SkBits2Float(0x41ae7aeb), SkBits2Float(0x4271f0a6));  // 21.281f, 59.772f, 21.541f, 60.129f, 21.81f, 60.485f

+path.cubicTo(SkBits2Float(0x41af000a), SkBits2Float(0x427248b6), SkBits2Float(0x41afb237), SkBits2Float(0x4272a1cd), SkBits2Float(0x41b020ce), SkBits2Float(0x4272ee9a));  // 21.875f, 60.571f, 21.962f, 60.658f, 22.016f, 60.733f

+path.cubicTo(SkBits2Float(0x41b06257), SkBits2Float(0x42731aa2), SkBits2Float(0x41b19797), SkBits2Float(0x4273f7d1), SkBits2Float(0x41b19797), SkBits2Float(0x4274199c));  // 22.048f, 60.776f, 22.199f, 60.992f, 22.199f, 61.025f

+path.cubicTo(SkBits2Float(0x41b1c4a5), SkBits2Float(0x427424e0), SkBits2Float(0x41b1831c), SkBits2Float(0x42746669), SkBits2Float(0x41b1831c), SkBits2Float(0x42746669));  // 22.221f, 61.036f, 22.189f, 61.1f, 22.189f, 61.1f

+path.cubicTo(SkBits2Float(0x41ac3d7a), SkBits2Float(0x42742f1d), SkBits2Float(0x41a96257), SkBits2Float(0x4271ae17), SkBits2Float(0x41a7a7f9), SkBits2Float(0x426fb12a));  // 21.53f, 61.046f, 21.173f, 60.42f, 20.957f, 59.923f

+path.cubicTo(SkBits2Float(0x41a77cf7), SkBits2Float(0x426f9ba9), SkBits2Float(0x41a73b6e), SkBits2Float(0x426f79de), SkBits2Float(0x41a73b6e), SkBits2Float(0x426f591a));  // 20.936f, 59.902f, 20.904f, 59.869f, 20.904f, 59.837f

+path.cubicTo(SkBits2Float(0x41a6e151), SkBits2Float(0x426eea82), SkBits2Float(0x41a68941), SkBits2Float(0x426e6564), SkBits2Float(0x41a672ba), SkBits2Float(0x426dec8f));  // 20.86f, 59.729f, 20.817f, 59.599f, 20.806f, 59.481f

+path.cubicTo(SkBits2Float(0x41a65e3f), SkBits2Float(0x426daa00), SkBits2Float(0x41a68941), SkBits2Float(0x426d71ae), SkBits2Float(0x41a65e3f), SkBits2Float(0x426d50e9));  // 20.796f, 59.416f, 20.817f, 59.361f, 20.796f, 59.329f

+path.cubicTo(SkBits2Float(0x41a63131), SkBits2Float(0x426d24e1), SkBits2Float(0x41a56a89), SkBits2Float(0x426cf8d9), SkBits2Float(0x41a4fbf1), SkBits2Float(0x426cf8d9));  // 20.774f, 59.286f, 20.677f, 59.243f, 20.623f, 59.243f

+path.cubicTo(SkBits2Float(0x41a449c4), SkBits2Float(0x426ced95), SkBits2Float(0x41a36c95), SkBits2Float(0x426cf8d9), SkBits2Float(0x41a31484), SkBits2Float(0x426d24e1));  // 20.536f, 59.232f, 20.428f, 59.243f, 20.385f, 59.286f

+path.cubicTo(SkBits2Float(0x41a20a47), SkBits2Float(0x426d71ae), SkBits2Float(0x41a1f5cc), SkBits2Float(0x426f645e), SkBits2Float(0x41a220ce), SkBits2Float(0x42701fc2));  // 20.255f, 59.361f, 20.245f, 59.848f, 20.266f, 60.031f

+path.cubicTo(SkBits2Float(0x41a28f66), SkBits2Float(0x4272e45e), SkBits2Float(0x41a4b85b), SkBits2Float(0x4274c9be), SkBits2Float(0x41a7eb8e), SkBits2Float(0x427621ce));  // 20.32f, 60.723f, 20.59f, 61.197f, 20.99f, 61.533f

+path.cubicTo(SkBits2Float(0x41a82d17), SkBits2Float(0x42764293), SkBits2Float(0x41a870ad), SkBits2Float(0x42764293), SkBits2Float(0x41a8b236), SkBits2Float(0x4276591a));  // 21.022f, 61.565f, 21.055f, 61.565f, 21.087f, 61.587f

+path.cubicTo(SkBits2Float(0x41a90a46), SkBits2Float(0x427679df), SkBits2Float(0x41a93755), SkBits2Float(0x4276b12a), SkBits2Float(0x41a98f65), SkBits2Float(0x4276c6ab));  // 21.13f, 61.619f, 21.152f, 61.673f, 21.195f, 61.694f

+path.cubicTo(SkBits2Float(0x41aadb2c), SkBits2Float(0x42774086), SkBits2Float(0x41ac958a), SkBits2Float(0x42778e59), SkBits2Float(0x41adb64e), SkBits2Float(0x427828f9));  // 21.357f, 61.813f, 21.573f, 61.889f, 21.714f, 62.04f

+path.cubicTo(SkBits2Float(0x41adb64e), SkBits2Float(0x427828f9), SkBits2Float(0x41ad8940), SkBits2Float(0x42786045), SkBits2Float(0x41ad72b8), SkBits2Float(0x42786045));  // 21.714f, 62.04f, 21.692f, 62.094f, 21.681f, 62.094f

+path.moveTo(SkBits2Float(0x41bd168f), SkBits2Float(0x4267be7a));  // 23.636f, 57.936f

+path.cubicTo(SkBits2Float(0x41bd168f), SkBits2Float(0x42679caf), SkBits2Float(0x41bd2d16), SkBits2Float(0x4267666a), SkBits2Float(0x41bd168f), SkBits2Float(0x42674fe2));  // 23.636f, 57.903f, 23.647f, 57.85f, 23.636f, 57.828f

+path.cubicTo(SkBits2Float(0x41bd168f), SkBits2Float(0x4267449e), SkBits2Float(0x41bd0008), SkBits2Float(0x42674fe2), SkBits2Float(0x41bce981), SkBits2Float(0x42672f1d));  // 23.636f, 57.817f, 23.625f, 57.828f, 23.614f, 57.796f

+path.cubicTo(SkBits2Float(0x41bcd2fa), SkBits2Float(0x42672f1d), SkBits2Float(0x41bc9171), SkBits2Float(0x4267449e), SkBits2Float(0x41bc7ae9), SkBits2Float(0x42672f1d));  // 23.603f, 57.796f, 23.571f, 57.817f, 23.56f, 57.796f

+path.cubicTo(SkBits2Float(0x41bb9dba), SkBits2Float(0x4267d500), SkBits2Float(0x41bbb441), SkBits2Float(0x42693648), SkBits2Float(0x41bb72b8), SkBits2Float(0x426a1377));  // 23.452f, 57.958f, 23.463f, 58.303f, 23.431f, 58.519f

+path.cubicTo(SkBits2Float(0x41bb45aa), SkBits2Float(0x426a6c8e), SkBits2Float(0x41bb2f22), SkBits2Float(0x426acfe1), SkBits2Float(0x41bb189b), SkBits2Float(0x426b3335));  // 23.409f, 58.606f, 23.398f, 58.703f, 23.387f, 58.8f

+path.lineTo(SkBits2Float(0x41baed99), SkBits2Float(0x426b5f3d));  // 23.366f, 58.843f

+path.cubicTo(SkBits2Float(0x41baac10), SkBits2Float(0x426bd918), SkBits2Float(0x41bac08b), SkBits2Float(0x426c3129), SkBits2Float(0x41baac10), SkBits2Float(0x426cab04));  // 23.334f, 58.962f, 23.344f, 59.048f, 23.334f, 59.167f

+path.cubicTo(SkBits2Float(0x41ba7f02), SkBits2Float(0x426d50e7), SkBits2Float(0x41ba3b6c), SkBits2Float(0x426e0d52), SkBits2Float(0x41ba106a), SkBits2Float(0x426ec9bc));  // 23.312f, 59.329f, 23.279f, 59.513f, 23.258f, 59.697f

+path.cubicTo(SkBits2Float(0x41b9ccd4), SkBits2Float(0x426f645c), SkBits2Float(0x41b974c4), SkBits2Float(0x42701fc0), SkBits2Float(0x41b949c2), SkBits2Float(0x4270c5a3));  // 23.225f, 59.848f, 23.182f, 60.031f, 23.161f, 60.193f

+path.cubicTo(SkBits2Float(0x41b9333b), SkBits2Float(0x42713f7e), SkBits2Float(0x41b98b4b), SkBits2Float(0x4271820d), SkBits2Float(0x41b9f9e3), SkBits2Float(0x4271ae16));  // 23.15f, 60.312f, 23.193f, 60.377f, 23.247f, 60.42f

+path.cubicTo(SkBits2Float(0x41ba3b6c), SkBits2Float(0x42718d51), SkBits2Float(0x41ba7f02), SkBits2Float(0x4271b95a), SkBits2Float(0x41ba9589), SkBits2Float(0x42716b87));  // 23.279f, 60.388f, 23.312f, 60.431f, 23.323f, 60.355f

+path.cubicTo(SkBits2Float(0x41baac10), SkBits2Float(0x4271343b), SkBits2Float(0x41ba9589), SkBits2Float(0x4270e76e), SkBits2Float(0x41ba9589), SkBits2Float(0x4270999b));  // 23.334f, 60.301f, 23.323f, 60.226f, 23.323f, 60.15f

+path.cubicTo(SkBits2Float(0x41ba9589), SkBits2Float(0x4270418b), SkBits2Float(0x41bac08b), SkBits2Float(0x426fd1ed), SkBits2Float(0x41baed99), SkBits2Float(0x426f645c));  // 23.323f, 60.064f, 23.344f, 59.955f, 23.366f, 59.848f

+path.cubicTo(SkBits2Float(0x41bb2f22), SkBits2Float(0x426e6562), SkBits2Float(0x41bb9dba), SkBits2Float(0x426d3b66), SkBits2Float(0x41bbf5ca), SkBits2Float(0x426c3c6c));  // 23.398f, 59.599f, 23.452f, 59.308f, 23.495f, 59.059f

+path.cubicTo(SkBits2Float(0x41bc0e5d), SkBits2Float(0x426bb853), SkBits2Float(0x41bc0e5d), SkBits2Float(0x426b5f3d), SkBits2Float(0x41bc22d8), SkBits2Float(0x426ae562));  // 23.507f, 58.93f, 23.507f, 58.843f, 23.517f, 58.724f

+path.cubicTo(SkBits2Float(0x41bc395f), SkBits2Float(0x426a820e), SkBits2Float(0x41bc9170), SkBits2Float(0x4269f2b2), SkBits2Float(0x41bca7f7), SkBits2Float(0x42698f5e));  // 23.528f, 58.627f, 23.571f, 58.487f, 23.582f, 58.39f

+path.cubicTo(SkBits2Float(0x41bcd2f9), SkBits2Float(0x426920c6), SkBits2Float(0x41bca7f7), SkBits2Float(0x4268d2f4), SkBits2Float(0x41bcd2f9), SkBits2Float(0x4268645c));  // 23.603f, 58.282f, 23.582f, 58.206f, 23.603f, 58.098f

+path.cubicTo(SkBits2Float(0x41bcd2f9), SkBits2Float(0x42684291), SkBits2Float(0x41bd168f), SkBits2Float(0x4267df3d), SkBits2Float(0x41bd168f), SkBits2Float(0x4267be79));  // 23.603f, 58.065f, 23.636f, 57.968f, 23.636f, 57.936f

+path.moveTo(SkBits2Float(0x41bd6e9f), SkBits2Float(0x426e916b));  // 23.679f, 59.642f

+path.cubicTo(SkBits2Float(0x41bdb028), SkBits2Float(0x426d199c), SkBits2Float(0x41bdf3be), SkBits2Float(0x426bb854), SkBits2Float(0x41be6255), SkBits2Float(0x426a343c));  // 23.711f, 59.275f, 23.744f, 58.93f, 23.798f, 58.551f

+path.cubicTo(SkBits2Float(0x41be78dc), SkBits2Float(0x4269f2b3), SkBits2Float(0x41bed0ed), SkBits2Float(0x4269841b), SkBits2Float(0x41bed0ed), SkBits2Float(0x4269418c));  // 23.809f, 58.487f, 23.852f, 58.379f, 23.852f, 58.314f

+path.cubicTo(SkBits2Float(0x41bee774), SkBits2Float(0x4268bc6d), SkBits2Float(0x41bee774), SkBits2Float(0x42684edc), SkBits2Float(0x41bf1276), SkBits2Float(0x4267df3e));  // 23.863f, 58.184f, 23.863f, 58.077f, 23.884f, 57.968f

+path.cubicTo(SkBits2Float(0x41bf3f84), SkBits2Float(0x4267a7f2), SkBits2Float(0x41bf3f84), SkBits2Float(0x4267872e), SkBits2Float(0x41bf9795), SkBits2Float(0x426770a6));  // 23.906f, 57.914f, 23.906f, 57.882f, 23.949f, 57.86f

+path.cubicTo(SkBits2Float(0x41c0ccd4), SkBits2Float(0x42675b25), SkBits2Float(0x41c6810e), SkBits2Float(0x4268d2f4), SkBits2Float(0x41c6d91e), SkBits2Float(0x426920c7));  // 24.1f, 57.839f, 24.813f, 58.206f, 24.856f, 58.282f

+path.cubicTo(SkBits2Float(0x41c7333b), SkBits2Float(0x42696d94), SkBits2Float(0x41c7062c), SkBits2Float(0x4270e76f), SkBits2Float(0x41c6ae1c), SkBits2Float(0x42713f7f));  // 24.9f, 58.357f, 24.878f, 60.226f, 24.835f, 60.312f

+path.cubicTo(SkBits2Float(0x41c63f84), SkBits2Float(0x4271a2d3), SkBits2Float(0x41c3a7f7), SkBits2Float(0x42716b87), SkBits2Float(0x41c2cac8), SkBits2Float(0x427176cb));  // 24.781f, 60.409f, 24.457f, 60.355f, 24.349f, 60.366f

+path.cubicTo(SkBits2Float(0x41c2b441), SkBits2Float(0x427176cb), SkBits2Float(0x41c270ab), SkBits2Float(0x4271a2d3), SkBits2Float(0x41c245a9), SkBits2Float(0x4271a2d3));  // 24.338f, 60.366f, 24.305f, 60.409f, 24.284f, 60.409f

+path.cubicTo(SkBits2Float(0x41c1aa03), SkBits2Float(0x4271b95a), SkBits2Float(0x41c1106a), SkBits2Float(0x4271ae17), SkBits2Float(0x41c05e3c), SkBits2Float(0x4271b95a));  // 24.208f, 60.431f, 24.133f, 60.42f, 24.046f, 60.431f

+path.cubicTo(SkBits2Float(0x41bf1275), SkBits2Float(0x4271e562), SkBits2Float(0x41be4bcd), SkBits2Float(0x427227f2), SkBits2Float(0x41bcd2f8), SkBits2Float(0x4272322f));  // 23.884f, 60.474f, 23.787f, 60.539f, 23.603f, 60.549f

+path.cubicTo(SkBits2Float(0x41bc395e), SkBits2Float(0x427128f8), SkBits2Float(0x41bd2d15), SkBits2Float(0x426f8f5e), SkBits2Float(0x41bd6e9e), SkBits2Float(0x426e916a));  // 23.528f, 60.29f, 23.647f, 59.89f, 23.679f, 59.642f

+path.moveTo(SkBits2Float(0x41d21481), SkBits2Float(0x42700a3f));  // 26.26f, 60.01f

+path.cubicTo(SkBits2Float(0x41d22b08), SkBits2Float(0x42704cce), SkBits2Float(0x41d299a0), SkBits2Float(0x4270f1ac), SkBits2Float(0x41d2418f), SkBits2Float(0x42713f7e));  // 26.271f, 60.075f, 26.325f, 60.236f, 26.282f, 60.312f

+path.cubicTo(SkBits2Float(0x41d2418f), SkBits2Float(0x42714ac2), SkBits2Float(0x41d22b08), SkBits2Float(0x42713f7e), SkBits2Float(0x41d21481), SkBits2Float(0x42715605));  // 26.282f, 60.323f, 26.271f, 60.312f, 26.26f, 60.334f

+path.cubicTo(SkBits2Float(0x41d1bc71), SkBits2Float(0x42715605), SkBits2Float(0x41d1916f), SkBits2Float(0x42715605), SkBits2Float(0x41d1395e), SkBits2Float(0x42714ac1));  // 26.217f, 60.334f, 26.196f, 60.334f, 26.153f, 60.323f

+path.cubicTo(SkBits2Float(0x41d0b233), SkBits2Float(0x42708419), SkBits2Float(0x41d0c8ba), SkBits2Float(0x426f645b), SkBits2Float(0x41d09db8), SkBits2Float(0x426e5a1d));  // 26.087f, 60.129f, 26.098f, 59.848f, 26.077f, 59.588f

+path.cubicTo(SkBits2Float(0x41d09db8), SkBits2Float(0x426e23d7), SkBits2Float(0x41d05a22), SkBits2Float(0x426d9375), SkBits2Float(0x41d070aa), SkBits2Float(0x426d50e6));  // 26.077f, 59.535f, 26.044f, 59.394f, 26.055f, 59.329f

+path.cubicTo(SkBits2Float(0x41d09db8), SkBits2Float(0x426d3b65), SkBits2Float(0x41d0b233), SkBits2Float(0x426d50e6), SkBits2Float(0x41d0b233), SkBits2Float(0x426d2f1b));  // 26.077f, 59.308f, 26.087f, 59.329f, 26.087f, 59.296f

+path.cubicTo(SkBits2Float(0x41d1395e), SkBits2Float(0x426d3b65), SkBits2Float(0x41d14dd9), SkBits2Float(0x426d2f1b), SkBits2Float(0x41d1916e), SkBits2Float(0x426d50e6));  // 26.153f, 59.308f, 26.163f, 59.296f, 26.196f, 59.329f

+path.cubicTo(SkBits2Float(0x41d1a5e9), SkBits2Float(0x426d50e6), SkBits2Float(0x41d1e97e), SkBits2Float(0x426de148), SkBits2Float(0x41d1e97e), SkBits2Float(0x426dec8c));  // 26.206f, 59.329f, 26.239f, 59.47f, 26.239f, 59.481f

+path.cubicTo(SkBits2Float(0x41d22b07), SkBits2Float(0x426e9cad), SkBits2Float(0x41d1e97e), SkBits2Float(0x426f4dd4), SkBits2Float(0x41d21480), SkBits2Float(0x42700a3e));  // 26.271f, 59.653f, 26.239f, 59.826f, 26.26f, 60.01f

+path.moveTo(SkBits2Float(0x41ee1274), SkBits2Float(0x42564ac1));  // 29.759f, 53.573f

+path.cubicTo(SkBits2Float(0x41ee1274), SkBits2Float(0x42566b86), SkBits2Float(0x41ee3f82), SkBits2Float(0x4256c49c), SkBits2Float(0x41ee28fb), SkBits2Float(0x4256fbe8));  // 29.759f, 53.605f, 29.781f, 53.692f, 29.77f, 53.746f

+path.cubicTo(SkBits2Float(0x41ee28fb), SkBits2Float(0x42571cad), SkBits2Float(0x41ede772), SkBits2Float(0x425748b5), SkBits2Float(0x41ede772), SkBits2Float(0x42576a80));  // 29.77f, 53.778f, 29.738f, 53.821f, 29.738f, 53.854f

+path.cubicTo(SkBits2Float(0x41ed8f62), SkBits2Float(0x425774bd), SkBits2Float(0x41ed20ca), SkBits2Float(0x42579688), SkBits2Float(0x41ec6e9d), SkBits2Float(0x42579688));  // 29.695f, 53.864f, 29.641f, 53.897f, 29.554f, 53.897f

+path.cubicTo(SkBits2Float(0x41ebeb8b), SkBits2Float(0x42579688), SkBits2Float(0x41eb666c), SkBits2Float(0x425774bd), SkBits2Float(0x41eaf7d4), SkBits2Float(0x42576a80));  // 29.49f, 53.897f, 29.425f, 53.864f, 29.371f, 53.854f

+path.cubicTo(SkBits2Float(0x41eacac6), SkBits2Float(0x425676ca), SkBits2Float(0x41eb666c), SkBits2Float(0x42556d92), SkBits2Float(0x41ebbe7c), SkBits2Float(0x42549063));  // 29.349f, 53.616f, 29.425f, 53.357f, 29.468f, 53.141f

+path.cubicTo(SkBits2Float(0x41ebd503), SkBits2Float(0x425421cb), SkBits2Float(0x41ebd503), SkBits2Float(0x4253d3f9), SkBits2Float(0x41ec0005), SkBits2Float(0x42537be8));  // 29.479f, 53.033f, 29.479f, 52.957f, 29.5f, 52.871f

+path.cubicTo(SkBits2Float(0x41ec2d13), SkBits2Float(0x42535a1d), SkBits2Float(0x41ec6e9d), SkBits2Float(0x42531894), SkBits2Float(0x41ecb232), SkBits2Float(0x42531894));  // 29.522f, 52.838f, 29.554f, 52.774f, 29.587f, 52.774f

+path.cubicTo(SkBits2Float(0x41ed3544), SkBits2Float(0x4253020d), SkBits2Float(0x41edd0ea), SkBits2Float(0x42531894), SkBits2Float(0x41ede771), SkBits2Float(0x4253449c));  // 29.651f, 52.752f, 29.727f, 52.774f, 29.738f, 52.817f

+path.cubicTo(SkBits2Float(0x41ee1273), SkBits2Float(0x42534fe0), SkBits2Float(0x41ede771), SkBits2Float(0x42536561), SkBits2Float(0x41ede771), SkBits2Float(0x42537be8));  // 29.759f, 52.828f, 29.738f, 52.849f, 29.738f, 52.871f

+path.cubicTo(SkBits2Float(0x41ee3f81), SkBits2Float(0x42544290), SkBits2Float(0x41ede771), SkBits2Float(0x42554ccd), SkBits2Float(0x41ee1273), SkBits2Float(0x42564ac1));  // 29.781f, 53.065f, 29.738f, 53.325f, 29.759f, 53.573f

+path.moveTo(SkBits2Float(0x41f51273), SkBits2Float(0x4258cbc7));  // 30.634f, 54.199f

+path.cubicTo(SkBits2Float(0x41f4e771), SkBits2Float(0x4259199a), SkBits2Float(0x41f3b025), SkBits2Float(0x4259bf7d), SkBits2Float(0x41f35815), SkBits2Float(0x4259eb85));  // 30.613f, 54.275f, 30.461f, 54.437f, 30.418f, 54.48f

+path.cubicTo(SkBits2Float(0x41f2395d), SkBits2Float(0x425aa6e9), SkBits2Float(0x41f2395d), SkBits2Float(0x425a449c), SkBits2Float(0x41f222d6), SkBits2Float(0x42596666));  // 30.278f, 54.663f, 30.278f, 54.567f, 30.267f, 54.35f

+path.cubicTo(SkBits2Float(0x41f222d6), SkBits2Float(0x425945a1), SkBits2Float(0x41f1f5c8), SkBits2Float(0x4258e24d), SkBits2Float(0x41f222d6), SkBits2Float(0x4258ab02));  // 30.267f, 54.318f, 30.245f, 54.221f, 30.267f, 54.167f

+path.cubicTo(SkBits2Float(0x41f2395d), SkBits2Float(0x42589fbe), SkBits2Float(0x41f2e97e), SkBits2Float(0x42588a3d), SkBits2Float(0x41f30005), SkBits2Float(0x425873b6));  // 30.278f, 54.156f, 30.364f, 54.135f, 30.375f, 54.113f

+path.cubicTo(SkBits2Float(0x41f3b026), SkBits2Float(0x42586872), SkBits2Float(0x41f48d55), SkBits2Float(0x42588937), SkBits2Float(0x41f51274), SkBits2Float(0x4258947b));  // 30.461f, 54.102f, 30.569f, 54.134f, 30.634f, 54.145f

+path.cubicTo(SkBits2Float(0x41f4fdf9), SkBits2Float(0x42589fbf), SkBits2Float(0x41f51274), SkBits2Float(0x4258b646), SkBits2Float(0x41f51274), SkBits2Float(0x4258cbc7));  // 30.624f, 54.156f, 30.634f, 54.178f, 30.634f, 54.199f

+path.moveTo(SkBits2Float(0x41f20e5b), SkBits2Float(0x425727f0));  // 30.257f, 53.789f

+path.cubicTo(SkBits2Float(0x41f1cac5), SkBits2Float(0x4256da1d), SkBits2Float(0x41f222d6), SkBits2Float(0x42561375), SkBits2Float(0x41f222d6), SkBits2Float(0x4255d0e6));  // 30.224f, 53.713f, 30.267f, 53.519f, 30.267f, 53.454f

+path.cubicTo(SkBits2Float(0x41f222d6), SkBits2Float(0x42553646), SkBits2Float(0x41f1b43e), SkBits2Float(0x4254374c), SkBits2Float(0x41f20e5b), SkBits2Float(0x42539169));  // 30.267f, 53.303f, 30.213f, 53.054f, 30.257f, 52.892f

+path.cubicTo(SkBits2Float(0x41f222d6), SkBits2Float(0x42536561), SkBits2Float(0x41f2916d), SkBits2Float(0x4253449c), SkBits2Float(0x41f2be7c), SkBits2Float(0x4253449c));  // 30.267f, 52.849f, 30.321f, 52.817f, 30.343f, 52.817f

+path.cubicTo(SkBits2Float(0x41f3b026), SkBits2Float(0x42532e15), SkBits2Float(0x41f845a7), SkBits2Float(0x42539cac), SkBits2Float(0x41f88730), SkBits2Float(0x4253d3f8));  // 30.461f, 52.795f, 31.034f, 52.903f, 31.066f, 52.957f

+path.cubicTo(SkBits2Float(0x41f8cac6), SkBits2Float(0x42540000), SkBits2Float(0x41f8cac6), SkBits2Float(0x42544290), SkBits2Float(0x41f8e14d), SkBits2Float(0x4254851f));  // 31.099f, 53, 31.099f, 53.065f, 31.11f, 53.13f

+path.cubicTo(SkBits2Float(0x41f8f5c8), SkBits2Float(0x4254d1ec), SkBits2Float(0x41f97ae7), SkBits2Float(0x425578d5), SkBits2Float(0x41f9666c), SkBits2Float(0x4255e76d));  // 31.12f, 53.205f, 31.185f, 53.368f, 31.175f, 53.476f

+path.cubicTo(SkBits2Float(0x41f94dd9), SkBits2Float(0x42561375), SkBits2Float(0x41f88731), SkBits2Float(0x4256a2d1), SkBits2Float(0x41f85c2f), SkBits2Float(0x4256c49c));  // 31.163f, 53.519f, 31.066f, 53.659f, 31.045f, 53.692f

+path.cubicTo(SkBits2Float(0x41f845a8), SkBits2Float(0x4256da1d), SkBits2Float(0x41f7d710), SkBits2Float(0x4256f0a4), SkBits2Float(0x41f7d710), SkBits2Float(0x4256fbe8));  // 31.034f, 53.713f, 30.98f, 53.735f, 30.98f, 53.746f

+path.lineTo(SkBits2Float(0x41f7d710), SkBits2Float(0x42571cad));  // 30.98f, 53.778f

+path.cubicTo(SkBits2Float(0x41f79587), SkBits2Float(0x4257322e), SkBits2Float(0x41f73b6a), SkBits2Float(0x425748b5), SkBits2Float(0x41f6f9e1), SkBits2Float(0x42575f3c));  // 30.948f, 53.799f, 30.904f, 53.821f, 30.872f, 53.843f

+path.cubicTo(SkBits2Float(0x41f6062b), SkBits2Float(0x425774bd), SkBits2Float(0x41f2395e), SkBits2Float(0x425774bd), SkBits2Float(0x41f20e5c), SkBits2Float(0x425727f0));  // 30.753f, 53.864f, 30.278f, 53.864f, 30.257f, 53.789f

+path.moveTo(SkBits2Float(0x42048f5f), SkBits2Float(0x426b072b));  // 33.14f, 58.757f

+path.cubicTo(SkBits2Float(0x42046d94), SkBits2Float(0x426acfdf), SkBits2Float(0x42048f5f), SkBits2Float(0x426ab958), SkBits2Float(0x420478d8), SkBits2Float(0x426a77cf));  // 33.107f, 58.703f, 33.14f, 58.681f, 33.118f, 58.617f

+path.cubicTo(SkBits2Float(0x42045813), SkBits2Float(0x4269d0e6), SkBits2Float(0x42042c0b), SkBits2Float(0x42693646), SkBits2Float(0x42041584), SkBits2Float(0x4268851f));  // 33.086f, 58.454f, 33.043f, 58.303f, 33.021f, 58.13f

+path.cubicTo(SkBits2Float(0x4203e97c), SkBits2Float(0x4267c9bb), SkBits2Float(0x42039caf), SkBits2Float(0x42670d50), SkBits2Float(0x4203a6ec), SkBits2Float(0x426624dd));  // 32.978f, 57.947f, 32.903f, 57.763f, 32.913f, 57.536f

+path.cubicTo(SkBits2Float(0x4203a6ec), SkBits2Float(0x426624dd), SkBits2Float(0x4203de38), SkBits2Float(0x4265f8d5), SkBits2Float(0x4203e97b), SkBits2Float(0x4265f8d5));  // 32.913f, 57.536f, 32.967f, 57.493f, 32.978f, 57.493f

+path.cubicTo(SkBits2Float(0x42042c0a), SkBits2Float(0x4265ee98), SkBits2Float(0x4204c6aa), SkBits2Float(0x4266199a), SkBits2Float(0x4204e875), SkBits2Float(0x42663b64));  // 33.043f, 57.483f, 33.194f, 57.525f, 33.227f, 57.558f

+path.cubicTo(SkBits2Float(0x42051ebb), SkBits2Float(0x42668937), SkBits2Float(0x42051ebb), SkBits2Float(0x42671893), SkBits2Float(0x42054085), SkBits2Float(0x426770a3));  // 33.28f, 57.634f, 33.28f, 57.774f, 33.313f, 57.86f

+path.cubicTo(SkBits2Float(0x42058314), SkBits2Float(0x4268a6e9), SkBits2Float(0x4206072d), SkBits2Float(0x4269d0e5), SkBits2Float(0x42061271), SkBits2Float(0x426b3e76));  // 33.378f, 58.163f, 33.507f, 58.454f, 33.518f, 58.811f

+path.cubicTo(SkBits2Float(0x4205e669), SkBits2Float(0x426b3e76), SkBits2Float(0x4205e669), SkBits2Float(0x426b49ba), SkBits2Float(0x4205b95a), SkBits2Float(0x426b5f3b));  // 33.475f, 58.811f, 33.475f, 58.822f, 33.431f, 58.843f

+path.cubicTo(SkBits2Float(0x42056c8d), SkBits2Float(0x426b5f3b), SkBits2Float(0x4204e875), SkBits2Float(0x426b75c2), SkBits2Float(0x4204b023), SkBits2Float(0x426b49ba));  // 33.356f, 58.843f, 33.227f, 58.865f, 33.172f, 58.822f

+path.lineTo(SkBits2Float(0x4204b023), SkBits2Float(0x426b3333));  // 33.172f, 58.8f

+path.cubicTo(SkBits2Float(0x4204b023), SkBits2Float(0x426b27ef), SkBits2Float(0x42048f5e), SkBits2Float(0x426b072b), SkBits2Float(0x42048f5e), SkBits2Float(0x426b072b));  // 33.172f, 58.789f, 33.14f, 58.757f, 33.14f, 58.757f

+path.moveTo(SkBits2Float(0x42035918), SkBits2Float(0x426b6a7f));  // 32.837f, 58.854f

+path.cubicTo(SkBits2Float(0x42032d10), SkBits2Float(0x426b6a7f), SkBits2Float(0x42030108), SkBits2Float(0x426b75c3), SkBits2Float(0x4202d4ff), SkBits2Float(0x426b75c3));  // 32.794f, 58.854f, 32.751f, 58.865f, 32.708f, 58.865f

+path.cubicTo(SkBits2Float(0x42026667), SkBits2Float(0x426b75c3), SkBits2Float(0x42020d51), SkBits2Float(0x426b5f3c), SkBits2Float(0x4201ec8c), SkBits2Float(0x426b27f0));  // 32.6f, 58.865f, 32.513f, 58.843f, 32.481f, 58.789f

+path.cubicTo(SkBits2Float(0x4201cbc7), SkBits2Float(0x426ae561), SkBits2Float(0x4201cbc7), SkBits2Float(0x426a6c8c), SkBits2Float(0x4201b540), SkBits2Float(0x426a0832));  // 32.449f, 58.724f, 32.449f, 58.606f, 32.427f, 58.508f

+path.cubicTo(SkBits2Float(0x42018938), SkBits2Float(0x426920c5), SkBits2Float(0x42016873), SkBits2Float(0x42683853), SkBits2Float(0x42013021), SkBits2Float(0x42672f1b));  // 32.384f, 58.282f, 32.352f, 58.055f, 32.297f, 57.796f

+path.cubicTo(SkBits2Float(0x42013021), SkBits2Float(0x4267020d), SkBits2Float(0x4200f9db), SkBits2Float(0x42669375), SkBits2Float(0x4200f9db), SkBits2Float(0x426651ec));  // 32.297f, 57.752f, 32.244f, 57.644f, 32.244f, 57.58f

+path.cubicTo(SkBits2Float(0x42010418), SkBits2Float(0x4266199a), SkBits2Float(0x420151eb), SkBits2Float(0x4265ee98), SkBits2Float(0x42018937), SkBits2Float(0x4265ee98));  // 32.254f, 57.525f, 32.33f, 57.483f, 32.384f, 57.483f

+path.cubicTo(SkBits2Float(0x4201e147), SkBits2Float(0x4265e24e), SkBits2Float(0x42022f1a), SkBits2Float(0x4265ee98), SkBits2Float(0x42023a5e), SkBits2Float(0x4266199a));  // 32.47f, 57.471f, 32.546f, 57.483f, 32.557f, 57.525f

+path.cubicTo(SkBits2Float(0x420271aa), SkBits2Float(0x42665c29), SkBits2Float(0x42027be7), SkBits2Float(0x42670d50), SkBits2Float(0x42029db2), SkBits2Float(0x426770a4));  // 32.611f, 57.59f, 32.621f, 57.763f, 32.654f, 57.86f

+path.cubicTo(SkBits2Float(0x42029db2), SkBits2Float(0x4267be77), SkBits2Float(0x4202d4fe), SkBits2Float(0x4268178d), SkBits2Float(0x4202e041), SkBits2Float(0x42684ed9));  // 32.654f, 57.936f, 32.708f, 58.023f, 32.719f, 58.077f

+path.cubicTo(SkBits2Float(0x4202ea7e), SkBits2Float(0x4268bc6a), SkBits2Float(0x4202ea7e), SkBits2Float(0x4268fefa), SkBits2Float(0x42030106), SkBits2Float(0x42695810));  // 32.729f, 58.184f, 32.729f, 58.249f, 32.751f, 58.336f

+path.cubicTo(SkBits2Float(0x420322d1), SkBits2Float(0x4269fced), SkBits2Float(0x4203645a), SkBits2Float(0x426a820c), SkBits2Float(0x4203645a), SkBits2Float(0x426b49ba));  // 32.784f, 58.497f, 32.848f, 58.627f, 32.848f, 58.822f

+path.cubicTo(SkBits2Float(0x42034395), SkBits2Float(0x426b49ba), SkBits2Float(0x42035916), SkBits2Float(0x426b49ba), SkBits2Float(0x42035916), SkBits2Float(0x426b6a7f));  // 32.816f, 58.822f, 32.837f, 58.822f, 32.837f, 58.854f

+path.moveTo(SkBits2Float(0x42009580), SkBits2Float(0x426b6a7f));  // 32.146f, 58.854f

+path.lineTo(SkBits2Float(0x42008b43), SkBits2Float(0x426b8106));  // 32.136f, 58.876f

+path.cubicTo(SkBits2Float(0x42007fff), SkBits2Float(0x426b8106), SkBits2Float(0x42005e35), SkBits2Float(0x426b75c2), SkBits2Float(0x420048b4), SkBits2Float(0x426b8106));  // 32.125f, 58.876f, 32.092f, 58.865f, 32.071f, 58.876f

+path.cubicTo(SkBits2Float(0x41fdcccc), SkBits2Float(0x426bad0e), SkBits2Float(0x41f94dd2), SkBits2Float(0x426b8c4a), SkBits2Float(0x41f6cccc), SkBits2Float(0x426b8c4a));  // 31.725f, 58.919f, 31.163f, 58.887f, 30.85f, 58.887f

+path.cubicTo(SkBits2Float(0x41f65e34), SkBits2Float(0x426b8106), SkBits2Float(0x41f39ba5), SkBits2Float(0x426b8106), SkBits2Float(0x41f35810), SkBits2Float(0x426b49bb));  // 30.796f, 58.876f, 30.451f, 58.876f, 30.418f, 58.822f

+path.cubicTo(SkBits2Float(0x41f35810), SkBits2Float(0x426b3334), SkBits2Float(0x41f2e978), SkBits2Float(0x4267926f), SkBits2Float(0x41f31687), SkBits2Float(0x426723d8));  // 30.418f, 58.8f, 30.364f, 57.893f, 30.386f, 57.785f

+path.lineTo(SkBits2Float(0x41f36e97), SkBits2Float(0x4266ec8c));  // 30.429f, 57.731f

+path.cubicTo(SkBits2Float(0x41f3f3b6), SkBits2Float(0x4266b540), SkBits2Float(0x41f4d0e5), SkBits2Float(0x4266b540), SkBits2Float(0x41f58106), SkBits2Float(0x42669eb9));  // 30.494f, 57.677f, 30.602f, 57.677f, 30.688f, 57.655f

+path.cubicTo(SkBits2Float(0x41f7ed91), SkBits2Float(0x42663b65), SkBits2Float(0x41fac6a8), SkBits2Float(0x4265ee98), SkBits2Float(0x41fdb646), SkBits2Float(0x4265d811));  // 30.991f, 57.558f, 31.347f, 57.483f, 31.714f, 57.461f

+path.cubicTo(SkBits2Float(0x41fe51ec), SkBits2Float(0x4265c18a), SkBits2Float(0x41ff2f1b), SkBits2Float(0x4265d811), SkBits2Float(0x41ff872b), SkBits2Float(0x4265f8d6));  // 31.79f, 57.439f, 31.898f, 57.461f, 31.941f, 57.493f

+path.cubicTo(SkBits2Float(0x41ffb439), SkBits2Float(0x4266199b), SkBits2Float(0x41ffb439), SkBits2Float(0x42669eb9), SkBits2Float(0x41ffdf3b), SkBits2Float(0x4266d605));  // 31.963f, 57.525f, 31.963f, 57.655f, 31.984f, 57.709f

+path.cubicTo(SkBits2Float(0x41fff5c2), SkBits2Float(0x42670d51), SkBits2Float(0x42001cac), SkBits2Float(0x42675b24), SkBits2Float(0x42001cac), SkBits2Float(0x4267926f));  // 31.995f, 57.763f, 32.028f, 57.839f, 32.028f, 57.893f

+path.cubicTo(SkBits2Float(0x42003d71), SkBits2Float(0x42684290), SkBits2Float(0x420048b4), SkBits2Float(0x4268c7ae), SkBits2Float(0x42005e35), SkBits2Float(0x42696d92));  // 32.06f, 58.065f, 32.071f, 58.195f, 32.092f, 58.357f

+path.cubicTo(SkBits2Float(0x42008000), SkBits2Float(0x4269d0e6), SkBits2Float(0x4200ac08), SkBits2Float(0x426a5605), SkBits2Float(0x4200b74c), SkBits2Float(0x426acfe0));  // 32.125f, 58.454f, 32.168f, 58.584f, 32.179f, 58.703f

+path.cubicTo(SkBits2Float(0x4200c189), SkBits2Float(0x426b072c), SkBits2Float(0x4200b74c), SkBits2Float(0x426b49bb), SkBits2Float(0x42009581), SkBits2Float(0x426b6a80));  // 32.189f, 58.757f, 32.179f, 58.822f, 32.146f, 58.854f

+path.moveTo(SkBits2Float(0x41eeae14), SkBits2Float(0x426bef9f));  // 29.835f, 58.984f

+path.cubicTo(SkBits2Float(0x41ee8312), SkBits2Float(0x426c26eb), SkBits2Float(0x41ed353f), SkBits2Float(0x426c52f3), SkBits2Float(0x41ecc8b4), SkBits2Float(0x426c73b8));  // 29.814f, 59.038f, 29.651f, 59.081f, 29.598f, 59.113f

+path.cubicTo(SkBits2Float(0x41eb7ae1), SkBits2Float(0x426cd70c), SkBits2Float(0x41ea3127), SkBits2Float(0x426d9376), SkBits2Float(0x41e96872), SkBits2Float(0x426e2e16));  // 29.435f, 59.21f, 29.274f, 59.394f, 29.176f, 59.545f

+path.cubicTo(SkBits2Float(0x41e88b43), SkBits2Float(0x426ed3f9), SkBits2Float(0x41e7c49b), SkBits2Float(0x426fdd31), SkBits2Float(0x41e6a5e3), SkBits2Float(0x4270570c));  // 29.068f, 59.707f, 28.971f, 59.966f, 28.831f, 60.085f

+path.cubicTo(SkBits2Float(0x41e678d5), SkBits2Float(0x427078d7), SkBits2Float(0x41e6624d), SkBits2Float(0x42706d93), SkBits2Float(0x41e620c4), SkBits2Float(0x427078d7));  // 28.809f, 60.118f, 28.798f, 60.107f, 28.766f, 60.118f

+path.cubicTo(SkBits2Float(0x41e60a3d), SkBits2Float(0x4270841b), SkBits2Float(0x41e5f3b6), SkBits2Float(0x4270999c), SkBits2Float(0x41e5f3b6), SkBits2Float(0x4270999c));  // 28.755f, 60.129f, 28.744f, 60.15f, 28.744f, 60.15f

+path.cubicTo(SkBits2Float(0x41e52d0e), SkBits2Float(0x4270d0e8), SkBits2Float(0x41e49374), SkBits2Float(0x4270e76f), SkBits2Float(0x41e39fbe), SkBits2Float(0x4270fcf0));  // 28.647f, 60.204f, 28.572f, 60.226f, 28.453f, 60.247f

+path.cubicTo(SkBits2Float(0x41e2c28f), SkBits2Float(0x42711377), SkBits2Float(0x41e1a1ca), SkBits2Float(0x42714ac3), SkBits2Float(0x41e03f7c), SkBits2Float(0x4271343c));  // 28.345f, 60.269f, 28.204f, 60.323f, 28.031f, 60.301f

+path.cubicTo(SkBits2Float(0x41de2d0d), SkBits2Float(0x42711377), SkBits2Float(0x41e0c49b), SkBits2Float(0x426e9caf), SkBits2Float(0x41e149b9), SkBits2Float(0x426e23da));  // 27.772f, 60.269f, 28.096f, 59.653f, 28.161f, 59.535f

+path.cubicTo(SkBits2Float(0x41e23d6f), SkBits2Float(0x426d2f1e), SkBits2Float(0x41e38936), SkBits2Float(0x426c52f5), SkBits2Float(0x41e4eb84), SkBits2Float(0x426b8109));  // 28.28f, 59.296f, 28.442f, 59.081f, 28.615f, 58.876f

+path.cubicTo(SkBits2Float(0x41e55a1c), SkBits2Float(0x426b49bd), SkBits2Float(0x41e5dd2e), SkBits2Float(0x426b1caf), SkBits2Float(0x41e6624d), SkBits2Float(0x426ae563));  // 28.669f, 58.822f, 28.733f, 58.778f, 28.798f, 58.724f

+path.cubicTo(SkBits2Float(0x41e78312), SkBits2Float(0x426a77d2), SkBits2Float(0x41e88b43), SkBits2Float(0x4269fcf0), SkBits2Float(0x41e99580), SkBits2Float(0x42698f5f));  // 28.939f, 58.617f, 29.068f, 58.497f, 29.198f, 58.39f

+path.cubicTo(SkBits2Float(0x41ea3126), SkBits2Float(0x42695813), SkBits2Float(0x41edd0e4), SkBits2Float(0x4267a7f2), SkBits2Float(0x41eeae13), SkBits2Float(0x42684292));  // 29.274f, 58.336f, 29.727f, 57.914f, 29.835f, 58.065f

+path.cubicTo(SkBits2Float(0x41eeae13), SkBits2Float(0x42684292), SkBits2Float(0x41eec49a), SkBits2Float(0x42684edc), SkBits2Float(0x41eec49a), SkBits2Float(0x42685919));  // 29.835f, 58.065f, 29.846f, 58.077f, 29.846f, 58.087f

+path.cubicTo(SkBits2Float(0x41ef0623), SkBits2Float(0x4268a6ec), SkBits2Float(0x41eedb21), SkBits2Float(0x426bb854), SkBits2Float(0x41eeae13), SkBits2Float(0x426befa0));  // 29.878f, 58.163f, 29.857f, 58.93f, 29.835f, 58.984f

+path.moveTo(SkBits2Float(0x41eaf7cd), SkBits2Float(0x4258947d));  // 29.371f, 54.145f

+path.cubicTo(SkBits2Float(0x41ebd4fc), SkBits2Float(0x425873b8), SkBits2Float(0x41ed353e), SkBits2Float(0x42589fc1), SkBits2Float(0x41edba5c), SkBits2Float(0x4258ab04));  // 29.479f, 54.113f, 29.651f, 54.156f, 29.716f, 54.167f

+path.cubicTo(SkBits2Float(0x41ede76a), SkBits2Float(0x4259c9bc), SkBits2Float(0x41ee3f7b), SkBits2Float(0x425b6e9a), SkBits2Float(0x41ee126c), SkBits2Float(0x425c8314));  // 29.738f, 54.447f, 29.781f, 54.858f, 29.759f, 55.128f

+path.cubicTo(SkBits2Float(0x41ede76a), SkBits2Float(0x425d343b), SkBits2Float(0x41ee5602), SkBits2Float(0x425dda1e), SkBits2Float(0x41edd0e3), SkBits2Float(0x425e74be));  // 29.738f, 55.301f, 29.792f, 55.463f, 29.727f, 55.614f

+path.cubicTo(SkBits2Float(0x41ed624b), SkBits2Float(0x425f1aa1), SkBits2Float(0x41ec6e95), SkBits2Float(0x425f947c), SkBits2Float(0x41ebd4fc), SkBits2Float(0x426023d9));  // 29.673f, 55.776f, 29.554f, 55.895f, 29.479f, 56.035f

+path.cubicTo(SkBits2Float(0x41eb22cf), SkBits2Float(0x4260c9bc), SkBits2Float(0x41ea5c27), SkBits2Float(0x4261645c), SkBits2Float(0x41e9957f), SkBits2Float(0x42621583));  // 29.392f, 56.197f, 29.295f, 56.348f, 29.198f, 56.521f

+path.cubicTo(SkBits2Float(0x41e8e55e), SkBits2Float(0x4262c6aa), SkBits2Float(0x41e849b8), SkBits2Float(0x42638314), SkBits2Float(0x41e78310), SkBits2Float(0x426427f2));  // 29.112f, 56.694f, 29.036f, 56.878f, 28.939f, 57.039f

+path.cubicTo(SkBits2Float(0x41e72b00), SkBits2Float(0x42646b88), SkBits2Float(0x41e6e76a), SkBits2Float(0x4264b854), SkBits2Float(0x41e68f5a), SkBits2Float(0x4264efa0));  // 28.896f, 57.105f, 28.863f, 57.18f, 28.82f, 57.234f

+path.cubicTo(SkBits2Float(0x41e6624c), SkBits2Float(0x42651ba8), SkBits2Float(0x41e60a3b), SkBits2Float(0x4265322f), SkBits2Float(0x41e5dd2d), SkBits2Float(0x426552f4));  // 28.798f, 57.277f, 28.755f, 57.299f, 28.733f, 57.331f

+path.cubicTo(SkBits2Float(0x41e570a2), SkBits2Float(0x4264ad11), SkBits2Float(0x41e620c3), SkBits2Float(0x4263c49e), SkBits2Float(0x41e6624c), SkBits2Float(0x426329fe));  // 28.68f, 57.169f, 28.766f, 56.942f, 28.798f, 56.791f

+path.cubicTo(SkBits2Float(0x41e6a5e2), SkBits2Float(0x4262418b), SkBits2Float(0x41e6e76b), SkBits2Float(0x42617ae3), SkBits2Float(0x41e72b00), SkBits2Float(0x42609271));  // 28.831f, 56.564f, 28.863f, 56.37f, 28.896f, 56.143f

+path.cubicTo(SkBits2Float(0x41e75602), SkBits2Float(0x42604fe2), SkBits2Float(0x41e7978b), SkBits2Float(0x425fe250), SkBits2Float(0x41e7c49a), SkBits2Float(0x425f9fc1));  // 28.917f, 56.078f, 28.949f, 55.971f, 28.971f, 55.906f

+path.cubicTo(SkBits2Float(0x41e7db21), SkBits2Float(0x425f25e6), SkBits2Float(0x41e7db21), SkBits2Float(0x425ec18c), SkBits2Float(0x41e80623), SkBits2Float(0x425e53fa));  // 28.982f, 55.787f, 28.982f, 55.689f, 29.003f, 55.582f

+path.lineTo(SkBits2Float(0x41e849b9), SkBits2Float(0x425e26ec));  // 29.036f, 55.538f

+path.cubicTo(SkBits2Float(0x41e874bb), SkBits2Float(0x425da2d3), SkBits2Float(0x41e8b851), SkBits2Float(0x425d28f8), SkBits2Float(0x41e8e55f), SkBits2Float(0x425caf1d));  // 29.057f, 55.409f, 29.09f, 55.29f, 29.112f, 55.171f

+path.cubicTo(SkBits2Float(0x41e93b63), SkBits2Float(0x425b8f5f), SkBits2Float(0x41e97ef9), SkBits2Float(0x425a7ae4), SkBits2Float(0x41ea0417), SkBits2Float(0x42596669));  // 29.154f, 54.89f, 29.187f, 54.62f, 29.252f, 54.35f

+path.cubicTo(SkBits2Float(0x41ea3125), SkBits2Float(0x4259199c), SkBits2Float(0x41ea5c27), SkBits2Float(0x4258ab05), SkBits2Float(0x41eaf7cd), SkBits2Float(0x4258947d));  // 29.274f, 54.275f, 29.295f, 54.167f, 29.371f, 54.145f

+path.moveTo(SkBits2Float(0x41e96871), SkBits2Float(0x4256a2d3));  // 29.176f, 53.659f

+path.cubicTo(SkBits2Float(0x41e953f6), SkBits2Float(0x4256e562), SkBits2Float(0x41e96871), SkBits2Float(0x425727f2), SkBits2Float(0x41e93b63), SkBits2Float(0x42575f3d));  // 29.166f, 53.724f, 29.176f, 53.789f, 29.154f, 53.843f

+path.cubicTo(SkBits2Float(0x41e8fbe6), SkBits2Float(0x42578002), SkBits2Float(0x41e88b42), SkBits2Float(0x42578002), SkBits2Float(0x41e81cab), SkBits2Float(0x42578002));  // 29.123f, 53.875f, 29.068f, 53.875f, 29.014f, 53.875f

+path.cubicTo(SkBits2Float(0x41e7db22), SkBits2Float(0x42578002), SkBits2Float(0x41e78311), SkBits2Float(0x42576a81), SkBits2Float(0x41e75603), SkBits2Float(0x42575f3d));  // 28.982f, 53.875f, 28.939f, 53.854f, 28.917f, 53.843f

+path.cubicTo(SkBits2Float(0x41e72b01), SkBits2Float(0x4257322f), SkBits2Float(0x41e72b01), SkBits2Float(0x4257322f), SkBits2Float(0x41e72b01), SkBits2Float(0x4256fbe9));  // 28.896f, 53.799f, 28.896f, 53.799f, 28.896f, 53.746f

+path.cubicTo(SkBits2Float(0x41e72b01), SkBits2Float(0x4256b95a), SkBits2Float(0x41e78311), SkBits2Float(0x42564ac2), SkBits2Float(0x41e7978c), SkBits2Float(0x42561376));  // 28.896f, 53.681f, 28.939f, 53.573f, 28.949f, 53.519f

+path.cubicTo(SkBits2Float(0x41e7db22), SkBits2Float(0x4255570c), SkBits2Float(0x41e80624), SkBits2Float(0x4254b128), SkBits2Float(0x41e86040), SkBits2Float(0x42540b45));  // 28.982f, 53.335f, 29.003f, 53.173f, 29.047f, 53.011f

+path.cubicTo(SkBits2Float(0x41e874bb), SkBits2Float(0x4253cac2), SkBits2Float(0x41e86040), SkBits2Float(0x4253916a), SkBits2Float(0x41e8b850), SkBits2Float(0x42536562));  // 29.057f, 52.948f, 29.047f, 52.892f, 29.09f, 52.849f

+path.cubicTo(SkBits2Float(0x41e8ced7), SkBits2Float(0x42534fe1), SkBits2Float(0x41e953f6), SkBits2Float(0x42532e16), SkBits2Float(0x41e97ef8), SkBits2Float(0x42532e16));  // 29.101f, 52.828f, 29.166f, 52.795f, 29.187f, 52.795f

+path.cubicTo(SkBits2Float(0x41ea0417), SkBits2Float(0x425323d9), SkBits2Float(0x41ea3125), SkBits2Float(0x42534fe1), SkBits2Float(0x41ea72ae), SkBits2Float(0x42535a1e));  // 29.252f, 52.785f, 29.274f, 52.828f, 29.306f, 52.838f

+path.cubicTo(SkBits2Float(0x41ea72ae), SkBits2Float(0x42548520), SkBits2Float(0x41e9d708), SkBits2Float(0x4255a4df), SkBits2Float(0x41e96871), SkBits2Float(0x4256a2d2));  // 29.306f, 53.13f, 29.23f, 53.411f, 29.176f, 53.659f

+path.moveTo(SkBits2Float(0x41e874bb), SkBits2Float(0x4258b647));  // 29.057f, 54.178f

+path.cubicTo(SkBits2Float(0x41e86040), SkBits2Float(0x42595c2a), SkBits2Float(0x41e849b9), SkBits2Float(0x4259bf7e), SkBits2Float(0x41e80623), SkBits2Float(0x425a4eda));  // 29.047f, 54.34f, 29.036f, 54.437f, 29.003f, 54.577f

+path.cubicTo(SkBits2Float(0x41e7db21), SkBits2Float(0x425ad3f9), SkBits2Float(0x41e76c89), SkBits2Float(0x425b8520), SkBits2Float(0x41e72b00), SkBits2Float(0x425c147c));  // 28.982f, 54.707f, 28.928f, 54.88f, 28.896f, 55.02f

+path.cubicTo(SkBits2Float(0x41e71479), SkBits2Float(0x425c570b), SkBits2Float(0x41e72b00), SkBits2Float(0x425c77d0), SkBits2Float(0x41e71479), SkBits2Float(0x425cba5f));  // 28.885f, 55.085f, 28.896f, 55.117f, 28.885f, 55.182f

+path.cubicTo(SkBits2Float(0x41e68f5a), SkBits2Float(0x425dfae2), SkBits2Float(0x41e5dd2d), SkBits2Float(0x425f676d), SkBits2Float(0x41e570a2), SkBits2Float(0x4260a8f7));  // 28.82f, 55.495f, 28.733f, 55.851f, 28.68f, 56.165f

+path.cubicTo(SkBits2Float(0x41e52d0c), SkBits2Float(0x42610c4b), SkBits2Float(0x41e55a1b), SkBits2Float(0x42614eda), SkBits2Float(0x41e52d0c), SkBits2Float(0x42619ba7));  // 28.647f, 56.262f, 28.669f, 56.327f, 28.647f, 56.402f

+path.cubicTo(SkBits2Float(0x41e51685), SkBits2Float(0x4261f4be), SkBits2Float(0x41e4be74), SkBits2Float(0x42624cce), SkBits2Float(0x41e4a7ed), SkBits2Float(0x42628f5d));  // 28.636f, 56.489f, 28.593f, 56.575f, 28.582f, 56.64f

+path.cubicTo(SkBits2Float(0x41e46664), SkBits2Float(0x42634bc7), SkBits2Float(0x41e43b62), SkBits2Float(0x4263e667), SkBits2Float(0x41e3f7cc), SkBits2Float(0x4264a1cc));  // 28.55f, 56.824f, 28.529f, 56.975f, 28.496f, 57.158f

+path.cubicTo(SkBits2Float(0x41e39fbc), SkBits2Float(0x42657efb), SkBits2Float(0x41e31a9d), SkBits2Float(0x42669376), SkBits2Float(0x41e2ac05), SkBits2Float(0x426770a5));  // 28.453f, 57.374f, 28.388f, 57.644f, 28.334f, 57.86f

+path.cubicTo(SkBits2Float(0x41e27ef7), SkBits2Float(0x426821cc), SkBits2Float(0x41e253f5), SkBits2Float(0x4268bc6c), SkBits2Float(0x41e2105f), SkBits2Float(0x42695812));  // 28.312f, 58.033f, 28.291f, 58.184f, 28.258f, 58.336f

+path.cubicTo(SkBits2Float(0x41e1ced6), SkBits2Float(0x4269f2b2), SkBits2Float(0x41e1082e), SkBits2Float(0x426aa3d9), SkBits2Float(0x41e09996), SkBits2Float(0x426b3335));  // 28.226f, 58.487f, 28.129f, 58.66f, 28.075f, 58.8f

+path.lineTo(SkBits2Float(0x41e05600), SkBits2Float(0x426b3e79));  // 28.042f, 58.811f

+path.cubicTo(SkBits2Float(0x41dfe768), SkBits2Float(0x426bb854), SkBits2Float(0x41dfba5a), SkBits2Float(0x426c3129), SkBits2Float(0x41df4dcf), SkBits2Float(0x426ccccf));  // 27.988f, 58.93f, 27.966f, 59.048f, 27.913f, 59.2f

+path.cubicTo(SkBits2Float(0x41def5bf), SkBits2Float(0x426d50e8), SkBits2Float(0x41de5a19), SkBits2Float(0x426de14a), SkBits2Float(0x41ddeb81), SkBits2Float(0x426e70a6));  // 27.87f, 59.329f, 27.794f, 59.47f, 27.74f, 59.61f

+path.cubicTo(SkBits2Float(0x41dd3b60), SkBits2Float(0x426f79dd), SkBits2Float(0x41dd4fdb), SkBits2Float(0x426e1896), SkBits2Float(0x41dd6662), SkBits2Float(0x426db43c));  // 27.654f, 59.869f, 27.664f, 59.524f, 27.675f, 59.426f

+path.cubicTo(SkBits2Float(0x41de9ba1), SkBits2Float(0x426aa3da), SkBits2Float(0x41e01476), SkBits2Float(0x42679271), SkBits2Float(0x41e1332f), SkBits2Float(0x42648109));  // 27.826f, 58.66f, 28.01f, 57.893f, 28.15f, 57.126f

+path.cubicTo(SkBits2Float(0x41e149b6), SkBits2Float(0x42645f3e), SkBits2Float(0x41e1a1c7), SkBits2Float(0x4264072e), SkBits2Float(0x41e1a1c7), SkBits2Float(0x4263f1ad));  // 28.161f, 57.093f, 28.204f, 57.007f, 28.204f, 56.986f

+path.cubicTo(SkBits2Float(0x41e253f4), SkBits2Float(0x42626e9b), SkBits2Float(0x41e2c28c), SkBits2Float(0x42610109), SkBits2Float(0x41e3459e), SkBits2Float(0x425f72b3));  // 28.291f, 56.608f, 28.345f, 56.251f, 28.409f, 55.862f

+path.cubicTo(SkBits2Float(0x41e372ac), SkBits2Float(0x425f51ee), SkBits2Float(0x41e3b642), SkBits2Float(0x425ef9de), SkBits2Float(0x41e3b642), SkBits2Float(0x425ed813));  // 28.431f, 55.83f, 28.464f, 55.744f, 28.464f, 55.711f

+path.cubicTo(SkBits2Float(0x41e46663), SkBits2Float(0x425d76cb), SkBits2Float(0x41e4be73), SkBits2Float(0x425c3542), SkBits2Float(0x41e570a0), SkBits2Float(0x425ad3fa));  // 28.55f, 55.366f, 28.593f, 55.052f, 28.68f, 54.707f

+path.cubicTo(SkBits2Float(0x41e570a0), SkBits2Float(0x425a916b), SkBits2Float(0x41e5dd2b), SkBits2Float(0x425a22d3), SkBits2Float(0x41e5f3b2), SkBits2Float(0x4259e044));  // 28.68f, 54.642f, 28.733f, 54.534f, 28.744f, 54.469f

+path.cubicTo(SkBits2Float(0x41e620c0), SkBits2Float(0x42595c2b), SkBits2Float(0x41e60a39), SkBits2Float(0x4258ab05), SkBits2Float(0x41e72afe), SkBits2Float(0x4258947d));  // 28.766f, 54.34f, 28.755f, 54.167f, 28.896f, 54.145f

+path.cubicTo(SkBits2Float(0x41e79789), SkBits2Float(0x4258947d), SkBits2Float(0x41e80621), SkBits2Float(0x4258ab04), SkBits2Float(0x41e874b8), SkBits2Float(0x4258b648));  // 28.949f, 54.145f, 29.003f, 54.167f, 29.057f, 54.178f

+path.moveTo(SkBits2Float(0x41e5b229), SkBits2Float(0x4256a2d3));  // 28.712f, 53.659f

+path.cubicTo(SkBits2Float(0x41e5851b), SkBits2Float(0x4256e562), SkBits2Float(0x41e59ba2), SkBits2Float(0x425727f2), SkBits2Float(0x41e570a0), SkBits2Float(0x42575f3d));  // 28.69f, 53.724f, 28.701f, 53.789f, 28.68f, 53.843f

+path.cubicTo(SkBits2Float(0x41e52d0a), SkBits2Float(0x42578002), SkBits2Float(0x41e4a7ec), SkBits2Float(0x42579689), SkBits2Float(0x41e43b61), SkBits2Float(0x42578002));  // 28.647f, 53.875f, 28.582f, 53.897f, 28.529f, 53.875f

+path.cubicTo(SkBits2Float(0x41e3f7cb), SkBits2Float(0x42578002), SkBits2Float(0x41e39fbb), SkBits2Float(0x425748b6), SkBits2Float(0x41e3459e), SkBits2Float(0x42573e79));  // 28.496f, 53.875f, 28.453f, 53.821f, 28.409f, 53.811f

+path.cubicTo(SkBits2Float(0x41e39fbb), SkBits2Float(0x42566044), SkBits2Float(0x41e40e52), SkBits2Float(0x42558e58), SkBits2Float(0x41e47add), SkBits2Float(0x4254c7b0));  // 28.453f, 53.594f, 28.507f, 53.389f, 28.56f, 53.195f

+path.cubicTo(SkBits2Float(0x41e49370), SkBits2Float(0x425479dd), SkBits2Float(0x41e49370), SkBits2Float(0x42541689), SkBits2Float(0x41e4eb81), SkBits2Float(0x4253df3d));  // 28.572f, 53.119f, 28.572f, 53.022f, 28.615f, 52.968f

+path.cubicTo(SkBits2Float(0x41e4fffc), SkBits2Float(0x4253c9bc), SkBits2Float(0x41e5b229), SkBits2Float(0x4253916a), SkBits2Float(0x41e60a39), SkBits2Float(0x4253916a));  // 28.625f, 52.947f, 28.712f, 52.892f, 28.755f, 52.892f

+path.cubicTo(SkBits2Float(0x41e68f58), SkBits2Float(0x4253872d), SkBits2Float(0x41e68f58), SkBits2Float(0x4253a7f1), SkBits2Float(0x41e6e768), SkBits2Float(0x4253be78));  // 28.82f, 52.882f, 28.82f, 52.914f, 28.863f, 52.936f

+path.cubicTo(SkBits2Float(0x41e68f58), SkBits2Float(0x4254c7af), SkBits2Float(0x41e60a39), SkBits2Float(0x4255af1c), SkBits2Float(0x41e5b229), SkBits2Float(0x4256a2d2));  // 28.82f, 53.195f, 28.755f, 53.421f, 28.712f, 53.659f

+path.moveTo(SkBits2Float(0x41e372ac), SkBits2Float(0x42589fc0));  // 28.431f, 54.156f

+path.cubicTo(SkBits2Float(0x41e55a19), SkBits2Float(0x42586874), SkBits2Float(0x41e40e52), SkBits2Float(0x425a178f), SkBits2Float(0x41e3cabc), SkBits2Float(0x425a7ae3));  // 28.669f, 54.102f, 28.507f, 54.523f, 28.474f, 54.62f

+path.cubicTo(SkBits2Float(0x41e1fbe3), SkBits2Float(0x425f3b66), SkBits2Float(0x41dfd0e1), SkBits2Float(0x4263f1ac), SkBits2Float(0x41ddeb81), SkBits2Float(0x4268c7b0));  // 28.248f, 55.808f, 27.977f, 56.986f, 27.74f, 58.195f

+path.cubicTo(SkBits2Float(0x41ddd4fa), SkBits2Float(0x42690a3f), SkBits2Float(0x41dd7ce9), SkBits2Float(0x42696d93), SkBits2Float(0x41dd6662), SkBits2Float(0x4269999c));  // 27.729f, 58.26f, 27.686f, 58.357f, 27.675f, 58.4f

+path.cubicTo(SkBits2Float(0x41dd3b60), SkBits2Float(0x426a29fe), SkBits2Float(0x41dd3b60), SkBits2Float(0x426a8d52), SkBits2Float(0x41dcf7ca), SkBits2Float(0x426b1cae));  // 27.654f, 58.541f, 27.654f, 58.638f, 27.621f, 58.778f

+path.cubicTo(SkBits2Float(0x41dcb641), SkBits2Float(0x426bf9dd), SkBits2Float(0x41dc0414), SkBits2Float(0x426cf8d7), SkBits2Float(0x41db957c), SkBits2Float(0x426dec8d));  // 27.589f, 58.994f, 27.502f, 59.243f, 27.448f, 59.481f

+path.cubicTo(SkBits2Float(0x41db53f3), SkBits2Float(0x426e916a), SkBits2Float(0x41db3d6c), SkBits2Float(0x426eea81), SkBits2Float(0x41daa3d2), SkBits2Float(0x426f5918));  // 27.416f, 59.642f, 27.405f, 59.729f, 27.33f, 59.837f

+path.cubicTo(SkBits2Float(0x41da76c4), SkBits2Float(0x426f4dd4), SkBits2Float(0x41da49b5), SkBits2Float(0x426f4291), SkBits2Float(0x41da082c), SkBits2Float(0x426f21cc));  // 27.308f, 59.826f, 27.286f, 59.815f, 27.254f, 59.783f

+path.cubicTo(SkBits2Float(0x41d9db1e), SkBits2Float(0x426f0b45), SkBits2Float(0x41d9f1a5), SkBits2Float(0x426f0b45), SkBits2Float(0x41d9c6a3), SkBits2Float(0x426eea80));  // 27.232f, 59.761f, 27.243f, 59.761f, 27.222f, 59.729f

+path.lineTo(SkBits2Float(0x41d99995), SkBits2Float(0x426edf3c));  // 27.2f, 59.718f

+path.cubicTo(SkBits2Float(0x41d91476), SkBits2Float(0x426ea7f0), SkBits2Float(0x41d8e768), SkBits2Float(0x426e6561), SkBits2Float(0x41d8a5df), SkBits2Float(0x426e020d));  // 27.135f, 59.664f, 27.113f, 59.599f, 27.081f, 59.502f

+path.cubicTo(SkBits2Float(0x41d8a5df), SkBits2Float(0x426e020d), SkBits2Float(0x41d86456), SkBits2Float(0x426dd605), SkBits2Float(0x41d8a5df), SkBits2Float(0x426dd605));  // 27.081f, 59.502f, 27.049f, 59.459f, 27.081f, 59.459f

+path.cubicTo(SkBits2Float(0x41d8e768), SkBits2Float(0x426d5c2a), SkBits2Float(0x41d8fdef), SkBits2Float(0x426cf8d6), SkBits2Float(0x41d92afe), SkBits2Float(0x426c7efb));  // 27.113f, 59.34f, 27.124f, 59.243f, 27.146f, 59.124f

+path.cubicTo(SkBits2Float(0x41d9830e), SkBits2Float(0x426bb853), SkBits2Float(0x41da1eb4), SkBits2Float(0x426ae561), SkBits2Float(0x41da8d4c), SkBits2Float(0x426a29fd));  // 27.189f, 58.93f, 27.265f, 58.724f, 27.319f, 58.541f

+path.cubicTo(SkBits2Float(0x41dccabd), SkBits2Float(0x4265d811), SkBits2Float(0x41e02afe), SkBits2Float(0x42617ae2), SkBits2Float(0x41e1332f), SkBits2Float(0x425cfcef));  // 27.599f, 57.461f, 28.021f, 56.37f, 28.15f, 55.247f

+path.cubicTo(SkBits2Float(0x41e149b6), SkBits2Float(0x425c4085), SkBits2Float(0x41e1a1c7), SkBits2Float(0x425b8f5e), SkBits2Float(0x41e1fbe3), SkBits2Float(0x425adf3d));  // 28.161f, 55.063f, 28.204f, 54.89f, 28.248f, 54.718f

+path.cubicTo(SkBits2Float(0x41e226e5), SkBits2Float(0x425a4edb), SkBits2Float(0x41e226e5), SkBits2Float(0x42598833), SkBits2Float(0x41e2ac04), SkBits2Float(0x4258f7d0));  // 28.269f, 54.577f, 28.269f, 54.383f, 28.334f, 54.242f

+path.cubicTo(SkBits2Float(0x41e2c28b), SkBits2Float(0x4258ec8c), SkBits2Float(0x41e372ac), SkBits2Float(0x42589fc0), SkBits2Float(0x41e372ac), SkBits2Float(0x42589fc0));  // 28.345f, 54.231f, 28.431f, 54.156f, 28.431f, 54.156f

+path.moveTo(SkBits2Float(0x41d9830e), SkBits2Float(0x427128f7));  // 27.189f, 60.29f

+path.cubicTo(SkBits2Float(0x41d95600), SkBits2Float(0x42714ac2), SkBits2Float(0x41d92afe), SkBits2Float(0x427176ca), SkBits2Float(0x41d8e768), SkBits2Float(0x427176ca));  // 27.167f, 60.323f, 27.146f, 60.366f, 27.113f, 60.366f

+path.cubicTo(SkBits2Float(0x41d86456), SkBits2Float(0x42718d51), SkBits2Float(0x41d67ce9), SkBits2Float(0x4271820e), SkBits2Float(0x41d60e51), SkBits2Float(0x42716b86));  // 27.049f, 60.388f, 26.811f, 60.377f, 26.757f, 60.355f

+path.cubicTo(SkBits2Float(0x41d5f7ca), SkBits2Float(0x42716b86), SkBits2Float(0x41d5ccc8), SkBits2Float(0x42714ac1), SkBits2Float(0x41d5b641), SkBits2Float(0x42713f7e));  // 26.746f, 60.355f, 26.725f, 60.323f, 26.714f, 60.312f

+path.cubicTo(SkBits2Float(0x41d5b641), SkBits2Float(0x42708e57), SkBits2Float(0x41d5f7ca), SkBits2Float(0x426ffefb), SkBits2Float(0x41d69370), SkBits2Float(0x426f8f5d));  // 26.714f, 60.139f, 26.746f, 59.999f, 26.822f, 59.89f

+path.cubicTo(SkBits2Float(0x41d6eb80), SkBits2Float(0x426f9ba7), SkBits2Float(0x41d7188f), SkBits2Float(0x426f8f5d), SkBits2Float(0x41d7709f), SkBits2Float(0x426f9ba7));  // 26.865f, 59.902f, 26.887f, 59.89f, 26.93f, 59.902f

+path.cubicTo(SkBits2Float(0x41d7b228), SkBits2Float(0x426fb128), SkBits2Float(0x41d99995), SkBits2Float(0x42706d93), SkBits2Float(0x41d9c6a3), SkBits2Float(0x42708e57));  // 26.962f, 59.923f, 27.2f, 60.107f, 27.222f, 60.139f

+path.cubicTo(SkBits2Float(0x41d9db1e), SkBits2Float(0x4270d0e6), SkBits2Float(0x41d99995), SkBits2Float(0x42710832), SkBits2Float(0x41d9830d), SkBits2Float(0x427128f7));  // 27.232f, 60.204f, 27.2f, 60.258f, 27.189f, 60.29f

+path.moveTo(SkBits2Float(0x41e1603c), SkBits2Float(0x4255f1ab));  // 28.172f, 53.486f

+path.cubicTo(SkBits2Float(0x41e149b5), SkBits2Float(0x42563f7e), SkBits2Float(0x41e1603c), SkBits2Float(0x425676ca), SkBits2Float(0x41e1332e), SkBits2Float(0x4256c49d));  // 28.161f, 53.562f, 28.172f, 53.616f, 28.15f, 53.692f

+path.cubicTo(SkBits2Float(0x41e11eb3), SkBits2Float(0x4256f0a5), SkBits2Float(0x41e0db1e), SkBits2Float(0x425727f1), SkBits2Float(0x41e0b01c), SkBits2Float(0x425748b6));  // 28.14f, 53.735f, 28.107f, 53.789f, 28.086f, 53.821f

+path.lineTo(SkBits2Float(0x41e055ff), SkBits2Float(0x425748b6));  // 28.042f, 53.821f

+path.cubicTo(SkBits2Float(0x41e055ff), SkBits2Float(0x425748b6), SkBits2Float(0x41df4dce), SkBits2Float(0x4256e562), SkBits2Float(0x41df3747), SkBits2Float(0x4256da1e));  // 28.042f, 53.821f, 27.913f, 53.724f, 27.902f, 53.713f

+path.cubicTo(SkBits2Float(0x41deb228), SkBits2Float(0x4256820e), SkBits2Float(0x41de4391), SkBits2Float(0x42561376), SkBits2Float(0x41ddbe72), SkBits2Float(0x4255ba60));  // 27.837f, 53.627f, 27.783f, 53.519f, 27.718f, 53.432f

+path.lineTo(SkBits2Float(0x41dd7ce9), SkBits2Float(0x4255af1c));  // 27.686f, 53.421f

+path.cubicTo(SkBits2Float(0x41dd7ce9), SkBits2Float(0x4255a4df), SkBits2Float(0x41dda7eb), SkBits2Float(0x425578d6), SkBits2Float(0x41dd7ce9), SkBits2Float(0x42556d93));  // 27.686f, 53.411f, 27.707f, 53.368f, 27.686f, 53.357f

+path.cubicTo(SkBits2Float(0x41de9ba1), SkBits2Float(0x4255147c), SkBits2Float(0x41df8f58), SkBits2Float(0x4254c7b0), SkBits2Float(0x41e0b01c), SkBits2Float(0x42549064));  // 27.826f, 53.27f, 27.945f, 53.195f, 28.086f, 53.141f

+path.cubicTo(SkBits2Float(0x41e0c497), SkBits2Float(0x42548520), SkBits2Float(0x41e11eb4), SkBits2Float(0x4254645c), SkBits2Float(0x41e1332e), SkBits2Float(0x4254645c));  // 28.096f, 53.13f, 28.14f, 53.098f, 28.15f, 53.098f

+path.cubicTo(SkBits2Float(0x41e18b3e), SkBits2Float(0x42545918), SkBits2Float(0x41e1ced4), SkBits2Float(0x425479dd), SkBits2Float(0x41e1fbe2), SkBits2Float(0x425479dd));  // 28.193f, 53.087f, 28.226f, 53.119f, 28.248f, 53.119f

+path.cubicTo(SkBits2Float(0x41e1fbe2), SkBits2Float(0x4255147d), SkBits2Float(0x41e1a1c5), SkBits2Float(0x4255841a), SkBits2Float(0x41e1603c), SkBits2Float(0x4255f1ac));  // 28.248f, 53.27f, 28.204f, 53.379f, 28.172f, 53.486f

+path.moveTo(SkBits2Float(0x41df6248), SkBits2Float(0x425b4ccf));  // 27.923f, 54.825f

+path.cubicTo(SkBits2Float(0x41dfe767), SkBits2Float(0x425b9aa2), SkBits2Float(0x41df4dcd), SkBits2Float(0x425c6c8d), SkBits2Float(0x41df20bf), SkBits2Float(0x425cd0e8));  // 27.988f, 54.901f, 27.913f, 55.106f, 27.891f, 55.204f

+path.cubicTo(SkBits2Float(0x41ddeb80), SkBits2Float(0x425f893a), SkBits2Float(0x41dc8932), SkBits2Float(0x4262374e), SkBits2Float(0x41db105d), SkBits2Float(0x4264e45d));  // 27.74f, 55.884f, 27.567f, 56.554f, 27.383f, 57.223f

+path.cubicTo(SkBits2Float(0x41daced4), SkBits2Float(0x42657efd), SkBits2Float(0x41d78726), SkBits2Float(0x426c52f5), SkBits2Float(0x41d6c07e), SkBits2Float(0x426c3c6d));  // 27.351f, 57.374f, 26.941f, 59.081f, 26.844f, 59.059f

+path.cubicTo(SkBits2Float(0x41d58932), SkBits2Float(0x426c3129), SkBits2Float(0x41d50620), SkBits2Float(0x426b1caf), SkBits2Float(0x41d48101), SkBits2Float(0x426aa3da));  // 26.692f, 59.048f, 26.628f, 58.778f, 26.563f, 58.66f

+path.cubicTo(SkBits2Float(0x41d3d0e0), SkBits2Float(0x426a0834), SkBits2Float(0x41d34bc2), SkBits2Float(0x42696d94), SkBits2Float(0x41d2db1e), SkBits2Float(0x4268bc6d));  // 26.477f, 58.508f, 26.412f, 58.357f, 26.357f, 58.184f

+path.cubicTo(SkBits2Float(0x41d21476), SkBits2Float(0x42674fe2), SkBits2Float(0x41d19164), SkBits2Float(0x4265c18c), SkBits2Float(0x41d19164), SkBits2Float(0x426449bd));  // 26.26f, 57.828f, 26.196f, 57.439f, 26.196f, 57.072f

+path.cubicTo(SkBits2Float(0x41d1a5df), SkBits2Float(0x4261bd73), SkBits2Float(0x41d3d0e1), SkBits2Float(0x425f51ee), SkBits2Float(0x41d79dae), SkBits2Float(0x425d820f));  // 26.206f, 56.435f, 26.477f, 55.83f, 26.952f, 55.377f

+path.cubicTo(SkBits2Float(0x41d8a5df), SkBits2Float(0x425cfcf0), SkBits2Float(0x41d9db1f), SkBits2Float(0x425c8e59), SkBits2Float(0x41db105e), SkBits2Float(0x425c3542));  // 27.081f, 55.247f, 27.232f, 55.139f, 27.383f, 55.052f

+path.cubicTo(SkBits2Float(0x41dbed8d), SkBits2Float(0x425bf4bf), SkBits2Float(0x41ddbe72), SkBits2Float(0x425b21cd), SkBits2Float(0x41dec8b0), SkBits2Float(0x425b21cd));  // 27.491f, 54.989f, 27.718f, 54.783f, 27.848f, 54.783f

+path.cubicTo(SkBits2Float(0x41df20c0), SkBits2Float(0x425b374e), SkBits2Float(0x41df4dcf), SkBits2Float(0x425b4292), SkBits2Float(0x41df624a), SkBits2Float(0x425b4ccf));  // 27.891f, 54.804f, 27.913f, 54.815f, 27.923f, 54.825f

+path.moveTo(SkBits2Float(0x41d453f4), SkBits2Float(0x426fbc6d));  // 26.541f, 59.934f

+path.cubicTo(SkBits2Float(0x41d48102), SkBits2Float(0x426f8521), SkBits2Float(0x41d51a9c), SkBits2Float(0x426ea7f2), SkBits2Float(0x41d4957d), SkBits2Float(0x426e872e));  // 26.563f, 59.88f, 26.638f, 59.664f, 26.573f, 59.632f

+path.cubicTo(SkBits2Float(0x41d453f4), SkBits2Float(0x426e70a7), SkBits2Float(0x41d428f2), SkBits2Float(0x426e872e), SkBits2Float(0x41d3fbe3), SkBits2Float(0x426e70a7));  // 26.541f, 59.61f, 26.52f, 59.632f, 26.498f, 59.61f

+path.cubicTo(SkBits2Float(0x41d3d0e1), SkBits2Float(0x426e916c), SkBits2Float(0x41d3b84d), SkBits2Float(0x426e872e), SkBits2Float(0x41d3a3d3), SkBits2Float(0x426e916c));  // 26.477f, 59.642f, 26.465f, 59.632f, 26.455f, 59.642f

+path.cubicTo(SkBits2Float(0x41d3603d), SkBits2Float(0x426f010a), SkBits2Float(0x41d3d0e1), SkBits2Float(0x426f9ba9), SkBits2Float(0x41d4126b), SkBits2Float(0x426fdd33));  // 26.422f, 59.751f, 26.477f, 59.902f, 26.509f, 59.966f

+path.lineTo(SkBits2Float(0x41d43d6d), SkBits2Float(0x426fdd33));  // 26.53f, 59.966f

+path.cubicTo(SkBits2Float(0x41d43d6d), SkBits2Float(0x426fd1ef), SkBits2Float(0x41d43d6d), SkBits2Float(0x426fbc6e), SkBits2Float(0x41d453f4), SkBits2Float(0x426fbc6e));  // 26.53f, 59.955f, 26.53f, 59.934f, 26.541f, 59.934f

+path.moveTo(SkBits2Float(0x42071ba4), SkBits2Float(0x42670210));  // 33.777f, 57.752f

+path.cubicTo(SkBits2Float(0x42075e33), SkBits2Float(0x42670d54), SkBits2Float(0x4207957f), SkBits2Float(0x42671897), SkBits2Float(0x4207cccb), SkBits2Float(0x42672f1e));  // 33.842f, 57.763f, 33.896f, 57.774f, 33.95f, 57.796f

+path.cubicTo(SkBits2Float(0x4208a9fa), SkBits2Float(0x4267872e), SkBits2Float(0x42097be6), SkBits2Float(0x42681791), SkBits2Float(0x420a3850), SkBits2Float(0x42688522));  // 34.166f, 57.882f, 34.371f, 58.023f, 34.555f, 58.13f

+path.cubicTo(SkBits2Float(0x420b0a3c), SkBits2Float(0x4268fefd), SkBits2Float(0x420d1167), SkBits2Float(0x4269e770), SkBits2Float(0x420d27ee), SkBits2Float(0x426ae564));  // 34.76f, 58.249f, 35.267f, 58.476f, 35.289f, 58.724f

+path.cubicTo(SkBits2Float(0x420d0729), SkBits2Float(0x426af0a8), SkBits2Float(0x420cdb21), SkBits2Float(0x426afbeb), SkBits2Float(0x420cb956), SkBits2Float(0x426b072f));  // 35.257f, 58.735f, 35.214f, 58.746f, 35.181f, 58.757f

+path.cubicTo(SkBits2Float(0x420b9998), SkBits2Float(0x426b27f4), SkBits2Float(0x420a6f9c), SkBits2Float(0x426b27f4), SkBits2Float(0x42095b21), SkBits2Float(0x426b3337));  // 34.9f, 58.789f, 34.609f, 58.789f, 34.339f, 58.8f

+path.cubicTo(SkBits2Float(0x42090d4e), SkBits2Float(0x426b3337), SkBits2Float(0x4207b644), SkBits2Float(0x426b49be), SkBits2Float(0x420773b4), SkBits2Float(0x426b3337));  // 34.263f, 58.8f, 33.928f, 58.822f, 33.863f, 58.8f

+path.cubicTo(SkBits2Float(0x4207322b), SkBits2Float(0x426b072f), SkBits2Float(0x4206ef9b), SkBits2Float(0x4269999d), SkBits2Float(0x4206ced7), SkBits2Float(0x426920c8));  // 33.799f, 58.757f, 33.734f, 58.4f, 33.702f, 58.282f

+path.cubicTo(SkBits2Float(0x42069685), SkBits2Float(0x4268645e), SkBits2Float(0x4205c49a), SkBits2Float(0x4266b543), SkBits2Float(0x42071ba4), SkBits2Float(0x42670210));  // 33.647f, 58.098f, 33.442f, 57.677f, 33.777f, 57.752f

+path.moveTo(SkBits2Float(0x41f026e6), SkBits2Float(0x423f0c4e));  // 30.019f, 47.762f

+path.cubicTo(SkBits2Float(0x41effbe4), SkBits2Float(0x42400004), SkBits2Float(0x41f0105f), SkBits2Float(0x4240e877), SkBits2Float(0x41f03b61), SkBits2Float(0x4241d0ea));  // 29.998f, 48, 30.008f, 48.227f, 30.029f, 48.454f

+path.lineTo(SkBits2Float(0x41f03b61), SkBits2Float(0x424228fa));  // 30.029f, 48.54f

+path.cubicTo(SkBits2Float(0x41f051e8), SkBits2Float(0x42423f81), SkBits2Float(0x41f0c080), SkBits2Float(0x424276cd), SkBits2Float(0x41f11890), SkBits2Float(0x424276cd));  // 30.04f, 48.562f, 30.094f, 48.616f, 30.137f, 48.616f

+path.cubicTo(SkBits2Float(0x41f11890), SkBits2Float(0x424276cd), SkBits2Float(0x41f18934), SkBits2Float(0x42426b89), SkBits2Float(0x41f12f17), SkBits2Float(0x42426b89));  // 30.137f, 48.616f, 30.192f, 48.605f, 30.148f, 48.605f

+path.cubicTo(SkBits2Float(0x41f1459e), SkBits2Float(0x42426045), SkBits2Float(0x41f18934), SkBits2Float(0x42426b89), SkBits2Float(0x41f18934), SkBits2Float(0x42426b89));  // 30.159f, 48.594f, 30.192f, 48.605f, 30.192f, 48.605f

+path.cubicTo(SkBits2Float(0x41f19daf), SkBits2Float(0x424249be), SkBits2Float(0x41f19daf), SkBits2Float(0x42423f81), SkBits2Float(0x41f1b436), SkBits2Float(0x42423f81));  // 30.202f, 48.572f, 30.202f, 48.562f, 30.213f, 48.562f

+path.cubicTo(SkBits2Float(0x41f18934), SkBits2Float(0x42414087), SkBits2Float(0x41f11890), SkBits2Float(0x424079df), SkBits2Float(0x41f0ac05), SkBits2Float(0x423f9cb0));  // 30.192f, 48.313f, 30.137f, 48.119f, 30.084f, 47.903f

+path.cubicTo(SkBits2Float(0x41f0957e), SkBits2Float(0x423f7ae5), SkBits2Float(0x41f0c080), SkBits2Float(0x423f5a21), SkBits2Float(0x41f0957e), SkBits2Float(0x423f395c));  // 30.073f, 47.87f, 30.094f, 47.838f, 30.073f, 47.806f

+path.lineTo(SkBits2Float(0x41f026e6), SkBits2Float(0x423f0c4e));  // 30.019f, 47.762f

+path.moveTo(SkBits2Float(0x41ed4dcf), SkBits2Float(0x423fd3fc));  // 29.663f, 47.957f

+path.cubicTo(SkBits2Float(0x41ecc8b0), SkBits2Float(0x42408523), SkBits2Float(0x41ec580c), SkBits2Float(0x42414bcb), SkBits2Float(0x41ec580c), SkBits2Float(0x42423f81));  // 29.598f, 48.13f, 29.543f, 48.324f, 29.543f, 48.562f

+path.cubicTo(SkBits2Float(0x41ec6e93), SkBits2Float(0x42423f81), SkBits2Float(0x41ec9ba2), SkBits2Float(0x42426046), SkBits2Float(0x41ecb229), SkBits2Float(0x42426b89));  // 29.554f, 48.562f, 29.576f, 48.594f, 29.587f, 48.605f

+path.cubicTo(SkBits2Float(0x41ecc8b0), SkBits2Float(0x42426b89), SkBits2Float(0x41ecdd2b), SkBits2Float(0x42426045), SkBits2Float(0x41ecf3b2), SkBits2Float(0x42426b89));  // 29.598f, 48.605f, 29.608f, 48.594f, 29.619f, 48.605f

+path.cubicTo(SkBits2Float(0x41ecf3b2), SkBits2Float(0x42426b89), SkBits2Float(0x41eda5df), SkBits2Float(0x42426045), SkBits2Float(0x41edba5a), SkBits2Float(0x42423f81));  // 29.619f, 48.605f, 29.706f, 48.594f, 29.716f, 48.562f

+path.cubicTo(SkBits2Float(0x41ee126a), SkBits2Float(0x4241e66a), SkBits2Float(0x41edd0e1), SkBits2Float(0x42403750), SkBits2Float(0x41eda5df), SkBits2Float(0x423fdf3f));  // 29.759f, 48.475f, 29.727f, 48.054f, 29.706f, 47.968f

+path.lineTo(SkBits2Float(0x41ed4dcf), SkBits2Float(0x423fd3fb));  // 29.663f, 47.957f

+path.moveTo(SkBits2Float(0x41d05a19), SkBits2Float(0x4258ab05));  // 26.044f, 54.167f

+path.cubicTo(SkBits2Float(0x41d05a19), SkBits2Float(0x42589fc1), SkBits2Float(0x41d070a0), SkBits2Float(0x42588a40), SkBits2Float(0x41d05a19), SkBits2Float(0x42586876));  // 26.044f, 54.156f, 26.055f, 54.135f, 26.044f, 54.102f

+path.cubicTo(SkBits2Float(0x41d05a19), SkBits2Float(0x42583c6e), SkBits2Float(0x41d02f17), SkBits2Float(0x4257ee9b), SkBits2Float(0x41d00209), SkBits2Float(0x4257c293));  // 26.044f, 54.059f, 26.023f, 53.983f, 26.001f, 53.94f

+path.cubicTo(SkBits2Float(0x41cfeb82), SkBits2Float(0x42571cb0), SkBits2Float(0x41d00209), SkBits2Float(0x42568210), SkBits2Float(0x41cfeb82), SkBits2Float(0x4255c5a5));  // 25.99f, 53.778f, 26.001f, 53.627f, 25.99f, 53.443f

+path.cubicTo(SkBits2Float(0x41cfeb82), SkBits2Float(0x4255a4e0), SkBits2Float(0x41cfc080), SkBits2Float(0x42552b05), SkBits2Float(0x41cfd4fb), SkBits2Float(0x4254dd32));  // 25.99f, 53.411f, 25.969f, 53.292f, 25.979f, 53.216f

+path.cubicTo(SkBits2Float(0x41cfeb82), SkBits2Float(0x4254b12a), SkBits2Float(0x41d05a1a), SkBits2Float(0x4254b12a), SkBits2Float(0x41d0df38), SkBits2Float(0x4254c7b1));  // 25.99f, 53.173f, 26.044f, 53.173f, 26.109f, 53.195f

+path.cubicTo(SkBits2Float(0x41d24186), SkBits2Float(0x42552b05), SkBits2Float(0x41d4ac05), SkBits2Float(0x42563f80), SkBits2Float(0x41d50621), SkBits2Float(0x42566044));  // 26.282f, 53.292f, 26.584f, 53.562f, 26.628f, 53.594f

+path.cubicTo(SkBits2Float(0x41d60e52), SkBits2Float(0x4256da1f), SkBits2Float(0x41d70208), SkBits2Float(0x425748b7), SkBits2Float(0x41d80a3a), SkBits2Float(0x4257c292));  // 26.757f, 53.713f, 26.876f, 53.821f, 27.005f, 53.94f

+path.cubicTo(SkBits2Float(0x41d8a5e0), SkBits2Float(0x4257f9de), SkBits2Float(0x41da1eb5), SkBits2Float(0x4258947e), SkBits2Float(0x41d8a5e0), SkBits2Float(0x4258ab05));  // 27.081f, 53.994f, 27.265f, 54.145f, 27.081f, 54.167f

+path.cubicTo(SkBits2Float(0x41d7df38), SkBits2Float(0x4258cbca), SkBits2Float(0x41d72d0b), SkBits2Float(0x4258b649), SkBits2Float(0x41d66663), SkBits2Float(0x4258b649));  // 26.984f, 54.199f, 26.897f, 54.178f, 26.8f, 54.178f

+path.cubicTo(SkBits2Float(0x41d547ab), SkBits2Float(0x4258cbca), SkBits2Float(0x41d1bc67), SkBits2Float(0x42592f1e), SkBits2Float(0x41d0b22a), SkBits2Float(0x4258e251));  // 26.66f, 54.199f, 26.217f, 54.296f, 26.087f, 54.221f

+path.lineTo(SkBits2Float(0x41d0b22a), SkBits2Float(0x4258d70d));  // 26.087f, 54.21f

+path.cubicTo(SkBits2Float(0x41d09daf), SkBits2Float(0x4258d70d), SkBits2Float(0x41d070a1), SkBits2Float(0x4258b648), SkBits2Float(0x41d05a1a), SkBits2Float(0x4258ab05));  // 26.077f, 54.21f, 26.055f, 54.178f, 26.044f, 54.167f

+path.moveTo(SkBits2Float(0x41ce8b41), SkBits2Float(0x42588a40));  // 25.818f, 54.135f

+path.cubicTo(SkBits2Float(0x41ceb643), SkBits2Float(0x4258ab05), SkBits2Float(0x41ce74ba), SkBits2Float(0x4258ab05), SkBits2Float(0x41ceccca), SkBits2Float(0x4258ab05));  // 25.839f, 54.167f, 25.807f, 54.167f, 25.85f, 54.167f

+path.cubicTo(SkBits2Float(0x41cef7cc), SkBits2Float(0x4258ab05), SkBits2Float(0x41cf0e53), SkBits2Float(0x4258b336), SkBits2Float(0x41cf0e53), SkBits2Float(0x42589db5));  // 25.871f, 54.167f, 25.882f, 54.175f, 25.882f, 54.154f

+path.cubicTo(SkBits2Float(0x41cf0e53), SkBits2Float(0x4258395b), SkBits2Float(0x41cf0a3a), SkBits2Float(0x42579790), SkBits2Float(0x41cedd2c), SkBits2Float(0x4257343c));  // 25.882f, 54.056f, 25.88f, 53.898f, 25.858f, 53.801f

+path.cubicTo(SkBits2Float(0x41cec8b1), SkBits2Float(0x42564086), SkBits2Float(0x41ceccca), SkBits2Float(0x4254f3b9), SkBits2Float(0x41ce5e32), SkBits2Float(0x425421cd));  // 25.848f, 53.563f, 25.85f, 53.238f, 25.796f, 53.033f

+path.lineTo(SkBits2Float(0x41cdef9a), SkBits2Float(0x425421cd));  // 25.742f, 53.033f

+path.cubicTo(SkBits2Float(0x41cdd913), SkBits2Float(0x4254dd31), SkBits2Float(0x41ce126b), SkBits2Float(0x425626ec), SkBits2Float(0x41ce28f2), SkBits2Float(0x4256e250));  // 25.731f, 53.216f, 25.759f, 53.538f, 25.77f, 53.721f

+path.cubicTo(SkBits2Float(0x41ce3f79), SkBits2Float(0x42579377), SkBits2Float(0x41ce47aa), SkBits2Float(0x42580f5e), SkBits2Float(0x41ce8b40), SkBits2Float(0x42588a40));  // 25.781f, 53.894f, 25.785f, 54.015f, 25.818f, 54.135f

+path.moveTo(SkBits2Float(0x41c58d4c), SkBits2Float(0x425271ad));  // 24.694f, 52.611f

+path.cubicTo(SkBits2Float(0x41c58d4c), SkBits2Float(0x42525c2c), SkBits2Float(0x41c5ba5a), SkBits2Float(0x42523024), SkBits2Float(0x41c5fbe4), SkBits2Float(0x425224e0));  // 24.694f, 52.59f, 24.716f, 52.547f, 24.748f, 52.536f

+path.lineTo(SkBits2Float(0x41c6126b), SkBits2Float(0x4252199c));  // 24.759f, 52.525f

+path.cubicTo(SkBits2Float(0x41c6978a), SkBits2Float(0x42520f5f), SkBits2Float(0x41c774b9), SkBits2Float(0x42523023), SkBits2Float(0x41c79fbb), SkBits2Float(0x42525c2b));  // 24.824f, 52.515f, 24.932f, 52.547f, 24.953f, 52.59f

+path.cubicTo(SkBits2Float(0x41c7f9d8), SkBits2Float(0x4252a9fe), SkBits2Float(0x41c79fbb), SkBits2Float(0x4258e250), SkBits2Float(0x41c78b40), SkBits2Float(0x4259199c));  // 24.997f, 52.666f, 24.953f, 54.221f, 24.943f, 54.275f

+path.lineTo(SkBits2Float(0x41c78b40), SkBits2Float(0x42592f1d));  // 24.943f, 54.296f

+path.cubicTo(SkBits2Float(0x41c747aa), SkBits2Float(0x42595c2b), SkBits2Float(0x41c68103), SkBits2Float(0x42596669), SkBits2Float(0x41c5fbe4), SkBits2Float(0x42596669));  // 24.91f, 54.34f, 24.813f, 54.35f, 24.748f, 54.35f

+path.cubicTo(SkBits2Float(0x41c5353c), SkBits2Float(0x425971ad), SkBits2Float(0x41c41684), SkBits2Float(0x425971ad), SkBits2Float(0x41c3e975), SkBits2Float(0x42592f1d));  // 24.651f, 54.361f, 24.511f, 54.361f, 24.489f, 54.296f

+path.cubicTo(SkBits2Float(0x41c3a7ec), SkBits2Float(0x4258cbc9), SkBits2Float(0x41c42afe), SkBits2Float(0x4257d919), SkBits2Float(0x41c44185), SkBits2Float(0x42578002));  // 24.457f, 54.199f, 24.521f, 53.962f, 24.532f, 53.875f

+path.cubicTo(SkBits2Float(0x41c46e93), SkBits2Float(0x42563f7f), SkBits2Float(0x41c4c6a4), SkBits2Float(0x42550a3f), SkBits2Float(0x41c5353b), SkBits2Float(0x4253df3d));  // 24.554f, 53.562f, 24.597f, 53.26f, 24.651f, 52.968f

+path.cubicTo(SkBits2Float(0x41c54bc2), SkBits2Float(0x42537be9), SkBits2Float(0x41c56249), SkBits2Float(0x42530d51), SkBits2Float(0x41c58d4b), SkBits2Float(0x4252cac2));  // 24.662f, 52.871f, 24.673f, 52.763f, 24.694f, 52.698f

+path.cubicTo(SkBits2Float(0x41c58d4b), SkBits2Float(0x4252a9fd), SkBits2Float(0x41c56249), SkBits2Float(0x42528833), SkBits2Float(0x41c58d4b), SkBits2Float(0x425271ab));  // 24.694f, 52.666f, 24.673f, 52.633f, 24.694f, 52.611f

+path.moveTo(SkBits2Float(0x41c36662), SkBits2Float(0x42534fe0));  // 24.425f, 52.828f

+path.cubicTo(SkBits2Float(0x41c33954), SkBits2Float(0x4253c9bb), SkBits2Float(0x41c34dcf), SkBits2Float(0x42541688), SkBits2Float(0x41c322cc), SkBits2Float(0x42549063));  // 24.403f, 52.947f, 24.413f, 53.022f, 24.392f, 53.141f

+path.cubicTo(SkBits2Float(0x41c2f5be), SkBits2Float(0x4254fefb), SkBits2Float(0x41c2b434), SkBits2Float(0x42558e57), SkBits2Float(0x41c29dad), SkBits2Float(0x42560832));  // 24.37f, 53.249f, 24.338f, 53.389f, 24.327f, 53.508f

+path.cubicTo(SkBits2Float(0x41c2709f), SkBits2Float(0x4256e561), SkBits2Float(0x41c2459d), SkBits2Float(0x4257ad0f), SkBits2Float(0x41c1ed8c), SkBits2Float(0x42586874));  // 24.305f, 53.724f, 24.284f, 53.919f, 24.241f, 54.102f

+path.cubicTo(SkBits2Float(0x41c1d705), SkBits2Float(0x4258cbc8), SkBits2Float(0x41c20207), SkBits2Float(0x42590e57), SkBits2Float(0x41c1c07e), SkBits2Float(0x425950e7));  // 24.23f, 54.199f, 24.251f, 54.264f, 24.219f, 54.329f

+path.cubicTo(SkBits2Float(0x41c1c07e), SkBits2Float(0x42596668), SkBits2Float(0x41c1686e), SkBits2Float(0x42599270), SkBits2Float(0x41c13b5f), SkBits2Float(0x42599270));  // 24.219f, 54.35f, 24.176f, 54.393f, 24.154f, 54.393f

+path.cubicTo(SkBits2Float(0x41c0ccc7), SkBits2Float(0x4259a8f7), SkBits2Float(0x41c074b7), SkBits2Float(0x42599270), SkBits2Float(0x41c00620), SkBits2Float(0x425971ab));  // 24.1f, 54.415f, 24.057f, 54.393f, 24.003f, 54.361f

+path.cubicTo(SkBits2Float(0x41c00620), SkBits2Float(0x425825e4), SkBits2Float(0x41c08b3f), SkBits2Float(0x4256da1e), SkBits2Float(0x41c0f9d6), SkBits2Float(0x42558e57));  // 24.003f, 54.037f, 24.068f, 53.713f, 24.122f, 53.389f

+path.cubicTo(SkBits2Float(0x41c151e6), SkBits2Float(0x425479dc), SkBits2Float(0x41c151e6), SkBits2Float(0x42534fe0), SkBits2Float(0x41c1ed8c), SkBits2Float(0x425245a3));  // 24.165f, 53.119f, 24.165f, 52.828f, 24.241f, 52.568f

+path.cubicTo(SkBits2Float(0x41c22f15), SkBits2Float(0x42520f5d), SkBits2Float(0x41c22f15), SkBits2Float(0x4251d70b), SkBits2Float(0x41c25c24), SkBits2Float(0x4251ccce));  // 24.273f, 52.515f, 24.273f, 52.46f, 24.295f, 52.45f

+path.cubicTo(SkBits2Float(0x41c2e143), SkBits2Float(0x4251b647), SkBits2Float(0x41c34dce), SkBits2Float(0x4251e24f), SkBits2Float(0x41c3a7eb), SkBits2Float(0x4251e24f));  // 24.36f, 52.428f, 24.413f, 52.471f, 24.457f, 52.471f

+path.cubicTo(SkBits2Float(0x41c3be72), SkBits2Float(0x42525c2a), SkBits2Float(0x41c37add), SkBits2Float(0x4252e149), SkBits2Float(0x41c36662), SkBits2Float(0x42534fe0));  // 24.468f, 52.59f, 24.435f, 52.72f, 24.425f, 52.828f

+path.moveTo(SkBits2Float(0x41b3105e), SkBits2Float(0x426e020d));  // 22.383f, 59.502f

+path.cubicTo(SkBits2Float(0x41b2ced5), SkBits2Float(0x426dcac1), SkBits2Float(0x41b28b3f), SkBits2Float(0x426d9375), SkBits2Float(0x41b21ca8), SkBits2Float(0x426d676d));  // 22.351f, 59.448f, 22.318f, 59.394f, 22.264f, 59.351f

+path.lineTo(SkBits2Float(0x41b1f1a6), SkBits2Float(0x426d676d));  // 22.243f, 59.351f

+path.lineTo(SkBits2Float(0x41b1f1a6), SkBits2Float(0x426d50e6));  // 22.243f, 59.329f

+path.cubicTo(SkBits2Float(0x41b1f1a6), SkBits2Float(0x426d2f1b), SkBits2Float(0x41b1830e), SkBits2Float(0x426d199a), SkBits2Float(0x41b15600), SkBits2Float(0x426d0f5d));  // 22.243f, 59.296f, 22.189f, 59.275f, 22.167f, 59.265f

+path.cubicTo(SkBits2Float(0x41b0e768), SkBits2Float(0x426cccce), SkBits2Float(0x41af1683), SkBits2Float(0x426bd917), SkBits2Float(0x41aefffc), SkBits2Float(0x426b8107));  // 22.113f, 59.2f, 21.886f, 58.962f, 21.875f, 58.876f

+path.cubicTo(SkBits2Float(0x41aeeb81), SkBits2Float(0x426b3334), SkBits2Float(0x41af5a19), SkBits2Float(0x426acfe0), SkBits2Float(0x41af70a0), SkBits2Float(0x426a8d51));  // 21.865f, 58.8f, 21.919f, 58.703f, 21.93f, 58.638f

+path.cubicTo(SkBits2Float(0x41b04dcf), SkBits2Float(0x42693647), SkBits2Float(0x41b1db1f), SkBits2Float(0x4268645b), SkBits2Float(0x41b43123), SkBits2Float(0x4267c9bc));  // 22.038f, 58.303f, 22.232f, 58.098f, 22.524f, 57.947f

+path.cubicTo(SkBits2Float(0x41b472ac), SkBits2Float(0x4267a7f1), SkBits2Float(0x41b4f7cb), SkBits2Float(0x426770a5), SkBits2Float(0x41b56662), SkBits2Float(0x42676668));  // 22.556f, 57.914f, 22.621f, 57.86f, 22.675f, 57.85f

+path.cubicTo(SkBits2Float(0x41b5a7eb), SkBits2Float(0x42675b24), SkBits2Float(0x41b5d4fa), SkBits2Float(0x42676668), SkBits2Float(0x41b62d0a), SkBits2Float(0x42675b24));  // 22.707f, 57.839f, 22.729f, 57.85f, 22.772f, 57.839f

+path.cubicTo(SkBits2Float(0x41b69ba2), SkBits2Float(0x42674fe0), SkBits2Float(0x41b78f58), SkBits2Float(0x42671895), SkBits2Float(0x41b828f1), SkBits2Float(0x42671895));  // 22.826f, 57.828f, 22.945f, 57.774f, 23.02f, 57.774f

+path.cubicTo(SkBits2Float(0x41b8ae10), SkBits2Float(0x42671895), SkBits2Float(0x41b8c497), SkBits2Float(0x42672f1c), SkBits2Float(0x41b91ca7), SkBits2Float(0x4267449d));  // 23.085f, 57.774f, 23.096f, 57.796f, 23.139f, 57.817f

+path.lineTo(SkBits2Float(0x41b91ca7), SkBits2Float(0x42675b24));  // 23.139f, 57.839f

+path.cubicTo(SkBits2Float(0x41b91ca7), SkBits2Float(0x42674fe0), SkBits2Float(0x41b9332e), SkBits2Float(0x426770a5), SkBits2Float(0x41b9332e), SkBits2Float(0x4267872c));  // 23.139f, 57.828f, 23.15f, 57.86f, 23.15f, 57.882f

+path.cubicTo(SkBits2Float(0x41b91ca7), SkBits2Float(0x4267df3c), SkBits2Float(0x41b90620), SkBits2Float(0x42685918), SkBits2Float(0x41b8db1e), SkBits2Float(0x4268bc6b));  // 23.139f, 57.968f, 23.128f, 58.087f, 23.107f, 58.184f

+path.cubicTo(SkBits2Float(0x41b855ff), SkBits2Float(0x426a29fc), SkBits2Float(0x41b7d0e1), SkBits2Float(0x426bc290), SkBits2Float(0x41b76249), SkBits2Float(0x426d2f1b));  // 23.042f, 58.541f, 22.977f, 58.94f, 22.923f, 59.296f

+path.cubicTo(SkBits2Float(0x41b720c0), SkBits2Float(0x426e0d50), SkBits2Float(0x41b720c0), SkBits2Float(0x426ed3f8), SkBits2Float(0x41b69ba1), SkBits2Float(0x426f79dc));  // 22.891f, 59.513f, 22.891f, 59.707f, 22.826f, 59.869f

+path.cubicTo(SkBits2Float(0x41b64391), SkBits2Float(0x426f645b), SkBits2Float(0x41b62d09), SkBits2Float(0x426f79dc), SkBits2Float(0x41b5eb80), SkBits2Float(0x426f645b));  // 22.783f, 59.848f, 22.772f, 59.869f, 22.74f, 59.848f

+path.cubicTo(SkBits2Float(0x41b5a7ea), SkBits2Float(0x426f5917), SkBits2Float(0x41b57adc), SkBits2Float(0x426f374d), SkBits2Float(0x41b53953), SkBits2Float(0x426f1688));  // 22.707f, 59.837f, 22.685f, 59.804f, 22.653f, 59.772f

+path.lineTo(SkBits2Float(0x41b53953), SkBits2Float(0x426f0107));  // 22.653f, 59.751f

+path.cubicTo(SkBits2Float(0x41b472ab), SkBits2Float(0x426ea7f0), SkBits2Float(0x41b3ac03), SkBits2Float(0x426e5a1e), SkBits2Float(0x41b3105d), SkBits2Float(0x426e020d));  // 22.556f, 59.664f, 22.459f, 59.588f, 22.383f, 59.502f

+    testSimplify(reporter, path, filename);
+}

+
+static void joel_5(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x43c5145a), SkBits2Float(0x43dc82f2));  // 394.159f, 441.023f

+path.lineTo(SkBits2Float(0x43c5145a), SkBits2Float(0x43dc82f2));  // 394.159f, 441.023f

+path.close();

+path.moveTo(SkBits2Float(0x43af4e56), SkBits2Float(0x43dbc604));  // 350.612f, 439.547f

+path.lineTo(SkBits2Float(0x43af4e56), SkBits2Float(0x43dbc604));  // 350.612f, 439.547f

+path.close();

+path.moveTo(SkBits2Float(0x43af4e56), SkBits2Float(0x43dbc604));  // 350.612f, 439.547f

+path.cubicTo(SkBits2Float(0x43b64a5e), SkBits2Float(0x43dc9604), SkBits2Float(0x43be0958), SkBits2Float(0x43dbb604), SkBits2Float(0x43c5145a), SkBits2Float(0x43dc8312));  // 364.581f, 441.172f, 380.073f, 439.422f, 394.159f, 441.024f

+path.cubicTo(SkBits2Float(0x43be0958), SkBits2Float(0x43dbb604), SkBits2Float(0x43b64a5e), SkBits2Float(0x43dc9604), SkBits2Float(0x43af4e56), SkBits2Float(0x43dbc604));  // 380.073f, 439.422f, 364.581f, 441.172f, 350.612f, 439.547f

+path.close();

+path.moveTo(SkBits2Float(0x43a9126f), SkBits2Float(0x43e11604));  // 338.144f, 450.172f

+path.lineTo(SkBits2Float(0x43a9126f), SkBits2Float(0x43e11604));  // 338.144f, 450.172f

+path.close();

+path.moveTo(SkBits2Float(0x43a9126f), SkBits2Float(0x43e11604));  // 338.144f, 450.172f

+path.cubicTo(SkBits2Float(0x43ab3c6b), SkBits2Float(0x43debc08), SkBits2Float(0x43ad1b65), SkBits2Float(0x43de18f6), SkBits2Float(0x43af4e77), SkBits2Float(0x43dbc604));  // 342.472f, 445.469f, 346.214f, 444.195f, 350.613f, 439.547f

+path.cubicTo(SkBits2Float(0x43ad1b65), SkBits2Float(0x43de18f6), SkBits2Float(0x43ab3c6b), SkBits2Float(0x43debc08), SkBits2Float(0x43a9126f), SkBits2Float(0x43e11604));  // 346.214f, 444.195f, 342.472f, 445.469f, 338.144f, 450.172f

+path.close();

+path.moveTo(SkBits2Float(0x43aa9d50), SkBits2Float(0x43e173f8));  // 341.229f, 450.906f

+path.lineTo(SkBits2Float(0x43aa9d50), SkBits2Float(0x43e173f8));  // 341.229f, 450.906f

+path.close();

+path.moveTo(SkBits2Float(0x43aa9d50), SkBits2Float(0x43e173f8));  // 341.229f, 450.906f

+path.cubicTo(SkBits2Float(0x43aa0852), SkBits2Float(0x43e183f8), SkBits2Float(0x43a9be56), SkBits2Float(0x43e0d2f2), SkBits2Float(0x43a9124e), SkBits2Float(0x43e11604));  // 340.065f, 451.031f, 339.487f, 449.648f, 338.143f, 450.172f

+path.cubicTo(SkBits2Float(0x43a9be56), SkBits2Float(0x43e0d2f2), SkBits2Float(0x43aa0852), SkBits2Float(0x43e183f8), SkBits2Float(0x43aa9d50), SkBits2Float(0x43e173f8));  // 339.487f, 449.648f, 340.065f, 451.031f, 341.229f, 450.906f

+path.close();

+path.moveTo(SkBits2Float(0x43b13667), SkBits2Float(0x43dce106));  // 354.425f, 441.758f

+path.lineTo(SkBits2Float(0x43b13667), SkBits2Float(0x43dce106));  // 354.425f, 441.758f

+path.close();

+path.moveTo(SkBits2Float(0x43b13667), SkBits2Float(0x43dce106));  // 354.425f, 441.758f

+path.cubicTo(SkBits2Float(0x43aead71), SkBits2Float(0x43dd9d0e), SkBits2Float(0x43acd375), SkBits2Float(0x43dff20c), SkBits2Float(0x43aa9d71), SkBits2Float(0x43e173f8));  // 349.355f, 443.227f, 345.652f, 447.891f, 341.23f, 450.906f

+path.cubicTo(SkBits2Float(0x43acd354), SkBits2Float(0x43dff20c), SkBits2Float(0x43aead50), SkBits2Float(0x43dd9d0f), SkBits2Float(0x43b13667), SkBits2Float(0x43dce106));  // 345.651f, 447.891f, 349.354f, 443.227f, 354.425f, 441.758f

+path.close();

+path.moveTo(SkBits2Float(0x43ac8561), SkBits2Float(0x43e30106));  // 345.042f, 454.008f

+path.lineTo(SkBits2Float(0x43ac8561), SkBits2Float(0x43e30106));  // 345.042f, 454.008f

+path.close();

+path.moveTo(SkBits2Float(0x43ac8561), SkBits2Float(0x43e30106));  // 345.042f, 454.008f

+path.cubicTo(SkBits2Float(0x43adc76d), SkBits2Float(0x43e0f4fe), SkBits2Float(0x43b21a5f), SkBits2Float(0x43df7efa), SkBits2Float(0x43b13667), SkBits2Float(0x43dce106));  // 347.558f, 449.914f, 356.206f, 446.992f, 354.425f, 441.758f

+path.cubicTo(SkBits2Float(0x43b21a5f), SkBits2Float(0x43df7efa), SkBits2Float(0x43adc76d), SkBits2Float(0x43e0f4fe), SkBits2Float(0x43ac8561), SkBits2Float(0x43e30106));  // 356.206f, 446.992f, 347.558f, 449.914f, 345.042f, 454.008f

+path.close();

+path.moveTo(SkBits2Float(0x43b33169), SkBits2Float(0x43dc82f2));  // 358.386f, 441.023f

+path.lineTo(SkBits2Float(0x43b33169), SkBits2Float(0x43dc82f2));  // 358.386f, 441.023f

+path.close();

+path.moveTo(SkBits2Float(0x43b33169), SkBits2Float(0x43dc82f2));  // 358.386f, 441.023f

+path.cubicTo(SkBits2Float(0x43b16169), SkBits2Float(0x43ded7f0), SkBits2Float(0x43aef375), SkBits2Float(0x43e13be8), SkBits2Float(0x43ac8561), SkBits2Float(0x43e300e6));  // 354.761f, 445.687f, 349.902f, 450.468f, 345.042f, 454.007f

+path.cubicTo(SkBits2Float(0x43aef355), SkBits2Float(0x43e13c09), SkBits2Float(0x43b16169), SkBits2Float(0x43ded811), SkBits2Float(0x43b33169), SkBits2Float(0x43dc82f2));  // 349.901f, 450.469f, 354.761f, 445.688f, 358.386f, 441.023f

+path.close();

+path.moveTo(SkBits2Float(0x43b4bb65), SkBits2Float(0x43dd4000));  // 361.464f, 442.5f

+path.lineTo(SkBits2Float(0x43b4bb65), SkBits2Float(0x43dd4000));  // 361.464f, 442.5f

+path.close();

+path.moveTo(SkBits2Float(0x43b4bb65), SkBits2Float(0x43dd4000));  // 361.464f, 442.5f

+path.cubicTo(SkBits2Float(0x43b44959), SkBits2Float(0x43dcddf4), SkBits2Float(0x43b3e76d), SkBits2Float(0x43dc48f6), SkBits2Float(0x43b33169), SkBits2Float(0x43dc82f2));  // 360.573f, 441.734f, 359.808f, 440.57f, 358.386f, 441.023f

+path.cubicTo(SkBits2Float(0x43b3e76d), SkBits2Float(0x43dc48f6), SkBits2Float(0x43b44959), SkBits2Float(0x43dcddf4), SkBits2Float(0x43b4bb65), SkBits2Float(0x43dd4000));  // 359.808f, 440.57f, 360.573f, 441.734f, 361.464f, 442.5f

+path.close();

+path.moveTo(SkBits2Float(0x43ae7f5d), SkBits2Float(0x43e5a70a));  // 348.995f, 459.305f

+path.lineTo(SkBits2Float(0x43ae7f5d), SkBits2Float(0x43e5a70a));  // 348.995f, 459.305f

+path.close();

+path.moveTo(SkBits2Float(0x43ae7f5d), SkBits2Float(0x43e5a70a));  // 348.995f, 459.305f

+path.cubicTo(SkBits2Float(0x43af945b), SkBits2Float(0x43e21d0e), SkBits2Float(0x43b3a74d), SkBits2Float(0x43e0ce14), SkBits2Float(0x43b4bb65), SkBits2Float(0x43dd4000));  // 351.159f, 452.227f, 359.307f, 449.61f, 361.464f, 442.5f

+path.cubicTo(SkBits2Float(0x43b3a76d), SkBits2Float(0x43e0cdf4), SkBits2Float(0x43af945b), SkBits2Float(0x43e21d0e), SkBits2Float(0x43ae7f5d), SkBits2Float(0x43e5a70a));  // 359.308f, 449.609f, 351.159f, 452.227f, 348.995f, 459.305f

+path.close();

+path.moveTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43dce106));  // 363.081f, 441.758f

+path.lineTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43dce106));  // 363.081f, 441.758f

+path.close();

+path.moveTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43dce106));  // 363.081f, 441.758f

+path.cubicTo(SkBits2Float(0x43b2c063), SkBits2Float(0x43dfa604), SkBits2Float(0x43b1d561), SkBits2Float(0x43e374fe), SkBits2Float(0x43ae7f5d), SkBits2Float(0x43e5a70a));  // 357.503f, 447.297f, 355.667f, 454.914f, 348.995f, 459.305f

+path.cubicTo(SkBits2Float(0x43b1d561), SkBits2Float(0x43e374fe), SkBits2Float(0x43b2c063), SkBits2Float(0x43dfa604), SkBits2Float(0x43b58a5f), SkBits2Float(0x43dce106));  // 355.667f, 454.914f, 357.503f, 447.297f, 363.081f, 441.758f

+path.close();

+path.moveTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43dd4000));  // 365.417f, 442.5f

+path.lineTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43dd4000));  // 365.417f, 442.5f

+path.close();

+path.moveTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43dd4000));  // 365.417f, 442.5f

+path.lineTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43dce106));  // 363.081f, 441.758f

+path.lineTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43dd4000));  // 365.417f, 442.5f

+path.close();

+path.moveTo(SkBits2Float(0x43b07a5f), SkBits2Float(0x43e7220c));  // 352.956f, 462.266f

+path.lineTo(SkBits2Float(0x43b07a5f), SkBits2Float(0x43e7220c));  // 352.956f, 462.266f

+path.close();

+path.moveTo(SkBits2Float(0x43b07a5f), SkBits2Float(0x43e7220c));  // 352.956f, 462.266f

+path.cubicTo(SkBits2Float(0x43b29f5d), SkBits2Float(0x43e3e810), SkBits2Float(0x43b59667), SkBits2Float(0x43e0f916), SkBits2Float(0x43b6b561), SkBits2Float(0x43dd4000));  // 357.245f, 455.813f, 363.175f, 449.946f, 365.417f, 442.5f

+path.cubicTo(SkBits2Float(0x43b59667), SkBits2Float(0x43e0f8f6), SkBits2Float(0x43b29f5d), SkBits2Float(0x43e3e7f0), SkBits2Float(0x43b07a5f), SkBits2Float(0x43e7220c));  // 363.175f, 449.945f, 357.245f, 455.812f, 352.956f, 462.266f

+path.close();

+path.moveTo(SkBits2Float(0x43b0d853), SkBits2Float(0x43e84efa));  // 353.69f, 464.617f

+path.lineTo(SkBits2Float(0x43b0d853), SkBits2Float(0x43e84efa));  // 353.69f, 464.617f

+path.close();

+path.moveTo(SkBits2Float(0x43b0d853), SkBits2Float(0x43e84efa));  // 353.69f, 464.617f

+path.cubicTo(SkBits2Float(0x43b03a5f), SkBits2Float(0x43e934fe), SkBits2Float(0x43b1345b), SkBits2Float(0x43e7870a), SkBits2Float(0x43b07a5f), SkBits2Float(0x43e721ec));  // 352.456f, 466.414f, 354.409f, 463.055f, 352.956f, 462.265f

+path.cubicTo(SkBits2Float(0x43b1345b), SkBits2Float(0x43e7870b), SkBits2Float(0x43b03a5f), SkBits2Float(0x43e934fe), SkBits2Float(0x43b0d853), SkBits2Float(0x43e84efa));  // 354.409f, 463.055f, 352.456f, 466.414f, 353.69f, 464.617f

+path.close();

+path.moveTo(SkBits2Float(0x43b84063), SkBits2Float(0x43ddb106));  // 368.503f, 443.383f

+path.lineTo(SkBits2Float(0x43b84063), SkBits2Float(0x43ddb106));  // 368.503f, 443.383f

+path.close();

+path.moveTo(SkBits2Float(0x43b84063), SkBits2Float(0x43ddb106));  // 368.503f, 443.383f

+path.cubicTo(SkBits2Float(0x43b42667), SkBits2Float(0x43e039fc), SkBits2Float(0x43b39d71), SkBits2Float(0x43e4e000), SkBits2Float(0x43b0d873), SkBits2Float(0x43e84efa));  // 360.3f, 448.453f, 359.23f, 457.75f, 353.691f, 464.617f

+path.cubicTo(SkBits2Float(0x43b39d50), SkBits2Float(0x43e4e000), SkBits2Float(0x43b42667), SkBits2Float(0x43e039fc), SkBits2Float(0x43b84063), SkBits2Float(0x43ddb106));  // 359.229f, 457.75f, 360.3f, 448.453f, 368.503f, 443.383f

+path.close();

+path.moveTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43de0efa));  // 369.229f, 444.117f

+path.lineTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43de0efa));  // 369.229f, 444.117f

+path.close();

+path.moveTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43de0efa));  // 369.229f, 444.117f

+path.lineTo(SkBits2Float(0x43b84043), SkBits2Float(0x43ddb106));  // 368.502f, 443.383f

+path.lineTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43de0efa));  // 369.229f, 444.117f

+path.close();

+path.moveTo(SkBits2Float(0x43b26270), SkBits2Float(0x43e90c08));  // 356.769f, 466.094f

+path.lineTo(SkBits2Float(0x43b26270), SkBits2Float(0x43e90c08));  // 356.769f, 466.094f

+path.close();

+path.moveTo(SkBits2Float(0x43b26270), SkBits2Float(0x43e90c08));  // 356.769f, 466.094f

+path.cubicTo(SkBits2Float(0x43b48d72), SkBits2Float(0x43e569fc), SkBits2Float(0x43b7897a), SkBits2Float(0x43e21d0e), SkBits2Float(0x43b89d72), SkBits2Float(0x43de0efa));  // 361.105f, 458.828f, 367.074f, 452.227f, 369.23f, 444.117f

+path.cubicTo(SkBits2Float(0x43b78959), SkBits2Float(0x43e21d0e), SkBits2Float(0x43b48d51), SkBits2Float(0x43e569fc), SkBits2Float(0x43b26270), SkBits2Float(0x43e90c08));  // 367.073f, 452.227f, 361.104f, 458.828f, 356.769f, 466.094f

+path.close();

+path.moveTo(SkBits2Float(0x43b3316a), SkBits2Float(0x43e90c08));  // 358.386f, 466.094f

+path.lineTo(SkBits2Float(0x43b3316a), SkBits2Float(0x43e90c08));  // 358.386f, 466.094f

+path.close();

+path.moveTo(SkBits2Float(0x43b3316a), SkBits2Float(0x43e90c08));  // 358.386f, 466.094f

+path.lineTo(SkBits2Float(0x43b26270), SkBits2Float(0x43e90c08));  // 356.769f, 466.094f

+path.lineTo(SkBits2Float(0x43b3316a), SkBits2Float(0x43e90c08));  // 358.386f, 466.094f

+path.close();

+path.moveTo(SkBits2Float(0x43ba2853), SkBits2Float(0x43ddb106));  // 372.315f, 443.383f

+path.lineTo(SkBits2Float(0x43ba2853), SkBits2Float(0x43ddb106));  // 372.315f, 443.383f

+path.close();

+path.moveTo(SkBits2Float(0x43ba2853), SkBits2Float(0x43ddb106));  // 372.315f, 443.383f

+path.cubicTo(SkBits2Float(0x43b7d74d), SkBits2Float(0x43e17604), SkBits2Float(0x43b5824f), SkBits2Float(0x43e59604), SkBits2Float(0x43b33149), SkBits2Float(0x43e90c08));  // 367.682f, 450.922f, 363.018f, 459.172f, 358.385f, 466.094f

+path.cubicTo(SkBits2Float(0x43b58270), SkBits2Float(0x43e59604), SkBits2Float(0x43b7d76e), SkBits2Float(0x43e17604), SkBits2Float(0x43ba2853), SkBits2Float(0x43ddb106));  // 363.019f, 459.172f, 367.683f, 450.922f, 372.315f, 443.383f

+path.close();

+path.moveTo(SkBits2Float(0x43bb5355), SkBits2Float(0x43de0efa));  // 374.651f, 444.117f

+path.lineTo(SkBits2Float(0x43bb5355), SkBits2Float(0x43de0efa));  // 374.651f, 444.117f

+path.close();

+path.moveTo(SkBits2Float(0x43bb5355), SkBits2Float(0x43de0efa));  // 374.651f, 444.117f

+path.cubicTo(SkBits2Float(0x43bb1853), SkBits2Float(0x43dd92f2), SkBits2Float(0x43ba9e57), SkBits2Float(0x43ddab02), SkBits2Float(0x43ba2853), SkBits2Float(0x43ddb106));  // 374.19f, 443.148f, 373.237f, 443.336f, 372.315f, 443.383f

+path.cubicTo(SkBits2Float(0x43ba9e57), SkBits2Float(0x43ddab02), SkBits2Float(0x43bb1853), SkBits2Float(0x43dd92f2), SkBits2Float(0x43bb5355), SkBits2Float(0x43de0efa));  // 373.237f, 443.336f, 374.19f, 443.148f, 374.651f, 444.117f

+path.close();

+path.moveTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43e90c08));  // 363.081f, 466.094f

+path.lineTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43e90c08));  // 363.081f, 466.094f

+path.close();

+path.moveTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43e90c08));  // 363.081f, 466.094f

+path.cubicTo(SkBits2Float(0x43b76c6b), SkBits2Float(0x43e55d0e), SkBits2Float(0x43ba4a5f), SkBits2Float(0x43e21312), SkBits2Float(0x43bb5355), SkBits2Float(0x43de0efa));  // 366.847f, 458.727f, 372.581f, 452.149f, 374.651f, 444.117f

+path.cubicTo(SkBits2Float(0x43ba4a5f), SkBits2Float(0x43e212f2), SkBits2Float(0x43b76c6c), SkBits2Float(0x43e55d0e), SkBits2Float(0x43b58a5f), SkBits2Float(0x43e90c08));  // 372.581f, 452.148f, 366.847f, 458.727f, 363.081f, 466.094f

+path.close();

+path.moveTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43e90c08));  // 365.417f, 466.094f

+path.lineTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43e90c08));  // 365.417f, 466.094f

+path.close();

+path.moveTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43e90c08));  // 365.417f, 466.094f

+path.lineTo(SkBits2Float(0x43b58a5f), SkBits2Float(0x43e90c08));  // 363.081f, 466.094f

+path.lineTo(SkBits2Float(0x43b6b561), SkBits2Float(0x43e90c08));  // 365.417f, 466.094f

+path.close();

+path.moveTo(SkBits2Float(0x43bc8063), SkBits2Float(0x43e058f6));  // 377.003f, 448.695f

+path.lineTo(SkBits2Float(0x43bc8063), SkBits2Float(0x43e058f6));  // 377.003f, 448.695f

+path.close();

+path.moveTo(SkBits2Float(0x43bc8063), SkBits2Float(0x43e058f6));  // 377.003f, 448.695f

+path.cubicTo(SkBits2Float(0x43b9de57), SkBits2Float(0x43e29df4), SkBits2Float(0x43b84355), SkBits2Float(0x43e5fefa), SkBits2Float(0x43b6b561), SkBits2Float(0x43e90be8));  // 371.737f, 453.234f, 368.526f, 459.992f, 365.417f, 466.093f

+path.cubicTo(SkBits2Float(0x43b84355), SkBits2Float(0x43e5fefa), SkBits2Float(0x43b9de57), SkBits2Float(0x43e29df4), SkBits2Float(0x43bc8063), SkBits2Float(0x43e058f6));  // 368.526f, 459.992f, 371.737f, 453.234f, 377.003f, 448.695f

+path.close();

+path.moveTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43e969fc));  // 369.229f, 466.828f

+path.lineTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43e969fc));  // 369.229f, 466.828f

+path.close();

+path.moveTo(SkBits2Float(0x43b89d51), SkBits2Float(0x43e969fc));  // 369.229f, 466.828f

+path.cubicTo(SkBits2Float(0x43b98149), SkBits2Float(0x43e637f0), SkBits2Float(0x43bd3355), SkBits2Float(0x43e3adf4), SkBits2Float(0x43bc8043), SkBits2Float(0x43e058f6));  // 371.01f, 460.437f, 378.401f, 455.359f, 377.002f, 448.695f

+path.cubicTo(SkBits2Float(0x43bd3355), SkBits2Float(0x43e3adf4), SkBits2Float(0x43b9816a), SkBits2Float(0x43e638f6), SkBits2Float(0x43b89d51), SkBits2Float(0x43e969fc));  // 378.401f, 455.359f, 371.011f, 460.445f, 369.229f, 466.828f

+path.close();

+path.moveTo(SkBits2Float(0x43ba8668), SkBits2Float(0x43e9c7f0));  // 373.05f, 467.562f

+path.lineTo(SkBits2Float(0x43ba8668), SkBits2Float(0x43e9c7f0));  // 373.05f, 467.562f

+path.close();

+path.moveTo(SkBits2Float(0x43ba8668), SkBits2Float(0x43e9c7f0));  // 373.05f, 467.562f

+path.cubicTo(SkBits2Float(0x43ba1376), SkBits2Float(0x43e90000), SkBits2Float(0x43b94270), SkBits2Float(0x43e8f1ec), SkBits2Float(0x43b89d72), SkBits2Float(0x43e969fc));  // 372.152f, 466, 370.519f, 465.89f, 369.23f, 466.828f

+path.cubicTo(SkBits2Float(0x43b94270), SkBits2Float(0x43e8f20c), SkBits2Float(0x43ba1355), SkBits2Float(0x43e90000), SkBits2Float(0x43ba8668), SkBits2Float(0x43e9c7f0));  // 370.519f, 465.891f, 372.151f, 466, 373.05f, 467.562f

+path.close();

+path.moveTo(SkBits2Float(0x43c0c064), SkBits2Float(0x43dc82f2));  // 385.503f, 441.023f

+path.lineTo(SkBits2Float(0x43c0c064), SkBits2Float(0x43dc82f2));  // 385.503f, 441.023f

+path.close();

+path.moveTo(SkBits2Float(0x43c0c064), SkBits2Float(0x43dc82f2));  // 385.503f, 441.023f

+path.cubicTo(SkBits2Float(0x43be095a), SkBits2Float(0x43e0acee), SkBits2Float(0x43bd8a60), SkBits2Float(0x43e5c0e6), SkBits2Float(0x43ba8668), SkBits2Float(0x43e9c7f0));  // 380.073f, 449.351f, 379.081f, 459.507f, 373.05f, 467.562f

+path.cubicTo(SkBits2Float(0x43bd8a60), SkBits2Float(0x43e5c107), SkBits2Float(0x43be095a), SkBits2Float(0x43e0ad0f), SkBits2Float(0x43c0c064), SkBits2Float(0x43dc82f2));  // 379.081f, 459.508f, 380.073f, 449.352f, 385.503f, 441.023f

+path.close();

+path.moveTo(SkBits2Float(0x43c00562), SkBits2Float(0x43e23000));  // 384.042f, 452.375f

+path.lineTo(SkBits2Float(0x43c00562), SkBits2Float(0x43e23000));  // 384.042f, 452.375f

+path.close();

+path.moveTo(SkBits2Float(0x43c00562), SkBits2Float(0x43e23000));  // 384.042f, 452.375f

+path.cubicTo(SkBits2Float(0x43bfaf5e), SkBits2Float(0x43e013f8), SkBits2Float(0x43c40668), SkBits2Float(0x43ddc2f2), SkBits2Float(0x43c0c064), SkBits2Float(0x43dc82f2));  // 383.37f, 448.156f, 392.05f, 443.523f, 385.503f, 441.023f

+path.cubicTo(SkBits2Float(0x43c40668), SkBits2Float(0x43ddc2f2), SkBits2Float(0x43bfaf5e), SkBits2Float(0x43e013f8), SkBits2Float(0x43c00562), SkBits2Float(0x43e23000));  // 392.05f, 443.523f, 383.37f, 448.156f, 384.042f, 452.375f

+path.close();

+path.moveTo(SkBits2Float(0x43bed854), SkBits2Float(0x43e5370a));  // 381.69f, 458.43f

+path.lineTo(SkBits2Float(0x43bed854), SkBits2Float(0x43e5370a));  // 381.69f, 458.43f

+path.close();

+path.moveTo(SkBits2Float(0x43bed854), SkBits2Float(0x43e5370a));  // 381.69f, 458.43f

+path.cubicTo(SkBits2Float(0x43c06562), SkBits2Float(0x43e4b4fe), SkBits2Float(0x43bf095a), SkBits2Float(0x43e2fd0e), SkBits2Float(0x43c00562), SkBits2Float(0x43e23000));  // 384.792f, 457.414f, 382.073f, 453.977f, 384.042f, 452.375f

+path.cubicTo(SkBits2Float(0x43bf095a), SkBits2Float(0x43e2fdf4), SkBits2Float(0x43c06562), SkBits2Float(0x43e4b4fe), SkBits2Float(0x43bed854), SkBits2Float(0x43e5370a));  // 382.073f, 453.984f, 384.792f, 457.414f, 381.69f, 458.43f

+path.close();

+path.moveTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43e5a70a));  // 382.425f, 459.305f

+path.lineTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43e5a70a));  // 382.425f, 459.305f

+path.close();

+path.moveTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43e5a70a));  // 382.425f, 459.305f

+path.lineTo(SkBits2Float(0x43bed874), SkBits2Float(0x43e5370a));  // 381.691f, 458.43f

+path.lineTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43e5a70a));  // 382.425f, 459.305f

+path.close();

+path.moveTo(SkBits2Float(0x43bcde58), SkBits2Float(0x43e9c7ef));  // 377.737f, 467.562f

+path.lineTo(SkBits2Float(0x43bcde58), SkBits2Float(0x43e9c7ef));  // 377.737f, 467.562f

+path.close();

+path.moveTo(SkBits2Float(0x43bcde58), SkBits2Float(0x43e9c7ef));  // 377.737f, 467.562f

+path.cubicTo(SkBits2Float(0x43bdfb66), SkBits2Float(0x43e888f5), SkBits2Float(0x43bd6854), SkBits2Float(0x43e69ced), SkBits2Float(0x43bf3668), SkBits2Float(0x43e5a6e9));  // 379.964f, 465.07f, 378.815f, 461.226f, 382.425f, 459.304f

+path.cubicTo(SkBits2Float(0x43bd6854), SkBits2Float(0x43e69d0e), SkBits2Float(0x43bdfb66), SkBits2Float(0x43e888f5), SkBits2Float(0x43bcde58), SkBits2Float(0x43e9c7ef));  // 378.815f, 461.227f, 379.964f, 465.07f, 377.737f, 467.562f

+path.close();

+path.moveTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43ea9810));  // 382.425f, 469.188f

+path.lineTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43ea9810));  // 382.425f, 469.188f

+path.close();

+path.moveTo(SkBits2Float(0x43bf3668), SkBits2Float(0x43ea9810));  // 382.425f, 469.188f

+path.cubicTo(SkBits2Float(0x43bebf5e), SkBits2Float(0x43e99e14), SkBits2Float(0x43bdc562), SkBits2Float(0x43e9d70a), SkBits2Float(0x43bcde58), SkBits2Float(0x43e9c810));  // 381.495f, 467.235f, 379.542f, 467.68f, 377.737f, 467.563f

+path.cubicTo(SkBits2Float(0x43bdc562), SkBits2Float(0x43e9d70a), SkBits2Float(0x43bebf5e), SkBits2Float(0x43e99df3), SkBits2Float(0x43bf3668), SkBits2Float(0x43ea9810));  // 379.542f, 467.68f, 381.495f, 467.234f, 382.425f, 469.188f

+path.close();

+path.moveTo(SkBits2Float(0x43c0c064), SkBits2Float(0x43e78000));  // 385.503f, 463

+path.lineTo(SkBits2Float(0x43c0c064), SkBits2Float(0x43e78000));  // 385.503f, 463

+path.close();

+path.moveTo(SkBits2Float(0x43c0c064), SkBits2Float(0x43e78000));  // 385.503f, 463

+path.cubicTo(SkBits2Float(0x43bfaf5e), SkBits2Float(0x43e7f9fc), SkBits2Float(0x43bfbe58), SkBits2Float(0x43e98b02), SkBits2Float(0x43bf3668), SkBits2Float(0x43ea9810));  // 383.37f, 463.953f, 383.487f, 467.086f, 382.425f, 469.188f

+path.cubicTo(SkBits2Float(0x43bfbe58), SkBits2Float(0x43e98b02), SkBits2Float(0x43bfaf5e), SkBits2Float(0x43e7f9fc), SkBits2Float(0x43c0c064), SkBits2Float(0x43e78000));  // 383.487f, 467.086f, 383.37f, 463.953f, 385.503f, 463

+path.close();

+path.moveTo(SkBits2Float(0x43c1316a), SkBits2Float(0x43e35efa));  // 386.386f, 454.742f

+path.lineTo(SkBits2Float(0x43c1316a), SkBits2Float(0x43e35efa));  // 386.386f, 454.742f

+path.close();

+path.moveTo(SkBits2Float(0x43c1316a), SkBits2Float(0x43e35efa));  // 386.386f, 454.742f

+path.cubicTo(SkBits2Float(0x43c35270), SkBits2Float(0x43e586ea), SkBits2Float(0x43beb064), SkBits2Float(0x43e561ec), SkBits2Float(0x43c0c064), SkBits2Float(0x43e78000));  // 390.644f, 459.054f, 381.378f, 458.765f, 385.503f, 463

+path.cubicTo(SkBits2Float(0x43beb064), SkBits2Float(0x43e5620c), SkBits2Float(0x43c35270), SkBits2Float(0x43e5870a), SkBits2Float(0x43c1316a), SkBits2Float(0x43e35efa));  // 381.378f, 458.766f, 390.644f, 459.055f, 386.386f, 454.742f

+path.close();

+path.moveTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43df2b02));  // 391.808f, 446.336f

+path.lineTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43df2b02));  // 391.808f, 446.336f

+path.close();

+path.moveTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43df2b02));  // 391.808f, 446.336f

+path.cubicTo(SkBits2Float(0x43c2ba60), SkBits2Float(0x43e07810), SkBits2Float(0x43c32a60), SkBits2Float(0x43e31106), SkBits2Float(0x43c1316a), SkBits2Float(0x43e35efa));  // 389.456f, 448.938f, 390.331f, 454.133f, 386.386f, 454.742f

+path.cubicTo(SkBits2Float(0x43c32a60), SkBits2Float(0x43e31106), SkBits2Float(0x43c2ba60), SkBits2Float(0x43e07811), SkBits2Float(0x43c3e76e), SkBits2Float(0x43df2b02));  // 390.331f, 454.133f, 389.456f, 448.938f, 391.808f, 446.336f

+path.close();

+path.moveTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43dd4000));  // 391.808f, 442.5f

+path.lineTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43dd4000));  // 391.808f, 442.5f

+path.close();

+path.moveTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43dd4000));  // 391.808f, 442.5f

+path.cubicTo(SkBits2Float(0x43c2a668), SkBits2Float(0x43ddbefa), SkBits2Float(0x43c35f7e), SkBits2Float(0x43def4fe), SkBits2Float(0x43c3e76e), SkBits2Float(0x43df2b02));  // 389.3f, 443.492f, 390.746f, 445.914f, 391.808f, 446.336f

+path.cubicTo(SkBits2Float(0x43c35f5e), SkBits2Float(0x43def4fe), SkBits2Float(0x43c2a668), SkBits2Float(0x43ddbefa), SkBits2Float(0x43c3e76e), SkBits2Float(0x43dd4000));  // 390.745f, 445.914f, 389.3f, 443.492f, 391.808f, 442.5f

+path.close();

+path.moveTo(SkBits2Float(0x43c44562), SkBits2Float(0x43ddb106));  // 392.542f, 443.383f

+path.lineTo(SkBits2Float(0x43c44562), SkBits2Float(0x43ddb106));  // 392.542f, 443.383f

+path.close();

+path.moveTo(SkBits2Float(0x43c44562), SkBits2Float(0x43ddb106));  // 392.542f, 443.383f

+path.lineTo(SkBits2Float(0x43c3e76e), SkBits2Float(0x43dd4000));  // 391.808f, 442.5f

+path.lineTo(SkBits2Float(0x43c44562), SkBits2Float(0x43ddb106));  // 392.542f, 443.383f

+path.close();

+path.moveTo(SkBits2Float(0x43c5145c), SkBits2Float(0x43dc82f2));  // 394.159f, 441.023f

+path.lineTo(SkBits2Float(0x43c44562), SkBits2Float(0x43ddb0e6));  // 392.542f, 443.382f

+path.lineTo(SkBits2Float(0x43c5145c), SkBits2Float(0x43dc82f2));  // 394.159f, 441.023f

+path.close();
+    testSimplify(reporter, path, filename);
+}

+
+static void joel_6(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.moveTo(SkBits2Float(0x43c38c6a), SkBits2Float(0x43a739fc));  // 391.097f, 334.453f

+path.lineTo(SkBits2Float(0x43c36168), SkBits2Float(0x43a74efa));  // 390.761f, 334.617f

+path.lineTo(SkBits2Float(0x43c33666), SkBits2Float(0x43a6f7f0));  // 390.425f, 333.937f

+path.lineTo(SkBits2Float(0x43c36168), SkBits2Float(0x43a6e1ec));  // 390.761f, 333.765f

+path.lineTo(SkBits2Float(0x43c38c6a), SkBits2Float(0x43a739fc));  // 391.097f, 334.453f

+path.close();

+path.moveTo(SkBits2Float(0x43c39062), SkBits2Float(0x43a73810));  // 391.128f, 334.438f

+path.lineTo(SkBits2Float(0x43c3676c), SkBits2Float(0x43a75106));  // 390.808f, 334.633f

+path.lineTo(SkBits2Float(0x43c33374), SkBits2Float(0x43a6fefa));  // 390.402f, 333.992f

+path.lineTo(SkBits2Float(0x43c35d70), SkBits2Float(0x43a6e3f8));  // 390.73f, 333.781f

+path.lineTo(SkBits2Float(0x43c39062), SkBits2Float(0x43a73811));  // 391.128f, 334.438f

+path.lineTo(SkBits2Float(0x43c39062), SkBits2Float(0x43a73810));  // 391.128f, 334.438f

+path.close();

+path.moveTo(SkBits2Float(0x43e38958), SkBits2Float(0x43971c08));  // 455.073f, 302.219f

+path.lineTo(SkBits2Float(0x43e3824e), SkBits2Float(0x43973000));  // 455.018f, 302.375f

+path.lineTo(SkBits2Float(0x43e36f5c), SkBits2Float(0x439739fc));  // 454.87f, 302.453f

+path.lineTo(SkBits2Float(0x43e35a5e), SkBits2Float(0x43970df4));  // 454.706f, 302.109f

+path.lineTo(SkBits2Float(0x43e38958), SkBits2Float(0x43971c08));  // 455.073f, 302.219f

+path.close();

+path.moveTo(SkBits2Float(0x43e36f5c), SkBits2Float(0x439739fc));  // 454.87f, 302.453f

+path.lineTo(SkBits2Float(0x43c38c6a), SkBits2Float(0x43a739fc));  // 391.097f, 334.453f

+path.lineTo(SkBits2Float(0x43c36168), SkBits2Float(0x43a6e1ec));  // 390.761f, 333.765f

+path.lineTo(SkBits2Float(0x43e3445a), SkBits2Float(0x4396e1ec));  // 454.534f, 301.765f

+path.lineTo(SkBits2Float(0x43e36f5c), SkBits2Float(0x439739fc));  // 454.87f, 302.453f

+path.close();

+path.moveTo(SkBits2Float(0x43e41f5c), SkBits2Float(0x43946efa));  // 456.245f, 296.867f

+path.lineTo(SkBits2Float(0x43e4545a), SkBits2Float(0x439479fc));  // 456.659f, 296.953f

+path.lineTo(SkBits2Float(0x43e44354), SkBits2Float(0x4394acee));  // 456.526f, 297.351f

+path.lineTo(SkBits2Float(0x43e41646), SkBits2Float(0x43949efa));  // 456.174f, 297.242f

+path.lineTo(SkBits2Float(0x43e41f5d), SkBits2Float(0x43946efa));  // 456.245f, 296.867f

+path.lineTo(SkBits2Float(0x43e41f5c), SkBits2Float(0x43946efa));  // 456.245f, 296.867f

+path.close();

+path.moveTo(SkBits2Float(0x43e44354), SkBits2Float(0x4394ad0e));  // 456.526f, 297.352f

+path.lineTo(SkBits2Float(0x43e38958), SkBits2Float(0x43971c08));  // 455.073f, 302.219f

+path.lineTo(SkBits2Float(0x43e32b64), SkBits2Float(0x43970000));  // 454.339f, 302

+path.lineTo(SkBits2Float(0x43e3e76c), SkBits2Float(0x43949106));  // 455.808f, 297.133f

+path.lineTo(SkBits2Float(0x43e44353), SkBits2Float(0x4394ad0e));  // 456.526f, 297.352f

+path.lineTo(SkBits2Float(0x43e44354), SkBits2Float(0x4394ad0e));  // 456.526f, 297.352f

+path.close();

+path.moveTo(SkBits2Float(0x43e17d50), SkBits2Float(0x4393f20c));  // 450.979f, 295.891f

+path.lineTo(SkBits2Float(0x43e18e56), SkBits2Float(0x4393e810));  // 451.112f, 295.813f

+path.lineTo(SkBits2Float(0x43e1a148), SkBits2Float(0x4393eb02));  // 451.26f, 295.836f

+path.lineTo(SkBits2Float(0x43e19852), SkBits2Float(0x43941b02));  // 451.19f, 296.211f

+path.lineTo(SkBits2Float(0x43e17d50), SkBits2Float(0x4393f20c));  // 450.979f, 295.891f

+path.close();

+path.moveTo(SkBits2Float(0x43e1a169), SkBits2Float(0x4393eb02));  // 451.261f, 295.836f

+path.lineTo(SkBits2Float(0x43e41f5d), SkBits2Float(0x43946efa));  // 456.245f, 296.867f

+path.lineTo(SkBits2Float(0x43e40b65), SkBits2Float(0x4394cefa));  // 456.089f, 297.617f

+path.lineTo(SkBits2Float(0x43e18d71), SkBits2Float(0x43944b02));  // 451.105f, 296.586f

+path.lineTo(SkBits2Float(0x43e1a169), SkBits2Float(0x4393eb02));  // 451.261f, 295.836f

+path.close();

+path.moveTo(SkBits2Float(0x43c35d50), SkBits2Float(0x43a6e3f8));  // 390.729f, 333.781f

+path.lineTo(SkBits2Float(0x43e17d50), SkBits2Float(0x4393f1ec));  // 450.979f, 295.89f

+path.lineTo(SkBits2Float(0x43e1b148), SkBits2Float(0x439443f8));  // 451.385f, 296.531f

+path.lineTo(SkBits2Float(0x43c39042), SkBits2Float(0x43a737f0));  // 391.127f, 334.437f

+path.lineTo(SkBits2Float(0x43c35d50), SkBits2Float(0x43a6e3f8));  // 390.729f, 333.781f

+path.close();
+testSimplify(reporter, path, filename);
+}

+
+static void joel_7(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.moveTo(SkBits2Float(0x4321220c), SkBits2Float(0x43eac70a));  // 161.133f, 469.555f

+path.lineTo(SkBits2Float(0x4321220c), SkBits2Float(0x43eac70a));  // 161.133f, 469.555f

+path.lineTo(SkBits2Float(0x431f8e14), SkBits2Float(0x43eb3b02));  // 159.555f, 470.461f

+path.lineTo(SkBits2Float(0x4321220c), SkBits2Float(0x43eac70a));  // 161.133f, 469.555f

+path.close();

+path.moveTo(SkBits2Float(0x431e33f8), SkBits2Float(0x43f03b02));  // 158.203f, 480.461f

+path.lineTo(SkBits2Float(0x431e33f8), SkBits2Float(0x43f03b02));  // 158.203f, 480.461f

+path.lineTo(SkBits2Float(0x431e33f8), SkBits2Float(0x43f03b02));  // 158.203f, 480.461f

+path.lineTo(SkBits2Float(0x431d4c08), SkBits2Float(0x43ef720c));  // 157.297f, 478.891f

+path.lineTo(SkBits2Float(0x431e33f8), SkBits2Float(0x43f03b02));  // 158.203f, 480.461f

+path.close();

+path.moveTo(SkBits2Float(0x431c6419), SkBits2Float(0x43eea7f0));  // 156.391f, 477.312f

+path.cubicTo(SkBits2Float(0x431d6e15), SkBits2Float(0x43ee5ae2), SkBits2Float(0x431e2000), SkBits2Float(0x43ede000), SkBits2Float(0x431e69fc), SkBits2Float(0x43ed55e4));  // 157.43f, 476.71f, 158.125f, 475.75f, 158.414f, 474.671f

+path.cubicTo(SkBits2Float(0x431eb3f8), SkBits2Float(0x43eccbc8), SkBits2Float(0x431e93f8), SkBits2Float(0x43ec35e4), SkBits2Float(0x431df9db), SkBits2Float(0x43ebafe0));  // 158.703f, 473.592f, 158.578f, 472.421f, 157.976f, 471.374f

+path.lineTo(SkBits2Float(0x432121cb), SkBits2Float(0x43eac6ea));  // 161.132f, 469.554f

+path.cubicTo(SkBits2Float(0x432355c3), SkBits2Float(0x43ecb0e6), SkBits2Float(0x432207ae), SkBits2Float(0x43ef1fe0), SkBits2Float(0x431e33b7), SkBits2Float(0x43f03ae2));  // 163.335f, 473.382f, 162.03f, 478.249f, 158.202f, 480.46f

+path.lineTo(SkBits2Float(0x431c6419), SkBits2Float(0x43eea7f0));  // 156.391f, 477.312f

+path.close();

+path.moveTo(SkBits2Float(0x43134c08), SkBits2Float(0x43eec4fe));  // 147.297f, 477.539f

+path.lineTo(SkBits2Float(0x43134c08), SkBits2Float(0x43eec4fe));  // 147.297f, 477.539f

+path.lineTo(SkBits2Float(0x43134c08), SkBits2Float(0x43eec4fe));  // 147.297f, 477.539f

+path.lineTo(SkBits2Float(0x4314e20c), SkBits2Float(0x43ee5106));  // 148.883f, 476.633f

+path.lineTo(SkBits2Float(0x43134c08), SkBits2Float(0x43eec4fe));  // 147.297f, 477.539f

+path.close();

+path.moveTo(SkBits2Float(0x431673f8), SkBits2Float(0x43eddc08));  // 150.453f, 475.719f

+path.cubicTo(SkBits2Float(0x43170e15), SkBits2Float(0x43ee620c), SkBits2Float(0x43180000), SkBits2Float(0x43eebb02), SkBits2Float(0x43191604), SkBits2Float(0x43eee000));  // 151.055f, 476.766f, 152, 477.461f, 153.086f, 477.75f

+path.cubicTo(SkBits2Float(0x431a2c08), SkBits2Float(0x43ef04fe), SkBits2Float(0x431b5810), SkBits2Float(0x43eef4fe), SkBits2Float(0x431c6418), SkBits2Float(0x43eea7f0));  // 154.172f, 478.039f, 155.344f, 477.914f, 156.391f, 477.312f

+path.lineTo(SkBits2Float(0x431e33f7), SkBits2Float(0x43f03ae2));  // 158.203f, 480.46f

+path.cubicTo(SkBits2Float(0x431a620b), SkBits2Float(0x43f154de), SkBits2Float(0x4315820c), SkBits2Float(0x43f0add4), SkBits2Float(0x43134c07), SkBits2Float(0x43eec4de));  // 154.383f, 482.663f, 149.508f, 481.358f, 147.297f, 477.538f

+path.lineTo(SkBits2Float(0x431673f8), SkBits2Float(0x43eddc08));  // 150.453f, 475.719f

+path.close();

+path.moveTo(SkBits2Float(0x43163a1d), SkBits2Float(0x43e95106));  // 150.227f, 466.633f

+path.lineTo(SkBits2Float(0x43163a1d), SkBits2Float(0x43e95106));  // 150.227f, 466.633f

+path.lineTo(SkBits2Float(0x4317220d), SkBits2Float(0x43ea19fc));  // 151.133f, 468.203f

+path.lineTo(SkBits2Float(0x43163a1d), SkBits2Float(0x43e95106));  // 150.227f, 466.633f

+path.close();

+path.moveTo(SkBits2Float(0x43180c08), SkBits2Float(0x43eae3f8));  // 152.047f, 469.781f

+path.cubicTo(SkBits2Float(0x43170000), SkBits2Float(0x43eb31ec), SkBits2Float(0x43164e14), SkBits2Float(0x43ebabe8), SkBits2Float(0x43160418), SkBits2Float(0x43ec3604));  // 151, 470.39f, 150.305f, 471.343f, 150.016f, 472.422f

+path.cubicTo(SkBits2Float(0x4315ba1c), SkBits2Float(0x43ecc106), SkBits2Float(0x4315d810), SkBits2Float(0x43ed570a), SkBits2Float(0x43167439), SkBits2Float(0x43eddc08));  // 149.727f, 473.508f, 149.844f, 474.68f, 150.454f, 475.719f

+path.lineTo(SkBits2Float(0x43134c49), SkBits2Float(0x43eec4fe));  // 147.298f, 477.539f

+path.cubicTo(SkBits2Float(0x43111851), SkBits2Float(0x43ecdb02), SkBits2Float(0x43126830), SkBits2Float(0x43ea6c08), SkBits2Float(0x43163a5d), SkBits2Float(0x43e95106));  // 145.095f, 473.711f, 146.407f, 468.844f, 150.228f, 466.633f

+path.lineTo(SkBits2Float(0x43180c08), SkBits2Float(0x43eae3f8));  // 152.047f, 469.781f

+path.close();

+path.moveTo(SkBits2Float(0x431dfa1d), SkBits2Float(0x43ebb000));  // 157.977f, 471.375f

+path.cubicTo(SkBits2Float(0x431d620d), SkBits2Float(0x43eb29fc), SkBits2Float(0x431c6e15), SkBits2Float(0x43ead20c), SkBits2Float(0x431b5811), SkBits2Float(0x43eaad0e));  // 157.383f, 470.328f, 156.43f, 469.641f, 155.344f, 469.352f

+path.cubicTo(SkBits2Float(0x431a420d), SkBits2Float(0x43ea8810), SkBits2Float(0x43191605), SkBits2Float(0x43ea970a), SkBits2Float(0x43180c09), SkBits2Float(0x43eae418));  // 154.258f, 469.063f, 153.086f, 469.18f, 152.047f, 469.782f

+path.lineTo(SkBits2Float(0x43163a1d), SkBits2Float(0x43e95126));  // 150.227f, 466.634f

+path.cubicTo(SkBits2Float(0x431a0c09), SkBits2Float(0x43e8372a), SkBits2Float(0x431eec08), SkBits2Float(0x43e8de34), SkBits2Float(0x4321220d), SkBits2Float(0x43eac72a));  // 154.047f, 464.431f, 158.922f, 465.736f, 161.133f, 469.556f

+path.lineTo(SkBits2Float(0x431dfa1d), SkBits2Float(0x43ebb000));  // 157.977f, 471.375f

+path.close();
+testSimplify(reporter, path, filename);
+}

+

+static void joel_8(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.moveTo(SkBits2Float(0x42d97520), SkBits2Float(0x410ac429)); // 108.729f, 8.67289f 

+path.cubicTo(SkBits2Float(0x42d97520), SkBits2Float(0x410ac429), SkBits2Float(0x42e9a9ce), SkBits2Float(0x41834e87), SkBits2Float(0x42e99c8c), SkBits2Float(0x41c5c960)); // 108.729f, 8.67289f, 116.832f, 16.4133f, 116.806f, 24.7233f 

+path.cubicTo(SkBits2Float(0x42e98f49), SkBits2Float(0x4204221c), SkBits2Float(0x42d97520), SkBits2Float(0x4223825f), SkBits2Float(0x42d97520), SkBits2Float(0x4223825f)); // 116.78f, 33.0333f, 108.729f, 40.8773f, 108.729f, 40.8773f 

+path.cubicTo(SkBits2Float(0x42d97520), SkBits2Float(0x4223825f), SkBits2Float(0x42dbbc54), SkBits2Float(0x42099f18), SkBits2Float(0x42d1cb74), SkBits2Float(0x41f77dc0)); // 108.729f, 40.8773f, 109.868f, 34.4054f, 104.897f, 30.9364f 

+path.cubicTo(SkBits2Float(0x42c7da94), SkBits2Float(0x41dbbd4f), SkBits2Float(0x42b1b1a1), SkBits2Float(0x41d802fb), SkBits2Float(0x42b1b1a1), SkBits2Float(0x41d802fb)); // 99.9269f, 27.4674f, 88.8469f, 27.0015f, 88.8469f, 27.0015f 

+path.cubicTo(SkBits2Float(0x42a75637), SkBits2Float(0x41d6909f), SkBits2Float(0x4296c543), SkBits2Float(0x41f1b139), SkBits2Float(0x4296c543), SkBits2Float(0x41f1b139)); // 83.6684f, 26.8206f, 75.3853f, 30.2115f, 75.3853f, 30.2115f 

+path.lineTo(SkBits2Float(0x42824475), SkBits2Float(0x41c69d70)); // 65.1337f, 24.8269f 

+path.lineTo(SkBits2Float(0x4296c543), SkBits2Float(0x419b89a8)); // 75.3853f, 19.4422f 

+path.cubicTo(SkBits2Float(0x4296c543), SkBits2Float(0x419b89a8), SkBits2Float(0x42a6b798), SkBits2Float(0x41b89815), SkBits2Float(0x42b1b1a1), SkBits2Float(0x41b95c48)); // 75.3853f, 19.4422f, 83.3586f, 23.0743f, 88.8469f, 23.1701f 

+path.cubicTo(SkBits2Float(0x42b1b1a1), SkBits2Float(0x41b95c48), SkBits2Float(0x42c80258), SkBits2Float(0x41b03f7a), SkBits2Float(0x42d1cb74), SkBits2Float(0x419340ee)); // 88.8469f, 23.1701f, 100.005f, 22.031f, 104.897f, 18.4067f 

+path.cubicTo(SkBits2Float(0x42db9490), SkBits2Float(0x416c84c2), SkBits2Float(0x42d97520), SkBits2Float(0x410ac42a), SkBits2Float(0x42d97520), SkBits2Float(0x410ac42a)); // 109.79f, 14.7824f, 108.729f, 8.67289f, 108.729f, 8.67289f 

+path.lineTo(SkBits2Float(0x42d97520), SkBits2Float(0x410ac429)); // 108.729f, 8.67289f 

+path.close(); 

+testSimplify(reporter, path, filename);
+}

+
+static void joel_9(skiatest::Reporter* reporter, const char* filename) {
+#if DEBUG_UNDER_DEVELOPMENT
+// fails with image mismatch
+    SkPath path;
+path.moveTo(SkBits2Float(0x4310dbe7), SkBits2Float(0x438e9604));  // 144.859f, 285.172f

+path.lineTo(SkBits2Float(0x4310dbe7), SkBits2Float(0x438e9604));  // 144.859f, 285.172f

+path.lineTo(SkBits2Float(0x4310dbe7), SkBits2Float(0x438e9604));  // 144.859f, 285.172f

+path.lineTo(SkBits2Float(0x430f21ca), SkBits2Float(0x438e4efa));  // 143.132f, 284.617f

+path.lineTo(SkBits2Float(0x4310dbe7), SkBits2Float(0x438e9604));  // 144.859f, 285.172f

+path.close();

+path.moveTo(SkBits2Float(0x4307ec08), SkBits2Float(0x438f6c08));  // 135.922f, 286.844f

+path.lineTo(SkBits2Float(0x4307ec08), SkBits2Float(0x438f6c08));  // 135.922f, 286.844f

+path.lineTo(SkBits2Float(0x4307ec08), SkBits2Float(0x438f6c08));  // 135.922f, 286.844f

+path.lineTo(SkBits2Float(0x43075df4), SkBits2Float(0x43904916));  // 135.367f, 288.571f

+path.lineTo(SkBits2Float(0x4307ec08), SkBits2Float(0x438f6c08));  // 135.922f, 286.844f

+path.close();

+path.moveTo(SkBits2Float(0x4307ec08), SkBits2Float(0x438f6c08));  // 135.922f, 286.844f

+path.cubicTo(SkBits2Float(0x430911eb), SkBits2Float(0x438f9c08), SkBits2Float(0x430a3df4), SkBits2Float(0x438f8b02), SkBits2Float(0x430b3df4), SkBits2Float(0x438f49fc));  // 137.07f, 287.219f, 138.242f, 287.086f, 139.242f, 286.578f

+path.cubicTo(SkBits2Float(0x430c3be8), SkBits2Float(0x438f09fc), SkBits2Float(0x430d07f0), SkBits2Float(0x438e99fc), SkBits2Float(0x430d67f0), SkBits2Float(0x438e070a));  // 140.234f, 286.078f, 141.031f, 285.203f, 141.406f, 284.055f

+path.lineTo(SkBits2Float(0x4310dbe8), SkBits2Float(0x438e9604));  // 144.859f, 285.172f

+path.cubicTo(SkBits2Float(0x430f7df4), SkBits2Float(0x4390b000), SkBits2Float(0x430afdf4), SkBits2Float(0x4391d3f8), SkBits2Float(0x4306cc09), SkBits2Float(0x43912604));  // 143.492f, 289.375f, 138.992f, 291.656f, 134.797f, 290.297f

+path.lineTo(SkBits2Float(0x4307ec08), SkBits2Float(0x438f6c08));  // 135.922f, 286.844f

+path.close();

+path.moveTo(SkBits2Float(0x4301ae14), SkBits2Float(0x438c1efa));  // 129.68f, 280.242f

+path.lineTo(SkBits2Float(0x4301ae14), SkBits2Float(0x438c1efa));  // 129.68f, 280.242f

+path.lineTo(SkBits2Float(0x4301ae14), SkBits2Float(0x438c1efa));  // 129.68f, 280.242f

+path.lineTo(SkBits2Float(0x43036831), SkBits2Float(0x438c66ea));  // 131.407f, 280.804f

+path.lineTo(SkBits2Float(0x4301ae14), SkBits2Float(0x438c1efa));  // 129.68f, 280.242f

+path.close();

+path.moveTo(SkBits2Float(0x4305220c), SkBits2Float(0x438caefa));  // 133.133f, 281.367f

+path.cubicTo(SkBits2Float(0x4304c20c), SkBits2Float(0x438d4106), SkBits2Float(0x4304e20c), SkBits2Float(0x438dd7f0), SkBits2Float(0x43056418), SkBits2Float(0x438e56ea));  // 132.758f, 282.508f, 132.883f, 283.687f, 133.391f, 284.679f

+path.cubicTo(SkBits2Float(0x4305e831), SkBits2Float(0x438ed6ea), SkBits2Float(0x4306c624), SkBits2Float(0x438f3be8), SkBits2Float(0x4307ec08), SkBits2Float(0x438f6be8));  // 133.907f, 285.679f, 134.774f, 286.468f, 135.922f, 286.843f

+path.lineTo(SkBits2Float(0x4306cc08), SkBits2Float(0x439125e4));  // 134.797f, 290.296f

+path.cubicTo(SkBits2Float(0x43029a1c), SkBits2Float(0x439076ea), SkBits2Float(0x43005021), SkBits2Float(0x438e37f0), SkBits2Float(0x4301ae14), SkBits2Float(0x438c1eda));  // 130.602f, 288.929f, 128.313f, 284.437f, 129.68f, 280.241f

+path.lineTo(SkBits2Float(0x4305220c), SkBits2Float(0x438caefa));  // 133.133f, 281.367f

+path.close();

+path.moveTo(SkBits2Float(0x430bbdf4), SkBits2Float(0x43898efa));  // 139.742f, 275.117f

+path.lineTo(SkBits2Float(0x430bbdf4), SkBits2Float(0x43898efa));  // 139.742f, 275.117f

+path.lineTo(SkBits2Float(0x430b2e15), SkBits2Float(0x438a6c08));  // 139.18f, 276.844f

+path.lineTo(SkBits2Float(0x430bbdf4), SkBits2Float(0x43898efa));  // 139.742f, 275.117f

+path.close();

+path.moveTo(SkBits2Float(0x430a9be7), SkBits2Float(0x438b48f6));  // 138.609f, 278.57f

+path.cubicTo(SkBits2Float(0x43097604), SkBits2Float(0x438b19fc), SkBits2Float(0x43084c08), SkBits2Float(0x438b29fc), SkBits2Float(0x43074c08), SkBits2Float(0x438b6b02));  // 137.461f, 278.203f, 136.297f, 278.328f, 135.297f, 278.836f

+path.cubicTo(SkBits2Float(0x43064c08), SkBits2Float(0x438bac08), SkBits2Float(0x4305820c), SkBits2Float(0x438c1c08), SkBits2Float(0x4305220c), SkBits2Float(0x438caefa));  // 134.297f, 279.344f, 133.508f, 280.219f, 133.133f, 281.367f

+path.lineTo(SkBits2Float(0x4301ae14), SkBits2Float(0x438c1efa));  // 129.68f, 280.242f

+path.cubicTo(SkBits2Float(0x43030c08), SkBits2Float(0x438a04fe), SkBits2Float(0x430789fb), SkBits2Float(0x4388e106), SkBits2Float(0x430bbdf3), SkBits2Float(0x43898efa));  // 131.047f, 276.039f, 135.539f, 273.758f, 139.742f, 275.117f

+path.lineTo(SkBits2Float(0x430a9be7), SkBits2Float(0x438b48f6));  // 138.609f, 278.57f

+path.close();

+path.moveTo(SkBits2Float(0x430d67f0), SkBits2Float(0x438e070a));  // 141.406f, 284.055f

+path.cubicTo(SkBits2Float(0x430dc5e4), SkBits2Float(0x438d7418), SkBits2Float(0x430da5e4), SkBits2Float(0x438cde14), SkBits2Float(0x430d25e4), SkBits2Float(0x438c5e14));  // 141.773f, 282.907f, 141.648f, 281.735f, 141.148f, 280.735f

+path.cubicTo(SkBits2Float(0x430ca001), SkBits2Float(0x438bde14), SkBits2Float(0x430bc1cb), SkBits2Float(0x438b7916), SkBits2Float(0x430a9be8), SkBits2Float(0x438b4916));  // 140.625f, 279.735f, 139.757f, 278.946f, 138.609f, 278.571f

+path.lineTo(SkBits2Float(0x430bbdf4), SkBits2Float(0x43898f1a));  // 139.742f, 275.118f

+path.cubicTo(SkBits2Float(0x430fefe0), SkBits2Float(0x438a3f1a), SkBits2Float(0x43123811), SkBits2Float(0x438c7d0e), SkBits2Float(0x4310dbe8), SkBits2Float(0x438e9624));  // 143.937f, 276.493f, 146.219f, 280.977f, 144.859f, 285.173f

+path.lineTo(SkBits2Float(0x430d67f0), SkBits2Float(0x438e070a));  // 141.406f, 284.055f

+path.close();
+testSimplify(reporter, path, filename);
+#endif
+}

+
+static void joel_10(skiatest::Reporter* reporter, const char* filename) {
+#if DEBUG_UNDER_DEVELOPMENT
+// fails with image mismatch
+    SkPath path;
+path.moveTo(SkBits2Float(0x440fc979), SkBits2Float(0x43d88000));  // 575.148f, 433

+path.lineTo(SkBits2Float(0x440fc979), SkBits2Float(0x43d88000));  // 575.148f, 433

+path.lineTo(SkBits2Float(0x440fc979), SkBits2Float(0x43d88000));  // 575.148f, 433

+path.lineTo(SkBits2Float(0x44103800), SkBits2Float(0x43d8c7f0));  // 576.875f, 433.562f

+path.lineTo(SkBits2Float(0x440fc979), SkBits2Float(0x43d88000));  // 575.148f, 433

+path.close();

+path.moveTo(SkBits2Float(0x44124d81), SkBits2Float(0x43d5f000));  // 585.211f, 427.875f

+path.lineTo(SkBits2Float(0x44124d81), SkBits2Float(0x43d5f000));  // 585.211f, 427.875f

+path.lineTo(SkBits2Float(0x44122906), SkBits2Float(0x43d6cdf4));  // 584.641f, 429.609f

+path.lineTo(SkBits2Float(0x44124d81), SkBits2Float(0x43d5f000));  // 585.211f, 427.875f

+path.close();

+path.moveTo(SkBits2Float(0x44120581), SkBits2Float(0x43d7ab02));  // 584.086f, 431.336f

+path.cubicTo(SkBits2Float(0x4411bc08), SkBits2Float(0x43d77b02), SkBits2Float(0x44117083), SkBits2Float(0x43d78b02), SkBits2Float(0x44113106), SkBits2Float(0x43d7cc08));  // 582.938f, 430.961f, 581.758f, 431.086f, 580.766f, 431.594f

+path.cubicTo(SkBits2Float(0x4410f189), SkBits2Float(0x43d80d0e), SkBits2Float(0x4410be87), SkBits2Float(0x43d87d0e), SkBits2Float(0x4410a687), SkBits2Float(0x43d91000));  // 579.774f, 432.102f, 578.977f, 432.977f, 578.602f, 434.125f

+path.lineTo(SkBits2Float(0x440fc979), SkBits2Float(0x43d88000));  // 575.148f, 433

+path.cubicTo(SkBits2Float(0x441020f6), SkBits2Float(0x43d66604), SkBits2Float(0x441140f6), SkBits2Float(0x43d5420c), SkBits2Float(0x44124d71), SkBits2Float(0x43d5f000));  // 576.515f, 428.797f, 581.015f, 426.516f, 585.21f, 427.875f

+path.lineTo(SkBits2Float(0x44120581), SkBits2Float(0x43d7ab02));  // 584.086f, 431.336f

+path.close();

+path.moveTo(SkBits2Float(0x441394fe), SkBits2Float(0x43daf810));  // 590.328f, 437.938f

+path.lineTo(SkBits2Float(0x441394fe), SkBits2Float(0x43daf810));  // 590.328f, 437.938f

+path.lineTo(SkBits2Float(0x441394fe), SkBits2Float(0x43daf810));  // 590.328f, 437.938f

+path.lineTo(SkBits2Float(0x44132677), SkBits2Float(0x43dab020));  // 588.601f, 437.376f

+path.lineTo(SkBits2Float(0x441394fe), SkBits2Float(0x43daf810));  // 590.328f, 437.938f

+path.close();

+path.moveTo(SkBits2Float(0x4412b800), SkBits2Float(0x43da67f0));  // 586.875f, 436.812f

+path.cubicTo(SkBits2Float(0x4412d000), SkBits2Float(0x43d9d4fe), SkBits2Float(0x4412c800), SkBits2Float(0x43d94000), SkBits2Float(0x4412a77d), SkBits2Float(0x43d8befa));  // 587.25f, 435.664f, 587.125f, 434.5f, 586.617f, 433.492f

+path.cubicTo(SkBits2Float(0x44128677), SkBits2Float(0x43d84000), SkBits2Float(0x44124efa), SkBits2Float(0x43d7d9fc), SkBits2Float(0x44120581), SkBits2Float(0x43d7ab02));  // 586.101f, 432.5f, 585.234f, 431.703f, 584.086f, 431.336f

+path.lineTo(SkBits2Float(0x44124d81), SkBits2Float(0x43d5f000));  // 585.211f, 427.875f

+path.cubicTo(SkBits2Float(0x441359fc), SkBits2Float(0x43d69efa), SkBits2Float(0x4413ec7b), SkBits2Float(0x43d8ddf4), SkBits2Float(0x441394fe), SkBits2Float(0x43daf7f0));  // 589.406f, 429.242f, 591.695f, 433.734f, 590.328f, 437.937f

+path.lineTo(SkBits2Float(0x4412b800), SkBits2Float(0x43da67f0));  // 586.875f, 436.812f

+path.close();

+path.moveTo(SkBits2Float(0x44111106), SkBits2Float(0x43dd870a));  // 580.266f, 443.055f

+path.lineTo(SkBits2Float(0x44111106), SkBits2Float(0x43dd870a));  // 580.266f, 443.055f

+path.lineTo(SkBits2Float(0x44111106), SkBits2Float(0x43dd870a));  // 580.266f, 443.055f

+path.lineTo(SkBits2Float(0x441134fe), SkBits2Float(0x43dca9fc));  // 580.828f, 441.328f

+path.lineTo(SkBits2Float(0x44111106), SkBits2Float(0x43dd870a));  // 580.266f, 443.055f

+path.close();

+path.moveTo(SkBits2Float(0x44115979), SkBits2Float(0x43dbcd0e));  // 581.398f, 439.602f

+path.cubicTo(SkBits2Float(0x4411a27f), SkBits2Float(0x43dbfc08), SkBits2Float(0x4411ed71), SkBits2Float(0x43dbed0e), SkBits2Float(0x44122d71), SkBits2Float(0x43dbac08));  // 582.539f, 439.969f, 583.71f, 439.852f, 584.71f, 439.344f

+path.cubicTo(SkBits2Float(0x44126cee), SkBits2Float(0x43db6b02), SkBits2Float(0x44129ff0), SkBits2Float(0x43dafb02), SkBits2Float(0x4412b7f0), SkBits2Float(0x43da6810));  // 585.702f, 438.836f, 586.499f, 437.961f, 586.874f, 436.813f

+path.lineTo(SkBits2Float(0x441394ee), SkBits2Float(0x43daf810));  // 590.327f, 437.938f

+path.cubicTo(SkBits2Float(0x44133cee), SkBits2Float(0x43dd1106), SkBits2Float(0x44121df4), SkBits2Float(0x43de3604), SkBits2Float(0x441110f6), SkBits2Float(0x43dd870a));  // 588.952f, 442.133f, 584.468f, 444.422f, 580.265f, 443.055f

+path.lineTo(SkBits2Float(0x44115979), SkBits2Float(0x43dbcd0e));  // 581.398f, 439.602f

+path.close();

+path.moveTo(SkBits2Float(0x4410a687), SkBits2Float(0x43d91000));  // 578.602f, 434.125f

+path.cubicTo(SkBits2Float(0x44108f0a), SkBits2Float(0x43d9a2f2), SkBits2Float(0x44109687), SkBits2Float(0x43da37f0), SkBits2Float(0x4410b70a), SkBits2Float(0x43dab8f6));  // 578.235f, 435.273f, 578.352f, 436.437f, 578.86f, 437.445f

+path.cubicTo(SkBits2Float(0x4410d78d), SkBits2Float(0x43db37f0), SkBits2Float(0x44111010), SkBits2Float(0x43db9cee), SkBits2Float(0x44115989), SkBits2Float(0x43dbccee));  // 579.368f, 438.437f, 580.251f, 439.226f, 581.399f, 439.601f

+path.lineTo(SkBits2Float(0x44111106), SkBits2Float(0x43dd86ea));  // 580.266f, 443.054f

+path.cubicTo(SkBits2Float(0x4410048b), SkBits2Float(0x43dcd7f0), SkBits2Float(0x440f720c), SkBits2Float(0x43da99dc), SkBits2Float(0x440fc989), SkBits2Float(0x43d87fe0));  // 576.071f, 441.687f, 573.782f, 437.202f, 575.149f, 432.999f

+path.lineTo(SkBits2Float(0x4410a687), SkBits2Float(0x43d91000));  // 578.602f, 434.125f

+path.close();
+testSimplify(reporter, path, filename);
+#endif
+}

+
+static void joel_11(skiatest::Reporter* reporter, const char* filename) {
+#if DEBUG_UNDER_DEVELOPMENT
+// fails with image mismatch
+    SkPath path;
+path.moveTo(SkBits2Float(0x43c9d000), SkBits2Float(0x4411977d));  // 403.625f, 582.367f

+path.lineTo(SkBits2Float(0x43c9d000), SkBits2Float(0x4411977d));  // 403.625f, 582.367f

+path.lineTo(SkBits2Float(0x43c9d000), SkBits2Float(0x4411977d));  // 403.625f, 582.367f

+path.lineTo(SkBits2Float(0x43ca0106), SkBits2Float(0x441208f6));  // 404.008f, 584.14f

+path.lineTo(SkBits2Float(0x43c9d000), SkBits2Float(0x4411977d));  // 403.625f, 582.367f

+path.close();

+path.moveTo(SkBits2Float(0x43ce8d0e), SkBits2Float(0x44132106));  // 413.102f, 588.516f

+path.lineTo(SkBits2Float(0x43ce8d0e), SkBits2Float(0x44132106));  // 413.102f, 588.516f

+path.lineTo(SkBits2Float(0x43ce8d0e), SkBits2Float(0x44132106));  // 413.102f, 588.516f

+path.lineTo(SkBits2Float(0x43cda916), SkBits2Float(0x44133989));  // 411.321f, 588.899f

+path.lineTo(SkBits2Float(0x43ce8d0e), SkBits2Float(0x44132106));  // 413.102f, 588.516f

+path.close();

+path.moveTo(SkBits2Float(0x43ccc4fe), SkBits2Float(0x44135179));  // 409.539f, 589.273f

+path.cubicTo(SkBits2Float(0x43cca604), SkBits2Float(0x44130571), SkBits2Float(0x43cc4c08), SkBits2Float(0x4412c8f6), SkBits2Float(0x43cbd4fe), SkBits2Float(0x4412a179));  // 409.297f, 588.085f, 408.594f, 587.14f, 407.664f, 586.523f

+path.cubicTo(SkBits2Float(0x43cb5c08), SkBits2Float(0x44127a7f), SkBits2Float(0x43cac7f0), SkBits2Float(0x44126af2), SkBits2Float(0x43ca3106), SkBits2Float(0x44127af2));  // 406.719f, 585.914f, 405.562f, 585.671f, 404.383f, 585.921f

+path.lineTo(SkBits2Float(0x43c9d000), SkBits2Float(0x4411976d));  // 403.625f, 582.366f

+path.cubicTo(SkBits2Float(0x43cbf9fc), SkBits2Float(0x44115cee), SkBits2Float(0x43ce170a), SkBits2Float(0x44120cee), SkBits2Float(0x43ce8d0e), SkBits2Float(0x441320e6));  // 407.953f, 581.452f, 412.18f, 584.202f, 413.102f, 588.514f

+path.lineTo(SkBits2Float(0x43ccc4fe), SkBits2Float(0x44135179));  // 409.539f, 589.273f

+path.close();

+path.moveTo(SkBits2Float(0x43cb78f6), SkBits2Float(0x44157efa));  // 406.945f, 597.984f

+path.lineTo(SkBits2Float(0x43cb78f6), SkBits2Float(0x44157f7d));  // 406.945f, 597.992f

+path.lineTo(SkBits2Float(0x43cb78f6), SkBits2Float(0x44157efa));  // 406.945f, 597.984f

+path.lineTo(SkBits2Float(0x43cb49fc), SkBits2Float(0x44150d81));  // 406.578f, 596.211f

+path.lineTo(SkBits2Float(0x43cb78f6), SkBits2Float(0x44157efa));  // 406.945f, 597.984f

+path.close();

+path.moveTo(SkBits2Float(0x43cb18f6), SkBits2Float(0x44149b85));  // 406.195f, 594.43f

+path.cubicTo(SkBits2Float(0x43cbb000), SkBits2Float(0x44148b85), SkBits2Float(0x43cc28f6), SkBits2Float(0x44145f0a), SkBits2Float(0x43cc76ea), SkBits2Float(0x44142302));  // 407.375f, 594.18f, 408.32f, 593.485f, 408.929f, 592.547f

+path.cubicTo(SkBits2Float(0x43ccc4de), SkBits2Float(0x4413e687), SkBits2Float(0x43cce4de), SkBits2Float(0x44139cfe), SkBits2Float(0x43ccc4de), SkBits2Float(0x44135189));  // 409.538f, 591.602f, 409.788f, 590.453f, 409.538f, 589.274f

+path.lineTo(SkBits2Float(0x43ce8cce), SkBits2Float(0x44132106));  // 413.1f, 588.516f

+path.cubicTo(SkBits2Float(0x43cf00c6), SkBits2Float(0x44143581), SkBits2Float(0x43cda2d2), SkBits2Float(0x44154408), SkBits2Float(0x43cb78d6), SkBits2Float(0x44157f0a));  // 414.006f, 592.836f, 411.272f, 597.063f, 406.944f, 597.985f

+path.lineTo(SkBits2Float(0x43cb18f6), SkBits2Float(0x44149b85));  // 406.195f, 594.43f

+path.close();

+path.moveTo(SkBits2Float(0x43c883f8), SkBits2Float(0x4413c4fe));  // 401.031f, 591.078f

+path.lineTo(SkBits2Float(0x43c883f8), SkBits2Float(0x4413c4fe));  // 401.031f, 591.078f

+path.lineTo(SkBits2Float(0x43c883f8), SkBits2Float(0x4413c4fe));  // 401.031f, 591.078f

+path.lineTo(SkBits2Float(0x43c7a106), SkBits2Float(0x4413dd81));  // 399.258f, 591.461f

+path.lineTo(SkBits2Float(0x43c883f8), SkBits2Float(0x4413c4fe));  // 401.031f, 591.078f

+path.close();

+path.moveTo(SkBits2Float(0x43c883f8), SkBits2Float(0x4413c4fe));  // 401.031f, 591.078f

+path.cubicTo(SkBits2Float(0x43c8a4fe), SkBits2Float(0x44141083), SkBits2Float(0x43c8fdf4), SkBits2Float(0x44144d81), SkBits2Float(0x43c974fe), SkBits2Float(0x4414747b));  // 401.289f, 592.258f, 401.984f, 593.211f, 402.914f, 593.82f

+path.cubicTo(SkBits2Float(0x43c9edf4), SkBits2Float(0x44149b75), SkBits2Float(0x43ca820c), SkBits2Float(0x4414ab75), SkBits2Float(0x43cb18f6), SkBits2Float(0x44149b75));  // 403.859f, 594.429f, 405.016f, 594.679f, 406.195f, 594.429f

+path.lineTo(SkBits2Float(0x43cb78f6), SkBits2Float(0x44157efa));  // 406.945f, 597.984f

+path.cubicTo(SkBits2Float(0x43c95000), SkBits2Float(0x4415b979), SkBits2Float(0x43c732f2), SkBits2Float(0x441509fc), SkBits2Float(0x43c6bcee), SkBits2Float(0x4413f581));  // 402.625f, 598.898f, 398.398f, 596.156f, 397.476f, 591.836f

+path.lineTo(SkBits2Float(0x43c883f8), SkBits2Float(0x4413c4fe));  // 401.031f, 591.078f

+path.close();

+path.moveTo(SkBits2Float(0x43ca3106), SkBits2Float(0x44127b02));  // 404.383f, 585.922f

+path.cubicTo(SkBits2Float(0x43c999fc), SkBits2Float(0x44128b02), SkBits2Float(0x43c92000), SkBits2Float(0x4412b77d), SkBits2Float(0x43c8d20c), SkBits2Float(0x4412f408));  // 403.203f, 586.172f, 402.25f, 586.867f, 401.641f, 587.813f

+path.cubicTo(SkBits2Float(0x43c88418), SkBits2Float(0x44133010), SkBits2Float(0x43c86418), SkBits2Float(0x44137989), SkBits2Float(0x43c88418), SkBits2Float(0x4413c50e));  // 401.032f, 588.751f, 400.782f, 589.899f, 401.032f, 591.079f

+path.lineTo(SkBits2Float(0x43c6bd0e), SkBits2Float(0x4413f591));  // 397.477f, 591.837f

+path.cubicTo(SkBits2Float(0x43c64810), SkBits2Float(0x4412e116), SkBits2Float(0x43c7a70a), SkBits2Float(0x4411d28f), SkBits2Float(0x43c9d000), SkBits2Float(0x4411978d));  // 396.563f, 587.517f, 399.305f, 583.29f, 403.625f, 582.368f

+path.lineTo(SkBits2Float(0x43ca3106), SkBits2Float(0x44127b02));  // 404.383f, 585.922f

+path.close();
+testSimplify(reporter, path, filename);
+#endif
+}

+
+static void make_joel_12(SkPath& path) {
+path.moveTo(SkBits2Float(0x4324e9fc), SkBits2Float(0x437211ec));  // 164.914f, 242.07f

+path.lineTo(SkBits2Float(0x4324e9fc), SkBits2Float(0x437211ec));  // 164.914f, 242.07f

+path.lineTo(SkBits2Float(0x4324e9fc), SkBits2Float(0x437211ec));  // 164.914f, 242.07f

+path.lineTo(SkBits2Float(0x43235810), SkBits2Float(0x437129fc));  // 163.344f, 241.164f

+path.lineTo(SkBits2Float(0x4324e9fc), SkBits2Float(0x437211ec));  // 164.914f, 242.07f

+path.close();

+path.moveTo(SkBits2Float(0x431a020c), SkBits2Float(0x4374fdf4));  // 154.008f, 244.992f

+path.lineTo(SkBits2Float(0x431a020c), SkBits2Float(0x4374fdf4));  // 154.008f, 244.992f

+path.lineTo(SkBits2Float(0x431a020c), SkBits2Float(0x4374fdf4));  // 154.008f, 244.992f

+path.lineTo(SkBits2Float(0x431aec08), SkBits2Float(0x437369fc));  // 154.922f, 243.414f

+path.lineTo(SkBits2Float(0x431a020c), SkBits2Float(0x4374fdf4));  // 154.008f, 244.992f

+path.close();

+path.moveTo(SkBits2Float(0x431bd3f8), SkBits2Float(0x4371d810));  // 155.828f, 241.844f

+path.cubicTo(SkBits2Float(0x431ce000), SkBits2Float(0x4372722d), SkBits2Float(0x431e0e15), SkBits2Float(0x43729020), SkBits2Float(0x431f2000), SkBits2Float(0x43724831));  // 156.875f, 242.446f, 158.055f, 242.563f, 159.125f, 242.282f

+path.cubicTo(SkBits2Float(0x43203604), SkBits2Float(0x4371fe35), SkBits2Float(0x43212c08), SkBits2Float(0x43714a3d), SkBits2Float(0x4321c5e3), SkBits2Float(0x43704041));  // 160.211f, 241.993f, 161.172f, 241.29f, 161.773f, 240.251f

+path.lineTo(SkBits2Float(0x4324e9fc), SkBits2Float(0x4372122d));  // 164.914f, 242.071f

+path.cubicTo(SkBits2Float(0x4322b3f8), SkBits2Float(0x4375e419), SkBits2Float(0x431dd810), SkBits2Float(0x4377322d), SkBits2Float(0x431a020c), SkBits2Float(0x4374fe35));  // 162.703f, 245.891f, 157.844f, 247.196f, 154.008f, 244.993f

+path.lineTo(SkBits2Float(0x431bd3f8), SkBits2Float(0x4371d810));  // 155.828f, 241.844f

+path.close();

+path.moveTo(SkBits2Float(0x43171810), SkBits2Float(0x436a1604));  // 151.094f, 234.086f

+path.lineTo(SkBits2Float(0x43171810), SkBits2Float(0x436a1604));  // 151.094f, 234.086f

+path.lineTo(SkBits2Float(0x43171810), SkBits2Float(0x436a1604));  // 151.094f, 234.086f

+path.lineTo(SkBits2Float(0x4318a9fc), SkBits2Float(0x436afdf4));  // 152.664f, 234.992f

+path.lineTo(SkBits2Float(0x43171810), SkBits2Float(0x436a1604));  // 151.094f, 234.086f

+path.close();

+path.moveTo(SkBits2Float(0x431a4000), SkBits2Float(0x436be7f0));  // 154.25f, 235.906f

+path.cubicTo(SkBits2Float(0x4319a20c), SkBits2Float(0x436cf3f8), SkBits2Float(0x431985e3), SkBits2Float(0x436e1df4), SkBits2Float(0x4319ce14), SkBits2Float(0x436f33f8));  // 153.633f, 236.953f, 153.523f, 238.117f, 153.805f, 239.203f

+path.cubicTo(SkBits2Float(0x431a1a1c), SkBits2Float(0x437047f0), SkBits2Float(0x431ac831), SkBits2Float(0x43713df4), SkBits2Float(0x431bd3f7), SkBits2Float(0x4371d811));  // 154.102f, 240.281f, 154.782f, 241.242f, 155.828f, 241.844f

+path.lineTo(SkBits2Float(0x431a020b), SkBits2Float(0x4374fdf4));  // 154.008f, 244.992f

+path.cubicTo(SkBits2Float(0x4316322c), SkBits2Float(0x4372c5e4), SkBits2Float(0x4314e417), SkBits2Float(0x436de9fc), SkBits2Float(0x4317180f), SkBits2Float(0x436a1604));  // 150.196f, 242.773f, 148.891f, 237.914f, 151.094f, 234.086f

+path.lineTo(SkBits2Float(0x431a4000), SkBits2Float(0x436be7f0));  // 154.25f, 235.906f

+path.close();

+path.moveTo(SkBits2Float(0x43220000), SkBits2Float(0x436729fc));  // 162, 231.164f

+path.lineTo(SkBits2Float(0x43220000), SkBits2Float(0x436729fc));  // 162, 231.164f

+path.lineTo(SkBits2Float(0x43220000), SkBits2Float(0x436729fc));  // 162, 231.164f

+path.lineTo(SkBits2Float(0x43211810), SkBits2Float(0x4368bbe8));  // 161.094f, 232.734f

+path.lineTo(SkBits2Float(0x43220000), SkBits2Float(0x436729fc));  // 162, 231.164f

+path.close();

+path.moveTo(SkBits2Float(0x43202e14), SkBits2Float(0x436a4fdf));  // 160.18f, 234.312f

+path.cubicTo(SkBits2Float(0x431f2418), SkBits2Float(0x4369b5c2), SkBits2Float(0x431df810), SkBits2Float(0x436995c2), SkBits2Float(0x431ce20c), SkBits2Float(0x4369dfbe));  // 159.141f, 233.71f, 157.969f, 233.585f, 156.883f, 233.874f

+path.cubicTo(SkBits2Float(0x431bcc08), SkBits2Float(0x436a2bc6), SkBits2Float(0x431ad810), SkBits2Float(0x436adba5), SkBits2Float(0x431a4000), SkBits2Float(0x436be7ae));  // 155.797f, 234.171f, 154.844f, 234.858f, 154.25f, 235.905f

+path.lineTo(SkBits2Float(0x43171810), SkBits2Float(0x436a15c2));  // 151.094f, 234.085f

+path.cubicTo(SkBits2Float(0x43194e14), SkBits2Float(0x436643d6), SkBits2Float(0x431e2c08), SkBits2Float(0x4364f3b6), SkBits2Float(0x43220000), SkBits2Float(0x436729ba));  // 153.305f, 230.265f, 158.172f, 228.952f, 162, 231.163f

+path.lineTo(SkBits2Float(0x43202e14), SkBits2Float(0x436a4fdf));  // 160.18f, 234.312f

+path.close();

+path.moveTo(SkBits2Float(0x4321c5e3), SkBits2Float(0x43704000));  // 161.773f, 240.25f

+path.cubicTo(SkBits2Float(0x43226000), SkBits2Float(0x436f3604), SkBits2Float(0x43228000), SkBits2Float(0x436e09fc), SkBits2Float(0x43223604), SkBits2Float(0x436cf3f8));  // 162.375f, 239.211f, 162.5f, 238.039f, 162.211f, 236.953f

+path.cubicTo(SkBits2Float(0x4321ec08), SkBits2Float(0x436be000), SkBits2Float(0x43213a1d), SkBits2Float(0x436ae9fc), SkBits2Float(0x43202e14), SkBits2Float(0x436a4fdf));  // 161.922f, 235.875f, 161.227f, 234.914f, 160.18f, 234.312f

+path.lineTo(SkBits2Float(0x43220000), SkBits2Float(0x436729fc));  // 162, 231.164f

+path.cubicTo(SkBits2Float(0x4325d1ec), SkBits2Float(0x43696000), SkBits2Float(0x4327220c), SkBits2Float(0x436e4000), SkBits2Float(0x4324e9fc), SkBits2Float(0x437211ec));  // 165.82f, 233.375f, 167.133f, 238.25f, 164.914f, 242.07f

+path.lineTo(SkBits2Float(0x4321c5e3), SkBits2Float(0x43704000));  // 161.773f, 240.25f

+path.close();
+}
+
+static void joel_12(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    make_joel_12(path);
+    testSimplify(reporter, path, filename);
+}

+
+static void joel_12x(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    make_joel_12(path);
+    testSimplify(reporter, path, filename);
+}
+
+static void make_joel_13(SkPath& path) {
+path.moveTo(SkBits2Float(0x43b4126f), SkBits2Float(0x43c058f6));  // 360.144f, 384.695f

+path.cubicTo(SkBits2Float(0x43bd7c6b), SkBits2Float(0x43c05b02), SkBits2Float(0x43c51d71), SkBits2Float(0x43b8e8f6), SkBits2Float(0x43c5276d), SkBits2Float(0x43afc1ec));  // 378.972f, 384.711f, 394.23f, 369.82f, 394.308f, 351.515f

+path.cubicTo(SkBits2Float(0x43c51d71), SkBits2Float(0x43a688f6), SkBits2Float(0x43bd7c6b), SkBits2Float(0x439f16ea), SkBits2Float(0x43b4126f), SkBits2Float(0x439f16ea));  // 394.23f, 333.07f, 378.972f, 318.179f, 360.144f, 318.179f

+path.cubicTo(SkBits2Float(0x43aaa979), SkBits2Float(0x439f16ea), SkBits2Float(0x43a3076d), SkBits2Float(0x43a688f6), SkBits2Float(0x43a31063), SkBits2Float(0x43afc1ec));  // 341.324f, 318.179f, 326.058f, 333.07f, 326.128f, 351.515f

+path.cubicTo(SkBits2Float(0x43a3076d), SkBits2Float(0x43b8e8f6), SkBits2Float(0x43aaa959), SkBits2Float(0x43c05b02), SkBits2Float(0x43b4126f), SkBits2Float(0x43c058f6));  // 326.058f, 369.82f, 341.323f, 384.711f, 360.144f, 384.695f

+path.close();
+}
+
+static void joel_13(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    make_joel_13(path);
+    testSimplify(reporter, path, filename);
+}

+
+
+static void joel_13x(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    make_joel_13(path);
+    testSimplify(reporter, path, filename);
+}
+
+static void make_joel_14(SkPath& path) {
+path.moveTo(SkBits2Float(0x43f3b354), SkBits2Float(0x43d6770a));  // 487.401f, 428.93f

+path.lineTo(SkBits2Float(0x43f3b354), SkBits2Float(0x43d6770a));  // 487.401f, 428.93f

+path.close();

+path.moveTo(SkBits2Float(0x43f0fd50), SkBits2Float(0x43d6770a));  // 481.979f, 428.93f

+path.lineTo(SkBits2Float(0x43f0fd50), SkBits2Float(0x43d6770a));  // 481.979f, 428.93f

+path.close();

+path.moveTo(SkBits2Float(0x43f0fd50), SkBits2Float(0x43d6770a));  // 481.979f, 428.93f

+path.lineTo(SkBits2Float(0x43f3b354), SkBits2Float(0x43d6770a));  // 487.401f, 428.93f

+path.lineTo(SkBits2Float(0x43f0fd50), SkBits2Float(0x43d6770a));  // 481.979f, 428.93f

+path.close();

+path.moveTo(SkBits2Float(0x43dfe76d), SkBits2Float(0x43d792f1));  // 447.808f, 431.148f

+path.lineTo(SkBits2Float(0x43dfe76d), SkBits2Float(0x43d792f1));  // 447.808f, 431.148f

+path.close();

+path.moveTo(SkBits2Float(0x43dfe76d), SkBits2Float(0x43d792f1));  // 447.808f, 431.148f

+path.cubicTo(SkBits2Float(0x43e51979), SkBits2Float(0x43d611eb), SkBits2Float(0x43eb8667), SkBits2Float(0x43d765e3), SkBits2Float(0x43f0fd71), SkBits2Float(0x43d676e9));  // 458.199f, 428.14f, 471.05f, 430.796f, 481.98f, 428.929f

+path.cubicTo(SkBits2Float(0x43eb8667), SkBits2Float(0x43d76604), SkBits2Float(0x43e51958), SkBits2Float(0x43d6120c), SkBits2Float(0x43dfe76d), SkBits2Float(0x43d792f1));  // 471.05f, 430.797f, 458.198f, 428.141f, 447.808f, 431.148f

+path.close();

+path.moveTo(SkBits2Float(0x43df776d), SkBits2Float(0x43d6d603));  // 446.933f, 429.672f

+path.lineTo(SkBits2Float(0x43df776d), SkBits2Float(0x43d6d603));  // 446.933f, 429.672f

+path.close();

+path.moveTo(SkBits2Float(0x43df776d), SkBits2Float(0x43d6d603));  // 446.933f, 429.672f

+path.lineTo(SkBits2Float(0x43dfe76d), SkBits2Float(0x43d79311));  // 447.808f, 431.149f

+path.lineTo(SkBits2Float(0x43df776d), SkBits2Float(0x43d6d603));  // 446.933f, 429.672f

+path.close();

+path.moveTo(SkBits2Float(0x43dd3169), SkBits2Float(0x43d792f1));  // 442.386f, 431.148f

+path.lineTo(SkBits2Float(0x43dd3169), SkBits2Float(0x43d792f1));  // 442.386f, 431.148f

+path.close();

+path.moveTo(SkBits2Float(0x43dd3169), SkBits2Float(0x43d792f1));  // 442.386f, 431.148f

+path.cubicTo(SkBits2Float(0x43de376d), SkBits2Float(0x43d743f7), SkBits2Float(0x43de2873), SkBits2Float(0x43d68df3), SkBits2Float(0x43df776d), SkBits2Float(0x43d6d5e3));  // 444.433f, 430.531f, 444.316f, 429.109f, 446.933f, 429.671f

+path.cubicTo(SkBits2Float(0x43de2852), SkBits2Float(0x43d68df3), SkBits2Float(0x43de376d), SkBits2Float(0x43d743f7), SkBits2Float(0x43dd3169), SkBits2Float(0x43d792f1));  // 444.315f, 429.109f, 444.433f, 430.531f, 442.386f, 431.148f

+path.close();

+path.moveTo(SkBits2Float(0x43dcc169), SkBits2Float(0x43d6d603));  // 441.511f, 429.672f

+path.lineTo(SkBits2Float(0x43dcc169), SkBits2Float(0x43d6d603));  // 441.511f, 429.672f

+path.close();

+path.moveTo(SkBits2Float(0x43dcc169), SkBits2Float(0x43d6d603));  // 441.511f, 429.672f

+path.lineTo(SkBits2Float(0x43dd3169), SkBits2Float(0x43d79311));  // 442.386f, 431.149f

+path.lineTo(SkBits2Float(0x43dcc169), SkBits2Float(0x43d6d603));  // 441.511f, 429.672f

+path.close();

+path.moveTo(SkBits2Float(0x43dad959), SkBits2Float(0x43d6d603));  // 437.698f, 429.672f

+path.lineTo(SkBits2Float(0x43dad959), SkBits2Float(0x43d6d603));  // 437.698f, 429.672f

+path.close();

+path.moveTo(SkBits2Float(0x43dad959), SkBits2Float(0x43d6d603));  // 437.698f, 429.672f

+path.lineTo(SkBits2Float(0x43dcc149), SkBits2Float(0x43d6d603));  // 441.51f, 429.672f

+path.lineTo(SkBits2Float(0x43dad959), SkBits2Float(0x43d6d603));  // 437.698f, 429.672f

+path.close();

+path.moveTo(SkBits2Float(0x43e3cb65), SkBits2Float(0x43e3bd0d));  // 455.589f, 455.477f

+path.lineTo(SkBits2Float(0x43e3cb65), SkBits2Float(0x43e3bd0d));  // 455.589f, 455.477f

+path.close();

+path.moveTo(SkBits2Float(0x43e3cb65), SkBits2Float(0x43e3bd0d));  // 455.589f, 455.477f

+path.lineTo(SkBits2Float(0x43dad959), SkBits2Float(0x43d6d603));  // 437.698f, 429.672f

+path.lineTo(SkBits2Float(0x43e3cb65), SkBits2Float(0x43e3bd0d));  // 455.589f, 455.477f

+path.close();

+path.moveTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e41b01));  // 452.354f, 456.211f

+path.lineTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e41b01));  // 452.354f, 456.211f

+path.close();

+path.moveTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e41b01));  // 452.354f, 456.211f

+path.cubicTo(SkBits2Float(0x43e2ba5f), SkBits2Float(0x43e3f9fb), SkBits2Float(0x43e37e57), SkBits2Float(0x43e46df3), SkBits2Float(0x43e3cb45), SkBits2Float(0x43e3bd0d));  // 453.456f, 455.953f, 454.987f, 456.859f, 455.588f, 455.477f

+path.cubicTo(SkBits2Float(0x43e37e57), SkBits2Float(0x43e46df2), SkBits2Float(0x43e2ba60), SkBits2Float(0x43e3f9fb), SkBits2Float(0x43e22d51), SkBits2Float(0x43e41b01));  // 454.987f, 456.859f, 453.456f, 455.953f, 452.354f, 456.211f

+path.close();

+path.moveTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e479fb));  // 452.354f, 456.953f

+path.lineTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e479fb));  // 452.354f, 456.953f

+path.close();

+path.moveTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e479fb));  // 452.354f, 456.953f

+path.lineTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e41b01));  // 452.354f, 456.211f

+path.lineTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e479fb));  // 452.354f, 456.953f

+path.close();

+path.moveTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e479fb));  // 454.706f, 456.953f

+path.lineTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e479fb));  // 454.706f, 456.953f

+path.close();

+path.moveTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e479fb));  // 454.706f, 456.953f

+path.lineTo(SkBits2Float(0x43e22d51), SkBits2Float(0x43e479fb));  // 452.354f, 456.953f

+path.lineTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e479fb));  // 454.706f, 456.953f

+path.close();

+path.moveTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e41b01));  // 454.706f, 456.211f

+path.lineTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e41b01));  // 454.706f, 456.211f

+path.close();

+path.moveTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e41b01));  // 454.706f, 456.211f

+path.lineTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e479fb));  // 454.706f, 456.953f

+path.lineTo(SkBits2Float(0x43e35a5f), SkBits2Float(0x43e41b01));  // 454.706f, 456.211f

+path.close();

+path.moveTo(SkBits2Float(0x43e1726f), SkBits2Float(0x43e90c07));  // 450.894f, 466.094f

+path.lineTo(SkBits2Float(0x43e1726f), SkBits2Float(0x43e90c07));  // 450.894f, 466.094f

+path.close();

+path.moveTo(SkBits2Float(0x43e1726f), SkBits2Float(0x43e90c07));  // 450.894f, 466.094f

+path.cubicTo(SkBits2Float(0x43e2226f), SkBits2Float(0x43e769fb), SkBits2Float(0x43e50a7f), SkBits2Float(0x43e63915), SkBits2Float(0x43e35a5f), SkBits2Float(0x43e41b01));  // 452.269f, 462.828f, 458.082f, 460.446f, 454.706f, 456.211f

+path.cubicTo(SkBits2Float(0x43e50a5f), SkBits2Float(0x43e638f5), SkBits2Float(0x43e2226f), SkBits2Float(0x43e769fb), SkBits2Float(0x43e1726f), SkBits2Float(0x43e90c07));  // 458.081f, 460.445f, 452.269f, 462.828f, 450.894f, 466.094f

+path.close();

+path.moveTo(SkBits2Float(0x43f09f5d), SkBits2Float(0x43ea2709));  // 481.245f, 468.305f

+path.lineTo(SkBits2Float(0x43f09f5d), SkBits2Float(0x43ea2709));  // 481.245f, 468.305f

+path.close();

+path.moveTo(SkBits2Float(0x43f09f5d), SkBits2Float(0x43ea2709));  // 481.245f, 468.305f

+path.cubicTo(SkBits2Float(0x43ebbc6b), SkBits2Float(0x43ea4105), SkBits2Float(0x43e56c6b), SkBits2Float(0x43ec9fff), SkBits2Float(0x43e1724f), SkBits2Float(0x43e90c07));  // 471.472f, 468.508f, 458.847f, 473.25f, 450.893f, 466.094f

+path.cubicTo(SkBits2Float(0x43e56c6c), SkBits2Float(0x43ec9fff), SkBits2Float(0x43ebbc6c), SkBits2Float(0x43ea4105), SkBits2Float(0x43f09f5d), SkBits2Float(0x43ea2709));  // 458.847f, 473.25f, 471.472f, 468.508f, 481.245f, 468.305f

+path.close();

+path.moveTo(SkBits2Float(0x43eea45b), SkBits2Float(0x43e9c7ee));  // 477.284f, 467.562f

+path.lineTo(SkBits2Float(0x43eea45b), SkBits2Float(0x43e9c7ee));  // 477.284f, 467.562f

+path.close();

+path.moveTo(SkBits2Float(0x43eea45b), SkBits2Float(0x43e9c7ee));  // 477.284f, 467.562f

+path.cubicTo(SkBits2Float(0x43ef0c4b), SkBits2Float(0x43ea7ef8), SkBits2Float(0x43eff355), SkBits2Float(0x43ea10e4), SkBits2Float(0x43f09f5d), SkBits2Float(0x43ea26e8));  // 478.096f, 468.992f, 479.901f, 468.132f, 481.245f, 468.304f

+path.cubicTo(SkBits2Float(0x43eff355), SkBits2Float(0x43ea1105), SkBits2Float(0x43ef0c6b), SkBits2Float(0x43ea7ef8), SkBits2Float(0x43eea45b), SkBits2Float(0x43e9c7ee));  // 479.901f, 468.133f, 478.097f, 468.992f, 477.284f, 467.562f

+path.close();

+path.moveTo(SkBits2Float(0x43ee4667), SkBits2Float(0x43ea2709));  // 476.55f, 468.305f

+path.lineTo(SkBits2Float(0x43ee4667), SkBits2Float(0x43ea2709));  // 476.55f, 468.305f

+path.close();

+path.moveTo(SkBits2Float(0x43ee4667), SkBits2Float(0x43ea2709));  // 476.55f, 468.305f

+path.lineTo(SkBits2Float(0x43eea45b), SkBits2Float(0x43e9c80f));  // 477.284f, 467.563f

+path.lineTo(SkBits2Float(0x43ee4667), SkBits2Float(0x43ea2709));  // 476.55f, 468.305f

+path.close();

+path.moveTo(SkBits2Float(0x43e9f26f), SkBits2Float(0x43e6c2f0));  // 467.894f, 461.523f

+path.lineTo(SkBits2Float(0x43e9f26f), SkBits2Float(0x43e6c2f0));  // 467.894f, 461.523f

+path.close();

+path.moveTo(SkBits2Float(0x43e9f26f), SkBits2Float(0x43e6c2f0));  // 467.894f, 461.523f

+path.cubicTo(SkBits2Float(0x43eb8873), SkBits2Float(0x43e7dcec), SkBits2Float(0x43eb747b), SkBits2Float(0x43ea9b00), SkBits2Float(0x43ee4667), SkBits2Float(0x43ea26e8));  // 471.066f, 463.726f, 470.91f, 469.211f, 476.55f, 468.304f

+path.cubicTo(SkBits2Float(0x43eb745b), SkBits2Float(0x43ea9b01), SkBits2Float(0x43eb8853), SkBits2Float(0x43e7dd0d), SkBits2Float(0x43e9f26f), SkBits2Float(0x43e6c2f0));  // 470.909f, 469.211f, 471.065f, 463.727f, 467.894f, 461.523f

+path.close();

+path.moveTo(SkBits2Float(0x43ebee56), SkBits2Float(0x43decc07));  // 471.862f, 445.594f

+path.lineTo(SkBits2Float(0x43ebee56), SkBits2Float(0x43decc07));  // 471.862f, 445.594f

+path.close();

+path.moveTo(SkBits2Float(0x43ebee56), SkBits2Float(0x43decc07));  // 471.862f, 445.594f

+path.cubicTo(SkBits2Float(0x43e85f5c), SkBits2Float(0x43e04915), SkBits2Float(0x43eaa148), SkBits2Float(0x43e41c07), SkBits2Float(0x43e9f24e), SkBits2Float(0x43e6c311));  // 464.745f, 448.571f, 469.26f, 456.219f, 467.893f, 461.524f

+path.cubicTo(SkBits2Float(0x43eaa169), SkBits2Float(0x43e41c07), SkBits2Float(0x43e85f5c), SkBits2Float(0x43e048f4), SkBits2Float(0x43ebee56), SkBits2Float(0x43decc07));  // 469.261f, 456.219f, 464.745f, 448.57f, 471.862f, 445.594f

+path.close();

+path.moveTo(SkBits2Float(0x43eac168), SkBits2Float(0x43dd3fff));  // 469.511f, 442.5f

+path.lineTo(SkBits2Float(0x43eac168), SkBits2Float(0x43dd3fff));  // 469.511f, 442.5f

+path.close();

+path.moveTo(SkBits2Float(0x43eac168), SkBits2Float(0x43dd3fff));  // 469.511f, 442.5f

+path.cubicTo(SkBits2Float(0x43eb245a), SkBits2Float(0x43ddc7ef), SkBits2Float(0x43eaf45a), SkBits2Float(0x43dedd0d), SkBits2Float(0x43ebee76), SkBits2Float(0x43decc07));  // 470.284f, 443.562f, 469.909f, 445.727f, 471.863f, 445.594f

+path.cubicTo(SkBits2Float(0x43eaf459), SkBits2Float(0x43dedd0d), SkBits2Float(0x43eb2459), SkBits2Float(0x43ddc7ee), SkBits2Float(0x43eac168), SkBits2Float(0x43dd3fff));  // 469.909f, 445.727f, 470.284f, 443.562f, 469.511f, 442.5f

+path.close();

+path.moveTo(SkBits2Float(0x43ec4c6a), SkBits2Float(0x43dce105));  // 472.597f, 441.758f

+path.lineTo(SkBits2Float(0x43ec4c6a), SkBits2Float(0x43dce105));  // 472.597f, 441.758f

+path.close();

+path.moveTo(SkBits2Float(0x43ec4c6a), SkBits2Float(0x43dce105));  // 472.597f, 441.758f

+path.cubicTo(SkBits2Float(0x43ebcb64), SkBits2Float(0x43dd08f5), SkBits2Float(0x43eb0c6a), SkBits2Float(0x43dc9603), SkBits2Float(0x43eac168), SkBits2Float(0x43dd3fff));  // 471.589f, 442.07f, 470.097f, 441.172f, 469.511f, 442.5f

+path.cubicTo(SkBits2Float(0x43eb0c6a), SkBits2Float(0x43dc9603), SkBits2Float(0x43ebcb64), SkBits2Float(0x43dd08f5), SkBits2Float(0x43ec4c6a), SkBits2Float(0x43dce105));  // 470.097f, 441.172f, 471.589f, 442.07f, 472.597f, 441.758f

+path.close();

+path.moveTo(SkBits2Float(0x43ecbb64), SkBits2Float(0x43ddb105));  // 473.464f, 443.383f

+path.lineTo(SkBits2Float(0x43ecbb64), SkBits2Float(0x43ddb105));  // 473.464f, 443.383f

+path.close();

+path.moveTo(SkBits2Float(0x43ecbb64), SkBits2Float(0x43ddb105));  // 473.464f, 443.383f

+path.lineTo(SkBits2Float(0x43ec4c6a), SkBits2Float(0x43dce105));  // 472.597f, 441.758f

+path.lineTo(SkBits2Float(0x43ecbb64), SkBits2Float(0x43ddb105));  // 473.464f, 443.383f

+path.close();

+path.moveTo(SkBits2Float(0x43eea45a), SkBits2Float(0x43dc24fd));  // 477.284f, 440.289f

+path.lineTo(SkBits2Float(0x43eea45a), SkBits2Float(0x43dc24fd));  // 477.284f, 440.289f

+path.close();

+path.moveTo(SkBits2Float(0x43eea45a), SkBits2Float(0x43dc24fd));  // 477.284f, 440.289f

+path.cubicTo(SkBits2Float(0x43eef354), SkBits2Float(0x43dd4c07), SkBits2Float(0x43ed4a5e), SkBits2Float(0x43dcfef9), SkBits2Float(0x43ecbb64), SkBits2Float(0x43ddb105));  // 477.901f, 442.594f, 474.581f, 441.992f, 473.464f, 443.383f

+path.cubicTo(SkBits2Float(0x43ed4a5e), SkBits2Float(0x43dcfef9), SkBits2Float(0x43eef354), SkBits2Float(0x43dd4c07), SkBits2Float(0x43eea45a), SkBits2Float(0x43dc24fd));  // 474.581f, 441.992f, 477.901f, 442.594f, 477.284f, 440.289f

+path.close();

+path.moveTo(SkBits2Float(0x43f09f5c), SkBits2Float(0x43dc24fd));  // 481.245f, 440.289f

+path.lineTo(SkBits2Float(0x43f09f5c), SkBits2Float(0x43dc24fd));  // 481.245f, 440.289f

+path.close();

+path.moveTo(SkBits2Float(0x43f09f5c), SkBits2Float(0x43dc24fd));  // 481.245f, 440.289f

+path.cubicTo(SkBits2Float(0x43effc6a), SkBits2Float(0x43daeced), SkBits2Float(0x43ef6a5e), SkBits2Float(0x43dbe4fd), SkBits2Float(0x43eea45a), SkBits2Float(0x43dc24fd));  // 479.972f, 437.851f, 478.831f, 439.789f, 477.284f, 440.289f

+path.cubicTo(SkBits2Float(0x43ef6a5e), SkBits2Float(0x43dbe4fd), SkBits2Float(0x43effc6a), SkBits2Float(0x43daed0d), SkBits2Float(0x43f09f5c), SkBits2Float(0x43dc24fd));  // 478.831f, 439.789f, 479.972f, 437.852f, 481.245f, 440.289f

+path.close();

+path.moveTo(SkBits2Float(0x43f2f76c), SkBits2Float(0x43dbc603));  // 485.933f, 439.547f

+path.lineTo(SkBits2Float(0x43f2f76c), SkBits2Float(0x43dbc603));  // 485.933f, 439.547f

+path.close();

+path.moveTo(SkBits2Float(0x43f2f76c), SkBits2Float(0x43dbc603));  // 485.933f, 439.547f

+path.cubicTo(SkBits2Float(0x43f24c6a), SkBits2Float(0x43dc3b01), SkBits2Float(0x43f16b64), SkBits2Float(0x43dc2311), SkBits2Float(0x43f09f5c), SkBits2Float(0x43dc24fd));  // 484.597f, 440.461f, 482.839f, 440.274f, 481.245f, 440.289f

+path.cubicTo(SkBits2Float(0x43f16b64), SkBits2Float(0x43dc23f7), SkBits2Float(0x43f24c6a), SkBits2Float(0x43dc3b01), SkBits2Float(0x43f2f76c), SkBits2Float(0x43dbc603));  // 482.839f, 440.281f, 484.597f, 440.461f, 485.933f, 439.547f

+path.close();

+path.moveTo(SkBits2Float(0x43f4de55), SkBits2Float(0x43d97d0d));  // 489.737f, 434.977f

+path.lineTo(SkBits2Float(0x43f4de55), SkBits2Float(0x43d97d0d));  // 489.737f, 434.977f

+path.close();

+path.moveTo(SkBits2Float(0x43f4de55), SkBits2Float(0x43d97d0d));  // 489.737f, 434.977f

+path.cubicTo(SkBits2Float(0x43f47665), SkBits2Float(0x43da020b), SkBits2Float(0x43f42851), SkBits2Float(0x43db9417), SkBits2Float(0x43f2f74b), SkBits2Float(0x43dbc603));  // 488.925f, 436.016f, 488.315f, 439.157f, 485.932f, 439.547f

+path.cubicTo(SkBits2Float(0x43f42851), SkBits2Float(0x43db93f7), SkBits2Float(0x43f47666), SkBits2Float(0x43da020b), SkBits2Float(0x43f4de55), SkBits2Float(0x43d97d0d));  // 488.315f, 439.156f, 488.925f, 436.016f, 489.737f, 434.977f

+path.close();

+path.moveTo(SkBits2Float(0x43f48061), SkBits2Float(0x43d97d0d));  // 489.003f, 434.977f

+path.lineTo(SkBits2Float(0x43f48061), SkBits2Float(0x43d97d0d));  // 489.003f, 434.977f

+path.close();

+path.moveTo(SkBits2Float(0x43f48061), SkBits2Float(0x43d97d0d));  // 489.003f, 434.977f

+path.lineTo(SkBits2Float(0x43f4de55), SkBits2Float(0x43d97d0d));  // 489.737f, 434.977f

+path.lineTo(SkBits2Float(0x43f48061), SkBits2Float(0x43d97d0d));  // 489.003f, 434.977f

+path.close();

+path.moveTo(SkBits2Float(0x43f3b353), SkBits2Float(0x43d67709));  // 487.401f, 428.93f

+path.cubicTo(SkBits2Float(0x43f39957), SkBits2Float(0x43d79ef9), SkBits2Float(0x43f3ca5d), SkBits2Float(0x43d8a603), SkBits2Float(0x43f48061), SkBits2Float(0x43d97d0d));  // 487.198f, 431.242f, 487.581f, 433.297f, 489.003f, 434.977f

+path.cubicTo(SkBits2Float(0x43f3ca5d), SkBits2Float(0x43d8a603), SkBits2Float(0x43f39957), SkBits2Float(0x43d79ef9), SkBits2Float(0x43f3b353), SkBits2Float(0x43d67709));  // 487.581f, 433.297f, 487.198f, 431.242f, 487.401f, 428.93f

+path.close();
+}
+
+static void joel_14(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    make_joel_14(path);
+testSimplify(reporter, path, filename);
+}

+
+static void joel_14x(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    make_joel_14(path);
+testSimplify(reporter, path, filename);
+}

+
+static void make_joel_15(SkPath& path) {
+path.moveTo(SkBits2Float(0x439e276d), SkBits2Float(0x43dad106));  // 316.308f, 437.633f

+path.lineTo(SkBits2Float(0x439e276d), SkBits2Float(0x43dad106));  // 316.308f, 437.633f

+path.close();

+path.moveTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d78000));  // 312.198f, 431

+path.lineTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d78000));  // 312.198f, 431

+path.close();

+path.moveTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d78000));  // 312.198f, 431

+path.cubicTo(SkBits2Float(0x439ea45b), SkBits2Float(0x43d6d000), SkBits2Float(0x439cce57), SkBits2Float(0x43d9f3f8), SkBits2Float(0x439e274d), SkBits2Float(0x43dad106));  // 317.284f, 429.625f, 313.612f, 435.906f, 316.307f, 437.633f

+path.cubicTo(SkBits2Float(0x439cce57), SkBits2Float(0x43d9f3f8), SkBits2Float(0x439ea45b), SkBits2Float(0x43d6d000), SkBits2Float(0x439c1959), SkBits2Float(0x43d78000));  // 313.612f, 435.906f, 317.284f, 429.625f, 312.198f, 431

+path.close();

+path.moveTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d8f8f6));  // 312.198f, 433.945f

+path.lineTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d8f8f6));  // 312.198f, 433.945f

+path.close();

+path.moveTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d8f8f6));  // 312.198f, 433.945f

+path.lineTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d78000));  // 312.198f, 431

+path.lineTo(SkBits2Float(0x439c1959), SkBits2Float(0x43d8f8f6));  // 312.198f, 433.945f

+path.close();

+path.moveTo(SkBits2Float(0x439f7853), SkBits2Float(0x43e5820c));  // 318.94f, 459.016f

+path.lineTo(SkBits2Float(0x439f7853), SkBits2Float(0x43e5820c));  // 318.94f, 459.016f

+path.close();

+path.moveTo(SkBits2Float(0x439f7853), SkBits2Float(0x43e5820c));  // 318.94f, 459.016f

+path.cubicTo(SkBits2Float(0x439e1647), SkBits2Float(0x43e17106), SkBits2Float(0x439d945b), SkBits2Float(0x43dd020c), SkBits2Float(0x439c1959), SkBits2Float(0x43d8f916));  // 316.174f, 450.883f, 315.159f, 442.016f, 312.198f, 433.946f

+path.cubicTo(SkBits2Float(0x439d945b), SkBits2Float(0x43dd020c), SkBits2Float(0x439e1667), SkBits2Float(0x43e17106), SkBits2Float(0x439f7853), SkBits2Float(0x43e5820c));  // 315.159f, 442.016f, 316.175f, 450.883f, 318.94f, 459.016f

+path.close();

+path.moveTo(SkBits2Float(0x439ffc6c), SkBits2Float(0x43e7f106));  // 319.972f, 463.883f

+path.lineTo(SkBits2Float(0x439ffc6c), SkBits2Float(0x43e7f106));  // 319.972f, 463.883f

+path.close();

+path.moveTo(SkBits2Float(0x439ffc6c), SkBits2Float(0x43e7f106));  // 319.972f, 463.883f

+path.cubicTo(SkBits2Float(0x439f5668), SkBits2Float(0x43e758f6), SkBits2Float(0x439fec6c), SkBits2Float(0x43e63604), SkBits2Float(0x439f7874), SkBits2Float(0x43e5820c));  // 318.675f, 462.695f, 319.847f, 460.422f, 318.941f, 459.016f

+path.cubicTo(SkBits2Float(0x439fec6c), SkBits2Float(0x43e63604), SkBits2Float(0x439f5668), SkBits2Float(0x43e758f5), SkBits2Float(0x439ffc6c), SkBits2Float(0x43e7f106));  // 319.847f, 460.422f, 318.675f, 462.695f, 319.972f, 463.883f

+path.close();

+path.moveTo(SkBits2Float(0x43a12853), SkBits2Float(0x43ede9fc));  // 322.315f, 475.828f

+path.lineTo(SkBits2Float(0x43a12853), SkBits2Float(0x43ede9fc));  // 322.315f, 475.828f

+path.close();

+path.moveTo(SkBits2Float(0x43a12853), SkBits2Float(0x43ede9fc));  // 322.315f, 475.828f

+path.cubicTo(SkBits2Float(0x43a18c4b), SkBits2Float(0x43eb7604), SkBits2Float(0x439fe45b), SkBits2Float(0x43ea4b02), SkBits2Float(0x439ffc4b), SkBits2Float(0x43e7f106));  // 323.096f, 470.922f, 319.784f, 468.586f, 319.971f, 463.883f

+path.cubicTo(SkBits2Float(0x439fe45b), SkBits2Float(0x43ea4b02), SkBits2Float(0x43a18c6c), SkBits2Float(0x43eb7604), SkBits2Float(0x43a12853), SkBits2Float(0x43ede9fc));  // 319.784f, 468.586f, 323.097f, 470.922f, 322.315f, 475.828f

+path.close();

+path.moveTo(SkBits2Float(0x43a1e45b), SkBits2Float(0x43ef63f8));  // 323.784f, 478.781f

+path.lineTo(SkBits2Float(0x43a1e45b), SkBits2Float(0x43ef63f8));  // 323.784f, 478.781f

+path.close();

+path.moveTo(SkBits2Float(0x43a1e45b), SkBits2Float(0x43ef63f8));  // 323.784f, 478.781f

+path.cubicTo(SkBits2Float(0x43a20561), SkBits2Float(0x43eeb9fc), SkBits2Float(0x43a1ae57), SkBits2Float(0x43ee4be8), SkBits2Float(0x43a12853), SkBits2Float(0x43ede9fc));  // 324.042f, 477.453f, 323.362f, 476.593f, 322.315f, 475.828f

+path.cubicTo(SkBits2Float(0x43a1ae57), SkBits2Float(0x43ee4c08), SkBits2Float(0x43a20561), SkBits2Float(0x43eeb9fc), SkBits2Float(0x43a1e45b), SkBits2Float(0x43ef63f8));  // 323.362f, 476.594f, 324.042f, 477.453f, 323.784f, 478.781f

+path.close();

+path.moveTo(SkBits2Float(0x439fb169), SkBits2Float(0x43f032f2));  // 319.386f, 480.398f

+path.lineTo(SkBits2Float(0x439fb169), SkBits2Float(0x43f032f2));  // 319.386f, 480.398f

+path.close();

+path.moveTo(SkBits2Float(0x439fb169), SkBits2Float(0x43f032f2));  // 319.386f, 480.398f

+path.cubicTo(SkBits2Float(0x43a08063), SkBits2Float(0x43f022f2), SkBits2Float(0x43a1ec6b), SkBits2Float(0x43f078f6), SkBits2Float(0x43a1e45b), SkBits2Float(0x43ef63f8));  // 321.003f, 480.273f, 323.847f, 480.945f, 323.784f, 478.781f

+path.cubicTo(SkBits2Float(0x43a1ec6b), SkBits2Float(0x43f078f6), SkBits2Float(0x43a08063), SkBits2Float(0x43f022f2), SkBits2Float(0x439fb169), SkBits2Float(0x43f032f2));  // 323.847f, 480.945f, 321.003f, 480.273f, 319.386f, 480.398f

+path.close();

+path.moveTo(SkBits2Float(0x439e4d50), SkBits2Float(0x43f16106));  // 316.604f, 482.758f

+path.lineTo(SkBits2Float(0x439e4d50), SkBits2Float(0x43f16106));  // 316.604f, 482.758f

+path.close();

+path.moveTo(SkBits2Float(0x439e4d50), SkBits2Float(0x43f16106));  // 316.604f, 482.758f

+path.cubicTo(SkBits2Float(0x439de45a), SkBits2Float(0x43f05000), SkBits2Float(0x439f445a), SkBits2Float(0x43f0b20c), SkBits2Float(0x439fb148), SkBits2Float(0x43f03312));  // 315.784f, 480.625f, 318.534f, 481.391f, 319.385f, 480.399f

+path.cubicTo(SkBits2Float(0x439f445a), SkBits2Float(0x43f0b20c), SkBits2Float(0x439de45a), SkBits2Float(0x43f05000), SkBits2Float(0x439e4d50), SkBits2Float(0x43f16106));  // 318.534f, 481.391f, 315.784f, 480.625f, 316.604f, 482.758f

+path.close();

+path.moveTo(SkBits2Float(0x43a0de56), SkBits2Float(0x43f7470a));  // 321.737f, 494.555f

+path.lineTo(SkBits2Float(0x43a0de56), SkBits2Float(0x43f7470a));  // 321.737f, 494.555f

+path.close();

+path.moveTo(SkBits2Float(0x43a0de56), SkBits2Float(0x43f7470a));  // 321.737f, 494.555f

+path.cubicTo(SkBits2Float(0x439f4062), SkBits2Float(0x43f5a106), SkBits2Float(0x439f2b64), SkBits2Float(0x43f33106), SkBits2Float(0x439e4d50), SkBits2Float(0x43f16106));  // 318.503f, 491.258f, 318.339f, 486.383f, 316.604f, 482.758f

+path.cubicTo(SkBits2Float(0x439f2b64), SkBits2Float(0x43f33106), SkBits2Float(0x439f4062), SkBits2Float(0x43f5a106), SkBits2Float(0x43a0de56), SkBits2Float(0x43f7470a));  // 318.339f, 486.383f, 318.503f, 491.258f, 321.737f, 494.555f

+path.close();

+path.moveTo(SkBits2Float(0x43a3945a), SkBits2Float(0x43fa13f8));  // 327.159f, 500.156f

+path.lineTo(SkBits2Float(0x43a3945a), SkBits2Float(0x43fa13f8));  // 327.159f, 500.156f

+path.close();

+path.moveTo(SkBits2Float(0x43a3945a), SkBits2Float(0x43fa13f8));  // 327.159f, 500.156f

+path.cubicTo(SkBits2Float(0x43a2dc4a), SkBits2Float(0x43f8ab02), SkBits2Float(0x43a0d74c), SkBits2Float(0x43f8f4fe), SkBits2Float(0x43a0de56), SkBits2Float(0x43f746ea));  // 325.721f, 497.336f, 321.682f, 497.914f, 321.737f, 494.554f

+path.cubicTo(SkBits2Float(0x43a0d76d), SkBits2Float(0x43f8f4fe), SkBits2Float(0x43a2dc6a), SkBits2Float(0x43f8ab03), SkBits2Float(0x43a3945a), SkBits2Float(0x43fa13f8));  // 321.683f, 497.914f, 325.722f, 497.336f, 327.159f, 500.156f

+path.close();

+path.moveTo(SkBits2Float(0x43a58e56), SkBits2Float(0x43fa98f6));  // 331.112f, 501.195f

+path.lineTo(SkBits2Float(0x43a58e56), SkBits2Float(0x43fa98f6));  // 331.112f, 501.195f

+path.close();

+path.moveTo(SkBits2Float(0x43a58e56), SkBits2Float(0x43fa98f6));  // 331.112f, 501.195f

+path.cubicTo(SkBits2Float(0x43a50148), SkBits2Float(0x43fa2be8), SkBits2Float(0x43a45646), SkBits2Float(0x43fa02f2), SkBits2Float(0x43a3945a), SkBits2Float(0x43fa13f8));  // 330.01f, 500.343f, 328.674f, 500.023f, 327.159f, 500.156f

+path.cubicTo(SkBits2Float(0x43a45666), SkBits2Float(0x43fa02f2), SkBits2Float(0x43a50168), SkBits2Float(0x43fa2c08), SkBits2Float(0x43a58e56), SkBits2Float(0x43fa98f6));  // 328.675f, 500.023f, 330.011f, 500.344f, 331.112f, 501.195f

+path.close();

+path.moveTo(SkBits2Float(0x43a64958), SkBits2Float(0x43f8c000));  // 332.573f, 497.5f

+path.lineTo(SkBits2Float(0x43a64958), SkBits2Float(0x43f8c000));  // 332.573f, 497.5f

+path.close();

+path.moveTo(SkBits2Float(0x43a64958), SkBits2Float(0x43f8c000));  // 332.573f, 497.5f

+path.lineTo(SkBits2Float(0x43a58e56), SkBits2Float(0x43fa98f6));  // 331.112f, 501.195f

+path.lineTo(SkBits2Float(0x43a64958), SkBits2Float(0x43f8c000));  // 332.573f, 497.5f

+path.close();

+path.moveTo(SkBits2Float(0x43a73e56), SkBits2Float(0x43f5820c));  // 334.487f, 491.016f

+path.lineTo(SkBits2Float(0x43a73e56), SkBits2Float(0x43f5820c));  // 334.487f, 491.016f

+path.close();

+path.moveTo(SkBits2Float(0x43a73e56), SkBits2Float(0x43f5820c));  // 334.487f, 491.016f

+path.cubicTo(SkBits2Float(0x43a64d50), SkBits2Float(0x43f654fe), SkBits2Float(0x43a7174c), SkBits2Float(0x43f7de14), SkBits2Float(0x43a64958), SkBits2Float(0x43f8c000));  // 332.604f, 492.664f, 334.182f, 495.735f, 332.573f, 497.5f

+path.cubicTo(SkBits2Float(0x43a7176c), SkBits2Float(0x43f7ddf4), SkBits2Float(0x43a64d50), SkBits2Float(0x43f654fe), SkBits2Float(0x43a73e56), SkBits2Float(0x43f5820c));  // 334.183f, 495.734f, 332.604f, 492.664f, 334.487f, 491.016f

+path.close();

+path.moveTo(SkBits2Float(0x43a6f26f), SkBits2Float(0x43f20b02));  // 333.894f, 484.086f

+path.lineTo(SkBits2Float(0x43a6f26f), SkBits2Float(0x43f20b02));  // 333.894f, 484.086f

+path.close();

+path.moveTo(SkBits2Float(0x43a6f26f), SkBits2Float(0x43f20b02));  // 333.894f, 484.086f

+path.cubicTo(SkBits2Float(0x43a78d71), SkBits2Float(0x43f2f810), SkBits2Float(0x43a72873), SkBits2Float(0x43f453f8), SkBits2Float(0x43a73e77), SkBits2Float(0x43f5820c));  // 335.105f, 485.938f, 334.316f, 488.656f, 334.488f, 491.016f

+path.cubicTo(SkBits2Float(0x43a72852), SkBits2Float(0x43f453f8), SkBits2Float(0x43a78d50), SkBits2Float(0x43f2f810), SkBits2Float(0x43a6f26f), SkBits2Float(0x43f20b02));  // 334.315f, 488.656f, 335.104f, 485.938f, 333.894f, 484.086f

+path.close();

+path.moveTo(SkBits2Float(0x43a6ba5f), SkBits2Float(0x43ef3d0e));  // 333.456f, 478.477f

+path.lineTo(SkBits2Float(0x43a6ba5f), SkBits2Float(0x43ef3d0e));  // 333.456f, 478.477f

+path.close();

+path.moveTo(SkBits2Float(0x43a6ba5f), SkBits2Float(0x43ef3d0e));  // 333.456f, 478.477f

+path.cubicTo(SkBits2Float(0x43a60e57), SkBits2Float(0x43f04000), SkBits2Float(0x43a82355), SkBits2Float(0x43f0fc08), SkBits2Float(0x43a6f26f), SkBits2Float(0x43f20b02));  // 332.112f, 480.5f, 336.276f, 481.969f, 333.894f, 484.086f

+path.cubicTo(SkBits2Float(0x43a82354), SkBits2Float(0x43f0fc08), SkBits2Float(0x43a60e56), SkBits2Float(0x43f04000), SkBits2Float(0x43a6ba5f), SkBits2Float(0x43ef3d0e));  // 336.276f, 481.969f, 332.112f, 480.5f, 333.456f, 478.477f

+path.close();

+path.moveTo(SkBits2Float(0x43a35c6b), SkBits2Float(0x43ef88f5));  // 326.722f, 479.07f

+path.lineTo(SkBits2Float(0x43a35c6b), SkBits2Float(0x43ef88f5));  // 326.722f, 479.07f

+path.close();

+path.moveTo(SkBits2Float(0x43a35c6b), SkBits2Float(0x43ef88f5));  // 326.722f, 479.07f

+path.cubicTo(SkBits2Float(0x43a4b26f), SkBits2Float(0x43efe105), SkBits2Float(0x43a5b76d), SkBits2Float(0x43ee2ef9), SkBits2Float(0x43a6ba5f), SkBits2Float(0x43ef3ced));  // 329.394f, 479.758f, 331.433f, 476.367f, 333.456f, 478.476f

+path.cubicTo(SkBits2Float(0x43a5b76d), SkBits2Float(0x43ee2ef9), SkBits2Float(0x43a4b26f), SkBits2Float(0x43efe106), SkBits2Float(0x43a35c6b), SkBits2Float(0x43ef88f5));  // 331.433f, 476.367f, 329.394f, 479.758f, 326.722f, 479.07f

+path.close();

+path.moveTo(SkBits2Float(0x43a08063), SkBits2Float(0x43e5a70a));  // 321.003f, 459.305f

+path.lineTo(SkBits2Float(0x43a08063), SkBits2Float(0x43e5a70a));  // 321.003f, 459.305f

+path.close();

+path.moveTo(SkBits2Float(0x43a08063), SkBits2Float(0x43e5a70a));  // 321.003f, 459.305f

+path.cubicTo(SkBits2Float(0x43a15169), SkBits2Float(0x43e90312), SkBits2Float(0x43a2626f), SkBits2Float(0x43ec4312), SkBits2Float(0x43a35c6b), SkBits2Float(0x43ef8916));  // 322.636f, 466.024f, 324.769f, 472.524f, 326.722f, 479.071f

+path.cubicTo(SkBits2Float(0x43a2626f), SkBits2Float(0x43ec42f1), SkBits2Float(0x43a15169), SkBits2Float(0x43e902f1), SkBits2Float(0x43a08063), SkBits2Float(0x43e5a70a));  // 324.769f, 472.523f, 322.636f, 466.023f, 321.003f, 459.305f

+path.close();

+path.moveTo(SkBits2Float(0x43a05a5f), SkBits2Float(0x43e407ef));  // 320.706f, 456.062f

+path.lineTo(SkBits2Float(0x43a05a5f), SkBits2Float(0x43e407ef));  // 320.706f, 456.062f

+path.close();

+path.moveTo(SkBits2Float(0x43a05a5f), SkBits2Float(0x43e407ef));  // 320.706f, 456.062f

+path.lineTo(SkBits2Float(0x43a08063), SkBits2Float(0x43e5a6e9));  // 321.003f, 459.304f

+path.lineTo(SkBits2Float(0x43a05a5f), SkBits2Float(0x43e407ef));  // 320.706f, 456.062f

+path.close();

+path.moveTo(SkBits2Float(0x439ecf5d), SkBits2Float(0x43dd3fff));  // 317.62f, 442.5f

+path.lineTo(SkBits2Float(0x439ecf5d), SkBits2Float(0x43dd3fff));  // 317.62f, 442.5f

+path.close();

+path.moveTo(SkBits2Float(0x439ecf5d), SkBits2Float(0x43dd3fff));  // 317.62f, 442.5f

+path.cubicTo(SkBits2Float(0x439e9c6b), SkBits2Float(0x43dfcb01), SkBits2Float(0x439fbe57), SkBits2Float(0x43e1cc07), SkBits2Float(0x43a05a5f), SkBits2Float(0x43e407ef));  // 317.222f, 447.586f, 319.487f, 451.594f, 320.706f, 456.062f

+path.cubicTo(SkBits2Float(0x439fbe57), SkBits2Float(0x43e1cc08), SkBits2Float(0x439e9c6b), SkBits2Float(0x43dfcb01), SkBits2Float(0x439ecf5d), SkBits2Float(0x43dd3fff));  // 319.487f, 451.594f, 317.222f, 447.586f, 317.62f, 442.5f

+path.close();

+path.moveTo(SkBits2Float(0x439e276d), SkBits2Float(0x43dad105));  // 316.308f, 437.633f

+path.cubicTo(SkBits2Float(0x439e4979), SkBits2Float(0x43dba4fd), SkBits2Float(0x439dc375), SkBits2Float(0x43dce915), SkBits2Float(0x439ecf5d), SkBits2Float(0x43dd3fff));  // 316.574f, 439.289f, 315.527f, 441.821f, 317.62f, 442.5f

+path.cubicTo(SkBits2Float(0x439dc355), SkBits2Float(0x43dce8f5), SkBits2Float(0x439e4959), SkBits2Float(0x43dba4fd), SkBits2Float(0x439e276d), SkBits2Float(0x43dad105));  // 315.526f, 441.82f, 316.573f, 439.289f, 316.308f, 437.633f

+path.close();
+}
+
+static void joel_15(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    make_joel_15(path);
+testSimplify(reporter, path, filename);
+}

+
+static void joel_15x(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    make_joel_15(path);
+testSimplify(reporter, path, filename);
+}

+
+static void make_joel_16(SkPath& path) {
+path.moveTo(SkBits2Float(0x420e6c8b), SkBits2Float(0x426bdf3b));  // 35.606f, 58.968f

+path.lineTo(SkBits2Float(0x420fcccd), SkBits2Float(0x426c7ef9));  // 35.95f, 59.124f

+path.cubicTo(SkBits2Float(0x420fcccd), SkBits2Float(0x426c7ef9), SkBits2Float(0x42093d71), SkBits2Float(0x426c6e97), SkBits2Float(0x42036c8b), SkBits2Float(0x426cbf7c));  // 35.95f, 59.124f, 34.31f, 59.108f, 32.856f, 59.187f

+path.cubicTo(SkBits2Float(0x41fb3958), SkBits2Float(0x426d0f5b), SkBits2Float(0x41f076c8), SkBits2Float(0x426d48b3), SkBits2Float(0x41ef47ae), SkBits2Float(0x426d947a));  // 31.403f, 59.265f, 30.058f, 59.321f, 29.91f, 59.395f

+path.cubicTo(SkBits2Float(0x41ee1aa0), SkBits2Float(0x426ddf3b), SkBits2Float(0x41ec6041), SkBits2Float(0x426edb22), SkBits2Float(0x41eb1aa0), SkBits2Float(0x426fee97));  // 29.763f, 59.468f, 29.547f, 59.714f, 29.388f, 59.983f

+path.cubicTo(SkBits2Float(0x41eb1eb9), SkBits2Float(0x426feb85), SkBits2Float(0x41e9ba5e), SkBits2Float(0x42711eb8), SkBits2Float(0x41e9ba5e), SkBits2Float(0x42711eb8));  // 29.39f, 59.98f, 29.216f, 60.28f, 29.216f, 60.28f

+path.lineTo(SkBits2Float(0x41e99999), SkBits2Float(0x42718f5c));  // 29.2f, 60.39f

+path.cubicTo(SkBits2Float(0x41ea76c8), SkBits2Float(0x4271a5e3), SkBits2Float(0x4212dd2f), SkBits2Float(0x42707efa), SkBits2Float(0x4212dd2f), SkBits2Float(0x42707efa));  // 29.308f, 60.412f, 36.716f, 60.124f, 36.716f, 60.124f

+path.cubicTo(SkBits2Float(0x4212dd2f), SkBits2Float(0x42707efa), SkBits2Float(0x42124395), SkBits2Float(0x42707be8), SkBits2Float(0x42131ba6), SkBits2Float(0x4270b646));  // 36.716f, 60.124f, 36.566f, 60.121f, 36.777f, 60.178f

+path.cubicTo(SkBits2Float(0x42131581), SkBits2Float(0x42710000), SkBits2Float(0x42130831), SkBits2Float(0x42711688), SkBits2Float(0x4213072b), SkBits2Float(0x42711688));  // 36.771f, 60.25f, 36.758f, 60.272f, 36.757f, 60.272f

+path.cubicTo(SkBits2Float(0x4212fae1), SkBits2Float(0x42711aa1), SkBits2Float(0x42127cee), SkBits2Float(0x42714eda), SkBits2Float(0x42127cee), SkBits2Float(0x42714eda));  // 36.745f, 60.276f, 36.622f, 60.327f, 36.622f, 60.327f

+path.cubicTo(SkBits2Float(0x42127ae2), SkBits2Float(0x42714eda), SkBits2Float(0x41c67ae2), SkBits2Float(0x42730f5d), SkBits2Float(0x41c345a2), SkBits2Float(0x427329fd));  // 36.62f, 60.327f, 24.81f, 60.765f, 24.409f, 60.791f

+path.cubicTo(SkBits2Float(0x41c247ae), SkBits2Float(0x42733e78), SkBits2Float(0x41c04396), SkBits2Float(0x42738e57), SkBits2Float(0x41bf4bc7), SkBits2Float(0x4273e45b));  // 24.285f, 60.811f, 24.033f, 60.889f, 23.912f, 60.973f

+path.cubicTo(SkBits2Float(0x41bf5c29), SkBits2Float(0x4273e042), SkBits2Float(0x41be9db3), SkBits2Float(0x4274322e), SkBits2Float(0x41be9db3), SkBits2Float(0x4274322e));  // 23.92f, 60.969f, 23.827f, 61.049f, 23.827f, 61.049f

+path.lineTo(SkBits2Float(0x41be26ea), SkBits2Float(0x42746c8c));  // 23.769f, 61.106f

+path.cubicTo(SkBits2Float(0x41be1eb9), SkBits2Float(0x427470a5), SkBits2Float(0x41bde354), SkBits2Float(0x42748313), SkBits2Float(0x41bde354), SkBits2Float(0x42748313));  // 23.765f, 61.11f, 23.736f, 61.128f, 23.736f, 61.128f

+path.lineTo(SkBits2Float(0x41bcc083), SkBits2Float(0x42751582));  // 23.594f, 61.271f

+path.lineTo(SkBits2Float(0x41bcf3b6), SkBits2Float(0x427526ea));  // 23.619f, 61.288f

+path.lineTo(SkBits2Float(0x41bd0e56), SkBits2Float(0x42756979));  // 23.632f, 61.353f

+path.lineTo(SkBits2Float(0x41bd7cee), SkBits2Float(0x42758313));  // 23.686f, 61.378f

+path.cubicTo(SkBits2Float(0x41be8107), SkBits2Float(0x427572b1), SkBits2Float(0x41bf2d0f), SkBits2Float(0x42754290), SkBits2Float(0x41bfd2f2), SkBits2Float(0x4275147b));  // 23.813f, 61.362f, 23.897f, 61.315f, 23.978f, 61.27f

+path.lineTo(SkBits2Float(0x41c0ba5f), SkBits2Float(0x4274da1d));  // 24.091f, 61.213f

+path.lineTo(SkBits2Float(0x41c0ef9e), SkBits2Float(0x4274de36));  // 24.117f, 61.217f

+path.lineTo(SkBits2Float(0x41c13f7d), SkBits2Float(0x4274d3f9));  // 24.156f, 61.207f

+path.cubicTo(SkBits2Float(0x41c13f7d), SkBits2Float(0x4274d3f9), SkBits2Float(0x41c174bc), SkBits2Float(0x4274c18a), SkBits2Float(0x41c17cee), SkBits2Float(0x4274be78));  // 24.156f, 61.207f, 24.182f, 61.189f, 24.186f, 61.186f

+path.cubicTo(SkBits2Float(0x41c18107), SkBits2Float(0x4274bf7e), SkBits2Float(0x41c1e561), SkBits2Float(0x4274b022), SkBits2Float(0x41c1e561), SkBits2Float(0x4274b022));  // 24.188f, 61.187f, 24.237f, 61.172f, 24.237f, 61.172f

+path.lineTo(SkBits2Float(0x41c45e36), SkBits2Float(0x42746e99));  // 24.546f, 61.108f

+path.cubicTo(SkBits2Float(0x41c4624f), SkBits2Float(0x42746e99), SkBits2Float(0x41cf999a), SkBits2Float(0x42743853), SkBits2Float(0x41cf999a), SkBits2Float(0x42743853));  // 24.548f, 61.108f, 25.95f, 61.055f, 25.95f, 61.055f

+path.lineTo(SkBits2Float(0x420d126f), SkBits2Float(0x4272b43a));  // 35.268f, 60.676f

+path.cubicTo(SkBits2Float(0x420d0938), SkBits2Float(0x4272c084), SkBits2Float(0x420cfcee), SkBits2Float(0x4272c49c), SkBits2Float(0x420cfcee), SkBits2Float(0x4272d917));  // 35.259f, 60.688f, 35.247f, 60.692f, 35.247f, 60.712f

+path.lineTo(SkBits2Float(0x420d0938), SkBits2Float(0x4272b43a));  // 35.259f, 60.676f

+path.cubicTo(SkBits2Float(0x420c7be8), SkBits2Float(0x42737efb), SkBits2Float(0x420b3128), SkBits2Float(0x42743128), SkBits2Float(0x420a27f0), SkBits2Float(0x4274c18a));  // 35.121f, 60.874f, 34.798f, 61.048f, 34.539f, 61.189f

+path.lineTo(SkBits2Float(0x42099eb9), SkBits2Float(0x42750c4b));  // 34.405f, 61.262f

+path.cubicTo(SkBits2Float(0x420872b1), SkBits2Float(0x4275b022), SkBits2Float(0x4206fbe8), SkBits2Float(0x42764397), SkBits2Float(0x42054396), SkBits2Float(0x4276c084));  // 34.112f, 61.422f, 33.746f, 61.566f, 33.316f, 61.688f

+path.cubicTo(SkBits2Float(0x42028313), SkBits2Float(0x42776b86), SkBits2Float(0x42007be8), SkBits2Float(0x4278de36), SkBits2Float(0x41fe7ae2), SkBits2Float(0x427b0f5d));  // 32.628f, 61.855f, 32.121f, 62.217f, 31.81f, 62.765f

+path.cubicTo(SkBits2Float(0x41fe4fe0), SkBits2Float(0x427b21cc), SkBits2Float(0x41fdbe78), SkBits2Float(0x427b8419), SkBits2Float(0x41fdbe78), SkBits2Float(0x427b8419));  // 31.789f, 62.783f, 31.718f, 62.879f, 31.718f, 62.879f

+path.cubicTo(SkBits2Float(0x41fdccce), SkBits2Float(0x427b71aa), SkBits2Float(0x41fd1cad), SkBits2Float(0x427c27f0), SkBits2Float(0x41fd1cad), SkBits2Float(0x427c27f0));  // 31.725f, 62.861f, 31.639f, 63.039f, 31.639f, 63.039f

+path.lineTo(SkBits2Float(0x41fc1eb9), SkBits2Float(0x427d178e));  // 31.515f, 63.273f

+path.lineTo(SkBits2Float(0x41fc7efb), SkBits2Float(0x427d020d));  // 31.562f, 63.252f

+path.lineTo(SkBits2Float(0x41fbb647), SkBits2Float(0x427d3646));  // 31.464f, 63.303f

+path.lineTo(SkBits2Float(0x41fbe76e), SkBits2Float(0x427d25e4));  // 31.488f, 63.287f

+path.lineTo(SkBits2Float(0x41fae149), SkBits2Float(0x427d1fbf));  // 31.36f, 63.281f

+path.lineTo(SkBits2Float(0x41fa5812), SkBits2Float(0x427d178e));  // 31.293f, 63.273f

+path.cubicTo(SkBits2Float(0x41f88108), SkBits2Float(0x427cf9dc), SkBits2Float(0x41f73541), SkBits2Float(0x427cb646), SkBits2Float(0x41f5d70c), SkBits2Float(0x427c6d92));  // 31.063f, 63.244f, 30.901f, 63.178f, 30.73f, 63.107f

+path.lineTo(SkBits2Float(0x41f5999b), SkBits2Float(0x427c6148));  // 30.7f, 63.095f

+path.cubicTo(SkBits2Float(0x41f5999b), SkBits2Float(0x427c6148), SkBits2Float(0x41f2d0e7), SkBits2Float(0x427bdc29), SkBits2Float(0x41f2a9fd), SkBits2Float(0x427bd4fe));  // 30.7f, 63.095f, 30.352f, 62.965f, 30.333f, 62.958f

+path.cubicTo(SkBits2Float(0x41f28d51), SkBits2Float(0x427bc49c), SkBits2Float(0x41f26667), SkBits2Float(0x427bb021), SkBits2Float(0x41f26667), SkBits2Float(0x427bb021));  // 30.319f, 62.942f, 30.3f, 62.922f, 30.3f, 62.922f

+path.lineTo(SkBits2Float(0x41efed92), SkBits2Float(0x427b1db2));  // 29.991f, 62.779f

+path.lineTo(SkBits2Float(0x41ec9582), SkBits2Float(0x427a624e));  // 29.573f, 62.596f

+path.cubicTo(SkBits2Float(0x41eca1cc), SkBits2Float(0x427a645a), SkBits2Float(0x41eaf9dc), SkBits2Float(0x427a3021), SkBits2Float(0x41eaf9dc), SkBits2Float(0x427a3021));  // 29.579f, 62.598f, 29.372f, 62.547f, 29.372f, 62.547f

+path.cubicTo(SkBits2Float(0x41eaf9dc), SkBits2Float(0x427a3021), SkBits2Float(0x41ea126f), SkBits2Float(0x427a1894), SkBits2Float(0x41e9f3b7), SkBits2Float(0x427a1687));  // 29.372f, 62.547f, 29.259f, 62.524f, 29.244f, 62.522f

+path.cubicTo(SkBits2Float(0x41e9ccce), SkBits2Float(0x427a072b), SkBits2Float(0x41e99375), SkBits2Float(0x4279f1aa), SkBits2Float(0x41e99375), SkBits2Float(0x4279f1aa));  // 29.225f, 62.507f, 29.197f, 62.486f, 29.197f, 62.486f

+path.lineTo(SkBits2Float(0x41e86e98), SkBits2Float(0x4279d604));  // 29.054f, 62.459f

+path.lineTo(SkBits2Float(0x41e6147b), SkBits2Float(0x4279a3d7));  // 28.76f, 62.41f

+path.cubicTo(SkBits2Float(0x41e00625), SkBits2Float(0x42796b85), SkBits2Float(0x41db49ba), SkBits2Float(0x427a7ae1), SkBits2Float(0x41d62b02), SkBits2Float(0x427bc8b4));  // 28.003f, 62.355f, 27.411f, 62.62f, 26.771f, 62.946f

+path.cubicTo(SkBits2Float(0x41d24fdf), SkBits2Float(0x427cba5e), SkBits2Float(0x41cecccd), SkBits2Float(0x427ce872), SkBits2Float(0x41ca0e56), SkBits2Float(0x427c6872));  // 26.289f, 63.182f, 25.85f, 63.227f, 25.257f, 63.102f

+path.cubicTo(SkBits2Float(0x41ca0a3d), SkBits2Float(0x427c676c), SkBits2Float(0x41c9353f), SkBits2Float(0x427c570a), SkBits2Float(0x41c9353f), SkBits2Float(0x427c570a));  // 25.255f, 63.101f, 25.151f, 63.085f, 25.151f, 63.085f

+path.lineTo(SkBits2Float(0x41c73b64), SkBits2Float(0x427c26e9));  // 24.904f, 63.038f

+path.lineTo(SkBits2Float(0x41c774bc), SkBits2Float(0x427c374b));  // 24.932f, 63.054f

+path.lineTo(SkBits2Float(0x41c67ef9), SkBits2Float(0x427c0312));  // 24.812f, 63.003f

+path.cubicTo(SkBits2Float(0x41c4df3b), SkBits2Float(0x427bc5a1), SkBits2Float(0x41c2a3d6), SkBits2Float(0x427b8d4f), SkBits2Float(0x41c0851e), SkBits2Float(0x427b6978));  // 24.609f, 62.943f, 24.33f, 62.888f, 24.065f, 62.853f

+path.cubicTo(SkBits2Float(0x41bf1893), SkBits2Float(0x427b52f1), SkBits2Float(0x41bd2d0e), SkBits2Float(0x427b52f1), SkBits2Float(0x41bc020c), SkBits2Float(0x427b5e34));  // 23.887f, 62.831f, 23.647f, 62.831f, 23.501f, 62.842f

+path.lineTo(SkBits2Float(0x41bac6a8), SkBits2Float(0x427b6871));  // 23.347f, 62.852f

+path.cubicTo(SkBits2Float(0x41b9db23), SkBits2Float(0x427b72ae), SkBits2Float(0x41b87cee), SkBits2Float(0x427b820b), SkBits2Float(0x41b7fbe7), SkBits2Float(0x427b655f));  // 23.232f, 62.862f, 23.061f, 62.877f, 22.998f, 62.849f

+path.cubicTo(SkBits2Float(0x41b7fbe7), SkBits2Float(0x427b5f3a), SkBits2Float(0x41b7dd2f), SkBits2Float(0x427b48b3), SkBits2Float(0x41b7dd2f), SkBits2Float(0x427b48b3));  // 22.998f, 62.843f, 22.983f, 62.821f, 22.983f, 62.821f

+path.lineTo(SkBits2Float(0x41b7a5e3), SkBits2Float(0x427b22d0));  // 22.956f, 62.784f

+path.cubicTo(SkBits2Float(0x41b7be76), SkBits2Float(0x427b3332), SkBits2Float(0x41b74395), SkBits2Float(0x427aed91), SkBits2Float(0x41b74395), SkBits2Float(0x427aed91));  // 22.968f, 62.8f, 22.908f, 62.732f, 22.908f, 62.732f

+path.lineTo(SkBits2Float(0x41b70c49), SkBits2Float(0x427acfdf));  // 22.881f, 62.703f

+path.cubicTo(SkBits2Float(0x41b70418), SkBits2Float(0x427ad916), SkBits2Float(0x41b6d70a), SkBits2Float(0x427a9168), SkBits2Float(0x41b6d70a), SkBits2Float(0x427a9168));  // 22.877f, 62.712f, 22.855f, 62.642f, 22.855f, 62.642f

+path.lineTo(SkBits2Float(0x41b6bc6a), SkBits2Float(0x427a645a));  // 22.842f, 62.598f

+path.lineTo(SkBits2Float(0x41b66e97), SkBits2Float(0x427a75c2));  // 22.804f, 62.615f

+path.cubicTo(SkBits2Float(0x41b6872a), SkBits2Float(0x427a71a9), SkBits2Float(0x41b5a9fb), SkBits2Float(0x4279c6a7), SkBits2Float(0x41b5a9fb), SkBits2Float(0x4279c6a7));  // 22.816f, 62.611f, 22.708f, 62.444f, 22.708f, 62.444f

+path.lineTo(SkBits2Float(0x41b59580), SkBits2Float(0x4279b645));  // 22.698f, 62.428f

+path.lineTo(SkBits2Float(0x41b549b9), SkBits2Float(0x42799fbe));  // 22.661f, 62.406f

+path.lineTo(SkBits2Float(0x41b53957), SkBits2Float(0x42799ba5));  // 22.653f, 62.402f

+path.cubicTo(SkBits2Float(0x41b52b01), SkBits2Float(0x42798d4f), SkBits2Float(0x41b4a3d6), SkBits2Float(0x427920c4), SkBits2Float(0x41b4a3d6), SkBits2Float(0x427920c4));  // 22.646f, 62.388f, 22.58f, 62.282f, 22.58f, 62.282f

+path.lineTo(SkBits2Float(0x41b43126), SkBits2Float(0x4278be76));  // 22.524f, 62.186f

+path.lineTo(SkBits2Float(0x41b3ed90), SkBits2Float(0x4278ab01));  // 22.491f, 62.167f

+path.lineTo(SkBits2Float(0x41b3be75), SkBits2Float(0x42789ba5));  // 22.468f, 62.152f

+path.lineTo(SkBits2Float(0x41b3d0e4), SkBits2Float(0x4278b957));  // 22.477f, 62.181f

+path.lineTo(SkBits2Float(0x41b351ea), SkBits2Float(0x42786353));  // 22.415f, 62.097f

+path.lineTo(SkBits2Float(0x41b33957), SkBits2Float(0x42786353));  // 22.403f, 62.097f

+path.cubicTo(SkBits2Float(0x41b326e8), SkBits2Float(0x42785a1c), SkBits2Float(0x41b2fbe6), SkBits2Float(0x427846a7), SkBits2Float(0x41b2fbe6), SkBits2Float(0x427846a7));  // 22.394f, 62.088f, 22.373f, 62.069f, 22.373f, 62.069f

+path.lineTo(SkBits2Float(0x41b2353e), SkBits2Float(0x4277f8d4));  // 22.276f, 61.993f

+path.cubicTo(SkBits2Float(0x41b26040), SkBits2Float(0x42780624), SkBits2Float(0x41b16e96), SkBits2Float(0x4277d0e4), SkBits2Float(0x41b16e96), SkBits2Float(0x4277d0e4));  // 22.297f, 62.006f, 22.179f, 61.954f, 22.179f, 61.954f

+path.cubicTo(SkBits2Float(0x41b16e96), SkBits2Float(0x4277d0e4), SkBits2Float(0x41b10417), SkBits2Float(0x4277c188), SkBits2Float(0x41b0fffe), SkBits2Float(0x4277c188));  // 22.179f, 61.954f, 22.127f, 61.939f, 22.125f, 61.939f

+path.cubicTo(SkBits2Float(0x41b0fffe), SkBits2Float(0x4277bf7c), SkBits2Float(0x41b03f7b), SkBits2Float(0x427778d4), SkBits2Float(0x41b03f7b), SkBits2Float(0x427778d4));  // 22.125f, 61.937f, 22.031f, 61.868f, 22.031f, 61.868f

+path.lineTo(SkBits2Float(0x41ae8729), SkBits2Float(0x4276f7ce));  // 21.816f, 61.742f

+path.cubicTo(SkBits2Float(0x41adb644), SkBits2Float(0x4276d0e5), SkBits2Float(0x41ad22cf), SkBits2Float(0x42768e55), SkBits2Float(0x41ac8729), SkBits2Float(0x427648b3));  // 21.714f, 61.704f, 21.642f, 61.639f, 21.566f, 61.571f

+path.lineTo(SkBits2Float(0x41ab957f), SkBits2Float(0x4275e24d));  // 21.448f, 61.471f

+path.cubicTo(SkBits2Float(0x41aa8f5a), SkBits2Float(0x42757df3), SkBits2Float(0x41a9b644), SkBits2Float(0x42751fbe), SkBits2Float(0x41a8a3d5), SkBits2Float(0x42747fff));  // 21.32f, 61.373f, 21.214f, 61.281f, 21.08f, 61.125f

+path.cubicTo(SkBits2Float(0x41a6d708), SkBits2Float(0x4273a3d6), SkBits2Float(0x41a645a0), SkBits2Float(0x4272dd2e), SkBits2Float(0x41a58935), SkBits2Float(0x4271b126));  // 20.855f, 60.91f, 20.784f, 60.716f, 20.692f, 60.423f

+path.lineTo(SkBits2Float(0x41a5851c), SkBits2Float(0x4271a7ef));  // 20.69f, 60.414f

+path.lineTo(SkBits2Float(0x41a56a7c), SkBits2Float(0x42719687));  // 20.677f, 60.397f

+path.lineTo(SkBits2Float(0x41a54dd0), SkBits2Float(0x4271820c));  // 20.663f, 60.377f

+path.cubicTo(SkBits2Float(0x41a50209), SkBits2Float(0x42711062), SkBits2Float(0x41a4ced6), SkBits2Float(0x42707efa), SkBits2Float(0x41a4be74), SkBits2Float(0x426ff4bc));  // 20.626f, 60.266f, 20.601f, 60.124f, 20.593f, 59.989f

+path.cubicTo(SkBits2Float(0x41a51478), SkBits2Float(0x427073b6), SkBits2Float(0x41a576c6), SkBits2Float(0x42710b43), SkBits2Float(0x41a576c6), SkBits2Float(0x42710b43));  // 20.635f, 60.113f, 20.683f, 60.261f, 20.683f, 60.261f

+path.cubicTo(SkBits2Float(0x41a71478), SkBits2Float(0x42730418), SkBits2Float(0x41a9df39), SkBits2Float(0x42746666), SkBits2Float(0x41adc6a5), SkBits2Float(0x427526e9));  // 20.885f, 60.754f, 21.234f, 61.1f, 21.722f, 61.288f

+path.cubicTo(SkBits2Float(0x41adc499), SkBits2Float(0x427525e3), SkBits2Float(0x41ae47ab), SkBits2Float(0x42754395), SkBits2Float(0x41ae47ab), SkBits2Float(0x42754395));  // 21.721f, 61.287f, 21.785f, 61.316f, 21.785f, 61.316f

+path.lineTo(SkBits2Float(0x41afe55d), SkBits2Float(0x4275978d));  // 21.987f, 61.398f

+path.cubicTo(SkBits2Float(0x41b27cea), SkBits2Float(0x4275e147), SkBits2Float(0x41b54dd0), SkBits2Float(0x4275d916), SkBits2Float(0x41b772ad), SkBits2Float(0x42758106));  // 22.311f, 61.47f, 22.663f, 61.462f, 22.931f, 61.376f

+path.cubicTo(SkBits2Float(0x41b8df38), SkBits2Float(0x42753d70), SkBits2Float(0x41ba1684), SkBits2Float(0x4274d1eb), SkBits2Float(0x41bb4186), SkBits2Float(0x42746979));  // 23.109f, 61.31f, 23.261f, 61.205f, 23.407f, 61.103f

+path.lineTo(SkBits2Float(0x41bdbc67), SkBits2Float(0x4273a1cb));  // 23.717f, 60.908f

+path.cubicTo(SkBits2Float(0x41c0f1a6), SkBits2Float(0x4272cccd), SkBits2Float(0x41c3cabd), SkBits2Float(0x4272b958), SkBits2Float(0x41c71684), SkBits2Float(0x4272a3d7));  // 24.118f, 60.7f, 24.474f, 60.681f, 24.886f, 60.66f

+path.lineTo(SkBits2Float(0x41ca4392), SkBits2Float(0x42728831));  // 25.283f, 60.633f

+path.lineTo(SkBits2Float(0x41def9d8), SkBits2Float(0x42723f7d));  // 27.872f, 60.562f

+path.cubicTo(SkBits2Float(0x41e15a1a), SkBits2Float(0x42722d0e), SkBits2Float(0x41e4105f), SkBits2Float(0x42723333), SkBits2Float(0x41e60e53), SkBits2Float(0x4271c7ae));  // 28.169f, 60.544f, 28.508f, 60.55f, 28.757f, 60.445f

+path.cubicTo(SkBits2Float(0x41e87ceb), SkBits2Float(0x42715810), SkBits2Float(0x41e97ef7), SkBits2Float(0x427077cf), SkBits2Float(0x41ea9165), SkBits2Float(0x426f8a3d));  // 29.061f, 60.336f, 29.187f, 60.117f, 29.321f, 59.885f

+path.lineTo(SkBits2Float(0x41ebccc9), SkBits2Float(0x426e8a3d));  // 29.475f, 59.635f

+path.cubicTo(SkBits2Float(0x41ebced5), SkBits2Float(0x426e8937), SkBits2Float(0x41ec2d0b), SkBits2Float(0x426e4ccc), SkBits2Float(0x41ec2d0b), SkBits2Float(0x426e4ccc));  // 29.476f, 59.634f, 29.522f, 59.575f, 29.522f, 59.575f

+path.lineTo(SkBits2Float(0x41ecae11), SkBits2Float(0x426dde34));  // 29.585f, 59.467f

+path.lineTo(SkBits2Float(0x41ecdf38), SkBits2Float(0x426dde34));  // 29.609f, 59.467f

+path.lineTo(SkBits2Float(0x41ed26e6), SkBits2Float(0x426dc082));  // 29.644f, 59.438f

+path.cubicTo(SkBits2Float(0x41ee1ca9), SkBits2Float(0x426d5a1c), SkBits2Float(0x41eeccc9), SkBits2Float(0x426d1061), SkBits2Float(0x41f01684), SkBits2Float(0x426ce978));  // 29.764f, 59.338f, 29.85f, 59.266f, 30.011f, 59.228f

+path.cubicTo(SkBits2Float(0x41f29fbb), SkBits2Float(0x426c8e55), SkBits2Float(0x420cced8), SkBits2Float(0x426bd4fd), SkBits2Float(0x420e6c8a), SkBits2Float(0x426bdf3b));  // 30.328f, 59.139f, 35.202f, 58.958f, 35.606f, 58.968f

+path.moveTo(SkBits2Float(0x41b60622), SkBits2Float(0x427adb22));  // 22.753f, 62.714f

+path.lineTo(SkBits2Float(0x41b60416), SkBits2Float(0x427ad709));  // 22.752f, 62.71f

+path.cubicTo(SkBits2Float(0x41b60416), SkBits2Float(0x427ad603), SkBits2Float(0x41b60416), SkBits2Float(0x427ad915), SkBits2Float(0x41b60622), SkBits2Float(0x427adb22));  // 22.752f, 62.709f, 22.752f, 62.712f, 22.753f, 62.714f

+path.moveTo(SkBits2Float(0x41bed2ef), SkBits2Float(0x4274cbc6));  // 23.853f, 61.199f

+path.close();

+path.moveTo(SkBits2Float(0x41c04fdd), SkBits2Float(0x42746560));  // 24.039f, 61.099f

+path.close();
+}
+
+static void joel_16(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    make_joel_16(path);
+testSimplify(reporter, path, filename);
+}

+
+static void joel_16x(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    make_joel_16(path);
+testSimplify(reporter, path, filename);
+}
+
+static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
 static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static TestDesc tests[] = {
+    TEST(joel_16x),
+    TEST(joel_16),
+    TEST(joel_15x),
+    TEST(joel_15),
+    TEST(joel_14x),
+    TEST(joel_14),
+    TEST(joel_13x),
+    TEST(joel_13),
+    TEST(joel_12x),
+    TEST(joel_12),
+    TEST(joel_11),
+    TEST(joel_10),
+    TEST(joel_9),
+    TEST(joel_8),
+    TEST(joel_7),
+    TEST(joel_6),
+    TEST(joel_5),
+    TEST(joel_4),
+    TEST(joel_3),
+    TEST(joel_2),
+    TEST(joel_1),
+    TEST(simplifyTest_1),
+    TEST(carsvg_1),
+    TEST(tiger8_393),
+    TEST(bug5169),
+    TEST(testQuads73),
+    TEST(testQuads72),
+    TEST(testQuads71),
+    TEST(testQuads70),
+    TEST(testQuads69),
+    TEST(testQuads68),
+    TEST(testQuads67),
+    TEST(testQuads66),
+    TEST(dean4),
+    TEST(fuzz763_4713_b),
+    TEST(fuzz_twister2),
+    TEST(fuzz_twister),
+    TEST(fuzz994s_3414),
+    TEST(fuzz994s_11),
+    TEST(cr514118),
+    TEST(fuzz864a),
+    TEST(testQuads65),
+    TEST(testIssue3838_3),
+    TEST(testIssue3838),
+    TEST(testArc),
+    TEST(testTriangle2),
+    TEST(testTriangle1),
+    TEST(testQuads64),
+    TEST(testQuads63),
+    TEST(testQuads62),
+    TEST(testRect4),
     TEST(testRect3),
     TEST(testQuadralateral10),
     TEST(testQuads61),
@@ -4738,7 +7791,7 @@
     TEST(testQuadralateral3),
     TEST(testDegenerate5),
     TEST(testQuad12),
-    TEST(testQuadratic51),  // has unorderable angles
+    TEST(testQuadratic51),
     TEST(testQuad8),
     TEST(testQuad11),
     TEST(testQuad10),
@@ -5099,9 +8152,8 @@
 static const size_t testCount = SK_ARRAY_COUNT(tests);
 
 static TestDesc subTests[] = {
-    TEST(testLine3),
-    TEST(testLine2),
-    TEST(testLine1),
+    TEST(fuzz994s_3414),
+    TEST(fuzz994s_11),
 };
 
 static const size_t subTestCount = SK_ARRAY_COUNT(subTests);
@@ -5111,14 +8163,13 @@
 static bool runSubTests = false;
 static bool runSubTestsFirst = false;
 static bool runReverse = false;
-static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 DEF_TEST(PathOpsSimplify, reporter) {
     if (runSubTests && runSubTestsFirst) {
-        RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
+        RunTestSet(reporter, subTests, subTestCount, firstSubTest, nullptr, stopTest, runReverse);
     }
-    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
     if (runSubTests && !runSubTestsFirst) {
-        RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
+        RunTestSet(reporter, subTests, subTestCount, firstSubTest, nullptr, stopTest, runReverse);
     }
 }
diff --git a/src/third_party/skia/tests/PathOpsSimplifyTrianglesThreadedTest.cpp b/src/third_party/skia/tests/PathOpsSimplifyTrianglesThreadedTest.cpp
old mode 100755
new mode 100644
index ee0ca2b..372b667
--- a/src/third_party/skia/tests/PathOpsSimplifyTrianglesThreadedTest.cpp
+++ b/src/third_party/skia/tests/PathOpsSimplifyTrianglesThreadedTest.cpp
@@ -6,15 +6,11 @@
  */
 #include "PathOpsExtendedTest.h"
 #include "PathOpsThreadedCommon.h"
+#include "SkString.h"
 
 static void testSimplifyTrianglesMain(PathOpsThreadState* data) {
     SkASSERT(data);
     PathOpsThreadState& state = *data;
-    char pathStr[1024];
-    bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter?
-    if (progress) {
-        sk_bzero(pathStr, sizeof(pathStr));
-    }
     state.fKey = "?";
     int ax = state.fA & 0x03;
     int ay = state.fA >> 2;
@@ -37,6 +33,7 @@
                 if ((ex - dx) * (fy - dy) == (ey - dy) * (fx - dx)) {
                     continue;
                 }
+                SkString pathStr;
                 SkPath path, out;
                 path.setFillType(SkPath::kWinding_FillType);
                 path.moveTo(SkIntToScalar(ax), SkIntToScalar(ay));
@@ -47,26 +44,25 @@
                 path.lineTo(SkIntToScalar(ex), SkIntToScalar(ey));
                 path.lineTo(SkIntToScalar(fx), SkIntToScalar(fy));
                 path.close();
-                if (progress) {
-                    char* str = pathStr;
-                    str += sprintf(str, "    path.moveTo(%d, %d);\n", ax, ay);
-                    str += sprintf(str, "    path.lineTo(%d, %d);\n", bx, by);
-                    str += sprintf(str, "    path.lineTo(%d, %d);\n", cx, cy);
-                    str += sprintf(str, "    path.close();\n");
-                    str += sprintf(str, "    path.moveTo(%d, %d);\n", dx, dy);
-                    str += sprintf(str, "    path.lineTo(%d, %d);\n", ex, ey);
-                    str += sprintf(str, "    path.lineTo(%d, %d);\n", fx, fy);
-                    str += sprintf(str, "    path.close();\n");
-                    outputProgress(state.fPathStr, pathStr, SkPath::kWinding_FillType);
+                if (state.fReporter->verbose()) {
+                    pathStr.appendf("    path.moveTo(%d, %d);\n", ax, ay);
+                    pathStr.appendf("    path.lineTo(%d, %d);\n", bx, by);
+                    pathStr.appendf("    path.lineTo(%d, %d);\n", cx, cy);
+                    pathStr.appendf("    path.close();\n");
+                    pathStr.appendf("    path.moveTo(%d, %d);\n", dx, dy);
+                    pathStr.appendf("    path.lineTo(%d, %d);\n", ex, ey);
+                    pathStr.appendf("    path.lineTo(%d, %d);\n", fx, fy);
+                    pathStr.appendf("    path.close();\n");
+                    state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType);
                 }
                 ShowTestName(&state, d, e, f, 0);
-                testSimplify(path, false, out, state, pathStr);
+                testSimplify(path, false, out, state, pathStr.c_str());
                 path.setFillType(SkPath::kEvenOdd_FillType);
-                if (progress) {
-                    outputProgress(state.fPathStr, pathStr, SkPath::kEvenOdd_FillType);
+                if (state.fReporter->verbose()) {
+                    state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType);
                 }
                 ShowTestName(&state, d, e, f, 1);
-                testSimplify(path, true, out, state, pathStr);
+                testSimplify(path, true, out, state, pathStr.c_str());
             }
         }
     }
@@ -90,8 +86,8 @@
                 if ((bx - ax) * (cy - ay) == (by - ay) * (cx - ax)) {
                     continue;
                 }
-                *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
-                        (&testSimplifyTrianglesMain, a, b, c, 0, &testRunner));
+                *testRunner.fRunnables.append() = new PathOpsThreadedRunnable(
+                        &testSimplifyTrianglesMain, a, b, c, 0, &testRunner);
             }
             if (!reporter->allowExtendedTest()) goto finish;
         }
diff --git a/src/third_party/skia/tests/PathOpsSkpClipTest.cpp b/src/third_party/skia/tests/PathOpsSkpClipTest.cpp
deleted file mode 100755
index b8142cd..0000000
--- a/src/third_party/skia/tests/PathOpsSkpClipTest.cpp
+++ /dev/null
@@ -1,1105 +0,0 @@
-#include "CrashHandler.h"
-// #include "OverwriteLine.h"
-#include "Resources.h"
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkColor.h"
-#include "SkColorPriv.h"
-#include "SkCommandLineFlags.h"
-#include "SkDevice.h"
-#include "SkForceLinking.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkImageEncoder.h"
-#include "SkOSFile.h"
-#include "SkPathOpsDebug.h"
-#include "SkPicture.h"
-#include "SkRTConf.h"
-#include "SkTSort.h"
-#include "SkStream.h"
-#include "SkString.h"
-#include "SkTArray.h"
-#include "SkTDArray.h"
-#include "SkTaskGroup.h"
-#include "SkTemplates.h"
-#include "SkTime.h"
-
-__SK_FORCE_IMAGE_DECODER_LINKING;
-
-/* add local exceptions here */
-/* TODO : add command flag interface */
-const struct SkipOverTest {
-    int directory;
-    const char* filename;
-    bool blamePathOps;
-} skipOver[] = {
-    { 2, "http___www_groupon_sg_.skp", false},  // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y));
-    { 6, "http___www_googleventures_com_.skp", true},  // addTCoincident SkASSERT(test->fT < 1);
-    { 7, "http___www_foxsports_nl_.skp", true},  // (no repro on mac) addT SkASSERT(this != other || fVerb == SkPath::kCubic_Verb)
-    {13, "http___www_modernqigong_com_.skp", false},  // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y));
-    {14, "http___www_devbridge_com_.skp", true},  // checkSmallCoincidence SkASSERT(!next->fSmall || checkMultiple);
-    {16, "http___www_1023world_net_.skp", false},  // bitmap decode assert (corrupt skp?)
-    {19, "http___www_alamdi_com_.skp", true},  // cubic/quad intersection
-    {26, "http___www_liveencounters_net_.skp", true},  // (no repro on mac) checkSmall addT:549 (line, expects cubic)
-    {28, "http___www_encros_fr_.skp", false},  // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y));
-    {37, "http___www_familysurvivalprotocol_wordpress_com_.skp", true},  // bumpSpan SkASSERT(span->fOppValue >= 0);
-    {39, "http___sufeinet_com_.skp", false}, // bitmap decode assert (corrupt skp?)
-    {41, "http___www_rano360_com_.skp", true}, // checkSmallCoincidence SkASSERT(!next->fSmall || checkMultiple);
-    {44, "http___www_firstunitedbank_com_.skp", true},  // addTCancel SkASSERT(oIndex > 0);
-    {46, "http___www_shinydemos_com_.skp", true},  // addSimpleAngle SkASSERT(index == count() - 2);
-    {48, "http___www_familysurvivalprotocol_com_.skp", true},  // bumpSpan SkASSERT "span->fOppValue >= 0"
-    {57, "http___www_lptemp_com_.skp", true}, // addTCoincident oPeek = &other->fTs[++oPeekIndex];
-    {71, "http___www_1milyonkahraman_org_.skp", true},  // addTCoincident SkASSERT(test->fT < 1);
-    {88, "http___www_apuntesdelechuza_wordpress_com_.skp", true},  // bumpSpan SkASSERT "span->fOppValue >= 0"
-    {89, "http___www_mobilizedconsulting_com_.skp", true}, // addTCancel SkASSERT(oIndex > 0);
-    {93, "http___www_simple_living_in_suffolk_co_uk_.skp", true},  // bumpSpan SkASSERT "span->fOppValue >= 0"
-};
-
-size_t skipOverCount = sizeof(skipOver) / sizeof(skipOver[0]);
-
-
-/* customize file in/out here */
-/* TODO : add command flag interface */
-#define CHROME_VERSION "1e5dfa4-4a995df"
-#define SUMMARY_RUN 1
-
-#ifdef SK_BUILD_FOR_WIN
-    #define DRIVE_SPEC "D:"
-    #define PATH_SLASH "\\"
-#else
-    #define DRIVE_SPEC ""
-    #define PATH_SLASH "/"
-#endif
-
-#define IN_DIR_PRE  DRIVE_SPEC PATH_SLASH "skps"   PATH_SLASH "slave"
-#define OUT_DIR_PRE DRIVE_SPEC PATH_SLASH "skpOut" PATH_SLASH "slave"
-#define OUT_DIR_SUM DRIVE_SPEC PATH_SLASH "skpOut" PATH_SLASH "summary"
-#define DIR_POST               PATH_SLASH "All"    PATH_SLASH CHROME_VERSION
-
-static const char outOpDir[]     = "opClip";
-static const char outOldDir[]    = "oldClip";
-static const char outStatusDir[] = "statusTest";
-
-static SkString get_in_path(int dirNo, const char* filename) {
-    SkString path;
-    SkASSERT(dirNo);
-    path.appendf("%s%d%s", IN_DIR_PRE, dirNo, DIR_POST);
-    if (!sk_exists(path.c_str())) {
-        SkDebugf("could not read %s\n", path.c_str());
-        return SkString();
-    }
-    if (filename) {
-        path.appendf("%s%s", PATH_SLASH, filename);
-        if (!sk_exists(path.c_str())) {
-            SkDebugf("could not read %s\n", path.c_str());
-            return SkString();
-        }
-    }
-    return path;
-}
-
-static void make_recursive_dir(const SkString& path) {
-    if (sk_exists(path.c_str())) {
-        return;
-    }
-    const char* pathStr = path.c_str();
-    int last = (int) path.size();
-    do {
-        while (last > 0 && pathStr[--last] != PATH_SLASH[0])
-            ;
-        SkASSERT(last > 0);
-        SkString shorter(pathStr, last);
-        if (sk_mkdir(shorter.c_str())) {
-            break;
-        }
-    } while (true);
-    do {
-        while (last < (int) path.size() && pathStr[++last] != PATH_SLASH[0])
-            ;
-        SkString shorter(pathStr, last);
-        SkAssertResult(sk_mkdir(shorter.c_str()));
-    } while (last < (int) path.size());
-}
-
-static SkString get_out_path(int dirNo, const char* dirName) {
-    SkString path;
-    SkASSERT(dirNo);
-    SkASSERT(dirName);
-    path.appendf("%s%d%s%s%s", OUT_DIR_PRE, dirNo, DIR_POST, PATH_SLASH, dirName);
-    make_recursive_dir(path);
-    return path;
-}
-
-static SkString get_sum_path(const char* dirName) {
-    SkString path;
-    SkASSERT(dirName);
-    path.appendf("%s%d%s%s", OUT_DIR_SUM, SUMMARY_RUN, PATH_SLASH, dirName);
-    SkDebugf("%s\n", path.c_str());
-    make_recursive_dir(path);
-    return path;
-}
-
-static SkString make_png_name(const char* filename) {
-    SkString pngName = SkString(filename);
-    pngName.remove(pngName.size() - 3, 3);
-    pngName.append("png");
-    return pngName;
-}
-
-////////////////////////////////////////////////////////
-
-enum TestStep {
-    kCompareBits,
-    kEncodeFiles,
-};
-
-enum {
-    kMaxLength = 256,
-    kMaxFiles = 128,
-    kSmallLimit = 1000,
-};
-
-struct TestResult {
-    void init(int dirNo) {
-        fDirNo = dirNo;
-        sk_bzero(fFilename, sizeof(fFilename));
-        fTestStep = kCompareBits;
-        fScale = 1;
-    }
-
-    void init(int dirNo, const SkString& filename) {
-        fDirNo = dirNo;
-        strcpy(fFilename, filename.c_str());
-        fTestStep = kCompareBits;
-        fScale = 1;
-    }
-
-    SkString status() {
-        SkString outStr;
-        outStr.printf("%s %d %d\n", fFilename, fPixelError, fTime);
-        return outStr;
-    }
-
-    SkString progress() {
-        SkString outStr;
-        outStr.printf("dir=%d %s ", fDirNo, fFilename);
-        if (fPixelError) {
-            outStr.appendf(" err=%d", fPixelError);
-        }
-        if (fTime) {
-            outStr.appendf(" time=%d", fTime);
-        }
-        if (fScale != 1) {
-            outStr.appendf(" scale=%d", fScale);
-        }
-        outStr.appendf("\n");
-        return outStr;
-
-    }
-
-    void test(int dirNo, const SkString& filename) {
-        init(dirNo);
-        strcpy(fFilename, filename.c_str());
-        testOne();
-    }
-
-    void testOne();
-
-    char fFilename[kMaxLength];
-    TestStep fTestStep;
-    int fDirNo;
-    int fPixelError;
-    int fTime;
-    int fScale;
-};
-
-class SortByPixel : public TestResult {
-public:
-    bool operator<(const SortByPixel& rh) const {
-        return fPixelError < rh.fPixelError;
-    }
-};
-
-class SortByTime : public TestResult {
-public:
-    bool operator<(const SortByTime& rh) const {
-        return fTime < rh.fTime;
-    }
-};
-
-class SortByName : public TestResult {
-public:
-    bool operator<(const SortByName& rh) const {
-        return strcmp(fFilename, rh.fFilename) < 0;
-    }
-};
-
-struct TestState {
-    void init(int dirNo) {
-        fResult.init(dirNo);
-    }
-
-    SkTDArray<SortByPixel> fPixelWorst;
-    SkTDArray<SortByTime> fSlowest;
-    TestResult fResult;
-};
-
-struct TestRunner {
-    ~TestRunner();
-    void render();
-    SkTDArray<class TestRunnable*> fRunnables;
-};
-
-class TestRunnable : public SkRunnable {
-public:
-    virtual void run() SK_OVERRIDE {
-        SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
-        (*fTestFun)(&fState);
-    }
-
-    TestState fState;
-    void (*fTestFun)(TestState*);
-};
-
-
-class TestRunnableDir : public TestRunnable {
-public:
-    TestRunnableDir(void (*testFun)(TestState*), int dirNo, TestRunner* runner) {
-        fState.init(dirNo);
-        fTestFun = testFun;
-    }
-
-};
-
-class TestRunnableFile : public TestRunnable {
-public:
-    TestRunnableFile(void (*testFun)(TestState*), int dirNo, const char* name, TestRunner* runner) {
-        fState.init(dirNo);
-        strcpy(fState.fResult.fFilename, name);
-        fTestFun = testFun;
-    }
-};
-
-class TestRunnableEncode : public TestRunnableFile {
-public:
-    TestRunnableEncode(void (*testFun)(TestState*), int dirNo, const char* name, TestRunner* runner)
-        : TestRunnableFile(testFun, dirNo, name, runner) {
-        fState.fResult.fTestStep = kEncodeFiles;
-    }
-};
-
-TestRunner::~TestRunner() {
-    for (int index = 0; index < fRunnables.count(); index++) {
-        SkDELETE(fRunnables[index]);
-    }
-}
-
-void TestRunner::render() {
-    SkTaskGroup tg;
-    for (int index = 0; index < fRunnables.count(); ++ index) {
-        tg.add(fRunnables[index]);
-    }
-}
-
-////////////////////////////////////////////////
-
-
-static int similarBits(const SkBitmap& gr, const SkBitmap& sk) {
-    const int kRowCount = 3;
-    const int kThreshold = 3;
-    int width = SkTMin(gr.width(), sk.width());
-    if (width < kRowCount) {
-        return true;
-    }
-    int height = SkTMin(gr.height(), sk.height());
-    if (height < kRowCount) {
-        return true;
-    }
-    int errorTotal = 0;
-    SkTArray<int, true> errorRows;
-    errorRows.push_back_n(width * kRowCount);
-    SkAutoLockPixels autoGr(gr);
-    SkAutoLockPixels autoSk(sk);
-    for (int y = 0; y < height; ++y) {
-        SkPMColor* grRow = gr.getAddr32(0, y);
-        SkPMColor* skRow = sk.getAddr32(0, y);
-        int* base = &errorRows[0];
-        int* cOut = &errorRows[y % kRowCount];
-        for (int x = 0; x < width; ++x) {
-            SkPMColor grColor = grRow[x];
-            SkPMColor skColor = skRow[x];
-            int dr = SkGetPackedR32(grColor) - SkGetPackedR32(skColor);
-            int dg = SkGetPackedG32(grColor) - SkGetPackedG32(skColor);
-            int db = SkGetPackedB32(grColor) - SkGetPackedB32(skColor);
-            int error = cOut[x] = SkTMax(SkAbs32(dr), SkTMax(SkAbs32(dg), SkAbs32(db)));
-            if (error < kThreshold || x < 2) {
-                continue;
-            }
-            if (base[x - 2] < kThreshold
-                    || base[width + x - 2] < kThreshold
-                    || base[width * 2 + x - 2] < kThreshold
-                    || base[x - 1] < kThreshold
-                    || base[width + x - 1] < kThreshold
-                    || base[width * 2 + x - 1] < kThreshold
-                    || base[x] < kThreshold
-                    || base[width + x] < kThreshold
-                    || base[width * 2 + x] < kThreshold) {
-                continue;
-            }
-            errorTotal += error;
-        }
-    }
-    return errorTotal;
-}
-
-static bool addError(TestState* data, const TestResult& testResult) {
-    if (testResult.fPixelError <= 0 && testResult.fTime <= 0) {
-        return false;
-    }
-    int worstCount = data->fPixelWorst.count();
-    int pixelError = testResult.fPixelError;
-    if (pixelError > 0) {
-        for (int index = 0; index < worstCount; ++index) {
-            if (pixelError > data->fPixelWorst[index].fPixelError) {
-                data->fPixelWorst[index] = *(SortByPixel*) &testResult;
-                return true;
-            }
-        }
-    }
-    int slowCount = data->fSlowest.count();
-    int time = testResult.fTime;
-    if (time > 0) {
-        for (int index = 0; index < slowCount; ++index) {
-            if (time > data->fSlowest[index].fTime) {
-                data->fSlowest[index] = *(SortByTime*) &testResult;
-                return true;
-            }
-        }
-    }
-    if (pixelError > 0 && worstCount < kMaxFiles) {
-        *data->fPixelWorst.append() = *(SortByPixel*) &testResult;
-        return true;
-    }
-    if (time > 0 && slowCount < kMaxFiles) {
-        *data->fSlowest.append() = *(SortByTime*) &testResult;
-        return true;
-    }
-    return false;
-}
-
-static SkMSec timePict(SkPicture* pic, SkCanvas* canvas) {
-    canvas->save();
-    SkScalar pWidth = pic->cullRect().width();
-    SkScalar pHeight = pic->cullRect().height();
-    const SkScalar maxDimension = 1000.0f;
-    const int slices = 3;
-    SkScalar xInterval = SkTMax(pWidth - maxDimension, 0.0f) / (slices - 1);
-    SkScalar yInterval = SkTMax(pHeight - maxDimension, 0.0f) / (slices - 1);
-    SkRect rect = {0, 0, SkTMin(maxDimension, pWidth), SkTMin(maxDimension, pHeight) };
-    canvas->clipRect(rect);
-    SkMSec start = SkTime::GetMSecs();
-    for (int x = 0; x < slices; ++x) {
-        for (int y = 0; y < slices; ++y) {
-            pic->playback(canvas);
-            canvas->translate(0, yInterval);
-        }
-        canvas->translate(xInterval, -yInterval * slices);
-    }
-    SkMSec end = SkTime::GetMSecs();
-    canvas->restore();
-    return end - start;
-}
-
-static void drawPict(SkPicture* pic, SkCanvas* canvas, int scale) {
-    canvas->clear(SK_ColorWHITE);
-    if (scale != 1) {
-        canvas->save();
-        canvas->scale(1.0f / scale, 1.0f / scale);
-    }
-    pic->playback(canvas);
-    if (scale != 1) {
-        canvas->restore();
-    }
-}
-
-static void writePict(const SkBitmap& bitmap, const char* outDir, const char* pngName) {
-    SkString outFile = get_sum_path(outDir);
-    outFile.appendf("%s%s", PATH_SLASH, pngName);
-    if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100)) {
-        SkDebugf("unable to encode gr %s (width=%d height=%d)\n", pngName,
-                    bitmap.width(), bitmap.height());
-    }
-}
-
-void TestResult::testOne() {
-    SkPicture* pic = NULL;
-    {
-    #if DEBUG_SHOW_TEST_NAME
-        if (fTestStep == kCompareBits) {
-            SkString testName(fFilename);
-            const char http[] = "http";
-            if (testName.startsWith(http)) {
-                testName.remove(0, sizeof(http) - 1);
-            }
-            while (testName.startsWith("_")) {
-                testName.remove(0, 1);
-            }
-            const char dotSkp[] = ".skp";
-            if (testName.endsWith(dotSkp)) {
-                size_t len = testName.size();
-                testName.remove(len - (sizeof(dotSkp) - 1), sizeof(dotSkp) - 1);
-            }
-            testName.prepend("skp");
-            testName.append("1");
-            strncpy(DEBUG_FILENAME_STRING, testName.c_str(), DEBUG_FILENAME_STRING_LENGTH);
-        } else if (fTestStep == kEncodeFiles) {
-            strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
-        }
-    #endif
-        SkString path = get_in_path(fDirNo, fFilename);
-        SkFILEStream stream(path.c_str());
-        if (!stream.isValid()) {
-            SkDebugf("invalid stream %s\n", path.c_str());
-            goto finish;
-        }
-        pic = SkPicture::CreateFromStream(&stream, &SkImageDecoder::DecodeMemory);
-        if (!pic) {
-            SkDebugf("unable to decode %s\n", fFilename);
-            goto finish;
-        }
-        SkScalar width = pic->cullRect().width();
-        SkScalar height = pic->cullRect().height();
-        SkBitmap oldBitmap, opBitmap;
-        fScale = 1;
-        while (width / fScale > 32767 || height / fScale > 32767) {
-            ++fScale;
-        }
-        do {
-            int dimX = SkScalarCeilToInt(width / fScale);
-            int dimY = SkScalarCeilToInt(height / fScale);
-            if (oldBitmap.tryAllocN32Pixels(dimX, dimY) && opBitmap.tryAllocN32Pixels(dimX, dimY)) {
-                break;
-            }
-            SkDebugf("-%d-", fScale);
-        } while (++fScale < 256);
-        if (fScale >= 256) {
-            SkDebugf("unable to allocate bitmap for %s (w=%f h=%f)\n", fFilename,
-                    width, height);
-            goto finish;
-        }
-        oldBitmap.eraseColor(SK_ColorWHITE);
-        SkCanvas oldCanvas(oldBitmap);
-        oldCanvas.setAllowSimplifyClip(false);
-        opBitmap.eraseColor(SK_ColorWHITE);
-        SkCanvas opCanvas(opBitmap);
-        opCanvas.setAllowSimplifyClip(true);
-        drawPict(pic, &oldCanvas, fScale);
-        drawPict(pic, &opCanvas, fScale);
-        if (fTestStep == kCompareBits) {
-            fPixelError = similarBits(oldBitmap, opBitmap);
-            int oldTime = timePict(pic, &oldCanvas);
-            int opTime = timePict(pic, &opCanvas);
-            fTime = SkTMax(0, oldTime - opTime);
-        } else if (fTestStep == kEncodeFiles) {
-            SkString pngStr = make_png_name(fFilename);
-            const char* pngName = pngStr.c_str();
-            writePict(oldBitmap, outOldDir, pngName);
-            writePict(opBitmap, outOpDir, pngName);
-        }
-    }
-finish:
-    if (pic) {
-        pic->unref();
-    }
-}
-
-DEFINE_string2(match, m, "PathOpsSkpClipThreaded",
-        "[~][^]substring[$] [...] of test name to run.\n"
-        "Multiple matches may be separated by spaces.\n"
-        "~ causes a matching test to always be skipped\n"
-        "^ requires the start of the test to match\n"
-        "$ requires the end of the test to match\n"
-        "^ and $ requires an exact match\n"
-        "If a test does not match any list entry,\n"
-        "it is skipped unless some list entry starts with ~");
-DEFINE_string2(dir, d, NULL, "range of directories (e.g., 1-100)");
-DEFINE_string2(skp, s, NULL, "skp to test");
-DEFINE_bool2(single, z, false, "run tests on a single thread internally.");
-DEFINE_int32(testIndex, 0, "override local test index (PathOpsSkpClipOneOff only).");
-DEFINE_bool2(verbose, v, false, "enable verbose output.");
-
-static bool verbose() {
-    return FLAGS_verbose;
-}
-
-class Dirs {
-public:
-    Dirs() {
-        reset();
-        sk_bzero(fRun, sizeof(fRun));
-        fSet = false;
-    }
-
-    int first() const {
-        int index = 0;
-        while (++index < kMaxDir) {
-            if (fRun[index]) {
-                return index;
-            }
-        }
-        SkASSERT(0);
-        return -1;
-    }
-
-    int last() const {
-        int index = kMaxDir;
-        while (--index > 0 && !fRun[index])
-            ;
-        return index;
-    }
-
-    int next() {
-        while (++fIndex < kMaxDir) {
-            if (fRun[fIndex]) {
-                return fIndex;
-            }
-        }
-        return -1;
-    }
-
-    void reset() {
-        fIndex = -1;
-    }
-
-    void set(int start, int end) {
-        while (start < end) {
-            fRun[start++] = 1;
-        }
-        fSet = true;
-    }
-
-    void setDefault() {
-        if (!fSet) {
-            set(1, 100);
-        }
-    }
-
-private:
-    enum {
-         kMaxDir = 101
-    };
-    char fRun[kMaxDir];
-    int fIndex;
-    bool fSet;
-} gDirs;
-
-class Filenames {
-public:
-    Filenames()
-        : fIndex(-1) {
-    }
-
-    const char* next() {
-        while (fNames && ++fIndex < fNames->count()) {
-            return (*fNames)[fIndex];
-        }
-        return NULL;
-    }
-
-    void set(const SkCommandLineFlags::StringArray& names) {
-        fNames = &names;
-    }
-
-private:
-    int fIndex;
-    const SkCommandLineFlags::StringArray* fNames;
-} gNames;
-
-static bool buildTestDir(int dirNo, int firstDirNo,
-        SkTDArray<TestResult>* tests, SkTDArray<SortByName*>* sorted) {
-    SkString dirName = get_out_path(dirNo, outStatusDir);
-    if (!dirName.size()) {
-        return false;
-    }
-    SkOSFile::Iter iter(dirName.c_str(), "skp");
-    SkString filename;
-    while (iter.next(&filename)) {
-        TestResult test;
-        test.init(dirNo);
-        SkString spaceFile(filename);
-        char* spaces = spaceFile.writable_str();
-        int spaceSize = (int) spaceFile.size();
-        for (int index = 0; index < spaceSize; ++index) {
-            if (spaces[index] == '.') {
-                spaces[index] = ' ';
-            }
-        }
-        int success = sscanf(spaces, "%s %d %d skp", test.fFilename,
-                &test.fPixelError, &test.fTime);
-        if (success < 3) {
-            SkDebugf("failed to scan %s matched=%d\n", filename.c_str(), success);
-            return false;
-        }
-        *tests[dirNo - firstDirNo].append() = test;
-    }
-    if (!sorted) {
-        return true;
-    }
-    SkTDArray<TestResult>& testSet = tests[dirNo - firstDirNo];
-    int count = testSet.count();
-    for (int index = 0; index < count; ++index) {
-        *sorted[dirNo - firstDirNo].append() = (SortByName*) &testSet[index];
-    }
-    if (sorted[dirNo - firstDirNo].count()) {
-        SkTQSort<SortByName>(sorted[dirNo - firstDirNo].begin(),
-                sorted[dirNo - firstDirNo].end() - 1);
-        if (verbose()) {
-            SkDebugf("+");
-        }
-    }
-    return true;
-}
-
-static void testSkpClip(TestState* data) {
-    data->fResult.testOne();
-    SkString statName(data->fResult.fFilename);
-    SkASSERT(statName.endsWith(".skp"));
-    statName.remove(statName.size() - 4, 4);
-    statName.appendf(".%d.%d.skp", data->fResult.fPixelError, data->fResult.fTime);
-    SkString statusFile = get_out_path(data->fResult.fDirNo, outStatusDir);
-    if (!statusFile.size()) {
-        SkDebugf("failed to create %s", statusFile.c_str());
-        return;
-    }
-    statusFile.appendf("%s%s", PATH_SLASH, statName.c_str());
-    SkFILE* file = sk_fopen(statusFile.c_str(), kWrite_SkFILE_Flag);
-    if (!file) {
-            SkDebugf("failed to create %s", statusFile.c_str());
-            return;
-    }
-    sk_fclose(file);
-    if (verbose()) {
-        if (data->fResult.fPixelError || data->fResult.fTime) {
-            SkDebugf("%s", data->fResult.progress().c_str());
-        } else {
-            SkDebugf(".");
-        }
-    }
-}
-
-bool Less(const SortByName& a, const SortByName& b);
-bool Less(const SortByName& a, const SortByName& b) {
-    return a < b;
-}
-
-static bool doOneDir(TestState* state, bool threaded) {
-    int dirNo = state->fResult.fDirNo;
-    SkString dirName = get_in_path(dirNo, NULL);
-    if (!dirName.size()) {
-        return false;
-    }
-    SkTDArray<TestResult> tests[1];
-    SkTDArray<SortByName*> sorted[1];
-    if (!buildTestDir(dirNo, dirNo, tests, sorted)) {
-        return false;
-    }
-    SkOSFile::Iter iter(dirName.c_str(), "skp");
-    SkString filename;
-    while (iter.next(&filename)) {
-        for (size_t index = 0; index < skipOverCount; ++index) {
-            if (skipOver[index].directory == dirNo
-                    && strcmp(filename.c_str(), skipOver[index].filename) == 0) {
-                goto checkEarlyExit;
-            }
-        }
-        {
-            SortByName name;
-            name.init(dirNo);
-            strncpy(name.fFilename, filename.c_str(), filename.size() - 4);  // drop .skp
-            int count = sorted[0].count();
-            int idx = SkTSearch<SortByName, Less>(sorted[0].begin(), count, &name, sizeof(&name));
-            if (idx >= 0) {
-                SortByName* found = sorted[0][idx];
-                (void) addError(state, *found);
-                continue;
-            }
-            TestResult test;
-            test.init(dirNo, filename);
-            state->fResult = test;
-            testSkpClip(state);
-#if 0 // artificially limit to a few while debugging code
-            static int debugLimit = 0;
-            if (++debugLimit == 5) {
-                return true;
-            }
-#endif
-        }
-checkEarlyExit:
-        ;
-    }
-    return true;
-}
-
-static void initTest() {
-#if !defined SK_BUILD_FOR_WIN && !defined SK_BUILD_FOR_MAC
-    SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true);
-    SK_CONF_SET("images.png.suppressDecoderWarnings", true);
-#endif
-}
-
-static void testSkpClipEncode(TestState* data) {
-    data->fResult.testOne();
-    if (verbose()) {
-        SkDebugf("+");
-    }
-}
-
-static void encodeFound(TestState& state) {
-    if (verbose()) {
-        if (state.fPixelWorst.count()) {
-            SkTDArray<SortByPixel*> worst;
-            for (int index = 0; index < state.fPixelWorst.count(); ++index) {
-                *worst.append() = &state.fPixelWorst[index];
-            }
-            SkTQSort<SortByPixel>(worst.begin(), worst.end() - 1);
-            for (int index = 0; index < state.fPixelWorst.count(); ++index) {
-                const TestResult& result = *worst[index];
-                SkDebugf("%d %s pixelError=%d\n", result.fDirNo, result.fFilename, result.fPixelError);
-            }
-        }
-        if (state.fSlowest.count()) {
-            SkTDArray<SortByTime*> slowest;
-            for (int index = 0; index < state.fSlowest.count(); ++index) {
-                *slowest.append() = &state.fSlowest[index];
-            }
-            if (slowest.count() > 0) {
-                SkTQSort<SortByTime>(slowest.begin(), slowest.end() - 1);
-                for (int index = 0; index < slowest.count(); ++index) {
-                    const TestResult& result = *slowest[index];
-                    SkDebugf("%d %s time=%d\n", result.fDirNo, result.fFilename, result.fTime);
-                }
-            }
-        }
-    }
-    TestRunner testRunner;
-    for (int index = 0; index < state.fPixelWorst.count(); ++index) {
-        const TestResult& result = state.fPixelWorst[index];
-        SkString filename(result.fFilename);
-        if (!filename.endsWith(".skp")) {
-            filename.append(".skp");
-        }
-        *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableEncode,
-                (&testSkpClipEncode, result.fDirNo, filename.c_str(), &testRunner));
-    }
-    testRunner.render();
-}
-
-class Test {
-public:
-    Test() {}
-    virtual ~Test() {}
-
-    const char* getName() { onGetName(&fName); return fName.c_str(); }
-    void run() { onRun(); }
-
-protected:
-    virtual void onGetName(SkString*) = 0;
-    virtual void onRun() = 0;
-
-private:
-    SkString    fName;
-};
-
-typedef SkTRegistry<Test*(*)(void*)> TestRegistry;
-
-#define DEF_TEST(name)                                        \
-    static void test_##name();                       \
-    class name##Class : public Test {                                   \
-    public:                                                             \
-        static Test* Factory(void*) { return SkNEW(name##Class); }      \
-    protected:                                                          \
-        virtual void onGetName(SkString* name) SK_OVERRIDE {            \
-            name->set(#name);                                           \
-        }                                                               \
-        virtual void onRun() SK_OVERRIDE { test_##name(); } \
-    };                                                                  \
-    static TestRegistry gReg_##name##Class(name##Class::Factory);       \
-    static void test_##name()
-
-DEF_TEST(PathOpsSkpClip) {
-    gDirs.setDefault();
-    initTest();
-    SkTArray<TestResult, true> errors;
-    TestState state;
-    state.init(0);
-    int dirNo;
-    gDirs.reset();
-    while ((dirNo = gDirs.next()) > 0) {
-        if (verbose()) {
-            SkDebugf("dirNo=%d\n", dirNo);
-        }
-        state.fResult.fDirNo = dirNo;
-        if (!doOneDir(&state, false)) {
-            break;
-        }
-    }
-    encodeFound(state);
-}
-
-static void testSkpClipMain(TestState* data) {
-        (void) doOneDir(data, true);
-}
-
-DEF_TEST(PathOpsSkpClipThreaded) {
-    gDirs.setDefault();
-    initTest();
-    TestRunner testRunner;
-    int dirNo;
-    gDirs.reset();
-    while ((dirNo = gDirs.next()) > 0) {
-        *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableDir,
-                (&testSkpClipMain, dirNo, &testRunner));
-    }
-    testRunner.render();
-    TestState state;
-    state.init(0);
-    gDirs.reset();
-    while ((dirNo = gDirs.next()) > 0) {
-        TestState& testState = testRunner.fRunnables[dirNo - 1]->fState;
-        SkASSERT(testState.fResult.fDirNo == dirNo);
-        for (int inner = 0; inner < testState.fPixelWorst.count(); ++inner) {
-            addError(&state, testState.fPixelWorst[inner]);
-        }
-        for (int inner = 0; inner < testState.fSlowest.count(); ++inner) {
-            addError(&state, testState.fSlowest[inner]);
-        }
-    }
-    encodeFound(state);
-}
-
-static bool buildTests(SkTDArray<TestResult>* tests, SkTDArray<SortByName*>* sorted) {
-    int firstDirNo = gDirs.first();
-    int dirNo;
-    while ((dirNo = gDirs.next()) > 0) {
-        if (!buildTestDir(dirNo, firstDirNo, tests, sorted)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-DEF_TEST(PathOpsSkpClipUberThreaded) {
-    gDirs.setDefault();
-    const int firstDirNo = gDirs.next();
-    const int lastDirNo = gDirs.last();
-    initTest();
-    int dirCount = lastDirNo - firstDirNo + 1;
-    SkAutoTDeleteArray<SkTDArray<TestResult> > tests(new SkTDArray<TestResult>[dirCount]);
-    SkAutoTDeleteArray<SkTDArray<SortByName*> > sorted(new SkTDArray<SortByName*>[dirCount]);
-    if (!buildTests(tests.get(), sorted.get())) {
-        return;
-    }
-    TestRunner testRunner;
-    int dirNo;
-    gDirs.reset();
-    while ((dirNo = gDirs.next()) > 0) {
-        SkString dirName = get_in_path(dirNo, NULL);
-        if (!dirName.size()) {
-            continue;
-        }
-        SkOSFile::Iter iter(dirName.c_str(), "skp");
-        SkString filename;
-        while (iter.next(&filename)) {
-            for (size_t index = 0; index < skipOverCount; ++index) {
-                if (skipOver[index].directory == dirNo
-                        && strcmp(filename.c_str(), skipOver[index].filename) == 0) {
-                    goto checkEarlyExit;
-                }
-            }
-            {
-                SortByName name;
-                name.init(dirNo);
-                strncpy(name.fFilename, filename.c_str(), filename.size() - 4);  // drop .skp
-                int count = sorted.get()[dirNo - firstDirNo].count();
-                if (SkTSearch<SortByName, Less>(sorted.get()[dirNo - firstDirNo].begin(),
-                        count, &name, sizeof(&name)) < 0) {
-                    *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableFile,
-                            (&testSkpClip, dirNo, filename.c_str(), &testRunner));
-                }
-            }
-    checkEarlyExit:
-            ;
-        }
-
-    }
-    testRunner.render();
-    SkAutoTDeleteArray<SkTDArray<TestResult> > results(new SkTDArray<TestResult>[dirCount]);
-    if (!buildTests(results.get(), NULL)) {
-        return;
-    }
-    SkTDArray<TestResult> allResults;
-    for (int dirNo = firstDirNo; dirNo <= lastDirNo; ++dirNo) {
-        SkTDArray<TestResult>& array = results.get()[dirNo - firstDirNo];
-        allResults.append(array.count(), array.begin());
-    }
-    int allCount = allResults.count();
-    SkTDArray<SortByPixel*> pixels;
-    SkTDArray<SortByTime*> times;
-    for (int index = 0; index < allCount; ++index) {
-        *pixels.append() = (SortByPixel*) &allResults[index];
-        *times.append() = (SortByTime*) &allResults[index];
-    }
-    TestState state;
-    if (pixels.count()) {
-        SkTQSort<SortByPixel>(pixels.begin(), pixels.end() - 1);
-        for (int inner = 0; inner < kMaxFiles; ++inner) {
-            *state.fPixelWorst.append() = *pixels[allCount - inner - 1];
-        }
-    }
-    if (times.count()) {
-        SkTQSort<SortByTime>(times.begin(), times.end() - 1);
-        for (int inner = 0; inner < kMaxFiles; ++inner) {
-            *state.fSlowest.append() = *times[allCount - inner - 1];
-        }
-    }
-    encodeFound(state);
-}
-
-DEF_TEST(PathOpsSkpClipOneOff) {
-    const int testIndex = FLAGS_testIndex;
-    int dirNo = gDirs.next();
-    if (dirNo < 0) {
-        dirNo = skipOver[testIndex].directory;
-    }
-    const char* skp = gNames.next();
-    if (!skp) {
-        skp = skipOver[testIndex].filename;
-    }
-    initTest();
-    SkAssertResult(get_in_path(dirNo, skp).size());
-    SkString filename(skp);
-    TestResult state;
-    state.test(dirNo, filename);
-    if (verbose()) {
-        SkDebugf("%s", state.status().c_str());
-    }
-    state.fTestStep = kEncodeFiles;
-    state.testOne();
-}
-
-DEF_TEST(PathOpsTestSkipped) {
-    for (size_t index = 0; index < skipOverCount; ++index) {
-        const SkipOverTest& skip = skipOver[index];
-        if (!skip.blamePathOps) {
-            continue;
-        }
-        int dirNo = skip.directory;
-        const char* skp = skip.filename;
-        initTest();
-        SkAssertResult(get_in_path(dirNo, skp).size());
-        SkString filename(skp);
-        TestResult state;
-        state.test(dirNo, filename);
-        if (verbose()) {
-            SkDebugf("%s", state.status().c_str());
-        }
-        state.fTestStep = kEncodeFiles;
-        state.testOne();
-    }
-}
-
-DEF_TEST(PathOpsCopyFails) {
-    FLAGS_verbose = true;
-    for (size_t index = 0; index < skipOverCount; ++index) {
-        int dirNo = skipOver[index].directory;
-        SkDebugf("mkdir -p " IN_DIR_PRE "%d" DIR_POST "\n", dirNo);
-    }
-    for (size_t index = 0; index < skipOverCount; ++index) {
-        int dirNo = skipOver[index].directory;
-        const char* filename = skipOver[index].filename;
-        SkDebugf("rsync -av cary-linux.cnc:/tera" PATH_SLASH "skps" PATH_SLASH "slave"
-            "%d" DIR_POST "/%s " IN_DIR_PRE "%d" DIR_POST "\n", dirNo, filename, dirNo);
-    }
-}
-
-template TestRegistry* TestRegistry::gHead;
-
-class Iter {
-public:
-    Iter() { this->reset(); }
-    void reset() { fReg = TestRegistry::Head(); }
-
-    Test* next() {
-        if (fReg) {
-            TestRegistry::Factory fact = fReg->factory();
-            fReg = fReg->next();
-            Test* test = fact(NULL);
-            return test;
-        }
-        return NULL;
-    }
-
-private:
-    const TestRegistry* fReg;
-};
-
-int tool_main(int argc, char** argv);
-int tool_main(int argc, char** argv) {
-    SetupCrashHandler();
-    SkCommandLineFlags::SetUsage("");
-    SkCommandLineFlags::Parse(argc, argv);
-    SkGraphics::Init();
-    SkString header("PathOps SkpClip:");
-    if (!FLAGS_match.isEmpty()) {
-        header.appendf(" --match");
-        for (int index = 0; index < FLAGS_match.count(); ++index) {
-            header.appendf(" %s", FLAGS_match[index]);
-        }
-    }
-    if (!FLAGS_dir.isEmpty()) {
-        int count = FLAGS_dir.count();
-        for (int i = 0; i < count; ++i) {
-            const char* range = FLAGS_dir[i];
-            const char* dash = strchr(range, '-');
-            if (!dash) {
-                dash = strchr(range, ',');
-            }
-            int first = atoi(range);
-            int last = dash ? atoi(dash + 1) : first;
-            if (!first || !last) {
-                SkDebugf("couldn't parse --dir %s\n", range);
-                return 1;
-            }
-            gDirs.set(first, last);
-        }
-    }
-    if (!FLAGS_skp.isEmpty()) {
-        gNames.set(FLAGS_skp);
-    }
-#ifdef SK_DEBUG
-    header.append(" SK_DEBUG");
-#else
-    header.append(" SK_RELEASE");
-#endif
-    header.appendf(" skia_arch_width=%d", (int)sizeof(void*) * 8);
-    if (FLAGS_verbose) {
-        header.appendf("\n");
-    }
-    SkDebugf(header.c_str());
-    Iter iter;
-    Test* test;
-    while ((test = iter.next()) != NULL) {
-        SkAutoTDelete<Test> owned(test);
-        if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, test->getName())) {
-            test->run();
-        }
-    }
-    SkGraphics::Term();
-    return 0;
-}
-
-#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
-int main(int argc, char * const argv[]) {
-    return tool_main(argc, (char**) argv);
-}
-#endif
diff --git a/src/third_party/skia/tests/PathOpsSkpTest.cpp b/src/third_party/skia/tests/PathOpsSkpTest.cpp
old mode 100755
new mode 100644
index 32ddf61..5e17422
--- a/src/third_party/skia/tests/PathOpsSkpTest.cpp
+++ b/src/third_party/skia/tests/PathOpsSkpTest.cpp
@@ -8,8 +8,6 @@
 
 #define TEST(name) { name, #name }
 
-#define TEST_NEW_FAILURES 0
-
 static void skpcheeseandburger_com225(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -368,7 +366,7 @@
     pathB.lineTo(716.868225f, 365.046783f);
     pathB.cubicTo(716.868225f, 363.740021f, 716.960083f, 363.043213f, 717.597961f, 362);
     pathB.cubicTo(715.331848f, 363.104095f, 714.19873f, 363.657166f, 711.928711f, 364.782227f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpeverytechpro_blogspot_com100(skiatest::Reporter* reporter, const char* filename) {
@@ -402,7 +400,7 @@
     pathB.lineTo(1075, 628);
     pathB.lineTo(1116.5f, 644.5f);
     pathB.lineTo(1134, 627);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpflite_com41(skiatest::Reporter* reporter, const char* filename) {
@@ -425,7 +423,7 @@
     pathB.lineTo(304.510101f, 438.724121f);
     pathB.lineTo(295.849854f, 433.724121f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpilkoora_com37(skiatest::Reporter* reporter, const char* filename) {
@@ -457,7 +455,7 @@
     pathB.lineTo(1001.5f, 325.5f);
     pathB.lineTo(1001.5f, 782.5f);
     pathB.lineTo(1185, 966);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpmm4everfriends_com43(skiatest::Reporter* reporter, const char* filename) {
@@ -480,7 +478,7 @@
     pathB.lineTo(576.435852f, 247.626068f);
     pathB.lineTo(535.280823f, 235.165573f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpmtrk_uz27(skiatest::Reporter* reporter, const char* filename) {
@@ -507,7 +505,7 @@
     pathB.quadTo(41.7867432f, 802, 37.3919678f, 797.608032f);
     pathB.quadTo(33, 793.213196f, 33, 787);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpfrauen_magazin_com83(skiatest::Reporter* reporter, const char* filename) {
@@ -531,7 +529,7 @@
     pathB.lineTo(803, 891);
     pathB.cubicTo(803, 888.238586f, 805.238586f, 886, 808, 886);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpi_gino_com16(skiatest::Reporter* reporter, const char* filename) {
@@ -556,7 +554,7 @@
     pathB.cubicTo(61, 789.06897f, 116.068977f, 734, 184, 734);
     pathB.lineTo(185, 734);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skppchappy_com_au102(skiatest::Reporter* reporter, const char* filename) {
@@ -584,7 +582,7 @@
     pathB.lineTo(359, 496);
     pathB.cubicTo(359, 494.895416f, 360.34314f, 494, 362, 494);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpsciality_com161(skiatest::Reporter* reporter, const char* filename) {
@@ -612,7 +610,7 @@
     pathB.lineTo(652, 731);
     pathB.cubicTo(652, 729.895447f, 653.34314f, 729, 655, 729);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpsudoestenegocios_com186(skiatest::Reporter* reporter, const char* filename) {
@@ -642,7 +640,7 @@
     pathB.lineTo(24, 471);
     pathB.lineTo(24, 317);
     pathB.lineTo(48, 293);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpthesuburbanite_com213(skiatest::Reporter* reporter, const char* filename) {
@@ -663,7 +661,7 @@
     pathB.lineTo(866.016724f, 701.620361f);
     pathB.lineTo(785.84491f, 723.102356f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skphostloco_com11(skiatest::Reporter* reporter, const char* filename) {
@@ -686,7 +684,7 @@
     pathB.lineTo(30, 648);
     pathB.lineTo(0, 648);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpsergeychunkevich_com8(skiatest::Reporter* reporter, const char* filename) {
@@ -708,7 +706,7 @@
     pathB.lineTo(34, 371);
     pathB.cubicTo(35.6568565f, 371, 37, 372.34314f, 37, 374);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skptracksflow_com9(skiatest::Reporter* reporter, const char* filename) {
@@ -742,7 +740,7 @@
     pathB.cubicTo(26.0091248f, 64.2129364f, 24.2174377f, 66.0046234f, 22.0072803f, 66.0046234f);
     pathB.cubicTo(19.7970943f, 66.0045929f, 18.0054054f, 64.2129059f, 18.0054054f, 62.0027809f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpautobutler_dk29(skiatest::Reporter* reporter, const char* filename) {
@@ -761,7 +759,7 @@
     pathB.lineTo(8.57224448e-15f, 301);
     pathB.lineTo(6.12303177e-17f, 162);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skponlinecollege_org144(skiatest::Reporter* reporter, const char* filename) {
@@ -789,7 +787,7 @@
     pathB.lineTo(177, 410);
     pathB.cubicTo(177, 408.895416f, 177.895432f, 408, 179, 408);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpnational_com_au81(skiatest::Reporter* reporter, const char* filename) {
@@ -815,7 +813,7 @@
     pathB.lineTo(806, 818);
     pathB.cubicTo(806, 817.447693f, 806.447693f, 817, 807, 817);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skprentacheat_com30(skiatest::Reporter* reporter, const char* filename) {
@@ -841,7 +839,7 @@
     pathB.lineTo(966, 264);
     pathB.cubicTo(966, 263.447723f, 966.447693f, 263, 967, 263);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbreakmystyle_com10(skiatest::Reporter* reporter, const char* filename) {
@@ -870,7 +868,7 @@
     pathB.quadTo(231.516815f, -40, 279.258392f, 7.74160004f);
     pathB.quadTo(327, 55.4831848f, 327, 123);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpsd_graphic_net104(skiatest::Reporter* reporter, const char* filename) {
@@ -895,22 +893,9 @@
     pathB.lineTo(390.578583f, 867.014099f);
     pathB.lineTo(433, 852.000061f);
     pathB.lineTo(490.435486f, 879.40741f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-/* this cubic/quad pair
- c = 430,280 430,278.895416 473.876068,278 528,278
- q = 430,280 430.009796,277.101196 458.703552,275.050262
- only intersect at the shared point (430,280)
- they sort backwards because the tangent from pt[0] to control pt[1]
- c' = (0.00000000000000000, -1.1045837402343750)
- q' = (0.0097961425781250000, -2.8988037109375000)
- suggests that the quad is counterclockwise of the cubic, when the reverse is true
- the angle code is fooled because the control pt[1] of both the quad and cubic
- is far away from cubic cntl [2] and quad pt [2].
- Maybe in angle setup, this instability can be detected to suppress sorting on the initial tangent
- Or the error term can be passed to NearRay that is magnified by the distance from the next ctrl?
- */
 static void skpnaoxrane_ru23(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -940,14 +925,9 @@
     pathB.lineTo(430, 280);
     pathB.cubicTo(430, 278.895416f, 473.876068f, 278, 528, 278);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-/* didn't investigate thoroughly, but looks to be missorting quad and cubic
-    {{468.507751,560.724426}, {467.275146,552.856262}, {465.84668,547.288391}}
-    {{463.779907,542.671143}, {464.829529,542.672974}, {466.946289,550.755676}, {468.507751,560.724426}}
-    decision maker is case 14 leftLessThanRight
- */
 static void skptcmevents_org23(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -973,7 +953,7 @@
     pathB.lineTo(325.968597f, 560.475708f);
     pathB.cubicTo(324.407104f, 550.506958f, 341.01001f, 542.456909f, 363.052246f, 542.495361f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpredbullskatearcade_es16(skiatest::Reporter* reporter, const char* filename) {
@@ -1004,7 +984,7 @@
     pathB.lineTo(652.258179f, 468.503662f);
     pathB.cubicTo(652.520996f, 463.401611f, 656.829834f, 459.128235f, 661.882263f, 458.958862f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpfinanzasdigital_com9(skiatest::Reporter* reporter, const char* filename) {
@@ -1030,7 +1010,7 @@
     pathB.lineTo(153, 130);
     pathB.cubicTo(153, 127.790863f, 154.34314f, 126, 156, 126);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skppartainasdemo250_org56(skiatest::Reporter* reporter, const char* filename) {
@@ -1053,7 +1033,7 @@
     pathB.lineTo(206.748749f, 634.748718f);
     pathB.lineTo(182.000015f, 610);
     pathB.lineTo(132.502533f, 610);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpmlk_com326(skiatest::Reporter* reporter, const char* filename) {
@@ -1081,7 +1061,7 @@
     pathB.lineTo(149, 675);
     pathB.cubicTo(149, 672.790833f, 151.238571f, 671, 154, 671);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpcyclist_friends_gr52(skiatest::Reporter* reporter, const char* filename) {
@@ -1107,10 +1087,9 @@
     pathB.cubicTo(52.238575f, 207, 50, 204.761429f, 50, 202);
     pathB.lineTo(50, 183);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-/* cubic ends just above opp line */
 static void skpwww_fj_p_com_22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1127,10 +1106,9 @@
     pathB.lineTo(161, 199);
     pathB.lineTo(223, 199.000015f);
     pathB.lineTo(223, 202);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// pair of lines are not quite coincident, so sorting line/cubic fails (i think)
 static void skpwww_lavoixdunord_fr_11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1160,12 +1138,9 @@
     pathB.lineTo(808, 56);
     pathB.lineTo(935.02002f, 56.0200005f);
     pathB.lineTo(933, 54);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// pair of curves have nearly the same initial tangent but are sorting by
-// that alone sorts them incorrectly. Need to detect that tangents are nearly
-// identical and not reliable by themselves
 static void skppptv_com_62(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1189,10 +1164,9 @@
     pathB.lineTo(169, 5346);
     pathB.cubicTo(169, 5343.79102f, 170.790863f, 5342, 173, 5342);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// nearly identical to lavoixdunord -- to not-quite-coincident lines
 static void skpwww_booking_com_68(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1222,10 +1196,9 @@
     pathB.lineTo(92, 186);
     pathB.lineTo(593.02002f, 186.020004f);
     pathB.lineTo(591, 184);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// visually looks like lavoixdunord and www_booking_com
 static void skpwww_despegar_com_mx_272(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1255,7 +1228,7 @@
     pathB.lineTo(833, 1787);
     pathB.lineTo(832.97998f, 1817.02002f);
     pathB.lineTo(835, 1815);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_joomla_org_23(skiatest::Reporter* reporter, const char* filename) {
@@ -1283,7 +1256,7 @@
     pathB.lineTo(320, 378);
     pathB.lineTo(421, 378.000031f);
     pathB.lineTo(421, 383);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_macrumors_com_131(skiatest::Reporter* reporter, const char* filename) {
@@ -1309,7 +1282,7 @@
     pathB.cubicTo(137.790863f, 14093, 136, 14091.209f, 136, 14089);
     pathB.lineTo(136, 14057);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_leadpages_net_84(skiatest::Reporter* reporter, const char* filename) {
@@ -1330,7 +1303,7 @@
     pathB.lineTo(378.481873f, 5909);
     pathB.lineTo(379.999878f, 5976);
     pathB.lineTo(376, 5976);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_briian_com_34(skiatest::Reporter* reporter, const char* filename) {
@@ -1362,7 +1335,7 @@
     pathB.lineTo(843, 779);
     pathB.lineTo(1196, 779.000061f);
     pathB.lineTo(1196, 784);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_sciality_com_100(skiatest::Reporter* reporter, const char* filename) {
@@ -1390,7 +1363,7 @@
     pathB.cubicTo(158, 469.34314f, 159.34314f, 468, 161, 468);
     pathB.lineTo(275, 468);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_sciality_com_101(skiatest::Reporter* reporter, const char* filename) {
@@ -1418,7 +1391,7 @@
     pathB.lineTo(158, 471);
     pathB.cubicTo(158, 469.895416f, 159.34314f, 469, 161, 469);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_meb_gov_tr_5(skiatest::Reporter* reporter, const char* filename) {
@@ -1441,7 +1414,7 @@
     pathB.lineTo(250, 177);
     pathB.lineTo(135, 177);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_meb_gov_tr_6(skiatest::Reporter* reporter, const char* filename) {
@@ -1467,7 +1440,7 @@
     pathB.lineTo(135, 151);
     pathB.cubicTo(135, 146.581726f, 138.581726f, 143, 143, 143);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpgithub_io_25(skiatest::Reporter* reporter, const char* filename) {
@@ -1499,7 +1472,7 @@
     pathB.lineTo(1003, 18);
     pathB.cubicTo(1003, 16.8954296f, 1003.89545f, 16, 1005, 16);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpgithub_io_26(skiatest::Reporter* reporter, const char* filename) {
@@ -1535,7 +1508,7 @@
     pathB.lineTo(1106, 16);
     pathB.lineTo(1105.97998f, 46.0200005f);
     pathB.lineTo(1108, 44);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpskpicture14(skiatest::Reporter* reporter, const char* filename) {
@@ -1558,7 +1531,7 @@
     pathB.lineTo(323, 193);
     pathB.lineTo(-317, 193);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpskpicture15(skiatest::Reporter* reporter, const char* filename) {
@@ -1582,14 +1555,9 @@
     pathB.lineTo(-317, 168);
     pathB.cubicTo(-317, 166.34314f, -315.65686f, 165, -314, 165);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-/* Three edges are partially coincident. Only one of the three knows about the other two.
-   Subsequently, when the angle loop is created, it misses one of the edges.
-   After coincident edges are processed, probably need a check-and-correct that makes sure the
-   coincidences are all self-consistent.
- */
 static void skpelpais_com_18(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1605,16 +1573,9 @@
     pathB.lineTo(183, 8506.99023f);
     pathB.lineTo(552, 8507);
     pathB.lineTo(552, 8508);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-/* this generates a pair of lines that are essentially coincident; but the next line at a right
-   angle is not treated as if it intersects at the same point.
-   There are several of options:
-     move the intersection of the right angle line to the coincident point (should 'near' do this?
-     construct another coincident pair from the right angle line to the coincident point
-     treat the intersection as simple and not coincident
- */
 static void skpnamecheap_com_405(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1630,10 +1591,9 @@
     pathB.lineTo(141.008835f, 837.9646f);
     pathB.lineTo(141.235291f, 1109.05884f);
     pathB.lineTo(140, 1114);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// fails on angle insert -- haven't investigated yet
 static void skpwww_alrakoba_net_62(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1656,10 +1616,9 @@
     pathB.lineTo(135.962357f, 9800);
     pathB.lineTo(140, 9830);
     pathB.lineTo(132, 9830);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-/* asserts in alignSpanState looks like a coincident related bug */
 static void skpwww_cityads_ru_249(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1693,10 +1652,9 @@
     pathB.lineTo(1000, 13);
     pathB.lineTo(999.969971f, 37.0299988f);
     pathB.lineTo(1003, 34);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// fails on angle insert
 static void skpwww_dealnews_com_315(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1720,10 +1678,9 @@
     pathB.lineTo(967.716675f, 4260);
     pathB.lineTo(970, 4281);
     pathB.lineTo(965, 4281);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// fails in intersections insert
 static void skpwww_inmotionhosting_com_9(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1743,7 +1700,7 @@
     pathB.lineTo(1018.73242f, 1894.26501f);
     pathB.lineTo(963.734985f, 1893.73242f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_alucinados_net_101(skiatest::Reporter* reporter, const char* filename) {
@@ -1765,10 +1722,9 @@
     pathB.lineTo(-43515.8555f, -177415.594f);
     pathB.lineTo(1129.76465f, 1173.05884f);
     pathB.lineTo(1131, 1178);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// /SkOpContour.cpp:278: failed assertion "!approximately_negative(oEndT - oStartT)
 static void skpwww_hairjobsearch_com_31(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1786,10 +1742,9 @@
     pathB.lineTo(144, 0);
     pathB.lineTo(1122, 0);
     pathB.lineTo(1123, 1);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkOpSegment::checkSmallCoincidence; line 1958 SkASSERT(span.fWindValue);
 static void skpwww_heartiste_wordpress_com_86(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1805,7 +1760,7 @@
     pathB.lineTo(741, 9431.99023f);
     pathB.lineTo(761, 9432);
     pathB.lineTo(761, 9433);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_argus_presse_fr_41(skiatest::Reporter* reporter, const char* filename) {
@@ -1824,10 +1779,9 @@
     pathB.lineTo(1000, 364.869904f);
     pathB.lineTo(165, 364.869904f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkOpSegment::checkSmallCoincidence; line 1958 SkASSERT(span.fWindValue);
 static void skpwww_320kbps_net_2231(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1843,10 +1797,9 @@
     pathB.lineTo(838, 9124.99023f);
     pathB.lineTo(862, 9125);
     pathB.lineTo(862, 9126);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// debugValidateLoop loop sum fails
 static void skpwww_exystence_net_61(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1862,10 +1815,9 @@
     pathB.lineTo(143, 9073.99023f);
     pathB.lineTo(316, 9074);
     pathB.lineTo(316, 9075);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// debugValidateLoop loop sum fails
 static void skpwww_trashness_com_36(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1881,10 +1833,9 @@
     pathB.lineTo(91.5f, 4835.99512f);
     pathB.lineTo(541.5f, 4836);
     pathB.lineTo(541.5f, 4836.5f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkIntersections::lineVertical fUsed >= fMax
 static void skpwww_getgold_jp_731(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1903,10 +1854,9 @@
     pathB.lineTo(286.05957f, 10129.8809f);
     pathB.lineTo(285.399994f, 10216.2002f);
     pathB.lineTo(284, 10219);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkOpContour::calcPartialCoincidentWinding SkASSERT(!approximately_negative(endT - startT));
 static void skpwww_maturesupertube_com_21(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1944,10 +1894,9 @@
     pathB.lineTo(4.5f, 11832.5f);
     pathB.lineTo(1260.5f, 11832.5f);
     pathB.lineTo(1263, 11830);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// can't find winding of remaining vertical edges
 static void skpwww_hubbyscook_com_22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1973,7 +1922,7 @@
     pathB.quadTo(1005.02942f, 920, 1001.51471f, 917.071045f);
     pathB.quadTo(998, 914.142151f, 998, 910);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void skpwww_gruposejaumdivulgador_com_br_4(skiatest::Reporter* reporter, const char* filename) {
@@ -1992,10 +1941,9 @@
     pathB.lineTo(1084, 469);
     pathB.lineTo(611, 469);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// asserts in bridgeOp simple->isClosed()
 static void skpwww_phototransferapp_com_24(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2018,7 +1966,7 @@
     pathB.lineTo(1219.10657f, 13);
     pathB.lineTo(80.1065979f, 13);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_phototransferapp_com_24x(skiatest::Reporter* reporter, const char* filename) {
@@ -2043,7 +1991,7 @@
     pathB.lineTo(119.10657f, 13);
     pathB.lineTo(80.1065979f, 13);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_helha_be_109(skiatest::Reporter* reporter, const char* filename) {
@@ -2065,7 +2013,7 @@
     pathB.lineTo(117.686981f, 3339.08423f);
     pathB.lineTo(98.4669647f, 3351.56104f);
     pathB.lineTo(104.291214f, 3359.87891f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_cooksnaps_com_32(skiatest::Reporter* reporter, const char* filename) {
@@ -2117,7 +2065,7 @@
     pathB.quadTo(509.696686f, 241.450104f, 497.29361f, 238.126709f);
     pathB.quadTo(484.890533f, 234.803314f, 478.470215f, 223.683014f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_cooksnaps_com_32a(skiatest::Reporter* reporter, const char* filename) {
@@ -2133,10 +2081,9 @@
     pathB.quadTo(478.196686f, 186.890503f, 489.316986f, 180.4702f);
     pathB.lineTo(490.183014f, 179.9702f);
     pathB.quadTo(501.303345f, 173.549896f, 513.706421f, 176.873276f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// !simple->isClosed()
 static void skpwww_contextualnewsfeeds_com_346(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2157,10 +2104,9 @@
     pathB.lineTo(458.828979f, 1203.67822f);
     pathB.lineTo(465.914215f, 1196.62122f);
     pathB.lineTo(467.32843f, 1198.03552f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// line quad intersection SkIntersections::assert
 static void skpwww_pindosiya_com_99(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2179,10 +2125,9 @@
     pathB.lineTo(901.086914f, 547);
     pathB.lineTo(899, 556);
     pathB.lineTo(898, 556);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkOpAngle::setSector SkASSERT(fSectorStart >= 0);
 static void skpwww_karnivool_com_au_11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2199,7 +2144,7 @@
     pathB.lineTo(427, 1081);
     pathB.lineTo(-3.81469727e-06f, 1081);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_tunero_de_24(skiatest::Reporter* reporter, const char* filename) {
@@ -2238,7 +2183,7 @@
     pathB.quadTo(1013.13599f, 2273, 1012.06104f, 2271.53564f);
     pathB.quadTo(1010.98615f, 2270.07104f, 1011.53705f, 2268);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_docgelo_com_66(skiatest::Reporter* reporter, const char* filename) {
@@ -2256,7 +2201,7 @@
     pathB.lineTo(22.5f, 24174.498f);
     pathB.lineTo(185.5f, 24174.5f);
     pathB.lineTo(185.5f, 24174.75f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_kpopexplorer_net_22(skiatest::Reporter* reporter, const char* filename) {
@@ -2284,7 +2229,7 @@
     pathB.quadTo(1005.02942f, 884, 1001.51471f, 881.071045f);
     pathB.quadTo(998, 878.142151f, 998, 874);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void skpwww_artblart_com_8(skiatest::Reporter* reporter, const char* filename) {
@@ -2302,10 +2247,9 @@
     pathB.lineTo(22.5f, 24527.248f);
     pathB.lineTo(45, 24527.25f);
     pathB.lineTo(45, 24527.5f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// joinCoincidence / findT / assert
 static void skpwww_jessicaslens_wordpress_com_222(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2331,10 +2275,9 @@
     pathB.quadTo(1005.02942f, 862, 1001.51471f, 859.071045f);
     pathB.quadTo(998, 856.142151f, 998, 852);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// joinCoincidence / findT / assert
 static void skpwww_simplysaru_com_40(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2360,10 +2303,9 @@
     pathB.quadTo(1005.02942f, 884, 1001.51471f, 881.071045f);
     pathB.quadTo(998, 878.142151f, 998, 874);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// cubic-cubic intersection reduce checkLinear assert
 static void skpwww_partsdata_de_53(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2659,10 +2601,9 @@
     pathB.cubicTo(631.989807f, 46.6754761f, 632.04364f, 47.0436478f, 632.04364f, 47.4595947f);
     pathB.cubicTo(632.042847f, 47.949852f, 631.916565f, 48.3282623f, 631.656494f, 48.6171875f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkOpAngle::setSector SkASSERT(fSectorStart >= 0);
 static void skpwww_seopack_blogspot_com_2153(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2683,10 +2624,9 @@
     pathB.lineTo(924, 245.472672f);
     pathB.lineTo(1143, 247);
     pathB.lineTo(1143, 248);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// joinCoincidence / findT / assert
 static void skpwww_lokado_de_173(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2720,10 +2660,9 @@
     pathB.quadTo(951.857849f, 916, 948.928955f, 913.071045f);
     pathB.quadTo(946, 910.142151f, 946, 906);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// !simple->isClosed()
 static void skpwww_wartepop_blogspot_com_br_6(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2753,7 +2692,7 @@
     pathB.lineTo(90, 163.666672f);
     pathB.lineTo(90, 163.666672f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void skpwww_wartepop_blogspot_com_br_6a(skiatest::Reporter* reporter, const char* filename) {
@@ -2785,10 +2724,9 @@
     pathB.lineTo(90, 163.666672f);
     pathB.lineTo(90, 163.666672f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// !simple->isClosed()
 static void skpwww_odia_com_br_26(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2833,7 +2771,7 @@
     pathB.lineTo(364.345337f, 754.288269f);
     pathB.lineTo(352.711792f, 751.345337f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_evolvehq_com_210(skiatest::Reporter* reporter, const char* filename) {
@@ -2869,10 +2807,9 @@
     pathB.quadTo(171.17157f, 1174, 170.585785f, 1173.12134f);
     pathB.quadTo(170, 1172.24268f, 170, 1171);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// hangs
 static void skpwww_catingueiraonline_com_352(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2888,10 +2825,9 @@
     pathB.lineTo(444.01001f, 8140);
     pathB.lineTo(444, 8292);
     pathB.lineTo(443, 8292);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// hangs
 static void skpwww_galaxystwo_com_4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2907,7 +2843,7 @@
     pathB.lineTo(10105, 2509.98999f);
     pathB.lineTo(10123, 2510);
     pathB.lineTo(10123, 2511);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_thaienews_blogspot_com_36(skiatest::Reporter* reporter, const char* filename) {
@@ -2925,7 +2861,7 @@
     pathB.lineTo(429.994995f, 2187);
     pathB.lineTo(430, 6268);
     pathB.lineTo(430.5f, 6268);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_fashionscandal_com_94(skiatest::Reporter* reporter, const char* filename) {
@@ -2961,7 +2897,7 @@
     pathB.quadTo(26.2238579f, 418, 26.0285969f, 417.75592f);
     pathB.quadTo(25.833334f, 417.511841f, 25.833334f, 417.166656f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void skpwww_kenlevine_blogspot_com_28(skiatest::Reporter* reporter, const char* filename) {
@@ -2979,7 +2915,7 @@
     pathB.lineTo(277.01001f, 7531);
     pathB.lineTo(277, 9506);
     pathB.lineTo(276, 9506);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_defense_studies_blogspot_com_64(skiatest::Reporter* reporter, const char* filename) {
@@ -2997,7 +2933,7 @@
     pathB.lineTo(277.01001f, 7703);
     pathB.lineTo(277, 9600);
     pathB.lineTo(276, 9600);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_uniquefx_net_442(skiatest::Reporter* reporter, const char* filename) {
@@ -3015,7 +2951,7 @@
     pathB.lineTo(958.997253f, 306.002747f);
     pathB.lineTo(1017, 307);
     pathB.lineTo(1019, 305);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_kitcheninspirations_wordpress_com_32(skiatest::Reporter* reporter, const char* filename) {
@@ -3033,7 +2969,7 @@
     pathB.lineTo(47.1666679f, 19651.332f);
     pathB.lineTo(65.8333359f, 19651.334f);
     pathB.lineTo(65.8333359f, 19651.5f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_educationalcraft_com_4(skiatest::Reporter* reporter, const char* filename) {
@@ -3211,7 +3147,7 @@
     pathB.cubicTo(980.968994f, 1478.18005f, 979.718018f, 1475.66199f, 983.632019f, 1473.87805f);
     pathB.cubicTo(983.632019f, 1473.87805f, 984.229004f, 1477.80103f, 980.968994f, 1478.18005f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_narayana_publishers_com_194(skiatest::Reporter* reporter, const char* filename) {
@@ -3239,7 +3175,7 @@
     pathB.lineTo(1082, 440);
     pathB.lineTo(1090.01001f, 448);
     pathB.lineTo(1081, 448);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_cooksnaps_com_17(skiatest::Reporter* reporter, const char* filename) {
@@ -3288,7 +3224,7 @@
     pathB.quadTo(170.696686f, 241.450104f, 158.293594f, 238.126709f);
     pathB.quadTo(145.890503f, 234.803314f, 139.4702f, 223.683014f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_swapspacesystems_com_5(skiatest::Reporter* reporter, const char* filename) {
@@ -3315,7 +3251,7 @@
     pathB.lineTo(1186.5199f, 5809.85059f);
     pathB.lineTo(811.648376f, 5800.03418f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_kitcheninspirations_wordpress_com_66(skiatest::Reporter* reporter, const char* filename) {
@@ -3333,7 +3269,7 @@
     pathB.lineTo(47.1666679f, 27820.498f);
     pathB.lineTo(60.8333359f, 27820.5f);
     pathB.lineTo(60.8333359f, 27820.668f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_etiqadd_com_2464(skiatest::Reporter* reporter, const char* filename) {
@@ -3360,7 +3296,7 @@
     pathB.lineTo(632.5f, 1309.69238f);
     pathB.lineTo(623.307617f, 1300.5f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_narayana_verlag_de_194(skiatest::Reporter* reporter, const char* filename) {
@@ -3388,7 +3324,7 @@
     pathB.lineTo(1082, 508);
     pathB.lineTo(1090.01001f, 516);
     pathB.lineTo(1081, 516);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_americascup_com_108(skiatest::Reporter* reporter, const char* filename) {
@@ -3411,7 +3347,7 @@
     pathB.lineTo(1002.17114f, 713);
     pathB.lineTo(987.171143f, 713);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_vantageproduction_com_109(skiatest::Reporter* reporter, const char* filename) {
@@ -3439,7 +3375,7 @@
     pathB.lineTo(792, 751);
     pathB.lineTo(804.01001f, 763);
     pathB.lineTo(791, 763);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_aceinfographics_com_106(skiatest::Reporter* reporter, const char* filename) {
@@ -3463,7 +3399,7 @@
     pathB.lineTo(168.020004f, 7635.97998f);
     pathB.lineTo(168, 11578);
     pathB.lineTo(166, 11580);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_tcmevents_org_13(skiatest::Reporter* reporter, const char* filename) {
@@ -3490,7 +3426,7 @@
     pathB.lineTo(468.507751f, 560.724426f);
     pathB.lineTo(325.968597f, 560.475708f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_paseoitaigara_com_br_56(skiatest::Reporter* reporter, const char* filename) {
@@ -3518,7 +3454,7 @@
     pathB.quadTo(634.389832f, 1248.24268f, 634.389832f, 1247);
     pathB.quadTo(634.389832f, 1245.75732f, 635.268494f, 1244.87866f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_mortgagemarketguide_com_109(skiatest::Reporter* reporter, const char* filename) {
@@ -3546,7 +3482,7 @@
     pathB.lineTo(814, 773);
     pathB.lineTo(826.01001f, 785);
     pathB.lineTo(813, 785);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_9to5mac_com_64(skiatest::Reporter* reporter, const char* filename) {
@@ -3568,7 +3504,7 @@
     pathB.lineTo(365.848175f, 5081.15186f);
     pathB.lineTo(368, 5103);
     pathB.lineTo(365, 5106);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_googleventures_com_32(skiatest::Reporter* reporter, const char* filename) {
@@ -3587,10 +3523,9 @@
     pathB.lineTo(738.767395f, 914.088379f);
     pathB.lineTo(713.055908f, 883.446594f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-#if TEST_NEW_FAILURES
 static void skpwww_devbridge_com_22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -3616,10 +3551,9 @@
     pathB.quadTo(4942.75146f, 1523, 4962.375f, 1542.6239f);
     pathB.quadTo(4981.99902f, 1562.24768f, 4981.99902f, 1590);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// cubic/quad intersection
 static void skpwww_alamdi_com_3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -3649,10 +3583,9 @@
     pathB.lineTo(10210, 5318);
     pathB.cubicTo(10210, 5316.34326f, 10211.3428f, 5315, 10213, 5315);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// bumpSpan failed assertion "span->fOppValue >= 0"
 static void skpwww_familysurvivalprotocol_wordpress_com_61(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -3668,9 +3601,8 @@
     pathB.lineTo(143, 14555.9902f);
     pathB.lineTo(165, 14556);
     pathB.lineTo(165, 14557);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
-#endif
 
 static void skpwww_firstunitedbank_com_19(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
@@ -3705,10 +3637,9 @@
     pathB.lineTo(809.5f, 11701.5f);
     pathB.lineTo(1062.91907f, 11687.0811f);
     pathB.lineTo(1047, 11703);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// addSimpleAngle: failed assertion "index == count() - 2"
 static void skpwww_shinydemos_com_5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -3725,10 +3656,9 @@
     pathB.lineTo(545.296204f, 987.615051f);
     pathB.lineTo(205.884949f, 648.203796f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// addTCoincident oPeek = &other->fTs[++oPeekIndex];
 static void skpwww_lptemp_com_3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -3758,11 +3688,9 @@
     pathB.lineTo(77.6666718f, 1396.66675f);
     pathB.cubicTo(77.6666718f, 1394.82568f, 79.15905f, 1393.33337f, 81, 1393.33337f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-#if TEST_NEW_FAILURES
-// SkOpSegment.cpp:3915: failed assertion "otherEnd >= 0"
 static void skpwww_shinydemos_com_15(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -3781,10 +3709,9 @@
     pathB.lineTo(545.296204f, 987.615051f);
     pathB.lineTo(205.884949f, 648.203796f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkOpSegment.cpp:4398: failed assertion "!span->fDone"
 static void skpwww_lptemp_com_5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -3814,22 +3741,917 @@
     pathB.lineTo(77.6666718f, 3153.3335f);
     pathB.cubicTo(77.6666718f, 3151.49268f, 79.15905f, 3150, 81, 3150);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
-#endif
 
+static void skpwww_educationalcraft_com_4a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(941, 1494);
+    path.lineTo(941, 1464);
+    path.lineTo(985, 1464);
+    path.lineTo(985, 1494);
+    path.lineTo(941, 1494);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+
+pathB.moveTo(984.546021f, 1478.31494f);
+pathB.cubicTo(984.546021f, 1478.31494f, 984.543213f, 1478.32239f, 984.537598f, 1478.33655f);
+pathB.cubicTo(984.419006f, 1478.63477f, 983.044373f, 1481.90405f, 980.026001f, 1481.276f);
+pathB.cubicTo(980.026001f, 1481.276f, 980.02594f, 1481.27576f, 980.025879f, 1481.27527f);
+pathB.cubicTo(980.018494f, 1481.22131f, 979.602478f, 1478.38831f, 984.546021f, 1478.31494f);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+
+}
+
+static void skpwww_woothemes_com_1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x44472795), SkBits2Float(0x455cdb8d));  // 796.618f, 3533.72f
+path.lineTo(SkBits2Float(0x44467c27), SkBits2Float(0x455cdb8d));  // 793.94f, 3533.72f
+path.lineTo(SkBits2Float(0x44467c27), SkBits2Float(0x455d055d));  // 793.94f, 3536.34f
+path.lineTo(SkBits2Float(0x44472795), SkBits2Float(0x455d055d));  // 796.618f, 3536.34f
+path.lineTo(SkBits2Float(0x44472795), SkBits2Float(0x455cdb8d));  // 796.618f, 3533.72f
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x4446861c), SkBits2Float(0x455cdb8d));  // 794.095f, 3533.72f
+path.cubicTo(SkBits2Float(0x4446a0d8), SkBits2Float(0x455cefbb), SkBits2Float(0x444727a5), SkBits2Float(0x455d055d), SkBits2Float(0x444727a5), SkBits2Float(0x455d055d));  // 794.513f, 3534.98f, 796.619f, 3536.34f, 796.619f, 3536.34f
+path.cubicTo(SkBits2Float(0x4446c5b0), SkBits2Float(0x455cf8a4), SkBits2Float(0x444693af), SkBits2Float(0x455cedad), SkBits2Float(0x44467c1b), SkBits2Float(0x455ce4b8));  // 795.089f, 3535.54f, 794.308f, 3534.85f, 793.939f, 3534.29f
+path.lineTo(SkBits2Float(0x44467d70), SkBits2Float(0x455ce016));  // 793.96f, 3534.01f
+path.cubicTo(SkBits2Float(0x44467fa9), SkBits2Float(0x455cde82), SkBits2Float(0x444682b5), SkBits2Float(0x455cdd03), SkBits2Float(0x4446861c), SkBits2Float(0x455cdb8d));  // 793.995f, 3533.91f, 794.042f, 3533.81f, 794.095f, 3533.72f
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+static void skpwww_gorcraft_ru_1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x44924000), SkBits2Float(0x458e7800));  // 1170, 4559
+path.conicTo(SkBits2Float(0x44930000), SkBits2Float(0x458e7800), SkBits2Float(0x44930000), SkBits2Float(0x458ea800), SkBits2Float(0x3f3504f3));  // 1176, 4559, 1176, 4565, 0.707107f
+path.lineTo(SkBits2Float(0x44930000), SkBits2Float(0x458f7000));  // 1176, 4590
+path.conicTo(SkBits2Float(0x44930000), SkBits2Float(0x458f9800), SkBits2Float(0x44926000), SkBits2Float(0x458f9800), SkBits2Float(0x3f3504f3));  // 1176, 4595, 1171, 4595, 0.707107f
+path.lineTo(SkBits2Float(0x42a60000), SkBits2Float(0x458f9800));  // 83, 4595
+path.conicTo(SkBits2Float(0x429c0471), SkBits2Float(0x458f9800), SkBits2Float(0x429c0000), SkBits2Float(0x458f700c), SkBits2Float(0x3f352d2d));  // 78.0087f, 4595, 78, 4590.01f, 0.707721f
+path.lineTo(SkBits2Float(0x429c0000), SkBits2Float(0x458ea800));  // 78, 4565
+path.conicTo(SkBits2Float(0x429c0000), SkBits2Float(0x458e7800), SkBits2Float(0x42a80000), SkBits2Float(0x458e7800), SkBits2Float(0x3f3504f3));  // 78, 4559, 84, 4559, 0.707107f
+path.lineTo(SkBits2Float(0x44924000), SkBits2Float(0x458e7800));  // 1170, 4559
+path.close();
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x429c0000), SkBits2Float(0x458f7000));  // 78, 4590
+path.lineTo(SkBits2Float(0x429c0000), SkBits2Float(0x458ea800));  // 78, 4565
+path.conicTo(SkBits2Float(0x429c0000), SkBits2Float(0x458e7800), SkBits2Float(0x42a80000), SkBits2Float(0x458e7800), SkBits2Float(0x3f3504f3));  // 78, 4559, 84, 4559, 0.707107f
+path.lineTo(SkBits2Float(0x431e0000), SkBits2Float(0x458e7800));  // 158, 4559
+path.conicTo(SkBits2Float(0x431e0000), SkBits2Float(0x458e7800), SkBits2Float(0x431e0000), SkBits2Float(0x458e7800), SkBits2Float(0x3f3504f3));  // 158, 4559, 158, 4559, 0.707107f
+path.lineTo(SkBits2Float(0x431e0000), SkBits2Float(0x458fa000));  // 158, 4596
+path.conicTo(SkBits2Float(0x431e0000), SkBits2Float(0x458fa000), SkBits2Float(0x431e0000), SkBits2Float(0x458fa000), SkBits2Float(0x3f3504f3));  // 158, 4596, 158, 4596, 0.707107f
+path.lineTo(SkBits2Float(0x42a80000), SkBits2Float(0x458fa000));  // 84, 4596
+path.conicTo(SkBits2Float(0x429c0000), SkBits2Float(0x458fa000), SkBits2Float(0x429c0000), SkBits2Float(0x458f7000), SkBits2Float(0x3f3504f3));  // 78, 4596, 78, 4590, 0.707107f
+path.close();
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void skpwww_neda_net_1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x447a0000), SkBits2Float(0x00000000));  // 1000, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x44b6e000));  // 0, 1463
+path.lineTo(SkBits2Float(0x447a0000), SkBits2Float(0x44b6e000));  // 1000, 1463
+path.lineTo(SkBits2Float(0x447a0000), SkBits2Float(0x00000000));  // 1000, 0
+path.close();
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x366a410f), SkBits2Float(0x43a38000));  // 3.49066e-06f, 327
+path.lineTo(SkBits2Float(0x447a0000), SkBits2Float(0x43a38001));  // 1000, 327
+path.lineTo(SkBits2Float(0x447a0000), SkBits2Float(0x4435c000));  // 1000, 727
+path.lineTo(SkBits2Float(0xb66a410d), SkBits2Float(0x4435c000));  // -3.49066e-06f, 727
+path.lineTo(SkBits2Float(0x366a410f), SkBits2Float(0x43a38000));  // 3.49066e-06f, 327
+path.close();
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+// "http___www_neda_net.skp" dir=87
+static void skpwww_neda_net_2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x442fc000), SkBits2Float(0x4546a000));  // 703, 3178
+path.lineTo(SkBits2Float(0x441f4000), SkBits2Float(0x4546a000));  // 637, 3178
+path.lineTo(SkBits2Float(0x441f4000), SkBits2Float(0x454ab000));  // 637, 3243
+path.lineTo(SkBits2Float(0x442fc000), SkBits2Float(0x454ab000));  // 703, 3243
+path.lineTo(SkBits2Float(0x442fc000), SkBits2Float(0x4546a000));  // 703, 3178
+path.close();
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x44220e6e), SkBits2Float(0x45469c4c));  // 648.225f, 3177.77f
+path.lineTo(SkBits2Float(0x442fc01c), SkBits2Float(0x45475696));  // 703.002f, 3189.41f
+path.lineTo(SkBits2Float(0x442cf191), SkBits2Float(0x454aa3b5));  // 691.774f, 3242.23f
+path.lineTo(SkBits2Float(0x441f3fe3), SkBits2Float(0x4549e96b));  // 636.998f, 3230.59f
+path.lineTo(SkBits2Float(0x44220e6e), SkBits2Float(0x45469c4c));  // 648.225f, 3177.77f
+path.close();
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+static void skpwww_mybuilder_com_1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.setFillType(SkPath::kEvenOdd_FillType);
+path.moveTo(1000, 659);
+path.lineTo(1000, 377);
+path.lineTo(455, 377);
+path.lineTo(455, 659);
+path.lineTo(1000, 659);
+path.close();
+    SkPath path1(path);
+    path.reset();
+    path.setFillType(SkPath::kEvenOdd_FillType);
+path.moveTo(921.472f, 414.086f);
+path.lineTo(968.815f, 386.754f);
+path.lineTo(993.069f, 428.761f);
+path.lineTo(945.726f, 456.096f);
+path.lineTo(921.471f, 414.086f);
+path.lineTo(921.472f, 414.086f);
+path.close();
+path.moveTo(971.151f, 422.889f);
+path.cubicTo(966.509f, 414.848f, 957.649f, 411.727f, 950.181f, 416.038f);
+path.lineTo(947.224f, 417.746f);
+path.lineTo(946.979f, 417.887f);
+path.lineTo(947.838f, 419.371f);
+path.lineTo(947.844f, 419.367f);
+path.lineTo(947.868f, 419.353f);
+path.lineTo(947.945f, 419.309f);
+path.cubicTo(947.988f, 419.285f, 947.988f, 419.285f, 948.023f, 419.263f);
+path.cubicTo(948.039f, 419.255f, 948.039f, 419.255f, 948.047f, 419.25f);
+path.lineTo(948.052f, 419.247f);
+path.lineTo(947.196f, 417.762f);
+path.lineTo(947.195f, 417.762f);
+path.lineTo(946.888f, 417.939f);
+path.lineTo(943.39f, 419.959f);
+path.lineTo(944.249f, 421.443f);
+path.lineTo(947.745f, 419.424f);
+path.lineTo(948.05f, 419.247f);
+path.lineTo(948.052f, 419.247f);
+path.lineTo(947.195f, 417.763f);
+path.cubicTo(947.193f, 417.763f, 947.193f, 417.763f, 947.19f, 417.766f);
+path.lineTo(947.166f, 417.779f);
+path.lineTo(947.087f, 417.825f);
+path.lineTo(947.011f, 417.868f);
+path.lineTo(946.987f, 417.883f);
+path.lineTo(946.982f, 417.886f);
+path.lineTo(946.98f, 417.886f);
+path.lineTo(947.839f, 419.37f);
+path.lineTo(948.083f, 419.229f);
+path.lineTo(951.039f, 417.522f);
+path.cubicTo(957.631f, 413.716f, 965.471f, 416.477f, 969.669f, 423.746f);
+path.lineTo(971.153f, 422.889f);
+path.lineTo(971.151f, 422.889f);
+path.close();
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
+}
+
+static void skpwww_nimble_com_au_1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.setFillType(SkPath::kEvenOdd_FillType);
+path.moveTo(188.6f, 1988.8f);
+path.lineTo(188.6f, 2041.6f);
+path.cubicTo(188.6f, 2065.4f, 208, 2084.8f, 231.8f, 2084.8f);
+path.cubicTo(255.6f, 2084.8f, 275, 2065.4f, 275, 2041.6f);
+path.lineTo(275.2f, 2041.6f);
+path.lineTo(275.2f, 1988.8f);
+path.lineTo(188.6f, 1988.8f);
+path.close();
+    SkPath path1(path);
+    path.reset();
+path.setFillType(SkPath::kWinding_FillType);
+path.moveTo(275, 2041.6f);
+path.conicTo(275, 2084.8f, 231.8f, 2084.8f, 0.707107f);
+path.conicTo(188.6f, 2084.8f, 188.6f, 2041.6f, 0.707107f);
+path.conicTo(188.6f, 1998.4f, 231.8f, 1998.4f, 0.707107f);
+path.conicTo(275, 1998.4f, 275, 2041.6f, 0.707107f);
+path.close();
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
+}
+
+static void skpwww_tinytots_com_1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.setFillType(SkPath::kEvenOdd_FillType);
+path.moveTo(75.96f, 26.318f);
+path.lineTo(70.337f, 26.318f);
+path.lineTo(70.337f, 32.376f);
+path.lineTo(75.96f, 32.376f);
+path.lineTo(75.96f, 26.318f);
+path.close();
+    SkPath path1(path);
+    path.reset();
+path.setFillType(SkPath::kWinding_FillType);
+path.moveTo(75.88f, 27.873f);
+path.cubicTo(75.929f, 28.138f, 75.956f, 29.196f, 75.96f, 31.046f);
+path.lineTo(72.766f, 32.376f);
+path.cubicTo(72.763f, 30.525f, 72.735f, 29.468f, 72.686f, 29.203f);
+path.cubicTo(72.636f, 28.94f, 72.519f, 28.722f, 72.335f, 28.552f);
+path.cubicTo(72.248f, 28.472f, 72.058f, 28.364f, 71.763f, 28.228f);
+path.cubicTo(72.425f, 27.933f, 72.425f, 27.933f, 73.395f, 27.498f);
+path.cubicTo(72.425f, 27.933f, 72.425f, 27.933f, 71.763f, 28.228f);
+path.cubicTo(71.425f, 28.072f, 70.95f, 27.878f, 70.337f, 27.647f);
+path.lineTo(73.531f, 26.317f);
+path.cubicTo(74.144f, 26.547f, 74.619f, 26.741f, 74.957f, 26.898f);
+path.cubicTo(74.475f, 27.113f, 73.993f, 27.329f, 73.511f, 27.544f);
+path.cubicTo(73.993f, 27.329f, 74.475f, 27.114f, 74.957f, 26.898f);
+path.cubicTo(75.252f, 27.034f, 75.442f, 27.142f, 75.529f, 27.222f);
+path.cubicTo(75.713f, 27.393f, 75.83f, 27.61f, 75.88f, 27.873f);
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
+}
+
+static void http___www_emuleteca_cl_26(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x44370000));  // 1, 732
+path.conicTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000), SkBits2Float(0x42680000), SkBits2Float(0x4428c000), SkBits2Float(0x3f3504f3));  // 1, 675, 58, 675, 0.707107f
+path.conicTo(SkBits2Float(0x40400000), SkBits2Float(0x4428c000), SkBits2Float(0x40400000), SkBits2Float(0x44370000), SkBits2Float(0x3f3504f3));  // 3, 675, 3, 732, 0.707107f
+path.conicTo(SkBits2Float(0x40400000), SkBits2Float(0x44444000), SkBits2Float(0x42680000), SkBits2Float(0x44444000), SkBits2Float(0x3f3504f3));  // 3, 785, 58, 785, 0.707107f
+path.lineTo(SkBits2Float(0x446d0000), SkBits2Float(0x44444000));  // 948, 785
+path.conicTo(SkBits2Float(0x447ac000), SkBits2Float(0x44444000), SkBits2Float(0x447ac000), SkBits2Float(0x44370000), SkBits2Float(0x3f3504f3));  // 1003, 785, 1003, 732, 0.707107f
+path.conicTo(SkBits2Float(0x447ac000), SkBits2Float(0x4428c000), SkBits2Float(0x446d0000), SkBits2Float(0x4428c000), SkBits2Float(0x3f3504f3));  // 1003, 675, 948, 675, 0.707107f
+path.conicTo(SkBits2Float(0x447b4000), SkBits2Float(0x4428c000), SkBits2Float(0x447b4000), SkBits2Float(0x44370000), SkBits2Float(0x3f3504f3));  // 1005, 675, 1005, 732, 0.707107f
+path.conicTo(SkBits2Float(0x447b4000), SkBits2Float(0x44454000), SkBits2Float(0x446d0000), SkBits2Float(0x44454000), SkBits2Float(0x3f3504f3));  // 1005, 789, 948, 789, 0.707107f
+path.lineTo(SkBits2Float(0x42680000), SkBits2Float(0x44454000));  // 58, 789
+path.conicTo(SkBits2Float(0x3f800000), SkBits2Float(0x44454000), SkBits2Float(0x3f800000), SkBits2Float(0x44370000), SkBits2Float(0x3f3504f3));  // 1, 789, 1, 732, 0.707107f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000));  // 1, 675
+path.lineTo(SkBits2Float(0x42680000), SkBits2Float(0x4428c000));  // 58, 675
+path.lineTo(SkBits2Float(0x3fc8f676), SkBits2Float(0x44454000));  // 1.57002f, 789
+path.lineTo(SkBits2Float(0x3f800000), SkBits2Float(0x44454000));  // 1, 789
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+static void http___www_emuleteca_cl_27(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 1);
+path.moveTo(SkBits2Float(0x42680000), SkBits2Float(0x4428c000));  // 58, 675
+path.conicTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000), SkBits2Float(0x3f800000), SkBits2Float(0x44370000), SkBits2Float(0x3f3504f3));  // 1, 675, 1, 732, 0.707107f
+path.conicTo(SkBits2Float(0x3f800000), SkBits2Float(0x443bd045), SkBits2Float(0x414acf56), SkBits2Float(0x443fa420), SkBits2Float(0x3f778612));  // 1, 751.254f, 12.6756f, 766.564f, 0.96689f
+path.lineTo(SkBits2Float(0x41606e3d), SkBits2Float(0x443ef569));  // 14.0269f, 763.835f
+path.conicTo(SkBits2Float(0x40400000), SkBits2Float(0x443b6c34), SkBits2Float(0x40400000), SkBits2Float(0x44370000), SkBits2Float(0x3f77ac46));  // 3, 749.691f, 3, 732, 0.967472f
+path.conicTo(SkBits2Float(0x40400000), SkBits2Float(0x4428c000), SkBits2Float(0x42680000), SkBits2Float(0x4428c000), SkBits2Float(0x3f3504f3));  // 3, 675, 58, 675, 0.707107f
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000));  // 1, 675
+path.lineTo(SkBits2Float(0x4c4a3de6), SkBits2Float(0xccca3d89));  // 5.30165e+07f, -1.06032e+08f
+path.lineTo(SkBits2Float(0x41a71147), SkBits2Float(0x443b4eec));  // 20.8834f, 749.233f
+path.lineTo(SkBits2Float(0x3f800000), SkBits2Float(0x44454000));  // 1, 789
+
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, (SkPathOp) 1, filename);
+}
+
+static void http___www_emuleteca_cl_28(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType(SkPath::kWinding_FillType);
+path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000));  // 1, 675
+path.lineTo(SkBits2Float(0x4c4a3de6), SkBits2Float(0xccca3d89));  // 5.30165e+07f, -1.06032e+08f
+path.lineTo(SkBits2Float(0x41a71147), SkBits2Float(0x443b4eec));  // 20.8834f, 749.233f
+path.lineTo(SkBits2Float(0x3f800000), SkBits2Float(0x44454000));  // 1, 789
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
+}
+
+static void http___www_project2061_org(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+path.moveTo(751, 62);
+path.lineTo(497, 62);
+path.lineTo(497, 138);
+path.lineTo(751, 138);
+path.lineTo(751, 62);
+path.close();
+    SkPath path1(path);
+    path.reset();
+    path.setFillType(SkPath::kWinding_FillType);
+path.moveTo(699.889f, 115.29f);
+path.cubicTo(699.889f, 114.913f, 699.627f, 114.723f, 699.111f, 114.723f);
+path.cubicTo(698.604f, 114.723f, 697.843f, 114.671f, 697.096f, 114.423f);
+path.cubicTo(695.94f, 114.094f, 694.53f, 113.168f, 692.746f, 108.775f);
+path.cubicTo(690.469f, 103.12f, 685.644f, 90.563f, 682.761f, 83.061f);
+path.lineTo(680.541f, 77.301f);
+path.cubicTo(679.927f, 75.721f, 679.67f, 75.261f, 679.151f, 75.261f);
+path.cubicTo(678.636f, 75.261f, 678.392f, 75.73f, 677.759f, 77.464f);
+path.lineTo(665.868f, 109.881f);
+path.cubicTo(664.861f, 112.669f, 663.852f, 114.339f, 661.675f, 114.622f);
+path.cubicTo(661.675f, 114.622f, 661.551f, 114.631f, 661.378f, 114.642f);
+path.cubicTo(661.072f, 114.597f, 660.75f, 114.531f, 660.427f, 114.424f);
+path.cubicTo(659.272f, 114.095f, 657.861f, 113.169f, 656.078f, 108.776f);
+path.cubicTo(653.797f, 103.112f, 648.967f, 90.541f, 646.08f, 83.029f);
+path.lineTo(643.873f, 77.302f);
+path.cubicTo(643.259f, 75.722f, 643.002f, 75.262f, 642.484f, 75.262f);
+path.cubicTo(641.968f, 75.262f, 641.724f, 75.731f, 641.092f, 77.465f);
+path.lineTo(629.2f, 109.879f);
+path.cubicTo(628.193f, 112.667f, 627.184f, 114.337f, 625.007f, 114.62f);
+path.cubicTo(625.007f, 114.62f, 624.884f, 114.629f, 624.71f, 114.64f);
+path.cubicTo(624.404f, 114.595f, 624.081f, 114.529f, 623.759f, 114.422f);
+path.cubicTo(622.603f, 114.093f, 621.193f, 113.167f, 619.409f, 108.774f);
+path.cubicTo(617.132f, 103.119f, 612.307f, 90.562f, 609.424f, 83.06f);
+path.lineTo(607.204f, 77.3f);
+path.cubicTo(606.59f, 75.72f, 606.333f, 75.26f, 605.815f, 75.26f);
+path.cubicTo(605.3f, 75.26f, 605.055f, 75.729f, 604.423f, 77.463f);
+path.lineTo(592.531f, 109.879f);
+path.cubicTo(591.524f, 112.667f, 590.514f, 114.337f, 588.338f, 114.62f);
+path.cubicTo(588.337f, 114.62f, 586.852f, 114.722f, 586.852f, 114.722f);
+path.cubicTo(586.418f, 114.722f, 586.126f, 114.95f, 586.126f, 115.289f);
+path.cubicTo(586.126f, 115.96f, 586.97f, 115.96f, 587.376f, 115.96f);
+path.lineTo(592.101f, 115.843f);
+path.lineTo(593.295f, 115.804f);
+path.lineTo(594.624f, 115.86f);
+path.lineTo(597.381f, 115.961f);
+path.cubicTo(598.241f, 115.961f, 598.422f, 115.596f, 598.422f, 115.29f);
+path.cubicTo(598.422f, 115.078f, 598.321f, 114.723f, 597.643f, 114.723f);
+path.lineTo(597.119f, 114.723f);
+path.cubicTo(596.448f, 114.723f, 595.383f, 114.381f, 595.383f, 113.463f);
+path.cubicTo(595.383f, 112.545f, 595.638f, 111.334f, 596.101f, 110.052f);
+path.cubicTo(596.103f, 110.048f, 599.246f, 100.809f, 599.246f, 100.809f);
+path.cubicTo(599.337f, 100.583f, 599.435f, 100.564f, 599.528f, 100.564f);
+path.lineTo(610.791f, 100.564f);
+path.cubicTo(610.929f, 100.564f, 610.958f, 100.585f, 611.035f, 100.737f);
+path.cubicTo(611.035f, 100.741f, 615.896f, 113.554f, 615.896f, 113.554f);
+path.cubicTo(615.972f, 113.754f, 616.01f, 113.942f, 616.01f, 114.105f);
+path.cubicTo(616.01f, 114.228f, 615.988f, 114.338f, 615.945f, 114.429f);
+path.cubicTo(615.876f, 114.573f, 615.779f, 114.619f, 615.741f, 114.631f);
+path.cubicTo(615.272f, 114.748f, 615.2f, 115.061f, 615.2f, 115.237f);
+path.cubicTo(615.2f, 115.766f, 615.792f, 115.796f, 616.772f, 115.844f);
+path.lineTo(617.012f, 115.857f);
+path.cubicTo(618.978f, 115.913f, 621.359f, 115.948f, 623.835f, 115.958f);
+path.cubicTo(623.912f, 115.961f, 623.984f, 115.961f, 624.045f, 115.961f);
+path.lineTo(624.118f, 115.959f);
+path.cubicTo(624.494f, 115.961f, 624.871f, 115.961f, 625.25f, 115.961f);
+path.cubicTo(625.47f, 115.961f, 625.662f, 115.946f, 625.827f, 115.917f);
+path.lineTo(628.77f, 115.844f);
+path.lineTo(629.965f, 115.805f);
+path.lineTo(631.293f, 115.861f);
+path.lineTo(634.051f, 115.962f);
+path.cubicTo(634.911f, 115.962f, 635.092f, 115.597f, 635.092f, 115.291f);
+path.cubicTo(635.092f, 115.079f, 634.991f, 114.724f, 634.313f, 114.724f);
+path.lineTo(633.789f, 114.724f);
+path.cubicTo(633.118f, 114.724f, 632.053f, 114.382f, 632.053f, 113.464f);
+path.cubicTo(632.053f, 112.546f, 632.308f, 111.335f, 632.771f, 110.053f);
+path.cubicTo(632.773f, 110.049f, 635.916f, 100.81f, 635.916f, 100.81f);
+path.cubicTo(636.007f, 100.584f, 636.105f, 100.565f, 636.198f, 100.565f);
+path.lineTo(647.46f, 100.565f);
+path.cubicTo(647.598f, 100.565f, 647.627f, 100.586f, 647.704f, 100.737f);
+path.cubicTo(647.704f, 100.741f, 652.565f, 113.554f, 652.565f, 113.554f);
+path.cubicTo(652.641f, 113.754f, 652.679f, 113.942f, 652.679f, 114.105f);
+path.cubicTo(652.679f, 114.228f, 652.657f, 114.338f, 652.613f, 114.43f);
+path.cubicTo(652.544f, 114.574f, 652.447f, 114.619f, 652.409f, 114.632f);
+path.cubicTo(651.94f, 114.749f, 651.868f, 115.062f, 651.868f, 115.238f);
+path.cubicTo(651.868f, 115.767f, 652.46f, 115.797f, 653.44f, 115.845f);
+path.lineTo(653.681f, 115.858f);
+path.cubicTo(655.647f, 115.914f, 658.028f, 115.949f, 660.503f, 115.959f);
+path.cubicTo(660.58f, 115.962f, 660.652f, 115.962f, 660.713f, 115.962f);
+path.lineTo(660.787f, 115.96f);
+path.cubicTo(661.162f, 115.962f, 661.54f, 115.962f, 661.918f, 115.962f);
+path.cubicTo(662.139f, 115.962f, 662.33f, 115.947f, 662.496f, 115.918f);
+path.lineTo(665.439f, 115.845f);
+path.lineTo(666.633f, 115.806f);
+path.lineTo(667.962f, 115.862f);
+path.lineTo(670.719f, 115.963f);
+path.cubicTo(671.579f, 115.963f, 671.76f, 115.598f, 671.76f, 115.292f);
+path.cubicTo(671.76f, 115.08f, 671.659f, 114.725f, 670.981f, 114.725f);
+path.lineTo(670.457f, 114.725f);
+path.cubicTo(669.786f, 114.725f, 668.721f, 114.383f, 668.721f, 113.465f);
+path.cubicTo(668.721f, 112.547f, 668.976f, 111.336f, 669.439f, 110.054f);
+path.cubicTo(669.441f, 110.05f, 672.584f, 100.811f, 672.584f, 100.811f);
+path.cubicTo(672.675f, 100.585f, 672.773f, 100.566f, 672.866f, 100.566f);
+path.lineTo(684.128f, 100.566f);
+path.cubicTo(684.266f, 100.566f, 684.295f, 100.587f, 684.372f, 100.739f);
+path.cubicTo(684.372f, 100.743f, 689.233f, 113.556f, 689.233f, 113.556f);
+path.cubicTo(689.309f, 113.756f, 689.347f, 113.944f, 689.347f, 114.107f);
+path.cubicTo(689.347f, 114.23f, 689.325f, 114.34f, 689.281f, 114.431f);
+path.cubicTo(689.212f, 114.575f, 689.115f, 114.621f, 689.077f, 114.633f);
+path.cubicTo(688.608f, 114.75f, 688.536f, 115.063f, 688.536f, 115.239f);
+path.cubicTo(688.536f, 115.768f, 689.128f, 115.798f, 690.108f, 115.846f);
+path.lineTo(690.348f, 115.859f);
+path.cubicTo(692.687f, 115.926f, 695.611f, 115.963f, 698.586f, 115.963f);
+path.cubicTo(699.451f, 115.961f, 699.889f, 115.735f, 699.889f, 115.29f);
+path.close();
+path.moveTo(600.18f, 98.176f);
+path.cubicTo(600.181f, 98.171f, 600.185f, 98.158f, 600.185f, 98.158f);
+path.cubicTo(600.191f, 98.142f, 604.801f, 84.049f, 604.801f, 84.049f);
+path.cubicTo(604.865f, 83.857f, 604.915f, 83.756f, 604.951f, 83.697f);
+path.cubicTo(604.987f, 83.756f, 605.037f, 83.858f, 605.102f, 84.051f);
+path.cubicTo(605.103f, 84.054f, 610.08f, 98.149f, 610.08f, 98.149f);
+path.cubicTo(610.079f, 98.145f, 610.079f, 98.145f, 610.079f, 98.147f);
+path.cubicTo(610.079f, 98.149f, 610.081f, 98.164f, 610.083f, 98.176f);
+path.lineTo(600.18f, 98.176f);
+path.close();
+path.moveTo(636.849f, 98.176f);
+path.cubicTo(636.851f, 98.171f, 636.854f, 98.158f, 636.854f, 98.158f);
+path.cubicTo(636.859f, 98.142f, 641.469f, 84.049f, 641.469f, 84.049f);
+path.cubicTo(641.533f, 83.857f, 641.584f, 83.756f, 641.62f, 83.697f);
+path.cubicTo(641.656f, 83.756f, 641.706f, 83.857f, 641.771f, 84.051f);
+path.cubicTo(641.773f, 84.054f, 646.749f, 98.149f, 646.749f, 98.149f);
+path.cubicTo(646.749f, 98.145f, 646.748f, 98.145f, 646.748f, 98.147f);
+path.cubicTo(646.748f, 98.15f, 646.75f, 98.165f, 646.751f, 98.176f);
+path.lineTo(636.849f, 98.176f);
+path.close();
+path.moveTo(673.517f, 98.176f);
+path.cubicTo(673.519f, 98.171f, 673.522f, 98.158f, 673.522f, 98.158f);
+path.cubicTo(673.528f, 98.142f, 678.138f, 84.049f, 678.138f, 84.049f);
+path.cubicTo(678.202f, 83.857f, 678.252f, 83.756f, 678.288f, 83.697f);
+path.cubicTo(678.324f, 83.756f, 678.375f, 83.858f, 678.439f, 84.051f);
+path.cubicTo(678.44f, 84.054f, 683.417f, 98.149f, 683.417f, 98.149f);
+path.cubicTo(683.416f, 98.145f, 683.416f, 98.145f, 683.416f, 98.147f);
+path.cubicTo(683.416f, 98.149f, 683.418f, 98.164f, 683.42f, 98.176f);
+path.lineTo(673.517f, 98.176f);
+path.close();
+path.moveTo(702.086f, 115.256f);
+path.lineTo(702.089f, 115.257f);
+path.cubicTo(704.075f, 116.223f, 706.408f, 116.692f, 709.22f, 116.692f);
+path.cubicTo(712.384f, 116.692f, 715.17f, 115.918f, 717.275f, 114.454f);
+path.cubicTo(720.639f, 112.074f, 721.837f, 108.31f, 721.837f, 105.581f);
+path.cubicTo(721.837f, 101.538f, 720.57f, 98.203f, 714.092f, 93.115f);
+path.lineTo(712.625f, 91.966f);
+path.cubicTo(707.567f, 87.838f, 706.331f, 86.155f, 706.331f, 83.396f);
+path.cubicTo(706.331f, 79.927f, 708.819f, 77.595f, 712.519f, 77.595f);
+path.cubicTo(716.925f, 77.595f, 718.41f, 79.437f, 718.843f, 80.229f);
+path.cubicTo(719.131f, 80.754f, 719.442f, 82.122f, 719.494f, 82.739f);
+path.cubicTo(719.543f, 83.13f, 719.608f, 83.65f, 720.167f, 83.65f);
+path.cubicTo(720.789f, 83.65f, 720.789f, 82.704f, 720.789f, 82.195f);
+path.cubicTo(720.789f, 79.413f, 720.918f, 77.758f, 720.973f, 77.052f);
+path.lineTo(720.998f, 76.662f);
+path.cubicTo(720.998f, 76.113f, 720.581f, 76.043f, 720.219f, 76.043f);
+path.cubicTo(719.847f, 76.043f, 719.469f, 76.003f, 718.438f, 75.788f);
+path.cubicTo(716.936f, 75.428f, 715.369f, 75.26f, 713.514f, 75.26f);
+path.cubicTo(706.771f, 75.26f, 702.415f, 79.048f, 702.415f, 84.91f);
+path.cubicTo(702.415f, 88.438f, 703.589f, 91.748f, 709.319f, 96.434f);
+path.lineTo(711.676f, 98.365f);
+path.cubicTo(716.332f, 102.196f, 717.607f, 104.172f, 717.607f, 107.564f);
+path.cubicTo(717.607f, 110.888f, 715.121f, 114.253f, 710.371f, 114.253f);
+path.cubicTo(708.428f, 114.253f, 703.808f, 113.754f, 702.868f, 109.132f);
+path.cubicTo(702.716f, 108.424f, 702.716f, 107.718f, 702.716f, 107.25f);
+path.cubicTo(702.716f, 106.94f, 702.716f, 106.422f, 702.094f, 106.422f);
+path.cubicTo(701.519f, 106.422f, 701.455f, 107.116f, 701.421f, 107.488f);
+path.cubicTo(701.421f, 107.495f, 701.375f, 108.117f, 701.375f, 108.117f);
+path.cubicTo(701.293f, 109.171f, 701.158f, 110.933f, 701.158f, 113.044f);
+path.cubicTo(701.16f, 114.432f, 701.197f, 114.813f, 702.086f, 115.256f);
+path.close();
+path.moveTo(501.274f, 129.973f);
+path.lineTo(498.058f, 136.963f);
+path.lineTo(498.62f, 136.963f);
+path.lineTo(499.723f, 134.561f);
+path.lineTo(503.124f, 134.561f);
+path.lineTo(504.227f, 136.963f);
+path.lineTo(504.788f, 136.963f);
+path.lineTo(501.571f, 129.973f);
+path.lineTo(501.274f, 129.973f);
+path.close();
+path.moveTo(499.926f, 134.114f);
+path.lineTo(501.417f, 130.848f);
+path.lineTo(502.918f, 134.114f);
+path.lineTo(499.926f, 134.114f);
+path.close();
+path.moveTo(510.117f, 130.023f);
+path.lineTo(507.677f, 130.023f);
+path.lineTo(507.677f, 136.963f);
+path.lineTo(510.209f, 136.963f);
+path.cubicTo(512.966f, 136.963f, 513.916f, 135.101f, 513.916f, 133.493f);
+path.cubicTo(513.916f, 131.967f, 513.078f, 130.023f, 510.117f, 130.023f);
+path.close();
+path.moveTo(510.178f, 136.515f);
+path.lineTo(508.217f, 136.515f);
+path.lineTo(508.217f, 130.471f);
+path.lineTo(510.147f, 130.471f);
+path.cubicTo(512.036f, 130.471f, 513.333f, 131.712f, 513.333f, 133.493f);
+path.cubicTo(513.333f, 135.447f, 511.853f, 136.515f, 510.178f, 136.515f);
+path.close();
+path.moveTo(519.105f, 135.894f);
+path.lineTo(516.634f, 130.023f);
+path.lineTo(516.093f, 130.023f);
+path.lineTo(519.024f, 137.034f);
+path.lineTo(519.177f, 137.034f);
+path.lineTo(522.098f, 130.023f);
+path.lineTo(521.557f, 130.023f);
+path.lineTo(519.105f, 135.894f);
+path.close();
+path.moveTo(526.817f, 129.973f);
+path.lineTo(523.601f, 136.963f);
+path.lineTo(524.162f, 136.963f);
+path.lineTo(525.265f, 134.561f);
+path.lineTo(528.665f, 134.561f);
+path.lineTo(529.768f, 136.963f);
+path.lineTo(530.33f, 136.963f);
+path.lineTo(527.113f, 129.973f);
+path.lineTo(526.817f, 129.973f);
+path.close();
+path.moveTo(525.469f, 134.114f);
+path.lineTo(526.96f, 130.848f);
+path.lineTo(528.461f, 134.114f);
+path.lineTo(525.469f, 134.114f);
+path.close();
+path.moveTo(538.947f, 136.088f);
+path.lineTo(533.668f, 130.024f);
+path.lineTo(533.208f, 130.024f);
+path.lineTo(533.208f, 136.964f);
+path.lineTo(533.749f, 136.964f);
+path.lineTo(533.749f, 130.899f);
+path.lineTo(539.038f, 136.964f);
+path.lineTo(539.487f, 136.964f);
+path.lineTo(539.487f, 130.024f);
+path.lineTo(538.946f, 130.024f);
+path.lineTo(538.946f, 136.088f);
+path.lineTo(538.947f, 136.088f);
+path.close();
+path.moveTo(543.41f, 133.503f);
+path.cubicTo(543.41f, 131.743f, 544.717f, 130.43f, 546.453f, 130.43f);
+path.cubicTo(547.28f, 130.43f, 548.067f, 130.644f, 548.934f, 131.102f);
+path.lineTo(548.934f, 130.512f);
+path.cubicTo(548.087f, 130.125f, 547.26f, 129.932f, 546.483f, 129.932f);
+path.cubicTo(544.574f, 129.932f, 542.828f, 131.377f, 542.828f, 133.503f);
+path.cubicTo(542.828f, 135.538f, 544.411f, 137.054f, 546.534f, 137.054f);
+path.cubicTo(547.341f, 137.054f, 548.198f, 136.83f, 549.005f, 136.402f);
+path.lineTo(549.005f, 135.843f);
+path.cubicTo(548.055f, 136.341f, 547.31f, 136.555f, 546.523f, 136.555f);
+path.cubicTo(544.707f, 136.556f, 543.41f, 135.294f, 543.41f, 133.503f);
+path.close();
+path.moveTo(552.079f, 136.963f);
+path.lineTo(552.62f, 136.963f);
+path.lineTo(552.62f, 130.023f);
+path.lineTo(552.079f, 130.023f);
+path.lineTo(552.079f, 136.963f);
+path.close();
+path.moveTo(561.984f, 136.088f);
+path.lineTo(556.705f, 130.024f);
+path.lineTo(556.245f, 130.024f);
+path.lineTo(556.245f, 136.964f);
+path.lineTo(556.786f, 136.964f);
+path.lineTo(556.786f, 130.899f);
+path.lineTo(562.075f, 136.964f);
+path.lineTo(562.524f, 136.964f);
+path.lineTo(562.524f, 130.024f);
+path.lineTo(561.983f, 130.024f);
+path.lineTo(561.983f, 136.088f);
+path.lineTo(561.984f, 136.088f);
+path.close();
+path.moveTo(570.122f, 134.257f);
+path.lineTo(571.592f, 134.257f);
+path.lineTo(571.592f, 136.129f);
+path.cubicTo(571.041f, 136.403f, 570.489f, 136.556f, 569.683f, 136.556f);
+path.cubicTo(567.488f, 136.556f, 566.456f, 135.05f, 566.456f, 133.493f);
+path.cubicTo(566.456f, 131.733f, 567.763f, 130.43f, 569.529f, 130.43f);
+path.cubicTo(570.366f, 130.43f, 571.153f, 130.654f, 572, 131.132f);
+path.lineTo(572, 130.522f);
+path.cubicTo(571.02f, 130.084f, 570.397f, 129.932f, 569.61f, 129.932f);
+path.cubicTo(567.455f, 129.932f, 565.873f, 131.438f, 565.873f, 133.473f);
+path.cubicTo(565.873f, 135.529f, 567.496f, 137.055f, 569.661f, 137.055f);
+path.cubicTo(570.468f, 137.055f, 571.305f, 136.862f, 572.132f, 136.465f);
+path.lineTo(572.132f, 133.809f);
+path.lineTo(570.121f, 133.809f);
+path.lineTo(570.121f, 134.257f);
+path.lineTo(570.122f, 134.257f);
+path.close();
+path.moveTo(580.681f, 131.753f);
+path.cubicTo(580.681f, 131.001f, 581.314f, 130.43f, 582.151f, 130.43f);
+path.cubicTo(582.631f, 130.43f, 582.998f, 130.552f, 583.713f, 130.959f);
+path.lineTo(583.713f, 130.37f);
+path.cubicTo(583.172f, 130.075f, 582.641f, 129.933f, 582.099f, 129.933f);
+path.cubicTo(580.996f, 129.933f, 580.098f, 130.747f, 580.098f, 131.775f);
+path.cubicTo(580.098f, 134.065f, 583.417f, 133.2f, 583.417f, 135.173f);
+path.cubicTo(583.417f, 135.956f, 582.774f, 136.557f, 581.947f, 136.557f);
+path.cubicTo(581.406f, 136.557f, 580.966f, 136.374f, 580.191f, 135.814f);
+path.lineTo(580.191f, 136.445f);
+path.cubicTo(580.804f, 136.852f, 581.406f, 137.055f, 581.978f, 137.055f);
+path.cubicTo(583.111f, 137.055f, 584, 136.221f, 584, 135.152f);
+path.cubicTo(584, 132.72f, 580.681f, 133.605f, 580.681f, 131.753f);
+path.close();
+path.moveTo(587.543f, 133.503f);
+path.cubicTo(587.543f, 131.743f, 588.85f, 130.43f, 590.586f, 130.43f);
+path.cubicTo(591.413f, 130.43f, 592.199f, 130.644f, 593.067f, 131.102f);
+path.lineTo(593.067f, 130.512f);
+path.cubicTo(592.22f, 130.125f, 591.393f, 129.932f, 590.616f, 129.932f);
+path.cubicTo(588.707f, 129.932f, 586.96f, 131.377f, 586.96f, 133.503f);
+path.cubicTo(586.96f, 135.538f, 588.543f, 137.054f, 590.667f, 137.054f);
+path.cubicTo(591.473f, 137.054f, 592.331f, 136.83f, 593.138f, 136.402f);
+path.lineTo(593.138f, 135.843f);
+path.cubicTo(592.188f, 136.341f, 591.443f, 136.555f, 590.657f, 136.555f);
+path.cubicTo(588.84f, 136.556f, 587.543f, 135.294f, 587.543f, 133.503f);
+path.close();
+path.moveTo(596.212f, 136.963f);
+path.lineTo(596.753f, 136.963f);
+path.lineTo(596.753f, 130.023f);
+path.lineTo(596.212f, 130.023f);
+path.lineTo(596.212f, 136.963f);
+path.close();
+path.moveTo(600.94f, 133.697f);
+path.lineTo(604.116f, 133.697f);
+path.lineTo(604.116f, 133.25f);
+path.lineTo(600.94f, 133.25f);
+path.lineTo(600.94f, 130.472f);
+path.lineTo(604.198f, 130.472f);
+path.lineTo(604.198f, 130.024f);
+path.lineTo(600.399f, 130.024f);
+path.lineTo(600.399f, 136.964f);
+path.lineTo(604.279f, 136.964f);
+path.lineTo(604.279f, 136.516f);
+path.lineTo(600.94f, 136.516f);
+path.lineTo(600.94f, 133.697f);
+path.close();
+path.moveTo(613.163f, 136.088f);
+path.lineTo(607.884f, 130.024f);
+path.lineTo(607.424f, 130.024f);
+path.lineTo(607.424f, 136.964f);
+path.lineTo(607.965f, 136.964f);
+path.lineTo(607.965f, 130.899f);
+path.lineTo(613.254f, 136.964f);
+path.lineTo(613.703f, 136.964f);
+path.lineTo(613.703f, 130.024f);
+path.lineTo(613.162f, 130.024f);
+path.lineTo(613.162f, 136.088f);
+path.lineTo(613.163f, 136.088f);
+path.close();
+path.moveTo(617.625f, 133.503f);
+path.cubicTo(617.625f, 131.743f, 618.932f, 130.43f, 620.668f, 130.43f);
+path.cubicTo(621.495f, 130.43f, 622.281f, 130.644f, 623.149f, 131.102f);
+path.lineTo(623.149f, 130.512f);
+path.cubicTo(622.301f, 130.125f, 621.474f, 129.932f, 620.698f, 129.932f);
+path.cubicTo(618.789f, 129.932f, 617.042f, 131.377f, 617.042f, 133.503f);
+path.cubicTo(617.042f, 135.538f, 618.625f, 137.054f, 620.749f, 137.054f);
+path.cubicTo(621.556f, 137.054f, 622.413f, 136.83f, 623.22f, 136.402f);
+path.lineTo(623.22f, 135.843f);
+path.cubicTo(622.27f, 136.341f, 621.525f, 136.555f, 620.739f, 136.555f);
+path.cubicTo(618.922f, 136.556f, 617.625f, 135.294f, 617.625f, 133.503f);
+path.close();
+path.moveTo(626.856f, 133.697f);
+path.lineTo(630.032f, 133.697f);
+path.lineTo(630.032f, 133.25f);
+path.lineTo(626.856f, 133.25f);
+path.lineTo(626.856f, 130.472f);
+path.lineTo(630.114f, 130.472f);
+path.lineTo(630.114f, 130.024f);
+path.lineTo(626.316f, 130.024f);
+path.lineTo(626.316f, 136.964f);
+path.lineTo(630.196f, 136.964f);
+path.lineTo(630.196f, 136.516f);
+path.lineTo(626.857f, 136.516f);
+path.lineTo(626.857f, 133.697f);
+path.lineTo(626.856f, 133.697f);
+path.close();
+path.moveTo(633.115f, 136.963f);
+path.lineTo(633.33f, 136.963f);
+path.cubicTo(633.33f, 137.309f, 633.299f, 137.523f, 633.115f, 137.797f);
+path.lineTo(633.115f, 138.154f);
+path.cubicTo(633.565f, 137.828f, 633.779f, 137.269f, 633.779f, 136.75f);
+path.lineTo(633.779f, 135.976f);
+path.lineTo(633.115f, 135.976f);
+path.lineTo(633.115f, 136.963f);
+path.close();
+path.moveTo(641.511f, 131.753f);
+path.cubicTo(641.511f, 131.001f, 642.144f, 130.43f, 642.981f, 130.43f);
+path.cubicTo(643.461f, 130.43f, 643.828f, 130.552f, 644.544f, 130.959f);
+path.lineTo(644.544f, 130.37f);
+path.cubicTo(644.003f, 130.075f, 643.472f, 129.933f, 642.931f, 129.933f);
+path.cubicTo(641.828f, 129.933f, 640.929f, 130.747f, 640.929f, 131.775f);
+path.cubicTo(640.929f, 134.065f, 644.247f, 133.2f, 644.247f, 135.173f);
+path.cubicTo(644.247f, 135.956f, 643.604f, 136.557f, 642.777f, 136.557f);
+path.cubicTo(642.236f, 136.557f, 641.797f, 136.374f, 641.02f, 135.814f);
+path.lineTo(641.02f, 136.445f);
+path.cubicTo(641.633f, 136.852f, 642.235f, 137.055f, 642.807f, 137.055f);
+path.cubicTo(643.941f, 137.055f, 644.829f, 136.221f, 644.829f, 135.152f);
+path.cubicTo(644.829f, 132.72f, 641.511f, 133.605f, 641.511f, 131.753f);
+path.close();
+path.moveTo(648.649f, 133.697f);
+path.lineTo(651.824f, 133.697f);
+path.lineTo(651.824f, 133.25f);
+path.lineTo(648.649f, 133.25f);
+path.lineTo(648.649f, 130.472f);
+path.lineTo(651.906f, 130.472f);
+path.lineTo(651.906f, 130.024f);
+path.lineTo(648.107f, 130.024f);
+path.lineTo(648.107f, 136.964f);
+path.lineTo(651.988f, 136.964f);
+path.lineTo(651.988f, 136.516f);
+path.lineTo(648.649f, 136.516f);
+path.lineTo(648.649f, 133.697f);
+path.close();
+path.moveTo(657.91f, 133.503f);
+path.cubicTo(658.799f, 133.177f, 659.237f, 132.618f, 659.237f, 131.814f);
+path.cubicTo(659.237f, 130.888f, 658.635f, 130.023f, 656.929f, 130.023f);
+path.lineTo(655.142f, 130.023f);
+path.lineTo(655.142f, 136.963f);
+path.lineTo(655.683f, 136.963f);
+path.lineTo(655.683f, 133.635f);
+path.lineTo(656.623f, 133.635f);
+path.cubicTo(657.46f, 133.635f, 657.726f, 133.747f, 658.563f, 135.06f);
+path.lineTo(659.778f, 136.963f);
+path.lineTo(660.411f, 136.963f);
+path.lineTo(658.838f, 134.561f);
+path.cubicTo(658.513f, 134.073f, 658.237f, 133.676f, 657.91f, 133.503f);
+path.close();
+path.moveTo(656.838f, 133.188f);
+path.lineTo(655.684f, 133.188f);
+path.lineTo(655.684f, 130.472f);
+path.lineTo(656.817f, 130.472f);
+path.cubicTo(658.094f, 130.472f, 658.655f, 131.041f, 658.655f, 131.825f);
+path.cubicTo(658.655f, 132.659f, 658.063f, 133.188f, 656.838f, 133.188f);
+path.close();
+path.moveTo(664.968f, 135.894f);
+path.lineTo(662.497f, 130.023f);
+path.lineTo(661.956f, 130.023f);
+path.lineTo(664.887f, 137.034f);
+path.lineTo(665.04f, 137.034f);
+path.lineTo(667.961f, 130.023f);
+path.lineTo(667.42f, 130.023f);
+path.lineTo(664.968f, 135.894f);
+path.close();
+path.moveTo(670.829f, 136.963f);
+path.lineTo(671.37f, 136.963f);
+path.lineTo(671.37f, 130.023f);
+path.lineTo(670.829f, 130.023f);
+path.lineTo(670.829f, 136.963f);
+path.close();
+path.moveTo(680.734f, 136.088f);
+path.lineTo(675.455f, 130.024f);
+path.lineTo(674.995f, 130.024f);
+path.lineTo(674.995f, 136.964f);
+path.lineTo(675.536f, 136.964f);
+path.lineTo(675.536f, 130.899f);
+path.lineTo(680.826f, 136.964f);
+path.lineTo(681.275f, 136.964f);
+path.lineTo(681.275f, 130.024f);
+path.lineTo(680.733f, 130.024f);
+path.lineTo(680.733f, 136.088f);
+path.lineTo(680.734f, 136.088f);
+path.close();
+path.moveTo(688.873f, 134.257f);
+path.lineTo(690.343f, 134.257f);
+path.lineTo(690.343f, 136.129f);
+path.cubicTo(689.791f, 136.403f, 689.24f, 136.556f, 688.433f, 136.556f);
+path.cubicTo(686.238f, 136.556f, 685.206f, 135.05f, 685.206f, 133.493f);
+path.cubicTo(685.206f, 131.733f, 686.514f, 130.43f, 688.28f, 130.43f);
+path.cubicTo(689.117f, 130.43f, 689.903f, 130.654f, 690.751f, 131.132f);
+path.lineTo(690.751f, 130.522f);
+path.cubicTo(689.771f, 130.084f, 689.148f, 129.932f, 688.362f, 129.932f);
+path.cubicTo(686.207f, 129.932f, 684.624f, 131.438f, 684.624f, 133.473f);
+path.cubicTo(684.624f, 135.529f, 686.247f, 137.055f, 688.413f, 137.055f);
+path.cubicTo(689.219f, 137.055f, 690.057f, 136.862f, 690.884f, 136.465f);
+path.lineTo(690.884f, 133.809f);
+path.lineTo(688.873f, 133.809f);
+path.lineTo(688.873f, 134.257f);
+path.close();
+path.moveTo(699.432f, 131.753f);
+path.cubicTo(699.432f, 131.001f, 700.065f, 130.43f, 700.902f, 130.43f);
+path.cubicTo(701.381f, 130.43f, 701.749f, 130.552f, 702.464f, 130.959f);
+path.lineTo(702.464f, 130.37f);
+path.cubicTo(701.923f, 130.075f, 701.391f, 129.933f, 700.85f, 129.933f);
+path.cubicTo(699.747f, 129.933f, 698.849f, 130.747f, 698.849f, 131.775f);
+path.cubicTo(698.849f, 134.065f, 702.168f, 133.2f, 702.168f, 135.173f);
+path.cubicTo(702.168f, 135.956f, 701.525f, 136.557f, 700.698f, 136.557f);
+path.cubicTo(700.156f, 136.557f, 699.718f, 136.374f, 698.941f, 135.814f);
+path.lineTo(698.941f, 136.445f);
+path.cubicTo(699.553f, 136.852f, 700.156f, 137.055f, 700.728f, 137.055f);
+path.cubicTo(701.862f, 137.055f, 702.75f, 136.221f, 702.75f, 135.152f);
+path.cubicTo(702.75f, 132.72f, 699.432f, 133.605f, 699.432f, 131.753f);
+path.close();
+path.moveTo(709.407f, 129.932f);
+path.cubicTo(707.345f, 129.932f, 705.731f, 131.499f, 705.731f, 133.493f);
+path.cubicTo(705.731f, 135.488f, 707.345f, 137.054f, 709.407f, 137.054f);
+path.cubicTo(711.48f, 137.054f, 713.104f, 135.487f, 713.104f, 133.493f);
+path.cubicTo(713.104f, 131.499f, 711.481f, 129.932f, 709.407f, 129.932f);
+path.close();
+path.moveTo(709.428f, 136.556f);
+path.cubicTo(707.683f, 136.556f, 706.314f, 135.223f, 706.314f, 133.493f);
+path.cubicTo(706.314f, 131.763f, 707.682f, 130.43f, 709.428f, 130.43f);
+path.cubicTo(711.175f, 130.43f, 712.523f, 131.763f, 712.523f, 133.493f);
+path.cubicTo(712.522f, 135.223f, 711.175f, 136.556f, 709.428f, 136.556f);
+path.close();
+path.moveTo(716.739f, 133.503f);
+path.cubicTo(716.739f, 131.743f, 718.046f, 130.43f, 719.782f, 130.43f);
+path.cubicTo(720.609f, 130.43f, 721.395f, 130.644f, 722.264f, 131.102f);
+path.lineTo(722.264f, 130.512f);
+path.cubicTo(721.416f, 130.125f, 720.589f, 129.932f, 719.813f, 129.932f);
+path.cubicTo(717.903f, 129.932f, 716.157f, 131.377f, 716.157f, 133.503f);
+path.cubicTo(716.157f, 135.538f, 717.74f, 137.054f, 719.863f, 137.054f);
+path.cubicTo(720.67f, 137.054f, 721.528f, 136.83f, 722.335f, 136.402f);
+path.lineTo(722.335f, 135.843f);
+path.cubicTo(721.385f, 136.341f, 720.64f, 136.555f, 719.854f, 136.555f);
+path.cubicTo(718.037f, 136.556f, 716.739f, 135.294f, 716.739f, 133.503f);
+path.close();
+path.moveTo(725.409f, 136.963f);
+path.lineTo(725.95f, 136.963f);
+path.lineTo(725.95f, 130.023f);
+path.lineTo(725.409f, 130.023f);
+path.lineTo(725.409f, 136.963f);
+path.close();
+path.moveTo(730.136f, 133.697f);
+path.lineTo(733.312f, 133.697f);
+path.lineTo(733.312f, 133.25f);
+path.lineTo(730.136f, 133.25f);
+path.lineTo(730.136f, 130.472f);
+path.lineTo(733.393f, 130.472f);
+path.lineTo(733.393f, 130.024f);
+path.lineTo(729.595f, 130.024f);
+path.lineTo(729.595f, 136.964f);
+path.lineTo(733.475f, 136.964f);
+path.lineTo(733.475f, 136.516f);
+path.lineTo(730.136f, 136.516f);
+path.lineTo(730.136f, 133.697f);
+path.close();
+path.moveTo(735.865f, 130.472f);
+path.lineTo(738.479f, 130.472f);
+path.lineTo(738.479f, 136.964f);
+path.lineTo(739.02f, 136.964f);
+path.lineTo(739.02f, 130.472f);
+path.lineTo(741.634f, 130.472f);
+path.lineTo(741.634f, 130.024f);
+path.lineTo(735.865f, 130.024f);
+path.lineTo(735.865f, 130.472f);
+path.close();
+path.moveTo(749.017f, 130.023f);
+path.lineTo(746.699f, 132.771f);
+path.lineTo(744.371f, 130.023f);
+path.lineTo(743.717f, 130.023f);
+path.lineTo(746.423f, 133.239f);
+path.lineTo(746.423f, 136.963f);
+path.lineTo(746.964f, 136.963f);
+path.lineTo(746.964f, 133.239f);
+path.lineTo(749.67f, 130.023f);
+path.lineTo(749.017f, 130.023f);
+path.close();
+path.moveTo(578.773f, 115.549f);
+path.lineTo(578.773f, 62.773f);
+path.lineTo(557.571f, 62.773f);
+path.cubicTo(562.752f, 67.658f, 575.797f, 106.652f, 578.773f, 115.549f);
+path.close();
+path.moveTo(544.754f, 62.861f);
+path.lineTo(524.496f, 62.861f);
+path.lineTo(524.496f, 115.55f);
+path.lineTo(524.934f, 115.55f);
+path.lineTo(544.544f, 64.169f);
+path.lineTo(528.157f, 115.549f);
+path.lineTo(528.682f, 115.549f);
+path.lineTo(546.557f, 67.658f);
+path.lineTo(531.675f, 115.548f);
+path.lineTo(532.376f, 115.548f);
+path.lineTo(548.57f, 71.846f);
+path.lineTo(535.293f, 115.507f);
+path.lineTo(535.906f, 115.507f);
+path.lineTo(544.281f, 97.58f);
+path.lineTo(542.267f, 97.58f);
+path.lineTo(550.409f, 76.033f);
+path.lineTo(544.281f, 97.578f);
+path.lineTo(546.119f, 97.578f);
+path.lineTo(538.678f, 115.551f);
+path.lineTo(565.029f, 115.551f);
+path.cubicTo(562.052f, 106.477f, 550.759f, 67.92f, 544.754f, 62.861f);
+path.close();
+    SkPath path2(path);
+    testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
+}
+
+static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
 static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static struct TestDesc tests[] = {
+    TEST(skpwww_gorcraft_ru_1),
+    TEST(http___www_project2061_org),
+    TEST(http___www_emuleteca_cl_27),
+    TEST(http___www_emuleteca_cl_26),
+    TEST(http___www_emuleteca_cl_28),
+    TEST(skpwww_nimble_com_au_1),
+    TEST(skpwww_mybuilder_com_1),
+    TEST(skpwww_neda_net_2),
+    TEST(skpwww_woothemes_com_1),
+    TEST(skpwww_neda_net_1),
+    TEST(skpwww_tinytots_com_1),
+    TEST(skpwww_educationalcraft_com_4a),
     TEST(skpwww_lptemp_com_3),
     TEST(skpwww_shinydemos_com_5),
-#if TEST_NEW_FAILURES
     TEST(skpwww_lptemp_com_5),
     TEST(skpwww_shinydemos_com_15),
     TEST(skpwww_familysurvivalprotocol_wordpress_com_61),
     TEST(skpwww_alamdi_com_3),
     TEST(skpwww_devbridge_com_22),
-#endif
     TEST(skpwww_firstunitedbank_com_19),
     TEST(skpwww_googleventures_com_32),
     TEST(skpwww_9to5mac_com_64),
@@ -3942,11 +4764,10 @@
 static const size_t testCount = SK_ARRAY_COUNT(tests);
 
 static bool runReverse = false;
-static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 DEF_TEST(PathOpsSkp, reporter) {
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
-    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
 }
diff --git a/src/third_party/skia/tests/PathOpsTSectDebug.h b/src/third_party/skia/tests/PathOpsTSectDebug.h
new file mode 100644
index 0000000..20915210
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsTSectDebug.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef PathOpsTSectDebug_DEFINED
+#define PathOpsTSectDebug_DEFINED
+
+#include "SkPathOpsTSect.h"
+
+template<typename TCurve, typename OppCurve>
+char SkTCoincident<TCurve, OppCurve>::dumpIsCoincidentStr() const {
+    if (!!fMatch != fMatch) {
+        return '?';
+    }
+    return fMatch ? '*' : 0;
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTCoincident<TCurve, OppCurve>::dump() const {
+    SkDebugf("t=%1.9g pt=(%1.9g,%1.9g)%s\n", fPerpT, fPerpPt.fX, fPerpPt.fY,
+            fMatch ? " match" : "");
+}
+
+template<typename TCurve, typename OppCurve>
+const SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::debugSpan(int id) const {
+    const SkTSpan<TCurve, OppCurve>* test = fHead;
+    do {
+        if (test->debugID() == id) {
+            return test;
+        }
+    } while ((test = test->next()));
+    return nullptr;
+}
+
+template<typename TCurve, typename OppCurve>
+const SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::debugT(double t) const {
+    const SkTSpan<TCurve, OppCurve>* test = fHead;
+    const SkTSpan<TCurve, OppCurve>* closest = nullptr;
+    double bestDist = DBL_MAX;
+    do {
+        if (between(test->fStartT, t, test->fEndT)) {
+            return test;
+        }
+        double testDist = SkTMin(fabs(test->fStartT - t), fabs(test->fEndT - t));
+        if (bestDist > testDist) {
+            bestDist = testDist;
+            closest = test;
+        }
+    } while ((test = test->next()));
+    SkASSERT(closest);
+    return closest;
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dump() const {
+    dumpCommon(fHead);
+}
+
+extern int gDumpTSectNum;
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpBoth(SkTSect<OppCurve, TCurve>* opp) const {
+#if DEBUG_T_SECT_DUMP <= 2
+#if DEBUG_T_SECT_DUMP == 2
+    SkDebugf("%d ", ++gDumpTSectNum);
+#endif
+    this->dump();
+    SkDebugf(" ");
+    opp->dump();
+    SkDebugf("\n");
+#elif DEBUG_T_SECT_DUMP == 3
+    SkDebugf("<div id=\"sect%d\">\n", ++gDumpTSectNum);
+    if (this->fHead) {
+        this->dumpCurves();
+    }
+    if (opp->fHead) {
+        opp->dumpCurves();
+    }
+    SkDebugf("</div>\n\n");
+#endif
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpBounded(int id) const {
+    const SkTSpan<TCurve, OppCurve>* bounded = debugSpan(id);
+    if (!bounded) {
+        SkDebugf("no span matches %d\n", id);
+        return;
+    }
+    const SkTSpan<OppCurve, TCurve>* test = bounded->debugOpp()->fHead;
+    do {
+        if (test->findOppSpan(bounded)) {
+            test->dump();
+            SkDebugf(" ");
+        }
+    } while ((test = test->next()));
+    SkDebugf("\n");
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpBounds() const {
+    const SkTSpan<TCurve, OppCurve>* test = fHead;
+    do {
+        test->dumpBounds();
+    } while ((test = test->next()));
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpCoin() const {
+    dumpCommon(fCoincident);
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpCoinCurves() const {
+    dumpCommonCurves(fCoincident);
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpCommon(const SkTSpan<TCurve, OppCurve>* test) const {
+    SkDebugf("id=%d", debugID());
+    if (!test) {
+        SkDebugf(" (empty)");
+        return;
+    }
+    do {
+        SkDebugf(" ");
+        test->dump();
+    } while ((test = test->next()));
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpCommonCurves(const SkTSpan<TCurve, OppCurve>* test) const {
+    do {
+        test->fPart.dumpID(test->debugID());
+    } while ((test = test->next()));
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpCurves() const {
+    dumpCommonCurves(fHead);
+}
+
+template<typename TCurve, typename OppCurve>
+const SkTSpan<TCurve, OppCurve>* SkTSpan<TCurve, OppCurve>::debugSpan(int id) const {
+    return SkDEBUGRELEASE(fDebugSect->debugSpan(id), nullptr);
+}
+
+template<typename TCurve, typename OppCurve>
+const SkTSpan<TCurve, OppCurve>* SkTSpan<TCurve, OppCurve>::debugT(double t) const {
+    return SkDEBUGRELEASE(fDebugSect->debugT(t), nullptr);
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::dumpAll() const {
+    dumpID();
+    SkDebugf("=(%g,%g) [", fStartT, fEndT);
+    const SkTSpanBounded<OppCurve, TCurve>* testBounded = fBounded;
+    while (testBounded) {
+        const SkTSpan<OppCurve, TCurve>* span = testBounded->fBounded;
+        const SkTSpanBounded<OppCurve, TCurve>* next = testBounded->fNext;
+        span->dumpID();
+        SkDebugf("=(%g,%g)", span->fStartT, span->fEndT);
+        if (next) {
+            SkDebugf(" ");
+        }
+        testBounded = next;
+    }
+    SkDebugf("]\n");
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::dump() const {
+    dumpID();
+    SkDebugf("=(%g,%g) [", fStartT, fEndT);
+    const SkTSpanBounded<OppCurve, TCurve>* testBounded = fBounded;
+    while (testBounded) {
+        const SkTSpan<OppCurve, TCurve>* span = testBounded->fBounded;
+        const SkTSpanBounded<OppCurve, TCurve>* next = testBounded->fNext;
+        span->dumpID();
+        if (next) {
+            SkDebugf(",");
+        }
+        testBounded = next;
+    }
+    SkDebugf("]");
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::dumpBounded(int id) const {
+    SkDEBUGCODE(fDebugSect->dumpBounded(id));
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::dumpBounds() const {
+    dumpID();
+    SkDebugf(" bounds=(%1.9g,%1.9g, %1.9g,%1.9g) boundsMax=%1.9g%s\n",
+            fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom, fBoundsMax,
+            fCollapsed ? " collapsed" : "");
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::dumpCoin() const {
+    dumpID();
+    SkDebugf(" coinStart ");
+    fCoinStart.dump();
+    SkDebugf(" coinEnd ");
+    fCoinEnd.dump();
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::dumpID() const {
+    char cS = fCoinStart.dumpIsCoincidentStr();
+    if (cS) {
+        SkDebugf("%c", cS);
+    }
+    SkDebugf("%d", debugID());
+    char cE = fCoinEnd.dumpIsCoincidentStr();
+    if (cE) {
+        SkDebugf("%c", cE);
+    }
+}
+#endif  // PathOpsTSectDebug_DEFINED
diff --git a/src/third_party/skia/tests/PathOpsTestCommon.cpp b/src/third_party/skia/tests/PathOpsTestCommon.cpp
index 60a12ee..8cc8fe8 100644
--- a/src/third_party/skia/tests/PathOpsTestCommon.cpp
+++ b/src/third_party/skia/tests/PathOpsTestCommon.cpp
@@ -6,14 +6,133 @@
  */
 #include "PathOpsTestCommon.h"
 #include "SkPathOpsBounds.h"
+#include "SkPathOpsConic.h"
 #include "SkPathOpsCubic.h"
 #include "SkPathOpsLine.h"
 #include "SkPathOpsQuad.h"
-#include "SkPathOpsTriangle.h"
+#include "SkReduceOrder.h"
+#include "SkTSort.h"
+
+static double calc_t_div(const SkDCubic& cubic, double precision, double start) {
+    const double adjust = sqrt(3.) / 36;
+    SkDCubic sub;
+    const SkDCubic* cPtr;
+    if (start == 0) {
+        cPtr = &cubic;
+    } else {
+        // OPTIMIZE: special-case half-split ?
+        sub = cubic.subDivide(start, 1);
+        cPtr = &sub;
+    }
+    const SkDCubic& c = *cPtr;
+    double dx = c[3].fX - 3 * (c[2].fX - c[1].fX) - c[0].fX;
+    double dy = c[3].fY - 3 * (c[2].fY - c[1].fY) - c[0].fY;
+    double dist = sqrt(dx * dx + dy * dy);
+    double tDiv3 = precision / (adjust * dist);
+    double t = SkDCubeRoot(tDiv3);
+    if (start > 0) {
+        t = start + (1 - start) * t;
+    }
+    return t;
+}
+
+static bool add_simple_ts(const SkDCubic& cubic, double precision, SkTArray<double, true>* ts) {
+    double tDiv = calc_t_div(cubic, precision, 0);
+    if (tDiv >= 1) {
+        return true;
+    }
+    if (tDiv >= 0.5) {
+        ts->push_back(0.5);
+        return true;
+    }
+    return false;
+}
+
+static void addTs(const SkDCubic& cubic, double precision, double start, double end,
+        SkTArray<double, true>* ts) {
+    double tDiv = calc_t_div(cubic, precision, 0);
+    double parts = ceil(1.0 / tDiv);
+    for (double index = 0; index < parts; ++index) {
+        double newT = start + (index / parts) * (end - start);
+        if (newT > 0 && newT < 1) {
+            ts->push_back(newT);
+        }
+    }
+}
+
+static void toQuadraticTs(const SkDCubic* cubic, double precision, SkTArray<double, true>* ts) {
+    SkReduceOrder reducer;
+    int order = reducer.reduce(*cubic, SkReduceOrder::kAllow_Quadratics);
+    if (order < 3) {
+        return;
+    }
+    double inflectT[5];
+    int inflections = cubic->findInflections(inflectT);
+    SkASSERT(inflections <= 2);
+    if (!cubic->endsAreExtremaInXOrY()) {
+        inflections += cubic->findMaxCurvature(&inflectT[inflections]);
+        SkASSERT(inflections <= 5);
+    }
+    SkTQSort<double>(inflectT, &inflectT[inflections - 1]);
+    // OPTIMIZATION: is this filtering common enough that it needs to be pulled out into its
+    // own subroutine?
+    while (inflections && approximately_less_than_zero(inflectT[0])) {
+        memmove(inflectT, &inflectT[1], sizeof(inflectT[0]) * --inflections);
+    }
+    int start = 0;
+    int next = 1;
+    while (next < inflections) {
+        if (!approximately_equal(inflectT[start], inflectT[next])) {
+            ++start;
+        ++next;
+            continue;
+        }
+        memmove(&inflectT[start], &inflectT[next], sizeof(inflectT[0]) * (--inflections - start));
+    }
+
+    while (inflections && approximately_greater_than_one(inflectT[inflections - 1])) {
+        --inflections;
+    }
+    SkDCubicPair pair;
+    if (inflections == 1) {
+        pair = cubic->chopAt(inflectT[0]);
+        int orderP1 = reducer.reduce(pair.first(), SkReduceOrder::kNo_Quadratics);
+        if (orderP1 < 2) {
+            --inflections;
+        } else {
+            int orderP2 = reducer.reduce(pair.second(), SkReduceOrder::kNo_Quadratics);
+            if (orderP2 < 2) {
+                --inflections;
+            }
+        }
+    }
+    if (inflections == 0 && add_simple_ts(*cubic, precision, ts)) {
+        return;
+    }
+    if (inflections == 1) {
+        pair = cubic->chopAt(inflectT[0]);
+        addTs(pair.first(), precision, 0, inflectT[0], ts);
+        addTs(pair.second(), precision, inflectT[0], 1, ts);
+        return;
+    }
+    if (inflections > 1) {
+        SkDCubic part = cubic->subDivide(0, inflectT[0]);
+        addTs(part, precision, 0, inflectT[0], ts);
+        int last = inflections - 1;
+        for (int idx = 0; idx < last; ++idx) {
+            part = cubic->subDivide(inflectT[idx], inflectT[idx + 1]);
+            addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts);
+        }
+        part = cubic->subDivide(inflectT[last], 1);
+        addTs(part, precision, inflectT[last], 1, ts);
+        return;
+    }
+    addTs(*cubic, precision, 0, 1, ts);
+}
 
 void CubicToQuads(const SkDCubic& cubic, double precision, SkTArray<SkDQuad, true>& quads) {
     SkTArray<double, true> ts;
-    cubic.toQuadraticTs(precision, &ts);
+    toQuadraticTs(&cubic, precision, &ts);
     if (ts.count() <= 0) {
         SkDQuad quad = cubic.toQuad();
         quads.push_back(quad);
@@ -22,8 +141,20 @@
     double tStart = 0;
     for (int i1 = 0; i1 <= ts.count(); ++i1) {
         const double tEnd = i1 < ts.count() ? ts[i1] : 1;
+        SkDRect bounds;
+        bounds.setBounds(cubic);
         SkDCubic part = cubic.subDivide(tStart, tEnd);
         SkDQuad quad = part.toQuad();
+        if (quad[1].fX < bounds.fLeft) {
+            quad[1].fX = bounds.fLeft;
+        } else if (quad[1].fX > bounds.fRight) {
+            quad[1].fX = bounds.fRight;
+        }
+        if (quad[1].fY < bounds.fTop) {
+            quad[1].fY = bounds.fTop;
+        } else if (quad[1].fY > bounds.fBottom) {
+            quad[1].fY = bounds.fBottom;
+        }
         quads.push_back(quad);
         tStart = tEnd;
     }
@@ -106,7 +237,7 @@
                     lo = hi;
                 }
                 break;
-            } 
+            }
             case SkPath::kClose_Verb:
                  simplePath->close();
                 break;
@@ -134,6 +265,18 @@
     return !SkScalarIsNaN(bounds.fBottom);
 }
 
+bool ValidConic(const SkDConic& conic) {
+    for (int index = 0; index < SkDConic::kPointCount; ++index) {
+        if (!ValidPoint(conic[index])) {
+            return false;
+        }
+    }
+    if (SkDoubleIsNaN(conic.fWeight)) {
+        return false;
+    }
+    return true;
+}
+
 bool ValidCubic(const SkDCubic& cubic) {
     for (int index = 0; index < 4; ++index) {
         if (!ValidPoint(cubic[index])) {
@@ -180,15 +323,6 @@
     return true;
 }
 
-bool ValidTriangle(const SkDTriangle& triangle) {
-    for (int index = 0; index < 3; ++index) {
-        if (!ValidPoint(triangle.fPts[index])) {
-            return false;
-        }
-    }
-    return true;
-}
-
 bool ValidVector(const SkDVector& v) {
     if (SkDoubleIsNaN(v.fX)) {
         return false;
diff --git a/src/third_party/skia/tests/PathOpsTestCommon.h b/src/third_party/skia/tests/PathOpsTestCommon.h
index 0c42bfb..ba64d93 100644
--- a/src/third_party/skia/tests/PathOpsTestCommon.h
+++ b/src/third_party/skia/tests/PathOpsTestCommon.h
@@ -12,16 +12,31 @@
 
 struct SkPathOpsBounds;
 
+struct QuadPts {
+    static const int kPointCount = 3;
+    SkDPoint fPts[kPointCount];
+};
+
+struct ConicPts {
+    QuadPts fPts;
+    SkScalar fWeight;
+};
+
+struct CubicPts {
+    static const int kPointCount = 4;
+    SkDPoint fPts[kPointCount];
+};
+
 void CubicPathToQuads(const SkPath& cubicPath, SkPath* quadPath);
 void CubicPathToSimple(const SkPath& cubicPath, SkPath* simplePath);
 void CubicToQuads(const SkDCubic& cubic, double precision, SkTArray<SkDQuad, true>& quads);
-bool ValidBounds(const SkPathOpsBounds&);
+bool ValidBounds(const SkPathOpsBounds& );
+bool ValidConic(const SkDConic& cubic);
 bool ValidCubic(const SkDCubic& cubic);
 bool ValidLine(const SkDLine& line);
 bool ValidPoint(const SkDPoint& pt);
 bool ValidPoints(const SkPoint* pts, int count);
 bool ValidQuad(const SkDQuad& quad);
-bool ValidTriangle(const SkDTriangle& triangle);
 bool ValidVector(const SkDVector& v);
 
 #endif
diff --git a/src/third_party/skia/tests/PathOpsThreadedCommon.cpp b/src/third_party/skia/tests/PathOpsThreadedCommon.cpp
index 0adde91..a1a65b7 100644
--- a/src/third_party/skia/tests/PathOpsThreadedCommon.cpp
+++ b/src/third_party/skia/tests/PathOpsThreadedCommon.cpp
@@ -11,13 +11,12 @@
 
 PathOpsThreadedTestRunner::~PathOpsThreadedTestRunner() {
     for (int index = 0; index < fRunnables.count(); index++) {
-        SkDELETE(fRunnables[index]);
+        delete fRunnables[index];
     }
 }
 
 void PathOpsThreadedTestRunner::render() {
-    SkTaskGroup tg;
-    for (int index = 0; index < fRunnables.count(); ++ index) {
-        tg.add(fRunnables[index]);
-    }
+    SkTaskGroup().batch(fRunnables.count(), [&](int i) {
+        (*fRunnables[i])();
+    });
 }
diff --git a/src/third_party/skia/tests/PathOpsThreadedCommon.h b/src/third_party/skia/tests/PathOpsThreadedCommon.h
index 124921e..706da6b 100644
--- a/src/third_party/skia/tests/PathOpsThreadedCommon.h
+++ b/src/third_party/skia/tests/PathOpsThreadedCommon.h
@@ -7,10 +7,14 @@
 #ifndef PathOpsThreadedCommon_DEFINED
 #define PathOpsThreadedCommon_DEFINED
 
+#include "SkBitmap.h"
 #include "SkGraphics.h"
-#include "SkRunnable.h"
+#include "SkPath.h"
+#include "SkPathOps.h"
 #include "SkTDArray.h"
 
+#include <string>
+
 #define PATH_STR_SIZE 512
 
 class PathOpsThreadedRunnable;
@@ -24,11 +28,14 @@
     unsigned char fB;
     unsigned char fC;
     unsigned char fD;
-    char* fPathStr;
+    std::string fPathStr;
     const char* fKey;
     char fSerialNo[256];
     skiatest::Reporter* fReporter;
     SkBitmap* fBitmap;
+
+    void outputProgress(const char* pathStr, SkPath::FillType);
+    void outputProgress(const char* pathStr, SkPathOp);
 };
 
 class PathOpsThreadedTestRunner {
@@ -44,7 +51,7 @@
     skiatest::Reporter* fReporter;
 };
 
-class PathOpsThreadedRunnable : public SkRunnable {
+class PathOpsThreadedRunnable {
 public:
     PathOpsThreadedRunnable(void (*testFun)(PathOpsThreadState*), int a, int b, int c, int d,
             PathOpsThreadedTestRunner* runner) {
@@ -73,11 +80,9 @@
         fTestFun = testFun;
     }
 
-    virtual void run() SK_OVERRIDE {
+    void operator()() {
         SkBitmap bitmap;
         fState.fBitmap = &bitmap;
-        char pathStr[PATH_STR_SIZE];
-        fState.fPathStr = pathStr;
         SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
         (*fTestFun)(&fState);
     }
diff --git a/src/third_party/skia/tests/PathOpsThreeWayTest.cpp b/src/third_party/skia/tests/PathOpsThreeWayTest.cpp
new file mode 100644
index 0000000..b86ff65
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsThreeWayTest.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsTestCommon.h"
+#include "SkIntersections.h"
+#include "SkTDArray.h"
+#include "Test.h"
+
+// check intersections for consistency
+
+struct Curve {
+    int ptCount;
+    CubicPts curve;  // largest can hold lines / quads/ cubics
+};
+
+static const Curve testSet0[] = {  // extracted from skpClip2
+    {4, {{{134,11414}, {131.990234,11414}, {130.32666,11415.4824}, {130.042755,11417.4131}}} },
+    {4, {{{130.042755,11417.4131}, {130.233124,11418.3193}, {131.037079,11419}, {132,11419}}} },
+    {4, {{{132,11419}, {130.895432,11419}, {130,11418.1045}, {130,11417}}} },
+};
+
+static const Curve testSet1[] = {  // extracted from cubicOp85i
+    {4, {{{3,4}, {1,5}, {4,3}, {6,4}}} },
+    {1, {{{6,4}, {3,4}}} },
+    {4, {{{3,4}, {4,6}, {4,3}, {5,1}}} },
+    {1, {{{5,1}, {3,4}}} },
+};
+
+static const struct TestSet {
+    const Curve* tests;
+    int testCount;
+} testSets[] = {
+    { testSet0, (int) SK_ARRAY_COUNT(testSet0) },
+    { testSet1, (int) SK_ARRAY_COUNT(testSet1) },
+};
+
+static const int testSetsCount = (int) SK_ARRAY_COUNT(testSets);
+
+static void testSetTest(skiatest::Reporter* reporter, int index) {
+    const TestSet& testSet = testSets[index];
+    int testCount = testSet.testCount;
+    SkASSERT(testCount > 1);
+    SkTDArray<SkIntersections> combos;
+    for (int outer = 0; outer < testCount - 1; ++outer) {
+        const Curve& oTest = testSet.tests[outer];
+        for (int inner = outer + 1; inner < testCount; ++inner) {
+            const Curve& iTest = testSet.tests[inner];
+            SkIntersections* i = combos.append();
+            sk_bzero(i, sizeof(SkIntersections));
+            SkDLine oLine = {{ oTest.curve.fPts[0], oTest.curve.fPts[1] }};
+            SkDLine iLine = {{ iTest.curve.fPts[0], iTest.curve.fPts[1] }};
+            SkDCubic iCurve, oCurve;
+            iCurve.debugSet(iTest.curve.fPts);
+            oCurve.debugSet(oTest.curve.fPts);
+            if (oTest.ptCount == 1 && iTest.ptCount == 1) {
+                i->intersect(oLine, iLine);
+            } else if (oTest.ptCount == 1 && iTest.ptCount == 4) {
+                i->intersect(iCurve, oLine);
+            } else if (oTest.ptCount == 4 && iTest.ptCount == 1) {
+                i->intersect(oCurve, iLine);
+            } else if (oTest.ptCount == 4 && iTest.ptCount == 4) {
+                i->intersect(oCurve, iCurve);
+            } else {
+                SkASSERT(0);
+            }
+//            i->dump();
+        }
+    }
+}
+
+DEF_TEST(PathOpsThreeWay, reporter) {
+    for (int index = 0; index < testSetsCount; ++index) {
+        testSetTest(reporter, index);
+        reporter->bumpTestCount();
+    }
+}
+
+DEF_TEST(PathOpsThreeWayOneOff, reporter) {
+    int index = 0;
+    testSetTest(reporter, index);
+}
diff --git a/src/third_party/skia/tests/PathOpsTigerTest.cpp b/src/third_party/skia/tests/PathOpsTigerTest.cpp
new file mode 100644
index 0000000..01c3d4e
--- /dev/null
+++ b/src/third_party/skia/tests/PathOpsTigerTest.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsExtendedTest.h"
+#include "PathOpsThreadedCommon.h"
+
+#define TEST(name) { name, #name }
+
+static void tiger8(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.moveTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a));  // 492.451f, 225.216f
+path.quadTo(SkBits2Float(0x43f58ce4), SkBits2Float(0x435d2a04), SkBits2Float(0x43f71bd9), SkBits2Float(0x435ac7d8));  // 491.101f, 221.164f, 494.218f, 218.781f
+path.quadTo(SkBits2Float(0x43f7d69d), SkBits2Float(0x4359aa35), SkBits2Float(0x43f8b3b3), SkBits2Float(0x435951c5));  // 495.677f, 217.665f, 497.404f, 217.319f
+path.conicTo(SkBits2Float(0x43f8ba67), SkBits2Float(0x43594f16), SkBits2Float(0x43f8c136), SkBits2Float(0x43594dd9), SkBits2Float(0x3f7fa2b1));  // 497.456f, 217.309f, 497.509f, 217.304f, 0.998576f
+path.quadTo(SkBits2Float(0x43fcc3a8), SkBits2Float(0x43589340), SkBits2Float(0x43ff01dc), SkBits2Float(0x4352e191));  // 505.529f, 216.575f, 510.015f, 210.881f
+path.conicTo(SkBits2Float(0x43ff5113), SkBits2Float(0x4352187b), SkBits2Float(0x43ffb59e), SkBits2Float(0x4352b6e9), SkBits2Float(0x3f3504f3));  // 510.633f, 210.096f, 511.419f, 210.714f, 0.707107f
+path.conicTo(SkBits2Float(0x43ffdc85), SkBits2Float(0x4352f435), SkBits2Float(0x43ffe4a9), SkBits2Float(0x435355e9), SkBits2Float(0x3f6ec0ae));  // 511.723f, 210.954f, 511.786f, 211.336f, 0.932628f
+path.quadTo(SkBits2Float(0x4400461c), SkBits2Float(0x435b3080), SkBits2Float(0x4400b692), SkBits2Float(0x4360b229));  // 513.095f, 219.189f, 514.853f, 224.696f
+path.conicTo(SkBits2Float(0x4400c662), SkBits2Float(0x43617856), SkBits2Float(0x44009920), SkBits2Float(0x4361decb), SkBits2Float(0x3f46ad5b));  // 515.1f, 225.47f, 514.393f, 225.87f, 0.776083f
+path.quadTo(SkBits2Float(0x43fb4920), SkBits2Float(0x43688f50), SkBits2Float(0x43f8340f), SkBits2Float(0x4365b887));  // 502.571f, 232.56f, 496.407f, 229.721f
+path.quadTo(SkBits2Float(0x43f72cd2), SkBits2Float(0x4364c612), SkBits2Float(0x43f69888), SkBits2Float(0x4362e330));  // 494.35f, 228.774f, 493.192f, 226.887f
+path.quadTo(SkBits2Float(0x43f66a00), SkBits2Float(0x43624bae), SkBits2Float(0x43f64c73), SkBits2Float(0x4361ad04));  // 492.828f, 226.296f, 492.597f, 225.676f
+path.quadTo(SkBits2Float(0x43f642ea), SkBits2Float(0x436179d2), SkBits2Float(0x43f63c1c), SkBits2Float(0x43614abe));  // 492.523f, 225.476f, 492.47f, 225.292f
+path.quadTo(SkBits2Float(0x43f639c9), SkBits2Float(0x43613aa5), SkBits2Float(0x43f63809), SkBits2Float(0x43612cda));  // 492.451f, 225.229f, 492.438f, 225.175f
+path.quadTo(SkBits2Float(0x43f63777), SkBits2Float(0x43612855), SkBits2Float(0x43f636df), SkBits2Float(0x43612357));  // 492.433f, 225.158f, 492.429f, 225.138f
+path.quadTo(SkBits2Float(0x43f6368f), SkBits2Float(0x436120b2), SkBits2Float(0x43f6367b), SkBits2Float(0x43612005));  // 492.426f, 225.128f, 492.426f, 225.125f
+path.lineTo(SkBits2Float(0x43f63656), SkBits2Float(0x43611ebc));  // 492.424f, 225.12f
+path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e34));  // 492.424f, 225.118f
+path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611df3));  // 492.424f, 225.117f
+path.lineTo(SkBits2Float(0x43f6363e), SkBits2Float(0x43611de5));  // 492.424f, 225.117f
+path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611deb));  // 492.424f, 225.117f
+path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e37));  // 492.424f, 225.118f
+path.lineTo(SkBits2Float(0x43f63644), SkBits2Float(0x43611e19));  // 492.424f, 225.118f
+path.quadTo(SkBits2Float(0x43f6365c), SkBits2Float(0x43611ee7), SkBits2Float(0x43f6365d), SkBits2Float(0x43611ef9));  // 492.425f, 225.121f, 492.425f, 225.121f
+path.quadTo(SkBits2Float(0x43f63666), SkBits2Float(0x43611f4b), SkBits2Float(0x43f63672), SkBits2Float(0x43611fb1));  // 492.425f, 225.122f, 492.425f, 225.124f
+path.quadTo(SkBits2Float(0x43f636ab), SkBits2Float(0x436121a4), SkBits2Float(0x43f636e3), SkBits2Float(0x4361236a));  // 492.427f, 225.131f, 492.429f, 225.138f
+path.quadTo(SkBits2Float(0x43f636fd), SkBits2Float(0x43612443), SkBits2Float(0x43f63705), SkBits2Float(0x4361247e));  // 492.43f, 225.142f, 492.43f, 225.143f
+path.quadTo(SkBits2Float(0x43f637d7), SkBits2Float(0x43612b15), SkBits2Float(0x43f638dc), SkBits2Float(0x436131b0));  // 492.436f, 225.168f, 492.444f, 225.194f
+path.quadTo(SkBits2Float(0x43f63b88), SkBits2Float(0x43614303), SkBits2Float(0x43f63f62), SkBits2Float(0x43615368));  // 492.465f, 225.262f, 492.495f, 225.326f
+path.quadTo(SkBits2Float(0x43f6436f), SkBits2Float(0x4361649f), SkBits2Float(0x43f648b2), SkBits2Float(0x43617468));  // 492.527f, 225.393f, 492.568f, 225.455f
+path.quadTo(SkBits2Float(0x43f68760), SkBits2Float(0x43623072), SkBits2Float(0x43f6ec71), SkBits2Float(0x4361cb60));  // 493.058f, 226.189f, 493.847f, 225.794f
+path.quadTo(SkBits2Float(0x43f722ef), SkBits2Float(0x436194e0), SkBits2Float(0x43f73027), SkBits2Float(0x43611df0));  // 494.273f, 225.582f, 494.376f, 225.117f
+path.quadTo(SkBits2Float(0x43f73334), SkBits2Float(0x43610284), SkBits2Float(0x43f73333), SkBits2Float(0x4360e667));  // 494.4f, 225.01f, 494.4f, 224.9f
+path.lineTo(SkBits2Float(0x43f63638), SkBits2Float(0x43611daf));  // 492.424f, 225.116f
+path.lineTo(SkBits2Float(0x43f6b333), SkBits2Float(0x4360e666));  // 493.4f, 224.9f
+path.lineTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a));  // 492.451f, 225.216f
+path.close();
+path.moveTo(SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572));  // 494.349f, 224.584f
+path.conicTo(SkBits2Float(0x43f72ebd), SkBits2Float(0x4360a219), SkBits2Float(0x43f7302e), SkBits2Float(0x4360af1f), SkBits2Float(0x3f7fa741));  // 494.365f, 224.633f, 494.376f, 224.684f, 0.998646f
+path.lineTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360e667));  // 492.4f, 224.9f
+path.quadTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360ca4b), SkBits2Float(0x43f6363f), SkBits2Float(0x4360aede));  // 492.4f, 224.79f, 492.424f, 224.683f
+path.quadTo(SkBits2Float(0x43f64377), SkBits2Float(0x436037ee), SkBits2Float(0x43f679f5), SkBits2Float(0x4360016e));  // 492.527f, 224.218f, 492.953f, 224.006f
+path.quadTo(SkBits2Float(0x43f6df06), SkBits2Float(0x435f9c5c), SkBits2Float(0x43f71db4), SkBits2Float(0x43605866));  // 493.742f, 223.611f, 494.232f, 224.345f
+path.quadTo(SkBits2Float(0x43f722f8), SkBits2Float(0x43606830), SkBits2Float(0x43f72704), SkBits2Float(0x43607966));  // 494.273f, 224.407f, 494.305f, 224.474f
+path.quadTo(SkBits2Float(0x43f72ae0), SkBits2Float(0x436089cd), SkBits2Float(0x43f72d8a), SkBits2Float(0x43609b1e));  // 494.335f, 224.538f, 494.356f, 224.606f
+path.quadTo(SkBits2Float(0x43f72e8e), SkBits2Float(0x4360a1b8), SkBits2Float(0x43f72f61), SkBits2Float(0x4360a850));  // 494.364f, 224.632f, 494.37f, 224.657f
+path.quadTo(SkBits2Float(0x43f72f68), SkBits2Float(0x4360a88a), SkBits2Float(0x43f72f83), SkBits2Float(0x4360a964));  // 494.37f, 224.658f, 494.371f, 224.662f
+path.quadTo(SkBits2Float(0x43f72fbb), SkBits2Float(0x4360ab2a), SkBits2Float(0x43f72ff4), SkBits2Float(0x4360ad1d));  // 494.373f, 224.669f, 494.375f, 224.676f
+path.quadTo(SkBits2Float(0x43f73000), SkBits2Float(0x4360ad83), SkBits2Float(0x43f73009), SkBits2Float(0x4360add5));  // 494.375f, 224.678f, 494.375f, 224.679f
+path.quadTo(SkBits2Float(0x43f7300b), SkBits2Float(0x4360ade9), SkBits2Float(0x43f73022), SkBits2Float(0x4360aeb5));  // 494.375f, 224.679f, 494.376f, 224.682f
+path.lineTo(SkBits2Float(0x43f7301f), SkBits2Float(0x4360ae97));  // 494.376f, 224.682f
+path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aee3));  // 494.376f, 224.683f
+path.lineTo(SkBits2Float(0x43f73028), SkBits2Float(0x4360aeeb));  // 494.376f, 224.683f
+path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aedf));  // 494.376f, 224.683f
+path.lineTo(SkBits2Float(0x43f73021), SkBits2Float(0x4360aeaa));  // 494.376f, 224.682f
+path.lineTo(SkBits2Float(0x43f73016), SkBits2Float(0x4360ae50));  // 494.376f, 224.681f
+path.lineTo(SkBits2Float(0x43f73007), SkBits2Float(0x4360adc1));  // 494.375f, 224.679f
+path.lineTo(SkBits2Float(0x43f72ff9), SkBits2Float(0x4360ad4d));  // 494.375f, 224.677f
+path.quadTo(SkBits2Float(0x43f7300d), SkBits2Float(0x4360adf7), SkBits2Float(0x43f73031), SkBits2Float(0x4360af12));  // 494.375f, 224.68f, 494.376f, 224.684f
+path.quadTo(SkBits2Float(0x43f730f0), SkBits2Float(0x4360b4f1), SkBits2Float(0x43f7320a), SkBits2Float(0x4360bc94));  // 494.382f, 224.707f, 494.391f, 224.737f
+path.quadTo(SkBits2Float(0x43f73625), SkBits2Float(0x4360d8fe), SkBits2Float(0x43f73c59), SkBits2Float(0x4360fa4a));  // 494.423f, 224.848f, 494.471f, 224.978f
+path.quadTo(SkBits2Float(0x43f75132), SkBits2Float(0x43616a36), SkBits2Float(0x43f772ac), SkBits2Float(0x4361d738));  // 494.634f, 225.415f, 494.896f, 225.841f
+path.quadTo(SkBits2Float(0x43f7de60), SkBits2Float(0x436335ea), SkBits2Float(0x43f89f25), SkBits2Float(0x4363e779));  // 495.737f, 227.211f, 497.243f, 227.904f
+path.quadTo(SkBits2Float(0x43fb3d30), SkBits2Float(0x436650a0), SkBits2Float(0x44005a14), SkBits2Float(0x43602133));  // 502.478f, 230.315f, 513.407f, 224.13f
+path.lineTo(SkBits2Float(0x4400799a), SkBits2Float(0x4360ffff));  // 513.9f, 225
+path.lineTo(SkBits2Float(0x44003ca2), SkBits2Float(0x43614dd5));  // 512.947f, 225.304f
+path.quadTo(SkBits2Float(0x43ff92b8), SkBits2Float(0x435ba8f8), SkBits2Float(0x43fee825), SkBits2Float(0x4353aa15));  // 511.146f, 219.66f, 509.814f, 211.664f
+path.lineTo(SkBits2Float(0x43ff6667), SkBits2Float(0x43537fff));  // 510.8f, 211.5f
+path.lineTo(SkBits2Float(0x43ffcaf2), SkBits2Float(0x43541e6d));  // 511.586f, 212.119f
+path.quadTo(SkBits2Float(0x43fd4888), SkBits2Float(0x435a7d38), SkBits2Float(0x43f8d864), SkBits2Float(0x435b4bbf));  // 506.567f, 218.489f, 497.691f, 219.296f
+path.lineTo(SkBits2Float(0x43f8cccd), SkBits2Float(0x435a4ccc));  // 497.6f, 218.3f
+path.lineTo(SkBits2Float(0x43f8e5e7), SkBits2Float(0x435b47d3));  // 497.796f, 219.281f
+path.quadTo(SkBits2Float(0x43f84300), SkBits2Float(0x435b88fd), SkBits2Float(0x43f7b75b), SkBits2Float(0x435c5e8e));  // 496.523f, 219.535f, 495.432f, 220.369f
+path.quadTo(SkBits2Float(0x43f6b984), SkBits2Float(0x435de2c4), SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572));  // 493.449f, 221.886f, 494.349f, 224.584f
+path.close();
+testSimplify(reporter, path, filename);
+}
+
+// fails to include a line of edges, probably mis-sorting
+static void tiger8a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.moveTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a));  // 492.451f, 225.216f
+path.quadTo(SkBits2Float(0x43f58ce4), SkBits2Float(0x435d2a04), SkBits2Float(0x43f71bd9), SkBits2Float(0x435ac7d8));  // 491.101f, 221.164f, 494.218f, 218.781f
+path.quadTo(SkBits2Float(0x43f7d69d), SkBits2Float(0x4359aa35), SkBits2Float(0x43f8b3b3), SkBits2Float(0x435951c5));  // 495.677f, 217.665f, 497.404f, 217.319f
+path.conicTo(SkBits2Float(0x43f8ba67), SkBits2Float(0x43594f16), SkBits2Float(0x43f8c136), SkBits2Float(0x43594dd9), SkBits2Float(0x3f7fa2b1));  // 497.456f, 217.309f, 497.509f, 217.304f, 0.998576f
+path.quadTo(SkBits2Float(0x43fcc3a8), SkBits2Float(0x43589340), SkBits2Float(0x43ff01dc), SkBits2Float(0x4352e191));  // 505.529f, 216.575f, 510.015f, 210.881f
+path.conicTo(SkBits2Float(0x43ff5113), SkBits2Float(0x4352187b), SkBits2Float(0x43ffb59e), SkBits2Float(0x4352b6e9), SkBits2Float(0x3f3504f3));  // 510.633f, 210.096f, 511.419f, 210.714f, 0.707107f
+path.conicTo(SkBits2Float(0x43ffdc85), SkBits2Float(0x4352f435), SkBits2Float(0x43ffe4a9), SkBits2Float(0x435355e9), SkBits2Float(0x3f6ec0ae));  // 511.723f, 210.954f, 511.786f, 211.336f, 0.932628f
+path.quadTo(SkBits2Float(0x4400461c), SkBits2Float(0x435b3080), SkBits2Float(0x4400b692), SkBits2Float(0x4360b229));  // 513.095f, 219.189f, 514.853f, 224.696f
+path.conicTo(SkBits2Float(0x4400c662), SkBits2Float(0x43617856), SkBits2Float(0x44009920), SkBits2Float(0x4361decb), SkBits2Float(0x3f46ad5b));  // 515.1f, 225.47f, 514.393f, 225.87f, 0.776083f
+path.quadTo(SkBits2Float(0x43fb4920), SkBits2Float(0x43688f50), SkBits2Float(0x43f8340f), SkBits2Float(0x4365b887));  // 502.571f, 232.56f, 496.407f, 229.721f
+path.quadTo(SkBits2Float(0x43f72cd2), SkBits2Float(0x4364c612), SkBits2Float(0x43f69888), SkBits2Float(0x4362e330));  // 494.35f, 228.774f, 493.192f, 226.887f
+path.quadTo(SkBits2Float(0x43f66a00), SkBits2Float(0x43624bae), SkBits2Float(0x43f64c73), SkBits2Float(0x4361ad04));  // 492.828f, 226.296f, 492.597f, 225.676f
+path.quadTo(SkBits2Float(0x43f642ea), SkBits2Float(0x436179d2), SkBits2Float(0x43f63c1c), SkBits2Float(0x43614abe));  // 492.523f, 225.476f, 492.47f, 225.292f
+path.quadTo(SkBits2Float(0x43f639c9), SkBits2Float(0x43613aa5), SkBits2Float(0x43f63809), SkBits2Float(0x43612cda));  // 492.451f, 225.229f, 492.438f, 225.175f
+path.quadTo(SkBits2Float(0x43f63777), SkBits2Float(0x43612855), SkBits2Float(0x43f636df), SkBits2Float(0x43612357));  // 492.433f, 225.158f, 492.429f, 225.138f
+path.quadTo(SkBits2Float(0x43f6368f), SkBits2Float(0x436120b2), SkBits2Float(0x43f6367b), SkBits2Float(0x43612005));  // 492.426f, 225.128f, 492.426f, 225.125f
+path.lineTo(SkBits2Float(0x43f63656), SkBits2Float(0x43611ebc));  // 492.424f, 225.12f
+path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e34));  // 492.424f, 225.118f
+path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611df3));  // 492.424f, 225.117f
+path.lineTo(SkBits2Float(0x43f6363e), SkBits2Float(0x43611de5));  // 492.424f, 225.117f
+path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611deb));  // 492.424f, 225.117f
+path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e37));  // 492.424f, 225.118f
+path.lineTo(SkBits2Float(0x43f63644), SkBits2Float(0x43611e19));  // 492.424f, 225.118f
+path.quadTo(SkBits2Float(0x43f6365c), SkBits2Float(0x43611ee7), SkBits2Float(0x43f6365d), SkBits2Float(0x43611ef9));  // 492.425f, 225.121f, 492.425f, 225.121f
+path.quadTo(SkBits2Float(0x43f63666), SkBits2Float(0x43611f4b), SkBits2Float(0x43f63672), SkBits2Float(0x43611fb1));  // 492.425f, 225.122f, 492.425f, 225.124f
+path.quadTo(SkBits2Float(0x43f636ab), SkBits2Float(0x436121a4), SkBits2Float(0x43f636e3), SkBits2Float(0x4361236a));  // 492.427f, 225.131f, 492.429f, 225.138f
+path.quadTo(SkBits2Float(0x43f636fd), SkBits2Float(0x43612443), SkBits2Float(0x43f63705), SkBits2Float(0x4361247e));  // 492.43f, 225.142f, 492.43f, 225.143f
+path.quadTo(SkBits2Float(0x43f637d7), SkBits2Float(0x43612b15), SkBits2Float(0x43f638dc), SkBits2Float(0x436131b0));  // 492.436f, 225.168f, 492.444f, 225.194f
+path.quadTo(SkBits2Float(0x43f63b88), SkBits2Float(0x43614303), SkBits2Float(0x43f63f62), SkBits2Float(0x43615368));  // 492.465f, 225.262f, 492.495f, 225.326f
+path.quadTo(SkBits2Float(0x43f6436f), SkBits2Float(0x4361649f), SkBits2Float(0x43f648b2), SkBits2Float(0x43617468));  // 492.527f, 225.393f, 492.568f, 225.455f
+path.quadTo(SkBits2Float(0x43f68760), SkBits2Float(0x43623072), SkBits2Float(0x43f6ec71), SkBits2Float(0x4361cb60));  // 493.058f, 226.189f, 493.847f, 225.794f
+path.quadTo(SkBits2Float(0x43f722ef), SkBits2Float(0x436194e0), SkBits2Float(0x43f73027), SkBits2Float(0x43611df0));  // 494.273f, 225.582f, 494.376f, 225.117f
+path.quadTo(SkBits2Float(0x43f73334), SkBits2Float(0x43610284), SkBits2Float(0x43f73333), SkBits2Float(0x4360e667));  // 494.4f, 225.01f, 494.4f, 224.9f
+path.lineTo(SkBits2Float(0x43f63638), SkBits2Float(0x43611daf));  // 492.424f, 225.116f
+path.lineTo(SkBits2Float(0x43f6b333), SkBits2Float(0x4360e666));  // 493.4f, 224.9f
+path.lineTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a));  // 492.451f, 225.216f
+path.close();
+testSimplify(reporter, path, filename);
+}
+
+static void tiger8a_x(skiatest::Reporter* reporter, uint64_t testlines) {
+    SkPath path;
+uint64_t i = 0;
+if (testlines & (1LL << i++)) path.moveTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a));  // 492.451f, 225.216f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f58ce4), SkBits2Float(0x435d2a04), SkBits2Float(0x43f71bd9), SkBits2Float(0x435ac7d8));  // 491.101f, 221.164f, 494.218f, 218.781f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f7d69d), SkBits2Float(0x4359aa35), SkBits2Float(0x43f8b3b3), SkBits2Float(0x435951c5));  // 495.677f, 217.665f, 497.404f, 217.319f
+if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43f8ba67), SkBits2Float(0x43594f16), SkBits2Float(0x43f8c136), SkBits2Float(0x43594dd9), SkBits2Float(0x3f7fa2b1));  // 497.456f, 217.309f, 497.509f, 217.304f, 0.998576f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43fcc3a8), SkBits2Float(0x43589340), SkBits2Float(0x43ff01dc), SkBits2Float(0x4352e191));  // 505.529f, 216.575f, 510.015f, 210.881f
+if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43ff5113), SkBits2Float(0x4352187b), SkBits2Float(0x43ffb59e), SkBits2Float(0x4352b6e9), SkBits2Float(0x3f3504f3));  // 510.633f, 210.096f, 511.419f, 210.714f, 0.707107f
+if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43ffdc85), SkBits2Float(0x4352f435), SkBits2Float(0x43ffe4a9), SkBits2Float(0x435355e9), SkBits2Float(0x3f6ec0ae));  // 511.723f, 210.954f, 511.786f, 211.336f, 0.932628f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x4400461c), SkBits2Float(0x435b3080), SkBits2Float(0x4400b692), SkBits2Float(0x4360b229));  // 513.095f, 219.189f, 514.853f, 224.696f
+if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x4400c662), SkBits2Float(0x43617856), SkBits2Float(0x44009920), SkBits2Float(0x4361decb), SkBits2Float(0x3f46ad5b));  // 515.1f, 225.47f, 514.393f, 225.87f, 0.776083f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43fb4920), SkBits2Float(0x43688f50), SkBits2Float(0x43f8340f), SkBits2Float(0x4365b887));  // 502.571f, 232.56f, 496.407f, 229.721f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f72cd2), SkBits2Float(0x4364c612), SkBits2Float(0x43f69888), SkBits2Float(0x4362e330));  // 494.35f, 228.774f, 493.192f, 226.887f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f66a00), SkBits2Float(0x43624bae), SkBits2Float(0x43f64c73), SkBits2Float(0x4361ad04));  // 492.828f, 226.296f, 492.597f, 225.676f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f642ea), SkBits2Float(0x436179d2), SkBits2Float(0x43f63c1c), SkBits2Float(0x43614abe));  // 492.523f, 225.476f, 492.47f, 225.292f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f639c9), SkBits2Float(0x43613aa5), SkBits2Float(0x43f63809), SkBits2Float(0x43612cda));  // 492.451f, 225.229f, 492.438f, 225.175f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63777), SkBits2Float(0x43612855), SkBits2Float(0x43f636df), SkBits2Float(0x43612357));  // 492.433f, 225.158f, 492.429f, 225.138f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6368f), SkBits2Float(0x436120b2), SkBits2Float(0x43f6367b), SkBits2Float(0x43612005));  // 492.426f, 225.128f, 492.426f, 225.125f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63656), SkBits2Float(0x43611ebc));  // 492.424f, 225.12f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e34));  // 492.424f, 225.118f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611df3));  // 492.424f, 225.117f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6363e), SkBits2Float(0x43611de5));  // 492.424f, 225.117f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6363f), SkBits2Float(0x43611deb));  // 492.424f, 225.117f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63647), SkBits2Float(0x43611e37));  // 492.424f, 225.118f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63644), SkBits2Float(0x43611e19));  // 492.424f, 225.118f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6365c), SkBits2Float(0x43611ee7), SkBits2Float(0x43f6365d), SkBits2Float(0x43611ef9));  // 492.425f, 225.121f, 492.425f, 225.121f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63666), SkBits2Float(0x43611f4b), SkBits2Float(0x43f63672), SkBits2Float(0x43611fb1));  // 492.425f, 225.122f, 492.425f, 225.124f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f636ab), SkBits2Float(0x436121a4), SkBits2Float(0x43f636e3), SkBits2Float(0x4361236a));  // 492.427f, 225.131f, 492.429f, 225.138f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f636fd), SkBits2Float(0x43612443), SkBits2Float(0x43f63705), SkBits2Float(0x4361247e));  // 492.43f, 225.142f, 492.43f, 225.143f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f637d7), SkBits2Float(0x43612b15), SkBits2Float(0x43f638dc), SkBits2Float(0x436131b0));  // 492.436f, 225.168f, 492.444f, 225.194f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63b88), SkBits2Float(0x43614303), SkBits2Float(0x43f63f62), SkBits2Float(0x43615368));  // 492.465f, 225.262f, 492.495f, 225.326f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6436f), SkBits2Float(0x4361649f), SkBits2Float(0x43f648b2), SkBits2Float(0x43617468));  // 492.527f, 225.393f, 492.568f, 225.455f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f68760), SkBits2Float(0x43623072), SkBits2Float(0x43f6ec71), SkBits2Float(0x4361cb60));  // 493.058f, 226.189f, 493.847f, 225.794f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f722ef), SkBits2Float(0x436194e0), SkBits2Float(0x43f73027), SkBits2Float(0x43611df0));  // 494.273f, 225.582f, 494.376f, 225.117f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f73334), SkBits2Float(0x43610284), SkBits2Float(0x43f73333), SkBits2Float(0x4360e667));  // 494.4f, 225.01f, 494.4f, 224.9f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63638), SkBits2Float(0x43611daf));  // 492.424f, 225.116f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f6b333), SkBits2Float(0x4360e666));  // 493.4f, 224.9f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f639c5), SkBits2Float(0x4361375a));  // 492.451f, 225.216f
+if (testlines & (1LL << i++)) path.close();
+testSimplify(reporter, path, "tiger");
+}
+
+#include "SkRandom.h"
+
+static void tiger8a_h_1(skiatest::Reporter* reporter, const char* ) {
+    uint64_t testlines = 0x0000000000002008;  // best so far: 0x0000001d14c14bb1;
+    tiger8a_x(reporter, testlines);
+}
+
+static void tiger8b_x(skiatest::Reporter* reporter, uint64_t testlines) {
+    SkPath path;
+uint64_t i = 0;
+if (testlines & (1LL << i++)) path.moveTo(SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572));  // 494.349f, 224.584f
+if (testlines & (1LL << i++)) path.conicTo(SkBits2Float(0x43f72ebd), SkBits2Float(0x4360a219), SkBits2Float(0x43f7302e), SkBits2Float(0x4360af1f), SkBits2Float(0x3f7fa741));  // 494.365f, 224.633f, 494.376f, 224.684f, 0.998646f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360e667));  // 492.4f, 224.9f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360ca4b), SkBits2Float(0x43f6363f), SkBits2Float(0x4360aede));  // 492.4f, 224.79f, 492.424f, 224.683f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f64377), SkBits2Float(0x436037ee), SkBits2Float(0x43f679f5), SkBits2Float(0x4360016e));  // 492.527f, 224.218f, 492.953f, 224.006f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6df06), SkBits2Float(0x435f9c5c), SkBits2Float(0x43f71db4), SkBits2Float(0x43605866));  // 493.742f, 223.611f, 494.232f, 224.345f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f722f8), SkBits2Float(0x43606830), SkBits2Float(0x43f72704), SkBits2Float(0x43607966));  // 494.273f, 224.407f, 494.305f, 224.474f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f72ae0), SkBits2Float(0x436089cd), SkBits2Float(0x43f72d8a), SkBits2Float(0x43609b1e));  // 494.335f, 224.538f, 494.356f, 224.606f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f72e8e), SkBits2Float(0x4360a1b8), SkBits2Float(0x43f72f61), SkBits2Float(0x4360a850));  // 494.364f, 224.632f, 494.37f, 224.657f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f72f68), SkBits2Float(0x4360a88a), SkBits2Float(0x43f72f83), SkBits2Float(0x4360a964));  // 494.37f, 224.658f, 494.371f, 224.662f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f72fbb), SkBits2Float(0x4360ab2a), SkBits2Float(0x43f72ff4), SkBits2Float(0x4360ad1d));  // 494.373f, 224.669f, 494.375f, 224.676f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f73000), SkBits2Float(0x4360ad83), SkBits2Float(0x43f73009), SkBits2Float(0x4360add5));  // 494.375f, 224.678f, 494.375f, 224.679f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f7300b), SkBits2Float(0x4360ade9), SkBits2Float(0x43f73022), SkBits2Float(0x4360aeb5));  // 494.375f, 224.679f, 494.376f, 224.682f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f7301f), SkBits2Float(0x4360ae97));  // 494.376f, 224.682f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aee3));  // 494.376f, 224.683f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f73028), SkBits2Float(0x4360aeeb));  // 494.376f, 224.683f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aedf));  // 494.376f, 224.683f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f73021), SkBits2Float(0x4360aeaa));  // 494.376f, 224.682f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f73016), SkBits2Float(0x4360ae50));  // 494.376f, 224.681f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f73007), SkBits2Float(0x4360adc1));  // 494.375f, 224.679f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f72ff9), SkBits2Float(0x4360ad4d));  // 494.375f, 224.677f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f7300d), SkBits2Float(0x4360adf7), SkBits2Float(0x43f73031), SkBits2Float(0x4360af12));  // 494.375f, 224.68f, 494.376f, 224.684f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f730f0), SkBits2Float(0x4360b4f1), SkBits2Float(0x43f7320a), SkBits2Float(0x4360bc94));  // 494.382f, 224.707f, 494.391f, 224.737f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f73625), SkBits2Float(0x4360d8fe), SkBits2Float(0x43f73c59), SkBits2Float(0x4360fa4a));  // 494.423f, 224.848f, 494.471f, 224.978f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f75132), SkBits2Float(0x43616a36), SkBits2Float(0x43f772ac), SkBits2Float(0x4361d738));  // 494.634f, 225.415f, 494.896f, 225.841f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f7de60), SkBits2Float(0x436335ea), SkBits2Float(0x43f89f25), SkBits2Float(0x4363e779));  // 495.737f, 227.211f, 497.243f, 227.904f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43fb3d30), SkBits2Float(0x436650a0), SkBits2Float(0x44005a14), SkBits2Float(0x43602133));  // 502.478f, 230.315f, 513.407f, 224.13f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4400799a), SkBits2Float(0x4360ffff));  // 513.9f, 225
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x44003ca2), SkBits2Float(0x43614dd5));  // 512.947f, 225.304f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43ff92b8), SkBits2Float(0x435ba8f8), SkBits2Float(0x43fee825), SkBits2Float(0x4353aa15));  // 511.146f, 219.66f, 509.814f, 211.664f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43ff6667), SkBits2Float(0x43537fff));  // 510.8f, 211.5f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43ffcaf2), SkBits2Float(0x43541e6d));  // 511.586f, 212.119f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43fd4888), SkBits2Float(0x435a7d38), SkBits2Float(0x43f8d864), SkBits2Float(0x435b4bbf));  // 506.567f, 218.489f, 497.691f, 219.296f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f8cccd), SkBits2Float(0x435a4ccc));  // 497.6f, 218.3f
+if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x43f8e5e7), SkBits2Float(0x435b47d3));  // 497.796f, 219.281f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f84300), SkBits2Float(0x435b88fd), SkBits2Float(0x43f7b75b), SkBits2Float(0x435c5e8e));  // 496.523f, 219.535f, 495.432f, 220.369f
+if (testlines & (1LL << i++)) path.quadTo(SkBits2Float(0x43f6b984), SkBits2Float(0x435de2c4), SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572));  // 493.449f, 221.886f, 494.349f, 224.584f
+if (testlines & (1LL << i++)) path.close();
+testSimplify(reporter, path, "tiger");
+}
+
+static void testTiger(PathOpsThreadState* data) {
+    uint64_t testlines = ((uint64_t) data->fB << 32) | (unsigned int) data->fA;
+    if (data->fC) {
+        tiger8b_x(data->fReporter, testlines);
+    } else {
+        tiger8a_x(data->fReporter, testlines);
+    }
+}
+
+static void tiger_threaded(skiatest::Reporter* reporter, const char* filename) {
+    initializeTests(reporter, "tigerb");
+    PathOpsThreadedTestRunner testRunner(reporter);
+    for (int ab = 0; ab < 2; ++ab) {
+        SkRandom r;
+        int testCount = reporter->allowExtendedTest() ? 10000 : 100;
+        for (int samples = 2; samples < 37; ++samples) {
+            for (int tests = 0; tests < testCount; ++tests) {
+                uint64_t testlines = 0;
+                for (int i = 0; i < samples; ++i) {
+                    int bit;
+                    do {
+                        bit = r.nextRangeU(0, 38);
+                    } while (testlines & (1LL << bit));
+                    testlines |= 1LL << bit;
+                }
+                *testRunner.fRunnables.append() =
+                        new PathOpsThreadedRunnable(&testTiger, 
+                                                    (int) (unsigned) (testlines & 0xFFFFFFFF),
+                                                    (int) (unsigned) (testlines >> 32),
+                                                    ab, 0, &testRunner);
+            }
+        }
+    }
+    testRunner.render();
+}
+
+static void tiger8b_h_1(skiatest::Reporter* reporter, const char* filename) {
+    uint64_t testlines = 0x000000000f27b9e3;  // best so far: 0x000000201304b4a3
+    tiger8b_x(reporter, testlines);
+}
+
+// tries to add same edge twice
+static void tiger8b(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.moveTo(SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572));  // 494.349f, 224.584f
+path.conicTo(SkBits2Float(0x43f72ebd), SkBits2Float(0x4360a219), SkBits2Float(0x43f7302e), SkBits2Float(0x4360af1f), SkBits2Float(0x3f7fa741));  // 494.365f, 224.633f, 494.376f, 224.684f, 0.998646f
+path.lineTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360e667));  // 492.4f, 224.9f
+path.quadTo(SkBits2Float(0x43f63333), SkBits2Float(0x4360ca4b), SkBits2Float(0x43f6363f), SkBits2Float(0x4360aede));  // 492.4f, 224.79f, 492.424f, 224.683f
+path.quadTo(SkBits2Float(0x43f64377), SkBits2Float(0x436037ee), SkBits2Float(0x43f679f5), SkBits2Float(0x4360016e));  // 492.527f, 224.218f, 492.953f, 224.006f
+path.quadTo(SkBits2Float(0x43f6df06), SkBits2Float(0x435f9c5c), SkBits2Float(0x43f71db4), SkBits2Float(0x43605866));  // 493.742f, 223.611f, 494.232f, 224.345f
+path.quadTo(SkBits2Float(0x43f722f8), SkBits2Float(0x43606830), SkBits2Float(0x43f72704), SkBits2Float(0x43607966));  // 494.273f, 224.407f, 494.305f, 224.474f
+path.quadTo(SkBits2Float(0x43f72ae0), SkBits2Float(0x436089cd), SkBits2Float(0x43f72d8a), SkBits2Float(0x43609b1e));  // 494.335f, 224.538f, 494.356f, 224.606f
+path.quadTo(SkBits2Float(0x43f72e8e), SkBits2Float(0x4360a1b8), SkBits2Float(0x43f72f61), SkBits2Float(0x4360a850));  // 494.364f, 224.632f, 494.37f, 224.657f
+path.quadTo(SkBits2Float(0x43f72f68), SkBits2Float(0x4360a88a), SkBits2Float(0x43f72f83), SkBits2Float(0x4360a964));  // 494.37f, 224.658f, 494.371f, 224.662f
+path.quadTo(SkBits2Float(0x43f72fbb), SkBits2Float(0x4360ab2a), SkBits2Float(0x43f72ff4), SkBits2Float(0x4360ad1d));  // 494.373f, 224.669f, 494.375f, 224.676f
+path.quadTo(SkBits2Float(0x43f73000), SkBits2Float(0x4360ad83), SkBits2Float(0x43f73009), SkBits2Float(0x4360add5));  // 494.375f, 224.678f, 494.375f, 224.679f
+path.quadTo(SkBits2Float(0x43f7300b), SkBits2Float(0x4360ade9), SkBits2Float(0x43f73022), SkBits2Float(0x4360aeb5));  // 494.375f, 224.679f, 494.376f, 224.682f
+path.lineTo(SkBits2Float(0x43f7301f), SkBits2Float(0x4360ae97));  // 494.376f, 224.682f
+path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aee3));  // 494.376f, 224.683f
+path.lineTo(SkBits2Float(0x43f73028), SkBits2Float(0x4360aeeb));  // 494.376f, 224.683f
+path.lineTo(SkBits2Float(0x43f73027), SkBits2Float(0x4360aedf));  // 494.376f, 224.683f
+path.lineTo(SkBits2Float(0x43f73021), SkBits2Float(0x4360aeaa));  // 494.376f, 224.682f
+path.lineTo(SkBits2Float(0x43f73016), SkBits2Float(0x4360ae50));  // 494.376f, 224.681f
+path.lineTo(SkBits2Float(0x43f73007), SkBits2Float(0x4360adc1));  // 494.375f, 224.679f
+path.lineTo(SkBits2Float(0x43f72ff9), SkBits2Float(0x4360ad4d));  // 494.375f, 224.677f
+path.quadTo(SkBits2Float(0x43f7300d), SkBits2Float(0x4360adf7), SkBits2Float(0x43f73031), SkBits2Float(0x4360af12));  // 494.375f, 224.68f, 494.376f, 224.684f
+path.quadTo(SkBits2Float(0x43f730f0), SkBits2Float(0x4360b4f1), SkBits2Float(0x43f7320a), SkBits2Float(0x4360bc94));  // 494.382f, 224.707f, 494.391f, 224.737f
+path.quadTo(SkBits2Float(0x43f73625), SkBits2Float(0x4360d8fe), SkBits2Float(0x43f73c59), SkBits2Float(0x4360fa4a));  // 494.423f, 224.848f, 494.471f, 224.978f
+path.quadTo(SkBits2Float(0x43f75132), SkBits2Float(0x43616a36), SkBits2Float(0x43f772ac), SkBits2Float(0x4361d738));  // 494.634f, 225.415f, 494.896f, 225.841f
+path.quadTo(SkBits2Float(0x43f7de60), SkBits2Float(0x436335ea), SkBits2Float(0x43f89f25), SkBits2Float(0x4363e779));  // 495.737f, 227.211f, 497.243f, 227.904f
+path.quadTo(SkBits2Float(0x43fb3d30), SkBits2Float(0x436650a0), SkBits2Float(0x44005a14), SkBits2Float(0x43602133));  // 502.478f, 230.315f, 513.407f, 224.13f
+path.lineTo(SkBits2Float(0x4400799a), SkBits2Float(0x4360ffff));  // 513.9f, 225
+path.lineTo(SkBits2Float(0x44003ca2), SkBits2Float(0x43614dd5));  // 512.947f, 225.304f
+path.quadTo(SkBits2Float(0x43ff92b8), SkBits2Float(0x435ba8f8), SkBits2Float(0x43fee825), SkBits2Float(0x4353aa15));  // 511.146f, 219.66f, 509.814f, 211.664f
+path.lineTo(SkBits2Float(0x43ff6667), SkBits2Float(0x43537fff));  // 510.8f, 211.5f
+path.lineTo(SkBits2Float(0x43ffcaf2), SkBits2Float(0x43541e6d));  // 511.586f, 212.119f
+path.quadTo(SkBits2Float(0x43fd4888), SkBits2Float(0x435a7d38), SkBits2Float(0x43f8d864), SkBits2Float(0x435b4bbf));  // 506.567f, 218.489f, 497.691f, 219.296f
+path.lineTo(SkBits2Float(0x43f8cccd), SkBits2Float(0x435a4ccc));  // 497.6f, 218.3f
+path.lineTo(SkBits2Float(0x43f8e5e7), SkBits2Float(0x435b47d3));  // 497.796f, 219.281f
+path.quadTo(SkBits2Float(0x43f84300), SkBits2Float(0x435b88fd), SkBits2Float(0x43f7b75b), SkBits2Float(0x435c5e8e));  // 496.523f, 219.535f, 495.432f, 220.369f
+path.quadTo(SkBits2Float(0x43f6b984), SkBits2Float(0x435de2c4), SkBits2Float(0x43f72ca1), SkBits2Float(0x43609572));  // 493.449f, 221.886f, 494.349f, 224.584f
+path.close();
+testSimplify(reporter, path, filename);
+}
+
+
+
+static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
+
+static TestDesc tests[] = {
+    TEST(tiger8a_h_1),
+    TEST(tiger8a),
+    TEST(tiger8b_h_1),
+    TEST(tiger8b),
+    TEST(tiger8),
+    TEST(tiger_threaded),
+};
+
+static const size_t testCount = SK_ARRAY_COUNT(tests);
+static bool runReverse = false;
+
+DEF_TEST(PathOpsTiger, reporter) {
+    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
+}
diff --git a/src/third_party/skia/tests/PathOpsTightBoundsTest.cpp b/src/third_party/skia/tests/PathOpsTightBoundsTest.cpp
index cea3752..a2e7bca 100644
--- a/src/third_party/skia/tests/PathOpsTightBoundsTest.cpp
+++ b/src/third_party/skia/tests/PathOpsTightBoundsTest.cpp
@@ -8,7 +8,6 @@
 #include "PathOpsThreadedCommon.h"
 #include "SkCanvas.h"
 #include "SkRandom.h"
-#include "SkTArray.h"
 #include "SkTSort.h"
 #include "Test.h"
 
@@ -40,8 +39,8 @@
     int outerCount = reporter->allowExtendedTest() ? 100 : 1;
     for (int index = 0; index < outerCount; ++index) {
         for (int idx2 = 0; idx2 < 10; ++idx2) {
-            *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
-                    (&testTightBoundsLines, 0, 0, 0, 0, &testRunner));
+            *testRunner.fRunnables.append() =
+                    new PathOpsThreadedRunnable(&testTightBoundsLines, 0, 0, 0, 0, &testRunner);
         }
     }
     testRunner.render();
@@ -115,9 +114,88 @@
     int outerCount = reporter->allowExtendedTest() ? 100 : 1;
     for (int index = 0; index < outerCount; ++index) {
         for (int idx2 = 0; idx2 < 10; ++idx2) {
-            *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
-                    (&testTightBoundsQuads, 0, 0, 0, 0, &testRunner));
+            *testRunner.fRunnables.append() =
+                    new PathOpsThreadedRunnable(&testTightBoundsQuads, 0, 0, 0, 0, &testRunner);
         }
     }
     testRunner.render();
 }
+
+DEF_TEST(PathOpsTightBoundsMove, reporter) {
+    SkPath path;
+    path.moveTo(10, 10);
+    path.close();
+    path.moveTo(20, 20);
+    path.lineTo(20, 20);
+    path.close();
+    path.moveTo(15, 15);
+    path.lineTo(15, 15);
+    path.close();
+    const SkRect& bounds = path.getBounds();
+    SkRect tight;
+    REPORTER_ASSERT(reporter, TightBounds(path, &tight));
+    REPORTER_ASSERT(reporter, bounds == tight);
+}
+
+DEF_TEST(PathOpsTightBoundsMoveOne, reporter) {
+    SkPath path;
+    path.moveTo(20, 20);
+    const SkRect& bounds = path.getBounds();
+    SkRect tight;
+    REPORTER_ASSERT(reporter, TightBounds(path, &tight));
+    REPORTER_ASSERT(reporter, bounds == tight);
+}
+
+DEF_TEST(PathOpsTightBoundsMoveTwo, reporter) {
+    SkPath path;
+    path.moveTo(20, 20);
+    path.moveTo(40, 40);
+    const SkRect& bounds = path.getBounds();
+    SkRect tight;
+    REPORTER_ASSERT(reporter, TightBounds(path, &tight));
+    REPORTER_ASSERT(reporter, bounds == tight);
+}
+
+DEF_TEST(PathOpsTightBoundsTiny, reporter) {
+    SkPath path;
+    path.moveTo(1, 1);
+    path.quadTo(1.000001f, 1, 1, 1);
+    const SkRect& bounds = path.getBounds();
+    SkRect tight;
+    REPORTER_ASSERT(reporter, TightBounds(path, &tight));
+    SkRect moveBounds = {1, 1, 1, 1};
+    REPORTER_ASSERT(reporter, bounds != tight);
+    REPORTER_ASSERT(reporter, moveBounds == tight);
+}
+
+DEF_TEST(PathOpsTightBoundsWellBehaved, reporter) {
+    SkPath path;
+    path.moveTo(1, 1);
+    path.quadTo(2, 3, 4, 5);
+    const SkRect& bounds = path.getBounds();
+    SkRect tight;
+    REPORTER_ASSERT(reporter, TightBounds(path, &tight));
+    REPORTER_ASSERT(reporter, bounds == tight);
+}
+
+DEF_TEST(PathOpsTightBoundsIllBehaved, reporter) {
+    SkPath path;
+    path.moveTo(1, 1);
+    path.quadTo(4, 3, 2, 2);
+    const SkRect& bounds = path.getBounds();
+    SkRect tight;
+    REPORTER_ASSERT(reporter, TightBounds(path, &tight));
+    REPORTER_ASSERT(reporter, bounds != tight);
+}
+
+DEF_TEST(PathOpsTightBoundsIllBehavedScaled, reporter) {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1048578, 1048577, 1048576, 1048576);
+    const SkRect& bounds = path.getBounds();
+    SkRect tight;
+    REPORTER_ASSERT(reporter, TightBounds(path, &tight));
+    REPORTER_ASSERT(reporter, bounds != tight);
+    REPORTER_ASSERT(reporter, tight.right() == 1048576);
+    REPORTER_ASSERT(reporter, tight.bottom() == 1048576);
+}
diff --git a/src/third_party/skia/tests/PathOpsTypesTest.cpp b/src/third_party/skia/tests/PathOpsTypesTest.cpp
old mode 100755
new mode 100644
diff --git a/src/third_party/skia/tests/PathTest.cpp b/src/third_party/skia/tests/PathTest.cpp
index 1f0422d..117da5b 100644
--- a/src/third_party/skia/tests/PathTest.cpp
+++ b/src/third_party/skia/tests/PathTest.cpp
@@ -5,21 +5,69 @@
  * found in the LICENSE file.
  */
 
+#include "SkAutoMalloc.h"
 #include "SkCanvas.h"
+#include "SkGeometry.h"
+#include "SkNullCanvas.h"
 #include "SkPaint.h"
 #include "SkParse.h"
 #include "SkParsePath.h"
-#include "SkPath.h"
 #include "SkPathEffect.h"
+#include "SkPathPriv.h"
 #include "SkRRect.h"
 #include "SkRandom.h"
 #include "SkReader32.h"
 #include "SkSize.h"
 #include "SkStream.h"
+#include "SkStrokeRec.h"
 #include "SkSurface.h"
-#include "SkTypes.h"
 #include "SkWriter32.h"
 #include "Test.h"
+#include <cmath>
+
+
+static void set_radii(SkVector radii[4], int index, float rad) {
+    sk_bzero(radii, sizeof(SkVector) * 4);
+    radii[index].set(rad, rad);
+}
+
+static void test_add_rrect(skiatest::Reporter* reporter, const SkRect& bounds,
+                           const SkVector radii[4]) {
+    SkRRect rrect;
+    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);
+    REPORTER_ASSERT(reporter, bounds == path.getBounds());
+}
+
+static void test_skbug_3469(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.moveTo(20, 20);
+    path.quadTo(20, 50, 80, 50);
+    path.quadTo(20, 50, 20, 80);
+    REPORTER_ASSERT(reporter, !path.isConvex());
+}
+
+static void test_skbug_3239(skiatest::Reporter* reporter) {
+    const float min = SkBits2Float(0xcb7f16c8); /* -16717512.000000 */
+    const float max = SkBits2Float(0x4b7f1c1d); /*  16718877.000000 */
+    const float big = SkBits2Float(0x4b7f1bd7); /*  16718807.000000 */
+
+    const float rad = 33436320;
+
+    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);
+        test_add_rrect(reporter, rectx, radii);
+        test_add_rrect(reporter, recty, radii);
+    }
+}
 
 static void make_path_crbug364224(SkPath* path) {
     path->reset();
@@ -51,10 +99,24 @@
     path->close();
 }
 
+static void test_sect_with_horizontal_needs_pinning() {
+    // Test that sect_with_horizontal in SkLineClipper.cpp needs to pin after computing the
+    // intersection.
+    SkPath path;
+    path.reset();
+    path.moveTo(-540000, -720000);
+    path.lineTo(-9.10000017e-05f, 9.99999996e-13f);
+    path.lineTo(1, 1);
+
+    // Without the pinning code in sect_with_horizontal(), this would assert in the lineclipper
+    SkPaint paint;
+    SkSurface::MakeRasterN32Premul(10, 10)->getCanvas()->drawPath(path, paint);
+}
+
 static void test_path_crbug364224() {
     SkPath path;
     SkPaint paint;
-    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(84, 88));
+    auto surface(SkSurface::MakeRasterN32Premul(84, 88));
     SkCanvas* canvas = surface->getCanvas();
 
     make_path_crbug364224_simplified(&path);
@@ -64,6 +126,112 @@
     canvas->drawPath(path, paint);
 }
 
+// this is a unit test instead of a GM because it doesn't draw anything
+static void test_fuzz_crbug_638223() {
+    auto surface(SkSurface::MakeRasterN32Premul(250, 250));
+    SkCanvas* canvas = surface->getCanvas();
+    SkPath path;
+    path.moveTo(SkBits2Float(0x47452a00), SkBits2Float(0x43211d01));  // 50474, 161.113f
+    path.conicTo(SkBits2Float(0x401c0000), SkBits2Float(0x40680000),
+        SkBits2Float(0x02c25a81), SkBits2Float(0x981a1fa0),
+        SkBits2Float(0x6bf9abea));  // 2.4375f, 3.625f, 2.85577e-37f, -1.992e-24f, 6.03669e+26f
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    canvas->drawPath(path, paint);
+}
+
+static void test_fuzz_crbug_643933() {
+    auto surface(SkSurface::MakeRasterN32Premul(250, 250));
+    SkCanvas* canvas = surface->getCanvas();
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    SkPath path;
+    path.moveTo(0, 0);
+    path.conicTo(SkBits2Float(0x002001f2), SkBits2Float(0x4161ffff),  // 2.93943e-39f, 14.125f
+            SkBits2Float(0x49f7224d), SkBits2Float(0x45eec8df), // 2.02452e+06f, 7641.11f
+            SkBits2Float(0x721aee0c));  // 3.0687e+30f
+    canvas->drawPath(path, paint);
+    path.reset();
+    path.moveTo(0, 0);
+    path.conicTo(SkBits2Float(0x00007ff2), SkBits2Float(0x4169ffff),  // 4.58981e-41f, 14.625f
+        SkBits2Float(0x43ff2261), SkBits2Float(0x41eeea04),  // 510.269f, 29.8643f
+        SkBits2Float(0x5d06eff8));  // 6.07704e+17f
+    canvas->drawPath(path, paint);
+}
+
+static void test_fuzz_crbug_647922() {
+    auto surface(SkSurface::MakeRasterN32Premul(250, 250));
+    SkCanvas* canvas = surface->getCanvas();
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    SkPath path;
+    path.moveTo(0, 0);
+    path.conicTo(SkBits2Float(0x00003939), SkBits2Float(0x42487fff),  // 2.05276e-41f, 50.125f
+            SkBits2Float(0x48082361), SkBits2Float(0x4408e8e9),  // 139406, 547.639f
+            SkBits2Float(0x4d1ade0f));  // 1.6239e+08f
+    canvas->drawPath(path, paint);
+}
+
+static void test_fuzz_crbug_662780() {
+    auto surface(SkSurface::MakeRasterN32Premul(250, 250));
+    SkCanvas* canvas = surface->getCanvas();
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    SkPath path;
+    path.moveTo(SkBits2Float(0x41000000), SkBits2Float(0x431e0000));  // 8, 158
+    path.lineTo(SkBits2Float(0x41000000), SkBits2Float(0x42f00000));  // 8, 120
+    // 8, 8, 8.00002f, 8, 0.707107f
+    path.conicTo(SkBits2Float(0x41000000), SkBits2Float(0x41000000),
+            SkBits2Float(0x41000010), SkBits2Float(0x41000000), SkBits2Float(0x3f3504f3));
+    path.lineTo(SkBits2Float(0x439a0000), SkBits2Float(0x41000000));  // 308, 8
+    // 308, 8, 308, 8, 0.707107f
+    path.conicTo(SkBits2Float(0x439a0000), SkBits2Float(0x41000000),
+            SkBits2Float(0x439a0000), SkBits2Float(0x41000000), SkBits2Float(0x3f3504f3));
+    path.lineTo(SkBits2Float(0x439a0000), SkBits2Float(0x431e0000));  // 308, 158
+    // 308, 158, 308, 158, 0.707107f
+    path.conicTo(SkBits2Float(0x439a0000), SkBits2Float(0x431e0000),
+            SkBits2Float(0x439a0000), SkBits2Float(0x431e0000), SkBits2Float(0x3f3504f3));
+    path.lineTo(SkBits2Float(0x41000000), SkBits2Float(0x431e0000));  // 8, 158
+    // 8, 158, 8, 158, 0.707107f
+    path.conicTo(SkBits2Float(0x41000000), SkBits2Float(0x431e0000),
+            SkBits2Float(0x41000000), SkBits2Float(0x431e0000), SkBits2Float(0x3f3504f3));
+    path.close();
+    canvas->clipPath(path, true);
+    canvas->drawRect(SkRect::MakeWH(250, 250), paint);
+}
+
+static void test_mask_overflow() {
+    auto surface(SkSurface::MakeRasterN32Premul(500, 500));
+    SkCanvas* canvas = surface->getCanvas();
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    SkPath path;
+    path.moveTo(SkBits2Float(0x43e28000), SkBits2Float(0x43aa8000));  // 453, 341
+    path.lineTo(SkBits2Float(0x43de6000), SkBits2Float(0x43aa8000));  // 444.75f, 341
+    // 440.47f, 341, 437, 344.47f, 437, 348.75f
+    path.cubicTo(SkBits2Float(0x43dc3c29), SkBits2Float(0x43aa8000),
+            SkBits2Float(0x43da8000), SkBits2Float(0x43ac3c29),
+            SkBits2Float(0x43da8000), SkBits2Float(0x43ae6000));
+    path.lineTo(SkBits2Float(0x43da8000), SkBits2Float(0x43b18000));  // 437, 355
+    path.lineTo(SkBits2Float(0x43e28000), SkBits2Float(0x43b18000));  // 453, 355
+    path.lineTo(SkBits2Float(0x43e28000), SkBits2Float(0x43aa8000));  // 453, 341
+    canvas->drawPath(path, paint);
+}
+
+static void test_fuzz_crbug_668907() {
+    auto surface(SkSurface::MakeRasterN32Premul(400, 500));
+    SkCanvas* canvas = surface->getCanvas();
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    SkPath path;
+    path.moveTo(SkBits2Float(0x46313741), SkBits2Float(0x3b00e540));  // 11341.8f, 0.00196679f
+    path.quadTo(SkBits2Float(0x41410041), SkBits2Float(0xc1414141), SkBits2Float(0x41414141),
+            SkBits2Float(0x414100ff));  // 12.0626f, -12.0784f, 12.0784f, 12.0627f
+    path.lineTo(SkBits2Float(0x46313741), SkBits2Float(0x3b00e540));  // 11341.8f, 0.00196679f
+    path.close();
+    canvas->drawPath(path, paint);
+}
+
 /**
  * In debug mode, this path was causing an assertion to fail in
  * SkPathStroker::preJoinTo() and, in Release, the use of an unitialized value.
@@ -90,6 +258,19 @@
     stroke.applyToPath(&path, path);
 }
 
+static void test_path_crbugskia5995() {
+    auto surface(SkSurface::MakeRasterN32Premul(500, 500));
+    SkCanvas* canvas = surface->getCanvas();
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    SkPath path;
+    path.moveTo(SkBits2Float(0x40303030), SkBits2Float(0x3e303030));  // 2.75294f, 0.172059f
+    path.quadTo(SkBits2Float(0x41d63030), SkBits2Float(0x30303030), SkBits2Float(0x41013030),
+            SkBits2Float(0x00000000));  // 26.7735f, 6.40969e-10f, 8.07426f, 0
+    path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+    canvas->drawPath(path, paint);
+}
+
 static void make_path0(SkPath* path) {
     // from  *  https://code.google.com/p/skia/issues/detail?id=1706
 
@@ -134,7 +315,7 @@
     }
 }
 
-#if defined(WIN32)
+#ifdef SK_BUILD_FOR_WIN
     #define SUPPRESS_VISIBILITY_WARNING
 #else
     #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
@@ -190,57 +371,6 @@
     REPORTER_ASSERT(reporter, 95 == last.fY);
 }
 
-static void test_android_specific_behavior(skiatest::Reporter* reporter) {
-#ifdef SK_BUILD_FOR_ANDROID
-    // Make sure we treat fGenerationID and fSourcePath correctly for each of
-    // copy, assign, rewind, reset, and swap.
-    SkPath original, source, anotherSource;
-    original.setSourcePath(&source);
-    original.moveTo(0, 0);
-    original.lineTo(1, 1);
-    REPORTER_ASSERT(reporter, original.getSourcePath() == &source);
-
-    uint32_t copyID, assignID;
-
-    // Test copy constructor.  Copy generation ID, copy source path.
-    SkPath copy(original);
-    REPORTER_ASSERT(reporter, copy.getGenerationID() == original.getGenerationID());
-    REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath());
-
-    // Test assigment operator.  Change generation ID, copy source path.
-    SkPath assign;
-    assignID = assign.getGenerationID();
-    assign = original;
-    REPORTER_ASSERT(reporter, assign.getGenerationID() != assignID);
-    REPORTER_ASSERT(reporter, assign.getSourcePath() == original.getSourcePath());
-
-    // Test rewind.  Change generation ID, don't touch source path.
-    copyID = copy.getGenerationID();
-    copy.rewind();
-    REPORTER_ASSERT(reporter, copy.getGenerationID() != copyID);
-    REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath());
-
-    // Test reset.  Change generation ID, don't touch source path.
-    assignID = assign.getGenerationID();
-    assign.reset();
-    REPORTER_ASSERT(reporter, assign.getGenerationID() != assignID);
-    REPORTER_ASSERT(reporter, assign.getSourcePath() == original.getSourcePath());
-
-    // Test swap.  Swap the generation IDs, swap source paths.
-    copy.reset();
-    copy.moveTo(2, 2);
-    copy.setSourcePath(&anotherSource);
-    copyID = copy.getGenerationID();
-    assign.moveTo(3, 3);
-    assignID = assign.getGenerationID();
-    copy.swap(assign);
-    REPORTER_ASSERT(reporter, copy.getGenerationID() != copyID);
-    REPORTER_ASSERT(reporter, assign.getGenerationID() != assignID);
-    REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath());
-    REPORTER_ASSERT(reporter, assign.getSourcePath() == &anotherSource);
-#endif
-}
-
 static void test_gen_id(skiatest::Reporter* reporter) {
     SkPath a, b;
     REPORTER_ASSERT(reporter, a.getGenerationID() == b.getGenerationID());
@@ -275,7 +405,7 @@
     REPORTER_ASSERT(reporter, a.getGenerationID() == x);
     REPORTER_ASSERT(reporter, w != x);
 
-#ifdef SK_BUILD_FOR_ANDROID
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
     static bool kExpectGenIDToIgnoreFill = false;
 #else
     static bool kExpectGenIDToIgnoreFill = true;
@@ -299,7 +429,7 @@
 
     SkPaint paint;
     paint.setAntiAlias(true);
-    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(84, 88));
+    auto surface(SkSurface::MakeRasterN32Premul(84, 88));
     surface->getCanvas()->drawPath(path, paint);
 }
 
@@ -322,7 +452,7 @@
     SkPath dst;
     // Before the fix, this would infinite-recurse, and run out of stack
     // because we would keep trying to subdivide a degenerate cubic segment.
-    paint.getFillPath(path, &dst, NULL);
+    paint.getFillPath(path, &dst, nullptr);
 }
 
 static void build_path_170666(SkPath& path) {
@@ -418,7 +548,7 @@
     SkPaint paint;
     paint.setAntiAlias(true);
 
-    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(1000, 1000));
+    auto surface(SkSurface::MakeRasterN32Premul(1000, 1000));
 
     build_path_simple_170666(path);
     surface->getCanvas()->drawPath(path, paint);
@@ -427,31 +557,104 @@
     surface->getCanvas()->drawPath(path, paint);
 }
 
+
+static void test_tiny_path_convexity(skiatest::Reporter* reporter, const char* pathBug,
+        SkScalar tx, SkScalar ty, SkScalar scale) {
+    SkPath smallPath;
+    SkAssertResult(SkParsePath::FromSVGString(pathBug, &smallPath));
+    bool smallConvex = smallPath.isConvex();
+    SkPath largePath;
+    SkAssertResult(SkParsePath::FromSVGString(pathBug, &largePath));
+    SkMatrix matrix;
+    matrix.reset();
+    matrix.preTranslate(100, 100);
+    matrix.preScale(scale, scale);
+    largePath.transform(matrix);
+    bool largeConvex = largePath.isConvex();
+    REPORTER_ASSERT(reporter, smallConvex == largeConvex);
+}
+
+static void test_crbug_493450(skiatest::Reporter* reporter) {
+    const char reducedCase[] =
+        "M0,0"
+        "L0.0002, 0"
+        "L0.0002, 0.0002"
+        "L0.0001, 0.0001"
+        "L0,0.0002"
+        "Z";
+    test_tiny_path_convexity(reporter, reducedCase, 100, 100, 100000);
+    const char originalFiddleData[] =
+        "M-0.3383152268862998,-0.11217565719203619L-0.33846085183212765,-0.11212264406895281"
+        "L-0.338509393480737,-0.11210607966681395L-0.33857792286700894,-0.1121889121487573"
+        "L-0.3383866116636664,-0.11228834570924921L-0.33842087635680235,-0.11246078673250548"
+        "L-0.33809536177201055,-0.11245415228342878L-0.33797257995493996,-0.11216571641452182"
+        "L-0.33802112160354925,-0.11201996164188659L-0.33819815585141844,-0.11218559834671019Z";
+    test_tiny_path_convexity(reporter, originalFiddleData, 280081.4116670522f, 93268.04618493588f,
+            826357.3384828606f);
+}
+
+static void test_crbug_495894(skiatest::Reporter* reporter) {
+    const char originalFiddleData[] =
+        "M-0.34004273849857214,-0.11332803232216355L-0.34008271397389744,-0.11324483772714951"
+        "L-0.3401940742265893,-0.11324483772714951L-0.34017694188002134,-0.11329807920275889"
+        "L-0.3402026403998733,-0.11333468903941245L-0.34029972369709194,-0.11334134592705701"
+        "L-0.3403054344792813,-0.11344121970007795L-0.3403140006525653,-0.11351115418399343"
+        "L-0.34024261587519866,-0.11353446986281181L-0.3402197727464413,-0.11360442946144192"
+        "L-0.34013696640469604,-0.11359110237029302L-0.34009128014718143,-0.1135877707043939"
+        "L-0.3400598708451401,-0.11360776134112742L-0.34004273849857214,-0.11355112520064405"
+        "L-0.3400113291965308,-0.11355112520064405L-0.3399970522410575,-0.11359110237029302"
+        "L-0.33997135372120546,-0.11355112520064405L-0.3399627875479215,-0.11353780084493197"
+        "L-0.3399485105924481,-0.11350782354357004L-0.3400027630232468,-0.11346452910331437"
+        "L-0.3399485105924481,-0.11340126558629839L-0.33993994441916414,-0.11340126558629839"
+        "L-0.33988283659727087,-0.11331804756574679L-0.33989140277055485,-0.11324483772714951"
+        "L-0.33997991989448945,-0.11324483772714951L-0.3399856306766788,-0.11324483772714951"
+        "L-0.34002560615200417,-0.11334467443478255ZM-0.3400684370184241,-0.11338461985124307"
+        "L-0.340154098751264,-0.11341791238732665L-0.340162664924548,-0.1134378899559977"
+        "L-0.34017979727111597,-0.11340126558629839L-0.3401655203156427,-0.11338129083212668"
+        "L-0.34012268944922275,-0.11332137577529414L-0.34007414780061346,-0.11334467443478255Z"
+        "M-0.3400027630232468,-0.11290567901106024L-0.3400113291965308,-0.11298876531245433"
+        "L-0.33997991989448945,-0.11301535852306784L-0.33990282433493346,-0.11296217481488612"
+        "L-0.33993994441916414,-0.11288906492739594Z";
+    test_tiny_path_convexity(reporter, originalFiddleData, 22682.240000000005f,7819.72220766405f,
+            65536);
+}
+
+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);
+
+    // This call could lead to an assert or uninitialized read due to a failure
+    // to check the return value from SkCubicClipper::ChopMonoAtY.
+    path.contains(-1.84817e-08f, 1.15465e-08f);
+}
+
 static void test_addrect(skiatest::Reporter* reporter) {
     SkPath path;
     path.lineTo(0, 0);
     path.addRect(SkRect::MakeWH(50, 100));
-    REPORTER_ASSERT(reporter, path.isRect(NULL));
+    REPORTER_ASSERT(reporter, path.isRect(nullptr));
 
     path.reset();
     path.lineTo(FLT_EPSILON, FLT_EPSILON);
     path.addRect(SkRect::MakeWH(50, 100));
-    REPORTER_ASSERT(reporter, !path.isRect(NULL));
+    REPORTER_ASSERT(reporter, !path.isRect(nullptr));
 
     path.reset();
     path.quadTo(0, 0, 0, 0);
     path.addRect(SkRect::MakeWH(50, 100));
-    REPORTER_ASSERT(reporter, !path.isRect(NULL));
+    REPORTER_ASSERT(reporter, !path.isRect(nullptr));
 
     path.reset();
     path.conicTo(0, 0, 0, 0, 0.5f);
     path.addRect(SkRect::MakeWH(50, 100));
-    REPORTER_ASSERT(reporter, !path.isRect(NULL));
+    REPORTER_ASSERT(reporter, !path.isRect(nullptr));
 
     path.reset();
     path.cubicTo(0, 0, 0, 0, 0, 0);
     path.addRect(SkRect::MakeWH(50, 100));
-    REPORTER_ASSERT(reporter, !path.isRect(NULL));
+    REPORTER_ASSERT(reporter, !path.isRect(nullptr));
 }
 
 // Make sure we stay non-finite once we get there (unless we reset or rewind).
@@ -497,7 +700,7 @@
 }
 
 static void test_clipped_cubic() {
-    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(640, 480));
+    auto surface(SkSurface::MakeRasterN32Premul(640, 480));
 
     // This path used to assert, because our cubic-chopping code incorrectly
     // moved control points after the chop. This test should be run in SK_DEBUG
@@ -514,6 +717,56 @@
     }
 }
 
+static void dump_if_ne(skiatest::Reporter* reporter, const SkRect& expected, const SkRect& bounds) {
+    if (expected != bounds) {
+        ERRORF(reporter, "path.getBounds() returned [%g %g %g %g], but expected [%g %g %g %g]",
+               bounds.left(), bounds.top(), bounds.right(), bounds.bottom(),
+               expected.left(), expected.top(), expected.right(), expected.bottom());
+    }
+}
+
+static void test_bounds_crbug_513799(skiatest::Reporter* reporter) {
+    SkPath path;
+#if 0
+    // As written these tests were failing on LLVM 4.2 MacMini Release mysteriously, so we've
+    // rewritten them to avoid this (compiler-bug?).
+    REPORTER_ASSERT(reporter, SkRect::MakeLTRB(0, 0, 0, 0) == path.getBounds());
+
+    path.moveTo(-5, -8);
+    REPORTER_ASSERT(reporter, SkRect::MakeLTRB(-5, -8, -5, -8) == path.getBounds());
+
+    path.addRect(SkRect::MakeLTRB(1, 2, 3, 4));
+    REPORTER_ASSERT(reporter, SkRect::MakeLTRB(-5, -8, 3, 4) == path.getBounds());
+
+    path.moveTo(1, 2);
+    REPORTER_ASSERT(reporter, SkRect::MakeLTRB(-5, -8, 3, 4) == path.getBounds());
+#else
+    dump_if_ne(reporter, SkRect::MakeLTRB(0, 0, 0, 0), path.getBounds());
+
+    path.moveTo(-5, -8);    // should set the bounds
+    dump_if_ne(reporter, SkRect::MakeLTRB(-5, -8, -5, -8), path.getBounds());
+
+    path.addRect(SkRect::MakeLTRB(1, 2, 3, 4)); // should extend the bounds
+    dump_if_ne(reporter, SkRect::MakeLTRB(-5, -8, 3, 4), path.getBounds());
+
+    path.moveTo(1, 2);  // don't expect this to have changed the bounds
+    dump_if_ne(reporter, SkRect::MakeLTRB(-5, -8, 3, 4), path.getBounds());
+#endif
+}
+
+#include "SkSurface.h"
+static void test_fuzz_crbug_627414(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.conicTo(3.58732e-43f, 2.72084f, 3.00392f, 3.00392f, 8.46e+37f);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    auto surf = SkSurface::MakeRasterN32Premul(100, 100);
+    surf->getCanvas()->drawPath(path, paint);
+}
+
 // Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
 // which triggered an assert, from a tricky cubic. This test replicates that
 // example, so we can ensure that we handle it (in SkEdge.cpp), and don't
@@ -533,9 +786,7 @@
     SkPaint paint;
     paint.setAntiAlias(true);
 
-    SkSurface* surface = SkSurface::NewRasterPMColor(19, 130);
-    surface->getCanvas()->drawPath(path, paint);
-    surface->unref();
+    SkSurface::MakeRasterN32Premul(19, 130)->getCanvas()->drawPath(path, paint);
 }
 
 // Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
@@ -725,6 +976,23 @@
     test_path_isfinite(reporter);
 }
 
+static void test_islastcontourclosed(skiatest::Reporter* reporter) {
+    SkPath path;
+    REPORTER_ASSERT(reporter, !path.isLastContourClosed());
+    path.moveTo(0, 0);
+    REPORTER_ASSERT(reporter, !path.isLastContourClosed());
+    path.close();
+    REPORTER_ASSERT(reporter, path.isLastContourClosed());
+    path.lineTo(100, 100);
+    REPORTER_ASSERT(reporter, !path.isLastContourClosed());
+    path.moveTo(200, 200);
+    REPORTER_ASSERT(reporter, !path.isLastContourClosed());
+    path.close();
+    REPORTER_ASSERT(reporter, path.isLastContourClosed());
+    path.moveTo(0, 0);
+    REPORTER_ASSERT(reporter, !path.isLastContourClosed());
+}
+
 // assert that we always
 //  start with a moveTo
 //  only have 1 moveTo
@@ -787,7 +1055,7 @@
     for (int doClose = 0; doClose <= 1; ++doClose) {
         for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
             SkPath path;
-            path.addPoly(pts, count, SkToBool(doClose));
+            path.addPoly(pts, SkToInt(count), SkToBool(doClose));
             test_poly(reporter, path, pts, SkToBool(doClose));
         }
     }
@@ -815,30 +1083,30 @@
 
 // Set this for paths that don't have a consistent direction such as a bowtie.
 // (cheapComputeDirection is not expected to catch these.)
-static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
+const SkPathPriv::FirstDirection kDontCheckDir = static_cast<SkPathPriv::FirstDirection>(-1);
 
 static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
-                            SkPath::Direction expected) {
+                            SkPathPriv::FirstDirection expected) {
     if (expected == kDontCheckDir) {
         return;
     }
     SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
 
-    SkPath::Direction dir;
-    if (copy.cheapComputeDirection(&dir)) {
+    SkPathPriv::FirstDirection dir;
+    if (SkPathPriv::CheapComputeFirstDirection(copy, &dir)) {
         REPORTER_ASSERT(reporter, dir == expected);
     } else {
-        REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
+        REPORTER_ASSERT(reporter, SkPathPriv::kUnknown_FirstDirection == expected);
     }
 }
 
 static void test_direction(skiatest::Reporter* reporter) {
     size_t i;
     SkPath path;
-    REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
-    REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
-    REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
-    REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
+    REPORTER_ASSERT(reporter, !SkPathPriv::CheapComputeFirstDirection(path, nullptr));
+    REPORTER_ASSERT(reporter, !SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCW_FirstDirection));
+    REPORTER_ASSERT(reporter, !SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCCW_FirstDirection));
+    REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kUnknown_FirstDirection));
 
     static const char* gDegen[] = {
         "M 10 10",
@@ -852,7 +1120,7 @@
         path.reset();
         bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
         REPORTER_ASSERT(reporter, valid);
-        REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
+        REPORTER_ASSERT(reporter, !SkPathPriv::CheapComputeFirstDirection(path, nullptr));
     }
 
     static const char* gCW[] = {
@@ -868,7 +1136,7 @@
         path.reset();
         bool valid = SkParsePath::FromSVGString(gCW[i], &path);
         REPORTER_ASSERT(reporter, valid);
-        check_direction(reporter, path, SkPath::kCW_Direction);
+        check_direction(reporter, path, SkPathPriv::kCW_FirstDirection);
     }
 
     static const char* gCCW[] = {
@@ -884,7 +1152,7 @@
         path.reset();
         bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
         REPORTER_ASSERT(reporter, valid);
-        check_direction(reporter, path, SkPath::kCCW_Direction);
+        check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection);
     }
 
     // Test two donuts, each wound a different direction. Only the outer contour
@@ -892,12 +1160,12 @@
     path.reset();
     path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
     path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
-    check_direction(reporter, path, SkPath::kCW_Direction);
+    check_direction(reporter, path, SkPathPriv::kCW_FirstDirection);
 
     path.reset();
     path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
     path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
-    check_direction(reporter, path, SkPath::kCCW_Direction);
+    check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection);
 
     // triangle with one point really far from the origin.
     path.reset();
@@ -905,19 +1173,19 @@
     path.moveTo(SkBits2Float(0x501c7652), SkBits2Float(0x501c7652));
     path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
     path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
-    check_direction(reporter, path, SkPath::kCCW_Direction);
+    check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection);
 
     path.reset();
     path.conicTo(20, 0, 20, 20, 0.5f);
     path.close();
-    check_direction(reporter, path, SkPath::kCW_Direction);
+    check_direction(reporter, path, SkPathPriv::kCW_FirstDirection);
 
     path.reset();
     path.lineTo(1, 1e7f);
     path.lineTo(1e7f, 2e7f);
     path.close();
     REPORTER_ASSERT(reporter, SkPath::kConvex_Convexity == path.getConvexity());
-    check_direction(reporter, path, SkPath::kCCW_Direction);
+    check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection);
 }
 
 static void add_rect(SkPath* path, const SkRect& r) {
@@ -1084,19 +1352,47 @@
     REPORTER_ASSERT(reporter, c == expected);
 }
 
+static void test_path_crbug389050(skiatest::Reporter* reporter) {
+    SkPath  tinyConvexPolygon;
+    tinyConvexPolygon.moveTo(600.131559f, 800.112512f);
+    tinyConvexPolygon.lineTo(600.161735f, 800.118627f);
+    tinyConvexPolygon.lineTo(600.148962f, 800.142338f);
+    tinyConvexPolygon.lineTo(600.134891f, 800.137724f);
+    tinyConvexPolygon.close();
+    tinyConvexPolygon.getConvexity();
+    check_convexity(reporter, tinyConvexPolygon, SkPath::kConvex_Convexity);
+    check_direction(reporter, tinyConvexPolygon, SkPathPriv::kCW_FirstDirection);
+
+    SkPath  platTriangle;
+    platTriangle.moveTo(0, 0);
+    platTriangle.lineTo(200, 0);
+    platTriangle.lineTo(100, 0.04f);
+    platTriangle.close();
+    platTriangle.getConvexity();
+    check_direction(reporter, platTriangle, SkPathPriv::kCW_FirstDirection);
+
+    platTriangle.reset();
+    platTriangle.moveTo(0, 0);
+    platTriangle.lineTo(200, 0);
+    platTriangle.lineTo(100, 0.03f);
+    platTriangle.close();
+    platTriangle.getConvexity();
+    check_direction(reporter, platTriangle, SkPathPriv::kCW_FirstDirection);
+}
+
 static void test_convexity2(skiatest::Reporter* reporter) {
     SkPath pt;
     pt.moveTo(0, 0);
     pt.close();
     check_convexity(reporter, pt, SkPath::kConvex_Convexity);
-    check_direction(reporter, pt, SkPath::kUnknown_Direction);
+    check_direction(reporter, pt, SkPathPriv::kUnknown_FirstDirection);
 
     SkPath line;
     line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
     line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
     line.close();
     check_convexity(reporter, line, SkPath::kConvex_Convexity);
-    check_direction(reporter, line, SkPath::kUnknown_Direction);
+    check_direction(reporter, line, SkPathPriv::kUnknown_FirstDirection);
 
     SkPath triLeft;
     triLeft.moveTo(0, 0);
@@ -1104,7 +1400,7 @@
     triLeft.lineTo(SK_Scalar1, SK_Scalar1);
     triLeft.close();
     check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
-    check_direction(reporter, triLeft, SkPath::kCW_Direction);
+    check_direction(reporter, triLeft, SkPathPriv::kCW_FirstDirection);
 
     SkPath triRight;
     triRight.moveTo(0, 0);
@@ -1112,7 +1408,7 @@
     triRight.lineTo(SK_Scalar1, SK_Scalar1);
     triRight.close();
     check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
-    check_direction(reporter, triRight, SkPath::kCCW_Direction);
+    check_direction(reporter, triRight, SkPathPriv::kCCW_FirstDirection);
 
     SkPath square;
     square.moveTo(0, 0);
@@ -1121,7 +1417,7 @@
     square.lineTo(0, SK_Scalar1);
     square.close();
     check_convexity(reporter, square, SkPath::kConvex_Convexity);
-    check_direction(reporter, square, SkPath::kCW_Direction);
+    check_direction(reporter, square, SkPathPriv::kCW_FirstDirection);
 
     SkPath redundantSquare;
     redundantSquare.moveTo(0, 0);
@@ -1138,7 +1434,7 @@
     redundantSquare.lineTo(0, SK_Scalar1);
     redundantSquare.close();
     check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
-    check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
+    check_direction(reporter, redundantSquare, SkPathPriv::kCW_FirstDirection);
 
     SkPath bowTie;
     bowTie.moveTo(0, 0);
@@ -1177,9 +1473,9 @@
     dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
     dent.close();
     check_convexity(reporter, dent, SkPath::kConcave_Convexity);
-    check_direction(reporter, dent, SkPath::kCW_Direction);
+    check_direction(reporter, dent, SkPathPriv::kCW_FirstDirection);
 
-    // http://skbug.com/2235
+    // https://bug.skia.org/2235
     SkPath strokedSin;
     for (int i = 0; i < 2000; i++) {
         SkScalar x = SkIntToScalar(i) / 2;
@@ -1205,7 +1501,21 @@
     degenerateConcave.lineTo(-55.971577f, 460.0f);
     degenerateConcave.lineTo(41.446522f, 376.25f);
     check_convexity(reporter, degenerateConcave, SkPath::kConcave_Convexity);
-    check_direction(reporter, degenerateConcave, SkPath::kUnknown_Direction);
+    check_direction(reporter, degenerateConcave, SkPathPriv::kUnknown_FirstDirection);
+
+    // http://crbug.com/433683
+    SkPath badFirstVector;
+    badFirstVector.moveTo(501.087708f, 319.610352f);
+    badFirstVector.lineTo(501.087708f, 319.610352f);
+    badFirstVector.cubicTo(501.087677f, 319.610321f, 449.271606f, 258.078674f, 395.084564f, 198.711182f);
+    badFirstVector.cubicTo(358.967072f, 159.140717f, 321.910553f, 120.650436f, 298.442322f, 101.955399f);
+    badFirstVector.lineTo(301.557678f, 98.044601f);
+    badFirstVector.cubicTo(325.283844f, 116.945084f, 362.615204f, 155.720825f, 398.777557f, 195.340454f);
+    badFirstVector.cubicTo(453.031860f, 254.781662f, 504.912262f, 316.389618f, 504.912292f, 316.389648f);
+    badFirstVector.lineTo(504.912292f, 316.389648f);
+    badFirstVector.lineTo(501.087708f, 319.610352f);
+    badFirstVector.close();
+    check_convexity(reporter, badFirstVector, SkPath::kConcave_Convexity);
 }
 
 static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
@@ -1228,7 +1538,7 @@
     while (str) {
         SkScalar x, y;
         str = SkParse::FindScalar(str, &x);
-        if (NULL == str) {
+        if (nullptr == str) {
             break;
         }
         str = SkParse::FindScalar(str, &y);
@@ -1254,26 +1564,30 @@
     path.reset();
     path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
     check_convexity(reporter, path, SkPath::kConvex_Convexity);
-    REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
+    REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCCW_FirstDirection));
 
     path.reset();
     path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
     check_convexity(reporter, path, SkPath::kConvex_Convexity);
-    REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
+    REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCW_FirstDirection));
+
+    path.reset();
+    path.quadTo(100, 100, 50, 50); // This is a convex path from GM:convexpaths
+    check_convexity(reporter, path, SkPath::kConvex_Convexity);
 
     static const struct {
-        const char*         fPathStr;
-        SkPath::Convexity   fExpectedConvexity;
-        SkPath::Direction   fExpectedDirection;
+        const char*                 fPathStr;
+        SkPath::Convexity           fExpectedConvexity;
+        SkPathPriv::FirstDirection  fExpectedDirection;
     } gRec[] = {
-        { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
-        { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
-        { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
-        { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
-        { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
-        { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
+        { "", SkPath::kConvex_Convexity, SkPathPriv::kUnknown_FirstDirection },
+        { "0 0", SkPath::kConvex_Convexity, SkPathPriv::kUnknown_FirstDirection },
+        { "0 0 10 10", SkPath::kConvex_Convexity, SkPathPriv::kUnknown_FirstDirection },
+        { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPathPriv::kUnknown_FirstDirection },
+        { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPathPriv::kCW_FirstDirection },
+        { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPathPriv::kCCW_FirstDirection },
         { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
-        { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
+        { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPathPriv::kCW_FirstDirection },
     };
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
@@ -1284,9 +1598,9 @@
         // check after setting the initial convex and direction
         if (kDontCheckDir != gRec[i].fExpectedDirection) {
             SkPath copy(path);
-            SkPath::Direction dir;
-            bool foundDir = copy.cheapComputeDirection(&dir);
-            REPORTER_ASSERT(reporter, (gRec[i].fExpectedDirection == SkPath::kUnknown_Direction)
+            SkPathPriv::FirstDirection dir;
+            bool foundDir = SkPathPriv::CheapComputeFirstDirection(copy, &dir);
+            REPORTER_ASSERT(reporter, (gRec[i].fExpectedDirection == SkPathPriv::kUnknown_FirstDirection)
                     ^ foundDir);
             REPORTER_ASSERT(reporter, !foundDir || gRec[i].fExpectedDirection == dir);
             check_convexity(reporter, copy, gRec[i].fExpectedConvexity);
@@ -1294,6 +1608,79 @@
         REPORTER_ASSERT(reporter, gRec[i].fExpectedConvexity == path.getConvexity());
         check_direction(reporter, path, gRec[i].fExpectedDirection);
     }
+
+    static const SkPoint nonFinitePts[] = {
+        { SK_ScalarInfinity, 0 },
+        { 0, SK_ScalarInfinity },
+        { SK_ScalarInfinity, SK_ScalarInfinity },
+        { SK_ScalarNegativeInfinity, 0},
+        { 0, SK_ScalarNegativeInfinity },
+        { SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity },
+        { SK_ScalarNegativeInfinity, SK_ScalarInfinity },
+        { SK_ScalarInfinity, SK_ScalarNegativeInfinity },
+        { SK_ScalarNaN, 0 },
+        { 0, SK_ScalarNaN },
+        { SK_ScalarNaN, SK_ScalarNaN },
+    };
+
+    const size_t nonFinitePtsCount = sizeof(nonFinitePts) / sizeof(nonFinitePts[0]);
+
+    static const SkPoint finitePts[] = {
+        { SK_ScalarMax, 0 },
+        { 0, SK_ScalarMax },
+        { SK_ScalarMax, SK_ScalarMax },
+        { SK_ScalarMin, 0 },
+        { 0, SK_ScalarMin },
+        { SK_ScalarMin, SK_ScalarMin },
+    };
+
+    const size_t finitePtsCount = sizeof(finitePts) / sizeof(finitePts[0]);
+
+    for (int index = 0; index < (int) (13 * nonFinitePtsCount * finitePtsCount); ++index) {
+        int i = (int) (index % nonFinitePtsCount);
+        int f = (int) (index % finitePtsCount);
+        int g = (int) ((f + 1) % finitePtsCount);
+        path.reset();
+        switch (index % 13) {
+            case 0: path.lineTo(nonFinitePts[i]); break;
+            case 1: path.quadTo(nonFinitePts[i], nonFinitePts[i]); break;
+            case 2: path.quadTo(nonFinitePts[i], finitePts[f]); break;
+            case 3: path.quadTo(finitePts[f], nonFinitePts[i]); break;
+            case 4: path.cubicTo(nonFinitePts[i], finitePts[f], finitePts[f]); break;
+            case 5: path.cubicTo(finitePts[f], nonFinitePts[i], finitePts[f]); break;
+            case 6: path.cubicTo(finitePts[f], finitePts[f], nonFinitePts[i]); break;
+            case 7: path.cubicTo(nonFinitePts[i], nonFinitePts[i], finitePts[f]); break;
+            case 8: path.cubicTo(nonFinitePts[i], finitePts[f], nonFinitePts[i]); break;
+            case 9: path.cubicTo(finitePts[f], nonFinitePts[i], nonFinitePts[i]); break;
+            case 10: path.cubicTo(nonFinitePts[i], nonFinitePts[i], nonFinitePts[i]); break;
+            case 11: path.cubicTo(nonFinitePts[i], finitePts[f], finitePts[g]); break;
+            case 12: path.moveTo(nonFinitePts[i]); break;
+        }
+        check_convexity(reporter, path, SkPath::kUnknown_Convexity);
+    }
+
+    for (int index = 0; index < (int) (11 * finitePtsCount); ++index) {
+        int f = (int) (index % finitePtsCount);
+        int g = (int) ((f + 1) % finitePtsCount);
+        path.reset();
+        int curveSelect = index % 11;
+        switch (curveSelect) {
+            case 0: path.moveTo(finitePts[f]); break;
+            case 1: path.lineTo(finitePts[f]); break;
+            case 2: path.quadTo(finitePts[f], finitePts[f]); break;
+            case 3: path.quadTo(finitePts[f], finitePts[g]); break;
+            case 4: path.quadTo(finitePts[g], finitePts[f]); break;
+            case 5: path.cubicTo(finitePts[f], finitePts[f], finitePts[f]); break;
+            case 6: path.cubicTo(finitePts[f], finitePts[f], finitePts[g]); break;
+            case 7: path.cubicTo(finitePts[f], finitePts[g], finitePts[f]); break;
+            case 8: path.cubicTo(finitePts[f], finitePts[g], finitePts[g]); break;
+            case 9: path.cubicTo(finitePts[g], finitePts[f], finitePts[f]); break;
+            case 10: path.cubicTo(finitePts[g], finitePts[f], finitePts[g]); break;
+        }
+        check_convexity(reporter, path, curveSelect == 0 ? SkPath::kConvex_Convexity
+                : SkPath::kUnknown_Convexity);
+    }
+
 }
 
 static void test_isLine(skiatest::Reporter* reporter) {
@@ -1301,7 +1688,7 @@
     SkPoint pts[2];
     const SkScalar value = SkIntToScalar(5);
 
-    REPORTER_ASSERT(reporter, !path.isLine(NULL));
+    REPORTER_ASSERT(reporter, !path.isLine(nullptr));
 
     // set some non-zero values
     pts[0].set(value, value);
@@ -1316,7 +1703,7 @@
     REPORTER_ASSERT(reporter, value != moveX && value != moveY);
 
     path.moveTo(moveX, moveY);
-    REPORTER_ASSERT(reporter, !path.isLine(NULL));
+    REPORTER_ASSERT(reporter, !path.isLine(nullptr));
     REPORTER_ASSERT(reporter, !path.isLine(pts));
     // check that pts was untouched
     REPORTER_ASSERT(reporter, pts[0].equals(value, value));
@@ -1327,7 +1714,7 @@
     REPORTER_ASSERT(reporter, value != lineX && value != lineY);
 
     path.lineTo(lineX, lineY);
-    REPORTER_ASSERT(reporter, path.isLine(NULL));
+    REPORTER_ASSERT(reporter, path.isLine(nullptr));
 
     REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
     REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
@@ -1336,14 +1723,14 @@
     REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
 
     path.lineTo(0, 0);  // too many points/verbs
-    REPORTER_ASSERT(reporter, !path.isLine(NULL));
+    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);
-    REPORTER_ASSERT(reporter, !path.isLine(NULL));
+    REPORTER_ASSERT(reporter, !path.isLine(nullptr));
 }
 
 static void test_conservativelyContains(skiatest::Reporter* reporter) {
@@ -1354,7 +1741,7 @@
 
     // A circle that bounds kBaseRect (with a significant amount of slop)
     SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
-    circleR = SkScalarMul(circleR, 1.75f) / 2;
+    circleR *= 1.75f / 2;
     static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
 
     // round-rect radii
@@ -1519,7 +1906,7 @@
     // Test that multiple move commands do not cause asserts.
 
     // At the time of writing, this would not modify cached convexity. This caused an assert while
-    // checking conservative containment again. http://skbug.com/1460
+    // checking conservative containment again. https://bug.skia.org/1460
     path.moveTo(SkIntToScalar(100), SkIntToScalar(100));
 #if 0
     REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
@@ -1538,6 +1925,18 @@
                                                                                SkIntToScalar(10),
                                                                                SkIntToScalar(10))));
 
+    // 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);
+
+    REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
+                                                                               SkIntToScalar(10),
+                                                                               SkIntToScalar(10))));
+
     // Test that multiple move commands do not cause asserts and that the function
     // is not confused by the multiple moves.
     path.reset();
@@ -1565,10 +1964,8 @@
     path.moveTo(0, 0); path.lineTo(1, 0); path.lineTo(1, 1); path.lineTo(0, 1);
     path.close();
 
-    REPORTER_ASSERT(reporter, path.isRect(NULL, NULL));
-    REPORTER_ASSERT(reporter, path.isRect(&isClosed, NULL));
+    REPORTER_ASSERT(reporter, path.isRect(nullptr, &isClosed, nullptr));
     REPORTER_ASSERT(reporter, isClosed);
-    REPORTER_ASSERT(reporter, SkPath::kStroke_PathAsRect == path.asRect(NULL));
 }
 
 // Simple isRect test is inline TestPath, below.
@@ -1619,7 +2016,7 @@
 
     struct IsRectTest {
         SkPoint *fPoints;
-        size_t fPointCount;
+        int fPointCount;
         bool fClose;
         bool fIsRect;
     } tests[] = {
@@ -1661,7 +2058,7 @@
     };
 
     const size_t testCount = SK_ARRAY_COUNT(tests);
-    size_t index;
+    int index;
     for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
         SkPath path;
         path.moveTo(tests[testIndex].fPoints[0].fX, tests[testIndex].fPoints[0].fY);
@@ -1671,105 +2068,222 @@
         if (tests[testIndex].fClose) {
             path.close();
         }
-        REPORTER_ASSERT(reporter, tests[testIndex].fIsRect == path.isRect(NULL));
-        REPORTER_ASSERT(reporter, tests[testIndex].fIsRect == path.isRect(NULL, NULL));
+        REPORTER_ASSERT(reporter, tests[testIndex].fIsRect == path.isRect(nullptr));
 
         if (tests[testIndex].fIsRect) {
             SkRect computed, expected;
-            expected.set(tests[testIndex].fPoints, tests[testIndex].fPointCount);
-            REPORTER_ASSERT(reporter, path.isRect(&computed));
-            REPORTER_ASSERT(reporter, expected == computed);
-
             bool isClosed;
-            SkPath::Direction direction, cheapDirection;
-            REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
-            REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
+            SkPath::Direction direction;
+            SkPathPriv::FirstDirection cheapDirection;
+            expected.set(tests[testIndex].fPoints, tests[testIndex].fPointCount);
+            REPORTER_ASSERT(reporter, SkPathPriv::CheapComputeFirstDirection(path, &cheapDirection));
+            REPORTER_ASSERT(reporter, path.isRect(&computed, &isClosed, &direction));
+            REPORTER_ASSERT(reporter, expected == computed);
             REPORTER_ASSERT(reporter, isClosed == tests[testIndex].fClose);
-            REPORTER_ASSERT(reporter, direction == cheapDirection);
-            direction = (SkPath::Direction) -1;
-            if (!tests[testIndex].fClose) {
-                REPORTER_ASSERT(reporter, SkPath::kFill_PathAsRect == path.asRect());
-                REPORTER_ASSERT(reporter, SkPath::kFill_PathAsRect == path.asRect(&direction));
-            } else {
-                REPORTER_ASSERT(reporter, SkPath::kStroke_PathAsRect == path.asRect());
-                REPORTER_ASSERT(reporter, SkPath::kStroke_PathAsRect == path.asRect(&direction));
-            }
-            REPORTER_ASSERT(reporter, direction == cheapDirection);
+            REPORTER_ASSERT(reporter, SkPathPriv::AsFirstDirection(direction) == cheapDirection);
         } else {
             SkRect computed;
             computed.set(123, 456, 789, 1011);
-            REPORTER_ASSERT(reporter, !path.isRect(&computed));
+            bool isClosed = (bool)-1;
+            SkPath::Direction direction = (SkPath::Direction) - 1;
+            REPORTER_ASSERT(reporter, !path.isRect(&computed, &isClosed, &direction));
             REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
             REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
-
-            bool isClosed = (bool) -1;
-            SkPath::Direction direction = (SkPath::Direction) -1;
-            REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
             REPORTER_ASSERT(reporter, isClosed == (bool) -1);
             REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
-            REPORTER_ASSERT(reporter, SkPath::kNone_PathAsRect == path.asRect());
-            REPORTER_ASSERT(reporter, SkPath::kNone_PathAsRect == path.asRect(&direction));
-            REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
         }
     }
 
     // fail, close then line
     SkPath path1;
     path1.moveTo(r1[0].fX, r1[0].fY);
-    for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
+    for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
         path1.lineTo(r1[index].fX, r1[index].fY);
     }
     path1.close();
     path1.lineTo(1, 0);
-    REPORTER_ASSERT(reporter, !path1.isRect(NULL));
+    REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
 
     // fail, move in the middle
     path1.reset();
     path1.moveTo(r1[0].fX, r1[0].fY);
-    for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
+    for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
         if (index == 2) {
             path1.moveTo(1, .5f);
         }
         path1.lineTo(r1[index].fX, r1[index].fY);
     }
     path1.close();
-    REPORTER_ASSERT(reporter, !path1.isRect(NULL));
+    REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
 
     // fail, move on the edge
     path1.reset();
-    for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
+    for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
         path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
         path1.lineTo(r1[index].fX, r1[index].fY);
     }
     path1.close();
-    REPORTER_ASSERT(reporter, !path1.isRect(NULL));
+    REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
 
     // fail, quad
     path1.reset();
     path1.moveTo(r1[0].fX, r1[0].fY);
-    for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
+    for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
         if (index == 2) {
             path1.quadTo(1, .5f, 1, .5f);
         }
         path1.lineTo(r1[index].fX, r1[index].fY);
     }
     path1.close();
-    REPORTER_ASSERT(reporter, !path1.isRect(NULL));
+    REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
 
     // fail, cubic
     path1.reset();
     path1.moveTo(r1[0].fX, r1[0].fY);
-    for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
+    for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
         if (index == 2) {
             path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
         }
         path1.lineTo(r1[index].fX, r1[index].fY);
     }
     path1.close();
-    REPORTER_ASSERT(reporter, !path1.isRect(NULL));
+    REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
 }
 
-static void test_isNestedRects(skiatest::Reporter* reporter) {
+static void check_simple_closed_rect(skiatest::Reporter* reporter, const SkPath& path,
+                                     const SkRect& rect, SkPath::Direction dir, unsigned start) {
+    SkRect r = SkRect::MakeEmpty();
+    SkPath::Direction d = SkPath::kCCW_Direction;
+    unsigned s = ~0U;
+
+    REPORTER_ASSERT(reporter, SkPathPriv::IsSimpleClosedRect(path, &r, &d, &s));
+    REPORTER_ASSERT(reporter, r == rect);
+    REPORTER_ASSERT(reporter, d == dir);
+    REPORTER_ASSERT(reporter, s == start);
+}
+
+static void test_is_simple_closed_rect(skiatest::Reporter* reporter) {
+    SkRect r = SkRect::MakeEmpty();
+    SkPath::Direction d = SkPath::kCCW_Direction;
+    unsigned s = ~0U;
+
+    const SkRect testRect = SkRect::MakeXYWH(10, 10, 50, 70);
+    const SkRect emptyRect = SkRect::MakeEmpty();
+    SkPath path;
+    for (int start = 0; start < 4; ++start) {
+        for (auto dir : {SkPath::kCCW_Direction, SkPath::kCW_Direction}) {
+            SkPath path;
+            path.addRect(testRect, dir, start);
+            check_simple_closed_rect(reporter, path, testRect, dir, start);
+            path.close();
+            check_simple_closed_rect(reporter, path, testRect, dir, start);
+            SkPath path2 = path;
+            path2.lineTo(10, 10);
+            REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
+            path2 = path;
+            path2.moveTo(10, 10);
+            REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
+            path2 = path;
+            path2.addRect(testRect, dir, start);
+            REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
+            // Make the path by hand, manually closing it.
+            path2.reset();
+            SkPath::RawIter iter(path);
+            SkPath::Verb v;
+            SkPoint verbPts[4];
+            SkPoint firstPt = {0.f, 0.f};
+            while ((v = iter.next(verbPts)) != SkPath::kDone_Verb) {
+                switch(v) {
+                    case SkPath::kMove_Verb:
+                        firstPt = verbPts[0];
+                        path2.moveTo(verbPts[0]);
+                        break;
+                    case SkPath::kLine_Verb:
+                        path2.lineTo(verbPts[1]);
+                        break;
+                    default:
+                        break;
+                }
+            }
+            // We haven't closed it yet...
+            REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
+            // ... now we do and test again.
+            path2.lineTo(firstPt);
+            check_simple_closed_rect(reporter, path2, testRect, dir, start);
+            // A redundant close shouldn't cause a failure.
+            path2.close();
+            check_simple_closed_rect(reporter, path2, testRect, dir, start);
+            // Degenerate point and line rects are not allowed
+            path2.reset();
+            path2.addRect(emptyRect, dir, start);
+            REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
+            SkRect degenRect = testRect;
+            degenRect.fLeft = degenRect.fRight;
+            path2.reset();
+            path2.addRect(degenRect, dir, start);
+            REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
+            degenRect = testRect;
+            degenRect.fTop = degenRect.fBottom;
+            path2.reset();
+            path2.addRect(degenRect, dir, start);
+            REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
+            // An inverted rect makes a rect path, but changes the winding dir and start point.
+            SkPath::Direction swapDir = (dir == SkPath::kCW_Direction)
+                                            ? SkPath::kCCW_Direction
+                                            : SkPath::kCW_Direction;
+            static constexpr unsigned kXSwapStarts[] = { 1, 0, 3, 2 };
+            static constexpr unsigned kYSwapStarts[] = { 3, 2, 1, 0 };
+            SkRect swapRect = testRect;
+            SkTSwap(swapRect.fLeft, swapRect.fRight);
+            path2.reset();
+            path2.addRect(swapRect, dir, start);
+            check_simple_closed_rect(reporter, path2, testRect, swapDir, kXSwapStarts[start]);
+            swapRect = testRect;
+            SkTSwap(swapRect.fTop, swapRect.fBottom);
+            path2.reset();
+            path2.addRect(swapRect, dir, start);
+            check_simple_closed_rect(reporter, path2, testRect, swapDir, kYSwapStarts[start]);
+        }
+    }
+    // down, up, left, close
+    path.reset();
+    path.moveTo(1, 1);
+    path.lineTo(1, 2);
+    path.lineTo(1, 1);
+    path.lineTo(0, 1);
+    SkRect rect;
+    SkPath::Direction  dir;
+    unsigned start;
+    path.close();
+    REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start));
+    // right, left, up, close
+    path.reset();
+    path.moveTo(1, 1);
+    path.lineTo(2, 1);
+    path.lineTo(1, 1);
+    path.lineTo(1, 0);
+    path.close();
+    REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start));
+    // parallelogram with horizontal edges
+    path.reset();
+    path.moveTo(1, 0);
+    path.lineTo(3, 0);
+    path.lineTo(2, 1);
+    path.lineTo(0, 1);
+    path.close();
+    REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start));
+    // parallelogram with vertical edges
+    path.reset();
+    path.moveTo(0, 1);
+    path.lineTo(0, 3);
+    path.lineTo(1, 2);
+    path.lineTo(1, 0);
+    path.close();
+    REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start));
+
+}
+
+static void test_isNestedFillRects(skiatest::Reporter* reporter) {
     // passing tests (all moveTo / lineTo...
     SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
     SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
@@ -1796,47 +2310,47 @@
     SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
     SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
 
-    // failing, no close
+    // success, no close is OK
     SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
     SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
 
     struct IsNestedRectTest {
         SkPoint *fPoints;
-        size_t fPointCount;
-        SkPath::Direction fDirection;
+        int fPointCount;
+        SkPathPriv::FirstDirection fDirection;
         bool fClose;
         bool fIsNestedRect; // nests with path.addRect(-1, -1, 2, 2);
     } tests[] = {
-        { r1, SK_ARRAY_COUNT(r1), SkPath::kCW_Direction , true, true },
-        { r2, SK_ARRAY_COUNT(r2), SkPath::kCW_Direction , true, true },
-        { r3, SK_ARRAY_COUNT(r3), SkPath::kCW_Direction , true, true },
-        { r4, SK_ARRAY_COUNT(r4), SkPath::kCW_Direction , true, true },
-        { r5, SK_ARRAY_COUNT(r5), SkPath::kCCW_Direction, true, true },
-        { r6, SK_ARRAY_COUNT(r6), SkPath::kCCW_Direction, true, true },
-        { r7, SK_ARRAY_COUNT(r7), SkPath::kCCW_Direction, true, true },
-        { r8, SK_ARRAY_COUNT(r8), SkPath::kCCW_Direction, true, true },
-        { r9, SK_ARRAY_COUNT(r9), SkPath::kCCW_Direction, true, true },
-        { ra, SK_ARRAY_COUNT(ra), SkPath::kCCW_Direction, true, true },
-        { rb, SK_ARRAY_COUNT(rb), SkPath::kCW_Direction,  true, true },
-        { rc, SK_ARRAY_COUNT(rc), SkPath::kCW_Direction,  true, true },
-        { rd, SK_ARRAY_COUNT(rd), SkPath::kCCW_Direction, true, true },
-        { re, SK_ARRAY_COUNT(re), SkPath::kCW_Direction,  true, true },
+        { r1, SK_ARRAY_COUNT(r1), SkPathPriv::kCW_FirstDirection , true, true },
+        { r2, SK_ARRAY_COUNT(r2), SkPathPriv::kCW_FirstDirection , true, true },
+        { r3, SK_ARRAY_COUNT(r3), SkPathPriv::kCW_FirstDirection , true, true },
+        { r4, SK_ARRAY_COUNT(r4), SkPathPriv::kCW_FirstDirection , true, true },
+        { r5, SK_ARRAY_COUNT(r5), SkPathPriv::kCCW_FirstDirection, true, true },
+        { r6, SK_ARRAY_COUNT(r6), SkPathPriv::kCCW_FirstDirection, true, true },
+        { r7, SK_ARRAY_COUNT(r7), SkPathPriv::kCCW_FirstDirection, true, true },
+        { r8, SK_ARRAY_COUNT(r8), SkPathPriv::kCCW_FirstDirection, true, true },
+        { r9, SK_ARRAY_COUNT(r9), SkPathPriv::kCCW_FirstDirection, true, true },
+        { ra, SK_ARRAY_COUNT(ra), SkPathPriv::kCCW_FirstDirection, true, true },
+        { rb, SK_ARRAY_COUNT(rb), SkPathPriv::kCW_FirstDirection,  true, true },
+        { rc, SK_ARRAY_COUNT(rc), SkPathPriv::kCW_FirstDirection,  true, true },
+        { rd, SK_ARRAY_COUNT(rd), SkPathPriv::kCCW_FirstDirection, true, true },
+        { re, SK_ARRAY_COUNT(re), SkPathPriv::kCW_FirstDirection,  true, true },
 
-        { f1, SK_ARRAY_COUNT(f1), SkPath::kUnknown_Direction, true, false },
-        { f2, SK_ARRAY_COUNT(f2), SkPath::kUnknown_Direction, true, false },
-        { f3, SK_ARRAY_COUNT(f3), SkPath::kUnknown_Direction, true, false },
-        { f4, SK_ARRAY_COUNT(f4), SkPath::kUnknown_Direction, true, false },
-        { f5, SK_ARRAY_COUNT(f5), SkPath::kUnknown_Direction, true, false },
-        { f6, SK_ARRAY_COUNT(f6), SkPath::kUnknown_Direction, true, false },
-        { f7, SK_ARRAY_COUNT(f7), SkPath::kUnknown_Direction, true, false },
-        { f8, SK_ARRAY_COUNT(f8), SkPath::kUnknown_Direction, true, false },
+        { f1, SK_ARRAY_COUNT(f1), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f2, SK_ARRAY_COUNT(f2), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f3, SK_ARRAY_COUNT(f3), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f4, SK_ARRAY_COUNT(f4), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f5, SK_ARRAY_COUNT(f5), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f6, SK_ARRAY_COUNT(f6), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f7, SK_ARRAY_COUNT(f7), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f8, SK_ARRAY_COUNT(f8), SkPathPriv::kUnknown_FirstDirection, true, false },
 
-        { c1, SK_ARRAY_COUNT(c1), SkPath::kUnknown_Direction, false, false },
-        { c2, SK_ARRAY_COUNT(c2), SkPath::kUnknown_Direction, false, false },
+        { c1, SK_ARRAY_COUNT(c1), SkPathPriv::kCW_FirstDirection, false, true },
+        { c2, SK_ARRAY_COUNT(c2), SkPathPriv::kCW_FirstDirection, false, true },
     };
 
     const size_t testCount = SK_ARRAY_COUNT(tests);
-    size_t index;
+    int index;
     for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
         for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
             SkPath path;
@@ -1853,25 +2367,27 @@
             if (!rectFirst) {
                 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
             }
-            REPORTER_ASSERT(reporter, tests[testIndex].fIsNestedRect == path.isNestedRects(NULL));
+            REPORTER_ASSERT(reporter,
+                    tests[testIndex].fIsNestedRect == path.isNestedFillRects(nullptr));
             if (tests[testIndex].fIsNestedRect) {
                 SkRect expected[2], computed[2];
-                SkPath::Direction expectedDirs[2], computedDirs[2];
+                SkPathPriv::FirstDirection expectedDirs[2];
+                SkPath::Direction computedDirs[2];
                 SkRect testBounds;
                 testBounds.set(tests[testIndex].fPoints, tests[testIndex].fPointCount);
                 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
                 expected[1] = testBounds;
                 if (rectFirst) {
-                    expectedDirs[0] = SkPath::kCW_Direction;
+                    expectedDirs[0] = SkPathPriv::kCW_FirstDirection;
                 } else {
-                    expectedDirs[0] = SkPath::kCCW_Direction;
+                    expectedDirs[0] = SkPathPriv::kCCW_FirstDirection;
                 }
                 expectedDirs[1] = tests[testIndex].fDirection;
-                REPORTER_ASSERT(reporter, path.isNestedRects(computed, computedDirs));
+                REPORTER_ASSERT(reporter, path.isNestedFillRects(computed, computedDirs));
                 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
                 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
-                REPORTER_ASSERT(reporter, expectedDirs[0] == computedDirs[0]);
-                REPORTER_ASSERT(reporter, expectedDirs[1] == computedDirs[1]);
+                REPORTER_ASSERT(reporter, expectedDirs[0] == SkPathPriv::AsFirstDirection(computedDirs[0]));
+                REPORTER_ASSERT(reporter, expectedDirs[1] == SkPathPriv::AsFirstDirection(computedDirs[1]));
             }
         }
 
@@ -1881,7 +2397,7 @@
             path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
         }
         path1.moveTo(r1[0].fX, r1[0].fY);
-        for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
+        for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
             path1.lineTo(r1[index].fX, r1[index].fY);
         }
         path1.close();
@@ -1889,7 +2405,7 @@
         if (!rectFirst) {
             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
         }
-        REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
+        REPORTER_ASSERT(reporter, !path1.isNestedFillRects(nullptr));
 
         // fail, move in the middle
         path1.reset();
@@ -1897,7 +2413,7 @@
             path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
         }
         path1.moveTo(r1[0].fX, r1[0].fY);
-        for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
+        for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
             if (index == 2) {
                 path1.moveTo(1, .5f);
             }
@@ -1907,14 +2423,14 @@
         if (!rectFirst) {
             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
         }
-        REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
+        REPORTER_ASSERT(reporter, !path1.isNestedFillRects(nullptr));
 
         // fail, move on the edge
         path1.reset();
         if (rectFirst) {
             path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
         }
-        for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
+        for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
             path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
             path1.lineTo(r1[index].fX, r1[index].fY);
         }
@@ -1922,7 +2438,7 @@
         if (!rectFirst) {
             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
         }
-        REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
+        REPORTER_ASSERT(reporter, !path1.isNestedFillRects(nullptr));
 
         // fail, quad
         path1.reset();
@@ -1930,7 +2446,7 @@
             path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
         }
         path1.moveTo(r1[0].fX, r1[0].fY);
-        for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
+        for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
             if (index == 2) {
                 path1.quadTo(1, .5f, 1, .5f);
             }
@@ -1940,7 +2456,7 @@
         if (!rectFirst) {
             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
         }
-        REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
+        REPORTER_ASSERT(reporter, !path1.isNestedFillRects(nullptr));
 
         // fail, cubic
         path1.reset();
@@ -1948,7 +2464,7 @@
             path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
         }
         path1.moveTo(r1[0].fX, r1[0].fY);
-        for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
+        for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
             if (index == 2) {
                 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
             }
@@ -1958,15 +2474,29 @@
         if (!rectFirst) {
             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
         }
-        REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
+        REPORTER_ASSERT(reporter, !path1.isNestedFillRects(nullptr));
 
         // fail,  not nested
         path1.reset();
         path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
         path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
-        REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
+        REPORTER_ASSERT(reporter, !path1.isNestedFillRects(nullptr));
     }
 
+    //  pass, constructed explicitly from manually closed rects specified as moves/lines.
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(10, 0);
+    path.lineTo(10, 10);
+    path.lineTo(0, 10);
+    path.lineTo(0, 0);
+    path.moveTo(1, 1);
+    path.lineTo(9, 1);
+    path.lineTo(9, 9);
+    path.lineTo(1, 9);
+    path.lineTo(1, 1);
+    REPORTER_ASSERT(reporter, path.isNestedFillRects(nullptr));
+
     // pass, stroke rect
     SkPath src, dst;
     src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
@@ -1974,7 +2504,7 @@
     strokePaint.setStyle(SkPaint::kStroke_Style);
     strokePaint.setStrokeWidth(2);
     strokePaint.getFillPath(src, &dst);
-    REPORTER_ASSERT(reporter, dst.isNestedRects(NULL));
+    REPORTER_ASSERT(reporter, dst.isNestedFillRects(nullptr));
 }
 
 static void write_and_read_back(skiatest::Reporter* reporter,
@@ -1994,14 +2524,91 @@
     REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
                               p.getConvexityOrUnknown());
 
-    REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
-
+    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();
 
     REPORTER_ASSERT(reporter, origBounds == readBackBounds);
 }
 
+static void test_corrupt_flattening(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.moveTo(1, 2);
+    path.lineTo(1, 2);
+    path.quadTo(1, 2, 3, 4);
+    path.conicTo(1, 2, 3, 4, 0.5f);
+    path.cubicTo(1, 2, 3, 4, 5, 6);
+    uint8_t buffer[1024];
+    SkDEBUGCODE(size_t size =) path.writeToMemory(buffer);
+    SkASSERT(size <= sizeof(buffer));
+    
+    // find where the counts and verbs are stored : from the impl in SkPathRef.cpp
+    int32_t* vCount = (int32_t*)&buffer[16];
+    SkASSERT(*vCount == 5);
+    int32_t* pCount = (int32_t*)&buffer[20];
+    SkASSERT(*pCount == 9);
+    int32_t* cCount = (int32_t*)&buffer[24];
+    SkASSERT(*cCount == 1);
+    uint8_t* verbs = &buffer[28];
+    
+    REPORTER_ASSERT(reporter, path.readFromMemory(buffer, sizeof(buffer)));
+    
+    // check that we detect under/over-flow of counts
+    
+    *vCount += 1;
+    REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer)));
+    *vCount -= 1;   // restore
+    
+    *pCount += 1;
+    REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer)));
+    *pCount -= 2;
+    REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer)));
+    *pCount += 1;   // restore
+    
+    *cCount += 1;
+    REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer)));
+    *cCount -= 2;
+    REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer)));
+    *cCount += 1;   // restore
+    
+    // Check that we detect when the verbs indicate more or fewer pts/conics
+    
+    uint8_t save = verbs[0];
+    SkASSERT(save == SkPath::kCubic_Verb);
+    verbs[0] = SkPath::kQuad_Verb;
+    REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer)));
+    verbs[0] = save;
+    
+    save = verbs[1];
+    SkASSERT(save == SkPath::kConic_Verb);
+    verbs[1] = SkPath::kQuad_Verb;
+    REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer)));
+    verbs[1] = SkPath::kCubic_Verb;
+    REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer)));
+    verbs[1] = save;
+    
+    // Check that we detect invalid verbs
+    save = verbs[1];
+    verbs[1] = 17;
+    REPORTER_ASSERT(reporter, !path.readFromMemory(buffer, sizeof(buffer)));
+    verbs[1] = save;
+}
+
 static void test_flattening(skiatest::Reporter* reporter) {
     SkPath p;
 
@@ -2021,7 +2628,7 @@
     // create a buffer that should be much larger than the path so we don't
     // kill our stack if writer goes too far.
     char buffer[1024];
-    size_t size1 = p.writeToMemory(NULL);
+    size_t size1 = p.writeToMemory(nullptr);
     size_t size2 = p.writeToMemory(buffer);
     REPORTER_ASSERT(reporter, size1 == size2);
 
@@ -2050,6 +2657,8 @@
 
         write_and_read_back(reporter, oval);
     }
+
+    test_corrupt_flattening(reporter);
 }
 
 static void test_transform(skiatest::Reporter* reporter) {
@@ -2104,14 +2713,14 @@
     {
         SkMatrix matrix;
         matrix.reset();
-        matrix.setPerspX(SkScalarToPersp(4));
+        matrix.setPerspX(4);
 
         SkPath p1;
         p1.moveTo(SkPoint::Make(0, 0));
 
         p.transform(matrix, &p1);
         REPORTER_ASSERT(reporter, matrix.invert(&matrix));
-        p1.transform(matrix, NULL);
+        p1.transform(matrix, nullptr);
         SkRect pBounds = p.getBounds();
         SkRect p1Bounds = p1.getBounds();
         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pBounds.fLeft, p1Bounds.fLeft));
@@ -2130,7 +2739,7 @@
         p1.moveTo(SkPoint::Make(0, 0));
 
         p.transform(matrix, &p1);
-        REPORTER_ASSERT(reporter, p1.cheapIsDirection(SkPath::kCW_Direction));
+        REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(p1, SkPathPriv::kCW_FirstDirection));
     }
 
 
@@ -2142,7 +2751,7 @@
         p1.moveTo(SkPoint::Make(0, 0)); // Make p1 unique (i.e., not empty path)
 
         p.transform(matrix, &p1);
-        REPORTER_ASSERT(reporter, p1.cheapIsDirection(SkPath::kCCW_Direction));
+        REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(p1, SkPathPriv::kCCW_FirstDirection));
     }
 
     {
@@ -2152,7 +2761,7 @@
         p1.moveTo(SkPoint::Make(0, 0)); // Make p1 unique (i.e., not empty path)
 
         p.transform(matrix, &p1);
-        REPORTER_ASSERT(reporter, p1.cheapIsDirection(SkPath::kUnknown_Direction));
+        REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(p1, SkPathPriv::kUnknown_FirstDirection));
     }
 }
 
@@ -2191,9 +2800,9 @@
         SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
     };
     static const struct zeroPathTestData gZeroLengthTests[] = {
-        { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
+        { "M 1 1", 1, {1, 1, 1, 1}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
         { "M 1 1 M 2 1", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
-        { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
+        { "M 1 1 z", 1, {1, 1, 1, 1}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
         { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
         { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
         { "M 1 1 L 1 1 M 2 1 L 2 1", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs6, SK_ARRAY_COUNT(resultVerbs6) },
@@ -2423,6 +3032,20 @@
     REPORTER_ASSERT(reporter, SkPath::kDone_Verb == iter.next(pts, true));
 
     // The GM degeneratesegments.cpp test is more extensive
+
+    // 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);
+    p.reset();
+    p.addRRect(rr);
+    iter.setPath(p, false);
+    REPORTER_ASSERT(reporter, SkPath::kMove_Verb == iter.next(pts));
+    REPORTER_ASSERT(reporter, SkPath::kLine_Verb == iter.next(pts));
+    REPORTER_ASSERT(reporter, SkPath::kLine_Verb == iter.next(pts));
+    REPORTER_ASSERT(reporter, SkPath::kConic_Verb == iter.next(pts));
+    REPORTER_ASSERT(reporter, SK_ScalarRoot2Over2 == iter.conicWeight());
 }
 
 static void test_raw_iter(skiatest::Reporter* reporter) {
@@ -2485,14 +3108,10 @@
     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
     REPORTER_ASSERT(reporter, pts[0].fY == 0);
     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
-    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
-    REPORTER_ASSERT(reporter, pts[0].fY == 0);
     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
     REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
-    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
-    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
     REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
@@ -2500,8 +3119,6 @@
     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
     REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
-    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
-    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
 
     // Generate random paths and verify
@@ -2644,7 +3261,6 @@
                     numIterPts += 3;
                     break;
                 case SkPath::kClose_Verb:
-                    REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
                     lastPt = lastMoveTo;
                     break;
                 default:
@@ -2659,37 +3275,42 @@
 static void check_for_circle(skiatest::Reporter* reporter,
                              const SkPath& path,
                              bool expectedCircle,
-                             SkPath::Direction expectedDir) {
+                             SkPathPriv::FirstDirection expectedDir) {
     SkRect rect = SkRect::MakeEmpty();
     REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
-    REPORTER_ASSERT(reporter, path.cheapIsDirection(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,
                              const SkPath& path,
-                             SkPath::Direction dir) {
+                             SkPathPriv::FirstDirection dir) {
     SkPath tmp;
 
     SkMatrix m;
     m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
     path.transform(m, &tmp);
     // this matrix reverses the direction.
-    if (SkPath::kCCW_Direction == dir) {
-        dir = SkPath::kCW_Direction;
+    if (SkPathPriv::kCCW_FirstDirection == dir) {
+        dir = SkPathPriv::kCW_FirstDirection;
     } else {
-        REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir);
-        dir = SkPath::kCCW_Direction;
+        REPORTER_ASSERT(reporter, SkPathPriv::kCW_FirstDirection == dir);
+        dir = SkPathPriv::kCCW_FirstDirection;
     }
     check_for_circle(reporter, tmp, false, dir);
 }
 
 static void test_circle_translate(skiatest::Reporter* reporter,
                                   const SkPath& path,
-                                  SkPath::Direction dir) {
+                                  SkPathPriv::FirstDirection dir) {
     SkPath tmp;
 
     // translate at small offset
@@ -2709,7 +3330,7 @@
 
 static void test_circle_rotate(skiatest::Reporter* reporter,
                                const SkPath& path,
-                               SkPath::Direction dir) {
+                               SkPathPriv::FirstDirection dir) {
     for (int angle = 0; angle < 360; ++angle) {
         SkPath tmp;
         SkMatrix m;
@@ -2729,37 +3350,35 @@
 
 static void test_circle_mirror_x(skiatest::Reporter* reporter,
                                  const SkPath& path,
-                                 SkPath::Direction dir) {
+                                 SkPathPriv::FirstDirection dir) {
     SkPath tmp;
     SkMatrix m;
     m.reset();
     m.setScaleX(-SK_Scalar1);
     path.transform(m, &tmp);
-
-    if (SkPath::kCW_Direction == dir) {
-        dir = SkPath::kCCW_Direction;
+    if (SkPathPriv::kCW_FirstDirection == dir) {
+        dir = SkPathPriv::kCCW_FirstDirection;
     } else {
-        REPORTER_ASSERT(reporter, SkPath::kCCW_Direction == dir);
-        dir = SkPath::kCW_Direction;
+        REPORTER_ASSERT(reporter, SkPathPriv::kCCW_FirstDirection == dir);
+        dir = SkPathPriv::kCW_FirstDirection;
     }
-
     check_for_circle(reporter, tmp, true, dir);
 }
 
 static void test_circle_mirror_y(skiatest::Reporter* reporter,
                                  const SkPath& path,
-                                 SkPath::Direction dir) {
+                                 SkPathPriv::FirstDirection dir) {
     SkPath tmp;
     SkMatrix m;
     m.reset();
     m.setScaleY(-SK_Scalar1);
     path.transform(m, &tmp);
 
-    if (SkPath::kCW_Direction == dir) {
-        dir = SkPath::kCCW_Direction;
+    if (SkPathPriv::kCW_FirstDirection == dir) {
+        dir = SkPathPriv::kCCW_FirstDirection;
     } else {
-        REPORTER_ASSERT(reporter, SkPath::kCCW_Direction == dir);
-        dir = SkPath::kCW_Direction;
+        REPORTER_ASSERT(reporter, SkPathPriv::kCCW_FirstDirection == dir);
+        dir = SkPathPriv::kCW_FirstDirection;
     }
 
     check_for_circle(reporter, tmp, true, dir);
@@ -2767,7 +3386,7 @@
 
 static void test_circle_mirror_xy(skiatest::Reporter* reporter,
                                  const SkPath& path,
-                                 SkPath::Direction dir) {
+                                 SkPathPriv::FirstDirection dir) {
     SkPath tmp;
     SkMatrix m;
     m.reset();
@@ -2779,20 +3398,13 @@
 }
 
 static void test_circle_with_direction(skiatest::Reporter* reporter,
-                                       SkPath::Direction dir) {
+                                       SkPath::Direction inDir) {
+    const SkPathPriv::FirstDirection dir = SkPathPriv::AsFirstDirection(inDir);
     SkPath path;
 
     // circle at origin
-    path.addCircle(0, 0, SkIntToScalar(20), dir);
-    check_for_circle(reporter, path, true, dir);
-    test_circle_rotate(reporter, path, dir);
-    test_circle_translate(reporter, path, dir);
-    test_circle_skew(reporter, path, dir);
+    path.addCircle(0, 0, SkIntToScalar(20), inDir);
 
-    // circle at an offset at (10, 10)
-    path.reset();
-    path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
-                   SkIntToScalar(20), dir);
     check_for_circle(reporter, path, true, dir);
     test_circle_rotate(reporter, path, dir);
     test_circle_translate(reporter, path, dir);
@@ -2800,6 +3412,31 @@
     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();
+    path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
+                   SkIntToScalar(20), inDir);
+
+    check_for_circle(reporter, path, true, dir);
+    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);
+
+    // 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) {
@@ -2808,8 +3445,8 @@
     SkPath rect;
     SkPath empty;
 
-    static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
-    static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
+    const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
+    const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
 
     circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
     rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
@@ -2824,17 +3461,18 @@
     // empty + circle (translate)
     path = empty;
     path.addPath(circle, translate);
-    check_for_circle(reporter, path, false, kCircleDir);
+    check_for_circle(reporter, path, false, SkPathPriv::AsFirstDirection(kCircleDir));
 
     // circle + empty (translate)
     path = circle;
     path.addPath(empty, translate);
-    check_for_circle(reporter, path, true, kCircleDir);
+
+    check_for_circle(reporter, path, true, SkPathPriv::AsFirstDirection(kCircleDir));
 
     // test reverseAddPath
     path = circle;
     path.reverseAddPath(rect);
-    check_for_circle(reporter, path, false, kCircleDirOpposite);
+    check_for_circle(reporter, path, false, SkPathPriv::AsFirstDirection(kCircleDirOpposite));
 }
 
 static void test_circle(skiatest::Reporter* reporter) {
@@ -2845,19 +3483,19 @@
     SkPath path;
     path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
     path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
-    check_for_circle(reporter, path, false, SkPath::kCW_Direction);
+    check_for_circle(reporter, path, false, SkPathPriv::kCW_FirstDirection);
 
     // some extra lineTo() would make isOval() fail
     path.reset();
     path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
     path.lineTo(0, 0);
-    check_for_circle(reporter, path, false, SkPath::kCW_Direction);
+    check_for_circle(reporter, path, false, SkPathPriv::kCW_FirstDirection);
 
     // not back to the original point
     path.reset();
     path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
     path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
-    check_for_circle(reporter, path, false, SkPath::kCW_Direction);
+    check_for_circle(reporter, path, false, SkPathPriv::kCW_FirstDirection);
 
     test_circle_with_add_paths(reporter);
 
@@ -2871,55 +3509,63 @@
     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);
 
-    REPORTER_ASSERT(reporter, path.isOval(NULL));
+    // 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(NULL));
+    // 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));
     tmp.reset();
     path.transform(m, &tmp);
     // an oval rotated 30 degrees is not an oval anymore.
-    REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
+    REPORTER_ASSERT(reporter, !tmp.isOval(nullptr));
 
     // since empty path being transformed.
     path.reset();
     tmp.reset();
     m.reset();
     path.transform(m, &tmp);
-    REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
+    REPORTER_ASSERT(reporter, !tmp.isOval(nullptr));
 
     // empty path is not an oval
     tmp.reset();
-    REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
+    REPORTER_ASSERT(reporter, !tmp.isOval(nullptr));
 
     // only has moveTo()s
     tmp.reset();
     tmp.moveTo(0, 0);
     tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
-    REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
+    REPORTER_ASSERT(reporter, !tmp.isOval(nullptr));
 
     // mimic WebKit's calling convention,
     // call moveTo() first and then call addOval()
     path.reset();
     path.moveTo(0, 0);
     path.addOval(rect);
-    REPORTER_ASSERT(reporter, path.isOval(NULL));
+    REPORTER_ASSERT(reporter, path.isOval(nullptr));
 
     // copy path
     path.reset();
     tmp.reset();
     tmp.addOval(rect);
     path = tmp;
-    REPORTER_ASSERT(reporter, path.isOval(NULL));
+    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) {
@@ -2939,12 +3585,21 @@
 static void test_rrect_is_convex(skiatest::Reporter* reporter, SkPath* path,
                                  SkPath::Direction dir) {
     REPORTER_ASSERT(reporter, path->isConvex());
-    REPORTER_ASSERT(reporter, path->cheapIsDirection(dir));
+    REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(*path, SkPathPriv::AsFirstDirection(dir)));
     path->setConvexity(SkPath::kUnknown_Convexity);
     REPORTER_ASSERT(reporter, path->isConvex());
     path->reset();
 }
 
+static void test_rrect_convexity_is_unknown(skiatest::Reporter* reporter, SkPath* path,
+                                 SkPath::Direction dir) {
+    REPORTER_ASSERT(reporter, path->isConvex());
+    REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(*path, SkPathPriv::AsFirstDirection(dir)));
+    path->setConvexity(SkPath::kUnknown_Convexity);
+    REPORTER_ASSERT(reporter, path->getConvexity() == SkPath::kUnknown_Convexity);
+    path->reset();
+}
+
 static void test_rrect(skiatest::Reporter* reporter) {
     SkPath p;
     SkRRect rr;
@@ -2981,7 +3636,7 @@
     p.addRRect(rr);
     bool closed;
     SkPath::Direction dir;
-    REPORTER_ASSERT(reporter, p.isRect(&closed, &dir));
+    REPORTER_ASSERT(reporter, p.isRect(nullptr, &closed, &dir));
     REPORTER_ASSERT(reporter, closed);
     REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir);
     test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
@@ -3000,11 +3655,13 @@
     SkRect largeR = {0, 0, SK_ScalarMax, SK_ScalarMax};
     rr.setRectRadii(largeR, radii);
     p.addRRect(rr);
-    test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
+    test_rrect_convexity_is_unknown(reporter, &p, SkPath::kCW_Direction);
+
+    // we check for non-finites
     SkRect infR = {0, 0, SK_ScalarMax, SK_ScalarInfinity};
     rr.setRectRadii(infR, radii);
-    p.addRRect(rr);
-    test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
+    REPORTER_ASSERT(reporter, rr.isEmpty());
+
     SkRect tinyR = {0, 0, 1e-9f, 1e-9f};
     p.addRoundRect(tinyR, 5e-11f, 5e-11f);
     test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
@@ -3023,21 +3680,85 @@
     p.reset();
     SkPath cwOval;
     cwOval.addOval(oval);
-    p.addArc(oval, 1, 360);
+    p.addArc(oval, 0, 360);
     REPORTER_ASSERT(reporter, p == cwOval);
     p.reset();
     SkPath ccwOval;
     ccwOval.addOval(oval, SkPath::kCCW_Direction);
-    p.addArc(oval, 1, -360);
+    p.addArc(oval, 0, -360);
     REPORTER_ASSERT(reporter, p == ccwOval);
     p.reset();
     p.addArc(oval, 1, 180);
     REPORTER_ASSERT(reporter, p.isConvex());
-    REPORTER_ASSERT(reporter, p.cheapIsDirection(SkPath::kCW_Direction));
+    REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(p, SkPathPriv::kCW_FirstDirection));
     p.setConvexity(SkPath::kUnknown_Convexity);
     REPORTER_ASSERT(reporter, p.isConvex());
 }
 
+static inline SkScalar oval_start_index_to_angle(unsigned start) {
+    switch (start) {
+        case 0:
+            return 270.f;
+        case 1:
+            return 0.f;
+        case 2:
+            return 90.f;
+        case 3:
+            return 180.f;
+        default:
+            return -1.f;
+    }
+}
+
+static inline SkScalar canonical_start_angle(float angle) {
+    while (angle < 0.f) {
+        angle += 360.f;
+    }
+    while (angle >= 360.f) {
+        angle -= 360.f;
+    }
+    return angle;
+}
+
+static void check_oval_arc(skiatest::Reporter* reporter, SkScalar start, SkScalar sweep,
+                           const SkPath& path) {
+    SkRect r = SkRect::MakeEmpty();
+    SkPath::Direction d = SkPath::kCCW_Direction;
+    unsigned s = ~0U;
+    bool isOval = path.isOval(&r, &d, &s);
+    REPORTER_ASSERT(reporter, isOval);
+    SkPath recreatedPath;
+    recreatedPath.addOval(r, d, s);
+    REPORTER_ASSERT(reporter, path == recreatedPath);
+    REPORTER_ASSERT(reporter, oval_start_index_to_angle(s) == canonical_start_angle(start));
+    REPORTER_ASSERT(reporter, (SkPath::kCW_Direction == d) == (sweep > 0.f));
+}
+
+static void test_arc_ovals(skiatest::Reporter* reporter) {
+    SkRect oval = SkRect::MakeWH(10, 20);
+    for (SkScalar sweep : {-720.f, -540.f, -360.f, 360.f, 432.f, 720.f}) {
+        for (SkScalar start = -360.f; start <= 360.f; start += 1.f) {
+            SkPath path;
+            path.addArc(oval, start, sweep);
+            // SkPath's interfaces for inserting and extracting ovals only allow contours
+            // to start at multiples of 90 degrees.
+            if (std::fmod(start, 90.f) == 0) {
+                check_oval_arc(reporter, start, sweep, path);
+            } else {
+                REPORTER_ASSERT(reporter, !path.isOval(nullptr));
+            }
+        }
+        // Test start angles that are nearly at valid oval start angles.
+        for (float start : {-180.f, -90.f, 90.f, 180.f}) {
+            for (float delta : {-SK_ScalarNearlyZero, SK_ScalarNearlyZero}) {
+                SkPath path;
+                path.addArc(oval, start + delta, sweep);
+                check_oval_arc(reporter, start, sweep, path);
+            }
+        }
+    }
+}
+
 static void check_move(skiatest::Reporter* reporter, SkPath::RawIter* iter,
                        SkScalar x0, SkScalar y0) {
     SkPoint pts[4];
@@ -3118,6 +3839,13 @@
     check_done_and_reset(reporter, p, &iter);
 }
 
+static bool nearly_equal(const SkRect& a, const SkRect& b) {
+    return  SkScalarNearlyEqual(a.fLeft, b.fLeft) &&
+            SkScalarNearlyEqual(a.fTop, b.fTop) &&
+            SkScalarNearlyEqual(a.fRight, b.fRight) &&
+            SkScalarNearlyEqual(a.fBottom, b.fBottom);
+}
+
 static void test_arcTo(skiatest::Reporter* reporter) {
     SkPath p;
     p.arcTo(0, 0, 1, 2, 1);
@@ -3144,15 +3872,16 @@
     check_path_is_move_and_reset(reporter, &p, oval.fRight, oval.centerY());
     p.arcTo(oval, 360, 0, false);
     check_path_is_move_and_reset(reporter, &p, oval.fRight, oval.centerY());
+
     for (float sweep = 359, delta = 0.5f; sweep != (float) (sweep + delta); ) {
         p.arcTo(oval, 0, sweep, false);
-        REPORTER_ASSERT(reporter, p.getBounds() == oval);
+        REPORTER_ASSERT(reporter, nearly_equal(p.getBounds(), oval));
         sweep += delta;
         delta /= 2;
     }
     for (float sweep = 361, delta = 0.5f; sweep != (float) (sweep - delta);) {
         p.arcTo(oval, 0, sweep, false);
-        REPORTER_ASSERT(reporter, p.getBounds() == oval);
+        REPORTER_ASSERT(reporter, nearly_equal(p.getBounds(), oval));
         sweep -= delta;
         delta /= 2;
     }
@@ -3261,18 +3990,28 @@
     SkPath p;
     SkPoint pt = p.getPoint(0);
     REPORTER_ASSERT(reporter, pt == SkPoint::Make(0, 0));
-    REPORTER_ASSERT(reporter, !p.getLastPt(NULL));
+    REPORTER_ASSERT(reporter, !p.getLastPt(nullptr));
     REPORTER_ASSERT(reporter, !p.getLastPt(&pt) && pt == SkPoint::Make(0, 0));
     p.setLastPt(10, 10);
     pt = p.getPoint(0);
     REPORTER_ASSERT(reporter, pt == SkPoint::Make(10, 10));
-    REPORTER_ASSERT(reporter, p.getLastPt(NULL));
+    REPORTER_ASSERT(reporter, p.getLastPt(nullptr));
     p.rMoveTo(10, 10);
     REPORTER_ASSERT(reporter, p.getLastPt(&pt) && pt == SkPoint::Make(20, 20));
 }
 
 static void test_contains(skiatest::Reporter* reporter) {
     SkPath p;
+    p.moveTo(SkBits2Float(0xe085e7b1), SkBits2Float(0x5f512c00));  // -7.7191e+19f, 1.50724e+19f
+    p.conicTo(SkBits2Float(0xdfdaa221), SkBits2Float(0x5eaac338), SkBits2Float(0x60342f13), SkBits2Float(0xdf0cbb58), SkBits2Float(0x3f3504f3));  // -3.15084e+19f, 6.15237e+18f, 5.19345e+19f, -1.01408e+19f, 0.707107f
+    p.conicTo(SkBits2Float(0x60ead799), SkBits2Float(0xdfb76c24), SkBits2Float(0x609b9872), SkBits2Float(0xdf730de8), SkBits2Float(0x3f3504f4));  // 1.35377e+20f, -2.6434e+19f, 8.96947e+19f, -1.75139e+19f, 0.707107f
+    p.lineTo(SkBits2Float(0x609b9872), SkBits2Float(0xdf730de8));  // 8.96947e+19f, -1.75139e+19f
+    p.conicTo(SkBits2Float(0x6018b296), SkBits2Float(0xdeee870d), SkBits2Float(0xe008cd8e), SkBits2Float(0x5ed5b2db), SkBits2Float(0x3f3504f3));  // 4.40121e+19f, -8.59386e+18f, -3.94308e+19f, 7.69931e+18f, 0.707107f
+    p.conicTo(SkBits2Float(0xe0d526d9), SkBits2Float(0x5fa67b31), SkBits2Float(0xe085e7b2), SkBits2Float(0x5f512c01), SkBits2Float(0x3f3504f3));  // -1.22874e+20f, 2.39925e+19f, -7.7191e+19f, 1.50724e+19f, 0.707107f
+    // this may return true or false, depending on the platform's numerics, but it should not crash
+    (void) p.contains(-77.2027664f, 15.3066053f);
+
+    p.reset();
     p.setFillType(SkPath::kInverseWinding_FillType);
     REPORTER_ASSERT(reporter, p.contains(0, 0));
     p.setFillType(SkPath::kWinding_FillType);
@@ -3280,6 +4019,10 @@
     p.moveTo(4, 4);
     p.lineTo(6, 8);
     p.lineTo(8, 4);
+    // test on edge
+    REPORTER_ASSERT(reporter, p.contains(6, 4));
+    REPORTER_ASSERT(reporter, p.contains(5, 6));
+    REPORTER_ASSERT(reporter, p.contains(7, 6));
     // test quick reject
     REPORTER_ASSERT(reporter, !p.contains(4, 0));
     REPORTER_ASSERT(reporter, !p.contains(0, 4));
@@ -3293,10 +4036,53 @@
     p.moveTo(4, 4);
     p.lineTo(8, 6);
     p.lineTo(4, 8);
+    // test on edge
+    REPORTER_ASSERT(reporter, p.contains(4, 6));
+    REPORTER_ASSERT(reporter, p.contains(6, 5));
+    REPORTER_ASSERT(reporter, p.contains(6, 7));
     // test various crossings in y
     REPORTER_ASSERT(reporter, !p.contains(7, 5));
     REPORTER_ASSERT(reporter, p.contains(7, 6));
     REPORTER_ASSERT(reporter, !p.contains(7, 7));
+    p.reset();
+    p.moveTo(4, 4);
+    p.lineTo(8, 4);
+    p.lineTo(8, 8);
+    p.lineTo(4, 8);
+    // test on vertices
+    REPORTER_ASSERT(reporter, p.contains(4, 4));
+    REPORTER_ASSERT(reporter, p.contains(8, 4));
+    REPORTER_ASSERT(reporter, p.contains(8, 8));
+    REPORTER_ASSERT(reporter, p.contains(4, 8));
+    p.reset();
+    p.moveTo(4, 4);
+    p.lineTo(6, 8);
+    p.lineTo(2, 8);
+    // test on edge
+    REPORTER_ASSERT(reporter, p.contains(5, 6));
+    REPORTER_ASSERT(reporter, p.contains(4, 8));
+    REPORTER_ASSERT(reporter, p.contains(3, 6));
+    p.reset();
+    p.moveTo(4, 4);
+    p.lineTo(0, 6);
+    p.lineTo(4, 8);
+    // test on edge
+    REPORTER_ASSERT(reporter, p.contains(2, 5));
+    REPORTER_ASSERT(reporter, p.contains(2, 7));
+    REPORTER_ASSERT(reporter, p.contains(4, 6));
+    // test canceling coincident edge (a smaller triangle is coincident with a larger one)
+    p.reset();
+    p.moveTo(4, 0);
+    p.lineTo(6, 4);
+    p.lineTo(2, 4);
+    p.moveTo(4, 0);
+    p.lineTo(0, 8);
+    p.lineTo(8, 8);
+    REPORTER_ASSERT(reporter, !p.contains(1, 2));
+    REPORTER_ASSERT(reporter, !p.contains(3, 2));
+    REPORTER_ASSERT(reporter, !p.contains(4, 0));
+    REPORTER_ASSERT(reporter, p.contains(4, 4));
+
     // test quads
     p.reset();
     p.moveTo(4, 4);
@@ -3305,25 +4091,49 @@
     p.quadTo(4, 6, 4, 4);
     REPORTER_ASSERT(reporter, p.contains(5, 6));
     REPORTER_ASSERT(reporter, !p.contains(6, 5));
+    // test quad edge
+    REPORTER_ASSERT(reporter, p.contains(5, 5));
+    REPORTER_ASSERT(reporter, p.contains(5, 8));
+    REPORTER_ASSERT(reporter, p.contains(4, 5));
+    // test quad endpoints
+    REPORTER_ASSERT(reporter, p.contains(4, 4));
+    REPORTER_ASSERT(reporter, p.contains(8, 8));
+    REPORTER_ASSERT(reporter, p.contains(4, 8));
 
     p.reset();
-    p.moveTo(6, 6);
-    p.quadTo(8, 8, 6, 8);
-    p.quadTo(4, 8, 4, 6);
-    p.quadTo(4, 4, 6, 6);
+    const SkPoint qPts[] = {{6, 6}, {8, 8}, {6, 8}, {4, 8}, {4, 6}, {4, 4}, {6, 6}};
+    p.moveTo(qPts[0]);
+    for (int index = 1; index < (int) SK_ARRAY_COUNT(qPts); index += 2) {
+        p.quadTo(qPts[index], qPts[index + 1]);
+    }
     REPORTER_ASSERT(reporter, p.contains(5, 6));
     REPORTER_ASSERT(reporter, !p.contains(6, 5));
+    // test quad edge
+    SkPoint halfway;
+    for (int index = 0; index < (int) SK_ARRAY_COUNT(qPts) - 2; index += 2) {
+        SkEvalQuadAt(&qPts[index], 0.5f, &halfway, nullptr);
+        REPORTER_ASSERT(reporter, p.contains(halfway.fX, halfway.fY));
+    }
 
-#define CONIC_CONTAINS_BUG_FIXED 0
-#if CONIC_CONTAINS_BUG_FIXED
+    // test conics
     p.reset();
-    p.moveTo(4, 4);
-    p.conicTo(6, 6, 8, 8, 0.5f);
-    p.conicTo(6, 8, 4, 8, 0.5f);
-    p.conicTo(4, 6, 4, 4, 0.5f);
+    const SkPoint kPts[] = {{4, 4}, {6, 6}, {8, 8}, {6, 8}, {4, 8}, {4, 6}, {4, 4}};
+    p.moveTo(kPts[0]);
+    for (int index = 1; index < (int) SK_ARRAY_COUNT(kPts); index += 2) {
+        p.conicTo(kPts[index], kPts[index + 1], 0.5f);
+    }
     REPORTER_ASSERT(reporter, p.contains(5, 6));
     REPORTER_ASSERT(reporter, !p.contains(6, 5));
-#endif
+    // test conic edge
+    for (int index = 0; index < (int) SK_ARRAY_COUNT(kPts) - 2; index += 2) {
+        SkConic conic(&kPts[index], 0.5f);
+        halfway = conic.evalAt(0.5f);
+        REPORTER_ASSERT(reporter, p.contains(halfway.fX, halfway.fY));
+    }
+    // test conic end points
+    REPORTER_ASSERT(reporter, p.contains(4, 4));
+    REPORTER_ASSERT(reporter, p.contains(8, 8));
+    REPORTER_ASSERT(reporter, p.contains(4, 8));
 
     // test cubics
     SkPoint pts[] = {{5, 4}, {6, 5}, {7, 6}, {6, 6}, {4, 6}, {5, 7}, {5, 5}, {5, 4}, {6, 5}, {7, 6}};
@@ -3336,6 +4146,15 @@
         p.close();
         REPORTER_ASSERT(reporter, p.contains(5.5f, 5.5f));
         REPORTER_ASSERT(reporter, !p.contains(4.5f, 5.5f));
+        // test cubic edge
+        SkEvalCubicAt(&pts[i], 0.5f, &halfway, nullptr, nullptr);
+        REPORTER_ASSERT(reporter, p.contains(halfway.fX, halfway.fY));
+        SkEvalCubicAt(&pts[i + 3], 0.5f, &halfway, nullptr, nullptr);
+        REPORTER_ASSERT(reporter, p.contains(halfway.fX, halfway.fY));
+        // test cubic end points
+        REPORTER_ASSERT(reporter, p.contains(pts[i].fX, pts[i].fY));
+        REPORTER_ASSERT(reporter, p.contains(pts[i + 3].fX, pts[i + 3].fY));
+        REPORTER_ASSERT(reporter, p.contains(pts[i + 6].fX, pts[i + 6].fY));
     }
 }
 
@@ -3344,7 +4163,7 @@
     static void TestPathRef(skiatest::Reporter* reporter) {
         static const int kRepeatCnt = 10;
 
-        SkAutoTUnref<SkPathRef> pathRef(SkNEW(SkPathRef));
+        sk_sp<SkPathRef> pathRef(new SkPathRef);
 
         SkPathRef::Editor ed(&pathRef);
 
@@ -3382,7 +4201,7 @@
         }
 
         {
-            SkScalar* weights = NULL;
+            SkScalar* weights = nullptr;
             ed.growForRepeatedVerb(SkPath::kConic_Verb, kRepeatCnt, &weights);
             REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countVerbs());
             REPORTER_ASSERT(reporter, 2*kRepeatCnt == pathRef->countPoints());
@@ -3433,50 +4252,81 @@
         bool dumpAsHex, const char* str) {
     SkDynamicMemoryWStream wStream;
     path.dump(&wStream, force, dumpAsHex);
-    SkAutoDataUnref data(wStream.copyToData());
+    sk_sp<SkData> data = wStream.detachAsData();
     REPORTER_ASSERT(reporter, data->size() == strlen(str));
-    REPORTER_ASSERT(reporter, !memcmp(data->data(), str, strlen(str)));
+    if (strlen(str) > 0) {
+        REPORTER_ASSERT(reporter, !memcmp(data->data(), str, strlen(str)));
+    } else {
+        REPORTER_ASSERT(reporter, data->data() == nullptr || !memcmp(data->data(), str, strlen(str)));
+    }
 }
 
 static void test_dump(skiatest::Reporter* reporter) {
     SkPath p;
-    compare_dump(reporter, p, false, false, "");
-    compare_dump(reporter, p, true, false, "");
+    compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kWinding_FillType);\n");
+    compare_dump(reporter, p, true, false,  "path.setFillType(SkPath::kWinding_FillType);\n");
     p.moveTo(1, 2);
     p.lineTo(3, 4);
-    compare_dump(reporter, p, false, false, "path.moveTo(1, 2);\n"
+    compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kWinding_FillType);\n"
+                                            "path.moveTo(1, 2);\n"
                                             "path.lineTo(3, 4);\n");
-    compare_dump(reporter, p, true, false,  "path.moveTo(1, 2);\n"
+    compare_dump(reporter, p, true, false,  "path.setFillType(SkPath::kWinding_FillType);\n"
+                                            "path.moveTo(1, 2);\n"
                                             "path.lineTo(3, 4);\n"
                                             "path.lineTo(1, 2);\n"
                                             "path.close();\n");
     p.reset();
+    p.setFillType(SkPath::kEvenOdd_FillType);
     p.moveTo(1, 2);
     p.quadTo(3, 4, 5, 6);
-    compare_dump(reporter, p, false, false, "path.moveTo(1, 2);\n"
+    compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kEvenOdd_FillType);\n"
+                                            "path.moveTo(1, 2);\n"
                                             "path.quadTo(3, 4, 5, 6);\n");
     p.reset();
+    p.setFillType(SkPath::kInverseWinding_FillType);
     p.moveTo(1, 2);
     p.conicTo(3, 4, 5, 6, 0.5f);
-    compare_dump(reporter, p, false, false, "path.moveTo(1, 2);\n"
+    compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kInverseWinding_FillType);\n"
+                                            "path.moveTo(1, 2);\n"
                                             "path.conicTo(3, 4, 5, 6, 0.5f);\n");
     p.reset();
+    p.setFillType(SkPath::kInverseEvenOdd_FillType);
     p.moveTo(1, 2);
     p.cubicTo(3, 4, 5, 6, 7, 8);
-    compare_dump(reporter, p, false, false, "path.moveTo(1, 2);\n"
+    compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kInverseEvenOdd_FillType);\n"
+                                            "path.moveTo(1, 2);\n"
                                             "path.cubicTo(3, 4, 5, 6, 7, 8);\n");
     p.reset();
+    p.setFillType(SkPath::kWinding_FillType);
     p.moveTo(1, 2);
     p.lineTo(3, 4);
-    compare_dump(reporter, p, false, true,  "path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x40000000));\n"
-                                            "path.lineTo(SkBits2Float(0x40400000), SkBits2Float(0x40800000));\n");
+    compare_dump(reporter, p, false, true,
+                 "path.setFillType(SkPath::kWinding_FillType);\n"
+                 "path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x40000000));  // 1, 2\n"
+                 "path.lineTo(SkBits2Float(0x40400000), SkBits2Float(0x40800000));  // 3, 4\n");
     p.reset();
     p.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x40000000));
     p.lineTo(SkBits2Float(0x40400000), SkBits2Float(0x40800000));
-    compare_dump(reporter, p, false, false, "path.moveTo(1, 2);\n"
+    compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kWinding_FillType);\n"
+                                            "path.moveTo(1, 2);\n"
                                             "path.lineTo(3, 4);\n");
 }
 
+namespace {
+
+class ChangeListener : public SkPathRef::GenIDChangeListener {
+public:
+    ChangeListener(bool *changed) : fChanged(changed) { *fChanged = false; }
+    ~ChangeListener() override {}
+    void onChange() override {
+        *fChanged = true;
+    }
+private:
+    bool* fChanged;
+};
+
+}
+
 class PathTest_Private {
 public:
     static void TestPathTo(skiatest::Reporter* reporter) {
@@ -3496,12 +4346,194 @@
         SkRect reverseExpected = {-4, -4, 8, 8};
         REPORTER_ASSERT(reporter, p.getBounds() == reverseExpected);
     }
+
+    static void TestPathrefListeners(skiatest::Reporter* reporter) {
+        SkPath p;
+
+        bool changed = false;
+        p.moveTo(0, 0);
+
+        // Check that listener is notified on moveTo().
+
+        SkPathPriv::AddGenIDChangeListener(p, new ChangeListener(&changed));
+        REPORTER_ASSERT(reporter, !changed);
+        p.moveTo(10, 0);
+        REPORTER_ASSERT(reporter, changed);
+
+        // Check that listener is notified on lineTo().
+        SkPathPriv::AddGenIDChangeListener(p, new ChangeListener(&changed));
+        REPORTER_ASSERT(reporter, !changed);
+        p.lineTo(20, 0);
+        REPORTER_ASSERT(reporter, changed);
+
+        // Check that listener is notified on reset().
+        SkPathPriv::AddGenIDChangeListener(p, new ChangeListener(&changed));
+        REPORTER_ASSERT(reporter, !changed);
+        p.reset();
+        REPORTER_ASSERT(reporter, changed);
+
+        p.moveTo(0, 0);
+
+        // Check that listener is notified on rewind().
+        SkPathPriv::AddGenIDChangeListener(p, new ChangeListener(&changed));
+        REPORTER_ASSERT(reporter, !changed);
+        p.rewind();
+        REPORTER_ASSERT(reporter, changed);
+
+        // Check that listener is notified when pathref is deleted.
+        {
+            SkPath q;
+            q.moveTo(10, 10);
+            SkPathPriv::AddGenIDChangeListener(q, new ChangeListener(&changed));
+            REPORTER_ASSERT(reporter, !changed);
+        }
+        // q went out of scope.
+        REPORTER_ASSERT(reporter, changed);
+    }
 };
 
-DEF_TEST(Paths, reporter) {
-    test_path_crbug364224();
+static void test_crbug_629455(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.cubicTo(SkBits2Float(0xcdcdcd00), SkBits2Float(0xcdcdcdcd),
+                 SkBits2Float(0xcdcdcdcd), SkBits2Float(0xcdcdcdcd),
+                 SkBits2Float(0x423fcdcd), SkBits2Float(0x40ed9341));
+//  AKA: cubicTo(-4.31596e+08f, -4.31602e+08f, -4.31602e+08f, -4.31602e+08f, 47.951f, 7.42423f);
+    path.lineTo(0, 0);
 
-    SkTSize<SkScalar>::Make(3,4);
+    auto surface = SkSurface::MakeRasterN32Premul(100, 100);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    surface->getCanvas()->drawPath(path, paint);
+}
+
+static void test_fuzz_crbug_662952(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.moveTo(SkBits2Float(0x4109999a), SkBits2Float(0x411c0000));  // 8.6f, 9.75f
+    path.lineTo(SkBits2Float(0x410a6666), SkBits2Float(0x411c0000));  // 8.65f, 9.75f
+    path.lineTo(SkBits2Float(0x410a6666), SkBits2Float(0x411e6666));  // 8.65f, 9.9f
+    path.lineTo(SkBits2Float(0x4109999a), SkBits2Float(0x411e6666));  // 8.6f, 9.9f
+    path.lineTo(SkBits2Float(0x4109999a), SkBits2Float(0x411c0000));  // 8.6f, 9.75f
+    path.close();
+
+    auto surface = SkSurface::MakeRasterN32Premul(100, 100);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    surface->getCanvas()->clipPath(path, true);
+    surface->getCanvas()->drawRect(SkRect::MakeWH(100, 100), paint);
+}
+
+static void test_path_crbugskia6003() {
+    auto surface(SkSurface::MakeRasterN32Premul(500, 500));
+    SkCanvas* canvas = surface->getCanvas();
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    SkPath path;
+    path.moveTo(SkBits2Float(0x4325e666), SkBits2Float(0x42a1999a));  // 165.9f, 80.8f
+    path.lineTo(SkBits2Float(0x4325e666), SkBits2Float(0x42a2999a));  // 165.9f, 81.3f
+    path.lineTo(SkBits2Float(0x4325b333), SkBits2Float(0x42a2999a));  // 165.7f, 81.3f
+    path.lineTo(SkBits2Float(0x4325b333), SkBits2Float(0x42a16666));  // 165.7f, 80.7f
+    path.lineTo(SkBits2Float(0x4325b333), SkBits2Float(0x429f6666));  // 165.7f, 79.7f
+    // 165.7f, 79.7f, 165.8f, 79.7f, 165.8f, 79.7f
+    path.cubicTo(SkBits2Float(0x4325b333), SkBits2Float(0x429f6666), SkBits2Float(0x4325cccc),
+            SkBits2Float(0x429f6666), SkBits2Float(0x4325cccc), SkBits2Float(0x429f6666));
+    // 165.8f, 79.7f, 165.8f, 79.7f, 165.9f, 79.7f
+    path.cubicTo(SkBits2Float(0x4325cccc), SkBits2Float(0x429f6666), SkBits2Float(0x4325cccc),
+            SkBits2Float(0x429f6666), SkBits2Float(0x4325e666), SkBits2Float(0x429f6666));
+    path.lineTo(SkBits2Float(0x4325e666), SkBits2Float(0x42a1999a));  // 165.9f, 80.8f
+    path.close();
+    canvas->clipPath(path, true);
+    canvas->drawRect(SkRect::MakeWH(500, 500), paint);
+}
+
+static void test_fuzz_crbug_662730(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+    path.lineTo(SkBits2Float(0xd5394437), SkBits2Float(0x37373737));  // -1.2731e+13f, 1.09205e-05f
+    path.lineTo(SkBits2Float(0x37373737), SkBits2Float(0x37373737));  // 1.09205e-05f, 1.09205e-05f
+    path.lineTo(SkBits2Float(0x37373745), SkBits2Float(0x0001b800));  // 1.09205e-05f, 1.57842e-40f
+    path.close();
+
+    auto surface = SkSurface::MakeRasterN32Premul(100, 100);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    surface->getCanvas()->drawPath(path, paint);
+}
+
+static void test_interp(skiatest::Reporter* reporter) {
+    SkPath p1, p2, out;
+    REPORTER_ASSERT(reporter, p1.isInterpolatable(p2));
+    REPORTER_ASSERT(reporter, p1.interpolate(p2, 0, &out));
+    REPORTER_ASSERT(reporter, p1 == out);
+    REPORTER_ASSERT(reporter, p1.interpolate(p2, 1, &out));
+    REPORTER_ASSERT(reporter, p1 == out);
+    p1.moveTo(0, 2);
+    p1.lineTo(0, 4);
+    REPORTER_ASSERT(reporter, !p1.isInterpolatable(p2));
+    REPORTER_ASSERT(reporter, !p1.interpolate(p2, 1, &out));
+    p2.moveTo(6, 0);
+    p2.lineTo(8, 0);
+    REPORTER_ASSERT(reporter, p1.isInterpolatable(p2));
+    REPORTER_ASSERT(reporter, p1.interpolate(p2, 0, &out));
+    REPORTER_ASSERT(reporter, p2 == out);
+    REPORTER_ASSERT(reporter, p1.interpolate(p2, 1, &out));
+    REPORTER_ASSERT(reporter, p1 == out);
+    REPORTER_ASSERT(reporter, p1.interpolate(p2, 0.5f, &out));
+    REPORTER_ASSERT(reporter, out.getBounds() == SkRect::MakeLTRB(3, 1, 4, 2));
+    p1.reset();
+    p1.moveTo(4, 4);
+    p1.conicTo(5, 4, 5, 5, 1 / SkScalarSqrt(2));
+    p2.reset();
+    p2.moveTo(4, 2);
+    p2.conicTo(7, 2, 7, 5, 1 / SkScalarSqrt(2));
+    REPORTER_ASSERT(reporter, p1.isInterpolatable(p2));
+    REPORTER_ASSERT(reporter, p1.interpolate(p2, 0.5f, &out));
+    REPORTER_ASSERT(reporter, out.getBounds() == SkRect::MakeLTRB(4, 3, 6, 5));
+    p2.reset();
+    p2.moveTo(4, 2);
+    p2.conicTo(6, 3, 6, 5, 1);
+    REPORTER_ASSERT(reporter, !p1.isInterpolatable(p2));
+    p2.reset();
+    p2.moveTo(4, 4);
+    p2.conicTo(5, 4, 5, 5, 0.5f);
+    REPORTER_ASSERT(reporter, !p1.isInterpolatable(p2));
+}
+
+DEF_TEST(PathInterp, reporter) {
+    test_interp(reporter);
+}
+
+#include "SkSurface.h"
+DEF_TEST(PathBigCubic, reporter) {
+    SkPath path;
+    path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
+    path.moveTo(SkBits2Float(0x44000000), SkBits2Float(0x373938b8));  // 512, 1.10401e-05f
+    path.cubicTo(SkBits2Float(0x00000001), SkBits2Float(0xdf000052), SkBits2Float(0x00000100), SkBits2Float(0x00000000), SkBits2Float(0x00000100), SkBits2Float(0x00000000));  // 1.4013e-45f, -9.22346e+18f, 3.58732e-43f, 0, 3.58732e-43f, 0
+    path.moveTo(0, 512);
+
+    // this call should not assert
+    SkSurface::MakeRasterN32Premul(255, 255, nullptr)->getCanvas()->drawPath(path, SkPaint());
+}
+
+DEF_TEST(PathContains, reporter) {
+    test_contains(reporter);
+}
+
+DEF_TEST(Paths, reporter) {
+    test_fuzz_crbug_647922();
+    test_fuzz_crbug_643933();
+    test_sect_with_horizontal_needs_pinning();
+    test_crbug_629455(reporter);
+    test_fuzz_crbug_627414(reporter);
+    test_path_crbug364224();
+    test_fuzz_crbug_662952(reporter);
+    test_fuzz_crbug_662730(reporter);
+    test_fuzz_crbug_662780();
+    test_mask_overflow();
+    test_path_crbugskia6003();
+    test_fuzz_crbug_668907();
+
+    SkSize::Make(3, 4);
 
     SkPath  p, empty;
     SkRect  bounds, bounds2;
@@ -3521,7 +4553,8 @@
     p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
     check_convex_bounds(reporter, p, bounds);
     // we have quads or cubics
-    REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
+    REPORTER_ASSERT(reporter,
+                    p.getSegmentMasks() & (kCurveSegmentMask | SkPath::kConic_SegmentMask));
     REPORTER_ASSERT(reporter, !p.isEmpty());
 
     p.reset();
@@ -3544,8 +4577,8 @@
     REPORTER_ASSERT(reporter, !(p == empty));
 
     // do getPoints and getVerbs return the right result
-    REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
-    REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
+    REPORTER_ASSERT(reporter, p.getPoints(nullptr, 0) == 4);
+    REPORTER_ASSERT(reporter, p.getVerbs(nullptr, 0) == 5);
     SkPoint pts[4];
     int count = p.getPoints(pts, 4);
     REPORTER_ASSERT(reporter, count == 4);
@@ -3565,7 +4598,7 @@
     p.offset(SK_Scalar1*3, SK_Scalar1*4);
     REPORTER_ASSERT(reporter, bounds == p.getBounds());
 
-    REPORTER_ASSERT(reporter, p.isRect(NULL));
+    REPORTER_ASSERT(reporter, p.isRect(nullptr));
     bounds2.setEmpty();
     REPORTER_ASSERT(reporter, p.isRect(&bounds2));
     REPORTER_ASSERT(reporter, bounds == bounds2);
@@ -3573,12 +4606,27 @@
     // now force p to not be a rect
     bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
     p.addRect(bounds);
-    REPORTER_ASSERT(reporter, !p.isRect(NULL));
+    REPORTER_ASSERT(reporter, !p.isRect(nullptr));
+
+    // Test an edge case w.r.t. the bound returned by isRect (i.e., the
+    // path has a trailing moveTo. Please see crbug.com\445368)
+    {
+        SkRect r;
+        p.reset();
+        p.addRect(bounds);
+        REPORTER_ASSERT(reporter, p.isRect(&r));
+        REPORTER_ASSERT(reporter, r == bounds);
+        // add a moveTo outside of our bounds
+        p.moveTo(bounds.fLeft + 10, bounds.fBottom + 10);
+        REPORTER_ASSERT(reporter, p.isRect(&r));
+        REPORTER_ASSERT(reporter, r == bounds);
+    }
 
     test_operatorEqual(reporter);
     test_isLine(reporter);
     test_isRect(reporter);
-    test_isNestedRects(reporter);
+    test_is_simple_closed_rect(reporter);
+    test_isNestedFillRects(reporter);
     test_zero_length_paths(reporter);
     test_direction(reporter);
     test_convexity(reporter);
@@ -3597,6 +4645,7 @@
     test_addPoly(reporter);
     test_isfinite(reporter);
     test_isfinite_after_transform(reporter);
+    test_islastcontourclosed(reporter);
     test_arb_round_rect_is_convex(reporter);
     test_arb_zero_rad_round_rect_is_rect(reporter);
     test_addrect(reporter);
@@ -3604,14 +4653,17 @@
     test_tricky_cubic();
     test_clipped_cubic();
     test_crbug_170666();
+    test_crbug_493450(reporter);
+    test_crbug_495894(reporter);
+    test_crbug_613918();
     test_bad_cubic_crbug229478();
     test_bad_cubic_crbug234190();
-    test_android_specific_behavior(reporter);
     test_gen_id(reporter);
     test_path_close_issue1474(reporter);
     test_path_to_region(reporter);
     test_rrect(reporter);
     test_arc(reporter);
+    test_arc_ovals(reporter);
     test_arcTo(reporter);
     test_addPath(reporter);
     test_addPathMode(reporter, false, false);
@@ -3626,6 +4678,100 @@
     test_contains(reporter);
     PathTest_Private::TestPathTo(reporter);
     PathRefTest_Private::TestPathRef(reporter);
+    PathTest_Private::TestPathrefListeners(reporter);
     test_dump(reporter);
+    test_path_crbug389050(reporter);
     test_path_crbugskia2820(reporter);
+    test_path_crbugskia5995();
+    test_skbug_3469(reporter);
+    test_skbug_3239(reporter);
+    test_bounds_crbug_513799(reporter);
+    test_fuzz_crbug_638223();
+}
+
+DEF_TEST(conservatively_contains_rect, reporter) {
+    SkPath path;
+
+    path.moveTo(SkBits2Float(0x44000000), SkBits2Float(0x373938b8));  // 512, 1.10401e-05f
+    // 1.4013e-45f, -9.22346e+18f, 3.58732e-43f, 0, 3.58732e-43f, 0
+    path.cubicTo(SkBits2Float(0x00000001), SkBits2Float(0xdf000052),
+                 SkBits2Float(0x00000100), SkBits2Float(0x00000000),
+                 SkBits2Float(0x00000100), SkBits2Float(0x00000000));
+    path.moveTo(0, 0);
+
+    // this guy should not assert
+    path.conservativelyContainsRect({ -211747, 12.1115f, -197893, 25.0321f });
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void rand_path(SkPath* path, SkRandom& rand, SkPath::Verb verb, int n) {
+    for (int i = 0; i < n; ++i) {
+        switch (verb) {
+            case SkPath::kLine_Verb:
+                path->lineTo(rand.nextF()*100, rand.nextF()*100);
+                break;
+            case SkPath::kQuad_Verb:
+                path->quadTo(rand.nextF()*100, rand.nextF()*100,
+                             rand.nextF()*100, rand.nextF()*100);
+                break;
+            case SkPath::kConic_Verb:
+                path->conicTo(rand.nextF()*100, rand.nextF()*100,
+                              rand.nextF()*100, rand.nextF()*100, rand.nextF()*10);
+                break;
+            case SkPath::kCubic_Verb:
+                path->cubicTo(rand.nextF()*100, rand.nextF()*100,
+                              rand.nextF()*100, rand.nextF()*100,
+                              rand.nextF()*100, rand.nextF()*100);
+                break;
+            default:
+                SkASSERT(false);
+        }
+    }
+}
+
+#include "SkPathOps.h"
+DEF_TEST(path_tight_bounds, reporter) {
+    SkRandom rand;
+
+    const SkPath::Verb verbs[] = {
+        SkPath::kLine_Verb, SkPath::kQuad_Verb, SkPath::kConic_Verb, SkPath::kCubic_Verb,
+    };
+    for (int i = 0; i < 1000; ++i) {
+        for (int n = 1; n <= 10; n += 9) {
+            for (SkPath::Verb verb : verbs) {
+                SkPath path;
+                rand_path(&path, rand, verb, n);
+                SkRect bounds = path.getBounds();
+                SkRect tight = path.computeTightBounds();
+                REPORTER_ASSERT(reporter, bounds.contains(tight));
+                
+                SkRect tight2;
+                TightBounds(path, &tight2);
+                REPORTER_ASSERT(reporter, nearly_equal(tight, tight2));
+            }
+        }
+    }
+}
+
+DEF_TEST(skbug_6450, r) {
+    SkRect ri = { 0.18554693f, 195.26283f, 0.185784385f, 752.644409f };
+    SkVector rdi[4] = {
+        { 1.81159976e-09f, 7.58768801e-05f },
+        { 0.000118725002f, 0.000118725002f },
+        { 0.000118725002f, 0.000118725002f },
+        { 0.000118725002f, 0.486297607f }
+    };
+    SkRRect irr;
+    irr.setRectRadii(ri, rdi);
+    SkRect ro = { 9.18354821e-39f, 2.1710848e+9f, 2.16945843e+9f, 3.47808128e+9f };
+    SkVector rdo[4] = {
+        { 0, 0 },
+        { 0.0103298295f, 0.185887396f },
+        { 2.52999727e-29f, 169.001938f },
+        { 195.262741f, 195.161255f }
+    };
+    SkRRect orr;
+    orr.setRectRadii(ro, rdo);
+    SkMakeNullCanvas()->drawDRRect(orr, irr, SkPaint());
 }
diff --git a/src/third_party/skia/tests/PathUtilsTest.cpp b/src/third_party/skia/tests/PathUtilsTest.cpp
deleted file mode 100644
index 4dfeda4..0000000
--- a/src/third_party/skia/tests/PathUtilsTest.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkPathUtils.h"
-#include "SkRandom.h"
-#include "SkTime.h"
-#include "Test.h"
-
-const int kNumIt = 100;
-
-static void fill_random_bits(int chars, char* bits){
-    SkRandom rand(SkTime::GetMSecs());
-
-    for (int i = 0; i < chars; ++i){
-        bits[i] = rand.nextU();
-    }
-}
-
-static int get_bit(const char* buffer, int x) {
-    int byte = x >> 3;
-    int bit = x & 7;
-
-    return buffer[byte] & (128 >> bit);
-}
-
-/* // useful for debugging errors
-   #include <iostream>
-static void print_bits( const char* bits, int w, int h) {
-
-    for (int y = 0; y < h; ++y) {
-        for (int x = 0; x < w; ++x){
-            bool bit = get_bit(&bits[y], x)!=0;
-            std::cout << bit;
-            }
-        std::cout << std::endl;
-    }
-}
-
-static void print_bmp( SkBitmap* bmp, int w, int h){
-
-    for (int y = 0; y < h; ++y) {
-        for (int x = 0; x < w; ++x) {
-            int d = *bmp->getAddr32(x,y);
-            if (d == -1)
-                std::cout << 0;
-            else
-                std::cout << 1;
-        }
-        std::cout << std::endl;
-    }
-}
-*/
-
-static void binary_to_skbitmap(const char* bin_bmp, SkBitmap* sk_bmp,
-                         int w, int h, int rowBytes){
-    //init the SkBitmap
-    sk_bmp->allocN32Pixels(w, h);
-
-    for (int y = 0; y < h; ++y) { // for every row
-
-        const char* curLine = &bin_bmp[y * rowBytes];
-        for (int x = 0; x < w; ++x) {// for every pixel
-            if (get_bit(curLine, x)) {
-                *sk_bmp->getAddr32(x,y) = SK_ColorBLACK;
-            }
-            else {
-                *sk_bmp->getAddr32(x,y) = SK_ColorWHITE;
-            }
-        }
-    }
-}
-
-static bool test_bmp(skiatest::Reporter* reporter,
-                      const SkBitmap* bmp1, const SkBitmap* bmp2,
-                      int w, int h) {
-    for (int y = 0; y < h; ++y) { // loop through all pixels
-        for (int x = 0; x < w; ++x) {
-            REPORTER_ASSERT( reporter, *bmp1->getAddr32(x,y) == *bmp2->getAddr32(x,y) );
-        }
-    }
-    return true;
-}
-
-static void test_path_eq(skiatest::Reporter* reporter, const SkPath* path,
-                      const SkBitmap* truth, int w, int h){
-    // make paint
-    SkPaint bmpPaint;
-    bmpPaint.setAntiAlias(true);  // Black paint for bitmap
-    bmpPaint.setStyle(SkPaint::kFill_Style);
-    bmpPaint.setColor(SK_ColorBLACK);
-
-    // make bmp
-    SkBitmap bmp;
-    bmp.allocN32Pixels(w, h);
-    SkCanvas canvas(bmp);
-    canvas.clear(SK_ColorWHITE);
-    canvas.drawPath(*path, bmpPaint);
-
-    // test bmp
-    test_bmp(reporter, truth, &bmp, w, h);
-}
-
-static void test_path(skiatest::Reporter* reporter, const SkBitmap* truth,
-                            const char* bin_bmp, int w, int h, int rowBytes){
-    // make path
-    SkPath path;
-    SkPathUtils::BitsToPath_Path(&path, bin_bmp, w, h, rowBytes);
-
-    //test for correctness
-    test_path_eq(reporter, &path, truth, w, h);
-}
-
-static void test_region(skiatest::Reporter* reporter, const SkBitmap* truth,
-                            const char* bin_bmp, int w, int h, int rowBytes){
-    //generate bitmap
-    SkPath path;
-    SkPathUtils::BitsToPath_Region(&path, bin_bmp, w, h, rowBytes);
-
-    //test for correctness
-    test_path_eq(reporter, &path, truth, w, h);
-}
-
-DEF_TEST(PathUtils, reporter) {
-    const int w[] = {4, 8, 12, 16};
-    const int h = 8, rowBytes = 4;
-
-    char bits[ h * rowBytes ];
-    static char* binBmp = &bits[0];
-
-    //loop to run randomized test lots of times
-    for (int it = 0; it < kNumIt; ++it)
-    {
-        // generate a random binary bitmap
-        fill_random_bits( h * rowBytes, binBmp); // generate random bitmap
-
-        // for each bitmap width, use subset of binary bitmap
-        for (unsigned int i = 0; i < SK_ARRAY_COUNT(w); ++i) {
-            // generate truth bitmap
-            SkBitmap bmpTruth;
-            binary_to_skbitmap(binBmp, &bmpTruth, w[i], h, rowBytes);
-
-            test_path(reporter, &bmpTruth, binBmp, w[i], h, rowBytes);
-            test_region(reporter, &bmpTruth, binBmp, w[i], h, rowBytes);
-        }
-    }
-}
diff --git a/src/third_party/skia/tests/PictureBBHTest.cpp b/src/third_party/skia/tests/PictureBBHTest.cpp
new file mode 100644
index 0000000..0bc81c2
--- /dev/null
+++ b/src/third_party/skia/tests/PictureBBHTest.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkBBoxHierarchy.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkPictureRecorder.h"
+
+#include "Test.h"
+
+class PictureBBHTestBase {
+public:
+    PictureBBHTestBase(int playbackWidth, int playbackHeight,
+        int recordWidth, int recordHeight) {
+
+        fResultBitmap.allocN32Pixels(playbackWidth, playbackHeight);
+        fPictureWidth = recordWidth;
+        fPictureHeight = recordHeight;
+    }
+
+    virtual ~PictureBBHTestBase() { }
+
+    virtual void doTest(SkCanvas& playbackCanvas, SkCanvas& recordingCanvas) = 0;
+
+    void run(skiatest::Reporter* reporter) {
+        // No BBH
+        this->run(nullptr, reporter);
+
+        // With an R-Tree
+        SkRTreeFactory RTreeFactory;
+        this->run(&RTreeFactory, reporter);
+    }
+
+private:
+    void run(SkBBHFactory* factory, skiatest::Reporter* reporter) {
+        SkCanvas playbackCanvas(fResultBitmap);
+        playbackCanvas.clear(SK_ColorGREEN);
+        SkPictureRecorder recorder;
+        SkCanvas* recordCanvas = recorder.beginRecording(SkIntToScalar(fPictureWidth),
+                                                         SkIntToScalar(fPictureHeight),
+                                                         factory);
+        this->doTest(playbackCanvas, *recordCanvas);
+        sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
+        playbackCanvas.drawPicture(picture);
+        REPORTER_ASSERT(reporter, SK_ColorGREEN == fResultBitmap.getColor(0, 0));
+    }
+
+    SkBitmap fResultBitmap;
+    int fPictureWidth, fPictureHeight;
+};
+
+// Test to verify the playback of an empty picture
+//
+class DrawEmptyPictureBBHTest : public PictureBBHTestBase {
+public:
+    DrawEmptyPictureBBHTest()
+        : PictureBBHTestBase(2, 2, 1, 1) {}
+    ~DrawEmptyPictureBBHTest() override {}
+
+    void doTest(SkCanvas&, SkCanvas&) override {}
+};
+
+// Test to verify the playback of a picture into a canvas that has
+// an empty clip.
+//
+class EmptyClipPictureBBHTest : public PictureBBHTestBase {
+public:
+    EmptyClipPictureBBHTest()
+        : PictureBBHTestBase(2, 2, 3, 3) {}
+
+    void doTest(SkCanvas& playbackCanvas, SkCanvas& recordingCanvas) override {
+        // intersect with out of bounds rect -> empty clip.
+        playbackCanvas.clipRect(SkRect::MakeXYWH(10, 10, 1, 1));
+        SkPaint paint;
+        recordingCanvas.drawRect(SkRect::MakeWH(3, 3), paint);
+    }
+
+    ~EmptyClipPictureBBHTest() override {}
+};
+
+DEF_TEST(PictureBBH, reporter) {
+
+    DrawEmptyPictureBBHTest emptyPictureTest;
+    emptyPictureTest.run(reporter);
+
+    EmptyClipPictureBBHTest emptyClipPictureTest;
+    emptyClipPictureTest.run(reporter);
+}
+
+DEF_TEST(RTreeMakeLargest, r) {
+    // A call to insert() with 2 or more rects and a bounds of SkRect::MakeLargest()
+    // used to fall into an infinite loop.
+
+    SkRTreeFactory factory;
+    std::unique_ptr<SkBBoxHierarchy> bbh{ factory(SkRect::MakeLargest()) };
+
+    SkRect rects[] = { {0,0, 10,10}, {5,5,15,15} };
+    bbh->insert(rects, SK_ARRAY_COUNT(rects));
+    REPORTER_ASSERT(r, bbh->getRootBound() == SkRect::MakeWH(15,15));
+}
diff --git a/src/third_party/skia/tests/PictureShaderTest.cpp b/src/third_party/skia/tests/PictureShaderTest.cpp
index 8d933db..742b72d 100644
--- a/src/third_party/skia/tests/PictureShaderTest.cpp
+++ b/src/third_party/skia/tests/PictureShaderTest.cpp
@@ -5,22 +5,76 @@
  * found in the LICENSE file.
  */
 
+#include "SkCanvas.h"
 #include "SkPicture.h"
 #include "SkPictureRecorder.h"
+#include "SkPictureShader.h"
 #include "SkShader.h"
+#include "SkSurface.h"
 #include "Test.h"
 
-// Test that attempting to create a picture shader with a NULL picture or
-// empty picture returns NULL.
+// Test that attempting to create a picture shader with a nullptr picture or
+// empty picture returns a shader that draws nothing.
 DEF_TEST(PictureShader_empty, reporter) {
-    SkShader* shader = SkShader::CreatePictureShader(NULL,
-            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, NULL, NULL);
-    REPORTER_ASSERT(reporter, NULL == shader);
+    SkPaint paint;
+
+    SkBitmap bitmap;
+    bitmap.allocN32Pixels(1,1);
+
+    SkCanvas canvas(bitmap);
+    canvas.clear(SK_ColorGREEN);
+
+    paint.setShader(SkShader::MakePictureShader(
+            nullptr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, nullptr, nullptr));
+
+    canvas.drawRect(SkRect::MakeWH(1,1), paint);
+    REPORTER_ASSERT(reporter, *bitmap.getAddr32(0,0) == SK_ColorGREEN);
+
 
     SkPictureRecorder factory;
-    factory.beginRecording(0, 0, NULL, 0);
-    SkAutoTUnref<SkPicture> picture(factory.endRecording());
-    shader = SkShader::CreatePictureShader(picture.get(),
-            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, NULL, NULL);
-    REPORTER_ASSERT(reporter, NULL == shader);
+    factory.beginRecording(0, 0, nullptr, 0);
+    paint.setShader(SkShader::MakePictureShader(factory.finishRecordingAsPicture(),
+                                                SkShader::kClamp_TileMode,
+                                                SkShader::kClamp_TileMode, nullptr, nullptr));
+
+    canvas.drawRect(SkRect::MakeWH(1,1), paint);
+    REPORTER_ASSERT(reporter, *bitmap.getAddr32(0,0) == SK_ColorGREEN);
+}
+
+// Test that the SkPictureShader cache is purged on shader deletion.
+DEF_TEST(PictureShader_caching, reporter) {
+    auto makePicture = [] () {
+        SkPictureRecorder recorder;
+        recorder.beginRecording(100, 100)->drawColor(SK_ColorGREEN);
+        return recorder.finishRecordingAsPicture();
+    };
+
+    sk_sp<SkPicture> picture = makePicture();
+    REPORTER_ASSERT(reporter, picture->unique());
+
+    sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(100, 100);
+
+    {
+        SkPaint paint;
+        paint.setShader(SkPictureShader::Make(picture,
+                                              SkShader::kRepeat_TileMode,
+                                              SkShader::kRepeat_TileMode, nullptr, nullptr));
+        surface->getCanvas()->drawPaint(paint);
+
+        // We should have about 3 refs by now: local + shader + shader cache.
+        REPORTER_ASSERT(reporter, !picture->unique());
+    }
+
+    // Draw another picture shader to have a chance to purge.
+    {
+        SkPaint paint;
+        paint.setShader(SkPictureShader::Make(makePicture(),
+                                              SkShader::kRepeat_TileMode,
+                                              SkShader::kRepeat_TileMode, nullptr, nullptr));
+        surface->getCanvas()->drawPaint(paint);
+
+    }
+
+    // All but the local ref should be gone now.
+    REPORTER_ASSERT(reporter, picture->unique());
 }
diff --git a/src/third_party/skia/tests/PictureStateTreeTest.cpp b/src/third_party/skia/tests/PictureStateTreeTest.cpp
deleted file mode 100644
index cb154de..0000000
--- a/src/third_party/skia/tests/PictureStateTreeTest.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBBHFactory.h"
-#include "SkCanvas.h"
-#include "SkPictureRecorder.h"
-#include "SkPictureStateTree.h"
-#include "Test.h"
-
-static SkPicture* draw_scene(SkBBHFactory* bbhFactory) {
-    SkPictureRecorder recorder;
-    SkCanvas* canvas = recorder.beginRecording(200, 200, bbhFactory, 0);
-
-    SkPaint p1, p2;
-    p1.setStyle(SkPaint::kFill_Style);
-    p1.setARGB(0x80, 0, 0xff, 0);
-    p2.setStyle(SkPaint::kFill_Style);
-    p2.setARGB(0x80, 0xff, 0, 0);
-
-    canvas->drawColor(SK_ColorWHITE);
-
-    // This is intended to exercise some tricky SkPictureStateTree code paths when
-    // played back with various clips:
-    //
-    //   * cleanup/rewind when the last draw is not at the root of the state tree.
-    //   * state nodes with both kSave_Flag & kSaveLayer_Flag set.
-    //   * state tree transitions which implicitly reset the matrix via. restore().
-
-    canvas->save();
-      canvas->translate(10, 10);
-
-      canvas->drawRect(SkRect::MakeWH(100, 50), p1);
-      canvas->drawRect(SkRect::MakeWH(50, 100), p2);
-
-      SkRect layerBounds = SkRect::MakeXYWH(0, 0, 90, 90);
-      canvas->saveLayer(&layerBounds, NULL);
-        canvas->save();
-          canvas->clipRect(layerBounds);
-
-          canvas->save();
-            canvas->clipRect(SkRect::MakeWH(25, 25));
-            canvas->rotate(90);
-            canvas->drawRect(SkRect::MakeWH(100, 50), p1);
-          canvas->restore();
-
-          canvas->save();
-            canvas->clipRect(SkRect::MakeWH(25, 25));
-            canvas->save();
-              canvas->rotate(90);
-              canvas->drawRect(SkRect::MakeWH(50, 100), p2);
-            canvas->restore();
-            canvas->drawRect(SkRect::MakeWH(100, 50), p1);
-          canvas->restore();
-          canvas->drawRect(SkRect::MakeXYWH(99, 99, 1, 1), p1);
-        canvas->restore();
-      canvas->restore();
-
-    canvas->restore();
-
-    return recorder.endRecording();
-}
-
-static void check_bms(skiatest::Reporter* reporter, const SkBitmap& bm1, const SkBitmap& bm2) {
-    SkASSERT(bm1.getSize() == bm2.getSize());
-    REPORTER_ASSERT(reporter, 0 == memcmp(bm1.getAddr(0, 0), bm2.getAddr(0, 0), bm1.getSize()));
-}
-
-static void test_reference_picture(skiatest::Reporter* reporter) {
-    SkRTreeFactory bbhFactory;
-
-    SkAutoTUnref<SkPicture> bbhPicture(draw_scene(&bbhFactory));
-    SkAutoTUnref<SkPicture> referencePicture(draw_scene(NULL));
-
-    SkBitmap referenceBitmap;
-    referenceBitmap.allocN32Pixels(100, 100);
-    SkCanvas referenceCanvas(referenceBitmap);
-
-    SkBitmap bbhBitmap;
-    bbhBitmap.allocN32Pixels(100, 100);
-    SkCanvas bbhCanvas(bbhBitmap);
-
-    referenceCanvas.drawColor(SK_ColorTRANSPARENT);
-    referenceCanvas.drawPicture(referencePicture.get());
-    bbhCanvas.drawColor(SK_ColorTRANSPARENT);
-    bbhCanvas.drawPicture(bbhPicture.get());
-    REPORTER_ASSERT(reporter,
-                    referenceCanvas.getSaveCount() == bbhCanvas.getSaveCount());
-    REPORTER_ASSERT(reporter,
-                    referenceCanvas.getTotalMatrix() == bbhCanvas.getTotalMatrix());
-    check_bms(reporter, referenceBitmap, bbhBitmap);
-
-    referenceCanvas.drawColor(SK_ColorTRANSPARENT);
-    referenceCanvas.clipRect(SkRect::MakeWH(50, 50));
-    referenceCanvas.drawPicture(referencePicture.get());
-    bbhCanvas.drawColor(SK_ColorTRANSPARENT);
-    bbhCanvas.clipRect(SkRect::MakeWH(50, 50));
-    bbhCanvas.drawPicture(bbhPicture.get());
-    REPORTER_ASSERT(reporter,
-                    referenceCanvas.getSaveCount() == bbhCanvas.getSaveCount());
-    REPORTER_ASSERT(reporter,
-                    referenceCanvas.getTotalMatrix() == bbhCanvas.getTotalMatrix());
-    check_bms(reporter, referenceBitmap, bbhBitmap);
-
-    referenceCanvas.drawColor(SK_ColorTRANSPARENT);
-    referenceCanvas.clipRect(SkRect::MakeWH(10, 10));
-    referenceCanvas.drawPicture(referencePicture.get());
-    bbhCanvas.drawColor(SK_ColorTRANSPARENT);
-    bbhCanvas.clipRect(SkRect::MakeWH(10, 10));
-    bbhCanvas.drawPicture(bbhPicture.get());
-    REPORTER_ASSERT(reporter,
-                    referenceCanvas.getSaveCount() == bbhCanvas.getSaveCount());
-    REPORTER_ASSERT(reporter,
-                    referenceCanvas.getTotalMatrix() == bbhCanvas.getTotalMatrix());
-    check_bms(reporter, referenceBitmap, bbhBitmap);
-}
-
-DEF_TEST(PictureStateTree, reporter) {
-    test_reference_picture(reporter);
-}
diff --git a/src/third_party/skia/tests/PictureTest.cpp b/src/third_party/skia/tests/PictureTest.cpp
index 9cd63df..0dc3bfe 100644
--- a/src/third_party/skia/tests/PictureTest.cpp
+++ b/src/third_party/skia/tests/PictureTest.cpp
@@ -5,39 +5,37 @@
  * found in the LICENSE file.
  */
 
+#include "SkBigPicture.h"
 #include "SkBBoxHierarchy.h"
 #include "SkBlurImageFilter.h"
 #include "SkCanvas.h"
+#include "SkColorMatrixFilter.h"
 #include "SkColorPriv.h"
 #include "SkDashPathEffect.h"
 #include "SkData.h"
-#include "SkDecodingImageGenerator.h"
-#include "SkError.h"
+#include "SkImageGenerator.h"
 #include "SkImageEncoder.h"
 #include "SkImageGenerator.h"
+#include "SkMD5.h"
 #include "SkPaint.h"
 #include "SkPicture.h"
+#include "SkPictureAnalyzer.h"
 #include "SkPictureRecorder.h"
-#include "SkPictureUtils.h"
 #include "SkPixelRef.h"
+#include "SkPixelSerializer.h"
+#include "SkMiniRecorder.h"
 #include "SkRRect.h"
 #include "SkRandom.h"
+#include "SkRecord.h"
 #include "SkShader.h"
 #include "SkStream.h"
+#include "sk_tool_utils.h"
 
-#if SK_SUPPORT_GPU
-#include "SkSurface.h"
-#include "GrContextFactory.h"
-#include "GrPictureUtils.h"
-#endif
 #include "Test.h"
 
 #include "SkLumaColorFilter.h"
 #include "SkColorFilterImageFilter.h"
 
-static const int gColorScale = 30;
-static const int gColorOffset = 60;
-
 static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
     bm->allocN32Pixels(w, h);
     bm->eraseColor(color);
@@ -46,555 +44,32 @@
     }
 }
 
-static void make_checkerboard(SkBitmap* bm, int w, int h, bool immutable) {
-    SkASSERT(w % 2 == 0);
-    SkASSERT(h % 2 == 0);
-    bm->allocPixels(SkImageInfo::Make(w, h, kAlpha_8_SkColorType,
-                                      kPremul_SkAlphaType));
-    SkAutoLockPixels lock(*bm);
-    for (int y = 0; y < h; y += 2) {
-        uint8_t* s = bm->getAddr8(0, y);
-        for (int x = 0; x < w; x += 2) {
-            *s++ = 0xFF;
-            *s++ = 0x00;
-        }
-        s = bm->getAddr8(0, y + 1);
-        for (int x = 0; x < w; x += 2) {
-            *s++ = 0x00;
-            *s++ = 0xFF;
-        }
-    }
-    if (immutable) {
-        bm->setImmutable();
-    }
-}
+// For a while willPlayBackBitmaps() ignored SkImages and just looked for SkBitmaps.
+static void test_images_are_found_by_willPlayBackBitmaps(skiatest::Reporter* reporter) {
+    // We just need _some_ SkImage
+    const SkPMColor pixel = 0;
+    const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
+    sk_sp<SkImage> image(SkImage::MakeRasterCopy(SkPixmap(info, &pixel, sizeof(pixel))));
 
-static void init_paint(SkPaint* paint, const SkBitmap &bm) {
-    SkShader* shader = SkShader::CreateBitmapShader(bm,
-                                                    SkShader::kClamp_TileMode,
-                                                    SkShader::kClamp_TileMode);
-    paint->setShader(shader)->unref();
-}
-
-typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&,
-                               const SkBitmap&, const SkPoint&,
-                               SkTDArray<SkPixelRef*>* usedPixRefs);
-
-static void drawpaint_proc(SkCanvas* canvas, const SkBitmap& bm,
-                           const SkBitmap& altBM, const SkPoint& pos,
-                           SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkPaint paint;
-    init_paint(&paint, bm);
-
-    canvas->drawPaint(paint);
-    *usedPixRefs->append() = bm.pixelRef();
-}
-
-static void drawpoints_proc(SkCanvas* canvas, const SkBitmap& bm,
-                            const SkBitmap& altBM, const SkPoint& pos,
-                            SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkPaint paint;
-    init_paint(&paint, bm);
-
-    // draw a rect
-    SkPoint points[5] = {
-        { pos.fX, pos.fY },
-        { pos.fX + bm.width() - 1, pos.fY },
-        { pos.fX + bm.width() - 1, pos.fY + bm.height() - 1 },
-        { pos.fX, pos.fY + bm.height() - 1 },
-        { pos.fX, pos.fY },
-    };
-
-    canvas->drawPoints(SkCanvas::kPolygon_PointMode, 5, points, paint);
-    *usedPixRefs->append() = bm.pixelRef();
-}
-
-static void drawrect_proc(SkCanvas* canvas, const SkBitmap& bm,
-                          const SkBitmap& altBM, const SkPoint& pos,
-                          SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkPaint paint;
-    init_paint(&paint, bm);
-
-    SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
-    r.offset(pos.fX, pos.fY);
-
-    canvas->drawRect(r, paint);
-    *usedPixRefs->append() = bm.pixelRef();
-}
-
-static void drawoval_proc(SkCanvas* canvas, const SkBitmap& bm,
-                          const SkBitmap& altBM, const SkPoint& pos,
-                          SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkPaint paint;
-    init_paint(&paint, bm);
-
-    SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
-    r.offset(pos.fX, pos.fY);
-
-    canvas->drawOval(r, paint);
-    *usedPixRefs->append() = bm.pixelRef();
-}
-
-static void drawrrect_proc(SkCanvas* canvas, const SkBitmap& bm,
-                           const SkBitmap& altBM, const SkPoint& pos,
-                           SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkPaint paint;
-    init_paint(&paint, bm);
-
-    SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
-    r.offset(pos.fX, pos.fY);
-
-    SkRRect rr;
-    rr.setRectXY(r, SkIntToScalar(bm.width())/4, SkIntToScalar(bm.height())/4);
-    canvas->drawRRect(rr, paint);
-    *usedPixRefs->append() = bm.pixelRef();
-}
-
-static void drawpath_proc(SkCanvas* canvas, const SkBitmap& bm,
-                          const SkBitmap& altBM, const SkPoint& pos,
-                          SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkPaint paint;
-    init_paint(&paint, bm);
-
-    SkPath path;
-    path.lineTo(bm.width()/2.0f, SkIntToScalar(bm.height()));
-    path.lineTo(SkIntToScalar(bm.width()), 0);
-    path.close();
-    path.offset(pos.fX, pos.fY);
-
-    canvas->drawPath(path, paint);
-    *usedPixRefs->append() = bm.pixelRef();
-}
-
-static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
-                            const SkBitmap& altBM, const SkPoint& pos,
-                            SkTDArray<SkPixelRef*>* usedPixRefs) {
-    canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
-    *usedPixRefs->append() = bm.pixelRef();
-}
-
-static void drawbitmap_withshader_proc(SkCanvas* canvas, const SkBitmap& bm,
-                                       const SkBitmap& altBM, const SkPoint& pos,
-                                       SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkPaint paint;
-    init_paint(&paint, bm);
-
-    // The bitmap in the paint is ignored unless we're drawing an A8 bitmap
-    canvas->drawBitmap(altBM, pos.fX, pos.fY, &paint);
-    *usedPixRefs->append() = bm.pixelRef();
-    *usedPixRefs->append() = altBM.pixelRef();
-}
-
-static void drawsprite_proc(SkCanvas* canvas, const SkBitmap& bm,
-                            const SkBitmap& altBM, const SkPoint& pos,
-                            SkTDArray<SkPixelRef*>* usedPixRefs) {
-    const SkMatrix& ctm = canvas->getTotalMatrix();
-
-    SkPoint p(pos);
-    ctm.mapPoints(&p, 1);
-
-    canvas->drawSprite(bm, (int)p.fX, (int)p.fY, NULL);
-    *usedPixRefs->append() = bm.pixelRef();
-}
-
-#if 0
-// Although specifiable, this case doesn't seem to make sense (i.e., the
-// bitmap in the shader is never used).
-static void drawsprite_withshader_proc(SkCanvas* canvas, const SkBitmap& bm,
-                                       const SkBitmap& altBM, const SkPoint& pos,
-                                       SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkPaint paint;
-    init_paint(&paint, bm);
-
-    const SkMatrix& ctm = canvas->getTotalMatrix();
-
-    SkPoint p(pos);
-    ctm.mapPoints(&p, 1);
-
-    canvas->drawSprite(altBM, (int)p.fX, (int)p.fY, &paint);
-    *usedPixRefs->append() = bm.pixelRef();
-    *usedPixRefs->append() = altBM.pixelRef();
-}
-#endif
-
-static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
-                                const SkBitmap& altBM, const SkPoint& pos,
-                                SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
-
-    r.offset(pos.fX, pos.fY);
-    canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
-    *usedPixRefs->append() = bm.pixelRef();
-}
-
-static void drawbitmaprect_withshader_proc(SkCanvas* canvas,
-                                           const SkBitmap& bm,
-                                           const SkBitmap& altBM,
-                                           const SkPoint& pos,
-                                           SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkPaint paint;
-    init_paint(&paint, bm);
-
-    SkRect r = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
-    r.offset(pos.fX, pos.fY);
-
-    // The bitmap in the paint is ignored unless we're drawing an A8 bitmap
-    canvas->drawBitmapRectToRect(altBM, NULL, r, &paint);
-    *usedPixRefs->append() = bm.pixelRef();
-    *usedPixRefs->append() = altBM.pixelRef();
-}
-
-static void drawtext_proc(SkCanvas* canvas, const SkBitmap& bm,
-                          const SkBitmap& altBM, const SkPoint& pos,
-                          SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkPaint paint;
-    init_paint(&paint, bm);
-    paint.setTextSize(SkIntToScalar(1.5*bm.width()));
-
-    canvas->drawText("0", 1, pos.fX, pos.fY+bm.width(), paint);
-    *usedPixRefs->append() = bm.pixelRef();
-}
-
-static void drawpostext_proc(SkCanvas* canvas, const SkBitmap& bm,
-                             const SkBitmap& altBM, const SkPoint& pos,
-                             SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkPaint paint;
-    init_paint(&paint, bm);
-    paint.setTextSize(SkIntToScalar(1.5*bm.width()));
-
-    SkPoint point = { pos.fX, pos.fY + bm.height() };
-    canvas->drawPosText("O", 1, &point, paint);
-    *usedPixRefs->append() = bm.pixelRef();
-}
-
-static void drawtextonpath_proc(SkCanvas* canvas, const SkBitmap& bm,
-                                const SkBitmap& altBM, const SkPoint& pos,
-                                SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkPaint paint;
-
-    init_paint(&paint, bm);
-    paint.setTextSize(SkIntToScalar(1.5*bm.width()));
-
-    SkPath path;
-    path.lineTo(SkIntToScalar(bm.width()), 0);
-    path.offset(pos.fX, pos.fY+bm.height());
-
-    canvas->drawTextOnPath("O", 1, path, NULL, paint);
-    *usedPixRefs->append() = bm.pixelRef();
-}
-
-static void drawverts_proc(SkCanvas* canvas, const SkBitmap& bm,
-                           const SkBitmap& altBM, const SkPoint& pos,
-                           SkTDArray<SkPixelRef*>* usedPixRefs) {
-    SkPaint paint;
-    init_paint(&paint, bm);
-
-    SkPoint verts[4] = {
-        { pos.fX, pos.fY },
-        { pos.fX + bm.width(), pos.fY },
-        { pos.fX + bm.width(), pos.fY + bm.height() },
-        { pos.fX, pos.fY + bm.height() }
-    };
-    SkPoint texs[4] = { { 0, 0 },
-                        { SkIntToScalar(bm.width()), 0 },
-                        { SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) },
-                        { 0, SkIntToScalar(bm.height()) } };
-    uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 };
-
-    canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 4, verts, texs, NULL, NULL,
-                         indices, 6, paint);
-    *usedPixRefs->append() = bm.pixelRef();
-}
-
-// Return a picture with the bitmaps drawn at the specified positions.
-static SkPicture* record_bitmaps(const SkBitmap bm[],
-                                 const SkPoint pos[],
-                                 SkTDArray<SkPixelRef*> analytic[],
-                                 int count,
-                                 DrawBitmapProc proc) {
     SkPictureRecorder recorder;
-    SkCanvas* canvas = recorder.beginRecording(1000, 1000);
-    for (int i = 0; i < count; ++i) {
-        analytic[i].rewind();
-        canvas->save();
-        SkRect clipRect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY,
-                                           SkIntToScalar(bm[i].width()),
-                                           SkIntToScalar(bm[i].height()));
-        canvas->clipRect(clipRect, SkRegion::kIntersect_Op);
-        proc(canvas, bm[i], bm[count+i], pos[i], &analytic[i]);
-        canvas->restore();
-    }
-    return recorder.endRecording();
+    recorder.beginRecording(100,100)->drawImage(image, 0,0);
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
+
+    REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
 }
 
-static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
-    rect->fLeft   = rand.nextRangeScalar(-W, 2*W);
-    rect->fTop    = rand.nextRangeScalar(-H, 2*H);
-    rect->fRight  = rect->fLeft + rand.nextRangeScalar(0, W);
-    rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
-
-    // we integralize rect to make our tests more predictable, since Gather is
-    // a little sloppy.
-    SkIRect ir;
-    rect->round(&ir);
-    rect->set(ir);
-}
-
-static void draw(SkPicture* pic, int width, int height, SkBitmap* result) {
-    make_bm(result, width, height, SK_ColorBLACK, false);
-
-    SkCanvas canvas(*result);
-    canvas.drawPicture(pic);
-}
-
-template <typename T> int find_index(const T* array, T elem, int count) {
-    for (int i = 0; i < count; ++i) {
-        if (array[i] == elem) {
-            return i;
-        }
-    }
-    return -1;
-}
-
-// Return true if 'ref' is found in array[]
-static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
-    return find_index<const SkPixelRef*>(array, ref, count) >= 0;
-}
-
-// Look at each pixel that is inside 'subset', and if its color appears in
-// colors[], find the corresponding value in refs[] and append that ref into
-// array, skipping duplicates of the same value.
-// Note that gathering pixelRefs from rendered colors suffers from the problem
-// that multiple simultaneous textures (e.g., A8 for alpha and 8888 for color)
-// isn't easy to reconstruct.
-static void gather_from_image(const SkBitmap& bm, SkPixelRef* const refs[],
-                              int count, SkTDArray<SkPixelRef*>* array,
-                              const SkRect& subset) {
-    SkIRect ir;
-    subset.roundOut(&ir);
-
-    if (!ir.intersect(0, 0, bm.width()-1, bm.height()-1)) {
-        return;
-    }
-
-    // Since we only want to return unique values in array, when we scan we just
-    // set a bit for each index'd color found. In practice we only have a few
-    // distinct colors, so we just use an int's bits as our array. Hence the
-    // assert that count <= number-of-bits-in-our-int.
-    SkASSERT((unsigned)count <= 32);
-    uint32_t bitarray = 0;
-
-    SkAutoLockPixels alp(bm);
-
-    for (int y = ir.fTop; y < ir.fBottom; ++y) {
-        for (int x = ir.fLeft; x < ir.fRight; ++x) {
-            SkPMColor pmc = *bm.getAddr32(x, y);
-            // the only good case where the color is not found would be if
-            // the color is transparent, meaning no bitmap was drawn in that
-            // pixel.
-            if (pmc) {
-                uint32_t index = SkGetPackedR32(pmc);
-                SkASSERT(SkGetPackedG32(pmc) == index);
-                SkASSERT(SkGetPackedB32(pmc) == index);
-                if (0 == index) {
-                    continue;           // background color
-                }
-                SkASSERT(0 == (index - gColorOffset) % gColorScale);
-                index = (index - gColorOffset) / gColorScale;
-                SkASSERT(static_cast<int>(index) < count);
-                bitarray |= 1 << index;
-            }
-        }
-    }
-
-    for (int i = 0; i < count; ++i) {
-        if (bitarray & (1 << i)) {
-            *array->append() = refs[i];
-        }
-    }
-}
-
-static void gather_from_analytic(const SkPoint pos[], SkScalar w, SkScalar h,
-                                 const SkTDArray<SkPixelRef*> analytic[],
-                                 int count,
-                                 SkTDArray<SkPixelRef*>* result,
-                                 const SkRect& subset) {
-    for (int i = 0; i < count; ++i) {
-        SkRect rect = SkRect::MakeXYWH(pos[i].fX, pos[i].fY, w, h);
-
-        if (SkRect::Intersects(subset, rect)) {
-            result->append(analytic[i].count(), analytic[i].begin());
-        }
-    }
-}
-
-
-static const struct {
-    const DrawBitmapProc proc;
-    const char* const desc;
-} gProcs[] = {
-    {drawpaint_proc, "drawpaint"},
-    {drawpoints_proc, "drawpoints"},
-    {drawrect_proc, "drawrect"},
-    {drawoval_proc, "drawoval"},
-    {drawrrect_proc, "drawrrect"},
-    {drawpath_proc, "drawpath"},
-    {drawbitmap_proc, "drawbitmap"},
-    {drawbitmap_withshader_proc, "drawbitmap_withshader"},
-    {drawsprite_proc, "drawsprite"},
-#if 0
-    {drawsprite_withshader_proc, "drawsprite_withshader"},
-#endif
-    {drawbitmaprect_proc, "drawbitmaprect"},
-    {drawbitmaprect_withshader_proc, "drawbitmaprect_withshader"},
-    {drawtext_proc, "drawtext"},
-    {drawpostext_proc, "drawpostext"},
-    {drawtextonpath_proc, "drawtextonpath"},
-    {drawverts_proc, "drawverts"},
-};
-
-static void create_textures(SkBitmap* bm, SkPixelRef** refs, int num, int w, int h) {
-    // Our convention is that the color components contain an encoding of
-    // the index of their corresponding bitmap/pixelref. (0,0,0,0) is
-    // reserved for the background
-    for (int i = 0; i < num; ++i) {
-        make_bm(&bm[i], w, h,
-                SkColorSetARGB(0xFF,
-                               gColorScale*i+gColorOffset,
-                               gColorScale*i+gColorOffset,
-                               gColorScale*i+gColorOffset),
-                true);
-        refs[i] = bm[i].pixelRef();
-    }
-
-    // The A8 alternate bitmaps are all BW checkerboards
-    for (int i = 0; i < num; ++i) {
-        make_checkerboard(&bm[num+i], w, h, true);
-        refs[num+i] = bm[num+i].pixelRef();
-    }
-}
-
-static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
-    const int IW = 32;
-    const int IH = IW;
-    const SkScalar W = SkIntToScalar(IW);
-    const SkScalar H = W;
-
-    static const int N = 4;
-    SkBitmap bm[2*N];
-    SkPixelRef* refs[2*N];
-    SkTDArray<SkPixelRef*> analytic[N];
-
-    const SkPoint pos[N] = {
-        { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
-    };
-
-    create_textures(bm, refs, N, IW, IH);
-
-    SkRandom rand;
-    for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) {
-        SkAutoTUnref<SkPicture> pic(
-            record_bitmaps(bm, pos, analytic, N, gProcs[k].proc));
-
-        REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
-        // quick check for a small piece of each quadrant, which should just
-        // contain 1 or 2 bitmaps.
-        for (size_t  i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
-            SkRect r;
-            r.set(2, 2, W - 2, H - 2);
-            r.offset(pos[i].fX, pos[i].fY);
-            SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
-            if (!data) {
-                ERRORF(reporter, "SkPictureUtils::GatherPixelRefs returned "
-                       "NULL for %s.", gProcs[k].desc);
-                continue;
-            }
-            SkPixelRef** gatheredRefs = (SkPixelRef**)data->data();
-            int count = static_cast<int>(data->size() / sizeof(SkPixelRef*));
-            REPORTER_ASSERT(reporter, 1 == count || 2 == count);
-            if (1 == count) {
-                REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]);
-            } else if (2 == count) {
-                REPORTER_ASSERT(reporter,
-                    (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) ||
-                    (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N]));
-            }
-        }
-
-        SkBitmap image;
-        draw(pic, 2*IW, 2*IH, &image);
-
-        // Test a bunch of random (mostly) rects, and compare the gather results
-        // with a deduced list of refs by looking at the colors drawn.
-        for (int j = 0; j < 100; ++j) {
-            SkRect r;
-            rand_rect(&r, rand, 2*W, 2*H);
-
-            SkTDArray<SkPixelRef*> fromImage;
-            gather_from_image(image, refs, N, &fromImage, r);
-
-            SkTDArray<SkPixelRef*> fromAnalytic;
-            gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r);
-
-            SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
-            size_t dataSize = data ? data->size() : 0;
-            int gatherCount = static_cast<int>(dataSize / sizeof(SkPixelRef*));
-            SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
-            SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
-            SkAutoDataUnref adu(data);
-
-            // Everything that we saw drawn should appear in the analytic list
-            // but the analytic list may contain some pixelRefs that were not
-            // seen in the image (e.g., A8 textures used as masks)
-            for (int i = 0; i < fromImage.count(); ++i) {
-                if (-1 == fromAnalytic.find(fromImage[i])) {
-                    ERRORF(reporter, "PixelRef missing %d %s",
-                           i, gProcs[k].desc);
-                }
-            }
-
-            /*
-             *  GatherPixelRefs is conservative, so it can return more bitmaps
-             *  than are strictly required. Thus our check here is only that
-             *  Gather didn't miss any that we actually needed. Even that isn't
-             *  a strict requirement on Gather, which is meant to be quick and
-             *  only mostly-correct, but at the moment this test should work.
-             */
-            for (int i = 0; i < fromAnalytic.count(); ++i) {
-                bool found = find(gatherRefs, fromAnalytic[i], gatherCount);
-                if (!found) {
-                    ERRORF(reporter, "PixelRef missing %d %s",
-                           i, gProcs[k].desc);
-                }
-#if 0
-                // enable this block of code to debug failures, as it will rerun
-                // the case that failed.
-                if (!found) {
-                    SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
-                    size_t dataSize = data ? data->size() : 0;
-                }
-#endif
-            }
-        }
-    }
-}
-
-#define GENERATE_CANVAS(recorder, x) \
-    (x) ? recorder.EXPERIMENTAL_beginRecording(100, 100) \
-        : recorder.  DEPRECATED_beginRecording(100,100);
-
 /* Hit a few SkPicture::Analysis cases not handled elsewhere. */
-static void test_analysis(skiatest::Reporter* reporter, bool useNewPath) {
+static void test_analysis(skiatest::Reporter* reporter) {
     SkPictureRecorder recorder;
 
-    SkCanvas* canvas = GENERATE_CANVAS(recorder, useNewPath);
+    SkCanvas* canvas = recorder.beginRecording(100, 100);
     {
         canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
     }
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
     REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
 
-    canvas = GENERATE_CANVAS(recorder, useNewPath);
+    canvas = recorder.beginRecording(100, 100);
     {
         SkPaint paint;
         // CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
@@ -603,103 +78,16 @@
         bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
         bitmap.eraseColor(SK_ColorBLUE);
         *(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
-        SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode,
-                                                        SkShader::kClamp_TileMode);
-        paint.setShader(shader)->unref();
-        REPORTER_ASSERT(reporter,
-                        shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType);
+        paint.setShader(SkShader::MakeBitmapShader(bitmap, SkShader::kClamp_TileMode,
+                                                   SkShader::kClamp_TileMode));
+        REPORTER_ASSERT(reporter, paint.getShader()->isAImage());
 
         canvas->drawRect(SkRect::MakeWH(10, 10), paint);
     }
-    picture.reset(recorder.endRecording());
-    REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
+    REPORTER_ASSERT(reporter, recorder.finishRecordingAsPicture()->willPlayBackBitmaps());
 }
 
 
-static void test_gatherpixelrefsandrects(skiatest::Reporter* reporter) {
-    const int IW = 32;
-    const int IH = IW;
-    const SkScalar W = SkIntToScalar(IW);
-    const SkScalar H = W;
-
-    static const int N = 4;
-    SkBitmap bm[2*N];
-    SkPixelRef* refs[2*N];
-    SkTDArray<SkPixelRef*> analytic[N];
-
-    const SkPoint pos[N] = {
-        { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
-    };
-
-    create_textures(bm, refs, N, IW, IH);
-
-    SkRandom rand;
-    for (size_t k = 0; k < SK_ARRAY_COUNT(gProcs); ++k) {
-        SkAutoTUnref<SkPicture> pic(
-            record_bitmaps(bm, pos, analytic, N, gProcs[k].proc));
-
-        REPORTER_ASSERT(reporter, pic->willPlayBackBitmaps() || N == 0);
-
-        SkAutoTUnref<SkPictureUtils::SkPixelRefContainer> prCont(
-                                new SkPictureUtils::SkPixelRefsAndRectsList);
-
-        SkPictureUtils::GatherPixelRefsAndRects(pic, prCont);
-
-        // quick check for a small piece of each quadrant, which should just
-        // contain 1 or 2 bitmaps.
-        for (size_t  i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
-            SkRect r;
-            r.set(2, 2, W - 2, H - 2);
-            r.offset(pos[i].fX, pos[i].fY);
-
-            SkTDArray<SkPixelRef*> gatheredRefs;
-            prCont->query(r, &gatheredRefs);
-
-            int count = gatheredRefs.count();
-            REPORTER_ASSERT(reporter, 1 == count || 2 == count);
-            if (1 == count) {
-                REPORTER_ASSERT(reporter, gatheredRefs[0] == refs[i]);
-            } else if (2 == count) {
-                REPORTER_ASSERT(reporter,
-                    (gatheredRefs[0] == refs[i] && gatheredRefs[1] == refs[i+N]) ||
-                    (gatheredRefs[1] == refs[i] && gatheredRefs[0] == refs[i+N]));
-            }
-        }
-
-        SkBitmap image;
-        draw(pic, 2*IW, 2*IH, &image);
-
-        // Test a bunch of random (mostly) rects, and compare the gather results
-        // with the analytic results and the pixel refs seen in a rendering.
-        for (int j = 0; j < 100; ++j) {
-            SkRect r;
-            rand_rect(&r, rand, 2*W, 2*H);
-
-            SkTDArray<SkPixelRef*> fromImage;
-            gather_from_image(image, refs, N, &fromImage, r);
-
-            SkTDArray<SkPixelRef*> fromAnalytic;
-            gather_from_analytic(pos, W, H, analytic, N, &fromAnalytic, r);
-
-            SkTDArray<SkPixelRef*> gatheredRefs;
-            prCont->query(r, &gatheredRefs);
-
-            // Everything that we saw drawn should appear in the analytic list
-            // but the analytic list may contain some pixelRefs that were not
-            // seen in the image (e.g., A8 textures used as masks)
-            for (int i = 0; i < fromImage.count(); ++i) {
-                REPORTER_ASSERT(reporter, -1 != fromAnalytic.find(fromImage[i]));
-            }
-
-            // Everything in the analytic list should appear in the gathered
-            // list.
-            for (int i = 0; i < fromAnalytic.count(); ++i) {
-                REPORTER_ASSERT(reporter, -1 != gatheredRefs.find(fromAnalytic[i]));
-            }
-        }
-    }
-}
-
 #ifdef SK_DEBUG
 // Ensure that deleting an empty SkPicture does not assert. Asserts only fire
 // in debug mode, so only run in debug mode.
@@ -708,7 +96,7 @@
     // Creates an SkPictureRecord
     recorder.beginRecording(0, 0);
     // Turns that into an SkPicture
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
     // Ceates a new SkPictureRecord
     recorder.beginRecording(0, 0);
 }
@@ -717,7 +105,7 @@
 static void test_serializing_empty_picture() {
     SkPictureRecorder recorder;
     recorder.beginRecording(0, 0);
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
     SkDynamicMemoryWStream stream;
     picture->serialize(&stream);
 }
@@ -745,34 +133,54 @@
 
 #if SK_SUPPORT_GPU
 
-static void test_gpu_veto(skiatest::Reporter* reporter,
-                          bool useNewPath) {
+static SkPath make_convex_path() {
+    SkPath path;
+    path.lineTo(100, 0);
+    path.lineTo(50, 100);
+    path.close();
 
+    return path;
+}
+
+static SkPath make_concave_path() {
+    SkPath path;
+    path.lineTo(50, 50);
+    path.lineTo(100, 0);
+    path.lineTo(50, 100);
+    path.close();
+
+    return path;
+}
+
+static void test_gpu_veto(skiatest::Reporter* reporter) {
     SkPictureRecorder recorder;
 
-    SkCanvas* canvas = GENERATE_CANVAS(recorder, useNewPath);
+    SkCanvas* canvas = recorder.beginRecording(100, 100);
     {
         SkPath path;
         path.moveTo(0, 0);
         path.lineTo(50, 50);
 
         SkScalar intervals[] = { 1.0f, 1.0f };
-        SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, 0));
+        sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0));
 
         SkPaint paint;
         paint.setStyle(SkPaint::kStroke_Style);
         paint.setPathEffect(dash);
 
-        canvas->drawPath(path, paint);
+        for (int i = 0; i < 50; ++i) {
+            canvas->drawPath(path, paint);
+        }
     }
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
     // path effects currently render an SkPicture undesireable for GPU rendering
 
-    const char *reason = NULL;
-    REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL, &reason));
+    const char *reason = nullptr;
+    REPORTER_ASSERT(reporter,
+        !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization(&reason));
     REPORTER_ASSERT(reporter, reason);
 
-    canvas = GENERATE_CANVAS(recorder, useNewPath);
+    canvas = recorder.beginRecording(100, 100);
     {
         SkPath path;
 
@@ -790,11 +198,33 @@
             canvas->drawPath(path, paint);
         }
     }
-    picture.reset(recorder.endRecording());
-    // A lot of AA concave paths currently render an SkPicture undesireable for GPU rendering
-    REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
+    picture = recorder.finishRecordingAsPicture();
+    // A lot of small AA concave paths should be fine for GPU rendering
+    REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
 
-    canvas = GENERATE_CANVAS(recorder, useNewPath);
+    canvas = recorder.beginRecording(100, 100);
+    {
+        SkPath path;
+
+        path.moveTo(0, 0);
+        path.lineTo(0, 100);
+        path.lineTo(50, 50);
+        path.lineTo(100, 100);
+        path.lineTo(100, 0);
+        path.close();
+        REPORTER_ASSERT(reporter, !path.isConvex());
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        for (int i = 0; i < 50; ++i) {
+            canvas->drawPath(path, paint);
+        }
+    }
+    picture = recorder.finishRecordingAsPicture();
+    // A lot of large AA concave paths currently render an SkPicture undesireable for GPU rendering
+    REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
+
+    canvas = recorder.beginRecording(100, 100);
     {
         SkPath path;
 
@@ -814,332 +244,78 @@
             canvas->drawPath(path, paint);
         }
     }
-    picture.reset(recorder.endRecording());
+    picture = recorder.finishRecordingAsPicture();
     // hairline stroked AA concave paths are fine for GPU rendering
-    REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
+    REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
 
-    canvas = GENERATE_CANVAS(recorder, useNewPath);
+    canvas = recorder.beginRecording(100, 100);
     {
         SkPaint paint;
         SkScalar intervals [] = { 10, 20 };
-        SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
-        paint.setPathEffect(pe)->unref();
+        paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
 
         SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
-        canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
-    }
-    picture.reset(recorder.endRecording());
-    // fast-path dashed effects are fine for GPU rendering ...
-    REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
 
-    canvas = GENERATE_CANVAS(recorder, useNewPath);
+        for (int i = 0; i < 50; ++i) {
+            canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
+        }
+    }
+    picture = recorder.finishRecordingAsPicture();
+    // fast-path dashed effects are fine for GPU rendering ...
+    REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
+
+    canvas = recorder.beginRecording(100, 100);
     {
         SkPaint paint;
         SkScalar intervals [] = { 10, 20 };
-        SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
-        paint.setPathEffect(pe)->unref();
+        paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
 
-        canvas->drawRect(SkRect::MakeWH(10, 10), paint);
+        for (int i = 0; i < 50; ++i) {
+            canvas->drawRect(SkRect::MakeWH(10, 10), paint);
+        }
     }
-    picture.reset(recorder.endRecording());
+    picture = recorder.finishRecordingAsPicture();
     // ... but only when applied to drawPoint() calls
-    REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
+    REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
+
+    canvas = recorder.beginRecording(100, 100);
+    {
+        const SkPath convexClip = make_convex_path();
+        const SkPath concaveClip = make_concave_path();
+
+        for (int i = 0; i < 50; ++i) {
+            canvas->clipPath(convexClip);
+            canvas->clipPath(concaveClip);
+            canvas->clipPath(convexClip, kIntersect_SkClipOp, true);
+            canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint());
+        }
+    }
+    picture = recorder.finishRecordingAsPicture();
+    // Convex clips and non-AA concave clips are fine on the GPU.
+    REPORTER_ASSERT(reporter, SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
+
+    canvas = recorder.beginRecording(100, 100);
+    {
+        const SkPath concaveClip = make_concave_path();
+        for (int i = 0; i < 50; ++i) {
+            canvas->clipPath(concaveClip, kIntersect_SkClipOp, true);
+            canvas->drawRect(SkRect::MakeWH(100, 100), SkPaint());
+        }
+    }
+    picture = recorder.finishRecordingAsPicture();
+    // ... but AA concave clips are not.
+    REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
 
     // Nest the previous picture inside a new one.
-    // This doesn't work in the old backend.
-    if (useNewPath) {
-        canvas = GENERATE_CANVAS(recorder, useNewPath);
-        {
-            canvas->drawPicture(picture.get());
-        }
-        picture.reset(recorder.endRecording());
-        REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
+    canvas = recorder.beginRecording(100, 100);
+    {
+        canvas->drawPicture(picture);
     }
+    picture = recorder.finishRecordingAsPicture();
+    REPORTER_ASSERT(reporter, !SkPictureGpuAnalyzer(picture).suitableForGpuRasterization());
 }
 
-#undef GENERATE_CANVAS
-
-static void test_gpu_picture_optimization(skiatest::Reporter* reporter,
-                                          GrContextFactory* factory) {
-    for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
-        GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
-
-        if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
-            continue;
-        }
-
-        GrContext* context = factory->get(glCtxType);
-
-        if (NULL == context) {
-            continue;
-        }
-
-        static const int kWidth = 100;
-        static const int kHeight = 100;
-
-        SkAutoTUnref<SkPicture> pict, child;
-
-        {
-            SkPictureRecorder recorder;
-
-            SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth), SkIntToScalar(kHeight));
-
-            c->saveLayer(NULL, NULL);
-            c->restore();
-
-            child.reset(recorder.endRecording());
-        }
-
-        // create a picture with the structure:
-        // 1)
-        //      SaveLayer
-        //      Restore
-        // 2)
-        //      SaveLayer
-        //          Translate
-        //          SaveLayer w/ bound
-        //          Restore
-        //      Restore
-        // 3)
-        //      SaveLayer w/ copyable paint
-        //      Restore
-        // 4)
-        //      SaveLayer
-        //          DrawPicture (which has a SaveLayer/Restore pair)
-        //      Restore
-        // 5)
-        //      SaveLayer
-        //          DrawPicture with Matrix & Paint (with SaveLayer/Restore pair)
-        //      Restore
-        {
-            SkPictureRecorder recorder;
-
-            SkCanvas* c = recorder.beginRecording(SkIntToScalar(kWidth),
-                                                  SkIntToScalar(kHeight));
-            // 1)
-            c->saveLayer(NULL, NULL); // layer #0
-            c->restore();
-
-            // 2)
-            c->saveLayer(NULL, NULL); // layer #1
-                c->translate(kWidth/2.0f, kHeight/2.0f);
-                SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2);
-                c->saveLayer(&r, NULL); // layer #2
-                c->restore();
-            c->restore();
-
-            // 3)
-            {
-                SkPaint p;
-                p.setColor(SK_ColorRED);
-                c->saveLayer(NULL, &p); // layer #3
-                c->restore();
-            }
-
-            SkPaint layerPaint;
-            layerPaint.setColor(SK_ColorRED);  // Non-alpha only to avoid SaveLayerDrawRestoreNooper
-            // 4)
-            {
-                c->saveLayer(NULL, &layerPaint);  // layer #4
-                    c->drawPicture(child);  // layer #5 inside picture
-                c->restore();
-            }
-            // 5
-            {
-                SkPaint picturePaint;
-                SkMatrix trans;
-                trans.setTranslate(10, 10);
-
-                c->saveLayer(NULL, &layerPaint);  // layer #6
-                    c->drawPicture(child, &trans, &picturePaint); // layer #7 inside picture
-                c->restore();
-            }
-
-            pict.reset(recorder.endRecording());
-        }
-
-        // Now test out the SaveLayer extraction
-        {
-            SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
-
-            SkAutoTUnref<SkSurface> surface(SkSurface::NewScratchRenderTarget(context, info));
-
-            SkCanvas* canvas = surface->getCanvas();
-
-            canvas->EXPERIMENTAL_optimize(pict);
-
-            SkPicture::AccelData::Key key = GrAccelData::ComputeAccelDataKey();
-
-            const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key);
-            REPORTER_ASSERT(reporter, data);
-
-            const GrAccelData *gpuData = static_cast<const GrAccelData*>(data);
-            REPORTER_ASSERT(reporter, 8 == gpuData->numSaveLayers());
-
-            const GrAccelData::SaveLayerInfo& info0 = gpuData->saveLayerInfo(0);
-            // The parent/child layers appear in reverse order
-            const GrAccelData::SaveLayerInfo& info1 = gpuData->saveLayerInfo(2);
-            const GrAccelData::SaveLayerInfo& info2 = gpuData->saveLayerInfo(1);
-
-            const GrAccelData::SaveLayerInfo& info3 = gpuData->saveLayerInfo(3);
-
-            // The parent/child layers appear in reverse order
-            const GrAccelData::SaveLayerInfo& info4 = gpuData->saveLayerInfo(5);
-            const GrAccelData::SaveLayerInfo& info5 = gpuData->saveLayerInfo(4);
-
-            // The parent/child layers appear in reverse order
-            const GrAccelData::SaveLayerInfo& info6 = gpuData->saveLayerInfo(7);
-            const GrAccelData::SaveLayerInfo& info7 = gpuData->saveLayerInfo(6);
-
-            REPORTER_ASSERT(reporter, info0.fValid);
-            REPORTER_ASSERT(reporter, NULL == info0.fPicture);
-            REPORTER_ASSERT(reporter, kWidth == info0.fSize.fWidth &&
-                                      kHeight == info0.fSize.fHeight);
-            REPORTER_ASSERT(reporter, info0.fOriginXform.isIdentity());
-            REPORTER_ASSERT(reporter, 0 == info0.fOffset.fX && 0 == info0.fOffset.fY);
-            REPORTER_ASSERT(reporter, NULL == info0.fPaint);
-            REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers);
-
-            REPORTER_ASSERT(reporter, info1.fValid);
-            REPORTER_ASSERT(reporter, NULL == info1.fPicture);
-            REPORTER_ASSERT(reporter, kWidth == info1.fSize.fWidth &&
-                                      kHeight == info1.fSize.fHeight);
-            REPORTER_ASSERT(reporter, info1.fOriginXform.isIdentity());
-            REPORTER_ASSERT(reporter, 0 == info1.fOffset.fX && 0 == info1.fOffset.fY);
-            REPORTER_ASSERT(reporter, NULL == info1.fPaint);
-            REPORTER_ASSERT(reporter, !info1.fIsNested &&
-                                      info1.fHasNestedLayers); // has a nested SL
-
-            REPORTER_ASSERT(reporter, info2.fValid);
-            REPORTER_ASSERT(reporter, NULL == info2.fPicture);
-            REPORTER_ASSERT(reporter, kWidth / 2 == info2.fSize.fWidth &&
-                                      kHeight/2 == info2.fSize.fHeight); // bound reduces size
-            REPORTER_ASSERT(reporter, !info2.fOriginXform.isIdentity());
-            REPORTER_ASSERT(reporter, kWidth/2 == info2.fOffset.fX &&   // translated
-                                      kHeight/2 == info2.fOffset.fY);
-            REPORTER_ASSERT(reporter, NULL == info1.fPaint);
-            REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested
-
-            REPORTER_ASSERT(reporter, info3.fValid);
-            REPORTER_ASSERT(reporter, NULL == info3.fPicture);
-            REPORTER_ASSERT(reporter, kWidth == info3.fSize.fWidth &&
-                                      kHeight == info3.fSize.fHeight);
-            REPORTER_ASSERT(reporter, info3.fOriginXform.isIdentity());
-            REPORTER_ASSERT(reporter, 0 == info3.fOffset.fX && 0 == info3.fOffset.fY);
-            REPORTER_ASSERT(reporter, info3.fPaint);
-            REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers);
-
-            REPORTER_ASSERT(reporter, info4.fValid);
-            REPORTER_ASSERT(reporter, NULL == info4.fPicture);
-            REPORTER_ASSERT(reporter, kWidth == info4.fSize.fWidth &&
-                                      kHeight == info4.fSize.fHeight);
-            REPORTER_ASSERT(reporter, 0 == info4.fOffset.fX && 0 == info4.fOffset.fY);
-            REPORTER_ASSERT(reporter, info4.fOriginXform.isIdentity());
-            REPORTER_ASSERT(reporter, info4.fPaint);
-            REPORTER_ASSERT(reporter, !info4.fIsNested &&
-                                      info4.fHasNestedLayers); // has a nested SL
-
-            REPORTER_ASSERT(reporter, info5.fValid);
-            REPORTER_ASSERT(reporter, child == info5.fPicture); // in a child picture
-            REPORTER_ASSERT(reporter, kWidth == info5.fSize.fWidth &&
-                                      kHeight == info5.fSize.fHeight);
-            REPORTER_ASSERT(reporter, 0 == info5.fOffset.fX && 0 == info5.fOffset.fY);
-            REPORTER_ASSERT(reporter, info5.fOriginXform.isIdentity());
-            REPORTER_ASSERT(reporter, NULL == info5.fPaint);
-            REPORTER_ASSERT(reporter, info5.fIsNested && !info5.fHasNestedLayers); // is nested
-
-            REPORTER_ASSERT(reporter, info6.fValid);
-            REPORTER_ASSERT(reporter, NULL == info6.fPicture);
-            REPORTER_ASSERT(reporter, kWidth == info6.fSize.fWidth &&
-                                      kHeight == info6.fSize.fHeight);
-            REPORTER_ASSERT(reporter, 0 == info6.fOffset.fX && 0 == info6.fOffset.fY);
-            REPORTER_ASSERT(reporter, info6.fOriginXform.isIdentity());
-            REPORTER_ASSERT(reporter, info6.fPaint);
-            REPORTER_ASSERT(reporter, !info6.fIsNested &&
-                                      info6.fHasNestedLayers); // has a nested SL
-
-            REPORTER_ASSERT(reporter, info7.fValid);
-            REPORTER_ASSERT(reporter, child == info7.fPicture); // in a child picture
-            REPORTER_ASSERT(reporter, kWidth == info7.fSize.fWidth &&
-                                      kHeight == info7.fSize.fHeight);
-            REPORTER_ASSERT(reporter, 0 == info7.fOffset.fX && 0 == info7.fOffset.fY);
-            REPORTER_ASSERT(reporter, info7.fOriginXform.isIdentity());
-            REPORTER_ASSERT(reporter, NULL == info7.fPaint);
-            REPORTER_ASSERT(reporter, info7.fIsNested && !info7.fHasNestedLayers); // is nested
-        }
-    }
-}
-
-#endif
-
-static void test_has_text(skiatest::Reporter* reporter, bool useNewPath) {
-    SkPictureRecorder recorder;
-#define BEGIN_RECORDING useNewPath ? recorder.EXPERIMENTAL_beginRecording(100, 100) \
-                                   : recorder.  DEPRECATED_beginRecording(100, 100)
-
-    SkCanvas* canvas = BEGIN_RECORDING;
-    {
-        canvas->drawRect(SkRect::MakeWH(20, 20), SkPaint());
-    }
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
-    REPORTER_ASSERT(reporter, !picture->hasText());
-
-    SkPoint point = SkPoint::Make(10, 10);
-    canvas = BEGIN_RECORDING;
-    {
-        canvas->drawText("Q", 1, point.fX, point.fY, SkPaint());
-    }
-    picture.reset(recorder.endRecording());
-    REPORTER_ASSERT(reporter, picture->hasText());
-
-    canvas = BEGIN_RECORDING;
-    {
-        canvas->drawPosText("Q", 1, &point, SkPaint());
-    }
-    picture.reset(recorder.endRecording());
-    REPORTER_ASSERT(reporter, picture->hasText());
-
-    canvas = BEGIN_RECORDING;
-    {
-        canvas->drawPosTextH("Q", 1, &point.fX, point.fY, SkPaint());
-    }
-    picture.reset(recorder.endRecording());
-    REPORTER_ASSERT(reporter, picture->hasText());
-
-    canvas = BEGIN_RECORDING;
-    {
-        SkPath path;
-        path.moveTo(0, 0);
-        path.lineTo(50, 50);
-
-        canvas->drawTextOnPathHV("Q", 1, path, point.fX, point.fY, SkPaint());
-    }
-    picture.reset(recorder.endRecording());
-    REPORTER_ASSERT(reporter, picture->hasText());
-
-    canvas = BEGIN_RECORDING;
-    {
-        SkPath path;
-        path.moveTo(0, 0);
-        path.lineTo(50, 50);
-
-        canvas->drawTextOnPath("Q", 1, path, NULL, SkPaint());
-    }
-    picture.reset(recorder.endRecording());
-    REPORTER_ASSERT(reporter, picture->hasText());
-
-    // Nest the previous picture inside a new one.
-    // This doesn't work in the old backend.
-    if (useNewPath) {
-        canvas = BEGIN_RECORDING;
-        {
-            canvas->drawPicture(picture.get());
-        }
-        picture.reset(recorder.endRecording());
-        REPORTER_ASSERT(reporter, picture->hasText());
-    }
-#undef BEGIN_RECORDING
-}
+#endif // SK_SUPPORT_GPU
 
 static void set_canvas_to_save_count_4(SkCanvas* canvas) {
     canvas->restoreToCount(1);
@@ -1160,18 +336,17 @@
         , fRestoreCount(0){
     }
 
-    virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
-                                            SaveFlags flags) SK_OVERRIDE {
+    SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
         ++fSaveLayerCount;
-        return this->INHERITED::willSaveLayer(bounds, paint, flags);
+        return this->INHERITED::getSaveLayerStrategy(rec);
     }
 
-    virtual void willSave() SK_OVERRIDE {
+    void willSave() override {
         ++fSaveCount;
         this->INHERITED::willSave();
     }
 
-    virtual void willRestore() SK_OVERRIDE {
+    void willRestore() override {
         ++fRestoreCount;
         this->INHERITED::willRestore();
     }
@@ -1207,14 +382,14 @@
 // the 'partialReplay' method.
 class SkPictureRecorderReplayTester {
 public:
-    static SkPicture* Copy(SkPictureRecorder* recorder) {
+    static sk_sp<SkPicture> Copy(SkPictureRecorder* recorder) {
         SkPictureRecorder recorder2;
 
         SkCanvas* canvas = recorder2.beginRecording(10, 10);
 
         recorder->partialReplay(canvas);
 
-        return recorder2.endRecording();
+        return recorder2.finishRecordingAsPicture();
     }
 };
 
@@ -1222,7 +397,7 @@
     SkRect clipRect = SkRect::MakeWH(2, 2);
     SkRect drawRect = SkRect::MakeWH(10, 10);
     canvas->save();
-        canvas->clipRect(clipRect, SkRegion::kReplace_Op);
+        canvas->clipRect(clipRect, kReplace_SkClipOp);
         canvas->translate(1.0f, 1.0f);
         SkPaint p;
         p.setColor(SK_ColorGREEN);
@@ -1241,18 +416,14 @@
 
     SkMatrix beforeMatrix = canvas.getTotalMatrix();
 
-    SkRect beforeClip;
-
-    canvas.getClipBounds(&beforeClip);
+    SkRect beforeClip = canvas.getLocalClipBounds();
 
     canvas.drawPicture(picture);
 
     REPORTER_ASSERT(reporter, beforeSaveCount == canvas.getSaveCount());
     REPORTER_ASSERT(reporter, beforeMatrix == canvas.getTotalMatrix());
 
-    SkRect afterClip;
-
-    canvas.getClipBounds(&afterClip);
+    SkRect afterClip = canvas.getLocalClipBounds();
 
     REPORTER_ASSERT(reporter, afterClip == beforeClip);
 }
@@ -1265,21 +436,21 @@
 
         SkCanvas* canvas = recorder.beginRecording(10, 10);
 
-        canvas->saveLayer(NULL, NULL);
+        canvas->saveLayer(nullptr, nullptr);
 
-        SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
+        sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
 
         // The extra save and restore comes from the Copy process.
-        check_save_state(reporter, copy, 2, 1, 3);
+        check_save_state(reporter, copy.get(), 2, 1, 3);
 
-        canvas->saveLayer(NULL, NULL);
+        canvas->saveLayer(nullptr, nullptr);
 
-        SkAutoTUnref<SkPicture> final(recorder.endRecording());
+        sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
 
-        check_save_state(reporter, final, 1, 2, 3);
+        check_save_state(reporter, final.get(), 1, 2, 3);
 
         // The copy shouldn't pick up any operations added after it was made
-        check_save_state(reporter, copy, 2, 1, 3);
+        check_save_state(reporter, copy.get(), 2, 1, 3);
     }
 
     // (partially) check leakage of draw ops
@@ -1293,7 +464,7 @@
 
         canvas->drawRect(r, p);
 
-        SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
+        sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
 
         REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
 
@@ -1301,9 +472,9 @@
         make_bm(&bm, 10, 10, SK_ColorRED, true);
 
         r.offset(5.0f, 5.0f);
-        canvas->drawBitmapRectToRect(bm, NULL, r);
+        canvas->drawBitmapRect(bm, r, nullptr);
 
-        SkAutoTUnref<SkPicture> final(recorder.endRecording());
+        sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
         REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
 
         REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
@@ -1316,19 +487,19 @@
     {
         SkPictureRecorder recorder;
 
-        SkCanvas* canvas = recorder.beginRecording(4, 3, NULL, 0);
+        SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0);
         create_imbalance(canvas);
 
         int expectedSaveCount = canvas->getSaveCount();
 
-        SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
-        check_balance(reporter, copy);
+        sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
+        check_balance(reporter, copy.get());
 
         REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount());
 
         // End the recording of source to test the picture finalization
         // process isn't complicated by the partialReplay step
-        SkAutoTUnref<SkPicture> final(recorder.endRecording());
+        sk_sp<SkPicture> final(recorder.finishRecordingAsPicture());
     }
 }
 
@@ -1352,7 +523,7 @@
         canvas->save();
         canvas->translate(10, 10);
         canvas->drawRect(rect, paint);
-        SkAutoTUnref<SkPicture> extraSavePicture(recorder.endRecording());
+        sk_sp<SkPicture> extraSavePicture(recorder.finishRecordingAsPicture());
 
         testCanvas.drawPicture(extraSavePicture);
         REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
@@ -1373,7 +544,7 @@
         canvas->restore();
         canvas->restore();
         canvas->restore();
-        SkAutoTUnref<SkPicture> extraRestorePicture(recorder.endRecording());
+        sk_sp<SkPicture> extraRestorePicture(recorder.finishRecordingAsPicture());
 
         testCanvas.drawPicture(extraRestorePicture);
         REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
@@ -1385,7 +556,7 @@
         SkCanvas* canvas = recorder.beginRecording(100, 100);
         canvas->translate(10, 10);
         canvas->drawRect(rect, paint);
-        SkAutoTUnref<SkPicture> noSavePicture(recorder.endRecording());
+        sk_sp<SkPicture> noSavePicture(recorder.finishRecordingAsPicture());
 
         testCanvas.drawPicture(noSavePicture);
         REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
@@ -1406,7 +577,7 @@
         for (int i = 0; i < 1000; ++i) {
             rand_op(canvas, rand);
         }
-        SkAutoTUnref<SkPicture> picture(recorder.endRecording());
+        sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
 
         rand = rand2;
     }
@@ -1422,7 +593,7 @@
             canvas->clipRect(rect);
             canvas->restore();
         }
-        SkAutoTUnref<SkPicture> picture(recorder.endRecording());
+        sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
     }
 }
 
@@ -1437,121 +608,13 @@
     SkPictureRecorder recorder;
     SkCanvas* recordingCanvas = recorder.beginRecording(100, 100);
     recordingCanvas->drawBitmap(bm, 0, 0);
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
 
     SkCanvas canvas;
     canvas.drawPicture(picture);
 }
 #endif
 
-static SkData* encode_bitmap_to_data(size_t*, const SkBitmap& bm) {
-    return SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, 100);
-}
-
-static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
-    SkPictureRecorder recorder;
-    SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()),
-                                               SkIntToScalar(bitmap.height()));
-    canvas->drawBitmap(bitmap, 0, 0);
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
-
-    SkDynamicMemoryWStream wStream;
-    picture->serialize(&wStream, &encode_bitmap_to_data);
-    return wStream.copyToData();
-}
-
-struct ErrorContext {
-    int fErrors;
-    skiatest::Reporter* fReporter;
-};
-
-static void assert_one_parse_error_cb(SkError error, void* context) {
-    ErrorContext* errorContext = static_cast<ErrorContext*>(context);
-    errorContext->fErrors++;
-    // This test only expects one error, and that is a kParseError. If there are others,
-    // there is some unknown problem.
-    REPORTER_ASSERT_MESSAGE(errorContext->fReporter, 1 == errorContext->fErrors,
-                            "This threw more errors than expected.");
-    REPORTER_ASSERT_MESSAGE(errorContext->fReporter, kParseError_SkError == error,
-                            SkGetLastErrorString());
-}
-
-static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
-    // Create a bitmap that will be encoded.
-    SkBitmap original;
-    make_bm(&original, 100, 100, SK_ColorBLUE, true);
-    SkDynamicMemoryWStream wStream;
-    if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
-        return;
-    }
-    SkAutoDataUnref data(wStream.copyToData());
-
-    SkBitmap bm;
-    bool installSuccess = SkInstallDiscardablePixelRef(
-         SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()), &bm);
-    REPORTER_ASSERT(reporter, installSuccess);
-
-    // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
-    // Flattening original will follow the old path of performing an encode, while flattening bm
-    // will use the already encoded data.
-    SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
-    SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
-    REPORTER_ASSERT(reporter, picture1->equals(picture2));
-    // Now test that a parse error was generated when trying to create a new SkPicture without
-    // providing a function to decode the bitmap.
-    ErrorContext context;
-    context.fErrors = 0;
-    context.fReporter = reporter;
-    SkSetErrorCallback(assert_one_parse_error_cb, &context);
-    SkMemoryStream pictureStream(picture1);
-    SkClearLastError();
-    SkAutoUnref pictureFromStream(SkPicture::CreateFromStream(&pictureStream, NULL));
-    REPORTER_ASSERT(reporter, pictureFromStream.get() != NULL);
-    SkClearLastError();
-    SkSetErrorCallback(NULL, NULL);
-}
-
-static void test_draw_empty(skiatest::Reporter* reporter) {
-    SkBitmap result;
-    make_bm(&result, 2, 2, SK_ColorBLACK, false);
-
-    SkCanvas canvas(result);
-
-    {
-        // stock SkPicture
-        SkPictureRecorder recorder;
-        recorder.beginRecording(1, 1);
-        SkAutoTUnref<SkPicture> picture(recorder.endRecording());
-
-        canvas.drawPicture(picture);
-    }
-
-    {
-        // tile grid
-        SkTileGridFactory::TileGridInfo gridInfo;
-        gridInfo.fMargin.setEmpty();
-        gridInfo.fOffset.setZero();
-        gridInfo.fTileInterval.set(1, 1);
-
-        SkTileGridFactory factory(gridInfo);
-        SkPictureRecorder recorder;
-        recorder.beginRecording(1, 1, &factory);
-        SkAutoTUnref<SkPicture> picture(recorder.endRecording());
-
-        canvas.drawPicture(picture);
-    }
-
-    {
-        // RTree
-        SkRTreeFactory factory;
-        SkPictureRecorder recorder;
-        recorder.beginRecording(1, 1, &factory);
-        SkAutoTUnref<SkPicture> picture(recorder.endRecording());
-
-        canvas.drawPicture(picture);
-    }
-}
-
 static void test_clip_bound_opt(skiatest::Reporter* reporter) {
     // Test for crbug.com/229011
     SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(4), SkIntToScalar(4),
@@ -1574,9 +637,8 @@
     // Testing conservative-raster-clip that is enabled by PictureRecord
     {
         SkCanvas* canvas = recorder.beginRecording(10, 10);
-        canvas->clipPath(invPath, SkRegion::kIntersect_Op);
-        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
-        REPORTER_ASSERT(reporter, true == nonEmpty);
+        canvas->clipPath(invPath);
+        clipBounds = canvas->getDeviceClipBounds();
         REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
         REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
         REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
@@ -1584,10 +646,9 @@
     }
     {
         SkCanvas* canvas = recorder.beginRecording(10, 10);
-        canvas->clipPath(path, SkRegion::kIntersect_Op);
-        canvas->clipPath(invPath, SkRegion::kIntersect_Op);
-        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
-        REPORTER_ASSERT(reporter, true == nonEmpty);
+        canvas->clipPath(path);
+        canvas->clipPath(invPath);
+        clipBounds = canvas->getDeviceClipBounds();
         REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
         REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
         REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
@@ -1595,10 +656,9 @@
     }
     {
         SkCanvas* canvas = recorder.beginRecording(10, 10);
-        canvas->clipPath(path, SkRegion::kIntersect_Op);
-        canvas->clipPath(invPath, SkRegion::kUnion_Op);
-        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
-        REPORTER_ASSERT(reporter, true == nonEmpty);
+        canvas->clipPath(path);
+        canvas->clipPath(invPath, kUnion_SkClipOp);
+        clipBounds = canvas->getDeviceClipBounds();
         REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
         REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
         REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
@@ -1606,9 +666,8 @@
     }
     {
         SkCanvas* canvas = recorder.beginRecording(10, 10);
-        canvas->clipPath(path, SkRegion::kDifference_Op);
-        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
-        REPORTER_ASSERT(reporter, true == nonEmpty);
+        canvas->clipPath(path, kDifference_SkClipOp);
+        clipBounds = canvas->getDeviceClipBounds();
         REPORTER_ASSERT(reporter, 0 == clipBounds.fLeft);
         REPORTER_ASSERT(reporter, 0 == clipBounds.fTop);
         REPORTER_ASSERT(reporter, 10 == clipBounds.fBottom);
@@ -1616,12 +675,11 @@
     }
     {
         SkCanvas* canvas = recorder.beginRecording(10, 10);
-        canvas->clipPath(path, SkRegion::kReverseDifference_Op);
-        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
+        canvas->clipPath(path, kReverseDifference_SkClipOp);
+        clipBounds = canvas->getDeviceClipBounds();
         // True clip is actually empty in this case, but the best
         // determination we can make using only bounds as input is that the
         // clip is included in the bounds of 'path'.
-        REPORTER_ASSERT(reporter, true == nonEmpty);
         REPORTER_ASSERT(reporter, 7 == clipBounds.fLeft);
         REPORTER_ASSERT(reporter, 7 == clipBounds.fTop);
         REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
@@ -1629,10 +687,9 @@
     }
     {
         SkCanvas* canvas = recorder.beginRecording(10, 10);
-        canvas->clipPath(path, SkRegion::kIntersect_Op);
-        canvas->clipPath(path2, SkRegion::kXOR_Op);
-        bool nonEmpty = canvas->getClipDeviceBounds(&clipBounds);
-        REPORTER_ASSERT(reporter, true == nonEmpty);
+        canvas->clipPath(path, kIntersect_SkClipOp);
+        canvas->clipPath(path2, kXOR_SkClipOp);
+        clipBounds = canvas->getDeviceClipBounds();
         REPORTER_ASSERT(reporter, 6 == clipBounds.fLeft);
         REPORTER_ASSERT(reporter, 6 == clipBounds.fTop);
         REPORTER_ASSERT(reporter, 8 == clipBounds.fBottom);
@@ -1640,6 +697,34 @@
     }
 }
 
+static void test_cull_rect_reset(skiatest::Reporter* reporter) {
+    SkPictureRecorder recorder;
+    SkRect bounds = SkRect::MakeWH(10, 10);
+    SkRTreeFactory factory;
+    SkCanvas* canvas = recorder.beginRecording(bounds, &factory);
+    bounds = SkRect::MakeWH(100, 100);
+    SkPaint paint;
+    canvas->drawRect(bounds, paint);
+    canvas->drawRect(bounds, paint);
+    sk_sp<SkPicture> p(recorder.finishRecordingAsPictureWithCull(bounds));
+    const SkBigPicture* picture = p->asSkBigPicture();
+    REPORTER_ASSERT(reporter, picture);
+
+    SkRect finalCullRect = picture->cullRect();
+    REPORTER_ASSERT(reporter, 0 == finalCullRect.fLeft);
+    REPORTER_ASSERT(reporter, 0 == finalCullRect.fTop);
+    REPORTER_ASSERT(reporter, 100 == finalCullRect.fBottom);
+    REPORTER_ASSERT(reporter, 100 == finalCullRect.fRight);
+
+    const SkBBoxHierarchy* pictureBBH = picture->bbh();
+    SkRect bbhCullRect = pictureBBH->getRootBound();
+    REPORTER_ASSERT(reporter, 0 == bbhCullRect.fLeft);
+    REPORTER_ASSERT(reporter, 0 == bbhCullRect.fTop);
+    REPORTER_ASSERT(reporter, 100 == bbhCullRect.fBottom);
+    REPORTER_ASSERT(reporter, 100 == bbhCullRect.fRight);
+}
+
+
 /**
  * A canvas that records the number of clip commands.
  */
@@ -1650,28 +735,22 @@
         , fClipCount(0){
     }
 
-    virtual void onClipRect(const SkRect& r,
-                            SkRegion::Op op,
-                            ClipEdgeStyle edgeStyle) SK_OVERRIDE {
+    void onClipRect(const SkRect& r, SkClipOp op, ClipEdgeStyle edgeStyle) override {
         fClipCount += 1;
         this->INHERITED::onClipRect(r, op, edgeStyle);
     }
 
-    virtual void onClipRRect(const SkRRect& rrect,
-                             SkRegion::Op op,
-                             ClipEdgeStyle edgeStyle)SK_OVERRIDE {
+    void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle)override {
         fClipCount += 1;
         this->INHERITED::onClipRRect(rrect, op, edgeStyle);
     }
 
-    virtual void onClipPath(const SkPath& path,
-                            SkRegion::Op op,
-                            ClipEdgeStyle edgeStyle) SK_OVERRIDE {
+    void onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) override {
         fClipCount += 1;
         this->INHERITED::onClipPath(path, op, edgeStyle);
     }
 
-    virtual void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE {
+    void onClipRegion(const SkRegion& deviceRgn, SkClipOp op) override {
         fClipCount += 1;
         this->INHERITED::onClipRegion(deviceRgn, op);
     }
@@ -1688,14 +767,14 @@
     SkPictureRecorder recorder;
     SkCanvas* canvas = recorder.beginRecording(10, 10);
 
-    canvas->clipRect(SkRect::MakeEmpty(), SkRegion::kReplace_Op);
+    canvas->clipRect(SkRect::MakeEmpty(), kReplace_SkClipOp);
     // The following expanding clip should not be skipped.
-    canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), SkRegion::kUnion_Op);
+    canvas->clipRect(SkRect::MakeXYWH(4, 4, 3, 3), kUnion_SkClipOp);
     // Draw something so the optimizer doesn't just fold the world.
     SkPaint p;
     p.setColor(SK_ColorBLUE);
     canvas->drawPaint(p);
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
 
     ClipCountingCanvas testCanvas(10, 10);
     picture->playback(&testCanvas);
@@ -1711,37 +790,37 @@
     SkPictureRecorder recorder;
 
     recorder.beginRecording(10, 10);
-    SkAutoTUnref<SkPicture> childPlain(recorder.endRecording());
+    sk_sp<SkPicture> childPlain(recorder.finishRecordingAsPicture());
     REPORTER_ASSERT(reporter, !childPlain->willPlayBackBitmaps()); // 0
 
     recorder.beginRecording(10, 10)->drawBitmap(bm, 0, 0);
-    SkAutoTUnref<SkPicture> childWithBitmap(recorder.endRecording());
+    sk_sp<SkPicture> childWithBitmap(recorder.finishRecordingAsPicture());
     REPORTER_ASSERT(reporter, childWithBitmap->willPlayBackBitmaps()); // 1
 
     {
         SkCanvas* canvas = recorder.beginRecording(10, 10);
         canvas->drawPicture(childPlain);
-        SkAutoTUnref<SkPicture> parentPP(recorder.endRecording());
+        sk_sp<SkPicture> parentPP(recorder.finishRecordingAsPicture());
         REPORTER_ASSERT(reporter, !parentPP->willPlayBackBitmaps()); // 0
     }
     {
         SkCanvas* canvas = recorder.beginRecording(10, 10);
         canvas->drawPicture(childWithBitmap);
-        SkAutoTUnref<SkPicture> parentPWB(recorder.endRecording());
+        sk_sp<SkPicture> parentPWB(recorder.finishRecordingAsPicture());
         REPORTER_ASSERT(reporter, parentPWB->willPlayBackBitmaps()); // 1
     }
     {
         SkCanvas* canvas = recorder.beginRecording(10, 10);
         canvas->drawBitmap(bm, 0, 0);
         canvas->drawPicture(childPlain);
-        SkAutoTUnref<SkPicture> parentWBP(recorder.endRecording());
+        sk_sp<SkPicture> parentWBP(recorder.finishRecordingAsPicture());
         REPORTER_ASSERT(reporter, parentWBP->willPlayBackBitmaps()); // 1
     }
     {
         SkCanvas* canvas = recorder.beginRecording(10, 10);
         canvas->drawBitmap(bm, 0, 0);
         canvas->drawPicture(childWithBitmap);
-        SkAutoTUnref<SkPicture> parentWBWB(recorder.endRecording());
+        sk_sp<SkPicture> parentWBWB(recorder.finishRecordingAsPicture());
         REPORTER_ASSERT(reporter, parentWBWB->willPlayBackBitmaps()); // 2
     }
 }
@@ -1750,14 +829,14 @@
 
     SkPictureRecorder recorder;
     recorder.beginRecording(0, 0);
-    SkAutoTUnref<SkPicture> empty(recorder.endRecording());
+    sk_sp<SkPicture> empty(recorder.finishRecordingAsPicture());
 
     // Empty pictures should still have a valid ID
     REPORTER_ASSERT(reporter, empty->uniqueID() != SK_InvalidGenID);
 
     SkCanvas* canvas = recorder.beginRecording(1, 1);
-    canvas->drawARGB(255, 255, 255, 255);
-    SkAutoTUnref<SkPicture> hasData(recorder.endRecording());
+    canvas->drawColor(SK_ColorWHITE);
+    sk_sp<SkPicture> hasData(recorder.finishRecordingAsPicture());
     // picture should have a non-zero id after recording
     REPORTER_ASSERT(reporter, hasData->uniqueID() != SK_InvalidGenID);
 
@@ -1765,7 +844,20 @@
     REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
 }
 
+static void test_typeface(skiatest::Reporter* reporter) {
+    SkPictureRecorder recorder;
+    SkCanvas* canvas = recorder.beginRecording(10, 10);
+    SkPaint paint;
+    paint.setTypeface(SkTypeface::MakeFromName("Arial",
+                                               SkFontStyle::FromOldStyle(SkTypeface::kItalic)));
+    canvas->drawString("Q", 0, 10, paint);
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
+    SkDynamicMemoryWStream stream;
+    picture->serialize(&stream);
+}
+
 DEF_TEST(Picture, reporter) {
+    test_typeface(reporter);
 #ifdef SK_DEBUG
     test_deleting_empty_picture();
     test_serializing_empty_picture();
@@ -1775,40 +867,32 @@
     test_unbalanced_save_restores(reporter);
     test_peephole();
 #if SK_SUPPORT_GPU
-    test_gpu_veto(reporter, false);
-    test_gpu_veto(reporter, true);
+    test_gpu_veto(reporter);
 #endif
-    test_has_text(reporter, false);
-    test_has_text(reporter, true);
-    test_analysis(reporter, false);
-    test_analysis(reporter, true);
-    test_gatherpixelrefs(reporter);
-    test_gatherpixelrefsandrects(reporter);
-    test_bitmap_with_encoded_data(reporter);
-    test_draw_empty(reporter);
+    test_images_are_found_by_willPlayBackBitmaps(reporter);
+    test_analysis(reporter);
     test_clip_bound_opt(reporter);
     test_clip_expansion(reporter);
     test_hierarchical(reporter);
     test_gen_id(reporter);
+    test_cull_rect_reset(reporter);
 }
 
-#if SK_SUPPORT_GPU
-DEF_GPUTEST(GPUPicture, reporter, factory) {
-    test_gpu_picture_optimization(reporter, factory);
-}
-#endif
-
 static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
     const SkPaint paint;
     const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };
     const SkIRect irect =  { 2, 2, 3, 3 };
+    int divs[] = { 2, 3 };
+    SkCanvas::Lattice lattice;
+    lattice.fXCount = lattice.fYCount = 2;
+    lattice.fXDivs = lattice.fYDivs = divs;
 
     // Don't care what these record, as long as they're legal.
     canvas->drawBitmap(bitmap, 0.0f, 0.0f, &paint);
-    canvas->drawBitmapRectToRect(bitmap, &rect, rect, &paint, SkCanvas::kNone_DrawBitmapRectFlag);
-    canvas->drawBitmapMatrix(bitmap, SkMatrix::I(), &paint);
+    canvas->drawBitmapRect(bitmap, rect, rect, &paint, SkCanvas::kStrict_SrcRectConstraint);
     canvas->drawBitmapNine(bitmap, irect, rect, &paint);
-    canvas->drawSprite(bitmap, 1, 1);
+    canvas->drawBitmap(bitmap, 1, 1);   // drawSprite
+    canvas->drawBitmapLattice(bitmap, lattice, rect, &paint);
 }
 
 static void test_draw_bitmaps(SkCanvas* canvas) {
@@ -1821,7 +905,7 @@
 DEF_TEST(Picture_EmptyBitmap, r) {
     SkPictureRecorder recorder;
     test_draw_bitmaps(recorder.beginRecording(10, 10));
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
 }
 
 DEF_TEST(Canvas_EmptyBitmap, r) {
@@ -1836,8 +920,8 @@
     // This test is from crbug.com/344987.
     // The commands are:
     //   saveLayer with paint that modifies alpha
-    //     drawBitmapRectToRect
-    //     drawBitmapRectToRect
+    //     drawBitmapRect
+    //     drawBitmapRect
     //   restore
     // The bug was that this structure was modified so that:
     //  - The saveLayer and restore were eliminated
@@ -1857,14 +941,14 @@
 
     SkPictureRecorder recorder;
     SkCanvas* canvas = recorder.beginRecording(100, 100);
-    canvas->drawARGB(0, 0, 0, 0);
+    canvas->drawColor(0);
 
     canvas->saveLayer(0, &semiTransparent);
     canvas->drawBitmap(blueBM, 25, 25);
     canvas->drawBitmap(redBM, 50, 50);
     canvas->restore();
 
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
 
     // Now replay the picture back on another canvas
     // and check a couple of its pixels.
@@ -1882,26 +966,23 @@
 
 struct CountingBBH : public SkBBoxHierarchy {
     mutable int searchCalls;
+    SkRect rootBound;
 
-    CountingBBH() : searchCalls(0) {}
+    CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
 
-    virtual void search(const SkRect& query, SkTDArray<void*>* results) const {
+    void search(const SkRect& query, SkTDArray<int>* results) const override {
         this->searchCalls++;
     }
 
-    // All other methods unimplemented.
-    virtual void insert(void* data, const SkRect& bounds, bool defer) {}
-    virtual void flushDeferredInserts() {}
-    virtual void clear() {}
-    virtual int getCount() const { return 0; }
-    virtual int getDepth() const { return 0; }
-    virtual void rewindInserts() {}
+    void insert(const SkRect[], int) override {}
+    virtual size_t bytesUsed() const override { return 0; }
+    SkRect getRootBound() const override { return rootBound; }
 };
 
 class SpoonFedBBHFactory : public SkBBHFactory {
 public:
     explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
-    virtual SkBBoxHierarchy* operator()(int width, int height) const {
+    SkBBoxHierarchy* operator()(const SkRect&) const override {
         return SkRef(fBBH);
     }
 private:
@@ -1910,12 +991,16 @@
 
 // When the canvas clip covers the full picture, we don't need to call the BBH.
 DEF_TEST(Picture_SkipBBH, r) {
-    CountingBBH bbh;
+    SkRect bound = SkRect::MakeWH(320, 240);
+    CountingBBH bbh(bound);
     SpoonFedBBHFactory factory(&bbh);
 
     SkPictureRecorder recorder;
-    recorder.beginRecording(320, 240, &factory);
-    SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
+    SkCanvas* c = recorder.beginRecording(bound, &factory);
+    // Record a few ops so we don't hit a small- or empty- picture optimization.
+        c->drawRect(bound, SkPaint());
+        c->drawRect(bound, SkPaint());
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
 
     SkCanvas big(640, 480), small(300, 200);
 
@@ -1938,18 +1023,152 @@
     REPORTER_ASSERT(r, mut.pixelRef()->unique());
     REPORTER_ASSERT(r, immut.pixelRef()->unique());
 
-    SkPictureRecorder rec;
-    SkCanvas* canvas = rec.beginRecording(1920, 1200);
-        canvas->drawBitmap(mut, 0, 0);
-        canvas->drawBitmap(immut, 800, 600);
-    SkAutoTDelete<const SkPicture> pic(rec.endRecording());
+    sk_sp<SkPicture> pic;
+    {
+        // we want the recorder to go out of scope before our subsequent checks, so we
+        // place it inside local braces.
+        SkPictureRecorder rec;
+        SkCanvas* canvas = rec.beginRecording(1920, 1200);
+            canvas->drawBitmap(mut, 0, 0);
+            canvas->drawBitmap(immut, 800, 600);
+        pic = rec.finishRecordingAsPicture();
+    }
 
     // The picture shares the immutable pixels but copies the mutable ones.
     REPORTER_ASSERT(r, mut.pixelRef()->unique());
     REPORTER_ASSERT(r, !immut.pixelRef()->unique());
 
     // When the picture goes away, it's just our bitmaps holding the refs.
-    pic.reset(NULL);
+    pic = nullptr;
     REPORTER_ASSERT(r, mut.pixelRef()->unique());
     REPORTER_ASSERT(r, immut.pixelRef()->unique());
 }
+
+// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
+DEF_TEST(Picture_getRecordingCanvas, r) {
+    SkPictureRecorder rec;
+    REPORTER_ASSERT(r, !rec.getRecordingCanvas());
+    for (int i = 0; i < 3; i++) {
+        rec.beginRecording(100, 100);
+        REPORTER_ASSERT(r, rec.getRecordingCanvas());
+        rec.finishRecordingAsPicture();
+        REPORTER_ASSERT(r, !rec.getRecordingCanvas());
+    }
+}
+
+DEF_TEST(MiniRecorderLeftHanging, r) {
+    // Any shader or other ref-counted effect will do just fine here.
+    SkPaint paint;
+    paint.setShader(SkShader::MakeColorShader(SK_ColorRED));
+
+    SkMiniRecorder rec;
+    REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint));
+    // Don't call rec.detachPicture().  Test succeeds by not asserting or leaking the shader.
+}
+
+DEF_TEST(Picture_preserveCullRect, r) {
+    SkPictureRecorder recorder;
+
+    SkCanvas* c = recorder.beginRecording(SkRect::MakeLTRB(1, 2, 3, 4));
+    c->clear(SK_ColorCYAN);
+
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
+    SkDynamicMemoryWStream wstream;
+    picture->serialize(&wstream);
+
+    std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
+    sk_sp<SkPicture> deserializedPicture(SkPicture::MakeFromStream(rstream.get()));
+
+    REPORTER_ASSERT(r, deserializedPicture != nullptr);
+    REPORTER_ASSERT(r, deserializedPicture->cullRect().left() == 1);
+    REPORTER_ASSERT(r, deserializedPicture->cullRect().top() == 2);
+    REPORTER_ASSERT(r, deserializedPicture->cullRect().right() == 3);
+    REPORTER_ASSERT(r, deserializedPicture->cullRect().bottom() == 4);
+}
+
+#if SK_SUPPORT_GPU
+
+DEF_TEST(PictureGpuAnalyzer, r) {
+    SkPictureRecorder recorder;
+
+    {
+        SkCanvas* canvas = recorder.beginRecording(10, 10);
+        SkPaint paint;
+        SkScalar intervals [] = { 10, 20 };
+        paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 25));
+
+        for (int i = 0; i < 50; ++i) {
+            canvas->drawRect(SkRect::MakeWH(10, 10), paint);
+        }
+    }
+    sk_sp<SkPicture> vetoPicture(recorder.finishRecordingAsPicture());
+
+    SkPictureGpuAnalyzer analyzer;
+    REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
+
+    analyzer.analyzePicture(vetoPicture.get());
+    REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
+
+    analyzer.reset();
+    REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
+
+    recorder.beginRecording(10, 10)->drawPicture(vetoPicture);
+    sk_sp<SkPicture> nestedVetoPicture(recorder.finishRecordingAsPicture());
+
+    analyzer.analyzePicture(nestedVetoPicture.get());
+    REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
+
+    analyzer.reset();
+
+    const SkPath convexClip = make_convex_path();
+    const SkPath concaveClip = make_concave_path();
+    for (int i = 0; i < 50; ++i) {
+        analyzer.analyzeClipPath(convexClip, kIntersect_SkClipOp, false);
+        analyzer.analyzeClipPath(convexClip, kIntersect_SkClipOp, true);
+        analyzer.analyzeClipPath(concaveClip, kIntersect_SkClipOp, false);
+    }
+    REPORTER_ASSERT(r, analyzer.suitableForGpuRasterization());
+
+    for (int i = 0; i < 50; ++i) {
+        analyzer.analyzeClipPath(concaveClip, kIntersect_SkClipOp, true);
+    }
+    REPORTER_ASSERT(r, !analyzer.suitableForGpuRasterization());
+}
+
+#endif // SK_SUPPORT_GPU
+
+// If we record bounded ops into a picture with a big cull and calculate the
+// bounds of those ops, we should trim down the picture cull to the ops' bounds.
+// If we're not using an SkBBH, we shouldn't change it.
+DEF_TEST(Picture_UpdatedCull_1, r) {
+    // Testing 1 draw exercises SkMiniPicture.
+    SkRTreeFactory factory;
+    SkPictureRecorder recorder;
+
+    auto canvas = recorder.beginRecording(SkRect::MakeLargest(), &factory);
+    canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
+    auto pic = recorder.finishRecordingAsPicture();
+    REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,20));
+
+    canvas = recorder.beginRecording(SkRect::MakeLargest());
+    canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
+    pic = recorder.finishRecordingAsPicture();
+    REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeLargest());
+}
+DEF_TEST(Picture_UpdatedCull_2, r) {
+    // Testing >1 draw exercises SkBigPicture.
+    SkRTreeFactory factory;
+    SkPictureRecorder recorder;
+
+    auto canvas = recorder.beginRecording(SkRect::MakeLargest(), &factory);
+    canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
+    canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
+    auto pic = recorder.finishRecordingAsPicture();
+    REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeWH(20,40));
+
+    canvas = recorder.beginRecording(SkRect::MakeLargest());
+    canvas->drawRect(SkRect::MakeWH(20,20), SkPaint{});
+    canvas->drawRect(SkRect::MakeWH(10,40), SkPaint{});
+    pic = recorder.finishRecordingAsPicture();
+    REPORTER_ASSERT(r, pic->cullRect() == SkRect::MakeLargest());
+}
diff --git a/src/third_party/skia/tests/PipeTest.cpp b/src/third_party/skia/tests/PipeTest.cpp
index 00a1a3f..e5d2f09 100644
--- a/src/third_party/skia/tests/PipeTest.cpp
+++ b/src/third_party/skia/tests/PipeTest.cpp
@@ -1,56 +1,187 @@
 /*
- * Copyright 2012 Google Inc.
+ * Copyright 2016 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
 
-#include "SamplePipeControllers.h"
-#include "SkBitmap.h"
+#include "Resources.h"
 #include "SkCanvas.h"
-#include "SkGPipe.h"
+#include "SkPipe.h"
 #include "SkPaint.h"
-#include "SkShader.h"
+#include "SkStream.h"
+#include "SkSurface.h"
 #include "Test.h"
 
-// Ensures that the pipe gracefully handles drawing an invalid bitmap.
-static void testDrawingBadBitmap(SkCanvas* pipeCanvas) {
-    SkBitmap badBitmap;
-    badBitmap.setInfo(SkImageInfo::MakeUnknown(5, 5));
-    pipeCanvas->drawBitmap(badBitmap, 0, 0);
+#include "SkNullCanvas.h"
+#include "SkAutoPixmapStorage.h"
+#include "SkPictureRecorder.h"
+
+static void drain(SkPipeDeserializer* deserial, SkDynamicMemoryWStream* stream) {
+    std::unique_ptr<SkCanvas> canvas = SkMakeNullCanvas();
+    sk_sp<SkData> data = stream->detachAsData();
+    deserial->playback(data->data(), data->size(), canvas.get());
 }
 
-// Ensure that pipe gracefully handles attempting to draw after endRecording is called on the
-// SkGPipeWriter.
-static void testDrawingAfterEndRecording(SkCanvas* canvas) {
-    PipeController pc(canvas);
-    SkGPipeWriter writer;
-    SkCanvas* pipeCanvas = writer.startRecording(&pc, SkGPipeWriter::kCrossProcess_Flag);
-    writer.endRecording();
+static bool deep_equal(SkImage* a, SkImage* b) {
+    if (a->width() != b->width() || a->height() != b->height()) {
+        return false;
+    }
 
-    SkBitmap bm;
-    bm.allocN32Pixels(2, 2);
-    bm.eraseColor(SK_ColorTRANSPARENT);
+    const SkImageInfo info = SkImageInfo::MakeN32Premul(a->width(), a->height());
+    SkAutoPixmapStorage pmapA, pmapB;
+    pmapA.alloc(info);
+    pmapB.alloc(info);
 
-    SkShader* shader = SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode,
-                                                    SkShader::kClamp_TileMode);
-    SkPaint paint;
-    paint.setShader(shader)->unref();
-    pipeCanvas->drawPaint(paint);
+    if (!a->readPixels(pmapA, 0, 0) || !b->readPixels(pmapB, 0, 0)) {
+        return false;
+    }
 
-    pipeCanvas->drawBitmap(bm, 0, 0);
+    for (int y = 0; y < info.height(); ++y) {
+        if (memcmp(pmapA.addr32(0, y), pmapB.addr32(0, y), info.width() * sizeof(SkPMColor))) {
+            return false;
+        }
+    }
+    return true;
 }
 
-DEF_TEST(Pipe, reporter) {
-    SkBitmap bitmap;
-    bitmap.setInfo(SkImageInfo::MakeN32Premul(64, 64));
-    SkCanvas canvas(bitmap);
+DEF_TEST(Pipe_image_draw_first, reporter) {
+    sk_sp<SkImage> img = GetResourceAsImage("mandrill_128.png");
+    SkASSERT(img.get());
 
-    PipeController pipeController(&canvas);
-    SkGPipeWriter writer;
-    SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
-    testDrawingBadBitmap(pipeCanvas);
-    writer.endRecording();
+    SkPipeSerializer serializer;
+    SkPipeDeserializer deserializer;
 
-    testDrawingAfterEndRecording(&canvas);
+    SkDynamicMemoryWStream stream;
+    SkCanvas* wc = serializer.beginWrite(SkRect::MakeWH(100, 100), &stream);
+    wc->drawImage(img, 0, 0, nullptr);
+    serializer.endWrite();
+    size_t offset0 = stream.bytesWritten();
+    REPORTER_ASSERT(reporter, offset0 > 100);   // the raw image must be sorta big
+    drain(&deserializer, &stream);
+
+    // try drawing the same image again -- it should be much smaller
+    wc = serializer.beginWrite(SkRect::MakeWH(100, 100), &stream);
+    wc->drawImage(img, 0, 0, nullptr);
+    size_t offset1 = stream.bytesWritten();
+    serializer.endWrite();
+    REPORTER_ASSERT(reporter, offset1 <= 32);
+    drain(&deserializer, &stream);
+
+    // try serializing the same image directly, again it should be small
+    sk_sp<SkData> data = serializer.writeImage(img.get());
+    size_t offset2 = data->size();
+    REPORTER_ASSERT(reporter, offset2 <= 32);
+    auto img1 = deserializer.readImage(data.get());
+    REPORTER_ASSERT(reporter, deep_equal(img.get(), img1.get()));
+
+    // try serializing the same image directly (again), check that it is the same!
+    data = serializer.writeImage(img.get());
+    size_t offset3 = data->size();
+    REPORTER_ASSERT(reporter, offset3 <= 32);
+    auto img2 = deserializer.readImage(data.get());
+    REPORTER_ASSERT(reporter, img1.get() == img2.get());
+}
+
+DEF_TEST(Pipe_image_draw_second, reporter) {
+    sk_sp<SkImage> img = GetResourceAsImage("mandrill_128.png");
+    SkASSERT(img.get());
+
+    SkPipeSerializer serializer;
+    SkPipeDeserializer deserializer;
+    SkDynamicMemoryWStream stream;
+
+    sk_sp<SkData> data = serializer.writeImage(img.get());
+    size_t offset0 = data->size();
+    REPORTER_ASSERT(reporter, offset0 > 100);   // the raw image must be sorta big
+    auto img1 = deserializer.readImage(data.get());
+
+    // The 2nd image should be nice and small
+    data = serializer.writeImage(img.get());
+    size_t offset1 = data->size();
+    REPORTER_ASSERT(reporter, offset1 <= 16);
+    auto img2 = deserializer.readImage(data.get());
+    REPORTER_ASSERT(reporter, img1.get() == img2.get());
+
+    // Now try drawing the image, it should also be small
+    SkCanvas* wc = serializer.beginWrite(SkRect::MakeWH(100, 100), &stream);
+    wc->drawImage(img, 0, 0, nullptr);
+    serializer.endWrite();
+    size_t offset2 = stream.bytesWritten();
+    REPORTER_ASSERT(reporter, offset2 <= 16);
+}
+
+DEF_TEST(Pipe_picture_draw_first, reporter) {
+    sk_sp<SkPicture> picture = []() {
+        SkPictureRecorder rec;
+        SkCanvas* c = rec.beginRecording(SkRect::MakeWH(100, 100));
+        for (int i = 0; i < 100; ++i) {
+            c->drawColor(i);
+        }
+        return rec.finishRecordingAsPicture();
+    }();
+    SkPipeSerializer serializer;
+    SkPipeDeserializer deserializer;
+
+    SkDynamicMemoryWStream stream;
+    SkCanvas* wc = serializer.beginWrite(SkRect::MakeWH(100, 100), &stream);
+    wc->drawPicture(picture);
+    serializer.endWrite();
+    size_t offset0 = stream.bytesWritten();
+    REPORTER_ASSERT(reporter, offset0 > 100);   // the raw picture must be sorta big
+    drain(&deserializer, &stream);
+
+    // try drawing the same picture again -- it should be much smaller
+    wc = serializer.beginWrite(SkRect::MakeWH(100, 100), &stream);
+    wc->drawPicture(picture);
+    size_t offset1 = stream.bytesWritten();
+    serializer.endWrite();
+    REPORTER_ASSERT(reporter, offset1 <= 16);
+    drain(&deserializer, &stream);
+
+    // try writing the picture directly, it should also be small
+    sk_sp<SkData> data = serializer.writePicture(picture.get());
+    size_t offset2 = data->size();
+    REPORTER_ASSERT(reporter, offset2 <= 16);
+    auto pic1 = deserializer.readPicture(data.get());
+
+    // try writing the picture directly, it should also be small
+    data = serializer.writePicture(picture.get());
+    size_t offset3 = data->size();
+    REPORTER_ASSERT(reporter, offset3 == offset2);
+    auto pic2 = deserializer.readPicture(data.get());
+    REPORTER_ASSERT(reporter, pic1.get() == pic2.get());
+}
+
+DEF_TEST(Pipe_picture_draw_second, reporter) {
+    sk_sp<SkPicture> picture = []() {
+        SkPictureRecorder rec;
+        SkCanvas* c = rec.beginRecording(SkRect::MakeWH(100, 100));
+        for (int i = 0; i < 100; ++i) {
+            c->drawColor(i);
+        }
+        return rec.finishRecordingAsPicture();
+    }();
+    SkPipeSerializer serializer;
+    SkPipeDeserializer deserializer;
+    SkDynamicMemoryWStream stream;
+
+    sk_sp<SkData> data = serializer.writePicture(picture.get());
+    size_t offset0 = data->size();
+    REPORTER_ASSERT(reporter, offset0 > 100);   // the raw picture must be sorta big
+    auto pic1 = deserializer.readPicture(data.get());
+
+    // The 2nd picture should be nice and small
+    data = serializer.writePicture(picture.get());
+    size_t offset1 = data->size();
+    REPORTER_ASSERT(reporter, offset1 <= 16);
+    auto pic2 = deserializer.readPicture(data.get());
+    SkASSERT(pic1.get() == pic2.get());
+
+    // Now try drawing the image, it should also be small
+    SkCanvas* wc = serializer.beginWrite(SkRect::MakeWH(100, 100), &stream);
+    wc->drawPicture(picture);
+    serializer.endWrite();
+    size_t offset2 = stream.bytesWritten();
+    REPORTER_ASSERT(reporter, offset2 <= 16);
 }
diff --git a/src/third_party/skia/tests/PixelRefTest.cpp b/src/third_party/skia/tests/PixelRefTest.cpp
index d1d3026..c978d81 100644
--- a/src/third_party/skia/tests/PixelRefTest.cpp
+++ b/src/third_party/skia/tests/PixelRefTest.cpp
@@ -1,12 +1,67 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #include "Test.h"
 
 #include "SkMallocPixelRef.h"
 #include "SkPixelRef.h"
 
+static void decrement_counter_proc(void* pixels, void* ctx) {
+    int* counter = (int*)ctx;
+    *counter -= 1;
+}
+
+static void test_dont_leak_install(skiatest::Reporter* reporter) {
+    bool success;
+    int release_counter;
+    SkImageInfo info;
+    SkBitmap bm;
+
+    info = SkImageInfo::MakeN32Premul(0, 0);
+    release_counter = 1;
+    success = bm.installPixels(info, nullptr, 0, decrement_counter_proc, &release_counter);
+    REPORTER_ASSERT(reporter, true == success);
+    bm.reset();
+    REPORTER_ASSERT(reporter, 0 == release_counter);
+
+    info = SkImageInfo::MakeN32Premul(10, 10);
+    release_counter = 1;
+    success = bm.installPixels(info, nullptr, 0, decrement_counter_proc, &release_counter);
+    REPORTER_ASSERT(reporter, true == success);
+    bm.reset();
+    REPORTER_ASSERT(reporter, 0 == release_counter);
+
+    info = SkImageInfo::MakeN32Premul(-10, -10);
+    release_counter = 1;
+    success = bm.installPixels(info, nullptr, 0, decrement_counter_proc, &release_counter);
+    REPORTER_ASSERT(reporter, false == success);
+    bm.reset();
+    REPORTER_ASSERT(reporter, 0 == release_counter);
+}
+
+static void test_install(skiatest::Reporter* reporter) {
+    bool success;
+    SkImageInfo info = SkImageInfo::MakeN32Premul(0, 0);
+    SkBitmap bm;
+    // make sure we don't assert on an empty install
+    success = bm.installPixels(info, nullptr, 0);
+    REPORTER_ASSERT(reporter, success);
+
+    // no pixels should be the same as setInfo()
+    info = SkImageInfo::MakeN32Premul(10, 10);
+    success = bm.installPixels(info, nullptr, 0);
+    REPORTER_ASSERT(reporter, success);
+
+}
+
 class TestListener : public SkPixelRef::GenIDChangeListener {
 public:
     explicit TestListener(int* ptr) : fPtr(ptr) {}
-    virtual void onChange() SK_OVERRIDE { (*fPtr)++; }
+    void onChange() override { (*fPtr)++; }
 private:
     int* fPtr;
 };
@@ -14,11 +69,11 @@
 DEF_TEST(PixelRef_GenIDChange, r) {
     SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
 
-    SkAutoTUnref<SkPixelRef> pixelRef(SkMallocPixelRef::NewAllocate(info, 0, NULL));
+    sk_sp<SkPixelRef> pixelRef = SkMallocPixelRef::MakeAllocate(info, 0);
 
     // Register a listener.
     int count = 0;
-    pixelRef->addGenIDChangeListener(SkNEW_ARGS(TestListener, (&count)));
+    pixelRef->addGenIDChangeListener(new TestListener(&count));
     REPORTER_ASSERT(r, 0 == count);
 
     // No one has looked at our pixelRef's generation ID, so invalidating it doesn't make sense.
@@ -35,12 +90,15 @@
 
     // Force the generation ID to be recalculated, then add a listener.
     REPORTER_ASSERT(r, 0 != pixelRef->getGenerationID());
-    pixelRef->addGenIDChangeListener(SkNEW_ARGS(TestListener, (&count)));
+    pixelRef->addGenIDChangeListener(new TestListener(&count));
     pixelRef->notifyPixelsChanged();
     REPORTER_ASSERT(r, 1 == count);
 
-    // Quick check that NULL is safe.
+    // Quick check that nullptr is safe.
     REPORTER_ASSERT(r, 0 != pixelRef->getGenerationID());
-    pixelRef->addGenIDChangeListener(NULL);
+    pixelRef->addGenIDChangeListener(nullptr);
     pixelRef->notifyPixelsChanged();
+
+    test_install(r);
+    test_dont_leak_install(r);
 }
diff --git a/src/third_party/skia/tests/Point3Test.cpp b/src/third_party/skia/tests/Point3Test.cpp
new file mode 100644
index 0000000..2dedd6b
--- /dev/null
+++ b/src/third_party/skia/tests/Point3Test.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Unit tests for src/core/SkPoint3.cpp and its header
+
+#include "SkPoint3.h"
+#include "Test.h"
+
+static void test_eq_ops(skiatest::Reporter* reporter) {
+    const SkPoint3 p0 = SkPoint3::Make(0, 0, 0);
+    const SkPoint3 p1 = SkPoint3::Make(1, 1, 1);
+    const SkPoint3 p2 = SkPoint3::Make(1, 1, 1);
+
+    REPORTER_ASSERT(reporter, p0 != p1);
+    REPORTER_ASSERT(reporter, p1 == p2);
+}
+
+static void test_ops(skiatest::Reporter* reporter) {
+    SkPoint3 v = SkPoint3::Make(1, 1, 1);
+    v.normalize();
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(v.length(), SK_Scalar1));
+
+    // scale
+    SkPoint3 p = v.makeScale(3.0f);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.length(), 3.0f));
+
+    p.scale(1.0f/3.0f);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.length(), SK_Scalar1));
+
+    SkPoint3 p1 = SkPoint3::Make(20.0f, 2.0f, 10.0f);
+    SkPoint3 p2 = -p1;
+
+    // -
+    p = p1 - p1;
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.x(), 0.0f));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.y(), 0.0f));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.z(), 0.0f));
+
+    // +
+    p = p1 + p2;
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.x(), 0.0f));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.y(), 0.0f));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.z(), 0.0f));
+}
+
+static void test_dot(skiatest::Reporter* reporter) {
+    const SkPoint3 xAxis = SkPoint3::Make(1.0f, 0.0f, 0.0f);
+    const SkPoint3 yAxis = SkPoint3::Make(0.0f, 1.0f, 0.0f);
+    const SkPoint3 zAxis = SkPoint3::Make(0.0f, 0.0f, 1.0f);
+
+    SkScalar dot = xAxis.dot(yAxis);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, 0.0f));
+
+    dot = yAxis.dot(zAxis);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, 0.0f));
+
+    dot = zAxis.dot(xAxis);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, 0.0f));
+
+    SkPoint3 v = SkPoint3::Make(13.0f, 2.0f, 7.0f);
+    v.normalize();
+
+    dot = v.dot(v);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, 1.0f));
+
+    v = SkPoint3::Make(SK_ScalarRoot2Over2, SK_ScalarRoot2Over2, 0.0f);
+
+    dot = xAxis.dot(v);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, SK_ScalarRoot2Over2));
+
+    dot = yAxis.dot(v);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dot, SK_ScalarRoot2Over2));
+}
+
+static void test_length(skiatest::Reporter* reporter,
+                        SkScalar x, SkScalar y, SkScalar z, SkScalar expectedLen) {
+    SkPoint3 point = SkPoint3::Make(x, y, z);
+
+    SkScalar s1 = point.length();
+    SkScalar s2 = SkPoint3::Length(x, y, z);
+
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, s2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, expectedLen));
+}
+
+static void test_normalize(skiatest::Reporter* reporter,
+                           SkScalar x, SkScalar y, SkScalar z, SkScalar expectedLen) {
+    SkPoint3 point = SkPoint3::Make(x, y, z);
+
+    bool result = point.normalize();
+    SkScalar newLength = point.length();
+
+    if (0 == expectedLen) {
+        const SkPoint3 empty = SkPoint3::Make(0.0f, 0.0f, 0.0f);
+
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(newLength, 0));
+        REPORTER_ASSERT(reporter, !result);
+        REPORTER_ASSERT(reporter, point == empty);
+    } else {
+        REPORTER_ASSERT(reporter, SkScalarNearlyEqual(newLength, SK_Scalar1));
+        REPORTER_ASSERT(reporter, result);
+    }
+}
+
+DEF_TEST(Point3, reporter) {
+    test_eq_ops(reporter);
+    test_ops(reporter);
+    test_dot(reporter);
+
+    static const struct {
+        SkScalar fX;
+        SkScalar fY;
+        SkScalar fZ;
+        SkScalar fLength;
+    } gRec[] = {
+        { 0.0f, 0.0f, 0.0f, 0.0f },
+        { 0.3f, 0.4f, 0.5f, SK_ScalarRoot2Over2 },
+        { 1.0e-37f, 1.0e-37f, 1.0e-37f, 0.0f },  // underflows
+        { 3.4e38f, 0.0f, 0.0f, 3.4e38f }         // overflows
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
+        test_length(reporter, gRec[i].fX, gRec[i].fY, gRec[i].fZ, gRec[i].fLength);
+        test_normalize(reporter, gRec[i].fX, gRec[i].fY, gRec[i].fZ, gRec[i].fLength);
+    }
+}
diff --git a/src/third_party/skia/tests/PointTest.cpp b/src/third_party/skia/tests/PointTest.cpp
index fed443a..c0d1f26 100644
--- a/src/third_party/skia/tests/PointTest.cpp
+++ b/src/third_party/skia/tests/PointTest.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -74,7 +73,7 @@
     // even the pair of memcpy calls are not sufficient, since those seem to
     // be no-op'd, so we add a runtime tests (just like get_value) to force
     // the compiler to give us an actual float.
-    if (NULL == reporter) {
+    if (nullptr == reporter) {
         storage = ~storage;
     }
     memcpy(&value, &storage, 4);
@@ -108,13 +107,13 @@
 // report failure if we try to normalize them.
 static void test_underflow(skiatest::Reporter* reporter) {
     SkPoint pt = { 1.0e-37f, 1.0e-37f };
-    SkPoint copy = pt;
+    const SkPoint empty = { 0, 0 };
 
     REPORTER_ASSERT(reporter, 0 == SkPoint::Normalize(&pt));
-    REPORTER_ASSERT(reporter, pt == copy);  // pt is unchanged
+    REPORTER_ASSERT(reporter, pt == empty);
 
     REPORTER_ASSERT(reporter, !pt.setLength(SK_Scalar1));
-    REPORTER_ASSERT(reporter, pt == copy);  // pt is unchanged
+    REPORTER_ASSERT(reporter, pt == empty);
 }
 
 DEF_TEST(Point, reporter) {
diff --git a/src/third_party/skia/tests/PremulAlphaRoundTripTest.cpp b/src/third_party/skia/tests/PremulAlphaRoundTripTest.cpp
index ce45f16..7719ad8 100644
--- a/src/third_party/skia/tests/PremulAlphaRoundTripTest.cpp
+++ b/src/third_party/skia/tests/PremulAlphaRoundTripTest.cpp
@@ -5,15 +5,13 @@
  * found in the LICENSE file.
  */
 
-#include "SkBitmapDevice.h"
 #include "SkCanvas.h"
-#include "SkConfig8888.h"
+#include "SkSurface.h"
 #include "Test.h"
 #include "sk_tool_utils.h"
 
 #if SK_SUPPORT_GPU
-#include "GrContextFactory.h"
-#include "SkGpuDevice.h"
+#include "GrContext.h"
 #endif
 
 static uint32_t pack_unpremul_rgba(SkColor c) {
@@ -46,7 +44,7 @@
     { kBGRA_8888_SkColorType, pack_unpremul_bgra },
 };
 
-static void fillCanvas(SkCanvas* canvas, SkColorType colorType, PackUnpremulProc proc) {
+static void fill_canvas(SkCanvas* canvas, SkColorType colorType, PackUnpremulProc proc) {
     // Don't strictly need a bitmap, but its a handy way to allocate the pixels
     SkBitmap bmp;
     bmp.allocN32Pixels(256, 256);
@@ -63,72 +61,55 @@
     canvas->writePixels(info, bmp.getPixels(), bmp.rowBytes(), 0, 0);
 }
 
-DEF_GPUTEST(PremulAlphaRoundTrip, reporter, factory) {
-    const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
+static void test_premul_alpha_roundtrip(skiatest::Reporter* reporter, SkSurface* surf) {
+    SkCanvas* canvas = surf->getCanvas();
+    for (size_t upmaIdx = 0; upmaIdx < SK_ARRAY_COUNT(gUnpremul); ++upmaIdx) {
+        fill_canvas(canvas, gUnpremul[upmaIdx].fColorType, gUnpremul[upmaIdx].fPackProc);
 
-    for (int dtype = 0; dtype < 2; ++dtype) {
+        const SkImageInfo info = SkImageInfo::Make(256, 256, gUnpremul[upmaIdx].fColorType,
+                                                   kUnpremul_SkAlphaType);
+        SkBitmap readBmp1;
+        readBmp1.allocPixels(info);
+        SkBitmap readBmp2;
+        readBmp2.allocPixels(info);
 
-        int glCtxTypeCnt = 1;
-#if SK_SUPPORT_GPU
-        if (0 != dtype)  {
-            glCtxTypeCnt = GrContextFactory::kGLContextTypeCnt;
-        }
-#endif
-        for (int glCtxType = 0; glCtxType < glCtxTypeCnt; ++glCtxType) {
-            SkAutoTUnref<SkBaseDevice> device;
-            if (0 == dtype) {
-                device.reset(SkBitmapDevice::Create(info));
-            } else {
-#if SK_SUPPORT_GPU
-                GrContextFactory::GLContextType type =
-                    static_cast<GrContextFactory::GLContextType>(glCtxType);
-                if (!GrContextFactory::IsRenderingGLContext(type)) {
-                    continue;
+        readBmp1.eraseColor(0);
+        readBmp2.eraseColor(0);
+
+        canvas->readPixels(readBmp1, 0, 0);
+        sk_tool_utils::write_pixels(canvas, readBmp1, 0, 0, gUnpremul[upmaIdx].fColorType,
+                                    kUnpremul_SkAlphaType);
+        canvas->readPixels(readBmp2, 0, 0);
+
+        bool success = true;
+        for (int y = 0; y < 256 && success; ++y) {
+            const uint32_t* pixels1 = readBmp1.getAddr32(0, y);
+            const uint32_t* pixels2 = readBmp2.getAddr32(0, y);
+            for (int x = 0; x < 256 && success; ++x) {
+                // We see sporadic failures here. May help to see where it goes wrong.
+                if (pixels1[x] != pixels2[x]) {
+                    SkDebugf("%x != %x, x = %d, y = %d\n", pixels1[x], pixels2[x], x, y);
                 }
-                GrContext* context = factory->get(type);
-                if (NULL == context) {
-                    continue;
-                }
-
-                device.reset(SkGpuDevice::Create(context, info,
-                                     SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType), 0));
-#else
-                continue;
-#endif
-            }
-            SkCanvas canvas(device);
-
-            for (size_t upmaIdx = 0; upmaIdx < SK_ARRAY_COUNT(gUnpremul); ++upmaIdx) {
-                fillCanvas(&canvas, gUnpremul[upmaIdx].fColorType, gUnpremul[upmaIdx].fPackProc);
-
-                const SkImageInfo info = SkImageInfo::Make(256, 256, gUnpremul[upmaIdx].fColorType,
-                                                           kUnpremul_SkAlphaType);
-                SkBitmap readBmp1;
-                readBmp1.allocPixels(info);
-                SkBitmap readBmp2;
-                readBmp2.allocPixels(info);
-
-                readBmp1.eraseColor(0);
-                readBmp2.eraseColor(0);
-
-                canvas.readPixels(&readBmp1, 0, 0);
-                sk_tool_utils::write_pixels(&canvas, readBmp1, 0, 0, gUnpremul[upmaIdx].fColorType,
-                                            kUnpremul_SkAlphaType);
-                canvas.readPixels(&readBmp2, 0, 0);
-
-                bool success = true;
-                for (int y = 0; y < 256 && success; ++y) {
-                    const uint32_t* pixels1 = readBmp1.getAddr32(0, y);
-                    const uint32_t* pixels2 = readBmp2.getAddr32(0, y);
-                    for (int x = 0; x < 256 && success; ++x) {
-                        // We see sporadic failures here. May help to see where it goes wrong.
-                        if (pixels1[x] != pixels2[x]) {
-                            SkDebugf("%x != %x, x = %d, y = %d\n", pixels1[x], pixels2[x], x, y);
-                        }
-                        REPORTER_ASSERT(reporter, success = pixels1[x] == pixels2[x]);
-                    }
-                }
+                REPORTER_ASSERT(reporter, success = pixels1[x] == pixels2[x]);
             }
         }
     }
 }
+
+DEF_TEST(PremulAlphaRoundTrip, reporter) {
+    const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
+
+    sk_sp<SkSurface> surf(SkSurface::MakeRaster(info));
+
+    test_premul_alpha_roundtrip(reporter, surf.get());
+}
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PremulAlphaRoundTrip_Gpu, reporter, ctxInfo) {
+    const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
+
+    sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(ctxInfo.grContext(),
+                                                      SkBudgeted::kNo,
+                                                      info));
+    test_premul_alpha_roundtrip(reporter, surf.get());
+}
+#endif
diff --git a/src/third_party/skia/tests/PrimitiveProcessorTest.cpp b/src/third_party/skia/tests/PrimitiveProcessorTest.cpp
new file mode 100644
index 0000000..75bdb83
--- /dev/null
+++ b/src/third_party/skia/tests/PrimitiveProcessorTest.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This is a GPU-backend specific test. It relies on static intializers to work
+
+#include "SkTypes.h"
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrGeometryProcessor.h"
+#include "GrGpu.h"
+#include "GrOpFlushState.h"
+#include "GrRenderTargetContext.h"
+#include "GrRenderTargetContextPriv.h"
+#include "SkString.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLGeometryProcessor.h"
+#include "glsl/GrGLSLVarying.h"
+#include "ops/GrMeshDrawOp.h"
+
+namespace {
+class Op : public GrMeshDrawOp {
+public:
+    DEFINE_OP_CLASS_ID
+
+    const char* name() const override { return "Dummy Op"; }
+
+    static std::unique_ptr<GrDrawOp> Make(int numAttribs) {
+        return std::unique_ptr<GrDrawOp>(new Op(numAttribs));
+    }
+
+    FixedFunctionFlags fixedFunctionFlags() const override {
+        return FixedFunctionFlags::kNone;
+    }
+
+    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
+        return RequiresDstTexture::kNo;
+    }
+
+private:
+    Op(int numAttribs) : INHERITED(ClassID()), fNumAttribs(numAttribs) {
+        this->setBounds(SkRect::MakeWH(1.f, 1.f), HasAABloat::kNo, IsZeroArea::kNo);
+    }
+
+    bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; }
+
+    void onPrepareDraws(Target* target) const override {
+        class GP : public GrGeometryProcessor {
+        public:
+            GP(int numAttribs) {
+                this->initClassID<GP>();
+                SkASSERT(numAttribs > 1);
+                for (auto i = 0; i < numAttribs; ++i) {
+                    fAttribNames.push_back().printf("attr%d", i);
+                }
+                for (auto i = 0; i < numAttribs; ++i) {
+                    this->addVertexAttrib(fAttribNames[i].c_str(), kVec2f_GrVertexAttribType);
+                }
+            }
+            const char* name() const override { return "Dummy GP"; }
+
+            GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
+                class GLSLGP : public GrGLSLGeometryProcessor {
+                public:
+                    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
+                        const GP& gp = args.fGP.cast<GP>();
+                        args.fVaryingHandler->emitAttributes(gp);
+                        this->setupPosition(args.fVertBuilder, gpArgs, gp.getAttrib(0).fName);
+                        GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
+                        fragBuilder->codeAppendf("%s = vec4(1);", args.fOutputColor);
+                        fragBuilder->codeAppendf("%s = vec4(1);", args.fOutputCoverage);
+                    }
+                    void setData(const GrGLSLProgramDataManager& pdman,
+                                 const GrPrimitiveProcessor& primProc,
+                                 FPCoordTransformIter&&) override {}
+                };
+                return new GLSLGP();
+            }
+            void getGLSLProcessorKey(const GrShaderCaps&,
+                                     GrProcessorKeyBuilder* builder) const override {
+                builder->add32(this->numAttribs());
+            }
+
+        private:
+            SkTArray<SkString> fAttribNames;
+        };
+        sk_sp<GrGeometryProcessor> gp(new GP(fNumAttribs));
+        QuadHelper helper;
+        size_t vertexStride = gp->getVertexStride();
+        SkPoint* vertices = reinterpret_cast<SkPoint*>(helper.init(target, vertexStride, 1));
+        vertices->setRectFan(0.f, 0.f, 1.f, 1.f, vertexStride);
+        helper.recordDraw(target, gp.get(), target->makePipeline(0, &GrProcessorSet::EmptySet()));
+    }
+
+    int fNumAttribs;
+
+    typedef GrMeshDrawOp INHERITED;
+};
+}
+
+DEF_GPUTEST_FOR_ALL_CONTEXTS(VertexAttributeCount, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+
+    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext(
+                                                                     SkBackingFit::kApprox,
+                                                                     1, 1, kRGBA_8888_GrPixelConfig,
+                                                                     nullptr));
+    if (!renderTargetContext) {
+        ERRORF(reporter, "Could not create render target context.");
+        return;
+    }
+    int attribCnt = context->caps()->maxVertexAttributes();
+    if (!attribCnt) {
+        ERRORF(reporter, "No attributes allowed?!");
+        return;
+    }
+    context->flush();
+    context->resetGpuStats();
+#if GR_GPU_STATS
+    REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0);
+    REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0);
+#endif
+    GrPaint grPaint;
+    // This one should succeed.
+    renderTargetContext->priv().testingOnly_addDrawOp(Op::Make(attribCnt));
+    context->flush();
+#if GR_GPU_STATS
+    REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 1);
+    REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0);
+#endif
+    context->resetGpuStats();
+    renderTargetContext->priv().testingOnly_addDrawOp(Op::Make(attribCnt + 1));
+    context->flush();
+#if GR_GPU_STATS
+    REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0);
+    REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 1);
+#endif
+}
+#endif
diff --git a/src/third_party/skia/tests/ProcessorTest.cpp b/src/third_party/skia/tests/ProcessorTest.cpp
new file mode 100644
index 0000000..fd025a0
--- /dev/null
+++ b/src/third_party/skia/tests/ProcessorTest.cpp
@@ -0,0 +1,459 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include <random>
+#include "GrClip.h"
+#include "GrContext.h"
+#include "GrGpuResource.h"
+#include "GrRenderTargetContext.h"
+#include "GrRenderTargetContextPriv.h"
+#include "GrResourceProvider.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "ops/GrMeshDrawOp.h"
+#include "ops/GrRectOpFactory.h"
+
+namespace {
+class TestOp : public GrMeshDrawOp {
+public:
+    DEFINE_OP_CLASS_ID
+    const char* name() const override { return "TestOp"; }
+
+    static std::unique_ptr<GrDrawOp> Make(sk_sp<GrFragmentProcessor> fp) {
+        return std::unique_ptr<GrDrawOp>(new TestOp(std::move(fp)));
+    }
+
+    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
+
+    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
+        static constexpr GrProcessorAnalysisColor kUnknownColor;
+        GrColor overrideColor;
+        fProcessors.finalize(kUnknownColor, GrProcessorAnalysisCoverage::kNone, clip, false, caps,
+                             &overrideColor);
+        return RequiresDstTexture::kNo;
+    }
+
+private:
+    TestOp(sk_sp<GrFragmentProcessor> fp) : INHERITED(ClassID()), fProcessors(std::move(fp)) {
+        this->setBounds(SkRect::MakeWH(100, 100), HasAABloat::kNo, IsZeroArea::kNo);
+    }
+
+    void onPrepareDraws(Target* target) const override { return; }
+
+    bool onCombineIfPossible(GrOp* op, const GrCaps& caps) override { return false; }
+
+    GrProcessorSet fProcessors;
+
+    typedef GrMeshDrawOp INHERITED;
+};
+
+/**
+ * FP used to test ref/IO counts on owned GrGpuResources. Can also be a parent FP to test counts
+ * of resources owned by child FPs.
+ */
+class TestFP : public GrFragmentProcessor {
+public:
+    struct Image {
+        Image(sk_sp<GrTextureProxy> proxy, GrIOType ioType) : fProxy(proxy), fIOType(ioType) {}
+        sk_sp<GrTextureProxy> fProxy;
+        GrIOType fIOType;
+    };
+    static sk_sp<GrFragmentProcessor> Make(sk_sp<GrFragmentProcessor> child) {
+        return sk_sp<GrFragmentProcessor>(new TestFP(std::move(child)));
+    }
+    static sk_sp<GrFragmentProcessor> Make(const SkTArray<sk_sp<GrTextureProxy>>& proxies,
+                                           const SkTArray<sk_sp<GrBuffer>>& buffers,
+                                           const SkTArray<Image>& images) {
+        return sk_sp<GrFragmentProcessor>(new TestFP(proxies, buffers, images));
+    }
+
+    const char* name() const override { return "test"; }
+
+    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
+        // We don't really care about reusing these.
+        static int32_t gKey = 0;
+        b->add32(sk_atomic_inc(&gKey));
+    }
+
+private:
+    TestFP(const SkTArray<sk_sp<GrTextureProxy>>& proxies,
+           const SkTArray<sk_sp<GrBuffer>>& buffers,
+           const SkTArray<Image>& images)
+            : INHERITED(kNone_OptimizationFlags), fSamplers(4), fBuffers(4), fImages(4) {
+        for (const auto& proxy : proxies) {
+            this->addTextureSampler(&fSamplers.emplace_back(proxy));
+        }
+        for (const auto& buffer : buffers) {
+            this->addBufferAccess(&fBuffers.emplace_back(kRGBA_8888_GrPixelConfig, buffer.get()));
+        }
+        for (const Image& image : images) {
+            fImages.emplace_back(image.fProxy, image.fIOType,
+                                 GrSLMemoryModel::kNone, GrSLRestrict::kNo);
+            this->addImageStorageAccess(&fImages.back());
+        }
+    }
+
+    TestFP(sk_sp<GrFragmentProcessor> child)
+            : INHERITED(kNone_OptimizationFlags), fSamplers(4), fBuffers(4), fImages(4) {
+        this->registerChildProcessor(std::move(child));
+    }
+
+    virtual GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
+        class TestGLSLFP : public GrGLSLFragmentProcessor {
+        public:
+            TestGLSLFP() {}
+            void emitCode(EmitArgs& args) override {
+                GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+                fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, args.fInputColor);
+            }
+
+        private:
+        };
+        return new TestGLSLFP();
+    }
+
+    bool onIsEqual(const GrFragmentProcessor&) const override { return false; }
+
+    GrTAllocator<TextureSampler> fSamplers;
+    GrTAllocator<BufferAccess> fBuffers;
+    GrTAllocator<ImageStorageAccess> fImages;
+    typedef GrFragmentProcessor INHERITED;
+};
+}
+
+template <typename T>
+inline void testingOnly_getIORefCnts(const T* resource, int* refCnt, int* readCnt, int* writeCnt) {
+    *refCnt = resource->fRefCnt;
+    *readCnt = resource->fPendingReads;
+    *writeCnt = resource->fPendingWrites;
+}
+
+void testingOnly_getIORefCnts(GrTextureProxy* proxy, int* refCnt, int* readCnt, int* writeCnt) {
+    *refCnt = proxy->getBackingRefCnt_TestOnly();
+    *readCnt = proxy->getPendingReadCnt_TestOnly();
+    *writeCnt = proxy->getPendingWriteCnt_TestOnly();
+}
+
+DEF_GPUTEST_FOR_ALL_CONTEXTS(ProcessorRefTest, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+
+    GrTextureDesc desc;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fWidth = 10;
+    desc.fHeight = 10;
+
+    for (int parentCnt = 0; parentCnt < 2; parentCnt++) {
+        sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext(
+                SkBackingFit::kApprox, 1, 1, kRGBA_8888_GrPixelConfig, nullptr));
+        {
+            bool texelBufferSupport = context->caps()->shaderCaps()->texelBufferSupport();
+            bool imageLoadStoreSupport = context->caps()->shaderCaps()->imageLoadStoreSupport();
+            sk_sp<GrTextureProxy> proxy1(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                      desc, SkBackingFit::kExact,
+                                                                      SkBudgeted::kYes));
+            sk_sp<GrTextureProxy> proxy2(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                      desc, SkBackingFit::kExact,
+                                                                      SkBudgeted::kYes));
+            sk_sp<GrTextureProxy> proxy3(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                      desc, SkBackingFit::kExact,
+                                                                      SkBudgeted::kYes));
+            sk_sp<GrTextureProxy> proxy4(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                      desc, SkBackingFit::kExact,
+                                                                      SkBudgeted::kYes));
+            sk_sp<GrBuffer> buffer(texelBufferSupport
+                                           ? context->resourceProvider()->createBuffer(
+                                                     1024, GrBufferType::kTexel_GrBufferType,
+                                                     GrAccessPattern::kStatic_GrAccessPattern, 0)
+                                           : nullptr);
+            {
+                SkTArray<sk_sp<GrTextureProxy>> proxies;
+                SkTArray<sk_sp<GrBuffer>> buffers;
+                SkTArray<TestFP::Image> images;
+                proxies.push_back(proxy1);
+                if (texelBufferSupport) {
+                    buffers.push_back(buffer);
+                }
+                if (imageLoadStoreSupport) {
+                    images.emplace_back(proxy2, GrIOType::kRead_GrIOType);
+                    images.emplace_back(proxy3, GrIOType::kWrite_GrIOType);
+                    images.emplace_back(proxy4, GrIOType::kRW_GrIOType);
+                }
+                auto fp = TestFP::Make(std::move(proxies), std::move(buffers), std::move(images));
+                for (int i = 0; i < parentCnt; ++i) {
+                    fp = TestFP::Make(std::move(fp));
+                }
+                std::unique_ptr<GrDrawOp> op(TestOp::Make(std::move(fp)));
+                renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
+            }
+            int refCnt, readCnt, writeCnt;
+
+            testingOnly_getIORefCnts(proxy1.get(), &refCnt, &readCnt, &writeCnt);
+            REPORTER_ASSERT(reporter, 1 == refCnt);
+            REPORTER_ASSERT(reporter, 1 == readCnt);
+            REPORTER_ASSERT(reporter, 0 == writeCnt);
+
+            if (texelBufferSupport) {
+                testingOnly_getIORefCnts(buffer.get(), &refCnt, &readCnt, &writeCnt);
+                REPORTER_ASSERT(reporter, 1 == refCnt);
+                REPORTER_ASSERT(reporter, 1 == readCnt);
+                REPORTER_ASSERT(reporter, 0 == writeCnt);
+            }
+
+            if (imageLoadStoreSupport) {
+                testingOnly_getIORefCnts(proxy2.get(), &refCnt, &readCnt, &writeCnt);
+                REPORTER_ASSERT(reporter, 1 == refCnt);
+                REPORTER_ASSERT(reporter, 1 == readCnt);
+                REPORTER_ASSERT(reporter, 0 == writeCnt);
+
+                testingOnly_getIORefCnts(proxy3.get(), &refCnt, &readCnt, &writeCnt);
+                REPORTER_ASSERT(reporter, 1 == refCnt);
+                REPORTER_ASSERT(reporter, 0 == readCnt);
+                REPORTER_ASSERT(reporter, 1 == writeCnt);
+
+                testingOnly_getIORefCnts(proxy4.get(), &refCnt, &readCnt, &writeCnt);
+                REPORTER_ASSERT(reporter, 1 == refCnt);
+                REPORTER_ASSERT(reporter, 1 == readCnt);
+                REPORTER_ASSERT(reporter, 1 == writeCnt);
+            }
+
+            context->flush();
+
+            testingOnly_getIORefCnts(proxy1.get(), &refCnt, &readCnt, &writeCnt);
+            REPORTER_ASSERT(reporter, 1 == refCnt);
+            REPORTER_ASSERT(reporter, 0 == readCnt);
+            REPORTER_ASSERT(reporter, 0 == writeCnt);
+
+            if (texelBufferSupport) {
+                testingOnly_getIORefCnts(buffer.get(), &refCnt, &readCnt, &writeCnt);
+                REPORTER_ASSERT(reporter, 1 == refCnt);
+                REPORTER_ASSERT(reporter, 0 == readCnt);
+                REPORTER_ASSERT(reporter, 0 == writeCnt);
+            }
+
+            if (texelBufferSupport) {
+                testingOnly_getIORefCnts(proxy2.get(), &refCnt, &readCnt, &writeCnt);
+                REPORTER_ASSERT(reporter, 1 == refCnt);
+                REPORTER_ASSERT(reporter, 0 == readCnt);
+                REPORTER_ASSERT(reporter, 0 == writeCnt);
+
+                testingOnly_getIORefCnts(proxy3.get(), &refCnt, &readCnt, &writeCnt);
+                REPORTER_ASSERT(reporter, 1 == refCnt);
+                REPORTER_ASSERT(reporter, 0 == readCnt);
+                REPORTER_ASSERT(reporter, 0 == writeCnt);
+
+                testingOnly_getIORefCnts(proxy4.get(), &refCnt, &readCnt, &writeCnt);
+                REPORTER_ASSERT(reporter, 1 == refCnt);
+                REPORTER_ASSERT(reporter, 0 == readCnt);
+                REPORTER_ASSERT(reporter, 0 == writeCnt);
+            }
+        }
+    }
+}
+
+// This test uses the random GrFragmentProcessor test factory, which relies on static initializers.
+#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+
+static GrColor texel_color(int i, int j) {
+    SkASSERT((unsigned)i < 256 && (unsigned)j < 256);
+    GrColor color = GrColorPackRGBA(j, (uint8_t)(i + j), (uint8_t)(2 * j - i), i);
+    return GrPremulColor(color);
+}
+
+static GrColor4f texel_color4f(int i, int j) { return GrColor4f::FromGrColor(texel_color(i, j)); }
+
+void test_draw_op(GrRenderTargetContext* rtc, sk_sp<GrFragmentProcessor> fp,
+                  sk_sp<GrTextureProxy> inputDataProxy) {
+    GrPaint paint;
+    paint.addColorTextureProcessor(std::move(inputDataProxy), nullptr, SkMatrix::I());
+    paint.addColorFragmentProcessor(std::move(fp));
+    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+    auto op = GrRectOpFactory::MakeNonAAFill(std::move(paint), SkMatrix::I(),
+                                             SkRect::MakeWH(rtc->width(), rtc->height()),
+                                             GrAAType::kNone);
+    rtc->addDrawOp(GrNoClip(), std::move(op));
+}
+
+#include "SkCommandLineFlags.h"
+DEFINE_bool(randomProcessorTest, false, "Use non-deterministic seed for random processor tests?");
+
+#if GR_TEST_UTILS
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    using FPFactory = GrProcessorTestFactory<GrFragmentProcessor>;
+
+    uint32_t seed = 0;
+    if (FLAGS_randomProcessorTest) {
+        std::random_device rd;
+        seed = rd();
+    }
+    // If a non-deterministic bot fails this test, check the output to see what seed it used, then
+    // hard-code that value here:
+    SkRandom random(seed);
+
+    sk_sp<GrRenderTargetContext> rtc = context->makeDeferredRenderTargetContext(
+            SkBackingFit::kExact, 256, 256, kRGBA_8888_GrPixelConfig, nullptr);
+    GrSurfaceDesc desc;
+    desc.fWidth = 256;
+    desc.fHeight = 256;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+    sk_sp<GrTextureProxy> proxies[2];
+
+    // Put premul data into the RGBA texture that the test FPs can optionally use.
+    std::unique_ptr<GrColor[]> rgbaData(new GrColor[256 * 256]);
+    for (int y = 0; y < 256; ++y) {
+        for (int x = 0; x < 256; ++x) {
+            rgbaData.get()[256 * y + x] =
+                    texel_color(random.nextULessThan(256), random.nextULessThan(256));
+        }
+    }
+    proxies[0] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kYes,
+                                              rgbaData.get(), 256 * sizeof(GrColor));
+
+    // Put random values into the alpha texture that the test FPs can optionally use.
+    desc.fConfig = kAlpha_8_GrPixelConfig;
+    std::unique_ptr<uint8_t[]> alphaData(new uint8_t[256 * 256]);
+    for (int y = 0; y < 256; ++y) {
+        for (int x = 0; x < 256; ++x) {
+            alphaData.get()[256 * y + x] = random.nextULessThan(256);
+        }
+    }
+    proxies[1] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kYes,
+                                              alphaData.get(), 256);
+
+    if (!proxies[0] || !proxies[1]) {
+        return;
+    }
+
+    GrProcessorTestData testData(&random, context, rtc.get(), proxies);
+
+    // Use a different array of premul colors for the output of the fragment processor that preceeds
+    // the fragment processor under test.
+    for (int y = 0; y < 256; ++y) {
+        for (int x = 0; x < 256; ++x) {
+            rgbaData.get()[256 * y + x] = texel_color(x, y);
+        }
+    }
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+    sk_sp<GrTextureProxy> dataProxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                   desc, SkBudgeted::kYes,
+                                                                   rgbaData.get(),
+                                                                   256 * sizeof(GrColor));
+
+    // Because processors factories configure themselves in random ways, this is not exhaustive.
+    for (int i = 0; i < FPFactory::Count(); ++i) {
+        int timesToInvokeFactory = 5;
+        // Increase the number of attempts if the FP has child FPs since optimizations likely depend
+        // on child optimizations being present.
+        sk_sp<GrFragmentProcessor> fp = FPFactory::MakeIdx(i, &testData);
+        for (int j = 0; j < fp->numChildProcessors(); ++j) {
+            // This value made a reasonable trade off between time and coverage when this test was
+            // written.
+            timesToInvokeFactory *= FPFactory::Count() / 2;
+        }
+        for (int j = 0; j < timesToInvokeFactory; ++j) {
+            fp = FPFactory::MakeIdx(i, &testData);
+            if (!fp->instantiate(context->resourceProvider())) {
+                continue;
+            }
+
+            if (!fp->hasConstantOutputForConstantInput() && !fp->preservesOpaqueInput() &&
+                !fp->compatibleWithCoverageAsAlpha()) {
+                continue;
+            }
+            test_draw_op(rtc.get(), fp, dataProxy);
+            memset(rgbaData.get(), 0x0, sizeof(GrColor) * 256 * 256);
+            rtc->readPixels(
+                    SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType, kPremul_SkAlphaType),
+                    rgbaData.get(), 0, 0, 0);
+            bool passing = true;
+            if (0) {  // Useful to see what FPs are being tested.
+                SkString children;
+                for (int c = 0; c < fp->numChildProcessors(); ++c) {
+                    if (!c) {
+                        children.append("(");
+                    }
+                    children.append(fp->childProcessor(c).name());
+                    children.append(c == fp->numChildProcessors() - 1 ? ")" : ", ");
+                }
+                SkDebugf("%s %s\n", fp->name(), children.c_str());
+            }
+            for (int y = 0; y < 256 && passing; ++y) {
+                for (int x = 0; x < 256 && passing; ++x) {
+                    GrColor input = texel_color(x, y);
+                    GrColor output = rgbaData.get()[y * 256 + x];
+                    if (fp->compatibleWithCoverageAsAlpha()) {
+                        // A modulating processor is allowed to modulate either the input color or
+                        // just the input alpha.
+                        bool legalColorModulation =
+                                GrColorUnpackA(output) <= GrColorUnpackA(input) &&
+                                GrColorUnpackR(output) <= GrColorUnpackR(input) &&
+                                GrColorUnpackG(output) <= GrColorUnpackG(input) &&
+                                GrColorUnpackB(output) <= GrColorUnpackB(input);
+                        bool legalAlphaModulation =
+                                GrColorUnpackA(output) <= GrColorUnpackA(input) &&
+                                GrColorUnpackR(output) <= GrColorUnpackA(input) &&
+                                GrColorUnpackG(output) <= GrColorUnpackA(input) &&
+                                GrColorUnpackB(output) <= GrColorUnpackA(input);
+                        if (!legalColorModulation && !legalAlphaModulation) {
+                            ERRORF(reporter,
+                                   "\"Modulating\" processor %s made color/alpha value larger. "
+                                   "Input: 0x%08x, Output: 0x%08x.",
+                                   fp->name(), input, output);
+                            passing = false;
+                        }
+                    }
+                    GrColor4f input4f = texel_color4f(x, y);
+                    GrColor4f output4f = GrColor4f::FromGrColor(output);
+                    GrColor4f expected4f;
+                    if (fp->hasConstantOutputForConstantInput(input4f, &expected4f)) {
+                        float rDiff = fabsf(output4f.fRGBA[0] - expected4f.fRGBA[0]);
+                        float gDiff = fabsf(output4f.fRGBA[1] - expected4f.fRGBA[1]);
+                        float bDiff = fabsf(output4f.fRGBA[2] - expected4f.fRGBA[2]);
+                        float aDiff = fabsf(output4f.fRGBA[3] - expected4f.fRGBA[3]);
+                        static constexpr float kTol = 4 / 255.f;
+                        if (rDiff > kTol || gDiff > kTol || bDiff > kTol || aDiff > kTol) {
+                            ERRORF(reporter,
+                                   "Processor %s claimed output for const input doesn't match "
+                                   "actual output. Error: %f, Tolerance: %f, input: (%f, %f, %f, "
+                                   "%f), actual: (%f, %f, %f, %f), expected(%f, %f, %f, %f)",
+                                   fp->name(), SkTMax(rDiff, SkTMax(gDiff, SkTMax(bDiff, aDiff))),
+                                   kTol, input4f.fRGBA[0], input4f.fRGBA[1], input4f.fRGBA[2],
+                                   input4f.fRGBA[3], output4f.fRGBA[0], output4f.fRGBA[1],
+                                   output4f.fRGBA[2], output4f.fRGBA[3], expected4f.fRGBA[0],
+                                   expected4f.fRGBA[1], expected4f.fRGBA[2], expected4f.fRGBA[3]);
+                            passing = false;
+                        }
+                    }
+                    if (GrColorIsOpaque(input) && fp->preservesOpaqueInput() &&
+                        !GrColorIsOpaque(output)) {
+                        ERRORF(reporter,
+                               "Processor %s claimed opaqueness is preserved but it is not. Input: "
+                               "0x%08x, Output: 0x%08x.",
+                               fp->name(), input, output);
+                        passing = false;
+                    }
+                    if (!passing) {
+                        ERRORF(reporter, "Seed: 0x%08x, Processor details: %s",
+                               seed, fp->dumpInfo().c_str());
+                    }
+                }
+            }
+        }
+    }
+}
+
+#endif  // GR_TEST_UTILS
+#endif  // SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+#endif  // SK_SUPPORT_GPU
diff --git a/src/third_party/skia/tests/ProxyConversionTest.cpp b/src/third_party/skia/tests/ProxyConversionTest.cpp
new file mode 100644
index 0000000..513b680
--- /dev/null
+++ b/src/third_party/skia/tests/ProxyConversionTest.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This is a GPU-backend specific test.
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrBackendSurface.h"
+#include "GrRenderTarget.h"
+#include "GrRenderTargetProxy.h"
+#include "GrResourceProvider.h"
+#include "GrSurfaceProxy.h"
+#include "GrTexture.h"
+#include "GrTextureProxy.h"
+
+static sk_sp<GrSurfaceProxy> make_wrapped_FBO0(GrResourceProvider* provider,
+                                               skiatest::Reporter* reporter,
+                                               const GrSurfaceDesc& desc) {
+    GrGLFramebufferInfo fboInfo;
+    fboInfo.fFBOID = 0;
+    GrBackendRenderTarget backendRT(desc.fWidth, desc.fHeight, desc.fSampleCnt, 8,
+                                    desc.fConfig, fboInfo);
+
+    sk_sp<GrRenderTarget> defaultFBO(provider->wrapBackendRenderTarget(backendRT, desc.fOrigin));
+    SkASSERT(!defaultFBO->asTexture());
+
+    return GrSurfaceProxy::MakeWrapped(std::move(defaultFBO));
+}
+
+static sk_sp<GrSurfaceProxy> make_wrapped_offscreen_rt(GrResourceProvider* provider,
+                                                       skiatest::Reporter* reporter,
+                                                       const GrSurfaceDesc& desc,
+                                                       SkBudgeted budgeted) {
+    SkASSERT(kRenderTarget_GrSurfaceFlag == desc.fFlags);
+
+    sk_sp<GrTexture> tex(provider->createTexture(desc, budgeted));
+
+    return GrSurfaceProxy::MakeWrapped(std::move(tex));
+}
+
+static sk_sp<GrSurfaceProxy> make_wrapped_texture(GrResourceProvider* provider,
+                                                  const GrSurfaceDesc& desc,
+                                                  SkBudgeted budgeted) {
+    sk_sp<GrTexture> tex(provider->createTexture(desc, budgeted));
+
+    return GrSurfaceProxy::MakeWrapped(std::move(tex));
+}
+
+// Test converting between RenderTargetProxies and TextureProxies for wrapped
+// Proxies
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WrappedProxyConversionTest, reporter, ctxInfo) {
+    GrResourceProvider* provider = ctxInfo.grContext()->resourceProvider();
+
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fWidth = 64;
+    desc.fHeight = 64;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+
+    if (kOpenGL_GrBackend == ctxInfo.backend()) {
+        // External on-screen render target.
+        sk_sp<GrSurfaceProxy> sProxy(make_wrapped_FBO0(provider, reporter, desc));
+
+        // RenderTarget-only
+        GrRenderTargetProxy* rtProxy = sProxy->asRenderTargetProxy();
+        REPORTER_ASSERT(reporter, rtProxy);
+        REPORTER_ASSERT(reporter, !rtProxy->asTextureProxy());
+        REPORTER_ASSERT(reporter, rtProxy->asRenderTargetProxy() == rtProxy);
+    }
+
+    {
+        // Internal offscreen render target.
+        sk_sp<GrSurfaceProxy> sProxy(make_wrapped_offscreen_rt(provider,
+                                                               reporter, desc,
+                                                               SkBudgeted::kYes));
+
+        // Both RenderTarget and Texture
+        GrRenderTargetProxy* rtProxy = sProxy->asRenderTargetProxy();
+        REPORTER_ASSERT(reporter, rtProxy);
+        GrTextureProxy* tProxy = rtProxy->asTextureProxy();
+        REPORTER_ASSERT(reporter, tProxy);
+        REPORTER_ASSERT(reporter, tProxy->asRenderTargetProxy() == rtProxy);
+        REPORTER_ASSERT(reporter, rtProxy->asRenderTargetProxy() == rtProxy);
+    }
+
+    {
+        // Internal offscreen render target - but through GrTextureProxy
+        sk_sp<GrSurfaceProxy> sProxy(make_wrapped_texture(provider,  desc, SkBudgeted::kYes));
+
+        // Both RenderTarget and Texture
+        GrTextureProxy* tProxy = sProxy->asTextureProxy();
+        REPORTER_ASSERT(reporter, tProxy);
+        GrRenderTargetProxy* rtProxy = tProxy->asRenderTargetProxy();
+        REPORTER_ASSERT(reporter, rtProxy);
+        REPORTER_ASSERT(reporter, rtProxy->asTextureProxy() == tProxy);
+        REPORTER_ASSERT(reporter, tProxy->asTextureProxy() == tProxy);
+    }
+
+    {
+        desc.fFlags = kNone_GrSurfaceFlags; // force no-RT
+
+        sk_sp<GrSurfaceProxy> sProxy(make_wrapped_texture(provider,  desc, SkBudgeted::kYes));
+
+        // Texture-only
+        GrTextureProxy* tProxy = sProxy->asTextureProxy();
+        REPORTER_ASSERT(reporter, tProxy);
+        REPORTER_ASSERT(reporter, tProxy->asTextureProxy() == tProxy);
+        REPORTER_ASSERT(reporter, !tProxy->asRenderTargetProxy());
+    }
+}
+
+// Test converting between RenderTargetProxies and TextureProxies for deferred
+// Proxies
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DefferredProxyConversionTest, reporter, ctxInfo) {
+    GrResourceProvider* resourceProvider = ctxInfo.grContext()->resourceProvider();
+
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fWidth = 64;
+    desc.fHeight = 64;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+    {
+        sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(resourceProvider, desc,
+                                                                 SkBackingFit::kApprox,
+                                                                 SkBudgeted::kYes));
+
+        // Both RenderTarget and Texture
+        GrRenderTargetProxy* rtProxy = proxy->asRenderTargetProxy();
+        REPORTER_ASSERT(reporter, rtProxy);
+        GrTextureProxy* tProxy = rtProxy->asTextureProxy();
+        REPORTER_ASSERT(reporter, tProxy);
+        REPORTER_ASSERT(reporter, tProxy->asRenderTargetProxy() == rtProxy);
+        REPORTER_ASSERT(reporter, rtProxy->asRenderTargetProxy() == rtProxy);
+    }
+
+    {
+        sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(resourceProvider, desc,
+                                                                 SkBackingFit::kApprox,
+                                                                 SkBudgeted::kYes));
+
+        // Both RenderTarget and Texture - but via GrTextureProxy
+        GrTextureProxy* tProxy = proxy->asTextureProxy();
+        REPORTER_ASSERT(reporter, tProxy);
+        GrRenderTargetProxy* rtProxy = tProxy->asRenderTargetProxy();
+        REPORTER_ASSERT(reporter, rtProxy);
+        REPORTER_ASSERT(reporter, rtProxy->asTextureProxy() == tProxy);
+        REPORTER_ASSERT(reporter, tProxy->asTextureProxy() == tProxy);
+    }
+
+    {
+        desc.fFlags = kNone_GrSurfaceFlags; // force no-RT
+
+        sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(resourceProvider, desc,
+                                                                 SkBackingFit::kApprox,
+                                                                 SkBudgeted::kYes));
+
+        // Texture-only
+        GrTextureProxy* tProxy = proxy->asTextureProxy();
+        REPORTER_ASSERT(reporter, tProxy);
+        REPORTER_ASSERT(reporter, tProxy->asTextureProxy() == tProxy);
+        REPORTER_ASSERT(reporter, !tProxy->asRenderTargetProxy());
+    }
+}
+#endif
diff --git a/src/third_party/skia/tests/ProxyRefTest.cpp b/src/third_party/skia/tests/ProxyRefTest.cpp
new file mode 100644
index 0000000..04510b3
--- /dev/null
+++ b/src/third_party/skia/tests/ProxyRefTest.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This is a GPU-backend specific test.
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContextPriv.h"
+#include "GrGpuResourceRef.h"
+#include "GrRenderTargetProxy.h"
+#include "GrResourceProvider.h"
+#include "GrSurfaceProxy.h"
+#include "GrTexture.h"
+#include "GrTextureProxy.h"
+
+int32_t GrIORefProxy::getProxyRefCnt_TestOnly() const {
+    return fRefCnt;
+}
+
+int32_t GrIORefProxy::getBackingRefCnt_TestOnly() const {
+    if (fTarget) {
+        return fTarget->fRefCnt;
+    }
+
+    return fRefCnt;
+}
+
+int32_t GrIORefProxy::getPendingReadCnt_TestOnly() const {
+    if (fTarget) {
+        return fTarget->fPendingReads;
+    }
+
+    return fPendingReads;
+}
+
+int32_t GrIORefProxy::getPendingWriteCnt_TestOnly() const {
+    if (fTarget) {
+        return fTarget->fPendingWrites;
+    }
+
+    return fPendingWrites;
+}
+
+static const int kWidthHeight = 128;
+
+static void check_refs(skiatest::Reporter* reporter,
+                       GrTextureProxy* proxy,
+                       int32_t expectedProxyRefs,
+                       int32_t expectedBackingRefs,
+                       int32_t expectedNumReads,
+                       int32_t expectedNumWrites) {
+    REPORTER_ASSERT(reporter, proxy->getProxyRefCnt_TestOnly() == expectedProxyRefs);
+    REPORTER_ASSERT(reporter, proxy->getBackingRefCnt_TestOnly() == expectedBackingRefs);
+    REPORTER_ASSERT(reporter, proxy->getPendingReadCnt_TestOnly() == expectedNumReads);
+    REPORTER_ASSERT(reporter, proxy->getPendingWriteCnt_TestOnly() == expectedNumWrites);
+
+    SkASSERT(proxy->getProxyRefCnt_TestOnly() == expectedProxyRefs);
+    SkASSERT(proxy->getBackingRefCnt_TestOnly() == expectedBackingRefs);
+    SkASSERT(proxy->getPendingReadCnt_TestOnly() == expectedNumReads);
+    SkASSERT(proxy->getPendingWriteCnt_TestOnly() == expectedNumWrites);
+}
+
+static sk_sp<GrTextureProxy> make_deferred(GrContext* context) {
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fWidth = kWidthHeight;
+    desc.fHeight = kWidthHeight;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+    return GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc,
+                                        SkBackingFit::kApprox, SkBudgeted::kYes,
+                                        GrResourceProvider::kNoPendingIO_Flag);
+}
+
+static sk_sp<GrTextureProxy> make_wrapped(GrContext* context) {
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fWidth = kWidthHeight;
+    desc.fHeight = kWidthHeight;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+    sk_sp<GrTexture> tex(context->resourceProvider()->createTexture(desc, SkBudgeted::kNo));
+
+    return GrSurfaceProxy::MakeWrapped(std::move(tex));
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ProxyRefTest, reporter, ctxInfo) {
+    GrResourceProvider* provider = ctxInfo.grContext()->resourceProvider();
+
+    for (auto make : { make_deferred, make_wrapped }) {
+        // A single write
+        {
+            sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext()));
+
+            GrPendingIOResource<GrSurfaceProxy, kWrite_GrIOType> fWrite(proxy.get());
+
+            static const int kExpectedReads = 0;
+            static const int kExpectedWrites = 1;
+
+            check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites);
+
+            proxy->instantiate(provider);
+
+            // In the deferred case, this checks that the refs transfered to the GrSurface
+            check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites);
+        }
+
+        // A single read
+        {
+            sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext()));
+
+            GrPendingIOResource<GrSurfaceProxy, kRead_GrIOType> fRead(proxy.get());
+
+            static const int kExpectedReads = 1;
+            static const int kExpectedWrites = 0;
+
+            check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites);
+
+            proxy->instantiate(provider);
+
+            // In the deferred case, this checks that the refs transfered to the GrSurface
+            check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites);
+        }
+
+        // A single read/write pair
+        {
+            sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext()));
+
+            GrPendingIOResource<GrSurfaceProxy, kRW_GrIOType> fRW(proxy.get());
+
+            static const int kExpectedReads = 1;
+            static const int kExpectedWrites = 1;
+
+            check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites);
+
+            proxy->instantiate(provider);
+
+            // In the deferred case, this checks that the refs transferred to the GrSurface
+            check_refs(reporter, proxy.get(), 1, 1, kExpectedReads, kExpectedWrites);
+        }
+
+        // Multiple normal refs
+        {
+            sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext()));
+            proxy->ref();
+            proxy->ref();
+
+            static const int kExpectedReads = 0;
+            static const int kExpectedWrites = 0;
+
+            check_refs(reporter, proxy.get(), 3, 3,kExpectedReads, kExpectedWrites);
+
+            proxy->instantiate(provider);
+
+            // In the deferred case, this checks that the refs transferred to the GrSurface
+            check_refs(reporter, proxy.get(), 3, 3, kExpectedReads, kExpectedWrites);
+
+            proxy->unref();
+            proxy->unref();
+        }
+
+        // Continue using (reffing) proxy after instantiation
+        {
+            sk_sp<GrTextureProxy> proxy((*make)(ctxInfo.grContext()));
+            proxy->ref();
+
+            GrPendingIOResource<GrSurfaceProxy, kWrite_GrIOType> fWrite(proxy.get());
+
+            static const int kExpectedWrites = 1;
+
+            check_refs(reporter, proxy.get(), 2, 2, 0, kExpectedWrites);
+
+            proxy->instantiate(provider);
+
+            // In the deferred case, this checks that the refs transfered to the GrSurface
+            check_refs(reporter, proxy.get(), 2, 2, 0, kExpectedWrites);
+
+            proxy->unref();
+            check_refs(reporter, proxy.get(), 1, 1, 0, kExpectedWrites);
+
+            GrPendingIOResource<GrSurfaceProxy, kRead_GrIOType> fRead(proxy.get());
+            check_refs(reporter, proxy.get(), 1, 1, 1, kExpectedWrites);
+        }
+    }
+}
+
+#endif
diff --git a/src/third_party/skia/tests/ProxyTest.cpp b/src/third_party/skia/tests/ProxyTest.cpp
new file mode 100644
index 0000000..dd1f4cb
--- /dev/null
+++ b/src/third_party/skia/tests/ProxyTest.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This is a GPU-backend specific test.
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrBackendSurface.h"
+#include "GrRenderTargetPriv.h"
+#include "GrRenderTargetProxy.h"
+#include "GrResourceProvider.h"
+#include "GrSurfaceProxy.h"
+#include "GrTexture.h"
+#include "GrTextureProxy.h"
+
+// Check that the surface proxy's member vars are set as expected
+static void check_surface(skiatest::Reporter* reporter,
+                          GrSurfaceProxy* proxy,
+                          GrSurfaceOrigin origin,
+                          int width, int height,
+                          GrPixelConfig config,
+                          const GrGpuResource::UniqueID& uniqueID,
+                          SkBudgeted budgeted) {
+    REPORTER_ASSERT(reporter, proxy->origin() == origin);
+    REPORTER_ASSERT(reporter, proxy->width() == width);
+    REPORTER_ASSERT(reporter, proxy->height() == height);
+    REPORTER_ASSERT(reporter, proxy->config() == config);
+    if (!uniqueID.isInvalid()) {
+        REPORTER_ASSERT(reporter, proxy->uniqueID().asUInt() == uniqueID.asUInt());
+    } else {
+        REPORTER_ASSERT(reporter, !proxy->uniqueID().isInvalid());
+    }
+    REPORTER_ASSERT(reporter, proxy->isBudgeted() == budgeted);
+}
+
+static void check_rendertarget(skiatest::Reporter* reporter,
+                               const GrCaps& caps,
+                               GrResourceProvider* provider,
+                               GrRenderTargetProxy* rtProxy,
+                               int numSamples,
+                               SkBackingFit fit,
+                               int expectedMaxWindowRects,
+                               bool wasWrapped) {
+    REPORTER_ASSERT(reporter, rtProxy->maxWindowRectangles(caps) == expectedMaxWindowRects);
+    REPORTER_ASSERT(reporter, rtProxy->numStencilSamples() == numSamples);
+
+    GrSurfaceProxy::UniqueID idBefore = rtProxy->uniqueID();
+    REPORTER_ASSERT(reporter, rtProxy->instantiate(provider));
+    GrRenderTarget* rt = rtProxy->priv().peekRenderTarget();
+
+    REPORTER_ASSERT(reporter, rtProxy->uniqueID() == idBefore);
+    if (wasWrapped) {
+        // Wrapped resources share their uniqueID with the wrapping RenderTargetProxy
+        REPORTER_ASSERT(reporter, rtProxy->uniqueID().asUInt() == rt->uniqueID().asUInt());
+    } else {
+        // Deferred resources should always have a different ID from their instantiated rendertarget
+        REPORTER_ASSERT(reporter, rtProxy->uniqueID().asUInt() != rt->uniqueID().asUInt());
+    }
+
+    REPORTER_ASSERT(reporter, rt->origin() == rtProxy->origin());
+    if (SkBackingFit::kExact == fit) {
+        REPORTER_ASSERT(reporter, rt->width() == rtProxy->width());
+        REPORTER_ASSERT(reporter, rt->height() == rtProxy->height());
+    } else {
+        REPORTER_ASSERT(reporter, rt->width() >= rtProxy->width());
+        REPORTER_ASSERT(reporter, rt->height() >= rtProxy->height());
+    }
+    REPORTER_ASSERT(reporter, rt->config() == rtProxy->config());
+
+    REPORTER_ASSERT(reporter, rt->fsaaType() == rtProxy->fsaaType());
+    REPORTER_ASSERT(reporter, rt->numColorSamples() == rtProxy->numColorSamples());
+    REPORTER_ASSERT(reporter, rt->numStencilSamples() == rtProxy->numStencilSamples());
+    REPORTER_ASSERT(reporter, rt->renderTargetPriv().flags() == rtProxy->testingOnly_getFlags());
+}
+
+static void check_texture(skiatest::Reporter* reporter,
+                          GrResourceProvider* provider,
+                          GrTextureProxy* texProxy,
+                          SkBackingFit fit,
+                          bool wasWrapped) {
+    GrSurfaceProxy::UniqueID idBefore = texProxy->uniqueID();
+
+    REPORTER_ASSERT(reporter, texProxy->instantiate(provider));
+    GrTexture* tex = texProxy->priv().peekTexture();
+
+    REPORTER_ASSERT(reporter, texProxy->uniqueID() == idBefore);
+    if (wasWrapped) {
+        // Wrapped resources share their uniqueID with the wrapping TextureProxy
+        REPORTER_ASSERT(reporter, texProxy->uniqueID().asUInt() == tex->uniqueID().asUInt());
+    } else {
+        // Deferred resources should always have a different ID from their instantiated texture
+        REPORTER_ASSERT(reporter, texProxy->uniqueID().asUInt() != tex->uniqueID().asUInt());
+    }
+
+    REPORTER_ASSERT(reporter, tex->origin() == texProxy->origin());
+    if (SkBackingFit::kExact == fit) {
+        REPORTER_ASSERT(reporter, tex->width() == texProxy->width());
+        REPORTER_ASSERT(reporter, tex->height() == texProxy->height());
+    } else {
+        REPORTER_ASSERT(reporter, tex->width() >= texProxy->width());
+        REPORTER_ASSERT(reporter, tex->height() >= texProxy->height());
+    }
+    REPORTER_ASSERT(reporter, tex->config() == texProxy->config());
+}
+
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredProxyTest, reporter, ctxInfo) {
+    GrResourceProvider* provider = ctxInfo.grContext()->resourceProvider();
+    const GrCaps& caps = *ctxInfo.grContext()->caps();
+
+    const GrGpuResource::UniqueID kInvalidResourceID = GrGpuResource::UniqueID::InvalidID();
+
+    int attempt = 0; // useful for debugging
+
+    for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) {
+        for (auto widthHeight : { 100, 128, 1048576 }) {
+            for (auto config : { kAlpha_8_GrPixelConfig, kRGB_565_GrPixelConfig,
+                                 kRGBA_8888_GrPixelConfig }) {
+                for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) {
+                    for (auto budgeted : { SkBudgeted::kYes, SkBudgeted::kNo }) {
+                        for (auto numSamples : { 0, 4, 16, 128 }) {
+                            GrSurfaceDesc desc;
+                            desc.fFlags = kRenderTarget_GrSurfaceFlag;
+                            desc.fOrigin = origin;
+                            desc.fWidth = widthHeight;
+                            desc.fHeight = widthHeight;
+                            desc.fConfig = config;
+                            desc.fSampleCnt = numSamples;
+
+                            {
+                                sk_sp<GrTexture> tex;
+                                if (SkBackingFit::kApprox == fit) {
+                                    tex = provider->createApproxTexture(desc, 0);
+                                } else {
+                                    tex = provider->createTexture(desc, budgeted);
+                                }
+
+                                sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(
+                                                                                provider, desc,
+                                                                                fit, budgeted));
+                                REPORTER_ASSERT(reporter, SkToBool(tex) == SkToBool(proxy));
+                                if (proxy) {
+                                    REPORTER_ASSERT(reporter, proxy->asRenderTargetProxy());
+                                    // This forces the proxy to compute and cache its
+                                    // pre-instantiation size guess. Later, when it is actually
+                                    // instantiated, it checks that the instantiated size is <= to
+                                    // the pre-computation. If the proxy never computed its
+                                    // pre-instantiation size then the check is skipped.
+                                    proxy->gpuMemorySize();
+
+                                    check_surface(reporter, proxy.get(), origin,
+                                                  widthHeight, widthHeight, config,
+                                                  kInvalidResourceID, budgeted);
+                                    int supportedSamples = caps.getSampleCount(numSamples, config);
+                                    check_rendertarget(reporter, caps, provider,
+                                                       proxy->asRenderTargetProxy(),
+                                                       supportedSamples,
+                                                       fit, caps.maxWindowRectangles(), false);
+                                }
+                            }
+
+                            desc.fFlags = kNone_GrSurfaceFlags;
+
+                            {
+                                sk_sp<GrTexture> tex;
+                                if (SkBackingFit::kApprox == fit) {
+                                    tex = provider->createApproxTexture(desc, 0);
+                                } else {
+                                    tex = provider->createTexture(desc, budgeted);
+                                }
+
+                                sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(provider,
+                                                                                         desc,
+                                                                                         fit,
+                                                                                         budgeted));
+                                REPORTER_ASSERT(reporter, SkToBool(tex) == SkToBool(proxy));
+                                if (proxy) {
+                                    // This forces the proxy to compute and cache its pre-instantiation
+                                    // size guess. Later, when it is actually instantiated, it checks
+                                    // that the instantiated size is <= to the pre-computation.
+                                    // If the proxy never computed its pre-instantiation size then the
+                                    // check is skipped.
+                                    proxy->gpuMemorySize();
+
+                                    check_surface(reporter, proxy.get(), origin,
+                                                  widthHeight, widthHeight, config,
+                                                  kInvalidResourceID, budgeted);
+                                    check_texture(reporter, provider, proxy->asTextureProxy(),
+                                                  fit, false);
+                                }
+                            }
+
+                            attempt++;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WrappedProxyTest, reporter, ctxInfo) {
+    GrResourceProvider* provider = ctxInfo.grContext()->resourceProvider();
+    const GrCaps& caps = *ctxInfo.grContext()->caps();
+
+    static const int kWidthHeight = 100;
+
+    for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) {
+        for (auto config : { kAlpha_8_GrPixelConfig, kRGBA_8888_GrPixelConfig }) {
+            for (auto budgeted : { SkBudgeted::kYes, SkBudgeted::kNo }) {
+                for (auto numSamples: { 0, 4}) {
+                    int supportedNumSamples = caps.getSampleCount(numSamples, config);
+
+                    bool renderable = caps.isConfigRenderable(config, numSamples > 0);
+
+                    GrSurfaceDesc desc;
+                    desc.fOrigin = origin;
+                    desc.fWidth = kWidthHeight;
+                    desc.fHeight = kWidthHeight;
+                    desc.fConfig = config;
+                    desc.fSampleCnt = supportedNumSamples;
+
+                    // External on-screen render target.
+                    if (renderable && kOpenGL_GrBackend == ctxInfo.backend()) {
+                        GrGLFramebufferInfo fboInfo;
+                        fboInfo.fFBOID = 0;
+                        GrBackendRenderTarget backendRT(kWidthHeight, kWidthHeight, numSamples, 8,
+                                                        config, fboInfo);
+
+                        sk_sp<GrRenderTarget> defaultFBO(
+                            provider->wrapBackendRenderTarget(backendRT, origin));
+
+                        sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(defaultFBO));
+                        check_surface(reporter, sProxy.get(), origin,
+                                      kWidthHeight, kWidthHeight, config,
+                                      defaultFBO->uniqueID(), SkBudgeted::kNo);
+                        check_rendertarget(reporter, caps, provider, sProxy->asRenderTargetProxy(),
+                                           supportedNumSamples, SkBackingFit::kExact, 0, true);
+                    }
+
+                    sk_sp<GrTexture> tex;
+
+                    // Internal offscreen render target.
+                    if (renderable) {
+                        desc.fFlags = kRenderTarget_GrSurfaceFlag;
+                        tex = provider->createTexture(desc, budgeted);
+                        sk_sp<GrRenderTarget> rt(sk_ref_sp(tex->asRenderTarget()));
+
+                        sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(rt));
+                        check_surface(reporter, sProxy.get(), origin,
+                                      kWidthHeight, kWidthHeight, config,
+                                      rt->uniqueID(), budgeted);
+                        check_rendertarget(reporter, caps, provider, sProxy->asRenderTargetProxy(),
+                                           supportedNumSamples, SkBackingFit::kExact,
+                                           caps.maxWindowRectangles(), true);
+                    }
+
+                    if (!tex) {
+                        SkASSERT(kNone_GrSurfaceFlags == desc.fFlags );
+                        desc.fSampleCnt = 0;
+                        tex = provider->createTexture(desc, budgeted);
+                    }
+
+                    sk_sp<GrSurfaceProxy> sProxy(GrSurfaceProxy::MakeWrapped(tex));
+                    check_surface(reporter, sProxy.get(), origin,
+                                  kWidthHeight, kWidthHeight, config, tex->uniqueID(), budgeted);
+                    check_texture(reporter, provider, sProxy->asTextureProxy(),
+                                  SkBackingFit::kExact, true);
+                }
+            }
+        }
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ZeroSizedProxyTest, reporter, ctxInfo) {
+    GrResourceProvider* provider = ctxInfo.grContext()->resourceProvider();
+
+    for (auto flags : { kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags }) {
+        for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) {
+            for (int width : { 0, 100 }) {
+                for (int height : { 0, 100}) {
+                    if (width && height) {
+                        continue; // not zero-sized
+                    }
+
+                    GrSurfaceDesc desc;
+                    desc.fFlags = flags;
+                    desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+                    desc.fWidth = width;
+                    desc.fHeight = height;
+                    desc.fConfig = kRGBA_8888_GrPixelConfig;
+                    desc.fSampleCnt = 0;
+
+                    sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(provider,
+                                                                             desc,
+                                                                             fit,
+                                                                             SkBudgeted::kNo));
+                    REPORTER_ASSERT(reporter, !proxy);
+                }
+            }
+        }
+    }
+}
+
+#endif
diff --git a/src/third_party/skia/tests/QuickRejectTest.cpp b/src/third_party/skia/tests/QuickRejectTest.cpp
index 8f4357e..d7d330f 100644
--- a/src/third_party/skia/tests/QuickRejectTest.cpp
+++ b/src/third_party/skia/tests/QuickRejectTest.cpp
@@ -5,8 +5,12 @@
  * found in the LICENSE file.
  */
 
+#include "SkArenaAlloc.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
+#include "SkColorSpaceXformer.h"
 #include "SkDrawLooper.h"
+#include "SkLightingImageFilter.h"
 #include "SkTypes.h"
 #include "Test.h"
 
@@ -16,25 +20,29 @@
 class TestLooper : public SkDrawLooper {
 public:
 
-    virtual SkDrawLooper::Context* createContext(SkCanvas*, void* storage) const SK_OVERRIDE {
-        return SkNEW_PLACEMENT(storage, TestDrawLooperContext);
+    SkDrawLooper::Context* makeContext(SkCanvas*, SkArenaAlloc* alloc) const override {
+        return alloc->make<TestDrawLooperContext>();
     }
 
-    virtual size_t contextSize() const SK_OVERRIDE { return sizeof(TestDrawLooperContext); }
+    sk_sp<SkDrawLooper> onMakeColorSpace(SkColorSpaceXformer*) const override {
+        return nullptr;
+    }
 
 #ifndef SK_IGNORE_TO_STRING
-    virtual void toString(SkString* str) const SK_OVERRIDE {
+    void toString(SkString* str) const override {
         str->append("TestLooper:");
     }
 #endif
 
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(TestLooper)
+
 private:
     class TestDrawLooperContext : public SkDrawLooper::Context {
     public:
         TestDrawLooperContext() : fOnce(true) {}
-        virtual ~TestDrawLooperContext() {}
+        ~TestDrawLooperContext() override {}
 
-        virtual bool next(SkCanvas* canvas, SkPaint*) SK_OVERRIDE {
+        bool next(SkCanvas* canvas, SkPaint*) override {
             if (fOnce) {
                 fOnce = false;
                 canvas->translate(SkIntToScalar(10), 0);
@@ -42,13 +50,14 @@
             }
             return false;
         }
+
     private:
         bool fOnce;
     };
-
-    SK_DECLARE_UNFLATTENABLE_OBJECT()
 };
 
+sk_sp<SkFlattenable> TestLooper::CreateProc(SkReadBuffer&) { return sk_make_sp<TestLooper>(); }
+
 static void test_drawBitmap(skiatest::Reporter* reporter) {
     SkBitmap src;
     src.allocN32Pixels(10, 10);
@@ -80,11 +89,89 @@
     // to the left. The test is to ensure that canvas' quickReject machinary
     // allows us through, even though sans-looper we would look like we should
     // be clipped out.
-    paint.setLooper(new TestLooper)->unref();
+    paint.setLooper(sk_make_sp<TestLooper>());
     canvas.drawBitmap(src, SkIntToScalar(-10), 0, &paint);
     REPORTER_ASSERT(reporter, 0xFFFFFFFF == *dst.getAddr32(5, 5));
 }
 
+static void test_layers(skiatest::Reporter* reporter) {
+    SkCanvas canvas(100, 100);
+
+    SkRect r = SkRect::MakeWH(10, 10);
+    REPORTER_ASSERT(reporter, false == canvas.quickReject(r));
+
+    r.offset(300, 300);
+    REPORTER_ASSERT(reporter, true == canvas.quickReject(r));
+
+    // Test that saveLayer updates quickReject
+    SkRect bounds = SkRect::MakeLTRB(50, 50, 70, 70);
+    canvas.saveLayer(&bounds, nullptr);
+    REPORTER_ASSERT(reporter, true == canvas.quickReject(SkRect::MakeWH(10, 10)));
+    REPORTER_ASSERT(reporter, false == canvas.quickReject(SkRect::MakeWH(60, 60)));
+}
+
+static void test_quick_reject(skiatest::Reporter* reporter) {
+    SkCanvas canvas(100, 100);
+    SkRect r0 = SkRect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
+    SkRect r1 = SkRect::MakeLTRB(-50.0f, 110.0f, 50.0f, 120.0f);
+    SkRect r2 = SkRect::MakeLTRB(110.0f, -50.0f, 120.0f, 50.0f);
+    SkRect r3 = SkRect::MakeLTRB(-120.0f, -50.0f, 120.0f, 50.0f);
+    SkRect r4 = SkRect::MakeLTRB(-50.0f, -120.0f, 50.0f, 120.0f);
+    SkRect r5 = SkRect::MakeLTRB(-120.0f, -120.0f, 120.0f, 120.0f);
+    SkRect r6 = SkRect::MakeLTRB(-120.0f, -120.0f, -110.0f, -110.0f);
+    SkRect r7 = SkRect::MakeLTRB(SK_ScalarNaN, -50.0f, 50.0f, 50.0f);
+    SkRect r8 = SkRect::MakeLTRB(-50.0f, SK_ScalarNaN, 50.0f, 50.0f);
+    SkRect r9 = SkRect::MakeLTRB(-50.0f, -50.0f, SK_ScalarNaN, 50.0f);
+    SkRect r10 = SkRect::MakeLTRB(-50.0f, -50.0f, 50.0f, SK_ScalarNaN);
+    REPORTER_ASSERT(reporter, false == canvas.quickReject(r0));
+    REPORTER_ASSERT(reporter, true == canvas.quickReject(r1));
+    REPORTER_ASSERT(reporter, true == canvas.quickReject(r2));
+    REPORTER_ASSERT(reporter, false == canvas.quickReject(r3));
+    REPORTER_ASSERT(reporter, false == canvas.quickReject(r4));
+    REPORTER_ASSERT(reporter, false == canvas.quickReject(r5));
+    REPORTER_ASSERT(reporter, true == canvas.quickReject(r6));
+    REPORTER_ASSERT(reporter, true == canvas.quickReject(r7));
+    REPORTER_ASSERT(reporter, true == canvas.quickReject(r8));
+    REPORTER_ASSERT(reporter, true == canvas.quickReject(r9));
+    REPORTER_ASSERT(reporter, true == canvas.quickReject(r10));
+
+    SkMatrix m = SkMatrix::MakeScale(2.0f);
+    m.setTranslateX(10.0f);
+    m.setTranslateY(10.0f);
+    canvas.setMatrix(m);
+    SkRect r11 = SkRect::MakeLTRB(5.0f, 5.0f, 100.0f, 100.0f);
+    SkRect r12 = SkRect::MakeLTRB(5.0f, 50.0f, 100.0f, 100.0f);
+    SkRect r13 = SkRect::MakeLTRB(50.0f, 5.0f, 100.0f, 100.0f);
+    REPORTER_ASSERT(reporter, false == canvas.quickReject(r11));
+    REPORTER_ASSERT(reporter, true == canvas.quickReject(r12));
+    REPORTER_ASSERT(reporter, true == canvas.quickReject(r13));
+}
+
 DEF_TEST(QuickReject, reporter) {
     test_drawBitmap(reporter);
+    test_layers(reporter);
+    test_quick_reject(reporter);
+}
+
+// Regression test to make sure that we keep fIsScaleTranslate up to date on the canvas.
+// It is possible to set a new matrix on the canvas without calling setMatrix().  This tests
+// that code path.
+DEF_TEST(QuickReject_MatrixState, reporter) {
+    SkCanvas canvas(100, 100);
+
+    SkMatrix matrix;
+    matrix.setRotate(45.0f);
+    canvas.setMatrix(matrix);
+
+    SkPaint paint;
+    sk_sp<SkImageFilter> filter = SkLightingImageFilter::MakeDistantLitDiffuse(
+            SkPoint3::Make(1.0f, 1.0f, 1.0f), 0xFF0000FF, 2.0f, 0.5f, nullptr);
+    REPORTER_ASSERT(reporter, filter);
+    paint.setImageFilter(filter);
+    SkCanvas::SaveLayerRec rec;
+    rec.fPaint = &paint;
+    canvas.saveLayer(rec);
+
+    // quickReject() will assert if the matrix is out of sync.
+    canvas.quickReject(SkRect::MakeWH(100.0f, 100.0f));
 }
diff --git a/src/third_party/skia/tests/RRectInPathTest.cpp b/src/third_party/skia/tests/RRectInPathTest.cpp
new file mode 100644
index 0000000..4aa535e
--- /dev/null
+++ b/src/third_party/skia/tests/RRectInPathTest.cpp
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkMatrix.h"
+#include "SkPath.h"
+//#include "SkPathRef.h"
+#include "SkRRect.h"
+#include "Test.h"
+
+static SkRRect path_contains_rrect(skiatest::Reporter* reporter, const SkPath& path,
+                                   SkPath::Direction* dir, unsigned* start) {
+    SkRRect 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);
+    }
+    return out;
+}
+
+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:
+        case SkRRect::kOval_Type:
+            return in;
+        default:
+            break;
+    }
+    SkPath 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,
+                                      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,
+                                        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], SkPath::Direction dir, unsigned start) {
+    SkRRect rrect;
+    rrect.setRectRadii(r, v);
+    path_contains_rrect_check(reporter, rrect, dir, start);
+}
+
+class ForceIsRRect_Private {
+public:
+    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,
+                                      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, weight);
+    path.lineTo(196.5f, 0);
+    path.conicTo(200, 0, 200, 3.5f, weight);
+    path.lineTo(200, 62.5f);
+    path.conicTo(200, 66, 196.5f, 66, weight);
+    path.lineTo(3.5f, 66);
+    path.conicTo(0, 66, 0, 62.5, weight);
+    path.close();
+    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, weight);
+    path.lineTo(149.5, 0);
+    path.conicTo(153, 0, 153, 3.5f, weight);
+    path.lineTo(153, 81.5f);
+    path.conicTo(153, 85, 149.5f, 85, weight);
+    path.lineTo(3.5f, 85);
+    path.conicTo(0, 85, 0, 81.5f, weight);
+    path.close();
+    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, weight);
+    path.lineTo(1363, 14);
+    path.conicTo(1370, 14, 1370, 21, weight);
+    path.lineTo(1370, 1189);
+    path.conicTo(1370, 1196, 1363, 1196, weight);
+    path.lineTo(21, 1196);
+    path.conicTo(14, 1196, 14, 1189, weight);
+    path.close();
+    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, weight);
+    path.lineTo(1363, 14);
+    path.conicTo(1370, 14, 1370, 21, weight);
+    path.lineTo(1370, 1743);
+    path.conicTo(1370, 1750, 1363, 1750, weight);
+    path.lineTo(21, 1750);
+    path.conicTo(14, 1750, 14, 1743, weight);
+    path.close();
+    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) {
+    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+        for (int start = 0; start < 8; ++start) {
+            {
+                // crbug.com/458522
+                SkRRect rr;
+                const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
+                const SkScalar rad = 12814;
+                const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } };
+                rr.setRectRadii(bounds, vec);
+                path_contains_rrect_check(reporter, rr, dir, start);
+            }
+
+            {
+                // crbug.com//463920
+                SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0);
+                SkVector radii[4] = {
+                    { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f }
+                };
+                SkRRect rr;
+                rr.setRectRadii(r, radii);
+                path_contains_rrect_nocheck(reporter, rr, dir, start);
+            }
+        }
+    }
+}
+
+static void test_empty_crbug_458524(skiatest::Reporter* reporter) {
+    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+        for (int start = 0; start < 8; ++start) {
+            SkRRect rr;
+            const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
+            const SkScalar rad = 40;
+            rr.setRectXY(bounds, rad, rad);
+            path_contains_rrect_check(reporter, rr, dir, start);
+
+            SkRRect other;
+            SkMatrix matrix;
+            matrix.setScale(0, 1);
+            rr.transform(matrix, &other);
+            path_contains_rrect_check(reporter, rr, dir, start);
+        }
+    }
+}
+
+static void test_inset(skiatest::Reporter* reporter) {
+    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+        for (int start = 0; start < 8; ++start) {
+            SkRRect rr, rr2;
+            SkRect r = { 0, 0, 100, 100 };
+
+            rr.setRect(r);
+            rr.inset(-20, -20, &rr2);
+            path_contains_rrect_check(reporter, rr, dir, start);
+
+            rr.inset(20, 20, &rr2);
+            path_contains_rrect_check(reporter, rr, dir, start);
+
+            rr.inset(r.width()/2, r.height()/2, &rr2);
+            path_contains_rrect_check(reporter, rr, dir, start);
+
+            rr.setRectXY(r, 20, 20);
+            rr.inset(19, 19, &rr2);
+            path_contains_rrect_check(reporter, rr, dir, start);
+            rr.inset(20, 20, &rr2);
+            path_contains_rrect_check(reporter, rr, dir, start);
+        }
+    }
+}
+
+
+static void test_9patch_rrect(skiatest::Reporter* reporter,
+                              const SkRect& rect,
+                              SkScalar l, SkScalar t, SkScalar r, SkScalar b,
+                              bool checkRadii) {
+    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+        for (int start = 0; start < 8; ++start) {
+            SkRRect rr;
+            rr.setNinePatch(rect, l, t, r, b);
+            if (checkRadii) {
+                path_contains_rrect_check(reporter, rr, dir, start);
+            } else {
+                path_contains_rrect_nocheck(reporter, rr, dir, start);
+            }
+
+            SkRRect rr2; // construct the same RR using the most general set function
+            SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } };
+            rr2.setRectRadii(rect, radii);
+            if (checkRadii) {
+                path_contains_rrect_check(reporter, rr, dir, start);
+            } else {
+                path_contains_rrect_nocheck(reporter, rr, dir, start);
+            }
+        }
+    }
+}
+
+// Test out the basic API entry points
+static void test_round_rect_basic(skiatest::Reporter* reporter) {
+    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+        for (int start = 0; start < 8; ++start) {
+            //----
+            SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
+
+            SkRRect rr1;
+            rr1.setRect(rect);
+            path_contains_rrect_check(reporter, rr1, dir, start);
+
+            SkRRect rr1_2; // construct the same RR using the most general set function
+            SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
+            rr1_2.setRectRadii(rect, rr1_2_radii);
+            path_contains_rrect_check(reporter, rr1_2, dir, start);
+            SkRRect rr1_3;  // construct the same RR using the nine patch set function
+            rr1_3.setNinePatch(rect, 0, 0, 0, 0);
+            path_contains_rrect_check(reporter, rr1_2, dir, start);
+
+            //----
+            SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) };
+            SkRRect rr2;
+            rr2.setOval(rect);
+            path_contains_rrect_check(reporter, rr2, dir, start);
+
+            SkRRect rr2_2;  // construct the same RR using the most general set function
+            SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY },
+                                        { halfPoint.fX, halfPoint.fY },
+                                        { halfPoint.fX, halfPoint.fY },
+                                        { halfPoint.fX, halfPoint.fY } };
+            rr2_2.setRectRadii(rect, rr2_2_radii);
+            path_contains_rrect_check(reporter, rr2_2, dir, start);
+            SkRRect rr2_3;  // construct the same RR using the nine patch set function
+            rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY);
+            path_contains_rrect_check(reporter, rr2_3, dir, start);
+
+            //----
+            SkPoint p = { 5, 5 };
+            SkRRect rr3;
+            rr3.setRectXY(rect, p.fX, p.fY);
+            path_contains_rrect_check(reporter, rr3, dir, start);
+
+            SkRRect rr3_2; // construct the same RR using the most general set function
+            SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } };
+            rr3_2.setRectRadii(rect, rr3_2_radii);
+            path_contains_rrect_check(reporter, rr3_2, dir, start);
+            SkRRect rr3_3;  // construct the same RR using the nine patch set function
+            rr3_3.setNinePatch(rect, 5, 5, 5, 5);
+            path_contains_rrect_check(reporter, rr3_3, dir, start);
+
+            //----
+            test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true);
+
+            {
+                // Test out the rrect from skia:3466
+                SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f,
+                                                0.806214333f);
+
+                test_9patch_rrect(reporter,
+                                  rect2,
+                                  0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f,
+                                  false);
+            }
+
+            //----
+            SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } };
+
+            SkRRect rr5;
+            rr5.setRectRadii(rect, radii2);
+            path_contains_rrect_check(reporter, rr5, dir, start);
+        }
+    }
+}
+
+// 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);
+
+            path_contains_rrect_check(reporter, rr1, dir, start);
+
+            //----
+            SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
+
+            SkRRect rr2;
+            rr2.setRectRadii(rect, radii);
+
+            path_contains_rrect_check(reporter, rr2, dir, start);
+
+            //----
+            SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
+
+            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) {
+    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+        for (int start = 0; start < 8; ++start) {
+            //----
+            SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
+            SkRRect rr1;
+            rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight));
+
+            path_contains_rrect_check(reporter, rr1, dir, start);
+        }
+    }
+}
+
+// Test out the non-degenerate RR cases
+static void test_round_rect_general(skiatest::Reporter* reporter) {
+    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, dir, start);
+
+            //----
+            SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
+
+            SkRRect rr2;
+            rr2.setRectRadii(rect, radii);
+
+            path_contains_rrect_check(reporter, rr2, dir, start);
+        }
+    }
+}
+
+static void test_round_rect_iffy_parameters(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);
+            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) {
+    sk_bzero(radii, sizeof(SkVector) * 4);
+    radii[index].set(rad, rad);
+}
+
+static void test_skbug_3239(skiatest::Reporter* reporter) {
+    const float min = SkBits2Float(0xcb7f16c8); /* -16717512.000000 */
+    const float max = SkBits2Float(0x4b7f1c1d); /*  16718877.000000 */
+    const float big = SkBits2Float(0x4b7f1bd7); /*  16718807.000000 */
+
+    const float rad = 33436320;
+
+    const SkRect rectx = SkRect::MakeLTRB(min, min, max, big);
+    const SkRect recty = SkRect::MakeLTRB(min, min, big, max);
+
+    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) {
+    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) {
+    test_tricky_radii(reporter);
+    test_empty_crbug_458524(reporter);
+    test_inset(reporter);
+    test_round_rect_basic(reporter);
+    test_round_rect_rects(reporter);
+    test_round_rect_ovals(reporter);
+    test_round_rect_general(reporter);
+    test_undetected_paths(reporter);
+    test_round_rect_iffy_parameters(reporter);
+    test_skbug_3239(reporter);
+    test_mix(reporter);
+}
diff --git a/src/third_party/skia/tests/RTConfRegistryTest.cpp b/src/third_party/skia/tests/RTConfRegistryTest.cpp
deleted file mode 100644
index 59fb3e9..0000000
--- a/src/third_party/skia/tests/RTConfRegistryTest.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkRTConf.h"
-#include "Test.h"
-
-// Friended proxy for SkRTConfRegistry::parse()
-template <typename T>
-bool test_rt_conf_parse(SkRTConfRegistry* reg, const char* key, T* value) {
-    return reg->parse(key, value);
-}
-
-static void portable_setenv(const char* key, const char* value) {
-#ifdef SK_BUILD_FOR_WIN32
-    _putenv_s(key, value);
-#else
-    setenv(key, value, 1);
-#endif
-}
-
-DEF_TEST(SkRTConfRegistry, reporter) {
-    SkRTConfRegistry reg;
-
-    portable_setenv("skia_nonexistent_item", "132");
-    int result = 0;
-    test_rt_conf_parse(&reg, "nonexistent.item", &result);
-    REPORTER_ASSERT(reporter, result == 132);
-}
diff --git a/src/third_party/skia/tests/RTreeTest.cpp b/src/third_party/skia/tests/RTreeTest.cpp
index 40af5fe..b39af67 100644
--- a/src/third_party/skia/tests/RTreeTest.cpp
+++ b/src/third_party/skia/tests/RTreeTest.cpp
@@ -7,21 +7,12 @@
 
 #include "SkRTree.h"
 #include "SkRandom.h"
-#include "SkTSort.h"
 #include "Test.h"
 
-static const size_t MIN_CHILDREN = 6;
-static const size_t MAX_CHILDREN = 11;
-
 static const int NUM_RECTS = 200;
 static const size_t NUM_ITERATIONS = 100;
 static const size_t NUM_QUERIES = 50;
 
-struct DataRect {
-    SkRect rect;
-    void* data;
-};
-
 static SkRect random_rect(SkRandom& rand) {
     SkRect rect = {0,0,0,0};
     while (rect.isEmpty()) {
@@ -34,121 +25,67 @@
     return rect;
 }
 
-static void random_data_rects(SkRandom& rand, DataRect out[], int n) {
-    for (int i = 0; i < n; ++i) {
-        out[i].rect = random_rect(rand);
-        out[i].data = reinterpret_cast<void*>(i);
-    }
-}
-
-static bool verify_query(SkRect query, DataRect rects[],
-                         SkTDArray<void*>& found) {
-    // TODO(mtklein): no need to do this after everything's SkRects
-    query.roundOut();
-
-    SkTDArray<void*> expected;
-
+static bool verify_query(SkRect query, SkRect rects[], SkTDArray<int>& found) {
+    SkTDArray<int> expected;
     // manually intersect with every rectangle
     for (int i = 0; i < NUM_RECTS; ++i) {
-        if (SkRect::Intersects(query, rects[i].rect)) {
-            expected.push(rects[i].data);
+        if (SkRect::Intersects(query, rects[i])) {
+            expected.push(i);
         }
     }
 
     if (expected.count() != found.count()) {
         return false;
     }
-
     if (0 == expected.count()) {
         return true;
     }
-
-    // Just cast to long since sorting by the value of the void*'s was being problematic...
-    SkTQSort(reinterpret_cast<long*>(expected.begin()),
-             reinterpret_cast<long*>(expected.end() - 1));
-    SkTQSort(reinterpret_cast<long*>(found.begin()),
-             reinterpret_cast<long*>(found.end() - 1));
     return found == expected;
 }
 
-static void run_queries(skiatest::Reporter* reporter, SkRandom& rand, DataRect rects[],
-                       SkRTree& tree) {
+static void run_queries(skiatest::Reporter* reporter, SkRandom& rand, SkRect rects[],
+                        const SkRTree& tree) {
     for (size_t i = 0; i < NUM_QUERIES; ++i) {
-        SkTDArray<void*> hits;
+        SkTDArray<int> hits;
         SkRect query = random_rect(rand);
         tree.search(query, &hits);
         REPORTER_ASSERT(reporter, verify_query(query, rects, hits));
     }
 }
 
-static void rtree_test_main(SkRTree* rtree, skiatest::Reporter* reporter) {
-    DataRect rects[NUM_RECTS];
-    SkRandom rand;
-    REPORTER_ASSERT(reporter, rtree);
-
+DEF_TEST(RTree, reporter) {
     int expectedDepthMin = -1;
-    int expectedDepthMax = -1;
-
     int tmp = NUM_RECTS;
     while (tmp > 0) {
-        tmp -= static_cast<int>(pow(static_cast<double>(MAX_CHILDREN),
-                                static_cast<double>(expectedDepthMin + 1)));
+        tmp -= static_cast<int>(pow(static_cast<double>(SkRTree::kMaxChildren),
+                                    static_cast<double>(expectedDepthMin + 1)));
         ++expectedDepthMin;
     }
 
+    int expectedDepthMax = -1;
     tmp = NUM_RECTS;
     while (tmp > 0) {
-        tmp -= static_cast<int>(pow(static_cast<double>(MIN_CHILDREN),
-                                static_cast<double>(expectedDepthMax + 1)));
+        tmp -= static_cast<int>(pow(static_cast<double>(SkRTree::kMinChildren),
+                                    static_cast<double>(expectedDepthMax + 1)));
         ++expectedDepthMax;
     }
 
+    SkRandom rand;
+    SkAutoTMalloc<SkRect> rects(NUM_RECTS);
     for (size_t i = 0; i < NUM_ITERATIONS; ++i) {
-        random_data_rects(rand, rects, NUM_RECTS);
+        SkRTree rtree;
+        REPORTER_ASSERT(reporter, 0 == rtree.getCount());
 
-        // First try bulk-loaded inserts
-        for (int i = 0; i < NUM_RECTS; ++i) {
-            rtree->insert(rects[i].data, rects[i].rect, true);
+        for (int j = 0; j < NUM_RECTS; j++) {
+            rects[j] = random_rect(rand);
         }
-        rtree->flushDeferredInserts();
-        run_queries(reporter, rand, rects, *rtree);
-        REPORTER_ASSERT(reporter, NUM_RECTS == rtree->getCount());
-        REPORTER_ASSERT(reporter, expectedDepthMin <= rtree->getDepth() &&
-                                  expectedDepthMax >= rtree->getDepth());
-        rtree->clear();
-        REPORTER_ASSERT(reporter, 0 == rtree->getCount());
 
-        // Then try immediate inserts
-        for (int i = 0; i < NUM_RECTS; ++i) {
-            rtree->insert(rects[i].data, rects[i].rect);
-        }
-        run_queries(reporter, rand, rects, *rtree);
-        REPORTER_ASSERT(reporter, NUM_RECTS == rtree->getCount());
-        REPORTER_ASSERT(reporter, expectedDepthMin <= rtree->getDepth() &&
-                                  expectedDepthMax >= rtree->getDepth());
-        rtree->clear();
-        REPORTER_ASSERT(reporter, 0 == rtree->getCount());
+        rtree.insert(rects.get(), NUM_RECTS);
+        SkASSERT(rects);  // SkRTree doesn't take ownership of rects.
 
-        // And for good measure try immediate inserts, but in reversed order
-        for (int i = NUM_RECTS - 1; i >= 0; --i) {
-            rtree->insert(rects[i].data, rects[i].rect);
-        }
-        run_queries(reporter, rand, rects, *rtree);
-        REPORTER_ASSERT(reporter, NUM_RECTS == rtree->getCount());
-        REPORTER_ASSERT(reporter, expectedDepthMin <= rtree->getDepth() &&
-                                  expectedDepthMax >= rtree->getDepth());
-        rtree->clear();
-        REPORTER_ASSERT(reporter, 0 == rtree->getCount());
+        run_queries(reporter, rand, rects, rtree);
+        REPORTER_ASSERT(reporter, NUM_RECTS == rtree.getCount());
+        REPORTER_ASSERT(reporter, expectedDepthMin <= rtree.getDepth() &&
+                                  expectedDepthMax >= rtree.getDepth());
     }
 }
-
-DEF_TEST(RTree, reporter) {
-    SkRTree* rtree = SkRTree::Create(MIN_CHILDREN, MAX_CHILDREN);
-    SkAutoUnref au(rtree);
-    rtree_test_main(rtree, reporter);
-
-    // Rtree that orders input rectangles on deferred insert.
-    SkRTree* unsortedRtree = SkRTree::Create(MIN_CHILDREN, MAX_CHILDREN, 1, false);
-    SkAutoUnref auo(unsortedRtree);
-    rtree_test_main(unsortedRtree, reporter);
-}
diff --git a/src/third_party/skia/tests/RandomTest.cpp b/src/third_party/skia/tests/RandomTest.cpp
index 8d93d87..448f073 100644
--- a/src/third_party/skia/tests/RandomTest.cpp
+++ b/src/third_party/skia/tests/RandomTest.cpp
@@ -119,7 +119,7 @@
 
     // now make some strings and track them
     for (int i = 0; i < kN; ++i) {
-        value <<= 1;
+        value = SkLeftShift(value, 1);
         unsigned int rnd = rand.nextU();
         value |= ((rnd >> shift) & 0x1);
 
diff --git a/src/third_party/skia/tests/ReadPixelsTest.cpp b/src/third_party/skia/tests/ReadPixelsTest.cpp
index 77aac1f..f426e5f 100644
--- a/src/third_party/skia/tests/ReadPixelsTest.cpp
+++ b/src/third_party/skia/tests/ReadPixelsTest.cpp
@@ -5,25 +5,30 @@
  * found in the LICENSE file.
  */
 
-#include "SkBitmapDevice.h"
 #include "SkCanvas.h"
 #include "SkColorPriv.h"
+#include "SkColorSpace_Base.h"
+#include "SkHalf.h"
+#include "SkImageInfoPriv.h"
 #include "SkMathPriv.h"
-#include "SkRegion.h"
 #include "SkSurface.h"
 #include "Test.h"
 
 #if SK_SUPPORT_GPU
+#include "GrContext.h"
 #include "GrContextFactory.h"
-#include "SkGpuDevice.h"
+#include "GrContextPriv.h"
+#include "SkGr.h"
 #endif
 
+#include <initializer_list>
+
 static const int DEV_W = 100, DEV_H = 100;
 static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
 static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1,
                                                 DEV_H * SK_Scalar1);
 
-static SkPMColor getCanvasColor(int x, int y) {
+static SkPMColor get_src_color(int x, int y) {
     SkASSERT(x >= 0 && x < DEV_W);
     SkASSERT(y >= 0 && y < DEV_H);
 
@@ -52,7 +57,7 @@
     return SkPremultiplyARGBInline(a, r, g, b);
 }
 
-static SkPMColor getBitmapColor(int x, int y, int w) {
+static SkPMColor get_dst_bmp_init_color(int x, int y, int w) {
     int n = y * w + x;
 
     U8CPU b = n & 0xff;
@@ -61,8 +66,8 @@
     return SkPackARGB32(0xff, r, g , b);
 }
 
-static SkPMColor convertToPMColor(SkColorType ct, SkAlphaType at, const uint32_t* addr,
-                                  bool* doUnpremul) {
+static SkPMColor convert_to_pmcolor(SkColorType ct, SkAlphaType at, const uint32_t* addr,
+                                    bool* doUnpremul) {
     *doUnpremul = (kUnpremul_SkAlphaType == at);
 
     const uint8_t* c = reinterpret_cast<const uint8_t*>(addr);
@@ -93,7 +98,7 @@
     return SkPackARGB32(a, r, g, b);
 }
 
-static void fillCanvas(SkCanvas* canvas) {
+static SkBitmap make_src_bitmap() {
     static SkBitmap bmp;
     if (bmp.isNull()) {
         bmp.allocN32Pixels(DEV_W, DEV_H);
@@ -101,34 +106,42 @@
         for (int y = 0; y < DEV_H; ++y) {
             for (int x = 0; x < DEV_W; ++x) {
                 SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel());
-                *pixel = getCanvasColor(x, y);
+                *pixel = get_src_color(x, y);
             }
         }
     }
+    return bmp;
+}
+
+static void fill_src_canvas(SkCanvas* canvas) {
     canvas->save();
     canvas->setMatrix(SkMatrix::I());
-    canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op);
+    canvas->clipRect(DEV_RECT_S, kReplace_SkClipOp);
     SkPaint paint;
-    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-    canvas->drawBitmap(bmp, 0, 0, &paint);
+    paint.setBlendMode(SkBlendMode::kSrc);
+    canvas->drawBitmap(make_src_bitmap(), 0, 0, &paint);
     canvas->restore();
 }
 
-static void fillBitmap(SkBitmap* bitmap) {
-    SkASSERT(bitmap->lockPixelsAreWritable());
-    SkAutoLockPixels alp(*bitmap);
+static void fill_dst_bmp_with_init_data(SkBitmap* bitmap) {
     int w = bitmap->width();
     int h = bitmap->height();
     intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels());
     for (int y = 0; y < h; ++y) {
         for (int x = 0; x < w; ++x) {
-            SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel());
-            *pixel = getBitmapColor(x, y, w);
+            SkPMColor initColor = get_dst_bmp_init_color(x, y, w);
+            if (kAlpha_8_SkColorType == bitmap->colorType()) {
+                uint8_t* alpha = reinterpret_cast<uint8_t*>(pixels + y * bitmap->rowBytes() + x);
+                *alpha = SkGetPackedA32(initColor);
+            } else {
+                SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel());
+                *pixel = initColor;
+            }
         }
     }
 }
 
-static bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
+static bool check_read_pixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
     if (!didPremulConversion) {
         return a == b;
     }
@@ -151,18 +164,17 @@
 // checks the bitmap contains correct pixels after the readPixels
 // if the bitmap was prefilled with pixels it checks that these weren't
 // overwritten in the area outside the readPixels.
-static bool checkRead(skiatest::Reporter* reporter,
-                      const SkBitmap& bitmap,
-                      int x, int y,
-                      bool checkCanvasPixels,
-                      bool checkBitmapPixels) {
-    SkASSERT(4 == bitmap.bytesPerPixel());
+static bool check_read(skiatest::Reporter* reporter,
+                       const SkBitmap& bitmap,
+                       int x, int y,
+                       bool checkCanvasPixels,
+                       bool checkBitmapPixels,
+                       SkColorType ct,
+                       SkAlphaType at) {
+    SkASSERT(ct == bitmap.colorType() && at == bitmap.alphaType());
     SkASSERT(!bitmap.isNull());
     SkASSERT(checkCanvasPixels || checkBitmapPixels);
 
-    const SkColorType ct = bitmap.colorType();
-    const SkAlphaType at = bitmap.alphaType();
-
     int bw = bitmap.width();
     int bh = bitmap.height();
 
@@ -171,7 +183,34 @@
     if (!clippedSrcRect.intersect(srcRect)) {
         clippedSrcRect.setEmpty();
     }
-    SkAutoLockPixels alp(bitmap);
+    if (kAlpha_8_SkColorType == ct) {
+        for (int by = 0; by < bh; ++by) {
+            for (int bx = 0; bx < bw; ++bx) {
+                int devx = bx + srcRect.fLeft;
+                int devy = by + srcRect.fTop;
+                const uint8_t* alpha = bitmap.getAddr8(bx, by);
+
+                if (clippedSrcRect.contains(devx, devy)) {
+                    if (checkCanvasPixels) {
+                        uint8_t canvasAlpha = SkGetPackedA32(get_src_color(devx, devy));
+                        if (canvasAlpha != *alpha) {
+                            ERRORF(reporter, "Expected readback alpha (%d, %d) value 0x%02x, got 0x%02x. ",
+                                   bx, by, canvasAlpha, *alpha);
+                            return false;
+                        }
+                    }
+                } else if (checkBitmapPixels) {
+                    uint32_t origDstAlpha = SkGetPackedA32(get_dst_bmp_init_color(bx, by, bw));
+                    if (origDstAlpha != *alpha) {
+                        ERRORF(reporter, "Expected clipped out area of readback to be unchanged. "
+                            "Expected 0x%02x, got 0x%02x", origDstAlpha, *alpha);
+                        return false;
+                    }
+                }
+            }
+        }
+        return true;
+    }
     for (int by = 0; by < bh; ++by) {
         for (int bx = 0; bx < bw; ++bx) {
             int devx = bx + srcRect.fLeft;
@@ -181,18 +220,20 @@
 
             if (clippedSrcRect.contains(devx, devy)) {
                 if (checkCanvasPixels) {
-                    SkPMColor canvasPixel = getCanvasColor(devx, devy);
+                    SkPMColor canvasPixel = get_src_color(devx, devy);
                     bool didPremul;
-                    SkPMColor pmPixel = convertToPMColor(ct, at, pixel, &didPremul);
-                    bool check;
-                    REPORTER_ASSERT(reporter, check = checkPixel(pmPixel, canvasPixel, didPremul));
-                    if (!check) {
+                    SkPMColor pmPixel = convert_to_pmcolor(ct, at, pixel, &didPremul);
+                    if (!check_read_pixel(pmPixel, canvasPixel, didPremul)) {
+                        ERRORF(reporter, "Expected readback pixel (%d, %d) value 0x%08x, got 0x%08x. "
+                               "Readback was unpremul: %d", bx, by, canvasPixel, pmPixel, didPremul);
                         return false;
                     }
                 }
             } else if (checkBitmapPixels) {
-                REPORTER_ASSERT(reporter, getBitmapColor(bx, by, bw) == *pixel);
-                if (getBitmapColor(bx, by, bw) != *pixel) {
+                uint32_t origDstPixel = get_dst_bmp_init_color(bx, by, bw);
+                if (origDstPixel != *pixel) {
+                    ERRORF(reporter, "Expected clipped out area of readback to be unchanged. "
+                           "Expected 0x%08x, got 0x%08x", origDstPixel, *pixel);
                     return false;
                 }
             }
@@ -204,11 +245,17 @@
 enum BitmapInit {
     kFirstBitmapInit = 0,
 
-    kNoPixels_BitmapInit = kFirstBitmapInit,
-    kTight_BitmapInit,
+    kTight_BitmapInit = kFirstBitmapInit,
     kRowBytes_BitmapInit,
+    kRowBytesOdd_BitmapInit,
 
-    kBitmapInitCnt
+    kLastAligned_BitmapInit = kRowBytes_BitmapInit,
+
+#if 0  // THIS CAUSES ERRORS ON WINDOWS AND SOME ANDROID DEVICES
+    kLast_BitmapInit = kRowBytesOdd_BitmapInit
+#else
+    kLast_BitmapInit = kLastAligned_BitmapInit
+#endif
 };
 
 static BitmapInit nextBMI(BitmapInit bmi) {
@@ -220,171 +267,345 @@
                         SkAlphaType at) {
     SkImageInfo info = SkImageInfo::Make(rect.width(), rect.height(), ct, at);
     size_t rowBytes = 0;
-    bool alloc = true;
     switch (init) {
-        case kNoPixels_BitmapInit:
-            alloc = false;
         case kTight_BitmapInit:
             break;
         case kRowBytes_BitmapInit:
-            rowBytes = (info.width() + 16) * sizeof(SkPMColor);
+            rowBytes = SkAlign4((info.width() + 16) * info.bytesPerPixel());
+            break;
+        case kRowBytesOdd_BitmapInit:
+            rowBytes = SkAlign4(info.width() * info.bytesPerPixel()) + 3;
             break;
         default:
             SkASSERT(0);
             break;
     }
+    bitmap->allocPixels(info, rowBytes);
+}
 
-    if (alloc) {
-        bitmap->allocPixels(info);
-    } else {
-        bitmap->setInfo(info, rowBytes);
+static const struct {
+    SkColorType fColorType;
+    SkAlphaType fAlphaType;
+} gReadPixelsConfigs[] = {
+    { kRGBA_8888_SkColorType,   kPremul_SkAlphaType },
+    { kRGBA_8888_SkColorType,   kUnpremul_SkAlphaType },
+    { kBGRA_8888_SkColorType,   kPremul_SkAlphaType },
+    { kBGRA_8888_SkColorType,   kUnpremul_SkAlphaType },
+    { kAlpha_8_SkColorType,     kPremul_SkAlphaType },
+};
+const SkIRect gReadPixelsTestRects[] = {
+    // entire thing
+    DEV_RECT,
+    // larger on all sides
+    SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10),
+    // fully contained
+    SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4),
+    // outside top left
+    SkIRect::MakeLTRB(-10, -10, -1, -1),
+    // touching top left corner
+    SkIRect::MakeLTRB(-10, -10, 0, 0),
+    // overlapping top left corner
+    SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4),
+    // overlapping top left and top right corners
+    SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, DEV_H / 4),
+    // touching entire top edge
+    SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, 0),
+    // overlapping top right corner
+    SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W  + 10, DEV_H / 4),
+    // contained in x, overlapping top edge
+    SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W  / 4, DEV_H / 4),
+    // outside top right corner
+    SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1),
+    // touching top right corner
+    SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0),
+    // overlapping top left and bottom left corners
+    SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10),
+    // touching entire left edge
+    SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10),
+    // overlapping bottom left corner
+    SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10),
+    // contained in y, overlapping left edge
+    SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4),
+    // outside bottom left corner
+    SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10),
+    // touching bottom left corner
+    SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10),
+    // overlapping bottom left and bottom right corners
+    SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
+    // touching entire left edge
+    SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10),
+    // overlapping bottom right corner
+    SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
+    // overlapping top right and bottom right corners
+    SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10),
+};
+
+static void test_readpixels(skiatest::Reporter* reporter, const sk_sp<SkSurface>& surface,
+                            BitmapInit lastBitmapInit) {
+    SkCanvas* canvas = surface->getCanvas();
+    fill_src_canvas(canvas);
+    for (size_t rect = 0; rect < SK_ARRAY_COUNT(gReadPixelsTestRects); ++rect) {
+        const SkIRect& srcRect = gReadPixelsTestRects[rect];
+        for (BitmapInit bmi = kFirstBitmapInit; bmi <= lastBitmapInit; bmi = nextBMI(bmi)) {
+            for (size_t c = 0; c < SK_ARRAY_COUNT(gReadPixelsConfigs); ++c) {
+                SkBitmap bmp;
+                init_bitmap(&bmp, srcRect, bmi,
+                            gReadPixelsConfigs[c].fColorType, gReadPixelsConfigs[c].fAlphaType);
+
+                // if the bitmap has pixels allocated before the readPixels,
+                // note that and fill them with pattern
+                bool startsWithPixels = !bmp.isNull();
+                if (startsWithPixels) {
+                    fill_dst_bmp_with_init_data(&bmp);
+                }
+                uint32_t idBefore = surface->generationID();
+                bool success = canvas->readPixels(bmp, srcRect.fLeft, srcRect.fTop);
+                uint32_t idAfter = surface->generationID();
+
+                // we expect to succeed when the read isn't fully clipped
+                // out.
+                bool expectSuccess = SkIRect::Intersects(srcRect, DEV_RECT);
+                // determine whether we expected the read to succeed.
+                REPORTER_ASSERT(reporter, success == expectSuccess);
+                // read pixels should never change the gen id
+                REPORTER_ASSERT(reporter, idBefore == idAfter);
+
+                if (success || startsWithPixels) {
+                    check_read(reporter, bmp, srcRect.fLeft, srcRect.fTop,
+                               success, startsWithPixels,
+                               gReadPixelsConfigs[c].fColorType, gReadPixelsConfigs[c].fAlphaType);
+                } else {
+                    // if we had no pixels beforehand and the readPixels
+                    // failed then our bitmap should still not have pixels
+                    REPORTER_ASSERT(reporter, bmp.isNull());
+                }
+            }
+        }
+    }
+}
+DEF_TEST(ReadPixels, reporter) {
+    const SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
+    auto surface(SkSurface::MakeRaster(info));
+    // SW readback fails a premul check when reading back to an unaligned rowbytes.
+    test_readpixels(reporter, surface, kLastAligned_BitmapInit);
+}
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadPixels_Gpu, reporter, ctxInfo) {
+    if (ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D9_ES2_ContextType ||
+        ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_GL_ES2_ContextType ||
+        ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D11_ES2_ContextType) {
+        // skbug.com/6742 ReadPixels_Texture & _Gpu don't work with ANGLE ES2 configs
+        return;
+    }
+
+    const SkImageInfo ii = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
+    for (auto& origin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) {
+        sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo,
+                                                             ii, 0, origin, nullptr));
+        test_readpixels(reporter, surface, kLast_BitmapInit);
+    }
+}
+#endif
+
+#if SK_SUPPORT_GPU
+static void test_readpixels_texture(skiatest::Reporter* reporter,
+                                    sk_sp<GrSurfaceContext> sContext) {
+    for (size_t rect = 0; rect < SK_ARRAY_COUNT(gReadPixelsTestRects); ++rect) {
+        const SkIRect& srcRect = gReadPixelsTestRects[rect];
+        for (BitmapInit bmi = kFirstBitmapInit; bmi <= kLast_BitmapInit; bmi = nextBMI(bmi)) {
+            for (size_t c = 0; c < SK_ARRAY_COUNT(gReadPixelsConfigs); ++c) {
+                SkBitmap bmp;
+                init_bitmap(&bmp, srcRect, bmi,
+                            gReadPixelsConfigs[c].fColorType, gReadPixelsConfigs[c].fAlphaType);
+
+                // if the bitmap has pixels allocated before the readPixels,
+                // note that and fill them with pattern
+                bool startsWithPixels = !bmp.isNull();
+                // Try doing the read directly from a non-renderable texture
+                if (startsWithPixels) {
+                    fill_dst_bmp_with_init_data(&bmp);
+                    uint32_t flags = 0;
+                    if (gReadPixelsConfigs[c].fAlphaType == kUnpremul_SkAlphaType) {
+                        flags = GrContextPriv::kUnpremul_PixelOpsFlag;
+                    }
+                    bool success = sContext->readPixels(bmp.info(), bmp.getPixels(),
+                                                        bmp.rowBytes(),
+                                                        srcRect.fLeft, srcRect.fTop, flags);
+                    check_read(reporter, bmp, srcRect.fLeft, srcRect.fTop,
+                               success, true,
+                               gReadPixelsConfigs[c].fColorType, gReadPixelsConfigs[c].fAlphaType);
+                }
+            }
+        }
     }
 }
 
-DEF_GPUTEST(ReadPixels, reporter, factory) {
-    const SkIRect testRects[] = {
-        // entire thing
-        DEV_RECT,
-        // larger on all sides
-        SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10),
-        // fully contained
-        SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4),
-        // outside top left
-        SkIRect::MakeLTRB(-10, -10, -1, -1),
-        // touching top left corner
-        SkIRect::MakeLTRB(-10, -10, 0, 0),
-        // overlapping top left corner
-        SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4),
-        // overlapping top left and top right corners
-        SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, DEV_H / 4),
-        // touching entire top edge
-        SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, 0),
-        // overlapping top right corner
-        SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W  + 10, DEV_H / 4),
-        // contained in x, overlapping top edge
-        SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W  / 4, DEV_H / 4),
-        // outside top right corner
-        SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1),
-        // touching top right corner
-        SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0),
-        // overlapping top left and bottom left corners
-        SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10),
-        // touching entire left edge
-        SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10),
-        // overlapping bottom left corner
-        SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10),
-        // contained in y, overlapping left edge
-        SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4),
-        // outside bottom left corner
-        SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10),
-        // touching bottom left corner
-        SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10),
-        // overlapping bottom left and bottom right corners
-        SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
-        // touching entire left edge
-        SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10),
-        // overlapping bottom right corner
-        SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
-        // overlapping top right and bottom right corners
-        SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10),
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadPixels_Texture, reporter, ctxInfo) {
+    if (ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D9_ES2_ContextType ||
+        ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_GL_ES2_ContextType ||
+        ctxInfo.type() == sk_gpu_test::GrContextFactory::kANGLE_D3D11_ES2_ContextType) {
+        // skbug.com/6742 ReadPixels_Texture & _Gpu don't work with ANGLE ES2 configs
+        return;
+    }
+
+    GrContext* context = ctxInfo.grContext();
+
+    SkBitmap bmp = make_src_bitmap();
+
+    // On the GPU we will also try reading back from a non-renderable texture.
+    for (auto origin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) {
+        for (auto flags : {kNone_GrSurfaceFlags, kRenderTarget_GrSurfaceFlag}) {
+            GrSurfaceDesc desc;
+            desc.fFlags = flags;
+            desc.fWidth = DEV_W;
+            desc.fHeight = DEV_H;
+            desc.fConfig = kSkia8888_GrPixelConfig;
+            desc.fOrigin = origin;
+
+            sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                       desc, SkBudgeted::kNo,
+                                                                       bmp.getPixels(),
+                                                                       bmp.rowBytes());
+
+            sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                                        std::move(proxy), nullptr);
+
+            test_readpixels_texture(reporter, std::move(sContext));
+        }
+    }
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static const uint32_t kNumPixels = 5;
+
+// The five reference pixels are: red, green, blue, white, black.
+// Five is an interesting number to test because we'll exercise a full 4-wide SIMD vector
+// plus a tail pixel.
+static const uint32_t rgba[kNumPixels] = {
+        0xFF0000FF, 0xFF00FF00, 0xFFFF0000, 0xFFFFFFFF, 0xFF000000
+};
+static const uint32_t bgra[kNumPixels] = {
+        0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFFFFFF, 0xFF000000
+};
+static const uint16_t rgb565[kNumPixels] = {
+        SK_R16_MASK_IN_PLACE, SK_G16_MASK_IN_PLACE, SK_B16_MASK_IN_PLACE, 0xFFFF, 0x0
+};
+
+static const uint16_t rgba4444[kNumPixels] = { 0xF00F, 0x0F0F, 0x00FF, 0xFFFF, 0x000F };
+
+static const uint64_t kRed      = (uint64_t) SK_Half1 <<  0;
+static const uint64_t kGreen    = (uint64_t) SK_Half1 << 16;
+static const uint64_t kBlue     = (uint64_t) SK_Half1 << 32;
+static const uint64_t kAlpha    = (uint64_t) SK_Half1 << 48;
+static const uint64_t f16[kNumPixels] = {
+        kAlpha | kRed, kAlpha | kGreen, kAlpha | kBlue, kAlpha | kBlue | kGreen | kRed, kAlpha
+};
+
+static const uint8_t alpha8[kNumPixels] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+static const uint8_t gray8[kNumPixels] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+static const void* five_reference_pixels(SkColorType colorType) {
+    switch (colorType) {
+        case kUnknown_SkColorType:
+            return nullptr;
+        case kAlpha_8_SkColorType:
+            return alpha8;
+        case kRGB_565_SkColorType:
+            return rgb565;
+        case kARGB_4444_SkColorType:
+            return rgba4444;
+        case kRGBA_8888_SkColorType:
+            return rgba;
+        case kBGRA_8888_SkColorType:
+            return bgra;
+        case kGray_8_SkColorType:
+            return gray8;
+        case kRGBA_F16_SkColorType:
+            return f16;
+        default:
+            return nullptr; // remove me when kIndex_8 is removed from the enum
+    }
+
+    SkASSERT(false);
+    return nullptr;
+}
+
+static void test_conversion(skiatest::Reporter* r, const SkImageInfo& dstInfo,
+                            const SkImageInfo& srcInfo) {
+    if (!SkImageInfoIsValidRenderingCS(srcInfo)) {
+        return;
+    }
+
+    const void* srcPixels = five_reference_pixels(srcInfo.colorType());
+    SkPixmap srcPixmap(srcInfo, srcPixels, srcInfo.minRowBytes());
+    sk_sp<SkImage> src = SkImage::MakeFromRaster(srcPixmap, nullptr, nullptr);
+    REPORTER_ASSERT(r, src);
+
+    // Enough space for 5 pixels when color type is F16, more than enough space in other cases.
+    uint64_t dstPixels[kNumPixels];
+    SkPixmap dstPixmap(dstInfo, dstPixels, dstInfo.minRowBytes());
+    bool success = src->readPixels(dstPixmap, 0, 0);
+    REPORTER_ASSERT(r, success == SkImageInfoValidConversion(dstInfo, srcInfo));
+
+    if (success) {
+        if (kGray_8_SkColorType == srcInfo.colorType() &&
+            kGray_8_SkColorType != dstInfo.colorType())
+        {
+            // This conversion is legal, but we won't get the "reference" pixels since we cannot
+            // represent colors in kGray8.
+            return;
+        }
+
+        REPORTER_ASSERT(r, 0 == memcmp(dstPixels, five_reference_pixels(dstInfo.colorType()),
+                                       kNumPixels * SkColorTypeBytesPerPixel(dstInfo.colorType())));
+
+    }
+}
+
+DEF_TEST(ReadPixels_ValidConversion, reporter) {
+    const SkColorType kColorTypes[] = {
+            kUnknown_SkColorType,
+            kAlpha_8_SkColorType,
+            kRGB_565_SkColorType,
+            kARGB_4444_SkColorType,
+            kRGBA_8888_SkColorType,
+            kBGRA_8888_SkColorType,
+            kGray_8_SkColorType,
+            kRGBA_F16_SkColorType,
     };
 
-    for (int dtype = 0; dtype < 3; ++dtype) {
-        int glCtxTypeCnt = 1;
-#if SK_SUPPORT_GPU
-        if (0 != dtype)  {
-            glCtxTypeCnt = GrContextFactory::kGLContextTypeCnt;
-        }
-#endif
-        const SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
-        for (int glCtxType = 0; glCtxType < glCtxTypeCnt; ++glCtxType) {
-            SkAutoTUnref<SkSurface> surface;
-            if (0 == dtype) {
-                surface.reset(SkSurface::NewRaster(info));
-            } else {
-#if SK_SUPPORT_GPU
-                GrContextFactory::GLContextType type =
-                    static_cast<GrContextFactory::GLContextType>(glCtxType);
-                if (!GrContextFactory::IsRenderingGLContext(type)) {
-                    continue;
-                }
-                GrContext* context = factory->get(type);
-                if (NULL == context) {
-                    continue;
-                }
-                GrTextureDesc desc;
-                desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
-                desc.fWidth = DEV_W;
-                desc.fHeight = DEV_H;
-                desc.fConfig = kSkia8888_GrPixelConfig;
-                desc.fOrigin = 1 == dtype ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
-                GrAutoScratchTexture ast(context, desc, GrContext::kExact_ScratchTexMatch);
-                SkAutoTUnref<GrTexture> tex(ast.detach());
-                surface.reset(SkSurface::NewRenderTargetDirect(tex->asRenderTarget()));
-#else
-                continue;
-#endif
-            }
-            SkCanvas& canvas = *surface->getCanvas();
-            fillCanvas(&canvas);
+    const SkAlphaType kAlphaTypes[] = {
+            kUnknown_SkAlphaType,
+            kOpaque_SkAlphaType,
+            kPremul_SkAlphaType,
+            kUnpremul_SkAlphaType,
+    };
 
-            static const struct {
-                SkColorType fColorType;
-                SkAlphaType fAlphaType;
-            } gReadConfigs[] = {
-                { kRGBA_8888_SkColorType,   kPremul_SkAlphaType },
-                { kRGBA_8888_SkColorType,   kUnpremul_SkAlphaType },
-                { kBGRA_8888_SkColorType,   kPremul_SkAlphaType },
-                { kBGRA_8888_SkColorType,   kUnpremul_SkAlphaType },
-            };
-            for (size_t rect = 0; rect < SK_ARRAY_COUNT(testRects); ++rect) {
-                const SkIRect& srcRect = testRects[rect];
-                for (BitmapInit bmi = kFirstBitmapInit; bmi < kBitmapInitCnt; bmi = nextBMI(bmi)) {
-                    for (size_t c = 0; c < SK_ARRAY_COUNT(gReadConfigs); ++c) {
-                        SkBitmap bmp;
-                        init_bitmap(&bmp, srcRect, bmi,
-                                    gReadConfigs[c].fColorType, gReadConfigs[c].fAlphaType);
+    const sk_sp<SkColorSpace> kColorSpaces[] = {
+            nullptr,
+            SkColorSpace::MakeSRGB(),
+    };
 
-                        // if the bitmap has pixels allocated before the readPixels,
-                        // note that and fill them with pattern
-                        bool startsWithPixels = !bmp.isNull();
-                        if (startsWithPixels) {
-                            fillBitmap(&bmp);
+    for (SkColorType dstCT : kColorTypes) {
+        for (SkAlphaType dstAT: kAlphaTypes) {
+            for (sk_sp<SkColorSpace> dstCS : kColorSpaces) {
+                for (SkColorType srcCT : kColorTypes) {
+                    for (SkAlphaType srcAT: kAlphaTypes) {
+                        for (sk_sp<SkColorSpace> srcCS : kColorSpaces) {
+                            if (kRGBA_F16_SkColorType == dstCT && dstCS) {
+                                dstCS = as_CSB(dstCS)->makeLinearGamma();
+                            }
+
+                            if (kRGBA_F16_SkColorType == srcCT && srcCS) {
+                                srcCS = as_CSB(srcCS)->makeLinearGamma();
+                            }
+
+                            test_conversion(reporter,
+                                            SkImageInfo::Make(kNumPixels, 1, dstCT, dstAT, dstCS),
+                                            SkImageInfo::Make(kNumPixels, 1, srcCT, srcAT, srcCS));
                         }
-                        uint32_t idBefore = surface->generationID();
-                        bool success = canvas.readPixels(&bmp, srcRect.fLeft, srcRect.fTop);
-                        uint32_t idAfter = surface->generationID();
-
-                        // we expect to succeed when the read isn't fully clipped
-                        // out.
-                        bool expectSuccess = SkIRect::Intersects(srcRect, DEV_RECT);
-                        // determine whether we expected the read to succeed.
-                        REPORTER_ASSERT(reporter, success == expectSuccess);
-                        // read pixels should never change the gen id
-                        REPORTER_ASSERT(reporter, idBefore == idAfter);
-
-                        if (success || startsWithPixels) {
-                            checkRead(reporter, bmp, srcRect.fLeft, srcRect.fTop,
-                                      success, startsWithPixels);
-                        } else {
-                            // if we had no pixels beforehand and the readPixels
-                            // failed then our bitmap should still not have pixels
-                            REPORTER_ASSERT(reporter, bmp.isNull());
-                        }
-                    }
-                    // check the old webkit version of readPixels that clips the
-                    // bitmap size
-                    SkBitmap wkbmp;
-                    bool success = canvas.readPixels(srcRect, &wkbmp);
-                    SkIRect clippedRect = DEV_RECT;
-                    if (clippedRect.intersect(srcRect)) {
-                        REPORTER_ASSERT(reporter, success);
-                        REPORTER_ASSERT(reporter, kN32_SkColorType == wkbmp.colorType());
-                        REPORTER_ASSERT(reporter, kPremul_SkAlphaType == wkbmp.alphaType());
-                        checkRead(reporter, wkbmp, clippedRect.fLeft,
-                                  clippedRect.fTop, true, false);
-                    } else {
-                        REPORTER_ASSERT(reporter, !success);
                     }
                 }
             }
diff --git a/src/third_party/skia/tests/ReadWriteAlphaTest.cpp b/src/third_party/skia/tests/ReadWriteAlphaTest.cpp
index 385a17e..344400c 100644
--- a/src/third_party/skia/tests/ReadWriteAlphaTest.cpp
+++ b/src/third_party/skia/tests/ReadWriteAlphaTest.cpp
@@ -5,108 +5,197 @@
  * found in the LICENSE file.
  */
 
-// This test is specific to the GPU backend.
-#if SK_SUPPORT_GPU && !defined(SK_BUILD_FOR_ANDROID)
-
-#include "GrContextFactory.h"
-#include "SkGpuDevice.h"
 #include "Test.h"
 
-static const int X_SIZE = 12;
-static const int Y_SIZE = 12;
+// This test is specific to the GPU backend.
+#if SK_SUPPORT_GPU
 
-DEF_GPUTEST(ReadWriteAlpha, reporter, factory) {
-    for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
-        GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type);
-        if (!GrContextFactory::IsRenderingGLContext(glType)) {
-            continue;
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrResourceProvider.h"
+#include "GrSurfaceContext.h"
+#include "GrSurfaceProxy.h"
+#include "GrTextureProxy.h"
+#include "SkCanvas.h"
+#include "SkSurface.h"
+
+// This was made indivisible by 4 to ensure we test setting GL_PACK_ALIGNMENT properly.
+static const int X_SIZE = 13;
+static const int Y_SIZE = 13;
+
+static void validate_alpha_data(skiatest::Reporter* reporter, int w, int h, const uint8_t* actual,
+                                size_t actualRowBytes, const uint8_t* expected, SkString extraMsg) {
+    for (int y = 0; y < h; ++y) {
+        for (int x = 0; x < w; ++x) {
+            uint8_t a = actual[y * actualRowBytes + x];
+            uint8_t e = expected[y * w + x];
+            if (e != a) {
+                ERRORF(reporter,
+                       "Failed alpha readback. Expected: 0x%02x, Got: 0x%02x at (%d,%d), %s",
+                       e, a, x, y, extraMsg.c_str());
+                return;
+            }
         }
-        GrContext* context = factory->get(glType);
-        if (NULL == context) {
-            continue;
-        }
+    }
+}
 
-        unsigned char textureData[X_SIZE][Y_SIZE];
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadWriteAlpha, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    unsigned char alphaData[X_SIZE * Y_SIZE];
 
-        memset(textureData, 0, X_SIZE * Y_SIZE);
+    static const int kClearValue = 0x2;
 
-        GrTextureDesc desc;
-
-        // let Skia know we will be using this texture as a render target
-        desc.fFlags     = kRenderTarget_GrTextureFlagBit;
-        // it is a single channel texture
-        desc.fConfig    = kAlpha_8_GrPixelConfig;
+    bool match;
+    static const size_t kRowBytes[] = {0, X_SIZE, X_SIZE + 1, 2 * X_SIZE - 1};
+    {
+        GrSurfaceDesc desc;
+        desc.fFlags     = kNone_GrSurfaceFlags;
+        desc.fConfig    = kAlpha_8_GrPixelConfig;    // it is a single channel texture
         desc.fWidth     = X_SIZE;
         desc.fHeight    = Y_SIZE;
 
         // We are initializing the texture with zeros here
-        GrTexture* texture = context->createUncachedTexture(desc, textureData, 0);
-        if (!texture) {
+        memset(alphaData, 0, X_SIZE * Y_SIZE);
+
+        sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                 desc,
+                                                                 SkBudgeted::kNo,
+                                                                 alphaData, 0));
+        if (!proxy) {
+            ERRORF(reporter, "Could not create alpha texture.");
             return;
         }
+        sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext(
+                                                                  std::move(proxy), nullptr));
 
-        SkAutoTUnref<GrTexture> au(texture);
+        const SkImageInfo ii = SkImageInfo::MakeA8(X_SIZE, Y_SIZE);
+        sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii));
 
         // create a distinctive texture
         for (int y = 0; y < Y_SIZE; ++y) {
             for (int x = 0; x < X_SIZE; ++x) {
-                textureData[x][y] = x*Y_SIZE+y;
+                alphaData[y * X_SIZE + x] = y*X_SIZE+x;
             }
         }
 
-        // upload the texture
-        texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
-                             textureData, 0);
+        for (auto rowBytes : kRowBytes) {
 
-        unsigned char readback[X_SIZE][Y_SIZE];
+            // upload the texture (do per-rowbytes iteration because we may overwrite below).
+            bool result = sContext->writePixels(ii, alphaData, 0, 0, 0);
+            REPORTER_ASSERT_MESSAGE(reporter, result, "Initial A8 writePixels failed");
 
-        // clear readback to something non-zero so we can detect readback failures
-        memset(readback, 0x1, X_SIZE * Y_SIZE);
+            size_t nonZeroRowBytes = rowBytes ? rowBytes : X_SIZE;
+            std::unique_ptr<uint8_t[]> readback(new uint8_t[nonZeroRowBytes * Y_SIZE]);
+            // clear readback to something non-zero so we can detect readback failures
+            memset(readback.get(), kClearValue, nonZeroRowBytes * Y_SIZE);
 
-        // read the texture back
-        texture->readPixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
-                            readback, 0);
+            // read the texture back
+            result = sContext->readPixels(ii, readback.get(), rowBytes, 0, 0);
+            REPORTER_ASSERT_MESSAGE(reporter, result, "Initial A8 readPixels failed");
 
-        // make sure the original & read back versions match
-        bool match = true;
+            // make sure the original & read back versions match
+            SkString msg;
+            msg.printf("rb:%d A8", SkToU32(rowBytes));
+            validate_alpha_data(reporter, X_SIZE, Y_SIZE, readback.get(), nonZeroRowBytes,
+                                alphaData, msg);
 
-        for (int y = 0; y < Y_SIZE; ++y) {
-            for (int x = 0; x < X_SIZE; ++x) {
-                if (textureData[x][y] != readback[x][y]) {
-                    match = false;
+            // Now try writing to a single channel surface (if we could create one).
+            if (surf) {
+                SkCanvas* canvas = surf->getCanvas();
+
+                SkPaint paint;
+
+                const SkRect rect = SkRect::MakeLTRB(-10, -10, X_SIZE + 10, Y_SIZE + 10);
+
+                paint.setColor(SK_ColorWHITE);
+
+                canvas->drawRect(rect, paint);
+
+                memset(readback.get(), kClearValue, nonZeroRowBytes * Y_SIZE);
+                result = surf->readPixels(ii, readback.get(), nonZeroRowBytes, 0, 0);
+                REPORTER_ASSERT_MESSAGE(reporter, result, "A8 readPixels after clear failed");
+
+                match = true;
+                for (int y = 0; y < Y_SIZE && match; ++y) {
+                    for (int x = 0; x < X_SIZE && match; ++x) {
+                        uint8_t rbValue = readback.get()[y * nonZeroRowBytes + x];
+                        if (0xFF != rbValue) {
+                            ERRORF(reporter,
+                                   "Failed alpha readback after clear. Expected: 0xFF, Got: 0x%02x"
+                                   " at (%d,%d), rb:%d", rbValue, x, y, SkToU32(rowBytes));
+                            match = false;
+                        }
+                    }
                 }
             }
         }
+    }
 
-        REPORTER_ASSERT(reporter, match);
+    static const GrPixelConfig kRGBAConfigs[] {
+        kRGBA_8888_GrPixelConfig,
+        kBGRA_8888_GrPixelConfig,
+        kSRGBA_8888_GrPixelConfig
+    };
 
-        // Now try writing on the single channel texture
-        SkAutoTUnref<SkBaseDevice> device(SkGpuDevice::Create(texture->asRenderTarget(),
-                                      SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType)));
-        SkCanvas canvas(device);
+    for (int y = 0; y < Y_SIZE; ++y) {
+        for (int x = 0; x < X_SIZE; ++x) {
+            alphaData[y * X_SIZE + x] = y*X_SIZE+x;
+        }
+    }
 
-        SkPaint paint;
+    const SkImageInfo dstInfo = SkImageInfo::Make(X_SIZE, Y_SIZE,
+                                                  kAlpha_8_SkColorType,
+                                                  kPremul_SkAlphaType);
 
-        const SkRect rect = SkRect::MakeLTRB(-10, -10, X_SIZE + 10, Y_SIZE + 10);
+    // Attempt to read back just alpha from a RGBA/BGRA texture. Once with a texture-only src and
+    // once with a render target.
+    for (auto config : kRGBAConfigs) {
+        for (int rt = 0; rt < 2; ++rt) {
+            GrSurfaceDesc desc;
+            desc.fFlags     = rt ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
+            desc.fConfig    = config;
+            desc.fWidth     = X_SIZE;
+            desc.fHeight    = Y_SIZE;
 
-        paint.setColor(SK_ColorWHITE);
-
-        canvas.drawRect(rect, paint);
-
-        texture->readPixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
-                            readback, 0);
-
-        match = true;
-
-        for (int y = 0; y < Y_SIZE; ++y) {
-            for (int x = 0; x < X_SIZE; ++x) {
-                if (0xFF != readback[x][y]) {
-                    match = false;
+            uint32_t rgbaData[X_SIZE * Y_SIZE];
+            // Make the alpha channel of the rgba texture come from alphaData.
+            for (int y = 0; y < Y_SIZE; ++y) {
+                for (int x = 0; x < X_SIZE; ++x) {
+                    rgbaData[y * X_SIZE + x] = GrColorPackRGBA(6, 7, 8, alphaData[y * X_SIZE + x]);
                 }
             }
-        }
+            sk_sp<GrTextureProxy> proxy =
+                GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kNo,
+                                             rgbaData, 0);
+            if (!proxy) {
+                // We always expect to be able to create a RGBA texture
+                if (!rt  && kRGBA_8888_GrPixelConfig == desc.fConfig) {
+                    ERRORF(reporter, "Failed to create RGBA texture.");
+                }
+                continue;
+            }
 
-        REPORTER_ASSERT(reporter, match);
+            sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                                       std::move(proxy), nullptr);
+
+            for (auto rowBytes : kRowBytes) {
+                size_t nonZeroRowBytes = rowBytes ? rowBytes : X_SIZE;
+
+                std::unique_ptr<uint8_t[]> readback(new uint8_t[nonZeroRowBytes * Y_SIZE]);
+                // Clear so we don't accidentally see values from previous iteration.
+                memset(readback.get(), kClearValue, nonZeroRowBytes * Y_SIZE);
+
+                // read the texture back
+                bool result = sContext->readPixels(dstInfo, readback.get(), rowBytes, 0, 0);
+                REPORTER_ASSERT_MESSAGE(reporter, result, "8888 readPixels failed");
+
+                // make sure the original & read back versions match
+                SkString msg;
+                msg.printf("rt:%d, rb:%d 8888", rt, SkToU32(rowBytes));
+                validate_alpha_data(reporter, X_SIZE, Y_SIZE, readback.get(), nonZeroRowBytes,
+                                    alphaData, msg);
+            }
+        }
     }
 }
 
diff --git a/src/third_party/skia/tests/Reader32Test.cpp b/src/third_party/skia/tests/Reader32Test.cpp
index cfa08b3..301f67c 100644
--- a/src/third_party/skia/tests/Reader32Test.cpp
+++ b/src/third_party/skia/tests/Reader32Test.cpp
@@ -35,8 +35,8 @@
 DEF_TEST(Reader32, reporter) {
     SkReader32 reader;
     assert_empty(reporter, reader);
-    REPORTER_ASSERT(reporter, NULL == reader.base());
-    REPORTER_ASSERT(reporter, NULL == reader.peek());
+    REPORTER_ASSERT(reporter, nullptr == reader.base());
+    REPORTER_ASSERT(reporter, nullptr == reader.peek());
 
     size_t i;
 
@@ -74,8 +74,17 @@
     reader.read(buffer, sizeof(data2));
     REPORTER_ASSERT(reporter, !memcmp(data2, buffer, sizeof(data2)));
 
-    reader.setMemory(NULL, 0);
+    reader.setMemory(nullptr, 0);
     assert_empty(reporter, reader);
-    REPORTER_ASSERT(reporter, NULL == reader.base());
-    REPORTER_ASSERT(reporter, NULL == reader.peek());
+    REPORTER_ASSERT(reporter, nullptr == reader.base());
+    REPORTER_ASSERT(reporter, nullptr == reader.peek());
+
+    // need to handle read(null, 0) and not get undefined behavior from memcpy
+    {
+        char storage[100];
+        reader.setMemory(storage, sizeof(storage));
+        char buffer[10];
+        reader.read(buffer, 0);     // easy case, since we pass a ptr
+        reader.read(nullptr, 0);    // undef case, read() can't blindly call memcpy
+    }
 }
diff --git a/src/third_party/skia/tests/RecordDrawTest.cpp b/src/third_party/skia/tests/RecordDrawTest.cpp
index 33efbd8..1dbd945 100644
--- a/src/third_party/skia/tests/RecordDrawTest.cpp
+++ b/src/third_party/skia/tests/RecordDrawTest.cpp
@@ -9,25 +9,53 @@
 #include "RecordTestUtils.h"
 
 #include "SkDebugCanvas.h"
-#include "SkDrawPictureCallback.h"
 #include "SkDropShadowImageFilter.h"
+#include "SkImagePriv.h"
 #include "SkRecord.h"
 #include "SkRecordDraw.h"
 #include "SkRecordOpts.h"
 #include "SkRecorder.h"
 #include "SkRecords.h"
+#include "SkSurface.h"
 
 static const int W = 1920, H = 1080;
 
-class JustOneDraw : public SkDrawPictureCallback {
+class JustOneDraw : public SkPicture::AbortCallback {
 public:
     JustOneDraw() : fCalls(0) {}
 
-    virtual bool abortDrawing() SK_OVERRIDE { return fCalls++ > 0; }
+    bool abort() override { return fCalls++ > 0; }
 private:
     int fCalls;
 };
 
+DEF_TEST(RecordDraw_LazySaves, r) {
+    // Record two commands.
+    SkRecord record;
+    SkRecorder recorder(&record, W, H);
+
+    REPORTER_ASSERT(r, 0 == record.count());
+    recorder.save();
+    REPORTER_ASSERT(r, 0 == record.count());    // the save was not recorded (yet)
+    recorder.drawColor(SK_ColorRED);
+    REPORTER_ASSERT(r, 1 == record.count());
+    recorder.scale(2, 2);
+    REPORTER_ASSERT(r, 3 == record.count());    // now we see the save
+    recorder.restore();
+    REPORTER_ASSERT(r, 4 == record.count());
+
+    assert_type<SkRecords::DrawPaint>(r, record, 0);
+    assert_type<SkRecords::Save>     (r, record, 1);
+    assert_type<SkRecords::Concat>   (r, record, 2);
+    assert_type<SkRecords::Restore>  (r, record, 3);
+
+    recorder.save();
+    recorder.save();
+    recorder.restore();
+    recorder.restore();
+    REPORTER_ASSERT(r, 4 == record.count());
+}
+
 DEF_TEST(RecordDraw_Abort, r) {
     // Record two commands.
     SkRecord record;
@@ -39,28 +67,25 @@
     SkRecorder canvas(&rerecord, W, H);
 
     JustOneDraw callback;
-    SkRecordDraw(record, &canvas, NULL/*bbh*/, &callback);
+    SkRecordDraw(record, &canvas, nullptr, nullptr, 0, nullptr/*bbh*/, &callback);
 
-    REPORTER_ASSERT(r, 3 == rerecord.count());
-    assert_type<SkRecords::Save>    (r, rerecord, 0);
-    assert_type<SkRecords::DrawRect>(r, rerecord, 1);
-    assert_type<SkRecords::Restore> (r, rerecord, 2);
+    REPORTER_ASSERT(r, 1 == count_instances_of_type<SkRecords::DrawRect>(rerecord));
+    REPORTER_ASSERT(r, 0 == count_instances_of_type<SkRecords::ClipRect>(rerecord));
 }
 
 DEF_TEST(RecordDraw_Unbalanced, r) {
     SkRecord record;
     SkRecorder recorder(&record, W, H);
     recorder.save();  // We won't balance this, but SkRecordDraw will for us.
+    recorder.scale(2, 2);
 
     SkRecord rerecord;
     SkRecorder canvas(&rerecord, W, H);
-    SkRecordDraw(record, &canvas, NULL/*bbh*/, NULL/*callback*/);
+    SkRecordDraw(record, &canvas, nullptr, nullptr, 0, nullptr/*bbh*/, nullptr/*callback*/);
 
-    REPORTER_ASSERT(r, 4 == rerecord.count());
-    assert_type<SkRecords::Save>    (r, rerecord, 0);
-    assert_type<SkRecords::Save>    (r, rerecord, 1);
-    assert_type<SkRecords::Restore> (r, rerecord, 2);
-    assert_type<SkRecords::Restore> (r, rerecord, 3);
+    int save_count = count_instances_of_type<SkRecords::Save>(rerecord);
+    int restore_count = count_instances_of_type<SkRecords::Save>(rerecord);
+    REPORTER_ASSERT(r, save_count == restore_count);
 }
 
 DEF_TEST(RecordDraw_SetMatrixClobber, r) {
@@ -78,7 +103,7 @@
     translate.setTranslate(20, 20);
     translateCanvas.setMatrix(translate);
 
-    SkRecordDraw(scaleRecord, &translateCanvas, NULL/*bbh*/, NULL/*callback*/);
+    SkRecordDraw(scaleRecord, &translateCanvas, nullptr, nullptr, 0, nullptr/*bbh*/, nullptr/*callback*/);
     REPORTER_ASSERT(r, 4 == translateRecord.count());
     assert_type<SkRecords::SetMatrix>(r, translateRecord, 0);
     assert_type<SkRecords::Save>     (r, translateRecord, 1);
@@ -97,27 +122,6 @@
     REPORTER_ASSERT(r, setMatrix->matrix == expected);
 }
 
-struct TestBBH : public SkBBoxHierarchy {
-    virtual void insert(void* data, const SkRect& bounds, bool defer) SK_OVERRIDE {
-        Entry e = { (uintptr_t)data, bounds };
-        entries.push(e);
-    }
-    virtual int getCount() const SK_OVERRIDE { return entries.count(); }
-
-    virtual void flushDeferredInserts() SK_OVERRIDE {}
-
-    virtual void search(const SkRect& query, SkTDArray<void*>* results) const SK_OVERRIDE {}
-    virtual void clear() SK_OVERRIDE {}
-    virtual void rewindInserts() SK_OVERRIDE {}
-    virtual int getDepth() const SK_OVERRIDE { return -1; }
-
-    struct Entry {
-        uintptr_t data;
-        SkRect bounds;
-    };
-    SkTDArray<Entry> entries;
-};
-
 // Like a==b, with a little slop recognizing that float equality can be weird.
 static bool sloppy_rect_eq(SkRect a, SkRect b) {
     SkRect inset(a), outset(a);
@@ -126,9 +130,7 @@
     return outset.contains(b) && !inset.contains(b);
 }
 
-// This test is not meant to make total sense yet.  It's testing the status quo
-// of SkRecordFillBounds(), which itself doesn't make total sense yet.
-DEF_TEST(RecordDraw_BBH, r) {
+DEF_TEST(RecordDraw_BasicBounds, r) {
     SkRecord record;
     SkRecorder recorder(&record, W, H);
     recorder.save();
@@ -137,14 +139,11 @@
         recorder.drawRect(SkRect::MakeWH(320, 240), SkPaint());
     recorder.restore();
 
-    TestBBH bbh;
-    SkRecordFillBounds(record, &bbh);
+    SkAutoTMalloc<SkRect> bounds(record.count());
+    SkRecordFillBounds(SkRect::MakeWH(SkIntToScalar(W), SkIntToScalar(H)), record, bounds);
 
-    REPORTER_ASSERT(r, bbh.entries.count() == 5);
-    for (int i = 0; i < bbh.entries.count(); i++) {
-        REPORTER_ASSERT(r, bbh.entries[i].data == (uintptr_t)i);
-
-        REPORTER_ASSERT(r, sloppy_rect_eq(SkRect::MakeWH(400, 480), bbh.entries[i].bounds));
+    for (int i = 0; i < record.count(); i++) {
+        REPORTER_ASSERT(r, sloppy_rect_eq(SkRect::MakeWH(400, 480), bounds[i]));
     }
 }
 
@@ -163,15 +162,14 @@
     const SkPoint pos[] = { {40, 50}, {60, 70} };
     recorder.drawPosText(text, bytes, pos, SkPaint());
 
-    TestBBH bbh;
-    SkRecordFillBounds(record, &bbh);
-    REPORTER_ASSERT(r, bbh.entries.count() == 2);
+    SkAutoTMalloc<SkRect> bounds(record.count());
+    SkRecordFillBounds(SkRect::MakeWH(SkIntToScalar(W), SkIntToScalar(H)), record, bounds);
 
     // We can make these next assertions confidently because SkRecordFillBounds
     // builds its bounds by overestimating font metrics in a platform-independent way.
     // If that changes, these tests will need to be more flexible.
-    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[0].bounds, SkRect::MakeLTRB(-86,  6, 116, 54)));
-    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[1].bounds, SkRect::MakeLTRB(-56, 26, 156, 94)));
+    REPORTER_ASSERT(r, sloppy_rect_eq(bounds[0], SkRect::MakeLTRB(0,  0, 140, 60)));
+    REPORTER_ASSERT(r, sloppy_rect_eq(bounds[1], SkRect::MakeLTRB(0, 20, 180, 100)));
 }
 
 // Base test to ensure start/stop range is respected
@@ -191,42 +189,15 @@
 
     SkRecord rerecord;
     SkRecorder canvas(&rerecord, kWidth, kHeight);
-    SkRecordPartialDraw(record, &canvas, r1, 1, 2, SkMatrix::I()); // replay just drawRect of r2
+    SkRecordPartialDraw(record, &canvas, nullptr, 0, 1, 2, SkMatrix::I()); // replay just drawRect of r2
 
-    REPORTER_ASSERT(r, 3 == rerecord.count());
-    assert_type<SkRecords::Save>     (r, rerecord, 0);
-    assert_type<SkRecords::DrawRect> (r, rerecord, 1);
-    assert_type<SkRecords::Restore>  (r, rerecord, 2);
-
-    const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, rerecord, 1);
+    REPORTER_ASSERT(r, 1 == count_instances_of_type<SkRecords::DrawRect>(rerecord));
+    int index = find_first_instances_of_type<SkRecords::DrawRect>(rerecord);
+    const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, rerecord, index);
     REPORTER_ASSERT(r, drawRect->rect == r2);
 }
 
-// Check that clears are converted to drawRects
-DEF_TEST(RecordDraw_PartialClear, r) {
-    static const int kWidth = 10, kHeight = 10;
-
-    SkRect rect = { 0, 0, kWidth, kHeight };
-
-    SkRecord record;
-    SkRecorder recorder(&record, kWidth, kHeight);
-    recorder.clear(SK_ColorRED);
-
-    SkRecord rerecord;
-    SkRecorder canvas(&rerecord, kWidth, kHeight);
-    SkRecordPartialDraw(record, &canvas, rect, 0, 1, SkMatrix::I()); // replay just the clear
-
-    REPORTER_ASSERT(r, 3 == rerecord.count());
-    assert_type<SkRecords::Save>    (r, rerecord, 0);
-    assert_type<SkRecords::DrawRect>(r, rerecord, 1);
-    assert_type<SkRecords::Restore> (r, rerecord, 2);
-
-    const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, rerecord, 1);
-    REPORTER_ASSERT(r, drawRect->rect == rect);
-    REPORTER_ASSERT(r, drawRect->paint.getColor() == SK_ColorRED);
-}
-
-// A regression test for crbug.com/415468 and skbug.com/2957.
+// A regression test for crbug.com/415468 and https://bug.skia.org/2957 .
 //
 // This also now serves as a regression test for crbug.com/418417.  We used to adjust the
 // bounds for the saveLayer, clip, and restore to be greater than the bounds of the picture.
@@ -238,9 +209,12 @@
     // We draw a rectangle with a long drop shadow.  We used to not update the clip
     // bounds based on SaveLayer paints, so the drop shadow could be cut off.
     SkPaint paint;
-    paint.setImageFilter(SkDropShadowImageFilter::Create(20, 0, 0, 0, SK_ColorBLACK))->unref();
+    paint.setImageFilter(SkDropShadowImageFilter::Make(
+                                 20, 0, 0, 0, SK_ColorBLACK,
+                                 SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
+                                 nullptr));
 
-    recorder.saveLayer(NULL, &paint);
+    recorder.saveLayer(nullptr, &paint);
         recorder.clipRect(SkRect::MakeWH(20, 40));
         recorder.drawRect(SkRect::MakeWH(20, 40), SkPaint());
     recorder.restore();
@@ -250,11 +224,84 @@
     //
     // The second bug showed up as adjusting the picture bounds (0,0,50,50) by the drop shadow too.
     // The saveLayer, clipRect, and restore bounds were incorrectly (0,0,70,50).
-    TestBBH bbh;
-    SkRecordFillBounds(record, &bbh);
-    REPORTER_ASSERT(r, bbh.entries.count() == 4);
-    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[0].bounds, SkRect::MakeLTRB(0, 0, 50, 50)));
-    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[1].bounds, SkRect::MakeLTRB(0, 0, 50, 50)));
-    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[2].bounds, SkRect::MakeLTRB(0, 0, 40, 40)));
-    REPORTER_ASSERT(r, sloppy_rect_eq(bbh.entries[3].bounds, SkRect::MakeLTRB(0, 0, 50, 50)));
+    SkAutoTMalloc<SkRect> bounds(record.count());
+    SkRecordFillBounds(SkRect::MakeWH(50, 50), record, bounds);
+    REPORTER_ASSERT(r, sloppy_rect_eq(bounds[0], SkRect::MakeLTRB(0, 0, 50, 50)));
+    REPORTER_ASSERT(r, sloppy_rect_eq(bounds[1], SkRect::MakeLTRB(0, 0, 50, 50)));
+    REPORTER_ASSERT(r, sloppy_rect_eq(bounds[2], SkRect::MakeLTRB(0, 0, 40, 40)));
+    REPORTER_ASSERT(r, sloppy_rect_eq(bounds[3], SkRect::MakeLTRB(0, 0, 50, 50)));
+}
+
+// When a saveLayer provides an explicit bound and has a complex paint (e.g., one that
+// affects transparent black), that bound should serve to shrink the area of the required
+// backing store.
+DEF_TEST(RecordDraw_SaveLayerBoundsAffectsClipBounds, r) {
+    SkRecord record;
+    SkRecorder recorder(&record, 50, 50);
+
+    SkPaint p;
+    p.setBlendMode(SkBlendMode::kSrc);
+
+    SkRect layerBounds = SkRect::MakeLTRB(10, 10, 40, 40);
+    recorder.saveLayer(&layerBounds, &p);
+    recorder.drawRect(SkRect::MakeLTRB(20, 20, 30, 30), SkPaint());
+    recorder.restore();
+
+    SkAutoTMalloc<SkRect> bounds(record.count());
+    SkRecordFillBounds(SkRect::MakeWH(50, 50), record, bounds);
+    if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) {
+        REPORTER_ASSERT(r, sloppy_rect_eq(bounds[0], SkRect::MakeLTRB(10, 10, 40, 40)));
+        REPORTER_ASSERT(r, sloppy_rect_eq(bounds[1], SkRect::MakeLTRB(20, 20, 30, 30)));
+        REPORTER_ASSERT(r, sloppy_rect_eq(bounds[2], SkRect::MakeLTRB(10, 10, 40, 40)));
+    }
+}
+
+DEF_TEST(RecordDraw_drawImage, r){
+    class SkCanvasMock : public SkCanvas {
+    public:
+        SkCanvasMock(int width, int height) : SkCanvas(width, height) {
+            this->resetTestValues();
+        }
+
+        void onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
+                         const SkPaint* paint) override {
+            fDrawImageCalled = true;
+        }
+
+        void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+                             const SkPaint* paint, SrcRectConstraint) override {
+            fDrawImageRectCalled = true;
+        }
+
+        void resetTestValues() {
+            fDrawImageCalled = fDrawImageRectCalled = false;
+        }
+
+        bool fDrawImageCalled;
+        bool fDrawImageRectCalled;
+    };
+
+    auto surface(SkSurface::MakeRasterN32Premul(10, 10));
+    surface->getCanvas()->clear(SK_ColorGREEN);
+    sk_sp<SkImage> image(surface->makeImageSnapshot());
+
+    SkCanvasMock canvas(10, 10);
+
+    {
+        SkRecord record;
+        SkRecorder recorder(&record, 10, 10);
+        recorder.drawImage(image, 0, 0);
+        SkRecordDraw(record, &canvas, nullptr, nullptr, 0, nullptr, 0);
+    }
+    REPORTER_ASSERT(r, canvas.fDrawImageCalled);
+    canvas.resetTestValues();
+
+    {
+        SkRecord record;
+        SkRecorder recorder(&record, 10, 10);
+        recorder.drawImageRect(image, SkRect::MakeWH(10, 10), nullptr);
+        SkRecordDraw(record, &canvas, nullptr, nullptr, 0, nullptr, 0);
+    }
+    REPORTER_ASSERT(r, canvas.fDrawImageRectCalled);
+
 }
diff --git a/src/third_party/skia/tests/RecordOptsTest.cpp b/src/third_party/skia/tests/RecordOptsTest.cpp
index c5c4471..cb80987 100644
--- a/src/third_party/skia/tests/RecordOptsTest.cpp
+++ b/src/third_party/skia/tests/RecordOptsTest.cpp
@@ -8,34 +8,31 @@
 #include "Test.h"
 #include "RecordTestUtils.h"
 
+#include "SkBlurImageFilter.h"
+#include "SkColorFilter.h"
 #include "SkRecord.h"
 #include "SkRecordOpts.h"
 #include "SkRecorder.h"
 #include "SkRecords.h"
-#include "SkXfermode.h"
+#include "SkPictureRecorder.h"
+#include "SkPictureImageFilter.h"
+#include "SkSurface.h"
 
 static const int W = 1920, H = 1080;
 
-DEF_TEST(RecordOpts_NoopDrawSaveRestore, r) {
+DEF_TEST(RecordOpts_NoopDraw, r) {
     SkRecord record;
     SkRecorder recorder(&record, W, H);
 
-    // The save and restore are pointless if there's only draw commands in the middle.
-    recorder.save();
-        recorder.drawRect(SkRect::MakeWH(200, 200), SkPaint());
-        recorder.drawRect(SkRect::MakeWH(300, 300), SkPaint());
-        recorder.drawRect(SkRect::MakeWH(100, 100), SkPaint());
-    recorder.restore();
+    recorder.drawRect(SkRect::MakeWH(200, 200), SkPaint());
+    recorder.drawRect(SkRect::MakeWH(300, 300), SkPaint());
+    recorder.drawRect(SkRect::MakeWH(100, 100), SkPaint());
 
-    record.replace<SkRecords::NoOp>(2);  // NoOps should be allowed.
+    record.replace<SkRecords::NoOp>(1);  // NoOps should be allowed.
 
     SkRecordNoopSaveRestores(&record);
 
-    assert_type<SkRecords::NoOp>(r, record, 0);
-    assert_type<SkRecords::DrawRect>(r, record, 1);
-    assert_type<SkRecords::NoOp>(r, record, 2);
-    assert_type<SkRecords::DrawRect>(r, record, 3);
-    assert_type<SkRecords::NoOp>(r, record, 4);
+    REPORTER_ASSERT(r, 2 == count_instances_of_type<SkRecords::DrawRect>(record));
 }
 
 DEF_TEST(RecordOpts_SingleNoopSaveRestore, r) {
@@ -47,7 +44,7 @@
     recorder.restore();
 
     SkRecordNoopSaveRestores(&record);
-    for (unsigned i = 0; i < 3; i++) {
+    for (int i = 0; i < 3; i++) {
         assert_type<SkRecords::NoOp>(r, record, i);
     }
 }
@@ -70,7 +67,7 @@
     recorder.restore();
 
     SkRecordNoopSaveRestores(&record);
-    for (unsigned index = 0; index < 8; index++) {
+    for (int index = 0; index < record.count(); index++) {
         assert_type<SkRecords::NoOp>(r, record, index);
     }
 }
@@ -81,24 +78,51 @@
 
     // A previous bug NoOp'd away the first 3 commands.
     recorder.save();
-        recorder.saveLayer(NULL, NULL);
+        recorder.saveLayer(nullptr, nullptr);
         recorder.restore();
     recorder.restore();
 
     SkRecordNoopSaveRestores(&record);
-    assert_type<SkRecords::Save>     (r, record, 0);
-    assert_type<SkRecords::SaveLayer>(r, record, 1);
-    assert_type<SkRecords::Restore>  (r, record, 2);
-    assert_type<SkRecords::Restore>  (r, record, 3);
+    switch (record.count()) {
+        case 4:
+            assert_type<SkRecords::Save>     (r, record, 0);
+            assert_type<SkRecords::SaveLayer>(r, record, 1);
+            assert_type<SkRecords::Restore>  (r, record, 2);
+            assert_type<SkRecords::Restore>  (r, record, 3);
+            break;
+        case 2:
+            assert_type<SkRecords::SaveLayer>(r, record, 0);
+            assert_type<SkRecords::Restore>  (r, record, 1);
+            break;
+        case 0:
+            break;
+        default:
+            REPORTER_ASSERT(r, false);
+    }
 }
 
+#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
 static void assert_savelayer_restore(skiatest::Reporter* r,
                                      SkRecord* record,
-                                     unsigned i,
+                                     int i,
                                      bool shouldBeNoOped) {
     SkRecordNoopSaveLayerDrawRestores(record);
     if (shouldBeNoOped) {
         assert_type<SkRecords::NoOp>(r, *record, i);
+        assert_type<SkRecords::NoOp>(r, *record, i+1);
+    } else {
+        assert_type<SkRecords::SaveLayer>(r, *record, i);
+        assert_type<SkRecords::Restore>(r, *record, i+1);
+    }
+}
+
+static void assert_savelayer_draw_restore(skiatest::Reporter* r,
+                                          SkRecord* record,
+                                          int i,
+                                          bool shouldBeNoOped) {
+    SkRecordNoopSaveLayerDrawRestores(record);
+    if (shouldBeNoOped) {
+        assert_type<SkRecords::NoOp>(r, *record, i);
         assert_type<SkRecords::NoOp>(r, *record, i+2);
     } else {
         assert_type<SkRecords::SaveLayer>(r, *record, i);
@@ -113,54 +137,293 @@
     SkRect bounds = SkRect::MakeWH(100, 200);
     SkRect   draw = SkRect::MakeWH(50, 60);
 
-    SkPaint goodLayerPaint, badLayerPaint, worseLayerPaint;
-    goodLayerPaint.setColor(0x03000000);  // Only alpha.
-    badLayerPaint.setColor( 0x03040506);  // Not only alpha.
-    worseLayerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);  // Any effect will do.
+    SkPaint alphaOnlyLayerPaint, translucentLayerPaint, xfermodeLayerPaint;
+    alphaOnlyLayerPaint.setColor(0x03000000);  // Only alpha.
+    translucentLayerPaint.setColor(0x03040506);  // Not only alpha.
+    xfermodeLayerPaint.setBlendMode(SkBlendMode::kDstIn);  // Any effect will do.
 
-    SkPaint goodDrawPaint, badDrawPaint;
-    goodDrawPaint.setColor(0xFF020202);  // Opaque.
-    badDrawPaint.setColor( 0x0F020202);  // Not opaque.
+    SkPaint opaqueDrawPaint, translucentDrawPaint;
+    opaqueDrawPaint.setColor(0xFF020202);  // Opaque.
+    translucentDrawPaint.setColor(0x0F020202);  // Not opaque.
 
-    // No change: optimization can't handle bounds.
-    recorder.saveLayer(&bounds, NULL);
-        recorder.drawRect(draw, goodDrawPaint);
+    // SaveLayer/Restore removed: No paint = no point.
+    recorder.saveLayer(nullptr, nullptr);
+        recorder.drawRect(draw, opaqueDrawPaint);
     recorder.restore();
-    assert_savelayer_restore(r, &record, 0, false);
+    assert_savelayer_draw_restore(r, &record, 0, true);
 
-    // SaveLayer/Restore removed: no bounds + no paint = no point.
-    recorder.saveLayer(NULL, NULL);
-        recorder.drawRect(draw, goodDrawPaint);
+    // Bounds don't matter.
+    recorder.saveLayer(&bounds, nullptr);
+        recorder.drawRect(draw, opaqueDrawPaint);
     recorder.restore();
-    assert_savelayer_restore(r, &record, 3, true);
+    assert_savelayer_draw_restore(r, &record, 3, true);
 
     // TODO(mtklein): test case with null draw paint
 
     // No change: layer paint isn't alpha-only.
-    recorder.saveLayer(NULL, &badLayerPaint);
-        recorder.drawRect(draw, goodDrawPaint);
+    recorder.saveLayer(nullptr, &translucentLayerPaint);
+        recorder.drawRect(draw, opaqueDrawPaint);
     recorder.restore();
-    assert_savelayer_restore(r, &record, 6, false);
+    assert_savelayer_draw_restore(r, &record, 6, false);
 
     // No change: layer paint has an effect.
-    recorder.saveLayer(NULL, &worseLayerPaint);
-        recorder.drawRect(draw, goodDrawPaint);
+    recorder.saveLayer(nullptr, &xfermodeLayerPaint);
+        recorder.drawRect(draw, opaqueDrawPaint);
     recorder.restore();
-    assert_savelayer_restore(r, &record, 9, false);
-
-    // No change: draw paint isn't opaque.
-    recorder.saveLayer(NULL, &goodLayerPaint);
-        recorder.drawRect(draw, badDrawPaint);
-    recorder.restore();
-    assert_savelayer_restore(r, &record, 12, false);
+    assert_savelayer_draw_restore(r, &record, 9, false);
 
     // SaveLayer/Restore removed: we can fold in the alpha!
-    recorder.saveLayer(NULL, &goodLayerPaint);
-        recorder.drawRect(draw, goodDrawPaint);
+    recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
+        recorder.drawRect(draw, translucentDrawPaint);
     recorder.restore();
-    assert_savelayer_restore(r, &record, 15, true);
+    assert_savelayer_draw_restore(r, &record, 12, true);
+
+    // SaveLayer/Restore removed: we can fold in the alpha!
+    recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
+        recorder.drawRect(draw, opaqueDrawPaint);
+    recorder.restore();
+    assert_savelayer_draw_restore(r, &record, 15, true);
 
     const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, record, 16);
-    REPORTER_ASSERT(r, drawRect != NULL);
+    REPORTER_ASSERT(r, drawRect != nullptr);
     REPORTER_ASSERT(r, drawRect->paint.getColor() == 0x03020202);
+
+    // saveLayer w/ backdrop should NOT go away
+    sk_sp<SkImageFilter> filter(SkBlurImageFilter::Make(3, 3, nullptr));
+    recorder.saveLayer({ nullptr, nullptr, filter.get(), nullptr, nullptr, 0});
+        recorder.drawRect(draw, opaqueDrawPaint);
+    recorder.restore();
+    assert_savelayer_draw_restore(r, &record, 18, false);
+
+    // saveLayer w/ clip mask should also NOT go away
+    {
+        sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(10, 10));
+        recorder.saveLayer({ nullptr, nullptr, nullptr, surface->makeImageSnapshot().get(),
+                             nullptr, 0});
+            recorder.drawRect(draw, opaqueDrawPaint);
+        recorder.restore();
+        assert_savelayer_draw_restore(r, &record, 21, false);
+    }
 }
+#endif
+
+static void assert_merge_svg_opacity_and_filter_layers(skiatest::Reporter* r,
+                                                       SkRecord* record,
+                                                       int i,
+                                                       bool shouldBeNoOped) {
+    SkRecordMergeSvgOpacityAndFilterLayers(record);
+    if (shouldBeNoOped) {
+        assert_type<SkRecords::NoOp>(r, *record, i);
+        assert_type<SkRecords::NoOp>(r, *record, i + 6);
+    } else {
+        assert_type<SkRecords::SaveLayer>(r, *record, i);
+        assert_type<SkRecords::Restore>(r, *record, i + 6);
+    }
+}
+
+DEF_TEST(RecordOpts_MergeSvgOpacityAndFilterLayers, r) {
+    SkRecord record;
+    SkRecorder recorder(&record, W, H);
+
+    SkRect bounds = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(200));
+    SkRect clip = SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(60));
+
+    SkPaint alphaOnlyLayerPaint;
+    alphaOnlyLayerPaint.setColor(0x03000000);  // Only alpha.
+    SkPaint translucentLayerPaint;
+    translucentLayerPaint.setColor(0x03040506);  // Not only alpha.
+    SkPaint xfermodePaint;
+    xfermodePaint.setBlendMode(SkBlendMode::kDstIn);
+    SkPaint colorFilterPaint;
+    colorFilterPaint.setColorFilter(
+        SkColorFilter::MakeModeFilter(SK_ColorLTGRAY, SkBlendMode::kSrcIn));
+
+    SkPaint opaqueFilterLayerPaint;
+    opaqueFilterLayerPaint.setColor(0xFF020202);  // Opaque.
+    SkPaint translucentFilterLayerPaint;
+    translucentFilterLayerPaint.setColor(0x0F020202);  // Not opaque.
+    sk_sp<SkPicture> shape;
+    {
+        SkPictureRecorder recorder;
+        SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(100), SkIntToScalar(100));
+        SkPaint shapePaint;
+        shapePaint.setColor(SK_ColorWHITE);
+        canvas->drawRect(SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(50)), shapePaint);
+        shape = recorder.finishRecordingAsPicture();
+    }
+    translucentFilterLayerPaint.setImageFilter(SkPictureImageFilter::Make(shape));
+
+    int index = 0;
+
+    {
+        sk_sp<SkImageFilter> filter(SkBlurImageFilter::Make(3, 3, nullptr));
+        // first (null) should be optimized, 2nd should not
+        SkImageFilter* filters[] = { nullptr, filter.get() };
+
+        // Any combination of these should cause the pattern to be optimized.
+        SkRect* firstBounds[] = { nullptr, &bounds };
+        SkPaint* firstPaints[] = { nullptr, &alphaOnlyLayerPaint };
+        SkRect* secondBounds[] = { nullptr, &bounds };
+        SkPaint* secondPaints[] = { &opaqueFilterLayerPaint, &translucentFilterLayerPaint };
+
+        for (auto outerF : filters) {
+            bool outerNoOped = !outerF;
+            for (auto innerF : filters) {
+                for (size_t i = 0; i < SK_ARRAY_COUNT(firstBounds); ++ i) {
+                    for (size_t j = 0; j < SK_ARRAY_COUNT(firstPaints); ++j) {
+                        for (size_t k = 0; k < SK_ARRAY_COUNT(secondBounds); ++k) {
+                            for (size_t m = 0; m < SK_ARRAY_COUNT(secondPaints); ++m) {
+                                bool innerNoOped = !secondBounds[k] && !secondPaints[m] && !innerF;
+
+                                recorder.saveLayer({firstBounds[i], firstPaints[j], outerF,
+                                                    nullptr, nullptr, 0});
+                                recorder.save();
+                                recorder.clipRect(clip);
+                                recorder.saveLayer({secondBounds[k], secondPaints[m], innerF,
+                                                    nullptr, nullptr, 0});
+                                recorder.restore();
+                                recorder.restore();
+                                recorder.restore();
+                                assert_merge_svg_opacity_and_filter_layers(r, &record, index,
+                                                                           outerNoOped);
+                            #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
+                                assert_savelayer_restore(r, &record, index + 3, innerNoOped);
+                            #endif
+                                index += 7;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // These should cause the pattern to stay unoptimized:
+    struct {
+        SkPaint* firstPaint;
+        SkPaint* secondPaint;
+    } noChangeTests[] = {
+        // No change: nullptr filter layer paint not implemented.
+        { &alphaOnlyLayerPaint, nullptr },
+        // No change: layer paint is not alpha-only.
+        { &translucentLayerPaint, &opaqueFilterLayerPaint },
+        // No change: layer paint has an xfereffect.
+        { &xfermodePaint, &opaqueFilterLayerPaint },
+        // No change: filter layer paint has an xfereffect.
+        { &alphaOnlyLayerPaint, &xfermodePaint },
+        // No change: layer paint has a color filter.
+        { &colorFilterPaint, &opaqueFilterLayerPaint },
+        // No change: filter layer paint has a color filter (until the optimization accounts for
+        // constant color draws that can filter the color).
+        { &alphaOnlyLayerPaint, &colorFilterPaint }
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(noChangeTests); ++i) {
+        recorder.saveLayer(nullptr, noChangeTests[i].firstPaint);
+        recorder.save();
+        recorder.clipRect(clip);
+        recorder.saveLayer(nullptr, noChangeTests[i].secondPaint);
+        recorder.restore();
+        recorder.restore();
+        recorder.restore();
+        assert_merge_svg_opacity_and_filter_layers(r, &record, index, false);
+        index += 7;
+    }
+
+    // Test the folded alpha value.
+    recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
+    recorder.save();
+    recorder.clipRect(clip);
+    recorder.saveLayer(nullptr, &opaqueFilterLayerPaint);
+    recorder.restore();
+    recorder.restore();
+    recorder.restore();
+    assert_merge_svg_opacity_and_filter_layers(r, &record, index, true);
+
+    const SkRecords::SaveLayer* saveLayer = assert_type<SkRecords::SaveLayer>(r, record, index + 3);
+    REPORTER_ASSERT(r, saveLayer != nullptr);
+    REPORTER_ASSERT(r, saveLayer->paint->getColor() == 0x03020202);
+
+    index += 7;
+
+    // Test that currently we do not fold alphas for patterns without the clip. This is just not
+    // implemented.
+    recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
+    recorder.saveLayer(nullptr, &opaqueFilterLayerPaint);
+    recorder.restore();
+    recorder.restore();
+    SkRecordMergeSvgOpacityAndFilterLayers(&record);
+    assert_type<SkRecords::SaveLayer>(r, record, index);
+    assert_type<SkRecords::SaveLayer>(r, record, index + 1);
+    assert_type<SkRecords::Restore>(r, record, index + 2);
+    assert_type<SkRecords::Restore>(r, record, index + 3);
+    index += 4;
+}
+
+static void do_draw(SkCanvas* canvas, SkColor color, bool doLayer) {
+    canvas->drawColor(SK_ColorWHITE);
+
+    SkPaint p;
+    p.setColor(color);
+
+    if (doLayer) {
+        canvas->saveLayer(nullptr, nullptr);
+        p.setBlendMode(SkBlendMode::kSrc);
+        canvas->drawPaint(p);
+        canvas->restore();
+    } else {
+        canvas->drawPaint(p);
+    }
+}
+
+static bool is_equal(SkSurface* a, SkSurface* b) {
+    const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
+    SkPMColor ca, cb;
+    a->readPixels(info, &ca, sizeof(SkPMColor), 0, 0);
+    b->readPixels(info, &cb, sizeof(SkPMColor), 0, 0);
+    return ca == cb;
+}
+
+// Test drawing w/ and w/o a simple layer (no bounds or paint), so see that drawing ops
+// that *should* draw the same in fact do.
+//
+// Perform this test twice : once directly, and once via a picture
+//
+static void do_savelayer_srcmode(skiatest::Reporter* r, SkColor color) {
+    for (int doPicture = 0; doPicture <= 1; ++doPicture) {
+        sk_sp<SkSurface> surf0 = SkSurface::MakeRasterN32Premul(10, 10);
+        sk_sp<SkSurface> surf1 = SkSurface::MakeRasterN32Premul(10, 10);
+        SkCanvas* c0 = surf0->getCanvas();
+        SkCanvas* c1 = surf1->getCanvas();
+
+        SkPictureRecorder rec0, rec1;
+        if (doPicture) {
+            c0 = rec0.beginRecording(10, 10);
+            c1 = rec1.beginRecording(10, 10);
+        }
+
+        do_draw(c0, color, false);
+        do_draw(c1, color, true);
+
+        if (doPicture) {
+            surf0->getCanvas()->drawPicture(rec0.finishRecordingAsPicture());
+            surf1->getCanvas()->drawPicture(rec1.finishRecordingAsPicture());
+        }
+
+        // we replicate the assert so we can see which line is reported if there is a failure
+        if (doPicture) {
+            REPORTER_ASSERT(r, is_equal(surf0.get(), surf1.get()));
+        } else {
+            REPORTER_ASSERT(r, is_equal(surf0.get(), surf1.get()));
+        }
+    }
+}
+
+DEF_TEST(savelayer_srcmode_opaque, r) {
+    do_savelayer_srcmode(r, SK_ColorRED);
+}
+
+DEF_TEST(savelayer_srcmode_alpha, r) {
+    do_savelayer_srcmode(r, 0x80FF0000);
+}
+
diff --git a/src/third_party/skia/tests/RecordPatternTest.cpp b/src/third_party/skia/tests/RecordPatternTest.cpp
index 5f4d006..33a0114 100644
--- a/src/third_party/skia/tests/RecordPatternTest.cpp
+++ b/src/third_party/skia/tests/RecordPatternTest.cpp
@@ -1,3 +1,10 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #include "Test.h"
 
 #include "SkRecord.h"
@@ -6,9 +13,9 @@
 #include "SkRecords.h"
 
 using namespace SkRecords;
-typedef Pattern3<Is<Save>,
-                 Is<ClipRect>,
-                 Is<Restore> >
+typedef Pattern<Is<Save>,
+                Is<ClipRect>,
+                Is<Restore>>
     SaveClipRectRestore;
 
 DEF_TEST(RecordPattern_Simple, r) {
@@ -28,9 +35,9 @@
 
     recorder.restore();
     REPORTER_ASSERT(r, pattern.match(&record, 0));
-    REPORTER_ASSERT(r, pattern.first<Save>()      != NULL);
-    REPORTER_ASSERT(r, pattern.second<ClipRect>() != NULL);
-    REPORTER_ASSERT(r, pattern.third<Restore>()   != NULL);
+    REPORTER_ASSERT(r, pattern.first<Save>()      != nullptr);
+    REPORTER_ASSERT(r, pattern.second<ClipRect>() != nullptr);
+    REPORTER_ASSERT(r, pattern.third<Restore>()   != nullptr);
 }
 
 DEF_TEST(RecordPattern_StartingIndex, r) {
@@ -47,7 +54,7 @@
     }
 
     // We should match only at 0 and 3.  Going over the length should fail gracefully.
-    for (unsigned i = 0; i < 8; i++) {
+    for (int i = 0; i < 8; i++) {
         if (i == 0 || i == 3) {
             REPORTER_ASSERT(r, pattern.match(&record, i) == i + 3);
         } else {
@@ -70,130 +77,76 @@
     REPORTER_ASSERT(r, !pattern.match(&record, 0));
 }
 
-DEF_TEST(RecordPattern_Star, r) {
-    Pattern3<Is<Save>, Star<Is<ClipRect> >, Is<Restore> > pattern;
+DEF_TEST(RecordPattern_Greedy, r) {
+    Pattern<Is<Save>, Greedy<Is<ClipRect>>, Is<Restore>> pattern;
 
     SkRecord record;
     SkRecorder recorder(&record, 1920, 1200);
-
-    recorder.save();
-    recorder.restore();
-    REPORTER_ASSERT(r, pattern.match(&record, 0));
+    int index = 0;
 
     recorder.save();
         recorder.clipRect(SkRect::MakeWH(300, 200));
     recorder.restore();
-    REPORTER_ASSERT(r, pattern.match(&record, 2));
+    REPORTER_ASSERT(r, pattern.match(&record, index));
+    index += 3;
 
     recorder.save();
         recorder.clipRect(SkRect::MakeWH(300, 200));
         recorder.clipRect(SkRect::MakeWH(100, 100));
     recorder.restore();
-    REPORTER_ASSERT(r, pattern.match(&record, 5));
-}
-
-DEF_TEST(RecordPattern_IsDraw, r) {
-    Pattern3<Is<Save>, IsDraw, Is<Restore> > pattern;
-
-    SkRecord record;
-    SkRecorder recorder(&record, 1920, 1200);
-
-    recorder.save();
-        recorder.clipRect(SkRect::MakeWH(300, 200));
-    recorder.restore();
-
-    REPORTER_ASSERT(r, !pattern.match(&record, 0));
-
-    SkPaint paint;
-
-    recorder.save();
-        paint.setColor(0xEEAA8822);
-        recorder.drawRect(SkRect::MakeWH(300, 200), paint);
-    recorder.restore();
-
-    recorder.save();
-        paint.setColor(0xFACEFACE);
-        recorder.drawPaint(paint);
-    recorder.restore();
-
-    REPORTER_ASSERT(r, pattern.match(&record, 3));
-    REPORTER_ASSERT(r, pattern.first<Save>()    != NULL);
-    REPORTER_ASSERT(r, pattern.second<SkPaint>()->getColor() == 0xEEAA8822);
-    REPORTER_ASSERT(r, pattern.third<Restore>() != NULL);
-
-    REPORTER_ASSERT(r, pattern.match(&record, 6));
-    REPORTER_ASSERT(r, pattern.first<Save>()    != NULL);
-    REPORTER_ASSERT(r, pattern.second<SkPaint>()->getColor() == 0xFACEFACE);
-    REPORTER_ASSERT(r, pattern.third<Restore>() != NULL);
+    REPORTER_ASSERT(r, pattern.match(&record, index));
 }
 
 DEF_TEST(RecordPattern_Complex, r) {
-    Pattern3<Is<Save>,
-             Star<Not<Or3<Is<Save>,
+    Pattern<Is<Save>,
+            Greedy<Not<Or<Is<Save>,
                           Is<Restore>,
-                          IsDraw> > >,
-             Is<Restore> > pattern;
+                          IsDraw>>>,
+            Is<Restore>> pattern;
 
     SkRecord record;
     SkRecorder recorder(&record, 1920, 1200);
+    int start, begin, end;
 
-    recorder.save();
-    recorder.restore();
-    REPORTER_ASSERT(r, pattern.match(&record, 0) == 2);
-
-    recorder.save();
-        recorder.save();
-        recorder.restore();
-    recorder.restore();
-    REPORTER_ASSERT(r, !pattern.match(&record, 2));
-    REPORTER_ASSERT(r, pattern.match(&record, 3) == 5);
-
+    start = record.count();
     recorder.save();
         recorder.clipRect(SkRect::MakeWH(300, 200));
     recorder.restore();
-    REPORTER_ASSERT(r, pattern.match(&record, 6) == 9);
+    REPORTER_ASSERT(r, pattern.match(&record, 0) == record.count());
+    end = start;
+    REPORTER_ASSERT(r, pattern.search(&record, &begin, &end));
+    REPORTER_ASSERT(r, begin == start);
+    REPORTER_ASSERT(r, end == record.count());
 
+    start = record.count();
     recorder.save();
         recorder.clipRect(SkRect::MakeWH(300, 200));
         recorder.drawRect(SkRect::MakeWH(100, 3000), SkPaint());
     recorder.restore();
-    REPORTER_ASSERT(r, !pattern.match(&record, 9));
+    REPORTER_ASSERT(r, !pattern.match(&record, start));
+    end = start;
+    REPORTER_ASSERT(r, !pattern.search(&record, &begin, &end));
 
+    start = record.count();
     recorder.save();
-        recorder.pushCull(SkRect::MakeWH(300, 200));
         recorder.clipRect(SkRect::MakeWH(300, 200));
         recorder.clipRect(SkRect::MakeWH(100, 400));
-        recorder.popCull();
     recorder.restore();
-    REPORTER_ASSERT(r, pattern.match(&record, 13) == 19);
-
-    // Same as above, but using pattern.search to step through matches.
-    unsigned begin, end = 0;
+    REPORTER_ASSERT(r, pattern.match(&record, start) == record.count());
+    end = start;
     REPORTER_ASSERT(r, pattern.search(&record, &begin, &end));
-    REPORTER_ASSERT(r, begin == 0);
-    REPORTER_ASSERT(r, end == 2);
-
-    REPORTER_ASSERT(r, pattern.search(&record, &begin, &end));
-    REPORTER_ASSERT(r, begin == 3);
-    REPORTER_ASSERT(r, end == 5);
-
-    REPORTER_ASSERT(r, pattern.search(&record, &begin, &end));
-    REPORTER_ASSERT(r, begin == 6);
-    REPORTER_ASSERT(r, end == 9);
-
-    REPORTER_ASSERT(r, pattern.search(&record, &begin, &end));
-    REPORTER_ASSERT(r, begin == 13);
-    REPORTER_ASSERT(r, end == 19);
+    REPORTER_ASSERT(r, begin == start);
+    REPORTER_ASSERT(r, end == record.count());
 
     REPORTER_ASSERT(r, !pattern.search(&record, &begin, &end));
 }
 
 DEF_TEST(RecordPattern_SaveLayerIsNotADraw, r) {
-    Pattern1<IsDraw> pattern;
+    Pattern<IsDraw> pattern;
 
     SkRecord record;
     SkRecorder recorder(&record, 1920, 1200);
-    recorder.saveLayer(NULL, NULL);
+    recorder.saveLayer(nullptr, nullptr);
 
     REPORTER_ASSERT(r, !pattern.match(&record, 0));
 }
diff --git a/src/third_party/skia/tests/RecordReplaceDrawTest.cpp b/src/third_party/skia/tests/RecordReplaceDrawTest.cpp
deleted file mode 100644
index 618be72..0000000
--- a/src/third_party/skia/tests/RecordReplaceDrawTest.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#if SK_SUPPORT_GPU
-
-#include "Test.h"
-#include "RecordTestUtils.h"
-
-#include "SkBBHFactory.h"
-#include "SkRecordDraw.h"
-#include "SkRecorder.h"
-#include "SkUtils.h"
-#include "GrRecordReplaceDraw.h"
-
-static const int kWidth = 100;
-static const int kHeight = 100;
-
-class JustOneDraw : public SkDrawPictureCallback {
-public:
-    JustOneDraw() : fCalls(0) {}
-
-    virtual bool abortDrawing() SK_OVERRIDE { return fCalls++ > 0; }
-private:
-    int fCalls;
-};
-
-// Make sure the abort callback works
-DEF_TEST(RecordReplaceDraw_Abort, r) {
-    // Record two commands.
-    SkRecord record;
-    SkRecorder recorder(&record, kWidth, kHeight);
-    recorder.drawRect(SkRect::MakeWH(SkIntToScalar(kWidth), SkIntToScalar(kHeight)), SkPaint());
-    recorder.clipRect(SkRect::MakeWH(SkIntToScalar(kWidth), SkIntToScalar(kHeight)));
-
-    SkRecord rerecord;
-    SkRecorder canvas(&rerecord, kWidth, kHeight);
-
-    GrReplacements replacements;
-    JustOneDraw callback;
-    GrRecordReplaceDraw(record, &canvas, NULL/*bbh*/, &replacements, &callback);
-
-    REPORTER_ASSERT(r, 3 == rerecord.count());
-    assert_type<SkRecords::Save>(r, rerecord, 0);
-    assert_type<SkRecords::DrawRect>(r, rerecord, 1);
-    assert_type<SkRecords::Restore>(r, rerecord, 2);
-}
-
-// Make sure GrRecordReplaceDraw balances unbalanced saves
-DEF_TEST(RecordReplaceDraw_Unbalanced, r) {
-    SkRecord record;
-    SkRecorder recorder(&record, kWidth, kHeight);
-    recorder.save();  // We won't balance this, but GrRecordReplaceDraw will for us.
-
-    SkRecord rerecord;
-    SkRecorder canvas(&rerecord, kWidth, kHeight);
-
-    GrReplacements replacements;
-    GrRecordReplaceDraw(record, &canvas, NULL/*bbh*/, &replacements, NULL/*callback*/);
-
-    REPORTER_ASSERT(r, 4 == rerecord.count());
-    assert_type<SkRecords::Save>(r, rerecord, 0);
-    assert_type<SkRecords::Save>(r, rerecord, 1);
-    assert_type<SkRecords::Restore>(r, rerecord, 2);
-    assert_type<SkRecords::Restore>(r, rerecord, 3);
-}
-
-static SkImage* make_image(SkColor color) {
-    const SkPMColor pmcolor = SkPreMultiplyColor(color);
-    const SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
-    const size_t rowBytes = info.minRowBytes();
-    const size_t size = rowBytes * info.height();
-
-    SkAutoMalloc addr(size);
-    sk_memset32((SkPMColor*)addr.get(), pmcolor, SkToInt(size >> 2));
-
-    return SkImage::NewRasterCopy(info, addr.get(), rowBytes);
-}
-
-// Test out the layer replacement functionality with and w/o a BBH
-void test_replacements(skiatest::Reporter* r, bool useBBH) {
-    SkRecord record;
-    SkRecorder recorder(&record, kWidth, kHeight);
-    SkAutoTDelete<SkPaint> paint(SkNEW(SkPaint));
-    recorder.saveLayer(NULL, paint);
-    recorder.clear(SK_ColorRED);
-    recorder.restore();
-    recorder.drawRect(SkRect::MakeWH(SkIntToScalar(kWidth/2), SkIntToScalar(kHeight/2)), 
-                      SkPaint());
-
-    GrReplacements replacements;
-    GrReplacements::ReplacementInfo* ri = replacements.push();
-    ri->fStart = 0;
-    ri->fStop = 2;
-    ri->fPos.set(0, 0);
-    ri->fImage = make_image(SK_ColorRED);
-    ri->fPaint = SkNEW(SkPaint);
-    ri->fSrcRect = SkIRect::MakeWH(kWidth, kHeight);
-
-    SkAutoTUnref<SkBBoxHierarchy> bbh;
-
-    if (useBBH) {
-        SkRTreeFactory factory;
-        bbh.reset((factory)(kWidth, kHeight));
-        SkRecordFillBounds(record, bbh);
-    }
-
-    SkRecord rerecord;
-    SkRecorder canvas(&rerecord, kWidth, kHeight);
-    GrRecordReplaceDraw(record, &canvas, bbh, &replacements, NULL/*callback*/);
-
-    REPORTER_ASSERT(r, 7 == rerecord.count());
-    assert_type<SkRecords::Save>(r, rerecord, 0);
-    assert_type<SkRecords::Save>(r, rerecord, 1);
-    assert_type<SkRecords::SetMatrix>(r, rerecord, 2);
-    assert_type<SkRecords::DrawBitmapRectToRect>(r, rerecord, 3);
-    assert_type<SkRecords::Restore>(r, rerecord, 4);
-    assert_type<SkRecords::DrawRect>(r, rerecord, 5);
-    assert_type<SkRecords::Restore>(r, rerecord, 6);
-}
-
-DEF_TEST(RecordReplaceDraw_Replace, r)        { test_replacements(r, false); }
-DEF_TEST(RecordReplaceDraw_ReplaceWithBBH, r) { test_replacements(r, true); }
-
-#endif
diff --git a/src/third_party/skia/tests/RecordTest.cpp b/src/third_party/skia/tests/RecordTest.cpp
index 2a0e615..a67d7e8 100644
--- a/src/third_party/skia/tests/RecordTest.cpp
+++ b/src/third_party/skia/tests/RecordTest.cpp
@@ -5,13 +5,14 @@
  * found in the LICENSE file.
  */
 
-#include "Test.h"
-
+#include "RecordTestUtils.h"
 #include "SkBitmap.h"
 #include "SkImageInfo.h"
-#include "SkShader.h"
 #include "SkRecord.h"
 #include "SkRecords.h"
+#include "SkShader.h"
+#include "Test.h"
+
 
 // Sums the area of any DrawRect command it sees.
 class AreaSummer {
@@ -27,8 +28,8 @@
     int area() const { return fArea; }
 
     void apply(const SkRecord& record) {
-        for (unsigned i = 0; i < record.count(); i++) {
-            record.visit<void>(i, *this);
+        for (int i = 0; i < record.count(); i++) {
+            record.visit(i, *this);
         }
     }
 
@@ -45,13 +46,13 @@
     }
 
     void apply(SkRecord* record) {
-        for (unsigned i = 0; i < record->count(); i++) {
-            record->mutate<void>(i, *this);
+        for (int i = 0; i < record->count(); i++) {
+            record->mutate(i, *this);
         }
     }
 };
 
-#define APPEND(record, type, ...) SkNEW_PLACEMENT_ARGS(record.append<type>(), type, (__VA_ARGS__))
+#define APPEND(record, type, ...) new (record.append<type>()) type{__VA_ARGS__}
 
 // Basic tests for the low-level SkRecord code.
 DEF_TEST(Record, r) {
@@ -76,6 +77,25 @@
     REPORTER_ASSERT(r, summer.area() == 500);
 }
 
+DEF_TEST(Record_defrag, r) {
+    SkRecord record;
+    APPEND(record, SkRecords::Save);
+    APPEND(record, SkRecords::ClipRect);
+    APPEND(record, SkRecords::NoOp);
+    APPEND(record, SkRecords::DrawRect);
+    APPEND(record, SkRecords::NoOp);
+    APPEND(record, SkRecords::NoOp);
+    APPEND(record, SkRecords::Restore);
+    REPORTER_ASSERT(r, record.count() == 7);
+
+    record.defrag();
+    REPORTER_ASSERT(r, record.count() == 4);
+    assert_type<SkRecords::Save    >(r, record, 0);
+    assert_type<SkRecords::ClipRect>(r, record, 1);
+    assert_type<SkRecords::DrawRect>(r, record, 2);
+    assert_type<SkRecords::Restore >(r, record, 3);
+}
+
 #undef APPEND
 
 template <typename T>
@@ -85,21 +105,14 @@
 
 DEF_TEST(Record_Alignment, r) {
     SkRecord record;
-
-    // Of course a byte's always aligned.
     REPORTER_ASSERT(r, is_aligned(record.alloc<uint8_t>()));
-
-    // (If packed tightly, the rest below here would be off by one.)
-
-    // It happens that the first implementation always aligned to 4 bytes,
-    // so these two were always correct.
     REPORTER_ASSERT(r, is_aligned(record.alloc<uint16_t>()));
     REPORTER_ASSERT(r, is_aligned(record.alloc<uint32_t>()));
-
-    // These two are regression tests (void* only on 64-bit machines).
-    REPORTER_ASSERT(r, is_aligned(record.alloc<uint64_t>()));
     REPORTER_ASSERT(r, is_aligned(record.alloc<void*>()));
 
-    // We're not testing beyond sizeof(void*), which is where the current implementation will break.
+    // It's not clear if we care that 8-byte values are aligned on 32-bit machines.
+    if (sizeof(void*) == 8) {
+        REPORTER_ASSERT(r, is_aligned(record.alloc<double>()));
+        REPORTER_ASSERT(r, is_aligned(record.alloc<uint64_t>()));
+    }
 }
-
diff --git a/src/third_party/skia/tests/RecordTestUtils.h b/src/third_party/skia/tests/RecordTestUtils.h
index 0575b83..853ee21 100644
--- a/src/third_party/skia/tests/RecordTestUtils.h
+++ b/src/third_party/skia/tests/RecordTestUtils.h
@@ -1,13 +1,21 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #ifndef RecordTestUtils_DEFINED
 #define RecordTestUtils_DEFINED
 
 #include "SkRecord.h"
 #include "SkRecords.h"
+#include "Test.h"
 
-// If the command we're reading is a U, set ptr to it, otherwise set it to NULL.
+// If the command we're reading is a U, set ptr to it, otherwise set it to nullptr.
 template <typename U>
 struct ReadAs {
-    ReadAs() : ptr(NULL), type(SkRecords::Type(~0)) {}
+    ReadAs() : ptr(nullptr), type(SkRecords::Type(~0)) {}
 
     const U* ptr;
     SkRecords::Type type;
@@ -20,12 +28,36 @@
 
 // Assert that the ith command in record is of type T, and return it.
 template <typename T>
-static const T* assert_type(skiatest::Reporter* r, const SkRecord& record, unsigned index) {
+static const T* assert_type(skiatest::Reporter* r, const SkRecord& record, int index) {
     ReadAs<T> reader;
-    record.visit<void>(index, reader);
+    record.visit(index, reader);
     REPORTER_ASSERT(r, T::kType == reader.type);
     REPORTER_ASSERT(r, SkToBool(reader.ptr));
     return reader.ptr;
 }
 
+template <typename DrawT> struct MatchType {
+    template <typename T> int operator()(const T&) { return 0; }
+    int operator()(const DrawT&) { return 1; }
+};
+
+template <typename DrawT> int count_instances_of_type(const SkRecord& record) {
+    MatchType<DrawT> matcher;
+    int counter = 0;
+    for (int i = 0; i < record.count(); i++) {
+        counter += record.visit(i, matcher);
+    }
+    return counter;
+}
+
+template <typename DrawT> int find_first_instances_of_type(const SkRecord& record) {
+    MatchType<DrawT> matcher;
+    for (int i = 0; i < record.count(); i++) {
+        if (record.visit(i, matcher)) {
+            return i;
+        }
+    }
+    return -1;
+}
+
 #endif//RecordTestUtils_DEFINED
diff --git a/src/third_party/skia/tests/RecorderTest.cpp b/src/third_party/skia/tests/RecorderTest.cpp
index aced54f..a709a51 100644
--- a/src/third_party/skia/tests/RecorderTest.cpp
+++ b/src/third_party/skia/tests/RecorderTest.cpp
@@ -12,6 +12,7 @@
 #include "SkRecorder.h"
 #include "SkRecords.h"
 #include "SkShader.h"
+#include "SkSurface.h"
 
 #define COUNT(T) + 1
 static const int kRecordTypes = SK_RECORD_TYPES(COUNT);
@@ -29,8 +30,8 @@
     int count() const { return fHistogram[T::kType]; }
 
     void apply(const SkRecord& record) {
-        for (unsigned i = 0; i < record.count(); i++) {
-            record.visit<void>(i, *this);
+        for (int i = 0; i < record.count(); i++) {
+            record.visit(i, *this);
         }
     }
 
@@ -49,39 +50,6 @@
     REPORTER_ASSERT(r, 1 == tally.count<SkRecords::DrawRect>());
 }
 
-// All of Skia will work fine without support for comment groups, but
-// Chrome's inspector can break.  This serves as a simple regression test.
-DEF_TEST(Recorder_CommentGroups, r) {
-    SkRecord record;
-    SkRecorder recorder(&record, 1920, 1080);
-
-    recorder.beginCommentGroup("test");
-        recorder.addComment("foo", "bar");
-        recorder.addComment("baz", "quux");
-    recorder.endCommentGroup();
-
-    Tally tally;
-    tally.apply(record);
-
-    REPORTER_ASSERT(r, 1 == tally.count<SkRecords::BeginCommentGroup>());
-    REPORTER_ASSERT(r, 2 == tally.count<SkRecords::AddComment>());
-    REPORTER_ASSERT(r, 1 == tally.count<SkRecords::EndCommentGroup>());
-}
-
-// DrawData is similar to comment groups.  It doesn't affect drawing, but
-// it's a pass-through we provide to the client.  Again, a simple reg. test.
-DEF_TEST(Recorder_DrawData, r) {
-    SkRecord record;
-    SkRecorder recorder(&record, 100, 100);
-
-    const char* data = "This sure is some data, eh?";
-    recorder.drawData(data, strlen(data));
-
-    Tally tally;
-    tally.apply(record);
-    REPORTER_ASSERT(r, 1 == tally.count<SkRecords::DrawData>());
-}
-
 // Regression test for leaking refs held by optional arguments.
 DEF_TEST(Recorder_RefLeaking, r) {
     // We use SaveLayer to test:
@@ -90,7 +58,7 @@
 
     SkRect bounds = SkRect::MakeWH(320, 240);
     SkPaint paint;
-    paint.setShader(SkShader::CreateEmptyShader())->unref();
+    paint.setShader(SkShader::MakeEmptyShader());
 
     REPORTER_ASSERT(r, paint.getShader()->unique());
     {
@@ -102,51 +70,41 @@
     REPORTER_ASSERT(r, paint.getShader()->unique());
 }
 
-DEF_TEST(Recorder_RefPictures, r) {
-    SkAutoTUnref<SkPicture> pic;
+DEF_TEST(Recorder_drawImage_takeReference, reporter) {
 
+    sk_sp<SkImage> image;
     {
-        SkPictureRecorder pr;
-        SkCanvas* canvas = pr.beginRecording(100, 100);
-        canvas->drawColor(SK_ColorRED);
-        pic.reset(pr.endRecording());
+        auto surface(SkSurface::MakeRasterN32Premul(100, 100));
+        surface->getCanvas()->clear(SK_ColorGREEN);
+        image = surface->makeImageSnapshot();
     }
-    REPORTER_ASSERT(r, pic->unique());
+    {
+        SkRecord record;
+        SkRecorder recorder(&record, 100, 100);
+
+        // DrawImage is supposed to take a reference
+        recorder.drawImage(image, 0, 0);
+        REPORTER_ASSERT(reporter, !image->unique());
+
+        Tally tally;
+        tally.apply(record);
+
+        REPORTER_ASSERT(reporter, 1 == tally.count<SkRecords::DrawImage>());
+    }
+    REPORTER_ASSERT(reporter, image->unique());
 
     {
         SkRecord record;
         SkRecorder recorder(&record, 100, 100);
-        recorder.drawPicture(pic);
-        // the recorder should now also be an owner
-        REPORTER_ASSERT(r, !pic->unique());
+
+        // DrawImageRect is supposed to take a reference
+        recorder.drawImageRect(image, SkRect::MakeWH(100, 100), nullptr);
+        REPORTER_ASSERT(reporter, !image->unique());
+
+        Tally tally;
+        tally.apply(record);
+
+        REPORTER_ASSERT(reporter, 1 == tally.count<SkRecords::DrawImageRect>());
     }
-    // the recorder destructor should have released us (back to unique)
-    REPORTER_ASSERT(r, pic->unique());
+    REPORTER_ASSERT(reporter, image->unique());
 }
-
-DEF_TEST(Recorder_IsDrawingToLayer, r) {
-    SkRecord record;
-    SkRecorder recorder(&record, 100, 100);
-
-    // We'll save, saveLayer, save, and saveLayer, then restore them all,
-    // checking that isDrawingToLayer() is correct at each step.
-
-    REPORTER_ASSERT(r, !recorder.isDrawingToLayer());
-    recorder.save();
-        REPORTER_ASSERT(r, !recorder.isDrawingToLayer());
-        recorder.saveLayer(NULL, NULL);
-            REPORTER_ASSERT(r, recorder.isDrawingToLayer());
-            recorder.save();
-                REPORTER_ASSERT(r, recorder.isDrawingToLayer());
-                recorder.saveLayer(NULL, NULL);
-                    REPORTER_ASSERT(r, recorder.isDrawingToLayer());
-                recorder.restore();
-                REPORTER_ASSERT(r, recorder.isDrawingToLayer());
-            recorder.restore();
-            REPORTER_ASSERT(r, recorder.isDrawingToLayer());
-        recorder.restore();
-        REPORTER_ASSERT(r, !recorder.isDrawingToLayer());
-    recorder.restore();
-    REPORTER_ASSERT(r, !recorder.isDrawingToLayer());
-}
-
diff --git a/src/third_party/skia/tests/RecordingTest.cpp b/src/third_party/skia/tests/RecordingTest.cpp
deleted file mode 100644
index 0066556..0000000
--- a/src/third_party/skia/tests/RecordingTest.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Test.h"
-
-#include "../include/record/SkRecording.h"
-
-// Minimally exercise the public SkRecording API.
-
-DEF_TEST(SkRecording, r) {
-    EXPERIMENTAL::SkRecording recording(1920, 1080);
-
-    // Some very exciting commands here.
-    recording.canvas()->clipRect(SkRect::MakeWH(320, 240));
-
-    SkAutoTDelete<const EXPERIMENTAL::SkPlayback> playback(recording.releasePlayback());
-
-    SkCanvas target;
-    playback->draw(&target);
-
-    // Here's another recording we never call releasePlayback().
-    // However pointless, this should be safe.
-    EXPERIMENTAL::SkRecording pointless(1920, 1080);
-    pointless.canvas()->clipRect(SkRect::MakeWH(320, 240));
-}
diff --git a/src/third_party/skia/tests/RecordingXfermodeTest.cpp b/src/third_party/skia/tests/RecordingXfermodeTest.cpp
index 8da81b3..20dccc6 100644
--- a/src/third_party/skia/tests/RecordingXfermodeTest.cpp
+++ b/src/third_party/skia/tests/RecordingXfermodeTest.cpp
@@ -7,12 +7,13 @@
 
 #include "Test.h"
 
+#include "../include/core/SkBitmap.h"
 #include "../include/core/SkCanvas.h"
 #include "../include/core/SkPicture.h"
 #include "../include/core/SkStream.h"
 #include "../include/core/SkString.h"
-#include "../include/record/SkRecording.h"
 #include "../include/core/SkPictureRecorder.h"
+#include "../src/core/SkBlendModePriv.h"
 #include <cstring>
 
 // Verify that replay of a recording into a clipped canvas
@@ -20,25 +21,22 @@
 // This arose from http://crbug.com/401593 which has
 // https://code.google.com/p/skia/issues/detail?id=1291 as its root cause.
 
-
 namespace {
 
 class Drawer {
  public:
-    explicit Drawer()
-            : fImageInfo(SkImageInfo::MakeN32Premul(200,100))
-    {
-        fCircleBM.allocPixels( SkImageInfo::MakeN32Premul(100,100) );
+    explicit Drawer() : fImageInfo(SkImageInfo::MakeN32Premul(200, 100)) {
+        fCircleBM.allocPixels(SkImageInfo::MakeN32Premul(100, 100));
         SkCanvas canvas(fCircleBM);
         canvas.clear(0xffffffff);
         SkPaint circlePaint;
         circlePaint.setColor(0xff000000);
-        canvas.drawCircle(50,50,50,circlePaint);
+        canvas.drawCircle(50, 50, 50, circlePaint);
     }
 
     const SkImageInfo& imageInfo() const { return fImageInfo; }
 
-    void draw(SkCanvas* canvas, const SkRect& clipRect, SkXfermode::Mode mode) const {
+    void draw(SkCanvas* canvas, const SkRect& clipRect, SkBlendMode mode) const {
         SkPaint greenPaint;
         greenPaint.setColor(0xff008000);
         SkPaint blackPaint;
@@ -47,16 +45,17 @@
         whitePaint.setColor(0xffffffff);
         SkPaint layerPaint;
         layerPaint.setColor(0xff000000);
-        layerPaint.setXfermodeMode(mode);
-        SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fImageInfo.width()),SkIntToScalar(fImageInfo.height())));
+        layerPaint.setBlendMode(mode);
+        SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fImageInfo.width()),
+                                         SkIntToScalar(fImageInfo.height())));
 
         canvas->clipRect(clipRect);
         canvas->clear(0xff000000);
 
-        canvas->saveLayer(NULL,&blackPaint);
-            canvas->drawRect(canvasRect,greenPaint);
-            canvas->saveLayer(NULL,&layerPaint);
-                canvas->drawBitmapRect(fCircleBM,SkRect::MakeXYWH(20,20,60,60),&blackPaint);
+        canvas->saveLayer(nullptr, &blackPaint);
+            canvas->drawRect(canvasRect, greenPaint);
+            canvas->saveLayer(nullptr, &layerPaint);
+                canvas->drawBitmapRect(fCircleBM, SkRect::MakeXYWH(20,20,60,60), &blackPaint);
             canvas->restore();
         canvas->restore();
     }
@@ -69,24 +68,20 @@
 class RecordingStrategy {
  public:
     virtual ~RecordingStrategy() {}
-    virtual void init(const SkImageInfo&) = 0;
     virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
                                             const SkRect& intoClip,
-                                            SkXfermode::Mode) = 0;
+                                            SkBlendMode) = 0;
 };
 
 class BitmapBackedCanvasStrategy : public RecordingStrategy {
     // This version just draws into a bitmap-backed canvas.
  public:
-    BitmapBackedCanvasStrategy() {}
-
-    virtual void init(const SkImageInfo& imageInfo) {
+    BitmapBackedCanvasStrategy(const SkImageInfo& imageInfo) {
         fBitmap.allocPixels(imageInfo);
     }
 
-    virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
-                                            const SkRect& intoClip,
-                                            SkXfermode::Mode mode) {
+    const SkBitmap& recordAndReplay(const Drawer& drawer, const SkRect& intoClip,
+                                    SkBlendMode mode) override {
         SkCanvas canvas(fBitmap);
         canvas.clear(0xffffffff);
         // Note that the scene is drawn just into the clipped region!
@@ -99,70 +94,27 @@
     SkBitmap fBitmap;
 };
 
-class DeprecatedRecorderStrategy : public RecordingStrategy {
-    // This version draws the entire scene into an SkPictureRecorder,
-    // using the deprecated recording backend.
+class PictureStrategy : public RecordingStrategy {
+    // This version draws the entire scene into an SkPictureRecorder.
     // Then it then replays the scene through a clip rectangle.
     // This backend proved to be buggy.
  public:
-    DeprecatedRecorderStrategy() {}
-
-    virtual void init(const SkImageInfo& imageInfo) {
+    PictureStrategy(const SkImageInfo& imageInfo) {
         fBitmap.allocPixels(imageInfo);
-        fWidth = imageInfo.width();
-        fHeight= imageInfo.height();
+        fWidth  = imageInfo.width();
+        fHeight = imageInfo.height();
     }
 
-    virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
-                                            const SkRect& intoClip,
-                                            SkXfermode::Mode mode) {
-        SkTileGridFactory::TileGridInfo tileGridInfo = { {100,100}, {0,0}, {0,0} };
-        SkTileGridFactory factory(tileGridInfo);
+    const SkBitmap& recordAndReplay(const Drawer& drawer, const SkRect& intoClip,
+                                    SkBlendMode mode) override {
+        SkRTreeFactory factory;
         SkPictureRecorder recorder;
         SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight)));
-        SkCanvas* canvas = recorder.DEPRECATED_beginRecording( SkIntToScalar(fWidth), SkIntToScalar(fHeight), &factory);
+        SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(fWidth),
+                                                   SkIntToScalar(fHeight),
+                                                   &factory);
         drawer.draw(canvas, canvasRect, mode);
-        SkAutoTDelete<SkPicture> picture(recorder.endRecording());
-
-        SkCanvas replayCanvas(fBitmap);
-        replayCanvas.clear(0xffffffff);
-        replayCanvas.clipRect(intoClip);
-        picture->playback(&replayCanvas);
-
-        return fBitmap;
-    }
-
- private:
-    SkBitmap fBitmap;
-    int fWidth;
-    int fHeight;
-};
-
-class NewRecordingStrategy : public RecordingStrategy {
-    // This version draws the entire scene into an SkPictureRecorder,
-    // using the new recording backend.
-    // Then it then replays the scene through a clip rectangle.
-    // This backend proved to be buggy.
- public:
-    NewRecordingStrategy() {}
-
-    virtual void init(const SkImageInfo& imageInfo) {
-        fBitmap.allocPixels(imageInfo);
-        fWidth = imageInfo.width();
-        fHeight= imageInfo.height();
-    }
-
-    virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
-                                            const SkRect& intoClip,
-                                            SkXfermode::Mode mode) {
-        SkTileGridFactory::TileGridInfo tileGridInfo = { {100,100}, {0,0}, {0,0} };
-        SkTileGridFactory factory(tileGridInfo);
-        SkPictureRecorder recorder;
-        SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight)));
-        SkCanvas* canvas = recorder.EXPERIMENTAL_beginRecording( SkIntToScalar(fWidth), SkIntToScalar(fHeight), &factory);
-
-        drawer.draw(canvas, canvasRect, mode);
-        SkAutoTDelete<SkPicture> picture(recorder.endRecording());
+        sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
 
         SkCanvas replayCanvas(fBitmap);
         replayCanvas.clear(0xffffffff);
@@ -177,64 +129,45 @@
     int fHeight;
 };
 
-}
-
+} // namespace
 
 
 DEF_TEST(SkRecordingAccuracyXfermode, reporter) {
 #define FINEGRAIN 0
-
     const Drawer drawer;
 
-    BitmapBackedCanvasStrategy golden; // This is the expected result.
-    DeprecatedRecorderStrategy deprecatedRecording;
-    NewRecordingStrategy newRecording;
-
-    golden.init(drawer.imageInfo());
-    deprecatedRecording.init(drawer.imageInfo());
-    newRecording.init(drawer.imageInfo());
+    BitmapBackedCanvasStrategy golden(drawer.imageInfo());
+    PictureStrategy picture(drawer.imageInfo());
 
 #if !FINEGRAIN
     unsigned numErrors = 0;
     SkString errors;
 #endif
 
-    for (int iMode = 0; iMode < int(SkXfermode::kLastMode) ; iMode++ ) {
-        const SkRect& clip = SkRect::MakeXYWH(100,0,100,100);
-        SkXfermode::Mode mode = SkXfermode::Mode(iMode);
+    for (int iMode = 0; iMode < int(SkBlendMode::kLastMode); iMode++) {
+        const SkRect& clip = SkRect::MakeXYWH(100, 0, 100, 100);
+        SkBlendMode mode = SkBlendMode(iMode);
 
         const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode);
-        const SkBitmap& deprecatedBM = deprecatedRecording.recordAndReplay(drawer, clip, mode);
-        const SkBitmap& newRecordingBM = newRecording.recordAndReplay(drawer, clip, mode);
+        const SkBitmap& pictureBM = picture.recordAndReplay(drawer, clip, mode);
 
         size_t pixelsSize = goldenBM.getSize();
-        REPORTER_ASSERT( reporter, pixelsSize == deprecatedBM.getSize() );
-        REPORTER_ASSERT( reporter, pixelsSize == newRecordingBM.getSize() );
+        REPORTER_ASSERT(reporter, pixelsSize == pictureBM.getSize());
 
         // The pixel arrays should match.
 #if FINEGRAIN
-        REPORTER_ASSERT_MESSAGE( reporter,
-                                 0==memcmp( goldenBM.getPixels(), deprecatedBM.getPixels(), pixelsSize ),
-                                 "Tiled bitmap is wrong");
-        REPORTER_ASSERT_MESSAGE( reporter,
-                                 0==memcmp( goldenBM.getPixels(), recordingBM.getPixels(), pixelsSize ),
-                                 "SkRecorder bitmap is wrong");
+        REPORTER_ASSERT(reporter,
+                        0 == memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize));
 #else
-        if ( memcmp( goldenBM.getPixels(), deprecatedBM.getPixels(), pixelsSize ) ) {
+        if (memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize)) {
             numErrors++;
-            SkString str;
-            str.printf("For SkXfermode %d %s:    Deprecated recorder bitmap is wrong\n", iMode, SkXfermode::ModeName(mode));
-            errors.append(str);
-        }
-        if ( memcmp( goldenBM.getPixels(), newRecordingBM.getPixels(), pixelsSize ) ) {
-            numErrors++;
-            SkString str;
-            str.printf("For SkXfermode %d %s:    SkPictureRecorder bitmap is wrong\n", iMode, SkXfermode::ModeName(mode));
-            errors.append(str);
+            errors.appendf("For SkXfermode %d %s:    SkPictureRecorder bitmap is wrong\n",
+                           iMode, SkBlendMode_Name(mode));
         }
 #endif
     }
+
 #if !FINEGRAIN
-    REPORTER_ASSERT_MESSAGE( reporter, 0==numErrors, errors.c_str() );
+    REPORTER_ASSERT_MESSAGE(reporter, 0 == numErrors, errors.c_str());
 #endif
 }
diff --git a/src/third_party/skia/tests/RectTest.cpp b/src/third_party/skia/tests/RectTest.cpp
new file mode 100644
index 0000000..820586c
--- /dev/null
+++ b/src/third_party/skia/tests/RectTest.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkRect.h"
+#include "Test.h"
+
+static bool has_green_pixels(const SkBitmap& bm) {
+    for (int j = 0; j < bm.height(); ++j) {
+        for (int i = 0; i < bm.width(); ++i) {
+            if (SkColorGetG(bm.getColor(i, j))) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+static void test_stroke_width_clipping(skiatest::Reporter* reporter) {
+    SkBitmap bm;
+    bm.allocN32Pixels(100, 10);
+    bm.eraseColor(SK_ColorTRANSPARENT);
+
+    SkCanvas canvas(bm);
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(10);
+    paint.setColor(0xff00ff00);
+
+    // clip out the left half of our canvas
+    canvas.clipRect(SkRect::MakeXYWH(51, 0, 49, 100));
+
+    // no stroke bleed should be visible
+    canvas.drawRect(SkRect::MakeWH(44, 100), paint);
+    REPORTER_ASSERT(reporter, !has_green_pixels(bm));
+
+    // right stroke edge should bleed into the visible area
+    canvas.scale(2, 2);
+    canvas.drawRect(SkRect::MakeWH(22, 50), paint);
+    REPORTER_ASSERT(reporter, has_green_pixels(bm));
+}
+
+static void test_skbug4406(skiatest::Reporter* reporter) {
+    SkBitmap bm;
+    bm.allocN32Pixels(10, 10);
+    bm.eraseColor(SK_ColorTRANSPARENT);
+
+    SkCanvas canvas(bm);
+    const SkRect r = { 1.5f, 1, 3.5f, 3 };
+    // draw filled green rect first
+    SkPaint paint;
+    paint.setStyle(SkPaint::kFill_Style);
+    paint.setColor(0xff00ff00);
+    paint.setStrokeWidth(1);
+    paint.setAntiAlias(true);
+    canvas.drawRect(r, paint);
+
+    // paint black with stroke rect (that asserts in bug 4406)
+    // over the filled rect, it should cover it
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setColor(0xff000000);
+    paint.setStrokeWidth(1);
+    canvas.drawRect(r, paint);
+    REPORTER_ASSERT(reporter, !has_green_pixels(bm));
+
+    // do it again with thinner stroke
+    paint.setStyle(SkPaint::kFill_Style);
+    paint.setColor(0xff00ff00);
+    paint.setStrokeWidth(1);
+    paint.setAntiAlias(true);
+    canvas.drawRect(r, paint);
+    // paint black with stroke rect (that asserts in bug 4406)
+    // over the filled rect, it doesnt cover it completelly with thinner stroke
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setColor(0xff000000);
+    paint.setStrokeWidth(0.99f);
+    canvas.drawRect(r, paint);
+    REPORTER_ASSERT(reporter, has_green_pixels(bm));
+}
+
+DEF_TEST(Rect, reporter) {
+    test_stroke_width_clipping(reporter);
+    test_skbug4406(reporter);
+}
diff --git a/src/third_party/skia/tests/RectangleTextureTest.cpp b/src/third_party/skia/tests/RectangleTextureTest.cpp
new file mode 100644
index 0000000..705f585
--- /dev/null
+++ b/src/third_party/skia/tests/RectangleTextureTest.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "TestUtils.h"
+
+#if SK_SUPPORT_GPU
+#include "GrClip.h"
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrRenderTargetContext.h"
+#include "GrTest.h"
+#include "gl/GLTestContext.h"
+#include "gl/GrGLGpu.h"
+#include "gl/GrGLUtil.h"
+
+// skbug.com/5932
+static void test_basic_draw_as_src(skiatest::Reporter* reporter, GrContext* context,
+                                   sk_sp<GrTextureProxy> rectProxy, uint32_t expectedPixelValues[]) {
+    sk_sp<GrRenderTargetContext> rtContext(
+            context->makeDeferredRenderTargetContext(SkBackingFit::kExact, rectProxy->width(),
+                                                     rectProxy->height(), rectProxy->config(),
+                                                     nullptr));
+    for (auto filter : {GrSamplerParams::kNone_FilterMode,
+                        GrSamplerParams::kBilerp_FilterMode,
+                        GrSamplerParams::kMipMap_FilterMode}) {
+        rtContext->clear(nullptr, 0xDDCCBBAA, true);
+        sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(
+                                                        rectProxy,
+                                                        nullptr,
+                                                        SkMatrix::I(), filter));
+        GrPaint paint;
+        paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+        paint.addColorFragmentProcessor(std::move(fp));
+        rtContext->drawPaint(GrNoClip(), std::move(paint), SkMatrix::I());
+        test_read_pixels(reporter, rtContext.get(), expectedPixelValues,
+                         "RectangleTexture-basic-draw");
+    }
+}
+
+static void test_clear(skiatest::Reporter* reporter, GrSurfaceContext* rectContext) {
+    if (GrRenderTargetContext* rtc = rectContext->asRenderTargetContext()) {
+        // Clear the whole thing.
+        GrColor color0 = GrColorPackRGBA(0xA, 0xB, 0xC, 0xD);
+        rtc->clear(nullptr, color0, false);
+
+        int w = rtc->width();
+        int h = rtc->height();
+        int pixelCnt = w * h;
+        SkAutoTMalloc<uint32_t> expectedPixels(pixelCnt);
+
+        // The clear color is a GrColor, our readback is to kRGBA_8888, which may be different.
+        uint32_t expectedColor0 = 0;
+        uint8_t* expectedBytes0 = SkTCast<uint8_t*>(&expectedColor0);
+        expectedBytes0[0] = GrColorUnpackR(color0);
+        expectedBytes0[1] = GrColorUnpackG(color0);
+        expectedBytes0[2] = GrColorUnpackB(color0);
+        expectedBytes0[3] = GrColorUnpackA(color0);
+        for (int i = 0; i < rtc->width() * rtc->height(); ++i) {
+            expectedPixels.get()[i] = expectedColor0;
+        }
+
+        // Clear the the top to a different color.
+        GrColor color1 = GrColorPackRGBA(0x1, 0x2, 0x3, 0x4);
+        SkIRect rect = SkIRect::MakeWH(w, h/2);
+        rtc->clear(&rect, color1, false);
+
+        uint32_t expectedColor1 = 0;
+        uint8_t* expectedBytes1 = SkTCast<uint8_t*>(&expectedColor1);
+        expectedBytes1[0] = GrColorUnpackR(color1);
+        expectedBytes1[1] = GrColorUnpackG(color1);
+        expectedBytes1[2] = GrColorUnpackB(color1);
+        expectedBytes1[3] = GrColorUnpackA(color1);
+
+        for (int y = 0; y < h/2; ++y) {
+            for (int x = 0; x < w; ++x) {
+                expectedPixels.get()[y * h + x] = expectedColor1;
+            }
+        }
+
+        test_read_pixels(reporter, rtc, expectedPixels.get(), "RectangleTexture-clear");
+    }
+}
+
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(RectangleTexture, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    sk_gpu_test::GLTestContext* glContext = ctxInfo.glContext();
+    static const int kWidth = 13;
+    static const int kHeight = 13;
+
+    GrColor pixels[kWidth * kHeight];
+    for (int y = 0; y < kHeight; ++y) {
+        for (int x = 0; x < kWidth; ++x) {
+            pixels[y * kWidth + x] = y * kWidth + x;
+        }
+    }
+
+    for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) {
+        bool useBLOrigin = kBottomLeft_GrSurfaceOrigin == origin;
+
+        GrGLuint rectTexID = glContext->createTextureRectangle(kWidth, kHeight, GR_GL_RGBA,
+                                                               GR_GL_RGBA, GR_GL_UNSIGNED_BYTE,
+                                                               pixels);
+
+        if (!rectTexID) {
+            return;
+        }
+
+        // Let GrContext know that we messed with the GL context directly.
+        context->resetContext();
+
+        // Wrap the rectangle texture ID in a GrTexture
+        GrGLTextureInfo rectangleInfo;
+        rectangleInfo.fID = rectTexID;
+        rectangleInfo.fTarget = GR_GL_TEXTURE_RECTANGLE;
+
+        GrBackendTexture rectangleTex(kWidth, kHeight, kRGBA_8888_GrPixelConfig, rectangleInfo);
+
+        GrColor refPixels[kWidth * kHeight];
+        for (int y = 0; y < kHeight; ++y) {
+            for (int x = 0; x < kWidth; ++x) {
+                int y0 = useBLOrigin ? kHeight - y - 1 : y;
+                refPixels[y * kWidth + x] = pixels[y0 * kWidth + x];
+            }
+        }
+
+        sk_sp<GrTextureProxy> rectProxy = GrSurfaceProxy::MakeWrappedBackend(context,
+                                                                             rectangleTex,
+                                                                             origin);
+        if (!rectProxy) {
+            ERRORF(reporter, "Error creating proxy for rectangle texture.");
+            GR_GL_CALL(glContext->gl(), DeleteTextures(1, &rectTexID));
+            continue;
+        }
+
+        test_basic_draw_as_src(reporter, context, rectProxy, refPixels);
+
+        // Test copy to both a texture and RT
+        test_copy_from_surface(reporter, context, rectProxy.get(), refPixels,
+                               false, "RectangleTexture-copy-from");
+
+        sk_sp<GrSurfaceContext> rectContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                                    std::move(rectProxy), nullptr);
+        SkASSERT(rectContext);
+
+        test_read_pixels(reporter, rectContext.get(), refPixels, "RectangleTexture-read");
+
+        test_copy_to_surface(reporter, context->resourceProvider(),
+                              rectContext.get(), "RectangleTexture-copy-to");
+
+        test_write_pixels(reporter, rectContext.get(), true, "RectangleTexture-write");
+
+        test_clear(reporter, rectContext.get());
+
+        GR_GL_CALL(glContext->gl(), DeleteTextures(1, &rectTexID));
+    }
+}
+
+#endif
diff --git a/src/third_party/skia/tests/RefCntTest.cpp b/src/third_party/skia/tests/RefCntTest.cpp
index bd4f348..4bad342 100644
--- a/src/third_party/skia/tests/RefCntTest.cpp
+++ b/src/third_party/skia/tests/RefCntTest.cpp
@@ -6,68 +6,11 @@
  */
 
 #include "SkRefCnt.h"
-#include "SkTRefArray.h"
 #include "SkThreadUtils.h"
 #include "SkTypes.h"
 #include "SkWeakRefCnt.h"
 #include "Test.h"
 
-class InstCounterClass {
-public:
-    InstCounterClass() { fCount = gInstCounter++; }
-    InstCounterClass(const InstCounterClass& src) {
-        fCount = src.fCount;
-        gInstCounter += 1;
-    }
-    virtual ~InstCounterClass() { gInstCounter -= 1; }
-
-    static int gInstCounter;
-    int fCount;
-};
-
-int InstCounterClass::gInstCounter;
-
-static void test_refarray(skiatest::Reporter* reporter) {
-    REPORTER_ASSERT(reporter, 0 == InstCounterClass::gInstCounter);
-
-    const int N = 10;
-    SkTRefArray<InstCounterClass>* array = SkTRefArray<InstCounterClass>::Create(N);
-
-    REPORTER_ASSERT(reporter, 1 == array->getRefCnt());
-    REPORTER_ASSERT(reporter, N == array->count());
-
-    REPORTER_ASSERT(reporter, N == InstCounterClass::gInstCounter);
-    array->unref();
-    REPORTER_ASSERT(reporter, 0 == InstCounterClass::gInstCounter);
-
-    // Now test the copy factory
-
-    int i;
-    InstCounterClass* src = new InstCounterClass[N];
-    REPORTER_ASSERT(reporter, N == InstCounterClass::gInstCounter);
-    for (i = 0; i < N; ++i) {
-        REPORTER_ASSERT(reporter, i == src[i].fCount);
-    }
-
-    array = SkTRefArray<InstCounterClass>::Create(src, N);
-    REPORTER_ASSERT(reporter, 1 == array->getRefCnt());
-    REPORTER_ASSERT(reporter, N == array->count());
-
-    REPORTER_ASSERT(reporter, 2*N == InstCounterClass::gInstCounter);
-    for (i = 0; i < N; ++i) {
-        REPORTER_ASSERT(reporter, i == (*array)[i].fCount);
-    }
-
-    delete[] src;
-    REPORTER_ASSERT(reporter, N == InstCounterClass::gInstCounter);
-
-    for (i = 0; i < N; ++i) {
-        REPORTER_ASSERT(reporter, i == (*array)[i].fCount);
-    }
-    array->unref();
-    REPORTER_ASSERT(reporter, 0 == InstCounterClass::gInstCounter);
-}
-
 static void bounce_ref(void* data) {
     SkRefCnt* ref = static_cast<SkRefCnt*>(data);
     for (int i = 0; i < 100000; ++i) {
@@ -82,16 +25,13 @@
     SkThread thing1(bounce_ref, ref);
     SkThread thing2(bounce_ref, ref);
 
-    thing1.setProcessorAffinity(0);
-    thing2.setProcessorAffinity(23);
-
-    SkASSERT(thing1.start());
-    SkASSERT(thing2.start());
+    SkAssertResult(thing1.start());
+    SkAssertResult(thing2.start());
 
     thing1.join();
     thing2.join();
 
-    REPORTER_ASSERT(reporter, ref->getRefCnt() == 1);
+    REPORTER_ASSERT(reporter, ref->unique());
     ref->unref();
 }
 
@@ -120,28 +60,341 @@
     SkThread thing3(bounce_weak_ref, ref);
     SkThread thing4(bounce_weak_weak_ref, ref);
 
-    thing1.setProcessorAffinity(0);
-    thing2.setProcessorAffinity(23);
-    thing3.setProcessorAffinity(2);
-    thing4.setProcessorAffinity(17);
-
-    SkASSERT(thing1.start());
-    SkASSERT(thing2.start());
-    SkASSERT(thing3.start());
-    SkASSERT(thing4.start());
+    SkAssertResult(thing1.start());
+    SkAssertResult(thing2.start());
+    SkAssertResult(thing3.start());
+    SkAssertResult(thing4.start());
 
     thing1.join();
     thing2.join();
     thing3.join();
     thing4.join();
 
-    REPORTER_ASSERT(reporter, ref->getRefCnt() == 1);
-    REPORTER_ASSERT(reporter, ref->getWeakCnt() == 1);
+    REPORTER_ASSERT(reporter, ref->unique());
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, ref->getWeakCnt() == 1));
     ref->unref();
 }
 
 DEF_TEST(RefCnt, reporter) {
     test_refCnt(reporter);
     test_weakRefCnt(reporter);
-    test_refarray(reporter);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static int gRefCounter;
+static int gUnrefCounter;
+static int gNewCounter;
+static int gDeleteCounter;
+
+#define check(reporter, ref, unref, make, kill)             \
+    REPORTER_ASSERT(reporter, gRefCounter == ref);          \
+    REPORTER_ASSERT(reporter, gUnrefCounter == unref);      \
+    REPORTER_ASSERT(reporter, gNewCounter == make);         \
+    REPORTER_ASSERT(reporter, gDeleteCounter == kill);
+
+
+class Effect {
+public:
+    Effect() : fRefCnt(1) {
+        gNewCounter += 1;
+    }
+    virtual ~Effect() {}
+
+    int fRefCnt;
+
+    void ref() {
+        gRefCounter += 1;
+        fRefCnt += 1;
+    }
+    void unref() {
+        gUnrefCounter += 1;
+
+        SkASSERT(fRefCnt > 0);
+        if (0 == --fRefCnt) {
+            gDeleteCounter += 1;
+            delete this;
+        }
+    }
+
+    int* method() const { return new int; }
+};
+
+static sk_sp<Effect> Create() {
+    return sk_make_sp<Effect>();
+}
+
+class Paint {
+public:
+    sk_sp<Effect> fEffect;
+
+    const sk_sp<Effect>& get() const { return fEffect; }
+
+    void set(sk_sp<Effect> value) {
+        fEffect = std::move(value);
+    }
+};
+
+struct EffectImpl : public Effect {
+    ~EffectImpl() override {}
+
+    static sk_sp<EffectImpl> Create() {
+        return sk_sp<EffectImpl>(new EffectImpl);
+    }
+    int fValue;
+};
+static sk_sp<Effect> make_effect() {
+    auto foo = EffectImpl::Create();
+    foo->fValue = 42;
+    return std::move(foo);
+}
+
+static void reset_counters() {
+    gRefCounter = 0;
+    gUnrefCounter = 0;
+    gNewCounter = 0;
+    gDeleteCounter = 0;
+}
+DEF_TEST(sk_sp, reporter) {
+    reset_counters();
+
+    Paint paint;
+    REPORTER_ASSERT(reporter, paint.fEffect.get() == nullptr);
+    REPORTER_ASSERT(reporter, !paint.get());
+    check(reporter, 0, 0, 0, 0);
+
+    paint.set(Create());
+    check(reporter, 0, 0, 1, 0);
+    REPORTER_ASSERT(reporter, paint.fEffect.get()->fRefCnt == 1);
+
+    if (paint.get()) {
+        REPORTER_ASSERT(reporter, true);
+    } else {
+        REPORTER_ASSERT(reporter, false);
+    }
+    if (!paint.get()) {
+        REPORTER_ASSERT(reporter, false);
+    } else {
+        REPORTER_ASSERT(reporter, true);
+    }
+
+    paint.set(nullptr);
+    check(reporter, 0, 1, 1, 1);
+
+    if (paint.get()) {
+        REPORTER_ASSERT(reporter, false);
+    } else {
+        REPORTER_ASSERT(reporter, true);
+    }
+    if (!paint.get()) {
+        REPORTER_ASSERT(reporter, true);
+    } else {
+        REPORTER_ASSERT(reporter, false);
+    }
+
+    auto e = Create();
+    REPORTER_ASSERT(reporter, sizeof(e) == sizeof(void*));
+
+    check(reporter, 0, 1, 2, 1);
+    paint.set(e);
+    check(reporter, 1, 1, 2, 1);
+    REPORTER_ASSERT(reporter, paint.fEffect.get()->fRefCnt == 2);
+
+    Paint paint2;
+    paint2.set(paint.get());
+    check(reporter, 2, 1, 2, 1);
+    REPORTER_ASSERT(reporter, paint.fEffect.get()->fRefCnt == 3);
+
+    // Test sk_sp::operator->
+    delete paint.get()->method();
+    check(reporter, 2, 1, 2, 1);
+
+    // Test sk_sp::operator*
+    delete (*paint.get()).method();
+    check(reporter, 2, 1, 2, 1);
+
+    paint.set(nullptr);
+    e = nullptr;
+    paint2.set(nullptr);
+    check(reporter, 2, 4, 2, 2);
+
+    reset_counters();
+    {
+        // Test convertible sk_sp assignment.
+        check(reporter, 0, 0, 0, 0);
+        sk_sp<Effect> foo(nullptr);
+        REPORTER_ASSERT(reporter, !foo);
+        foo = make_effect();
+        REPORTER_ASSERT(reporter, foo);
+        check(reporter, 0, 0, 1, 0);
+    }
+    check(reporter, 0, 1, 1, 1);
+
+    // Test passing convertible rvalue into funtion.
+    reset_counters();
+    paint.set(EffectImpl::Create());
+    check(reporter, 0, 0, 1, 0);
+    paint.set(nullptr);
+    check(reporter, 0, 1, 1, 1);
+
+    reset_counters();
+    auto baz = EffectImpl::Create();
+    check(reporter, 0, 0, 1, 0);
+    paint.set(std::move(baz));
+    check(reporter, 0, 0, 1, 0);
+    REPORTER_ASSERT(reporter, !baz);
+    paint.set(nullptr);
+    check(reporter, 0, 1, 1, 1);
+
+    reset_counters();
+    {
+        // test comparison operator with convertible type.
+        sk_sp<EffectImpl> bar1 = EffectImpl::Create();
+        sk_sp<Effect> bar2(bar1);  // convertible copy constructor
+        check(reporter, 1, 0, 1, 0);
+        REPORTER_ASSERT(reporter, bar1);
+        REPORTER_ASSERT(reporter, bar2);
+        REPORTER_ASSERT(reporter, bar1 == bar2);
+        REPORTER_ASSERT(reporter, bar2 == bar1);
+        REPORTER_ASSERT(reporter, !(bar1 != bar2));
+        REPORTER_ASSERT(reporter, !(bar2 != bar1));
+        sk_sp<Effect> bar3(nullptr);
+        bar3 = bar1;  // convertible copy assignment
+        check(reporter, 2, 0, 1, 0);
+
+    }
+    check(reporter, 2, 3, 1, 1);
+
+    // test passing convertible copy into funtion.
+    reset_counters();
+    baz = EffectImpl::Create();
+    check(reporter, 0, 0, 1, 0);
+    paint.set(baz);
+    check(reporter, 1, 0, 1, 0);
+    baz = nullptr;
+    check(reporter, 1, 1, 1, 0);
+    paint.set(nullptr);
+    check(reporter, 1, 2, 1, 1);
+
+    {
+        sk_sp<SkRefCnt> empty;
+        sk_sp<SkRefCnt> notEmpty = sk_make_sp<SkRefCnt>();
+        REPORTER_ASSERT(reporter, empty == sk_sp<SkRefCnt>());
+
+        REPORTER_ASSERT(reporter, notEmpty != empty);
+        REPORTER_ASSERT(reporter, empty != notEmpty);
+
+        REPORTER_ASSERT(reporter, nullptr == empty);
+        REPORTER_ASSERT(reporter, empty == nullptr);
+        REPORTER_ASSERT(reporter, empty == empty);
+
+        REPORTER_ASSERT(reporter, nullptr <= empty);
+        REPORTER_ASSERT(reporter, empty <= nullptr);
+        REPORTER_ASSERT(reporter, empty <= empty);
+
+        REPORTER_ASSERT(reporter, nullptr >= empty);
+        REPORTER_ASSERT(reporter, empty >= nullptr);
+        REPORTER_ASSERT(reporter, empty >= empty);
+    }
+
+    {
+        sk_sp<SkRefCnt> a = sk_make_sp<SkRefCnt>();
+        sk_sp<SkRefCnt> b = sk_make_sp<SkRefCnt>();
+        REPORTER_ASSERT(reporter, a != b);
+        REPORTER_ASSERT(reporter, (a < b) != (b < a));
+        REPORTER_ASSERT(reporter, (b > a) != (a > b));
+        REPORTER_ASSERT(reporter, (a <= b) != (b <= a));
+        REPORTER_ASSERT(reporter, (b >= a) != (a >= b));
+
+        REPORTER_ASSERT(reporter, a == a);
+        REPORTER_ASSERT(reporter, a <= a);
+        REPORTER_ASSERT(reporter, a >= a);
+    }
+
+    // http://wg21.cmeerw.net/lwg/issue998
+    {
+        class foo : public SkRefCnt {
+        public:
+            foo() : bar(this) {}
+            void reset() { bar.reset(); }
+        private:
+            sk_sp<foo> bar;
+        };
+        // The following should properly delete the object and not cause undefined behavior.
+        // This is an ugly example, but the same issue can arise in more subtle ways.
+        (new foo)->reset();
+    }
+
+    // https://crrev.com/0d4ef2583a6f19c3e61be04d36eb1a60b133832c
+    {
+        struct StructB;
+        struct StructA : public SkRefCnt {
+            sk_sp<StructB> b;
+        };
+
+        struct StructB : public SkRefCnt {
+            sk_sp<StructA> a;
+            ~StructB() override {} // Some clang versions don't emit this implicitly.
+        };
+
+        // Create a reference cycle.
+        StructA* a = new StructA;
+        a->b.reset(new StructB);
+        a->b->a.reset(a);
+
+        // Break the cycle by calling reset(). This will cause |a| (and hence, |a.b|)
+        // to be deleted before the call to reset() returns. This tests that the
+        // implementation of sk_sp::reset() doesn't access |this| after it
+        // deletes the underlying pointer. This behaviour is consistent with the
+        // definition of unique_ptr::reset in C++11.
+        a->b.reset();
+    }
+}
+
+namespace {
+struct FooAbstract : public SkRefCnt {
+    virtual void f() = 0;
+};
+struct FooConcrete : public FooAbstract {
+    void f() override {}
+};
+}
+static sk_sp<FooAbstract> make_foo() {
+    // can not cast FooConcrete to FooAbstract.
+    // can cast FooConcrete* to FooAbstract*.
+    return sk_make_sp<FooConcrete>();
+}
+DEF_TEST(sk_make_sp, r) {
+    auto x = make_foo();
+}
+
+// Test that reset() "adopts" ownership from the caller, even if we are given the same ptr twice
+//
+DEF_TEST(sk_sp_reset, r) {
+    SkRefCnt* rc = new SkRefCnt;
+    REPORTER_ASSERT(r, rc->unique());
+
+    sk_sp<SkRefCnt> sp;
+    sp.reset(rc);
+    // We have transfered our ownership over to sp
+    REPORTER_ASSERT(r, rc->unique());
+
+    rc->ref();  // now "rc" is also an owner
+    REPORTER_ASSERT(r, !rc->unique());
+
+    sp.reset(rc);   // this should transfer our ownership over to sp
+    REPORTER_ASSERT(r, rc->unique());
+}
+
+DEF_TEST(sk_sp_ref, r) {
+    SkRefCnt* rc = new SkRefCnt;
+    REPORTER_ASSERT(r, rc->unique());
+
+    {
+        sk_sp<SkRefCnt> sp = sk_ref_sp(rc);
+        REPORTER_ASSERT(r, !rc->unique());
+    }
+
+    REPORTER_ASSERT(r, rc->unique());
+    rc->unref();
 }
diff --git a/src/third_party/skia/tests/RefDictTest.cpp b/src/third_party/skia/tests/RefDictTest.cpp
index 1e18a68..f3ba657 100644
--- a/src/third_party/skia/tests/RefDictTest.cpp
+++ b/src/third_party/skia/tests/RefDictTest.cpp
@@ -10,7 +10,7 @@
 
 class TestRC : public SkRefCnt {
 public:
-    SK_DECLARE_INST_COUNT(TestRC)
+
 private:
     typedef SkRefCnt INHERITED;
 };
@@ -19,56 +19,56 @@
     TestRC    data0, data1;
     SkRefDict dict;
 
-    REPORTER_ASSERT(reporter, NULL == dict.find(NULL));
-    REPORTER_ASSERT(reporter, NULL == dict.find("foo"));
-    REPORTER_ASSERT(reporter, NULL == dict.find("bar"));
+    REPORTER_ASSERT(reporter, nullptr == dict.find(nullptr));
+    REPORTER_ASSERT(reporter, nullptr == dict.find("foo"));
+    REPORTER_ASSERT(reporter, nullptr == dict.find("bar"));
 
     dict.set("foo", &data0);
     REPORTER_ASSERT(reporter, &data0 == dict.find("foo"));
-    REPORTER_ASSERT(reporter, 2 == data0.getRefCnt());
+    REPORTER_ASSERT(reporter, !data0.unique());
 
     dict.set("foo", &data0);
     REPORTER_ASSERT(reporter, &data0 == dict.find("foo"));
-    REPORTER_ASSERT(reporter, 2 == data0.getRefCnt());
+    REPORTER_ASSERT(reporter, !data0.unique());
 
     dict.set("foo", &data1);
     REPORTER_ASSERT(reporter, &data1 == dict.find("foo"));
-    REPORTER_ASSERT(reporter, 1 == data0.getRefCnt());
-    REPORTER_ASSERT(reporter, 2 == data1.getRefCnt());
+    REPORTER_ASSERT(reporter, data0.unique());
+    REPORTER_ASSERT(reporter, !data1.unique());
 
-    dict.set("foo", NULL);
-    REPORTER_ASSERT(reporter, NULL == dict.find("foo"));
-    REPORTER_ASSERT(reporter, 1 == data0.getRefCnt());
-    REPORTER_ASSERT(reporter, 1 == data1.getRefCnt());
+    dict.set("foo", nullptr);
+    REPORTER_ASSERT(reporter, nullptr == dict.find("foo"));
+    REPORTER_ASSERT(reporter, data0.unique());
+    REPORTER_ASSERT(reporter, data1.unique());
 
     dict.set("foo", &data0);
     dict.set("bar", &data1);
     REPORTER_ASSERT(reporter, &data0 == dict.find("foo"));
     REPORTER_ASSERT(reporter, &data1 == dict.find("bar"));
-    REPORTER_ASSERT(reporter, 2 == data0.getRefCnt());
-    REPORTER_ASSERT(reporter, 2 == data1.getRefCnt());
+    REPORTER_ASSERT(reporter, !data0.unique());
+    REPORTER_ASSERT(reporter, !data1.unique());
 
     dict.set("foo", &data1);
     REPORTER_ASSERT(reporter, &data1 == dict.find("foo"));
     REPORTER_ASSERT(reporter, &data1 == dict.find("bar"));
-    REPORTER_ASSERT(reporter, 1 == data0.getRefCnt());
-    REPORTER_ASSERT(reporter, 3 == data1.getRefCnt());
+    REPORTER_ASSERT(reporter, data0.unique());
+    REPORTER_ASSERT(reporter, !data1.unique());
 
     dict.removeAll();
-    REPORTER_ASSERT(reporter, NULL == dict.find("foo"));
-    REPORTER_ASSERT(reporter, NULL == dict.find("bar"));
-    REPORTER_ASSERT(reporter, 1 == data0.getRefCnt());
-    REPORTER_ASSERT(reporter, 1 == data1.getRefCnt());
+    REPORTER_ASSERT(reporter, nullptr == dict.find("foo"));
+    REPORTER_ASSERT(reporter, nullptr == dict.find("bar"));
+    REPORTER_ASSERT(reporter, data0.unique());
+    REPORTER_ASSERT(reporter, data1.unique());
 
     {
         SkRefDict d;
-        REPORTER_ASSERT(reporter, NULL == d.find("foo"));
-        REPORTER_ASSERT(reporter, 1 == data0.getRefCnt());
+        REPORTER_ASSERT(reporter, nullptr == d.find("foo"));
+        REPORTER_ASSERT(reporter, data0.unique());
         d.set("foo", &data0);
         REPORTER_ASSERT(reporter, &data0 == d.find("foo"));
-        REPORTER_ASSERT(reporter, 2 == data0.getRefCnt());
+        REPORTER_ASSERT(reporter, !data0.unique());
         // let d go out of scope still with a ref on data0
     }
     // be sure d's destructor lowered data0's owner count back to 1
-    REPORTER_ASSERT(reporter, 1 == data0.getRefCnt());
+    REPORTER_ASSERT(reporter, data0.unique());
 }
diff --git a/src/third_party/skia/tests/RegionTest.cpp b/src/third_party/skia/tests/RegionTest.cpp
index ae58ae6..7fcf101 100644
--- a/src/third_party/skia/tests/RegionTest.cpp
+++ b/src/third_party/skia/tests/RegionTest.cpp
@@ -5,6 +5,8 @@
  * found in the LICENSE file.
  */
 
+#include "SkAutoMalloc.h"
+#include "SkPath.h"
 #include "SkRandom.h"
 #include "SkRegion.h"
 #include "Test.h"
@@ -91,6 +93,13 @@
     REPORTER_ASSERT(reporter, !empty.contains(empty2));
     REPORTER_ASSERT(reporter, !valid.contains(empty));
     REPORTER_ASSERT(reporter, !empty.contains(valid));
+
+    SkPath emptyPath;
+    emptyPath.moveTo(1, 5);
+    emptyPath.close();
+    SkRegion openClip;
+    openClip.setRect(-16000, -16000, 16000, 16000);
+    empty.setPath(emptyPath, openClip);  // should not assert
 }
 
 enum {
@@ -254,3 +263,136 @@
     test_empties(reporter);
     test_fromchrome(reporter);
 }
+
+// Test that writeToMemory reports the same number of bytes whether there was a
+// buffer to write to or not.
+static void test_write(const SkRegion& region, skiatest::Reporter* r) {
+    const size_t bytesNeeded = region.writeToMemory(nullptr);
+    SkAutoMalloc storage(bytesNeeded);
+    const size_t bytesWritten = region.writeToMemory(storage.get());
+    REPORTER_ASSERT(r, bytesWritten == bytesNeeded);
+
+    // Also check that the bytes are meaningful.
+    SkRegion copy;
+    REPORTER_ASSERT(r, copy.readFromMemory(storage.get(), bytesNeeded));
+    REPORTER_ASSERT(r, region == copy);
+}
+
+DEF_TEST(Region_writeToMemory, r) {
+    // Test an empty region.
+    SkRegion region;
+    REPORTER_ASSERT(r, region.isEmpty());
+    test_write(region, r);
+
+    // Test a rectangular region
+    bool nonEmpty = region.setRect(0, 0, 50, 50);
+    REPORTER_ASSERT(r, nonEmpty);
+    REPORTER_ASSERT(r, region.isRect());
+    test_write(region, r);
+
+    // Test a complex region
+    nonEmpty = region.op(50, 50, 100, 100, SkRegion::kUnion_Op);
+    REPORTER_ASSERT(r, nonEmpty);
+    REPORTER_ASSERT(r, region.isComplex());
+    test_write(region, r);
+
+    SkRegion complexRegion;
+    Union(&complexRegion, SkIRect::MakeXYWH(0, 0, 1, 1));
+    Union(&complexRegion, SkIRect::MakeXYWH(0, 0, 3, 3));
+    Union(&complexRegion, SkIRect::MakeXYWH(10, 0, 3, 3));
+    Union(&complexRegion, SkIRect::MakeXYWH(0, 10, 13, 3));
+    test_write(complexRegion, r);
+
+    Union(&complexRegion, SkIRect::MakeXYWH(10, 20, 3, 3));
+    Union(&complexRegion, SkIRect::MakeXYWH(0,  20, 3, 3));
+    test_write(complexRegion, r);
+}
+
+DEF_TEST(Region_readFromMemory_bad, r) {
+    // These assume what our binary format is: conceivably we could change it
+    // and might need to remove or change some of these tests.
+    SkRegion region;
+
+    {
+        // invalid boundary rectangle
+        int32_t data[5] = {0, 4, 4, 8, 2};
+        REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data)));
+    }
+    // Region Layout, Serialized Format:
+    //    COUNT LEFT TOP RIGHT BOTTOM Y_SPAN_COUNT TOTAL_INTERVAL_COUNT
+    //    Top ( Bottom Span_Interval_Count ( Left Right )* Sentinel )+ Sentinel
+    {
+        // Example of valid data
+        int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10,
+                          2147483647, 2147483647};
+        REPORTER_ASSERT(r, 0 != region.readFromMemory(data, sizeof(data)));
+    }
+    {
+        // Example of valid data with 4 intervals
+        int32_t data[] = {19, 0, 0, 30, 30, 3, 4, 0, 10, 2, 0, 10, 20, 30,
+                          2147483647, 20, 0, 2147483647, 30, 2, 0, 10, 20, 30,
+                          2147483647, 2147483647};
+        REPORTER_ASSERT(r, 0 != region.readFromMemory(data, sizeof(data)));
+    }
+    {
+        // Short count
+        int32_t data[] = {8, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10,
+                          2147483647, 2147483647};
+        REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data)));
+    }
+    {
+        // bounds don't match
+        int32_t data[] = {9, 0, 0, 10, 11, 1, 2, 0, 10, 2, 0, 4, 6, 10,
+                          2147483647, 2147483647};
+        REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data)));
+    }
+    {
+        //  bad yspan count
+        int32_t data[] = {9, 0, 0, 10, 10, 2, 2, 0, 10, 2, 0, 4, 6, 10,
+                          2147483647, 2147483647};
+        REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data)));
+    }
+    {
+        // bad int count
+        int32_t data[] = {9, 0, 0, 10, 10, 1, 3, 0, 10, 2, 0, 4, 6, 10,
+                          2147483647, 2147483647};
+        REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data)));
+    }
+    {
+        // bad final sentinal
+        int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10,
+                          2147483647, -1};
+        REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data)));
+    }
+    {
+        // bad row sentinal
+        int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10,
+                          -1, 2147483647};
+        REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data)));
+    }
+    {
+        // starts with empty yspan
+        int32_t data[] = {12, 0, 0, 10, 10, 2, 2, -5, 0, 0, 2147483647, 10,
+                          2, 0, 4, 6, 10, 2147483647, 2147483647};
+        REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data)));
+    }
+    {
+        // ends with empty yspan
+        int32_t data[] = {12, 0, 0, 10, 10, 2, 2, 0, 10, 2, 0, 4, 6, 10,
+                          2147483647, 15, 0, 2147483647, 2147483647};
+        REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data)));
+    }
+    {
+        // y intervals out of order
+        int32_t data[] = {19, 0, -20, 30, 10, 3, 4, 0, 10, 2, 0, 10, 20, 30,
+                          2147483647, -20, 0, 2147483647, -10, 2, 0, 10, 20, 30,
+                          2147483647, 2147483647};
+        REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data)));
+    }
+    {
+        // x intervals out of order
+        int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 6, 10, 0, 4,
+                          2147483647, 2147483647};
+        REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data)));
+    }
+}
diff --git a/src/third_party/skia/tests/RenderTargetContextTest.cpp b/src/third_party/skia/tests/RenderTargetContextTest.cpp
new file mode 100644
index 0000000..311643e
--- /dev/null
+++ b/src/third_party/skia/tests/RenderTargetContextTest.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This is a GPU-backend specific test.
+
+#include "Test.h"
+
+// MDB TODO: the early instantiation of the renderTargetContext's backing GrRenderTargetProxy
+// mixes this test up. Re-enable once backing GPU resources are distributed by MDB at flush time.
+#if 0
+
+#if SK_SUPPORT_GPU
+#include "GrTextureProxy.h"
+#include "GrRenderTargetContext.h"
+
+static const int kSize = 64;
+
+static sk_sp<GrRenderTargetContext> get_rtc(GrContext* ctx) {
+    return ctx->makeDeferredRenderTargetContext(SkBackingFit::kExact,
+                                                kSize, kSize,
+                                                kRGBA_8888_GrPixelConfig, nullptr);
+}
+
+static void check_is_wrapped_status(skiatest::Reporter* reporter,
+                                    GrRenderTargetContext* rtCtx,
+                                    bool wrappedExpectation) {
+    REPORTER_ASSERT(reporter, rtCtx->isWrapped_ForTesting() == wrappedExpectation);
+
+    GrTextureProxy* tProxy = rtCtx->asTextureProxy();
+    REPORTER_ASSERT(reporter, tProxy);
+
+    REPORTER_ASSERT(reporter, tProxy->isWrapped_ForTesting() == wrappedExpectation);
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(RenderTargetContextTest, reporter, ctxInfo) {
+    GrContext* ctx = ctxInfo.grContext();
+
+    // Calling instantiate on a GrRenderTargetContext's textureProxy also instantiates the
+    // GrRenderTargetContext
+    {
+        sk_sp<GrRenderTargetContext> rtCtx(get_rtc(ctx));
+
+        check_is_wrapped_status(reporter, rtCtx.get(), false);
+
+        GrTextureProxy* tProxy = rtCtx->asTextureProxy();
+        REPORTER_ASSERT(reporter, tProxy);
+
+        REPORTER_ASSERT(reporter, tProxy->instantiate(ctx->resourceProvider()));
+
+        check_is_wrapped_status(reporter, rtCtx.get(), true);
+    }
+
+    // readPixels switches a deferred rtCtx to wrapped
+    {
+        sk_sp<GrRenderTargetContext> rtCtx(get_rtc(ctx));
+
+        check_is_wrapped_status(reporter, rtCtx.get(), false);
+
+        SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(kSize, kSize);
+        SkAutoTMalloc<uint32_t> dstBuffer(kSize * kSize);
+        static const size_t kRowBytes = sizeof(uint32_t) * kSize;
+
+        bool result = rtCtx->readPixels(dstInfo, dstBuffer.get(), kRowBytes, 0, 0);
+        REPORTER_ASSERT(reporter, result);
+
+        check_is_wrapped_status(reporter, rtCtx.get(), true);
+    }
+
+    // TODO: in a future world we should be able to add a test that the majority of
+    // GrRenderTargetContext calls do not force the instantiation of a deferred 
+    // GrRenderTargetContext
+}
+#endif
+#endif
diff --git a/src/third_party/skia/tests/ResourceAllocatorTest.cpp b/src/third_party/skia/tests/ResourceAllocatorTest.cpp
new file mode 100644
index 0000000..2398d68
--- /dev/null
+++ b/src/third_party/skia/tests/ResourceAllocatorTest.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Include here to ensure SK_SUPPORT_GPU is set correctly before it is examined.
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU
+#include "Test.h"
+
+#include "GrResourceAllocator.h"
+#include "GrSurfaceProxyPriv.h"
+#include "GrTextureProxy.h"
+
+// Basic test that two proxies with overlapping intervals and compatible descriptors are
+// assigned different GrSurfaces.
+static void overlap_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider) {
+    GrSurfaceDesc desc;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fWidth  = 64;
+    desc.fHeight = 64;
+
+    sk_sp<GrSurfaceProxy> p1 = GrSurfaceProxy::MakeDeferred(resourceProvider, desc,
+                                                            SkBackingFit::kApprox,
+                                                            SkBudgeted::kNo);
+    sk_sp<GrSurfaceProxy> p2 = GrSurfaceProxy::MakeDeferred(resourceProvider, desc,
+                                                            SkBackingFit::kApprox,
+                                                            SkBudgeted::kNo);
+
+    GrResourceAllocator alloc(resourceProvider);
+
+    alloc.addInterval(p1.get(), 0, 4);
+    alloc.addInterval(p2.get(), 1, 2);
+
+    alloc.assign();
+
+    REPORTER_ASSERT(reporter, p1->priv().peekSurface());
+    REPORTER_ASSERT(reporter, p2->priv().peekSurface());
+    REPORTER_ASSERT(reporter, p1->underlyingUniqueID() != p2->underlyingUniqueID());
+}
+
+DEF_GPUTEST_FOR_ALL_CONTEXTS(ResourceAllocatorTest, reporter, ctxInfo) {
+    GrResourceProvider* resourceProvider = ctxInfo.grContext()->resourceProvider();
+
+    overlap_test(reporter, resourceProvider);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/ResourceCacheTest.cpp b/src/third_party/skia/tests/ResourceCacheTest.cpp
index f9f94d1..3cfa157 100644
--- a/src/third_party/skia/tests/ResourceCacheTest.cpp
+++ b/src/third_party/skia/tests/ResourceCacheTest.cpp
@@ -5,11 +5,26 @@
  * found in the LICENSE file.
  */
 
+// Include here to ensure SK_SUPPORT_GPU is set correctly before it is examined.
+#include "SkTypes.h"
+
 #if SK_SUPPORT_GPU
+#include <thread>
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrContextFactory.h"
+#include "GrGpu.h"
+#include "GrGpuResourceCacheAccess.h"
+#include "GrGpuResourcePriv.h"
+#include "GrResourceCache.h"
+#include "GrResourceProvider.h"
+#include "GrTest.h"
+#include "GrTexture.h"
 
 #include "SkCanvas.h"
-#include "GrContextFactory.h"
-#include "GrResourceCache.h"
+#include "SkGr.h"
+#include "SkMessageBus.h"
+#include "SkMipMap.h"
 #include "SkSurface.h"
 #include "Test.h"
 
@@ -17,9 +32,17 @@
 static const int gHeight = 480;
 
 ////////////////////////////////////////////////////////////////////////////////
-static void test_cache(skiatest::Reporter* reporter,
-                       GrContext* context,
-                       SkCanvas* canvas) {
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceCacheCache, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    GrSurfaceDesc desc;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fWidth = gWidth;
+    desc.fHeight = gHeight;
+    SkImageInfo info = SkImageInfo::MakeN32Premul(gWidth, gHeight);
+    auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
+    SkCanvas* canvas = surface->getCanvas();
+
     const SkIRect size = SkIRect::MakeWH(gWidth, gHeight);
 
     SkBitmap src;
@@ -28,7 +51,7 @@
     size_t srcSize = src.getSize();
 
     size_t initialCacheSize;
-    context->getResourceCacheUsage(NULL, &initialCacheSize);
+    context->getResourceCacheUsage(nullptr, &initialCacheSize);
 
     int oldMaxNum;
     size_t oldMaxBytes;
@@ -44,13 +67,13 @@
 
     for (int i = 0; i < 100; ++i) {
         canvas->drawBitmap(src, 0, 0);
-        canvas->readPixels(size, &readback);
+        canvas->readPixels(readback, 0, 0);
 
         // "modify" the src texture
         src.notifyPixelsChanged();
 
         size_t curCacheSize;
-        context->getResourceCacheUsage(NULL, &curCacheSize);
+        context->getResourceCacheUsage(nullptr, &curCacheSize);
 
         // we should never go over the size limit
         REPORTER_ASSERT(reporter, curCacheSize <= maxCacheSize);
@@ -59,28 +82,214 @@
     context->setResourceCacheLimits(oldMaxNum, oldMaxBytes);
 }
 
-class TestResource : public GrGpuResource {
-    static const size_t kDefaultSize = 100;
+static bool is_rendering_and_not_angle_es3(sk_gpu_test::GrContextFactory::ContextType type) {
+    if (type == sk_gpu_test::GrContextFactory::kANGLE_D3D11_ES3_ContextType ||
+        type == sk_gpu_test::GrContextFactory::kANGLE_GL_ES3_ContextType) {
+        return false;
+    }
+    return sk_gpu_test::GrContextFactory::IsRenderingContext(type);
+}
 
-public:
-    SK_DECLARE_INST_COUNT(TestResource);
-    TestResource(GrGpu* gpu, size_t size = kDefaultSize)
-        : INHERITED(gpu, false)
-        , fCache(NULL)
-        , fToDelete(NULL)
-        , fSize(size) {
-        ++fAlive;
-        this->registerWithCache();
+// This currently fails on ES3 ANGLE contexts
+DEF_GPUTEST_FOR_CONTEXTS(ResourceCacheStencilBuffers, &is_rendering_and_not_angle_es3, reporter,
+                         ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    GrSurfaceDesc smallDesc;
+    smallDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    smallDesc.fConfig = kRGBA_8888_GrPixelConfig;
+    smallDesc.fWidth = 4;
+    smallDesc.fHeight = 4;
+    smallDesc.fSampleCnt = 0;
+
+    if (context->caps()->avoidStencilBuffers()) {
+        return;
+    }
+    GrResourceProvider* resourceProvider = context->resourceProvider();
+    // Test that two budgeted RTs with the same desc share a stencil buffer.
+    sk_sp<GrTexture> smallRT0(resourceProvider->createTexture(smallDesc, SkBudgeted::kYes));
+    if (smallRT0 && smallRT0->asRenderTarget()) {
+        resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget());
     }
 
-    ~TestResource() {
-        --fAlive;
-        if (fToDelete) {
-            // Breaks our little 2-element cycle below.
-            fToDelete->setDeleteWhenDestroyed(NULL, NULL);
-            fCache->deleteResource(fToDelete->getCacheEntry());
+    sk_sp<GrTexture> smallRT1(resourceProvider->createTexture(smallDesc, SkBudgeted::kYes));
+    if (smallRT1 && smallRT1->asRenderTarget()) {
+        resourceProvider->attachStencilAttachment(smallRT1->asRenderTarget());
+    }
+
+    REPORTER_ASSERT(reporter,
+                    smallRT0 && smallRT1 &&
+                    smallRT0->asRenderTarget() && smallRT1->asRenderTarget() &&
+                    resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) ==
+                    resourceProvider->attachStencilAttachment(smallRT1->asRenderTarget()));
+
+    // An unbudgeted RT with the same desc should also share.
+    sk_sp<GrTexture> smallRT2(resourceProvider->createTexture(smallDesc, SkBudgeted::kNo));
+    if (smallRT2 && smallRT2->asRenderTarget()) {
+        resourceProvider->attachStencilAttachment(smallRT2->asRenderTarget());
+    }
+    REPORTER_ASSERT(reporter,
+                    smallRT0 && smallRT2 &&
+                    smallRT0->asRenderTarget() && smallRT2->asRenderTarget() &&
+                    resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) ==
+                    resourceProvider->attachStencilAttachment(smallRT2->asRenderTarget()));
+
+    // An RT with a much larger size should not share.
+    GrSurfaceDesc bigDesc;
+    bigDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    bigDesc.fConfig = kRGBA_8888_GrPixelConfig;
+    bigDesc.fWidth = 400;
+    bigDesc.fHeight = 200;
+    bigDesc.fSampleCnt = 0;
+    sk_sp<GrTexture> bigRT(resourceProvider->createTexture(bigDesc, SkBudgeted::kNo));
+    if (bigRT && bigRT->asRenderTarget()) {
+        resourceProvider->attachStencilAttachment(bigRT->asRenderTarget());
+    }
+    REPORTER_ASSERT(reporter,
+                    smallRT0 && bigRT &&
+                    smallRT0->asRenderTarget() && bigRT->asRenderTarget() &&
+                    resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) !=
+                    resourceProvider->attachStencilAttachment(bigRT->asRenderTarget()));
+
+    int supportedSampleCount = context->caps()->getSampleCount(4, smallDesc.fConfig);
+    if (supportedSampleCount > 0) {
+        // An RT with a different sample count should not share.
+        GrSurfaceDesc smallMSAADesc = smallDesc;
+        smallMSAADesc.fSampleCnt = supportedSampleCount;
+        sk_sp<GrTexture> smallMSAART0(resourceProvider->createTexture(smallMSAADesc,
+                                                                      SkBudgeted::kNo));
+        if (smallMSAART0 && smallMSAART0->asRenderTarget()) {
+            resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget());
         }
-        this->release();
+#ifdef SK_BUILD_FOR_ANDROID
+        if (!smallMSAART0) {
+            // The nexus player seems to fail to create MSAA textures.
+            return;
+        }
+#endif
+        REPORTER_ASSERT(reporter,
+                        smallRT0 && smallMSAART0 &&
+                        smallRT0->asRenderTarget() && smallMSAART0->asRenderTarget() &&
+                        resourceProvider->attachStencilAttachment(smallRT0->asRenderTarget()) !=
+                        resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget()));
+        // A second MSAA RT should share with the first MSAA RT.
+        sk_sp<GrTexture> smallMSAART1(resourceProvider->createTexture(smallMSAADesc,
+                                                                      SkBudgeted::kNo));
+        if (smallMSAART1 && smallMSAART1->asRenderTarget()) {
+            resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget());
+        }
+        REPORTER_ASSERT(reporter,
+                        smallMSAART0 && smallMSAART1 &&
+                        smallMSAART0->asRenderTarget() &&
+                        smallMSAART1->asRenderTarget() &&
+                        resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget()) ==
+                        resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget()));
+        // But not one with a larger sample count should not. (Also check that the request for 4
+        // samples didn't get rounded up to >= 8 or else they could share.).
+        supportedSampleCount = context->caps()->getSampleCount(8, smallDesc.fConfig);
+        if (supportedSampleCount != smallMSAADesc.fSampleCnt &&
+            smallMSAART0 && smallMSAART0->asRenderTarget()) {
+            smallMSAADesc.fSampleCnt = supportedSampleCount;
+            smallMSAART1 = resourceProvider->createTexture(smallMSAADesc, SkBudgeted::kNo);
+            sk_sp<GrTexture> smallMSAART1(
+                resourceProvider->createTexture(smallMSAADesc, SkBudgeted::kNo));
+            if (smallMSAART1 && smallMSAART1->asRenderTarget()) {
+                resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget());
+            }
+            REPORTER_ASSERT(reporter,
+                        smallMSAART0 && smallMSAART1 &&
+                        smallMSAART0->asRenderTarget() &&
+                        smallMSAART1->asRenderTarget() &&
+                        resourceProvider->attachStencilAttachment(smallMSAART0->asRenderTarget()) !=
+                        resourceProvider->attachStencilAttachment(smallMSAART1->asRenderTarget()));
+        }
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceCacheWrappedResources, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    GrGpu* gpu = context->getGpu();
+    // this test is only valid for GL
+    if (!gpu || !gpu->glContextForTesting()) {
+        return;
+    }
+
+    GrBackendObject texHandles[2];
+    static const int kW = 100;
+    static const int kH = 100;
+
+    texHandles[0] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kRGBA_8888_GrPixelConfig);
+    texHandles[1] = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kRGBA_8888_GrPixelConfig);
+
+    context->resetContext();
+
+    GrBackendTexture backendTex1 = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                                kW,
+                                                                kH,
+                                                                kRGBA_8888_GrPixelConfig,
+                                                                texHandles[0]);
+    sk_sp<GrTexture> borrowed(context->resourceProvider()->wrapBackendTexture(
+            backendTex1, kTopLeft_GrSurfaceOrigin, kBorrow_GrWrapOwnership));
+
+    GrBackendTexture backendTex2 = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                                kW,
+                                                                kH,
+                                                                kRGBA_8888_GrPixelConfig,
+                                                                texHandles[1]);
+    sk_sp<GrTexture> adopted(context->resourceProvider()->wrapBackendTexture(
+            backendTex2, kTopLeft_GrSurfaceOrigin, kAdopt_GrWrapOwnership));
+
+    REPORTER_ASSERT(reporter, borrowed != nullptr && adopted != nullptr);
+    if (!borrowed || !adopted) {
+        return;
+    }
+
+    borrowed.reset(nullptr);
+    adopted.reset(nullptr);
+
+    context->flush();
+
+    bool borrowedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[0]);
+    bool adoptedIsAlive = gpu->isTestingOnlyBackendTexture(texHandles[1]);
+
+    REPORTER_ASSERT(reporter, borrowedIsAlive);
+    REPORTER_ASSERT(reporter, !adoptedIsAlive);
+
+    gpu->deleteTestingOnlyBackendTexture(texHandles[0], !borrowedIsAlive);
+    gpu->deleteTestingOnlyBackendTexture(texHandles[1], !adoptedIsAlive);
+
+    context->resetContext();
+}
+
+class TestResource : public GrGpuResource {
+    enum ScratchConstructor { kScratchConstructor };
+public:
+    static const size_t kDefaultSize = 100;
+
+    /** Property that distinctly categorizes the resource.
+     * For example, textures have width, height, ... */
+    enum SimulatedProperty { kA_SimulatedProperty, kB_SimulatedProperty };
+
+    TestResource(GrGpu* gpu, SkBudgeted budgeted = SkBudgeted::kYes, size_t size = kDefaultSize)
+        : INHERITED(gpu)
+        , fToDelete(nullptr)
+        , fSize(size)
+        , fProperty(kA_SimulatedProperty)
+        , fIsScratch(false) {
+        ++fNumAlive;
+        this->registerWithCache(budgeted);
+    }
+
+    static TestResource* CreateScratch(GrGpu* gpu, SkBudgeted budgeted,
+                                       SimulatedProperty property) {
+        return new TestResource(gpu, budgeted, property, kScratchConstructor);
+    }
+    static TestResource* CreateWrapped(GrGpu* gpu, size_t size = kDefaultSize) {
+        return new TestResource(gpu, size);
+    }
+
+    ~TestResource() override {
+        --fNumAlive;
+        SkSafeUnref(fToDelete);
     }
 
     void setSize(size_t size) {
@@ -88,222 +297,1447 @@
         this->didChangeGpuMemorySize();
     }
 
-    size_t gpuMemorySize() const SK_OVERRIDE { return fSize; }
+    static int NumAlive() { return fNumAlive; }
 
-    static int alive() { return fAlive; }
-
-    void setDeleteWhenDestroyed(GrResourceCache* cache, TestResource* resource) {
-        fCache = cache;
-        fToDelete = resource;
+    void setUnrefWhenDestroyed(TestResource* resource) {
+        SkRefCnt_SafeAssign(fToDelete, resource);
     }
 
+    static void ComputeScratchKey(SimulatedProperty property, GrScratchKey* key) {
+        static GrScratchKey::ResourceType t = GrScratchKey::GenerateResourceType();
+        GrScratchKey::Builder builder(key, t, kScratchKeyFieldCnt);
+        for (int i = 0; i < kScratchKeyFieldCnt; ++i) {
+            builder[i] = static_cast<uint32_t>(i + property);
+        }
+    }
+
+    static size_t ExpectedScratchKeySize() {
+        return sizeof(uint32_t) * (kScratchKeyFieldCnt + GrScratchKey::kMetaDataCnt);
+    }
 private:
-    GrResourceCache* fCache;
+    static const int kScratchKeyFieldCnt = 6;
+
+    TestResource(GrGpu* gpu, SkBudgeted budgeted, SimulatedProperty property, ScratchConstructor)
+        : INHERITED(gpu)
+        , fToDelete(nullptr)
+        , fSize(kDefaultSize)
+        , fProperty(property)
+        , fIsScratch(true) {
+        ++fNumAlive;
+        this->registerWithCache(budgeted);
+    }
+
+    // Constructor for simulating resources that wrap backend objects.
+    TestResource(GrGpu* gpu, size_t size)
+        : INHERITED(gpu)
+        , fToDelete(nullptr)
+        , fSize(size)
+        , fProperty(kA_SimulatedProperty)
+        , fIsScratch(false) {
+        ++fNumAlive;
+        this->registerWithCacheWrapped();
+    }
+
+    void computeScratchKey(GrScratchKey* key) const override {
+        if (fIsScratch) {
+            ComputeScratchKey(fProperty, key);
+        }
+    }
+
+    size_t onGpuMemorySize() const override { return fSize; }
+
     TestResource* fToDelete;
     size_t fSize;
-    static int fAlive;
-
+    static int fNumAlive;
+    SimulatedProperty fProperty;
+    bool fIsScratch;
     typedef GrGpuResource INHERITED;
 };
-int TestResource::fAlive = 0;
+int TestResource::fNumAlive = 0;
 
-static void test_purge_invalidated(skiatest::Reporter* reporter, GrContext* context) {
-    GrCacheID::Domain domain = GrCacheID::GenerateDomain();
-    GrCacheID::Key keyData;
-    keyData.fData64[0] = 5;
-    keyData.fData64[1] = 18;
-    GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType();
-    GrResourceKey key(GrCacheID(domain, keyData), t, 0);
+class Mock {
+public:
+    Mock(int maxCnt, size_t maxBytes) {
+        fContext.reset(GrContext::Create(kMock_GrBackend, (GrBackendContext) nullptr));
+        SkASSERT(fContext);
+        fContext->setResourceCacheLimits(maxCnt, maxBytes);
+        GrResourceCache* cache = fContext->getResourceCache();
+        cache->purgeAllUnlocked();
+        SkASSERT(0 == cache->getResourceCount() && 0 == cache->getResourceBytes());
+    }
 
-    GrResourceCache cache(5, 30000);
+    GrResourceCache* cache() { return fContext->getResourceCache(); }
 
-    // Add two resources with the same key that delete each other from the cache when destroyed.
+    GrContext* context() { return fContext.get(); }
+
+private:
+    sk_sp<GrContext> fContext;
+};
+
+static void test_no_key(skiatest::Reporter* reporter) {
+    Mock mock(10, 30000);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    // Create a bunch of resources with no keys
     TestResource* a = new TestResource(context->getGpu());
     TestResource* b = new TestResource(context->getGpu());
-    cache.addResource(key, a);
-    cache.addResource(key, b);
-    // Circle back.
-    a->setDeleteWhenDestroyed(&cache, b);
-    b->setDeleteWhenDestroyed(&cache, a);
+    TestResource* c = new TestResource(context->getGpu());
+    TestResource* d = new TestResource(context->getGpu());
+    a->setSize(11);
+    b->setSize(12);
+    c->setSize(13);
+    d->setSize(14);
+
+    REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive());
+    REPORTER_ASSERT(reporter, 4 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() + c->gpuMemorySize() +
+                              d->gpuMemorySize() == cache->getResourceBytes());
+
+    // Should be safe to purge without deleting the resources since we still have refs.
+    cache->purgeAllUnlocked();
+    REPORTER_ASSERT(reporter, 4 == TestResource::NumAlive());
+
+    // Since the resources have neither unique nor scratch keys, delete immediately upon unref.
+
+    a->unref();
+    REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive());
+    REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() + d->gpuMemorySize() ==
+                              cache->getResourceBytes());
+
+    c->unref();
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+    REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, b->gpuMemorySize() + d->gpuMemorySize() ==
+                              cache->getResourceBytes());
+
+    d->unref();
+    REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
+    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes());
+
+    b->unref();
+    REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
+}
+
+// Each integer passed as a template param creates a new domain.
+template <int>
+static void make_unique_key(GrUniqueKey* key, int data, const char* tag = nullptr) {
+    static GrUniqueKey::Domain d = GrUniqueKey::GenerateDomain();
+    GrUniqueKey::Builder builder(key, d, 1, tag);
+    builder[0] = data;
+}
+
+static void test_budgeting(skiatest::Reporter* reporter) {
+    Mock mock(10, 300);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    GrUniqueKey uniqueKey;
+    make_unique_key<0>(&uniqueKey, 0);
+
+    // Create a scratch, a unique, and a wrapped resource
+    TestResource* scratch =
+            TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes, TestResource::kB_SimulatedProperty);
+    scratch->setSize(10);
+    TestResource* unique = new TestResource(context->getGpu());
+    unique->setSize(11);
+    unique->resourcePriv().setUniqueKey(uniqueKey);
+    TestResource* wrapped = TestResource::CreateWrapped(context->getGpu());
+    wrapped->setSize(12);
+    TestResource* unbudgeted =
+            new TestResource(context->getGpu(), SkBudgeted::kNo);
+    unbudgeted->setSize(13);
+
+    // Make sure we can add a unique key to the wrapped resource
+    GrUniqueKey uniqueKey2;
+    make_unique_key<0>(&uniqueKey2, 1);
+    wrapped->resourcePriv().setUniqueKey(uniqueKey2);
+    GrGpuResource* wrappedViaKey = cache->findAndRefUniqueResource(uniqueKey2);
+    REPORTER_ASSERT(reporter, wrappedViaKey != nullptr);
+
+    // Remove the extra ref we just added.
+    wrappedViaKey->unref();
+
+    // Make sure sizes are as we expect
+    REPORTER_ASSERT(reporter, 4 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() +
+                              wrapped->gpuMemorySize() + unbudgeted->gpuMemorySize() ==
+                              cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
+    REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() ==
+                              cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+
+    // Our refs mean that the resources are non purgeable.
+    cache->purgeAllUnlocked();
+    REPORTER_ASSERT(reporter, 4 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() +
+                              wrapped->gpuMemorySize() + unbudgeted->gpuMemorySize() ==
+                              cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
+    REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() ==
+                              cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+
+    // Unreffing the wrapped resource should free it right away.
+    wrapped->unref();
+    REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + unique->gpuMemorySize() +
+                              unbudgeted->gpuMemorySize() == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+
+    // Now try freeing the budgeted resources first
+    wrapped = TestResource::CreateWrapped(context->getGpu());
+    scratch->setSize(12);
+    unique->unref();
+    REPORTER_ASSERT(reporter, 11 == cache->getPurgeableBytes());
+    cache->purgeAllUnlocked();
+    REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, scratch->gpuMemorySize() + wrapped->gpuMemorySize() +
+                              unbudgeted->gpuMemorySize() == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
+    REPORTER_ASSERT(reporter, scratch->gpuMemorySize() == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+
+    scratch->unref();
+    REPORTER_ASSERT(reporter, 12 == cache->getPurgeableBytes());
+    cache->purgeAllUnlocked();
+    REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() + wrapped->gpuMemorySize() ==
+                              cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
+    REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+
+    wrapped->unref();
+    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, unbudgeted->gpuMemorySize() == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
+    REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+
+    unbudgeted->unref();
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
+    REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+}
+
+static void test_unbudgeted(skiatest::Reporter* reporter) {
+    Mock mock(10, 30000);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    GrUniqueKey uniqueKey;
+    make_unique_key<0>(&uniqueKey, 0);
+
+    TestResource* scratch;
+    TestResource* unique;
+    TestResource* wrapped;
+    TestResource* unbudgeted;
+
+    // A large uncached or wrapped resource shouldn't evict anything.
+    scratch = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
+                                          TestResource::kB_SimulatedProperty);
+
+    scratch->setSize(10);
+    scratch->unref();
+    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, 10 == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
+    REPORTER_ASSERT(reporter, 10 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 10 == cache->getPurgeableBytes());
+
+    unique = new TestResource(context->getGpu());
+    unique->setSize(11);
+    unique->resourcePriv().setUniqueKey(uniqueKey);
+    unique->unref();
+    REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
+    REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
+
+    size_t large = 2 * cache->getResourceBytes();
+    unbudgeted = new TestResource(context->getGpu(), SkBudgeted::kNo, large);
+    REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
+    REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
+
+    unbudgeted->unref();
+    REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
+    REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
+
+    wrapped = TestResource::CreateWrapped(context->getGpu(), large);
+    REPORTER_ASSERT(reporter, 3 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, 21 + large == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
+    REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
+
+    wrapped->unref();
+    REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, 21 == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 2 == cache->getBudgetedResourceCount());
+    REPORTER_ASSERT(reporter, 21 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 21 == cache->getPurgeableBytes());
+
+    cache->purgeAllUnlocked();
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
+    REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+}
+
+// This method can't be static because it needs to friended in GrGpuResource::CacheAccess.
+void test_unbudgeted_to_scratch(skiatest::Reporter* reporter);
+/*static*/ void test_unbudgeted_to_scratch(skiatest::Reporter* reporter) {
+    Mock mock(10, 300);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    TestResource* resource =
+        TestResource::CreateScratch(context->getGpu(), SkBudgeted::kNo,
+                                    TestResource::kA_SimulatedProperty);
+    GrScratchKey key;
+    TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &key);
+
+    size_t size = resource->gpuMemorySize();
+    for (int i = 0; i < 2; ++i) {
+        // Since this resource is unbudgeted, it should not be reachable as scratch.
+        REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key);
+        REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch());
+        REPORTER_ASSERT(reporter, SkBudgeted::kNo == resource->resourcePriv().isBudgeted());
+        REPORTER_ASSERT(reporter, nullptr == cache->findAndRefScratchResource(key, TestResource::kDefaultSize, 0));
+        REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+        REPORTER_ASSERT(reporter, size == cache->getResourceBytes());
+        REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
+        REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+        REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+
+        // Once it is unrefed, it should become available as scratch.
+        resource->unref();
+        REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+        REPORTER_ASSERT(reporter, size == cache->getResourceBytes());
+        REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
+        REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes());
+        REPORTER_ASSERT(reporter, size == cache->getPurgeableBytes());
+        resource = static_cast<TestResource*>(cache->findAndRefScratchResource(key, TestResource::kDefaultSize, 0));
+        REPORTER_ASSERT(reporter, resource);
+        REPORTER_ASSERT(reporter, resource->resourcePriv().getScratchKey() == key);
+        REPORTER_ASSERT(reporter, resource->cacheAccess().isScratch());
+        REPORTER_ASSERT(reporter, SkBudgeted::kYes == resource->resourcePriv().isBudgeted());
+
+        if (0 == i) {
+            // If made unbudgeted, it should return to original state: ref'ed and unbudgeted. Try
+            // the above tests again.
+            resource->resourcePriv().makeUnbudgeted();
+        } else {
+            // After the second time around, try removing the scratch key
+            resource->resourcePriv().removeScratchKey();
+            REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+            REPORTER_ASSERT(reporter, size == cache->getResourceBytes());
+            REPORTER_ASSERT(reporter, 1 == cache->getBudgetedResourceCount());
+            REPORTER_ASSERT(reporter, size == cache->getBudgetedResourceBytes());
+            REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+            REPORTER_ASSERT(reporter, !resource->resourcePriv().getScratchKey().isValid());
+            REPORTER_ASSERT(reporter, !resource->cacheAccess().isScratch());
+            REPORTER_ASSERT(reporter, SkBudgeted::kYes == resource->resourcePriv().isBudgeted());
+
+            // now when it is unrefed it should die since it has no key.
+            resource->unref();
+            REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+            REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
+            REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
+            REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+            REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+        }
+    }
+}
+
+static void test_duplicate_scratch_key(skiatest::Reporter* reporter) {
+    Mock mock(5, 30000);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    // Create two resources that have the same scratch key.
+    TestResource* a = TestResource::CreateScratch(context->getGpu(),
+                                                  SkBudgeted::kYes,
+                                                  TestResource::kB_SimulatedProperty);
+    TestResource* b = TestResource::CreateScratch(context->getGpu(),
+                                                  SkBudgeted::kYes,
+                                                  TestResource::kB_SimulatedProperty);
+    a->setSize(11);
+    b->setSize(12);
+    GrScratchKey scratchKey1;
+    TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey1);
+    // Check for negative case consistency. (leaks upon test failure.)
+    REPORTER_ASSERT(reporter, nullptr == cache->findAndRefScratchResource(scratchKey1, TestResource::kDefaultSize, 0));
+
+    GrScratchKey scratchKey;
+    TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey);
+
+    // Scratch resources are registered with GrResourceCache just by existing. There are 2.
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));)
+    REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() ==
+                              cache->getResourceBytes());
+
+    // Our refs mean that the resources are non purgeable.
+    cache->purgeAllUnlocked();
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+    REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
+
+    // Unref but don't purge
+    a->unref();
+    b->unref();
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));)
+
+    // Purge again. This time resources should be purgeable.
+    cache->purgeAllUnlocked();
+    REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
+}
+
+static void test_remove_scratch_key(skiatest::Reporter* reporter) {
+    Mock mock(5, 30000);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    // Create two resources that have the same scratch key.
+    TestResource* a = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
+                                                  TestResource::kB_SimulatedProperty);
+    TestResource* b = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
+                                                  TestResource::kB_SimulatedProperty);
     a->unref();
     b->unref();
 
-    // Add a third independent resource also with the same key.
-    GrGpuResource* r = new TestResource(context->getGpu());
-    cache.addResource(key, r);
-    r->unref();
+    GrScratchKey scratchKey;
+    // Ensure that scratch key lookup is correct for negative case.
+    TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey);
+    // (following leaks upon test failure).
+    REPORTER_ASSERT(reporter, cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0) == nullptr);
 
-    // Invalidate all three, all three should be purged and destroyed.
-    REPORTER_ASSERT(reporter, 3 == TestResource::alive());
-    const GrResourceInvalidatedMessage msg = { key };
-    SkMessageBus<GrResourceInvalidatedMessage>::Post(msg);
-    cache.purgeAsNeeded();
-    REPORTER_ASSERT(reporter, 0 == TestResource::alive());
+    // Scratch resources are registered with GrResourceCache just by existing. There are 2.
+    TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey);
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 2 == cache->countScratchEntriesForKey(scratchKey));)
+    REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
+
+    // Find the first resource and remove its scratch key
+    GrGpuResource* find;
+    find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0);
+    find->resourcePriv().removeScratchKey();
+    // It's still alive, but not cached by scratch key anymore
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->countScratchEntriesForKey(scratchKey));)
+    REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
+
+    // The cache should immediately delete it when it's unrefed since it isn't accessible.
+    find->unref();
+    REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 1 == cache->countScratchEntriesForKey(scratchKey));)
+    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+
+    // Repeat for the second resource.
+    find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0);
+    find->resourcePriv().removeScratchKey();
+    REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
+    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+
+    // Should be able to call this multiple times with no problem.
+    find->resourcePriv().removeScratchKey();
+    REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
+    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+
+    find->unref();
+    REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
+    SkDEBUGCODE(REPORTER_ASSERT(reporter, 0 == cache->countScratchEntriesForKey(scratchKey));)
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
 }
 
-static void test_cache_delete_on_destruction(skiatest::Reporter* reporter,
-                                             GrContext* context) {
-    GrCacheID::Domain domain = GrCacheID::GenerateDomain();
-    GrCacheID::Key keyData;
-    keyData.fData64[0] = 5;
-    keyData.fData64[1] = 0;
-    GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType();
+static void test_scratch_key_consistency(skiatest::Reporter* reporter) {
+    Mock mock(5, 30000);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
 
-    GrResourceKey key(GrCacheID(domain, keyData), t, 0);
+    // Create two resources that have the same scratch key.
+    TestResource* a = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
+                                                  TestResource::kB_SimulatedProperty);
+    TestResource* b = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
+                                                  TestResource::kB_SimulatedProperty);
+    a->unref();
+    b->unref();
 
-    {
-        {
-            GrResourceCache cache(3, 30000);
-            TestResource* a = new TestResource(context->getGpu());
-            TestResource* b = new TestResource(context->getGpu());
-            cache.addResource(key, a);
-            cache.addResource(key, b);
+    GrScratchKey scratchKey;
+    // Ensure that scratch key comparison and assignment is consistent.
+    GrScratchKey scratchKey1;
+    TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey1);
+    GrScratchKey scratchKey2;
+    TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey2);
+    REPORTER_ASSERT(reporter, scratchKey1.size() == TestResource::ExpectedScratchKeySize());
+    REPORTER_ASSERT(reporter, scratchKey1 != scratchKey2);
+    REPORTER_ASSERT(reporter, scratchKey2 != scratchKey1);
+    scratchKey = scratchKey1;
+    REPORTER_ASSERT(reporter, scratchKey.size() == TestResource::ExpectedScratchKeySize());
+    REPORTER_ASSERT(reporter, scratchKey1 == scratchKey);
+    REPORTER_ASSERT(reporter, scratchKey == scratchKey1);
+    REPORTER_ASSERT(reporter, scratchKey2 != scratchKey);
+    REPORTER_ASSERT(reporter, scratchKey != scratchKey2);
+    scratchKey = scratchKey2;
+    REPORTER_ASSERT(reporter, scratchKey.size() == TestResource::ExpectedScratchKeySize());
+    REPORTER_ASSERT(reporter, scratchKey1 != scratchKey);
+    REPORTER_ASSERT(reporter, scratchKey != scratchKey1);
+    REPORTER_ASSERT(reporter, scratchKey2 == scratchKey);
+    REPORTER_ASSERT(reporter, scratchKey == scratchKey2);
 
-            a->setDeleteWhenDestroyed(&cache, b);
-            b->setDeleteWhenDestroyed(&cache, a);
+    // Ensure that scratch key lookup is correct for negative case.
+    TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey);
+    // (following leaks upon test failure).
+    REPORTER_ASSERT(reporter, cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0) == nullptr);
 
-            a->unref();
-            b->unref();
-            REPORTER_ASSERT(reporter, 2 == TestResource::alive());
-        }
-        REPORTER_ASSERT(reporter, 0 == TestResource::alive());
-    }
-    {
-        GrResourceCache cache(3, 30000);
-        TestResource* a = new TestResource(context->getGpu());
-        TestResource* b = new TestResource(context->getGpu());
-        cache.addResource(key, a);
-        cache.addResource(key, b);
+    // Find the first resource with a scratch key and a copy of a scratch key.
+    TestResource::ComputeScratchKey(TestResource::kB_SimulatedProperty, &scratchKey);
+    GrGpuResource* find = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0);
+    REPORTER_ASSERT(reporter, find != nullptr);
+    find->unref();
 
-        a->setDeleteWhenDestroyed(&cache, b);
-        b->setDeleteWhenDestroyed(&cache, a);
+    scratchKey2 = scratchKey;
+    find = cache->findAndRefScratchResource(scratchKey2, TestResource::kDefaultSize, 0);
+    REPORTER_ASSERT(reporter, find != nullptr);
+    REPORTER_ASSERT(reporter, find == a || find == b);
 
-        a->unref();
-        b->unref();
-
-        cache.deleteResource(a->getCacheEntry());
-
-        REPORTER_ASSERT(reporter, 0 == TestResource::alive());
-    }
+    GrGpuResource* find2 = cache->findAndRefScratchResource(scratchKey2, TestResource::kDefaultSize, 0);
+    REPORTER_ASSERT(reporter, find2 != nullptr);
+    REPORTER_ASSERT(reporter, find2 == a || find2 == b);
+    REPORTER_ASSERT(reporter, find2 != find);
+    find2->unref();
+    find->unref();
 }
 
-static void test_resource_size_changed(skiatest::Reporter* reporter,
-                                       GrContext* context) {
-    GrCacheID::Domain domain = GrCacheID::GenerateDomain();
-    GrResourceKey::ResourceType t = GrResourceKey::GenerateResourceType();
+static void test_duplicate_unique_key(skiatest::Reporter* reporter) {
+    Mock mock(5, 30000);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
 
-    GrCacheID::Key key1Data;
-    key1Data.fData64[0] = 0;
-    key1Data.fData64[1] = 0;
-    GrResourceKey key1(GrCacheID(domain, key1Data), t, 0);
+    GrUniqueKey key;
+    make_unique_key<0>(&key, 0);
 
-    GrCacheID::Key key2Data;
-    key2Data.fData64[0] = 1;
-    key2Data.fData64[1] = 0;
-    GrResourceKey key2(GrCacheID(domain, key2Data), t, 0);
+    // Create two resources that we will attempt to register with the same unique key.
+    TestResource* a = new TestResource(context->getGpu());
+    a->setSize(11);
+
+    // Set key on resource a.
+    a->resourcePriv().setUniqueKey(key);
+    REPORTER_ASSERT(reporter, a == cache->findAndRefUniqueResource(key));
+    a->unref();
+
+    // Make sure that redundantly setting a's key works.
+    a->resourcePriv().setUniqueKey(key);
+    REPORTER_ASSERT(reporter, a == cache->findAndRefUniqueResource(key));
+    a->unref();
+    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, a->gpuMemorySize() == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
+
+    // Create resource b and set the same key. It should replace a's unique key cache entry.
+    TestResource* b = new TestResource(context->getGpu());
+    b->setSize(12);
+    b->resourcePriv().setUniqueKey(key);
+    REPORTER_ASSERT(reporter, b == cache->findAndRefUniqueResource(key));
+    b->unref();
+
+    // Still have two resources because a is still reffed.
+    REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, a->gpuMemorySize() + b->gpuMemorySize() == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+
+    a->unref();
+    // Now a should be gone.
+    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, b->gpuMemorySize() == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
+
+    // Now replace b with c, but make sure c can start with one unique key and change it to b's key.
+    // Also make b be unreffed when replacement occurs.
+    b->unref();
+    TestResource* c = new TestResource(context->getGpu());
+    GrUniqueKey differentKey;
+    make_unique_key<0>(&differentKey, 1);
+    c->setSize(13);
+    c->resourcePriv().setUniqueKey(differentKey);
+    REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, b->gpuMemorySize() + c->gpuMemorySize() == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+    // c replaces b and b should be immediately purged.
+    c->resourcePriv().setUniqueKey(key);
+    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
+
+    // c shouldn't be purged because it is ref'ed.
+    cache->purgeAllUnlocked();
+    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
+
+    // Drop the ref on c, it should be kept alive because it has a unique key.
+    c->unref();
+    REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, c->gpuMemorySize() == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
+
+    // Verify that we can find c, then remove its unique key. It should get purged immediately.
+    REPORTER_ASSERT(reporter, c == cache->findAndRefUniqueResource(key));
+    c->resourcePriv().removeUniqueKey();
+    c->unref();
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
+
+    {
+        GrUniqueKey key2;
+        make_unique_key<0>(&key2, 0);
+        sk_sp<TestResource> d(new TestResource(context->getGpu()));
+        int foo = 4132;
+        key2.setCustomData(SkData::MakeWithCopy(&foo, sizeof(foo)));
+        d->resourcePriv().setUniqueKey(key2);
+    }
+
+    GrUniqueKey key3;
+    make_unique_key<0>(&key3, 0);
+    sk_sp<GrGpuResource> d2(cache->findAndRefUniqueResource(key3));
+    REPORTER_ASSERT(reporter, *(int*) d2->getUniqueKey().getCustomData()->data() == 4132);
+}
+
+static void test_purge_invalidated(skiatest::Reporter* reporter) {
+    Mock mock(5, 30000);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    GrUniqueKey key1, key2, key3;
+    make_unique_key<0>(&key1, 1);
+    make_unique_key<0>(&key2, 2);
+    make_unique_key<0>(&key3, 3);
+
+    // Add three resources to the cache. Only c is usable as scratch.
+    TestResource* a = new TestResource(context->getGpu());
+    TestResource* b = new TestResource(context->getGpu());
+    TestResource* c = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
+                                                  TestResource::kA_SimulatedProperty);
+    a->resourcePriv().setUniqueKey(key1);
+    b->resourcePriv().setUniqueKey(key2);
+    c->resourcePriv().setUniqueKey(key3);
+    a->unref();
+    // hold b until *after* the message is sent.
+    c->unref();
+
+    REPORTER_ASSERT(reporter, cache->hasUniqueKey(key1));
+    REPORTER_ASSERT(reporter, cache->hasUniqueKey(key2));
+    REPORTER_ASSERT(reporter, cache->hasUniqueKey(key3));
+    REPORTER_ASSERT(reporter, 3 == TestResource::NumAlive());
+
+    typedef GrUniqueKeyInvalidatedMessage Msg;
+    typedef SkMessageBus<GrUniqueKeyInvalidatedMessage> Bus;
+
+    // Invalidate two of the three, they should be purged and no longer accessible via their keys.
+    Bus::Post(Msg(key1));
+    Bus::Post(Msg(key2));
+    cache->purgeAsNeeded();
+    // a should be deleted now, but we still have a ref on b.
+    REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1));
+    REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key2));
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+    REPORTER_ASSERT(reporter, cache->hasUniqueKey(key3));
+
+    // Invalidate the third.
+    Bus::Post(Msg(key3));
+    cache->purgeAsNeeded();
+    // we still have a ref on b, c should be recycled as scratch.
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+    REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key3));
+
+    // make b purgeable. It should be immediately deleted since it has no key.
+    b->unref();
+    REPORTER_ASSERT(reporter, 1 == TestResource::NumAlive());
+
+    // Make sure we actually get to c via it's scratch key, before we say goodbye.
+    GrScratchKey scratchKey;
+    TestResource::ComputeScratchKey(TestResource::kA_SimulatedProperty, &scratchKey);
+    GrGpuResource* scratch = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0);
+    REPORTER_ASSERT(reporter, scratch == c);
+    SkSafeUnref(scratch);
+
+    // Get rid of c.
+    cache->purgeAllUnlocked();
+    scratch = cache->findAndRefScratchResource(scratchKey, TestResource::kDefaultSize, 0);
+    REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceBytes());
+    REPORTER_ASSERT(reporter, !scratch);
+    SkSafeUnref(scratch);
+}
+
+static void test_cache_chained_purge(skiatest::Reporter* reporter) {
+    Mock mock(3, 30000);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    GrUniqueKey key1, key2;
+    make_unique_key<0>(&key1, 1);
+    make_unique_key<0>(&key2, 2);
+
+    TestResource* a = new TestResource(context->getGpu());
+    TestResource* b = new TestResource(context->getGpu());
+    a->resourcePriv().setUniqueKey(key1);
+    b->resourcePriv().setUniqueKey(key2);
+
+    // Make a cycle
+    a->setUnrefWhenDestroyed(b);
+    b->setUnrefWhenDestroyed(a);
+
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+
+    a->unref();
+    b->unref();
+
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+
+    cache->purgeAllUnlocked();
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+
+    // Break the cycle
+    a->setUnrefWhenDestroyed(nullptr);
+    REPORTER_ASSERT(reporter, 2 == TestResource::NumAlive());
+
+    cache->purgeAllUnlocked();
+    REPORTER_ASSERT(reporter, 0 == TestResource::NumAlive());
+}
+
+static void test_resource_size_changed(skiatest::Reporter* reporter) {
+    GrUniqueKey key1, key2;
+    make_unique_key<0>(&key1, 1);
+    make_unique_key<0>(&key2, 2);
 
     // Test changing resources sizes (both increase & decrease).
     {
-        GrResourceCache cache(2, 300);
+        Mock mock(3, 30000);
+        GrContext* context = mock.context();
+        GrResourceCache* cache = mock.cache();
 
         TestResource* a = new TestResource(context->getGpu());
-        a->setSize(100); // Test didChangeGpuMemorySize() when not in the cache.
-        cache.addResource(key1, a);
+        a->resourcePriv().setUniqueKey(key1);
         a->unref();
 
         TestResource* b = new TestResource(context->getGpu());
-        b->setSize(100);
-        cache.addResource(key2, b);
+        b->resourcePriv().setUniqueKey(key2);
         b->unref();
 
-        REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes());
-        REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
+        REPORTER_ASSERT(reporter, 200 == cache->getResourceBytes());
+        REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
+        {
+            sk_sp<TestResource> find2(
+                static_cast<TestResource*>(cache->findAndRefUniqueResource(key2)));
+            find2->setSize(200);
+            sk_sp<TestResource> find1(
+                static_cast<TestResource*>(cache->findAndRefUniqueResource(key1)));
+            find1->setSize(50);
+        }
 
-        static_cast<TestResource*>(cache.find(key2))->setSize(200);
-        static_cast<TestResource*>(cache.find(key1))->setSize(50);
-
-        REPORTER_ASSERT(reporter, 250 == cache.getCachedResourceBytes());
-        REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
+        REPORTER_ASSERT(reporter, 250 == cache->getResourceBytes());
+        REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
     }
 
     // Test increasing a resources size beyond the cache budget.
     {
-        GrResourceCache cache(2, 300);
+        Mock mock(2, 300);
+        GrContext* context = mock.context();
+        GrResourceCache* cache = mock.cache();
 
-        TestResource* a = new TestResource(context->getGpu(), 100);
-        cache.addResource(key1, a);
+        TestResource* a = new TestResource(context->getGpu());
+        a->setSize(100);
+        a->resourcePriv().setUniqueKey(key1);
         a->unref();
 
-        TestResource* b = new TestResource(context->getGpu(), 100);
-        cache.addResource(key2, b);
+        TestResource* b = new TestResource(context->getGpu());
+        b->setSize(100);
+        b->resourcePriv().setUniqueKey(key2);
         b->unref();
 
-        REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes());
-        REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
+        REPORTER_ASSERT(reporter, 200 == cache->getResourceBytes());
+        REPORTER_ASSERT(reporter, 2 == cache->getResourceCount());
 
-        static_cast<TestResource*>(cache.find(key2))->setSize(201);
-        REPORTER_ASSERT(reporter, NULL == cache.find(key1));
+        {
+            sk_sp<TestResource> find2(static_cast<TestResource*>(
+                cache->findAndRefUniqueResource(key2)));
+            find2->setSize(201);
+        }
+        REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1));
 
-        REPORTER_ASSERT(reporter, 201 == cache.getCachedResourceBytes());
-        REPORTER_ASSERT(reporter, 1 == cache.getCachedResourceCount());
-    }
-
-    // Test changing the size of an exclusively-held resource.
-    {
-        GrResourceCache cache(2, 300);
-
-        TestResource* a = new TestResource(context->getGpu(), 100);
-        cache.addResource(key1, a);
-        cache.makeExclusive(a->getCacheEntry());
-
-        TestResource* b = new TestResource(context->getGpu(), 100);
-        cache.addResource(key2, b);
-        b->unref();
-
-        REPORTER_ASSERT(reporter, 200 == cache.getCachedResourceBytes());
-        REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
-        REPORTER_ASSERT(reporter, NULL == cache.find(key1));
-
-        a->setSize(200);
-
-        REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes());
-        REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
-        // Internal resource cache validation will test the detached size (debug mode only).
-
-        cache.makeNonExclusive(a->getCacheEntry());
-        a->unref();
-
-        REPORTER_ASSERT(reporter, 300 == cache.getCachedResourceBytes());
-        REPORTER_ASSERT(reporter, 2 == cache.getCachedResourceCount());
-        REPORTER_ASSERT(reporter, cache.find(key1));
-        // Internal resource cache validation will test the detached size (debug mode only).
+        REPORTER_ASSERT(reporter, 201 == cache->getResourceBytes());
+        REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
     }
 }
 
+static void test_timestamp_wrap(skiatest::Reporter* reporter) {
+    static const int kCount = 50;
+    static const int kBudgetCnt = kCount / 2;
+    static const int kLockedFreq = 8;
+    static const int kBudgetSize = 0x80000000;
+
+    SkRandom random;
+
+    // Run the test 2*kCount times;
+    for (int i = 0; i < 2 * kCount; ++i ) {
+        Mock mock(kBudgetCnt, kBudgetSize);
+        GrContext* context = mock.context();
+        GrResourceCache* cache = mock.cache();
+
+        // Pick a random number of resources to add before the timestamp will wrap.
+        cache->changeTimestamp(SK_MaxU32 - random.nextULessThan(kCount + 1));
+
+        static const int kNumToPurge = kCount - kBudgetCnt;
+
+        SkTDArray<int> shouldPurgeIdxs;
+        int purgeableCnt = 0;
+        SkTDArray<GrGpuResource*> resourcesToUnref;
+
+        // Add kCount resources, holding onto resources at random so we have a mix of purgeable and
+        // unpurgeable resources.
+        for (int j = 0; j < kCount; ++j) {
+            GrUniqueKey key;
+            make_unique_key<0>(&key, j);
+
+            TestResource* r = new TestResource(context->getGpu());
+            r->resourcePriv().setUniqueKey(key);
+            if (random.nextU() % kLockedFreq) {
+                // Make this is purgeable.
+                r->unref();
+                ++purgeableCnt;
+                if (purgeableCnt <= kNumToPurge) {
+                    *shouldPurgeIdxs.append() = j;
+                }
+            } else {
+                *resourcesToUnref.append() = r;
+            }
+        }
+
+        // Verify that the correct resources were purged.
+        int currShouldPurgeIdx = 0;
+        for (int j = 0; j < kCount; ++j) {
+            GrUniqueKey key;
+            make_unique_key<0>(&key, j);
+            GrGpuResource* res = cache->findAndRefUniqueResource(key);
+            if (currShouldPurgeIdx < shouldPurgeIdxs.count() &&
+                shouldPurgeIdxs[currShouldPurgeIdx] == j) {
+                ++currShouldPurgeIdx;
+                REPORTER_ASSERT(reporter, nullptr == res);
+            } else {
+                REPORTER_ASSERT(reporter, nullptr != res);
+            }
+            SkSafeUnref(res);
+        }
+
+        for (int j = 0; j < resourcesToUnref.count(); ++j) {
+            resourcesToUnref[j]->unref();
+        }
+    }
+}
+
+static void test_flush(skiatest::Reporter* reporter) {
+    Mock mock(1000000, 1000000);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    // The current cache impl will round the max flush count to the next power of 2. So we choose a
+    // power of two here to keep things simpler.
+    static const int kFlushCount = 16;
+    cache->setLimits(1000000, 1000000, kFlushCount);
+
+    {
+        // Insert a resource and send a flush notification kFlushCount times.
+        for (int i = 0; i < kFlushCount; ++i) {
+            TestResource* r = new TestResource(context->getGpu());
+            GrUniqueKey k;
+            make_unique_key<1>(&k, i);
+            r->resourcePriv().setUniqueKey(k);
+            r->unref();
+            cache->notifyFlushOccurred(GrResourceCache::kExternal);
+        }
+
+        // Send flush notifications to the cache. Each flush should purge the oldest resource.
+        for (int i = 0; i < kFlushCount; ++i) {
+            cache->notifyFlushOccurred(GrResourceCache::kExternal);
+            REPORTER_ASSERT(reporter, kFlushCount - i - 1 == cache->getResourceCount());
+            for (int j = 0; j < i; ++j) {
+                GrUniqueKey k;
+                make_unique_key<1>(&k, j);
+                GrGpuResource* r = cache->findAndRefUniqueResource(k);
+                REPORTER_ASSERT(reporter, !SkToBool(r));
+                SkSafeUnref(r);
+            }
+        }
+
+        REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+        cache->purgeAllUnlocked();
+    }
+
+    // Do a similar test but where we leave refs on some resources to prevent them from being
+    // purged.
+    {
+        GrGpuResource* refedResources[kFlushCount >> 1];
+        for (int i = 0; i < kFlushCount; ++i) {
+            TestResource* r = new TestResource(context->getGpu());
+            GrUniqueKey k;
+            make_unique_key<1>(&k, i);
+            r->resourcePriv().setUniqueKey(k);
+            // Leave a ref on every other resource, beginning with the first.
+            if (SkToBool(i & 0x1)) {
+                refedResources[i/2] = r;
+            } else {
+                r->unref();
+            }
+            cache->notifyFlushOccurred(GrResourceCache::kExternal);
+        }
+
+        for (int i = 0; i < kFlushCount; ++i) {
+            // Should get a resource purged every other flush.
+            cache->notifyFlushOccurred(GrResourceCache::kExternal);
+            REPORTER_ASSERT(reporter, kFlushCount - i/2 - 1 == cache->getResourceCount());
+        }
+
+        // Unref all the resources that we kept refs on in the first loop.
+        for (int i = 0; i < kFlushCount >> 1; ++i) {
+            refedResources[i]->unref();
+        }
+
+        // After kFlushCount + 1 flushes they all will have sat in the purgeable queue for
+        // kFlushCount full flushes.
+        for (int i = 0; i < kFlushCount + 1; ++i) {
+            REPORTER_ASSERT(reporter, kFlushCount >> 1 == cache->getResourceCount());
+            cache->notifyFlushOccurred(GrResourceCache::kExternal);
+        }
+        REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+
+        cache->purgeAllUnlocked();
+    }
+
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+
+    // Verify that calling flush() on a GrContext with nothing to do will not trigger resource
+    // eviction.
+    context->flush();
+    for (int i = 0; i < 10; ++i) {
+        TestResource* r = new TestResource(context->getGpu());
+        GrUniqueKey k;
+        make_unique_key<1>(&k, i);
+        r->resourcePriv().setUniqueKey(k);
+        r->unref();
+    }
+    REPORTER_ASSERT(reporter, 10 == cache->getResourceCount());
+    for (int i = 0; i < 10 * kFlushCount; ++i) {
+        context->flush();
+    }
+    REPORTER_ASSERT(reporter, 10 == cache->getResourceCount());
+}
+
+static void test_time_purge(skiatest::Reporter* reporter) {
+    Mock mock(1000000, 1000000);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    static constexpr int kCnts[] = {1, 10, 1024};
+    auto nowish = []() {
+        // We sleep so that we ensure we get a value that is greater than the last call to
+        // GrStdSteadyClock::now().
+        std::this_thread::sleep_for(GrStdSteadyClock::duration(5));
+        auto result = GrStdSteadyClock::now();
+        // Also sleep afterwards so we don't get this value again.
+        std::this_thread::sleep_for(GrStdSteadyClock::duration(5));
+        return result;
+    };
+
+    for (int cnt : kCnts) {
+        std::unique_ptr<GrStdSteadyClock::time_point[]> timeStamps(
+                new GrStdSteadyClock::time_point[cnt]);
+        {
+            // Insert resources and get time points between each addition.
+            for (int i = 0; i < cnt; ++i) {
+                TestResource* r = new TestResource(context->getGpu());
+                GrUniqueKey k;
+                make_unique_key<1>(&k, i);
+                r->resourcePriv().setUniqueKey(k);
+                r->unref();
+                timeStamps.get()[i] = nowish();
+            }
+
+            // Purge based on the time points between resource additions. Each purge should remove
+            // the oldest resource.
+            for (int i = 0; i < cnt; ++i) {
+                cache->purgeResourcesNotUsedSince(timeStamps[i]);
+                REPORTER_ASSERT(reporter, cnt - i - 1 == cache->getResourceCount());
+                for (int j = 0; j < i; ++j) {
+                    GrUniqueKey k;
+                    make_unique_key<1>(&k, j);
+                    GrGpuResource* r = cache->findAndRefUniqueResource(k);
+                    REPORTER_ASSERT(reporter, !SkToBool(r));
+                    SkSafeUnref(r);
+                }
+            }
+
+            REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+            cache->purgeAllUnlocked();
+        }
+
+        // Do a similar test but where we leave refs on some resources to prevent them from being
+        // purged.
+        {
+            std::unique_ptr<GrGpuResource* []> refedResources(new GrGpuResource*[cnt / 2]);
+            for (int i = 0; i < cnt; ++i) {
+                TestResource* r = new TestResource(context->getGpu());
+                GrUniqueKey k;
+                make_unique_key<1>(&k, i);
+                r->resourcePriv().setUniqueKey(k);
+                // Leave a ref on every other resource, beginning with the first.
+                if (SkToBool(i & 0x1)) {
+                    refedResources.get()[i / 2] = r;
+                } else {
+                    r->unref();
+                }
+                timeStamps.get()[i] = nowish();
+            }
+
+            for (int i = 0; i < cnt; ++i) {
+                // Should get a resource purged every other frame.
+                cache->purgeResourcesNotUsedSince(timeStamps[i]);
+                REPORTER_ASSERT(reporter, cnt - i / 2 - 1 == cache->getResourceCount());
+            }
+
+            // Unref all the resources that we kept refs on in the first loop.
+            for (int i = 0; i < (cnt / 2); ++i) {
+                refedResources.get()[i]->unref();
+                cache->purgeResourcesNotUsedSince(nowish());
+                REPORTER_ASSERT(reporter, cnt / 2 - i - 1 == cache->getResourceCount());
+            }
+
+            cache->purgeAllUnlocked();
+        }
+
+        REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+
+        // Verify that calling flush() on a GrContext with nothing to do will not trigger resource
+        // eviction
+        context->flush();
+        for (int i = 0; i < 10; ++i) {
+            TestResource* r = new TestResource(context->getGpu());
+            GrUniqueKey k;
+            make_unique_key<1>(&k, i);
+            r->resourcePriv().setUniqueKey(k);
+            r->unref();
+        }
+        REPORTER_ASSERT(reporter, 10 == cache->getResourceCount());
+        context->flush();
+        REPORTER_ASSERT(reporter, 10 == cache->getResourceCount());
+        cache->purgeResourcesNotUsedSince(nowish());
+        REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+    }
+}
+
+static void test_partial_purge(skiatest::Reporter* reporter) {
+    Mock mock(6, 100);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    enum TestsCase {
+        kOnlyScratch_TestCase = 0,
+        kPartialScratch_TestCase = 1,
+        kAllScratch_TestCase = 2,
+        kPartial_TestCase = 3,
+        kAll_TestCase = 4,
+        kNone_TestCase = 5,
+        kEndTests_TestCase = kNone_TestCase + 1
+    };
+
+    for (int testCase = 0; testCase < kEndTests_TestCase; testCase++) {
+
+        GrUniqueKey key1, key2, key3;
+        make_unique_key<0>(&key1, 1);
+        make_unique_key<0>(&key2, 2);
+        make_unique_key<0>(&key3, 3);
+
+        // Add three unique resources to the cache.
+        TestResource *unique1 = new TestResource(context->getGpu());
+        TestResource *unique2 = new TestResource(context->getGpu());
+        TestResource *unique3 = new TestResource(context->getGpu());
+
+        unique1->resourcePriv().setUniqueKey(key1);
+        unique2->resourcePriv().setUniqueKey(key2);
+        unique3->resourcePriv().setUniqueKey(key3);
+
+        unique1->setSize(10);
+        unique2->setSize(11);
+        unique3->setSize(12);
+
+        // Add two scratch resources to the cache.
+        TestResource *scratch1 = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
+                                                             TestResource::kA_SimulatedProperty);
+        TestResource *scratch2 = TestResource::CreateScratch(context->getGpu(), SkBudgeted::kYes,
+                                                             TestResource::kB_SimulatedProperty);
+        scratch1->setSize(13);
+        scratch2->setSize(14);
+
+
+        REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount());
+        REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes());
+        REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+
+        // Add resources to the purgeable queue
+        unique1->unref();
+        scratch1->unref();
+        unique2->unref();
+        scratch2->unref();
+        unique3->unref();
+
+        REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount());
+        REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes());
+        REPORTER_ASSERT(reporter, 60 == cache->getPurgeableBytes());
+
+        switch(testCase) {
+            case kOnlyScratch_TestCase: {
+                context->purgeUnlockedResources(14, true);
+                REPORTER_ASSERT(reporter, 3 == cache->getBudgetedResourceCount());
+                REPORTER_ASSERT(reporter, 33 == cache->getBudgetedResourceBytes());
+                break;
+            }
+            case kPartialScratch_TestCase: {
+                context->purgeUnlockedResources(3, true);
+                REPORTER_ASSERT(reporter, 4 == cache->getBudgetedResourceCount());
+                REPORTER_ASSERT(reporter, 47 == cache->getBudgetedResourceBytes());
+                break;
+            }
+            case kAllScratch_TestCase: {
+                context->purgeUnlockedResources(50, true);
+                REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
+                REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+                break;
+            }
+            case kPartial_TestCase: {
+                context->purgeUnlockedResources(13, false);
+                REPORTER_ASSERT(reporter, 3 == cache->getBudgetedResourceCount());
+                REPORTER_ASSERT(reporter, 37 == cache->getBudgetedResourceBytes());
+                break;
+            }
+            case kAll_TestCase: {
+                context->purgeUnlockedResources(50, false);
+                REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
+                REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceBytes());
+                break;
+            }
+            case kNone_TestCase: {
+                context->purgeUnlockedResources(0, true);
+                context->purgeUnlockedResources(0, false);
+                REPORTER_ASSERT(reporter, 5 == cache->getBudgetedResourceCount());
+                REPORTER_ASSERT(reporter, 60 == cache->getBudgetedResourceBytes());
+                REPORTER_ASSERT(reporter, 60 == cache->getPurgeableBytes());
+                break;
+            }
+        };
+
+        // ensure all are purged before the next
+        context->purgeAllUnlockedResources();
+        REPORTER_ASSERT(reporter, 0 == cache->getBudgetedResourceCount());
+        REPORTER_ASSERT(reporter, 0 == cache->getPurgeableBytes());
+
+    }
+}
+
+static void test_large_resource_count(skiatest::Reporter* reporter) {
+    // Set the cache size to double the resource count because we're going to create 2x that number
+    // resources, using two different key domains. Add a little slop to the bytes because we resize
+    // down to 1 byte after creating the resource.
+    static const int kResourceCnt = 2000;
+
+    Mock mock(2 * kResourceCnt, 2 * kResourceCnt + 1000);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    for (int i = 0; i < kResourceCnt; ++i) {
+        GrUniqueKey key1, key2;
+        make_unique_key<1>(&key1, i);
+        make_unique_key<2>(&key2, i);
+
+        TestResource* resource;
+
+        resource = new TestResource(context->getGpu());
+        resource->resourcePriv().setUniqueKey(key1);
+        resource->setSize(1);
+        resource->unref();
+
+        resource = new TestResource(context->getGpu());
+        resource->resourcePriv().setUniqueKey(key2);
+        resource->setSize(1);
+        resource->unref();
+    }
+
+    REPORTER_ASSERT(reporter, TestResource::NumAlive() == 2 * kResourceCnt);
+    REPORTER_ASSERT(reporter, cache->getPurgeableBytes() == 2 * kResourceCnt);
+    REPORTER_ASSERT(reporter, cache->getBudgetedResourceBytes() == 2 * kResourceCnt);
+    REPORTER_ASSERT(reporter, cache->getBudgetedResourceCount() == 2 * kResourceCnt);
+    REPORTER_ASSERT(reporter, cache->getResourceBytes() == 2 * kResourceCnt);
+    REPORTER_ASSERT(reporter, cache->getResourceCount() == 2 * kResourceCnt);
+    for (int i = 0; i < kResourceCnt; ++i) {
+        GrUniqueKey key1, key2;
+        make_unique_key<1>(&key1, i);
+        make_unique_key<2>(&key2, i);
+
+        REPORTER_ASSERT(reporter, cache->hasUniqueKey(key1));
+        REPORTER_ASSERT(reporter, cache->hasUniqueKey(key2));
+    }
+
+    cache->purgeAllUnlocked();
+    REPORTER_ASSERT(reporter, TestResource::NumAlive() == 0);
+    REPORTER_ASSERT(reporter, cache->getPurgeableBytes() == 0);
+    REPORTER_ASSERT(reporter, cache->getBudgetedResourceBytes() == 0);
+    REPORTER_ASSERT(reporter, cache->getBudgetedResourceCount() == 0);
+    REPORTER_ASSERT(reporter, cache->getResourceBytes() == 0);
+    REPORTER_ASSERT(reporter, cache->getResourceCount() == 0);
+
+    for (int i = 0; i < kResourceCnt; ++i) {
+        GrUniqueKey key1, key2;
+        make_unique_key<1>(&key1, i);
+        make_unique_key<2>(&key2, i);
+
+        REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key1));
+        REPORTER_ASSERT(reporter, !cache->hasUniqueKey(key2));
+    }
+}
+
+static void test_custom_data(skiatest::Reporter* reporter) {
+    GrUniqueKey key1, key2;
+    make_unique_key<0>(&key1, 1);
+    make_unique_key<0>(&key2, 2);
+    int foo = 4132;
+    key1.setCustomData(SkData::MakeWithCopy(&foo, sizeof(foo)));
+    REPORTER_ASSERT(reporter, *(int*) key1.getCustomData()->data() == 4132);
+    REPORTER_ASSERT(reporter, key2.getCustomData() == nullptr);
+
+    // Test that copying a key also takes a ref on its custom data.
+    GrUniqueKey key3 = key1;
+    REPORTER_ASSERT(reporter, *(int*) key3.getCustomData()->data() == 4132);
+}
+
+static void test_abandoned(skiatest::Reporter* reporter) {
+    Mock mock(10, 300);
+    GrContext* context = mock.context();
+    sk_sp<GrGpuResource> resource(new TestResource(context->getGpu()));
+    context->abandonContext();
+
+    REPORTER_ASSERT(reporter, resource->wasDestroyed());
+
+    // Call all the public methods on resource in the abandoned state. They shouldn't crash.
+
+    resource->uniqueID();
+    resource->getUniqueKey();
+    resource->wasDestroyed();
+    resource->gpuMemorySize();
+    resource->getContext();
+
+    resource->abandon();
+    resource->resourcePriv().getScratchKey();
+    resource->resourcePriv().isBudgeted();
+    resource->resourcePriv().makeBudgeted();
+    resource->resourcePriv().makeUnbudgeted();
+    resource->resourcePriv().removeScratchKey();
+    GrUniqueKey key;
+    make_unique_key<0>(&key, 1);
+    resource->resourcePriv().setUniqueKey(key);
+    resource->resourcePriv().removeUniqueKey();
+}
+
+static void test_tags(skiatest::Reporter* reporter) {
+#ifdef SK_DEBUG
+    // We will insert 1 resource with tag "tag1", 2 with "tag2", and so on, up through kLastTagIdx.
+    static constexpr int kLastTagIdx = 10;
+    static constexpr int kNumResources = kLastTagIdx * (kLastTagIdx + 1) / 2;
+
+    Mock mock(kNumResources, kNumResources * TestResource::kDefaultSize);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    SkString tagStr;
+    int tagIdx = 0;
+    int currTagCnt = 0;
+
+    for (int i = 0; i < kNumResources; ++i, ++currTagCnt) {
+        sk_sp<GrGpuResource> resource(new TestResource(context->getGpu()));
+        GrUniqueKey key;
+        if (currTagCnt == tagIdx) {
+            tagIdx += 1;
+            currTagCnt = 0;
+            tagStr.printf("tag%d", tagIdx);
+        }
+        make_unique_key<1>(&key, i, tagStr.c_str());
+        resource->resourcePriv().setUniqueKey(key);
+    }
+    SkASSERT(kLastTagIdx == tagIdx);
+    SkASSERT(currTagCnt == kLastTagIdx);
+
+    // Test i = 0 to exercise unused tag string.
+    for (int i = 0; i <= kLastTagIdx; ++i) {
+        tagStr.printf("tag%d", i);
+        REPORTER_ASSERT(reporter, cache->countUniqueKeysWithTag(tagStr.c_str()) == i);
+    }
+#endif
+}
+
+DEF_GPUTEST(ResourceCacheMisc, reporter, factory) {
+    // The below tests create their own mock contexts.
+    test_no_key(reporter);
+    test_budgeting(reporter);
+    test_unbudgeted(reporter);
+    test_unbudgeted_to_scratch(reporter);
+    test_duplicate_unique_key(reporter);
+    test_duplicate_scratch_key(reporter);
+    test_remove_scratch_key(reporter);
+    test_scratch_key_consistency(reporter);
+    test_purge_invalidated(reporter);
+    test_cache_chained_purge(reporter);
+    test_resource_size_changed(reporter);
+    test_timestamp_wrap(reporter);
+    test_flush(reporter);
+    test_time_purge(reporter);
+    test_partial_purge(reporter);
+    test_large_resource_count(reporter);
+    test_custom_data(reporter);
+    test_abandoned(reporter);
+    test_tags(reporter);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
-DEF_GPUTEST(ResourceCache, reporter, factory) {
-    for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
-        GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type);
-        if (!GrContextFactory::IsRenderingGLContext(glType)) {
-            continue;
-        }
-        GrContext* context = factory->get(glType);
-        if (NULL == context) {
-            continue;
+static sk_sp<GrTexture> make_normal_texture(GrResourceProvider* provider,
+                                            GrSurfaceFlags flags,
+                                            int width, int height,
+                                            int sampleCnt) {
+    GrSurfaceDesc desc;
+    desc.fFlags = flags;
+    desc.fWidth = width;
+    desc.fHeight = height;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fSampleCnt = sampleCnt;
+
+    return provider->createTexture(desc, SkBudgeted::kYes);
+}
+
+static sk_sp<GrTextureProxy> make_mipmap_proxy(GrResourceProvider* provider,
+                                               GrSurfaceFlags flags,
+                                               int width, int height,
+                                               int sampleCnt) {
+    SkBitmap bm;
+
+    bm.allocN32Pixels(width, height, true);
+    bm.eraseColor(SK_ColorBLUE);
+
+    sk_sp<SkMipMap> mipmaps(SkMipMap::Build(bm, SkDestinationSurfaceColorMode::kLegacy, nullptr));
+    SkASSERT(mipmaps);
+    SkASSERT(mipmaps->countLevels() > 1);
+
+    int mipLevelCount = mipmaps->countLevels() + 1;
+
+    std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]);
+
+    texels[0].fPixels = bm.getPixels();
+    texels[0].fRowBytes = bm.rowBytes();
+
+    for (int i = 1; i < mipLevelCount; ++i) {
+        SkMipMap::Level generatedMipLevel;
+        mipmaps->getLevel(i - 1, &generatedMipLevel);
+        texels[i].fPixels = generatedMipLevel.fPixmap.addr();
+        texels[i].fRowBytes = generatedMipLevel.fPixmap.rowBytes();
+    }
+
+    GrSurfaceDesc desc;
+    desc.fFlags = flags;
+    desc.fWidth = width;
+    desc.fHeight = height;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fSampleCnt = sampleCnt;
+    desc.fIsMipMapped = true;
+
+    return GrSurfaceProxy::MakeDeferredMipMap(provider, desc, SkBudgeted::kYes,
+                                              texels.get(), mipLevelCount);
+}
+
+// Exercise GrSurface::gpuMemorySize for different combos of MSAA, RT-only,
+// Texture-only, both-RT-and-Texture and MIPmapped
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GPUMemorySize, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    GrResourceProvider* provider = context->resourceProvider();
+
+    static const int kSize = 64;
+
+    // Normal versions
+    {
+        sk_sp<GrTexture> tex;
+
+        tex = make_normal_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 0);
+        size_t size = tex->gpuMemorySize();
+        REPORTER_ASSERT(reporter, kSize*kSize*4 == size);
+
+        size_t sampleCount = (size_t)context->caps()->getSampleCount(4, kRGBA_8888_GrPixelConfig);
+        if (sampleCount >= 4) {
+            tex = make_normal_texture(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize,
+                                      sampleCount);
+            size = tex->gpuMemorySize();
+            REPORTER_ASSERT(reporter,
+                            kSize*kSize*4 == size ||                  // msaa4 failed
+                            kSize*kSize*4*sampleCount == size ||      // auto-resolving
+                            kSize*kSize*4*(sampleCount+1) == size);   // explicit resolve buffer
         }
 
-        GrTextureDesc desc;
-        desc.fConfig = kSkia8888_GrPixelConfig;
-        desc.fFlags = kRenderTarget_GrTextureFlagBit;
-        desc.fWidth = gWidth;
-        desc.fHeight = gHeight;
-        SkImageInfo info = SkImageInfo::MakeN32Premul(gWidth, gHeight);
-        SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(context, info));
+        tex = make_normal_texture(provider, kNone_GrSurfaceFlags, kSize, kSize, 0);
+        size = tex->gpuMemorySize();
+        REPORTER_ASSERT(reporter, kSize*kSize*4 == size);
+    }
 
-        test_cache(reporter, context, surface->getCanvas());
-        test_purge_invalidated(reporter, context);
-        test_cache_delete_on_destruction(reporter, context);
-        test_resource_size_changed(reporter, context);
+
+    // Mipmapped versions
+    if (context->caps()->mipMapSupport()) {
+        sk_sp<GrTextureProxy> proxy;
+
+        proxy = make_mipmap_proxy(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize, 0);
+        size_t size = proxy->gpuMemorySize();
+        REPORTER_ASSERT(reporter, kSize*kSize*4+(kSize*kSize*4)/3 == size);
+
+        size_t sampleCount = (size_t)context->caps()->getSampleCount(4, kRGBA_8888_GrPixelConfig);
+        if (sampleCount >= 4) {
+            proxy = make_mipmap_proxy(provider, kRenderTarget_GrSurfaceFlag, kSize, kSize,
+                                      sampleCount);
+            size = proxy->gpuMemorySize();
+            REPORTER_ASSERT(reporter,
+               kSize*kSize*4+(kSize*kSize*4)/3 == size ||                 // msaa4 failed
+               kSize*kSize*4*sampleCount+(kSize*kSize*4)/3 == size ||     // auto-resolving
+               kSize*kSize*4*(sampleCount+1)+(kSize*kSize*4)/3 == size);  // explicit resolve buffer
+        }
+
+        proxy = make_mipmap_proxy(provider, kNone_GrSurfaceFlags, kSize, kSize, 0);
+        size = proxy->gpuMemorySize();
+        REPORTER_ASSERT(reporter, kSize*kSize*4+(kSize*kSize*4)/3 == size);
     }
 }
 
diff --git a/src/third_party/skia/tests/RoundRectTest.cpp b/src/third_party/skia/tests/RoundRectTest.cpp
index 261ec67..ce97445 100644
--- a/src/third_party/skia/tests/RoundRectTest.cpp
+++ b/src/third_party/skia/tests/RoundRectTest.cpp
@@ -9,6 +9,99 @@
 #include "SkRRect.h"
 #include "Test.h"
 
+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);
+    }
+
+    {
+        // 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);
+
+        REPORTER_ASSERT(reporter, (double) rr.radii(SkRRect::kUpperRight_Corner).fY +
+                                  (double) rr.radii(SkRRect::kLowerRight_Corner).fY <=
+                                  rr.height());
+    }
+}
+
+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);
+
+    SkRRect other;
+    SkMatrix matrix;
+    matrix.setScale(0, 1);
+    rr.transform(matrix, &other);
+    REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == other.getType());
+}
+
+// Test that all the SkRRect entry points correctly handle un-sorted and
+// zero-sized input rects
+static void test_empty(skiatest::Reporter* reporter) {
+    static const SkRect oooRects[] = {  // out of order
+        { 100, 0, 0, 100 },  // ooo horizontal
+        { 0, 100, 100, 0 },  // ooo vertical
+        { 100, 100, 0, 0 },  // ooo both
+    };
+
+    static const SkRect emptyRects[] = {
+        { 100, 100, 100, 200 }, // empty horizontal
+        { 100, 100, 200, 100 }, // empty vertical
+        { 100, 100, 100, 100 }, // empty both
+        { 0, 0, 0, 0 }          // setEmpty-empty
+    };
+
+    static const SkVector radii[4] = { { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 } };
+
+    SkRRect r;
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(oooRects); ++i) {
+        r.setRect(oooRects[i]);
+        REPORTER_ASSERT(reporter, !r.isEmpty());
+
+        r.setOval(oooRects[i]);
+        REPORTER_ASSERT(reporter, !r.isEmpty());
+
+        r.setRectXY(oooRects[i], 1, 2);
+        REPORTER_ASSERT(reporter, !r.isEmpty());
+
+        r.setNinePatch(oooRects[i], 0, 1, 2, 3);
+        REPORTER_ASSERT(reporter, !r.isEmpty());
+
+        r.setRectRadii(oooRects[i], radii);
+        REPORTER_ASSERT(reporter, !r.isEmpty());
+    }
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(emptyRects); ++i) {
+        r.setRect(emptyRects[i]);
+        REPORTER_ASSERT(reporter, r.isEmpty());
+
+        r.setOval(emptyRects[i]);
+        REPORTER_ASSERT(reporter, r.isEmpty());
+
+        r.setRectXY(emptyRects[i], 1, 2);
+        REPORTER_ASSERT(reporter, r.isEmpty());
+
+        r.setNinePatch(emptyRects[i], 0, 1, 2, 3);
+        REPORTER_ASSERT(reporter, r.isEmpty());
+
+        r.setRectRadii(emptyRects[i], radii);
+        REPORTER_ASSERT(reporter, r.isEmpty());
+    }
+}
+
 static const SkScalar kWidth = 100.0f;
 static const SkScalar kHeight = 100.0f;
 
@@ -33,6 +126,32 @@
     REPORTER_ASSERT(reporter, rr2.isRect());
 }
 
+
+static void test_9patch_rrect(skiatest::Reporter* reporter,
+                              const SkRect& rect,
+                              SkScalar l, SkScalar t, SkScalar r, SkScalar b,
+                              bool checkRadii) {
+    SkRRect rr;
+    rr.setNinePatch(rect, l, t, r, b);
+
+    REPORTER_ASSERT(reporter, SkRRect::kNinePatch_Type == rr.type());
+    REPORTER_ASSERT(reporter, rr.rect() == rect);
+
+    if (checkRadii) {
+        // This test doesn't hold if the radii will be rescaled by SkRRect
+        SkRect ninePatchRadii = { l, t, r, b };
+        SkPoint rquad[4];
+        ninePatchRadii.toQuad(rquad);
+        for (int i = 0; i < 4; ++i) {
+            REPORTER_ASSERT(reporter, rquad[i] == rr.radii((SkRRect::Corner) i));
+        }
+    }
+    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);
+    REPORTER_ASSERT(reporter, rr2 == rr && rr2.getType() == rr.getType());
+}
+
 // Test out the basic API entry points
 static void test_round_rect_basic(skiatest::Reporter* reporter) {
     // Test out initialization methods
@@ -109,24 +228,17 @@
     REPORTER_ASSERT(reporter, rr3_3 == rr3 && rr3_3.getType() == rr3.getType());
 
     //----
-    SkRect ninePatchRadii = { 10, 9, 8, 7 };
+    test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true);
 
-    SkRRect rr4;
-    rr4.setNinePatch(rect, ninePatchRadii.fLeft, ninePatchRadii.fTop, ninePatchRadii.fRight,
-                     ninePatchRadii.fBottom);
+    {
+        // Test out the rrect from skia:3466
+        SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f);
 
-    REPORTER_ASSERT(reporter, SkRRect::kNinePatch_Type == rr4.type());
-    REPORTER_ASSERT(reporter, rr4.rect() == rect);
-
-    SkPoint rquad[4];
-    ninePatchRadii.toQuad(rquad);
-    for (int i = 0; i < 4; ++i) {
-        REPORTER_ASSERT(reporter, rquad[i] == rr4.radii((SkRRect::Corner) i));
+        test_9patch_rrect(reporter,
+                          rect2,
+                          0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f,
+                          false);
     }
-    SkRRect rr4_2; // construct the same RR using the most general set function
-    SkVector rr4_2_radii[4] = { { 10, 9 }, { 8, 9 }, {8, 7 }, { 10, 7 } };
-    rr4_2.setRectRadii(rect, rr4_2_radii);
-    REPORTER_ASSERT(reporter, rr4_2 == rr4 && rr4_2.getType() == rr4.getType());
 
     //----
     SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } };
@@ -143,8 +255,7 @@
 
     // Test out == & !=
     REPORTER_ASSERT(reporter, empty != rr3);
-    REPORTER_ASSERT(reporter, rr3 != rr4);
-    REPORTER_ASSERT(reporter, rr4 != rr5);
+    REPORTER_ASSERT(reporter, rr3 != rr5);
 }
 
 // Test out the cases when the RR degenerates to a rect
@@ -433,11 +544,11 @@
     assert_transform_failure(reporter, orig, matrix);
 
     matrix.reset();
-    matrix.setPerspX(SkScalarToPersp(SkIntToScalar(4)));
+    matrix.setPerspX(4);
     assert_transform_failure(reporter, orig, matrix);
 
     matrix.reset();
-    matrix.setPerspY(SkScalarToPersp(SkIntToScalar(5)));
+    matrix.setPerspY(5);
     assert_transform_failure(reporter, orig, matrix);
 
     // Rotation fails.
@@ -492,7 +603,7 @@
     REPORTER_ASSERT(reporter, orig.rect().top() == dst.rect().top());
 
     // Keeping the scale, but adding a persp will make transform fail.
-    matrix.setPerspX(SkScalarToPersp(SkIntToScalar(7)));
+    matrix.setPerspX(7);
     assert_transform_failure(reporter, orig, matrix);
 
     // Scaling in -y will flip the round rect vertically.
@@ -549,18 +660,18 @@
     // Radii are scaled.
     for (int i = 0; i < 4; ++i) {
         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX,
-                                    SkScalarMul(orig.radii((SkRRect::Corner) i).fX, xScale)));
+                                    orig.radii((SkRRect::Corner) i).fX * xScale));
         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY,
-                                    SkScalarMul(orig.radii((SkRRect::Corner) i).fY, yScale)));
+                                    orig.radii((SkRRect::Corner) i).fY * yScale));
     }
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(),
-                                                  SkScalarMul(orig.rect().width(), xScale)));
+                                                  orig.rect().width() * xScale));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(),
-                                                  SkScalarMul(orig.rect().height(), yScale)));
+                                                  orig.rect().height() * yScale));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(),
-                                                  SkScalarMul(orig.rect().left(), xScale)));
+                                                  orig.rect().left() * xScale));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(),
-                                                  SkScalarMul(orig.rect().top(), yScale)));
+                                                  orig.rect().top() * yScale));
 }
 
 static void test_round_rect_transform(skiatest::Reporter* reporter) {
@@ -582,7 +693,7 @@
     }
 }
 
-// Test out the case where an oval already off in space is translated/scaled 
+// Test out the case where an oval already off in space is translated/scaled
 // further off into space - yielding numerical issues when the rect & radii
 // are transformed separatly
 // BUG=skia:2696
@@ -604,9 +715,9 @@
     SkScalar halfHeight = SkScalarHalf(dst.height());
 
     for (int i = 0; i < 4; ++i) {
-        REPORTER_ASSERT(reporter, 
+        REPORTER_ASSERT(reporter,
                         SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth));
-        REPORTER_ASSERT(reporter, 
+        REPORTER_ASSERT(reporter,
                         SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight));
     }
 }
@@ -621,4 +732,7 @@
     test_round_rect_contains_rect(reporter);
     test_round_rect_transform(reporter);
     test_issue_2696(reporter);
+    test_tricky_radii(reporter);
+    test_empty_crbug_458524(reporter);
+    test_empty(reporter);
 }
diff --git a/src/third_party/skia/tests/RuntimeConfigTest.cpp b/src/third_party/skia/tests/RuntimeConfigTest.cpp
deleted file mode 100644
index d40a4f0..0000000
--- a/src/third_party/skia/tests/RuntimeConfigTest.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SkRTConf.h"
-#include "Test.h"
-
-SK_CONF_DECLARE(int, c_RTConfTestVariable,
-                "test.utils.rtconf.testVariable", 1,
-                "This is only a test. Do not be alarmed.");
-// TODO(skia-team): more comprehensive unit tests of the SkRTConf
-// system.
-DEF_TEST(RuntimeConfig, reporter) {
-    REPORTER_ASSERT(reporter, 1 == c_RTConfTestVariable);
-
-    SK_CONF_SET("test.utils.rtconf.testVariable", 2);
-#ifdef SK_DEVELOPER
-    REPORTER_ASSERT(reporter, 2 == c_RTConfTestVariable);
-#else  // not SK_DEVELOPER
-    // Can not change RTConf variables in Release.
-    REPORTER_ASSERT(reporter, 1 == c_RTConfTestVariable);
-#endif  // SK_DEVELOPER
-
-    // This should not give a warning.
-    SK_CONF_TRY_SET("test.utils.rtconf.nonexistentVariable", 7);
-}
diff --git a/src/third_party/skia/tests/SHA1Test.cpp b/src/third_party/skia/tests/SHA1Test.cpp
deleted file mode 100644
index fd5fcb7..0000000
--- a/src/third_party/skia/tests/SHA1Test.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkSHA1.h"
-#include "Test.h"
-
-static bool digests_equal(const SkSHA1::Digest& expectedDigest, const SkSHA1::Digest& computedDigest) {
-    for (size_t i = 0; i < SK_ARRAY_COUNT(expectedDigest.data); ++i) {
-        if (expectedDigest.data[i] != computedDigest.data[i]) {
-            return false;
-        }
-    }
-    return true;
-}
-
-static struct SHA1Test {
-    const char* message;
-    const unsigned long int repeatCount;
-    const SkSHA1::Digest digest;
-} sha1_tests[] = {
-    // Reference tests from RFC3174 Section 7.3 ( http://www.ietf.org/rfc/rfc3174.txt )
-    { "abc", 1, {{ 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D }} },
-    { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 1, {{ 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 }} },
-    { "a", 1000000, {{ 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E, 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F }} },
-    { "0123456701234567012345670123456701234567012345670123456701234567", 10, {{ 0xDE, 0xA3, 0x56, 0xA2, 0xCD, 0xDD, 0x90, 0xC7, 0xA7, 0xEC, 0xED, 0xC5, 0xEB, 0xB5, 0x63, 0x93, 0x4F, 0x46, 0x04, 0x52 }} },
-
-    // Reference tests from running GNU sha1sum on test input
-    { "The quick brown fox jumps over the lazy dog", 1, {{ 0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e, 0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12 }} },
-    { "", 1, {{ 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 }} },
-};
-
-static void sha1_test(const SHA1Test& test, skiatest::Reporter* reporter) {
-    size_t len = strlen(test.message);
-
-    SkSHA1 context;
-    for (unsigned long int i = 0; i < test.repeatCount; ++i) {
-        context.update(reinterpret_cast<const uint8_t*>(test.message), len);
-    }
-    SkSHA1::Digest digest;
-    context.finish(digest);
-
-    REPORTER_ASSERT(reporter, digests_equal(test.digest, digest));
-}
-
-DEF_TEST(SHA1, reporter) {
-    for (size_t i = 0; i < SK_ARRAY_COUNT(sha1_tests); ++i) {
-        sha1_test(sha1_tests[i], reporter);
-    }
-}
diff --git a/src/third_party/skia/tests/SListTest.cpp b/src/third_party/skia/tests/SListTest.cpp
deleted file mode 100644
index 78fcc65..0000000
--- a/src/third_party/skia/tests/SListTest.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkTInternalSList.h"
-#include "Test.h"
-
-class SListEntry {
-public:
-    SListEntry* next() { return getSListNext(); }
-private:
-    SK_DECLARE_INTERNAL_SLIST_INTERFACE(SListEntry);
-};
-
-static bool verifyEmptyList(skiatest::Reporter* reporter,
-                            const SkTInternalSList<SListEntry>& list,
-                            const char* stage) {
-
-    if (!list.isEmpty()) {
-        ERRORF(reporter, "%s - List not empty", stage);
-        return false;
-    }
-    if (0 != list.getCount()) {
-        ERRORF(reporter, "%s - List count is not zero, %d instead", stage, list.getCount());
-        return false;
-    }
-    if (list.head()) {
-        ERRORF(reporter, "%s - List has elements when empty", stage);
-        return false;
-    }
-    return true;
-}
-
-static bool verifyList(skiatest::Reporter* reporter,
-                       const SkTInternalSList<SListEntry>& list,
-                       const char* stage,
-                       SListEntry* start, int count, int step = 1) {
-    SListEntry* next = list.head();
-    if (list.getCount() != count) {
-        ERRORF(reporter, "%s - List was too short, %d instead of %d", stage, list.getCount(), count);
-        return false;
-    }
-    int index = 0;
-    for(SListEntry* value = start; index < count; value += step, ++index) {
-        if (NULL == next) {
-            ERRORF(reporter, "%s - List too short, should be %d", stage, count);
-            return false;
-        }
-        if (next!= value) {
-            ERRORF(reporter, "%s - List entries at index %d of %d don't match", stage, index, count);
-            return false;
-        }
-        next = next->next();
-    }
-    if (next) {
-        ERRORF(reporter, "%s - List too long, should be %d", stage, count);
-        return false;
-    }
-    return true;
-}
-
-static void testTInternalSList(skiatest::Reporter* reporter) {
-    // Build a test array of data
-    static const int testArraySize = 10;
-    SListEntry testArray[testArraySize];
-    // Basic add remove tests
-    SkTInternalSList<SListEntry> list;
-    verifyEmptyList(reporter, list, "start");
-    // Push values in, testing on the way
-    for (int index = 0; index < testArraySize; ++index) {
-        list.push(&testArray[index]);
-        if (!verifyList(reporter, list, "push", &testArray[index], index+1, -1)) {
-            return;
-        }
-    }
-    // Now remove them again
-    for (int index = testArraySize - 1; index >= 0; --index) {
-        REPORTER_ASSERT(reporter, &testArray[index] == list.pop());
-        if ((index != 0) &&
-            !verifyList(reporter, list, "pop", &testArray[index-1], index, -1)) {
-            return;
-        }
-    }
-    verifyEmptyList(reporter, list, "end");
-    // Move between list tests
-    for (int index = 0; index < testArraySize; ++index) {
-        list.push(&testArray[index]);
-    }
-    verifyList(reporter, list, "swap", &testArray[testArraySize-1], testArraySize, -1);
-    SkTInternalSList<SListEntry> other;
-    // Check swap moves the list over unchanged
-    other.swap(&list);
-    verifyEmptyList(reporter, list, "swap");
-    verifyList(reporter, other, "swap", &testArray[testArraySize-1], testArraySize, -1);
-    // Check pushAll optimizes to a swap when one of the is empty
-    list.pushAll(&other);
-    verifyList(reporter, list, "pushAll-empty", &testArray[testArraySize-1], testArraySize, -1);
-    verifyEmptyList(reporter, other, "pushAll-empty");
-    // Check pushAll when non empty works
-    other.push(list.pop());
-    other.pushAll(&list);
-    verifyEmptyList(reporter, list, "pushAll");
-    verifyList(reporter, other, "pushAll", &testArray[0], testArraySize, 1);
-}
-
-DEF_TEST(SList, reporter) {
-    testTInternalSList(reporter);
-}
diff --git a/src/third_party/skia/tests/SRGBMipMapTest.cpp b/src/third_party/skia/tests/SRGBMipMapTest.cpp
new file mode 100644
index 0000000..8d035e1
--- /dev/null
+++ b/src/third_party/skia/tests/SRGBMipMapTest.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#if SK_SUPPORT_GPU
+#include "GrCaps.h"
+#include "GrClip.h"
+#include "GrContext.h"
+#include "GrRenderTargetContext.h"
+#include "SkCanvas.h"
+#include "SkGr.h"
+#include "SkSurface.h"
+#include "gl/GrGLGpu.h"
+
+// using anonymous namespace because these functions are used as template params.
+namespace {
+/** convert 0..1 srgb value to 0..1 linear */
+float srgb_to_linear(float srgb) {
+    if (srgb <= 0.04045f) {
+        return srgb / 12.92f;
+    } else {
+        return powf((srgb + 0.055f) / 1.055f, 2.4f);
+    }
+}
+
+/** convert 0..1 linear value to 0..1 srgb */
+float linear_to_srgb(float linear) {
+    if (linear <= 0.0031308) {
+        return linear * 12.92f;
+    } else {
+        return 1.055f * powf(linear, 1.f / 2.4f) - 0.055f;
+    }
+}
+}
+
+static bool check_value(U8CPU value, U8CPU expected, U8CPU error) {
+    if (value >= expected) {
+        return (value - expected) <= error;
+    } else {
+        return (expected - value) <= error;
+    }
+}
+
+void read_and_check_pixels(skiatest::Reporter* reporter, GrSurfaceContext* context,
+                           U8CPU expected, const SkImageInfo& dstInfo,
+                           U8CPU error, const char* subtestName) {
+    int w = dstInfo.width();
+    int h = dstInfo.height();
+    SkAutoTMalloc<uint32_t> readData(w * h);
+    memset(readData.get(), 0, sizeof(uint32_t) * w * h);
+
+    if (!context->readPixels(dstInfo, readData.get(), 0, 0, 0)) {
+        ERRORF(reporter, "Could not read pixels for %s.", subtestName);
+        return;
+    }
+
+    for (int j = 0; j < h; ++j) {
+        for (int i = 0; i < w; ++i) {
+            uint32_t read = readData[j * w + i];
+
+            bool success =
+                check_value(read & 0xff, expected, error) &&
+                check_value((read >> 8) & 0xff, expected, error) &&
+                check_value((read >> 16) & 0xff, expected, error);
+
+            if (!success) {
+                ERRORF(reporter, "Expected 0xff%02x%02x%02x, read back as 0x%08x in %s at %d, %d.",
+                       expected, expected, expected, read, subtestName, i, j);
+                return;
+            }
+        }
+    }
+}
+
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SRGBMipMaps, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    if (!context->caps()->srgbSupport()) {
+        return;
+    }
+
+    const int rtS = 16;
+    const int texS = rtS * 2;
+
+    // Fill texture with a dither of black and 60% sRGB (~ 32.5% linear) gray. Although there is
+    // only one likely failure mode (doing a direct downsample of the sRGB values), this pattern
+    // maximizes the minimum error across all three conceivable failure modes:
+    // 1) Likely incorrect:
+    //      (A + B) / 2
+    // 2) No input decode, decode output:
+    //      linear_to_srgb((A + B) / 2)
+    // 3) Decode input, no output encode:
+    //      (srgb_to_linear(A) + srgb_to_linear(B)) / 2
+
+    const U8CPU srgb60 = sk_float_round2int(0.6f * 255.0f);
+    static const SkPMColor colors[2] = {
+        SkPackARGB32(0xFF, srgb60, srgb60, srgb60),
+        SkPackARGB32(0xFF, 0x00, 0x00, 0x00)
+    };
+    uint32_t texData[texS * texS];
+    for (int y = 0; y < texS; ++y) {
+        for (int x = 0; x < texS; ++x) {
+            texData[y * texS + x] = colors[(x + y) % 2];
+        }
+    }
+
+    // We can be pretty generous with the error detection, thanks to the choice of input.
+    // The closest likely failure mode is off by > 0.1, so anything that encodes within
+    // 10/255 of optimal is more than good enough for this test.
+    const U8CPU expectedSRGB = sk_float_round2int(
+        linear_to_srgb(srgb_to_linear(srgb60 / 255.0f) / 2.0f) * 255.0f);
+    const U8CPU expectedLinear = srgb60 / 2;
+    const U8CPU error = 10;
+
+    const SkImageInfo iiSRGBA = SkImageInfo::Make(rtS, rtS, kRGBA_8888_SkColorType,
+                                                  kPremul_SkAlphaType,
+                                                  SkColorSpace::MakeSRGB());
+    const SkImageInfo iiRGBA = SkImageInfo::Make(rtS, rtS, kRGBA_8888_SkColorType,
+                                                 kPremul_SkAlphaType);
+
+    // Create our test texture
+    GrSurfaceDesc desc;
+    desc.fFlags = kNone_GrSurfaceFlags;
+    desc.fConfig = kSRGBA_8888_GrPixelConfig;
+    desc.fWidth = texS;
+    desc.fHeight = texS;
+
+    GrResourceProvider* resourceProvider = context->resourceProvider();
+    sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(resourceProvider,
+                                                               desc, SkBudgeted::kNo,
+                                                               texData, 0);
+
+    // Create two render target contexts (L32 and S32)
+    sk_sp<SkColorSpace> srgbColorSpace = SkColorSpace::MakeSRGB();
+    sk_sp<GrRenderTargetContext> l32RenderTargetContext = context->makeDeferredRenderTargetContext(
+        SkBackingFit::kExact, rtS, rtS, kRGBA_8888_GrPixelConfig, nullptr);
+    sk_sp<GrRenderTargetContext> s32RenderTargetContext = context->makeDeferredRenderTargetContext(
+        SkBackingFit::kExact, rtS, rtS, kSRGBA_8888_GrPixelConfig, std::move(srgbColorSpace));
+
+    SkRect rect = SkRect::MakeWH(SkIntToScalar(rtS), SkIntToScalar(rtS));
+    GrNoClip noClip;
+    GrPaint paint;
+    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
+    GrSamplerParams mipMapParams(SkShader::kRepeat_TileMode, GrSamplerParams::kMipMap_FilterMode);
+    paint.addColorTextureProcessor(std::move(proxy),
+                                   nullptr, SkMatrix::MakeScale(rtS), mipMapParams);
+
+    // 1) Draw texture to S32 surface (should generate/use sRGB mips)
+    paint.setGammaCorrect(true);
+    s32RenderTargetContext->drawRect(noClip, GrPaint(paint), GrAA::kNo, SkMatrix::I(), rect);
+    read_and_check_pixels(reporter, s32RenderTargetContext.get(), expectedSRGB, iiSRGBA, error,
+                          "first render of sRGB");
+
+    // 2) Draw texture to L32 surface (should generate/use linear mips)
+    paint.setGammaCorrect(false);
+    l32RenderTargetContext->drawRect(noClip, GrPaint(paint), GrAA::kNo, SkMatrix::I(), rect);
+
+    // Right now, this test only runs on GL (because Vulkan doesn't support legacy mip-mapping
+    // skbug.com/5048). On GL, we may not have sRGB decode support. In that case, rendering sRGB
+    // textures to a legacy surface produces nonsense, so this part of the test is meaningless.
+    //
+    // We also skip this part of the test on command buffer (via srgbDecodeDisableAffectsMipmaps),
+    // because that implementation of the extension doesn't ensure that mips respect the setting.
+    //
+    // TODO: Once Vulkan supports legacy mip-mapping, we can promote this to GrCaps. Right now,
+    // Vulkan has most of the functionality, but not the mip-mapping part that's being tested here.
+    GrGLGpu* glGpu = static_cast<GrGLGpu*>(context->getGpu());
+    if (glGpu->glCaps().srgbDecodeDisableSupport() &&
+        glGpu->glCaps().srgbDecodeDisableAffectsMipmaps()) {
+        read_and_check_pixels(reporter, l32RenderTargetContext.get(), expectedLinear, iiRGBA,
+                              error, "re-render as linear");
+    }
+
+    // 3) Go back to sRGB
+    paint.setGammaCorrect(true);
+    s32RenderTargetContext->drawRect(noClip, std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
+    read_and_check_pixels(reporter, s32RenderTargetContext.get(), expectedSRGB, iiSRGBA, error,
+                          "re-render as sRGB");
+}
+#endif
diff --git a/src/third_party/skia/tests/SRGBReadWritePixelsTest.cpp b/src/third_party/skia/tests/SRGBReadWritePixelsTest.cpp
new file mode 100644
index 0000000..a52e961
--- /dev/null
+++ b/src/third_party/skia/tests/SRGBReadWritePixelsTest.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#if SK_SUPPORT_GPU
+#include "GrCaps.h"
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrSurfaceContext.h"
+#include "SkCanvas.h"
+#include "SkGr.h"
+#include "SkSurface.h"
+
+// using anonymous namespace because these functions are used as template params.
+namespace {
+/** convert 0..1 srgb value to 0..1 linear */
+float srgb_to_linear(float srgb) {
+    if (srgb <= 0.04045f) {
+        return srgb / 12.92f;
+    } else {
+        return powf((srgb + 0.055f) / 1.055f, 2.4f);
+    }
+}
+
+/** convert 0..1 linear value to 0..1 srgb */
+float linear_to_srgb(float linear) {
+    if (linear <= 0.0031308) {
+        return linear * 12.92f;
+    } else {
+        return 1.055f * powf(linear, 1.f / 2.4f) - 0.055f;
+    }
+}
+}
+
+/** tests a conversion with an error tolerance */
+template <float (*CONVERT)(float)> static bool check_conversion(uint32_t input, uint32_t output,
+                                                                float error) {
+    // alpha should always be exactly preserved.
+    if ((input & 0xff000000) != (output & 0xff000000)) {
+        return false;
+    }
+
+    for (int c = 0; c < 3; ++c) {
+        uint8_t inputComponent = (uint8_t) ((input & (0xff << (c*8))) >> (c*8));
+        float lower = SkTMax(0.f, (float) inputComponent - error);
+        float upper = SkTMin(255.f, (float) inputComponent + error);
+        lower = CONVERT(lower / 255.f);
+        upper = CONVERT(upper / 255.f);
+        SkASSERT(lower >= 0.f && lower <= 255.f);
+        SkASSERT(upper >= 0.f && upper <= 255.f);
+        uint8_t outputComponent = (output & (0xff << (c*8))) >> (c*8);
+        if (outputComponent < SkScalarFloorToInt(lower * 255.f) ||
+            outputComponent > SkScalarCeilToInt(upper * 255.f)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+/** tests a forward and backward conversion with an error tolerance */
+template <float (*FORWARD)(float), float (*BACKWARD)(float)>
+static bool check_double_conversion(uint32_t input, uint32_t output, float error) {
+    // alpha should always be exactly preserved.
+    if ((input & 0xff000000) != (output & 0xff000000)) {
+        return false;
+    }
+
+    for (int c = 0; c < 3; ++c) {
+        uint8_t inputComponent = (uint8_t) ((input & (0xff << (c*8))) >> (c*8));
+        float lower = SkTMax(0.f, (float) inputComponent - error);
+        float upper = SkTMin(255.f, (float) inputComponent + error);
+        lower = FORWARD(lower / 255.f);
+        upper = FORWARD(upper / 255.f);
+        SkASSERT(lower >= 0.f && lower <= 255.f);
+        SkASSERT(upper >= 0.f && upper <= 255.f);
+        uint8_t upperComponent = SkScalarCeilToInt(upper * 255.f);
+        uint8_t lowerComponent = SkScalarFloorToInt(lower * 255.f);
+        lower = SkTMax(0.f, (float) lowerComponent - error);
+        upper = SkTMin(255.f, (float) upperComponent + error);
+        lower = BACKWARD(lowerComponent / 255.f);
+        upper = BACKWARD(upperComponent / 255.f);
+        SkASSERT(lower >= 0.f && lower <= 255.f);
+        SkASSERT(upper >= 0.f && upper <= 255.f);
+        upperComponent = SkScalarCeilToInt(upper * 255.f);
+        lowerComponent = SkScalarFloorToInt(lower * 255.f);
+
+        uint8_t outputComponent = (output & (0xff << (c*8))) >> (c*8);
+        if (outputComponent < lowerComponent || outputComponent > upperComponent) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static bool check_srgb_to_linear_conversion(uint32_t srgb, uint32_t linear, float error) {
+    return check_conversion<srgb_to_linear>(srgb, linear, error);
+}
+
+static bool check_linear_to_srgb_conversion(uint32_t linear, uint32_t srgb, float error) {
+    return check_conversion<linear_to_srgb>(linear, srgb, error);
+}
+
+static bool check_linear_to_srgb_to_linear_conversion(uint32_t input, uint32_t output, float error) {
+    return check_double_conversion<linear_to_srgb, srgb_to_linear>(input, output, error);
+}
+
+static bool check_srgb_to_linear_to_srgb_conversion(uint32_t input, uint32_t output, float error) {
+    return check_double_conversion<srgb_to_linear, linear_to_srgb>(input, output, error);
+}
+
+typedef bool (*CheckFn) (uint32_t orig, uint32_t actual, float error);
+
+void read_and_check_pixels(skiatest::Reporter* reporter, GrSurfaceContext* context,
+                           uint32_t* origData,
+                           const SkImageInfo& dstInfo, CheckFn checker, float error,
+                           const char* subtestName) {
+    int w = dstInfo.width();
+    int h = dstInfo.height();
+    SkAutoTMalloc<uint32_t> readData(w * h);
+    memset(readData.get(), 0, sizeof(uint32_t) * w * h);
+
+    if (!context->readPixels(dstInfo, readData.get(), 0, 0, 0)) {
+        ERRORF(reporter, "Could not read pixels for %s.", subtestName);
+        return;
+    }
+
+    for (int j = 0; j < h; ++j) {
+        for (int i = 0; i < w; ++i) {
+            uint32_t orig = origData[j * w + i];
+            uint32_t read = readData[j * w + i];
+
+            if (!checker(orig, read, error)) {
+                ERRORF(reporter, "Expected 0x%08x, read back as 0x%08x in %s at %d, %d).",
+                       orig, read, subtestName, i, j);
+                return;
+            }
+        }
+    }
+}
+
+// TODO: Add tests for copySurface between srgb/linear textures. Add tests for unpremul/premul
+// conversion during read/write along with srgb/linear conversions.
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SRGBReadWritePixels, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+#if defined(GOOGLE3)
+    // Stack frame size is limited in GOOGLE3.
+    static const int kW = 63;
+    static const int kH = 63;
+#else
+    static const int kW = 255;
+    static const int kH = 255;
+#endif
+    uint32_t origData[kW * kH];
+    for (int j = 0; j < kH; ++j) {
+        for (int i = 0; i < kW; ++i) {
+            origData[j * kW + i] = (j << 24) | (i << 16) | (i << 8) | i;
+        }
+    }
+
+    const SkImageInfo iiSRGBA = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType,
+                                                  kPremul_SkAlphaType,
+                                                  SkColorSpace::MakeSRGB());
+    const SkImageInfo iiRGBA = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType,
+                                                 kPremul_SkAlphaType);
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+    desc.fWidth = kW;
+    desc.fHeight = kH;
+    desc.fConfig = kSRGBA_8888_GrPixelConfig;
+    if (context->caps()->isConfigRenderable(desc.fConfig, false) &&
+        context->caps()->isConfigTexturable(desc.fConfig)) {
+
+        sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeDeferredSurfaceContext(
+                                                                    desc, SkBackingFit::kExact,
+                                                                    SkBudgeted::kNo);
+        if (!sContext) {
+            ERRORF(reporter, "Could not create SRGBA surface context.");
+            return;
+        }
+
+        float error = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.2f  : 0.5f;
+
+        // Write srgba data and read as srgba and then as rgba
+        if (sContext->writePixels(iiSRGBA, origData, 0, 0, 0)) {
+            // For the all-srgba case, we allow a small error only for devices that have
+            // precision variation because the srgba data gets converted to linear and back in
+            // the shader.
+            float smallError = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.f : 0.0f;
+            read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA,
+                                  check_srgb_to_linear_to_srgb_conversion, smallError,
+                                  "write/read srgba to srgba texture");
+            read_and_check_pixels(reporter, sContext.get(), origData, iiRGBA,
+                                  check_srgb_to_linear_conversion, error,
+                                  "write srgba/read rgba with srgba texture");
+        } else {
+            ERRORF(reporter, "Could not write srgba data to srgba texture.");
+        }
+
+        // Now verify that we can write linear data
+        if (sContext->writePixels(iiRGBA, origData, 0, 0, 0)) {
+            // We allow more error on GPUs with lower precision shader variables.
+            read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA,
+                                  check_linear_to_srgb_conversion, error,
+                                  "write rgba/read srgba with srgba texture");
+            read_and_check_pixels(reporter, sContext.get(), origData, iiRGBA,
+                                  check_linear_to_srgb_to_linear_conversion, error,
+                                  "write/read rgba with srgba texture");
+        } else {
+            ERRORF(reporter, "Could not write rgba data to srgba texture.");
+        }
+
+        desc.fConfig = kRGBA_8888_GrPixelConfig;
+        sContext = context->contextPriv().makeDeferredSurfaceContext(desc, SkBackingFit::kExact,
+                                                                     SkBudgeted::kNo);
+        if (!sContext) {
+            ERRORF(reporter, "Could not create RGBA surface context.");
+            return;
+        }
+
+        // Write srgba data to a rgba texture and read back as srgba and rgba
+        if (sContext->writePixels(iiSRGBA, origData, 0, 0, 0)) {
+#if 0
+            // We don't support this conversion (read from untagged source into tagged destination.
+            // If we decide there is a meaningful way to implement this, restore this test.
+            read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA,
+                                  check_srgb_to_linear_to_srgb_conversion, error,
+                                  "write/read srgba to rgba texture");
+#endif
+            // We expect the sRGB -> linear write to do no sRGB conversion (to match the behavior of
+            // drawing tagged sources). skbug.com/6547. So the data we read should still contain
+            // sRGB encoded values.
+            //
+            // srgb_to_linear_to_srgb is a proxy for the expected identity transform.
+            read_and_check_pixels(reporter, sContext.get(), origData, iiRGBA,
+                                  check_srgb_to_linear_to_srgb_conversion, error,
+                                  "write srgba/read rgba to rgba texture");
+        } else {
+            ERRORF(reporter, "Could not write srgba data to rgba texture.");
+        }
+
+        // Write rgba data to a rgba texture and read back as srgba
+        if (sContext->writePixels(iiRGBA, origData, 0, 0, 0)) {
+            read_and_check_pixels(reporter, sContext.get(), origData, iiSRGBA,
+                                  check_linear_to_srgb_conversion, 1.2f,
+                                  "write rgba/read srgba to rgba texture");
+        } else {
+            ERRORF(reporter, "Could not write rgba data to rgba texture.");
+        }
+    }
+}
+#endif
diff --git a/src/third_party/skia/tests/SRGBTest.cpp b/src/third_party/skia/tests/SRGBTest.cpp
new file mode 100644
index 0000000..fc02f7d
--- /dev/null
+++ b/src/third_party/skia/tests/SRGBTest.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPM4f.h"
+#include "SkRasterPipeline.h"
+#include "SkSRGB.h"
+#include "SkTypes.h"
+#include "Test.h"
+#include <math.h>
+
+static uint8_t linear_to_srgb(float l) {
+    return (uint8_t)sk_linear_to_srgb(Sk4f{l})[0];
+}
+
+DEF_TEST(sk_linear_to_srgb, r) {
+    // All bytes should round trip.
+    for (int i = 0; i < 256; i++) {
+        int actual = linear_to_srgb(sk_linear_from_srgb[i]);
+        if (i != actual) {
+            ERRORF(r, "%d -> %d\n", i, actual);
+        }
+    }
+
+    // Should be monotonic between 0 and 1.
+    uint8_t prev = 0;
+    for (float f = FLT_MIN; f <= 1.0f; ) {  // We don't bother checking denorm values.
+        uint8_t srgb = linear_to_srgb(f);
+
+        REPORTER_ASSERT(r, srgb >= prev);
+        prev = srgb;
+
+        union { float flt; uint32_t bits; } pun = { f };
+        pun.bits++;
+        SkDEBUGCODE(pun.bits += 127);
+        f = pun.flt;
+    }
+}
+
+DEF_TEST(sk_pipeline_srgb_roundtrip, r) {
+    uint32_t reds[256];
+    for (int i = 0; i < 256; i++) {
+        reds[i] = i;
+    }
+
+    auto ptr = (void*)reds;
+
+    SkRasterPipeline_<256> p;
+    p.append(SkRasterPipeline::load_8888,  &ptr);
+    p.append_from_srgb(kUnpremul_SkAlphaType);
+    p.append(SkRasterPipeline::to_srgb);
+    p.append(SkRasterPipeline::store_8888, &ptr);
+
+    p.run(0,0,256);
+
+    for (int i = 0; i < 256; i++) {
+        if (reds[i] != (uint32_t)i) {
+            ERRORF(r, "%d doesn't round trip, %d", i, reds[i]);
+        }
+    }
+}
+
+DEF_TEST(sk_pipeline_srgb_edge_cases, r) {
+    // We need to run at least 4 pixels to make sure we hit all specializations.
+    SkPM4f colors[4] = { {{0,1,1,1}}, {{0,0,0,0}}, {{0,0,0,0}}, {{0,0,0,0}} };
+    auto& color = colors[0];
+    void* dst = &color;
+
+    SkRasterPipeline_<256> p;
+    p.append(SkRasterPipeline::uniform_color, &color);
+    p.append(SkRasterPipeline::to_srgb);
+    p.append(SkRasterPipeline::store_f32, &dst);
+    p.run(0,0,4);
+
+    if (color.r() != 0.0f) {
+        ERRORF(r, "expected to_srgb() to map 0.0f to 0.0f, got %f", color.r());
+    }
+    if (color.g() != 1.0f) {
+        float f = color.g();
+        uint32_t x;
+        memcpy(&x, &f, 4);
+        ERRORF(r, "expected to_srgb() to map 1.0f to 1.0f, got %f (%08x)", color.g(), x);
+    }
+}
diff --git a/src/third_party/skia/tests/SVGDeviceTest.cpp b/src/third_party/skia/tests/SVGDeviceTest.cpp
new file mode 100644
index 0000000..715dbc7
--- /dev/null
+++ b/src/third_party/skia/tests/SVGDeviceTest.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCanvas.h"
+#include "SkData.h"
+#include "SkDOM.h"
+#include "SkParse.h"
+#include "SkStream.h"
+#include "SkSVGCanvas.h"
+#include "SkXMLWriter.h"
+#include "Test.h"
+
+#include <string.h>
+
+namespace {
+
+void check_text_node(skiatest::Reporter* reporter,
+                     const SkDOM& dom,
+                     const SkDOM::Node* root,
+                     const SkPoint& offset,
+                     unsigned scalarsPerPos,
+                     const char* expected) {
+    if (root == nullptr) {
+        ERRORF(reporter, "root element not found.");
+        return;
+    }
+
+    const SkDOM::Node* textElem = dom.getFirstChild(root, "text");
+    if (textElem == nullptr) {
+        ERRORF(reporter, "<text> element not found.");
+        return;
+    }
+    REPORTER_ASSERT(reporter, dom.getType(textElem) == SkDOM::kElement_Type);
+
+    const SkDOM::Node* textNode= dom.getFirstChild(textElem);
+    REPORTER_ASSERT(reporter, textNode != nullptr);
+    if (textNode != nullptr) {
+        REPORTER_ASSERT(reporter, dom.getType(textNode) == SkDOM::kText_Type);
+        REPORTER_ASSERT(reporter, strcmp(expected, dom.getName(textNode)) == 0);
+    }
+
+    int textLen = SkToInt(strlen(expected));
+
+    const char* x = dom.findAttr(textElem, "x");
+    REPORTER_ASSERT(reporter, x != nullptr);
+    if (x != nullptr) {
+        int xposCount = (scalarsPerPos < 1) ? 1 : textLen;
+        REPORTER_ASSERT(reporter, SkParse::Count(x) == xposCount);
+
+        SkAutoTMalloc<SkScalar> xpos(xposCount);
+        SkParse::FindScalars(x, xpos.get(), xposCount);
+        if (scalarsPerPos < 1) {
+            REPORTER_ASSERT(reporter, xpos[0] == offset.x());
+        } else {
+            for (int i = 0; i < xposCount; ++i) {
+                REPORTER_ASSERT(reporter, xpos[i] == SkIntToScalar(expected[i]));
+            }
+        }
+    }
+
+    const char* y = dom.findAttr(textElem, "y");
+    REPORTER_ASSERT(reporter, y != nullptr);
+    if (y != nullptr) {
+        int yposCount = (scalarsPerPos < 2) ? 1 : textLen;
+        REPORTER_ASSERT(reporter, SkParse::Count(y) == yposCount);
+
+        SkAutoTMalloc<SkScalar> ypos(yposCount);
+        SkParse::FindScalars(y, ypos.get(), yposCount);
+        if (scalarsPerPos < 2) {
+            REPORTER_ASSERT(reporter, ypos[0] == offset.y());
+        } else {
+            for (int i = 0; i < yposCount; ++i) {
+                REPORTER_ASSERT(reporter, ypos[i] == -SkIntToScalar(expected[i]));
+            }
+        }
+    }
+}
+
+void test_whitespace_pos(skiatest::Reporter* reporter,
+                         const char* txt,
+                         const char* expected) {
+    size_t len = strlen(txt);
+
+    SkDOM dom;
+    SkPaint paint;
+    SkPoint offset = SkPoint::Make(10, 20);
+
+    {
+        SkXMLParserWriter writer(dom.beginParsing());
+        std::unique_ptr<SkCanvas> svgCanvas = SkSVGCanvas::Make(SkRect::MakeWH(100, 100), &writer);
+        svgCanvas->drawText(txt, len, offset.x(), offset.y(), paint);
+    }
+    check_text_node(reporter, dom, dom.finishParsing(), offset, 0, expected);
+
+    {
+        SkAutoTMalloc<SkScalar> xpos(len);
+        for (int i = 0; i < SkToInt(len); ++i) {
+            xpos[i] = SkIntToScalar(txt[i]);
+        }
+
+        SkXMLParserWriter writer(dom.beginParsing());
+        std::unique_ptr<SkCanvas> svgCanvas = SkSVGCanvas::Make(SkRect::MakeWH(100, 100), &writer);
+        svgCanvas->drawPosTextH(txt, len, xpos, offset.y(), paint);
+    }
+    check_text_node(reporter, dom, dom.finishParsing(), offset, 1, expected);
+
+    {
+        SkAutoTMalloc<SkPoint> pos(len);
+        for (int i = 0; i < SkToInt(len); ++i) {
+            pos[i] = SkPoint::Make(SkIntToScalar(txt[i]), -SkIntToScalar(txt[i]));
+        }
+
+        SkXMLParserWriter writer(dom.beginParsing());
+        std::unique_ptr<SkCanvas> svgCanvas = SkSVGCanvas::Make(SkRect::MakeWH(100, 100), &writer);
+        svgCanvas->drawPosText(txt, len, pos, paint);
+    }
+    check_text_node(reporter, dom, dom.finishParsing(), offset, 2, expected);
+}
+
+}
+
+DEF_TEST(SVGDevice_whitespace_pos, reporter) {
+    static const struct {
+        const char* tst_in;
+        const char* tst_out;
+    } tests[] = {
+        { "abcd"      , "abcd" },
+        { "ab cd"     , "ab cd" },
+        { "ab \t\t cd", "ab cd" },
+        { " abcd"     , "abcd" },
+        { "  abcd"    , "abcd" },
+        { " \t\t abcd", "abcd" },
+        { "abcd "     , "abcd " }, // we allow one trailing whitespace char
+        { "abcd  "    , "abcd " }, // because it makes no difference and
+        { "abcd\t  "  , "abcd\t" }, // simplifies the implementation
+        { "\t\t  \t ab \t\t  \t cd \t\t   \t  ", "ab cd " },
+    };
+
+    for (unsigned i = 0; i < SK_ARRAY_COUNT(tests); ++i) {
+        test_whitespace_pos(reporter, tests[i].tst_in, tests[i].tst_out);
+    }
+}
diff --git a/src/third_party/skia/tests/ScalarTest.cpp b/src/third_party/skia/tests/ScalarTest.cpp
index b6f71d1..f2f924e 100644
--- a/src/third_party/skia/tests/ScalarTest.cpp
+++ b/src/third_party/skia/tests/ScalarTest.cpp
@@ -54,7 +54,7 @@
 
     static const struct {
         const SkPoint* fPts;
-        size_t         fCount;
+        int            fCount;
         bool           fIsFinite;
     } gSets[] = {
         { gF0, SK_ARRAY_COUNT(gF0), true },
diff --git a/src/third_party/skia/tests/ScaleToSidesTest.cpp b/src/third_party/skia/tests/ScaleToSidesTest.cpp
new file mode 100644
index 0000000..430bb8c
--- /dev/null
+++ b/src/third_party/skia/tests/ScaleToSidesTest.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkScaleToSides.h"
+
+#include <algorithm>
+#include "Test.h"
+
+DEF_TEST(ScaleToSides, reporter) {
+    double interestingValues[] = {
+        // From sample app - PathFuzzer
+        260.01662826538085938,
+        63.61007690429687500,
+        795.98901367187500000,
+        217.71697616577148438,
+        686.15960693359375000,
+        556.57641601562500000,
+        // From skp bitbucket
+        111.60000228881836,
+        55.800003051757813,
+        0.99999996581812677920,
+        0.0,
+        0.5,
+        1.0,
+        2.0,
+        3.0,
+        33.0,
+        33554430.0,
+        33554431.0,
+        33554464.0,
+        333333332.0,
+        333333333.0,
+        333333334.0,
+        FLT_MAX,
+        FLT_EPSILON,
+        FLT_MIN
+    };
+
+    int numInterestingValues = (int)SK_ARRAY_COUNT(interestingValues);
+
+    for (int s = 0; s <= numInterestingValues; s++) {
+        for (int i = 0; i < numInterestingValues; i++) {
+            for (int j = 0; j < numInterestingValues; j++) {
+                for (int k = 0; k < numInterestingValues; k++) {
+                    float radius1 = (float)interestingValues[i];
+                    float radius2 = (float)interestingValues[j];
+                    double width = interestingValues[k];
+                    double scale = width / ((double)radius1 + (double)radius2);
+                    if (width > 0.0) {
+                        if (s != 0) {
+                            scale = std::min(scale, interestingValues[s-1]);
+                        }
+                        if (scale < 1.0 && scale > 0.0) {
+                            SkScaleToSides::AdjustRadii(width, scale, &radius1, &radius2);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/third_party/skia/tests/SerializationTest.cpp b/src/third_party/skia/tests/SerializationTest.cpp
index dd9b938..3c24c55 100644
--- a/src/third_party/skia/tests/SerializationTest.cpp
+++ b/src/third_party/skia/tests/SerializationTest.cpp
@@ -6,17 +6,26 @@
  */
 
 #include "Resources.h"
-#include "SkBitmapSource.h"
+#include "SkAnnotationKeys.h"
 #include "SkCanvas.h"
+#include "SkFixed.h"
+#include "SkFontDescriptor.h"
+#include "SkImage.h"
+#include "SkImageSource.h"
+#include "SkLightingShader.h"
+#include "SkMakeUnique.h"
 #include "SkMallocPixelRef.h"
+#include "SkNormalSource.h"
 #include "SkOSFile.h"
 #include "SkPictureRecorder.h"
+#include "SkShaderBase.h"
 #include "SkTableColorFilter.h"
 #include "SkTemplates.h"
 #include "SkTypeface.h"
 #include "SkWriteBuffer.h"
 #include "SkValidatingReadBuffer.h"
 #include "SkXfermodeImageFilter.h"
+#include "sk_tool_utils.h"
 #include "Test.h"
 
 static const uint32_t kArraySize = 64;
@@ -96,6 +105,15 @@
     }
 };
 
+template<> struct SerializationUtils<SkColor4f> {
+    static void Write(SkWriteBuffer& writer, SkColor4f* data, uint32_t arraySize) {
+        writer.writeColor4fArray(data, arraySize);
+    }
+    static bool Read(SkValidatingReadBuffer& reader, SkColor4f* data, uint32_t arraySize) {
+        return reader.readColor4fArray(data, arraySize);
+    }
+};
+
 template<> struct SerializationUtils<int32_t> {
     static void Write(SkWriteBuffer& writer, int32_t* data, uint32_t arraySize) {
         writer.writeIntArray(data, arraySize);
@@ -135,7 +153,7 @@
 
 template<typename T, bool testInvalid>
 static void TestObjectSerializationNoAlign(T* testObj, skiatest::Reporter* reporter) {
-    SkWriteBuffer writer(SkWriteBuffer::kValidation_Flag);
+    SkBinaryWriteBuffer writer;
     SerializationUtils<T>::Write(writer, testObj);
     size_t bytesWritten = writer.bytesWritten();
     REPORTER_ASSERT(reporter, SkAlign4(bytesWritten) == bytesWritten);
@@ -153,15 +171,15 @@
 
     // Make sure this succeeds when it should
     SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
-    const unsigned char* peekBefore = static_cast<const unsigned char*>(buffer2.skip(0));
+    size_t offsetBefore = buffer2.offset();
     T obj2;
     SerializationUtils<T>::Read(buffer2, &obj2);
-    const unsigned char* peekAfter = static_cast<const unsigned char*>(buffer2.skip(0));
+    size_t offsetAfter = buffer2.offset();
     // This should have succeeded, since there are enough bytes to read this
     REPORTER_ASSERT(reporter, buffer2.isValid() == !testInvalid);
     // Note: This following test should always succeed, regardless of whether the buffer is valid,
     // since if it is invalid, it will simply skip to the end, as if it had read the whole buffer.
-    REPORTER_ASSERT(reporter, static_cast<size_t>(peekAfter - peekBefore) == bytesWritten);
+    REPORTER_ASSERT(reporter, offsetAfter - offsetBefore == bytesWritten);
 }
 
 template<typename T>
@@ -173,26 +191,26 @@
 template<typename T>
 static T* TestFlattenableSerialization(T* testObj, bool shouldSucceed,
                                        skiatest::Reporter* reporter) {
-    SkWriteBuffer writer(SkWriteBuffer::kValidation_Flag);
+    SkBinaryWriteBuffer writer;
     SerializationUtils<T>::Write(writer, testObj);
     size_t bytesWritten = writer.bytesWritten();
     REPORTER_ASSERT(reporter, SkAlign4(bytesWritten) == bytesWritten);
 
+    SkASSERT(bytesWritten <= 4096);
     unsigned char dataWritten[4096];
-    SkASSERT(bytesWritten <= sizeof(dataWritten));
     writer.writeToMemory(dataWritten);
 
     // Make sure this fails when it should (test with smaller size, but still multiple of 4)
     SkValidatingReadBuffer buffer(dataWritten, bytesWritten - 4);
-    T* obj = NULL;
+    T* obj = nullptr;
     SerializationUtils<T>::Read(buffer, &obj);
     REPORTER_ASSERT(reporter, !buffer.isValid());
-    REPORTER_ASSERT(reporter, NULL == obj);
+    REPORTER_ASSERT(reporter, nullptr == obj);
 
     // Make sure this succeeds when it should
     SkValidatingReadBuffer buffer2(dataWritten, bytesWritten);
     const unsigned char* peekBefore = static_cast<const unsigned char*>(buffer2.skip(0));
-    T* obj2 = NULL;
+    T* obj2 = nullptr;
     SerializationUtils<T>::Read(buffer2, &obj2);
     const unsigned char* peekAfter = static_cast<const unsigned char*>(buffer2.skip(0));
     if (shouldSucceed) {
@@ -203,7 +221,7 @@
     } else {
         // If the deserialization was supposed to fail, make sure it did
         REPORTER_ASSERT(reporter, !buffer.isValid());
-        REPORTER_ASSERT(reporter, NULL == obj2);
+        REPORTER_ASSERT(reporter, nullptr == obj2);
     }
 
     return obj2; // Return object to perform further validity tests on it
@@ -211,13 +229,13 @@
 
 template<typename T>
 static void TestArraySerialization(T* data, skiatest::Reporter* reporter) {
-    SkWriteBuffer writer(SkWriteBuffer::kValidation_Flag);
+    SkBinaryWriteBuffer writer;
     SerializationUtils<T>::Write(writer, data, kArraySize);
     size_t bytesWritten = writer.bytesWritten();
     // This should write the length (in 4 bytes) and the array
     REPORTER_ASSERT(reporter, (4 + kArraySize * sizeof(T)) == bytesWritten);
 
-    unsigned char dataWritten[1024];
+    unsigned char dataWritten[2048];
     writer.writeToMemory(dataWritten);
 
     // Make sure this fails when it should
@@ -238,15 +256,18 @@
                                     const SkBitmap& invalidBitmap,
                                     bool shouldSucceed,
                                     skiatest::Reporter* reporter) {
-    SkAutoTUnref<SkBitmapSource> validBitmapSource(SkBitmapSource::Create(validBitmap));
-    SkAutoTUnref<SkBitmapSource> invalidBitmapSource(SkBitmapSource::Create(invalidBitmap));
-    SkAutoTUnref<SkXfermode> mode(SkXfermode::Create(SkXfermode::kSrcOver_Mode));
-    SkAutoTUnref<SkXfermodeImageFilter> xfermodeImageFilter(
-        SkXfermodeImageFilter::Create(mode, invalidBitmapSource, validBitmapSource));
+    sk_sp<SkImage> validImage(SkImage::MakeFromBitmap(validBitmap));
+    sk_sp<SkImageFilter> validBitmapSource(SkImageSource::Make(std::move(validImage)));
+    sk_sp<SkImage> invalidImage(SkImage::MakeFromBitmap(invalidBitmap));
+    sk_sp<SkImageFilter> invalidBitmapSource(SkImageSource::Make(std::move(invalidImage)));
+    sk_sp<SkImageFilter> xfermodeImageFilter(
+        SkXfermodeImageFilter::Make(SkBlendMode::kSrcOver,
+                                    std::move(invalidBitmapSource),
+                                    std::move(validBitmapSource), nullptr));
 
-    SkAutoTUnref<SkImageFilter> deserializedFilter(
+    sk_sp<SkImageFilter> deserializedFilter(
         TestFlattenableSerialization<SkImageFilter>(
-            xfermodeImageFilter, shouldSucceed, reporter));
+            xfermodeImageFilter.get(), shouldSucceed, reporter));
 
     // Try to render a small bitmap using the invalid deserialized filter
     // to make sure we don't crash while trying to render it
@@ -262,32 +283,19 @@
     }
 }
 
-static void TestXfermodeSerialization(skiatest::Reporter* reporter) {
-    for (size_t i = 0; i <= SkXfermode::kLastMode; ++i) {
-        if (i == SkXfermode::kSrcOver_Mode) {
-            // skip SrcOver, as it is allowed to return NULL from Create()
-            continue;
-        }
-        SkAutoTUnref<SkXfermode> mode(SkXfermode::Create(static_cast<SkXfermode::Mode>(i)));
-        REPORTER_ASSERT(reporter, mode.get());
-        SkAutoTUnref<SkXfermode> copy(
-            TestFlattenableSerialization<SkXfermode>(mode.get(), true, reporter));
-    }
-}
-
 static void TestColorFilterSerialization(skiatest::Reporter* reporter) {
     uint8_t table[256];
     for (int i = 0; i < 256; ++i) {
         table[i] = (i * 41) % 256;
     }
-    SkAutoTUnref<SkColorFilter> colorFilter(SkTableColorFilter::Create(table));
-    SkAutoTUnref<SkColorFilter> copy(
+    auto colorFilter(SkTableColorFilter::Make(table));
+    sk_sp<SkColorFilter> copy(
         TestFlattenableSerialization<SkColorFilter>(colorFilter.get(), true, reporter));
 }
 
 static SkBitmap draw_picture(SkPicture& picture) {
      SkBitmap bitmap;
-     bitmap.allocN32Pixels(SkScalarCeilToInt(picture.cullRect().width()), 
+     bitmap.allocN32Pixels(SkScalarCeilToInt(picture.cullRect().width()),
                            SkScalarCeilToInt(picture.cullRect().height()));
      SkCanvas canvas(bitmap);
      picture.playback(&canvas);
@@ -298,8 +306,6 @@
                             const SkBitmap& b1, const SkBitmap& b2) {
     REPORTER_ASSERT(reporter, b1.width() == b2.width());
     REPORTER_ASSERT(reporter, b1.height() == b2.height());
-    SkAutoLockPixels autoLockPixels1(b1);
-    SkAutoLockPixels autoLockPixels2(b2);
 
     if ((b1.width() != b2.width()) ||
         (b1.height() != b2.height())) {
@@ -315,43 +321,30 @@
     }
     REPORTER_ASSERT(reporter, 0 == pixelErrors);
 }
-
-static void TestPictureTypefaceSerialization(skiatest::Reporter* reporter) {
-    // Load typeface form file.
-    // This test cannot run if there is no resource path.
-    SkString resourcePath = GetResourcePath();
-    if (resourcePath.isEmpty()) {
-        SkDebugf("Could not run fontstream test because resourcePath not specified.");
-        return;
-    }
-    SkString filename = SkOSPath::Join(resourcePath.c_str(), "test.ttc");
-    SkTypeface* typeface = SkTypeface::CreateFromFile(filename.c_str(), 1);
-    if (!typeface) {
-        SkDebugf("Could not run fontstream test because test.ttc not found.");
-        return;
-    }
-
-    // Create a paint with the typeface we loaded.
+static void serialize_and_compare_typeface(sk_sp<SkTypeface> typeface, const char* text,
+                                           skiatest::Reporter* reporter)
+{
+    // Create a paint with the typeface.
     SkPaint paint;
     paint.setColor(SK_ColorGRAY);
     paint.setTextSize(SkIntToScalar(30));
-    SkSafeUnref(paint.setTypeface(typeface));
+    paint.setTypeface(std::move(typeface));
 
     // Paint some text.
     SkPictureRecorder recorder;
     SkIRect canvasRect = SkIRect::MakeWH(kBitmapSize, kBitmapSize);
-    SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(canvasRect.width()), 
-                                               SkIntToScalar(canvasRect.height()), 
-                                               NULL, 0);
+    SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(canvasRect.width()),
+                                               SkIntToScalar(canvasRect.height()),
+                                               nullptr, 0);
     canvas->drawColor(SK_ColorWHITE);
-    canvas->drawText("A!", 2, 24, 32, paint);
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
+    canvas->drawText(text, 2, 24, 32, paint);
+    sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
 
     // Serlialize picture and create its clone from stream.
     SkDynamicMemoryWStream stream;
     picture->serialize(&stream);
-    SkAutoTUnref<SkStream> inputStream(stream.detachAsStream());
-    SkAutoTUnref<SkPicture> loadedPicture(SkPicture::CreateFromStream(inputStream.get()));
+    std::unique_ptr<SkStream> inputStream(stream.detachAsStream());
+    sk_sp<SkPicture> loadedPicture(SkPicture::MakeFromStream(inputStream.get()));
 
     // Draw both original and clone picture and compare bitmaps -- they should be identical.
     SkBitmap origBitmap = draw_picture(*picture);
@@ -359,6 +352,36 @@
     compare_bitmaps(reporter, origBitmap, destBitmap);
 }
 
+static void TestPictureTypefaceSerialization(skiatest::Reporter* reporter) {
+    {
+        // Load typeface from file to test CreateFromFile with index.
+        SkString filename = GetResourcePath("/fonts/test.ttc");
+        sk_sp<SkTypeface> typeface(SkTypeface::MakeFromFile(filename.c_str(), 1));
+        if (!typeface) {
+            INFOF(reporter, "Could not run fontstream test because test.ttc not found.");
+        } else {
+            serialize_and_compare_typeface(std::move(typeface), "A!", reporter);
+        }
+    }
+
+    {
+        // Load typeface as stream to create with axis settings.
+        std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("/fonts/Distortable.ttf"));
+        if (!distortable) {
+            INFOF(reporter, "Could not run fontstream test because Distortable.ttf not found.");
+        } else {
+            SkFixed axis = SK_FixedSqrt2;
+            sk_sp<SkTypeface> typeface(SkTypeface::MakeFromFontData(
+                skstd::make_unique<SkFontData>(std::move(distortable), 0, &axis, 1)));
+            if (!typeface) {
+                INFOF(reporter, "Could not run fontstream test because Distortable.ttf not created.");
+            } else {
+                serialize_and_compare_typeface(std::move(typeface), "abc", reporter);
+            }
+        }
+    }
+}
+
 static void setup_bitmap_for_canvas(SkBitmap* bitmap) {
     bitmap->allocN32Pixels(kBitmapSize, kBitmapSize);
 }
@@ -394,21 +417,16 @@
 
     canvas->save();
     canvas->scale(0.5f, 0.5f);
-    canvas->drawBitmap(bitmap, 0, 0, NULL);
+    canvas->drawBitmap(bitmap, 0, 0, nullptr);
     canvas->restore();
 
-    const char beforeStr[] = "before circle";
-    const char afterStr[] = "after circle";
-
     paint.setAntiAlias(true);
 
     paint.setColor(SK_ColorRED);
-    canvas->drawData(beforeStr, sizeof(beforeStr));
     canvas->drawCircle(SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/3), paint);
-    canvas->drawData(afterStr, sizeof(afterStr));
     paint.setColor(SK_ColorBLACK);
     paint.setTextSize(SkIntToScalar(kBitmapSize/3));
-    canvas->drawText("Picture", 7, SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/4), paint);
+    canvas->drawString("Picture", SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/4), paint);
 }
 
 DEF_TEST(Serialization, reporter) {
@@ -430,11 +448,6 @@
         TestObjectSerialization(&region, reporter);
     }
 
-    // Test xfermode serialization
-    {
-        TestXfermodeSerialization(reporter);
-    }
-
     // Test color filter serialization
     {
         TestColorFilterSerialization(reporter);
@@ -471,6 +484,17 @@
         TestArraySerialization(data, reporter);
     }
 
+    // Test readColor4fArray
+    {
+        SkColor4f data[kArraySize] = {
+            SkColor4f::FromColor(SK_ColorBLACK),
+            SkColor4f::FromColor(SK_ColorWHITE),
+            SkColor4f::FromColor(SK_ColorRED),
+            { 1.f, 2.f, 4.f, 8.f }
+        };
+        TestArraySerialization(data, reporter);
+    }
+
     // Test readIntArray
     {
         int32_t data[kArraySize] = { 1, 2, 4, 8 };
@@ -510,11 +534,11 @@
         SkPictureRecorder recorder;
         draw_something(recorder.beginRecording(SkIntToScalar(kBitmapSize),
                                                SkIntToScalar(kBitmapSize),
-                                               NULL, 0));
-        SkAutoTUnref<SkPicture> pict(recorder.endRecording());
+                                               nullptr, 0));
+        sk_sp<SkPicture> pict(recorder.finishRecordingAsPicture());
 
         // Serialize picture
-        SkWriteBuffer writer(SkWriteBuffer::kValidation_Flag);
+        SkBinaryWriteBuffer writer;
         pict->flatten(writer);
         size_t size = writer.bytesWritten();
         SkAutoTMalloc<unsigned char> data(size);
@@ -522,10 +546,149 @@
 
         // Deserialize picture
         SkValidatingReadBuffer reader(static_cast<void*>(data.get()), size);
-        SkAutoTUnref<SkPicture> readPict(
-            SkPicture::CreateFromBuffer(reader));
+        sk_sp<SkPicture> readPict(SkPicture::MakeFromBuffer(reader));
+        REPORTER_ASSERT(reporter, reader.isValid());
         REPORTER_ASSERT(reporter, readPict.get());
     }
 
     TestPictureTypefaceSerialization(reporter);
+
+    // Test SkLightingShader/NormalMapSource serialization
+    {
+        const int kTexSize = 2;
+
+        SkLights::Builder builder;
+
+        builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
+                                                     SkVector3::Make(1.0f, 0.0f, 0.0f)));
+        builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
+
+        sk_sp<SkLights> fLights = builder.finish();
+
+        SkBitmap diffuse = sk_tool_utils::create_checkerboard_bitmap(
+                kTexSize, kTexSize,
+                sk_tool_utils::color_to_565(0x0),
+                sk_tool_utils::color_to_565(0xFF804020),
+                8);
+
+        SkRect bitmapBounds = SkRect::MakeIWH(diffuse.width(), diffuse.height());
+
+        SkMatrix matrix;
+        SkRect r = SkRect::MakeWH(SkIntToScalar(kTexSize), SkIntToScalar(kTexSize));
+        matrix.setRectToRect(bitmapBounds, r, SkMatrix::kFill_ScaleToFit);
+
+        SkMatrix ctm;
+        ctm.setRotate(45);
+        SkBitmap normals;
+        normals.allocN32Pixels(kTexSize, kTexSize);
+
+        sk_tool_utils::create_frustum_normal_map(&normals, SkIRect::MakeWH(kTexSize, kTexSize));
+        sk_sp<SkShader> normalMap = SkShader::MakeBitmapShader(normals, SkShader::kClamp_TileMode,
+                SkShader::kClamp_TileMode, &matrix);
+        sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(std::move(normalMap),
+                                                                               ctm);
+        sk_sp<SkShader> diffuseShader = SkShader::MakeBitmapShader(diffuse,
+                SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix);
+
+        sk_sp<SkShader> lightingShader = SkLightingShader::Make(diffuseShader,
+                                                                normalSource,
+                                                                fLights);
+        sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter));
+
+        lightingShader = SkLightingShader::Make(std::move(diffuseShader),
+                                                nullptr,
+                                                fLights);
+        sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter));
+
+        lightingShader = SkLightingShader::Make(nullptr,
+                                                std::move(normalSource),
+                                                fLights);
+        sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter));
+
+        lightingShader = SkLightingShader::Make(nullptr,
+                                                nullptr,
+                                                fLights);
+        sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter));
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#include "SkAnnotation.h"
+
+static sk_sp<SkPicture> copy_picture_via_serialization(SkPicture* src) {
+    SkDynamicMemoryWStream wstream;
+    src->serialize(&wstream);
+    std::unique_ptr<SkStreamAsset> rstream(wstream.detachAsStream());
+    return SkPicture::MakeFromStream(rstream.get());
+}
+
+struct AnnotationRec {
+    const SkRect    fRect;
+    const char*     fKey;
+    sk_sp<SkData>   fValue;
+};
+
+class TestAnnotationCanvas : public SkCanvas {
+    skiatest::Reporter*     fReporter;
+    const AnnotationRec*    fRec;
+    int                     fCount;
+    int                     fCurrIndex;
+
+public:
+    TestAnnotationCanvas(skiatest::Reporter* reporter, const AnnotationRec rec[], int count)
+        : SkCanvas(100, 100)
+        , fReporter(reporter)
+        , fRec(rec)
+        , fCount(count)
+        , fCurrIndex(0)
+    {}
+
+    ~TestAnnotationCanvas() {
+        REPORTER_ASSERT(fReporter, fCount == fCurrIndex);
+    }
+
+protected:
+    void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
+        REPORTER_ASSERT(fReporter, fCurrIndex < fCount);
+        REPORTER_ASSERT(fReporter, rect == fRec[fCurrIndex].fRect);
+        REPORTER_ASSERT(fReporter, !strcmp(key, fRec[fCurrIndex].fKey));
+        REPORTER_ASSERT(fReporter, value->equals(fRec[fCurrIndex].fValue.get()));
+        fCurrIndex += 1;
+    }
+};
+
+/*
+ *  Test the 3 annotation types by recording them into a picture, serializing, and then playing
+ *  them back into another canvas.
+ */
+DEF_TEST(Annotations, reporter) {
+    SkPictureRecorder recorder;
+    SkCanvas* recordingCanvas = recorder.beginRecording(SkRect::MakeWH(100, 100));
+
+    const char* str0 = "rect-with-url";
+    const SkRect r0 = SkRect::MakeWH(10, 10);
+    sk_sp<SkData> d0(SkData::MakeWithCString(str0));
+    SkAnnotateRectWithURL(recordingCanvas, r0, d0.get());
+
+    const char* str1 = "named-destination";
+    const SkRect r1 = SkRect::MakeXYWH(5, 5, 0, 0); // collapsed to a point
+    sk_sp<SkData> d1(SkData::MakeWithCString(str1));
+    SkAnnotateNamedDestination(recordingCanvas, {r1.x(), r1.y()}, d1.get());
+
+    const char* str2 = "link-to-destination";
+    const SkRect r2 = SkRect::MakeXYWH(20, 20, 5, 6);
+    sk_sp<SkData> d2(SkData::MakeWithCString(str2));
+    SkAnnotateLinkToDestination(recordingCanvas, r2, d2.get());
+
+    const AnnotationRec recs[] = {
+        { r0, SkAnnotationKeys::URL_Key(),                  std::move(d0) },
+        { r1, SkAnnotationKeys::Define_Named_Dest_Key(),    std::move(d1) },
+        { r2, SkAnnotationKeys::Link_Named_Dest_Key(),      std::move(d2) },
+    };
+
+    sk_sp<SkPicture> pict0(recorder.finishRecordingAsPicture());
+    sk_sp<SkPicture> pict1(copy_picture_via_serialization(pict0.get()));
+
+    TestAnnotationCanvas canvas(reporter, recs, SK_ARRAY_COUNT(recs));
+    canvas.drawPicture(pict1);
 }
diff --git a/src/third_party/skia/tests/ShaderImageFilterTest.cpp b/src/third_party/skia/tests/ShaderImageFilterTest.cpp
deleted file mode 100644
index 45fb5ca..0000000
--- a/src/third_party/skia/tests/ShaderImageFilterTest.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkRectShaderImageFilter.h"
-#include "SkShader.h"
-#include "Test.h"
-
-DEF_TEST(ShaderImageFilter, reporter) {
-    int w = 10, h = 10;
-    SkRect r = SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h)); // Make small 10x10 gradient
-
-    SkBitmap filterResult, shaderResult;
-
-    filterResult.allocN32Pixels(w, h);
-    SkCanvas canvasFilter(filterResult);
-    canvasFilter.clear(0x00000000);
-
-    shaderResult.allocN32Pixels(w, h);
-    SkCanvas canvasShader(shaderResult);
-    canvasShader.clear(0x00000000);
-
-    SkPoint center = SkPoint::Make(SkIntToScalar(5), SkIntToScalar(5));
-    SkColor colors[] = {SK_ColorBLUE, SK_ColorRED, SK_ColorGREEN};
-    SkScalar pos[] = {0, SK_ScalarHalf, SK_Scalar1};
-    SkScalar radius = SkIntToScalar(5);
-
-    // Test using the image filter
-    {
-        SkShader* s = SkGradientShader::CreateRadial(
-            center, radius, colors, pos, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode);
-        SkPaint paint;
-        SkImageFilter::CropRect cr(r);
-        paint.setImageFilter(SkRectShaderImageFilter::Create(s, &cr))->unref();
-        canvasFilter.drawRect(r, paint);
-        s->unref();
-    }
-
-    // Test using the shader directly
-    {
-        SkShader* s = SkGradientShader::CreateRadial(
-            center, radius, colors, pos, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode);
-        SkPaint paint;
-        paint.setShader(s)->unref();
-        canvasShader.drawRect(r, paint);
-    }
-
-    // Assert that both paths yielded the same result
-    for (int y = 0; y < r.height(); ++y) {
-        const SkPMColor* filterPtr = filterResult.getAddr32(0, y);
-        const SkPMColor* shaderPtr = shaderResult.getAddr32(0, y);
-        for (int x = 0; x < r.width(); ++x, ++filterPtr, ++shaderPtr) {
-            REPORTER_ASSERT(reporter, *filterPtr == *shaderPtr);
-        }
-    }
-}
diff --git a/src/third_party/skia/tests/ShaderOpacityTest.cpp b/src/third_party/skia/tests/ShaderOpacityTest.cpp
index 90d25d7..698d544 100644
--- a/src/third_party/skia/tests/ShaderOpacityTest.cpp
+++ b/src/third_party/skia/tests/ShaderOpacityTest.cpp
@@ -17,42 +17,36 @@
     bmp.setInfo(info);
 
     // test 1: bitmap without pixel data
-    SkShader* shader = SkShader::CreateBitmapShader(bmp,
+    auto shader = SkShader::MakeBitmapShader(bmp,
         SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     REPORTER_ASSERT(reporter, shader);
     REPORTER_ASSERT(reporter, !shader->isOpaque());
-    shader->unref();
 
     // From this point on, we have pixels
     bmp.allocPixels(info);
 
     // test 2: not opaque by default
-    shader = SkShader::CreateBitmapShader(bmp,
+    shader = SkShader::MakeBitmapShader(bmp,
         SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     REPORTER_ASSERT(reporter, shader);
     REPORTER_ASSERT(reporter, !shader->isOpaque());
-    shader->unref();
 
     // test 3: explicitly opaque
     bmp.setAlphaType(kOpaque_SkAlphaType);
-    shader = SkShader::CreateBitmapShader(bmp,
+    shader = SkShader::MakeBitmapShader(bmp,
         SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     REPORTER_ASSERT(reporter, shader);
     REPORTER_ASSERT(reporter, shader->isOpaque());
-    shader->unref();
 
     // test 4: explicitly not opaque
     bmp.setAlphaType(kPremul_SkAlphaType);
-    shader = SkShader::CreateBitmapShader(bmp,
+    shader = SkShader::MakeBitmapShader(bmp,
         SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     REPORTER_ASSERT(reporter, shader);
     REPORTER_ASSERT(reporter, !shader->isOpaque());
-    shader->unref();
-
 }
 
-static void test_gradient(skiatest::Reporter* reporter)
-{
+static void test_gradient(skiatest::Reporter* reporter) {
     SkPoint pts[2];
     pts[0].iset(0, 0);
     pts[1].iset(1, 0);
@@ -64,39 +58,33 @@
     // test 1: all opaque
     colors[0] = SkColorSetARGB(0xFF, 0, 0, 0);
     colors[1] = SkColorSetARGB(0xFF, 0, 0, 0);
-    SkShader* grad = SkGradientShader::CreateLinear(pts, colors, pos, count,
-                                                    mode);
+    auto grad = SkGradientShader::MakeLinear(pts, colors, pos, count, mode);
     REPORTER_ASSERT(reporter, grad);
     REPORTER_ASSERT(reporter, grad->isOpaque());
-    grad->unref();
 
     // test 2: all 0 alpha
     colors[0] = SkColorSetARGB(0, 0, 0, 0);
     colors[1] = SkColorSetARGB(0, 0, 0, 0);
-    grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode);
+    grad = SkGradientShader::MakeLinear(pts, colors, pos, count, mode);
     REPORTER_ASSERT(reporter, grad);
     REPORTER_ASSERT(reporter, !grad->isOpaque());
-    grad->unref();
 
     // test 3: one opaque, one transparent
     colors[0] = SkColorSetARGB(0xFF, 0, 0, 0);
     colors[1] = SkColorSetARGB(0x40, 0, 0, 0);
-    grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode);
+    grad = SkGradientShader::MakeLinear(pts, colors, pos, count, mode);
     REPORTER_ASSERT(reporter, grad);
     REPORTER_ASSERT(reporter, !grad->isOpaque());
-    grad->unref();
 
     // test 4: test 3, swapped
     colors[0] = SkColorSetARGB(0x40, 0, 0, 0);
     colors[1] = SkColorSetARGB(0xFF, 0, 0, 0);
-    grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode);
+    grad = SkGradientShader::MakeLinear(pts, colors, pos, count, mode);
     REPORTER_ASSERT(reporter, grad);
     REPORTER_ASSERT(reporter, !grad->isOpaque());
-    grad->unref();
 }
 
-static void test_color(skiatest::Reporter* reporter)
-{
+static void test_color(skiatest::Reporter* reporter) {
     SkColorShader colorShader1(SkColorSetARGB(0,0,0,0));
     REPORTER_ASSERT(reporter, !colorShader1.isOpaque());
     SkColorShader colorShader2(SkColorSetARGB(0xFF,0,0,0));
diff --git a/src/third_party/skia/tests/ShaderTest.cpp b/src/third_party/skia/tests/ShaderTest.cpp
new file mode 100644
index 0000000..06aca1c
--- /dev/null
+++ b/src/third_party/skia/tests/ShaderTest.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkImage.h"
+#include "SkPerlinNoiseShader.h"
+#include "SkRRect.h"
+#include "SkShader.h"
+#include "SkSurface.h"
+#include "SkData.h"
+
+static void check_isabitmap(skiatest::Reporter* reporter, SkShader* shader,
+                            int expectedW, int expectedH,
+                            SkShader::TileMode expectedX, SkShader::TileMode expectedY,
+                            const SkMatrix& expectedM) {
+    SkShader::TileMode tileModes[2];
+    SkMatrix localM;
+
+#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
+    SkBitmap bm;
+    REPORTER_ASSERT(reporter, shader->isABitmap(&bm, &localM, tileModes));
+    REPORTER_ASSERT(reporter, bm.width() == expectedW);
+    REPORTER_ASSERT(reporter, bm.height() == expectedH);
+    REPORTER_ASSERT(reporter, localM == expectedM);
+    REPORTER_ASSERT(reporter, tileModes[0] == expectedX);
+    REPORTER_ASSERT(reporter, tileModes[1] == expectedY);
+#endif
+
+    // wack these so we don't get a false positive
+    localM.setScale(9999, -9999);
+    tileModes[0] = tileModes[1] = (SkShader::TileMode)99;
+
+    SkImage* image = shader->isAImage(&localM, tileModes);
+    REPORTER_ASSERT(reporter, image);
+    REPORTER_ASSERT(reporter, image->width() == expectedW);
+    REPORTER_ASSERT(reporter, image->height() == expectedH);
+    REPORTER_ASSERT(reporter, localM == expectedM);
+    REPORTER_ASSERT(reporter, tileModes[0] == expectedX);
+    REPORTER_ASSERT(reporter, tileModes[1] == expectedY);
+}
+
+DEF_TEST(Shader_isABitmap, reporter) {
+    const int W = 100;
+    const int H = 100;
+    SkBitmap bm;
+    bm.allocN32Pixels(W, H);
+    auto img = SkImage::MakeFromBitmap(bm);
+    const SkMatrix localM = SkMatrix::MakeScale(2, 3);
+    const SkShader::TileMode tmx = SkShader::kRepeat_TileMode;
+    const SkShader::TileMode tmy = SkShader::kMirror_TileMode;
+
+    auto shader0 = SkShader::MakeBitmapShader(bm, tmx, tmy, &localM);
+    auto shader1 = SkImage::MakeFromBitmap(bm)->makeShader(tmx, tmy, &localM);
+
+    check_isabitmap(reporter, shader0.get(), W, H, tmx, tmy, localM);
+    check_isabitmap(reporter, shader1.get(), W, H, tmx, tmy, localM);
+}
+
+// Make sure things are ok with just a single leg.
+DEF_TEST(ComposeShaderSingle, reporter) {
+    SkBitmap srcBitmap;
+    srcBitmap.allocN32Pixels(10, 10);
+    srcBitmap.eraseColor(SK_ColorRED);
+    SkCanvas canvas(srcBitmap);
+    SkPaint p;
+    p.setShader(
+        SkShader::MakeComposeShader(
+        SkShader::MakeEmptyShader(),
+        SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 2, 0.0f),
+        SkBlendMode::kClear));
+    SkRRect rr;
+    SkVector rd[] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
+    rr.setRectRadii({0, 0, 0, 0}, rd);
+    canvas.drawRRect(rr, p);
+}
diff --git a/src/third_party/skia/tests/ShadowUtilsTest.cpp b/src/third_party/skia/tests/ShadowUtilsTest.cpp
new file mode 100644
index 0000000..c0e20d5
--- /dev/null
+++ b/src/third_party/skia/tests/ShadowUtilsTest.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCanvas.h"
+#include "SkPath.h"
+#include "SkShadowTessellator.h"
+#include "SkShadowUtils.h"
+#include "SkVertices.h"
+#include "Test.h"
+
+void tessellate_shadow(skiatest::Reporter* reporter, const SkPath& path, const SkMatrix& ctm,
+                       bool expectSuccess) {
+
+    auto heightParams = SkPoint3::Make(0, 0, 4);
+
+    auto verts = SkShadowTessellator::MakeAmbient(path, ctm, heightParams, true);
+    if (expectSuccess != SkToBool(verts)) {
+        ERRORF(reporter, "Expected shadow tessellation to %s but it did not.",
+               expectSuccess ? "succeed" : "fail");
+    }
+    verts = SkShadowTessellator::MakeAmbient(path, ctm, heightParams, false);
+    if (expectSuccess != SkToBool(verts)) {
+        ERRORF(reporter, "Expected shadow tessellation to %s but it did not.",
+               expectSuccess ? "succeed" : "fail");
+    }
+    verts = SkShadowTessellator::MakeSpot(path, ctm, heightParams, {0, 0, 128}, 128.f, false);
+    if (expectSuccess != SkToBool(verts)) {
+        ERRORF(reporter, "Expected shadow tessellation to %s but it did not.",
+               expectSuccess ? "succeed" : "fail");
+    }
+    verts = SkShadowTessellator::MakeSpot(path, ctm, heightParams, {0, 0, 128}, 128.f, false);
+    if (expectSuccess != SkToBool(verts)) {
+        ERRORF(reporter, "Expected shadow tessellation to %s but it did not.",
+               expectSuccess ? "succeed" : "fail");
+    }
+}
+
+DEF_TEST(ShadowUtils, reporter) {
+    SkCanvas canvas(100, 100);
+
+    SkPath path;
+    path.cubicTo(100, 50, 20, 100, 0, 0);
+    tessellate_shadow(reporter, path, canvas.getTotalMatrix(), true);
+
+    // This line segment has no area and no shadow.
+    path.reset();
+    path.lineTo(10.f, 10.f);
+    tessellate_shadow(reporter, path, canvas.getTotalMatrix(), false);
+
+    // A series of colinear line segments
+    path.reset();
+    for (int i = 0; i < 10; ++i) {
+        path.lineTo((SkScalar)i, (SkScalar)i);
+    }
+    tessellate_shadow(reporter, path, canvas.getTotalMatrix(), false);
+}
diff --git a/src/third_party/skia/tests/SizeTest.cpp b/src/third_party/skia/tests/SizeTest.cpp
index 9800aa2..9dccdeb 100644
--- a/src/third_party/skia/tests/SizeTest.cpp
+++ b/src/third_party/skia/tests/SizeTest.cpp
@@ -16,7 +16,7 @@
     REPORTER_ASSERT(reporter, a.isEmpty());
     a.set(5, -5);
     REPORTER_ASSERT(reporter, a.isEmpty());
-    a.clampNegToZero();
+    a = SkISize{5, 0};
     REPORTER_ASSERT(reporter, a.isEmpty());
     b.set(5, 0);
     REPORTER_ASSERT(reporter, a == b);
@@ -42,7 +42,7 @@
     REPORTER_ASSERT(reporter, a.isEmpty());
     a.set(x, -x);
     REPORTER_ASSERT(reporter, a.isEmpty());
-    a.clampNegToZero();
+    a = SkSize{x, 0};
     REPORTER_ASSERT(reporter, a.isEmpty());
     b.set(x, 0);
     REPORTER_ASSERT(reporter, a == b);
diff --git a/src/third_party/skia/tests/Sk4x4fTest.cpp b/src/third_party/skia/tests/Sk4x4fTest.cpp
new file mode 100644
index 0000000..100e139
--- /dev/null
+++ b/src/third_party/skia/tests/Sk4x4fTest.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Sk4x4f.h"
+#include "Test.h"
+
+DEF_TEST(Sk4x4f, r) {
+    Sk4x4f f;
+
+    Sk4f x{ 0, 1, 2, 3},
+         y{ 4, 5, 6, 7},
+         z{ 8, 9,10,11},
+         w{12,13,14,15};
+    f = Sk4x4f::Transpose(x,y,z,w);
+    REPORTER_ASSERT(r, f.r[0] == 0 && f.r[1] == 4 && f.r[2] ==  8 && f.r[3] == 12);
+    REPORTER_ASSERT(r, f.g[0] == 1 && f.g[1] == 5 && f.g[2] ==  9 && f.g[3] == 13);
+    REPORTER_ASSERT(r, f.b[0] == 2 && f.b[1] == 6 && f.b[2] == 10 && f.b[3] == 14);
+    REPORTER_ASSERT(r, f.a[0] == 3 && f.a[1] == 7 && f.a[2] == 11 && f.a[3] == 15);
+
+    Sk4f s,t,u,v;
+    f.transpose(&s,&t,&u,&v);
+    REPORTER_ASSERT(r, (x == s).allTrue()
+                    && (y == t).allTrue()
+                    && (z == u).allTrue()
+                    && (w == v).allTrue());
+
+
+    float fs[16] = {0,1,2,3, 4,5,6,7, 8,9,10,11, 12,13,14,15};
+    f = Sk4x4f::Transpose(fs);
+    REPORTER_ASSERT(r, f.r[0] == 0 && f.r[1] == 4 && f.r[2] ==  8 && f.r[3] == 12);
+    REPORTER_ASSERT(r, f.g[0] == 1 && f.g[1] == 5 && f.g[2] ==  9 && f.g[3] == 13);
+    REPORTER_ASSERT(r, f.b[0] == 2 && f.b[1] == 6 && f.b[2] == 10 && f.b[3] == 14);
+    REPORTER_ASSERT(r, f.a[0] == 3 && f.a[1] == 7 && f.a[2] == 11 && f.a[3] == 15);
+
+    float fs_back[16];
+    f.transpose(fs_back);
+    REPORTER_ASSERT(r, 0 == memcmp(fs, fs_back, sizeof(fs)));
+
+
+    uint8_t bs[16] = {0,1,2,3, 4,5,6,7, 8,9,10,11, 12,13,14,15};
+    f = Sk4x4f::Transpose(bs);
+    REPORTER_ASSERT(r, f.r[0] == 0 && f.r[1] == 4 && f.r[2] ==  8 && f.r[3] == 12);
+    REPORTER_ASSERT(r, f.g[0] == 1 && f.g[1] == 5 && f.g[2] ==  9 && f.g[3] == 13);
+    REPORTER_ASSERT(r, f.b[0] == 2 && f.b[1] == 6 && f.b[2] == 10 && f.b[3] == 14);
+    REPORTER_ASSERT(r, f.a[0] == 3 && f.a[1] == 7 && f.a[2] == 11 && f.a[3] == 15);
+
+    uint8_t bs_back[16];
+    f.transpose(bs_back);
+    REPORTER_ASSERT(r, 0 == memcmp(bs, bs_back, sizeof(bs)));
+}
diff --git a/src/third_party/skia/tests/SkBase64Test.cpp b/src/third_party/skia/tests/SkBase64Test.cpp
index 14ff1f7..d855ef5 100644
--- a/src/third_party/skia/tests/SkBase64Test.cpp
+++ b/src/third_party/skia/tests/SkBase64Test.cpp
@@ -17,10 +17,10 @@
 
     for (int offset = 0; offset < 6; ++offset) {
         size_t length = 256 - offset;
-        size_t encodeLength = SkBase64::Encode(all + offset, length, NULL);
+        size_t encodeLength = SkBase64::Encode(all + offset, length, nullptr);
         SkAutoTMalloc<char> src(encodeLength + 1);
         SkBase64::Encode(all + offset, length, src.get());
-        src[encodeLength] = '\0';
+        src[SkToInt(encodeLength)] = '\0';
         SkBase64 tryMe;
         tryMe.decode(src.get(), encodeLength);
         REPORTER_ASSERT(reporter, (strcmp((const char*) (all + offset), tryMe.getData()) == 0));
diff --git a/src/third_party/skia/tests/SkBlend_optsTest.cpp b/src/third_party/skia/tests/SkBlend_optsTest.cpp
new file mode 100644
index 0000000..f8cf2d8
--- /dev/null
+++ b/src/third_party/skia/tests/SkBlend_optsTest.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <string>
+#include <tuple>
+#include <vector>
+#include "Resources.h"
+#include "SkCpu.h"
+#include "SkImage.h"
+#include "SkImage_Base.h"
+#include "SkOpts.h"
+#include "SkPM4fPriv.h"
+#include "SkNx.h"
+#include "Test.h"
+
+typedef void (*Blender)(uint32_t* dst, const uint32_t* const srcStart, int ndst, const int nsrc);
+
+static inline void srcover_srgb_srgb_1(uint32_t* dst, uint32_t src) {
+    auto d = Sk4f_fromS32(*dst),
+         s = Sk4f_fromS32( src);
+    *dst = Sk4f_toS32(s + d * (1.0f - s[3]));
+}
+
+static void brute_force_srcover_srgb_srgb(
+    uint32_t* dst, const uint32_t* const src, int ndst, const int nsrc) {
+    while (ndst > 0) {
+        int n = SkTMin(ndst, nsrc);
+
+        for (int i = 0; i < n; i++) {
+            srcover_srgb_srgb_1(dst++, src[i]);
+        }
+        ndst -= n;
+    }
+}
+
+static SkString mismatch_message(std::string resourceName, int x, int y,
+                                  uint32_t src, uint32_t good, uint32_t bad) {
+    return SkStringPrintf(
+        "%s - missmatch at %d, %d src: %08x good: %08x bad: %08x",
+        resourceName.c_str(), x, y, src, good, bad);
+}
+
+static void test_blender(std::string resourceName, skiatest::Reporter* reporter) {
+    std::string fileName = resourceName + ".png";
+    sk_sp<SkImage> image = GetResourceAsImage(fileName.c_str());
+    if (image == nullptr) {
+        ERRORF(reporter, "image is NULL");
+        return;
+    }
+    SkBitmap bm;
+    sk_sp<SkColorSpace> srgbColorSpace = SkColorSpace::MakeSRGB();
+    if (!as_IB(image)->getROPixels(&bm, srgbColorSpace.get())) {
+        ERRORF(reporter, "Could not read resource");
+        return;
+    }
+
+    SkPixmap pixmap;
+    bm.peekPixels(&pixmap);
+    SkASSERTF(pixmap.colorType() == kN32_SkColorType, "colorType: %d", pixmap.colorType());
+    SkASSERT(pixmap.alphaType() != kUnpremul_SkAlphaType);
+    const uint32_t* src = pixmap.addr32();
+    const int width = pixmap.rowBytesAsPixels();
+    SkASSERT(width > 0);
+    SkASSERT(width < 4000);
+    SkAutoTArray<uint32_t> correctDst(width);
+    SkAutoTArray<uint32_t> testDst(width);
+
+    for (int y = 0; y < pixmap.height(); y++) {
+        // TODO: zero is not the most interesting dst to test srcover...
+        sk_bzero(correctDst.get(), width * sizeof(uint32_t));
+        sk_bzero(testDst.get(), width * sizeof(uint32_t));
+        brute_force_srcover_srgb_srgb(correctDst.get(), src, width, width);
+        SkOpts::    srcover_srgb_srgb(   testDst.get(), src, width, width);
+        for (int x = 0; x < width; x++) {
+            REPORTER_ASSERT_MESSAGE(
+                reporter, correctDst[x] == testDst[x],
+                mismatch_message(resourceName, x, y, src[x], correctDst[x], testDst[x]));
+            if (correctDst[x] != testDst[x]) break;
+        }
+        src += width;
+    }
+}
+
+DEF_TEST(SkBlend_optsCheck, reporter) {
+    std::vector<std::string> testResources = {
+        "yellow_rose", "baby_tux", "plane", "mandrill_512", "iconstrip"
+    };
+
+    for (auto& resourceName : testResources) {
+        test_blender(resourceName, reporter);
+    }
+}
+
+DEF_TEST(SkBlend_optsSqrtCheck, reporter) {
+    for (int c = 0; c < 256; c++) {
+        Sk4f i{(float)c};
+        Sk4f ii = i * i;
+        Sk4f s = ii.sqrt() + 0.5f;
+        Sk4f sf = s.floor();
+        REPORTER_ASSERT_MESSAGE(
+            reporter, i[0] == sf[0], SkStringPrintf("i: %f, s: %f", i[0], sf[0]));
+    }
+}
diff --git a/src/third_party/skia/tests/SkColor4fTest.cpp b/src/third_party/skia/tests/SkColor4fTest.cpp
new file mode 100644
index 0000000..f268e5f
--- /dev/null
+++ b/src/third_party/skia/tests/SkColor4fTest.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmapProcShader.h"
+#include "SkColor.h"
+#include "SkColorMatrixFilter.h"
+#include "SkGradientShader.h"
+#include "SkImage.h"
+#include "SkPM4f.h"
+#include "SkShader.h"
+
+#include "Test.h"
+#include "SkRandom.h"
+
+const float kTolerance = 1.0f / (1 << 20);
+
+static bool nearly_equal(float a, float b, float tol = kTolerance) {
+    SkASSERT(tol >= 0);
+    return fabsf(a - b) <= tol;
+}
+
+DEF_TEST(SkColor4f_FromColor, reporter) {
+    const struct {
+        SkColor     fC;
+        SkColor4f   fC4;
+    } recs[] = {
+        { SK_ColorBLACK, { 0, 0, 0, 1 } },
+        { SK_ColorWHITE, { 1, 1, 1, 1 } },
+        { SK_ColorRED,   { 1, 0, 0, 1 } },
+        { SK_ColorGREEN, { 0, 1, 0, 1 } },
+        { SK_ColorBLUE,  { 0, 0, 1, 1 } },
+        { 0,             { 0, 0, 0, 0 } },
+    };
+
+    for (const auto& r : recs) {
+        SkColor4f c4 = SkColor4f::FromColor(r.fC);
+        REPORTER_ASSERT(reporter, c4 == r.fC4);
+    }
+}
+
+DEF_TEST(Color4f_premul, reporter) {
+    SkRandom rand;
+
+    for (int i = 0; i < 1000000; ++i) {
+        // First just test opaque colors, so that the premul should be exact
+        SkColor4f c4 {
+            rand.nextUScalar1(), rand.nextUScalar1(), rand.nextUScalar1(), 1
+        };
+        SkPM4f pm4 = c4.premul();
+        REPORTER_ASSERT(reporter, pm4.a() == c4.fA);
+        REPORTER_ASSERT(reporter, pm4.r() == c4.fA * c4.fR);
+        REPORTER_ASSERT(reporter, pm4.g() == c4.fA * c4.fG);
+        REPORTER_ASSERT(reporter, pm4.b() == c4.fA * c4.fB);
+
+        // We compare with a tolerance, in case our premul multiply is implemented at slightly
+        // different precision than the test code.
+        c4.fA = rand.nextUScalar1();
+        pm4 = c4.premul();
+        REPORTER_ASSERT(reporter, pm4.fVec[SK_A_INDEX] == c4.fA);
+        REPORTER_ASSERT(reporter, nearly_equal(pm4.r(), c4.fA * c4.fR));
+        REPORTER_ASSERT(reporter, nearly_equal(pm4.g(), c4.fA * c4.fG));
+        REPORTER_ASSERT(reporter, nearly_equal(pm4.b(), c4.fA * c4.fB));
+    }
+}
diff --git a/src/third_party/skia/tests/SkDOMTest.cpp b/src/third_party/skia/tests/SkDOMTest.cpp
new file mode 100644
index 0000000..6ea4e78
--- /dev/null
+++ b/src/third_party/skia/tests/SkDOMTest.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkStream.h"
+#include "Test.h"
+
+#if defined(SK_XML)
+
+#include "SkDOM.h"
+
+static const SkDOM::Node* check_node(skiatest::Reporter* r, const SkDOM& dom,
+                                     const SkDOM::Node* node, const char* expectedName,
+                                     SkDOM::Type expectedType) {
+    REPORTER_ASSERT(r, node);
+    if (node) {
+        REPORTER_ASSERT(r, !strcmp(dom.getName(node), expectedName));
+        REPORTER_ASSERT(r, dom.getType(node) == expectedType);
+    }
+    return node;
+}
+
+DEF_TEST(SkDOM_test, r) {
+    static const char gDoc[] =
+        "<root a='1' b='2'>"
+            "<elem1 c='3' />"
+            "<elem2 d='4' />"
+            "<elem3 e='5'>"
+                "<subelem1>Some text.</subelem1>"
+                "<subelem2 f='6' g='7'/>"
+                "<subelem3>Some more text.</subelem3>"
+            "</elem3>"
+            "<elem4 h='8'/>"
+        "</root>"
+        ;
+
+    SkMemoryStream docStream(gDoc, sizeof(gDoc) - 1);
+
+    SkDOM   dom;
+    REPORTER_ASSERT(r, !dom.getRootNode());
+
+    const SkDOM::Node* root = dom.build(docStream);
+    REPORTER_ASSERT(r, root && dom.getRootNode() == root);
+
+    const char* v = dom.findAttr(root, "a");
+    REPORTER_ASSERT(r, v && !strcmp(v, "1"));
+    v = dom.findAttr(root, "b");
+    REPORTER_ASSERT(r, v && !strcmp(v, "2"));
+    v = dom.findAttr(root, "c");
+    REPORTER_ASSERT(r, v == nullptr);
+
+    REPORTER_ASSERT(r, dom.getFirstChild(root, "elem1"));
+    REPORTER_ASSERT(r, !dom.getFirstChild(root, "subelem1"));
+
+    {
+        const auto* elem1 = check_node(r, dom, dom.getFirstChild(root),
+                                       "elem1", SkDOM::kElement_Type);
+        const auto* elem2 = check_node(r, dom, dom.getNextSibling(elem1),
+                                       "elem2", SkDOM::kElement_Type);
+        const auto* elem3 = check_node(r, dom, dom.getNextSibling(elem2),
+                                       "elem3", SkDOM::kElement_Type);
+        {
+            const auto* subelem1 = check_node(r, dom, dom.getFirstChild(elem3),
+                                              "subelem1", SkDOM::kElement_Type);
+            {
+                check_node(r, dom, dom.getFirstChild(subelem1),
+                           "Some text.", SkDOM::kText_Type);
+            }
+            const auto* subelem2 = check_node(r, dom, dom.getNextSibling(subelem1),
+                                              "subelem2", SkDOM::kElement_Type);
+            const auto* subelem3 = check_node(r, dom, dom.getNextSibling(subelem2),
+                                              "subelem3", SkDOM::kElement_Type);
+            {
+                check_node(r, dom, dom.getFirstChild(subelem3),
+                           "Some more text.", SkDOM::kText_Type);
+            }
+        }
+        check_node(r, dom, dom.getNextSibling(elem3),
+                   "elem4", SkDOM::kElement_Type);
+    }
+}
+
+#endif // SK_XML
diff --git a/src/third_party/skia/tests/SkFixed15Test.cpp b/src/third_party/skia/tests/SkFixed15Test.cpp
new file mode 100644
index 0000000..df957b1
--- /dev/null
+++ b/src/third_party/skia/tests/SkFixed15Test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "SkFixed15.h"
+
+DEF_TEST(SkFixed15, r) {
+    // For all v, v*0 == 0, v*1 == v.
+    for (uint16_t bits = 0; bits <= 32768; bits++) {
+        auto v = SkFixed15::Load(bits);
+        REPORTER_ASSERT(r, v * 0.0f == 0.0f);
+        REPORTER_ASSERT(r, v * 1.0f == v);
+    }
+
+    // Division and multiplication by powers of 2 is exact.
+    SkFixed15 v = 1.0f;
+    REPORTER_ASSERT(r, (v>>1)    == 0.50f);
+    REPORTER_ASSERT(r, (v>>2)    == 0.25f);
+    REPORTER_ASSERT(r, (v>>2<<1) == 0.50f);
+
+    // FromU8() should be just as good as going through float.
+    for (int x = 0; x < 256; x++) {
+        REPORTER_ASSERT(r, SkFixed15::FromU8(x) == SkFixed15(x * (1/255.0f)));
+    }
+
+    // to_u8() and FromU8() should roundtrip all bytes.
+    for (int x = 0; x < 256; x++) {
+        REPORTER_ASSERT(r, x == SkFixed15::FromU8(x).to_u8());
+    }
+}
diff --git a/src/third_party/skia/tests/SkImageTest.cpp b/src/third_party/skia/tests/SkImageTest.cpp
new file mode 100644
index 0000000..4a05b44
--- /dev/null
+++ b/src/third_party/skia/tests/SkImageTest.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCanvas.h"
+#include "SkImagePriv.h"
+#include "Test.h"
+
+static const int gWidth = 20;
+static const int gHeight = 20;
+
+// Tests that SkNewImageFromBitmap obeys pixelref origin.
+DEF_TEST(SkImageFromBitmap_extractSubset, reporter) {
+    sk_sp<SkImage> image;
+    {
+        SkBitmap srcBitmap;
+        srcBitmap.allocN32Pixels(gWidth, gHeight);
+        srcBitmap.eraseColor(SK_ColorRED);
+        SkCanvas canvas(srcBitmap);
+        SkIRect r = SkIRect::MakeXYWH(5, 5, gWidth - 5, gWidth - 5);
+        SkPaint p;
+        p.setColor(SK_ColorGREEN);
+        canvas.drawIRect(r, p);
+        SkBitmap dstBitmap;
+        srcBitmap.extractSubset(&dstBitmap, r);
+        image = SkImage::MakeFromBitmap(dstBitmap);
+    }
+
+    SkBitmap tgt;
+    tgt.allocN32Pixels(gWidth, gHeight);
+    SkCanvas canvas(tgt);
+    canvas.clear(SK_ColorTRANSPARENT);
+    canvas.drawImage(image, 0, 0, nullptr);
+
+    uint32_t pixel = 0;
+    SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType);
+    canvas.readPixels(info, &pixel, 4, 0, 0);
+    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
+    canvas.readPixels(info, &pixel, 4, gWidth - 6, gWidth - 6);
+    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
+
+    canvas.readPixels(info, &pixel, 4, gWidth - 5, gWidth - 5);
+    REPORTER_ASSERT(reporter, pixel == SK_ColorTRANSPARENT);
+}
diff --git a/src/third_party/skia/tests/SkLinearBitmapPipelineTest.cpp b/src/third_party/skia/tests/SkLinearBitmapPipelineTest.cpp
new file mode 100644
index 0000000..4b54641
--- /dev/null
+++ b/src/third_party/skia/tests/SkLinearBitmapPipelineTest.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <algorithm>
+#include <array>
+#include <tuple>
+#include <vector>
+#include "SkLinearBitmapPipeline.h"
+#include "SkColor.h"
+#include "SkNx.h"
+#include "SkPoint.h"
+#include "SkPM4f.h"
+#include "Test.h"
+#include "SkLinearBitmapPipeline_tile.h"
+
+
+DEF_TEST(LBPBilerpEdge, reporter) {
+
+}
+
+static SkString dump(SkScalar cut, Span prefix, Span remainder) {
+    SkPoint prefixStart; SkScalar prefixLen; int prefixCount;
+    std::tie(prefixStart, prefixLen, prefixCount) = prefix;
+    SkPoint remainderStart; SkScalar remainderLen; int remainderCount;
+    std::tie(remainderStart, remainderLen, remainderCount) = remainder;
+    return SkStringPrintf("cut: %f prefix: (%f, %f), %f, %d - remainder: (%f, %f), %f, %d",
+                          cut,
+                          prefixStart.fX, prefixStart.fY, prefixLen, prefixCount,
+                          remainderStart.fX, remainderStart.fY, remainderLen, remainderCount);
+}
+
+static void check_span_result(
+    skiatest::Reporter* reporter,
+    Span span, SkScalar dx, SkScalar cut, SkPoint start, SkScalar len, int count) {
+    SkPoint originalStart; SkScalar originalLen; int originalCount;
+    std::tie(originalStart, originalLen, originalCount) = span;
+
+    Span prefix = span.breakAt(cut, dx);
+
+    SkPoint prefixStart; SkScalar prefixLen; int prefixCount;
+    std::tie(prefixStart, prefixLen, prefixCount) = prefix;
+
+    REPORTER_ASSERT_MESSAGE(reporter, prefixStart == start, dump(cut, prefix, span));
+    REPORTER_ASSERT_MESSAGE(reporter, prefixLen == len, dump(cut, prefix, span));
+    REPORTER_ASSERT_MESSAGE(reporter, prefixCount == count, dump(cut, prefix, span));
+    SkPoint expectedRemainderStart;
+    SkScalar expectedRemainderLen;
+    int expectedRemainderCount;
+    if (prefix.isEmpty()) {
+        expectedRemainderStart = originalStart;
+        expectedRemainderLen = originalLen;
+        expectedRemainderCount = originalCount;
+    } else {
+        expectedRemainderStart = SkPoint::Make(originalStart.fX + prefixLen + dx, originalStart.fY);
+        expectedRemainderLen = originalLen - prefixLen - dx;
+        expectedRemainderCount = originalCount - prefixCount;
+    }
+
+    if (!span.isEmpty()) {
+        SkPoint remainderStart;
+        SkScalar remainderLen;
+        int remainderCount;
+        std::tie(remainderStart, remainderLen, remainderCount) = span;
+        // Remainder span
+        REPORTER_ASSERT_MESSAGE(reporter, expectedRemainderStart == remainderStart,
+                                dump(cut, prefix, span));
+        REPORTER_ASSERT_MESSAGE(reporter,
+                                expectedRemainderLen == remainderLen,
+                                dump(cut, prefix, span));
+        REPORTER_ASSERT_MESSAGE(reporter,
+                                expectedRemainderCount == remainderCount,
+                                dump(cut, prefix, span));
+    }
+}
+
+DEF_TEST(LBPSpanOps, reporter) {
+    {
+        SkScalar dx = 1.0f;
+        SkPoint start = SkPoint::Make(-5, -5);
+        Span span{start, 9.0f, 10};
+        check_span_result(reporter, span, dx,  0.0f, start, 4.0f, 5);
+        check_span_result(reporter, span, dx, -6.0f, SkPoint::Make(0, 0), 0.0f, 0);
+        check_span_result(reporter, span, dx, -5.0f, SkPoint::Make(0, 0), 0.0f, 0);
+        check_span_result(reporter, span, dx, -4.0f, SkPoint::Make(-5, -5), 0.0f, 1);
+        check_span_result(reporter, span, dx,  4.0f, SkPoint::Make(-5, -5), 8.0f, 9);
+        check_span_result(reporter, span, dx,  5.0f, SkPoint::Make(-5, -5), 9.0f, 10);
+        check_span_result(reporter, span, dx,  6.0f, SkPoint::Make(-5, -5), 9.0f, 10);
+    }
+    {
+        SkScalar dx = -1.0f;
+        SkPoint start = SkPoint::Make(5, 5);
+        Span span{start, -9.0f, 10};
+        check_span_result(reporter, span, dx,  0.0f, start, -5.0f, 6);
+        check_span_result(reporter, span, dx, -6.0f, SkPoint::Make(5, 5), -9.0f, 10);
+        check_span_result(reporter, span, dx, -5.0f, SkPoint::Make(5, 5), -9.0f, 10);
+        check_span_result(reporter, span, dx, -4.0f, SkPoint::Make(5, 5), -9.0f, 10);
+        check_span_result(reporter, span, dx,  4.0f, SkPoint::Make(5, 5), -1.0f, 2);
+        check_span_result(reporter, span, dx,  5.0f, SkPoint::Make(5, 5), 0.0f, 1);
+        check_span_result(reporter, span, dx,  6.0f, SkPoint::Make(0, 0), 0.0f, 0);
+    }
+}
+
+DEF_TEST(LBPBilerpSpanOps, reporter) {
+
+}
+
+template <typename XTiler, typename YTiler>
+static bool compare_tiler_case(
+    XTiler& xTiler, YTiler& yTiler, Span span, skiatest::Reporter* reporter) {
+    Span originalSpan = span;
+    std::vector<SkPoint> listPoints;
+    std::vector<SkPoint> spanPoints;
+    struct Sink {
+        void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) {
+            SkASSERT(0 < n && n < 4);
+            if (n >= 1) storePoint({xs[0], ys[0]});
+            if (n >= 2) storePoint({xs[1], ys[1]});
+            if (n >= 3) storePoint({xs[2], ys[2]});
+        }
+
+        void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) {
+            storePoint({xs[0], ys[0]});
+            storePoint({xs[1], ys[1]});
+            storePoint({xs[2], ys[2]});
+            storePoint({xs[3], ys[3]});
+        }
+
+        void pointSpan(Span span) {
+            span_fallback(span, this);
+        }
+
+        void storePoint(SkPoint pt) {
+            fPoints->push_back({SkScalarFloorToScalar(X(pt)), SkScalarFloorToScalar(Y(pt))});
+        }
+
+        std::vector<SkPoint>* fPoints;
+    };
+
+    Sink listSink = {&listPoints};
+    Sink spanSink = {&spanPoints};
+
+    SkPoint start; SkScalar length; int count;
+    std::tie(start, length, count) = span;
+
+    SkScalar dx = length / (count - 1);
+    Sk4f xs = Sk4f{X(start)} + Sk4f{0.0f, dx, 2 * dx, 3 * dx};
+    Sk4f ys = Sk4f{Y(start)};
+    while (count >= 4) {
+        Sk4f txs = xs;
+        Sk4f tys = ys;
+        xTiler.tileXPoints(&txs);
+        yTiler.tileYPoints(&tys);
+        listSink.pointList4(txs, tys);
+        xs = xs + 4.0f * dx;
+        count -= 4;
+    }
+    if (count > 0) {
+        xTiler.tileXPoints(&xs);
+        yTiler.tileYPoints(&ys);
+        listSink.pointListFew(count, xs, ys);
+    }
+
+    std::tie(start, length, count) = originalSpan;
+    SkScalar x = X(start);
+    SkScalar y = yTiler.tileY(Y(start));
+    Span yAdjustedSpan{{x, y}, length, count};
+
+    bool handledSpan = xTiler.maybeProcessSpan(yAdjustedSpan, &spanSink);
+    if (handledSpan) {
+        auto firstNotTheSame = std::mismatch(
+            listPoints.begin(), listPoints.end(), spanPoints.begin());
+        if (firstNotTheSame.first != listSink.fPoints->end()) {
+            auto element = std::distance(listPoints.begin(), firstNotTheSame.first);
+            SkASSERT(element >= 0);
+            std::tie(start, length, count) = originalSpan;
+            ERRORF(reporter, "Span: {%f, %f}, %f, %d", start.fX, start.fY, length, count);
+            ERRORF(reporter, "Size points: %d, size span: %d",
+                   listPoints.size(), spanPoints.size());
+            if ((unsigned)element >= spanPoints.size()) {
+                ERRORF(reporter, "Size points: %d, size span: %d",
+                       listPoints.size(), spanPoints.size());
+                // Mismatch off the end
+                ERRORF(reporter,
+                       "The mismatch is at position %d and has value %f, %f - it is off the end "
+                           "of the other.",
+                       element, X(*firstNotTheSame.first), Y(*firstNotTheSame.first));
+            } else {
+                ERRORF(reporter,
+                       "Mismatch at %d - points: %f, %f - span: %f, %f",
+                       element, listPoints[element].fX, listPoints[element].fY,
+                       spanPoints[element].fX, spanPoints[element].fY);
+            }
+            SkFAIL("aha");
+        }
+    }
+    return true;
+}
+
+template <typename XTiler, typename YTiler>
+static bool compare_tiler_spans(int width, int height, skiatest::Reporter* reporter) {
+    XTiler xTiler{width};
+    YTiler yTiler{height};
+    INFOF(reporter, "w: %d, h: %d \n", width, height);
+    std::array<int, 8> interestingX {{-5, -1, 0, 1, width - 1, width, width + 1, width + 5}};
+    std::array<int, 8> interestingY {{-5, -1, 0, 1, height - 1, height, height + 1, height + 5}};
+    std::array<int, 6> interestingCount {{1, 2, 3, 4, 5, 10}};
+    std::array<SkScalar, 7> interestingScale {{0.0f, 1.0f, 0.5f, 2.1f, -2.1f, -1.0f, -0.5f}};
+    for (auto scale : interestingScale) {
+        for (auto startX : interestingX) {
+            for (auto count : interestingCount) {
+                for (auto y : interestingY) {
+                    Span span{
+                        SkPoint::Make((SkScalar)startX, (SkScalar)y), (count-1.0f) * scale, count};
+                    if (!compare_tiler_case(xTiler, yTiler, span, reporter)) {
+                        return false;
+                    }
+                }
+            }
+        }
+    }
+    return true;
+}
+
+template <typename XTiler, typename YTiler>
+static void test_tiler(skiatest::Reporter* reporter) {
+    std::array<int, 6> interestingSize {{1, 2, 3, 4, 5, 10}};
+    for (auto width : interestingSize) {
+        for (auto height : interestingSize) {
+            if (!compare_tiler_spans<XTiler, YTiler>(width, height, reporter)) { return; }
+        }
+    }
+}
+/*
+DEF_TEST(LBPStrategyClampTile, reporter) {
+#if 0
+    ClampStrategy tiler{SkSize::Make(1, 1)};
+    Span span{SkPoint::Make(0, -5), 1.0f, 2};
+    compare_tiler_case<ClampStrategy>(tiler, span, reporter);
+#else
+    test_tiler<XClampStrategy, YClampStrategy>(reporter);
+#endif
+}
+
+DEF_TEST(LBPStrategyRepeatTile, reporter) {
+#if 0
+    RepeatStrategy tiler{SkSize::Make(3, 1)};
+    Span span{SkPoint::Make(-5, -5), 20 * 2.1f, 100};
+    compare_tiler_case<RepeatStrategy>(tiler, span, reporter);
+#else
+    test_tiler<XRepeatStrategy, YRepeatStrategy>(reporter);
+#endif
+}
+*/
diff --git a/src/third_party/skia/tests/SkLiteDLTest.cpp b/src/third_party/skia/tests/SkLiteDLTest.cpp
new file mode 100644
index 0000000..514464a
--- /dev/null
+++ b/src/third_party/skia/tests/SkLiteDLTest.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "SkLiteDL.h"
+#include "SkLiteRecorder.h"
+
+DEF_TEST(SkLiteDL_basics, r) {
+    SkLiteDL p;
+    p.save();
+        p.clipRect(SkRect{2,3,4,5}, kIntersect_SkClipOp, true);
+        p.drawRect(SkRect{0,0,9,9}, SkPaint{});
+    p.restore();
+}
+
+DEF_TEST(SkLiteDL_unbalanced, r) {
+    SkLiteRecorder rec;
+    SkCanvas* c = &rec;
+
+    SkLiteDL p;
+    rec.reset(&p, {2,2,3,3});
+    c->save();
+        c->scale(2,2);
+        c->save();
+            c->translate(1,1);
+        // missing restore() but SkLiteDL::draw should balance it for us
+    c->restore();
+
+    // reinit the recorder so we can playback the original SkLiteDL
+    SkLiteDL p2;
+    rec.reset(&p2, {2,2,3,3});
+
+    REPORTER_ASSERT(r, 1 == rec.getSaveCount());
+    p.draw(c);
+    REPORTER_ASSERT(r, 1 == rec.getSaveCount());
+}
+
+DEF_TEST(SkLiteRecorder, r) {
+    SkLiteDL p;
+    SkLiteRecorder rec;
+    SkCanvas* c = &rec;
+
+    rec.reset(&p, {2,2,3,3});
+
+    c->save();
+        c->clipRect(SkRect{2,3,4,5}, kIntersect_SkClipOp, true);
+        c->drawRect(SkRect{0,0,9,9}, SkPaint{});
+    c->restore();
+}
diff --git a/src/third_party/skia/tests/SkNxTest.cpp b/src/third_party/skia/tests/SkNxTest.cpp
new file mode 100644
index 0000000..e3f3cd6
--- /dev/null
+++ b/src/third_party/skia/tests/SkNxTest.cpp
@@ -0,0 +1,360 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Sk4px.h"
+#include "SkNx.h"
+#include "SkRandom.h"
+#include "Test.h"
+
+template <int N>
+static void test_Nf(skiatest::Reporter* r) {
+
+    auto assert_nearly_eq = [&](float eps, const SkNx<N, float>& v,
+                                float a, float b, float c, float d) {
+        auto close = [=](float a, float b) { return fabsf(a-b) <= eps; };
+        float vals[4];
+        v.store(vals);
+        bool ok = close(vals[0], a) && close(vals[1], b)
+               && close(   v[0], a) && close(   v[1], b);
+        REPORTER_ASSERT(r, ok);
+        if (N == 4) {
+            ok = close(vals[2], c) && close(vals[3], d)
+              && close(   v[2], c) && close(   v[3], d);
+            REPORTER_ASSERT(r, ok);
+        }
+    };
+    auto assert_eq = [&](const SkNx<N, float>& v, float a, float b, float c, float d) {
+        return assert_nearly_eq(0, v, a,b,c,d);
+    };
+
+    float vals[] = {3, 4, 5, 6};
+    SkNx<N,float> a = SkNx<N,float>::Load(vals),
+                  b(a),
+                  c = a;
+    SkNx<N,float> d;
+    d = a;
+
+    assert_eq(a, 3, 4, 5, 6);
+    assert_eq(b, 3, 4, 5, 6);
+    assert_eq(c, 3, 4, 5, 6);
+    assert_eq(d, 3, 4, 5, 6);
+
+    assert_eq(a+b, 6, 8, 10, 12);
+    assert_eq(a*b, 9, 16, 25, 36);
+    assert_eq(a*b-b, 6, 12, 20, 30);
+    assert_eq((a*b).sqrt(), 3, 4, 5, 6);
+    assert_eq(a/b, 1, 1, 1, 1);
+    assert_eq(SkNx<N,float>(0)-a, -3, -4, -5, -6);
+
+    SkNx<N,float> fours(4);
+
+    assert_eq(fours.sqrt(), 2,2,2,2);
+    assert_nearly_eq(0.001f, fours.rsqrt(), 0.5, 0.5, 0.5, 0.5);
+
+    assert_nearly_eq(0.001f, fours.invert(), 0.25, 0.25, 0.25, 0.25);
+
+    assert_eq(SkNx<N,float>::Min(a, fours), 3, 4, 4, 4);
+    assert_eq(SkNx<N,float>::Max(a, fours), 4, 4, 5, 6);
+
+    // Test some comparisons.  This is not exhaustive.
+    REPORTER_ASSERT(r, (a == b).allTrue());
+    REPORTER_ASSERT(r, (a+b == a*b-b).anyTrue());
+    REPORTER_ASSERT(r, !(a+b == a*b-b).allTrue());
+    REPORTER_ASSERT(r, !(a+b == a*b).anyTrue());
+    REPORTER_ASSERT(r, !(a != b).anyTrue());
+    REPORTER_ASSERT(r, (a < fours).anyTrue());
+    REPORTER_ASSERT(r, (a <= fours).anyTrue());
+    REPORTER_ASSERT(r, !(a > fours).allTrue());
+    REPORTER_ASSERT(r, !(a >= fours).allTrue());
+}
+
+DEF_TEST(SkNf, r) {
+    test_Nf<2>(r);
+    test_Nf<4>(r);
+}
+
+template <int N, typename T>
+void test_Ni(skiatest::Reporter* r) {
+    auto assert_eq = [&](const SkNx<N,T>& v, T a, T b, T c, T d, T e, T f, T g, T h) {
+        T vals[8];
+        v.store(vals);
+
+        switch (N) {
+          case 8: REPORTER_ASSERT(r, vals[4] == e && vals[5] == f && vals[6] == g && vals[7] == h);
+          case 4: REPORTER_ASSERT(r, vals[2] == c && vals[3] == d);
+          case 2: REPORTER_ASSERT(r, vals[0] == a && vals[1] == b);
+        }
+        switch (N) {
+          case 8: REPORTER_ASSERT(r, v[4] == e && v[5] == f &&
+                                     v[6] == g && v[7] == h);
+          case 4: REPORTER_ASSERT(r, v[2] == c && v[3] == d);
+          case 2: REPORTER_ASSERT(r, v[0] == a && v[1] == b);
+        }
+    };
+
+    T vals[] = { 1,2,3,4,5,6,7,8 };
+    SkNx<N,T> a = SkNx<N,T>::Load(vals),
+              b(a),
+              c = a;
+    SkNx<N,T> d;
+    d = a;
+
+    assert_eq(a, 1,2,3,4,5,6,7,8);
+    assert_eq(b, 1,2,3,4,5,6,7,8);
+    assert_eq(c, 1,2,3,4,5,6,7,8);
+    assert_eq(d, 1,2,3,4,5,6,7,8);
+
+    assert_eq(a+a, 2,4,6,8,10,12,14,16);
+    assert_eq(a*a, 1,4,9,16,25,36,49,64);
+    assert_eq(a*a-a, 0,2,6,12,20,30,42,56);
+
+    assert_eq(a >> 2, 0,0,0,1,1,1,1,2);
+    assert_eq(a << 1, 2,4,6,8,10,12,14,16);
+
+    REPORTER_ASSERT(r, a[1] == 2);
+}
+
+DEF_TEST(SkNx, r) {
+    test_Ni<2, uint16_t>(r);
+    test_Ni<4, uint16_t>(r);
+    test_Ni<8, uint16_t>(r);
+
+    test_Ni<2, int>(r);
+    test_Ni<4, int>(r);
+    test_Ni<8, int>(r);
+}
+
+DEF_TEST(SkNi_min_lt, r) {
+    // Exhaustively check the 8x8 bit space.
+    for (int a = 0; a < (1<<8); a++) {
+    for (int b = 0; b < (1<<8); b++) {
+        Sk16b aw(a), bw(b);
+        REPORTER_ASSERT(r, Sk16b::Min(aw, bw)[0] == SkTMin(a, b));
+        REPORTER_ASSERT(r, !(aw < bw)[0] == !(a < b));
+    }}
+
+    // Exhausting the 16x16 bit space is kind of slow, so only do that in release builds.
+#ifdef SK_DEBUG
+    SkRandom rand;
+    for (int i = 0; i < (1<<16); i++) {
+        uint16_t a = rand.nextU() >> 16,
+                 b = rand.nextU() >> 16;
+        REPORTER_ASSERT(r, Sk16h::Min(Sk16h(a), Sk16h(b))[0] == SkTMin(a, b));
+    }
+#else
+    for (int a = 0; a < (1<<16); a++) {
+    for (int b = 0; b < (1<<16); b++) {
+        REPORTER_ASSERT(r, Sk16h::Min(Sk16h(a), Sk16h(b))[0] == SkTMin(a, b));
+    }}
+#endif
+}
+
+DEF_TEST(SkNi_saturatedAdd, r) {
+    for (int a = 0; a < (1<<8); a++) {
+    for (int b = 0; b < (1<<8); b++) {
+        int exact = a+b;
+        if (exact > 255) { exact = 255; }
+        if (exact <   0) { exact =   0; }
+
+        REPORTER_ASSERT(r, Sk16b(a).saturatedAdd(Sk16b(b))[0] == exact);
+    }
+    }
+}
+
+DEF_TEST(Sk4px_muldiv255round, r) {
+    for (int a = 0; a < (1<<8); a++) {
+    for (int b = 0; b < (1<<8); b++) {
+        int exact = (a*b+127)/255;
+
+        // Duplicate a and b 16x each.
+        auto av = Sk4px::DupAlpha(a),
+             bv = Sk4px::DupAlpha(b);
+
+        // This way should always be exactly correct.
+        int correct = (av * bv).div255()[0];
+        REPORTER_ASSERT(r, correct == exact);
+
+        // We're a bit more flexible on this method: correct for 0 or 255, otherwise off by <=1.
+        int fast = av.approxMulDiv255(bv)[0];
+        REPORTER_ASSERT(r, fast-exact >= -1 && fast-exact <= 1);
+        if (a == 0 || a == 255 || b == 0 || b == 255) {
+            REPORTER_ASSERT(r, fast == exact);
+        }
+    }
+    }
+}
+
+DEF_TEST(Sk4px_widening, r) {
+    SkPMColor colors[] = {
+        SkPreMultiplyColor(0xff00ff00),
+        SkPreMultiplyColor(0x40008000),
+        SkPreMultiplyColor(0x7f020406),
+        SkPreMultiplyColor(0x00000000),
+    };
+    auto packed = Sk4px::Load4(colors);
+
+    auto wideLo = packed.widenLo(),
+         wideHi = packed.widenHi(),
+         wideLoHi    = packed.widenLoHi(),
+         wideLoHiAlt = wideLo + wideHi;
+    REPORTER_ASSERT(r, 0 == memcmp(&wideLoHi, &wideLoHiAlt, sizeof(wideLoHi)));
+}
+
+DEF_TEST(SkNx_abs, r) {
+    auto fs = Sk4f(0.0f, -0.0f, 2.0f, -4.0f).abs();
+    REPORTER_ASSERT(r, fs[0] == 0.0f);
+    REPORTER_ASSERT(r, fs[1] == 0.0f);
+    REPORTER_ASSERT(r, fs[2] == 2.0f);
+    REPORTER_ASSERT(r, fs[3] == 4.0f);
+}
+
+DEF_TEST(Sk4i_abs, r) {
+    auto is = Sk4i(0, -1, 2, -2147483647).abs();
+    REPORTER_ASSERT(r, is[0] == 0);
+    REPORTER_ASSERT(r, is[1] == 1);
+    REPORTER_ASSERT(r, is[2] == 2);
+    REPORTER_ASSERT(r, is[3] == 2147483647);
+}
+
+DEF_TEST(Sk4i_minmax, r) {
+    auto a = Sk4i(0, 2, 4, 6);
+    auto b = Sk4i(1, 1, 3, 7);
+    auto min = Sk4i::Min(a, b);
+    auto max = Sk4i::Max(a, b);
+    for(int i = 0; i < 4; ++i) {
+        REPORTER_ASSERT(r, min[i] == SkTMin(a[i], b[i]));
+        REPORTER_ASSERT(r, max[i] == SkTMax(a[i], b[i]));
+    }
+}
+
+DEF_TEST(SkNx_floor, r) {
+    auto fs = Sk4f(0.4f, -0.4f, 0.6f, -0.6f).floor();
+    REPORTER_ASSERT(r, fs[0] ==  0.0f);
+    REPORTER_ASSERT(r, fs[1] == -1.0f);
+    REPORTER_ASSERT(r, fs[2] ==  0.0f);
+    REPORTER_ASSERT(r, fs[3] == -1.0f);
+}
+
+DEF_TEST(SkNx_shuffle, r) {
+    Sk4f f4(0,10,20,30);
+
+    Sk2f f2 = SkNx_shuffle<2,1>(f4);
+    REPORTER_ASSERT(r, f2[0] == 20);
+    REPORTER_ASSERT(r, f2[1] == 10);
+
+    f4 = SkNx_shuffle<0,1,1,0>(f2);
+    REPORTER_ASSERT(r, f4[0] == 20);
+    REPORTER_ASSERT(r, f4[1] == 10);
+    REPORTER_ASSERT(r, f4[2] == 10);
+    REPORTER_ASSERT(r, f4[3] == 20);
+}
+
+DEF_TEST(SkNx_int_float, r) {
+    Sk4f f(-2.3f, 1.0f, 0.45f, 0.6f);
+
+    Sk4i i = SkNx_cast<int>(f);
+    REPORTER_ASSERT(r, i[0] == -2);
+    REPORTER_ASSERT(r, i[1] ==  1);
+    REPORTER_ASSERT(r, i[2] ==  0);
+    REPORTER_ASSERT(r, i[3] ==  0);
+
+    f = SkNx_cast<float>(i);
+    REPORTER_ASSERT(r, f[0] == -2.0f);
+    REPORTER_ASSERT(r, f[1] ==  1.0f);
+    REPORTER_ASSERT(r, f[2] ==  0.0f);
+    REPORTER_ASSERT(r, f[3] ==  0.0f);
+}
+
+#include "SkRandom.h"
+
+DEF_TEST(SkNx_u16_float, r) {
+    {
+        // u16 --> float
+        auto h4 = Sk4h(15, 17, 257, 65535);
+        auto f4 = SkNx_cast<float>(h4);
+        REPORTER_ASSERT(r, f4[0] == 15.0f);
+        REPORTER_ASSERT(r, f4[1] == 17.0f);
+        REPORTER_ASSERT(r, f4[2] == 257.0f);
+        REPORTER_ASSERT(r, f4[3] == 65535.0f);
+    }
+    {
+        // float -> u16
+        auto f4 = Sk4f(15, 17, 257, 65535);
+        auto h4 = SkNx_cast<uint16_t>(f4);
+        REPORTER_ASSERT(r, h4[0] == 15);
+        REPORTER_ASSERT(r, h4[1] == 17);
+        REPORTER_ASSERT(r, h4[2] == 257);
+        REPORTER_ASSERT(r, h4[3] == 65535);
+    }
+
+    // starting with any u16 value, we should be able to have a perfect round-trip in/out of floats
+    //
+    SkRandom rand;
+    for (int i = 0; i < 10000; ++i) {
+        const uint16_t s16[4] {
+            (uint16_t)rand.nextU16(), (uint16_t)rand.nextU16(),
+            (uint16_t)rand.nextU16(), (uint16_t)rand.nextU16(),
+        };
+        auto u4_0 = Sk4h::Load(s16);
+        auto f4 = SkNx_cast<float>(u4_0);
+        auto u4_1 = SkNx_cast<uint16_t>(f4);
+        uint16_t d16[4];
+        u4_1.store(d16);
+        REPORTER_ASSERT(r, !memcmp(s16, d16, sizeof(s16)));
+    }
+}
+
+// The SSE2 implementation of SkNx_cast<uint16_t>(Sk4i) is non-trivial, so worth a test.
+DEF_TEST(SkNx_int_u16, r) {
+    // These are pretty hard to get wrong.
+    for (int i = 0; i <= 0x7fff; i++) {
+        uint16_t expected = (uint16_t)i;
+        uint16_t actual = SkNx_cast<uint16_t>(Sk4i(i))[0];
+
+        REPORTER_ASSERT(r, expected == actual);
+    }
+
+    // A naive implementation with _mm_packs_epi32 would succeed up to 0x7fff but fail here:
+    for (int i = 0x8000; (1) && i <= 0xffff; i++) {
+        uint16_t expected = (uint16_t)i;
+        uint16_t actual = SkNx_cast<uint16_t>(Sk4i(i))[0];
+
+        REPORTER_ASSERT(r, expected == actual);
+    }
+}
+
+DEF_TEST(SkNx_4fLoad4Store4, r) {
+    float src[] = {
+         0.0f,  1.0f,  2.0f,  3.0f,
+         4.0f,  5.0f,  6.0f,  7.0f,
+         8.0f,  9.0f, 10.0f, 11.0f,
+        12.0f, 13.0f, 14.0f, 15.0f
+    };
+
+    Sk4f a, b, c, d;
+    Sk4f::Load4(src, &a, &b, &c, &d);
+    REPORTER_ASSERT(r,  0.0f == a[0]);
+    REPORTER_ASSERT(r,  4.0f == a[1]);
+    REPORTER_ASSERT(r,  8.0f == a[2]);
+    REPORTER_ASSERT(r, 12.0f == a[3]);
+    REPORTER_ASSERT(r,  1.0f == b[0]);
+    REPORTER_ASSERT(r,  5.0f == b[1]);
+    REPORTER_ASSERT(r,  9.0f == b[2]);
+    REPORTER_ASSERT(r, 13.0f == b[3]);
+    REPORTER_ASSERT(r,  2.0f == c[0]);
+    REPORTER_ASSERT(r,  6.0f == c[1]);
+    REPORTER_ASSERT(r, 10.0f == c[2]);
+    REPORTER_ASSERT(r, 14.0f == c[3]);
+    REPORTER_ASSERT(r,  3.0f == d[0]);
+    REPORTER_ASSERT(r,  7.0f == d[1]);
+    REPORTER_ASSERT(r, 11.0f == d[2]);
+    REPORTER_ASSERT(r, 15.0f == d[3]);
+
+    float dst[16];
+    Sk4f::Store4(dst, a, b, c, d);
+    REPORTER_ASSERT(r, 0 == memcmp(dst, src, 16 * sizeof(float)));
+}
diff --git a/src/third_party/skia/tests/SkPEGTest.cpp b/src/third_party/skia/tests/SkPEGTest.cpp
new file mode 100644
index 0000000..6773c1b
--- /dev/null
+++ b/src/third_party/skia/tests/SkPEGTest.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#if defined(SK_XML)
+#include "SkPEG.h"
+
+using namespace skpeg;
+
+namespace {
+
+struct Alpha {
+    using V = char;
+    using MatchT = MatchResult<V>;
+
+    static MatchT Match(const char* in) {
+        static constexpr unsigned kAlphaRange = 'z' - 'a';
+        return static_cast<unsigned>(*in - 'a') <= kAlphaRange
+            || static_cast<unsigned>(*in - 'A') <= kAlphaRange
+            ? MatchT(in + 1, *in)
+            : nullptr;
+    }
+};
+
+struct Digit {
+    using V = uint8_t;
+    using MatchT = MatchResult<V>;
+
+    static MatchT Match(const char* in) {
+        static constexpr unsigned kDigitRange = '9' - '0';
+        return static_cast<unsigned>(*in - '0') <= kDigitRange
+            ? MatchT(in + 1, SkTo<uint8_t>(*in - '0'))
+            : nullptr;
+    }
+};
+
+void test_EOS(skiatest::Reporter* r) {
+    static const struct {
+        const char* fInput;
+        bool        fMatch;
+    } gTests[] = {
+        { ""   , true  },
+        { " "  , false },
+        { "\0" , true  },
+        { "foo", false },
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
+        const auto match = EOS::Match(gTests[i].fInput);
+        REPORTER_ASSERT(r, match == gTests[i].fMatch);
+        REPORTER_ASSERT(r, match.fNext == (match ? gTests[i].fInput : nullptr));
+    }
+}
+
+void test_LIT(skiatest::Reporter* r) {
+    static const struct {
+        const char* fInput;
+        bool        fMatch;
+    } gTests[] = {
+        { ""  , false },
+        { " " , false },
+        { "x" , false },
+        { "X" , true  },
+        { "xX", false },
+        { "Xx", true  },
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
+        const auto match = LIT<'X'>::Match(gTests[i].fInput);
+        REPORTER_ASSERT(r, match == gTests[i].fMatch);
+        REPORTER_ASSERT(r, match.fNext == (match ? gTests[i].fInput + 1 : nullptr));
+    }
+
+    REPORTER_ASSERT(r, !(LIT<'F', 'o', 'o'>::Match("")));
+    REPORTER_ASSERT(r, !(LIT<'F', 'o', 'o'>::Match("Fo")));
+    REPORTER_ASSERT(r, !(LIT<'F', 'o', 'o'>::Match("FoO")));
+    REPORTER_ASSERT(r,  (LIT<'F', 'o', 'o'>::Match("Foo")));
+    REPORTER_ASSERT(r,  (LIT<'F', 'o', 'o'>::Match("Foobar")));
+}
+
+void test_Alpha(skiatest::Reporter* r) {
+    static const struct {
+        const char* fInput;
+        bool        fMatch;
+        char        fMatchValue;
+    } gTests[] = {
+        { ""  , false,  0  },
+        { "\r", false,  0  },
+        { "\n", false,  0  },
+        { "\t", false,  0  },
+        { "0" , false,  0  },
+        { "9" , false,  0  },
+        { "a" , true , 'a' },
+        { "a" , true , 'a' },
+        { "z" , true , 'z' },
+        { "A" , true , 'A' },
+        { "Z" , true , 'Z' },
+        { "az", true , 'a' },
+        { "a0", true , 'a' },
+        { "0a", false,  0  },
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
+        const auto match = Alpha::Match(gTests[i].fInput);
+        REPORTER_ASSERT(r, match == gTests[i].fMatch);
+        REPORTER_ASSERT(r, match.fNext == (match ? gTests[i].fInput + 1 : nullptr));
+        if (match) {
+            REPORTER_ASSERT(r, *match == gTests[i].fMatchValue);
+        }
+    }
+}
+
+void test_Digit(skiatest::Reporter* r) {
+    static const struct {
+        const char* fInput;
+        bool        fMatch;
+        uint8_t     fMatchValue;
+    } gTests[] = {
+        { ""   , false, 0 },
+        { "/"  , false, 0 },
+        { ":"  , false, 0 },
+        { "x"  , false, 0 },
+        { "x0" , false, 0 },
+        { "0"  , true , 0 },
+        { "1x" , true , 1 },
+        { "9 a", true , 9 },
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
+        const auto match = Digit::Match(gTests[i].fInput);
+        REPORTER_ASSERT(r, match == gTests[i].fMatch);
+        REPORTER_ASSERT(r, match.fNext == (match ? gTests[i].fInput + 1 : nullptr));
+        if (match) {
+            REPORTER_ASSERT(r, *match == gTests[i].fMatchValue);
+        }
+    }
+}
+
+void test_Opt(skiatest::Reporter* r) {
+    static const struct {
+        const char* fInput;
+        bool        fMatch;
+    } gTests[] = {
+        { ""       , false },
+        { "fo"     , false },
+        { " foo"   , false },
+        { "foo"    , true },
+        { "foobar" , true },
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
+        const auto m = Opt<LIT<'f', 'o', 'o'>>::Match(gTests[i].fInput);
+        REPORTER_ASSERT(r, m);
+        REPORTER_ASSERT(r, m->fValue.isValid() == gTests[i].fMatch);
+    }
+}
+
+void test_Seq(skiatest::Reporter* r) {
+    REPORTER_ASSERT(r,  (Seq<LIT<'X'>, EOS>::Match("X")));
+    REPORTER_ASSERT(r, !(Seq<LIT<'X'>, EOS>::Match("x")));
+    REPORTER_ASSERT(r, !(Seq<LIT<'X'>, EOS>::Match("xX")));
+    REPORTER_ASSERT(r, !(Seq<LIT<'X'>, EOS>::Match("XX")));
+    REPORTER_ASSERT(r,  (Seq<LIT<'X'>, Seq<LIT<'X'>, EOS>>::Match("XX")));
+    REPORTER_ASSERT(r,  (Seq<LIT<'X'>, Seq<LIT<'X'>, EOS>>::Match("XX")));
+
+    REPORTER_ASSERT(r, !(Seq<LIT<'F', 'o', 'o'>, EOS>::Match("FooBar")));
+    REPORTER_ASSERT(r,  (Seq<LIT<'F', 'o', 'o'>, EOS>::Match("Foo")));
+
+    {
+        const auto m = Seq<LIT<'x'>, Digit>::Match("x5");
+        REPORTER_ASSERT(r, m);
+        REPORTER_ASSERT(r, m->get<1>() == 5);
+    }
+    {
+        const auto m = Seq<Digit, Digit>::Match("42");
+        REPORTER_ASSERT(r, m);
+        REPORTER_ASSERT(r, m->get<0>() == 4);
+        REPORTER_ASSERT(r, m->get<1>() == 2);
+    }
+}
+
+void test_Choice(skiatest::Reporter* r) {
+    REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match("")));
+    REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match("\t")));
+    REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match(" ")));
+    REPORTER_ASSERT(r,  (Choice<Digit,Alpha>::Match("a")));
+    REPORTER_ASSERT(r,  (Choice<Digit,Alpha>::Match("3")));
+    REPORTER_ASSERT(r,  (Choice<Digit,Alpha>::Match("a ")));
+    REPORTER_ASSERT(r,  (Choice<Digit,Alpha>::Match("3 ")));
+    REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match(" a ")));
+    REPORTER_ASSERT(r, !(Choice<Digit,Alpha>::Match(" 3 ")));
+
+    {
+        const auto m = Choice<Alpha, Digit>::Match("x");
+        REPORTER_ASSERT(r,  m);
+        REPORTER_ASSERT(r,  m->v1.isValid());
+        REPORTER_ASSERT(r, !m->v2.isValid());
+        REPORTER_ASSERT(r, *m->v1.get() == 'x');
+    }
+
+    {
+        const auto m = Choice<Alpha, Digit>::Match("7");
+        REPORTER_ASSERT(r,  m);
+        REPORTER_ASSERT(r, !m->v1.isValid());
+        REPORTER_ASSERT(r,  m->v2.isValid());
+        REPORTER_ASSERT(r, *m->v2.get() == 7);
+    }
+}
+
+void test_AnySome(skiatest::Reporter* r) {
+    static const struct {
+        const char* fInput;
+        int         fCount;
+    } gTests[] = {
+        { ""      , 0 },
+        { "fo"    , 0 },
+        { "Foo"   , 0 },
+        { "foo"   , 1 },
+        { "foofoo", 2 },
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
+        const auto matchAny = Any<LIT<'f', 'o', 'o'>>::Match(gTests[i].fInput);
+        REPORTER_ASSERT(r, matchAny);
+        REPORTER_ASSERT(r, matchAny->fValues.count() == gTests[i].fCount);
+
+        const auto matchSome = Some<LIT<'f', 'o', 'o'>>::Match(gTests[i].fInput);
+        REPORTER_ASSERT(r,  matchSome == (gTests[i].fCount > 0));
+        REPORTER_ASSERT(r, !matchSome ||
+                            matchSome->get<1>().fValues.count() == gTests[i].fCount - 1);
+    }
+
+    {
+        const auto m = Any<Digit>::Match("0123456789foo");
+        REPORTER_ASSERT(r, m);
+        REPORTER_ASSERT(r, m->fValues.count() == 10);
+        for (int i = 0; i < m->fValues.count(); ++i) {
+            REPORTER_ASSERT(r, m->fValues[i] == i);
+        }
+    }
+}
+
+void test_Complex(skiatest::Reporter* r) {
+    // [0-9]+(,[0-9]+)?$
+    using P0 =
+        Seq<
+          Some<Digit>,
+          Opt<Seq<
+            LIT<','>,
+            Some<Digit>>>,
+          EOS>;
+
+    REPORTER_ASSERT(r, !P0::Match(""));
+    REPORTER_ASSERT(r, !P0::Match(","));
+    REPORTER_ASSERT(r, !P0::Match("1,"));
+    REPORTER_ASSERT(r, !P0::Match(",1"));
+    REPORTER_ASSERT(r,  P0::Match("1"));
+    REPORTER_ASSERT(r,  P0::Match("1,2"));
+    REPORTER_ASSERT(r, !P0::Match("1,2 "));
+    REPORTER_ASSERT(r,  P0::Match("123,456"));
+
+    // [ ]*[Ff]oo([Bb]ar)+[Bb]az[ ]*$
+    using P1 =
+        Seq<
+          Any<LIT<' '>>,
+          Choice<LIT<'F'>, LIT<'f'>>,
+          LIT<'o', 'o'>,
+          Some<Seq<
+            Choice<LIT<'B'>, LIT<'b'>>,
+            LIT<'a', 'r'>>>,
+          Choice<LIT<'B'>, LIT<'b'>>,
+          LIT<'a', 'z'>,
+          Any<LIT<' '>>,
+          EOS>;
+
+    REPORTER_ASSERT(r, !P1::Match(""));
+    REPORTER_ASSERT(r, !P1::Match("FooBar"));
+    REPORTER_ASSERT(r, !P1::Match("FooBaz"));
+    REPORTER_ASSERT(r,  P1::Match("FooBarBaz"));
+    REPORTER_ASSERT(r,  P1::Match("foobarbaz"));
+    REPORTER_ASSERT(r,  P1::Match("  FooBarbaz     "));
+    REPORTER_ASSERT(r,  P1::Match(" FooBarbarbarBaz "));
+}
+
+} // anonymous ns
+
+DEF_TEST(SkPEG, r) {
+    test_EOS(r);
+    test_LIT(r);
+    test_Alpha(r);
+    test_Digit(r);
+    test_Opt(r);
+    test_Seq(r);
+    test_Choice(r);
+    test_AnySome(r);
+    test_Complex(r);
+}
+
+#endif // SK_XML
diff --git a/src/third_party/skia/tests/SkRasterPipelineTest.cpp b/src/third_party/skia/tests/SkRasterPipelineTest.cpp
new file mode 100644
index 0000000..8edff7e
--- /dev/null
+++ b/src/third_party/skia/tests/SkRasterPipelineTest.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "SkHalf.h"
+#include "SkRasterPipeline.h"
+#include "../src/jumper/SkJumper.h"
+
+DEF_TEST(SkRasterPipeline, r) {
+    // Build and run a simple pipeline to exercise SkRasterPipeline,
+    // drawing 50% transparent blue over opaque red in half-floats.
+    uint64_t red  = 0x3c00000000003c00ull,
+             blue = 0x3800380000000000ull,
+             result;
+
+    void* load_s_ctx = &blue;
+    void* load_d_ctx = &red;
+    void* store_ctx  = &result;
+
+    SkRasterPipeline_<256> p;
+    p.append(SkRasterPipeline::load_f16,     &load_s_ctx);
+    p.append(SkRasterPipeline::load_f16_dst, &load_d_ctx);
+    p.append(SkRasterPipeline::srcover);
+    p.append(SkRasterPipeline::store_f16, &store_ctx);
+    p.run(0,0,1);
+
+    // We should see half-intensity magenta.
+    REPORTER_ASSERT(r, ((result >>  0) & 0xffff) == 0x3800);
+    REPORTER_ASSERT(r, ((result >> 16) & 0xffff) == 0x0000);
+    REPORTER_ASSERT(r, ((result >> 32) & 0xffff) == 0x3800);
+    REPORTER_ASSERT(r, ((result >> 48) & 0xffff) == 0x3c00);
+}
+
+DEF_TEST(SkRasterPipeline_empty, r) {
+    // No asserts... just a test that this is safe to run.
+    SkRasterPipeline_<256> p;
+    p.run(0,0,20);
+}
+
+DEF_TEST(SkRasterPipeline_nonsense, r) {
+    // No asserts... just a test that this is safe to run and terminates.
+    // srcover() calls st->next(); this makes sure we've always got something there to call.
+    SkRasterPipeline_<256> p;
+    p.append(SkRasterPipeline::srcover);
+    p.run(0,0,20);
+}
+
+DEF_TEST(SkRasterPipeline_JIT, r) {
+    // This tests a couple odd corners that a JIT backend can stumble over.
+
+    uint32_t buf[72] = {
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
+        13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+    };
+
+    const uint32_t* src = buf +  0;
+    uint32_t*       dst = buf + 36;
+
+    // Copy buf[x] to buf[x+36] for x in [15,35).
+    SkRasterPipeline_<256> p;
+    p.append(SkRasterPipeline:: load_8888, &src);
+    p.append(SkRasterPipeline::store_8888, &dst);
+    p.run(15,0, 20);
+
+    for (int i = 0; i < 36; i++) {
+        if (i < 15 || i == 35) {
+            REPORTER_ASSERT(r, dst[i] == 0);
+        } else {
+            REPORTER_ASSERT(r, dst[i] == (uint32_t)(i - 11));
+        }
+    }
+}
+
+static uint16_t h(float f) {
+    // Remember, a float is 1-8-23 (sign-exponent-mantissa) with 127 exponent bias.
+    uint32_t sem;
+    memcpy(&sem, &f, sizeof(sem));
+    uint32_t s  = sem & 0x80000000,
+             em = sem ^ s;
+
+    // Convert to 1-5-10 half with 15 bias, flushing denorm halfs (including zero) to zero.
+    auto denorm = (int32_t)em < 0x38800000;  // I32 comparison is often quicker, and always safe
+    // here.
+    return denorm ? SkTo<uint16_t>(0)
+                  : SkTo<uint16_t>((s>>16) + (em>>13) - ((127-15)<<10));
+}
+
+static uint16_t n(uint16_t x) {
+    return (x<<8) | (x>>8);
+}
+
+static float a(uint16_t x) {
+    return (1/65535.0f) * x;
+}
+
+DEF_TEST(SkRasterPipeline_tail, r) {
+    {
+        float data[][4] = {
+            {00, 01, 02, 03},
+            {10, 11, 12, 13},
+            {20, 21, 22, 23},
+            {30, 31, 32, 33},
+        };
+
+        float buffer[4][4];
+        float* src = &data[0][0];
+        float* dst = &buffer[0][0];
+
+        for (unsigned i = 1; i <= 4; i++) {
+            memset(buffer, 0xff, sizeof(buffer));
+            SkRasterPipeline_<256> p;
+            p.append(SkRasterPipeline::load_f32, &src);
+            p.append(SkRasterPipeline::store_f32, &dst);
+            p.run(0,0, i);
+            for (unsigned j = 0; j < i; j++) {
+                for (unsigned k = 0; k < 4; k++) {
+                    if (buffer[j][k] != data[j][k]) {
+                        ERRORF(r, "(%u, %u) - a: %g r: %g\n", j, k, data[j][k], buffer[j][k]);
+                    }
+                }
+            }
+            for (int j = i; j < 4; j++) {
+                for (auto f : buffer[j]) {
+                    REPORTER_ASSERT(r, SkScalarIsNaN(f));
+                }
+            }
+        }
+    }
+
+    {
+        uint16_t data[][4] = {
+            {h(00), h(01), h(02), h(03)},
+            {h(10), h(11), h(12), h(13)},
+            {h(20), h(21), h(22), h(23)},
+            {h(30), h(31), h(32), h(33)},
+        };
+        uint16_t buffer[4][4];
+        uint16_t* src = &data[0][0];
+        uint16_t* dst = &buffer[0][0];
+
+        for (unsigned i = 1; i <= 4; i++) {
+            memset(buffer, 0xff, sizeof(buffer));
+            SkRasterPipeline_<256> p;
+            p.append(SkRasterPipeline::load_f16, &src);
+            p.append(SkRasterPipeline::store_f16, &dst);
+            p.run(0,0, i);
+            for (unsigned j = 0; j < i; j++) {
+                REPORTER_ASSERT(r,
+                                !memcmp(&data[j][0], &buffer[j][0], sizeof(buffer[j])));
+            }
+            for (int j = i; j < 4; j++) {
+                for (auto f : buffer[j]) {
+                    REPORTER_ASSERT(r, f == 0xffff);
+                }
+            }
+        }
+    }
+
+    {
+        uint16_t data[][3] = {
+            {n(00), n(01), n(02)},
+            {n(10), n(11), n(12)},
+            {n(20), n(21), n(22)},
+            {n(30), n(31), n(32)}
+        };
+
+        float answer[][4] = {
+            {a(00), a(01), a(02), 1.0f},
+            {a(10), a(11), a(12), 1.0f},
+            {a(20), a(21), a(22), 1.0f},
+            {a(30), a(31), a(32), 1.0f}
+        };
+
+        float buffer[4][4];
+        uint16_t* src = &data[0][0];
+        float* dst = &buffer[0][0];
+
+        for (unsigned i = 1; i <= 4; i++) {
+            memset(buffer, 0xff, sizeof(buffer));
+            SkRasterPipeline_<256> p;
+            p.append(SkRasterPipeline::load_rgb_u16_be, &src);
+            p.append(SkRasterPipeline::store_f32, &dst);
+            p.run(0,0, i);
+            for (unsigned j = 0; j < i; j++) {
+                for (unsigned k = 0; k < 4; k++) {
+                    if (buffer[j][k] != answer[j][k]) {
+                        ERRORF(r, "(%u, %u) - a: %g r: %g\n", j, k, answer[j][k], buffer[j][k]);
+                    }
+                }
+            }
+            for (int j = i; j < 4; j++) {
+                for (auto f : buffer[j]) {
+                    REPORTER_ASSERT(r, SkScalarIsNaN(f));
+                }
+            }
+        }
+    }
+}
+
+DEF_TEST(SkRasterPipeline_lowp, r) {
+    uint32_t rgba[64];
+    for (int i = 0; i < 64; i++) {
+        rgba[i] = (4*i+0) << 0
+                | (4*i+1) << 8
+                | (4*i+2) << 16
+                | (4*i+3) << 24;
+    }
+
+    void* ptr = rgba;
+
+    SkRasterPipeline_<256> p;
+    p.append(SkRasterPipeline::load_bgra,  &ptr);
+    p.append(SkRasterPipeline::store_8888, &ptr);
+    p.run(0,0,64);
+
+    for (int i = 0; i < 64; i++) {
+        uint32_t want = (4*i+0) << 16
+                      | (4*i+1) << 8
+                      | (4*i+2) << 0
+                      | (4*i+3) << 24;
+        if (rgba[i] != want) {
+            ERRORF(r, "got %08x, want %08x\n", rgba[i], want);
+        }
+    }
+}
+
+DEF_TEST(SkRasterPipeline_2d, r) {
+    uint32_t rgba[2*2] = {0,0,0,0};
+
+    SkSTArenaAlloc<256> alloc;
+    SkRasterPipeline p(&alloc);
+
+    // Splat out the (2d) dst coordinates: (0.5,0.5), (1.5,0.5), (0.5,1.5), (1.5,1.5).
+    p.append(SkRasterPipeline::seed_shader);
+
+    // Scale down to [0,1] range to write out as bytes.
+    p.append_matrix(&alloc, SkMatrix::Concat(SkMatrix::MakeScale(0.5f),
+                                             SkMatrix::MakeTrans(-0.5f, -0.5f)));
+
+    // Write out to rgba, with row stride = 2 pixels.
+    SkJumper_MemoryCtx ctx = { rgba, 2 };
+    p.append(SkRasterPipeline::store_8888_2d, &ctx);
+
+    p.run_2d(0,0, 2,2);
+
+    REPORTER_ASSERT(r, ((rgba[0] >> 0) & 0xff) ==   0);
+    REPORTER_ASSERT(r, ((rgba[1] >> 0) & 0xff) == 128);
+    REPORTER_ASSERT(r, ((rgba[2] >> 0) & 0xff) ==   0);
+    REPORTER_ASSERT(r, ((rgba[3] >> 0) & 0xff) == 128);
+
+    REPORTER_ASSERT(r, ((rgba[0] >> 8) & 0xff) ==   0);
+    REPORTER_ASSERT(r, ((rgba[1] >> 8) & 0xff) ==   0);
+    REPORTER_ASSERT(r, ((rgba[2] >> 8) & 0xff) == 128);
+    REPORTER_ASSERT(r, ((rgba[3] >> 8) & 0xff) == 128);
+}
diff --git a/src/third_party/skia/tests/SkResourceCacheTest.cpp b/src/third_party/skia/tests/SkResourceCacheTest.cpp
index f13476a..5f8770d 100644
--- a/src/third_party/skia/tests/SkResourceCacheTest.cpp
+++ b/src/third_party/skia/tests/SkResourceCacheTest.cpp
@@ -10,159 +10,280 @@
 #include "SkCanvas.h"
 #include "SkDiscardableMemoryPool.h"
 #include "SkGraphics.h"
+#include "SkMakeUnique.h"
+#include "SkMipMap.h"
+#include "SkPicture.h"
+#include "SkPictureRecorder.h"
 #include "SkResourceCache.h"
-
-static const int kCanvasSize = 1;
-static const int kBitmapSize = 16;
-static const int kScale = 8;
-
-static bool is_in_scaled_image_cache(const SkBitmap& orig,
-                                     SkScalar xScale,
-                                     SkScalar yScale) {
-    SkBitmap scaled;
-    float roundedImageWidth = SkScalarRoundToScalar(orig.width() * xScale);
-    float roundedImageHeight = SkScalarRoundToScalar(orig.height() * xScale);
-    return SkBitmapCache::Find(orig, roundedImageWidth, roundedImageHeight, &scaled);
-}
-
-// Draw a scaled bitmap, then return true iff it has been cached.
-static bool test_scaled_image_cache_useage() {
-    SkAutoTUnref<SkCanvas> canvas(
-            SkCanvas::NewRasterN32(kCanvasSize, kCanvasSize));
-    SkBitmap bitmap;
-    bitmap.allocN32Pixels(kBitmapSize, kBitmapSize);
-    bitmap.eraseColor(0xFFFFFFFF);
-    SkScalar scale = SkIntToScalar(kScale);
-    SkScalar scaledSize = SkIntToScalar(kBitmapSize) * scale;
-    canvas->clipRect(SkRect::MakeLTRB(0, 0, scaledSize, scaledSize));
-    SkPaint paint;
-    paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
-
-    canvas->drawBitmapRect(bitmap,
-                           SkRect::MakeLTRB(0, 0, scaledSize, scaledSize),
-                           &paint);
-
-    return is_in_scaled_image_cache(bitmap, scale, scale);
-}
-
-// http://crbug.com/389439
-DEF_TEST(ResourceCache_SingleAllocationByteLimit, reporter) {
-    size_t originalByteLimit = SkGraphics::GetResourceCacheTotalByteLimit();
-    size_t originalAllocationLimit =
-        SkGraphics::GetResourceCacheSingleAllocationByteLimit();
-
-    size_t size = kBitmapSize * kScale * kBitmapSize * kScale
-        * SkColorTypeBytesPerPixel(kN32_SkColorType);
-
-    SkGraphics::SetResourceCacheTotalByteLimit(0);  // clear cache
-    SkGraphics::SetResourceCacheTotalByteLimit(2 * size);
-    SkGraphics::SetResourceCacheSingleAllocationByteLimit(0);  // No limit
-
-    REPORTER_ASSERT(reporter, test_scaled_image_cache_useage());
-
-    SkGraphics::SetResourceCacheTotalByteLimit(0);  // clear cache
-    SkGraphics::SetResourceCacheTotalByteLimit(2 * size);
-    SkGraphics::SetResourceCacheSingleAllocationByteLimit(size * 2);  // big enough
-
-    REPORTER_ASSERT(reporter, test_scaled_image_cache_useage());
-
-    SkGraphics::SetResourceCacheTotalByteLimit(0);  // clear cache
-    SkGraphics::SetResourceCacheTotalByteLimit(2 * size);
-    SkGraphics::SetResourceCacheSingleAllocationByteLimit(size / 2);  // too small
-
-    REPORTER_ASSERT(reporter, !test_scaled_image_cache_useage());
-
-    SkGraphics::SetResourceCacheSingleAllocationByteLimit(originalAllocationLimit);
-    SkGraphics::SetResourceCacheTotalByteLimit(originalByteLimit);
-}
+#include "SkSurface.h"
 
 ////////////////////////////////////////////////////////////////////////////////////////
 
-static void make_bitmap(SkBitmap* bitmap, const SkImageInfo& info, SkBitmap::Allocator* allocator) {
-    if (allocator) {
-        bitmap->setInfo(info);
-        allocator->allocPixelRef(bitmap, 0);
-    } else {
-        bitmap->allocPixels(info);
+enum LockedState {
+    kNotLocked,
+    kLocked,
+};
+
+enum CachedState {
+    kNotInCache,
+    kInCache,
+};
+
+static void check_data(skiatest::Reporter* reporter, const SkCachedData* data,
+                       int refcnt, CachedState cacheState, LockedState lockedState) {
+    REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt);
+    REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState));
+    bool isLocked = (data->data() != nullptr);
+    REPORTER_ASSERT(reporter, isLocked == (lockedState == kLocked));
+}
+
+static void test_mipmapcache(skiatest::Reporter* reporter, SkResourceCache* cache) {
+    cache->purgeAll();
+
+    SkBitmap src;
+    src.allocN32Pixels(5, 5);
+    src.setImmutable();
+
+    const SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
+
+    const SkMipMap* mipmap = SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(src), colorMode,
+                                                       cache);
+    REPORTER_ASSERT(reporter, nullptr == mipmap);
+
+    mipmap = SkMipMapCache::AddAndRef(src, colorMode, cache);
+    REPORTER_ASSERT(reporter, mipmap);
+
+    {
+        const SkMipMap* mm = SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(src), colorMode,
+                                                       cache);
+        REPORTER_ASSERT(reporter, mm);
+        REPORTER_ASSERT(reporter, mm == mipmap);
+        mm->unref();
+    }
+
+    check_data(reporter, mipmap, 2, kInCache, kLocked);
+
+    mipmap->unref();
+    // tricky, since technically after this I'm no longer an owner, but since the cache is
+    // local, I know it won't get purged behind my back
+    check_data(reporter, mipmap, 1, kInCache, kNotLocked);
+
+    // find us again
+    mipmap = SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(src), colorMode, cache);
+    check_data(reporter, mipmap, 2, kInCache, kLocked);
+
+    cache->purgeAll();
+    check_data(reporter, mipmap, 1, kNotInCache, kLocked);
+
+    mipmap->unref();
+}
+
+static void test_mipmap_notify(skiatest::Reporter* reporter, SkResourceCache* cache) {
+    const SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
+    const int N = 3;
+
+    SkBitmap src[N];
+    for (int i = 0; i < N; ++i) {
+        src[i].allocN32Pixels(5, 5);
+        src[i].setImmutable();
+        SkMipMapCache::AddAndRef(src[i], colorMode, cache)->unref();
+    }
+
+    for (int i = 0; i < N; ++i) {
+        const auto desc = SkBitmapCacheDesc::Make(src[i]);
+        const SkMipMap* mipmap = SkMipMapCache::FindAndRef(desc, colorMode, cache);
+        if (cache) {
+            // if cache is null, we're working on the global cache, and other threads might purge
+            // it, making this check fragile.
+            REPORTER_ASSERT(reporter, mipmap);
+        }
+        SkSafeUnref(mipmap);
+
+        src[i].reset(); // delete the underlying pixelref, which *should* remove us from the cache
+
+        mipmap = SkMipMapCache::FindAndRef(desc, colorMode, cache);
+        REPORTER_ASSERT(reporter, !mipmap);
     }
 }
 
-// http://skbug.com/2894
-DEF_TEST(BitmapCache_add_rect, reporter) {
-    SkResourceCache::DiscardableFactory factory = SkResourceCache::GetDiscardableFactory();
-    SkBitmap::Allocator* allocator = SkBitmapCache::GetAllocator();
+#include "SkDiscardableMemoryPool.h"
 
-    SkAutoTDelete<SkResourceCache> cache;
-    if (factory) {
-        cache.reset(SkNEW_ARGS(SkResourceCache, (factory)));
-    } else {
-        const size_t byteLimit = 100 * 1024;
-        cache.reset(SkNEW_ARGS(SkResourceCache, (byteLimit)));
-    }
-    SkBitmap cachedBitmap;
-    make_bitmap(&cachedBitmap, SkImageInfo::MakeN32Premul(5, 5), allocator);
-    cachedBitmap.setImmutable();
+static SkDiscardableMemoryPool* gPool = 0;
+static SkDiscardableMemory* pool_factory(size_t bytes) {
+    SkASSERT(gPool);
+    return gPool->create(bytes);
+}
 
-    SkBitmap bm;
-    SkIRect rect = SkIRect::MakeWH(5, 5);
-
-    // Wrong subset size
-    REPORTER_ASSERT(reporter, !SkBitmapCache::Add(cachedBitmap.getGenerationID(), SkIRect::MakeWH(4, 6), cachedBitmap, cache));
-    REPORTER_ASSERT(reporter, !SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache));
-    // Wrong offset value
-    REPORTER_ASSERT(reporter, !SkBitmapCache::Add(cachedBitmap.getGenerationID(), SkIRect::MakeXYWH(-1, 0, 5, 5), cachedBitmap, cache));
-    REPORTER_ASSERT(reporter, !SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache));
-
-    // Should not be in the cache
-    REPORTER_ASSERT(reporter, !SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache));
-
-    REPORTER_ASSERT(reporter, SkBitmapCache::Add(cachedBitmap.getGenerationID(), rect, cachedBitmap, cache));
-    // Should be in the cache, we just added it
-    REPORTER_ASSERT(reporter, SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache));
+static void testBitmapCache_discarded_bitmap(skiatest::Reporter* reporter, SkResourceCache* cache,
+                                             SkResourceCache::DiscardableFactory factory) {
+    test_mipmapcache(reporter, cache);
+    test_mipmap_notify(reporter, cache);
 }
 
 DEF_TEST(BitmapCache_discarded_bitmap, reporter) {
-    SkResourceCache::DiscardableFactory factory = SkResourceCache::GetDiscardableFactory();
-    SkBitmap::Allocator* allocator = SkBitmapCache::GetAllocator();
-    
-    SkAutoTDelete<SkResourceCache> cache;
-    if (factory) {
-        cache.reset(SkNEW_ARGS(SkResourceCache, (factory)));
+    const size_t byteLimit = 100 * 1024;
+    {
+        SkResourceCache cache(byteLimit);
+        testBitmapCache_discarded_bitmap(reporter, &cache, nullptr);
+    }
+    {
+        sk_sp<SkDiscardableMemoryPool> pool(SkDiscardableMemoryPool::Make(byteLimit));
+        gPool = pool.get();
+        SkResourceCache::DiscardableFactory factory = pool_factory;
+        SkResourceCache cache(factory);
+        testBitmapCache_discarded_bitmap(reporter, &cache, factory);
+    }
+}
+
+static void test_discarded_image(skiatest::Reporter* reporter, const SkMatrix& transform,
+                                 sk_sp<SkImage> (*buildImage)()) {
+    auto surface(SkSurface::MakeRasterN32Premul(10, 10));
+    SkCanvas* canvas = surface->getCanvas();
+
+    // SkBitmapCache is global, so other threads could be evicting our bitmaps.  Loop a few times
+    // to mitigate this risk.
+    const unsigned kRepeatCount = 42;
+    for (unsigned i = 0; i < kRepeatCount; ++i) {
+        SkAutoCanvasRestore acr(canvas, true);
+
+        sk_sp<SkImage> image(buildImage());
+
+        // always use high quality to ensure caching when scaled
+        SkPaint paint;
+        paint.setFilterQuality(kHigh_SkFilterQuality);
+
+        // draw the image (with a transform, to tickle different code paths) to ensure
+        // any associated resources get cached
+        canvas->concat(transform);
+        canvas->drawImage(image, 0, 0, &paint);
+
+        const auto desc = SkBitmapCacheDesc::Make(image.get());
+
+        // delete the image
+        image.reset(nullptr);
+
+        // all resources should have been purged
+        SkBitmap result;
+        REPORTER_ASSERT(reporter, !SkBitmapCache::Find(desc, &result));
+    }
+}
+
+
+// Verify that associated bitmap cache entries are purged on SkImage destruction.
+DEF_TEST(BitmapCache_discarded_image, reporter) {
+    // Cache entries associated with SkImages fall into two categories:
+    //
+    // 1) generated image bitmaps (managed by the image cacherator)
+    // 2) scaled/resampled bitmaps (cached when HQ filters are used)
+    //
+    // To exercise the first cache type, we use generated/picture-backed SkImages.
+    // To exercise the latter, we draw scaled bitmap images using HQ filters.
+
+    const SkMatrix xforms[] = {
+        SkMatrix::MakeScale(1, 1),
+        SkMatrix::MakeScale(1.7f, 0.5f),
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(xforms); ++i) {
+        test_discarded_image(reporter, xforms[i], []() {
+            auto surface(SkSurface::MakeRasterN32Premul(10, 10));
+            surface->getCanvas()->clear(SK_ColorCYAN);
+            return surface->makeImageSnapshot();
+        });
+
+        test_discarded_image(reporter, xforms[i], []() {
+            SkPictureRecorder recorder;
+            SkCanvas* canvas = recorder.beginRecording(10, 10);
+            canvas->clear(SK_ColorCYAN);
+            return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(),
+                                            SkISize::Make(10, 10), nullptr, nullptr,
+                                            SkImage::BitDepth::kU8,
+                                            SkColorSpace::MakeSRGB());
+        });
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void* gTestNamespace;
+
+struct TestKey : SkResourceCache::Key {
+    int32_t fData;
+
+    TestKey(int sharedID, int32_t data) : fData(data) {
+        this->init(&gTestNamespace, sharedID, sizeof(fData));
+    }
+};
+
+struct TestRec : SkResourceCache::Rec {
+    enum {
+        kDidInstall = 1 << 0,
+    };
+
+    TestKey fKey;
+    int*    fFlags;
+    bool    fCanBePurged;
+
+    TestRec(int sharedID, int32_t data, int* flagPtr) : fKey(sharedID, data), fFlags(flagPtr) {
+        fCanBePurged = false;
+    }
+
+    const Key& getKey() const override { return fKey; }
+    size_t bytesUsed() const override { return 1024; /* just need a value */ }
+    bool canBePurged() override { return fCanBePurged; }
+    void postAddInstall(void*) override {
+        *fFlags |= kDidInstall;
+    }
+    const char* getCategory() const override { return "test-category"; }
+};
+
+static void test_duplicate_add(SkResourceCache* cache, skiatest::Reporter* reporter,
+                               bool purgable) {
+    int sharedID = 1;
+    int data = 0;
+
+    int flags0 = 0, flags1 = 0;
+
+    auto rec0 = skstd::make_unique<TestRec>(sharedID, data, &flags0);
+    auto rec1 = skstd::make_unique<TestRec>(sharedID, data, &flags1);
+    SkASSERT(rec0->getKey() == rec1->getKey());
+
+    TestRec* r0 = rec0.get();   // save the bare-pointer since we will release rec0
+    r0->fCanBePurged = purgable;
+
+    REPORTER_ASSERT(reporter, !(flags0 & TestRec::kDidInstall));
+    REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall));
+
+    cache->add(rec0.release(), nullptr);
+    REPORTER_ASSERT(reporter, flags0 & TestRec::kDidInstall);
+    REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall));
+    flags0 = 0; // reset the flag
+
+    cache->add(rec1.release(), nullptr);
+    if (purgable) {
+        // we purged rec0, and did install rec1
+        REPORTER_ASSERT(reporter, !(flags0 & TestRec::kDidInstall));
+        REPORTER_ASSERT(reporter, flags1 & TestRec::kDidInstall);
     } else {
-        const size_t byteLimit = 100 * 1024;
-        cache.reset(SkNEW_ARGS(SkResourceCache, (byteLimit)));
+        // we re-used rec0 and did not install rec1
+        REPORTER_ASSERT(reporter, flags0 & TestRec::kDidInstall);
+        REPORTER_ASSERT(reporter, !(flags1 & TestRec::kDidInstall));
+        r0->fCanBePurged = true;  // so we can cleanup the cache
     }
-    SkBitmap cachedBitmap;
-    make_bitmap(&cachedBitmap, SkImageInfo::MakeN32Premul(5, 5), allocator);
-    cachedBitmap.setImmutable();
-    cachedBitmap.unlockPixels();
+}
 
-    SkBitmap bm;
-    SkIRect rect = SkIRect::MakeWH(5, 5);
-
-    // Add a bitmap to the cache.
-    REPORTER_ASSERT(reporter, SkBitmapCache::Add(cachedBitmap.getGenerationID(), rect, cachedBitmap, cache));
-    REPORTER_ASSERT(reporter, SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache));
-
-    // Finding more than once works fine.
-    REPORTER_ASSERT(reporter, SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache));
-    bm.unlockPixels();
-
-    // Drop the pixels in the bitmap.
-    if (factory) {
-        REPORTER_ASSERT(reporter, SkGetGlobalDiscardableMemoryPool()->getRAMUsed() > 0);
-        SkGetGlobalDiscardableMemoryPool()->dumpPool();
-        REPORTER_ASSERT(reporter, SkGetGlobalDiscardableMemoryPool()->getRAMUsed() == 0);
-
-        // The bitmap is not in the cache since it has been dropped.
-        REPORTER_ASSERT(reporter, !SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache));
+/*
+ *  Test behavior when the same key is added more than once.
+ */
+DEF_TEST(ResourceCache_purge, reporter) {
+    for (bool purgable : { false, true }) {
+        {
+            SkResourceCache cache(1024 * 1024);
+            test_duplicate_add(&cache, reporter, purgable);
+        }
+        {
+            SkResourceCache cache(SkDiscardableMemory::Create);
+            test_duplicate_add(&cache, reporter, purgable);
+        }
     }
-
-    make_bitmap(&cachedBitmap, SkImageInfo::MakeN32Premul(5, 5), allocator);
-    cachedBitmap.setImmutable();
-    cachedBitmap.unlockPixels();
-
-    // We can add the bitmap back to the cache and find it again.
-    REPORTER_ASSERT(reporter, SkBitmapCache::Add(cachedBitmap.getGenerationID(), rect, cachedBitmap, cache));
-    REPORTER_ASSERT(reporter, SkBitmapCache::Find(cachedBitmap.getGenerationID(), rect, &bm, cache));
 }
diff --git a/src/third_party/skia/tests/SkSLErrorTest.cpp b/src/third_party/skia/tests/SkSLErrorTest.cpp
new file mode 100644
index 0000000..c631382
--- /dev/null
+++ b/src/third_party/skia/tests/SkSLErrorTest.cpp
@@ -0,0 +1,492 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSLCompiler.h"
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+static void test_failure(skiatest::Reporter* r, const char* src, const char* error) {
+    SkSL::Compiler compiler;
+    SkSL::Program::Settings settings;
+    sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
+    settings.fCaps = caps.get();
+    compiler.convertProgram(SkSL::Program::kFragment_Kind, SkString(src), settings);
+    SkSL::String skError(error);
+    if (compiler.errorText() != skError) {
+        SkDebugf("SKSL ERROR:\n    source: %s\n    expected: %s    received: %s", src, error,
+                 compiler.errorText().c_str());
+    }
+    REPORTER_ASSERT(r, compiler.errorText() == skError);
+}
+
+static void test_success(skiatest::Reporter* r, const char* src) {
+    SkSL::Compiler compiler;
+    SkSL::Program::Settings settings;
+    sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
+    settings.fCaps = caps.get();
+    std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kFragment_Kind,
+                                                                     SkString(src), settings);
+    REPORTER_ASSERT(r, program);
+}
+
+DEF_TEST(SkSLUndefinedSymbol, r) {
+    test_failure(r,
+                 "void main() { x = vec2(1); }",
+                 "error: 1: unknown identifier 'x'\n1 error\n");
+}
+
+DEF_TEST(SkSLUndefinedFunction, r) {
+    test_failure(r,
+                 "void main() { int x = foo(1); }", 
+                 "error: 1: unknown identifier 'foo'\n1 error\n");
+}
+
+DEF_TEST(SkSLGenericArgumentMismatch, r) {
+    test_failure(r,
+                 "void main() { float x = sin(1, 2); }", 
+                 "error: 1: call to 'sin' expected 1 argument, but found 2\n1 error\n");
+    test_failure(r,
+                 "void main() { float x = sin(true); }", 
+                 "error: 1: no match for sin(bool)\n1 error\n");
+    test_success(r,
+                 "void main() { float x = sin(1); }");
+}
+
+DEF_TEST(SkSLArgumentCountMismatch, r) {
+    test_failure(r,
+                 "float foo(float x) { return x * x; }"
+                 "void main() { float x = foo(1, 2); }", 
+                 "error: 1: call to 'foo' expected 1 argument, but found 2\n1 error\n");
+}
+
+DEF_TEST(SkSLArgumentMismatch, r) {
+    test_failure(r, 
+                 "float foo(float x) { return x * x; }"
+                 "void main() { float x = foo(true); }", 
+                 "error: 1: expected 'float', but found 'bool'\n1 error\n");
+}
+
+DEF_TEST(SkSLIfTypeMismatch, r) {
+    test_failure(r,
+                 "void main() { if (3) { } }",
+                 "error: 1: expected 'bool', but found 'int'\n1 error\n");
+}
+
+DEF_TEST(SkSLDoTypeMismatch, r) {
+    test_failure(r,
+                 "void main() { do { } while (vec2(1)); }", 
+                 "error: 1: expected 'bool', but found 'vec2'\n1 error\n");
+}
+
+DEF_TEST(SkSLWhileTypeMismatch, r) {
+    test_failure(r,
+                 "void main() { while (vec3(1)) { } }", 
+                 "error: 1: expected 'bool', but found 'vec3'\n1 error\n");
+}
+
+DEF_TEST(SkSLForTypeMismatch, r) {
+    test_failure(r,
+                 "void main() { for (int x = 0; x; x++) { } }", 
+                 "error: 1: expected 'bool', but found 'int'\n1 error\n");
+}
+
+DEF_TEST(SkSLConstructorTypeMismatch, r) {
+    test_failure(r,
+                 "void main() { vec2 x = vec2(1.0, false); }", 
+                 "error: 1: expected 'float', but found 'bool'\n1 error\n");
+    test_failure(r,
+                 "void main() { vec2 x = vec2(bvec2(false)); }",
+                 "error: 1: 'bvec2' is not a valid parameter to 'vec2' constructor\n1 error\n");
+    test_failure(r,
+                 "void main() { bvec2 x = bvec2(vec2(1)); }",
+                 "error: 1: 'vec2' is not a valid parameter to 'bvec2' constructor\n1 error\n");
+    test_failure(r,
+                 "void main() { bool x = bool(1.0); }",
+                 "error: 1: cannot construct 'bool'\n1 error\n");
+    test_failure(r,
+                 "struct foo { int x; }; void main() { foo x = foo(5); }",
+                 "error: 1: cannot construct 'foo'\n1 error\n");
+    test_failure(r,
+                 "struct foo { int x; } foo; void main() { float x = float(foo); }",
+                 "error: 1: invalid argument to 'float' constructor (expected a number or bool, but found 'foo')\n1 error\n");
+    test_failure(r,
+                 "struct foo { int x; } foo; void main() { vec2 x = vec2(foo); }",
+                 "error: 1: 'foo' is not a valid parameter to 'vec2' constructor\n1 error\n");
+    test_failure(r,
+                 "void main() { mat2 x = mat2(true); }",
+                 "error: 1: expected 'float', but found 'bool'\n1 error\n");
+}
+
+DEF_TEST(SkSLConstructorArgumentCount, r) {
+    test_failure(r,
+                 "void main() { vec3 x = vec3(1.0, 2.0); }",
+                 "error: 1: invalid arguments to 'vec3' constructor (expected 3 scalars, but "
+                 "found 2)\n1 error\n");
+    test_failure(r,
+                 "void main() { vec3 x = vec3(1.0, 2.0, 3.0, 4.0); }",
+                 "error: 1: invalid arguments to 'vec3' constructor (expected 3 scalars, but found "
+                 "4)\n1 error\n");
+}
+
+DEF_TEST(SkSLSwizzleScalar, r) {
+    test_failure(r,
+                 "void main() { float x = 1; float y = x.y; }",
+                 "error: 1: cannot swizzle value of type 'float'\n1 error\n");
+}
+
+DEF_TEST(SkSLSwizzleMatrix, r) {
+    test_failure(r,
+                 "void main() { mat2 x = mat2(1); float y = x.y; }",
+                 "error: 1: cannot swizzle value of type 'mat2'\n1 error\n");
+}
+
+DEF_TEST(SkSLSwizzleOutOfBounds, r) {
+    test_failure(r,
+                 "void main() { vec3 test = vec2(1).xyz; }",
+                 "error: 1: invalid swizzle component 'z'\n1 error\n");
+}
+
+DEF_TEST(SkSLSwizzleTooManyComponents, r) {
+    test_failure(r,
+                 "void main() { vec4 test = vec2(1).xxxxx; }",
+                 "error: 1: too many components in swizzle mask 'xxxxx'\n1 error\n");
+}
+
+DEF_TEST(SkSLSwizzleDuplicateOutput, r) {
+    test_failure(r,
+                 "void main() { vec4 test = vec4(1); test.xyyz = vec4(1); }",
+                 "error: 1: cannot write to the same swizzle field more than once\n1 error\n");
+}
+
+DEF_TEST(SkSLAssignmentTypeMismatch, r) {
+    test_failure(r,
+                 "void main() { int x = 1.0; }",
+                 "error: 1: expected 'int', but found 'float'\n1 error\n");
+    test_failure(r,
+                 "void main() { int x; x = 1.0; }",
+                 "error: 1: type mismatch: '=' cannot operate on 'int', 'float'\n1 error\n");
+    test_success(r,
+                 "void main() { vec3 x = vec3(0); x *= 1.0; }");
+    test_failure(r,
+                 "void main() { ivec3 x = ivec3(0); x *= 1.0; }",
+                 "error: 1: type mismatch: '*=' cannot operate on 'ivec3', 'float'\n1 error\n");
+}
+
+DEF_TEST(SkSLReturnFromVoid, r) {
+    test_failure(r,
+                 "void main() { return true; }",
+                 "error: 1: may not return a value from a void function\n1 error\n");
+}
+
+DEF_TEST(SkSLReturnMissingValue, r) {
+    test_failure(r,
+                 "int foo() { return; } void main() { }",
+                 "error: 1: expected function to return 'int'\n1 error\n");
+}
+
+DEF_TEST(SkSLReturnTypeMismatch, r) {
+    test_failure(r,
+                 "int foo() { return 1.0; } void main() { }", 
+                 "error: 1: expected 'int', but found 'float'\n1 error\n");
+}
+
+DEF_TEST(SkSLDuplicateFunction, r) {
+    test_failure(r,
+                 "void main() { } void main() { }", 
+                 "error: 1: duplicate definition of void main()\n1 error\n");
+    test_success(r,
+                 "void main(); void main() { }");
+}
+
+DEF_TEST(SkSLUsingInvalidValue, r) {
+    test_failure(r,
+                 "void main() { int x = int; }", 
+                 "error: 1: expected '(' to begin constructor invocation\n1 error\n");
+    test_failure(r,
+                 "int test() { return 1; } void main() { int x = test; }", 
+                 "error: 1: expected '(' to begin function call\n1 error\n");
+}
+DEF_TEST(SkSLDifferentReturnType, r) {
+    test_failure(r,
+                 "int main() { return 1; } void main() { }", 
+                 "error: 1: functions 'void main()' and 'int main()' differ only in return type\n1 "
+                 "error\n");
+}
+
+DEF_TEST(SkSLDifferentModifiers, r) {
+    test_failure(r,
+                 "void test(int x); void test(out int x) { }", 
+                 "error: 1: modifiers on parameter 1 differ between declaration and definition\n1 "
+                 "error\n");
+}
+
+DEF_TEST(SkSLDuplicateSymbol, r) {
+    test_failure(r,
+                 "int main; void main() { }", 
+                 "error: 1: symbol 'main' was already defined\n1 error\n");
+
+    test_failure(r,
+                 "int x; int x; void main() { }",
+                 "error: 1: symbol 'x' was already defined\n1 error\n");
+
+    test_success(r, "int x; void main() { int x; }");
+}
+
+DEF_TEST(SkSLBinaryTypeMismatch, r) {
+    test_failure(r,
+                 "void main() { float x = 3 * true; }",
+                 "error: 1: type mismatch: '*' cannot operate on 'int', 'bool'\n1 error\n");
+    test_failure(r,
+                 "void main() { bool x = 1 || 2.0; }",
+                 "error: 1: type mismatch: '||' cannot operate on 'int', 'float'\n1 error\n");
+}
+
+DEF_TEST(SkSLCallNonFunction, r) {
+    test_failure(r,
+                 "void main() { float x = 3; x(); }",
+                 "error: 1: 'x' is not a function\n1 error\n");
+}
+
+DEF_TEST(SkSLInvalidUnary, r) {
+    test_failure(r,
+                 "void main() { mat4 x = mat4(1); ++x; }",
+                 "error: 1: '++' cannot operate on 'mat4'\n1 error\n");
+    test_failure(r,
+                 "void main() { vec3 x = vec3(1); --x; }",
+                 "error: 1: '--' cannot operate on 'vec3'\n1 error\n");
+    test_failure(r,
+                 "void main() { mat4 x = mat4(1); x++; }",
+                 "error: 1: '++' cannot operate on 'mat4'\n1 error\n");
+    test_failure(r,
+                 "void main() { vec3 x = vec3(1); x--; }",
+                 "error: 1: '--' cannot operate on 'vec3'\n1 error\n");
+    test_failure(r,
+                 "void main() { int x = !12; }",
+                 "error: 1: '!' cannot operate on 'int'\n1 error\n");
+    test_failure(r,
+                 "struct foo { } bar; void main() { foo x = +bar; }",
+                 "error: 1: '+' cannot operate on 'foo'\n1 error\n");
+    test_failure(r,
+                 "struct foo { } bar; void main() { foo x = -bar; }",
+                 "error: 1: '-' cannot operate on 'foo'\n1 error\n");
+    test_success(r,
+                 "void main() { vec2 x = vec2(1, 1); x = +x; x = -x; }");
+}
+
+DEF_TEST(SkSLInvalidAssignment, r) {
+    test_failure(r,
+                 "void main() { 1 = 2; }",
+                 "error: 1: cannot assign to '1'\n1 error\n");
+    test_failure(r,
+                 "uniform int x; void main() { x = 0; }",
+                 "error: 1: cannot modify immutable variable 'x'\n1 error\n");
+    test_failure(r,
+                 "const int x; void main() { x = 0; }",
+                 "error: 1: cannot modify immutable variable 'x'\n1 error\n");
+}
+
+DEF_TEST(SkSLBadIndex, r) {
+    test_failure(r,
+                 "void main() { int x = 2[0]; }",
+                 "error: 1: expected array, but found 'int'\n1 error\n");
+    test_failure(r,
+                 "void main() { vec2 x = vec2(0); int y = x[0][0]; }",
+                 "error: 1: expected array, but found 'float'\n1 error\n");
+}
+
+DEF_TEST(SkSLTernaryMismatch, r) {
+    test_failure(r,
+                 "void main() { int x = 5 > 2 ? true : 1.0; }",
+                 "error: 1: ternary operator result mismatch: 'bool', 'float'\n1 error\n");
+    test_failure(r,
+                 "void main() { int x = 5 > 2 ? vec3(1) : 1.0; }",
+                 "error: 1: ternary operator result mismatch: 'vec3', 'float'\n1 error\n");
+}
+
+DEF_TEST(SkSLInterfaceBlockStorageModifiers, r) {
+    test_failure(r,
+                 "uniform foo { out int x; };",
+                 "error: 1: interface block fields may not have storage qualifiers\n1 error\n");
+}
+
+DEF_TEST(SkSLUseWithoutInitialize, r) {
+    test_failure(r,
+                 "void main() { int x; if (5 == 2) x = 3; x++; }",
+                 "error: 1: 'x' has not been assigned\n1 error\n");
+    test_failure(r,
+                 "void main() { int x[2][2]; int i; x[i][1] = 4; }",
+                 "error: 1: 'i' has not been assigned\n1 error\n");
+    test_failure(r,
+                 "int main() { int r; return r; }",
+                 "error: 1: 'r' has not been assigned\n1 error\n");
+    test_failure(r,
+                 "void main() { int x; int y = x; }",
+                 "error: 1: 'x' has not been assigned\n1 error\n");
+    test_failure(r,
+                 "void main() { bool x; if (true && (false || x)) return; }",
+                 "error: 1: 'x' has not been assigned\n1 error\n");
+    test_failure(r,
+                 "void main() { int x; switch (3) { case 0: x = 0; case 1: x = 1; }"
+                               "sk_FragColor = vec4(x); }",
+                 "error: 1: 'x' has not been assigned\n1 error\n");
+}
+
+DEF_TEST(SkSLUnreachable, r) {
+    test_failure(r,
+                 "void main() { return; return; }",
+                 "error: 1: unreachable\n1 error\n");
+    test_failure(r,
+                 "void main() { for (;;) { continue; int x = 1; } }",
+                 "error: 1: unreachable\n1 error\n");
+/*    test_failure(r,
+                 "void main() { for (;;) { } return; }",
+                 "error: 1: unreachable\n1 error\n");*/
+    test_failure(r,
+                 "void main() { if (true) return; else discard; return; }",
+                 "error: 1: unreachable\n1 error\n");
+    test_failure(r,
+                 "void main() { return; while (true); }",
+                 "error: 1: unreachable\n1 error\n");
+}
+
+DEF_TEST(SkSLNoReturn, r) {
+    test_failure(r,
+                 "int foo() { if (2 > 5) return 3; }",
+                 "error: 1: function can exit without returning a value\n1 error\n");
+}
+
+DEF_TEST(SkSLBreakOutsideLoop, r) {
+    test_failure(r,
+                 "void foo() { while(true) {} if (true) break; }",
+                 "error: 1: break statement must be inside a loop or switch\n1 error\n");
+}
+
+DEF_TEST(SkSLContinueOutsideLoop, r) {
+    test_failure(r,
+                 "void foo() { for(;;); continue; }",
+                 "error: 1: continue statement must be inside a loop\n1 error\n");
+    test_failure(r,
+                 "void foo() { switch (1) { default: continue; } }",
+                 "error: 1: continue statement must be inside a loop\n1 error\n");
+}
+
+DEF_TEST(SkSLStaticIfError, r) {
+    // ensure eliminated branch of static if / ternary is still checked for errors
+    test_failure(r,
+                 "void foo() { if (true); else x = 5; }",
+                 "error: 1: unknown identifier 'x'\n1 error\n");
+    test_failure(r,
+                 "void foo() { if (false) x = 5; }",
+                 "error: 1: unknown identifier 'x'\n1 error\n");
+    test_failure(r,
+                 "void foo() { true ? 5 : x; }",
+                 "error: 1: unknown identifier 'x'\n1 error\n");
+    test_failure(r,
+                 "void foo() { false ? x : 5; }",
+                 "error: 1: unknown identifier 'x'\n1 error\n");
+}
+
+DEF_TEST(SkSLBadCap, r) {
+    test_failure(r,
+                 "bool b = sk_Caps.bugFreeDriver;",
+                 "error: 1: unknown capability flag 'bugFreeDriver'\n1 error\n");
+}
+
+DEF_TEST(SkSLDivByZero, r) {
+    test_failure(r,
+                 "int x = 1 / 0;",
+                 "error: 1: division by zero\n1 error\n");
+    test_failure(r,
+                 "float x = 1 / 0;",
+                 "error: 1: division by zero\n1 error\n");
+    test_failure(r,
+                 "float x = 1.0 / 0.0;",
+                 "error: 1: division by zero\n1 error\n");
+    test_failure(r,
+                 "float x = -67.0 / (3.0 - 3);",
+                 "error: 1: division by zero\n1 error\n");
+}
+
+DEF_TEST(SkSLUnsupportedGLSLIdentifiers, r) {
+    test_failure(r,
+                 "void main() { float x = gl_FragCoord.x; };",
+                 "error: 1: unknown identifier 'gl_FragCoord'\n1 error\n");
+    test_failure(r,
+                 "void main() { float r = gl_FragColor.r; };",
+                 "error: 1: unknown identifier 'gl_FragColor'\n1 error\n");
+}
+
+DEF_TEST(SkSLWrongSwitchTypes, r) {
+    test_failure(r,
+                 "void main() { switch (vec2(1)) { case 1: break; } }",
+                 "error: 1: expected 'int', but found 'vec2'\n1 error\n");
+    test_failure(r,
+                 "void main() { switch (1) { case vec2(1): break; } }",
+                 "error: 1: expected 'int', but found 'vec2'\n1 error\n");
+}
+
+DEF_TEST(SkSLNonConstantCase, r) {
+    test_failure(r,
+                 "void main() { int x = 1; switch (1) { case x: break; } }",
+                 "error: 1: case value must be a constant\n1 error\n");
+}
+
+DEF_TEST(SkSLDuplicateCase, r) {
+    test_failure(r,
+                 "void main() { switch (1) { case 0: case 1: case 0: break; } }",
+                 "error: 1: duplicate case value\n1 error\n");
+}
+
+DEF_TEST(SkSLFieldAfterRuntimeArray, r) {
+    test_failure(r,
+                 "buffer broken { float x[]; float y; };",
+                 "error: 1: only the last entry in an interface block may be a runtime-sized "
+                 "array\n1 error\n");
+}
+
+DEF_TEST(SkSLStaticIf, r) {
+    test_success(r,
+                 "void main() { float x = 5; float y = 10;"
+                 "@if (x < y) { sk_FragColor = vec4(1); } }");
+    test_failure(r,
+                 "void main() { float x = sqrt(25); float y = 10;"
+                 "@if (x < y) { sk_FragColor = vec4(1); } }",
+                 "error: 1: static if has non-static test\n1 error\n");
+}
+
+DEF_TEST(SkSLStaticSwitch, r) {
+    test_success(r,
+                 "void main() {"
+                 "int x = 1;"
+                 "@switch (x) {"
+                 "case 1: sk_FragColor = vec4(1); break;"
+                 "default: sk_FragColor = vec4(0);"
+                 "}"
+                 "}");
+    test_failure(r,
+                 "void main() {"
+                 "int x = int(sqrt(1));"
+                 "@switch (x) {"
+                 "case 1: sk_FragColor = vec4(1); break;"
+                 "default: sk_FragColor = vec4(0);"
+                 "}"
+                 "}",
+                 "error: 1: static switch has non-static test\n1 error\n");
+    test_failure(r,
+                 "void main() {"
+                 "int x = 1;"
+                 "@switch (x) {"
+                 "case 1: sk_FragColor = vec4(1); if (sqrt(0) < sqrt(1)) break;"
+                 "default: sk_FragColor = vec4(0);"
+                 "}"
+                 "}",
+                 "error: 1: static switch contains non-static conditional break\n1 error\n");
+}
+
+#endif
diff --git a/src/third_party/skia/tests/SkSLFPTest.cpp b/src/third_party/skia/tests/SkSLFPTest.cpp
new file mode 100644
index 0000000..bf8e835
--- /dev/null
+++ b/src/third_party/skia/tests/SkSLFPTest.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSLCompiler.h"
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+static void test(skiatest::Reporter* r, const char* src, const GrShaderCaps& caps,
+                 std::vector<const char*> expectedH, std::vector<const char*> expectedCPP) {
+    SkSL::Program::Settings settings;
+    settings.fCaps = &caps;
+    SkSL::Compiler compiler;
+    SkSL::StringStream output;
+    std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
+                                                             SkSL::Program::kFragmentProcessor_Kind,
+                                                             SkString(src),
+                                                             settings);
+    if (!program) {
+        SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
+        return;
+    }
+    REPORTER_ASSERT(r, program);
+    bool success = compiler.toH(*program, "Test", output);
+    if (!success) {
+        SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
+    }
+    REPORTER_ASSERT(r, success);
+    if (success) {
+        for (const char* expected : expectedH) {
+            bool found = strstr(output.str().c_str(), expected);
+            if (!found) {
+                SkDebugf("HEADER MISMATCH:\nsource:\n%s\n\nexpected:\n'%s'\n\nreceived:\n'%s'", src,
+                         expected, output.str().c_str());
+            }
+            REPORTER_ASSERT(r, found);
+        }
+    }
+    output.reset();
+    success = compiler.toCPP(*program, "Test", output);
+    if (!success) {
+        SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
+    }
+    REPORTER_ASSERT(r, success);
+    if (success) {
+        for (const char* expected : expectedCPP) {
+            bool found = strstr(output.str().c_str(), expected);
+            if (!found) {
+                SkDebugf("CPP MISMATCH:\nsource:\n%s\n\nexpected:\n'%s'\n\nreceived:\n'%s'", src,
+                         expected, output.str().c_str());
+            }
+            REPORTER_ASSERT(r, found);
+        }
+    }
+}
+
+DEF_TEST(SkSLFPHelloWorld, r) {
+    test(r,
+         "void main() {"
+         "sk_OutColor = vec4(1);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {
+             "/*\n"
+             " * Copyright 2017 Google Inc.\n"
+             " *\n"
+             " * Use of this source code is governed by a BSD-style license that can be\n"
+             " * found in the LICENSE file.\n"
+             " */\n"
+             "\n"
+             "/*\n"
+             " * This file was autogenerated from GrTest.fp; do not modify.\n"
+             " */\n"
+             "#ifndef GrTest_DEFINED\n"
+             "#define GrTest_DEFINED\n"
+             "#include \"SkTypes.h\"\n"
+             "#if SK_SUPPORT_GPU\n"
+             "#include \"GrFragmentProcessor.h\"\n"
+             "#include \"GrCoordTransform.h\"\n"
+             "#include \"GrColorSpaceXform.h\"\n"
+             "#include \"effects/GrProxyMove.h\"\n"
+             "class GrTest : public GrFragmentProcessor {\n"
+             "public:\n"
+             "    static sk_sp<GrFragmentProcessor> Make() {\n"
+             "        return sk_sp<GrFragmentProcessor>(new GrTest());\n"
+             "    }\n"
+             "    const char* name() const override { return \"Test\"; }\n"
+             "private:\n"
+             "    GrTest()\n"
+             "    : INHERITED(kNone_OptimizationFlags) {\n"
+             "        this->initClassID<GrTest>();\n"
+             "    }\n"
+             "    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;\n"
+             "    void onGetGLSLProcessorKey(const GrShaderCaps&,GrProcessorKeyBuilder*) "
+                    "const override;\n"
+             "    bool onIsEqual(const GrFragmentProcessor&) const override;\n"
+             "    GR_DECLARE_FRAGMENT_PROCESSOR_TEST\n"
+             "    typedef GrFragmentProcessor INHERITED;\n"
+             "};\n"
+             "#endif\n"
+             "#endif\n"
+         },
+         {
+             "/*\n"
+             " * Copyright 2017 Google Inc.\n"
+             " *\n"
+             " * Use of this source code is governed by a BSD-style license that can be\n"
+             " * found in the LICENSE file.\n"
+             " */\n"
+             "\n"
+             "/*\n"
+             " * This file was autogenerated from GrTest.fp; do not modify.\n"
+             " */\n"
+             "#include \"GrTest.h\"\n"
+             "#if SK_SUPPORT_GPU\n"
+             "#include \"glsl/GrGLSLColorSpaceXformHelper.h\"\n"
+             "#include \"glsl/GrGLSLFragmentProcessor.h\"\n"
+             "#include \"glsl/GrGLSLFragmentShaderBuilder.h\"\n"
+             "#include \"glsl/GrGLSLProgramBuilder.h\"\n"
+             "#include \"SkSLCPP.h\"\n"
+             "#include \"SkSLUtil.h\"\n"
+             "class GrGLSLTest : public GrGLSLFragmentProcessor {\n"
+             "public:\n"
+             "    GrGLSLTest() {}\n"
+             "    void emitCode(EmitArgs& args) override {\n"
+             "        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;\n"
+             "        const GrTest& _outer = args.fFp.cast<GrTest>();\n"
+             "        (void) _outer;\n"
+             "        fragBuilder->codeAppendf(\"%s = vec4(1.0);\\n\", args.fOutputColor);\n"
+             "    }\n"
+             "private:\n"
+             "    void onSetData(const GrGLSLProgramDataManager& pdman, "
+                                "const GrFragmentProcessor& _proc) override {\n"
+             "    }\n"
+             "};\n"
+             "GrGLSLFragmentProcessor* GrTest::onCreateGLSLInstance() const {\n"
+             "    return new GrGLSLTest();\n"
+             "}\n"
+             "void GrTest::onGetGLSLProcessorKey(const GrShaderCaps& caps, "
+                                                "GrProcessorKeyBuilder* b) const {\n"
+             "}\n"
+             "bool GrTest::onIsEqual(const GrFragmentProcessor& other) const {\n"
+             "    const GrTest& that = other.cast<GrTest>();\n"
+             "    (void) that;\n"
+             "    return true;\n"
+             "}\n"
+             "#endif\n"
+         });
+}
+
+DEF_TEST(SkSLFPInput, r) {
+    test(r,
+         "in vec2 point;"
+         "void main() {"
+         "sk_OutColor = vec4(point, point);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {
+             "SkPoint point() const { return fPoint; }",
+             "static sk_sp<GrFragmentProcessor> Make(SkPoint point) {",
+             "return sk_sp<GrFragmentProcessor>(new GrTest(point));",
+             "GrTest(SkPoint point)",
+             ", fPoint(point)"
+         },
+         {
+             "fragBuilder->codeAppendf(\"%s = vec4(vec2(%f, %f), vec2(%f, %f));\\n\", "
+                                      "args.fOutputColor, _outer.point().fX, _outer.point().fY, "
+                                      "_outer.point().fX, _outer.point().fY);",
+             "if (fPoint != that.fPoint) return false;"
+         });
+}
+
+DEF_TEST(SkSLFPUniform, r) {
+    test(r,
+         "uniform vec4 color;"
+         "void main() {"
+         "sk_OutColor = color;"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {
+             "static sk_sp<GrFragmentProcessor> Make()"
+         },
+         {
+            "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType, "
+                                                         "kDefault_GrSLPrecision, \"color\");",
+         });
+}
+
+DEF_TEST(SkSLFPInUniform, r) {
+    test(r,
+         "in uniform vec4 color;"
+         "void main() {"
+         "sk_OutColor = color;"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {
+             "static sk_sp<GrFragmentProcessor> Make(SkRect color) {",
+         },
+         {
+            "fColorVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType, "
+                                                         "kDefault_GrSLPrecision, \"color\");",
+            "const SkRect colorValue = _outer.color();",
+            "pdman.set4fv(fColorVar, 1, (float*) &colorValue);"
+         });
+}
+
+DEF_TEST(SkSLFPSections, r) {
+    test(r,
+         "@header { header section }"
+         "void main() {"
+         "sk_OutColor = vec4(1);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {
+             "#if SK_SUPPORT_GPU\n header section"
+         },
+         {});
+    test(r,
+         "@class { class section }"
+         "void main() {"
+         "sk_OutColor = vec4(1);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {
+             "class GrTest : public GrFragmentProcessor {\n"
+             "public:\n"
+             " class section"
+         },
+         {});
+    test(r,
+         "@cpp { cpp section }"
+         "void main() {"
+         "sk_OutColor = vec4(1);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {},
+         {"cpp section"});
+    test(r,
+         "@constructorParams { int x, float y, std::vector<float> z }"
+         "in float w;"
+         "void main() {"
+         "sk_OutColor = vec4(1);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {
+             "Make(float w,  int x, float y, std::vector<float> z )",
+             "return sk_sp<GrFragmentProcessor>(new GrTest(w, x, y, z));",
+             "GrTest(float w,  int x, float y, std::vector<float> z )",
+             ", fW(w) {"
+         },
+         {});
+    test(r,
+         "@constructor { constructor section }"
+         "void main() {"
+         "sk_OutColor = vec4(1);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {
+             "private:\n constructor section"
+         },
+         {});
+    test(r,
+         "@initializers { initializers section }"
+         "void main() {"
+         "sk_OutColor = vec4(1);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {
+             ": INHERITED(kNone_OptimizationFlags)\n    ,  initializers section"
+         },
+         {});
+    test(r,
+         "float x = 10;"
+         "@emitCode { fragBuilder->codeAppendf(\"float y = %d\\n\", x * 2); }"
+         "void main() {"
+         "sk_OutColor = vec4(1);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {},
+         {
+            "x = 10.0;\n"
+            " fragBuilder->codeAppendf(\"float y = %d\\n\", x * 2);"
+         });
+    test(r,
+         "@fields { fields section }"
+         "void main() {"
+         "sk_OutColor = vec4(1);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {
+            "GR_DECLARE_FRAGMENT_PROCESSOR_TEST\n"
+            " fields section     typedef GrFragmentProcessor INHERITED;"
+         },
+         {});
+    test(r,
+         "@make { make section }"
+         "void main() {"
+         "sk_OutColor = vec4(1);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {
+            "public:\n"
+            " make section"
+         },
+         {});
+    test(r,
+         "uniform float calculated;"
+         "in float provided;"
+         "@setData(varName) { varName.set1f(calculated, provided * 2); }"
+         "void main() {"
+         "sk_OutColor = vec4(1);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {},
+         {
+             "void onSetData(const GrGLSLProgramDataManager& varName, "
+                            "const GrFragmentProcessor& _proc) override {\n",
+             "UniformHandle& calculated = fCalculatedVar;",
+             "auto provided = _outer.provided();",
+             "varName.set1f(calculated, provided * 2);"
+         });
+    test(r,
+         "@test(testDataName) { testDataName section }"
+         "void main() {"
+         "sk_OutColor = vec4(1);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {},
+         {
+             "#if GR_TEST_UTILS\n"
+             "sk_sp<GrFragmentProcessor> GrTest::TestCreate(GrProcessorTestData* testDataName) {\n"
+             " testDataName section }\n"
+             "#endif"
+         });
+}
+
+DEF_TEST(SkSLFPColorSpaceXform, r) {
+    test(r,
+         "in uniform sampler2D image;"
+         "in uniform colorSpaceXform colorXform;"
+         "void main() {"
+         "sk_OutColor = sk_InColor * texture(image, vec2(0, 0), colorXform);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {
+             "sk_sp<GrColorSpaceXform> colorXform() const { return fColorXform; }",
+             "GrTest(sk_sp<GrTextureProxy> image, sk_sp<GrColorSpaceXform> colorXform)",
+             "this->addTextureSampler(&fImage);",
+             "sk_sp<GrColorSpaceXform> fColorXform;"
+         },
+         {
+             "fragBuilder->codeAppendf(\"vec4 _tmpVar1;%s = %s * %stexture(%s, "
+             "vec2(0.0, 0.0)).%s%s;\\n\", args.fOutputColor, args.fInputColor ? args.fInputColor : "
+             "\"vec4(1)\", fColorSpaceHelper.isValid() ? \"(_tmpVar1 = \" : \"\", "
+             "fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(), "
+             "fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(), "
+             "fColorSpaceHelper.isValid() ? SkStringPrintf(\", vec4(clamp((%s * vec4(_tmpVar1.rgb, "
+             "1.0)).rgb, 0.0, _tmpVar1.a), _tmpVar1.a))\", args.fUniformHandler->getUniformCStr("
+             "fColorSpaceHelper.gamutXformUniform())).c_str() : \"\");"
+         });
+}
+
+DEF_TEST(SkSLFPTransformedCoords, r) {
+    test(r,
+         "void main() {"
+         "sk_OutColor = vec4(sk_TransformedCoords2D[0], sk_TransformedCoords2D[0]);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {},
+         {
+            "SkSL::String sk_TransformedCoords2D_0 = "
+                                         "fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);",
+            "fragBuilder->codeAppendf(\"%s = vec4(%s, %s);\\n\", args.fOutputColor, "
+                              "sk_TransformedCoords2D_0.c_str(), sk_TransformedCoords2D_0.c_str());"
+         });
+
+}
+
+DEF_TEST(SkSLFPLayoutWhen, r) {
+    test(r,
+         "layout(when=someExpression(someOtherExpression())) uniform float sometimes;"
+         "void main() {"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         {},
+         {
+            "if (someExpression(someOtherExpression())) {\n"
+            "            fSometimesVar = args.fUniformHandler->addUniform"
+         });
+
+}
+
+#endif
diff --git a/src/third_party/skia/tests/SkSLGLSLTest.cpp b/src/third_party/skia/tests/SkSLGLSLTest.cpp
new file mode 100644
index 0000000..8f09d6b
--- /dev/null
+++ b/src/third_party/skia/tests/SkSLGLSLTest.cpp
@@ -0,0 +1,1433 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSLCompiler.h"
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+// Note that the optimizer will aggressively kill dead code and substitute constants in place of
+// variables, so we have to jump through a few hoops to ensure that the code in these tests has the
+// necessary side-effects to remain live. In some cases we rely on the optimizer not (yet) being
+// smart enough to optimize around certain constructs; as the optimizer gets smarter it will
+// undoubtedly end up breaking some of these tests. That is a good thing, as long as the new code is
+// equivalent!
+
+static void test(skiatest::Reporter* r, const char* src, const SkSL::Program::Settings& settings,
+                 const char* expected, SkSL::Program::Inputs* inputs,
+                 SkSL::Program::Kind kind = SkSL::Program::kFragment_Kind) {
+    SkSL::Compiler compiler;
+    SkSL::String output;
+    std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, SkString(src), settings);
+    if (!program) {
+        SkDebugf("Unexpected error compiling %s\n%s", src, compiler.errorText().c_str());
+    }
+    REPORTER_ASSERT(r, program);
+    *inputs = program->fInputs;
+    REPORTER_ASSERT(r, compiler.toGLSL(*program, &output));
+    if (program) {
+        SkSL::String skExpected(expected);
+        if (output != skExpected) {
+            SkDebugf("GLSL MISMATCH:\nsource:\n%s\n\nexpected:\n'%s'\n\nreceived:\n'%s'", src,
+                     expected, output.c_str());
+        }
+        REPORTER_ASSERT(r, output == skExpected);
+    }
+}
+
+static void test(skiatest::Reporter* r, const char* src, const GrShaderCaps& caps,
+                 const char* expected, SkSL::Program::Kind kind = SkSL::Program::kFragment_Kind) {
+    SkSL::Program::Settings settings;
+    settings.fCaps = &caps;
+    SkSL::Program::Inputs inputs;
+    test(r, src, settings, expected, &inputs, kind);
+}
+
+DEF_TEST(SkSLHelloWorld, r) {
+    test(r,
+         "void main() { sk_FragColor = vec4(0.75); }",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(0.75);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLControl, r) {
+    test(r,
+         "void main() {"
+         "if (sqrt(2) > 5) { sk_FragColor = vec4(0.75); } else { discard; }"
+         "int i = 0;"
+         "while (i < 10) { sk_FragColor *= 0.5; i++; }"
+         "do { sk_FragColor += 0.01; } while (sk_FragColor.x < 0.75);"
+         "for (int i = 0; i < 10; i++) {"
+         "if (i % 2 == 1) break; else continue;"
+         "}"
+         "return;"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    if (sqrt(2.0) > 5.0) {\n"
+         "        sk_FragColor = vec4(0.75);\n"
+         "    } else {\n"
+         "        discard;\n"
+         "    }\n"
+         "    int i = 0;\n"
+         "    while (i < 10) {\n"
+         "        sk_FragColor *= 0.5;\n"
+         "        i++;\n"
+         "    }\n"
+         "    do {\n"
+         "        sk_FragColor += 0.01;\n"
+         "    } while (sk_FragColor.x < 0.75);\n"
+         "    for (int i = 0;i < 10; i++) {\n"
+         "        if (i % 2 == 1) break; else continue;\n"
+         "    }\n"
+         "    return;\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLFunctions, r) {
+    test(r,
+         "float foo(float v[2]) { return v[0] * v[1]; }"
+         "void bar(inout float x) { float y[2], z; y[0] = x; y[1] = x * 2; z = foo(y); x = z; }"
+         "void main() { float x = 10; bar(x); sk_FragColor = vec4(x); }",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "float foo(float v[2]) {\n"
+         "    return v[0] * v[1];\n"
+         "}\n"
+         "void bar(inout float x) {\n"
+         "    float y[2], z;\n"
+         "    y[0] = x;\n"
+         "    y[1] = x * 2.0;\n"
+         "    z = foo(y);\n"
+         "    x = z;\n"
+         "}\n"
+         "void main() {\n"
+         "    float x = 10.0;\n"
+         "    bar(x);\n"
+         "    sk_FragColor = vec4(x);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLOperators, r) {
+    test(r,
+         "void main() {"
+         "float x = 1, y = 2;"
+         "int z = 3;"
+         "x = x - x + y * z * x * (y - z);"
+         "y = x / y / z;"
+         "z = (z / 2 % 3 << 4) >> 2 << 1;"
+         "bool b = (x > 4) == x < 2 || 2 >= sqrt(2) && y <= z;"
+         "x += 12;"
+         "x -= 12;"
+         "x *= y /= z = 10;"
+         "b ||= false;"
+         "b &&= true;"
+         "b ^^= false;"
+         "z |= 0;"
+         "z &= -1;"
+         "z ^= 0;"
+         "z >>= 2;"
+         "z <<= 4;"
+         "z %= 5;"
+         "x = (vec2(sqrt(1)) , 6);"
+         "z = (vec2(sqrt(1)) , 6);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    float x = 1.0, y = 2.0;\n"
+         "    int z = 3;\n"
+         "    x = -6.0;\n"
+         "    y = -1.0;\n"
+         "    z = 8;\n"
+         "    bool b = false == true || 2.0 >= sqrt(2.0) && true;\n"
+         "    x += 12.0;\n"
+         "    x -= 12.0;\n"
+         "    x *= (y /= float(z = 10));\n"
+         "    b ||= false;\n"
+         "    b &&= true;\n"
+         "    b ^^= false;\n"
+         "    z |= 0;\n"
+         "    z &= -1;\n"
+         "    z ^= 0;\n"
+         "    z >>= 2;\n"
+         "    z <<= 4;\n"
+         "    z %= 5;\n"
+         "    x = float((vec2(sqrt(1.0)) , 6));\n"
+         "    z = (vec2(sqrt(1.0)) , 6);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLMatrices, r) {
+    test(r,
+         "void main() {"
+         "mat2x4 x = mat2x4(1);"
+         "mat3x2 y = mat3x2(1, 0, 0, 1, vec2(2, 2));"
+         "mat3x4 z = x * y;"
+         "vec3 v1 = mat3(1) * vec3(2);"
+         "vec3 v2 = vec3(2) * mat3(1);"
+         "sk_FragColor = vec4(z[0].x, v1 + v2);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    mat3x4 z = mat2x4(1.0) * mat3x2(1.0, 0.0, 0.0, 1.0, vec2(2.0, 2.0));\n"
+         "    vec3 v1 = mat3(1.0) * vec3(2.0);\n"
+         "    vec3 v2 = vec3(2.0) * mat3(1.0);\n"
+         "    sk_FragColor = vec4(z[0].x, v1 + v2);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLInterfaceBlock, r) {
+    test(r,
+         "uniform testBlock {"
+         "float x;"
+         "float y[2];"
+         "layout(binding=12) mat3x2 z;"
+         "bool w;"
+         "};"
+         "void main() {"
+         "    sk_FragColor = vec4(x, y[0], y[1], 0);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "uniform testBlock {\n"
+         "    float x;\n"
+         "    float[2] y;\n"
+         "    layout (binding = 12) mat3x2 z;\n"
+         "    bool w;\n"
+         "};\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(x, y[0], y[1], 0.0);\n"
+         "}\n");
+    test(r,
+         "uniform testBlock {"
+         "float x;"
+         "} test;"
+         "void main() {"
+         "    sk_FragColor = vec4(test.x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "uniform testBlock {\n"
+         "    float x;\n"
+         "} test;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(test.x);\n"
+         "}\n");
+    test(r,
+         "uniform testBlock {"
+         "float x;"
+         "} test[2];"
+         "void main() {"
+         "    sk_FragColor = vec4(test[1].x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "uniform testBlock {\n"
+         "    float x;\n"
+         "} test[2];\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(test[1].x);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLStructs, r) {
+    test(r,
+         "struct A {"
+         "int x;"
+         "int y;"
+         "} a1, a2;"
+         "A a3;"
+         "struct B {"
+         "float x;"
+         "float y[2];"
+         "layout(binding=1) A z;"
+         "};"
+         "B b1, b2, b3;"
+         "void main() {"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "struct A {\n"
+         "    int x;\n"
+         "    int y;\n"
+         "} a1, a2;\n"
+         "A a3;\n"
+         "struct B {\n"
+         "    float x;\n"
+         "    float[2] y;\n"
+         "    layout (binding = 1) A z;\n"
+         "} b1, b2, b3;\n"
+         "void main() {\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLVersion, r) {
+    test(r,
+         "in float test; void main() { sk_FragColor = vec4(0.75); }",
+         *SkSL::ShaderCapsFactory::Version450Core(),
+         "#version 450 core\n"
+         "out vec4 sk_FragColor;\n"
+         "in float test;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(0.75);\n"
+         "}\n");
+    test(r,
+         "in float test; void main() { sk_FragColor = vec4(0.75); }",
+         *SkSL::ShaderCapsFactory::Version110(),
+         "#version 110\n"
+         "varying float test;\n"
+         "void main() {\n"
+         "    gl_FragColor = vec4(0.75);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLUsesPrecisionModifiers, r) {
+    test(r,
+         "void main() { float x = 0.75; highp float y = 1; x++; y++;"
+         "sk_FragColor.rg = vec2(x, y); }",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    float x = 0.75;\n"
+         "    float y = 1.0;\n"
+         "    x++;\n"
+         "    y++;\n"
+         "    sk_FragColor.xy = vec2(x, y);\n"
+         "}\n");
+    test(r,
+         "void main() { float x = 0.75; highp float y = 1; x++; y++;"
+         "sk_FragColor.rg = vec2(x, y); }",
+         *SkSL::ShaderCapsFactory::UsesPrecisionModifiers(),
+         "#version 400\n"
+         "precision highp float;\n"
+         "out mediump vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    float x = 0.75;\n"
+         "    highp float y = 1.0;\n"
+         "    x++;\n"
+         "    y++;\n"
+         "    sk_FragColor.xy = vec2(x, y);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLMinAbs, r) {
+    test(r,
+         "void main() {"
+         "float x = -5;"
+         "sk_FragColor.r = min(abs(x), 6);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = min(abs(-5.0), 6.0);\n"
+         "}\n");
+
+    test(r,
+         "void main() {"
+         "float x = -5.0;"
+         "sk_FragColor.r = min(abs(x), 6.0);"
+         "}",
+         *SkSL::ShaderCapsFactory::CannotUseMinAndAbsTogether(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    float minAbsHackVar0;\n"
+         "    float minAbsHackVar1;\n"
+         "    sk_FragColor.x = ((minAbsHackVar0 = abs(-5.0)) < (minAbsHackVar1 = 6.0) ? "
+                                                               "minAbsHackVar0 : minAbsHackVar1);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLNegatedAtan, r) {
+    test(r,
+         "void main() { vec2 x = vec2(sqrt(2)); sk_FragColor.r = atan(x.x, -x.y); }",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    vec2 x = vec2(sqrt(2.0));\n"
+         "    sk_FragColor.x = atan(x.x, -x.y);\n"
+         "}\n");
+    test(r,
+         "void main() { vec2 x = vec2(sqrt(2)); sk_FragColor.r = atan(x.x, -x.y); }",
+         *SkSL::ShaderCapsFactory::MustForceNegatedAtanParamToFloat(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    vec2 x = vec2(sqrt(2.0));\n"
+         "    sk_FragColor.x = atan(x.x, -1.0 * x.y);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLModifiersDeclaration, r) {
+    test(r,
+         "layout(blend_support_all_equations) out;"
+         "void main() { }",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "layout (blend_support_all_equations) out ;\n"
+         "void main() {\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLHex, r) {
+    test(r,
+         "void main() {"
+         "int i1 = 0x0;"
+         "i1++;"
+         "int i2 = 0x1234abcd;"
+         "i2++;"
+         "int i3 = 0x7fffffff;"
+         "i3++;"
+         "int i4 = 0xffffffff;"
+         "i4++;"
+         "int i5 = -0xbeef;"
+         "i5++;"
+         "uint u1 = 0x0;"
+         "u1++;"
+         "uint u2 = 0x1234abcd;"
+         "u2++;"
+         "uint u3 = 0x7fffffff;"
+         "u3++;"
+         "uint u4 = 0xffffffff;"
+         "u4++;"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    int i1 = 0;\n"
+         "    i1++;\n"
+         "    int i2 = 305441741;\n"
+         "    i2++;\n"
+         "    int i3 = 2147483647;\n"
+         "    i3++;\n"
+         "    int i4 = -1;\n"
+         "    i4++;\n"
+         "    int i5 = -48879;\n"
+         "    i5++;\n"
+         "    uint u1 = 0u;\n"
+         "    u1++;\n"
+         "    uint u2 = 305441741u;\n"
+         "    u2++;\n"
+         "    uint u3 = 2147483647u;\n"
+         "    u3++;\n"
+         "    uint u4 = 4294967295u;\n"
+         "    u4++;\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLVectorConstructors, r) {
+    test(r,
+         "vec2 v1 = vec2(1);"
+         "vec2 v2 = vec2(1, 2);"
+         "vec2 v3 = vec2(vec2(1));"
+         "vec3 v4 = vec3(vec2(1), 1.0);"
+         "ivec2 v5 = ivec2(1);"
+         "ivec2 v6 = ivec2(vec2(1, 2));"
+         "vec2 v7 = vec2(ivec2(1, 2));",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "vec2 v1 = vec2(1.0);\n"
+         "vec2 v2 = vec2(1.0, 2.0);\n"
+         "vec2 v3 = vec2(1.0);\n"
+         "vec3 v4 = vec3(vec2(1.0), 1.0);\n"
+         "ivec2 v5 = ivec2(1);\n"
+         "ivec2 v6 = ivec2(vec2(1.0, 2.0));\n"
+         "vec2 v7 = vec2(ivec2(1, 2));\n");
+}
+
+DEF_TEST(SkSLArrayConstructors, r) {
+    test(r,
+         "float test1[] = float[](1, 2, 3, 4);"
+         "vec2 test2[] = vec2[](vec2(1, 2), vec2(3, 4));"
+         "mat4 test3[] = mat4[]();",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "float test1[] = float[](1.0, 2.0, 3.0, 4.0);\n"
+         "vec2 test2[] = vec2[](vec2(1.0, 2.0), vec2(3.0, 4.0));\n"
+         "mat4 test3[] = mat4[]();\n");
+}
+
+DEF_TEST(SkSLDerivatives, r) {
+    test(r,
+         "void main() { sk_FragColor.r = dFdx(1); }",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = dFdx(1.0);\n"
+         "}\n");
+    test(r,
+         "void main() { sk_FragColor.r = 1; }",
+         *SkSL::ShaderCapsFactory::ShaderDerivativeExtensionString(),
+         "#version 400\n"
+         "precision highp float;\n"
+         "out mediump vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = 1.0;\n"
+         "}\n");
+    test(r,
+         "void main() { sk_FragColor.r = dFdx(1); }",
+         *SkSL::ShaderCapsFactory::ShaderDerivativeExtensionString(),
+         "#version 400\n"
+         "#extension GL_OES_standard_derivatives : require\n"
+         "precision highp float;\n"
+         "out mediump vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = dFdx(1.0);\n"
+         "}\n");
+}
+
+
+DEF_TEST(SkSLIntFolding, r) {
+    test(r,
+         "void main() {"
+         "sk_FragColor.r = 32 + 2;"
+         "sk_FragColor.r = 32 - 2;"
+         "sk_FragColor.r = 32 * 2;"
+         "sk_FragColor.r = 32 / 2;"
+         "sk_FragColor.r = 12 | 6;"
+         "sk_FragColor.r = 254 & 7;"
+         "sk_FragColor.r = 2 ^ 7;"
+         "sk_FragColor.r = 1 << 4;"
+         "sk_FragColor.r = 128 >> 2;"
+         "sk_FragColor.r = -1 == -1 ? 1 : -1;"
+         "sk_FragColor.r = -1 == -2 ? 2 : -2;"
+         "sk_FragColor.r = 0 != 1 ? 3 : -3;"
+         "sk_FragColor.r = 0 != 0 ? 4 : -4;"
+         "sk_FragColor.r = 6 > 5 ? 5 : -5;"
+         "sk_FragColor.r = 6 > 6 ? 6 : -6;"
+         "sk_FragColor.r = -1 < 0 ? 7 : -7;"
+         "sk_FragColor.r = 1 < 0 ? 8 : -8;"
+         "sk_FragColor.r = 6 >= 6 ? 9 : -9;"
+         "sk_FragColor.r = 6 >= 7 ? 10 : -10;"
+         "sk_FragColor.r = 6 <= 6 ? 11 : -11;"
+         "sk_FragColor.r = 6 <= 5 ? 12 : -12;"
+         "sk_FragColor.r = int(sqrt(1)) + 0;"
+         "sk_FragColor.r = 0 + int(sqrt(2));"
+         "sk_FragColor.r = int(sqrt(3)) - 0;"
+         "sk_FragColor.r = int(sqrt(4)) * 0;"
+         "sk_FragColor.r = int(sqrt(5)) * 1;"
+         "sk_FragColor.r = 1 * int(sqrt(6));"
+         "sk_FragColor.r = 0 * int(sqrt(7));"
+         "sk_FragColor.r = int(sqrt(8)) / 1;"
+         "sk_FragColor.r = 0 / int(sqrt(9));"
+         "int x = int(sqrt(2));"
+         "x += 1;"
+         "x += 0;"
+         "x -= 1;"
+         "x -= 0;"
+         "x *= 1;"
+         "x *= 2;"
+         "x /= 1;"
+         "x /= 2;"
+         "sk_FragColor.r = x;"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = 34.0;\n"
+         "    sk_FragColor.x = 30.0;\n"
+         "    sk_FragColor.x = 64.0;\n"
+         "    sk_FragColor.x = 16.0;\n"
+         "    sk_FragColor.x = 14.0;\n"
+         "    sk_FragColor.x = 6.0;\n"
+         "    sk_FragColor.x = 5.0;\n"
+         "    sk_FragColor.x = 16.0;\n"
+         "    sk_FragColor.x = 32.0;\n"
+         "    sk_FragColor.x = 1.0;\n"
+         "    sk_FragColor.x = -2.0;\n"
+         "    sk_FragColor.x = 3.0;\n"
+         "    sk_FragColor.x = -4.0;\n"
+         "    sk_FragColor.x = 5.0;\n"
+         "    sk_FragColor.x = -6.0;\n"
+         "    sk_FragColor.x = 7.0;\n"
+         "    sk_FragColor.x = -8.0;\n"
+         "    sk_FragColor.x = 9.0;\n"
+         "    sk_FragColor.x = -10.0;\n"
+         "    sk_FragColor.x = 11.0;\n"
+         "    sk_FragColor.x = -12.0;\n"
+         "    sk_FragColor.x = float(int(sqrt(1.0)));\n"
+         "    sk_FragColor.x = float(int(sqrt(2.0)));\n"
+         "    sk_FragColor.x = float(int(sqrt(3.0)));\n"
+         "    sk_FragColor.x = 0.0;\n"
+         "    sk_FragColor.x = float(int(sqrt(5.0)));\n"
+         "    sk_FragColor.x = float(int(sqrt(6.0)));\n"
+         "    sk_FragColor.x = 0.0;\n"
+         "    sk_FragColor.x = float(int(sqrt(8.0)));\n"
+         "    sk_FragColor.x = 0.0;\n"
+         "    int x = int(sqrt(2.0));\n"
+         "    x += 1;\n"
+         "    x -= 1;\n"
+         "    x *= 2;\n"
+         "    x /= 2;\n"
+         "    sk_FragColor.x = float(x);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLFloatFolding, r) {
+    test(r,
+         "void main() {"
+         "sk_FragColor.r = 32.0 + 2.0;"
+         "sk_FragColor.r = 32.0 - 2.0;"
+         "sk_FragColor.r = 32.0 * 2.0;"
+         "sk_FragColor.r = 32.0 / 2.0;"
+         "sk_FragColor.r = (12 > 2.0) ? (10 * 2 / 5 + 18 - 3) : 0;"
+         "sk_FragColor.r = 0.0 == 0.0 ? 1 : -1;"
+         "sk_FragColor.r = 0.0 == 1.0 ? 2 : -2;"
+         "sk_FragColor.r = 0.0 != 1.0 ? 3 : -3;"
+         "sk_FragColor.r = 0.0 != 0.0 ? 4 : -4;"
+         "sk_FragColor.r = 6.0 > 5.0 ? 5 : -5;"
+         "sk_FragColor.r = 6.0 > 6.0 ? 6 : -6;"
+         "sk_FragColor.r = 6.0 >= 6.0 ? 7 : -7;"
+         "sk_FragColor.r = 6.0 >= 7.0 ? 8 : -8;"
+         "sk_FragColor.r = 5.0 < 6.0 ? 9 : -9;"
+         "sk_FragColor.r = 6.0 < 6.0 ? 10 : -10;"
+         "sk_FragColor.r = 6.0 <= 6.0 ? 11 : -11;"
+         "sk_FragColor.r = 6.0 <= 5.0 ? 12 : -12;"
+         "sk_FragColor.r = sqrt(1) + 0;"
+         "sk_FragColor.r = 0 + sqrt(2);"
+         "sk_FragColor.r = sqrt(3) - 0;"
+         "sk_FragColor.r = sqrt(4) * 0;"
+         "sk_FragColor.r = sqrt(5) * 1;"
+         "sk_FragColor.r = 1 * sqrt(6);"
+         "sk_FragColor.r = 0 * sqrt(7);"
+         "sk_FragColor.r = sqrt(8) / 1;"
+         "sk_FragColor.r = 0 / sqrt(9);"
+         "sk_FragColor.r += 1;"
+         "sk_FragColor.r += 0;"
+         "sk_FragColor.r -= 1;"
+         "sk_FragColor.r -= 0;"
+         "sk_FragColor.r *= 1;"
+         "sk_FragColor.r *= 2;"
+         "sk_FragColor.r /= 1;"
+         "sk_FragColor.r /= 2;"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = 34.0;\n"
+         "    sk_FragColor.x = 30.0;\n"
+         "    sk_FragColor.x = 64.0;\n"
+         "    sk_FragColor.x = 16.0;\n"
+         "    sk_FragColor.x = 19.0;\n"
+         "    sk_FragColor.x = 1.0;\n"
+         "    sk_FragColor.x = -2.0;\n"
+         "    sk_FragColor.x = 3.0;\n"
+         "    sk_FragColor.x = -4.0;\n"
+         "    sk_FragColor.x = 5.0;\n"
+         "    sk_FragColor.x = -6.0;\n"
+         "    sk_FragColor.x = 7.0;\n"
+         "    sk_FragColor.x = -8.0;\n"
+         "    sk_FragColor.x = 9.0;\n"
+         "    sk_FragColor.x = -10.0;\n"
+         "    sk_FragColor.x = 11.0;\n"
+         "    sk_FragColor.x = -12.0;\n"
+         "    sk_FragColor.x = sqrt(1.0);\n"
+         "    sk_FragColor.x = sqrt(2.0);\n"
+         "    sk_FragColor.x = sqrt(3.0);\n"
+         "    sk_FragColor.x = 0.0;\n"
+         "    sk_FragColor.x = sqrt(5.0);\n"
+         "    sk_FragColor.x = sqrt(6.0);\n"
+         "    sk_FragColor.x = 0.0;\n"
+         "    sk_FragColor.x = sqrt(8.0);\n"
+         "    sk_FragColor.x = 0.0;\n"
+         "    sk_FragColor.x += 1.0;\n"
+         "    sk_FragColor.x -= 1.0;\n"
+         "    sk_FragColor.x *= 2.0;\n"
+         "    sk_FragColor.x /= 2.0;\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLBoolFolding, r) {
+    test(r,
+         "void main() {"
+         "sk_FragColor.r = 1 == 1 || 2 == 8 ? 1 : -1;"
+         "sk_FragColor.r = 1 > 1 || 2 == 8 ? 2 : -2;"
+         "sk_FragColor.r = 1 == 1 && 2 <= 8 ? 3 : -3;"
+         "sk_FragColor.r = 1 == 2 && 2 == 8 ? 4 : -4;"
+         "sk_FragColor.r = 1 == 1 ^^ 1 != 1 ? 5 : -5;"
+         "sk_FragColor.r = 1 == 1 ^^ 1 == 1 ? 6 : -6;"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = 1.0;\n"
+         "    sk_FragColor.x = -2.0;\n"
+         "    sk_FragColor.x = 3.0;\n"
+         "    sk_FragColor.x = -4.0;\n"
+         "    sk_FragColor.x = 5.0;\n"
+         "    sk_FragColor.x = -6.0;\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLVecFolding, r) {
+    test(r,
+         "void main() {"
+         "sk_FragColor.r = vec4(0.5, 1, 1, 1).x;"
+         "sk_FragColor = vec4(vec2(1), vec2(2, 3)) + vec4(5, 6, 7, 8);"
+         "sk_FragColor = vec4(8, vec3(10)) - vec4(1);"
+         "sk_FragColor = vec4(2) * vec4(1, 2, 3, 4);"
+         "sk_FragColor = vec4(12) / vec4(1, 2, 3, 4);"
+         "sk_FragColor.r = (vec4(12) / vec4(1, 2, 3, 4)).y;"
+         "sk_FragColor.x = vec4(1) == vec4(1) ? 1.0 : -1.0;"
+         "sk_FragColor.x = vec4(1) == vec4(2) ? 2.0 : -2.0;"
+         "sk_FragColor.x = vec2(1) == vec2(1, 1) ? 3.0 : -3.0;"
+         "sk_FragColor.x = vec2(1, 1) == vec2(1, 1) ? 4.0 : -4.0;"
+         "sk_FragColor.x = vec2(1) == vec2(1, 0) ? 5.0 : -5.0;"
+         "sk_FragColor.x = vec4(1) == vec4(vec2(1), vec2(1)) ? 6.0 : -6.0;"
+         "sk_FragColor.x = vec4(vec3(1), 1) == vec4(vec2(1), vec2(1)) ? 7.0 : -7.0;"
+         "sk_FragColor.x = vec4(vec3(1), 1) == vec4(vec2(1), 1, 0) ? 8.0 : -8.0;"
+         "sk_FragColor.x = vec2(1) != vec2(1, 0) ? 9.0 : -9.0;"
+         "sk_FragColor.x = vec4(1) != vec4(vec2(1), vec2(1)) ? 10.0 : -10.0;"
+         "sk_FragColor = vec4(sqrt(1)) * vec4(1);"
+         "sk_FragColor = vec4(1) * vec4(sqrt(2));"
+         "sk_FragColor = vec4(0) * vec4(sqrt(3));"
+         "sk_FragColor = vec4(sqrt(4)) * vec4(0);"
+         "sk_FragColor = vec4(0) / vec4(sqrt(5));"
+         "sk_FragColor = vec4(0) + vec4(sqrt(6));"
+         "sk_FragColor = vec4(sqrt(7)) + vec4(0);"
+         "sk_FragColor = vec4(sqrt(8)) - vec4(0);"
+         "sk_FragColor = vec4(0) + sqrt(9);"
+         "sk_FragColor = vec4(0) * sqrt(10);"
+         "sk_FragColor = vec4(0) / sqrt(11);"
+         "sk_FragColor = vec4(1) * sqrt(12);"
+         "sk_FragColor = 0 + vec4(sqrt(13));"
+         "sk_FragColor = 0 * vec4(sqrt(14));"
+         "sk_FragColor = 0 / vec4(sqrt(15));"
+         "sk_FragColor = 1 * vec4(sqrt(16));"
+         "sk_FragColor = vec4(sqrt(17)) + 0;"
+         "sk_FragColor = vec4(sqrt(18)) * 0;"
+         "sk_FragColor = vec4(sqrt(19)) * 1;"
+         "sk_FragColor = vec4(sqrt(19.5)) - 0;"
+         "sk_FragColor = sqrt(20) * vec4(1);"
+         "sk_FragColor = sqrt(21) + vec4(0);"
+         "sk_FragColor = sqrt(22) - vec4(0);"
+         "sk_FragColor = sqrt(23) / vec4(1);"
+         "sk_FragColor = vec4(sqrt(24)) / 1;"
+         "sk_FragColor += vec4(1);"
+         "sk_FragColor += vec4(0);"
+         "sk_FragColor -= vec4(1);"
+         "sk_FragColor -= vec4(0);"
+         "sk_FragColor *= vec4(1);"
+         "sk_FragColor *= vec4(2);"
+         "sk_FragColor /= vec4(1);"
+         "sk_FragColor /= vec4(2);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = 0.5;\n"
+         "    sk_FragColor = vec4(6.0, 7.0, 9.0, 11.0);\n"
+         "    sk_FragColor = vec4(7.0, 9.0, 9.0, 9.0);\n"
+         "    sk_FragColor = vec4(2.0, 4.0, 6.0, 8.0);\n"
+         "    sk_FragColor = vec4(12.0, 6.0, 4.0, 3.0);\n"
+         "    sk_FragColor.x = 6.0;\n"
+         "    sk_FragColor.x = 1.0;\n"
+         "    sk_FragColor.x = -2.0;\n"
+         "    sk_FragColor.x = 3.0;\n"
+         "    sk_FragColor.x = 4.0;\n"
+         "    sk_FragColor.x = -5.0;\n"
+         "    sk_FragColor.x = 6.0;\n"
+         "    sk_FragColor.x = 7.0;\n"
+         "    sk_FragColor.x = -8.0;\n"
+         "    sk_FragColor.x = 9.0;\n"
+         "    sk_FragColor.x = -10.0;\n"
+         "    sk_FragColor = vec4(sqrt(1.0));\n"
+         "    sk_FragColor = vec4(sqrt(2.0));\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(sqrt(6.0));\n"
+         "    sk_FragColor = vec4(sqrt(7.0));\n"
+         "    sk_FragColor = vec4(sqrt(8.0));\n"
+         "    sk_FragColor = vec4(sqrt(9.0));\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(sqrt(12.0));\n"
+         "    sk_FragColor = vec4(sqrt(13.0));\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(sqrt(16.0));\n"
+         "    sk_FragColor = vec4(sqrt(17.0));\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "    sk_FragColor = vec4(sqrt(19.0));\n"
+         "    sk_FragColor = vec4(sqrt(19.5));\n"
+         "    sk_FragColor = vec4(sqrt(20.0));\n"
+         "    sk_FragColor = vec4(sqrt(21.0));\n"
+         "    sk_FragColor = vec4(sqrt(22.0));\n"
+         "    sk_FragColor = vec4(sqrt(23.0));\n"
+         "    sk_FragColor = vec4(sqrt(24.0));\n"
+         "    sk_FragColor += vec4(1.0);\n"
+         "    sk_FragColor -= vec4(1.0);\n"
+         "    sk_FragColor *= vec4(2.0);\n"
+         "    sk_FragColor /= vec4(2.0);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLMatFolding, r) {
+    test(r,
+         "void main() {"
+         "sk_FragColor.x = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) == "
+                          "mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) ? 1 : -1;"
+         "sk_FragColor.x = mat2(vec2(1.0, 0.0), vec2(1.0, 1.0)) == "
+                          "mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) ? 2 : -2;"
+         "sk_FragColor.x = mat2(1) == mat2(1) ? 3 : -3;"
+         "sk_FragColor.x = mat2(1) == mat2(0) ? 4 : -4;"
+         "sk_FragColor.x = mat2(1) == mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) ? 5 : -5;"
+         "sk_FragColor.x = mat2(2) == mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)) ? 6 : -6;"
+         "sk_FragColor.x = mat3x2(2) == mat3x2(vec2(2.0, 0.0), vec2(0.0, 2.0), vec2(0.0)) ? 7 : -7;"
+         "sk_FragColor.x = mat2(1) != mat2(1) ? 8 : -8;"
+         "sk_FragColor.x = mat2(1) != mat2(0) ? 9 : -9;"
+         "sk_FragColor.x = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 0.0)) == "
+                          "mat3(mat2(1.0)) ? 10 : -10;"
+         "sk_FragColor.x = mat2(mat3(1.0)) == mat2(1.0) ? 11 : -11;"
+         "sk_FragColor.x = mat2(vec4(1.0, 0.0, 0.0, 1.0)) == mat2(1.0) ? 12 : -12;"
+         "sk_FragColor.x = mat2(1.0, 0.0, vec2(0.0, 1.0)) == mat2(1.0) ? 13 : -13;"
+         "sk_FragColor.x = mat2(vec2(1.0, 0.0), 0.0, 1.0) == mat2(1.0) ? 14 : -14;"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = 1.0;\n"
+         "    sk_FragColor.x = -2.0;\n"
+         "    sk_FragColor.x = 3.0;\n"
+         "    sk_FragColor.x = -4.0;\n"
+         "    sk_FragColor.x = 5.0;\n"
+         "    sk_FragColor.x = -6.0;\n"
+         "    sk_FragColor.x = 7.0;\n"
+         "    sk_FragColor.x = -8.0;\n"
+         "    sk_FragColor.x = 9.0;\n"
+         "    sk_FragColor.x = 10.0;\n"
+         "    sk_FragColor.x = 11.0;\n"
+         "    sk_FragColor.x = 12.0;\n"
+         "    sk_FragColor.x = 13.0;\n"
+         "    sk_FragColor.x = 14.0;\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLConstantIf, r) {
+    test(r,
+         "void main() {"
+         "int x;"
+         "if (true) x = 1;"
+         "if (2 > 1) x = 2; else x = 3;"
+         "if (1 > 2) x = 4; else x = 5;"
+         "if (false) x = 6;"
+         "sk_FragColor.r = x;"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.x = 5.0;\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLCaps, r) {
+    test(r,
+         "void main() {"
+         "int x = 0;"
+         "int y = 0;"
+         "int z = 0;"
+         "int w = 0;"
+         "if (sk_Caps.externalTextureSupport) x = 1;"
+         "if (sk_Caps.fbFetchSupport) y = 1;"
+         "if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.texelFetchSupport) z = 1;"
+         "if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.canUseAnyFunctionInShader) w = 1;"
+         "sk_FragColor = vec4(x, y, z, w);"
+         "}",
+         *SkSL::ShaderCapsFactory::VariousCaps(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(1.0, 0.0, 1.0, 0.0);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLTexture, r) {
+    test(r,
+         "uniform sampler1D one;"
+         "uniform sampler2D two;"
+         "void main() {"
+         "vec4 a = texture(one, 0);"
+         "vec4 b = texture(two, vec2(0));"
+         "vec4 c = texture(one, vec2(0));"
+         "vec4 d = texture(two, vec3(0));"
+         "sk_FragColor = vec4(a.x, b.x, c.x, d.x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "uniform sampler1D one;\n"
+         "uniform sampler2D two;\n"
+         "void main() {\n"
+         "    vec4 a = texture(one, 0.0);\n"
+         "    vec4 b = texture(two, vec2(0.0));\n"
+         "    vec4 c = textureProj(one, vec2(0.0));\n"
+         "    vec4 d = textureProj(two, vec3(0.0));\n"
+         "    sk_FragColor = vec4(a.x, b.x, c.x, d.x);\n"
+         "}\n");
+    test(r,
+         "uniform sampler1D one;"
+         "uniform sampler2D two;"
+         "void main() {"
+         "vec4 a = texture(one, 0);"
+         "vec4 b = texture(two, vec2(0));"
+         "vec4 c = texture(one, vec2(0));"
+         "vec4 d = texture(two, vec3(0));"
+         "sk_FragColor = vec4(a.x, b.x, c.x, d.x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Version110(),
+         "#version 110\n"
+         "uniform sampler1D one;\n"
+         "uniform sampler2D two;\n"
+         "void main() {\n"
+         "    vec4 a = texture1D(one, 0.0);\n"
+         "    vec4 b = texture2D(two, vec2(0.0));\n"
+         "    vec4 c = texture1DProj(one, vec2(0.0));\n"
+         "    vec4 d = texture2DProj(two, vec3(0.0));\n"
+         "    gl_FragColor = vec4(a.x, b.x, c.x, d.x);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLOffset, r) {
+    test(r,
+         "struct Test {"
+         "layout(offset = 0) int x;"
+         "layout(offset = 4) int y;"
+         "int z;"
+         "} test;",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "struct Test {\n"
+         "    layout (offset = 0) int x;\n"
+         "    layout (offset = 4) int y;\n"
+         "    int z;\n"
+         "} test;\n");
+}
+
+DEF_TEST(SkSLFragCoord, r) {
+    SkSL::Program::Settings settings;
+    settings.fFlipY = true;
+    sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::FragCoordsOld();
+    settings.fCaps = caps.get();
+    SkSL::Program::Inputs inputs;
+    test(r,
+         "void main() { sk_FragColor.xy = sk_FragCoord.xy; }",
+         settings,
+         "#version 110\n"
+         "#extension GL_ARB_fragment_coord_conventions : require\n"
+         "layout(origin_upper_left) in vec4 gl_FragCoord;\n"
+         "void main() {\n"
+         "    gl_FragColor.xy = gl_FragCoord.xy;\n"
+         "}\n",
+         &inputs);
+    REPORTER_ASSERT(r, !inputs.fRTHeight);
+
+    caps = SkSL::ShaderCapsFactory::FragCoordsNew();
+    settings.fCaps = caps.get();
+    test(r,
+         "void main() { sk_FragColor.xy = sk_FragCoord.xy; }",
+         settings,
+         "#version 400\n"
+         "layout(origin_upper_left) in vec4 gl_FragCoord;\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.xy = gl_FragCoord.xy;\n"
+         "}\n",
+         &inputs);
+    REPORTER_ASSERT(r, !inputs.fRTHeight);
+
+    caps = SkSL::ShaderCapsFactory::Default();
+    settings.fCaps = caps.get();
+    test(r,
+         "void main() { sk_FragColor.xy = sk_FragCoord.xy; }",
+         settings,
+         "#version 400\n"
+         "uniform float u_skRTHeight;\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    vec2 _sktmpCoord = gl_FragCoord.xy;\n"
+         "    vec4 sk_FragCoord = vec4(_sktmpCoord.x, u_skRTHeight - _sktmpCoord.y, 1.0, 1.0);\n"
+         "    sk_FragColor.xy = sk_FragCoord.xy;\n"
+         "}\n",
+         &inputs);
+    REPORTER_ASSERT(r, inputs.fRTHeight);
+
+    settings.fFlipY = false;
+    test(r,
+         "void main() { sk_FragColor.xy = sk_FragCoord.xy; }",
+         settings,
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor.xy = gl_FragCoord.xy;\n"
+         "}\n",
+         &inputs);
+    REPORTER_ASSERT(r, !inputs.fRTHeight);
+}
+
+DEF_TEST(SkSLVertexID, r) {
+    test(r,
+         "out int id; void main() { id = sk_VertexID; }",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out int id;\n"
+         "void main() {\n"
+         "    id = gl_VertexID;\n"
+         "}\n",
+         SkSL::Program::kVertex_Kind);
+}
+
+DEF_TEST(SkSLClipDistance, r) {
+    test(r,
+         "void main() { sk_ClipDistance[0] = 0; }",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "void main() {\n"
+         "    gl_ClipDistance[0] = 0.0;\n"
+         "}\n",
+         SkSL::Program::kVertex_Kind);
+    test(r,
+         "void main() { sk_FragColor = vec4(sk_ClipDistance[0]); }",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(gl_ClipDistance[0]);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLArrayTypes, r) {
+    test(r,
+         "void main() { vec2 x[2] = vec2[2](vec2(1), vec2(2));"
+         "vec2[2] y = vec2[2](vec2(3), vec2(4));"
+         "sk_FragColor = vec4(x[0], y[1]); }",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(vec2[2](vec2(1.0), vec2(2.0))[0], "
+                                 "vec2[2](vec2(3.0), vec2(4.0))[1]);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLGeometry, r) {
+    test(r,
+         "layout(points) in;"
+         "layout(invocations = 2) in;"
+         "layout(line_strip, max_vertices = 2) out;"
+         "void main() {"
+         "gl_Position = sk_in[0].gl_Position + vec4(-0.5, 0, 0, sk_InvocationID);"
+         "EmitVertex();"
+         "gl_Position = sk_in[0].gl_Position + vec4(0.5, 0, 0, sk_InvocationID);"
+         "EmitVertex();"
+         "EndPrimitive();"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "layout (points) in ;\n"
+         "layout (invocations = 2) in ;\n"
+         "layout (line_strip, max_vertices = 2) out ;\n"
+         "void main() {\n"
+         "    gl_Position = gl_in[0].gl_Position + vec4(-0.5, 0.0, 0.0, float(gl_InvocationID));\n"
+         "    EmitVertex();\n"
+         "    gl_Position = gl_in[0].gl_Position + vec4(0.5, 0.0, 0.0, float(gl_InvocationID));\n"
+         "    EmitVertex();\n"
+         "    EndPrimitive();\n"
+         "}\n",
+         SkSL::Program::kGeometry_Kind);
+}
+
+DEF_TEST(SkSLSwitch, r) {
+    // basic "does a switch even work" test
+    test(r,
+         "void main() {"
+         "    float x;"
+         "    switch (int(sqrt(1))) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "            break;"
+         "        case 1:"
+         "            x = 1.0;"
+         "            break;"
+         "        default:"
+         "            x = 2.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    float x;\n"
+         "    switch (int(sqrt(1.0))) {\n"
+         "        case 0:\n"
+         "            x = 0.0;\n"
+         "            break;\n"
+         "        case 1:\n"
+         "            x = 1.0;\n"
+         "            break;\n"
+         "        default:\n"
+         "            x = 2.0;\n"
+         "    }\n"
+         "    sk_FragColor = vec4(x);\n"
+         "}\n");
+    // dead code inside of switch
+    test(r,
+         "void main() {"
+         "    float x;"
+         "    switch (int(sqrt(2))) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "        case 1:"
+         "            x = 1.0;"
+         "        default:"
+         "            x = 2.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    switch (int(sqrt(2.0))) {\n"
+         "        case 0:\n"
+         "            ;\n"
+         "        case 1:\n"
+         "            ;\n"
+         "        default:\n"
+         "            ;\n"
+         "    }\n"
+         "    sk_FragColor = vec4(2.0);\n"
+         "}\n");
+    // non-static test w/ fallthrough
+    test(r,
+         "void main() {"
+         "    float x = 0.0;"
+         "    switch (int(sqrt(3))) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "        case 1:"
+         "            x = 1.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    float x = 0.0;\n"
+         "    switch (int(sqrt(3.0))) {\n"
+         "        case 0:\n"
+         "            x = 0.0;\n"
+         "        case 1:\n"
+         "            x = 1.0;\n"
+         "    }\n"
+         "    sk_FragColor = vec4(x);\n"
+         "}\n");
+    // static test w/ fallthrough
+    test(r,
+         "void main() {"
+         "    float x = 0.0;"
+         "    switch (0) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "        case 1:"
+         "            x = 1.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(1.0);\n"
+         "}\n");
+    // static test w/ fallthrough, different entry point
+    test(r,
+         "void main() {"
+         "    float x = 0.0;"
+         "    switch (1) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "        case 1:"
+         "            x = 1.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(1.0);\n"
+         "}\n");
+    // static test w/ break
+    test(r,
+         "void main() {"
+         "    float x = 0.0;"
+         "    switch (0) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "            break;"
+         "        case 1:"
+         "            x = 1.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "}\n");
+    // static test w/ static conditional break
+    test(r,
+         "void main() {"
+         "    float x = 0.0;"
+         "    switch (0) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "            if (x < 1) break;"
+         "        case 1:"
+         "            x = 1.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(0.0);\n"
+         "}\n");
+    // static test w/ non-static conditional break
+    test(r,
+         "void main() {"
+         "    float x = 0.0;"
+         "    switch (0) {"
+         "        case 0:"
+         "            x = 0.0;"
+         "            if (x < sqrt(1)) break;"
+         "        case 1:"
+         "            x = 1.0;"
+         "    }"
+         "    sk_FragColor = vec4(x);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    float x = 0.0;\n"
+         "    switch (0) {\n"
+         "        case 0:\n"
+         "            x = 0.0;\n"
+         "            if (0.0 < sqrt(1.0)) break;\n"
+         "        case 1:\n"
+         "            x = 1.0;\n"
+         "    }\n"
+         "    sk_FragColor = vec4(x);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLRectangleTexture, r) {
+    test(r,
+         "uniform sampler2D test;"
+         "void main() {"
+         "    sk_FragColor = texture(test, vec2(0.5));"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "uniform sampler2D test;\n"
+         "void main() {\n"
+         "    sk_FragColor = texture(test, vec2(0.5));\n"
+         "}\n");
+    test(r,
+         "uniform sampler2DRect test;"
+         "void main() {"
+         "    sk_FragColor = texture(test, vec2(0.5));"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "uniform sampler2DRect test;\n"
+         "void main() {\n"
+         "    sk_FragColor = texture(test, textureSize(test) * vec2(0.5));\n"
+         "}\n");
+    test(r,
+         "uniform sampler2DRect test;"
+         "void main() {"
+         "    sk_FragColor = texture(test, vec3(0.5));"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "uniform sampler2DRect test;\n"
+         "void main() {\n"
+         "    sk_FragColor = texture(test, vec3(textureSize(test), 1.0) * vec3(0.5));\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLUnusedVars, r) {
+    test(r,
+         "void main() {"
+         "float a = 1, b = 2, c = 3;"
+         "float d = c;"
+         "float e = d;"
+         "b++;"
+         "d++;"
+         "sk_FragColor = vec4(b, b, d, d);"
+         "}",
+        *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    float b = 2.0;\n"
+         "    float d = 3.0;\n"
+         "    b++;\n"
+         "    d++;\n"
+         "    sk_FragColor = vec4(b, b, d, d);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLMultipleAssignments, r) {
+    test(r,
+         "void main() {"
+         "float x;"
+         "float y;"
+         "int z;"
+         "x = y = z = 1;"
+         "sk_FragColor = vec4(z);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(1.0);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLComplexDelete, r) {
+    test(r,
+         "uniform mat4 colorXform;"
+         "uniform sampler2D sampler;"
+         "void main() {"
+         "vec4 tmpColor;"
+         "sk_FragColor = vec4(1.0) * (tmpColor = texture(sampler, vec2(1)) , "
+         "colorXform != mat4(1.0) ? vec4(clamp((mat4(colorXform) * vec4(tmpColor.xyz, 1.0)).xyz, "
+         "0.0, tmpColor.w), tmpColor.w) : tmpColor);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "uniform mat4 colorXform;\n"
+         "uniform sampler2D sampler;\n"
+         "void main() {\n"
+         "    vec4 tmpColor;\n"
+         "    sk_FragColor = (tmpColor = texture(sampler, vec2(1.0)) , colorXform != mat4(1.0) ? "
+         "vec4(clamp((colorXform * vec4(tmpColor.xyz, 1.0)).xyz, 0.0, tmpColor.w), tmpColor.w) : "
+         "tmpColor);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLDependentInitializers, r) {
+    test(r,
+         "void main() {"
+         "float x = 0.5, y = x * 2;"
+         "sk_FragColor = vec4(y);"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(1.0);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLDeadLoopVar, r) {
+    test(r,
+         "void main() {"
+         "for (int x = 0; x < 4; ) {"
+         "break;"
+         "}"
+         "}",
+         *SkSL::ShaderCapsFactory::Default(),
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    for (; true; ) {\n"
+         "        break;\n"
+         "    }\n"
+         "}\n"
+         );
+}
+
+DEF_TEST(SkSLInvocations, r) {
+    test(r,
+         "layout(points) in;"
+         "layout(invocations = 2) in;"
+         "layout(line_strip, max_vertices = 2) out;"
+         "void test() {"
+         "gl_Position = sk_in[0].gl_Position + vec4(0.5, 0, 0, sk_InvocationID);"
+         "EmitVertex();"
+         "}"
+         "void main() {"
+         "gl_Position = sk_in[0].gl_Position + vec4(-0.5, 0, 0, sk_InvocationID);"
+         "EmitVertex();"
+         "}",
+         *SkSL::ShaderCapsFactory::MustImplementGSInvocationsWithLoop(),
+         "#version 400\n"
+         "int sk_InvocationID;\n"
+         "layout (points) in ;\n"
+         "layout (line_strip, max_vertices = 4) out ;\n"
+         "void test() {\n"
+         "    gl_Position = gl_in[0].gl_Position + vec4(0.5, 0.0, 0.0, float(sk_InvocationID));\n"
+         "    EmitVertex();\n"
+         "}\n"
+         "void _invoke() {\n"
+         "    gl_Position = gl_in[0].gl_Position + vec4(-0.5, 0.0, 0.0, float(sk_InvocationID));\n"
+         "    EmitVertex();\n"
+         "}\n"
+         "void main() {\n"
+         "    for (sk_InvocationID = 0;sk_InvocationID < 2; sk_InvocationID++) {\n"
+         "        _invoke();\n"
+         "        EndPrimitive();\n"
+         "    }\n"
+         "}\n",
+         SkSL::Program::kGeometry_Kind);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/SkSLMemoryLayoutTest.cpp b/src/third_party/skia/tests/SkSLMemoryLayoutTest.cpp
new file mode 100644
index 0000000..ff3d010
--- /dev/null
+++ b/src/third_party/skia/tests/SkSLMemoryLayoutTest.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSLContext.h"
+#include "SkSLMemoryLayout.h"
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+DEF_TEST(SkSLMemoryLayout140Test, r) {
+    SkSL::Context context;
+    SkSL::MemoryLayout layout(SkSL::MemoryLayout::k140_Standard);
+
+    // basic types
+    REPORTER_ASSERT(r,  4 == layout.size(*context.fFloat_Type));
+    REPORTER_ASSERT(r,  8 == layout.size(*context.fVec2_Type));
+    REPORTER_ASSERT(r, 12 == layout.size(*context.fVec3_Type));
+    REPORTER_ASSERT(r, 16 == layout.size(*context.fVec4_Type));
+    REPORTER_ASSERT(r,  4 == layout.size(*context.fInt_Type));
+    REPORTER_ASSERT(r,  8 == layout.size(*context.fIVec2_Type));
+    REPORTER_ASSERT(r, 12 == layout.size(*context.fIVec3_Type));
+    REPORTER_ASSERT(r, 16 == layout.size(*context.fIVec4_Type));
+    REPORTER_ASSERT(r,  1 == layout.size(*context.fBool_Type));
+    REPORTER_ASSERT(r,  2 == layout.size(*context.fBVec2_Type));
+    REPORTER_ASSERT(r,  3 == layout.size(*context.fBVec3_Type));
+    REPORTER_ASSERT(r,  4 == layout.size(*context.fBVec4_Type));
+    REPORTER_ASSERT(r, 32 == layout.size(*context.fMat2x2_Type));
+    REPORTER_ASSERT(r, 32 == layout.size(*context.fMat2x4_Type));
+    REPORTER_ASSERT(r, 48 == layout.size(*context.fMat3x3_Type));
+    REPORTER_ASSERT(r, 64 == layout.size(*context.fMat4x2_Type));
+    REPORTER_ASSERT(r, 64 == layout.size(*context.fMat4x4_Type));
+    REPORTER_ASSERT(r,  4 == layout.alignment(*context.fFloat_Type));
+    REPORTER_ASSERT(r,  8 == layout.alignment(*context.fVec2_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fVec3_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fVec4_Type));
+    REPORTER_ASSERT(r,  4 == layout.alignment(*context.fInt_Type));
+    REPORTER_ASSERT(r,  8 == layout.alignment(*context.fIVec2_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fIVec3_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fIVec4_Type));
+    REPORTER_ASSERT(r,  1 == layout.alignment(*context.fBool_Type));
+    REPORTER_ASSERT(r,  2 == layout.alignment(*context.fBVec2_Type));
+    REPORTER_ASSERT(r,  4 == layout.alignment(*context.fBVec3_Type));
+    REPORTER_ASSERT(r,  4 == layout.alignment(*context.fBVec4_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat2x2_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat2x4_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat3x3_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat4x2_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat4x4_Type));
+
+    // struct 1
+    std::vector<SkSL::Type::Field> fields1;
+    fields1.emplace_back(SkSL::Modifiers(), SkString("a"), context.fVec3_Type.get());
+    SkSL::Type s1(SkSL::Position(), SkString("s1"), fields1);
+    REPORTER_ASSERT(r, 16 == layout.size(s1));
+    REPORTER_ASSERT(r, 16 == layout.alignment(s1));
+
+    fields1.emplace_back(SkSL::Modifiers(), SkString("b"), context.fFloat_Type.get());
+    SkSL::Type s2(SkSL::Position(), SkString("s2"), fields1);
+    REPORTER_ASSERT(r, 16 == layout.size(s2));
+    REPORTER_ASSERT(r, 16 == layout.alignment(s2));
+
+    fields1.emplace_back(SkSL::Modifiers(), SkString("c"), context.fBool_Type.get());
+    SkSL::Type s3(SkSL::Position(), SkString("s3"), fields1);
+    REPORTER_ASSERT(r, 32 == layout.size(s3));
+    REPORTER_ASSERT(r, 16 == layout.alignment(s3));
+
+    // struct 2
+    std::vector<SkSL::Type::Field> fields2;
+    fields2.emplace_back(SkSL::Modifiers(), SkString("a"), context.fInt_Type.get());
+    SkSL::Type s4(SkSL::Position(), SkString("s4"), fields2);
+    REPORTER_ASSERT(r, 16 == layout.size(s4));
+    REPORTER_ASSERT(r, 16 == layout.alignment(s4));
+
+    fields2.emplace_back(SkSL::Modifiers(), SkString("b"), context.fVec3_Type.get());
+    SkSL::Type s5(SkSL::Position(), SkString("s5"), fields2);
+    REPORTER_ASSERT(r, 32 == layout.size(s5));
+    REPORTER_ASSERT(r, 16 == layout.alignment(s5));
+
+    // arrays
+    SkSL::Type array1(SkString("float[4]"), SkSL::Type::kArray_Kind, *context.fFloat_Type, 4);
+    REPORTER_ASSERT(r, 64 == layout.size(array1));
+    REPORTER_ASSERT(r, 16 == layout.alignment(array1));
+    REPORTER_ASSERT(r, 16 == layout.stride(array1));
+
+    SkSL::Type array2(SkString("vec4[4]"), SkSL::Type::kArray_Kind, *context.fVec4_Type, 4);
+    REPORTER_ASSERT(r, 64 == layout.size(array2));
+    REPORTER_ASSERT(r, 16 == layout.alignment(array2));
+    REPORTER_ASSERT(r, 16 == layout.stride(array2));
+}
+
+DEF_TEST(SkSLMemoryLayout430Test, r) {
+    SkSL::Context context;
+    SkSL::MemoryLayout layout(SkSL::MemoryLayout::k430_Standard);
+
+    // basic types
+    REPORTER_ASSERT(r,  4 == layout.size(*context.fFloat_Type));
+    REPORTER_ASSERT(r,  8 == layout.size(*context.fVec2_Type));
+    REPORTER_ASSERT(r, 12 == layout.size(*context.fVec3_Type));
+    REPORTER_ASSERT(r, 16 == layout.size(*context.fVec4_Type));
+    REPORTER_ASSERT(r,  4 == layout.size(*context.fInt_Type));
+    REPORTER_ASSERT(r,  8 == layout.size(*context.fIVec2_Type));
+    REPORTER_ASSERT(r, 12 == layout.size(*context.fIVec3_Type));
+    REPORTER_ASSERT(r, 16 == layout.size(*context.fIVec4_Type));
+    REPORTER_ASSERT(r,  1 == layout.size(*context.fBool_Type));
+    REPORTER_ASSERT(r,  2 == layout.size(*context.fBVec2_Type));
+    REPORTER_ASSERT(r,  3 == layout.size(*context.fBVec3_Type));
+    REPORTER_ASSERT(r,  4 == layout.size(*context.fBVec4_Type));
+    REPORTER_ASSERT(r, 16 == layout.size(*context.fMat2x2_Type));
+    REPORTER_ASSERT(r, 32 == layout.size(*context.fMat2x4_Type));
+    REPORTER_ASSERT(r, 48 == layout.size(*context.fMat3x3_Type));
+    REPORTER_ASSERT(r, 32 == layout.size(*context.fMat4x2_Type));
+    REPORTER_ASSERT(r, 64 == layout.size(*context.fMat4x4_Type));
+    REPORTER_ASSERT(r,  4 == layout.alignment(*context.fFloat_Type));
+    REPORTER_ASSERT(r,  8 == layout.alignment(*context.fVec2_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fVec3_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fVec4_Type));
+    REPORTER_ASSERT(r,  4 == layout.alignment(*context.fInt_Type));
+    REPORTER_ASSERT(r,  8 == layout.alignment(*context.fIVec2_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fIVec3_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fIVec4_Type));
+    REPORTER_ASSERT(r,  1 == layout.alignment(*context.fBool_Type));
+    REPORTER_ASSERT(r,  2 == layout.alignment(*context.fBVec2_Type));
+    REPORTER_ASSERT(r,  4 == layout.alignment(*context.fBVec3_Type));
+    REPORTER_ASSERT(r,  4 == layout.alignment(*context.fBVec4_Type));
+    REPORTER_ASSERT(r,  8 == layout.alignment(*context.fMat2x2_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat2x4_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat3x3_Type));
+    REPORTER_ASSERT(r,  8 == layout.alignment(*context.fMat4x2_Type));
+    REPORTER_ASSERT(r, 16 == layout.alignment(*context.fMat4x4_Type));
+
+    // struct 1
+    std::vector<SkSL::Type::Field> fields1;
+    fields1.emplace_back(SkSL::Modifiers(), SkString("a"), context.fVec3_Type.get());
+    SkSL::Type s1(SkSL::Position(), SkString("s1"), fields1);
+    REPORTER_ASSERT(r, 16 == layout.size(s1));
+    REPORTER_ASSERT(r, 16 == layout.alignment(s1));
+
+    fields1.emplace_back(SkSL::Modifiers(), SkString("b"), context.fFloat_Type.get());
+    SkSL::Type s2(SkSL::Position(), SkString("s2"), fields1);
+    REPORTER_ASSERT(r, 16 == layout.size(s2));
+    REPORTER_ASSERT(r, 16 == layout.alignment(s2));
+
+    fields1.emplace_back(SkSL::Modifiers(), SkString("c"), context.fBool_Type.get());
+    SkSL::Type s3(SkSL::Position(), SkString("s3"), fields1);
+    REPORTER_ASSERT(r, 32 == layout.size(s3));
+    REPORTER_ASSERT(r, 16 == layout.alignment(s3));
+
+    // struct 2
+    std::vector<SkSL::Type::Field> fields2;
+    fields2.emplace_back(SkSL::Modifiers(), SkString("a"), context.fInt_Type.get());
+    SkSL::Type s4(SkSL::Position(), SkString("s4"), fields2);
+    REPORTER_ASSERT(r, 4 == layout.size(s4));
+    REPORTER_ASSERT(r, 4 == layout.alignment(s4));
+
+    fields2.emplace_back(SkSL::Modifiers(), SkString("b"), context.fVec3_Type.get());
+    SkSL::Type s5(SkSL::Position(), SkString("s5"), fields2);
+    REPORTER_ASSERT(r, 32 == layout.size(s5));
+    REPORTER_ASSERT(r, 16 == layout.alignment(s5));
+
+    // arrays
+    SkSL::Type array1(SkString("float[4]"), SkSL::Type::kArray_Kind, *context.fFloat_Type, 4);
+    REPORTER_ASSERT(r, 16 == layout.size(array1));
+    REPORTER_ASSERT(r, 4 == layout.alignment(array1));
+    REPORTER_ASSERT(r, 4 == layout.stride(array1));
+
+    SkSL::Type array2(SkString("vec4[4]"), SkSL::Type::kArray_Kind, *context.fVec4_Type, 4);
+    REPORTER_ASSERT(r, 64 == layout.size(array2));
+    REPORTER_ASSERT(r, 16 == layout.alignment(array2));
+    REPORTER_ASSERT(r, 16 == layout.stride(array2));
+}
+#endif
diff --git a/src/third_party/skia/tests/SkSLSPIRVTest.cpp b/src/third_party/skia/tests/SkSLSPIRVTest.cpp
new file mode 100644
index 0000000..4d5457f
--- /dev/null
+++ b/src/third_party/skia/tests/SkSLSPIRVTest.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSLCompiler.h"
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+static void test_failure(skiatest::Reporter* r, const char* src, const char* error) {
+    SkSL::Compiler compiler;
+    SkSL::Program::Settings settings;
+    sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
+    settings.fCaps = caps.get();
+    std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kFragment_Kind,
+                                                                     SkString(src), settings);
+    if (program) {
+        SkSL::String ignored;
+        compiler.toSPIRV(*program, &ignored);
+    }
+    SkSL::String skError(error);
+    if (compiler.errorText() != skError) {
+        SkDebugf("SKSL ERROR:\n    source: %s\n    expected: %s    received: %s", src, error,
+                 compiler.errorText().c_str());
+    }
+    REPORTER_ASSERT(r, compiler.errorText() == skError);
+}
+
+DEF_TEST(SkSLBadOffset, r) {
+    test_failure(r,
+                 "struct Bad { layout (offset = 5) int x; } bad; void main() { bad.x = 5; }",
+                 "error: 1: offset of field 'x' must be a multiple of 4\n1 error\n");
+    test_failure(r,
+                 "struct Bad { int x; layout (offset = 0) int y; } bad; void main() { bad.x = 5; }",
+                 "error: 1: offset of field 'y' must be at least 4\n1 error\n");
+}
+
+#endif
diff --git a/src/third_party/skia/tests/SkSharedMutexTest.cpp b/src/third_party/skia/tests/SkSharedMutexTest.cpp
new file mode 100644
index 0000000..8458891
--- /dev/null
+++ b/src/third_party/skia/tests/SkSharedMutexTest.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSharedMutex.h"
+#include "SkTaskGroup.h"
+
+#include "Test.h"
+
+DEF_TEST(SkSharedMutexBasic, r) {
+    SkSharedMutex sm;
+    sm.acquire();
+    sm.assertHeld();
+    sm.release();
+    sm.acquireShared();
+    sm.assertHeldShared();
+    sm.releaseShared();
+}
+
+DEF_TEST(SkSharedMutexMultiThreaded, r) {
+    SkSharedMutex sm;
+    static const int kSharedSize = 10;
+    int shared[kSharedSize];
+    int value = 0;
+    for (int i = 0; i < kSharedSize; ++i) {
+        shared[i] = 0;
+    }
+    SkTaskGroup().batch(8, [&](int threadIndex) {
+        if (threadIndex % 4 != 0) {
+            for (int c = 0; c < 100000; ++c) {
+                sm.acquireShared();
+                sm.assertHeldShared();
+                int v = shared[0];
+                for (int i = 1; i < kSharedSize; ++i) {
+                    REPORTER_ASSERT(r, v == shared[i]);
+                }
+                sm.releaseShared();
+            }
+        } else {
+            for (int c = 0; c < 100000; ++c) {
+                sm.acquire();
+                sm.assertHeld();
+                value += 1;
+                for (int i = 0; i < kSharedSize; ++i) {
+                    shared[i] = value;
+                }
+                sm.release();
+            }
+        }
+    });
+}
diff --git a/src/third_party/skia/tests/SkpSkGrTest.cpp b/src/third_party/skia/tests/SkpSkGrTest.cpp
index c882654..8280e14 100644
--- a/src/third_party/skia/tests/SkpSkGrTest.cpp
+++ b/src/third_party/skia/tests/SkpSkGrTest.cpp
@@ -1,24 +1,22 @@
-#if !SK_SUPPORT_GPU
-#error "GPU support required"
-#endif
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
 
 #include "GrContext.h"
 #include "GrContextFactory.h"
-#include "GrRenderTarget.h"
 #include "SkGpuDevice.h"
 #include "gl/GrGLDefines.h"
 
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkColor.h"
-#include "SkDevice.h"
 #include "SkGraphics.h"
-#include "SkImageDecoder.h"
 #include "SkImageEncoder.h"
 #include "SkOSFile.h"
 #include "SkPicture.h"
-#include "SkRTConf.h"
-#include "SkRunnable.h"
 #include "SkStream.h"
 #include "SkString.h"
 #include "SkTArray.h"
@@ -27,6 +25,10 @@
 #include "SkTime.h"
 #include "Test.h"
 
+#if !SK_SUPPORT_GPU
+#error "GPU support required"
+#endif
+
 #ifdef SK_BUILD_FOR_WIN
     #define PATH_SLASH "\\"
     #define IN_DIR "D:\\9-30-13\\"
@@ -101,7 +103,7 @@
     TestStep fTestStep;
     int fDirNo;
     int fPixelError;
-    int fTime;
+    SkMSec fTime;
     bool fScaleOversized;
 };
 
@@ -135,7 +137,7 @@
     skiatest::Reporter* fReporter;
 };
 
-class SkpSkGrThreadedRunnable : public SkRunnable {
+class SkpSkGrThreadedRunnable {
 public:
     SkpSkGrThreadedRunnable(void (*testFun)(SkpSkGrThreadState*), int dirNo, const char* str,
             SkpSkGrThreadedTestRunner* runner) {
@@ -146,7 +148,7 @@
         fTestFun = testFun;
     }
 
-    virtual void run() SK_OVERRIDE {
+    void operator()() {
         SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
         (*fTestFun)(&fState);
     }
@@ -157,15 +159,14 @@
 
 SkpSkGrThreadedTestRunner::~SkpSkGrThreadedTestRunner() {
     for (int index = 0; index < fRunnables.count(); index++) {
-        SkDELETE(fRunnables[index]);
+        delete fRunnables[index];
     }
 }
 
 void SkpSkGrThreadedTestRunner::render() {
-    SkTaskGroup tg;
-    for (int index = 0; index < fRunnables.count(); ++ index) {
-        tg.add(fRunnables[index]);
-    }
+    SkTaskGroup().batch(fRunnables.count(), [&](int i) {
+        fRunnables[i]();
+    });
 }
 
 ////////////////////////////////////////////////
@@ -249,11 +250,11 @@
     return pngName;
 }
 
-typedef GrContextFactory::GLContextType GLContextType;
+typedef GrContextFactory::ContextType ContextType;
 #ifdef SK_BUILD_FOR_WIN
-static const GLContextType kAngle = GrContextFactory::kANGLE_GLContextType;
+static const ContextType kAngle = GrContextFactory::kANGLE_ContextType;
 #else
-static const GLContextType kNative = GrContextFactory::kNative_GLContextType;
+static const ContextType kNative = GrContextFactory::kNativeGL_ContextType;
 #endif
 
 static int similarBits(const SkBitmap& gr, const SkBitmap& sk) {
@@ -339,7 +340,7 @@
     SkRect rect = {0, 0, SkIntToScalar(SkTMin(maxDimension, pWidth)),
             SkIntToScalar(SkTMin(maxDimension, pHeight))};
     canvas->clipRect(rect);
-    SkMSec start = SkTime::GetMSecs();
+    skiatest::Timer timer;
     for (int x = 0; x < slices; ++x) {
         for (int y = 0; y < slices; ++y) {
             pic->draw(canvas);
@@ -347,9 +348,9 @@
         }
         canvas->translate(SkIntToScalar(xInterval), SkIntToScalar(-yInterval * slices));
     }
-    SkMSec end = SkTime::GetMSecs();
+    SkMSec elapsed = timer.elapsedMsInt();
     canvas->restore();
-    return end - start;
+    return elapsed;
 }
 
 static void drawPict(SkPicture* pic, SkCanvas* canvas, int scale) {
@@ -366,15 +367,15 @@
 
 static void writePict(const SkBitmap& bitmap, const char* outDir, const char* pngName) {
     SkString outFile = make_filepath(0, outDir, pngName);
-    if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap,
-            SkImageEncoder::kPNG_Type, 100)) {
+    if (!sk_tool_utils::EncodeImageToFile(outFile.c_str(), bitmap,
+            SkEncodedImageFormat::kPNG, 100)) {
         SkDebugf("unable to encode gr %s (width=%d height=%d)br \n", pngName,
                     bitmap.width(), bitmap.height());
     }
 }
 
 void TestResult::testOne() {
-    SkPicture* pic = NULL;
+    sk_sp<SkPicture> pic;
     {
         SkString d;
         d.printf("    {%d, \"%s\"},", fDirNo, fFilename);
@@ -395,7 +396,7 @@
             wStream.write(&bytes[0], length);
             wStream.flush();
         }
-        pic = SkPicture::CreateFromStream(&stream, &SkImageDecoder::DecodeMemory);
+        pic = SkPicture::MakeFromStream(&stream);
         if (!pic) {
             SkDebugf("unable to decode %s\n", fFilename);
             goto finish;
@@ -409,7 +410,7 @@
 #else
         GrContext* context = contextFactory.get(kNative);
 #endif
-        if (NULL == context) {
+        if (nullptr == context) {
             SkDebugf("unable to allocate context for %s\n", fFilename);
             goto finish;
         }
@@ -432,25 +433,25 @@
         if (scale >= 256) {
             SkDebugf("unable to allocate bitmap for %s (w=%d h=%d) (sw=%d sh=%d)\n",
                     fFilename, pWidth, pHeight, dim.fX, dim.fY);
-            goto finish;
+            return;
         }
         SkCanvas skCanvas(bitmap);
         drawPict(pic, &skCanvas, fScaleOversized ? scale : 1);
         GrTextureDesc desc;
-        desc.fConfig = kSkia8888_GrPixelConfig;
+        desc.fConfig = kRGBA_8888_GrPixelConfig;
         desc.fFlags = kRenderTarget_GrTextureFlagBit;
         desc.fWidth = dim.fX;
         desc.fHeight = dim.fY;
         desc.fSampleCnt = 0;
-        SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0));
+        sk_sp<GrTexture> texture(context->createUncachedTexture(desc, nullptr, 0));
         if (!texture) {
             SkDebugf("unable to allocate texture for %s (w=%d h=%d)\n", fFilename,
                 dim.fX, dim.fY);
-            goto finish;
+            return;
         }
         SkGpuDevice grDevice(context, texture.get());
         SkCanvas grCanvas(&grDevice);
-        drawPict(pic, &grCanvas, fScaleOversized ? scale : 1);
+        drawPict(pic.get(), &grCanvas, fScaleOversized ? scale : 1);
 
         SkBitmap grBitmap;
         grBitmap.allocPixels(grCanvas.imageInfo());
@@ -458,8 +459,8 @@
 
         if (fTestStep == kCompareBits) {
             fPixelError = similarBits(grBitmap, bitmap);
-            int skTime = timePict(pic, &skCanvas);
-            int grTime = timePict(pic, &grCanvas);
+            SkMSec skTime = timePict(pic, &skCanvas);
+            SkMSec grTime = timePict(pic, &grCanvas);
             fTime = skTime - grTime;
         } else if (fTestStep == kEncodeFiles) {
             SkString pngStr = make_png_name(fFilename);
@@ -468,8 +469,6 @@
             writePict(bitmap, outSkDir, pngName);
         }
     }
-finish:
-    SkDELETE(pic);
 }
 
 static SkString makeStatusString(int dirNo) {
@@ -566,10 +565,6 @@
 };
 
 static bool initTest() {
-#if !defined SK_BUILD_FOR_WIN && !defined SK_BUILD_FOR_MAC
-    SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true);
-    SK_CONF_SET("images.png.suppressDecoderWarnings", true);
-#endif
     return make_out_dirs();
 }
 
@@ -697,8 +692,8 @@
                     goto skipOver;
                 }
             }
-            *testRunner.fRunnables.append() = SkNEW_ARGS(SkpSkGrThreadedRunnable,
-                    (&testSkGrMain, dirIndex, filename.c_str(), &testRunner));
+            *testRunner.fRunnables.append() = new SkpSkGrThreadedRunnable(
+                    &testSkGrMain, dirIndex, filename.c_str(), &testRunner);
     skipOver:
             ;
         }
diff --git a/src/third_party/skia/tests/SmallAllocatorTest.cpp b/src/third_party/skia/tests/SmallAllocatorTest.cpp
deleted file mode 100644
index 30c8ffa..0000000
--- a/src/third_party/skia/tests/SmallAllocatorTest.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2014 Google, Inc
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkSmallAllocator.h"
-#include "SkTypes.h"
-#include "Test.h"
-
-class CountingClass {
-public:
-    CountingClass() {
-        kCount++;
-    }
-
-    ~CountingClass() {
-        kCount--;
-    }
-
-    static int GetCount() { return kCount; }
-
-private:
-    static int kCount;
-};
-
-int CountingClass::kCount;
-
-template<uint32_t kMaxObjects, size_t kBytes> void test_allocator(skiatest::Reporter* reporter) {
-    {
-        SkSmallAllocator<kMaxObjects, kBytes> alloc;
-        for (uint32_t i = 0; i < kMaxObjects; ++i) {
-            CountingClass* c = alloc.template createT<CountingClass>();
-            REPORTER_ASSERT(reporter, c != NULL);
-            REPORTER_ASSERT(reporter, CountingClass::GetCount() == static_cast<int>(i+1));
-        }
-    }
-    REPORTER_ASSERT(reporter, CountingClass::GetCount() == 0);
-}
-
-// Tests that ensure that the destructor is called, whether the objects
-// were created in fStorage or on the heap.
-DEF_TEST(SmallAllocator_destructor, reporter) {
-    // Four times as many bytes as objects will never require any heap
-    // allocations (since SkAlign4(sizeof(CountingClass)) == 4 and the allocator
-    // will stop once it reaches kMaxObjects).
-    test_allocator<5, 20>(reporter);
-    test_allocator<10, 40>(reporter);
-    test_allocator<20, 80>(reporter);
-
-#ifndef SK_DEBUG
-    // Allowing less bytes than objects means some will be allocated on the
-    // heap. Don't run these in debug where we assert.
-    test_allocator<50, 20>(reporter);
-    test_allocator<100, 20>(reporter);
-#endif
-}
-
-class Dummy {
-};
-
-class DummyContainer {
-public:
-    explicit DummyContainer(Dummy* d)
-        :fDummy(d)
-    {}
-
-    Dummy* getDummy() const { return fDummy; }
-
-private:
-    Dummy* fDummy;
-};
-
-// Test that using a createT with a constructor taking a pointer as a
-// parameter works as expected.
-DEF_TEST(SmallAllocator_pointer, reporter) {
-    SkSmallAllocator<1, 8> alloc;
-    Dummy d;
-    DummyContainer* container = alloc.createT<DummyContainer>(&d);
-    REPORTER_ASSERT(reporter, container != NULL);
-    REPORTER_ASSERT(reporter, container->getDummy() == &d);
-}
diff --git a/src/third_party/skia/tests/SortTest.cpp b/src/third_party/skia/tests/SortTest.cpp
index e8713dd..d544ebd 100644
--- a/src/third_party/skia/tests/SortTest.cpp
+++ b/src/third_party/skia/tests/SortTest.cpp
@@ -9,6 +9,8 @@
 #include "SkTSort.h"
 #include "Test.h"
 
+#include <stdlib.h>
+
 extern "C" {
     static int compare_int(const void* a, const void* b) {
         return *(const int*)a - *(const int*)b;
diff --git a/src/third_party/skia/tests/SpecialImageTest.cpp b/src/third_party/skia/tests/SpecialImageTest.cpp
new file mode 100644
index 0000000..70d6fd4
--- /dev/null
+++ b/src/third_party/skia/tests/SpecialImageTest.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file
+ */
+
+#include "SkAutoPixmapStorage.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkImage.h"
+#include "SkPixmap.h"
+#include "SkSpecialImage.h"
+#include "SkSpecialSurface.h"
+#include "SkSurface.h"
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrSurfaceProxy.h"
+#include "GrTextureProxy.h"
+#include "SkGr.h"
+#endif
+
+
+// This test creates backing resources exactly sized to [kFullSize x kFullSize].
+// It then wraps them in an SkSpecialImage with only the center (red) region being active.
+// It then draws the SkSpecialImage to a full sized (all blue) canvas and checks that none
+// of the inactive (green) region leaked out.
+
+static const int kSmallerSize = 10;
+static const int kPad = 3;
+static const int kFullSize = kSmallerSize + 2 * kPad;
+
+// Create a bitmap with red in the center and green around it
+static SkBitmap create_bm() {
+    SkBitmap bm;
+    bm.allocN32Pixels(kFullSize, kFullSize, true);
+
+    SkCanvas temp(bm);
+
+    temp.clear(SK_ColorGREEN);
+    SkPaint p;
+    p.setColor(SK_ColorRED);
+    p.setAntiAlias(false);
+
+    temp.drawRect(SkRect::MakeXYWH(SkIntToScalar(kPad), SkIntToScalar(kPad),
+                                   SkIntToScalar(kSmallerSize), SkIntToScalar(kSmallerSize)),
+                  p);
+
+    return bm;
+}
+
+// Basic test of the SkSpecialImage public API (e.g., peekTexture, peekPixels & draw)
+static void test_image(const sk_sp<SkSpecialImage>& img, skiatest::Reporter* reporter,
+                       GrContext* context, bool isGPUBacked,
+                       int offset, int size) {
+    const SkIRect subset = img->subset();
+    REPORTER_ASSERT(reporter, offset == subset.left());
+    REPORTER_ASSERT(reporter, offset == subset.top());
+    REPORTER_ASSERT(reporter, kSmallerSize == subset.width());
+    REPORTER_ASSERT(reporter, kSmallerSize == subset.height());
+
+    //--------------
+    // Test that isTextureBacked reports the correct backing type
+    REPORTER_ASSERT(reporter, isGPUBacked == img->isTextureBacked());
+
+#if SK_SUPPORT_GPU
+    //--------------
+    // Test asTextureProxyRef - as long as there is a context this should succeed
+    if (context) {
+        sk_sp<GrTextureProxy> proxy(img->asTextureProxyRef(context));
+        REPORTER_ASSERT(reporter, proxy);
+    }
+#endif
+
+    //--------------
+    // Test getROPixels - this should always succeed regardless of backing store
+    SkBitmap bitmap;
+    REPORTER_ASSERT(reporter, img->getROPixels(&bitmap));
+    if (context) {
+        REPORTER_ASSERT(reporter, kSmallerSize == bitmap.width());
+        REPORTER_ASSERT(reporter, kSmallerSize == bitmap.height());
+    } else {
+        REPORTER_ASSERT(reporter, size == bitmap.width());
+        REPORTER_ASSERT(reporter, size == bitmap.height());
+    }
+
+    //--------------
+    // Test that draw restricts itself to the subset
+    SkImageFilter::OutputProperties outProps(img->getColorSpace());
+    sk_sp<SkSpecialSurface> surf(img->makeSurface(outProps, SkISize::Make(kFullSize, kFullSize),
+                                                  kPremul_SkAlphaType));
+
+    SkCanvas* canvas = surf->getCanvas();
+
+    canvas->clear(SK_ColorBLUE);
+    img->draw(canvas, SkIntToScalar(kPad), SkIntToScalar(kPad), nullptr);
+
+    SkBitmap bm;
+    bm.allocN32Pixels(kFullSize, kFullSize, false);
+
+    bool result = canvas->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), 0, 0);
+    SkASSERT_RELEASE(result);
+
+    // Only the center (red) portion should've been drawn into the canvas
+    REPORTER_ASSERT(reporter, SK_ColorBLUE == bm.getColor(kPad-1, kPad-1));
+    REPORTER_ASSERT(reporter, SK_ColorRED  == bm.getColor(kPad, kPad));
+    REPORTER_ASSERT(reporter, SK_ColorRED  == bm.getColor(kSmallerSize+kPad-1,
+                                                          kSmallerSize+kPad-1));
+    REPORTER_ASSERT(reporter, SK_ColorBLUE == bm.getColor(kSmallerSize+kPad,
+                                                          kSmallerSize+kPad));
+
+    //--------------
+    // Test that asImage & makeTightSurface return appropriately sized objects
+    // of the correct backing type
+    SkIRect newSubset = SkIRect::MakeWH(subset.width(), subset.height());
+    {
+        sk_sp<SkImage> tightImg(img->asImage(&newSubset));
+
+        REPORTER_ASSERT(reporter, tightImg->width() == subset.width());
+        REPORTER_ASSERT(reporter, tightImg->height() == subset.height());
+        REPORTER_ASSERT(reporter, isGPUBacked == tightImg->isTextureBacked());
+        SkPixmap tmpPixmap;
+        REPORTER_ASSERT(reporter, isGPUBacked != !!tightImg->peekPixels(&tmpPixmap));
+    }
+    {
+        SkImageFilter::OutputProperties outProps(img->getColorSpace());
+        sk_sp<SkSurface> tightSurf(img->makeTightSurface(outProps, subset.size()));
+
+        REPORTER_ASSERT(reporter, tightSurf->width() == subset.width());
+        REPORTER_ASSERT(reporter, tightSurf->height() == subset.height());
+        REPORTER_ASSERT(reporter, isGPUBacked ==
+                     !!tightSurf->getTextureHandle(SkSurface::kDiscardWrite_BackendHandleAccess));
+        SkPixmap tmpPixmap;
+        REPORTER_ASSERT(reporter, isGPUBacked != !!tightSurf->peekPixels(&tmpPixmap));
+    }
+}
+
+DEF_TEST(SpecialImage_Raster, reporter) {
+    SkBitmap bm = create_bm();
+
+    sk_sp<SkSpecialImage> fullSImage(SkSpecialImage::MakeFromRaster(
+                                                            SkIRect::MakeWH(kFullSize, kFullSize),
+                                                            bm));
+
+    const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
+
+    {
+        sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeFromRaster(subset, bm));
+        test_image(subSImg1, reporter, nullptr, false, kPad, kFullSize);
+    }
+
+    {
+        sk_sp<SkSpecialImage> subSImg2(fullSImage->makeSubset(subset));
+        test_image(subSImg2, reporter, nullptr, false, 0, kSmallerSize);
+    }
+}
+
+static void test_specialimage_image(skiatest::Reporter* reporter, SkColorSpace* dstColorSpace) {
+    SkBitmap bm = create_bm();
+
+    sk_sp<SkImage> fullImage(SkImage::MakeFromBitmap(bm));
+
+    sk_sp<SkSpecialImage> fullSImage(SkSpecialImage::MakeFromImage(
+                                                            SkIRect::MakeWH(kFullSize, kFullSize),
+                                                            fullImage, dstColorSpace));
+
+    const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
+
+    {
+        sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeFromImage(subset, fullImage,
+                                                                     dstColorSpace));
+        test_image(subSImg1, reporter, nullptr, false, kPad, kFullSize);
+    }
+
+    {
+        sk_sp<SkSpecialImage> subSImg2(fullSImage->makeSubset(subset));
+        test_image(subSImg2, reporter, nullptr, false, 0, kSmallerSize);
+    }
+}
+
+DEF_TEST(SpecialImage_Image_Legacy, reporter) {
+    SkColorSpace* legacyColorSpace = nullptr;
+    test_specialimage_image(reporter, legacyColorSpace);
+}
+
+DEF_TEST(SpecialImage_Image_ColorSpaceAware, reporter) {
+    sk_sp<SkColorSpace> srgbColorSpace = SkColorSpace::MakeSRGB();
+    test_specialimage_image(reporter, srgbColorSpace.get());
+}
+
+#if SK_SUPPORT_GPU
+
+static void test_texture_backed(skiatest::Reporter* reporter,
+                                const sk_sp<SkSpecialImage>& orig,
+                                const sk_sp<SkSpecialImage>& gpuBacked) {
+    REPORTER_ASSERT(reporter, gpuBacked);
+    REPORTER_ASSERT(reporter, gpuBacked->isTextureBacked());
+    REPORTER_ASSERT(reporter, gpuBacked->uniqueID() == orig->uniqueID());
+    REPORTER_ASSERT(reporter, gpuBacked->subset().width() == orig->subset().width() &&
+                              gpuBacked->subset().height() == orig->subset().height());
+    REPORTER_ASSERT(reporter, gpuBacked->getColorSpace() == orig->getColorSpace());
+}
+
+// Test out the SkSpecialImage::makeTextureImage entry point
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_MakeTexture, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    SkBitmap bm = create_bm();
+
+    const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
+
+    {
+        // raster
+        sk_sp<SkSpecialImage> rasterImage(SkSpecialImage::MakeFromRaster(
+                                                                        SkIRect::MakeWH(kFullSize,
+                                                                                        kFullSize),
+                                                                        bm));
+
+        {
+            sk_sp<SkSpecialImage> fromRaster(rasterImage->makeTextureImage(context));
+            test_texture_backed(reporter, rasterImage, fromRaster);
+        }
+
+        {
+            sk_sp<SkSpecialImage> subRasterImage(rasterImage->makeSubset(subset));
+
+            sk_sp<SkSpecialImage> fromSubRaster(subRasterImage->makeTextureImage(context));
+            test_texture_backed(reporter, subRasterImage, fromSubRaster);
+        }
+    }
+
+    {
+        // gpu
+        const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps());
+
+        sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                                 desc, SkBudgeted::kNo,
+                                                                 bm.getPixels(), bm.rowBytes()));
+        if (!proxy) {
+            return;
+        }
+
+        sk_sp<SkSpecialImage> gpuImage(SkSpecialImage::MakeDeferredFromGpu(
+                                                            context,
+                                                            SkIRect::MakeWH(kFullSize, kFullSize),
+                                                            kNeedNewImageUniqueID_SpecialImage,
+                                                            std::move(proxy), nullptr));
+
+        {
+            sk_sp<SkSpecialImage> fromGPU(gpuImage->makeTextureImage(context));
+            test_texture_backed(reporter, gpuImage, fromGPU);
+        }
+
+        {
+            sk_sp<SkSpecialImage> subGPUImage(gpuImage->makeSubset(subset));
+
+            sk_sp<SkSpecialImage> fromSubGPU(subGPUImage->makeTextureImage(context));
+            test_texture_backed(reporter, subGPUImage, fromSubGPU);
+        }
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_Gpu, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    SkBitmap bm = create_bm();
+
+    const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps());
+
+    sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                             desc, SkBudgeted::kNo,
+                                                             bm.getPixels(), bm.rowBytes()));
+    if (!proxy) {
+        return;
+    }
+
+    sk_sp<SkSpecialImage> fullSImg(SkSpecialImage::MakeDeferredFromGpu(
+                                                            context,
+                                                            SkIRect::MakeWH(kFullSize, kFullSize),
+                                                            kNeedNewImageUniqueID_SpecialImage,
+                                                            proxy, nullptr));
+
+    const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
+
+    {
+        sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeDeferredFromGpu(
+                                                               context, subset,
+                                                               kNeedNewImageUniqueID_SpecialImage,
+                                                               std::move(proxy), nullptr));
+        test_image(subSImg1, reporter, context, true, kPad, kFullSize);
+    }
+
+    {
+        sk_sp<SkSpecialImage> subSImg2(fullSImg->makeSubset(subset));
+        test_image(subSImg2, reporter, context, true, kPad, kFullSize);
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_DeferredGpu, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    SkBitmap bm = create_bm();
+
+    GrSurfaceDesc desc;
+    desc.fConfig = kSkia8888_GrPixelConfig;
+    desc.fFlags  = kNone_GrSurfaceFlags;
+    desc.fWidth  = kFullSize;
+    desc.fHeight = kFullSize;
+
+    sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                             desc, SkBudgeted::kNo,
+                                                             bm.getPixels(), 0));
+    if (!proxy) {
+        return;
+    }
+
+    sk_sp<SkSpecialImage> fullSImg(SkSpecialImage::MakeDeferredFromGpu(
+                                                            context,
+                                                            SkIRect::MakeWH(kFullSize, kFullSize),
+                                                            kNeedNewImageUniqueID_SpecialImage,
+                                                            proxy, nullptr));
+
+    const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
+
+    {
+        sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeDeferredFromGpu(
+                                                               context, subset,
+                                                               kNeedNewImageUniqueID_SpecialImage,
+                                                               std::move(proxy), nullptr));
+        test_image(subSImg1, reporter, context, true, kPad, kFullSize);
+    }
+
+    {
+        sk_sp<SkSpecialImage> subSImg2(fullSImg->makeSubset(subset));
+        test_image(subSImg2, reporter, context, true, kPad, kFullSize);
+    }
+}
+
+#endif
diff --git a/src/third_party/skia/tests/SpecialSurfaceTest.cpp b/src/third_party/skia/tests/SpecialSurfaceTest.cpp
new file mode 100644
index 0000000..a336cf6
--- /dev/null
+++ b/src/third_party/skia/tests/SpecialSurfaceTest.cpp
@@ -0,0 +1,91 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file
+*/
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkSpecialImage.h"
+#include "SkSpecialSurface.h"
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "SkGr.h"
+#endif
+
+class TestingSpecialSurfaceAccess {
+public:
+    static const SkIRect& Subset(const SkSpecialSurface* surf) {
+        return surf->subset();
+    }
+};
+
+// Both 'kSmallerSize' and 'kFullSize' need to be a non-power-of-2 to exercise
+// the gpu's loose fit behavior
+static const int kSmallerSize = 10;
+static const int kPad = 5;
+static const int kFullSize = kSmallerSize + 2 * kPad;
+
+// Exercise the public API of SkSpecialSurface (e.g., getCanvas, newImageSnapshot)
+static void test_surface(const sk_sp<SkSpecialSurface>& surf,
+                         skiatest::Reporter* reporter,
+                         int offset) {
+
+    const SkIRect surfSubset = TestingSpecialSurfaceAccess::Subset(surf.get());
+    REPORTER_ASSERT(reporter, offset == surfSubset.fLeft);
+    REPORTER_ASSERT(reporter, offset == surfSubset.fTop);
+    REPORTER_ASSERT(reporter, kSmallerSize == surfSubset.width());
+    REPORTER_ASSERT(reporter, kSmallerSize == surfSubset.height());
+
+    SkCanvas* canvas = surf->getCanvas();
+    SkASSERT_RELEASE(canvas);
+
+    canvas->clear(SK_ColorRED);
+
+    sk_sp<SkSpecialImage> img(surf->makeImageSnapshot());
+    REPORTER_ASSERT(reporter, img);
+
+    const SkIRect imgSubset = img->subset();
+    REPORTER_ASSERT(reporter, surfSubset == imgSubset);
+
+    // the canvas was invalidated by the newImageSnapshot call
+    REPORTER_ASSERT(reporter, !surf->getCanvas());
+}
+
+DEF_TEST(SpecialSurface_Raster, reporter) {
+
+    SkImageInfo info = SkImageInfo::MakeN32(kSmallerSize, kSmallerSize, kOpaque_SkAlphaType);
+    sk_sp<SkSpecialSurface> surf(SkSpecialSurface::MakeRaster(info));
+
+    test_surface(surf, reporter, 0);
+}
+
+DEF_TEST(SpecialSurface_Raster2, reporter) {
+
+    SkBitmap bm;
+    bm.allocN32Pixels(kFullSize, kFullSize, true);
+
+    const SkIRect subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
+
+    sk_sp<SkSpecialSurface> surf(SkSpecialSurface::MakeFromBitmap(subset, bm));
+
+    test_surface(surf, reporter, kPad);
+
+    // TODO: check that the clear didn't escape the active region
+}
+
+#if SK_SUPPORT_GPU
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialSurface_Gpu1, reporter, ctxInfo) {
+    sk_sp<SkSpecialSurface> surf(SkSpecialSurface::MakeRenderTarget(ctxInfo.grContext(),
+                                                                    kSmallerSize, kSmallerSize,
+                                                                    kRGBA_8888_GrPixelConfig,
+                                                                    nullptr));
+
+    test_surface(surf, reporter, 0);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/SrcOverTest.cpp b/src/third_party/skia/tests/SrcOverTest.cpp
index be64710..df31598 100644
--- a/src/third_party/skia/tests/SrcOverTest.cpp
+++ b/src/third_party/skia/tests/SrcOverTest.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "SkColorPriv.h"
-#include "SkXfermode.h"
 #include "Test.h"
 
 // our std SkAlpha255To256
@@ -42,8 +41,8 @@
         opaqueCounter2 += (result2 == 0xFF);
     }
 #if 0
-    SkDebugf("---- opaque test: [%d %d %d]\n",
-             opaqueCounter0, opaqueCounter1, opaqueCounter2);
+    INFOF(reporter, "---- opaque test: [%d %d %d]\n",
+          opaqueCounter0, opaqueCounter1, opaqueCounter2);
 #endif
     // we acknowledge that technique0 does not always return opaque
     REPORTER_ASSERT(reporter, opaqueCounter0 == 256);
@@ -67,8 +66,8 @@
 #if 0
             // this shows where r1 (faster) differs from r2 (more exact)
             if (r1 != r2) {
-                SkDebugf("--- dst=%d i=%d r1=%d r2=%d exact=%g\n",
-                         dst, i, r1, r2, i + dst - dst*i/255.0f);
+                INFOF(reporter, "--- dst=%d i=%d r1=%d r2=%d exact=%g\n",
+                      dst, i, r1, r2, i + dst - dst*i/255.0f);
             }
 #endif
         }
diff --git a/src/third_party/skia/tests/StreamBufferTest.cpp b/src/third_party/skia/tests/StreamBufferTest.cpp
new file mode 100644
index 0000000..96bca35
--- /dev/null
+++ b/src/third_party/skia/tests/StreamBufferTest.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkData.h"
+#include "SkOSPath.h"
+#include "SkStream.h"
+#include "SkStreamBuffer.h"
+
+#include "FakeStreams.h"
+#include "Test.h"
+
+static const char* gText = "Four score and seven years ago";
+
+static void test_get_data_at_position(skiatest::Reporter* r, SkStreamBuffer* buffer, size_t position,
+                                    size_t length) {
+    sk_sp<SkData> data = buffer->getDataAtPosition(position, length);
+    REPORTER_ASSERT(r, data);
+    if (data) {
+        REPORTER_ASSERT(r, !memcmp(data->data(), gText + position, length));
+    }
+}
+
+// Test buffering from the beginning, by different amounts.
+static void test_buffer_from_beginning(skiatest::Reporter* r, SkStream* stream, size_t length) {
+    SkStreamBuffer buffer(stream);
+
+    // Buffer an arbitrary amount:
+    size_t buffered = length / 2;
+    REPORTER_ASSERT(r, buffer.buffer(buffered));
+    REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, buffered));
+
+    // Buffering less is free:
+    REPORTER_ASSERT(r, buffer.buffer(buffered / 2));
+
+    // Buffer more should succeed:
+    REPORTER_ASSERT(r, buffer.buffer(length));
+    REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, length));
+}
+
+// Test flushing the stream as we read.
+static void test_flushing(skiatest::Reporter* r, SkStream* stream, size_t length,
+                          bool getDataAtPosition) {
+    SkStreamBuffer buffer(stream);
+    const size_t step = 5;
+    for (size_t position = 0; position + step <= length; position += step) {
+        REPORTER_ASSERT(r, buffer.buffer(step));
+        REPORTER_ASSERT(r, buffer.markPosition() == position);
+
+        if (!getDataAtPosition) {
+            REPORTER_ASSERT(r, !memcmp(buffer.get(), gText + position, step));
+        }
+        buffer.flush();
+    }
+
+    REPORTER_ASSERT(r, !buffer.buffer(step));
+
+    if (getDataAtPosition) {
+        for (size_t position = 0; position + step <= length; position += step) {
+            test_get_data_at_position(r, &buffer, position, step);
+        }
+    }
+}
+
+DEF_TEST(StreamBuffer, r) {
+    const size_t size = strlen(gText);
+    sk_sp<SkData> data(SkData::MakeWithoutCopy(gText, size));
+
+    SkString tmpDir = skiatest::GetTmpDir();
+    const char* subdir = "streamBuffer.txt";
+    SkString path;
+
+    if (!tmpDir.isEmpty()) {
+        path = SkOSPath::Join(tmpDir.c_str(), subdir);
+        SkFILEWStream writer(path.c_str());
+        writer.write(gText, size);
+    }
+
+    struct {
+        std::function<SkStream*()> createStream;
+        bool                       skipIfNoTmpDir;
+    } factories[] = {
+        { [&data]() { return new SkMemoryStream(data); },       false },
+        { [&data]() { return new NotAssetMemStream(data); },    false },
+        { [&path]() { return new SkFILEStream(path.c_str()); }, true  },
+    };
+
+    for (auto f : factories) {
+        if (tmpDir.isEmpty() && f.skipIfNoTmpDir) {
+            continue;
+        }
+        test_buffer_from_beginning(r, f.createStream(), size);
+        test_flushing(r, f.createStream(), size, false);
+        test_flushing(r, f.createStream(), size, true);
+    }
+
+    // Stream that will receive more data. Will be owned by the SkStreamBuffer.
+    HaltingStream* stream = new HaltingStream(data, 6);
+    SkStreamBuffer buffer(stream);
+
+    // Can only buffer less than what's available (6).
+    REPORTER_ASSERT(r, !buffer.buffer(7));
+    REPORTER_ASSERT(r, buffer.buffer(5));
+    REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, 5));
+
+    // Add some more data. We can buffer and read all of it.
+    stream->addNewData(8);
+    REPORTER_ASSERT(r, buffer.buffer(14));
+    REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, 14));
+
+    // Flush the buffer, which moves the position.
+    buffer.flush();
+
+    // Add some data, and try to read more. Can only read what is
+    // available.
+    stream->addNewData(9);
+    REPORTER_ASSERT(r, !buffer.buffer(13));
+    stream->addNewData(4);
+    REPORTER_ASSERT(r, buffer.buffer(13));
+
+    // Do not call get on this data. We'll come back to this data after adding
+    // more.
+    buffer.flush();
+    const size_t remaining = size - 27;
+    REPORTER_ASSERT(r, remaining > 0);
+    stream->addNewData(remaining);
+    REPORTER_ASSERT(r, buffer.buffer(remaining));
+    REPORTER_ASSERT(r, !memcmp(buffer.get(), gText + 27, remaining));
+
+    // Now go back to the data we skipped.
+    test_get_data_at_position(r, &buffer, 14, 13);
+}
diff --git a/src/third_party/skia/tests/StreamTest.cpp b/src/third_party/skia/tests/StreamTest.cpp
index ce391a3..8b5b2ae 100644
--- a/src/third_party/skia/tests/StreamTest.cpp
+++ b/src/third_party/skia/tests/StreamTest.cpp
@@ -5,10 +5,15 @@
  * found in the LICENSE file.
  */
 
+#include "Resources.h"
+#include "SkAutoMalloc.h"
 #include "SkData.h"
+#include "SkFrontBufferedStream.h"
 #include "SkOSFile.h"
+#include "SkOSPath.h"
 #include "SkRandom.h"
 #include "SkStream.h"
+#include "SkStreamPriv.h"
 #include "Test.h"
 
 #ifndef SK_BUILD_FOR_WIN
@@ -58,17 +63,17 @@
         REPORTER_ASSERT(reporter, stream.isValid());
         test_loop_stream(reporter, &stream, s, 26, 100);
 
-        SkAutoTUnref<SkStreamAsset> stream2(stream.duplicate());
+        std::unique_ptr<SkStreamAsset> stream2(stream.duplicate());
         test_loop_stream(reporter, stream2.get(), s, 26, 100);
     }
 
     {
         FILE* file = ::fopen(path.c_str(), "rb");
-        SkFILEStream stream(file, SkFILEStream::kCallerPasses_Ownership);
+        SkFILEStream stream(file);
         REPORTER_ASSERT(reporter, stream.isValid());
         test_loop_stream(reporter, &stream, s, 26, 100);
 
-        SkAutoTUnref<SkStreamAsset> stream2(stream.duplicate());
+        std::unique_ptr<SkStreamAsset> stream2(stream.duplicate());
         test_loop_stream(reporter, stream2.get(), s, 26, 100);
     }
 }
@@ -80,7 +85,7 @@
     for (i = 0; i < 100; i++) {
         REPORTER_ASSERT(reporter, ds.write(s, 26));
     }
-    REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
+    REPORTER_ASSERT(reporter, ds.bytesWritten() == 100 * 26);
 
     char* dst = new char[100 * 26 + 1];
     dst[100*26] = '*';
@@ -91,15 +96,15 @@
     }
 
     {
-        SkAutoTUnref<SkStreamAsset> stream(ds.detachAsStream());
+        std::unique_ptr<SkStreamAsset> stream(ds.detachAsStream());
         REPORTER_ASSERT(reporter, 100 * 26 == stream->getLength());
-        REPORTER_ASSERT(reporter, ds.getOffset() == 0);
+        REPORTER_ASSERT(reporter, ds.bytesWritten() == 0);
         test_loop_stream(reporter, stream.get(), s, 26, 100);
 
-        SkAutoTUnref<SkStreamAsset> stream2(stream->duplicate());
+        std::unique_ptr<SkStreamAsset> stream2(stream->duplicate());
         test_loop_stream(reporter, stream2.get(), s, 26, 100);
 
-        SkAutoTUnref<SkStreamAsset> stream3(stream->fork());
+        std::unique_ptr<SkStreamAsset> stream3(stream->fork());
         REPORTER_ASSERT(reporter, stream3->isAtEnd());
         char tmp;
         size_t bytes = stream->read(&tmp, 1);
@@ -111,26 +116,20 @@
     for (i = 0; i < 100; i++) {
         REPORTER_ASSERT(reporter, ds.write(s, 26));
     }
-    REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
+    REPORTER_ASSERT(reporter, ds.bytesWritten() == 100 * 26);
 
     {
-        SkAutoTUnref<SkData> data(ds.copyToData());
-        REPORTER_ASSERT(reporter, 100 * 26 == data->size());
-        REPORTER_ASSERT(reporter, memcmp(dst, data->data(), data->size()) == 0);
-    }
-
-    {
-        // Test that this works after a copyToData.
-        SkAutoTUnref<SkStreamAsset> stream(ds.detachAsStream());
-        REPORTER_ASSERT(reporter, ds.getOffset() == 0);
+        // Test that this works after a snapshot.
+        std::unique_ptr<SkStreamAsset> stream(ds.detachAsStream());
+        REPORTER_ASSERT(reporter, ds.bytesWritten() == 0);
         test_loop_stream(reporter, stream.get(), s, 26, 100);
 
-        SkAutoTUnref<SkStreamAsset> stream2(stream->duplicate());
+        std::unique_ptr<SkStreamAsset> stream2(stream->duplicate());
         test_loop_stream(reporter, stream2.get(), s, 26, 100);
     }
     delete[] dst;
 
-    SkString tmpDir = skiatest::Test::GetTmpDir();
+    SkString tmpDir = skiatest::GetTmpDir();
     if (!tmpDir.isEmpty()) {
         test_filestreams(reporter, tmpDir.c_str());
     }
@@ -148,39 +147,35 @@
 
 
     size_t i;
-    char buffer[sizeof(sizes) * 4];
+    SkDynamicMemoryWStream wstream;
 
-    SkMemoryWStream wstream(buffer, sizeof(buffer));
     for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
         bool success = wstream.writePackedUInt(sizes[i]);
         REPORTER_ASSERT(reporter, success);
     }
-    wstream.flush();
 
-    SkMemoryStream rstream(buffer, sizeof(buffer));
+    std::unique_ptr<SkStreamAsset> rstream(wstream.detachAsStream());
     for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
-        size_t n = rstream.readPackedUInt();
+        size_t n = rstream->readPackedUInt();
         if (sizes[i] != n) {
-            SkDebugf("-- %d: sizes:%x n:%x\n", i, sizes[i], n);
+            ERRORF(reporter, "sizes:%x != n:%x\n", i, sizes[i], n);
         }
-        REPORTER_ASSERT(reporter, sizes[i] == n);
     }
 }
 
-// Test that setting an SkMemoryStream to a NULL data does not result in a crash when calling
+// Test that setting an SkMemoryStream to a nullptr data does not result in a crash when calling
 // methods that access fData.
 static void TestDereferencingData(SkMemoryStream* memStream) {
-    memStream->read(NULL, 0);
+    memStream->read(nullptr, 0);
     memStream->getMemoryBase();
-    SkAutoDataUnref data(memStream->copyToData());
+    (void)memStream->asData();
 }
 
 static void TestNullData() {
-    SkData* nullData = NULL;
-    SkMemoryStream memStream(nullData);
+    SkMemoryStream memStream(nullptr);
     TestDereferencingData(&memStream);
 
-    memStream.setData(nullData);
+    memStream.setData(nullptr);
     TestDereferencingData(&memStream);
 
 }
@@ -190,3 +185,282 @@
     TestPackedUInt(reporter);
     TestNullData();
 }
+
+#ifndef SK_BUILD_FOR_IOS
+/**
+ *  Tests peeking and then reading the same amount. The two should provide the
+ *  same results.
+ *  Returns the amount successfully read minus the amount successfully peeked.
+ */
+static size_t compare_peek_to_read(skiatest::Reporter* reporter,
+                                   SkStream* stream, size_t bytesToPeek) {
+    // The rest of our tests won't be very interesting if bytesToPeek is zero.
+    REPORTER_ASSERT(reporter, bytesToPeek > 0);
+    SkAutoMalloc peekStorage(bytesToPeek);
+    SkAutoMalloc readStorage(bytesToPeek);
+    void* peekPtr = peekStorage.get();
+    void* readPtr = peekStorage.get();
+
+    const size_t bytesPeeked = stream->peek(peekPtr, bytesToPeek);
+    const size_t bytesRead = stream->read(readPtr, bytesToPeek);
+
+    // bytesRead should only be less than attempted if the stream is at the
+    // end.
+    REPORTER_ASSERT(reporter, bytesRead == bytesToPeek || stream->isAtEnd());
+
+    // peek and read should behave the same, except peek returned to the
+    // original position, so they read the same data.
+    REPORTER_ASSERT(reporter, !memcmp(peekPtr, readPtr, bytesPeeked));
+
+    // A stream should never be able to peek more than it can read.
+    REPORTER_ASSERT(reporter, bytesRead >= bytesPeeked);
+
+    return bytesRead - bytesPeeked;
+}
+
+static void test_fully_peekable_stream(skiatest::Reporter* r, SkStream* stream, size_t limit) {
+    for (size_t i = 1; !stream->isAtEnd(); i++) {
+        REPORTER_ASSERT(r, compare_peek_to_read(r, stream, i) == 0);
+    }
+}
+
+static void test_peeking_front_buffered_stream(skiatest::Reporter* r,
+                                               const SkStream& original,
+                                               size_t bufferSize) {
+    SkStream* dupe = original.duplicate();
+    REPORTER_ASSERT(r, dupe != nullptr);
+    std::unique_ptr<SkStream> bufferedStream(SkFrontBufferedStream::Create(dupe, bufferSize));
+    REPORTER_ASSERT(r, bufferedStream != nullptr);
+
+    size_t peeked = 0;
+    for (size_t i = 1; !bufferedStream->isAtEnd(); i++) {
+        const size_t unpeekableBytes = compare_peek_to_read(r, bufferedStream.get(), i);
+        if (unpeekableBytes > 0) {
+            // This could not have returned a number greater than i.
+            REPORTER_ASSERT(r, unpeekableBytes <= i);
+
+            // We have reached the end of the buffer. Verify that it was at least
+            // bufferSize.
+            REPORTER_ASSERT(r, peeked + i - unpeekableBytes >= bufferSize);
+            // No more peeking is supported.
+            break;
+        }
+        peeked += i;
+    }
+
+    // Test that attempting to peek beyond the length of the buffer does not prevent rewinding.
+    bufferedStream.reset(SkFrontBufferedStream::Create(original.duplicate(), bufferSize));
+    REPORTER_ASSERT(r, bufferedStream != nullptr);
+
+    const size_t bytesToPeek = bufferSize + 1;
+    SkAutoMalloc peekStorage(bytesToPeek);
+    SkAutoMalloc readStorage(bytesToPeek);
+
+    for (size_t start = 0; start <= bufferSize; start++) {
+        // Skip to the starting point
+        REPORTER_ASSERT(r, bufferedStream->skip(start) == start);
+
+        const size_t bytesPeeked = bufferedStream->peek(peekStorage.get(), bytesToPeek);
+        if (0 == bytesPeeked) {
+            // Peeking should only fail completely if we have read/skipped beyond the buffer.
+            REPORTER_ASSERT(r, start >= bufferSize);
+            break;
+        }
+
+        // Only read the amount that was successfully peeked.
+        const size_t bytesRead = bufferedStream->read(readStorage.get(), bytesPeeked);
+        REPORTER_ASSERT(r, bytesRead == bytesPeeked);
+        REPORTER_ASSERT(r, !memcmp(peekStorage.get(), readStorage.get(), bytesPeeked));
+
+        // This should be safe to rewind.
+        REPORTER_ASSERT(r, bufferedStream->rewind());
+    }
+}
+
+// This test uses file system operations that don't work out of the
+// box on iOS. It's likely that we don't need them on iOS. Ignoring for now.
+// TODO(stephana): Re-evaluate if we need this in the future.
+DEF_TEST(StreamPeek, reporter) {
+    // Test a memory stream.
+    const char gAbcs[] = "abcdefghijklmnopqrstuvwxyz";
+    SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
+    test_fully_peekable_stream(reporter, &memStream, memStream.getLength());
+
+    // Test an arbitrary file stream. file streams do not support peeking.
+    SkFILEStream fileStream(GetResourcePath("baby_tux.webp").c_str());
+    REPORTER_ASSERT(reporter, fileStream.isValid());
+    if (!fileStream.isValid()) {
+        return;
+    }
+    SkAutoMalloc storage(fileStream.getLength());
+    for (size_t i = 1; i < fileStream.getLength(); i++) {
+        REPORTER_ASSERT(reporter, fileStream.peek(storage.get(), i) == 0);
+    }
+
+    // Now test some FrontBufferedStreams
+    for (size_t i = 1; i < memStream.getLength(); i++) {
+        test_peeking_front_buffered_stream(reporter, memStream, i);
+    }
+}
+#endif
+
+// Asserts that asset == expected and is peekable.
+static void stream_peek_test(skiatest::Reporter* rep,
+                             SkStreamAsset* asset,
+                             const SkData* expected) {
+    if (asset->getLength() != expected->size()) {
+        ERRORF(rep, "Unexpected length.");
+        return;
+    }
+    SkRandom rand;
+    uint8_t buffer[4096];
+    const uint8_t* expect = expected->bytes();
+    for (size_t i = 0; i < asset->getLength(); ++i) {
+        uint32_t maxSize =
+                SkToU32(SkTMin(sizeof(buffer), asset->getLength() - i));
+        size_t size = rand.nextRangeU(1, maxSize);
+        SkASSERT(size >= 1);
+        SkASSERT(size <= sizeof(buffer));
+        SkASSERT(size + i <= asset->getLength());
+        if (asset->peek(buffer, size) < size) {
+            ERRORF(rep, "Peek Failed!");
+            return;
+        }
+        if (0 != memcmp(buffer, &expect[i], size)) {
+            ERRORF(rep, "Peek returned wrong bytes!");
+            return;
+        }
+        uint8_t value;
+        REPORTER_ASSERT(rep, 1 == asset->read(&value, 1));
+        if (value != expect[i]) {
+            ERRORF(rep, "Read Failed!");
+            return;
+        }
+    }
+}
+
+DEF_TEST(StreamPeek_BlockMemoryStream, rep) {
+    const static int kSeed = 1234;
+    SkRandom valueSource(kSeed);
+    SkRandom rand(kSeed << 1);
+    uint8_t buffer[4096];
+    SkDynamicMemoryWStream dynamicMemoryWStream;
+    size_t totalWritten = 0;
+    for (int i = 0; i < 32; ++i) {
+        // Randomize the length of the blocks.
+        size_t size = rand.nextRangeU(1, sizeof(buffer));
+        for (size_t j = 0; j < size; ++j) {
+            buffer[j] = valueSource.nextU() & 0xFF;
+        }
+        dynamicMemoryWStream.write(buffer, size);
+        totalWritten += size;
+        REPORTER_ASSERT(rep, totalWritten == dynamicMemoryWStream.bytesWritten());
+    }
+    std::unique_ptr<SkStreamAsset> asset(dynamicMemoryWStream.detachAsStream());
+    sk_sp<SkData> expected(SkData::MakeUninitialized(asset->getLength()));
+    uint8_t* expectedPtr = static_cast<uint8_t*>(expected->writable_data());
+    valueSource.setSeed(kSeed);  // reseed.
+    // We want the exact same same "random" string of numbers to put
+    // in expected. i.e.: don't rely on SkDynamicMemoryStream to work
+    // correctly while we are testing SkDynamicMemoryStream.
+    for (size_t i = 0; i < asset->getLength(); ++i) {
+        expectedPtr[i] = valueSource.nextU() & 0xFF;
+    }
+    stream_peek_test(rep, asset.get(), expected.get());
+}
+
+namespace {
+class DumbStream : public SkStream {
+public:
+    DumbStream(const uint8_t* data, size_t n)
+        : fData(data), fCount(n), fIdx(0) {}
+    size_t read(void* buffer, size_t size) override {
+        size_t copyCount = SkTMin(fCount - fIdx, size);
+        if (copyCount) {
+            memcpy(buffer, &fData[fIdx], copyCount);
+            fIdx += copyCount;
+        }
+        return copyCount;
+    }
+    bool isAtEnd() const override {
+        return fCount == fIdx;
+    }
+ private:
+    const uint8_t* fData;
+    size_t fCount, fIdx;
+};
+}  // namespace
+
+static void stream_copy_test(skiatest::Reporter* reporter,
+                             const void* srcData,
+                             size_t N,
+                             SkStream* stream) {
+    SkDynamicMemoryWStream tgt;
+    if (!SkStreamCopy(&tgt, stream)) {
+        ERRORF(reporter, "SkStreamCopy failed");
+        return;
+    }
+    sk_sp<SkData> data(tgt.detachAsData());
+    if (data->size() != N) {
+        ERRORF(reporter, "SkStreamCopy incorrect size");
+        return;
+    }
+    if (0 != memcmp(data->data(), srcData, N)) {
+        ERRORF(reporter, "SkStreamCopy bad copy");
+    }
+}
+
+DEF_TEST(DynamicMemoryWStream_detachAsData, r) {
+    const char az[] = "abcdefghijklmnopqrstuvwxyz";
+    const unsigned N = 40000;
+    SkDynamicMemoryWStream dmws;
+    for (unsigned i = 0; i < N; ++i) {
+        dmws.writeText(az);
+    }
+    REPORTER_ASSERT(r, dmws.bytesWritten() == N * strlen(az));
+    auto data = dmws.detachAsData();
+    REPORTER_ASSERT(r, data->size() == N * strlen(az));
+    const uint8_t* ptr = data->bytes();
+    for (unsigned i = 0; i < N; ++i) {
+        if (0 != memcmp(ptr, az, strlen(az))) {
+            ERRORF(r, "detachAsData() memcmp failed");
+            return;
+        }
+        ptr += strlen(az);
+    }
+}
+
+DEF_TEST(StreamCopy, reporter) {
+    SkRandom random(123456);
+    static const int N = 10000;
+    SkAutoTMalloc<uint8_t> src((size_t)N);
+    for (int j = 0; j < N; ++j) {
+        src[j] = random.nextU() & 0xff;
+    }
+    // SkStreamCopy had two code paths; this test both.
+    DumbStream dumbStream(src.get(), (size_t)N);
+    stream_copy_test(reporter, src, N, &dumbStream);
+    SkMemoryStream smartStream(src.get(), (size_t)N);
+    stream_copy_test(reporter, src, N, &smartStream);
+}
+
+DEF_TEST(StreamEmptyStreamMemoryBase, r) {
+    SkDynamicMemoryWStream tmp;
+    std::unique_ptr<SkStreamAsset> asset(tmp.detachAsStream());
+    REPORTER_ASSERT(r, nullptr == asset->getMemoryBase());
+}
+
+#include "SkBuffer.h"
+
+DEF_TEST(RBuffer, reporter) {
+    int32_t value = 0;
+    SkRBuffer buffer(&value, 4);
+    REPORTER_ASSERT(reporter, buffer.isValid());
+
+    int32_t tmp;
+    REPORTER_ASSERT(reporter, buffer.read(&tmp, 4));
+    REPORTER_ASSERT(reporter, buffer.isValid());
+
+    REPORTER_ASSERT(reporter, !buffer.read(&tmp, 4));
+    REPORTER_ASSERT(reporter, !buffer.isValid());
+}
diff --git a/src/third_party/skia/tests/StringTest.cpp b/src/third_party/skia/tests/StringTest.cpp
index 13ee5ec..8ae7412 100644
--- a/src/third_party/skia/tests/StringTest.cpp
+++ b/src/third_party/skia/tests/StringTest.cpp
@@ -36,7 +36,7 @@
     SkString    a;
     SkString    b((size_t)0);
     SkString    c("");
-    SkString    d(NULL, 0);
+    SkString    d(nullptr, 0);
 
     REPORTER_ASSERT(reporter, a.isEmpty());
     REPORTER_ASSERT(reporter, a == b && a == c && a == d);
@@ -148,6 +148,13 @@
     a.appendU64(0x0000000001000000ULL, 15);
     REPORTER_ASSERT(reporter, a.equals("000000016777216"));
 
+    a.printf("%i", 0);
+    REPORTER_ASSERT(reporter, a.equals("0"));
+    a.printf("%g", 3.14);
+    REPORTER_ASSERT(reporter, a.equals("3.14"));
+    a.printf("hello %s", "skia");
+    REPORTER_ASSERT(reporter, a.equals("hello skia"));
+
     static const struct {
         SkScalar    fValue;
         const char* fString;
@@ -156,7 +163,7 @@
         { SK_Scalar1,   "1" },
         { -SK_Scalar1,  "-1" },
         { SK_Scalar1/2, "0.5" },
-  #ifdef SK_BUILD_FOR_WIN
+  #if defined(SK_BUILD_FOR_WIN) && (_MSC_VER < 1900)
         { 3.4028234e38f,   "3.4028235e+038" },
         { -3.4028234e38f, "-3.4028235e+038" },
   #else
@@ -168,8 +175,9 @@
         a.reset();
         a.appendScalar(gRec[i].fValue);
         REPORTER_ASSERT(reporter, a.size() <= SkStrAppendScalar_MaxSize);
-//        SkDebugf(" received <%s> expected <%s>\n", a.c_str(), gRec[i].fString);
-        REPORTER_ASSERT(reporter, a.equals(gRec[i].fString));
+        if (!a.equals(gRec[i].fString)) {
+            ERRORF(reporter, "received <%s> expected <%s>\n", a.c_str(), gRec[i].fString);
+        }
     }
 
     REPORTER_ASSERT(reporter, SkStringPrintf("%i", 0).equals("0"));
@@ -184,6 +192,26 @@
     REPORTER_ASSERT(reporter, buffer[19] == 0);
     REPORTER_ASSERT(reporter, buffer[20] == 'a');
 
+    REPORTER_ASSERT(reporter, SkStringPrintf("%i", 0).equals("0"));
+
+    // 2000 is larger than the static buffer size inside SkString.cpp
+    a = SkStringPrintf("%2000s", " ");
+    REPORTER_ASSERT(reporter, a.size() == 2000);
+    for (size_t i = 0; i < a.size(); ++i) {
+        if (a[i] != ' ') {
+            ERRORF(reporter, "SkStringPrintf fail: a[%d] = '%c'", i, a[i]);
+            break;
+        }
+    }
+    a.reset();
+    a.printf("%2000s", " ");
+    REPORTER_ASSERT(reporter, a.size() == 2000);
+    for (size_t i = 0; i < a.size(); ++i) {
+        if (a[i] != ' ') {
+            ERRORF(reporter, "SkStringPrintf fail: a[%d] = '%c'", i, a[i]);
+            break;
+        }
+    }
 }
 
 DEF_TEST(String_SkStrSplit, r) {
@@ -197,4 +225,65 @@
     REPORTER_ASSERT(r, results[3].equals("dee"));
     REPORTER_ASSERT(r, results[4].equals("f"));
     REPORTER_ASSERT(r, results[5].equals("g"));
+
+    results.reset();
+    SkStrSplit("\n", "\n", &results);
+    REPORTER_ASSERT(r, results.count() == 0);
+
+    results.reset();
+    SkStrSplit("", "\n", &results);
+    REPORTER_ASSERT(r, results.count() == 0);
+
+    results.reset();
+    SkStrSplit("a", "\n", &results);
+    REPORTER_ASSERT(r, results.count() == 1);
+    REPORTER_ASSERT(r, results[0].equals("a"));
+}
+DEF_TEST(String_SkStrSplit_All, r) {
+    SkTArray<SkString> results;
+    SkStrSplit("a-_b_c-dee--f-_-_-g-", "-_", kStrict_SkStrSplitMode, &results);
+    REPORTER_ASSERT(r, results.count() == 13);
+    REPORTER_ASSERT(r, results[0].equals("a"));
+    REPORTER_ASSERT(r, results[1].equals(""));
+    REPORTER_ASSERT(r, results[2].equals("b"));
+    REPORTER_ASSERT(r, results[3].equals("c"));
+    REPORTER_ASSERT(r, results[4].equals("dee"));
+    REPORTER_ASSERT(r, results[5].equals(""));
+    REPORTER_ASSERT(r, results[6].equals("f"));
+    REPORTER_ASSERT(r, results[7].equals(""));
+    REPORTER_ASSERT(r, results[8].equals(""));
+    REPORTER_ASSERT(r, results[9].equals(""));
+    REPORTER_ASSERT(r, results[10].equals(""));
+    REPORTER_ASSERT(r, results[11].equals("g"));
+    REPORTER_ASSERT(r, results[12].equals(""));
+
+    results.reset();
+    SkStrSplit("\n", "\n", kStrict_SkStrSplitMode, &results);
+    REPORTER_ASSERT(r, results.count() == 2);
+    REPORTER_ASSERT(r, results[0].equals(""));
+    REPORTER_ASSERT(r, results[1].equals(""));
+
+    results.reset();
+    SkStrSplit("", "\n", kStrict_SkStrSplitMode, &results);
+    REPORTER_ASSERT(r, results.count() == 0);
+
+    results.reset();
+    SkStrSplit("a", "\n", kStrict_SkStrSplitMode, &results);
+    REPORTER_ASSERT(r, results.count() == 1);
+    REPORTER_ASSERT(r, results[0].equals("a"));
+
+    results.reset();
+    SkStrSplit(",,", ",", kStrict_SkStrSplitMode, &results);
+    REPORTER_ASSERT(r, results.count() == 3);
+    REPORTER_ASSERT(r, results[0].equals(""));
+    REPORTER_ASSERT(r, results[1].equals(""));
+    REPORTER_ASSERT(r, results[2].equals(""));
+
+    results.reset();
+    SkStrSplit(",a,b,", ",", kStrict_SkStrSplitMode, &results);
+    REPORTER_ASSERT(r, results.count() == 4);
+    REPORTER_ASSERT(r, results[0].equals(""));
+    REPORTER_ASSERT(r, results[1].equals("a"));
+    REPORTER_ASSERT(r, results[2].equals("b"));
+    REPORTER_ASSERT(r, results[3].equals(""));
 }
diff --git a/src/third_party/skia/tests/StrokeTest.cpp b/src/third_party/skia/tests/StrokeTest.cpp
index 15220c4..43b293f 100644
--- a/src/third_party/skia/tests/StrokeTest.cpp
+++ b/src/third_party/skia/tests/StrokeTest.cpp
@@ -9,6 +9,7 @@
 #include "SkPath.h"
 #include "SkRect.h"
 #include "SkStroke.h"
+#include "SkStrokeRec.h"
 #include "Test.h"
 
 static bool equal(const SkRect& a, const SkRect& b) {
@@ -18,6 +19,35 @@
             SkScalarNearlyEqual(a.bottom(), b.bottom());
 }
 
+static void test_strokecubic(skiatest::Reporter* reporter) {
+    uint32_t hexCubicVals[] = {
+        0x424c1086, 0x44bcf0cb,  // fX=51.0161362 fY=1511.52478
+        0x424c107c, 0x44bcf0cb,  // fX=51.0160980 fY=1511.52478
+        0x424c10c2, 0x44bcf0cb,  // fX=51.0163651 fY=1511.52478
+        0x424c1119, 0x44bcf0ca,  // fX=51.0166969 fY=1511.52466
+    };
+    SkPoint cubicVals[] = {
+        {51.0161362f, 1511.52478f },
+        {51.0160980f, 1511.52478f },
+        {51.0163651f, 1511.52478f },
+        {51.0166969f, 1511.52466f },
+    };
+    SkPaint paint;
+
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(0.394537568f);
+    SkPath path, fillPath;
+    path.moveTo(cubicVals[0]);
+    path.cubicTo(cubicVals[1], cubicVals[2], cubicVals[3]);
+    paint.getFillPath(path, &fillPath);
+    path.reset();
+    path.moveTo(SkBits2Float(hexCubicVals[0]), SkBits2Float(hexCubicVals[1]));
+    path.cubicTo(SkBits2Float(hexCubicVals[2]), SkBits2Float(hexCubicVals[3]),
+            SkBits2Float(hexCubicVals[4]), SkBits2Float(hexCubicVals[5]),
+            SkBits2Float(hexCubicVals[6]), SkBits2Float(hexCubicVals[7]));
+    paint.getFillPath(path, &fillPath);
+}
+
 static void test_strokerect(skiatest::Reporter* reporter) {
     const SkScalar width = SkIntToScalar(10);
     SkPaint paint;
@@ -45,7 +75,7 @@
 
         bool isMiter = SkPaint::kMiter_Join == joins[i];
         SkRect nested[2];
-        REPORTER_ASSERT(reporter, fillPath.isNestedRects(nested) == isMiter);
+        REPORTER_ASSERT(reporter, fillPath.isNestedFillRects(nested) == isMiter);
         if (isMiter) {
             SkRect inner(r);
             inner.inset(width/2, width/2);
@@ -55,6 +85,83 @@
     }
 }
 
+static void test_strokerec_equality(skiatest::Reporter* reporter) {
+    {
+        SkStrokeRec s1(SkStrokeRec::kFill_InitStyle);
+        SkStrokeRec s2(SkStrokeRec::kFill_InitStyle);
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+
+        // Test that style mismatch is detected.
+        s2.setHairlineStyle();
+        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
+
+        s1.setHairlineStyle();
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+
+        // ResScale is not part of equality.
+        s1.setResScale(2.1f);
+        s2.setResScale(1.2f);
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+        s1.setFillStyle();
+        s2.setFillStyle();
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+        s1.setStrokeStyle(1.0f, false);
+        s2.setStrokeStyle(1.0f, false);
+        s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
+        s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+    }
+
+    // Stroke parameters on fill or hairline style are not part of equality.
+    {
+        SkStrokeRec s1(SkStrokeRec::kFill_InitStyle);
+        SkStrokeRec s2(SkStrokeRec::kFill_InitStyle);
+        for (int i = 0; i < 2; ++i) {
+            s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
+            s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.1f);
+            REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+            s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 2.9f);
+            REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+            s2.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 2.9f);
+            REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+            s1.setHairlineStyle();
+            s2.setHairlineStyle();
+        }
+    }
+
+    // Stroke parameters on stroke style are part of equality.
+    {
+        SkStrokeRec s1(SkStrokeRec::kFill_InitStyle);
+        SkStrokeRec s2(SkStrokeRec::kFill_InitStyle);
+        s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
+        s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
+        s1.setStrokeStyle(1.0f, false);
+
+        s2.setStrokeStyle(1.0f, true);
+        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
+
+        s2.setStrokeStyle(2.1f, false);
+        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
+
+        s2.setStrokeStyle(1.0f, false);
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+
+        s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.1f);
+        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
+        s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 2.9f);
+        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
+        s2.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 2.9f);
+        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
+
+        // Sets fill.
+        s1.setStrokeStyle(0.0f, true);
+        s2.setStrokeStyle(0.0f, true);
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+    }
+}
+
 DEF_TEST(Stroke, reporter) {
+    test_strokecubic(reporter);
     test_strokerect(reporter);
+    test_strokerec_equality(reporter);
 }
diff --git a/src/third_party/skia/tests/StrokerTest.cpp b/src/third_party/skia/tests/StrokerTest.cpp
new file mode 100644
index 0000000..7b9a340
--- /dev/null
+++ b/src/third_party/skia/tests/StrokerTest.cpp
@@ -0,0 +1,468 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "PathOpsCubicIntersectionTestData.h"
+#include "PathOpsQuadIntersectionTestData.h"
+#include "SkCommonFlags.h"
+#include "SkPathOpsCubic.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkStrokerPriv.h"
+#include "SkTime.h"
+#include "Test.h"
+
+DEFINE_bool(timeout, true, "run until alloted time expires");
+
+#define MS_TEST_DURATION 10
+
+const SkScalar widths[] = {-FLT_MAX, -1, -0.1f, -FLT_EPSILON, 0, FLT_EPSILON,
+        0.0000001f, 0.000001f, 0.00001f, 0.0001f, 0.001f, 0.01f,
+        0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 1, 1.1f, 2, 10, 10e2f, 10e3f, 10e4f, 10e5f, 10e6f, 10e7f,
+        10e8f, 10e9f, 10e10f, 10e20f,  FLT_MAX };
+size_t widths_count = SK_ARRAY_COUNT(widths);
+
+static void pathTest(const SkPath& path) {
+    SkPaint p;
+    SkPath fill;
+    p.setStyle(SkPaint::kStroke_Style);
+    for (size_t index = 0; index < widths_count; ++index) {
+        p.setStrokeWidth(widths[index]);
+        p.getFillPath(path, &fill);
+    }
+}
+
+static void cubicTest(const SkPoint c[4]) {
+    SkPath path;
+    path.moveTo(c[0].fX, c[0].fY);
+    path.cubicTo(c[1].fX, c[1].fY, c[2].fX, c[2].fY, c[3].fX, c[3].fY);
+    pathTest(path);
+}
+
+static void quadTest(const SkPoint c[3]) {
+    SkPath path;
+    path.moveTo(c[0].fX, c[0].fY);
+    path.quadTo(c[1].fX, c[1].fY, c[2].fX, c[2].fY);
+    pathTest(path);
+}
+
+static void cubicSetTest(const CubicPts* dCubic, size_t count) {
+    skiatest::Timer timer;
+    for (size_t index = 0; index < count; ++index) {
+        const CubicPts& dPts = dCubic[index];
+        SkDCubic d;
+        d.debugSet(dPts.fPts);
+        SkPoint c[4] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY},
+                         {(float) d[2].fX, (float) d[2].fY}, {(float) d[3].fX, (float) d[3].fY} };
+        cubicTest(c);
+        if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) {
+            return;
+        }
+    }
+}
+
+static void cubicPairSetTest(const CubicPts dCubic[][2], size_t count) {
+    skiatest::Timer timer;
+    for (size_t index = 0; index < count; ++index) {
+        for (int pair = 0; pair < 2; ++pair) {
+            const CubicPts& dPts = dCubic[index][pair];
+            SkDCubic d;
+            d.debugSet(dPts.fPts);
+            SkPoint c[4] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY},
+                             {(float) d[2].fX, (float) d[2].fY}, {(float) d[3].fX, (float) d[3].fY} };
+            cubicTest(c);
+            if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) {
+                return;
+            }
+        }
+    }
+}
+
+static void quadSetTest(const QuadPts* dQuad, size_t count) {
+    skiatest::Timer timer;
+    for (size_t index = 0; index < count; ++index) {
+        const QuadPts& dPts = dQuad[index];
+        SkDQuad d;
+        d.debugSet(dPts.fPts);
+        SkPoint c[3] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY},
+                         {(float) d[2].fX, (float) d[2].fY}  };
+        quadTest(c);
+        if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) {
+            return;
+        }
+    }
+}
+
+static void quadPairSetTest(const QuadPts dQuad[][2], size_t count) {
+    skiatest::Timer timer;
+    for (size_t index = 0; index < count; ++index) {
+        for (int pair = 0; pair < 2; ++pair) {
+            const QuadPts& dPts = dQuad[index][pair];
+            SkDQuad d;
+            d.debugSet(dPts.fPts);
+            SkPoint c[3] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY},
+                             {(float) d[2].fX, (float) d[2].fY}  };
+            quadTest(c);
+            if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) {
+                return;
+            }
+        }
+    }
+}
+
+DEF_TEST(QuadStrokerSet, reporter) {
+    quadSetTest(quadraticLines, quadraticLines_count);
+    quadSetTest(quadraticPoints, quadraticPoints_count);
+    quadSetTest(quadraticModEpsilonLines, quadraticModEpsilonLines_count);
+    quadPairSetTest(quadraticTests, quadraticTests_count);
+}
+
+DEF_TEST(CubicStrokerSet, reporter) {
+    cubicSetTest(pointDegenerates, pointDegenerates_count);
+    cubicSetTest(notPointDegenerates, notPointDegenerates_count);
+    cubicSetTest(lines, lines_count);
+    cubicSetTest(notLines, notLines_count);
+    cubicSetTest(modEpsilonLines, modEpsilonLines_count);
+    cubicSetTest(lessEpsilonLines, lessEpsilonLines_count);
+    cubicSetTest(negEpsilonLines, negEpsilonLines_count);
+    cubicPairSetTest(tests, tests_count);
+}
+
+static SkScalar unbounded(SkRandom& r) {
+    uint32_t val = r.nextU();
+    return SkBits2Float(val);
+}
+
+static SkScalar unboundedPos(SkRandom& r) {
+    uint32_t val = r.nextU() & 0x7fffffff;
+    return SkBits2Float(val);
+}
+
+DEF_TEST(QuadStrokerUnbounded, reporter) {
+    SkRandom r;
+    SkPaint p;
+    p.setStyle(SkPaint::kStroke_Style);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    int best = 0;
+    sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+    skiatest::Timer timer;
+    for (int i = 0; i < 1000000; ++i) {
+        SkPath path, fill;
+        path.moveTo(unbounded(r), unbounded(r));
+        path.quadTo(unbounded(r), unbounded(r), unbounded(r), unbounded(r));
+        p.setStrokeWidth(unboundedPos(r));
+        p.getFillPath(path, &fill);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+        if (best < gMaxRecursion[2]) {
+            if (FLAGS_veryVerbose) {
+                SkDebugf("\n%s quad=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[2],
+                        p.getStrokeWidth());
+                path.dumpHex();
+                SkDebugf("fill:\n");
+                fill.dumpHex();
+            }
+            best = gMaxRecursion[2];
+        }
+#endif
+        if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) {
+            return;
+        }
+    }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    if (FLAGS_veryVerbose) {
+       SkDebugf("\n%s max quad=%d\n", __FUNCTION__, best);
+    }
+#endif
+}
+
+DEF_TEST(CubicStrokerUnbounded, reporter) {
+    SkRandom r;
+    SkPaint p;
+    p.setStyle(SkPaint::kStroke_Style);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    int bestTan = 0;
+    int bestCubic = 0;
+    sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+    skiatest::Timer timer;
+    for (int i = 0; i < 1000000; ++i) {
+        SkPath path, fill;
+        path.moveTo(unbounded(r), unbounded(r));
+        path.cubicTo(unbounded(r), unbounded(r), unbounded(r), unbounded(r),
+                unbounded(r), unbounded(r));
+        p.setStrokeWidth(unboundedPos(r));
+        p.getFillPath(path, &fill);
+    #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+        if (bestTan < gMaxRecursion[0] || bestCubic < gMaxRecursion[1]) {
+            if (FLAGS_veryVerbose) {
+                SkDebugf("\n%s tan=%d cubic=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[0],
+                        gMaxRecursion[1], p.getStrokeWidth());
+                path.dumpHex();
+                SkDebugf("fill:\n");
+                fill.dumpHex();
+            }
+            bestTan = SkTMax(bestTan, gMaxRecursion[0]);
+            bestCubic = SkTMax(bestCubic, gMaxRecursion[1]);
+        }
+    #endif
+        if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) {
+            return;
+        }
+    }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    if (FLAGS_veryVerbose) {
+        SkDebugf("\n%s max tan=%d cubic=%d\n", __FUNCTION__, bestTan, bestCubic);
+    }
+#endif
+}
+
+DEF_TEST(QuadStrokerConstrained, reporter) {
+    SkRandom r;
+    SkPaint p;
+    p.setStyle(SkPaint::kStroke_Style);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    int best = 0;
+    sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+    skiatest::Timer timer;
+    for (int i = 0; i < 1000000; ++i) {
+        SkPath path, fill;
+        SkPoint quad[3];
+        quad[0].fX = r.nextRangeF(0, 500);
+        quad[0].fY = r.nextRangeF(0, 500);
+        const SkScalar halfSquared = 0.5f * 0.5f;
+        do {
+            quad[1].fX = r.nextRangeF(0, 500);
+            quad[1].fY = r.nextRangeF(0, 500);
+        } while (quad[0].distanceToSqd(quad[1]) < halfSquared);
+        do {
+            quad[2].fX = r.nextRangeF(0, 500);
+            quad[2].fY = r.nextRangeF(0, 500);
+        } while (quad[0].distanceToSqd(quad[2]) < halfSquared
+                || quad[1].distanceToSqd(quad[2]) < halfSquared);
+        path.moveTo(quad[0].fX, quad[0].fY);
+        path.quadTo(quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY);
+        p.setStrokeWidth(r.nextRangeF(0, 500));
+        p.getFillPath(path, &fill);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+        if (best < gMaxRecursion[2]) {
+            if (FLAGS_veryVerbose) {
+                SkDebugf("\n%s quad=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[2],
+                        p.getStrokeWidth());
+                path.dumpHex();
+                SkDebugf("fill:\n");
+                fill.dumpHex();
+            }
+            best = gMaxRecursion[2];
+        }
+#endif
+        if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) {
+            return;
+        }
+    }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    if (FLAGS_veryVerbose) {
+        SkDebugf("\n%s max quad=%d\n", __FUNCTION__, best);
+    }
+#endif
+}
+
+DEF_TEST(CubicStrokerConstrained, reporter) {
+    SkRandom r;
+    SkPaint p;
+    p.setStyle(SkPaint::kStroke_Style);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    int bestTan = 0;
+    int bestCubic = 0;
+    sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+    skiatest::Timer timer;
+    for (int i = 0; i < 1000000; ++i) {
+        SkPath path, fill;
+        SkPoint cubic[4];
+        cubic[0].fX = r.nextRangeF(0, 500);
+        cubic[0].fY = r.nextRangeF(0, 500);
+        const SkScalar halfSquared = 0.5f * 0.5f;
+        do {
+            cubic[1].fX = r.nextRangeF(0, 500);
+            cubic[1].fY = r.nextRangeF(0, 500);
+        } while (cubic[0].distanceToSqd(cubic[1]) < halfSquared);
+        do {
+            cubic[2].fX = r.nextRangeF(0, 500);
+            cubic[2].fY = r.nextRangeF(0, 500);
+        } while (  cubic[0].distanceToSqd(cubic[2]) < halfSquared
+                || cubic[1].distanceToSqd(cubic[2]) < halfSquared);
+        do {
+            cubic[3].fX = r.nextRangeF(0, 500);
+            cubic[3].fY = r.nextRangeF(0, 500);
+        } while (  cubic[0].distanceToSqd(cubic[3]) < halfSquared
+                || cubic[1].distanceToSqd(cubic[3]) < halfSquared
+                || cubic[2].distanceToSqd(cubic[3]) < halfSquared);
+        path.moveTo(cubic[0].fX, cubic[0].fY);
+        path.cubicTo(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY);
+        p.setStrokeWidth(r.nextRangeF(0, 500));
+        p.getFillPath(path, &fill);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+        if (bestTan < gMaxRecursion[0] || bestCubic < gMaxRecursion[1]) {
+            if (FLAGS_veryVerbose) {
+                SkDebugf("\n%s tan=%d cubic=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[0],
+                        gMaxRecursion[1], p.getStrokeWidth());
+                path.dumpHex();
+                SkDebugf("fill:\n");
+                fill.dumpHex();
+            }
+            bestTan = SkTMax(bestTan, gMaxRecursion[0]);
+            bestCubic = SkTMax(bestCubic, gMaxRecursion[1]);
+        }
+#endif
+        if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) {
+            return;
+        }
+    }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    if (FLAGS_veryVerbose) {
+        SkDebugf("\n%s max tan=%d cubic=%d\n", __FUNCTION__, bestTan, bestCubic);
+    }
+#endif
+}
+
+DEF_TEST(QuadStrokerRange, reporter) {
+    SkRandom r;
+    SkPaint p;
+    p.setStyle(SkPaint::kStroke_Style);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    int best = 0;
+    sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+    skiatest::Timer timer;
+    for (int i = 0; i < 1000000; ++i) {
+        SkPath path, fill;
+        SkPoint quad[3];
+        quad[0].fX = r.nextRangeF(0, 500);
+        quad[0].fY = r.nextRangeF(0, 500);
+        quad[1].fX = r.nextRangeF(0, 500);
+        quad[1].fY = r.nextRangeF(0, 500);
+        quad[2].fX = r.nextRangeF(0, 500);
+        quad[2].fY = r.nextRangeF(0, 500);
+        path.moveTo(quad[0].fX, quad[0].fY);
+        path.quadTo(quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY);
+        p.setStrokeWidth(r.nextRangeF(0, 500));
+        p.getFillPath(path, &fill);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+        if (best < gMaxRecursion[2]) {
+            if (FLAGS_veryVerbose) {
+                SkDebugf("\n%s quad=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[2],
+                        p.getStrokeWidth());
+                path.dumpHex();
+                SkDebugf("fill:\n");
+                fill.dumpHex();
+            }
+            best = gMaxRecursion[2];
+        }
+#endif
+        if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) {
+            return;
+        }
+    }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    if (FLAGS_verbose) {
+        SkDebugf("\n%s max quad=%d\n", __FUNCTION__, best);
+    }
+#endif
+}
+
+DEF_TEST(CubicStrokerRange, reporter) {
+    SkRandom r;
+    SkPaint p;
+    p.setStyle(SkPaint::kStroke_Style);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    int best[2] = { 0 };
+    sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+    skiatest::Timer timer;
+    for (int i = 0; i < 1000000; ++i) {
+        SkPath path, fill;
+        path.moveTo(r.nextRangeF(0, 500), r.nextRangeF(0, 500));
+        path.cubicTo(r.nextRangeF(0, 500), r.nextRangeF(0, 500), r.nextRangeF(0, 500),
+                r.nextRangeF(0, 500), r.nextRangeF(0, 500), r.nextRangeF(0, 500));
+        p.setStrokeWidth(r.nextRangeF(0, 100));
+        p.getFillPath(path, &fill);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+        if (best[0] < gMaxRecursion[0] || best[1] < gMaxRecursion[1]) {
+            if (FLAGS_veryVerbose) {
+                SkDebugf("\n%s tan=%d cubic=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[0],
+                        gMaxRecursion[1], p.getStrokeWidth());
+                path.dumpHex();
+                SkDebugf("fill:\n");
+                fill.dumpHex();
+            }
+            best[0] = SkTMax(best[0], gMaxRecursion[0]);
+            best[1] = SkTMax(best[1], gMaxRecursion[1]);
+        }
+#endif
+        if (FLAGS_timeout && timer.elapsedMs() > MS_TEST_DURATION) {
+            return;
+        }
+    }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    if (FLAGS_veryVerbose) {
+        SkDebugf("\n%s max tan=%d cubic=%d\n", __FUNCTION__, best[0], best[1]);
+    }
+#endif
+}
+
+
+DEF_TEST(QuadStrokerOneOff, reporter) {
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+    SkPaint p;
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SkDoubleToScalar(164.683548));
+
+    SkPath path, fill;
+path.moveTo(SkBits2Float(0x43c99223), SkBits2Float(0x42b7417e));
+path.quadTo(SkBits2Float(0x4285d839), SkBits2Float(0x43ed6645), SkBits2Float(0x43c941c8), SkBits2Float(0x42b3ace3));
+    p.getFillPath(path, &fill);
+    if (FLAGS_veryVerbose) {
+        SkDebugf("\n%s path\n", __FUNCTION__);
+        path.dump();
+        SkDebugf("fill:\n");
+        fill.dump();
+    }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    if (FLAGS_veryVerbose) {
+        SkDebugf("max quad=%d\n", gMaxRecursion[2]);
+    }
+#endif
+}
+
+DEF_TEST(CubicStrokerOneOff, reporter) {
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+    SkPaint p;
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SkDoubleToScalar(42.835968));
+
+    SkPath path, fill;
+path.moveTo(SkBits2Float(0x433f5370), SkBits2Float(0x43d1f4b3));
+path.cubicTo(SkBits2Float(0x4331cb76), SkBits2Float(0x43ea3340), SkBits2Float(0x4388f498), SkBits2Float(0x42f7f08d), SkBits2Float(0x43f1cd32), SkBits2Float(0x42802ec1));
+    p.getFillPath(path, &fill);
+    if (FLAGS_veryVerbose) {
+        SkDebugf("\n%s path\n", __FUNCTION__);
+        path.dump();
+        SkDebugf("fill:\n");
+        fill.dump();
+    }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+    if (FLAGS_veryVerbose) {
+        SkDebugf("max tan=%d cubic=%d\n", gMaxRecursion[0], gMaxRecursion[1]);
+    }
+#endif
+}
diff --git a/src/third_party/skia/tests/SubsetPath.cpp b/src/third_party/skia/tests/SubsetPath.cpp
new file mode 100644
index 0000000..6a7660e
--- /dev/null
+++ b/src/third_party/skia/tests/SubsetPath.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SubsetPath.h"
+#include "SkMathPriv.h"
+
+SubsetPath::SubsetPath(const SkPath& path)
+        : fPath(path)
+        , fSubset(1) {
+}
+
+int SubsetPath::range(int* end) const {
+    int leadingZero = SkCLZ(fSubset);
+    int parts = 1 << (31 - leadingZero);
+    int partIndex = fSubset - parts;
+    SkASSERT(partIndex >= 0);
+    int count = fSelected.count();
+    int start = count * partIndex / parts;
+    *end = count * (partIndex + 1) / parts;
+    return start;
+}
+
+bool SubsetPath::subset(bool testFailed, SkPath* sub) {
+    int start, end;
+    if (!testFailed) {
+        start = range(&end);
+        for (; start < end; ++start) {
+            fSelected[start] = true;
+        }
+    }
+    do {
+        do {
+            ++fSubset;
+            start = range(&end);
+ //           SkDebugf("%d s=%d e=%d t=%d\n", fSubset, start, end, fTries);
+            if (end - start > 1) {
+                fTries = fSelected.count();
+            } else if (end - start == 1) {
+                if (--fTries <= 0) {
+                    return false;
+                }
+            }
+        } while (start == end);
+    } while (!fSelected[start]);
+    for (; start < end; ++start) {
+        fSelected[start] = false;
+    }
+#if 1
+    SkDebugf("selected: ");
+    for (int index = 0; index < fSelected.count(); ++index) {
+        SkDebugf("%c", fSelected[index] ? 'x' : '-');
+    }
+#endif
+    *sub = getSubsetPath();
+    return true;
+}
+
+SubsetContours::SubsetContours(const SkPath& path)
+        : SubsetPath(path) {
+    SkPath::RawIter iter(fPath);
+    uint8_t verb;
+    SkPoint pts[4];
+    bool foundCurve = false;
+    int contourCount = 0;
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                break;
+            case SkPath::kLine_Verb:
+            case SkPath::kQuad_Verb:
+            case SkPath::kConic_Verb:
+            case SkPath::kCubic_Verb:
+                foundCurve = true;
+                break;
+            case SkPath::kClose_Verb:
+                ++contourCount;
+                foundCurve = false;
+                break;
+            default:
+                SkDEBUGFAIL("bad verb");
+                return;
+        }
+    }
+    contourCount += foundCurve;
+    for (int index = 0; index < contourCount; ++index) {
+        *fSelected.append() = true;
+    }
+    fTries = contourCount;
+}
+
+SkPath SubsetContours::getSubsetPath() const {
+    SkPath result;
+    result.setFillType(fPath.getFillType());
+    if (!fSelected.count()) {
+        return result;
+    }
+    SkPath::RawIter iter(fPath);
+    uint8_t verb;
+    SkPoint pts[4];
+    int contourCount = 0;
+    bool enabled = fSelected[0];
+    bool addMoveTo = true;
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        if (enabled && addMoveTo) {
+            result.moveTo(pts[0]);
+            addMoveTo = false;
+        }
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                break;
+            case SkPath::kLine_Verb:
+                if (enabled) {
+                    result.lineTo(pts[1]);
+                }
+                break;
+            case SkPath::kQuad_Verb:
+                if (enabled) {
+                    result.quadTo(pts[1], pts[2]);
+                }
+                break;
+            case SkPath::kConic_Verb:
+                if (enabled) {
+                    result.conicTo(pts[1], pts[2], iter.conicWeight());
+                }
+                break;
+            case SkPath::kCubic_Verb:
+                 if (enabled) {
+                    result.cubicTo(pts[1], pts[2], pts[3]);
+                }
+                break;
+            case SkPath::kClose_Verb:
+                if (enabled) {
+                    result.close();
+                }
+                if (++contourCount >= fSelected.count()) {
+                    break;
+                }
+                enabled = fSelected[contourCount];
+                addMoveTo = true;
+                continue;
+            default:
+                SkDEBUGFAIL("bad verb");
+                return result;
+        }
+    }
+    return result;
+}
+
+SubsetVerbs::SubsetVerbs(const SkPath& path)
+        : SubsetPath(path) {
+    SkPath::RawIter iter(fPath);
+    uint8_t verb;
+    SkPoint pts[4];
+    int verbCount = 0;
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                break;
+            case SkPath::kLine_Verb:
+            case SkPath::kQuad_Verb:
+            case SkPath::kConic_Verb:
+            case SkPath::kCubic_Verb:
+                ++verbCount;
+                break;
+            case SkPath::kClose_Verb:
+                break;
+            default:
+                SkDEBUGFAIL("bad verb");
+                return;
+        }
+    }
+    for (int index = 0; index < verbCount; ++index) {
+        *fSelected.append() = true;
+    }
+    fTries = verbCount;
+}
+
+SkPath SubsetVerbs::getSubsetPath() const {
+    SkPath result;
+    result.setFillType(fPath.getFillType());
+    if (!fSelected.count()) {
+        return result;
+    }
+    SkPath::RawIter iter(fPath);
+    uint8_t verb;
+    SkPoint pts[4];
+    int verbIndex = 0;
+    bool addMoveTo = true;
+    bool addLineTo = false;
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        bool enabled = SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb
+            ? fSelected[verbIndex++] : false;
+        if (enabled) {
+            if (addMoveTo) {
+                result.moveTo(pts[0]);
+                addMoveTo = false;
+            } else if (addLineTo) {
+                result.lineTo(pts[0]);
+                addLineTo = false;
+            }
+        }
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                break;
+            case SkPath::kLine_Verb:
+                if (enabled) {
+                    result.lineTo(pts[1]);
+                }
+                break;
+            case SkPath::kQuad_Verb:
+                if (enabled) {
+                    result.quadTo(pts[1], pts[2]);
+                }
+                break;
+            case SkPath::kConic_Verb:
+                if (enabled) {
+                    result.conicTo(pts[1], pts[2], iter.conicWeight());
+                }
+                break;
+            case SkPath::kCubic_Verb:
+                 if (enabled) {
+                    result.cubicTo(pts[1], pts[2], pts[3]);
+                }
+                break;
+            case SkPath::kClose_Verb:
+                result.close();
+                addMoveTo = true;
+                addLineTo = false;
+                continue;
+            default:
+                SkDEBUGFAIL("bad verb");
+                return result;
+        }
+        addLineTo = !enabled;
+    }
+    return result;
+}
diff --git a/src/third_party/skia/tests/SubsetPath.h b/src/third_party/skia/tests/SubsetPath.h
new file mode 100644
index 0000000..9e4d231
--- /dev/null
+++ b/src/third_party/skia/tests/SubsetPath.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SubsetPath_DEFINED
+#define SubsetPath_DEFINED
+
+#include "SkPath.h"
+#include "SkTDArray.h"
+
+/* Given a path, generate a the desired minimal subset of the original.
+
+   This does a binary divide-and-conquer on the path, first splitting by
+   contours, and then by verbs. The caller passes whether the previous subset
+   behaved the same as the original. If not, the subset() call restores the
+   prior state before returning a new subset.
+
+   If a path fails a local test, this reduces the data to the
+   minimal set that fails using a pattern like:
+
+   bool testFailed = true;
+   SkPath minimal;
+   SubsetContours subsetContours(testPath);
+   while (subsetContours.subset(testFailed, &minimal)) {
+       testFailed = myPathTest(minimal);
+   }
+   testFailed = true;
+   SubsetVerbs subsetVerbs(testPath);
+   while (subsetVerbs.subset(testFailed, &minimal)) {
+       testFailed = myPathTest(minimal);
+   }
+*/
+
+class SubsetPath {
+public:
+    SubsetPath(const SkPath& path);
+    virtual ~SubsetPath() {}
+    bool subset(bool testFailed, SkPath* sub);
+protected:
+    int range(int* end) const;
+    virtual SkPath getSubsetPath() const = 0;
+
+    const SkPath& fPath;
+    SkTDArray<bool> fSelected;
+    int fSubset;
+    int fTries;
+
+};
+
+class SubsetContours : public SubsetPath {
+public:
+    SubsetContours(const SkPath& path);
+protected:
+    SkPath getSubsetPath() const override;
+};
+
+class SubsetVerbs : public SubsetPath {
+public:
+    SubsetVerbs(const SkPath& path);
+protected:
+    SkPath getSubsetPath() const override;
+};
+
+#endif
diff --git a/src/third_party/skia/tests/SurfaceSemaphoreTest.cpp b/src/third_party/skia/tests/SurfaceSemaphoreTest.cpp
new file mode 100644
index 0000000..72e8e05
--- /dev/null
+++ b/src/third_party/skia/tests/SurfaceSemaphoreTest.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContextFactory.h"
+#include "GrTest.h"
+#include "Test.h"
+
+#include "GrBackendSemaphore.h"
+#include "GrBackendSurface.h"
+#include "SkCanvas.h"
+#include "SkSurface.h"
+
+#ifdef SK_VULKAN
+#include "vk/GrVkTypes.h"
+#endif
+
+static const int MAIN_W = 8, MAIN_H = 16;
+static const int CHILD_W = 16, CHILD_H = 16;
+
+void check_pixels(skiatest::Reporter* reporter, const SkBitmap& bitmap) {
+    const uint32_t* canvasPixels = static_cast<const uint32_t*>(bitmap.getPixels());
+
+    bool failureFound = false;
+    SkPMColor expectedPixel;
+    for (int cy = 0; cy < CHILD_H && !failureFound; ++cy) {
+        for (int cx = 0; cx < CHILD_W && !failureFound; ++cx) {
+            SkPMColor canvasPixel = canvasPixels[cy * CHILD_W + cx];
+            if (cy < CHILD_H / 2) {
+                if (cx < CHILD_W / 2) {
+                    expectedPixel = 0xFF0000FF; // Red
+                } else {
+                    expectedPixel = 0xFFFF0000; // Blue
+                }
+            } else {
+                expectedPixel = 0xFF00FF00; // Green
+            }
+            if (expectedPixel != canvasPixel) {
+                failureFound = true;
+                ERRORF(reporter, "Wrong color at %d, %d. Got 0x%08x when we expected 0x%08x",
+                       cx, cy, canvasPixel, expectedPixel);
+            }
+        }
+    }
+}
+
+void draw_child(skiatest::Reporter* reporter,
+                const sk_gpu_test::ContextInfo& childInfo,
+                const GrBackendObject& backendImage,
+                const GrBackendSemaphore& semaphore) {
+    GrBackendTexture backendTexture = GrTest::CreateBackendTexture(childInfo.backend(),
+                                                                   MAIN_W, MAIN_H,
+                                                                   kRGBA_8888_GrPixelConfig,
+                                                                   backendImage);
+
+    childInfo.testContext()->makeCurrent();
+
+    const SkImageInfo childII = SkImageInfo::Make(CHILD_W, CHILD_H, kRGBA_8888_SkColorType,
+                                                  kPremul_SkAlphaType);
+
+    GrContext* childCtx = childInfo.grContext();
+    sk_sp<SkSurface> childSurface(SkSurface::MakeRenderTarget(childCtx, SkBudgeted::kNo,
+                                                              childII, 0, kTopLeft_GrSurfaceOrigin,
+                                                              nullptr));
+
+    sk_sp<SkImage> childImage = SkImage::MakeFromTexture(childCtx,
+                                                         backendTexture,
+                                                         kTopLeft_GrSurfaceOrigin,
+                                                         kPremul_SkAlphaType,
+                                                         nullptr);
+
+    SkCanvas* childCanvas = childSurface->getCanvas();
+    childCanvas->clear(SK_ColorRED);
+
+    childSurface->wait(1, &semaphore);
+
+    childCanvas->drawImage(childImage, CHILD_W/2, 0);
+
+    SkPaint paint;
+    paint.setColor(SK_ColorGREEN);
+    SkIRect rect = SkIRect::MakeLTRB(0, CHILD_H/2, CHILD_W, CHILD_H);
+    childCanvas->drawIRect(rect, paint);
+
+    // read pixels
+    SkBitmap bitmap;
+    bitmap.allocPixels(childII);
+    childCanvas->readPixels(bitmap, 0, 0);
+
+    check_pixels(reporter, bitmap);
+}
+
+void surface_semaphore_test(skiatest::Reporter* reporter,
+                            const sk_gpu_test::ContextInfo& mainInfo,
+                            const sk_gpu_test::ContextInfo& childInfo1,
+                            const sk_gpu_test::ContextInfo& childInfo2) {
+    GrContext* mainCtx = mainInfo.grContext();
+    if (!mainCtx->caps()->fenceSyncSupport()) {
+        return;
+    }
+
+    const SkImageInfo ii = SkImageInfo::Make(MAIN_W, MAIN_H, kRGBA_8888_SkColorType,
+                                             kPremul_SkAlphaType);
+
+    sk_sp<SkSurface> mainSurface(SkSurface::MakeRenderTarget(mainCtx, SkBudgeted::kNo,
+                                                             ii, 0, kTopLeft_GrSurfaceOrigin,
+                                                             nullptr));
+    SkCanvas* mainCanvas = mainSurface->getCanvas();
+    mainCanvas->clear(SK_ColorBLUE);
+
+    SkAutoTArray<GrBackendSemaphore> semaphores(2);
+
+    mainSurface->flushAndSignalSemaphores(2, semaphores.get());
+
+    sk_sp<SkImage> mainImage = mainSurface->makeImageSnapshot();
+    GrBackendObject backendImage = mainImage->getTextureHandle(false);
+
+    draw_child(reporter, childInfo1, backendImage, semaphores[0]);
+
+#ifdef SK_VULKAN
+    if (kVulkan_GrBackend == mainInfo.backend()) {
+        // In Vulkan we need to make sure we are sending the correct VkImageLayout in with the
+        // backendImage. After the first child draw the layout gets changed to SHADER_READ, so
+        // we just manually set that here.
+        GrVkImageInfo* vkInfo = (GrVkImageInfo*)backendImage;
+        vkInfo->updateImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+    }
+#endif
+
+    draw_child(reporter, childInfo2, backendImage, semaphores[1]);
+}
+
+DEF_GPUTEST(SurfaceSemaphores, reporter, factory) {
+#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC)
+    static constexpr auto kNativeGLType = sk_gpu_test::GrContextFactory::kGL_ContextType;
+#else
+    static constexpr auto kNativeGLType = sk_gpu_test::GrContextFactory::kGLES_ContextType;
+#endif
+
+    for (int typeInt = 0; typeInt < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++typeInt) {
+        sk_gpu_test::GrContextFactory::ContextType contextType =
+                (sk_gpu_test::GrContextFactory::ContextType) typeInt;
+        // Use "native" instead of explicitly trying OpenGL and OpenGL ES. Do not use GLES on
+        // desktop since tests do not account for not fixing http://skbug.com/2809
+        if (contextType == sk_gpu_test::GrContextFactory::kGL_ContextType ||
+            contextType == sk_gpu_test::GrContextFactory::kGLES_ContextType) {
+            if (contextType != kNativeGLType) {
+                continue;
+            }
+        }
+        sk_gpu_test::ContextInfo ctxInfo = factory->getContextInfo(
+                contextType, sk_gpu_test::GrContextFactory::ContextOverrides::kDisableNVPR);
+        if (!sk_gpu_test::GrContextFactory::IsRenderingContext(contextType)) {
+            continue;
+        }
+        skiatest::ReporterContext ctx(
+                reporter, SkString(sk_gpu_test::GrContextFactory::ContextTypeName(contextType)));
+        if (ctxInfo.grContext()) {
+            sk_gpu_test::ContextInfo child1 = factory->getSharedContextInfo(ctxInfo.grContext(), 0);
+            sk_gpu_test::ContextInfo child2 = factory->getSharedContextInfo(ctxInfo.grContext(), 1);
+            if (!child1.grContext() || !child2.grContext()) {
+                continue;
+            }
+
+            surface_semaphore_test(reporter, ctxInfo, child1, child2);
+        }
+    }
+}
+
+#endif
diff --git a/src/third_party/skia/tests/SurfaceTest.cpp b/src/third_party/skia/tests/SurfaceTest.cpp
index 69c8b84..5249b3d 100644
--- a/src/third_party/skia/tests/SurfaceTest.cpp
+++ b/src/third_party/skia/tests/SurfaceTest.cpp
@@ -5,237 +5,271 @@
  * found in the LICENSE file.
  */
 
+#include <functional>
 #include "SkCanvas.h"
+#include "SkColorSpace_Base.h"
 #include "SkData.h"
-#include "SkDecodingImageGenerator.h"
-#include "SkImageEncoder.h"
+#include "SkDevice.h"
+#include "SkImage_Base.h"
+#include "SkOverdrawCanvas.h"
+#include "SkPath.h"
+#include "SkRegion.h"
 #include "SkRRect.h"
 #include "SkSurface.h"
 #include "SkUtils.h"
 #include "Test.h"
 
 #if SK_SUPPORT_GPU
-#include "GrContextFactory.h"
-#else
-class GrContextFactory;
-class GrContext;
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrRenderTargetContext.h"
+#include "GrGpu.h"
+#include "GrResourceProvider.h"
+#include "GrTest.h"
+#include <vector>
 #endif
 
-enum SurfaceType {
-    kRaster_SurfaceType,
-    kRasterDirect_SurfaceType,
-    kGpu_SurfaceType,
-    kGpuScratch_SurfaceType,
-};
+#include <initializer_list>
 
-static void release_storage(void* pixels, void* context) {
+static void release_direct_surface_storage(void* pixels, void* context) {
     SkASSERT(pixels == context);
     sk_free(pixels);
 }
-
-static SkSurface* createSurface(SurfaceType surfaceType, GrContext* context,
-                                SkImageInfo* requestedInfo = NULL) {
-    static const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
-
+static sk_sp<SkSurface> create_surface(SkAlphaType at = kPremul_SkAlphaType,
+                                       SkImageInfo* requestedInfo = nullptr) {
+    const SkImageInfo info = SkImageInfo::MakeN32(10, 10, at);
     if (requestedInfo) {
         *requestedInfo = info;
     }
-
-    switch (surfaceType) {
-        case kRaster_SurfaceType:
-            return SkSurface::NewRaster(info);
-        case kRasterDirect_SurfaceType: {
-            const size_t rowBytes = info.minRowBytes();
-            void* storage = sk_malloc_throw(info.getSafeSize(rowBytes));
-            return SkSurface::NewRasterDirectReleaseProc(info, storage, rowBytes,
-                                                         release_storage, storage);
-        }
-        case kGpu_SurfaceType:
-#if SK_SUPPORT_GPU
-            return context ? SkSurface::NewRenderTarget(context, info, 0, NULL) : NULL;
-#endif
-            break;
-        case kGpuScratch_SurfaceType:
-#if SK_SUPPORT_GPU
-            return context ? SkSurface::NewScratchRenderTarget(context, info) : NULL;
-#endif
-            break;
+    return SkSurface::MakeRaster(info);
+}
+static sk_sp<SkSurface> create_direct_surface(SkAlphaType at = kPremul_SkAlphaType,
+                                              SkImageInfo* requestedInfo = nullptr) {
+    const SkImageInfo info = SkImageInfo::MakeN32(10, 10, at);
+    if (requestedInfo) {
+        *requestedInfo = info;
     }
-    return NULL;
-}
-
-enum ImageType {
-    kRasterCopy_ImageType,
-    kRasterData_ImageType,
-    kGpu_ImageType,
-    kCodec_ImageType,
-};
-
-static void test_image(skiatest::Reporter* reporter) {
-    SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
-    size_t rowBytes = info.minRowBytes();
-    size_t size = info.getSafeSize(rowBytes);
-    SkData* data = SkData::NewUninitialized(size);
-
-    REPORTER_ASSERT(reporter, 1 == data->getRefCnt());
-    SkImage* image = SkImage::NewRasterData(info, data, rowBytes);
-    REPORTER_ASSERT(reporter, 2 == data->getRefCnt());
-    image->unref();
-    REPORTER_ASSERT(reporter, 1 == data->getRefCnt());
-    data->unref();
-}
-
-static SkImage* createImage(ImageType imageType, GrContext* context,
-                            SkColor color) {
-    const SkPMColor pmcolor = SkPreMultiplyColor(color);
-    const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
     const size_t rowBytes = info.minRowBytes();
-    const size_t size = rowBytes * info.height();
-
-    SkAutoTUnref<SkData> data(SkData::NewUninitialized(size));
-    void* addr = data->writable_data();
-    sk_memset32((SkPMColor*)addr, pmcolor, SkToInt(size >> 2));
-
-    switch (imageType) {
-        case kRasterCopy_ImageType:
-            return SkImage::NewRasterCopy(info, addr, rowBytes);
-        case kRasterData_ImageType:
-            return SkImage::NewRasterData(info, data, rowBytes);
-        case kGpu_ImageType:
-            return NULL;        // TODO
-        case kCodec_ImageType: {
-            SkBitmap bitmap;
-            bitmap.installPixels(info, addr, rowBytes);
-            SkAutoTUnref<SkData> src(
-                 SkImageEncoder::EncodeData(bitmap, SkImageEncoder::kPNG_Type,
-                                            100));
-            return SkImage::NewFromGenerator(
-                SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()));
-        }
-    }
-    SkASSERT(false);
-    return NULL;
+    void* storage = sk_malloc_throw(info.getSafeSize(rowBytes));
+    return SkSurface::MakeRasterDirectReleaseProc(info, storage, rowBytes,
+                                                  release_direct_surface_storage,
+                                                  storage);
 }
+#if SK_SUPPORT_GPU
+static sk_sp<SkSurface> create_gpu_surface(GrContext* context, SkAlphaType at = kPremul_SkAlphaType,
+                                           SkImageInfo* requestedInfo = nullptr) {
+    const SkImageInfo info = SkImageInfo::MakeN32(10, 10, at);
+    if (requestedInfo) {
+        *requestedInfo = info;
+    }
+    return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
+}
+static sk_sp<SkSurface> create_gpu_scratch_surface(GrContext* context,
+                                                   SkAlphaType at = kPremul_SkAlphaType,
+                                                   SkImageInfo* requestedInfo = nullptr) {
+    const SkImageInfo info = SkImageInfo::MakeN32(10, 10, at);
+    if (requestedInfo) {
+        *requestedInfo = info;
+    }
+    return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info);
+}
+#endif
 
-static void test_imagepeek(skiatest::Reporter* reporter) {
-    static const struct {
-        ImageType   fType;
-        bool        fPeekShouldSucceed;
-    } gRec[] = {
-        { kRasterCopy_ImageType,    true    },
-        { kRasterData_ImageType,    true    },
-        { kGpu_ImageType,           false   },
-        { kCodec_ImageType,         false   },
-    };
+DEF_TEST(SurfaceEmpty, reporter) {
+    const SkImageInfo info = SkImageInfo::Make(0, 0, kN32_SkColorType, kPremul_SkAlphaType);
+    REPORTER_ASSERT(reporter, nullptr == SkSurface::MakeRaster(info));
+    REPORTER_ASSERT(reporter, nullptr == SkSurface::MakeRasterDirect(info, nullptr, 0));
 
+}
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceEmpty_Gpu, reporter, ctxInfo) {
+    const SkImageInfo info = SkImageInfo::Make(0, 0, kN32_SkColorType, kPremul_SkAlphaType);
+    REPORTER_ASSERT(reporter, nullptr ==
+                    SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo, info));
+}
+#endif
+
+static void test_canvas_peek(skiatest::Reporter* reporter,
+                             sk_sp<SkSurface>& surface,
+                             const SkImageInfo& requestInfo,
+                             bool expectPeekSuccess) {
     const SkColor color = SK_ColorRED;
     const SkPMColor pmcolor = SkPreMultiplyColor(color);
+    surface->getCanvas()->clear(color);
 
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
-        SkImageInfo info;
-        size_t rowBytes;
+    SkPixmap pmap;
+    bool success = surface->getCanvas()->peekPixels(&pmap);
+    REPORTER_ASSERT(reporter, expectPeekSuccess == success);
 
-        SkAutoTUnref<SkImage> image(createImage(gRec[i].fType, NULL, color));
-        if (!image.get()) {
-            continue;   // gpu may not be enabled
-        }
-        const void* addr = image->peekPixels(&info, &rowBytes);
-        bool success = SkToBool(addr);
-        REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success);
-        if (success) {
-            REPORTER_ASSERT(reporter, 10 == info.width());
-            REPORTER_ASSERT(reporter, 10 == info.height());
-            REPORTER_ASSERT(reporter, kN32_SkColorType == info.colorType());
-            REPORTER_ASSERT(reporter, kPremul_SkAlphaType == info.alphaType() ||
-                            kOpaque_SkAlphaType == info.alphaType());
-            REPORTER_ASSERT(reporter, info.minRowBytes() <= rowBytes);
-            REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr);
+    SkPixmap pmap2;
+    const void* addr2 = surface->peekPixels(&pmap2) ? pmap2.addr() : nullptr;
+
+    if (success) {
+        REPORTER_ASSERT(reporter, requestInfo == pmap.info());
+        REPORTER_ASSERT(reporter, requestInfo.minRowBytes() <= pmap.rowBytes());
+        REPORTER_ASSERT(reporter, pmcolor == *pmap.addr32());
+
+        REPORTER_ASSERT(reporter, pmap.addr() == pmap2.addr());
+        REPORTER_ASSERT(reporter, pmap.info() == pmap2.info());
+        REPORTER_ASSERT(reporter, pmap.rowBytes() == pmap2.rowBytes());
+    } else {
+        REPORTER_ASSERT(reporter, nullptr == addr2);
+    }
+}
+DEF_TEST(SurfaceCanvasPeek, reporter) {
+    for (auto& surface_func : { &create_surface, &create_direct_surface }) {
+        SkImageInfo requestInfo;
+        auto surface(surface_func(kPremul_SkAlphaType, &requestInfo));
+        test_canvas_peek(reporter, surface, requestInfo, true);
+    }
+}
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceCanvasPeek_Gpu, reporter, ctxInfo) {
+    for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) {
+        SkImageInfo requestInfo;
+        auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, &requestInfo));
+        test_canvas_peek(reporter, surface, requestInfo, false);
+    }
+}
+#endif
+
+static void test_snapshot_alphatype(skiatest::Reporter* reporter, const sk_sp<SkSurface>& surface,
+                                    SkAlphaType expectedAlphaType) {
+    REPORTER_ASSERT(reporter, surface);
+    if (surface) {
+        sk_sp<SkImage> image(surface->makeImageSnapshot());
+        REPORTER_ASSERT(reporter, image);
+        if (image) {
+            REPORTER_ASSERT(reporter, image->alphaType() == expectedAlphaType);
         }
     }
 }
-
-static void test_canvaspeek(skiatest::Reporter* reporter,
-                            GrContextFactory* factory) {
-    static const struct {
-        SurfaceType fType;
-        bool        fPeekShouldSucceed;
-    } gRec[] = {
-        { kRaster_SurfaceType,          true    },
-        { kRasterDirect_SurfaceType,    true    },
+DEF_TEST(SurfaceSnapshotAlphaType, reporter) {
+    for (auto& surface_func : { &create_surface, &create_direct_surface }) {
+        for (auto& at: { kOpaque_SkAlphaType, kPremul_SkAlphaType, kUnpremul_SkAlphaType }) {
+            auto surface(surface_func(at, nullptr));
+            test_snapshot_alphatype(reporter, surface, at);
+        }
+    }
+}
 #if SK_SUPPORT_GPU
-        { kGpu_SurfaceType,             false   },
-        { kGpuScratch_SurfaceType,      false   },
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceSnapshotAlphaType_Gpu, reporter, ctxInfo) {
+    for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) {
+        // GPU doesn't support creating unpremul surfaces, so only test opaque + premul
+        for (auto& at : { kOpaque_SkAlphaType, kPremul_SkAlphaType }) {
+            auto surface(surface_func(ctxInfo.grContext(), at, nullptr));
+            test_snapshot_alphatype(reporter, surface, at);
+        }
+    }
+}
 #endif
+
+static GrBackendObject get_surface_backend_texture_handle(
+    SkSurface* s, SkSurface::BackendHandleAccess a) {
+    return s->getTextureHandle(a);
+}
+static GrBackendObject get_surface_backend_render_target_handle(
+    SkSurface* s, SkSurface::BackendHandleAccess a) {
+    GrBackendObject result;
+    if (!s->getRenderTargetHandle(&result, a)) {
+        return 0;
+    }
+    return result;
+}
+
+static void test_backend_handle_access_copy_on_write(
+    skiatest::Reporter* reporter, SkSurface* surface, SkSurface::BackendHandleAccess mode,
+    GrBackendObject (*func)(SkSurface*, SkSurface::BackendHandleAccess)) {
+    GrBackendObject obj1 = func(surface, mode);
+    sk_sp<SkImage> snap1(surface->makeImageSnapshot());
+
+    GrBackendObject obj2 = func(surface, mode);
+    sk_sp<SkImage> snap2(surface->makeImageSnapshot());
+
+    // If the access mode triggers CoW, then the backend objects should reflect it.
+    REPORTER_ASSERT(reporter, (obj1 == obj2) == (snap1 == snap2));
+}
+DEF_TEST(SurfaceBackendHandleAccessCopyOnWrite, reporter) {
+    const SkSurface::BackendHandleAccess accessModes[] = {
+        SkSurface::kFlushRead_BackendHandleAccess,
+        SkSurface::kFlushWrite_BackendHandleAccess,
+        SkSurface::kDiscardWrite_BackendHandleAccess,
     };
-
-    const SkColor color = SK_ColorRED;
-    const SkPMColor pmcolor = SkPreMultiplyColor(color);
-
-    int cnt;
-#if SK_SUPPORT_GPU
-    cnt = GrContextFactory::kGLContextTypeCnt;
-#else
-    cnt = 1;
-#endif
-
-    for (int i= 0; i < cnt; ++i) {
-        GrContext* context = NULL;
-#if SK_SUPPORT_GPU
-        GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
-        if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
-            continue;
+    for (auto& handle_access_func :
+            { &get_surface_backend_texture_handle, &get_surface_backend_render_target_handle }) {
+        for (auto& accessMode : accessModes) {
+            auto surface(create_surface());
+            test_backend_handle_access_copy_on_write(reporter, surface.get(), accessMode,
+                                                     handle_access_func);
         }
-        context = factory->get(glCtxType);
-
-        if (NULL == context) {
-            continue;
-        }
-#endif
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
-            SkImageInfo info, requestInfo;
-            size_t rowBytes;
-
-            SkAutoTUnref<SkSurface> surface(createSurface(gRec[i].fType, context,
-                                                          &requestInfo));
-            surface->getCanvas()->clear(color);
-
-            const void* addr = surface->getCanvas()->peekPixels(&info, &rowBytes);
-            bool success = SkToBool(addr);
-            REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success);
-
-            SkImageInfo info2;
-            size_t rb2;
-            const void* addr2 = surface->peekPixels(&info2, &rb2);
-
-            if (success) {
-                REPORTER_ASSERT(reporter, requestInfo == info);
-                REPORTER_ASSERT(reporter, requestInfo.minRowBytes() <= rowBytes);
-                REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr);
-
-                REPORTER_ASSERT(reporter, addr2 == addr);
-                REPORTER_ASSERT(reporter, info2 == info);
-                REPORTER_ASSERT(reporter, rb2 == rowBytes);
-            } else {
-                REPORTER_ASSERT(reporter, NULL == addr2);
+    }
+}
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceBackendHandleAccessCopyOnWrite_Gpu, reporter, ctxInfo) {
+        const SkSurface::BackendHandleAccess accessModes[] = {
+        SkSurface::kFlushRead_BackendHandleAccess,
+        SkSurface::kFlushWrite_BackendHandleAccess,
+        SkSurface::kDiscardWrite_BackendHandleAccess,
+    };
+    for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) {
+        for (auto& handle_access_func :
+                { &get_surface_backend_texture_handle, &get_surface_backend_render_target_handle }) {
+            for (auto& accessMode : accessModes) {
+                auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr));
+                test_backend_handle_access_copy_on_write(reporter, surface.get(), accessMode,
+                                                         handle_access_func);
             }
         }
     }
 }
+#endif
 
-static void TestSurfaceCopyOnWrite(skiatest::Reporter* reporter, SurfaceType surfaceType,
-                                   GrContext* context) {
-    // Verify that the right canvas commands trigger a copy on write
-    SkSurface* surface = createSurface(surfaceType, context);
-    SkAutoTUnref<SkSurface> aur_surface(surface);
+#if SK_SUPPORT_GPU
+
+static void test_backend_handle_unique_id(
+    skiatest::Reporter* reporter, SkSurface* surface,
+    GrBackendObject (*func)(SkSurface*, SkSurface::BackendHandleAccess)) {
+    sk_sp<SkImage> image0(surface->makeImageSnapshot());
+    GrBackendObject obj = func(surface, SkSurface::kFlushRead_BackendHandleAccess);
+    REPORTER_ASSERT(reporter, obj != 0);
+    sk_sp<SkImage> image1(surface->makeImageSnapshot());
+    // just read access should not affect the snapshot
+    REPORTER_ASSERT(reporter, image0->uniqueID() == image1->uniqueID());
+
+    obj = func(surface, SkSurface::kFlushWrite_BackendHandleAccess);
+    REPORTER_ASSERT(reporter, obj != 0);
+    sk_sp<SkImage> image2(surface->makeImageSnapshot());
+    // expect a new image, since we claimed we would write
+    REPORTER_ASSERT(reporter, image0->uniqueID() != image2->uniqueID());
+
+    obj = func(surface, SkSurface::kDiscardWrite_BackendHandleAccess);
+    REPORTER_ASSERT(reporter, obj != 0);
+    sk_sp<SkImage> image3(surface->makeImageSnapshot());
+    // expect a new(er) image, since we claimed we would write
+    REPORTER_ASSERT(reporter, image0->uniqueID() != image3->uniqueID());
+    REPORTER_ASSERT(reporter, image2->uniqueID() != image3->uniqueID());
+}
+// No CPU test.
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceBackendHandleAccessIDs_Gpu, reporter, ctxInfo) {
+    for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) {
+        for (auto& test_func : { &test_backend_handle_unique_id }) {
+            for (auto& handle_access_func :
+                { &get_surface_backend_texture_handle, &get_surface_backend_render_target_handle}) {
+                auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr));
+                test_func(reporter, surface.get(), handle_access_func);
+            }
+        }
+    }
+}
+#endif
+
+// Verify that the right canvas commands trigger a copy on write.
+static void test_copy_on_write(skiatest::Reporter* reporter, SkSurface* surface) {
     SkCanvas* canvas = surface->getCanvas();
 
     const SkRect testRect =
         SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
                          SkIntToScalar(4), SkIntToScalar(5));
-    SkMatrix testMatrix;
-    testMatrix.reset();
-    testMatrix.setScale(SkIntToScalar(2), SkIntToScalar(3));
-
     SkPath testPath;
     testPath.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
                                       SkIntToScalar(2), SkIntToScalar(1)));
@@ -279,11 +313,11 @@
 
 #define EXPECT_COPY_ON_WRITE(command)                               \
     {                                                               \
-        SkImage* imageBefore = surface->newImageSnapshot();         \
-        SkAutoTUnref<SkImage> aur_before(imageBefore);              \
+        sk_sp<SkImage> imageBefore = surface->makeImageSnapshot();  \
+        sk_sp<SkImage> aur_before(imageBefore);                     \
         canvas-> command ;                                          \
-        SkImage* imageAfter = surface->newImageSnapshot();          \
-        SkAutoTUnref<SkImage> aur_after(imageAfter);                \
+        sk_sp<SkImage> imageAfter = surface->makeImageSnapshot();   \
+        sk_sp<SkImage> aur_after(imageAfter);                       \
         REPORTER_ASSERT(reporter, imageBefore != imageAfter);       \
     }
 
@@ -296,179 +330,633 @@
     EXPECT_COPY_ON_WRITE(drawRRect(testRRect, testPaint))
     EXPECT_COPY_ON_WRITE(drawPath(testPath, testPaint))
     EXPECT_COPY_ON_WRITE(drawBitmap(testBitmap, 0, 0))
-    EXPECT_COPY_ON_WRITE(drawBitmapRect(testBitmap, NULL, testRect))
-    EXPECT_COPY_ON_WRITE(drawBitmapMatrix(testBitmap, testMatrix, NULL))
-    EXPECT_COPY_ON_WRITE(drawBitmapNine(testBitmap, testIRect, testRect, NULL))
-    EXPECT_COPY_ON_WRITE(drawSprite(testBitmap, 0, 0, NULL))
-    EXPECT_COPY_ON_WRITE(drawText(testText.c_str(), testText.size(), 0, 1, testPaint))
+    EXPECT_COPY_ON_WRITE(drawBitmapRect(testBitmap, testRect, nullptr))
+    EXPECT_COPY_ON_WRITE(drawBitmapNine(testBitmap, testIRect, testRect, nullptr))
+    EXPECT_COPY_ON_WRITE(drawString(testText, 0, 1, testPaint))
     EXPECT_COPY_ON_WRITE(drawPosText(testText.c_str(), testText.size(), testPoints2, \
         testPaint))
-    EXPECT_COPY_ON_WRITE(drawTextOnPath(testText.c_str(), testText.size(), testPath, NULL, \
+    EXPECT_COPY_ON_WRITE(drawTextOnPath(testText.c_str(), testText.size(), testPath, nullptr, \
         testPaint))
 }
+DEF_TEST(SurfaceCopyOnWrite, reporter) {
+    test_copy_on_write(reporter, create_surface().get());
+}
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceCopyOnWrite_Gpu, reporter, ctxInfo) {
+    for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) {
+        auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr));
+        test_copy_on_write(reporter, surface.get());
+    }
+}
+#endif
 
-static void TestSurfaceWritableAfterSnapshotRelease(skiatest::Reporter* reporter,
-                                                    SurfaceType surfaceType,
-                                                    GrContext* context) {
+static void test_writable_after_snapshot_release(skiatest::Reporter* reporter,
+                                                 SkSurface* surface) {
     // This test succeeds by not triggering an assertion.
     // The test verifies that the surface remains writable (usable) after
     // acquiring and releasing a snapshot without triggering a copy on write.
-    SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context));
     SkCanvas* canvas = surface->getCanvas();
     canvas->clear(1);
-    surface->newImageSnapshot()->unref();  // Create and destroy SkImage
+    surface->makeImageSnapshot();  // Create and destroy SkImage
     canvas->clear(2);  // Must not assert internally
 }
+DEF_TEST(SurfaceWriteableAfterSnapshotRelease, reporter) {
+    test_writable_after_snapshot_release(reporter, create_surface().get());
+}
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceWriteableAfterSnapshotRelease_Gpu, reporter, ctxInfo) {
+    for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) {
+        auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr));
+        test_writable_after_snapshot_release(reporter, surface.get());
+    }
+}
+#endif
 
 #if SK_SUPPORT_GPU
-static void TestSurfaceInCache(skiatest::Reporter* reporter,
-                               SurfaceType surfaceType,
-                               GrContext* context) {
-    context->freeGpuResources();
-    int resourceCount;
-
-    context->getResourceCacheUsage(&resourceCount, NULL);
-    REPORTER_ASSERT(reporter, 0 == resourceCount);
-    SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context));
-    // Note: the stencil buffer is always cached, so kGpu_SurfaceType uses
-    // one cached resource, and kGpuScratch_SurfaceType uses two.
-    int expectedCachedResources = surfaceType == kGpuScratch_SurfaceType ? 2 : 1;
-    context->getResourceCacheUsage(&resourceCount, NULL);
-    REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount);
-
-    // Verify that all the cached resources are locked in cache.
-    context->freeGpuResources();
-    context->getResourceCacheUsage(&resourceCount, NULL);
-    REPORTER_ASSERT(reporter, expectedCachedResources == resourceCount);
-
-    // Verify that all the cached resources are unlocked upon surface release
-    surface.reset(0);
-    context->freeGpuResources();
-    context->getResourceCacheUsage(&resourceCount, NULL);
-    REPORTER_ASSERT(reporter, 0 == resourceCount);
-}
-
-static void Test_crbug263329(skiatest::Reporter* reporter,
-                             SurfaceType surfaceType,
-                             GrContext* context) {
+static void test_crbug263329(skiatest::Reporter* reporter,
+                             SkSurface* surface1,
+                             SkSurface* surface2) {
     // This is a regression test for crbug.com/263329
     // Bug was caused by onCopyOnWrite releasing the old surface texture
     // back to the scratch texture pool even though the texture is used
     // by and active SkImage_Gpu.
-    SkAutoTUnref<SkSurface> surface1(createSurface(surfaceType, context));
-    SkAutoTUnref<SkSurface> surface2(createSurface(surfaceType, context));
     SkCanvas* canvas1 = surface1->getCanvas();
     SkCanvas* canvas2 = surface2->getCanvas();
     canvas1->clear(1);
-    SkAutoTUnref<SkImage> image1(surface1->newImageSnapshot());
+    sk_sp<SkImage> image1(surface1->makeImageSnapshot());
     // Trigger copy on write, new backing is a scratch texture
     canvas1->clear(2);
-    SkAutoTUnref<SkImage> image2(surface1->newImageSnapshot());
+    sk_sp<SkImage> image2(surface1->makeImageSnapshot());
     // Trigger copy on write, old backing should not be returned to scratch
     // pool because it is held by image2
     canvas1->clear(3);
 
     canvas2->clear(4);
-    SkAutoTUnref<SkImage> image3(surface2->newImageSnapshot());
+    sk_sp<SkImage> image3(surface2->makeImageSnapshot());
     // Trigger copy on write on surface2. The new backing store should not
     // be recycling a texture that is held by an existing image.
     canvas2->clear(5);
-    SkAutoTUnref<SkImage> image4(surface2->newImageSnapshot());
-    REPORTER_ASSERT(reporter, image4->getTexture() != image3->getTexture());
+    sk_sp<SkImage> image4(surface2->makeImageSnapshot());
+    REPORTER_ASSERT(reporter, as_IB(image4)->getTexture() != as_IB(image3)->getTexture());
     // The following assertion checks crbug.com/263329
-    REPORTER_ASSERT(reporter, image4->getTexture() != image2->getTexture());
-    REPORTER_ASSERT(reporter, image4->getTexture() != image1->getTexture());
-    REPORTER_ASSERT(reporter, image3->getTexture() != image2->getTexture());
-    REPORTER_ASSERT(reporter, image3->getTexture() != image1->getTexture());
-    REPORTER_ASSERT(reporter, image2->getTexture() != image1->getTexture());
+    REPORTER_ASSERT(reporter, as_IB(image4)->getTexture() != as_IB(image2)->getTexture());
+    REPORTER_ASSERT(reporter, as_IB(image4)->getTexture() != as_IB(image1)->getTexture());
+    REPORTER_ASSERT(reporter, as_IB(image3)->getTexture() != as_IB(image2)->getTexture());
+    REPORTER_ASSERT(reporter, as_IB(image3)->getTexture() != as_IB(image1)->getTexture());
+    REPORTER_ASSERT(reporter, as_IB(image2)->getTexture() != as_IB(image1)->getTexture());
 }
-
-static void TestGetTexture(skiatest::Reporter* reporter,
-                                 SurfaceType surfaceType,
-                                 GrContext* context) {
-    SkAutoTUnref<SkSurface> surface(createSurface(surfaceType, context));
-    SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
-    GrTexture* texture = image->getTexture();
-    if (surfaceType == kGpu_SurfaceType || surfaceType == kGpuScratch_SurfaceType) {
-        REPORTER_ASSERT(reporter, texture);
-        REPORTER_ASSERT(reporter, 0 != texture->getTextureHandle());
-    } else {
-        REPORTER_ASSERT(reporter, NULL == texture);
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceCRBug263329_Gpu, reporter, ctxInfo) {
+    for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) {
+        auto surface1(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr));
+        auto surface2(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr));
+        test_crbug263329(reporter, surface1.get(), surface2.get());
     }
-    surface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode);
-    REPORTER_ASSERT(reporter, image->getTexture() == texture);
 }
 #endif
 
-static void TestSurfaceNoCanvas(skiatest::Reporter* reporter,
-                                          SurfaceType surfaceType,
-                                          GrContext* context,
-                                          SkSurface::ContentChangeMode mode) {
-    // Verifies the robustness of SkSurface for handling use cases where calls
-    // are made before a canvas is created.
-    {
-        // Test passes by not asserting
-        SkSurface* surface = createSurface(surfaceType, context);
-        SkAutoTUnref<SkSurface> aur_surface(surface);
-        surface->notifyContentWillChange(mode);
-        SkDEBUGCODE(surface->validate();)
-    }
-    {
-        SkSurface* surface = createSurface(surfaceType, context);
-        SkAutoTUnref<SkSurface> aur_surface(surface);
-        SkImage* image1 = surface->newImageSnapshot();
-        SkAutoTUnref<SkImage> aur_image1(image1);
-        SkDEBUGCODE(image1->validate();)
-        SkDEBUGCODE(surface->validate();)
-        surface->notifyContentWillChange(mode);
-        SkDEBUGCODE(image1->validate();)
-        SkDEBUGCODE(surface->validate();)
-        SkImage* image2 = surface->newImageSnapshot();
-        SkAutoTUnref<SkImage> aur_image2(image2);
-        SkDEBUGCODE(image2->validate();)
-        SkDEBUGCODE(surface->validate();)
-        REPORTER_ASSERT(reporter, image1 != image2);
-    }
-
+DEF_TEST(SurfaceGetTexture, reporter) {
+    auto surface(create_surface());
+    sk_sp<SkImage> image(surface->makeImageSnapshot());
+    REPORTER_ASSERT(reporter, !as_IB(image)->isTextureBacked());
+    surface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode);
+    REPORTER_ASSERT(reporter, !as_IB(image)->isTextureBacked());
 }
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfacepeekTexture_Gpu, reporter, ctxInfo) {
+    for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) {
+        auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr));
+        sk_sp<SkImage> image(surface->makeImageSnapshot());
 
-DEF_GPUTEST(Surface, reporter, factory) {
-    test_image(reporter);
-
-    TestSurfaceCopyOnWrite(reporter, kRaster_SurfaceType, NULL);
-    TestSurfaceWritableAfterSnapshotRelease(reporter, kRaster_SurfaceType, NULL);
-    TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kDiscard_ContentChangeMode);
-    TestSurfaceNoCanvas(reporter, kRaster_SurfaceType, NULL, SkSurface::kRetain_ContentChangeMode);
-
-    test_imagepeek(reporter);
-    test_canvaspeek(reporter, factory);
+        REPORTER_ASSERT(reporter, as_IB(image)->isTextureBacked());
+        GrBackendObject textureHandle = image->getTextureHandle(false);
+        REPORTER_ASSERT(reporter, 0 != textureHandle);
+        surface->notifyContentWillChange(SkSurface::kDiscard_ContentChangeMode);
+        REPORTER_ASSERT(reporter, as_IB(image)->isTextureBacked());
+        REPORTER_ASSERT(reporter, textureHandle == image->getTextureHandle(false));
+    }
+}
+#endif
 
 #if SK_SUPPORT_GPU
-    TestGetTexture(reporter, kRaster_SurfaceType, NULL);
-    if (factory) {
-        for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
-            GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
-            if (!GrContextFactory::IsRenderingGLContext(glCtxType)) {
-                continue;
-            }
-            GrContext* context = factory->get(glCtxType);
-            if (context) {
-                TestSurfaceInCache(reporter, kGpu_SurfaceType, context);
-                TestSurfaceInCache(reporter, kGpuScratch_SurfaceType, context);
-                Test_crbug263329(reporter, kGpu_SurfaceType, context);
-                Test_crbug263329(reporter, kGpuScratch_SurfaceType, context);
-                TestSurfaceCopyOnWrite(reporter, kGpu_SurfaceType, context);
-                TestSurfaceCopyOnWrite(reporter, kGpuScratch_SurfaceType, context);
-                TestSurfaceWritableAfterSnapshotRelease(reporter, kGpu_SurfaceType, context);
-                TestSurfaceWritableAfterSnapshotRelease(reporter, kGpuScratch_SurfaceType, context);
-                TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kDiscard_ContentChangeMode);
-                TestSurfaceNoCanvas(reporter, kGpuScratch_SurfaceType, context, SkSurface::kDiscard_ContentChangeMode);
-                TestSurfaceNoCanvas(reporter, kGpu_SurfaceType, context, SkSurface::kRetain_ContentChangeMode);
-                TestSurfaceNoCanvas(reporter, kGpuScratch_SurfaceType, context, SkSurface::kRetain_ContentChangeMode);
-                TestGetTexture(reporter, kGpu_SurfaceType, context);
-                TestGetTexture(reporter, kGpuScratch_SurfaceType, context);
+#include "GrGpuResourcePriv.h"
+#include "SkGpuDevice.h"
+#include "SkImage_Gpu.h"
+#include "SkSurface_Gpu.h"
+
+static SkBudgeted is_budgeted(const sk_sp<SkSurface>& surf) {
+    SkSurface_Gpu* gsurf = (SkSurface_Gpu*)surf.get();
+
+    GrRenderTargetProxy* proxy = gsurf->getDevice()->accessRenderTargetContext()
+                                                                        ->asRenderTargetProxy();
+    return proxy->isBudgeted();
+}
+
+static SkBudgeted is_budgeted(SkImage* image) {
+    return ((SkImage_Gpu*)image)->peekProxy()->isBudgeted();
+}
+
+static SkBudgeted is_budgeted(const sk_sp<SkImage> image) {
+    return is_budgeted(image.get());
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceBudget, reporter, ctxInfo) {
+    SkImageInfo info = SkImageInfo::MakeN32Premul(8,8);
+    for (auto budgeted : { SkBudgeted::kNo, SkBudgeted::kYes }) {
+        auto surface(SkSurface::MakeRenderTarget(ctxInfo.grContext(), budgeted, info));
+        SkASSERT(surface);
+        REPORTER_ASSERT(reporter, budgeted == is_budgeted(surface));
+
+        sk_sp<SkImage> image(surface->makeImageSnapshot());
+
+        // Initially the image shares a texture with the surface, and the
+        // the budgets should always match.
+        REPORTER_ASSERT(reporter, budgeted == is_budgeted(surface));
+        REPORTER_ASSERT(reporter, budgeted == is_budgeted(image));
+
+        // Now trigger copy-on-write
+        surface->getCanvas()->clear(SK_ColorBLUE);
+
+        // They don't share a texture anymore but the budgets should still match.
+        REPORTER_ASSERT(reporter, budgeted == is_budgeted(surface));
+        REPORTER_ASSERT(reporter, budgeted == is_budgeted(image));
+    }
+}
+#endif
+
+static void test_no_canvas1(skiatest::Reporter* reporter,
+                            SkSurface* surface,
+                            SkSurface::ContentChangeMode mode) {
+    // Test passes by not asserting
+    surface->notifyContentWillChange(mode);
+    SkDEBUGCODE(surface->validate();)
+}
+static void test_no_canvas2(skiatest::Reporter* reporter,
+                            SkSurface* surface,
+                            SkSurface::ContentChangeMode mode) {
+    // Verifies the robustness of SkSurface for handling use cases where calls
+    // are made before a canvas is created.
+    sk_sp<SkImage> image1 = surface->makeImageSnapshot();
+    sk_sp<SkImage> aur_image1(image1);
+    SkDEBUGCODE(image1->validate();)
+    SkDEBUGCODE(surface->validate();)
+    surface->notifyContentWillChange(mode);
+    SkDEBUGCODE(image1->validate();)
+    SkDEBUGCODE(surface->validate();)
+    sk_sp<SkImage> image2 = surface->makeImageSnapshot();
+    sk_sp<SkImage> aur_image2(image2);
+    SkDEBUGCODE(image2->validate();)
+    SkDEBUGCODE(surface->validate();)
+    REPORTER_ASSERT(reporter, image1 != image2);
+}
+DEF_TEST(SurfaceNoCanvas, reporter) {
+    SkSurface::ContentChangeMode modes[] =
+            { SkSurface::kDiscard_ContentChangeMode, SkSurface::kRetain_ContentChangeMode};
+    for (auto& test_func : { &test_no_canvas1, &test_no_canvas2 }) {
+        for (auto& mode : modes) {
+            test_func(reporter, create_surface().get(), mode);
+        }
+    }
+}
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceNoCanvas_Gpu, reporter, ctxInfo) {
+    SkSurface::ContentChangeMode modes[] =
+            { SkSurface::kDiscard_ContentChangeMode, SkSurface::kRetain_ContentChangeMode};
+    for (auto& surface_func : { &create_gpu_surface, &create_gpu_scratch_surface }) {
+        for (auto& test_func : { &test_no_canvas1, &test_no_canvas2 }) {
+            for (auto& mode : modes) {
+                auto surface(surface_func(ctxInfo.grContext(), kPremul_SkAlphaType, nullptr));
+                test_func(reporter, surface.get(), mode);
             }
         }
     }
+}
 #endif
+
+static void check_rowbytes_remain_consistent(SkSurface* surface, skiatest::Reporter* reporter) {
+    SkPixmap surfacePM;
+    REPORTER_ASSERT(reporter, surface->peekPixels(&surfacePM));
+
+    sk_sp<SkImage> image(surface->makeImageSnapshot());
+    SkPixmap pm;
+    REPORTER_ASSERT(reporter, image->peekPixels(&pm));
+
+    REPORTER_ASSERT(reporter, surfacePM.rowBytes() == pm.rowBytes());
+
+    // trigger a copy-on-write
+    surface->getCanvas()->drawPaint(SkPaint());
+    sk_sp<SkImage> image2(surface->makeImageSnapshot());
+    REPORTER_ASSERT(reporter, image->uniqueID() != image2->uniqueID());
+
+    SkPixmap pm2;
+    REPORTER_ASSERT(reporter, image2->peekPixels(&pm2));
+    REPORTER_ASSERT(reporter, pm2.rowBytes() == pm.rowBytes());
+}
+
+DEF_TEST(surface_rowbytes, reporter) {
+    const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
+
+    auto surf0(SkSurface::MakeRaster(info));
+    check_rowbytes_remain_consistent(surf0.get(), reporter);
+
+    // specify a larger rowbytes
+    auto surf1(SkSurface::MakeRaster(info, 500, nullptr));
+    check_rowbytes_remain_consistent(surf1.get(), reporter);
+
+    // Try some illegal rowByte values
+    auto s = SkSurface::MakeRaster(info, 396, nullptr);    // needs to be at least 400
+    REPORTER_ASSERT(reporter, nullptr == s);
+    s = SkSurface::MakeRaster(info, 1 << 30, nullptr); // allocation to large
+    REPORTER_ASSERT(reporter, nullptr == s);
+}
+
+DEF_TEST(surface_raster_zeroinitialized, reporter) {
+    sk_sp<SkSurface> s(SkSurface::MakeRasterN32Premul(100, 100));
+    SkPixmap pixmap;
+    REPORTER_ASSERT(reporter, s->peekPixels(&pixmap));
+
+    for (int i = 0; i < pixmap.info().width(); ++i) {
+        for (int j = 0; j < pixmap.info().height(); ++j) {
+            REPORTER_ASSERT(reporter, *pixmap.addr32(i, j) == 0);
+        }
+    }
+}
+
+#if SK_SUPPORT_GPU
+static sk_sp<SkSurface> create_gpu_surface_backend_texture(
+    GrContext* context, int sampleCnt, uint32_t color, GrBackendObject* outTexture) {
+    const int kWidth = 10;
+    const int kHeight = 10;
+    std::unique_ptr<uint32_t[]> pixels(new uint32_t[kWidth * kHeight]);
+    sk_memset32(pixels.get(), color, kWidth * kHeight);
+
+    GrBackendObject backendHandle = context->getGpu()->createTestingOnlyBackendTexture(
+        pixels.get(), kWidth, kHeight, kRGBA_8888_GrPixelConfig, true);
+
+    GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                               kWidth,
+                                                               kHeight,
+                                                               kRGBA_8888_GrPixelConfig,
+                                                               backendHandle);
+
+    sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(context, backendTex,
+                                                                 kDefault_GrSurfaceOrigin, sampleCnt,
+                                                                 nullptr, nullptr);
+    if (!surface) {
+        context->getGpu()->deleteTestingOnlyBackendTexture(backendHandle);
+        return nullptr;
+    }
+    *outTexture = backendHandle;
+    return surface;
+}
+
+static sk_sp<SkSurface> create_gpu_surface_backend_texture_as_render_target(
+    GrContext* context, int sampleCnt, uint32_t color, GrBackendObject* outTexture) {
+    const int kWidth = 10;
+    const int kHeight = 10;
+    std::unique_ptr<uint32_t[]> pixels(new uint32_t[kWidth * kHeight]);
+    sk_memset32(pixels.get(), color, kWidth * kHeight);
+
+    GrBackendObject backendHandle = context->getGpu()->createTestingOnlyBackendTexture(
+        pixels.get(), kWidth, kHeight, kRGBA_8888_GrPixelConfig, true);
+
+    GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                               kWidth,
+                                                               kHeight,
+                                                               kRGBA_8888_GrPixelConfig,
+                                                               backendHandle);
+    sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTextureAsRenderTarget(
+            context, backendTex, kDefault_GrSurfaceOrigin, sampleCnt, nullptr, nullptr);
+
+    if (!surface) {
+        context->getGpu()->deleteTestingOnlyBackendTexture(backendHandle);
+        return nullptr;
+    }
+    *outTexture = backendHandle;
+    return surface;
+}
+
+static void test_surface_clear(skiatest::Reporter* reporter, sk_sp<SkSurface> surface,
+                               std::function<sk_sp<GrSurfaceContext>(SkSurface*)> grSurfaceGetter,
+                               uint32_t expectedValue) {
+    if (!surface) {
+        ERRORF(reporter, "Could not create GPU SkSurface.");
+        return;
+    }
+    int w = surface->width();
+    int h = surface->height();
+    std::unique_ptr<uint32_t[]> pixels(new uint32_t[w * h]);
+    sk_memset32(pixels.get(), ~expectedValue, w * h);
+
+    sk_sp<GrSurfaceContext> grSurfaceContext(grSurfaceGetter(surface.get()));
+    if (!grSurfaceContext) {
+        ERRORF(reporter, "Could access render target of GPU SkSurface.");
+        return;
+    }
+    surface.reset();
+
+    SkImageInfo ii = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+    grSurfaceContext->readPixels(ii, pixels.get(), 0, 0, 0);
+    for (int y = 0; y < h; ++y) {
+        for (int x = 0; x < w; ++x) {
+            uint32_t pixel = pixels.get()[y * w + x];
+            if (pixel != expectedValue) {
+                SkString msg;
+                if (expectedValue) {
+                    msg = "SkSurface should have left render target unmodified";
+                } else {
+                    msg = "SkSurface should have cleared the render target";
+                }
+                ERRORF(reporter,
+                       "%s but read 0x%08x (instead of 0x%08x) at %x,%d", msg.c_str(), pixel,
+                       expectedValue, x, y);
+                return;
+            }
+        }
+    }
+}
+
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SurfaceClear_Gpu, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+
+    std::function<sk_sp<GrSurfaceContext>(SkSurface*)> grSurfaceContextGetters[] = {
+        [] (SkSurface* s){
+            return sk_ref_sp(s->getCanvas()->internal_private_accessTopLayerRenderTargetContext());
+        },
+        [] (SkSurface* s){
+            sk_sp<SkImage> i(s->makeImageSnapshot());
+            SkImage_Gpu* gpuImage = (SkImage_Gpu *) as_IB(i);
+            sk_sp<GrTextureProxy> proxy = gpuImage->asTextureProxyRef();
+            GrContext* context = gpuImage->context();
+            return context->contextPriv().makeWrappedSurfaceContext(std::move(proxy),
+                                                                    gpuImage->refColorSpace());
+        }
+    };
+
+    for (auto grSurfaceGetter : grSurfaceContextGetters) {
+        // Test that non-wrapped RTs are created clear.
+        for (auto& surface_func : {&create_gpu_surface, &create_gpu_scratch_surface}) {
+            auto surface = surface_func(context, kPremul_SkAlphaType, nullptr);
+            test_surface_clear(reporter, surface, grSurfaceGetter, 0x0);
+        }
+        // Wrapped RTs are *not* supposed to clear (to allow client to partially update a surface).
+        const uint32_t kOrigColor = 0xABABABAB;
+        for (auto& surfaceFunc : {&create_gpu_surface_backend_texture,
+                                  &create_gpu_surface_backend_texture_as_render_target}) {
+            GrBackendObject textureObject;
+            auto surface = surfaceFunc(context, 0, kOrigColor, &textureObject);
+            test_surface_clear(reporter, surface, grSurfaceGetter, kOrigColor);
+            surface.reset();
+            context->getGpu()->deleteTestingOnlyBackendTexture(textureObject);
+        }
+    }
+}
+
+static void test_surface_draw_partially(
+    skiatest::Reporter* reporter, sk_sp<SkSurface> surface, uint32_t origColor) {
+    const int kW = surface->width();
+    const int kH = surface->height();
+    SkPaint paint;
+    const SkColor kRectColor = ~origColor | 0xFF000000;
+    paint.setColor(kRectColor);
+    surface->getCanvas()->drawRect(SkRect::MakeWH(SkIntToScalar(kW), SkIntToScalar(kH)/2),
+                                   paint);
+    std::unique_ptr<uint32_t[]> pixels(new uint32_t[kW * kH]);
+    sk_memset32(pixels.get(), ~origColor, kW * kH);
+    // Read back RGBA to avoid format conversions that may not be supported on all platforms.
+    SkImageInfo readInfo = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+    SkAssertResult(surface->readPixels(readInfo, pixels.get(), kW * sizeof(uint32_t), 0, 0));
+    bool stop = false;
+    SkPMColor origColorPM = SkPackARGB_as_RGBA((origColor >> 24 & 0xFF),
+                                               (origColor >>  0 & 0xFF),
+                                               (origColor >>  8 & 0xFF),
+                                               (origColor >> 16 & 0xFF));
+    SkPMColor rectColorPM = SkPackARGB_as_RGBA((kRectColor >> 24 & 0xFF),
+                                               (kRectColor >> 16 & 0xFF),
+                                               (kRectColor >>  8 & 0xFF),
+                                               (kRectColor >>  0 & 0xFF));
+    for (int y = 0; y < kH/2 && !stop; ++y) {
+       for (int x = 0; x < kW && !stop; ++x) {
+            REPORTER_ASSERT(reporter, rectColorPM == pixels[x + y * kW]);
+            if (rectColorPM != pixels[x + y * kW]) {
+                stop = true;
+            }
+        }
+    }
+    stop = false;
+    for (int y = kH/2; y < kH && !stop; ++y) {
+        for (int x = 0; x < kW && !stop; ++x) {
+            REPORTER_ASSERT(reporter, origColorPM == pixels[x + y * kW]);
+            if (origColorPM != pixels[x + y * kW]) {
+                stop = true;
+            }
+        }
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfacePartialDraw_Gpu, reporter, ctxInfo) {
+    GrGpu* gpu = ctxInfo.grContext()->getGpu();
+    if (!gpu) {
+        return;
+    }
+    static const uint32_t kOrigColor = 0xFFAABBCC;
+
+    for (auto& surfaceFunc : {&create_gpu_surface_backend_texture,
+                              &create_gpu_surface_backend_texture_as_render_target}) {
+        // Validate that we can draw to the canvas and that the original texture color is
+        // preserved in pixels that aren't rendered to via the surface.
+        // This works only for non-multisampled case.
+        GrBackendObject textureObject;
+        auto surface = surfaceFunc(ctxInfo.grContext(), 0, kOrigColor, &textureObject);
+        if (surface) {
+            test_surface_draw_partially(reporter, surface, kOrigColor);
+            surface.reset();
+            gpu->deleteTestingOnlyBackendTexture(textureObject);
+        }
+    }
+}
+
+
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SurfaceAttachStencil_Gpu, reporter, ctxInfo) {
+    GrGpu* gpu = ctxInfo.grContext()->getGpu();
+    if (!gpu) {
+        return;
+    }
+    if (gpu->caps()->avoidStencilBuffers()) {
+        return;
+    }
+    static const uint32_t kOrigColor = 0xFFAABBCC;
+
+    for (auto& surfaceFunc : {&create_gpu_surface_backend_texture,
+                              &create_gpu_surface_backend_texture_as_render_target}) {
+        for (int sampleCnt : {0, 4, 8}) {
+            GrBackendObject textureObject;
+            auto surface = surfaceFunc(ctxInfo.grContext(), sampleCnt, kOrigColor, &textureObject);
+
+            if (!surface && sampleCnt > 0) {
+              // Certain platforms don't support MSAA, skip these.
+              continue;
+            }
+
+            // Validate that we can attach a stencil buffer to an SkSurface created by either of
+            // our surface functions.
+            GrRenderTarget* rt = surface->getCanvas()
+                ->internal_private_accessTopLayerRenderTargetContext()->accessRenderTarget();
+            REPORTER_ASSERT(reporter,
+                            ctxInfo.grContext()->resourceProvider()->attachStencilAttachment(rt));
+            gpu->deleteTestingOnlyBackendTexture(textureObject);
+        }
+    }
+}
+#endif
+
+static void test_surface_creation_and_snapshot_with_color_space(
+    skiatest::Reporter* reporter,
+    const char* prefix,
+    bool f16Support,
+    std::function<sk_sp<SkSurface>(const SkImageInfo&)> surfaceMaker) {
+
+    auto srgbColorSpace = SkColorSpace::MakeSRGB();
+    auto adobeColorSpace = SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named);
+    const SkMatrix44* srgbMatrix = as_CSB(srgbColorSpace)->toXYZD50();
+    SkASSERT(srgbMatrix);
+    SkColorSpaceTransferFn oddGamma;
+    oddGamma.fA = 1.0f;
+    oddGamma.fB = oddGamma.fC = oddGamma.fD = oddGamma.fE = oddGamma.fF = 0.0f;
+    oddGamma.fG = 4.0f;
+    auto oddColorSpace = SkColorSpace::MakeRGB(oddGamma, *srgbMatrix);
+    auto linearColorSpace = SkColorSpace::MakeSRGBLinear();
+
+    const struct {
+        SkColorType         fColorType;
+        sk_sp<SkColorSpace> fColorSpace;
+        bool                fShouldWork;
+        const char*         fDescription;
+    } testConfigs[] = {
+        { kN32_SkColorType,       nullptr,          true,  "N32-nullptr" },
+        { kN32_SkColorType,       linearColorSpace, false, "N32-linear"  },
+        { kN32_SkColorType,       srgbColorSpace,   true,  "N32-srgb"    },
+        { kN32_SkColorType,       adobeColorSpace,  true,  "N32-adobe"   },
+        { kN32_SkColorType,       oddColorSpace,    false, "N32-odd"     },
+        { kRGBA_F16_SkColorType,  nullptr,          false, "F16-nullptr" },
+        { kRGBA_F16_SkColorType,  linearColorSpace, true,  "F16-linear"  },
+        { kRGBA_F16_SkColorType,  srgbColorSpace,   false, "F16-srgb"    },
+        { kRGBA_F16_SkColorType,  adobeColorSpace,  false, "F16-adobe"   },
+        { kRGBA_F16_SkColorType,  oddColorSpace,    false, "F16-odd"     },
+        { kRGB_565_SkColorType,   srgbColorSpace,   false, "565-srgb"    },
+        { kAlpha_8_SkColorType,   srgbColorSpace,   false, "A8-srgb"     },
+    };
+
+    for (auto& testConfig : testConfigs) {
+        SkString fullTestName = SkStringPrintf("%s-%s", prefix, testConfig.fDescription);
+        SkImageInfo info = SkImageInfo::Make(10, 10, testConfig.fColorType, kPremul_SkAlphaType,
+                                             testConfig.fColorSpace);
+
+        // For some GPU contexts (eg ANGLE), we don't have f16 support, so we should fail to create
+        // any surface of that type:
+        bool shouldWork = testConfig.fShouldWork &&
+                          (f16Support || kRGBA_F16_SkColorType != testConfig.fColorType);
+
+        auto surface(surfaceMaker(info));
+        REPORTER_ASSERT_MESSAGE(reporter, SkToBool(surface) == shouldWork, fullTestName.c_str());
+
+        if (shouldWork && surface) {
+            sk_sp<SkImage> image(surface->makeImageSnapshot());
+            REPORTER_ASSERT_MESSAGE(reporter, image, testConfig.fDescription);
+            SkColorSpace* imageColorSpace = as_IB(image)->onImageInfo().colorSpace();
+            REPORTER_ASSERT_MESSAGE(reporter, imageColorSpace == testConfig.fColorSpace.get(),
+                                    fullTestName.c_str());
+        }
+    }
+}
+
+DEF_TEST(SurfaceCreationWithColorSpace, reporter) {
+    auto surfaceMaker = [](const SkImageInfo& info) {
+        return SkSurface::MakeRaster(info);
+    };
+
+    test_surface_creation_and_snapshot_with_color_space(reporter, "raster", true, surfaceMaker);
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceCreationWithColorSpace_Gpu, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    bool f16Support = context->caps()->isConfigRenderable(kRGBA_half_GrPixelConfig, false);
+    auto surfaceMaker = [context](const SkImageInfo& info) {
+        return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
+    };
+
+    test_surface_creation_and_snapshot_with_color_space(reporter, "gpu", f16Support, surfaceMaker);
+
+    std::vector<GrBackendObject> textureHandles;
+    auto wrappedSurfaceMaker = [context,&textureHandles](const SkImageInfo& info) {
+        static const int kSize = 10;
+        GrPixelConfig config = SkImageInfo2GrPixelConfig(info, *context->caps());
+
+        GrBackendObject backendHandle = context->getGpu()->createTestingOnlyBackendTexture(
+                nullptr, kSize, kSize, config, true);
+
+        if (!backendHandle) {
+            return sk_sp<SkSurface>(nullptr);
+        }
+        textureHandles.push_back(backendHandle);
+
+        GrBackendTexture backendTex = GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
+                                                                   kSize,
+                                                                   kSize,
+                                                                   config,
+                                                                   backendHandle);
+
+        return SkSurface::MakeFromBackendTexture(context, backendTex,
+                                                 kDefault_GrSurfaceOrigin, 0,
+                                                 sk_ref_sp(info.colorSpace()), nullptr);
+    };
+
+    test_surface_creation_and_snapshot_with_color_space(reporter, "wrapped", f16Support,
+                                                        wrappedSurfaceMaker);
+
+    context->flush();
+
+    for (auto textureHandle : textureHandles) {
+        context->getGpu()->deleteTestingOnlyBackendTexture(textureHandle);
+    }
+}
+#endif
+
+static void test_overdraw_surface(skiatest::Reporter* r, SkSurface* surface) {
+    SkOverdrawCanvas canvas(surface->getCanvas());
+    canvas.drawPaint(SkPaint());
+    sk_sp<SkImage> image = surface->makeImageSnapshot();
+
+    SkBitmap bitmap;
+    image->asLegacyBitmap(&bitmap, SkImage::kRO_LegacyBitmapMode);
+    for (int y = 0; y < 10; y++) {
+        for (int x = 0; x < 10; x++) {
+            REPORTER_ASSERT(r, 1 == SkGetPackedA32(*bitmap.getAddr32(x, y)));
+        }
+    }
+}
+
+DEF_TEST(OverdrawSurface_Raster, r) {
+    sk_sp<SkSurface> surface = create_surface();
+    test_overdraw_surface(r, surface.get());
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(OverdrawSurface_Gpu, r, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    sk_sp<SkSurface> surface = create_gpu_surface(context);
+    test_overdraw_surface(r, surface.get());
+}
+#endif
+
+DEF_TEST(Surface_null, r) {
+    REPORTER_ASSERT(r, SkSurface::MakeNull(0, 0) == nullptr);
+
+    const int w = 37;
+    const int h = 1000;
+    auto surf = SkSurface::MakeNull(w, h);
+    auto canvas = surf->getCanvas();
+
+    canvas->drawPaint(SkPaint());   // should not crash, but don't expect anything to draw
+    REPORTER_ASSERT(r, surf->makeImageSnapshot() == nullptr);
 }
diff --git a/src/third_party/skia/tests/SwizzlerTest.cpp b/src/third_party/skia/tests/SwizzlerTest.cpp
new file mode 100644
index 0000000..655c97e
--- /dev/null
+++ b/src/third_party/skia/tests/SwizzlerTest.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSwizzle.h"
+#include "SkSwizzler.h"
+#include "Test.h"
+#include "SkOpts.h"
+
+// These are the values that we will look for to indicate that the fill was successful
+static const uint8_t kFillGray = 0x22;
+static const uint16_t kFill565 = 0x3344;
+static const uint32_t kFillColor = 0x55667788;
+
+static void check_fill(skiatest::Reporter* r,
+                       const SkImageInfo& imageInfo,
+                       uint32_t startRow,
+                       uint32_t endRow,
+                       size_t rowBytes,
+                       uint32_t offset,
+                       uint32_t colorOrIndex) {
+
+    // Calculate the total size of the image in bytes.  Use the smallest possible size.
+    // The offset value tells us to adjust the pointer from the memory we allocate in order
+    // to test on different memory alignments.  If offset is nonzero, we need to increase the
+    // size of the memory we allocate in order to make sure that we have enough.  We are
+    // still allocating the smallest possible size.
+    const size_t totalBytes = imageInfo.getSafeSize(rowBytes) + offset;
+
+    // Create fake image data where every byte has a value of 0
+    std::unique_ptr<uint8_t[]> storage(new uint8_t[totalBytes]);
+    memset(storage.get(), 0, totalBytes);
+    // Adjust the pointer in order to test on different memory alignments
+    uint8_t* imageData = storage.get() + offset;
+    uint8_t* imageStart = imageData + rowBytes * startRow;
+    const SkImageInfo fillInfo = imageInfo.makeWH(imageInfo.width(), endRow - startRow + 1);
+    SkSampler::Fill(fillInfo, imageStart, rowBytes, colorOrIndex, SkCodec::kNo_ZeroInitialized);
+
+    // Ensure that the pixels are filled properly
+    // The bots should catch any memory corruption
+    uint8_t* indexPtr = imageData + startRow * rowBytes;
+    uint8_t* grayPtr = indexPtr;
+    uint32_t* colorPtr = (uint32_t*) indexPtr;
+    uint16_t* color565Ptr = (uint16_t*) indexPtr;
+    for (uint32_t y = startRow; y <= endRow; y++) {
+        for (int32_t x = 0; x < imageInfo.width(); x++) {
+            switch (imageInfo.colorType()) {
+                case kN32_SkColorType:
+                    REPORTER_ASSERT(r, kFillColor == colorPtr[x]);
+                    break;
+                case kGray_8_SkColorType:
+                    REPORTER_ASSERT(r, kFillGray == grayPtr[x]);
+                    break;
+                case kRGB_565_SkColorType:
+                    REPORTER_ASSERT(r, kFill565 == color565Ptr[x]);
+                    break;
+                default:
+                    REPORTER_ASSERT(r, false);
+                    break;
+            }
+        }
+        indexPtr += rowBytes;
+        colorPtr = (uint32_t*) indexPtr;
+    }
+}
+
+// Test Fill() with different combinations of dimensions, alignment, and padding
+DEF_TEST(SwizzlerFill, r) {
+    // Test on an invalid width and representative widths
+    const uint32_t widths[] = { 0, 10, 50 };
+
+    // In order to call Fill(), there must be at least one row to fill
+    // Test on the smallest possible height and representative heights
+    const uint32_t heights[] = { 1, 5, 10 };
+
+    // Test on interesting possibilities for row padding
+    const uint32_t paddings[] = { 0, 4 };
+
+    // Iterate over test dimensions
+    for (uint32_t width : widths) {
+        for (uint32_t height : heights) {
+
+            // Create image info objects
+            const SkImageInfo colorInfo = SkImageInfo::MakeN32(width, height, kUnknown_SkAlphaType);
+            const SkImageInfo grayInfo = colorInfo.makeColorType(kGray_8_SkColorType);
+            const SkImageInfo color565Info = colorInfo.makeColorType(kRGB_565_SkColorType);
+
+            for (uint32_t padding : paddings) {
+
+                // Calculate row bytes
+                const size_t colorRowBytes = SkColorTypeBytesPerPixel(kN32_SkColorType) * width
+                        + padding;
+                const size_t indexRowBytes = width + padding;
+                const size_t grayRowBytes = indexRowBytes;
+                const size_t color565RowBytes =
+                        SkColorTypeBytesPerPixel(kRGB_565_SkColorType) * width + padding;
+
+                // If there is padding, we can invent an offset to change the memory alignment
+                for (uint32_t offset = 0; offset <= padding; offset += 4) {
+
+                    // Test all possible start rows with all possible end rows
+                    for (uint32_t startRow = 0; startRow < height; startRow++) {
+                        for (uint32_t endRow = startRow; endRow < height; endRow++) {
+
+                            // Test fill with each color type
+                            check_fill(r, colorInfo, startRow, endRow, colorRowBytes, offset,
+                                    kFillColor);
+                            check_fill(r, grayInfo, startRow, endRow, grayRowBytes, offset,
+                                    kFillGray);
+                            check_fill(r, color565Info, startRow, endRow, color565RowBytes, offset,
+                                    kFill565);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+DEF_TEST(SwizzleOpts, r) {
+    uint32_t dst, src;
+
+    // forall c, c*255 == c, c*0 == 0
+    for (int c = 0; c <= 255; c++) {
+        src = (255<<24) | c;
+        SkOpts::RGBA_to_rgbA(&dst, &src, 1);
+        REPORTER_ASSERT(r, dst == src);
+        SkOpts::RGBA_to_bgrA(&dst, &src, 1);
+        REPORTER_ASSERT(r, dst == (uint32_t)((255<<24) | (c<<16)));
+
+        src = (0<<24) | c;
+        SkOpts::RGBA_to_rgbA(&dst, &src, 1);
+        REPORTER_ASSERT(r, dst == 0);
+        SkOpts::RGBA_to_bgrA(&dst, &src, 1);
+        REPORTER_ASSERT(r, dst == 0);
+    }
+
+    // check a totally arbitrary color
+    src = 0xFACEB004;
+    SkOpts::RGBA_to_rgbA(&dst, &src, 1);
+    REPORTER_ASSERT(r, dst == 0xFACAAD04);
+
+    // swap red and blue
+    SkOpts::RGBA_to_BGRA(&dst, &src, 1);
+    REPORTER_ASSERT(r, dst == 0xFA04B0CE);
+
+    // all together now
+    SkOpts::RGBA_to_bgrA(&dst, &src, 1);
+    REPORTER_ASSERT(r, dst == 0xFA04ADCA);
+}
+
+DEF_TEST(PublicSwizzleOpts, r) {
+    uint32_t dst, src;
+
+    // check a totally arbitrary color
+    src = 0xFACEB004;
+    SkSwapRB(&dst, &src, 1);
+    REPORTER_ASSERT(r, dst == 0xFA04B0CE);
+}
diff --git a/src/third_party/skia/tests/TArrayTest.cpp b/src/third_party/skia/tests/TArrayTest.cpp
index b85efca..1b376e4 100644
--- a/src/third_party/skia/tests/TArrayTest.cpp
+++ b/src/third_party/skia/tests/TArrayTest.cpp
@@ -5,14 +5,16 @@
  * found in the LICENSE file.
  */
 
+#include "SkRandom.h"
+#include "SkRefCnt.h"
 #include "SkTArray.h"
 #include "Test.h"
 
 // Tests the SkTArray<T> class template.
 
-template <bool MEM_COPY>
+template <bool MEM_MOVE>
 static void TestTSet_basic(skiatest::Reporter* reporter) {
-    SkTArray<int, MEM_COPY> a;
+    SkTArray<int, MEM_MOVE> a;
 
     // Starts empty.
     REPORTER_ASSERT(reporter, a.empty());
@@ -58,7 +60,307 @@
     // {0, 3, 2 }
 }
 
+template <typename T> static void test_swap(skiatest::Reporter* reporter,
+                                            SkTArray<T>* (&arrays)[4],
+                                            int (&sizes)[7])
+{
+    for (auto a : arrays) {
+    for (auto b : arrays) {
+        if (a == b) {
+            continue;
+        }
+
+        for (auto sizeA : sizes) {
+        for (auto sizeB : sizes) {
+            a->reset();
+            b->reset();
+
+            int curr = 0;
+            for (int i = 0; i < sizeA; i++) { a->push_back(curr++); }
+            for (int i = 0; i < sizeB; i++) { b->push_back(curr++); }
+
+            a->swap(b);
+            REPORTER_ASSERT(reporter, b->count() == sizeA);
+            REPORTER_ASSERT(reporter, a->count() == sizeB);
+
+            curr = 0;
+            for (auto&& x : *b) { REPORTER_ASSERT(reporter, x == curr++); }
+            for (auto&& x : *a) { REPORTER_ASSERT(reporter, x == curr++); }
+
+            a->swap(a);
+            curr = sizeA;
+            for (auto&& x : *a) { REPORTER_ASSERT(reporter, x == curr++); }
+        }}
+    }}
+}
+
+static void test_swap(skiatest::Reporter* reporter) {
+    int sizes[] = {0, 1, 5, 10, 15, 20, 25};
+
+    SkTArray<int> arr;
+    SkSTArray< 5, int> arr5;
+    SkSTArray<10, int> arr10;
+    SkSTArray<20, int> arr20;
+    SkTArray<int>* arrays[] = { &arr, &arr5, &arr10, &arr20 };
+    test_swap(reporter, arrays, sizes);
+
+    struct MoveOnlyInt {
+        MoveOnlyInt(int i) : fInt(i) {}
+        MoveOnlyInt(MoveOnlyInt&& that) : fInt(that.fInt) {}
+        bool operator==(int i) { return fInt == i; }
+        int fInt;
+    };
+
+    SkTArray<MoveOnlyInt> moi;
+    SkSTArray< 5, MoveOnlyInt> moi5;
+    SkSTArray<10, MoveOnlyInt> moi10;
+    SkSTArray<20, MoveOnlyInt> moi20;
+    SkTArray<MoveOnlyInt>* arraysMoi[] = { &moi, &moi5, &moi10, &moi20 };
+    test_swap(reporter, arraysMoi, sizes);
+}
+
+template <typename T, bool MEM_MOVE>
+void test_copy_ctor(skiatest::Reporter* reporter, SkTArray<T, MEM_MOVE>&& array) {
+    SkASSERT(array.empty());
+    for (int i = 0; i < 5; ++i) {
+        array.emplace_back(new SkRefCnt);
+        REPORTER_ASSERT(reporter, array.back()->unique());
+    }
+
+    {
+        SkTArray<T, MEM_MOVE> copy(array);
+        for (const auto& ref : array)
+            REPORTER_ASSERT(reporter, !ref->unique());
+        for (const auto& ref : copy)
+            REPORTER_ASSERT(reporter, !ref->unique());
+    }
+
+    for (const auto& ref : array)
+        REPORTER_ASSERT(reporter, ref->unique());
+}
+
+static void test_move(skiatest::Reporter* reporter) {
+#define TEST_MOVE do {                                 \
+    SRC_T src;                                         \
+    src.emplace_back(sk_make_sp<SkRefCnt>());          \
+    {                                                  \
+        /* copy ctor */                                \
+        DST_T copy(src);                               \
+        REPORTER_ASSERT(reporter, !copy[0]->unique()); \
+    }                                                  \
+    {                                                  \
+        /* move ctor */                                \
+        DST_T move(std::move(src));                    \
+        REPORTER_ASSERT(reporter, move[0]->unique());  \
+    }                                                  \
+    REPORTER_ASSERT(reporter, src.empty());            \
+    src.emplace_back(sk_make_sp<SkRefCnt>());          \
+    {                                                  \
+        /* copy assignment */                          \
+        DST_T copy;                                    \
+        copy = src;                                    \
+        REPORTER_ASSERT(reporter, !copy[0]->unique()); \
+    }                                                  \
+    {                                                  \
+        /* move assignment */                          \
+        DST_T move;                                    \
+        move = std::move(src);                         \
+        REPORTER_ASSERT(reporter, move[0]->unique());  \
+    }                                                  \
+    REPORTER_ASSERT(reporter, src.empty());            \
+} while (false)
+
+    {
+        using SRC_T = SkTArray<sk_sp<SkRefCnt>, false>;
+        using DST_T = SkTArray<sk_sp<SkRefCnt>, false>;
+        TEST_MOVE;
+    }
+
+    {
+        using SRC_T = SkTArray<sk_sp<SkRefCnt>, true>;
+        using DST_T = SkTArray<sk_sp<SkRefCnt>, true>;
+        TEST_MOVE;
+    }
+
+    {
+        using SRC_T = SkSTArray<1, sk_sp<SkRefCnt>, false>;
+        using DST_T = SkSTArray<1, sk_sp<SkRefCnt>, false>;
+        TEST_MOVE;
+    }
+
+    {
+        using SRC_T = SkSTArray<1, sk_sp<SkRefCnt>, true>;
+        using DST_T = SkSTArray<1, sk_sp<SkRefCnt>, true>;
+        TEST_MOVE;
+    }
+
+    {
+        using SRC_T = SkTArray<sk_sp<SkRefCnt>, false>;
+        using DST_T = SkSTArray<1, sk_sp<SkRefCnt>, false>;
+        TEST_MOVE;
+    }
+
+    {
+        using SRC_T = SkTArray<sk_sp<SkRefCnt>, true>;
+        using DST_T = SkSTArray<1, sk_sp<SkRefCnt>, true>;
+        TEST_MOVE;
+    }
+
+    {
+        using SRC_T = SkSTArray<1, sk_sp<SkRefCnt>, false>;
+        using DST_T = SkTArray<sk_sp<SkRefCnt>, false>;
+        TEST_MOVE;
+    }
+
+    {
+        using SRC_T = SkSTArray<1, sk_sp<SkRefCnt>, true>;
+        using DST_T = SkTArray<sk_sp<SkRefCnt>, true>;
+        TEST_MOVE;
+    }
+#undef TEST_MOVE
+}
+
+template <typename T, bool MEM_MOVE> int SkTArray<T, MEM_MOVE>::allocCntForTest() const {
+    return fAllocCount;
+}
+
+void test_unnecessary_alloc(skiatest::Reporter* reporter) {
+    {
+        SkTArray<int> a;
+        REPORTER_ASSERT(reporter, a.allocCntForTest() == 0);
+    }
+    {
+        SkSTArray<10, int> a;
+        REPORTER_ASSERT(reporter, a.allocCntForTest() == 10);
+    }
+    {
+        SkTArray<int> a(1);
+        REPORTER_ASSERT(reporter, a.allocCntForTest() >= 1);
+    }
+    {
+        SkTArray<int> a, b;
+        b = a;
+        REPORTER_ASSERT(reporter, b.allocCntForTest() == 0);
+    }
+    {
+        SkSTArray<10, int> a;
+        SkTArray<int> b;
+        b = a;
+        REPORTER_ASSERT(reporter, b.allocCntForTest() == 0);
+    }
+    {
+        SkTArray<int> a;
+        SkTArray<int> b(a);
+        REPORTER_ASSERT(reporter, b.allocCntForTest() == 0);
+    }
+    {
+        SkSTArray<10, int> a;
+        SkTArray<int> b(a);
+        REPORTER_ASSERT(reporter, b.allocCntForTest() == 0);
+    }
+    {
+        SkTArray<int> a;
+        SkTArray<int> b(std::move(a));
+        REPORTER_ASSERT(reporter, b.allocCntForTest() == 0);
+    }
+    {
+        SkSTArray<10, int> a;
+        SkTArray<int> b(std::move(a));
+        REPORTER_ASSERT(reporter, b.allocCntForTest() == 0);
+    }
+    {
+        SkTArray<int> a;
+        SkTArray<int> b;
+        b = std::move(a);
+        REPORTER_ASSERT(reporter, b.allocCntForTest() == 0);
+    }
+    {
+        SkSTArray<10, int> a;
+        SkTArray<int> b;
+        b = std::move(a);
+        REPORTER_ASSERT(reporter, b.allocCntForTest() == 0);
+    }
+}
+
+static void test_self_assignment(skiatest::Reporter* reporter) {
+    SkTArray<int> a;
+    a.push_back(1);
+    REPORTER_ASSERT(reporter, !a.empty());
+    REPORTER_ASSERT(reporter, a.count() == 1);
+    REPORTER_ASSERT(reporter, a[0] == 1);
+
+    a = a;
+    REPORTER_ASSERT(reporter, !a.empty());
+    REPORTER_ASSERT(reporter, a.count() == 1);
+    REPORTER_ASSERT(reporter, a[0] == 1);
+}
+
+template <typename Array> static void test_array_reserve(skiatest::Reporter* reporter,
+                                                         Array* array, int reserveCount) {
+    SkRandom random;
+    REPORTER_ASSERT(reporter, array->allocCntForTest() >= reserveCount);
+    array->push_back();
+    REPORTER_ASSERT(reporter, array->allocCntForTest() >= reserveCount);
+    array->pop_back();
+    REPORTER_ASSERT(reporter, array->allocCntForTest() >= reserveCount);
+    while (array->count() < reserveCount) {
+        // Two steps forward, one step back
+        if (random.nextULessThan(3) < 2) {
+            array->push_back();
+        } else if (array->count() > 0) {
+            array->pop_back();
+        }
+        REPORTER_ASSERT(reporter, array->allocCntForTest() >= reserveCount);
+    }
+}
+
+template<typename Array> static void test_reserve(skiatest::Reporter* reporter) {
+    // Test that our allocated space stays >= to the reserve count until the array is filled to
+    // the reserve count
+    for (int reserveCount : {1, 2, 10, 100}) {
+        // Test setting reserve in constructor.
+        Array array1(reserveCount);
+        test_array_reserve(reporter, &array1, reserveCount);
+
+        // Test setting reserve after constructor.
+        Array array2;
+        array2.reserve(reserveCount);
+        test_array_reserve(reporter, &array2, reserveCount);
+
+        // Test increasing reserve after constructor.
+        Array array3(reserveCount/2);
+        array3.reserve(reserveCount);
+        test_array_reserve(reporter, &array3, reserveCount);
+
+        // Test setting reserve on non-empty array.
+        Array array4;
+        array4.push_back_n(reserveCount);
+        array4.reserve(reserveCount);
+        array4.pop_back_n(reserveCount);
+        test_array_reserve(reporter, &array4, 2 * reserveCount);
+    }
+}
+
 DEF_TEST(TArray, reporter) {
     TestTSet_basic<true>(reporter);
     TestTSet_basic<false>(reporter);
+    test_swap(reporter);
+
+    test_copy_ctor(reporter, SkTArray<sk_sp<SkRefCnt>, false>());
+    test_copy_ctor(reporter, SkTArray<sk_sp<SkRefCnt>,  true>());
+    test_copy_ctor(reporter, SkSTArray< 1, sk_sp<SkRefCnt>, false>());
+    test_copy_ctor(reporter, SkSTArray< 1, sk_sp<SkRefCnt>,  true>());
+    test_copy_ctor(reporter, SkSTArray<10, sk_sp<SkRefCnt>, false>());
+    test_copy_ctor(reporter, SkSTArray<10, sk_sp<SkRefCnt>,  true>());
+
+    test_move(reporter);
+
+    test_unnecessary_alloc(reporter);
+
+    test_self_assignment(reporter);
+
+    test_reserve<SkTArray<int>>(reporter);
+    test_reserve<SkSTArray<1, int>>(reporter);
+    test_reserve<SkSTArray<2, int>>(reporter);
+    test_reserve<SkSTArray<16, int>>(reporter);
 }
diff --git a/src/third_party/skia/tests/TDPQueueTest.cpp b/src/third_party/skia/tests/TDPQueueTest.cpp
new file mode 100644
index 0000000..75eab25
--- /dev/null
+++ b/src/third_party/skia/tests/TDPQueueTest.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTDPQueue.h"
+#include "SkRandom.h"
+#include "Test.h"
+
+namespace { bool intless(const int& a, const int& b) { return a < b; } }
+
+static void simple_test(skiatest::Reporter* reporter) {
+    SkTDPQueue<int, intless> heap;
+    REPORTER_ASSERT(reporter, 0 == heap.count());
+
+    heap.insert(0);
+    REPORTER_ASSERT(reporter, 1 == heap.count());
+    REPORTER_ASSERT(reporter, 0 == heap.peek());
+    heap.pop();
+    REPORTER_ASSERT(reporter, 0 == heap.count());
+
+    heap.insert(0);
+    heap.insert(1);
+    REPORTER_ASSERT(reporter, 2 == heap.count());
+    REPORTER_ASSERT(reporter, 0 == heap.peek());
+    heap.pop();
+    REPORTER_ASSERT(reporter, 1 == heap.count());
+    REPORTER_ASSERT(reporter, 1 == heap.peek());
+    heap.pop();
+    REPORTER_ASSERT(reporter, 0 == heap.count());
+
+    heap.insert(2);
+    heap.insert(1);
+    heap.insert(0);
+    REPORTER_ASSERT(reporter, 3 == heap.count());
+    REPORTER_ASSERT(reporter, 0 == heap.peek());
+    heap.pop();
+    REPORTER_ASSERT(reporter, 2 == heap.count());
+    REPORTER_ASSERT(reporter, 1 == heap.peek());
+    heap.pop();
+    REPORTER_ASSERT(reporter, 1 == heap.count());
+    REPORTER_ASSERT(reporter, 2 == heap.peek());
+    heap.pop();
+    REPORTER_ASSERT(reporter, 0 == heap.count());
+
+    heap.insert(2);
+    heap.insert(3);
+    heap.insert(0);
+    heap.insert(1);
+    REPORTER_ASSERT(reporter, 4 == heap.count());
+    REPORTER_ASSERT(reporter, 0 == heap.peek());
+    heap.pop();
+    REPORTER_ASSERT(reporter, 3 == heap.count());
+    REPORTER_ASSERT(reporter, 1 == heap.peek());
+    heap.pop();
+    REPORTER_ASSERT(reporter, 2 == heap.count());
+    REPORTER_ASSERT(reporter, 2 == heap.peek());
+    heap.pop();
+    REPORTER_ASSERT(reporter, 1 == heap.count());
+    REPORTER_ASSERT(reporter, 3 == heap.peek());
+    heap.pop();
+    REPORTER_ASSERT(reporter, 0 == heap.count());
+}
+
+struct Dummy {
+    int fValue;
+    int fPriority;
+    mutable int fIndex;
+
+    static bool LessP(Dummy* const& a, Dummy* const& b) { return a->fPriority < b->fPriority; }
+    static int* PQIndex(Dummy* const& dummy) { return &dummy->fIndex; }
+
+    bool operator== (const Dummy& that) const {
+        return fValue == that.fValue && fPriority == that.fPriority;
+    }
+    bool operator!= (const Dummy& that) const { return !(*this == that); }
+};
+
+void random_test(skiatest::Reporter* reporter) {
+    SkRandom random;
+    static const Dummy kSentinel = {-1, -1, -1};
+
+    for (int i = 0; i < 100; ++i) {
+        // Create a random set of Dummy objects.
+        int count = random.nextULessThan(100);
+        SkTDArray<Dummy> array;
+        array.setReserve(count);
+        for (int j = 0; j < count; ++j) {
+            Dummy* dummy = array.append();
+            dummy->fPriority = random.nextS();
+            dummy->fValue = random.nextS();
+            dummy->fIndex = -1;
+            if (*dummy == kSentinel) {
+                array.pop();
+                --j;
+            }
+        }
+
+        // Stick the dummy objects in the pqueue.
+        SkTDPQueue<Dummy*, Dummy::LessP, Dummy::PQIndex> pq;
+        for (int j = 0; j < count; ++j) {
+            pq.insert(&array[j]);
+        }
+        REPORTER_ASSERT(reporter, pq.count() == array.count());
+        for (int j = 0; j < count; ++j) {
+            // every item should have an entry in the queue.
+            REPORTER_ASSERT(reporter, -1 != array[j].fIndex);
+        }
+
+        // Begin the test.
+        while (pq.count()) {
+            // Make sure the top of the queue is really the highest priority.
+            Dummy* top = pq.peek();
+            for (int k = 0; k < count; ++k) {
+                REPORTER_ASSERT(reporter, kSentinel == array[k] ||
+                                            array[k].fPriority >= top->fPriority);
+            }
+            // Do one of three random actions:
+            unsigned action = random.nextULessThan(3);
+            switch (action) {
+                case 0: { // pop the top,
+                    Dummy* top = pq.peek();
+                    REPORTER_ASSERT(reporter, array.begin() <= top && top < array.end());
+                    pq.pop();
+                    *top = kSentinel;
+                    break;
+                }
+                case 1: { // remove a random element,
+                    int item;
+                    do {
+                        item = random.nextULessThan(count);
+                    } while (array[item] == kSentinel);
+                    pq.remove(&array[item]);
+                    array[item] = kSentinel;
+                    break;
+                }
+                case 2: { // or change an element's priority.
+                    int item;
+                    do {
+                        item = random.nextULessThan(count);
+                    } while (array[item] == kSentinel);
+                    array[item].fPriority = random.nextS();
+                    pq.priorityDidChange(&array[item]);
+                    break;
+                }
+            }
+        }
+   }
+}
+
+void sort_test(skiatest::Reporter* reporter) {
+    SkRandom random;
+
+    SkTDPQueue<Dummy *, Dummy::LessP, Dummy::PQIndex> pqTest;
+    SkTDPQueue<Dummy *, Dummy::LessP, Dummy::PQIndex> pqControl;
+
+    // Create a random set of Dummy objects and populate the test queue.
+    int count = random.nextULessThan(100);
+    SkTDArray<Dummy> testArray;
+    testArray.setReserve(count);
+    for (int i = 0; i < count; i++) {
+        Dummy *dummy = testArray.append();
+        dummy->fPriority = random.nextS();
+        dummy->fValue = random.nextS();
+        dummy->fIndex = -1;
+        pqTest.insert(&testArray[i]);
+    }
+
+    // Stick equivalent dummy objects into the control queue.
+    SkTDArray<Dummy> controlArray;
+    controlArray.setReserve(count);
+    for (int i = 0; i < count; i++) {
+        Dummy *dummy = controlArray.append();
+        dummy->fPriority = testArray[i].fPriority;
+        dummy->fValue = testArray[i].fValue;
+        dummy->fIndex = -1;
+        pqControl.insert(&controlArray[i]);
+    }
+
+    // Sort the queue
+    pqTest.sort();
+
+    // Compare elements in the queue to ensure they are in sorted order
+    int prevPriority = pqTest.peek()->fPriority;
+    for (int i = 0; i < count; i++) {
+        REPORTER_ASSERT(reporter, i <= pqTest.at(i)->fIndex);
+        REPORTER_ASSERT(reporter, prevPriority <= pqTest.at(i)->fPriority);
+        prevPriority = pqTest.at(i)->fPriority;
+    }
+
+    // Verify that after sorting the queue still produces the same result as the control queue
+    for (int i = 0; i < count; i++) {
+        REPORTER_ASSERT(reporter, *pqControl.peek() == *pqTest.peek());
+        pqControl.pop();
+        pqTest.pop();
+    }
+}
+
+DEF_TEST(TDPQueueTest, reporter) {
+    simple_test(reporter);
+    random_test(reporter);
+    sort_test(reporter);
+}
diff --git a/src/third_party/skia/tests/TDStackNesterTest.cpp b/src/third_party/skia/tests/TDStackNesterTest.cpp
deleted file mode 100644
index 968654b..0000000
--- a/src/third_party/skia/tests/TDStackNesterTest.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkTDStackNester.h"
-
-#include "Test.h"
-
-/**
- *  Test SkTDStackNester<int>::push(). Pushes the current count onto the stack,
- *  and checks that the count has increased by one.
- */
-static void test_push(skiatest::Reporter* reporter, SkTDStackNester<int>* nester) {
-    SkASSERT(nester);
-    const int count = nester->count();
-    // test_pop depends on this value.
-    nester->push(count);
-    REPORTER_ASSERT(reporter, nester->count() == count + 1);
-}
-
-/**
- *  Test SkTDStackNester<int>::pop(). Pops the top element off the stack, and
- *  checks that the new count is one smaller, and that the popped element
- *  matches the new count (as was pushed by test_push).
- */
-static void test_pop(skiatest::Reporter* reporter, SkTDStackNester<int>* nester) {
-    SkASSERT(nester);
-    const int count = nester->count();
-    // This test should not be called with a count <= 0.
-    SkASSERT(count > 0);
-    const int top = nester->top();
-    int value = -1;
-    nester->pop(&value);
-    REPORTER_ASSERT(reporter, top == value);
-    const int newCount = nester->count();
-    REPORTER_ASSERT(reporter, newCount == count - 1);
-    // Since test_push always pushes the count prior to the push, value should
-    // always be one less than count.
-    REPORTER_ASSERT(reporter, newCount == value);
-}
-
-/**
- *  Test nest() and unnest(). nest() is called, and it is confirmed that the
- *  count is now zero. Then test_push() is called inc times, followed by a call to
- *  unnest(). After this call, check that the count has returned to the initial count, and
- *  that nestingLevel() has returned to its initial value.
- */
-static void test_nest(skiatest::Reporter* reporter, SkTDStackNester<int>* nester, int inc) {
-    SkASSERT(nester);
-    SkASSERT(inc > 0);
-    const int initialCount = nester->count();
-    const int initialNesting = nester->nestingLevel();
-
-    nester->nest();
-    REPORTER_ASSERT(reporter, nester->count() == 0);
-    REPORTER_ASSERT(reporter, nester->nestingLevel() == initialNesting + 1);
-
-    for (int i = 0; i < inc; ++i) {
-        test_push(reporter, nester);
-    }
-
-    nester->unnest();
-    REPORTER_ASSERT(reporter, nester->count() == initialCount);
-    REPORTER_ASSERT(reporter, nester->nestingLevel() == initialNesting);
-}
-
-class SkTDStackNesterTester {
-public:
-    static int GetSlotCount() {
-        return SkTDStackNester<int>::kSlotCount;
-    }
-};
-
-static void test_stack_nester(skiatest::Reporter* reporter) {
-    SkTDStackNester<int> nester;
-    int count = nester.count();
-    REPORTER_ASSERT(reporter, 0 == count);
-    REPORTER_ASSERT(reporter, nester.nestingLevel() == 0);
-    REPORTER_ASSERT(reporter, nester.empty());
-
-    // Test nesting (with arbitrary number of pushes) from the beginning.
-    test_nest(reporter, &nester, 3);
-
-    const int slotCount = SkTDStackNesterTester::GetSlotCount();
-
-    // Test pushing beyond the boundary of the first Rec.
-    for (; count < 2 * slotCount; ++count) {
-        if (3 == count) {
-            // Test nesting (an arbitrary number of pushes) early on.
-            test_nest(reporter, &nester, 7);
-        } else if (slotCount - 4 == count) {
-            // Test nesting across the boundary of a Rec.
-            test_nest(reporter, &nester, 6);
-        }
-        test_push(reporter, &nester);
-    }
-
-    // Pop everything off the stack except for the last one, to confirm
-    // that the destructor handles a remaining object.
-    while (nester.count() > 1) {
-        test_pop(reporter, &nester);
-    }
-}
-
-DEF_TEST(TDStackNester, reporter) {
-    test_stack_nester(reporter);
-}
diff --git a/src/third_party/skia/tests/TLSTest.cpp b/src/third_party/skia/tests/TLSTest.cpp
index 53bb5fe..2702d9c 100644
--- a/src/third_party/skia/tests/TLSTest.cpp
+++ b/src/third_party/skia/tests/TLSTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkAtomics.h"
 #include "SkGraphics.h"
 #include "SkPaint.h"
 #include "SkTLS.h"
@@ -56,7 +57,7 @@
 
 static void* FakeCreateTLS() {
     sk_atomic_inc(&gCounter);
-    return NULL;
+    return nullptr;
 }
 
 static void FakeDeleteTLS(void*) {
diff --git a/src/third_party/skia/tests/TSetTest.cpp b/src/third_party/skia/tests/TSetTest.cpp
deleted file mode 100644
index 3f82648..0000000
--- a/src/third_party/skia/tests/TSetTest.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkTSet.h"
-#include "Test.h"
-
-// Tests the SkTSet<T> class template.
-// Functions that just call SkTDArray are not tested.
-
-static void TestTSet_basic(skiatest::Reporter* reporter) {
-    SkTSet<int> set0;
-    REPORTER_ASSERT(reporter,  set0.isEmpty());
-    REPORTER_ASSERT(reporter, !set0.contains(-1));
-    REPORTER_ASSERT(reporter, !set0.contains(0));
-    REPORTER_ASSERT(reporter, !set0.contains(1));
-    REPORTER_ASSERT(reporter,  set0.count() == 0);
-
-    REPORTER_ASSERT(reporter,  set0.add(0));
-    REPORTER_ASSERT(reporter, !set0.isEmpty());
-    REPORTER_ASSERT(reporter, !set0.contains(-1));
-    REPORTER_ASSERT(reporter,  set0.contains(0));
-    REPORTER_ASSERT(reporter, !set0.contains(1));
-    REPORTER_ASSERT(reporter,  set0.count() == 1);
-    REPORTER_ASSERT(reporter, !set0.add(0));
-    REPORTER_ASSERT(reporter,  set0.count() == 1);
-
-#ifdef SK_DEBUG
-    set0.validate();
-#endif
-}
-
-#define COUNT 1732
-#define PRIME1 10007
-#define PRIME2 1733
-
-// Generates a series of positive unique pseudo-random numbers.
-static int f(int i) {
-    return (long(i) * PRIME1) % PRIME2;
-}
-
-// Will expose contains() too.
-static void TestTSet_advanced(skiatest::Reporter* reporter) {
-    SkTSet<int> set0;
-
-    for (int i = 0; i < COUNT; i++) {
-        REPORTER_ASSERT(reporter, !set0.contains(f(i)));
-        if (i > 0) {
-            REPORTER_ASSERT(reporter,  set0.contains(f(0)));
-            REPORTER_ASSERT(reporter,  set0.contains(f(i / 2)));
-            REPORTER_ASSERT(reporter,  set0.contains(f(i - 1)));
-        }
-        REPORTER_ASSERT(reporter, !set0.contains(f(i)));
-        REPORTER_ASSERT(reporter,  set0.count() == i);
-        REPORTER_ASSERT(reporter,  set0.add(f(i)));
-        REPORTER_ASSERT(reporter,  set0.contains(f(i)));
-        REPORTER_ASSERT(reporter,  set0.count() == i + 1);
-        REPORTER_ASSERT(reporter, !set0.add(f(i)));
-    }
-
-    // Test deterministic output
-    for (int i = 0; i < COUNT; i++) {
-        REPORTER_ASSERT(reporter, set0[i] == f(i));
-    }
-
-    // Test copy constructor too.
-    SkTSet<int> set1 = set0;
-
-    REPORTER_ASSERT(reporter, set0.count() == set1.count());
-    REPORTER_ASSERT(reporter, !set1.contains(-1000));
-
-    for (int i = 0; i < COUNT; i++) {
-        REPORTER_ASSERT(reporter, set1.contains(f(i)));
-        REPORTER_ASSERT(reporter, set1[i] == f(i));
-    }
-
-    // Test operator= too.
-    SkTSet<int> set2;
-    set2 = set0;
-
-    REPORTER_ASSERT(reporter, set0.count() == set2.count());
-    REPORTER_ASSERT(reporter, !set2.contains(-1000));
-
-    for (int i = 0; i < COUNT; i++) {
-        REPORTER_ASSERT(reporter, set2.contains(f(i)));
-        REPORTER_ASSERT(reporter, set2[i] == f(i));
-    }
-
-#ifdef SK_DEBUG
-    set0.validate();
-    set1.validate();
-    set2.validate();
-#endif
-}
-
-static void TestTSet_merge(skiatest::Reporter* reporter) {
-    SkTSet<int> set;
-    SkTSet<int> setOdd;
-
-    for (int i = 0; i < COUNT; i++) {
-        REPORTER_ASSERT(reporter, set.add(2 * i));
-        REPORTER_ASSERT(reporter, setOdd.add(2 * i + 1));
-    }
-    // mergeInto returns the number of duplicates. Expected 0.
-    REPORTER_ASSERT(reporter, set.mergeInto(setOdd) == 0);
-    REPORTER_ASSERT(reporter, set.count() == 2 * COUNT);
-
-    // mergeInto should now find all new numbers duplicate.
-    REPORTER_ASSERT(reporter, set.mergeInto(setOdd) == setOdd.count());
-    REPORTER_ASSERT(reporter, set.count() == 2 * COUNT);
-
-    for (int i = 0; i < 2 * COUNT; i++) {
-        REPORTER_ASSERT(reporter, set.contains(i));
-    }
-
-    // check deterministic output
-    for (int i = 0; i < COUNT; i++) {
-        REPORTER_ASSERT(reporter, set[i] == 2 * i);
-        REPORTER_ASSERT(reporter, set[COUNT + i] == 2 * i + 1);
-    }
-
-#ifdef SK_DEBUG
-    set.validate();
-    setOdd.validate();
-#endif
-}
-
-DEF_TEST(TSet, reporter) {
-    TestTSet_basic(reporter);
-    TestTSet_advanced(reporter);
-    TestTSet_merge(reporter);
-}
diff --git a/src/third_party/skia/tests/TemplatesTest.cpp b/src/third_party/skia/tests/TemplatesTest.cpp
new file mode 100644
index 0000000..9d5ca77
--- /dev/null
+++ b/src/third_party/skia/tests/TemplatesTest.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTemplates.h"
+#include "Test.h"
+
+// Tests for some of the helpers in SkTemplates.h
+static void test_automalloc_realloc(skiatest::Reporter* reporter) {
+    SkAutoSTMalloc<1, int> array;
+
+    // test we have a valid pointer, should not crash
+    array[0] = 1;
+    REPORTER_ASSERT(reporter, array[0] == 1);
+
+    // using realloc for init
+    array.realloc(1);
+
+    array[0] = 1;
+    REPORTER_ASSERT(reporter, array[0] == 1);
+
+    // verify realloc can grow
+    array.realloc(2);
+    REPORTER_ASSERT(reporter, array[0] == 1);
+
+    // realloc can shrink
+    array.realloc(1);
+    REPORTER_ASSERT(reporter, array[0] == 1);
+
+    // should not crash
+    array.realloc(0);
+
+    // grow and shrink again
+    array.realloc(10);
+    for (int i = 0; i < 10; i++) {
+        array[i] = 10 - i;
+    }
+    array.realloc(20);
+    for (int i = 0; i < 10; i++) {
+        REPORTER_ASSERT(reporter, array[i] == 10 - i);
+    }
+    array.realloc(10);
+    for (int i = 0; i < 10; i++) {
+        REPORTER_ASSERT(reporter, array[i] == 10 - i);
+    }
+
+    array.realloc(1);
+    REPORTER_ASSERT(reporter, array[0] = 10);
+
+    // resets mixed with realloc, below stack alloc size
+    array.reset(0);
+    array.realloc(1);
+    array.reset(1);
+
+    array[0] = 1;
+    REPORTER_ASSERT(reporter, array[0] == 1);
+
+    // reset and realloc > stack size
+    array.reset(2);
+    array.realloc(3);
+    array[0] = 1;
+    REPORTER_ASSERT(reporter, array[0] == 1);
+    array.realloc(1);
+    REPORTER_ASSERT(reporter, array[0] == 1);
+}
+
+DEF_TEST(Templates, reporter) {
+    test_automalloc_realloc(reporter);
+}
+
+constexpr int static kStackPreallocCount = 10;
+
+// Ensures the containers in SkTemplates.h all have a consistent api.
+template<typename TContainer, typename TCount>
+static void test_container_apis(skiatest::Reporter* reporter) {
+    REPORTER_ASSERT(reporter, !TContainer((TCount)0).get());
+    REPORTER_ASSERT(reporter, TContainer((TCount)1).get());
+    REPORTER_ASSERT(reporter, TContainer((TCount)kStackPreallocCount).get());
+    REPORTER_ASSERT(reporter, TContainer((TCount)kStackPreallocCount + 1).get());
+
+    TContainer container;
+    // The default constructor may or may not init to empty, depending on the type of container.
+
+    container.reset((TCount)1);
+    REPORTER_ASSERT(reporter, container.get());
+
+    container.reset((TCount)kStackPreallocCount);
+    REPORTER_ASSERT(reporter, container.get());
+
+    container.reset((TCount)kStackPreallocCount + 1);
+    REPORTER_ASSERT(reporter, container.get());
+
+    container.reset((TCount)0);
+    REPORTER_ASSERT(reporter, !container.get());
+}
+
+DEF_TEST(TemplateContainerAPIs, reporter) {
+    test_container_apis<SkAutoTArray<int>, int>(reporter);
+    test_container_apis<SkAutoSTArray<kStackPreallocCount, int>, int>(reporter);
+    test_container_apis<SkAutoTMalloc<int>, size_t>(reporter);
+    test_container_apis<SkAutoSTMalloc<kStackPreallocCount, int>, size_t>(reporter);
+}
+
+// Ensures that realloc(0) results in a null pointer.
+template<typename TAutoMalloc> static void test_realloc_to_zero(skiatest::Reporter* reporter) {
+    TAutoMalloc autoMalloc(kStackPreallocCount);
+    REPORTER_ASSERT(reporter, autoMalloc.get());
+
+    autoMalloc.realloc(0);
+    REPORTER_ASSERT(reporter, !autoMalloc.get());
+
+    autoMalloc.realloc(kStackPreallocCount + 1);
+    REPORTER_ASSERT(reporter, autoMalloc.get());
+
+    autoMalloc.realloc(0);
+    REPORTER_ASSERT(reporter, !autoMalloc.get());
+
+    autoMalloc.realloc(kStackPreallocCount);
+    REPORTER_ASSERT(reporter, autoMalloc.get());
+}
+
+DEF_TEST(AutoReallocToZero, reporter) {
+    test_realloc_to_zero<SkAutoTMalloc<int> >(reporter);
+    test_realloc_to_zero<SkAutoSTMalloc<kStackPreallocCount, int> >(reporter);
+}
+
+DEF_TEST(SkAutoTMallocSelfMove, r) {
+#if defined(__clang__)
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wself-move"
+#endif
+
+    SkAutoTMalloc<int> foo(20);
+    REPORTER_ASSERT(r, foo.get());
+
+    foo = std::move(foo);
+    REPORTER_ASSERT(r, foo.get());
+
+#if defined(__clang__)
+    #pragma clang diagnostic pop
+#endif
+}
diff --git a/src/third_party/skia/tests/TessellatingPathRendererTests.cpp b/src/third_party/skia/tests/TessellatingPathRendererTests.cpp
new file mode 100644
index 0000000..f9db895
--- /dev/null
+++ b/src/third_party/skia/tests/TessellatingPathRendererTests.cpp
@@ -0,0 +1,430 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#include "SkPath.h"
+
+#if SK_SUPPORT_GPU
+#include "GrClip.h"
+#include "GrContext.h"
+#include "SkGradientShader.h"
+#include "SkShaderBase.h"
+#include "effects/GrPorterDuffXferProcessor.h"
+#include "ops/GrTessellatingPathRenderer.h"
+
+/*
+ * These tests pass by not crashing, hanging or asserting in Debug.
+ */
+
+// Tests active edges made inactive by splitting.
+// Also tests active edge list forced into an invalid ordering by
+// splitting (mopped up in cleanup_active_edges()).
+static SkPath create_path_0() {
+    SkPath path;
+    path.moveTo(229.127044677734375f,  67.34100341796875f);
+    path.lineTo(187.8097381591796875f, -6.7729740142822265625f);
+    path.lineTo(171.411407470703125f,  50.94266510009765625f);
+    path.lineTo(245.5253753662109375f,  9.6253643035888671875f);
+    path.moveTo(208.4683990478515625f, 30.284009933471679688f);
+    path.lineTo(171.411407470703125f,  50.94266510009765625f);
+    path.lineTo(187.8097381591796875f, -6.7729740142822265625f);
+    return path;
+}
+
+// Intersections which fall exactly on the current vertex, and require
+// a restart of the intersection checking.
+static SkPath create_path_1() {
+    SkPath path;
+    path.moveTo(314.483551025390625f, 486.246002197265625f);
+    path.lineTo(385.41949462890625f,  532.8087158203125f);
+    path.lineTo(373.232879638671875f, 474.05938720703125f);
+    path.lineTo(326.670166015625f,    544.995361328125f);
+    path.moveTo(349.951507568359375f, 509.52734375f);
+    path.lineTo(373.232879638671875f, 474.05938720703125f);
+    path.lineTo(385.41949462890625f,  532.8087158203125f);
+    return path;
+}
+
+// Tests active edges which are removed by splitting.
+static SkPath create_path_2() {
+    SkPath path;
+    path.moveTo(343.107391357421875f, 613.62176513671875f);
+    path.lineTo(426.632415771484375f, 628.5740966796875f);
+    path.lineTo(392.3460693359375f,   579.33544921875f);
+    path.lineTo(377.39373779296875f,  662.86041259765625f);
+    path.moveTo(384.869873046875f,    621.097900390625f);
+    path.lineTo(392.3460693359375f,   579.33544921875f);
+    path.lineTo(426.632415771484375f, 628.5740966796875f);
+    return path;
+}
+
+// Collinear edges merged in set_top().
+// Also, an intersection between left and right enclosing edges which
+// falls above the current vertex.
+static SkPath create_path_3() {
+    SkPath path;
+    path.moveTo(545.95751953125f,    791.69854736328125f);
+    path.lineTo(612.05816650390625f, 738.494140625f);
+    path.lineTo(552.4056396484375f,  732.0460205078125f);
+    path.lineTo(605.61004638671875f, 798.14666748046875f);
+    path.moveTo(579.00787353515625f, 765.0963134765625f);
+    path.lineTo(552.4056396484375f,  732.0460205078125f);
+    path.lineTo(612.05816650390625f, 738.494140625f);
+    return path;
+}
+
+// Tests active edges which are made inactive by set_top().
+static SkPath create_path_4() {
+    SkPath path;
+    path.moveTo(819.2725830078125f,  751.77447509765625f);
+    path.lineTo(820.70904541015625f, 666.933837890625f);
+    path.lineTo(777.57049560546875f, 708.63592529296875f);
+    path.lineTo(862.4111328125f,     710.0723876953125f);
+    path.moveTo(819.99078369140625f, 709.3541259765625f);
+    path.lineTo(777.57049560546875f, 708.63592529296875f);
+    path.lineTo(820.70904541015625f, 666.933837890625f);
+    return path;
+}
+
+static SkPath create_path_5() {
+    SkPath path;
+    path.moveTo(823.33209228515625f, 749.052734375f);
+    path.lineTo(823.494873046875f,   664.20013427734375f);
+    path.lineTo(780.9871826171875f,  706.5450439453125f);
+    path.lineTo(865.8397216796875f,  706.70782470703125f);
+    path.moveTo(823.4134521484375f,  706.6263427734375f);
+    path.lineTo(780.9871826171875f,  706.5450439453125f);
+    path.lineTo(823.494873046875f,   664.20013427734375f);
+    return path;
+}
+
+static SkPath create_path_6() {
+    SkPath path;
+    path.moveTo(954.862548828125f,   562.8349609375f);
+    path.lineTo(899.32818603515625f, 498.679443359375f);
+    path.lineTo(895.017578125f,      558.52435302734375f);
+    path.lineTo(959.17315673828125f, 502.990081787109375f);
+    path.moveTo(927.0953369140625f,  530.7572021484375f);
+    path.lineTo(895.017578125f,      558.52435302734375f);
+    path.lineTo(899.32818603515625f, 498.679443359375f);
+    return path;
+}
+
+static SkPath create_path_7() {
+    SkPath path;
+    path.moveTo(958.5330810546875f,  547.35516357421875f);
+    path.lineTo(899.93109130859375f, 485.989013671875f);
+    path.lineTo(898.54901123046875f, 545.97308349609375f);
+    path.lineTo(959.9151611328125f,  487.37109375f);
+    path.moveTo(929.2320556640625f,  516.67205810546875f);
+    path.lineTo(898.54901123046875f, 545.97308349609375f);
+    path.lineTo(899.93109130859375f, 485.989013671875f);
+    return path;
+}
+
+static SkPath create_path_8() {
+    SkPath path;
+    path.moveTo(389.8609619140625f,   369.326873779296875f);
+    path.lineTo(470.6290283203125f,   395.33697509765625f);
+    path.lineTo(443.250030517578125f, 341.9478759765625f);
+    path.lineTo(417.239959716796875f, 422.7159423828125f);
+    path.moveTo(430.244964599609375f, 382.3319091796875f);
+    path.lineTo(443.250030517578125f, 341.9478759765625f);
+    path.lineTo(470.6290283203125f,   395.33697509765625f);
+    return path;
+}
+
+static SkPath create_path_9() {
+    SkPath path;
+    path.moveTo(20, 20);
+    path.lineTo(50, 80);
+    path.lineTo(20, 80);
+    path.moveTo(80, 50);
+    path.lineTo(50, 50);
+    path.lineTo(20, 50);
+    return path;
+}
+
+static SkPath create_path_10() {
+    SkPath path;
+    path.moveTo(257.19439697265625f, 320.876617431640625f);
+    path.lineTo(190.113037109375f,   320.58978271484375f);
+    path.lineTo(203.64404296875f,    293.8145751953125f);
+    path.moveTo(203.357177734375f,   360.896026611328125f);
+    path.lineTo(216.88824462890625f, 334.120819091796875f);
+    path.lineTo(230.41925048828125f, 307.345611572265625f);
+    return path;
+}
+
+// A degenerate segments case, where both upper and lower segments of
+// a split edge must remain active.
+static SkPath create_path_11() {
+    SkPath path;
+    path.moveTo(231.9331207275390625f, 306.2012939453125f);
+    path.lineTo(191.4859161376953125f, 306.04547119140625f);
+    path.lineTo(231.0659332275390625f, 300.2642822265625f);
+    path.moveTo(189.946807861328125f,  302.072265625f);
+    path.lineTo(179.79705810546875f,   294.859771728515625f);
+    path.lineTo(191.0016021728515625f, 296.165679931640625f);
+    path.moveTo(150.8942108154296875f, 304.900146484375f);
+    path.lineTo(179.708892822265625f,  297.849029541015625f);
+    path.lineTo(190.4742279052734375f, 299.11895751953125f);
+    return path;
+}
+
+// Handle the case where edge.dist(edge.fTop) != 0.0.
+static SkPath create_path_12() {
+    SkPath path;
+    path.moveTo(                  0.0f,  400.0f);
+    path.lineTo(                138.0f,  202.0f);
+    path.lineTo(                  0.0f,  202.0f);
+    path.moveTo( 12.62693023681640625f,  250.57464599609375f);
+    path.lineTo(  8.13896942138671875f,  254.556884765625f);
+    path.lineTo(-18.15641021728515625f,  220.40203857421875f);
+    path.lineTo(-15.986493110656738281f, 219.6513519287109375f);
+    path.moveTo( 36.931194305419921875f, 282.485504150390625f);
+    path.lineTo( 15.617521286010742188f, 261.2901611328125f);
+    path.lineTo( 10.3829498291015625f,   252.565765380859375f);
+    path.lineTo(-16.165292739868164062f, 222.646026611328125f);
+    return path;
+}
+
+// A degenerate segments case which exercises inactive edges being
+// made active by splitting.
+static SkPath create_path_13() {
+    SkPath path;
+    path.moveTo(690.62127685546875f, 509.25555419921875f);
+    path.lineTo(99.336181640625f,    511.71405029296875f);
+    path.lineTo(708.362548828125f,   512.4349365234375f);
+    path.lineTo(729.9940185546875f,  516.3114013671875f);
+    path.lineTo(738.708984375f,      518.76995849609375f);
+    path.lineTo(678.3463134765625f,  510.0819091796875f);
+    path.lineTo(681.21795654296875f, 504.81378173828125f);
+    path.moveTo(758.52764892578125f, 521.55963134765625f);
+    path.lineTo(719.1549072265625f,  514.50372314453125f);
+    path.lineTo(689.59063720703125f, 512.0628662109375f);
+    path.lineTo(679.78216552734375f, 507.447845458984375f);
+    return path;
+}
+
+// Tests vertices which become "orphaned" (ie., no connected edges)
+// after simplification.
+static SkPath create_path_14() {
+    SkPath path;
+    path.moveTo(217.326019287109375f, 166.4752960205078125f);
+    path.lineTo(226.279266357421875f, 170.929473876953125f);
+    path.lineTo(234.3973388671875f,   177.0623626708984375f);
+    path.lineTo(262.0921630859375f,   188.746124267578125f);
+    path.moveTo(196.23638916015625f,  174.0722198486328125f);
+    path.lineTo(416.15277099609375f,  180.138214111328125f);
+    path.lineTo(192.651947021484375f, 304.0228271484375f);
+    return path;
+}
+
+static SkPath create_path_15() {
+    SkPath path;
+    path.moveTo(    0.0f,   0.0f);
+    path.lineTo(10000.0f,   0.0f);
+    path.lineTo(    0.0f,  -1.0f);
+    path.lineTo(10000.0f,   0.000001f);
+    path.lineTo(    0.0f, -30.0f);
+    return path;
+}
+
+// Reduction of Nebraska-StateSeal.svg. Floating point error causes the
+// same edge to be added to more than one poly on the same side.
+static SkPath create_path_16() {
+    SkPath path;
+    path.moveTo(170.8199920654296875,   491.86700439453125);
+    path.lineTo(173.7649993896484375,    489.7340087890625);
+    path.lineTo(174.1450958251953125,  498.545989990234375);
+    path.lineTo( 171.998992919921875,   500.88201904296875);
+    path.moveTo(168.2922515869140625,   498.66265869140625);
+    path.lineTo(169.8589935302734375,   497.94500732421875);
+    path.lineTo(                 172,   500.88299560546875);
+    path.moveTo( 169.555267333984375,   490.70111083984375);
+    path.lineTo(173.7649993896484375,    489.7340087890625);
+    path.lineTo(  170.82000732421875,   491.86700439453125);
+    return path;
+}
+
+// A simple concave path. Test this with a non-invertible matrix.
+static SkPath create_path_17() {
+    SkPath path;
+    path.moveTo(20, 20);
+    path.lineTo(80, 20);
+    path.lineTo(30, 30);
+    path.lineTo(20, 80);
+    return path;
+}
+
+// A shape with a vertex collinear to the right hand edge.
+// This messes up find_enclosing_edges.
+static SkPath create_path_18() {
+    SkPath path;
+    path.moveTo(80, 20);
+    path.lineTo(80, 60);
+    path.lineTo(20, 60);
+    path.moveTo(80, 50);
+    path.lineTo(80, 80);
+    path.lineTo(20, 80);
+    return path;
+}
+
+// Exercises the case where an edge becomes collinear with *two* of its
+// adjacent neighbour edges after splitting.
+// This is a reduction from
+// http://mooooo.ooo/chebyshev-sine-approximation/horner_ulp.svg
+static SkPath create_path_19() {
+    SkPath path;
+    path.moveTo(  351.99298095703125,         348.23046875);
+    path.lineTo(  351.91876220703125,         347.33984375);
+    path.lineTo(  351.91876220703125,          346.1953125);
+    path.lineTo(  351.90313720703125,           347.734375);
+    path.lineTo(  351.90313720703125,          346.1328125);
+    path.lineTo(  351.87579345703125,         347.93359375);
+    path.lineTo(  351.87579345703125,           345.484375);
+    path.lineTo(  351.86407470703125,          347.7890625);
+    path.lineTo(  351.86407470703125,          346.2109375);
+    path.lineTo(  351.84844970703125,   347.63763427734375);
+    path.lineTo(  351.84454345703125,   344.19232177734375);
+    path.lineTo(  351.78204345703125,    346.9483642578125);
+    path.lineTo( 351.758636474609375,      347.18310546875);
+    path.lineTo(  351.75469970703125,               346.75);
+    path.lineTo(  351.75469970703125,            345.46875);
+    path.lineTo(         352.5546875,            345.46875);
+    path.lineTo(        352.55078125,         347.01953125);
+    path.lineTo(  351.75079345703125,   347.02313232421875);
+    path.lineTo(  351.74688720703125,   346.15203857421875);
+    path.lineTo(  351.74688720703125,  347.646148681640625);
+    path.lineTo(         352.5390625,         346.94140625);
+    path.lineTo(  351.73907470703125,   346.94268798828125);
+    path.lineTo(  351.73516845703125,   344.48565673828125);
+    path.lineTo(          352.484375,         346.73828125);
+    path.lineTo(  351.68438720703125,    346.7401123046875);
+    path.lineTo(         352.4765625,           346.546875);
+    path.lineTo(  351.67657470703125,   346.54937744140625);
+    path.lineTo(        352.47265625,         346.75390625);
+    path.lineTo(  351.67266845703125,  346.756622314453125);
+    path.lineTo(  351.66876220703125,  345.612091064453125);
+    return path;
+}
+
+// An intersection above the first vertex in the mesh.
+// Reduction from http://crbug.com/730687
+static SkPath create_path_20() {
+    SkPath path;
+    path.moveTo(           2822128.5,  235.026336669921875);
+    path.lineTo(          2819349.25, 235.3623504638671875);
+    path.lineTo(          -340558688, 23.83478546142578125);
+    path.lineTo(          -340558752, 25.510419845581054688);
+    path.lineTo(          -340558720, 27.18605804443359375);
+    return path;
+}
+
+// An intersection whose result is NaN (due to rounded-to-inf endpoint).
+static SkPath create_path_21() {
+    SkPath path;
+    path.moveTo(1.7889142061167663539e+38, 39338463358011572224.0);
+    path.lineTo(  1647.4193115234375,       -522.603515625);
+    path.lineTo(    1677.74560546875,   -529.0028076171875);
+    path.lineTo(    1678.29541015625,   -528.7847900390625);
+    path.lineTo(  1637.5167236328125,  -519.79266357421875);
+    path.lineTo(  1647.4193115234375,       -522.603515625);
+    return path;
+}
+
+// A quad which becomes NaN when interpolated.
+static SkPath create_path_22() {
+    SkPath path;
+    path.moveTo(-5.71889e+13f, 1.36759e+09f);
+    path.quadTo(2.45472e+19f, -3.12406e+15f, -2.19589e+18f, 2.79462e+14f);
+    return path;
+}
+
+static sk_sp<GrFragmentProcessor> create_linear_gradient_processor(GrContext* ctx) {
+    SkPoint pts[2] = { {0, 0}, {1, 1} };
+    SkColor colors[2] = { SK_ColorGREEN, SK_ColorBLUE };
+    sk_sp<SkShader> shader = SkGradientShader::MakeLinear(
+        pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode);
+    SkShaderBase::AsFPArgs args(
+        ctx, &SkMatrix::I(), &SkMatrix::I(), SkFilterQuality::kLow_SkFilterQuality, nullptr);
+    return as_SB(shader)->asFragmentProcessor(args);
+}
+
+static void test_path(GrContext* ctx,
+                      GrRenderTargetContext* renderTargetContext,
+                      const SkPath& path,
+                      const SkMatrix& matrix = SkMatrix::I(),
+                      GrAAType aaType = GrAAType::kNone,
+                      sk_sp<GrFragmentProcessor> fp = nullptr) {
+    GrTessellatingPathRenderer tess;
+
+    GrPaint paint;
+    paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
+    if (fp) {
+        paint.addColorFragmentProcessor(fp);
+    }
+
+    GrNoClip noClip;
+    GrStyle style(SkStrokeRec::kFill_InitStyle);
+    GrShape shape(path, style);
+    GrPathRenderer::DrawPathArgs args{ctx,
+                                      std::move(paint),
+                                      &GrUserStencilSettings::kUnused,
+                                      renderTargetContext,
+                                      &noClip,
+                                      &matrix,
+                                      &shape,
+                                      aaType,
+                                      false};
+    tess.drawPath(args);
+}
+
+DEF_GPUTEST_FOR_ALL_CONTEXTS(TessellatingPathRendererTests, reporter, ctxInfo) {
+    GrContext* ctx = ctxInfo.grContext();
+
+    sk_sp<GrRenderTargetContext> rtc(ctx->makeDeferredRenderTargetContext(
+                                                                  SkBackingFit::kApprox,
+                                                                  800, 800,
+                                                                  kRGBA_8888_GrPixelConfig,
+                                                                  nullptr,
+                                                                  0,
+                                                                  kTopLeft_GrSurfaceOrigin));
+    if (!rtc) {
+        return;
+    }
+
+    ctx->flush();
+    test_path(ctx, rtc.get(), create_path_0());
+    test_path(ctx, rtc.get(), create_path_1());
+    test_path(ctx, rtc.get(), create_path_2());
+    test_path(ctx, rtc.get(), create_path_3());
+    test_path(ctx, rtc.get(), create_path_4());
+    test_path(ctx, rtc.get(), create_path_5());
+    test_path(ctx, rtc.get(), create_path_6());
+    test_path(ctx, rtc.get(), create_path_7());
+    test_path(ctx, rtc.get(), create_path_8());
+    test_path(ctx, rtc.get(), create_path_9());
+    test_path(ctx, rtc.get(), create_path_10());
+    test_path(ctx, rtc.get(), create_path_11());
+    test_path(ctx, rtc.get(), create_path_12());
+    test_path(ctx, rtc.get(), create_path_13());
+    test_path(ctx, rtc.get(), create_path_14());
+    test_path(ctx, rtc.get(), create_path_15());
+    test_path(ctx, rtc.get(), create_path_16());
+    SkMatrix nonInvertibleMatrix = SkMatrix::MakeScale(0, 0);
+    sk_sp<GrFragmentProcessor> fp(create_linear_gradient_processor(ctx));
+    test_path(ctx, rtc.get(), create_path_17(), nonInvertibleMatrix, GrAAType::kCoverage, fp);
+    test_path(ctx, rtc.get(), create_path_18());
+    test_path(ctx, rtc.get(), create_path_19());
+    test_path(ctx, rtc.get(), create_path_20(), SkMatrix(), GrAAType::kCoverage);
+    test_path(ctx, rtc.get(), create_path_21(), SkMatrix(), GrAAType::kCoverage);
+    test_path(ctx, rtc.get(), create_path_22());
+}
+#endif
diff --git a/src/third_party/skia/tests/Test.cpp b/src/third_party/skia/tests/Test.cpp
index d0147e1..37515dd 100644
--- a/src/third_party/skia/tests/Test.cpp
+++ b/src/third_party/skia/tests/Test.cpp
@@ -8,111 +8,44 @@
 #include "Test.h"
 
 #include "SkCommandLineFlags.h"
-#include "SkError.h"
 #include "SkString.h"
-#include "SkTArray.h"
 #include "SkTime.h"
 
-#if SK_SUPPORT_GPU
-#include "GrContext.h"
-#include "gl/SkNativeGLContext.h"
-#else
-class GrContext;
-#endif
+DEFINE_string2(tmpDir, t, nullptr, "Temp directory to use.");
 
-DEFINE_string2(tmpDir, t, NULL, "tmp directory for tests to use.");
+void skiatest::Reporter::bumpTestCount() {}
 
-using namespace skiatest;
+bool skiatest::Reporter::allowExtendedTest() const { return false; }
 
-Reporter::Reporter() : fTestCount(0) {
-}
+bool skiatest::Reporter::verbose() const { return false; }
 
-void Reporter::startTest(Test* test) {
-    this->onStart(test);
-}
-
-void Reporter::reportFailed(const SkString& desc) {
-    this->onReportFailed(desc);
-}
-
-void Reporter::endTest(Test* test) {
-    this->onEnd(test);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-Test::Test() : fReporter(NULL), fPassed(true) {}
-
-Test::~Test() {
-    SkSafeUnref(fReporter);
-}
-
-void Test::setReporter(Reporter* r) {
-    SkRefCnt_SafeAssign(fReporter, r);
-}
-
-const char* Test::getName() {
-    if (fName.size() == 0) {
-        this->onGetName(&fName);
+SkString skiatest::Failure::toString() const {
+    SkString result = SkStringPrintf("%s:%d\t", this->fileName, this->lineNo);
+    if (!this->message.isEmpty()) {
+        result.append(this->message);
+        if (strlen(this->condition) > 0) {
+            result.append(": ");
+        }
     }
-    return fName.c_str();
+    result.append(this->condition);
+    return result;
 }
 
-class LocalReporter : public Reporter {
-public:
-    explicit LocalReporter(Reporter* reporterToMimic) : fReporter(reporterToMimic) {}
-
-    int numFailures() const { return fFailures.count(); }
-    const SkString& failure(int i) const { return fFailures[i]; }
-
-protected:
-    virtual void onReportFailed(const SkString& desc) SK_OVERRIDE {
-        fFailures.push_back(desc);
-    }
-
-    // Proxy down to fReporter.  We assume these calls are threadsafe.
-    virtual bool allowExtendedTest() const SK_OVERRIDE {
-        return fReporter->allowExtendedTest();
-    }
-
-    virtual void bumpTestCount() SK_OVERRIDE {
-        fReporter->bumpTestCount();
-    }
-
-    virtual bool verbose() const SK_OVERRIDE {
-        return fReporter->verbose();
-    }
-
-private:
-    Reporter* fReporter;  // Unowned.
-    SkTArray<SkString> fFailures;
-};
-
-void Test::run() {
-    // Clear the Skia error callback before running any test, to ensure that tests
-    // don't have unintended side effects when running more than one.
-    SkSetErrorCallback( NULL, NULL );
-
-    // Tell (likely shared) fReporter that this test has started.
-    fReporter->startTest(this);
-
-    const SkMSec start = SkTime::GetMSecs();
-    // Run the test into a LocalReporter so we know if it's passed or failed without interference
-    // from other tests that might share fReporter.
-    LocalReporter local(fReporter);
-    this->onRun(&local);
-    fPassed = local.numFailures() == 0;
-    fElapsed = SkTime::GetMSecs() - start;
-
-    // Now tell fReporter about any failures and wrap up.
-    for (int i = 0; i < local.numFailures(); i++) {
-      fReporter->reportFailed(local.failure(i));
-    }
-    fReporter->endTest(this);
-
-}
-
-SkString Test::GetTmpDir() {
-    const char* tmpDir = FLAGS_tmpDir.isEmpty() ? NULL : FLAGS_tmpDir[0];
+SkString skiatest::GetTmpDir() {
+    const char* tmpDir = FLAGS_tmpDir.isEmpty() ? nullptr : FLAGS_tmpDir[0];
     return SkString(tmpDir);
 }
+
+skiatest::Timer::Timer() : fStartNanos(SkTime::GetNSecs()) {}
+
+double skiatest::Timer::elapsedNs() const {
+    return SkTime::GetNSecs() - fStartNanos;
+}
+
+double skiatest::Timer::elapsedMs() const { return this->elapsedNs() * 1e-6; }
+
+SkMSec skiatest::Timer::elapsedMsInt() const {
+    const double elapsedMs = this->elapsedMs();
+    SkASSERT(SK_MSecMax >= elapsedMs);
+    return static_cast<SkMSec>(elapsedMs);
+}
diff --git a/src/third_party/skia/tests/Test.h b/src/third_party/skia/tests/Test.h
index 6c85b32..db2552b 100644
--- a/src/third_party/skia/tests/Test.h
+++ b/src/third_party/skia/tests/Test.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -8,88 +7,96 @@
 #ifndef skiatest_Test_DEFINED
 #define skiatest_Test_DEFINED
 
-#include "SkRefCnt.h"
 #include "SkString.h"
-#include "SkTRegistry.h"
-#include "SkThread.h"
+#include "../tools/Registry.h"
 #include "SkTypes.h"
+#include "SkClipOpPriv.h"
 
+#if SK_SUPPORT_GPU
+#include "GrContextFactory.h"
+#else
+namespace sk_gpu_test {
 class GrContextFactory;
+class ContextInfo;
+class GLTestContext;
+}  // namespace sk_gpu_test
+class GrContext;
+#endif
 
 namespace skiatest {
 
-    class Test;
+SkString GetTmpDir();
 
-    class Reporter : public SkRefCnt {
-    public:
-        SK_DECLARE_INST_COUNT(Reporter)
-        Reporter();
+struct Failure {
+    Failure(const char* f, int l, const char* c, const SkString& m)
+        : fileName(f), lineNo(l), condition(c), message(m) {}
+    const char* fileName;
+    int lineNo;
+    const char* condition;
+    SkString message;
+    SkString toString() const;
+};
 
-        int countTests() const { return fTestCount; }
+class Reporter : SkNoncopyable {
+public:
+    virtual ~Reporter() {}
+    virtual void bumpTestCount();
+    virtual void reportFailed(const skiatest::Failure&) = 0;
+    virtual bool allowExtendedTest() const;
+    virtual bool verbose() const;
+    virtual void* stats() const { return nullptr; }
 
-        void startTest(Test*);
-        void reportFailed(const SkString& desc);
-        void endTest(Test*);
-
-        virtual bool allowExtendedTest() const { return false; }
-        virtual bool verbose() const { return false; }
-        virtual void bumpTestCount() { sk_atomic_inc(&fTestCount); }
-
-    protected:
-        virtual void onStart(Test*) {}
-        virtual void onReportFailed(const SkString& desc) {}
-        virtual void onEnd(Test*) {}
-
-    private:
-        int32_t fTestCount;
-
-        typedef SkRefCnt INHERITED;
-    };
-
-    class Test {
-    public:
-        Test();
-        virtual ~Test();
-
-        Reporter* getReporter() const { return fReporter; }
-        void setReporter(Reporter*);
-
-        const char* getName();
-        void run();
-        bool passed() const { return fPassed; }
-        SkMSec elapsedMs() const { return fElapsed; }
-
-        static SkString GetTmpDir();
-
-        virtual bool isGPUTest() const { return false; }
-        virtual void setGrContextFactory(GrContextFactory* factory) {}
-
-    protected:
-        virtual void onGetName(SkString*) = 0;
-        virtual void onRun(Reporter*) = 0;
-
-    private:
-        Reporter*   fReporter;
-        SkString    fName;
-        bool        fPassed;
-        SkMSec      fElapsed;
-    };
-
-    class GpuTest : public Test{
-    public:
-        GpuTest() : Test(), fGrContextFactory(NULL) {}
-
-        virtual bool isGPUTest() const { return true; }
-        virtual void setGrContextFactory(GrContextFactory* factory) {
-            fGrContextFactory = factory;
+    void reportFailedWithContext(const skiatest::Failure& f) {
+        SkString fullMessage = f.message;
+        if (!fContextStack.empty()) {
+            fullMessage.append(" [");
+            for (int i = 0; i < fContextStack.count(); ++i) {
+                if (i > 0) {
+                    fullMessage.append(", ");
+                }
+                fullMessage.append(fContextStack[i]);
+            }
+            fullMessage.append("]");
         }
+        this->reportFailed(skiatest::Failure(f.fileName, f.lineNo, f.condition, fullMessage));
+    }
+    void push(const SkString& message) {
+        fContextStack.push_back(message);
+    }
+    void pop() {
+        fContextStack.pop_back();
+    }
 
-    protected:
-        GrContextFactory* fGrContextFactory;  // Unowned.
-    };
+private:
+    SkTArray<SkString> fContextStack;
+};
 
-    typedef SkTRegistry<Test*(*)(void*)> TestRegistry;
-}  // namespace skiatest
+#define REPORT_FAILURE(reporter, cond, message) \
+    reporter->reportFailedWithContext(skiatest::Failure(__FILE__, __LINE__, cond, message))
+
+class ReporterContext : SkNoncopyable {
+public:
+    ReporterContext(Reporter* reporter, const SkString& message) : fReporter(reporter) {
+        fReporter->push(message);
+    }
+    ~ReporterContext() {
+        fReporter->pop();
+    }
+
+private:
+    Reporter* fReporter;
+};
+
+typedef void (*TestProc)(skiatest::Reporter*, sk_gpu_test::GrContextFactory*);
+
+struct Test {
+    Test(const char* n, bool g, TestProc p) : name(n), needsGpu(g), proc(p) {}
+    const char* name;
+    bool needsGpu;
+    TestProc proc;
+};
+
+typedef sk_tools::Registry<Test> TestRegistry;
 
 /*
     Use the following macros to make use of the skiatest classes, e.g.
@@ -110,65 +117,117 @@
     }
 */
 
-#define REPORTER_ASSERT(r, cond)                                 \
-    do {                                                         \
-        if (!(cond)) {                                           \
-            SkString desc;                                       \
-            desc.printf("%s:%d\t%s", __FILE__, __LINE__, #cond); \
-            r->reportFailed(desc);                               \
-        }                                                        \
-    } while(0)
+#if SK_SUPPORT_GPU
+using GrContextFactoryContextType = sk_gpu_test::GrContextFactory::ContextType;
+#else
+using GrContextFactoryContextType = int;
+#endif
 
-#define REPORTER_ASSERT_MESSAGE(r, cond, message)            \
-    do {                                                     \
-        if (!(cond)) {                                       \
-            SkString desc;                                   \
-            desc.printf("%s:%d\t%s: %s", __FILE__, __LINE__, \
-                        message, #cond);                     \
-            r->reportFailed(desc);                           \
-        }                                                    \
-    } while(0)
+typedef void GrContextTestFn(Reporter*, const sk_gpu_test::ContextInfo&);
+typedef bool GrContextTypeFilterFn(GrContextFactoryContextType);
 
-#define ERRORF(reporter, ...)                       \
-    do {                                            \
-        SkString desc;                              \
-        desc.printf("%s:%d\t", __FILE__, __LINE__); \
-        desc.appendf(__VA_ARGS__) ;                 \
-        (reporter)->reportFailed(desc);             \
-    } while(0)
+extern bool IsGLContextType(GrContextFactoryContextType);
+extern bool IsVulkanContextType(GrContextFactoryContextType);
+extern bool IsRenderingGLContextType(GrContextFactoryContextType);
+extern bool IsNullGLContextType(GrContextFactoryContextType);
+void RunWithGPUTestContexts(GrContextTestFn*, GrContextTypeFilterFn*,
+                            Reporter*, sk_gpu_test::GrContextFactory*);
 
-#define DEF_TEST(name, reporter)                                        \
-    static void test_##name(skiatest::Reporter*);                       \
-    namespace skiatest {                                                \
-    class name##Class : public Test {                                   \
-    public:                                                             \
-        static Test* Factory(void*) { return SkNEW(name##Class); }      \
-    protected:                                                          \
-        virtual void onGetName(SkString* name) SK_OVERRIDE {            \
-            name->set(#name);                                           \
-        }                                                               \
-        virtual void onRun(Reporter* r) SK_OVERRIDE { test_##name(r); } \
-    };                                                                  \
-    static TestRegistry gReg_##name##Class(name##Class::Factory);       \
-    }                                                                   \
-    static void test_##name(skiatest::Reporter* reporter)
+/** Timer provides wall-clock duration since its creation. */
+class Timer {
+public:
+    /** Starts the timer. */
+    Timer();
 
-#define DEF_GPUTEST(name, reporter, factory)                          \
-    static void test_##name(skiatest::Reporter*, GrContextFactory*);  \
-    namespace skiatest {                                              \
-    class name##Class : public GpuTest {                              \
-    public:                                                           \
-        static Test* Factory(void*) { return SkNEW(name##Class); }    \
-    protected:                                                        \
-        virtual void onGetName(SkString* name) SK_OVERRIDE {          \
-            name->set(#name);                                         \
-        }                                                             \
-        virtual void onRun(Reporter* r) SK_OVERRIDE {                 \
-            test_##name(r, fGrContextFactory);                        \
-        }                                                             \
-    };                                                                \
-    static TestRegistry gReg_##name##Class(name##Class::Factory);     \
-    }                                                                 \
-    static void test_##name(skiatest::Reporter* reporter, GrContextFactory* factory)
+    /** Nanoseconds since creation. */
+    double elapsedNs() const;
+
+    /** Milliseconds since creation. */
+    double elapsedMs() const;
+
+    /** Milliseconds since creation as an integer.
+        Behavior is undefined for durations longer than SK_MSecMax.
+    */
+    SkMSec elapsedMsInt() const;
+private:
+    double fStartNanos;
+};
+
+}  // namespace skiatest
+
+#define REPORTER_ASSERT(r, cond)                  \
+    do {                                          \
+        if (!(cond)) {                            \
+            REPORT_FAILURE(r, #cond, SkString()); \
+        }                                         \
+    } while (0)
+
+#define REPORTER_ASSERT_MESSAGE(r, cond, message)        \
+    do {                                                 \
+        if (!(cond)) {                                   \
+            REPORT_FAILURE(r, #cond, SkString(message)); \
+        }                                                \
+    } while (0)
+
+#define ERRORF(r, ...)                                      \
+    do {                                                    \
+        REPORT_FAILURE(r, "", SkStringPrintf(__VA_ARGS__)); \
+    } while (0)
+
+#define INFOF(REPORTER, ...)         \
+    do {                             \
+        if ((REPORTER)->verbose()) { \
+            SkDebugf(__VA_ARGS__);   \
+        }                            \
+    } while (0)
+
+#define DEF_TEST(name, reporter)                                                  \
+    static void test_##name(skiatest::Reporter*, sk_gpu_test::GrContextFactory*); \
+    skiatest::TestRegistry name##TestRegistry(                                    \
+            skiatest::Test(#name, false, test_##name));                           \
+    void test_##name(skiatest::Reporter* reporter, sk_gpu_test::GrContextFactory*)
+
+
+#define DEF_GPUTEST(name, reporter, factory)                                                 \
+    static void test_##name(skiatest::Reporter*, sk_gpu_test::GrContextFactory*);            \
+    skiatest::TestRegistry name##TestRegistry(                                               \
+            skiatest::Test(#name, true, test_##name));                                       \
+    void test_##name(skiatest::Reporter* reporter, sk_gpu_test::GrContextFactory* factory)
+
+#define DEF_GPUTEST_FOR_CONTEXTS(name, context_filter, reporter, context_info)            \
+    static void test_##name(skiatest::Reporter*,                                          \
+                            const sk_gpu_test::ContextInfo& context_info);                \
+    static void test_gpu_contexts_##name(skiatest::Reporter* reporter,                    \
+                                         sk_gpu_test::GrContextFactory* factory) {        \
+        skiatest::RunWithGPUTestContexts(test_##name, context_filter, reporter, factory); \
+    }                                                                                     \
+    skiatest::TestRegistry name##TestRegistry(                                            \
+            skiatest::Test(#name, true, test_gpu_contexts_##name));                       \
+    void test_##name(skiatest::Reporter* reporter,                                        \
+                     const sk_gpu_test::ContextInfo& context_info)
+
+#define DEF_GPUTEST_FOR_ALL_CONTEXTS(name, reporter, context_info)                          \
+        DEF_GPUTEST_FOR_CONTEXTS(name, nullptr, reporter, context_info)
+#define DEF_GPUTEST_FOR_RENDERING_CONTEXTS(name, reporter, context_info)                    \
+        DEF_GPUTEST_FOR_CONTEXTS(name, sk_gpu_test::GrContextFactory::IsRenderingContext,   \
+                                 reporter, context_info)
+#define DEF_GPUTEST_FOR_ALL_GL_CONTEXTS(name, reporter, context_info)                       \
+        DEF_GPUTEST_FOR_CONTEXTS(name, &skiatest::IsGLContextType, reporter, context_info)
+#define DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(name, reporter, context_info)                 \
+        DEF_GPUTEST_FOR_CONTEXTS(name, &skiatest::IsRenderingGLContextType, reporter, context_info)
+#define DEF_GPUTEST_FOR_NULLGL_CONTEXT(name, reporter, context_info)                        \
+        DEF_GPUTEST_FOR_CONTEXTS(name, &skiatest::IsNullGLContextType, reporter, context_info)
+#define DEF_GPUTEST_FOR_VULKAN_CONTEXT(name, reporter, context_info)                        \
+        DEF_GPUTEST_FOR_CONTEXTS(name, &skiatest::IsVulkanContextType, reporter, context_info)
+
+#define REQUIRE_PDF_DOCUMENT(TEST_NAME, REPORTER)                          \
+    do {                                                                   \
+        SkDynamicMemoryWStream testStream;                                 \
+        sk_sp<SkDocument> testDoc(SkDocument::MakePDF(&testStream));       \
+        if (!testDoc) {                                                    \
+            INFOF(REPORTER, "PDF disabled; %s test skipped.", #TEST_NAME); \
+            return;                                                        \
+        }                                                                  \
+    } while (false)
 
 #endif
diff --git a/src/third_party/skia/tests/TestConfigParsing.cpp b/src/third_party/skia/tests/TestConfigParsing.cpp
new file mode 100644
index 0000000..e24c019
--- /dev/null
+++ b/src/third_party/skia/tests/TestConfigParsing.cpp
@@ -0,0 +1,463 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCommonFlagsConfig.h"
+#include "SkColorSpace_Base.h"
+#include "Test.h"
+#include <initializer_list>
+
+using sk_gpu_test::GrContextFactory;
+
+namespace {
+// The code
+//   SkCommandLineFlags::StringArray FLAGS_config1 = make_string_array({"a", "b"})
+// can be used to construct string array that one gets with command line flags.
+// For example, the call above is equivalent of
+//   DEFINE_string(config1, "a b", "");
+// in cases where the default command line flag value ("a b") is used.
+// make_string_array can be used to construct StringArray strings that have spaces in
+// them.
+SkCommandLineFlags::StringArray make_string_array(std::initializer_list<const char*> strings) {
+    SkTArray<SkString> array;
+    for (auto& s : strings) {
+        array.push_back(SkString(s));
+    }
+    return SkCommandLineFlags::StringArray(array);
+}
+}
+DEF_TEST(ParseConfigs_Gpu, reporter) {
+    // Parses a normal config and returns correct "tag".
+    // Simple GL config works
+    SkCommandLineFlags::StringArray config1 = make_string_array({"gl"});
+    SkCommandLineConfigArray configs;
+    ParseConfigs(config1, &configs);
+
+    REPORTER_ASSERT(reporter, configs.count() == 1);
+    REPORTER_ASSERT(reporter, configs[0]->getTag().equals("gl"));
+    REPORTER_ASSERT(reporter, configs[0]->getViaParts().count() == 0);
+#if SK_SUPPORT_GPU
+    REPORTER_ASSERT(reporter, configs[0]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getContextType()
+                    == GrContextFactory::kGL_ContextType);
+    REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getUseNVPR() == false);
+    REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getUseInstanced() == false);
+    REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getUseDIText() == false);
+    REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getSamples() == 0);
+    REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getColorType() == kRGBA_8888_SkColorType);
+    REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getColorSpace() == nullptr);
+#endif
+}
+
+DEF_TEST(ParseConfigs_OutParam, reporter) {
+    // Clears the out parameter.
+    SkCommandLineFlags::StringArray config1 = make_string_array({"gles"});
+    SkCommandLineConfigArray configs;
+    ParseConfigs(config1, &configs);
+    REPORTER_ASSERT(reporter, configs.count() == 1);
+    REPORTER_ASSERT(reporter, configs[0]->getTag().equals("gles"));
+
+    SkCommandLineFlags::StringArray config2 = make_string_array({"8888"});
+    ParseConfigs(config2, &configs);
+    REPORTER_ASSERT(reporter, configs.count() == 1);
+    REPORTER_ASSERT(reporter, configs[0]->getTag().equals("8888"));
+
+    SkCommandLineFlags::StringArray config3 = make_string_array({"gl"});
+    ParseConfigs(config3, &configs);
+    REPORTER_ASSERT(reporter, configs.count() == 1);
+    REPORTER_ASSERT(reporter, configs[0]->getTag().equals("gl"));
+}
+
+DEF_TEST(ParseConfigs_DefaultConfigs, reporter) {
+    // Parses all default configs and returns correct "tag".
+
+    SkCommandLineFlags::StringArray config1 = make_string_array({
+        "565",
+        "8888",
+        "debuggl",
+        "gl",
+        "gldft",
+        "nullgl",
+        "glmsaa8",
+        "glmsaa4",
+        "nonrendering",
+        "nullgl",
+        "gles",
+        "glnvpr8",
+        "glnvpr4",
+        "glnvprdit8",
+        "glesnvprdit4",
+        "pdf",
+        "skp",
+        "svg",
+        "xps",
+        "angle_d3d11_es2",
+        "angle_gl_es2",
+        "commandbuffer",
+        "mesa",
+        "hwui",
+        "glf16",
+        "glessrgb",
+        "gl",
+        "glnvpr4",
+        "glnvprdit4",
+        "glsrgb",
+        "glmsaa4",
+        "vk",
+        "glinst",
+        "glinst4",
+        "glinstdit4",
+        "glinst8",
+        "glinstdit8",
+        "glesinst",
+        "glesinst4",
+        "glesinstdit4",
+        "glwide",
+        "glnarrow",
+        "glnostencils",
+        "mock",
+        "mtl",
+        "gl4444",
+        "gl565"
+    });
+
+    SkCommandLineConfigArray configs;
+    ParseConfigs(config1, &configs);
+
+    auto srgbColorSpace = SkColorSpace::MakeSRGB();
+
+    REPORTER_ASSERT(reporter, configs.count() == config1.count());
+    for (int i = 0; i < config1.count(); ++i) {
+        REPORTER_ASSERT(reporter, configs[i]->getTag().equals(config1[i]));
+        REPORTER_ASSERT(reporter, configs[i]->getViaParts().count() == 0);
+    }
+#if SK_SUPPORT_GPU
+    REPORTER_ASSERT(reporter, !configs[0]->asConfigGpu());
+    REPORTER_ASSERT(reporter, !configs[1]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[2]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[3]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[4]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, configs[5]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[6]->asConfigGpu()->getSamples() == 8);
+    REPORTER_ASSERT(reporter, configs[7]->asConfigGpu()->getSamples() == 4);
+    REPORTER_ASSERT(reporter, !configs[8]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[9]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[10]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[11]->asConfigGpu()->getSamples() == 8);
+    REPORTER_ASSERT(reporter, configs[11]->asConfigGpu()->getUseNVPR());
+    REPORTER_ASSERT(reporter, !configs[11]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, configs[12]->asConfigGpu()->getSamples() == 4);
+    REPORTER_ASSERT(reporter, configs[12]->asConfigGpu()->getUseNVPR());
+    REPORTER_ASSERT(reporter, !configs[12]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, configs[13]->asConfigGpu()->getSamples() == 8);
+    REPORTER_ASSERT(reporter, configs[13]->asConfigGpu()->getUseNVPR());
+    REPORTER_ASSERT(reporter, configs[13]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, configs[14]->asConfigGpu()->getSamples() == 4);
+    REPORTER_ASSERT(reporter, configs[14]->asConfigGpu()->getUseNVPR());
+    REPORTER_ASSERT(reporter, configs[14]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, !configs[15]->asConfigGpu());
+    REPORTER_ASSERT(reporter, !configs[16]->asConfigGpu());
+    REPORTER_ASSERT(reporter, !configs[17]->asConfigGpu());
+    REPORTER_ASSERT(reporter, !configs[18]->asConfigGpu());
+    REPORTER_ASSERT(reporter, !configs[23]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[24]->asConfigGpu()->getColorType() == kRGBA_F16_SkColorType);
+    REPORTER_ASSERT(reporter, configs[24]->asConfigGpu()->getColorSpace());
+    REPORTER_ASSERT(reporter, configs[24]->asConfigGpu()->getColorSpace()->gammaIsLinear());
+    const SkMatrix44* srgbXYZ = as_CSB(srgbColorSpace)->toXYZD50();
+    SkASSERT(srgbXYZ);
+    const SkMatrix44* config25XYZ =
+            as_CSB(configs[24]->asConfigGpu()->getColorSpace())->toXYZD50();
+    SkASSERT(config25XYZ);
+    REPORTER_ASSERT(reporter, *config25XYZ == *srgbXYZ);
+    REPORTER_ASSERT(reporter, configs[25]->asConfigGpu()->getColorType() == kRGBA_8888_SkColorType);
+    REPORTER_ASSERT(reporter, configs[25]->asConfigGpu()->getColorSpace() == srgbColorSpace.get());
+    REPORTER_ASSERT(reporter, configs[40]->asConfigGpu()->getColorType() == kRGBA_F16_SkColorType);
+    REPORTER_ASSERT(reporter, configs[40]->asConfigGpu()->getColorSpace());
+    REPORTER_ASSERT(reporter, configs[40]->asConfigGpu()->getColorSpace()->gammaIsLinear());
+    const SkMatrix44* config41XYZ =
+            as_CSB(configs[40]->asConfigGpu()->getColorSpace())->toXYZD50();
+    SkASSERT(config41XYZ);
+    REPORTER_ASSERT(reporter, *config41XYZ != *srgbXYZ);
+    REPORTER_ASSERT(reporter, configs[32]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kGL_ContextType);
+    REPORTER_ASSERT(reporter, configs[41]->asConfigGpu()->getColorType() == kRGBA_F16_SkColorType);
+    REPORTER_ASSERT(reporter, configs[41]->asConfigGpu()->getColorSpace());
+    REPORTER_ASSERT(reporter, configs[41]->asConfigGpu()->getColorSpace()->gammaIsLinear());
+    REPORTER_ASSERT(reporter, *as_CSB(configs[41]->asConfigGpu()->getColorSpace())->toXYZD50() !=
+                    *as_CSB(srgbColorSpace)->toXYZD50());
+    REPORTER_ASSERT(reporter, configs[42]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kGL_ContextType);
+    REPORTER_ASSERT(reporter, SkToBool(configs[42]->asConfigGpu()->getContextOverrides() &
+                              SkCommandLineConfigGpu::ContextOverrides::kAvoidStencilBuffers));
+    REPORTER_ASSERT(reporter, configs[43]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kMock_ContextType);
+    REPORTER_ASSERT(reporter, configs[32]->asConfigGpu()->getUseInstanced());
+    REPORTER_ASSERT(reporter, configs[33]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kGL_ContextType);
+    REPORTER_ASSERT(reporter, configs[33]->asConfigGpu()->getUseInstanced());
+    REPORTER_ASSERT(reporter, configs[33]->asConfigGpu()->getSamples() == 4);
+    REPORTER_ASSERT(reporter, configs[34]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kGL_ContextType);
+    REPORTER_ASSERT(reporter, configs[34]->asConfigGpu()->getUseInstanced());
+    REPORTER_ASSERT(reporter, configs[34]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, configs[34]->asConfigGpu()->getSamples() == 4);
+    REPORTER_ASSERT(reporter, configs[35]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kGL_ContextType);
+    REPORTER_ASSERT(reporter, configs[35]->asConfigGpu()->getUseInstanced());
+    REPORTER_ASSERT(reporter, configs[35]->asConfigGpu()->getSamples() == 8);
+    REPORTER_ASSERT(reporter, configs[36]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kGL_ContextType);
+    REPORTER_ASSERT(reporter, configs[36]->asConfigGpu()->getUseInstanced());
+    REPORTER_ASSERT(reporter, configs[36]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, configs[36]->asConfigGpu()->getSamples() == 8);
+    REPORTER_ASSERT(reporter, configs[37]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kGLES_ContextType);
+    REPORTER_ASSERT(reporter, configs[37]->asConfigGpu()->getUseInstanced());
+    REPORTER_ASSERT(reporter, configs[38]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kGLES_ContextType);
+    REPORTER_ASSERT(reporter, configs[38]->asConfigGpu()->getUseInstanced());
+    REPORTER_ASSERT(reporter, configs[38]->asConfigGpu()->getSamples() == 4);
+    REPORTER_ASSERT(reporter, configs[39]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kGLES_ContextType);
+    REPORTER_ASSERT(reporter, configs[39]->asConfigGpu()->getUseInstanced());
+    REPORTER_ASSERT(reporter, configs[39]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, configs[39]->asConfigGpu()->getSamples() == 4);
+    REPORTER_ASSERT(reporter, configs[19]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[20]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[21]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[45]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kGL_ContextType);
+    REPORTER_ASSERT(reporter, configs[45]->asConfigGpu()->getColorType() == kARGB_4444_SkColorType);
+    REPORTER_ASSERT(reporter, configs[45]->asConfigGpu()->getAlphaType() == kPremul_SkAlphaType);
+    REPORTER_ASSERT(reporter, configs[46]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kGL_ContextType);
+    REPORTER_ASSERT(reporter, configs[46]->asConfigGpu()->getColorType() == kRGB_565_SkColorType);
+    REPORTER_ASSERT(reporter, configs[46]->asConfigGpu()->getAlphaType() == kOpaque_SkAlphaType);
+#if SK_MESA
+    REPORTER_ASSERT(reporter, configs[23]->asConfigGpu());
+#else
+    REPORTER_ASSERT(reporter, !configs[22]->asConfigGpu());
+#endif
+    REPORTER_ASSERT(reporter, configs[26]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[27]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[27]->asConfigGpu()->getSamples() == 4);
+    REPORTER_ASSERT(reporter, configs[27]->asConfigGpu()->getUseNVPR());
+    REPORTER_ASSERT(reporter, configs[28]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[28]->asConfigGpu()->getSamples() == 4);
+    REPORTER_ASSERT(reporter, configs[28]->asConfigGpu()->getUseNVPR());
+    REPORTER_ASSERT(reporter, configs[28]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, configs[29]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[29]->asConfigGpu()->getColorType()  == kRGBA_8888_SkColorType);
+    REPORTER_ASSERT(reporter, configs[29]->asConfigGpu()->getColorSpace() == srgbColorSpace.get());
+    REPORTER_ASSERT(reporter, configs[30]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[30]->asConfigGpu()->getSamples() == 4);
+#ifdef SK_VULKAN
+    REPORTER_ASSERT(reporter, configs[31]->asConfigGpu());
+#endif
+#endif
+}
+
+DEF_TEST(ParseConfigs_ExtendedGpuConfigsCorrect, reporter) {
+    SkCommandLineFlags::StringArray config1 = make_string_array({
+        "gpu[api=gl,nvpr=true,dit=false]",
+        "gpu[api=angle_d3d9_es2]",
+        "gpu[api=angle_gl_es3]",
+        "gpu[api=mesa,samples=77]",
+        "gpu[dit=true,api=commandbuffer]",
+        "gpu[api=gles]",
+        "gpu[api=gl]",
+        "gpu[api=vulkan]",
+        "gpu[api=metal]",
+        "gpu[api=mock]",
+    });
+
+    SkCommandLineConfigArray configs;
+    ParseConfigs(config1, &configs);
+    REPORTER_ASSERT(reporter, configs.count() == config1.count());
+    for (int i = 0; i < config1.count(); ++i) {
+        REPORTER_ASSERT(reporter, configs[i]->getTag().equals(config1[i]));
+    }
+#if SK_SUPPORT_GPU
+    REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getContextType() ==
+                    GrContextFactory::kGL_ContextType);
+    REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getUseNVPR());
+    REPORTER_ASSERT(reporter, !configs[0]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, configs[0]->asConfigGpu()->getSamples() == 0);
+    REPORTER_ASSERT(reporter, configs[1]->asConfigGpu()->getContextType() ==
+                    GrContextFactory::kANGLE_D3D9_ES2_ContextType);
+    REPORTER_ASSERT(reporter, configs[1]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[2]->asConfigGpu()->getContextType() ==
+                    GrContextFactory::kANGLE_GL_ES3_ContextType);
+    REPORTER_ASSERT(reporter, configs[2]->asConfigGpu());
+#if SK_MESA
+    REPORTER_ASSERT(reporter, configs[3]->asConfigGpu()->getContextType() ==
+                    GrContextFactory::kMESA_ContextType);
+#else
+    REPORTER_ASSERT(reporter, !configs[3]->asConfigGpu());
+#endif
+    REPORTER_ASSERT(reporter, configs[4]->asConfigGpu()->getContextType() ==
+                    GrContextFactory::kCommandBuffer_ContextType);
+    REPORTER_ASSERT(reporter, configs[5]->asConfigGpu()->getContextType() ==
+                    GrContextFactory::kGLES_ContextType);
+    REPORTER_ASSERT(reporter, !configs[5]->asConfigGpu()->getUseNVPR());
+    REPORTER_ASSERT(reporter, !configs[5]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, configs[5]->asConfigGpu()->getSamples() == 0);
+    REPORTER_ASSERT(reporter, configs[6]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kGL_ContextType);
+    REPORTER_ASSERT(reporter, !configs[6]->asConfigGpu()->getUseNVPR());
+    REPORTER_ASSERT(reporter, !configs[6]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, configs[6]->asConfigGpu()->getSamples() == 0);
+#ifdef SK_VULKAN
+    REPORTER_ASSERT(reporter, configs[7]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kVulkan_ContextType);
+    REPORTER_ASSERT(reporter, !configs[7]->asConfigGpu()->getUseNVPR());
+    REPORTER_ASSERT(reporter, !configs[7]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, configs[7]->asConfigGpu()->getSamples() == 0);
+#endif
+#ifdef SK_METAL
+    REPORTER_ASSERT(reporter, configs[8]->asConfigGpu()->getContextType() ==
+                              GrContextFactory::kMetal_ContextType);
+    REPORTER_ASSERT(reporter, !configs[8]->asConfigGpu()->getUseNVPR());
+    REPORTER_ASSERT(reporter, !configs[8]->asConfigGpu()->getUseDIText());
+    REPORTER_ASSERT(reporter, configs[8]->asConfigGpu()->getSamples() == 0);
+#endif
+    REPORTER_ASSERT(reporter, configs[9]->asConfigGpu()->getContextType() ==
+                   GrContextFactory::kMock_ContextType);
+#endif
+}
+
+DEF_TEST(ParseConfigs_ExtendedGpuConfigsIncorrect, reporter) {
+    SkCommandLineFlags::StringArray config1 = make_string_array({
+        "gpu[api=gl,nvpr=1]", // Number as bool.
+        "gpu[api=gl,]", // Trailing in comma.
+        "gpu[api=angle_glu]", // Unknown api.
+        "gpu[api=,samples=0]", // Empty api.
+        "gpu[api=gl,samples=true]", // Value true as a number.
+        "gpu[api=gl,samples=0,samples=0]", // Duplicate option key.
+        "gpu[,api=gl,samples=0]", // Leading comma.
+        "gpu[samples=54", // Missing closing parenthesis.
+        ",,",
+        "gpu[]", // Missing required api specifier
+        "gpu[samples=4]", // Missing required api specifier
+        "gpu[", // Missing bracket.
+        "samples=54" // No backend.
+        "gpu[nvpr=true ]", // Space.
+    });
+
+    SkCommandLineConfigArray configs;
+    ParseConfigs(config1, &configs);
+    REPORTER_ASSERT(reporter, configs.count() == config1.count());
+    for (int i = 0; i < config1.count(); ++i) {
+        REPORTER_ASSERT(reporter, configs[i]->getTag().equals(config1[i]));
+        REPORTER_ASSERT(reporter, configs[i]->getBackend().equals(config1[i]));
+#if SK_SUPPORT_GPU
+        REPORTER_ASSERT(reporter, !configs[i]->asConfigGpu());
+#endif
+    }
+}
+
+DEF_TEST(ParseConfigs_ExtendedGpuConfigsSurprises, reporter) {
+    // These just list explicitly some properties of the system.
+    SkCommandLineFlags::StringArray config1 = make_string_array({
+        // Options are not canonized -> two same configs have a different tag.
+        "gpu[api=gl,nvpr=true,dit=true]", "gpu[api=gl,dit=true,nvpr=true]",
+        "gpu[api=debuggl]", "gpu[api=gl]", "gpu[api=gles]", ""
+        "gpu[api=gl]", "gpu[api=gl,samples=0]", "gpu[api=gles,samples=0]"
+    });
+    SkCommandLineConfigArray configs;
+    ParseConfigs(config1, &configs);
+    REPORTER_ASSERT(reporter, configs.count() == config1.count());
+    for (int i = 0; i < config1.count(); ++i) {
+        REPORTER_ASSERT(reporter, configs[i]->getTag().equals(config1[i]));
+#if SK_SUPPORT_GPU
+        REPORTER_ASSERT(reporter, configs[i]->getBackend().equals("gpu"));
+        REPORTER_ASSERT(reporter, configs[i]->asConfigGpu());
+#else
+        REPORTER_ASSERT(reporter, configs[i]->getBackend().equals(config1[i]));
+#endif
+    }
+}
+
+#if SK_SUPPORT_GPU
+DEF_TEST(ParseConfigs_ViaParsing, reporter) {
+    SkCommandLineFlags::StringArray config1 = make_string_array({
+        "a-b-c-8888",
+        "zz-qq-gpu",
+        "a-angle_gl_es2"
+    });
+
+    SkCommandLineConfigArray configs;
+    ParseConfigs(config1, &configs);
+    const struct {
+        const char* backend;
+        const char* vias[3];
+    } expectedConfigs[] = {
+        {"8888", {"a", "b", "c"}},
+        {"gpu", {"zz", "qq", nullptr}},
+        {"gpu", { "a", nullptr, nullptr }}
+    };
+    for (int i = 0; i < config1.count(); ++i) {
+        REPORTER_ASSERT(reporter, configs[i]->getTag().equals(config1[i]));
+        REPORTER_ASSERT(reporter, configs[i]->getBackend().equals(expectedConfigs[i].backend));
+        for (int j = 0; j < static_cast<int>(SK_ARRAY_COUNT(expectedConfigs[i].vias)); ++j) {
+            if (!expectedConfigs[i].vias[j]) {
+                REPORTER_ASSERT(reporter, configs[i]->getViaParts().count() == j);
+                break;
+            }
+            REPORTER_ASSERT(reporter,
+                            configs[i]->getViaParts()[j].equals(expectedConfigs[i].vias[j]));
+        }
+    }
+}
+#endif
+
+DEF_TEST(ParseConfigs_ViaParsingExtendedForm, reporter) {
+    SkCommandLineFlags::StringArray config1 = make_string_array({
+        "zz-qq-gpu[api=gles]",
+        "abc-nbc-cbs-gpu[api=angle_d3d9_es2,samples=1]",
+        "a-gpu[api=gl",
+        "abc-def-angle_gl_es2[api=gles]",
+    });
+
+    SkCommandLineConfigArray configs;
+    ParseConfigs(config1, &configs);
+    const struct {
+        const char* backend;
+        const char* vias[3];
+    } expectedConfigs[] = {
+#if SK_SUPPORT_GPU
+        {"gpu", {"zz", "qq", nullptr}},
+        {"gpu", {"abc", "nbc", "cbs"}},
+#else
+        {"gpu[api=gles]", {"zz", "qq", nullptr}},
+        {"gpu[api=angle_d3d9_es2,samples=1]", {"abc", "nbc", "cbs"}},
+#endif
+        {"gpu[api=gl", {"a", nullptr, nullptr}}, // Missing bracket makes this is not extended
+                                                 // form but via still works as expected.
+        {"angle_gl_es2[api=gles]", {"abc", "def", nullptr}}  // This is not extended form.
+                                                             // angle_gl_es2 is an api type not a
+                                                             // backend.
+    };
+    for (int i = 0; i < config1.count(); ++i) {
+        REPORTER_ASSERT(reporter, configs[i]->getTag().equals(config1[i]));
+        REPORTER_ASSERT(reporter, configs[i]->getBackend().equals(expectedConfigs[i].backend));
+        for (int j = 0; j < static_cast<int>(SK_ARRAY_COUNT(expectedConfigs[i].vias)); ++j) {
+            if (!expectedConfigs[i].vias[j]) {
+                REPORTER_ASSERT(reporter, configs[i]->getViaParts().count() ==
+                                static_cast<int>(j));
+                break;
+            }
+            REPORTER_ASSERT(reporter,
+                            configs[i]->getViaParts()[j].equals(expectedConfigs[i].vias[j]));
+        }
+    }
+#if SK_SUPPORT_GPU
+    REPORTER_ASSERT(reporter, configs[0]->asConfigGpu());
+    REPORTER_ASSERT(reporter, configs[1]->asConfigGpu());
+    REPORTER_ASSERT(reporter, !configs[2]->asConfigGpu());
+    REPORTER_ASSERT(reporter, !configs[3]->asConfigGpu());
+#endif
+}
diff --git a/src/third_party/skia/tests/TestTest.cpp b/src/third_party/skia/tests/TestTest.cpp
new file mode 100644
index 0000000..d366bb4
--- /dev/null
+++ b/src/third_party/skia/tests/TestTest.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "gl/GLTestContext.h"
+#endif
+
+
+// This is an example of a normal test.
+DEF_TEST(TestNormal, reporter) {
+    REPORTER_ASSERT(reporter, reporter);
+}
+
+// This is an example of a GPU test that uses common GrContextFactory factory to do the test.
+#if SK_SUPPORT_GPU
+DEF_GPUTEST(TestGpuFactory, reporter, factory) {
+    REPORTER_ASSERT(reporter, reporter);
+    REPORTER_ASSERT(reporter, factory);
+}
+#endif
+
+// This is an example of a GPU test that tests a property that should work for all GPU contexts.
+// Note: Some of the contexts might not produce a rendering output.
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_ALL_CONTEXTS(TestGpuAllContexts, reporter, ctxInfo) {
+    REPORTER_ASSERT(reporter, reporter);
+    REPORTER_ASSERT(reporter, ctxInfo.grContext());
+}
+#endif
+
+// This is an example of a GPU test that tests a property that should work for all GPU contexts that
+// produce a rendering output.
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TestGpuRenderingContexts, reporter, ctxInfo) {
+    REPORTER_ASSERT(reporter, reporter);
+    REPORTER_ASSERT(reporter, ctxInfo.grContext());
+}
+#endif
+
+// This is an example of a GPU test that tests a property that uses the null GPU context.  It should
+// be used if the test tests some behavior that is mocked with the null context.
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_NULLGL_CONTEXT(TestGpuNullContext, reporter, ctxInfo) {
+    REPORTER_ASSERT(reporter, reporter);
+    REPORTER_ASSERT(reporter, ctxInfo.grContext());
+}
+#endif
diff --git a/src/third_party/skia/tests/TestUtils.cpp b/src/third_party/skia/tests/TestUtils.cpp
new file mode 100644
index 0000000..aca7509
--- /dev/null
+++ b/src/third_party/skia/tests/TestUtils.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "TestUtils.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrSurfaceContext.h"
+#include "GrSurfaceProxy.h"
+#include "GrTextureProxy.h"
+
+void test_read_pixels(skiatest::Reporter* reporter,
+                      GrSurfaceContext* srcContext, uint32_t expectedPixelValues[],
+                      const char* testName) {
+    int pixelCnt = srcContext->width() * srcContext->height();
+    SkAutoTMalloc<uint32_t> pixels(pixelCnt);
+    memset(pixels.get(), 0, sizeof(uint32_t)*pixelCnt);
+
+    SkImageInfo ii = SkImageInfo::Make(srcContext->width(), srcContext->height(),
+                                       kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+    bool read = srcContext->readPixels(ii, pixels.get(), 0, 0, 0);
+    if (!read) {
+        ERRORF(reporter, "%s: Error reading from texture.", testName);
+    }
+
+    for (int i = 0; i < pixelCnt; ++i) {
+        if (pixels.get()[i] != expectedPixelValues[i]) {
+            ERRORF(reporter, "%s: Error, pixel value %d should be 0x%08x, got 0x%08x.",
+                   testName, i, expectedPixelValues[i], pixels.get()[i]);
+            break;
+        }
+    }
+}
+
+void test_write_pixels(skiatest::Reporter* reporter,
+                       GrSurfaceContext* dstContext, bool expectedToWork,
+                       const char* testName) {
+    int pixelCnt = dstContext->width() * dstContext->height();
+    SkAutoTMalloc<uint32_t> pixels(pixelCnt);
+    for (int y = 0; y < dstContext->width(); ++y) {
+        for (int x = 0; x < dstContext->height(); ++x) {
+            pixels.get()[y * dstContext->width() + x] =
+                GrPremulColor(GrColorPackRGBA(x, y, x + y, 2*y));
+        }
+    }
+
+    SkImageInfo ii = SkImageInfo::Make(dstContext->width(), dstContext->height(),
+                                       kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+    bool write = dstContext->writePixels(ii, pixels.get(), 0, 0, 0);
+    if (!write) {
+        if (expectedToWork) {
+            ERRORF(reporter, "%s: Error writing to texture.", testName);
+        }
+        return;
+    }
+
+    if (write && !expectedToWork) {
+        ERRORF(reporter, "%s: writePixels succeeded when it wasn't supposed to.", testName);
+        return;
+    }
+
+    test_read_pixels(reporter, dstContext, pixels.get(), testName);
+}
+
+void test_copy_from_surface(skiatest::Reporter* reporter, GrContext* context,
+                            GrSurfaceProxy* proxy, uint32_t expectedPixelValues[],
+                            bool onlyTestRTConfig, const char* testName) {
+    GrSurfaceDesc copyDstDesc;
+    copyDstDesc.fConfig = kRGBA_8888_GrPixelConfig;
+    copyDstDesc.fWidth = proxy->width();
+    copyDstDesc.fHeight = proxy->height();
+
+    for (auto flags : { kNone_GrSurfaceFlags, kRenderTarget_GrSurfaceFlag }) {
+        if (kNone_GrSurfaceFlags == flags && onlyTestRTConfig) {
+            continue;
+        }
+
+        copyDstDesc.fFlags = flags;
+        copyDstDesc.fOrigin = (kNone_GrSurfaceFlags == flags) ? kTopLeft_GrSurfaceOrigin
+                                                              : kBottomLeft_GrSurfaceOrigin;
+
+        sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, copyDstDesc, proxy));
+
+        test_read_pixels(reporter, dstContext.get(), expectedPixelValues, testName);
+    }
+}
+
+void test_copy_to_surface(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider,
+                          GrSurfaceContext* dstContext, const char* testName) {
+
+    int pixelCnt = dstContext->width() * dstContext->height();
+    SkAutoTMalloc<uint32_t> pixels(pixelCnt);
+    for (int y = 0; y < dstContext->width(); ++y) {
+        for (int x = 0; x < dstContext->height(); ++x) {
+            pixels.get()[y * dstContext->width() + x] =
+                GrPremulColor(GrColorPackRGBA(y, x, x * y, 2*y));
+        }
+    }
+
+    GrSurfaceDesc copySrcDesc;
+    copySrcDesc.fConfig = kRGBA_8888_GrPixelConfig;
+    copySrcDesc.fWidth = dstContext->width();
+    copySrcDesc.fHeight = dstContext->height();
+
+    for (auto flags : { kNone_GrSurfaceFlags, kRenderTarget_GrSurfaceFlag }) {
+        copySrcDesc.fFlags = flags;
+        copySrcDesc.fOrigin = (kNone_GrSurfaceFlags == flags) ? kTopLeft_GrSurfaceOrigin
+                                                              : kBottomLeft_GrSurfaceOrigin;
+
+        sk_sp<GrTextureProxy> src(GrSurfaceProxy::MakeDeferred(resourceProvider,
+                                                               copySrcDesc,
+                                                               SkBudgeted::kYes, pixels.get(), 0));
+        dstContext->copy(src.get());
+
+        test_read_pixels(reporter, dstContext, pixels.get(), testName);
+    }
+}
+
+#endif
diff --git a/src/third_party/skia/tests/TestUtils.h b/src/third_party/skia/tests/TestUtils.h
new file mode 100644
index 0000000..6f878bf
--- /dev/null
+++ b/src/third_party/skia/tests/TestUtils.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+class GrSurfaceContext;
+class GrSurfaceProxy;
+
+// Ensure that reading back from 'srcContext' as RGBA 8888 matches 'expectedPixelValues
+void test_read_pixels(skiatest::Reporter*,
+                      GrSurfaceContext* srcContext, uint32_t expectedPixelValues[],
+                      const char* testName);
+
+// See if trying to write RGBA 8888 pixels to 'dstContext' matches matches the
+// expectation ('expectedToWork')
+void test_write_pixels(skiatest::Reporter*,
+                       GrSurfaceContext* srcContext, bool expectedToWork, const char* testName);
+
+// Ensure that the pixels can be copied from 'proxy' to an RGBA 8888 destination (both
+// texture-backed and rendertarget-backed).
+void test_copy_from_surface(skiatest::Reporter*, GrContext*,
+                            GrSurfaceProxy* proxy, uint32_t expectedPixelValues[],
+                            bool onlyTestRTConfig, const char* testName);
+
+// Ensure that RGBA 8888 pixels can be copied into 'dstContext'
+void test_copy_to_surface(skiatest::Reporter*, GrResourceProvider*,
+                          GrSurfaceContext* dstContext, const char* testName);
+#endif
diff --git a/src/third_party/skia/tests/TextBlobCacheTest.cpp b/src/third_party/skia/tests/TextBlobCacheTest.cpp
new file mode 100644
index 0000000..995328d
--- /dev/null
+++ b/src/third_party/skia/tests/TextBlobCacheTest.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "sk_tool_utils.h"
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkPoint.h"
+#include "SkTextBlob.h"
+#include "SkFontMgr.h"
+#include "SkGraphics.h"
+#include "SkSurface.h"
+#include "SkTypeface.h"
+#include "../src/fonts/SkRandomScalerContext.h"
+
+#ifdef SK_BUILD_FOR_WIN
+    #include "SkTypeface_win.h"
+#endif
+
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrTest.h"
+
+static void draw(SkCanvas* canvas, int redraw, const SkTArray<sk_sp<SkTextBlob>>& blobs) {
+    int yOffset = 0;
+    for (int r = 0; r < redraw; r++) {
+        for (int i = 0; i < blobs.count(); i++) {
+            const auto& blob = blobs[i];
+            const SkRect& bounds = blob->bounds();
+            yOffset += SkScalarCeilToInt(bounds.height());
+            SkPaint paint;
+            canvas->drawTextBlob(blob, 0, SkIntToScalar(yOffset), paint);
+        }
+    }
+}
+
+static const int kWidth = 1024;
+static const int kHeight = 768;
+
+// This test hammers the GPU textblobcache and font atlas
+static void text_blob_cache_inner(skiatest::Reporter* reporter, GrContext* context,
+                                  int maxTotalText, int maxGlyphID, int maxFamilies, bool normal,
+                                  bool stressTest) {
+    // setup surface
+    uint32_t flags = 0;
+    SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
+
+    // configure our context for maximum stressing of cache and atlas
+    if (stressTest) {
+        GrTest::SetupAlwaysEvictAtlas(context);
+        context->setTextBlobCacheLimit_ForTesting(0);
+    }
+
+    SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kN32_SkColorType, kPremul_SkAlphaType);
+    auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0, &props));
+    REPORTER_ASSERT(reporter, surface);
+    if (!surface) {
+        return;
+    }
+
+    SkCanvas* canvas = surface->getCanvas();
+
+    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+
+    int count = SkMin32(fm->countFamilies(), maxFamilies);
+
+    // make a ton of text
+    SkAutoTArray<uint16_t> text(maxTotalText);
+    for (int i = 0; i < maxTotalText; i++) {
+        text[i] = i % maxGlyphID;
+    }
+
+    // generate textblobs
+    SkTArray<sk_sp<SkTextBlob>> blobs;
+    for (int i = 0; i < count; i++) {
+        SkPaint paint;
+        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+        paint.setTextSize(48); // draw big glyphs to really stress the atlas
+
+        SkString familyName;
+        fm->getFamilyName(i, &familyName);
+        sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
+        for (int j = 0; j < set->count(); ++j) {
+            SkFontStyle fs;
+            set->getStyle(j, &fs, nullptr);
+
+            // We use a typeface which randomy returns unexpected mask formats to fuzz
+            sk_sp<SkTypeface> orig(set->createTypeface(j));
+            if (normal) {
+                paint.setTypeface(orig);
+            } else {
+                paint.setTypeface(sk_make_sp<SkRandomTypeface>(orig, paint, true));
+            }
+
+            SkTextBlobBuilder builder;
+            for (int aa = 0; aa < 2; aa++) {
+                for (int subpixel = 0; subpixel < 2; subpixel++) {
+                    for (int lcd = 0; lcd < 2; lcd++) {
+                        paint.setAntiAlias(SkToBool(aa));
+                        paint.setSubpixelText(SkToBool(subpixel));
+                        paint.setLCDRenderText(SkToBool(lcd));
+                        if (!SkToBool(lcd)) {
+                            paint.setTextSize(160);
+                        }
+                        const SkTextBlobBuilder::RunBuffer& run = builder.allocRun(paint,
+                                                                                   maxTotalText,
+                                                                                   0, 0,
+                                                                                   nullptr);
+                        memcpy(run.glyphs, text.get(), maxTotalText * sizeof(uint16_t));
+                    }
+                }
+            }
+            blobs.emplace_back(builder.make());
+        }
+    }
+
+    // create surface where LCD is impossible
+    info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
+    SkSurfaceProps propsNoLCD(0, kUnknown_SkPixelGeometry);
+    auto surfaceNoLCD(canvas->makeSurface(info, &propsNoLCD));
+    REPORTER_ASSERT(reporter, surface);
+    if (!surface) {
+        return;
+    }
+
+    SkCanvas* canvasNoLCD = surfaceNoLCD->getCanvas();
+
+    // test redraw
+    draw(canvas, 2, blobs);
+    draw(canvasNoLCD, 2, blobs);
+
+    // test draw after free
+    context->freeGpuResources();
+    draw(canvas, 1, blobs);
+
+    context->freeGpuResources();
+    draw(canvasNoLCD, 1, blobs);
+
+    // test draw after abandon
+    context->abandonContext();
+    draw(canvas, 1, blobs);
+}
+
+DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobCache, reporter, ctxInfo) {
+    text_blob_cache_inner(reporter, ctxInfo.grContext(), 1024, 256, 30, true, false);
+}
+
+DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobStressCache, reporter, ctxInfo) {
+    text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, true, true);
+}
+
+DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobAbnormal, reporter, ctxInfo) {
+    text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, false, false);
+}
+
+DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobStressAbnormal, reporter, ctxInfo) {
+    text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, false, true);
+}
+#endif
diff --git a/src/third_party/skia/tests/TextBlobTest.cpp b/src/third_party/skia/tests/TextBlobTest.cpp
index 5d08b01..c00970f 100644
--- a/src/third_party/skia/tests/TextBlobTest.cpp
+++ b/src/third_party/skia/tests/TextBlobTest.cpp
@@ -7,11 +7,11 @@
 
 #include "SkPaint.h"
 #include "SkPoint.h"
-#include "SkTextBlob.h"
+#include "SkTextBlobRunIterator.h"
+#include "SkTypeface.h"
 
 #include "Test.h"
 
-
 class TextBlobTester {
 public:
     // This unit test feeds an SkTextBlobBuilder various runs then checks to see if
@@ -20,7 +20,7 @@
         SkTextBlobBuilder builder;
 
         // empty run set
-        RunBuilderTest(reporter, builder, NULL, 0, NULL, 0);
+        RunBuilderTest(reporter, builder, nullptr, 0, nullptr, 0);
 
         RunDef set1[] = {
             { 128, SkTextBlob::kDefault_Positioning, 100, 100 },
@@ -104,28 +104,28 @@
 
         // Explicit bounds.
         {
-            SkAutoTUnref<const SkTextBlob> blob(builder.build());
-            REPORTER_ASSERT(reporter, blob->bounds().isEmpty());
+            sk_sp<SkTextBlob> blob(builder.make());
+            REPORTER_ASSERT(reporter, !blob);
         }
 
         {
             SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20);
             builder.allocRun(font, 16, 0, 0, &r1);
-            SkAutoTUnref<const SkTextBlob> blob(builder.build());
+            sk_sp<SkTextBlob> blob(builder.make());
             REPORTER_ASSERT(reporter, blob->bounds() == r1);
         }
 
         {
             SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20);
             builder.allocRunPosH(font, 16, 0, &r1);
-            SkAutoTUnref<const SkTextBlob> blob(builder.build());
+            sk_sp<SkTextBlob> blob(builder.make());
             REPORTER_ASSERT(reporter, blob->bounds() == r1);
         }
 
         {
             SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20);
             builder.allocRunPos(font, 16, &r1);
-            SkAutoTUnref<const SkTextBlob> blob(builder.build());
+            sk_sp<SkTextBlob> blob(builder.make());
             REPORTER_ASSERT(reporter, blob->bounds() == r1);
         }
 
@@ -138,18 +138,115 @@
             builder.allocRunPosH(font, 16, 0, &r2);
             builder.allocRunPos(font, 16, &r3);
 
-            SkAutoTUnref<const SkTextBlob> blob(builder.build());
+            sk_sp<SkTextBlob> blob(builder.make());
             REPORTER_ASSERT(reporter, blob->bounds() == SkRect::MakeXYWH(0, 5, 65, 65));
         }
 
         {
-            // Verify empty blob bounds after building some non-empty blobs.
-            SkAutoTUnref<const SkTextBlob> blob(builder.build());
-            REPORTER_ASSERT(reporter, blob->bounds().isEmpty());
+            sk_sp<SkTextBlob> blob(builder.make());
+            REPORTER_ASSERT(reporter, !blob);
         }
 
         // Implicit bounds
-        // FIXME: not supported yet.
+
+        {
+            // Exercise the empty bounds path, and ensure that RunRecord-aligned pos buffers
+            // don't trigger asserts (http://crbug.com/542643).
+            SkPaint p;
+            p.setTextSize(0);
+            p.setTextEncoding(SkPaint::kUTF8_TextEncoding);
+
+            const char* txt = "BOOO";
+            const size_t txtLen = strlen(txt);
+            const int glyphCount = p.textToGlyphs(txt, txtLen, nullptr);
+
+            p.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+            const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(p, glyphCount);
+
+            p.setTextEncoding(SkPaint::kUTF8_TextEncoding);
+            p.textToGlyphs(txt, txtLen, buffer.glyphs);
+
+            memset(buffer.pos, 0, sizeof(SkScalar) * glyphCount * 2);
+            sk_sp<SkTextBlob> blob(builder.make());
+            REPORTER_ASSERT(reporter, blob->bounds().isEmpty());
+        }
+    }
+
+    // Verify that text-related properties are captured in run paints.
+    static void TestPaintProps(skiatest::Reporter* reporter) {
+        SkPaint font;
+        font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+        // Kitchen sink font.
+        font.setTextSize(42);
+        font.setTextScaleX(4.2f);
+        font.setTypeface(SkTypeface::MakeDefault());
+        font.setTextSkewX(0.42f);
+        font.setTextAlign(SkPaint::kCenter_Align);
+        font.setHinting(SkPaint::kFull_Hinting);
+        font.setAntiAlias(true);
+        font.setFakeBoldText(true);
+        font.setLinearText(true);
+        font.setSubpixelText(true);
+        font.setDevKernText(true);
+        font.setLCDRenderText(true);
+        font.setEmbeddedBitmapText(true);
+        font.setAutohinted(true);
+        font.setVerticalText(true);
+        font.setFlags(font.getFlags() | SkPaint::kGenA8FromLCD_Flag);
+
+        // Ensure we didn't pick default values by mistake.
+        SkPaint defaultPaint;
+        REPORTER_ASSERT(reporter, defaultPaint.getTextSize() != font.getTextSize());
+        REPORTER_ASSERT(reporter, defaultPaint.getTextScaleX() != font.getTextScaleX());
+        REPORTER_ASSERT(reporter, defaultPaint.getTypeface() != font.getTypeface());
+        REPORTER_ASSERT(reporter, defaultPaint.getTextSkewX() != font.getTextSkewX());
+        REPORTER_ASSERT(reporter, defaultPaint.getTextAlign() != font.getTextAlign());
+        REPORTER_ASSERT(reporter, defaultPaint.getHinting() != font.getHinting());
+        REPORTER_ASSERT(reporter, defaultPaint.isAntiAlias() != font.isAntiAlias());
+        REPORTER_ASSERT(reporter, defaultPaint.isFakeBoldText() != font.isFakeBoldText());
+        REPORTER_ASSERT(reporter, defaultPaint.isLinearText() != font.isLinearText());
+        REPORTER_ASSERT(reporter, defaultPaint.isSubpixelText() != font.isSubpixelText());
+        REPORTER_ASSERT(reporter, defaultPaint.isDevKernText() != font.isDevKernText());
+        REPORTER_ASSERT(reporter, defaultPaint.isLCDRenderText() != font.isLCDRenderText());
+        REPORTER_ASSERT(reporter, defaultPaint.isEmbeddedBitmapText() != font.isEmbeddedBitmapText());
+        REPORTER_ASSERT(reporter, defaultPaint.isAutohinted() != font.isAutohinted());
+        REPORTER_ASSERT(reporter, defaultPaint.isVerticalText() != font.isVerticalText());
+        REPORTER_ASSERT(reporter, (defaultPaint.getFlags() & SkPaint::kGenA8FromLCD_Flag) !=
+                                  (font.getFlags() & SkPaint::kGenA8FromLCD_Flag));
+
+        SkTextBlobBuilder builder;
+        AddRun(font, 1, SkTextBlob::kDefault_Positioning, SkPoint::Make(0, 0), builder);
+        AddRun(font, 1, SkTextBlob::kHorizontal_Positioning, SkPoint::Make(0, 0), builder);
+        AddRun(font, 1, SkTextBlob::kFull_Positioning, SkPoint::Make(0, 0), builder);
+        sk_sp<SkTextBlob> blob(builder.make());
+
+        SkTextBlobRunIterator it(blob.get());
+        while (!it.done()) {
+            SkPaint paint;
+            it.applyFontToPaint(&paint);
+
+            REPORTER_ASSERT(reporter, paint.getTextSize() == font.getTextSize());
+            REPORTER_ASSERT(reporter, paint.getTextScaleX() == font.getTextScaleX());
+            REPORTER_ASSERT(reporter, paint.getTypeface() == font.getTypeface());
+            REPORTER_ASSERT(reporter, paint.getTextSkewX() == font.getTextSkewX());
+            REPORTER_ASSERT(reporter, paint.getTextAlign() == font.getTextAlign());
+            REPORTER_ASSERT(reporter, paint.getHinting() == font.getHinting());
+            REPORTER_ASSERT(reporter, paint.isAntiAlias() == font.isAntiAlias());
+            REPORTER_ASSERT(reporter, paint.isFakeBoldText() == font.isFakeBoldText());
+            REPORTER_ASSERT(reporter, paint.isLinearText() == font.isLinearText());
+            REPORTER_ASSERT(reporter, paint.isSubpixelText() == font.isSubpixelText());
+            REPORTER_ASSERT(reporter, paint.isDevKernText() == font.isDevKernText());
+            REPORTER_ASSERT(reporter, paint.isLCDRenderText() == font.isLCDRenderText());
+            REPORTER_ASSERT(reporter, paint.isEmbeddedBitmapText() == font.isEmbeddedBitmapText());
+            REPORTER_ASSERT(reporter, paint.isAutohinted() == font.isAutohinted());
+            REPORTER_ASSERT(reporter, paint.isVerticalText() == font.isVerticalText());
+            REPORTER_ASSERT(reporter, (paint.getFlags() & SkPaint::kGenA8FromLCD_Flag) ==
+                                      (font.getFlags() & SkPaint::kGenA8FromLCD_Flag));
+
+            it.next();
+        }
+
     }
 
 private:
@@ -174,9 +271,13 @@
             posCount += in[i].count * in[i].pos;
         }
 
-        SkAutoTUnref<const SkTextBlob> blob(builder.build());
+        sk_sp<SkTextBlob> blob(builder.make());
+        REPORTER_ASSERT(reporter, (inCount > 0) == SkToBool(blob));
+        if (!blob) {
+            return;
+        }
 
-        SkTextBlob::RunIterator it(blob);
+        SkTextBlobRunIterator it(blob.get());
         for (unsigned i = 0; i < outCount; ++i) {
             REPORTER_ASSERT(reporter, !it.done());
             REPORTER_ASSERT(reporter, out[i].pos == it.positioning());
@@ -206,7 +307,7 @@
 
     static void AddRun(const SkPaint& font, int count, SkTextBlob::GlyphPositioning pos,
                        const SkPoint& offset, SkTextBlobBuilder& builder,
-                       const SkRect* bounds = NULL) {
+                       const SkRect* bounds = nullptr) {
         switch (pos) {
         case SkTextBlob::kDefault_Positioning: {
             const SkTextBlobBuilder::RunBuffer& rb = builder.allocRun(font, count, offset.x(),
@@ -241,3 +342,128 @@
     TextBlobTester::TestBuilder(reporter);
     TextBlobTester::TestBounds(reporter);
 }
+
+DEF_TEST(TextBlob_paint, reporter) {
+    TextBlobTester::TestPaintProps(reporter);
+}
+
+DEF_TEST(TextBlob_extended, reporter) {
+    SkTextBlobBuilder textBlobBuilder;
+    SkPaint paint;
+    const char text1[] = "Foo";
+    const char text2[] = "Bar";
+
+    int glyphCount = paint.textToGlyphs(text1, strlen(text1), nullptr);
+    SkAutoTMalloc<uint16_t> glyphs(glyphCount);
+    (void)paint.textToGlyphs(text1, strlen(text1), glyphs.get());
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+    auto run = textBlobBuilder.allocRunText(
+            paint, glyphCount, 0, 0, SkToInt(strlen(text2)), SkString(), nullptr);
+    memcpy(run.glyphs, glyphs.get(), sizeof(uint16_t) * glyphCount);
+    memcpy(run.utf8text, text2, strlen(text2));
+    for (int i = 0; i < glyphCount; ++i) {
+        run.clusters[i] = SkTMin(SkToU32(i), SkToU32(strlen(text2)));
+    }
+    sk_sp<SkTextBlob> blob(textBlobBuilder.make());
+    REPORTER_ASSERT(reporter, blob);
+
+    for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) {
+        REPORTER_ASSERT(reporter, it.glyphCount() == (uint32_t)glyphCount);
+        for (uint32_t i = 0; i < it.glyphCount(); ++i) {
+            REPORTER_ASSERT(reporter, it.glyphs()[i] == glyphs[i]);
+        }
+        REPORTER_ASSERT(reporter, SkTextBlob::kDefault_Positioning == it.positioning());
+        REPORTER_ASSERT(reporter, (SkPoint{0.0f, 0.0f}) == it.offset());
+        REPORTER_ASSERT(reporter, it.textSize() > 0);
+        REPORTER_ASSERT(reporter, it.clusters());
+        for (uint32_t i = 0; i < it.glyphCount(); ++i) {
+            REPORTER_ASSERT(reporter, i == it.clusters()[i]);
+        }
+        REPORTER_ASSERT(reporter, 0 == strncmp(text2, it.text(), it.textSize()));
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#include "SkCanvas.h"
+#include "SkSurface.h"
+#include "SkTDArray.h"
+
+static void add_run(SkTextBlobBuilder* builder, const char text[], SkScalar x, SkScalar y,
+                    sk_sp<SkTypeface> tf) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setSubpixelText(true);
+    paint.setTextSize(16);
+    paint.setTypeface(tf);
+
+    int glyphCount = paint.textToGlyphs(text, strlen(text), nullptr);
+
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    SkTextBlobBuilder::RunBuffer buffer = builder->allocRun(paint, glyphCount, x, y);
+
+    paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
+    (void)paint.textToGlyphs(text, strlen(text), buffer.glyphs);
+}
+
+static sk_sp<SkImage> render(const SkTextBlob* blob) {
+    auto surf = SkSurface::MakeRasterN32Premul(SkScalarRoundToInt(blob->bounds().width()),
+                                               SkScalarRoundToInt(blob->bounds().height()));
+    if (!surf) {
+        return nullptr; // bounds are empty?
+    }
+    surf->getCanvas()->clear(SK_ColorWHITE);
+    surf->getCanvas()->drawTextBlob(blob, -blob->bounds().left(), -blob->bounds().top(), SkPaint());
+    return surf->makeImageSnapshot();
+}
+
+/*
+ *  Build a blob with more than one typeface.
+ *  Draw it into an offscreen,
+ *  then serialize and deserialize,
+ *  Then draw the new instance and assert it draws the same as the original.
+ */
+DEF_TEST(TextBlob_serialize, reporter) {
+    SkTextBlobBuilder builder;
+
+    sk_sp<SkTypeface> tf0;
+    sk_sp<SkTypeface> tf1 = SkTypeface::MakeFromName("Times", SkFontStyle());
+
+    add_run(&builder, "Hello", 10, 20, tf0);
+    add_run(&builder, "World", 10, 40, tf1);
+    sk_sp<SkTextBlob> blob0 = builder.make();
+
+    SkTDArray<SkTypeface*> array;
+    sk_sp<SkData> data = blob0->serialize([&array](SkTypeface* tf) {
+        if (array.find(tf) < 0) {
+            *array.append() = tf;
+        }
+    });
+    REPORTER_ASSERT(reporter, array.count() > 0);
+
+    sk_sp<SkTextBlob> blob1 = SkTextBlob::Deserialize(data->data(), data->size(),
+                                                      [&array, reporter](uint32_t uniqueID) {
+        for (int i = 0; i < array.count(); ++i) {
+            if (array[i]->uniqueID() == uniqueID) {
+                return sk_ref_sp(array[i]);
+            }
+        }
+        REPORTER_ASSERT(reporter, false);
+        return sk_sp<SkTypeface>(nullptr);
+    });
+
+    sk_sp<SkImage> img0 = render(blob0.get());
+    sk_sp<SkImage> img1 = render(blob1.get());
+    if (img0 && img1) {
+        REPORTER_ASSERT(reporter, img0->width() == img1->width());
+        REPORTER_ASSERT(reporter, img0->height() == img1->height());
+
+        sk_sp<SkData> enc0 = img0->encodeToData();
+        sk_sp<SkData> enc1 = img1->encodeToData();
+        REPORTER_ASSERT(reporter, enc0->equals(enc1.get()));
+        if (false) {    // in case you want to actually see the images...
+            SkFILEWStream("textblob_serialize_img0.png").write(enc0->data(), enc0->size());
+            SkFILEWStream("textblob_serialize_img1.png").write(enc1->data(), enc1->size());
+        }
+    }
+}
diff --git a/src/third_party/skia/tests/TextureCompressionTest.cpp b/src/third_party/skia/tests/TextureCompressionTest.cpp
deleted file mode 100644
index 7dd285d..0000000
--- a/src/third_party/skia/tests/TextureCompressionTest.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBitmap.h"
-#include "SkData.h"
-#include "SkEndian.h"
-#include "SkImageInfo.h"
-#include "SkTextureCompressor.h"
-#include "Test.h"
-
-// TODO: Create separate tests for RGB and RGBA data once
-// ASTC and ETC1 decompression is implemented.
-
-static bool decompresses_a8(SkTextureCompressor::Format fmt) {
-    switch (fmt) {
-        case SkTextureCompressor::kLATC_Format:
-        case SkTextureCompressor::kR11_EAC_Format:
-            return true;
-
-        default:
-            return false;
-    }
-}
-
-static bool compresses_a8(SkTextureCompressor::Format fmt) {
-    switch (fmt) {
-        case SkTextureCompressor::kLATC_Format:
-        case SkTextureCompressor::kR11_EAC_Format:
-        case SkTextureCompressor::kASTC_12x12_Format:
-            return true;
-
-        default:
-            return false;
-    }
-}
-
-/**
- * Make sure that we properly fail when we don't have multiple of four image dimensions.
- */
-DEF_TEST(CompressAlphaFailDimensions, reporter) {
-    SkBitmap bitmap;
-    static const int kWidth = 17;
-    static const int kHeight = 17;
-    SkImageInfo info = SkImageInfo::MakeA8(kWidth, kHeight);
-
-    // R11_EAC and LATC are both dimensions of 4, so we need to make sure that we
-    // are violating those assumptions. And if we are, then we're also violating the
-    // assumptions of ASTC, which is 12x12 since any number not divisible by 4 is
-    // also not divisible by 12. Our dimensions are prime, so any block dimension
-    // larger than 1 should fail.
-    REPORTER_ASSERT(reporter, kWidth % 4 != 0);
-    REPORTER_ASSERT(reporter, kHeight % 4 != 0);
-
-    bool setInfoSuccess = bitmap.setInfo(info);
-    REPORTER_ASSERT(reporter, setInfoSuccess);
-
-    bitmap.allocPixels(info);
-    bitmap.unlockPixels();
-    
-    for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) {
-        const SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i);
-        if (!compresses_a8(fmt)) {
-            continue;
-        }
-        SkAutoDataUnref data(SkTextureCompressor::CompressBitmapToFormat(bitmap, fmt));
-        REPORTER_ASSERT(reporter, NULL == data);
-    }
-}
-
-/**
- * Make sure that we properly fail when we don't have the correct bitmap type.
- * compressed textures can (currently) only be created from A8 bitmaps.
- */
-DEF_TEST(CompressAlphaFailColorType, reporter) {
-    SkBitmap bitmap;
-    static const int kWidth = 12;
-    static const int kHeight = 12;
-    SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
-
-    // ASTC is at most 12x12, and any dimension divisible by 12 is also divisible
-    // by 4, which is the dimensions of R11_EAC and LATC. In the future, we might
-    // support additional variants of ASTC, such as 5x6 and 8x8, in which case this would
-    // need to be updated.
-    REPORTER_ASSERT(reporter, kWidth % 12 == 0);
-    REPORTER_ASSERT(reporter, kHeight % 12 == 0);
-
-    bool setInfoSuccess = bitmap.setInfo(info);
-    REPORTER_ASSERT(reporter, setInfoSuccess);
-
-    bitmap.allocPixels(info);
-    bitmap.unlockPixels();
-
-    for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) {
-        const SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i);
-        if (!compresses_a8(fmt)) {
-            continue;
-        }
-        SkAutoDataUnref data(SkTextureCompressor::CompressBitmapToFormat(bitmap, fmt));
-        REPORTER_ASSERT(reporter, NULL == data);
-    }
-}
-
-/**
- * Make sure that if you compress a texture with alternating black/white pixels, and
- * then decompress it, you get what you started with.
- */
-DEF_TEST(CompressCheckerboard, reporter) {
-    SkBitmap bitmap;
-    static const int kWidth = 48;  // We need the number to be divisible by both
-    static const int kHeight = 48; // 12 (ASTC) and 16 (ARM NEON R11 EAC).
-    SkImageInfo info = SkImageInfo::MakeA8(kWidth, kHeight);
-
-    // ASTC is at most 12x12, and any dimension divisible by 12 is also divisible
-    // by 4, which is the dimensions of R11_EAC and LATC. In the future, we might
-    // support additional variants of ASTC, such as 5x6 and 8x8, in which case this would
-    // need to be updated. Additionally, ARM NEON and SSE code paths support up to
-    // four blocks of R11 EAC at once, so they operate on 16-wide blocks. Hence, the
-    // valid width and height is going to be the LCM of 12 and 16 which is 4*4*3 = 48
-    REPORTER_ASSERT(reporter, kWidth % 48 == 0);
-    REPORTER_ASSERT(reporter, kHeight % 48 == 0);
-
-    bool setInfoSuccess = bitmap.setInfo(info);
-    REPORTER_ASSERT(reporter, setInfoSuccess);
-
-    bitmap.allocPixels(info);
-    bitmap.unlockPixels();
-
-    // Populate bitmap
-    {
-        SkAutoLockPixels alp(bitmap);
-
-        uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels());
-        REPORTER_ASSERT(reporter, pixels);
-        if (NULL == pixels) {
-            return;
-        }
-
-        for (int y = 0; y < kHeight; ++y) {
-            for (int x = 0; x < kWidth; ++x) {
-                if ((x ^ y) & 1) {
-                    pixels[x] = 0xFF;
-                } else {
-                    pixels[x] = 0;
-                }
-            }
-            pixels += bitmap.rowBytes();
-        }
-    }
-
-    SkAutoMalloc decompMemory(kWidth*kHeight);
-    uint8_t* decompBuffer = reinterpret_cast<uint8_t*>(decompMemory.get());
-    REPORTER_ASSERT(reporter, decompBuffer);
-    if (NULL == decompBuffer) {
-        return;
-    }
-
-    for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) {
-        const SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i);
-
-        // Ignore formats for RGBA data, since the decompressed buffer
-        // won't match the size and contents of the original.
-        if (!decompresses_a8(fmt) || !compresses_a8(fmt)) {
-            continue;
-        }
-
-        SkAutoDataUnref data(SkTextureCompressor::CompressBitmapToFormat(bitmap, fmt));
-        REPORTER_ASSERT(reporter, data);
-        if (NULL == data) {
-            continue;
-        }
-
-        bool decompResult =
-            SkTextureCompressor::DecompressBufferFromFormat(
-                decompBuffer, kWidth,
-                data->bytes(),
-                kWidth, kHeight, fmt);
-        REPORTER_ASSERT(reporter, decompResult);
-
-        SkAutoLockPixels alp(bitmap);
-        uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels());
-        REPORTER_ASSERT(reporter, pixels);
-        if (NULL == pixels) {
-            continue;
-        }
-
-        for (int y = 0; y < kHeight; ++y) {
-            for (int x = 0; x < kWidth; ++x) {
-                bool ok = pixels[y*bitmap.rowBytes() + x] == decompBuffer[y*kWidth + x];
-                REPORTER_ASSERT(reporter, ok);
-            }
-        }
-    }
-}
-
-/**
- * Make sure that if we pass in a solid color bitmap that we get the appropriate results
- */
-DEF_TEST(CompressLATC, reporter) {
-
-    const SkTextureCompressor::Format kLATCFormat = SkTextureCompressor::kLATC_Format;
-    static const int kLATCEncodedBlockSize = 8;
-
-    SkBitmap bitmap;
-    static const int kWidth = 8;
-    static const int kHeight = 8;
-    SkImageInfo info = SkImageInfo::MakeA8(kWidth, kHeight);
-
-    bool setInfoSuccess = bitmap.setInfo(info);
-    REPORTER_ASSERT(reporter, setInfoSuccess);
-
-    bitmap.allocPixels(info);
-    bitmap.unlockPixels();
-
-    int latcDimX, latcDimY;
-    SkTextureCompressor::GetBlockDimensions(kLATCFormat, &latcDimX, &latcDimY);
-
-    REPORTER_ASSERT(reporter, kWidth % latcDimX == 0);
-    REPORTER_ASSERT(reporter, kHeight % latcDimY == 0);
-    const size_t kSizeToBe =
-        SkTextureCompressor::GetCompressedDataSize(kLATCFormat, kWidth, kHeight);
-    REPORTER_ASSERT(reporter, kSizeToBe == ((kWidth*kHeight*kLATCEncodedBlockSize)/16));
-    REPORTER_ASSERT(reporter, (kSizeToBe % kLATCEncodedBlockSize) == 0);
-
-    for (int lum = 0; lum < 256; ++lum) {
-        bitmap.lockPixels();
-        uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels());
-        REPORTER_ASSERT(reporter, pixels);
-        if (NULL == pixels) {
-            bitmap.unlockPixels();
-            continue;
-        }
-
-        for (int i = 0; i < kWidth*kHeight; ++i) {
-            pixels[i] = lum;
-        }
-        bitmap.unlockPixels();
-
-        SkAutoDataUnref latcData(
-            SkTextureCompressor::CompressBitmapToFormat(bitmap, kLATCFormat));
-        REPORTER_ASSERT(reporter, latcData);
-        if (NULL == latcData) {
-            continue;
-        }
-
-        REPORTER_ASSERT(reporter, kSizeToBe == latcData->size());
-
-        // Make sure that it all matches a given block encoding. Since we have
-        // COMPRESS_LATC_FAST defined in SkTextureCompressor_LATC.cpp, we are using
-        // an approximation scheme that optimizes for speed against coverage maps.
-        // That means that each palette in the encoded block is exactly the same,
-        // and that the three bits saved per pixel are computed from the top three
-        // bits of the luminance value.
-        const uint64_t kIndexEncodingMap[8] = { 1, 7, 6, 5, 4, 3, 2, 0 };
-        const uint64_t kIndex = kIndexEncodingMap[lum >> 5];
-        const uint64_t kConstColorEncoding =
-            SkEndian_SwapLE64(
-                255 |
-                (kIndex << 16) | (kIndex << 19) | (kIndex << 22) | (kIndex << 25) |
-                (kIndex << 28) | (kIndex << 31) | (kIndex << 34) | (kIndex << 37) |
-                (kIndex << 40) | (kIndex << 43) | (kIndex << 46) | (kIndex << 49) |
-                (kIndex << 52) | (kIndex << 55) | (kIndex << 58) | (kIndex << 61));
-
-        const uint64_t* blockPtr = reinterpret_cast<const uint64_t*>(latcData->data());
-        for (size_t i = 0; i < (kSizeToBe/8); ++i) {
-            REPORTER_ASSERT(reporter, blockPtr[i] == kConstColorEncoding);
-        }
-    }
-}
diff --git a/src/third_party/skia/tests/TileGridTest.cpp b/src/third_party/skia/tests/TileGridTest.cpp
deleted file mode 100644
index 16434ab..0000000
--- a/src/third_party/skia/tests/TileGridTest.cpp
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkCanvas.h"
-#include "SkPictureRecorder.h"
-#include "SkTileGrid.h"
-#include "Test.h"
-
-enum Tile {
-    kTopLeft_Tile = 0x1,
-    kTopRight_Tile = 0x2,
-    kBottomLeft_Tile = 0x4,
-    kBottomRight_Tile = 0x8,
-
-    kAll_Tile = kTopLeft_Tile | kTopRight_Tile | kBottomLeft_Tile | kBottomRight_Tile,
-};
-
-class MockCanvas : public SkCanvas {
-public:
-    MockCanvas(const SkBitmap& bm) : SkCanvas(bm) {}
-
-    virtual void drawRect(const SkRect& rect, const SkPaint&) {
-        // This capture occurs before quick reject.
-        fRects.push(rect);
-    }
-
-    SkTDArray<SkRect> fRects;
-};
-
-static void verifyTileHits(skiatest::Reporter* reporter, SkRect rect,
-                           uint32_t tileMask, int borderPixels = 0) {
-    SkTileGridFactory::TileGridInfo info;
-    info.fMargin.set(borderPixels, borderPixels);
-    info.fOffset.setZero();
-    info.fTileInterval.set(10 - 2 * borderPixels, 10 - 2 * borderPixels);
-    SkTileGrid grid(2, 2, info);
-    grid.insert(NULL, rect, false);
-    REPORTER_ASSERT(reporter, grid.tileCount(0, 0) ==
-                    ((tileMask & kTopLeft_Tile)? 1 : 0));
-    REPORTER_ASSERT(reporter, grid.tileCount(1, 0) ==
-                    ((tileMask & kTopRight_Tile)? 1 : 0));
-    REPORTER_ASSERT(reporter, grid.tileCount(0, 1) ==
-                    ((tileMask & kBottomLeft_Tile)? 1 : 0));
-    REPORTER_ASSERT(reporter, grid.tileCount(1, 1) ==
-                    ((tileMask & kBottomRight_Tile)? 1 : 0));
-}
-
-DEF_TEST(TileGrid_UnalignedQuery, reporter) {
-    // Use SkTileGridPicture to generate a SkTileGrid with a helper
-    SkTileGridFactory::TileGridInfo info;
-    info.fMargin.setEmpty();
-    info.fOffset.setZero();
-    info.fTileInterval.set(10, 10);
-    SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
-                                    SkIntToScalar(8), SkIntToScalar(8));
-    SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(11), SkIntToScalar(11),
-                                    SkIntToScalar(1), SkIntToScalar(1));
-    SkTileGridFactory factory(info);
-    SkPictureRecorder recorder;
-    SkCanvas* canvas = recorder.beginRecording(20, 20, &factory, 0);
-    SkPaint paint;
-    canvas->drawRect(rect1, paint);
-    canvas->drawRect(rect2, paint);
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
-
-    SkBitmap store;
-    store.allocN32Pixels(1, 1);
-
-    // Test parts of top-left tile
-    {
-        MockCanvas mockCanvas(store);
-        picture->playback(&mockCanvas);
-        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
-        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
-    }
-    {
-        MockCanvas mockCanvas(store);
-        mockCanvas.translate(-7.99f, -7.99f);
-        picture->playback(&mockCanvas);
-        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
-        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
-    }
-    // Corner overlap
-    {
-        MockCanvas mockCanvas(store);
-        mockCanvas.translate(-9.5f, -9.5f);
-        picture->playback(&mockCanvas);
-        REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count());
-        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
-        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[1]);
-    }
-    // Intersect bottom right tile, but does not overlap rect 2
-    {
-        MockCanvas mockCanvas(store);
-        mockCanvas.translate(-16.0f, -16.0f);
-        picture->playback(&mockCanvas);
-        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
-        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]);
-    }
-    // Out of bounds queries, snap to border tiles
-    {
-        MockCanvas mockCanvas(store);
-        mockCanvas.translate(2.0f, 0.0f);
-        picture->playback(&mockCanvas);
-        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
-        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
-    }
-    {
-        MockCanvas mockCanvas(store);
-        mockCanvas.translate(0.0f, 2.0f);
-        picture->playback(&mockCanvas);
-        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
-        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
-    }
-    {
-        MockCanvas mockCanvas(store);
-        mockCanvas.translate(-22.0f, -16.0f);
-        picture->playback(&mockCanvas);
-        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
-        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]);
-    }
-    {
-        MockCanvas mockCanvas(store);
-        mockCanvas.translate(-16.0f, -22.0f);
-        picture->playback(&mockCanvas);
-        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
-        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]);
-    }
-}
-
-DEF_TEST(TileGrid_OverlapOffsetQueryAlignment, reporter) {
-    // Use SkTileGridPicture to generate a SkTileGrid with a helper
-    SkTileGridFactory::TileGridInfo info;
-    info.fMargin.set(1, 1);
-    info.fOffset.set(-1, -1);
-    info.fTileInterval.set(8, 8);
-
-    // rect landing entirely in top left tile
-    SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
-                                    SkIntToScalar(1), SkIntToScalar(1));
-    // rect landing entirely in center tile
-    SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(12), SkIntToScalar(12),
-                                    SkIntToScalar(1), SkIntToScalar(1));
-    // rect landing entirely in bottomright tile
-    SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(19), SkIntToScalar(19),
-                                    SkIntToScalar(1), SkIntToScalar(1));
-    SkTileGridFactory factory(info);
-    SkPictureRecorder recorder;
-    SkCanvas* canvas = recorder.beginRecording(20, 20, &factory, 0);
-    SkPaint paint;
-    canvas->drawRect(rect1, paint);
-    canvas->drawRect(rect2, paint);
-    canvas->drawRect(rect3, paint);
-    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
-
-    SkBitmap tileBitmap;
-    tileBitmap.allocN32Pixels(10, 10);
-    SkBitmap moreThanATileBitmap;
-    moreThanATileBitmap.allocN32Pixels(11, 11);
-    SkBitmap tinyBitmap;
-    tinyBitmap.allocN32Pixels(2, 2);
-    // Test parts of top-left tile
-    {
-        // The offset should cancel the top and left borders of the top left tile
-        // So a look-up at interval 0-10 should be grid aligned,
-        MockCanvas mockCanvas(tileBitmap);
-        picture->playback(&mockCanvas);
-        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
-        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
-    }
-    {
-        // Encroaching border by one pixel
-        MockCanvas mockCanvas(moreThanATileBitmap);
-        picture->playback(&mockCanvas);
-        REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count());
-        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
-        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[1]);
-    }
-    {
-        // Tile stride is 8 (tileWidth - 2 * border pixels
-        // so translating by 8, should make query grid-aligned
-        // with middle tile.
-        MockCanvas mockCanvas(tileBitmap);
-        mockCanvas.translate(SkIntToScalar(-8), SkIntToScalar(-8));
-        picture->playback(&mockCanvas);
-        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
-        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]);
-    }
-    {
-        MockCanvas mockCanvas(tileBitmap);
-        mockCanvas.translate(-7.9f, -7.9f);
-        picture->playback(&mockCanvas);
-        REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count());
-        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
-        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[1]);
-    }
-    {
-        MockCanvas mockCanvas(tileBitmap);
-        mockCanvas.translate(-8.1f, -8.1f);
-        picture->playback(&mockCanvas);
-        REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count());
-        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]);
-        REPORTER_ASSERT(reporter, rect3 == mockCanvas.fRects[1]);
-    }
-    {
-        // Regression test for crbug.com/234688
-        // Once the 2x2 device region is inset by margin, it yields an empty
-        // adjusted region, sitting right on top of the tile boundary.
-        MockCanvas mockCanvas(tinyBitmap);
-        mockCanvas.translate(-8.0f, -8.0f);
-        picture->playback(&mockCanvas);
-        // This test passes by not asserting. We do not validate the rects recorded
-        // because the result is numerically unstable (floating point equality).
-        // The content of any one of the four tiles of the tilegrid would be a valid
-        // result since any bbox that covers the center point of the canvas will be
-        // recorded in all four tiles.
-    }
-}
-
-DEF_TEST(TileGrid, reporter) {
-    // Out of bounds
-    verifyTileHits(reporter, SkRect::MakeXYWH(30, 0, 1, 1),  0);
-    verifyTileHits(reporter, SkRect::MakeXYWH(0, 30, 1, 1),  0);
-    verifyTileHits(reporter, SkRect::MakeXYWH(-10, 0, 1, 1),  0);
-    verifyTileHits(reporter, SkRect::MakeXYWH(0, -10, 1, 1),  0);
-
-    // Dilation for AA consideration
-    verifyTileHits(reporter, SkRect::MakeXYWH(0, 0, 9, 9),  kTopLeft_Tile);
-    verifyTileHits(reporter, SkRect::MakeXYWH(0, 0, 10, 10),  kAll_Tile);
-    verifyTileHits(reporter, SkRect::MakeXYWH(9, 9, 1, 1),  kAll_Tile);
-    verifyTileHits(reporter, SkRect::MakeXYWH(10, 10, 1, 1),  kAll_Tile);
-    verifyTileHits(reporter, SkRect::MakeXYWH(11, 11, 1, 1),  kBottomRight_Tile);
-
-    // BorderPixels
-    verifyTileHits(reporter, SkRect::MakeXYWH(0, 0, 6, 6),  kTopLeft_Tile, 1);
-    verifyTileHits(reporter, SkRect::MakeXYWH(0, 0, 7, 7),  kAll_Tile, 1);
-    verifyTileHits(reporter, SkRect::MakeXYWH(9, 9, 1, 1),  kAll_Tile, 1);
-    verifyTileHits(reporter, SkRect::MakeXYWH(10, 10, 1, 1),  kBottomRight_Tile, 1);
-    verifyTileHits(reporter, SkRect::MakeXYWH(17, 17, 1, 1),  kBottomRight_Tile, 1);
-
-    // BBoxes that overlap tiles
-    verifyTileHits(reporter, SkRect::MakeXYWH(5, 5, 10, 1),  kTopLeft_Tile | kTopRight_Tile);
-    verifyTileHits(reporter, SkRect::MakeXYWH(5, 5, 1, 10),  kTopLeft_Tile |
-                   kBottomLeft_Tile);
-    verifyTileHits(reporter, SkRect::MakeXYWH(5, 5, 10, 10),  kAll_Tile);
-    verifyTileHits(reporter, SkRect::MakeXYWH(-10, -10, 40, 40),  kAll_Tile);
-}
diff --git a/src/third_party/skia/tests/Time.cpp b/src/third_party/skia/tests/Time.cpp
new file mode 100644
index 0000000..f6bf9ff
--- /dev/null
+++ b/src/third_party/skia/tests/Time.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTime.h"
+#include "Test.h"
+
+// Sanity checks for the GetDateTime function.
+DEF_TEST(Time_GetDateTime, r) {
+    SkTime::DateTime dateTime;
+    SkTime::GetDateTime(&dateTime);
+
+    // TODO(future generation): update these values.
+    const uint16_t kMinimumSaneYear = 1964;
+    const uint16_t kMaximumSaneYear = 2064;
+
+    if (dateTime.fYear < kMinimumSaneYear) {
+        ERRORF(r,
+               "SkTime::GetDateTime: %u (CurrentYear) < %u (MinimumSaneYear)",
+               static_cast<unsigned>(dateTime.fYear),
+               static_cast<unsigned>(kMinimumSaneYear));
+    }
+    if (dateTime.fYear > kMaximumSaneYear) {
+        ERRORF(r,
+               "SkTime::GetDateTime: %u (CurrentYear) > %u (MaximumSaneYear)",
+               static_cast<unsigned>(dateTime.fYear),
+               static_cast<unsigned>(kMaximumSaneYear));
+    }
+
+    REPORTER_ASSERT(r, dateTime.fMonth >= 1);
+    REPORTER_ASSERT(r, dateTime.fMonth <= 12);
+
+    REPORTER_ASSERT(r, dateTime.fDay >= 1);
+    REPORTER_ASSERT(r, dateTime.fDay <= 31);
+
+    REPORTER_ASSERT(r, dateTime.fHour <= 23);
+
+    REPORTER_ASSERT(r, dateTime.fMinute <= 59);
+
+    REPORTER_ASSERT(r, dateTime.fSecond <= 60);  // leap seconds are 23:59:60
+
+    // The westernmost timezone is -12:00.
+    // The easternmost timezone is +14:00.
+    REPORTER_ASSERT(r, SkTAbs(SkToInt(dateTime.fTimeZoneMinutes)) <= 14 * 60);
+
+    SkString timeStamp;
+    dateTime.toISO8601(&timeStamp);
+    REPORTER_ASSERT(r, timeStamp.size() > 0);
+    INFOF(r, "\nCurrent Time (ISO-8601 format): \"%s\"\n",
+          timeStamp.c_str());
+}
diff --git a/src/third_party/skia/tests/TopoSortTest.cpp b/src/third_party/skia/tests/TopoSortTest.cpp
new file mode 100644
index 0000000..9cee485
--- /dev/null
+++ b/src/third_party/skia/tests/TopoSortTest.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkRandom.h"
+#include "SkTTopoSort.h"
+#include "Test.h"
+
+#include "sk_tool_utils.h"
+
+typedef void (*CreateGraphPF)(SkTDArray<sk_tool_utils::TopoTestNode*>* graph);
+
+/* Simple diamond
+ *       3
+ *     /   \
+ *    1     2
+ *     \   /
+ *       0
+ */
+static void create_graph0(SkTDArray<sk_tool_utils::TopoTestNode*>* graph) {
+    sk_tool_utils::TopoTestNode::AllocNodes(graph, 4);
+
+    (*graph)[0]->dependsOn((*graph)[1]);
+    (*graph)[0]->dependsOn((*graph)[2]);
+    (*graph)[1]->dependsOn((*graph)[3]);
+    (*graph)[2]->dependsOn((*graph)[3]);
+}
+
+/* Simple chain
+ *     3
+ *     |
+ *     2
+ *     |
+ *     1
+ *     |
+ *     0
+ */
+static void create_graph1(SkTDArray<sk_tool_utils::TopoTestNode*>* graph) {
+    sk_tool_utils::TopoTestNode::AllocNodes(graph, 4);
+
+    (*graph)[0]->dependsOn((*graph)[1]);
+    (*graph)[1]->dependsOn((*graph)[2]);
+    (*graph)[2]->dependsOn((*graph)[3]);
+}
+
+/* Loop
+ *       2
+ *     /   \
+ *    0 --- 1
+ */
+static void create_graph2(SkTDArray<sk_tool_utils::TopoTestNode*>* graph) {
+    sk_tool_utils::TopoTestNode::AllocNodes(graph, 3);
+
+    (*graph)[0]->dependsOn((*graph)[1]);
+    (*graph)[1]->dependsOn((*graph)[2]);
+    (*graph)[2]->dependsOn((*graph)[0]);
+}
+
+/* Double diamond
+ *       6
+ *     /   \
+ *    4     5
+ *     \   /
+ *       3
+ *     /   \
+ *    1     2
+ *     \   /
+ *       0
+ */
+static void create_graph3(SkTDArray<sk_tool_utils::TopoTestNode*>* graph) {
+    sk_tool_utils::TopoTestNode::AllocNodes(graph, 7);
+
+    (*graph)[0]->dependsOn((*graph)[1]);
+    (*graph)[0]->dependsOn((*graph)[2]);
+    (*graph)[1]->dependsOn((*graph)[3]);
+    (*graph)[2]->dependsOn((*graph)[3]);
+
+    (*graph)[3]->dependsOn((*graph)[4]);
+    (*graph)[3]->dependsOn((*graph)[5]);
+    (*graph)[4]->dependsOn((*graph)[6]);
+    (*graph)[5]->dependsOn((*graph)[6]);
+}
+
+/* Two independent diamonds
+ *       3           7
+ *     /   \       /   \
+ *    1     2     5     6
+ *     \   /       \   /
+ *       0           4
+ */
+static void create_graph4(SkTDArray<sk_tool_utils::TopoTestNode*>* graph) {
+    sk_tool_utils::TopoTestNode::AllocNodes(graph, 8);
+
+    (*graph)[0]->dependsOn((*graph)[1]);
+    (*graph)[0]->dependsOn((*graph)[2]);
+    (*graph)[1]->dependsOn((*graph)[3]);
+    (*graph)[2]->dependsOn((*graph)[3]);
+
+    (*graph)[4]->dependsOn((*graph)[5]);
+    (*graph)[4]->dependsOn((*graph)[6]);
+    (*graph)[5]->dependsOn((*graph)[7]);
+    (*graph)[6]->dependsOn((*graph)[7]);
+}
+
+DEF_TEST(TopoSort, reporter) {
+    SkRandom rand;
+
+    struct {
+        CreateGraphPF fCreate;
+        bool          fExpectedResult;
+    } tests[] = {
+        { create_graph0, true  },
+        { create_graph1, true  },
+        { create_graph2, false },
+        { create_graph3, true  },
+        { create_graph4, true  },
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(tests); ++i) {
+        SkTDArray<sk_tool_utils::TopoTestNode*> graph;
+
+        (tests[i].fCreate)(&graph);
+
+        sk_tool_utils::TopoTestNode::Shuffle(&graph, &rand);
+
+        bool actualResult = SkTTopoSort<sk_tool_utils::TopoTestNode>(&graph);
+        REPORTER_ASSERT(reporter, actualResult == tests[i].fExpectedResult);
+
+        if (tests[i].fExpectedResult) {
+            for (int j = 0; j < graph.count(); ++j) {
+                REPORTER_ASSERT(reporter, graph[j]->check());
+            }
+        }
+
+        //SkDEBUGCODE(print(graph);)
+        sk_tool_utils::TopoTestNode::DeallocNodes(&graph);
+    }
+}
diff --git a/src/third_party/skia/tests/TraceMemoryDumpTest.cpp b/src/third_party/skia/tests/TraceMemoryDumpTest.cpp
new file mode 100644
index 0000000..a8cdbfe
--- /dev/null
+++ b/src/third_party/skia/tests/TraceMemoryDumpTest.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTraceMemoryDump.h"
+
+#include "Test.h"
+
+/*
+ * Build test for SkTraceMemoryDump.
+ */
+class TestSkTraceMemoryDump : public SkTraceMemoryDump {
+public:
+    TestSkTraceMemoryDump() { }
+    ~TestSkTraceMemoryDump() override { }
+
+    void dumpNumericValue(const char* dumpName, const char* valueName, const char* units,
+                          uint64_t value) override { }
+    void setMemoryBacking(const char* dumpName, const char* backingType,
+                          const char* backingObjectId) override { }
+    void setDiscardableMemoryBacking(
+        const char* dumpName,
+        const SkDiscardableMemory& discardableMemoryObject) override { }
+    LevelOfDetail getRequestedDetails() const override {
+        return SkTraceMemoryDump::kObjectsBreakdowns_LevelOfDetail;
+    }
+};
+
+DEF_TEST(SkTraceMemoryDump, reporter) {
+    TestSkTraceMemoryDump x;
+    x.dumpNumericValue("foobar", "size", "bytes", 42);
+    if (x.getRequestedDetails() == SkTraceMemoryDump::kObjectsBreakdowns_LevelOfDetail) {
+        x.dumpNumericValue("foobar/object1", "size", "bytes", 23);
+    }
+}
diff --git a/src/third_party/skia/tests/TransferPixelsTest.cpp b/src/third_party/skia/tests/TransferPixelsTest.cpp
new file mode 100755
index 0000000..5da292f
--- /dev/null
+++ b/src/third_party/skia/tests/TransferPixelsTest.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This is a GPU-backend specific test. It relies on static intializers to work
+
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContextFactory.h"
+#include "GrContextPriv.h"
+#include "GrGpu.h"
+#include "GrResourceProvider.h"
+#include "GrSurfaceProxy.h"
+#include "GrTexture.h"
+#include "GrTest.h"
+#include "SkGr.h"
+#include "SkSurface.h"
+#include "Test.h"
+
+using sk_gpu_test::GrContextFactory;
+
+void fill_transfer_data(int left, int top, int width, int height, int bufferWidth,
+                        GrColor* data) {
+
+    // build red-green gradient
+    for (int j = top; j < top + height; ++j) {
+        for (int i = left; i < left + width; ++i) {
+            unsigned int red = (unsigned int)(256.f*((i - left) / (float)width));
+            unsigned int green = (unsigned int)(256.f*((j - top) / (float)height));
+            data[i + j*bufferWidth] = GrColorPackRGBA(red - (red>>8),
+                                                      green - (green>>8), 0xff, 0xff);
+        }
+    }
+}
+
+bool does_full_buffer_contain_correct_values(GrColor* srcBuffer,
+                                             GrColor* dstBuffer,
+                                             int width,
+                                             int height,
+                                             int bufferWidth,
+                                             int bufferHeight,
+                                             GrSurfaceOrigin origin) {
+    GrColor* srcPtr = srcBuffer;
+    bool bottomUp = SkToBool(kBottomLeft_GrSurfaceOrigin == origin);
+    GrColor* dstPtr = bottomUp ? dstBuffer + bufferWidth*(bufferHeight-1) : dstBuffer;
+    int dstIncrement = bottomUp ? -bufferWidth : +bufferWidth;
+
+    for (int j = 0; j < height; ++j) {
+        for (int i = 0; i < width; ++i) {
+            if (srcPtr[i] != dstPtr[i]) {
+                return false;
+            }
+        }
+        srcPtr += bufferWidth;
+        dstPtr += dstIncrement;
+    }
+    return true;
+}
+
+void basic_transfer_test(skiatest::Reporter* reporter, GrContext* context, GrPixelConfig config,
+                         GrSurfaceOrigin origin, bool renderTarget) {
+    // set up the data
+    const int kTextureWidth = 16;
+    const int kTextureHeight = 16;
+    const int kBufferWidth = 20;
+    const int kBufferHeight = 16;
+    size_t rowBytes = kBufferWidth * sizeof(GrColor);
+    SkAutoTMalloc<GrColor> srcBuffer(kBufferWidth*kBufferHeight);
+    SkAutoTMalloc<GrColor> dstBuffer(kBufferWidth*kBufferHeight);
+
+    fill_transfer_data(0, 0, kTextureWidth, kTextureHeight, kBufferWidth, srcBuffer.get());
+
+    // create and fill transfer buffer
+    size_t size = rowBytes*kBufferHeight;
+    uint32_t bufferFlags = GrResourceProvider::kNoPendingIO_Flag;
+    sk_sp<GrBuffer> buffer(context->resourceProvider()->createBuffer(size,
+                                                                     kXferCpuToGpu_GrBufferType,
+                                                                     kDynamic_GrAccessPattern,
+                                                                     bufferFlags));
+    if (!buffer) {
+        return;
+    }
+
+    void* data = buffer->map();
+    memcpy(data, srcBuffer.get(), size);
+    buffer->unmap();
+
+    // create texture
+    GrSurfaceDesc desc;
+    desc.fConfig = config;
+    desc.fFlags = renderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
+    desc.fOrigin = origin;
+    desc.fWidth = kTextureWidth;
+    desc.fHeight = kTextureHeight;
+    desc.fSampleCnt = 0;
+    sk_sp<GrTexture> tex = context->resourceProvider()->createTexture(desc, SkBudgeted::kNo);
+
+    //////////////////////////
+    // transfer full data
+
+    bool result;
+    result = context->getGpu()->transferPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight,
+                                               config, buffer.get(), 0, rowBytes);
+    REPORTER_ASSERT(reporter, result);
+
+    memset(dstBuffer.get(), 0xCDCD, size);
+    result = context->getGpu()->readPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight, config,
+                                           dstBuffer.get(), rowBytes);
+    if (result) {
+        REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_values(srcBuffer,
+                                                                          dstBuffer,
+                                                                          kTextureWidth,
+                                                                          kTextureHeight,
+                                                                          kBufferWidth,
+                                                                          kBufferHeight,
+                                                                          origin));
+    }
+    
+    //////////////////////////
+    // transfer partial data
+
+    const int kLeft = 2;
+    const int kTop = 10;
+    const int kWidth = 10;
+    const int kHeight = 2;
+
+    // change color of subrectangle
+    fill_transfer_data(kLeft, kTop, kWidth, kHeight, kBufferWidth, srcBuffer.get());
+    data = buffer->map();
+    memcpy(data, srcBuffer.get(), size);
+    buffer->unmap();
+
+    size_t offset = sizeof(GrColor)*(kTop*kBufferWidth + kLeft);
+    result = context->getGpu()->transferPixels(tex.get(), kLeft, kTop, kWidth, kHeight, config,
+                                               buffer.get(), offset, rowBytes);
+    REPORTER_ASSERT(reporter, result);
+
+    memset(dstBuffer.get(), 0xCDCD, size);
+    result = context->getGpu()->readPixels(tex.get(), 0, 0, kTextureWidth, kTextureHeight, config,
+                                           dstBuffer.get(), rowBytes);
+    if (result) {
+        REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_values(srcBuffer,
+                                                                          dstBuffer,
+                                                                          kTextureWidth,
+                                                                          kTextureHeight,
+                                                                          kBufferWidth,
+                                                                          kBufferHeight,
+                                                                          origin));
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsTest, reporter, ctxInfo) {
+    // RGBA
+    basic_transfer_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig,
+                        kTopLeft_GrSurfaceOrigin, false);
+    basic_transfer_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig,
+                        kTopLeft_GrSurfaceOrigin, true);
+    basic_transfer_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig,
+                        kBottomLeft_GrSurfaceOrigin, false);
+    basic_transfer_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig,
+                        kBottomLeft_GrSurfaceOrigin, true);
+
+    // BGRA
+    basic_transfer_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig,
+                        kTopLeft_GrSurfaceOrigin, false);
+    basic_transfer_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig,
+                        kTopLeft_GrSurfaceOrigin, true);
+    basic_transfer_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig,
+                        kBottomLeft_GrSurfaceOrigin, false);
+    basic_transfer_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig,
+                        kBottomLeft_GrSurfaceOrigin, true);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/TypefaceTest.cpp b/src/third_party/skia/tests/TypefaceTest.cpp
index bb5d361..1da94bb 100644
--- a/src/third_party/skia/tests/TypefaceTest.cpp
+++ b/src/third_party/skia/tests/TypefaceTest.cpp
@@ -5,13 +5,171 @@
  * found in the LICENSE file.
  */
 
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkData.h"
+#include "SkFixed.h"
+#include "SkFontMgr.h"
+#include "SkMakeUnique.h"
+#include "SkOTTable_OS_2.h"
+#include "SkSFNTHeader.h"
+#include "SkStream.h"
+#include "SkRefCnt.h"
 #include "SkTypeface.h"
+#include "SkTypefaceCache.h"
+#include "Resources.h"
 #include "Test.h"
 
+#include <memory>
+
+static void TypefaceStyle_test(skiatest::Reporter* reporter,
+                               uint16_t weight, uint16_t width, SkData* data)
+{
+    sk_sp<SkData> dataCopy;
+    if (!data->unique()) {
+        dataCopy = SkData::MakeWithCopy(data->data(), data->size());
+        data = dataCopy.get();
+    }
+    SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(data->writable_data());
+
+    SkSFNTHeader::TableDirectoryEntry* tableEntry =
+        SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
+    SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr;
+    int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
+    for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
+        if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) {
+            os2TableEntry = tableEntry + tableEntryIndex;
+            break;
+        }
+    }
+    SkASSERT_RELEASE(os2TableEntry);
+
+    size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset);
+    SkOTTableOS2_V0* os2Table = SkTAddOffset<SkOTTableOS2_V0>(sfntHeader, os2TableOffset);
+    os2Table->usWeightClass.value = SkEndian_SwapBE16(weight);
+    using WidthType = SkOTTableOS2_V0::WidthClass::Value;
+    os2Table->usWidthClass.value = static_cast<WidthType>(SkEndian_SwapBE16(width));
+
+    sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromStream(new SkMemoryStream(sk_ref_sp(data))));
+    SkASSERT_RELEASE(newTypeface);
+
+    SkFontStyle newStyle = newTypeface->fontStyle();
+
+    //printf("%d, %f\n", weight, (newStyle.weight() - (float)0x7FFF) / (float)0x7FFF);
+    //printf("%d, %f\n", width , (newStyle.width()  - (float)0x7F)   / (float)0x7F);
+    //printf("%d, %d\n", weight, newStyle.weight());
+    //printf("%d, %d\n", width , newStyle.width());
+
+    // Some back-ends (CG, GDI, DW) support OS/2 version A which uses 0 - 10 (but all differently).
+    REPORTER_ASSERT(reporter,
+                    newStyle.weight() == weight ||
+                    (weight <=   10 && newStyle.weight() == 100 * weight) ||
+                    (weight ==    4 && newStyle.weight() == 350) ||  // GDI weirdness
+                    (weight ==    5 && newStyle.weight() == 400) ||  // GDI weirdness
+                    (weight ==    0 && newStyle.weight() ==   1) ||  // DW weirdness
+                    (weight == 1000 && newStyle.weight() == 999)     // DW weirdness
+    );
+
+    // Some back-ends (GDI) don't support width, ensure these always report 'medium'.
+    REPORTER_ASSERT(reporter,
+                    newStyle.width() == width ||
+                    newStyle.width() == 5);
+}
+DEF_TEST(TypefaceStyle, reporter) {
+    std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("/fonts/Em.ttf"));
+    if (!stream) {
+        REPORT_FAILURE(reporter, "/fonts/Em.ttf", SkString("Cannot load resource"));
+        return;
+    }
+    sk_sp<SkData> data(SkData::MakeFromStream(stream.get(), stream->getLength()));
+
+    using SkFS = SkFontStyle;
+    for (int weight = SkFS::kInvisible_Weight; weight <= SkFS::kExtraBlack_Weight; ++weight) {
+        TypefaceStyle_test(reporter, weight, 5, data.get());
+    }
+    for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltraExpanded_Width; ++width) {
+        TypefaceStyle_test(reporter, 400, width, data.get());
+    }
+}
+
+DEF_TEST(TypefaceAxes, reporter) {
+    std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("/fonts/Distortable.ttf"));
+    if (!distortable) {
+        REPORT_FAILURE(reporter, "distortable", SkString());
+        return;
+    }
+    constexpr int numberOfAxesInDistortable = 1;
+
+    sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
+    // The position may be over specified. If there are multiple values for a given axis,
+    // ensure the last one since that's what css-fonts-4 requires.
+    const SkFontArguments::VariationPosition::Coordinate position[] = {
+        { SkSetFourByteTag('w','g','h','t'), 1.618033988749895f },
+        { SkSetFourByteTag('w','g','h','t'), SK_ScalarSqrt2 },
+    };
+    SkFontArguments params;
+    params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
+    // TODO: if axes are set and the back-end doesn't support them, should we create the typeface?
+    sk_sp<SkTypeface> typeface(fm->createFromStream(distortable.release(), params));
+
+    int count = typeface->getVariationDesignPosition(nullptr, 0);
+    if (count == -1) {
+        return;
+    }
+    REPORTER_ASSERT(reporter, count == numberOfAxesInDistortable);
+
+    SkFontArguments::VariationPosition::Coordinate positionRead[numberOfAxesInDistortable];
+    count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
+    REPORTER_ASSERT(reporter, count == SK_ARRAY_COUNT(positionRead));
+
+    REPORTER_ASSERT(reporter, positionRead[0].axis == position[1].axis);
+
+    // Convert to fixed for "almost equal".
+    SkFixed fixedRead = SkScalarToFixed(positionRead[0].value);
+    SkFixed fixedOriginal = SkScalarToFixed(position[1].value);
+    REPORTER_ASSERT(reporter, fixedRead == fixedOriginal);
+}
+
+DEF_TEST(TypefaceVariationIndex, reporter) {
+    std::unique_ptr<SkStreamAsset> distortable(GetResourceAsStream("/fonts/Distortable.ttf"));
+    if (!distortable) {
+        REPORT_FAILURE(reporter, "distortable", SkString());
+        return;
+    }
+
+    sk_sp<SkFontMgr> fm = SkFontMgr::RefDefault();
+    SkFontArguments params;
+    // The first named variation position in Distortable is 'Thin'.
+    params.setCollectionIndex(0x00010000);
+    sk_sp<SkTypeface> typeface(fm->createFromStream(distortable.release(), params));
+    if (!typeface) {
+        // FreeType is the only weird thing that supports this, Skia just needs to make sure if it
+        // gets one of these things make sense.
+        return;
+    }
+
+    int count = typeface->getVariationDesignPosition(nullptr, 0);
+    if (!(count == 1)) {
+        REPORT_FAILURE(reporter, "count == 1", SkString());
+        return;
+    }
+
+    SkFontArguments::VariationPosition::Coordinate positionRead[1];
+    count = typeface->getVariationDesignPosition(positionRead, SK_ARRAY_COUNT(positionRead));
+    if (count == -1) {
+        return;
+    }
+    if (!(count == 1)) {
+        REPORT_FAILURE(reporter, "count == 1", SkString());
+        return;
+    }
+    REPORTER_ASSERT(reporter, positionRead[0].axis == SkSetFourByteTag('w','g','h','t'));
+    REPORTER_ASSERT(reporter, positionRead[0].value == 0.5);
+}
+
 DEF_TEST(Typeface, reporter) {
 
-    SkAutoTUnref<SkTypeface> t1(SkTypeface::CreateFromName(NULL, SkTypeface::kNormal));
-    SkAutoTUnref<SkTypeface> t2(SkTypeface::RefDefault(SkTypeface::kNormal));
+    sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));
+    sk_sp<SkTypeface> t2(SkTypeface::MakeDefault(SkTypeface::kNormal));
 
     REPORTER_ASSERT(reporter, SkTypeface::Equal(t1.get(), t2.get()));
     REPORTER_ASSERT(reporter, SkTypeface::Equal(0, t1.get()));
@@ -20,7 +178,81 @@
     REPORTER_ASSERT(reporter, SkTypeface::Equal(t2.get(), 0));
 
 #ifdef SK_BUILD_FOR_ANDROID
-    SkAutoTUnref<SkTypeface> t3(SkTypeface::CreateFromName("non-existent-font", SkTypeface::kNormal));
-    REPORTER_ASSERT(reporter, NULL == t3.get());
+    sk_sp<SkTypeface> t3(SkTypeface::MakeFromName("non-existent-font", SkFontStyle()));
+    REPORTER_ASSERT(reporter, nullptr == t3);
 #endif
 }
+
+namespace {
+
+class SkEmptyTypeface : public SkTypeface {
+public:
+    static sk_sp<SkTypeface> Create() { return sk_sp<SkTypeface>(new SkEmptyTypeface()); }
+protected:
+    SkEmptyTypeface() : SkTypeface(SkFontStyle(), true) { }
+
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override { return nullptr; }
+    SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
+                                           const SkDescriptor*) const override {
+        return nullptr;
+    }
+    void onFilterRec(SkScalerContextRec*) const override { }
+    std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override {
+        return nullptr;
+    }
+    void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { }
+    virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
+                                uint16_t glyphs[], int glyphCount) const override {
+        SK_ABORT("unimplemented");
+        return 0;
+    }
+    int onCountGlyphs() const override { return 0; }
+    int onGetUPEM() const override { return 0; }
+    void onGetFamilyName(SkString* familyName) const override { familyName->reset(); }
+    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override {
+        SK_ABORT("unimplemented");
+        return nullptr;
+    }
+    int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
+                                     int coordinateCount) const override
+    {
+        return 0;
+    }
+    int onGetTableTags(SkFontTableTag tags[]) const override { return 0; }
+    size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { return 0; }
+};
+
+}
+
+static bool count_proc(SkTypeface* face, void* ctx) {
+    int* count = static_cast<int*>(ctx);
+    *count = *count + 1;
+    return false;
+}
+static int count(skiatest::Reporter* reporter, const SkTypefaceCache& cache) {
+    int count = 0;
+    SkTypeface* none = cache.findByProcAndRef(count_proc, &count);
+    REPORTER_ASSERT(reporter, none == nullptr);
+    return count;
+}
+
+DEF_TEST(TypefaceCache, reporter) {
+    sk_sp<SkTypeface> t1(SkEmptyTypeface::Create());
+    {
+        SkTypefaceCache cache;
+        REPORTER_ASSERT(reporter, count(reporter, cache) == 0);
+        {
+            sk_sp<SkTypeface> t0(SkEmptyTypeface::Create());
+            cache.add(t0.get());
+            REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
+            cache.add(t1.get());
+            REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
+            cache.purgeAll();
+            REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
+        }
+        REPORTER_ASSERT(reporter, count(reporter, cache) == 2);
+        cache.purgeAll();
+        REPORTER_ASSERT(reporter, count(reporter, cache) == 1);
+    }
+    REPORTER_ASSERT(reporter, t1->unique());
+}
diff --git a/src/third_party/skia/tests/UtilsTest.cpp b/src/third_party/skia/tests/UtilsTest.cpp
index 2c84c95..ed861c6 100644
--- a/src/third_party/skia/tests/UtilsTest.cpp
+++ b/src/third_party/skia/tests/UtilsTest.cpp
@@ -14,7 +14,7 @@
 
 class RefClass : public SkRefCnt {
 public:
-    SK_DECLARE_INST_COUNT(RefClass)
+
 
     RefClass(int n) : fN(n) {}
     int get() const { return fN; }
@@ -27,33 +27,33 @@
 
 static void test_autounref(skiatest::Reporter* reporter) {
     RefClass obj(0);
-    REPORTER_ASSERT(reporter, 1 == obj.getRefCnt());
+    REPORTER_ASSERT(reporter, obj.unique());
 
-    SkAutoTUnref<RefClass> tmp(&obj);
+    sk_sp<RefClass> tmp(&obj);
     REPORTER_ASSERT(reporter, &obj == tmp.get());
-    REPORTER_ASSERT(reporter, 1 == obj.getRefCnt());
+    REPORTER_ASSERT(reporter, obj.unique());
 
-    REPORTER_ASSERT(reporter, &obj == tmp.detach());
-    REPORTER_ASSERT(reporter, 1 == obj.getRefCnt());
-    REPORTER_ASSERT(reporter, NULL == tmp.detach());
-    REPORTER_ASSERT(reporter, NULL == tmp.get());
+    REPORTER_ASSERT(reporter, &obj == tmp.release());
+    REPORTER_ASSERT(reporter, obj.unique());
+    REPORTER_ASSERT(reporter, nullptr == tmp.release());
+    REPORTER_ASSERT(reporter, nullptr == tmp.get());
 
     obj.ref();
-    REPORTER_ASSERT(reporter, 2 == obj.getRefCnt());
+    REPORTER_ASSERT(reporter, !obj.unique());
     {
-        SkAutoTUnref<RefClass> tmp2(&obj);
+        sk_sp<RefClass> tmp2(&obj);
     }
-    REPORTER_ASSERT(reporter, 1 == obj.getRefCnt());
+    REPORTER_ASSERT(reporter, obj.unique());
 }
 
 static void test_autostarray(skiatest::Reporter* reporter) {
     RefClass obj0(0);
     RefClass obj1(1);
-    REPORTER_ASSERT(reporter, 1 == obj0.getRefCnt());
-    REPORTER_ASSERT(reporter, 1 == obj1.getRefCnt());
+    REPORTER_ASSERT(reporter, obj0.unique());
+    REPORTER_ASSERT(reporter, obj1.unique());
 
     {
-        SkAutoSTArray<2, SkAutoTUnref<RefClass> > tmp;
+        SkAutoSTArray<2, sk_sp<RefClass> > tmp;
         REPORTER_ASSERT(reporter, 0 == tmp.count());
 
         tmp.reset(0);   // test out reset(0) when already at 0
@@ -61,14 +61,14 @@
         REPORTER_ASSERT(reporter, 4 == tmp.count());
         tmp[0].reset(SkRef(&obj0));
         tmp[1].reset(SkRef(&obj1));
-        REPORTER_ASSERT(reporter, 2 == obj0.getRefCnt());
-        REPORTER_ASSERT(reporter, 2 == obj1.getRefCnt());
+        REPORTER_ASSERT(reporter, !obj0.unique());
+        REPORTER_ASSERT(reporter, !obj1.unique());
 
         // test out reset with data in the array (and a new allocation)
         tmp.reset(0);
         REPORTER_ASSERT(reporter, 0 == tmp.count());
-        REPORTER_ASSERT(reporter, 1 == obj0.getRefCnt());
-        REPORTER_ASSERT(reporter, 1 == obj1.getRefCnt());
+        REPORTER_ASSERT(reporter, obj0.unique());
+        REPORTER_ASSERT(reporter, obj1.unique());
 
         tmp.reset(2);   // this should use the preexisting allocation
         REPORTER_ASSERT(reporter, 2 == tmp.count());
@@ -77,42 +77,42 @@
     }
 
     // test out destructor with data in the array (and using existing allocation)
-    REPORTER_ASSERT(reporter, 1 == obj0.getRefCnt());
-    REPORTER_ASSERT(reporter, 1 == obj1.getRefCnt());
+    REPORTER_ASSERT(reporter, obj0.unique());
+    REPORTER_ASSERT(reporter, obj1.unique());
 
     {
         // test out allocating ctor (this should allocate new memory)
-        SkAutoSTArray<2, SkAutoTUnref<RefClass> > tmp(4);
+        SkAutoSTArray<2, sk_sp<RefClass> > tmp(4);
         REPORTER_ASSERT(reporter, 4 == tmp.count());
 
         tmp[0].reset(SkRef(&obj0));
         tmp[1].reset(SkRef(&obj1));
-        REPORTER_ASSERT(reporter, 2 == obj0.getRefCnt());
-        REPORTER_ASSERT(reporter, 2 == obj1.getRefCnt());
+        REPORTER_ASSERT(reporter, !obj0.unique());
+        REPORTER_ASSERT(reporter, !obj1.unique());
 
         // Test out resut with data in the array and malloced storage
         tmp.reset(0);
-        REPORTER_ASSERT(reporter, 1 == obj0.getRefCnt());
-        REPORTER_ASSERT(reporter, 1 == obj1.getRefCnt());
+        REPORTER_ASSERT(reporter, obj0.unique());
+        REPORTER_ASSERT(reporter, obj1.unique());
 
         tmp.reset(2);   // this should use the preexisting storage
         tmp[0].reset(SkRef(&obj0));
         tmp[1].reset(SkRef(&obj1));
-        REPORTER_ASSERT(reporter, 2 == obj0.getRefCnt());
-        REPORTER_ASSERT(reporter, 2 == obj1.getRefCnt());
+        REPORTER_ASSERT(reporter, !obj0.unique());
+        REPORTER_ASSERT(reporter, !obj1.unique());
 
         tmp.reset(4);   // this should force a new malloc
-        REPORTER_ASSERT(reporter, 1 == obj0.getRefCnt());
-        REPORTER_ASSERT(reporter, 1 == obj1.getRefCnt());
+        REPORTER_ASSERT(reporter, obj0.unique());
+        REPORTER_ASSERT(reporter, obj1.unique());
 
         tmp[0].reset(SkRef(&obj0));
         tmp[1].reset(SkRef(&obj1));
-        REPORTER_ASSERT(reporter, 2 == obj0.getRefCnt());
-        REPORTER_ASSERT(reporter, 2 == obj1.getRefCnt());
+        REPORTER_ASSERT(reporter, !obj0.unique());
+        REPORTER_ASSERT(reporter, !obj1.unique());
     }
 
-    REPORTER_ASSERT(reporter, 1 == obj0.getRefCnt());
-    REPORTER_ASSERT(reporter, 1 == obj1.getRefCnt());
+    REPORTER_ASSERT(reporter, obj0.unique());
+    REPORTER_ASSERT(reporter, obj1.unique());
 }
 
 /////////////////////////////////////////////////////////////////////////////
@@ -219,3 +219,64 @@
     test_autounref(reporter);
     test_autostarray(reporter);
 }
+
+#define ASCII_BYTE         "X"
+#define CONTINUATION_BYTE  "\x80"
+#define LEADING_TWO_BYTE   "\xC4"
+#define LEADING_THREE_BYTE "\xE0"
+#define LEADING_FOUR_BYTE  "\xF0"
+#define INVALID_BYTE       "\xFC"
+static bool valid_utf8(const char* p, size_t l) {
+    return SkUTF8_CountUnicharsWithError(p, l) >= 0;
+}
+DEF_TEST(Utils_UTF8_ValidLength, r) {
+    const char* goodTestcases[] = {
+        "",
+        ASCII_BYTE,
+        ASCII_BYTE ASCII_BYTE,
+        LEADING_TWO_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE LEADING_TWO_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE ASCII_BYTE LEADING_TWO_BYTE CONTINUATION_BYTE,
+        LEADING_THREE_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE LEADING_THREE_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE ASCII_BYTE LEADING_THREE_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+        LEADING_FOUR_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE LEADING_FOUR_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE ASCII_BYTE LEADING_FOUR_BYTE CONTINUATION_BYTE CONTINUATION_BYTE
+            CONTINUATION_BYTE,
+    };
+    for (const char* testcase : goodTestcases) {
+        REPORTER_ASSERT(r, valid_utf8(testcase, strlen(testcase)));
+    }
+    const char* badTestcases[] = {
+        INVALID_BYTE,
+        INVALID_BYTE CONTINUATION_BYTE,
+        INVALID_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+        INVALID_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+        LEADING_TWO_BYTE,
+        CONTINUATION_BYTE,
+        CONTINUATION_BYTE CONTINUATION_BYTE,
+        LEADING_THREE_BYTE CONTINUATION_BYTE,
+        CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+        LEADING_FOUR_BYTE CONTINUATION_BYTE,
+        CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+
+        ASCII_BYTE INVALID_BYTE,
+        ASCII_BYTE INVALID_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE INVALID_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE INVALID_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE LEADING_TWO_BYTE,
+        ASCII_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE LEADING_THREE_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE LEADING_FOUR_BYTE CONTINUATION_BYTE,
+        ASCII_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE,
+
+        // LEADING_FOUR_BYTE LEADING_TWO_BYTE CONTINUATION_BYTE,
+    };
+    for (const char* testcase : badTestcases) {
+        REPORTER_ASSERT(r, !valid_utf8(testcase, strlen(testcase)));
+    }
+
+}
diff --git a/src/third_party/skia/tests/VerticesTest.cpp b/src/third_party/skia/tests/VerticesTest.cpp
new file mode 100644
index 0000000..399aba2
--- /dev/null
+++ b/src/third_party/skia/tests/VerticesTest.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkVertices.h"
+#include "Test.h"
+
+static bool equal(const SkVertices* v0, const SkVertices* v1) {
+    if (v0->mode() != v1->mode()) {
+        return false;
+    }
+    if (v0->vertexCount() != v1->vertexCount()) {
+        return false;
+    }
+    if (v0->indexCount() != v1->indexCount()) {
+        return false;
+    }
+
+    if (!!v0->texCoords() != !!v1->texCoords()) {
+        return false;
+    }
+    if (!!v0->colors() != !!v1->colors()) {
+        return false;
+    }
+
+    for (int i = 0; i < v0->vertexCount(); ++i) {
+        if (v0->positions()[i] != v1->positions()[i]) {
+            return false;
+        }
+        if (v0->texCoords()) {
+            if (v0->texCoords()[i] != v1->texCoords()[i]) {
+                return false;
+            }
+        }
+        if (v0->colors()) {
+            if (v0->colors()[i] != v1->colors()[i]) {
+                return false;
+            }
+        }
+    }
+    for (int i = 0; i < v0->indexCount(); ++i) {
+        if (v0->indices()[i] != v1->indices()[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+DEF_TEST(Vertices, reporter) {
+    int vCount = 5;
+    int iCount = 9; // odd value exercises padding logic in encode()
+
+    const uint32_t texFlags[] = { 0, SkVertices::kHasTexCoords_BuilderFlag };
+    const uint32_t colFlags[] = { 0, SkVertices::kHasColors_BuilderFlag };
+    for (auto texF : texFlags) {
+        for (auto colF : colFlags) {
+            uint32_t flags = texF | colF;
+
+            SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, vCount, iCount, flags);
+
+            for (int i = 0; i < vCount; ++i) {
+                float x = (float)i;
+                builder.positions()[i].set(x, 1);
+                if (builder.texCoords()) {
+                    builder.texCoords()[i].set(x, 2);
+                }
+                if (builder.colors()) {
+                    builder.colors()[i] = SkColorSetARGB(0xFF, i, 0x80, 0);
+                }
+            }
+            for (int i = 0; i < builder.indexCount(); ++i) {
+                builder.indices()[i] = i % vCount;
+            }
+
+            sk_sp<SkVertices> v0 = builder.detach();
+            sk_sp<SkData> data = v0->encode();
+            sk_sp<SkVertices> v1 = SkVertices::Decode(data->data(), data->size());
+
+            REPORTER_ASSERT(reporter, v0->uniqueID() != 0);
+            REPORTER_ASSERT(reporter, v1->uniqueID() != 0);
+            REPORTER_ASSERT(reporter, v0->uniqueID() != v1->uniqueID());
+            REPORTER_ASSERT(reporter, equal(v0.get(), v1.get()));
+        }
+    }
+}
diff --git a/src/third_party/skia/tests/VkClearTests.cpp b/src/third_party/skia/tests/VkClearTests.cpp
new file mode 100644
index 0000000..c797ef2
--- /dev/null
+++ b/src/third_party/skia/tests/VkClearTests.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This is a GPU-backend specific test. It relies on static intializers to work
+
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU && defined(SK_VULKAN)
+
+#include "GrContextFactory.h"
+#include "GrTest.h"
+#include "Test.h"
+#include "vk/GrVkGpu.h"
+
+using sk_gpu_test::GrContextFactory;
+
+bool does_full_buffer_contain_correct_color(GrColor* buffer,
+                                            GrColor clearColor,
+                                            GrPixelConfig config,
+                                            int width,
+                                            int height) {
+    GrColor matchColor;
+    if (kRGBA_8888_GrPixelConfig == config) {
+        matchColor = clearColor;
+    } else if (kBGRA_8888_GrPixelConfig) {
+        // Hack to flip the R, B componets in the GrColor so that the comparrison will work below
+        matchColor = GrColorPackRGBA(GrColorUnpackB(clearColor),
+                                     GrColorUnpackG(clearColor),
+                                     GrColorUnpackR(clearColor),
+                                     GrColorUnpackA(clearColor));
+    } else {
+        // currently only supporting rgba_8888 and bgra_8888
+        return false;
+    }
+
+    for (int j = 0; j < height; ++j) {
+        for (int i = 0; i < width; ++i) {
+            if (buffer[j * width + i] != matchColor) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+void basic_clear_test(skiatest::Reporter* reporter, GrContext* context, GrPixelConfig config) {
+#if 0
+    GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu());
+    SkAutoTMalloc<GrColor> buffer(25);
+
+    GrSurfaceDesc surfDesc;
+    surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    surfDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
+    surfDesc.fWidth = 5;
+    surfDesc.fHeight = 5;
+    surfDesc.fConfig = config;
+    surfDesc.fSampleCnt = 0;
+    GrTexture* tex = gpu->createTexture(surfDesc, SkBudgeted::kNo);
+    SkASSERT(tex);
+    SkASSERT(tex->asRenderTarget());
+    SkIRect rect = SkIRect::MakeWH(5, 5);
+
+    gpu->clear(rect, GrColor_TRANSPARENT_BLACK, tex->asRenderTarget());
+
+    gpu->readPixels(tex, 0, 0, 5, 5, config, (void*)buffer.get(), 0);
+
+    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(),
+                                                                     GrColor_TRANSPARENT_BLACK,
+                                                                     config,
+                                                                     5,
+                                                                     5));
+
+    gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget());
+
+    gpu->readPixels(tex, 0, 0, 5, 5, config, (void*)buffer.get(), 0);
+
+    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(),
+                                                                     GrColor_WHITE,
+                                                                     config,
+                                                                     5,
+                                                                     5));
+
+    GrColor myColor = GrColorPackRGBA(0xFF, 0x7F, 0x40, 0x20);
+
+    gpu->clear(rect, myColor, tex->asRenderTarget());
+
+    gpu->readPixels(tex, 0, 0, 5, 5, config, (void*)buffer.get(), 0);
+
+    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(),
+                                                                     myColor,
+                                                                     config,
+                                                                     5,
+                                                                     5));
+#endif
+}
+
+void sub_clear_test(skiatest::Reporter* reporter, GrContext* context, GrPixelConfig config) {
+#if 0
+    const int width = 10;
+    const int height = 10;
+    const int subWidth = width/2;
+    const int subHeight = height/2;
+    GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu());
+    SkAutoTMalloc<GrColor> buffer(width * height);
+    SkAutoTMalloc<GrColor> subBuffer(subWidth * subHeight);
+
+    GrSurfaceDesc surfDesc;
+    surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    surfDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
+    surfDesc.fWidth = width;
+    surfDesc.fHeight = height;
+    surfDesc.fConfig = config;
+    surfDesc.fSampleCnt = 0;
+    GrTexture* tex = gpu->createTexture(surfDesc, SkBudgeted::kNo);
+    SkASSERT(tex);
+    SkASSERT(tex->asRenderTarget());
+
+    SkIRect fullRect = SkIRect::MakeWH(10, 10);
+    gpu->clear(fullRect, GrColor_TRANSPARENT_BLACK, tex->asRenderTarget());
+
+    gpu->readPixels(tex, 0, 0, width, height, config, (void*)buffer.get(), 0);
+
+    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(),
+                                                                     GrColor_TRANSPARENT_BLACK,
+                                                                     config,
+                                                                     width,
+                                                                     height));
+    SkIRect rect;
+    rect = SkIRect::MakeXYWH(0, 0, subWidth, subHeight);
+    gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget());
+    rect = SkIRect::MakeXYWH(subWidth, 0, subWidth, subHeight);
+    gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget());
+    rect = SkIRect::MakeXYWH(0, subHeight, subWidth, subHeight);
+    gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget());
+
+    // Should fail since bottom right sub area has not been cleared to white
+    gpu->readPixels(tex, 0, 0, width, height, config, (void*)buffer.get(), 0);
+    REPORTER_ASSERT(reporter, !does_full_buffer_contain_correct_color(buffer.get(),
+                                                                      GrColor_WHITE,
+                                                                      config,
+                                                                      width,
+                                                                      height));
+
+    rect = SkIRect::MakeXYWH(subWidth, subHeight, subWidth, subHeight);
+    gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget());
+
+    gpu->readPixels(tex, 0, 0, width, height, config, (void*)buffer.get(), 0);
+    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(),
+                                                                     GrColor_WHITE,
+                                                                     config,
+                                                                     width,
+                                                                     height));
+
+    // Try different colors and that each sub area has correct color
+    GrColor subColor1 = GrColorPackRGBA(0xFF, 0x00, 0x00, 0xFF);
+    GrColor subColor2 = GrColorPackRGBA(0x00, 0xFF, 0x00, 0xFF);
+    GrColor subColor3 = GrColorPackRGBA(0x00, 0x00, 0xFF, 0xFF);
+    GrColor subColor4 = GrColorPackRGBA(0xFF, 0xFF, 0x00, 0xFF);
+
+    rect = SkIRect::MakeXYWH(0, 0, subWidth, subHeight);
+    gpu->clear(rect, subColor1, tex->asRenderTarget());
+    rect = SkIRect::MakeXYWH(subWidth, 0, subWidth, subHeight);
+    gpu->clear(rect, subColor2, tex->asRenderTarget());
+    rect = SkIRect::MakeXYWH(0, subHeight, subWidth, subHeight);
+    gpu->clear(rect, subColor3, tex->asRenderTarget());
+    rect = SkIRect::MakeXYWH(subWidth, subHeight, subWidth, subHeight);
+    gpu->clear(rect, subColor4, tex->asRenderTarget());
+
+    gpu->readPixels(tex, 0, 0, subWidth, subHeight, config, (void*)subBuffer.get(), 0);
+    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(),
+                                                                     subColor1,
+                                                                     config,
+                                                                     subWidth,
+                                                                     subHeight));
+    gpu->readPixels(tex, subWidth, 0, subWidth, subHeight, config, (void*)subBuffer.get(), 0);
+    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(),
+                                                                     subColor2,
+                                                                     config,
+                                                                     subWidth,
+                                                                     subHeight));
+    gpu->readPixels(tex, 0, subHeight, subWidth, subHeight, config, (void*)subBuffer.get(), 0);
+    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(),
+                                                                     subColor3,
+                                                                     config,
+                                                                     subWidth,
+                                                                     subHeight));
+    gpu->readPixels(tex, subWidth, subHeight, subWidth, subHeight,
+                    config, (void*)subBuffer.get(), 0);
+    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(),
+                                                                     subColor4,
+                                                                     config,
+                                                                     subWidth,
+                                                                     subHeight));
+#endif
+}
+
+DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkClearTests, reporter, ctxInfo) {
+    basic_clear_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig);
+    basic_clear_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig);
+    sub_clear_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig);
+    sub_clear_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/VkHeapTests.cpp b/src/third_party/skia/tests/VkHeapTests.cpp
new file mode 100644
index 0000000..fd5f88a
--- /dev/null
+++ b/src/third_party/skia/tests/VkHeapTests.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This is a GPU-backend specific test. It relies on static intializers to work
+
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU && defined(SK_VULKAN)
+
+#include "GrContextFactory.h"
+#include "GrTest.h"
+#include "Test.h"
+#include "vk/GrVkGpu.h"
+
+using sk_gpu_test::GrContextFactory;
+
+void subheap_test(skiatest::Reporter* reporter, GrContext* context) {
+    GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu());
+
+    // memtype doesn't matter, we're just testing the suballocation algorithm so we'll use 0
+    GrVkSubHeap heap(gpu, 0, 0, 64 * 1024, 32);
+    GrVkAlloc alloc0, alloc1, alloc2, alloc3;
+    // test full allocation and free
+    REPORTER_ASSERT(reporter, heap.alloc(64 * 1024, &alloc0));
+    REPORTER_ASSERT(reporter, alloc0.fOffset == 0);
+    REPORTER_ASSERT(reporter, alloc0.fSize == 64 * 1024);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 0 && heap.largestBlockSize() == 0);
+    heap.free(alloc0);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 64*1024 && heap.largestBlockSize() == 64 * 1024);
+
+    // now let's suballoc some memory
+    REPORTER_ASSERT(reporter, heap.alloc(16 * 1024, &alloc0));
+    REPORTER_ASSERT(reporter, heap.alloc(23 * 1024, &alloc1));
+    REPORTER_ASSERT(reporter, heap.alloc(18 * 1024, &alloc2));
+    REPORTER_ASSERT(reporter, heap.freeSize() == 7 * 1024 && heap.largestBlockSize() == 7 * 1024);
+    // free lone block
+    heap.free(alloc1);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 30 * 1024 && heap.largestBlockSize() == 23 * 1024);
+    // allocate into smallest free block
+    REPORTER_ASSERT(reporter, heap.alloc(6 * 1024, &alloc3));
+    REPORTER_ASSERT(reporter, heap.freeSize() == 24 * 1024 && heap.largestBlockSize() == 23 * 1024);
+    // allocate into exact size free block
+    REPORTER_ASSERT(reporter, heap.alloc(23 * 1024, &alloc1));
+    REPORTER_ASSERT(reporter, heap.freeSize() == 1 * 1024 && heap.largestBlockSize() == 1 * 1024);
+    // free lone block
+    heap.free(alloc2);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 19 * 1024 && heap.largestBlockSize() == 18 * 1024);
+    // free and merge with preceding block and following
+    heap.free(alloc3);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 25 * 1024 && heap.largestBlockSize() == 25 * 1024);
+    // free and merge with following block
+    heap.free(alloc1);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 48 * 1024 && heap.largestBlockSize() == 48 * 1024);
+    // free starting block and merge with following
+    heap.free(alloc0);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 64 * 1024 && heap.largestBlockSize() == 64 * 1024);
+
+    // realloc
+    REPORTER_ASSERT(reporter, heap.alloc(4 * 1024, &alloc0));
+    REPORTER_ASSERT(reporter, heap.alloc(35 * 1024, &alloc1));
+    REPORTER_ASSERT(reporter, heap.alloc(10 * 1024, &alloc2));
+    REPORTER_ASSERT(reporter, heap.freeSize() == 15 * 1024 && heap.largestBlockSize() == 15 * 1024);
+    // free starting block and merge with following
+    heap.free(alloc0);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 19 * 1024 && heap.largestBlockSize() == 15 * 1024);
+    // free block and merge with preceding
+    heap.free(alloc1);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 54 * 1024 && heap.largestBlockSize() == 39 * 1024);
+    // free block and merge with preceding and following
+    heap.free(alloc2);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 64 * 1024 && heap.largestBlockSize() == 64 * 1024);
+
+    // fragment
+    REPORTER_ASSERT(reporter, heap.alloc(19 * 1024, &alloc0));
+    REPORTER_ASSERT(reporter, heap.alloc(5 * 1024, &alloc1));
+    REPORTER_ASSERT(reporter, heap.alloc(15 * 1024, &alloc2));
+    REPORTER_ASSERT(reporter, heap.alloc(3 * 1024, &alloc3));
+    REPORTER_ASSERT(reporter, heap.freeSize() == 22 * 1024 && heap.largestBlockSize() == 22 * 1024);
+    heap.free(alloc0);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 41 * 1024 && heap.largestBlockSize() == 22 * 1024);
+    heap.free(alloc2);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 56 * 1024 && heap.largestBlockSize() == 22 * 1024);
+    REPORTER_ASSERT(reporter, !heap.alloc(40 * 1024, &alloc0));
+    heap.free(alloc3);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 59 * 1024 && heap.largestBlockSize() == 40 * 1024);
+    REPORTER_ASSERT(reporter, heap.alloc(40 * 1024, &alloc0));
+    REPORTER_ASSERT(reporter, heap.freeSize() == 19 * 1024 && heap.largestBlockSize() == 19 * 1024);
+    heap.free(alloc1);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 24 * 1024 && heap.largestBlockSize() == 24 * 1024);
+    heap.free(alloc0);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 64 * 1024 && heap.largestBlockSize() == 64 * 1024);
+
+    // unaligned sizes
+    REPORTER_ASSERT(reporter, heap.alloc(19 * 1024 - 31, &alloc0));
+    REPORTER_ASSERT(reporter, heap.alloc(5 * 1024 - 5, &alloc1));
+    REPORTER_ASSERT(reporter, heap.alloc(15 * 1024 - 19, &alloc2));
+    REPORTER_ASSERT(reporter, heap.alloc(3 * 1024 - 3, &alloc3));
+    REPORTER_ASSERT(reporter, heap.freeSize() == 22 * 1024 && heap.largestBlockSize() == 22 * 1024);
+    heap.free(alloc0);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 41 * 1024 && heap.largestBlockSize() == 22 * 1024);
+    heap.free(alloc2);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 56 * 1024 && heap.largestBlockSize() == 22 * 1024);
+    REPORTER_ASSERT(reporter, !heap.alloc(40 * 1024, &alloc0));
+    heap.free(alloc3);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 59 * 1024 && heap.largestBlockSize() == 40 * 1024);
+    REPORTER_ASSERT(reporter, heap.alloc(40 * 1024, &alloc0));
+    REPORTER_ASSERT(reporter, heap.freeSize() == 19 * 1024 && heap.largestBlockSize() == 19 * 1024);
+    heap.free(alloc1);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 24 * 1024 && heap.largestBlockSize() == 24 * 1024);
+    heap.free(alloc0);
+    REPORTER_ASSERT(reporter, heap.freeSize() == 64 * 1024 && heap.largestBlockSize() == 64 * 1024);
+}
+
+void suballoc_test(skiatest::Reporter* reporter, GrContext* context) {
+    GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu());
+
+    // memtype/heap index don't matter, we're just testing the allocation algorithm so we'll use 0
+    GrVkHeap heap(gpu, GrVkHeap::kSubAlloc_Strategy, 64 * 1024);
+    GrVkAlloc alloc0, alloc1, alloc2, alloc3;
+    const VkDeviceSize kAlignment = 16;
+    const uint32_t kMemType = 0;
+    const uint32_t kHeapIndex = 0;
+
+    REPORTER_ASSERT(reporter, heap.allocSize() == 0 && heap.usedSize() == 0);
+
+    // fragment allocations so we need to grow heap
+    REPORTER_ASSERT(reporter, heap.alloc(19 * 1024 - 3, kAlignment, kMemType, kHeapIndex, &alloc0));
+    REPORTER_ASSERT(reporter, heap.alloc(5 * 1024 - 9, kAlignment, kMemType, kHeapIndex, &alloc1));
+    REPORTER_ASSERT(reporter, heap.alloc(15 * 1024 - 15, kAlignment, kMemType, kHeapIndex, &alloc2));
+    REPORTER_ASSERT(reporter, heap.alloc(3 * 1024 - 6, kAlignment, kMemType, kHeapIndex, &alloc3));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 64 * 1024 && heap.usedSize() == 42 * 1024);
+    heap.free(alloc0);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 64 * 1024 && heap.usedSize() == 23 * 1024);
+    heap.free(alloc2);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 64 * 1024 && heap.usedSize() == 8 * 1024);
+    // we expect the heap to grow here
+    REPORTER_ASSERT(reporter, heap.alloc(40 * 1024, kAlignment, kMemType, kHeapIndex, &alloc0));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 48 * 1024);
+    heap.free(alloc3);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 45 * 1024);
+    // heap should not grow here (first subheap has exactly enough room)
+    REPORTER_ASSERT(reporter, heap.alloc(40 * 1024, kAlignment, kMemType, kHeapIndex, &alloc3));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 85 * 1024);
+    // heap should not grow here (second subheap has room)
+    REPORTER_ASSERT(reporter, heap.alloc(22 * 1024, kAlignment, kMemType, kHeapIndex, &alloc2));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 107 * 1024);
+    heap.free(alloc1);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 102 * 1024);
+    heap.free(alloc0);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 62 * 1024);
+    heap.free(alloc2);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 40 * 1024);
+    heap.free(alloc3);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 0 * 1024);
+    // heap should not grow here (allocating more than subheap size)
+    REPORTER_ASSERT(reporter, heap.alloc(128 * 1024, kAlignment, kMemType, kHeapIndex, &alloc0));
+    REPORTER_ASSERT(reporter, 0 == alloc0.fSize);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 0 * 1024);
+    heap.free(alloc0);
+    REPORTER_ASSERT(reporter, heap.alloc(24 * 1024, kAlignment, kMemType, kHeapIndex, &alloc0));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 128 * 1024 && heap.usedSize() == 24 * 1024);
+    // heap should alloc a new subheap because the memory type is different
+    REPORTER_ASSERT(reporter, heap.alloc(24 * 1024, kAlignment, kMemType+1, kHeapIndex, &alloc1));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 192 * 1024 && heap.usedSize() == 48 * 1024);
+    // heap should alloc a new subheap because the alignment is different
+    REPORTER_ASSERT(reporter, heap.alloc(24 * 1024, 128, kMemType, kHeapIndex, &alloc2));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 256 * 1024 && heap.usedSize() == 72 * 1024);
+    heap.free(alloc2);
+    heap.free(alloc0);
+    heap.free(alloc1);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 256 * 1024 && heap.usedSize() == 0 * 1024);
+}
+
+void singlealloc_test(skiatest::Reporter* reporter, GrContext* context) {
+    GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu());
+
+    // memtype/heap index don't matter, we're just testing the allocation algorithm so we'll use 0
+    GrVkHeap heap(gpu, GrVkHeap::kSingleAlloc_Strategy, 64 * 1024);
+    GrVkAlloc alloc0, alloc1, alloc2, alloc3;
+    const VkDeviceSize kAlignment = 64;
+    const uint32_t kMemType = 0;
+    const uint32_t kHeapIndex = 0;
+
+    REPORTER_ASSERT(reporter, heap.allocSize() == 0 && heap.usedSize() == 0);
+
+    // make a few allocations
+    REPORTER_ASSERT(reporter, heap.alloc(49 * 1024 - 3, kAlignment, kMemType, kHeapIndex, &alloc0));
+    REPORTER_ASSERT(reporter, heap.alloc(5 * 1024 - 37, kAlignment, kMemType, kHeapIndex, &alloc1));
+    REPORTER_ASSERT(reporter, heap.alloc(15 * 1024 - 11, kAlignment, kMemType, kHeapIndex, &alloc2));
+    REPORTER_ASSERT(reporter, heap.alloc(3 * 1024 - 29, kAlignment, kMemType, kHeapIndex, &alloc3));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 72 * 1024);
+    heap.free(alloc0);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 23 * 1024);
+    heap.free(alloc2);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 8 * 1024);
+    // heap should not grow here (first subheap has room)
+    REPORTER_ASSERT(reporter, heap.alloc(40 * 1024, kAlignment, kMemType, kHeapIndex, &alloc0));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 48 * 1024);
+    heap.free(alloc3);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 45 * 1024);
+    // check for exact fit -- heap should not grow here (third subheap has room)
+    REPORTER_ASSERT(reporter, heap.alloc(15 * 1024 - 63, kAlignment, kMemType, kHeapIndex, &alloc2));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 60 * 1024);
+    heap.free(alloc2);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 72 * 1024 && heap.usedSize() == 45 * 1024);
+    // heap should grow here (no subheap has room)
+    REPORTER_ASSERT(reporter, heap.alloc(40 * 1024, kAlignment, kMemType, kHeapIndex, &alloc3));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 112 * 1024 && heap.usedSize() == 85 * 1024);
+    heap.free(alloc1);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 112 * 1024 && heap.usedSize() == 80 * 1024);
+    heap.free(alloc0);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 112 * 1024 && heap.usedSize() == 40 * 1024);
+    heap.free(alloc3);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 112 * 1024 && heap.usedSize() == 0 * 1024);
+    REPORTER_ASSERT(reporter, heap.alloc(24 * 1024, kAlignment, kMemType, kHeapIndex, &alloc0));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 112 * 1024 && heap.usedSize() == 24 * 1024);
+    // heap should alloc a new subheap because the memory type is different
+    REPORTER_ASSERT(reporter, heap.alloc(24 * 1024, kAlignment, kMemType + 1, kHeapIndex, &alloc1));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 136 * 1024 && heap.usedSize() == 48 * 1024);
+    // heap should alloc a new subheap because the alignment is different
+    REPORTER_ASSERT(reporter, heap.alloc(24 * 1024, 128, kMemType, kHeapIndex, &alloc2));
+    REPORTER_ASSERT(reporter, heap.allocSize() == 160 * 1024 && heap.usedSize() == 72 * 1024);
+    heap.free(alloc1);
+    heap.free(alloc2);
+    heap.free(alloc0);
+    REPORTER_ASSERT(reporter, heap.allocSize() == 160 * 1024 && heap.usedSize() == 0 * 1024);
+}
+
+DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkHeapTests, reporter, ctxInfo) {
+    subheap_test(reporter, ctxInfo.grContext());
+    suballoc_test(reporter, ctxInfo.grContext());
+    singlealloc_test(reporter, ctxInfo.grContext());
+}
+
+#endif
diff --git a/src/third_party/skia/tests/VkUploadPixelsTests.cpp b/src/third_party/skia/tests/VkUploadPixelsTests.cpp
new file mode 100644
index 0000000..3fa9419
--- /dev/null
+++ b/src/third_party/skia/tests/VkUploadPixelsTests.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This is a GPU-backend specific test. It relies on static intializers to work
+
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU && defined(SK_VULKAN)
+
+#include "GrContextFactory.h"
+#include "GrContextPriv.h"
+#include "GrSurfaceProxy.h"
+#include "GrTest.h"
+#include "SkGr.h"
+#include "Test.h"
+#include "vk/GrVkGpu.h"
+
+using sk_gpu_test::GrContextFactory;
+
+void fill_pixel_data(int width, int height, GrColor* data) {
+
+    // build red-green gradient
+    for (int j = 0; j < height; ++j) {
+        for (int i = 0; i < width; ++i) {
+            unsigned int red = (unsigned int)(256.f*(i / (float)width));
+            unsigned int green = (unsigned int)(256.f*(j / (float)height));
+            data[i + j*width] = GrColorPackRGBA(red - (red>>8), green - (green>>8), 0xff, 0xff);
+        }
+    }
+}
+
+bool does_full_buffer_contain_correct_color(GrColor* srcBuffer,
+                                            GrColor* dstBuffer,
+                                            int width,
+                                            int height) {
+    GrColor* srcPtr = srcBuffer;
+    GrColor* dstPtr = dstBuffer;
+    for (int j = 0; j < height; ++j) {
+        for (int i = 0; i < width; ++i) {
+            if (srcPtr[i] != dstPtr[i]) {
+                return false;
+            }
+        }
+        srcPtr += width;
+        dstPtr += width;
+    }
+    return true;
+}
+
+void basic_texture_test(skiatest::Reporter* reporter, GrContext* context, GrPixelConfig config,
+                        bool renderTarget) {
+    const int kWidth = 16;
+    const int kHeight = 16;
+    SkAutoTMalloc<GrColor> srcBuffer(kWidth*kHeight);
+    SkAutoTMalloc<GrColor> dstBuffer(kWidth*kHeight);
+
+    fill_pixel_data(kWidth, kHeight, srcBuffer.get());
+
+    GrSurfaceDesc surfDesc;
+    surfDesc.fFlags = renderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
+    surfDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
+    surfDesc.fWidth = kWidth;
+    surfDesc.fHeight = kHeight;
+    surfDesc.fConfig = config;
+    surfDesc.fSampleCnt = 0;
+
+    SkColorType ct;
+    SkAssertResult(GrPixelConfigToColorType(config, &ct));
+
+    sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                                               surfDesc, SkBudgeted::kNo,
+                                                               srcBuffer, 0);
+    REPORTER_ASSERT(reporter, proxy);
+    if (proxy) {
+        sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                                                proxy, nullptr);
+
+        SkImageInfo dstInfo = SkImageInfo::Make(kWidth, kHeight, ct, kOpaque_SkAlphaType);
+
+        bool result = sContext->readPixels(dstInfo, dstBuffer, 0, 0, 0);
+        REPORTER_ASSERT(reporter, result);
+        REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(srcBuffer,
+                                                                         dstBuffer,
+                                                                         kWidth,
+                                                                         kHeight));
+
+        dstInfo = SkImageInfo::Make(10, 2, ct, kOpaque_SkAlphaType);
+        result = sContext->writePixels(dstInfo, srcBuffer, 0, 2, 10);
+        REPORTER_ASSERT(reporter, result);
+
+        memset(dstBuffer, 0, kWidth*kHeight*sizeof(GrColor));
+
+        result = sContext->readPixels(dstInfo, dstBuffer, 0, 2, 10);
+        REPORTER_ASSERT(reporter, result);
+
+        REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(srcBuffer,
+                                                                         dstBuffer,
+                                                                         10,
+                                                                         2));
+    }
+
+    surfDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+
+    proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
+                                         surfDesc, SkBudgeted::kNo,
+                                         srcBuffer, 0);
+    REPORTER_ASSERT(reporter, proxy);
+    if (proxy) {
+        sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
+                                                                                proxy, nullptr);
+
+        SkImageInfo dstInfo = SkImageInfo::Make(kWidth, kHeight, ct, kOpaque_SkAlphaType);
+
+        bool result = sContext->readPixels(dstInfo, dstBuffer, 0, 0, 0);
+        REPORTER_ASSERT(reporter, result);
+        REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(srcBuffer,
+                                                                         dstBuffer,
+                                                                         kWidth,
+                                                                         kHeight));
+
+        dstInfo = SkImageInfo::Make(4, 5, ct, kOpaque_SkAlphaType);
+        result = sContext->writePixels(dstInfo, srcBuffer, 0, 5, 4);
+        REPORTER_ASSERT(reporter, result);
+
+        memset(dstBuffer, 0, kWidth*kHeight*sizeof(GrColor));
+
+        result = sContext->readPixels(dstInfo, dstBuffer, 0, 5, 4);
+        REPORTER_ASSERT(reporter, result);
+
+        REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(srcBuffer,
+                                                                         dstBuffer,
+                                                                         4,
+                                                                         5));
+
+    }
+}
+
+DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkUploadPixelsTests, reporter, ctxInfo) {
+    // RGBA
+    basic_texture_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, false);
+    basic_texture_test(reporter, ctxInfo.grContext(), kRGBA_8888_GrPixelConfig, true);
+
+    // BGRA
+    basic_texture_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, false);
+    basic_texture_test(reporter, ctxInfo.grContext(), kBGRA_8888_GrPixelConfig, true);
+}
+
+#endif
diff --git a/src/third_party/skia/tests/VkWrapTests.cpp b/src/third_party/skia/tests/VkWrapTests.cpp
new file mode 100644
index 0000000..4e36315
--- /dev/null
+++ b/src/third_party/skia/tests/VkWrapTests.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This is a GPU-backend specific test. It relies on static intializers to work
+
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU && defined(SK_VULKAN)
+
+#include "GrContextFactory.h"
+#include "GrTest.h"
+#include "GrTexture.h"
+
+#include "Test.h"
+#include "vk/GrVkCaps.h"
+#include "vk/GrVkGpu.h"
+#include "vk/GrVkMemory.h"
+#include "vk/GrVkTypes.h"
+
+using sk_gpu_test::GrContextFactory;
+
+const int kW = 1024;
+const int kH = 1024;
+const GrPixelConfig kPixelConfig = kRGBA_8888_GrPixelConfig;
+
+void wrap_tex_test(skiatest::Reporter* reporter, GrContext* context) {
+
+    GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu());
+
+    GrBackendObject backendObj = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kPixelConfig,
+                                                                      false);
+    const GrVkImageInfo* imageInfo = reinterpret_cast<const GrVkImageInfo*>(backendObj);
+
+    GrBackendTexture backendTex = GrBackendTexture(kW, kH, *imageInfo);
+    sk_sp<GrTexture> tex = gpu->wrapBackendTexture(backendTex,
+                                                   kTopLeft_GrSurfaceOrigin,
+                                                   kBorrow_GrWrapOwnership);
+    REPORTER_ASSERT(reporter, tex);
+
+    // image is null
+    GrVkImageInfo backendCopy = *imageInfo;
+    backendCopy.fImage = VK_NULL_HANDLE;
+    backendTex = GrBackendTexture(kW, kH, backendCopy);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kBorrow_GrWrapOwnership);
+    REPORTER_ASSERT(reporter, !tex);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kAdopt_GrWrapOwnership);
+    REPORTER_ASSERT(reporter, !tex);
+
+    // alloc is null
+    backendCopy.fImage = imageInfo->fImage;
+    backendCopy.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 };
+    backendTex = GrBackendTexture(kW, kH, backendCopy);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kBorrow_GrWrapOwnership);
+    REPORTER_ASSERT(reporter, !tex);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kAdopt_GrWrapOwnership);
+    REPORTER_ASSERT(reporter, !tex);
+    // check adopt creation
+    backendCopy.fAlloc = imageInfo->fAlloc;
+    backendTex = GrBackendTexture(kW, kH, backendCopy);
+    tex = gpu->wrapBackendTexture(backendTex,
+                                  kTopLeft_GrSurfaceOrigin,
+                                  kAdopt_GrWrapOwnership);
+
+    REPORTER_ASSERT(reporter, tex);
+
+    gpu->deleteTestingOnlyBackendTexture(backendObj, true);
+}
+
+void wrap_rt_test(skiatest::Reporter* reporter, GrContext* context) {
+    GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu());
+
+    GrBackendObject backendObj = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kPixelConfig,
+                                                                      true);
+    const GrVkImageInfo* backendTex = reinterpret_cast<const GrVkImageInfo*>(backendObj);
+
+    GrBackendRenderTarget backendRT(kW, kH, 0, 0, *backendTex);
+
+    sk_sp<GrRenderTarget> rt = gpu->wrapBackendRenderTarget(backendRT, kTopLeft_GrSurfaceOrigin);
+    REPORTER_ASSERT(reporter, rt);
+
+    // image is null
+    GrVkImageInfo backendCopy = *backendTex;
+    backendCopy.fImage = VK_NULL_HANDLE;
+    GrBackendRenderTarget backendRT2(kW, kH, 0, 0, backendCopy);
+    rt = gpu->wrapBackendRenderTarget(backendRT2, kTopLeft_GrSurfaceOrigin);
+    REPORTER_ASSERT(reporter, !rt);
+
+    // alloc is null
+    backendCopy.fImage = backendTex->fImage;
+    backendCopy.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 };
+    // can wrap null alloc
+    GrBackendRenderTarget backendRT3(kW, kH, 0, 0, backendCopy);
+    rt = gpu->wrapBackendRenderTarget(backendRT3, kTopLeft_GrSurfaceOrigin);
+    REPORTER_ASSERT(reporter, rt);
+
+    // When we wrapBackendRenderTarget it is always borrowed, so we must make sure to free the
+    // resource when we're done.
+    gpu->deleteTestingOnlyBackendTexture(backendObj, false);
+}
+
+void wrap_trt_test(skiatest::Reporter* reporter, GrContext* context) {
+    GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu());
+
+    GrBackendObject backendObj = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kPixelConfig,
+                                                                      true);
+    const GrVkImageInfo* imageInfo = reinterpret_cast<const GrVkImageInfo*>(backendObj);
+
+    GrBackendTexture backendTex = GrBackendTexture(kW, kH, *imageInfo);
+    sk_sp<GrTexture> tex = gpu->wrapRenderableBackendTexture(backendTex,
+                                                             kTopLeft_GrSurfaceOrigin,
+                                                             0,
+                                                             kBorrow_GrWrapOwnership);
+    REPORTER_ASSERT(reporter, tex);
+
+    // image is null
+    GrVkImageInfo backendCopy = *imageInfo;
+    backendCopy.fImage = VK_NULL_HANDLE;
+    backendTex = GrBackendTexture(kW, kH, backendCopy);
+    tex = gpu->wrapRenderableBackendTexture(backendTex,
+                                            kTopLeft_GrSurfaceOrigin,
+                                            0,
+                                            kBorrow_GrWrapOwnership);
+    REPORTER_ASSERT(reporter, !tex);
+    tex = gpu->wrapRenderableBackendTexture(backendTex,
+                                            kTopLeft_GrSurfaceOrigin,
+                                            0,
+                                            kAdopt_GrWrapOwnership);
+    REPORTER_ASSERT(reporter, !tex);
+
+    // alloc is null
+    backendCopy.fImage = imageInfo->fImage;
+    backendCopy.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 };
+    backendTex = GrBackendTexture(kW, kH, backendCopy);
+    tex = gpu->wrapRenderableBackendTexture(backendTex,
+                                            kTopLeft_GrSurfaceOrigin,
+                                            0,
+                                            kBorrow_GrWrapOwnership);
+    REPORTER_ASSERT(reporter, !tex);
+    tex = gpu->wrapRenderableBackendTexture(backendTex,
+                                            kTopLeft_GrSurfaceOrigin,
+                                            0,
+                                            kAdopt_GrWrapOwnership);
+    REPORTER_ASSERT(reporter, !tex);
+
+    // check adopt creation
+    backendCopy.fAlloc = imageInfo->fAlloc;
+    backendTex = GrBackendTexture(kW, kH, backendCopy);
+    tex = gpu->wrapRenderableBackendTexture(backendTex,
+                                            kTopLeft_GrSurfaceOrigin,
+                                            0,
+                                            kAdopt_GrWrapOwnership);
+    REPORTER_ASSERT(reporter, tex);
+
+    gpu->deleteTestingOnlyBackendTexture(backendObj, true);
+}
+
+DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkWrapTests, reporter, ctxInfo) {
+    wrap_tex_test(reporter, ctxInfo.grContext());
+    wrap_rt_test(reporter, ctxInfo.grContext());
+    wrap_trt_test(reporter, ctxInfo.grContext());
+}
+
+#endif
diff --git a/src/third_party/skia/tests/WArrayTest.cpp b/src/third_party/skia/tests/WArrayTest.cpp
deleted file mode 100644
index 7f03e5b..0000000
--- a/src/third_party/skia/tests/WArrayTest.cpp
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Test.h"
-
-// Include the implementation so we can make an appropriate template instance.
-#include "SkAdvancedTypefaceMetrics.h"
-
-using namespace skia_advanced_typeface_metrics_utils;
-
-// Negative values and zeros in a range plus trailing zeros.
-//                        0  1   2  3  4  5  6  7  8  9 10 11 12 13 14
-static const int16_t data1[] = {-1, 0, -3, 4, 5, 6, 7, 0, 0, 0, 8, 0, 0, 0, 0};
-static const char* expected1 = "0[-1 0 -3 4 5 6 7 0 0 0 8]";
-
-// Run with leading and trailing zeros.
-// Test rules: d         0  1  2    3    4    5    6    7    8    9 10 11
-static const int16_t data2[] = {0, 0, 0, 100, 100, 100, 100, 100, 100, 100, 0, 0};
-static const char* expected2 = "3 9 100";
-
-// Removing 0's from a range.
-// Test rules: a         0  1  2  3  4  5  6  7  8  9 10 11
-static const int16_t data3[] = {1, 2, 0, 0, 0, 3, 4, 0, 0, 0, 0, 5};
-static const char* expected3 = "0[1 2 0 0 0 3 4] 11[5]";
-
-// Removing 0's from a run/range and between runs.
-// Test rules: a, b      0  1  2  3  4  5  6  7  8  9 10 11 12 14 15
-static const int16_t data4[] = {1, 0, 0, 0, 1, 2, 2, 2, 3, 0, 0, 0, 0, 3, 4};
-static const char* expected4 = "0[1 0 0 0 1] 5 7 2 8[3] 13[3 4]";
-
-// Runs that starts outside a range.
-// Test rules: a, e      0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17
-static const int16_t data5[] = {1, 1, 2, 3, 0, 0, 0, 0, 5, 5, 6, 7, 0, 0, 0, 0, 8, 0};
-static const char* expected5 = "0 1 1 2[2 3] 8 9 5 10[6 7] 16[8]";
-
-// Zeros and runs that should be broken out.
-// Test rules: a, b, e   0  1  2  3  4  5  6  7  8  9 10 11 12 13
-static const int16_t data6[] = {1, 0, 0, 0, 0, 1, 2, 3, 3, 4, 5, 5, 5, 6};
-static const char* expected6 = "0[1] 5[1 2 3 3 4] 10 12 5 13[6]";
-
-// Don't cares that aren't enough to break out a run.
-// Test rules: c         0  1   2   3  4  5
-static const int16_t data7[] = {1, 2, 10, 11, 2, 3};
-static const char* expected7 = "0[1 2 10 11 2 3]";
-static const uint32_t subset7[] = {0, 1, 4, 5};
-static const char* expectedSubset7 = "0[1 2 0 0 2 3]";
-
-// Don't cares that are enough to break out a run.
-// Test rules: c         0  1   2   3  4   5  6
-static const int16_t data8[] = {1, 2, 10, 11, 12, 2, 3};
-static const char* expected8 = "0[1 2 10 11 12 2 3]";
-static const uint32_t subset8[] = {0, 1, 5, 6};
-static const char* expectedSubset8 = "0[1] 1 5 2 6[3]";
-
-// Leading don't cares.
-// Test rules: d         0  1   2  3  4
-static const int16_t data9[] = {1, 1, 10, 2, 3};
-static const char* expected9 = "0 1 1 2[10 2 3]";
-static const uint32_t subset9[] = {0, 1, 3, 4};
-static const char* expectedSubset9 = "0 1 1 3[2 3]";
-
-// Almost run of don't cares inside a range.
-// Test rules: c          0  1   2   3   4  5
-static const int16_t data10[] = {1, 2, 10, 11, 12, 3};
-static const char* expected10 = "0[1 2 10 11 12 3]";
-static const uint32_t subset10[] = {0, 1, 5};
-static const char* expectedSubset10 = "0[1 2 0 0 0 3]";
-
-// Run of don't cares inside a range.
-// Test rules: c          0  1   2   3   4   5  6
-static const int16_t data11[] = {1, 2, 10, 11, 12, 13, 3};
-static const char* expected11 = "0[1 2 10 11 12 13 3]";
-static const uint32_t subset11[] = {0, 1, 6};
-static const char* expectedSubset11 = "0[1 2] 6[3]";
-
-// Almost run within a range with leading don't cares.
-// Test rules: c          0   1   2  3   4   5  6
-static const int16_t data12[] = {1, 10, 11, 2, 12, 13, 3};
-static const char* expected12 = "0[1 10 11 2 12 13 3]";
-static const uint32_t subset12[] = {0, 3, 6};
-static const char* expectedSubset12 = "0[1 0 0 2 0 0 3]";
-
-// Run within a range with leading don't cares.
-// Test rules: c          0   1   2  3  4   5   6  7
-static const int16_t data13[] = {1, 10, 11, 2, 2, 12, 13, 3};
-static const char* expected13 = "0[1 10 11 2 2 12 13 3]";
-static const uint32_t subset13[] = {0, 3, 4, 7};
-static const char* expectedSubset13 = "0[1] 1 6 2 7[3]";
-
-// Enough don't cares to breakup something.
-// Test rules: a          0  1  2  3  4  5
-static const int16_t data14[] = {1, 0, 0, 0, 0, 2};
-static const char* expected14 = "0[1] 5[2]";
-static const uint32_t subset14[] = {0, 5};
-static const char* expectedSubset14 = "0[1] 5[2]";
-
-static SkString stringify_advance_data(SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>* data) {
-    SkString result;
-    bool leadingSpace = false;
-    while (data != NULL) {
-      if (leadingSpace) {
-        result.append(" ");
-      } else {
-        leadingSpace = true;
-      }
-      switch(data->fType) {
-        case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kRun:
-          result.appendf("%d %d %d", data->fStartId, data->fEndId, data->fAdvance[0]);
-          break;
-        case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kRange:
-          result.appendf("%d[", data->fStartId);
-          for (int i = 0; i < data->fAdvance.count(); ++i) {
-            if (i > 0) {
-              result.append(" ");
-            }
-            result.appendf("%d", data->fAdvance[i]);
-          }
-          result.append("]");
-          break;
-        case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kDefault:
-          result.appendf("<Default=%d>", data->fAdvance[0]);
-          break;
-      }
-      data = data->fNext.get();
-    }
-    return result;
-}
-
-class TestWData {
-  public:
-    TestWData(skiatest::Reporter* reporter,
-              const int16_t advances[], int advanceLen,
-              const uint32_t subset[], int subsetLen,
-              const char* expected)
-            : fAdvances(advances)
-            , fAdvancesLen(advanceLen)
-            , fSubset(subset)
-            , fSubsetLen(subsetLen)
-            , fExpected(expected) {
-        REPORTER_ASSERT(reporter, RunTest());
-    }
-
-  private:
-    const int16_t* fAdvances;
-    const int fAdvancesLen;
-    const uint32_t* fSubset;
-    const int fSubsetLen;
-    const char* fExpected;
-
-    static bool getAdvance(void* tc, int gId, int16_t* advance) {
-        TestWData* testCase = (TestWData*)tc;
-        if (gId >= 0 && gId < testCase->fAdvancesLen) {
-            *advance = testCase->fAdvances[gId];
-            return true;
-        }
-        return false;
-    }
-
-    bool RunTest() {
-        SkAutoTDelete<SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t> > result;
-        result.reset(getAdvanceData((void*)this, fAdvancesLen, fSubset, fSubsetLen, getAdvance));
-
-        SkString stringResult = stringify_advance_data(result);
-        if (!stringResult.equals(fExpected)) {
-            SkDebugf("Expected: %s\n  Result: %s\n", fExpected, stringResult.c_str());
-            return false;
-        }
-        return true;
-    }
-};
-
-DEF_TEST(WArray, reporter) {
-    TestWData(reporter, data1, SK_ARRAY_COUNT(data1), NULL, 0, expected1);
-    TestWData(reporter, data2, SK_ARRAY_COUNT(data2), NULL, 0, expected2);
-    TestWData(reporter, data3, SK_ARRAY_COUNT(data3), NULL, 0, expected3);
-    TestWData(reporter, data4, SK_ARRAY_COUNT(data4), NULL, 0, expected4);
-    TestWData(reporter, data5, SK_ARRAY_COUNT(data5), NULL, 0, expected5);
-    TestWData(reporter, data6, SK_ARRAY_COUNT(data6), NULL, 0, expected6);
-    TestWData(reporter, data7, SK_ARRAY_COUNT(data7), NULL, 0, expected7);
-    TestWData(reporter, data7, SK_ARRAY_COUNT(data7), subset7,
-              SK_ARRAY_COUNT(subset7), expectedSubset7);
-    TestWData(reporter, data8, SK_ARRAY_COUNT(data8), NULL, 0, expected8);
-    TestWData(reporter, data8, SK_ARRAY_COUNT(data8), subset8,
-              SK_ARRAY_COUNT(subset8), expectedSubset8);
-    TestWData(reporter, data9, SK_ARRAY_COUNT(data9), NULL, 0, expected9);
-    TestWData(reporter, data9, SK_ARRAY_COUNT(data9), subset9,
-              SK_ARRAY_COUNT(subset9), expectedSubset9);
-    TestWData(reporter, data10, SK_ARRAY_COUNT(data10), NULL, 0, expected10);
-    TestWData(reporter, data10, SK_ARRAY_COUNT(data10), subset10,
-              SK_ARRAY_COUNT(subset10), expectedSubset10);
-    TestWData(reporter, data11, SK_ARRAY_COUNT(data11), NULL, 0, expected11);
-    TestWData(reporter, data11, SK_ARRAY_COUNT(data11), subset11,
-              SK_ARRAY_COUNT(subset11), expectedSubset11);
-    TestWData(reporter, data12, SK_ARRAY_COUNT(data12), NULL, 0, expected12);
-    TestWData(reporter, data12, SK_ARRAY_COUNT(data12), subset12,
-              SK_ARRAY_COUNT(subset12), expectedSubset12);
-    TestWData(reporter, data13, SK_ARRAY_COUNT(data13), NULL, 0, expected13);
-    TestWData(reporter, data13, SK_ARRAY_COUNT(data13), subset13,
-              SK_ARRAY_COUNT(subset13), expectedSubset13);
-    TestWData(reporter, data14, SK_ARRAY_COUNT(data14), NULL, 0, expected14);
-    TestWData(reporter, data14, SK_ARRAY_COUNT(data14), subset14,
-              SK_ARRAY_COUNT(subset14), expectedSubset14);
-}
diff --git a/src/third_party/skia/tests/WindowRectanglesTest.cpp b/src/third_party/skia/tests/WindowRectanglesTest.cpp
new file mode 100644
index 0000000..e954565
--- /dev/null
+++ b/src/third_party/skia/tests/WindowRectanglesTest.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrWindowRectangles.h"
+
+static SkIRect next_irect(SkRandom& r) {
+    return {r.nextS(), r.nextS(), r.nextS(), r.nextS()};
+}
+
+DEF_TEST(WindowRectangles, reporter) {
+    SkRandom r;
+
+    SkIRect windowData[GrWindowRectangles::kMaxWindows];
+    for (int i = 0; i < GrWindowRectangles::kMaxWindows; ++i) {
+        windowData[i] = next_irect(r);
+    }
+
+    GrWindowRectangles wr;
+    for (int i = 0; i < GrWindowRectangles::kMaxWindows - 1; ++i) {
+        REPORTER_ASSERT(reporter, wr.count() == i);
+        REPORTER_ASSERT(reporter, !memcmp(wr.data(), windowData, i * sizeof(SkIRect)));
+
+        GrWindowRectangles wr2(wr);
+        REPORTER_ASSERT(reporter, wr2 == wr);
+        REPORTER_ASSERT(reporter, wr2.count() == wr.count());
+        REPORTER_ASSERT(reporter, !memcmp(wr2.data(), wr.data(), i * sizeof(SkIRect)));
+
+        wr.addWindow(windowData[i]);
+    }
+
+    SkASSERT(wr.count() == GrWindowRectangles::kMaxWindows - 1);
+    {
+        GrWindowRectangles A(wr), B(wr);
+        REPORTER_ASSERT(reporter, B == A);
+        REPORTER_ASSERT(reporter, B.data() == A.data()); // Should use copy-on-write.
+
+        A.addWindow(windowData[GrWindowRectangles::kMaxWindows - 1]);
+        REPORTER_ASSERT(reporter, B.data() != A.data());
+        REPORTER_ASSERT(reporter, B != A);
+
+        B.addWindow(SkIRect::MakeLargest());
+        REPORTER_ASSERT(reporter, B != A);
+
+        REPORTER_ASSERT(reporter, !memcmp(A.data(), windowData,
+                                          GrWindowRectangles::kMaxWindows * sizeof(SkIRect)));
+        REPORTER_ASSERT(reporter, !memcmp(B.data(), windowData,
+                                          (GrWindowRectangles::kMaxWindows - 1) * sizeof(SkIRect)));
+        REPORTER_ASSERT(reporter,
+                        B.data()[GrWindowRectangles::kMaxWindows - 1] == SkIRect::MakeLargest());
+    }
+    {
+        GrWindowRectangles A(wr), B(wr);
+        REPORTER_ASSERT(reporter, B == A);
+        REPORTER_ASSERT(reporter, B.data() == A.data()); // Should use copy-on-write.
+
+        A.addWindow(windowData[GrWindowRectangles::kMaxWindows - 1]);
+        B.addWindow(windowData[GrWindowRectangles::kMaxWindows - 1]);
+        REPORTER_ASSERT(reporter, B == A);
+        REPORTER_ASSERT(reporter, B.data() != A.data());
+        REPORTER_ASSERT(reporter, !memcmp(B.data(), A.data(),
+                                          GrWindowRectangles::kMaxWindows * sizeof(SkIRect)));
+        REPORTER_ASSERT(reporter, !memcmp(A.data(), windowData,
+                                          GrWindowRectangles::kMaxWindows * sizeof(SkIRect)));
+    }
+}
+
+#endif
diff --git a/src/third_party/skia/tests/WritePixelsTest.cpp b/src/third_party/skia/tests/WritePixelsTest.cpp
index f47c67b..e876c8f 100644
--- a/src/third_party/skia/tests/WritePixelsTest.cpp
+++ b/src/third_party/skia/tests/WritePixelsTest.cpp
@@ -5,30 +5,29 @@
  * found in the LICENSE file.
  */
 
-#include "SkBitmapDevice.h"
 #include "SkCanvas.h"
 #include "SkColorPriv.h"
 #include "SkMathPriv.h"
-#include "SkRegion.h"
 #include "SkSurface.h"
 #include "Test.h"
 #include "sk_tool_utils.h"
 
 #if SK_SUPPORT_GPU
-#include "GrContextFactory.h"
-#include "SkGpuDevice.h"
-#else
-class GrContext;
-class GrContextFactory;
+#include "GrBackendSurface.h"
+#include "GrContext.h"
+#include "GrGpu.h"
+#include "GrTest.h"
 #endif
 
+#include <initializer_list>
+
 static const int DEV_W = 100, DEV_H = 100;
 static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
 static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1,
                                                 DEV_H * SK_Scalar1);
 static const U8CPU DEV_PAD = 0xee;
 
-static SkPMColor getCanvasColor(int x, int y) {
+static SkPMColor get_canvas_color(int x, int y) {
     SkASSERT(x >= 0 && x < DEV_W);
     SkASSERT(y >= 0 && y < DEV_H);
 
@@ -58,7 +57,7 @@
 }
 
 // assumes any premu/.unpremul has been applied
-static uint32_t packColorType(SkColorType ct, U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
+static uint32_t pack_color_type(SkColorType ct, U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
     uint32_t r32;
     uint8_t* result = reinterpret_cast<uint8_t*>(&r32);
     switch (ct) {
@@ -81,7 +80,7 @@
     return r32;
 }
 
-static uint32_t getBitmapColor(int x, int y, int w, SkColorType ct, SkAlphaType at) {
+static uint32_t get_bitmap_color(int x, int y, int w, SkColorType ct, SkAlphaType at) {
     int n = y * w + x;
     U8CPU b = n & 0xff;
     U8CPU g = (n >> 8) & 0xff;
@@ -109,24 +108,24 @@
         g = SkMulDiv255Ceiling(g, a);
         b = SkMulDiv255Ceiling(b, a);
     }
-    return packColorType(ct, a, r, g , b);
+    return pack_color_type(ct, a, r, g , b);
 }
 
-static void fillCanvas(SkCanvas* canvas) {
+static void fill_canvas(SkCanvas* canvas) {
     SkBitmap bmp;
     if (bmp.isNull()) {
         bmp.allocN32Pixels(DEV_W, DEV_H);
         for (int y = 0; y < DEV_H; ++y) {
             for (int x = 0; x < DEV_W; ++x) {
-                *bmp.getAddr32(x, y) = getCanvasColor(x, y);
+                *bmp.getAddr32(x, y) = get_canvas_color(x, y);
             }
         }
     }
     canvas->save();
     canvas->setMatrix(SkMatrix::I());
-    canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op);
+    canvas->clipRect(DEV_RECT_S, kReplace_SkClipOp);
     SkPaint paint;
-    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    paint.setBlendMode(SkBlendMode::kSrc);
     canvas->drawBitmap(bmp, 0, 0, &paint);
     canvas->restore();
 }
@@ -165,7 +164,7 @@
     return color;
 }
 
-static bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
+static bool check_pixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
     if (!didPremulConversion) {
         return a == b;
     }
@@ -195,13 +194,15 @@
     // At some point this will be unsupported, as we won't allow accessBitmap() to magically call
     // readPixels for the client.
     SkBitmap secretDevBitmap;
-    canvas->readPixels(SkIRect::MakeWH(canvasInfo.width(), canvasInfo.height()), &secretDevBitmap);
+    secretDevBitmap.allocN32Pixels(canvasInfo.width(), canvasInfo.height());
+    if (!canvas->readPixels(secretDevBitmap, 0, 0)) {
+        return false;
+    }
 
-    SkAutoLockPixels alp(secretDevBitmap);
     canvasRowBytes = secretDevBitmap.rowBytes();
     canvasPixels = static_cast<const uint32_t*>(secretDevBitmap.getPixels());
 
-    if (NULL == canvasPixels) {
+    if (nullptr == canvasPixels) {
         return false;
     }
 
@@ -220,21 +221,21 @@
             if (writeRect.contains(cx, cy)) {
                 int bx = cx - writeX;
                 int by = cy - writeY;
-                uint32_t bmpColor8888 = getBitmapColor(bx, by, bitmap.width(),
+                uint32_t bmpColor8888 = get_bitmap_color(bx, by, bitmap.width(),
                                                        bmInfo.colorType(), bmInfo.alphaType());
                 bool mul = (kUnpremul_SkAlphaType == bmInfo.alphaType());
                 SkPMColor bmpPMColor = convert_to_PMColor(bmInfo.colorType(), bmInfo.alphaType(),
                                                           bmpColor8888);
-                bool check = checkPixel(bmpPMColor, canvasPixel, mul);
-                REPORTER_ASSERT(reporter, check);
-                if (!check) {
+                if (!check_pixel(bmpPMColor, canvasPixel, mul)) {
+                    ERRORF(reporter, "Expected canvas pixel at %d, %d to be 0x%08x, got 0x%08x. "
+                           "Write performed premul: %d", cx, cy, bmpPMColor, canvasPixel, mul);
                     return false;
                 }
             } else {
-                bool check;
-                SkPMColor testColor = getCanvasColor(cx, cy);
-                REPORTER_ASSERT(reporter, check = (canvasPixel == testColor));
-                if (!check) {
+                SkPMColor testColor = get_canvas_color(cx, cy);
+                if (canvasPixel != testColor) {
+                    ERRORF(reporter, "Canvas pixel outside write rect at %d, %d changed."
+                           " Should be 0x%08x, got 0x%08x. ", cx, cy, testColor, canvasPixel);
                     return false;
                 }
             }
@@ -255,40 +256,18 @@
     return true;
 }
 
-enum DevType {
-    kRaster_DevType,
-#if SK_SUPPORT_GPU
-    kGpu_BottomLeft_DevType,
-    kGpu_TopLeft_DevType,
-#endif
-};
-
-struct CanvasConfig {
-    DevType fDevType;
-    bool fTightRowBytes;
-};
-
-static const CanvasConfig gCanvasConfigs[] = {
-    {kRaster_DevType, true},
-    {kRaster_DevType, false},
-#if SK_SUPPORT_GPU
-    {kGpu_BottomLeft_DevType, true}, // row bytes has no meaning on gpu devices
-    {kGpu_TopLeft_DevType, true}, // row bytes has no meaning on gpu devices
-#endif
-};
-
 #include "SkMallocPixelRef.h"
 
 // This is a tricky pattern, because we have to setConfig+rowBytes AND specify
 // a custom pixelRef (which also has to specify its rowBytes), so we have to be
 // sure that the two rowBytes match (and the infos match).
 //
-static bool allocRowBytes(SkBitmap* bm, const SkImageInfo& info, size_t rowBytes) {
+static bool alloc_row_bytes(SkBitmap* bm, const SkImageInfo& info, size_t rowBytes) {
     if (!bm->setInfo(info, rowBytes)) {
         return false;
     }
-    SkPixelRef* pr = SkMallocPixelRef::NewAllocate(info, rowBytes, NULL);
-    bm->setPixelRef(pr)->unref();
+    sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(info, rowBytes);
+    bm->setPixelRef(std::move(pr), 0, 0);
     return true;
 }
 
@@ -296,47 +275,15 @@
     sk_free(pixels);
 }
 
-static SkSurface* create_surface(const CanvasConfig& c, GrContext* grCtx) {
-    SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
-    switch (c.fDevType) {
-        case kRaster_DevType: {
-            const size_t rowBytes = c.fTightRowBytes ? info.minRowBytes() : 4 * DEV_W + 100;
-            const size_t size = info.getSafeSize(rowBytes);
-            void* pixels = sk_malloc_throw(size);
-            // if rowBytes isn't tight then set the padding to a known value
-            if (!c.fTightRowBytes) {
-                memset(pixels, DEV_PAD, size);
-            }
-            return SkSurface::NewRasterDirectReleaseProc(info, pixels, rowBytes, free_pixels, NULL);
-        }
-#if SK_SUPPORT_GPU
-        case kGpu_BottomLeft_DevType:
-        case kGpu_TopLeft_DevType:
-            GrTextureDesc desc;
-            desc.fFlags = kRenderTarget_GrTextureFlagBit;
-            desc.fWidth = DEV_W;
-            desc.fHeight = DEV_H;
-            desc.fConfig = kSkia8888_GrPixelConfig;
-            desc.fOrigin = kGpu_TopLeft_DevType == c.fDevType ?
-                kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
-            GrAutoScratchTexture ast(grCtx, desc, GrContext::kExact_ScratchTexMatch);
-            SkAutoTUnref<GrTexture> tex(ast.detach());
-            return SkSurface::NewRenderTargetDirect(tex->asRenderTarget());
-#endif
-    }
-    return NULL;
-}
-
 static bool setup_bitmap(SkBitmap* bm, SkColorType ct, SkAlphaType at, int w, int h, int tightRB) {
     size_t rowBytes = tightRB ? 0 : 4 * w + 60;
     SkImageInfo info = SkImageInfo::Make(w, h, ct, at);
-    if (!allocRowBytes(bm, info, rowBytes)) {
+    if (!alloc_row_bytes(bm, info, rowBytes)) {
         return false;
     }
-    SkAutoLockPixels alp(*bm);
     for (int y = 0; y < h; ++y) {
         for (int x = 0; x < w; ++x) {
-            *bm->getAddr32(x, y) = getBitmapColor(x, y, w, ct, at);
+            *bm->getAddr32(x, y) = get_bitmap_color(x, y, w, ct, at);
         }
     }
     return true;
@@ -348,20 +295,16 @@
     canvas->writePixels(info, &pixel, sizeof(SkPMColor), 0, 0);
 }
 
-static void test_surface_genid(skiatest::Reporter* reporter) {
+DEF_TEST(WritePixelsSurfaceGenID, reporter) {
     const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
-    SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info));
+    auto surface(SkSurface::MakeRaster(info));
     uint32_t genID1 = surface->generationID();
     call_writepixels(surface->getCanvas());
     uint32_t genID2 = surface->generationID();
     REPORTER_ASSERT(reporter, genID1 != genID2);
 }
 
-DEF_GPUTEST(WritePixels, reporter, factory) {
-    test_surface_genid(reporter);
-
-    SkCanvas canvas;
-
+static void test_write_pixels(skiatest::Reporter* reporter, SkSurface* surface) {
     const SkIRect testRects[] = {
         // entire thing
         DEV_RECT,
@@ -409,72 +352,101 @@
         SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10),
     };
 
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gCanvasConfigs); ++i) {
-        int glCtxTypeCnt = 1;
-#if SK_SUPPORT_GPU
-        bool isGPUDevice = kGpu_TopLeft_DevType == gCanvasConfigs[i].fDevType ||
-                           kGpu_BottomLeft_DevType == gCanvasConfigs[i].fDevType;
-        if (isGPUDevice) {
-            glCtxTypeCnt = GrContextFactory::kGLContextTypeCnt;
-        }
-#endif
-        for (int glCtxType = 0; glCtxType < glCtxTypeCnt; ++glCtxType) {
-            GrContext* context = NULL;
-#if SK_SUPPORT_GPU
-            if (isGPUDevice) {
-                GrContextFactory::GLContextType type =
-                    static_cast<GrContextFactory::GLContextType>(glCtxType);
-                if (!GrContextFactory::IsRenderingGLContext(type)) {
-                    continue;
-                }
-                context = factory->get(type);
-                if (NULL == context) {
-                    continue;
-                }
-            }
-#endif
+    SkCanvas& canvas = *surface->getCanvas();
 
-            SkAutoTUnref<SkSurface> surface(create_surface(gCanvasConfigs[i], context));
-            SkCanvas& canvas = *surface->getCanvas();
+    static const struct {
+        SkColorType fColorType;
+        SkAlphaType fAlphaType;
+    } gSrcConfigs[] = {
+        { kRGBA_8888_SkColorType, kPremul_SkAlphaType },
+        { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType },
+        { kBGRA_8888_SkColorType, kPremul_SkAlphaType },
+        { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType },
+    };
+    for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) {
+        const SkIRect& rect = testRects[r];
+        for (int tightBmp = 0; tightBmp < 2; ++tightBmp) {
+            for (size_t c = 0; c < SK_ARRAY_COUNT(gSrcConfigs); ++c) {
+                const SkColorType ct = gSrcConfigs[c].fColorType;
+                const SkAlphaType at = gSrcConfigs[c].fAlphaType;
 
-            static const struct {
-                SkColorType fColorType;
-                SkAlphaType fAlphaType;
-            } gSrcConfigs[] = {
-                { kRGBA_8888_SkColorType, kPremul_SkAlphaType },
-                { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType },
-                { kBGRA_8888_SkColorType, kPremul_SkAlphaType },
-                { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType },
-            };
-            for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) {
-                const SkIRect& rect = testRects[r];
-                for (int tightBmp = 0; tightBmp < 2; ++tightBmp) {
-                    for (size_t c = 0; c < SK_ARRAY_COUNT(gSrcConfigs); ++c) {
-                        const SkColorType ct = gSrcConfigs[c].fColorType;
-                        const SkAlphaType at = gSrcConfigs[c].fAlphaType;
+                fill_canvas(&canvas);
+                SkBitmap bmp;
+                REPORTER_ASSERT(reporter, setup_bitmap(&bmp, ct, at, rect.width(),
+                                                       rect.height(), SkToBool(tightBmp)));
+                uint32_t idBefore = surface->generationID();
 
-                        fillCanvas(&canvas);
-                        SkBitmap bmp;
-                        REPORTER_ASSERT(reporter, setup_bitmap(&bmp, ct, at, rect.width(),
-                                                               rect.height(), SkToBool(tightBmp)));
-                        uint32_t idBefore = surface->generationID();
+                // sk_tool_utils::write_pixels(&canvas, bmp, rect.fLeft, rect.fTop, ct, at);
+                canvas.writePixels(bmp, rect.fLeft, rect.fTop);
 
-                       // sk_tool_utils::write_pixels(&canvas, bmp, rect.fLeft, rect.fTop, ct, at);
-                        canvas.writePixels(bmp, rect.fLeft, rect.fTop);
+                uint32_t idAfter = surface->generationID();
+                REPORTER_ASSERT(reporter, check_write(reporter, &canvas, bmp,
+                                                      rect.fLeft, rect.fTop));
 
-                        uint32_t idAfter = surface->generationID();
-                        REPORTER_ASSERT(reporter, check_write(reporter, &canvas, bmp,
-                                                              rect.fLeft, rect.fTop));
-
-                        // we should change the genID iff pixels were actually written.
-                        SkIRect canvasRect = SkIRect::MakeSize(canvas.getDeviceSize());
-                        SkIRect writeRect = SkIRect::MakeXYWH(rect.fLeft, rect.fTop,
-                                                              bmp.width(), bmp.height());
-                        bool intersects = SkIRect::Intersects(canvasRect, writeRect) ;
-                        REPORTER_ASSERT(reporter, intersects == (idBefore != idAfter));
-                    }
-                }
+                // we should change the genID iff pixels were actually written.
+                SkIRect canvasRect = SkIRect::MakeSize(canvas.getBaseLayerSize());
+                SkIRect writeRect = SkIRect::MakeXYWH(rect.fLeft, rect.fTop,
+                                                      bmp.width(), bmp.height());
+                bool intersects = SkIRect::Intersects(canvasRect, writeRect) ;
+                REPORTER_ASSERT(reporter, intersects == (idBefore != idAfter));
             }
         }
     }
 }
+DEF_TEST(WritePixels, reporter) {
+    const SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
+    for (auto& tightRowBytes : { true, false }) {
+        const size_t rowBytes = tightRowBytes ? info.minRowBytes() : 4 * DEV_W + 100;
+        const size_t size = info.getSafeSize(rowBytes);
+        void* pixels = sk_malloc_throw(size);
+        // if rowBytes isn't tight then set the padding to a known value
+        if (!tightRowBytes) {
+            memset(pixels, DEV_PAD, size);
+        }
+        auto surface(SkSurface::MakeRasterDirectReleaseProc(info, pixels, rowBytes,
+                                                            free_pixels, nullptr));
+        test_write_pixels(reporter, surface.get());
+    }
+}
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixels_Gpu, reporter, ctxInfo) {
+    const SkImageInfo ii = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
+
+    for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
+        for (int sampleCnt : {0, 4}) {
+            sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctxInfo.grContext(),
+                                                                 SkBudgeted::kNo, ii, sampleCnt,
+                                                                 origin, nullptr));
+            if (!surface && sampleCnt > 0) {
+                // Some platforms don't support MSAA
+                continue;
+            }
+            test_write_pixels(reporter, surface.get());
+        }
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WritePixelsNonTexture_Gpu, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+
+    for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
+        for (int sampleCnt : {0, 4}) {
+            auto handle = context->getGpu()->createTestingOnlyBackendTexture(
+                    nullptr, DEV_W, DEV_H, kSkia8888_GrPixelConfig, true);
+            GrBackendTexture backendTexture = GrTest::CreateBackendTexture(
+                    ctxInfo.backend(), DEV_W, DEV_H, kSkia8888_GrPixelConfig, handle);
+            sk_sp<SkSurface> surface(SkSurface::MakeFromBackendTextureAsRenderTarget(
+                    context, backendTexture, origin, sampleCnt, nullptr, nullptr));
+            if (!surface) {
+                context->getGpu()->deleteTestingOnlyBackendTexture(handle);
+                continue;
+            }
+
+            test_write_pixels(reporter, surface.get());
+
+            surface.reset();
+            context->getGpu()->deleteTestingOnlyBackendTexture(handle);
+        }
+    }
+}
+#endif
diff --git a/src/third_party/skia/tests/Writer32Test.cpp b/src/third_party/skia/tests/Writer32Test.cpp
index 56cea2a..6cb79f8 100644
--- a/src/third_party/skia/tests/Writer32Test.cpp
+++ b/src/third_party/skia/tests/Writer32Test.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkAutoMalloc.h"
 #include "SkRandom.h"
 #include "SkReader32.h"
 #include "SkWriter32.h"
@@ -31,8 +32,8 @@
     uint8_t storage[8];
     SkWriter32 writer(storage, sizeof(storage));
 
-    // Can we write NULL?
-    writer.writeString(NULL);
+    // Can we write nullptr?
+    writer.writeString(nullptr);
     const int32_t expected[] = { 0x0, 0x0 };
     check_contents(reporter, writer, expected, sizeof(expected));
 }
@@ -144,16 +145,13 @@
 
 static void testWritePad(skiatest::Reporter* reporter, SkWriter32* writer) {
     // Create some random data to write.
-    const size_t dataSize = 10<<2;
-    SkASSERT(SkIsAlign4(dataSize));
+    const size_t dataSize = 10;
 
-    SkAutoMalloc originalData(dataSize);
+    SkAutoTMalloc<uint32_t> originalData(dataSize);
     {
         SkRandom rand(0);
-        uint32_t* ptr = static_cast<uint32_t*>(originalData.get());
-        uint32_t* stop = ptr + (dataSize>>2);
-        while (ptr < stop) {
-            *ptr++ = rand.nextU();
+        for (size_t i = 0; i < dataSize; i++) {
+            originalData[(int) i] = rand.nextU();
         }
 
         // Write  the random data to the writer at different lengths for
@@ -163,7 +161,7 @@
         }
     }
 
-    uint32_t totalBytes = writer->bytesWritten();
+    size_t totalBytes = writer->bytesWritten();
 
     SkAutoMalloc readStorage(totalBytes);
     writer->flatten(readStorage.get());
@@ -235,19 +233,6 @@
     testOverwriteT(reporter, &writer);
 }
 
-DEF_TEST(Writer32_contiguous, reporter) {
-    uint32_t storage[256];
-    SkWriter32 writer;
-    writer.reset(storage, sizeof(storage));
-    // This write is small enough to fit in storage, so it's contiguous.
-    test1(reporter, &writer);
-    REPORTER_ASSERT(reporter, writer.contiguousArray() != NULL);
-
-    // Everything other aspect of contiguous/non-contiguous is an
-    // implementation detail, not part of the public contract for
-    // SkWriter32, and so not tested here.
-}
-
 DEF_TEST(Writer32_small, reporter) {
     SkSWriter32<8 * sizeof(intptr_t)> writer;
     test1(reporter, &writer);
@@ -281,34 +266,44 @@
     test_rewind(reporter);
 }
 
-DEF_TEST(Writer32_snapshot, reporter) {
-    int32_t array[] = { 1, 2, 4, 11 };
-    SkSWriter32<sizeof(array) + 4> writer;
-    writer.write(array, sizeof(array));
-    check_contents(reporter, writer, array, sizeof(array));
-    const void* beforeData = writer.contiguousArray();
-    SkAutoDataUnref snapshot(writer.snapshotAsData());
-    // check the snapshot forced a copy of the static data
-    REPORTER_ASSERT(reporter, snapshot->data() != beforeData);
-    REPORTER_ASSERT(reporter, snapshot->size() == writer.bytesWritten());
-}
+DEF_TEST(Writer32_data, reporter) {
+    const char* str = "0123456789";
+    sk_sp<SkData> data0(SkData::MakeWithCString(str));
+    sk_sp<SkData> data1(SkData::MakeEmpty());
 
-DEF_TEST(Writer32_snapshot_dynamic, reporter) {
-    int32_t array[] = { 1, 2, 4, 11 };
-    SkWriter32 writer;
-    writer.write(array, sizeof(array));
-    check_contents(reporter, writer, array, sizeof(array));
-    // force a capacity increase so we can test COW behaviour
-    writer.write(array, sizeof(array));
-    writer.rewindToOffset(sizeof(array));
-    const void* beforeData = writer.contiguousArray();
-    SkAutoDataUnref snapshot(writer.snapshotAsData());
-    // check the snapshot still points to the same data as the writer
-    REPORTER_ASSERT(reporter, writer.contiguousArray() == beforeData);
-    REPORTER_ASSERT(reporter, snapshot->data() == beforeData);
-    REPORTER_ASSERT(reporter, snapshot->size() == writer.bytesWritten());
-    // write more data that would fit in the buffer
-    writer.write(array, sizeof(array));
-    // test it triggered COW anyway
-    REPORTER_ASSERT(reporter, writer.contiguousArray() != beforeData);
+    const size_t sizes[] = {
+        SkWriter32::WriteDataSize(nullptr),
+        SkWriter32::WriteDataSize(data0.get()),
+        SkWriter32::WriteDataSize(data1.get()),
+    };
+
+    SkSWriter32<1000> writer;
+    size_t sizeWritten = 0;
+
+    writer.writeData(nullptr);
+    sizeWritten += sizes[0];
+    REPORTER_ASSERT(reporter, sizeWritten == writer.bytesWritten());
+
+    writer.writeData(data0.get());
+    sizeWritten += sizes[1];
+    REPORTER_ASSERT(reporter, sizeWritten == writer.bytesWritten());
+
+    writer.writeData(data1.get());
+    sizeWritten += sizes[2];
+    REPORTER_ASSERT(reporter, sizeWritten == writer.bytesWritten());
+
+    auto result(writer.snapshotAsData());
+
+    SkReader32 reader(result->data(), result->size());
+    auto d0(reader.readData()),
+         d1(reader.readData()),
+         d2(reader.readData());
+
+    REPORTER_ASSERT(reporter, 0 == d0->size());
+    REPORTER_ASSERT(reporter, strlen(str)+1 == d1->size());
+    REPORTER_ASSERT(reporter, !memcmp(str, d1->data(), strlen(str)+1));
+    REPORTER_ASSERT(reporter, 0 == d2->size());
+
+    REPORTER_ASSERT(reporter, reader.offset() == sizeWritten);
+    REPORTER_ASSERT(reporter, reader.eof());
 }
diff --git a/src/third_party/skia/tests/XfermodeTest.cpp b/src/third_party/skia/tests/XfermodeTest.cpp
deleted file mode 100644
index 34f5233..0000000
--- a/src/third_party/skia/tests/XfermodeTest.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkColor.h"
-#include "SkXfermode.h"
-#include "Test.h"
-
-#define ILLEGAL_MODE    ((SkXfermode::Mode)-1)
-
-static void test_asMode(skiatest::Reporter* reporter) {
-    for (int mode = 0; mode <= SkXfermode::kLastMode; mode++) {
-        SkXfermode* xfer = SkXfermode::Create((SkXfermode::Mode) mode);
-
-        SkXfermode::Mode reportedMode = ILLEGAL_MODE;
-        REPORTER_ASSERT(reporter, reportedMode != mode);
-
-        // test IsMode
-        REPORTER_ASSERT(reporter, SkXfermode::AsMode(xfer, &reportedMode));
-        REPORTER_ASSERT(reporter, reportedMode == mode);
-
-        // repeat that test, but with asMode instead
-        if (xfer) {
-            reportedMode = (SkXfermode::Mode) -1;
-            REPORTER_ASSERT(reporter, xfer->asMode(&reportedMode));
-            REPORTER_ASSERT(reporter, reportedMode == mode);
-            xfer->unref();
-        } else {
-            REPORTER_ASSERT(reporter, SkXfermode::kSrcOver_Mode == mode);
-        }
-    }
-}
-
-static void test_IsMode(skiatest::Reporter* reporter) {
-    REPORTER_ASSERT(reporter, SkXfermode::IsMode(NULL,
-                                                 SkXfermode::kSrcOver_Mode));
-
-    for (int i = 0; i <= SkXfermode::kLastMode; ++i) {
-        SkXfermode::Mode mode = (SkXfermode::Mode)i;
-
-        SkXfermode* xfer = SkXfermode::Create(mode);
-        REPORTER_ASSERT(reporter, SkXfermode::IsMode(xfer, mode));
-        SkSafeUnref(xfer);
-
-        if (SkXfermode::kSrcOver_Mode != mode) {
-            REPORTER_ASSERT(reporter, !SkXfermode::IsMode(NULL, mode));
-        }
-    }
-}
-
-DEF_TEST(Xfermode, reporter) {
-    test_asMode(reporter);
-    test_IsMode(reporter);
-}
diff --git a/src/third_party/skia/tests/YUVCacheTest.cpp b/src/third_party/skia/tests/YUVCacheTest.cpp
new file mode 100644
index 0000000..b34cf06
--- /dev/null
+++ b/src/third_party/skia/tests/YUVCacheTest.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCachedData.h"
+#include "SkYUVPlanesCache.h"
+#include "SkResourceCache.h"
+#include "Test.h"
+
+enum LockedState {
+    kUnlocked,
+    kLocked,
+};
+
+enum CachedState {
+    kNotInCache,
+    kInCache,
+};
+
+static void check_data(skiatest::Reporter* reporter, SkCachedData* data,
+                       int refcnt, CachedState cacheState, LockedState lockedState) {
+    REPORTER_ASSERT(reporter, data->testing_only_getRefCnt() == refcnt);
+    REPORTER_ASSERT(reporter, data->testing_only_isInCache() == (kInCache == cacheState));
+    bool isLocked = (data->data() != nullptr);
+    REPORTER_ASSERT(reporter, isLocked == (lockedState == kLocked));
+}
+
+DEF_TEST(YUVPlanesCache, reporter) {
+    SkResourceCache cache(1024);
+
+    SkYUVPlanesCache::Info yuvInfo;
+    for (int i = 0; i < 3; i++) {
+        yuvInfo.fSizeInfo.fSizes[i].fWidth = 20 * i;
+        yuvInfo.fSizeInfo.fSizes[i].fHeight = 10 * i;
+        yuvInfo.fSizeInfo.fWidthBytes[i] = 80 * i;
+    }
+    yuvInfo.fColorSpace = kRec601_SkYUVColorSpace;
+
+    const uint32_t genID = 12345678;
+
+    SkCachedData* data = SkYUVPlanesCache::FindAndRef(genID, &yuvInfo, &cache);
+    REPORTER_ASSERT(reporter, nullptr == data);
+
+    size_t size = 256;
+    data = cache.newCachedData(size);
+    memset(data->writable_data(), 0xff, size);
+
+    SkYUVPlanesCache::Add(genID, data, &yuvInfo, &cache);
+    check_data(reporter, data, 2, kInCache, kLocked);
+
+    data->unref();
+    check_data(reporter, data, 1, kInCache, kUnlocked);
+
+    SkYUVPlanesCache::Info yuvInfoRead;
+    data = SkYUVPlanesCache::FindAndRef(genID, &yuvInfoRead, &cache);
+
+    REPORTER_ASSERT(reporter, data);
+    REPORTER_ASSERT(reporter, data->size() == size);
+    for (int i = 0; i < 3; ++i) {
+        REPORTER_ASSERT(reporter, yuvInfo.fSizeInfo.fSizes[i].fWidth ==
+                yuvInfoRead.fSizeInfo.fSizes[i].fWidth);
+        REPORTER_ASSERT(reporter, yuvInfo.fSizeInfo.fSizes[i].fHeight ==
+                yuvInfoRead.fSizeInfo.fSizes[i].fHeight);
+        REPORTER_ASSERT(reporter, yuvInfo.fSizeInfo.fWidthBytes[i] ==
+                yuvInfoRead.fSizeInfo.fWidthBytes[i]);
+    }
+    REPORTER_ASSERT(reporter, yuvInfo.fColorSpace == yuvInfoRead.fColorSpace);
+
+    check_data(reporter, data, 2, kInCache, kLocked);
+
+    cache.purgeAll();
+    check_data(reporter, data, 1, kNotInCache, kLocked);
+    data->unref();
+}
diff --git a/src/third_party/skia/tests/YUVTest.cpp b/src/third_party/skia/tests/YUVTest.cpp
new file mode 100644
index 0000000..0c31c09
--- /dev/null
+++ b/src/third_party/skia/tests/YUVTest.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Resources.h"
+#include "SkAutoMalloc.h"
+#include "SkCodec.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkYUVSizeInfo.h"
+#include "Test.h"
+
+static void codec_yuv(skiatest::Reporter* reporter,
+                  const char path[],
+                  SkISize expectedSizes[3]) {
+    std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
+    if (!stream) {
+        return;
+    }
+    std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
+    REPORTER_ASSERT(reporter, codec);
+    if (!codec) {
+        return;
+    }
+
+    // Test queryYUV8()
+    SkYUVSizeInfo info;
+    bool success = codec->queryYUV8(nullptr, nullptr);
+    REPORTER_ASSERT(reporter, !success);
+    success = codec->queryYUV8(&info, nullptr);
+    REPORTER_ASSERT(reporter, (expectedSizes == nullptr) == !success);
+    if (!success) {
+        return;
+    }
+    REPORTER_ASSERT(reporter,
+            0 == memcmp((const void*) &info, (const void*) expectedSizes, 3 * sizeof(SkISize)));
+    REPORTER_ASSERT(reporter, info.fWidthBytes[SkYUVSizeInfo::kY] ==
+            (uint32_t) SkAlign8(info.fSizes[SkYUVSizeInfo::kY].width()));
+    REPORTER_ASSERT(reporter, info.fWidthBytes[SkYUVSizeInfo::kU] ==
+            (uint32_t) SkAlign8(info.fSizes[SkYUVSizeInfo::kU].width()));
+    REPORTER_ASSERT(reporter, info.fWidthBytes[SkYUVSizeInfo::kV] ==
+            (uint32_t) SkAlign8(info.fSizes[SkYUVSizeInfo::kV].width()));
+    SkYUVColorSpace colorSpace;
+    success = codec->queryYUV8(&info, &colorSpace);
+    REPORTER_ASSERT(reporter,
+            0 == memcmp((const void*) &info, (const void*) expectedSizes, 3 * sizeof(SkISize)));
+    REPORTER_ASSERT(reporter, info.fWidthBytes[SkYUVSizeInfo::kY] ==
+            (uint32_t) SkAlign8(info.fSizes[SkYUVSizeInfo::kY].width()));
+    REPORTER_ASSERT(reporter, info.fWidthBytes[SkYUVSizeInfo::kU] ==
+            (uint32_t) SkAlign8(info.fSizes[SkYUVSizeInfo::kU].width()));
+    REPORTER_ASSERT(reporter, info.fWidthBytes[SkYUVSizeInfo::kV] ==
+            (uint32_t) SkAlign8(info.fSizes[SkYUVSizeInfo::kV].width()));
+    REPORTER_ASSERT(reporter, kJPEG_SkYUVColorSpace == colorSpace);
+
+    // Allocate the memory for the YUV decode
+    size_t totalBytes =
+            info.fWidthBytes[SkYUVSizeInfo::kY] * info.fSizes[SkYUVSizeInfo::kY].height() +
+            info.fWidthBytes[SkYUVSizeInfo::kU] * info.fSizes[SkYUVSizeInfo::kU].height() +
+            info.fWidthBytes[SkYUVSizeInfo::kV] * info.fSizes[SkYUVSizeInfo::kV].height();
+    SkAutoMalloc storage(totalBytes);
+    void* planes[3];
+    planes[0] = storage.get();
+    planes[1] = SkTAddOffset<void>(planes[0],
+            info.fWidthBytes[SkYUVSizeInfo::kY] * info.fSizes[SkYUVSizeInfo::kY].height());
+    planes[2] = SkTAddOffset<void>(planes[1],
+            info.fWidthBytes[SkYUVSizeInfo::kU] * info.fSizes[SkYUVSizeInfo::kU].height());
+
+    // Test getYUV8Planes()
+    REPORTER_ASSERT(reporter, SkCodec::kInvalidInput ==
+            codec->getYUV8Planes(info, nullptr));
+    REPORTER_ASSERT(reporter, SkCodec::kSuccess ==
+            codec->getYUV8Planes(info, planes));
+}
+
+DEF_TEST(Jpeg_YUV_Codec, r) {
+    SkISize sizes[3];
+
+    sizes[0].set(128, 128);
+    sizes[1].set(64, 64);
+    sizes[2].set(64, 64);
+    codec_yuv(r, "color_wheel.jpg", sizes);
+
+    // H2V2
+    sizes[0].set(512, 512);
+    sizes[1].set(256, 256);
+    sizes[2].set(256, 256);
+    codec_yuv(r, "mandrill_512_q075.jpg", sizes);
+
+    // H1V1
+    sizes[1].set(512, 512);
+    sizes[2].set(512, 512);
+    codec_yuv(r, "mandrill_h1v1.jpg", sizes);
+
+    // H2V1
+    sizes[1].set(256, 512);
+    sizes[2].set(256, 512);
+    codec_yuv(r, "mandrill_h2v1.jpg", sizes);
+
+    // Non-power of two dimensions
+    sizes[0].set(439, 154);
+    sizes[1].set(220, 77);
+    sizes[2].set(220, 77);
+    codec_yuv(r, "cropped_mandrill.jpg", sizes);
+
+    sizes[0].set(8, 8);
+    sizes[1].set(4, 4);
+    sizes[2].set(4, 4);
+    codec_yuv(r, "randPixels.jpg", sizes);
+
+    // Progressive images
+    sizes[0].set(512, 512);
+    sizes[1].set(512, 512);
+    sizes[2].set(512, 512);
+    codec_yuv(r, "brickwork-texture.jpg", sizes);
+    codec_yuv(r, "brickwork_normal-map.jpg", sizes);
+
+    // A CMYK encoded image should fail.
+    codec_yuv(r, "CMYK.jpg", nullptr);
+    // A grayscale encoded image should fail.
+    codec_yuv(r, "grayscale.jpg", nullptr);
+    // A PNG should fail.
+    codec_yuv(r, "arrow.png", nullptr);
+}
diff --git a/src/third_party/skia/tests/image-bitmap.cpp b/src/third_party/skia/tests/image-bitmap.cpp
new file mode 100644
index 0000000..59ab3f7
--- /dev/null
+++ b/src/third_party/skia/tests/image-bitmap.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+#include "SkBitmap.h"
+#include "SkImage.h"
+
+// https://bug.skia.org/5096
+// Test that when we make an image from a subset of a bitmap, that it
+// has a diff (ID, dimensions) from an image made from the entire
+// bitmap or a different subset of the image.
+DEF_TEST(ImageBitmapIdentity, r) {
+    SkBitmap bm, a, b;
+    bm.allocN32Pixels(32, 64);
+    bm.eraseColor(SK_ColorBLACK);
+    bm.setImmutable();
+    (void)bm.extractSubset(&a, SkIRect::MakeXYWH(0, 0, 32, 32));
+    (void)bm.extractSubset(&b, SkIRect::MakeXYWH(0, 32, 32, 32));
+    REPORTER_ASSERT(r, a.getGenerationID() == b.getGenerationID());
+    auto img = SkImage::MakeFromBitmap(bm);
+    auto imgA = SkImage::MakeFromBitmap(a);
+    auto imgB = SkImage::MakeFromBitmap(b);
+    REPORTER_ASSERT(r, img->uniqueID() == bm.getGenerationID());
+    REPORTER_ASSERT(r, img->uniqueID() != imgA->uniqueID());
+    REPORTER_ASSERT(r, img->uniqueID() != imgB->uniqueID());
+    REPORTER_ASSERT(r, imgA->uniqueID() != imgB->uniqueID());
+    REPORTER_ASSERT(r, imgA->uniqueID() != a.getGenerationID());
+    REPORTER_ASSERT(r, imgB->uniqueID() != b.getGenerationID());
+}
diff --git a/src/third_party/skia/tests/skbug5221.cpp b/src/third_party/skia/tests/skbug5221.cpp
new file mode 100644
index 0000000..356a620
--- /dev/null
+++ b/src/third_party/skia/tests/skbug5221.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "SkCanvas.h"
+#include "SkSurface.h"
+
+// This passes by not crashing.
+static void test(SkCanvas* canvas) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    canvas->scale(63, 0);
+    static const char kTxt[] = "A";
+    canvas->drawText(kTxt, SK_ARRAY_COUNT(kTxt), 50, 50, paint);
+}
+
+DEF_TEST(skbug5221, r) {
+    sk_sp<SkSurface> surface(SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(256, 256)));
+    test(surface->getCanvas());
+}
+
+#if SK_SUPPORT_GPU
+DEF_GPUTEST_FOR_ALL_CONTEXTS(skbug5221_GPU, r, contextInfo) {
+    sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(
+            contextInfo.grContext(), SkBudgeted::kYes,
+            SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType, kPremul_SkAlphaType)));
+    test(surface->getCanvas());
+}
+#endif
diff --git a/src/third_party/skia/tests/skbug6389.cpp b/src/third_party/skia/tests/skbug6389.cpp
new file mode 100644
index 0000000..f525158
--- /dev/null
+++ b/src/third_party/skia/tests/skbug6389.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Resources.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkImageSource.h"
+#include "SkSurface.h"
+#include "Test.h"
+
+DEF_TEST(skbug_6389, r) {
+    auto s = SkSurface::MakeRasterN32Premul(100, 100);
+    SkPaint p;
+    p.setMaskFilter(SkBlurMaskFilter::Make(SkBlurStyle::kNormal_SkBlurStyle, 5,
+                                           SkBlurMaskFilter::kHighQuality_BlurFlag));
+    p.setImageFilter(SkImageSource::Make(GetResourceAsImage("mandrill_512.png"), {0, 0, 0, 0},
+                                         {0, 0, 0, 0}, (SkFilterQuality)0));
+    s->getCanvas()->drawPaint(p);
+}
diff --git a/src/third_party/skia/tests/skbug6653.cpp b/src/third_party/skia/tests/skbug6653.cpp
new file mode 100644
index 0000000..d200e14
--- /dev/null
+++ b/src/third_party/skia/tests/skbug6653.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#include "SkCanvas.h"
+#include "SkSurface.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrTest.h"
+#include "Test.h"
+
+static SkBitmap read_pixels(sk_sp<SkSurface> surface) {
+    SkBitmap bmp;
+    bmp.allocN32Pixels(surface->width(), surface->height());
+    if (!surface->getCanvas()->readPixels(bmp, 0, 0)) {
+        SkDebugf("readPixels failed\n");
+    }
+    return bmp;
+}
+
+static sk_sp<SkSurface> make_surface(GrContext* context) {
+    SkImageInfo info = SkImageInfo::Make(50, 50, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+    return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 4,
+                                       kBottomLeft_GrSurfaceOrigin, nullptr);
+}
+
+// Tests that readPixels returns up-to-date results. Demonstrates a bug on Galaxy S6
+// (Mali T760), in MSAA mode.
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(skbug6653, reporter, ctxInfo) {
+    GrContext* ctx = ctxInfo.grContext();
+    SkRect rect = SkRect::MakeWH(50, 50);
+
+    SkPaint paint;
+    paint.setColor(SK_ColorWHITE);
+    paint.setStrokeWidth(5);
+    paint.setStyle(SkPaint::kStroke_Style);
+
+    // The one device that fails this test (Galaxy S6) does so in a flaky fashion. Trying many
+    // times makes it more likely to fail. Also, interacting with the phone (eg swiping between
+    // different home screens) while the test is running makes it fail close to 100%.
+    static const int kNumIterations = 50;
+
+    for (int i = 0; i < kNumIterations; ++i) {
+        auto s0 = make_surface(ctx);
+        if (!s0) {
+            // MSAA may not be supported
+            return;
+        }
+
+        auto s1 = make_surface(ctx);
+        s1->getCanvas()->clear(SK_ColorBLACK);
+        s1->getCanvas()->drawOval(rect, paint);
+        SkBitmap b1 = read_pixels(s1);
+        s1 = nullptr;
+
+        // The bug requires that all three of the following surfaces are cleared to the same color
+        auto s2 = make_surface(ctx);
+        s2->getCanvas()->clear(SK_ColorBLUE);
+        SkBitmap b2 = read_pixels(s2);
+        s2 = nullptr;
+
+        auto s3 = make_surface(ctx);
+        s3->getCanvas()->clear(SK_ColorBLUE);
+        SkBitmap b3 = read_pixels(s3);
+        s0->getCanvas()->drawBitmap(b3, 0, 0);
+        s3 = nullptr;
+
+        auto s4 = make_surface(ctx);
+        s4->getCanvas()->clear(SK_ColorBLUE);
+        s4->getCanvas()->drawOval(rect, paint);
+
+        // When this fails, b4 will "succeed", but return an empty bitmap (containing just the
+        // clear color). Regardless, b5 will contain the oval that was just drawn, so diffing the
+        // two bitmaps tests for the failure case.
+        SkBitmap b4 = read_pixels(s4);
+        SkBitmap b5 = read_pixels(s4);
+
+        bool match = true;
+        for (int y = 0; y < b4.height() && match; ++y) {
+            for (int x = 0; x < b4.width() && match; ++x) {
+                uint32_t pixelA = *b4.getAddr32(x, y);
+                uint32_t pixelB = *b5.getAddr32(x, y);
+                if (pixelA != pixelB) {
+                    match = false;
+                }
+            }
+        }
+
+        REPORTER_ASSERT(reporter, match);
+    }
+}
+
+#endif
diff --git a/src/third_party/skia/tests/skia_test.cpp b/src/third_party/skia/tests/skia_test.cpp
index 0058215..6b7df71 100644
--- a/src/third_party/skia/tests/skia_test.cpp
+++ b/src/third_party/skia/tests/skia_test.cpp
@@ -8,9 +8,11 @@
 #include "CrashHandler.h"
 #include "OverwriteLine.h"
 #include "Resources.h"
+#include "SkAtomics.h"
 #include "SkCommonFlags.h"
 #include "SkGraphics.h"
 #include "SkOSFile.h"
+#include "SkPathOpsDebug.h"
 #include "SkTArray.h"
 #include "SkTaskGroup.h"
 #include "SkTemplates.h"
@@ -23,82 +25,101 @@
 #endif
 
 using namespace skiatest;
+using namespace sk_gpu_test;
 
+DEFINE_bool2(dumpOp, d, false, "dump the pathOps to a file to recover mid-crash.");
 DEFINE_bool2(extendedTest, x, false, "run extended tests for pathOps.");
+DEFINE_bool2(runFail, f, false, "check for success on tests known to fail.");
+DEFINE_bool2(verifyOp, y, false, "compare the pathOps result against a region.");
+
+#if DEBUG_COIN
+DEFINE_bool2(coinTest, c, false, "detect unused coincidence algorithms.");
+#endif
 
 // need to explicitly declare this, or we get some weird infinite loop llist
 template TestRegistry* TestRegistry::gHead;
+void (*gVerboseFinalize)() = nullptr;
 
-class Iter {
+// The threads report back to this object when they are done.
+class Status {
 public:
-    Iter() { this->reset(); }
-    void reset() { fReg = TestRegistry::Head(); }
-
-    Test* next(Reporter* r) {
-        if (fReg) {
-            TestRegistry::Factory fact = fReg->factory();
-            fReg = fReg->next();
-            Test* test = fact(NULL);
-            test->setReporter(r);
-            return test;
-        }
-        return NULL;
-    }
-
-private:
-    const TestRegistry* fReg;
-};
-
-class DebugfReporter : public Reporter {
-public:
-    explicit DebugfReporter(int total) : fDone(0), fTotal(total) {}
-
-    virtual bool allowExtendedTest() const SK_OVERRIDE { return FLAGS_extendedTest; }
-    virtual bool verbose()           const SK_OVERRIDE { return FLAGS_veryVerbose; }
-
-protected:
-    virtual void onReportFailed(const SkString& desc) SK_OVERRIDE {
-        SkDebugf("\nFAILED: %s", desc.c_str());
-    }
-
-    virtual void onEnd(Test* test) SK_OVERRIDE {
+    explicit Status(int total)
+        : fDone(0), fTestCount(0), fFailCount(0), fTotal(total) {}
+    // Threadsafe.
+    void endTest(const char* testName,
+                 bool success,
+                 SkMSec elapsed,
+                 int testCount) {
         const int done = 1 + sk_atomic_inc(&fDone);
-
-        if (!test->passed()) {
-            SkDebugf("\n---- %s FAILED", test->getName());
+        for (int i = 0; i < testCount; ++i) {
+            sk_atomic_inc(&fTestCount);
+        }
+        if (!success) {
+            SkDebugf("\n---- %s FAILED", testName);
         }
 
         SkString prefix(kSkOverwriteLine);
         SkString time;
         if (FLAGS_verbose) {
             prefix.printf("\n");
-            time.printf("%5dms ", test->elapsedMs());
+            time.printf("%5dms ", elapsed);
         }
-        SkDebugf("%s[%3d/%3d] %s%s", prefix.c_str(), done, fTotal, time.c_str(), test->getName());
+        SkDebugf("%s[%3d/%3d] %s%s", prefix.c_str(), done, fTotal, time.c_str(),
+                 testName);
     }
 
+    void reportFailure() { sk_atomic_inc(&fFailCount); }
+
+    int32_t testCount() { return fTestCount; }
+    int32_t failCount() { return fFailCount; }
+
 private:
     int32_t fDone;  // atomic
+    int32_t fTestCount;  // atomic
+    int32_t fFailCount;  // atomic
     const int fTotal;
 };
 
-// Deletes self when run.
-class SkTestRunnable : public SkRunnable {
+class SkTestRunnable {
 public:
-  // Takes ownership of test.
-  SkTestRunnable(Test* test, int32_t* failCount) : fTest(test), fFailCount(failCount) {}
+    SkTestRunnable(const Test& test,
+                   Status* status,
+                   GrContextFactory* grContextFactory = nullptr)
+        : fTest(test), fStatus(status), fGrContextFactory(grContextFactory) {}
 
-  virtual void run() {
-      fTest->run();
-      if(!fTest->passed()) {
-          sk_atomic_inc(fFailCount);
+  void operator()() {
+      struct TestReporter : public skiatest::Reporter {
+      public:
+          TestReporter() : fStats(nullptr), fError(false), fTestCount(0) {}
+          void bumpTestCount() override { ++fTestCount; }
+          bool allowExtendedTest() const override {
+              return FLAGS_extendedTest;
+          }
+          bool verbose() const override { return FLAGS_veryVerbose; }
+          void reportFailed(const skiatest::Failure& failure) override {
+              SkDebugf("\nFAILED: %s", failure.toString().c_str());
+              fError = true;
+          }
+          void* stats() const override { return fStats; }
+          void* fStats;
+          bool fError;
+          int fTestCount;
+      } reporter;
+
+      const Timer timer;
+      fTest.proc(&reporter, fGrContextFactory);
+      SkMSec elapsed = timer.elapsedMsInt();
+      if (reporter.fError) {
+          fStatus->reportFailure();
       }
-      SkDELETE(this);
+      fStatus->endTest(fTest.name, !reporter.fError, elapsed,
+                       reporter.fTestCount);
   }
 
 private:
-    SkAutoTDelete<Test> fTest;
-    int32_t* fFailCount;
+    Test fTest;
+    Status* fStatus;
+    GrContextFactory* fGrContextFactory;
 };
 
 static bool should_run(const char* testName, bool isGPUTest) {
@@ -114,17 +135,17 @@
     return true;
 }
 
-int test_main();
-int test_main() {
+int main(int argc, char** argv) {
+    SkCommandLineFlags::Parse(argc, argv);
+#if DEBUG_DUMP_VERIFY
+    SkPathOpsDebug::gDumpOp = FLAGS_dumpOp;
+    SkPathOpsDebug::gVerifyOp = FLAGS_verifyOp;
+#endif
+    SkPathOpsDebug::gRunFail = FLAGS_runFail;
+    SkPathOpsDebug::gVeryVerbose = FLAGS_veryVerbose;
     SetupCrashHandler();
 
-#if SK_ENABLE_INST_COUNT
-    if (FLAGS_leaks) {
-        gPrintInstCount = true;
-    }
-#endif
-
-    SkGraphics::Init();
+    SkAutoGraphics ag;
 
     {
         SkString header("Skia UnitTests:");
@@ -134,7 +155,7 @@
                 header.appendf(" %s", FLAGS_match[index]);
             }
         }
-        SkString tmpDir = Test::GetTmpDir();
+        SkString tmpDir = skiatest::GetTmpDir();
         if (!tmpDir.isEmpty()) {
             header.appendf(" --tmpDir %s", tmpDir.c_str());
         }
@@ -142,83 +163,108 @@
         if (!resourcePath.isEmpty()) {
             header.appendf(" --resourcePath %s", resourcePath.c_str());
         }
+#if DEBUG_COIN
+        if (FLAGS_coinTest) {
+            header.appendf(" -c");
+        }
+#endif
+        if (FLAGS_dumpOp) {
+            header.appendf(" -d");
+        }
+#ifdef SK_DEBUG
+        if (FLAGS_runFail) {
+            header.appendf(" -f");
+        }
+#endif
+        if (FLAGS_verbose) {
+            header.appendf(" -v");
+        }
+        if (FLAGS_veryVerbose) {
+            header.appendf(" -V");
+        }
+        if (FLAGS_extendedTest) {
+            header.appendf(" -x");
+        }
+        if (FLAGS_verifyOp) {
+            header.appendf(" -y");
+        }
 #ifdef SK_DEBUG
         header.append(" SK_DEBUG");
 #else
         header.append(" SK_RELEASE");
 #endif
-        header.appendf(" skia_arch_width=%d", (int)sizeof(void*) * 8);
         if (FLAGS_veryVerbose) {
             header.appendf("\n");
         }
-        SkDebugf(header.c_str());
+        SkDebugf("%s", header.c_str());
     }
 
 
     // Count tests first.
     int total = 0;
     int toRun = 0;
-    Test* test;
 
-    Iter iter;
-    while ((test = iter.next(NULL/*reporter not needed*/)) != NULL) {
-        SkAutoTDelete<Test> owned(test);
-        if (should_run(test->getName(), test->isGPUTest())) {
+    for (const TestRegistry* iter = TestRegistry::Head(); iter;
+         iter = iter->next()) {
+        const Test& test = iter->factory();
+        if (should_run(test.name, test.needsGpu)) {
             toRun++;
         }
         total++;
     }
 
     // Now run them.
-    iter.reset();
-    int32_t failCount = 0;
     int skipCount = 0;
 
     SkTaskGroup::Enabler enabled(FLAGS_threads);
     SkTaskGroup cpuTests;
-    SkTArray<Test*> gpuTests;  // Always passes ownership to an SkTestRunnable
+    SkTArray<const Test*> gpuTests;
 
-    DebugfReporter reporter(toRun);
-    for (int i = 0; i < total; i++) {
-        SkAutoTDelete<Test> test(iter.next(&reporter));
-        if (!should_run(test->getName(), test->isGPUTest())) {
+    Status status(toRun);
+    for (const TestRegistry* iter = TestRegistry::Head(); iter;
+         iter = iter->next()) {
+        const Test& test = iter->factory();
+        if (!should_run(test.name, test.needsGpu)) {
             ++skipCount;
-        } else if (test->isGPUTest()) {
-            gpuTests.push_back() = test.detach();
+        } else if (test.needsGpu) {
+            gpuTests.push_back(&test);
         } else {
-            cpuTests.add(SkNEW_ARGS(SkTestRunnable, (test.detach(), &failCount)));
+            cpuTests.add(SkTestRunnable(test, &status));
         }
     }
 
+    GrContextFactory* grContextFactoryPtr = nullptr;
 #if SK_SUPPORT_GPU
     // Give GPU tests a context factory if that makes sense on this machine.
     GrContextFactory grContextFactory;
-    for (int i = 0; i < gpuTests.count(); i++) {
-        gpuTests[i]->setGrContextFactory(&grContextFactory);
-    }
+    grContextFactoryPtr = &grContextFactory;
+
 #endif
 
     // Run GPU tests on this thread.
     for (int i = 0; i < gpuTests.count(); i++) {
-        SkNEW_ARGS(SkTestRunnable, (gpuTests[i], &failCount))->run();
+        SkTestRunnable(*gpuTests[i], &status, grContextFactoryPtr)();
     }
 
     // Block until threaded tests finish.
     cpuTests.wait();
 
     if (FLAGS_verbose) {
-        SkDebugf("\nFinished %d tests, %d failures, %d skipped. (%d internal tests)",
-                 toRun, failCount, skipCount, reporter.countTests());
+        SkDebugf(
+                "\nFinished %d tests, %d failures, %d skipped. "
+                "(%d internal tests)",
+                toRun, status.failCount(), skipCount, status.testCount());
+        if (gVerboseFinalize) {
+            (*gVerboseFinalize)();
+        }
     }
-    SkGraphics::Term();
 
     SkDebugf("\n");
-    return (failCount == 0) ? 0 : 1;
-}
-
-#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
-int main(int argc, char** argv) {
-    SkCommandLineFlags::Parse(argc, argv);
-    return test_main();
-}
+#if DEBUG_COIN
+    if (FLAGS_coinTest) {
+        SkPathOpsDebug::DumpCoinDict();
+    }
 #endif
+
+    return (status.failCount() == 0) ? 0 : 1;
+}