blob: 2e31cf1b65c1ccff52de490d666ddf47db483827 [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
scroggo@google.com20e3cd22013-11-05 15:54:42 +00008#include "SkMatrix.h"
Cary Clarkdf429f32017-11-08 11:44:31 -05009#include "SkPointPriv.h"
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000010#include "SkRRect.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000011#include "Test.h"
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000012
robertphillips2a679ae2015-03-13 09:53:01 -070013static void test_tricky_radii(skiatest::Reporter* reporter) {
14 {
15 // crbug.com/458522
16 SkRRect rr;
17 const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
18 const SkScalar rad = 12814;
19 const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } };
20 rr.setRectRadii(bounds, vec);
21 }
22
23 {
24 // crbug.com//463920
25 SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0);
26 SkVector radii[4] = {
27 { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f }
28 };
29 SkRRect rr;
30 rr.setRectRadii(r, radii);
31
32 REPORTER_ASSERT(reporter, (double) rr.radii(SkRRect::kUpperRight_Corner).fY +
33 (double) rr.radii(SkRRect::kLowerRight_Corner).fY <=
34 rr.height());
35 }
reed694b0d12015-02-13 14:33:02 -080036}
37
38static void test_empty_crbug_458524(skiatest::Reporter* reporter) {
39 SkRRect rr;
40 const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
41 const SkScalar rad = 40;
42 rr.setRectXY(bounds, rad, rad);
43
44 SkRRect other;
45 SkMatrix matrix;
46 matrix.setScale(0, 1);
47 rr.transform(matrix, &other);
48 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == other.getType());
49}
50
robertphillips05302f82015-09-29 11:24:07 -070051// Test that all the SkRRect entry points correctly handle un-sorted and
52// zero-sized input rects
53static void test_empty(skiatest::Reporter* reporter) {
54 static const SkRect oooRects[] = { // out of order
55 { 100, 0, 0, 100 }, // ooo horizontal
56 { 0, 100, 100, 0 }, // ooo vertical
57 { 100, 100, 0, 0 }, // ooo both
58 };
59
60 static const SkRect emptyRects[] = {
61 { 100, 100, 100, 200 }, // empty horizontal
62 { 100, 100, 200, 100 }, // empty vertical
63 { 100, 100, 100, 100 }, // empty both
64 { 0, 0, 0, 0 } // setEmpty-empty
65 };
66
67 static const SkVector radii[4] = { { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 } };
68
69 SkRRect r;
70
71 for (size_t i = 0; i < SK_ARRAY_COUNT(oooRects); ++i) {
72 r.setRect(oooRects[i]);
73 REPORTER_ASSERT(reporter, !r.isEmpty());
74
75 r.setOval(oooRects[i]);
76 REPORTER_ASSERT(reporter, !r.isEmpty());
77
78 r.setRectXY(oooRects[i], 1, 2);
79 REPORTER_ASSERT(reporter, !r.isEmpty());
80
81 r.setNinePatch(oooRects[i], 0, 1, 2, 3);
82 REPORTER_ASSERT(reporter, !r.isEmpty());
83
84 r.setRectRadii(oooRects[i], radii);
85 REPORTER_ASSERT(reporter, !r.isEmpty());
86 }
87
88 for (size_t i = 0; i < SK_ARRAY_COUNT(emptyRects); ++i) {
89 r.setRect(emptyRects[i]);
90 REPORTER_ASSERT(reporter, r.isEmpty());
91
92 r.setOval(emptyRects[i]);
93 REPORTER_ASSERT(reporter, r.isEmpty());
94
95 r.setRectXY(emptyRects[i], 1, 2);
96 REPORTER_ASSERT(reporter, r.isEmpty());
97
98 r.setNinePatch(emptyRects[i], 0, 1, 2, 3);
99 REPORTER_ASSERT(reporter, r.isEmpty());
100
101 r.setRectRadii(emptyRects[i], radii);
102 REPORTER_ASSERT(reporter, r.isEmpty());
103 }
104}
105
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000106static const SkScalar kWidth = 100.0f;
107static const SkScalar kHeight = 100.0f;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000108
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000109static void test_inset(skiatest::Reporter* reporter) {
110 SkRRect rr, rr2;
111 SkRect r = { 0, 0, 100, 100 };
112
113 rr.setRect(r);
114 rr.inset(-20, -20, &rr2);
115 REPORTER_ASSERT(reporter, rr2.isRect());
116
117 rr.inset(20, 20, &rr2);
118 REPORTER_ASSERT(reporter, rr2.isRect());
skia.committer@gmail.com1a60dab2012-12-24 02:01:25 +0000119
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000120 rr.inset(r.width()/2, r.height()/2, &rr2);
121 REPORTER_ASSERT(reporter, rr2.isEmpty());
122
123 rr.setRectXY(r, 20, 20);
124 rr.inset(19, 19, &rr2);
125 REPORTER_ASSERT(reporter, rr2.isSimple());
126 rr.inset(20, 20, &rr2);
127 REPORTER_ASSERT(reporter, rr2.isRect());
128}
129
robertphillipsfe1b1802015-02-24 11:18:48 -0800130
131static void test_9patch_rrect(skiatest::Reporter* reporter,
132 const SkRect& rect,
133 SkScalar l, SkScalar t, SkScalar r, SkScalar b,
134 bool checkRadii) {
135 SkRRect rr;
136 rr.setNinePatch(rect, l, t, r, b);
137
138 REPORTER_ASSERT(reporter, SkRRect::kNinePatch_Type == rr.type());
139 REPORTER_ASSERT(reporter, rr.rect() == rect);
140
141 if (checkRadii) {
142 // This test doesn't hold if the radii will be rescaled by SkRRect
143 SkRect ninePatchRadii = { l, t, r, b };
144 SkPoint rquad[4];
145 ninePatchRadii.toQuad(rquad);
146 for (int i = 0; i < 4; ++i) {
147 REPORTER_ASSERT(reporter, rquad[i] == rr.radii((SkRRect::Corner) i));
148 }
149 }
150 SkRRect rr2; // construct the same RR using the most general set function
151 SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } };
152 rr2.setRectRadii(rect, radii);
153 REPORTER_ASSERT(reporter, rr2 == rr && rr2.getType() == rr.getType());
154}
155
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000156// Test out the basic API entry points
157static void test_round_rect_basic(skiatest::Reporter* reporter) {
158 // Test out initialization methods
reed@google.com2b57dc62013-01-08 13:23:32 +0000159 SkPoint zeroPt = { 0, 0 };
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000160 SkRRect empty;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000161
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000162 empty.setEmpty();
163
164 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
165 REPORTER_ASSERT(reporter, empty.rect().isEmpty());
166
167 for (int i = 0; i < 4; ++i) {
168 REPORTER_ASSERT(reporter, zeroPt == empty.radii((SkRRect::Corner) i));
169 }
170
171 //----
172 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
173
174 SkRRect rr1;
175 rr1.setRect(rect);
176
177 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
178 REPORTER_ASSERT(reporter, rr1.rect() == rect);
179
180 for (int i = 0; i < 4; ++i) {
181 REPORTER_ASSERT(reporter, zeroPt == rr1.radii((SkRRect::Corner) i));
182 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000183 SkRRect rr1_2; // construct the same RR using the most general set function
184 SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
185 rr1_2.setRectRadii(rect, rr1_2_radii);
186 REPORTER_ASSERT(reporter, rr1_2 == rr1 && rr1_2.getType() == rr1.getType());
187 SkRRect rr1_3; // construct the same RR using the nine patch set function
188 rr1_3.setNinePatch(rect, 0, 0, 0, 0);
189 REPORTER_ASSERT(reporter, rr1_3 == rr1 && rr1_3.getType() == rr1.getType());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000190
191 //----
192 SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) };
193 SkRRect rr2;
194 rr2.setOval(rect);
195
196 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr2.type());
197 REPORTER_ASSERT(reporter, rr2.rect() == rect);
198
199 for (int i = 0; i < 4; ++i) {
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000200 REPORTER_ASSERT(reporter,
Cary Clarkdf429f32017-11-08 11:44:31 -0500201 SkPointPriv::EqualsWithinTolerance(rr2.radii((SkRRect::Corner) i),
202 halfPoint));
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000203 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000204 SkRRect rr2_2; // construct the same RR using the most general set function
205 SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY },
206 { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY } };
207 rr2_2.setRectRadii(rect, rr2_2_radii);
208 REPORTER_ASSERT(reporter, rr2_2 == rr2 && rr2_2.getType() == rr2.getType());
209 SkRRect rr2_3; // construct the same RR using the nine patch set function
210 rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY);
211 REPORTER_ASSERT(reporter, rr2_3 == rr2 && rr2_3.getType() == rr2.getType());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000212
213 //----
214 SkPoint p = { 5, 5 };
215 SkRRect rr3;
216 rr3.setRectXY(rect, p.fX, p.fY);
217
218 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr3.type());
219 REPORTER_ASSERT(reporter, rr3.rect() == rect);
220
221 for (int i = 0; i < 4; ++i) {
222 REPORTER_ASSERT(reporter, p == rr3.radii((SkRRect::Corner) i));
223 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000224 SkRRect rr3_2; // construct the same RR using the most general set function
225 SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } };
226 rr3_2.setRectRadii(rect, rr3_2_radii);
227 REPORTER_ASSERT(reporter, rr3_2 == rr3 && rr3_2.getType() == rr3.getType());
228 SkRRect rr3_3; // construct the same RR using the nine patch set function
229 rr3_3.setNinePatch(rect, 5, 5, 5, 5);
230 REPORTER_ASSERT(reporter, rr3_3 == rr3 && rr3_3.getType() == rr3.getType());
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000231
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000232 //----
robertphillipsfe1b1802015-02-24 11:18:48 -0800233 test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000234
robertphillipsfe1b1802015-02-24 11:18:48 -0800235 {
236 // Test out the rrect from skia:3466
237 SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000238
robertphillipsfe1b1802015-02-24 11:18:48 -0800239 test_9patch_rrect(reporter,
240 rect2,
241 0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f,
242 false);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000243 }
244
245 //----
246 SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } };
247
248 SkRRect rr5;
249 rr5.setRectRadii(rect, radii2);
250
251 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr5.type());
252 REPORTER_ASSERT(reporter, rr5.rect() == rect);
253
254 for (int i = 0; i < 4; ++i) {
255 REPORTER_ASSERT(reporter, radii2[i] == rr5.radii((SkRRect::Corner) i));
256 }
257
258 // Test out == & !=
259 REPORTER_ASSERT(reporter, empty != rr3);
robertphillipsfe1b1802015-02-24 11:18:48 -0800260 REPORTER_ASSERT(reporter, rr3 != rr5);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000261}
262
263// Test out the cases when the RR degenerates to a rect
264static void test_round_rect_rects(skiatest::Reporter* reporter) {
265 SkRect r;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000266
267 //----
268 SkRRect empty;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000269
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000270 empty.setEmpty();
271
272 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
273 r = empty.rect();
274 REPORTER_ASSERT(reporter, 0 == r.fLeft && 0 == r.fTop && 0 == r.fRight && 0 == r.fBottom);
275
276 //----
277 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
278 SkRRect rr1;
279 rr1.setRectXY(rect, 0, 0);
280
281 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
282 r = rr1.rect();
283 REPORTER_ASSERT(reporter, rect == r);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000284
285 //----
286 SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
287
288 SkRRect rr2;
289 rr2.setRectRadii(rect, radii);
290
291 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
292 r = rr2.rect();
293 REPORTER_ASSERT(reporter, rect == r);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000294
295 //----
296 SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
297
298 SkRRect rr3;
299 rr3.setRectRadii(rect, radii2);
300 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr3.type());
301}
302
303// Test out the cases when the RR degenerates to an oval
304static void test_round_rect_ovals(skiatest::Reporter* reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000305 //----
306 SkRect oval;
307 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
308 SkRRect rr1;
309 rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight));
310
311 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr1.type());
312 oval = rr1.rect();
313 REPORTER_ASSERT(reporter, oval == rect);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000314}
315
316// Test out the non-degenerate RR cases
317static void test_round_rect_general(skiatest::Reporter* reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000318 //----
319 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
320 SkRRect rr1;
321 rr1.setRectXY(rect, 20, 20);
322
323 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr1.type());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000324
325 //----
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000326 SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
327
328 SkRRect rr2;
329 rr2.setRectRadii(rect, radii);
330
331 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr2.type());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000332}
333
334// Test out questionable-parameter handling
335static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) {
336
337 // When the radii exceed the base rect they are proportionally scaled down
338 // to fit
339 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
340 SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } };
341
342 SkRRect rr1;
343 rr1.setRectRadii(rect, radii);
344
345 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr1.type());
346
347 const SkPoint& p = rr1.radii(SkRRect::kUpperLeft_Corner);
348
349 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fX, 33.33333f));
350 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fY, 66.66666f));
351
352 // Negative radii should be capped at zero
353 SkRRect rr2;
354 rr2.setRectXY(rect, -10, -20);
355
356 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
357
358 const SkPoint& p2 = rr2.radii(SkRRect::kUpperLeft_Corner);
359
360 REPORTER_ASSERT(reporter, 0.0f == p2.fX);
361 REPORTER_ASSERT(reporter, 0.0f == p2.fY);
362}
363
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000364// Move a small box from the start position by (stepX, stepY) 'numSteps' times
365// testing for containment in 'rr' at each step.
366static void test_direction(skiatest::Reporter* reporter, const SkRRect &rr,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000367 SkScalar initX, int stepX, SkScalar initY, int stepY,
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000368 int numSteps, const bool* contains) {
369 SkScalar x = initX, y = initY;
370 for (int i = 0; i < numSteps; ++i) {
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000371 SkRect test = SkRect::MakeXYWH(x, y,
372 stepX ? SkIntToScalar(stepX) : SK_Scalar1,
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000373 stepY ? SkIntToScalar(stepY) : SK_Scalar1);
374 test.sort();
375
376 REPORTER_ASSERT(reporter, contains[i] == rr.contains(test));
377
378 x += stepX;
379 y += stepY;
380 }
381}
382
383// Exercise the RR's contains rect method
384static void test_round_rect_contains_rect(skiatest::Reporter* reporter) {
385
386 static const int kNumRRects = 4;
387 static const SkVector gRadii[kNumRRects][4] = {
388 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // rect
389 { { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 } }, // circle
390 { { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 10 } }, // simple
391 { { 0, 0 }, { 20, 20 }, { 10, 10 }, { 30, 30 } } // complex
392 };
393
394 SkRRect rrects[kNumRRects];
395 for (int i = 0; i < kNumRRects; ++i) {
396 rrects[i].setRectRadii(SkRect::MakeWH(40, 40), gRadii[i]);
397 }
398
399 // First test easy outs - boxes that are obviously out on
400 // each corner and edge
401 static const SkRect easyOuts[] = {
402 { -5, -5, 5, 5 }, // NW
403 { 15, -5, 20, 5 }, // N
404 { 35, -5, 45, 5 }, // NE
405 { 35, 15, 45, 20 }, // E
406 { 35, 45, 35, 45 }, // SE
407 { 15, 35, 20, 45 }, // S
408 { -5, 35, 5, 45 }, // SW
409 { -5, 15, 5, 20 } // W
410 };
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000411
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000412 for (int i = 0; i < kNumRRects; ++i) {
413 for (size_t j = 0; j < SK_ARRAY_COUNT(easyOuts); ++j) {
414 REPORTER_ASSERT(reporter, !rrects[i].contains(easyOuts[j]));
415 }
416 }
417
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000418 // Now test non-trivial containment. For each compass
419 // point walk a 1x1 rect in from the edge of the bounding
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000420 // rect
421 static const int kNumSteps = 15;
422 bool answers[kNumRRects][8][kNumSteps] = {
423 // all the test rects are inside the degenerate rrect
424 {
425 // rect
426 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
427 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
428 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
429 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
430 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
431 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
432 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
433 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
434 },
435 // for the circle we expect 6 blocks to be out on the
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000436 // corners (then the rest in) and only the first block
437 // out on the vertical and horizontal axes (then
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000438 // the rest in)
439 {
440 // circle
441 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
442 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
443 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
444 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
445 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
446 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
447 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
448 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
449 },
450 // for the simple round rect we expect 3 out on
451 // the corners (then the rest in) and no blocks out
452 // on the vertical and horizontal axes
453 {
454 // simple RR
455 { 0, 0, 0, 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 { 0, 0, 0, 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 { 0, 0, 0, 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 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
462 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
463 },
464 // for the complex case the answer is different for each direction
465 {
466 // complex RR
467 // all in for NW (rect) corner (same as rect case)
468 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
469 // only first block out for N (same as circle case)
470 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
471 // first 6 blocks out for NE (same as circle case)
472 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
473 // only first block out for E (same as circle case)
474 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
475 // first 3 blocks out for SE (same as simple case)
476 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
477 // first two blocks out for S
478 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
479 // first 9 blocks out for SW
480 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
481 // first two blocks out for W (same as S)
482 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
483 }
484 };
485
486 for (int i = 0; i < kNumRRects; ++i) {
487 test_direction(reporter, rrects[i], 0, 1, 0, 1, kNumSteps, answers[i][0]); // NW
488 test_direction(reporter, rrects[i], 19.5f, 0, 0, 1, kNumSteps, answers[i][1]); // N
489 test_direction(reporter, rrects[i], 40, -1, 0, 1, kNumSteps, answers[i][2]); // NE
490 test_direction(reporter, rrects[i], 40, -1, 19.5f, 0, kNumSteps, answers[i][3]); // E
491 test_direction(reporter, rrects[i], 40, -1, 40, -1, kNumSteps, answers[i][4]); // SE
492 test_direction(reporter, rrects[i], 19.5f, 0, 40, -1, kNumSteps, answers[i][5]); // S
493 test_direction(reporter, rrects[i], 0, 1, 40, -1, kNumSteps, answers[i][6]); // SW
494 test_direction(reporter, rrects[i], 0, 1, 19.5f, 0, kNumSteps, answers[i][7]); // W
495 }
496}
497
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000498// Called for a matrix that should cause SkRRect::transform to fail.
499static void assert_transform_failure(skiatest::Reporter* reporter, const SkRRect& orig,
500 const SkMatrix& matrix) {
501 // The test depends on the fact that the original is not empty.
502 SkASSERT(!orig.isEmpty());
503 SkRRect dst;
504 dst.setEmpty();
505
506 const SkRRect copyOfDst = dst;
507 const SkRRect copyOfOrig = orig;
508 bool success = orig.transform(matrix, &dst);
509 // This transform should fail.
510 REPORTER_ASSERT(reporter, !success);
511 // Since the transform failed, dst should be unchanged.
512 REPORTER_ASSERT(reporter, copyOfDst == dst);
513 // original should not be modified.
514 REPORTER_ASSERT(reporter, copyOfOrig == orig);
515 REPORTER_ASSERT(reporter, orig != dst);
516}
517
518#define GET_RADII \
519 const SkVector& origUL = orig.radii(SkRRect::kUpperLeft_Corner); \
520 const SkVector& origUR = orig.radii(SkRRect::kUpperRight_Corner); \
521 const SkVector& origLR = orig.radii(SkRRect::kLowerRight_Corner); \
522 const SkVector& origLL = orig.radii(SkRRect::kLowerLeft_Corner); \
523 const SkVector& dstUL = dst.radii(SkRRect::kUpperLeft_Corner); \
524 const SkVector& dstUR = dst.radii(SkRRect::kUpperRight_Corner); \
525 const SkVector& dstLR = dst.radii(SkRRect::kLowerRight_Corner); \
526 const SkVector& dstLL = dst.radii(SkRRect::kLowerLeft_Corner)
527
528// Called to test various transforms on a single SkRRect.
529static void test_transform_helper(skiatest::Reporter* reporter, const SkRRect& orig) {
530 SkRRect dst;
531 dst.setEmpty();
532
533 // The identity matrix will duplicate the rrect.
534 bool success = orig.transform(SkMatrix::I(), &dst);
535 REPORTER_ASSERT(reporter, success);
536 REPORTER_ASSERT(reporter, orig == dst);
537
538 // Skew and Perspective make transform fail.
539 SkMatrix matrix;
540 matrix.reset();
541 matrix.setSkewX(SkIntToScalar(2));
542 assert_transform_failure(reporter, orig, matrix);
543
544 matrix.reset();
545 matrix.setSkewY(SkIntToScalar(3));
546 assert_transform_failure(reporter, orig, matrix);
547
548 matrix.reset();
reed3f43f8a2015-01-20 19:58:36 -0800549 matrix.setPerspX(4);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000550 assert_transform_failure(reporter, orig, matrix);
551
552 matrix.reset();
reed3f43f8a2015-01-20 19:58:36 -0800553 matrix.setPerspY(5);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000554 assert_transform_failure(reporter, orig, matrix);
555
556 // Rotation fails.
557 matrix.reset();
558 matrix.setRotate(SkIntToScalar(90));
559 assert_transform_failure(reporter, orig, matrix);
560 matrix.setRotate(SkIntToScalar(37));
561 assert_transform_failure(reporter, orig, matrix);
562
563 // Translate will keep the rect moved, but otherwise the same.
564 matrix.reset();
565 SkScalar translateX = SkIntToScalar(32);
566 SkScalar translateY = SkIntToScalar(15);
567 matrix.setTranslateX(translateX);
568 matrix.setTranslateY(translateY);
569 dst.setEmpty();
570 success = orig.transform(matrix, &dst);
571 REPORTER_ASSERT(reporter, success);
572 for (int i = 0; i < 4; ++i) {
573 REPORTER_ASSERT(reporter,
574 orig.radii((SkRRect::Corner) i) == dst.radii((SkRRect::Corner) i));
575 }
576 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
577 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
578 REPORTER_ASSERT(reporter, dst.rect().left() == orig.rect().left() + translateX);
579 REPORTER_ASSERT(reporter, dst.rect().top() == orig.rect().top() + translateY);
580
581 // Keeping the translation, but adding skew will make transform fail.
582 matrix.setSkewY(SkIntToScalar(7));
583 assert_transform_failure(reporter, orig, matrix);
584
585 // Scaling in -x will flip the round rect horizontally.
586 matrix.reset();
587 matrix.setScaleX(SkIntToScalar(-1));
588 dst.setEmpty();
589 success = orig.transform(matrix, &dst);
590 REPORTER_ASSERT(reporter, success);
591 {
592 GET_RADII;
593 // Radii have swapped in x.
594 REPORTER_ASSERT(reporter, origUL == dstUR);
595 REPORTER_ASSERT(reporter, origUR == dstUL);
596 REPORTER_ASSERT(reporter, origLR == dstLL);
597 REPORTER_ASSERT(reporter, origLL == dstLR);
598 }
599 // Width and height remain the same.
600 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
601 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
602 // Right and left have swapped (sort of)
603 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
604 // Top has stayed the same.
605 REPORTER_ASSERT(reporter, orig.rect().top() == dst.rect().top());
606
607 // Keeping the scale, but adding a persp will make transform fail.
reed3f43f8a2015-01-20 19:58:36 -0800608 matrix.setPerspX(7);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000609 assert_transform_failure(reporter, orig, matrix);
610
611 // Scaling in -y will flip the round rect vertically.
612 matrix.reset();
613 matrix.setScaleY(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 y.
620 REPORTER_ASSERT(reporter, origUL == dstLL);
621 REPORTER_ASSERT(reporter, origUR == dstLR);
622 REPORTER_ASSERT(reporter, origLR == dstUR);
623 REPORTER_ASSERT(reporter, origLL == dstUL);
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 // Top and bottom have swapped (sort of)
629 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
630 // Left has stayed the same.
631 REPORTER_ASSERT(reporter, orig.rect().left() == dst.rect().left());
632
633 // Scaling in -x and -y will swap in both directions.
634 matrix.reset();
635 matrix.setScaleY(SkIntToScalar(-1));
636 matrix.setScaleX(SkIntToScalar(-1));
637 dst.setEmpty();
638 success = orig.transform(matrix, &dst);
639 REPORTER_ASSERT(reporter, success);
640 {
641 GET_RADII;
642 REPORTER_ASSERT(reporter, origUL == dstLR);
643 REPORTER_ASSERT(reporter, origUR == dstLL);
644 REPORTER_ASSERT(reporter, origLR == dstUL);
645 REPORTER_ASSERT(reporter, origLL == dstUR);
646 }
647 // Width and height remain the same.
648 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
649 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
650 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
651 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
652
653 // Scale in both directions.
654 SkScalar xScale = SkIntToScalar(3);
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000655 SkScalar yScale = 3.2f;
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000656 matrix.reset();
657 matrix.setScaleX(xScale);
658 matrix.setScaleY(yScale);
659 dst.setEmpty();
660 success = orig.transform(matrix, &dst);
661 REPORTER_ASSERT(reporter, success);
662 // Radii are scaled.
663 for (int i = 0; i < 4; ++i) {
664 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX,
Mike Reeddf85c382017-02-14 10:59:19 -0500665 orig.radii((SkRRect::Corner) i).fX * xScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000666 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY,
Mike Reeddf85c382017-02-14 10:59:19 -0500667 orig.radii((SkRRect::Corner) i).fY * yScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000668 }
669 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(),
Mike Reeddf85c382017-02-14 10:59:19 -0500670 orig.rect().width() * xScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000671 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(),
Mike Reeddf85c382017-02-14 10:59:19 -0500672 orig.rect().height() * yScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000673 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(),
Mike Reeddf85c382017-02-14 10:59:19 -0500674 orig.rect().left() * xScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000675 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(),
Mike Reeddf85c382017-02-14 10:59:19 -0500676 orig.rect().top() * yScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000677}
678
679static void test_round_rect_transform(skiatest::Reporter* reporter) {
680 SkRRect rrect;
681 {
682 SkRect r = { 0, 0, kWidth, kHeight };
683 rrect.setRectXY(r, SkIntToScalar(4), SkIntToScalar(7));
684 test_transform_helper(reporter, rrect);
685 }
686 {
687 SkRect r = { SkIntToScalar(5), SkIntToScalar(15),
688 SkIntToScalar(27), SkIntToScalar(34) };
689 SkVector radii[4] = { { 0, SkIntToScalar(1) },
690 { SkIntToScalar(2), SkIntToScalar(3) },
691 { SkIntToScalar(4), SkIntToScalar(5) },
692 { SkIntToScalar(6), SkIntToScalar(7) } };
693 rrect.setRectRadii(r, radii);
694 test_transform_helper(reporter, rrect);
695 }
696}
697
halcanary9d524f22016-03-29 09:03:52 -0700698// Test out the case where an oval already off in space is translated/scaled
robertphillipse5c1e3c2014-06-27 08:59:26 -0700699// further off into space - yielding numerical issues when the rect & radii
700// are transformed separatly
701// BUG=skia:2696
702static void test_issue_2696(skiatest::Reporter* reporter) {
703 SkRRect rrect;
704 SkRect r = { 28443.8594f, 53.1428604f, 28446.7148f, 56.0000038f };
705 rrect.setOval(r);
706
707 SkMatrix xform;
708 xform.setAll(2.44f, 0.0f, 485411.7f,
709 0.0f, 2.44f, -438.7f,
710 0.0f, 0.0f, 1.0f);
711 SkRRect dst;
712
713 bool success = rrect.transform(xform, &dst);
714 REPORTER_ASSERT(reporter, success);
715
716 SkScalar halfWidth = SkScalarHalf(dst.width());
717 SkScalar halfHeight = SkScalarHalf(dst.height());
718
719 for (int i = 0; i < 4; ++i) {
halcanary9d524f22016-03-29 09:03:52 -0700720 REPORTER_ASSERT(reporter,
robertphillipse5c1e3c2014-06-27 08:59:26 -0700721 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth));
halcanary9d524f22016-03-29 09:03:52 -0700722 REPORTER_ASSERT(reporter,
robertphillipse5c1e3c2014-06-27 08:59:26 -0700723 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight));
724 }
725}
726
Brian Salomonfb6a7892017-09-20 11:05:49 -0400727void test_read_rrect(skiatest::Reporter* reporter, const SkRRect& rrect, bool shouldSucceed) {
728 // It would be cleaner to call rrect.writeToMemory into a buffer. However, writeToMemory asserts
729 // that the rrect is valid and our caller may have fiddled with the internals of rrect to make
730 // it invalid.
731 const void* buffer = reinterpret_cast<const void*>(&rrect);
732 SkRRect deserialized;
733 size_t size = deserialized.readFromMemory(buffer, sizeof(SkRRect));
734 if (shouldSucceed) {
735 REPORTER_ASSERT(reporter, size == SkRRect::kSizeInMemory);
736 if (size) {
737 REPORTER_ASSERT(reporter, rrect == deserialized);
738 REPORTER_ASSERT(reporter, rrect.getType() == deserialized.getType());
739 }
740 } else {
741 REPORTER_ASSERT(reporter, !size);
742 }
743}
744
745static void test_read(skiatest::Reporter* reporter) {
746 static const SkRect kRect = {10.f, 10.f, 20.f, 20.f};
747 static const SkRect kNaNRect = {10.f, 10.f, 20.f, SK_ScalarNaN};
748 static const SkRect kInfRect = {10.f, 10.f, SK_ScalarInfinity, 20.f};
749 SkRRect rrect;
750
751 test_read_rrect(reporter, SkRRect::MakeEmpty(), true);
752 test_read_rrect(reporter, SkRRect::MakeRect(kRect), true);
753 // These get coerced to empty.
754 test_read_rrect(reporter, SkRRect::MakeRect(kInfRect), true);
755 test_read_rrect(reporter, SkRRect::MakeRect(kNaNRect), true);
756
757 rrect.setRect(kRect);
758 SkRect* innerRect = reinterpret_cast<SkRect*>(&rrect);
759 SkASSERT(*innerRect == kRect);
760 *innerRect = kInfRect;
761 test_read_rrect(reporter, rrect, false);
762 *innerRect = kNaNRect;
763 test_read_rrect(reporter, rrect, false);
764
765 test_read_rrect(reporter, SkRRect::MakeOval(kRect), true);
766 test_read_rrect(reporter, SkRRect::MakeOval(kInfRect), true);
767 test_read_rrect(reporter, SkRRect::MakeOval(kNaNRect), true);
768 rrect.setOval(kRect);
769 *innerRect = kInfRect;
770 test_read_rrect(reporter, rrect, false);
771 *innerRect = kNaNRect;
772 test_read_rrect(reporter, rrect, false);
773
774 test_read_rrect(reporter, SkRRect::MakeRectXY(kRect, 5.f, 5.f), true);
775 // rrect should scale down the radii to make this legal
776 test_read_rrect(reporter, SkRRect::MakeRectXY(kRect, 5.f, 400.f), true);
777
778 static const SkVector kRadii[4] = {{0.5f, 1.f}, {1.5f, 2.f}, {2.5f, 3.f}, {3.5f, 4.f}};
779 rrect.setRectRadii(kRect, kRadii);
780 test_read_rrect(reporter, rrect, true);
781 SkScalar* innerRadius = reinterpret_cast<SkScalar*>(&rrect) + 6;
782 SkASSERT(*innerRadius == 1.5f);
783 *innerRadius = 400.f;
784 test_read_rrect(reporter, rrect, false);
785 *innerRadius = SK_ScalarInfinity;
786 test_read_rrect(reporter, rrect, false);
787 *innerRadius = SK_ScalarNaN;
788 test_read_rrect(reporter, rrect, false);
789}
790
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000791DEF_TEST(RoundRect, reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000792 test_round_rect_basic(reporter);
793 test_round_rect_rects(reporter);
794 test_round_rect_ovals(reporter);
795 test_round_rect_general(reporter);
796 test_round_rect_iffy_parameters(reporter);
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000797 test_inset(reporter);
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000798 test_round_rect_contains_rect(reporter);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000799 test_round_rect_transform(reporter);
robertphillipse5c1e3c2014-06-27 08:59:26 -0700800 test_issue_2696(reporter);
robertphillips2a679ae2015-03-13 09:53:01 -0700801 test_tricky_radii(reporter);
reed694b0d12015-02-13 14:33:02 -0800802 test_empty_crbug_458524(reporter);
robertphillips05302f82015-09-29 11:24:07 -0700803 test_empty(reporter);
Brian Salomonfb6a7892017-09-20 11:05:49 -0400804 test_read(reporter);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000805}