1 2 /* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 #include "Test.h" 9 #include "SkCanvas.h" 10 #include "SkPaint.h" 11 #include "SkPath.h" 12 #include "SkParse.h" 13 #include "SkParsePath.h" 14 #include "SkPathEffect.h" 15 #include "SkRandom.h" 16 #include "SkReader32.h" 17 #include "SkSize.h" 18 #include "SkSurface.h" 19 #include "SkTypes.h" 20 #include "SkWriter32.h" 21 22 #if defined(WIN32) 23 #define SUPPRESS_VISIBILITY_WARNING 24 #else 25 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden"))) 26 #endif 27 28 static SkSurface* new_surface(int w, int h) { 29 SkImage::Info info = { 30 w, h, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType 31 }; 32 return SkSurface::NewRaster(info); 33 } 34 35 static void test_path_close_issue1474(skiatest::Reporter* reporter) { 36 // This test checks that r{Line,Quad,Conic,Cubic}To following a close() 37 // are relative to the point we close to, not relative to the point we close from. 38 SkPath path; 39 SkPoint last; 40 41 // Test rLineTo(). 42 path.rLineTo(0, 100); 43 path.rLineTo(100, 0); 44 path.close(); // Returns us back to 0,0. 45 path.rLineTo(50, 50); // This should go to 50,50. 46 47 path.getLastPt(&last); 48 REPORTER_ASSERT(reporter, 50 == last.fX); 49 REPORTER_ASSERT(reporter, 50 == last.fY); 50 51 // Test rQuadTo(). 52 path.rewind(); 53 path.rLineTo(0, 100); 54 path.rLineTo(100, 0); 55 path.close(); 56 path.rQuadTo(50, 50, 75, 75); 57 58 path.getLastPt(&last); 59 REPORTER_ASSERT(reporter, 75 == last.fX); 60 REPORTER_ASSERT(reporter, 75 == last.fY); 61 62 // Test rConicTo(). 63 path.rewind(); 64 path.rLineTo(0, 100); 65 path.rLineTo(100, 0); 66 path.close(); 67 path.rConicTo(50, 50, 85, 85, 2); 68 69 path.getLastPt(&last); 70 REPORTER_ASSERT(reporter, 85 == last.fX); 71 REPORTER_ASSERT(reporter, 85 == last.fY); 72 73 // Test rCubicTo(). 74 path.rewind(); 75 path.rLineTo(0, 100); 76 path.rLineTo(100, 0); 77 path.close(); 78 path.rCubicTo(50, 50, 85, 85, 95, 95); 79 80 path.getLastPt(&last); 81 REPORTER_ASSERT(reporter, 95 == last.fX); 82 REPORTER_ASSERT(reporter, 95 == last.fY); 83 } 84 85 static void test_android_specific_behavior(skiatest::Reporter* reporter) { 86 #ifdef SK_BUILD_FOR_ANDROID 87 // Copy constructor should preserve generation ID, but assignment shouldn't. 88 SkPath original; 89 original.moveTo(0, 0); 90 original.lineTo(1, 1); 91 REPORTER_ASSERT(reporter, original.getGenerationID() > 0); 92 93 const SkPath copy(original); 94 REPORTER_ASSERT(reporter, copy.getGenerationID() == original.getGenerationID()); 95 96 SkPath assign; 97 assign = original; 98 REPORTER_ASSERT(reporter, assign.getGenerationID() != original.getGenerationID()); 99 #endif 100 } 101 102 // This used to assert in the debug build, as the edges did not all line-up. 103 static void test_bad_cubic_crbug234190() { 104 SkPath path; 105 path.moveTo(13.8509f, 3.16858f); 106 path.cubicTo(-2.35893e+08f, -4.21044e+08f, 107 -2.38991e+08f, -4.26573e+08f, 108 -2.41016e+08f, -4.30188e+08f); 109 110 SkPaint paint; 111 paint.setAntiAlias(true); 112 SkAutoTUnref<SkSurface> surface(new_surface(84, 88)); 113 surface->getCanvas()->drawPath(path, paint); 114 } 115 116 static void test_bad_cubic_crbug229478() { 117 const SkPoint pts[] = { 118 { 4595.91064f, -11596.9873f }, 119 { 4597.2168f, -11595.9414f }, 120 { 4598.52344f, -11594.8955f }, 121 { 4599.83008f, -11593.8496f }, 122 }; 123 124 SkPath path; 125 path.moveTo(pts[0]); 126 path.cubicTo(pts[1], pts[2], pts[3]); 127 128 SkPaint paint; 129 paint.setStyle(SkPaint::kStroke_Style); 130 paint.setStrokeWidth(20); 131 132 SkPath dst; 133 // Before the fix, this would infinite-recurse, and run out of stack 134 // because we would keep trying to subdivide a degenerate cubic segment. 135 paint.getFillPath(path, &dst, NULL); 136 } 137 138 static void build_path_170666(SkPath& path) { 139 path.moveTo(17.9459f, 21.6344f); 140 path.lineTo(139.545f, -47.8105f); 141 path.lineTo(139.545f, -47.8105f); 142 path.lineTo(131.07f, -47.3888f); 143 path.lineTo(131.07f, -47.3888f); 144 path.lineTo(122.586f, -46.9532f); 145 path.lineTo(122.586f, -46.9532f); 146 path.lineTo(18076.6f, 31390.9f); 147 path.lineTo(18076.6f, 31390.9f); 148 path.lineTo(18085.1f, 31390.5f); 149 path.lineTo(18085.1f, 31390.5f); 150 path.lineTo(18076.6f, 31390.9f); 151 path.lineTo(18076.6f, 31390.9f); 152 path.lineTo(17955, 31460.3f); 153 path.lineTo(17955, 31460.3f); 154 path.lineTo(17963.5f, 31459.9f); 155 path.lineTo(17963.5f, 31459.9f); 156 path.lineTo(17971.9f, 31459.5f); 157 path.lineTo(17971.9f, 31459.5f); 158 path.lineTo(17.9551f, 21.6205f); 159 path.lineTo(17.9551f, 21.6205f); 160 path.lineTo(9.47091f, 22.0561f); 161 path.lineTo(9.47091f, 22.0561f); 162 path.lineTo(17.9459f, 21.6344f); 163 path.lineTo(17.9459f, 21.6344f); 164 path.close();path.moveTo(0.995934f, 22.4779f); 165 path.lineTo(0.986725f, 22.4918f); 166 path.lineTo(0.986725f, 22.4918f); 167 path.lineTo(17955, 31460.4f); 168 path.lineTo(17955, 31460.4f); 169 path.lineTo(17971.9f, 31459.5f); 170 path.lineTo(17971.9f, 31459.5f); 171 path.lineTo(18093.6f, 31390.1f); 172 path.lineTo(18093.6f, 31390.1f); 173 path.lineTo(18093.6f, 31390); 174 path.lineTo(18093.6f, 31390); 175 path.lineTo(139.555f, -47.8244f); 176 path.lineTo(139.555f, -47.8244f); 177 path.lineTo(122.595f, -46.9671f); 178 path.lineTo(122.595f, -46.9671f); 179 path.lineTo(0.995934f, 22.4779f); 180 path.lineTo(0.995934f, 22.4779f); 181 path.close(); 182 path.moveTo(5.43941f, 25.5223f); 183 path.lineTo(798267, -28871.1f); 184 path.lineTo(798267, -28871.1f); 185 path.lineTo(3.12512e+06f, -113102); 186 path.lineTo(3.12512e+06f, -113102); 187 path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813); 188 path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f); 189 path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f); 190 path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f); 191 path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f); 192 path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f); 193 path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f); 194 path.lineTo(2.78271e+08f, -1.00733e+07f); 195 path.lineTo(2.78271e+08f, -1.00733e+07f); 196 path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f); 197 path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f); 198 path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f); 199 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f); 200 path.lineTo(2.77473e+08f, -1.00444e+07f); 201 path.lineTo(2.77473e+08f, -1.00444e+07f); 202 path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f); 203 path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f); 204 path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f); 205 path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f); 206 path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f); 207 path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814); 208 path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103); 209 path.lineTo(798284, -28872); 210 path.lineTo(798284, -28872); 211 path.lineTo(22.4044f, 24.6677f); 212 path.lineTo(22.4044f, 24.6677f); 213 path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f); 214 path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f); 215 path.close(); 216 } 217 218 static void build_path_simple_170666(SkPath& path) { 219 path.moveTo(126.677f, 24.1591f); 220 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f); 221 } 222 223 // This used to assert in the SK_DEBUG build, as the clip step would fail with 224 // too-few interations in our cubic-line intersection code. That code now runs 225 // 24 interations (instead of 16). 226 static void test_crbug_170666() { 227 SkPath path; 228 SkPaint paint; 229 paint.setAntiAlias(true); 230 231 SkAutoTUnref<SkSurface> surface(new_surface(1000, 1000)); 232 233 build_path_simple_170666(path); 234 surface->getCanvas()->drawPath(path, paint); 235 236 build_path_170666(path); 237 surface->getCanvas()->drawPath(path, paint); 238 } 239 240 // Make sure we stay non-finite once we get there (unless we reset or rewind). 241 static void test_addrect_isfinite(skiatest::Reporter* reporter) { 242 SkPath path; 243 244 path.addRect(SkRect::MakeWH(50, 100)); 245 REPORTER_ASSERT(reporter, path.isFinite()); 246 247 path.moveTo(0, 0); 248 path.lineTo(SK_ScalarInfinity, 42); 249 REPORTER_ASSERT(reporter, !path.isFinite()); 250 251 path.addRect(SkRect::MakeWH(50, 100)); 252 REPORTER_ASSERT(reporter, !path.isFinite()); 253 254 path.reset(); 255 REPORTER_ASSERT(reporter, path.isFinite()); 256 257 path.addRect(SkRect::MakeWH(50, 100)); 258 REPORTER_ASSERT(reporter, path.isFinite()); 259 } 260 261 static void build_big_path(SkPath* path, bool reducedCase) { 262 if (reducedCase) { 263 path->moveTo(577330, 1971.72f); 264 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f); 265 } else { 266 path->moveTo(60.1631f, 7.70567f); 267 path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f); 268 path->lineTo(577379, 1977.77f); 269 path->quadTo(577364, 1979.57f, 577325, 1980.26f); 270 path->quadTo(577286, 1980.95f, 577245, 1980.13f); 271 path->quadTo(577205, 1979.3f, 577187, 1977.45f); 272 path->quadTo(577168, 1975.6f, 577183, 1973.8f); 273 path->quadTo(577198, 1972, 577238, 1971.31f); 274 path->quadTo(577277, 1970.62f, 577317, 1971.45f); 275 path->quadTo(577330, 1971.72f, 577341, 1972.11f); 276 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f); 277 path->moveTo(306.718f, -32.912f); 278 path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f); 279 } 280 } 281 282 static void test_clipped_cubic() { 283 SkAutoTUnref<SkSurface> surface(new_surface(640, 480)); 284 285 // This path used to assert, because our cubic-chopping code incorrectly 286 // moved control points after the chop. This test should be run in SK_DEBUG 287 // mode to ensure that we no long assert. 288 SkPath path; 289 for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) { 290 build_big_path(&path, SkToBool(doReducedCase)); 291 292 SkPaint paint; 293 for (int doAA = 0; doAA <= 1; ++doAA) { 294 paint.setAntiAlias(SkToBool(doAA)); 295 surface->getCanvas()->drawPath(path, paint); 296 } 297 } 298 } 299 300 // Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/ 301 // which triggered an assert, from a tricky cubic. This test replicates that 302 // example, so we can ensure that we handle it (in SkEdge.cpp), and don't 303 // assert in the SK_DEBUG build. 304 static void test_tricky_cubic() { 305 const SkPoint pts[] = { 306 { SkDoubleToScalar(18.8943768), SkDoubleToScalar(129.121277) }, 307 { SkDoubleToScalar(18.8937435), SkDoubleToScalar(129.121689) }, 308 { SkDoubleToScalar(18.8950119), SkDoubleToScalar(129.120422) }, 309 { SkDoubleToScalar(18.5030727), SkDoubleToScalar(129.13121) }, 310 }; 311 312 SkPath path; 313 path.moveTo(pts[0]); 314 path.cubicTo(pts[1], pts[2], pts[3]); 315 316 SkPaint paint; 317 paint.setAntiAlias(true); 318 319 SkSurface* surface = new_surface(19, 130); 320 surface->getCanvas()->drawPath(path, paint); 321 surface->unref(); 322 } 323 324 // Inspired by http://code.google.com/p/chromium/issues/detail?id=141651 325 // 326 static void test_isfinite_after_transform(skiatest::Reporter* reporter) { 327 SkPath path; 328 path.quadTo(157, 366, 286, 208); 329 path.arcTo(37, 442, 315, 163, 957494590897113.0f); 330 331 SkMatrix matrix; 332 matrix.setScale(1000*1000, 1000*1000); 333 334 // Be sure that path::transform correctly updates isFinite and the bounds 335 // if the transformation overflows. The previous bug was that isFinite was 336 // set to true in this case, but the bounds were not set to empty (which 337 // they should be). 338 while (path.isFinite()) { 339 REPORTER_ASSERT(reporter, path.getBounds().isFinite()); 340 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty()); 341 path.transform(matrix); 342 } 343 REPORTER_ASSERT(reporter, path.getBounds().isEmpty()); 344 345 matrix.setTranslate(SK_Scalar1, SK_Scalar1); 346 path.transform(matrix); 347 // we need to still be non-finite 348 REPORTER_ASSERT(reporter, !path.isFinite()); 349 REPORTER_ASSERT(reporter, path.getBounds().isEmpty()); 350 } 351 352 static void add_corner_arc(SkPath* path, const SkRect& rect, 353 SkScalar xIn, SkScalar yIn, 354 int startAngle) 355 { 356 357 SkScalar rx = SkMinScalar(rect.width(), xIn); 358 SkScalar ry = SkMinScalar(rect.height(), yIn); 359 360 SkRect arcRect; 361 arcRect.set(-rx, -ry, rx, ry); 362 switch (startAngle) { 363 case 0: 364 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom); 365 break; 366 case 90: 367 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom); 368 break; 369 case 180: 370 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop); 371 break; 372 case 270: 373 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop); 374 break; 375 default: 376 break; 377 } 378 379 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false); 380 } 381 382 static void make_arb_round_rect(SkPath* path, const SkRect& r, 383 SkScalar xCorner, SkScalar yCorner) { 384 // we are lazy here and use the same x & y for each corner 385 add_corner_arc(path, r, xCorner, yCorner, 270); 386 add_corner_arc(path, r, xCorner, yCorner, 0); 387 add_corner_arc(path, r, xCorner, yCorner, 90); 388 add_corner_arc(path, r, xCorner, yCorner, 180); 389 path->close(); 390 } 391 392 // Chrome creates its own round rects with each corner possibly being different. 393 // Performance will suffer if they are not convex. 394 // Note: PathBench::ArbRoundRectBench performs almost exactly 395 // the same test (but with drawing) 396 static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) { 397 SkMWCRandom rand; 398 SkRect r; 399 400 for (int i = 0; i < 5000; ++i) { 401 402 SkScalar size = rand.nextUScalar1() * 30; 403 if (size < SK_Scalar1) { 404 continue; 405 } 406 r.fLeft = rand.nextUScalar1() * 300; 407 r.fTop = rand.nextUScalar1() * 300; 408 r.fRight = r.fLeft + 2 * size; 409 r.fBottom = r.fTop + 2 * size; 410 411 SkPath temp; 412 413 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15); 414 415 REPORTER_ASSERT(reporter, temp.isConvex()); 416 } 417 } 418 419 // Chrome will sometimes create a 0 radius round rect. The degenerate 420 // quads prevent the path from being converted to a rect 421 // Note: PathBench::ArbRoundRectBench performs almost exactly 422 // the same test (but with drawing) 423 static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) { 424 SkMWCRandom rand; 425 SkRect r; 426 427 for (int i = 0; i < 5000; ++i) { 428 429 SkScalar size = rand.nextUScalar1() * 30; 430 if (size < SK_Scalar1) { 431 continue; 432 } 433 r.fLeft = rand.nextUScalar1() * 300; 434 r.fTop = rand.nextUScalar1() * 300; 435 r.fRight = r.fLeft + 2 * size; 436 r.fBottom = r.fTop + 2 * size; 437 438 SkPath temp; 439 440 make_arb_round_rect(&temp, r, 0, 0); 441 442 SkRect result; 443 REPORTER_ASSERT(reporter, temp.isRect(&result)); 444 REPORTER_ASSERT(reporter, r == result); 445 } 446 } 447 448 static void test_rect_isfinite(skiatest::Reporter* reporter) { 449 const SkScalar inf = SK_ScalarInfinity; 450 const SkScalar negInf = SK_ScalarNegativeInfinity; 451 const SkScalar nan = SK_ScalarNaN; 452 453 SkRect r; 454 r.setEmpty(); 455 REPORTER_ASSERT(reporter, r.isFinite()); 456 r.set(0, 0, inf, negInf); 457 REPORTER_ASSERT(reporter, !r.isFinite()); 458 r.set(0, 0, nan, 0); 459 REPORTER_ASSERT(reporter, !r.isFinite()); 460 461 SkPoint pts[] = { 462 { 0, 0 }, 463 { SK_Scalar1, 0 }, 464 { 0, SK_Scalar1 }, 465 }; 466 467 bool isFine = r.setBoundsCheck(pts, 3); 468 REPORTER_ASSERT(reporter, isFine); 469 REPORTER_ASSERT(reporter, !r.isEmpty()); 470 471 pts[1].set(inf, 0); 472 isFine = r.setBoundsCheck(pts, 3); 473 REPORTER_ASSERT(reporter, !isFine); 474 REPORTER_ASSERT(reporter, r.isEmpty()); 475 476 pts[1].set(nan, 0); 477 isFine = r.setBoundsCheck(pts, 3); 478 REPORTER_ASSERT(reporter, !isFine); 479 REPORTER_ASSERT(reporter, r.isEmpty()); 480 } 481 482 static void test_path_isfinite(skiatest::Reporter* reporter) { 483 const SkScalar inf = SK_ScalarInfinity; 484 const SkScalar negInf = SK_ScalarNegativeInfinity; 485 const SkScalar nan = SK_ScalarNaN; 486 487 SkPath path; 488 REPORTER_ASSERT(reporter, path.isFinite()); 489 490 path.reset(); 491 REPORTER_ASSERT(reporter, path.isFinite()); 492 493 path.reset(); 494 path.moveTo(SK_Scalar1, 0); 495 REPORTER_ASSERT(reporter, path.isFinite()); 496 497 path.reset(); 498 path.moveTo(inf, negInf); 499 REPORTER_ASSERT(reporter, !path.isFinite()); 500 501 path.reset(); 502 path.moveTo(nan, 0); 503 REPORTER_ASSERT(reporter, !path.isFinite()); 504 } 505 506 static void test_isfinite(skiatest::Reporter* reporter) { 507 test_rect_isfinite(reporter); 508 test_path_isfinite(reporter); 509 } 510 511 // assert that we always 512 // start with a moveTo 513 // only have 1 moveTo 514 // only have Lines after that 515 // end with a single close 516 // only have (at most) 1 close 517 // 518 static void test_poly(skiatest::Reporter* reporter, const SkPath& path, 519 const SkPoint srcPts[], bool expectClose) { 520 SkPath::RawIter iter(path); 521 SkPoint pts[4]; 522 523 bool firstTime = true; 524 bool foundClose = false; 525 for (;;) { 526 switch (iter.next(pts)) { 527 case SkPath::kMove_Verb: 528 REPORTER_ASSERT(reporter, firstTime); 529 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]); 530 srcPts++; 531 firstTime = false; 532 break; 533 case SkPath::kLine_Verb: 534 REPORTER_ASSERT(reporter, !firstTime); 535 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]); 536 srcPts++; 537 break; 538 case SkPath::kQuad_Verb: 539 REPORTER_ASSERT(reporter, !"unexpected quad verb"); 540 break; 541 case SkPath::kConic_Verb: 542 REPORTER_ASSERT(reporter, !"unexpected conic verb"); 543 break; 544 case SkPath::kCubic_Verb: 545 REPORTER_ASSERT(reporter, !"unexpected cubic verb"); 546 break; 547 case SkPath::kClose_Verb: 548 REPORTER_ASSERT(reporter, !firstTime); 549 REPORTER_ASSERT(reporter, !foundClose); 550 REPORTER_ASSERT(reporter, expectClose); 551 foundClose = true; 552 break; 553 case SkPath::kDone_Verb: 554 goto DONE; 555 } 556 } 557 DONE: 558 REPORTER_ASSERT(reporter, foundClose == expectClose); 559 } 560 561 static void test_addPoly(skiatest::Reporter* reporter) { 562 SkPoint pts[32]; 563 SkMWCRandom rand; 564 565 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) { 566 pts[i].fX = rand.nextSScalar1(); 567 pts[i].fY = rand.nextSScalar1(); 568 } 569 570 for (int doClose = 0; doClose <= 1; ++doClose) { 571 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) { 572 SkPath path; 573 path.addPoly(pts, count, SkToBool(doClose)); 574 test_poly(reporter, path, pts, SkToBool(doClose)); 575 } 576 } 577 } 578 579 static void test_strokerec(skiatest::Reporter* reporter) { 580 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); 581 REPORTER_ASSERT(reporter, rec.isFillStyle()); 582 583 rec.setHairlineStyle(); 584 REPORTER_ASSERT(reporter, rec.isHairlineStyle()); 585 586 rec.setStrokeStyle(SK_Scalar1, false); 587 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle()); 588 589 rec.setStrokeStyle(SK_Scalar1, true); 590 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle()); 591 592 rec.setStrokeStyle(0, false); 593 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle()); 594 595 rec.setStrokeStyle(0, true); 596 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle()); 597 } 598 599 // Set this for paths that don't have a consistent direction such as a bowtie. 600 // (cheapComputeDirection is not expected to catch these.) 601 static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1); 602 603 static void check_direction(skiatest::Reporter* reporter, const SkPath& path, 604 SkPath::Direction expected) { 605 if (expected == kDontCheckDir) { 606 return; 607 } 608 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path. 609 610 SkPath::Direction dir; 611 if (copy.cheapComputeDirection(&dir)) { 612 REPORTER_ASSERT(reporter, dir == expected); 613 } else { 614 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected); 615 } 616 } 617 618 static void test_direction(skiatest::Reporter* reporter) { 619 size_t i; 620 SkPath path; 621 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL)); 622 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction)); 623 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction)); 624 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction)); 625 626 static const char* gDegen[] = { 627 "M 10 10", 628 "M 10 10 M 20 20", 629 "M 10 10 L 20 20", 630 "M 10 10 L 10 10 L 10 10", 631 "M 10 10 Q 10 10 10 10", 632 "M 10 10 C 10 10 10 10 10 10", 633 }; 634 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) { 635 path.reset(); 636 bool valid = SkParsePath::FromSVGString(gDegen[i], &path); 637 REPORTER_ASSERT(reporter, valid); 638 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL)); 639 } 640 641 static const char* gCW[] = { 642 "M 10 10 L 10 10 Q 20 10 20 20", 643 "M 10 10 C 20 10 20 20 20 20", 644 "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max 645 // rect with top two corners replaced by cubics with identical middle 646 // control points 647 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10", 648 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif 649 }; 650 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) { 651 path.reset(); 652 bool valid = SkParsePath::FromSVGString(gCW[i], &path); 653 REPORTER_ASSERT(reporter, valid); 654 check_direction(reporter, path, SkPath::kCW_Direction); 655 } 656 657 static const char* gCCW[] = { 658 "M 10 10 L 10 10 Q 20 10 20 -20", 659 "M 10 10 C 20 10 20 -20 20 -20", 660 "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max 661 // rect with top two corners replaced by cubics with identical middle 662 // control points 663 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10", 664 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif 665 }; 666 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) { 667 path.reset(); 668 bool valid = SkParsePath::FromSVGString(gCCW[i], &path); 669 REPORTER_ASSERT(reporter, valid); 670 check_direction(reporter, path, SkPath::kCCW_Direction); 671 } 672 673 // Test two donuts, each wound a different direction. Only the outer contour 674 // determines the cheap direction 675 path.reset(); 676 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction); 677 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction); 678 check_direction(reporter, path, SkPath::kCW_Direction); 679 680 path.reset(); 681 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction); 682 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction); 683 check_direction(reporter, path, SkPath::kCCW_Direction); 684 685 #ifdef SK_SCALAR_IS_FLOAT 686 // triangle with one point really far from the origin. 687 path.reset(); 688 // the first point is roughly 1.05e10, 1.05e10 689 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652))); 690 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1); 691 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1); 692 check_direction(reporter, path, SkPath::kCCW_Direction); 693 #endif 694 } 695 696 static void add_rect(SkPath* path, const SkRect& r) { 697 path->moveTo(r.fLeft, r.fTop); 698 path->lineTo(r.fRight, r.fTop); 699 path->lineTo(r.fRight, r.fBottom); 700 path->lineTo(r.fLeft, r.fBottom); 701 path->close(); 702 } 703 704 static void test_bounds(skiatest::Reporter* reporter) { 705 static const SkRect rects[] = { 706 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) }, 707 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) }, 708 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) }, 709 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) }, 710 }; 711 712 SkPath path0, path1; 713 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) { 714 path0.addRect(rects[i]); 715 add_rect(&path1, rects[i]); 716 } 717 718 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds()); 719 } 720 721 static void stroke_cubic(const SkPoint pts[4]) { 722 SkPath path; 723 path.moveTo(pts[0]); 724 path.cubicTo(pts[1], pts[2], pts[3]); 725 726 SkPaint paint; 727 paint.setStyle(SkPaint::kStroke_Style); 728 paint.setStrokeWidth(SK_Scalar1 * 2); 729 730 SkPath fill; 731 paint.getFillPath(path, &fill); 732 } 733 734 // just ensure this can run w/o any SkASSERTS firing in the debug build 735 // we used to assert due to differences in how we determine a degenerate vector 736 // but that was fixed with the introduction of SkPoint::CanNormalize 737 static void stroke_tiny_cubic() { 738 SkPoint p0[] = { 739 { 372.0f, 92.0f }, 740 { 372.0f, 92.0f }, 741 { 372.0f, 92.0f }, 742 { 372.0f, 92.0f }, 743 }; 744 745 stroke_cubic(p0); 746 747 SkPoint p1[] = { 748 { 372.0f, 92.0f }, 749 { 372.0007f, 92.000755f }, 750 { 371.99927f, 92.003922f }, 751 { 371.99826f, 92.003899f }, 752 }; 753 754 stroke_cubic(p1); 755 } 756 757 static void check_close(skiatest::Reporter* reporter, const SkPath& path) { 758 for (int i = 0; i < 2; ++i) { 759 SkPath::Iter iter(path, SkToBool(i)); 760 SkPoint mv; 761 SkPoint pts[4]; 762 SkPath::Verb v; 763 int nMT = 0; 764 int nCL = 0; 765 mv.set(0, 0); 766 while (SkPath::kDone_Verb != (v = iter.next(pts))) { 767 switch (v) { 768 case SkPath::kMove_Verb: 769 mv = pts[0]; 770 ++nMT; 771 break; 772 case SkPath::kClose_Verb: 773 REPORTER_ASSERT(reporter, mv == pts[0]); 774 ++nCL; 775 break; 776 default: 777 break; 778 } 779 } 780 // if we force a close on the interator we should have a close 781 // for every moveTo 782 REPORTER_ASSERT(reporter, !i || nMT == nCL); 783 } 784 } 785 786 static void test_close(skiatest::Reporter* reporter) { 787 SkPath closePt; 788 closePt.moveTo(0, 0); 789 closePt.close(); 790 check_close(reporter, closePt); 791 792 SkPath openPt; 793 openPt.moveTo(0, 0); 794 check_close(reporter, openPt); 795 796 SkPath empty; 797 check_close(reporter, empty); 798 empty.close(); 799 check_close(reporter, empty); 800 801 SkPath rect; 802 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1); 803 check_close(reporter, rect); 804 rect.close(); 805 check_close(reporter, rect); 806 807 SkPath quad; 808 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1); 809 check_close(reporter, quad); 810 quad.close(); 811 check_close(reporter, quad); 812 813 SkPath cubic; 814 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 815 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1); 816 check_close(reporter, cubic); 817 cubic.close(); 818 check_close(reporter, cubic); 819 820 SkPath line; 821 line.moveTo(SK_Scalar1, SK_Scalar1); 822 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1); 823 check_close(reporter, line); 824 line.close(); 825 check_close(reporter, line); 826 827 SkPath rect2; 828 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1); 829 rect2.close(); 830 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1); 831 check_close(reporter, rect2); 832 rect2.close(); 833 check_close(reporter, rect2); 834 835 SkPath oval3; 836 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100)); 837 oval3.close(); 838 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200)); 839 check_close(reporter, oval3); 840 oval3.close(); 841 check_close(reporter, oval3); 842 843 SkPath moves; 844 moves.moveTo(SK_Scalar1, SK_Scalar1); 845 moves.moveTo(5 * SK_Scalar1, SK_Scalar1); 846 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1); 847 moves.moveTo(10 *SK_Scalar1, SK_Scalar1); 848 check_close(reporter, moves); 849 850 stroke_tiny_cubic(); 851 } 852 853 static void check_convexity(skiatest::Reporter* reporter, const SkPath& path, 854 SkPath::Convexity expected) { 855 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path. 856 SkPath::Convexity c = copy.getConvexity(); 857 REPORTER_ASSERT(reporter, c == expected); 858 } 859 860 static void test_convexity2(skiatest::Reporter* reporter) { 861 SkPath pt; 862 pt.moveTo(0, 0); 863 pt.close(); 864 check_convexity(reporter, pt, SkPath::kConvex_Convexity); 865 check_direction(reporter, pt, SkPath::kUnknown_Direction); 866 867 SkPath line; 868 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1); 869 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1); 870 line.close(); 871 check_convexity(reporter, line, SkPath::kConvex_Convexity); 872 check_direction(reporter, line, SkPath::kUnknown_Direction); 873 874 SkPath triLeft; 875 triLeft.moveTo(0, 0); 876 triLeft.lineTo(SK_Scalar1, 0); 877 triLeft.lineTo(SK_Scalar1, SK_Scalar1); 878 triLeft.close(); 879 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity); 880 check_direction(reporter, triLeft, SkPath::kCW_Direction); 881 882 SkPath triRight; 883 triRight.moveTo(0, 0); 884 triRight.lineTo(-SK_Scalar1, 0); 885 triRight.lineTo(SK_Scalar1, SK_Scalar1); 886 triRight.close(); 887 check_convexity(reporter, triRight, SkPath::kConvex_Convexity); 888 check_direction(reporter, triRight, SkPath::kCCW_Direction); 889 890 SkPath square; 891 square.moveTo(0, 0); 892 square.lineTo(SK_Scalar1, 0); 893 square.lineTo(SK_Scalar1, SK_Scalar1); 894 square.lineTo(0, SK_Scalar1); 895 square.close(); 896 check_convexity(reporter, square, SkPath::kConvex_Convexity); 897 check_direction(reporter, square, SkPath::kCW_Direction); 898 899 SkPath redundantSquare; 900 redundantSquare.moveTo(0, 0); 901 redundantSquare.lineTo(0, 0); 902 redundantSquare.lineTo(0, 0); 903 redundantSquare.lineTo(SK_Scalar1, 0); 904 redundantSquare.lineTo(SK_Scalar1, 0); 905 redundantSquare.lineTo(SK_Scalar1, 0); 906 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1); 907 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1); 908 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1); 909 redundantSquare.lineTo(0, SK_Scalar1); 910 redundantSquare.lineTo(0, SK_Scalar1); 911 redundantSquare.lineTo(0, SK_Scalar1); 912 redundantSquare.close(); 913 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity); 914 check_direction(reporter, redundantSquare, SkPath::kCW_Direction); 915 916 SkPath bowTie; 917 bowTie.moveTo(0, 0); 918 bowTie.lineTo(0, 0); 919 bowTie.lineTo(0, 0); 920 bowTie.lineTo(SK_Scalar1, SK_Scalar1); 921 bowTie.lineTo(SK_Scalar1, SK_Scalar1); 922 bowTie.lineTo(SK_Scalar1, SK_Scalar1); 923 bowTie.lineTo(SK_Scalar1, 0); 924 bowTie.lineTo(SK_Scalar1, 0); 925 bowTie.lineTo(SK_Scalar1, 0); 926 bowTie.lineTo(0, SK_Scalar1); 927 bowTie.lineTo(0, SK_Scalar1); 928 bowTie.lineTo(0, SK_Scalar1); 929 bowTie.close(); 930 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity); 931 check_direction(reporter, bowTie, kDontCheckDir); 932 933 SkPath spiral; 934 spiral.moveTo(0, 0); 935 spiral.lineTo(100*SK_Scalar1, 0); 936 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1); 937 spiral.lineTo(0, 100*SK_Scalar1); 938 spiral.lineTo(0, 50*SK_Scalar1); 939 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1); 940 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1); 941 spiral.close(); 942 check_convexity(reporter, spiral, SkPath::kConcave_Convexity); 943 check_direction(reporter, spiral, kDontCheckDir); 944 945 SkPath dent; 946 dent.moveTo(0, 0); 947 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1); 948 dent.lineTo(0, 100*SK_Scalar1); 949 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1); 950 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1); 951 dent.close(); 952 check_convexity(reporter, dent, SkPath::kConcave_Convexity); 953 check_direction(reporter, dent, SkPath::kCW_Direction); 954 } 955 956 static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p, 957 const SkRect& bounds) { 958 REPORTER_ASSERT(reporter, p.isConvex()); 959 REPORTER_ASSERT(reporter, p.getBounds() == bounds); 960 961 SkPath p2(p); 962 REPORTER_ASSERT(reporter, p2.isConvex()); 963 REPORTER_ASSERT(reporter, p2.getBounds() == bounds); 964 965 SkPath other; 966 other.swap(p2); 967 REPORTER_ASSERT(reporter, other.isConvex()); 968 REPORTER_ASSERT(reporter, other.getBounds() == bounds); 969 } 970 971 static void setFromString(SkPath* path, const char str[]) { 972 bool first = true; 973 while (str) { 974 SkScalar x, y; 975 str = SkParse::FindScalar(str, &x); 976 if (NULL == str) { 977 break; 978 } 979 str = SkParse::FindScalar(str, &y); 980 SkASSERT(str); 981 if (first) { 982 path->moveTo(x, y); 983 first = false; 984 } else { 985 path->lineTo(x, y); 986 } 987 } 988 } 989 990 static void test_convexity(skiatest::Reporter* reporter) { 991 SkPath path; 992 993 check_convexity(reporter, path, SkPath::kConvex_Convexity); 994 path.addCircle(0, 0, SkIntToScalar(10)); 995 check_convexity(reporter, path, SkPath::kConvex_Convexity); 996 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle 997 check_convexity(reporter, path, SkPath::kConcave_Convexity); 998 999 path.reset(); 1000 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction); 1001 check_convexity(reporter, path, SkPath::kConvex_Convexity); 1002 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction)); 1003 1004 path.reset(); 1005 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction); 1006 check_convexity(reporter, path, SkPath::kConvex_Convexity); 1007 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction)); 1008 1009 static const struct { 1010 const char* fPathStr; 1011 SkPath::Convexity fExpectedConvexity; 1012 SkPath::Direction fExpectedDirection; 1013 } gRec[] = { 1014 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction }, 1015 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction }, 1016 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction }, 1017 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction }, 1018 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction }, 1019 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction }, 1020 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir }, 1021 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction }, 1022 }; 1023 1024 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 1025 SkPath path; 1026 setFromString(&path, gRec[i].fPathStr); 1027 check_convexity(reporter, path, gRec[i].fExpectedConvexity); 1028 check_direction(reporter, path, gRec[i].fExpectedDirection); 1029 } 1030 } 1031 1032 static void test_isLine(skiatest::Reporter* reporter) { 1033 SkPath path; 1034 SkPoint pts[2]; 1035 const SkScalar value = SkIntToScalar(5); 1036 1037 REPORTER_ASSERT(reporter, !path.isLine(NULL)); 1038 1039 // set some non-zero values 1040 pts[0].set(value, value); 1041 pts[1].set(value, value); 1042 REPORTER_ASSERT(reporter, !path.isLine(pts)); 1043 // check that pts was untouched 1044 REPORTER_ASSERT(reporter, pts[0].equals(value, value)); 1045 REPORTER_ASSERT(reporter, pts[1].equals(value, value)); 1046 1047 const SkScalar moveX = SkIntToScalar(1); 1048 const SkScalar moveY = SkIntToScalar(2); 1049 SkASSERT(value != moveX && value != moveY); 1050 1051 path.moveTo(moveX, moveY); 1052 REPORTER_ASSERT(reporter, !path.isLine(NULL)); 1053 REPORTER_ASSERT(reporter, !path.isLine(pts)); 1054 // check that pts was untouched 1055 REPORTER_ASSERT(reporter, pts[0].equals(value, value)); 1056 REPORTER_ASSERT(reporter, pts[1].equals(value, value)); 1057 1058 const SkScalar lineX = SkIntToScalar(2); 1059 const SkScalar lineY = SkIntToScalar(2); 1060 SkASSERT(value != lineX && value != lineY); 1061 1062 path.lineTo(lineX, lineY); 1063 REPORTER_ASSERT(reporter, path.isLine(NULL)); 1064 1065 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY)); 1066 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY)); 1067 REPORTER_ASSERT(reporter, path.isLine(pts)); 1068 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY)); 1069 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY)); 1070 1071 path.lineTo(0, 0); // too many points/verbs 1072 REPORTER_ASSERT(reporter, !path.isLine(NULL)); 1073 REPORTER_ASSERT(reporter, !path.isLine(pts)); 1074 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY)); 1075 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY)); 1076 } 1077 1078 static void test_conservativelyContains(skiatest::Reporter* reporter) { 1079 SkPath path; 1080 1081 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect. 1082 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100)); 1083 1084 // A circle that bounds kBaseRect (with a significant amount of slop) 1085 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height()); 1086 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2; 1087 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()}; 1088 1089 // round-rect radii 1090 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)}; 1091 1092 static const struct SUPPRESS_VISIBILITY_WARNING { 1093 SkRect fQueryRect; 1094 bool fInRect; 1095 bool fInCircle; 1096 bool fInRR; 1097 } kQueries[] = { 1098 {kBaseRect, true, true, false}, 1099 1100 // rect well inside of kBaseRect 1101 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(), 1102 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(), 1103 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(), 1104 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()), 1105 true, true, true}, 1106 1107 // rects with edges off by one from kBaseRect's edges 1108 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 1109 kBaseRect.width(), kBaseRect.height() + 1), 1110 false, true, false}, 1111 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 1112 kBaseRect.width() + 1, kBaseRect.height()), 1113 false, true, false}, 1114 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 1115 kBaseRect.width() + 1, kBaseRect.height() + 1), 1116 false, true, false}, 1117 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop, 1118 kBaseRect.width(), kBaseRect.height()), 1119 false, true, false}, 1120 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1, 1121 kBaseRect.width(), kBaseRect.height()), 1122 false, true, false}, 1123 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop, 1124 kBaseRect.width() + 2, kBaseRect.height()), 1125 false, true, false}, 1126 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1, 1127 kBaseRect.width() + 2, kBaseRect.height()), 1128 false, true, false}, 1129 1130 // zero-w/h rects at each corner of kBaseRect 1131 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false}, 1132 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false}, 1133 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false}, 1134 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false}, 1135 1136 // far away rect 1137 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom, 1138 SkIntToScalar(10), SkIntToScalar(10)), 1139 false, false, false}, 1140 1141 // very large rect containing kBaseRect 1142 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(), 1143 kBaseRect.fTop - 5 * kBaseRect.height(), 1144 11 * kBaseRect.width(), 11 * kBaseRect.height()), 1145 false, false, false}, 1146 1147 // skinny rect that spans same y-range as kBaseRect 1148 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop, 1149 SkIntToScalar(1), kBaseRect.height()), 1150 true, true, true}, 1151 1152 // short rect that spans same x-range as kBaseRect 1153 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)), 1154 true, true, true}, 1155 1156 // skinny rect that spans slightly larger y-range than kBaseRect 1157 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop, 1158 SkIntToScalar(1), kBaseRect.height() + 1), 1159 false, true, false}, 1160 1161 // short rect that spans slightly larger x-range than kBaseRect 1162 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), 1163 kBaseRect.width() + 1, SkScalar(1)), 1164 false, true, false}, 1165 }; 1166 1167 for (int inv = 0; inv < 4; ++inv) { 1168 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) { 1169 SkRect qRect = kQueries[q].fQueryRect; 1170 if (inv & 0x1) { 1171 SkTSwap(qRect.fLeft, qRect.fRight); 1172 } 1173 if (inv & 0x2) { 1174 SkTSwap(qRect.fTop, qRect.fBottom); 1175 } 1176 for (int d = 0; d < 2; ++d) { 1177 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction; 1178 path.reset(); 1179 path.addRect(kBaseRect, dir); 1180 REPORTER_ASSERT(reporter, kQueries[q].fInRect == 1181 path.conservativelyContainsRect(qRect)); 1182 1183 path.reset(); 1184 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir); 1185 REPORTER_ASSERT(reporter, kQueries[q].fInCircle == 1186 path.conservativelyContainsRect(qRect)); 1187 1188 path.reset(); 1189 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir); 1190 REPORTER_ASSERT(reporter, kQueries[q].fInRR == 1191 path.conservativelyContainsRect(qRect)); 1192 } 1193 // Slightly non-convex shape, shouldn't contain any rects. 1194 path.reset(); 1195 path.moveTo(0, 0); 1196 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f)); 1197 path.lineTo(SkIntToScalar(100), 0); 1198 path.lineTo(SkIntToScalar(100), SkIntToScalar(100)); 1199 path.lineTo(0, SkIntToScalar(100)); 1200 path.close(); 1201 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect)); 1202 } 1203 } 1204 1205 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes. 1206 path.reset(); 1207 path.moveTo(0, 0); 1208 path.lineTo(SkIntToScalar(100), 0); 1209 path.lineTo(0, SkIntToScalar(100)); 1210 1211 // inside, on along top edge 1212 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0, 1213 SkIntToScalar(10), 1214 SkIntToScalar(10)))); 1215 // above 1216 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect( 1217 SkRect::MakeXYWH(SkIntToScalar(50), 1218 SkIntToScalar(-10), 1219 SkIntToScalar(10), 1220 SkIntToScalar(10)))); 1221 // to the left 1222 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10), 1223 SkIntToScalar(5), 1224 SkIntToScalar(5), 1225 SkIntToScalar(5)))); 1226 1227 // outside the diagonal edge 1228 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10), 1229 SkIntToScalar(200), 1230 SkIntToScalar(20), 1231 SkIntToScalar(5)))); 1232 1233 // same as above path and first test but with an extra moveTo. 1234 path.reset(); 1235 path.moveTo(100, 100); 1236 path.moveTo(0, 0); 1237 path.lineTo(SkIntToScalar(100), 0); 1238 path.lineTo(0, SkIntToScalar(100)); 1239 1240 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0, 1241 SkIntToScalar(10), 1242 SkIntToScalar(10)))); 1243 1244 } 1245 1246 // Simple isRect test is inline TestPath, below. 1247 // test_isRect provides more extensive testing. 1248 static void test_isRect(skiatest::Reporter* reporter) { 1249 // passing tests (all moveTo / lineTo... 1250 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; 1251 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}}; 1252 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}}; 1253 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}}; 1254 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; 1255 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}}; 1256 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}}; 1257 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}}; 1258 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}}; 1259 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, 1260 {1, 0}, {.5f, 0}}; 1261 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, 1262 {0, 1}, {0, .5f}}; 1263 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}; 1264 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; 1265 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}}; 1266 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}}; 1267 1268 // failing tests 1269 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points 1270 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal 1271 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps 1272 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up 1273 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots 1274 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots 1275 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots 1276 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L' 1277 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps 1278 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap 1279 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short 1280 1281 // failing, no close 1282 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match 1283 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto 1284 1285 size_t testLen[] = { 1286 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6), 1287 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc), 1288 sizeof(rd), sizeof(re), sizeof(rf), 1289 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6), 1290 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb), 1291 sizeof(c1), sizeof(c2) 1292 }; 1293 SkPoint* tests[] = { 1294 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf, 1295 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb, 1296 c1, c2 1297 }; 1298 SkPoint* lastPass = rf; 1299 SkPoint* lastClose = fb; 1300 bool fail = false; 1301 bool close = true; 1302 const size_t testCount = sizeof(tests) / sizeof(tests[0]); 1303 size_t index; 1304 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) { 1305 SkPath path; 1306 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY); 1307 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) { 1308 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY); 1309 } 1310 if (close) { 1311 path.close(); 1312 } 1313 REPORTER_ASSERT(reporter, fail ^ path.isRect(0)); 1314 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL)); 1315 1316 if (!fail) { 1317 SkRect computed, expected; 1318 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint)); 1319 REPORTER_ASSERT(reporter, path.isRect(&computed)); 1320 REPORTER_ASSERT(reporter, expected == computed); 1321 1322 bool isClosed; 1323 SkPath::Direction direction, cheapDirection; 1324 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection)); 1325 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction)); 1326 REPORTER_ASSERT(reporter, isClosed == close); 1327 REPORTER_ASSERT(reporter, direction == cheapDirection); 1328 } else { 1329 SkRect computed; 1330 computed.set(123, 456, 789, 1011); 1331 REPORTER_ASSERT(reporter, !path.isRect(&computed)); 1332 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456); 1333 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011); 1334 1335 bool isClosed = (bool) -1; 1336 SkPath::Direction direction = (SkPath::Direction) -1; 1337 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction)); 1338 REPORTER_ASSERT(reporter, isClosed == (bool) -1); 1339 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1); 1340 } 1341 1342 if (tests[testIndex] == lastPass) { 1343 fail = true; 1344 } 1345 if (tests[testIndex] == lastClose) { 1346 close = false; 1347 } 1348 } 1349 1350 // fail, close then line 1351 SkPath path1; 1352 path1.moveTo(r1[0].fX, r1[0].fY); 1353 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 1354 path1.lineTo(r1[index].fX, r1[index].fY); 1355 } 1356 path1.close(); 1357 path1.lineTo(1, 0); 1358 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); 1359 1360 // fail, move in the middle 1361 path1.reset(); 1362 path1.moveTo(r1[0].fX, r1[0].fY); 1363 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 1364 if (index == 2) { 1365 path1.moveTo(1, .5f); 1366 } 1367 path1.lineTo(r1[index].fX, r1[index].fY); 1368 } 1369 path1.close(); 1370 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); 1371 1372 // fail, move on the edge 1373 path1.reset(); 1374 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 1375 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY); 1376 path1.lineTo(r1[index].fX, r1[index].fY); 1377 } 1378 path1.close(); 1379 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); 1380 1381 // fail, quad 1382 path1.reset(); 1383 path1.moveTo(r1[0].fX, r1[0].fY); 1384 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 1385 if (index == 2) { 1386 path1.quadTo(1, .5f, 1, .5f); 1387 } 1388 path1.lineTo(r1[index].fX, r1[index].fY); 1389 } 1390 path1.close(); 1391 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); 1392 1393 // fail, cubic 1394 path1.reset(); 1395 path1.moveTo(r1[0].fX, r1[0].fY); 1396 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 1397 if (index == 2) { 1398 path1.cubicTo(1, .5f, 1, .5f, 1, .5f); 1399 } 1400 path1.lineTo(r1[index].fX, r1[index].fY); 1401 } 1402 path1.close(); 1403 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); 1404 } 1405 1406 static void test_isNestedRects(skiatest::Reporter* reporter) { 1407 // passing tests (all moveTo / lineTo... 1408 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW 1409 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}}; 1410 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}}; 1411 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}}; 1412 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; // CCW 1413 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}}; 1414 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}}; 1415 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}}; 1416 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}}; 1417 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, // CCW 1418 {1, 0}, {.5f, 0}}; 1419 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, // CW 1420 {0, 1}, {0, .5f}}; 1421 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}; // CW 1422 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; // CCW 1423 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW 1424 1425 // failing tests 1426 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points 1427 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal 1428 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps 1429 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up 1430 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots 1431 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots 1432 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots 1433 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L' 1434 1435 // failing, no close 1436 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match 1437 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto 1438 1439 size_t testLen[] = { 1440 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6), 1441 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc), 1442 sizeof(rd), sizeof(re), 1443 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6), 1444 sizeof(f7), sizeof(f8), 1445 sizeof(c1), sizeof(c2) 1446 }; 1447 SkPoint* tests[] = { 1448 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, 1449 f1, f2, f3, f4, f5, f6, f7, f8, 1450 c1, c2 1451 }; 1452 SkPath::Direction dirs[] = { 1453 SkPath::kCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction, 1454 SkPath::kCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction, 1455 SkPath::kCCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction, 1456 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction, 1457 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kUnknown_Direction, 1458 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, 1459 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, 1460 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, 1461 }; 1462 SkASSERT(SK_ARRAY_COUNT(tests) == SK_ARRAY_COUNT(dirs)); 1463 1464 const SkPoint* lastPass = re; 1465 const SkPoint* lastClose = f8; 1466 const size_t testCount = sizeof(tests) / sizeof(tests[0]); 1467 size_t index; 1468 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) { 1469 bool fail = false; 1470 bool close = true; 1471 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) { 1472 SkPath path; 1473 if (rectFirst) { 1474 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction); 1475 } 1476 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY); 1477 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) { 1478 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY); 1479 } 1480 if (close) { 1481 path.close(); 1482 } 1483 if (!rectFirst) { 1484 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction); 1485 } 1486 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0)); 1487 if (!fail) { 1488 SkRect expected[2], computed[2]; 1489 SkPath::Direction expectedDirs[2], computedDirs[2]; 1490 SkRect testBounds; 1491 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint)); 1492 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2); 1493 expected[1] = testBounds; 1494 if (rectFirst) { 1495 expectedDirs[0] = SkPath::kCW_Direction; 1496 } else { 1497 expectedDirs[0] = SkPath::kCCW_Direction; 1498 } 1499 expectedDirs[1] = dirs[testIndex]; 1500 REPORTER_ASSERT(reporter, path.isNestedRects(computed, computedDirs)); 1501 REPORTER_ASSERT(reporter, expected[0] == computed[0]); 1502 REPORTER_ASSERT(reporter, expected[1] == computed[1]); 1503 REPORTER_ASSERT(reporter, expectedDirs[0] == computedDirs[0]); 1504 REPORTER_ASSERT(reporter, expectedDirs[1] == computedDirs[1]); 1505 } 1506 if (tests[testIndex] == lastPass) { 1507 fail = true; 1508 } 1509 if (tests[testIndex] == lastClose) { 1510 close = false; 1511 } 1512 } 1513 1514 // fail, close then line 1515 SkPath path1; 1516 if (rectFirst) { 1517 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction); 1518 } 1519 path1.moveTo(r1[0].fX, r1[0].fY); 1520 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 1521 path1.lineTo(r1[index].fX, r1[index].fY); 1522 } 1523 path1.close(); 1524 path1.lineTo(1, 0); 1525 if (!rectFirst) { 1526 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction); 1527 } 1528 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0)); 1529 1530 // fail, move in the middle 1531 path1.reset(); 1532 if (rectFirst) { 1533 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction); 1534 } 1535 path1.moveTo(r1[0].fX, r1[0].fY); 1536 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 1537 if (index == 2) { 1538 path1.moveTo(1, .5f); 1539 } 1540 path1.lineTo(r1[index].fX, r1[index].fY); 1541 } 1542 path1.close(); 1543 if (!rectFirst) { 1544 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction); 1545 } 1546 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0)); 1547 1548 // fail, move on the edge 1549 path1.reset(); 1550 if (rectFirst) { 1551 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction); 1552 } 1553 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 1554 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY); 1555 path1.lineTo(r1[index].fX, r1[index].fY); 1556 } 1557 path1.close(); 1558 if (!rectFirst) { 1559 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction); 1560 } 1561 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0)); 1562 1563 // fail, quad 1564 path1.reset(); 1565 if (rectFirst) { 1566 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction); 1567 } 1568 path1.moveTo(r1[0].fX, r1[0].fY); 1569 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 1570 if (index == 2) { 1571 path1.quadTo(1, .5f, 1, .5f); 1572 } 1573 path1.lineTo(r1[index].fX, r1[index].fY); 1574 } 1575 path1.close(); 1576 if (!rectFirst) { 1577 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction); 1578 } 1579 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0)); 1580 1581 // fail, cubic 1582 path1.reset(); 1583 if (rectFirst) { 1584 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction); 1585 } 1586 path1.moveTo(r1[0].fX, r1[0].fY); 1587 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 1588 if (index == 2) { 1589 path1.cubicTo(1, .5f, 1, .5f, 1, .5f); 1590 } 1591 path1.lineTo(r1[index].fX, r1[index].fY); 1592 } 1593 path1.close(); 1594 if (!rectFirst) { 1595 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction); 1596 } 1597 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0)); 1598 1599 // fail, not nested 1600 path1.reset(); 1601 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction); 1602 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction); 1603 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0)); 1604 } 1605 1606 // pass, stroke rect 1607 SkPath src, dst; 1608 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction); 1609 SkPaint strokePaint; 1610 strokePaint.setStyle(SkPaint::kStroke_Style); 1611 strokePaint.setStrokeWidth(2); 1612 strokePaint.getFillPath(src, &dst); 1613 REPORTER_ASSERT(reporter, dst.isNestedRects(0)); 1614 } 1615 1616 static void write_and_read_back(skiatest::Reporter* reporter, 1617 const SkPath& p) { 1618 SkWriter32 writer(100); 1619 writer.writePath(p); 1620 size_t size = writer.size(); 1621 SkAutoMalloc storage(size); 1622 writer.flatten(storage.get()); 1623 SkReader32 reader(storage.get(), size); 1624 1625 SkPath readBack; 1626 REPORTER_ASSERT(reporter, readBack != p); 1627 reader.readPath(&readBack); 1628 REPORTER_ASSERT(reporter, readBack == p); 1629 1630 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() == 1631 p.getConvexityOrUnknown()); 1632 1633 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL)); 1634 1635 const SkRect& origBounds = p.getBounds(); 1636 const SkRect& readBackBounds = readBack.getBounds(); 1637 1638 REPORTER_ASSERT(reporter, origBounds == readBackBounds); 1639 } 1640 1641 static void test_flattening(skiatest::Reporter* reporter) { 1642 SkPath p; 1643 1644 static const SkPoint pts[] = { 1645 { 0, 0 }, 1646 { SkIntToScalar(10), SkIntToScalar(10) }, 1647 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 }, 1648 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) } 1649 }; 1650 p.moveTo(pts[0]); 1651 p.lineTo(pts[1]); 1652 p.quadTo(pts[2], pts[3]); 1653 p.cubicTo(pts[4], pts[5], pts[6]); 1654 1655 write_and_read_back(reporter, p); 1656 1657 // create a buffer that should be much larger than the path so we don't 1658 // kill our stack if writer goes too far. 1659 char buffer[1024]; 1660 uint32_t size1 = p.writeToMemory(NULL); 1661 uint32_t size2 = p.writeToMemory(buffer); 1662 REPORTER_ASSERT(reporter, size1 == size2); 1663 1664 SkPath p2; 1665 uint32_t size3 = p2.readFromMemory(buffer); 1666 REPORTER_ASSERT(reporter, size1 == size3); 1667 REPORTER_ASSERT(reporter, p == p2); 1668 1669 char buffer2[1024]; 1670 size3 = p2.writeToMemory(buffer2); 1671 REPORTER_ASSERT(reporter, size1 == size3); 1672 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0); 1673 1674 // test persistence of the oval flag & convexity 1675 { 1676 SkPath oval; 1677 SkRect rect = SkRect::MakeWH(10, 10); 1678 oval.addOval(rect); 1679 1680 write_and_read_back(reporter, oval); 1681 } 1682 } 1683 1684 static void test_transform(skiatest::Reporter* reporter) { 1685 SkPath p, p1; 1686 1687 static const SkPoint pts[] = { 1688 { 0, 0 }, 1689 { SkIntToScalar(10), SkIntToScalar(10) }, 1690 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 }, 1691 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) } 1692 }; 1693 p.moveTo(pts[0]); 1694 p.lineTo(pts[1]); 1695 p.quadTo(pts[2], pts[3]); 1696 p.cubicTo(pts[4], pts[5], pts[6]); 1697 1698 SkMatrix matrix; 1699 matrix.reset(); 1700 p.transform(matrix, &p1); 1701 REPORTER_ASSERT(reporter, p == p1); 1702 1703 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3); 1704 p.transform(matrix, &p1); 1705 SkPoint pts1[7]; 1706 int count = p1.getPoints(pts1, 7); 1707 REPORTER_ASSERT(reporter, 7 == count); 1708 for (int i = 0; i < count; ++i) { 1709 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3); 1710 REPORTER_ASSERT(reporter, newPt == pts1[i]); 1711 } 1712 } 1713 1714 static void test_zero_length_paths(skiatest::Reporter* reporter) { 1715 SkPath p; 1716 uint8_t verbs[32]; 1717 1718 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData { 1719 const char* testPath; 1720 const size_t numResultPts; 1721 const SkRect resultBound; 1722 const SkPath::Verb* resultVerbs; 1723 const size_t numResultVerbs; 1724 }; 1725 1726 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb }; 1727 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb }; 1728 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb }; 1729 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb }; 1730 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb }; 1731 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb }; 1732 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb }; 1733 static const SkPath::Verb resultVerbs8[] = { 1734 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb 1735 }; 1736 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb }; 1737 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb }; 1738 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb }; 1739 static const SkPath::Verb resultVerbs12[] = { 1740 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb 1741 }; 1742 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb }; 1743 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb }; 1744 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb }; 1745 static const SkPath::Verb resultVerbs16[] = { 1746 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb 1747 }; 1748 static const struct zeroPathTestData gZeroLengthTests[] = { 1749 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1750 { "M 1 1 M 2 1", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) }, 1751 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) }, 1752 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) }, 1753 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) }, 1754 { "M 1 1 L 1 1 M 2 1 L 2 1", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs6, SK_ARRAY_COUNT(resultVerbs6) }, 1755 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) }, 1756 { "M 1 1 L 1 1 z M 2 1 L 2 1 z", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs8, SK_ARRAY_COUNT(resultVerbs8) }, 1757 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) }, 1758 { "M 1 1 Q 1 1 1 1 M 2 1 Q 2 1 2 1", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs10, SK_ARRAY_COUNT(resultVerbs10) }, 1759 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) }, 1760 { "M 1 1 Q 1 1 1 1 z M 2 1 Q 2 1 2 1 z", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs12, SK_ARRAY_COUNT(resultVerbs12) }, 1761 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) }, 1762 { "M 1 1 C 1 1 1 1 1 1 M 2 1 C 2 1 2 1 2 1", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs14, 1763 SK_ARRAY_COUNT(resultVerbs14) 1764 }, 1765 { "M 1 1 C 1 1 1 1 1 1 z", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs15, SK_ARRAY_COUNT(resultVerbs15) }, 1766 { "M 1 1 C 1 1 1 1 1 1 z M 2 1 C 2 1 2 1 2 1 z", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs16, 1767 SK_ARRAY_COUNT(resultVerbs16) 1768 } 1769 }; 1770 1771 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) { 1772 p.reset(); 1773 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p); 1774 REPORTER_ASSERT(reporter, valid); 1775 REPORTER_ASSERT(reporter, !p.isEmpty()); 1776 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints()); 1777 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds()); 1778 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs))); 1779 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) { 1780 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]); 1781 } 1782 } 1783 } 1784 1785 struct SegmentInfo { 1786 SkPath fPath; 1787 int fPointCount; 1788 }; 1789 1790 #define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask) 1791 1792 static void test_segment_masks(skiatest::Reporter* reporter) { 1793 SkPath p, p2; 1794 1795 p.moveTo(0, 0); 1796 p.quadTo(100, 100, 200, 200); 1797 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks()); 1798 REPORTER_ASSERT(reporter, !p.isEmpty()); 1799 p2 = p; 1800 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks()); 1801 p.cubicTo(100, 100, 200, 200, 300, 300); 1802 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks()); 1803 REPORTER_ASSERT(reporter, !p.isEmpty()); 1804 p2 = p; 1805 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks()); 1806 1807 p.reset(); 1808 p.moveTo(0, 0); 1809 p.cubicTo(100, 100, 200, 200, 300, 300); 1810 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks()); 1811 p2 = p; 1812 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks()); 1813 1814 REPORTER_ASSERT(reporter, !p.isEmpty()); 1815 } 1816 1817 static void test_iter(skiatest::Reporter* reporter) { 1818 SkPath p; 1819 SkPoint pts[4]; 1820 1821 // Test an iterator with no path 1822 SkPath::Iter noPathIter; 1823 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); 1824 1825 // Test that setting an empty path works 1826 noPathIter.setPath(p, false); 1827 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); 1828 1829 // Test that close path makes no difference for an empty path 1830 noPathIter.setPath(p, true); 1831 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); 1832 1833 // Test an iterator with an initial empty path 1834 SkPath::Iter iter(p, false); 1835 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 1836 1837 // Test that close path makes no difference 1838 iter.setPath(p, true); 1839 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 1840 1841 1842 struct iterTestData { 1843 const char* testPath; 1844 const bool forceClose; 1845 const bool consumeDegenerates; 1846 const size_t* numResultPtsPerVerb; 1847 const SkPoint* resultPts; 1848 const SkPath::Verb* resultVerbs; 1849 const size_t numResultVerbs; 1850 }; 1851 1852 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb }; 1853 static const SkPath::Verb resultVerbs2[] = { 1854 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb 1855 }; 1856 static const SkPath::Verb resultVerbs3[] = { 1857 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb 1858 }; 1859 static const SkPath::Verb resultVerbs4[] = { 1860 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb 1861 }; 1862 static const SkPath::Verb resultVerbs5[] = { 1863 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb 1864 }; 1865 static const size_t resultPtsSizes1[] = { 0 }; 1866 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 }; 1867 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 }; 1868 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 }; 1869 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 }; 1870 static const SkPoint* resultPts1 = 0; 1871 static const SkPoint resultPts2[] = { 1872 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 } 1873 }; 1874 static const SkPoint resultPts3[] = { 1875 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }, 1876 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 } 1877 }; 1878 static const SkPoint resultPts4[] = { 1879 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 } 1880 }; 1881 static const SkPoint resultPts5[] = { 1882 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 } 1883 }; 1884 static const struct iterTestData gIterTests[] = { 1885 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1886 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1887 { "M 1 0 M 1 0 M 3 0 M 4 0 M 5 0", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1888 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1889 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1890 { "z M 1 0 z z M 2 0 z M 3 0 M 4 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1891 { "z M 1 0 z z M 2 0 z M 3 0 M 4 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1892 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) }, 1893 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) }, 1894 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1895 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1896 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) }, 1897 { "M 1 0 L 1 0 M 0 0 z", true, false, resultPtsSizes5, resultPts5, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) } 1898 }; 1899 1900 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) { 1901 p.reset(); 1902 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p); 1903 REPORTER_ASSERT(reporter, valid); 1904 iter.setPath(p, gIterTests[i].forceClose); 1905 int j = 0, l = 0; 1906 do { 1907 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]); 1908 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) { 1909 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]); 1910 } 1911 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb); 1912 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs); 1913 } 1914 1915 // The GM degeneratesegments.cpp test is more extensive 1916 } 1917 1918 static void test_raw_iter(skiatest::Reporter* reporter) { 1919 SkPath p; 1920 SkPoint pts[4]; 1921 1922 // Test an iterator with no path 1923 SkPath::RawIter noPathIter; 1924 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); 1925 // Test that setting an empty path works 1926 noPathIter.setPath(p); 1927 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); 1928 1929 // Test an iterator with an initial empty path 1930 SkPath::RawIter iter(p); 1931 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 1932 1933 // Test that a move-only path returns the move. 1934 p.moveTo(SK_Scalar1, 0); 1935 iter.setPath(p); 1936 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1937 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); 1938 REPORTER_ASSERT(reporter, pts[0].fY == 0); 1939 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 1940 1941 // No matter how many moves we add, we should get them all back 1942 p.moveTo(SK_Scalar1*2, SK_Scalar1); 1943 p.moveTo(SK_Scalar1*3, SK_Scalar1*2); 1944 iter.setPath(p); 1945 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1946 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); 1947 REPORTER_ASSERT(reporter, pts[0].fY == 0); 1948 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1949 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2); 1950 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1); 1951 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1952 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3); 1953 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2); 1954 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 1955 1956 // Initial close is never ever stored 1957 p.reset(); 1958 p.close(); 1959 iter.setPath(p); 1960 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 1961 1962 // Move/close sequences 1963 p.reset(); 1964 p.close(); // Not stored, no purpose 1965 p.moveTo(SK_Scalar1, 0); 1966 p.close(); 1967 p.close(); // Not stored, no purpose 1968 p.moveTo(SK_Scalar1*2, SK_Scalar1); 1969 p.close(); 1970 p.moveTo(SK_Scalar1*3, SK_Scalar1*2); 1971 p.moveTo(SK_Scalar1*4, SK_Scalar1*3); 1972 p.close(); 1973 iter.setPath(p); 1974 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1975 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); 1976 REPORTER_ASSERT(reporter, pts[0].fY == 0); 1977 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb); 1978 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); 1979 REPORTER_ASSERT(reporter, pts[0].fY == 0); 1980 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1981 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2); 1982 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1); 1983 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb); 1984 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2); 1985 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1); 1986 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1987 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3); 1988 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2); 1989 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1990 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4); 1991 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3); 1992 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb); 1993 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4); 1994 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3); 1995 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 1996 1997 // Generate random paths and verify 1998 SkPoint randomPts[25]; 1999 for (int i = 0; i < 5; ++i) { 2000 for (int j = 0; j < 5; ++j) { 2001 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j); 2002 } 2003 } 2004 2005 // Max of 10 segments, max 3 points per segment 2006 SkMWCRandom rand(9876543); 2007 SkPoint expectedPts[31]; // May have leading moveTo 2008 SkPath::Verb expectedVerbs[22]; // May have leading moveTo 2009 SkPath::Verb nextVerb; 2010 2011 for (int i = 0; i < 500; ++i) { 2012 p.reset(); 2013 bool lastWasClose = true; 2014 bool haveMoveTo = false; 2015 SkPoint lastMoveToPt = { 0, 0 }; 2016 int numPoints = 0; 2017 int numVerbs = (rand.nextU() >> 16) % 10; 2018 int numIterVerbs = 0; 2019 for (int j = 0; j < numVerbs; ++j) { 2020 do { 2021 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb); 2022 } while (lastWasClose && nextVerb == SkPath::kClose_Verb); 2023 switch (nextVerb) { 2024 case SkPath::kMove_Verb: 2025 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25]; 2026 p.moveTo(expectedPts[numPoints]); 2027 lastMoveToPt = expectedPts[numPoints]; 2028 numPoints += 1; 2029 lastWasClose = false; 2030 haveMoveTo = true; 2031 break; 2032 case SkPath::kLine_Verb: 2033 if (!haveMoveTo) { 2034 expectedPts[numPoints++] = lastMoveToPt; 2035 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb; 2036 haveMoveTo = true; 2037 } 2038 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25]; 2039 p.lineTo(expectedPts[numPoints]); 2040 numPoints += 1; 2041 lastWasClose = false; 2042 break; 2043 case SkPath::kQuad_Verb: 2044 if (!haveMoveTo) { 2045 expectedPts[numPoints++] = lastMoveToPt; 2046 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb; 2047 haveMoveTo = true; 2048 } 2049 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25]; 2050 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25]; 2051 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]); 2052 numPoints += 2; 2053 lastWasClose = false; 2054 break; 2055 case SkPath::kConic_Verb: 2056 if (!haveMoveTo) { 2057 expectedPts[numPoints++] = lastMoveToPt; 2058 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb; 2059 haveMoveTo = true; 2060 } 2061 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25]; 2062 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25]; 2063 p.conicTo(expectedPts[numPoints], expectedPts[numPoints + 1], 2064 rand.nextUScalar1() * 4); 2065 numPoints += 2; 2066 lastWasClose = false; 2067 break; 2068 case SkPath::kCubic_Verb: 2069 if (!haveMoveTo) { 2070 expectedPts[numPoints++] = lastMoveToPt; 2071 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb; 2072 haveMoveTo = true; 2073 } 2074 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25]; 2075 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25]; 2076 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25]; 2077 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1], 2078 expectedPts[numPoints + 2]); 2079 numPoints += 3; 2080 lastWasClose = false; 2081 break; 2082 case SkPath::kClose_Verb: 2083 p.close(); 2084 haveMoveTo = false; 2085 lastWasClose = true; 2086 break; 2087 default: 2088 SkASSERT(!"unexpected verb"); 2089 } 2090 expectedVerbs[numIterVerbs++] = nextVerb; 2091 } 2092 2093 iter.setPath(p); 2094 numVerbs = numIterVerbs; 2095 numIterVerbs = 0; 2096 int numIterPts = 0; 2097 SkPoint lastMoveTo; 2098 SkPoint lastPt; 2099 lastMoveTo.set(0, 0); 2100 lastPt.set(0, 0); 2101 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) { 2102 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]); 2103 numIterVerbs++; 2104 switch (nextVerb) { 2105 case SkPath::kMove_Verb: 2106 REPORTER_ASSERT(reporter, numIterPts < numPoints); 2107 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]); 2108 lastPt = lastMoveTo = pts[0]; 2109 numIterPts += 1; 2110 break; 2111 case SkPath::kLine_Verb: 2112 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1); 2113 REPORTER_ASSERT(reporter, pts[0] == lastPt); 2114 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]); 2115 lastPt = pts[1]; 2116 numIterPts += 1; 2117 break; 2118 case SkPath::kQuad_Verb: 2119 case SkPath::kConic_Verb: 2120 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2); 2121 REPORTER_ASSERT(reporter, pts[0] == lastPt); 2122 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]); 2123 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]); 2124 lastPt = pts[2]; 2125 numIterPts += 2; 2126 break; 2127 case SkPath::kCubic_Verb: 2128 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3); 2129 REPORTER_ASSERT(reporter, pts[0] == lastPt); 2130 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]); 2131 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]); 2132 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]); 2133 lastPt = pts[3]; 2134 numIterPts += 3; 2135 break; 2136 case SkPath::kClose_Verb: 2137 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo); 2138 lastPt = lastMoveTo; 2139 break; 2140 default: 2141 SkASSERT(!"unexpected verb"); 2142 } 2143 } 2144 REPORTER_ASSERT(reporter, numIterPts == numPoints); 2145 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs); 2146 } 2147 } 2148 2149 static void check_for_circle(skiatest::Reporter* reporter, 2150 const SkPath& path, 2151 bool expectedCircle, 2152 SkPath::Direction expectedDir) { 2153 SkRect rect; 2154 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle); 2155 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir)); 2156 2157 if (expectedCircle) { 2158 REPORTER_ASSERT(reporter, rect.height() == rect.width()); 2159 } 2160 } 2161 2162 static void test_circle_skew(skiatest::Reporter* reporter, 2163 const SkPath& path, 2164 SkPath::Direction dir) { 2165 SkPath tmp; 2166 2167 SkMatrix m; 2168 m.setSkew(SkIntToScalar(3), SkIntToScalar(5)); 2169 path.transform(m, &tmp); 2170 // this matrix reverses the direction. 2171 if (SkPath::kCCW_Direction == dir) { 2172 dir = SkPath::kCW_Direction; 2173 } else { 2174 SkASSERT(SkPath::kCW_Direction == dir); 2175 dir = SkPath::kCCW_Direction; 2176 } 2177 check_for_circle(reporter, tmp, false, dir); 2178 } 2179 2180 static void test_circle_translate(skiatest::Reporter* reporter, 2181 const SkPath& path, 2182 SkPath::Direction dir) { 2183 SkPath tmp; 2184 2185 // translate at small offset 2186 SkMatrix m; 2187 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15)); 2188 path.transform(m, &tmp); 2189 check_for_circle(reporter, tmp, true, dir); 2190 2191 tmp.reset(); 2192 m.reset(); 2193 2194 // translate at a relatively big offset 2195 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000)); 2196 path.transform(m, &tmp); 2197 check_for_circle(reporter, tmp, true, dir); 2198 } 2199 2200 static void test_circle_rotate(skiatest::Reporter* reporter, 2201 const SkPath& path, 2202 SkPath::Direction dir) { 2203 for (int angle = 0; angle < 360; ++angle) { 2204 SkPath tmp; 2205 SkMatrix m; 2206 m.setRotate(SkIntToScalar(angle)); 2207 path.transform(m, &tmp); 2208 2209 // TODO: a rotated circle whose rotated angle is not a multiple of 90 2210 // degrees is not an oval anymore, this can be improved. we made this 2211 // for the simplicity of our implementation. 2212 if (angle % 90 == 0) { 2213 check_for_circle(reporter, tmp, true, dir); 2214 } else { 2215 check_for_circle(reporter, tmp, false, dir); 2216 } 2217 } 2218 } 2219 2220 static void test_circle_mirror_x(skiatest::Reporter* reporter, 2221 const SkPath& path, 2222 SkPath::Direction dir) { 2223 SkPath tmp; 2224 SkMatrix m; 2225 m.reset(); 2226 m.setScaleX(-SK_Scalar1); 2227 path.transform(m, &tmp); 2228 2229 if (SkPath::kCW_Direction == dir) { 2230 dir = SkPath::kCCW_Direction; 2231 } else { 2232 SkASSERT(SkPath::kCCW_Direction == dir); 2233 dir = SkPath::kCW_Direction; 2234 } 2235 2236 check_for_circle(reporter, tmp, true, dir); 2237 } 2238 2239 static void test_circle_mirror_y(skiatest::Reporter* reporter, 2240 const SkPath& path, 2241 SkPath::Direction dir) { 2242 SkPath tmp; 2243 SkMatrix m; 2244 m.reset(); 2245 m.setScaleY(-SK_Scalar1); 2246 path.transform(m, &tmp); 2247 2248 if (SkPath::kCW_Direction == dir) { 2249 dir = SkPath::kCCW_Direction; 2250 } else { 2251 SkASSERT(SkPath::kCCW_Direction == dir); 2252 dir = SkPath::kCW_Direction; 2253 } 2254 2255 check_for_circle(reporter, tmp, true, dir); 2256 } 2257 2258 static void test_circle_mirror_xy(skiatest::Reporter* reporter, 2259 const SkPath& path, 2260 SkPath::Direction dir) { 2261 SkPath tmp; 2262 SkMatrix m; 2263 m.reset(); 2264 m.setScaleX(-SK_Scalar1); 2265 m.setScaleY(-SK_Scalar1); 2266 path.transform(m, &tmp); 2267 2268 check_for_circle(reporter, tmp, true, dir); 2269 } 2270 2271 static void test_circle_with_direction(skiatest::Reporter* reporter, 2272 SkPath::Direction dir) { 2273 SkPath path; 2274 2275 // circle at origin 2276 path.addCircle(0, 0, SkIntToScalar(20), dir); 2277 check_for_circle(reporter, path, true, dir); 2278 test_circle_rotate(reporter, path, dir); 2279 test_circle_translate(reporter, path, dir); 2280 test_circle_skew(reporter, path, dir); 2281 2282 // circle at an offset at (10, 10) 2283 path.reset(); 2284 path.addCircle(SkIntToScalar(10), SkIntToScalar(10), 2285 SkIntToScalar(20), dir); 2286 check_for_circle(reporter, path, true, dir); 2287 test_circle_rotate(reporter, path, dir); 2288 test_circle_translate(reporter, path, dir); 2289 test_circle_skew(reporter, path, dir); 2290 test_circle_mirror_x(reporter, path, dir); 2291 test_circle_mirror_y(reporter, path, dir); 2292 test_circle_mirror_xy(reporter, path, dir); 2293 } 2294 2295 static void test_circle_with_add_paths(skiatest::Reporter* reporter) { 2296 SkPath path; 2297 SkPath circle; 2298 SkPath rect; 2299 SkPath empty; 2300 2301 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction; 2302 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction; 2303 2304 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir); 2305 rect.addRect(SkIntToScalar(5), SkIntToScalar(5), 2306 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction); 2307 2308 SkMatrix translate; 2309 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12)); 2310 2311 // For simplicity, all the path concatenation related operations 2312 // would mark it non-circle, though in theory it's still a circle. 2313 2314 // empty + circle (translate) 2315 path = empty; 2316 path.addPath(circle, translate); 2317 check_for_circle(reporter, path, false, kCircleDir); 2318 2319 // circle + empty (translate) 2320 path = circle; 2321 path.addPath(empty, translate); 2322 check_for_circle(reporter, path, false, kCircleDir); 2323 2324 // test reverseAddPath 2325 path = circle; 2326 path.reverseAddPath(rect); 2327 check_for_circle(reporter, path, false, kCircleDirOpposite); 2328 } 2329 2330 static void test_circle(skiatest::Reporter* reporter) { 2331 test_circle_with_direction(reporter, SkPath::kCW_Direction); 2332 test_circle_with_direction(reporter, SkPath::kCCW_Direction); 2333 2334 // multiple addCircle() 2335 SkPath path; 2336 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction); 2337 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction); 2338 check_for_circle(reporter, path, false, SkPath::kCW_Direction); 2339 2340 // some extra lineTo() would make isOval() fail 2341 path.reset(); 2342 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction); 2343 path.lineTo(0, 0); 2344 check_for_circle(reporter, path, false, SkPath::kCW_Direction); 2345 2346 // not back to the original point 2347 path.reset(); 2348 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction); 2349 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5)); 2350 check_for_circle(reporter, path, false, SkPath::kCW_Direction); 2351 2352 test_circle_with_add_paths(reporter); 2353 } 2354 2355 static void test_oval(skiatest::Reporter* reporter) { 2356 SkRect rect; 2357 SkMatrix m; 2358 SkPath path; 2359 2360 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50)); 2361 path.addOval(rect); 2362 2363 REPORTER_ASSERT(reporter, path.isOval(NULL)); 2364 2365 m.setRotate(SkIntToScalar(90)); 2366 SkPath tmp; 2367 path.transform(m, &tmp); 2368 // an oval rotated 90 degrees is still an oval. 2369 REPORTER_ASSERT(reporter, tmp.isOval(NULL)); 2370 2371 m.reset(); 2372 m.setRotate(SkIntToScalar(30)); 2373 tmp.reset(); 2374 path.transform(m, &tmp); 2375 // an oval rotated 30 degrees is not an oval anymore. 2376 REPORTER_ASSERT(reporter, !tmp.isOval(NULL)); 2377 2378 // since empty path being transformed. 2379 path.reset(); 2380 tmp.reset(); 2381 m.reset(); 2382 path.transform(m, &tmp); 2383 REPORTER_ASSERT(reporter, !tmp.isOval(NULL)); 2384 2385 // empty path is not an oval 2386 tmp.reset(); 2387 REPORTER_ASSERT(reporter, !tmp.isOval(NULL)); 2388 2389 // only has moveTo()s 2390 tmp.reset(); 2391 tmp.moveTo(0, 0); 2392 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10)); 2393 REPORTER_ASSERT(reporter, !tmp.isOval(NULL)); 2394 2395 // mimic WebKit's calling convention, 2396 // call moveTo() first and then call addOval() 2397 path.reset(); 2398 path.moveTo(0, 0); 2399 path.addOval(rect); 2400 REPORTER_ASSERT(reporter, path.isOval(NULL)); 2401 2402 // copy path 2403 path.reset(); 2404 tmp.reset(); 2405 tmp.addOval(rect); 2406 path = tmp; 2407 REPORTER_ASSERT(reporter, path.isOval(NULL)); 2408 } 2409 2410 static void test_empty(skiatest::Reporter* reporter, const SkPath& p) { 2411 SkPath empty; 2412 2413 REPORTER_ASSERT(reporter, p.isEmpty()); 2414 REPORTER_ASSERT(reporter, 0 == p.countPoints()); 2415 REPORTER_ASSERT(reporter, 0 == p.countVerbs()); 2416 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks()); 2417 REPORTER_ASSERT(reporter, p.isConvex()); 2418 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType); 2419 REPORTER_ASSERT(reporter, !p.isInverseFillType()); 2420 REPORTER_ASSERT(reporter, p == empty); 2421 REPORTER_ASSERT(reporter, !(p != empty)); 2422 } 2423 2424 static void TestPath(skiatest::Reporter* reporter) { 2425 SkTSize<SkScalar>::Make(3,4); 2426 2427 SkPath p, empty; 2428 SkRect bounds, bounds2; 2429 test_empty(reporter, p); 2430 2431 REPORTER_ASSERT(reporter, p.getBounds().isEmpty()); 2432 2433 bounds.set(0, 0, SK_Scalar1, SK_Scalar1); 2434 2435 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1); 2436 check_convex_bounds(reporter, p, bounds); 2437 // we have quads or cubics 2438 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask); 2439 REPORTER_ASSERT(reporter, !p.isEmpty()); 2440 2441 p.reset(); 2442 test_empty(reporter, p); 2443 2444 p.addOval(bounds); 2445 check_convex_bounds(reporter, p, bounds); 2446 REPORTER_ASSERT(reporter, !p.isEmpty()); 2447 2448 p.rewind(); 2449 test_empty(reporter, p); 2450 2451 p.addRect(bounds); 2452 check_convex_bounds(reporter, p, bounds); 2453 // we have only lines 2454 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks()); 2455 REPORTER_ASSERT(reporter, !p.isEmpty()); 2456 2457 REPORTER_ASSERT(reporter, p != empty); 2458 REPORTER_ASSERT(reporter, !(p == empty)); 2459 2460 // do getPoints and getVerbs return the right result 2461 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4); 2462 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5); 2463 SkPoint pts[4]; 2464 int count = p.getPoints(pts, 4); 2465 REPORTER_ASSERT(reporter, count == 4); 2466 uint8_t verbs[6]; 2467 verbs[5] = 0xff; 2468 p.getVerbs(verbs, 5); 2469 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]); 2470 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]); 2471 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]); 2472 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]); 2473 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]); 2474 REPORTER_ASSERT(reporter, 0xff == verbs[5]); 2475 bounds2.set(pts, 4); 2476 REPORTER_ASSERT(reporter, bounds == bounds2); 2477 2478 bounds.offset(SK_Scalar1*3, SK_Scalar1*4); 2479 p.offset(SK_Scalar1*3, SK_Scalar1*4); 2480 REPORTER_ASSERT(reporter, bounds == p.getBounds()); 2481 2482 REPORTER_ASSERT(reporter, p.isRect(NULL)); 2483 bounds2.setEmpty(); 2484 REPORTER_ASSERT(reporter, p.isRect(&bounds2)); 2485 REPORTER_ASSERT(reporter, bounds == bounds2); 2486 2487 // now force p to not be a rect 2488 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2); 2489 p.addRect(bounds); 2490 REPORTER_ASSERT(reporter, !p.isRect(NULL)); 2491 2492 test_isLine(reporter); 2493 test_isRect(reporter); 2494 test_isNestedRects(reporter); 2495 test_zero_length_paths(reporter); 2496 test_direction(reporter); 2497 test_convexity(reporter); 2498 test_convexity2(reporter); 2499 test_conservativelyContains(reporter); 2500 test_close(reporter); 2501 test_segment_masks(reporter); 2502 test_flattening(reporter); 2503 test_transform(reporter); 2504 test_bounds(reporter); 2505 test_iter(reporter); 2506 test_raw_iter(reporter); 2507 test_circle(reporter); 2508 test_oval(reporter); 2509 test_strokerec(reporter); 2510 test_addPoly(reporter); 2511 test_isfinite(reporter); 2512 test_isfinite_after_transform(reporter); 2513 test_arb_round_rect_is_convex(reporter); 2514 test_arb_zero_rad_round_rect_is_rect(reporter); 2515 test_addrect_isfinite(reporter); 2516 test_tricky_cubic(); 2517 test_clipped_cubic(); 2518 test_crbug_170666(); 2519 test_bad_cubic_crbug229478(); 2520 test_bad_cubic_crbug234190(); 2521 test_android_specific_behavior(reporter); 2522 test_path_close_issue1474(reporter); 2523 } 2524 2525 #include "TestClassDef.h" 2526 DEFINE_TESTCLASS("Path", PathTestClass, TestPath) 2527