CCPR: Transform path points before handling curves

Does the transform before handling curves. This way all curves can be
carefully chopped in device space in order two avoid msaa.

Bug: skia:
Change-Id: I0508668142cda3fe3fda41f8475c408288aa4a47
Reviewed-on: https://skia-review.googlesource.com/29720
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.cpp b/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.cpp
index 5beba4c..93e81ae 100644
--- a/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.cpp
@@ -66,7 +66,7 @@
  * and "45 degree" device-space bounds (| 1 -1 | * devCoords).
  *                                      | 1  1 |
  */
-class GrCCPRCoverageOpsBuilder::AccumulatingViewMatrix {
+class AccumulatingViewMatrix {
 public:
     AccumulatingViewMatrix(const SkMatrix& m, const SkPoint& initialPoint);
 
@@ -158,6 +158,59 @@
     return true;
 }
 
+using MaxBufferItems = GrCCPRCoverageOpsBuilder::MaxBufferItems;
+
+void MaxBufferItems::countPathItems(GrCCPRCoverageOpsBuilder::ScissorMode scissorMode,
+                                    const SkPath& path) {
+    MaxPrimitives& maxPrimitives = fMaxPrimitives[(int)scissorMode];
+    int currFanPts = 0;
+
+    for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+            case SkPath::kClose_Verb:
+                fMaxFanPoints += currFanPts;
+                maxPrimitives.fMaxTriangles += SkTMax(0, currFanPts - 2);
+                currFanPts = SkPath::kMove_Verb == verb ? 1 : 0;
+                continue;
+            case SkPath::kLine_Verb:
+                SkASSERT(currFanPts > 0);
+                ++currFanPts;
+                continue;
+            case SkPath::kQuad_Verb:
+                SkASSERT(currFanPts > 0);
+                ++currFanPts;
+                ++fMaxControlPoints;
+                ++maxPrimitives.fMaxQuadratics;
+                continue;
+            case SkPath::kCubic_Verb:
+                SkASSERT(currFanPts > 0);
+                // Over-allocate for the worst case when the cubic is chopped into 3 segments.
+                enum { kMaxSegments = 3 };
+                currFanPts += kMaxSegments;
+                // Each cubic segment has two control points.
+                fMaxControlPoints += kMaxSegments * 2;
+                // Each cubic segment also emits two root t,s values as "control points".
+                fMaxControlPoints += kMaxSegments * 2;
+                maxPrimitives.fMaxCubics += kMaxSegments;
+                // The cubic may also turn out to be a quadratic. While we over-allocate by a fair
+                // amount, this is still a relatively small amount of space.
+                ++maxPrimitives.fMaxQuadratics;
+                continue;
+            case SkPath::kConic_Verb:
+                SkASSERT(currFanPts > 0);
+                SkFAIL("Conics are not supported.");
+            default:
+                SkFAIL("Unexpected path verb.");
+        }
+    }
+
+    fMaxFanPoints += currFanPts;
+    maxPrimitives.fMaxTriangles += SkTMax(0, currFanPts - 2);
+
+    ++fMaxPaths;
+}
+
 void GrCCPRCoverageOpsBuilder::parsePath(ScissorMode scissorMode, const SkMatrix& viewMatrix,
                                          const SkPath& path, SkRect* devBounds,
                                          SkRect* devBounds45) {
@@ -179,21 +232,22 @@
     for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
         switch (verb) {
             case SkPath::kMove_Verb:
-                this->startContour(m, pts[ptsIdx++]);
+                this->startContour(m.transform(pts[ptsIdx++]));
                 continue;
             case SkPath::kClose_Verb:
                 this->closeContour();
                 continue;
             case SkPath::kLine_Verb:
-                this->fanTo(m, pts[ptsIdx]);
+                this->fanTo(m.transform(pts[ptsIdx]));
                 break;
             case SkPath::kQuad_Verb:
                 SkASSERT(ptsIdx >= 1); // SkPath should have inserted an implicit moveTo if needed.
-                this->quadraticTo(m, &pts[ptsIdx - 1]);
+                this->quadraticTo(m.transform(pts[ptsIdx]), m.transform(pts[ptsIdx + 1]));
                 break;
             case SkPath::kCubic_Verb:
                 SkASSERT(ptsIdx >= 1); // SkPath should have inserted an implicit moveTo if needed.
-                this->cubicTo(m, &pts[ptsIdx - 1]);
+                this->cubicTo(m.transform(pts[ptsIdx]), m.transform(pts[ptsIdx + 1]),
+                              m.transform(pts[ptsIdx + 2]));
                 break;
             case SkPath::kConic_Verb:
                 SkFAIL("Conics are not supported.");
@@ -235,27 +289,26 @@
     fInstanceIndices[(int)fCurrScissorMode] = fCurrPathIndices;
 }
 
-void GrCCPRCoverageOpsBuilder::startContour(AccumulatingViewMatrix& m, const SkPoint& anchorPoint) {
+void GrCCPRCoverageOpsBuilder::startContour(const SkPoint& anchorPoint) {
     this->closeContour();
-    fCurrPathSpaceAnchorPoint = anchorPoint;
-    fPointsData[fFanPtsIdx++] = m.transform(anchorPoint);
+    fPointsData[fFanPtsIdx++] = fCurrAnchorPoint = fCurrFanPoint = anchorPoint;
     SkASSERT(fCurrContourStartIdx == fFanPtsIdx - 1);
 }
 
-void GrCCPRCoverageOpsBuilder::fanTo(AccumulatingViewMatrix& m, const SkPoint& pt) {
+void GrCCPRCoverageOpsBuilder::fanTo(const SkPoint& pt) {
     SkASSERT(fCurrContourStartIdx < fFanPtsIdx);
-    if (pt == fCurrPathSpaceAnchorPoint) {
-        this->startContour(m, pt);
+    if (pt == fCurrAnchorPoint) {
+        this->startContour(pt);
         return;
     }
-    fPointsData[fFanPtsIdx++] = m.transform(pt);
+    fPointsData[fFanPtsIdx++] = fCurrFanPoint = pt;
 }
 
-void GrCCPRCoverageOpsBuilder::quadraticTo(AccumulatingViewMatrix& m, const SkPoint P[3]) {
+void GrCCPRCoverageOpsBuilder::quadraticTo(SkPoint controlPt, SkPoint endPt) {
     SkASSERT(fCurrPathIndices.fQuadratics < fBaseInstances[(int)fCurrScissorMode].fSerpentines);
 
-    this->fanTo(m, P[2]);
-    fPointsData[fControlPtsIdx++] = m.transform(P[1]);
+    this->fanTo(endPt);
+    fPointsData[fControlPtsIdx++] = controlPt;
 
     fInstanceData[fCurrPathIndices.fQuadratics++].fQuadraticData = {
         fControlPtsIdx - 1,
@@ -263,12 +316,13 @@
     };
 }
 
-void GrCCPRCoverageOpsBuilder::cubicTo(AccumulatingViewMatrix& m, const SkPoint P[4]) {
+void GrCCPRCoverageOpsBuilder::cubicTo(SkPoint controlPt1, SkPoint controlPt2, SkPoint endPt) {
+    SkPoint P[4] = {fCurrFanPoint, controlPt1, controlPt2, endPt};
     double t[2], s[2];
     SkCubicType type = SkClassifyCubic(P, t, s);
 
     if (SkCubicType::kLineOrPoint == type) {
-        this->fanTo(m, P[3]);
+        this->fanTo(P[3]);
         return;
     }
 
@@ -278,8 +332,7 @@
         SkScalar x2 = P[2].y() - P[3].y(),  y2 = P[3].x() - P[2].x(),
                  k2 = x2 * P[3].x() + y2 * P[3].y();
         SkScalar rdet = 1 / (x1*y2 - y1*x2);
-        SkPoint Q[3] = {P[0], {(y2*k1 - y1*k2) * rdet, (x1*k2 - x2*k1) * rdet}, P[3]};
-        this->quadraticTo(m, Q);
+        this->quadraticTo({(y2*k1 - y1*k2) * rdet, (x1*k2 - x2*k1) * rdet}, P[3]);
         return;
     }
 
@@ -310,7 +363,7 @@
         }
 
         // (This might put ts0/ts1 out of order, but it doesn't matter anymore at this point.)
-        this->emitCubicSegment(m, type, chopped.first(),
+        this->emitCubicSegment(type, chopped.first(),
                                to_skpoint(t[1 - x], s[1 - x] * chopT), to_skpoint(1, 1));
         t[x] = 0;
         s[x] = 1;
@@ -322,17 +375,16 @@
         C = chopped.second();
     }
 
-    this->emitCubicSegment(m, type, C, to_skpoint(t[0], s[0]), to_skpoint(t[1], s[1]));
+    this->emitCubicSegment(type, C, to_skpoint(t[0], s[0]), to_skpoint(t[1], s[1]));
 }
 
-void GrCCPRCoverageOpsBuilder::emitCubicSegment(AccumulatingViewMatrix& m,
-                                                SkCubicType type, const SkDCubic& C,
+void GrCCPRCoverageOpsBuilder::emitCubicSegment(SkCubicType type, const SkDCubic& C,
                                                 const SkPoint& ts0, const SkPoint& ts1) {
     SkASSERT(fCurrPathIndices.fSerpentines < fCurrPathIndices.fLoops);
 
-    fPointsData[fControlPtsIdx++] = m.transform(to_skpoint(C[1]));
-    fPointsData[fControlPtsIdx++] = m.transform(to_skpoint(C[2]));
-    this->fanTo(m, to_skpoint(C[3]));
+    fPointsData[fControlPtsIdx++] = to_skpoint(C[1]);
+    fPointsData[fControlPtsIdx++] = to_skpoint(C[2]);
+    this->fanTo(to_skpoint(C[3]));
 
     // Also emit the cubic's root t,s values as "control points".
     fPointsData[fControlPtsIdx++] = ts0;
@@ -420,60 +472,6 @@
 
 #endif
 
-using MaxBufferItems = GrCCPRCoverageOpsBuilder::MaxBufferItems;
-
-void MaxBufferItems::countPathItems(GrCCPRCoverageOpsBuilder::ScissorMode scissorMode,
-                                    const SkPath& path) {
-    MaxPrimitives& maxPrimitives = fMaxPrimitives[(int)scissorMode];
-    int currFanPts = 0;
-
-    for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
-        switch (verb) {
-        case SkPath::kMove_Verb:
-        case SkPath::kClose_Verb:
-            fMaxFanPoints += currFanPts;
-            maxPrimitives.fMaxTriangles += SkTMax(0, currFanPts - 2);
-            currFanPts = SkPath::kMove_Verb == verb ? 1 : 0;
-            continue;
-        case SkPath::kLine_Verb:
-            SkASSERT(currFanPts > 0);
-            ++currFanPts;
-            continue;
-        case SkPath::kQuad_Verb:
-            SkASSERT(currFanPts > 0);
-            ++currFanPts;
-            ++fMaxControlPoints;
-            ++maxPrimitives.fMaxQuadratics;
-            continue;
-        case SkPath::kCubic_Verb: {
-            SkASSERT(currFanPts > 0);
-            // Over-allocate for the worst case when the cubic is chopped into 3 segments.
-            static constexpr int kMaxSegments = 3;
-            currFanPts += kMaxSegments;
-            // Each cubic segment has two control points.
-            fMaxControlPoints += kMaxSegments * 2;
-            // Each cubic segment also emits two root t,s values as "control points".
-            fMaxControlPoints += kMaxSegments * 2;
-            maxPrimitives.fMaxCubics += kMaxSegments;
-            // The cubic may also turn out to be a quadratic. While we over-allocate by a fair
-            // amount, this is still a relatively small amount of space.
-            ++maxPrimitives.fMaxQuadratics;
-            continue;
-        }
-        case SkPath::kConic_Verb:
-            SkASSERT(currFanPts > 0);
-            SkFAIL("Conics are not supported.");
-        default:
-            SkFAIL("Unexpected path verb.");
-        }
-    }
-
-    fMaxFanPoints += currFanPts;
-    maxPrimitives.fMaxTriangles += SkTMax(0, currFanPts - 2);
-
-    ++fMaxPaths;
-}
-
 using CoverageOp = GrCCPRCoverageOpsBuilder::CoverageOp;
 
 GrCCPRCoverageOpsBuilder::CoverageOp::CoverageOp(const SkISize& drawBounds,
@@ -597,8 +595,6 @@
     return fTriangles + fQuadratics + fSerpentines + fLoops;
 }
 
-using AccumulatingViewMatrix = GrCCPRCoverageOpsBuilder::AccumulatingViewMatrix;
-
 inline AccumulatingViewMatrix::AccumulatingViewMatrix(const SkMatrix& m,
                                                       const SkPoint& initialPoint) {
     // m45 transforms into 45 degree space in order to find the octagon's diagonals. We could
diff --git a/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.h b/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.h
index 92d0203..710ee88 100644
--- a/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.h
+++ b/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.h
@@ -95,7 +95,6 @@
     std::unique_ptr<GrDrawOp> SK_WARN_UNUSED_RESULT finalize(SkISize drawBounds);
 
     class CoverageOp;
-    class AccumulatingViewMatrix;
 
 private:
     using PrimitiveInstance = GrCCPRCoverageProcessor::PrimitiveInstance;
@@ -116,12 +115,11 @@
         SkIRect            fScissor;
     };
 
-    void startContour(AccumulatingViewMatrix&, const SkPoint& anchorPoint);
-    void fanTo(AccumulatingViewMatrix&, const SkPoint& pt);
-    void quadraticTo(AccumulatingViewMatrix&, const SkPoint P[3]);
-    void cubicTo(AccumulatingViewMatrix&, const SkPoint P[4]);
-    void emitCubicSegment(AccumulatingViewMatrix&, SkCubicType, const SkDCubic&,
-                          const SkPoint& ts0, const SkPoint& ts1);
+    void startContour(const SkPoint& anchorPoint);
+    void fanTo(const SkPoint& pt);
+    void quadraticTo(SkPoint controlPt, SkPoint endPt);
+    void cubicTo(SkPoint controlPt1, SkPoint controlPt2, SkPoint endPt);
+    void emitCubicSegment(SkCubicType, const SkDCubic&, const SkPoint& ts0, const SkPoint& ts1);
     void closeContour();
     void emitHierarchicalFan(int32_t indices[], int count);
     SkDEBUGCODE(void validate();)
@@ -129,7 +127,8 @@
     ScissorMode              fCurrScissorMode;
     PrimitiveTallies         fCurrPathIndices;
     int32_t                  fCurrContourStartIdx;
-    SkPoint                  fCurrPathSpaceAnchorPoint;
+    SkPoint                  fCurrAnchorPoint;
+    SkPoint                  fCurrFanPoint;
 
     sk_sp<GrBuffer>          fPointsBuffer;
     SkPoint*                 fPointsData;