blob: 8d8f76fbf0084ee51bd32353cfd7d2f7cd097b19 [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"
robertphillips@google.com5985e7c2012-11-29 13:24:55 +00009#include "SkRRect.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000010#include "Test.h"
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000011
robertphillips2a679ae2015-03-13 09:53:01 -070012static void test_tricky_radii(skiatest::Reporter* reporter) {
13 {
14 // crbug.com/458522
15 SkRRect rr;
16 const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
17 const SkScalar rad = 12814;
18 const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } };
19 rr.setRectRadii(bounds, vec);
20 }
21
22 {
23 // crbug.com//463920
24 SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0);
25 SkVector radii[4] = {
26 { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f }
27 };
28 SkRRect rr;
29 rr.setRectRadii(r, radii);
30
31 REPORTER_ASSERT(reporter, (double) rr.radii(SkRRect::kUpperRight_Corner).fY +
32 (double) rr.radii(SkRRect::kLowerRight_Corner).fY <=
33 rr.height());
34 }
reed694b0d12015-02-13 14:33:02 -080035}
36
37static void test_empty_crbug_458524(skiatest::Reporter* reporter) {
38 SkRRect rr;
39 const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
40 const SkScalar rad = 40;
41 rr.setRectXY(bounds, rad, rad);
42
43 SkRRect other;
44 SkMatrix matrix;
45 matrix.setScale(0, 1);
46 rr.transform(matrix, &other);
47 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == other.getType());
48}
49
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +000050static const SkScalar kWidth = 100.0f;
51static const SkScalar kHeight = 100.0f;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000052
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +000053static void test_inset(skiatest::Reporter* reporter) {
54 SkRRect rr, rr2;
55 SkRect r = { 0, 0, 100, 100 };
56
57 rr.setRect(r);
58 rr.inset(-20, -20, &rr2);
59 REPORTER_ASSERT(reporter, rr2.isRect());
60
61 rr.inset(20, 20, &rr2);
62 REPORTER_ASSERT(reporter, rr2.isRect());
skia.committer@gmail.com1a60dab2012-12-24 02:01:25 +000063
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +000064 rr.inset(r.width()/2, r.height()/2, &rr2);
65 REPORTER_ASSERT(reporter, rr2.isEmpty());
66
67 rr.setRectXY(r, 20, 20);
68 rr.inset(19, 19, &rr2);
69 REPORTER_ASSERT(reporter, rr2.isSimple());
70 rr.inset(20, 20, &rr2);
71 REPORTER_ASSERT(reporter, rr2.isRect());
72}
73
robertphillipsfe1b1802015-02-24 11:18:48 -080074
75static void test_9patch_rrect(skiatest::Reporter* reporter,
76 const SkRect& rect,
77 SkScalar l, SkScalar t, SkScalar r, SkScalar b,
78 bool checkRadii) {
79 SkRRect rr;
80 rr.setNinePatch(rect, l, t, r, b);
81
82 REPORTER_ASSERT(reporter, SkRRect::kNinePatch_Type == rr.type());
83 REPORTER_ASSERT(reporter, rr.rect() == rect);
84
85 if (checkRadii) {
86 // This test doesn't hold if the radii will be rescaled by SkRRect
87 SkRect ninePatchRadii = { l, t, r, b };
88 SkPoint rquad[4];
89 ninePatchRadii.toQuad(rquad);
90 for (int i = 0; i < 4; ++i) {
91 REPORTER_ASSERT(reporter, rquad[i] == rr.radii((SkRRect::Corner) i));
92 }
93 }
94 SkRRect rr2; // construct the same RR using the most general set function
95 SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } };
96 rr2.setRectRadii(rect, radii);
97 REPORTER_ASSERT(reporter, rr2 == rr && rr2.getType() == rr.getType());
98}
99
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000100// Test out the basic API entry points
101static void test_round_rect_basic(skiatest::Reporter* reporter) {
102 // Test out initialization methods
reed@google.com2b57dc62013-01-08 13:23:32 +0000103 SkPoint zeroPt = { 0, 0 };
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000104 SkRRect empty;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000105
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000106 empty.setEmpty();
107
108 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
109 REPORTER_ASSERT(reporter, empty.rect().isEmpty());
110
111 for (int i = 0; i < 4; ++i) {
112 REPORTER_ASSERT(reporter, zeroPt == empty.radii((SkRRect::Corner) i));
113 }
114
115 //----
116 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
117
118 SkRRect rr1;
119 rr1.setRect(rect);
120
121 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
122 REPORTER_ASSERT(reporter, rr1.rect() == rect);
123
124 for (int i = 0; i < 4; ++i) {
125 REPORTER_ASSERT(reporter, zeroPt == rr1.radii((SkRRect::Corner) i));
126 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000127 SkRRect rr1_2; // construct the same RR using the most general set function
128 SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
129 rr1_2.setRectRadii(rect, rr1_2_radii);
130 REPORTER_ASSERT(reporter, rr1_2 == rr1 && rr1_2.getType() == rr1.getType());
131 SkRRect rr1_3; // construct the same RR using the nine patch set function
132 rr1_3.setNinePatch(rect, 0, 0, 0, 0);
133 REPORTER_ASSERT(reporter, rr1_3 == rr1 && rr1_3.getType() == rr1.getType());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000134
135 //----
136 SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) };
137 SkRRect rr2;
138 rr2.setOval(rect);
139
140 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr2.type());
141 REPORTER_ASSERT(reporter, rr2.rect() == rect);
142
143 for (int i = 0; i < 4; ++i) {
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000144 REPORTER_ASSERT(reporter,
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000145 rr2.radii((SkRRect::Corner) i).equalsWithinTolerance(halfPoint));
146 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000147 SkRRect rr2_2; // construct the same RR using the most general set function
148 SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY },
149 { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY } };
150 rr2_2.setRectRadii(rect, rr2_2_radii);
151 REPORTER_ASSERT(reporter, rr2_2 == rr2 && rr2_2.getType() == rr2.getType());
152 SkRRect rr2_3; // construct the same RR using the nine patch set function
153 rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY);
154 REPORTER_ASSERT(reporter, rr2_3 == rr2 && rr2_3.getType() == rr2.getType());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000155
156 //----
157 SkPoint p = { 5, 5 };
158 SkRRect rr3;
159 rr3.setRectXY(rect, p.fX, p.fY);
160
161 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr3.type());
162 REPORTER_ASSERT(reporter, rr3.rect() == rect);
163
164 for (int i = 0; i < 4; ++i) {
165 REPORTER_ASSERT(reporter, p == rr3.radii((SkRRect::Corner) i));
166 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000167 SkRRect rr3_2; // construct the same RR using the most general set function
168 SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } };
169 rr3_2.setRectRadii(rect, rr3_2_radii);
170 REPORTER_ASSERT(reporter, rr3_2 == rr3 && rr3_2.getType() == rr3.getType());
171 SkRRect rr3_3; // construct the same RR using the nine patch set function
172 rr3_3.setNinePatch(rect, 5, 5, 5, 5);
173 REPORTER_ASSERT(reporter, rr3_3 == rr3 && rr3_3.getType() == rr3.getType());
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000174
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000175 //----
robertphillipsfe1b1802015-02-24 11:18:48 -0800176 test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000177
robertphillipsfe1b1802015-02-24 11:18:48 -0800178 {
179 // Test out the rrect from skia:3466
180 SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000181
robertphillipsfe1b1802015-02-24 11:18:48 -0800182 test_9patch_rrect(reporter,
183 rect2,
184 0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f,
185 false);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000186 }
187
188 //----
189 SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } };
190
191 SkRRect rr5;
192 rr5.setRectRadii(rect, radii2);
193
194 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr5.type());
195 REPORTER_ASSERT(reporter, rr5.rect() == rect);
196
197 for (int i = 0; i < 4; ++i) {
198 REPORTER_ASSERT(reporter, radii2[i] == rr5.radii((SkRRect::Corner) i));
199 }
200
201 // Test out == & !=
202 REPORTER_ASSERT(reporter, empty != rr3);
robertphillipsfe1b1802015-02-24 11:18:48 -0800203 REPORTER_ASSERT(reporter, rr3 != rr5);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000204}
205
206// Test out the cases when the RR degenerates to a rect
207static void test_round_rect_rects(skiatest::Reporter* reporter) {
208 SkRect r;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000209
210 //----
211 SkRRect empty;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000212
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000213 empty.setEmpty();
214
215 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
216 r = empty.rect();
217 REPORTER_ASSERT(reporter, 0 == r.fLeft && 0 == r.fTop && 0 == r.fRight && 0 == r.fBottom);
218
219 //----
220 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
221 SkRRect rr1;
222 rr1.setRectXY(rect, 0, 0);
223
224 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
225 r = rr1.rect();
226 REPORTER_ASSERT(reporter, rect == r);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000227
228 //----
229 SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
230
231 SkRRect rr2;
232 rr2.setRectRadii(rect, radii);
233
234 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
235 r = rr2.rect();
236 REPORTER_ASSERT(reporter, rect == r);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000237
238 //----
239 SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
240
241 SkRRect rr3;
242 rr3.setRectRadii(rect, radii2);
243 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr3.type());
244}
245
246// Test out the cases when the RR degenerates to an oval
247static void test_round_rect_ovals(skiatest::Reporter* reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000248 //----
249 SkRect oval;
250 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
251 SkRRect rr1;
252 rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight));
253
254 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr1.type());
255 oval = rr1.rect();
256 REPORTER_ASSERT(reporter, oval == rect);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000257}
258
259// Test out the non-degenerate RR cases
260static void test_round_rect_general(skiatest::Reporter* reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000261 //----
262 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
263 SkRRect rr1;
264 rr1.setRectXY(rect, 20, 20);
265
266 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr1.type());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000267
268 //----
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000269 SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
270
271 SkRRect rr2;
272 rr2.setRectRadii(rect, radii);
273
274 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr2.type());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000275}
276
277// Test out questionable-parameter handling
278static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) {
279
280 // When the radii exceed the base rect they are proportionally scaled down
281 // to fit
282 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
283 SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } };
284
285 SkRRect rr1;
286 rr1.setRectRadii(rect, radii);
287
288 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr1.type());
289
290 const SkPoint& p = rr1.radii(SkRRect::kUpperLeft_Corner);
291
292 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fX, 33.33333f));
293 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fY, 66.66666f));
294
295 // Negative radii should be capped at zero
296 SkRRect rr2;
297 rr2.setRectXY(rect, -10, -20);
298
299 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
300
301 const SkPoint& p2 = rr2.radii(SkRRect::kUpperLeft_Corner);
302
303 REPORTER_ASSERT(reporter, 0.0f == p2.fX);
304 REPORTER_ASSERT(reporter, 0.0f == p2.fY);
305}
306
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000307// Move a small box from the start position by (stepX, stepY) 'numSteps' times
308// testing for containment in 'rr' at each step.
309static void test_direction(skiatest::Reporter* reporter, const SkRRect &rr,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000310 SkScalar initX, int stepX, SkScalar initY, int stepY,
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000311 int numSteps, const bool* contains) {
312 SkScalar x = initX, y = initY;
313 for (int i = 0; i < numSteps; ++i) {
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000314 SkRect test = SkRect::MakeXYWH(x, y,
315 stepX ? SkIntToScalar(stepX) : SK_Scalar1,
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000316 stepY ? SkIntToScalar(stepY) : SK_Scalar1);
317 test.sort();
318
319 REPORTER_ASSERT(reporter, contains[i] == rr.contains(test));
320
321 x += stepX;
322 y += stepY;
323 }
324}
325
326// Exercise the RR's contains rect method
327static void test_round_rect_contains_rect(skiatest::Reporter* reporter) {
328
329 static const int kNumRRects = 4;
330 static const SkVector gRadii[kNumRRects][4] = {
331 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // rect
332 { { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 } }, // circle
333 { { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 10 } }, // simple
334 { { 0, 0 }, { 20, 20 }, { 10, 10 }, { 30, 30 } } // complex
335 };
336
337 SkRRect rrects[kNumRRects];
338 for (int i = 0; i < kNumRRects; ++i) {
339 rrects[i].setRectRadii(SkRect::MakeWH(40, 40), gRadii[i]);
340 }
341
342 // First test easy outs - boxes that are obviously out on
343 // each corner and edge
344 static const SkRect easyOuts[] = {
345 { -5, -5, 5, 5 }, // NW
346 { 15, -5, 20, 5 }, // N
347 { 35, -5, 45, 5 }, // NE
348 { 35, 15, 45, 20 }, // E
349 { 35, 45, 35, 45 }, // SE
350 { 15, 35, 20, 45 }, // S
351 { -5, 35, 5, 45 }, // SW
352 { -5, 15, 5, 20 } // W
353 };
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000354
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000355 for (int i = 0; i < kNumRRects; ++i) {
356 for (size_t j = 0; j < SK_ARRAY_COUNT(easyOuts); ++j) {
357 REPORTER_ASSERT(reporter, !rrects[i].contains(easyOuts[j]));
358 }
359 }
360
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000361 // Now test non-trivial containment. For each compass
362 // point walk a 1x1 rect in from the edge of the bounding
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000363 // rect
364 static const int kNumSteps = 15;
365 bool answers[kNumRRects][8][kNumSteps] = {
366 // all the test rects are inside the degenerate rrect
367 {
368 // rect
369 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
370 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
371 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
372 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
373 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
374 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
375 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
376 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
377 },
378 // for the circle we expect 6 blocks to be out on the
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000379 // corners (then the rest in) and only the first block
380 // out on the vertical and horizontal axes (then
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000381 // the rest in)
382 {
383 // circle
384 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
385 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
386 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
387 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
388 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
389 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
390 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
391 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
392 },
393 // for the simple round rect we expect 3 out on
394 // the corners (then the rest in) and no blocks out
395 // on the vertical and horizontal axes
396 {
397 // simple RR
398 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
399 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
400 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
401 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
402 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
403 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
404 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
405 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
406 },
407 // for the complex case the answer is different for each direction
408 {
409 // complex RR
410 // all in for NW (rect) corner (same as rect case)
411 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
412 // only first block out for N (same as circle case)
413 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
414 // first 6 blocks out for NE (same as circle case)
415 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
416 // only first block out for E (same as circle case)
417 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
418 // first 3 blocks out for SE (same as simple case)
419 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
420 // first two blocks out for S
421 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
422 // first 9 blocks out for SW
423 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
424 // first two blocks out for W (same as S)
425 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
426 }
427 };
428
429 for (int i = 0; i < kNumRRects; ++i) {
430 test_direction(reporter, rrects[i], 0, 1, 0, 1, kNumSteps, answers[i][0]); // NW
431 test_direction(reporter, rrects[i], 19.5f, 0, 0, 1, kNumSteps, answers[i][1]); // N
432 test_direction(reporter, rrects[i], 40, -1, 0, 1, kNumSteps, answers[i][2]); // NE
433 test_direction(reporter, rrects[i], 40, -1, 19.5f, 0, kNumSteps, answers[i][3]); // E
434 test_direction(reporter, rrects[i], 40, -1, 40, -1, kNumSteps, answers[i][4]); // SE
435 test_direction(reporter, rrects[i], 19.5f, 0, 40, -1, kNumSteps, answers[i][5]); // S
436 test_direction(reporter, rrects[i], 0, 1, 40, -1, kNumSteps, answers[i][6]); // SW
437 test_direction(reporter, rrects[i], 0, 1, 19.5f, 0, kNumSteps, answers[i][7]); // W
438 }
439}
440
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000441// Called for a matrix that should cause SkRRect::transform to fail.
442static void assert_transform_failure(skiatest::Reporter* reporter, const SkRRect& orig,
443 const SkMatrix& matrix) {
444 // The test depends on the fact that the original is not empty.
445 SkASSERT(!orig.isEmpty());
446 SkRRect dst;
447 dst.setEmpty();
448
449 const SkRRect copyOfDst = dst;
450 const SkRRect copyOfOrig = orig;
451 bool success = orig.transform(matrix, &dst);
452 // This transform should fail.
453 REPORTER_ASSERT(reporter, !success);
454 // Since the transform failed, dst should be unchanged.
455 REPORTER_ASSERT(reporter, copyOfDst == dst);
456 // original should not be modified.
457 REPORTER_ASSERT(reporter, copyOfOrig == orig);
458 REPORTER_ASSERT(reporter, orig != dst);
459}
460
461#define GET_RADII \
462 const SkVector& origUL = orig.radii(SkRRect::kUpperLeft_Corner); \
463 const SkVector& origUR = orig.radii(SkRRect::kUpperRight_Corner); \
464 const SkVector& origLR = orig.radii(SkRRect::kLowerRight_Corner); \
465 const SkVector& origLL = orig.radii(SkRRect::kLowerLeft_Corner); \
466 const SkVector& dstUL = dst.radii(SkRRect::kUpperLeft_Corner); \
467 const SkVector& dstUR = dst.radii(SkRRect::kUpperRight_Corner); \
468 const SkVector& dstLR = dst.radii(SkRRect::kLowerRight_Corner); \
469 const SkVector& dstLL = dst.radii(SkRRect::kLowerLeft_Corner)
470
471// Called to test various transforms on a single SkRRect.
472static void test_transform_helper(skiatest::Reporter* reporter, const SkRRect& orig) {
473 SkRRect dst;
474 dst.setEmpty();
475
476 // The identity matrix will duplicate the rrect.
477 bool success = orig.transform(SkMatrix::I(), &dst);
478 REPORTER_ASSERT(reporter, success);
479 REPORTER_ASSERT(reporter, orig == dst);
480
481 // Skew and Perspective make transform fail.
482 SkMatrix matrix;
483 matrix.reset();
484 matrix.setSkewX(SkIntToScalar(2));
485 assert_transform_failure(reporter, orig, matrix);
486
487 matrix.reset();
488 matrix.setSkewY(SkIntToScalar(3));
489 assert_transform_failure(reporter, orig, matrix);
490
491 matrix.reset();
reed3f43f8a2015-01-20 19:58:36 -0800492 matrix.setPerspX(4);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000493 assert_transform_failure(reporter, orig, matrix);
494
495 matrix.reset();
reed3f43f8a2015-01-20 19:58:36 -0800496 matrix.setPerspY(5);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000497 assert_transform_failure(reporter, orig, matrix);
498
499 // Rotation fails.
500 matrix.reset();
501 matrix.setRotate(SkIntToScalar(90));
502 assert_transform_failure(reporter, orig, matrix);
503 matrix.setRotate(SkIntToScalar(37));
504 assert_transform_failure(reporter, orig, matrix);
505
506 // Translate will keep the rect moved, but otherwise the same.
507 matrix.reset();
508 SkScalar translateX = SkIntToScalar(32);
509 SkScalar translateY = SkIntToScalar(15);
510 matrix.setTranslateX(translateX);
511 matrix.setTranslateY(translateY);
512 dst.setEmpty();
513 success = orig.transform(matrix, &dst);
514 REPORTER_ASSERT(reporter, success);
515 for (int i = 0; i < 4; ++i) {
516 REPORTER_ASSERT(reporter,
517 orig.radii((SkRRect::Corner) i) == dst.radii((SkRRect::Corner) i));
518 }
519 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
520 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
521 REPORTER_ASSERT(reporter, dst.rect().left() == orig.rect().left() + translateX);
522 REPORTER_ASSERT(reporter, dst.rect().top() == orig.rect().top() + translateY);
523
524 // Keeping the translation, but adding skew will make transform fail.
525 matrix.setSkewY(SkIntToScalar(7));
526 assert_transform_failure(reporter, orig, matrix);
527
528 // Scaling in -x will flip the round rect horizontally.
529 matrix.reset();
530 matrix.setScaleX(SkIntToScalar(-1));
531 dst.setEmpty();
532 success = orig.transform(matrix, &dst);
533 REPORTER_ASSERT(reporter, success);
534 {
535 GET_RADII;
536 // Radii have swapped in x.
537 REPORTER_ASSERT(reporter, origUL == dstUR);
538 REPORTER_ASSERT(reporter, origUR == dstUL);
539 REPORTER_ASSERT(reporter, origLR == dstLL);
540 REPORTER_ASSERT(reporter, origLL == dstLR);
541 }
542 // Width and height remain the same.
543 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
544 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
545 // Right and left have swapped (sort of)
546 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
547 // Top has stayed the same.
548 REPORTER_ASSERT(reporter, orig.rect().top() == dst.rect().top());
549
550 // Keeping the scale, but adding a persp will make transform fail.
reed3f43f8a2015-01-20 19:58:36 -0800551 matrix.setPerspX(7);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000552 assert_transform_failure(reporter, orig, matrix);
553
554 // Scaling in -y will flip the round rect vertically.
555 matrix.reset();
556 matrix.setScaleY(SkIntToScalar(-1));
557 dst.setEmpty();
558 success = orig.transform(matrix, &dst);
559 REPORTER_ASSERT(reporter, success);
560 {
561 GET_RADII;
562 // Radii have swapped in y.
563 REPORTER_ASSERT(reporter, origUL == dstLL);
564 REPORTER_ASSERT(reporter, origUR == dstLR);
565 REPORTER_ASSERT(reporter, origLR == dstUR);
566 REPORTER_ASSERT(reporter, origLL == dstUL);
567 }
568 // Width and height remain the same.
569 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
570 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
571 // Top and bottom have swapped (sort of)
572 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
573 // Left has stayed the same.
574 REPORTER_ASSERT(reporter, orig.rect().left() == dst.rect().left());
575
576 // Scaling in -x and -y will swap in both directions.
577 matrix.reset();
578 matrix.setScaleY(SkIntToScalar(-1));
579 matrix.setScaleX(SkIntToScalar(-1));
580 dst.setEmpty();
581 success = orig.transform(matrix, &dst);
582 REPORTER_ASSERT(reporter, success);
583 {
584 GET_RADII;
585 REPORTER_ASSERT(reporter, origUL == dstLR);
586 REPORTER_ASSERT(reporter, origUR == dstLL);
587 REPORTER_ASSERT(reporter, origLR == dstUL);
588 REPORTER_ASSERT(reporter, origLL == dstUR);
589 }
590 // Width and height remain the same.
591 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
592 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
593 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
594 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
595
596 // Scale in both directions.
597 SkScalar xScale = SkIntToScalar(3);
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000598 SkScalar yScale = 3.2f;
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000599 matrix.reset();
600 matrix.setScaleX(xScale);
601 matrix.setScaleY(yScale);
602 dst.setEmpty();
603 success = orig.transform(matrix, &dst);
604 REPORTER_ASSERT(reporter, success);
605 // Radii are scaled.
606 for (int i = 0; i < 4; ++i) {
607 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX,
608 SkScalarMul(orig.radii((SkRRect::Corner) i).fX, xScale)));
609 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY,
610 SkScalarMul(orig.radii((SkRRect::Corner) i).fY, yScale)));
611 }
612 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(),
613 SkScalarMul(orig.rect().width(), xScale)));
614 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(),
615 SkScalarMul(orig.rect().height(), yScale)));
616 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(),
617 SkScalarMul(orig.rect().left(), xScale)));
618 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(),
619 SkScalarMul(orig.rect().top(), yScale)));
620}
621
622static void test_round_rect_transform(skiatest::Reporter* reporter) {
623 SkRRect rrect;
624 {
625 SkRect r = { 0, 0, kWidth, kHeight };
626 rrect.setRectXY(r, SkIntToScalar(4), SkIntToScalar(7));
627 test_transform_helper(reporter, rrect);
628 }
629 {
630 SkRect r = { SkIntToScalar(5), SkIntToScalar(15),
631 SkIntToScalar(27), SkIntToScalar(34) };
632 SkVector radii[4] = { { 0, SkIntToScalar(1) },
633 { SkIntToScalar(2), SkIntToScalar(3) },
634 { SkIntToScalar(4), SkIntToScalar(5) },
635 { SkIntToScalar(6), SkIntToScalar(7) } };
636 rrect.setRectRadii(r, radii);
637 test_transform_helper(reporter, rrect);
638 }
639}
640
robertphillipse5c1e3c2014-06-27 08:59:26 -0700641// Test out the case where an oval already off in space is translated/scaled
642// further off into space - yielding numerical issues when the rect & radii
643// are transformed separatly
644// BUG=skia:2696
645static void test_issue_2696(skiatest::Reporter* reporter) {
646 SkRRect rrect;
647 SkRect r = { 28443.8594f, 53.1428604f, 28446.7148f, 56.0000038f };
648 rrect.setOval(r);
649
650 SkMatrix xform;
651 xform.setAll(2.44f, 0.0f, 485411.7f,
652 0.0f, 2.44f, -438.7f,
653 0.0f, 0.0f, 1.0f);
654 SkRRect dst;
655
656 bool success = rrect.transform(xform, &dst);
657 REPORTER_ASSERT(reporter, success);
658
659 SkScalar halfWidth = SkScalarHalf(dst.width());
660 SkScalar halfHeight = SkScalarHalf(dst.height());
661
662 for (int i = 0; i < 4; ++i) {
663 REPORTER_ASSERT(reporter,
664 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth));
665 REPORTER_ASSERT(reporter,
666 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight));
667 }
668}
669
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000670DEF_TEST(RoundRect, reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000671 test_round_rect_basic(reporter);
672 test_round_rect_rects(reporter);
673 test_round_rect_ovals(reporter);
674 test_round_rect_general(reporter);
675 test_round_rect_iffy_parameters(reporter);
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000676 test_inset(reporter);
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000677 test_round_rect_contains_rect(reporter);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000678 test_round_rect_transform(reporter);
robertphillipse5c1e3c2014-06-27 08:59:26 -0700679 test_issue_2696(reporter);
robertphillips2a679ae2015-03-13 09:53:01 -0700680 test_tricky_radii(reporter);
reed694b0d12015-02-13 14:33:02 -0800681 test_empty_crbug_458524(reporter);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000682}