fix cubic clip in y
authorcaryclark <caryclark@google.com>
Thu, 20 Aug 2015 17:35:43 +0000 (10:35 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 20 Aug 2015 17:35:43 +0000 (10:35 -0700)
Follow on to https://codereview.chromium.org/1299243002/

Clip using a geometric solution if the algebraic solution
fails in Y as well as in X.

If the root finder discovers real roots that are sufficiently
far apart, the root in the range of 0..1 can contain so much
error that it is computed to be slightly smaller than 0 or
larger than 1.

In this case, binary search the mono curve for the actual
answer.

R=reed@google.com

Review URL: https://codereview.chromium.org/1303873003

gm/cubicpaths.cpp
src/core/SkEdgeClipper.cpp

index ca6c057..f89cc99 100644 (file)
@@ -68,7 +68,7 @@ protected:
 
     void onDraw(SkCanvas* canvas) override {
         canvas->save();
-        canvas->translate(-2, 20);
+        canvas->translate(-2, 120);
         drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 150));
         canvas->translate(0, 170);
         drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 100));
@@ -77,6 +77,16 @@ protected:
         canvas->translate(0, 170);
         drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 10, 150));
         canvas->restore();
+        canvas->save();
+        canvas->translate(20, -2);
+        drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 150, 80));
+        canvas->translate(170, 0);
+        drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 100, 80));
+        canvas->translate(170, 0);
+        drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 150, 30));
+        canvas->translate(170, 0);
+        drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 150, 10));
+        canvas->restore();
     }
 
     void drawOne(SkCanvas* canvas, const SkPath& path, const SkRect& clip) {
@@ -97,9 +107,18 @@ protected:
         fPath.cubicTo( 11.608591683531916f, 87.33115f, -0.010765133872116195f, 109.16448333333332f,
                 -0.013089005235602302f, 131);
         fPath.close();
+        fFlipped = fPath;
+        SkMatrix matrix;
+        matrix.reset();
+        matrix.setScaleX(0);
+        matrix.setScaleY(0);
+        matrix.setSkewX(1);
+        matrix.setSkewY(1);
+        fFlipped.transform(matrix);
     }
 
     SkPath fPath;
+    SkPath fFlipped;
 private:
     typedef skiagm::GM INHERITED;
 };
index 02fb453..c6a4fb2 100644 (file)
@@ -225,63 +225,15 @@ bool SkEdgeClipper::clipQuad(const SkPoint srcPts[3], const SkRect& clip) {
 
 ///////////////////////////////////////////////////////////////////////////////
 
-// Modify pts[] in place so that it is clipped in Y to the clip rect
-static void chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) {
-
-    // are we partially above
-    if (pts[0].fY < clip.fTop) {
-        SkPoint tmp[7];
-        if (SkChopMonoCubicAtY(pts, clip.fTop, tmp)) {
-            // tmp[3, 4].fY should all be to the below clip.fTop.
-            // Since we can't trust the numerics of
-            // the chopper, we force those conditions now
-            tmp[3].fY = clip.fTop;
-            clamp_ge(tmp[4].fY, clip.fTop);
-
-            pts[0] = tmp[3];
-            pts[1] = tmp[4];
-            pts[2] = tmp[5];
-        } else {
-            // if chopMonoCubicAtY failed, then we may have hit inexact numerics
-            // so we just clamp against the top
-            for (int i = 0; i < 4; i++) {
-                clamp_ge(pts[i].fY, clip.fTop);
-            }
-        }
-    }
-
-    // are we partially below
-    if (pts[3].fY > clip.fBottom) {
-        SkPoint tmp[7];
-        if (SkChopMonoCubicAtY(pts, clip.fBottom, tmp)) {
-            tmp[3].fY = clip.fBottom;
-            clamp_le(tmp[2].fY, clip.fBottom);
-
-            pts[1] = tmp[1];
-            pts[2] = tmp[2];
-            pts[3] = tmp[3];
-        } else {
-            // if chopMonoCubicAtY failed, then we may have hit inexact numerics
-            // so we just clamp against the bottom
-            for (int i = 0; i < 4; i++) {
-                clamp_le(pts[i].fY, clip.fBottom);
-            }
-        }
-    }
-}
-
-static void chop_mono_cubic_at_x(SkPoint pts[4], SkScalar x, SkPoint tmp[7]) {
-    if (SkChopMonoCubicAtX(pts, x, tmp)) {
-        return;
-    }
+static SkScalar mono_cubic_closestT(const SkScalar src[], SkScalar x) {
     SkScalar t = 0.5f;
     SkScalar lastT;
     SkScalar bestT  SK_INIT_TO_AVOID_WARNING;
     SkScalar step = 0.25f;
-    SkScalar D = pts[0].fX;
-    SkScalar A = pts[3].fX + 3*(pts[1].fX - pts[2].fX) - D;
-    SkScalar B = 3*(pts[2].fX - pts[1].fX - pts[1].fX + D);
-    SkScalar C = 3*(pts[1].fX - D);
+    SkScalar D = src[0];
+    SkScalar A = src[6] + 3*(src[2] - src[4]) - D;
+    SkScalar B = 3*(src[4] - src[2] - src[2] + D);
+    SkScalar C = 3*(src[2] - D);
     x -= D;
     SkScalar closest = SK_ScalarMax;
     do {
@@ -295,7 +247,52 @@ static void chop_mono_cubic_at_x(SkPoint pts[4], SkScalar x, SkPoint tmp[7]) {
         t += loc < x ? step : -step;
         step *= 0.5f;
     } while (closest > 0.25f && lastT != t);
-    SkChopCubicAt(pts, tmp, bestT);
+    return bestT;
+}
+
+static void chop_mono_cubic_at_y(SkPoint src[4], SkScalar y, SkPoint dst[7]) {
+    if (SkChopMonoCubicAtY(src, y, dst)) {
+        return;
+    }
+    SkChopCubicAt(src, dst, mono_cubic_closestT(&src->fY, y));
+}
+
+// Modify pts[] in place so that it is clipped in Y to the clip rect
+static void chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) {
+
+    // are we partially above
+    if (pts[0].fY < clip.fTop) {
+        SkPoint tmp[7];
+        chop_mono_cubic_at_y(pts, clip.fTop, tmp);
+        // tmp[3, 4].fY should all be to the below clip.fTop.
+        // Since we can't trust the numerics of
+        // the chopper, we force those conditions now
+        tmp[3].fY = clip.fTop;
+        clamp_ge(tmp[4].fY, clip.fTop);
+
+        pts[0] = tmp[3];
+        pts[1] = tmp[4];
+        pts[2] = tmp[5];
+    }
+
+    // are we partially below
+    if (pts[3].fY > clip.fBottom) {
+        SkPoint tmp[7];
+        chop_mono_cubic_at_y(pts, clip.fBottom, tmp);
+        tmp[3].fY = clip.fBottom;
+        clamp_le(tmp[2].fY, clip.fBottom);
+
+        pts[1] = tmp[1];
+        pts[2] = tmp[2];
+        pts[3] = tmp[3];
+    }
+}
+
+static void chop_mono_cubic_at_x(SkPoint src[4], SkScalar x, SkPoint dst[7]) {
+    if (SkChopMonoCubicAtX(src, x, dst)) {
+        return;
+    }
+    SkChopCubicAt(src, dst, mono_cubic_closestT(&src->fX, x));
 }
 
 // srcPts[] must be monotonic in X and Y