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 "SkPointPriv.h" 10 #include "SkRRect.h" 11 #include "Test.h" 12 13 static void test_tricky_radii(skiatest::Reporter* reporter) { 14 { 15 // crbug.com/458522 16 SkRRect rr; 17 const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 }; 18 const SkScalar rad = 12814; 19 const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } }; 20 rr.setRectRadii(bounds, vec); 21 } 22 23 { 24 // crbug.com//463920 25 SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0); 26 SkVector radii[4] = { 27 { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f } 28 }; 29 SkRRect rr; 30 rr.setRectRadii(r, radii); 31 32 REPORTER_ASSERT(reporter, (double) rr.radii(SkRRect::kUpperRight_Corner).fY + 33 (double) rr.radii(SkRRect::kLowerRight_Corner).fY <= 34 rr.height()); 35 } 36 } 37 38 static void test_empty_crbug_458524(skiatest::Reporter* reporter) { 39 SkRRect rr; 40 const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 }; 41 const SkScalar rad = 40; 42 rr.setRectXY(bounds, rad, rad); 43 44 SkRRect other; 45 SkMatrix matrix; 46 matrix.setScale(0, 1); 47 rr.transform(matrix, &other); 48 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == other.getType()); 49 } 50 51 // Test that all the SkRRect entry points correctly handle un-sorted and 52 // zero-sized input rects 53 static void test_empty(skiatest::Reporter* reporter) { 54 static const SkRect oooRects[] = { // out of order 55 { 100, 0, 0, 100 }, // ooo horizontal 56 { 0, 100, 100, 0 }, // ooo vertical 57 { 100, 100, 0, 0 }, // ooo both 58 }; 59 60 static const SkRect emptyRects[] = { 61 { 100, 100, 100, 200 }, // empty horizontal 62 { 100, 100, 200, 100 }, // empty vertical 63 { 100, 100, 100, 100 }, // empty both 64 { 0, 0, 0, 0 } // setEmpty-empty 65 }; 66 67 static const SkVector radii[4] = { { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 } }; 68 69 SkRRect r; 70 71 for (size_t i = 0; i < SK_ARRAY_COUNT(oooRects); ++i) { 72 r.setRect(oooRects[i]); 73 REPORTER_ASSERT(reporter, !r.isEmpty()); 74 REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted()); 75 76 r.setOval(oooRects[i]); 77 REPORTER_ASSERT(reporter, !r.isEmpty()); 78 REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted()); 79 80 r.setRectXY(oooRects[i], 1, 2); 81 REPORTER_ASSERT(reporter, !r.isEmpty()); 82 REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted()); 83 84 r.setNinePatch(oooRects[i], 0, 1, 2, 3); 85 REPORTER_ASSERT(reporter, !r.isEmpty()); 86 REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted()); 87 88 r.setRectRadii(oooRects[i], radii); 89 REPORTER_ASSERT(reporter, !r.isEmpty()); 90 REPORTER_ASSERT(reporter, r.rect() == oooRects[i].makeSorted()); 91 } 92 93 for (size_t i = 0; i < SK_ARRAY_COUNT(emptyRects); ++i) { 94 r.setRect(emptyRects[i]); 95 REPORTER_ASSERT(reporter, r.isEmpty()); 96 REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]); 97 98 r.setOval(emptyRects[i]); 99 REPORTER_ASSERT(reporter, r.isEmpty()); 100 REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]); 101 102 r.setRectXY(emptyRects[i], 1, 2); 103 REPORTER_ASSERT(reporter, r.isEmpty()); 104 REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]); 105 106 r.setNinePatch(emptyRects[i], 0, 1, 2, 3); 107 REPORTER_ASSERT(reporter, r.isEmpty()); 108 REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]); 109 110 r.setRectRadii(emptyRects[i], radii); 111 REPORTER_ASSERT(reporter, r.isEmpty()); 112 REPORTER_ASSERT(reporter, r.rect() == emptyRects[i]); 113 } 114 115 r.setRect({SK_ScalarNaN, 10, 10, 20}); 116 REPORTER_ASSERT(reporter, r == SkRRect::MakeEmpty()); 117 r.setRect({0, 10, 10, SK_ScalarInfinity}); 118 REPORTER_ASSERT(reporter, r == SkRRect::MakeEmpty()); 119 } 120 121 static const SkScalar kWidth = 100.0f; 122 static const SkScalar kHeight = 100.0f; 123 124 static void test_inset(skiatest::Reporter* reporter) { 125 SkRRect rr, rr2; 126 SkRect r = { 0, 0, 100, 100 }; 127 128 rr.setRect(r); 129 rr.inset(-20, -20, &rr2); 130 REPORTER_ASSERT(reporter, rr2.isRect()); 131 132 rr.inset(20, 20, &rr2); 133 REPORTER_ASSERT(reporter, rr2.isRect()); 134 135 rr.inset(r.width()/2, r.height()/2, &rr2); 136 REPORTER_ASSERT(reporter, rr2.isEmpty()); 137 138 rr.setRectXY(r, 20, 20); 139 rr.inset(19, 19, &rr2); 140 REPORTER_ASSERT(reporter, rr2.isSimple()); 141 rr.inset(20, 20, &rr2); 142 REPORTER_ASSERT(reporter, rr2.isRect()); 143 } 144 145 146 static void test_9patch_rrect(skiatest::Reporter* reporter, 147 const SkRect& rect, 148 SkScalar l, SkScalar t, SkScalar r, SkScalar b, 149 bool checkRadii) { 150 SkRRect rr; 151 rr.setNinePatch(rect, l, t, r, b); 152 153 REPORTER_ASSERT(reporter, SkRRect::kNinePatch_Type == rr.type()); 154 REPORTER_ASSERT(reporter, rr.rect() == rect); 155 156 if (checkRadii) { 157 // This test doesn't hold if the radii will be rescaled by SkRRect 158 SkRect ninePatchRadii = { l, t, r, b }; 159 SkPoint rquad[4]; 160 ninePatchRadii.toQuad(rquad); 161 for (int i = 0; i < 4; ++i) { 162 REPORTER_ASSERT(reporter, rquad[i] == rr.radii((SkRRect::Corner) i)); 163 } 164 } 165 SkRRect rr2; // construct the same RR using the most general set function 166 SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } }; 167 rr2.setRectRadii(rect, radii); 168 REPORTER_ASSERT(reporter, rr2 == rr && rr2.getType() == rr.getType()); 169 } 170 171 // Test out the basic API entry points 172 static void test_round_rect_basic(skiatest::Reporter* reporter) { 173 // Test out initialization methods 174 SkPoint zeroPt = { 0, 0 }; 175 SkRRect empty; 176 177 empty.setEmpty(); 178 179 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type()); 180 REPORTER_ASSERT(reporter, empty.rect().isEmpty()); 181 182 for (int i = 0; i < 4; ++i) { 183 REPORTER_ASSERT(reporter, zeroPt == empty.radii((SkRRect::Corner) i)); 184 } 185 186 //---- 187 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 188 189 SkRRect rr1; 190 rr1.setRect(rect); 191 192 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type()); 193 REPORTER_ASSERT(reporter, rr1.rect() == rect); 194 195 for (int i = 0; i < 4; ++i) { 196 REPORTER_ASSERT(reporter, zeroPt == rr1.radii((SkRRect::Corner) i)); 197 } 198 SkRRect rr1_2; // construct the same RR using the most general set function 199 SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; 200 rr1_2.setRectRadii(rect, rr1_2_radii); 201 REPORTER_ASSERT(reporter, rr1_2 == rr1 && rr1_2.getType() == rr1.getType()); 202 SkRRect rr1_3; // construct the same RR using the nine patch set function 203 rr1_3.setNinePatch(rect, 0, 0, 0, 0); 204 REPORTER_ASSERT(reporter, rr1_3 == rr1 && rr1_3.getType() == rr1.getType()); 205 206 //---- 207 SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) }; 208 SkRRect rr2; 209 rr2.setOval(rect); 210 211 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr2.type()); 212 REPORTER_ASSERT(reporter, rr2.rect() == rect); 213 214 for (int i = 0; i < 4; ++i) { 215 REPORTER_ASSERT(reporter, 216 SkPointPriv::EqualsWithinTolerance(rr2.radii((SkRRect::Corner) i), 217 halfPoint)); 218 } 219 SkRRect rr2_2; // construct the same RR using the most general set function 220 SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY }, 221 { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY } }; 222 rr2_2.setRectRadii(rect, rr2_2_radii); 223 REPORTER_ASSERT(reporter, rr2_2 == rr2 && rr2_2.getType() == rr2.getType()); 224 SkRRect rr2_3; // construct the same RR using the nine patch set function 225 rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY); 226 REPORTER_ASSERT(reporter, rr2_3 == rr2 && rr2_3.getType() == rr2.getType()); 227 228 //---- 229 SkPoint p = { 5, 5 }; 230 SkRRect rr3; 231 rr3.setRectXY(rect, p.fX, p.fY); 232 233 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr3.type()); 234 REPORTER_ASSERT(reporter, rr3.rect() == rect); 235 236 for (int i = 0; i < 4; ++i) { 237 REPORTER_ASSERT(reporter, p == rr3.radii((SkRRect::Corner) i)); 238 } 239 SkRRect rr3_2; // construct the same RR using the most general set function 240 SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } }; 241 rr3_2.setRectRadii(rect, rr3_2_radii); 242 REPORTER_ASSERT(reporter, rr3_2 == rr3 && rr3_2.getType() == rr3.getType()); 243 SkRRect rr3_3; // construct the same RR using the nine patch set function 244 rr3_3.setNinePatch(rect, 5, 5, 5, 5); 245 REPORTER_ASSERT(reporter, rr3_3 == rr3 && rr3_3.getType() == rr3.getType()); 246 247 //---- 248 test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true); 249 250 { 251 // Test out the rrect from skia:3466 252 SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f); 253 254 test_9patch_rrect(reporter, 255 rect2, 256 0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f, 257 false); 258 } 259 260 //---- 261 SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } }; 262 263 SkRRect rr5; 264 rr5.setRectRadii(rect, radii2); 265 266 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr5.type()); 267 REPORTER_ASSERT(reporter, rr5.rect() == rect); 268 269 for (int i = 0; i < 4; ++i) { 270 REPORTER_ASSERT(reporter, radii2[i] == rr5.radii((SkRRect::Corner) i)); 271 } 272 273 // Test out == & != 274 REPORTER_ASSERT(reporter, empty != rr3); 275 REPORTER_ASSERT(reporter, rr3 != rr5); 276 } 277 278 // Test out the cases when the RR degenerates to a rect 279 static void test_round_rect_rects(skiatest::Reporter* reporter) { 280 SkRect r; 281 282 //---- 283 SkRRect empty; 284 285 empty.setEmpty(); 286 287 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type()); 288 r = empty.rect(); 289 REPORTER_ASSERT(reporter, 0 == r.fLeft && 0 == r.fTop && 0 == r.fRight && 0 == r.fBottom); 290 291 //---- 292 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 293 SkRRect rr1; 294 rr1.setRectXY(rect, 0, 0); 295 296 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type()); 297 r = rr1.rect(); 298 REPORTER_ASSERT(reporter, rect == r); 299 300 //---- 301 SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; 302 303 SkRRect rr2; 304 rr2.setRectRadii(rect, radii); 305 306 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type()); 307 r = rr2.rect(); 308 REPORTER_ASSERT(reporter, rect == r); 309 310 //---- 311 SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } }; 312 313 SkRRect rr3; 314 rr3.setRectRadii(rect, radii2); 315 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr3.type()); 316 } 317 318 // Test out the cases when the RR degenerates to an oval 319 static void test_round_rect_ovals(skiatest::Reporter* reporter) { 320 //---- 321 SkRect oval; 322 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 323 SkRRect rr1; 324 rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight)); 325 326 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr1.type()); 327 oval = rr1.rect(); 328 REPORTER_ASSERT(reporter, oval == rect); 329 } 330 331 // Test out the non-degenerate RR cases 332 static void test_round_rect_general(skiatest::Reporter* reporter) { 333 //---- 334 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 335 SkRRect rr1; 336 rr1.setRectXY(rect, 20, 20); 337 338 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr1.type()); 339 340 //---- 341 SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } }; 342 343 SkRRect rr2; 344 rr2.setRectRadii(rect, radii); 345 346 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr2.type()); 347 } 348 349 // Test out questionable-parameter handling 350 static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) { 351 352 // When the radii exceed the base rect they are proportionally scaled down 353 // to fit 354 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 355 SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } }; 356 357 SkRRect rr1; 358 rr1.setRectRadii(rect, radii); 359 360 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr1.type()); 361 362 const SkPoint& p = rr1.radii(SkRRect::kUpperLeft_Corner); 363 364 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fX, 33.33333f)); 365 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fY, 66.66666f)); 366 367 // Negative radii should be capped at zero 368 SkRRect rr2; 369 rr2.setRectXY(rect, -10, -20); 370 371 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type()); 372 373 const SkPoint& p2 = rr2.radii(SkRRect::kUpperLeft_Corner); 374 375 REPORTER_ASSERT(reporter, 0.0f == p2.fX); 376 REPORTER_ASSERT(reporter, 0.0f == p2.fY); 377 } 378 379 // Move a small box from the start position by (stepX, stepY) 'numSteps' times 380 // testing for containment in 'rr' at each step. 381 static void test_direction(skiatest::Reporter* reporter, const SkRRect &rr, 382 SkScalar initX, int stepX, SkScalar initY, int stepY, 383 int numSteps, const bool* contains) { 384 SkScalar x = initX, y = initY; 385 for (int i = 0; i < numSteps; ++i) { 386 SkRect test = SkRect::MakeXYWH(x, y, 387 stepX ? SkIntToScalar(stepX) : SK_Scalar1, 388 stepY ? SkIntToScalar(stepY) : SK_Scalar1); 389 test.sort(); 390 391 REPORTER_ASSERT(reporter, contains[i] == rr.contains(test)); 392 393 x += stepX; 394 y += stepY; 395 } 396 } 397 398 // Exercise the RR's contains rect method 399 static void test_round_rect_contains_rect(skiatest::Reporter* reporter) { 400 401 static const int kNumRRects = 4; 402 static const SkVector gRadii[kNumRRects][4] = { 403 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // rect 404 { { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 } }, // circle 405 { { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 10 } }, // simple 406 { { 0, 0 }, { 20, 20 }, { 10, 10 }, { 30, 30 } } // complex 407 }; 408 409 SkRRect rrects[kNumRRects]; 410 for (int i = 0; i < kNumRRects; ++i) { 411 rrects[i].setRectRadii(SkRect::MakeWH(40, 40), gRadii[i]); 412 } 413 414 // First test easy outs - boxes that are obviously out on 415 // each corner and edge 416 static const SkRect easyOuts[] = { 417 { -5, -5, 5, 5 }, // NW 418 { 15, -5, 20, 5 }, // N 419 { 35, -5, 45, 5 }, // NE 420 { 35, 15, 45, 20 }, // E 421 { 35, 45, 35, 45 }, // SE 422 { 15, 35, 20, 45 }, // S 423 { -5, 35, 5, 45 }, // SW 424 { -5, 15, 5, 20 } // W 425 }; 426 427 for (int i = 0; i < kNumRRects; ++i) { 428 for (size_t j = 0; j < SK_ARRAY_COUNT(easyOuts); ++j) { 429 REPORTER_ASSERT(reporter, !rrects[i].contains(easyOuts[j])); 430 } 431 } 432 433 // Now test non-trivial containment. For each compass 434 // point walk a 1x1 rect in from the edge of the bounding 435 // rect 436 static const int kNumSteps = 15; 437 bool answers[kNumRRects][8][kNumSteps] = { 438 // all the test rects are inside the degenerate rrect 439 { 440 // rect 441 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 442 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 443 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 444 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 445 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 446 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 447 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 448 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 449 }, 450 // for the circle we expect 6 blocks to be out on the 451 // corners (then the rest in) and only the first block 452 // out on the vertical and horizontal axes (then 453 // the rest in) 454 { 455 // circle 456 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 457 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 458 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 459 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 460 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 461 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 462 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 463 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 464 }, 465 // for the simple round rect we expect 3 out on 466 // the corners (then the rest in) and no blocks out 467 // on the vertical and horizontal axes 468 { 469 // simple RR 470 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 471 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 472 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 473 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 474 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 475 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 476 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 477 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 478 }, 479 // for the complex case the answer is different for each direction 480 { 481 // complex RR 482 // all in for NW (rect) corner (same as rect case) 483 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 484 // only first block out for N (same as circle case) 485 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 486 // first 6 blocks out for NE (same as circle case) 487 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 488 // only first block out for E (same as circle case) 489 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 490 // first 3 blocks out for SE (same as simple case) 491 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 492 // first two blocks out for S 493 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 494 // first 9 blocks out for SW 495 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 }, 496 // first two blocks out for W (same as S) 497 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 498 } 499 }; 500 501 for (int i = 0; i < kNumRRects; ++i) { 502 test_direction(reporter, rrects[i], 0, 1, 0, 1, kNumSteps, answers[i][0]); // NW 503 test_direction(reporter, rrects[i], 19.5f, 0, 0, 1, kNumSteps, answers[i][1]); // N 504 test_direction(reporter, rrects[i], 40, -1, 0, 1, kNumSteps, answers[i][2]); // NE 505 test_direction(reporter, rrects[i], 40, -1, 19.5f, 0, kNumSteps, answers[i][3]); // E 506 test_direction(reporter, rrects[i], 40, -1, 40, -1, kNumSteps, answers[i][4]); // SE 507 test_direction(reporter, rrects[i], 19.5f, 0, 40, -1, kNumSteps, answers[i][5]); // S 508 test_direction(reporter, rrects[i], 0, 1, 40, -1, kNumSteps, answers[i][6]); // SW 509 test_direction(reporter, rrects[i], 0, 1, 19.5f, 0, kNumSteps, answers[i][7]); // W 510 } 511 } 512 513 // Called for a matrix that should cause SkRRect::transform to fail. 514 static void assert_transform_failure(skiatest::Reporter* reporter, const SkRRect& orig, 515 const SkMatrix& matrix) { 516 // The test depends on the fact that the original is not empty. 517 SkASSERT(!orig.isEmpty()); 518 SkRRect dst; 519 dst.setEmpty(); 520 521 const SkRRect copyOfDst = dst; 522 const SkRRect copyOfOrig = orig; 523 bool success = orig.transform(matrix, &dst); 524 // This transform should fail. 525 REPORTER_ASSERT(reporter, !success); 526 // Since the transform failed, dst should be unchanged. 527 REPORTER_ASSERT(reporter, copyOfDst == dst); 528 // original should not be modified. 529 REPORTER_ASSERT(reporter, copyOfOrig == orig); 530 REPORTER_ASSERT(reporter, orig != dst); 531 } 532 533 #define GET_RADII \ 534 const SkVector& origUL = orig.radii(SkRRect::kUpperLeft_Corner); \ 535 const SkVector& origUR = orig.radii(SkRRect::kUpperRight_Corner); \ 536 const SkVector& origLR = orig.radii(SkRRect::kLowerRight_Corner); \ 537 const SkVector& origLL = orig.radii(SkRRect::kLowerLeft_Corner); \ 538 const SkVector& dstUL = dst.radii(SkRRect::kUpperLeft_Corner); \ 539 const SkVector& dstUR = dst.radii(SkRRect::kUpperRight_Corner); \ 540 const SkVector& dstLR = dst.radii(SkRRect::kLowerRight_Corner); \ 541 const SkVector& dstLL = dst.radii(SkRRect::kLowerLeft_Corner) 542 543 // Called to test various transforms on a single SkRRect. 544 static void test_transform_helper(skiatest::Reporter* reporter, const SkRRect& orig) { 545 SkRRect dst; 546 dst.setEmpty(); 547 548 // The identity matrix will duplicate the rrect. 549 bool success = orig.transform(SkMatrix::I(), &dst); 550 REPORTER_ASSERT(reporter, success); 551 REPORTER_ASSERT(reporter, orig == dst); 552 553 // Skew and Perspective make transform fail. 554 SkMatrix matrix; 555 matrix.reset(); 556 matrix.setSkewX(SkIntToScalar(2)); 557 assert_transform_failure(reporter, orig, matrix); 558 559 matrix.reset(); 560 matrix.setSkewY(SkIntToScalar(3)); 561 assert_transform_failure(reporter, orig, matrix); 562 563 matrix.reset(); 564 matrix.setPerspX(4); 565 assert_transform_failure(reporter, orig, matrix); 566 567 matrix.reset(); 568 matrix.setPerspY(5); 569 assert_transform_failure(reporter, orig, matrix); 570 571 // Rotation fails. 572 matrix.reset(); 573 matrix.setRotate(SkIntToScalar(90)); 574 assert_transform_failure(reporter, orig, matrix); 575 matrix.setRotate(SkIntToScalar(37)); 576 assert_transform_failure(reporter, orig, matrix); 577 578 // Translate will keep the rect moved, but otherwise the same. 579 matrix.reset(); 580 SkScalar translateX = SkIntToScalar(32); 581 SkScalar translateY = SkIntToScalar(15); 582 matrix.setTranslateX(translateX); 583 matrix.setTranslateY(translateY); 584 dst.setEmpty(); 585 success = orig.transform(matrix, &dst); 586 REPORTER_ASSERT(reporter, success); 587 for (int i = 0; i < 4; ++i) { 588 REPORTER_ASSERT(reporter, 589 orig.radii((SkRRect::Corner) i) == dst.radii((SkRRect::Corner) i)); 590 } 591 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width()); 592 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height()); 593 REPORTER_ASSERT(reporter, dst.rect().left() == orig.rect().left() + translateX); 594 REPORTER_ASSERT(reporter, dst.rect().top() == orig.rect().top() + translateY); 595 596 // Keeping the translation, but adding skew will make transform fail. 597 matrix.setSkewY(SkIntToScalar(7)); 598 assert_transform_failure(reporter, orig, matrix); 599 600 // Scaling in -x will flip the round rect horizontally. 601 matrix.reset(); 602 matrix.setScaleX(SkIntToScalar(-1)); 603 dst.setEmpty(); 604 success = orig.transform(matrix, &dst); 605 REPORTER_ASSERT(reporter, success); 606 { 607 GET_RADII; 608 // Radii have swapped in x. 609 REPORTER_ASSERT(reporter, origUL == dstUR); 610 REPORTER_ASSERT(reporter, origUR == dstUL); 611 REPORTER_ASSERT(reporter, origLR == dstLL); 612 REPORTER_ASSERT(reporter, origLL == dstLR); 613 } 614 // Width and height remain the same. 615 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width()); 616 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height()); 617 // Right and left have swapped (sort of) 618 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left()); 619 // Top has stayed the same. 620 REPORTER_ASSERT(reporter, orig.rect().top() == dst.rect().top()); 621 622 // Keeping the scale, but adding a persp will make transform fail. 623 matrix.setPerspX(7); 624 assert_transform_failure(reporter, orig, matrix); 625 626 // Scaling in -y will flip the round rect vertically. 627 matrix.reset(); 628 matrix.setScaleY(SkIntToScalar(-1)); 629 dst.setEmpty(); 630 success = orig.transform(matrix, &dst); 631 REPORTER_ASSERT(reporter, success); 632 { 633 GET_RADII; 634 // Radii have swapped in y. 635 REPORTER_ASSERT(reporter, origUL == dstLL); 636 REPORTER_ASSERT(reporter, origUR == dstLR); 637 REPORTER_ASSERT(reporter, origLR == dstUR); 638 REPORTER_ASSERT(reporter, origLL == dstUL); 639 } 640 // Width and height remain the same. 641 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width()); 642 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height()); 643 // Top and bottom have swapped (sort of) 644 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom()); 645 // Left has stayed the same. 646 REPORTER_ASSERT(reporter, orig.rect().left() == dst.rect().left()); 647 648 // Scaling in -x and -y will swap in both directions. 649 matrix.reset(); 650 matrix.setScaleY(SkIntToScalar(-1)); 651 matrix.setScaleX(SkIntToScalar(-1)); 652 dst.setEmpty(); 653 success = orig.transform(matrix, &dst); 654 REPORTER_ASSERT(reporter, success); 655 { 656 GET_RADII; 657 REPORTER_ASSERT(reporter, origUL == dstLR); 658 REPORTER_ASSERT(reporter, origUR == dstLL); 659 REPORTER_ASSERT(reporter, origLR == dstUL); 660 REPORTER_ASSERT(reporter, origLL == dstUR); 661 } 662 // Width and height remain the same. 663 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width()); 664 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height()); 665 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom()); 666 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left()); 667 668 // Scale in both directions. 669 SkScalar xScale = SkIntToScalar(3); 670 SkScalar yScale = 3.2f; 671 matrix.reset(); 672 matrix.setScaleX(xScale); 673 matrix.setScaleY(yScale); 674 dst.setEmpty(); 675 success = orig.transform(matrix, &dst); 676 REPORTER_ASSERT(reporter, success); 677 // Radii are scaled. 678 for (int i = 0; i < 4; ++i) { 679 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX, 680 orig.radii((SkRRect::Corner) i).fX * xScale)); 681 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY, 682 orig.radii((SkRRect::Corner) i).fY * yScale)); 683 } 684 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(), 685 orig.rect().width() * xScale)); 686 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(), 687 orig.rect().height() * yScale)); 688 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(), 689 orig.rect().left() * xScale)); 690 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(), 691 orig.rect().top() * yScale)); 692 } 693 694 static void test_round_rect_transform(skiatest::Reporter* reporter) { 695 SkRRect rrect; 696 { 697 SkRect r = { 0, 0, kWidth, kHeight }; 698 rrect.setRectXY(r, SkIntToScalar(4), SkIntToScalar(7)); 699 test_transform_helper(reporter, rrect); 700 } 701 { 702 SkRect r = { SkIntToScalar(5), SkIntToScalar(15), 703 SkIntToScalar(27), SkIntToScalar(34) }; 704 SkVector radii[4] = { { 0, SkIntToScalar(1) }, 705 { SkIntToScalar(2), SkIntToScalar(3) }, 706 { SkIntToScalar(4), SkIntToScalar(5) }, 707 { SkIntToScalar(6), SkIntToScalar(7) } }; 708 rrect.setRectRadii(r, radii); 709 test_transform_helper(reporter, rrect); 710 } 711 } 712 713 // Test out the case where an oval already off in space is translated/scaled 714 // further off into space - yielding numerical issues when the rect & radii 715 // are transformed separatly 716 // BUG=skia:2696 717 static void test_issue_2696(skiatest::Reporter* reporter) { 718 SkRRect rrect; 719 SkRect r = { 28443.8594f, 53.1428604f, 28446.7148f, 56.0000038f }; 720 rrect.setOval(r); 721 722 SkMatrix xform; 723 xform.setAll(2.44f, 0.0f, 485411.7f, 724 0.0f, 2.44f, -438.7f, 725 0.0f, 0.0f, 1.0f); 726 SkRRect dst; 727 728 bool success = rrect.transform(xform, &dst); 729 REPORTER_ASSERT(reporter, success); 730 731 SkScalar halfWidth = SkScalarHalf(dst.width()); 732 SkScalar halfHeight = SkScalarHalf(dst.height()); 733 734 for (int i = 0; i < 4; ++i) { 735 REPORTER_ASSERT(reporter, 736 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fX, halfWidth)); 737 REPORTER_ASSERT(reporter, 738 SkScalarNearlyEqual(dst.radii((SkRRect::Corner)i).fY, halfHeight)); 739 } 740 } 741 742 void test_read_rrect(skiatest::Reporter* reporter, const SkRRect& rrect, bool shouldEqualSrc) { 743 // It would be cleaner to call rrect.writeToMemory into a buffer. However, writeToMemory asserts 744 // that the rrect is valid and our caller may have fiddled with the internals of rrect to make 745 // it invalid. 746 const void* buffer = reinterpret_cast<const void*>(&rrect); 747 SkRRect deserialized; 748 size_t size = deserialized.readFromMemory(buffer, sizeof(SkRRect)); 749 REPORTER_ASSERT(reporter, size == SkRRect::kSizeInMemory); 750 REPORTER_ASSERT(reporter, deserialized.isValid()); 751 if (shouldEqualSrc) { 752 REPORTER_ASSERT(reporter, rrect == deserialized); 753 } 754 } 755 756 static void test_read(skiatest::Reporter* reporter) { 757 static const SkRect kRect = {10.f, 10.f, 20.f, 20.f}; 758 static const SkRect kNaNRect = {10.f, 10.f, 20.f, SK_ScalarNaN}; 759 static const SkRect kInfRect = {10.f, 10.f, SK_ScalarInfinity, 20.f}; 760 SkRRect rrect; 761 762 test_read_rrect(reporter, SkRRect::MakeEmpty(), true); 763 test_read_rrect(reporter, SkRRect::MakeRect(kRect), true); 764 // These get coerced to empty. 765 test_read_rrect(reporter, SkRRect::MakeRect(kInfRect), true); 766 test_read_rrect(reporter, SkRRect::MakeRect(kNaNRect), true); 767 768 rrect.setRect(kRect); 769 SkRect* innerRect = reinterpret_cast<SkRect*>(&rrect); 770 SkASSERT(*innerRect == kRect); 771 *innerRect = kInfRect; 772 test_read_rrect(reporter, rrect, false); 773 *innerRect = kNaNRect; 774 test_read_rrect(reporter, rrect, false); 775 776 test_read_rrect(reporter, SkRRect::MakeOval(kRect), true); 777 test_read_rrect(reporter, SkRRect::MakeOval(kInfRect), true); 778 test_read_rrect(reporter, SkRRect::MakeOval(kNaNRect), true); 779 rrect.setOval(kRect); 780 *innerRect = kInfRect; 781 test_read_rrect(reporter, rrect, false); 782 *innerRect = kNaNRect; 783 test_read_rrect(reporter, rrect, false); 784 785 test_read_rrect(reporter, SkRRect::MakeRectXY(kRect, 5.f, 5.f), true); 786 // rrect should scale down the radii to make this legal 787 test_read_rrect(reporter, SkRRect::MakeRectXY(kRect, 5.f, 400.f), true); 788 789 static const SkVector kRadii[4] = {{0.5f, 1.f}, {1.5f, 2.f}, {2.5f, 3.f}, {3.5f, 4.f}}; 790 rrect.setRectRadii(kRect, kRadii); 791 test_read_rrect(reporter, rrect, true); 792 SkScalar* innerRadius = reinterpret_cast<SkScalar*>(&rrect) + 6; 793 SkASSERT(*innerRadius == 1.5f); 794 *innerRadius = 400.f; 795 test_read_rrect(reporter, rrect, false); 796 *innerRadius = SK_ScalarInfinity; 797 test_read_rrect(reporter, rrect, false); 798 *innerRadius = SK_ScalarNaN; 799 test_read_rrect(reporter, rrect, false); 800 *innerRadius = -10.f; 801 test_read_rrect(reporter, rrect, false); 802 } 803 804 DEF_TEST(RoundRect, reporter) { 805 test_round_rect_basic(reporter); 806 test_round_rect_rects(reporter); 807 test_round_rect_ovals(reporter); 808 test_round_rect_general(reporter); 809 test_round_rect_iffy_parameters(reporter); 810 test_inset(reporter); 811 test_round_rect_contains_rect(reporter); 812 test_round_rect_transform(reporter); 813 test_issue_2696(reporter); 814 test_tricky_radii(reporter); 815 test_empty_crbug_458524(reporter); 816 test_empty(reporter); 817 test_read(reporter); 818 } 819