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