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 "include/core/SkMatrix.h"
9 #include "include/core/SkPath.h"
10 #include "include/core/SkPoint.h"
11 #include "include/core/SkRRect.h"
12 #include "include/core/SkRect.h"
13 #include "include/core/SkScalar.h"
14 #include "include/core/SkTypes.h"
15 #include "include/pathops/SkPathOps.h"
16 #include "src/base/SkRandom.h"
17 #include "src/core/SkPointPriv.h"
18 #include "src/core/SkRRectPriv.h"
19 #include "tests/Test.h"
20 
21 #include <algorithm>
22 #include <array>
23 #include <cstddef>
24 #include <cstdint>
25 
test_tricky_radii(skiatest::Reporter * reporter)26 static void test_tricky_radii(skiatest::Reporter* reporter) {
27     {
28         // crbug.com/458522
29         SkRRect rr;
30         const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
31         const SkScalar rad = 12814;
32         const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } };
33         rr.setRectRadii(bounds, vec);
34     }
35 
36     {
37         // crbug.com//463920
38         SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0);
39         SkVector radii[4] = {
40             { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f }
41         };
42         SkRRect rr;
43         rr.setRectRadii(r, radii);
44 
45         REPORTER_ASSERT(reporter, (double) rr.radii(SkRRect::kUpperRight_Corner).fY +
46                                   (double) rr.radii(SkRRect::kLowerRight_Corner).fY <=
47                                   rr.height());
48     }
49 }
50 
test_empty_crbug_458524(skiatest::Reporter * reporter)51 static void test_empty_crbug_458524(skiatest::Reporter* reporter) {
52     SkRRect rr;
53     const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
54     const SkScalar rad = 40;
55     rr.setRectXY(bounds, rad, rad);
56 
57     SkRRect other;
58     SkMatrix matrix;
59     matrix.setScale(0, 1);
60     rr.transform(matrix, &other);
61     REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == other.getType());
62 }
63 
64 // Test that all the SkRRect entry points correctly handle un-sorted and
65 // zero-sized input rects
test_empty(skiatest::Reporter * reporter)66 static void test_empty(skiatest::Reporter* reporter) {
67     static const SkRect oooRects[] = {  // out of order
68         { 100, 0, 0, 100 },  // ooo horizontal
69         { 0, 100, 100, 0 },  // ooo vertical
70         { 100, 100, 0, 0 },  // ooo both
71     };
72 
73     static const SkRect emptyRects[] = {
74         { 100, 100, 100, 200 }, // empty horizontal
75         { 100, 100, 200, 100 }, // empty vertical
76         { 100, 100, 100, 100 }, // empty both
77         { 0, 0, 0, 0 }          // setEmpty-empty
78     };
79 
80     static const SkVector radii[4] = { { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 } };
81 
82     SkRRect r;
83 
84     for (size_t i = 0; i < std::size(oooRects); ++i) {
85         r.setRect(oooRects[i]);
86         REPORTER_ASSERT(reporter, !r.isEmpty());
87         REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
88 
89         r.setOval(oooRects[i]);
90         REPORTER_ASSERT(reporter, !r.isEmpty());
91         REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
92 
93         r.setRectXY(oooRects[i], 1, 2);
94         REPORTER_ASSERT(reporter, !r.isEmpty());
95         REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
96 
97         r.setNinePatch(oooRects[i], 0, 1, 2, 3);
98         REPORTER_ASSERT(reporter, !r.isEmpty());
99         REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
100 
101         r.setRectRadii(oooRects[i], radii);
102         REPORTER_ASSERT(reporter, !r.isEmpty());
103         REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
104     }
105 
106     for (size_t i = 0; i < std::size(emptyRects); ++i) {
107         r.setRect(emptyRects[i]);
108         REPORTER_ASSERT(reporter, r.isEmpty());
109         REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
110 
111         r.setOval(emptyRects[i]);
112         REPORTER_ASSERT(reporter, r.isEmpty());
113         REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
114 
115         r.setRectXY(emptyRects[i], 1, 2);
116         REPORTER_ASSERT(reporter, r.isEmpty());
117         REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
118 
119         r.setNinePatch(emptyRects[i], 0, 1, 2, 3);
120         REPORTER_ASSERT(reporter, r.isEmpty());
121         REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
122 
123         r.setRectRadii(emptyRects[i], radii);
124         REPORTER_ASSERT(reporter, r.isEmpty());
125         REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
126     }
127 
128     r.setRect({SK_ScalarNaN, 10, 10, 20});
129     REPORTER_ASSERT(reporter, r == SkRRect::MakeEmpty());
130     r.setRect({0, 10, 10, SK_ScalarInfinity});
131     REPORTER_ASSERT(reporter, r == SkRRect::MakeEmpty());
132 }
133 
134 static const SkScalar kWidth = 100.0f;
135 static const SkScalar kHeight = 100.0f;
136 
test_inset(skiatest::Reporter * reporter)137 static void test_inset(skiatest::Reporter* reporter) {
138     SkRRect rr, rr2;
139     SkRect r = { 0, 0, 100, 100 };
140 
141     rr.setRect(r);
142     rr.inset(-20, -20, &rr2);
143     REPORTER_ASSERT(reporter, rr2.isRect());
144 
145     rr.inset(20, 20, &rr2);
146     REPORTER_ASSERT(reporter, rr2.isRect());
147 
148     rr.inset(r.width()/2, r.height()/2, &rr2);
149     REPORTER_ASSERT(reporter, rr2.isEmpty());
150 
151     rr.setRectXY(r, 20, 20);
152     rr.inset(19, 19, &rr2);
153     REPORTER_ASSERT(reporter, rr2.isSimple());
154     rr.inset(20, 20, &rr2);
155     REPORTER_ASSERT(reporter, rr2.isRect());
156 }
157 
158 
test_9patch_rrect(skiatest::Reporter * reporter,const SkRect & rect,SkScalar l,SkScalar t,SkScalar r,SkScalar b,bool checkRadii)159 static void test_9patch_rrect(skiatest::Reporter* reporter,
160                               const SkRect& rect,
161                               SkScalar l, SkScalar t, SkScalar r, SkScalar b,
162                               bool checkRadii) {
163     SkRRect rr;
164     rr.setNinePatch(rect, l, t, r, b);
165 
166     REPORTER_ASSERT(reporter, SkRRect::kNinePatch_Type == rr.type());
167     REPORTER_ASSERT(reporter, rr.rect() == rect);
168 
169     if (checkRadii) {
170         // This test doesn't hold if the radii will be rescaled by SkRRect
171         SkRect ninePatchRadii = { l, t, r, b };
172         SkPoint rquad[4];
173         ninePatchRadii.toQuad(rquad);
174         for (int i = 0; i < 4; ++i) {
175             REPORTER_ASSERT(reporter, rquad[i] == rr.radii((SkRRect::Corner) i));
176         }
177     }
178     SkRRect rr2; // construct the same RR using the most general set function
179     SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } };
180     rr2.setRectRadii(rect, radii);
181     REPORTER_ASSERT(reporter, rr2 == rr && rr2.getType() == rr.getType());
182 }
183 
184 // Test out the basic API entry points
test_round_rect_basic(skiatest::Reporter * reporter)185 static void test_round_rect_basic(skiatest::Reporter* reporter) {
186     // Test out initialization methods
187     SkPoint zeroPt = { 0, 0 };
188     SkRRect empty;
189 
190     empty.setEmpty();
191 
192     REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
193     REPORTER_ASSERT(reporter, empty.rect().isEmpty());
194 
195     for (int i = 0; i < 4; ++i) {
196         REPORTER_ASSERT(reporter, zeroPt == empty.radii((SkRRect::Corner) i));
197     }
198 
199     //----
200     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
201 
202     SkRRect rr1;
203     rr1.setRect(rect);
204 
205     REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
206     REPORTER_ASSERT(reporter, rr1.rect() == rect);
207 
208     for (int i = 0; i < 4; ++i) {
209         REPORTER_ASSERT(reporter, zeroPt == rr1.radii((SkRRect::Corner) i));
210     }
211     SkRRect rr1_2; // construct the same RR using the most general set function
212     SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
213     rr1_2.setRectRadii(rect, rr1_2_radii);
214     REPORTER_ASSERT(reporter, rr1_2 == rr1 && rr1_2.getType() == rr1.getType());
215     SkRRect rr1_3;  // construct the same RR using the nine patch set function
216     rr1_3.setNinePatch(rect, 0, 0, 0, 0);
217     REPORTER_ASSERT(reporter, rr1_3 == rr1 && rr1_3.getType() == rr1.getType());
218 
219     //----
220     SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) };
221     SkRRect rr2;
222     rr2.setOval(rect);
223 
224     REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr2.type());
225     REPORTER_ASSERT(reporter, rr2.rect() == rect);
226 
227     for (int i = 0; i < 4; ++i) {
228         REPORTER_ASSERT(reporter,
229                         SkPointPriv::EqualsWithinTolerance(rr2.radii((SkRRect::Corner) i),
230                         halfPoint));
231     }
232     SkRRect rr2_2;  // construct the same RR using the most general set function
233     SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY },
234                                 { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY } };
235     rr2_2.setRectRadii(rect, rr2_2_radii);
236     REPORTER_ASSERT(reporter, rr2_2 == rr2 && rr2_2.getType() == rr2.getType());
237     SkRRect rr2_3;  // construct the same RR using the nine patch set function
238     rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY);
239     REPORTER_ASSERT(reporter, rr2_3 == rr2 && rr2_3.getType() == rr2.getType());
240 
241     //----
242     SkPoint p = { 5, 5 };
243     SkRRect rr3;
244     rr3.setRectXY(rect, p.fX, p.fY);
245 
246     REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr3.type());
247     REPORTER_ASSERT(reporter, rr3.rect() == rect);
248 
249     for (int i = 0; i < 4; ++i) {
250         REPORTER_ASSERT(reporter, p == rr3.radii((SkRRect::Corner) i));
251     }
252     SkRRect rr3_2; // construct the same RR using the most general set function
253     SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } };
254     rr3_2.setRectRadii(rect, rr3_2_radii);
255     REPORTER_ASSERT(reporter, rr3_2 == rr3 && rr3_2.getType() == rr3.getType());
256     SkRRect rr3_3;  // construct the same RR using the nine patch set function
257     rr3_3.setNinePatch(rect, 5, 5, 5, 5);
258     REPORTER_ASSERT(reporter, rr3_3 == rr3 && rr3_3.getType() == rr3.getType());
259 
260     //----
261     test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true);
262 
263     {
264         // Test out the rrect from skia:3466
265         SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f);
266 
267         test_9patch_rrect(reporter,
268                           rect2,
269                           0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f,
270                           false);
271     }
272 
273     //----
274     SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } };
275 
276     SkRRect rr5;
277     rr5.setRectRadii(rect, radii2);
278 
279     REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr5.type());
280     REPORTER_ASSERT(reporter, rr5.rect() == rect);
281 
282     for (int i = 0; i < 4; ++i) {
283         REPORTER_ASSERT(reporter, radii2[i] == rr5.radii((SkRRect::Corner) i));
284     }
285 
286     // Test out == & !=
287     REPORTER_ASSERT(reporter, empty != rr3);
288     REPORTER_ASSERT(reporter, rr3 != rr5);
289 }
290 
291 // Test out the cases when the RR degenerates to a rect
test_round_rect_rects(skiatest::Reporter * reporter)292 static void test_round_rect_rects(skiatest::Reporter* reporter) {
293     SkRect r;
294 
295     //----
296     SkRRect empty;
297 
298     empty.setEmpty();
299 
300     REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
301     r = empty.rect();
302     REPORTER_ASSERT(reporter, 0 == r.fLeft && 0 == r.fTop && 0 == r.fRight && 0 == r.fBottom);
303 
304     //----
305     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
306     SkRRect rr1;
307     rr1.setRectXY(rect, 0, 0);
308 
309     REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
310     r = rr1.rect();
311     REPORTER_ASSERT(reporter, rect == r);
312 
313     //----
314     SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
315 
316     SkRRect rr2;
317     rr2.setRectRadii(rect, radii);
318 
319     REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
320     r = rr2.rect();
321     REPORTER_ASSERT(reporter, rect == r);
322 
323     //----
324     SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
325 
326     SkRRect rr3;
327     rr3.setRectRadii(rect, radii2);
328     REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr3.type());
329 }
330 
331 // Test out the cases when the RR degenerates to an oval
test_round_rect_ovals(skiatest::Reporter * reporter)332 static void test_round_rect_ovals(skiatest::Reporter* reporter) {
333     //----
334     SkRect oval;
335     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
336     SkRRect rr1;
337     rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight));
338 
339     REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr1.type());
340     oval = rr1.rect();
341     REPORTER_ASSERT(reporter, oval == rect);
342 }
343 
344 // Test out the non-degenerate RR cases
test_round_rect_general(skiatest::Reporter * reporter)345 static void test_round_rect_general(skiatest::Reporter* reporter) {
346     //----
347     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
348     SkRRect rr1;
349     rr1.setRectXY(rect, 20, 20);
350 
351     REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr1.type());
352 
353     //----
354     SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
355 
356     SkRRect rr2;
357     rr2.setRectRadii(rect, radii);
358 
359     REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr2.type());
360 }
361 
362 // Test out questionable-parameter handling
test_round_rect_iffy_parameters(skiatest::Reporter * reporter)363 static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) {
364 
365     // When the radii exceed the base rect they are proportionally scaled down
366     // to fit
367     SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
368     SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } };
369 
370     SkRRect rr1;
371     rr1.setRectRadii(rect, radii);
372 
373     REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr1.type());
374 
375     const SkPoint& p = rr1.radii(SkRRect::kUpperLeft_Corner);
376 
377     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fX, 33.33333f));
378     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fY, 66.66666f));
379 
380     // Negative radii should be capped at zero
381     SkRRect rr2;
382     rr2.setRectXY(rect, -10, -20);
383 
384     REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
385 
386     const SkPoint& p2 = rr2.radii(SkRRect::kUpperLeft_Corner);
387 
388     REPORTER_ASSERT(reporter, 0.0f == p2.fX);
389     REPORTER_ASSERT(reporter, 0.0f == p2.fY);
390 }
391 
392 // Move a small box from the start position by (stepX, stepY) 'numSteps' times
393 // 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)394 static void test_direction(skiatest::Reporter* reporter, const SkRRect &rr,
395                            SkScalar initX, int stepX, SkScalar initY, int stepY,
396                            int numSteps, const bool* contains) {
397     SkScalar x = initX, y = initY;
398     for (int i = 0; i < numSteps; ++i) {
399         SkRect test = SkRect::MakeXYWH(x, y,
400                                        stepX ? SkIntToScalar(stepX) : SK_Scalar1,
401                                        stepY ? SkIntToScalar(stepY) : SK_Scalar1);
402         test.sort();
403 
404         REPORTER_ASSERT(reporter, contains[i] == rr.contains(test));
405 
406         x += stepX;
407         y += stepY;
408     }
409 }
410 
411 // Exercise the RR's contains rect method
test_round_rect_contains_rect(skiatest::Reporter * reporter)412 static void test_round_rect_contains_rect(skiatest::Reporter* reporter) {
413 
414     static const int kNumRRects = 4;
415     static const SkVector gRadii[kNumRRects][4] = {
416         { {  0,  0 }, {  0,  0 }, {  0,  0 }, {  0,  0 } },  // rect
417         { { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 } },  // circle
418         { { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 10 } },  // simple
419         { {  0,  0 }, { 20, 20 }, { 10, 10 }, { 30, 30 } }   // complex
420     };
421 
422     SkRRect rrects[kNumRRects];
423     for (int i = 0; i < kNumRRects; ++i) {
424         rrects[i].setRectRadii(SkRect::MakeWH(40, 40), gRadii[i]);
425     }
426 
427     // First test easy outs - boxes that are obviously out on
428     // each corner and edge
429     static const SkRect easyOuts[] = {
430         { -5, -5,  5,  5 }, // NW
431         { 15, -5, 20,  5 }, // N
432         { 35, -5, 45,  5 }, // NE
433         { 35, 15, 45, 20 }, // E
434         { 35, 45, 35, 45 }, // SE
435         { 15, 35, 20, 45 }, // S
436         { -5, 35,  5, 45 }, // SW
437         { -5, 15,  5, 20 }  // W
438     };
439 
440     for (int i = 0; i < kNumRRects; ++i) {
441         for (size_t j = 0; j < std::size(easyOuts); ++j) {
442             REPORTER_ASSERT(reporter, !rrects[i].contains(easyOuts[j]));
443         }
444     }
445 
446     // Now test non-trivial containment. For each compass
447     // point walk a 1x1 rect in from the edge  of the bounding
448     // rect
449     static const int kNumSteps = 15;
450     bool answers[kNumRRects][8][kNumSteps] = {
451         // all the test rects are inside the degenerate rrect
452         {
453             // rect
454             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
455             { 1, 1, 1, 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             { 1, 1, 1, 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             { 1, 1, 1, 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             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
462         },
463         // for the circle we expect 6 blocks to be out on the
464         // corners (then the rest in) and only the first block
465         // out on the vertical and horizontal axes (then
466         // the rest in)
467         {
468             // circle
469             { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
470             { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
471             { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
472             { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
473             { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
474             { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
475             { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
476             { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
477         },
478         // for the simple round rect we expect 3 out on
479         // the corners (then the rest in) and no blocks out
480         // on the vertical and horizontal axes
481         {
482             // simple RR
483             { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
484             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
485             { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
486             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
487             { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
488             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
489             { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
490             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
491         },
492         // for the complex case the answer is different for each direction
493         {
494             // complex RR
495             // all in for NW (rect) corner (same as rect case)
496             { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
497             // only first block out for N (same as circle case)
498             { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
499             // first 6 blocks out for NE (same as circle case)
500             { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
501             // only first block out for E (same as circle case)
502             { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
503             // first 3 blocks out for SE (same as simple case)
504             { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
505             // first two blocks out for S
506             { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
507             // first 9 blocks out for SW
508             { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
509             // first two blocks out for W (same as S)
510             { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
511          }
512     };
513 
514     for (int i = 0; i < kNumRRects; ++i) {
515         test_direction(reporter, rrects[i],     0,  1,     0,  1, kNumSteps, answers[i][0]); // NW
516         test_direction(reporter, rrects[i], 19.5f,  0,     0,  1, kNumSteps, answers[i][1]); // N
517         test_direction(reporter, rrects[i],    40, -1,     0,  1, kNumSteps, answers[i][2]); // NE
518         test_direction(reporter, rrects[i],    40, -1, 19.5f,  0, kNumSteps, answers[i][3]); // E
519         test_direction(reporter, rrects[i],    40, -1,    40, -1, kNumSteps, answers[i][4]); // SE
520         test_direction(reporter, rrects[i], 19.5f,  0,    40, -1, kNumSteps, answers[i][5]); // S
521         test_direction(reporter, rrects[i],     0,  1,    40, -1, kNumSteps, answers[i][6]); // SW
522         test_direction(reporter, rrects[i],     0,  1, 19.5f,  0, kNumSteps, answers[i][7]); // W
523     }
524 }
525 
526 // Called for a matrix that should cause SkRRect::transform to fail.
assert_transform_failure(skiatest::Reporter * reporter,const SkRRect & orig,const SkMatrix & matrix)527 static void assert_transform_failure(skiatest::Reporter* reporter, const SkRRect& orig,
528                                      const SkMatrix& matrix) {
529     // The test depends on the fact that the original is not empty.
530     SkASSERT(!orig.isEmpty());
531     SkRRect dst;
532     dst.setEmpty();
533 
534     const SkRRect copyOfDst = dst;
535     const SkRRect copyOfOrig = orig;
536     bool success = orig.transform(matrix, &dst);
537     // This transform should fail.
538     REPORTER_ASSERT(reporter, !success);
539     // Since the transform failed, dst should be unchanged.
540     REPORTER_ASSERT(reporter, copyOfDst == dst);
541     // original should not be modified.
542     REPORTER_ASSERT(reporter, copyOfOrig == orig);
543     REPORTER_ASSERT(reporter, orig != dst);
544 }
545 
546 #define GET_RADII                                                       \
547     const SkVector& origUL = orig.radii(SkRRect::kUpperLeft_Corner);    \
548     const SkVector& origUR = orig.radii(SkRRect::kUpperRight_Corner);   \
549     const SkVector& origLR = orig.radii(SkRRect::kLowerRight_Corner);   \
550     const SkVector& origLL = orig.radii(SkRRect::kLowerLeft_Corner);    \
551     const SkVector& dstUL = dst.radii(SkRRect::kUpperLeft_Corner);      \
552     const SkVector& dstUR = dst.radii(SkRRect::kUpperRight_Corner);     \
553     const SkVector& dstLR = dst.radii(SkRRect::kLowerRight_Corner);     \
554     const SkVector& dstLL = dst.radii(SkRRect::kLowerLeft_Corner)
555 
556 // Called to test various transforms on a single SkRRect.
test_transform_helper(skiatest::Reporter * reporter,const SkRRect & orig)557 static void test_transform_helper(skiatest::Reporter* reporter, const SkRRect& orig) {
558     SkRRect dst;
559     dst.setEmpty();
560 
561     // The identity matrix will duplicate the rrect.
562     bool success = orig.transform(SkMatrix::I(), &dst);
563     REPORTER_ASSERT(reporter, success);
564     REPORTER_ASSERT(reporter, orig == dst);
565 
566     // Skew and Perspective make transform fail.
567     SkMatrix matrix;
568     matrix.reset();
569     matrix.setSkewX(SkIntToScalar(2));
570     assert_transform_failure(reporter, orig, matrix);
571 
572     matrix.reset();
573     matrix.setSkewY(SkIntToScalar(3));
574     assert_transform_failure(reporter, orig, matrix);
575 
576     matrix.reset();
577     matrix.setPerspX(4);
578     assert_transform_failure(reporter, orig, matrix);
579 
580     matrix.reset();
581     matrix.setPerspY(5);
582     assert_transform_failure(reporter, orig, matrix);
583 
584     // Rotation fails.
585     matrix.reset();
586     matrix.setRotate(SkIntToScalar(37));
587     assert_transform_failure(reporter, orig, matrix);
588 
589     // Translate will keep the rect moved, but otherwise the same.
590     matrix.reset();
591     SkScalar translateX = SkIntToScalar(32);
592     SkScalar translateY = SkIntToScalar(15);
593     matrix.setTranslateX(translateX);
594     matrix.setTranslateY(translateY);
595     dst.setEmpty();
596     success = orig.transform(matrix, &dst);
597     REPORTER_ASSERT(reporter, success);
598     for (int i = 0; i < 4; ++i) {
599         REPORTER_ASSERT(reporter,
600                 orig.radii((SkRRect::Corner) i) == dst.radii((SkRRect::Corner) i));
601     }
602     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
603     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
604     REPORTER_ASSERT(reporter, dst.rect().left() == orig.rect().left() + translateX);
605     REPORTER_ASSERT(reporter, dst.rect().top() == orig.rect().top() + translateY);
606 
607     // Keeping the translation, but adding skew will make transform fail.
608     matrix.setSkewY(SkIntToScalar(7));
609     assert_transform_failure(reporter, orig, matrix);
610 
611     // Scaling in -x will flip the round rect horizontally.
612     matrix.reset();
613     matrix.setScaleX(SkIntToScalar(-1));
614     dst.setEmpty();
615     success = orig.transform(matrix, &dst);
616     REPORTER_ASSERT(reporter, success);
617     {
618         GET_RADII;
619         // Radii have swapped in x.
620         REPORTER_ASSERT(reporter, origUL == dstUR);
621         REPORTER_ASSERT(reporter, origUR == dstUL);
622         REPORTER_ASSERT(reporter, origLR == dstLL);
623         REPORTER_ASSERT(reporter, origLL == dstLR);
624     }
625     // Width and height remain the same.
626     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
627     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
628     // Right and left have swapped (sort of)
629     REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
630     // Top has stayed the same.
631     REPORTER_ASSERT(reporter, orig.rect().top() == dst.rect().top());
632 
633     // Keeping the scale, but adding a persp will make transform fail.
634     matrix.setPerspX(7);
635     assert_transform_failure(reporter, orig, matrix);
636 
637     // Scaling in -y will flip the round rect vertically.
638     matrix.reset();
639     matrix.setScaleY(SkIntToScalar(-1));
640     dst.setEmpty();
641     success = orig.transform(matrix, &dst);
642     REPORTER_ASSERT(reporter, success);
643     {
644         GET_RADII;
645         // Radii have swapped in y.
646         REPORTER_ASSERT(reporter, origUL == dstLL);
647         REPORTER_ASSERT(reporter, origUR == dstLR);
648         REPORTER_ASSERT(reporter, origLR == dstUR);
649         REPORTER_ASSERT(reporter, origLL == dstUL);
650     }
651     // Width and height remain the same.
652     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
653     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
654     // Top and bottom have swapped (sort of)
655     REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
656     // Left has stayed the same.
657     REPORTER_ASSERT(reporter, orig.rect().left() == dst.rect().left());
658 
659     // Scaling in -x and -y will swap in both directions.
660     matrix.reset();
661     matrix.setScaleY(SkIntToScalar(-1));
662     matrix.setScaleX(SkIntToScalar(-1));
663     dst.setEmpty();
664     success = orig.transform(matrix, &dst);
665     REPORTER_ASSERT(reporter, success);
666     {
667         GET_RADII;
668         REPORTER_ASSERT(reporter, origUL == dstLR);
669         REPORTER_ASSERT(reporter, origUR == dstLL);
670         REPORTER_ASSERT(reporter, origLR == dstUL);
671         REPORTER_ASSERT(reporter, origLL == dstUR);
672     }
673     // Width and height remain the same.
674     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
675     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
676     REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
677     REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
678 
679     // Scale in both directions.
680     SkScalar xScale = SkIntToScalar(3);
681     SkScalar yScale = 3.2f;
682     matrix.reset();
683     matrix.setScaleX(xScale);
684     matrix.setScaleY(yScale);
685     dst.setEmpty();
686     success = orig.transform(matrix, &dst);
687     REPORTER_ASSERT(reporter, success);
688     // Radii are scaled.
689     for (int i = 0; i < 4; ++i) {
690         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX,
691                                     orig.radii((SkRRect::Corner) i).fX * xScale));
692         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY,
693                                     orig.radii((SkRRect::Corner) i).fY * yScale));
694     }
695     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(),
696                                                   orig.rect().width() * xScale));
697     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(),
698                                                   orig.rect().height() * yScale));
699     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(),
700                                                   orig.rect().left() * xScale));
701     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(),
702                                                   orig.rect().top() * yScale));
703 
704 
705     //  a-----b            d-----a
706     //  |     |     ->     |     |
707     //  |     |  Rotate 90 |     |
708     //  d-----c            c-----b
709     matrix.reset();
710     matrix.setRotate(SkIntToScalar(90));
711     dst.setEmpty();
712     success = orig.transform(matrix, &dst);
713     REPORTER_ASSERT(reporter, success);
714     {
715         GET_RADII;
716         // Radii have cycled clockwise and swapped their x and y axis.
717         REPORTER_ASSERT(reporter, dstUL.x() == origLL.y());
718         REPORTER_ASSERT(reporter, dstUL.y() == origLL.x());
719         REPORTER_ASSERT(reporter, dstUR.x() == origUL.y());
720         REPORTER_ASSERT(reporter, dstUR.y() == origUL.x());
721         REPORTER_ASSERT(reporter, dstLR.x() == origUR.y());
722         REPORTER_ASSERT(reporter, dstLR.y() == origUR.x());
723         REPORTER_ASSERT(reporter, dstLL.x() == origLR.y());
724         REPORTER_ASSERT(reporter, dstLL.y() == origLR.x());
725     }
726     // Width and height would get swapped.
727     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().height());
728     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().width());
729 
730     //  a-----b        b-----a           c-----b
731     //  |     |   ->   |     |    ->     |     |
732     //  |     | Flip X |     | Rotate 90 |     |
733     //  d-----c        c-----d           d-----a
734     matrix.reset();
735     matrix.setRotate(SkIntToScalar(90));
736     matrix.postScale(SkIntToScalar(-1), SkIntToScalar(1));
737     dst.setEmpty();
738     success = orig.transform(matrix, &dst);
739     REPORTER_ASSERT(reporter, success);
740     {
741         GET_RADII;
742         REPORTER_ASSERT(reporter, dstUL.x() == origLR.y());
743         REPORTER_ASSERT(reporter, dstUL.y() == origLR.x());
744         REPORTER_ASSERT(reporter, dstUR.x() == origUR.y());
745         REPORTER_ASSERT(reporter, dstUR.y() == origUR.x());
746         REPORTER_ASSERT(reporter, dstLR.x() == origUL.y());
747         REPORTER_ASSERT(reporter, dstLR.y() == origUL.x());
748         REPORTER_ASSERT(reporter, dstLL.x() == origLL.y());
749         REPORTER_ASSERT(reporter, dstLL.y() == origLL.x());
750     }
751     // Width and height would get swapped.
752     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().height());
753     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().width());
754 
755     //  a-----b           d-----a        c-----b
756     //  |     |    ->     |     |   ->   |     |
757     //  |     | Rotate 90 |     | Flip Y |     |
758     //  d-----c           c-----b        d-----a
759     //
760     // This is the same as Flip X and Rotate 90.
761     matrix.reset();
762     matrix.setScale(SkIntToScalar(1), SkIntToScalar(-1));
763     matrix.postRotate(SkIntToScalar(90));
764     SkRRect dst2;
765     dst2.setEmpty();
766     success = orig.transform(matrix, &dst2);
767     REPORTER_ASSERT(reporter, success);
768     REPORTER_ASSERT(reporter, dst == dst2);
769 
770     //  a-----b            b-----c        c-----b
771     //  |     |     ->     |     |   ->   |     |
772     //  |     | Rotate 270 |     | Flip X |     |
773     //  d-----c            a-----d        d-----a
774     matrix.reset();
775     matrix.setScale(SkIntToScalar(-1), SkIntToScalar(1));
776     matrix.postRotate(SkIntToScalar(270));
777     dst2.setEmpty();
778     success = orig.transform(matrix, &dst2);
779     REPORTER_ASSERT(reporter, success);
780     REPORTER_ASSERT(reporter, dst == dst2);
781 
782     //  a-----b        d-----c            c-----b
783     //  |     |   ->   |     |     ->     |     |
784     //  |     | Flip Y |     | Rotate 270 |     |
785     //  d-----c        a-----b            d-----a
786     matrix.reset();
787     matrix.setRotate(SkIntToScalar(270));
788     matrix.postScale(SkIntToScalar(1), SkIntToScalar(-1));
789     dst2.setEmpty();
790     success = orig.transform(matrix, &dst2);
791     REPORTER_ASSERT(reporter, success);
792     REPORTER_ASSERT(reporter, dst == dst2);
793 
794     //  a-----b           d-----a        a-----d
795     //  |     |    ->     |     |   ->   |     |
796     //  |     | Rotate 90 |     | Flip X |     |
797     //  d-----c           c-----b        b-----c
798     matrix.reset();
799     matrix.setScale(SkIntToScalar(-1), SkIntToScalar(1));
800     matrix.postRotate(SkIntToScalar(90));
801     dst.setEmpty();
802     success = orig.transform(matrix, &dst);
803     REPORTER_ASSERT(reporter, success);
804     {
805         GET_RADII;
806         REPORTER_ASSERT(reporter, dstUL.x() == origUL.y());
807         REPORTER_ASSERT(reporter, dstUL.y() == origUL.x());
808         REPORTER_ASSERT(reporter, dstUR.x() == origLL.y());
809         REPORTER_ASSERT(reporter, dstUR.y() == origLL.x());
810         REPORTER_ASSERT(reporter, dstLR.x() == origLR.y());
811         REPORTER_ASSERT(reporter, dstLR.y() == origLR.x());
812         REPORTER_ASSERT(reporter, dstLL.x() == origUR.y());
813         REPORTER_ASSERT(reporter, dstLL.y() == origUR.x());
814     }
815     // Width and height would get swapped.
816     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().height());
817     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().width());
818 
819     //  a-----b        d-----c           a-----d
820     //  |     |   ->   |     |    ->     |     |
821     //  |     | Flip Y |     | Rotate 90 |     |
822     //  d-----c        a-----b           b-----c
823     // This is the same as rotate 90 and flip x.
824     matrix.reset();
825     matrix.setRotate(SkIntToScalar(90));
826     matrix.postScale(SkIntToScalar(1), SkIntToScalar(-1));
827     dst2.setEmpty();
828     success = orig.transform(matrix, &dst2);
829     REPORTER_ASSERT(reporter, success);
830     REPORTER_ASSERT(reporter, dst == dst2);
831 
832     //  a-----b        b-----a            a-----d
833     //  |     |   ->   |     |     ->     |     |
834     //  |     | Flip X |     | Rotate 270 |     |
835     //  d-----c        c-----d            b-----c
836     matrix.reset();
837     matrix.setRotate(SkIntToScalar(270));
838     matrix.postScale(SkIntToScalar(-1), SkIntToScalar(1));
839     dst2.setEmpty();
840     success = orig.transform(matrix, &dst2);
841     REPORTER_ASSERT(reporter, success);
842     REPORTER_ASSERT(reporter, dst == dst2);
843 
844     //  a-----b            b-----c        a-----d
845     //  |     |     ->     |     |   ->   |     |
846     //  |     | Rotate 270 |     | Flip Y |     |
847     //  d-----c            a-----d        b-----c
848     matrix.reset();
849     matrix.setScale(SkIntToScalar(1), SkIntToScalar(-1));
850     matrix.postRotate(SkIntToScalar(270));
851     dst2.setEmpty();
852     success = orig.transform(matrix, &dst2);
853     REPORTER_ASSERT(reporter, success);
854     REPORTER_ASSERT(reporter, dst == dst2);
855 
856 
857     //  a-----b        b-----a        c-----d            b-----c
858     //  |     |   ->   |     |   ->   |     |    ->      |     |
859     //  |     | Flip X |     | Flip Y |     | Rotate 90  |     |
860     //  d-----c        c-----d        b-----a            a-----d
861     //
862     // This is the same as rotation by 270.
863     matrix.reset();
864     matrix.setRotate(SkIntToScalar(90));
865     matrix.postScale(SkIntToScalar(-1), SkIntToScalar(-1));
866     dst.setEmpty();
867     success = orig.transform(matrix, &dst);
868     REPORTER_ASSERT(reporter, success);
869     {
870         GET_RADII;
871         // Radii have cycled clockwise and swapped their x and y axis.
872         REPORTER_ASSERT(reporter, dstUL.x() == origUR.y());
873         REPORTER_ASSERT(reporter, dstUL.y() == origUR.x());
874         REPORTER_ASSERT(reporter, dstUR.x() == origLR.y());
875         REPORTER_ASSERT(reporter, dstUR.y() == origLR.x());
876         REPORTER_ASSERT(reporter, dstLR.x() == origLL.y());
877         REPORTER_ASSERT(reporter, dstLR.y() == origLL.x());
878         REPORTER_ASSERT(reporter, dstLL.x() == origUL.y());
879         REPORTER_ASSERT(reporter, dstLL.y() == origUL.x());
880     }
881     // Width and height would get swapped.
882     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().height());
883     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().width());
884 
885     //  a-----b             b-----c
886     //  |     |     ->      |     |
887     //  |     | Rotate 270  |     |
888     //  d-----c             a-----d
889     //
890     dst2.setEmpty();
891     matrix.reset();
892     matrix.setRotate(SkIntToScalar(270));
893     success = orig.transform(matrix, &dst2);
894     REPORTER_ASSERT(reporter, success);
895     REPORTER_ASSERT(reporter, dst == dst2);
896 
897     //  a-----b        b-----a        c-----d             d-----a
898     //  |     |   ->   |     |   ->   |     |     ->      |     |
899     //  |     | Flip X |     | Flip Y |     | Rotate 270  |     |
900     //  d-----c        c-----d        b-----a             c-----b
901     //
902     // This is the same as rotation by 90 degrees.
903     matrix.reset();
904     matrix.setRotate(SkIntToScalar(270));
905     matrix.postScale(SkIntToScalar(-1), SkIntToScalar(-1));
906     dst.setEmpty();
907     success = orig.transform(matrix, &dst);
908     REPORTER_ASSERT(reporter, success);
909 
910     matrix.reset();
911     matrix.setRotate(SkIntToScalar(90));
912     dst2.setEmpty();
913     success = orig.transform(matrix, &dst2);
914     REPORTER_ASSERT(reporter, dst == dst2);
915 
916 }
917 
test_round_rect_transform(skiatest::Reporter * reporter)918 static void test_round_rect_transform(skiatest::Reporter* reporter) {
919     SkRRect rrect;
920     {
921         SkRect r = { 0, 0, kWidth, kHeight };
922         rrect.setRectXY(r, SkIntToScalar(4), SkIntToScalar(7));
923         test_transform_helper(reporter, rrect);
924     }
925     {
926         SkRect r = { SkIntToScalar(5), SkIntToScalar(15),
927                      SkIntToScalar(27), SkIntToScalar(34) };
928         SkVector radii[4] = { { 0, SkIntToScalar(1) },
929                               { SkIntToScalar(2), SkIntToScalar(3) },
930                               { SkIntToScalar(4), SkIntToScalar(5) },
931                               { SkIntToScalar(6), SkIntToScalar(7) } };
932         rrect.setRectRadii(r, radii);
933         test_transform_helper(reporter, rrect);
934     }
935 }
936 
937 // Test out the case where an oval already off in space is translated/scaled
938 // further off into space - yielding numerical issues when the rect & radii
939 // are transformed separatly
940 // BUG=skia:2696
test_issue_2696(skiatest::Reporter * reporter)941 static void test_issue_2696(skiatest::Reporter* reporter) {
942     SkRRect rrect;
943     SkRect r = { 28443.8594f, 53.1428604f, 28446.7148f, 56.0000038f };
944     rrect.setOval(r);
945 
946     SkMatrix xform;
947     xform.setAll(2.44f,  0.0f, 485411.7f,
948                  0.0f,  2.44f,   -438.7f,
949                  0.0f,   0.0f,      1.0f);
950     SkRRect dst;
951 
952     bool success = rrect.transform(xform, &dst);
953     REPORTER_ASSERT(reporter, success);
954 
955     SkScalar halfWidth = SkScalarHalf(dst.width());
956     SkScalar halfHeight = SkScalarHalf(dst.height());
957 
958     for (int i = 0; i < 4; ++i) {
959         REPORTER_ASSERT(reporter,
960                         SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth));
961         REPORTER_ASSERT(reporter,
962                         SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight));
963     }
964 }
965 
test_read_rrect(skiatest::Reporter * reporter,const SkRRect & rrect,bool shouldEqualSrc)966 void test_read_rrect(skiatest::Reporter* reporter, const SkRRect& rrect, bool shouldEqualSrc) {
967     // It would be cleaner to call rrect.writeToMemory into a buffer. However, writeToMemory asserts
968     // that the rrect is valid and our caller may have fiddled with the internals of rrect to make
969     // it invalid.
970     const void* buffer = reinterpret_cast<const void*>(&rrect);
971     SkRRect deserialized;
972     size_t size = deserialized.readFromMemory(buffer, sizeof(SkRRect));
973     REPORTER_ASSERT(reporter, size == SkRRect::kSizeInMemory);
974     REPORTER_ASSERT(reporter, deserialized.isValid());
975     if (shouldEqualSrc) {
976        REPORTER_ASSERT(reporter, rrect == deserialized);
977     }
978 }
979 
test_read(skiatest::Reporter * reporter)980 static void test_read(skiatest::Reporter* reporter) {
981     static const SkRect kRect = {10.f, 10.f, 20.f, 20.f};
982     static const SkRect kNaNRect = {10.f, 10.f, 20.f, SK_ScalarNaN};
983     static const SkRect kInfRect = {10.f, 10.f, SK_ScalarInfinity, 20.f};
984     SkRRect rrect;
985 
986     test_read_rrect(reporter, SkRRect::MakeEmpty(), true);
987     test_read_rrect(reporter, SkRRect::MakeRect(kRect), true);
988     // These get coerced to empty.
989     test_read_rrect(reporter, SkRRect::MakeRect(kInfRect), true);
990     test_read_rrect(reporter, SkRRect::MakeRect(kNaNRect), true);
991 
992     rrect.setRect(kRect);
993     SkRect* innerRect = reinterpret_cast<SkRect*>(&rrect);
994     SkASSERT(*innerRect == kRect);
995     *innerRect = kInfRect;
996     test_read_rrect(reporter, rrect, false);
997     *innerRect = kNaNRect;
998     test_read_rrect(reporter, rrect, false);
999 
1000     test_read_rrect(reporter, SkRRect::MakeOval(kRect), true);
1001     test_read_rrect(reporter, SkRRect::MakeOval(kInfRect), true);
1002     test_read_rrect(reporter, SkRRect::MakeOval(kNaNRect), true);
1003     rrect.setOval(kRect);
1004     *innerRect = kInfRect;
1005     test_read_rrect(reporter, rrect, false);
1006     *innerRect = kNaNRect;
1007     test_read_rrect(reporter, rrect, false);
1008 
1009     test_read_rrect(reporter, SkRRect::MakeRectXY(kRect, 5.f, 5.f), true);
1010     // rrect should scale down the radii to make this legal
1011     test_read_rrect(reporter, SkRRect::MakeRectXY(kRect, 5.f, 400.f), true);
1012 
1013     static const SkVector kRadii[4] = {{0.5f, 1.f}, {1.5f, 2.f}, {2.5f, 3.f}, {3.5f, 4.f}};
1014     rrect.setRectRadii(kRect, kRadii);
1015     test_read_rrect(reporter, rrect, true);
1016     SkScalar* innerRadius = reinterpret_cast<SkScalar*>(&rrect) + 6;
1017     SkASSERT(*innerRadius == 1.5f);
1018     *innerRadius = 400.f;
1019     test_read_rrect(reporter, rrect, false);
1020     *innerRadius = SK_ScalarInfinity;
1021     test_read_rrect(reporter, rrect, false);
1022     *innerRadius = SK_ScalarNaN;
1023     test_read_rrect(reporter, rrect, false);
1024     *innerRadius = -10.f;
1025     test_read_rrect(reporter, rrect, false);
1026 }
1027 
test_inner_bounds(skiatest::Reporter * reporter)1028 static void test_inner_bounds(skiatest::Reporter* reporter) {
1029     // Because InnerBounds() insets the computed bounds slightly to correct for numerical inaccuracy
1030     // when finding the maximum inscribed point on a curve, we use a larger epsilon for comparing
1031     // expected areas.
1032     static constexpr SkScalar kEpsilon = 0.005f;
1033 
1034     // Test that an empty rrect reports empty inner bounds
1035     REPORTER_ASSERT(reporter, SkRRectPriv::InnerBounds(SkRRect::MakeEmpty()).isEmpty());
1036     // Test that a rect rrect reports itself as the inner bounds
1037     SkRect r = SkRect::MakeLTRB(0, 1, 2, 3);
1038     REPORTER_ASSERT(reporter, SkRRectPriv::InnerBounds(SkRRect::MakeRect(r)) == r);
1039     // Test that a circle rrect has an inner bounds area equal to 2*radius^2
1040     float radius = 5.f;
1041     SkRect inner = SkRRectPriv::InnerBounds(SkRRect::MakeOval(SkRect::MakeWH(2.f * radius,
1042                                                                              2.f * radius)));
1043     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(inner.width() * inner.height(),
1044                                                   2.f * radius * radius, kEpsilon));
1045 
1046     float width = 20.f;
1047     float height = 25.f;
1048     r = SkRect::MakeWH(width, height);
1049     // Test that a rrect with circular corners has an area equal to:
1050     float expectedArea =
1051             (2.f * radius * radius) +                      // area in the 4 circular corners
1052             (width-2.f*radius) * (height-2.f*radius) +     // inner area excluding corners and edges
1053             SK_ScalarSqrt2 * radius * (width-2.f*radius) + // two horiz. rects between corners
1054             SK_ScalarSqrt2 * radius * (height-2.f*radius); // two vert. rects between corners
1055 
1056     inner = SkRRectPriv::InnerBounds(SkRRect::MakeRectXY(r, radius, radius));
1057     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(inner.width() * inner.height(),
1058                                                   expectedArea, kEpsilon));
1059 
1060     // Test that a rrect with a small y radius but large x radius selects the horizontal interior
1061     SkRRect rr = SkRRect::MakeRectXY(r, 2.f * radius, 0.1f * radius);
1062     REPORTER_ASSERT(reporter, SkRRectPriv::InnerBounds(rr) ==
1063                               SkRect::MakeLTRB(0.f, 0.1f * radius, width, height - 0.1f * radius));
1064     // And vice versa with large y and small x radii
1065     rr = SkRRect::MakeRectXY(r, 0.1f * radius, 2.f * radius);
1066     REPORTER_ASSERT(reporter, SkRRectPriv::InnerBounds(rr) ==
1067                               SkRect::MakeLTRB(0.1f * radius, 0.f, width - 0.1f * radius, height));
1068 
1069     // Test a variety of complex round rects produce a non-empty rect that is at least contained,
1070     // and larger than the inner area avoiding all corners.
1071     SkRandom rng;
1072     for (int i = 0; i < 1000; ++i) {
1073         float maxRadiusX = rng.nextRangeF(0.f, 40.f);
1074         float maxRadiusY = rng.nextRangeF(0.f, 40.f);
1075 
1076         float innerWidth = rng.nextRangeF(0.f, 40.f);
1077         float innerHeight = rng.nextRangeF(0.f, 40.f);
1078 
1079         SkVector radii[4] = {{rng.nextRangeF(0.f, maxRadiusX), rng.nextRangeF(0.f, maxRadiusY)},
1080                              {rng.nextRangeF(0.f, maxRadiusX), rng.nextRangeF(0.f, maxRadiusY)},
1081                              {rng.nextRangeF(0.f, maxRadiusX), rng.nextRangeF(0.f, maxRadiusY)},
1082                              {rng.nextRangeF(0.f, maxRadiusX), rng.nextRangeF(0.f, maxRadiusY)}};
1083 
1084         float maxLeft   = std::max(radii[0].fX, radii[3].fX);
1085         float maxTop    = std::max(radii[0].fY, radii[1].fY);
1086         float maxRight  = std::max(radii[1].fX, radii[2].fX);
1087         float maxBottom = std::max(radii[2].fY, radii[3].fY);
1088 
1089         SkRect outer = SkRect::MakeWH(maxLeft + maxRight + innerWidth,
1090                                       maxTop + maxBottom + innerHeight);
1091         rr.setRectRadii(outer, radii);
1092 
1093         SkRect maxInner = SkRRectPriv::InnerBounds(rr);
1094         // Test upper limit on the size of 'maxInner'
1095         REPORTER_ASSERT(reporter, outer.contains(maxInner));
1096         REPORTER_ASSERT(reporter, rr.contains(maxInner));
1097 
1098         // Test lower limit on the size of 'maxInner'
1099         inner = SkRect::MakeXYWH(maxLeft, maxTop, innerWidth, innerHeight);
1100         inner.inset(kEpsilon, kEpsilon);
1101 
1102         if (inner.isSorted()) {
1103             REPORTER_ASSERT(reporter, maxInner.contains(inner));
1104         } else {
1105             // Flipped from the inset, just test two points of inner
1106             float midX = maxLeft + 0.5f * innerWidth;
1107             float midY = maxTop + 0.5f * innerHeight;
1108             REPORTER_ASSERT(reporter, maxInner.contains(midX, maxTop));
1109             REPORTER_ASSERT(reporter, maxInner.contains(midX, maxTop + innerHeight));
1110             REPORTER_ASSERT(reporter, maxInner.contains(maxLeft, midY));
1111             REPORTER_ASSERT(reporter, maxInner.contains(maxLeft + innerWidth, midY));
1112         }
1113     }
1114 }
1115 
1116 namespace {
1117     // Helper to test expected intersection, relying on the fact that all round rect intersections
1118     // will have their bounds equal to the intersection of the bounds of the input round rects, and
1119     // their corner radii will be a one of A's, B's, or rectangular.
1120     enum CornerChoice : uint8_t {
1121         kA, kB, kRect
1122     };
1123 
verify_success(skiatest::Reporter * reporter,const SkRRect & a,const SkRRect & b,CornerChoice tl,CornerChoice tr,CornerChoice br,CornerChoice bl)1124     static void verify_success(skiatest::Reporter* reporter, const SkRRect& a, const SkRRect& b,
1125                                CornerChoice tl, CornerChoice tr, CornerChoice br, CornerChoice bl) {
1126         static const SkRRect kRect = SkRRect::MakeEmpty(); // has (0,0) for all corners
1127 
1128         // Compute expected round rect intersection given bounds of A and B, and the specified
1129         // corner choices for the 4 corners.
1130         SkRect expectedBounds;
1131         SkAssertResult(expectedBounds.intersect(a.rect(), b.rect()));
1132 
1133         SkVector radii[4] = {
1134             (tl == kA ? a : (tl == kB ? b : kRect)).radii(SkRRect::kUpperLeft_Corner),
1135             (tr == kA ? a : (tr == kB ? b : kRect)).radii(SkRRect::kUpperRight_Corner),
1136             (br == kA ? a : (br == kB ? b : kRect)).radii(SkRRect::kLowerRight_Corner),
1137             (bl == kA ? a : (bl == kB ? b : kRect)).radii(SkRRect::kLowerLeft_Corner)
1138         };
1139         SkRRect expected;
1140         expected.setRectRadii(expectedBounds, radii);
1141 
1142         SkRRect actual = SkRRectPriv::ConservativeIntersect(a, b);
1143         // Intersections are commutative so ba and ab should be the same
1144         REPORTER_ASSERT(reporter, actual == SkRRectPriv::ConservativeIntersect(b, a));
1145 
1146         // Intersection of the result with either A or B should remain the intersection
1147         REPORTER_ASSERT(reporter, actual == SkRRectPriv::ConservativeIntersect(actual, a));
1148         REPORTER_ASSERT(reporter, actual == SkRRectPriv::ConservativeIntersect(actual, b));
1149 
1150         // Bounds of intersection round rect should equal intersection of bounds of a and b
1151         REPORTER_ASSERT(reporter, actual.rect() == expectedBounds);
1152 
1153         // Use PathOps to confirm that the explicit round rect is correct.
1154         SkPath aPath, bPath, expectedPath;
1155         aPath.addRRect(a);
1156         bPath.addRRect(b);
1157         SkAssertResult(Op(aPath, bPath, kIntersect_SkPathOp, &expectedPath));
1158 
1159         // The isRRect() heuristics in SkPath are based on having called addRRect(), so a path from
1160         // path ops that is a rounded rectangle will return false. However, if test XOR expected is
1161         // empty, then we know that the shapes were the same.
1162         SkPath testPath;
1163         testPath.addRRect(actual);
1164 
1165         SkPath empty;
1166         SkAssertResult(Op(testPath, expectedPath, kXOR_SkPathOp, &empty));
1167         REPORTER_ASSERT(reporter, empty.isEmpty());
1168     }
1169 
verify_failure(skiatest::Reporter * reporter,const SkRRect & a,const SkRRect & b)1170     static void verify_failure(skiatest::Reporter* reporter, const SkRRect& a, const SkRRect& b) {
1171         SkRRect intersection = SkRRectPriv::ConservativeIntersect(a, b);
1172         // Expected the intersection to fail (no intersection or complex intersection is not
1173         // disambiguated).
1174         REPORTER_ASSERT(reporter, intersection.isEmpty());
1175         REPORTER_ASSERT(reporter, SkRRectPriv::ConservativeIntersect(b, a).isEmpty());
1176     }
1177 }  // namespace
1178 
test_conservative_intersection(skiatest::Reporter * reporter)1179 static void test_conservative_intersection(skiatest::Reporter* reporter) {
1180     // Helper to inline making an inset round rect
1181     auto make_inset = [](const SkRRect& r, float dx, float dy) {
1182         SkRRect i = r;
1183         i.inset(dx, dy);
1184         return i;
1185     };
1186 
1187     // A is a wide, short round rect
1188     SkRRect a = SkRRect::MakeRectXY({0.f, 4.f, 16.f, 12.f}, 2.f, 2.f);
1189     // B is a narrow, tall round rect
1190     SkRRect b = SkRRect::MakeRectXY({4.f, 0.f, 12.f, 16.f}, 3.f, 3.f);
1191     // NOTE: As positioned by default, A and B intersect as the rectangle {4, 4, 12, 12}.
1192     // There is a 2 px buffer between the corner curves of A and the vertical edges of B, and
1193     // a 1 px buffer between the corner curves of B and the horizontal edges of A. Since the shapes
1194     // form a symmetric rounded cross, we can easily test edge and corner combinations by simply
1195     // flipping signs and/or swapping x and y offsets.
1196 
1197     // Successful intersection operations:
1198     //  - for clarity these are formed by moving A around to intersect with B in different ways.
1199     //  - the expected bounds of the round rect intersection is calculated automatically
1200     //    in check_success, so all we have to specify are the expected corner radii
1201 
1202     // A and B intersect as a rectangle
1203     verify_success(reporter, a, b, kRect, kRect, kRect, kRect);
1204     // Move A to intersect B on a vertical edge, preserving two corners of A inside B
1205     verify_success(reporter, a.makeOffset(6.f, 0.f), b, kA, kRect, kRect, kA);
1206     verify_success(reporter, a.makeOffset(-6.f, 0.f), b, kRect, kA, kA, kRect);
1207     // Move B to intersect A on a horizontal edge, preserving two corners of B inside A
1208     verify_success(reporter, a, b.makeOffset(0.f, 6.f), kB, kB, kRect, kRect);
1209     verify_success(reporter, a, b.makeOffset(0.f, -6.f), kRect, kRect, kB, kB);
1210     // Move A to intersect B on a corner, preserving one corner of A and one of B
1211     verify_success(reporter, a.makeOffset(-7.f, -8.f), b, kB, kRect, kA, kRect); // TL of B
1212     verify_success(reporter, a.makeOffset(7.f, -8.f), b, kRect, kB, kRect, kA);  // TR of B
1213     verify_success(reporter, a.makeOffset(7.f, 8.f), b, kA, kRect, kB, kRect);   // BR of B
1214     verify_success(reporter, a.makeOffset(-7.f, 8.f), b, kRect, kA, kRect, kB);  // BL of B
1215     // An inset is contained inside the original (note that SkRRect::inset modifies radii too) so
1216     // is returned unmodified when intersected.
1217     verify_success(reporter, a, make_inset(a, 1.f, 1.f), kB, kB, kB, kB);
1218     verify_success(reporter, make_inset(b, 2.f, 2.f), b, kA, kA, kA, kA);
1219 
1220     // A rectangle exactly matching the corners of the rrect bounds keeps the rrect radii,
1221     // regardless of whether or not it's the 1st or 2nd arg to ConservativeIntersect.
1222     SkRRect c = SkRRect::MakeRectXY({0.f, 0.f, 10.f, 10.f}, 2.f, 2.f);
1223     SkRRect cT = SkRRect::MakeRect({0.f, 0.f, 10.f, 5.f});
1224     verify_success(reporter, c, cT, kA, kA, kRect, kRect);
1225     verify_success(reporter, cT, c, kB, kB, kRect, kRect);
1226     SkRRect cB = SkRRect::MakeRect({0.f, 5.f, 10.f, 10.});
1227     verify_success(reporter, c, cB, kRect, kRect, kA, kA);
1228     verify_success(reporter, cB, c, kRect, kRect, kB, kB);
1229     SkRRect cL = SkRRect::MakeRect({0.f, 0.f, 5.f, 10.f});
1230     verify_success(reporter, c, cL, kA, kRect, kRect, kA);
1231     verify_success(reporter, cL, c, kB, kRect, kRect, kB);
1232     SkRRect cR = SkRRect::MakeRect({5.f, 0.f, 10.f, 10.f});
1233     verify_success(reporter, c, cR, kRect, kA, kA, kRect);
1234     verify_success(reporter, cR, c, kRect, kB, kB, kRect);
1235 
1236     // Failed intersection operations:
1237 
1238     // A and B's bounds do not intersect
1239     verify_failure(reporter, a.makeOffset(32.f, 0.f), b);
1240     // A and B's bounds intersect, but corner curves do not -> no intersection
1241     verify_failure(reporter, a.makeOffset(11.5f, -11.5f), b);
1242     // A is empty -> no intersection
1243     verify_failure(reporter, SkRRect::MakeEmpty(), b);
1244     // A is contained in B, but is too close to the corner curves for the conservative
1245     // approximations to construct a valid round rect intersection.
1246     verify_failure(reporter, make_inset(b, 0.3f, 0.3f), b);
1247     // A intersects a straight edge, but not far enough for B to contain A's corners
1248     verify_failure(reporter, a.makeOffset(2.5f, 0.f), b);
1249     verify_failure(reporter, a.makeOffset(-2.5f, 0.f), b);
1250     // And vice versa for B into A
1251     verify_failure(reporter, a, b.makeOffset(0.f, 1.5f));
1252     verify_failure(reporter, a, b.makeOffset(0.f, -1.5f));
1253     // A intersects a straight edge and part of B's corner
1254     verify_failure(reporter, a.makeOffset(5.f, -2.f), b);
1255     verify_failure(reporter, a.makeOffset(-5.f, -2.f), b);
1256     verify_failure(reporter, a.makeOffset(5.f, 2.f), b);
1257     verify_failure(reporter, a.makeOffset(-5.f, 2.f), b);
1258     // And vice versa
1259     verify_failure(reporter, a, b.makeOffset(3.f, -5.f));
1260     verify_failure(reporter, a, b.makeOffset(-3.f, -5.f));
1261     verify_failure(reporter, a, b.makeOffset(3.f, 5.f));
1262     verify_failure(reporter, a, b.makeOffset(-3.f, 5.f));
1263     // A intersects B on a corner, but the corner curves overlap each other
1264     verify_failure(reporter, a.makeOffset(8.f, 10.f), b);
1265     verify_failure(reporter, a.makeOffset(-8.f, 10.f), b);
1266     verify_failure(reporter, a.makeOffset(8.f, -10.f), b);
1267     verify_failure(reporter, a.makeOffset(-8.f, -10.f), b);
1268 
1269     // Another variant of corners overlapping, this is two circles of radius r that overlap by r
1270     // pixels (e.g. the leftmost point of the right circle touches the center of the left circle).
1271     // The key difference with the above case is that the intersection of the circle bounds have
1272     // corners that are contained in both circles, but because it is only r wide, can not satisfy
1273     // all corners having radii = r.
1274     float r = 100.f;
1275     a = SkRRect::MakeOval(SkRect::MakeWH(2*r, 2*r));
1276     verify_failure(reporter, a, a.makeOffset(r, 0.f));
1277 }
1278 
DEF_TEST(RoundRect,reporter)1279 DEF_TEST(RoundRect, reporter) {
1280     test_round_rect_basic(reporter);
1281     test_round_rect_rects(reporter);
1282     test_round_rect_ovals(reporter);
1283     test_round_rect_general(reporter);
1284     test_round_rect_iffy_parameters(reporter);
1285     test_inset(reporter);
1286     test_round_rect_contains_rect(reporter);
1287     test_round_rect_transform(reporter);
1288     test_issue_2696(reporter);
1289     test_tricky_radii(reporter);
1290     test_empty_crbug_458524(reporter);
1291     test_empty(reporter);
1292     test_read(reporter);
1293     test_inner_bounds(reporter);
1294     test_conservative_intersection(reporter);
1295 }
1296 
DEF_TEST(RRect_fuzzer_regressions,r)1297 DEF_TEST(RRect_fuzzer_regressions, r) {
1298     {
1299         unsigned char buf[] = {
1300             0x0a, 0x00, 0x00, 0xff, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f,
1301             0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
1302             0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
1303             0x7f, 0x7f, 0x7f, 0x02, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x02, 0x00
1304         };
1305         REPORTER_ASSERT(r, sizeof(buf) == SkRRect{}.readFromMemory(buf, sizeof(buf)));
1306     }
1307 
1308     {
1309         unsigned char buf[] = {
1310             0x5d, 0xff, 0xff, 0x5d, 0x0a, 0x60, 0x0a, 0x0a, 0x0a, 0x7e, 0x0a, 0x5a,
1311             0x0a, 0x12, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
1312             0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x00, 0x00, 0x00, 0x0a,
1313             0x0a, 0x0a, 0x0a, 0x26, 0x0a, 0x0a, 0x0a, 0x0a, 0xff, 0xff, 0x0a, 0x0a
1314         };
1315         REPORTER_ASSERT(r, sizeof(buf) == SkRRect{}.readFromMemory(buf, sizeof(buf)));
1316     }
1317 
1318     {
1319         unsigned char buf[] = {
1320             0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x04, 0xdd, 0xdd, 0x15,
1321             0xfe, 0x00, 0x00, 0x04, 0x05, 0x7e, 0x00, 0x00, 0x00, 0xff, 0x08, 0x04,
1322             0xff, 0xff, 0xfe, 0xfe, 0xff, 0x32, 0x32, 0x32, 0x32, 0x00, 0x32, 0x32,
1323             0x04, 0xdd, 0x3d, 0x1c, 0xfe, 0x89, 0x04, 0x0a, 0x0e, 0x05, 0x7e, 0x0a
1324         };
1325         REPORTER_ASSERT(r, sizeof(buf) == SkRRect{}.readFromMemory(buf, sizeof(buf)));
1326     }
1327 }
1328