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