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