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