• 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 // Test that all the SkRRect entry points correctly handle un-sorted and
51 // zero-sized input rects
test_empty(skiatest::Reporter * reporter)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 
test_inset(skiatest::Reporter * reporter)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 
test_9patch_rrect(skiatest::Reporter * reporter,const SkRect & rect,SkScalar l,SkScalar t,SkScalar r,SkScalar b,bool checkRadii)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
test_round_rect_basic(skiatest::Reporter * reporter)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
test_round_rect_rects(skiatest::Reporter * reporter)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
test_round_rect_ovals(skiatest::Reporter * reporter)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
test_round_rect_general(skiatest::Reporter * reporter)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
test_round_rect_iffy_parameters(skiatest::Reporter * reporter)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.
test_direction(skiatest::Reporter * reporter,const SkRRect & rr,SkScalar initX,int stepX,SkScalar initY,int stepY,int numSteps,const bool * contains)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
test_round_rect_contains_rect(skiatest::Reporter * reporter)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.
assert_transform_failure(skiatest::Reporter * reporter,const SkRRect & orig,const SkMatrix & matrix)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.
test_transform_helper(skiatest::Reporter * reporter,const SkRRect & orig)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                                     SkScalarMul(orig.radii((SkRRect::Corner) i).fX, xScale)));
664         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY,
665                                     SkScalarMul(orig.radii((SkRRect::Corner) i).fY, yScale)));
666     }
667     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(),
668                                                   SkScalarMul(orig.rect().width(), xScale)));
669     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(),
670                                                   SkScalarMul(orig.rect().height(), yScale)));
671     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(),
672                                                   SkScalarMul(orig.rect().left(), xScale)));
673     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(),
674                                                   SkScalarMul(orig.rect().top(), yScale)));
675 }
676 
test_round_rect_transform(skiatest::Reporter * reporter)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
test_issue_2696(skiatest::Reporter * reporter)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 
DEF_TEST(RoundRect,reporter)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