Home | History | Annotate | Download | only in tests
      1 #include "PathOpsCubicIntersectionTestData.h"
      2 #include "PathOpsQuadIntersectionTestData.h"
      3 #include "SkCommonFlags.h"
      4 #include "SkPaint.h"
      5 #include "SkPath.h"
      6 #include "SkRandom.h"
      7 #include "SkStrokerPriv.h"
      8 #include "SkTime.h"
      9 #include "Test.h"
     10 
     11 DEFINE_bool(timeout, true, "run until alloted time expires");
     12 
     13 #define MS_TEST_DURATION 10
     14 
     15 const SkScalar widths[] = {-FLT_MAX, -1, -0.1f, -FLT_EPSILON, 0, FLT_EPSILON,
     16         0.0000001f, 0.000001f, 0.00001f, 0.0001f, 0.001f, 0.01f,
     17         0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 1, 1.1f, 2, 10, 10e2f, 10e3f, 10e4f, 10e5f, 10e6f, 10e7f,
     18         10e8f, 10e9f, 10e10f, 10e20f,  FLT_MAX };
     19 size_t widths_count = SK_ARRAY_COUNT(widths);
     20 
     21 static void pathTest(const SkPath& path) {
     22     SkPaint p;
     23 	SkPath fill;
     24     p.setStyle(SkPaint::kStroke_Style);
     25     for (size_t index = 0; index < widths_count; ++index) {
     26         p.setStrokeWidth(widths[index]);
     27         p.getFillPath(path, &fill);
     28     }
     29 }
     30 
     31 static void cubicTest(const SkPoint c[4]) {
     32 	SkPath path;
     33 	path.moveTo(c[0].fX, c[0].fY);
     34 	path.cubicTo(c[1].fX, c[1].fY, c[2].fX, c[2].fY, c[3].fX, c[3].fY);
     35 	pathTest(path);
     36 }
     37 
     38 static void quadTest(const SkPoint c[3]) {
     39 	SkPath path;
     40 	path.moveTo(c[0].fX, c[0].fY);
     41 	path.quadTo(c[1].fX, c[1].fY, c[2].fX, c[2].fY);
     42 	pathTest(path);
     43 }
     44 
     45 static void cubicSetTest(const SkDCubic* dCubic, size_t count) {
     46     SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
     47 	for (size_t index = 0; index < count; ++index) {
     48 		const SkDCubic& d = dCubic[index];
     49 		SkPoint c[4] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY},
     50                          {(float) d[2].fX, (float) d[2].fY}, {(float) d[3].fX, (float) d[3].fY} };
     51 	    cubicTest(c);
     52         if (FLAGS_timeout && SkTime::GetMSecs() > limit) {
     53             return;
     54         }
     55 	}
     56 }
     57 
     58 static void cubicPairSetTest(const SkDCubic dCubic[][2], size_t count) {
     59     SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
     60 	for (size_t index = 0; index < count; ++index) {
     61 		for (int pair = 0; pair < 2; ++pair) {
     62 			const SkDCubic& d = dCubic[index][pair];
     63 			SkPoint c[4] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY},
     64                              {(float) d[2].fX, (float) d[2].fY}, {(float) d[3].fX, (float) d[3].fY} };
     65 			cubicTest(c);
     66             if (FLAGS_timeout && SkTime::GetMSecs() > limit) {
     67                 return;
     68             }
     69 		}
     70 	}
     71 }
     72 
     73 static void quadSetTest(const SkDQuad* dQuad, size_t count) {
     74     SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
     75 	for (size_t index = 0; index < count; ++index) {
     76 		const SkDQuad& d = dQuad[index];
     77 		SkPoint c[3] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY},
     78                          {(float) d[2].fX, (float) d[2].fY}  };
     79 	    quadTest(c);
     80         if (FLAGS_timeout && SkTime::GetMSecs() > limit) {
     81             return;
     82         }
     83 	}
     84 }
     85 
     86 static void quadPairSetTest(const SkDQuad dQuad[][2], size_t count) {
     87     SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
     88 	for (size_t index = 0; index < count; ++index) {
     89 		for (int pair = 0; pair < 2; ++pair) {
     90 			const SkDQuad& d = dQuad[index][pair];
     91 			SkPoint c[3] = { {(float) d[0].fX, (float) d[0].fY}, {(float) d[1].fX, (float) d[1].fY},
     92                              {(float) d[2].fX, (float) d[2].fY}  };
     93 			quadTest(c);
     94             if (FLAGS_timeout && SkTime::GetMSecs() > limit) {
     95                 return;
     96             }
     97 		}
     98 	}
     99 }
    100 
    101 DEF_TEST(QuadStrokerSet, reporter) {
    102 	quadSetTest(quadraticLines, quadraticLines_count);
    103 	quadSetTest(quadraticPoints, quadraticPoints_count);
    104 	quadSetTest(quadraticModEpsilonLines, quadraticModEpsilonLines_count);
    105 	quadPairSetTest(quadraticTests, quadraticTests_count);
    106 }
    107 
    108 DEF_TEST(CubicStrokerSet, reporter) {
    109 	cubicSetTest(pointDegenerates, pointDegenerates_count);
    110 	cubicSetTest(notPointDegenerates, notPointDegenerates_count);
    111 	cubicSetTest(lines, lines_count);
    112 	cubicSetTest(notLines, notLines_count);
    113 	cubicSetTest(modEpsilonLines, modEpsilonLines_count);
    114 	cubicSetTest(lessEpsilonLines, lessEpsilonLines_count);
    115 	cubicSetTest(negEpsilonLines, negEpsilonLines_count);
    116 	cubicPairSetTest(tests, tests_count);
    117 }
    118 
    119 static SkScalar unbounded(SkRandom& r) {
    120     uint32_t val = r.nextU();
    121     return SkBits2Float(val);
    122 }
    123 
    124 static SkScalar unboundedPos(SkRandom& r) {
    125     uint32_t val = r.nextU() & 0x7fffffff;
    126     return SkBits2Float(val);
    127 }
    128 
    129 DEF_TEST(QuadStrokerUnbounded, reporter) {
    130     SkRandom r;
    131     SkPaint p;
    132     p.setStyle(SkPaint::kStroke_Style);
    133 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    134     int best = 0;
    135     sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
    136 #endif
    137     SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
    138     for (int i = 0; i < 1000000; ++i) {
    139         SkPath path, fill;
    140         path.moveTo(unbounded(r), unbounded(r));
    141         path.quadTo(unbounded(r), unbounded(r), unbounded(r), unbounded(r));
    142         p.setStrokeWidth(unboundedPos(r));
    143         p.getFillPath(path, &fill);
    144 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    145         if (best < gMaxRecursion[2]) {
    146             if (FLAGS_veryVerbose) {
    147                 SkDebugf("\n%s quad=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[2],
    148                         p.getStrokeWidth());
    149                 path.dumpHex();
    150                 SkDebugf("fill:\n");
    151                 fill.dumpHex();
    152             }
    153             best = gMaxRecursion[2];
    154         }
    155 #endif
    156         if (FLAGS_timeout && SkTime::GetMSecs() > limit) {
    157             return;
    158         }
    159     }
    160 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    161     if (FLAGS_veryVerbose) {
    162        SkDebugf("\n%s max quad=%d\n", __FUNCTION__, best);
    163     }
    164 #endif
    165 }
    166 
    167 DEF_TEST(CubicStrokerUnbounded, reporter) {
    168     SkRandom r;
    169     SkPaint p;
    170     p.setStyle(SkPaint::kStroke_Style);
    171 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    172     int bestTan = 0;
    173     int bestCubic = 0;
    174     sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
    175 #endif
    176     SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
    177     for (int i = 0; i < 1000000; ++i) {
    178         SkPath path, fill;
    179         path.moveTo(unbounded(r), unbounded(r));
    180         path.cubicTo(unbounded(r), unbounded(r), unbounded(r), unbounded(r),
    181                 unbounded(r), unbounded(r));
    182         p.setStrokeWidth(unboundedPos(r));
    183         p.getFillPath(path, &fill);
    184     #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    185         if (bestTan < gMaxRecursion[0] || bestCubic < gMaxRecursion[1]) {
    186             if (FLAGS_veryVerbose) {
    187                 SkDebugf("\n%s tan=%d cubic=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[0],
    188                         gMaxRecursion[1], p.getStrokeWidth());
    189                 path.dumpHex();
    190                 SkDebugf("fill:\n");
    191                 fill.dumpHex();
    192             }
    193             bestTan = SkTMax(bestTan, gMaxRecursion[0]);
    194             bestCubic = SkTMax(bestCubic, gMaxRecursion[1]);
    195         }
    196     #endif
    197         if (FLAGS_timeout && SkTime::GetMSecs() > limit) {
    198             return;
    199         }
    200     }
    201 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    202     if (FLAGS_veryVerbose) {
    203         SkDebugf("\n%s max tan=%d cubic=%d\n", __FUNCTION__, bestTan, bestCubic);
    204     }
    205 #endif
    206 }
    207 
    208 DEF_TEST(QuadStrokerConstrained, reporter) {
    209     SkRandom r;
    210     SkPaint p;
    211     p.setStyle(SkPaint::kStroke_Style);
    212 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    213     int best = 0;
    214     sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
    215 #endif
    216     SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
    217     for (int i = 0; i < 1000000; ++i) {
    218         SkPath path, fill;
    219         SkPoint quad[3];
    220         quad[0].fX = r.nextRangeF(0, 500);
    221         quad[0].fY = r.nextRangeF(0, 500);
    222         const SkScalar halfSquared = 0.5f * 0.5f;
    223         do {
    224             quad[1].fX = r.nextRangeF(0, 500);
    225             quad[1].fY = r.nextRangeF(0, 500);
    226         } while (quad[0].distanceToSqd(quad[1]) < halfSquared);
    227         do {
    228             quad[2].fX = r.nextRangeF(0, 500);
    229             quad[2].fY = r.nextRangeF(0, 500);
    230         } while (quad[0].distanceToSqd(quad[2]) < halfSquared
    231                 || quad[1].distanceToSqd(quad[2]) < halfSquared);
    232         path.moveTo(quad[0].fX, quad[0].fY);
    233         path.quadTo(quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY);
    234         p.setStrokeWidth(r.nextRangeF(0, 500));
    235         p.getFillPath(path, &fill);
    236 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    237         if (best < gMaxRecursion[2]) {
    238             if (FLAGS_veryVerbose) {
    239                 SkDebugf("\n%s quad=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[2],
    240                         p.getStrokeWidth());
    241                 path.dumpHex();
    242                 SkDebugf("fill:\n");
    243                 fill.dumpHex();
    244             }
    245             best = gMaxRecursion[2];
    246         }
    247 #endif
    248         if (FLAGS_timeout && SkTime::GetMSecs() > limit) {
    249             return;
    250         }
    251     }
    252 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    253     if (FLAGS_veryVerbose) {
    254         SkDebugf("\n%s max quad=%d\n", __FUNCTION__, best);
    255     }
    256 #endif
    257 }
    258 
    259 DEF_TEST(CubicStrokerConstrained, reporter) {
    260     SkRandom r;
    261     SkPaint p;
    262     p.setStyle(SkPaint::kStroke_Style);
    263 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    264     int bestTan = 0;
    265     int bestCubic = 0;
    266     sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
    267 #endif
    268     SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
    269     for (int i = 0; i < 1000000; ++i) {
    270         SkPath path, fill;
    271         SkPoint cubic[4];
    272         cubic[0].fX = r.nextRangeF(0, 500);
    273         cubic[0].fY = r.nextRangeF(0, 500);
    274         const SkScalar halfSquared = 0.5f * 0.5f;
    275         do {
    276             cubic[1].fX = r.nextRangeF(0, 500);
    277             cubic[1].fY = r.nextRangeF(0, 500);
    278         } while (cubic[0].distanceToSqd(cubic[1]) < halfSquared);
    279         do {
    280             cubic[2].fX = r.nextRangeF(0, 500);
    281             cubic[2].fY = r.nextRangeF(0, 500);
    282         } while (  cubic[0].distanceToSqd(cubic[2]) < halfSquared
    283                 || cubic[1].distanceToSqd(cubic[2]) < halfSquared);
    284         do {
    285             cubic[3].fX = r.nextRangeF(0, 500);
    286             cubic[3].fY = r.nextRangeF(0, 500);
    287         } while (  cubic[0].distanceToSqd(cubic[3]) < halfSquared
    288                 || cubic[1].distanceToSqd(cubic[3]) < halfSquared
    289                 || cubic[2].distanceToSqd(cubic[3]) < halfSquared);
    290         path.moveTo(cubic[0].fX, cubic[0].fY);
    291         path.cubicTo(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY);
    292         p.setStrokeWidth(r.nextRangeF(0, 500));
    293         p.getFillPath(path, &fill);
    294 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    295         if (bestTan < gMaxRecursion[0] || bestCubic < gMaxRecursion[1]) {
    296             if (FLAGS_veryVerbose) {
    297                 SkDebugf("\n%s tan=%d cubic=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[0],
    298                         gMaxRecursion[1], p.getStrokeWidth());
    299                 path.dumpHex();
    300                 SkDebugf("fill:\n");
    301                 fill.dumpHex();
    302             }
    303             bestTan = SkTMax(bestTan, gMaxRecursion[0]);
    304             bestCubic = SkTMax(bestCubic, gMaxRecursion[1]);
    305         }
    306 #endif
    307         if (FLAGS_timeout && SkTime::GetMSecs() > limit) {
    308             return;
    309         }
    310     }
    311 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    312     if (FLAGS_veryVerbose) {
    313         SkDebugf("\n%s max tan=%d cubic=%d\n", __FUNCTION__, bestTan, bestCubic);
    314     }
    315 #endif
    316 }
    317 
    318 DEF_TEST(QuadStrokerRange, reporter) {
    319     SkRandom r;
    320     SkPaint p;
    321     p.setStyle(SkPaint::kStroke_Style);
    322 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    323     int best = 0;
    324     sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
    325 #endif
    326     SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
    327     for (int i = 0; i < 1000000; ++i) {
    328         SkPath path, fill;
    329         SkPoint quad[3];
    330         quad[0].fX = r.nextRangeF(0, 500);
    331         quad[0].fY = r.nextRangeF(0, 500);
    332         quad[1].fX = r.nextRangeF(0, 500);
    333         quad[1].fY = r.nextRangeF(0, 500);
    334         quad[2].fX = r.nextRangeF(0, 500);
    335         quad[2].fY = r.nextRangeF(0, 500);
    336         path.moveTo(quad[0].fX, quad[0].fY);
    337         path.quadTo(quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY);
    338         p.setStrokeWidth(r.nextRangeF(0, 500));
    339         p.getFillPath(path, &fill);
    340 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    341         if (best < gMaxRecursion[2]) {
    342             if (FLAGS_veryVerbose) {
    343                 SkDebugf("\n%s quad=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[2],
    344                         p.getStrokeWidth());
    345                 path.dumpHex();
    346                 SkDebugf("fill:\n");
    347                 fill.dumpHex();
    348             }
    349             best = gMaxRecursion[2];
    350         }
    351 #endif
    352         if (FLAGS_timeout && SkTime::GetMSecs() > limit) {
    353             return;
    354         }
    355     }
    356 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    357     if (FLAGS_verbose) {
    358         SkDebugf("\n%s max quad=%d\n", __FUNCTION__, best);
    359     }
    360 #endif
    361 }
    362 
    363 DEF_TEST(CubicStrokerRange, reporter) {
    364     SkRandom r;
    365     SkPaint p;
    366     p.setStyle(SkPaint::kStroke_Style);
    367 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    368     int best[2] = { 0 };
    369     sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
    370 #endif
    371     SkMSec limit = SkTime::GetMSecs() + MS_TEST_DURATION;
    372     for (int i = 0; i < 1000000; ++i) {
    373         SkPath path, fill;
    374         path.moveTo(r.nextRangeF(0, 500), r.nextRangeF(0, 500));
    375         path.cubicTo(r.nextRangeF(0, 500), r.nextRangeF(0, 500), r.nextRangeF(0, 500),
    376                 r.nextRangeF(0, 500), r.nextRangeF(0, 500), r.nextRangeF(0, 500));
    377         p.setStrokeWidth(r.nextRangeF(0, 100));
    378         p.getFillPath(path, &fill);
    379 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    380         if (best[0] < gMaxRecursion[0] || best[1] < gMaxRecursion[1]) {
    381             if (FLAGS_veryVerbose) {
    382                 SkDebugf("\n%s tan=%d cubic=%d width=%1.9g\n", __FUNCTION__, gMaxRecursion[0],
    383                         gMaxRecursion[1], p.getStrokeWidth());
    384                 path.dumpHex();
    385                 SkDebugf("fill:\n");
    386                 fill.dumpHex();
    387             }
    388             best[0] = SkTMax(best[0], gMaxRecursion[0]);
    389             best[1] = SkTMax(best[1], gMaxRecursion[1]);
    390         }
    391 #endif
    392         if (FLAGS_timeout && SkTime::GetMSecs() > limit) {
    393             return;
    394         }
    395     }
    396 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    397     if (FLAGS_veryVerbose) {
    398         SkDebugf("\n%s max tan=%d cubic=%d\n", __FUNCTION__, best[0], best[1]);
    399     }
    400 #endif
    401 }
    402 
    403 
    404 DEF_TEST(QuadStrokerOneOff, reporter) {
    405 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    406     sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
    407 #endif
    408     SkPaint p;
    409     p.setStyle(SkPaint::kStroke_Style);
    410     p.setStrokeWidth(SkDoubleToScalar(164.683548));
    411 
    412     SkPath path, fill;
    413 path.moveTo(SkBits2Float(0x43c99223), SkBits2Float(0x42b7417e));
    414 path.quadTo(SkBits2Float(0x4285d839), SkBits2Float(0x43ed6645), SkBits2Float(0x43c941c8), SkBits2Float(0x42b3ace3));
    415     p.getFillPath(path, &fill);
    416     if (FLAGS_veryVerbose) {
    417         SkDebugf("\n%s path\n", __FUNCTION__);
    418         path.dump();
    419         SkDebugf("fill:\n");
    420         fill.dump();
    421     }
    422 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    423     if (FLAGS_veryVerbose) {
    424         SkDebugf("max quad=%d\n", gMaxRecursion[2]);
    425     }
    426 #endif
    427 }
    428 
    429 DEF_TEST(CubicStrokerOneOff, reporter) {
    430 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    431     sk_bzero(gMaxRecursion, sizeof(gMaxRecursion[0]) * 3);
    432 #endif
    433     SkPaint p;
    434     p.setStyle(SkPaint::kStroke_Style);
    435     p.setStrokeWidth(SkDoubleToScalar(42.835968));
    436 
    437     SkPath path, fill;
    438 path.moveTo(SkBits2Float(0x433f5370), SkBits2Float(0x43d1f4b3));
    439 path.cubicTo(SkBits2Float(0x4331cb76), SkBits2Float(0x43ea3340), SkBits2Float(0x4388f498), SkBits2Float(0x42f7f08d), SkBits2Float(0x43f1cd32), SkBits2Float(0x42802ec1));
    440     p.getFillPath(path, &fill);
    441     if (FLAGS_veryVerbose) {
    442         SkDebugf("\n%s path\n", __FUNCTION__);
    443         path.dump();
    444         SkDebugf("fill:\n");
    445         fill.dump();
    446     }
    447 #if defined(SK_DEBUG) && QUAD_STROKE_APPROXIMATION
    448     if (FLAGS_veryVerbose) {
    449         SkDebugf("max tan=%d cubic=%d\n", gMaxRecursion[0], gMaxRecursion[1]);
    450     }
    451 #endif
    452 }
    453