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