Clean up GrPathUtils

Removes unused code, including utilities for dealing with KLM
functionals for the implicit cubic function. The implicit has proven
to not be a very good tool for rendering cubics.

Change-Id: I577b50a9eb296c52dc0101a20394480a4a008654
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/329440
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/bench/CubicKLMBench.cpp b/bench/CubicKLMBench.cpp
deleted file mode 100644
index d9ce672..0000000
--- a/bench/CubicKLMBench.cpp
+++ /dev/null
@@ -1,63 +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 "bench/Benchmark.h"
-
-#include "src/core/SkGeometry.h"
-#include "src/gpu/geometry/GrPathUtils.h"
-
-class CubicKLMBench : public Benchmark {
-public:
-    CubicKLMBench(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
-                  SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3)  {
-        fPoints[0].set(x0, y0);
-        fPoints[1].set(x1, y1);
-        fPoints[2].set(x2, y2);
-        fPoints[3].set(x3, y3);
-
-        fName = "cubic_klm_";
-        switch (SkClassifyCubic(fPoints)) {
-            case SkCubicType::kSerpentine:
-                fName.append("serp");
-                break;
-            case SkCubicType::kLoop:
-                fName.append("loop");
-                break;
-            default:
-                SK_ABORT("Unexpected cubic type");
-                break;
-        }
-    }
-
-    bool isSuitableFor(Backend backend) override {
-        return backend == kNonRendering_Backend;
-    }
-
-    const char* onGetName() override {
-        return fName.c_str();
-    }
-
-    void onDraw(int loops, SkCanvas*) override {
-        SkPoint dst[10];
-        SkMatrix klm;
-        int loopIdx;
-        for (int i = 0; i < loops * 50000; ++i) {
-            GrPathUtils::chopCubicAtLoopIntersection(fPoints, dst, &klm, &loopIdx);
-        }
-    }
-
-private:
-    SkPoint     fPoints[4];
-    SkString    fName;
-
-    using INHERITED = Benchmark;
-};
-
-DEF_BENCH( return new CubicKLMBench(285.625f, 499.687f, 411.625f, 808.188f,
-                                    1064.62f, 135.688f, 1042.63f, 585.187f); )
-DEF_BENCH( return new CubicKLMBench(635.625f, 614.687f, 171.625f, 236.188f,
-                                    1064.62f, 135.688f, 516.625f, 570.187f); )
diff --git a/gn/bench.gni b/gn/bench.gni
index 79aa70f..6c31fb0 100644
--- a/gn/bench.gni
+++ b/gn/bench.gni
@@ -38,7 +38,6 @@
   "$_bench/ControlBench.cpp",
   "$_bench/CoverageBench.cpp",
   "$_bench/CreateBackendTextureBench.cpp",
-  "$_bench/CubicKLMBench.cpp",
   "$_bench/CubicMapBench.cpp",
   "$_bench/DDLRecorderBench.cpp",
   "$_bench/DashBench.cpp",
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp
index d918c18..7e6c394 100644
--- a/samplecode/SampleCCPRGeometry.cpp
+++ b/samplecode/SampleCCPRGeometry.cpp
@@ -67,7 +67,6 @@
 
     PrimitiveType fPrimitiveType = PrimitiveType::kCubics;
     SkCubicType fCubicType;
-    SkMatrix fCubicKLM;
 
     SkPoint fPoints[4] = {
             {100.05f, 100.05f}, {400.75f, 100.05f}, {400.75f, 300.95f}, {100.05f, 300.95f}};
@@ -154,27 +153,6 @@
     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);
 
@@ -248,12 +226,6 @@
 
     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);
@@ -274,8 +246,6 @@
     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);
diff --git a/src/gpu/geometry/GrPathUtils.cpp b/src/gpu/geometry/GrPathUtils.cpp
index 3b5fc01..3e80318 100644
--- a/src/gpu/geometry/GrPathUtils.cpp
+++ b/src/gpu/geometry/GrPathUtils.cpp
@@ -162,54 +162,6 @@
     return a + b;
 }
 
