Add a GrCCPRGeometry file

Enough ccpr-specific geometry code is in flight that it feels like it
should have its own file.

Bug: skia:
Change-Id: I99ef620a7dc35178cf774b3a4ec6159d46f401c7
Reviewed-on: https://skia-review.googlesource.com/39162
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrPathUtils.cpp b/src/gpu/GrPathUtils.cpp
index 0089fc1..a8abf81 100644
--- a/src/gpu/GrPathUtils.cpp
+++ b/src/gpu/GrPathUtils.cpp
@@ -567,66 +567,6 @@
     }
 }
 
-static inline Sk2f normalize(const Sk2f& n) {
-    Sk2f nn = n*n;
-    return n * (nn + SkNx_shuffle<1,0>(nn)).rsqrt();
-}
-
-bool GrPathUtils::chopMonotonicQuads(const SkPoint p[3], SkPoint dst[5]) {
-    GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
-    GR_STATIC_ASSERT(2 * sizeof(float) == sizeof(SkPoint));
-    GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
-
-    Sk2f p0 = Sk2f::Load(&p[0]);
-    Sk2f p1 = Sk2f::Load(&p[1]);
-    Sk2f p2 = Sk2f::Load(&p[2]);
-
-    Sk2f tan0 = p1 - p0;
-    Sk2f tan1 = p2 - p1;
-    Sk2f v = p2 - p0;
-
-    // Check if the curve is already monotonic (i.e. (tan0 dot v) >= 0 and (tan1 dot v) >= 0).
-    // This should almost always be this case for well-behaved curves in the real world.
-    float dot0[2], dot1[2];
-    (tan0 * v).store(dot0);
-    (tan1 * v).store(dot1);
-    if (dot0[0] + dot0[1] >= 0 && dot1[0] + dot1[1] >= 0) {
-        return false;
-    }
-
-    // Chop the curve into two segments with equal curvature. To do this we find the T value whose
-    // tangent is perpendicular to the vector that bisects tan0 and -tan1.
-    Sk2f n = normalize(tan0) - normalize(tan1);
-
-    // This tangent can be found where (dQ(t) dot n) = 0:
-    //
-    //   0 = (dQ(t) dot n) = | 2*t  1 | * | p0 - 2*p1 + p2 | * | n |
-    //                                    | -2*p0 + 2*p1   |   | . |
-    //
-    //                     = | 2*t  1 | * | tan1 - tan0 | * | n |
-    //                                    | 2*tan0      |   | . |
-    //
-    //                     = 2*t * ((tan1 - tan0) dot n) + (2*tan0 dot n)
-    //
-    //   t = (tan0 dot n) / ((tan0 - tan1) dot n)
-    Sk2f dQ1n = (tan0 - tan1) * n;
-    Sk2f dQ0n = tan0 * n;
-    Sk2f t = (dQ0n + SkNx_shuffle<1,0>(dQ0n)) / (dQ1n + SkNx_shuffle<1,0>(dQ1n));
-    t = Sk2f::Min(Sk2f::Max(t, 0), 1); // Clamp for FP error.
-
-    Sk2f p01 = SkNx_fma(t, tan0, p0);
-    Sk2f p12 = SkNx_fma(t, tan1, p1);
-    Sk2f p012 = SkNx_fma(t, p12 - p01, p01);
-
-    p0.store(&dst[0]);
-    p01.store(&dst[1]);
-    p012.store(&dst[2]);
-    p12.store(&dst[3]);
-    p2.store(&dst[4]);
-
-    return true;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
 /**
diff --git a/src/gpu/GrPathUtils.h b/src/gpu/GrPathUtils.h
index 4643bff..e9dee73 100644
--- a/src/gpu/GrPathUtils.h
+++ b/src/gpu/GrPathUtils.h
@@ -124,14 +124,6 @@
                                                 SkPathPriv::FirstDirection dir,
                                                 SkTArray<SkPoint, true>* quads);
 
-    // Ensures that a quadratic bezier is monotonic with respect to its vector [P2 - P0] (the vector
-    // between its endpoints). In the event that the curve is not monotonic, it is chopped into two
-    // segments that are monotonic. This should be rare for well-behaved curves in the real world.
-    //
-    // Returns false if the curve was already monotonic.
-    //         true if it was chopped into two monotonic segments, now contained in dst.
-    bool chopMonotonicQuads(const SkPoint p[3], SkPoint dst[5]);
-
     // 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:
diff --git a/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.cpp b/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.cpp
index 361a159..f2d27e8 100644
--- a/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.cpp
+++ b/src/gpu/ccpr/GrCCPRCoverageOpsBuilder.cpp
@@ -11,7 +11,6 @@
 #include "GrGpuCommandBuffer.h"
 #include "GrOnFlushResourceProvider.h"
 #include "GrOpFlushState.h"
-#include "GrPathUtils.h"
 #include "SkGeometry.h"
 #include "SkMakeUnique.h"
 #include "SkMathPriv.h"
@@ -19,6 +18,7 @@
 #include "SkPathPriv.h"
 #include "SkPoint.h"
 #include "SkNx.h"
+#include "ccpr/GrCCPRGeometry.h"
 #include "ops/GrDrawOp.h"
 #include "../pathops/SkPathOpsCubic.h"
 #include <numeric>
@@ -311,9 +311,8 @@
 void GrCCPRCoverageOpsBuilder::quadraticTo(SkPoint controlPt, SkPoint endPt) {
     SkASSERT(fCurrPathIndices.fQuadratics+2 <= fBaseInstances[(int)fCurrScissorMode].fSerpentines);
 
-    SkPoint P[3] = {fCurrFanPoint, controlPt, endPt};
     SkPoint chopped[5];
-    if (GrPathUtils::chopMonotonicQuads(P, chopped)) {
+    if (GrCCPRChopMonotonicQuadratics(fCurrFanPoint, controlPt, endPt, chopped)) {
         this->fanTo(chopped[2]);
         fPointsData[fControlPtsIdx++] = chopped[1];
         fInstanceData[fCurrPathIndices.fQuadratics++].fQuadraticData = {
diff --git a/src/gpu/ccpr/GrCCPRGeometry.cpp b/src/gpu/ccpr/GrCCPRGeometry.cpp
new file mode 100644
index 0000000..f756f6e
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRGeometry.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 "GrCCPRGeometry.h"
+
+#include "GrTypes.h"
+#include "SkPoint.h"
+#include "SkNx.h"
+#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+
+// We convert between SkPoint and Sk2f freely throughout this file.
+GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
+GR_STATIC_ASSERT(2 * sizeof(float) == sizeof(SkPoint));
+GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
+
+static inline Sk2f normalize(const Sk2f& n) {
+    Sk2f nn = n*n;
+    return n * (nn + SkNx_shuffle<1,0>(nn)).rsqrt();
+}
+
+static inline float dot(const Sk2f& a, const Sk2f& b) {
+    float product[2];
+    (a * b).store(product);
+    return product[0] + product[1];
+}
+
+// Returns whether the (convex) curve segment is monotonic with respect to [endPt - startPt].
+static inline bool is_convex_curve_monotonic(const Sk2f& startPt, const Sk2f& startTan,
+                                             const Sk2f& endPt, const Sk2f& endTan) {
+    Sk2f v = endPt - startPt;
+    float dot0 = dot(startTan, v);
+    float dot1 = dot(endTan, v);
+
+    // A small, negative tolerance handles floating-point error in the case when one tangent
+    // approaches 0 length, meaning the (convex) curve segment is effectively a flat line.
+    float tolerance = -std::max(std::abs(dot0), std::abs(dot1)) * SK_ScalarNearlyZero;
+    return dot0 >= tolerance && dot1 >= tolerance;
+}
+
+static inline Sk2f lerp(const Sk2f& a, const Sk2f& b, const Sk2f& t) {
+    return SkNx_fma(t, b - a, a);
+}
+
+bool GrCCPRChopMonotonicQuadratics(const SkPoint& startPt, const SkPoint& controlPt,
+                                   const SkPoint& endPt, SkPoint dst[5]) {
+    Sk2f p0 = Sk2f::Load(&startPt);
+    Sk2f p1 = Sk2f::Load(&controlPt);
+    Sk2f p2 = Sk2f::Load(&endPt);
+
+    Sk2f tan0 = p1 - p0;
+    Sk2f tan1 = p2 - p1;
+    // This should almost always be this case for well-behaved curves in the real world.
+    if (is_convex_curve_monotonic(p0, tan0, p2, tan1)) {
+        return false;
+    }
+
+    // Chop the curve into two segments with equal curvature. To do this we find the T value whose
+    // tangent is perpendicular to the vector that bisects tan0 and -tan1.
+    Sk2f n = normalize(tan0) - normalize(tan1);
+
+    // This tangent can be found where (dQ(t) dot n) = 0:
+    //
+    //   0 = (dQ(t) dot n) = | 2*t  1 | * | p0 - 2*p1 + p2 | * | n |
+    //                                    | -2*p0 + 2*p1   |   | . |
+    //
+    //                     = | 2*t  1 | * | tan1 - tan0 | * | n |
+    //                                    | 2*tan0      |   | . |
+    //
+    //                     = 2*t * ((tan1 - tan0) dot n) + (2*tan0 dot n)
+    //
+    //   t = (tan0 dot n) / ((tan0 - tan1) dot n)
+    Sk2f dQ1n = (tan0 - tan1) * n;
+    Sk2f dQ0n = tan0 * n;
+    Sk2f t = (dQ0n + SkNx_shuffle<1,0>(dQ0n)) / (dQ1n + SkNx_shuffle<1,0>(dQ1n));
+    t = Sk2f::Min(Sk2f::Max(t, 0), 1); // Clamp for FP error.
+
+    Sk2f p01 = SkNx_fma(t, tan0, p0);
+    Sk2f p12 = SkNx_fma(t, tan1, p1);
+    Sk2f p012 = lerp(p01, p12, t);
+
+    p0.store(&dst[0]);
+    p01.store(&dst[1]);
+    p012.store(&dst[2]);
+    p12.store(&dst[3]);
+    p2.store(&dst[4]);
+
+    return true;
+}
diff --git a/src/gpu/ccpr/GrCCPRGeometry.h b/src/gpu/ccpr/GrCCPRGeometry.h
new file mode 100644
index 0000000..cb8bb3a
--- /dev/null
+++ b/src/gpu/ccpr/GrCCPRGeometry.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGrCCPRGeometry_DEFINED
+#define GrGrCCPRGeometry_DEFINED
+
+#include "SkTypes.h"
+
+struct SkPoint;
+
+/*
+ * Ensures that a quadratic bezier is monotonic with respect to the vector between its endpoints
+ * [P2 - P0]. In the event that the curve is not monotonic, it is chopped into two segments that
+ * are. This should be rare for well-behaved curves in the real world.
+ *
+ * NOTE: This must be done in device space, since an affine transformation can change whether a
+ * curve is monotonic.
+ *
+ * Returns false if the curve was already monotonic.
+ *         true if it was chopped into two monotonic segments, now contained in dst.
+ */
+bool GrCCPRChopMonotonicQuadratics(const SkPoint& startPt, const SkPoint& controlPt,
+                                   const SkPoint& endPt, SkPoint dst[5]);
+
+#endif