xref: /aosp_15_r20/external/skia/tests/RoundRectTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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     // Test out possible floating point issues w/ the radii transform
638     matrix = SkMatrix::Scale(0.999999f, 0.999999f);
639     dst.setEmpty();
640     success = orig.transform(matrix, &dst);
641     REPORTER_ASSERT(reporter, success);
642 
643     // Scaling in -y will flip the round rect vertically.
644     matrix.reset();
645     matrix.setScaleY(SkIntToScalar(-1));
646     dst.setEmpty();
647     success = orig.transform(matrix, &dst);
648     REPORTER_ASSERT(reporter, success);
649     {
650         GET_RADII;
651         // Radii have swapped in y.
652         REPORTER_ASSERT(reporter, origUL == dstLL);
653         REPORTER_ASSERT(reporter, origUR == dstLR);
654         REPORTER_ASSERT(reporter, origLR == dstUR);
655         REPORTER_ASSERT(reporter, origLL == dstUL);
656     }
657     // Width and height remain the same.
658     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
659     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
660     // Top and bottom have swapped (sort of)
661     REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
662     // Left has stayed the same.
663     REPORTER_ASSERT(reporter, orig.rect().left() == dst.rect().left());
664 
665     // Scaling in -x and -y will swap in both directions.
666     matrix.reset();
667     matrix.setScaleY(SkIntToScalar(-1));
668     matrix.setScaleX(SkIntToScalar(-1));
669     dst.setEmpty();
670     success = orig.transform(matrix, &dst);
671     REPORTER_ASSERT(reporter, success);
672     {
673         GET_RADII;
674         REPORTER_ASSERT(reporter, origUL == dstLR);
675         REPORTER_ASSERT(reporter, origUR == dstLL);
676         REPORTER_ASSERT(reporter, origLR == dstUL);
677         REPORTER_ASSERT(reporter, origLL == dstUR);
678     }
679     // Width and height remain the same.
680     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
681     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
682     REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
683     REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
684 
685     // Scale in both directions.
686     SkScalar xScale = SkIntToScalar(3);
687     SkScalar yScale = 3.2f;
688     matrix.reset();
689     matrix.setScaleX(xScale);
690     matrix.setScaleY(yScale);
691     dst.setEmpty();
692     success = orig.transform(matrix, &dst);
693     REPORTER_ASSERT(reporter, success);
694     // Radii are scaled.
695     for (int i = 0; i < 4; ++i) {
696         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX,
697                                     orig.radii((SkRRect::Corner) i).fX * xScale));
698         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY,
699                                     orig.radii((SkRRect::Corner) i).fY * yScale));
700     }
701     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(),
702                                                   orig.rect().width() * xScale));
703     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(),
704                                                   orig.rect().height() * yScale));
705     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(),
706                                                   orig.rect().left() * xScale));
707     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(),
708                                                   orig.rect().top() * yScale));
709 
710 
711     //  a-----b            d-----a
712     //  |     |     ->     |     |
713     //  |     |  Rotate 90 |     |
714     //  d-----c            c-----b
715     matrix.reset();
716     matrix.setRotate(SkIntToScalar(90));
717     dst.setEmpty();
718     success = orig.transform(matrix, &dst);
719     REPORTER_ASSERT(reporter, success);
720     {
721         GET_RADII;
722         // Radii have cycled clockwise and swapped their x and y axis.
723         REPORTER_ASSERT(reporter, dstUL.x() == origLL.y());
724         REPORTER_ASSERT(reporter, dstUL.y() == origLL.x());
725         REPORTER_ASSERT(reporter, dstUR.x() == origUL.y());
726         REPORTER_ASSERT(reporter, dstUR.y() == origUL.x());
727         REPORTER_ASSERT(reporter, dstLR.x() == origUR.y());
728         REPORTER_ASSERT(reporter, dstLR.y() == origUR.x());
729         REPORTER_ASSERT(reporter, dstLL.x() == origLR.y());
730         REPORTER_ASSERT(reporter, dstLL.y() == origLR.x());
731     }
732     // Width and height would get swapped.
733     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().height());
734     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().width());
735 
736     //  a-----b           d-----a        a-----d
737     //  |     |    ->     |     |    ->  |     |
738     //  |     | Rotate 90 |     | Flip X |     |
739     //  d-----c           c-----b        b-----c
740     matrix.reset();
741     matrix.setRotate(SkIntToScalar(90));
742     matrix.postScale(SkIntToScalar(-1), SkIntToScalar(1));
743     dst.setEmpty();
744     success = orig.transform(matrix, &dst);
745     REPORTER_ASSERT(reporter, success);
746     {
747         GET_RADII;
748         REPORTER_ASSERT(reporter, dstUL.x() == origUL.y());
749         REPORTER_ASSERT(reporter, dstUL.y() == origUL.x());
750         REPORTER_ASSERT(reporter, dstUR.x() == origLL.y());
751         REPORTER_ASSERT(reporter, dstUR.y() == origLL.x());
752         REPORTER_ASSERT(reporter, dstLR.x() == origLR.y());
753         REPORTER_ASSERT(reporter, dstLR.y() == origLR.x());
754         REPORTER_ASSERT(reporter, dstLL.x() == origUR.y());
755         REPORTER_ASSERT(reporter, dstLL.y() == origUR.x());
756     }
757     // Width and height would get swapped.
758     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().height());
759     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().width());
760 
761     //  a-----b        d-----c           a-----d
762     //  |     |   ->   |     |     ->    |     |
763     //  |     | Flip Y |     | Rotate 90 |     |
764     //  d-----c        a-----b           b-----c
765     //
766     // This is the same as Rotate 90 and Flip X.
767     matrix.reset();
768     matrix.setScale(SkIntToScalar(1), SkIntToScalar(-1));
769     matrix.postRotate(SkIntToScalar(90));
770     SkRRect dst2;
771     dst2.setEmpty();
772     success = orig.transform(matrix, &dst2);
773     REPORTER_ASSERT(reporter, success);
774     REPORTER_ASSERT(reporter, dst == dst2);
775 
776     //  a-----b        b-----a            a-----d
777     //  |     |   ->   |     |    ->      |     |
778     //  |     | Flip X |     | Rotate 270 |     |
779     //  d-----c        c-----d            b-----c
780     matrix.reset();
781     matrix.setScale(SkIntToScalar(-1), SkIntToScalar(1));
782     matrix.postRotate(SkIntToScalar(270));
783     dst2.setEmpty();
784     success = orig.transform(matrix, &dst2);
785     REPORTER_ASSERT(reporter, success);
786     REPORTER_ASSERT(reporter, dst == dst2);
787 
788     //  a-----b            b-----c        a-----d
789     //  |     |     ->     |     |   ->   |     |
790     //  |     | Rotate 270 |     | Flip Y |     |
791     //  d-----c            a-----d        b-----c
792     matrix.reset();
793     matrix.setRotate(SkIntToScalar(270));
794     matrix.postScale(SkIntToScalar(1), SkIntToScalar(-1));
795     dst2.setEmpty();
796     success = orig.transform(matrix, &dst2);
797     REPORTER_ASSERT(reporter, success);
798     REPORTER_ASSERT(reporter, dst == dst2);
799 
800     //  a-----b        b-----a           c-----b
801     //  |     |   ->   |     |    ->     |     |
802     //  |     | Flip X |     | Rotate 90 |     |
803     //  d-----c        c-----d           d-----a
804     matrix.reset();
805     matrix.setScale(SkIntToScalar(-1), SkIntToScalar(1));
806     matrix.postRotate(SkIntToScalar(90));
807     dst.setEmpty();
808     success = orig.transform(matrix, &dst);
809     REPORTER_ASSERT(reporter, success);
810     {
811         GET_RADII;
812         REPORTER_ASSERT(reporter, dstUL.x() == origLR.y());
813         REPORTER_ASSERT(reporter, dstUL.y() == origLR.x());
814         REPORTER_ASSERT(reporter, dstUR.x() == origUR.y());
815         REPORTER_ASSERT(reporter, dstUR.y() == origUR.x());
816         REPORTER_ASSERT(reporter, dstLR.x() == origUL.y());
817         REPORTER_ASSERT(reporter, dstLR.y() == origUL.x());
818         REPORTER_ASSERT(reporter, dstLL.x() == origLL.y());
819         REPORTER_ASSERT(reporter, dstLL.y() == origLL.x());
820     }
821     // Width and height would get swapped.
822     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().height());
823     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().width());
824 
825     //  a-----b           d-----a        c-----b
826     //  |     |    ->     |     |   ->   |     |
827     //  |     | Rotate 90 |     | Flip Y |     |
828     //  d-----c           c-----b        d-----a
829     // This is the same as flip X and rotate 90
830     matrix.reset();
831     matrix.setRotate(SkIntToScalar(90));
832     matrix.postScale(SkIntToScalar(1), SkIntToScalar(-1));
833     dst2.setEmpty();
834     success = orig.transform(matrix, &dst2);
835     REPORTER_ASSERT(reporter, success);
836     REPORTER_ASSERT(reporter, dst == dst2);
837 
838     //  a-----b            b-----c        c-----b
839     //  |     |     ->     |     |   ->   |     |
840     //  |     | Rotate 270 |     | Flip X |     |
841     //  d-----c            a-----d        d-----a
842     matrix.reset();
843     matrix.setRotate(SkIntToScalar(270));
844     matrix.postScale(SkIntToScalar(-1), SkIntToScalar(1));
845     dst2.setEmpty();
846     success = orig.transform(matrix, &dst2);
847     REPORTER_ASSERT(reporter, success);
848     REPORTER_ASSERT(reporter, dst == dst2);
849 
850     //  a-----b        d-----c            c-----b
851     //  |     |   ->   |     |    ->      |     |
852     //  |     | Flip Y |     | Rotate 270 |     |
853     //  d-----c        a-----b            d-----a
854     matrix.reset();
855     matrix.setScale(SkIntToScalar(1), SkIntToScalar(-1));
856     matrix.postRotate(SkIntToScalar(270));
857     dst2.setEmpty();
858     success = orig.transform(matrix, &dst2);
859     REPORTER_ASSERT(reporter, success);
860     REPORTER_ASSERT(reporter, dst == dst2);
861 
862     //  a-----b           d-----a          b-----c
863     //  |     |    ->     |     |   ->     |     |
864     //  |     | Rotate 90 |     | Flip X+Y |     |
865     //  d-----c           c-----b          a-----d
866     // This is the same as rotation by 270.
867     matrix.reset();
868     matrix.setRotate(SkIntToScalar(90));
869     matrix.postScale(SkIntToScalar(-1), SkIntToScalar(-1));
870     dst.setEmpty();
871     success = orig.transform(matrix, &dst);
872     REPORTER_ASSERT(reporter, success);
873     {
874         GET_RADII;
875         // Radii have cycled clockwise and swapped their x and y axis.
876         REPORTER_ASSERT(reporter, dstUL.x() == origUR.y());
877         REPORTER_ASSERT(reporter, dstUL.y() == origUR.x());
878         REPORTER_ASSERT(reporter, dstUR.x() == origLR.y());
879         REPORTER_ASSERT(reporter, dstUR.y() == origLR.x());
880         REPORTER_ASSERT(reporter, dstLR.x() == origLL.y());
881         REPORTER_ASSERT(reporter, dstLR.y() == origLL.x());
882         REPORTER_ASSERT(reporter, dstLL.x() == origUL.y());
883         REPORTER_ASSERT(reporter, dstLL.y() == origUL.x());
884     }
885     // Width and height would get swapped.
886     REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().height());
887     REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().width());
888 
889     //  a-----b             b-----c
890     //  |     |     ->      |     |
891     //  |     | Rotate 270  |     |
892     //  d-----c             a-----d
893     //
894     dst2.setEmpty();
895     matrix.reset();
896     matrix.setRotate(SkIntToScalar(270));
897     success = orig.transform(matrix, &dst2);
898     REPORTER_ASSERT(reporter, success);
899     REPORTER_ASSERT(reporter, dst == dst2);
900 
901     //  a-----b            b-----c          d-----a
902     //  |     |    ->      |     |   ->     |     |
903     //  |     | Rotate 270 |     | Flip X+Y |     |
904     //  d-----c            a-----d          c-----b
905     // This is the same as rotation by 90 degrees.
906     matrix.reset();
907     matrix.setRotate(SkIntToScalar(270));
908     matrix.postScale(SkIntToScalar(-1), SkIntToScalar(-1));
909     dst.setEmpty();
910     success = orig.transform(matrix, &dst);
911     REPORTER_ASSERT(reporter, success);
912 
913     matrix.reset();
914     matrix.setRotate(SkIntToScalar(90));
915     dst2.setEmpty();
916     success = orig.transform(matrix, &dst2);
917     REPORTER_ASSERT(reporter, dst == dst2);
918 
919     // Non-uniorm scale factor and +/-90 degree rotation must scale the dst X or Y radii
920     // by the correct swapped axis.
921     // 90 CW:
922     // -------------
923 
924     // a----b               a------b             d--a
925     // |    |     ->        |      |      ->     |  |
926     // |    |   Scale Y,    d------c  Rotate 90  |  |
927     // d----c   Scale X                          |  |
928     //                                           c--b
929     yScale = 0.5f;
930     xScale = 1.5f;
931     matrix.reset();
932     matrix.setRotate(SkIntToScalar(90));
933     matrix.preScale(xScale, yScale);
934     dst.setEmpty();
935     success = orig.transform(matrix, &dst);
936     REPORTER_ASSERT(reporter, success);
937     // Make scale factors positive for length comparisons
938     yScale = std::abs(yScale);
939     xScale = std::abs(xScale);
940     {
941         GET_RADII;
942         // Radii have counter clock-wise and swapped their x and y axis. The dst x radii should be
943         // scaled 1/2x compared to the y radii, dst y scaled 1.5 compared to src x.
944         REPORTER_ASSERT(reporter, dstUL.x() == yScale*origLL.y());
945         REPORTER_ASSERT(reporter, dstUL.y() == xScale*origLL.x());
946         REPORTER_ASSERT(reporter, dstUR.x() == yScale*origUL.y());
947         REPORTER_ASSERT(reporter, dstUR.y() == xScale*origUL.x());
948         REPORTER_ASSERT(reporter, dstLR.x() == yScale*origUR.y());
949         REPORTER_ASSERT(reporter, dstLR.y() == xScale*origUR.x());
950         REPORTER_ASSERT(reporter, dstLL.x() == yScale*origLR.y());
951         REPORTER_ASSERT(reporter, dstLL.y() == xScale*origLR.x());
952     }
953     // Width and height would get swapped, with the dst width half of the original height.
954     REPORTER_ASSERT(reporter, xScale*orig.rect().width() == dst.rect().height());
955     REPORTER_ASSERT(reporter, yScale*orig.rect().height() == dst.rect().width());
956 
957     // a----b               b------a             c--b
958     // |    |     ->        |      |      ->     |  |
959     // |    |   Scale Y,    c------d  Rotate 90  |  |
960     // d----c Flip+Scale X                       |  |
961     //                                           d--a
962     yScale = 0.5f;
963     xScale = -1.5f;
964     matrix.reset();
965     matrix.setRotate(SkIntToScalar(90));
966     matrix.preScale(xScale, yScale);
967     dst.setEmpty();
968     success = orig.transform(matrix, &dst);
969     REPORTER_ASSERT(reporter, success);
970     // Make scale factors positive for length comparisons
971     yScale = std::abs(yScale);
972     xScale = std::abs(xScale);
973     {
974         GET_RADII;
975         REPORTER_ASSERT(reporter, dstUL.x() == yScale*origLR.y());
976         REPORTER_ASSERT(reporter, dstUL.y() == xScale*origLR.x());
977         REPORTER_ASSERT(reporter, dstUR.x() == yScale*origUR.y());
978         REPORTER_ASSERT(reporter, dstUR.y() == xScale*origUR.x());
979         REPORTER_ASSERT(reporter, dstLR.x() == yScale*origUL.y());
980         REPORTER_ASSERT(reporter, dstLR.y() == xScale*origUL.x());
981         REPORTER_ASSERT(reporter, dstLL.x() == yScale*origLL.y());
982         REPORTER_ASSERT(reporter, dstLL.y() == xScale*origLL.x());
983     }
984     // Width and height would get swapped, with the dst width half of the original height.
985     REPORTER_ASSERT(reporter, xScale*orig.rect().width() == dst.rect().height());
986     REPORTER_ASSERT(reporter, yScale*orig.rect().height() == dst.rect().width());
987 
988     // a----b               d------c             a--d
989     // |    |     ->        |      |      ->     |  |
990     // |    | Flip+Scale Y, a------b  Rotate 90  |  |
991     // d----c    Scale X                         |  |
992     //                                           b--c
993     yScale = -0.5f;
994     xScale = 1.5f;
995     matrix.reset();
996     matrix.setRotate(SkIntToScalar(90));
997     matrix.preScale(xScale, yScale);
998     dst.setEmpty();
999     success = orig.transform(matrix, &dst);
1000     REPORTER_ASSERT(reporter, success);
1001     // Make scale factors positive for length comparisons
1002     yScale = std::abs(yScale);
1003     xScale = std::abs(xScale);
1004     {
1005         GET_RADII;
1006         REPORTER_ASSERT(reporter, dstUL.x() == yScale*origUL.y());
1007         REPORTER_ASSERT(reporter, dstUL.y() == xScale*origUL.x());
1008         REPORTER_ASSERT(reporter, dstUR.x() == yScale*origLL.y());
1009         REPORTER_ASSERT(reporter, dstUR.y() == xScale*origLL.x());
1010         REPORTER_ASSERT(reporter, dstLR.x() == yScale*origLR.y());
1011         REPORTER_ASSERT(reporter, dstLR.y() == xScale*origLR.x());
1012         REPORTER_ASSERT(reporter, dstLL.x() == yScale*origUR.y());
1013         REPORTER_ASSERT(reporter, dstLL.y() == xScale*origUR.x());
1014     }
1015     // Width and height would get swapped, with the dst width half of the original height.
1016     REPORTER_ASSERT(reporter, xScale*orig.rect().width() == dst.rect().height());
1017     REPORTER_ASSERT(reporter, yScale*orig.rect().height() == dst.rect().width());
1018 
1019     // a----b               c------d             b--c
1020     // |    |     ->        |      |      ->     |  |
1021     // |    | Flip+Scale Y, b------a  Rotate 90  |  |
1022     // d----c Flip+Scale X                       |  |
1023     //                                           a--d
1024     yScale = -0.5f;
1025     xScale = -1.5f;
1026     matrix.reset();
1027     matrix.setRotate(SkIntToScalar(90));
1028     matrix.preScale(xScale, yScale);
1029     dst.setEmpty();
1030     success = orig.transform(matrix, &dst);
1031     REPORTER_ASSERT(reporter, success);
1032     // Make scale factors positive for length comparisons
1033     yScale = std::abs(yScale);
1034     xScale = std::abs(xScale);
1035     {
1036         GET_RADII;
1037         // With double-flip the corners rotate 90 degrees counter clockwise although the scale
1038         // factors are swapped.
1039         REPORTER_ASSERT(reporter, dstUL.x() == yScale*origUR.y());
1040         REPORTER_ASSERT(reporter, dstUL.y() == xScale*origUR.x());
1041         REPORTER_ASSERT(reporter, dstUR.x() == yScale*origLR.y());
1042         REPORTER_ASSERT(reporter, dstUR.y() == xScale*origLR.x());
1043         REPORTER_ASSERT(reporter, dstLR.x() == yScale*origLL.y());
1044         REPORTER_ASSERT(reporter, dstLR.y() == xScale*origLL.x());
1045         REPORTER_ASSERT(reporter, dstLL.x() == yScale*origUL.y());
1046         REPORTER_ASSERT(reporter, dstLL.y() == xScale*origUL.x());
1047     }
1048     // Width and height would get swapped, with the dst width half of the original height.
1049     REPORTER_ASSERT(reporter, xScale*orig.rect().width() == dst.rect().height());
1050     REPORTER_ASSERT(reporter, yScale*orig.rect().height() == dst.rect().width());
1051 
1052     // 90 CCW (270):
1053     // -------------
1054     // a----b               a------b              b--c
1055     // |    |     ->        |      |      ->      |  |
1056     // |    |   Scale Y,    d------c  Rotate 270  |  |
1057     // d----c   Scale X                           |  |
1058     //                                            a--d
1059     yScale = 0.5f;
1060     xScale = 1.5f;
1061     matrix.reset();
1062     matrix.setRotate(SkIntToScalar(270));
1063     matrix.preScale(xScale, yScale);
1064     dst.setEmpty();
1065     success = orig.transform(matrix, &dst);
1066     REPORTER_ASSERT(reporter, success);
1067     // Make scale factors positive for length comparisons
1068     yScale = std::abs(yScale);
1069     xScale = std::abs(xScale);
1070     {
1071         GET_RADII;
1072         // Radii have cycled counter clock-wise and swapped their x and y axis. The dst x radii
1073         // should be scaled 1/2x compared to the y radii, dst y scaled 1.5 compared to src x.
1074         REPORTER_ASSERT(reporter, dstUL.x() == yScale*origUR.y());
1075         REPORTER_ASSERT(reporter, dstUL.y() == xScale*origUR.x());
1076         REPORTER_ASSERT(reporter, dstUR.x() == yScale*origLR.y());
1077         REPORTER_ASSERT(reporter, dstUR.y() == xScale*origLR.x());
1078         REPORTER_ASSERT(reporter, dstLR.x() == yScale*origLL.y());
1079         REPORTER_ASSERT(reporter, dstLR.y() == xScale*origLL.x());
1080         REPORTER_ASSERT(reporter, dstLL.x() == yScale*origUL.y());
1081         REPORTER_ASSERT(reporter, dstLL.y() == xScale*origUL.x());
1082     }
1083     // Width and height would get swapped, with the dst width half of the original height.
1084     REPORTER_ASSERT(reporter, xScale*orig.rect().width() == dst.rect().height());
1085     REPORTER_ASSERT(reporter, yScale*orig.rect().height() == dst.rect().width());
1086 
1087     // a----b               b------a              a--d
1088     // |    |     ->        |      |      ->      |  |
1089     // |    |   Scale Y,    c------d  Rotate 270  |  |
1090     // d----c Flip+Scale X                        |  |
1091     //                                            b--c
1092     yScale = 0.5f;
1093     xScale = -1.5f;
1094     matrix.reset();
1095     matrix.setRotate(SkIntToScalar(270));
1096     matrix.preScale(xScale, yScale);
1097     dst.setEmpty();
1098     success = orig.transform(matrix, &dst);
1099     REPORTER_ASSERT(reporter, success);
1100     // Make scale factors positive for length comparisons
1101     yScale = std::abs(yScale);
1102     xScale = std::abs(xScale);
1103     {
1104         GET_RADII;
1105         REPORTER_ASSERT(reporter, dstUL.x() == yScale*origUL.y());
1106         REPORTER_ASSERT(reporter, dstUL.y() == xScale*origUL.x());
1107         REPORTER_ASSERT(reporter, dstUR.x() == yScale*origLL.y());
1108         REPORTER_ASSERT(reporter, dstUR.y() == xScale*origLL.x());
1109         REPORTER_ASSERT(reporter, dstLR.x() == yScale*origLR.y());
1110         REPORTER_ASSERT(reporter, dstLR.y() == xScale*origLR.x());
1111         REPORTER_ASSERT(reporter, dstLL.x() == yScale*origUR.y());
1112         REPORTER_ASSERT(reporter, dstLL.y() == xScale*origUR.x());
1113     }
1114     // Width and height would get swapped, with the dst width half of the original height.
1115     REPORTER_ASSERT(reporter, xScale*orig.rect().width() == dst.rect().height());
1116     REPORTER_ASSERT(reporter, yScale*orig.rect().height() == dst.rect().width());
1117 
1118     // a----b               d------c              c--b
1119     // |    |     ->        |      |      ->      |  |
1120     // |    | Flip+Scale Y, a------b  Rotate 270  |  |
1121     // d----c    Scale X                          |  |
1122     //                                            d--a
1123     yScale = -0.5f;
1124     xScale = 1.5f;
1125     matrix.reset();
1126     matrix.setRotate(SkIntToScalar(270));
1127     matrix.preScale(xScale, yScale);
1128     dst.setEmpty();
1129     success = orig.transform(matrix, &dst);
1130     REPORTER_ASSERT(reporter, success);
1131     // Make scale factors positive for length comparisons
1132     yScale = std::abs(yScale);
1133     xScale = std::abs(xScale);
1134     {
1135         GET_RADII;
1136         REPORTER_ASSERT(reporter, dstUL.x() == yScale*origLR.y());
1137         REPORTER_ASSERT(reporter, dstUL.y() == xScale*origLR.x());
1138         REPORTER_ASSERT(reporter, dstUR.x() == yScale*origUR.y());
1139         REPORTER_ASSERT(reporter, dstUR.y() == xScale*origUR.x());
1140         REPORTER_ASSERT(reporter, dstLR.x() == yScale*origUL.y());
1141         REPORTER_ASSERT(reporter, dstLR.y() == xScale*origUL.x());
1142         REPORTER_ASSERT(reporter, dstLL.x() == yScale*origLL.y());
1143         REPORTER_ASSERT(reporter, dstLL.y() == xScale*origLL.x());
1144     }
1145     // Width and height would get swapped, with the dst width half of the original height.
1146     REPORTER_ASSERT(reporter, xScale*orig.rect().width() == dst.rect().height());
1147     REPORTER_ASSERT(reporter, yScale*orig.rect().height() == dst.rect().width());
1148 
1149     // a----b               c------d              d--a
1150     // |    |     ->        |      |      ->      |  |
1151     // |    | Flip+Scale Y, b------a  Rotate 270  |  |
1152     // d----c Flip+Scale X                        |  |
1153     //                                            c--b
1154     yScale = -0.5f;
1155     xScale = -1.5f;
1156     matrix.reset();
1157     matrix.setRotate(SkIntToScalar(270));
1158     matrix.preScale(xScale, yScale);
1159     dst.setEmpty();
1160     success = orig.transform(matrix, &dst);
1161     REPORTER_ASSERT(reporter, success);
1162     // Make scale factors positive for length comparisons
1163     yScale = std::abs(yScale);
1164     xScale = std::abs(xScale);
1165     {
1166         GET_RADII;
1167         // With double-flip the corners rotate 90 degrees clockwise although the scale factors
1168         // are swapped.
1169         REPORTER_ASSERT(reporter, dstUL.x() == yScale*origLL.y());
1170         REPORTER_ASSERT(reporter, dstUL.y() == xScale*origLL.x());
1171         REPORTER_ASSERT(reporter, dstUR.x() == yScale*origUL.y());
1172         REPORTER_ASSERT(reporter, dstUR.y() == xScale*origUL.x());
1173         REPORTER_ASSERT(reporter, dstLR.x() == yScale*origUR.y());
1174         REPORTER_ASSERT(reporter, dstLR.y() == xScale*origUR.x());
1175         REPORTER_ASSERT(reporter, dstLL.x() == yScale*origLR.y());
1176         REPORTER_ASSERT(reporter, dstLL.y() == xScale*origLR.x());
1177     }
1178     // Width and height would get swapped, with the dst width half of the original height.
1179     REPORTER_ASSERT(reporter, xScale*orig.rect().width() == dst.rect().height());
1180     REPORTER_ASSERT(reporter, yScale*orig.rect().height() == dst.rect().width());
1181 }
1182 
test_round_rect_transform(skiatest::Reporter * reporter)1183 static void test_round_rect_transform(skiatest::Reporter* reporter) {
1184     SkRRect rrect;
1185     {
1186         SkRect r = { 0, 0, kWidth, kHeight };
1187         rrect.setRectXY(r, SkIntToScalar(4), SkIntToScalar(7));
1188         test_transform_helper(reporter, rrect);
1189     }
1190     {
1191         SkRect r = { SkIntToScalar(5), SkIntToScalar(15),
1192                      SkIntToScalar(27), SkIntToScalar(34) };
1193         SkVector radii[4] = { { 0, SkIntToScalar(1) },
1194                               { SkIntToScalar(2), SkIntToScalar(3) },
1195                               { SkIntToScalar(4), SkIntToScalar(5) },
1196                               { SkIntToScalar(6), SkIntToScalar(7) } };
1197         rrect.setRectRadii(r, radii);
1198         test_transform_helper(reporter, rrect);
1199     }
1200     {
1201         SkRect r = { 760.0f, 228.0f, 1160.0f, 1028.0f };
1202         SkVector radii[4] = { { 400.0f, 400.0f },
1203                               { 0, 0 },
1204                               { 0, 0 },
1205                               { 400.0f, 400.0f } };
1206         rrect.setRectRadii(r, radii);
1207         test_transform_helper(reporter, rrect);
1208     }
1209 }
1210 
1211 // Test out the case where an oval already off in space is translated/scaled
1212 // further off into space - yielding numerical issues when the rect & radii
1213 // are transformed separatly
1214 // BUG=skia:2696
test_issue_2696(skiatest::Reporter * reporter)1215 static void test_issue_2696(skiatest::Reporter* reporter) {
1216     SkRRect rrect;
1217     SkRect r = { 28443.8594f, 53.1428604f, 28446.7148f, 56.0000038f };
1218     rrect.setOval(r);
1219 
1220     SkMatrix xform;
1221     xform.setAll(2.44f,  0.0f, 485411.7f,
1222                  0.0f,  2.44f,   -438.7f,
1223                  0.0f,   0.0f,      1.0f);
1224     SkRRect dst;
1225 
1226     bool success = rrect.transform(xform, &dst);
1227     REPORTER_ASSERT(reporter, success);
1228 
1229     SkScalar halfWidth = SkScalarHalf(dst.width());
1230     SkScalar halfHeight = SkScalarHalf(dst.height());
1231 
1232     for (int i = 0; i < 4; ++i) {
1233         REPORTER_ASSERT(reporter,
1234                         SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth));
1235         REPORTER_ASSERT(reporter,
1236                         SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight));
1237     }
1238 }
1239 
test_read_rrect(skiatest::Reporter * reporter,const SkRRect & rrect,bool shouldEqualSrc)1240 void test_read_rrect(skiatest::Reporter* reporter, const SkRRect& rrect, bool shouldEqualSrc) {
1241     // It would be cleaner to call rrect.writeToMemory into a buffer. However, writeToMemory asserts
1242     // that the rrect is valid and our caller may have fiddled with the internals of rrect to make
1243     // it invalid.
1244     const void* buffer = reinterpret_cast<const void*>(&rrect);
1245     SkRRect deserialized;
1246     size_t size = deserialized.readFromMemory(buffer, sizeof(SkRRect));
1247     REPORTER_ASSERT(reporter, size == SkRRect::kSizeInMemory);
1248     REPORTER_ASSERT(reporter, deserialized.isValid());
1249     if (shouldEqualSrc) {
1250        REPORTER_ASSERT(reporter, rrect == deserialized);
1251     }
1252 }
1253 
test_read(skiatest::Reporter * reporter)1254 static void test_read(skiatest::Reporter* reporter) {
1255     static const SkRect kRect = {10.f, 10.f, 20.f, 20.f};
1256     static const SkRect kNaNRect = {10.f, 10.f, 20.f, SK_ScalarNaN};
1257     static const SkRect kInfRect = {10.f, 10.f, SK_ScalarInfinity, 20.f};
1258     SkRRect rrect;
1259 
1260     test_read_rrect(reporter, SkRRect::MakeEmpty(), true);
1261     test_read_rrect(reporter, SkRRect::MakeRect(kRect), true);
1262     // These get coerced to empty.
1263     test_read_rrect(reporter, SkRRect::MakeRect(kInfRect), true);
1264     test_read_rrect(reporter, SkRRect::MakeRect(kNaNRect), true);
1265 
1266     rrect.setRect(kRect);
1267     SkRect* innerRect = reinterpret_cast<SkRect*>(&rrect);
1268     SkASSERT(*innerRect == kRect);
1269     *innerRect = kInfRect;
1270     test_read_rrect(reporter, rrect, false);
1271     *innerRect = kNaNRect;
1272     test_read_rrect(reporter, rrect, false);
1273 
1274     test_read_rrect(reporter, SkRRect::MakeOval(kRect), true);
1275     test_read_rrect(reporter, SkRRect::MakeOval(kInfRect), true);
1276     test_read_rrect(reporter, SkRRect::MakeOval(kNaNRect), true);
1277     rrect.setOval(kRect);
1278     *innerRect = kInfRect;
1279     test_read_rrect(reporter, rrect, false);
1280     *innerRect = kNaNRect;
1281     test_read_rrect(reporter, rrect, false);
1282 
1283     test_read_rrect(reporter, SkRRect::MakeRectXY(kRect, 5.f, 5.f), true);
1284     // rrect should scale down the radii to make this legal
1285     test_read_rrect(reporter, SkRRect::MakeRectXY(kRect, 5.f, 400.f), true);
1286 
1287     static const SkVector kRadii[4] = {{0.5f, 1.f}, {1.5f, 2.f}, {2.5f, 3.f}, {3.5f, 4.f}};
1288     rrect.setRectRadii(kRect, kRadii);
1289     test_read_rrect(reporter, rrect, true);
1290     SkScalar* innerRadius = reinterpret_cast<SkScalar*>(&rrect) + 6;
1291     SkASSERT(*innerRadius == 1.5f);
1292     *innerRadius = 400.f;
1293     test_read_rrect(reporter, rrect, false);
1294     *innerRadius = SK_ScalarInfinity;
1295     test_read_rrect(reporter, rrect, false);
1296     *innerRadius = SK_ScalarNaN;
1297     test_read_rrect(reporter, rrect, false);
1298     *innerRadius = -10.f;
1299     test_read_rrect(reporter, rrect, false);
1300 }
1301 
test_inner_bounds(skiatest::Reporter * reporter)1302 static void test_inner_bounds(skiatest::Reporter* reporter) {
1303     // Because InnerBounds() insets the computed bounds slightly to correct for numerical inaccuracy
1304     // when finding the maximum inscribed point on a curve, we use a larger epsilon for comparing
1305     // expected areas.
1306     static constexpr SkScalar kEpsilon = 0.005f;
1307 
1308     // Test that an empty rrect reports empty inner bounds
1309     REPORTER_ASSERT(reporter, SkRRectPriv::InnerBounds(SkRRect::MakeEmpty()).isEmpty());
1310     // Test that a rect rrect reports itself as the inner bounds
1311     SkRect r = SkRect::MakeLTRB(0, 1, 2, 3);
1312     REPORTER_ASSERT(reporter, SkRRectPriv::InnerBounds(SkRRect::MakeRect(r)) == r);
1313     // Test that a circle rrect has an inner bounds area equal to 2*radius^2
1314     float radius = 5.f;
1315     SkRect inner = SkRRectPriv::InnerBounds(SkRRect::MakeOval(SkRect::MakeWH(2.f * radius,
1316                                                                              2.f * radius)));
1317     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(inner.width() * inner.height(),
1318                                                   2.f * radius * radius, kEpsilon));
1319 
1320     float width = 20.f;
1321     float height = 25.f;
1322     r = SkRect::MakeWH(width, height);
1323     // Test that a rrect with circular corners has an area equal to:
1324     float expectedArea =
1325             (2.f * radius * radius) +                      // area in the 4 circular corners
1326             (width-2.f*radius) * (height-2.f*radius) +     // inner area excluding corners and edges
1327             SK_ScalarSqrt2 * radius * (width-2.f*radius) + // two horiz. rects between corners
1328             SK_ScalarSqrt2 * radius * (height-2.f*radius); // two vert. rects between corners
1329 
1330     inner = SkRRectPriv::InnerBounds(SkRRect::MakeRectXY(r, radius, radius));
1331     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(inner.width() * inner.height(),
1332                                                   expectedArea, kEpsilon));
1333 
1334     // Test that a rrect with a small y radius but large x radius selects the horizontal interior
1335     SkRRect rr = SkRRect::MakeRectXY(r, 2.f * radius, 0.1f * radius);
1336     REPORTER_ASSERT(reporter, SkRRectPriv::InnerBounds(rr) ==
1337                               SkRect::MakeLTRB(0.f, 0.1f * radius, width, height - 0.1f * radius));
1338     // And vice versa with large y and small x radii
1339     rr = SkRRect::MakeRectXY(r, 0.1f * radius, 2.f * radius);
1340     REPORTER_ASSERT(reporter, SkRRectPriv::InnerBounds(rr) ==
1341                               SkRect::MakeLTRB(0.1f * radius, 0.f, width - 0.1f * radius, height));
1342 
1343     // Test a variety of complex round rects produce a non-empty rect that is at least contained,
1344     // and larger than the inner area avoiding all corners.
1345     SkRandom rng;
1346     for (int i = 0; i < 1000; ++i) {
1347         float maxRadiusX = rng.nextRangeF(0.f, 40.f);
1348         float maxRadiusY = rng.nextRangeF(0.f, 40.f);
1349 
1350         float innerWidth = rng.nextRangeF(0.f, 40.f);
1351         float innerHeight = rng.nextRangeF(0.f, 40.f);
1352 
1353         SkVector radii[4] = {{rng.nextRangeF(0.f, maxRadiusX), rng.nextRangeF(0.f, maxRadiusY)},
1354                              {rng.nextRangeF(0.f, maxRadiusX), rng.nextRangeF(0.f, maxRadiusY)},
1355                              {rng.nextRangeF(0.f, maxRadiusX), rng.nextRangeF(0.f, maxRadiusY)},
1356                              {rng.nextRangeF(0.f, maxRadiusX), rng.nextRangeF(0.f, maxRadiusY)}};
1357 
1358         float maxLeft   = std::max(radii[0].fX, radii[3].fX);
1359         float maxTop    = std::max(radii[0].fY, radii[1].fY);
1360         float maxRight  = std::max(radii[1].fX, radii[2].fX);
1361         float maxBottom = std::max(radii[2].fY, radii[3].fY);
1362 
1363         SkRect outer = SkRect::MakeWH(maxLeft + maxRight + innerWidth,
1364                                       maxTop + maxBottom + innerHeight);
1365         rr.setRectRadii(outer, radii);
1366 
1367         SkRect maxInner = SkRRectPriv::InnerBounds(rr);
1368         // Test upper limit on the size of 'maxInner'
1369         REPORTER_ASSERT(reporter, outer.contains(maxInner));
1370         REPORTER_ASSERT(reporter, rr.contains(maxInner));
1371 
1372         // Test lower limit on the size of 'maxInner'
1373         inner = SkRect::MakeXYWH(maxLeft, maxTop, innerWidth, innerHeight);
1374         inner.inset(kEpsilon, kEpsilon);
1375 
1376         if (inner.isSorted()) {
1377             REPORTER_ASSERT(reporter, maxInner.contains(inner));
1378         } else {
1379             // Flipped from the inset, just test two points of inner
1380             float midX = maxLeft + 0.5f * innerWidth;
1381             float midY = maxTop + 0.5f * innerHeight;
1382             REPORTER_ASSERT(reporter, maxInner.contains(midX, maxTop));
1383             REPORTER_ASSERT(reporter, maxInner.contains(midX, maxTop + innerHeight));
1384             REPORTER_ASSERT(reporter, maxInner.contains(maxLeft, midY));
1385             REPORTER_ASSERT(reporter, maxInner.contains(maxLeft + innerWidth, midY));
1386         }
1387     }
1388 }
1389 
1390 namespace {
1391     // Helper to test expected intersection, relying on the fact that all round rect intersections
1392     // will have their bounds equal to the intersection of the bounds of the input round rects, and
1393     // their corner radii will be a one of A's, B's, or rectangular.
1394     enum CornerChoice : uint8_t {
1395         kA, kB, kRect
1396     };
1397 
verify_success(skiatest::Reporter * reporter,const SkRRect & a,const SkRRect & b,CornerChoice tl,CornerChoice tr,CornerChoice br,CornerChoice bl)1398     static void verify_success(skiatest::Reporter* reporter, const SkRRect& a, const SkRRect& b,
1399                                CornerChoice tl, CornerChoice tr, CornerChoice br, CornerChoice bl) {
1400         static const SkRRect kRect = SkRRect::MakeEmpty(); // has (0,0) for all corners
1401 
1402         // Compute expected round rect intersection given bounds of A and B, and the specified
1403         // corner choices for the 4 corners.
1404         SkRect expectedBounds;
1405         SkAssertResult(expectedBounds.intersect(a.rect(), b.rect()));
1406 
1407         SkVector radii[4] = {
1408             (tl == kA ? a : (tl == kB ? b : kRect)).radii(SkRRect::kUpperLeft_Corner),
1409             (tr == kA ? a : (tr == kB ? b : kRect)).radii(SkRRect::kUpperRight_Corner),
1410             (br == kA ? a : (br == kB ? b : kRect)).radii(SkRRect::kLowerRight_Corner),
1411             (bl == kA ? a : (bl == kB ? b : kRect)).radii(SkRRect::kLowerLeft_Corner)
1412         };
1413         SkRRect expected;
1414         expected.setRectRadii(expectedBounds, radii);
1415 
1416         SkRRect actual = SkRRectPriv::ConservativeIntersect(a, b);
1417         // Intersections are commutative so ba and ab should be the same
1418         REPORTER_ASSERT(reporter, actual == SkRRectPriv::ConservativeIntersect(b, a));
1419 
1420         // Intersection of the result with either A or B should remain the intersection
1421         REPORTER_ASSERT(reporter, actual == SkRRectPriv::ConservativeIntersect(actual, a));
1422         REPORTER_ASSERT(reporter, actual == SkRRectPriv::ConservativeIntersect(actual, b));
1423 
1424         // Bounds of intersection round rect should equal intersection of bounds of a and b
1425         REPORTER_ASSERT(reporter, actual.rect() == expectedBounds);
1426 
1427         // Use PathOps to confirm that the explicit round rect is correct.
1428         SkPath aPath, bPath, expectedPath;
1429         aPath.addRRect(a);
1430         bPath.addRRect(b);
1431         SkAssertResult(Op(aPath, bPath, kIntersect_SkPathOp, &expectedPath));
1432 
1433         // The isRRect() heuristics in SkPath are based on having called addRRect(), so a path from
1434         // path ops that is a rounded rectangle will return false. However, if test XOR expected is
1435         // empty, then we know that the shapes were the same.
1436         SkPath testPath;
1437         testPath.addRRect(actual);
1438 
1439         SkPath empty;
1440         SkAssertResult(Op(testPath, expectedPath, kXOR_SkPathOp, &empty));
1441         REPORTER_ASSERT(reporter, empty.isEmpty());
1442     }
1443 
verify_failure(skiatest::Reporter * reporter,const SkRRect & a,const SkRRect & b)1444     static void verify_failure(skiatest::Reporter* reporter, const SkRRect& a, const SkRRect& b) {
1445         SkRRect intersection = SkRRectPriv::ConservativeIntersect(a, b);
1446         // Expected the intersection to fail (no intersection or complex intersection is not
1447         // disambiguated).
1448         REPORTER_ASSERT(reporter, intersection.isEmpty());
1449         REPORTER_ASSERT(reporter, SkRRectPriv::ConservativeIntersect(b, a).isEmpty());
1450     }
1451 }  // namespace
1452 
test_conservative_intersection(skiatest::Reporter * reporter)1453 static void test_conservative_intersection(skiatest::Reporter* reporter) {
1454     // Helper to inline making an inset round rect
1455     auto make_inset = [](const SkRRect& r, float dx, float dy) {
1456         SkRRect i = r;
1457         i.inset(dx, dy);
1458         return i;
1459     };
1460 
1461     // A is a wide, short round rect
1462     SkRRect a = SkRRect::MakeRectXY({0.f, 4.f, 16.f, 12.f}, 2.f, 2.f);
1463     // B is a narrow, tall round rect
1464     SkRRect b = SkRRect::MakeRectXY({4.f, 0.f, 12.f, 16.f}, 3.f, 3.f);
1465     // NOTE: As positioned by default, A and B intersect as the rectangle {4, 4, 12, 12}.
1466     // There is a 2 px buffer between the corner curves of A and the vertical edges of B, and
1467     // a 1 px buffer between the corner curves of B and the horizontal edges of A. Since the shapes
1468     // form a symmetric rounded cross, we can easily test edge and corner combinations by simply
1469     // flipping signs and/or swapping x and y offsets.
1470 
1471     // Successful intersection operations:
1472     //  - for clarity these are formed by moving A around to intersect with B in different ways.
1473     //  - the expected bounds of the round rect intersection is calculated automatically
1474     //    in check_success, so all we have to specify are the expected corner radii
1475 
1476     // A and B intersect as a rectangle
1477     verify_success(reporter, a, b, kRect, kRect, kRect, kRect);
1478     // Move A to intersect B on a vertical edge, preserving two corners of A inside B
1479     verify_success(reporter, a.makeOffset(6.f, 0.f), b, kA, kRect, kRect, kA);
1480     verify_success(reporter, a.makeOffset(-6.f, 0.f), b, kRect, kA, kA, kRect);
1481     // Move B to intersect A on a horizontal edge, preserving two corners of B inside A
1482     verify_success(reporter, a, b.makeOffset(0.f, 6.f), kB, kB, kRect, kRect);
1483     verify_success(reporter, a, b.makeOffset(0.f, -6.f), kRect, kRect, kB, kB);
1484     // Move A to intersect B on a corner, preserving one corner of A and one of B
1485     verify_success(reporter, a.makeOffset(-7.f, -8.f), b, kB, kRect, kA, kRect); // TL of B
1486     verify_success(reporter, a.makeOffset(7.f, -8.f), b, kRect, kB, kRect, kA);  // TR of B
1487     verify_success(reporter, a.makeOffset(7.f, 8.f), b, kA, kRect, kB, kRect);   // BR of B
1488     verify_success(reporter, a.makeOffset(-7.f, 8.f), b, kRect, kA, kRect, kB);  // BL of B
1489     // An inset is contained inside the original (note that SkRRect::inset modifies radii too) so
1490     // is returned unmodified when intersected.
1491     verify_success(reporter, a, make_inset(a, 1.f, 1.f), kB, kB, kB, kB);
1492     verify_success(reporter, make_inset(b, 2.f, 2.f), b, kA, kA, kA, kA);
1493 
1494     // A rectangle exactly matching the corners of the rrect bounds keeps the rrect radii,
1495     // regardless of whether or not it's the 1st or 2nd arg to ConservativeIntersect.
1496     SkRRect c = SkRRect::MakeRectXY({0.f, 0.f, 10.f, 10.f}, 2.f, 2.f);
1497     SkRRect cT = SkRRect::MakeRect({0.f, 0.f, 10.f, 5.f});
1498     verify_success(reporter, c, cT, kA, kA, kRect, kRect);
1499     verify_success(reporter, cT, c, kB, kB, kRect, kRect);
1500     SkRRect cB = SkRRect::MakeRect({0.f, 5.f, 10.f, 10.});
1501     verify_success(reporter, c, cB, kRect, kRect, kA, kA);
1502     verify_success(reporter, cB, c, kRect, kRect, kB, kB);
1503     SkRRect cL = SkRRect::MakeRect({0.f, 0.f, 5.f, 10.f});
1504     verify_success(reporter, c, cL, kA, kRect, kRect, kA);
1505     verify_success(reporter, cL, c, kB, kRect, kRect, kB);
1506     SkRRect cR = SkRRect::MakeRect({5.f, 0.f, 10.f, 10.f});
1507     verify_success(reporter, c, cR, kRect, kA, kA, kRect);
1508     verify_success(reporter, cR, c, kRect, kB, kB, kRect);
1509 
1510     // Failed intersection operations:
1511 
1512     // A and B's bounds do not intersect
1513     verify_failure(reporter, a.makeOffset(32.f, 0.f), b);
1514     // A and B's bounds intersect, but corner curves do not -> no intersection
1515     verify_failure(reporter, a.makeOffset(11.5f, -11.5f), b);
1516     // A is empty -> no intersection
1517     verify_failure(reporter, SkRRect::MakeEmpty(), b);
1518     // A is contained in B, but is too close to the corner curves for the conservative
1519     // approximations to construct a valid round rect intersection.
1520     verify_failure(reporter, make_inset(b, 0.3f, 0.3f), b);
1521     // A intersects a straight edge, but not far enough for B to contain A's corners
1522     verify_failure(reporter, a.makeOffset(2.5f, 0.f), b);
1523     verify_failure(reporter, a.makeOffset(-2.5f, 0.f), b);
1524     // And vice versa for B into A
1525     verify_failure(reporter, a, b.makeOffset(0.f, 1.5f));
1526     verify_failure(reporter, a, b.makeOffset(0.f, -1.5f));
1527     // A intersects a straight edge and part of B's corner
1528     verify_failure(reporter, a.makeOffset(5.f, -2.f), b);
1529     verify_failure(reporter, a.makeOffset(-5.f, -2.f), b);
1530     verify_failure(reporter, a.makeOffset(5.f, 2.f), b);
1531     verify_failure(reporter, a.makeOffset(-5.f, 2.f), b);
1532     // And vice versa
1533     verify_failure(reporter, a, b.makeOffset(3.f, -5.f));
1534     verify_failure(reporter, a, b.makeOffset(-3.f, -5.f));
1535     verify_failure(reporter, a, b.makeOffset(3.f, 5.f));
1536     verify_failure(reporter, a, b.makeOffset(-3.f, 5.f));
1537     // A intersects B on a corner, but the corner curves overlap each other
1538     verify_failure(reporter, a.makeOffset(8.f, 10.f), b);
1539     verify_failure(reporter, a.makeOffset(-8.f, 10.f), b);
1540     verify_failure(reporter, a.makeOffset(8.f, -10.f), b);
1541     verify_failure(reporter, a.makeOffset(-8.f, -10.f), b);
1542 
1543     // Another variant of corners overlapping, this is two circles of radius r that overlap by r
1544     // pixels (e.g. the leftmost point of the right circle touches the center of the left circle).
1545     // The key difference with the above case is that the intersection of the circle bounds have
1546     // corners that are contained in both circles, but because it is only r wide, can not satisfy
1547     // all corners having radii = r.
1548     float r = 100.f;
1549     a = SkRRect::MakeOval(SkRect::MakeWH(2*r, 2*r));
1550     verify_failure(reporter, a, a.makeOffset(r, 0.f));
1551 }
1552 
DEF_TEST(RoundRect,reporter)1553 DEF_TEST(RoundRect, reporter) {
1554     test_round_rect_basic(reporter);
1555     test_round_rect_rects(reporter);
1556     test_round_rect_ovals(reporter);
1557     test_round_rect_general(reporter);
1558     test_round_rect_iffy_parameters(reporter);
1559     test_inset(reporter);
1560     test_round_rect_contains_rect(reporter);
1561     test_round_rect_transform(reporter);
1562     test_issue_2696(reporter);
1563     test_tricky_radii(reporter);
1564     test_empty_crbug_458524(reporter);
1565     test_empty(reporter);
1566     test_read(reporter);
1567     test_inner_bounds(reporter);
1568     test_conservative_intersection(reporter);
1569 }
1570 
DEF_TEST(RRect_fuzzer_regressions,r)1571 DEF_TEST(RRect_fuzzer_regressions, r) {
1572     {
1573         unsigned char buf[] = {
1574             0x0a, 0x00, 0x00, 0xff, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f,
1575             0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
1576             0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
1577             0x7f, 0x7f, 0x7f, 0x02, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x02, 0x00
1578         };
1579         REPORTER_ASSERT(r, sizeof(buf) == SkRRect{}.readFromMemory(buf, sizeof(buf)));
1580     }
1581 
1582     {
1583         unsigned char buf[] = {
1584             0x5d, 0xff, 0xff, 0x5d, 0x0a, 0x60, 0x0a, 0x0a, 0x0a, 0x7e, 0x0a, 0x5a,
1585             0x0a, 0x12, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
1586             0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x00, 0x00, 0x00, 0x0a,
1587             0x0a, 0x0a, 0x0a, 0x26, 0x0a, 0x0a, 0x0a, 0x0a, 0xff, 0xff, 0x0a, 0x0a
1588         };
1589         REPORTER_ASSERT(r, sizeof(buf) == SkRRect{}.readFromMemory(buf, sizeof(buf)));
1590     }
1591 
1592     {
1593         unsigned char buf[] = {
1594             0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x04, 0xdd, 0xdd, 0x15,
1595             0xfe, 0x00, 0x00, 0x04, 0x05, 0x7e, 0x00, 0x00, 0x00, 0xff, 0x08, 0x04,
1596             0xff, 0xff, 0xfe, 0xfe, 0xff, 0x32, 0x32, 0x32, 0x32, 0x00, 0x32, 0x32,
1597             0x04, 0xdd, 0x3d, 0x1c, 0xfe, 0x89, 0x04, 0x0a, 0x0e, 0x05, 0x7e, 0x0a
1598         };
1599         REPORTER_ASSERT(r, sizeof(buf) == SkRRect{}.readFromMemory(buf, sizeof(buf)));
1600     }
1601 }
1602