-int GrPathUtils::worstCasePointCount(const SkPath& path, int* subpaths, SkScalar tol) {
-    // You should have called scaleToleranceToSrc, which guarantees this
-    SkASSERT(tol >= gMinCurveTol);
-
-    int pointCount = 0;
-    *subpaths = 1;
-
-    bool first = true;
-
-    SkPath::Iter iter(path, false);
-    SkPath::Verb verb;
-
-    SkPoint pts[4];
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-
-        switch (verb) {
-            case SkPath::kLine_Verb:
-                pointCount += 1;
-                break;
-            case SkPath::kConic_Verb: {
-                SkScalar weight = iter.conicWeight();
-                SkAutoConicToQuads converter;
-                const SkPoint* quadPts = converter.computeQuads(pts, weight, tol);
-                for (int i = 0; i < converter.countQuads(); ++i) {
-                    pointCount += quadraticPointCount(quadPts + 2*i, tol);
-                }
-                [[fallthrough]];
-            }
-            case SkPath::kQuad_Verb:
-                pointCount += quadraticPointCount(pts, tol);
-                break;
-            case SkPath::kCubic_Verb:
-                pointCount += cubicPointCount(pts, tol);
-                break;
-            case SkPath::kMove_Verb:
-                pointCount += 1;
-                if (!first) {
-                    ++(*subpaths);
-                }
-                break;
-            default:
-                break;
-        }
-        first = false;
-    }
-    return pointCount;
-}
-
 void GrPathUtils::QuadUVMatrix::set(const SkPoint qPts[3]) {
     SkMatrix m;
     // We want M such that M * xy_pt = uv_pt
@@ -631,230 +583,3 @@
         convert_noninflect_cubic_to_quads_with_constraint(cubic, tolSqd, dir, quads);
     }
 }
