blob: 012b9102c015e1d1754e9159aed6a5db35bf1d46 [file] [log] [blame]
robertphillips@google.com5985e7c2012-11-29 13:24:55 +00001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkMatrix.h"
9#include "include/core/SkRRect.h"
Michael Ludwig76312fb2020-04-20 18:21:42 -040010#include "include/pathops/SkPathOps.h"
Michael Ludwigeaeb9962020-04-17 12:11:21 -040011#include "include/utils/SkRandom.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "src/core/SkPointPriv.h"
Michael Ludwigeaeb9962020-04-17 12:11:21 -040013#include "src/core/SkRRectPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "tests/Test.h"
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000015
robertphillips2a679ae2015-03-13 09:53:01 -070016static 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 }
reed694b0d12015-02-13 14:33:02 -080039}
40
41static 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
robertphillips05302f82015-09-29 11:24:07 -070054// Test that all the SkRRect entry points correctly handle un-sorted and
55// zero-sized input rects
56static 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());
Brian Salomon0a241ce2017-12-15 11:31:05 -050077 REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
robertphillips05302f82015-09-29 11:24:07 -070078
79 r.setOval(oooRects[i]);
80 REPORTER_ASSERT(reporter, !r.isEmpty());
Brian Salomon0a241ce2017-12-15 11:31:05 -050081 REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
robertphillips05302f82015-09-29 11:24:07 -070082
83 r.setRectXY(oooRects[i], 1, 2);
84 REPORTER_ASSERT(reporter, !r.isEmpty());
Brian Salomon0a241ce2017-12-15 11:31:05 -050085 REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
robertphillips05302f82015-09-29 11:24:07 -070086
87 r.setNinePatch(oooRects[i], 0, 1, 2, 3);
88 REPORTER_ASSERT(reporter, !r.isEmpty());
Brian Salomon0a241ce2017-12-15 11:31:05 -050089 REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
robertphillips05302f82015-09-29 11:24:07 -070090
91 r.setRectRadii(oooRects[i], radii);
92 REPORTER_ASSERT(reporter, !r.isEmpty());
Brian Salomon0a241ce2017-12-15 11:31:05 -050093 REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted());
robertphillips05302f82015-09-29 11:24:07 -070094 }
95
96 for (size_t i = 0; i < SK_ARRAY_COUNT(emptyRects); ++i) {
97 r.setRect(emptyRects[i]);
98 REPORTER_ASSERT(reporter, r.isEmpty());
Brian Salomon0a241ce2017-12-15 11:31:05 -050099 REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
robertphillips05302f82015-09-29 11:24:07 -0700100
101 r.setOval(emptyRects[i]);
102 REPORTER_ASSERT(reporter, r.isEmpty());
Brian Salomon0a241ce2017-12-15 11:31:05 -0500103 REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
robertphillips05302f82015-09-29 11:24:07 -0700104
105 r.setRectXY(emptyRects[i], 1, 2);
106 REPORTER_ASSERT(reporter, r.isEmpty());
Brian Salomon0a241ce2017-12-15 11:31:05 -0500107 REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
robertphillips05302f82015-09-29 11:24:07 -0700108
109 r.setNinePatch(emptyRects[i], 0, 1, 2, 3);
110 REPORTER_ASSERT(reporter, r.isEmpty());
Brian Salomon0a241ce2017-12-15 11:31:05 -0500111 REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
robertphillips05302f82015-09-29 11:24:07 -0700112
113 r.setRectRadii(emptyRects[i], radii);
114 REPORTER_ASSERT(reporter, r.isEmpty());
Brian Salomon0a241ce2017-12-15 11:31:05 -0500115 REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]);
robertphillips05302f82015-09-29 11:24:07 -0700116 }
Brian Salomon0a241ce2017-12-15 11:31:05 -0500117
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());
robertphillips05302f82015-09-29 11:24:07 -0700122}
123
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000124static const SkScalar kWidth = 100.0f;
125static const SkScalar kHeight = 100.0f;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000126
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000127static 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());
skia.committer@gmail.com1a60dab2012-12-24 02:01:25 +0000137
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000138 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
robertphillipsfe1b1802015-02-24 11:18:48 -0800148
149static 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
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000174// Test out the basic API entry points
175static void test_round_rect_basic(skiatest::Reporter* reporter) {
176 // Test out initialization methods
reed@google.com2b57dc62013-01-08 13:23:32 +0000177 SkPoint zeroPt = { 0, 0 };
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000178 SkRRect empty;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000179
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000180 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 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000201 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());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000208
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) {
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000218 REPORTER_ASSERT(reporter,
Cary Clarkdf429f32017-11-08 11:44:31 -0500219 SkPointPriv::EqualsWithinTolerance(rr2.radii((SkRRect::Corner) i),
220 halfPoint));
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000221 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000222 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());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000230
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 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000242 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());
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000249
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000250 //----
robertphillipsfe1b1802015-02-24 11:18:48 -0800251 test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000252
robertphillipsfe1b1802015-02-24 11:18:48 -0800253 {
254 // Test out the rrect from skia:3466
255 SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000256
robertphillipsfe1b1802015-02-24 11:18:48 -0800257 test_9patch_rrect(reporter,
258 rect2,
259 0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f,
260 false);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000261 }
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);
robertphillipsfe1b1802015-02-24 11:18:48 -0800278 REPORTER_ASSERT(reporter, rr3 != rr5);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000279}
280
281// Test out the cases when the RR degenerates to a rect
282static void test_round_rect_rects(skiatest::Reporter* reporter) {
283 SkRect r;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000284
285 //----
286 SkRRect empty;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000287
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000288 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);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000302
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);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000312
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
322static void test_round_rect_ovals(skiatest::Reporter* reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000323 //----
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);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000332}
333
334// Test out the non-degenerate RR cases
335static void test_round_rect_general(skiatest::Reporter* reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000336 //----
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());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000342
343 //----
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000344 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());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000350}
351
352// Test out questionable-parameter handling
353static 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
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000382// Move a small box from the start position by (stepX, stepY) 'numSteps' times
383// testing for containment in 'rr' at each step.
384static void test_direction(skiatest::Reporter* reporter, const SkRRect &rr,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000385 SkScalar initX, int stepX, SkScalar initY, int stepY,
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000386 int numSteps, const bool* contains) {
387 SkScalar x = initX, y = initY;
388 for (int i = 0; i < numSteps; ++i) {
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000389 SkRect test = SkRect::MakeXYWH(x, y,
390 stepX ? SkIntToScalar(stepX) : SK_Scalar1,
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000391 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
402static 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 };
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000429
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000430 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
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000436 // Now test non-trivial containment. For each compass
437 // point walk a 1x1 rect in from the edge of the bounding
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000438 // 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
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000454 // corners (then the rest in) and only the first block
455 // out on the vertical and horizontal axes (then
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000456 // 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
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000516// Called for a matrix that should cause SkRRect::transform to fail.
517static 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.
547static 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();
reed3f43f8a2015-01-20 19:58:36 -0800567 matrix.setPerspX(4);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000568 assert_transform_failure(reporter, orig, matrix);
569
570 matrix.reset();
reed3f43f8a2015-01-20 19:58:36 -0800571 matrix.setPerspY(5);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000572 assert_transform_failure(reporter, orig, matrix);
573
574 // Rotation fails.
575 matrix.reset();
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000576 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.
reed3f43f8a2015-01-20 19:58:36 -0800624 matrix.setPerspX(7);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000625 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);
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000671 SkScalar yScale = 3.2f;
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000672 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,
Mike Reeddf85c382017-02-14 10:59:19 -0500681 orig.radii((SkRRect::Corner) i).fX * xScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000682 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY,
Mike Reeddf85c382017-02-14 10:59:19 -0500683 orig.radii((SkRRect::Corner) i).fY * yScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000684 }
685 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(),
Mike Reeddf85c382017-02-14 10:59:19 -0500686 orig.rect().width() * xScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000687 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(),
Mike Reeddf85c382017-02-14 10:59:19 -0500688 orig.rect().height() * yScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000689 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(),
Mike Reeddf85c382017-02-14 10:59:19 -0500690 orig.rect().left() * xScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000691 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(),
Mike Reeddf85c382017-02-14 10:59:19 -0500692 orig.rect().top() * yScale));
Malay Keshave4628b12019-04-03 13:28:05 -0700693
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
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000906}
907
908static 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
halcanary9d524f22016-03-29 09:03:52 -0700927// Test out the case where an oval already off in space is translated/scaled
robertphillipse5c1e3c2014-06-27 08:59:26 -0700928// further off into space - yielding numerical issues when the rect & radii
929// are transformed separatly
930// BUG=skia:2696
931static 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) {
halcanary9d524f22016-03-29 09:03:52 -0700949 REPORTER_ASSERT(reporter,
robertphillipse5c1e3c2014-06-27 08:59:26 -0700950 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth));
halcanary9d524f22016-03-29 09:03:52 -0700951 REPORTER_ASSERT(reporter,
robertphillipse5c1e3c2014-06-27 08:59:26 -0700952 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight));
953 }
954}
955
Mike Reede5826002018-01-19 12:28:52 -0500956void test_read_rrect(skiatest::Reporter* reporter, const SkRRect& rrect, bool shouldEqualSrc) {
Brian Salomonfb6a7892017-09-20 11:05:49 -0400957 // 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));
Mike Reede5826002018-01-19 12:28:52 -0500963 REPORTER_ASSERT(reporter, size == SkRRect::kSizeInMemory);
964 REPORTER_ASSERT(reporter, deserialized.isValid());
965 if (shouldEqualSrc) {
966 REPORTER_ASSERT(reporter, rrect == deserialized);
Brian Salomonfb6a7892017-09-20 11:05:49 -0400967 }
968}
969
970static 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);
Brian Salomon4b0e3642017-11-20 21:56:29 -05001014 *innerRadius = -10.f;
1015 test_read_rrect(reporter, rrect, false);
Brian Salomonfb6a7892017-09-20 11:05:49 -04001016}
1017
Michael Ludwigeaeb9962020-04-17 12:11:21 -04001018static 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 SkRect 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
Michael Ludwig76312fb2020-04-20 18:21:42 -04001106namespace {
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
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
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} // anonymous
1168
1169static 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 // Failed intersection operations:
1211
1212 // A and B's bounds do not intersect
1213 verify_failure(reporter, a.makeOffset(32.f, 0.f), b);
1214 // A and B's bounds intersect, but corner curves do not -> no intersection
1215 verify_failure(reporter, a.makeOffset(11.5f, -11.5f), b);
1216 // A is empty -> no intersection
1217 verify_failure(reporter, SkRRect::MakeEmpty(), b);
1218 // A is contained in B, but is too close to the corner curves for the conservative
1219 // approximations to construct a valid round rect intersection.
1220 verify_failure(reporter, make_inset(b, 0.3f, 0.3f), b);
1221 // A intersects a straight edge, but not far enough for B to contain A's corners
1222 verify_failure(reporter, a.makeOffset(2.5f, 0.f), b);
1223 verify_failure(reporter, a.makeOffset(-2.5f, 0.f), b);
1224 // And vice versa for B into A
1225 verify_failure(reporter, a, b.makeOffset(0.f, 1.5f));
1226 verify_failure(reporter, a, b.makeOffset(0.f, -1.5f));
1227 // A intersects a straight edge and part of B's corner
1228 verify_failure(reporter, a.makeOffset(5.f, -2.f), b);
1229 verify_failure(reporter, a.makeOffset(-5.f, -2.f), b);
1230 verify_failure(reporter, a.makeOffset(5.f, 2.f), b);
1231 verify_failure(reporter, a.makeOffset(-5.f, 2.f), b);
1232 // And vice versa
1233 verify_failure(reporter, a, b.makeOffset(3.f, -5.f));
1234 verify_failure(reporter, a, b.makeOffset(-3.f, -5.f));
1235 verify_failure(reporter, a, b.makeOffset(3.f, 5.f));
1236 verify_failure(reporter, a, b.makeOffset(-3.f, 5.f));
1237 // A intersects B on a corner, but the corner curves overlap each other
1238 verify_failure(reporter, a.makeOffset(8.f, 10.f), b);
1239 verify_failure(reporter, a.makeOffset(-8.f, 10.f), b);
1240 verify_failure(reporter, a.makeOffset(8.f, -10.f), b);
1241 verify_failure(reporter, a.makeOffset(-8.f, -10.f), b);
Michael Ludwig29c2f712020-05-05 18:29:32 -04001242
1243 // Another variant of corners overlapping, this is two circles of radius r that overlap by r
1244 // pixels (e.g. the leftmost point of the right circle touches the center of the left circle).
1245 // The key difference with the above case is that the intersection of the circle bounds have
1246 // corners that are contained in both circles, but because it is only r wide, can not satisfy
1247 // all corners having radii = r.
1248 float r = 100.f;
1249 a = SkRRect::MakeOval(SkRect::MakeWH(2*r, 2*r));
1250 verify_failure(reporter, a, a.makeOffset(r, 0.f));
Michael Ludwig76312fb2020-04-20 18:21:42 -04001251}
1252
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001253DEF_TEST(RoundRect, reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +00001254 test_round_rect_basic(reporter);
1255 test_round_rect_rects(reporter);
1256 test_round_rect_ovals(reporter);
1257 test_round_rect_general(reporter);
1258 test_round_rect_iffy_parameters(reporter);
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +00001259 test_inset(reporter);
robertphillips@google.com32c1b662013-04-25 12:23:00 +00001260 test_round_rect_contains_rect(reporter);
scroggo@google.com20e3cd22013-11-05 15:54:42 +00001261 test_round_rect_transform(reporter);
robertphillipse5c1e3c2014-06-27 08:59:26 -07001262 test_issue_2696(reporter);
robertphillips2a679ae2015-03-13 09:53:01 -07001263 test_tricky_radii(reporter);
reed694b0d12015-02-13 14:33:02 -08001264 test_empty_crbug_458524(reporter);
robertphillips05302f82015-09-29 11:24:07 -07001265 test_empty(reporter);
Brian Salomonfb6a7892017-09-20 11:05:49 -04001266 test_read(reporter);
Michael Ludwigeaeb9962020-04-17 12:11:21 -04001267 test_inner_bounds(reporter);
Michael Ludwig76312fb2020-04-20 18:21:42 -04001268 test_conservative_intersection(reporter);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +00001269}