Import Cobalt 25.master.0.1033734
diff --git a/third_party/skia/samplecode/PerlinPatch.cpp b/third_party/skia/samplecode/PerlinPatch.cpp
deleted file mode 100644
index 43dce62..0000000
--- a/third_party/skia/samplecode/PerlinPatch.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * 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 "include/core/SkCanvas.h"
-#include "include/effects/SkGradientShader.h"
-#include "include/effects/SkPerlinNoiseShader.h"
-#include "samplecode/Sample.h"
-#include "src/utils/SkPatchUtils.h"
-#include "tools/skui/ModifierKey.h"
-
-static void draw_control_points(SkCanvas* canvas, const SkPoint cubics[12]) {
-    //draw control points
-    SkPaint paint;
-    SkPoint bottom[SkPatchUtils::kNumPtsCubic];
-    SkPatchUtils::GetBottomCubic(cubics, bottom);
-    SkPoint top[SkPatchUtils::kNumPtsCubic];
-    SkPatchUtils::GetTopCubic(cubics, top);
-    SkPoint left[SkPatchUtils::kNumPtsCubic];
-    SkPatchUtils::GetLeftCubic(cubics, left);
-    SkPoint right[SkPatchUtils::kNumPtsCubic];
-    SkPatchUtils::GetRightCubic(cubics, right);
-
-    paint.setColor(SK_ColorBLACK);
-    paint.setStrokeWidth(0.5f);
-    SkPoint corners[4] = { bottom[0], bottom[3], top[0], top[3] };
-    canvas->drawPoints(SkCanvas::kLines_PointMode, 4, bottom, paint);
-    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, bottom + 1, paint);
-    canvas->drawPoints(SkCanvas::kLines_PointMode, 4, top, paint);
-    canvas->drawPoints(SkCanvas::kLines_PointMode, 4, left, paint);
-    canvas->drawPoints(SkCanvas::kLines_PointMode, 4, right, paint);
-
-    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, top + 1, paint);
-    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, left + 1, paint);
-    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, right + 1, paint);
-
-    paint.setStrokeWidth(2);
-
-    paint.setColor(SK_ColorRED);
-    canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, corners, paint);
-
-    paint.setColor(SK_ColorBLUE);
-    canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, bottom + 1, paint);
-
-    paint.setColor(SK_ColorCYAN);
-    canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, top + 1, paint);
-
-    paint.setColor(SK_ColorYELLOW);
-    canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, left + 1, paint);
-
-    paint.setColor(SK_ColorGREEN);
-    canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, right + 1, paint);
-}
-
-// These are actually half the total width and hieghts
-const SkScalar TexWidth = 100.0f;
-const SkScalar TexHeight = 100.0f;
-
-class PerlinPatchView : public Sample {
-    sk_sp<SkShader> fShader0;
-    sk_sp<SkShader> fShader1;
-    sk_sp<SkShader> fShaderCompose;
-    SkScalar fXFreq;
-    SkScalar fYFreq;
-    SkScalar fSeed;
-    SkPoint  fPts[SkPatchUtils::kNumCtrlPts];
-    SkScalar fTexX;
-    SkScalar fTexY;
-    SkScalar fTexScale;
-    SkMatrix fInvMatrix;
-    bool     fShowGrid = false;
-
-public:
-    PerlinPatchView() : fXFreq(0.025f), fYFreq(0.025f), fSeed(0.0f),
-                        fTexX(100.0), fTexY(50.0), fTexScale(1.0f) {
-        const SkScalar s = 2;
-        // The order of the colors and points is clockwise starting at upper-left corner.
-        //top points
-        fPts[0].set(100 * s, 100 * s);
-        fPts[1].set(150 * s, 50 * s);
-        fPts[2].set(250 * s, 150 * s);
-        fPts[3].set(300 * s, 100 * s);
-        //right points
-        fPts[4].set(275 * s, 150 * s);
-        fPts[5].set(350 * s, 250 * s);
-        //bottom points
-        fPts[6].set(300 * s, 300 * s);
-        fPts[7].set(250 * s, 250 * s);
-        //left points
-        fPts[8].set(150 * s, 350 * s);
-        fPts[9].set(100 * s, 300 * s);
-        fPts[10].set(50 * s, 250 * s);
-        fPts[11].set(150 * s, 150 * s);
-
-        const SkColor colors[SkPatchUtils::kNumCorners] = {
-            0xFF5555FF, 0xFF8888FF, 0xFFCCCCFF
-        };
-        const SkPoint points[2] = { SkPoint::Make(0.0f, 0.0f),
-                                    SkPoint::Make(100.0f, 100.0f) };
-        fShader0 = SkGradientShader::MakeLinear(points,
-                                                  colors,
-                                                  nullptr,
-                                                  3,
-                                                  SkTileMode::kMirror,
-                                                  0,
-                                                  nullptr);
-    }
-
-protected:
-    SkString name() override { return SkString("PerlinPatch"); }
-
-    bool onChar(SkUnichar uni) override {
-            switch (uni) {
-                case 'g': fShowGrid = !fShowGrid; return true;
-                default: break;
-            }
-            return false;
-    }
-
-    bool onAnimate(double nanos) override {
-        fSeed += 0.005f;
-        return true;
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        if (!canvas->getTotalMatrix().invert(&fInvMatrix)) {
-            return;
-        }
-
-        SkPaint paint;
-
-        SkScalar texWidth = fTexScale * TexWidth;
-        SkScalar texHeight = fTexScale * TexHeight;
-        const SkPoint texCoords[SkPatchUtils::kNumCorners] = {
-            { fTexX - texWidth, fTexY - texHeight},
-            { fTexX + texWidth, fTexY - texHeight},
-            { fTexX + texWidth, fTexY + texHeight},
-            { fTexX - texWidth, fTexY + texHeight}}
-        ;
-
-        SkScalar scaleFreq = 2.0;
-        fShader1 = SkPerlinNoiseShader::MakeImprovedNoise(fXFreq/scaleFreq, fYFreq/scaleFreq, 4,
-                                                             fSeed);
-        fShaderCompose = SkShaders::Blend(SkBlendMode::kSrcOver, fShader0, fShader1);
-
-        paint.setShader(fShaderCompose);
-
-        const SkPoint* tex = texCoords;
-        if (fShowGrid) {
-            tex = nullptr;
-        }
-        canvas->drawPatch(fPts, nullptr, tex, SkBlendMode::kSrc, paint);
-
-        draw_control_points(canvas, fPts);
-    }
-
-    class PtClick : public Click {
-    public:
-        int fIndex;
-        PtClick(int index) : fIndex(index) {}
-    };
-
-    static bool hittest(const SkPoint& pt, SkScalar x, SkScalar y) {
-        return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(5);
-    }
-
-    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
-        modi &= ~skui::ModifierKey::kFirstPress;  // ignore this
-        if (skui::ModifierKey::kShift == modi) {
-            return new PtClick(-1);
-        }
-        if (skui::ModifierKey::kControl == modi) {
-            return new PtClick(-2);
-        }
-        SkPoint clickPoint = {x, y};
-        fInvMatrix.mapPoints(&clickPoint, 1);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) {
-            if (hittest(fPts[i], clickPoint.fX, clickPoint.fY)) {
-                return new PtClick((int)i);
-            }
-        }
-        return nullptr;
-    }
-
-    bool onClick(Click* click) override {
-        PtClick* ptClick = (PtClick*)click;
-        if (ptClick->fIndex >= 0) {
-            fPts[ptClick->fIndex].set(click->fCurr.fX , click->fCurr.fY );
-        } else if (-1 == ptClick->fIndex) {
-            SkScalar xDiff = click->fPrev.fX - click->fCurr.fX;
-            SkScalar yDiff = click->fPrev.fY - click->fCurr.fY;
-            fTexX += xDiff * fTexScale;
-            fTexY += yDiff * fTexScale;
-        } else if (-2 == ptClick->fIndex) {
-            SkScalar yDiff = click->fCurr.fY - click->fPrev.fY;
-            fTexScale += yDiff / 10.0f;
-            fTexScale = SkTMax(0.1f, SkTMin(20.f, fTexScale));
-        }
-        return true;
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-DEF_SAMPLE( return new PerlinPatchView(); )
diff --git a/third_party/skia/samplecode/Sample.cpp b/third_party/skia/samplecode/Sample.cpp
index c2ba788..a69de8e 100644
--- a/third_party/skia/samplecode/Sample.cpp
+++ b/third_party/skia/samplecode/Sample.cpp
@@ -10,16 +10,16 @@
 #include "samplecode/Sample.h"
 
 #if SK_SUPPORT_GPU
-#   include "include/gpu/GrContext.h"
+#   include "include/gpu/GrDirectContext.h"
 #else
-class GrContext;
+class GrDirectContext;
 #endif
 
 //////////////////////////////////////////////////////////////////////////////
 
 void Sample::setSize(SkScalar width, SkScalar height) {
-    width = SkMaxScalar(0, width);
-    height = SkMaxScalar(0, height);
+    width = std::max(0.0f, width);
+    height = std::max(0.0f, height);
 
     if (fWidth != width || fHeight != height)
     {
@@ -49,9 +49,9 @@
         SkAutoCanvasRestore acr(canvas, true);
         this->onDrawContent(canvas);
 #if SK_SUPPORT_GPU
-        // Ensure the GrContext doesn't combine GrDrawOps across draw loops.
-        if (GrContext* context = canvas->getGrContext()) {
-            context->flush();
+        // Ensure the context doesn't combine GrDrawOps across draw loops.
+        if (auto direct = GrAsDirectContext(canvas->recordingContext())) {
+            direct->flushAndSubmit();
         }
 #endif
 
@@ -62,12 +62,13 @@
 ////////////////////////////////////////////////////////////////////////////
 
 bool Sample::mouse(SkPoint point, skui::InputState clickState, skui::ModifierKey modifierKeys) {
+    auto dispatch = [this](Click* c) {
+        return c->fHasFunc ? c->fFunc(c) : this->onClick(c);
+    };
+
     switch (clickState) {
         case skui::InputState::kDown:
             fClick = nullptr;
-            if (point.x() < 0 || point.y() < 0 || point.x() >= fWidth || point.y() >= fHeight) {
-                return false;
-            }
             fClick.reset(this->onFindClickHandler(point.x(), point.y(), modifierKeys));
             if (!fClick) {
                 return false;
@@ -75,7 +76,7 @@
             fClick->fPrev = fClick->fCurr = fClick->fOrig = point;
             fClick->fState = skui::InputState::kDown;
             fClick->fModifierKeys = modifierKeys;
-            this->onClick(fClick.get());
+            dispatch(fClick.get());
             return true;
         case skui::InputState::kMove:
             if (fClick) {
@@ -83,7 +84,7 @@
                 fClick->fCurr = point;
                 fClick->fState = skui::InputState::kMove;
                 fClick->fModifierKeys = modifierKeys;
-                return this->onClick(fClick.get());
+                return dispatch(fClick.get());
             }
             return false;
         case skui::InputState::kUp:
@@ -92,7 +93,7 @@
                 fClick->fCurr = point;
                 fClick->fState = skui::InputState::kUp;
                 fClick->fModifierKeys = modifierKeys;
-                bool result = this->onClick(fClick.get());
+                bool result = dispatch(fClick.get());
                 fClick = nullptr;
                 return result;
             }
diff --git a/third_party/skia/samplecode/Sample.h b/third_party/skia/samplecode/Sample.h
index d6ccf48..f1026bd 100644
--- a/third_party/skia/samplecode/Sample.h
+++ b/third_party/skia/samplecode/Sample.h
@@ -18,6 +18,8 @@
 #include "tools/skui/InputState.h"
 #include "tools/skui/ModifierKey.h"
 
+#include <functional>
+
 class SkCanvas;
 class Sample;
 
@@ -55,13 +57,19 @@
     // Click handling
     class Click {
     public:
+        Click() {}
+        Click(std::function<bool(Click*)> f) : fFunc(f), fHasFunc(true) {}
         virtual ~Click() = default;
+
         SkPoint     fOrig = {0, 0};
         SkPoint     fPrev = {0, 0};
         SkPoint     fCurr = {0, 0};
         skui::InputState  fState = skui::InputState::kDown;
         skui::ModifierKey fModifierKeys = skui::ModifierKey::kNone;
         SkMetaData  fMeta;
+
+        std::function<bool(Click*)> fFunc;
+        bool fHasFunc = false;
     };
     bool mouse(SkPoint point, skui::InputState clickState, skui::ModifierKey modifierKeys);
 
diff --git a/third_party/skia/samplecode/Sample2PtRadial.cpp b/third_party/skia/samplecode/Sample2PtRadial.cpp
index 64f9e66..f404d14 100644
--- a/third_party/skia/samplecode/Sample2PtRadial.cpp
+++ b/third_party/skia/samplecode/Sample2PtRadial.cpp
@@ -14,9 +14,9 @@
     TwoPtConicalView() {}
 
 protected:
-    virtual SkString name() { return SkString("2PtConical"); }
+    SkString name() override { return SkString("2PtConical"); }
 
-    virtual void onDrawContent(SkCanvas* canvas) {
+    void onDrawContent(SkCanvas* canvas) override {
         canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
 
         SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
@@ -32,7 +32,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/Sample3D.cpp b/third_party/skia/samplecode/Sample3D.cpp
new file mode 100644
index 0000000..7e958bc
--- /dev/null
+++ b/third_party/skia/samplecode/Sample3D.cpp
@@ -0,0 +1,487 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkCanvas.h"
+#include "include/core/SkM44.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkRRect.h"
+#include "include/core/SkVertices.h"
+#include "include/utils/SkRandom.h"
+#include "samplecode/Sample.h"
+#include "tools/Resources.h"
+
+struct VSphere {
+    SkV2     fCenter;
+    SkScalar fRadius;
+
+    VSphere(SkV2 center, SkScalar radius) : fCenter(center), fRadius(radius) {}
+
+    bool contains(SkV2 v) const {
+        return (v - fCenter).length() <= fRadius;
+    }
+
+    SkV2 pinLoc(SkV2 p) const {
+        auto v = p - fCenter;
+        if (v.length() > fRadius) {
+            v *= (fRadius / v.length());
+        }
+        return fCenter + v;
+    }
+
+    SkV3 computeUnitV3(SkV2 v) const {
+        v = (v - fCenter) * (1 / fRadius);
+        SkScalar len2 = v.lengthSquared();
+        if (len2 > 1) {
+            v = v.normalize();
+            len2 = 1;
+        }
+        SkScalar z = SkScalarSqrt(1 - len2);
+        return {v.x, v.y, z};
+    }
+
+    struct RotateInfo {
+        SkV3    fAxis;
+        SkScalar fAngle;
+    };
+
+    RotateInfo computeRotationInfo(SkV2 a, SkV2 b) const {
+        SkV3 u = this->computeUnitV3(a);
+        SkV3 v = this->computeUnitV3(b);
+        SkV3 axis = u.cross(v);
+        SkScalar length = axis.length();
+
+        if (!SkScalarNearlyZero(length)) {
+            return {axis * (1.0f / length), acos(u.dot(v))};
+        }
+        return {{0, 0, 0}, 0};
+    }
+
+    SkM44 computeRotation(SkV2 a, SkV2 b) const {
+        auto [axis, angle] = this->computeRotationInfo(a, b);
+        return SkM44::Rotate(axis, angle);
+    }
+};
+
+static SkM44 inv(const SkM44& m) {
+    SkM44 inverse;
+    SkAssertResult(m.invert(&inverse));
+    return inverse;
+}
+
+// Compute the inverse transpose (of the upper-left 3x3) of a matrix, used to transform vectors
+static SkM44 normals(SkM44 m) {
+    m.setRow(3, {0, 0, 0, 1});
+    m.setCol(3, {0, 0, 0, 1});
+    SkAssertResult(m.invert(&m));
+    return m.transpose();
+}
+
+class Sample3DView : public Sample {
+protected:
+    float   fNear = 0.05f;
+    float   fFar = 4;
+    float   fAngle = SK_ScalarPI / 12;
+
+    SkV3    fEye { 0, 0, 1.0f/tan(fAngle/2) - 1 };
+    SkV3    fCOA { 0, 0, 0 };
+    SkV3    fUp  { 0, 1, 0 };
+
+public:
+    void concatCamera(SkCanvas* canvas, const SkRect& area, SkScalar zscale) {
+        SkM44 camera = SkM44::LookAt(fEye, fCOA, fUp),
+              perspective = SkM44::Perspective(fNear, fFar, fAngle),
+              viewport = SkM44::Translate(area.centerX(), area.centerY(), 0) *
+                         SkM44::Scale(area.width()*0.5f, area.height()*0.5f, zscale);
+
+        canvas->concat(viewport * perspective * camera * inv(viewport));
+    }
+};
+
+struct Face {
+    SkScalar fRx, fRy;
+    SkColor  fColor;
+
+    static SkM44 T(SkScalar x, SkScalar y, SkScalar z) {
+        return SkM44::Translate(x, y, z);
+    }
+
+    static SkM44 R(SkV3 axis, SkScalar rad) {
+        return SkM44::Rotate(axis, rad);
+    }
+
+    SkM44 asM44(SkScalar scale) const {
+        return R({0,1,0}, fRy) * R({1,0,0}, fRx) * T(0, 0, scale);
+    }
+};
+
+static bool front(const SkM44& m) {
+    SkM44 m2(SkM44::kUninitialized_Constructor);
+    if (!m.invert(&m2)) {
+        m2.setIdentity();
+    }
+    /*
+     *  Classically we want to dot the transpose(inverse(ctm)) with our surface normal.
+     *  In this case, the normal is known to be {0, 0, 1}, so we only actually need to look
+     *  at the z-scale of the inverse (the transpose doesn't change the main diagonal, so
+     *  no need to actually transpose).
+     */
+    return m2.rc(2,2) > 0;
+}
+
+const Face faces[] = {
+    {             0,             0,  SK_ColorRED }, // front
+    {             0,   SK_ScalarPI,  SK_ColorGREEN }, // back
+
+    { SK_ScalarPI/2,             0,  SK_ColorBLUE }, // top
+    {-SK_ScalarPI/2,             0,  SK_ColorCYAN }, // bottom
+
+    {             0, SK_ScalarPI/2,  SK_ColorMAGENTA }, // left
+    {             0,-SK_ScalarPI/2,  SK_ColorYELLOW }, // right
+};
+
+#include "include/effects/SkRuntimeEffect.h"
+
+struct LightOnSphere {
+    SkV2     fLoc;
+    SkScalar fDistance;
+    SkScalar fRadius;
+
+    SkV3 computeWorldPos(const VSphere& s) const {
+        return s.computeUnitV3(fLoc) * fDistance;
+    }
+
+    void draw(SkCanvas* canvas) const {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(SK_ColorWHITE);
+        canvas->drawCircle(fLoc.x, fLoc.y, fRadius + 2, paint);
+        paint.setColor(SK_ColorBLACK);
+        canvas->drawCircle(fLoc.x, fLoc.y, fRadius, paint);
+    }
+};
+
+#include "include/core/SkTime.h"
+
+class RotateAnimator {
+    SkV3        fAxis = {0, 0, 0};
+    SkScalar    fAngle = 0,
+                fPrevAngle = 1234567;
+    double      fNow = 0,
+                fPrevNow = 0;
+
+    SkScalar    fAngleSpeed = 0,
+                fAngleSign = 1;
+
+    inline static constexpr double kSlowDown = 4;
+    inline static constexpr SkScalar kMaxSpeed = 16;
+
+public:
+    void update(SkV3 axis, SkScalar angle) {
+        if (angle != fPrevAngle) {
+            fPrevAngle = fAngle;
+            fAngle = angle;
+
+            fPrevNow = fNow;
+            fNow = SkTime::GetSecs();
+
+            fAxis = axis;
+        }
+    }
+
+    SkM44 rotation() {
+        if (fAngleSpeed > 0) {
+            double now = SkTime::GetSecs();
+            double dtime = now - fPrevNow;
+            fPrevNow = now;
+            double delta = fAngleSign * fAngleSpeed * dtime;
+            fAngle += delta;
+            fAngleSpeed -= kSlowDown * dtime;
+            if (fAngleSpeed < 0) {
+                fAngleSpeed = 0;
+            }
+        }
+        return SkM44::Rotate(fAxis, fAngle);
+
+    }
+
+    void start() {
+        if (fPrevNow != fNow) {
+            fAngleSpeed = (fAngle - fPrevAngle) / (fNow - fPrevNow);
+            fAngleSign = fAngleSpeed < 0 ? -1 : 1;
+            fAngleSpeed = std::min(kMaxSpeed, std::abs(fAngleSpeed));
+        } else {
+            fAngleSpeed = 0;
+        }
+        fPrevNow = SkTime::GetSecs();
+        fAngle = 0;
+    }
+
+    void reset() {
+        fAngleSpeed = 0;
+        fAngle = 0;
+        fPrevAngle = 1234567;
+    }
+
+    bool isAnimating() const { return fAngleSpeed != 0; }
+};
+
+class SampleCubeBase : public Sample3DView {
+    enum {
+        DX = 400,
+        DY = 300
+    };
+
+    SkM44 fRotation;        // part of model
+
+    RotateAnimator fRotateAnimator;
+
+protected:
+    enum Flags {
+        kCanRunOnCPU    = 1 << 0,
+        kShowLightDome  = 1 << 1,
+    };
+
+    LightOnSphere fLight = {{200 + DX, 200 + DY}, 800, 12};
+
+    VSphere fSphere;
+    Flags   fFlags;
+
+public:
+    SampleCubeBase(Flags flags)
+        : fSphere({200 + DX, 200 + DY}, 400)
+        , fFlags(flags)
+    {}
+
+    bool onChar(SkUnichar uni) override {
+        switch (uni) {
+            case 'Z': fLight.fDistance += 10; return true;
+            case 'z': fLight.fDistance -= 10; return true;
+        }
+        return this->Sample3DView::onChar(uni);
+    }
+
+    virtual void drawContent(
+            SkCanvas* canvas, SkColor, int index, bool drawFront, const SkM44& localToWorld) = 0;
+
+    void onDrawContent(SkCanvas* canvas) override {
+        if (!canvas->recordingContext() && !(fFlags & kCanRunOnCPU)) {
+            return;
+        }
+
+        canvas->save();
+        canvas->translate(DX, DY);
+
+        this->concatCamera(canvas, {0, 0, 400, 400}, 200);
+
+        for (bool drawFront : {false, true}) {
+            int index = 0;
+            for (auto f : faces) {
+                SkAutoCanvasRestore acr(canvas, true);
+
+                SkM44 trans = SkM44::Translate(200, 200, 0);   // center of the rotation
+                SkM44 m = fRotateAnimator.rotation() * fRotation * f.asM44(200);
+
+                canvas->concat(trans);
+
+                // "World" space - content is centered at the origin, in device scale (+-200)
+                SkM44 localToWorld = m * inv(trans);
+
+                canvas->concat(localToWorld);
+                this->drawContent(canvas, f.fColor, index++, drawFront, localToWorld);
+            }
+        }
+
+        canvas->restore();  // camera & center the content in the window
+
+        if (fFlags & kShowLightDome){
+            fLight.draw(canvas);
+
+            SkPaint paint;
+            paint.setAntiAlias(true);
+            paint.setStyle(SkPaint::kStroke_Style);
+            paint.setColor(0x40FF0000);
+            canvas->drawCircle(fSphere.fCenter.x, fSphere.fCenter.y, fSphere.fRadius, paint);
+            canvas->drawLine(fSphere.fCenter.x, fSphere.fCenter.y - fSphere.fRadius,
+                             fSphere.fCenter.x, fSphere.fCenter.y + fSphere.fRadius, paint);
+            canvas->drawLine(fSphere.fCenter.x - fSphere.fRadius, fSphere.fCenter.y,
+                             fSphere.fCenter.x + fSphere.fRadius, fSphere.fCenter.y, paint);
+        }
+    }
+
+    Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
+        SkV2 p = fLight.fLoc - SkV2{x, y};
+        if (p.length() <= fLight.fRadius) {
+            Click* c = new Click();
+            c->fMeta.setS32("type", 0);
+            return c;
+        }
+        if (fSphere.contains({x, y})) {
+            Click* c = new Click();
+            c->fMeta.setS32("type", 1);
+
+            fRotation = fRotateAnimator.rotation() * fRotation;
+            fRotateAnimator.reset();
+            return c;
+        }
+        return nullptr;
+    }
+    bool onClick(Click* click) override {
+        if (click->fMeta.hasS32("type", 0)) {
+            fLight.fLoc = fSphere.pinLoc({click->fCurr.fX, click->fCurr.fY});
+            return true;
+        }
+        if (click->fMeta.hasS32("type", 1)) {
+            if (click->fState == skui::InputState::kUp) {
+                fRotation = fRotateAnimator.rotation() * fRotation;
+                fRotateAnimator.start();
+            } else {
+                auto [axis, angle] = fSphere.computeRotationInfo(
+                                                {click->fOrig.fX, click->fOrig.fY},
+                                                {click->fCurr.fX, click->fCurr.fY});
+                fRotateAnimator.update(axis, angle);
+            }
+            return true;
+        }
+        return true;
+    }
+
+    bool onAnimate(double nanos) override {
+        return fRotateAnimator.isAnimating();
+    }
+
+private:
+    using INHERITED = Sample3DView;
+};
+
+class SampleBump3D : public SampleCubeBase {
+    sk_sp<SkShader>        fBmpShader, fImgShader;
+    sk_sp<SkRuntimeEffect> fEffect;
+    SkRRect                fRR;
+
+public:
+    SampleBump3D() : SampleCubeBase(Flags(kCanRunOnCPU | kShowLightDome)) {}
+
+    SkString name() override { return SkString("bump3d"); }
+
+    void onOnceBeforeDraw() override {
+        fRR = SkRRect::MakeRectXY({20, 20, 380, 380}, 50, 50);
+        auto img = GetResourceAsImage("images/brickwork-texture.jpg");
+        fImgShader = img->makeShader(SkSamplingOptions(), SkMatrix::Scale(2, 2));
+        img = GetResourceAsImage("images/brickwork_normal-map.jpg");
+        fBmpShader = img->makeShader(SkSamplingOptions(), SkMatrix::Scale(2, 2));
+
+        const char code[] = R"(
+            uniform shader color_map;
+            uniform shader normal_map;
+
+            uniform float4x4 localToWorld;
+            uniform float4x4 localToWorldAdjInv;
+            uniform float3   lightPos;
+
+            float3 convert_normal_sample(half4 c) {
+                float3 n = 2 * c.rgb - 1;
+                n.y = -n.y;
+                return n;
+            }
+
+            half4 main(float2 p) {
+                float3 norm = convert_normal_sample(normal_map.eval(p));
+                float3 plane_norm = normalize(localToWorldAdjInv * norm.xyz0).xyz;
+
+                float3 plane_pos = (localToWorld * p.xy01).xyz;
+                float3 light_dir = normalize(lightPos - plane_pos);
+
+                float ambient = 0.2;
+                float dp = dot(plane_norm, light_dir);
+                float scale = min(ambient + max(dp, 0), 1);
+
+                return color_map.eval(p) * scale.xxx1;
+            }
+        )";
+        auto [effect, error] = SkRuntimeEffect::MakeForShader(SkString(code));
+        if (!effect) {
+            SkDebugf("runtime error %s\n", error.c_str());
+        }
+        fEffect = effect;
+    }
+
+    void drawContent(SkCanvas* canvas,
+                     SkColor color,
+                     int index,
+                     bool drawFront,
+                     const SkM44& localToWorld) override {
+        if (!drawFront || !front(canvas->getLocalToDevice())) {
+            return;
+        }
+
+        SkRuntimeShaderBuilder builder(fEffect);
+        builder.uniform("lightPos") = fLight.computeWorldPos(fSphere);
+        builder.uniform("localToWorld") = localToWorld;
+        builder.uniform("localToWorldAdjInv") = normals(localToWorld);
+
+        builder.child("color_map")  = fImgShader;
+        builder.child("normal_map") = fBmpShader;
+
+        SkPaint paint;
+        paint.setColor(color);
+        paint.setShader(builder.makeShader());
+
+        canvas->drawRRect(fRR, paint);
+    }
+};
+DEF_SAMPLE( return new SampleBump3D; )
+
+#include "modules/skottie/include/Skottie.h"
+
+class SampleSkottieCube : public SampleCubeBase {
+    sk_sp<skottie::Animation> fAnim[6];
+
+public:
+    SampleSkottieCube() : SampleCubeBase(kCanRunOnCPU) {}
+
+    SkString name() override { return SkString("skottie3d"); }
+
+    void onOnceBeforeDraw() override {
+        const char* files[] = {
+            "skottie/skottie-chained-mattes.json",
+            "skottie/skottie-gradient-ramp.json",
+            "skottie/skottie_sample_2.json",
+            "skottie/skottie-3d-3planes.json",
+            "skottie/skottie-text-animator-4.json",
+            "skottie/skottie-motiontile-effect-phase.json",
+
+        };
+        for (unsigned i = 0; i < SK_ARRAY_COUNT(files); ++i) {
+            if (auto stream = GetResourceAsStream(files[i])) {
+                fAnim[i] = skottie::Animation::Make(stream.get());
+            }
+        }
+    }
+
+    void drawContent(
+            SkCanvas* canvas, SkColor color, int index, bool drawFront, const SkM44&) override {
+        if (!drawFront || !front(canvas->getLocalToDevice())) {
+            return;
+        }
+
+        SkPaint paint;
+        paint.setColor(color);
+        SkRect r = {0, 0, 400, 400};
+        canvas->drawRect(r, paint);
+        fAnim[index]->render(canvas, &r);
+    }
+
+    bool onAnimate(double nanos) override {
+        for (auto& anim : fAnim) {
+            SkScalar dur = anim->duration();
+            SkScalar t = fmod(1e-9 * nanos, dur) / dur;
+            anim->seek(t);
+        }
+        return true;
+    }
+};
+DEF_SAMPLE( return new SampleSkottieCube; )
diff --git a/third_party/skia/samplecode/SampleAAClip.cpp b/third_party/skia/samplecode/SampleAAClip.cpp
deleted file mode 100644
index 46e4fe2..0000000
--- a/third_party/skia/samplecode/SampleAAClip.cpp
+++ /dev/null
@@ -1,106 +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 "include/core/SkCanvas.h"
-#include "include/core/SkPath.h"
-#include "samplecode/Sample.h"
-#include "src/core/SkAAClip.h"
-
-static void testop(const SkIRect& r0, const SkIRect& r1, SkRegion::Op op,
-                   const SkIRect& expectedR) {
-    SkAAClip c0, c1, c2;
-    c0.setRect(r0);
-    c1.setRect(r1);
-    c2.op(c0, c1, op);
-
-    SkDEBUGCODE(SkIRect r2 = c2.getBounds());
-    SkASSERT(r2 == expectedR);
-}
-
-static const struct {
-    SkIRect r0;
-    SkIRect r1;
-    SkRegion::Op op;
-    SkIRect expectedR;
-} gRec[] = {
-    {{ 1, 2, 9, 3 }, { -3, 2, 5, 11 }, SkRegion::kDifference_Op, { 5, 2, 9, 3 }},
-    {{ 1, 10, 5, 13 }, { 1, 2, 5, 11 }, SkRegion::kDifference_Op, { 1, 11, 5, 13 }},
-    {{ 1, 10, 5, 13 }, { 1, 2, 5, 11 }, SkRegion::kReverseDifference_Op, { 1, 2, 5, 10 }},
-};
-
-static void testop() {
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
-        testop(gRec[i].r0, gRec[i].r1, gRec[i].op, gRec[i].expectedR);
-    }
-}
-
-static void drawClip(SkCanvas* canvas, const SkAAClip& clip) {
-    SkMask mask;
-    SkBitmap bm;
-
-    clip.copyToMask(&mask);
-    SkAutoMaskFreeImage amfi(mask.fImage);
-
-    bm.installMaskPixels(mask);
-
-    SkPaint paint;
-    canvas->drawBitmap(bm,
-                       SK_Scalar1 * mask.fBounds.fLeft,
-                       SK_Scalar1 * mask.fBounds.fTop,
-                       &paint);
-}
-
-class AAClipView : public Sample {
-    SkString name() override { return SkString("AAClip"); }
-
-    void onOnceBeforeDraw() override { testop(); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-#if 1
-        SkAAClip aaclip;
-        SkPath path;
-        SkRect bounds;
-
-        bounds.setLTRB(0, 0, 20, 20);
-        bounds.inset(SK_ScalarHalf, SK_ScalarHalf);
-
-//        path.addRect(bounds);
-//        path.addOval(bounds);
-        path.addRoundRect(bounds, 4, 4);
-        aaclip.setPath(path);
-        canvas->translate(30, 30);
-        drawClip(canvas, aaclip);
-
-        SkAAClip aaclip2;
-        path.offset(10, 10);
-        aaclip2.setPath(path);
-        canvas->translate(30, 0);
-        drawClip(canvas, aaclip2);
-
-        SkAAClip aaclip3;
-        aaclip3.op(aaclip, aaclip2, SkRegion::kIntersect_Op);
-        canvas->translate(30, 0);
-        drawClip(canvas, aaclip3);
-
-#endif
-
-#if 0
-        SkRect r;
-        r.set(0, 0, this->width(), this->height());
-        r.inset(20, 20);
-        canvas->clipRect(r);
-
-        SkPath path;
-        path.addRect(r);
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setColor(SK_ColorRED);
-        canvas->drawPath(path, paint);
-#endif
-    }
-};
-DEF_SAMPLE( return new AAClipView(); )
diff --git a/third_party/skia/samplecode/SampleAAGeometry.cpp b/third_party/skia/samplecode/SampleAAGeometry.cpp
deleted file mode 100644
index b2aa97d..0000000
--- a/third_party/skia/samplecode/SampleAAGeometry.cpp
+++ /dev/null
@@ -1,1847 +0,0 @@
-/*
- * 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 "include/core/SkBitmap.h"
-#include "include/core/SkCanvas.h"
-#include "include/core/SkString.h"
-#include "include/private/SkMacros.h"
-#include "include/utils/SkTextUtils.h"
-#include "samplecode/Sample.h"
-#include "src/core/SkGeometry.h"
-#include "src/core/SkPointPriv.h"
-#include "src/pathops/SkIntersections.h"
-#include "src/pathops/SkOpEdgeBuilder.h"
-
-#if 0
-void SkStrokeSegment::dump() const {
-    SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}", fPts[0].fX, fPts[0].fY, fPts[1].fX, fPts[1].fY);
-    if (SkPath::kQuad_Verb == fVerb) {
-        SkDebugf(", {%1.9g,%1.9g}", fPts[2].fX, fPts[2].fY);
-    }
-    SkDebugf("}}");
-#ifdef SK_DEBUG
-    SkDebugf(" id=%d", fDebugID);
-#endif
-    SkDebugf("\n");
-}
-
-void SkStrokeSegment::dumpAll() const {
-    const SkStrokeSegment* segment = this;
-    while (segment) {
-        segment->dump();
-        segment = segment->fNext;
-    }
-}
-
-void SkStrokeTriple::dump() const {
-    SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}", fPts[0].fX, fPts[0].fY, fPts[1].fX, fPts[1].fY);
-    if (SkPath::kQuad_Verb <= fVerb) {
-        SkDebugf(", {%1.9g,%1.9g}", fPts[2].fX, fPts[2].fY);
-    }
-    if (SkPath::kCubic_Verb == fVerb) {
-        SkDebugf(", {%1.9g,%1.9g}", fPts[3].fX, fPts[3].fY);
-    } else if (SkPath::kConic_Verb == fVerb) {
-        SkDebugf(", %1.9g", weight());
-    }
-    SkDebugf("}}");
-#ifdef SK_DEBUG
-    SkDebugf(" triple id=%d", fDebugID);
-#endif
-    SkDebugf("\ninner:\n");
-    fInner->dumpAll();
-    SkDebugf("outer:\n");
-    fOuter->dumpAll();
-    SkDebugf("join:\n");
-    fJoin->dumpAll();
-}
-
-void SkStrokeTriple::dumpAll() const {
-    const SkStrokeTriple* triple = this;
-    while (triple) {
-        triple->dump();
-        triple = triple->fNext;
-    }
-}
-
-void SkStrokeContour::dump() const {
-#ifdef SK_DEBUG
-    SkDebugf("id=%d ", fDebugID);
-#endif
-    SkDebugf("head:\n");
-    fHead->dumpAll();
-    SkDebugf("head cap:\n");
-    fHeadCap->dumpAll();
-    SkDebugf("tail cap:\n");
-    fTailCap->dumpAll();
-}
-
-void SkStrokeContour::dumpAll() const {
-    const SkStrokeContour* contour = this;
-    while (contour) {
-        contour->dump();
-        contour = contour->fNext;
-    }
-}
-#endif
-
-SkScalar gCurveDistance = 10;
-
-#if 0  // unused
-static SkPath::Verb get_path_verb(int index, const SkPath& path) {
-    if (index < 0) {
-        return SkPath::kMove_Verb;
-    }
-    SkPoint pts[4];
-    SkPath::Verb verb;
-    SkPath::Iter iter(path, true);
-    int counter = -1;
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-        if (++counter < index) {
-            continue;
-        }
-        return verb;
-    }
-    SkASSERT(0);
-    return SkPath::kMove_Verb;
-}
-#endif
-
-static SkScalar get_path_weight(int index, const SkPath& path) {
-    SkPoint pts[4];
-    SkPath::Verb verb;
-    SkPath::Iter iter(path, true);
-    int counter = -1;
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-        if (++counter < index) {
-            continue;
-        }
-        return verb == SkPath::kConic_Verb ? iter.conicWeight() : 1;
-    }
-    SkASSERT(0);
-    return 0;
-}
-
-static void set_path_pt(int index, const SkPoint& pt, SkPath* path) {
-    SkPath result;
-    SkPoint pts[4];
-    SkPath::Verb verb;
-    SkPath::RawIter iter(*path);
-    int startIndex = 0;
-    int endIndex = 0;
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-        switch (verb) {
-            case SkPath::kMove_Verb:
-                endIndex += 1;
-                break;
-            case SkPath::kLine_Verb:
-                endIndex += 1;
-                break;
-            case SkPath::kQuad_Verb:
-            case SkPath::kConic_Verb:
-                endIndex += 2;
-                break;
-            case SkPath::kCubic_Verb:
-                endIndex += 3;
-                break;
-            case SkPath::kClose_Verb:
-                break;
-            case SkPath::kDone_Verb:
-                break;
-            default:
-                SkASSERT(0);
-        }
-        if (startIndex <= index && index < endIndex) {
-            pts[index - startIndex] = pt;
-            index = -1;
-        }
-        switch (verb) {
-            case SkPath::kMove_Verb:
-                result.moveTo(pts[0]);
-                break;
-            case SkPath::kLine_Verb:
-                result.lineTo(pts[1]);
-                startIndex += 1;
-                break;
-            case SkPath::kQuad_Verb:
-                result.quadTo(pts[1], pts[2]);
-                startIndex += 2;
-                break;
-            case SkPath::kConic_Verb:
-                result.conicTo(pts[1], pts[2], iter.conicWeight());
-                startIndex += 2;
-                break;
-            case SkPath::kCubic_Verb:
-                result.cubicTo(pts[1], pts[2], pts[3]);
-                startIndex += 3;
-                break;
-            case SkPath::kClose_Verb:
-                result.close();
-                startIndex += 1;
-                break;
-            case SkPath::kDone_Verb:
-                break;
-            default:
-                SkASSERT(0);
-        }
-    }
-#if 0
-    SkDebugf("\n\noriginal\n");
-    path->dump();
-    SkDebugf("\nedited\n");
-    result.dump();
-#endif
-    *path = result;
-}
-
-static void add_path_segment(int index, SkPath* path) {
-    SkPath result;
-    SkPoint pts[4];
-    SkPoint firstPt = { 0, 0 };  // init to avoid warning
-    SkPoint lastPt = { 0, 0 };  // init to avoid warning
-    SkPath::Verb verb;
-    SkPath::RawIter iter(*path);
-    int counter = -1;
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-        SkScalar weight  SK_INIT_TO_AVOID_WARNING;
-        if (++counter == index) {
-            switch (verb) {
-                case SkPath::kLine_Verb:
-                    result.lineTo((pts[0].fX + pts[1].fX) / 2, (pts[0].fY + pts[1].fY) / 2);
-                    break;
-                case SkPath::kQuad_Verb: {
-                    SkPoint chop[5];
-                    SkChopQuadAtHalf(pts, chop);
-                    result.quadTo(chop[1], chop[2]);
-                    pts[1] = chop[3];
-                    } break;
-                case SkPath::kConic_Verb: {
-                    SkConic chop[2];
-                    SkConic conic;
-                    conic.set(pts, iter.conicWeight());
-                    if (!conic.chopAt(0.5f, chop)) {
-                        return;
-                    }
-                    result.conicTo(chop[0].fPts[1], chop[0].fPts[2], chop[0].fW);
-                    pts[1] = chop[1].fPts[1];
-                    weight = chop[1].fW;
-                    } break;
-                case SkPath::kCubic_Verb: {
-                    SkPoint chop[7];
-                    SkChopCubicAtHalf(pts, chop);
-                    result.cubicTo(chop[1], chop[2], chop[3]);
-                    pts[1] = chop[4];
-                    pts[2] = chop[5];
-                    } break;
-                case SkPath::kClose_Verb: {
-                    result.lineTo((lastPt.fX + firstPt.fX) / 2, (lastPt.fY + firstPt.fY) / 2);
-                    } break;
-                default:
-                    SkASSERT(0);
-            }
-        } else if (verb == SkPath::kConic_Verb) {
-            weight = iter.conicWeight();
-        }
-        switch (verb) {
-            case SkPath::kMove_Verb:
-                result.moveTo(firstPt = pts[0]);
-                break;
-            case SkPath::kLine_Verb:
-                result.lineTo(lastPt = pts[1]);
-                break;
-            case SkPath::kQuad_Verb:
-                result.quadTo(pts[1], lastPt = pts[2]);
-                break;
-            case SkPath::kConic_Verb:
-                result.conicTo(pts[1], lastPt = pts[2], weight);
-                break;
-            case SkPath::kCubic_Verb:
-                result.cubicTo(pts[1], pts[2], lastPt = pts[3]);
-                break;
-            case SkPath::kClose_Verb:
-                result.close();
-                break;
-            case SkPath::kDone_Verb:
-                break;
-            default:
-                SkASSERT(0);
-        }
-    }
-    *path = result;
-}
-
-static void delete_path_segment(int index, SkPath* path) {
-    SkPath result;
-    SkPoint pts[4];
-    SkPath::Verb verb;
-    SkPath::RawIter iter(*path);
-    int counter = -1;
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-        if (++counter == index) {
-            continue;
-        }
-        switch (verb) {
-            case SkPath::kMove_Verb:
-                result.moveTo(pts[0]);
-                break;
-            case SkPath::kLine_Verb:
-                result.lineTo(pts[1]);
-                break;
-            case SkPath::kQuad_Verb:
-                result.quadTo(pts[1], pts[2]);
-                break;
-            case SkPath::kConic_Verb:
-                result.conicTo(pts[1], pts[2], iter.conicWeight());
-                break;
-            case SkPath::kCubic_Verb:
-                result.cubicTo(pts[1], pts[2], pts[3]);
-                break;
-            case SkPath::kClose_Verb:
-                result.close();
-                break;
-            case SkPath::kDone_Verb:
-                break;
-            default:
-                SkASSERT(0);
-        }
-    }
-    *path = result;
-}
-
-static void set_path_weight(int index, SkScalar w, SkPath* path) {
-    SkPath result;
-    SkPoint pts[4];
-    SkPath::Verb verb;
-    SkPath::Iter iter(*path, true);
-    int counter = -1;
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-        ++counter;
-        switch (verb) {
-            case SkPath::kMove_Verb:
-                result.moveTo(pts[0]);
-                break;
-            case SkPath::kLine_Verb:
-                result.lineTo(pts[1]);
-                break;
-            case SkPath::kQuad_Verb:
-                result.quadTo(pts[1], pts[2]);
-                break;
-            case SkPath::kConic_Verb:
-                result.conicTo(pts[1], pts[2], counter == index ? w : iter.conicWeight());
-                break;
-            case SkPath::kCubic_Verb:
-                result.cubicTo(pts[1], pts[2], pts[3]);
-                break;
-            case SkPath::kClose_Verb:
-                result.close();
-                break;
-            case SkPath::kDone_Verb:
-                break;
-            default:
-                SkASSERT(0);
-        }
-    }
-    *path = result;
-}
-
-static void set_path_verb(int index, SkPath::Verb v, SkPath* path, SkScalar w) {
-    SkASSERT(SkPath::kLine_Verb <= v && v <= SkPath::kCubic_Verb);
-    SkPath result;
-    SkPoint pts[4];
-    SkPath::Verb verb;
-    SkPath::Iter iter(*path, true);
-    int counter = -1;
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-        SkScalar weight = verb == SkPath::kConic_Verb ? iter.conicWeight() : 1;
-        if (++counter == index && v != verb) {
-            SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb);
-            switch (verb) {
-                case SkPath::kLine_Verb:
-                    switch (v) {
-                        case SkPath::kConic_Verb:
-                            weight = w;
-                        case SkPath::kQuad_Verb:
-                            pts[2] = pts[1];
-                            pts[1].fX = (pts[0].fX + pts[2].fX) / 2;
-                            pts[1].fY = (pts[0].fY + pts[2].fY) / 2;
-                            break;
-                        case SkPath::kCubic_Verb:
-                            pts[3] = pts[1];
-                            pts[1].fX = (pts[0].fX * 2 + pts[3].fX) / 3;
-                            pts[1].fY = (pts[0].fY * 2 + pts[3].fY) / 3;
-                            pts[2].fX = (pts[0].fX + pts[3].fX * 2) / 3;
-                            pts[2].fY = (pts[0].fY + pts[3].fY * 2) / 3;
-                            break;
-                         default:
-                            SkASSERT(0);
-                            break;
-                    }
-                    break;
-                case SkPath::kQuad_Verb:
-                case SkPath::kConic_Verb:
-                    switch (v) {
-                        case SkPath::kLine_Verb:
-                            pts[1] = pts[2];
-                            break;
-                        case SkPath::kConic_Verb:
-                            weight = w;
-                        case SkPath::kQuad_Verb:
-                            break;
-                        case SkPath::kCubic_Verb: {
-                            SkDQuad dQuad;
-                            dQuad.set(pts);
-                            SkDCubic dCubic = dQuad.debugToCubic();
-                            pts[3] = pts[2];
-                            pts[1] = dCubic[1].asSkPoint();
-                            pts[2] = dCubic[2].asSkPoint();
-                            } break;
-                         default:
-                            SkASSERT(0);
-                            break;
-                    }
-                    break;
-                case SkPath::kCubic_Verb:
-                    switch (v) {
-                        case SkPath::kLine_Verb:
-                            pts[1] = pts[3];
-                            break;
-                        case SkPath::kConic_Verb:
-                            weight = w;
-                        case SkPath::kQuad_Verb: {
-                            SkDCubic dCubic;
-                            dCubic.set(pts);
-                            SkDQuad dQuad = dCubic.toQuad();
-                            pts[1] = dQuad[1].asSkPoint();
-                            pts[2] = pts[3];
-                            } break;
-                        default:
-                            SkASSERT(0);
-                            break;
-                    }
-                    break;
-                default:
-                    SkASSERT(0);
-                    break;
-            }
-            verb = v;
-        }
-        switch (verb) {
-            case SkPath::kMove_Verb:
-                result.moveTo(pts[0]);
-                break;
-            case SkPath::kLine_Verb:
-                result.lineTo(pts[1]);
-                break;
-            case SkPath::kQuad_Verb:
-                result.quadTo(pts[1], pts[2]);
-                break;
-            case SkPath::kConic_Verb:
-                result.conicTo(pts[1], pts[2], weight);
-                break;
-            case SkPath::kCubic_Verb:
-                result.cubicTo(pts[1], pts[2], pts[3]);
-                break;
-            case SkPath::kClose_Verb:
-                result.close();
-                break;
-            default:
-                SkASSERT(0);
-                break;
-        }
-    }
-    *path = result;
-}
-
-static void add_to_map(SkScalar coverage, int x, int y, uint8_t* distanceMap, int w, int h) {
-    int byteCoverage = (int) (coverage * 256);
-    if (byteCoverage < 0) {
-        byteCoverage = 0;
-    } else if (byteCoverage > 255) {
-        byteCoverage = 255;
-    }
-    SkASSERT(x < w);
-    SkASSERT(y < h);
-    distanceMap[y * w + x] = SkTMax(distanceMap[y * w + x], (uint8_t) byteCoverage);
-}
-
-static void filter_coverage(const uint8_t* map, int len, uint8_t min, uint8_t max,
-        uint8_t* filter) {
-    for (int index = 0; index < len; ++index) {
-        uint8_t in = map[index];
-        filter[index] = in < min ? 0 : max < in ? 0 : in;
-    }
-}
-
-static void construct_path(SkPath& path) {
-    path.reset();
-    path.moveTo(442, 101.5f);
-    path.quadTo(413.5f, 691, 772, 514);
-    path.lineTo(346, 721.5f);
-    path.lineTo(154, 209);
-    path.lineTo(442, 101.5f);
-    path.close();
-}
-
-struct ButtonPaints {
-    static const int kMaxStateCount = 3;
-    SkPaint fDisabled;
-    SkPaint fStates[kMaxStateCount];
-    SkFont  fLabelFont;
-
-    ButtonPaints() {
-        fStates[0].setAntiAlias(true);
-        fStates[0].setStyle(SkPaint::kStroke_Style);
-        fStates[0].setColor(0xFF3F0000);
-        fStates[1] = fStates[0];
-        fStates[1].setStrokeWidth(3);
-        fStates[2] = fStates[1];
-        fStates[2].setColor(0xFFcf0000);
-        fLabelFont.setSize(25.0f);
-    }
-};
-
-struct Button {
-    SkRect fBounds;
-    int fStateCount;
-    int fState;
-    char fLabel;
-    bool fVisible;
-
-    Button(char label) {
-        fStateCount = 2;
-        fState = 0;
-        fLabel = label;
-        fVisible = false;
-    }
-
-    Button(char label, int stateCount) {
-        SkASSERT(stateCount <= ButtonPaints::kMaxStateCount);
-        fStateCount = stateCount;
-        fState = 0;
-        fLabel = label;
-        fVisible = false;
-    }
-
-    bool contains(const SkRect& rect) {
-        return fVisible && fBounds.contains(rect);
-    }
-
-    bool enabled() {
-        return SkToBool(fState);
-    }
-
-    void draw(SkCanvas* canvas, const ButtonPaints& paints) {
-        if (!fVisible) {
-            return;
-        }
-        canvas->drawRect(fBounds, paints.fStates[fState]);
-        SkTextUtils::Draw(canvas, &fLabel, 1, SkTextEncoding::kUTF8, fBounds.centerX(), fBounds.fBottom - 5,
-                          paints.fLabelFont, SkPaint(), SkTextUtils::kCenter_Align);
-    }
-
-    void toggle() {
-        if (++fState == fStateCount) {
-            fState = 0;
-        }
-    }
-
-    void setEnabled(bool enabled) {
-        fState = (int) enabled;
-    }
-};
-
-struct ControlPaints {
-    SkPaint fOutline;
-    SkPaint fIndicator;
-    SkPaint fFill;
-    SkPaint fLabel;
-    SkPaint fValue;
-
-    SkFont fLabelFont;
-    SkFont fValueFont;
-
-    ControlPaints() {
-        fOutline.setAntiAlias(true);
-        fOutline.setStyle(SkPaint::kStroke_Style);
-        fIndicator = fOutline;
-        fIndicator.setColor(SK_ColorRED);
-        fFill.setAntiAlias(true);
-        fFill.setColor(0x7fff0000);
-        fLabel.setAntiAlias(true);
-        fLabelFont.setSize(13.0f);
-        fValue.setAntiAlias(true);
-        fValueFont.setSize(11.0f);
-    }
-};
-
-struct UniControl {
-    SkString fName;
-    SkRect fBounds;
-    SkScalar fMin;
-    SkScalar fMax;
-    SkScalar fValLo;
-    SkScalar fYLo;
-    bool fVisible;
-
-    UniControl(const char* name, SkScalar min, SkScalar max) {
-        fName = name;
-        fValLo =  fMin = min;
-        fMax = max;
-        fVisible = false;
-
-    }
-
-    virtual ~UniControl() {}
-
-    bool contains(const SkRect& rect) {
-        return fVisible && fBounds.contains(rect);
-    }
-
-    virtual void draw(SkCanvas* canvas, const ControlPaints& paints) {
-        if (!fVisible) {
-            return;
-        }
-        canvas->drawRect(fBounds, paints.fOutline);
-        fYLo = fBounds.fTop + (fValLo - fMin) * fBounds.height() / (fMax - fMin);
-        canvas->drawLine(fBounds.fLeft - 5, fYLo, fBounds.fRight + 5, fYLo, paints.fIndicator);
-        SkString label;
-        label.printf("%0.3g", fValLo);
-        canvas->drawString(label, fBounds.fLeft + 5, fYLo - 5, paints.fValueFont, paints.fValue);
-        canvas->drawString(fName, fBounds.fLeft, fBounds.bottom() + 11, paints.fLabelFont,
-                           paints.fLabel);
-    }
-};
-
-struct BiControl : public UniControl {
-    SkScalar fValHi;
-
-    BiControl(const char* name, SkScalar min, SkScalar max)
-        : UniControl(name, min, max)
-        ,  fValHi(fMax) {
-    }
-
-    virtual ~BiControl() {}
-
-    virtual void draw(SkCanvas* canvas, const ControlPaints& paints) {
-        UniControl::draw(canvas, paints);
-        if (!fVisible || fValHi == fValLo) {
-            return;
-        }
-        SkScalar yPos = fBounds.fTop + (fValHi - fMin) * fBounds.height() / (fMax - fMin);
-        canvas->drawLine(fBounds.fLeft - 5, yPos, fBounds.fRight + 5, yPos, paints.fIndicator);
-        SkString label;
-        label.printf("%0.3g", fValHi);
-        if (yPos < fYLo + 10) {
-            yPos = fYLo + 10;
-        }
-        canvas->drawString(label, fBounds.fLeft + 5, yPos - 5, paints.fValueFont, paints.fValue);
-        SkRect fill = { fBounds.fLeft, fYLo, fBounds.fRight, yPos };
-        canvas->drawRect(fill, paints.fFill);
-    }
-};
-
-
-class MyClick : public Sample::Click {
-public:
-    enum ClickType {
-        kInvalidType = -1,
-        kPtType,
-        kVerbType,
-        kControlType,
-        kPathType,
-    } fType;
-
-    enum ControlType {
-        kInvalidControl = -1,
-        kFirstControl,
-        kFilterControl = kFirstControl,
-        kResControl,
-        kWeightControl,
-        kWidthControl,
-        kLastControl = kWidthControl,
-        kFirstButton,
-        kCubicButton = kFirstButton,
-        kConicButton,
-        kQuadButton,
-        kLineButton,
-        kLastVerbButton = kLineButton,
-        kAddButton,
-        kDeleteButton,
-        kInOutButton,
-        kFillButton,
-        kSkeletonButton,
-        kFilterButton,
-        kBisectButton,
-        kJoinButton,
-        kLastButton = kJoinButton,
-        kPathMove,
-    } fControl;
-
-    SkPath::Verb fVerb;
-    SkScalar fWeight;
-
-    MyClick(ClickType type, ControlType control)
-        : fType(type)
-        , fControl(control)
-        , fVerb((SkPath::Verb) -1)
-        , fWeight(1) {
-    }
-
-    MyClick(ClickType type, int index)
-        : fType(type)
-        , fControl((ControlType) index)
-        , fVerb((SkPath::Verb) -1)
-        , fWeight(1) {
-    }
-
-    MyClick(ClickType type, int index, SkPath::Verb verb, SkScalar weight)
-        : fType(type)
-        , fControl((ControlType) index)
-        , fVerb(verb)
-        , fWeight(weight) {
-    }
-
-    bool isButton() {
-        return kFirstButton <= fControl && fControl <= kLastButton;
-    }
-
-    int ptHit() const {
-        SkASSERT(fType == kPtType);
-        return (int) fControl;
-    }
-
-    int verbHit() const {
-        SkASSERT(fType == kVerbType);
-        return (int) fControl;
-    }
-};
-
-enum {
-    kControlCount = MyClick::kLastControl - MyClick::kFirstControl + 1,
-};
-
-static struct ControlPair {
-    UniControl* fControl;
-    MyClick::ControlType fControlType;
-} kControlList[kControlCount];
-
-enum {
-    kButtonCount = MyClick::kLastButton - MyClick::kFirstButton + 1,
-    kVerbCount = MyClick::kLastVerbButton - MyClick::kFirstButton + 1,
-};
-
-static struct ButtonPair {
-    Button* fButton;
-    MyClick::ControlType fButtonType;
-} kButtonList[kButtonCount];
-
-static void enable_verb_button(MyClick::ControlType type) {
-    for (int index = 0; index < kButtonCount; ++index) {
-        MyClick::ControlType testType = kButtonList[index].fButtonType;
-        if (MyClick::kFirstButton <= testType && testType <= MyClick::kLastVerbButton) {
-            Button* button = kButtonList[index].fButton;
-            button->setEnabled(testType == type);
-        }
-    }
-}
-
-struct Stroke;
-
-struct Active {
-    Active* fNext;
-    Stroke* fParent;
-    SkScalar fStart;
-    SkScalar fEnd;
-
-    void reset() {
-        fNext = nullptr;
-        fStart = 0;
-        fEnd = 1;
-    }
-};
-
-struct Stroke {
-    SkPath fPath;
-    Active fActive;
-    bool fInner;
-
-    void reset() {
-        fPath.reset();
-        fActive.reset();
-    }
-};
-
-struct PathUndo {
-    SkPath fPath;
-    std::unique_ptr<PathUndo> fNext;
-};
-
-class AAGeometryView : public Sample {
-    SkPaint fActivePaint;
-    SkPaint fComplexPaint;
-    SkPaint fCoveragePaint;
-    SkFont fLegendLeftFont;
-    SkFont fLegendRightFont;
-    SkPaint fPointPaint;
-    SkPaint fSkeletonPaint;
-    SkPaint fLightSkeletonPaint;
-    SkPath fPath;
-    ControlPaints fControlPaints;
-    UniControl fResControl;
-    UniControl fWeightControl;
-    UniControl fWidthControl;
-    BiControl fFilterControl;
-    ButtonPaints fButtonPaints;
-    Button fCubicButton;
-    Button fConicButton;
-    Button fQuadButton;
-    Button fLineButton;
-    Button fAddButton;
-    Button fDeleteButton;
-    Button fFillButton;
-    Button fSkeletonButton;
-    Button fFilterButton;
-    Button fBisectButton;
-    Button fJoinButton;
-    Button fInOutButton;
-    SkTArray<Stroke> fStrokes;
-    std::unique_ptr<PathUndo> fUndo;
-    int fActivePt;
-    int fActiveVerb;
-    bool fHandlePathMove;
-    bool fShowLegend;
-    bool fHideAll;
-    const int kHitToleranace = 25;
-
-public:
-
-    AAGeometryView()
-        : fResControl("error", 0, 10)
-        , fWeightControl("weight", 0, 5)
-        , fWidthControl("width", FLT_EPSILON, 100)
-        , fFilterControl("filter", 0, 255)
-        , fCubicButton('C')
-        , fConicButton('K')
-        , fQuadButton('Q')
-        , fLineButton('L')
-        , fAddButton('+')
-        , fDeleteButton('x')
-        , fFillButton('p')
-        , fSkeletonButton('s')
-        , fFilterButton('f', 3)
-        , fBisectButton('b')
-        , fJoinButton('j')
-        , fInOutButton('|')
-        , fActivePt(-1)
-        , fActiveVerb(-1)
-        , fHandlePathMove(true)
-        , fShowLegend(false)
-        , fHideAll(false)
-    {
-        fCoveragePaint.setAntiAlias(true);
-        fCoveragePaint.setColor(SK_ColorBLUE);
-        SkPaint strokePaint;
-        strokePaint.setAntiAlias(true);
-        strokePaint.setStyle(SkPaint::kStroke_Style);
-        fPointPaint = strokePaint;
-        fPointPaint.setColor(0x99ee3300);
-        fSkeletonPaint = strokePaint;
-        fSkeletonPaint.setColor(SK_ColorRED);
-        fLightSkeletonPaint = fSkeletonPaint;
-        fLightSkeletonPaint.setColor(0xFFFF7f7f);
-        fActivePaint = strokePaint;
-        fActivePaint.setColor(0x99ee3300);
-        fActivePaint.setStrokeWidth(5);
-        fComplexPaint = fActivePaint;
-        fComplexPaint.setColor(SK_ColorBLUE);
-        fLegendLeftFont.setSize(13);
-        fLegendRightFont = fLegendLeftFont;
-        construct_path(fPath);
-        fFillButton.fVisible = fSkeletonButton.fVisible = fFilterButton.fVisible
-                = fBisectButton.fVisible = fJoinButton.fVisible = fInOutButton.fVisible = true;
-        fSkeletonButton.setEnabled(true);
-        fInOutButton.setEnabled(true);
-        fJoinButton.setEnabled(true);
-        fFilterControl.fValLo = 120;
-        fFilterControl.fValHi = 141;
-        fFilterControl.fVisible = fFilterButton.fState == 2;
-        fResControl.fValLo = 5;
-        fResControl.fVisible = true;
-        fWidthControl.fValLo = 50;
-        fWidthControl.fVisible = true;
-        init_controlList();
-        init_buttonList();
-    }
-
-    ~AAGeometryView() override {
-        // Free linked list without deep recursion.
-        std::unique_ptr<PathUndo> undo = std::move(fUndo);
-        while (undo) {
-            undo = std::move(undo->fNext);
-        }
-    }
-
-    bool constructPath() {
-        construct_path(fPath);
-        return true;
-    }
-
-    void savePath(skui::InputState state) {
-        if (state != skui::InputState::kDown) {
-            return;
-        }
-        if (fUndo && fUndo->fPath == fPath) {
-            return;
-        }
-        std::unique_ptr<PathUndo> undo(new PathUndo);
-        undo->fPath = fPath;
-        undo->fNext = std::move(fUndo);
-        fUndo = std::move(undo);
-    }
-
-    bool undo() {
-        if (!fUndo) {
-            return false;
-        }
-        fPath = std::move(fUndo->fPath);
-        fUndo = std::move(fUndo->fNext);
-        validatePath();
-        return true;
-    }
-
-    void validatePath() {}
-
-    void set_controlList(int index, UniControl* control, MyClick::ControlType type) {
-        kControlList[index].fControl = control;
-        kControlList[index].fControlType = type;
-    }
-
-    #define SET_CONTROL(Name) set_controlList(index++, &f##Name##Control, \
-        MyClick::k##Name##Control)
-
-    bool hideAll() {
-        fHideAll ^= true;
-        return true;
-    }
-
-    void init_controlList() {
-        int index = 0;
-        SET_CONTROL(Width);
-        SET_CONTROL(Res);
-        SET_CONTROL(Filter);
-        SET_CONTROL(Weight);
-    }
-
-    #undef SET_CONTROL
-
-    void set_buttonList(int index, Button* button, MyClick::ControlType type) {
-        kButtonList[index].fButton = button;
-        kButtonList[index].fButtonType = type;
-    }
-
-    #define SET_BUTTON(Name) set_buttonList(index++, &f##Name##Button, \
-            MyClick::k##Name##Button)
-
-    void init_buttonList() {
-        int index = 0;
-        SET_BUTTON(Fill);
-        SET_BUTTON(Skeleton);
-        SET_BUTTON(Filter);
-        SET_BUTTON(Bisect);
-        SET_BUTTON(Join);
-        SET_BUTTON(InOut);
-        SET_BUTTON(Cubic);
-        SET_BUTTON(Conic);
-        SET_BUTTON(Quad);
-        SET_BUTTON(Line);
-        SET_BUTTON(Add);
-        SET_BUTTON(Delete);
-    }
-
-    #undef SET_BUTTON
-
-    SkString name() override { return SkString("AAGeometry"); }
-
-    bool onChar(SkUnichar) override;
-
-    void onSizeChange() override {
-        setControlButtonsPos();
-        this->INHERITED::onSizeChange();
-    }
-
-    bool pathDump() {
-        fPath.dump();
-        return true;
-    }
-
-    bool scaleDown() {
-        SkMatrix matrix;
-        SkRect bounds = fPath.getBounds();
-        matrix.setScale(1.f / 1.5f, 1.f / 1.5f, bounds.centerX(), bounds.centerY());
-        fPath.transform(matrix);
-        validatePath();
-        return true;
-    }
-
-    bool scaleToFit() {
-        SkMatrix matrix;
-        SkRect bounds = fPath.getBounds();
-        SkScalar scale = SkTMin(this->width() / bounds.width(), this->height() / bounds.height())
-                * 0.8f;
-        matrix.setScale(scale, scale, bounds.centerX(), bounds.centerY());
-        fPath.transform(matrix);
-        bounds = fPath.getBounds();
-        SkScalar offsetX = (this->width() - bounds.width()) / 2 - bounds.fLeft;
-        SkScalar offsetY = (this->height() - bounds.height()) / 2 - bounds.fTop;
-        fPath.offset(offsetX, offsetY);
-        validatePath();
-        return true;
-    }
-
-    bool scaleUp() {
-        SkMatrix matrix;
-        SkRect bounds = fPath.getBounds();
-        matrix.setScale(1.5f, 1.5f, bounds.centerX(), bounds.centerY());
-        fPath.transform(matrix);
-        validatePath();
-        return true;
-    }
-
-    void setControlButtonsPos() {
-        SkScalar widthOffset = this->width() - 100;
-        for (int index = 0; index < kControlCount; ++index) {
-            if (kControlList[index].fControl->fVisible) {
-                kControlList[index].fControl->fBounds.setXYWH(widthOffset, 30, 30, 400);
-                widthOffset -= 50;
-            }
-        }
-        SkScalar buttonOffset = 0;
-        for (int index = 0; index < kButtonCount; ++index) {
-            kButtonList[index].fButton->fBounds.setXYWH(this->width() - 50,
-                    buttonOffset += 50, 30, 30);
-        }
-    }
-
-    bool showLegend() {
-        fShowLegend ^= true;
-        return true;
-    }
-
-    void draw_bisect(SkCanvas* canvas, const SkVector& lastVector, const SkVector& vector,
-                const SkPoint& pt) {
-        SkVector lastV = lastVector;
-        SkScalar lastLen = lastVector.length();
-        SkVector nextV = vector;
-        SkScalar nextLen = vector.length();
-        if (lastLen < nextLen) {
-            lastV.setLength(nextLen);
-        } else {
-            nextV.setLength(lastLen);
-        }
-
-        SkVector bisect = { (lastV.fX + nextV.fX) / 2, (lastV.fY + nextV.fY) / 2 };
-        bisect.setLength(fWidthControl.fValLo * 2);
-        if (fBisectButton.enabled()) {
-            canvas->drawLine(pt, pt + bisect, fSkeletonPaint);
-        }
-        lastV.setLength(fWidthControl.fValLo);
-        if (fBisectButton.enabled()) {
-            canvas->drawLine(pt, {pt.fX - lastV.fY, pt.fY + lastV.fX}, fSkeletonPaint);
-        }
-        nextV.setLength(fWidthControl.fValLo);
-        if (fBisectButton.enabled()) {
-            canvas->drawLine(pt, {pt.fX + nextV.fY, pt.fY - nextV.fX}, fSkeletonPaint);
-        }
-        if (fJoinButton.enabled()) {
-            SkScalar r = fWidthControl.fValLo;
-            SkRect oval = { pt.fX - r, pt.fY - r, pt.fX + r, pt.fY + r};
-            SkScalar startAngle = SkScalarATan2(lastV.fX, -lastV.fY) * 180.f / SK_ScalarPI;
-            SkScalar endAngle = SkScalarATan2(-nextV.fX, nextV.fY) * 180.f / SK_ScalarPI;
-            if (endAngle > startAngle) {
-                canvas->drawArc(oval, startAngle, endAngle - startAngle, false, fSkeletonPaint);
-            } else {
-                canvas->drawArc(oval, startAngle, 360 - (startAngle - endAngle), false,
-                        fSkeletonPaint);
-            }
-        }
-    }
-
-    void draw_bisects(SkCanvas* canvas, bool activeOnly) {
-        SkVector firstVector, lastVector, nextLast, vector;
-        SkPoint pts[4];
-        SkPoint firstPt = { 0, 0 };  // init to avoid warning;
-        SkPath::Verb verb;
-        SkPath::Iter iter(fPath, true);
-        bool foundFirst = false;
-        int counter = -1;
-        while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-            ++counter;
-            if (activeOnly && counter != fActiveVerb && counter - 1 != fActiveVerb
-                    && counter + 1 != fActiveVerb
-                    && (fActiveVerb != 1 || counter != fPath.countVerbs())) {
-                continue;
-            }
-            switch (verb) {
-                case SkPath::kLine_Verb:
-                    nextLast = pts[0] - pts[1];
-                    vector = pts[1] - pts[0];
-                    break;
-                case SkPath::kQuad_Verb: {
-                    nextLast = pts[1] - pts[2];
-                    if (SkScalarNearlyZero(nextLast.length())) {
-                        nextLast = pts[0] - pts[2];
-                    }
-                    vector = pts[1] - pts[0];
-                    if (SkScalarNearlyZero(vector.length())) {
-                        vector = pts[2] - pts[0];
-                    }
-                    if (!fBisectButton.enabled()) {
-                        break;
-                    }
-                    SkScalar t = SkFindQuadMaxCurvature(pts);
-                    if (0 < t && t < 1) {
-                        SkPoint maxPt = SkEvalQuadAt(pts, t);
-                        SkVector tangent = SkEvalQuadTangentAt(pts, t);
-                        tangent.setLength(fWidthControl.fValLo * 2);
-                        canvas->drawLine(maxPt, {maxPt.fX + tangent.fY, maxPt.fY - tangent.fX},
-                                         fSkeletonPaint);
-                    }
-                    } break;
-                case SkPath::kConic_Verb:
-                    nextLast = pts[1] - pts[2];
-                    if (SkScalarNearlyZero(nextLast.length())) {
-                        nextLast = pts[0] - pts[2];
-                    }
-                    vector = pts[1] - pts[0];
-                    if (SkScalarNearlyZero(vector.length())) {
-                        vector = pts[2] - pts[0];
-                    }
-                    if (!fBisectButton.enabled()) {
-                        break;
-                    }
-                    // FIXME : need max curvature or equivalent here
-                    break;
-                case SkPath::kCubic_Verb: {
-                    nextLast = pts[2] - pts[3];
-                    if (SkScalarNearlyZero(nextLast.length())) {
-                        nextLast = pts[1] - pts[3];
-                        if (SkScalarNearlyZero(nextLast.length())) {
-                            nextLast = pts[0] - pts[3];
-                        }
-                    }
-                    vector = pts[0] - pts[1];
-                    if (SkScalarNearlyZero(vector.length())) {
-                        vector = pts[0] - pts[2];
-                        if (SkScalarNearlyZero(vector.length())) {
-                            vector = pts[0] - pts[3];
-                        }
-                    }
-                    if (!fBisectButton.enabled()) {
-                        break;
-                    }
-                    SkScalar tMax[2];
-                    int tMaxCount = SkFindCubicMaxCurvature(pts, tMax);
-                    for (int tIndex = 0; tIndex < tMaxCount; ++tIndex) {
-                        if (0 >= tMax[tIndex] || tMax[tIndex] >= 1) {
-                            continue;
-                        }
-                        SkPoint maxPt;
-                        SkVector tangent;
-                        SkEvalCubicAt(pts, tMax[tIndex], &maxPt, &tangent, nullptr);
-                        tangent.setLength(fWidthControl.fValLo * 2);
-                        canvas->drawLine(maxPt, {maxPt.fX + tangent.fY, maxPt.fY - tangent.fX},
-                                         fSkeletonPaint);
-                    }
-                    } break;
-                case SkPath::kClose_Verb:
-                    if (foundFirst) {
-                        draw_bisect(canvas, lastVector, firstVector, firstPt);
-                        foundFirst = false;
-                    }
-                    break;
-                default:
-                    break;
-            }
-            if (SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb) {
-                if (!foundFirst) {
-                    firstPt = pts[0];
-                    firstVector = vector;
-                    foundFirst = true;
-                } else {
-                    draw_bisect(canvas, lastVector, vector, pts[0]);
-                }
-                lastVector = nextLast;
-            }
-        }
-    }
-
-    void draw_legend(SkCanvas* canvas);
-
-    void draw_segment(SkCanvas* canvas) {
-        SkPoint pts[4];
-        SkPath::Verb verb;
-        SkPath::Iter iter(fPath, true);
-        int counter = -1;
-        while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-            if (++counter < fActiveVerb) {
-                continue;
-            }
-            switch (verb) {
-                case SkPath::kLine_Verb:
-                    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, fActivePaint);
-                    draw_points(canvas, pts, 2);
-                    break;
-                case SkPath::kQuad_Verb: {
-                    SkPath qPath;
-                    qPath.moveTo(pts[0]);
-                    qPath.quadTo(pts[1], pts[2]);
-                    canvas->drawPath(qPath, fActivePaint);
-                    draw_points(canvas, pts, 3);
-                    } break;
-                case SkPath::kConic_Verb: {
-                    SkPath conicPath;
-                    conicPath.moveTo(pts[0]);
-                    conicPath.conicTo(pts[1], pts[2], iter.conicWeight());
-                    canvas->drawPath(conicPath, fActivePaint);
-                    draw_points(canvas, pts, 3);
-                    } break;
-                case SkPath::kCubic_Verb: {
-                    SkScalar loopT[3];
-                    int complex = SkDCubic::ComplexBreak(pts, loopT);
-                    SkPath cPath;
-                    cPath.moveTo(pts[0]);
-                    cPath.cubicTo(pts[1], pts[2], pts[3]);
-                    canvas->drawPath(cPath, complex ? fComplexPaint : fActivePaint);
-                    draw_points(canvas, pts, 4);
-                    } break;
-                default:
-                    break;
-            }
-            return;
-        }
-    }
-
-    void draw_points(SkCanvas* canvas, SkPoint* points, int count) {
-        for (int index = 0; index < count; ++index) {
-            canvas->drawCircle(points[index].fX, points[index].fY, 10, fPointPaint);
-        }
-    }
-
-    int hittest_verb(SkPoint pt, SkPath::Verb* verbPtr, SkScalar* weight) {
-        SkIntersections i;
-        SkDLine hHit = {{{pt.fX - kHitToleranace, pt.fY }, {pt.fX + kHitToleranace, pt.fY}}};
-        SkDLine vHit = {{{pt.fX, pt.fY - kHitToleranace }, {pt.fX, pt.fY + kHitToleranace}}};
-        SkPoint pts[4];
-        SkPath::Verb verb;
-        SkPath::Iter iter(fPath, true);
-        int counter = -1;
-        while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-            ++counter;
-            switch (verb) {
-                case SkPath::kLine_Verb: {
-                    SkDLine line;
-                    line.set(pts);
-                    if (i.intersect(line, hHit) || i.intersect(line, vHit)) {
-                        *verbPtr = verb;
-                        *weight = 1;
-                        return counter;
-                    }
-                    } break;
-                case SkPath::kQuad_Verb: {
-                    SkDQuad quad;
-                    quad.set(pts);
-                    if (i.intersect(quad, hHit) || i.intersect(quad, vHit)) {
-                        *verbPtr = verb;
-                        *weight = 1;
-                        return counter;
-                    }
-                    } break;
-                case SkPath::kConic_Verb: {
-                    SkDConic conic;
-                    SkScalar w = iter.conicWeight();
-                    conic.set(pts, w);
-                    if (i.intersect(conic, hHit) || i.intersect(conic, vHit)) {
-                        *verbPtr = verb;
-                        *weight = w;
-                        return counter;
-                    }
-                    } break;
-                case SkPath::kCubic_Verb: {
-                    SkDCubic cubic;
-                    cubic.set(pts);
-                    if (i.intersect(cubic, hHit) || i.intersect(cubic, vHit)) {
-                        *verbPtr = verb;
-                        *weight = 1;
-                        return counter;
-                    }
-                    } break;
-                default:
-                    break;
-            }
-        }
-        return -1;
-    }
-
-    SkScalar pt_to_line(SkPoint s, SkPoint e, int x, int y) {
-        SkScalar radius = fWidthControl.fValLo;
-        SkVector adjOpp = e - s;
-        SkScalar lenSq = SkPointPriv::LengthSqd(adjOpp);
-        SkPoint rotated = {
-                (y - s.fY) * adjOpp.fY + (x - s.fX) * adjOpp.fX,
-                (y - s.fY) * adjOpp.fX - (x - s.fX) * adjOpp.fY,
-        };
-        if (rotated.fX < 0 || rotated.fX > lenSq) {
-                return -radius;
-        }
-        rotated.fY /= SkScalarSqrt(lenSq);
-        return SkTMax(-radius, SkTMin(radius, rotated.fY));
-    }
-
-    // given a line, compute the interior and exterior gradient coverage
-    bool coverage(SkPoint s, SkPoint e, uint8_t* distanceMap, int w, int h) {
-        SkScalar radius = fWidthControl.fValLo;
-        int minX = SkTMax(0, (int) (SkTMin(s.fX, e.fX) - radius));
-        int minY = SkTMax(0, (int) (SkTMin(s.fY, e.fY) - radius));
-        int maxX = SkTMin(w, (int) (SkTMax(s.fX, e.fX) + radius) + 1);
-        int maxY = SkTMin(h, (int) (SkTMax(s.fY, e.fY) + radius) + 1);
-        for (int y = minY; y < maxY; ++y) {
-            for (int x = minX; x < maxX; ++x) {
-                SkScalar ptToLineDist = pt_to_line(s, e, x, y);
-                if (ptToLineDist > -radius && ptToLineDist < radius) {
-                    SkScalar coverage = ptToLineDist / radius;
-                    add_to_map(1 - SkScalarAbs(coverage), x, y, distanceMap, w, h);
-                }
-                SkVector ptToS = { x - s.fX, y - s.fY };
-                SkScalar dist = ptToS.length();
-                if (dist < radius) {
-                    SkScalar coverage = dist / radius;
-                    add_to_map(1 - SkScalarAbs(coverage), x, y, distanceMap, w, h);
-                }
-                SkVector ptToE = { x - e.fX, y - e.fY };
-                dist = ptToE.length();
-                if (dist < radius) {
-                    SkScalar coverage = dist / radius;
-                    add_to_map(1 - SkScalarAbs(coverage), x, y, distanceMap, w, h);
-                }
-            }
-        }
-        return true;
-    }
-
-    void quad_coverage(SkPoint pts[3], uint8_t* distanceMap, int w, int h) {
-        SkScalar dist = pts[0].Distance(pts[0], pts[2]);
-        if (dist < gCurveDistance) {
-            (void) coverage(pts[0], pts[2], distanceMap, w, h);
-            return;
-        }
-        SkPoint split[5];
-        SkChopQuadAt(pts, split, 0.5f);
-        quad_coverage(&split[0], distanceMap, w, h);
-        quad_coverage(&split[2], distanceMap, w, h);
-    }
-
-    void conic_coverage(SkPoint pts[3], SkScalar weight, uint8_t* distanceMap, int w, int h) {
-        SkScalar dist = pts[0].Distance(pts[0], pts[2]);
-        if (dist < gCurveDistance) {
-            (void) coverage(pts[0], pts[2], distanceMap, w, h);
-            return;
-        }
-        SkConic split[2];
-        SkConic conic;
-        conic.set(pts, weight);
-        if (conic.chopAt(0.5f, split)) {
-            conic_coverage(split[0].fPts, split[0].fW, distanceMap, w, h);
-            conic_coverage(split[1].fPts, split[1].fW, distanceMap, w, h);
-        }
-    }
-
-    void cubic_coverage(SkPoint pts[4], uint8_t* distanceMap, int w, int h) {
-        SkScalar dist = pts[0].Distance(pts[0], pts[3]);
-        if (dist < gCurveDistance) {
-            (void) coverage(pts[0], pts[3], distanceMap, w, h);
-            return;
-        }
-        SkPoint split[7];
-        SkChopCubicAt(pts, split, 0.5f);
-        cubic_coverage(&split[0], distanceMap, w, h);
-        cubic_coverage(&split[3], distanceMap, w, h);
-    }
-
-    void path_coverage(const SkPath& path, uint8_t* distanceMap, int w, int h) {
-        memset(distanceMap, 0, sizeof(distanceMap[0]) * w * h);
-        SkPoint pts[4];
-        SkPath::Verb verb;
-        SkPath::Iter iter(path, true);
-        while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-            switch (verb) {
-                case SkPath::kLine_Verb:
-                    (void) coverage(pts[0], pts[1], distanceMap, w, h);
-                    break;
-                case SkPath::kQuad_Verb:
-                    quad_coverage(pts, distanceMap, w, h);
-                    break;
-                case SkPath::kConic_Verb:
-                    conic_coverage(pts, iter.conicWeight(), distanceMap, w, h);
-                    break;
-                case SkPath::kCubic_Verb:
-                    cubic_coverage(pts, distanceMap, w, h);
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    static uint8_t* set_up_dist_map(const SkImageInfo& imageInfo, SkBitmap* distMap) {
-        distMap->setInfo(imageInfo);
-        distMap->setIsVolatile(true);
-        SkAssertResult(distMap->tryAllocPixels());
-        SkASSERT((int) distMap->rowBytes() == imageInfo.width());
-        return distMap->getAddr8(0, 0);
-    }
-
-    void path_stroke(int index, SkPath* inner, SkPath* outer) {
-        #if 0
-        SkPathStroker stroker(fPath, fWidthControl.fValLo, 0,
-                SkPaint::kRound_Cap, SkPaint::kRound_Join, fResControl.fValLo);
-        SkPoint pts[4], firstPt, lastPt;
-        SkPath::Verb verb;
-        SkPath::Iter iter(fPath, true);
-        int counter = -1;
-        while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-            ++counter;
-            switch (verb) {
-                case SkPath::kMove_Verb:
-                    firstPt = pts[0];
-                    break;
-                case SkPath::kLine_Verb:
-                    if (counter == index) {
-                        stroker.moveTo(pts[0]);
-                        stroker.lineTo(pts[1]);
-                        goto done;
-                    }
-                    lastPt = pts[1];
-                    break;
-                case SkPath::kQuad_Verb:
-                    if (counter == index) {
-                        stroker.moveTo(pts[0]);
-                        stroker.quadTo(pts[1], pts[2]);
-                        goto done;
-                    }
-                    lastPt = pts[2];
-                    break;
-                case SkPath::kConic_Verb:
-                    if (counter == index) {
-                        stroker.moveTo(pts[0]);
-                        stroker.conicTo(pts[1], pts[2], iter.conicWeight());
-                        goto done;
-                    }
-                    lastPt = pts[2];
-                    break;
-                case SkPath::kCubic_Verb:
-                    if (counter == index) {
-                        stroker.moveTo(pts[0]);
-                        stroker.cubicTo(pts[1], pts[2], pts[3]);
-                        goto done;
-                    }
-                    lastPt = pts[3];
-                    break;
-                case SkPath::kClose_Verb:
-                    if (counter == index) {
-                        stroker.moveTo(lastPt);
-                        stroker.lineTo(firstPt);
-                        goto done;
-                    }
-                    break;
-                case SkPath::kDone_Verb:
-                    break;
-                default:
-                    SkASSERT(0);
-            }
-        }
-    done:
-        *inner = stroker.fInner;
-        *outer = stroker.fOuter;
-#endif
-    }
-
-    void draw_stroke(SkCanvas* canvas, int active) {
-        SkPath inner, outer;
-        path_stroke(active, &inner, &outer);
-        canvas->drawPath(inner, fSkeletonPaint);
-        canvas->drawPath(outer, fSkeletonPaint);
-    }
-
-    void gather_strokes() {
-        fStrokes.reset();
-        for (int index = 0; index < fPath.countVerbs(); ++index) {
-            Stroke& inner = fStrokes.push_back();
-            inner.reset();
-            inner.fInner = true;
-            Stroke& outer = fStrokes.push_back();
-            outer.reset();
-            outer.fInner = false;
-            path_stroke(index, &inner.fPath, &outer.fPath);
-        }
-    }
-
-    void trim_strokes() {
-        // eliminate self-itersecting loops
-        // trim outside edges
-        gather_strokes();
-        for (int index = 0; index < fStrokes.count(); ++index) {
-            SkPath& outPath = fStrokes[index].fPath;
-            for (int inner = 0; inner < fStrokes.count(); ++inner) {
-                if (index == inner) {
-                    continue;
-                }
-                SkPath& inPath = fStrokes[inner].fPath;
-                if (!outPath.getBounds().intersects(inPath.getBounds())) {
-                    continue;
-                }
-
-            }
-        }
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-#if 0
-        SkDEBUGCODE(SkDebugStrokeGlobals debugGlobals);
-        SkOpAA aaResult(fPath, fWidthControl.fValLo, fResControl.fValLo
-                SkDEBUGPARAMS(&debugGlobals));
-#endif
-        SkPath strokePath;
-//        aaResult.simplify(&strokePath);
-        canvas->drawPath(strokePath, fSkeletonPaint);
-        SkRect bounds = fPath.getBounds();
-        SkScalar radius = fWidthControl.fValLo;
-        int w = (int) (bounds.fRight + radius + 1);
-        int h = (int) (bounds.fBottom + radius + 1);
-        SkImageInfo imageInfo = SkImageInfo::MakeA8(w, h);
-        SkBitmap distMap;
-        uint8_t* distanceMap = set_up_dist_map(imageInfo, &distMap);
-        path_coverage(fPath, distanceMap, w, h);
-        if (fFillButton.enabled()) {
-            canvas->drawPath(fPath, fCoveragePaint);
-        }
-        if (fFilterButton.fState == 2
-                && (0 < fFilterControl.fValLo || fFilterControl.fValHi < 255)) {
-            SkBitmap filteredMap;
-            uint8_t* filtered = set_up_dist_map(imageInfo, &filteredMap);
-            filter_coverage(distanceMap, sizeof(uint8_t) * w * h, (uint8_t) fFilterControl.fValLo,
-                    (uint8_t) fFilterControl.fValHi, filtered);
-            canvas->drawBitmap(filteredMap, 0, 0, &fCoveragePaint);
-        } else if (fFilterButton.enabled()) {
-            canvas->drawBitmap(distMap, 0, 0, &fCoveragePaint);
-        }
-        if (fSkeletonButton.enabled()) {
-            canvas->drawPath(fPath, fActiveVerb >= 0 ? fLightSkeletonPaint : fSkeletonPaint);
-        }
-        if (fActiveVerb >= 0) {
-            draw_segment(canvas);
-        }
-        if (fBisectButton.enabled() || fJoinButton.enabled()) {
-            draw_bisects(canvas, fActiveVerb >= 0);
-        }
-        if (fInOutButton.enabled()) {
-            if (fActiveVerb >= 0) {
-                draw_stroke(canvas, fActiveVerb);
-            } else {
-                for (int index = 0; index < fPath.countVerbs(); ++index) {
-                    draw_stroke(canvas, index);
-                }
-            }
-        }
-        if (fHideAll) {
-            return;
-        }
-        for (int index = 0; index < kControlCount; ++index) {
-            kControlList[index].fControl->draw(canvas, fControlPaints);
-        }
-        for (int index = 0; index < kButtonCount; ++index) {
-            kButtonList[index].fButton->draw(canvas, fButtonPaints);
-        }
-        if (fShowLegend) {
-            draw_legend(canvas);
-        }
-
-#if 0
-        SkPaint paint;
-        paint.setARGB(255, 34, 31, 31);
-        paint.setAntiAlias(true);
-
-        SkPath path;
-        path.moveTo(18,439);
-        path.lineTo(414,439);
-        path.lineTo(414,702);
-        path.lineTo(18,702);
-        path.lineTo(18,439);
-
-        path.moveTo(19,701);
-        path.lineTo(413,701);
-        path.lineTo(413,440);
-        path.lineTo(19,440);
-        path.lineTo(19,701);
-        path.close();
-        canvas->drawPath(path, paint);
-
-        canvas->scale(1.0f, -1.0f);
-        canvas->translate(0.0f, -800.0f);
-        canvas->drawPath(path, paint);
-#endif
-
-    }
-
-    int hittest_pt(SkPoint pt) {
-        for (int index = 0; index < fPath.countPoints(); ++index) {
-            if (SkPoint::Distance(fPath.getPoint(index), pt) <= kHitToleranace * 2) {
-                return index;
-            }
-        }
-        return -1;
-    }
-
-    virtual Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
-        SkPoint pt = {x, y};
-        int ptHit = hittest_pt(pt);
-        if (ptHit >= 0) {
-            return new MyClick(MyClick::kPtType, ptHit);
-        }
-        SkPath::Verb verb;
-        SkScalar weight;
-        int verbHit = hittest_verb(pt, &verb, &weight);
-        if (verbHit >= 0) {
-            return new MyClick(MyClick::kVerbType, verbHit, verb, weight);
-        }
-        if (!fHideAll) {
-            const SkRect& rectPt = SkRect::MakeXYWH(x, y, 1, 1);
-            for (int index = 0; index < kControlCount; ++index) {
-                if (kControlList[index].fControl->contains(rectPt)) {
-                    return new MyClick(MyClick::kControlType,
-                            kControlList[index].fControlType);
-                }
-            }
-            for (int index = 0; index < kButtonCount; ++index) {
-                if (kButtonList[index].fButton->contains(rectPt)) {
-                    return new MyClick(MyClick::kControlType, kButtonList[index].fButtonType);
-                }
-            }
-        }
-        fLineButton.fVisible = fQuadButton.fVisible = fConicButton.fVisible
-                = fCubicButton.fVisible = fWeightControl.fVisible = fAddButton.fVisible
-                = fDeleteButton.fVisible = false;
-        fActiveVerb = -1;
-        fActivePt = -1;
-        if (fHandlePathMove) {
-            return new MyClick(MyClick::kPathType, MyClick::kPathMove);
-        }
-        return nullptr;
-    }
-
-    static SkScalar MapScreenYtoValue(int y, const UniControl& control) {
-        return SkTMin(1.f, SkTMax(0.f,
-                SkIntToScalar(y) - control.fBounds.fTop) / control.fBounds.height())
-                * (control.fMax - control.fMin) + control.fMin;
-    }
-
-    bool onClick(Click* click) override {
-        MyClick* myClick = (MyClick*) click;
-        switch (myClick->fType) {
-            case MyClick::kPtType: {
-                savePath(click->fState);
-                fActivePt = myClick->ptHit();
-                SkPoint pt = fPath.getPoint((int) myClick->fControl);
-                pt.offset(SkIntToScalar(click->fCurr.fX - click->fPrev.fX),
-                        SkIntToScalar(click->fCurr.fY - click->fPrev.fY));
-                set_path_pt(fActivePt, pt, &fPath);
-                validatePath();
-                return true;
-                }
-            case MyClick::kPathType:
-                savePath(click->fState);
-                fPath.offset(SkIntToScalar(click->fCurr.fX - click->fPrev.fX),
-                        SkIntToScalar(click->fCurr.fY - click->fPrev.fY));
-                validatePath();
-                return true;
-            case MyClick::kVerbType: {
-                fActiveVerb = myClick->verbHit();
-                fLineButton.fVisible = fQuadButton.fVisible = fConicButton.fVisible
-                        = fCubicButton.fVisible = fAddButton.fVisible = fDeleteButton.fVisible
-                        = true;
-                fLineButton.setEnabled(myClick->fVerb == SkPath::kLine_Verb);
-                fQuadButton.setEnabled(myClick->fVerb == SkPath::kQuad_Verb);
-                fConicButton.setEnabled(myClick->fVerb == SkPath::kConic_Verb);
-                fCubicButton.setEnabled(myClick->fVerb == SkPath::kCubic_Verb);
-                fWeightControl.fValLo = myClick->fWeight;
-                fWeightControl.fVisible = myClick->fVerb == SkPath::kConic_Verb;
-                } break;
-            case MyClick::kControlType: {
-                if (click->fState != skui::InputState::kDown && myClick->isButton()) {
-                    return true;
-                }
-                switch (myClick->fControl) {
-                    case MyClick::kFilterControl: {
-                        SkScalar val = MapScreenYtoValue(click->fCurr.fY, fFilterControl);
-                        if (val - fFilterControl.fValLo < fFilterControl.fValHi - val) {
-                            fFilterControl.fValLo = SkTMax(0.f, val);
-                        } else {
-                            fFilterControl.fValHi = SkTMin(255.f, val);
-                        }
-                        } break;
-                    case MyClick::kResControl:
-                        fResControl.fValLo = MapScreenYtoValue(click->fCurr.fY, fResControl);
-                        break;
-                    case MyClick::kWeightControl: {
-                        savePath(click->fState);
-                        SkScalar w = MapScreenYtoValue(click->fCurr.fY, fWeightControl);
-                        set_path_weight(fActiveVerb, w, &fPath);
-                        validatePath();
-                        fWeightControl.fValLo = w;
-                        } break;
-                    case MyClick::kWidthControl:
-                        fWidthControl.fValLo = MapScreenYtoValue(click->fCurr.fY, fWidthControl);
-                        break;
-                    case MyClick::kLineButton:
-                        savePath(click->fState);
-                        enable_verb_button(myClick->fControl);
-                        fWeightControl.fVisible = false;
-                        set_path_verb(fActiveVerb, SkPath::kLine_Verb, &fPath, 1);
-                        validatePath();
-                        break;
-                    case MyClick::kQuadButton:
-                        savePath(click->fState);
-                        enable_verb_button(myClick->fControl);
-                        fWeightControl.fVisible = false;
-                        set_path_verb(fActiveVerb, SkPath::kQuad_Verb, &fPath, 1);
-                        validatePath();
-                        break;
-                    case MyClick::kConicButton: {
-                        savePath(click->fState);
-                        enable_verb_button(myClick->fControl);
-                        fWeightControl.fVisible = true;
-                        const SkScalar defaultConicWeight = 1.f / SkScalarSqrt(2);
-                        set_path_verb(fActiveVerb, SkPath::kConic_Verb, &fPath, defaultConicWeight);
-                        validatePath();
-                        fWeightControl.fValLo = get_path_weight(fActiveVerb, fPath);
-                        } break;
-                    case MyClick::kCubicButton:
-                        savePath(click->fState);
-                        enable_verb_button(myClick->fControl);
-                        fWeightControl.fVisible = false;
-                        set_path_verb(fActiveVerb, SkPath::kCubic_Verb, &fPath, 1);
-                        validatePath();
-                        break;
-                    case MyClick::kAddButton:
-                        savePath(click->fState);
-                        add_path_segment(fActiveVerb, &fPath);
-                        validatePath();
-                        if (fWeightControl.fVisible) {
-                            fWeightControl.fValLo = get_path_weight(fActiveVerb, fPath);
-                        }
-                        break;
-                    case MyClick::kDeleteButton:
-                        savePath(click->fState);
-                        delete_path_segment(fActiveVerb, &fPath);
-                        validatePath();
-                        break;
-                    case MyClick::kFillButton:
-                        fFillButton.toggle();
-                        break;
-                    case MyClick::kSkeletonButton:
-                        fSkeletonButton.toggle();
-                        break;
-                    case MyClick::kFilterButton:
-                        fFilterButton.toggle();
-                        fFilterControl.fVisible = fFilterButton.fState == 2;
-                        break;
-                    case MyClick::kBisectButton:
-                        fBisectButton.toggle();
-                        break;
-                    case MyClick::kJoinButton:
-                        fJoinButton.toggle();
-                        break;
-                    case MyClick::kInOutButton:
-                        fInOutButton.toggle();
-                        break;
-                    default:
-                        SkASSERT(0);
-                        break;
-                }
-            } break;
-            default:
-                SkASSERT(0);
-                break;
-        }
-        setControlButtonsPos();
-        return true;
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-static struct KeyCommand {
-    char fKey;
-    char fAlternate;
-    const char* fDescriptionL;
-    const char* fDescriptionR;
-    bool (AAGeometryView::*fFunction)();
-} kKeyCommandList[] = {
-    { ' ',  0,  "space",   "center path", &AAGeometryView::scaleToFit },
-    { '-',  0,  "-",          "zoom out", &AAGeometryView::scaleDown },
-    { '+', '=', "+/=",         "zoom in", &AAGeometryView::scaleUp },
-    { 'D',  0,  "D",   "dump to console", &AAGeometryView::pathDump },
-    { 'H',  0,  "H",     "hide controls", &AAGeometryView::hideAll },
-    { 'R',  0,  "R",        "reset path", &AAGeometryView::constructPath },
-    { 'Z',  0,  "Z",              "undo", &AAGeometryView::undo },
-    { '?',  0,  "?",       "show legend", &AAGeometryView::showLegend },
-};
-
-const int kKeyCommandCount = (int) SK_ARRAY_COUNT(kKeyCommandList);
-
-void AAGeometryView::draw_legend(SkCanvas* canvas) {
-    SkScalar bottomOffset = this->height() - 10;
-    for (int index = kKeyCommandCount - 1; index >= 0; --index) {
-        bottomOffset -= 15;
-        SkTextUtils::DrawString(canvas, kKeyCommandList[index].fDescriptionL, this->width() - 160, bottomOffset,
-                                fLegendLeftFont, SkPaint());
-        SkTextUtils::DrawString(canvas, kKeyCommandList[index].fDescriptionR,
-                this->width() - 20, bottomOffset,
-                fLegendRightFont, SkPaint(), SkTextUtils::kRight_Align);
-    }
-}
-
-bool AAGeometryView::onChar(SkUnichar uni) {
-        for (int index = 0; index < kButtonCount; ++index) {
-            Button* button = kButtonList[index].fButton;
-            if (button->fVisible && uni == button->fLabel) {
-                MyClick click(MyClick::kControlType, kButtonList[index].fButtonType);
-                click.fState = skui::InputState::kDown;
-                (void) this->onClick(&click);
-                return true;
-            }
-        }
-        for (int index = 0; index < kKeyCommandCount; ++index) {
-            KeyCommand& keyCommand = kKeyCommandList[index];
-            if (uni == keyCommand.fKey || uni == keyCommand.fAlternate) {
-                return (this->*keyCommand.fFunction)();
-            }
-        }
-        if (('A' <= uni && uni <= 'Z') || ('a' <= uni && uni <= 'z')) {
-            for (int index = 0; index < kButtonCount; ++index) {
-                Button* button = kButtonList[index].fButton;
-                if (button->fVisible && (uni & ~0x20) == (button->fLabel & ~0x20)) {
-                    MyClick click(MyClick::kControlType, kButtonList[index].fButtonType);
-                    click.fState = skui::InputState::kDown;
-                    (void) this->onClick(&click);
-                    return true;
-                }
-            }
-        }
-        return false;
-}
-
-DEF_SAMPLE( return new AAGeometryView; )
diff --git a/third_party/skia/samplecode/SampleAARectModes.cpp b/third_party/skia/samplecode/SampleAARectModes.cpp
index a3c5ea3..99d8f77 100644
--- a/third_party/skia/samplecode/SampleAARectModes.cpp
+++ b/third_party/skia/samplecode/SampleAARectModes.cpp
@@ -62,10 +62,8 @@
     *bm.getAddr32(1, 0) = *bm.getAddr32(0, 1) = SkPackARGB32(0xFF, 0xCC,
                                                              0xCC, 0xCC);
 
-    SkMatrix m;
-    m.setScale(SkIntToScalar(6), SkIntToScalar(6));
-
-    return bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, &m);
+    return bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions(),
+                         SkMatrix::Scale(6, 6));
 }
 
 class AARectsModesView : public Sample {
diff --git a/third_party/skia/samplecode/SampleAARects.cpp b/third_party/skia/samplecode/SampleAARects.cpp
index 834a1e5..b7cc5e8 100644
--- a/third_party/skia/samplecode/SampleAARects.cpp
+++ b/third_party/skia/samplecode/SampleAARects.cpp
@@ -49,7 +49,8 @@
         SkPaint bluePaint;
         bluePaint.setARGB(0xff, 0x0, 0x0, 0xff);
         SkPaint bmpPaint;
-        bmpPaint.setShader(fBitmap.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat));
+        bmpPaint.setShader(fBitmap.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat,
+                                              SkSamplingOptions()));
         bluePaint.setStrokeWidth(3);
         bmpPaint.setStrokeWidth(3);
 
@@ -170,7 +171,7 @@
 
 private:
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleAndroidShadows.cpp b/third_party/skia/samplecode/SampleAndroidShadows.cpp
index a4518f2..50c626e 100644
--- a/third_party/skia/samplecode/SampleAndroidShadows.cpp
+++ b/third_party/skia/samplecode/SampleAndroidShadows.cpp
@@ -9,7 +9,6 @@
 #include "include/core/SkColorFilter.h"
 #include "include/core/SkPath.h"
 #include "include/core/SkPoint3.h"
-#include "include/effects/SkBlurMaskFilter.h"
 #include "include/pathops/SkPathOps.h"
 #include "include/utils/SkCamera.h"
 #include "include/utils/SkShadowUtils.h"
@@ -192,55 +191,55 @@
 
         paint.setColor(SK_ColorWHITE);
         canvas->translate(200, 90);
-        zPlaneParams.fZ = SkTMax(1.0f, 2 + fZDelta);
+        zPlaneParams.fZ = std::max(1.0f, 2 + fZDelta);
         this->drawShadowedPath(canvas, fRRPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
                                lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
 
         paint.setColor(SK_ColorRED);
         canvas->translate(250, 0);
-        zPlaneParams.fZ = SkTMax(1.0f, 8 + fZDelta);
+        zPlaneParams.fZ = std::max(1.0f, 8 + fZDelta);
         this->drawShadowedPath(canvas, fRectPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
                                lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
 
         paint.setColor(SK_ColorBLUE);
         canvas->translate(-250, 110);
-        zPlaneParams.fZ = SkTMax(1.0f, 12 + fZDelta);
+        zPlaneParams.fZ = std::max(1.0f, 12 + fZDelta);
         this->drawShadowedPath(canvas, fCirclePath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
                                lightPos, kLightWidth, fAnimAlpha*0.5f);
 
         paint.setColor(SK_ColorGREEN);
         canvas->translate(250, 0);
-        zPlaneParams.fZ = SkTMax(1.0f, 64 + fZDelta);
+        zPlaneParams.fZ = std::max(1.0f, 64 + fZDelta);
         this->drawShadowedPath(canvas, fRRPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
                                lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
 
         paint.setColor(SK_ColorYELLOW);
         canvas->translate(-250, 110);
-        zPlaneParams.fZ = SkTMax(1.0f, 8 + fZDelta);
+        zPlaneParams.fZ = std::max(1.0f, 8 + fZDelta);
         this->drawShadowedPath(canvas, fFunkyRRPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
                                lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
 
         paint.setColor(SK_ColorCYAN);
         canvas->translate(250, 0);
-        zPlaneParams.fZ = SkTMax(1.0f, 16 + fZDelta);
+        zPlaneParams.fZ = std::max(1.0f, 16 + fZDelta);
         this->drawShadowedPath(canvas, fCubicPath, zPlaneParams, paint, fAnimAlpha*kAmbientAlpha,
                                lightPos, kLightWidth, fAnimAlpha*kSpotAlpha);
 
         paint.setColor(SK_ColorWHITE);
         canvas->translate(250, -180);
-        zPlaneParams.fZ = SkTMax(1.0f, 8 + fZDelta);
+        zPlaneParams.fZ = std::max(1.0f, 8 + fZDelta);
         this->drawShadowedPath(canvas, fStarPath, zPlaneParams, paint,
                                kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
 
         paint.setColor(SK_ColorWHITE);
         canvas->translate(150, 0);
-        zPlaneParams.fZ = SkTMax(1.0f, 2 + fZDelta);
+        zPlaneParams.fZ = std::max(1.0f, 2 + fZDelta);
         this->drawShadowedPath(canvas, fNotchPath, zPlaneParams, paint,
                                kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
 
         paint.setColor(SK_ColorWHITE);
         canvas->translate(200, 0);
-        zPlaneParams.fZ = SkTMax(1.0f, 16 + fZDelta);
+        zPlaneParams.fZ = std::max(1.0f, 16 + fZDelta);
         this->drawShadowedPath(canvas, fTabPath, zPlaneParams, paint,
                                kAmbientAlpha, lightPos, kLightWidth, kSpotAlpha);
 
@@ -252,7 +251,7 @@
 
         paint.setColor(SK_ColorMAGENTA);
         canvas->translate(-725, 240);
-        zPlaneParams.fZ = SkTMax(1.0f, 32 + fZDelta);
+        zPlaneParams.fZ = std::max(1.0f, 32 + fZDelta);
         this->drawShadowedPath(canvas, tmpPath, zPlaneParams, paint, .1f,
                                lightPos, kLightWidth, .5f);
 
@@ -262,7 +261,7 @@
         Op(fSquareRRectPath, tmpClipPathBug, kIntersect_SkPathOp, &tmpPath);
 
         canvas->translate(250, 0);
-        zPlaneParams.fZ = SkTMax(1.0f, 32 + fZDelta);
+        zPlaneParams.fZ = std::max(1.0f, 32 + fZDelta);
         this->drawShadowedPath(canvas, tmpPath, zPlaneParams, paint, .1f,
                                lightPos, kLightWidth, .5f);
 
@@ -282,7 +281,7 @@
         SkScalar radians = SkDegreesToRadians(fAnimAngle);
         zPlaneParams = SkPoint3::Make(0,
                                       SkScalarSin(radians),
-                                      SkTMax(1.0f, 16 + fZDelta) - SkScalarSin(radians)*pivot.fY);
+                                      std::max(1.0f, 16 + fZDelta) - SkScalarSin(radians)*pivot.fY);
         this->drawShadowedPath(canvas, fWideRectPath, zPlaneParams, paint, .1f,
                                lightPos, kLightWidth, .5f);
 
@@ -298,7 +297,7 @@
         canvas->setMatrix(persp);
         zPlaneParams = SkPoint3::Make(-SkScalarSin(radians),
                                       0,
-                                      SkTMax(1.0f, 32 + fZDelta) + SkScalarSin(radians)*pivot.fX);
+                                      std::max(1.0f, 32 + fZDelta) + SkScalarSin(radians)*pivot.fX);
         this->drawShadowedPath(canvas, fWideOvalPath, zPlaneParams, paint, .1f,
                                lightPos, kLightWidth, .5f);
 
@@ -313,7 +312,7 @@
         canvas->setMatrix(persp);
         zPlaneParams = SkPoint3::Make(-SkScalarSin(radians),
                                       0,
-                                      SkTMax(1.0f, 8 + fZDelta) + SkScalarSin(radians)*pivot.fX);
+                                      std::max(1.0f, 8 + fZDelta) + SkScalarSin(radians)*pivot.fX);
         this->drawShadowedPath(canvas, fStarPath, zPlaneParams, paint, .1f,
                                lightPos, kLightWidth, .5f);
     }
@@ -328,7 +327,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleAnimatedImage.cpp b/third_party/skia/samplecode/SampleAnimatedImage.cpp
index da6ac28..1ca7ac1 100644
--- a/third_party/skia/samplecode/SampleAnimatedImage.cpp
+++ b/third_party/skia/samplecode/SampleAnimatedImage.cpp
@@ -104,11 +104,10 @@
             switch (uni) {
                 case kPauseKey:
                     fRunning = !fRunning;
-                    if (fImage->isFinished()) {
-                        // fall through
-                    } else {
+                    if (!fImage->isFinished()) {
                         return true;
                     }
+                    [[fallthrough]];
                 case kResetKey:
                     fImage->reset();
                     fCurrentTime = fLastWallTime;
diff --git a/third_party/skia/samplecode/SampleAnimatedText.cpp b/third_party/skia/samplecode/SampleAnimatedText.cpp
index 8e7ed0e..80aafdc 100644
--- a/third_party/skia/samplecode/SampleAnimatedText.cpp
+++ b/third_party/skia/samplecode/SampleAnimatedText.cpp
@@ -8,6 +8,7 @@
 #include "include/core/SkCanvas.h"
 #include "include/core/SkColorFilter.h"
 #include "include/core/SkColorPriv.h"
+#include "include/core/SkFont.h"
 #include "include/core/SkImage.h"
 #include "include/core/SkTime.h"
 #include "include/core/SkTypeface.h"
@@ -16,8 +17,8 @@
 #include "src/utils/SkUTF.h"
 
 #if SK_SUPPORT_GPU
-#include "include/gpu/GrContext.h"
-#include "src/gpu/GrContextPriv.h"
+#include "include/gpu/GrDirectContext.h"
+#include "src/gpu/GrDirectContextPriv.h"
 #endif
 
 SkRandom gRand;
@@ -62,17 +63,18 @@
 
         SkPaint paint;
         paint.setAntiAlias(true);
-        paint.setFilterQuality(kMedium_SkFilterQuality);
 
         canvas->save();
 
 #if SK_SUPPORT_GPU
-        GrContext* grContext = canvas->getGrContext();
-        if (grContext) {
-            sk_sp<SkImage> image = grContext->priv().testingOnly_getFontAtlasImage(
+        auto direct = GrAsDirectContext(canvas->recordingContext());
+        if (direct) {
+            SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kNearest);
+            sk_sp<SkImage> image = direct->priv().testingOnly_getFontAtlasImage(
                                                                 GrMaskFormat::kA8_GrMaskFormat);
-            canvas->drawImageRect(image,
-                                  SkRect::MakeXYWH(512.0f, 10.0f, 512.0f, 512.0f), &paint);
+            const SkRect rect = SkRect::MakeXYWH(512.0f, 10.0f, 512.0f, 512.0f);
+            canvas->drawImageRect(image.get(), rect, rect, sampling, &paint,
+                                  SkCanvas::kFast_SrcRectConstraint);
         }
 #endif
         canvas->translate(180, 180);
diff --git a/third_party/skia/samplecode/SampleAtlas.cpp b/third_party/skia/samplecode/SampleAtlas.cpp
index 85d267c..8f7f116 100644
--- a/third_party/skia/samplecode/SampleAtlas.cpp
+++ b/third_party/skia/samplecode/SampleAtlas.cpp
@@ -13,27 +13,30 @@
 #include "include/utils/SkRandom.h"
 #include "include/utils/SkTextUtils.h"
 #include "samplecode/Sample.h"
+#include "src/core/SkPaintPriv.h"
 
 typedef void (*DrawAtlasProc)(SkCanvas*, SkImage*, const SkRSXform[], const SkRect[],
-                              const SkColor[], int, const SkRect*, const SkPaint*);
+                              const SkColor[], int, const SkRect*, const SkSamplingOptions&,
+                              const SkPaint*);
 
 static void draw_atlas(SkCanvas* canvas, SkImage* atlas, const SkRSXform xform[],
                        const SkRect tex[], const SkColor colors[], int count, const SkRect* cull,
-                       const SkPaint* paint) {
-    canvas->drawAtlas(atlas, xform, tex, colors, count, SkBlendMode::kModulate, cull, paint);
+                       const SkSamplingOptions& sampling, const SkPaint* paint) {
+    canvas->drawAtlas(atlas, xform, tex, colors, count, SkBlendMode::kModulate,
+                      sampling, cull, paint);
 }
 
 static void draw_atlas_sim(SkCanvas* canvas, SkImage* atlas, const SkRSXform xform[],
                            const SkRect tex[], const SkColor colors[], int count, const SkRect* cull,
-                           const SkPaint* paint) {
+                           const SkSamplingOptions& sampling, const SkPaint* paint) {
     for (int i = 0; i < count; ++i) {
         SkMatrix matrix;
         matrix.setRSXform(xform[i]);
 
         canvas->save();
         canvas->concat(matrix);
-        canvas->drawImageRect(atlas, tex[i], tex[i].makeOffset(-tex[i].x(), -tex[i].y()), paint,
-                              SkCanvas::kFast_SrcRectConstraint);
+        canvas->drawImageRect(atlas, tex[i], tex[i].makeOffset(-tex[i].x(), -tex[i].y()),
+                              sampling, paint, SkCanvas::kFast_SrcRectConstraint);
         canvas->restore();
     }
 }
@@ -183,11 +186,11 @@
             }
         }
         SkPaint paint;
-        paint.setFilterQuality(kLow_SkFilterQuality);
+        SkSamplingOptions sampling(SkFilterMode::kLinear);
 
         const SkRect cull = this->getBounds();
         const SkColor* colorsPtr = fUseColors ? colors : nullptr;
-        fProc(canvas, fAtlas.get(), xform, fTex, colorsPtr, N, &cull, &paint);
+        fProc(canvas, fAtlas.get(), xform, fTex, colorsPtr, N, &cull, sampling, &paint);
     }
 
     SkRect onGetBounds() override {
@@ -198,7 +201,7 @@
     }
 
 private:
-    typedef SkDrawable INHERITED;
+    using INHERITED = SkDrawable;
 };
 
 class DrawAtlasView : public Sample {
@@ -239,7 +242,7 @@
 #endif
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleAudio.cpp b/third_party/skia/samplecode/SampleAudio.cpp
new file mode 100644
index 0000000..a3347d7
--- /dev/null
+++ b/third_party/skia/samplecode/SampleAudio.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkCanvas.h"
+#include "include/core/SkData.h"
+#include "modules/audioplayer/SkAudioPlayer.h"
+#include "samplecode/Sample.h"
+#include "src/core/SkUtils.h"
+#include "tools/Resources.h"
+
+class AudioView : public Sample {
+    std::unique_ptr<SkAudioPlayer> fPlayer;
+    SkRect                         fBar;
+
+public:
+    AudioView() {}
+
+protected:
+    SkString name() override { return SkString("Audio"); }
+
+    void onOnceBeforeDraw() override {
+        auto data = SkData::MakeFromFileName("/Users/reed/skia/mp3/scott-joplin-peacherine-rag.mp3");
+        if (data) {
+            fPlayer = SkAudioPlayer::Make(data);
+
+            SkDebugf("make: dur:%g time%g state:%d",
+                     fPlayer->duration(),
+                     fPlayer->time(),
+                     (int)fPlayer->state());
+        }
+
+        fBar = { 10, 10, 510, 30 };
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        if (!fPlayer) {
+            return;
+        }
+
+        SkPaint p;
+        p.setColor(0xFFCCCCCC);
+        canvas->drawRect(fBar, p);
+
+        p.setColor(fPlayer->isPlaying() ? SK_ColorBLUE : 0xFF8888FF);
+        SkRect r = fBar;
+        r.fRight = r.fLeft + (float)fPlayer->normalizedTime() * r.width();
+        canvas->drawRect(r, p);
+    }
+
+    bool onChar(SkUnichar c) override {
+        if (c == ' ') {
+            switch (fPlayer->state()) {
+                case SkAudioPlayer::State::kPlaying: fPlayer->pause(); break;
+                case SkAudioPlayer::State::kPaused:  fPlayer->play(); break;
+                case SkAudioPlayer::State::kStopped: fPlayer->play(); break;
+            }
+            return true;
+        }
+        return this->INHERITED::onChar(c);
+    }
+
+    Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
+        if (fPlayer && fBar.contains(x, y)) {
+            bool wasPlaying = fPlayer->isPlaying();
+            if (wasPlaying) {
+                fPlayer->pause();
+            }
+            return new Click([this, wasPlaying](Click* click) {
+                if (fBar.contains(click->fCurr.fX, click->fCurr.fY)) {
+                    fPlayer->setNormalizedTime((click->fCurr.fX - fBar.fLeft) / fBar.width());
+                }
+
+                if (click->fState == skui::InputState::kUp) {
+                    if (wasPlaying) {
+                        fPlayer->play();
+                    }
+                }
+                return true;
+            });
+        }
+        return nullptr;
+    }
+
+    bool onAnimate(double /*nanos*/) override {
+        return true;
+    }
+
+private:
+    using INHERITED = Sample;
+};
+DEF_SAMPLE( return new AudioView; )
diff --git a/third_party/skia/samplecode/SampleBackdropBounds.cpp b/third_party/skia/samplecode/SampleBackdropBounds.cpp
deleted file mode 100644
index d16d0b9..0000000
--- a/third_party/skia/samplecode/SampleBackdropBounds.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright 2019 Google LLC
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "samplecode/Sample.h"
-
-#include "include/core/SkCanvas.h"
-#include "include/core/SkColor.h"
-#include "include/core/SkFont.h"
-#include "include/core/SkPaint.h"
-#include "include/core/SkPath.h"
-#include "include/core/SkPoint.h"
-#include "include/core/SkRect.h"
-
-#include "tools/ToolUtils.h"
-
-static constexpr float kLineHeight = 16.f;
-static constexpr float kLineInset = 8.f;
-
-static float print_size(SkCanvas* canvas, const char* prefix, const SkIRect& rect,
-                        float x, float y, const SkFont& font, const SkPaint& paint) {
-    canvas->drawString(prefix, x, y, font, paint);
-    y += kLineHeight;
-    SkString sz;
-    sz.appendf("%d x %d", rect.width(), rect.height());
-    canvas->drawString(sz, x, y, font, paint);
-    return y + kLineHeight;
-}
-
-static float print_info(SkCanvas* canvas, const SkIRect& origLayerBounds,
-                        const SkIRect& localLayerBounds, const SkIRect& filterInputBounds,
-                        const SkIRect& devLayerBounds) {
-    SkFont font(nullptr, 12);
-    SkPaint text;
-    text.setAntiAlias(true);
-
-    float y = kLineHeight;
-
-    text.setColor(SK_ColorBLACK);
-    y = print_size(canvas, "Orig layer", origLayerBounds, kLineInset, y, font, text);
-    text.setColor(SK_ColorRED);
-    y = print_size(canvas, "Filter layer", localLayerBounds, kLineInset, y, font, text);
-    text.setColor(SK_ColorBLUE);
-    y = print_size(canvas, "Filter input", filterInputBounds, kLineInset, y, font, text);
-    text.setColor(SK_ColorMAGENTA);
-    y = print_size(canvas, "Backdrop size", devLayerBounds, kLineInset, y, font, text);
-
-    return y;
-}
-
-static SkPaint line_paint(SkScalar width, SkColor color) {
-    SkPaint paint;
-    paint.setColor(color);
-    paint.setStrokeWidth(width);
-    paint.setStyle(SkPaint::kStroke_Style);
-    paint.setAntiAlias(true);
-    return paint;
-}
-
-class BackdropBoundsSample : public Sample {
-public:
-    BackdropBoundsSample() {}
-
-    void onDrawContent(SkCanvas* canvas) override {
-        SkMatrix ctm = canvas->getTotalMatrix();
-
-        // This decomposition is for the backdrop filtering, and does not represent the CTM that
-        // the layer actually uses (unless it also has a filter during restore).
-        SkMatrix toGlobal, layerMatrix;
-        SkSize scale;
-        if (ctm.isScaleTranslate()) {
-            // No decomposition needed
-            toGlobal = SkMatrix::I();
-            layerMatrix = ctm;
-        } else if (ctm.decomposeScale(&scale, &toGlobal)) {
-            layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
-        } else {
-            toGlobal = ctm;
-            layerMatrix = SkMatrix::I();
-        }
-
-        SkMatrix fromGlobal;
-        if (!toGlobal.invert(&fromGlobal)) {
-            SkDebugf("Unable to invert CTM\n");
-            return;
-        }
-
-        // The local content, e.g. what would be submitted to drawRect
-        const SkRect localContentRect = SkRect::MakeLTRB(45.5f, 23.123f, 150.f, 140.45f);
-        canvas->drawRect(localContentRect, line_paint(0.f, SK_ColorBLACK));
-
-        canvas->save();
-        // The layer bounds of the content, this is the size of the actual layer and does not
-        // reflect the backdrop specific decomposition.
-        canvas->setMatrix(SkMatrix::I());
-        SkIRect origLayerBounds = ctm.mapRect(localContentRect).roundOut();
-        canvas->drawRect(SkRect::Make(origLayerBounds), line_paint(1.f, SK_ColorBLACK));
-
-        // Have to undo the full CTM transform on the layer bounds to get the layer bounds
-        // for the specific backdrop filter decomposition
-        canvas->setMatrix(toGlobal);
-        SkIRect layerBounds = fromGlobal.mapRect(SkRect::Make(origLayerBounds)).roundOut();
-        canvas->drawRect(SkRect::Make(layerBounds), line_paint(0.5f, SK_ColorRED));
-
-        // Input bounds for the backdrop filter to cover the actual layer bounds (emulate some
-        // blur that must outset by 5px for reading on the edge).
-        SkIRect filterInputBounds = layerBounds;
-        filterInputBounds.outset(5, 5);
-        canvas->drawRect(SkRect::Make(filterInputBounds), line_paint(1.f, SK_ColorBLUE));
-
-        // The destination bounds that must be snapped in order to transform and fill the
-        // filterInputBounds
-        canvas->setMatrix(SkMatrix::I());
-        SkIRect devLayerBounds = toGlobal.mapRect(SkRect::Make(filterInputBounds)).roundOut();
-        canvas->drawRect(SkRect::Make(devLayerBounds), line_paint(2.f, SK_ColorMAGENTA));
-
-        // The destination bounds mapped back into the layer space, which should cover 'layerBounds'
-        SkPath backdropCoveringBounds;
-
-        // Add axis lines, to show perspective distortion
-        SkIRect local = fromGlobal.mapRect(SkRect::Make(devLayerBounds)).roundOut();
-        static int kAxisSpace = 10;
-        for (int y = local.fTop + kAxisSpace; y <= local.fBottom - kAxisSpace; y += kAxisSpace) {
-            backdropCoveringBounds.moveTo(local.fLeft, y);
-            backdropCoveringBounds.lineTo(local.fRight, y);
-        }
-        for (int x = local.fLeft + kAxisSpace; x <= local.fRight - kAxisSpace; x += kAxisSpace) {
-            backdropCoveringBounds.moveTo(x, local.fTop);
-            backdropCoveringBounds.lineTo(x, local.fBottom);
-        }
-
-        canvas->setMatrix(toGlobal);
-        canvas->drawPath(backdropCoveringBounds, line_paint(0.f, SK_ColorGREEN));
-
-        canvas->resetMatrix();
-        print_info(canvas, origLayerBounds, layerBounds, filterInputBounds, devLayerBounds);
-
-        canvas->restore();
-    }
-
-    SkString name() override { return SkString("BackdropBounds"); }
-
-private:
-
-    typedef Sample INHERITED;
-};
-
-DEF_SAMPLE(return new BackdropBoundsSample();)
diff --git a/third_party/skia/samplecode/SampleBitmapRect.cpp b/third_party/skia/samplecode/SampleBitmapRect.cpp
deleted file mode 100644
index 1f757f5..0000000
--- a/third_party/skia/samplecode/SampleBitmapRect.cpp
+++ /dev/null
@@ -1,160 +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 "include/core/SkBitmap.h"
-#include "include/core/SkCanvas.h"
-#include "include/core/SkColorFilter.h"
-#include "include/core/SkColorPriv.h"
-#include "include/core/SkFont.h"
-#include "include/core/SkGraphics.h"
-#include "include/core/SkPath.h"
-#include "include/core/SkRegion.h"
-#include "include/core/SkShader.h"
-#include "include/core/SkTime.h"
-#include "include/core/SkTypeface.h"
-#include "include/effects/SkGradientShader.h"
-#include "samplecode/Sample.h"
-#include "src/utils/SkUTF.h"
-
-#include "include/core/SkStream.h"
-#include "src/core/SkOSFile.h"
-
-static constexpr int INT_SIZE = 64;
-static constexpr float SCALAR_SIZE = (float)INT_SIZE;
-
-static void make_bitmap(SkBitmap* bitmap) {
-    bitmap->allocN32Pixels(INT_SIZE, INT_SIZE);
-    SkCanvas canvas(*bitmap);
-
-    canvas.drawColor(SK_ColorRED);
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    const SkPoint pts[] = { { 0, 0 }, { SCALAR_SIZE, SCALAR_SIZE } };
-    const SkColor colors[] = { SK_ColorWHITE, SK_ColorBLUE };
-    paint.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp));
-    canvas.drawCircle(SCALAR_SIZE/2, SCALAR_SIZE/2, SCALAR_SIZE/2, paint);
-}
-
-static void bounce(SkScalar* value, SkScalar* delta, SkScalar min, SkScalar max) {
-    *value += *delta;
-    if (*value < min) {
-        *value = min;
-        *delta = - *delta;
-    } else if (*value > max) {
-        *value = max;
-        *delta = - *delta;
-    }
-}
-
-static void bounce_pt(SkPoint* pt, SkVector* vec, const SkRect& limit) {
-    bounce(&pt->fX, &vec->fX, limit.fLeft, limit.fRight);
-    bounce(&pt->fY, &vec->fY, limit.fTop, limit.fBottom);
-}
-
-class BitmapRectView : public Sample {
-    SkPoint fSrcPt = {0, 0};
-    SkPoint fSrcVec = {0.866025f, 0.5f};
-
-    SkRect  fSrcLimit = {-SCALAR_SIZE/4,  -SCALAR_SIZE/4,
-                          SCALAR_SIZE*5/4, SCALAR_SIZE*5/4};
-    SkRect  fDstR[2] = {{10, 100, 260, 400}, {322.5, 100, 572.5, 400}};
-    SkBitmap fBitmap;
-
-    SkString name() override { return SkString("BitmapRect"); }
-
-    void onOnceBeforeDraw() override {
-        this->setBGColor(SK_ColorGRAY);
-        make_bitmap(&fBitmap);
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        SkRect srcR = {fSrcPt.fX - 16, fSrcPt.fY - 16,
-                       fSrcPt.fX + 16, fSrcPt.fY + 16};
-
-        SkPaint paint(SkColors::kYellow);
-        paint.setStyle(SkPaint::kStroke_Style);
-
-        canvas->translate(20, 20);
-
-        canvas->drawBitmap(fBitmap, 0, 0, &paint);
-        canvas->drawRect(srcR, paint);
-
-        for (int i = 0; i < 2; ++i) {
-            paint.setFilterQuality(1 == i ? kLow_SkFilterQuality : kNone_SkFilterQuality);
-            canvas->drawBitmapRect(fBitmap, srcR, fDstR[i], &paint,
-                                   SkCanvas::kStrict_SrcRectConstraint);
-            canvas->drawRect(fDstR[i], paint);
-        }
-    }
-
-    bool onAnimate(double nanos) override {
-        bounce_pt(&fSrcPt, &fSrcVec, fSrcLimit);
-        return true;
-    }
-};
-
-static constexpr int BIG_H = 120;
-
-static void make_big_bitmap(SkBitmap* bm) {
-    static const char gText[] =
-        "We the people, in order to form a more perfect union, establish justice,"
-        " ensure domestic tranquility, provide for the common defense, promote the"
-        " general welfare and ensure the blessings of liberty to ourselves and our"
-        " posterity, do ordain and establish this constitution for the United"
-        " States of America.";
-
-    SkFont font;
-    font.setSize(SkIntToScalar(BIG_H));
-
-    const int BIG_W = SkScalarRoundToInt(font.measureText(gText, strlen(gText), SkTextEncoding::kUTF8));
-
-    bm->allocN32Pixels(BIG_W, BIG_H);
-    bm->eraseColor(SK_ColorWHITE);
-
-    SkCanvas canvas(*bm);
-
-    canvas.drawSimpleText(gText, strlen(gText), SkTextEncoding::kUTF8, 0, font.getSize()*4/5, font, SkPaint());
-}
-
-class BitmapRectView2 : public Sample {
-    SkBitmap fBitmap;
-    SkRect   fSrcR = {0, 0, 3 * BIG_H, BIG_H};
-    SkRect   fLimitR;
-    SkScalar fDX = 1;
-    SkRect   fDstR[2] = {{20, 20, 620, 220}, {20, 270, 620, 470}};
-
-    SkString name() override { return SkString("BigBitmapRect"); }
-
-    void onOnceBeforeDraw() override {
-        this->setBGColor(SK_ColorGRAY);
-        make_big_bitmap(&fBitmap);
-        fLimitR = SkRect::Make(fBitmap.dimensions());
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        SkPaint paint;
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setColor(SK_ColorYELLOW);
-
-        for (int i = 0; i < 2; ++i) {
-            paint.setFilterQuality(1 == i ? kLow_SkFilterQuality : kNone_SkFilterQuality);
-            canvas->drawBitmapRect(fBitmap, fSrcR, fDstR[i], &paint,
-                                   SkCanvas::kStrict_SrcRectConstraint);
-            canvas->drawRect(fDstR[i], paint);
-        }
-    }
-
-    bool onAnimate(double nanos) override {
-        SkScalar width = fSrcR.width();
-        bounce(&fSrcR.fLeft, &fDX, fLimitR.fLeft, fLimitR.fRight - width);
-        fSrcR.fRight = fSrcR.fLeft + width;
-        return true;
-    }
-};
-
-DEF_SAMPLE( return new BitmapRectView(); )
-DEF_SAMPLE( return new BitmapRectView2(); )
diff --git a/third_party/skia/samplecode/SampleCCPRGeometry.cpp b/third_party/skia/samplecode/SampleCCPRGeometry.cpp
deleted file mode 100644
index dff8ed9..0000000
--- a/third_party/skia/samplecode/SampleCCPRGeometry.cpp
+++ /dev/null
@@ -1,495 +0,0 @@
-/*
- * 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 "include/core/SkTypes.h"
-
-#if SK_SUPPORT_GPU
-
-#include "include/core/SkCanvas.h"
-#include "include/core/SkPaint.h"
-#include "include/core/SkPath.h"
-#include "samplecode/Sample.h"
-#include "src/core/SkMakeUnique.h"
-#include "src/core/SkRectPriv.h"
-#include "src/gpu/GrClip.h"
-#include "src/gpu/GrContextPriv.h"
-#include "src/gpu/GrMemoryPool.h"
-#include "src/gpu/GrRenderTargetContext.h"
-#include "src/gpu/GrRenderTargetContextPriv.h"
-#include "src/gpu/GrResourceProvider.h"
-#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
-#include "src/gpu/ccpr/GrCCFillGeometry.h"
-#include "src/gpu/ccpr/GrCCStroker.h"
-#include "src/gpu/ccpr/GrGSCoverageProcessor.h"
-#include "src/gpu/ccpr/GrVSCoverageProcessor.h"
-#include "src/gpu/geometry/GrPathUtils.h"
-#include "src/gpu/gl/GrGLGpu.h"
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/ops/GrDrawOp.h"
-
-using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
-using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
-using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
-
-static constexpr float kDebugBloat = 40;
-
-/**
- * This sample visualizes the AA bloat geometry generated by the ccpr geometry shaders. It
- * increases the AA bloat by 50x and outputs color instead of coverage (coverage=+1 -> green,
- * coverage=0 -> black, coverage=-1 -> red). Use the keys 1-7 to cycle through the different
- * geometry processors.
- */
-class CCPRGeometryView : public Sample {
-    void onOnceBeforeDraw() override { this->updateGpuData(); }
-    void onDrawContent(SkCanvas*) override;
-
-    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
-    bool onClick(Sample::Click*) override;
-    bool onChar(SkUnichar) override;
-    SkString name() override { return SkString("CCPRGeometry"); }
-
-    class Click;
-    class DrawCoverageCountOp;
-    class VisualizeCoverageCountFP;
-
-    void updateAndInval() { this->updateGpuData(); }
-
-    void updateGpuData();
-
-    PrimitiveType fPrimitiveType = PrimitiveType::kTriangles;
-    SkCubicType fCubicType;
-    SkMatrix fCubicKLM;
-
-    SkPoint fPoints[4] = {
-            {100.05f, 100.05f}, {400.75f, 100.05f}, {400.75f, 300.95f}, {100.05f, 300.95f}};
-
-    float fConicWeight = .5;
-    float fStrokeWidth = 40;
-    bool fDoStroke = false;
-
-    SkTArray<TriPointInstance> fTriPointInstances;
-    SkTArray<QuadPointInstance> fQuadPointInstances;
-    SkPath fPath;
-};
-
-class CCPRGeometryView::DrawCoverageCountOp : public GrDrawOp {
-    DEFINE_OP_CLASS_ID
-
-public:
-    DrawCoverageCountOp(CCPRGeometryView* view) : INHERITED(ClassID()), fView(view) {
-        this->setBounds(SkRect::MakeIWH(fView->width(), fView->height()), GrOp::HasAABloat::kNo,
-                        GrOp::IsHairline::kNo);
-    }
-
-    const char* name() const override {
-        return "[Testing/Sample code] CCPRGeometryView::DrawCoverageCountOp";
-    }
-
-private:
-    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
-    GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
-                                      bool hasMixedSampledCoverage, GrClampType) override {
-        return GrProcessorSet::EmptySetAnalysis();
-    }
-    void onPrepare(GrOpFlushState*) override {}
-    void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
-
-    CCPRGeometryView* fView;
-
-    typedef GrDrawOp INHERITED;
-};
-
-class CCPRGeometryView::VisualizeCoverageCountFP : public GrFragmentProcessor {
-public:
-    VisualizeCoverageCountFP() : GrFragmentProcessor(kTestFP_ClassID, kNone_OptimizationFlags) {}
-
-private:
-    const char* name() const override {
-        return "[Testing/Sample code] CCPRGeometryView::VisualizeCoverageCountFP";
-    }
-    std::unique_ptr<GrFragmentProcessor> clone() const override {
-        return skstd::make_unique<VisualizeCoverageCountFP>();
-    }
-    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
-    bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
-
-    class Impl : public GrGLSLFragmentProcessor {
-        void emitCode(EmitArgs& args) override {
-            GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
-            f->codeAppendf("half count = %s.a;", args.fInputColor);
-            f->codeAppendf("%s = half4(clamp(-count, 0, 1), clamp(+count, 0, 1), 0, abs(count));",
-                           args.fOutputColor);
-        }
-    };
-
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new Impl; }
-};
-
-static void draw_klm_line(int w, int h, SkCanvas* canvas, const SkScalar line[3], SkColor color) {
-    SkPoint p1, p2;
-    if (SkScalarAbs(line[1]) > SkScalarAbs(line[0])) {
-        // Draw from vertical edge to vertical edge.
-        p1 = {0, -line[2] / line[1]};
-        p2 = {(SkScalar)w, (-line[2] - w * line[0]) / line[1]};
-    } else {
-        // Draw from horizontal edge to horizontal edge.
-        p1 = {-line[2] / line[0], 0};
-        p2 = {(-line[2] - h * line[1]) / line[0], (SkScalar)h};
-    }
-
-    SkPaint linePaint;
-    linePaint.setColor(color);
-    linePaint.setAlpha(128);
-    linePaint.setStyle(SkPaint::kStroke_Style);
-    linePaint.setStrokeWidth(0);
-    linePaint.setAntiAlias(true);
-    canvas->drawLine(p1, p2, linePaint);
-}
-
-void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
-    canvas->clear(SK_ColorBLACK);
-
-    if (!fDoStroke) {
-        SkPaint outlinePaint;
-        outlinePaint.setColor(0x80ffffff);
-        outlinePaint.setStyle(SkPaint::kStroke_Style);
-        outlinePaint.setStrokeWidth(0);
-        outlinePaint.setAntiAlias(true);
-        canvas->drawPath(fPath, outlinePaint);
-    }
-
-#if 0
-    SkPaint gridPaint;
-    gridPaint.setColor(0x10000000);
-    gridPaint.setStyle(SkPaint::kStroke_Style);
-    gridPaint.setStrokeWidth(0);
-    gridPaint.setAntiAlias(true);
-    for (int y = 0; y < this->height(); y += kDebugBloat) {
-        canvas->drawLine(0, y, this->width(), y, gridPaint);
-    }
-    for (int x = 0; x < this->width(); x += kDebugBloat) {
-        canvas->drawLine(x, 0, x, this->height(), outlinePaint);
-    }
-#endif
-
-    SkString caption;
-    if (GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext()) {
-        // Render coverage count.
-        GrContext* ctx = canvas->getGrContext();
-        SkASSERT(ctx);
-
-        GrOpMemoryPool* pool = ctx->priv().opMemoryPool();
-
-        auto ccbuff = ctx->priv().makeDeferredRenderTargetContext(SkBackingFit::kApprox,
-                                                                  this->width(), this->height(),
-                                                                  GrColorType::kAlpha_F16, nullptr);
-        SkASSERT(ccbuff);
-        ccbuff->clear(nullptr, SK_PMColor4fTRANSPARENT,
-                      GrRenderTargetContext::CanClearFullscreen::kYes);
-        ccbuff->priv().testingOnly_addDrawOp(pool->allocate<DrawCoverageCountOp>(this));
-
-        // Visualize coverage count in main canvas.
-        GrPaint paint;
-        paint.addColorFragmentProcessor(
-                GrSimpleTextureEffect::Make(sk_ref_sp(ccbuff->asTextureProxy()),
-                                            ccbuff->colorInfo().alphaType(), SkMatrix::I()));
-        paint.addColorFragmentProcessor(
-                skstd::make_unique<VisualizeCoverageCountFP>());
-        paint.setPorterDuffXPFactory(SkBlendMode::kSrcOver);
-        rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
-                      SkRect::MakeIWH(this->width(), this->height()));
-
-        // Add label.
-        caption.appendf("PrimitiveType_%s",
-                        GrCCCoverageProcessor::PrimitiveTypeName(fPrimitiveType));
-        if (PrimitiveType::kCubics == fPrimitiveType) {
-            caption.appendf(" (%s)", SkCubicTypeName(fCubicType));
-        } else if (PrimitiveType::kConics == fPrimitiveType) {
-            caption.appendf(" (w=%f)", fConicWeight);
-        }
-        if (fDoStroke) {
-            caption.appendf(" (stroke_width=%f)", fStrokeWidth);
-        }
-    } else {
-        caption = "Use GPU backend to visualize geometry.";
-    }
-
-    SkPaint pointsPaint;
-    pointsPaint.setColor(SK_ColorBLUE);
-    pointsPaint.setStrokeWidth(8);
-    pointsPaint.setAntiAlias(true);
-
-    if (PrimitiveType::kCubics == fPrimitiveType) {
-        canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
-        if (!fDoStroke) {
-            int w = this->width(), h = this->height();
-            draw_klm_line(w, h, canvas, &fCubicKLM[0], SK_ColorYELLOW);
-            draw_klm_line(w, h, canvas, &fCubicKLM[3], SK_ColorBLUE);
-            draw_klm_line(w, h, canvas, &fCubicKLM[6], SK_ColorRED);
-        }
-    } else {
-        canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, fPoints, pointsPaint);
-        canvas->drawPoints(SkCanvas::kPoints_PointMode, 1, fPoints + 3, pointsPaint);
-    }
-
-    SkFont font(nullptr, 20);
-    SkPaint captionPaint;
-    captionPaint.setColor(SK_ColorWHITE);
-    canvas->drawString(caption, 10, 30, font, captionPaint);
-}
-
-void CCPRGeometryView::updateGpuData() {
-    using Verb = GrCCFillGeometry::Verb;
-    fTriPointInstances.reset();
-    fQuadPointInstances.reset();
-
-    fPath.reset();
-    fPath.moveTo(fPoints[0]);
-
-    if (PrimitiveType::kCubics == fPrimitiveType) {
-        double t[2], s[2];
-        fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
-        GrCCFillGeometry geometry;
-        geometry.beginContour(fPoints[0]);
-        geometry.cubicTo(fPoints, kDebugBloat / 2, kDebugBloat / 2);
-        geometry.endContour();
-        int ptsIdx = 0;
-        for (Verb verb : geometry.verbs()) {
-            switch (verb) {
-                case Verb::kLineTo:
-                    ++ptsIdx;
-                    continue;
-                case Verb::kMonotonicQuadraticTo:
-                    ptsIdx += 2;
-                    continue;
-                case Verb::kMonotonicCubicTo:
-                    fQuadPointInstances.push_back().set(&geometry.points()[ptsIdx], 0, 0);
-                    ptsIdx += 3;
-                    continue;
-                default:
-                    continue;
-            }
-        }
-        fPath.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
-    } else if (PrimitiveType::kTriangles != fPrimitiveType) {
-        SkPoint P3[3] = {fPoints[0], fPoints[1], fPoints[3]};
-        GrCCFillGeometry geometry;
-        geometry.beginContour(P3[0]);
-        if (PrimitiveType::kQuadratics == fPrimitiveType) {
-            geometry.quadraticTo(P3);
-            fPath.quadTo(fPoints[1], fPoints[3]);
-        } else {
-            SkASSERT(PrimitiveType::kConics == fPrimitiveType);
-            geometry.conicTo(P3, fConicWeight);
-            fPath.conicTo(fPoints[1], fPoints[3], fConicWeight);
-        }
-        geometry.endContour();
-        int ptsIdx = 0, conicWeightIdx = 0;
-        for (Verb verb : geometry.verbs()) {
-            if (Verb::kBeginContour == verb ||
-                Verb::kEndOpenContour == verb ||
-                Verb::kEndClosedContour == verb) {
-                continue;
-            }
-            if (Verb::kLineTo == verb) {
-                ++ptsIdx;
-                continue;
-            }
-            SkASSERT(Verb::kMonotonicQuadraticTo == verb || Verb::kMonotonicConicTo == verb);
-            if (PrimitiveType::kQuadratics == fPrimitiveType &&
-                Verb::kMonotonicQuadraticTo == verb) {
-                fTriPointInstances.push_back().set(
-                        &geometry.points()[ptsIdx], Sk2f(0, 0),
-                        TriPointInstance::Ordering::kXYTransposed);
-            } else if (PrimitiveType::kConics == fPrimitiveType &&
-                       Verb::kMonotonicConicTo == verb) {
-                fQuadPointInstances.push_back().setW(&geometry.points()[ptsIdx], Sk2f(0, 0),
-                                                     geometry.getConicWeight(conicWeightIdx++));
-            }
-            ptsIdx += 2;
-        }
-    } else {
-        fTriPointInstances.push_back().set(
-                fPoints[0], fPoints[1], fPoints[3], Sk2f(0, 0),
-                TriPointInstance::Ordering::kXYTransposed);
-        fPath.lineTo(fPoints[1]);
-        fPath.lineTo(fPoints[3]);
-        fPath.close();
-    }
-}
-
-void CCPRGeometryView::DrawCoverageCountOp::onExecute(GrOpFlushState* state,
-                                                      const SkRect& chainBounds) {
-    GrResourceProvider* rp = state->resourceProvider();
-    GrContext* context = state->gpu()->getContext();
-    GrGLGpu* glGpu = GrBackendApi::kOpenGL == context->backend()
-                             ? static_cast<GrGLGpu*>(state->gpu())
-                             : nullptr;
-    if (glGpu) {
-        glGpu->handleDirtyContext();
-        // GR_GL_CALL(glGpu->glInterface(), PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_LINE));
-        GR_GL_CALL(glGpu->glInterface(), Enable(GR_GL_LINE_SMOOTH));
-    }
-
-    GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kPlus,
-                        state->drawOpArgs().outputSwizzle());
-
-    std::unique_ptr<GrCCCoverageProcessor> proc;
-    if (state->caps().shaderCaps()->geometryShaderSupport()) {
-        proc = skstd::make_unique<GrGSCoverageProcessor>();
-    } else {
-        proc = skstd::make_unique<GrVSCoverageProcessor>();
-    }
-
-    if (!fView->fDoStroke) {
-        proc->reset(fView->fPrimitiveType, rp);
-        SkDEBUGCODE(proc->enableDebugBloat(kDebugBloat));
-
-        SkSTArray<1, GrMesh> mesh;
-        if (PrimitiveType::kCubics == fView->fPrimitiveType ||
-            PrimitiveType::kConics == fView->fPrimitiveType) {
-            sk_sp<GrGpuBuffer> instBuff(
-                    rp->createBuffer(fView->fQuadPointInstances.count() * sizeof(QuadPointInstance),
-                                     GrGpuBufferType::kVertex, kDynamic_GrAccessPattern,
-                                     fView->fQuadPointInstances.begin()));
-            if (!fView->fQuadPointInstances.empty() && instBuff) {
-                proc->appendMesh(std::move(instBuff), fView->fQuadPointInstances.count(), 0, &mesh);
-            }
-        } else {
-            sk_sp<GrGpuBuffer> instBuff(
-                    rp->createBuffer(fView->fTriPointInstances.count() * sizeof(TriPointInstance),
-                                     GrGpuBufferType::kVertex, kDynamic_GrAccessPattern,
-                                     fView->fTriPointInstances.begin()));
-            if (!fView->fTriPointInstances.empty() && instBuff) {
-                proc->appendMesh(std::move(instBuff), fView->fTriPointInstances.count(), 0, &mesh);
-            }
-        }
-
-        if (!mesh.empty()) {
-            SkASSERT(1 == mesh.count());
-            proc->draw(state, pipeline, nullptr, mesh.begin(), 1, this->bounds());
-        }
-    } else if (PrimitiveType::kConics != fView->fPrimitiveType) {  // No conic stroke support yet.
-        GrCCStroker stroker(0,0,0);
-
-        SkPaint p;
-        p.setStyle(SkPaint::kStroke_Style);
-        p.setStrokeWidth(fView->fStrokeWidth);
-        p.setStrokeJoin(SkPaint::kMiter_Join);
-        p.setStrokeMiter(4);
-        // p.setStrokeCap(SkPaint::kRound_Cap);
-        stroker.parseDeviceSpaceStroke(fView->fPath, SkPathPriv::PointData(fView->fPath),
-                                       SkStrokeRec(p), p.getStrokeWidth(), GrScissorTest::kDisabled,
-                                       SkIRect::MakeWH(fView->width(), fView->height()), {0, 0});
-        GrCCStroker::BatchID batchID = stroker.closeCurrentBatch();
-
-        GrOnFlushResourceProvider onFlushRP(context->priv().drawingManager());
-        stroker.prepareToDraw(&onFlushRP);
-
-        SkIRect ibounds;
-        this->bounds().roundOut(&ibounds);
-        stroker.drawStrokes(state, proc.get(), batchID, ibounds);
-    }
-
-    if (glGpu) {
-        context->resetContext(kMisc_GrGLBackendState);
-    }
-}
-
-class CCPRGeometryView::Click : public Sample::Click {
-public:
-    Click(int ptIdx) : fPtIdx(ptIdx) {}
-
-    void doClick(SkPoint points[]) {
-        if (fPtIdx >= 0) {
-            points[fPtIdx] += fCurr - fPrev;
-        } else {
-            for (int i = 0; i < 4; ++i) {
-                points[i] += fCurr - fPrev;
-            }
-        }
-    }
-
-private:
-    int fPtIdx;
-};
-
-Sample::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) {
-    for (int i = 0; i < 4; ++i) {
-        if (PrimitiveType::kCubics != fPrimitiveType && 2 == i) {
-            continue;
-        }
-        if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
-            return new Click(i);
-        }
-    }
-    return new Click(-1);
-}
-
-bool CCPRGeometryView::onClick(Sample::Click* click) {
-    Click* myClick = (Click*)click;
-    myClick->doClick(fPoints);
-    this->updateAndInval();
-    return true;
-}
-
-bool CCPRGeometryView::onChar(SkUnichar unichar) {
-        if (unichar >= '1' && unichar <= '4') {
-            fPrimitiveType = PrimitiveType(unichar - '1');
-            if (fPrimitiveType >= PrimitiveType::kWeightedTriangles) {
-                fPrimitiveType = (PrimitiveType) ((int)fPrimitiveType + 1);
-            }
-            this->updateAndInval();
-            return true;
-        }
-        float* valueToScale = nullptr;
-        if (fDoStroke) {
-            valueToScale = &fStrokeWidth;
-        } else if (PrimitiveType::kConics == fPrimitiveType) {
-            valueToScale = &fConicWeight;
-        }
-        if (valueToScale) {
-            if (unichar == '+') {
-                *valueToScale *= 2;
-                this->updateAndInval();
-                return true;
-            }
-            if (unichar == '+' || unichar == '=') {
-                *valueToScale *= 5/4.f;
-                this->updateAndInval();
-                return true;
-            }
-            if (unichar == '-') {
-                *valueToScale *= 4/5.f;
-                this->updateAndInval();
-                return true;
-            }
-            if (unichar == '_') {
-                *valueToScale *= .5f;
-                this->updateAndInval();
-                return true;
-            }
-        }
-        if (unichar == 'D') {
-            SkDebugf("    SkPoint fPoints[4] = {\n");
-            SkDebugf("        {%ff, %ff},\n", fPoints[0].x(), fPoints[0].y());
-            SkDebugf("        {%ff, %ff},\n", fPoints[1].x(), fPoints[1].y());
-            SkDebugf("        {%ff, %ff},\n", fPoints[2].x(), fPoints[2].y());
-            SkDebugf("        {%ff, %ff}\n", fPoints[3].x(), fPoints[3].y());
-            SkDebugf("    };\n");
-            return true;
-        }
-        if (unichar == 'S') {
-            fDoStroke = !fDoStroke;
-            this->updateAndInval();
-        }
-        return false;
-}
-
-DEF_SAMPLE(return new CCPRGeometryView;)
-
-#endif  // SK_SUPPORT_GPU
diff --git a/third_party/skia/samplecode/SampleCamera.cpp b/third_party/skia/samplecode/SampleCamera.cpp
index 38201eb..41da126 100644
--- a/third_party/skia/samplecode/SampleCamera.cpp
+++ b/third_party/skia/samplecode/SampleCamera.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "include/core/SkCanvas.h"
+#include "include/core/SkImage.h"
 #include "include/core/SkShader.h"
 #include "include/core/SkString.h"
 #include "include/utils/SkCamera.h"
@@ -35,9 +36,8 @@
             if (GetResourceAsBitmap(resource, &bm)) {
                 SkRect src = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
                 SkRect dst = { -150, -150, 150, 150 };
-                SkMatrix matrix;
-                matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
-                fShaders.push_back(bm.makeShader(&matrix));
+                fShaders.push_back(bm.makeShader(SkSamplingOptions(SkFilterMode::kLinear),
+                                                 SkMatrix::RectToRect(src, dst)));
             }
         }
         this->setBGColor(0xFFDDDDDD);
@@ -61,7 +61,6 @@
             SkPaint paint;
             paint.setAntiAlias(true);
             paint.setShader(fShaders[fShaderIndex]);
-            paint.setFilterQuality(kLow_SkFilterQuality);
             SkRect r = { -150, -150, 150, 150 };
             canvas->drawRoundRect(r, 30, 30, paint);
         }
diff --git a/third_party/skia/samplecode/SampleChart.cpp b/third_party/skia/samplecode/SampleChart.cpp
index 448e1d7..37b9d06 100644
--- a/third_party/skia/samplecode/SampleChart.cpp
+++ b/third_party/skia/samplecode/SampleChart.cpp
@@ -7,7 +7,7 @@
 
 #include "include/core/SkCanvas.h"
 #include "include/core/SkPaint.h"
-#include "include/core/SkPath.h"
+#include "include/core/SkPathBuilder.h"
 #include "include/utils/SkRandom.h"
 #include "samplecode/Sample.h"
 
@@ -30,9 +30,7 @@
                       SkScalar yBase,
                       SkScalar xLeft, SkScalar xDelta,
                       int leftShift,
-                      SkPath* plot, SkPath* fill) {
-    plot->rewind();
-    fill->rewind();
+                      SkPathBuilder* plot, SkPathBuilder* fill) {
     plot->incReserve(topData.count());
     if (nullptr == bottomData) {
         fill->incReserve(topData.count() + 2);
@@ -81,9 +79,9 @@
 // A set of scrolling line plots with the area between each plot filled. Stresses out GPU path
 // filling
 class ChartView : public Sample {
-    static constexpr int kNumGraphs = 5;
-    static constexpr int kPixelsPerTick = 3;
-    static constexpr int kShiftPerFrame = 1;
+    inline static constexpr int kNumGraphs = 5;
+    inline static constexpr int kPixelsPerTick = 3;
+    inline static constexpr int kShiftPerFrame = 1;
     int                 fShift = 0;
     SkISize             fSize = {-1, -1};
     SkTDArray<SkScalar> fData[kNumGraphs];
@@ -102,7 +100,7 @@
         SkScalar height = SkIntToScalar(fSize.fHeight);
 
         if (sizeChanged) {
-            int dataPointCount = SkMax32(fSize.fWidth / kPixelsPerTick + 1, 2);
+            int dataPointCount = std::max(fSize.fWidth / kPixelsPerTick + 1, 2);
 
             for (int i = 0; i < kNumGraphs; ++i) {
                 SkScalar y = (kNumGraphs - i) * (height - ySpread) / (kNumGraphs + 1);
@@ -121,9 +119,6 @@
             }
         }
 
-        SkPath plotPath;
-        SkPath fillPath;
-
         static const SkScalar kStrokeWidth = SkIntToScalar(2);
         SkPaint plotPaint;
         SkPaint fillPaint;
@@ -135,7 +130,9 @@
         fillPaint.setAntiAlias(true);
         fillPaint.setStyle(SkPaint::kFill_Style);
 
+        SkPathBuilder plotPath, fillPath;
         SkTDArray<SkScalar>* prevData = nullptr;
+
         for (int i = 0; i < kNumGraphs; ++i) {
             gen_paths(fData[i],
                       prevData,
@@ -148,10 +145,10 @@
 
             // Make the fills partially transparent
             fillPaint.setColor((gColors[i] & 0x00ffffff) | 0x80000000);
-            canvas->drawPath(fillPath, fillPaint);
+            canvas->drawPath(fillPath.detach(), fillPaint);
 
             plotPaint.setColor(gColors[i]);
-            canvas->drawPath(plotPath, plotPaint);
+            canvas->drawPath(plotPath.detach(), plotPaint);
 
             prevData = fData + i;
         }
diff --git a/third_party/skia/samplecode/SampleChineseFling.cpp b/third_party/skia/samplecode/SampleChineseFling.cpp
index 1cdf784..0e62725 100644
--- a/third_party/skia/samplecode/SampleChineseFling.cpp
+++ b/third_party/skia/samplecode/SampleChineseFling.cpp
@@ -17,8 +17,8 @@
 #include "include/utils/SkRandom.h"
 
 #if SK_SUPPORT_GPU
-#include "include/gpu/GrContext.h"
-#include "src/gpu/GrContextPriv.h"
+#include "include/gpu/GrDirectContext.h"
+#include "src/gpu/GrDirectContextPriv.h"
 #endif
 
 static sk_sp<SkTypeface> chinese_typeface() {
@@ -38,8 +38,8 @@
 }
 
 class ChineseFlingView : public Sample {
-    static constexpr int kNumBlobs = 200;
-    static constexpr int kWordLength = 16;
+    inline static constexpr int kNumBlobs = 200;
+    inline static constexpr int kWordLength = 16;
 
     sk_sp<SkTypeface>    fTypeface;
     SkFontMetrics        fMetrics;
@@ -103,8 +103,8 @@
 };
 
 class ChineseZoomView : public Sample {
-    static constexpr int kNumBlobs = 8;
-    static constexpr int kParagraphLength = 175;
+    inline static constexpr int kNumBlobs = 8;
+    inline static constexpr int kParagraphLength = 175;
 
     bool                 fAfterFirstFrame = false;
     sk_sp<SkTypeface>    fTypeface;
@@ -137,24 +137,28 @@
 
         if (fAfterFirstFrame) {
 #if SK_SUPPORT_GPU
-            GrContext* grContext = canvas->getGrContext();
-            if (grContext) {
-                sk_sp<SkImage> image = grContext->priv().testingOnly_getFontAtlasImage(
+            auto direct = GrAsDirectContext(canvas->recordingContext());
+            if (direct) {
+                sk_sp<SkImage> image = direct->priv().testingOnly_getFontAtlasImage(
                             GrMaskFormat::kA8_GrMaskFormat, 0);
                 canvas->drawImageRect(image,
-                                      SkRect::MakeXYWH(10.0f, 10.0f, 512.0f, 512.0), &paint);
-                image = grContext->priv().testingOnly_getFontAtlasImage(
+                                      SkRect::MakeXYWH(10.0f, 10.0f, 512.0f, 512.0),
+                                      SkSamplingOptions(), &paint);
+                image = direct->priv().testingOnly_getFontAtlasImage(
                         GrMaskFormat::kA8_GrMaskFormat, 1);
                 canvas->drawImageRect(image,
-                                      SkRect::MakeXYWH(522.0f, 10.0f, 512.f, 512.0f), &paint);
-                image = grContext->priv().testingOnly_getFontAtlasImage(
+                                      SkRect::MakeXYWH(522.0f, 10.0f, 512.f, 512.0f),
+                                      SkSamplingOptions(), &paint);
+                image = direct->priv().testingOnly_getFontAtlasImage(
                         GrMaskFormat::kA8_GrMaskFormat, 2);
                 canvas->drawImageRect(image,
-                                      SkRect::MakeXYWH(10.0f, 522.0f, 512.0f, 512.0f), &paint);
-                image = grContext->priv().testingOnly_getFontAtlasImage(
+                                      SkRect::MakeXYWH(10.0f, 522.0f, 512.0f, 512.0f),
+                                      SkSamplingOptions(), &paint);
+                image = direct->priv().testingOnly_getFontAtlasImage(
                         GrMaskFormat::kA8_GrMaskFormat, 3);
                 canvas->drawImageRect(image,
-                                      SkRect::MakeXYWH(522.0f, 522.0f, 512.0f, 512.0f), &paint);
+                                      SkRect::MakeXYWH(522.0f, 522.0f, 512.0f, 512.0f),
+                                      SkSamplingOptions(), &paint);
             }
 #endif
         }
@@ -191,7 +195,7 @@
             auto paragraphLength = kParagraphLength;
             SkScalar y = 0;
             while (paragraphLength - 45 > 0) {
-                auto currentLineLength = SkTMin(45, paragraphLength - 45);
+                auto currentLineLength = std::min(45, paragraphLength - 45);
                 this->createRandomLine(glyphs, currentLineLength);
 
                 ToolUtils::add_to_text_blob_w_len(&builder,
diff --git a/third_party/skia/samplecode/SampleCircle.cpp b/third_party/skia/samplecode/SampleCircle.cpp
index c4126f9..3cd7c0b 100644
--- a/third_party/skia/samplecode/SampleCircle.cpp
+++ b/third_party/skia/samplecode/SampleCircle.cpp
@@ -35,7 +35,7 @@
             paint.setStrokeWidth(SkIntToScalar(width));
         }
         canvas->drawCircle(0, 0, 9.0f, paint);
-        if (false) { // avoid bit rot, suppress warning
+        if ((false)) { // avoid bit rot, suppress warning
             test_circlebounds(canvas);
         }
     }
diff --git a/third_party/skia/samplecode/SampleClip.cpp b/third_party/skia/samplecode/SampleClip.cpp
index bff7704..358e3f8 100644
--- a/third_party/skia/samplecode/SampleClip.cpp
+++ b/third_party/skia/samplecode/SampleClip.cpp
@@ -9,10 +9,11 @@
 #include "include/core/SkColorPriv.h"
 #include "include/core/SkFont.h"
 #include "include/core/SkPaint.h"
-#include "include/core/SkPath.h"
+#include "include/core/SkPathBuilder.h"
 #include "include/utils/SkRandom.h"
 #include "samplecode/Sample.h"
-#include "src/core/SkClipOpPriv.h"
+#include "src/core/SkPathPriv.h"
+#include "tools/Resources.h"
 
 constexpr int W = 150;
 constexpr int H = 200;
@@ -37,7 +38,6 @@
 
     for (int i = 0; i < 50; ++i) {
         SkRect r;
-        SkPath p;
 
         r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
                   rand.nextUScalar1() * W, rand.nextUScalar1() * H);
@@ -47,8 +47,7 @@
         r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
                   rand.nextUScalar1() * W, rand.nextUScalar1() * H);
         paint.setColor(rand.nextU());
-        p.addOval(r);
-        canvas->drawPath(p, paint);
+        canvas->drawOval(r, paint);
     }
 }
 
@@ -66,7 +65,6 @@
 
     for (int i = 0; i < n; ++i) {
         SkRect r;
-        SkPath p;
 
         r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
                   rand.nextUScalar1() * W, rand.nextUScalar1() * H);
@@ -76,8 +74,7 @@
         r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
                   rand.nextUScalar1() * W, rand.nextUScalar1() * H);
         paint.setColor(rand.nextU());
-        p.addOval(r);
-        canvas->drawPath(p, paint);
+        canvas->drawOval(r, paint);
 
         const SkScalar minx = -SkIntToScalar(W)/4;
         const SkScalar maxx = 5*SkIntToScalar(W)/4;
@@ -112,9 +109,8 @@
         };
 
         SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
-        SkPath clipPath;
         r.inset(SK_Scalar1 / 4, SK_Scalar1 / 4);
-        clipPath.addRoundRect(r, SkIntToScalar(20), SkIntToScalar(20));
+        SkPath clipPath = SkPathBuilder().addRRect(SkRRect::MakeRectXY(r, 20, 20)).detach();
 
 //        clipPath.toggleInverseFillType();
 
@@ -122,7 +118,7 @@
             canvas->save();
             for (size_t i = 0; i < SK_ARRAY_COUNT(gProc); ++i) {
                 canvas->save();
-                canvas->clipPath(clipPath, kIntersect_SkClipOp, SkToBool(aa));
+                canvas->clipPath(clipPath, SkClipOp::kIntersect, SkToBool(aa));
 //                canvas->drawColor(SK_ColorWHITE);
                 gProc[i](canvas, SkToBool(aa));
                 canvas->restore();
@@ -211,7 +207,7 @@
 
 #include "src/core/SkEdgeClipper.h"
 
-static void clip(const SkPath& path, SkPoint p0, SkPoint p1, SkPath* clippedPath) {
+static SkPath clip(const SkPath& path, SkPoint p0, SkPoint p1) {
     SkMatrix mx, inv;
     SkVector v = p1 - p0;
     mx.setAll(v.fX, -v.fY, p0.fX,
@@ -226,9 +222,9 @@
     SkRect clip = {-big, 0, big, big };
 
     struct Rec {
-        SkPath* fResult;
-        SkPoint fPrev;
-    } rec = { clippedPath, {0, 0} };
+        SkPathBuilder   fResult;
+        SkPoint         fPrev = {0, 0};
+    } rec;
 
     SkEdgeClipper::ClipPath(rotated, clip, false,
                             [](SkEdgeClipper* clipper, bool newCtr, void* ctx) {
@@ -239,26 +235,26 @@
         SkPath::Verb verb;
         while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
             if (newCtr) {
-                rec->fResult->moveTo(pts[0]);
+                rec->fResult.moveTo(pts[0]);
                 rec->fPrev = pts[0];
                 newCtr = false;
             }
 
             if (addLineTo || pts[0] != rec->fPrev) {
-                rec->fResult->lineTo(pts[0]);
+                rec->fResult.lineTo(pts[0]);
             }
 
             switch (verb) {
                 case SkPath::kLine_Verb:
-                    rec->fResult->lineTo(pts[1]);
+                    rec->fResult.lineTo(pts[1]);
                     rec->fPrev = pts[1];
                     break;
                 case SkPath::kQuad_Verb:
-                    rec->fResult->quadTo(pts[1], pts[2]);
+                    rec->fResult.quadTo(pts[1], pts[2]);
                     rec->fPrev = pts[2];
                     break;
                 case SkPath::kCubic_Verb:
-                    rec->fResult->cubicTo(pts[1], pts[2], pts[3]);
+                    rec->fResult.cubicTo(pts[1], pts[2], pts[3]);
                     rec->fPrev = pts[3];
                     break;
                 default: break;
@@ -267,27 +263,7 @@
         }
     }, &rec);
 
-    rec.fResult->transform(mx);
-}
-
-// true means use clippedPath.
-// false means there was no clipping -- use the original path
-static bool clip(const SkPath& path, const SkHalfPlane& plane, SkPath* clippedPath) {
-    switch (plane.test(path.getBounds())) {
-        case SkHalfPlane::kAllPositive:
-            return false;
-        case SkHalfPlane::kMixed: {
-            SkPoint pts[2];
-            if (plane.twoPts(pts)) {
-                clip(path, pts[0], pts[1], clippedPath);
-                return true;
-            }
-        } break;
-        default: break; // handled outside of the switch
-    }
-    // clipped out (or failed)
-    clippedPath->reset();
-    return true;
+    return rec.fResult.detach().makeTransform(mx);
 }
 
 static void draw_halfplane(SkCanvas* canvas, SkPoint p0, SkPoint p1, SkColor c) {
@@ -302,14 +278,21 @@
 
 static SkPath make_path() {
     SkRandom rand;
-    auto rand_pt = [&rand]() { return SkPoint{rand.nextF() * 400, rand.nextF() * 400}; };
+    auto rand_pt = [&rand]() {
+        auto x = rand.nextF();
+        auto y = rand.nextF();
+        return SkPoint{x * 400, y * 400};
+    };
 
-    SkPath path;
+    SkPathBuilder path;
     for (int i = 0; i < 4; ++i) {
-        path.moveTo(rand_pt()).quadTo(rand_pt(), rand_pt())
-            .quadTo(rand_pt(), rand_pt()).lineTo(rand_pt());
+        SkPoint pts[6];
+        for (auto& p : pts) {
+            p = rand_pt();
+        }
+        path.moveTo(pts[0]).quadTo(pts[1], pts[2]).quadTo(pts[3], pts[4]).lineTo(pts[5]);
     }
-    return path;
+    return path.detach();
 }
 
 class HalfPlaneView : public Sample {
@@ -332,9 +315,7 @@
 
         paint.setColor({0, 0, 0, 1}, nullptr);
 
-        SkPath clippedPath;
-        clip(fPath, fPts[0], fPts[1], &clippedPath);
-        canvas->drawPath(clippedPath, paint);
+        canvas->drawPath(clip(fPath, fPts[0], fPts[1]), paint);
 
         draw_halfplane(canvas, fPts[0], fPts[1], SK_ColorRED);
     }
@@ -357,16 +338,16 @@
     draw_halfplane(canvas, pts[0], pts[1], c);
 }
 
-static void compute_half_planes(const SkMatrix& mx, SkScalar W, SkScalar H,
+static void compute_half_planes(const SkMatrix& mx, SkScalar width, SkScalar height,
                                 SkHalfPlane planes[4]) {
     SkScalar a = mx[0], b = mx[1], c = mx[2],
              d = mx[3], e = mx[4], f = mx[5],
              g = mx[6], h = mx[7], i = mx[8];
 
-    planes[0] = { 2*g - 2*a/W,  2*h - 2*b/W,  2*i - 2*c/W };
-    planes[1] = { 2*a/W,        2*b/W,        2*c/W };
-    planes[2] = { 2*g - 2*d/H,  2*h - 2*e/H,  2*i - 2*f/H };
-    planes[3] = { 2*d/H,        2*e/H,        2*f/H };
+    planes[0] = { 2*g - 2*a/width,  2*h - 2*b/width,  2*i - 2*c/width };
+    planes[1] = { 2*a/width,        2*b/width,        2*c/width };
+    planes[2] = { 2*g - 2*d/height, 2*h - 2*e/height, 2*i - 2*f/height };
+    planes[3] = { 2*d/height,       2*e/height,       2*f/height };
 }
 
 class HalfPlaneView2 : public Sample {
@@ -435,55 +416,78 @@
 };
 DEF_SAMPLE( return new HalfPlaneView2(); )
 
-#include "include/core/SkMatrix44.h"
-#include "include/utils/Sk3D.h"
-#include "tools/Resources.h"
-
-static SkMatrix44 inv(const SkMatrix44& m) {
-    SkMatrix44 inverse;
+static SkM44 inv(const SkM44& m) {
+    SkM44 inverse;
     SkAssertResult(m.invert(&inverse));
     return inverse;
 }
 
-#if 0   // Jim's general half-planes math
-static void half_planes(const SkMatrix44& m44, SkScalar W, SkScalar H, SkHalfPlane planes[6]) {
-    float mx[16];
-    m44.asColMajorf(mx);
-
-    SkScalar a = mx[0], b = mx[4], /* c = mx[ 8], */ d = mx[12],
-             e = mx[1], f = mx[5], /* g = mx[ 9], */ h = mx[13],
-             i = mx[2], j = mx[6], /* k = mx[10], */ l = mx[14],
-             m = mx[3], n = mx[7], /* o = mx[11], */ p = mx[15];
-
-    a = 2*a/W - m;  b = 2*b/W - n;  d = 2*d/W - p;
-    e = 2*e/H - m;  f = 2*f/H - n;  h = 2*h/H - p;
-//    i = 2*i   - m;  j = 2*j   - n;  l = 2*l   - p;
-
-    planes[0] = { m - a, n - b, p - d }; // w - x
-    planes[1] = { m + a, n + b, p + d }; // w + x
-    planes[2] = { m - e, n - f, p - h }; // w - y
-    planes[3] = { m + e, n + f, p + h }; // w + y
-    planes[4] = { m - i, n - j, p - l }; // w - z
-    planes[5] = { m + i, n + j, p + l }; // w + z
-}
-#endif
-
 static SkHalfPlane half_plane_w0(const SkMatrix& m) {
     return { m[SkMatrix::kMPersp0], m[SkMatrix::kMPersp1], m[SkMatrix::kMPersp2] - 0.05f };
 }
 
-class HalfPlaneView3 : public Sample {
+class SampleCameraView : public Sample {
     float   fNear = 0.05f;
     float   fFar = 4;
     float   fAngle = SK_ScalarPI / 4;
 
-    SkPoint3    fEye { 0, 0, 1.0f/tan(fAngle/2) - 1 };
-    SkPoint3    fCOA { 0, 0, 0 };
-    SkPoint3    fUp  { 0, 1, 0 };
+    SkV3    fEye { 0, 0, 1.0f/tan(fAngle/2) - 1 };
+    SkV3    fCOA { 0, 0, 0 };
+    SkV3    fUp  { 0, 1, 0 };
 
-    SkMatrix44  fRot;
-    SkPoint3    fTrans;
+    SkM44  fRot;
+    SkV3   fTrans;
 
+    void rotate(float x, float y, float z) {
+        SkM44 r;
+        if (x) {
+            r.setRotateUnit({1, 0, 0}, x);
+        } else if (y) {
+            r.setRotateUnit({0, 1, 0}, y);
+        } else {
+            r.setRotateUnit({0, 0, 1}, z);
+        }
+        fRot = r * fRot;
+    }
+
+public:
+    SkM44 get44(const SkRect& r) const {
+        SkScalar w = r.width();
+        SkScalar h = r.height();
+
+        SkM44 camera = SkM44::LookAt(fEye, fCOA, fUp),
+              perspective = SkM44::Perspective(fNear, fFar, fAngle),
+              translate = SkM44::Translate(fTrans.x, fTrans.y, fTrans.z),
+              viewport = SkM44::Translate(r.centerX(), r.centerY(), 0) *
+                         SkM44::Scale(w*0.5f, h*0.5f, 1);
+
+        return viewport * perspective * camera * translate * fRot * inv(viewport);
+    }
+
+    bool onChar(SkUnichar uni) override {
+        float delta = SK_ScalarPI / 30;
+        switch (uni) {
+            case '8': this->rotate( delta, 0, 0); return true;
+            case '2': this->rotate(-delta, 0, 0); return true;
+            case '4': this->rotate(0,  delta, 0); return true;
+            case '6': this->rotate(0, -delta, 0); return true;
+            case '-': this->rotate(0, 0,  delta); return true;
+            case '+': this->rotate(0, 0, -delta); return true;
+
+            case 'i': fTrans.z += 0.1f; SkDebugf("z %g\n", fTrans.z); return true;
+            case 'k': fTrans.z -= 0.1f; SkDebugf("z %g\n", fTrans.z); return true;
+
+            case 'n': fNear += 0.1f; SkDebugf("near %g\n", fNear); return true;
+            case 'N': fNear -= 0.1f; SkDebugf("near %g\n", fNear); return true;
+            case 'f': fFar  += 0.1f; SkDebugf("far  %g\n", fFar); return true;
+            case 'F': fFar  -= 0.1f; SkDebugf("far  %g\n", fFar); return true;
+            default: break;
+        }
+        return false;
+    }
+};
+
+class HalfPlaneView3 : public SampleCameraView {
     SkPath fPath;
     sk_sp<SkShader> fShader;
     bool fShowUnclipped = false;
@@ -493,37 +497,19 @@
     void onOnceBeforeDraw() override {
         fPath = make_path();
         fShader = GetResourceAsImage("images/mandrill_128.png")
-                        ->makeShader(SkMatrix::MakeScale(3, 3));
+                        ->makeShader(SkSamplingOptions(), SkMatrix::Scale(3, 3));
     }
 
-    void rotate(float x, float y, float z) {
-        SkMatrix44 r;
-        if (x) {
-            r.setRotateAboutUnit(1, 0, 0, x);
-        } else if (y) {
-            r.setRotateAboutUnit(0, 1, 0, y);
-        } else {
-            r.setRotateAboutUnit(0, 0, 1, z);
+    bool onChar(SkUnichar uni) override {
+        switch (uni) {
+            case 'u': fShowUnclipped = !fShowUnclipped; return true;
+            default: break;
         }
-        fRot.postConcat(r);
-    }
-
-    SkMatrix44 get44() const {
-        SkMatrix44  camera,
-                    perspective,
-                    translate,
-                    viewport;
-
-        Sk3Perspective(&perspective, fNear, fFar, fAngle);
-        Sk3LookAt(&camera, fEye, fCOA, fUp);
-        translate.setTranslate(fTrans.fX, fTrans.fY, fTrans.fZ);
-        viewport.setScale(200, 200, 1).postTranslate( 200,  200, 0);
-
-        return viewport * perspective * camera * translate * fRot * inv(viewport);
+        return this->SampleCameraView::onChar(uni);
     }
 
     void onDrawContent(SkCanvas* canvas) override {
-        SkMatrix mx = this->get44();
+        SkM44 mx = this->get44({0, 0, 400, 400});
 
         SkPaint paint;
         paint.setColor({0.75, 0.75, 0.75, 1});
@@ -540,11 +526,10 @@
             canvas->restore();
         }
 
-        SkHalfPlane hpw = half_plane_w0(mx);
 
         SkColor planeColor = SK_ColorBLUE;
         SkPath clippedPath, *path = &fPath;
-        if (clip(fPath, hpw, &clippedPath)) {
+        if (SkPathPriv::PerspectiveClip(fPath, mx.asM33(), &clippedPath)) {
             path = &clippedPath;
             planeColor = SK_ColorRED;
         }
@@ -553,38 +538,97 @@
         canvas->drawPath(*path, paint);
         canvas->restore();
 
+        SkHalfPlane hpw = half_plane_w0(mx.asM33());
         draw_halfplane(canvas, hpw, planeColor);
     }
-
-    bool onChar(SkUnichar uni) override {
-        float delta = SK_ScalarPI / 30;
-        switch (uni) {
-            case '8': this->rotate( delta, 0, 0); return true;
-            case '2': this->rotate(-delta, 0, 0); return true;
-            case '4': this->rotate(0,  delta, 0); return true;
-            case '6': this->rotate(0, -delta, 0); return true;
-            case '-': this->rotate(0, 0,  delta); return true;
-            case '+': this->rotate(0, 0, -delta); return true;
-
-            case 'i': fTrans.fZ += 0.1f; SkDebugf("z %g\n", fTrans.fZ); return true;
-            case 'k': fTrans.fZ -= 0.1f; SkDebugf("z %g\n", fTrans.fZ); return true;
-
-            case 'n': fNear += 0.1f; SkDebugf("near %g\n", fNear); return true;
-            case 'N': fNear -= 0.1f; SkDebugf("near %g\n", fNear); return true;
-            case 'f': fFar  += 0.1f; SkDebugf("far  %g\n", fFar); return true;
-            case 'F': fFar  -= 0.1f; SkDebugf("far  %g\n", fFar); return true;
-
-            case 'u': fShowUnclipped = !fShowUnclipped; return true;
-            default: break;
-        }
-        return false;
-    }
-    Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
-        return nullptr;
-    }
-
-    bool onClick(Click* click) override {
-        return false;
-    }
 };
 DEF_SAMPLE( return new HalfPlaneView3(); )
+
+class HalfPlaneCoons : public SampleCameraView {
+    SkPoint fPatch[12];
+    SkColor fColors[4] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK };
+    SkPoint fTex[4]    = {{0, 0}, {256, 0}, {256, 256}, {0, 256}};
+    sk_sp<SkShader> fShader;
+
+    bool fShowHandles = false;
+    bool fShowSkeleton = false;
+    bool fShowTex = false;
+
+    SkString name() override { return SkString("halfplane-coons"); }
+
+    void onOnceBeforeDraw() override {
+        fPatch[0] = {   0, 0 };
+        fPatch[1] = { 100, 0 };
+        fPatch[2] = { 200, 0 };
+        fPatch[3] = { 300, 0 };
+        fPatch[4] = { 300, 100 };
+        fPatch[5] = { 300, 200 };
+        fPatch[6] = { 300, 300 };
+        fPatch[7] = { 200, 300 };
+        fPatch[8] = { 100, 300 };
+        fPatch[9] = {   0, 300 };
+        fPatch[10] = {  0, 200 };
+        fPatch[11] = {  0, 100 };
+
+        fShader = GetResourceAsImage("images/mandrill_256.png")->makeShader(SkSamplingOptions());
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        SkPaint paint;
+
+        canvas->save();
+        canvas->concat(this->get44({0, 0, 300, 300}));
+
+        const SkPoint* tex = nullptr;
+        const SkColor* col = nullptr;
+        if (!fShowSkeleton) {
+            if (fShowTex) {
+                paint.setShader(fShader);
+                tex = fTex;
+            } else {
+                col = fColors;
+            }
+        }
+        canvas->drawPatch(fPatch, col, tex, SkBlendMode::kSrc, paint);
+        paint.setShader(nullptr);
+
+        if (fShowHandles) {
+            paint.setAntiAlias(true);
+            paint.setStrokeCap(SkPaint::kRound_Cap);
+            paint.setStrokeWidth(8);
+            canvas->drawPoints(SkCanvas::kPoints_PointMode, 12, fPatch, paint);
+            paint.setColor(SK_ColorWHITE);
+            paint.setStrokeWidth(6);
+            canvas->drawPoints(SkCanvas::kPoints_PointMode, 12, fPatch, paint);
+        }
+
+        canvas->restore();
+    }
+
+    Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
+        auto dist = [](SkPoint a, SkPoint b) { return (b - a).length(); };
+
+        const float tol = 15;
+        for (int i = 0; i < 12; ++i) {
+            if (dist({x,y}, fPatch[i]) <= tol) {
+                return new Click([this, i](Click* c) {
+                    fPatch[i] = c->fCurr;
+                    return true;
+                });
+            }
+        }
+        return nullptr;
+    }
+
+    bool onChar(SkUnichar uni) override {
+        switch (uni) {
+            case 'h': fShowHandles = !fShowHandles; return true;
+            case 'k': fShowSkeleton = !fShowSkeleton; return true;
+            case 't': fShowTex = !fShowTex; return true;
+            default: break;
+        }
+        return this->SampleCameraView::onChar(uni);
+    }
+
+};
+DEF_SAMPLE( return new HalfPlaneCoons(); )
diff --git a/third_party/skia/samplecode/SampleClipDrawMatch.cpp b/third_party/skia/samplecode/SampleClipDrawMatch.cpp
deleted file mode 100644
index bdbb54b..0000000
--- a/third_party/skia/samplecode/SampleClipDrawMatch.cpp
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * 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 "include/core/SkCanvas.h"
-#include "include/core/SkPath.h"
-#include "include/core/SkRRect.h"
-#include "include/core/SkTime.h"
-#include "include/utils/SkInterpolator.h"
-#include "samplecode/Sample.h"
-
-// This slide tests out the match up between BW clipping and rendering. It can
-// draw a large rect through some clip geometry and draw the same geometry
-// normally. Which one is drawn first can be toggled. The pair of objects is translated
-// fractionally (via an animator) to expose snapping bugs. The key bindings are:
-//      1-9: the different geometries
-//      t:   toggle which is drawn first the clip or the normal geometry
-//      f:   flip-flops which corner the bottom AA clip rect occupies in the complex clip cases
-
-// The possible geometric combinations to test
-enum Geometry {
-    kRect_Geometry,
-    kRRect_Geometry,
-    kCircle_Geometry,
-    kConvexPath_Geometry,
-    kConcavePath_Geometry,
-    kRectAndRect_Geometry,
-    kRectAndRRect_Geometry,
-    kRectAndConvex_Geometry,
-    kRectAndConcave_Geometry
-};
-
-// The basic rect used is [kMin,kMin]..[kMax,kMax]
-static const float kMin = 100.5f;
-static const float kMid = 200.0f;
-static const float kMax = 299.5f;
-
-// The translation applied to the base AA rect in the combination cases
-// (i.e., kRectAndRect through kRectAndConcave)
-static const float kXlate = 100.0f;
-
-SkRect create_rect(const SkPoint& offset) {
-    SkRect r = SkRect::MakeLTRB(kMin, kMin, kMax, kMax);
-    r.offset(offset);
-    return r;
-}
-
-SkRRect create_rrect(const SkPoint& offset) {
-    SkRRect rrect;
-    rrect.setRectXY(create_rect(offset), 10, 10);
-    return rrect;
-}
-
-SkRRect create_circle(const SkPoint& offset) {
-    SkRRect circle;
-    circle.setOval(create_rect(offset));
-    return circle;
-}
-
-SkPath create_convex_path(const SkPoint& offset) {
-    SkPath convexPath;
-    convexPath.moveTo(kMin, kMin);
-    convexPath.lineTo(kMax, kMax);
-    convexPath.lineTo(kMin, kMax);
-    convexPath.close();
-    convexPath.offset(offset.fX, offset.fY);
-    return convexPath;
-}
-
-SkPath create_concave_path(const SkPoint& offset) {
-    SkPath concavePath;
-    concavePath.moveTo(kMin, kMin);
-    concavePath.lineTo(kMid, 105.0f);
-    concavePath.lineTo(kMax, kMin);
-    concavePath.lineTo(295.0f, kMid);
-    concavePath.lineTo(kMax, kMax);
-    concavePath.lineTo(kMid, 295.0f);
-    concavePath.lineTo(kMin, kMax);
-    concavePath.lineTo(105.0f, kMid);
-    concavePath.close();
-
-    concavePath.offset(offset.fX, offset.fY);
-    return concavePath;
-}
-
-static void draw_normal_geom(SkCanvas* canvas, const SkPoint& offset, int geom, bool useAA) {
-    SkPaint p;
-    p.setAntiAlias(useAA);
-    p.setColor(SK_ColorBLACK);
-
-    switch (geom) {
-    case kRect_Geometry:                // fall thru
-    case kRectAndRect_Geometry:
-        canvas->drawRect(create_rect(offset), p);
-        break;
-    case kRRect_Geometry:               // fall thru
-    case kRectAndRRect_Geometry:
-        canvas->drawRRect(create_rrect(offset), p);
-        break;
-    case kCircle_Geometry:
-        canvas->drawRRect(create_circle(offset), p);
-        break;
-    case kConvexPath_Geometry:          // fall thru
-    case kRectAndConvex_Geometry:
-        canvas->drawPath(create_convex_path(offset), p);
-        break;
-    case kConcavePath_Geometry:         // fall thru
-    case kRectAndConcave_Geometry:
-        canvas->drawPath(create_concave_path(offset), p);
-        break;
-    }
-}
-
-class ClipDrawMatchView : public Sample {
-    SkInterpolator  fTrans;
-    Geometry        fGeom;
-    bool            fClipFirst = true;
-    int             fSign = 1;
-    const double    fStart = SkTime::GetMSecs();
-
-public:
-    ClipDrawMatchView() : fTrans(2, 5), fGeom(kRect_Geometry) {}
-
-private:
-    void onOnceBeforeDraw() override {
-        SkScalar values[2];
-
-        fTrans.setRepeatCount(999);
-        values[0] = values[1] = 0;
-        fTrans.setKeyFrame(0, GetMSecs() + 1000, values);
-        values[1] = 1;
-        fTrans.setKeyFrame(1, GetMSecs() + 2000, values);
-        values[0] = values[1] = 1;
-        fTrans.setKeyFrame(2, GetMSecs() + 3000, values);
-        values[1] = 0;
-        fTrans.setKeyFrame(3, GetMSecs() + 4000, values);
-        values[0] = 0;
-        fTrans.setKeyFrame(4, GetMSecs() + 5000, values);
-    }
-
-    SkString name() override { return SkString("ClipDrawMatch"); }
-
-    bool onChar(SkUnichar uni) override {
-            switch (uni) {
-                case '1': fGeom = kRect_Geometry; return true;
-                case '2': fGeom = kRRect_Geometry; return true;
-                case '3': fGeom = kCircle_Geometry; return true;
-                case '4': fGeom = kConvexPath_Geometry; return true;
-                case '5': fGeom = kConcavePath_Geometry; return true;
-                case '6': fGeom = kRectAndRect_Geometry; return true;
-                case '7': fGeom = kRectAndRRect_Geometry; return true;
-                case '8': fGeom = kRectAndConvex_Geometry; return true;
-                case '9': fGeom = kRectAndConcave_Geometry; return true;
-                case 'f': fSign = -fSign; return true;
-                case 't': fClipFirst = !fClipFirst; return true;
-                default: break;
-            }
-            return false;
-    }
-
-    void drawClippedGeom(SkCanvas* canvas, const SkPoint& offset, bool useAA) {
-
-        int count = canvas->save();
-
-        switch (fGeom) {
-        case kRect_Geometry:
-            canvas->clipRect(create_rect(offset), useAA);
-            break;
-        case kRRect_Geometry:
-            canvas->clipRRect(create_rrect(offset), useAA);
-            break;
-        case kCircle_Geometry:
-            canvas->clipRRect(create_circle(offset), useAA);
-            break;
-        case kConvexPath_Geometry:
-            canvas->clipPath(create_convex_path(offset), useAA);
-            break;
-        case kConcavePath_Geometry:
-            canvas->clipPath(create_concave_path(offset), useAA);
-            break;
-        case kRectAndRect_Geometry: {
-            SkRect r = create_rect(offset);
-            r.offset(fSign * kXlate, fSign * kXlate);
-            canvas->clipRect(r, true); // AA here forces shader clips
-            canvas->clipRect(create_rect(offset), useAA);
-            } break;
-        case kRectAndRRect_Geometry: {
-            SkRect r = create_rect(offset);
-            r.offset(fSign * kXlate, fSign * kXlate);
-            canvas->clipRect(r, true); // AA here forces shader clips
-            canvas->clipRRect(create_rrect(offset), useAA);
-            } break;
-        case kRectAndConvex_Geometry: {
-            SkRect r = create_rect(offset);
-            r.offset(fSign * kXlate, fSign * kXlate);
-            canvas->clipRect(r, true); // AA here forces shader clips
-            canvas->clipPath(create_convex_path(offset), useAA);
-            } break;
-        case kRectAndConcave_Geometry: {
-            SkRect r = create_rect(offset);
-            r.offset(fSign * kXlate, fSign * kXlate);
-            canvas->clipRect(r, true); // AA here forces shader clips
-            canvas->clipPath(create_concave_path(offset), useAA);
-            } break;
-        }
-
-        SkISize size = canvas->getBaseLayerSize();
-        SkRect bigR = SkRect::MakeWH(SkIntToScalar(size.width()), SkIntToScalar(size.height()));
-
-        SkPaint p;
-        p.setColor(SK_ColorRED);
-
-        canvas->drawRect(bigR, p);
-        canvas->restoreToCount(count);
-    }
-
-    // Draw a big red rect through some clip geometry and also draw that same
-    // geometry in black. The order in which they are drawn can be swapped.
-    // This tests whether the clip and normally drawn geometry match up.
-    void drawGeometry(SkCanvas* canvas, const SkPoint& offset, bool useAA) {
-        if (fClipFirst) {
-            this->drawClippedGeom(canvas, offset, useAA);
-        }
-
-        draw_normal_geom(canvas, offset, fGeom, useAA);
-
-        if (!fClipFirst) {
-            this->drawClippedGeom(canvas, offset, useAA);
-        }
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        SkScalar trans[2];
-        fTrans.timeToValues(GetMSecs(), trans);
-
-        SkPoint offset;
-        offset.set(trans[0], trans[1]);
-
-        int saveCount = canvas->save();
-        this->drawGeometry(canvas, offset, false);
-        canvas->restoreToCount(saveCount);
-    }
-
-    SkMSec GetMSecs() const {
-        return static_cast<SkMSec>(SkTime::GetMSecs() - fStart);
-    }
-};
-
-DEF_SAMPLE( return new ClipDrawMatchView(); )
diff --git a/third_party/skia/samplecode/SampleColorFilter.cpp b/third_party/skia/samplecode/SampleColorFilter.cpp
deleted file mode 100644
index 7f57a32..0000000
--- a/third_party/skia/samplecode/SampleColorFilter.cpp
+++ /dev/null
@@ -1,183 +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 "include/core/SkCanvas.h"
-#include "include/core/SkColorFilter.h"
-#include "include/core/SkPaint.h"
-#include "include/core/SkShader.h"
-#include "samplecode/Sample.h"
-#include "tools/ToolUtils.h"
-
-static int inflate5To8(int x) {
-    return (x << 3) | (x >> 2);
-}
-
-static int trunc5(int x) {
-    return x >> 3;
-}
-
-#define SK_R16_BITS 5
-
-#ifdef SK_DEBUG
-static int round5_slow(int x) {
-    int orig = x & 7;
-    int fake = x >> 5;
-    int trunc = x >> 3;
-
-    int diff = fake - orig;
-
-    int bias = 0;
-    if (diff > 4) {
-        bias = -1;
-    } else if (diff < -4) {
-        bias = 1;
-    }
-    return trunc + bias;
-}
-#endif
-
-static int round5_fast(int x) {
-    int result = x + 3 - (x >> 5) + (x >> 7);
-    result >>= 3;
-#ifdef SK_DEBUG
-    {
-        int r2 = round5_slow(x);
-        SkASSERT(r2 == result);
-    }
-#endif
-    return result;
-}
-
-static void test_5bits() {
-    int e0 = 0;
-    int e1 = 0;
-    int e2 = 0;
-    int ae0 = 0;
-    int ae1 = 0;
-    int ae2 = 0;
-    for (int i = 0; i < 256; i++) {
-        int t0 = trunc5(i);
-        int t1 = round5_fast(i);
-        int t2 = trunc5(i);
-        int v0 = inflate5To8(t0);
-        int v1 = inflate5To8(t1);
-        int v2 = inflate5To8(t2);
-        int err0 = i - v0;
-        int err1 = i - v1;
-        int err2 = i - v2;
-        SkDebugf("--- %3d : trunc=%3d (%2d) round:%3d (%2d) \n"/*new:%d (%2d)\n"*/, i,
-                 v0, err0, v1, err1, v2, err2);
-
-
-        e0 += err0;
-        e1 += err1;
-        e2 += err2;
-        ae0 += SkAbs32(err0);
-        ae1 += SkAbs32(err1);
-        ae2 += SkAbs32(err2);
-    }
-    SkDebugf("--- trunc: %d %d  round: %d %d new: %d %d\n", e0, ae0, e1, ae1, e2, ae2);
-}
-
-static SkBitmap createBitmap(int n) {
-    SkBitmap bitmap;
-    bitmap.allocN32Pixels(n, n);
-    bitmap.eraseColor(SK_ColorTRANSPARENT);
-
-    SkCanvas canvas(bitmap);
-    SkRect r;
-    r.setWH(SkIntToScalar(n), SkIntToScalar(n));
-    r.inset(SK_Scalar1, SK_Scalar1);
-
-    SkPaint paint;
-    paint.setAntiAlias(true);
-
-    paint.setColor(SK_ColorRED);
-    canvas.drawOval(r, paint);
-
-    r.inset(SK_Scalar1*n/4, SK_Scalar1*n/4);
-    paint.setBlendMode(SkBlendMode::kSrc);
-    paint.setColor(0x800000FF);
-    canvas.drawOval(r, paint);
-
-    return bitmap;
-}
-
-class ColorFilterView : public Sample {
-    SkBitmap fBitmap;
-    sk_sp<SkShader> fShader;
-    enum {
-        N = 64
-    };
-
-    void onOnceBeforeDraw() override {
-        fBitmap = createBitmap(N);
-        fShader = ToolUtils::create_checkerboard_shader(0xFFCCCCCC, 0xFFFFFFFF, 12);
-
-        if (false) { // avoid bit rot, suppress warning
-            test_5bits();
-        }
-    }
-
-    SkString name() override { return SkString("ColorFilter"); }
-
-    void onDrawBackground(SkCanvas* canvas) override {
-        SkPaint paint;
-        paint.setShader(fShader);
-        canvas->drawPaint(paint);
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        if (false) {
-            SkPaint p;
-            p.setAntiAlias(true);
-            SkRect r = { 20.4f, 10, 20.6f, 20 };
-            canvas->drawRect(r, p);
-            r.setLTRB(30.9f, 10, 31.1f, 20);
-            canvas->drawRect(r, p);
-            return;
-        }
-
-        static const SkBlendMode gModes[] = {
-            SkBlendMode::kClear,
-            SkBlendMode::kSrc,
-            SkBlendMode::kDst,
-            SkBlendMode::kSrcOver,
-            SkBlendMode::kDstOver,
-            SkBlendMode::kSrcIn,
-            SkBlendMode::kDstIn,
-            SkBlendMode::kSrcOut,
-            SkBlendMode::kDstOut,
-            SkBlendMode::kSrcATop,
-            SkBlendMode::kDstATop,
-            SkBlendMode::kXor,
-            SkBlendMode::kPlus,
-            SkBlendMode::kModulate,
-        };
-
-        static const SkColor gColors[] = {
-            0xFF000000,
-            0x80000000,
-            0xFF00FF00,
-            0x8000FF00,
-            0x00000000,
-        };
-
-        float scale = 1.5f;
-        SkPaint paint;
-        canvas->translate(N / 8, N / 8);
-
-        for (size_t y = 0; y < SK_ARRAY_COUNT(gColors); y++) {
-            for (size_t x = 0; x < SK_ARRAY_COUNT(gModes); x++) {
-                paint.setColorFilter(SkColorFilters::Blend(gColors[y], gModes[x]));
-                canvas->drawBitmap(fBitmap, x * N * 1.25f, y * N * scale, &paint);
-            }
-        }
-
-    }
-};
-
-DEF_SAMPLE( return new ColorFilterView(); )
diff --git a/third_party/skia/samplecode/SampleComplexClip.cpp b/third_party/skia/samplecode/SampleComplexClip.cpp
index d903148..64e7af5 100644
--- a/third_party/skia/samplecode/SampleComplexClip.cpp
+++ b/third_party/skia/samplecode/SampleComplexClip.cpp
@@ -9,7 +9,6 @@
 #include "include/core/SkFont.h"
 #include "include/core/SkPath.h"
 #include "samplecode/Sample.h"
-#include "src/core/SkClipOpPriv.h"
 
 class ComplexClipView : public Sample {
     void onOnceBeforeDraw() override {
@@ -78,11 +77,8 @@
             SkClipOp    fOp;
             const char* fName;
         } gOps[] = { //extra spaces in names for measureText
-            {kIntersect_SkClipOp,         "Isect "},
-            {kDifference_SkClipOp,        "Diff " },
-            {kUnion_SkClipOp,             "Union "},
-            {kXOR_SkClipOp,               "Xor "  },
-            {kReverseDifference_SkClipOp, "RDiff "}
+            {SkClipOp::kIntersect,         "Isect "},
+            {SkClipOp::kDifference,        "Diff " },
         };
 
         canvas->translate(0, SkIntToScalar(40));
diff --git a/third_party/skia/samplecode/SampleCowboy.cpp b/third_party/skia/samplecode/SampleCowboy.cpp
index 6ba3177..2ed47cb 100644
--- a/third_party/skia/samplecode/SampleCowboy.cpp
+++ b/third_party/skia/samplecode/SampleCowboy.cpp
@@ -7,12 +7,13 @@
 
 #include "include/core/SkTypes.h"
 
-#ifdef SK_XML
+#if defined(SK_ENABLE_SVG)
 
-#include "experimental/svg/model/SkSVGDOM.h"
 #include "include/core/SkCanvas.h"
 #include "include/core/SkRect.h"
 #include "include/core/SkStream.h"
+#include "modules/svg/include/SkSVGDOM.h"
+#include "modules/svg/include/SkSVGNode.h"
 #include "samplecode/Sample.h"
 #include "src/core/SkOSFile.h"
 #include "src/utils/SkOSPath.h"
@@ -21,7 +22,7 @@
 
 namespace {
 class AnimatedSVGSample : public Sample {
-    static constexpr auto kAnimationIterations = 5;
+    inline static constexpr auto kAnimationIterations = 5;
     enum State {
         kZoomIn,
         kScroll,
@@ -47,13 +48,7 @@
         }
         SkMemoryStream svgStream(std::move(data));
 
-        SkDOM xmlDom;
-        if (!xmlDom.build(svgStream)) {
-            SkDebugf("XML parsing failed: \"%s\"\n", fResource);
-            return;
-        }
-
-        fDom = SkSVGDOM::MakeFromDOM(xmlDom);
+        fDom = SkSVGDOM::MakeFromStream(svgStream);
         if (fDom) {
             fDom->setContainerSize(SkSize::Make(this->width(), this->height()));
         }
@@ -61,12 +56,12 @@
 
     void onDrawContent(SkCanvas* canvas) override {
         if (fDom) {
-            canvas->setMatrix(SkMatrix::MakeScale(3));
+            canvas->setMatrix(SkMatrix::Scale(3, 3));
             canvas->clipRect(SkRect::MakeLTRB(0, 0, 400, 400));
             switch (fState) {
                 case kZoomIn:
                     fDelta += 0.2f;
-                    canvas->concat(SkMatrix::MakeScale(fDelta));
+                    canvas->scale(fDelta, fDelta);
                     break;
                 case kScroll:
                     if (fAnimationLoop > kAnimationIterations/2) {
@@ -74,12 +69,12 @@
                     } else {
                         fDelta -= 80.f;
                     }
-                    canvas->concat(SkMatrix::MakeScale(fDelta));
+                    canvas->scale(fDelta, fDelta);
                     canvas->translate(fDelta, 0);
                     break;
                 case kZoomOut:
                     fDelta += 0.2f;
-                    canvas->concat(SkMatrix::MakeScale(fDelta));
+                    canvas->scale(fDelta, fDelta);
                     break;
             }
 
@@ -125,4 +120,4 @@
 
 DEF_SAMPLE( return new AnimatedSVGSample("Cowboy.svg", "SampleCowboy"); )
 
-#endif  // SK_XML
+#endif  // defined(SK_ENABLE_SVG)
diff --git a/third_party/skia/samplecode/SampleCusp.cpp b/third_party/skia/samplecode/SampleCusp.cpp
index 28b1233..543a3a3 100644
--- a/third_party/skia/samplecode/SampleCusp.cpp
+++ b/third_party/skia/samplecode/SampleCusp.cpp
@@ -144,12 +144,14 @@
         bool split;
         path = cusp(pts, pp, split, 8000, .125);
         auto debugOutCubic = [](const SkPoint* pts) {
-            return false; // comment out to capture stream of cusp'd cubics in stdout
-            SkDebugf("{{");
-            for (int i = 0; i < 4; ++i) {
-                SkDebugf("{0x%08x,0x%08x},", SkFloat2Bits(pts[i].fX), SkFloat2Bits(pts[i].fY));
+            if ((false)) { // enable to capture stream of cusp'd cubics in stdout
+                SkDebugf("{{");
+                for (int i = 0; i < 4; ++i) {
+                    SkDebugf("{0x%08x,0x%08x},", SkFloat2Bits(pts[i].fX), SkFloat2Bits(pts[i].fY));
+                }
+                SkDebugf("}},\n");
             }
-            SkDebugf("}},\n");
+            return false;
         };
         if (split) {
             debugOutCubic(&pp[0]);
@@ -162,7 +164,6 @@
         // draw time to make it easier to guess when the bad cubic was drawn
         std::string timeStr = std::to_string((float) (curTime - start) / 1000.f);
         canvas->drawSimpleText(timeStr.c_str(), timeStr.size(), SkTextEncoding::kUTF8, 20, 20, SkFont(), SkPaint());
-        SkDebugf("");
     }
 
     bool onAnimate(double nanos) override {
@@ -175,7 +176,7 @@
 
 private:
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 DEF_SAMPLE( return new CuspView(); )
diff --git a/third_party/skia/samplecode/SampleDegenerateQuads.cpp b/third_party/skia/samplecode/SampleDegenerateQuads.cpp
index 3d48cec..e3ab138 100644
--- a/third_party/skia/samplecode/SampleDegenerateQuads.cpp
+++ b/third_party/skia/samplecode/SampleDegenerateQuads.cpp
@@ -8,12 +8,18 @@
 #include "samplecode/Sample.h"
 
 #include "src/gpu/geometry/GrQuad.h"
-#include "src/gpu/ops/GrQuadPerEdgeAA.h"
+#include "src/gpu/ops/QuadPerEdgeAA.h"
 
 #include "include/core/SkCanvas.h"
 #include "include/core/SkPaint.h"
 #include "include/effects/SkDashPathEffect.h"
 #include "include/pathops/SkPathOps.h"
+#include "include/private/SkTPin.h"
+
+using VertexSpec = skgpu::v1::QuadPerEdgeAA::VertexSpec;
+using ColorType = skgpu::v1::QuadPerEdgeAA::ColorType;
+using Subset = skgpu::v1::QuadPerEdgeAA::Subset;
+using IndexBufferOption = skgpu::v1::QuadPerEdgeAA::IndexBufferOption;
 
 // Draw a line through the two points, outset by a fixed length in screen space
 static void draw_extended_line(SkCanvas* canvas, const SkPaint paint,
@@ -216,11 +222,11 @@
             SkScalar coverage = bary[0] * c0 + bary[1] * c1 + bary[2] * c2;
             if (coverage < 0.5f) {
                 // Check distances to domain
-                SkScalar l = SkScalarPin(point.fX - geomDomain.fLeft, 0.f, 1.f);
-                SkScalar t = SkScalarPin(point.fY - geomDomain.fTop, 0.f, 1.f);
-                SkScalar r = SkScalarPin(geomDomain.fRight - point.fX, 0.f, 1.f);
-                SkScalar b = SkScalarPin(geomDomain.fBottom - point.fY, 0.f, 1.f);
-                coverage = SkMinScalar(coverage, l * t * r * b);
+                SkScalar l = SkTPin(point.fX - geomDomain.fLeft, 0.f, 1.f);
+                SkScalar t = SkTPin(point.fY - geomDomain.fTop, 0.f, 1.f);
+                SkScalar r = SkTPin(geomDomain.fRight - point.fX, 0.f, 1.f);
+                SkScalar b = SkTPin(geomDomain.fBottom - point.fY, 0.f, 1.f);
+                coverage = std::min(coverage, l * t * r * b);
             }
             return coverage;
         }
@@ -406,10 +412,10 @@
     void getTessellatedPoints(SkPoint inset[4], SkScalar insetCoverage[4], SkPoint outset[4],
                               SkScalar outsetCoverage[4], SkRect* domain) const {
         // Fixed vertex spec for extracting the picture frame geometry
-        static const GrQuadPerEdgeAA::VertexSpec kSpec =
-            {GrQuad::Type::kGeneral, GrQuadPerEdgeAA::ColorType::kNone,
-             GrQuad::Type::kAxisAligned, false, GrQuadPerEdgeAA::Domain::kNo,
-             GrAAType::kCoverage, false, GrQuadPerEdgeAA::IndexBufferOption::kPictureFramed};
+        static const VertexSpec kSpec =
+            {GrQuad::Type::kGeneral, ColorType::kNone,
+             GrQuad::Type::kAxisAligned, false, Subset::kNo,
+             GrAAType::kCoverage, false, IndexBufferOption::kPictureFramed};
         static const GrQuad kIgnored(SkRect::MakeEmpty());
 
         GrQuadAAFlags flags = GrQuadAAFlags::kNone;
@@ -421,7 +427,7 @@
         GrQuad quad = GrQuad::MakeFromSkQuad(fCorners, SkMatrix::I());
 
         float vertices[56]; // 2 quads, with x, y, coverage, and geometry domain (7 floats x 8 vert)
-        GrQuadPerEdgeAA::Tessellator tessellator(kSpec, (char*) vertices);
+        skgpu::v1::QuadPerEdgeAA::Tessellator tessellator(kSpec, (char*) vertices);
         tessellator.append(&quad, nullptr, {1.f, 1.f, 1.f, 1.f},
                            SkRect::MakeEmpty(), flags);
 
@@ -448,7 +454,7 @@
         *domain = {vertices[52], vertices[53], vertices[54], vertices[55]};
     }
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 class DegenerateQuadSample::Click : public Sample::Click {
@@ -474,8 +480,8 @@
     void drag(SkPoint* point) {
         SkPoint delta = fCurr - fPrev;
         *point += SkPoint::Make(delta.x() / kViewScale, delta.y() / kViewScale);
-        point->fX = SkMinScalar(fOuterRect.fRight, SkMaxScalar(point->fX, fOuterRect.fLeft));
-        point->fY = SkMinScalar(fOuterRect.fBottom, SkMaxScalar(point->fY, fOuterRect.fTop));
+        point->fX = std::min(fOuterRect.fRight, std::max(point->fX, fOuterRect.fLeft));
+        point->fY = std::min(fOuterRect.fBottom, std::max(point->fY, fOuterRect.fTop));
     }
 };
 
diff --git a/third_party/skia/samplecode/SampleDegenerateTwoPtRadials.cpp b/third_party/skia/samplecode/SampleDegenerateTwoPtRadials.cpp
index f26403e..f0c74be 100644
--- a/third_party/skia/samplecode/SampleDegenerateTwoPtRadials.cpp
+++ b/third_party/skia/samplecode/SampleDegenerateTwoPtRadials.cpp
@@ -73,7 +73,7 @@
 
 private:
     SkScalar           fTime;
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleEffects.cpp b/third_party/skia/samplecode/SampleEffects.cpp
index f22aea1..5a49ded 100644
--- a/third_party/skia/samplecode/SampleEffects.cpp
+++ b/third_party/skia/samplecode/SampleEffects.cpp
@@ -86,9 +86,9 @@
     }
 
 protected:
-    virtual SkString name() { return SkString("Effects"); }
+    SkString name() override { return SkString("Effects"); }
 
-    virtual void onDrawContent(SkCanvas* canvas) {
+    void onDrawContent(SkCanvas* canvas) override {
         canvas->scale(3, 3);
         canvas->translate(10, 30);
         for (size_t i = 0; i < SK_ARRAY_COUNT(fPaint); i++) {
@@ -98,7 +98,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleEmboss.cpp b/third_party/skia/samplecode/SampleEmboss.cpp
index beacecb..0c49a85 100644
--- a/third_party/skia/samplecode/SampleEmboss.cpp
+++ b/third_party/skia/samplecode/SampleEmboss.cpp
@@ -33,9 +33,9 @@
     }
 
 protected:
-    virtual SkString name() { return SkString("Emboss"); }
+    SkString name() override { return SkString("Emboss"); }
 
-    virtual void onDrawContent(SkCanvas* canvas) {
+    void onDrawContent(SkCanvas* canvas) override {
         SkPaint paint;
 
         paint.setAntiAlias(true);
@@ -51,7 +51,7 @@
 
 private:
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleFatBits.cpp b/third_party/skia/samplecode/SampleFatBits.cpp
index f1dcd29..f80fb25 100644
--- a/third_party/skia/samplecode/SampleFatBits.cpp
+++ b/third_party/skia/samplecode/SampleFatBits.cpp
@@ -21,7 +21,6 @@
 #include "include/core/SkSurface.h"
 #include "include/core/SkTypes.h"
 #include "samplecode/Sample.h"
-#include "src/core/SkClipOpPriv.h"
 #include "src/core/SkPointPriv.h"
 #include "tools/ToolUtils.h"
 
@@ -172,7 +171,7 @@
         SkCanvas* canvas = fMaxSurface->getCanvas();
         canvas->save();
         canvas->concat(fMatrix);
-        fMinSurface->draw(canvas, 0, 0, nullptr);
+        fMinSurface->draw(canvas, 0, 0);
         canvas->restore();
 
         SkPaint paint;
@@ -271,7 +270,7 @@
         fMinSurface->getCanvas()->save();
         SkRect r = fClipRect;
         r.inset(SK_Scalar1/3, SK_Scalar1/3);
-        fMinSurface->getCanvas()->clipRect(r, kIntersect_SkClipOp, true);
+        fMinSurface->getCanvas()->clipRect(r, SkClipOp::kIntersect, true);
     }
     fMinSurface->getCanvas()->drawLine(pts[0], pts[1], paint);
     if (fUseClip) {
@@ -284,7 +283,7 @@
     fMatrix.mapPoints(pts, 2);
     this->drawLineSkeleton(max, pts);
 
-    fMaxSurface->draw(canvas, 0, 0, nullptr);
+    fMaxSurface->draw(canvas, 0, 0);
 }
 
 void FatBits::drawRect(SkCanvas* canvas, SkPoint pts[2]) {
@@ -314,7 +313,7 @@
     r.setBounds(pts, 2);
     this->drawRectSkeleton(max, r);
 
-    fMaxSurface->draw(canvas, 0, 0, nullptr);
+    fMaxSurface->draw(canvas, 0, 0);
 }
 
 void FatBits::drawTriangleSkeleton(SkCanvas* max, const SkPoint pts[]) {
@@ -356,7 +355,7 @@
     fMatrix.mapPoints(pts, 3);
     this->drawTriangleSkeleton(max, pts);
 
-    fMaxSurface->draw(canvas, 0, 0, nullptr);
+    fMaxSurface->draw(canvas, 0, 0);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -382,7 +381,7 @@
         fPts[0].set(1, 1);
         fPts[1].set(5, 4);
         fPts[2].set(2, 6);
-        SkMatrix::MakeScale(SkIntToScalar(fZoom)).mapPoints(fPts, 3);
+        SkMatrix::Scale(fZoom, fZoom).mapPoints(fPts, 3);
         fIsRect = false;
     }
 
@@ -499,7 +498,7 @@
 
 private:
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleFillType.cpp b/third_party/skia/samplecode/SampleFillType.cpp
index 230f41b..7330549 100644
--- a/third_party/skia/samplecode/SampleFillType.cpp
+++ b/third_party/skia/samplecode/SampleFillType.cpp
@@ -25,7 +25,7 @@
     }
 
 protected:
-    virtual SkString name() { return SkString("FillType"); }
+    SkString name() override{ return SkString("FillType"); }
 
     void showPath(SkCanvas* canvas, int x, int y, SkPathFillType ft,
                   SkScalar scale, const SkPaint& paint) {
@@ -55,7 +55,7 @@
                  scale, paint);
     }
 
-    virtual void onDrawContent(SkCanvas* canvas) {
+    void onDrawContent(SkCanvas* canvas) override {
         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
 
         SkPaint paint;
@@ -77,7 +77,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleFilter2.cpp b/third_party/skia/samplecode/SampleFilter2.cpp
deleted file mode 100644
index 453bfa7..0000000
--- a/third_party/skia/samplecode/SampleFilter2.cpp
+++ /dev/null
@@ -1,85 +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 "include/core/SkCanvas.h"
-#include "include/core/SkFont.h"
-#include "include/core/SkString.h"
-#include "include/utils/SkTextUtils.h"
-#include "samplecode/DecodeFile.h"
-#include "samplecode/Sample.h"
-#include "tools/Resources.h"
-
-#include <vector>
-
-static const char* gNames[] = {
-    "images/mandrill_512_q075.jpg",
-    "images/dog.jpg",
-};
-
-struct Filter2View : public Sample {
-    std::vector<SkBitmap> fBitmaps;
-
-    void onOnceBeforeDraw() override {
-        SkASSERT(fBitmaps.empty());
-        fBitmaps.reserve(SK_ARRAY_COUNT(gNames) * 2);
-        for (const char* name : gNames) {
-            SkBitmap bitmap;
-            (void)decode_file(GetResourceAsData(name), &bitmap);
-            fBitmaps.push_back(std::move(bitmap));
-        }
-        for (const char* name : gNames) {
-            SkBitmap bitmap;
-            (void)decode_file(GetResourceAsData(name), &bitmap, kRGB_565_SkColorType);
-            fBitmaps.push_back(std::move(bitmap));
-        }
-        this->setBGColor(SK_ColorGRAY);
-    }
-
-    SkString name() override { return SkString("Filter/Dither"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(50));
-
-        const SkScalar W = SkIntToScalar(fBitmaps[0].width() + 1);
-        const SkScalar H = SkIntToScalar(fBitmaps[0].height() + 1);
-        SkPaint paint;
-
-        const SkScalar scale = 0.897917f;
-        canvas->scale(SK_Scalar1, scale);
-
-        for (int k = 0; k < 2; k++) {
-            paint.setFilterQuality(k == 1 ? kLow_SkFilterQuality : kNone_SkFilterQuality);
-            for (int j = 0; j < 2; j++) {
-                paint.setDither(j == 1);
-                for (int i = 0; i < (int)fBitmaps.size(); i++) {
-                    SkScalar x = (k * (int)fBitmaps.size() + j) * W;
-                    SkScalar y = i * H;
-                    x = SkScalarRoundToScalar(x);
-                    y = SkScalarRoundToScalar(y);
-                    canvas->drawBitmap(fBitmaps[i], x, y, &paint);
-                    SkFont font;
-                    font.setSize(SkIntToScalar(18));
-                    if (i == 0) {
-                        SkString s("dither=");
-                        s.appendS32(paint.isDither());
-                        s.append(" filter=");
-                        s.appendS32(paint.getFilterQuality() != kNone_SkFilterQuality);
-                        SkTextUtils::DrawString(canvas, s.c_str(), x + W/2, y - font.getSize(), font, SkPaint(),
-                                                SkTextUtils::kCenter_Align);
-                    }
-                    if (k+j == 2) {
-                        SkString s;
-                        s.append(" depth=");
-                        s.appendS32(fBitmaps[i].colorType() == kRGB_565_SkColorType ? 16 : 32);
-                        SkTextUtils::DrawString(canvas, s.c_str(), x + W + SkIntToScalar(4), y + H/2, font, SkPaint());
-                    }
-                }
-            }
-        }
-    }
-};
-DEF_SAMPLE( return new Filter2View(); )
diff --git a/third_party/skia/samplecode/SampleFilterBounds.cpp b/third_party/skia/samplecode/SampleFilterBounds.cpp
new file mode 100644
index 0000000..682891c
--- /dev/null
+++ b/third_party/skia/samplecode/SampleFilterBounds.cpp
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "samplecode/Sample.h"
+
+#include "include/core/SkBitmap.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkColor.h"
+#include "include/core/SkFont.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkPath.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkRect.h"
+#include "include/effects/SkDashPathEffect.h"
+#include "include/effects/SkGradientShader.h"
+#include "include/effects/SkImageFilters.h"
+
+#include "src/core/SkImageFilterTypes.h"
+#include "src/core/SkImageFilter_Base.h"
+#include "src/core/SkMatrixPriv.h"
+
+#include "tools/ToolUtils.h"
+
+static constexpr float kLineHeight = 16.f;
+static constexpr float kLineInset = 8.f;
+
+static float print_size(SkCanvas* canvas, const char* prefix, const SkIRect& rect,
+                        float x, float y, const SkFont& font, const SkPaint& paint) {
+    canvas->drawString(prefix, x, y, font, paint);
+    y += kLineHeight;
+    SkString sz;
+    sz.appendf("%d x %d", rect.width(), rect.height());
+    canvas->drawString(sz, x, y, font, paint);
+    return y + kLineHeight;
+}
+
+static float print_info(SkCanvas* canvas,
+                        const SkIRect& layerContentBounds,
+                        const SkIRect& outputBounds,
+                        const SkIRect& hintedOutputBounds,
+                        const SkIRect& unhintedLayerBounds) {
+    SkFont font(nullptr, 12);
+    SkPaint text;
+    text.setAntiAlias(true);
+
+    float y = kLineHeight;
+
+    text.setColor(SK_ColorRED);
+    y = print_size(canvas, "Content (in layer)", layerContentBounds, kLineInset, y, font, text);
+    text.setColor(SK_ColorDKGRAY);
+    y = print_size(canvas, "Target (in device)", outputBounds, kLineInset, y, font, text);
+    text.setColor(SK_ColorBLUE);
+    y = print_size(canvas, "Output (w/ hint)", hintedOutputBounds, kLineInset, y, font, text);
+    text.setColor(SK_ColorGREEN);
+    y = print_size(canvas, "Input (w/ no hint)", unhintedLayerBounds, kLineInset, y, font, text);
+
+    return y;
+}
+
+static void print_label(SkCanvas* canvas, float x, float y, float value) {
+    SkFont font(nullptr, 12);
+    SkPaint text;
+    text.setAntiAlias(true);
+
+    SkString label;
+    label.printf("%.3f", value);
+
+    canvas->drawString(label, x, y + kLineHeight / 2.f, font, text);
+}
+
+static SkPaint line_paint(SkColor color, bool dashed = false) {
+    SkPaint paint;
+    paint.setColor(color);
+    paint.setStrokeWidth(0.f);
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setAntiAlias(true);
+    if (dashed) {
+        SkScalar dash[2] = {10.f, 10.f};
+        paint.setPathEffect(SkDashPathEffect::Make(dash, 2, 0.f));
+    }
+    return paint;
+}
+
+static SkPath create_axis_path(const SkRect& rect, float axisSpace) {
+    SkPath localSpace;
+    for (float y = rect.fTop + axisSpace; y <= rect.fBottom; y += axisSpace) {
+        localSpace.moveTo(rect.fLeft, y);
+        localSpace.lineTo(rect.fRight, y);
+    }
+    for (float x = rect.fLeft + axisSpace; x <= rect.fRight; x += axisSpace) {
+        localSpace.moveTo(x, rect.fTop);
+        localSpace.lineTo(x, rect.fBottom);
+    }
+    return localSpace;
+}
+
+static const SkColor4f kScaleGradientColors[] =
+                { { 0.05f, 0.0f, 6.f,  1.f },   // Severe downscaling, s < 1/8, log(s) < -3
+                  { 0.6f,  0.6f, 0.8f, 0.6f },  // Okay downscaling,   s < 1/2, log(s) < -1
+                  { 1.f,   1.f,  1.f,  0.2f },  // No scaling,         s = 1,   log(s) = 0
+                  { 0.95f, 0.6f, 0.5f, 0.6f },  // Okay upscaling,     s > 2,   log(s) > 1
+                  { 0.8f,  0.1f, 0.f,  1.f } }; // Severe upscaling,   s > 8,   log(s) > 3
+static const SkScalar kLogScaleFactors[] = { -3.f, -1.f, 0.f, 1.f, 3.f };
+static const SkScalar kGradientStops[] = { 0.f, 0.33333f, 0.5f, 0.66667f, 1.f };
+static const int kStopCount = (int) SK_ARRAY_COUNT(kScaleGradientColors);
+
+static void draw_scale_key(SkCanvas* canvas, float y) {
+    SkRect key = SkRect::MakeXYWH(15.f, y + 30.f, 15.f, 100.f);
+    SkPoint pts[] = {{key.centerX(), key.fTop}, {key.centerX(), key.fBottom}};
+    sk_sp<SkShader> gradient = SkGradientShader::MakeLinear(
+            pts, kScaleGradientColors, nullptr, kGradientStops, kStopCount, SkTileMode::kClamp,
+            SkGradientShader::kInterpolateColorsInPremul_Flag, nullptr);
+    SkPaint keyPaint;
+    keyPaint.setShader(gradient);
+    canvas->drawRect(key, keyPaint);
+    for (int i = 0; i < kStopCount; ++i) {
+        print_label(canvas, key.fRight + 5.f, key.fTop + kGradientStops[i] * key.height(),
+                    SkScalarPow(2.f, kLogScaleFactors[i]));
+    }
+}
+
+static void draw_scale_factors(SkCanvas* canvas, const skif::Mapping& mapping, const SkRect& rect) {
+    SkPoint testPoints[5];
+    testPoints[0] = {rect.centerX(), rect.centerY()};
+    rect.toQuad(testPoints + 1);
+    for (int i = 0; i < 5; ++i) {
+        float scale = SkMatrixPriv::DifferentialAreaScale(
+                mapping.deviceMatrix(),
+                SkPoint(mapping.paramToLayer(skif::ParameterSpace<SkPoint>(testPoints[i]))));
+        SkColor4f color = {0.f, 0.f, 0.f, 1.f};
+
+        if (SkScalarIsFinite(scale)) {
+            float logScale = SkScalarLog2(scale);
+            for (int j = 0; j <= kStopCount; ++j) {
+                if (j == kStopCount) {
+                    color = kScaleGradientColors[j - 1];
+                    break;
+                } else if (kLogScaleFactors[j] >= logScale) {
+                    if (j == 0) {
+                        color = kScaleGradientColors[0];
+                    } else {
+                        SkScalar t = (logScale - kLogScaleFactors[j - 1]) /
+                                    (kLogScaleFactors[j] - kLogScaleFactors[j - 1]);
+
+                        SkColor4f a = kScaleGradientColors[j - 1] * (1.f - t);
+                        SkColor4f b = kScaleGradientColors[j] * t;
+                        color = {a.fR + b.fR, a.fG + b.fG, a.fB + b.fB, a.fA + b.fA};
+                    }
+                    break;
+                }
+            }
+        }
+
+        SkPaint p;
+        p.setAntiAlias(true);
+        p.setColor4f(color, nullptr);
+        canvas->drawRect(SkRect::MakeLTRB(testPoints[i].fX - 4.f, testPoints[i].fY - 4.f,
+                                          testPoints[i].fX + 4.f, testPoints[i].fY + 4.f), p);
+    }
+}
+
+class FilterBoundsSample : public Sample {
+public:
+    FilterBoundsSample() {}
+
+    void onOnceBeforeDraw() override {
+        fBlur = SkImageFilters::Blur(8.f, 8.f, nullptr);
+        fImage = ToolUtils::create_checkerboard_image(
+                300, 300, SK_ColorMAGENTA, SK_ColorLTGRAY, 50);
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        // The local content, e.g. what would be submitted to drawRect or the bounds to saveLayer
+        const SkRect localContentRect = SkRect::MakeLTRB(100.f, 20.f, 180.f, 140.f);
+        SkMatrix ctm = canvas->getLocalToDeviceAs3x3();
+
+        // Base rendering of a filter
+        SkPaint blurPaint;
+        blurPaint.setImageFilter(fBlur);
+        canvas->saveLayer(&localContentRect, &blurPaint);
+        canvas->drawImageRect(fImage.get(), localContentRect, localContentRect,
+                              SkSamplingOptions(SkFilterMode::kLinear),
+                              nullptr, SkCanvas::kFast_SrcRectConstraint);
+        canvas->restore();
+
+        // Now visualize the underlying bounds calculations used to determine the layer for the blur
+        SkIRect target = ctm.mapRect(localContentRect).roundOut();
+        if (!target.intersect(SkIRect::MakeWH(canvas->imageInfo().width(),
+                                              canvas->imageInfo().height()))) {
+            return;
+        }
+        skif::DeviceSpace<SkIRect> targetOutput(target);
+        skif::ParameterSpace<SkRect> contentBounds(localContentRect);
+        skif::ParameterSpace<SkPoint> contentCenter({localContentRect.centerX(),
+                                                     localContentRect.centerY()});
+        skif::Mapping mapping;
+        SkAssertResult(mapping.decomposeCTM(ctm, fBlur.get(), contentCenter));
+
+        // Add axis lines, to show perspective distortion
+        canvas->save();
+        canvas->setMatrix(mapping.deviceMatrix());
+        canvas->drawPath(create_axis_path(SkRect(mapping.paramToLayer(contentBounds)), 20.f),
+                         line_paint(SK_ColorGRAY));
+        canvas->restore();
+
+        // Visualize scale factors at the four corners and center of the local rect
+        draw_scale_factors(canvas, mapping, localContentRect);
+
+        // The device content rect, e.g. the clip bounds if 'localContentRect' were used as a clip
+        // before the draw or saveLayer, representing what the filter must cover if it affects
+        // transparent black or doesn't have a local content hint.
+        canvas->setMatrix(SkMatrix::I());
+        canvas->drawRect(ctm.mapRect(localContentRect), line_paint(SK_ColorDKGRAY));
+
+        // Layer bounds for the filter, in the layer space compatible with the filter's matrix
+        // type requirements.
+        skif::LayerSpace<SkIRect> targetOutputInLayer = mapping.deviceToLayer(targetOutput);
+        skif::LayerSpace<SkIRect> hintedLayerBounds = as_IFB(fBlur)->getInputBounds(
+                mapping, targetOutput, &contentBounds);
+        skif::LayerSpace<SkIRect> unhintedLayerBounds = as_IFB(fBlur)->getInputBounds(
+                mapping, targetOutput, nullptr);
+
+        canvas->setMatrix(mapping.deviceMatrix());
+        canvas->drawRect(SkRect::Make(SkIRect(targetOutputInLayer)),
+                         line_paint(SK_ColorDKGRAY, true));
+        canvas->drawRect(SkRect::Make(SkIRect(hintedLayerBounds)), line_paint(SK_ColorRED));
+        canvas->drawRect(SkRect::Make(SkIRect(unhintedLayerBounds)), line_paint(SK_ColorGREEN));
+
+        // For visualization purposes, we want to show the layer-space output, this is what we get
+        // when contentBounds is provided as a hint in local/parameter space.
+        skif::Mapping layerOnly{mapping.layerMatrix()};
+        skif::DeviceSpace<SkIRect> hintedOutputBounds = as_IFB(fBlur)->getOutputBounds(
+                layerOnly, contentBounds);
+        canvas->drawRect(SkRect::Make(SkIRect(hintedOutputBounds)), line_paint(SK_ColorBLUE));
+
+        canvas->resetMatrix();
+        float y = print_info(canvas, SkIRect(mapping.paramToLayer(contentBounds).roundOut()),
+                             SkIRect(targetOutput),
+                             SkIRect(hintedOutputBounds),
+                             SkIRect(unhintedLayerBounds));
+
+        // Draw color key for layer visualization
+        draw_scale_key(canvas, y);
+    }
+
+    SkString name() override { return SkString("FilterBounds"); }
+
+private:
+    sk_sp<SkImageFilter> fBlur;
+    sk_sp<SkImage>       fImage;
+
+    using INHERITED = Sample;
+};
+
+DEF_SAMPLE(return new FilterBoundsSample();)
diff --git a/third_party/skia/samplecode/SampleFilterQuality.cpp b/third_party/skia/samplecode/SampleFilterQuality.cpp
deleted file mode 100644
index f18a1a6..0000000
--- a/third_party/skia/samplecode/SampleFilterQuality.cpp
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * 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 "include/core/SkCanvas.h"
-#include "include/core/SkData.h"
-#include "include/core/SkFont.h"
-#include "include/core/SkPath.h"
-#include "include/core/SkSurface.h"
-#include "include/core/SkTime.h"
-#include "include/effects/SkGradientShader.h"
-#include "include/utils/SkInterpolator.h"
-#include "include/utils/SkRandom.h"
-#include "samplecode/Sample.h"
-#include "tools/Resources.h"
-#include "tools/timer/TimeUtils.h"
-
-static sk_sp<SkSurface> make_surface(SkCanvas* canvas, const SkImageInfo& info) {
-    auto surface = canvas->makeSurface(info);
-    if (!surface) {
-        surface = SkSurface::MakeRaster(info);
-    }
-    return surface;
-}
-
-static sk_sp<SkShader> make_shader(const SkRect& bounds) {
-    sk_sp<SkImage> image(GetResourceAsImage("images/mandrill_128.png"));
-    return image ? image->makeShader() : nullptr;
-}
-
-#define N   128
-#define ANGLE_DELTA 3
-#define SCALE_DELTA (SK_Scalar1 / 32)
-
-static sk_sp<SkImage> make_image() {
-    SkImageInfo info = SkImageInfo::MakeN32(N, N, kOpaque_SkAlphaType);
-    auto surface(SkSurface::MakeRaster(info));
-    SkCanvas* canvas = surface->getCanvas();
-    canvas->drawColor(SK_ColorWHITE);
-
-    SkPath path;
-    path.setFillType(SkPathFillType::kEvenOdd);
-
-    path.addRect(SkRect::MakeWH(N/2, N));
-    path.addRect(SkRect::MakeWH(N, N/2));
-    path.moveTo(0, 0); path.lineTo(N, 0); path.lineTo(0, N); path.close();
-
-    SkPaint paint;
-    paint.setShader(make_shader(SkRect::MakeWH(N, N)));
-
-    canvas->drawPath(path, paint);
-    return surface->makeImageSnapshot();
-}
-
-static sk_sp<SkImage> zoom_up(SkSurface* origSurf, SkImage* orig) {
-    const SkScalar S = 16;    // amount to scale up
-    const int D = 2;    // dimension scaling for the offscreen
-    // since we only view the center, don't need to produce the entire thing
-
-    SkImageInfo info = SkImageInfo::MakeN32(orig->width() * D, orig->height() * D,
-                                            kOpaque_SkAlphaType);
-    auto surface(origSurf->makeSurface(info));
-    SkCanvas* canvas = surface->getCanvas();
-    canvas->drawColor(SK_ColorWHITE);
-    canvas->scale(S, S);
-    canvas->translate(-SkScalarHalf(orig->width()) * (S - D) / S,
-                      -SkScalarHalf(orig->height()) * (S - D) / S);
-    canvas->drawImage(orig, 0, 0, nullptr);
-
-    if (S > 3) {
-        SkPaint paint;
-        paint.setColor(SK_ColorWHITE);
-        for (int i = 1; i < orig->height(); ++i) {
-            SkScalar y = SkIntToScalar(i);
-            canvas->drawLine(0, y, SkIntToScalar(orig->width()), y, paint);
-        }
-        for (int i = 1; i < orig->width(); ++i) {
-            SkScalar x = SkIntToScalar(i);
-            canvas->drawLine(x, 0, x, SkIntToScalar(orig->height()), paint);
-        }
-    }
-    return surface->makeImageSnapshot();
-}
-
-struct AnimValue {
-    SkScalar fValue;
-    SkScalar fMin;
-    SkScalar fMax;
-    SkScalar fMod;
-
-    operator SkScalar() const { return fValue; }
-
-    void set(SkScalar value, SkScalar min, SkScalar max) {
-        fValue = value;
-        fMin = min;
-        fMax = max;
-        fMod = 0;
-    }
-
-    void setMod(SkScalar value, SkScalar mod) {
-        fValue = value;
-        fMin = 0;
-        fMax = 0;
-        fMod = mod;
-    }
-
-    SkScalar inc(SkScalar delta) {
-        fValue += delta;
-        return this->fixUp();
-    }
-
-    SkScalar fixUp() {
-        if (fMod) {
-            fValue = SkScalarMod(fValue, fMod);
-        } else {
-            if (fValue > fMax) {
-                fValue = fMax;
-            } else if (fValue < fMin) {
-                fValue = fMin;
-            }
-        }
-        return fValue;
-    }
-};
-
-static void draw_box_frame(SkCanvas* canvas, int width, int height) {
-    SkPaint p;
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setColor(SK_ColorRED);
-    SkRect r = SkRect::MakeIWH(width, height);
-    r.inset(0.5f, 0.5f);
-    canvas->drawRect(r, p);
-    canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), p);
-    canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), p);
-}
-
-class FilterQualityView : public Sample {
-    sk_sp<SkImage>  fImage;
-    AnimValue       fScale, fAngle;
-    SkSize          fCell;
-    SkInterpolator  fTrans;
-    SkMSec          fCurrTime;
-    bool            fShowFatBits;
-
-public:
-    FilterQualityView() : fTrans(2, 2), fShowFatBits(true) {
-        fCell.set(256, 256);
-
-        fScale.set(1, SK_Scalar1 / 8, 1);
-        fAngle.setMod(0, 360);
-
-        SkScalar values[2];
-        fTrans.setMirror(true);
-        fTrans.setReset(true);
-
-        fCurrTime = 0;
-
-        fTrans.setRepeatCount(999);
-        values[0] = values[1] = 0;
-        fTrans.setKeyFrame(0, fCurrTime, values);
-        values[0] = values[1] = 1;
-        fTrans.setKeyFrame(1, fCurrTime + 2000, values);
-    }
-
-protected:
-    SkString name() override { return SkString("FilterQuality"); }
-
-    bool onChar(SkUnichar uni) override {
-            switch (uni) {
-                case '1': fAngle.inc(-ANGLE_DELTA); return true;
-                case '2': fAngle.inc( ANGLE_DELTA); return true;
-                case '3': fScale.inc(-SCALE_DELTA); return true;
-                case '4': fScale.inc( SCALE_DELTA); return true;
-                case '5': fShowFatBits = !fShowFatBits; return true;
-                default: break;
-            }
-            return false;
-    }
-
-    void drawTheImage(SkCanvas* canvas, const SkISize& size, SkFilterQuality filter,
-                      SkScalar dx, SkScalar dy) {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setFilterQuality(filter);
-
-        SkAutoCanvasRestore acr(canvas, true);
-
-        canvas->translate(dx, dy);
-
-        canvas->translate(SkScalarHalf(size.width()), SkScalarHalf(size.height()));
-        canvas->scale(fScale, fScale);
-        canvas->rotate(fAngle);
-        canvas->drawImage(fImage.get(), -SkScalarHalf(fImage->width()), -SkScalarHalf(fImage->height()),
-                          &paint);
-
-        if (false) {
-            acr.restore();
-            draw_box_frame(canvas, size.width(), size.height());
-        }
-    }
-
-    void drawHere(SkCanvas* canvas, SkFilterQuality filter, SkScalar dx, SkScalar dy) {
-        SkCanvas* origCanvas = canvas;
-        SkAutoCanvasRestore acr(canvas, true);
-
-        SkISize size = SkISize::Make(fImage->width(), fImage->height());
-
-        sk_sp<SkSurface> surface;
-        if (fShowFatBits) {
-            // scale up so we don't clip rotations
-            SkImageInfo info = SkImageInfo::MakeN32(fImage->width() * 2, fImage->height() * 2,
-                                                    kOpaque_SkAlphaType);
-            surface = make_surface(canvas, info);
-            canvas = surface->getCanvas();
-            canvas->drawColor(SK_ColorWHITE);
-            size.set(info.width(), info.height());
-        } else {
-            canvas->translate(SkScalarHalf(fCell.width() - fImage->width()),
-                              SkScalarHalf(fCell.height() - fImage->height()));
-        }
-        this->drawTheImage(canvas, size, filter, dx, dy);
-
-        if (surface) {
-            sk_sp<SkImage> orig(surface->makeImageSnapshot());
-            sk_sp<SkImage> zoomed(zoom_up(surface.get(), orig.get()));
-            origCanvas->drawImage(zoomed.get(),
-                                  SkScalarHalf(fCell.width() - zoomed->width()),
-                                  SkScalarHalf(fCell.height() - zoomed->height()));
-        }
-    }
-
-    void drawBorders(SkCanvas* canvas) {
-        SkPaint p;
-        p.setStyle(SkPaint::kStroke_Style);
-        p.setColor(SK_ColorBLUE);
-
-        SkRect r = SkRect::MakeWH(fCell.width() * 2, fCell.height() * 2);
-        r.inset(SK_ScalarHalf, SK_ScalarHalf);
-        canvas->drawRect(r, p);
-        canvas->drawLine(r.left(), r.centerY(), r.right(), r.centerY(), p);
-        canvas->drawLine(r.centerX(), r.top(), r.centerX(), r.bottom(), p);
-    }
-
-    void onOnceBeforeDraw() override {
-        fImage = make_image();
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        fCell.set(this->height() / 2, this->height() / 2);
-
-        SkScalar trans[2];
-        fTrans.timeToValues(fCurrTime, trans);
-
-        for (int y = 0; y < 2; ++y) {
-            for (int x = 0; x < 2; ++x) {
-                int index = y * 2 + x;
-                SkAutoCanvasRestore acr(canvas, true);
-                canvas->translate(fCell.width() * x, fCell.height() * y);
-                SkRect r = SkRect::MakeWH(fCell.width(), fCell.height());
-                r.inset(4, 4);
-                canvas->clipRect(r);
-                this->drawHere(canvas, SkFilterQuality(index), trans[0], trans[1]);
-            }
-        }
-
-        this->drawBorders(canvas);
-
-        const SkScalar textX = fCell.width() * 2 + 30;
-
-        SkFont font(nullptr, 36);
-        SkPaint paint;
-        canvas->drawString(SkStringPrintf("%.8g", (float)fScale), textX, 100, font, paint);
-        canvas->drawString(SkStringPrintf("%.8g", (float)fAngle), textX, 150, font, paint);
-        canvas->drawString(SkStringPrintf("%.8g", trans[0]     ), textX, 200, font, paint);
-        canvas->drawString(SkStringPrintf("%.8g", trans[1]     ), textX, 250, font, paint);
-    }
-
-    bool onAnimate(double nanos) override {
-        fCurrTime = TimeUtils::NanosToMSec(nanos);
-        return true;
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE( return new FilterQualityView(); )
diff --git a/third_party/skia/samplecode/SampleFitCubicToCircle.cpp b/third_party/skia/samplecode/SampleFitCubicToCircle.cpp
new file mode 100644
index 0000000..84042e5
--- /dev/null
+++ b/third_party/skia/samplecode/SampleFitCubicToCircle.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "samplecode/Sample.h"
+
+#include "include/core/SkCanvas.h"
+#include "include/core/SkFont.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkPath.h"
+#include <tuple>
+
+// Math constants are not always defined.
+#ifndef M_PI
+#define M_PI 3.14159265358979323846264338327950288
+#endif
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880168872420969808
+#endif
+
+constexpr static int kCenterX = 300;
+constexpr static int kCenterY = 325;
+constexpr static int kRadius = 250;
+
+// This sample fits a cubic to the arc between two interactive points on a circle. It also finds the
+// T-coordinate of max error, and outputs it and its value in pixels. (It turns out that max error
+// always occurs at T=0.21132486540519.)
+//
+// Press 'E' to iteratively cut the arc in half and report the improvement in max error after each
+// halving. (It turns out that max error improves by exactly 64x on every halving.)
+class SampleFitCubicToCircle : public Sample {
+    SkString name() override { return SkString("FitCubicToCircle"); }
+    void onOnceBeforeDraw() override { this->fitCubic(); }
+    void fitCubic();
+    void onDrawContent(SkCanvas*) override;
+    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
+    bool onClick(Sample::Click*) override;
+    bool onChar(SkUnichar) override;
+
+    // Coordinates of two points on the unit circle. These are the two endpoints of the arc we fit.
+    double fEndptsX[2] = {0, 1};
+    double fEndptsY[2] = {-1, 0};
+
+    // Fitted cubic and info, set by fitCubic().
+    double fControlLength;  // Length of (p1 - p0) and/or (p3 - p2) in unit circle space.
+    double fMaxErrorT;  // T value where the cubic diverges most from the true arc.
+    std::array<double, 4> fCubicX;  // Screen space cubic control points.
+    std::array<double, 4> fCubicY;
+    double fMaxError;  // Max error (in pixels) between the cubic and the screen-space arc.
+    double fTheta;  // Angle of the arc. This is only used for informational purposes.
+    SkTArray<SkString> fInfoStrings;
+
+    class Click;
+};
+
+// Fits a cubic to an arc on the unit circle with endpoints (x0, y0) and (x1, y1). Using the
+// following 3 constraints, we arrive at the formula used in the method:
+//
+//   1) The endpoints and tangent directions at the endpoints must match the arc.
+//   2) The cubic must be symmetric (i.e., length(p1 - p0) == length(p3 - p2)).
+//   3) The height of the cubic must match the height of the arc.
+//
+// Returns the "control length", or length of (p1 - p0) and/or (p3 - p2).
+static float fit_cubic_to_unit_circle(double x0, double y0, double x1, double y1,
+                                      std::array<double, 4>* X, std::array<double, 4>* Y) {
+    constexpr static double kM = -4.0/3;
+    constexpr static double kA = 4*M_SQRT2/3;
+    double d = x0*x1 + y0*y1;
+    double c = (std::sqrt(1 + d) * kM + kA) / std::sqrt(1 - d);
+    *X = {x0, x0 - y0*c, x1 + y1*c, x1};
+    *Y = {y0, y0 + x0*c, y1 - x1*c, y1};
+    return c;
+}
+
+static double lerp(double x, double y, double T) {
+    return x + T*(y - x);
+}
+
+// Evaluates the cubic and 1st and 2nd derivatives at T.
+static std::tuple<double, double, double> eval_cubic(double x[], double T) {
+    // Use De Casteljau's algorithm for better accuracy and stability.
+    double ab = lerp(x[0], x[1], T);
+    double bc = lerp(x[1], x[2], T);
+    double cd = lerp(x[2], x[3], T);
+    double abc = lerp(ab, bc, T);
+    double bcd = lerp(bc, cd, T);
+    double abcd = lerp(abc, bcd, T);
+    return {abcd, 3 * (bcd - abc) /*1st derivative.*/, 6 * (cd - 2*bc + ab) /*2nd derivative.*/};
+}
+
+// Uses newton-raphson convergence to find the point where the provided cubic diverges most from the
+// unit circle. i.e., the point where the derivative of error == 0. For error we use:
+//
+//     error = x^2 + y^2 - 1
+//     error' = 2xx' + 2yy'
+//     error'' = 2xx'' + 2yy'' + 2x'^2 + 2y'^2
+//
+double find_max_error_T(double cubicX[4], double cubicY[4]) {
+    constexpr static double kInitialT = .25;
+    double T = kInitialT;
+    for (int i = 0; i < 64; ++i) {
+        auto [x, dx, ddx] = eval_cubic(cubicX, T);
+        auto [y, dy, ddy] = eval_cubic(cubicY, T);
+        double dError = 2*(x*dx + y*dy);
+        double ddError = 2*(x*ddx + y*ddy + dx*dx + dy*dy);
+        T -= dError / ddError;
+    }
+    return T;
+}
+
+void SampleFitCubicToCircle::fitCubic() {
+    fInfoStrings.reset();
+
+    std::array<double, 4> X, Y;
+    // "Control length" is the length of (p1 - p0) and/or (p3 - p2) in unit circle space.
+    fControlLength = fit_cubic_to_unit_circle(fEndptsX[0], fEndptsY[0], fEndptsX[1], fEndptsY[1],
+                                              &X, &Y);
+    fInfoStrings.push_back().printf("control length=%0.14f", fControlLength);
+
+    fMaxErrorT = find_max_error_T(X.data(), Y.data());
+    fInfoStrings.push_back().printf("max error T=%0.14f", fMaxErrorT);
+
+    for (int i = 0; i < 4; ++i) {
+        fCubicX[i] = X[i] * kRadius + kCenterX;
+        fCubicY[i] = Y[i] * kRadius + kCenterY;
+    }
+    double errX = std::get<0>(eval_cubic(fCubicX.data(), fMaxErrorT)) - kCenterX;
+    double errY = std::get<0>(eval_cubic(fCubicY.data(), fMaxErrorT)) - kCenterY;
+    fMaxError = std::sqrt(errX*errX + errY*errY) - kRadius;
+    fInfoStrings.push_back().printf("max error=%.5gpx", fMaxError);
+
+    fTheta = std::atan2(fEndptsY[1], fEndptsX[1]) - std::atan2(fEndptsY[0], fEndptsX[0]);
+    fTheta = std::abs(fTheta * 180/M_PI);
+    if (fTheta > 180) {
+        fTheta = 360 - fTheta;
+    }
+    fInfoStrings.push_back().printf("(theta=%.2f)", fTheta);
+
+    SkDebugf("\n");
+    for (const SkString& infoString : fInfoStrings) {
+        SkDebugf("%s\n", infoString.c_str());
+    }
+}
+
+void SampleFitCubicToCircle::onDrawContent(SkCanvas* canvas) {
+    canvas->clear(SK_ColorBLACK);
+
+    SkPaint circlePaint;
+    circlePaint.setColor(0x80ffffff);
+    circlePaint.setStyle(SkPaint::kStroke_Style);
+    circlePaint.setStrokeWidth(0);
+    circlePaint.setAntiAlias(true);
+    canvas->drawArc(SkRect::MakeXYWH(kCenterX - kRadius, kCenterY - kRadius, kRadius * 2,
+                                     kRadius * 2), 0, 360, false, circlePaint);
+
+    SkPaint cubicPaint;
+    cubicPaint.setColor(SK_ColorGREEN);
+    cubicPaint.setStyle(SkPaint::kStroke_Style);
+    cubicPaint.setStrokeWidth(10);
+    cubicPaint.setAntiAlias(true);
+    SkPath cubicPath;
+    cubicPath.moveTo(fCubicX[0], fCubicY[0]);
+    cubicPath.cubicTo(fCubicX[1], fCubicY[1], fCubicX[2], fCubicY[2], fCubicX[3], fCubicY[3]);
+    canvas->drawPath(cubicPath, cubicPaint);
+
+    SkPaint endpointsPaint;
+    endpointsPaint.setColor(SK_ColorBLUE);
+    endpointsPaint.setStrokeWidth(8);
+    endpointsPaint.setAntiAlias(true);
+    SkPoint points[2] = {{(float)fCubicX[0], (float)fCubicY[0]},
+                         {(float)fCubicX[3], (float)fCubicY[3]}};
+    canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, points, endpointsPaint);
+
+    SkPaint textPaint;
+    textPaint.setColor(SK_ColorWHITE);
+    constexpr static float kInfoTextSize = 16;
+    SkFont font(nullptr, kInfoTextSize);
+    int infoY = 10 + kInfoTextSize;
+    for (const SkString& infoString : fInfoStrings) {
+        canvas->drawString(infoString.c_str(), 10, infoY, font, textPaint);
+        infoY += kInfoTextSize * 3/2;
+    }
+}
+
+class SampleFitCubicToCircle::Click : public Sample::Click {
+public:
+    Click(int ptIdx) : fPtIdx(ptIdx) {}
+
+    void doClick(SampleFitCubicToCircle* that) {
+        double dx = fCurr.fX - kCenterX;
+        double dy = fCurr.fY - kCenterY;
+        double l = std::sqrt(dx*dx + dy*dy);
+        that->fEndptsX[fPtIdx] = dx/l;
+        that->fEndptsY[fPtIdx] = dy/l;
+        if (that->fEndptsX[0] * that->fEndptsY[1] - that->fEndptsY[0] * that->fEndptsX[1] < 0) {
+            std::swap(that->fEndptsX[0], that->fEndptsX[1]);
+            std::swap(that->fEndptsY[0], that->fEndptsY[1]);
+            fPtIdx = 1 - fPtIdx;
+        }
+        that->fitCubic();
+    }
+
+private:
+    int fPtIdx;
+};
+
+Sample::Click* SampleFitCubicToCircle::onFindClickHandler(SkScalar x, SkScalar y,
+                                                          skui::ModifierKey) {
+    double dx0 = x - fCubicX[0];
+    double dy0 = y - fCubicY[0];
+    double dx3 = x - fCubicX[3];
+    double dy3 = y - fCubicY[3];
+    if (dx0*dx0 + dy0*dy0 < dx3*dx3 + dy3*dy3) {
+        return new Click(0);
+    } else {
+        return new Click(1);
+    }
+}
+
+bool SampleFitCubicToCircle::onClick(Sample::Click* click) {
+    Click* myClick = (Click*)click;
+    myClick->doClick(this);
+    return true;
+}
+
+bool SampleFitCubicToCircle::onChar(SkUnichar unichar) {
+    if (unichar == 'E') {
+        constexpr static double kMaxErrorT = 0.21132486540519;  // Always the same.
+        // Split the arc in half until error =~0, and report the improvement after each halving.
+        double lastError = -1;
+        for (double theta = fTheta; lastError != 0; theta /= 2) {
+            double rads = theta * M_PI/180;
+            std::array<double, 4> X, Y;
+            fit_cubic_to_unit_circle(1, 0, std::cos(rads), std::sin(rads), &X, &Y);
+            auto [x, dx, ddx] = eval_cubic(X.data(), kMaxErrorT);
+            auto [y, dy, ddy] = eval_cubic(Y.data(), kMaxErrorT);
+            double error = std::sqrt(x*x + y*y) * kRadius - kRadius;
+            if ((float)error <= 0) {
+                error = 0;
+            }
+            SkDebugf("%6.2f degrees:   error= %10.5gpx", theta, error);
+            if (lastError > 0) {
+                SkDebugf(" (%17.14fx improvement)", lastError / error);
+            }
+            SkDebugf("\n");
+            lastError = error;
+        }
+        return true;
+    }
+    return false;
+}
+
+DEF_SAMPLE(return new SampleFitCubicToCircle;)
diff --git a/third_party/skia/samplecode/SampleFlutterAnimate.cpp b/third_party/skia/samplecode/SampleFlutterAnimate.cpp
index e3e53e0..48198bf 100644
--- a/third_party/skia/samplecode/SampleFlutterAnimate.cpp
+++ b/third_party/skia/samplecode/SampleFlutterAnimate.cpp
@@ -16,10 +16,6 @@
 #include "samplecode/Sample.h"
 #include "tools/timer/Timer.h"
 
-#if SK_SUPPORT_GPU
-#include "include/gpu/GrContext.h"
-#endif
-
 // Create an animation of a bunch of letters that rotate in place. This is intended to stress
 // the glyph atlas and test that we don't see corruption or bad slowdowns.
 class FlutterAnimateView : public Sample {
@@ -37,7 +33,6 @@
     void onDrawContent(SkCanvas* canvas) override {
         SkFont font(fTypeface, 50);
         SkPaint paint;
-        paint.setFilterQuality(kMedium_SkFilterQuality);
 
         // rough center of each glyph
         static constexpr auto kMidX = 35;
@@ -79,7 +74,7 @@
         }
     }
 
-    static constexpr double kDuration = 5.0;
+    inline static constexpr double kDuration = 5.0;
     double fCurrTime;
     double fResetTime;
     SkRandom fRand;
@@ -91,10 +86,10 @@
         SkScalar fEndRotation;
     };
     sk_sp<SkTypeface> fTypeface;
-    static constexpr int kNumChars = 40;
+    inline static constexpr int kNumChars = 40;
     AnimatedChar fChars[kNumChars];
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleFontCache.cpp b/third_party/skia/samplecode/SampleFontCache.cpp
deleted file mode 100644
index 9f39e9c..0000000
--- a/third_party/skia/samplecode/SampleFontCache.cpp
+++ /dev/null
@@ -1,133 +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 "include/core/SkCanvas.h"
-#include "include/core/SkGraphics.h"
-#include "include/utils/SkRandom.h"
-#include "samplecode/Sample.h"
-
-#include <pthread.h>
-
-static void call_measure() {
-    SkPaint paint;
-    uint16_t text[32];
-    SkRandom rand;
-
-    paint.setAntiAlias(true);
-    paint.setTextEncoding(SkTextEncoding::kUTF16);
-    for (int j = 0; j < SK_ARRAY_COUNT(text); j++)
-        text[j] = (uint16_t)((rand.nextU() & 0xFF) + 32);
-
-    for (int i = 9; i < 36; i++) {
-        SkFontMetrics m;
-
-        paint.setTextSize(SkIntToScalar(i));
-        paint.getFontMetrics(&m);
-        paint.measureText(text, sizeof(text));
-    }
-}
-
-static void call_draw(SkCanvas* canvas) {
-    SkPaint paint;
-    uint16_t text[32];
-    SkRandom rand;
-
-    paint.setAntiAlias(true);
-    paint.setTextEncoding(SkTextEncoding::kUTF16);
-    for (int j = 0; j < SK_ARRAY_COUNT(text); j++)
-        text[j] = (uint16_t)((rand.nextU() & 0xFF) + 32);
-
-    SkScalar x = SkIntToScalar(10);
-    SkScalar y = SkIntToScalar(20);
-
-    canvas->drawColor(SK_ColorWHITE);
-    for (int i = 9; i < 36; i++)
-    {
-        SkFontMetrics m;
-
-        paint.setTextSize(SkIntToScalar(i));
-        paint.getFontMetrics(&m);
-        canvas->drawText(text, sizeof(text), x, y, paint);
-        y += m.fDescent - m.fAscent;
-    }
-}
-
-static bool gDone;
-
-static void* measure_proc(void* context) {
-    while (!gDone) {
-        call_measure();
-    }
-    return nullptr;
-}
-
-static void* draw_proc(void* context) {
-    SkBitmap* bm = (SkBitmap*)context;
-    SkCanvas    canvas(*bm);
-
-    while (!gDone) {
-        call_draw(&canvas);
-    }
-    return nullptr;
-}
-
-class FontCacheView : public Sample {
-public:
-    enum { N = 4 };
-
-    pthread_t   fMThreads[N];
-    pthread_t   fDThreads[N];
-    SkBitmap    fBitmaps[N];
-
-    FontCacheView() {
-        gDone = false;
-        for (int i = 0; i < N; i++) {
-            int status;
-
-            status = pthread_create(&fMThreads[i], nullptr,  measure_proc, nullptr);
-            SkASSERT(0 == status);
-
-            fBitmaps[i].allocPixels(SkImageInfo::Make(320, 240,
-                                                      kRGB_565_SkColorType,
-                                                      kOpaque_SkAlphaType));
-            status = pthread_create(&fDThreads[i], nullptr,  draw_proc, &fBitmaps[i]);
-            SkASSERT(0 == status);
-        }
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-    virtual ~FontCacheView() {
-        gDone = true;
-        for (int i = 0; i < N; i++) {
-            void* ret;
-            int status = pthread_join(fMThreads[i], &ret);
-            SkASSERT(0 == status);
-            status = pthread_join(fDThreads[i], &ret);
-            SkASSERT(0 == status);
-        }
-    }
-
-protected:
-    SkString name() override { return SkString("FontCache"); }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkScalar x = 0;
-        SkScalar y = 0;
-        for (int i = 0; i < N; i++) {
-            canvas->drawBitmap(fBitmaps[i], x, y);
-            x += SkIntToScalar(fBitmaps[i].width());
-        }
-        this->inval(nullptr);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static Sample* MyFactory() { return new FontCacheView; }
-static SampleRegister reg(MyFactory);
diff --git a/third_party/skia/samplecode/SampleGlyphTransform.cpp b/third_party/skia/samplecode/SampleGlyphTransform.cpp
index 2260a1a..cd7191b 100644
--- a/third_party/skia/samplecode/SampleGlyphTransform.cpp
+++ b/third_party/skia/samplecode/SampleGlyphTransform.cpp
@@ -1,4 +1,4 @@
-/*
+/*
  * Copyright 2018 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
@@ -75,7 +75,7 @@
     SkScalar fScale;
     SkScalar fRotate;
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleGradients.cpp b/third_party/skia/samplecode/SampleGradients.cpp
index 9eb4e71..a101ec3 100644
--- a/third_party/skia/samplecode/SampleGradients.cpp
+++ b/third_party/skia/samplecode/SampleGradients.cpp
@@ -149,13 +149,13 @@
         canvas->restore();
 
         canvas->translate(0, SkIntToScalar(370));
-        if (false) { // avoid bit rot, suppress warning
+        if ((false)) { // avoid bit rot, suppress warning
             test_alphagradients(canvas);
         }
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleHT.cpp b/third_party/skia/samplecode/SampleHT.cpp
deleted file mode 100644
index bec4a7d..0000000
--- a/third_party/skia/samplecode/SampleHT.cpp
+++ /dev/null
@@ -1,176 +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 "include/core/SkCanvas.h"
-#include "include/core/SkDrawable.h"
-#include "include/core/SkPictureRecorder.h"
-#include "include/utils/SkInterpolator.h"
-#include "include/utils/SkRandom.h"
-#include "samplecode/Sample.h"
-#include "src/core/SkPointPriv.h"
-#include "tools/timer/TimeUtils.h"
-
-const SkRect gUnitSquare = { -1, -1, 1, 1 };
-
-static void color_to_floats(SkColor c, SkScalar f[4]) {
-    f[0] = SkIntToScalar(SkColorGetA(c));
-    f[1] = SkIntToScalar(SkColorGetR(c));
-    f[2] = SkIntToScalar(SkColorGetG(c));
-    f[3] = SkIntToScalar(SkColorGetB(c));
-}
-
-static SkColor floats_to_color(const SkScalar f[4]) {
-    return SkColorSetARGB(SkScalarRoundToInt(f[0]),
-                          SkScalarRoundToInt(f[1]),
-                          SkScalarRoundToInt(f[2]),
-                          SkScalarRoundToInt(f[3]));
-}
-
-static bool oval_contains(const SkRect& r, SkScalar x, SkScalar y) {
-    SkMatrix m;
-    m.setRectToRect(r, gUnitSquare, SkMatrix::kFill_ScaleToFit);
-    SkPoint pt;
-    m.mapXY(x, y, &pt);
-    return SkPointPriv::LengthSqd(pt) <= 1;
-}
-
-static SkColor rand_opaque_color(uint32_t seed) {
-    SkRandom rand(seed);
-    return rand.nextU() | (0xFF << 24);
-}
-
-class HTDrawable : public SkDrawable {
-    SkRect          fR;
-    SkColor         fColor;
-    SkInterpolator* fInterp;
-    SkMSec          fTime;
-
-public:
-    HTDrawable(SkRandom& rand) {
-        fR = SkRect::MakeXYWH(rand.nextRangeF(0, 640), rand.nextRangeF(0, 480),
-                              rand.nextRangeF(20, 200), rand.nextRangeF(20, 200));
-        fColor = rand_opaque_color(rand.nextU());
-        fInterp = nullptr;
-        fTime = 0;
-    }
-
-    void spawnAnimation(SkMSec now) {
-        this->setTime(now);
-
-        delete fInterp;
-        fInterp = new SkInterpolator(5, 3);
-        SkScalar values[5];
-        color_to_floats(fColor, values); values[4] = 0;
-        fInterp->setKeyFrame(0, now, values);
-        values[0] = 0; values[4] = 180;
-        fInterp->setKeyFrame(1, now + 1000, values);
-        color_to_floats(rand_opaque_color(fColor), values); values[4] = 360;
-        fInterp->setKeyFrame(2, now + 2000, values);
-
-        fInterp->setMirror(true);
-        fInterp->setRepeatCount(3);
-
-        this->notifyDrawingChanged();
-    }
-
-    bool hitTest(SkScalar x, SkScalar y) {
-        return oval_contains(fR, x, y);
-    }
-
-    void setTime(SkMSec time) { fTime = time; }
-
-    void onDraw(SkCanvas* canvas) override {
-        SkAutoCanvasRestore acr(canvas, false);
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-
-        if (fInterp) {
-            SkScalar values[5];
-            SkInterpolator::Result res = fInterp->timeToValues(fTime, values);
-            fColor = floats_to_color(values);
-
-            canvas->save();
-            canvas->rotate(values[4], fR.centerX(), fR.centerY());
-
-            switch (res) {
-                case SkInterpolator::kFreezeEnd_Result:
-                    delete fInterp;
-                    fInterp = nullptr;
-                    break;
-                default:
-                    break;
-            }
-        }
-        paint.setColor(fColor);
-        canvas->drawRect(fR, paint);
-    }
-
-    SkRect onGetBounds() override { return fR; }
-};
-
-class HTView : public Sample {
-public:
-    enum {
-        N = 50,
-        W = 640,
-        H = 480,
-    };
-
-    struct Rec {
-        HTDrawable* fDrawable;
-    };
-    Rec fArray[N];
-    sk_sp<SkDrawable> fRoot;
-    SkMSec fTime;
-
-    HTView() {
-        SkRandom rand;
-
-        SkPictureRecorder recorder;
-        SkCanvas* canvas = recorder.beginRecording(SkRect::MakeWH(W, H));
-        for (int i = 0; i < N; ++i) {
-            fArray[i].fDrawable = new HTDrawable(rand);
-            canvas->drawDrawable(fArray[i].fDrawable);
-            fArray[i].fDrawable->unref();
-        }
-        fRoot = recorder.finishRecordingAsDrawable();
-    }
-
-protected:
-    SkString name() override { return SkString("HT"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawDrawable(fRoot.get());
-    }
-
-    bool onAnimate(double nanos) override {
-        fTime = TimeUtils::NanosToMSec(nanos);
-        for (int i = 0; i < N; ++i) {
-            fArray[i].fDrawable->setTime(fTime);
-        }
-        return true;
-    }
-
-    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
-        // search backwards to find the top-most
-        for (int i = N - 1; i >= 0; --i) {
-            if (fArray[i].fDrawable->hitTest(x, y)) {
-                fArray[i].fDrawable->spawnAnimation(fTime);
-                break;
-            }
-        }
-        return nullptr;
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE( return new HTView(); )
diff --git a/third_party/skia/samplecode/SampleHairCurves.cpp b/third_party/skia/samplecode/SampleHairCurves.cpp
index 73a6aee..cdb8ed8 100644
--- a/third_party/skia/samplecode/SampleHairCurves.cpp
+++ b/third_party/skia/samplecode/SampleHairCurves.cpp
@@ -114,7 +114,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleHairModes.cpp b/third_party/skia/samplecode/SampleHairModes.cpp
index 5df28f5..6e05fbc 100644
--- a/third_party/skia/samplecode/SampleHairModes.cpp
+++ b/third_party/skia/samplecode/SampleHairModes.cpp
@@ -61,10 +61,8 @@
     *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = 0xFFFFFFFF;
     *bm.getAddr32(1, 0) = *bm.getAddr32(0, 1) = SkPackARGB32(0xFF, 0xCC, 0xCC, 0xCC);
 
-    SkMatrix m;
-    m.setScale(SkIntToScalar(6), SkIntToScalar(6));
-
-    return bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, &m);
+    return bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions(),
+                         SkMatrix::Scale(6, 6));
 }
 
 class HairModesView : public Sample {
@@ -108,7 +106,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleHairline.cpp b/third_party/skia/samplecode/SampleHairline.cpp
deleted file mode 100644
index a43ac44..0000000
--- a/third_party/skia/samplecode/SampleHairline.cpp
+++ /dev/null
@@ -1,231 +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 "include/core/SkBitmap.h"
-#include "include/core/SkCanvas.h"
-#include "include/core/SkColorFilter.h"
-#include "include/core/SkColorPriv.h"
-#include "include/core/SkGraphics.h"
-#include "include/core/SkPath.h"
-#include "include/core/SkRegion.h"
-#include "include/core/SkShader.h"
-#include "include/core/SkStream.h"
-#include "include/core/SkTime.h"
-#include "include/core/SkTypeface.h"
-#include "include/effects/SkCornerPathEffect.h"
-#include "include/effects/SkGradientShader.h"
-#include "include/private/SkTo.h"
-#include "include/utils/SkRandom.h"
-#include "samplecode/Sample.h"
-#include "src/utils/SkUTF.h"
-
-static SkRandom gRand;
-
-static void generate_pts(SkPoint pts[], int count, int w, int h) {
-    for (int i = 0; i < count; i++) {
-        pts[i].set(gRand.nextUScalar1() * 3 * w - SkIntToScalar(w),
-                   gRand.nextUScalar1() * 3 * h - SkIntToScalar(h));
-    }
-}
-
-static bool check_zeros(const SkPMColor pixels[], int count, int skip) {
-    for (int i = 0; i < count; i++) {
-        if (*pixels) {
-            return false;
-        }
-        pixels += skip;
-    }
-    return true;
-}
-
-static bool check_bitmap_margin(const SkBitmap& bm, int margin) {
-    size_t rb = bm.rowBytes();
-    for (int i = 0; i < margin; i++) {
-        if (!check_zeros(bm.getAddr32(0, i), bm.width(), 1)) {
-            return false;
-        }
-        int bottom = bm.height() - i - 1;
-        if (!check_zeros(bm.getAddr32(0, bottom), bm.width(), 1)) {
-            return false;
-        }
-        // left column
-        if (!check_zeros(bm.getAddr32(i, 0), bm.height(), SkToInt(rb >> 2))) {
-            return false;
-        }
-        int right = bm.width() - margin + i;
-        if (!check_zeros(bm.getAddr32(right, 0), bm.height(),
-                         SkToInt(rb >> 2))) {
-            return false;
-        }
-    }
-    return true;
-}
-
-#define WIDTH   620
-#define HEIGHT  460
-#define MARGIN  10
-
-static void line_proc(SkCanvas* canvas, const SkPaint& paint,
-                      const SkBitmap& bm) {
-    const int N = 2;
-    SkPoint pts[N];
-    for (int i = 0; i < 400; i++) {
-        generate_pts(pts, N, WIDTH, HEIGHT);
-
-        canvas->drawLine(pts[0], pts[1], paint);
-        if (!check_bitmap_margin(bm, MARGIN)) {
-            SkDebugf("---- hairline failure (%g %g) (%g %g)\n",
-                     pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
-            break;
-        }
-    }
-}
-
-static void poly_proc(SkCanvas* canvas, const SkPaint& paint,
-                      const SkBitmap& bm) {
-    const int N = 8;
-    SkPoint pts[N];
-    for (int i = 0; i < 50; i++) {
-        generate_pts(pts, N, WIDTH, HEIGHT);
-
-        SkPath path;
-        path.moveTo(pts[0]);
-        for (int j = 1; j < N; j++) {
-            path.lineTo(pts[j]);
-        }
-        canvas->drawPath(path, paint);
-    }
-}
-
-static SkPoint ave(const SkPoint& a, const SkPoint& b) {
-    SkPoint c = a + b;
-    c.fX = SkScalarHalf(c.fX);
-    c.fY = SkScalarHalf(c.fY);
-    return c;
-}
-
-static void quad_proc(SkCanvas* canvas, const SkPaint& paint,
-                      const SkBitmap& bm) {
-    const int N = 30;
-    SkPoint pts[N];
-    for (int i = 0; i < 10; i++) {
-        generate_pts(pts, N, WIDTH, HEIGHT);
-
-        SkPath path;
-        path.moveTo(pts[0]);
-        for (int j = 1; j < N - 2; j++) {
-            path.quadTo(pts[j], ave(pts[j], pts[j+1]));
-        }
-        path.quadTo(pts[N - 2], pts[N - 1]);
-
-        canvas->drawPath(path, paint);
-    }
-}
-
-static void add_cubic(SkPath* path, const SkPoint& mid, const SkPoint& end) {
-    SkPoint start;
-    path->getLastPt(&start);
-    path->cubicTo(ave(start, mid), ave(mid, end), end);
-}
-
-static void cube_proc(SkCanvas* canvas, const SkPaint& paint,
-                      const SkBitmap& bm) {
-    const int N = 30;
-    SkPoint pts[N];
-    for (int i = 0; i < 10; i++) {
-        generate_pts(pts, N, WIDTH, HEIGHT);
-
-        SkPath path;
-        path.moveTo(pts[0]);
-        for (int j = 1; j < N - 2; j++) {
-            add_cubic(&path, pts[j], ave(pts[j], pts[j+1]));
-        }
-        add_cubic(&path, pts[N - 2], pts[N - 1]);
-
-        canvas->drawPath(path, paint);
-    }
-}
-
-typedef void (*HairProc)(SkCanvas*, const SkPaint&, const SkBitmap&);
-
-static const struct {
-    const char* fName;
-    HairProc    fProc;
-} gProcs[] = {
-    { "line",   line_proc },
-    { "poly",   poly_proc },
-    { "quad",   quad_proc },
-    { "cube",   cube_proc },
-};
-
-static int cycle_hairproc_index(int index) {
-    return (index + 1) % SK_ARRAY_COUNT(gProcs);
-}
-
-class HairlineView : public Sample {
-    SkMSec fNow;
-    int fProcIndex;
-    bool fDoAA;
-public:
-    HairlineView() {
-        fProcIndex = 0;
-        fDoAA = true;
-        fNow = 0;
-    }
-
-protected:
-    SkString name() override { return SkStringPrintf("Hair-%s", gProcs[fProcIndex].fName); }
-
-    void show_bitmaps(SkCanvas* canvas, const SkBitmap& b0, const SkBitmap& b1,
-                      const SkIRect& inset) {
-        canvas->drawBitmap(b0, 0, 0, nullptr);
-        canvas->drawBitmap(b1, SkIntToScalar(b0.width()), 0, nullptr);
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        gRand.setSeed(fNow);
-
-        SkBitmap bm, bm2;
-        bm.allocN32Pixels(WIDTH + MARGIN*2, HEIGHT + MARGIN*2);
-        // this will erase our margin, which we want to always stay 0
-        bm.eraseColor(SK_ColorTRANSPARENT);
-
-        bm2.installPixels(SkImageInfo::MakeN32Premul(WIDTH, HEIGHT),
-                          bm.getAddr32(MARGIN, MARGIN), bm.rowBytes());
-
-        SkCanvas c2(bm2);
-        SkPaint paint;
-        paint.setAntiAlias(fDoAA);
-        paint.setStyle(SkPaint::kStroke_Style);
-
-        bm2.eraseColor(SK_ColorTRANSPARENT);
-        gProcs[fProcIndex].fProc(&c2, paint, bm);
-        canvas->drawBitmap(bm2, SkIntToScalar(10), SkIntToScalar(10), nullptr);
-    }
-
-    bool onAnimate(double /*nanos*/) override {
-        if (fDoAA) {
-            fProcIndex = cycle_hairproc_index(fProcIndex);
-            // todo: signal that we want to rebuild our TITLE
-        }
-        fDoAA = !fDoAA;
-        return true;
-    }
-
-    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
-        fDoAA = !fDoAA;
-        return this->INHERITED::onFindClickHandler(x, y, modi);
-    }
-
-
-private:
-    typedef Sample INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE( return new HairlineView(); )
diff --git a/third_party/skia/samplecode/SampleIdentityScale.cpp b/third_party/skia/samplecode/SampleIdentityScale.cpp
deleted file mode 100644
index 4e7b1dd..0000000
--- a/third_party/skia/samplecode/SampleIdentityScale.cpp
+++ /dev/null
@@ -1,74 +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 "include/core/SkCanvas.h"
-#include "include/core/SkColorPriv.h"
-#include "include/core/SkFont.h"
-#include "include/core/SkPath.h"
-#include "include/core/SkStream.h"
-#include "include/core/SkTime.h"
-#include "include/effects/SkBlurMaskFilter.h"
-#include "include/utils/SkRandom.h"
-#include "samplecode/DecodeFile.h"
-#include "samplecode/Sample.h"
-#include "src/core/SkClipOpPriv.h"
-#include "tools/Resources.h"
-
-// Intended to exercise pixel snapping observed with scaled images (and
-// with non-scaled images, but for a different reason):  Bug 1145
-
-class IdentityScaleView : public Sample {
-public:
-    IdentityScaleView(const char imageFilename[]) {
-        if (!DecodeDataToBitmap(GetResourceAsData(imageFilename), &fBM)) {
-            fBM.allocN32Pixels(1, 1);
-            *(fBM.getAddr32(0,0)) = 0xFF0000FF; // red == bad
-        }
-    }
-
-protected:
-    SkBitmap fBM;
-
-    SkString name() override { return SkString("IdentityScale"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-
-        SkFont font(nullptr, 48);
-        SkPaint paint;
-
-        paint.setAntiAlias(true);
-        paint.setFilterQuality(kHigh_SkFilterQuality);
-
-        SkTime::DateTime time;
-        SkTime::GetDateTime(&time);
-
-        bool use_scale = (time.fSecond % 2 == 1);
-        const char *text;
-
-        canvas->save();
-        if (use_scale) {
-          text = "Scaled = 1";
-        } else {
-
-          SkRect r = { 100, 100, 356, 356 };
-          SkPath clipPath;
-          clipPath.addRoundRect(r, SkIntToScalar(5), SkIntToScalar(5));
-          canvas->clipPath(clipPath, kIntersect_SkClipOp, true);
-          text = "Scaled = 0";
-        }
-        canvas->drawBitmap( fBM, 100, 100, &paint );
-        canvas->restore();
-        canvas->drawString(text, 100, 400, font, paint);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE( return new IdentityScaleView("images/mandrill_256.png"); )
diff --git a/third_party/skia/samplecode/SampleImageFilterDAG.cpp b/third_party/skia/samplecode/SampleImageFilterDAG.cpp
index 41340b6..3c925d1 100644
--- a/third_party/skia/samplecode/SampleImageFilterDAG.cpp
+++ b/third_party/skia/samplecode/SampleImageFilterDAG.cpp
@@ -42,224 +42,89 @@
     int fDepth;
 
     // The source content rect (this is the same for all nodes, but is stored here for convenience)
-    SkRect fContent;
-    // The portion of the original CTM that is kept as the local matrix/ctm when filtering
-    SkMatrix fLocalCTM;
-    // The portion of the original CTM that the results should be drawn with (or given current
-    // canvas impl., the portion of the CTM that is baked into a new DAG)
-    SkMatrix fRemainingCTM;
+    skif::ParameterSpace<SkRect> fContent;
+    // The mapping for the filter dag (same for all nodes, but stored here for convenience)
+    skif::Mapping fMapping;
 
-    // Cached reverse bounds using device-space clip bounds (e.g. SkCanvas::clipRectBounds with
-    // null first argument). This represents the layer calculated in SkCanvas for the filtering.
-    // FIXME: SkCanvas (and this sample), this is seeded with the device-space clip bounds so that
-    // the implicit matrix node's reverse bounds are updated appropriately when it recurses to the
-    // original root node.
-    SkIRect fLayerBounds;
+    // Cached reverse bounds using device-space clip bounds (e.g. no local bounds hint passed to
+    // saveLayer). This represents the layer calculated in SkCanvas for the filtering.
+    skif::LayerSpace<SkIRect> fUnhintedLayerBounds;
 
-    // Cached reverse bounds using the local draw bounds (e.g. SkCanvas::clipRectBounds with the
-    // draw bounds provided as first argument). For intermediate nodes in a DAG, this is calculated
-    // to match what the filter would compute when being evaluated as part of the original DAG
-    // (i.e. if the implicit matrix filter node were not inserted at the beginning).
-    // fReverseLocalIsolatedBounds is the same, except it represents what would be calculated if
-    // only this node were being applied as the image filter.
-    SkIRect fReverseLocalBounds;
-    SkIRect fReverseLocalIsolatedBounds;
+    // Cached input bounds using the local draw bounds (e.g. saveLayer with a bounds rect, or
+    // an auto-layer for a draw with image filter). This represents the layer bounds up to this
+    // point of the DAG.
+    skif::LayerSpace<SkIRect> fHintedLayerBounds;
 
-    // Cached forward bounds based on local draw bounds. For intermediate nodes in a DAG, this is
-    // calculated to match what the filter computes as part of the whole DAG. fForwardIsolatedBounds
-    // is the same but represents what would be calculated if only this node were applied.
-    SkIRect fForwardBounds;
-    SkIRect fForwardIsolatedBounds;
+    // Cached output bounds based on local draw bounds. This represents the output up to this
+    // point of the DAG.
+    skif::LayerSpace<SkIRect> fOutputBounds;
 
-    // Should be called after the input nodes have been created since this will complete the
-    // entire tree.
-    void computeBounds() {
-        // In normal usage, forward bounds are filter-space bounds of the geometry content, so
-        // fContent mapped by the local matrix, since we assume the layer content is made by
-        // concat(localCTM) -> clipRect(content) -> drawRect(content).
-        // Similarly, in normal usage, reverse bounds are the filter-space bounds of the space to
-        // be filled by image filter results. Since the clip rect is set to the same as the content,
-        // it's the same bounds forward or reverse in this contrived case.
-        SkIRect inputRect;
-        fLocalCTM.mapRect(fContent).roundOut(&inputRect);
-
-        this->computeForwardBounds(inputRect);
-
-        // The layer bounds (matching what SkCanvas computes), use the content rect mapped by the
-        // entire CTM as its input rect. If this is an implicit matrix node, the computeReverseX
-        // functions will switch to using the local-mapped bounds for children in order to simulate
-        // what would happen if the last step were done as a draw. When there's no implicit matrix
-        // node, this calculated rectangle is the same as inputRect.
-        SkIRect deviceRect;
-        SkMatrix ctm = SkMatrix::Concat(fRemainingCTM, fLocalCTM);
-        ctm.mapRect(fContent).roundOut(&deviceRect);
-
-        SkASSERT(this->isImplicitMatrixNode() || inputRect == deviceRect);
-        this->computeReverseLocalIsolatedBounds(deviceRect);
-        this->computeReverseBounds(deviceRect, false);
-        // Unlike the above two calls, calculating layer bounds will keep the device bounds for
-        // intermediate nodes to show the current SkCanvas behavior vs. the ideal
-        this->computeReverseBounds(deviceRect, true);
-    }
-
-    bool isImplicitMatrixNode() const {
-        // In the future we wish to replace the implicit matrix node with direct draws to the final
-        // destination (instead of using an SkMatrixImageFilter). Visualizing the DAG correctly
-        // requires handling these nodes differently since it has part of the canvas CTM built in.
-        return fDepth == 1 && !fRemainingCTM.isIdentity();
+    FilterNode(const SkImageFilter* filter,
+               const skif::Mapping& mapping,
+               const skif::ParameterSpace<SkRect>& content,
+               int depth)
+            : fFilter(sk_ref_sp(filter))
+            , fDepth(depth)
+            , fContent(content)
+            , fMapping(mapping) {
+        this->computeInputBounds();
+        this->computeOutputBounds();
+        if (fFilter) {
+            fInputNodes.reserve_back(fFilter->countInputs());
+            for (int i = 0; i < fFilter->countInputs(); ++i) {
+                fInputNodes.emplace_back(fFilter->getInput(i), mapping, content, depth + 1);
+            }
+        }
     }
 
 private:
-    void computeForwardBounds(const SkIRect srcRect) {
+    void computeOutputBounds() {
         if (fFilter) {
-            // For forward filtering, the leaves of the DAG are evaluated first and are then
-            // propagated to the root. This means that every filter's filterBounds() function sees
-            // the original src rect. It is never dependent on the parent node (unlike reverse
-            // filtering), so calling filterBounds() on an intermediate node gives us the correct
-            // intermediate values.
-            fForwardBounds = fFilter->filterBounds(
-                    srcRect, fLocalCTM, SkImageFilter::kForward_MapDirection, nullptr);
-
-            // For isolated forward filtering, it uses the same input but should not be propagated
-            // to the inputs, so get the filter node bounds directly.
-            fForwardIsolatedBounds = as_IFB(fFilter)->filterNodeBounds(
-                    srcRect, fLocalCTM, SkImageFilter::kForward_MapDirection, nullptr);
+            // For visualization purposes, we want the output bounds in layer space, before it's
+            // been transformed to device space. To achieve that, we mock a new mapping with the
+            // identity matrix transform.
+            skif::Mapping layerOnly{fMapping.layerMatrix()};
+            skif::DeviceSpace<SkIRect> pseudoDeviceBounds =
+                    as_IFB(fFilter)->getOutputBounds(layerOnly, fContent);
+            // Since layerOnly's device matrix is I, this is effectively a cast to layer space
+            fOutputBounds = layerOnly.deviceToLayer(pseudoDeviceBounds);
         } else {
-            fForwardBounds = srcRect;
-            fForwardIsolatedBounds = srcRect;
+            fOutputBounds = fMapping.paramToLayer(fContent).roundOut();
         }
 
         // Fill in children
         for (int i = 0; i < fInputNodes.count(); ++i) {
-            fInputNodes[i].computeForwardBounds(srcRect);
+            fInputNodes[i].computeOutputBounds();
         }
     }
 
-    void computeReverseLocalIsolatedBounds(const SkIRect& srcRect) {
-        if (fFilter) {
-            fReverseLocalIsolatedBounds = as_IFB(fFilter)->filterNodeBounds(
-                    srcRect, fLocalCTM, SkImageFilter::kReverse_MapDirection, &srcRect);
-        } else {
-            fReverseLocalIsolatedBounds = srcRect;
-        }
-
-        SkIRect childSrcRect = srcRect;
-        if (this->isImplicitMatrixNode()) {
-            // Switch srcRect from the device-space bounds to what would be used when the draw is
-            // the final step of filtering, as if the implicit node weren't needed
-            fLocalCTM.mapRect(fContent).roundOut(&childSrcRect);
-        }
-
-        // Fill in children. Unlike regular reverse bounds mapping, the input nodes see the original
-        // bounds. Normally, the bounds that the child nodes see have already been mapped processed
-        // by this node.
-        for (int i = 0; i < fInputNodes.count(); ++i) {
-            fInputNodes[i].computeReverseLocalIsolatedBounds(childSrcRect);
-        }
-    }
-
-    // fReverseLocalBounds and fLayerBounds are computed the same, except they differ in what the
-    // initial bounding rectangle was. It is assumed that the 'srcRect' has already been processed
-    // by the parent node's onFilterNodeBounds() function, as in SkImageFilter::filterBounds().
-    void computeReverseBounds(const SkIRect& srcRect, bool writeToLayerBounds) {
-        SkIRect reverseBounds = srcRect;
+    void computeInputBounds() {
+        // As a proxy for what the base device had, use the content rect mapped to device space
+        // (e.g. clipRect() was called with the same coords prior to the draw).
+        skif::DeviceSpace<SkIRect> targetOutput(fMapping.totalMatrix()
+                                                        .mapRect(SkRect(fContent))
+                                                        .roundOut());
 
         if (fFilter) {
-            // Since srcRect has been through parent's onFilterNodeBounds(), calling filterBounds()
-            // directly on this node will calculate the same rectangle that this filter would report
-            // during the parent node's onFilterBounds() recursion.
-            reverseBounds = fFilter->filterBounds(
-                    srcRect, fLocalCTM, SkImageFilter::kReverse_MapDirection, &srcRect);
-
-            SkIRect nextSrcRect;
-            if (this->isImplicitMatrixNode() && !writeToLayerBounds) {
-                // When not writing to the layer bounds, and we're the implicit matrix node
-                // we reset the src rect to be what it should be if no implicit node was necessary.
-                fLocalCTM.mapRect(fContent).roundOut(&nextSrcRect);
-            } else {
-                // To calculate the appropriate intermediate reverse bounds for the children, we
-                // need this node's onFilterNodeBounds() results based on its parents' bounds (the
-                // current 'srcRect').
-                nextSrcRect = as_IFB(fFilter)->filterNodeBounds(
-                    srcRect, fLocalCTM, SkImageFilter::kReverse_MapDirection, &srcRect);
-            }
-
-            // Fill in the children. The union of these bounds should equal the value calculated
-            // for reverseBounds already.
-            SkDEBUGCODE(SkIRect netReverseBounds = SkIRect::MakeEmpty();)
-            for (int i = 0; i < fInputNodes.count(); ++i) {
-                fInputNodes[i].computeReverseBounds(nextSrcRect, writeToLayerBounds);
-                SkDEBUGCODE(netReverseBounds.join(
-                        writeToLayerBounds ? fInputNodes[i].fLayerBounds
-                                           : fInputNodes[i].fReverseLocalBounds);)
-            }
-            // Because of the resetting done when not computing layer bounds for the implicit
-            // matrix node, this assertion doesn't hold in that particular scenario.
-            SkASSERT(netReverseBounds == reverseBounds ||
-                     (this->isImplicitMatrixNode() && !writeToLayerBounds));
-        }
-
-        if (writeToLayerBounds) {
-            fLayerBounds = reverseBounds;
+            fHintedLayerBounds = as_IFB(fFilter)->getInputBounds(fMapping, targetOutput, &fContent);
+            fUnhintedLayerBounds = as_IFB(fFilter)->getInputBounds(fMapping, targetOutput, nullptr);
         } else {
-            fReverseLocalBounds = reverseBounds;
+            fHintedLayerBounds = fMapping.paramToLayer(fContent).roundOut();
+            fUnhintedLayerBounds = fMapping.deviceToLayer(targetOutput);
         }
     }
 };
 
 } // anonymous namespace
 
-static FilterNode build_dag(const SkMatrix& local, const SkMatrix& remainder, const SkRect& rect,
-                            const SkImageFilter* filter, int depth) {
-    FilterNode node;
-    node.fFilter = sk_ref_sp(filter);
-    node.fDepth = depth;
-    node.fContent = rect;
-
-    node.fLocalCTM = local;
-    node.fRemainingCTM = remainder;
-
-    if (node.fFilter) {
-        if (depth > 0) {
-            // We don't visit children when at the root because the real child filters are replaced
-            // with the internalSaveLayer decomposition emulation, which then cycles back to the
-            // original filter but with an updated matrix (and then we process the children).
-            node.fInputNodes.reserve(node.fFilter->countInputs());
-            for (int i = 0; i < node.fFilter->countInputs(); ++i) {
-                node.fInputNodes.push_back() =
-                        build_dag(local, remainder, rect, node.fFilter->getInput(i), depth + 1);
-            }
-        }
-    }
-
-    return node;
-}
-
 static FilterNode build_dag(const SkMatrix& ctm, const SkRect& rect,
                             const SkImageFilter* rootFilter) {
     // Emulate SkCanvas::internalSaveLayer's decomposition of the CTM.
-    SkMatrix local;
-    sk_sp<SkImageFilter> finalFilter = as_IFB(rootFilter)->applyCTM(ctm, &local);
-
-    // In ApplyCTMToFilter, the CTM is decomposed such that CTM = remainder * local. The matrix
-    // that is embedded in 'finalFilter' is actually local^-1*remainder*local to account for
-    // how SkMatrixImageFilter is specified, but we want the true remainder since it is what should
-    // transform the results to put in the correct place after filtering.
-    SkMatrix invLocal, remaining;
-    if (as_IFB(rootFilter)->uniqueID() != as_IFB(finalFilter)->uniqueID()) {
-        remaining = SkMatrix::Concat(ctm, invLocal);
-    } else {
-        remaining = SkMatrix::I();
-    }
-
-    // Create a root node that represents the full result
-    FilterNode rootNode = build_dag(ctm, SkMatrix::I(), rect, rootFilter, 0);
-    // Set its only child as the modified DAG that handles the CTM decomposition
-    rootNode.fInputNodes.push_back() =
-            build_dag(local, remaining, rect, finalFilter.get(), 1);
-    // Fill in bounds information that requires the entire node DAG to have been extracted first.
-    rootNode.fInputNodes[0].computeBounds();
-    return rootNode;
+    skif::ParameterSpace<SkRect> content(rect);
+    skif::ParameterSpace<SkPoint> center({rect.centerX(), rect.centerY()});
+    skif::Mapping mapping;
+    SkAssertResult(mapping.decomposeCTM(ctm, rootFilter, center));
+    return FilterNode(rootFilter, mapping, content, 0);
 }
 
 static void draw_node(SkCanvas* canvas, const FilterNode& node) {
@@ -268,10 +133,11 @@
     SkPaint filterPaint;
     filterPaint.setImageFilter(node.fFilter);
 
+    SkRect content = SkRect(node.fContent);
     SkPaint paint;
     static const SkColor kColors[2] = {SK_ColorGREEN, SK_ColorWHITE};
-    SkPoint points[2] = { {node.fContent.fLeft + 15.f, node.fContent.fTop + 15.f},
-                          {node.fContent.fRight - 15.f, node.fContent.fBottom - 15.f} };
+    SkPoint points[2] = { {content.fLeft + 15.f, content.fTop + 15.f},
+                          {content.fRight - 15.f, content.fBottom - 15.f} };
     paint.setShader(SkGradientShader::MakeLinear(points, kColors, nullptr, SK_ARRAY_COUNT(kColors),
                                                  SkTileMode::kRepeat));
 
@@ -279,77 +145,40 @@
     line.setStrokeWidth(0.f);
     line.setStyle(SkPaint::kStroke_Style);
 
-    if (node.fDepth == 0) {
-        // The root node, so draw this one the canonical way through SkCanvas to show current
-        // net behavior. Will not include bounds visualization.
-        canvas->save();
-        canvas->concat(node.fLocalCTM);
-        SkASSERT(node.fRemainingCTM.isIdentity());
+    canvas->save();
+    canvas->concat(node.fMapping.deviceMatrix());
+    canvas->save();
+    canvas->concat(node.fMapping.layerMatrix());
 
-        canvas->clipRect(node.fContent, /* aa */ true);
-        canvas->saveLayer(nullptr, &filterPaint);
-        canvas->drawRect(node.fContent, paint);
-        canvas->restore(); // Completes the image filter
-        canvas->restore(); // Undoes matrix and clip
+    canvas->saveLayer(&content, &filterPaint);
+    canvas->drawRect(content, paint);
+    canvas->restore(); // Completes the image filter
 
-        // Draw content rect (no clipping)
-        canvas->save();
-        canvas->concat(node.fLocalCTM);
-        line.setColor(SK_ColorBLACK);
-        canvas->drawRect(node.fContent, line);
-        canvas->restore();
-    } else {
-        canvas->save();
-        if (!node.isImplicitMatrixNode()) {
-            canvas->concat(node.fRemainingCTM);
-        }
-        canvas->concat(node.fLocalCTM);
+    // Draw content-rect bounds
+    line.setColor(SK_ColorBLACK);
+    canvas->drawRect(content, line);
 
-        canvas->saveLayer(nullptr, &filterPaint);
-        canvas->drawRect(node.fContent, paint);
-        canvas->restore(); // Completes the image filter
+    // Bounding boxes have all been mapped by the layer matrix from local to layer space, so undo
+    // the layer matrix, leaving just the device matrix.
+    canvas->restore();
 
-        // Draw content-rect bounds
-        line.setColor(SK_ColorBLACK);
-        if (node.isImplicitMatrixNode()) {
-            canvas->setMatrix(SkMatrix::Concat(node.fRemainingCTM, node.fLocalCTM));
-        }
-        canvas->drawRect(node.fContent, line);
-        canvas->restore(); // Undoes the matrix
+    // The hinted bounds of the layer saved for the filtering
+    line.setColor(SK_ColorRED);
+    canvas->drawRect(SkRect::Make(SkIRect(node.fHintedLayerBounds)).makeOutset(3.f, 3.f), line);
+    // The bounds of the layer if there was no local content hint
+    line.setColor(SK_ColorGREEN);
+    canvas->drawRect(SkRect::Make(SkIRect(node.fUnhintedLayerBounds)).makeOutset(2.f, 2.f), line);
 
-        // Bounding boxes have all been mapped by the local matrix already, so drawing them with
-        // the remaining CTM should align everything to the already drawn filter outputs. The
-        // exception is forward bounds of the implicit matrix node, which also have been mapped
-        // by the remainder matrix.
-        canvas->save();
-        canvas->concat(node.fRemainingCTM);
-
-        // The bounds of the layer saved for the filtering as currently implemented
-        line.setColor(SK_ColorRED);
-        canvas->drawRect(SkRect::Make(node.fLayerBounds).makeOutset(5.f, 5.f), line);
-        // The bounds of the layer that could be saved if the last step were a draw
-        line.setColor(SK_ColorMAGENTA);
-        canvas->drawRect(SkRect::Make(node.fReverseLocalBounds).makeOutset(4.f, 4.f), line);
-
-        // Dashed lines for the isolated shapes
-        static const SkScalar kDashParams[] = {6.f, 12.f};
-        line.setPathEffect(SkDashPathEffect::Make(kDashParams, 2, 0.f));
-        // The bounds of the layer if it were the only filter in the DAG
-        canvas->drawRect(SkRect::Make(node.fReverseLocalIsolatedBounds).makeOutset(3.f, 3.f), line);
-
-        if (node.isImplicitMatrixNode()) {
-            canvas->resetMatrix();
-        }
-        // The output bounds calculated as if the node were the only filter in the DAG
-        line.setColor(SK_ColorBLUE);
-        canvas->drawRect(SkRect::Make(node.fForwardIsolatedBounds).makeOutset(1.f, 1.f), line);
-
-        // The output bounds calculated for the node
-        line.setPathEffect(nullptr);
-        canvas->drawRect(SkRect::Make(node.fForwardBounds).makeOutset(2.f, 2.f), line);
-
-        canvas->restore();
-    }
+    // The output bounds in layer space
+    line.setColor(SK_ColorBLUE);
+    canvas->drawRect(SkRect::Make(SkIRect(node.fOutputBounds)).makeOutset(1.f, 1.f), line);
+    // Device-space bounding box of the output bounds (e.g. what legacy DAG manipulation via
+    // MatrixTransform would produce).
+    static const SkScalar kDashParams[] = {6.f, 12.f};
+    line.setPathEffect(SkDashPathEffect::Make(kDashParams, 2, 0.f));
+    SkRect devOutputBounds = SkRect::Make(SkIRect(node.fMapping.layerToDevice(node.fOutputBounds)));
+    canvas->restore(); // undoes device matrix
+    canvas->drawRect(devOutputBounds, line);
 }
 
 static constexpr float kLineHeight = 16.f;
@@ -385,21 +214,23 @@
     text.setAntiAlias(true);
 
     float y = kLineHeight;
-    if (node.fDepth == 0) {
-        canvas->drawString("Final Results", kLineInset, y, font, text);
-        // The actual interesting matrices are in the root node's first child
-        y = print_matrix(canvas, "Local", node.fInputNodes[0].fLocalCTM,
-                     kLineInset, y + kLineHeight, font, text);
-        y = print_matrix(canvas, "Embedded", node.fInputNodes[0].fRemainingCTM,
-                     kLineInset, y, font, text);
-    } else if (node.fFilter) {
+    if (node.fFilter) {
         canvas->drawString(node.fFilter->getTypeName(), kLineInset, y, font, text);
-        print_size(canvas, "Layer Size", node.fLayerBounds, kLineInset, y + kLineHeight,
-                   font, text);
-        y = print_size(canvas, "Ideal Size", node.fReverseLocalBounds, 10 * kLineInset,
-                       y + kLineHeight, font, text);
+        y += kLineHeight;
+        if (node.fDepth == 0) {
+            // The mapping is the same for all nodes, so only print at the root
+            y = print_matrix(canvas, "Param->Layer", node.fMapping.layerMatrix(),
+                        kLineInset, y, font, text);
+            y = print_matrix(canvas, "Layer->Device", node.fMapping.deviceMatrix(),
+                        kLineInset, y, font, text);
+        }
+
+        y = print_size(canvas, "Layer Size", SkIRect(node.fUnhintedLayerBounds),
+                       kLineInset, y, font, text);
+        y = print_size(canvas, "Layer Size (hinted)", SkIRect(node.fHintedLayerBounds),
+                       kLineInset, y, font, text);
     } else {
-        canvas->drawString("Source Input", kLineInset, kLineHeight, font, text);
+        canvas->drawString("Source Input", kLineInset, y, font, text);
         y += kLineHeight;
     }
 
@@ -448,7 +279,7 @@
         y = draw_dag(canvas, nodeSurface, node.fInputNodes[i]);
         canvas->restore();
     }
-    return SkMaxScalar(y, nodeResults->height() + textHeight + kPad);
+    return std::max(y, nodeResults->height() + textHeight + kPad);
 }
 
 static void draw_dag(SkCanvas* canvas, sk_sp<SkImageFilter> filter,
@@ -509,7 +340,7 @@
 
 private:
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 DEF_SAMPLE(return new ImageFilterDAGSample();)
diff --git a/third_party/skia/samplecode/SampleLCD.cpp b/third_party/skia/samplecode/SampleLCD.cpp
index 3c71415..0fd46a1 100644
--- a/third_party/skia/samplecode/SampleLCD.cpp
+++ b/third_party/skia/samplecode/SampleLCD.cpp
@@ -49,7 +49,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleLayerMask.cpp b/third_party/skia/samplecode/SampleLayerMask.cpp
deleted file mode 100644
index 546c6c4..0000000
--- a/third_party/skia/samplecode/SampleLayerMask.cpp
+++ /dev/null
@@ -1,66 +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 "include/core/SkBitmap.h"
-#include "include/core/SkCanvas.h"
-#include "include/core/SkPaint.h"
-#include "include/core/SkPath.h"
-#include "samplecode/Sample.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-class LayerMaskView : public Sample {
-public:
-    LayerMaskView() {
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-protected:
-    virtual SkString name() { return SkString("LayerMask"); }
-
-    void drawMask(SkCanvas* canvas, const SkRect& r) {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-
-        if (true) {
-            SkBitmap mask;
-            int w = SkScalarRoundToInt(r.width());
-            int h = SkScalarRoundToInt(r.height());
-            mask.allocN32Pixels(w, h);
-            mask.eraseColor(SK_ColorTRANSPARENT);
-            SkCanvas c(mask);
-            SkRect bounds = r;
-            bounds.offset(-bounds.fLeft, -bounds.fTop);
-            c.drawOval(bounds, paint);
-
-            paint.setBlendMode(SkBlendMode::kDstIn);
-            canvas->drawBitmap(mask, r.fLeft, r.fTop, &paint);
-        } else {
-            SkPath p;
-            p.addOval(r);
-            p.setFillType(SkPathFillType::kInverseWinding);
-            paint.setBlendMode(SkBlendMode::kDstOut);
-            canvas->drawPath(p, paint);
-        }
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkRect  r;
-        r.setLTRB(20, 20, 120, 120);
-        canvas->saveLayer(&r, nullptr);
-        canvas->drawColor(SK_ColorRED);
-        drawMask(canvas, r);
-        canvas->restore();
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE( return new LayerMaskView(); )
diff --git a/third_party/skia/samplecode/SampleLayers.cpp b/third_party/skia/samplecode/SampleLayers.cpp
index aeddc46..c228578 100644
--- a/third_party/skia/samplecode/SampleLayers.cpp
+++ b/third_party/skia/samplecode/SampleLayers.cpp
@@ -15,12 +15,9 @@
 #include "include/core/SkShader.h"
 #include "include/core/SkTime.h"
 #include "include/core/SkTypeface.h"
-#include "include/effects/SkBlurMaskFilter.h"
 #include "include/effects/SkGradientShader.h"
 #include "include/utils/SkCamera.h"
-#include "include/utils/SkInterpolator.h"
 #include "samplecode/Sample.h"
-#include "src/core/SkClipOpPriv.h"
 #include "src/utils/SkUTF.h"
 
 static void make_paint(SkPaint* paint, const SkMatrix& localMatrix) {
@@ -37,9 +34,6 @@
 
     SkRect r;
 
-    SkPaint p;
-    p.setAlpha(0x88);
-
     SkAutoCanvasRestore ar2(canvas, false);
 
     // create the layers
@@ -56,7 +50,7 @@
 
     // now draw the "content"
 
-    if (true) {
+    if ((true)) {
         r.setWH(100, 100);
 
         canvas->saveLayerAlpha(&r, 0x80);
@@ -109,7 +103,7 @@
     void onDrawContent(SkCanvas* canvas) override {
         this->drawBG(canvas);
 
-        if (true) {
+        if ((true)) {
             SkRect r;
             r.setWH(220, 120);
             SkPaint p;
@@ -122,7 +116,7 @@
             return;
         }
 
-        if (false) {
+        if ((false)) {
             SkRect r;
             r.setWH(220, 120);
             SkPaint p;
@@ -141,7 +135,7 @@
             canvas->drawOval(r, p);
         }
 
-        if (false) {
+        if ((false)) {
             SkPaint p;
             p.setAlpha(0x88);
             p.setAntiAlias(true);
@@ -164,7 +158,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 DEF_SAMPLE( return new LayersView; )
 
@@ -191,7 +185,7 @@
     SkString name() override { return SkString("Backdrop"); }
 
     void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawImage(fImage.get(), 0, 0, nullptr);
+        canvas->drawImage(fImage.get(), 0, 0);
 
         const SkScalar w = 250;
         const SkScalar h = 150;
@@ -202,12 +196,12 @@
         m.postTranslate(fCenter.x(), fCenter.y());
         path.transform(m);
 
-        canvas->clipPath(path, kIntersect_SkClipOp, true);
+        canvas->clipPath(path, SkClipOp::kIntersect, true);
         const SkRect bounds = path.getBounds();
 
         SkPaint paint;
         paint.setAlpha(0xCC);
-        canvas->saveLayer({ &bounds, &paint, fFilter.get(), nullptr, nullptr, 0 });
+        canvas->saveLayer(SkCanvas::SaveLayerRec(&bounds, &paint, fFilter.get(), 0));
 
         canvas->restore();
     }
@@ -227,6 +221,6 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 DEF_SAMPLE( return new BackdropView; )
diff --git a/third_party/skia/samplecode/SampleLighting.cpp b/third_party/skia/samplecode/SampleLighting.cpp
deleted file mode 100644
index 24a0a64..0000000
--- a/third_party/skia/samplecode/SampleLighting.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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 "include/core/SkCanvas.h"
-#include "include/core/SkPoint3.h"
-#include "samplecode/DecodeFile.h"
-#include "samplecode/Sample.h"
-#include "src/core/SkNormalSource.h"
-#include "src/shaders/SkLightingShader.h"
-#include "tools/Resources.h"
-
-static sk_sp<SkLights> create_lights(SkScalar angle, SkScalar blue) {
-
-    const SkVector3 dir = SkVector3::Make(SkScalarSin(angle)*SkScalarSin(SK_ScalarPI*0.25f),
-                                          SkScalarCos(angle)*SkScalarSin(SK_ScalarPI*0.25f),
-                                          SkScalarCos(SK_ScalarPI*0.25f));
-
-    SkLights::Builder builder;
-
-    builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, blue), dir));
-    builder.setAmbientLightColor(SkColor3f::Make(0.1f, 0.1f, 0.1f));
-
-    return builder.finish();
-}
-
-////////////////////////////////////////////////////////////////////////////
-
-class LightingView : public Sample {
-public:
-    LightingView() : fLightAngle(0.0f), fColorFactor(0.0f) {}
-
-protected:
-    SkString name() override { return SkString("Lighting"); }
-
-    void onOnceBeforeDraw() override {
-        {
-            SkBitmap diffuseBitmap;
-            SkAssertResult(GetResourceAsBitmap("images/brickwork-texture.jpg", &diffuseBitmap));
-
-            fRect = SkRect::MakeIWH(diffuseBitmap.width(), diffuseBitmap.height());
-
-            fDiffuseShader = diffuseBitmap.makeShader();
-        }
-
-        {
-            SkBitmap normalBitmap;
-            SkAssertResult(GetResourceAsBitmap("images/brickwork_normal-map.jpg", &normalBitmap));
-
-            sk_sp<SkShader> normalMap = normalBitmap.makeShader();
-            fNormalSource = SkNormalSource::MakeFromNormalMap(std::move(normalMap), SkMatrix::I());
-        }
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        sk_sp<SkLights> lights(create_lights(fLightAngle, fColorFactor));
-
-        SkPaint paint;
-        paint.setShader(SkLightingShader::Make(fDiffuseShader,
-                                               fNormalSource,
-                                               std::move(lights)));
-        paint.setColor(SK_ColorBLACK);
-
-        canvas->drawRect(fRect, paint);
-    }
-
-    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
-        return this->INHERITED::onFindClickHandler(x, y, modi);
-    }
-
-    bool onAnimate(double nanos) override {
-        fLightAngle += 0.015f;
-        fColorFactor += 0.01f;
-        if (fColorFactor > 1.0f) {
-            fColorFactor = 0.0f;
-        }
-
-        return true;
-    }
-
-private:
-    SkRect                fRect;
-    sk_sp<SkShader>       fDiffuseShader;
-    sk_sp<SkNormalSource> fNormalSource;
-
-    SkScalar              fLightAngle;
-    SkScalar              fColorFactor;
-
-    typedef Sample INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE( return new LightingView(); )
diff --git a/third_party/skia/samplecode/SampleLitAtlas.cpp b/third_party/skia/samplecode/SampleLitAtlas.cpp
deleted file mode 100644
index d43e6d7..0000000
--- a/third_party/skia/samplecode/SampleLitAtlas.cpp
+++ /dev/null
@@ -1,488 +0,0 @@
-/*
- * 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 "include/core/SkCanvas.h"
-#include "include/core/SkDrawable.h"
-#include "include/core/SkRSXform.h"
-#include "include/utils/SkRandom.h"
-#include "samplecode/Sample.h"
-#include "src/core/SkNormalSource.h"
-#include "src/shaders/SkBitmapProcShader.h"
-#include "src/shaders/SkLightingShader.h"
-#include "src/shaders/SkLights.h"
-
-#include "tools/ToolUtils.h"
-
-// A crude normal mapped asteroids-like sample
-class DrawLitAtlasDrawable : public SkDrawable {
-public:
-    DrawLitAtlasDrawable(const SkRect& r)
-            : fBounds(r)
-            , fUseColors(false)
-            , fLightDir(SkVector3::Make(1.0f, 0.0f, 0.0f)) {
-        fAtlas = MakeAtlas();
-
-        SkRandom rand;
-        for (int i = 0; i < kNumAsteroids; ++i) {
-            fAsteroids[i].initAsteroid(&rand, fBounds, &fDiffTex[i], &fNormTex[i]);
-        }
-
-        fShip.initShip(fBounds, &fDiffTex[kNumAsteroids], &fNormTex[kNumAsteroids]);
-
-        this->updateLights();
-    }
-
-    void toggleUseColors() {
-        fUseColors = !fUseColors;
-    }
-
-    void rotateLight() {
-        SkScalar r = SK_ScalarPI / 6.0f,
-                 s = SkScalarSin(r),
-                 c = SkScalarCos(r);
-
-        SkScalar newX = c * fLightDir.fX - s * fLightDir.fY;
-        SkScalar newY = s * fLightDir.fX + c * fLightDir.fY;
-
-        fLightDir.set(newX, newY, 0.0f);
-
-        this->updateLights();
-    }
-
-    void left() {
-        SkScalar newRot = SkScalarMod(fShip.rot() + (2*SK_ScalarPI - SK_ScalarPI/32.0f),
-                                      2 * SK_ScalarPI);
-        fShip.setRot(newRot);
-    }
-
-    void right() {
-        SkScalar newRot = SkScalarMod(fShip.rot() + SK_ScalarPI/32.0f, 2 * SK_ScalarPI);
-        fShip.setRot(newRot);
-    }
-
-    void thrust() {
-        SkScalar s = SkScalarSin(fShip.rot()),
-                 c = SkScalarCos(fShip.rot());
-
-        SkVector newVel = fShip.velocity();
-        newVel.fX += s;
-        newVel.fY += -c;
-
-        SkScalar len = newVel.length();
-        if (len > kMaxShipSpeed) {
-            newVel.setLength(SkIntToScalar(kMaxShipSpeed));
-        }
-
-        fShip.setVelocity(newVel);
-    }
-
-protected:
-    void onDraw(SkCanvas* canvas) override {
-        SkRSXform xforms[kNumAsteroids+kNumShips];
-        SkColor colors[kNumAsteroids+kNumShips];
-
-        for (int i = 0; i < kNumAsteroids; ++i) {
-            fAsteroids[i].advance(fBounds);
-            xforms[i] = fAsteroids[i].asRSXform();
-            if (fUseColors) {
-                colors[i] = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
-            }
-        }
-
-        fShip.advance(fBounds);
-        xforms[kNumAsteroids] = fShip.asRSXform();
-        if (fUseColors) {
-            colors[kNumAsteroids] = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
-        }
-
-#ifdef SK_DEBUG
-        canvas->drawBitmap(fAtlas, 0, 0); // just to see the atlas
-
-        this->drawLightDir(canvas, fBounds.centerX(), fBounds.centerY());
-#endif
-
-#if 0
-        // TODO: revitalize when drawLitAtlas API lands
-        SkPaint paint;
-        paint.setFilterQuality(kLow_SkFilterQuality);
-
-        const SkRect cull = this->getBounds();
-        const SkColor* colorsPtr = fUseColors ? colors : NULL;
-
-        canvas->drawLitAtlas(fAtlas, xforms, fDiffTex, fNormTex, colorsPtr, kNumAsteroids+1,
-                             SkXfermode::kModulate_Mode, &cull, &paint, fLights);
-#else
-        SkMatrix diffMat, normalMat;
-
-        for (int i = 0; i < kNumAsteroids+1; ++i) {
-            colors[i] = colors[i] & 0xFF000000; // to silence compilers
-            SkPaint paint;
-
-            SkRect r = fDiffTex[i];
-            r.offsetTo(0, 0);
-
-            diffMat.setRectToRect(fDiffTex[i], r, SkMatrix::kFill_ScaleToFit);
-            normalMat.setRectToRect(fNormTex[i], r, SkMatrix::kFill_ScaleToFit);
-
-            SkMatrix m;
-            m.setRSXform(xforms[i]);
-
-            sk_sp<SkShader> normalMap = fAtlas.makeShader(&normalMat);
-            sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(
-                    std::move(normalMap), m);
-            sk_sp<SkShader> diffuseShader = fAtlas.makeShader(&diffMat);
-            paint.setShader(SkLightingShader::Make(std::move(diffuseShader),
-                    std::move(normalSource), fLights));
-
-            canvas->save();
-                canvas->setMatrix(m);
-                canvas->drawRect(r, paint);
-            canvas->restore();
-        }
-#endif
-
-#ifdef SK_DEBUG
-        {
-            SkPaint paint;
-            paint.setColor(SK_ColorRED);
-
-            for (int i = 0; i < kNumAsteroids; ++i) {
-                canvas->drawCircle(fAsteroids[i].pos().x(), fAsteroids[i].pos().y(), 2, paint);
-            }
-            canvas->drawCircle(fShip.pos().x(), fShip.pos().y(), 2, paint);
-
-            paint.setStyle(SkPaint::kStroke_Style);
-            canvas->drawRect(this->getBounds(), paint);
-        }
-#endif
-    }
-
-    SkRect onGetBounds() override {
-        return fBounds;
-    }
-
-private:
-
-    enum ObjType {
-        kBigAsteroid_ObjType = 0,
-        kMedAsteroid_ObjType,
-        kSmAsteroid_ObjType,
-        kShip_ObjType,
-
-        kLast_ObjType = kShip_ObjType
-    };
-
-    static const int kObjTypeCount = kLast_ObjType + 1;
-
-    void updateLights() {
-        SkLights::Builder builder;
-
-        builder.add(SkLights::Light::MakeDirectional(
-                SkColor3f::Make(1.0f, 1.0f, 1.0f), fLightDir));
-        builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
-
-        fLights = builder.finish();
-    }
-
-#ifdef SK_DEBUG
-    // Draw a vector to the light
-    void drawLightDir(SkCanvas* canvas, SkScalar centerX, SkScalar centerY) {
-        static const int kBgLen = 30;
-        static const int kSmLen = 5;
-
-        // TODO: change the lighting coordinate system to be right handed
-        SkPoint p1 = SkPoint::Make(centerX + kBgLen * fLightDir.fX,
-                                   centerY - kBgLen * fLightDir.fY);
-        SkPoint p2 = SkPoint::Make(centerX + (kBgLen-kSmLen) * fLightDir.fX,
-                                   centerY - (kBgLen-kSmLen) * fLightDir.fY);
-
-        SkPaint p;
-        canvas->drawLine(centerX, centerY, p1.fX, p1.fY, p);
-        canvas->drawLine(p1.fX, p1.fY,
-                         p2.fX - kSmLen * fLightDir.fY, p2.fY - kSmLen * fLightDir.fX, p);
-        canvas->drawLine(p1.fX, p1.fY,
-                         p2.fX + kSmLen * fLightDir.fY, p2.fY + kSmLen * fLightDir.fX, p);
-    }
-#endif
-
-    // Create the mixed diffuse & normal atlas
-    //
-    //    big color circle  |  big normal hemi
-    //    ------------------------------------
-    //    med color circle  |  med normal pyra
-    //    ------------------------------------
-    //    sm color circle   |   sm normal hemi
-    //    ------------------------------------
-    //    big ship          | big tetra normal
-    static SkBitmap MakeAtlas() {
-
-        SkBitmap atlas;
-        atlas.allocN32Pixels(kAtlasWidth, kAtlasHeight);
-
-        for (int y = 0; y < kAtlasHeight; ++y) {
-            int x = 0;
-            for ( ; x < kBigSize+kPad; ++x) {
-                *atlas.getAddr32(x, y) = SK_ColorTRANSPARENT;
-            }
-            for ( ; x < kAtlasWidth; ++x) {
-                *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0x88, 0x88, 0xFF);
-            }
-        }
-
-        // big asteroid
-        {
-            SkPoint bigCenter = SkPoint::Make(kDiffXOff + kBigSize/2.0f, kBigYOff + kBigSize/2.0f);
-
-            for (int y = kBigYOff; y < kBigYOff+kBigSize; ++y) {
-                for (int x = kDiffXOff; x < kDiffXOff+kBigSize; ++x) {
-                    SkScalar distSq = (x - bigCenter.fX) * (x - bigCenter.fX) +
-                                      (y - bigCenter.fY) * (y - bigCenter.fY);
-                    if (distSq > kBigSize*kBigSize/4.0f) {
-                        *atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
-                    } else {
-                        *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0xFF, 0, 0);
-                    }
-                }
-            }
-
-            ToolUtils::create_hemi_normal_map(
-                    &atlas, SkIRect::MakeXYWH(kNormXOff, kBigYOff, kBigSize, kBigSize));
-        }
-
-        // medium asteroid
-        {
-            for (int y = kMedYOff; y < kMedYOff+kMedSize; ++y) {
-                for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
-                    *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0);
-                }
-            }
-
-            ToolUtils::create_frustum_normal_map(
-                    &atlas, SkIRect::MakeXYWH(kNormXOff, kMedYOff, kMedSize, kMedSize));
-        }
-
-        // small asteroid
-        {
-            SkPoint smCenter = SkPoint::Make(kDiffXOff + kSmSize/2.0f, kSmYOff + kSmSize/2.0f);
-
-            for (int y = kSmYOff; y < kSmYOff+kSmSize; ++y) {
-                for (int x = kDiffXOff; x < kDiffXOff+kSmSize; ++x) {
-                    SkScalar distSq = (x - smCenter.fX) * (x - smCenter.fX) +
-                                      (y - smCenter.fY) * (y - smCenter.fY);
-                    if (distSq > kSmSize*kSmSize/4.0f) {
-                        *atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
-                    } else {
-                        *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0, 0xFF);
-                    }
-                }
-            }
-
-            ToolUtils::create_hemi_normal_map(
-                    &atlas, SkIRect::MakeXYWH(kNormXOff, kSmYOff, kSmSize, kSmSize));
-        }
-
-        // ship
-        {
-            SkScalar shipMidLine = kDiffXOff + kMedSize/2.0f;
-
-            for (int y = kShipYOff; y < kShipYOff+kMedSize; ++y) {
-                SkScalar scaledY = (y - kShipYOff)/(float)kMedSize; // 0..1
-
-                for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
-                    SkScalar scaledX;
-
-                    if (x < shipMidLine) {
-                        scaledX = 1.0f - (x - kDiffXOff)/(kMedSize/2.0f); // 0..1
-                    } else {
-                        scaledX = (x - shipMidLine)/(kMedSize/2.0f);      // 0..1
-                    }
-
-                    if (scaledX < scaledY) {
-                        *atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0xFF);
-                    } else {
-                        *atlas.getAddr32(x, y) = SkPackARGB32(0, 0, 0, 0);
-                    }
-                }
-            }
-
-            ToolUtils::create_tetra_normal_map(
-                    &atlas, SkIRect::MakeXYWH(kNormXOff, kShipYOff, kMedSize, kMedSize));
-        }
-
-        return atlas;
-    }
-
-    class ObjectRecord {
-    public:
-        void initAsteroid(SkRandom *rand, const SkRect& bounds,
-                          SkRect* diffTex, SkRect* normTex) {
-            static const SkScalar gMaxSpeeds[3] = { 1, 2, 5 }; // smaller asteroids can go faster
-            static const SkScalar gYOffs[3] = { kBigYOff, kMedYOff, kSmYOff };
-            static const SkScalar gSizes[3] = { kBigSize, kMedSize, kSmSize };
-
-            static unsigned int asteroidType = 0;
-            fObjType = static_cast<ObjType>(asteroidType++ % 3);
-
-            fPosition.set(bounds.fLeft + rand->nextUScalar1() * bounds.width(),
-                          bounds.fTop + rand->nextUScalar1() * bounds.height());
-            fVelocity.fX = rand->nextSScalar1();
-            fVelocity.fY = sqrt(1.0f - fVelocity.fX * fVelocity.fX);
-            SkASSERT(SkScalarNearlyEqual(fVelocity.length(), 1.0f));
-            fVelocity *= gMaxSpeeds[fObjType];
-            fRot = 0;
-            fDeltaRot = rand->nextSScalar1() / 32;
-
-            diffTex->setXYWH(SkIntToScalar(kDiffXOff), gYOffs[fObjType],
-                             gSizes[fObjType], gSizes[fObjType]);
-            normTex->setXYWH(SkIntToScalar(kNormXOff), gYOffs[fObjType],
-                             gSizes[fObjType], gSizes[fObjType]);
-        }
-
-        void initShip(const SkRect& bounds, SkRect* diffTex, SkRect* normTex) {
-            fObjType = kShip_ObjType;
-            fPosition.set(bounds.centerX(), bounds.centerY());
-            fVelocity = SkVector::Make(0.0f, 0.0f);
-            fRot = 0.0f;
-            fDeltaRot = 0.0f;
-
-            diffTex->setXYWH(SkIntToScalar(kDiffXOff), SkIntToScalar(kShipYOff),
-                             SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
-            normTex->setXYWH(SkIntToScalar(kNormXOff), SkIntToScalar(kShipYOff),
-                             SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
-        }
-
-        void advance(const SkRect& bounds) {
-            fPosition += fVelocity;
-            if (fPosition.fX > bounds.right()) {
-                SkASSERT(fVelocity.fX > 0);
-                fVelocity.fX = -fVelocity.fX;
-            } else if (fPosition.fX < bounds.left()) {
-                SkASSERT(fVelocity.fX < 0);
-                fVelocity.fX = -fVelocity.fX;
-            }
-            if (fPosition.fY > bounds.bottom()) {
-                if (fVelocity.fY > 0) {
-                    fVelocity.fY = -fVelocity.fY;
-                }
-            } else if (fPosition.fY < bounds.top()) {
-                if (fVelocity.fY < 0) {
-                    fVelocity.fY = -fVelocity.fY;
-                }
-            }
-
-            fRot += fDeltaRot;
-            fRot = SkScalarMod(fRot, 2 * SK_ScalarPI);
-        }
-
-        const SkPoint& pos() const { return fPosition; }
-
-        SkScalar rot() const { return fRot; }
-        void setRot(SkScalar rot) { fRot = rot; }
-
-        const SkPoint& velocity() const { return fVelocity; }
-        void setVelocity(const SkPoint& velocity) { fVelocity = velocity; }
-
-        SkRSXform asRSXform() const {
-            static const SkScalar gHalfSizes[kObjTypeCount] = {
-                SkScalarHalf(kBigSize),
-                SkScalarHalf(kMedSize),
-                SkScalarHalf(kSmSize),
-                SkScalarHalf(kMedSize),
-            };
-
-            return SkRSXform::MakeFromRadians(1.0f, fRot, fPosition.x(), fPosition.y(),
-                                              gHalfSizes[fObjType],
-                                              gHalfSizes[fObjType]);
-        }
-
-    private:
-        ObjType     fObjType;
-        SkPoint     fPosition;
-        SkVector    fVelocity;
-        SkScalar    fRot;        // In radians.
-        SkScalar    fDeltaRot;   // In radiands. Not used by ship.
-    };
-
-private:
-    static const int kNumLights = 2;
-    static const int kNumAsteroids = 6;
-    static const int kNumShips = 1;
-
-    static const int kBigSize = 128;
-    static const int kMedSize = 64;
-    static const int kSmSize = 32;
-    static const int kPad = 1;
-    static const int kAtlasWidth = kBigSize + kBigSize + 2 * kPad; // 2 pads in the middle
-    static const int kAtlasHeight = kBigSize + kMedSize + kSmSize + kMedSize + 3 * kPad;
-
-    static const int kDiffXOff = 0;
-    static const int kNormXOff = kBigSize + 2 * kPad;
-
-    static const int kBigYOff = 0;
-    static const int kMedYOff = kBigSize + kPad;
-    static const int kSmYOff = kMedYOff + kMedSize + kPad;
-    static const int kShipYOff = kSmYOff + kSmSize + kPad;
-    static const int kMaxShipSpeed = 5;
-
-    SkBitmap        fAtlas;
-    ObjectRecord    fAsteroids[kNumAsteroids];
-    ObjectRecord    fShip;
-    SkRect          fDiffTex[kNumAsteroids+kNumShips];
-    SkRect          fNormTex[kNumAsteroids+kNumShips];
-    SkRect          fBounds;
-    bool            fUseColors;
-    SkVector3       fLightDir;
-    sk_sp<SkLights> fLights;
-
-    typedef SkDrawable INHERITED;
-};
-
-class DrawLitAtlasView : public Sample {
-public:
-    DrawLitAtlasView() : fDrawable(new DrawLitAtlasDrawable(SkRect::MakeWH(640, 480))) {}
-
-protected:
-    SkString name() override { return SkString("DrawLitAtlas"); }
-
-    bool onChar(SkUnichar uni) override {
-            switch (uni) {
-                case 'C':
-                    fDrawable->toggleUseColors();
-                    return true;
-                case 'j':
-                    fDrawable->left();
-                    return true;
-                case 'k':
-                    fDrawable->thrust();
-                    return true;
-                case 'l':
-                    fDrawable->right();
-                    return true;
-                case 'o':
-                    fDrawable->rotateLight();
-                    return true;
-                default:
-                    break;
-            }
-            return false;
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawDrawable(fDrawable.get());
-    }
-
-    bool onAnimate(double nanos) override { return true; }
-
-private:
-    sk_sp<DrawLitAtlasDrawable> fDrawable;
-
-    typedef Sample INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE( return new DrawLitAtlasView(); )
diff --git a/third_party/skia/samplecode/SampleLua.cpp b/third_party/skia/samplecode/SampleLua.cpp
deleted file mode 100644
index 1a094bb..0000000
--- a/third_party/skia/samplecode/SampleLua.cpp
+++ /dev/null
@@ -1,162 +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 "include/core/SkCanvas.h"
-#include "include/core/SkData.h"
-#include "include/utils/SkLua.h"
-#include "samplecode/Sample.h"
-#include "tools/Resources.h"
-
-extern "C" {
-#include "lua.h"
-#include "lualib.h"
-#include "lauxlib.h"
-}
-
-//#define LUA_FILENAME    "lua/test.lua"
-#define LUA_FILENAME    "lua/slides.lua"
-
-static const char gDrawName[] = "onDrawContent";
-static const char gClickName[] = "onClickHandler";
-static const char gUnicharName[] = "onCharHandler";
-
-static const char gMissingCode[] = ""
-    "local paint = Sk.newPaint()"
-    "paint:setAntiAlias(true)"
-    "paint:setTextSize(30)"
-    ""
-    "function onDrawContent(canvas)"
-    "   canvas:drawText('missing \"test.lua\"', 20, 50, paint)"
-    "end"
-    ;
-
-class LuaView : public Sample {
-public:
-    LuaView() : fLua(nullptr) {}
-
-    ~LuaView() override { delete fLua; }
-
-    void setImageFilename(lua_State* L) {
-        SkString str = GetResourcePath("images/mandrill_256.png");
-
-        lua_getglobal(L, "setImageFilename");
-        if (lua_isfunction(L, -1)) {
-            fLua->pushString(str.c_str());
-            if (lua_pcall(L, 1, 0, 0) != LUA_OK) {
-                SkDebugf("lua err: %s\n", lua_tostring(L, -1));
-            }
-        }
-    }
-
-    lua_State* ensureLua() {
-        if (nullptr == fLua) {
-            fLua = new SkLua;
-
-            sk_sp<SkData> data = GetResourceAsData(LUA_FILENAME);
-            if (data) {
-                fLua->runCode(data->data(), data->size());
-                this->setImageFilename(fLua->get());
-            } else {
-                fLua->runCode(gMissingCode);
-            }
-        }
-        return fLua->get();
-    }
-
-protected:
-    SkString name() override { return SkString("Lua"); }
-
-    bool onChar(SkUnichar uni) override {
-            lua_State* L = this->ensureLua();
-            lua_getglobal(L, gUnicharName);
-            if (lua_isfunction(L, -1)) {
-                SkString str;
-                str.appendUnichar(uni);
-                fLua->pushString(str.c_str());
-                if (lua_pcall(L, 1, 1, 0) != LUA_OK) {
-                    SkDebugf("lua err: %s\n", lua_tostring(L, -1));
-                } else {
-                    if (lua_isboolean(L, -1) && lua_toboolean(L, -1)) {
-                        return true;
-                    }
-                }
-            }
-            return false;
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        lua_State* L = this->ensureLua();
-
-        lua_getglobal(L, gDrawName);
-        if (!lua_isfunction(L, -1)) {
-            int t = lua_type(L, -1);
-            SkDebugf("--- expected %s function %d, ignoring.\n", gDrawName, t);
-            lua_pop(L, 1);
-        } else {
-            // does it make sense to try to "cache" the lua version of this
-            // canvas between draws?
-            fLua->pushCanvas(canvas);
-            fLua->pushScalar(this->width());
-            fLua->pushScalar(this->height());
-            if (lua_pcall(L, 3, 1, 0) != LUA_OK) {
-                SkDebugf("lua err: %s\n", lua_tostring(L, -1));
-            }
-        }
-    }
-
-    virtual Sample::Click* onFindClickHandler(SkScalar x, SkScalar y,
-                                              skui::ModifierKey modi) override {
-        lua_State* L = this->ensureLua();
-        lua_getglobal(L, gClickName);
-        if (lua_isfunction(L, -1)) {
-            fLua->pushScalar(x);
-            fLua->pushScalar(y);
-            fLua->pushString("down");
-            if (lua_pcall(L, 3, 1, 0) != LUA_OK) {
-                SkDebugf("lua err: %s\n", lua_tostring(L, -1));
-            } else {
-                if (lua_isboolean(L, -1) && lua_toboolean(L, -1)) {
-                    return new Click();
-                }
-            }
-        }
-        return this->INHERITED::onFindClickHandler(x, y, modi);
-    }
-
-    bool onClick(Click* click) override {
-        const char* state = nullptr;
-        switch (click->fState) {
-            case skui::InputState::kMove:
-                state = "moved";
-                break;
-            case skui::InputState::kUp:
-                state = "up";
-                break;
-            default:
-                break;
-        }
-        if (state) {
-            lua_State* L = fLua->get();
-            lua_getglobal(L, gClickName);
-            fLua->pushScalar(click->fCurr.x());
-            fLua->pushScalar(click->fCurr.y());
-            fLua->pushString(state);
-            lua_pcall(L, 3, 1, 0);
-            return lua_isboolean(L, -1) && lua_toboolean(L, -1);
-        }
-        return true;
-    }
-
-private:
-    SkLua* fLua;
-
-    typedef Sample INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE( return new LuaView(); )
diff --git a/third_party/skia/samplecode/SampleManyRects.cpp b/third_party/skia/samplecode/SampleManyRects.cpp
index cfd66c9..ba3bdb1 100644
--- a/third_party/skia/samplecode/SampleManyRects.cpp
+++ b/third_party/skia/samplecode/SampleManyRects.cpp
@@ -38,7 +38,7 @@
 
             canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
             // Uncomment to test rotated rect draw combining.
-            if (false) {
+            if ((false)) {
                 SkMatrix rotate;
                 rotate.setRotate(fRandom.nextUScalar1() * 360,
                                  SkIntToScalar(x) + SkScalarHalf(rect.fRight),
@@ -59,7 +59,7 @@
 
 private:
     SkRandom fRandom;
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleMaterialShadows.cpp b/third_party/skia/samplecode/SampleMaterialShadows.cpp
new file mode 100644
index 0000000..8cb951f
--- /dev/null
+++ b/third_party/skia/samplecode/SampleMaterialShadows.cpp
@@ -0,0 +1,147 @@
+
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "include/core/SkCanvas.h"
+#include "include/core/SkColorFilter.h"
+#include "include/core/SkPath.h"
+#include "include/core/SkPoint3.h"
+#include "include/pathops/SkPathOps.h"
+#include "include/utils/SkCamera.h"
+#include "include/utils/SkShadowUtils.h"
+#include "samplecode/Sample.h"
+#include "src/core/SkBlurMask.h"
+#include "src/utils/SkUTF.h"
+#include "tools/ToolUtils.h"
+#include "tools/timer/TimeUtils.h"
+
+////////////////////////////////////////////////////////////////////////////
+
+class MaterialShadowsView : public Sample {
+    SkPath    fCirclePath;
+    SkPath    fCapsulePath;
+    SkPath    fLargeRRPath;
+    SkPath    fSmallRRPath;
+
+    SkPoint3  fLightPos;
+
+    void onOnceBeforeDraw() override {
+        fCirclePath.addCircle(0, 0, 56/2);
+        fCapsulePath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-64, -24, 128, 48), 24, 24));
+        fLargeRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-64, -64, 128, 128), 4, 4));
+        fSmallRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-40, -40, 80, 80), 4, 4));
+
+        fLightPos = SkPoint3::Make(0, -700, 700);
+    }
+
+    SkString name() override { return SkString("MaterialShadows"); }
+
+    void drawShadowedPath(SkCanvas* canvas, const SkPath& path,
+                          const SkPoint3& zPlaneParams,
+                          const SkPaint& paint, SkScalar ambientAlpha,
+                          const SkPoint3& lightPos, SkScalar lightRadius, SkScalar spotAlpha) {
+        uint32_t flags = 0;
+        flags |= SkShadowFlags::kDirectionalLight_ShadowFlag;
+
+        SkColor ambientColor = SkColorSetARGB(ambientAlpha * 255, 0, 0, 0);
+        SkColor spotColor = SkColorSetARGB(spotAlpha * 255, 0, 0, 0);
+        SkShadowUtils::DrawShadow(canvas, path, zPlaneParams, lightPos, lightRadius,
+                                  ambientColor, spotColor, flags);
+
+        canvas->drawPath(path, paint);
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        canvas->drawColor(0xFFFFFFFF);
+
+        const SkScalar kLightRadius = 1.1f;
+        const SkScalar kAmbientAlpha = 0.05f;
+        const SkScalar kSpotAlpha = 0.35f;
+
+        const SkScalar elevations[] = { 1, 3, 6, 8, 12, 24 };
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+
+        SkPoint3 lightPos = fLightPos;
+        SkPoint3 zPlaneParams = SkPoint3::Make(0, 0, 0);
+
+        paint.setColor(SK_ColorWHITE);
+        canvas->save();
+        canvas->translate(80, 80);
+        for (unsigned int i = 0; i < SK_ARRAY_COUNT(elevations); ++i) {
+            zPlaneParams.fZ = elevations[i];
+            this->drawShadowedPath(canvas, fCirclePath, zPlaneParams, paint, kAmbientAlpha,
+                                   lightPos, kLightRadius, kSpotAlpha);
+            canvas->translate(80, 0);
+        }
+        canvas->restore();
+
+        canvas->save();
+        canvas->translate(120, 175);
+        for (unsigned int i = 0; i < SK_ARRAY_COUNT(elevations); ++i) {
+            zPlaneParams.fZ = elevations[i];
+            this->drawShadowedPath(canvas, fCapsulePath, zPlaneParams, paint, kAmbientAlpha,
+                                   lightPos, kLightRadius, kSpotAlpha);
+            canvas->translate(160, 0);
+        }
+        canvas->restore();
+
+        canvas->save();
+        canvas->translate(120, 320);
+        for (unsigned int i = 0; i < SK_ARRAY_COUNT(elevations); ++i) {
+            zPlaneParams.fZ = elevations[i];
+            this->drawShadowedPath(canvas, fLargeRRPath, zPlaneParams, paint, kAmbientAlpha,
+                                   lightPos, kLightRadius, kSpotAlpha);
+            canvas->translate(160, 0);
+        }
+        canvas->restore();
+
+        canvas->save();
+        canvas->translate(100, 475);
+        for (unsigned int i = 0; i < SK_ARRAY_COUNT(elevations); ++i) {
+            zPlaneParams.fZ = elevations[i];
+            this->drawShadowedPath(canvas, fSmallRRPath, zPlaneParams, paint, kAmbientAlpha,
+                                   lightPos, kLightRadius, kSpotAlpha);
+            canvas->translate(160, 0);
+        }
+        canvas->restore();
+
+        canvas->save();
+        canvas->translate(100, 600);
+        for (unsigned int i = 0; i < SK_ARRAY_COUNT(elevations); ++i) {
+            canvas->save();
+            zPlaneParams.fZ = elevations[i];
+            canvas->rotate(10);
+            this->drawShadowedPath(canvas, fSmallRRPath, zPlaneParams, paint, kAmbientAlpha,
+                                   lightPos, kLightRadius, kSpotAlpha);
+            canvas->restore();
+            canvas->translate(160, 0);
+        }
+        canvas->restore();
+
+        canvas->save();
+        canvas->translate(100, 725);
+        for (unsigned int i = 0; i < SK_ARRAY_COUNT(elevations); ++i) {
+            canvas->save();
+            zPlaneParams.fZ = elevations[i];
+            canvas->rotate(45);
+            this->drawShadowedPath(canvas, fSmallRRPath, zPlaneParams, paint, kAmbientAlpha,
+                                   lightPos, kLightRadius, kSpotAlpha);
+            canvas->restore();
+            canvas->translate(160, 0);
+        }
+        canvas->restore();
+
+    }
+
+private:
+    using INHERITED = Sample;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_SAMPLE( return new MaterialShadowsView(); )
diff --git a/third_party/skia/samplecode/SampleMegaStroke.cpp b/third_party/skia/samplecode/SampleMegaStroke.cpp
index 33fa431..15579d9 100644
--- a/third_party/skia/samplecode/SampleMegaStroke.cpp
+++ b/third_party/skia/samplecode/SampleMegaStroke.cpp
@@ -76,7 +76,7 @@
     SkRect      fClip;
     int         fAngle;
     int         fPlusMinus;
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleMixer.cpp b/third_party/skia/samplecode/SampleMixer.cpp
index 7e39ab1..aa164b4 100644
--- a/third_party/skia/samplecode/SampleMixer.cpp
+++ b/third_party/skia/samplecode/SampleMixer.cpp
@@ -40,13 +40,14 @@
     void dodraw(SkCanvas* canvas, sk_sp<SkColorFilter> cf0, sk_sp<SkColorFilter> cf1, float gap) {
         SkPaint paint;
         paint.setColorFilter(cf0);
-        canvas->drawImage(fImg, 0, 0, &paint);
+        canvas->drawImage(fImg, 0, 0, SkSamplingOptions(), &paint);
 
         paint.setColorFilter(SkColorFilters::Lerp(fWeight, cf0, cf1));
-        canvas->drawImage(fImg, fImg->width() + gap * fWeight, 0, &paint);
+        canvas->drawImage(fImg, fImg->width() + gap * fWeight, 0,
+                          SkSamplingOptions(), &paint);
 
         paint.setColorFilter(cf1);
-        canvas->drawImage(fImg, 2*fImg->width() + gap, 0, &paint);
+        canvas->drawImage(fImg, 2*fImg->width() + gap, 0, SkSamplingOptions(), &paint);
     }
 
     void onDrawContent(SkCanvas* canvas) override {
@@ -71,7 +72,7 @@
         }
     }
 
-    virtual Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
+    Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
         return fRect.contains(SkScalarRoundToInt(x),
                               SkScalarRoundToInt(y)) ? new Click() : nullptr;
     }
@@ -85,94 +86,6 @@
 private:
     SkIRect fRect;
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 DEF_SAMPLE( return new MixerView; )
-
-//////////////////////////////////////////////////////////////////////////////
-
-#include "include/core/SkMaskFilter.h"
-#include "include/core/SkSurface.h"
-
-static sk_sp<SkShader> make_resource_shader(const char path[], int size) {
-    auto img = GetResourceAsImage(path);
-    if (!img) {
-        return nullptr;
-    }
-    SkRect src = SkRect::MakeIWH(img->width(), img->height());
-    SkRect dst = SkRect::MakeIWH(size, size);
-    SkMatrix m;
-    m.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
-    return img->makeShader(&m);
-}
-
-class ShaderMixerView : public Sample {
-    sk_sp<SkShader>     fSH0;
-    sk_sp<SkShader>     fSH1;
-    sk_sp<SkSurface>    fSurface;
-    SkBlendMode         fMode = SkBlendMode::kClear;
-
-    enum { SIZE = 256 };
-
-    const SkRect fRect = SkRect::MakeXYWH(10, 10 + SIZE + 10, SIZE, SIZE);
-
-public:
-    ShaderMixerView() {}
-
-    void onOnceBeforeDraw() override {
-        fSH0 = make_resource_shader("images/mandrill_256.png", SIZE);
-        fSH1 = make_resource_shader("images/baby_tux.png", SIZE);
-    }
-
-protected:
-    SkString name() override { return SkString("ShaderMixer"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        if (!fSurface) {
-            fSurface = canvas->makeSurface(SkImageInfo::MakeN32Premul(SIZE, SIZE));
-        }
-
-        SkPaint paint;
-        const SkRect r = SkRect::MakeIWH(SIZE, SIZE);
-
-        canvas->translate(10, 10);
-
-        canvas->save();
-        paint.setShader(fSH0); canvas->drawRect(r, paint);
-        canvas->translate(SIZE + 10.f, 0);
-        paint.setShader(fSH1); canvas->drawRect(r, paint);
-        canvas->restore();
-
-        canvas->translate(0, SIZE + 10.f);
-
-        auto sh = fSurface->makeImageSnapshot()->makeShader();
-
-        canvas->save();
-        paint.setShader(sh); canvas->drawRect(r, paint);
-        canvas->translate(SIZE + 10.f, 0);
-        paint.setShader(SkShaders::Lerp(sh, fSH0, fSH1)); canvas->drawRect(r, paint);
-        canvas->restore();
-    }
-
-    virtual Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
-        fMode = (fMode == SkBlendMode::kSrcOver) ? SkBlendMode::kClear : SkBlendMode::kSrcOver;
-        return fRect.contains(SkScalarRoundToInt(x),
-                              SkScalarRoundToInt(y)) ? new Click() : nullptr;
-    }
-
-    bool onClick(Click* click) override {
-        SkPaint p;
-        p.setAntiAlias(true);
-        p.setColor(SK_ColorRED);
-        p.setBlendMode(fMode);
-        p.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 12));
-        SkScalar x = click->fCurr.fX - fRect.fLeft;
-        SkScalar y = click->fCurr.fY - fRect.fTop;
-        fSurface->getCanvas()->drawCircle(x, y, 10, p);
-        return true;
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-DEF_SAMPLE( return new ShaderMixerView; )
diff --git a/third_party/skia/samplecode/SampleParagraph.cpp b/third_party/skia/samplecode/SampleParagraph.cpp
deleted file mode 100644
index cc10e03..0000000
--- a/third_party/skia/samplecode/SampleParagraph.cpp
+++ /dev/null
@@ -1,1720 +0,0 @@
-// Copyright 2019 Google LLC.
-#include "include/core/SkCanvas.h"
-#include "include/core/SkColorFilter.h"
-#include "include/core/SkColorPriv.h"
-#include "include/core/SkGraphics.h"
-#include "include/core/SkPath.h"
-#include "include/core/SkRegion.h"
-#include "include/core/SkShader.h"
-#include "include/core/SkStream.h"
-#include "include/core/SkTextBlob.h"
-#include "include/core/SkTime.h"
-#include "include/core/SkTypeface.h"
-#include "include/effects/SkBlurMaskFilter.h"
-#include "include/effects/SkGradientShader.h"
-#include "include/utils/SkRandom.h"
-#include "modules/skparagraph/include/Paragraph.h"
-#include "modules/skparagraph/include/TypefaceFontProvider.h"
-#include "modules/skparagraph/src/ParagraphBuilderImpl.h"
-#include "modules/skparagraph/src/ParagraphImpl.h"
-#include "modules/skparagraph/utils/TestFontCollection.h"
-#include "samplecode/Sample.h"
-#include "src/core/SkOSFile.h"
-#include "src/shaders/SkColorShader.h"
-#include "src/utils/SkOSPath.h"
-#include "src/utils/SkUTF.h"
-#include "tools/Resources.h"
-
-using namespace skia::textlayout;
-namespace {
-
-class ParagraphView_Base : public Sample {
-protected:
-    sk_sp<TestFontCollection> getFontCollection() {
-        // If we reset font collection we need to reset paragraph cache
-        static sk_sp<TestFontCollection> fFC = nullptr;
-        if (fFC == nullptr) {
-            fFC = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
-        }
-        return fFC;
-    }
-};
-
-sk_sp<SkShader> setgrad(const SkRect& r, SkColor c0, SkColor c1) {
-    SkColor colors[] = {c0, c1};
-    SkPoint pts[] = {{r.fLeft, r.fTop}, {r.fRight, r.fTop}};
-    return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);
-}
-/*
-void writeHtml(const char* name, Paragraph* paragraph) {
-        SkString tmpDir = skiatest::GetTmpDir();
-        if (!tmpDir.isEmpty()) {
-            SkString path = SkOSPath::Join(tmpDir.c_str(), name);
-            SkFILEWStream file(path.c_str());
-            file.write(nullptr, 0);
-        }
-}
-*/
-}  // namespace
-
-class ParagraphView1 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph1"); }
-
-    void drawTest(SkCanvas* canvas, SkScalar w, SkScalar h, SkColor fg, SkColor bg) {
-        const std::vector<
-            std::tuple<std::string, bool, bool, int, SkColor, SkColor, bool, TextDecorationStyle>>
-            gParagraph = {{"monospace", true, false, 14, SK_ColorWHITE, SK_ColorRED, true,
-                           TextDecorationStyle::kDashed},
-                          {"Assyrian", false, false, 20, SK_ColorWHITE, SK_ColorBLUE, false,
-                           TextDecorationStyle::kDotted},
-                          {"serif", true, true, 10, SK_ColorWHITE, SK_ColorRED, true,
-                           TextDecorationStyle::kDouble},
-                          {"Arial", false, true, 16, SK_ColorGRAY, SK_ColorGREEN, true,
-                           TextDecorationStyle::kSolid},
-                          {"sans-serif", false, false, 8, SK_ColorWHITE, SK_ColorRED, false,
-                           TextDecorationStyle::kWavy}};
-        SkAutoCanvasRestore acr(canvas, true);
-
-        canvas->clipRect(SkRect::MakeWH(w, h));
-        canvas->drawColor(SK_ColorWHITE);
-
-        SkScalar margin = 20;
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setColor(fg);
-
-        SkPaint blue;
-        blue.setColor(SK_ColorBLUE);
-
-        TextStyle defaultStyle;
-        defaultStyle.setBackgroundColor(blue);
-        defaultStyle.setForegroundColor(paint);
-        ParagraphStyle paraStyle;
-
-        auto fontCollection = sk_make_sp<FontCollection>();
-        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
-        for (auto i = 1; i < 5; ++i) {
-            defaultStyle.setFontSize(24 * i);
-            paraStyle.setTextStyle(defaultStyle);
-            ParagraphBuilderImpl builder(paraStyle, fontCollection);
-            std::string name = "Paragraph: " + std::to_string(24 * i);
-            builder.addText(name.c_str(), name.length());
-            for (auto para : gParagraph) {
-                TextStyle style;
-                style.setFontFamilies({SkString(std::get<0>(para).c_str())});
-                SkFontStyle fontStyle(std::get<1>(para) ? SkFontStyle::Weight::kBold_Weight
-                                                        : SkFontStyle::Weight::kNormal_Weight,
-                                      SkFontStyle::Width::kNormal_Width,
-                                      std::get<2>(para) ? SkFontStyle::Slant::kItalic_Slant
-                                                        : SkFontStyle::Slant::kUpright_Slant);
-                style.setFontStyle(fontStyle);
-                style.setFontSize(std::get<3>(para) * i);
-                SkPaint background;
-                background.setColor(std::get<4>(para));
-                style.setBackgroundColor(background);
-                SkPaint foreground;
-                foreground.setColor(std::get<5>(para));
-                foreground.setAntiAlias(true);
-                style.setForegroundColor(foreground);
-                if (std::get<6>(para)) {
-                    style.addShadow(TextShadow(SK_ColorBLACK, SkPoint::Make(5, 5), 2));
-                }
-
-                auto decoration = (i % 4);
-                if (decoration == 3) {
-                    decoration = 4;
-                }
-
-                bool test = (TextDecoration)decoration != TextDecoration::kNoDecoration;
-                std::string deco = std::to_string((int)decoration);
-                if (test) {
-                    style.setDecoration((TextDecoration)decoration);
-                    style.setDecorationStyle(std::get<7>(para));
-                    style.setDecorationColor(std::get<5>(para));
-                }
-                builder.pushStyle(style);
-                std::string name = " " + std::get<0>(para) + " " +
-                                   (std::get<1>(para) ? ", bold" : "") +
-                                   (std::get<2>(para) ? ", italic" : "") + " " +
-                                   std::to_string(std::get<3>(para) * i) +
-                                   (std::get<4>(para) != bg ? ", background" : "") +
-                                   (std::get<5>(para) != fg ? ", foreground" : "") +
-                                   (std::get<6>(para) ? ", shadow" : "") +
-                                   (test ? ", decorations " + deco : "") + ";";
-                builder.addText(name.c_str(), name.length());
-                builder.pop();
-            }
-
-            auto paragraph = builder.Build();
-            paragraph->layout(w - margin * 2);
-            paragraph->paint(canvas, margin, margin);
-
-            canvas->translate(0, paragraph->getHeight());
-        }
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        drawTest(canvas, this->width(), this->height(), SK_ColorRED, SK_ColorWHITE);
-    }
-
-private:
-
-    typedef Sample INHERITED;
-};
-
-class ParagraphView2 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph2"); }
-
-    void drawCode(SkCanvas* canvas, SkScalar w, SkScalar h) {
-        SkPaint comment;
-        comment.setColor(SK_ColorGRAY);
-        SkPaint constant;
-        constant.setColor(SK_ColorMAGENTA);
-        SkPaint null;
-        null.setColor(SK_ColorMAGENTA);
-        SkPaint literal;
-        literal.setColor(SK_ColorGREEN);
-        SkPaint code;
-        code.setColor(SK_ColorDKGRAY);
-        SkPaint number;
-        number.setColor(SK_ColorBLUE);
-        SkPaint name;
-        name.setColor(SK_ColorRED);
-
-        SkPaint white;
-        white.setColor(SK_ColorWHITE);
-
-        TextStyle defaultStyle;
-        defaultStyle.setBackgroundColor(white);
-        defaultStyle.setForegroundColor(code);
-        defaultStyle.setFontFamilies({SkString("monospace")});
-        defaultStyle.setFontSize(30);
-        ParagraphStyle paraStyle;
-        paraStyle.setTextStyle(defaultStyle);
-
-        auto fontCollection = sk_make_sp<FontCollection>();
-        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
-        ParagraphBuilderImpl builder(paraStyle, fontCollection);
-
-        const char* text1 = "RaisedButton";
-        const char* text2 = "(\n";
-        const char* text3 = "  child: ";
-        const char* text4 = "const";
-        const char* text5 = "Text";
-        const char* text6 = "'BUTTON TITLE'";
-        const char* text7 = "),\n";
-
-        builder.pushStyle(style(name));
-        builder.addText(text1, strlen(text1));
-        builder.pop();
-        builder.addText(text2, strlen(text2));
-        builder.addText(text3, strlen(text3));
-        builder.pushStyle(style(constant));
-        builder.addText(text4, strlen(text4));
-        builder.pop();
-        builder.addText(" ", 1);
-        builder.pushStyle(style(name));
-        builder.addText(text5, strlen(text5));
-        builder.pop();
-        builder.addText("(", 1);
-        builder.pushStyle(style(literal));
-        builder.addText(text6, strlen(text6));
-        builder.pop();
-        builder.addText(text7, strlen(text7));
-
-        auto paragraph = builder.Build();
-        paragraph->layout(w - 20);
-
-        paragraph->paint(canvas, 20, 20);
-    }
-
-    TextStyle style(SkPaint paint) {
-        TextStyle style;
-        paint.setAntiAlias(true);
-        style.setForegroundColor(paint);
-        style.setFontFamilies({SkString("monospace")});
-        style.setFontSize(30);
-
-        return style;
-    }
-
-    void drawText(SkCanvas* canvas, SkScalar w, SkScalar h, std::vector<const char*>& text,
-                  SkColor fg = SK_ColorDKGRAY, SkColor bg = SK_ColorWHITE,
-                  const char* ff = "sans-serif", SkScalar fs = 24,
-                  size_t lineLimit = 30,
-                  const std::u16string& ellipsis = u"\u2026") {
-        SkAutoCanvasRestore acr(canvas, true);
-
-        canvas->clipRect(SkRect::MakeWH(w, h));
-        canvas->drawColor(bg);
-
-        SkScalar margin = 20;
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setColor(fg);
-
-        SkPaint blue;
-        blue.setColor(SK_ColorBLUE);
-
-        SkPaint background;
-        background.setColor(bg);
-
-        TextStyle style;
-        style.setBackgroundColor(blue);
-        style.setForegroundColor(paint);
-        style.setFontFamilies({SkString(ff)});
-        style.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight,
-                                       SkFontStyle::kNormal_Width,
-                                       SkFontStyle::kUpright_Slant));
-        style.setFontSize(fs);
-        ParagraphStyle paraStyle;
-        paraStyle.setTextStyle(style);
-        paraStyle.setMaxLines(lineLimit);
-
-        paraStyle.setEllipsis(ellipsis);
-        TextStyle defaultStyle;
-        defaultStyle.setFontSize(20);
-        paraStyle.setTextStyle(defaultStyle);
-        ParagraphBuilderImpl builder(paraStyle, getFontCollection());
-
-        SkPaint foreground;
-        foreground.setColor(fg);
-        style.setForegroundColor(foreground);
-        style.setBackgroundColor(background);
-
-        for (auto& part : text) {
-            builder.pushStyle(style);
-            builder.addText(part, strlen(part));
-            builder.pop();
-        }
-
-        auto paragraph = builder.Build();
-        paragraph->layout(w - margin * 2);
-        paragraph->paint(canvas, margin, margin);
-
-        canvas->translate(0, paragraph->getHeight() + margin);
-    }
-
-    void drawLine(SkCanvas* canvas, SkScalar w, SkScalar h, const std::string& text,
-                  TextAlign align) {
-        SkAutoCanvasRestore acr(canvas, true);
-
-        canvas->clipRect(SkRect::MakeWH(w, h));
-        canvas->drawColor(SK_ColorWHITE);
-
-        SkScalar margin = 20;
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setColor(SK_ColorBLUE);
-
-        SkPaint gray;
-        gray.setColor(SK_ColorLTGRAY);
-
-        TextStyle style;
-        style.setBackgroundColor(gray);
-        style.setForegroundColor(paint);
-        style.setFontFamilies({SkString("Arial")});
-        style.setFontSize(30);
-        ParagraphStyle paraStyle;
-        paraStyle.setTextStyle(style);
-        paraStyle.setTextAlign(align);
-
-        auto fontCollection = sk_make_sp<FontCollection>();
-        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
-        ParagraphBuilderImpl builder(paraStyle, fontCollection);
-        builder.addText(text.c_str(), text.length());
-
-        auto paragraph = builder.Build();
-        paragraph->layout(w - margin * 2);
-        paragraph->layout(w - margin);
-        paragraph->paint(canvas, margin, margin);
-
-        canvas->translate(0, paragraph->getHeight() + margin);
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        std::vector<const char*> cupertino = {
-                "google_logogoogle_gsuper_g_logo 1 "
-                "google_logogoogle_gsuper_g_logo 12 "
-                "google_logogoogle_gsuper_g_logo 123 "
-                "google_logogoogle_gsuper_g_logo 1234 "
-                "google_logogoogle_gsuper_g_logo 12345 "
-                "google_logogoogle_gsuper_g_logo 123456 "
-                "google_logogoogle_gsuper_g_logo 1234567 "
-                "google_logogoogle_gsuper_g_logo 12345678 "
-                "google_logogoogle_gsuper_g_logo 123456789 "
-                "google_logogoogle_gsuper_g_logo 1234567890 "
-                "google_logogoogle_gsuper_g_logo 123456789 "
-                "google_logogoogle_gsuper_g_logo 12345678 "
-                "google_logogoogle_gsuper_g_logo 1234567 "
-                "google_logogoogle_gsuper_g_logo 123456 "
-                "google_logogoogle_gsuper_g_logo 12345 "
-                "google_logogoogle_gsuper_g_logo 1234 "
-                "google_logogoogle_gsuper_g_logo 123 "
-                "google_logogoogle_gsuper_g_logo 12 "
-                "google_logogoogle_gsuper_g_logo 1 "
-                "google_logogoogle_gsuper_g_logo "
-                "google_logogoogle_gsuper_g_logo "
-                "google_logogoogle_gsuper_g_logo "
-                "google_logogoogle_gsuper_g_logo "
-                "google_logogoogle_gsuper_g_logo "
-                "google_logogoogle_gsuper_g_logo"};
-        std::vector<const char*> text = {
-                "My neighbor came over to say,\n"
-                "Although not in a neighborly way,\n\n"
-                "That he'd knock me around,\n\n\n"
-                "If I didn't stop the sound,\n\n\n\n"
-                "Of the classical music I play."};
-
-        std::vector<const char*> long_word = {
-                "A_very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
-                "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
-                "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
-                "very_very_very_very_very_very_very_long_text"};
-
-        std::vector<const char*> very_long = {
-                "A very very very very very very very very very very very very very very very very "
-                "very very very very very very very very very very very very very very very very "
-                "very very very very very very very very very very very very very very very very "
-                "very very very very very very very long text"};
-
-        std::vector<const char*> very_word = {
-                "A very_very_very_very_very_very_very_very_very_very "
-                "very_very_very_very_very_very_very_very_very_very very very very very very very "
-                "very very very very very very very very very very very very very very very very "
-                "very very very very very very very very very very very very very long text"};
-
-        SkScalar width = this->width() / 5;
-        SkScalar height = this->height();
-        drawText(canvas, width, height, long_word, SK_ColorBLACK, SK_ColorWHITE, "Google Sans", 30);
-        canvas->translate(width, 0);
-        drawText(canvas, width, height, very_long, SK_ColorBLACK, SK_ColorWHITE, "Google Sans", 30);
-        canvas->translate(width, 0);
-        drawText(canvas, width, height, very_word, SK_ColorBLACK, SK_ColorWHITE, "Google Sans", 30);
-        canvas->translate(width, 0);
-        drawText(canvas, width, height / 2, text, SK_ColorBLACK, SK_ColorWHITE, "Roboto", 20, 100,
-                 u"\u2026");
-        canvas->translate(0, height / 2);
-        drawCode(canvas, width, height / 2);
-        canvas->translate(width, -height / 2);
-
-        drawText(canvas, width, height, cupertino, SK_ColorBLACK, SK_ColorWHITE, "Google Sans", 30);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView3 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph3"); }
-
-    void drawLine(SkCanvas* canvas, SkScalar w, SkScalar h, const std::string& text,
-                  TextAlign align, size_t lineLimit = std::numeric_limits<size_t>::max(),
-                  bool RTL = false, SkColor background = SK_ColorGRAY,
-                  const std::u16string& ellipsis = u"\u2026") {
-        SkAutoCanvasRestore acr(canvas, true);
-
-        canvas->clipRect(SkRect::MakeWH(w, h));
-        canvas->drawColor(SK_ColorWHITE);
-
-        SkScalar margin = 20;
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setColor(SK_ColorBLACK);
-
-        SkPaint gray;
-        gray.setColor(background);
-
-        SkPaint yellow;
-        yellow.setColor(SK_ColorYELLOW);
-
-        TextStyle style;
-        style.setBackgroundColor(gray);
-        style.setForegroundColor(paint);
-        style.setFontFamilies({SkString("sans-serif")});
-        style.setFontSize(30);
-        ParagraphStyle paraStyle;
-        paraStyle.setTextStyle(style);
-        paraStyle.setTextAlign(align);
-        paraStyle.setMaxLines(lineLimit);
-        paraStyle.setEllipsis(ellipsis);
-        // paraStyle.setTextDirection(RTL ? SkTextDirection::rtl : SkTextDirection::ltr);
-
-        auto fontCollection = sk_make_sp<FontCollection>();
-        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
-        ParagraphBuilderImpl builder(paraStyle, fontCollection);
-        if (RTL) {
-            builder.addText(mirror(text));
-        } else {
-            builder.addText(normal(text));
-        }
-
-        canvas->drawRect(SkRect::MakeXYWH(margin, margin, w - margin * 2, h - margin * 2), yellow);
-        auto paragraph = builder.Build();
-        paragraph->layout(w - margin * 2);
-        paragraph->paint(canvas, margin, margin);
-    }
-
-    std::u16string mirror(const std::string& text) {
-        std::u16string result;
-        result += u"\u202E";
-        // for (auto i = text.size(); i > 0; --i) {
-        //  result += text[i - 1];
-        //}
-
-        for (auto i = text.size(); i > 0; --i) {
-            auto ch = text[i - 1];
-            if (ch == ',') {
-                result += u"!";
-            } else if (ch == '.') {
-                result += u"!";
-            } else {
-                result += ch;
-            }
-        }
-
-        result += u"\u202C";
-        return result;
-    }
-
-    std::u16string normal(const std::string& text) {
-        std::u16string result;
-        result += u"\u202D";
-        for (auto ch : text) {
-            result += ch;
-        }
-        result += u"\u202C";
-        return result;
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        const std::string options =  // { "open-source open-source open-source open-source" };
-                {"Flutter is an open-source project to help developers "
-                 "build high-performance, high-fidelity, mobile apps for "
-                 "iOS and Android "
-                 "from a single codebase. This design lab is a playground "
-                 "and showcase of Flutter's many widgets, behaviors, "
-                 "animations, layouts, and more."};
-
-        canvas->drawColor(SK_ColorDKGRAY);
-        SkScalar width = this->width() / 4;
-        SkScalar height = this->height() / 2;
-
-        const std::string line =
-                "World domination is such an ugly phrase - I prefer to call it world optimisation";
-
-        drawLine(canvas, width, height, line, TextAlign::kLeft, 1, false, SK_ColorLTGRAY);
-        canvas->translate(width, 0);
-        drawLine(canvas, width, height, line, TextAlign::kRight, 2, false, SK_ColorLTGRAY);
-        canvas->translate(width, 0);
-        drawLine(canvas, width, height, line, TextAlign::kCenter, 3, false, SK_ColorLTGRAY);
-        canvas->translate(width, 0);
-        drawLine(canvas, width, height, line, TextAlign::kJustify, 4, false, SK_ColorLTGRAY);
-        canvas->translate(-width * 3, height);
-
-        drawLine(canvas, width, height, line, TextAlign::kLeft, 1, true, SK_ColorLTGRAY);
-        canvas->translate(width, 0);
-        drawLine(canvas, width, height, line, TextAlign::kRight, 2, true, SK_ColorLTGRAY);
-        canvas->translate(width, 0);
-        drawLine(canvas, width, height, line, TextAlign::kCenter, 3, true, SK_ColorLTGRAY);
-        canvas->translate(width, 0);
-        drawLine(canvas, width, height, line, TextAlign::kJustify, 4, true, SK_ColorLTGRAY);
-        canvas->translate(width, 0);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView4 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph4"); }
-
-    void drawFlutter(SkCanvas* canvas, SkScalar w, SkScalar h,
-                     const char* ff = "Google Sans", SkScalar fs = 30,
-                     size_t lineLimit = std::numeric_limits<size_t>::max(),
-                     const std::u16string& ellipsis = u"\u2026") {
-        SkAutoCanvasRestore acr(canvas, true);
-
-        canvas->clipRect(SkRect::MakeWH(w, h));
-
-        SkScalar margin = 20;
-
-        SkPaint black;
-        black.setAntiAlias(true);
-        black.setColor(SK_ColorBLACK);
-
-        SkPaint blue;
-        blue.setAntiAlias(true);
-        blue.setColor(SK_ColorBLUE);
-
-        SkPaint red;
-        red.setAntiAlias(true);
-        red.setColor(SK_ColorRED);
-
-        SkPaint green;
-        green.setAntiAlias(true);
-        green.setColor(SK_ColorGREEN);
-
-        SkPaint gray;
-        gray.setColor(SK_ColorLTGRAY);
-
-        SkPaint yellow;
-        yellow.setColor(SK_ColorYELLOW);
-
-        SkPaint magenta;
-        magenta.setAntiAlias(true);
-        magenta.setColor(SK_ColorMAGENTA);
-
-        TextStyle style;
-        style.setFontFamilies({SkString(ff)});
-        style.setFontSize(fs);
-
-        TextStyle style0;
-        style0.setForegroundColor(black);
-        style0.setBackgroundColor(gray);
-        style0.setFontFamilies({SkString(ff)});
-        style0.setFontSize(fs);
-        style0.setDecoration(TextDecoration::kUnderline);
-        style0.setDecorationStyle(TextDecorationStyle::kDouble);
-        style0.setDecorationColor(SK_ColorBLACK);
-
-        TextStyle style1;
-        style1.setForegroundColor(blue);
-        style1.setBackgroundColor(yellow);
-        style1.setFontFamilies({SkString(ff)});
-        style1.setFontSize(fs);
-        style1.setDecoration(TextDecoration::kOverline);
-        style1.setDecorationStyle(TextDecorationStyle::kWavy);
-        style1.setDecorationColor(SK_ColorBLACK);
-
-        TextStyle style2;
-        style2.setForegroundColor(red);
-        style2.setFontFamilies({SkString(ff)});
-        style2.setFontSize(fs);
-
-        TextStyle style3;
-        style3.setForegroundColor(green);
-        style3.setFontFamilies({SkString(ff)});
-        style3.setFontSize(fs);
-
-        TextStyle style4;
-        style4.setForegroundColor(magenta);
-        style4.setFontFamilies({SkString(ff)});
-        style4.setFontSize(fs);
-
-        ParagraphStyle paraStyle;
-        paraStyle.setTextStyle(style);
-        paraStyle.setMaxLines(lineLimit);
-
-        paraStyle.setEllipsis(ellipsis);
-
-        const char* logo1 = "google_";
-        const char* logo2 = "logo";
-        const char* logo3 = "go";
-        const char* logo4 = "ogle_logo";
-        const char* logo5 = "google_lo";
-        const char* logo6 = "go";
-        {
-            ParagraphBuilderImpl builder(paraStyle, getFontCollection());
-
-            builder.pushStyle(style0);
-            builder.addText(logo1, strlen(logo1));
-            builder.pop();
-            builder.pushStyle(style1);
-            builder.addText(logo2, strlen(logo2));
-            builder.pop();
-
-            builder.addText(" ", 1);
-
-            builder.pushStyle(style0);
-            builder.addText(logo3, strlen(logo3));
-            builder.pop();
-            builder.pushStyle(style1);
-            builder.addText(logo4, strlen(logo4));
-            builder.pop();
-
-            builder.addText(" ", 1);
-
-            builder.pushStyle(style0);
-            builder.addText(logo5, strlen(logo5));
-            builder.pop();
-            builder.pushStyle(style1);
-            builder.addText(logo6, strlen(logo6));
-            builder.pop();
-
-            auto paragraph = builder.Build();
-            paragraph->layout(w - margin * 2);
-            paragraph->paint(canvas, margin, margin);
-            canvas->translate(0, h + margin);
-        }
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-        SkScalar width = this->width();
-        SkScalar height = this->height();
-
-        drawFlutter(canvas, width, height / 2);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView5 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph5"); }
-
-    void bidi(SkCanvas* canvas, SkScalar w, SkScalar h, const std::u16string& text,
-              const std::u16string& expected, size_t lineLimit = std::numeric_limits<size_t>::max(),
-              const char* ff = "Roboto", SkScalar fs = 30,
-              const std::u16string& ellipsis = u"\u2026") {
-        SkAutoCanvasRestore acr(canvas, true);
-
-        canvas->clipRect(SkRect::MakeWH(w, h));
-
-        SkScalar margin = 20;
-
-        SkPaint black;
-        black.setColor(SK_ColorBLACK);
-        SkPaint gray;
-        gray.setColor(SK_ColorLTGRAY);
-
-        TextStyle style;
-        style.setForegroundColor(black);
-        style.setFontFamilies({SkString(ff)});
-        style.setFontSize(fs);
-
-        TextStyle style0;
-        style0.setForegroundColor(black);
-        style0.setFontFamilies({SkString(ff)});
-        style0.setFontSize(fs);
-        style0.setFontStyle(SkFontStyle(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width,
-                                        SkFontStyle::kItalic_Slant));
-
-        TextStyle style1;
-        style1.setForegroundColor(gray);
-        style1.setFontFamilies({SkString(ff)});
-        style1.setFontSize(fs);
-        style1.setFontStyle(SkFontStyle(SkFontStyle::kBold_Weight, SkFontStyle::kNormal_Width,
-                                        SkFontStyle::kUpright_Slant));
-
-        ParagraphStyle paraStyle;
-        paraStyle.setTextStyle(style);
-        paraStyle.setMaxLines(lineLimit);
-
-        paraStyle.setEllipsis(ellipsis);
-
-        ParagraphBuilderImpl builder(paraStyle, getFontCollection());
-
-        if (text.empty()) {
-            const std::u16string text0 = u"\u202Dabc";
-            const std::u16string text1 = u"\u202EFED";
-            const std::u16string text2 = u"\u202Dghi";
-            const std::u16string text3 = u"\u202ELKJ";
-            const std::u16string text4 = u"\u202Dmno";
-            builder.pushStyle(style0);
-            builder.addText(text0);
-            builder.pop();
-            builder.pushStyle(style1);
-            builder.addText(text1);
-            builder.pop();
-            builder.pushStyle(style0);
-            builder.addText(text2);
-            builder.pop();
-            builder.pushStyle(style1);
-            builder.addText(text3);
-            builder.pop();
-            builder.pushStyle(style0);
-            builder.addText(text4);
-            builder.pop();
-        } else {
-            // icu::UnicodeString unicode((UChar*) text.data(), SkToS32(text.size()));
-            // std::string str;
-            // unicode.toUTF8String(str);
-            // SkDebugf("Text: %s\n", str.c_str());
-            builder.addText(text + expected);
-        }
-
-        auto paragraph = builder.Build();
-        paragraph->layout(w - margin * 2);
-        paragraph->paint(canvas, margin, margin);
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-        SkScalar width = this->width();
-        SkScalar height = this->height() / 8;
-
-        const std::u16string text1 =
-                u"A \u202ENAC\u202Cner, exceedingly \u202ENAC\u202Cny,\n"
-                "One morning remarked to his granny:\n"
-                "A \u202ENAC\u202Cner \u202ENAC\u202C \u202ENAC\u202C,\n"
-                "Anything that he \u202ENAC\u202C,\n"
-                "But a \u202ENAC\u202Cner \u202ENAC\u202C't \u202ENAC\u202C a \u202ENAC\u202C, "
-                "\u202ENAC\u202C he?";
-        bidi(canvas, width, height * 3, text1, u"", 5);
-        canvas->translate(0, height * 3);
-
-        bidi(canvas, width, height, u"\u2067DETALOSI\u2069", u"");
-        canvas->translate(0, height);
-
-        bidi(canvas, width, height, u"\u202BDEDDEBME\u202C", u"");
-        canvas->translate(0, height);
-
-        bidi(canvas, width, height, u"\u202EEDIRREVO\u202C", u"");
-        canvas->translate(0, height);
-
-        bidi(canvas, width, height, u"\u200FTICILPMI\u200E", u"");
-        canvas->translate(0, height);
-
-        bidi(canvas, width, height, u"123 456 7890 \u202EZYXWV UTS RQP ONM LKJ IHG FED CBA\u202C.",
-             u"", 2);
-        canvas->translate(0, height);
-
-        // bidi(canvas, width, height, u"", u"");
-        // canvas->translate(0, height);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView6 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph6"); }
-
-    void hangingS(SkCanvas* canvas, SkScalar w, SkScalar h, SkScalar fs = 60.0) {
-        auto ff = "HangingS";
-
-        canvas->drawColor(SK_ColorLTGRAY);
-
-        SkPaint black;
-        black.setAntiAlias(true);
-        black.setColor(SK_ColorBLACK);
-
-        SkPaint blue;
-        blue.setAntiAlias(true);
-        blue.setColor(SK_ColorBLUE);
-
-        SkPaint red;
-        red.setAntiAlias(true);
-        red.setColor(SK_ColorRED);
-
-        SkPaint green;
-        green.setAntiAlias(true);
-        green.setColor(SK_ColorGREEN);
-
-        SkPaint gray;
-        gray.setColor(SK_ColorCYAN);
-
-        SkPaint yellow;
-        yellow.setColor(SK_ColorYELLOW);
-
-        SkPaint magenta;
-        magenta.setAntiAlias(true);
-        magenta.setColor(SK_ColorMAGENTA);
-
-        SkFontStyle fontStyle(SkFontStyle::kBold_Weight, SkFontStyle::kNormal_Width,
-                              SkFontStyle::kItalic_Slant);
-
-        TextStyle style;
-        style.setFontFamilies({SkString(ff)});
-        style.setFontSize(fs);
-        style.setFontStyle(fontStyle);
-
-        TextStyle style0;
-        style0.setForegroundColor(black);
-        style0.setBackgroundColor(gray);
-        style0.setFontFamilies({SkString(ff)});
-        style0.setFontSize(fs);
-        style0.setFontStyle(fontStyle);
-
-        TextStyle style1;
-        style1.setForegroundColor(blue);
-        style1.setBackgroundColor(yellow);
-        style1.setFontFamilies({SkString(ff)});
-        style1.setFontSize(fs);
-        style1.setFontStyle(fontStyle);
-
-        TextStyle style2;
-        style2.setForegroundColor(red);
-        style2.setFontFamilies({SkString(ff)});
-        style2.setFontSize(fs);
-        style2.setFontStyle(fontStyle);
-
-        TextStyle style3;
-        style3.setForegroundColor(green);
-        style3.setFontFamilies({SkString(ff)});
-        style3.setFontSize(fs);
-        style3.setFontStyle(fontStyle);
-
-        TextStyle style4;
-        style4.setForegroundColor(magenta);
-        style4.setFontFamilies({SkString(ff)});
-        style4.setFontSize(fs);
-        style4.setFontStyle(fontStyle);
-
-        ParagraphStyle paraStyle;
-        paraStyle.setTextStyle(style);
-
-        const char* logo1 = "S";
-        const char* logo2 = "kia";
-        const char* logo3 = "Sk";
-        const char* logo4 = "ia";
-        const char* logo5 = "Ski";
-        const char* logo6 = "a";
-        {
-            ParagraphBuilderImpl builder(paraStyle, getFontCollection());
-
-            builder.pushStyle(style0);
-            builder.addText(logo1, strlen(logo1));
-            builder.pop();
-            builder.pushStyle(style1);
-            builder.addText(logo2, strlen(logo2));
-            builder.pop();
-
-            builder.addText("   ", 3);
-
-            builder.pushStyle(style0);
-            builder.addText(logo3, strlen(logo3));
-            builder.pop();
-            builder.pushStyle(style1);
-            builder.addText(logo4, strlen(logo4));
-            builder.pop();
-
-            builder.addText("   ", 3);
-
-            builder.pushStyle(style0);
-            builder.addText(logo5, strlen(logo5));
-            builder.pop();
-            builder.pushStyle(style1);
-            builder.addText(logo6, strlen(logo6));
-            builder.pop();
-
-            auto paragraph = builder.Build();
-            paragraph->layout(w);
-            paragraph->paint(canvas, 40, 40);
-            canvas->translate(0, h);
-        }
-
-        const char* logo11 = "S";
-        const char* logo12 = "S";
-        const char* logo13 = "S";
-        const char* logo14 = "S";
-        const char* logo15 = "S";
-        const char* logo16 = "S";
-        {
-            ParagraphBuilderImpl builder(paraStyle, getFontCollection());
-
-            builder.pushStyle(style0);
-            builder.addText(logo11, strlen(logo1));
-            builder.pop();
-            builder.pushStyle(style1);
-            builder.addText(logo12, strlen(logo2));
-            builder.pop();
-
-            builder.addText("   ", 3);
-
-            builder.pushStyle(style0);
-            builder.addText(logo13, strlen(logo3));
-            builder.pop();
-            builder.pushStyle(style1);
-            builder.addText(logo14, strlen(logo4));
-            builder.pop();
-
-            builder.addText("   ", 3);
-
-            builder.pushStyle(style0);
-            builder.addText(logo15, strlen(logo5));
-            builder.pop();
-            builder.pushStyle(style1);
-            builder.addText(logo16, strlen(logo6));
-            builder.pop();
-
-            auto paragraph = builder.Build();
-            paragraph->layout(w);
-            paragraph->paint(canvas, 40, h);
-            canvas->translate(0, h);
-        }
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-        SkScalar width = this->width();
-        SkScalar height = this->height() / 4;
-
-        hangingS(canvas, width, height);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView7 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph7"); }
-
-    void drawText(SkCanvas* canvas, SkColor background, SkScalar letterSpace, SkScalar w,
-                  SkScalar h) {
-        SkAutoCanvasRestore acr(canvas, true);
-        canvas->clipRect(SkRect::MakeWH(w, h));
-        canvas->drawColor(background);
-
-        const char* line =
-                "World domination is such an ugly phrase - I prefer to call it world optimisation.";
-
-        ParagraphStyle paragraphStyle;
-        paragraphStyle.setTextAlign(TextAlign::kLeft);
-        paragraphStyle.setMaxLines(10);
-        paragraphStyle.turnHintingOff();
-        TextStyle textStyle;
-        textStyle.setFontFamilies({SkString("Roboto")});
-        textStyle.setFontSize(30);
-        textStyle.setLetterSpacing(letterSpace);
-        textStyle.setColor(SK_ColorBLACK);
-        textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
-                                           SkFontStyle::kUpright_Slant));
-
-        ParagraphBuilderImpl builder(paragraphStyle, getFontCollection());
-        builder.pushStyle(textStyle);
-        builder.addText(line, strlen(line));
-        builder.pop();
-
-        auto paragraph = builder.Build();
-        paragraph->layout(w - 20);
-        paragraph->paint(canvas, 10, 10);
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-
-        auto h = this->height() / 4;
-        auto w = this->width() / 2;
-
-        drawText(canvas, SK_ColorGRAY, 1, w, h);
-        canvas->translate(0, h);
-
-        drawText(canvas, SK_ColorLTGRAY, 2, w, h);
-        canvas->translate(0, h);
-
-        drawText(canvas, SK_ColorCYAN, 3, w, h);
-        canvas->translate(0, h);
-
-        drawText(canvas, SK_ColorGRAY, 4, w, h);
-        canvas->translate(w, -3 * h);
-
-        drawText(canvas, SK_ColorYELLOW, 5, w, h);
-        canvas->translate(0, h);
-
-        drawText(canvas, SK_ColorGREEN, 10, w, h);
-        canvas->translate(0, h);
-
-        drawText(canvas, SK_ColorRED, 15, w, h);
-        canvas->translate(0, h);
-
-        drawText(canvas, SK_ColorBLUE, 20, w, h);
-        canvas->translate(0, h);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView8 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph8"); }
-
-    void drawText(SkCanvas* canvas, SkColor background, SkScalar wordSpace, SkScalar w,
-                  SkScalar h) {
-        SkAutoCanvasRestore acr(canvas, true);
-        canvas->clipRect(SkRect::MakeWH(w, h));
-        canvas->drawColor(background);
-
-        const char* line =
-                "World domination is such an ugly phrase - I prefer to call it world optimisation.";
-
-        ParagraphStyle paragraphStyle;
-        paragraphStyle.setTextAlign(TextAlign::kLeft);
-        paragraphStyle.setMaxLines(10);
-        paragraphStyle.turnHintingOff();
-        TextStyle textStyle;
-        textStyle.setFontFamilies({SkString("Roboto")});
-        textStyle.setFontSize(30);
-        textStyle.setWordSpacing(wordSpace);
-        textStyle.setColor(SK_ColorBLACK);
-        textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
-                                           SkFontStyle::kUpright_Slant));
-
-        ParagraphBuilderImpl builder(paragraphStyle, getFontCollection());
-        builder.pushStyle(textStyle);
-        builder.addText(line, strlen(line));
-        builder.pop();
-
-        auto paragraph = builder.Build();
-        paragraph->layout(w - 20);
-        paragraph->paint(canvas, 10, 10);
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-
-        auto h = this->height() / 4;
-        auto w = this->width() / 2;
-
-        drawText(canvas, SK_ColorGRAY, 1, w, h);
-        canvas->translate(0, h);
-
-        drawText(canvas, SK_ColorLTGRAY, 2, w, h);
-        canvas->translate(0, h);
-
-        drawText(canvas, SK_ColorCYAN, 3, w, h);
-        canvas->translate(0, h);
-
-        drawText(canvas, SK_ColorGRAY, 4, w, h);
-        canvas->translate(w, -3 * h);
-
-        drawText(canvas, SK_ColorYELLOW, 5, w, h);
-        canvas->translate(0, h);
-
-        drawText(canvas, SK_ColorGREEN, 10, w, h);
-        canvas->translate(0, h);
-
-        drawText(canvas, SK_ColorRED, 15, w, h);
-        canvas->translate(0, h);
-
-        drawText(canvas, SK_ColorBLUE, 20, w, h);
-        canvas->translate(0, h);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView9 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph9"); }
-
-    bool onChar(SkUnichar uni) override {
-            switch (uni) {
-                case 'w':
-                    ++wordSpacing;
-                    return true;
-                case 'q':
-                    if (wordSpacing > 0) --wordSpacing;
-                    return true;
-                case 'l':
-                    ++letterSpacing;
-                    return true;
-                case 'k':
-                    if (letterSpacing > 0) --letterSpacing;
-                    return true;
-                default:
-                    break;
-            }
-            return false;
-    }
-
-    void drawText(SkCanvas* canvas, SkColor background, SkScalar w, SkScalar h) {
-        SkAutoCanvasRestore acr(canvas, true);
-        canvas->clipRect(SkRect::MakeWH(w, h));
-        canvas->drawColor(background);
-
-        auto fontCollection = sk_make_sp<FontCollection>();
-        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
-        fontCollection->enableFontFallback();
-
-        const char* text =
-                "( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
-                " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
-                " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)";
-        auto multiplier = 5.67;
-        ParagraphStyle paragraphStyle;
-        paragraphStyle.setTextAlign(TextAlign::kLeft);
-        paragraphStyle.setMaxLines(10);
-        paragraphStyle.turnHintingOff();
-        TextStyle textStyle;
-        textStyle.setFontFamilies({SkString("Roboto")});
-        textStyle.setFontSize(5 * multiplier);
-        textStyle.setHeight(1.3f);
-        textStyle.setColor(SK_ColorBLACK);
-        textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
-                                           SkFontStyle::kUpright_Slant));
-
-        ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
-        builder.pushStyle(textStyle);
-        builder.addText(text, strlen(text));
-        builder.pop();
-
-        auto paragraph = builder.Build();
-        paragraph->layout(200 * multiplier);
-
-        std::vector<size_t> sizes = {0, 1, 2, 8, 19, 21, 22, 30, 150};
-
-        std::vector<size_t> colors = {SK_ColorBLUE, SK_ColorCYAN,  SK_ColorLTGRAY, SK_ColorGREEN,
-                                      SK_ColorRED,  SK_ColorWHITE, SK_ColorYELLOW, SK_ColorMAGENTA};
-
-        RectHeightStyle rect_height_style = RectHeightStyle::kTight;
-        RectWidthStyle rect_width_style = RectWidthStyle::kTight;
-
-        for (size_t i = 0; i < sizes.size() - 1; ++i) {
-            size_t from = sizes[i];
-            size_t to = sizes[i + 1];
-            auto boxes = paragraph->getRectsForRange(from, to, rect_height_style, rect_width_style);
-            if (boxes.empty()) {
-                continue;
-            }
-            for (auto& box : boxes) {
-                SkPaint paint;
-                paint.setColor(colors[i % colors.size()]);
-                paint.setShader(setgrad(box.rect, colors[i % colors.size()], SK_ColorWHITE));
-                canvas->drawRect(box.rect, paint);
-            }
-        }
-
-        paragraph->paint(canvas, 0, 0);
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-
-        auto h = this->height();
-        auto w = this->width();
-
-        drawText(canvas, SK_ColorGRAY, w, h);
-    }
-
-private:
-    typedef Sample INHERITED;
-    SkScalar letterSpacing;
-    SkScalar wordSpacing;
-};
-
-class ParagraphView10 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph10"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-        auto multiplier = 5.67;
-        const char* text = "English English 字典 字典 😀😃😄 😀😃😄";
-
-        auto fontCollection = sk_make_sp<FontCollection>();
-        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
-        fontCollection->enableFontFallback();
-
-        ParagraphStyle paragraph_style;
-        paragraph_style.turnHintingOff();
-        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
-
-        TextStyle text_style;
-        text_style.setFontFamilies({SkString("Roboto"),
-                                    SkString("Noto Color Emoji"),
-                                    SkString("Noto Serif CJK JP")});
-        text_style.setFontSize(10 * multiplier);
-        text_style.setLetterSpacing(0);
-        text_style.setWordSpacing(0);
-        text_style.setColor(SK_ColorBLACK);
-        text_style.setHeight(1);
-        builder.pushStyle(text_style);
-        builder.addText(text, strlen(text));
-        builder.pop();
-
-        auto paragraph = builder.Build();
-        paragraph->layout(width());
-
-        paragraph->paint(canvas, 0, 0);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView11 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph11"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-
-        auto text = "\U0001f469\U0000200D\U0001f469\U0000200D\U0001f466\U0001f469\U0000200D\U0001f469\U0000200D\U0001f467\U0000200D\U0001f467\U0001f1fa\U0001f1f8";
-
-        TextStyle text_style;
-        text_style.setFontFamilies({SkString("Ahem")});
-        text_style.setColor(SK_ColorBLACK);
-        text_style.setFontSize(60);
-        text_style.setLetterSpacing(0);
-        text_style.setWordSpacing(0);
-        ParagraphStyle paragraph_style;
-        paragraph_style.setTextStyle(text_style);
-
-        auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), true, true);
-        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
-        builder.addText(text, strlen(text));
-        auto paragraph = builder.Build();
-        paragraph->layout(1000);
-        paragraph->paint(canvas, 0, 0);
-
-        struct pair {
-            unsigned fX;
-            unsigned fY;
-        };
-
-        pair hit1[] =
-              {{ 0, 8},{1, 33}, {2, 34}, { 3, 19}, {4, 20},
-               { 5, 21}, { 6, 22 }, { 7, 23 }, {8, 24 }, { 9, 25},
-               { 10, 26}, { 11, 27}, {12, 28}, { 13, 21}, {14, 22 },
-               { 15, 23}, {16, 24}, {17, 21}, { 18, 22}, {19, 21},
-               { 20, 24}, { 21, 23}, };
-
-        pair miss[] =
-              {{ 0, 4},{1, 17}, {2, 18}, { 3, 11}, {4, 12},
-               { 5, 13}, { 6, 14 }, { 7, 15 }, {8, 16 }, { 9, 17},
-               { 10, 18}, { 11, 19}, {12, 20}, { 13, 17}, {14, 18 },
-               { 15, 19}, {16, 20}, {17, 19}, { 18, 20},
-               { 20, 22}, };
-
-        auto rects = paragraph->getRectsForRange(7, 9, RectHeightStyle::kTight, RectWidthStyle::kTight);
-        SkPaint paint;
-        paint.setColor(SK_ColorRED);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setAntiAlias(true);
-        paint.setStrokeWidth(1);
-        if (!rects.empty()) {
-            canvas->drawRect(rects[0].rect, paint);
-        }
-
-        for (auto& query : hit1) {
-            auto rects = paragraph->getRectsForRange(query.fX, query.fY, RectHeightStyle::kTight, RectWidthStyle::kTight);
-            if (rects.size() >= 1 && rects[0].rect.width() > 0) {
-            } else {
-                SkDebugf("+[%d:%d): Bad\n", query.fX, query.fY);
-            }
-        }
-
-        for (auto& query : miss) {
-            auto miss = paragraph->getRectsForRange(query.fX, query.fY, RectHeightStyle::kTight, RectWidthStyle::kTight);
-            if (miss.empty()) {
-            } else {
-                SkDebugf("-[%d:%d): Bad\n", query.fX, query.fY);
-            }
-        }
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView12 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph12"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-
-        const char* text = "Atwater Peel Sherbrooke Bonaventure Angrignon Peel Côte-des-Neiges";
-        TextStyle text_style;
-        text_style.setFontFamilies({SkString("Ahem")});
-        text_style.setColor(SK_ColorBLACK);
-        text_style.setFontSize(16);
-        //text_style.setLetterSpacing(-0.41);
-        StrutStyle strut_style;
-        strut_style.setStrutEnabled(false);
-        ParagraphStyle paragraph_style;
-        paragraph_style.setStrutStyle(strut_style);
-        paragraph_style.setTextStyle(text_style);
-        ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
-        builder.addText(text);
-        auto paragraph = builder.Build();
-        paragraph->layout(1095.000000);
-        auto result = paragraph->getRectsForRange(65, 66, RectHeightStyle::kTight, RectWidthStyle::kTight);
-        paragraph->paint(canvas, 0, 0);
-
-        SkPaint paint;
-        paint.setColor(SK_ColorRED);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setAntiAlias(true);
-        paint.setStrokeWidth(1);
-        canvas->drawRect(result.front().rect, paint);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView14 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph14"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-        TextStyle text_style;
-        text_style.setFontFamilies({SkString("Ahem")});
-        text_style.setColor(SK_ColorBLACK);
-        text_style.setFontSize(25);
-        text_style.setDecoration((TextDecoration)(TextDecoration::kUnderline | TextDecoration::kOverline | TextDecoration::kLineThrough));
-        text_style.setDecorationColor(SK_ColorBLUE);
-        text_style.setDecorationStyle(TextDecorationStyle::kWavy);
-        text_style.setDecorationThicknessMultiplier(4.0f);
-        ParagraphStyle paragraph_style;
-        paragraph_style.setTextStyle(text_style);
-        paragraph_style.setTextDirection(TextDirection::kRtl);
-        ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
-        builder.pushStyle(text_style);
-        builder.addText("Hello, wor!\nabcd.");
-        auto paragraph = builder.Build();
-        paragraph->layout(300);
-        paragraph->paint(canvas, 0, 0);
-        SkPaint paint;
-        paint.setColor(SK_ColorRED);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setAntiAlias(true);
-        paint.setStrokeWidth(1);
-        canvas->drawRect(SkRect::MakeXYWH(0, 0, 300, 100), paint);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView15 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph15"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-
-        TextStyle text_style;
-        text_style.setFontFamilies({SkString("abc.ttf")});
-        text_style.setFontSize(50);
-
-        auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false);
-
-        fontCollection->addFontFromFile("abc/abc.ttf", "abc");
-        fontCollection->addFontFromFile("abc/abc+grave.ttf", "abc+grave");
-        fontCollection->addFontFromFile("abc/abc+agrave.ttf", "abc+agrave");
-
-        ParagraphStyle paragraph_style;
-        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
-
-        text_style.setFontFamilies({SkString("abc"), SkString("abc+grave")});
-        text_style.setColor(SK_ColorBLUE);
-        builder.pushStyle(text_style);
-        builder.addText(u"a\u0300");
-        text_style.setColor(SK_ColorMAGENTA);
-        builder.pushStyle(text_style);
-        builder.addText(u"à");
-
-        text_style.setFontFamilies({SkString("abc"), SkString("abc+agrave")});
-
-        text_style.setColor(SK_ColorRED);
-        builder.pushStyle(text_style);
-        builder.addText(u"a\u0300");
-        text_style.setColor(SK_ColorGREEN);
-        builder.pushStyle(text_style);
-        builder.addText(u"à");
-
-        auto paragraph = builder.Build();
-        paragraph->layout(800);
-        paragraph->paint(canvas, 50, 50);
-
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView16 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph16"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-
-        const char* text = "content";
-
-        ParagraphStyle paragraph_style;
-        paragraph_style.setMaxLines(1);
-        paragraph_style.setEllipsis(u"\u2026");
-        //auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
-        auto fontCollection = sk_make_sp<FontCollection>();
-        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
-        fontCollection->enableFontFallback();
-        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
-
-        TextStyle text_style;
-        text_style.setFontFamilies({SkString(".SF Pro Text")});
-        text_style.setColor(SK_ColorBLACK);
-        text_style.setFontSize(17.0f * 99.0f);
-        text_style.setLetterSpacing(0.41f);
-        builder.pushStyle(text_style);
-        builder.addText(text);
-
-        auto paragraph = builder.Build();
-        paragraph->layout(800);
-        paragraph->paint(canvas, 0, 0);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView17 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph17"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-
-        auto fontCollection = sk_make_sp<FontCollection>();
-        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
-        fontCollection->enableFontFallback();
-        auto navy = SkColorSetRGB(0, 0, 139);
-        auto ltgray = SkColorSetRGB(211, 211, 211);
-        auto multiplier = 5.67;
-
-        const char* text = ">Sͬ͑̀͐̈͒̈́̋̎ͮͩ̽̓ͬ̂̆̔͗́̓ͣͧ͊ͫ͛̉͌̐̑ͪ͗̚͝҉̴͉͢k̡̊̓ͫͭͩ͂͊ͨͪͬ̑ͫ̍̌̄͛̌̂̑̂̋̊̔ͫ͛̽̑ͨ̍ͭ̓̀ͪͪ̉͐͗̌̓̃̚͟͝҉̢͏̫̞̙͇͖̮͕̗̟͕͇͚̻͈̣̻̪͉̰̲̣̫ͅͅP̴̅̍͒̿͗͗̇ͩ̃͆͌̀̽͏̧̡͕͖̝̖̼̺̰̣̬͔͖͔̼͙̞̦̫͓̘͜a̸̴̸̴̢̢̨̨̫͍͓̥̼̭̼̻̤̯̙̤̻̠͚̍̌͋̂ͦͨ̽̇͌͌͆̀̽̎͒̄ͪ̐ͦ̈ͫ͐͗̓̚̚͜ͅr͐͐ͤͫ̐ͥ͂̈́̿́ͮ̃͗̓̏ͫ̀̿͏̸̵̧́͘̕͟͝͠͞͠҉̷̧͚͢͟a̓̽̎̄͗̔͛̄̐͊͛ͫ͂͌̂̂̈̈̓̔̅̅̄͊̉́ͪ̑̄͆ͬ̍͆ͭ͋̐ͬ͏̷̵̨̢̩̹̖͓̥̳̰͔̱̬͖̙͓̙͇̀̀̕͜͟͟͢͟͜͠͡g̨̅̇ͦ͋̂ͦͨͭ̓͐͆̏̂͛̉ͧ̑ͫ̐̒͛ͫ̍̒͛́̚҉̷̨̛̛̀͜͢͞҉̩̘̲͍͎̯̹̝̭̗̱͇͉̲̱͔̯̠̹̥̻͉̲̜̤̰̪̗̺̖̺r̷͌̓̇̅ͭ̀̐̃̃ͭ͑͗̉̈̇̈́ͥ̓ͣ́ͤ͂ͤ͂̏͌̆̚҉̴̸̧̢̢̛̫͉̦̥̤̙͈͉͈͉͓̙̗̟̳̜͈̗̺̟̠̠͖͓̖̪͕̠̕̕͝ͅả̸̴̡̡̧͠͞͡͞҉̛̕͟͏̷̘̪̱͈̲͉̞̠̞̪̫͎̲̬̖̀̀͟͝͞͞͠p̛͂̈͐̚͠҉̵̸̡̢̢̩̹͙̯͖̙̙̮̥̙͚̠͔̥̭̮̞̣̪̬̥̠̖̝̥̪͎́̀̕͜͡͡ͅͅh̵̷̵̡̛ͤ̂͌̐̓̐̋̋͊̒̆̽́̀̀̀͢͠͞͞҉̷̸̢̕҉͚̯͖̫̜̞̟̠̱͉̝̲̹̼͉̟͉̩̮͔̤͖̞̭̙̹̬ͅ<";
-
-        ParagraphStyle paragraph_style;
-        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
-        SkPaint paint;
-        paint.setColor(ltgray);
-        TextStyle text_style;
-        text_style.setBackgroundColor(paint);
-        text_style.setColor(navy);
-        text_style.setFontFamilies({SkString("Roboto")});
-        text_style.setFontSize(20 * multiplier);
-        builder.pushStyle(text_style);
-        builder.addText(text);
-        auto paragraph = builder.Build();
-        paragraph->layout(10000);
-        paragraph->paint(canvas, 0, 0);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class Zalgo {
-    private:
-    std::u16string COMBINING_DOWN = u"\u0316\u0317\u0318\u0319\u031c\u031d\u031e\u031f\u0320\u0324\u0325\u0326\u0329\u032a\u032b\u032c\u032d\u032e\u032f\u0330\u0331\u0332\u0333\u0339\u033a\u033b\u033c\u0345\u0347\u0348\u0349\u034d\u034e\u0353\u0354\u0355\u0356\u0359\u035a\u0323";
-    std::u16string COMBINING_UP = u"\u030d\u030e\u0304\u0305\u033f\u0311\u0306\u0310\u0352\u0357\u0351\u0307\u0308\u030a\u0342\u0343\u0344\u034a\u034b\u034c\u0303\u0302\u030c\u0350\u0300\u0301\u030b\u030f\u0312\u0313\u0314\u033d\u0309\u0363\u0364\u0365\u0366\u0367\u0368\u0369\u036a\u036b\u036c\u036d\u036e\u035b\u0346\u031a";
-    std::u16string COMBINING_MIDDLE = u"\u0315\u031b\u0340\u0341\u0358\u0321\u0322\u0327\u0328\u0334\u0335\u0336\u034f\u035c\u035d\u035e\u035f\u0360\u0362\u0338\u0337\u0361\u0489";
-
-    std::u16string randomMarks(std::u16string& combiningMarks) {
-        std::u16string result;
-        auto num = std::rand() % (combiningMarks.size() / 1);
-        for (size_t i = 0; i < num; ++i) {
-            auto index = std::rand() % combiningMarks.size();
-            result += combiningMarks[index];
-        }
-        return result;
-    }
-
-public:
-    std::u16string zalgo(std::string victim) {
-        std::u16string result;
-        for (auto& c : victim) {
-            result += c;
-            result += randomMarks(COMBINING_UP);
-            result += randomMarks(COMBINING_MIDDLE);
-            result += randomMarks(COMBINING_DOWN);
-        }
-        return result;
-    }
-};
-
-class ParagraphView18 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph18"); }
-
-    bool onChar(SkUnichar uni) override {
-            switch (uni) {
-                case ' ':
-                    fLimit = 400;
-                    return true;
-                case 's':
-                    fLimit += 10;
-                    return true;
-                case 'f':
-                    if (fLimit > 10) {
-                        fLimit -= 10;
-                    }
-                    return true;
-                default:
-                    break;
-            }
-            return false;
-    }
-
-    bool onAnimate(double nanos) override {
-        if (++fIndex > fLimit) {
-            fRedraw = true;
-            fIndex = 0;
-        } else {
-            fRepeat = true;
-        }
-        return true;
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-
-        auto navy = SkColorSetRGB(0, 0, 139);
-        auto ltgray = SkColorSetRGB(211, 211, 211);
-
-        auto multiplier = 5.67;
-        auto fontCollection = sk_make_sp<FontCollection>();
-        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
-        fontCollection->enableFontFallback();
-
-        ParagraphStyle paragraph_style;
-        TextStyle text_style;
-        text_style.setFontFamilies({SkString("Roboto")});
-        text_style.setFontSize(20 * multiplier);
-        text_style.setColor(navy);
-        SkPaint paint;
-        paint.setColor(ltgray);
-        text_style.setBackgroundColor(paint);
-
-        Zalgo zalgo;
-
-        if (fRedraw || fRepeat) {
-
-            if (fRedraw || fParagraph.get() == nullptr) {
-                ParagraphBuilderImpl builder(paragraph_style, fontCollection);
-                builder.pushStyle(text_style);
-                auto utf16text = zalgo.zalgo("SkParagraph");
-                icu::UnicodeString unicode((UChar*)utf16text.data(), SkToS32(utf16text.size()));
-                std::string str;
-                unicode.toUTF8String(str);
-                SkDebugf("Text:>%s<\n", str.data());
-                builder.addText(utf16text);
-                fParagraph = builder.Build();
-            }
-
-            auto impl = static_cast<ParagraphImpl*>(fParagraph.get());
-            impl->setState(InternalState::kUnknown);
-            fParagraph->layout(1000);
-            fParagraph->paint(canvas, 300, 200);
-
-            for (auto& run : impl->runs()) {
-                SkString fontFamily("unresolved");
-                if (run.font().getTypeface() != nullptr) {
-                    run.font().getTypeface()->getFamilyName(&fontFamily);
-                }
-                if (run.font().getTypeface() != nullptr) {
-                    for (size_t i = 0; i < run.size(); ++i) {
-                        auto glyph = run.glyphs().begin() + i;
-                        if (*glyph == 0) {
-                            SkDebugf("Run[%d] @pos=%d\n", run.index(), i);
-                            SkASSERT(false);
-                        }
-                    }
-                } else {
-                    SkDebugf("Run[%d]: %s\n", run.index(), fontFamily.c_str());
-                    SkASSERT(false);
-                }
-            }
-            fRedraw = false;
-            fRepeat = false;
-        }
-    }
-
-private:
-    bool fRedraw = true;
-    bool fRepeat = false;
-    size_t fIndex = 0;
-    size_t fLimit = 20;
-    std::unique_ptr<Paragraph> fParagraph;
-    typedef Sample INHERITED;
-};
-
-class ParagraphView19 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph19"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-
-        auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
-
-        const char* text = "World domination is such an ugly phrase - I prefer to call it world optimisation";
-        ParagraphStyle paragraph_style;
-        paragraph_style.setMaxLines(7);
-        paragraph_style.setEllipsis(u"\u2026");
-        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
-        TextStyle text_style;
-        text_style.setColor(SK_ColorBLACK);
-        text_style.setFontFamilies({SkString("Roboto")});
-        text_style.setFontSize(40);
-        builder.pushStyle(text_style);
-        builder.addText(text);
-        auto paragraph = builder.Build();
-        paragraph->layout(this->width());
-
-        paragraph->paint(canvas, 0, 0);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-class ParagraphView20 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph20"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-
-        auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
-
-        const char* text = "";
-        ParagraphStyle paragraph_style;
-        paragraph_style.setMaxLines(std::numeric_limits<size_t>::max());
-        //paragraph_style.setEllipsis(u"\u2026");
-        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
-        TextStyle text_style;
-        text_style.setColor(SK_ColorBLACK);
-        text_style.setFontFamilies({SkString("Roboto")});
-        text_style.setFontSize(40);
-        builder.pushStyle(text_style);
-        builder.addText(text);
-        auto paragraph = builder.Build();
-        paragraph->layout(this->width());
-
-        paragraph->paint(canvas, 0, 0);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE(return new ParagraphView1();)
-DEF_SAMPLE(return new ParagraphView2();)
-DEF_SAMPLE(return new ParagraphView3();)
-DEF_SAMPLE(return new ParagraphView4();)
-DEF_SAMPLE(return new ParagraphView5();)
-DEF_SAMPLE(return new ParagraphView6();)
-DEF_SAMPLE(return new ParagraphView7();)
-DEF_SAMPLE(return new ParagraphView8();)
-DEF_SAMPLE(return new ParagraphView9();)
-DEF_SAMPLE(return new ParagraphView10();)
-DEF_SAMPLE(return new ParagraphView11();)
-DEF_SAMPLE(return new ParagraphView12();)
-DEF_SAMPLE(return new ParagraphView14();)
-DEF_SAMPLE(return new ParagraphView15();)
-DEF_SAMPLE(return new ParagraphView16();)
-DEF_SAMPLE(return new ParagraphView17();)
-DEF_SAMPLE(return new ParagraphView18();)
-DEF_SAMPLE(return new ParagraphView19();)
-DEF_SAMPLE(return new ParagraphView20();)
diff --git a/third_party/skia/samplecode/SamplePatch.cpp b/third_party/skia/samplecode/SamplePatch.cpp
index 3fd7d3d..98dde31 100644
--- a/third_party/skia/samplecode/SamplePatch.cpp
+++ b/third_party/skia/samplecode/SamplePatch.cpp
@@ -34,7 +34,7 @@
     SkBitmap bm;
     decode_file(GetResourceAsData("images/dog.jpg"), &bm);
     *size = SkIPoint{bm.width(), bm.height()};
-    return bm.makeShader();
+    return bm.makeShader(SkSamplingOptions(SkFilterMode::kLinear));
 }
 
 static sk_sp<SkShader> make_shader1(const SkIPoint& size) {
@@ -241,7 +241,6 @@
 
         SkPaint paint;
         paint.setDither(true);
-        paint.setFilterQuality(kLow_SkFilterQuality);
 
         canvas->translate(DX, DY);
 
@@ -314,7 +313,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 }  // namespace
 DEF_SAMPLE( return new PatchView(); )
@@ -345,8 +344,7 @@
 
     int vertCount = pts.count();
     int indexCount = 0; // no texture
-    unsigned flags = SkVertices::kHasColors_BuilderFlag |
-                     SkVertices::kIsNonVolatile_BuilderFlag;
+    unsigned flags = SkVertices::kHasColors_BuilderFlag;
     SkVertices::Builder builder(SkVertices::kTriangleStrip_VertexMode,
                                 vertCount, indexCount, flags);
     memcpy(builder.positions(), pts.begin(), vertCount * sizeof(SkPoint));
@@ -416,7 +414,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 }  // namespace
 DEF_SAMPLE( return new PseudoInkView(); )
@@ -503,7 +501,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 }  // namespace
 DEF_SAMPLE( return new ManyStrokesView(); )
diff --git a/third_party/skia/samplecode/SamplePath.cpp b/third_party/skia/samplecode/SamplePath.cpp
index 478f6a0..ac725d9 100644
--- a/third_party/skia/samplecode/SamplePath.cpp
+++ b/third_party/skia/samplecode/SamplePath.cpp
@@ -11,7 +11,7 @@
 #include "include/core/SkColorPriv.h"
 #include "include/core/SkFont.h"
 #include "include/core/SkGraphics.h"
-#include "include/core/SkPath.h"
+#include "include/core/SkPathBuilder.h"
 #include "include/core/SkRegion.h"
 #include "include/core/SkShader.h"
 #include "include/core/SkTime.h"
@@ -195,7 +195,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 DEF_SAMPLE( return new PathView; )
 
@@ -279,38 +279,29 @@
         canvas->drawPath(path, fSkeletonPaint);
     }
 
-    bool onClick(Click* click) override {
-        int32_t index;
-        if (click->fMeta.findS32("index", &index)) {
-            SkASSERT((unsigned)index < N);
-            fPts[index] = click->fCurr;
-            return true;
-        }
-        return false;
-    }
-
     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
         const SkScalar tol = 4;
         const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
         for (int i = 0; i < N; ++i) {
             if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
-                Click* click = new Click();
-                click->fMeta.setS32("index", i);
-                return click;
+                return new Click([this, i](Click* c) {
+                    fPts[i] = c->fCurr;
+                    return true;
+                });
             }
         }
         return nullptr;
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 DEF_SAMPLE( return new ArcToView; )
 
 /////////////
 
 class FatStroke : public Sample {
-    bool fClosed, fShowStroke, fShowHidden, fShowSkeleton;
+    bool fClosed, fShowStroke, fShowHidden, fShowSkeleton, fAsCurves = false;
     int  fJoinType, fCapType;
     float fWidth = 30;
     SkPaint fPtsPaint, fHiddenPaint, fSkeletonPaint, fStrokePaint;
@@ -366,6 +357,7 @@
                 case '4': this->toggle3(fJoinType); return true;
                 case '5': this->toggle3(fCapType); return true;
                 case '6': this->toggle(fClosed); return true;
+                case 'c': this->toggle(fAsCurves); return true;
                 case '-': fWidth -= 5; return true;
                 case '=': fWidth += 5; return true;
                 default: break;
@@ -375,8 +367,15 @@
 
     void makePath(SkPath* path) {
         path->moveTo(fPts[0]);
-        for (int i = 1; i < N; ++i) {
-            path->lineTo(fPts[i]);
+        if (fAsCurves) {
+            for (int i = 1; i < N-2; ++i) {
+                path->quadTo(fPts[i], (fPts[i+1] + fPts[i]) * 0.5f);
+            }
+            path->quadTo(fPts[N-2], fPts[N-1]);
+        } else {
+            for (int i = 1; i < N; ++i) {
+                path->lineTo(fPts[i]);
+            }
         }
         if (fClosed) {
             path->close();
@@ -407,31 +406,22 @@
         canvas->drawPoints(SkCanvas::kPoints_PointMode, N, fPts, fPtsPaint);
     }
 
-    bool onClick(Click* click) override {
-        int32_t index;
-        if (click->fMeta.findS32("index", &index)) {
-            SkASSERT((unsigned)index < N);
-            fPts[index] = click->fCurr;
-            return true;
-        }
-        return false;
-    }
-
     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
         const SkScalar tol = 4;
         const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
         for (int i = 0; i < N; ++i) {
             if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
-                Click* click = new Click();
-                click->fMeta.setS32("index", i);
-                return click;
+                return new Click([this, i](Click* c) {
+                    fPts[i] = c->fCurr;
+                    return true;
+                });
             }
         }
         return nullptr;
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 DEF_SAMPLE( return new FatStroke; )
 
@@ -524,31 +514,22 @@
         }
     }
 
-    bool onClick(Click* click) override {
-        int32_t index;
-        if (click->fMeta.findS32("index", &index)) {
-            SkASSERT((unsigned)index < N);
-            fPts[index] = click->fCurr;
-            return true;
-        }
-        return false;
-    }
-
     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
         const SkScalar tol = 8;
         const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
         for (int i = 0; i < N; ++i) {
             if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
-                Click* click = new Click();
-                click->fMeta.setS32("index", i);
-                return click;
+                return new Click([this, i](Click* c) {
+                    fPts[i] = c->fCurr;
+                    return true;
+                });
             }
         }
         return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 DEF_SAMPLE( return new CubicCurve; )
 
@@ -581,6 +562,7 @@
     SkScalar fT = 0.5f;
     bool fShowSub = false;
     bool fShowFlatness = false;
+    bool fShowInnerQuads = false;
     SkScalar fScale = 0.75;
 
     CubicCurve2() {
@@ -603,18 +585,25 @@
                 case 'f': fShowFlatness = !fShowFlatness; break;
                 case '-': fT -= 1.0f / 32; break;
                 case '=': fT += 1.0f / 32; break;
+                case 'q': fShowInnerQuads = !fShowInnerQuads; break;
                 default: return false;
             }
             fT = std::min(1.0f, std::max(0.0f, fT));
             return true;
     }
 
+    static void Dot(SkCanvas* canvas, SkPoint p, SkScalar radius, SkColor c) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(c);
+        canvas->drawCircle(p.fX, p.fY, radius, paint);
+    }
+
     void showFrame(SkCanvas* canvas, const SkPoint pts[], int count, const SkPaint& p) {
         SkPaint paint(p);
         SkPoint storage[3 + 2 + 1];
         SkPoint* tmp = storage;
         const SkPoint* prev = pts;
-        int n = count;
         for (int n = count; n > 0; --n) {
             for (int i = 0; i < n; ++i) {
                 canvas->drawLine(prev[i], prev[i+1], paint);
@@ -626,9 +615,9 @@
 
         paint.setColor(SK_ColorBLUE);
         paint.setStyle(SkPaint::kFill_Style);
-        n = tmp - storage;
+        int n = tmp - storage;
         for (int i = 0; i < n; ++i) {
-            canvas->drawCircle(storage[i].fX, storage[i].fY, 4, paint);
+            Dot(canvas, storage[i], 4, SK_ColorBLUE);
         }
     }
 
@@ -672,6 +661,33 @@
         // not sure we can get here
     }
 
+    void showInnerQuads(SkCanvas* canvas) {
+        auto draw_quad = [canvas](SkPoint a, SkPoint b, SkPoint c, SkColor color) {
+            SkPaint paint;
+            paint.setAntiAlias(true);
+            paint.setStroke(true);
+            paint.setColor(color);
+
+            canvas->drawPath(SkPathBuilder().moveTo(a).quadTo(b, c).detach(), paint);
+        };
+
+        SkPoint p0 = SkEvalQuadAt(&fPts[0], fT),
+                p1 = SkEvalQuadAt(&fPts[1], fT),
+                p2 = lerp(p0, p1, fT);
+
+        draw_quad(fPts[0], fPts[1], fPts[2], SK_ColorRED);
+        Dot(canvas, p0, 4, SK_ColorRED);
+
+        draw_quad(fPts[1], fPts[2], fPts[3], SK_ColorBLUE);
+        Dot(canvas, p1, 4, SK_ColorBLUE);
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xFF008800);
+        canvas->drawLine(p0, p1, paint);
+        Dot(canvas, p2, 4, 0xFF00AA00);
+    }
+
     void onDrawContent(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
@@ -702,13 +718,20 @@
             this->showFlattness(canvas);
         }
 
-        paint.setStyle(SkPaint::kFill_Style);
-        paint.setColor(SK_ColorRED);
-        for (SkPoint p : fPts) {
-            canvas->drawCircle(p.fX, p.fY, 7, paint);
+        if (fShowInnerQuads) {
+            this->showInnerQuads(canvas);
         }
 
-        {
+        paint.setColor(SK_ColorGRAY);
+        paint.setStroke(true);
+        canvas->drawPath(SkPathBuilder().addPolygon(fPts, 4, false).detach(), paint);
+        canvas->drawPath(SkPathBuilder().addPolygon(fQuad, 3, false).detach(), paint);
+
+        for (SkPoint p : fPts) {
+            Dot(canvas, p, 7, SK_ColorBLACK);
+        }
+
+        if ((false)) {
             SkScalar ts[2];
             int n = SkFindCubicInflections(fPts, ts);
             for (int i = 0; i < n; ++i) {
@@ -720,31 +743,22 @@
 
     }
 
-    bool onClick(Click* click) override {
-        int32_t index;
-        if (click->fMeta.findS32("index", &index)) {
-            SkASSERT((unsigned)index < N);
-            fPts[index] = click->fCurr;
-            return true;
-        }
-        return false;
-    }
-
     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
         const SkScalar tol = 8;
         const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
         for (int i = 0; i < N; ++i) {
             if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
-                Click* click = new Click();
-                click->fMeta.setS32("index", i);
-                return click;
+                return new Click([this, i](Click* c) {
+                    fPts[i] = c->fCurr;
+                    return true;
+                });
             }
         }
         return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 DEF_SAMPLE( return new CubicCurve2; )
 
diff --git a/third_party/skia/samplecode/SamplePathClip.cpp b/third_party/skia/samplecode/SamplePathClip.cpp
index c7852ab..8db008d 100644
--- a/third_party/skia/samplecode/SamplePathClip.cpp
+++ b/third_party/skia/samplecode/SamplePathClip.cpp
@@ -53,16 +53,14 @@
     }
 
     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
-        return new Click();
-    }
-
-    bool onClick(Click* click) override {
-        fCenter.set(click->fCurr.fX, click->fCurr.fY);
-        return false;
+        return new Click([&](Click* c) {
+            fCenter = c->fCurr;
+            return false;
+        });
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 DEF_SAMPLE( return new PathClipView; )
 
@@ -298,11 +296,11 @@
 
     bool onClick(Click* click) override {
         ((MyClick*)click)->handleMove();
-        return false;
+        return true;
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 DEF_SAMPLE( return new EdgeClipView; )
diff --git a/third_party/skia/samplecode/SamplePathEffects.cpp b/third_party/skia/samplecode/SamplePathEffects.cpp
index ccdc541..5ed3241 100644
--- a/third_party/skia/samplecode/SamplePathEffects.cpp
+++ b/third_party/skia/samplecode/SamplePathEffects.cpp
@@ -137,7 +137,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SamplePathOverstroke.cpp b/third_party/skia/samplecode/SamplePathOverstroke.cpp
index a01562b..9ebf1d4 100644
--- a/third_party/skia/samplecode/SamplePathOverstroke.cpp
+++ b/third_party/skia/samplecode/SamplePathOverstroke.cpp
@@ -191,7 +191,7 @@
     }
 
    private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SamplePathTessellators.cpp b/third_party/skia/samplecode/SamplePathTessellators.cpp
new file mode 100644
index 0000000..30f4383
--- /dev/null
+++ b/third_party/skia/samplecode/SamplePathTessellators.cpp
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2019 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkCanvas.h"
+#include "samplecode/Sample.h"
+#include "src/core/SkPathPriv.h"
+
+#if SK_SUPPORT_GPU
+
+#include "src/core/SkCanvasPriv.h"
+#include "src/gpu/GrOpFlushState.h"
+#include "src/gpu/GrRecordingContextPriv.h"
+#include "src/gpu/ops/GrDrawOp.h"
+#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
+#include "src/gpu/ops/TessellationPathRenderer.h"
+#include "src/gpu/tessellate/AffineMatrix.h"
+#include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
+#include "src/gpu/tessellate/PathCurveTessellator.h"
+#include "src/gpu/tessellate/PathWedgeTessellator.h"
+#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
+#include "src/gpu/v1/SurfaceDrawContext_v1.h"
+
+namespace skgpu {
+
+namespace {
+
+enum class Mode {
+    kWedgeMiddleOut,
+    kCurveMiddleOut,
+    kWedgeTessellate,
+    kCurveTessellate
+};
+
+static const char* ModeName(Mode mode) {
+    switch (mode) {
+        case Mode::kWedgeMiddleOut:
+            return "MiddleOutShader (kWedges)";
+        case Mode::kCurveMiddleOut:
+            return "MiddleOutShader (kCurves)";
+        case Mode::kWedgeTessellate:
+            return "HardwareWedgeShader";
+        case Mode::kCurveTessellate:
+            return "HardwareCurveShader";
+    }
+    SkUNREACHABLE;
+}
+
+// Draws a path directly to the screen using a specific tessellator.
+class SamplePathTessellatorOp : public GrDrawOp {
+private:
+    DEFINE_OP_CLASS_ID
+
+    SamplePathTessellatorOp(const SkRect& drawBounds, const SkPath& path, const SkMatrix& m,
+                            GrPipeline::InputFlags pipelineFlags, Mode mode)
+            : GrDrawOp(ClassID())
+            , fPath(path)
+            , fMatrix(m)
+            , fPipelineFlags(pipelineFlags)
+            , fMode(mode) {
+        this->setBounds(drawBounds, HasAABloat::kNo, IsHairline::kNo);
+    }
+    const char* name() const override { return "SamplePathTessellatorOp"; }
+    void visitProxies(const GrVisitProxyFunc&) const override {}
+    FixedFunctionFlags fixedFunctionFlags() const override {
+        return FixedFunctionFlags::kUsesHWAA;
+    }
+    GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
+                                      GrClampType clampType) override {
+        SkPMColor4f color;
+        return fProcessors.finalize(SK_PMColor4fWHITE, GrProcessorAnalysisCoverage::kNone, clip,
+                                    nullptr, caps, clampType, &color);
+    }
+    void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
+                      const GrDstProxyView&, GrXferBarrierFlags, GrLoadOp colorLoadOp) override {}
+    void onPrepare(GrOpFlushState* flushState) override {
+        constexpr static SkPMColor4f kCyan = {0,1,1,1};
+        auto alloc = flushState->allocator();
+        const SkMatrix& shaderMatrix = SkMatrix::I();
+        const SkMatrix& pathMatrix = fMatrix;
+        const GrCaps& caps = flushState->caps();
+        const GrShaderCaps& shaderCaps = *caps.shaderCaps();
+        int numVerbsToGetMiddleOut = 0;
+        int numVerbsToGetTessellation = caps.minPathVerbsForHwTessellation();
+        auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, std::move(fProcessors),
+                                                                 fPipelineFlags);
+        int numVerbs;
+        bool needsInnerFan;
+        switch (fMode) {
+            case Mode::kWedgeMiddleOut:
+                fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
+                numVerbs = numVerbsToGetMiddleOut;
+                needsInnerFan = false;
+                break;
+            case Mode::kCurveMiddleOut:
+                fTessellator = PathCurveTessellator::Make(alloc,
+                                                          shaderCaps.infinitySupport());
+                numVerbs = numVerbsToGetMiddleOut;
+                needsInnerFan = true;
+                break;
+            case Mode::kWedgeTessellate:
+                fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
+                numVerbs = numVerbsToGetTessellation;
+                needsInnerFan = false;
+                break;
+            case Mode::kCurveTessellate:
+                fTessellator = PathCurveTessellator::Make(alloc,
+                                                          shaderCaps.infinitySupport());
+                numVerbs = numVerbsToGetTessellation;
+                needsInnerFan = true;
+                break;
+        }
+        auto* tessShader = GrPathTessellationShader::Make(alloc,
+                                                          shaderMatrix,
+                                                          kCyan,
+                                                          numVerbs,
+                                                          *pipeline,
+                                                          fTessellator->patchAttribs(),
+                                                          caps);
+        fProgram = GrTessellationShader::MakeProgram({alloc, flushState->writeView(),
+                                                     flushState->usesMSAASurface(),
+                                                     &flushState->dstProxyView(),
+                                                     flushState->renderPassBarriers(),
+                                                     GrLoadOp::kClear, &flushState->caps()},
+                                                     tessShader,
+                                                     pipeline,
+                                                     &GrUserStencilSettings::kUnused);
+
+
+        int patchPreallocCount = fTessellator->patchPreallocCount(fPath.countVerbs());
+        if (needsInnerFan) {
+            patchPreallocCount += fPath.countVerbs() - 1;
+        }
+        PatchWriter patchWriter(flushState,
+                                fTessellator,
+                                tessShader->maxTessellationSegments(*caps.shaderCaps()),
+                                patchPreallocCount);
+
+        if (needsInnerFan) {
+            // Write out inner fan triangles.
+            AffineMatrix m(pathMatrix);
+            for (PathMiddleOutFanIter it(fPath); !it.done();) {
+                for (auto [p0, p1, p2] : it.nextStack()) {
+                    auto [mp0, mp1] = m.map2Points(p0, p1);
+                    auto mp2 = m.map1Point(&p2);
+                    patchWriter.writeTriangle(mp0, mp1, mp2);
+                }
+            }
+        }
+
+        // Write out the curves.
+        fTessellator->writePatches(patchWriter, shaderMatrix, {pathMatrix, fPath, kCyan});
+
+        if (!tessShader->willUseTessellationShaders()) {
+            fTessellator->prepareFixedCountBuffers(flushState);
+        }
+
+    }
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+        flushState->bindPipeline(*fProgram, chainBounds);
+        fTessellator->draw(flushState, fProgram->geomProc().willUseTessellationShaders());
+    }
+
+    const SkPath fPath;
+    const SkMatrix fMatrix;
+    const GrPipeline::InputFlags fPipelineFlags;
+    const Mode fMode;
+    PathTessellator* fTessellator = nullptr;
+    GrProgramInfo* fProgram;
+    GrProcessorSet fProcessors{SkBlendMode::kSrcOver};
+
+    friend class GrOp;  // For ctor.
+};
+
+}  // namespace
+
+// This sample enables wireframe and visualizes the triangles generated by path tessellators.
+class SamplePathTessellators : public Sample {
+public:
+    SamplePathTessellators() {
+#if 0
+        // For viewing middle-out triangulations of the inner fan.
+        fPath.moveTo(1, 0);
+        int numSides = 32 * 3;
+        for (int i = 1; i < numSides; ++i) {
+            float theta = 2*3.1415926535897932384626433832785 * i / numSides;
+            fPath.lineTo(std::cos(theta), std::sin(theta));
+        }
+        fPath.transform(SkMatrix::Scale(200, 200));
+        fPath.transform(SkMatrix::Translate(300, 300));
+#else
+        fPath.moveTo(100, 500);
+        fPath.cubicTo(300, 400, -100, 300, 100, 200);
+        fPath.quadTo(250, 0, 400, 200);
+        fPath.conicTo(600, 350, 400, 500, fConicWeight);
+        fPath.close();
+#endif
+    }
+
+private:
+    void onDrawContent(SkCanvas*) override;
+    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
+    bool onClick(Sample::Click*) override;
+    bool onChar(SkUnichar) override;
+
+    SkString name() override { return SkString("PathTessellators"); }
+
+    SkPath fPath;
+    GrPipeline::InputFlags fPipelineFlags = GrPipeline::InputFlags::kWireframe;
+    Mode fMode = Mode::kWedgeMiddleOut;
+
+    float fConicWeight = .5;
+
+    class Click;
+};
+
+void SamplePathTessellators::onDrawContent(SkCanvas* canvas) {
+    canvas->clear(SK_ColorBLACK);
+
+    auto ctx = canvas->recordingContext();
+    auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
+
+    SkString error;
+    if (!sdc || !ctx) {
+        error = "GPU Only.";
+    } else if (!skgpu::v1::TessellationPathRenderer::IsSupported(*ctx->priv().caps())) {
+        error = "TessellationPathRenderer not supported.";
+    } else if (fMode >= Mode::kWedgeTessellate &&
+               !ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
+        error.printf("%s requires hardware tessellation support.", ModeName(fMode));
+    }
+    if (!error.isEmpty()) {
+        canvas->clear(SK_ColorRED);
+        SkFont font(nullptr, 20);
+        SkPaint captionPaint;
+        captionPaint.setColor(SK_ColorWHITE);
+        canvas->drawString(error.c_str(), 10, 30, font, captionPaint);
+        return;
+    }
+
+    sdc->addDrawOp(GrOp::Make<SamplePathTessellatorOp>(ctx,
+                                                       sdc->asRenderTargetProxy()->getBoundsRect(),
+                                                       fPath, canvas->getTotalMatrix(),
+                                                       fPipelineFlags, fMode));
+
+    // Draw the path points.
+    SkPaint pointsPaint;
+    pointsPaint.setColor(SK_ColorBLUE);
+    pointsPaint.setStrokeWidth(8);
+    SkPath devPath = fPath;
+    devPath.transform(canvas->getTotalMatrix());
+    {
+        SkAutoCanvasRestore acr(canvas, true);
+        canvas->setMatrix(SkMatrix::I());
+        SkString caption(ModeName(fMode));
+        caption.appendf(" (w=%g)", fConicWeight);
+        SkFont font(nullptr, 20);
+        SkPaint captionPaint;
+        captionPaint.setColor(SK_ColorWHITE);
+        canvas->drawString(caption, 10, 30, font, captionPaint);
+        canvas->drawPoints(SkCanvas::kPoints_PointMode, devPath.countPoints(),
+                           SkPathPriv::PointData(devPath), pointsPaint);
+    }
+}
+
+class SamplePathTessellators::Click : public Sample::Click {
+public:
+    Click(int ptIdx) : fPtIdx(ptIdx) {}
+
+    void doClick(SkPath* path) {
+        SkPoint pt = path->getPoint(fPtIdx);
+        SkPathPriv::UpdatePathPoint(path, fPtIdx, pt + fCurr - fPrev);
+    }
+
+private:
+    int fPtIdx;
+};
+
+Sample::Click* SamplePathTessellators::onFindClickHandler(SkScalar x, SkScalar y,
+                                                          skui::ModifierKey) {
+    const SkPoint* pts = SkPathPriv::PointData(fPath);
+    float fuzz = 30;
+    for (int i = 0; i < fPath.countPoints(); ++i) {
+        if (fabs(x - pts[i].x()) < fuzz && fabsf(y - pts[i].y()) < fuzz) {
+            return new Click(i);
+        }
+    }
+    return nullptr;
+}
+
+bool SamplePathTessellators::onClick(Sample::Click* click) {
+    Click* myClick = (Click*)click;
+    myClick->doClick(&fPath);
+    return true;
+}
+
+static SkPath update_weight(const SkPath& path, float w) {
+    SkPath path_;
+    for (auto [verb, pts, _] : SkPathPriv::Iterate(path)) {
+        switch (verb) {
+            case SkPathVerb::kMove:
+                path_.moveTo(pts[0]);
+                break;
+            case SkPathVerb::kLine:
+                path_.lineTo(pts[1]);
+                break;
+            case SkPathVerb::kQuad:
+                path_.quadTo(pts[1], pts[2]);
+                break;
+            case SkPathVerb::kCubic:
+                path_.cubicTo(pts[1], pts[2], pts[3]);
+                break;
+            case SkPathVerb::kConic:
+                path_.conicTo(pts[1], pts[2], (w != 1) ? w : .99f);
+                break;
+            case SkPathVerb::kClose:
+                break;
+        }
+    }
+    return path_;
+}
+
+bool SamplePathTessellators::onChar(SkUnichar unichar) {
+    switch (unichar) {
+        case 'w':
+            fPipelineFlags = (GrPipeline::InputFlags)(
+                    (int)fPipelineFlags ^ (int)GrPipeline::InputFlags::kWireframe);
+            return true;
+        case 'D': {
+            fPath.dump();
+            return true;
+        }
+        case '+':
+            fConicWeight *= 2;
+            fPath = update_weight(fPath, fConicWeight);
+            return true;
+        case '=':
+            fConicWeight *= 5/4.f;
+            fPath = update_weight(fPath, fConicWeight);
+            return true;
+        case '_':
+            fConicWeight *= .5f;
+            fPath = update_weight(fPath, fConicWeight);
+            return true;
+        case '-':
+            fConicWeight *= 4/5.f;
+            fPath = update_weight(fPath, fConicWeight);
+            return true;
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+            fMode = (Mode)(unichar - '1');
+            return true;
+    }
+    return false;
+}
+
+Sample* MakeTessellatedPathSample() { return new SamplePathTessellators; }
+static SampleRegistry gTessellatedPathSample(MakeTessellatedPathSample);
+
+}  // namespace skgpu
+
+#endif  // SK_SUPPORT_GPU
diff --git a/third_party/skia/samplecode/SamplePathText.cpp b/third_party/skia/samplecode/SamplePathText.cpp
index 0b8ef02..1f5cd82 100644
--- a/third_party/skia/samplecode/SamplePathText.cpp
+++ b/third_party/skia/samplecode/SamplePathText.cpp
@@ -10,7 +10,8 @@
 #include "include/core/SkPath.h"
 #include "include/utils/SkRandom.h"
 #include "samplecode/Sample.h"
-#include "src/core/SkStrike.h"
+#include "src/core/SkPathPriv.h"
+#include "src/core/SkScalerCache.h"
 #include "src/core/SkStrikeCache.h"
 #include "src/core/SkStrikeSpec.h"
 #include "src/core/SkTaskGroup.h"
@@ -29,18 +30,24 @@
         for (Glyph& glyph : fGlyphs) {
             glyph.reset(fRand, this->width(), this->height());
         }
+        fGlyphAnimator->reset(&fRand, this->width(), this->height());
     }
 
     void onOnceBeforeDraw() final {
         SkFont defaultFont;
         SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(defaultFont);
-        auto cache = strikeSpec.findOrCreateExclusiveStrike();
+        auto strike = strikeSpec.findOrCreateStrike();
+        SkArenaAlloc alloc(1 << 12); // This is a mock SkStrikeCache.
         SkPath glyphPaths[52];
         for (int i = 0; i < 52; ++i) {
             // I and l are rects on OS X ...
             char c = "aQCDEFGH7JKLMNOPBRZTUVWXYSAbcdefghijk1mnopqrstuvwxyz"[i];
             SkPackedGlyphID id(defaultFont.unicharToGlyph(c));
-            sk_ignore_unused_variable(cache->getScalerContext()->getPath(id, &glyphPaths[i]));
+            SkGlyph glyph = strike->getScalerContext()->makeGlyph(id, &alloc);
+            strike->getScalerContext()->getPath(glyph, &alloc);
+            if (glyph.path()) {
+                glyphPaths[i] = *glyph.path();
+            }
         }
 
         for (int i = 0; i < kNumPaths; ++i) {
@@ -48,43 +55,30 @@
             fGlyphs[i].init(fRand, p);
         }
 
-        this->INHERITED::onOnceBeforeDraw();
+        this->Sample::onOnceBeforeDraw();
         this->reset();
     }
-    void onSizeChange() final { this->INHERITED::onSizeChange(); this->reset(); }
+    void onSizeChange() final { this->Sample::onSizeChange(); this->reset(); }
 
     SkString name() override { return SkString(this->getName()); }
 
-    bool onChar(SkUnichar unichar) override {
-            if (unichar == 'X') {
-                fDoClip = !fDoClip;
-                return true;
-            }
-            return false;
+    bool onChar(SkUnichar) override;
+
+    bool onAnimate(double nanos) final {
+        return fGlyphAnimator->animate(nanos, this->width(), this->height());
     }
 
     void onDrawContent(SkCanvas* canvas) override {
         if (fDoClip) {
             SkPath deviceSpaceClipPath = fClipPath;
-            deviceSpaceClipPath.transform(SkMatrix::MakeScale(this->width(), this->height()));
+            deviceSpaceClipPath.transform(SkMatrix::Scale(this->width(), this->height()));
             canvas->save();
             canvas->clipPath(deviceSpaceClipPath, SkClipOp::kDifference, true);
             canvas->clear(SK_ColorBLACK);
             canvas->restore();
             canvas->clipPath(deviceSpaceClipPath, SkClipOp::kIntersect, true);
         }
-        this->drawGlyphs(canvas);
-    }
-
-    virtual void drawGlyphs(SkCanvas* canvas) {
-        for (Glyph& glyph : fGlyphs) {
-            SkAutoCanvasRestore acr(canvas, true);
-            canvas->translate(glyph.fPosition.x(), glyph.fPosition.y());
-            canvas->scale(glyph.fZoom, glyph.fZoom);
-            canvas->rotate(glyph.fSpin);
-            canvas->translate(-glyph.fMidpt.x(), -glyph.fMidpt.y());
-            canvas->drawPath(glyph.fPath, glyph.fPaint);
-        }
+        fGlyphAnimator->draw(canvas);
     }
 
 protected:
@@ -100,12 +94,36 @@
         SkPoint    fMidpt;
     };
 
-    Glyph      fGlyphs[kNumPaths];
-    SkRandom   fRand{25};
-    SkPath     fClipPath = ToolUtils::make_star(SkRect{0, 0, 1, 1}, 11, 3);
-    bool       fDoClip = false;
+    class GlyphAnimator {
+    public:
+        GlyphAnimator(Glyph* glyphs) : fGlyphs(glyphs) {}
+        virtual void reset(SkRandom*, int screenWidth, int screenHeight) {}
+        virtual bool animate(double nanos, int screenWidth, int screenHeight) { return false; }
+        virtual void draw(SkCanvas* canvas) {
+            for (int i = 0; i < kNumPaths; ++i) {
+                Glyph& glyph = fGlyphs[i];
+                SkAutoCanvasRestore acr(canvas, true);
+                canvas->translate(glyph.fPosition.x(), glyph.fPosition.y());
+                canvas->scale(glyph.fZoom, glyph.fZoom);
+                canvas->rotate(glyph.fSpin);
+                canvas->translate(-glyph.fMidpt.x(), -glyph.fMidpt.y());
+                canvas->drawPath(glyph.fPath, glyph.fPaint);
+            }
+        }
+        virtual ~GlyphAnimator() {}
 
-    typedef Sample INHERITED;
+    protected:
+        Glyph* const fGlyphs;
+    };
+
+    class MovingGlyphAnimator;
+    class WavyGlyphAnimator;
+
+    Glyph fGlyphs[kNumPaths];
+    SkRandom fRand{25};
+    SkPath fClipPath = ToolUtils::make_star(SkRect{0, 0, 1, 1}, 11, 3);
+    bool fDoClip = false;
+    std::unique_ptr<GlyphAnimator> fGlyphAnimator = std::make_unique<GlyphAnimator>(fGlyphs);
 };
 
 void PathText::Glyph::init(SkRandom& rand, const SkPath& path) {
@@ -115,62 +133,60 @@
 }
 
 void PathText::Glyph::reset(SkRandom& rand, int w, int h) {
-    int screensize = SkTMax(w, h);
+    int screensize = std::max(w, h);
     const SkRect& bounds = fPath.getBounds();
     SkScalar t;
 
     fPosition = {rand.nextF() * w, rand.nextF() * h};
     t = pow(rand.nextF(), 100);
     fZoom = ((1 - t) * screensize / 50 + t * screensize / 3) /
-            SkTMax(bounds.width(), bounds.height());
+            std::max(bounds.width(), bounds.height());
     fSpin = rand.nextF() * 360;
     fMidpt = {bounds.centerX(), bounds.centerY()};
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // Text from paths with animated transformation matrices.
-class MovingPathText : public PathText {
+class PathText::MovingGlyphAnimator : public PathText::GlyphAnimator {
 public:
-    const char* getName() const override { return "MovingPathText"; }
-
-    MovingPathText()
-        : fFrontMatrices(kNumPaths)
-        , fBackMatrices(kNumPaths) {
+    MovingGlyphAnimator(Glyph* glyphs)
+            : GlyphAnimator(glyphs)
+            , fFrontMatrices(kNumPaths)
+            , fBackMatrices(kNumPaths) {
     }
 
-    ~MovingPathText() override {
+    ~MovingGlyphAnimator() override {
         fBackgroundAnimationTask.wait();
     }
 
-    void reset() override {
-        const SkScalar screensize = static_cast<SkScalar>(SkTMax(this->width(), this->height()));
-        this->INHERITED::reset();
+    void reset(SkRandom* rand, int screenWidth, int screenHeight) override {
+        const SkScalar screensize = static_cast<SkScalar>(std::max(screenWidth, screenHeight));
 
         for (auto& v : fVelocities) {
             for (SkScalar* d : {&v.fDx, &v.fDy}) {
-                SkScalar t = pow(fRand.nextF(), 3);
-                *d = ((1 - t) / 60 + t / 10) * (fRand.nextBool() ? screensize : -screensize);
+                SkScalar t = pow(rand->nextF(), 3);
+                *d = ((1 - t) / 60 + t / 10) * (rand->nextBool() ? screensize : -screensize);
             }
 
-            SkScalar t = pow(fRand.nextF(), 25);
-            v.fDSpin = ((1 - t) * 360 / 7.5 + t * 360 / 1.5) * (fRand.nextBool() ? 1 : -1);
+            SkScalar t = pow(rand->nextF(), 25);
+            v.fDSpin = ((1 - t) * 360 / 7.5 + t * 360 / 1.5) * (rand->nextBool() ? 1 : -1);
         }
 
         // Get valid front data.
         fBackgroundAnimationTask.wait();
-        this->runAnimationTask(0, 0, this->width(), this->height());
-        memcpy(fFrontMatrices, fBackMatrices, kNumPaths * sizeof(SkMatrix));
+        this->runAnimationTask(0, 0, screenWidth, screenHeight);
+        std::copy_n(fBackMatrices.get(), kNumPaths, fFrontMatrices.get());
         fLastTick = 0;
     }
 
-    bool onAnimate(double nanos) final {
+    bool animate(double nanos, int screenWidth, int screenHeight) final {
         fBackgroundAnimationTask.wait();
         this->swapAnimationBuffers();
 
         const double tsec = 1e-9 * nanos;
         const double dt = fLastTick ? (1e-9 * nanos - fLastTick) : 0;
-        fBackgroundAnimationTask.add(std::bind(&MovingPathText::runAnimationTask, this, tsec,
-                                               dt, this->width(), this->height()));
+        fBackgroundAnimationTask.add(std::bind(&MovingGlyphAnimator::runAnimationTask, this, tsec,
+                                               dt, screenWidth, screenHeight));
         fLastTick = 1e-9 * nanos;
         return true;
     }
@@ -215,7 +231,7 @@
         std::swap(fFrontMatrices, fBackMatrices);
     }
 
-    void drawGlyphs(SkCanvas* canvas) override {
+    void draw(SkCanvas* canvas) override {
         for (int i = 0; i < kNumPaths; ++i) {
             SkAutoCanvasRestore acr(canvas, true);
             canvas->concat(fFrontMatrices[i]);
@@ -229,42 +245,40 @@
         SkScalar fDSpin;
     };
 
-    Velocity                  fVelocities[kNumPaths];
-    SkAutoTMalloc<SkMatrix>   fFrontMatrices;
-    SkAutoTMalloc<SkMatrix>   fBackMatrices;
-    SkTaskGroup               fBackgroundAnimationTask;
-    double                    fLastTick;
-
-    typedef PathText INHERITED;
+    Velocity fVelocities[kNumPaths];
+    SkAutoTArray<SkMatrix> fFrontMatrices;
+    SkAutoTArray<SkMatrix> fBackMatrices;
+    SkTaskGroup fBackgroundAnimationTask;
+    double fLastTick;
 };
 
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // Text from paths with animated control points.
-class WavyPathText : public MovingPathText {
+class PathText::WavyGlyphAnimator : public PathText::MovingGlyphAnimator {
 public:
-    const char* getName() const override { return "WavyPathText"; }
+    WavyGlyphAnimator(Glyph* glyphs)
+            : MovingGlyphAnimator(glyphs)
+            , fFrontPaths(kNumPaths)
+            , fBackPaths(kNumPaths) {
+    }
 
-    WavyPathText()
-        : fFrontPaths(kNumPaths)
-        , fBackPaths(kNumPaths) {}
-
-    ~WavyPathText() override {
+    ~WavyGlyphAnimator() override {
         fBackgroundAnimationTask.wait();
     }
 
-    void reset() override {
-        fWaves.reset(fRand, this->width(), this->height());
-        this->INHERITED::reset();
+    void reset(SkRandom* rand, int screenWidth, int screenHeight) override {
+        fWaves.reset(*rand, screenWidth, screenHeight);
+        this->MovingGlyphAnimator::reset(rand, screenWidth, screenHeight);
         std::copy(fBackPaths.get(), fBackPaths.get() + kNumPaths, fFrontPaths.get());
     }
 
     /**
      * Called on a background thread. Here we can only modify fBackPaths.
      */
-    void runAnimationTask(double t, double dt, int w, int h) override {
+    void runAnimationTask(double t, double dt, int width, int height) override {
         const float tsec = static_cast<float>(t);
-        this->INHERITED::runAnimationTask(t, 0.5 * dt, w, h);
+        this->MovingGlyphAnimator::runAnimationTask(t, 0.5 * dt, width, height);
 
         for (int i = 0; i < kNumPaths; ++i) {
             const Glyph& glyph = fGlyphs[i];
@@ -280,35 +294,30 @@
             backpath->reset();
             backpath->setFillType(SkPathFillType::kEvenOdd);
 
-            SkPath::RawIter iter(glyph.fPath);
-            SkPath::Verb verb;
-            SkPoint pts[4];
-
-            while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+            for (auto [verb, pts, w] : SkPathPriv::Iterate(glyph.fPath)) {
                 switch (verb) {
-                    case SkPath::kMove_Verb: {
+                    case SkPathVerb::kMove: {
                         SkPoint pt = fWaves.apply(tsec, matrix, pts[0]);
                         backpath->moveTo(pt.x(), pt.y());
                         break;
                     }
-                    case SkPath::kLine_Verb: {
+                    case SkPathVerb::kLine: {
                         SkPoint endpt = fWaves.apply(tsec, matrix, pts[1]);
                         backpath->lineTo(endpt.x(), endpt.y());
                         break;
                     }
-                    case SkPath::kQuad_Verb: {
+                    case SkPathVerb::kQuad: {
                         SkPoint controlPt = fWaves.apply(tsec, matrix, pts[1]);
                         SkPoint endpt = fWaves.apply(tsec, matrix, pts[2]);
                         backpath->quadTo(controlPt.x(), controlPt.y(), endpt.x(), endpt.y());
                         break;
                     }
-                    case SkPath::kClose_Verb: {
+                    case SkPathVerb::kClose: {
                         backpath->close();
                         break;
                     }
-                    case SkPath::kCubic_Verb:
-                    case SkPath::kConic_Verb:
-                    case SkPath::kDone_Verb:
+                    case SkPathVerb::kCubic:
+                    case SkPathVerb::kConic:
                         SK_ABORT("Unexpected path verb");
                         break;
                 }
@@ -317,11 +326,11 @@
     }
 
     void swapAnimationBuffers() override {
-        this->INHERITED::swapAnimationBuffers();
+        this->MovingGlyphAnimator::swapAnimationBuffers();
         std::swap(fFrontPaths, fBackPaths);
     }
 
-    void drawGlyphs(SkCanvas* canvas) override {
+    void draw(SkCanvas* canvas) override {
         for (int i = 0; i < kNumPaths; ++i) {
             canvas->drawPath(fFrontPaths[i], fGlyphs[i].fPaint);
         }
@@ -348,15 +357,13 @@
         float fOffsets[4];
     };
 
-    SkAutoTArray<SkPath>   fFrontPaths;
-    SkAutoTArray<SkPath>   fBackPaths;
-    Waves                  fWaves;
-
-    typedef MovingPathText INHERITED;
+    SkAutoTArray<SkPath> fFrontPaths;
+    SkAutoTArray<SkPath> fBackPaths;
+    Waves fWaves;
 };
 
-void WavyPathText::Waves::reset(SkRandom& rand, int w, int h) {
-    const double pixelsPerMeter = 0.06 * SkTMax(w, h);
+void PathText::WavyGlyphAnimator::Waves::reset(SkRandom& rand, int w, int h) {
+    const double pixelsPerMeter = 0.06 * std::max(w, h);
     const double medianWavelength = 8 * pixelsPerMeter;
     const double medianWaveAmplitude = 0.05 * 4 * pixelsPerMeter;
     const double gravity = 9.8 * pixelsPerMeter;
@@ -375,7 +382,8 @@
     }
 }
 
-SkPoint WavyPathText::Waves::apply(float tsec, const Sk2f matrix[3], const SkPoint& pt) const {
+SkPoint PathText::WavyGlyphAnimator::Waves::apply(float tsec, const Sk2f matrix[3],
+                                                  const SkPoint& pt) const {
     constexpr static int kTablePeriod = 1 << 12;
     static float sin2table[kTablePeriod + 1];
     static SkOnce initTable;
@@ -422,8 +430,28 @@
     return {devicePt[0] + offsetY[0] + offsetY[1], devicePt[1] - offsetX[0] - offsetX[1]};
 }
 
+bool PathText::onChar(SkUnichar unichar) {
+    switch (unichar) {
+        case 'X':
+            fDoClip = !fDoClip;
+            return true;
+        case 'S':
+            fGlyphAnimator = std::make_unique<GlyphAnimator>(fGlyphs);
+            fGlyphAnimator->reset(&fRand, this->width(), this->height());
+            return true;
+        case 'M':
+            fGlyphAnimator = std::make_unique<MovingGlyphAnimator>(fGlyphs);
+            fGlyphAnimator->reset(&fRand, this->width(), this->height());
+            return true;
+        case 'W':
+            fGlyphAnimator = std::make_unique<WavyGlyphAnimator>(fGlyphs);
+            fGlyphAnimator->reset(&fRand, this->width(), this->height());
+            return true;
+    }
+    return false;
+}
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 
-DEF_SAMPLE( return new WavyPathText; )
-DEF_SAMPLE( return new MovingPathText; )
-DEF_SAMPLE( return new PathText; )
+Sample* MakePathTextSample() { return new PathText; }
+static SampleRegistry gPathTextSample(MakePathTextSample);
diff --git a/third_party/skia/samplecode/SamplePolyToPoly.cpp b/third_party/skia/samplecode/SamplePolyToPoly.cpp
index f8caf38..41af4a0 100644
--- a/third_party/skia/samplecode/SamplePolyToPoly.cpp
+++ b/third_party/skia/samplecode/SamplePolyToPoly.cpp
@@ -50,26 +50,24 @@
 
             (void) m2.setPolyToPoly((const SkPoint*)src1, (SkPoint*)dst1, 4);
 
-            {
-                const SkPoint src[] = {
-                    { SkIntToScalar(1), SkIntToScalar(0) },
-                    { SkIntToScalar(4), SkIntToScalar(7) },
-                    { SkIntToScalar(10), SkIntToScalar(2) }
-                };
-                const SkPoint dst[] = {
-                    { SkIntToScalar(4), SkIntToScalar(2) },
-                    { SkIntToScalar(45), SkIntToScalar(26) },
-                    { SkIntToScalar(32), SkIntToScalar(17) }
-                };
+            const SkPoint src2[] = {
+                { SkIntToScalar(1), SkIntToScalar(0) },
+                { SkIntToScalar(4), SkIntToScalar(7) },
+                { SkIntToScalar(10), SkIntToScalar(2) }
+            };
+            const SkPoint dst2[] = {
+                { SkIntToScalar(4), SkIntToScalar(2) },
+                { SkIntToScalar(45), SkIntToScalar(26) },
+                { SkIntToScalar(32), SkIntToScalar(17) }
+            };
 
-                SkMatrix m0;
-                m0.setPolyToPoly(src, dst, 3);
-            }
+            SkMatrix m0;
+            m0.setPolyToPoly(src2, dst2, 3);
         }
     }
 
 protected:
-    virtual SkString name() { return SkString("PolyToPolyView"); }
+    SkString name() override { return SkString("PolyToPolyView"); }
 
     static void doDraw(SkCanvas* canvas, SkPaint* paint, const SkFont& font, const int isrc[],
                        const int idst[], int count) {
@@ -105,7 +103,7 @@
         canvas->restore();
     }
 
-    virtual void onDrawContent(SkCanvas* canvas) {
+    void onDrawContent(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setStrokeWidth(SkIntToScalar(4));
@@ -147,7 +145,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleQuadStroker.cpp b/third_party/skia/samplecode/SampleQuadStroker.cpp
index 6b679e2..bd98ad7 100644
--- a/third_party/skia/samplecode/SampleQuadStroker.cpp
+++ b/third_party/skia/samplecode/SampleQuadStroker.cpp
@@ -28,6 +28,7 @@
 #include "include/utils/SkTextUtils.h"
 #include "samplecode/Sample.h"
 #include "src/core/SkGeometry.h"
+#include "src/core/SkPathPriv.h"
 #include "src/core/SkPointPriv.h"
 #include "src/core/SkStroke.h"
 #include "tools/ToolUtils.h"
@@ -42,18 +43,14 @@
 }
 
 static int getOnCurvePoints(const SkPath& path, SkPoint storage[]) {
-    SkPath::RawIter iter(path);
-    SkPoint pts[4];
-    SkPath::Verb verb;
-
     int count = 0;
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+    for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
         switch (verb) {
-            case SkPath::kMove_Verb:
-            case SkPath::kLine_Verb:
-            case SkPath::kQuad_Verb:
-            case SkPath::kConic_Verb:
-            case SkPath::kCubic_Verb:
+            case SkPathVerb::kMove:
+            case SkPathVerb::kLine:
+            case SkPathVerb::kQuad:
+            case SkPathVerb::kConic:
+            case SkPathVerb::kCubic:
                 storage[count++] = pts[0];
                 break;
             default:
@@ -64,25 +61,21 @@
 }
 
 static void getContourCounts(const SkPath& path, SkTArray<int>* contourCounts) {
-    SkPath::RawIter iter(path);
-    SkPoint pts[4];
-    SkPath::Verb verb;
-
     int count = 0;
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+    for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
         switch (verb) {
-            case SkPath::kMove_Verb:
-            case SkPath::kLine_Verb:
+            case SkPathVerb::kMove:
+            case SkPathVerb::kLine:
                 count += 1;
                 break;
-            case SkPath::kQuad_Verb:
-            case SkPath::kConic_Verb:
+            case SkPathVerb::kQuad:
+            case SkPathVerb::kConic:
                 count += 2;
                 break;
-            case SkPath::kCubic_Verb:
+            case SkPathVerb::kCubic:
                 count += 3;
                 break;
-            case SkPath::kClose_Verb:
+            case SkPathVerb::kClose:
                 contourCounts->push_back(count);
                 count = 0;
                 break;
@@ -218,7 +211,7 @@
                     fText = "";
                     break;
                 case '-':
-                    fTextSize = SkTMax(1.0f, fTextSize - 1);
+                    fTextSize = std::max(1.0f, fTextSize - 1);
                     break;
                 case '+':
                 case '=':
@@ -259,7 +252,7 @@
         SkCanvas* canvas = fMaxSurface->getCanvas();
         canvas->save();
         canvas->concat(fMatrix);
-        fMinSurface->draw(canvas, 0, 0, nullptr);
+        fMinSurface->draw(canvas, 0, 0);
         canvas->restore();
 
         SkPaint paint;
@@ -356,14 +349,14 @@
         for (SkScalar dist = 0; dist <= total; dist += delta) {
             ++ribs;
         }
-        SkPath::RawIter iter(path);
-        SkPoint pts[4];
-        if (SkPath::kMove_Verb != iter.next(pts)) {
+        const uint8_t* verbs = SkPathPriv::VerbData(path);
+        if (path.countVerbs() < 2 || SkPath::kMove_Verb != verbs[0]) {
             SkASSERT(0);
             return;
         }
-        SkPath::Verb verb = iter.next(pts);
+        auto verb = static_cast<SkPath::Verb>(verbs[1]);
         SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb);
+        const SkPoint* pts = SkPathPriv::PointData(path);
         SkPoint pos, tan;
         for (int index = 0; index < ribs; ++index) {
             SkScalar t = (SkScalar) index / ribs;
@@ -379,7 +372,7 @@
                     tan = SkEvalQuadTangentAt(pts, t);
                     break;
                 case SkPath::kConic_Verb: {
-                    SkConic conic(pts, iter.conicWeight());
+                    SkConic conic(pts, SkPathPriv::ConicWeightData(path)[0]);
                     pos = conic.evalAt(t);
                     tan = conic.evalTangentAt(t);
                     } break;
@@ -421,7 +414,7 @@
         if (drawText) {
             fMinSurface->getCanvas()->drawPath(path, paint);
             this->copyMinToMax();
-            fMaxSurface->draw(canvas, 0, 0, nullptr);
+            fMaxSurface->draw(canvas, 0, 0);
         }
         paint.setAntiAlias(true);
         paint.setStyle(SkPaint::kStroke_Style);
@@ -478,7 +471,7 @@
         paint.setStyle(SkPaint::kStroke_Style);
         paint.setStrokeWidth(width);
         SkPath path;
-        SkScalar maxSide = SkTMax(rect.width(), rect.height()) / 2;
+        SkScalar maxSide = std::max(rect.width(), rect.height()) / 2;
         SkPoint center = { rect.fLeft + maxSide, rect.fTop + maxSide };
         path.addCircle(center.fX, center.fY, maxSide);
         canvas->drawPath(path, paint);
@@ -721,8 +714,7 @@
         MyClick(int index) : fIndex(index) {}
     };
 
-    virtual Sample::Click* onFindClickHandler(SkScalar x, SkScalar y,
-                                              skui::ModifierKey modi) override {
+    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
         for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) {
             if (hittest(fPts[i], x, y)) {
                 return new MyClick((int)i);
@@ -793,13 +785,13 @@
         }
 #ifdef SK_DEBUG
         else if (index == (int) SK_ARRAY_COUNT(fPts) + 3) {
-            gDebugStrokerError = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fCurr.fY,
+            gDebugStrokerError = std::max(FLT_EPSILON, MapScreenYtoValue(click->fCurr.fY,
                     fErrorControl, kStrokerErrorMin, kStrokerErrorMax));
             gDebugStrokerErrorSet = true;
         }
 #endif
         else if (index == (int) SK_ARRAY_COUNT(fPts) + 4) {
-            fWidth = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fCurr.fY, fWidthControl,
+            fWidth = std::max(FLT_EPSILON, MapScreenYtoValue(click->fCurr.fY, fWidthControl,
                     kWidthMin, kWidthMax));
             fAnimate = fWidth <= kWidthMin;
         }
@@ -807,7 +799,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleRectanizer.cpp b/third_party/skia/samplecode/SampleRectanizer.cpp
index c9faa78..2b81079 100644
--- a/third_party/skia/samplecode/SampleRectanizer.cpp
+++ b/third_party/skia/samplecode/SampleRectanizer.cpp
@@ -12,8 +12,8 @@
 #include "samplecode/Sample.h"
 #include "src/utils/SkUTF.h"
 #if SK_SUPPORT_GPU
-#include "src/gpu/GrRectanizer_pow2.h"
-#include "src/gpu/GrRectanizer_skyline.h"
+#include "src/gpu/GrRectanizerPow2.h"
+#include "src/gpu/GrRectanizerSkyline.h"
 
 // This slide visualizes the various GrRectanizer-derived classes behavior
 // for various input sets
@@ -180,7 +180,7 @@
         fCurRandRect = 0;
     }
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleRegion.cpp b/third_party/skia/samplecode/SampleRegion.cpp
deleted file mode 100644
index 7c16c01..0000000
--- a/third_party/skia/samplecode/SampleRegion.cpp
+++ /dev/null
@@ -1,349 +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 "include/core/SkBitmap.h"
-#include "include/core/SkCanvas.h"
-#include "include/core/SkFont.h"
-#include "include/core/SkFontMetrics.h"
-#include "include/core/SkPath.h"
-#include "include/core/SkRegion.h"
-#include "include/core/SkShader.h"
-#include "include/effects/SkGradientShader.h"
-#include "samplecode/Sample.h"
-#include "src/utils/SkUTF.h"
-
-#include <math.h>
-
-static void test_strokerect(SkCanvas* canvas) {
-    int width = 100;
-    int height = 100;
-
-    SkBitmap bitmap;
-    bitmap.allocPixels(SkImageInfo::MakeA8(width*2, height*2));
-    bitmap.eraseColor(SK_ColorTRANSPARENT);
-
-    SkScalar dx = 20;
-    SkScalar dy = 20;
-
-    SkPath path;
-    path.addRect(0.0f, 0.0f,
-                 SkIntToScalar(width), SkIntToScalar(height),
-                 SkPathDirection::kCW);
-    SkRect r = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
-
-    SkCanvas c(bitmap);
-    c.translate(dx, dy);
-
-    SkPaint paint;
-    paint.setStyle(SkPaint::kStroke_Style);
-    paint.setStrokeWidth(1);
-
-    // use the rect
-    c.clear(SK_ColorTRANSPARENT);
-    c.drawRect(r, paint);
-    canvas->drawBitmap(bitmap, 0, 0, nullptr);
-
-    // use the path
-    c.clear(SK_ColorTRANSPARENT);
-    c.drawPath(path, paint);
-    canvas->drawBitmap(bitmap, SkIntToScalar(2*width), 0, nullptr);
-}
-
-static void drawFadingText(SkCanvas* canvas,
-                           const char* text, size_t len, SkScalar x, SkScalar y,
-                           const SkFont& font, const SkPaint& paint) {
-    // Need a bounds for the text
-    SkRect bounds;
-    SkFontMetrics fm;
-
-    font.getMetrics(&fm);
-    bounds.setLTRB(x, y + fm.fTop,
-                   x + font.measureText(text, len, SkTextEncoding::kUTF8), y + fm.fBottom);
-
-    // may need to outset bounds a little, to account for hinting and/or
-    // antialiasing
-    bounds.inset(-SkIntToScalar(2), -SkIntToScalar(2));
-
-    canvas->saveLayer(&bounds, nullptr);
-    canvas->drawSimpleText(text, len, SkTextEncoding::kUTF8, x, y, font, paint);
-
-    const SkPoint pts[] = {
-        { bounds.fLeft, y },
-        { bounds.fRight, y }
-    };
-    const SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, 0 };
-
-    // pos[1] value is where we start to fade, relative to the width
-    // of our pts[] array.
-    const SkScalar pos[] = { 0, 0.9f, SK_Scalar1 };
-
-    SkPaint p;
-    p.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 3, SkTileMode::kClamp));
-    p.setBlendMode(SkBlendMode::kDstIn);
-    canvas->drawRect(bounds, p);
-
-    canvas->restore();
-}
-
-static void test_text(SkCanvas* canvas) {
-    SkPaint paint;
-    paint.setAntiAlias(true);
-
-    SkFont font;
-    font.setSize(20);
-
-    const char* str = "Hamburgefons";
-    size_t len = strlen(str);
-    SkScalar x = 20;
-    SkScalar y = 20;
-
-    canvas->drawSimpleText(str, len, SkTextEncoding::kUTF8, x, y, font, paint);
-
-    y += 20;
-
-    const SkPoint pts[] = { { x                                                    , y },
-                            { x + font.measureText(str, len, SkTextEncoding::kUTF8), y } };
-    const SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, 0 };
-    const SkScalar pos[] = { 0, 0.9f, 1 };
-    paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos,
-                                                 SK_ARRAY_COUNT(colors),
-                                                 SkTileMode::kClamp));
-    canvas->drawSimpleText(str, len, SkTextEncoding::kUTF8, x, y, font, paint);
-
-    y += 20;
-    paint.setShader(nullptr);
-    drawFadingText(canvas, str, len, x, y, font, paint);
-}
-
-static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
-    dst->fLeft = (int)::roundf(src.fLeft * scale);
-    dst->fTop = (int)::roundf(src.fTop * scale);
-    dst->fRight = (int)::roundf(src.fRight * scale);
-    dst->fBottom = (int)::roundf(src.fBottom * scale);
-}
-
-static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
-    SkRegion tmp;
-    SkRegion::Iterator iter(src);
-
-    for (; !iter.done(); iter.next()) {
-        SkIRect r;
-        scale_rect(&r, iter.rect(), scale);
-        tmp.op(r, SkRegion::kUnion_Op);
-    }
-    dst->swap(tmp);
-}
-
-static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn,
-                      const SkPaint& paint) {
-    SkRegion scaled;
-    scale_rgn(&scaled, rgn, 0.5f);
-
-    SkRegion::Iterator  iter(rgn);
-
-    for (; !iter.done(); iter.next())
-    {
-        SkRect    r;
-        r.set(iter.rect());
-        canvas->drawRect(r, paint);
-    }
-}
-
-class RegionView : public Sample {
-public:
-    RegionView() {
-        fBase.setLTRB(100, 100, 150, 150);
-        fRect = fBase;
-        fRect.inset(5, 5);
-        fRect.offset(25, 25);
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-    void build_base_rgn(SkRegion* rgn) {
-        rgn->setRect(fBase);
-        SkIRect r = fBase;
-        r.offset(75, 20);
-        rgn->op(r, SkRegion::kUnion_Op);
-    }
-
-    void build_rgn(SkRegion* rgn, SkRegion::Op op) {
-        build_base_rgn(rgn);
-        rgn->op(fRect, op);
-    }
-
-
-protected:
-    SkString name() override { return SkString("Regions"); }
-
-    static void drawstr(SkCanvas* canvas, const char text[], const SkPoint& loc,
-                        bool hilite) {
-        SkPaint paint;
-        paint.setColor(hilite ? SK_ColorRED : 0x40FF0000);
-        SkFont font;
-        font.setSize(SkIntToScalar(20));
-        canvas->drawSimpleText(text, strlen(text), SkTextEncoding::kUTF8, loc.fX, loc.fY, font, paint);
-    }
-
-    void drawPredicates(SkCanvas* canvas, const SkPoint pts[]) {
-        SkRegion rgn;
-        build_base_rgn(&rgn);
-
-        drawstr(canvas, "Intersects", pts[0], rgn.intersects(fRect));
-        drawstr(canvas, "Contains", pts[1], rgn.contains(fRect));
-    }
-
-    void drawOrig(SkCanvas* canvas, bool bg) {
-        SkRect      r;
-        SkPaint     paint;
-
-        paint.setStyle(SkPaint::kStroke_Style);
-        if (bg)
-            paint.setColor(0xFFBBBBBB);
-
-        SkRegion rgn;
-        build_base_rgn(&rgn);
-        paint_rgn(canvas, rgn, paint);
-
-        r.set(fRect);
-        canvas->drawRect(r, paint);
-    }
-
-    void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
-        SkRegion    rgn;
-
-        this->build_rgn(&rgn, op);
-
-        {
-            SkRegion tmp, tmp2(rgn);
-
-            tmp = tmp2;
-            tmp.translate(5, -3);
-
-            {
-                char    buffer[1000];
-                SkDEBUGCODE(size_t  size = ) tmp.writeToMemory(nullptr);
-                SkASSERT(size <= sizeof(buffer));
-                SkDEBUGCODE(size_t  size2 = ) tmp.writeToMemory(buffer);
-                SkASSERT(size == size2);
-
-                SkRegion    tmp3;
-                SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer, 1000);
-                SkASSERT(size == size2);
-
-                SkASSERT(tmp3 == tmp);
-            }
-
-            rgn.translate(20, 30, &tmp);
-            SkASSERT(rgn.isEmpty() || tmp != rgn);
-            tmp.translate(-20, -30);
-            SkASSERT(tmp == rgn);
-        }
-
-        this->drawOrig(canvas, true);
-
-        SkPaint paint;
-        paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
-        paint_rgn(canvas, rgn, paint);
-
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setColor(color);
-        paint_rgn(canvas, rgn, paint);
-    }
-
-    void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
-        SkRegion    rgn;
-        SkPath      path;
-
-        this->build_rgn(&rgn, op);
-        rgn.getBoundaryPath(&path);
-
-        this->drawOrig(canvas, true);
-
-        SkPaint paint;
-
-        paint.setStyle(SkPaint::kFill_Style);
-        paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
-        canvas->drawPath(path, paint);
-        paint.setColor(color);
-        paint.setStyle(SkPaint::kStroke_Style);
-        canvas->drawPath(path, paint);
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        if (false) { // avoid bit rot, suppress warning
-            test_strokerect(canvas);
-            return;
-        }
-        if (false) { // avoid bit rot, suppress warning
-            test_text(canvas);
-            return;
-        }
-
-        const SkPoint origins[] = {
-            { 30*SK_Scalar1, 50*SK_Scalar1 },
-            { 150*SK_Scalar1, 50*SK_Scalar1 },
-        };
-        this->drawPredicates(canvas, origins);
-
-        static const struct {
-            SkColor         fColor;
-            const char*     fName;
-            SkRegion::Op    fOp;
-        } gOps[] = {
-            { SK_ColorBLACK,    "Difference",   SkRegion::kDifference_Op    },
-            { SK_ColorRED,      "Intersect",    SkRegion::kIntersect_Op     },
-            { 0xFF008800,       "Union",        SkRegion::kUnion_Op         },
-            { SK_ColorBLUE,     "XOR",          SkRegion::kXOR_Op           }
-        };
-
-        SkFont font;
-        font.setSize(SK_Scalar1*24);
-
-        this->drawOrig(canvas, false);
-        canvas->save();
-            canvas->translate(SkIntToScalar(200), 0);
-            this->drawRgnOped(canvas, SkRegion::kUnion_Op, SK_ColorBLACK);
-        canvas->restore();
-
-        canvas->translate(0, SkIntToScalar(200));
-
-        for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
-            canvas->drawSimpleText(gOps[op].fName, strlen(gOps[op].fName), SkTextEncoding::kUTF8,
-                                   SkIntToScalar(75), SkIntToScalar(50), font, SkPaint());
-
-            this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);
-
-            canvas->save();
-            canvas->translate(0, SkIntToScalar(200));
-            this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor);
-            canvas->restore();
-
-            canvas->translate(SkIntToScalar(200), 0);
-        }
-    }
-
-    virtual Sample::Click* onFindClickHandler(SkScalar x, SkScalar y,
-                                              skui::ModifierKey modi) override {
-        return fRect.contains(SkScalarRoundToInt(x),
-                              SkScalarRoundToInt(y)) ? new Click() : nullptr;
-    }
-
-    bool onClick(Click* click) override {
-        fRect.offset(click->fCurr.fX - click->fPrev.fX,
-                     click->fCurr.fY - click->fPrev.fY);
-        return true;
-    }
-
-private:
-    SkIRect    fBase, fRect;
-
-    typedef Sample INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE( return new RegionView(); )
diff --git a/third_party/skia/samplecode/SampleRepeatTile.cpp b/third_party/skia/samplecode/SampleRepeatTile.cpp
index 3b7dea0..4022db6 100644
--- a/third_party/skia/samplecode/SampleRepeatTile.cpp
+++ b/third_party/skia/samplecode/SampleRepeatTile.cpp
@@ -35,7 +35,7 @@
     SkBitmap bm;
     make_bitmap(&bm);
 
-    paint->setShader(bm.makeShader(tm, tm));
+    paint->setShader(bm.makeShader(tm, tm, SkSamplingOptions()));
 }
 
 class RepeatTileView : public Sample {
@@ -57,7 +57,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleSG.cpp b/third_party/skia/samplecode/SampleSG.cpp
index bb2bf89..1fa8994 100644
--- a/third_party/skia/samplecode/SampleSG.cpp
+++ b/third_party/skia/samplecode/SampleSG.cpp
@@ -52,7 +52,7 @@
     SampleSG() {
         fGroup = sksg::Group::Make();
 
-        fScene = sksg::Scene::Make(fGroup, sksg::AnimatorList());
+        fScene = sksg::Scene::Make(fGroup);
 
         auto r = sksg::Rect::Make({20, 20, 400, 300});
         auto p = sksg::Color::Make(SK_ColorRED);
@@ -101,7 +101,7 @@
 
 private:
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleSVGFile.cpp b/third_party/skia/samplecode/SampleSVGFile.cpp
index 9fb62ed..b607d9a 100644
--- a/third_party/skia/samplecode/SampleSVGFile.cpp
+++ b/third_party/skia/samplecode/SampleSVGFile.cpp
@@ -7,11 +7,12 @@
 
 #include "include/core/SkTypes.h"
 
-#ifdef SK_XML
+#if defined(SK_ENABLE_SVG)
 
-#include "experimental/svg/model/SkSVGDOM.h"
 #include "include/core/SkCanvas.h"
 #include "include/core/SkStream.h"
+#include "modules/svg/include/SkSVGDOM.h"
+#include "modules/svg/include/SkSVGNode.h"
 #include "samplecode/Sample.h"
 #include "src/core/SkOSFile.h"
 #include "src/utils/SkOSPath.h"
@@ -29,17 +30,11 @@
     void onOnceBeforeDraw() override {
         SkFILEStream svgStream(fPath.c_str());
         if (!svgStream.isValid()) {
-            SkDebugf("file not found: \"path\"\n", fPath.c_str());
+            SkDebugf("file not found: \"%s\"\n", fPath.c_str());
             return;
         }
 
-        SkDOM xmlDom;
-        if (!xmlDom.build(svgStream)) {
-            SkDebugf("XML parsing failed: \"path\"\n", fPath.c_str());
-            return;
-        }
-
-        fDom = SkSVGDOM::MakeFromDOM(xmlDom);
+        fDom = SkSVGDOM::MakeFromStream(svgStream);
         if (fDom) {
             fDom->setContainerSize(SkSize::Make(this->width(), this->height()));
         }
@@ -66,7 +61,7 @@
     SkString        fPath;
     SkString        fLabel;
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 } // anonymous namespace
@@ -75,4 +70,4 @@
 Sample* CreateSampleSVGFileView(const SkString& filename) {
     return new SVGFileView(filename);
 }
-#endif  // SK_XML
+#endif  // defined(SK_ENABLE_SVG)
diff --git a/third_party/skia/samplecode/SampleShaders.cpp b/third_party/skia/samplecode/SampleShaders.cpp
deleted file mode 100644
index fb3fec9..0000000
--- a/third_party/skia/samplecode/SampleShaders.cpp
+++ /dev/null
@@ -1,82 +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 "include/core/SkCanvas.h"
-#include "include/core/SkShader.h"
-#include "include/effects/SkGradientShader.h"
-#include "samplecode/DecodeFile.h"
-#include "samplecode/Sample.h"
-#include "tools/Resources.h"
-
-namespace {
-static sk_sp<SkShader> make_bitmapfade(const SkBitmap& bm) {
-    SkPoint pts[2] = {
-        {0, 0},
-        {0, (float)bm.height()},
-    };
-    SkColor colors[2] = {
-        SkColorSetARGB(255, 0, 0, 0),
-        SkColorSetARGB(0,   0, 0, 0),
-    };
-    return SkShaders::Blend(SkBlendMode::kDstIn,
-                            bm.makeShader(),
-                            SkGradientShader::MakeLinear(pts, colors, nullptr, 2,
-                                                         SkTileMode::kClamp));
-}
-
-static sk_sp<SkShader> make_blend_shader() {
-    SkPoint pts[2];
-    SkColor colors[2];
-
-    pts[0].set(0, 0);
-    pts[1].set(SkIntToScalar(100), 0);
-    colors[0] = SK_ColorRED;
-    colors[1] = SK_ColorBLUE;
-    auto shaderA = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);
-
-    pts[0].set(0, 0);
-    pts[1].set(0, SkIntToScalar(100));
-    colors[0] = SK_ColorBLACK;
-    colors[1] = SkColorSetARGB(0x80, 0, 0, 0);
-    auto shaderB = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);
-
-    return SkShaders::Blend(SkBlendMode::kDstIn, std::move(shaderA), std::move(shaderB));
-}
-
-struct ShaderView : public Sample {
-    sk_sp<SkShader> fShader;
-    sk_sp<SkShader> fShaderFade;
-    SkBitmap        fBitmap;
-
-    void onOnceBeforeDraw() override {
-        decode_file(GetResourceAsData("images/dog.jpg"), &fBitmap);
-        fShader = make_blend_shader();
-        fShaderFade = make_bitmapfade(fBitmap);
-    }
-
-    SkString name() override { return SkString("Shaders"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawBitmap(fBitmap, 0, 0);
-        canvas->translate(20, 120);
-
-        SkPaint paint;
-        paint.setColor(SK_ColorGREEN);
-        canvas->drawRect(SkRect{0, 0, 100, 100}, paint);
-        paint.setShader(fShader);
-        canvas->drawRect(SkRect{0, 0, 100, 100}, paint);
-
-        canvas->translate(SkIntToScalar(110), 0);
-
-        paint.setShader(nullptr);
-        canvas->drawRect(SkRect{0, 0, 120, 80}, paint);
-        paint.setShader(fShaderFade);
-        canvas->drawRect(SkRect{0, 0, 120, 80}, paint);
-    }
-};
-}  // namespace
-DEF_SAMPLE( return new ShaderView(); )
diff --git a/third_party/skia/samplecode/SampleShadowColor.cpp b/third_party/skia/samplecode/SampleShadowColor.cpp
index e0dd7fe..7313bda 100644
--- a/third_party/skia/samplecode/SampleShadowColor.cpp
+++ b/third_party/skia/samplecode/SampleShadowColor.cpp
@@ -74,11 +74,11 @@
                     handled = true;
                     break;
                 case '>':
-                    fZIndex = SkTMin(9, fZIndex+1);
+                    fZIndex = std::min(9, fZIndex+1);
                     handled = true;
                     break;
                 case '<':
-                    fZIndex = SkTMax(0, fZIndex-1);
+                    fZIndex = std::max(0, fZIndex-1);
                     handled = true;
                     break;
                 default:
@@ -115,9 +115,9 @@
             if (paint.getColor() != SK_ColorBLACK) {
                 SkColor color = paint.getColor();
 
-                uint8_t max = SkTMax(SkTMax(SkColorGetR(color), SkColorGetG(color)),
+                uint8_t max = std::max(std::max(SkColorGetR(color), SkColorGetG(color)),
                                      SkColorGetB(color));
-                uint8_t min = SkTMin(SkTMin(SkColorGetR(color), SkColorGetG(color)),
+                uint8_t min = std::min(std::min(SkColorGetR(color), SkColorGetG(color)),
                                      SkColorGetB(color));
                 SkScalar luminance = 0.5f*(max + min) / 255.f;
                 SkScalar alpha = (.6 - .4*luminance)*luminance*luminance + 0.3f;
@@ -221,7 +221,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleShadowReference.cpp b/third_party/skia/samplecode/SampleShadowReference.cpp
index db8e9db..76439fb 100644
--- a/third_party/skia/samplecode/SampleShadowReference.cpp
+++ b/third_party/skia/samplecode/SampleShadowReference.cpp
@@ -191,7 +191,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleShadowUtils.cpp b/third_party/skia/samplecode/SampleShadowUtils.cpp
index 86151c1..e313875 100644
--- a/third_party/skia/samplecode/SampleShadowUtils.cpp
+++ b/third_party/skia/samplecode/SampleShadowUtils.cpp
@@ -9,7 +9,6 @@
 #include "include/core/SkColorFilter.h"
 #include "include/core/SkPath.h"
 #include "include/core/SkPoint3.h"
-#include "include/effects/SkBlurMaskFilter.h"
 #include "include/pathops/SkPathOps.h"
 #include "include/utils/SkCamera.h"
 #include "include/utils/SkShadowUtils.h"
@@ -172,13 +171,11 @@
         SkScalar dy = 0;
         SkTDArray<SkMatrix> matrices;
         matrices.push()->reset();
-        SkMatrix* m = matrices.push();
-        m->setRotate(33.f, 25.f, 25.f);
-        m->postScale(1.2f, 0.8f, 25.f, 25.f);
-        SkPaint paint;
-        paint.setColor(SK_ColorGREEN);
-        paint.setAntiAlias(true);
-        SkPoint3 zPlaneParams = SkPoint3::Make(0, 0, SkTMax(1.0f, kHeight + fZDelta));
+        matrices.push()->setRotate(33.f, 25.f, 25.f).postScale(1.2f, 0.8f, 25.f, 25.f);
+        SkPaint greenPaint;
+        greenPaint.setColor(SK_ColorGREEN);
+        greenPaint.setAntiAlias(true);
+        SkPoint3 zPlaneParams = SkPoint3::Make(0, 0, std::max(1.0f, kHeight + fZDelta));
 
         // convex paths
         for (auto& m : matrices) {
@@ -198,13 +195,13 @@
 
                     canvas->save();
                     canvas->concat(m);
-                    this->drawShadowedPath(canvas, path, zPlaneParams, paint, kAmbientAlpha,
+                    this->drawShadowedPath(canvas, path, zPlaneParams, greenPaint, kAmbientAlpha,
                                            lightPos, kLightR, kSpotAlpha, flags);
                     canvas->restore();
 
                     canvas->translate(dx, 0);
                     x += dx;
-                    dy = SkTMax(dy, postMBounds.height() + kPad + kHeight);
+                    dy = std::max(dy, postMBounds.height() + kPad + kHeight);
                 }
             }
         }
@@ -224,13 +221,13 @@
 
                 canvas->save();
                 canvas->concat(m);
-                this->drawShadowedPath(canvas, path, zPlaneParams, paint, kAmbientAlpha, lightPos,
-                                       kLightR, kSpotAlpha, kNone_ShadowFlag);
+                this->drawShadowedPath(canvas, path, zPlaneParams, greenPaint, kAmbientAlpha,
+                                       lightPos, kLightR, kSpotAlpha, kNone_ShadowFlag);
                 canvas->restore();
 
                 canvas->translate(dx, 0);
                 x += dx;
-                dy = SkTMax(dy, postMBounds.height() + kPad + kHeight);
+                dy = std::max(dy, postMBounds.height() + kPad + kHeight);
             }
         }
 
@@ -239,16 +236,16 @@
         if (invCanvasM.invert(&invCanvasM)) {
             canvas->save();
             canvas->concat(invCanvasM);
-            SkPaint paint;
-            paint.setColor(SK_ColorBLACK);
-            paint.setAntiAlias(true);
-            canvas->drawCircle(lightPos.fX, lightPos.fY, kLightR / 10.f, paint);
+            SkPaint blackPaint;
+            blackPaint.setColor(SK_ColorBLACK);
+            blackPaint.setAntiAlias(true);
+            canvas->drawCircle(lightPos.fX, lightPos.fY, kLightR / 10.f, blackPaint);
             canvas->restore();
         }
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleShip.cpp b/third_party/skia/samplecode/SampleShip.cpp
index 8fdc9eb..a20f8b2 100644
--- a/third_party/skia/samplecode/SampleShip.cpp
+++ b/third_party/skia/samplecode/SampleShip.cpp
@@ -10,6 +10,7 @@
 #include "include/core/SkRSXform.h"
 #include "include/core/SkSurface.h"
 #include "samplecode/Sample.h"
+#include "src/core/SkPaintPriv.h"
 #include "tools/Resources.h"
 #include "tools/timer/Timer.h"
 
@@ -20,25 +21,26 @@
 static const int kHeight = 640;
 
 typedef void (*DrawAtlasProc)(SkCanvas*, SkImage*, const SkRSXform[], const SkRect[],
-const SkColor[], int, const SkRect*, const SkPaint*);
+const SkColor[], int, const SkRect*, const SkSamplingOptions&, const SkPaint*);
 
 static void draw_atlas(SkCanvas* canvas, SkImage* atlas, const SkRSXform xform[],
                        const SkRect tex[], const SkColor colors[], int count, const SkRect* cull,
-                       const SkPaint* paint) {
-    canvas->drawAtlas(atlas, xform, tex, colors, count, SkBlendMode::kModulate, cull, paint);
+                       const SkSamplingOptions& sampling, const SkPaint* paint) {
+    canvas->drawAtlas(atlas, xform, tex, colors, count, SkBlendMode::kModulate, sampling,
+                      cull, paint);
 }
 
 static void draw_atlas_sim(SkCanvas* canvas, SkImage* atlas, const SkRSXform xform[],
                            const SkRect tex[], const SkColor colors[], int count, const SkRect* cull,
-                           const SkPaint* paint) {
+                           const SkSamplingOptions& sampling, const SkPaint* paint) {
     for (int i = 0; i < count; ++i) {
         SkMatrix matrix;
         matrix.setRSXform(xform[i]);
 
         canvas->save();
         canvas->concat(matrix);
-        canvas->drawImageRect(atlas, tex[i], tex[i].makeOffset(-tex[i].x(), -tex[i].y()), paint,
-                              SkCanvas::kFast_SrcRectConstraint);
+        canvas->drawImageRect(atlas, tex[i], tex[i].makeOffset(-tex[i].x(), -tex[i].y()),
+                              sampling, paint, SkCanvas::kFast_SrcRectConstraint);
         canvas->restore();
     }
 }
@@ -96,7 +98,6 @@
         }
 
         SkPaint paint;
-        paint.setFilterQuality(kLow_SkFilterQuality);
         paint.setColor(SK_ColorWHITE);
 
         SkScalar anchorX = fAtlas->width()*0.5f;
@@ -117,7 +118,8 @@
             fXform[i].fTy += dy;
         }
 
-        fProc(canvas, fAtlas.get(), fXform, fTex, nullptr, kGrid*kGrid+1, nullptr, &paint);
+        fProc(canvas, fAtlas.get(), fXform, fTex, nullptr, kGrid*kGrid+1, nullptr,
+              SkSamplingOptions(SkFilterMode::kLinear), &paint);
     }
 
     bool onAnimate(double nanos) override {
@@ -135,7 +137,7 @@
     SkRSXform   fXform[kGrid*kGrid+1];
     SkRect      fTex[kGrid*kGrid+1];
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleSimpleStroker.cpp b/third_party/skia/samplecode/SampleSimpleStroker.cpp
new file mode 100644
index 0000000..651d2ed
--- /dev/null
+++ b/third_party/skia/samplecode/SampleSimpleStroker.cpp
@@ -0,0 +1,482 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkBitmap.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkPath.h"
+#include "include/utils/SkParsePath.h"
+#include "samplecode/Sample.h"
+
+#include "src/core/SkGeometry.h"
+
+namespace {
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkPoint rotate90(const SkPoint& p) { return {p.fY, -p.fX}; }
+static SkPoint rotate180(const SkPoint& p) { return p * -1; }
+static SkPoint setLength(SkPoint p, float len) {
+    if (!p.setLength(len)) {
+        SkDebugf("Failed to set point length\n");
+    }
+    return p;
+}
+static bool isClockwise(const SkPoint& a, const SkPoint& b) { return a.cross(b) > 0; }
+
+//////////////////////////////////////////////////////////////////////////////
+// Testing ground for a new stroker implementation
+
+/** Helper class for constructing paths, with undo support */
+class PathRecorder {
+public:
+    SkPath getPath() const {
+        return SkPath::Make(fPoints.data(), fPoints.size(), fVerbs.data(), fVerbs.size(), nullptr,
+                            0, SkPathFillType::kWinding);
+    }
+
+    void moveTo(SkPoint p) {
+        fVerbs.push_back(SkPath::kMove_Verb);
+        fPoints.push_back(p);
+    }
+
+    void lineTo(SkPoint p) {
+        fVerbs.push_back(SkPath::kLine_Verb);
+        fPoints.push_back(p);
+    }
+
+    void close() { fVerbs.push_back(SkPath::kClose_Verb); }
+
+    void rewind() {
+        fVerbs.clear();
+        fPoints.clear();
+    }
+
+    int countPoints() const { return fPoints.size(); }
+
+    int countVerbs() const { return fVerbs.size(); }
+
+    bool getLastPt(SkPoint* lastPt) const {
+        if (fPoints.empty()) {
+            return false;
+        }
+        *lastPt = fPoints.back();
+        return true;
+    }
+
+    void setLastPt(SkPoint lastPt) {
+        if (fPoints.empty()) {
+            moveTo(lastPt);
+        } else {
+            fPoints.back().set(lastPt.fX, lastPt.fY);
+        }
+    }
+
+    const std::vector<uint8_t>& verbs() const { return fVerbs; }
+
+    const std::vector<SkPoint>& points() const { return fPoints; }
+
+private:
+    std::vector<uint8_t> fVerbs;
+    std::vector<SkPoint> fPoints;
+};
+
+class SkPathStroker2 {
+public:
+    // Returns the fill path
+    SkPath getFillPath(const SkPath& path, const SkPaint& paint);
+
+private:
+    struct PathSegment {
+        SkPath::Verb fVerb;
+        SkPoint fPoints[4];
+    };
+
+    float fRadius;
+    SkPaint::Cap fCap;
+    SkPaint::Join fJoin;
+    PathRecorder fInner, fOuter;
+
+    // Initialize stroker state
+    void initForPath(const SkPath& path, const SkPaint& paint);
+
+    // Strokes a line segment
+    void strokeLine(const PathSegment& line, bool needsMove);
+
+    // Adds an endcap to fOuter
+    enum class CapLocation { Start, End };
+    void endcap(CapLocation loc);
+
+    // Adds a join between the two segments
+    void join(const PathSegment& prev, const PathSegment& curr);
+
+    // Appends path in reverse to result
+    static void appendPathReversed(const PathRecorder& path, PathRecorder* result);
+
+    // Returns the segment unit normal
+    static SkPoint unitNormal(const PathSegment& seg, float t);
+
+    // Returns squared magnitude of line segments.
+    static float squaredLineLength(const PathSegment& lineSeg);
+};
+
+void SkPathStroker2::initForPath(const SkPath& path, const SkPaint& paint) {
+    fRadius = paint.getStrokeWidth() / 2;
+    fCap = paint.getStrokeCap();
+    fJoin = paint.getStrokeJoin();
+    fInner.rewind();
+    fOuter.rewind();
+}
+
+SkPath SkPathStroker2::getFillPath(const SkPath& path, const SkPaint& paint) {
+    initForPath(path, paint);
+
+    // Trace the inner and outer paths simultaneously. Inner will therefore be
+    // recorded in reverse from how we trace the outline.
+    SkPath::Iter it(path, false);
+    PathSegment segment, prevSegment;
+    bool firstSegment = true;
+    while ((segment.fVerb = it.next(segment.fPoints)) != SkPath::kDone_Verb) {
+        // Join to the previous segment
+        if (!firstSegment) {
+            join(prevSegment, segment);
+        }
+
+        // Stroke the current segment
+        switch (segment.fVerb) {
+            case SkPath::kLine_Verb:
+                strokeLine(segment, firstSegment);
+                break;
+            case SkPath::kMove_Verb:
+                // Don't care about multiple contours currently
+                continue;
+            default:
+                SkDebugf("Unhandled path verb %d\n", segment.fVerb);
+                break;
+        }
+
+        std::swap(segment, prevSegment);
+        firstSegment = false;
+    }
+
+    // Open contour => endcap at the end
+    const bool isClosed = path.isLastContourClosed();
+    if (isClosed) {
+        SkDebugf("Unhandled closed contour\n");
+    } else {
+        endcap(CapLocation::End);
+    }
+
+    // Walk inner path in reverse, appending to result
+    appendPathReversed(fInner, &fOuter);
+    endcap(CapLocation::Start);
+
+    return fOuter.getPath();
+}
+
+void SkPathStroker2::strokeLine(const PathSegment& line, bool needsMove) {
+    const SkPoint tangent = line.fPoints[1] - line.fPoints[0];
+    const SkPoint normal = rotate90(tangent);
+    const SkPoint offset = setLength(normal, fRadius);
+    if (needsMove) {
+        fOuter.moveTo(line.fPoints[0] + offset);
+        fInner.moveTo(line.fPoints[0] - offset);
+    }
+    fOuter.lineTo(line.fPoints[1] + offset);
+    fInner.lineTo(line.fPoints[1] - offset);
+}
+
+void SkPathStroker2::endcap(CapLocation loc) {
+    const auto buttCap = [this](CapLocation loc) {
+        if (loc == CapLocation::Start) {
+            // Back at the start of the path: just close the stroked outline
+            fOuter.close();
+        } else {
+            // Inner last pt == first pt when appending in reverse
+            SkPoint innerLastPt;
+            fInner.getLastPt(&innerLastPt);
+            fOuter.lineTo(innerLastPt);
+        }
+    };
+
+    switch (fCap) {
+        case SkPaint::kButt_Cap:
+            buttCap(loc);
+            break;
+        default:
+            SkDebugf("Unhandled endcap %d\n", fCap);
+            buttCap(loc);
+            break;
+    }
+}
+
+void SkPathStroker2::join(const PathSegment& prev, const PathSegment& curr) {
+    const auto miterJoin = [this](const PathSegment& prev, const PathSegment& curr) {
+        // Common path endpoint of the two segments is the midpoint of the miter line.
+        const SkPoint miterMidpt = curr.fPoints[0];
+
+        SkPoint before = unitNormal(prev, 1);
+        SkPoint after = unitNormal(curr, 0);
+
+        // Check who's inside and who's outside.
+        PathRecorder *outer = &fOuter, *inner = &fInner;
+        if (!isClockwise(before, after)) {
+            std::swap(inner, outer);
+            before = rotate180(before);
+            after = rotate180(after);
+        }
+
+        const float cosTheta = before.dot(after);
+        if (SkScalarNearlyZero(1 - cosTheta)) {
+            // Nearly identical normals: don't bother.
+            return;
+        }
+
+        // Before and after have the same origin and magnitude, so before+after is the diagonal of
+        // their rhombus. Origin of this vector is the midpoint of the miter line.
+        SkPoint miterVec = before + after;
+
+        // Note the relationship (draw a right triangle with the miter line as its hypoteneuse):
+        //     sin(theta/2) = strokeWidth / miterLength
+        // so miterLength = strokeWidth / sin(theta/2)
+        // where miterLength is the length of the miter from outer point to inner corner.
+        // miterVec's origin is the midpoint of the miter line, so we use strokeWidth/2.
+        // Sqrt is just an application of half-angle identities.
+        const float sinHalfTheta = sqrtf(0.5 * (1 + cosTheta));
+        const float halfMiterLength = fRadius / sinHalfTheta;
+        miterVec.setLength(halfMiterLength);  // TODO: miter length limit
+
+        // Outer: connect to the miter point, and then to t=0 (on outside stroke) of next segment.
+        const SkPoint dest = setLength(after, fRadius);
+        outer->lineTo(miterMidpt + miterVec);
+        outer->lineTo(miterMidpt + dest);
+
+        // Inner miter is more involved. We're already at t=1 (on inside stroke) of 'prev'.
+        // Check 2 cases to see we can directly connect to the inner miter point
+        // (midpoint - miterVec), or if we need to add extra "loop" geometry.
+        const SkPoint prevUnitTangent = rotate90(before);
+        const float radiusSquared = fRadius * fRadius;
+        // 'alpha' is angle between prev tangent and the curr inwards normal
+        const float cosAlpha = prevUnitTangent.dot(-after);
+        // Solve triangle for len^2:  radius^2 = len^2 + (radius * sin(alpha))^2
+        // This is the point at which the inside "corner" of curr at t=0 will lie on a
+        // line connecting the inner and outer corners of prev at t=0. If len is below
+        // this threshold, the inside corner of curr will "poke through" the start of prev,
+        // and we'll need the inner loop geometry.
+        const float threshold1 = radiusSquared * cosAlpha * cosAlpha;
+        // Solve triangle for len^2:  halfMiterLen^2 = radius^2 + len^2
+        // This is the point at which the inner miter point will lie on the inner stroke
+        // boundary of the curr segment. If len is below this threshold, the miter point
+        // moves 'inside' of the stroked outline, and we'll need the inner loop geometry.
+        const float threshold2 = halfMiterLength * halfMiterLength - radiusSquared;
+        // If a segment length is smaller than the larger of the two thresholds,
+        // we'll have to add the inner loop geometry.
+        const float maxLenSqd = std::max(threshold1, threshold2);
+        const bool needsInnerLoop =
+                squaredLineLength(prev) < maxLenSqd || squaredLineLength(curr) < maxLenSqd;
+        if (needsInnerLoop) {
+            // Connect to the miter midpoint (common path endpoint of the two segments),
+            // and then to t=0 (on inside) of the next segment. This adds an interior "loop" of
+            // geometry that handles edge cases where segment lengths are shorter than the
+            // stroke width.
+            inner->lineTo(miterMidpt);
+            inner->lineTo(miterMidpt - dest);
+        } else {
+            // Directly connect to inner miter point.
+            inner->setLastPt(miterMidpt - miterVec);
+        }
+    };
+
+    switch (fJoin) {
+        case SkPaint::kMiter_Join:
+            miterJoin(prev, curr);
+            break;
+        default:
+            SkDebugf("Unhandled join %d\n", fJoin);
+            miterJoin(prev, curr);
+            break;
+    }
+}
+
+void SkPathStroker2::appendPathReversed(const PathRecorder& path, PathRecorder* result) {
+    const int numVerbs = path.countVerbs();
+    const int numPoints = path.countPoints();
+    const std::vector<uint8_t>& verbs = path.verbs();
+    const std::vector<SkPoint>& points = path.points();
+
+    for (int i = numVerbs - 1, j = numPoints; i >= 0; i--) {
+        auto verb = static_cast<SkPath::Verb>(verbs[i]);
+        switch (verb) {
+            case SkPath::kLine_Verb: {
+                j -= 1;
+                SkASSERT(j >= 1);
+                result->lineTo(points[j - 1]);
+                break;
+            }
+            case SkPath::kMove_Verb:
+                // Ignore
+                break;
+            default:
+                SkASSERT(false);
+                break;
+        }
+    }
+}
+
+SkPoint SkPathStroker2::unitNormal(const PathSegment& seg, float t) {
+    if (seg.fVerb != SkPath::kLine_Verb) {
+        SkDebugf("Unhandled verb for unit normal %d\n", seg.fVerb);
+    }
+
+    (void)t;  // Not needed for lines
+    const SkPoint tangent = seg.fPoints[1] - seg.fPoints[0];
+    const SkPoint normal = rotate90(tangent);
+    return setLength(normal, 1);
+}
+
+float SkPathStroker2::squaredLineLength(const PathSegment& lineSeg) {
+    SkASSERT(lineSeg.fVerb == SkPath::kLine_Verb);
+    const SkPoint diff = lineSeg.fPoints[1] - lineSeg.fPoints[0];
+    return diff.dot(diff);
+}
+
+}  // namespace
+
+//////////////////////////////////////////////////////////////////////////////
+
+class SimpleStroker : public Sample {
+    bool fShowSkiaStroke, fShowHidden, fShowSkeleton;
+    float fWidth = 175;
+    SkPaint fPtsPaint, fStrokePaint, fMirrorStrokePaint, fNewFillPaint, fHiddenPaint,
+            fSkeletonPaint;
+    inline static constexpr int kN = 3;
+
+public:
+    SkPoint fPts[kN];
+
+    SimpleStroker() : fShowSkiaStroke(true), fShowHidden(true), fShowSkeleton(true) {
+        fPts[0] = {500, 200};
+        fPts[1] = {300, 200};
+        fPts[2] = {100, 100};
+
+        fPtsPaint.setAntiAlias(true);
+        fPtsPaint.setStrokeWidth(10);
+        fPtsPaint.setStrokeCap(SkPaint::kRound_Cap);
+
+        fStrokePaint.setAntiAlias(true);
+        fStrokePaint.setStyle(SkPaint::kStroke_Style);
+        fStrokePaint.setColor(0x80FF0000);
+
+        fMirrorStrokePaint.setAntiAlias(true);
+        fMirrorStrokePaint.setStyle(SkPaint::kStroke_Style);
+        fMirrorStrokePaint.setColor(0x80FFFF00);
+
+        fNewFillPaint.setAntiAlias(true);
+        fNewFillPaint.setColor(0x8000FF00);
+
+        fHiddenPaint.setAntiAlias(true);
+        fHiddenPaint.setStyle(SkPaint::kStroke_Style);
+        fHiddenPaint.setColor(0xFF0000FF);
+
+        fSkeletonPaint.setAntiAlias(true);
+        fSkeletonPaint.setStyle(SkPaint::kStroke_Style);
+        fSkeletonPaint.setColor(SK_ColorRED);
+    }
+
+    void toggle(bool& value) { value = !value; }
+
+protected:
+    SkString name() override { return SkString("SimpleStroker"); }
+
+    bool onChar(SkUnichar uni) override {
+        switch (uni) {
+            case '1':
+                this->toggle(fShowSkeleton);
+                return true;
+            case '2':
+                this->toggle(fShowSkiaStroke);
+                return true;
+            case '3':
+                this->toggle(fShowHidden);
+                return true;
+            case '-':
+                fWidth -= 5;
+                return true;
+            case '=':
+                fWidth += 5;
+                return true;
+            default:
+                break;
+        }
+        return false;
+    }
+
+    void makePath(SkPath* path) {
+        path->moveTo(fPts[0]);
+        for (int i = 1; i < kN; ++i) {
+            path->lineTo(fPts[i]);
+        }
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        canvas->drawColor(0xFFEEEEEE);
+
+        SkPath path;
+        this->makePath(&path);
+
+        fStrokePaint.setStrokeWidth(fWidth);
+
+        // The correct result
+        if (fShowSkiaStroke) {
+            canvas->drawPath(path, fStrokePaint);
+        }
+
+        // Simple stroker result
+        SkPathStroker2 stroker;
+        SkPath fillPath = stroker.getFillPath(path, fStrokePaint);
+        canvas->drawPath(fillPath, fNewFillPaint);
+
+        if (fShowHidden) {
+            canvas->drawPath(fillPath, fHiddenPaint);
+        }
+        if (fShowSkeleton) {
+            canvas->drawPath(path, fSkeletonPaint);
+        }
+        canvas->drawPoints(SkCanvas::kPoints_PointMode, kN, fPts, fPtsPaint);
+
+        // Draw a mirror but using Skia's stroker.
+        canvas->translate(0, 400);
+        fMirrorStrokePaint.setStrokeWidth(fWidth);
+        canvas->drawPath(path, fMirrorStrokePaint);
+        if (fShowHidden) {
+            SkPath hidden;
+            fStrokePaint.getFillPath(path, &hidden);
+            canvas->drawPath(hidden, fHiddenPaint);
+        }
+        if (fShowSkeleton) {
+            canvas->drawPath(path, fSkeletonPaint);
+        }
+    }
+
+    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
+        const SkScalar tol = 4;
+        const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
+        for (int i = 0; i < kN; ++i) {
+            if (r.intersects(SkRect::MakeXYWH(fPts[i].fX, fPts[i].fY, 1, 1))) {
+                return new Click([this, i](Click* c) {
+                    fPts[i] = c->fCurr;
+                    return true;
+                });
+            }
+        }
+        return nullptr;
+    }
+
+private:
+    using INHERITED = Sample;
+};
+
+DEF_SAMPLE(return new SimpleStroker;)
diff --git a/third_party/skia/samplecode/SampleSlides.cpp b/third_party/skia/samplecode/SampleSlides.cpp
index d58f4f1..a17a674 100644
--- a/third_party/skia/samplecode/SampleSlides.cpp
+++ b/third_party/skia/samplecode/SampleSlides.cpp
@@ -7,7 +7,6 @@
 #include "include/core/SkCanvas.h"
 #include "include/core/SkPaint.h"
 #include "include/core/SkVertices.h"
-#include "include/effects/SkBlurMaskFilter.h"
 #include "include/effects/SkGradientShader.h"
 #include "samplecode/Sample.h"
 #include "src/core/SkBlurMask.h"
@@ -246,7 +245,7 @@
 
     decode_file("/skimages/logo.gif", &bm);
     size->set(bm.width(), bm.height());
-    return bm.makeShader();
+    return bm.makeShader(SkSamplingOptions(SkFilterMode::kLinear));
 }
 
 static sk_sp<SkShader> make_shader1(const SkIPoint& size) {
@@ -363,7 +362,6 @@
 
     SkPaint paint;
     paint.setDither(true);
-    paint.setFilterQuality(kLow_SkFilterQuality);
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) {
         auto verts = SkVertices::MakeCopy(fRecs[i].fMode, fRecs[i].fCount,
@@ -429,7 +427,7 @@
             gProc[i](&canvas);
             canvas.restore();
             SkString str;
-            str.printf("/skimages/slide_" SK_SIZE_T_SPECIFIER ".png", i);
+            str.printf("/skimages/slide_%zu.png", i);
             ToolUtils::EncodeImageToFile(str.c_str(), bm, SkEncodedImageFormat::kPNG, 100);
         }
         this->setBGColor(BG_COLOR);
@@ -450,7 +448,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleStringArt.cpp b/third_party/skia/samplecode/SampleStringArt.cpp
index bda466c..9f2b3b1 100644
--- a/third_party/skia/samplecode/SampleStringArt.cpp
+++ b/third_party/skia/samplecode/SampleStringArt.cpp
@@ -32,7 +32,7 @@
         SkPath path;
         path.moveTo(center);
 
-        while (length < (SkScalarHalf(SkMinScalar(this->width(), this->height())) - 10.f))
+        while (length < (SkScalarHalf(std::min(this->width(), this->height())) - 10.f))
         {
             SkPoint rp = SkPoint::Make(length*SkScalarCos(step) + center.fX,
                                        length*SkScalarSin(step) + center.fY);
@@ -57,7 +57,7 @@
 private:
 
     SkScalar fAngle;
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleStrokePath.cpp b/third_party/skia/samplecode/SampleStrokePath.cpp
index b22ba8d..5feb87c 100644
--- a/third_party/skia/samplecode/SampleStrokePath.cpp
+++ b/third_party/skia/samplecode/SampleStrokePath.cpp
@@ -35,9 +35,7 @@
     paint.setStrokeJoin(SkPaint::kRound_Join);
     paint.setStyle(SkPaint::kStroke_Style);
 
-    SkMatrix matrix;
-    matrix.setRectToRect(srcR, dstR, SkMatrix::kCenter_ScaleToFit);
-    canvas->concat(matrix);
+    canvas->concat(SkMatrix::RectToRect(srcR, dstR, SkMatrix::kCenter_ScaleToFit));
 
     canvas->drawPath(path, paint);
 }
@@ -140,7 +138,8 @@
     }
 
     void onDrawContent(SkCanvas* canvas) override {
-        test_huge_stroke(canvas); return;
+        test_huge_stroke(canvas);
+#if 0
         canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
 
         SkPaint paint;
@@ -198,10 +197,11 @@
         canvas->translate(0, fPath.getBounds().height() * 5 / 4);
         fPath.setFillType(SkPathFillType::kEvenOdd);
         drawSet(canvas, &paint);
+#endif
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleStrokeRect.cpp b/third_party/skia/samplecode/SampleStrokeRect.cpp
index 669a357..d4cc4ae 100644
--- a/third_party/skia/samplecode/SampleStrokeRect.cpp
+++ b/third_party/skia/samplecode/SampleStrokeRect.cpp
@@ -13,9 +13,9 @@
     StrokeRectSample() {}
 
 protected:
-    virtual SkString name() { return SkString("Stroke Rects"); }
+    SkString name() override { return SkString("Stroke Rects"); }
 
-    virtual void onDrawContent(SkCanvas* canvas) {
+    void onDrawContent(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setStyle(SkPaint::kStroke_Style);
@@ -56,7 +56,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleStrokeVerb.cpp b/third_party/skia/samplecode/SampleStrokeVerb.cpp
new file mode 100644
index 0000000..bb90ee3
--- /dev/null
+++ b/third_party/skia/samplecode/SampleStrokeVerb.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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 "include/core/SkTypes.h"
+
+#if SK_SUPPORT_GPU
+
+#include "include/core/SkCanvas.h"
+#include "include/core/SkFont.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkPath.h"
+#include "samplecode/Sample.h"
+#include "src/core/SkGeometry.h"
+
+enum class VerbType {
+    kTriangles,
+    kQuadratics,
+    kCubics,
+    kConics
+};
+
+static const char* verb_type_name(VerbType verbType) {
+    switch (verbType) {
+        case VerbType::kTriangles: return "kTriangles";
+        case VerbType::kQuadratics: return "kQuadratics";
+        case VerbType::kCubics: return "kCubics";
+        case VerbType::kConics: return "kConics";
+    }
+    SkUNREACHABLE;
+};
+
+/**
+ * This sample visualizes simple strokes.
+ */
+class StrokeVerbView : public Sample {
+    void onOnceBeforeDraw() override { this->updatePath(); }
+    void onDrawContent(SkCanvas*) override;
+
+    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
+    bool onClick(Sample::Click*) override;
+    bool onChar(SkUnichar) override;
+    SkString name() override { return SkString("StrokeVerb"); }
+
+    class Click;
+
+    void updateAndInval() { this->updatePath(); }
+
+    void updatePath();
+
+    VerbType fVerbType = VerbType::kCubics;
+
+    SkPoint fPoints[4] = {
+            {100.05f, 100.05f}, {400.75f, 100.05f}, {400.75f, 300.95f}, {100.05f, 300.95f}};
+
+    float fConicWeight = .5;
+    float fStrokeWidth = 40;
+    SkPaint::Join fStrokeJoin = SkPaint::kMiter_Join;
+    SkPaint::Cap fStrokeCap = SkPaint::kButt_Cap;
+
+    SkPath fPath;
+};
+
+void StrokeVerbView::onDrawContent(SkCanvas* canvas) {
+    canvas->clear(SK_ColorBLACK);
+
+    SkPaint outlinePaint;
+    outlinePaint.setColor(0xff808080);
+    outlinePaint.setStyle(SkPaint::kStroke_Style);
+    outlinePaint.setStrokeWidth(fStrokeWidth);
+    outlinePaint.setStrokeJoin(fStrokeJoin);
+    outlinePaint.setStrokeCap(fStrokeCap);
+    outlinePaint.setAntiAlias(true);
+    canvas->drawPath(fPath, outlinePaint);
+
+    SkString caption;
+    caption.appendf("VerbType_%s", verb_type_name(fVerbType));
+    if (VerbType::kCubics == fVerbType) {
+        caption.appendf(" (%s)", SkCubicTypeName(SkClassifyCubic(fPoints)));
+    } else if (VerbType::kConics == fVerbType) {
+        caption.appendf(" (w=%f)", fConicWeight);
+    }
+
+    caption.appendf(" (stroke_width=%f)", fStrokeWidth);
+
+    SkPaint pointsPaint;
+    pointsPaint.setColor(SK_ColorBLUE);
+    pointsPaint.setStrokeWidth(8);
+    pointsPaint.setAntiAlias(true);
+
+    if (VerbType::kCubics == fVerbType) {
+        canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
+    } else {
+        canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, fPoints, pointsPaint);
+        canvas->drawPoints(SkCanvas::kPoints_PointMode, 1, fPoints + 3, pointsPaint);
+    }
+
+    SkFont font(nullptr, 20);
+    SkPaint captionPaint;
+    captionPaint.setColor(SK_ColorWHITE);
+    canvas->drawString(caption, 10, 30, font, captionPaint);
+}
+
+void StrokeVerbView::updatePath() {
+    fPath.reset();
+    fPath.moveTo(fPoints[0]);
+    switch (fVerbType) {
+        case VerbType::kCubics:
+            fPath.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
+            break;
+        case VerbType::kQuadratics:
+            fPath.quadTo(fPoints[1], fPoints[3]);
+            break;
+        case VerbType::kConics:
+            fPath.conicTo(fPoints[1], fPoints[3], fConicWeight);
+            break;
+        case VerbType::kTriangles:
+            fPath.lineTo(fPoints[1]);
+            fPath.lineTo(fPoints[3]);
+            fPath.close();
+            break;
+    }
+}
+
+class StrokeVerbView::Click : public Sample::Click {
+public:
+    Click(int ptIdx) : fPtIdx(ptIdx) {}
+
+    void doClick(SkPoint points[]) {
+        if (fPtIdx >= 0) {
+            points[fPtIdx] += fCurr - fPrev;
+        } else {
+            for (int i = 0; i < 4; ++i) {
+                points[i] += fCurr - fPrev;
+            }
+        }
+    }
+
+private:
+    int fPtIdx;
+};
+
+Sample::Click* StrokeVerbView::onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) {
+    for (int i = 0; i < 4; ++i) {
+        if (VerbType::kCubics != fVerbType && 2 == i) {
+            continue;
+        }
+        if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
+            return new Click(i);
+        }
+    }
+    return new Click(-1);
+}
+
+bool StrokeVerbView::onClick(Sample::Click* click) {
+    Click* myClick = (Click*)click;
+    myClick->doClick(fPoints);
+    this->updateAndInval();
+    return true;
+}
+
+bool StrokeVerbView::onChar(SkUnichar unichar) {
+        if (unichar >= '1' && unichar <= '4') {
+            fVerbType = VerbType(unichar - '1');
+            this->updateAndInval();
+            return true;
+        }
+        float* valueToScale = nullptr;
+        if (VerbType::kConics == fVerbType) {
+            valueToScale = &fConicWeight;
+        } else {
+            valueToScale = &fStrokeWidth;
+        }
+        if (valueToScale) {
+            if (unichar == '+') {
+                *valueToScale *= 2;
+                this->updateAndInval();
+                return true;
+            }
+            if (unichar == '+' || unichar == '=') {
+                *valueToScale *= 5/4.f;
+                this->updateAndInval();
+                return true;
+            }
+            if (unichar == '-') {
+                *valueToScale *= 4/5.f;
+                this->updateAndInval();
+                return true;
+            }
+            if (unichar == '_') {
+                *valueToScale *= .5f;
+                this->updateAndInval();
+                return true;
+            }
+        }
+        if (unichar == 'D') {
+            SkDebugf("    SkPoint fPoints[4] = {\n");
+            SkDebugf("        {%ff, %ff},\n", fPoints[0].x(), fPoints[0].y());
+            SkDebugf("        {%ff, %ff},\n", fPoints[1].x(), fPoints[1].y());
+            SkDebugf("        {%ff, %ff},\n", fPoints[2].x(), fPoints[2].y());
+            SkDebugf("        {%ff, %ff}\n", fPoints[3].x(), fPoints[3].y());
+            SkDebugf("    };\n");
+            return true;
+        }
+        if (unichar == 'J') {
+            fStrokeJoin = (SkPaint::Join)((fStrokeJoin + 1) % 3);
+            this->updateAndInval();
+            return true;
+        }
+        if (unichar == 'C') {
+            fStrokeCap = (SkPaint::Cap)((fStrokeCap + 1) % 3);
+            this->updateAndInval();
+            return true;
+        }
+        return false;
+}
+
+DEF_SAMPLE(return new StrokeVerbView;)
+
+#endif  // SK_SUPPORT_GPU
diff --git a/third_party/skia/samplecode/SampleSubpixelTranslate.cpp b/third_party/skia/samplecode/SampleSubpixelTranslate.cpp
deleted file mode 100644
index b658bc3..0000000
--- a/third_party/skia/samplecode/SampleSubpixelTranslate.cpp
+++ /dev/null
@@ -1,112 +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 "samplecode/Sample.h"
-
-#include "include/core/SkCanvas.h"
-#include "include/core/SkColorPriv.h"
-#include "include/core/SkFont.h"
-#include "include/core/SkStream.h"
-#include "include/effects/SkBlurMaskFilter.h"
-#include "include/utils/SkRandom.h"
-#include "samplecode/DecodeFile.h"
-#include "tools/Resources.h"
-
-// Intended to exercise pixel snapping observed with scaled images (and
-// with non-scaled images, but for a different reason):  Bug 1145
-
-class SubpixelTranslateView : public Sample {
-public:
-    SubpixelTranslateView(const char imageFilename[],
-                          float horizontalVelocity,
-                          float verticalVelocity)
-        : fHorizontalVelocity(horizontalVelocity)
-        , fVerticalVelocity(verticalVelocity)
-    {
-        if (!DecodeDataToBitmap(GetResourceAsData(imageFilename), &fBM)) {
-            fBM.allocN32Pixels(1, 1);
-            *(fBM.getAddr32(0,0)) = 0xFF0000FF; // red == bad
-        }
-        fCurPos = SkPoint::Make(0,0);
-        fSize = 200;
-    }
-
-protected:
-    SkBitmap fBM;
-    SkScalar fSize;
-    float fHorizontalVelocity, fVerticalVelocity;
-
-    SkPoint fCurPos;
-
-    SkString name() override { return SkString("SubpixelTranslate"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-
-        static const SkFilterQuality gQualitys[] = {
-            kNone_SkFilterQuality,
-            kLow_SkFilterQuality,
-            kMedium_SkFilterQuality,
-            kHigh_SkFilterQuality
-        };
-
-        SkPaint paint;
-        SkFont font(nullptr, 48);
-        font.setSubpixel(true);
-
-        paint.setAntiAlias(true);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) {
-            paint.setFilterQuality(gQualitys[i]);
-            SkRect r = SkRect::MakeXYWH( fCurPos.fX + i * (fSize + 10), fCurPos.fY, fSize, fSize );
-            canvas->drawBitmapRect( fBM, r, &paint );
-        }
-
-        canvas->drawString("AA Scaled", fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fSize + 10),
-                           fCurPos.fY + fSize/2, font, paint);
-
-        paint.setAntiAlias(false);
-        font.setEdging(SkFont::Edging::kAlias);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) {
-            paint.setFilterQuality(gQualitys[i]);
-            SkRect r = SkRect::MakeXYWH( fCurPos.fX + i * (fSize + 10), fCurPos.fY + fSize + 10, fSize, fSize );
-            canvas->drawBitmapRect( fBM, r, &paint );
-        }
-        canvas->drawString("Scaled", fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fSize + 10),
-                           fCurPos.fY + fSize + 10 + fSize/2, font, paint);
-
-        paint.setAntiAlias(true);
-        font.setEdging(SkFont::Edging::kAntiAlias);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) {
-            paint.setFilterQuality(gQualitys[i]);
-            canvas->drawBitmap( fBM, fCurPos.fX + i * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10), &paint );
-        }
-
-        canvas->drawString("AA No Scale",
-                           fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fBM.width() + 10),
-                           fCurPos.fY + 2*(fSize + 10) + fSize/2, font, paint);
-
-        paint.setAntiAlias(false);
-        font.setEdging(SkFont::Edging::kAlias);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) {
-            paint.setFilterQuality(gQualitys[i]);
-            canvas->drawBitmap( fBM, fCurPos.fX + i * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10) + fBM.height() + 10, &paint );
-        }
-
-        canvas->drawString("No Scale", fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fBM.width() + 10),
-                           fCurPos.fY + 2*(fSize + 10) + fBM.height() + 10 + fSize/2, font, paint);
-
-
-        fCurPos.fX += fHorizontalVelocity;
-        fCurPos.fY += fVerticalVelocity;
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE( return new SubpixelTranslateView("images/mandrill_256.png", .05f, .05f); )
diff --git a/third_party/skia/samplecode/SampleTextBox.cpp b/third_party/skia/samplecode/SampleTextBox.cpp
index 7aa1598..d929cad 100644
--- a/third_party/skia/samplecode/SampleTextBox.cpp
+++ b/third_party/skia/samplecode/SampleTextBox.cpp
@@ -17,7 +17,6 @@
 #include "include/core/SkTextBlob.h"
 #include "include/core/SkTime.h"
 #include "include/core/SkTypeface.h"
-#include "include/effects/SkBlurMaskFilter.h"
 #include "include/effects/SkGradientShader.h"
 #include "include/utils/SkRandom.h"
 #include "modules/skshaper/include/SkShaper.h"
@@ -25,6 +24,8 @@
 #include "src/shaders/SkColorShader.h"
 #include "src/utils/SkUTF.h"
 
+typedef std::unique_ptr<SkShaper> (*ShaperFactory)();
+
 static const char gText[] =
     "When in the Course of human events it becomes necessary for one people "
     "to dissolve the political bands which have connected them with another "
@@ -34,11 +35,14 @@
     "declare the causes which impel them to the separation.";
 
 class TextBoxView : public Sample {
+    SkString fName;
 public:
-    TextBoxView() : fShaper(SkShaper::Make()) {}
+    TextBoxView(ShaperFactory fact, const char suffix[]) : fShaper(fact()) {
+        fName.printf("TextBox_%s", suffix);
+    }
 
 protected:
-    SkString name() override { return SkString("TextBox"); }
+    SkString name() override { return fName; }
 
     void drawTest(SkCanvas* canvas, SkScalar w, SkScalar h, SkColor fg, SkColor bg) {
         SkAutoCanvasRestore acr(canvas, true);
@@ -52,9 +56,11 @@
         paint.setColor(fg);
 
         for (int i = 9; i < 24; i += 2) {
+            SkShaper::PurgeCaches();
             SkTextBlobBuilderRunHandler builder(gText, { margin, margin });
             SkFont srcFont(nullptr, SkIntToScalar(i));
             srcFont.setEdging(SkFont::Edging::kSubpixelAntiAlias);
+            srcFont.setSubpixel(true);
 
             const char* utf8 = gText;
             size_t utf8Bytes = sizeof(gText) - 1;
@@ -105,9 +111,80 @@
 
 private:
     std::unique_ptr<SkShaper> fShaper;
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
-//////////////////////////////////////////////////////////////////////////////
+DEF_SAMPLE( return new TextBoxView([](){ return SkShaper::Make(); }, "default"); );
+#ifdef SK_SHAPER_CORETEXT_AVAILABLE
+DEF_SAMPLE( return new TextBoxView(SkShaper::MakeCoreText, "coretext"); );
+#endif
 
-DEF_SAMPLE( return new TextBoxView(); )
+class SampleShaper : public Sample {
+public:
+    SampleShaper() {}
+
+protected:
+    SkString name() override { return SkString("shaper"); }
+
+    void drawTest(SkCanvas* canvas, const char str[], SkScalar size,
+                  std::unique_ptr<SkShaper> shaper) {
+        if (!shaper) return;
+
+        SkTextBlobBuilderRunHandler builder(str, {0, 0});
+        SkFont srcFont;
+        srcFont.setSize(size);
+        srcFont.setEdging(SkFont::Edging::kSubpixelAntiAlias);
+        srcFont.setSubpixel(true);
+
+        size_t len = strlen(str);
+
+        std::unique_ptr<SkShaper::BiDiRunIterator> bidi(
+            SkShaper::MakeBiDiRunIterator(str, len, 0xfe));
+        if (!bidi) {
+            return;
+        }
+
+        std::unique_ptr<SkShaper::LanguageRunIterator> language(
+            SkShaper::MakeStdLanguageRunIterator(str, len));
+        if (!language) {
+            return;
+        }
+
+        SkFourByteTag undeterminedScript = SkSetFourByteTag('Z','y','y','y');
+        std::unique_ptr<SkShaper::ScriptRunIterator> script(
+            SkShaper::MakeScriptRunIterator(str, len, undeterminedScript));
+        if (!script) {
+            return;
+        }
+
+        std::unique_ptr<SkShaper::FontRunIterator> font(
+            SkShaper::MakeFontMgrRunIterator(str, len, srcFont, SkFontMgr::RefDefault(),
+                                             "Arial", SkFontStyle::Bold(), &*language));
+        if (!font) {
+            return;
+        }
+
+        shaper->shape(str, len, *font, *bidi, *script, *language, 2000, &builder);
+
+        canvas->drawTextBlob(builder.makeBlob(), 0, 0, SkPaint());
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        canvas->translate(10, 30);
+
+        const char text[] = "world";
+
+        for (SkScalar size = 30; size <= 30; size += 10) {
+            this->drawTest(canvas, text, size, SkShaper::Make());
+            canvas->translate(0, size + 5);
+            #ifdef SK_SHAPER_CORETEXT_AVAILABLE
+            this->drawTest(canvas, text, size, SkShaper::MakeCoreText());
+            #endif
+            canvas->translate(0, size*2);
+        }
+    }
+
+private:
+    using INHERITED = Sample;
+};
+DEF_SAMPLE( return new SampleShaper; );
diff --git a/third_party/skia/samplecode/SampleTextEffects.cpp b/third_party/skia/samplecode/SampleTextEffects.cpp
deleted file mode 100644
index 8e57750..0000000
--- a/third_party/skia/samplecode/SampleTextEffects.cpp
+++ /dev/null
@@ -1,173 +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 "include/core/SkCanvas.h"
-#include "include/core/SkColorFilter.h"
-#include "include/core/SkColorPriv.h"
-#include "include/core/SkPath.h"
-#include "include/core/SkRegion.h"
-#include "include/core/SkShader.h"
-#include "include/core/SkStrokeRec.h"
-#include "include/core/SkTypeface.h"
-#include "include/effects/SkGradientShader.h"
-#include "include/utils/SkTextUtils.h"
-#include "samplecode/Sample.h"
-#include "src/core/SkReadBuffer.h"
-#include "src/core/SkWriteBuffer.h"
-#include "src/utils/SkUTF.h"
-
-#include "include/effects/SkBlurMaskFilter.h"
-#include "include/effects/SkGradientShader.h"
-
-#include "include/effects/Sk2DPathEffect.h"
-
-class Dot2DPathEffect : public Sk2DPathEffect {
-public:
-    Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix,
-                    SkTDArray<SkPoint>* pts)
-    : Sk2DPathEffect(matrix), fRadius(radius), fPts(pts) {}
-
-    SK_FLATTENABLE_HOOKS(Dot2DPathEffect)
-protected:
-    void begin(const SkIRect& uvBounds, SkPath* dst) const override {
-        if (fPts) {
-            fPts->reset();
-        }
-        this->INHERITED::begin(uvBounds, dst);
-    }
-
-    virtual void next(const SkPoint& loc, int u, int v,
-                      SkPath* dst) const override {
-        if (fPts) {
-            *fPts->append() = loc;
-        }
-        dst->addCircle(loc.fX, loc.fY, fRadius);
-    }
-
-    void flatten(SkWriteBuffer& buffer) const override {
-        buffer.writeMatrix(this->getMatrix());
-        buffer.writeScalar(fRadius);
-    }
-
-private:
-
-    SkScalar fRadius;
-    SkTDArray<SkPoint>* fPts;
-
-    typedef Sk2DPathEffect INHERITED;
-};
-
-// Register this path effect as deserializable before main().
-namespace {
-    static struct Initializer {
-        Initializer() {
-            SK_REGISTER_FLATTENABLE(Dot2DPathEffect);
-        }
-    } initializer;
-}
-
-
-sk_sp<SkFlattenable> Dot2DPathEffect::CreateProc(SkReadBuffer& buffer) {
-    SkMatrix matrix;
-    buffer.readMatrix(&matrix);
-    return sk_make_sp<Dot2DPathEffect>(buffer.readScalar(), matrix, nullptr);
-}
-
-class InverseFillPE : public SkPathEffect {
-public:
-    InverseFillPE() {}
-    virtual bool onFilterPath(SkPath* dst, const SkPath& src,
-                              SkStrokeRec*, const SkRect*) const override {
-        *dst = src;
-        dst->setFillType(SkPathFillType::kInverseWinding);
-        return true;
-    }
-
-private:
-    SK_FLATTENABLE_HOOKS(InverseFillPE)
-
-    typedef SkPathEffect INHERITED;
-};
-
-sk_sp<SkFlattenable> InverseFillPE::CreateProc(SkReadBuffer& buffer) {
-    return sk_make_sp<InverseFillPE>();
-}
-
-static sk_sp<SkPathEffect> makepe(float interp, SkTDArray<SkPoint>* pts) {
-    SkMatrix    lattice;
-    SkScalar    rad = 3 + SkIntToScalar(4) * (1 - interp);
-    lattice.setScale(rad*2, rad*2, 0, 0);
-    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
-    return sk_make_sp<Dot2DPathEffect>(rad, lattice, pts);
-}
-
-class TextEffectsView : public Sample {
-    sk_sp<SkTypeface> fFace;
-    SkScalar fInterp;
-    SkScalar fDx;
-
-public:
-    TextEffectsView() {
-        fFace = SkTypeface::MakeFromFile("/Users/reed/Downloads/p052024l.pfb");
-        fInterp = 0;
-        fDx = SK_Scalar1/64;
-    }
-
-protected:
-    SkString name() override { return SkString("Text Effects"); }
-
-    void drawBG(SkCanvas* canvas) {
-        canvas->drawColor(SK_ColorWHITE);
-    }
-
-    void drawdots(SkCanvas* canvas, SkString s, SkScalar x, SkScalar y, const SkFont& font) {
-        SkTDArray<SkPoint> pts;
-        auto pe = makepe(fInterp, &pts);
-
-        SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
-        SkPath path, dstPath;
-        SkTextUtils::GetPath(s.c_str(), s.size(), SkTextEncoding::kUTF8, x, y, font, &path);
-        pe->filterPath(&dstPath, path, &rec, nullptr);
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setStrokeWidth(10);
-        paint.setColor(SK_ColorRED);
-        canvas->drawPoints(SkCanvas::kPoints_PointMode, pts.count(), pts.begin(), paint);
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        this->drawBG(canvas);
-
-        SkScalar x = SkIntToScalar(20);
-        SkScalar y = SkIntToScalar(300);
-
-        SkFont font(SkTypeface::MakeFromName("sans-serif", SkFontStyle::Bold()), 240);
-
-        SkString str("9");
-
-        canvas->drawString(str, x, y, font, SkPaint());
-        drawdots(canvas, str, x, y, font);
-
-        if (false) {
-            fInterp += fDx;
-            if (fInterp > 1) {
-                fInterp = 1;
-                fDx = -fDx;
-            } else if (fInterp < 0) {
-                fInterp = 0;
-                fDx = -fDx;
-            }
-        }
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE( return new TextEffectsView(); )
diff --git a/third_party/skia/samplecode/SampleTextureDomain.cpp b/third_party/skia/samplecode/SampleTextureDomain.cpp
deleted file mode 100644
index 3bac799..0000000
--- a/third_party/skia/samplecode/SampleTextureDomain.cpp
+++ /dev/null
@@ -1,95 +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 "include/core/SkCanvas.h"
-#include "include/core/SkSurface.h"
-#include "include/effects/SkBlurMaskFilter.h"
-#include "samplecode/Sample.h"
-#include "src/core/SkBlurMask.h"
-
-static SkBitmap make_bitmap() {
-    SkBitmap bm;
-    bm.allocN32Pixels(5, 5);
-
-    for (int y = 0; y < bm.height(); y++) {
-        uint32_t* p = bm.getAddr32(0, y);
-        for (int x = 0; x < bm.width(); x++) {
-            p[x] = ((x + y) & 1) ? SK_ColorWHITE : SK_ColorBLACK;
-        }
-    }
-    return bm;
-}
-
-class TextureDomainView : public Sample {
-    SkBitmap    fBM;
-
-public:
-    TextureDomainView(){
-        fBM = make_bitmap();
-    }
-
-protected:
-    virtual SkString name() { return SkString("Texture Domain"); }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkRect srcRect;
-        SkRect dstRect;
-        SkPaint paint;
-        paint.setFilterQuality(kLow_SkFilterQuality);
-
-        // Test that bitmap draws from malloc-backed bitmaps respect
-        // the constrained texture domain.
-        srcRect.setXYWH(1, 1, 3, 3);
-        dstRect.setXYWH(5, 5, 305, 305);
-        canvas->drawBitmapRect(fBM, srcRect, dstRect, &paint, SkCanvas::kStrict_SrcRectConstraint);
-
-        // Test that bitmap draws across separate devices also respect
-        // the constrainted texture domain.
-        // Note:  GPU-backed bitmaps follow a different rendering path
-        // when copying from one GPU device to another.
-        SkImageInfo info = SkImageInfo::MakeN32(5, 5, kOpaque_SkAlphaType);
-        auto surface(canvas->makeSurface(info));
-
-        srcRect.setXYWH(1, 1, 3, 3);
-        dstRect.setXYWH(1, 1, 3, 3);
-        surface->getCanvas()->drawBitmapRect(fBM, srcRect, dstRect, &paint,
-                                             SkCanvas::kStrict_SrcRectConstraint);
-
-        sk_sp<SkImage> image(surface->makeImageSnapshot());
-
-        srcRect.setXYWH(1, 1, 3, 3);
-        dstRect.setXYWH(405, 5, 305, 305);
-        canvas->drawImageRect(image, srcRect, dstRect, &paint);
-
-        // Test that bitmap blurring using a subrect
-        // renders correctly
-        srcRect.setXYWH(1, 1, 3, 3);
-        dstRect.setXYWH(5, 405, 305, 305);
-        paint.setMaskFilter(SkMaskFilter::MakeBlur(
-            kNormal_SkBlurStyle, SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(5)), false));
-        canvas->drawImageRect(image, srcRect, dstRect, &paint);
-
-        // Blur and a rotation + nullptr src rect
-        // This should not trigger the texture domain code
-        // but it will test a code path in SkGpuDevice::drawBitmap
-        // that handles blurs with rects transformed to non-
-        // orthogonal rects. It also tests the nullptr src rect handling
-        paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle,
-                                                   SkBlurMask::ConvertRadiusToSigma(5)));
-
-        dstRect.setXYWH(-150, -150, 300, 300);
-        canvas->translate(550, 550);
-        canvas->rotate(45);
-        canvas->drawBitmapRect(fBM, dstRect, &paint);
-    }
-private:
-    typedef Sample INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE( return new TextureDomainView(); )
diff --git a/third_party/skia/samplecode/SampleTextureUpload.cpp b/third_party/skia/samplecode/SampleTextureUpload.cpp
index a33d1d9..933748c 100644
--- a/third_party/skia/samplecode/SampleTextureUpload.cpp
+++ b/third_party/skia/samplecode/SampleTextureUpload.cpp
@@ -9,7 +9,7 @@
 #include "include/core/SkPaint.h"
 #include "include/core/SkSurface.h"
 #include "include/core/SkTypes.h"
-#include "include/gpu/GrContext.h"
+#include "include/gpu/GrDirectContext.h"
 #include "samplecode/Sample.h"
 #include "tools/timer/TimeUtils.h"
 
@@ -17,9 +17,9 @@
  * This sample exercises heavy texture updates and uploads.
  */
 class TextureUploadSample : public Sample {
-    static constexpr int kMinTileSize = 128;
-    static constexpr int kMaxTileSize = 2048;
-    static constexpr float kGridScale = 0.25f;
+    inline static constexpr int kMinTileSize = 128;
+    inline static constexpr int kMaxTileSize = 2048;
+    inline static constexpr float kGridScale = 0.25f;
 
     bool fDrawTexturesToScreen = true;
     int fTileSize = 256;
@@ -31,11 +31,11 @@
 
     class RenderTargetTexture : public SkRefCnt {
     public:
-        RenderTargetTexture(GrContext* context, int size) {
-            SkSurfaceProps surfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
+        RenderTargetTexture(GrDirectContext* direct, int size) {
+            SkSurfaceProps surfaceProps(0, kRGB_H_SkPixelGeometry);
             SkImageInfo imageInfo = SkImageInfo::Make(size, size, kRGBA_8888_SkColorType,
                                                       kPremul_SkAlphaType);
-            fSurface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, imageInfo, 0,
+            fSurface = SkSurface::MakeRenderTarget(direct, SkBudgeted::kNo, imageInfo, 0,
                                                    &surfaceProps);
         }
 
@@ -55,7 +55,7 @@
     };
 
     SkTArray<sk_sp<RenderTargetTexture>> fTextures;
-    GrContext* fCachedContext = nullptr;
+    GrDirectContext* fCachedContext = nullptr;
     SkScalar fActiveTileIndex = 0;
 
     SkString name() override {
@@ -67,12 +67,12 @@
             fDrawTexturesToScreen = !fDrawTexturesToScreen;
             return true;
         } else if ('>' == uni) {
-            fTileSize = SkTMin(kMaxTileSize, 2*fTileSize);
+            fTileSize = std::min(kMaxTileSize, 2*fTileSize);
             fTileRows = kMaxTileSize/fTileSize;
             fTileCols = kMaxTileSize/fTileSize;
             fCachedContext = nullptr;
         } else if ('<' == uni) {
-            fTileSize = SkTMax(kMinTileSize, fTileSize/2);
+            fTileSize = std::max(kMinTileSize, fTileSize/2);
             fTileRows = kMaxTileSize/fTileSize;
             fTileCols = kMaxTileSize/fTileSize;
             fCachedContext = nullptr;
@@ -92,11 +92,11 @@
         this->setSize(1024, 1024);
     }
 
-    void initializeTextures(GrContext* context) {
+    void initializeTextures(GrDirectContext* direct) {
         fTextures.reset();
         int textureCount = fTileRows * fTileCols;
         for (int i = 0; i < textureCount; i++) {
-            fTextures.emplace_back(new RenderTargetTexture(context, fTileSize));
+            fTextures.emplace_back(new RenderTargetTexture(direct, fTileSize));
         }
 
         // Construct two simple rasters of differing colors to serve
@@ -107,14 +107,12 @@
 
     void onDrawContent(SkCanvas* canvas) override {
 #if SK_SUPPORT_GPU
-        SkPaint paint;
-
-        GrContext* context = canvas->getGrContext();
-        if (context) {
+        auto direct = GrAsDirectContext(canvas->recordingContext());
+        if (direct) {
             // One-time context-specific setup.
-            if (context != fCachedContext) {
-                fCachedContext = context;
-                this->initializeTextures(context);
+            if (direct != fCachedContext) {
+                fCachedContext = direct;
+                this->initializeTextures(direct);
             }
 
             // Upload new texture data for all textures, simulating a full page of tiles
@@ -133,7 +131,7 @@
                     for (int x = 0; x < fTileCols; x++) {
                         int currentIndex = y * fTileCols + x;
                         canvas->drawImage(fTextures[currentIndex]->getImage(),
-                                          x * fTileSize, y * fTileSize, &paint);
+                                          x * fTileSize, y * fTileSize);
                     }
                 }
             }
@@ -150,8 +148,6 @@
     }
 };
 
-const int TextureUploadSample::kMinTileSize;
-const int TextureUploadSample::kMaxTileSize;
 
 DEF_SAMPLE( return new TextureUploadSample(); )
 
diff --git a/third_party/skia/samplecode/SampleThinAA.cpp b/third_party/skia/samplecode/SampleThinAA.cpp
index 92aa202..5989deb 100644
--- a/third_party/skia/samplecode/SampleThinAA.cpp
+++ b/third_party/skia/samplecode/SampleThinAA.cpp
@@ -18,10 +18,8 @@
 
 class ShapeRenderer : public SkRefCntBase {
 public:
-    static constexpr SkScalar kTileWidth = 20.f;
-    static constexpr SkScalar kTileHeight = 20.f;
-
-    virtual ~ShapeRenderer() {}
+    inline static constexpr SkScalar kTileWidth = 20.f;
+    inline static constexpr SkScalar kTileHeight = 20.f;
 
     // Draw the shape, limited to kTileWidth x kTileHeight. It must apply the local subpixel (tx,
     // ty) translation and rotation by angle. Prior to these transform adjustments, the SkCanvas
@@ -66,7 +64,7 @@
 private:
     RectRenderer() {}
 
-    typedef ShapeRenderer INHERITED;
+    using INHERITED = ShapeRenderer;
 };
 
 class PathRenderer : public ShapeRenderer {
@@ -133,7 +131,7 @@
 
         // Adding round caps forces Ganesh to use the path renderer for lines instead of converting
         // them to rectangles (which are already explicitly tested). However, when not curved, the
-        // GrShape will still find a way to turn it into a rrect draw so it doesn't hit the
+        // GrStyledShape will still find a way to turn it into a rrect draw so it doesn't hit the
         // path renderer in that condition.
         paint->setStrokeCap(SkPaint::kRound_Cap);
         paint->setStrokeJoin(SkPaint::kMiter_Join);
@@ -151,7 +149,7 @@
             : fDepth(depth)
             , fHairline(hairline) {}
 
-    typedef ShapeRenderer INHERITED;
+    using INHERITED = ShapeRenderer;
 };
 
 class OffscreenShapeRenderer : public ShapeRenderer {
@@ -210,7 +208,6 @@
         // Use medium quality filter to get mipmaps when drawing smaller, or use nearest filtering
         // when upscaling
         SkPaint blit;
-        blit.setFilterQuality(scale > 1.f ? kNone_SkFilterQuality : kMedium_SkFilterQuality);
         if (debugMode) {
             // Makes anything that's > 1/255 alpha fully opaque and sets color to medium green.
             static constexpr float kFilter[] = {
@@ -223,8 +220,15 @@
             blit.setColorFilter(SkColorFilters::Matrix(kFilter));
         }
 
+        auto sampling = scale > 1 ? SkSamplingOptions(SkFilterMode::kNearest)
+                                  : SkSamplingOptions(SkFilterMode::kLinear,
+                                                      SkMipmapMode::kLinear);
+
         canvas->scale(scale, scale);
-        canvas->drawImageRect(fLastRendered, SkRect::MakeWH(kTileWidth, kTileHeight), &blit);
+        canvas->drawImageRect(fLastRendered.get(),
+                              SkRect::MakeWH(kTileWidth, kTileHeight),
+                              SkRect::MakeWH(kTileWidth, kTileHeight),
+                              sampling, &blit, SkCanvas::kFast_SrcRectConstraint);
     }
 
 private:
@@ -239,7 +243,7 @@
             , fRenderer(std::move(renderer))
             , fSupersampleFactor(supersample) { }
 
-    typedef ShapeRenderer INHERITED;
+    using INHERITED = ShapeRenderer;
 };
 
 class ThinAASample : public Sample {
@@ -403,8 +407,8 @@
                 case 'u': fAngle = 0.f; return true;
                 case 'y': fAngle = 90.f; return true;
                 case ' ': fAngle = SkScalarMod(fAngle + 15.f, 360.f); return true;
-                case '-': fStrokeWidth = SkMaxScalar(0.1f, fStrokeWidth - 0.05f); return true;
-                case '=': fStrokeWidth = SkMinScalar(1.f, fStrokeWidth + 0.05f); return true;
+                case '-': fStrokeWidth = std::max(0.1f, fStrokeWidth - 0.05f); return true;
+                case '=': fStrokeWidth = std::min(1.f, fStrokeWidth + 0.05f); return true;
             }
             return false;
     }
@@ -480,8 +484,7 @@
         SkFont font(nullptr, 12);
 
         if (gridX == 0) {
-            SkString name = shape->name();
-            SkScalar centering = name.size() * 4.f; // ad-hoc
+            SkScalar centering = shape->name().size() * 4.f; // ad-hoc
 
             canvas->save();
             canvas->translate(-10.f, 4 * ShapeRenderer::kTileHeight + centering);
@@ -537,11 +540,11 @@
         canvas->translate(0.f, 8.f * ShapeRenderer::kTileHeight + 20.f);
     }
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
 
 DEF_SAMPLE( return new ThinAASample; )
 
-}
+}  // namespace skiagm
diff --git a/third_party/skia/samplecode/SampleTiming.cpp b/third_party/skia/samplecode/SampleTiming.cpp
new file mode 100644
index 0000000..d8e3216
--- /dev/null
+++ b/third_party/skia/samplecode/SampleTiming.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkCanvas.h"
+#include "include/core/SkFont.h"
+#include "include/core/SkSurface.h"
+#include "samplecode/Sample.h"
+#include <chrono>
+
+struct TimingSample : public Sample {
+    inline static constexpr int W = 24,
+                                H = 16;
+    sk_sp<SkImage> fImg;
+
+    SkString name() override { return SkString("Timing"); }
+
+    void onOnceBeforeDraw() override {
+        sk_sp<SkSurface> surf = SkSurface::MakeRasterN32Premul(W,H);
+        surf->getCanvas()->drawString("abc", 2,H-4, SkFont{}, SkPaint{});
+        fImg = surf->makeImageSnapshot();
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        canvas->scale(8,8);
+
+        // Draw normally.
+        canvas->drawImage(fImg, 0,0);
+
+        canvas->translate(0,H);
+
+        // Draw one pixel at a time with drawImageRect(),
+        // timing how long each drawImageRect() call takes.
+        double cost[H][W];
+        double min = +INFINITY,
+               max = -INFINITY;
+        for (int y = 0; y < H; y++)
+        for (int x = 0; x < W; x++) {
+            auto start = std::chrono::steady_clock::now();
+            canvas->drawImageRect(fImg.get(),
+                                  SkRect::MakeXYWH(x,y,1,1), SkRect::MakeXYWH(x,y,1,1),
+                                  SkSamplingOptions(), /*paint=*/nullptr,
+                                  SkCanvas::kStrict_SrcRectConstraint);
+            auto elapsed = std::chrono::steady_clock::now() - start;
+
+            cost[y][x] = elapsed.count();
+            min = std::min(min, cost[y][x]);
+            max = std::max(max, cost[y][x]);
+        }
+
+        canvas->translate(0,H);
+
+        // Draw using those per-pixel timings,
+        // with the slowest pixel scaled to alpha=1, the fastest to alpha=0.
+        for (int y = 0; y < H; y++)
+        for (int x = 0; x < W; x++) {
+            SkPaint p;
+            p.setAlphaf( (cost[y][x] - min) / (max - min) );
+            canvas->drawRect(SkRect::MakeXYWH(x,y,1,1), p);
+        }
+
+        canvas->translate(0,H);
+
+        // Draw each pixel into offscreen, timing each draw.
+        SkImageInfo info = canvas->imageInfo().makeWH(1024,1024);
+        if (sk_sp<SkSurface> offscreen = canvas->makeSurface(info)) {
+            min = +INFINITY;
+            max = -INFINITY;
+            for (int y = 0; y < H; y++)
+            for (int x = 0; x < W; x++) {
+                auto start = std::chrono::steady_clock::now();
+                offscreen->getCanvas()->drawImageRect(fImg,
+                                                      SkRect::MakeXYWH(x,y,1,1),
+                                                      SkRect::MakeXYWH(0,0,1024,1024),
+                                                      SkSamplingOptions(),
+                                                      /*paint=*/nullptr,
+                                                      SkCanvas::kStrict_SrcRectConstraint);
+                auto elapsed = std::chrono::steady_clock::now() - start;
+
+                cost[y][x] = elapsed.count();
+                min = std::min(min, cost[y][x]);
+                max = std::max(max, cost[y][x]);
+            }
+            for (int y = 0; y < H; y++)
+            for (int x = 0; x < W; x++) {
+                SkPaint p;
+                p.setAlphaf( (cost[y][x] - min) / (max - min) );
+                canvas->drawRect(SkRect::MakeXYWH(x,y,1,1), p);
+            }
+        }
+    }
+};
+DEF_SAMPLE( return new TimingSample; )
diff --git a/third_party/skia/samplecode/SampleUnpremul.cpp b/third_party/skia/samplecode/SampleUnpremul.cpp
deleted file mode 100644
index 9c606bd..0000000
--- a/third_party/skia/samplecode/SampleUnpremul.cpp
+++ /dev/null
@@ -1,166 +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 "include/core/SkCanvas.h"
-#include "include/core/SkColorPriv.h"
-#include "include/core/SkStream.h"
-#include "include/core/SkString.h"
-#include "include/core/SkTypes.h"
-#include "samplecode/DecodeFile.h"
-#include "samplecode/Sample.h"
-#include "src/core/SkBlurMask.h"
-#include "src/core/SkOSFile.h"
-#include "src/utils/SkOSPath.h"
-#include "src/utils/SkUTF.h"
-#include "tools/Resources.h"
-#include "tools/ToolUtils.h"
-
-/**
- *  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);
-}
-
-class UnpremulView : public Sample {
-public:
-    UnpremulView(SkString res)
-    : fResPath(res)
-    , fPremul(true)
-    , fDecodeSucceeded(false) {
-        this->nextImage();
-    }
-
-protected:
-    SkString name() override { return SkString("unpremul"); }
-
-    bool onChar(SkUnichar uni) override {
-            char utf8[SkUTF::kMaxBytesInUTF8Sequence];
-            size_t size = SkUTF::ToUTF8(uni, utf8);
-            // Only consider events for single char keys
-            if (1 == size) {
-                switch (utf8[0]) {
-                    case fNextImageChar:
-                        this->nextImage();
-                        return true;
-                    case fTogglePremulChar:
-                        this->togglePremul();
-                        return true;
-                    default:
-                        break;
-                }
-            }
-            return false;
-    }
-
-    void onDrawBackground(SkCanvas* canvas) override {
-        ToolUtils::draw_checkerboard(canvas, 0xFFCCCCCC, 0xFFFFFFFF, 12);
-    }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-
-        SkFont font;
-        font.setSize(24);
-        SkScalar height = font.getMetrics(nullptr);
-        if (!fDecodeSucceeded) {
-            SkString failure;
-            if (fResPath.size() == 0) {
-                failure.printf("resource path is required!");
-            } else {
-                failure.printf("Failed to decode %s", fCurrFile.c_str());
-            }
-            canvas->drawString(failure, 0, height, font, paint);
-            return;
-        }
-
-        // Name, size of the file, and whether or not it is premultiplied.
-        SkString header(SkOSPath::Basename(fCurrFile.c_str()));
-        header.appendf("     [%dx%d]     %s", fBitmap.width(), fBitmap.height(),
-                       (fPremul ? "premultiplied" : "unpremultiplied"));
-        canvas->drawString(header, 0, height, font, paint);
-        canvas->translate(0, height);
-
-        // Help messages
-        header.printf("Press '%c' to move to the next image.'", fNextImageChar);
-        canvas->drawString(header, 0, height, font, paint);
-        canvas->translate(0, height);
-
-        header.printf("Press '%c' to toggle premultiplied decode.", fTogglePremulChar);
-        canvas->drawString(header, 0, height, font, paint);
-
-        // Now draw the image itself.
-        canvas->translate(height * 2, height * 2);
-        if (!fPremul) {
-            // A premultiplied bitmap cannot currently be drawn.
-            // Copy it to a bitmap which can be drawn, converting
-            // to premultiplied:
-            SkBitmap bm;
-            bm.allocN32Pixels(fBitmap.width(), fBitmap.height());
-            for (int i = 0; i < fBitmap.width(); ++i) {
-                for (int j = 0; j < fBitmap.height(); ++j) {
-                    *bm.getAddr32(i, j) = premultiply_unpmcolor(*fBitmap.getAddr32(i, j));
-                }
-            }
-            canvas->drawBitmap(bm, 0, 0);
-        } else {
-            canvas->drawBitmap(fBitmap, 0, 0);
-        }
-    }
-
-private:
-    const SkString  fResPath;
-    SkString        fCurrFile;
-    bool            fPremul;
-    bool            fDecodeSucceeded;
-    SkBitmap        fBitmap;
-    SkOSFile::Iter  fFileIter;
-
-    static const char   fNextImageChar      = 'j';
-    static const char   fTogglePremulChar   = 'h';
-
-    void nextImage() {
-        if (fResPath.size() == 0) {
-            return;
-        }
-        SkString basename;
-        if (!fFileIter.next(&basename)) {
-            fFileIter.reset(fResPath.c_str());
-            if (!fFileIter.next(&basename)) {
-                // Perhaps this should draw some error message?
-                return;
-            }
-        }
-        fCurrFile = SkOSPath::Join(fResPath.c_str(), basename.c_str());
-        this->decodeCurrFile();
-    }
-
-    void decodeCurrFile() {
-        if (fCurrFile.size() == 0) {
-            fDecodeSucceeded = false;
-            return;
-        }
-        fDecodeSucceeded = decode_file(fCurrFile.c_str(), &fBitmap, kN32_SkColorType, !fPremul);
-    }
-
-    void togglePremul() {
-        fPremul = !fPremul;
-        this->decodeCurrFile();
-    }
-
-    typedef Sample INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-DEF_SAMPLE( return new UnpremulView(GetResourcePath("images")); )
diff --git a/third_party/skia/samplecode/SampleVariableWidthStroker.cpp b/third_party/skia/samplecode/SampleVariableWidthStroker.cpp
new file mode 100644
index 0000000..65efa11
--- /dev/null
+++ b/third_party/skia/samplecode/SampleVariableWidthStroker.cpp
@@ -0,0 +1,1391 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "imgui.h"
+#include "include/core/SkBitmap.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkPath.h"
+#include "include/core/SkPathMeasure.h"
+#include "include/utils/SkParsePath.h"
+#include "samplecode/Sample.h"
+
+#include "src/core/SkGeometry.h"
+
+#include <stack>
+
+namespace {
+
+//////////////////////////////////////////////////////////////////////////////
+
+constexpr inline SkPoint rotate90(const SkPoint& p) { return {p.fY, -p.fX}; }
+inline SkPoint rotate180(const SkPoint& p) { return p * -1; }
+inline bool isClockwise(const SkPoint& a, const SkPoint& b) { return a.cross(b) > 0; }
+
+static SkPoint checkSetLength(SkPoint p, float len, const char* file, int line) {
+    if (!p.setLength(len)) {
+        SkDebugf("%s:%d: Failed to set point length\n", file, line);
+    }
+    return p;
+}
+
+/** Version of setLength that prints debug msg on failure to help catch edge cases */
+#define setLength(p, len) checkSetLength(p, len, __FILE__, __LINE__)
+
+constexpr uint64_t choose(uint64_t n, uint64_t k) {
+    SkASSERT(n >= k);
+    uint64_t result = 1;
+    for (uint64_t i = 1; i <= k; i++) {
+        result *= (n + 1 - i);
+        result /= i;
+    }
+    return result;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+ * A scalar (float-valued weights) Bezier curve of arbitrary degree.
+ */
+class ScalarBezCurve {
+public:
+    inline static constexpr int kDegreeInvalid = -1;
+
+    /** Creates an empty curve with invalid degree. */
+    ScalarBezCurve() : fDegree(kDegreeInvalid) {}
+
+    /** Creates a curve of the specified degree with weights initialized to 0. */
+    explicit ScalarBezCurve(int degree) : fDegree(degree) {
+        SkASSERT(degree >= 0);
+        fWeights.resize(degree + 1, {0});
+    }
+
+    /** Creates a curve of specified degree with the given weights. */
+    ScalarBezCurve(int degree, const std::vector<float>& weights) : ScalarBezCurve(degree) {
+        SkASSERT(degree >= 0);
+        SkASSERT(weights.size() == (size_t)degree + 1);
+        fWeights.insert(fWeights.begin(), weights.begin(), weights.end());
+    }
+
+    /** Returns the extreme-valued weight */
+    float extremumWeight() const {
+        float f = 0;
+        int sign = 1;
+        for (float w : fWeights) {
+            if (std::abs(w) > f) {
+                f = std::abs(w);
+                sign = w >= 0 ? 1 : -1;
+            }
+        }
+        return sign * f;
+    }
+
+    /** Evaluates the curve at t */
+    float eval(float t) const { return Eval(*this, t); }
+
+    /** Evaluates the curve at t */
+    static float Eval(const ScalarBezCurve& curve, float t) {
+        // Set up starting point of recursion (k=0)
+        ScalarBezCurve result = curve;
+
+        for (int k = 1; k <= curve.fDegree; k++) {
+            // k is level of recursion, k-1 has previous level's values.
+            for (int i = curve.fDegree; i >= k; i--) {
+                result.fWeights[i] = result.fWeights[i - 1] * (1 - t) + result.fWeights[i] * t;
+            }
+        }
+
+        return result.fWeights[curve.fDegree];
+    }
+
+    /** Splits this curve at t into two halves (of the same degree) */
+    void split(float t, ScalarBezCurve* left, ScalarBezCurve* right) const {
+        Split(*this, t, left, right);
+    }
+
+    /** Splits this curve into the subinterval [tmin,tmax]. */
+    void split(float tmin, float tmax, ScalarBezCurve* result) const {
+        // TODO: I believe there's a more efficient algorithm for this
+        const float tRel = tmin / tmax;
+        ScalarBezCurve ll, rl, rr;
+        this->split(tmax, &rl, &rr);
+        rl.split(tRel, &ll, result);
+    }
+
+    /** Splits the curve at t into two halves (of the same degree) */
+    static void Split(const ScalarBezCurve& curve,
+                      float t,
+                      ScalarBezCurve* left,
+                      ScalarBezCurve* right) {
+        // Set up starting point of recursion (k=0)
+        const int degree = curve.fDegree;
+        ScalarBezCurve result = curve;
+        *left = ScalarBezCurve(degree);
+        *right = ScalarBezCurve(degree);
+        left->fWeights[0] = curve.fWeights[0];
+        right->fWeights[degree] = curve.fWeights[degree];
+
+        for (int k = 1; k <= degree; k++) {
+            // k is level of recursion, k-1 has previous level's values.
+            for (int i = degree; i >= k; i--) {
+                result.fWeights[i] = result.fWeights[i - 1] * (1 - t) + result.fWeights[i] * t;
+            }
+
+            left->fWeights[k] = result.fWeights[k];
+            right->fWeights[degree - k] = result.fWeights[degree];
+        }
+    }
+
+    /**
+     * Increases the degree of the curve to the given degree. Has no effect if the
+     * degree is already equal to the given degree.
+     *
+     * This process is always exact (NB the reverse, degree reduction, is not exact).
+     */
+    void elevateDegree(int newDegree) {
+        if (newDegree == fDegree) {
+            return;
+        }
+
+        fWeights = ElevateDegree(*this, newDegree).fWeights;
+        fDegree = newDegree;
+    }
+
+    /**
+     * Increases the degree of the curve to the given degree. Has no effect if the
+     * degree is already equal to the given degree.
+     *
+     * This process is always exact (NB the reverse, degree reduction, is not exact).
+     */
+    static ScalarBezCurve ElevateDegree(const ScalarBezCurve& curve, int newDegree) {
+        SkASSERT(newDegree >= curve.degree());
+        if (newDegree == curve.degree()) {
+            return curve;
+        }
+
+        // From Farouki, Rajan, "Algorithms for polynomials in Bernstein form" 1988.
+        ScalarBezCurve elevated(newDegree);
+        const int r = newDegree - curve.fDegree;
+        const int n = curve.fDegree;
+
+        for (int i = 0; i <= n + r; i++) {
+            elevated.fWeights[i] = 0;
+            for (int j = std::max(0, i - r); j <= std::min(n, i); j++) {
+                const float f =
+                        (choose(n, j) * choose(r, i - j)) / static_cast<float>(choose(n + r, i));
+                elevated.fWeights[i] += curve.fWeights[j] * f;
+            }
+        }
+
+        return elevated;
+    }
+
+    /**
+     * Returns the zero-set of this curve, which is a list of t values where the curve crosses 0.
+     */
+    std::vector<float> zeroSet() const { return ZeroSet(*this); }
+
+    /**
+     * Returns the zero-set of the curve, which is a list of t values where the curve crosses 0.
+     */
+    static std::vector<float> ZeroSet(const ScalarBezCurve& curve) {
+        constexpr float kTol = 0.001f;
+        std::vector<float> result;
+        ZeroSetRec(curve, 0, 1, kTol, &result);
+        return result;
+    }
+
+    /** Multiplies the curve's weights by a constant value */
+    static ScalarBezCurve Mul(const ScalarBezCurve& curve, float f) {
+        ScalarBezCurve result = curve;
+        for (int k = 0; k <= curve.fDegree; k++) {
+            result.fWeights[k] *= f;
+        }
+        return result;
+    }
+
+    /**
+     * Multiplies the two curves and returns the result.
+     *
+     * Degree of resulting curve is the sum of the degrees of the input curves.
+     */
+    static ScalarBezCurve Mul(const ScalarBezCurve& a, const ScalarBezCurve& b) {
+        // From G. Elber, "Free form surface analysis using a hybrid of symbolic and numeric
+        // computation". PhD thesis, 1992. p.11.
+        const int n = a.degree(), m = b.degree();
+        const int newDegree = n + m;
+        ScalarBezCurve result(newDegree);
+
+        for (int k = 0; k <= newDegree; k++) {
+            result.fWeights[k] = 0;
+            for (int i = std::max(0, k - n); i <= std::min(k, m); i++) {
+                const float f =
+                        (choose(m, i) * choose(n, k - i)) / static_cast<float>(choose(m + n, k));
+                result.fWeights[k] += a.fWeights[i] * b.fWeights[k - i] * f;
+            }
+        }
+
+        return result;
+    }
+
+    /** Returns a^2 + b^2. This is a specialized method because the loops are easily fused. */
+    static ScalarBezCurve AddSquares(const ScalarBezCurve& a, const ScalarBezCurve& b) {
+        const int n = a.degree(), m = b.degree();
+        const int newDegree = n + m;
+        ScalarBezCurve result(newDegree);
+
+        for (int k = 0; k <= newDegree; k++) {
+            float aSq = 0, bSq = 0;
+            for (int i = std::max(0, k - n); i <= std::min(k, m); i++) {
+                const float f =
+                        (choose(m, i) * choose(n, k - i)) / static_cast<float>(choose(m + n, k));
+                aSq += a.fWeights[i] * a.fWeights[k - i] * f;
+                bSq += b.fWeights[i] * b.fWeights[k - i] * f;
+            }
+            result.fWeights[k] = aSq + bSq;
+        }
+
+        return result;
+    }
+
+    /** Returns a - b. */
+    static ScalarBezCurve Sub(const ScalarBezCurve& a, const ScalarBezCurve& b) {
+        ScalarBezCurve result = a;
+        result.sub(b);
+        return result;
+    }
+
+    /** Subtracts the other curve from this curve */
+    void sub(const ScalarBezCurve& other) {
+        SkASSERT(other.fDegree == fDegree);
+        for (int k = 0; k <= fDegree; k++) {
+            fWeights[k] -= other.fWeights[k];
+        }
+    }
+
+    /** Subtracts a constant from this curve */
+    void sub(float f) {
+        for (int k = 0; k <= fDegree; k++) {
+            fWeights[k] -= f;
+        }
+    }
+
+    /** Returns the curve degree */
+    int degree() const { return fDegree; }
+
+    /** Returns the curve weights */
+    const std::vector<float>& weights() const { return fWeights; }
+
+    float operator[](size_t i) const { return fWeights[i]; }
+    float& operator[](size_t i) { return fWeights[i]; }
+
+private:
+    /** Recursive helper for ZeroSet */
+    static void ZeroSetRec(const ScalarBezCurve& curve,
+                           float tmin,
+                           float tmax,
+                           float tol,
+                           std::vector<float>* result) {
+        float lenP = 0;
+        bool allPos = curve.fWeights[0] >= 0, allNeg = curve.fWeights[0] < 0;
+        for (int i = 1; i <= curve.fDegree; i++) {
+            lenP += std::abs(curve.fWeights[i] - curve.fWeights[i - 1]);
+            allPos &= curve.fWeights[i] >= 0;
+            allNeg &= curve.fWeights[i] < 0;
+        }
+        if (lenP <= tol) {
+            result->push_back((tmin + tmax) * 0.5);
+            return;
+        } else if (allPos || allNeg) {
+            // No zero crossings possible if the coefficients don't change sign (convex hull
+            // property)
+            return;
+        } else if (SkScalarNearlyZero(tmax - tmin)) {
+            return;
+        } else {
+            ScalarBezCurve left(curve.fDegree), right(curve.fDegree);
+            Split(curve, 0.5f, &left, &right);
+
+            const float tmid = (tmin + tmax) * 0.5;
+            ZeroSetRec(left, tmin, tmid, tol, result);
+            ZeroSetRec(right, tmid, tmax, tol, result);
+        }
+    }
+
+    int fDegree;
+    std::vector<float> fWeights;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+/** Helper class that measures per-verb path lengths. */
+class PathVerbMeasure {
+public:
+    explicit PathVerbMeasure(const SkPath& path) : fPath(path), fIter(path, false) { nextVerb(); }
+
+    SkScalar totalLength() const;
+
+    SkScalar currentVerbLength() { return fMeas.getLength(); }
+
+    void nextVerb();
+
+private:
+    const SkPath& fPath;
+    SkPoint fFirstPointInContour;
+    SkPoint fPreviousPoint;
+    SkPath fCurrVerb;
+    SkPath::Iter fIter;
+    SkPathMeasure fMeas;
+};
+
+SkScalar PathVerbMeasure::totalLength() const {
+    SkPathMeasure meas(fPath, false);
+    return meas.getLength();
+}
+
+void PathVerbMeasure::nextVerb() {
+    SkPoint pts[4];
+    SkPath::Verb verb = fIter.next(pts);
+
+    while (verb == SkPath::kMove_Verb || verb == SkPath::kClose_Verb) {
+        if (verb == SkPath::kMove_Verb) {
+            fFirstPointInContour = pts[0];
+            fPreviousPoint = fFirstPointInContour;
+        }
+        verb = fIter.next(pts);
+    }
+
+    fCurrVerb.rewind();
+    fCurrVerb.moveTo(fPreviousPoint);
+    switch (verb) {
+        case SkPath::kLine_Verb:
+            fCurrVerb.lineTo(pts[1]);
+            break;
+        case SkPath::kQuad_Verb:
+            fCurrVerb.quadTo(pts[1], pts[2]);
+            break;
+        case SkPath::kCubic_Verb:
+            fCurrVerb.cubicTo(pts[1], pts[2], pts[3]);
+            break;
+        case SkPath::kConic_Verb:
+            fCurrVerb.conicTo(pts[1], pts[2], fIter.conicWeight());
+            break;
+        case SkPath::kDone_Verb:
+            break;
+        case SkPath::kClose_Verb:
+        case SkPath::kMove_Verb:
+            SkASSERT(false);
+            break;
+    }
+
+    fCurrVerb.getLastPt(&fPreviousPoint);
+    fMeas.setPath(&fCurrVerb, false);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Several debug-only visualization helpers
+namespace viz {
+std::unique_ptr<ScalarBezCurve> outerErr;
+SkPath outerFirstApprox;
+}  // namespace viz
+
+/**
+ * Prototype variable-width path stroker.
+ *
+ * Takes as input a path to be stroked, and two distance functions (inside and outside).
+ * Produces a fill path with the stroked path geometry.
+ *
+ * The algorithms in use here are from:
+ *
+ * G. Elber, E. Cohen. "Error bounded variable distance offset operator for free form curves and
+ * surfaces." International Journal of Computational Geometry & Applications 1, no. 01 (1991)
+ *
+ * G. Elber. "Free form surface analysis using a hybrid of symbolic and numeric computation."
+ * PhD diss., Dept. of Computer Science, University of Utah, 1992.
+ */
+class SkVarWidthStroker {
+public:
+    /** Metric to use for interpolation of distance function across path segments. */
+    enum class LengthMetric {
+        /** Each path segment gets an equal interval of t in [0,1] */
+        kNumSegments,
+        /** Each path segment gets t interval equal to its percent of the total path length */
+        kPathLength,
+    };
+
+    /**
+     * Strokes the path with a fixed-width distance function. This produces a traditional stroked
+     * path.
+     */
+    SkPath getFillPath(const SkPath& path, const SkPaint& paint) {
+        return getFillPath(path, paint, identityVarWidth(paint.getStrokeWidth()),
+                           identityVarWidth(paint.getStrokeWidth()));
+    }
+
+    /**
+     * Strokes the given path using the two given distance functions for inner and outer offsets.
+     */
+    SkPath getFillPath(const SkPath& path,
+                       const SkPaint& paint,
+                       const ScalarBezCurve& varWidth,
+                       const ScalarBezCurve& varWidthInner,
+                       LengthMetric lengthMetric = LengthMetric::kNumSegments);
+
+private:
+    /** Helper struct referring to a single segment of an SkPath */
+    struct PathSegment {
+        SkPath::Verb fVerb;
+        std::array<SkPoint, 4> fPoints;
+    };
+
+    struct OffsetSegments {
+        std::vector<PathSegment> fInner;
+        std::vector<PathSegment> fOuter;
+    };
+
+    /** Initialize stroker state */
+    void initForPath(const SkPath& path, const SkPaint& paint);
+
+    /** Strokes a path segment */
+    OffsetSegments strokeSegment(const PathSegment& segment,
+                                 const ScalarBezCurve& varWidth,
+                                 const ScalarBezCurve& varWidthInner,
+                                 bool needsMove);
+
+    /**
+     * Strokes the given segment using the given distance function.
+     *
+     * Returns a list of quad segments that approximate the offset curve.
+     * TODO: no reason this needs to return a vector of quads, can just append to the path
+     */
+    std::vector<PathSegment> strokeSegment(const PathSegment& seg,
+                                           const ScalarBezCurve& distanceFunc) const;
+
+    /** Adds an endcap to fOuter */
+    enum class CapLocation { Start, End };
+    void endcap(CapLocation loc);
+
+    /** Adds a join between the two segments */
+    void join(const SkPoint& common,
+              float innerRadius,
+              float outerRadius,
+              const OffsetSegments& prev,
+              const OffsetSegments& curr);
+
+    /** Appends path in reverse to result */
+    static void appendPathReversed(const SkPath& path, SkPath* result);
+
+    /** Returns the segment unit normal and unit tangent if not nullptr */
+    static SkPoint unitNormal(const PathSegment& seg, float t, SkPoint* tangentOut);
+
+    /** Returns the degree of a segment curve */
+    static int segmentDegree(const PathSegment& seg);
+
+    /** Splits a path segment at t */
+    static void splitSegment(const PathSegment& seg, float t, PathSegment* segA, PathSegment* segB);
+
+    /**
+     * Returns a quadratic segment that approximates the given segment using the given distance
+     * function.
+     */
+    static void approximateSegment(const PathSegment& seg,
+                                   const ScalarBezCurve& distFnc,
+                                   PathSegment* approxQuad);
+
+    /** Returns a constant (deg 0) distance function for the given stroke width */
+    static ScalarBezCurve identityVarWidth(float strokeWidth) {
+        return ScalarBezCurve(0, {strokeWidth / 2.0f});
+    }
+
+    float fRadius;
+    SkPaint::Cap fCap;
+    SkPaint::Join fJoin;
+    SkPath fInner, fOuter;
+    ScalarBezCurve fVarWidth, fVarWidthInner;
+    float fCurrT;
+};
+
+void SkVarWidthStroker::initForPath(const SkPath& path, const SkPaint& paint) {
+    fRadius = paint.getStrokeWidth() / 2;
+    fCap = paint.getStrokeCap();
+    fJoin = paint.getStrokeJoin();
+    fInner.rewind();
+    fOuter.rewind();
+    fCurrT = 0;
+}
+
+SkPath SkVarWidthStroker::getFillPath(const SkPath& path,
+                                      const SkPaint& paint,
+                                      const ScalarBezCurve& varWidth,
+                                      const ScalarBezCurve& varWidthInner,
+                                      LengthMetric lengthMetric) {
+    const auto appendStrokes = [this](const OffsetSegments& strokes, bool needsMove) {
+        if (needsMove) {
+            fOuter.moveTo(strokes.fOuter.front().fPoints[0]);
+            fInner.moveTo(strokes.fInner.front().fPoints[0]);
+        }
+
+        for (const PathSegment& seg : strokes.fOuter) {
+            fOuter.quadTo(seg.fPoints[1], seg.fPoints[2]);
+        }
+
+        for (const PathSegment& seg : strokes.fInner) {
+            fInner.quadTo(seg.fPoints[1], seg.fPoints[2]);
+        }
+    };
+
+    initForPath(path, paint);
+    fVarWidth = varWidth;
+    fVarWidthInner = varWidthInner;
+
+    // TODO: this assumes one contour:
+    PathVerbMeasure meas(path);
+    const float totalPathLength = lengthMetric == LengthMetric::kPathLength
+                                          ? meas.totalLength()
+                                          : (path.countVerbs() - 1);
+
+    // Trace the inner and outer paths simultaneously. Inner will therefore be
+    // recorded in reverse from how we trace the outline.
+    SkPath::Iter it(path, false);
+    PathSegment segment, prevSegment;
+    OffsetSegments offsetSegs, prevOffsetSegs;
+    bool firstSegment = true, prevWasFirst = false;
+
+    float lenTraveled = 0;
+    while ((segment.fVerb = it.next(&segment.fPoints[0])) != SkPath::kDone_Verb) {
+        const float verbLength = lengthMetric == LengthMetric::kPathLength
+                                         ? (meas.currentVerbLength() / totalPathLength)
+                                         : (1.0f / totalPathLength);
+        const float tmin = lenTraveled;
+        const float tmax = lenTraveled + verbLength;
+
+        // Subset the distance function for the current interval.
+        ScalarBezCurve partVarWidth, partVarWidthInner;
+        fVarWidth.split(tmin, tmax, &partVarWidth);
+        fVarWidthInner.split(tmin, tmax, &partVarWidthInner);
+        partVarWidthInner = ScalarBezCurve::Mul(partVarWidthInner, -1);
+
+        // Stroke the current segment
+        switch (segment.fVerb) {
+            case SkPath::kLine_Verb:
+            case SkPath::kQuad_Verb:
+            case SkPath::kCubic_Verb:
+                offsetSegs = strokeSegment(segment, partVarWidth, partVarWidthInner, firstSegment);
+                break;
+            case SkPath::kMove_Verb:
+                // Don't care about multiple contours currently
+                continue;
+            default:
+                SkDebugf("Unhandled path verb %d\n", segment.fVerb);
+                SkASSERT(false);
+                break;
+        }
+
+        // Join to the previous segment
+        if (!firstSegment) {
+            // Append prev inner and outer strokes
+            appendStrokes(prevOffsetSegs, prevWasFirst);
+
+            // Append the join
+            const float innerRadius = varWidthInner.eval(tmin);
+            const float outerRadius = varWidth.eval(tmin);
+            join(segment.fPoints[0], innerRadius, outerRadius, prevOffsetSegs, offsetSegs);
+        }
+
+        std::swap(segment, prevSegment);
+        std::swap(offsetSegs, prevOffsetSegs);
+        prevWasFirst = firstSegment;
+        firstSegment = false;
+        lenTraveled += verbLength;
+        meas.nextVerb();
+    }
+
+    // Finish appending final offset segments
+    appendStrokes(prevOffsetSegs, prevWasFirst);
+
+    // Open contour => endcap at the end
+    const bool isClosed = path.isLastContourClosed();
+    if (isClosed) {
+        SkDebugf("Unhandled closed contour\n");
+        SkASSERT(false);
+    } else {
+        endcap(CapLocation::End);
+    }
+
+    // Walk inner path in reverse, appending to result
+    appendPathReversed(fInner, &fOuter);
+    endcap(CapLocation::Start);
+
+    return fOuter;
+}
+
+SkVarWidthStroker::OffsetSegments SkVarWidthStroker::strokeSegment(
+        const PathSegment& segment,
+        const ScalarBezCurve& varWidth,
+        const ScalarBezCurve& varWidthInner,
+        bool needsMove) {
+    viz::outerErr.reset(nullptr);
+
+    std::vector<PathSegment> outer = strokeSegment(segment, varWidth);
+    std::vector<PathSegment> inner = strokeSegment(segment, varWidthInner);
+    return {inner, outer};
+}
+
+std::vector<SkVarWidthStroker::PathSegment> SkVarWidthStroker::strokeSegment(
+        const PathSegment& seg, const ScalarBezCurve& distanceFunc) const {
+    // Work item for the recursive splitting stack.
+    struct Item {
+        PathSegment fSeg;
+        ScalarBezCurve fDistFnc, fDistFncSqd;
+        ScalarBezCurve fSegX, fSegY;
+
+        Item(const PathSegment& seg,
+             const ScalarBezCurve& distFnc,
+             const ScalarBezCurve& distFncSqd)
+                : fSeg(seg), fDistFnc(distFnc), fDistFncSqd(distFncSqd) {
+            const int segDegree = segmentDegree(seg);
+            fSegX = ScalarBezCurve(segDegree);
+            fSegY = ScalarBezCurve(segDegree);
+            for (int i = 0; i <= segDegree; i++) {
+                fSegX[i] = seg.fPoints[i].fX;
+                fSegY[i] = seg.fPoints[i].fY;
+            }
+        }
+    };
+
+    // Push the initial segment and distance function
+    std::stack<Item> stack;
+    stack.push(Item(seg, distanceFunc, ScalarBezCurve::Mul(distanceFunc, distanceFunc)));
+
+    std::vector<PathSegment> result;
+    constexpr int kMaxIters = 5000; /** TODO: this is completely arbitrary */
+    int iter = 0;
+    while (!stack.empty()) {
+        if (iter++ >= kMaxIters) break;
+        const Item item = stack.top();
+        stack.pop();
+
+        const ScalarBezCurve& distFnc = item.fDistFnc;
+        ScalarBezCurve distFncSqd = item.fDistFncSqd;
+        const float kTol = std::abs(0.5f * distFnc.extremumWeight());
+
+        // Compute a quad that approximates stroke outline
+        PathSegment quadApprox;
+        approximateSegment(item.fSeg, distFnc, &quadApprox);
+        ScalarBezCurve quadApproxX(2), quadApproxY(2);
+        for (int i = 0; i < 3; i++) {
+            quadApproxX[i] = quadApprox.fPoints[i].fX;
+            quadApproxY[i] = quadApprox.fPoints[i].fY;
+        }
+
+        // Compute control polygon for the delta(t) curve. First must elevate to a common degree.
+        const int deltaDegree = std::max(quadApproxX.degree(), item.fSegX.degree());
+        ScalarBezCurve segX = item.fSegX, segY = item.fSegY;
+        segX.elevateDegree(deltaDegree);
+        segY.elevateDegree(deltaDegree);
+        quadApproxX.elevateDegree(deltaDegree);
+        quadApproxY.elevateDegree(deltaDegree);
+
+        ScalarBezCurve deltaX = ScalarBezCurve::Sub(quadApproxX, segX);
+        ScalarBezCurve deltaY = ScalarBezCurve::Sub(quadApproxY, segY);
+
+        // Compute psi(t) = delta_x(t)^2 + delta_y(t)^2.
+        ScalarBezCurve E = ScalarBezCurve::AddSquares(deltaX, deltaY);
+
+        // Promote E and d(t)^2 to a common degree.
+        const int commonDeg = std::max(distFncSqd.degree(), E.degree());
+        distFncSqd.elevateDegree(commonDeg);
+        E.elevateDegree(commonDeg);
+
+        // Subtract dist squared curve from E, resulting in:
+        //   eps(t) = delta_x(t)^2 + delta_y(t)^2 - d(t)^2
+        E.sub(distFncSqd);
+
+        // Purely for debugging/testing, save the first approximation and error function:
+        if (viz::outerErr == nullptr) {
+            using namespace viz;
+            outerErr = std::make_unique<ScalarBezCurve>(E);
+            outerFirstApprox.rewind();
+            outerFirstApprox.moveTo(quadApprox.fPoints[0]);
+            outerFirstApprox.quadTo(quadApprox.fPoints[1], quadApprox.fPoints[2]);
+        }
+
+        // Compute maxErr, which is just the max coefficient of eps (using convex hull property
+        // of bez curves)
+        float maxAbsErr = std::abs(E.extremumWeight());
+
+        if (maxAbsErr > kTol) {
+            PathSegment left, right;
+            splitSegment(item.fSeg, 0.5f, &left, &right);
+
+            ScalarBezCurve distFncL, distFncR;
+            distFnc.split(0.5f, &distFncL, &distFncR);
+
+            ScalarBezCurve distFncSqdL, distFncSqdR;
+            distFncSqd.split(0.5f, &distFncSqdL, &distFncSqdR);
+
+            stack.push(Item(right, distFncR, distFncSqdR));
+            stack.push(Item(left, distFncL, distFncSqdL));
+        } else {
+            // Approximation is good enough.
+            quadApprox.fVerb = SkPath::kQuad_Verb;
+            result.push_back(quadApprox);
+        }
+    }
+    SkASSERT(!result.empty());
+    return result;
+}
+
+void SkVarWidthStroker::endcap(CapLocation loc) {
+    const auto buttCap = [this](CapLocation loc) {
+        if (loc == CapLocation::Start) {
+            // Back at the start of the path: just close the stroked outline
+            fOuter.close();
+        } else {
+            // Inner last pt == first pt when appending in reverse
+            SkPoint innerLastPt;
+            fInner.getLastPt(&innerLastPt);
+            fOuter.lineTo(innerLastPt);
+        }
+    };
+
+    switch (fCap) {
+        case SkPaint::kButt_Cap:
+            buttCap(loc);
+            break;
+        default:
+            SkDebugf("Unhandled endcap %d\n", fCap);
+            buttCap(loc);
+            break;
+    }
+}
+
+void SkVarWidthStroker::join(const SkPoint& common,
+                             float innerRadius,
+                             float outerRadius,
+                             const OffsetSegments& prev,
+                             const OffsetSegments& curr) {
+    const auto miterJoin = [this](const SkPoint& common,
+                                  float leftRadius,
+                                  float rightRadius,
+                                  const OffsetSegments& prev,
+                                  const OffsetSegments& curr) {
+        // With variable-width stroke you can actually have a situation where both sides
+        // need an "inner" or an "outer" join. So we call the two sides "left" and
+        // "right" and they can each independently get an inner or outer join.
+        const auto makeJoin = [this, &common, &prev, &curr](bool left, float radius) {
+            SkPath* path = left ? &fOuter : &fInner;
+            const auto& prevSegs = left ? prev.fOuter : prev.fInner;
+            const auto& currSegs = left ? curr.fOuter : curr.fInner;
+            SkASSERT(!prevSegs.empty());
+            SkASSERT(!currSegs.empty());
+            const SkPoint afterEndpt = currSegs.front().fPoints[0];
+            SkPoint before = unitNormal(prevSegs.back(), 1, nullptr);
+            SkPoint after = unitNormal(currSegs.front(), 0, nullptr);
+
+            // Don't create any join geometry if the normals are nearly identical.
+            const float cosTheta = before.dot(after);
+            if (!SkScalarNearlyZero(1 - cosTheta)) {
+                bool outerJoin;
+                if (left) {
+                    outerJoin = isClockwise(before, after);
+                } else {
+                    before = rotate180(before);
+                    after = rotate180(after);
+                    outerJoin = !isClockwise(before, after);
+                }
+
+                if (outerJoin) {
+                    // Before and after have the same origin and magnitude, so before+after is the
+                    // diagonal of their rhombus. Origin of this vector is the midpoint of the miter
+                    // line.
+                    SkPoint miterVec = before + after;
+
+                    // Note the relationship (draw a right triangle with the miter line as its
+                    // hypoteneuse):
+                    //     sin(theta/2) = strokeWidth / miterLength
+                    // so miterLength = strokeWidth / sin(theta/2)
+                    // where miterLength is the length of the miter from outer point to inner
+                    // corner. miterVec's origin is the midpoint of the miter line, so we use
+                    // strokeWidth/2. Sqrt is just an application of half-angle identities.
+                    const float sinHalfTheta = sqrtf(0.5 * (1 + cosTheta));
+                    const float halfMiterLength = radius / sinHalfTheta;
+                    // TODO: miter length limit
+                    miterVec = setLength(miterVec, halfMiterLength);
+
+                    // Outer join: connect to the miter point, and then to t=0 of next segment.
+                    path->lineTo(common + miterVec);
+                    path->lineTo(afterEndpt);
+                } else {
+                    // Connect to the miter midpoint (common path endpoint of the two segments),
+                    // and then to t=0 of the next segment. This adds an interior "loop"
+                    // of geometry that handles edge cases where segment lengths are shorter than
+                    // the stroke width.
+                    path->lineTo(common);
+                    path->lineTo(afterEndpt);
+                }
+            }
+        };
+
+        makeJoin(true, leftRadius);
+        makeJoin(false, rightRadius);
+    };
+
+    switch (fJoin) {
+        case SkPaint::kMiter_Join:
+            miterJoin(common, innerRadius, outerRadius, prev, curr);
+            break;
+        default:
+            SkDebugf("Unhandled join %d\n", fJoin);
+            miterJoin(common, innerRadius, outerRadius, prev, curr);
+            break;
+    }
+}
+
+void SkVarWidthStroker::appendPathReversed(const SkPath& path, SkPath* result) {
+    const int numVerbs = path.countVerbs();
+    const int numPoints = path.countPoints();
+    std::vector<uint8_t> verbs;
+    std::vector<SkPoint> points;
+    verbs.resize(numVerbs);
+    points.resize(numPoints);
+    path.getVerbs(verbs.data(), numVerbs);
+    path.getPoints(points.data(), numPoints);
+
+    for (int i = numVerbs - 1, j = numPoints; i >= 0; i--) {
+        auto verb = static_cast<SkPath::Verb>(verbs[i]);
+        switch (verb) {
+            case SkPath::kLine_Verb: {
+                j -= 1;
+                SkASSERT(j >= 1);
+                result->lineTo(points[j - 1]);
+                break;
+            }
+            case SkPath::kQuad_Verb: {
+                j -= 1;
+                SkASSERT(j >= 2);
+                result->quadTo(points[j - 1], points[j - 2]);
+                j -= 1;
+                break;
+            }
+            case SkPath::kMove_Verb:
+                // Ignore
+                break;
+            default:
+                SkASSERT(false);
+                break;
+        }
+    }
+}
+
+int SkVarWidthStroker::segmentDegree(const PathSegment& seg) {
+    static constexpr int lut[] = {
+            -1,  // move,
+            1,   // line
+            2,   // quad
+            -1,  // conic
+            3,   // cubic
+            -1   // done
+    };
+    const int deg = lut[static_cast<uint8_t>(seg.fVerb)];
+    SkASSERT(deg > 0);
+    return deg;
+}
+
+void SkVarWidthStroker::splitSegment(const PathSegment& seg,
+                                     float t,
+                                     PathSegment* segA,
+                                     PathSegment* segB) {
+    // TODO: although general, this is a pretty slow way to do this
+    const int degree = segmentDegree(seg);
+    ScalarBezCurve x(degree), y(degree);
+    for (int i = 0; i <= degree; i++) {
+        x[i] = seg.fPoints[i].fX;
+        y[i] = seg.fPoints[i].fY;
+    }
+
+    ScalarBezCurve leftX(degree), rightX(degree), leftY(degree), rightY(degree);
+    x.split(t, &leftX, &rightX);
+    y.split(t, &leftY, &rightY);
+
+    segA->fVerb = segB->fVerb = seg.fVerb;
+    for (int i = 0; i <= degree; i++) {
+        segA->fPoints[i] = {leftX[i], leftY[i]};
+        segB->fPoints[i] = {rightX[i], rightY[i]};
+    }
+}
+
+void SkVarWidthStroker::approximateSegment(const PathSegment& seg,
+                                           const ScalarBezCurve& distFnc,
+                                           PathSegment* approxQuad) {
+    // This is a simple control polygon transformation.
+    // From F. Yzerman. "Precise offsetting of quadratic Bezier curves". 2019.
+    // TODO: detect and handle more degenerate cases (e.g. linear)
+    // TODO: Tiller-Hanson works better in many cases but does not generalize well
+    SkPoint tangentStart, tangentEnd;
+    SkPoint offsetStart = unitNormal(seg, 0, &tangentStart);
+    SkPoint offsetEnd = unitNormal(seg, 1, &tangentEnd);
+    SkPoint offsetMid = offsetStart + offsetEnd;
+
+    const float radiusStart = distFnc.eval(0);
+    const float radiusMid = distFnc.eval(0.5f);
+    const float radiusEnd = distFnc.eval(1);
+
+    offsetStart = radiusStart == 0 ? SkPoint::Make(0, 0) : setLength(offsetStart, radiusStart);
+    offsetMid = radiusMid == 0 ? SkPoint::Make(0, 0) : setLength(offsetMid, radiusMid);
+    offsetEnd = radiusEnd == 0 ? SkPoint::Make(0, 0) : setLength(offsetEnd, radiusEnd);
+
+    SkPoint start, mid, end;
+    switch (segmentDegree(seg)) {
+        case 1:
+            start = seg.fPoints[0];
+            end = seg.fPoints[1];
+            mid = (start + end) * 0.5f;
+            break;
+        case 2:
+            start = seg.fPoints[0];
+            mid = seg.fPoints[1];
+            end = seg.fPoints[2];
+            break;
+        case 3:
+            start = seg.fPoints[0];
+            mid = (seg.fPoints[1] + seg.fPoints[2]) * 0.5f;
+            end = seg.fPoints[3];
+            break;
+        default:
+            SkDebugf("Unhandled degree for segment approximation");
+            SkASSERT(false);
+            break;
+    }
+
+    approxQuad->fPoints[0] = start + offsetStart;
+    approxQuad->fPoints[1] = mid + offsetMid;
+    approxQuad->fPoints[2] = end + offsetEnd;
+}
+
+SkPoint SkVarWidthStroker::unitNormal(const PathSegment& seg, float t, SkPoint* tangentOut) {
+    switch (seg.fVerb) {
+        case SkPath::kLine_Verb: {
+            const SkPoint tangent = setLength(seg.fPoints[1] - seg.fPoints[0], 1);
+            const SkPoint normal = rotate90(tangent);
+            if (tangentOut) {
+                *tangentOut = tangent;
+            }
+            return normal;
+        }
+        case SkPath::kQuad_Verb: {
+            SkPoint tangent;
+            if (t == 0) {
+                tangent = seg.fPoints[1] - seg.fPoints[0];
+            } else if (t == 1) {
+                tangent = seg.fPoints[2] - seg.fPoints[1];
+            } else {
+                tangent = ((seg.fPoints[1] - seg.fPoints[0]) * (1 - t) +
+                           (seg.fPoints[2] - seg.fPoints[1]) * t) *
+                          2;
+            }
+            if (!tangent.normalize()) {
+                SkDebugf("Failed to normalize quad tangent\n");
+                SkASSERT(false);
+            }
+            if (tangentOut) {
+                *tangentOut = tangent;
+            }
+            return rotate90(tangent);
+        }
+        case SkPath::kCubic_Verb: {
+            SkPoint tangent;
+            SkEvalCubicAt(seg.fPoints.data(), t, nullptr, &tangent, nullptr);
+            if (!tangent.normalize()) {
+                SkDebugf("Failed to normalize cubic tangent\n");
+                SkASSERT(false);
+            }
+            if (tangentOut) {
+                *tangentOut = tangent;
+            }
+            return rotate90(tangent);
+        }
+        default:
+            SkDebugf("Unhandled verb for unit normal %d\n", seg.fVerb);
+            SkASSERT(false);
+            return {};
+    }
+}
+
+}  // namespace
+
+//////////////////////////////////////////////////////////////////////////////
+
+class VariableWidthStroker : public Sample {
+public:
+    VariableWidthStroker()
+            : fShowHidden(true)
+            , fShowSkeleton(true)
+            , fShowStrokePoints(false)
+            , fShowUI(false)
+            , fDifferentInnerFunc(false)
+            , fShowErrorCurve(false) {
+        resetToDefaults();
+
+        fPtsPaint.setAntiAlias(true);
+        fPtsPaint.setStrokeWidth(10);
+        fPtsPaint.setStrokeCap(SkPaint::kRound_Cap);
+
+        fStrokePointsPaint.setAntiAlias(true);
+        fStrokePointsPaint.setStrokeWidth(5);
+        fStrokePointsPaint.setStrokeCap(SkPaint::kRound_Cap);
+
+        fStrokePaint.setAntiAlias(true);
+        fStrokePaint.setStyle(SkPaint::kStroke_Style);
+        fStrokePaint.setColor(0x80FF0000);
+
+        fNewFillPaint.setAntiAlias(true);
+        fNewFillPaint.setColor(0x8000FF00);
+
+        fHiddenPaint.setAntiAlias(true);
+        fHiddenPaint.setStyle(SkPaint::kStroke_Style);
+        fHiddenPaint.setColor(0xFF0000FF);
+
+        fSkeletonPaint.setAntiAlias(true);
+        fSkeletonPaint.setStyle(SkPaint::kStroke_Style);
+        fSkeletonPaint.setColor(SK_ColorRED);
+    }
+
+private:
+    /** Selectable menu item for choosing distance functions */
+    struct DistFncMenuItem {
+        std::string fName;
+        int fDegree;
+        bool fSelected;
+        std::vector<float> fWeights;
+
+        DistFncMenuItem(const std::string& name, int degree, bool selected) {
+            fName = name;
+            fDegree = degree;
+            fSelected = selected;
+            fWeights.resize(degree + 1, 1.0f);
+        }
+    };
+
+    SkString name() override { return SkString("VariableWidthStroker"); }
+
+    void onSizeChange() override {
+        fWinSize = SkSize::Make(this->width(), this->height());
+        INHERITED::onSizeChange();
+    }
+
+    bool onChar(SkUnichar uni) override {
+        switch (uni) {
+            case '0':
+                this->toggle(fShowUI);
+                return true;
+            case '1':
+                this->toggle(fShowSkeleton);
+                return true;
+            case '2':
+                this->toggle(fShowHidden);
+                return true;
+            case '3':
+                this->toggle(fShowStrokePoints);
+                return true;
+            case '4':
+                this->toggle(fShowErrorCurve);
+                return true;
+            case '5':
+                this->toggle(fLengthMetric);
+                return true;
+            case 'x':
+                resetToDefaults();
+                return true;
+            case '-':
+                fWidth -= 5;
+                return true;
+            case '=':
+                fWidth += 5;
+                return true;
+            default:
+                break;
+        }
+        return false;
+    }
+
+    void toggle(bool& value) { value = !value; }
+    void toggle(SkVarWidthStroker::LengthMetric& value) {
+        value = value == SkVarWidthStroker::LengthMetric::kPathLength
+                        ? SkVarWidthStroker::LengthMetric::kNumSegments
+                        : SkVarWidthStroker::LengthMetric::kPathLength;
+    }
+
+    void resetToDefaults() {
+        fPathPts[0] = {300, 400};
+        fPathPts[1] = {500, 400};
+        fPathPts[2] = {700, 400};
+        fPathPts[3] = {900, 400};
+        fPathPts[4] = {1100, 400};
+
+        fWidth = 175;
+
+        fLengthMetric = SkVarWidthStroker::LengthMetric::kPathLength;
+        fDistFncs = fDefaultsDistFncs;
+        fDistFncsInner = fDefaultsDistFncs;
+    }
+
+    void makePath(SkPath* path) {
+        path->moveTo(fPathPts[0]);
+        path->quadTo(fPathPts[1], fPathPts[2]);
+        path->quadTo(fPathPts[3], fPathPts[4]);
+    }
+
+    static ScalarBezCurve makeDistFnc(const std::vector<DistFncMenuItem>& fncs, float strokeWidth) {
+        const float radius = strokeWidth / 2;
+        for (const auto& df : fncs) {
+            if (df.fSelected) {
+                return ScalarBezCurve::Mul(ScalarBezCurve(df.fDegree, df.fWeights), radius);
+            }
+        }
+        SkASSERT(false);
+        return ScalarBezCurve(0, {radius});
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        canvas->drawColor(0xFFEEEEEE);
+
+        SkPath path;
+        this->makePath(&path);
+
+        fStrokePaint.setStrokeWidth(fWidth);
+
+        // Elber-Cohen stroker result
+        ScalarBezCurve distFnc = makeDistFnc(fDistFncs, fWidth);
+        ScalarBezCurve distFncInner =
+                fDifferentInnerFunc ? makeDistFnc(fDistFncsInner, fWidth) : distFnc;
+        SkVarWidthStroker stroker;
+        SkPath fillPath =
+                stroker.getFillPath(path, fStrokePaint, distFnc, distFncInner, fLengthMetric);
+        fillPath.setFillType(SkPathFillType::kWinding);
+        canvas->drawPath(fillPath, fNewFillPaint);
+
+        if (fShowHidden) {
+            canvas->drawPath(fillPath, fHiddenPaint);
+        }
+
+        if (fShowSkeleton) {
+            canvas->drawPath(path, fSkeletonPaint);
+            canvas->drawPoints(SkCanvas::kPoints_PointMode, fPathPts.size(), fPathPts.data(),
+                               fPtsPaint);
+        }
+
+        if (fShowStrokePoints) {
+            drawStrokePoints(canvas, fillPath);
+        }
+
+        if (fShowUI) {
+            drawUI();
+        }
+
+        if (fShowErrorCurve && viz::outerErr != nullptr) {
+            SkPaint firstApproxPaint;
+            firstApproxPaint.setStrokeWidth(4);
+            firstApproxPaint.setStyle(SkPaint::kStroke_Style);
+            firstApproxPaint.setColor(SK_ColorRED);
+            canvas->drawPath(viz::outerFirstApprox, firstApproxPaint);
+            drawErrorCurve(canvas, *viz::outerErr);
+        }
+    }
+
+    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
+        const SkScalar tol = 4;
+        const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
+        for (size_t i = 0; i < fPathPts.size(); ++i) {
+            if (r.intersects(SkRect::MakeXYWH(fPathPts[i].fX, fPathPts[i].fY, 1, 1))) {
+                return new Click([this, i](Click* c) {
+                    fPathPts[i] = c->fCurr;
+                    return true;
+                });
+            }
+        }
+        return nullptr;
+    }
+
+    void drawStrokePoints(SkCanvas* canvas, const SkPath& fillPath) {
+        SkPath::Iter it(fillPath, false);
+        SkPoint points[4];
+        SkPath::Verb verb;
+        std::vector<SkPoint> pointsVec, ctrlPts;
+        while ((verb = it.next(&points[0])) != SkPath::kDone_Verb) {
+            switch (verb) {
+                case SkPath::kLine_Verb:
+                    pointsVec.push_back(points[1]);
+                    break;
+                case SkPath::kQuad_Verb:
+                    ctrlPts.push_back(points[1]);
+                    pointsVec.push_back(points[2]);
+                    break;
+                case SkPath::kMove_Verb:
+                    pointsVec.push_back(points[0]);
+                    break;
+                case SkPath::kClose_Verb:
+                    break;
+                default:
+                    SkDebugf("Unhandled path verb %d for stroke points\n", verb);
+                    SkASSERT(false);
+                    break;
+            }
+        }
+
+        canvas->drawPoints(SkCanvas::kPoints_PointMode, pointsVec.size(), pointsVec.data(),
+                           fStrokePointsPaint);
+        fStrokePointsPaint.setColor(SK_ColorBLUE);
+        fStrokePointsPaint.setStrokeWidth(3);
+        canvas->drawPoints(SkCanvas::kPoints_PointMode, ctrlPts.size(), ctrlPts.data(),
+                           fStrokePointsPaint);
+        fStrokePointsPaint.setColor(SK_ColorBLACK);
+        fStrokePointsPaint.setStrokeWidth(5);
+    }
+
+    void drawErrorCurve(SkCanvas* canvas, const ScalarBezCurve& E) {
+        const float winW = fWinSize.width() * 0.75f, winH = fWinSize.height() * 0.25f;
+        const float padding = 25;
+        const SkRect box = SkRect::MakeXYWH(padding, fWinSize.height() - winH - padding,
+                                            winW - 2 * padding, winH);
+        constexpr int nsegs = 100;
+        constexpr float dt = 1.0f / nsegs;
+        constexpr float dx = 10.0f;
+        const int deg = E.degree();
+        SkPath path;
+        for (int i = 0; i < nsegs; i++) {
+            const float tmin = i * dt, tmax = (i + 1) * dt;
+            ScalarBezCurve left(deg), right(deg);
+            E.split(tmax, &left, &right);
+            const float tRel = tmin / tmax;
+            ScalarBezCurve rl(deg), rr(deg);
+            left.split(tRel, &rl, &rr);
+
+            const float x = i * dx;
+            if (i == 0) {
+                path.moveTo(x, -rr[0]);
+            }
+            path.lineTo(x + dx, -rr[deg]);
+        }
+
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setAntiAlias(true);
+        paint.setStrokeWidth(0);
+        paint.setColor(SK_ColorRED);
+        const SkRect pathBounds = path.computeTightBounds();
+        constexpr float yAxisMax = 8000;
+        const float sx = box.width() / pathBounds.width();
+        const float sy = box.height() / (2 * yAxisMax);
+        canvas->save();
+        canvas->translate(box.left(), box.top() + box.height() / 2);
+        canvas->scale(sx, sy);
+        canvas->drawPath(path, paint);
+
+        SkPath axes;
+        axes.moveTo(0, 0);
+        axes.lineTo(pathBounds.width(), 0);
+        axes.moveTo(0, -yAxisMax);
+        axes.lineTo(0, yAxisMax);
+        paint.setColor(SK_ColorBLACK);
+        paint.setAntiAlias(false);
+        canvas->drawPath(axes, paint);
+
+        canvas->restore();
+    }
+
+    void drawUI() {
+        static constexpr auto kUIOpacity = 0.35f;
+        static constexpr float kUIWidth = 200.0f, kUIHeight = 400.0f;
+        ImGui::SetNextWindowBgAlpha(kUIOpacity);
+        if (ImGui::Begin("E-C Controls", nullptr,
+                         ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize |
+                                 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
+                                 ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) {
+            const SkRect uiArea = SkRect::MakeXYWH(10, 10, kUIWidth, kUIHeight);
+            ImGui::SetWindowPos(ImVec2(uiArea.x(), uiArea.y()));
+            ImGui::SetWindowSize(ImVec2(uiArea.width(), uiArea.height()));
+
+            const auto drawControls = [](std::vector<DistFncMenuItem>& distFncs,
+                                         const std::string& menuPfx,
+                                         const std::string& ptPfx) {
+                std::string degreeMenuLabel = menuPfx + ": ";
+                for (const auto& df : distFncs) {
+                    if (df.fSelected) {
+                        degreeMenuLabel += df.fName;
+                        break;
+                    }
+                }
+                if (ImGui::BeginMenu(degreeMenuLabel.c_str())) {
+                    for (size_t i = 0; i < distFncs.size(); i++) {
+                        if (ImGui::MenuItem(distFncs[i].fName.c_str(), nullptr,
+                                            distFncs[i].fSelected)) {
+                            for (size_t j = 0; j < distFncs.size(); j++) {
+                                distFncs[j].fSelected = j == i;
+                            }
+                        }
+                    }
+                    ImGui::EndMenu();
+                }
+
+                for (auto& df : distFncs) {
+                    if (df.fSelected) {
+                        for (int i = 0; i <= df.fDegree; i++) {
+                            const std::string label = ptPfx + std::to_string(i);
+                            ImGui::SliderFloat(label.c_str(), &(df.fWeights[i]), 0, 1);
+                        }
+                    }
+                }
+            };
+
+            const std::array<std::pair<std::string, SkVarWidthStroker::LengthMetric>, 2> metrics = {
+                    std::make_pair("% path length", SkVarWidthStroker::LengthMetric::kPathLength),
+                    std::make_pair("% segment count",
+                                   SkVarWidthStroker::LengthMetric::kNumSegments),
+            };
+            if (ImGui::BeginMenu("Interpolation metric:")) {
+                for (const auto& metric : metrics) {
+                    if (ImGui::MenuItem(metric.first.c_str(), nullptr,
+                                        fLengthMetric == metric.second)) {
+                        fLengthMetric = metric.second;
+                    }
+                }
+                ImGui::EndMenu();
+            }
+
+            drawControls(fDistFncs, "Degree", "P");
+
+            if (ImGui::CollapsingHeader("Inner stroke", true)) {
+                fDifferentInnerFunc = true;
+                drawControls(fDistFncsInner, "Degree (inner)", "Q");
+            } else {
+                fDifferentInnerFunc = false;
+            }
+        }
+        ImGui::End();
+    }
+
+    bool fShowHidden, fShowSkeleton, fShowStrokePoints, fShowUI, fDifferentInnerFunc,
+            fShowErrorCurve;
+    float fWidth = 175;
+    SkPaint fPtsPaint, fStrokePaint, fNewFillPaint, fHiddenPaint, fSkeletonPaint,
+            fStrokePointsPaint;
+    inline static constexpr int kNPts = 5;
+    std::array<SkPoint, kNPts> fPathPts;
+    SkSize fWinSize;
+    SkVarWidthStroker::LengthMetric fLengthMetric;
+    const std::vector<DistFncMenuItem> fDefaultsDistFncs = {
+            DistFncMenuItem("Linear", 1, true), DistFncMenuItem("Quadratic", 2, false),
+            DistFncMenuItem("Cubic", 3, false), DistFncMenuItem("One Louder (11)", 11, false),
+            DistFncMenuItem("30?!", 30, false)};
+    std::vector<DistFncMenuItem> fDistFncs = fDefaultsDistFncs;
+    std::vector<DistFncMenuItem> fDistFncsInner = fDefaultsDistFncs;
+
+    using INHERITED = Sample;
+};
+
+DEF_SAMPLE(return new VariableWidthStroker;)
diff --git a/third_party/skia/samplecode/SampleVertices.cpp b/third_party/skia/samplecode/SampleVertices.cpp
index 25afd41..d2c7fea 100644
--- a/third_party/skia/samplecode/SampleVertices.cpp
+++ b/third_party/skia/samplecode/SampleVertices.cpp
@@ -34,7 +34,8 @@
     pixels[0] = pixels[2] = color0;
     pixels[1] = pixels[3] = color1;
 
-    return bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat);
+    return bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat,
+                         SkSamplingOptions(SkFilterMode::kLinear));
 }
 
 static sk_sp<SkShader> make_shader1(const SkIPoint& size) {
@@ -73,7 +74,6 @@
     void onDrawContent(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setDither(true);
-        paint.setFilterQuality(kLow_SkFilterQuality);
 
         for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) {
             auto verts = SkVertices::MakeCopy(fRecs[i].fMode, fRecs[i].fCount,
@@ -204,7 +204,7 @@
 
     Rec fRecs[3];
 
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleWritePixels.cpp b/third_party/skia/samplecode/SampleWritePixels.cpp
index f6d31e5..eeed376 100644
--- a/third_party/skia/samplecode/SampleWritePixels.cpp
+++ b/third_party/skia/samplecode/SampleWritePixels.cpp
@@ -32,9 +32,9 @@
     WritePixelsView() {}
 
 protected:
-    virtual SkString name() { return SkString("WritePixels"); }
+    SkString name() override { return SkString("WritePixels"); }
 
-    virtual void onDrawContent(SkCanvas* canvas) {
+    void onDrawContent(SkCanvas* canvas) override {
         SkBitmap bitmap;
         create_bitmap(&bitmap);
         int x = bitmap.width() / 2;
@@ -50,7 +50,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/third_party/skia/samplecode/SampleXfer.cpp b/third_party/skia/samplecode/SampleXfer.cpp
index ba0a939..68b179e 100644
--- a/third_party/skia/samplecode/SampleXfer.cpp
+++ b/third_party/skia/samplecode/SampleXfer.cpp
@@ -193,9 +193,114 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
+DEF_SAMPLE( return new XferDemo; )
 
 //////////////////////////////////////////////////////////////////////////////
 
-DEF_SAMPLE( return new XferDemo; )
+#include "tools/Resources.h"
+
+class CubicResamplerDemo : public Sample {
+    struct Rec {
+        sk_sp<SkImage>  fImage;
+        SkRect          fBounds;
+
+        void draw(SkCanvas* canvas, SkCubicResampler cubic) const {
+            SkRect r = fBounds;
+            SkPaint paint;
+
+            SkMatrix lm = SkMatrix::Translate(r.x(), r.y())
+                        * SkMatrix::Scale(10, 10);
+            paint.setShader(fImage->makeShader(SkSamplingOptions(), lm));
+            canvas->drawRect(r, paint);
+
+            r.offset(r.width() + 10, 0);
+            lm.postTranslate(r.width() + 10, 0);
+
+            paint.setShader(fImage->makeShader(SkSamplingOptions(SkFilterMode::kLinear), lm));
+            canvas->drawRect(r, paint);
+
+            r.offset(r.width() + 10, 0);
+            lm.postTranslate(r.width() + 10, 0);
+
+            paint.setShader(fImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+                                               SkSamplingOptions(cubic), &lm));
+            canvas->drawRect(r, paint);
+        }
+    };
+    std::vector<Rec> fRecs;
+
+public:
+    CubicResamplerDemo() {
+        const char* names[] = {
+            "images/mandrill_128.png",
+            "images/rle.bmp",
+            "images/example_4.png",
+        };
+        SkRect r = {10, 10, 200, 200};
+        for (auto name : names) {
+            fRecs.push_back({GetResourceAsImage(name), r});
+            r.offset(0, r.height() + 10);
+        }
+
+        fDomain.setXYWH(r.fLeft + 3*r.width() + 40, 50, 200, 200);
+        fCubic = {.3f, .5f};
+    }
+
+protected:
+    SkString name() override { return SkString("CubicResampler"); }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        for (const auto& rec : fRecs) {
+            rec.draw(canvas, fCubic);
+        }
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStroke(true);
+        canvas->drawRect(fDomain, paint);
+
+        paint.setColor(SK_ColorRED);
+        paint.setStroke(false);
+        SkPoint loc = SkMatrix::RectToRect({0,0,1,1}, fDomain).mapXY(fCubic.B, fCubic.C);
+        canvas->drawCircle(loc.fX, loc.fY, 8, paint);
+
+        SkString str;
+        str.printf("B=%4.2f  C=%4.2f", fCubic.B, fCubic.C);
+        SkFont font;
+        font.setSize(25);
+        font.setEdging(SkFont::Edging::kAntiAlias);
+        paint.setColor(SK_ColorBLACK);
+        canvas->drawSimpleText(str.c_str(), str.size(), SkTextEncoding::kUTF8,
+                               fDomain.fLeft + 10, fDomain.fBottom + 40, font, paint);
+    }
+
+    static float pin_unitize(float min, float max, float value) {
+        return (std::min(std::max(value, min), max) - min) / (max - min);
+    }
+    static SkPoint pin_unitize(const SkRect& r, SkPoint p) {
+        return {
+            pin_unitize(r.fLeft, r.fRight,  p.fX),
+            pin_unitize(r.fTop,  r.fBottom, p.fY),
+        };
+    }
+
+    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override {
+        if (fDomain.contains(x, y)) {
+            return new Click([this](Click* click) {
+                auto [B, C] = pin_unitize(fDomain, click->fCurr);
+                fCubic = {B, C};
+                return true;
+            });
+        }
+        return nullptr;
+    }
+
+private:
+    SkRect                  fDomain;
+    SkImage::CubicResampler fCubic;
+
+    using INHERITED = Sample;
+};
+DEF_SAMPLE( return new CubicResamplerDemo; )
diff --git a/third_party/skia/samplecode/SampleXfermodesBlur.cpp b/third_party/skia/samplecode/SampleXfermodesBlur.cpp
index 9ec35dc..bd603d5 100644
--- a/third_party/skia/samplecode/SampleXfermodesBlur.cpp
+++ b/third_party/skia/samplecode/SampleXfermodesBlur.cpp
@@ -25,8 +25,8 @@
 #include "src/utils/SkUTF.h"
 
 #include "include/core/SkColorPriv.h"
+#include "include/core/SkMaskFilter.h"
 #include "include/core/SkStream.h"
-#include "include/effects/SkBlurMaskFilter.h"
 
 static void setNamedTypeface(SkFont* font, const char name[]) {
     font->setTypeface(SkTypeface::MakeFromName(name, SkFontStyle()));
@@ -73,9 +73,9 @@
     }
 
 protected:
-    virtual SkString name() { return SkString("XfermodesBlur"); }
+    SkString name() override { return SkString("XfermodesBlur"); }
 
-    virtual void onDrawContent(SkCanvas* canvas) {
+    void onDrawContent(SkCanvas* canvas) override {
         canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
 
         const SkBlendMode gModes[] = {
@@ -98,13 +98,13 @@
         const SkScalar h = SkIntToScalar(H);
         SkMatrix m;
         m.setScale(SkIntToScalar(6), SkIntToScalar(6));
-        auto s = fBG.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, &m);
+        auto s = fBG.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions(), m);
 
         SkFont font;
         font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
         setNamedTypeface(&font, "Menlo Regular");
 
-        const int W = 5;
+        const int kWrap = 5;
 
         SkScalar x0 = 0;
         for (int twice = 0; twice < 2; twice++) {
@@ -131,7 +131,7 @@
                 SkTextUtils::DrawString(canvas, label, x + w/2, y - font.getSize()/2, font, SkPaint(),
                                         SkTextUtils::kCenter_Align);
                 x += w + SkIntToScalar(10);
-                if ((i % W) == W - 1) {
+                if ((i % kWrap) == kWrap - 1) {
                     x = x0;
                     y += h + SkIntToScalar(30);
                 }
@@ -141,7 +141,7 @@
     }
 
 private:
-    typedef Sample INHERITED;
+    using INHERITED = Sample;
 };
 
 //////////////////////////////////////////////////////////////////////////////