blob: cff3e8f35ad5bc498390c2ddd992f97db2314549 [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
reed694b0d12015-02-13 14:33:02 -080012static void test_tricky_radii_crbug_458522(skiatest::Reporter* reporter) {
13 SkRRect rr;
14 const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
15 const SkScalar rad = 12814;
16 const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } };
17 rr.setRectRadii(bounds, vec);
18}
19
20static void test_empty_crbug_458524(skiatest::Reporter* reporter) {
21 SkRRect rr;
22 const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
23 const SkScalar rad = 40;
24 rr.setRectXY(bounds, rad, rad);
25
26 SkRRect other;
27 SkMatrix matrix;
28 matrix.setScale(0, 1);
29 rr.transform(matrix, &other);
30 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == other.getType());
31}
32
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +000033static const SkScalar kWidth = 100.0f;
34static const SkScalar kHeight = 100.0f;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000035
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +000036static void test_inset(skiatest::Reporter* reporter) {
37 SkRRect rr, rr2;
38 SkRect r = { 0, 0, 100, 100 };
39
40 rr.setRect(r);
41 rr.inset(-20, -20, &rr2);
42 REPORTER_ASSERT(reporter, rr2.isRect());
43
44 rr.inset(20, 20, &rr2);
45 REPORTER_ASSERT(reporter, rr2.isRect());
skia.committer@gmail.com1a60dab2012-12-24 02:01:25 +000046
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +000047 rr.inset(r.width()/2, r.height()/2, &rr2);
48 REPORTER_ASSERT(reporter, rr2.isEmpty());
49
50 rr.setRectXY(r, 20, 20);
51 rr.inset(19, 19, &rr2);
52 REPORTER_ASSERT(reporter, rr2.isSimple());
53 rr.inset(20, 20, &rr2);
54 REPORTER_ASSERT(reporter, rr2.isRect());
55}
56
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000057// Test out the basic API entry points
58static void test_round_rect_basic(skiatest::Reporter* reporter) {
59 // Test out initialization methods
reed@google.com2b57dc62013-01-08 13:23:32 +000060 SkPoint zeroPt = { 0, 0 };
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000061 SkRRect empty;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000062
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000063 empty.setEmpty();
64
65 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
66 REPORTER_ASSERT(reporter, empty.rect().isEmpty());
67
68 for (int i = 0; i < 4; ++i) {
69 REPORTER_ASSERT(reporter, zeroPt == empty.radii((SkRRect::Corner) i));
70 }
71
72 //----
73 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
74
75 SkRRect rr1;
76 rr1.setRect(rect);
77
78 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
79 REPORTER_ASSERT(reporter, rr1.rect() == rect);
80
81 for (int i = 0; i < 4; ++i) {
82 REPORTER_ASSERT(reporter, zeroPt == rr1.radii((SkRRect::Corner) i));
83 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +000084 SkRRect rr1_2; // construct the same RR using the most general set function
85 SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
86 rr1_2.setRectRadii(rect, rr1_2_radii);
87 REPORTER_ASSERT(reporter, rr1_2 == rr1 && rr1_2.getType() == rr1.getType());
88 SkRRect rr1_3; // construct the same RR using the nine patch set function
89 rr1_3.setNinePatch(rect, 0, 0, 0, 0);
90 REPORTER_ASSERT(reporter, rr1_3 == rr1 && rr1_3.getType() == rr1.getType());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000091
92 //----
93 SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) };
94 SkRRect rr2;
95 rr2.setOval(rect);
96
97 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr2.type());
98 REPORTER_ASSERT(reporter, rr2.rect() == rect);
99
100 for (int i = 0; i < 4; ++i) {
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000101 REPORTER_ASSERT(reporter,
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000102 rr2.radii((SkRRect::Corner) i).equalsWithinTolerance(halfPoint));
103 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000104 SkRRect rr2_2; // construct the same RR using the most general set function
105 SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY },
106 { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY } };
107 rr2_2.setRectRadii(rect, rr2_2_radii);
108 REPORTER_ASSERT(reporter, rr2_2 == rr2 && rr2_2.getType() == rr2.getType());
109 SkRRect rr2_3; // construct the same RR using the nine patch set function
110 rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY);
111 REPORTER_ASSERT(reporter, rr2_3 == rr2 && rr2_3.getType() == rr2.getType());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000112
113 //----
114 SkPoint p = { 5, 5 };
115 SkRRect rr3;
116 rr3.setRectXY(rect, p.fX, p.fY);
117
118 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr3.type());
119 REPORTER_ASSERT(reporter, rr3.rect() == rect);
120
121 for (int i = 0; i < 4; ++i) {
122 REPORTER_ASSERT(reporter, p == rr3.radii((SkRRect::Corner) i));
123 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000124 SkRRect rr3_2; // construct the same RR using the most general set function
125 SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } };
126 rr3_2.setRectRadii(rect, rr3_2_radii);
127 REPORTER_ASSERT(reporter, rr3_2 == rr3 && rr3_2.getType() == rr3.getType());
128 SkRRect rr3_3; // construct the same RR using the nine patch set function
129 rr3_3.setNinePatch(rect, 5, 5, 5, 5);
130 REPORTER_ASSERT(reporter, rr3_3 == rr3 && rr3_3.getType() == rr3.getType());
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000131
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000132 //----
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000133 SkRect ninePatchRadii = { 10, 9, 8, 7 };
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000134
135 SkRRect rr4;
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000136 rr4.setNinePatch(rect, ninePatchRadii.fLeft, ninePatchRadii.fTop, ninePatchRadii.fRight,
137 ninePatchRadii.fBottom);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000138
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000139 REPORTER_ASSERT(reporter, SkRRect::kNinePatch_Type == rr4.type());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000140 REPORTER_ASSERT(reporter, rr4.rect() == rect);
141
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000142 SkPoint rquad[4];
143 ninePatchRadii.toQuad(rquad);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000144 for (int i = 0; i < 4; ++i) {
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000145 REPORTER_ASSERT(reporter, rquad[i] == rr4.radii((SkRRect::Corner) i));
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000146 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000147 SkRRect rr4_2; // construct the same RR using the most general set function
148 SkVector rr4_2_radii[4] = { { 10, 9 }, { 8, 9 }, {8, 7 }, { 10, 7 } };
149 rr4_2.setRectRadii(rect, rr4_2_radii);
150 REPORTER_ASSERT(reporter, rr4_2 == rr4 && rr4_2.getType() == rr4.getType());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000151
152 //----
153 SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } };
154
155 SkRRect rr5;
156 rr5.setRectRadii(rect, radii2);
157
158 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr5.type());
159 REPORTER_ASSERT(reporter, rr5.rect() == rect);
160
161 for (int i = 0; i < 4; ++i) {
162 REPORTER_ASSERT(reporter, radii2[i] == rr5.radii((SkRRect::Corner) i));
163 }
164
165 // Test out == & !=
166 REPORTER_ASSERT(reporter, empty != rr3);
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000167 REPORTER_ASSERT(reporter, rr3 != rr4);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000168 REPORTER_ASSERT(reporter, rr4 != rr5);
169}
170
171// Test out the cases when the RR degenerates to a rect
172static void test_round_rect_rects(skiatest::Reporter* reporter) {
173 SkRect r;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000174
175 //----
176 SkRRect empty;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000177
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000178 empty.setEmpty();
179
180 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
181 r = empty.rect();
182 REPORTER_ASSERT(reporter, 0 == r.fLeft && 0 == r.fTop && 0 == r.fRight && 0 == r.fBottom);
183
184 //----
185 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
186 SkRRect rr1;
187 rr1.setRectXY(rect, 0, 0);
188
189 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
190 r = rr1.rect();
191 REPORTER_ASSERT(reporter, rect == r);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000192
193 //----
194 SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
195
196 SkRRect rr2;
197 rr2.setRectRadii(rect, radii);
198
199 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
200 r = rr2.rect();
201 REPORTER_ASSERT(reporter, rect == r);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000202
203 //----
204 SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
205
206 SkRRect rr3;
207 rr3.setRectRadii(rect, radii2);
208 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr3.type());
209}
210
211// Test out the cases when the RR degenerates to an oval
212static void test_round_rect_ovals(skiatest::Reporter* reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000213 //----
214 SkRect oval;
215 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
216 SkRRect rr1;
217 rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight));
218
219 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr1.type());
220 oval = rr1.rect();
221 REPORTER_ASSERT(reporter, oval == rect);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000222}
223
224// Test out the non-degenerate RR cases
225static void test_round_rect_general(skiatest::Reporter* reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000226 //----
227 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
228 SkRRect rr1;
229 rr1.setRectXY(rect, 20, 20);
230
231 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr1.type());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000232
233 //----
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000234 SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
235
236 SkRRect rr2;
237 rr2.setRectRadii(rect, radii);
238
239 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr2.type());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000240}
241
242// Test out questionable-parameter handling
243static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) {
244
245 // When the radii exceed the base rect they are proportionally scaled down
246 // to fit
247 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
248 SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } };
249
250 SkRRect rr1;
251 rr1.setRectRadii(rect, radii);
252
253 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr1.type());
254
255 const SkPoint& p = rr1.radii(SkRRect::kUpperLeft_Corner);
256
257 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fX, 33.33333f));
258 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fY, 66.66666f));
259
260 // Negative radii should be capped at zero
261 SkRRect rr2;
262 rr2.setRectXY(rect, -10, -20);
263
264 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
265
266 const SkPoint& p2 = rr2.radii(SkRRect::kUpperLeft_Corner);
267
268 REPORTER_ASSERT(reporter, 0.0f == p2.fX);
269 REPORTER_ASSERT(reporter, 0.0f == p2.fY);
270}
271
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000272// Move a small box from the start position by (stepX, stepY) 'numSteps' times
273// testing for containment in 'rr' at each step.
274static void test_direction(skiatest::Reporter* reporter, const SkRRect &rr,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000275 SkScalar initX, int stepX, SkScalar initY, int stepY,
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000276 int numSteps, const bool* contains) {
277 SkScalar x = initX, y = initY;
278 for (int i = 0; i < numSteps; ++i) {
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000279 SkRect test = SkRect::MakeXYWH(x, y,
280 stepX ? SkIntToScalar(stepX) : SK_Scalar1,
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000281 stepY ? SkIntToScalar(stepY) : SK_Scalar1);
282 test.sort();
283
284 REPORTER_ASSERT(reporter, contains[i] == rr.contains(test));
285
286 x += stepX;
287 y += stepY;
288 }
289}
290
291// Exercise the RR's contains rect method
292static void test_round_rect_contains_rect(skiatest::Reporter* reporter) {
293
294 static const int kNumRRects = 4;
295 static const SkVector gRadii[kNumRRects][4] = {
296 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // rect
297 { { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 } }, // circle
298 { { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 10 } }, // simple
299 { { 0, 0 }, { 20, 20 }, { 10, 10 }, { 30, 30 } } // complex
300 };
301
302 SkRRect rrects[kNumRRects];
303 for (int i = 0; i < kNumRRects; ++i) {
304 rrects[i].setRectRadii(SkRect::MakeWH(40, 40), gRadii[i]);
305 }
306
307 // First test easy outs - boxes that are obviously out on
308 // each corner and edge
309 static const SkRect easyOuts[] = {
310 { -5, -5, 5, 5 }, // NW
311 { 15, -5, 20, 5 }, // N
312 { 35, -5, 45, 5 }, // NE
313 { 35, 15, 45, 20 }, // E
314 { 35, 45, 35, 45 }, // SE
315 { 15, 35, 20, 45 }, // S
316 { -5, 35, 5, 45 }, // SW
317 { -5, 15, 5, 20 } // W
318 };
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000319
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000320 for (int i = 0; i < kNumRRects; ++i) {
321 for (size_t j = 0; j < SK_ARRAY_COUNT(easyOuts); ++j) {
322 REPORTER_ASSERT(reporter, !rrects[i].contains(easyOuts[j]));
323 }
324 }
325
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000326 // Now test non-trivial containment. For each compass
327 // point walk a 1x1 rect in from the edge of the bounding
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000328 // rect
329 static const int kNumSteps = 15;
330 bool answers[kNumRRects][8][kNumSteps] = {
331 // all the test rects are inside the degenerate rrect
332 {
333 // rect
334 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
335 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
336 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
337 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
338 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
339 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
340 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
341 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
342 },
343 // for the circle we expect 6 blocks to be out on the
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000344 // corners (then the rest in) and only the first block
345 // out on the vertical and horizontal axes (then
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000346 // the rest in)
347 {
348 // circle
349 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
350 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
351 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
352 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
353 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
354 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
355 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
356 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
357 },
358 // for the simple round rect we expect 3 out on
359 // the corners (then the rest in) and no blocks out
360 // on the vertical and horizontal axes
361 {
362 // simple RR
363 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
364 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
365 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
366 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
367 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
368 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
369 { 0, 0, 0, 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 },
372 // for the complex case the answer is different for each direction
373 {
374 // complex RR
375 // all in for NW (rect) corner (same as rect case)
376 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
377 // only first block out for N (same as circle case)
378 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
379 // first 6 blocks out for NE (same as circle case)
380 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
381 // only first block out for E (same as circle case)
382 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
383 // first 3 blocks out for SE (same as simple case)
384 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
385 // first two blocks out for S
386 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
387 // first 9 blocks out for SW
388 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
389 // first two blocks out for W (same as S)
390 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
391 }
392 };
393
394 for (int i = 0; i < kNumRRects; ++i) {
395 test_direction(reporter, rrects[i], 0, 1, 0, 1, kNumSteps, answers[i][0]); // NW
396 test_direction(reporter, rrects[i], 19.5f, 0, 0, 1, kNumSteps, answers[i][1]); // N
397 test_direction(reporter, rrects[i], 40, -1, 0, 1, kNumSteps, answers[i][2]); // NE
398 test_direction(reporter, rrects[i], 40, -1, 19.5f, 0, kNumSteps, answers[i][3]); // E
399 test_direction(reporter, rrects[i], 40, -1, 40, -1, kNumSteps, answers[i][4]); // SE
400 test_direction(reporter, rrects[i], 19.5f, 0, 40, -1, kNumSteps, answers[i][5]); // S
401 test_direction(reporter, rrects[i], 0, 1, 40, -1, kNumSteps, answers[i][6]); // SW
402 test_direction(reporter, rrects[i], 0, 1, 19.5f, 0, kNumSteps, answers[i][7]); // W
403 }
404}
405
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000406// Called for a matrix that should cause SkRRect::transform to fail.
407static void assert_transform_failure(skiatest::Reporter* reporter, const SkRRect& orig,
408 const SkMatrix& matrix) {
409 // The test depends on the fact that the original is not empty.
410 SkASSERT(!orig.isEmpty());
411 SkRRect dst;
412 dst.setEmpty();
413
414 const SkRRect copyOfDst = dst;
415 const SkRRect copyOfOrig = orig;
416 bool success = orig.transform(matrix, &dst);
417 // This transform should fail.
418 REPORTER_ASSERT(reporter, !success);
419 // Since the transform failed, dst should be unchanged.
420 REPORTER_ASSERT(reporter, copyOfDst == dst);
421 // original should not be modified.
422 REPORTER_ASSERT(reporter, copyOfOrig == orig);
423 REPORTER_ASSERT(reporter, orig != dst);
424}
425
426#define GET_RADII \
427 const SkVector& origUL = orig.radii(SkRRect::kUpperLeft_Corner); \
428 const SkVector& origUR = orig.radii(SkRRect::kUpperRight_Corner); \
429 const SkVector& origLR = orig.radii(SkRRect::kLowerRight_Corner); \
430 const SkVector& origLL = orig.radii(SkRRect::kLowerLeft_Corner); \
431 const SkVector& dstUL = dst.radii(SkRRect::kUpperLeft_Corner); \
432 const SkVector& dstUR = dst.radii(SkRRect::kUpperRight_Corner); \
433 const SkVector& dstLR = dst.radii(SkRRect::kLowerRight_Corner); \
434 const SkVector& dstLL = dst.radii(SkRRect::kLowerLeft_Corner)
435
436// Called to test various transforms on a single SkRRect.
437static void test_transform_helper(skiatest::Reporter* reporter, const SkRRect& orig) {
438 SkRRect dst;
439 dst.setEmpty();
440
441 // The identity matrix will duplicate the rrect.
442 bool success = orig.transform(SkMatrix::I(), &dst);
443 REPORTER_ASSERT(reporter, success);
444 REPORTER_ASSERT(reporter, orig == dst);
445
446 // Skew and Perspective make transform fail.
447 SkMatrix matrix;
448 matrix.reset();
449 matrix.setSkewX(SkIntToScalar(2));
450 assert_transform_failure(reporter, orig, matrix);
451
452 matrix.reset();
453 matrix.setSkewY(SkIntToScalar(3));
454 assert_transform_failure(reporter, orig, matrix);
455
456 matrix.reset();
reed3f43f8a2015-01-20 19:58:36 -0800457 matrix.setPerspX(4);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000458 assert_transform_failure(reporter, orig, matrix);
459
460 matrix.reset();
reed3f43f8a2015-01-20 19:58:36 -0800461 matrix.setPerspY(5);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000462 assert_transform_failure(reporter, orig, matrix);
463
464 // Rotation fails.
465 matrix.reset();
466 matrix.setRotate(SkIntToScalar(90));
467 assert_transform_failure(reporter, orig, matrix);
468 matrix.setRotate(SkIntToScalar(37));
469 assert_transform_failure(reporter, orig, matrix);
470
471 // Translate will keep the rect moved, but otherwise the same.
472 matrix.reset();
473 SkScalar translateX = SkIntToScalar(32);
474 SkScalar translateY = SkIntToScalar(15);
475 matrix.setTranslateX(translateX);
476 matrix.setTranslateY(translateY);
477 dst.setEmpty();
478 success = orig.transform(matrix, &dst);
479 REPORTER_ASSERT(reporter, success);
480 for (int i = 0; i < 4; ++i) {
481 REPORTER_ASSERT(reporter,
482 orig.radii((SkRRect::Corner) i) == dst.radii((SkRRect::Corner) i));
483 }
484 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
485 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
486 REPORTER_ASSERT(reporter, dst.rect().left() == orig.rect().left() + translateX);
487 REPORTER_ASSERT(reporter, dst.rect().top() == orig.rect().top() + translateY);
488
489 // Keeping the translation, but adding skew will make transform fail.
490 matrix.setSkewY(SkIntToScalar(7));
491 assert_transform_failure(reporter, orig, matrix);
492
493 // Scaling in -x will flip the round rect horizontally.
494 matrix.reset();
495 matrix.setScaleX(SkIntToScalar(-1));
496 dst.setEmpty();
497 success = orig.transform(matrix, &dst);
498 REPORTER_ASSERT(reporter, success);
499 {
500 GET_RADII;
501 // Radii have swapped in x.
502 REPORTER_ASSERT(reporter, origUL == dstUR);
503 REPORTER_ASSERT(reporter, origUR == dstUL);
504 REPORTER_ASSERT(reporter, origLR == dstLL);
505 REPORTER_ASSERT(reporter, origLL == dstLR);
506 }
507 // Width and height remain the same.
508 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
509 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
510 // Right and left have swapped (sort of)
511 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
512 // Top has stayed the same.
513 REPORTER_ASSERT(reporter, orig.rect().top() == dst.rect().top());
514
515 // Keeping the scale, but adding a persp will make transform fail.
reed3f43f8a2015-01-20 19:58:36 -0800516 matrix.setPerspX(7);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000517 assert_transform_failure(reporter, orig, matrix);
518
519 // Scaling in -y will flip the round rect vertically.
520 matrix.reset();
521 matrix.setScaleY(SkIntToScalar(-1));
522 dst.setEmpty();
523 success = orig.transform(matrix, &dst);
524 REPORTER_ASSERT(reporter, success);
525 {
526 GET_RADII;
527 // Radii have swapped in y.
528 REPORTER_ASSERT(reporter, origUL == dstLL);
529 REPORTER_ASSERT(reporter, origUR == dstLR);
530 REPORTER_ASSERT(reporter, origLR == dstUR);
531 REPORTER_ASSERT(reporter, origLL == dstUL);
532 }
533 // Width and height remain the same.
534 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
535 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
536 // Top and bottom have swapped (sort of)
537 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
538 // Left has stayed the same.
539 REPORTER_ASSERT(reporter, orig.rect().left() == dst.rect().left());
540
541 // Scaling in -x and -y will swap in both directions.
542 matrix.reset();
543 matrix.setScaleY(SkIntToScalar(-1));
544 matrix.setScaleX(SkIntToScalar(-1));
545 dst.setEmpty();
546 success = orig.transform(matrix, &dst);
547 REPORTER_ASSERT(reporter, success);
548 {
549 GET_RADII;
550 REPORTER_ASSERT(reporter, origUL == dstLR);
551 REPORTER_ASSERT(reporter, origUR == dstLL);
552 REPORTER_ASSERT(reporter, origLR == dstUL);
553 REPORTER_ASSERT(reporter, origLL == dstUR);
554 }
555 // Width and height remain the same.
556 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
557 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
558 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
559 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
560
561 // Scale in both directions.
562 SkScalar xScale = SkIntToScalar(3);
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000563 SkScalar yScale = 3.2f;
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000564 matrix.reset();
565 matrix.setScaleX(xScale);
566 matrix.setScaleY(yScale);
567 dst.setEmpty();
568 success = orig.transform(matrix, &dst);
569 REPORTER_ASSERT(reporter, success);
570 // Radii are scaled.
571 for (int i = 0; i < 4; ++i) {
572 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX,
573 SkScalarMul(orig.radii((SkRRect::Corner) i).fX, xScale)));
574 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY,
575 SkScalarMul(orig.radii((SkRRect::Corner) i).fY, yScale)));
576 }
577 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(),
578 SkScalarMul(orig.rect().width(), xScale)));
579 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(),
580 SkScalarMul(orig.rect().height(), yScale)));
581 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(),
582 SkScalarMul(orig.rect().left(), xScale)));
583 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(),
584 SkScalarMul(orig.rect().top(), yScale)));
585}
586
587static void test_round_rect_transform(skiatest::Reporter* reporter) {
588 SkRRect rrect;
589 {
590 SkRect r = { 0, 0, kWidth, kHeight };
591 rrect.setRectXY(r, SkIntToScalar(4), SkIntToScalar(7));
592 test_transform_helper(reporter, rrect);
593 }
594 {
595 SkRect r = { SkIntToScalar(5), SkIntToScalar(15),
596 SkIntToScalar(27), SkIntToScalar(34) };
597 SkVector radii[4] = { { 0, SkIntToScalar(1) },
598 { SkIntToScalar(2), SkIntToScalar(3) },
599 { SkIntToScalar(4), SkIntToScalar(5) },
600 { SkIntToScalar(6), SkIntToScalar(7) } };
601 rrect.setRectRadii(r, radii);
602 test_transform_helper(reporter, rrect);
603 }
604}
605
robertphillipse5c1e3c2014-06-27 08:59:26 -0700606// Test out the case where an oval already off in space is translated/scaled
607// further off into space - yielding numerical issues when the rect & radii
608// are transformed separatly
609// BUG=skia:2696
610static void test_issue_2696(skiatest::Reporter* reporter) {
611 SkRRect rrect;
612 SkRect r = { 28443.8594f, 53.1428604f, 28446.7148f, 56.0000038f };
613 rrect.setOval(r);
614
615 SkMatrix xform;
616 xform.setAll(2.44f, 0.0f, 485411.7f,
617 0.0f, 2.44f, -438.7f,
618 0.0f, 0.0f, 1.0f);
619 SkRRect dst;
620
621 bool success = rrect.transform(xform, &dst);
622 REPORTER_ASSERT(reporter, success);
623
624 SkScalar halfWidth = SkScalarHalf(dst.width());
625 SkScalar halfHeight = SkScalarHalf(dst.height());
626
627 for (int i = 0; i < 4; ++i) {
628 REPORTER_ASSERT(reporter,
629 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth));
630 REPORTER_ASSERT(reporter,
631 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight));
632 }
633}
634
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000635DEF_TEST(RoundRect, reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000636 test_round_rect_basic(reporter);
637 test_round_rect_rects(reporter);
638 test_round_rect_ovals(reporter);
639 test_round_rect_general(reporter);
640 test_round_rect_iffy_parameters(reporter);
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000641 test_inset(reporter);
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000642 test_round_rect_contains_rect(reporter);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000643 test_round_rect_transform(reporter);
robertphillipse5c1e3c2014-06-27 08:59:26 -0700644 test_issue_2696(reporter);
reed694b0d12015-02-13 14:33:02 -0800645 test_tricky_radii_crbug_458522(reporter);
646 test_empty_crbug_458524(reporter);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000647}