blob: d9b7a9aa087b97b846134b8f0d8d126c956a6138 [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
robertphillips05302f82015-09-29 11:24:07 -070050// Test that all the SkRRect entry points correctly handle un-sorted and
51// zero-sized input rects
52static void test_empty(skiatest::Reporter* reporter) {
53 static const SkRect oooRects[] = { // out of order
54 { 100, 0, 0, 100 }, // ooo horizontal
55 { 0, 100, 100, 0 }, // ooo vertical
56 { 100, 100, 0, 0 }, // ooo both
57 };
58
59 static const SkRect emptyRects[] = {
60 { 100, 100, 100, 200 }, // empty horizontal
61 { 100, 100, 200, 100 }, // empty vertical
62 { 100, 100, 100, 100 }, // empty both
63 { 0, 0, 0, 0 } // setEmpty-empty
64 };
65
66 static const SkVector radii[4] = { { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 } };
67
68 SkRRect r;
69
70 for (size_t i = 0; i < SK_ARRAY_COUNT(oooRects); ++i) {
71 r.setRect(oooRects[i]);
72 REPORTER_ASSERT(reporter, !r.isEmpty());
73
74 r.setOval(oooRects[i]);
75 REPORTER_ASSERT(reporter, !r.isEmpty());
76
77 r.setRectXY(oooRects[i], 1, 2);
78 REPORTER_ASSERT(reporter, !r.isEmpty());
79
80 r.setNinePatch(oooRects[i], 0, 1, 2, 3);
81 REPORTER_ASSERT(reporter, !r.isEmpty());
82
83 r.setRectRadii(oooRects[i], radii);
84 REPORTER_ASSERT(reporter, !r.isEmpty());
85 }
86
87 for (size_t i = 0; i < SK_ARRAY_COUNT(emptyRects); ++i) {
88 r.setRect(emptyRects[i]);
89 REPORTER_ASSERT(reporter, r.isEmpty());
90
91 r.setOval(emptyRects[i]);
92 REPORTER_ASSERT(reporter, r.isEmpty());
93
94 r.setRectXY(emptyRects[i], 1, 2);
95 REPORTER_ASSERT(reporter, r.isEmpty());
96
97 r.setNinePatch(emptyRects[i], 0, 1, 2, 3);
98 REPORTER_ASSERT(reporter, r.isEmpty());
99
100 r.setRectRadii(emptyRects[i], radii);
101 REPORTER_ASSERT(reporter, r.isEmpty());
102 }
103}
104
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000105static const SkScalar kWidth = 100.0f;
106static const SkScalar kHeight = 100.0f;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000107
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000108static void test_inset(skiatest::Reporter* reporter) {
109 SkRRect rr, rr2;
110 SkRect r = { 0, 0, 100, 100 };
111
112 rr.setRect(r);
113 rr.inset(-20, -20, &rr2);
114 REPORTER_ASSERT(reporter, rr2.isRect());
115
116 rr.inset(20, 20, &rr2);
117 REPORTER_ASSERT(reporter, rr2.isRect());
skia.committer@gmail.com1a60dab2012-12-24 02:01:25 +0000118
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000119 rr.inset(r.width()/2, r.height()/2, &rr2);
120 REPORTER_ASSERT(reporter, rr2.isEmpty());
121
122 rr.setRectXY(r, 20, 20);
123 rr.inset(19, 19, &rr2);
124 REPORTER_ASSERT(reporter, rr2.isSimple());
125 rr.inset(20, 20, &rr2);
126 REPORTER_ASSERT(reporter, rr2.isRect());
127}
128
robertphillipsfe1b1802015-02-24 11:18:48 -0800129
130static void test_9patch_rrect(skiatest::Reporter* reporter,
131 const SkRect& rect,
132 SkScalar l, SkScalar t, SkScalar r, SkScalar b,
133 bool checkRadii) {
134 SkRRect rr;
135 rr.setNinePatch(rect, l, t, r, b);
136
137 REPORTER_ASSERT(reporter, SkRRect::kNinePatch_Type == rr.type());
138 REPORTER_ASSERT(reporter, rr.rect() == rect);
139
140 if (checkRadii) {
141 // This test doesn't hold if the radii will be rescaled by SkRRect
142 SkRect ninePatchRadii = { l, t, r, b };
143 SkPoint rquad[4];
144 ninePatchRadii.toQuad(rquad);
145 for (int i = 0; i < 4; ++i) {
146 REPORTER_ASSERT(reporter, rquad[i] == rr.radii((SkRRect::Corner) i));
147 }
148 }
149 SkRRect rr2; // construct the same RR using the most general set function
150 SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } };
151 rr2.setRectRadii(rect, radii);
152 REPORTER_ASSERT(reporter, rr2 == rr && rr2.getType() == rr.getType());
153}
154
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000155// Test out the basic API entry points
156static void test_round_rect_basic(skiatest::Reporter* reporter) {
157 // Test out initialization methods
reed@google.com2b57dc62013-01-08 13:23:32 +0000158 SkPoint zeroPt = { 0, 0 };
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000159 SkRRect empty;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000160
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000161 empty.setEmpty();
162
163 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
164 REPORTER_ASSERT(reporter, empty.rect().isEmpty());
165
166 for (int i = 0; i < 4; ++i) {
167 REPORTER_ASSERT(reporter, zeroPt == empty.radii((SkRRect::Corner) i));
168 }
169
170 //----
171 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
172
173 SkRRect rr1;
174 rr1.setRect(rect);
175
176 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
177 REPORTER_ASSERT(reporter, rr1.rect() == rect);
178
179 for (int i = 0; i < 4; ++i) {
180 REPORTER_ASSERT(reporter, zeroPt == rr1.radii((SkRRect::Corner) i));
181 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000182 SkRRect rr1_2; // construct the same RR using the most general set function
183 SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
184 rr1_2.setRectRadii(rect, rr1_2_radii);
185 REPORTER_ASSERT(reporter, rr1_2 == rr1 && rr1_2.getType() == rr1.getType());
186 SkRRect rr1_3; // construct the same RR using the nine patch set function
187 rr1_3.setNinePatch(rect, 0, 0, 0, 0);
188 REPORTER_ASSERT(reporter, rr1_3 == rr1 && rr1_3.getType() == rr1.getType());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000189
190 //----
191 SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) };
192 SkRRect rr2;
193 rr2.setOval(rect);
194
195 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr2.type());
196 REPORTER_ASSERT(reporter, rr2.rect() == rect);
197
198 for (int i = 0; i < 4; ++i) {
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000199 REPORTER_ASSERT(reporter,
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000200 rr2.radii((SkRRect::Corner) i).equalsWithinTolerance(halfPoint));
201 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000202 SkRRect rr2_2; // construct the same RR using the most general set function
203 SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY },
204 { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY } };
205 rr2_2.setRectRadii(rect, rr2_2_radii);
206 REPORTER_ASSERT(reporter, rr2_2 == rr2 && rr2_2.getType() == rr2.getType());
207 SkRRect rr2_3; // construct the same RR using the nine patch set function
208 rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY);
209 REPORTER_ASSERT(reporter, rr2_3 == rr2 && rr2_3.getType() == rr2.getType());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000210
211 //----
212 SkPoint p = { 5, 5 };
213 SkRRect rr3;
214 rr3.setRectXY(rect, p.fX, p.fY);
215
216 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr3.type());
217 REPORTER_ASSERT(reporter, rr3.rect() == rect);
218
219 for (int i = 0; i < 4; ++i) {
220 REPORTER_ASSERT(reporter, p == rr3.radii((SkRRect::Corner) i));
221 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000222 SkRRect rr3_2; // construct the same RR using the most general set function
223 SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } };
224 rr3_2.setRectRadii(rect, rr3_2_radii);
225 REPORTER_ASSERT(reporter, rr3_2 == rr3 && rr3_2.getType() == rr3.getType());
226 SkRRect rr3_3; // construct the same RR using the nine patch set function
227 rr3_3.setNinePatch(rect, 5, 5, 5, 5);
228 REPORTER_ASSERT(reporter, rr3_3 == rr3 && rr3_3.getType() == rr3.getType());
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000229
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000230 //----
robertphillipsfe1b1802015-02-24 11:18:48 -0800231 test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000232
robertphillipsfe1b1802015-02-24 11:18:48 -0800233 {
234 // Test out the rrect from skia:3466
235 SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000236
robertphillipsfe1b1802015-02-24 11:18:48 -0800237 test_9patch_rrect(reporter,
238 rect2,
239 0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f,
240 false);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000241 }
242
243 //----
244 SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } };
245
246 SkRRect rr5;
247 rr5.setRectRadii(rect, radii2);
248
249 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr5.type());
250 REPORTER_ASSERT(reporter, rr5.rect() == rect);
251
252 for (int i = 0; i < 4; ++i) {
253 REPORTER_ASSERT(reporter, radii2[i] == rr5.radii((SkRRect::Corner) i));
254 }
255
256 // Test out == & !=
257 REPORTER_ASSERT(reporter, empty != rr3);
robertphillipsfe1b1802015-02-24 11:18:48 -0800258 REPORTER_ASSERT(reporter, rr3 != rr5);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000259}
260
261// Test out the cases when the RR degenerates to a rect
262static void test_round_rect_rects(skiatest::Reporter* reporter) {
263 SkRect r;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000264
265 //----
266 SkRRect empty;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000267
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000268 empty.setEmpty();
269
270 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
271 r = empty.rect();
272 REPORTER_ASSERT(reporter, 0 == r.fLeft && 0 == r.fTop && 0 == r.fRight && 0 == r.fBottom);
273
274 //----
275 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
276 SkRRect rr1;
277 rr1.setRectXY(rect, 0, 0);
278
279 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
280 r = rr1.rect();
281 REPORTER_ASSERT(reporter, rect == r);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000282
283 //----
284 SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
285
286 SkRRect rr2;
287 rr2.setRectRadii(rect, radii);
288
289 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
290 r = rr2.rect();
291 REPORTER_ASSERT(reporter, rect == r);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000292
293 //----
294 SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
295
296 SkRRect rr3;
297 rr3.setRectRadii(rect, radii2);
298 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr3.type());
299}
300
301// Test out the cases when the RR degenerates to an oval
302static void test_round_rect_ovals(skiatest::Reporter* reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000303 //----
304 SkRect oval;
305 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
306 SkRRect rr1;
307 rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight));
308
309 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr1.type());
310 oval = rr1.rect();
311 REPORTER_ASSERT(reporter, oval == rect);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000312}
313
314// Test out the non-degenerate RR cases
315static void test_round_rect_general(skiatest::Reporter* reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000316 //----
317 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
318 SkRRect rr1;
319 rr1.setRectXY(rect, 20, 20);
320
321 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr1.type());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000322
323 //----
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000324 SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
325
326 SkRRect rr2;
327 rr2.setRectRadii(rect, radii);
328
329 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr2.type());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000330}
331
332// Test out questionable-parameter handling
333static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) {
334
335 // When the radii exceed the base rect they are proportionally scaled down
336 // to fit
337 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
338 SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } };
339
340 SkRRect rr1;
341 rr1.setRectRadii(rect, radii);
342
343 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr1.type());
344
345 const SkPoint& p = rr1.radii(SkRRect::kUpperLeft_Corner);
346
347 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fX, 33.33333f));
348 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fY, 66.66666f));
349
350 // Negative radii should be capped at zero
351 SkRRect rr2;
352 rr2.setRectXY(rect, -10, -20);
353
354 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
355
356 const SkPoint& p2 = rr2.radii(SkRRect::kUpperLeft_Corner);
357
358 REPORTER_ASSERT(reporter, 0.0f == p2.fX);
359 REPORTER_ASSERT(reporter, 0.0f == p2.fY);
360}
361
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000362// Move a small box from the start position by (stepX, stepY) 'numSteps' times
363// testing for containment in 'rr' at each step.
364static void test_direction(skiatest::Reporter* reporter, const SkRRect &rr,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000365 SkScalar initX, int stepX, SkScalar initY, int stepY,
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000366 int numSteps, const bool* contains) {
367 SkScalar x = initX, y = initY;
368 for (int i = 0; i < numSteps; ++i) {
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000369 SkRect test = SkRect::MakeXYWH(x, y,
370 stepX ? SkIntToScalar(stepX) : SK_Scalar1,
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000371 stepY ? SkIntToScalar(stepY) : SK_Scalar1);
372 test.sort();
373
374 REPORTER_ASSERT(reporter, contains[i] == rr.contains(test));
375
376 x += stepX;
377 y += stepY;
378 }
379}
380
381// Exercise the RR's contains rect method
382static void test_round_rect_contains_rect(skiatest::Reporter* reporter) {
383
384 static const int kNumRRects = 4;
385 static const SkVector gRadii[kNumRRects][4] = {
386 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // rect
387 { { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 } }, // circle
388 { { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 10 } }, // simple
389 { { 0, 0 }, { 20, 20 }, { 10, 10 }, { 30, 30 } } // complex
390 };
391
392 SkRRect rrects[kNumRRects];
393 for (int i = 0; i < kNumRRects; ++i) {
394 rrects[i].setRectRadii(SkRect::MakeWH(40, 40), gRadii[i]);
395 }
396
397 // First test easy outs - boxes that are obviously out on
398 // each corner and edge
399 static const SkRect easyOuts[] = {
400 { -5, -5, 5, 5 }, // NW
401 { 15, -5, 20, 5 }, // N
402 { 35, -5, 45, 5 }, // NE
403 { 35, 15, 45, 20 }, // E
404 { 35, 45, 35, 45 }, // SE
405 { 15, 35, 20, 45 }, // S
406 { -5, 35, 5, 45 }, // SW
407 { -5, 15, 5, 20 } // W
408 };
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000409
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000410 for (int i = 0; i < kNumRRects; ++i) {
411 for (size_t j = 0; j < SK_ARRAY_COUNT(easyOuts); ++j) {
412 REPORTER_ASSERT(reporter, !rrects[i].contains(easyOuts[j]));
413 }
414 }
415
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000416 // Now test non-trivial containment. For each compass
417 // point walk a 1x1 rect in from the edge of the bounding
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000418 // rect
419 static const int kNumSteps = 15;
420 bool answers[kNumRRects][8][kNumSteps] = {
421 // all the test rects are inside the degenerate rrect
422 {
423 // rect
424 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
425 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
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 },
433 // for the circle we expect 6 blocks to be out on the
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000434 // corners (then the rest in) and only the first block
435 // out on the vertical and horizontal axes (then
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000436 // the rest in)
437 {
438 // circle
439 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
440 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
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 },
448 // for the simple round rect we expect 3 out on
449 // the corners (then the rest in) and no blocks out
450 // on the vertical and horizontal axes
451 {
452 // simple RR
453 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
454 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
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 },
462 // for the complex case the answer is different for each direction
463 {
464 // complex RR
465 // all in for NW (rect) corner (same as rect case)
466 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
467 // only first block out for N (same as circle case)
468 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
469 // first 6 blocks out for NE (same as circle case)
470 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
471 // only first block out for E (same as circle case)
472 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
473 // first 3 blocks out for SE (same as simple case)
474 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
475 // first two blocks out for S
476 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
477 // first 9 blocks out for SW
478 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
479 // first two blocks out for W (same as S)
480 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
481 }
482 };
483
484 for (int i = 0; i < kNumRRects; ++i) {
485 test_direction(reporter, rrects[i], 0, 1, 0, 1, kNumSteps, answers[i][0]); // NW
486 test_direction(reporter, rrects[i], 19.5f, 0, 0, 1, kNumSteps, answers[i][1]); // N
487 test_direction(reporter, rrects[i], 40, -1, 0, 1, kNumSteps, answers[i][2]); // NE
488 test_direction(reporter, rrects[i], 40, -1, 19.5f, 0, kNumSteps, answers[i][3]); // E
489 test_direction(reporter, rrects[i], 40, -1, 40, -1, kNumSteps, answers[i][4]); // SE
490 test_direction(reporter, rrects[i], 19.5f, 0, 40, -1, kNumSteps, answers[i][5]); // S
491 test_direction(reporter, rrects[i], 0, 1, 40, -1, kNumSteps, answers[i][6]); // SW
492 test_direction(reporter, rrects[i], 0, 1, 19.5f, 0, kNumSteps, answers[i][7]); // W
493 }
494}
495
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000496// Called for a matrix that should cause SkRRect::transform to fail.
497static void assert_transform_failure(skiatest::Reporter* reporter, const SkRRect& orig,
498 const SkMatrix& matrix) {
499 // The test depends on the fact that the original is not empty.
500 SkASSERT(!orig.isEmpty());
501 SkRRect dst;
502 dst.setEmpty();
503
504 const SkRRect copyOfDst = dst;
505 const SkRRect copyOfOrig = orig;
506 bool success = orig.transform(matrix, &dst);
507 // This transform should fail.
508 REPORTER_ASSERT(reporter, !success);
509 // Since the transform failed, dst should be unchanged.
510 REPORTER_ASSERT(reporter, copyOfDst == dst);
511 // original should not be modified.
512 REPORTER_ASSERT(reporter, copyOfOrig == orig);
513 REPORTER_ASSERT(reporter, orig != dst);
514}
515
516#define GET_RADII \
517 const SkVector& origUL = orig.radii(SkRRect::kUpperLeft_Corner); \
518 const SkVector& origUR = orig.radii(SkRRect::kUpperRight_Corner); \
519 const SkVector& origLR = orig.radii(SkRRect::kLowerRight_Corner); \
520 const SkVector& origLL = orig.radii(SkRRect::kLowerLeft_Corner); \
521 const SkVector& dstUL = dst.radii(SkRRect::kUpperLeft_Corner); \
522 const SkVector& dstUR = dst.radii(SkRRect::kUpperRight_Corner); \
523 const SkVector& dstLR = dst.radii(SkRRect::kLowerRight_Corner); \
524 const SkVector& dstLL = dst.radii(SkRRect::kLowerLeft_Corner)
525
526// Called to test various transforms on a single SkRRect.
527static void test_transform_helper(skiatest::Reporter* reporter, const SkRRect& orig) {
528 SkRRect dst;
529 dst.setEmpty();
530
531 // The identity matrix will duplicate the rrect.
532 bool success = orig.transform(SkMatrix::I(), &dst);
533 REPORTER_ASSERT(reporter, success);
534 REPORTER_ASSERT(reporter, orig == dst);
535
536 // Skew and Perspective make transform fail.
537 SkMatrix matrix;
538 matrix.reset();
539 matrix.setSkewX(SkIntToScalar(2));
540 assert_transform_failure(reporter, orig, matrix);
541
542 matrix.reset();
543 matrix.setSkewY(SkIntToScalar(3));
544 assert_transform_failure(reporter, orig, matrix);
545
546 matrix.reset();
reed3f43f8a2015-01-20 19:58:36 -0800547 matrix.setPerspX(4);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000548 assert_transform_failure(reporter, orig, matrix);
549
550 matrix.reset();
reed3f43f8a2015-01-20 19:58:36 -0800551 matrix.setPerspY(5);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000552 assert_transform_failure(reporter, orig, matrix);
553
554 // Rotation fails.
555 matrix.reset();
556 matrix.setRotate(SkIntToScalar(90));
557 assert_transform_failure(reporter, orig, matrix);
558 matrix.setRotate(SkIntToScalar(37));
559 assert_transform_failure(reporter, orig, matrix);
560
561 // Translate will keep the rect moved, but otherwise the same.
562 matrix.reset();
563 SkScalar translateX = SkIntToScalar(32);
564 SkScalar translateY = SkIntToScalar(15);
565 matrix.setTranslateX(translateX);
566 matrix.setTranslateY(translateY);
567 dst.setEmpty();
568 success = orig.transform(matrix, &dst);
569 REPORTER_ASSERT(reporter, success);
570 for (int i = 0; i < 4; ++i) {
571 REPORTER_ASSERT(reporter,
572 orig.radii((SkRRect::Corner) i) == dst.radii((SkRRect::Corner) i));
573 }
574 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
575 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
576 REPORTER_ASSERT(reporter, dst.rect().left() == orig.rect().left() + translateX);
577 REPORTER_ASSERT(reporter, dst.rect().top() == orig.rect().top() + translateY);
578
579 // Keeping the translation, but adding skew will make transform fail.
580 matrix.setSkewY(SkIntToScalar(7));
581 assert_transform_failure(reporter, orig, matrix);
582
583 // Scaling in -x will flip the round rect horizontally.
584 matrix.reset();
585 matrix.setScaleX(SkIntToScalar(-1));
586 dst.setEmpty();
587 success = orig.transform(matrix, &dst);
588 REPORTER_ASSERT(reporter, success);
589 {
590 GET_RADII;
591 // Radii have swapped in x.
592 REPORTER_ASSERT(reporter, origUL == dstUR);
593 REPORTER_ASSERT(reporter, origUR == dstUL);
594 REPORTER_ASSERT(reporter, origLR == dstLL);
595 REPORTER_ASSERT(reporter, origLL == dstLR);
596 }
597 // Width and height remain the same.
598 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
599 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
600 // Right and left have swapped (sort of)
601 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
602 // Top has stayed the same.
603 REPORTER_ASSERT(reporter, orig.rect().top() == dst.rect().top());
604
605 // Keeping the scale, but adding a persp will make transform fail.
reed3f43f8a2015-01-20 19:58:36 -0800606 matrix.setPerspX(7);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000607 assert_transform_failure(reporter, orig, matrix);
608
609 // Scaling in -y will flip the round rect vertically.
610 matrix.reset();
611 matrix.setScaleY(SkIntToScalar(-1));
612 dst.setEmpty();
613 success = orig.transform(matrix, &dst);
614 REPORTER_ASSERT(reporter, success);
615 {
616 GET_RADII;
617 // Radii have swapped in y.
618 REPORTER_ASSERT(reporter, origUL == dstLL);
619 REPORTER_ASSERT(reporter, origUR == dstLR);
620 REPORTER_ASSERT(reporter, origLR == dstUR);
621 REPORTER_ASSERT(reporter, origLL == dstUL);
622 }
623 // Width and height remain the same.
624 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
625 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
626 // Top and bottom have swapped (sort of)
627 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
628 // Left has stayed the same.
629 REPORTER_ASSERT(reporter, orig.rect().left() == dst.rect().left());
630
631 // Scaling in -x and -y will swap in both directions.
632 matrix.reset();
633 matrix.setScaleY(SkIntToScalar(-1));
634 matrix.setScaleX(SkIntToScalar(-1));
635 dst.setEmpty();
636 success = orig.transform(matrix, &dst);
637 REPORTER_ASSERT(reporter, success);
638 {
639 GET_RADII;
640 REPORTER_ASSERT(reporter, origUL == dstLR);
641 REPORTER_ASSERT(reporter, origUR == dstLL);
642 REPORTER_ASSERT(reporter, origLR == dstUL);
643 REPORTER_ASSERT(reporter, origLL == dstUR);
644 }
645 // Width and height remain the same.
646 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
647 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
648 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
649 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
650
651 // Scale in both directions.
652 SkScalar xScale = SkIntToScalar(3);
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000653 SkScalar yScale = 3.2f;
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000654 matrix.reset();
655 matrix.setScaleX(xScale);
656 matrix.setScaleY(yScale);
657 dst.setEmpty();
658 success = orig.transform(matrix, &dst);
659 REPORTER_ASSERT(reporter, success);
660 // Radii are scaled.
661 for (int i = 0; i < 4; ++i) {
662 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX,
Mike Reeddf85c382017-02-14 10:59:19 -0500663 orig.radii((SkRRect::Corner) i).fX * xScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000664 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY,
Mike Reeddf85c382017-02-14 10:59:19 -0500665 orig.radii((SkRRect::Corner) i).fY * yScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000666 }
667 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(),
Mike Reeddf85c382017-02-14 10:59:19 -0500668 orig.rect().width() * xScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000669 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(),
Mike Reeddf85c382017-02-14 10:59:19 -0500670 orig.rect().height() * yScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000671 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(),
Mike Reeddf85c382017-02-14 10:59:19 -0500672 orig.rect().left() * xScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000673 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(),
Mike Reeddf85c382017-02-14 10:59:19 -0500674 orig.rect().top() * yScale));
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000675}
676
677static void test_round_rect_transform(skiatest::Reporter* reporter) {
678 SkRRect rrect;
679 {
680 SkRect r = { 0, 0, kWidth, kHeight };
681 rrect.setRectXY(r, SkIntToScalar(4), SkIntToScalar(7));
682 test_transform_helper(reporter, rrect);
683 }
684 {
685 SkRect r = { SkIntToScalar(5), SkIntToScalar(15),
686 SkIntToScalar(27), SkIntToScalar(34) };
687 SkVector radii[4] = { { 0, SkIntToScalar(1) },
688 { SkIntToScalar(2), SkIntToScalar(3) },
689 { SkIntToScalar(4), SkIntToScalar(5) },
690 { SkIntToScalar(6), SkIntToScalar(7) } };
691 rrect.setRectRadii(r, radii);
692 test_transform_helper(reporter, rrect);
693 }
694}
695
halcanary9d524f22016-03-29 09:03:52 -0700696// Test out the case where an oval already off in space is translated/scaled
robertphillipse5c1e3c2014-06-27 08:59:26 -0700697// further off into space - yielding numerical issues when the rect & radii
698// are transformed separatly
699// BUG=skia:2696
700static void test_issue_2696(skiatest::Reporter* reporter) {
701 SkRRect rrect;
702 SkRect r = { 28443.8594f, 53.1428604f, 28446.7148f, 56.0000038f };
703 rrect.setOval(r);
704
705 SkMatrix xform;
706 xform.setAll(2.44f, 0.0f, 485411.7f,
707 0.0f, 2.44f, -438.7f,
708 0.0f, 0.0f, 1.0f);
709 SkRRect dst;
710
711 bool success = rrect.transform(xform, &dst);
712 REPORTER_ASSERT(reporter, success);
713
714 SkScalar halfWidth = SkScalarHalf(dst.width());
715 SkScalar halfHeight = SkScalarHalf(dst.height());
716
717 for (int i = 0; i < 4; ++i) {
halcanary9d524f22016-03-29 09:03:52 -0700718 REPORTER_ASSERT(reporter,
robertphillipse5c1e3c2014-06-27 08:59:26 -0700719 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth));
halcanary9d524f22016-03-29 09:03:52 -0700720 REPORTER_ASSERT(reporter,
robertphillipse5c1e3c2014-06-27 08:59:26 -0700721 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight));
722 }
723}
724
Brian Salomonfb6a7892017-09-20 11:05:49 -0400725void test_read_rrect(skiatest::Reporter* reporter, const SkRRect& rrect, bool shouldSucceed) {
726 // It would be cleaner to call rrect.writeToMemory into a buffer. However, writeToMemory asserts
727 // that the rrect is valid and our caller may have fiddled with the internals of rrect to make
728 // it invalid.
729 const void* buffer = reinterpret_cast<const void*>(&rrect);
730 SkRRect deserialized;
731 size_t size = deserialized.readFromMemory(buffer, sizeof(SkRRect));
732 if (shouldSucceed) {
733 REPORTER_ASSERT(reporter, size == SkRRect::kSizeInMemory);
734 if (size) {
735 REPORTER_ASSERT(reporter, rrect == deserialized);
736 REPORTER_ASSERT(reporter, rrect.getType() == deserialized.getType());
737 }
738 } else {
739 REPORTER_ASSERT(reporter, !size);
740 }
741}
742
743static void test_read(skiatest::Reporter* reporter) {
744 static const SkRect kRect = {10.f, 10.f, 20.f, 20.f};
745 static const SkRect kNaNRect = {10.f, 10.f, 20.f, SK_ScalarNaN};
746 static const SkRect kInfRect = {10.f, 10.f, SK_ScalarInfinity, 20.f};
747 SkRRect rrect;
748
749 test_read_rrect(reporter, SkRRect::MakeEmpty(), true);
750 test_read_rrect(reporter, SkRRect::MakeRect(kRect), true);
751 // These get coerced to empty.
752 test_read_rrect(reporter, SkRRect::MakeRect(kInfRect), true);
753 test_read_rrect(reporter, SkRRect::MakeRect(kNaNRect), true);
754
755 rrect.setRect(kRect);
756 SkRect* innerRect = reinterpret_cast<SkRect*>(&rrect);
757 SkASSERT(*innerRect == kRect);
758 *innerRect = kInfRect;
759 test_read_rrect(reporter, rrect, false);
760 *innerRect = kNaNRect;
761 test_read_rrect(reporter, rrect, false);
762
763 test_read_rrect(reporter, SkRRect::MakeOval(kRect), true);
764 test_read_rrect(reporter, SkRRect::MakeOval(kInfRect), true);
765 test_read_rrect(reporter, SkRRect::MakeOval(kNaNRect), true);
766 rrect.setOval(kRect);
767 *innerRect = kInfRect;
768 test_read_rrect(reporter, rrect, false);
769 *innerRect = kNaNRect;
770 test_read_rrect(reporter, rrect, false);
771
772 test_read_rrect(reporter, SkRRect::MakeRectXY(kRect, 5.f, 5.f), true);
773 // rrect should scale down the radii to make this legal
774 test_read_rrect(reporter, SkRRect::MakeRectXY(kRect, 5.f, 400.f), true);
775
776 static const SkVector kRadii[4] = {{0.5f, 1.f}, {1.5f, 2.f}, {2.5f, 3.f}, {3.5f, 4.f}};
777 rrect.setRectRadii(kRect, kRadii);
778 test_read_rrect(reporter, rrect, true);
779 SkScalar* innerRadius = reinterpret_cast<SkScalar*>(&rrect) + 6;
780 SkASSERT(*innerRadius == 1.5f);
781 *innerRadius = 400.f;
782 test_read_rrect(reporter, rrect, false);
783 *innerRadius = SK_ScalarInfinity;
784 test_read_rrect(reporter, rrect, false);
785 *innerRadius = SK_ScalarNaN;
786 test_read_rrect(reporter, rrect, false);
787}
788
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000789DEF_TEST(RoundRect, reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000790 test_round_rect_basic(reporter);
791 test_round_rect_rects(reporter);
792 test_round_rect_ovals(reporter);
793 test_round_rect_general(reporter);
794 test_round_rect_iffy_parameters(reporter);
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000795 test_inset(reporter);
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000796 test_round_rect_contains_rect(reporter);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000797 test_round_rect_transform(reporter);
robertphillipse5c1e3c2014-06-27 08:59:26 -0700798 test_issue_2696(reporter);
robertphillips2a679ae2015-03-13 09:53:01 -0700799 test_tricky_radii(reporter);
reed694b0d12015-02-13 14:33:02 -0800800 test_empty_crbug_458524(reporter);
robertphillips05302f82015-09-29 11:24:07 -0700801 test_empty(reporter);
Brian Salomonfb6a7892017-09-20 11:05:49 -0400802 test_read(reporter);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000803}