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 "Test.h" 9 #include "SkMatrix.h" 10 #include "SkRRect.h" 11 12 static const SkScalar kWidth = SkFloatToScalar(100.0f); 13 static const SkScalar kHeight = SkFloatToScalar(100.0f); 14 15 static 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()); 25 26 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 36 // Test out the basic API entry points 37 static void test_round_rect_basic(skiatest::Reporter* reporter) { 38 // Test out initialization methods 39 SkPoint zeroPt = { 0, 0 }; 40 SkRRect empty; 41 42 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 } 63 64 //---- 65 SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) }; 66 SkRRect rr2; 67 rr2.setOval(rect); 68 69 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr2.type()); 70 REPORTER_ASSERT(reporter, rr2.rect() == rect); 71 72 for (int i = 0; i < 4; ++i) { 73 REPORTER_ASSERT(reporter, 74 rr2.radii((SkRRect::Corner) i).equalsWithinTolerance(halfPoint)); 75 } 76 77 //---- 78 SkPoint p = { 5, 5 }; 79 SkRRect rr3; 80 rr3.setRectXY(rect, p.fX, p.fY); 81 82 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr3.type()); 83 REPORTER_ASSERT(reporter, rr3.rect() == rect); 84 85 for (int i = 0; i < 4; ++i) { 86 REPORTER_ASSERT(reporter, p == rr3.radii((SkRRect::Corner) i)); 87 } 88 89 //---- 90 SkPoint radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } }; 91 92 SkRRect rr4; 93 rr4.setRectRadii(rect, radii); 94 95 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr4.type()); 96 REPORTER_ASSERT(reporter, rr4.rect() == rect); 97 98 for (int i = 0; i < 4; ++i) { 99 REPORTER_ASSERT(reporter, radii[i] == rr4.radii((SkRRect::Corner) i)); 100 } 101 102 //---- 103 SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } }; 104 105 SkRRect rr5; 106 rr5.setRectRadii(rect, radii2); 107 108 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr5.type()); 109 REPORTER_ASSERT(reporter, rr5.rect() == rect); 110 111 for (int i = 0; i < 4; ++i) { 112 REPORTER_ASSERT(reporter, radii2[i] == rr5.radii((SkRRect::Corner) i)); 113 } 114 115 // Test out == & != 116 REPORTER_ASSERT(reporter, empty != rr3); 117 REPORTER_ASSERT(reporter, rr3 == rr4); 118 REPORTER_ASSERT(reporter, rr4 != rr5); 119 } 120 121 // Test out the cases when the RR degenerates to a rect 122 static void test_round_rect_rects(skiatest::Reporter* reporter) { 123 SkRect r; 124 static const SkPoint pts[] = { 125 // Upper Left 126 { -SK_Scalar1, -SK_Scalar1 }, // out 127 { SK_Scalar1, SK_Scalar1 }, // in 128 // Upper Right 129 { SkIntToScalar(101), -SK_Scalar1}, // out 130 { SkIntToScalar(99), SK_Scalar1 }, // in 131 // Lower Right 132 { SkIntToScalar(101), SkIntToScalar(101) }, // out 133 { SkIntToScalar(99), SkIntToScalar(99) }, // in 134 // Lower Left 135 { -SK_Scalar1, SkIntToScalar(101) }, // out 136 { SK_Scalar1, SkIntToScalar(99) }, // in 137 // Middle 138 { SkIntToScalar(50), SkIntToScalar(50) } // in 139 }; 140 static const bool isIn[] = { false, true, false, true, false, true, false, true, true }; 141 142 SkASSERT(SK_ARRAY_COUNT(pts) == SK_ARRAY_COUNT(isIn)); 143 144 //---- 145 SkRRect empty; 146 147 empty.setEmpty(); 148 149 REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type()); 150 r = empty.rect(); 151 REPORTER_ASSERT(reporter, 0 == r.fLeft && 0 == r.fTop && 0 == r.fRight && 0 == r.fBottom); 152 153 //---- 154 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 155 SkRRect rr1; 156 rr1.setRectXY(rect, 0, 0); 157 158 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type()); 159 r = rr1.rect(); 160 REPORTER_ASSERT(reporter, rect == r); 161 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) { 162 REPORTER_ASSERT(reporter, isIn[i] == rr1.contains(pts[i].fX, pts[i].fY)); 163 } 164 165 //---- 166 SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; 167 168 SkRRect rr2; 169 rr2.setRectRadii(rect, radii); 170 171 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type()); 172 r = rr2.rect(); 173 REPORTER_ASSERT(reporter, rect == r); 174 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) { 175 REPORTER_ASSERT(reporter, isIn[i] == rr2.contains(pts[i].fX, pts[i].fY)); 176 } 177 178 //---- 179 SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } }; 180 181 SkRRect rr3; 182 rr3.setRectRadii(rect, radii2); 183 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr3.type()); 184 } 185 186 // Test out the cases when the RR degenerates to an oval 187 static void test_round_rect_ovals(skiatest::Reporter* reporter) { 188 static const SkScalar kEps = 0.1f; 189 static const SkScalar kWidthTol = SkScalarHalf(kWidth) * (SK_Scalar1 - SK_ScalarRoot2Over2); 190 static const SkScalar kHeightTol = SkScalarHalf(kHeight) * (SK_Scalar1 - SK_ScalarRoot2Over2); 191 static const SkPoint pts[] = { 192 // Upper Left 193 { kWidthTol - kEps, kHeightTol - kEps }, // out 194 { kWidthTol + kEps, kHeightTol + kEps }, // in 195 // Upper Right 196 { kWidth + kEps - kWidthTol, kHeightTol - kEps }, // out 197 { kWidth - kEps - kWidthTol, kHeightTol + kEps }, // in 198 // Lower Right 199 { kWidth + kEps - kWidthTol, kHeight + kEps - kHeightTol }, // out 200 { kWidth - kEps - kWidthTol, kHeight - kEps - kHeightTol }, // in 201 // Lower Left 202 { kWidthTol - kEps, kHeight + kEps - kHeightTol }, //out 203 { kWidthTol + kEps, kHeight - kEps - kHeightTol }, // in 204 // Middle 205 { SkIntToScalar(50), SkIntToScalar(50) } // in 206 }; 207 static const bool isIn[] = { false, true, false, true, false, true, false, true, true }; 208 209 SkASSERT(SK_ARRAY_COUNT(pts) == SK_ARRAY_COUNT(isIn)); 210 211 //---- 212 SkRect oval; 213 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 214 SkRRect rr1; 215 rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight)); 216 217 REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr1.type()); 218 oval = rr1.rect(); 219 REPORTER_ASSERT(reporter, oval == rect); 220 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) { 221 REPORTER_ASSERT(reporter, isIn[i] == rr1.contains(pts[i].fX, pts[i].fY)); 222 } 223 } 224 225 // Test out the non-degenerate RR cases 226 static void test_round_rect_general(skiatest::Reporter* reporter) { 227 static const SkScalar kEps = 0.1f; 228 static const SkScalar kDist20 = 20 * (SK_Scalar1 - SK_ScalarRoot2Over2); 229 static const SkPoint pts[] = { 230 // Upper Left 231 { kDist20 - kEps, kDist20 - kEps }, // out 232 { kDist20 + kEps, kDist20 + kEps }, // in 233 // Upper Right 234 { kWidth + kEps - kDist20, kDist20 - kEps }, // out 235 { kWidth - kEps - kDist20, kDist20 + kEps }, // in 236 // Lower Right 237 { kWidth + kEps - kDist20, kHeight + kEps - kDist20 }, // out 238 { kWidth - kEps - kDist20, kHeight - kEps - kDist20 }, // in 239 // Lower Left 240 { kDist20 - kEps, kHeight + kEps - kDist20 }, //out 241 { kDist20 + kEps, kHeight - kEps - kDist20 }, // in 242 // Middle 243 { SkIntToScalar(50), SkIntToScalar(50) } // in 244 }; 245 static const bool isIn[] = { false, true, false, true, false, true, false, true, true }; 246 247 SkASSERT(SK_ARRAY_COUNT(pts) == SK_ARRAY_COUNT(isIn)); 248 249 //---- 250 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 251 SkRRect rr1; 252 rr1.setRectXY(rect, 20, 20); 253 254 REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr1.type()); 255 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) { 256 REPORTER_ASSERT(reporter, isIn[i] == rr1.contains(pts[i].fX, pts[i].fY)); 257 } 258 259 //---- 260 static const SkScalar kDist50 = 50*(SK_Scalar1 - SK_ScalarRoot2Over2); 261 static const SkPoint pts2[] = { 262 // Upper Left 263 { -SK_Scalar1, -SK_Scalar1 }, // out 264 { SK_Scalar1, SK_Scalar1 }, // in 265 // Upper Right 266 { kWidth + kEps - kDist20, kDist20 - kEps }, // out 267 { kWidth - kEps - kDist20, kDist20 + kEps }, // in 268 // Lower Right 269 { kWidth + kEps - kDist50, kHeight + kEps - kDist50 }, // out 270 { kWidth - kEps - kDist50, kHeight - kEps - kDist50 }, // in 271 // Lower Left 272 { kDist20 - kEps, kHeight + kEps - kDist50 }, // out 273 { kDist20 + kEps, kHeight - kEps - kDist50 }, // in 274 // Middle 275 { SkIntToScalar(50), SkIntToScalar(50) } // in 276 }; 277 278 SkASSERT(SK_ARRAY_COUNT(pts2) == SK_ARRAY_COUNT(isIn)); 279 280 SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } }; 281 282 SkRRect rr2; 283 rr2.setRectRadii(rect, radii); 284 285 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr2.type()); 286 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) { 287 REPORTER_ASSERT(reporter, isIn[i] == rr2.contains(pts2[i].fX, pts2[i].fY)); 288 } 289 } 290 291 // Test out questionable-parameter handling 292 static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) { 293 294 // When the radii exceed the base rect they are proportionally scaled down 295 // to fit 296 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 297 SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } }; 298 299 SkRRect rr1; 300 rr1.setRectRadii(rect, radii); 301 302 REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr1.type()); 303 304 const SkPoint& p = rr1.radii(SkRRect::kUpperLeft_Corner); 305 306 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fX, 33.33333f)); 307 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fY, 66.66666f)); 308 309 // Negative radii should be capped at zero 310 SkRRect rr2; 311 rr2.setRectXY(rect, -10, -20); 312 313 REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type()); 314 315 const SkPoint& p2 = rr2.radii(SkRRect::kUpperLeft_Corner); 316 317 REPORTER_ASSERT(reporter, 0.0f == p2.fX); 318 REPORTER_ASSERT(reporter, 0.0f == p2.fY); 319 } 320 321 // Move a small box from the start position by (stepX, stepY) 'numSteps' times 322 // testing for containment in 'rr' at each step. 323 static void test_direction(skiatest::Reporter* reporter, const SkRRect &rr, 324 SkScalar initX, int stepX, SkScalar initY, int stepY, 325 int numSteps, const bool* contains) { 326 SkScalar x = initX, y = initY; 327 for (int i = 0; i < numSteps; ++i) { 328 SkRect test = SkRect::MakeXYWH(x, y, 329 stepX ? SkIntToScalar(stepX) : SK_Scalar1, 330 stepY ? SkIntToScalar(stepY) : SK_Scalar1); 331 test.sort(); 332 333 REPORTER_ASSERT(reporter, contains[i] == rr.contains(test)); 334 335 x += stepX; 336 y += stepY; 337 } 338 } 339 340 // Exercise the RR's contains rect method 341 static void test_round_rect_contains_rect(skiatest::Reporter* reporter) { 342 343 static const int kNumRRects = 4; 344 static const SkVector gRadii[kNumRRects][4] = { 345 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // rect 346 { { 20, 20 }, { 20, 20 }, { 20, 20 }, { 20, 20 } }, // circle 347 { { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 10 } }, // simple 348 { { 0, 0 }, { 20, 20 }, { 10, 10 }, { 30, 30 } } // complex 349 }; 350 351 SkRRect rrects[kNumRRects]; 352 for (int i = 0; i < kNumRRects; ++i) { 353 rrects[i].setRectRadii(SkRect::MakeWH(40, 40), gRadii[i]); 354 } 355 356 // First test easy outs - boxes that are obviously out on 357 // each corner and edge 358 static const SkRect easyOuts[] = { 359 { -5, -5, 5, 5 }, // NW 360 { 15, -5, 20, 5 }, // N 361 { 35, -5, 45, 5 }, // NE 362 { 35, 15, 45, 20 }, // E 363 { 35, 45, 35, 45 }, // SE 364 { 15, 35, 20, 45 }, // S 365 { -5, 35, 5, 45 }, // SW 366 { -5, 15, 5, 20 } // W 367 }; 368 369 for (int i = 0; i < kNumRRects; ++i) { 370 for (size_t j = 0; j < SK_ARRAY_COUNT(easyOuts); ++j) { 371 REPORTER_ASSERT(reporter, !rrects[i].contains(easyOuts[j])); 372 } 373 } 374 375 // Now test non-trivial containment. For each compass 376 // point walk a 1x1 rect in from the edge of the bounding 377 // rect 378 static const int kNumSteps = 15; 379 bool answers[kNumRRects][8][kNumSteps] = { 380 // all the test rects are inside the degenerate rrect 381 { 382 // rect 383 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 384 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 385 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 386 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 387 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 388 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 389 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 390 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 391 }, 392 // for the circle we expect 6 blocks to be out on the 393 // corners (then the rest in) and only the first block 394 // out on the vertical and horizontal axes (then 395 // the rest in) 396 { 397 // circle 398 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 399 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 400 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 401 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 402 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 403 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 404 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 405 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 406 }, 407 // for the simple round rect we expect 3 out on 408 // the corners (then the rest in) and no blocks out 409 // on the vertical and horizontal axes 410 { 411 // simple RR 412 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 413 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 414 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 415 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 416 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 417 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 418 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 419 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 420 }, 421 // for the complex case the answer is different for each direction 422 { 423 // complex RR 424 // all in for NW (rect) corner (same as rect case) 425 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 426 // only first block out for N (same as circle case) 427 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 428 // first 6 blocks out for NE (same as circle case) 429 { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 430 // only first block out for E (same as circle case) 431 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 432 // first 3 blocks out for SE (same as simple case) 433 { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 434 // first two blocks out for S 435 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 436 // first 9 blocks out for SW 437 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 }, 438 // first two blocks out for W (same as S) 439 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 440 } 441 }; 442 443 for (int i = 0; i < kNumRRects; ++i) { 444 test_direction(reporter, rrects[i], 0, 1, 0, 1, kNumSteps, answers[i][0]); // NW 445 test_direction(reporter, rrects[i], 19.5f, 0, 0, 1, kNumSteps, answers[i][1]); // N 446 test_direction(reporter, rrects[i], 40, -1, 0, 1, kNumSteps, answers[i][2]); // NE 447 test_direction(reporter, rrects[i], 40, -1, 19.5f, 0, kNumSteps, answers[i][3]); // E 448 test_direction(reporter, rrects[i], 40, -1, 40, -1, kNumSteps, answers[i][4]); // SE 449 test_direction(reporter, rrects[i], 19.5f, 0, 40, -1, kNumSteps, answers[i][5]); // S 450 test_direction(reporter, rrects[i], 0, 1, 40, -1, kNumSteps, answers[i][6]); // SW 451 test_direction(reporter, rrects[i], 0, 1, 19.5f, 0, kNumSteps, answers[i][7]); // W 452 } 453 } 454 455 // Called for a matrix that should cause SkRRect::transform to fail. 456 static void assert_transform_failure(skiatest::Reporter* reporter, const SkRRect& orig, 457 const SkMatrix& matrix) { 458 // The test depends on the fact that the original is not empty. 459 SkASSERT(!orig.isEmpty()); 460 SkRRect dst; 461 dst.setEmpty(); 462 463 const SkRRect copyOfDst = dst; 464 const SkRRect copyOfOrig = orig; 465 bool success = orig.transform(matrix, &dst); 466 // This transform should fail. 467 REPORTER_ASSERT(reporter, !success); 468 // Since the transform failed, dst should be unchanged. 469 REPORTER_ASSERT(reporter, copyOfDst == dst); 470 // original should not be modified. 471 REPORTER_ASSERT(reporter, copyOfOrig == orig); 472 REPORTER_ASSERT(reporter, orig != dst); 473 } 474 475 #define GET_RADII \ 476 const SkVector& origUL = orig.radii(SkRRect::kUpperLeft_Corner); \ 477 const SkVector& origUR = orig.radii(SkRRect::kUpperRight_Corner); \ 478 const SkVector& origLR = orig.radii(SkRRect::kLowerRight_Corner); \ 479 const SkVector& origLL = orig.radii(SkRRect::kLowerLeft_Corner); \ 480 const SkVector& dstUL = dst.radii(SkRRect::kUpperLeft_Corner); \ 481 const SkVector& dstUR = dst.radii(SkRRect::kUpperRight_Corner); \ 482 const SkVector& dstLR = dst.radii(SkRRect::kLowerRight_Corner); \ 483 const SkVector& dstLL = dst.radii(SkRRect::kLowerLeft_Corner) 484 485 // Called to test various transforms on a single SkRRect. 486 static void test_transform_helper(skiatest::Reporter* reporter, const SkRRect& orig) { 487 SkRRect dst; 488 dst.setEmpty(); 489 490 // The identity matrix will duplicate the rrect. 491 bool success = orig.transform(SkMatrix::I(), &dst); 492 REPORTER_ASSERT(reporter, success); 493 REPORTER_ASSERT(reporter, orig == dst); 494 495 // Skew and Perspective make transform fail. 496 SkMatrix matrix; 497 matrix.reset(); 498 matrix.setSkewX(SkIntToScalar(2)); 499 assert_transform_failure(reporter, orig, matrix); 500 501 matrix.reset(); 502 matrix.setSkewY(SkIntToScalar(3)); 503 assert_transform_failure(reporter, orig, matrix); 504 505 matrix.reset(); 506 matrix.setPerspX(SkScalarToPersp(SkIntToScalar(4))); 507 assert_transform_failure(reporter, orig, matrix); 508 509 matrix.reset(); 510 matrix.setPerspY(SkScalarToPersp(SkIntToScalar(5))); 511 assert_transform_failure(reporter, orig, matrix); 512 513 // Rotation fails. 514 matrix.reset(); 515 matrix.setRotate(SkIntToScalar(90)); 516 assert_transform_failure(reporter, orig, matrix); 517 matrix.setRotate(SkIntToScalar(37)); 518 assert_transform_failure(reporter, orig, matrix); 519 520 // Translate will keep the rect moved, but otherwise the same. 521 matrix.reset(); 522 SkScalar translateX = SkIntToScalar(32); 523 SkScalar translateY = SkIntToScalar(15); 524 matrix.setTranslateX(translateX); 525 matrix.setTranslateY(translateY); 526 dst.setEmpty(); 527 success = orig.transform(matrix, &dst); 528 REPORTER_ASSERT(reporter, success); 529 for (int i = 0; i < 4; ++i) { 530 REPORTER_ASSERT(reporter, 531 orig.radii((SkRRect::Corner) i) == dst.radii((SkRRect::Corner) i)); 532 } 533 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width()); 534 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height()); 535 REPORTER_ASSERT(reporter, dst.rect().left() == orig.rect().left() + translateX); 536 REPORTER_ASSERT(reporter, dst.rect().top() == orig.rect().top() + translateY); 537 538 // Keeping the translation, but adding skew will make transform fail. 539 matrix.setSkewY(SkIntToScalar(7)); 540 assert_transform_failure(reporter, orig, matrix); 541 542 // Scaling in -x will flip the round rect horizontally. 543 matrix.reset(); 544 matrix.setScaleX(SkIntToScalar(-1)); 545 dst.setEmpty(); 546 success = orig.transform(matrix, &dst); 547 REPORTER_ASSERT(reporter, success); 548 { 549 GET_RADII; 550 // Radii have swapped in x. 551 REPORTER_ASSERT(reporter, origUL == dstUR); 552 REPORTER_ASSERT(reporter, origUR == dstUL); 553 REPORTER_ASSERT(reporter, origLR == dstLL); 554 REPORTER_ASSERT(reporter, origLL == dstLR); 555 } 556 // Width and height remain the same. 557 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width()); 558 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height()); 559 // Right and left have swapped (sort of) 560 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left()); 561 // Top has stayed the same. 562 REPORTER_ASSERT(reporter, orig.rect().top() == dst.rect().top()); 563 564 // Keeping the scale, but adding a persp will make transform fail. 565 matrix.setPerspX(SkScalarToPersp(SkIntToScalar(7))); 566 assert_transform_failure(reporter, orig, matrix); 567 568 // Scaling in -y will flip the round rect vertically. 569 matrix.reset(); 570 matrix.setScaleY(SkIntToScalar(-1)); 571 dst.setEmpty(); 572 success = orig.transform(matrix, &dst); 573 REPORTER_ASSERT(reporter, success); 574 { 575 GET_RADII; 576 // Radii have swapped in y. 577 REPORTER_ASSERT(reporter, origUL == dstLL); 578 REPORTER_ASSERT(reporter, origUR == dstLR); 579 REPORTER_ASSERT(reporter, origLR == dstUR); 580 REPORTER_ASSERT(reporter, origLL == dstUL); 581 } 582 // Width and height remain the same. 583 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width()); 584 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height()); 585 // Top and bottom have swapped (sort of) 586 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom()); 587 // Left has stayed the same. 588 REPORTER_ASSERT(reporter, orig.rect().left() == dst.rect().left()); 589 590 // Scaling in -x and -y will swap in both directions. 591 matrix.reset(); 592 matrix.setScaleY(SkIntToScalar(-1)); 593 matrix.setScaleX(SkIntToScalar(-1)); 594 dst.setEmpty(); 595 success = orig.transform(matrix, &dst); 596 REPORTER_ASSERT(reporter, success); 597 { 598 GET_RADII; 599 REPORTER_ASSERT(reporter, origUL == dstLR); 600 REPORTER_ASSERT(reporter, origUR == dstLL); 601 REPORTER_ASSERT(reporter, origLR == dstUL); 602 REPORTER_ASSERT(reporter, origLL == dstUR); 603 } 604 // Width and height remain the same. 605 REPORTER_ASSERT(reporter, orig.rect().width() == dst.rect().width()); 606 REPORTER_ASSERT(reporter, orig.rect().height() == dst.rect().height()); 607 REPORTER_ASSERT(reporter, orig.rect().top() == -dst.rect().bottom()); 608 REPORTER_ASSERT(reporter, orig.rect().right() == -dst.rect().left()); 609 610 // Scale in both directions. 611 SkScalar xScale = SkIntToScalar(3); 612 SkScalar yScale = SkFloatToScalar(3.2f); 613 matrix.reset(); 614 matrix.setScaleX(xScale); 615 matrix.setScaleY(yScale); 616 dst.setEmpty(); 617 success = orig.transform(matrix, &dst); 618 REPORTER_ASSERT(reporter, success); 619 // Radii are scaled. 620 for (int i = 0; i < 4; ++i) { 621 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fX, 622 SkScalarMul(orig.radii((SkRRect::Corner) i).fX, xScale))); 623 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.radii((SkRRect::Corner) i).fY, 624 SkScalarMul(orig.radii((SkRRect::Corner) i).fY, yScale))); 625 } 626 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().width(), 627 SkScalarMul(orig.rect().width(), xScale))); 628 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().height(), 629 SkScalarMul(orig.rect().height(), yScale))); 630 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().left(), 631 SkScalarMul(orig.rect().left(), xScale))); 632 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst.rect().top(), 633 SkScalarMul(orig.rect().top(), yScale))); 634 } 635 636 static void test_round_rect_transform(skiatest::Reporter* reporter) { 637 SkRRect rrect; 638 { 639 SkRect r = { 0, 0, kWidth, kHeight }; 640 rrect.setRectXY(r, SkIntToScalar(4), SkIntToScalar(7)); 641 test_transform_helper(reporter, rrect); 642 } 643 { 644 SkRect r = { SkIntToScalar(5), SkIntToScalar(15), 645 SkIntToScalar(27), SkIntToScalar(34) }; 646 SkVector radii[4] = { { 0, SkIntToScalar(1) }, 647 { SkIntToScalar(2), SkIntToScalar(3) }, 648 { SkIntToScalar(4), SkIntToScalar(5) }, 649 { SkIntToScalar(6), SkIntToScalar(7) } }; 650 rrect.setRectRadii(r, radii); 651 test_transform_helper(reporter, rrect); 652 } 653 } 654 655 static void TestRoundRect(skiatest::Reporter* reporter) { 656 test_round_rect_basic(reporter); 657 test_round_rect_rects(reporter); 658 test_round_rect_ovals(reporter); 659 test_round_rect_general(reporter); 660 test_round_rect_iffy_parameters(reporter); 661 test_inset(reporter); 662 test_round_rect_contains_rect(reporter); 663 test_round_rect_transform(reporter); 664 } 665 666 #include "TestClassDef.h" 667 DEFINE_TESTCLASS("RoundRect", TestRoundRectClass, TestRoundRect) 668