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 static const SkScalar kWidth = 100.0f; 51 static const SkScalar kHeight = 100.0f; 52 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 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 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 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 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 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 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. 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 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. 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. 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 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 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 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