Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2012 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 "SkRRect.h"
     10 #include "Test.h"
     11 
     12 static void test_tricky_radii(skiatest::Reporter* reporter) {
     13     {
     14         // crbug.com/458522
     15         SkRRect rr;
     16         const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
     17         const SkScalar rad = 12814;
     18         const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } };
     19         rr.setRectRadii(bounds, vec);
     20     }
     21 
     22     {
     23         // crbug.com//463920
     24         SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0);
     25         SkVector radii[4] = {
     26             { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f }
     27         };
     28         SkRRect rr;
     29         rr.setRectRadii(r, radii);
     30 
     31         REPORTER_ASSERT(reporter, (double) rr.radii(SkRRect::kUpperRight_Corner).fY +
     32                                   (double) rr.radii(SkRRect::kLowerRight_Corner).fY <=
     33                                   rr.height());
     34     }
     35 }
     36 
     37 static void test_empty_crbug_458524(skiatest::Reporter* reporter) {
     38     SkRRect rr;
     39     const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
     40     const SkScalar rad = 40;
     41     rr.setRectXY(bounds, rad, rad);
     42 
     43     SkRRect other;
     44     SkMatrix matrix;
     45     matrix.setScale(0, 1);
     46     rr.transform(matrix, &other);
     47     REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == other.getType());
     48 }
     49 
     50 // Test that all the SkRRect entry points correctly handle un-sorted and
     51 // zero-sized input rects
     52 static void test_empty(skiatest::Reporter* reporter) {
     53     static const SkRect oooRects[] = {  // out of order
     54         { 100, 0, 0, 100 },  // ooo horizontal
     55         { 0, 100, 100, 0 },  // ooo vertical
     56         { 100, 100, 0, 0 },  // ooo both
     57     };
     58 
     59     static const SkRect emptyRects[] = {
     60         { 100, 100, 100, 200 }, // empty horizontal
     61         { 100, 100, 200, 100 }, // empty vertical
     62         { 100, 100, 100, 100 }, // empty both
     63         { 0, 0, 0, 0 }          // setEmpty-empty
     64     };
     65 
     66     static const SkVector radii[4] = { { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 } };
     67 
     68     SkRRect r;
     69 
     70     for (size_t i = 0; i < SK_ARRAY_COUNT(oooRects); ++i) {
     71         r.setRect(oooRects[i]);
     72         REPORTER_ASSERT(reporter, !r.isEmpty());
     73 
     74         r.setOval(oooRects[i]);
     75         REPORTER_ASSERT(reporter, !r.isEmpty());
     76 
     77         r.setRectXY(oooRects[i], 1, 2);
     78         REPORTER_ASSERT(reporter, !r.isEmpty());
     79 
     80         r.setNinePatch(oooRects[i], 0, 1, 2, 3);
     81         REPORTER_ASSERT(reporter, !r.isEmpty());
     82 
     83         r.setRectRadii(oooRects[i], radii);
     84         REPORTER_ASSERT(reporter, !r.isEmpty());
     85     }
     86 
     87     for (size_t i = 0; i < SK_ARRAY_COUNT(emptyRects); ++i) {
     88         r.setRect(emptyRects[i]);
     89         REPORTER_ASSERT(reporter, r.isEmpty());
     90 
     91         r.setOval(emptyRects[i]);
     92         REPORTER_ASSERT(reporter, r.isEmpty());
     93 
     94         r.setRectXY(emptyRects[i], 1, 2);
     95         REPORTER_ASSERT(reporter, r.isEmpty());
     96 
     97         r.setNinePatch(emptyRects[i], 0, 1, 2, 3);
     98         REPORTER_ASSERT(reporter, r.isEmpty());
     99 
    100         r.setRectRadii(emptyRects[i], radii);
    101         REPORTER_ASSERT(reporter, r.isEmpty());
    102     }
    103 }
    104 
    105 static const SkScalar kWidth = 100.0f;
    106 static const SkScalar kHeight = 100.0f;
    107 
    108 static void test_inset(skiatest::Reporter* reporter) {
    109     SkRRect rr, rr2;
    110     SkRect r = { 0, 0, 100, 100 };
    111 
    112     rr.setRect(r);
    113     rr.inset(-20, -20, &rr2);
    114     REPORTER_ASSERT(reporter, rr2.isRect());
    115 
    116     rr.inset(20, 20, &rr2);
    117     REPORTER_ASSERT(reporter, rr2.isRect());
    118 
    119     rr.inset(r.width()/2, r.height()/2, &rr2);
    120     REPORTER_ASSERT(reporter, rr2.isEmpty());
    121 
    122     rr.setRectXY(r, 20, 20);
    123     rr.inset(19, 19, &rr2);
    124     REPORTER_ASSERT(reporter, rr2.isSimple());
    125     rr.inset(20, 20, &rr2);
    126     REPORTER_ASSERT(reporter, rr2.isRect());
    127 }
    128 
    129 
    130 static void test_9patch_rrect(skiatest::Reporter* reporter,
    131                               const SkRect& rect,
    132                               SkScalar l, SkScalar t, SkScalar r, SkScalar b,
    133                               bool checkRadii) {
    134     SkRRect rr;
    135     rr.setNinePatch(rect, l, t, r, b);
    136 
    137     REPORTER_ASSERT(reporter, SkRRect::kNinePatch_Type == rr.type());
    138     REPORTER_ASSERT(reporter, rr.rect() == rect);
    139 
    140     if (checkRadii) {
    141         // This test doesn't hold if the radii will be rescaled by SkRRect
    142         SkRect ninePatchRadii = { l, t, r, b };
    143         SkPoint rquad[4];
    144         ninePatchRadii.toQuad(rquad);
    145         for (int i = 0; i < 4; ++i) {
    146             REPORTER_ASSERT(reporter, rquad[i] == rr.radii((SkRRect::Corner) i));
    147         }
    148     }
    149     SkRRect rr2; // construct the same RR using the most general set function
    150     SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } };
    151     rr2.setRectRadii(rect, radii);
    152     REPORTER_ASSERT(reporter, rr2 == rr && rr2.getType() == rr.getType());
    153 }
    154 
    155 // Test out the basic API entry points
    156 static void test_round_rect_basic(skiatest::Reporter* reporter) {
    157     // Test out initialization methods
    158     SkPoint zeroPt = { 0, 0 };
    159     SkRRect empty;
    160 
    161     empty.setEmpty();
    162 
    163     REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
    164     REPORTER_ASSERT(reporter, empty.rect().isEmpty());
    165 
    166     for (int i = 0; i < 4; ++i) {
    167         REPORTER_ASSERT(reporter, zeroPt == empty.radii((SkRRect::Corner) i));
    168     }
    169 
    170     //----
    171     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
    172 
    173     SkRRect rr1;
    174     rr1.setRect(rect);
    175 
    176     REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
    177     REPORTER_ASSERT(reporter, rr1.rect() == rect);
    178 
    179     for (int i = 0; i < 4; ++i) {
    180         REPORTER_ASSERT(reporter, zeroPt == rr1.radii((SkRRect::Corner) i));
    181     }
    182     SkRRect rr1_2; // construct the same RR using the most general set function
    183     SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
    184     rr1_2.setRectRadii(rect, rr1_2_radii);
    185     REPORTER_ASSERT(reporter, rr1_2 == rr1 && rr1_2.getType() == rr1.getType());
    186     SkRRect rr1_3;  // construct the same RR using the nine patch set function
    187     rr1_3.setNinePatch(rect, 0, 0, 0, 0);
    188     REPORTER_ASSERT(reporter, rr1_3 == rr1 && rr1_3.getType() == rr1.getType());
    189 
    190     //----
    191     SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) };
    192     SkRRect rr2;
    193     rr2.setOval(rect);
    194 
    195     REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr2.type());
    196     REPORTER_ASSERT(reporter, rr2.rect() == rect);
    197 
    198     for (int i = 0; i < 4; ++i) {
    199         REPORTER_ASSERT(reporter,
    200                         rr2.radii((SkRRect::Corner) i).equalsWithinTolerance(halfPoint));
    201     }
    202     SkRRect rr2_2;  // construct the same RR using the most general set function
    203     SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY },
    204                                 { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY } };
    205     rr2_2.setRectRadii(rect, rr2_2_radii);
    206     REPORTER_ASSERT(reporter, rr2_2 == rr2 && rr2_2.getType() == rr2.getType());
    207     SkRRect rr2_3;  // construct the same RR using the nine patch set function
    208     rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY);
    209     REPORTER_ASSERT(reporter, rr2_3 == rr2 && rr2_3.getType() == rr2.getType());
    210 
    211     //----
    212     SkPoint p = { 5, 5 };
    213     SkRRect rr3;
    214     rr3.setRectXY(rect, p.fX, p.fY);
    215 
    216     REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr3.type());
    217     REPORTER_ASSERT(reporter, rr3.rect() == rect);
    218 
    219     for (int i = 0; i < 4; ++i) {
    220         REPORTER_ASSERT(reporter, p == rr3.radii((SkRRect::Corner) i));
    221     }
    222     SkRRect rr3_2; // construct the same RR using the most general set function
    223     SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } };
    224     rr3_2.setRectRadii(rect, rr3_2_radii);
    225     REPORTER_ASSERT(reporter, rr3_2 == rr3 && rr3_2.getType() == rr3.getType());
    226     SkRRect rr3_3;  // construct the same RR using the nine patch set function
    227     rr3_3.setNinePatch(rect, 5, 5, 5, 5);
    228     REPORTER_ASSERT(reporter, rr3_3 == rr3 && rr3_3.getType() == rr3.getType());
    229 
    230     //----
    231     test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true);
    232 
    233     {
    234         // Test out the rrect from skia:3466
    235         SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f);
    236 
    237         test_9patch_rrect(reporter,
    238                           rect2,
    239                           0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f,
    240                           false);
    241     }
    242 
    243     //----
    244     SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } };
    245 
    246     SkRRect rr5;
    247     rr5.setRectRadii(rect, radii2);
    248 
    249     REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr5.type());
    250     REPORTER_ASSERT(reporter, rr5.rect() == rect);
    251 
    252     for (int i = 0; i < 4; ++i) {
    253         REPORTER_ASSERT(reporter, radii2[i] == rr5.radii((SkRRect::Corner) i));
    254     }
    255 
    256     // Test out == & !=
    257     REPORTER_ASSERT(reporter, empty != rr3);
    258     REPORTER_ASSERT(reporter, rr3 != rr5);
    259 }
    260 
    261 // Test out the cases when the RR degenerates to a rect
    262 static void test_round_rect_rects(skiatest::Reporter* reporter) {
    263     SkRect r;
    264 
    265     //----
    266     SkRRect empty;
    267 
    268     empty.setEmpty();
    269 
    270     REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
    271     r = empty.rect();
    272     REPORTER_ASSERT(reporter, 0 == r.fLeft && 0 == r.fTop && 0 == r.fRight && 0 == r.fBottom);
    273 
    274     //----
    275     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
    276     SkRRect rr1;
    277     rr1.setRectXY(rect, 0, 0);
    278 
    279     REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
    280     r = rr1.rect();
    281     REPORTER_ASSERT(reporter, rect == r);
    282 
    283     //----
    284     SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
    285 
    286     SkRRect rr2;
    287     rr2.setRectRadii(rect, radii);
    288 
    289     REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
    290     r = rr2.rect();
    291     REPORTER_ASSERT(reporter, rect == r);
    292 
    293     //----
    294     SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
    295 
    296     SkRRect rr3;
    297     rr3.setRectRadii(rect, radii2);
    298     REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr3.type());
    299 }
    300 
    301 // Test out the cases when the RR degenerates to an oval
    302 static void test_round_rect_ovals(skiatest::Reporter* reporter) {
    303     //----
    304     SkRect oval;
    305     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
    306     SkRRect rr1;
    307     rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight));
    308 
    309     REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr1.type());
    310     oval = rr1.rect();
    311     REPORTER_ASSERT(reporter, oval == rect);
    312 }
    313 
    314 // Test out the non-degenerate RR cases
    315 static void test_round_rect_general(skiatest::Reporter* reporter) {
    316     //----
    317     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
    318     SkRRect rr1;
    319     rr1.setRectXY(rect, 20, 20);
    320 
    321     REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr1.type());
    322 
    323     //----
    324     SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
    325 
    326     SkRRect rr2;
    327     rr2.setRectRadii(rect, radii);
    328 
    329     REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr2.type());
    330 }
    331 
    332 // Test out questionable-parameter handling
    333 static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) {
    334 
    335     // When the radii exceed the base rect they are proportionally scaled down
    336     // to fit
    337     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
    338     SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } };
    339 
    340     SkRRect rr1;
    341     rr1.setRectRadii(rect, radii);
    342 
    343     REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr1.type());
    344 
    345     const SkPoint& p = rr1.radii(SkRRect::kUpperLeft_Corner);
    346 
    347     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fX, 33.33333f));
    348     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fY, 66.66666f));
    349 
    350     // Negative radii should be capped at zero
    351     SkRRect rr2;
    352     rr2.setRectXY(rect, -10, -20);
    353 
    354     REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
    355 
    356     const SkPoint& p2 = rr2.radii(SkRRect::kUpperLeft_Corner);
    357 
    358     REPORTER_ASSERT(reporter, 0.0f == p2.fX);
    359     REPORTER_ASSERT(reporter, 0.0f == p2.fY);
    360 }
    361 
    362 // Move a small box from the start position by (stepX, stepY) 'numSteps' times
    363 // testing for containment in 'rr' at each step.
    364 static void test_direction(skiatest::Reporter* reporter, const SkRRect &rr,
    365                            SkScalar initX, int stepX, SkScalar initY, int stepY,
    366                            int numSteps, const bool* contains) {
    367     SkScalar x = initX, y = initY;
    368     for (int i = 0; i < numSteps; ++i) {
    369         SkRect test = SkRect::MakeXYWH(x, y,
    370                                        stepX ? SkIntToScalar(stepX) : SK_Scalar1,
    371                                        stepY ? SkIntToScalar(stepY) : SK_Scalar1);
    372         test.sort();
    373 
    374         REPORTER_ASSERT(reporter, contains[i] == rr.contains(test));
    375 
    376         x += stepX;
    377         y += stepY;
    378     }
    379 }
    380 
    381 // Exercise the RR's contains rect method
    382 static void test_round_rect_contains_rect(skiatest::Reporter* reporter) {
    383 
    384     static const int kNumRRects = 4;
    385     static const SkVector gRadii[kNumRRects][4] = {
    386         { {  0,  0 }, {  0,  0 }, {  0,  0 }, {  0,  0 } },  // rect
    387         { { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 } },  // circle
    388         { { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 10 } },  // simple
    389         { {  0,  0 }, { 20, 20 }, { 10, 10 }, { 30, 30 } }   // complex
    390     };
    391 
    392     SkRRect rrects[kNumRRects];
    393     for (int i = 0; i < kNumRRects; ++i) {
    394         rrects[i].setRectRadii(SkRect::MakeWH(40, 40), gRadii[i]);
    395     }
    396 
    397     // First test easy outs - boxes that are obviously out on
    398     // each corner and edge
    399     static const SkRect easyOuts[] = {
    400         { -5, -5,  5,  5 }, // NW
    401         { 15, -5, 20,  5 }, // N
    402         { 35, -5, 45,  5 }, // NE
    403         { 35, 15, 45, 20 }, // E
    404         { 35, 45, 35, 45 }, // SE
    405         { 15, 35, 20, 45 }, // S
    406         { -5, 35,  5, 45 }, // SW
    407         { -5, 15,  5, 20 }  // W
    408     };
    409 
    410     for (int i = 0; i < kNumRRects; ++i) {
    411         for (size_t j = 0; j < SK_ARRAY_COUNT(easyOuts); ++j) {
    412             REPORTER_ASSERT(reporter, !rrects[i].contains(easyOuts[j]));
    413         }
    414     }
    415 
    416     // Now test non-trivial containment. For each compass
    417     // point walk a 1x1 rect in from the edge  of the bounding
    418     // rect
    419     static const int kNumSteps = 15;
    420     bool answers[kNumRRects][8][kNumSteps] = {
    421         // all the test rects are inside the degenerate rrect
    422         {
    423             // rect
    424             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    425             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    426             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    427             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    428             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    429             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    430             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    431             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    432         },
    433         // for the circle we expect 6 blocks to be out on the
    434         // corners (then the rest in) and only the first block
    435         // out on the vertical and horizontal axes (then
    436         // the rest in)
    437         {
    438             // circle
    439             { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    440             { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    441             { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    442             { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    443             { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    444             { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    445             { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    446             { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    447         },
    448         // for the simple round rect we expect 3 out on
    449         // the corners (then the rest in) and no blocks out
    450         // on the vertical and horizontal axes
    451         {
    452             // simple RR
    453             { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    454             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    455             { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    456             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    457             { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    458             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    459             { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    460             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    461         },
    462         // for the complex case the answer is different for each direction
    463         {
    464             // complex RR
    465             // all in for NW (rect) corner (same as rect case)
    466             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    467             // only first block out for N (same as circle case)
    468             { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    469             // first 6 blocks out for NE (same as circle case)
    470             { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    471             // only first block out for E (same as circle case)
    472             { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    473             // first 3 blocks out for SE (same as simple case)
    474             { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    475             // first two blocks out for S
    476             { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    477             // first 9 blocks out for SW
    478             { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
    479             // first two blocks out for W (same as S)
    480             { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    481          }
    482     };
    483 
    484     for (int i = 0; i < kNumRRects; ++i) {
    485         test_direction(reporter, rrects[i],     0,  1,     0,  1, kNumSteps, answers[i][0]); // NW
    486         test_direction(reporter, rrects[i], 19.5f,  0,     0,  1, kNumSteps, answers[i][1]); // N
    487         test_direction(reporter, rrects[i],    40, -1,     0,  1, kNumSteps, answers[i][2]); // NE
    488         test_direction(reporter, rrects[i],    40, -1, 19.5f,  0, kNumSteps, answers[i][3]); // E
    489         test_direction(reporter, rrects[i],    40, -1,    40, -1, kNumSteps, answers[i][4]); // SE
    490         test_direction(reporter, rrects[i], 19.5f,  0,    40, -1, kNumSteps, answers[i][5]); // S
    491         test_direction(reporter, rrects[i],     0,  1,    40, -1, kNumSteps, answers[i][6]); // SW
    492         test_direction(reporter, rrects[i],     0,  1, 19.5f,  0, kNumSteps, answers[i][7]); // W
    493     }
    494 }
    495 
    496 // Called for a matrix that should cause SkRRect::transform to fail.
    497 static void assert_transform_failure(skiatest::Reporter* reporter, const SkRRect& orig,
    498                                      const SkMatrix& matrix) {
    499     // The test depends on the fact that the original is not empty.
    500     SkASSERT(!orig.isEmpty());
    501     SkRRect dst;
    502     dst.setEmpty();
    503 
    504     const SkRRect copyOfDst = dst;
    505     const SkRRect copyOfOrig = orig;
    506     bool success = orig.transform(matrix, &dst);
    507     // This transform should fail.
    508     REPORTER_ASSERT(reporter, !success);
    509     // Since the transform failed, dst should be unchanged.
    510     REPORTER_ASSERT(reporter, copyOfDst == dst);
    511     // original should not be modified.
    512     REPORTER_ASSERT(reporter, copyOfOrig == orig);
    513     REPORTER_ASSERT(reporter, orig != dst);
    514 }
    515 
    516 #define GET_RADII                                                       \
    517     const SkVector& origUL = orig.radii(SkRRect::kUpperLeft_Corner);    \
    518     const SkVector& origUR = orig.radii(SkRRect::kUpperRight_Corner);   \
    519     const SkVector& origLR = orig.radii(SkRRect::kLowerRight_Corner);   \
    520     const SkVector& origLL = orig.radii(SkRRect::kLowerLeft_Corner);    \
    521     const SkVector& dstUL = dst.radii(SkRRect::kUpperLeft_Corner);      \
    522     const SkVector& dstUR = dst.radii(SkRRect::kUpperRight_Corner);     \
    523     const SkVector& dstLR = dst.radii(SkRRect::kLowerRight_Corner);     \
    524     const SkVector& dstLL = dst.radii(SkRRect::kLowerLeft_Corner)
    525 
    526 // Called to test various transforms on a single SkRRect.
    527 static void test_transform_helper(skiatest::Reporter* reporter, const SkRRect& orig) {
    528     SkRRect dst;
    529     dst.setEmpty();
    530 
    531     // The identity matrix will duplicate the rrect.
    532     bool success = orig.transform(SkMatrix::I(), &dst);
    533     REPORTER_ASSERT(reporter, success);
    534     REPORTER_ASSERT(reporter, orig == dst);
    535 
    536     // Skew and Perspective make transform fail.
    537     SkMatrix matrix;
    538     matrix.reset();
    539     matrix.setSkewX(SkIntToScalar(2));
    540     assert_transform_failure(reporter, orig, matrix);
    541 
    542     matrix.reset();
    543     matrix.setSkewY(SkIntToScalar(3));
    544     assert_transform_failure(reporter, orig, matrix);
    545 
    546     matrix.reset();
    547     matrix.setPerspX(4);
    548     assert_transform_failure(reporter, orig, matrix);
    549 
    550     matrix.reset();
    551     matrix.setPerspY(5);
    552     assert_transform_failure(reporter, orig, matrix);
    553 
    554     // Rotation fails.
    555     matrix.reset();
    556     matrix.setRotate(SkIntToScalar(90));
    557     assert_transform_failure(reporter, orig, matrix);
    558     matrix.setRotate(SkIntToScalar(37));
    559     assert_transform_failure(reporter, orig, matrix);
    560 
    561     // Translate will keep the rect moved, but otherwise the same.
    562     matrix.reset();
    563     SkScalar translateX = SkIntToScalar(32);
    564     SkScalar translateY = SkIntToScalar(15);
    565     matrix.setTranslateX(translateX);
    566     matrix.setTranslateY(translateY);
    567     dst.setEmpty();
    568     success = orig.transform(matrix, &dst);
    569     REPORTER_ASSERT(reporter, success);
    570     for (int i = 0; i < 4; ++i) {
    571         REPORTER_ASSERT(reporter,
    572                 orig.radii((SkRRect::Corner) i) == dst.radii((SkRRect::Corner) i));
    573     }
    574     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
    575     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
    576     REPORTER_ASSERT(reporter, dst.rect().left() == orig.rect().left() + translateX);
    577     REPORTER_ASSERT(reporter, dst.rect().top() == orig.rect().top() + translateY);
    578 
    579     // Keeping the translation, but adding skew will make transform fail.
    580     matrix.setSkewY(SkIntToScalar(7));
    581     assert_transform_failure(reporter, orig, matrix);
    582 
    583     // Scaling in -x will flip the round rect horizontally.
    584     matrix.reset();
    585     matrix.setScaleX(SkIntToScalar(-1));
    586     dst.setEmpty();
    587     success = orig.transform(matrix, &dst);
    588     REPORTER_ASSERT(reporter, success);
    589     {
    590         GET_RADII;
    591         // Radii have swapped in x.
    592         REPORTER_ASSERT(reporter, origUL == dstUR);
    593         REPORTER_ASSERT(reporter, origUR == dstUL);
    594         REPORTER_ASSERT(reporter, origLR == dstLL);
    595         REPORTER_ASSERT(reporter, origLL == dstLR);
    596     }
    597     // Width and height remain the same.
    598     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
    599     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
    600     // Right and left have swapped (sort of)
    601     REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
    602     // Top has stayed the same.
    603     REPORTER_ASSERT(reporter, orig.rect().top() == dst.rect().top());
    604 
    605     // Keeping the scale, but adding a persp will make transform fail.
    606     matrix.setPerspX(7);
    607     assert_transform_failure(reporter, orig, matrix);
    608 
    609     // Scaling in -y will flip the round rect vertically.
    610     matrix.reset();
    611     matrix.setScaleY(SkIntToScalar(-1));
    612     dst.setEmpty();
    613     success = orig.transform(matrix, &dst);
    614     REPORTER_ASSERT(reporter, success);
    615     {
    616         GET_RADII;
    617         // Radii have swapped in y.
    618         REPORTER_ASSERT(reporter, origUL == dstLL);
    619         REPORTER_ASSERT(reporter, origUR == dstLR);
    620         REPORTER_ASSERT(reporter, origLR == dstUR);
    621         REPORTER_ASSERT(reporter, origLL == dstUL);
    622     }
    623     // Width and height remain the same.
    624     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
    625     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
    626     // Top and bottom have swapped (sort of)
    627     REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
    628     // Left has stayed the same.
    629     REPORTER_ASSERT(reporter, orig.rect().left() == dst.rect().left());
    630 
    631     // Scaling in -x and -y will swap in both directions.
    632     matrix.reset();
    633     matrix.setScaleY(SkIntToScalar(-1));
    634     matrix.setScaleX(SkIntToScalar(-1));
    635     dst.setEmpty();
    636     success = orig.transform(matrix, &dst);
    637     REPORTER_ASSERT(reporter, success);
    638     {
    639         GET_RADII;
    640         REPORTER_ASSERT(reporter, origUL == dstLR);
    641         REPORTER_ASSERT(reporter, origUR == dstLL);
    642         REPORTER_ASSERT(reporter, origLR == dstUL);
    643         REPORTER_ASSERT(reporter, origLL == dstUR);
    644     }
    645     // Width and height remain the same.
    646     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
    647     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
    648     REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
    649     REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
    650 
    651     // Scale in both directions.
    652     SkScalar xScale = SkIntToScalar(3);
    653     SkScalar yScale = 3.2f;
    654     matrix.reset();
    655     matrix.setScaleX(xScale);
    656     matrix.setScaleY(yScale);
    657     dst.setEmpty();
    658     success = orig.transform(matrix, &dst);
    659     REPORTER_ASSERT(reporter, success);
    660     // Radii are scaled.
    661     for (int i = 0; i < 4; ++i) {
    662         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX,
    663                                     orig.radii((SkRRect::Corner) i).fX * xScale));
    664         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY,
    665                                     orig.radii((SkRRect::Corner) i).fY * yScale));
    666     }
    667     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(),
    668                                                   orig.rect().width() * xScale));
    669     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(),
    670                                                   orig.rect().height() * yScale));
    671     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(),
    672                                                   orig.rect().left() * xScale));
    673     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(),
    674                                                   orig.rect().top() * yScale));
    675 }
    676 
    677 static void test_round_rect_transform(skiatest::Reporter* reporter) {
    678     SkRRect rrect;
    679     {
    680         SkRect r = { 0, 0, kWidth, kHeight };
    681         rrect.setRectXY(r, SkIntToScalar(4), SkIntToScalar(7));
    682         test_transform_helper(reporter, rrect);
    683     }
    684     {
    685         SkRect r = { SkIntToScalar(5), SkIntToScalar(15),
    686                      SkIntToScalar(27), SkIntToScalar(34) };
    687         SkVector radii[4] = { { 0, SkIntToScalar(1) },
    688                               { SkIntToScalar(2), SkIntToScalar(3) },
    689                               { SkIntToScalar(4), SkIntToScalar(5) },
    690                               { SkIntToScalar(6), SkIntToScalar(7) } };
    691         rrect.setRectRadii(r, radii);
    692         test_transform_helper(reporter, rrect);
    693     }
    694 }
    695 
    696 // Test out the case where an oval already off in space is translated/scaled
    697 // further off into space - yielding numerical issues when the rect & radii
    698 // are transformed separatly
    699 // BUG=skia:2696
    700 static void test_issue_2696(skiatest::Reporter* reporter) {
    701     SkRRect rrect;
    702     SkRect r = { 28443.8594f, 53.1428604f, 28446.7148f, 56.0000038f };
    703     rrect.setOval(r);
    704 
    705     SkMatrix xform;
    706     xform.setAll(2.44f,  0.0f, 485411.7f,
    707                  0.0f,  2.44f,   -438.7f,
    708                  0.0f,   0.0f,      1.0f);
    709     SkRRect dst;
    710 
    711     bool success = rrect.transform(xform, &dst);
    712     REPORTER_ASSERT(reporter, success);
    713 
    714     SkScalar halfWidth = SkScalarHalf(dst.width());
    715     SkScalar halfHeight = SkScalarHalf(dst.height());
    716 
    717     for (int i = 0; i < 4; ++i) {
    718         REPORTER_ASSERT(reporter,
    719                         SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth));
    720         REPORTER_ASSERT(reporter,
    721                         SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight));
    722     }
    723 }
    724 
    725 DEF_TEST(RoundRect, reporter) {
    726     test_round_rect_basic(reporter);
    727     test_round_rect_rects(reporter);
    728     test_round_rect_ovals(reporter);
    729     test_round_rect_general(reporter);
    730     test_round_rect_iffy_parameters(reporter);
    731     test_inset(reporter);
    732     test_round_rect_contains_rect(reporter);
    733     test_round_rect_transform(reporter);
    734     test_issue_2696(reporter);
    735     test_tricky_radii(reporter);
    736     test_empty_crbug_458524(reporter);
    737     test_empty(reporter);
    738 }
    739