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