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 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 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 // Test that all the SkRRect entry points correctly handle un-sorted and 51 // zero-sized input rects 52 static 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 105 static const SkScalar kWidth = 100.0f; 106 static const SkScalar kHeight = 100.0f; 107 108 static 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()); 118 119 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 129 130 static 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 155 // Test out the basic API entry points 156 static void test_round_rect_basic(skiatest::Reporter* reporter) { 157 // Test out initialization methods 158 SkPoint zeroPt = { 0, 0 }; 159 SkRRect empty; 160 161 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 } 182 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()); 189 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) { 199 REPORTER_ASSERT(reporter, 200 rr2.radii((SkRRect::Corner) i).equalsWithinTolerance(halfPoint)); 201 } 202 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()); 210 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 } 222 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()); 229 230 //---- 231 test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true); 232 233 { 234 // Test out the rrect from skia:3466 235 SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f); 236 237 test_9patch_rrect(reporter, 238 rect2, 239 0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f, 240 false); 241 } 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); 258 REPORTER_ASSERT(reporter, rr3 != rr5); 259 } 260 261 // Test out the cases when the RR degenerates to a rect 262 static void test_round_rect_rects(skiatest::Reporter* reporter) { 263 SkRect r; 264 265 //---- 266 SkRRect empty; 267 268 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); 282 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); 292 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 302 static void test_round_rect_ovals(skiatest::Reporter* reporter) { 303 //---- 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); 312 } 313 314 // Test out the non-degenerate RR cases 315 static void test_round_rect_general(skiatest::Reporter* reporter) { 316 //---- 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()); 322 323 //---- 324 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()); 330 } 331 332 // Test out questionable-parameter handling 333 static 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 362 // Move a small box from the start position by (stepX, stepY) 'numSteps' times 363 // testing for containment in 'rr' at each step. 364 static void test_direction(skiatest::Reporter* reporter, const SkRRect &rr, 365 SkScalar initX, int stepX, SkScalar initY, int stepY, 366 int numSteps, const bool* contains) { 367 SkScalar x = initX, y = initY; 368 for (int i = 0; i < numSteps; ++i) { 369 SkRect test = SkRect::MakeXYWH(x, y, 370 stepX ? SkIntToScalar(stepX) : SK_Scalar1, 371 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 382 static 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 }; 409 410 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 416 // Now test non-trivial containment. For each compass 417 // point walk a 1x1 rect in from the edge of the bounding 418 // 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 434 // corners (then the rest in) and only the first block 435 // out on the vertical and horizontal axes (then 436 // 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 496 // Called for a matrix that should cause SkRRect::transform to fail. 497 static 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. 527 static 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(); 547 matrix.setPerspX(4); 548 assert_transform_failure(reporter, orig, matrix); 549 550 matrix.reset(); 551 matrix.setPerspY(5); 552 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. 606 matrix.setPerspX(7); 607 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); 653 SkScalar yScale = 3.2f; 654 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, 663 SkScalarMul(orig.radii((SkRRect::Corner) i).fX, xScale))); 664 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY, 665 SkScalarMul(orig.radii((SkRRect::Corner) i).fY, yScale))); 666 } 667 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(), 668 SkScalarMul(orig.rect().width(), xScale))); 669 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(), 670 SkScalarMul(orig.rect().height(), yScale))); 671 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(), 672 SkScalarMul(orig.rect().left(), xScale))); 673 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(), 674 SkScalarMul(orig.rect().top(), yScale))); 675 } 676 677 static 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 696 // Test out the case where an oval already off in space is translated/scaled 697 // further off into space - yielding numerical issues when the rect & radii 698 // are transformed separatly 699 // BUG=skia:2696 700 static 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) { 718 REPORTER_ASSERT(reporter, 719 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth)); 720 REPORTER_ASSERT(reporter, 721 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight)); 722 } 723 } 724 725 DEF_TEST(RoundRect, reporter) { 726 test_round_rect_basic(reporter); 727 test_round_rect_rects(reporter); 728 test_round_rect_ovals(reporter); 729 test_round_rect_general(reporter); 730 test_round_rect_iffy_parameters(reporter); 731 test_inset(reporter); 732 test_round_rect_contains_rect(reporter); 733 test_round_rect_transform(reporter); 734 test_issue_2696(reporter); 735 test_tricky_radii(reporter); 736 test_empty_crbug_458524(reporter); 737 test_empty(reporter); 738 } 739