Unit test and bug fixes for cubic Bezier clipping.
authorturk@google.com <turk@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 3 Mar 2009 02:56:05 +0000 (02:56 +0000)
committerturk@google.com <turk@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 3 Mar 2009 02:56:05 +0000 (02:56 +0000)
git-svn-id: http://skia.googlecode.com/svn/trunk@102 2bbb7eff-a529-9590-31e7-b0007b416f81

Makefile
src/core/SkCubicClipper.cpp
tests/ClipCubicTest.cpp [new file with mode: 0644]

index 188e915..9dd85f5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -92,6 +92,7 @@ bench: $(BENCH_OBJS) out/libskia.a
 
 TESTS_SRCS := GeometryTest.cpp MathTest.cpp MatrixTest.cpp PackBitsTest.cpp \
               Sk64Test.cpp StringTest.cpp Test.cpp UtilsTest.cpp PathTest.cpp \
+             ClipCubicTest.cpp \
               main.cpp
 TESTS_SRCS := $(addprefix tests/, $(TESTS_SRCS))
 
@@ -121,4 +122,3 @@ help:
        @echo "    SKIA_SCALAR=fixed for fixed-point build"
        @echo "    SKIA_BUILD_FOR=mac for mac build (e.g. CG for image decoding)"
        @echo ""