-
-////////////////////////////////////////////////////////////////////////////////
-
-using ExcludedTerm = GrPathUtils::ExcludedTerm;
-
-ExcludedTerm GrPathUtils::calcCubicInverseTransposePowerBasisMatrix(const SkPoint p[4],
-                                                                    SkMatrix* out) {
-    static_assert(SK_SCALAR_IS_FLOAT);
-
-    // First convert the bezier coordinates p[0..3] to power basis coefficients X,Y(,W=[0 0 0 1]).
-    // M3 is the matrix that does this conversion. The homogeneous equation for the cubic becomes:
-    //
-    //                                     | X   Y   0 |
-    // C(t,s) = [t^3  t^2*s  t*s^2  s^3] * | .   .   0 |
-    //                                     | .   .   0 |
-    //                                     | .   .   1 |
-    //
-    const Sk4f M3[3] = {Sk4f(-1, 3, -3, 1),
-                        Sk4f(3, -6, 3, 0),
-                        Sk4f(-3, 3, 0, 0)};
-    // 4th col of M3 =  Sk4f(1, 0, 0, 0)};
-    Sk4f X(p[3].x(), 0, 0, 0);
-    Sk4f Y(p[3].y(), 0, 0, 0);
-    for (int i = 2; i >= 0; --i) {
-        X += M3[i] * p[i].x();
-        Y += M3[i] * p[i].y();
-    }
-
-    // The matrix is 3x4. In order to invert it, we first need to make it square by throwing out one
-    // of the middle two rows. We toss the row that leaves us with the largest absolute determinant.
-    // Since the right column will be [0 0 1], the respective determinants reduce to x0*y2 - y0*x2
-    // and x0*y1 - y0*x1.
-    SkScalar dets[4];
-    Sk4f D = SkNx_shuffle<0,0,2,1>(X) * SkNx_shuffle<2,1,0,0>(Y);
-    D -= SkNx_shuffle<2,3,0,1>(D);
-    D.store(dets);
-    ExcludedTerm skipTerm = SkScalarAbs(dets[0]) > SkScalarAbs(dets[1]) ?
-                            ExcludedTerm::kQuadraticTerm : ExcludedTerm::kLinearTerm;
-    SkScalar det = dets[ExcludedTerm::kQuadraticTerm == skipTerm ? 0 : 1];
-    if (0 == det) {
-        return ExcludedTerm::kNonInvertible;
-    }
-    SkScalar rdet = 1 / det;
-
-    // Compute the inverse-transpose of the power basis matrix with the 'skipRow'th row removed.
-    // Since W=[0 0 0 1], it follows that our corresponding solution will be equal to:
-    //
-    //             |  y1  -x1   x1*y2 - y1*x2 |
-    //     1/det * | -y0   x0  -x0*y2 + y0*x2 |
-    //             |   0    0             det |
-    //
-    SkScalar x[4], y[4], z[4];
-    X.store(x);
-    Y.store(y);
-    (X * SkNx_shuffle<3,3,3,3>(Y) - Y * SkNx_shuffle<3,3,3,3>(X)).store(z);
-
-    int middleRow = ExcludedTerm::kQuadraticTerm == skipTerm ? 2 : 1;
-    out->setAll( y[middleRow] * rdet, -x[middleRow] * rdet,  z[middleRow] * rdet,
-                        -y[0] * rdet,          x[0] * rdet,         -z[0] * rdet,
-                                   0,                    0,                    1);
-
-    return skipTerm;
-}
-
-inline static void calc_serp_kcoeffs(SkScalar tl, SkScalar sl, SkScalar tm, SkScalar sm,
-                                     ExcludedTerm skipTerm, SkScalar outCoeffs[3]) {
-    SkASSERT(ExcludedTerm::kQuadraticTerm == skipTerm || ExcludedTerm::kLinearTerm == skipTerm);
-    outCoeffs[0] = 0;
-    outCoeffs[1] = (ExcludedTerm::kLinearTerm == skipTerm) ? sl*sm : -tl*sm - tm*sl;
-    outCoeffs[2] = tl*tm;
-}
-
-inline static void calc_serp_lmcoeffs(SkScalar t, SkScalar s, ExcludedTerm skipTerm,
-                                      SkScalar outCoeffs[3]) {
-    SkASSERT(ExcludedTerm::kQuadraticTerm == skipTerm || ExcludedTerm::kLinearTerm == skipTerm);
-    outCoeffs[0] = -s*s*s;
-    outCoeffs[1] = (ExcludedTerm::kLinearTerm == skipTerm) ? 3*s*s*t : -3*s*t*t;
-    outCoeffs[2] = t*t*t;
-}
-
-inline static void calc_loop_kcoeffs(SkScalar td, SkScalar sd, SkScalar te, SkScalar se,
-                                     SkScalar tdse, SkScalar tesd, ExcludedTerm skipTerm,
-                                     SkScalar outCoeffs[3]) {
-    SkASSERT(ExcludedTerm::kQuadraticTerm == skipTerm || ExcludedTerm::kLinearTerm == skipTerm);
-    outCoeffs[0] = 0;
-    outCoeffs[1] = (ExcludedTerm::kLinearTerm == skipTerm) ? sd*se : -tdse - tesd;
-    outCoeffs[2] = td*te;
-}
-
-inline static void calc_loop_lmcoeffs(SkScalar t2, SkScalar s2, SkScalar t1, SkScalar s1,
-                                      SkScalar t2s1, SkScalar t1s2, ExcludedTerm skipTerm,
-                                      SkScalar outCoeffs[3]) {
-    SkASSERT(ExcludedTerm::kQuadraticTerm == skipTerm || ExcludedTerm::kLinearTerm == skipTerm);
-    outCoeffs[0] = -s2*s2*s1;
-    outCoeffs[1] = (ExcludedTerm::kLinearTerm == skipTerm) ? s2 * (2*t2s1 + t1s2)
-                                                           : -t2 * (t2s1 + 2*t1s2);
-    outCoeffs[2] = t2*t2*t1;
-}
-
-// For the case when a cubic bezier is actually a quadratic. We duplicate k in l so that the
-// implicit becomes:
-//
-//     k^3 - l*m == k^3 - l*k == k * (k^2 - l)
-//
-// In the quadratic case we can simply assign fixed values at each control point:
-//
-//     | ..K.. |     | pts[0]  pts[1]  pts[2]  pts[3] |      | 0   1/3  2/3  1 |
-//     | ..L.. |  *  |   .       .       .       .    |  ==  | 0     0  1/3  1 |
-//     | ..K.. |     |   1       1       1       1    |      | 0   1/3  2/3  1 |
-//
-static void calc_quadratic_klm(const SkPoint pts[4], double d3, SkMatrix* klm) {
-    SkMatrix klmAtPts;
-    klmAtPts.setAll(0,  1.f/3,  1,
-                    0,      0,  1,
-                    0,  1.f/3,  1);
-
-    SkMatrix inversePts;
-    inversePts.setAll(pts[0].x(),  pts[1].x(),  pts[3].x(),
-                      pts[0].y(),  pts[1].y(),  pts[3].y(),
-                               1,           1,           1);
-    SkAssertResult(inversePts.invert(&inversePts));
-
-    klm->setConcat(klmAtPts, inversePts);
-
-    // If d3 > 0 we need to flip the orientation of our curve
-    // This is done by negating the k and l values
-    if (d3 > 0) {
-        klm->postScale(-1, -1);
-    }
-}
-
-// For the case when a cubic bezier is actually a line. We set K=0, L=1, M=-line, which results in
-// the following implicit:
-//
-//     k^3 - l*m == 0^3 - 1*(-line) == -(-line) == line
-//
-static void calc_line_klm(const SkPoint pts[4], SkMatrix* klm) {
-    SkScalar ny = pts[0].x() - pts[3].x();
-    SkScalar nx = pts[3].y() - pts[0].y();
-    SkScalar k = nx * pts[0].x() + ny * pts[0].y();
-    klm->setAll(  0,   0, 0,
-                  0,   0, 1,
-                -nx, -ny, k);
-}
-
-SkCubicType GrPathUtils::getCubicKLM(const SkPoint src[4], SkMatrix* klm, double tt[2],
-                                     double ss[2]) {
-    double d[4];
-    SkCubicType type = SkClassifyCubic(src, tt, ss, d);
-
-    if (SkCubicType::kLineOrPoint == type) {
-        calc_line_klm(src, klm);
-        return SkCubicType::kLineOrPoint;
-    }
-
-    if (SkCubicType::kQuadratic == type) {
-        calc_quadratic_klm(src, d[3], klm);
-        return SkCubicType::kQuadratic;
-    }
-
-    SkMatrix CIT;
-    ExcludedTerm skipTerm = calcCubicInverseTransposePowerBasisMatrix(src, &CIT);
-    if (ExcludedTerm::kNonInvertible == skipTerm) {
-        // This could technically also happen if the curve were quadratic, but SkClassifyCubic
-        // should have detected that case already with tolerance.
-        calc_line_klm(src, klm);
-        return SkCubicType::kLineOrPoint;
-    }
-
-    const SkScalar t0 = static_cast<SkScalar>(tt[0]), t1 = static_cast<SkScalar>(tt[1]),
-                   s0 = static_cast<SkScalar>(ss[0]), s1 = static_cast<SkScalar>(ss[1]);
-
-    SkMatrix klmCoeffs;
-    switch (type) {
-        case SkCubicType::kCuspAtInfinity:
-            SkASSERT(1 == t1 && 0 == s1); // Infinity.
-            [[fallthrough]];
-        case SkCubicType::kLocalCusp:
-        case SkCubicType::kSerpentine:
-            calc_serp_kcoeffs(t0, s0, t1, s1, skipTerm, &klmCoeffs[0]);
-            calc_serp_lmcoeffs(t0, s0, skipTerm, &klmCoeffs[3]);
-            calc_serp_lmcoeffs(t1, s1, skipTerm, &klmCoeffs[6]);
-            break;
-        case SkCubicType::kLoop: {
-            const SkScalar tdse = t0 * s1;
-            const SkScalar tesd = t1 * s0;
-            calc_loop_kcoeffs(t0, s0, t1, s1, tdse, tesd, skipTerm, &klmCoeffs[0]);
-            calc_loop_lmcoeffs(t0, s0, t1, s1, tdse, tesd, skipTerm, &klmCoeffs[3]);
-            calc_loop_lmcoeffs(t1, s1, t0, s0, tesd, tdse, skipTerm, &klmCoeffs[6]);
-            break;
-        }
-        default:
-            SK_ABORT("Unexpected cubic type.");
-            break;
-    }
-
-    klm->setConcat(klmCoeffs, CIT);
-    return type;
-}
-
-int GrPathUtils::chopCubicAtLoopIntersection(const SkPoint src[4], SkPoint dst[10], SkMatrix* klm,
-                                             int* loopIndex) {
-    SkSTArray<2, SkScalar> chops;
-    *loopIndex = -1;
-
-    double t[2], s[2];
-    if (SkCubicType::kLoop == GrPathUtils::getCubicKLM(src, klm, t, s)) {
-        SkScalar t0 = static_cast<SkScalar>(t[0] / s[0]);
-        SkScalar t1 = static_cast<SkScalar>(t[1] / s[1]);
-        SkASSERT(t0 <= t1); // Technically t0 != t1 in a loop, but there may be FP error.
-
-        if (t0 < 1 && t1 > 0) {
-            *loopIndex = 0;
-            if (t0 > 0) {
-                chops.push_back(t0);
-                *loopIndex = 1;
-            }
-            if (t1 < 1) {
-                chops.push_back(t1);
-                *loopIndex = chops.count() - 1;
-            }
-        }
-    }
-
-    SkChopCubicAt(src, dst, chops.begin(), chops.count());
-    return chops.count() + 1;
-}
diff --git a/src/gpu/geometry/GrPathUtils.h b/src/gpu/geometry/GrPathUtils.h
index 48f537d..6c21cdc 100644
--- a/src/gpu/geometry/GrPathUtils.h
+++ b/src/gpu/geometry/GrPathUtils.h
@@ -19,199 +19,110 @@
  *  Utilities for evaluating paths.
  */
 namespace GrPathUtils {
-    // Very small tolerances will be increased to a minimum threshold value, to avoid division
-    // problems in subsequent math.
-    SkScalar scaleToleranceToSrc(SkScalar devTol,
-                                 const SkMatrix& viewM,
-                                 const SkRect& pathBounds);
 
-    int worstCasePointCount(const SkPath&,
-                            int* subpaths,
-                            SkScalar tol);
+// Very small tolerances will be increased to a minimum threshold value, to avoid division problems
+// in subsequent math.
+SkScalar scaleToleranceToSrc(SkScalar devTol,
+                             const SkMatrix& viewM,
+                             const SkRect& pathBounds);
 
-    uint32_t quadraticPointCount(const SkPoint points[], SkScalar tol);
+uint32_t quadraticPointCount(const SkPoint points[], SkScalar tol);
 
-    uint32_t generateQuadraticPoints(const SkPoint& p0,
-                                     const SkPoint& p1,
-                                     const SkPoint& p2,
-                                     SkScalar tolSqd,
-                                     SkPoint** points,
-                                     uint32_t pointsLeft);
-
-    uint32_t cubicPointCount(const SkPoint points[], SkScalar tol);
-
-    uint32_t generateCubicPoints(const SkPoint& p0,
+uint32_t generateQuadraticPoints(const SkPoint& p0,
                                  const SkPoint& p1,
                                  const SkPoint& p2,
-                                 const SkPoint& p3,
                                  SkScalar tolSqd,
                                  SkPoint** points,
                                  uint32_t pointsLeft);
 
-    // A 2x3 matrix that goes from the 2d space coordinates to UV space where
-    // u^2-v = 0 specifies the quad. The matrix is determined by the control
-    // points of the quadratic.
-    class QuadUVMatrix {
-    public:
-        QuadUVMatrix() {}
-        // Initialize the matrix from the control pts
-        QuadUVMatrix(const SkPoint controlPts[3]) { this->set(controlPts); }
-        void set(const SkPoint controlPts[3]);
+uint32_t cubicPointCount(const SkPoint points[], SkScalar tol);
 
-        /**
-         * Applies the matrix to vertex positions to compute UV coords.
-         *
-         * vertices is a pointer to the first vertex.
-         * vertexCount is the number of vertices.
-         * stride is the size of each vertex.
-         * uvOffset is the offset of the UV values within each vertex.
-         */
-        void apply(void* vertices, int vertexCount, size_t stride, size_t uvOffset) const {
-            intptr_t xyPtr = reinterpret_cast<intptr_t>(vertices);
-            intptr_t uvPtr = reinterpret_cast<intptr_t>(vertices) + uvOffset;
-            float sx = fM[0];
-            float kx = fM[1];
-            float tx = fM[2];
-            float ky = fM[3];
-            float sy = fM[4];
-            float ty = fM[5];
-            for (int i = 0; i < vertexCount; ++i) {
-                const SkPoint* xy = reinterpret_cast<const SkPoint*>(xyPtr);
-                SkPoint* uv = reinterpret_cast<SkPoint*>(uvPtr);
-                uv->fX = sx * xy->fX + kx * xy->fY + tx;
-                uv->fY = ky * xy->fX + sy * xy->fY + ty;
-                xyPtr += stride;
-                uvPtr += stride;
-            }
+uint32_t generateCubicPoints(const SkPoint& p0,
+                             const SkPoint& p1,
+                             const SkPoint& p2,
+                             const SkPoint& p3,
+                             SkScalar tolSqd,
+                             SkPoint** points,
+                             uint32_t pointsLeft);
+
+// A 2x3 matrix that goes from the 2d space coordinates to UV space where u^2-v = 0 specifies the
+// quad. The matrix is determined by the control points of the quadratic.
+class QuadUVMatrix {
+public:
+    QuadUVMatrix() {}
+    // Initialize the matrix from the control pts
+    QuadUVMatrix(const SkPoint controlPts[3]) { this->set(controlPts); }
+    void set(const SkPoint controlPts[3]);
+
+    /**
+     * Applies the matrix to vertex positions to compute UV coords.
+     *
+     * vertices is a pointer to the first vertex.
+     * vertexCount is the number of vertices.
+     * stride is the size of each vertex.
+     * uvOffset is the offset of the UV values within each vertex.
+     */
+    void apply(void* vertices, int vertexCount, size_t stride, size_t uvOffset) const {
+        intptr_t xyPtr = reinterpret_cast<intptr_t>(vertices);
+        intptr_t uvPtr = reinterpret_cast<intptr_t>(vertices) + uvOffset;
+        float sx = fM[0];
+        float kx = fM[1];
+        float tx = fM[2];
+        float ky = fM[3];
+        float sy = fM[4];
+        float ty = fM[5];
+        for (int i = 0; i < vertexCount; ++i) {
+            const SkPoint* xy = reinterpret_cast<const SkPoint*>(xyPtr);
+            SkPoint* uv = reinterpret_cast<SkPoint*>(uvPtr);
+            uv->fX = sx * xy->fX + kx * xy->fY + tx;
+            uv->fY = ky * xy->fX + sy * xy->fY + ty;
+            xyPtr += stride;
+            uvPtr += stride;
         }
-    private:
-        float fM[6];
-    };
+    }
+private:
+    float fM[6];
+};
 
-    // Input is 3 control points and a weight for a bezier conic. Calculates the
-    // three linear functionals (K,L,M) that represent the implicit equation of the
-    // conic, k^2 - lm.
-    //
-    // Output: klm holds the linear functionals K,L,M as row vectors:
-    //
-    //     | ..K.. |   | x |      | k |
-    //     | ..L.. | * | y |  ==  | l |
-    //     | ..M.. |   | 1 |      | m |
-    //
-    void getConicKLM(const SkPoint p[3], const SkScalar weight, SkMatrix* klm);
+// Input is 3 control points and a weight for a bezier conic. Calculates the three linear
+// functionals (K,L,M) that represent the implicit equation of the conic, k^2 - lm.
+//
+// Output: klm holds the linear functionals K,L,M as row vectors:
+//
+//     | ..K.. |   | x |      | k |
+//     | ..L.. | * | y |  ==  | l |
+//     | ..M.. |   | 1 |      | m |
+//
+void getConicKLM(const SkPoint p[3], const SkScalar weight, SkMatrix* klm);
 
-    // Converts a cubic into a sequence of quads. If working in device space
-    // use tolScale = 1, otherwise set based on stretchiness of the matrix. The
-    // result is sets of 3 points in quads. This will preserve the starting and
-    // ending tangent vectors (modulo FP precision).
-    void convertCubicToQuads(const SkPoint p[4],
-                             SkScalar tolScale,
-                             SkTArray<SkPoint, true>* quads);
+// Converts a cubic into a sequence of quads. If working in device space use tolScale = 1, otherwise
+// set based on stretchiness of the matrix. The result is sets of 3 points in quads. This will
+// preserve the starting and ending tangent vectors (modulo FP precision).
+void convertCubicToQuads(const SkPoint p[4],
+                         SkScalar tolScale,
+                         SkTArray<SkPoint, true>* quads);
 
-    // When we approximate a cubic {a,b,c,d} with a quadratic we may have to
-    // ensure that the new control point lies between the lines ab and cd. The
-    // convex path renderer requires this. It starts with a path where all the
-    // control points taken together form a convex polygon. It relies on this
-    // property and the quadratic approximation of cubics step cannot alter it.
-    // This variation enforces this constraint. The cubic must be simple and dir
-    // must specify the orientation of the contour containing the cubic.
-    void convertCubicToQuadsConstrainToTangents(const SkPoint p[4],
-                                                SkScalar tolScale,
-                                                SkPathFirstDirection dir,
-                                                SkTArray<SkPoint, true>* quads);
+// When we approximate a cubic {a,b,c,d} with a quadratic we may have to ensure that the new control
+// point lies between the lines ab and cd. The convex path renderer requires this. It starts with a
+// path where all the control points taken together form a convex polygon. It relies on this
+// property and the quadratic approximation of cubics step cannot alter it. This variation enforces
+// this constraint. The cubic must be simple and dir must specify the orientation of the contour
+// containing the cubic.
+void convertCubicToQuadsConstrainToTangents(const SkPoint p[4],
+                                            SkScalar tolScale,
+                                            SkPathFirstDirection dir,
+                                            SkTArray<SkPoint, true>* quads);
 
-    enum class ExcludedTerm {
-        kNonInvertible,
-        kQuadraticTerm,
-        kLinearTerm
-    };
+// When tessellating curved paths into linear segments, this defines the maximum distance in screen
+// space which a segment may deviate from the mathematically correct value. Above this value, the
+// segment will be subdivided.
+// This value was chosen to approximate the supersampling accuracy of the raster path (16 samples,
+// or one quarter pixel).
+static const SkScalar kDefaultTolerance = SkDoubleToScalar(0.25);
 
-    // Computes the inverse-transpose of the cubic's power basis matrix, after removing a specific
-    // row of coefficients.
-    //
-    // E.g. if the cubic is defined in power basis form as follows:
-    //
-    //                                         | x3   y3   0 |
-    //     C(t,s) = [t^3  t^2*s  t*s^2  s^3] * | x2   y2   0 |
-    //                                         | x1   y1   0 |
-    //                                         | x0   y0   1 |
-    //
-    // And the excluded term is "kQuadraticTerm", then the resulting inverse-transpose will be:
-    //
-    //     | x3   y3   0 | -1 T
-    //     | x1   y1   0 |
-    //     | x0   y0   1 |
-    //
-    // (The term to exclude is chosen based on maximizing the resulting matrix determinant.)
-    //
-    // This can be used to find the KLM linear functionals:
-    //
-    //     | ..K.. |   | ..kcoeffs.. |
-    //     | ..L.. | = | ..lcoeffs.. | * inverse_transpose_power_basis_matrix
-    //     | ..M.. |   | ..mcoeffs.. |
-    //
-    // NOTE: the same term that was excluded here must also be removed from the corresponding column
-    // of the klmcoeffs matrix.
-    //
-    // Returns which row of coefficients was removed, or kNonInvertible if the cubic was degenerate.
-    ExcludedTerm calcCubicInverseTransposePowerBasisMatrix(const SkPoint p[4], SkMatrix* out);
+// We guarantee that no quad or cubic will ever produce more than this many points
+static const int kMaxPointsPerCurve = 1 << 10;
 
-    // Computes the KLM linear functionals for the cubic implicit form. The "right" side of the
-    // curve (when facing in the direction of increasing parameter values) will be the area that
-    // satisfies:
-    //
-    //     k^3 < l*m
-    //
-    // Output:
-    //
-    // klm: Holds the linear functionals K,L,M as row vectors:
-    //
-    //          | ..K.. |   | x |      | k |
-    //          | ..L.. | * | y |  ==  | l |
-    //          | ..M.. |   | 1 |      | m |
-    //
-    // NOTE: the KLM lines are calculated in the same space as the input control points. If you
-    // transform the points the lines will also need to be transformed. This can be done by mapping
-    // the lines with the inverse-transpose of the matrix used to map the points.
-    //
-    // t[],s[]: These are set to the two homogeneous parameter values at which points the lines L&M
-    // intersect with K (See SkClassifyCubic).
-    //
-    // Returns the cubic's classification.
-    SkCubicType getCubicKLM(const SkPoint src[4], SkMatrix* klm, double t[2], double s[2]);
-
-    // Chops the cubic bezier passed in by src, at the double point (intersection point)
-    // if the curve is a cubic loop. If it is a loop, there will be two parametric values for
-    // the double point: t1 and t2. We chop the cubic at these values if they are between 0 and 1.
-    // Return value:
-    // Value of 3: t1 and t2 are both between (0,1), and dst will contain the three cubics,
-    //             dst[0..3], dst[3..6], and dst[6..9] if dst is not nullptr
-    // Value of 2: Only one of t1 and t2 are between (0,1), and dst will contain the two cubics,
-    //             dst[0..3] and dst[3..6] if dst is not nullptr
-    // Value of 1: Neither t1 nor t2 are between (0,1), and dst will contain the one original cubic,
-    //             src[0..3]
-    //
-    // Output:
-    //
-    // klm: Holds the linear functionals K,L,M as row vectors. (See getCubicKLM().)
-    //
-    // loopIndex: This value will tell the caller which of the chopped sections (if any) are the
-    //            actual loop. A value of -1 means there is no loop section. The caller can then use
-    //            this value to decide how/if they want to flip the orientation of this section.
-    //            The flip should be done by negating the k and l values as follows:
-    //
-    //            KLM.postScale(-1, -1)
-    int chopCubicAtLoopIntersection(const SkPoint src[4], SkPoint dst[10], SkMatrix* klm,
-                                    int* loopIndex);
-
-    // When tessellating curved paths into linear segments, this defines the maximum distance
-    // in screen space which a segment may deviate from the mathmatically correct value.
-    // Above this value, the segment will be subdivided.
-    // This value was chosen to approximate the supersampling accuracy of the raster path (16
-    // samples, or one quarter pixel).
-    static const SkScalar kDefaultTolerance = SkDoubleToScalar(0.25);
-
-    // We guarantee that no quad or cubic will ever produce more than this many points
-    static const int kMaxPointsPerCurve = 1 << 10;
 }  // namespace GrPathUtils
+
 #endif