caryclark | da707bf | 2015-11-19 14:47:43 -0800 | [diff] [blame] | 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 | } |