blob: 261ec67ca0963cc313db4d1c178bec6505d69e47 [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
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +000012static const SkScalar kWidth = 100.0f;
13static const SkScalar kHeight = 100.0f;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000014
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +000015static void test_inset(skiatest::Reporter* reporter) {
16 SkRRect rr, rr2;
17 SkRect r = { 0, 0, 100, 100 };
18
19 rr.setRect(r);
20 rr.inset(-20, -20, &rr2);
21 REPORTER_ASSERT(reporter, rr2.isRect());
22
23 rr.inset(20, 20, &rr2);
24 REPORTER_ASSERT(reporter, rr2.isRect());
skia.committer@gmail.com1a60dab2012-12-24 02:01:25 +000025
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +000026 rr.inset(r.width()/2, r.height()/2, &rr2);
27 REPORTER_ASSERT(reporter, rr2.isEmpty());
28
29 rr.setRectXY(r, 20, 20);
30 rr.inset(19, 19, &rr2);
31 REPORTER_ASSERT(reporter, rr2.isSimple());
32 rr.inset(20, 20, &rr2);
33 REPORTER_ASSERT(reporter, rr2.isRect());
34}
35
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000036// Test out the basic API entry points
37static void test_round_rect_basic(skiatest::Reporter* reporter) {
38 // Test out initialization methods
reed@google.com2b57dc62013-01-08 13:23:32 +000039 SkPoint zeroPt = { 0, 0 };
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000040 SkRRect empty;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000041
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000042 empty.setEmpty();
43
44 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
45 REPORTER_ASSERT(reporter, empty.rect().isEmpty());
46
47 for (int i = 0; i < 4; ++i) {
48 REPORTER_ASSERT(reporter, zeroPt == empty.radii((SkRRect::Corner) i));
49 }
50
51 //----
52 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
53
54 SkRRect rr1;
55 rr1.setRect(rect);
56
57 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
58 REPORTER_ASSERT(reporter, rr1.rect() == rect);
59
60 for (int i = 0; i < 4; ++i) {
61 REPORTER_ASSERT(reporter, zeroPt == rr1.radii((SkRRect::Corner) i));
62 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +000063 SkRRect rr1_2; // construct the same RR using the most general set function
64 SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
65 rr1_2.setRectRadii(rect, rr1_2_radii);
66 REPORTER_ASSERT(reporter, rr1_2 == rr1 && rr1_2.getType() == rr1.getType());
67 SkRRect rr1_3; // construct the same RR using the nine patch set function
68 rr1_3.setNinePatch(rect, 0, 0, 0, 0);
69 REPORTER_ASSERT(reporter, rr1_3 == rr1 && rr1_3.getType() == rr1.getType());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000070
71 //----
72 SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) };
73 SkRRect rr2;
74 rr2.setOval(rect);
75
76 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr2.type());
77 REPORTER_ASSERT(reporter, rr2.rect() == rect);
78
79 for (int i = 0; i < 4; ++i) {
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +000080 REPORTER_ASSERT(reporter,
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000081 rr2.radii((SkRRect::Corner) i).equalsWithinTolerance(halfPoint));
82 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +000083 SkRRect rr2_2; // construct the same RR using the most general set function
84 SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY },
85 { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY } };
86 rr2_2.setRectRadii(rect, rr2_2_radii);
87 REPORTER_ASSERT(reporter, rr2_2 == rr2 && rr2_2.getType() == rr2.getType());
88 SkRRect rr2_3; // construct the same RR using the nine patch set function
89 rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY);
90 REPORTER_ASSERT(reporter, rr2_3 == rr2 && rr2_3.getType() == rr2.getType());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +000091
92 //----
93 SkPoint p = { 5, 5 };
94 SkRRect rr3;
95 rr3.setRectXY(rect, p.fX, p.fY);
96
97 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr3.type());
98 REPORTER_ASSERT(reporter, rr3.rect() == rect);
99
100 for (int i = 0; i < 4; ++i) {
101 REPORTER_ASSERT(reporter, p == rr3.radii((SkRRect::Corner) i));
102 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000103 SkRRect rr3_2; // construct the same RR using the most general set function
104 SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } };
105 rr3_2.setRectRadii(rect, rr3_2_radii);
106 REPORTER_ASSERT(reporter, rr3_2 == rr3 && rr3_2.getType() == rr3.getType());
107 SkRRect rr3_3; // construct the same RR using the nine patch set function
108 rr3_3.setNinePatch(rect, 5, 5, 5, 5);
109 REPORTER_ASSERT(reporter, rr3_3 == rr3 && rr3_3.getType() == rr3.getType());
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000110
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000111 //----
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000112 SkRect ninePatchRadii = { 10, 9, 8, 7 };
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000113
114 SkRRect rr4;
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000115 rr4.setNinePatch(rect, ninePatchRadii.fLeft, ninePatchRadii.fTop, ninePatchRadii.fRight,
116 ninePatchRadii.fBottom);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000117
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000118 REPORTER_ASSERT(reporter, SkRRect::kNinePatch_Type == rr4.type());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000119 REPORTER_ASSERT(reporter, rr4.rect() == rect);
120
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000121 SkPoint rquad[4];
122 ninePatchRadii.toQuad(rquad);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000123 for (int i = 0; i < 4; ++i) {
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000124 REPORTER_ASSERT(reporter, rquad[i] == rr4.radii((SkRRect::Corner) i));
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000125 }
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000126 SkRRect rr4_2; // construct the same RR using the most general set function
127 SkVector rr4_2_radii[4] = { { 10, 9 }, { 8, 9 }, {8, 7 }, { 10, 7 } };
128 rr4_2.setRectRadii(rect, rr4_2_radii);
129 REPORTER_ASSERT(reporter, rr4_2 == rr4 && rr4_2.getType() == rr4.getType());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000130
131 //----
132 SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } };
133
134 SkRRect rr5;
135 rr5.setRectRadii(rect, radii2);
136
137 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr5.type());
138 REPORTER_ASSERT(reporter, rr5.rect() == rect);
139
140 for (int i = 0; i < 4; ++i) {
141 REPORTER_ASSERT(reporter, radii2[i] == rr5.radii((SkRRect::Corner) i));
142 }
143
144 // Test out == & !=
145 REPORTER_ASSERT(reporter, empty != rr3);
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000146 REPORTER_ASSERT(reporter, rr3 != rr4);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000147 REPORTER_ASSERT(reporter, rr4 != rr5);
148}
149
150// Test out the cases when the RR degenerates to a rect
151static void test_round_rect_rects(skiatest::Reporter* reporter) {
152 SkRect r;
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000153
154 //----
155 SkRRect empty;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +0000156
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000157 empty.setEmpty();
158
159 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
160 r = empty.rect();
161 REPORTER_ASSERT(reporter, 0 == r.fLeft && 0 == r.fTop && 0 == r.fRight && 0 == r.fBottom);
162
163 //----
164 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
165 SkRRect rr1;
166 rr1.setRectXY(rect, 0, 0);
167
168 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
169 r = rr1.rect();
170 REPORTER_ASSERT(reporter, rect == r);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000171
172 //----
173 SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
174
175 SkRRect rr2;
176 rr2.setRectRadii(rect, radii);
177
178 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
179 r = rr2.rect();
180 REPORTER_ASSERT(reporter, rect == r);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000181
182 //----
183 SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
184
185 SkRRect rr3;
186 rr3.setRectRadii(rect, radii2);
187 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr3.type());
188}
189
190// Test out the cases when the RR degenerates to an oval
191static void test_round_rect_ovals(skiatest::Reporter* reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000192 //----
193 SkRect oval;
194 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
195 SkRRect rr1;
196 rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight));
197
198 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr1.type());
199 oval = rr1.rect();
200 REPORTER_ASSERT(reporter, oval == rect);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000201}
202
203// Test out the non-degenerate RR cases
204static void test_round_rect_general(skiatest::Reporter* reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000205 //----
206 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
207 SkRRect rr1;
208 rr1.setRectXY(rect, 20, 20);
209
210 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr1.type());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000211
212 //----
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000213 SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
214
215 SkRRect rr2;
216 rr2.setRectRadii(rect, radii);
217
218 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr2.type());
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000219}
220
221// Test out questionable-parameter handling
222static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) {
223
224 // When the radii exceed the base rect they are proportionally scaled down
225 // to fit
226 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
227 SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } };
228
229 SkRRect rr1;
230 rr1.setRectRadii(rect, radii);
231
232 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr1.type());
233
234 const SkPoint& p = rr1.radii(SkRRect::kUpperLeft_Corner);
235
236 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fX, 33.33333f));
237 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fY, 66.66666f));
238
239 // Negative radii should be capped at zero
240 SkRRect rr2;
241 rr2.setRectXY(rect, -10, -20);
242
243 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
244
245 const SkPoint& p2 = rr2.radii(SkRRect::kUpperLeft_Corner);
246
247 REPORTER_ASSERT(reporter, 0.0f == p2.fX);
248 REPORTER_ASSERT(reporter, 0.0f == p2.fY);
249}
250
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000251// Move a small box from the start position by (stepX, stepY) 'numSteps' times
252// testing for containment in 'rr' at each step.
253static void test_direction(skiatest::Reporter* reporter, const SkRRect &rr,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000254 SkScalar initX, int stepX, SkScalar initY, int stepY,
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000255 int numSteps, const bool* contains) {
256 SkScalar x = initX, y = initY;
257 for (int i = 0; i < numSteps; ++i) {
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000258 SkRect test = SkRect::MakeXYWH(x, y,
259 stepX ? SkIntToScalar(stepX) : SK_Scalar1,
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000260 stepY ? SkIntToScalar(stepY) : SK_Scalar1);
261 test.sort();
262
263 REPORTER_ASSERT(reporter, contains[i] == rr.contains(test));
264
265 x += stepX;
266 y += stepY;
267 }
268}
269
270// Exercise the RR's contains rect method
271static void test_round_rect_contains_rect(skiatest::Reporter* reporter) {
272
273 static const int kNumRRects = 4;
274 static const SkVector gRadii[kNumRRects][4] = {
275 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // rect
276 { { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 } }, // circle
277 { { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 10 } }, // simple
278 { { 0, 0 }, { 20, 20 }, { 10, 10 }, { 30, 30 } } // complex
279 };
280
281 SkRRect rrects[kNumRRects];
282 for (int i = 0; i < kNumRRects; ++i) {
283 rrects[i].setRectRadii(SkRect::MakeWH(40, 40), gRadii[i]);
284 }
285
286 // First test easy outs - boxes that are obviously out on
287 // each corner and edge
288 static const SkRect easyOuts[] = {
289 { -5, -5, 5, 5 }, // NW
290 { 15, -5, 20, 5 }, // N
291 { 35, -5, 45, 5 }, // NE
292 { 35, 15, 45, 20 }, // E
293 { 35, 45, 35, 45 }, // SE
294 { 15, 35, 20, 45 }, // S
295 { -5, 35, 5, 45 }, // SW
296 { -5, 15, 5, 20 } // W
297 };
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000298
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000299 for (int i = 0; i < kNumRRects; ++i) {
300 for (size_t j = 0; j < SK_ARRAY_COUNT(easyOuts); ++j) {
301 REPORTER_ASSERT(reporter, !rrects[i].contains(easyOuts[j]));
302 }
303 }
304
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000305 // Now test non-trivial containment. For each compass
306 // point walk a 1x1 rect in from the edge of the bounding
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000307 // rect
308 static const int kNumSteps = 15;
309 bool answers[kNumRRects][8][kNumSteps] = {
310 // all the test rects are inside the degenerate rrect
311 {
312 // rect
313 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
314 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
315 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
316 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
317 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
318 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
319 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
320 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
321 },
322 // for the circle we expect 6 blocks to be out on the
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000323 // corners (then the rest in) and only the first block
324 // out on the vertical and horizontal axes (then
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000325 // the rest in)
326 {
327 // circle
328 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
329 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
330 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
331 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
332 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
333 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
334 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
335 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
336 },
337 // for the simple round rect we expect 3 out on
338 // the corners (then the rest in) and no blocks out
339 // on the vertical and horizontal axes
340 {
341 // simple RR
342 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
343 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
344 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
345 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
346 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
347 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
348 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
349 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
350 },
351 // for the complex case the answer is different for each direction
352 {
353 // complex RR
354 // all in for NW (rect) corner (same as rect case)
355 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
356 // only first block out for N (same as circle case)
357 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
358 // first 6 blocks out for NE (same as circle case)
359 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
360 // only first block out for E (same as circle case)
361 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
362 // first 3 blocks out for SE (same as simple case)
363 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
364 // first two blocks out for S
365 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
366 // first 9 blocks out for SW
367 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
368 // first two blocks out for W (same as S)
369 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
370 }
371 };
372
373 for (int i = 0; i < kNumRRects; ++i) {
374 test_direction(reporter, rrects[i], 0, 1, 0, 1, kNumSteps, answers[i][0]); // NW
375 test_direction(reporter, rrects[i], 19.5f, 0, 0, 1, kNumSteps, answers[i][1]); // N
376 test_direction(reporter, rrects[i], 40, -1, 0, 1, kNumSteps, answers[i][2]); // NE
377 test_direction(reporter, rrects[i], 40, -1, 19.5f, 0, kNumSteps, answers[i][3]); // E
378 test_direction(reporter, rrects[i], 40, -1, 40, -1, kNumSteps, answers[i][4]); // SE
379 test_direction(reporter, rrects[i], 19.5f, 0, 40, -1, kNumSteps, answers[i][5]); // S
380 test_direction(reporter, rrects[i], 0, 1, 40, -1, kNumSteps, answers[i][6]); // SW
381 test_direction(reporter, rrects[i], 0, 1, 19.5f, 0, kNumSteps, answers[i][7]); // W
382 }
383}
384
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000385// Called for a matrix that should cause SkRRect::transform to fail.
386static void assert_transform_failure(skiatest::Reporter* reporter, const SkRRect& orig,
387 const SkMatrix& matrix) {
388 // The test depends on the fact that the original is not empty.
389 SkASSERT(!orig.isEmpty());
390 SkRRect dst;
391 dst.setEmpty();
392
393 const SkRRect copyOfDst = dst;
394 const SkRRect copyOfOrig = orig;
395 bool success = orig.transform(matrix, &dst);
396 // This transform should fail.
397 REPORTER_ASSERT(reporter, !success);
398 // Since the transform failed, dst should be unchanged.
399 REPORTER_ASSERT(reporter, copyOfDst == dst);
400 // original should not be modified.
401 REPORTER_ASSERT(reporter, copyOfOrig == orig);
402 REPORTER_ASSERT(reporter, orig != dst);
403}
404
405#define GET_RADII \
406 const SkVector& origUL = orig.radii(SkRRect::kUpperLeft_Corner); \
407 const SkVector& origUR = orig.radii(SkRRect::kUpperRight_Corner); \
408 const SkVector& origLR = orig.radii(SkRRect::kLowerRight_Corner); \
409 const SkVector& origLL = orig.radii(SkRRect::kLowerLeft_Corner); \
410 const SkVector& dstUL = dst.radii(SkRRect::kUpperLeft_Corner); \
411 const SkVector& dstUR = dst.radii(SkRRect::kUpperRight_Corner); \
412 const SkVector& dstLR = dst.radii(SkRRect::kLowerRight_Corner); \
413 const SkVector& dstLL = dst.radii(SkRRect::kLowerLeft_Corner)
414
415// Called to test various transforms on a single SkRRect.
416static void test_transform_helper(skiatest::Reporter* reporter, const SkRRect& orig) {
417 SkRRect dst;
418 dst.setEmpty();
419
420 // The identity matrix will duplicate the rrect.
421 bool success = orig.transform(SkMatrix::I(), &dst);
422 REPORTER_ASSERT(reporter, success);
423 REPORTER_ASSERT(reporter, orig == dst);
424
425 // Skew and Perspective make transform fail.
426 SkMatrix matrix;
427 matrix.reset();
428 matrix.setSkewX(SkIntToScalar(2));
429 assert_transform_failure(reporter, orig, matrix);
430
431 matrix.reset();
432 matrix.setSkewY(SkIntToScalar(3));
433 assert_transform_failure(reporter, orig, matrix);
434
435 matrix.reset();
436 matrix.setPerspX(SkScalarToPersp(SkIntToScalar(4)));
437 assert_transform_failure(reporter, orig, matrix);
438
439 matrix.reset();
440 matrix.setPerspY(SkScalarToPersp(SkIntToScalar(5)));
441 assert_transform_failure(reporter, orig, matrix);
442
443 // Rotation fails.
444 matrix.reset();
445 matrix.setRotate(SkIntToScalar(90));
446 assert_transform_failure(reporter, orig, matrix);
447 matrix.setRotate(SkIntToScalar(37));
448 assert_transform_failure(reporter, orig, matrix);
449
450 // Translate will keep the rect moved, but otherwise the same.
451 matrix.reset();
452 SkScalar translateX = SkIntToScalar(32);
453 SkScalar translateY = SkIntToScalar(15);
454 matrix.setTranslateX(translateX);
455 matrix.setTranslateY(translateY);
456 dst.setEmpty();
457 success = orig.transform(matrix, &dst);
458 REPORTER_ASSERT(reporter, success);
459 for (int i = 0; i < 4; ++i) {
460 REPORTER_ASSERT(reporter,
461 orig.radii((SkRRect::Corner) i) == dst.radii((SkRRect::Corner) i));
462 }
463 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
464 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
465 REPORTER_ASSERT(reporter, dst.rect().left() == orig.rect().left() + translateX);
466 REPORTER_ASSERT(reporter, dst.rect().top() == orig.rect().top() + translateY);
467
468 // Keeping the translation, but adding skew will make transform fail.
469 matrix.setSkewY(SkIntToScalar(7));
470 assert_transform_failure(reporter, orig, matrix);
471
472 // Scaling in -x will flip the round rect horizontally.
473 matrix.reset();
474 matrix.setScaleX(SkIntToScalar(-1));
475 dst.setEmpty();
476 success = orig.transform(matrix, &dst);
477 REPORTER_ASSERT(reporter, success);
478 {
479 GET_RADII;
480 // Radii have swapped in x.
481 REPORTER_ASSERT(reporter, origUL == dstUR);
482 REPORTER_ASSERT(reporter, origUR == dstUL);
483 REPORTER_ASSERT(reporter, origLR == dstLL);
484 REPORTER_ASSERT(reporter, origLL == dstLR);
485 }
486 // Width and height remain the same.
487 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
488 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
489 // Right and left have swapped (sort of)
490 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
491 // Top has stayed the same.
492 REPORTER_ASSERT(reporter, orig.rect().top() == dst.rect().top());
493
494 // Keeping the scale, but adding a persp will make transform fail.
495 matrix.setPerspX(SkScalarToPersp(SkIntToScalar(7)));
496 assert_transform_failure(reporter, orig, matrix);
497
498 // Scaling in -y will flip the round rect vertically.
499 matrix.reset();
500 matrix.setScaleY(SkIntToScalar(-1));
501 dst.setEmpty();
502 success = orig.transform(matrix, &dst);
503 REPORTER_ASSERT(reporter, success);
504 {
505 GET_RADII;
506 // Radii have swapped in y.
507 REPORTER_ASSERT(reporter, origUL == dstLL);
508 REPORTER_ASSERT(reporter, origUR == dstLR);
509 REPORTER_ASSERT(reporter, origLR == dstUR);
510 REPORTER_ASSERT(reporter, origLL == dstUL);
511 }
512 // Width and height remain the same.
513 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
514 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
515 // Top and bottom have swapped (sort of)
516 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
517 // Left has stayed the same.
518 REPORTER_ASSERT(reporter, orig.rect().left() == dst.rect().left());
519
520 // Scaling in -x and -y will swap in both directions.
521 matrix.reset();
522 matrix.setScaleY(SkIntToScalar(-1));
523 matrix.setScaleX(SkIntToScalar(-1));
524 dst.setEmpty();
525 success = orig.transform(matrix, &dst);
526 REPORTER_ASSERT(reporter, success);
527 {
528 GET_RADII;
529 REPORTER_ASSERT(reporter, origUL == dstLR);
530 REPORTER_ASSERT(reporter, origUR == dstLL);
531 REPORTER_ASSERT(reporter, origLR == dstUL);
532 REPORTER_ASSERT(reporter, origLL == dstUR);
533 }
534 // Width and height remain the same.
535 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width());
536 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height());
537 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom());
538 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left());
539
540 // Scale in both directions.
541 SkScalar xScale = SkIntToScalar(3);
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000542 SkScalar yScale = 3.2f;
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000543 matrix.reset();
544 matrix.setScaleX(xScale);
545 matrix.setScaleY(yScale);
546 dst.setEmpty();
547 success = orig.transform(matrix, &dst);
548 REPORTER_ASSERT(reporter, success);
549 // Radii are scaled.
550 for (int i = 0; i < 4; ++i) {
551 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX,
552 SkScalarMul(orig.radii((SkRRect::Corner) i).fX, xScale)));
553 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY,
554 SkScalarMul(orig.radii((SkRRect::Corner) i).fY, yScale)));
555 }
556 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(),
557 SkScalarMul(orig.rect().width(), xScale)));
558 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(),
559 SkScalarMul(orig.rect().height(), yScale)));
560 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(),
561 SkScalarMul(orig.rect().left(), xScale)));
562 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(),
563 SkScalarMul(orig.rect().top(), yScale)));
564}
565
566static void test_round_rect_transform(skiatest::Reporter* reporter) {
567 SkRRect rrect;
568 {
569 SkRect r = { 0, 0, kWidth, kHeight };
570 rrect.setRectXY(r, SkIntToScalar(4), SkIntToScalar(7));
571 test_transform_helper(reporter, rrect);
572 }
573 {
574 SkRect r = { SkIntToScalar(5), SkIntToScalar(15),
575 SkIntToScalar(27), SkIntToScalar(34) };
576 SkVector radii[4] = { { 0, SkIntToScalar(1) },
577 { SkIntToScalar(2), SkIntToScalar(3) },
578 { SkIntToScalar(4), SkIntToScalar(5) },
579 { SkIntToScalar(6), SkIntToScalar(7) } };
580 rrect.setRectRadii(r, radii);
581 test_transform_helper(reporter, rrect);
582 }
583}
584
robertphillipse5c1e3c2014-06-27 08:59:26 -0700585// Test out the case where an oval already off in space is translated/scaled
586// further off into space - yielding numerical issues when the rect & radii
587// are transformed separatly
588// BUG=skia:2696
589static void test_issue_2696(skiatest::Reporter* reporter) {
590 SkRRect rrect;
591 SkRect r = { 28443.8594f, 53.1428604f, 28446.7148f, 56.0000038f };
592 rrect.setOval(r);
593
594 SkMatrix xform;
595 xform.setAll(2.44f, 0.0f, 485411.7f,
596 0.0f, 2.44f, -438.7f,
597 0.0f, 0.0f, 1.0f);
598 SkRRect dst;
599
600 bool success = rrect.transform(xform, &dst);
601 REPORTER_ASSERT(reporter, success);
602
603 SkScalar halfWidth = SkScalarHalf(dst.width());
604 SkScalar halfHeight = SkScalarHalf(dst.height());
605
606 for (int i = 0; i < 4; ++i) {
607 REPORTER_ASSERT(reporter,
608 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth));
609 REPORTER_ASSERT(reporter,
610 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight));
611 }
612}
613
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000614DEF_TEST(RoundRect, reporter) {
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000615 test_round_rect_basic(reporter);
616 test_round_rect_rects(reporter);
617 test_round_rect_ovals(reporter);
618 test_round_rect_general(reporter);
619 test_round_rect_iffy_parameters(reporter);
mike@reedtribe.orgbcbef572012-12-23 23:11:21 +0000620 test_inset(reporter);
robertphillips@google.com32c1b662013-04-25 12:23:00 +0000621 test_round_rect_contains_rect(reporter);
scroggo@google.com20e3cd22013-11-05 15:54:42 +0000622 test_round_rect_transform(reporter);
robertphillipse5c1e3c2014-06-27 08:59:26 -0700623 test_issue_2696(reporter);
robertphillips@google.com5985e7c2012-11-29 13:24:55 +0000624}