Import Cobalt 25.master.0.1033734
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