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