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