1 /*
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
8 #include "SkMatrix.h"
9 #include "SkRRect.h"
10 #include "Test.h"
11
test_tricky_radii(skiatest::Reporter * reporter)12 static 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 }
35 }
36
test_empty_crbug_458524(skiatest::Reporter * reporter)37 static 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
50 static const SkScalar kWidth = 100.0f;
51 static const SkScalar kHeight = 100.0f;
52
test_inset(skiatest::Reporter * reporter)53 static 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());
63
64 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
74
test_9patch_rrect(skiatest::Reporter * reporter,const SkRect & rect,SkScalar l,SkScalar t,SkScalar r,SkScalar b,bool checkRadii)75 static 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
100 // Test out the basic API entry points
test_round_rect_basic(skiatest::Reporter * reporter)101 static void test_round_rect_basic(skiatest::Reporter* reporter) {
102 // Test out initialization methods
103 SkPoint zeroPt = { 0, 0 };
104 SkRRect empty;
105
106 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 }
127 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());
134
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) {
144 REPORTER_ASSERT(reporter,
145 rr2.radii((SkRRect::Corner) i).equalsWithinTolerance(halfPoint));
146 }
147 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());
155
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 }
167 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());
174
175 //----
176 test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true);
177
178 {
179 // Test out the rrect from skia:3466
180 SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f);
181
182 test_9patch_rrect(reporter,
183 rect2,
184 0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f,
185 false);
186 }
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);
203 REPORTER_ASSERT(reporter, rr3 != rr5);
204 }
205
206 // Test out the cases when the RR degenerates to a rect
test_round_rect_rects(skiatest::Reporter * reporter)207 static void test_round_rect_rects(skiatest::Reporter* reporter) {
208 SkRect r;
209
210 //----
211 SkRRect empty;
212
213 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);
227
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);
237
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
test_round_rect_ovals(skiatest::Reporter * reporter)247 static void test_round_rect_ovals(skiatest::Reporter* reporter) {
248 //----
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);
257 }
258
259 // Test out the non-degenerate RR cases
test_round_rect_general(skiatest::Reporter * reporter)260 static void test_round_rect_general(skiatest::Reporter* reporter) {
261 //----
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());
267
268 //----
269 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());
275 }
276
277 // Test out questionable-parameter handling
test_round_rect_iffy_parameters(skiatest::Reporter * reporter)278 static 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
307 // Move a small box from the start position by (stepX, stepY) 'numSteps' times
308 // testing for containment in 'rr' at each step.
test_direction(skiatest::Reporter * reporter,const SkRRect & rr,SkScalar initX,int stepX,SkScalar initY,int stepY,int numSteps,const bool * contains)309 static void test_direction(skiatest::Reporter* reporter, const SkRRect &rr,
310 SkScalar initX, int stepX, SkScalar initY, int stepY,
311 int numSteps, const bool* contains) {
312 SkScalar x = initX, y = initY;
313 for (int i = 0; i < numSteps; ++i) {
314 SkRect test = SkRect::MakeXYWH(x, y,
315 stepX ? SkIntToScalar(stepX) : SK_Scalar1,
316 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
test_round_rect_contains_rect(skiatest::Reporter * reporter)327 static 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 };
354
355 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
361 // Now test non-trivial containment. For each compass
362 // point walk a 1x1 rect in from the edge of the bounding
363 // 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
379 // corners (then the rest in) and only the first block
380 // out on the vertical and horizontal axes (then
381 // 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
441 // Called for a matrix that should cause SkRRect::transform to fail.
assert_transform_failure(skiatest::Reporter * reporter,const SkRRect & orig,const SkMatrix & matrix)442 static 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.
test_transform_helper(skiatest::Reporter * reporter,const SkRRect & orig)472 static 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();
492 matrix.setPerspX(4);
493 assert_transform_failure(reporter, orig, matrix);
494
495 matrix.reset();
496 matrix.setPerspY(5);
497 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.
551 matrix.setPerspX(7);
552 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);
598 SkScalar yScale = 3.2f;
599 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
test_round_rect_transform(skiatest::Reporter * reporter)622 static 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
641 // 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
test_issue_2696(skiatest::Reporter * reporter)645 static 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
DEF_TEST(RoundRect,reporter)670 DEF_TEST(RoundRect, reporter) {
671 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);
676 test_inset(reporter);
677 test_round_rect_contains_rect(reporter);
678 test_round_rect_transform(reporter);
679 test_issue_2696(reporter);
680 test_tricky_radii(reporter);
681 test_empty_crbug_458524(reporter);
682 }
683