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