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