Add test for quadratic Wang's formula

Just verify that the max error on any of the intervals after splitting
is less than the specified tolerance. Looks like we're running into
some FP precision issues with large coord values, so I'm limiting the
max multiplier to 2^15 in this test.

Bug: skia:10419
Change-Id: I39e76dca5f77389833ba3c94930e6b67c2b1bc97
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/340116
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Tyler Denniston <tdenniston@google.com>
diff --git a/tests/WangsFormulaTest.cpp b/tests/WangsFormulaTest.cpp
index b701540..a1c6461 100644
--- a/tests/WangsFormulaTest.cpp
+++ b/tests/WangsFormulaTest.cpp
@@ -55,10 +55,11 @@
 }
 
 static void for_random_beziers(int numPoints, SkRandom* rand,
-                               std::function<void(const SkPoint[])> f) {
+                               std::function<void(const SkPoint[])> f,
+                               int maxExponent = 30) {
     SkASSERT(numPoints <= 4);
     SkPoint pts[4];
-    for (int i = -10; i <= 30; ++i) {
+    for (int i = -10; i <= maxExponent; ++i) {
         for (int j = 0; j < numPoints; ++j) {
             pts[j].set(std::ldexp(1 + rand->nextF(), i), std::ldexp(1 + rand->nextF(), i));
         }
@@ -295,3 +296,54 @@
         });
     }
 }
+
+// Ensure Wang's formula for quads produces max error within tolerance.
+DEF_TEST(WangsFormula_quad_within_tol, r) {
+    // Wang's formula and the quad math starts to lose precision with very large
+    // coordinate values, so limit the magnitude a bit to prevent test failures
+    // due to loss of precision.
+    constexpr int maxExponent = 15;
+    SkRandom rand;
+    for_random_beziers(3, &rand, [&r](const SkPoint pts[]) {
+        const int nsegs = static_cast<int>(
+                std::ceil(wangs_formula_quadratic_reference_impl(kIntolerance, pts)));
+
+        const float tdelta = 1.f / nsegs;
+        for (int j = 0; j < nsegs; ++j) {
+            const float tmin = j * tdelta, tmax = (j + 1) * tdelta;
+
+            // Get section of quad in [tmin,tmax]
+            const SkPoint* sectionPts;
+            SkPoint tmp0[5];
+            SkPoint tmp1[5];
+            if (tmin == 0) {
+                if (tmax == 1) {
+                    sectionPts = pts;
+                } else {
+                    SkChopQuadAt(pts, tmp0, tmax);
+                    sectionPts = tmp0;
+                }
+            } else {
+                SkChopQuadAt(pts, tmp0, tmin);
+                if (tmax == 1) {
+                    sectionPts = tmp0 + 2;
+                } else {
+                    SkChopQuadAt(tmp0 + 2, tmp1, (tmax - tmin) / (1 - tmin));
+                    sectionPts = tmp1;
+                }
+            }
+
+            // For quads, max distance from baseline is always at t=0.5.
+            SkPoint p;
+            p = SkEvalQuadAt(sectionPts, 0.5f);
+
+            // Get distance of p to baseline
+            const SkPoint n = {sectionPts[2].fY - sectionPts[0].fY,
+                               sectionPts[0].fX - sectionPts[2].fX};
+            const float d = std::abs((p - sectionPts[0]).dot(n)) / n.length();
+
+            // Check distance is within specified tolerance
+            REPORTER_ASSERT(r, d <= (1.f / kIntolerance) + SK_ScalarNearlyZero);
+        }
+    }, maxExponent);
+}