aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/StrokerTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/StrokerTest.cpp')
-rwxr-xr-xtests/StrokerTest.cpp452
1 files changed, 452 insertions, 0 deletions
diff --git a/tests/StrokerTest.cpp b/tests/StrokerTest.cpp
new file mode 100755
index 0000000000..804e0a1d5f
--- /dev/null
+++ b/tests/StrokerTest.cpp
@@ -0,0 +1,452 @@
+#include "PathOpsCubicIntersectionTestData.h"
+#include "PathOpsQuadIntersectionTestData.h"
+#include "SkCommonFlags.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkStrokerPriv.h"
+#include "SkTime.h"
+#include "Test.h"
+
+DEFINE_bool2(extendedTest, x, false, "run extended tests regardless of how long takes");
+
+#define MS_TEST_DURATION 10
+
+const SkScalar widths[] = {-FLT_MAX, -1, -0.1f, -FLT_EPSILON, 0, FLT_EPSILON,
+ 0.0000001f, 0.000001f, 0.00001f, 0.0001f, 0.001f, 0.01f,
+ 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 1, 1.1f, 2, 10, 10e2f, 10e3f, 10e4f, 10e5f, 10e6f, 10e7f,
+ 10e8f, 10e9f, 10e10f, 10e20f, FLT_MAX };
+size_t widths_count = SK_ARRAY_COUNT(widths);
+
+static void pathTest(const SkPath& path) {
+ SkPaint p;
+ SkPath fill;
+ p.setStyle(SkPaint::kStroke_Style);
+ for (size_t index = 0; index < widths_count; ++index) {
+ p.setStrokeWidth(widths[index]);
+ p.getFillPath(path, &fill);
+ }
+}
+
+static void cubicTest(const SkPoint c[4]) {
+ SkPath path;
+ path.moveTo(c[0].fX, c[0].fY);
+ path.cubicTo(c[1].fX, c[1].fY, c[2].fX, c[2].fY, c[3].fX, c[3].fY);
+ pathTest(path);
+}
+
+static void quadTest(const SkPoint c[3]) {
+ SkPath path;
+ path.moveTo(c[0].fX, c[0].fY);
+ path.quadTo(c[1].fX, c[1].fY, c[2].fX, c[2].fY);
+ pathTest(path);
+}
+
+static void cubicSetTest(const SkDCubic* dCubic, size_t count) {
+ SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
+ for (size_t index = 0; index < count; ++index) {
+ const SkDCubic& d = dCubic[index];
+ SkPoint c[4] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY},
+ {(float) d[2].fX, (float) d[2].fY}, {(float) d[3].fX, (float) d[3].fY} };
+ cubicTest(c);
+ if (!FLAGS_extendedTest && SkTime::GetMSecs() > limit) {
+ return;
+ }
+ }
+}
+
+static void cubicPairSetTest(const SkDCubic dCubic[][2], size_t count) {
+ SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
+ for (size_t index = 0; index < count; ++index) {
+ for (int pair = 0; pair < 2; ++pair) {
+ const SkDCubic& d = dCubic[index][pair];
+ SkPoint c[4] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY},
+ {(float) d[2].fX, (float) d[2].fY}, {(float) d[3].fX, (float) d[3].fY} };
+ cubicTest(c);
+ if (!FLAGS_extendedTest && SkTime::GetMSecs() > limit) {
+ return;
+ }
+ }
+ }
+}
+
+static void quadSetTest(const SkDQuad* dQuad, size_t count) {
+ SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
+ for (size_t index = 0; index < count; ++index) {
+ const SkDQuad& d = dQuad[index];
+ SkPoint c[3] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY},
+ {(float) d[2].fX, (float) d[2].fY} };
+ quadTest(c);
+ if (!FLAGS_extendedTest && SkTime::GetMSecs() > limit) {
+ return;
+ }
+ }
+}
+
+static void quadPairSetTest(const SkDQuad dQuad[][2], size_t count) {
+ SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
+ for (size_t index = 0; index < count; ++index) {
+ for (int pair = 0; pair < 2; ++pair) {
+ const SkDQuad& d = dQuad[index][pair];
+ SkPoint c[3] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY},
+ {(float) d[2].fX, (float) d[2].fY} };
+ quadTest(c);
+ if (!FLAGS_extendedTest && SkTime::GetMSecs() > limit) {
+ return;
+ }
+ }
+ }
+}
+
+DEF_TEST(QuadStrokerSet, reporter) {
+ quadSetTest(quadraticLines, quadraticLines_count);
+ quadSetTest(quadraticPoints, quadraticPoints_count);
+ quadSetTest(quadraticModEpsilonLines, quadraticModEpsilonLines_count);
+ quadPairSetTest(quadraticTests, quadraticTests_count);
+}
+
+DEF_TEST(CubicStrokerSet, reporter) {
+ cubicSetTest(pointDegenerates, pointDegenerates_count);
+ cubicSetTest(notPointDegenerates, notPointDegenerates_count);
+ cubicSetTest(lines, lines_count);
+ cubicSetTest(notLines, notLines_count);
+ cubicSetTest(modEpsilonLines, modEpsilonLines_count);
+ cubicSetTest(lessEpsilonLines, lessEpsilonLines_count);
+ cubicSetTest(negEpsilonLines, negEpsilonLines_count);
+ cubicPairSetTest(tests, tests_count);
+}
+
+static SkScalar unbounded(SkLCGRandom& r) {
+ uint32_t val = r.nextU();
+ return SkBits2Float(val);
+}
+
+static SkScalar unboundedPos(SkLCGRandom& r) {
+ uint32_t val = r.nextU() & 0x7fffffff;
+ return SkBits2Float(val);
+}
+
+DEF_TEST(QuadStrokerUnbounded, reporter) {
+ SkLCGRandom r;
+ SkPaint p;
+ p.setStyle(SkPaint::kStroke_Style);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ int best = 0;
+ sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+ SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
+ for (int i = 0; i < 1000000; ++i) {
+ SkPath path, fill;
+ path.moveTo(unbounded(r), unbounded(r));
+ path.quadTo(unbounded(r), unbounded(r), unbounded(r), unbounded(r));
+ p.setStrokeWidth(unboundedPos(r));
+ p.getFillPath(path, &fill);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (best < gMaxRecursion[2]) {
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s quad=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[2],
+ p.getStrokeWidth());
+ path.dumpHex();
+ SkDebugf("fill:\n");
+ fill.dumpHex();
+ }
+ best = gMaxRecursion[2];
+ }
+#endif
+ if (!FLAGS_extendedTest && SkTime::GetMSecs() > limit) {
+ return;
+ }
+ }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s max quad=%d\n", __FUNCTION__, best);
+ }
+#endif
+}
+
+DEF_TEST(CubicStrokerUnbounded, reporter) {
+ SkLCGRandom r;
+ SkPaint p;
+ p.setStyle(SkPaint::kStroke_Style);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ int bestTan = 0;
+ int bestCubic = 0;
+ sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+ SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
+ for (int i = 0; i < 1000000; ++i) {
+ SkPath path, fill;
+ path.moveTo(unbounded(r), unbounded(r));
+ path.cubicTo(unbounded(r), unbounded(r), unbounded(r), unbounded(r),
+ unbounded(r), unbounded(r));
+ p.setStrokeWidth(unboundedPos(r));
+ p.getFillPath(path, &fill);
+ #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (bestTan < gMaxRecursion[0] || bestCubic < gMaxRecursion[1]) {
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s tan=%d cubic=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[0],
+ gMaxRecursion[1], p.getStrokeWidth());
+ path.dumpHex();
+ SkDebugf("fill:\n");
+ fill.dumpHex();
+ }
+ bestTan = SkTMax(bestTan, gMaxRecursion[0]);
+ bestCubic = SkTMax(bestCubic, gMaxRecursion[1]);
+ }
+ #endif
+ if (!FLAGS_extendedTest && SkTime::GetMSecs() > limit) {
+ return;
+ }
+ }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s max tan=%d cubic=%d\n", __FUNCTION__, bestTan, bestCubic);
+ }
+#endif
+}
+
+DEF_TEST(QuadStrokerConstrained, reporter) {
+ SkLCGRandom r;
+ SkPaint p;
+ p.setStyle(SkPaint::kStroke_Style);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ int best = 0;
+ sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+ SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
+ for (int i = 0; i < 1000000; ++i) {
+ SkPath path, fill;
+ SkPoint quad[3];
+ quad[0].fX = r.nextRangeF(0, 500);
+ quad[0].fY = r.nextRangeF(0, 500);
+ const SkScalar halfSquared = 0.5f * 0.5f;
+ do {
+ quad[1].fX = r.nextRangeF(0, 500);
+ quad[1].fY = r.nextRangeF(0, 500);
+ } while (quad[0].distanceToSqd(quad[1]) < halfSquared);
+ do {
+ quad[2].fX = r.nextRangeF(0, 500);
+ quad[2].fY = r.nextRangeF(0, 500);
+ } while (quad[0].distanceToSqd(quad[2]) < halfSquared
+ || quad[1].distanceToSqd(quad[2]) < halfSquared);
+ path.moveTo(quad[0].fX, quad[0].fY);
+ path.quadTo(quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY);
+ p.setStrokeWidth(r.nextRangeF(0, 500));
+ p.getFillPath(path, &fill);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (best < gMaxRecursion[2]) {
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s quad=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[2],
+ p.getStrokeWidth());
+ path.dumpHex();
+ SkDebugf("fill:\n");
+ fill.dumpHex();
+ }
+ best = gMaxRecursion[2];
+ }
+#endif
+ if (!FLAGS_extendedTest && SkTime::GetMSecs() > limit) {
+ return;
+ }
+ }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s max quad=%d\n", __FUNCTION__, best);
+ }
+#endif
+}
+
+DEF_TEST(CubicStrokerConstrained, reporter) {
+ SkLCGRandom r;
+ SkPaint p;
+ p.setStyle(SkPaint::kStroke_Style);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ int bestTan = 0;
+ int bestCubic = 0;
+ sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+ SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
+ for (int i = 0; i < 1000000; ++i) {
+ SkPath path, fill;
+ SkPoint cubic[4];
+ cubic[0].fX = r.nextRangeF(0, 500);
+ cubic[0].fY = r.nextRangeF(0, 500);
+ const SkScalar halfSquared = 0.5f * 0.5f;
+ do {
+ cubic[1].fX = r.nextRangeF(0, 500);
+ cubic[1].fY = r.nextRangeF(0, 500);
+ } while (cubic[0].distanceToSqd(cubic[1]) < halfSquared);
+ do {
+ cubic[2].fX = r.nextRangeF(0, 500);
+ cubic[2].fY = r.nextRangeF(0, 500);
+ } while ( cubic[0].distanceToSqd(cubic[2]) < halfSquared
+ || cubic[1].distanceToSqd(cubic[2]) < halfSquared);
+ do {
+ cubic[3].fX = r.nextRangeF(0, 500);
+ cubic[3].fY = r.nextRangeF(0, 500);
+ } while ( cubic[0].distanceToSqd(cubic[3]) < halfSquared
+ || cubic[1].distanceToSqd(cubic[3]) < halfSquared
+ || cubic[2].distanceToSqd(cubic[3]) < halfSquared);
+ path.moveTo(cubic[0].fX, cubic[0].fY);
+ path.cubicTo(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY);
+ p.setStrokeWidth(r.nextRangeF(0, 500));
+ p.getFillPath(path, &fill);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (bestTan < gMaxRecursion[0] || bestCubic < gMaxRecursion[1]) {
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s tan=%d cubic=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[0],
+ gMaxRecursion[1], p.getStrokeWidth());
+ path.dumpHex();
+ SkDebugf("fill:\n");
+ fill.dumpHex();
+ }
+ bestTan = SkTMax(bestTan, gMaxRecursion[0]);
+ bestCubic = SkTMax(bestCubic, gMaxRecursion[1]);
+ }
+#endif
+ if (!FLAGS_extendedTest && SkTime::GetMSecs() > limit) {
+ return;
+ }
+ }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s max tan=%d cubic=%d\n", __FUNCTION__, bestTan, bestCubic);
+ }
+#endif
+}
+
+DEF_TEST(QuadStrokerRange, reporter) {
+ SkLCGRandom r;
+ SkPaint p;
+ p.setStyle(SkPaint::kStroke_Style);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ int best = 0;
+ sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+ SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
+ for (int i = 0; i < 1000000; ++i) {
+ SkPath path, fill;
+ SkPoint quad[3];
+ quad[0].fX = r.nextRangeF(0, 500);
+ quad[0].fY = r.nextRangeF(0, 500);
+ quad[1].fX = r.nextRangeF(0, 500);
+ quad[1].fY = r.nextRangeF(0, 500);
+ quad[2].fX = r.nextRangeF(0, 500);
+ quad[2].fY = r.nextRangeF(0, 500);
+ path.moveTo(quad[0].fX, quad[0].fY);
+ path.quadTo(quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY);
+ p.setStrokeWidth(r.nextRangeF(0, 500));
+ p.getFillPath(path, &fill);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (best < gMaxRecursion[2]) {
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s quad=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[2],
+ p.getStrokeWidth());
+ path.dumpHex();
+ SkDebugf("fill:\n");
+ fill.dumpHex();
+ }
+ best = gMaxRecursion[2];
+ }
+#endif
+ if (!FLAGS_extendedTest && SkTime::GetMSecs() > limit) {
+ return;
+ }
+ }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s max quad=%d\n", __FUNCTION__, best);
+ }
+#endif
+}
+
+DEF_TEST(CubicStrokerRange, reporter) {
+ SkLCGRandom r;
+ SkPaint p;
+ p.setStyle(SkPaint::kStroke_Style);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ int best[2] = { 0 };
+ sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+ SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
+ for (int i = 0; i < 1000000; ++i) {
+ SkPath path, fill;
+ path.moveTo(r.nextRangeF(0, 500), r.nextRangeF(0, 500));
+ path.cubicTo(r.nextRangeF(0, 500), r.nextRangeF(0, 500), r.nextRangeF(0, 500),
+ r.nextRangeF(0, 500), r.nextRangeF(0, 500), r.nextRangeF(0, 500));
+ p.setStrokeWidth(r.nextRangeF(0, 100));
+ p.getFillPath(path, &fill);
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (best[0] < gMaxRecursion[0] || best[1] < gMaxRecursion[1]) {
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s tan=%d cubic=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[0],
+ gMaxRecursion[1], p.getStrokeWidth());
+ path.dumpHex();
+ SkDebugf("fill:\n");
+ fill.dumpHex();
+ }
+ best[0] = SkTMax(best[0], gMaxRecursion[0]);
+ best[1] = SkTMax(best[1], gMaxRecursion[1]);
+ }
+#endif
+ if (!FLAGS_extendedTest && SkTime::GetMSecs() > limit) {
+ return;
+ }
+ }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s max tan=%d cubic=%d\n", __FUNCTION__, best[0], best[1]);
+ }
+#endif
+}
+
+
+DEF_TEST(QuadStrokerOneOff, reporter) {
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+ SkPaint p;
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SkDoubleToScalar(164.683548));
+
+ SkPath path, fill;
+path.moveTo(SkBits2Float(0x43c99223), SkBits2Float(0x42b7417e));
+path.quadTo(SkBits2Float(0x4285d839), SkBits2Float(0x43ed6645), SkBits2Float(0x43c941c8), SkBits2Float(0x42b3ace3));
+ p.getFillPath(path, &fill);
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s path\n", __FUNCTION__);
+ path.dump();
+ SkDebugf("fill:\n");
+ fill.dump();
+ }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (FLAGS_verbose) {
+ SkDebugf("max quad=%d\n", gMaxRecursion[2]);
+ }
+#endif
+}
+
+DEF_TEST(CubicStrokerOneOff, reporter) {
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
+#endif
+ SkPaint p;
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(SkDoubleToScalar(42.835968));
+
+ SkPath path, fill;
+path.moveTo(SkBits2Float(0x433f5370), SkBits2Float(0x43d1f4b3));
+path.cubicTo(SkBits2Float(0x4331cb76), SkBits2Float(0x43ea3340), SkBits2Float(0x4388f498), SkBits2Float(0x42f7f08d), SkBits2Float(0x43f1cd32), SkBits2Float(0x42802ec1));
+ p.getFillPath(path, &fill);
+ if (FLAGS_verbose) {
+ SkDebugf("\n%s path\n", __FUNCTION__);
+ path.dump();
+ SkDebugf("fill:\n");
+ fill.dump();
+ }
+#if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
+ if (FLAGS_verbose) {
+ SkDebugf("max tan=%d cubic=%d\n", gMaxRecursion[0], gMaxRecursion[1]);
+ }
+#endif
+}