Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2015 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 "SkMatrix.h"
      9 #include "SkPath.h"
     10 #include "SkPathRef.h"
     11 #include "SkPathOps.h"
     12 #include "SkRRect.h"
     13 #include "Test.h"
     14 
     15 static SkRRect path_contains_rrect(skiatest::Reporter* reporter, const SkPath& path) {
     16     SkRRect out;
     17     REPORTER_ASSERT(reporter, path.isRRect(&out));
     18     SkPath path2, xorBoth;
     19     path2.addRRect(out);
     20     if (path == path2) {
     21         return out;
     22     }
     23     Op(path, path2, SkPathOp::kXOR_SkPathOp, &xorBoth);
     24     REPORTER_ASSERT(reporter, xorBoth.isEmpty());
     25     return out;
     26 }
     27 
     28 static SkRRect inner_path_contains_rrect(skiatest::Reporter* reporter, const SkRRect& in) {
     29     switch (in.getType()) {
     30         case SkRRect::kEmpty_Type:
     31         case SkRRect::kRect_Type:
     32         case SkRRect::kOval_Type:
     33             return in;
     34         default:
     35             break;
     36     }
     37     SkPath path;
     38     path.addRRect(in);
     39     return path_contains_rrect(reporter, path);
     40 }
     41 
     42 static void path_contains_rrect_check(skiatest::Reporter* reporter, const SkRRect& in) {
     43     SkRRect out = inner_path_contains_rrect(reporter, in);
     44     if (in != out) {
     45         SkDebugf("");
     46     }
     47     REPORTER_ASSERT(reporter, in == out);
     48 }
     49 
     50 static void path_contains_rrect_nocheck(skiatest::Reporter* reporter, const SkRRect& in) {
     51     SkRRect out = inner_path_contains_rrect(reporter, in);
     52     if (in == out) {
     53         SkDebugf("");
     54     }
     55 }
     56 
     57 static void path_contains_rrect_check(skiatest::Reporter* reporter, const SkRect& r,
     58         SkVector v[4]) {
     59     SkRRect rrect;
     60     rrect.setRectRadii(r, v);
     61     path_contains_rrect_check(reporter, rrect);
     62 }
     63 
     64 class ForceIsRRect_Private {
     65 public:
     66     ForceIsRRect_Private(SkPath* path) {
     67         path->fPathRef->setIsRRect(true);
     68     }
     69 };
     70 
     71 static void force_path_contains_rrect(skiatest::Reporter* reporter, SkPath& path) {
     72     ForceIsRRect_Private force_rrect(&path);
     73     path_contains_rrect(reporter, path);
     74 }
     75 
     76 static void test_undetected_paths(skiatest::Reporter* reporter) {
     77     SkPath path;
     78     path.moveTo(0, 62.5f);
     79     path.lineTo(0, 3.5f);
     80     path.conicTo(0, 0, 3.5f, 0, 0.70710677f);
     81     path.lineTo(196.5f, 0);
     82     path.conicTo(200, 0, 200, 3.5f, 0.70710677f);
     83     path.lineTo(200, 62.5f);
     84     path.conicTo(200, 66, 196.5f, 66, 0.70710677f);
     85     path.lineTo(3.5f, 66);
     86     path.conicTo(0, 66, 0, 62.5, 0.70710677f);
     87     path.close();
     88     force_path_contains_rrect(reporter, path);
     89 
     90     path.reset();
     91     path.moveTo(0, 81.5f);
     92     path.lineTo(0, 3.5f);
     93     path.conicTo(0, 0, 3.5f, 0, 0.70710677f);
     94     path.lineTo(149.5, 0);
     95     path.conicTo(153, 0, 153, 3.5f, 0.70710677f);
     96     path.lineTo(153, 81.5f);
     97     path.conicTo(153, 85, 149.5f, 85, 0.70710677f);
     98     path.lineTo(3.5f, 85);
     99     path.conicTo(0, 85, 0, 81.5f, 0.70710677f);
    100     path.close();
    101     force_path_contains_rrect(reporter, path);
    102 
    103     path.reset();
    104     path.moveTo(14, 1189);
    105     path.lineTo(14, 21);
    106     path.conicTo(14, 14, 21, 14, 0.70710677f);
    107     path.lineTo(1363, 14);
    108     path.conicTo(1370, 14, 1370, 21, 0.70710677f);
    109     path.lineTo(1370, 1189);
    110     path.conicTo(1370, 1196, 1363, 1196, 0.70710677f);
    111     path.lineTo(21, 1196);
    112     path.conicTo(14, 1196, 14, 1189, 0.70710677f);
    113     path.close();
    114     force_path_contains_rrect(reporter, path);
    115 
    116     path.reset();
    117     path.moveTo(14, 1743);
    118     path.lineTo(14, 21);
    119     path.conicTo(14, 14, 21, 14, 0.70710677f);
    120     path.lineTo(1363, 14);
    121     path.conicTo(1370, 14, 1370, 21, 0.70710677f);
    122     path.lineTo(1370, 1743);
    123     path.conicTo(1370, 1750, 1363, 1750, 0.70710677f);
    124     path.lineTo(21, 1750);
    125     path.conicTo(14, 1750, 14, 1743, 0.70710677f);
    126     path.close();
    127     force_path_contains_rrect(reporter, path);
    128 }
    129 
    130 static const SkScalar kWidth = 100.0f;
    131 static const SkScalar kHeight = 100.0f;
    132 
    133 static void test_tricky_radii(skiatest::Reporter* reporter) {
    134     {
    135         // crbug.com/458522
    136         SkRRect rr;
    137         const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
    138         const SkScalar rad = 12814;
    139         const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } };
    140         rr.setRectRadii(bounds, vec);
    141         path_contains_rrect_check(reporter, rr);
    142     }
    143 
    144     {
    145         // crbug.com//463920
    146         SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0);
    147         SkVector radii[4] = {
    148             { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f }
    149         };
    150         SkRRect rr;
    151         rr.setRectRadii(r, radii);
    152         path_contains_rrect_nocheck(reporter, rr);
    153     }
    154 }
    155 
    156 static void test_empty_crbug_458524(skiatest::Reporter* reporter) {
    157     SkRRect rr;
    158     const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
    159     const SkScalar rad = 40;
    160     rr.setRectXY(bounds, rad, rad);
    161     path_contains_rrect_check(reporter, rr);
    162 
    163     SkRRect other;
    164     SkMatrix matrix;
    165     matrix.setScale(0, 1);
    166     rr.transform(matrix, &other);
    167     path_contains_rrect_check(reporter, rr);
    168 }
    169 
    170 static void test_inset(skiatest::Reporter* reporter) {
    171     SkRRect rr, rr2;
    172     SkRect r = { 0, 0, 100, 100 };
    173 
    174     rr.setRect(r);
    175     rr.inset(-20, -20, &rr2);
    176     path_contains_rrect_check(reporter, rr);
    177 
    178     rr.inset(20, 20, &rr2);
    179     path_contains_rrect_check(reporter, rr);
    180 
    181     rr.inset(r.width()/2, r.height()/2, &rr2);
    182     path_contains_rrect_check(reporter, rr);
    183 
    184     rr.setRectXY(r, 20, 20);
    185     rr.inset(19, 19, &rr2);
    186     path_contains_rrect_check(reporter, rr);
    187     rr.inset(20, 20, &rr2);
    188     path_contains_rrect_check(reporter, rr);
    189 }
    190 
    191 
    192 static void test_9patch_rrect(skiatest::Reporter* reporter,
    193                               const SkRect& rect,
    194                               SkScalar l, SkScalar t, SkScalar r, SkScalar b,
    195                               bool checkRadii) {
    196     SkRRect rr;
    197     rr.setNinePatch(rect, l, t, r, b);
    198     if (checkRadii) {
    199         path_contains_rrect_check(reporter, rr);
    200     } else {
    201         path_contains_rrect_nocheck(reporter, rr);
    202     }
    203 
    204     SkRRect rr2; // construct the same RR using the most general set function
    205     SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } };
    206     rr2.setRectRadii(rect, radii);
    207     if (checkRadii) {
    208         path_contains_rrect_check(reporter, rr);
    209     } else {
    210         path_contains_rrect_nocheck(reporter, rr);
    211     }
    212 }
    213 
    214 // Test out the basic API entry points
    215 static void test_round_rect_basic(skiatest::Reporter* reporter) {
    216 
    217     //----
    218     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
    219 
    220     SkRRect rr1;
    221     rr1.setRect(rect);
    222     path_contains_rrect_check(reporter, rr1);
    223 
    224     SkRRect rr1_2; // construct the same RR using the most general set function
    225     SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
    226     rr1_2.setRectRadii(rect, rr1_2_radii);
    227     path_contains_rrect_check(reporter, rr1_2);
    228     SkRRect rr1_3;  // construct the same RR using the nine patch set function
    229     rr1_3.setNinePatch(rect, 0, 0, 0, 0);
    230     path_contains_rrect_check(reporter, rr1_2);
    231 
    232     //----
    233     SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) };
    234     SkRRect rr2;
    235     rr2.setOval(rect);
    236     path_contains_rrect_check(reporter, rr2);
    237 
    238     SkRRect rr2_2;  // construct the same RR using the most general set function
    239     SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY },
    240                                 { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY } };
    241     rr2_2.setRectRadii(rect, rr2_2_radii);
    242     path_contains_rrect_check(reporter, rr2_2);
    243     SkRRect rr2_3;  // construct the same RR using the nine patch set function
    244     rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY);
    245     path_contains_rrect_check(reporter, rr2_3);
    246 
    247     //----
    248     SkPoint p = { 5, 5 };
    249     SkRRect rr3;
    250     rr3.setRectXY(rect, p.fX, p.fY);
    251     path_contains_rrect_check(reporter, rr3);
    252 
    253     SkRRect rr3_2; // construct the same RR using the most general set function
    254     SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } };
    255     rr3_2.setRectRadii(rect, rr3_2_radii);
    256     path_contains_rrect_check(reporter, rr3_2);
    257     SkRRect rr3_3;  // construct the same RR using the nine patch set function
    258     rr3_3.setNinePatch(rect, 5, 5, 5, 5);
    259     path_contains_rrect_check(reporter, rr3_3);
    260 
    261     //----
    262     test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true);
    263 
    264     {
    265         // Test out the rrect from skia:3466
    266         SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f);
    267 
    268         test_9patch_rrect(reporter,
    269                           rect2,
    270                           0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f,
    271                           false);
    272     }
    273 
    274     //----
    275     SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } };
    276 
    277     SkRRect rr5;
    278     rr5.setRectRadii(rect, radii2);
    279     path_contains_rrect_check(reporter, rr5);
    280 }
    281 
    282 // Test out the cases when the RR degenerates to a rect
    283 static void test_round_rect_rects(skiatest::Reporter* reporter) {
    284 
    285     //----
    286     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
    287     SkRRect rr1;
    288     rr1.setRectXY(rect, 0, 0);
    289 
    290     path_contains_rrect_check(reporter, rr1);
    291 
    292     //----
    293     SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
    294 
    295     SkRRect rr2;
    296     rr2.setRectRadii(rect, radii);
    297 
    298     path_contains_rrect_check(reporter, rr2);
    299 
    300     //----
    301     SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
    302 
    303     SkRRect rr3;
    304     rr3.setRectRadii(rect, radii2);
    305     path_contains_rrect_check(reporter, rr3);
    306 }
    307 
    308 // Test out the cases when the RR degenerates to an oval
    309 static void test_round_rect_ovals(skiatest::Reporter* reporter) {
    310     //----
    311     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
    312     SkRRect rr1;
    313     rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight));
    314 
    315     path_contains_rrect_check(reporter, rr1);
    316 }
    317 
    318 // Test out the non-degenerate RR cases
    319 static void test_round_rect_general(skiatest::Reporter* reporter) {
    320     //----
    321     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
    322     SkRRect rr1;
    323     rr1.setRectXY(rect, 20, 20);
    324 
    325     path_contains_rrect_check(reporter, rr1);
    326 
    327     //----
    328     SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
    329 
    330     SkRRect rr2;
    331     rr2.setRectRadii(rect, radii);
    332 
    333     path_contains_rrect_check(reporter, rr2);
    334 }
    335 
    336 static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) {
    337     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
    338     SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } };
    339     SkRRect rr1;
    340     rr1.setRectRadii(rect, radii);
    341     path_contains_rrect_nocheck(reporter, rr1);
    342 }
    343 
    344 static void set_radii(SkVector radii[4], int index, float rad) {
    345     sk_bzero(radii, sizeof(SkVector) * 4);
    346     radii[index].set(rad, rad);
    347 }
    348 
    349 static void test_skbug_3239(skiatest::Reporter* reporter) {
    350     const float min = SkBits2Float(0xcb7f16c8); /* -16717512.000000 */
    351     const float max = SkBits2Float(0x4b7f1c1d); /*  16718877.000000 */
    352     const float big = SkBits2Float(0x4b7f1bd7); /*  16718807.000000 */
    353 
    354     const float rad = 33436320;
    355 
    356     const SkRect rectx = SkRect::MakeLTRB(min, min, max, big);
    357     const SkRect recty = SkRect::MakeLTRB(min, min, big, max);
    358 
    359     SkVector radii[4];
    360     for (int i = 0; i < 4; ++i) {
    361         set_radii(radii, i, rad);
    362         path_contains_rrect_check(reporter, rectx, radii);
    363         path_contains_rrect_check(reporter, recty, radii);
    364     }
    365 }
    366 
    367 static void test_mix(skiatest::Reporter* reporter) {
    368     // Test out mixed degenerate and non-degenerate geometry with Conics
    369     const SkVector radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 100, 100 } };
    370     SkRect r = SkRect::MakeWH(100, 100);
    371     SkRRect rr;
    372     rr.setRectRadii(r, radii);
    373     path_contains_rrect_check(reporter, rr);
    374 }
    375 
    376 DEF_TEST(RoundRectInPath, reporter) {
    377     test_tricky_radii(reporter);
    378     test_empty_crbug_458524(reporter);
    379     test_inset(reporter);
    380     test_round_rect_basic(reporter);
    381     test_round_rect_rects(reporter);
    382     test_round_rect_ovals(reporter);
    383     test_round_rect_general(reporter);
    384     test_undetected_paths(reporter);
    385     test_round_rect_iffy_parameters(reporter);
    386     test_skbug_3239(reporter);
    387     test_mix(reporter);
    388 }
    389