-       
index 9022ff3..d11eb99 100644 (file)
@@ -122,39 +122,41 @@ bool SkCubicClipper::clipCubic(const SkPoint srcPts[4], SkPoint dst[4]) {
     bool reverse;
 
     // we need the data to be monotonically descending in Y
-    if (srcPts[0].fY > srcPts[2].fY) {
+    if (srcPts[0].fY > srcPts[3].fY) {
         dst[0] = srcPts[3];
         dst[1] = srcPts[2];
         dst[2] = srcPts[1];
         dst[3] = srcPts[0];
         reverse = true;
     } else {
-        memcpy(dst, srcPts, 3 * sizeof(SkPoint));
+        memcpy(dst, srcPts, 4 * sizeof(SkPoint));
         reverse = false;
     }
 
     // are we completely above or below
     const SkScalar ctop = fClip.fTop;
     const SkScalar cbot = fClip.fBottom;
-    if (dst[2].fY <= ctop || dst[0].fY >= cbot) {
+    if (dst[3].fY <= ctop || dst[0].fY >= cbot) {
         return false;
     }
     
     SkScalar t;
-    SkPoint tmp[5]; // for SkChopCubicAt
+    SkPoint tmp[7]; // for SkChopCubicAt
     
     // are we partially above
     if (dst[0].fY < ctop && chopMonoCubicAtY(dst, ctop, &t)) {
         SkChopCubicAt(dst, tmp, t);
-        dst[0] = tmp[2];
-        dst[1] = tmp[3];
+        dst[0] = tmp[3];
+        dst[1] = tmp[4];
+        dst[2] = tmp[5];
     }
     
     // are we partially below
-    if (dst[2].fY > cbot && chopMonoCubicAtY(dst, cbot, &t)) {
+    if (dst[3].fY > cbot && chopMonoCubicAtY(dst, cbot, &t)) {
         SkChopCubicAt(dst, tmp, t);
         dst[1] = tmp[1];
         dst[2] = tmp[2];
+        dst[3] = tmp[3];
     }
     
     if (reverse) {
diff --git a/tests/ClipCubicTest.cpp b/tests/ClipCubicTest.cpp
new file mode 100644 (file)
index 0000000..0cd1d85
--- /dev/null
@@ -0,0 +1,152 @@
+#include "Test.h"
+
+#include "../../src/core/SkCubicClipper.h"
+#include "SkGeometry.h"
+
+
+static void PrintCurve(const char *name, const SkPoint crv[4]) {
+    printf("%s: %.10g, %.10g, %.10g, %.10g, %.10g, %.10g, %.10g, %.10g\n",
+            name,
+            crv[0].fX, crv[0].fY,
+            crv[1].fX, crv[1].fY,
+            crv[2].fX, crv[2].fY,
+            crv[3].fX, crv[3].fY);
+                     
+}
+
+
+static bool CurvesAreEqual(
+  const SkPoint c0[4], const SkPoint c1[4], float tol) {
+      for (int i = 0; i < 4; i++) {
+          if (SkScalarAbs(c0[i].fX - c1[i].fX) > SkFloatToScalar(tol) ||
+              SkScalarAbs(c0[i].fY - c1[i].fY) > SkFloatToScalar(tol)
+          ) {
+              PrintCurve("c0", c0);
+              PrintCurve("c1", c1);
+              return false;
+          }
+      }
+      return true;
+}
+
+
+static SkPoint* SetCurve(
+    float x0, float y0,
+    float x1, float y1,
+    float x2, float y2,
+    float x3, float y3,
+    SkPoint crv[4]
+) {
+    crv[0].fX = SkFloatToScalar(x0);   crv[0].fY = SkFloatToScalar(y0);
+    crv[1].fX = SkFloatToScalar(x1);   crv[1].fY = SkFloatToScalar(y1);
+    crv[2].fX = SkFloatToScalar(x2);   crv[2].fY = SkFloatToScalar(y2);
+    crv[3].fX = SkFloatToScalar(x3);   crv[3].fY = SkFloatToScalar(y3);
+    return crv;
+}
+    
+
+static void TestCubicClipping(skiatest::Reporter* reporter) {
+  static SkPoint crv[4] = {
+    { SkFloatToScalar(0), SkFloatToScalar(0)  },
+    { SkFloatToScalar(2), SkFloatToScalar(3)  },
+    { SkFloatToScalar(1), SkFloatToScalar(10) },
+    { SkFloatToScalar(4), SkFloatToScalar(12) }
+  };
+  
+  SkCubicClipper clipper;
+  SkPoint clipped[4], shouldbe[4];
+  SkIRect clipRect;
+  bool success;
+  const float tol = 1e-4;
+
+  // Test no clip, with plenty of room.
+  clipRect.set(-2, -2, 6, 14);
+  clipper.setClip(clipRect);
+  success = clipper.clipCubic(crv, clipped);
+  REPORTER_ASSERT(reporter, success == true);
+  REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+      0, 0, 2, 3, 1, 10, 4, 12, shouldbe
+  ), tol));
+
+  // Test no clip, touching first point.
+  clipRect.set(-2, 0, 6, 14);
+  clipper.setClip(clipRect);
+  success = clipper.clipCubic(crv, clipped);
+  REPORTER_ASSERT(reporter, success == true);
+  REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+      0, 0, 2, 3, 1, 10, 4, 12, shouldbe
+  ), tol));
+
+  // Test no clip, touching last point.
+  clipRect.set(-2, -2, 6, 12);
+  clipper.setClip(clipRect);
+  success = clipper.clipCubic(crv, clipped);
+  REPORTER_ASSERT(reporter, success == true);
+  REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+      0, 0, 2, 3, 1, 10, 4, 12, shouldbe
+  ), tol));
+
+  // Test all clip.
+  clipRect.set(-2, 14, 6, 20);
+  clipper.setClip(clipRect);
+  success = clipper.clipCubic(crv, clipped);
+  REPORTER_ASSERT(reporter, success == false);
+
+  // Test clip at 1.
+  clipRect.set(-2, 1, 6, 14);
+  clipper.setClip(clipRect);
+  success = clipper.clipCubic(crv, clipped);
+  REPORTER_ASSERT(reporter, success == true);
+  REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+      0.5126125216, 1,
+      1.841195941,  4.337081432,
+      1.297019958,  10.19801331,
+      4,            12,
+      shouldbe
+  ), tol));
+
+  // Test clip at 2.
+  clipRect.set(-2, 2, 6, 14);
+  clipper.setClip(clipRect);
+  success = clipper.clipCubic(crv, clipped);
+  REPORTER_ASSERT(reporter, success == true);
+  REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+      00.8412352204, 2,
+      1.767683744,   5.400758266,
+      1.55052948,    10.36701965,
+      4,             12,
+      shouldbe
+  ), tol));
+
+  // Test clip at 11.
+  clipRect.set(-2, -2, 6, 11);
+  clipper.setClip(clipRect);
+  success = clipper.clipCubic(crv, clipped);
+  REPORTER_ASSERT(reporter, success == true);
+  REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+      0,           0,
+      1.742904663, 2.614356995,
+      1.207521796, 8.266430855,
+      3.026495695, 11,
+      shouldbe
+  ), tol));
+
+  // Test clip at 10.
+  clipRect.set(-2, -2, 6, 10);
+  clipper.setClip(clipRect);
+  success = clipper.clipCubic(crv, clipped);
+  REPORTER_ASSERT(reporter, success == true);
+  REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+      0,           0,
+      1.551193237, 2.326789856,
+      1.297736168, 7.059780121,
+      2.505550385, 10,
+      shouldbe
+  ), tol));
+}
+
+
+
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("CubicClipper", CubicClippingTestClass, TestCubicClipping)