• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
test_tricky_radii(skiatest::Reporter * reporter)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 
test_empty_crbug_458524(skiatest::Reporter * reporter)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 
test_inset(skiatest::Reporter * reporter)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 
test_9patch_rrect(skiatest::Reporter * reporter,const SkRect & rect,SkScalar l,SkScalar t,SkScalar r,SkScalar b,bool checkRadii)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
test_round_rect_basic(skiatest::Reporter * reporter)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
test_round_rect_rects(skiatest::Reporter * reporter)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
test_round_rect_ovals(skiatest::Reporter * reporter)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
test_round_rect_general(skiatest::Reporter * reporter)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
test_round_rect_iffy_parameters(skiatest::Reporter * reporter)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.
test_direction(skiatest::Reporter * reporter,const SkRRect & rr,SkScalar initX,int stepX,SkScalar initY,int stepY,int numSteps,const bool * contains)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
test_round_rect_contains_rect(skiatest::Reporter * reporter)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.
assert_transform_failure(skiatest::Reporter * reporter,const SkRRect & orig,const SkMatrix & matrix)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.
test_transform_helper(skiatest::Reporter * reporter,const SkRRect & orig)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 
test_round_rect_transform(skiatest::Reporter * reporter)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
test_issue_2696(skiatest::Reporter * reporter)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 
DEF_TEST(RoundRect,reporter)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