1 /* 2 * Copyright 2015 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 "SkPath.h" 10 #include "SkPathRef.h" 11 #include "SkPathOps.h" 12 #include "SkRRect.h" 13 #include "Test.h" 14 15 static SkRRect path_contains_rrect(skiatest::Reporter* reporter, const SkPath& path) { 16 SkRRect out; 17 REPORTER_ASSERT(reporter, path.isRRect(&out)); 18 SkPath path2, xorBoth; 19 path2.addRRect(out); 20 if (path == path2) { 21 return out; 22 } 23 Op(path, path2, SkPathOp::kXOR_SkPathOp, &xorBoth); 24 REPORTER_ASSERT(reporter, xorBoth.isEmpty()); 25 return out; 26 } 27 28 static SkRRect inner_path_contains_rrect(skiatest::Reporter* reporter, const SkRRect& in) { 29 switch (in.getType()) { 30 case SkRRect::kEmpty_Type: 31 case SkRRect::kRect_Type: 32 case SkRRect::kOval_Type: 33 return in; 34 default: 35 break; 36 } 37 SkPath path; 38 path.addRRect(in); 39 return path_contains_rrect(reporter, path); 40 } 41 42 static void path_contains_rrect_check(skiatest::Reporter* reporter, const SkRRect& in) { 43 SkRRect out = inner_path_contains_rrect(reporter, in); 44 if (in != out) { 45 SkDebugf(""); 46 } 47 REPORTER_ASSERT(reporter, in == out); 48 } 49 50 static void path_contains_rrect_nocheck(skiatest::Reporter* reporter, const SkRRect& in) { 51 SkRRect out = inner_path_contains_rrect(reporter, in); 52 if (in == out) { 53 SkDebugf(""); 54 } 55 } 56 57 static void path_contains_rrect_check(skiatest::Reporter* reporter, const SkRect& r, 58 SkVector v[4]) { 59 SkRRect rrect; 60 rrect.setRectRadii(r, v); 61 path_contains_rrect_check(reporter, rrect); 62 } 63 64 class ForceIsRRect_Private { 65 public: 66 ForceIsRRect_Private(SkPath* path) { 67 path->fPathRef->setIsRRect(true); 68 } 69 }; 70 71 static void force_path_contains_rrect(skiatest::Reporter* reporter, SkPath& path) { 72 ForceIsRRect_Private force_rrect(&path); 73 path_contains_rrect(reporter, path); 74 } 75 76 static void test_undetected_paths(skiatest::Reporter* reporter) { 77 SkPath path; 78 path.moveTo(0, 62.5f); 79 path.lineTo(0, 3.5f); 80 path.conicTo(0, 0, 3.5f, 0, 0.70710677f); 81 path.lineTo(196.5f, 0); 82 path.conicTo(200, 0, 200, 3.5f, 0.70710677f); 83 path.lineTo(200, 62.5f); 84 path.conicTo(200, 66, 196.5f, 66, 0.70710677f); 85 path.lineTo(3.5f, 66); 86 path.conicTo(0, 66, 0, 62.5, 0.70710677f); 87 path.close(); 88 force_path_contains_rrect(reporter, path); 89 90 path.reset(); 91 path.moveTo(0, 81.5f); 92 path.lineTo(0, 3.5f); 93 path.conicTo(0, 0, 3.5f, 0, 0.70710677f); 94 path.lineTo(149.5, 0); 95 path.conicTo(153, 0, 153, 3.5f, 0.70710677f); 96 path.lineTo(153, 81.5f); 97 path.conicTo(153, 85, 149.5f, 85, 0.70710677f); 98 path.lineTo(3.5f, 85); 99 path.conicTo(0, 85, 0, 81.5f, 0.70710677f); 100 path.close(); 101 force_path_contains_rrect(reporter, path); 102 103 path.reset(); 104 path.moveTo(14, 1189); 105 path.lineTo(14, 21); 106 path.conicTo(14, 14, 21, 14, 0.70710677f); 107 path.lineTo(1363, 14); 108 path.conicTo(1370, 14, 1370, 21, 0.70710677f); 109 path.lineTo(1370, 1189); 110 path.conicTo(1370, 1196, 1363, 1196, 0.70710677f); 111 path.lineTo(21, 1196); 112 path.conicTo(14, 1196, 14, 1189, 0.70710677f); 113 path.close(); 114 force_path_contains_rrect(reporter, path); 115 116 path.reset(); 117 path.moveTo(14, 1743); 118 path.lineTo(14, 21); 119 path.conicTo(14, 14, 21, 14, 0.70710677f); 120 path.lineTo(1363, 14); 121 path.conicTo(1370, 14, 1370, 21, 0.70710677f); 122 path.lineTo(1370, 1743); 123 path.conicTo(1370, 1750, 1363, 1750, 0.70710677f); 124 path.lineTo(21, 1750); 125 path.conicTo(14, 1750, 14, 1743, 0.70710677f); 126 path.close(); 127 force_path_contains_rrect(reporter, path); 128 } 129 130 static const SkScalar kWidth = 100.0f; 131 static const SkScalar kHeight = 100.0f; 132 133 static void test_tricky_radii(skiatest::Reporter* reporter) { 134 { 135 // crbug.com/458522 136 SkRRect rr; 137 const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 }; 138 const SkScalar rad = 12814; 139 const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } }; 140 rr.setRectRadii(bounds, vec); 141 path_contains_rrect_check(reporter, rr); 142 } 143 144 { 145 // crbug.com//463920 146 SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0); 147 SkVector radii[4] = { 148 { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f } 149 }; 150 SkRRect rr; 151 rr.setRectRadii(r, radii); 152 path_contains_rrect_nocheck(reporter, rr); 153 } 154 } 155 156 static void test_empty_crbug_458524(skiatest::Reporter* reporter) { 157 SkRRect rr; 158 const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 }; 159 const SkScalar rad = 40; 160 rr.setRectXY(bounds, rad, rad); 161 path_contains_rrect_check(reporter, rr); 162 163 SkRRect other; 164 SkMatrix matrix; 165 matrix.setScale(0, 1); 166 rr.transform(matrix, &other); 167 path_contains_rrect_check(reporter, rr); 168 } 169 170 static void test_inset(skiatest::Reporter* reporter) { 171 SkRRect rr, rr2; 172 SkRect r = { 0, 0, 100, 100 }; 173 174 rr.setRect(r); 175 rr.inset(-20, -20, &rr2); 176 path_contains_rrect_check(reporter, rr); 177 178 rr.inset(20, 20, &rr2); 179 path_contains_rrect_check(reporter, rr); 180 181 rr.inset(r.width()/2, r.height()/2, &rr2); 182 path_contains_rrect_check(reporter, rr); 183 184 rr.setRectXY(r, 20, 20); 185 rr.inset(19, 19, &rr2); 186 path_contains_rrect_check(reporter, rr); 187 rr.inset(20, 20, &rr2); 188 path_contains_rrect_check(reporter, rr); 189 } 190 191 192 static void test_9patch_rrect(skiatest::Reporter* reporter, 193 const SkRect& rect, 194 SkScalar l, SkScalar t, SkScalar r, SkScalar b, 195 bool checkRadii) { 196 SkRRect rr; 197 rr.setNinePatch(rect, l, t, r, b); 198 if (checkRadii) { 199 path_contains_rrect_check(reporter, rr); 200 } else { 201 path_contains_rrect_nocheck(reporter, rr); 202 } 203 204 SkRRect rr2; // construct the same RR using the most general set function 205 SkVector radii[4] = { { l, t }, { r, t }, { r, b }, { l, b } }; 206 rr2.setRectRadii(rect, radii); 207 if (checkRadii) { 208 path_contains_rrect_check(reporter, rr); 209 } else { 210 path_contains_rrect_nocheck(reporter, rr); 211 } 212 } 213 214 // Test out the basic API entry points 215 static void test_round_rect_basic(skiatest::Reporter* reporter) { 216 217 //---- 218 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 219 220 SkRRect rr1; 221 rr1.setRect(rect); 222 path_contains_rrect_check(reporter, rr1); 223 224 SkRRect rr1_2; // construct the same RR using the most general set function 225 SkVector rr1_2_radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; 226 rr1_2.setRectRadii(rect, rr1_2_radii); 227 path_contains_rrect_check(reporter, rr1_2); 228 SkRRect rr1_3; // construct the same RR using the nine patch set function 229 rr1_3.setNinePatch(rect, 0, 0, 0, 0); 230 path_contains_rrect_check(reporter, rr1_2); 231 232 //---- 233 SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) }; 234 SkRRect rr2; 235 rr2.setOval(rect); 236 path_contains_rrect_check(reporter, rr2); 237 238 SkRRect rr2_2; // construct the same RR using the most general set function 239 SkVector rr2_2_radii[4] = { { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY }, 240 { halfPoint.fX, halfPoint.fY }, { halfPoint.fX, halfPoint.fY } }; 241 rr2_2.setRectRadii(rect, rr2_2_radii); 242 path_contains_rrect_check(reporter, rr2_2); 243 SkRRect rr2_3; // construct the same RR using the nine patch set function 244 rr2_3.setNinePatch(rect, halfPoint.fX, halfPoint.fY, halfPoint.fX, halfPoint.fY); 245 path_contains_rrect_check(reporter, rr2_3); 246 247 //---- 248 SkPoint p = { 5, 5 }; 249 SkRRect rr3; 250 rr3.setRectXY(rect, p.fX, p.fY); 251 path_contains_rrect_check(reporter, rr3); 252 253 SkRRect rr3_2; // construct the same RR using the most general set function 254 SkVector rr3_2_radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } }; 255 rr3_2.setRectRadii(rect, rr3_2_radii); 256 path_contains_rrect_check(reporter, rr3_2); 257 SkRRect rr3_3; // construct the same RR using the nine patch set function 258 rr3_3.setNinePatch(rect, 5, 5, 5, 5); 259 path_contains_rrect_check(reporter, rr3_3); 260 261 //---- 262 test_9patch_rrect(reporter, rect, 10, 9, 8, 7, true); 263 264 { 265 // Test out the rrect from skia:3466 266 SkRect rect2 = SkRect::MakeLTRB(0.358211994f, 0.755430222f, 0.872866154f, 0.806214333f); 267 268 test_9patch_rrect(reporter, 269 rect2, 270 0.926942348f, 0.642850280f, 0.529063463f, 0.587844372f, 271 false); 272 } 273 274 //---- 275 SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } }; 276 277 SkRRect rr5; 278 rr5.setRectRadii(rect, radii2); 279 path_contains_rrect_check(reporter, rr5); 280 } 281 282 // Test out the cases when the RR degenerates to a rect 283 static void test_round_rect_rects(skiatest::Reporter* reporter) { 284 285 //---- 286 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 287 SkRRect rr1; 288 rr1.setRectXY(rect, 0, 0); 289 290 path_contains_rrect_check(reporter, rr1); 291 292 //---- 293 SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; 294 295 SkRRect rr2; 296 rr2.setRectRadii(rect, radii); 297 298 path_contains_rrect_check(reporter, rr2); 299 300 //---- 301 SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } }; 302 303 SkRRect rr3; 304 rr3.setRectRadii(rect, radii2); 305 path_contains_rrect_check(reporter, rr3); 306 } 307 308 // Test out the cases when the RR degenerates to an oval 309 static void test_round_rect_ovals(skiatest::Reporter* reporter) { 310 //---- 311 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 312 SkRRect rr1; 313 rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight)); 314 315 path_contains_rrect_check(reporter, rr1); 316 } 317 318 // Test out the non-degenerate RR cases 319 static void test_round_rect_general(skiatest::Reporter* reporter) { 320 //---- 321 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 322 SkRRect rr1; 323 rr1.setRectXY(rect, 20, 20); 324 325 path_contains_rrect_check(reporter, rr1); 326 327 //---- 328 SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } }; 329 330 SkRRect rr2; 331 rr2.setRectRadii(rect, radii); 332 333 path_contains_rrect_check(reporter, rr2); 334 } 335 336 static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) { 337 SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight); 338 SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } }; 339 SkRRect rr1; 340 rr1.setRectRadii(rect, radii); 341 path_contains_rrect_nocheck(reporter, rr1); 342 } 343 344 static void set_radii(SkVector radii[4], int index, float rad) { 345 sk_bzero(radii, sizeof(SkVector) * 4); 346 radii[index].set(rad, rad); 347 } 348 349 static void test_skbug_3239(skiatest::Reporter* reporter) { 350 const float min = SkBits2Float(0xcb7f16c8); /* -16717512.000000 */ 351 const float max = SkBits2Float(0x4b7f1c1d); /* 16718877.000000 */ 352 const float big = SkBits2Float(0x4b7f1bd7); /* 16718807.000000 */ 353 354 const float rad = 33436320; 355 356 const SkRect rectx = SkRect::MakeLTRB(min, min, max, big); 357 const SkRect recty = SkRect::MakeLTRB(min, min, big, max); 358 359 SkVector radii[4]; 360 for (int i = 0; i < 4; ++i) { 361 set_radii(radii, i, rad); 362 path_contains_rrect_check(reporter, rectx, radii); 363 path_contains_rrect_check(reporter, recty, radii); 364 } 365 } 366 367 static void test_mix(skiatest::Reporter* reporter) { 368 // Test out mixed degenerate and non-degenerate geometry with Conics 369 const SkVector radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 100, 100 } }; 370 SkRect r = SkRect::MakeWH(100, 100); 371 SkRRect rr; 372 rr.setRectRadii(r, radii); 373 path_contains_rrect_check(reporter, rr); 374 } 375 376 DEF_TEST(RoundRectInPath, reporter) { 377 test_tricky_radii(reporter); 378 test_empty_crbug_458524(reporter); 379 test_inset(reporter); 380 test_round_rect_basic(reporter); 381 test_round_rect_rects(reporter); 382 test_round_rect_ovals(reporter); 383 test_round_rect_general(reporter); 384 test_undetected_paths(reporter); 385 test_round_rect_iffy_parameters(reporter); 386 test_skbug_3239(reporter); 387 test_mix(reporter); 388 } 389