PathTest.cpp revision 2972bb5fd2441709026b350c6b9b66eecd80f868
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 "SkPaint.h" 10#include "SkPath.h" 11#include "SkParse.h" 12#include "SkParsePath.h" 13#include "SkPathEffect.h" 14#include "SkRandom.h" 15#include "SkReader32.h" 16#include "SkSize.h" 17#include "SkWriter32.h" 18 19static void test_rect_isfinite(skiatest::Reporter* reporter) { 20 const SkScalar inf = SK_ScalarInfinity; 21 const SkScalar nan = SK_ScalarNaN; 22 23 SkRect r; 24 r.setEmpty(); 25 REPORTER_ASSERT(reporter, r.isFinite()); 26 r.set(0, 0, inf, -inf); 27 REPORTER_ASSERT(reporter, !r.isFinite()); 28 r.set(0, 0, nan, 0); 29 REPORTER_ASSERT(reporter, !r.isFinite()); 30 31 SkPoint pts[] = { 32 { 0, 0 }, 33 { SK_Scalar1, 0 }, 34 { 0, SK_Scalar1 }, 35 }; 36 37 bool isFine = r.setBoundsCheck(pts, 3); 38 REPORTER_ASSERT(reporter, isFine); 39 REPORTER_ASSERT(reporter, !r.isEmpty()); 40 41 pts[1].set(inf, 0); 42 isFine = r.setBoundsCheck(pts, 3); 43 REPORTER_ASSERT(reporter, !isFine); 44 REPORTER_ASSERT(reporter, r.isEmpty()); 45 46 pts[1].set(nan, 0); 47 isFine = r.setBoundsCheck(pts, 3); 48 REPORTER_ASSERT(reporter, !isFine); 49 REPORTER_ASSERT(reporter, r.isEmpty()); 50} 51 52static void test_path_isfinite(skiatest::Reporter* reporter) { 53 const SkScalar inf = SK_ScalarInfinity; 54 const SkScalar nan = SK_ScalarNaN; 55 56 SkPath path; 57 REPORTER_ASSERT(reporter, path.isFinite()); 58 59 path.reset(); 60 REPORTER_ASSERT(reporter, path.isFinite()); 61 62 path.reset(); 63 path.moveTo(SK_Scalar1, 0); 64 REPORTER_ASSERT(reporter, path.isFinite()); 65 66 path.reset(); 67 path.moveTo(inf, -inf); 68 REPORTER_ASSERT(reporter, !path.isFinite()); 69 70 path.reset(); 71 path.moveTo(nan, 0); 72 REPORTER_ASSERT(reporter, !path.isFinite()); 73} 74 75static void test_isfinite(skiatest::Reporter* reporter) { 76 test_rect_isfinite(reporter); 77 test_path_isfinite(reporter); 78} 79 80// assert that we always 81// start with a moveTo 82// only have 1 moveTo 83// only have Lines after that 84// end with a single close 85// only have (at most) 1 close 86// 87static void test_poly(skiatest::Reporter* reporter, const SkPath& path, 88 const SkPoint srcPts[], int count, bool expectClose) { 89 SkPath::RawIter iter(path); 90 SkPoint pts[4]; 91 92 bool firstTime = true; 93 bool foundClose = false; 94 for (;;) { 95 switch (iter.next(pts)) { 96 case SkPath::kMove_Verb: 97 REPORTER_ASSERT(reporter, firstTime); 98 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]); 99 srcPts++; 100 firstTime = false; 101 break; 102 case SkPath::kLine_Verb: 103 REPORTER_ASSERT(reporter, !firstTime); 104 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]); 105 srcPts++; 106 break; 107 case SkPath::kQuad_Verb: 108 REPORTER_ASSERT(reporter, !"unexpected quad verb"); 109 break; 110 case SkPath::kCubic_Verb: 111 REPORTER_ASSERT(reporter, !"unexpected cubic verb"); 112 break; 113 case SkPath::kClose_Verb: 114 REPORTER_ASSERT(reporter, !firstTime); 115 REPORTER_ASSERT(reporter, !foundClose); 116 REPORTER_ASSERT(reporter, expectClose); 117 foundClose = true; 118 break; 119 case SkPath::kDone_Verb: 120 goto DONE; 121 } 122 } 123DONE: 124 REPORTER_ASSERT(reporter, foundClose == expectClose); 125} 126 127static void test_addPoly(skiatest::Reporter* reporter) { 128 SkPoint pts[32]; 129 SkRandom rand; 130 131 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) { 132 pts[i].fX = rand.nextSScalar1(); 133 pts[i].fY = rand.nextSScalar1(); 134 } 135 136 for (int doClose = 0; doClose <= 1; ++doClose) { 137 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) { 138 SkPath path; 139 path.addPoly(pts, count, SkToBool(doClose)); 140 test_poly(reporter, path, pts, count, SkToBool(doClose)); 141 } 142 } 143} 144 145static void test_strokerec(skiatest::Reporter* reporter) { 146 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); 147 REPORTER_ASSERT(reporter, rec.isFillStyle()); 148 149 rec.setHairlineStyle(); 150 REPORTER_ASSERT(reporter, rec.isHairlineStyle()); 151 152 rec.setStrokeStyle(SK_Scalar1, false); 153 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle()); 154 155 rec.setStrokeStyle(SK_Scalar1, true); 156 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle()); 157 158 rec.setStrokeStyle(0, false); 159 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle()); 160 161 rec.setStrokeStyle(0, true); 162 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle()); 163} 164 165/** 166 * cheapIsDirection can take a shortcut when a path is marked convex. 167 * This function ensures that we always test cheapIsDirection when the path 168 * is flagged with unknown convexity status. 169 */ 170static void check_direction(SkPath* path, 171 SkPath::Direction expectedDir, 172 skiatest::Reporter* reporter) { 173 if (SkPath::kConvex_Convexity == path->getConvexity()) { 174 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir)); 175 path->setConvexity(SkPath::kUnknown_Convexity); 176 } 177 REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir)); 178} 179 180static void test_direction(skiatest::Reporter* reporter) { 181 size_t i; 182 SkPath path; 183 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL)); 184 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction)); 185 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction)); 186 187 static const char* gDegen[] = { 188 "M 10 10", 189 "M 10 10 M 20 20", 190 "M 10 10 L 20 20", 191 "M 10 10 L 10 10 L 10 10", 192 "M 10 10 Q 10 10 10 10", 193 "M 10 10 C 10 10 10 10 10 10", 194 }; 195 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) { 196 path.reset(); 197 bool valid = SkParsePath::FromSVGString(gDegen[i], &path); 198 REPORTER_ASSERT(reporter, valid); 199 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL)); 200 } 201 202 static const char* gCW[] = { 203 "M 10 10 L 10 10 Q 20 10 20 20", 204 "M 10 10 C 20 10 20 20 20 20", 205 "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max 206 // rect with top two corners replaced by cubics with identical middle 207 // control points 208 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10" 209 }; 210 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) { 211 path.reset(); 212 bool valid = SkParsePath::FromSVGString(gCW[i], &path); 213 REPORTER_ASSERT(reporter, valid); 214 check_direction(&path, SkPath::kCW_Direction, reporter); 215 } 216 217 static const char* gCCW[] = { 218 "M 10 10 L 10 10 Q 20 10 20 -20", 219 "M 10 10 C 20 10 20 -20 20 -20", 220 "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max 221 // rect with top two corners replaced by cubics with identical middle 222 // control points 223 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10" 224 }; 225 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) { 226 path.reset(); 227 bool valid = SkParsePath::FromSVGString(gCCW[i], &path); 228 REPORTER_ASSERT(reporter, valid); 229 check_direction(&path, SkPath::kCCW_Direction, reporter); 230 } 231 232 // Test two donuts, each wound a different direction. Only the outer contour 233 // determines the cheap direction 234 path.reset(); 235 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction); 236 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction); 237 check_direction(&path, SkPath::kCW_Direction, reporter); 238 239 path.reset(); 240 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction); 241 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction); 242 check_direction(&path, SkPath::kCCW_Direction, reporter); 243 244#ifdef SK_SCALAR_IS_FLOAT 245 // triangle with one point really far from the origin. 246 path.reset(); 247 // the first point is roughly 1.05e10, 1.05e10 248 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652))); 249 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1); 250 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1); 251 check_direction(&path, SkPath::kCCW_Direction, reporter); 252#endif 253} 254 255static void add_rect(SkPath* path, const SkRect& r) { 256 path->moveTo(r.fLeft, r.fTop); 257 path->lineTo(r.fRight, r.fTop); 258 path->lineTo(r.fRight, r.fBottom); 259 path->lineTo(r.fLeft, r.fBottom); 260 path->close(); 261} 262 263static void test_bounds(skiatest::Reporter* reporter) { 264 static const SkRect rects[] = { 265 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) }, 266 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) }, 267 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) }, 268 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) }, 269 }; 270 271 SkPath path0, path1; 272 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) { 273 path0.addRect(rects[i]); 274 add_rect(&path1, rects[i]); 275 } 276 277 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds()); 278} 279 280static void stroke_cubic(const SkPoint pts[4]) { 281 SkPath path; 282 path.moveTo(pts[0]); 283 path.cubicTo(pts[1], pts[2], pts[3]); 284 285 SkPaint paint; 286 paint.setStyle(SkPaint::kStroke_Style); 287 paint.setStrokeWidth(SK_Scalar1 * 2); 288 289 SkPath fill; 290 paint.getFillPath(path, &fill); 291} 292 293// just ensure this can run w/o any SkASSERTS firing in the debug build 294// we used to assert due to differences in how we determine a degenerate vector 295// but that was fixed with the introduction of SkPoint::CanNormalize 296static void stroke_tiny_cubic() { 297 SkPoint p0[] = { 298 { 372.0f, 92.0f }, 299 { 372.0f, 92.0f }, 300 { 372.0f, 92.0f }, 301 { 372.0f, 92.0f }, 302 }; 303 304 stroke_cubic(p0); 305 306 SkPoint p1[] = { 307 { 372.0f, 92.0f }, 308 { 372.0007f, 92.000755f }, 309 { 371.99927f, 92.003922f }, 310 { 371.99826f, 92.003899f }, 311 }; 312 313 stroke_cubic(p1); 314} 315 316static void check_close(skiatest::Reporter* reporter, const SkPath& path) { 317 for (int i = 0; i < 2; ++i) { 318 SkPath::Iter iter(path, SkToBool(i)); 319 SkPoint mv; 320 SkPoint pts[4]; 321 SkPath::Verb v; 322 int nMT = 0; 323 int nCL = 0; 324 mv.set(0, 0); 325 while (SkPath::kDone_Verb != (v = iter.next(pts))) { 326 switch (v) { 327 case SkPath::kMove_Verb: 328 mv = pts[0]; 329 ++nMT; 330 break; 331 case SkPath::kClose_Verb: 332 REPORTER_ASSERT(reporter, mv == pts[0]); 333 ++nCL; 334 break; 335 default: 336 break; 337 } 338 } 339 // if we force a close on the interator we should have a close 340 // for every moveTo 341 REPORTER_ASSERT(reporter, !i || nMT == nCL); 342 } 343} 344 345static void test_close(skiatest::Reporter* reporter) { 346 SkPath closePt; 347 closePt.moveTo(0, 0); 348 closePt.close(); 349 check_close(reporter, closePt); 350 351 SkPath openPt; 352 openPt.moveTo(0, 0); 353 check_close(reporter, openPt); 354 355 SkPath empty; 356 check_close(reporter, empty); 357 empty.close(); 358 check_close(reporter, empty); 359 360 SkPath rect; 361 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1); 362 check_close(reporter, rect); 363 rect.close(); 364 check_close(reporter, rect); 365 366 SkPath quad; 367 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1); 368 check_close(reporter, quad); 369 quad.close(); 370 check_close(reporter, quad); 371 372 SkPath cubic; 373 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 374 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1); 375 check_close(reporter, cubic); 376 cubic.close(); 377 check_close(reporter, cubic); 378 379 SkPath line; 380 line.moveTo(SK_Scalar1, SK_Scalar1); 381 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1); 382 check_close(reporter, line); 383 line.close(); 384 check_close(reporter, line); 385 386 SkPath rect2; 387 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1); 388 rect2.close(); 389 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1); 390 check_close(reporter, rect2); 391 rect2.close(); 392 check_close(reporter, rect2); 393 394 SkPath oval3; 395 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100)); 396 oval3.close(); 397 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200)); 398 check_close(reporter, oval3); 399 oval3.close(); 400 check_close(reporter, oval3); 401 402 SkPath moves; 403 moves.moveTo(SK_Scalar1, SK_Scalar1); 404 moves.moveTo(5 * SK_Scalar1, SK_Scalar1); 405 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1); 406 moves.moveTo(10 *SK_Scalar1, SK_Scalar1); 407 check_close(reporter, moves); 408 409 stroke_tiny_cubic(); 410} 411 412static void check_convexity(skiatest::Reporter* reporter, const SkPath& path, 413 SkPath::Convexity expected) { 414 SkPath::Convexity c = SkPath::ComputeConvexity(path); 415 REPORTER_ASSERT(reporter, c == expected); 416} 417 418static void test_convexity2(skiatest::Reporter* reporter) { 419 SkPath pt; 420 pt.moveTo(0, 0); 421 pt.close(); 422 check_convexity(reporter, pt, SkPath::kConvex_Convexity); 423 424 SkPath line; 425 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1); 426 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1); 427 line.close(); 428 check_convexity(reporter, pt, SkPath::kConvex_Convexity); 429 430 SkPath triLeft; 431 triLeft.moveTo(0, 0); 432 triLeft.lineTo(SK_Scalar1, 0); 433 triLeft.lineTo(SK_Scalar1, SK_Scalar1); 434 triLeft.close(); 435 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity); 436 437 SkPath triRight; 438 triRight.moveTo(0, 0); 439 triRight.lineTo(-SK_Scalar1, 0); 440 triRight.lineTo(SK_Scalar1, SK_Scalar1); 441 triRight.close(); 442 check_convexity(reporter, triRight, SkPath::kConvex_Convexity); 443 444 SkPath square; 445 square.moveTo(0, 0); 446 square.lineTo(SK_Scalar1, 0); 447 square.lineTo(SK_Scalar1, SK_Scalar1); 448 square.lineTo(0, SK_Scalar1); 449 square.close(); 450 check_convexity(reporter, square, SkPath::kConvex_Convexity); 451 452 SkPath redundantSquare; 453 redundantSquare.moveTo(0, 0); 454 redundantSquare.lineTo(0, 0); 455 redundantSquare.lineTo(0, 0); 456 redundantSquare.lineTo(SK_Scalar1, 0); 457 redundantSquare.lineTo(SK_Scalar1, 0); 458 redundantSquare.lineTo(SK_Scalar1, 0); 459 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1); 460 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1); 461 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1); 462 redundantSquare.lineTo(0, SK_Scalar1); 463 redundantSquare.lineTo(0, SK_Scalar1); 464 redundantSquare.lineTo(0, SK_Scalar1); 465 redundantSquare.close(); 466 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity); 467 468 SkPath bowTie; 469 bowTie.moveTo(0, 0); 470 bowTie.lineTo(0, 0); 471 bowTie.lineTo(0, 0); 472 bowTie.lineTo(SK_Scalar1, SK_Scalar1); 473 bowTie.lineTo(SK_Scalar1, SK_Scalar1); 474 bowTie.lineTo(SK_Scalar1, SK_Scalar1); 475 bowTie.lineTo(SK_Scalar1, 0); 476 bowTie.lineTo(SK_Scalar1, 0); 477 bowTie.lineTo(SK_Scalar1, 0); 478 bowTie.lineTo(0, SK_Scalar1); 479 bowTie.lineTo(0, SK_Scalar1); 480 bowTie.lineTo(0, SK_Scalar1); 481 bowTie.close(); 482 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity); 483 484 SkPath spiral; 485 spiral.moveTo(0, 0); 486 spiral.lineTo(100*SK_Scalar1, 0); 487 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1); 488 spiral.lineTo(0, 100*SK_Scalar1); 489 spiral.lineTo(0, 50*SK_Scalar1); 490 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1); 491 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1); 492 spiral.close(); 493 check_convexity(reporter, spiral, SkPath::kConcave_Convexity); 494 495 SkPath dent; 496 dent.moveTo(0, 0); 497 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1); 498 dent.lineTo(0, 100*SK_Scalar1); 499 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1); 500 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1); 501 dent.close(); 502 check_convexity(reporter, dent, SkPath::kConcave_Convexity); 503} 504 505static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p, 506 const SkRect& bounds) { 507 REPORTER_ASSERT(reporter, p.isConvex()); 508 REPORTER_ASSERT(reporter, p.getBounds() == bounds); 509 510 SkPath p2(p); 511 REPORTER_ASSERT(reporter, p2.isConvex()); 512 REPORTER_ASSERT(reporter, p2.getBounds() == bounds); 513 514 SkPath other; 515 other.swap(p2); 516 REPORTER_ASSERT(reporter, other.isConvex()); 517 REPORTER_ASSERT(reporter, other.getBounds() == bounds); 518} 519 520static void setFromString(SkPath* path, const char str[]) { 521 bool first = true; 522 while (str) { 523 SkScalar x, y; 524 str = SkParse::FindScalar(str, &x); 525 if (NULL == str) { 526 break; 527 } 528 str = SkParse::FindScalar(str, &y); 529 SkASSERT(str); 530 if (first) { 531 path->moveTo(x, y); 532 first = false; 533 } else { 534 path->lineTo(x, y); 535 } 536 } 537} 538 539static void test_convexity(skiatest::Reporter* reporter) { 540 static const SkPath::Convexity C = SkPath::kConcave_Convexity; 541 static const SkPath::Convexity V = SkPath::kConvex_Convexity; 542 543 SkPath path; 544 545 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path)); 546 path.addCircle(0, 0, SkIntToScalar(10)); 547 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path)); 548 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle 549 REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path)); 550 path.reset(); 551 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction); 552 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path)); 553 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction)); 554 path.reset(); 555 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction); 556 REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path)); 557 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction)); 558 559 static const struct { 560 const char* fPathStr; 561 SkPath::Convexity fExpectedConvexity; 562 } gRec[] = { 563 { "", SkPath::kConvex_Convexity }, 564 { "0 0", SkPath::kConvex_Convexity }, 565 { "0 0 10 10", SkPath::kConvex_Convexity }, 566 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity }, 567 { "0 0 10 10 10 20", SkPath::kConvex_Convexity }, 568 { "0 0 10 10 10 0", SkPath::kConvex_Convexity }, 569 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity }, 570 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity }, 571 }; 572 573 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { 574 SkPath path; 575 setFromString(&path, gRec[i].fPathStr); 576 SkPath::Convexity c = SkPath::ComputeConvexity(path); 577 REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity); 578 } 579} 580 581static void test_isLine(skiatest::Reporter* reporter) { 582 SkPath path; 583 SkPoint pts[2]; 584 const SkScalar value = SkIntToScalar(5); 585 586 REPORTER_ASSERT(reporter, !path.isLine(NULL)); 587 588 // set some non-zero values 589 pts[0].set(value, value); 590 pts[1].set(value, value); 591 REPORTER_ASSERT(reporter, !path.isLine(pts)); 592 // check that pts was untouched 593 REPORTER_ASSERT(reporter, pts[0].equals(value, value)); 594 REPORTER_ASSERT(reporter, pts[1].equals(value, value)); 595 596 const SkScalar moveX = SkIntToScalar(1); 597 const SkScalar moveY = SkIntToScalar(2); 598 SkASSERT(value != moveX && value != moveY); 599 600 path.moveTo(moveX, moveY); 601 REPORTER_ASSERT(reporter, !path.isLine(NULL)); 602 REPORTER_ASSERT(reporter, !path.isLine(pts)); 603 // check that pts was untouched 604 REPORTER_ASSERT(reporter, pts[0].equals(value, value)); 605 REPORTER_ASSERT(reporter, pts[1].equals(value, value)); 606 607 const SkScalar lineX = SkIntToScalar(2); 608 const SkScalar lineY = SkIntToScalar(2); 609 SkASSERT(value != lineX && value != lineY); 610 611 path.lineTo(lineX, lineY); 612 REPORTER_ASSERT(reporter, path.isLine(NULL)); 613 614 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY)); 615 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY)); 616 REPORTER_ASSERT(reporter, path.isLine(pts)); 617 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY)); 618 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY)); 619 620 path.lineTo(0, 0); // too many points/verbs 621 REPORTER_ASSERT(reporter, !path.isLine(NULL)); 622 REPORTER_ASSERT(reporter, !path.isLine(pts)); 623 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY)); 624 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY)); 625} 626 627// Simple isRect test is inline TestPath, below. 628// test_isRect provides more extensive testing. 629static void test_isRect(skiatest::Reporter* reporter) { 630 // passing tests (all moveTo / lineTo... 631 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; 632 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}}; 633 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}}; 634 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}}; 635 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; 636 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}}; 637 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}}; 638 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}}; 639 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}}; 640 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, 641 {1, 0}, {.5f, 0}}; 642 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, 643 {0, 1}, {0, .5f}}; 644 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}; 645 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; 646 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}}; 647 648 // failing tests 649 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points 650 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal 651 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps 652 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up 653 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots 654 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots 655 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots 656 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L' 657 658 // failing, no close 659 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match 660 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto 661 662 size_t testLen[] = { 663 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6), 664 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc), 665 sizeof(rd), sizeof(re), 666 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6), 667 sizeof(f7), sizeof(f8), 668 sizeof(c1), sizeof(c2) 669 }; 670 SkPoint* tests[] = { 671 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, 672 f1, f2, f3, f4, f5, f6, f7, f8, 673 c1, c2 674 }; 675 SkPoint* lastPass = re; 676 SkPoint* lastClose = f8; 677 bool fail = false; 678 bool close = true; 679 const size_t testCount = sizeof(tests) / sizeof(tests[0]); 680 size_t index; 681 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) { 682 SkPath path; 683 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY); 684 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) { 685 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY); 686 } 687 if (close) { 688 path.close(); 689 } 690 REPORTER_ASSERT(reporter, fail ^ path.isRect(0)); 691 if (tests[testIndex] == lastPass) { 692 fail = true; 693 } 694 if (tests[testIndex] == lastClose) { 695 close = false; 696 } 697 } 698 699 // fail, close then line 700 SkPath path1; 701 path1.moveTo(r1[0].fX, r1[0].fY); 702 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 703 path1.lineTo(r1[index].fX, r1[index].fY); 704 } 705 path1.close(); 706 path1.lineTo(1, 0); 707 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); 708 709 // fail, move in the middle 710 path1.reset(); 711 path1.moveTo(r1[0].fX, r1[0].fY); 712 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 713 if (index == 2) { 714 path1.moveTo(1, .5f); 715 } 716 path1.lineTo(r1[index].fX, r1[index].fY); 717 } 718 path1.close(); 719 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); 720 721 // fail, move on the edge 722 path1.reset(); 723 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 724 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY); 725 path1.lineTo(r1[index].fX, r1[index].fY); 726 } 727 path1.close(); 728 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); 729 730 // fail, quad 731 path1.reset(); 732 path1.moveTo(r1[0].fX, r1[0].fY); 733 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 734 if (index == 2) { 735 path1.quadTo(1, .5f, 1, .5f); 736 } 737 path1.lineTo(r1[index].fX, r1[index].fY); 738 } 739 path1.close(); 740 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); 741 742 // fail, cubic 743 path1.reset(); 744 path1.moveTo(r1[0].fX, r1[0].fY); 745 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) { 746 if (index == 2) { 747 path1.cubicTo(1, .5f, 1, .5f, 1, .5f); 748 } 749 path1.lineTo(r1[index].fX, r1[index].fY); 750 } 751 path1.close(); 752 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0)); 753} 754 755static void write_and_read_back(skiatest::Reporter* reporter, 756 const SkPath& p) { 757 SkWriter32 writer(100); 758 writer.writePath(p); 759 size_t size = writer.size(); 760 SkAutoMalloc storage(size); 761 writer.flatten(storage.get()); 762 SkReader32 reader(storage.get(), size); 763 764 SkPath readBack; 765 REPORTER_ASSERT(reporter, readBack != p); 766 reader.readPath(&readBack); 767 REPORTER_ASSERT(reporter, readBack == p); 768 769 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() == 770 p.getConvexityOrUnknown()); 771 772 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL)); 773 774 const SkRect& origBounds = p.getBounds(); 775 const SkRect& readBackBounds = readBack.getBounds(); 776 777 REPORTER_ASSERT(reporter, origBounds == readBackBounds); 778} 779 780static void test_flattening(skiatest::Reporter* reporter) { 781 SkPath p; 782 783 static const SkPoint pts[] = { 784 { 0, 0 }, 785 { SkIntToScalar(10), SkIntToScalar(10) }, 786 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 }, 787 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) } 788 }; 789 p.moveTo(pts[0]); 790 p.lineTo(pts[1]); 791 p.quadTo(pts[2], pts[3]); 792 p.cubicTo(pts[4], pts[5], pts[6]); 793 794 write_and_read_back(reporter, p); 795 796 // create a buffer that should be much larger than the path so we don't 797 // kill our stack if writer goes too far. 798 char buffer[1024]; 799 uint32_t size1 = p.writeToMemory(NULL); 800 uint32_t size2 = p.writeToMemory(buffer); 801 REPORTER_ASSERT(reporter, size1 == size2); 802 803 SkPath p2; 804 uint32_t size3 = p2.readFromMemory(buffer); 805 REPORTER_ASSERT(reporter, size1 == size3); 806 REPORTER_ASSERT(reporter, p == p2); 807 808 char buffer2[1024]; 809 size3 = p2.writeToMemory(buffer2); 810 REPORTER_ASSERT(reporter, size1 == size3); 811 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0); 812 813 // test persistence of the oval flag & convexity 814 { 815 SkPath oval; 816 SkRect rect = SkRect::MakeWH(10, 10); 817 oval.addOval(rect); 818 819 write_and_read_back(reporter, oval); 820 } 821} 822 823static void test_transform(skiatest::Reporter* reporter) { 824 SkPath p, p1; 825 826 static const SkPoint pts[] = { 827 { 0, 0 }, 828 { SkIntToScalar(10), SkIntToScalar(10) }, 829 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 }, 830 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) } 831 }; 832 p.moveTo(pts[0]); 833 p.lineTo(pts[1]); 834 p.quadTo(pts[2], pts[3]); 835 p.cubicTo(pts[4], pts[5], pts[6]); 836 837 SkMatrix matrix; 838 matrix.reset(); 839 p.transform(matrix, &p1); 840 REPORTER_ASSERT(reporter, p == p1); 841 842 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3); 843 p.transform(matrix, &p1); 844 SkPoint pts1[7]; 845 int count = p1.getPoints(pts1, 7); 846 REPORTER_ASSERT(reporter, 7 == count); 847 for (int i = 0; i < count; ++i) { 848 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3); 849 REPORTER_ASSERT(reporter, newPt == pts1[i]); 850 } 851} 852 853static void test_zero_length_paths(skiatest::Reporter* reporter) { 854 SkPath p; 855 uint8_t verbs[32]; 856 857 struct zeroPathTestData { 858 const char* testPath; 859 const size_t numResultPts; 860 const SkRect resultBound; 861 const SkPath::Verb* resultVerbs; 862 const size_t numResultVerbs; 863 }; 864 865 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb }; 866 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb }; 867 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb }; 868 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb }; 869 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb }; 870 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb }; 871 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb }; 872 static const SkPath::Verb resultVerbs8[] = { 873 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb 874 }; 875 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb }; 876 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb }; 877 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb }; 878 static const SkPath::Verb resultVerbs12[] = { 879 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb 880 }; 881 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb }; 882 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb }; 883 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb }; 884 static const SkPath::Verb resultVerbs16[] = { 885 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb 886 }; 887 static const struct zeroPathTestData gZeroLengthTests[] = { 888 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 889 { "M 1 1 M 2 1", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) }, 890 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) }, 891 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) }, 892 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) }, 893 { "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) }, 894 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) }, 895 { "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) }, 896 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) }, 897 { "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) }, 898 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) }, 899 { "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) }, 900 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) }, 901 { "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, 902 SK_ARRAY_COUNT(resultVerbs14) 903 }, 904 { "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) }, 905 { "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, 906 SK_ARRAY_COUNT(resultVerbs16) 907 } 908 }; 909 910 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) { 911 p.reset(); 912 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p); 913 REPORTER_ASSERT(reporter, valid); 914 REPORTER_ASSERT(reporter, !p.isEmpty()); 915 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints()); 916 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds()); 917 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs))); 918 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) { 919 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]); 920 } 921 } 922} 923 924struct SegmentInfo { 925 SkPath fPath; 926 int fPointCount; 927}; 928 929#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask) 930 931static void test_segment_masks(skiatest::Reporter* reporter) { 932 SkPath p, p2; 933 934 p.moveTo(0, 0); 935 p.quadTo(100, 100, 200, 200); 936 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks()); 937 REPORTER_ASSERT(reporter, !p.isEmpty()); 938 p2 = p; 939 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks()); 940 p.cubicTo(100, 100, 200, 200, 300, 300); 941 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks()); 942 REPORTER_ASSERT(reporter, !p.isEmpty()); 943 p2 = p; 944 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks()); 945 946 p.reset(); 947 p.moveTo(0, 0); 948 p.cubicTo(100, 100, 200, 200, 300, 300); 949 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks()); 950 p2 = p; 951 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks()); 952 953 REPORTER_ASSERT(reporter, !p.isEmpty()); 954} 955 956static void test_iter(skiatest::Reporter* reporter) { 957 SkPath p; 958 SkPoint pts[4]; 959 960 // Test an iterator with no path 961 SkPath::Iter noPathIter; 962 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); 963 964 // Test that setting an empty path works 965 noPathIter.setPath(p, false); 966 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); 967 968 // Test that close path makes no difference for an empty path 969 noPathIter.setPath(p, true); 970 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); 971 972 // Test an iterator with an initial empty path 973 SkPath::Iter iter(p, false); 974 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 975 976 // Test that close path makes no difference 977 iter.setPath(p, true); 978 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 979 980 981 struct iterTestData { 982 const char* testPath; 983 const bool forceClose; 984 const bool consumeDegenerates; 985 const size_t* numResultPtsPerVerb; 986 const SkPoint* resultPts; 987 const SkPath::Verb* resultVerbs; 988 const size_t numResultVerbs; 989 }; 990 991 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb }; 992 static const SkPath::Verb resultVerbs2[] = { 993 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb 994 }; 995 static const SkPath::Verb resultVerbs3[] = { 996 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb 997 }; 998 static const SkPath::Verb resultVerbs4[] = { 999 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb 1000 }; 1001 static const SkPath::Verb resultVerbs5[] = { 1002 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb 1003 }; 1004 static const size_t resultPtsSizes1[] = { 0 }; 1005 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 }; 1006 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 }; 1007 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 }; 1008 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 }; 1009 static const SkPoint* resultPts1 = 0; 1010 static const SkPoint resultPts2[] = { 1011 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 } 1012 }; 1013 static const SkPoint resultPts3[] = { 1014 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }, 1015 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 } 1016 }; 1017 static const SkPoint resultPts4[] = { 1018 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 } 1019 }; 1020 static const SkPoint resultPts5[] = { 1021 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 } 1022 }; 1023 static const struct iterTestData gIterTests[] = { 1024 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1025 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1026 { "M 1 0 M 1 0 M 3 0 M 4 0 M 5 0", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1027 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1028 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1029 { "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) }, 1030 { "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) }, 1031 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) }, 1032 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) }, 1033 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1034 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) }, 1035 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) }, 1036 { "M 1 0 L 1 0 M 0 0 z", true, false, resultPtsSizes5, resultPts5, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) } 1037 }; 1038 1039 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) { 1040 p.reset(); 1041 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p); 1042 REPORTER_ASSERT(reporter, valid); 1043 iter.setPath(p, gIterTests[i].forceClose); 1044 int j = 0, l = 0; 1045 do { 1046 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]); 1047 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) { 1048 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]); 1049 } 1050 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb); 1051 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs); 1052 } 1053 1054 // The GM degeneratesegments.cpp test is more extensive 1055} 1056 1057static void test_raw_iter(skiatest::Reporter* reporter) { 1058 SkPath p; 1059 SkPoint pts[4]; 1060 1061 // Test an iterator with no path 1062 SkPath::RawIter noPathIter; 1063 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); 1064 // Test that setting an empty path works 1065 noPathIter.setPath(p); 1066 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb); 1067 1068 // Test an iterator with an initial empty path 1069 SkPath::RawIter iter(p); 1070 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 1071 1072 // Test that a move-only path returns the move. 1073 p.moveTo(SK_Scalar1, 0); 1074 iter.setPath(p); 1075 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1076 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); 1077 REPORTER_ASSERT(reporter, pts[0].fY == 0); 1078 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 1079 1080 // No matter how many moves we add, we should get them all back 1081 p.moveTo(SK_Scalar1*2, SK_Scalar1); 1082 p.moveTo(SK_Scalar1*3, SK_Scalar1*2); 1083 iter.setPath(p); 1084 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1085 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); 1086 REPORTER_ASSERT(reporter, pts[0].fY == 0); 1087 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1088 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2); 1089 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1); 1090 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1091 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3); 1092 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2); 1093 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 1094 1095 // Initial close is never ever stored 1096 p.reset(); 1097 p.close(); 1098 iter.setPath(p); 1099 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 1100 1101 // Move/close sequences 1102 p.reset(); 1103 p.close(); // Not stored, no purpose 1104 p.moveTo(SK_Scalar1, 0); 1105 p.close(); 1106 p.close(); // Not stored, no purpose 1107 p.moveTo(SK_Scalar1*2, SK_Scalar1); 1108 p.close(); 1109 p.moveTo(SK_Scalar1*3, SK_Scalar1*2); 1110 p.moveTo(SK_Scalar1*4, SK_Scalar1*3); 1111 p.close(); 1112 iter.setPath(p); 1113 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1114 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); 1115 REPORTER_ASSERT(reporter, pts[0].fY == 0); 1116 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb); 1117 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1); 1118 REPORTER_ASSERT(reporter, pts[0].fY == 0); 1119 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1120 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2); 1121 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1); 1122 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb); 1123 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2); 1124 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1); 1125 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1126 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3); 1127 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2); 1128 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb); 1129 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4); 1130 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3); 1131 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb); 1132 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4); 1133 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3); 1134 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb); 1135 1136 // Generate random paths and verify 1137 SkPoint randomPts[25]; 1138 for (int i = 0; i < 5; ++i) { 1139 for (int j = 0; j < 5; ++j) { 1140 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j); 1141 } 1142 } 1143 1144 // Max of 10 segments, max 3 points per segment 1145 SkRandom rand(9876543); 1146 SkPoint expectedPts[31]; // May have leading moveTo 1147 SkPath::Verb expectedVerbs[22]; // May have leading moveTo 1148 SkPath::Verb nextVerb; 1149 1150 for (int i = 0; i < 500; ++i) { 1151 p.reset(); 1152 bool lastWasClose = true; 1153 bool haveMoveTo = false; 1154 SkPoint lastMoveToPt = { 0, 0 }; 1155 int numPoints = 0; 1156 int numVerbs = (rand.nextU() >> 16) % 10; 1157 int numIterVerbs = 0; 1158 for (int j = 0; j < numVerbs; ++j) { 1159 do { 1160 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb); 1161 } while (lastWasClose && nextVerb == SkPath::kClose_Verb); 1162 switch (nextVerb) { 1163 case SkPath::kMove_Verb: 1164 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25]; 1165 p.moveTo(expectedPts[numPoints]); 1166 lastMoveToPt = expectedPts[numPoints]; 1167 numPoints += 1; 1168 lastWasClose = false; 1169 haveMoveTo = true; 1170 break; 1171 case SkPath::kLine_Verb: 1172 if (!haveMoveTo) { 1173 expectedPts[numPoints++] = lastMoveToPt; 1174 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb; 1175 haveMoveTo = true; 1176 } 1177 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25]; 1178 p.lineTo(expectedPts[numPoints]); 1179 numPoints += 1; 1180 lastWasClose = false; 1181 break; 1182 case SkPath::kQuad_Verb: 1183 if (!haveMoveTo) { 1184 expectedPts[numPoints++] = lastMoveToPt; 1185 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb; 1186 haveMoveTo = true; 1187 } 1188 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25]; 1189 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25]; 1190 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]); 1191 numPoints += 2; 1192 lastWasClose = false; 1193 break; 1194 case SkPath::kCubic_Verb: 1195 if (!haveMoveTo) { 1196 expectedPts[numPoints++] = lastMoveToPt; 1197 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb; 1198 haveMoveTo = true; 1199 } 1200 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25]; 1201 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25]; 1202 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25]; 1203 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1], 1204 expectedPts[numPoints + 2]); 1205 numPoints += 3; 1206 lastWasClose = false; 1207 break; 1208 case SkPath::kClose_Verb: 1209 p.close(); 1210 haveMoveTo = false; 1211 lastWasClose = true; 1212 break; 1213 default:; 1214 } 1215 expectedVerbs[numIterVerbs++] = nextVerb; 1216 } 1217 1218 iter.setPath(p); 1219 numVerbs = numIterVerbs; 1220 numIterVerbs = 0; 1221 int numIterPts = 0; 1222 SkPoint lastMoveTo; 1223 SkPoint lastPt; 1224 lastMoveTo.set(0, 0); 1225 lastPt.set(0, 0); 1226 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) { 1227 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]); 1228 numIterVerbs++; 1229 switch (nextVerb) { 1230 case SkPath::kMove_Verb: 1231 REPORTER_ASSERT(reporter, numIterPts < numPoints); 1232 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]); 1233 lastPt = lastMoveTo = pts[0]; 1234 numIterPts += 1; 1235 break; 1236 case SkPath::kLine_Verb: 1237 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1); 1238 REPORTER_ASSERT(reporter, pts[0] == lastPt); 1239 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]); 1240 lastPt = pts[1]; 1241 numIterPts += 1; 1242 break; 1243 case SkPath::kQuad_Verb: 1244 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2); 1245 REPORTER_ASSERT(reporter, pts[0] == lastPt); 1246 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]); 1247 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]); 1248 lastPt = pts[2]; 1249 numIterPts += 2; 1250 break; 1251 case SkPath::kCubic_Verb: 1252 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3); 1253 REPORTER_ASSERT(reporter, pts[0] == lastPt); 1254 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]); 1255 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]); 1256 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]); 1257 lastPt = pts[3]; 1258 numIterPts += 3; 1259 break; 1260 case SkPath::kClose_Verb: 1261 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo); 1262 lastPt = lastMoveTo; 1263 break; 1264 default:; 1265 } 1266 } 1267 REPORTER_ASSERT(reporter, numIterPts == numPoints); 1268 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs); 1269 } 1270} 1271 1272static void check_for_circle(skiatest::Reporter* reporter, 1273 const SkPath& path, bool expected) { 1274 SkRect rect; 1275 REPORTER_ASSERT(reporter, path.isOval(&rect) == expected); 1276 if (expected) { 1277 REPORTER_ASSERT(reporter, rect.height() == rect.width()); 1278 } 1279} 1280 1281static void test_circle_skew(skiatest::Reporter* reporter, 1282 const SkPath& path) { 1283 SkPath tmp; 1284 1285 SkMatrix m; 1286 m.setSkew(SkIntToScalar(3), SkIntToScalar(5)); 1287 path.transform(m, &tmp); 1288 check_for_circle(reporter, tmp, false); 1289} 1290 1291static void test_circle_translate(skiatest::Reporter* reporter, 1292 const SkPath& path) { 1293 SkPath tmp; 1294 1295 // translate at small offset 1296 SkMatrix m; 1297 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15)); 1298 path.transform(m, &tmp); 1299 check_for_circle(reporter, tmp, true); 1300 1301 tmp.reset(); 1302 m.reset(); 1303 1304 // translate at a relatively big offset 1305 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000)); 1306 path.transform(m, &tmp); 1307 check_for_circle(reporter, tmp, true); 1308} 1309 1310static void test_circle_rotate(skiatest::Reporter* reporter, 1311 const SkPath& path) { 1312 for (int angle = 0; angle < 360; ++angle) { 1313 SkPath tmp; 1314 SkMatrix m; 1315 m.setRotate(SkIntToScalar(angle)); 1316 path.transform(m, &tmp); 1317 1318 // TODO: a rotated circle whose rotated angle is not a mutiple of 90 1319 // degrees is not an oval anymore, this can be improved. we made this 1320 // for the simplicity of our implementation. 1321 if (angle % 90 == 0) { 1322 check_for_circle(reporter, tmp, true); 1323 } else { 1324 check_for_circle(reporter, tmp, false); 1325 } 1326 } 1327} 1328 1329static void test_circle_with_direction(skiatest::Reporter* reporter, 1330 SkPath::Direction dir) { 1331 SkPath path; 1332 1333 // circle at origin 1334 path.addCircle(0, 0, SkIntToScalar(20), dir); 1335 check_for_circle(reporter, path, true); 1336 test_circle_rotate(reporter, path); 1337 test_circle_translate(reporter, path); 1338 test_circle_skew(reporter, path); 1339 1340 // circle at an offset at (10, 10) 1341 path.reset(); 1342 path.addCircle(SkIntToScalar(10), SkIntToScalar(10), 1343 SkIntToScalar(20), dir); 1344 check_for_circle(reporter, path, true); 1345 test_circle_rotate(reporter, path); 1346 test_circle_translate(reporter, path); 1347 test_circle_skew(reporter, path); 1348} 1349 1350static void test_circle_with_add_paths(skiatest::Reporter* reporter) { 1351 SkPath path; 1352 SkPath circle; 1353 SkPath rect; 1354 SkPath empty; 1355 1356 circle.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction); 1357 rect.addRect(SkIntToScalar(5), SkIntToScalar(5), 1358 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction); 1359 1360 SkMatrix translate; 1361 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12)); 1362 1363 // For simplicity, all the path concatenation related operations 1364 // would mark it non-circle, though in theory it's still a circle. 1365 1366 // empty + circle (translate) 1367 path = empty; 1368 path.addPath(circle, translate); 1369 check_for_circle(reporter, path, false); 1370 1371 // circle + empty (translate) 1372 path = circle; 1373 path.addPath(empty, translate); 1374 check_for_circle(reporter, path, false); 1375 1376 // test reverseAddPath 1377 path = circle; 1378 path.reverseAddPath(rect); 1379 check_for_circle(reporter, path, false); 1380} 1381 1382static void test_circle(skiatest::Reporter* reporter) { 1383 test_circle_with_direction(reporter, SkPath::kCW_Direction); 1384 test_circle_with_direction(reporter, SkPath::kCCW_Direction); 1385 1386 // multiple addCircle() 1387 SkPath path; 1388 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction); 1389 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction); 1390 check_for_circle(reporter, path, false); 1391 1392 // some extra lineTo() would make isOval() fail 1393 path.reset(); 1394 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction); 1395 path.lineTo(0, 0); 1396 check_for_circle(reporter, path, false); 1397 1398 // not back to the original point 1399 path.reset(); 1400 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction); 1401 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5)); 1402 check_for_circle(reporter, path, false); 1403 1404 test_circle_with_add_paths(reporter); 1405} 1406 1407static void test_oval(skiatest::Reporter* reporter) { 1408 SkRect rect; 1409 SkMatrix m; 1410 SkPath path; 1411 1412 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50)); 1413 path.addOval(rect); 1414 1415 REPORTER_ASSERT(reporter, path.isOval(NULL)); 1416 1417 m.setRotate(SkIntToScalar(90)); 1418 SkPath tmp; 1419 path.transform(m, &tmp); 1420 // an oval rotated 90 degrees is still an oval. 1421 REPORTER_ASSERT(reporter, tmp.isOval(NULL)); 1422 1423 m.reset(); 1424 m.setRotate(SkIntToScalar(30)); 1425 tmp.reset(); 1426 path.transform(m, &tmp); 1427 // an oval rotated 30 degrees is not an oval anymore. 1428 REPORTER_ASSERT(reporter, !tmp.isOval(NULL)); 1429 1430 // since empty path being transformed. 1431 path.reset(); 1432 tmp.reset(); 1433 m.reset(); 1434 path.transform(m, &tmp); 1435 REPORTER_ASSERT(reporter, !tmp.isOval(NULL)); 1436 1437 // empty path is not an oval 1438 tmp.reset(); 1439 REPORTER_ASSERT(reporter, !tmp.isOval(NULL)); 1440 1441 // only has moveTo()s 1442 tmp.reset(); 1443 tmp.moveTo(0, 0); 1444 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10)); 1445 REPORTER_ASSERT(reporter, !tmp.isOval(NULL)); 1446 1447 // mimic WebKit's calling convention, 1448 // call moveTo() first and then call addOval() 1449 path.reset(); 1450 path.moveTo(0, 0); 1451 path.addOval(rect); 1452 REPORTER_ASSERT(reporter, path.isOval(NULL)); 1453 1454 // copy path 1455 path.reset(); 1456 tmp.reset(); 1457 tmp.addOval(rect); 1458 path = tmp; 1459 REPORTER_ASSERT(reporter, path.isOval(NULL)); 1460} 1461 1462static void TestPath(skiatest::Reporter* reporter) { 1463 { 1464 SkSize size; 1465 size.fWidth = 3.4f; 1466 size.width(); 1467 size = SkSize::Make(3,4); 1468 SkISize isize = SkISize::Make(3,4); 1469 } 1470 1471 SkTSize<SkScalar>::Make(3,4); 1472 1473 SkPath p, p2; 1474 SkRect bounds, bounds2; 1475 1476 REPORTER_ASSERT(reporter, p.isEmpty()); 1477 REPORTER_ASSERT(reporter, 0 == p.countPoints()); 1478 REPORTER_ASSERT(reporter, 0 == p.countVerbs()); 1479 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks()); 1480 REPORTER_ASSERT(reporter, p.isConvex()); 1481 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType); 1482 REPORTER_ASSERT(reporter, !p.isInverseFillType()); 1483 REPORTER_ASSERT(reporter, p == p2); 1484 REPORTER_ASSERT(reporter, !(p != p2)); 1485 1486 REPORTER_ASSERT(reporter, p.getBounds().isEmpty()); 1487 1488 bounds.set(0, 0, SK_Scalar1, SK_Scalar1); 1489 1490 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1); 1491 check_convex_bounds(reporter, p, bounds); 1492 // we have quads or cubics 1493 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask); 1494 REPORTER_ASSERT(reporter, !p.isEmpty()); 1495 1496 p.reset(); 1497 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks()); 1498 REPORTER_ASSERT(reporter, p.isEmpty()); 1499 1500 p.addOval(bounds); 1501 check_convex_bounds(reporter, p, bounds); 1502 REPORTER_ASSERT(reporter, !p.isEmpty()); 1503 1504 p.reset(); 1505 p.addRect(bounds); 1506 check_convex_bounds(reporter, p, bounds); 1507 // we have only lines 1508 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks()); 1509 REPORTER_ASSERT(reporter, !p.isEmpty()); 1510 1511 REPORTER_ASSERT(reporter, p != p2); 1512 REPORTER_ASSERT(reporter, !(p == p2)); 1513 1514 // do getPoints and getVerbs return the right result 1515 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4); 1516 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5); 1517 SkPoint pts[4]; 1518 int count = p.getPoints(pts, 4); 1519 REPORTER_ASSERT(reporter, count == 4); 1520 uint8_t verbs[6]; 1521 verbs[5] = 0xff; 1522 p.getVerbs(verbs, 5); 1523 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]); 1524 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]); 1525 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]); 1526 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]); 1527 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]); 1528 REPORTER_ASSERT(reporter, 0xff == verbs[5]); 1529 bounds2.set(pts, 4); 1530 REPORTER_ASSERT(reporter, bounds == bounds2); 1531 1532 bounds.offset(SK_Scalar1*3, SK_Scalar1*4); 1533 p.offset(SK_Scalar1*3, SK_Scalar1*4); 1534 REPORTER_ASSERT(reporter, bounds == p.getBounds()); 1535 1536 REPORTER_ASSERT(reporter, p.isRect(NULL)); 1537 bounds2.setEmpty(); 1538 REPORTER_ASSERT(reporter, p.isRect(&bounds2)); 1539 REPORTER_ASSERT(reporter, bounds == bounds2); 1540 1541 // now force p to not be a rect 1542 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2); 1543 p.addRect(bounds); 1544 REPORTER_ASSERT(reporter, !p.isRect(NULL)); 1545 1546 test_isLine(reporter); 1547 test_isRect(reporter); 1548 test_zero_length_paths(reporter); 1549 test_direction(reporter); 1550 test_convexity(reporter); 1551 test_convexity2(reporter); 1552 test_close(reporter); 1553 test_segment_masks(reporter); 1554 test_flattening(reporter); 1555 test_transform(reporter); 1556 test_bounds(reporter); 1557 test_iter(reporter); 1558 test_raw_iter(reporter); 1559 test_circle(reporter); 1560 test_oval(reporter); 1561 test_strokerec(reporter); 1562 test_addPoly(reporter); 1563 test_isfinite(reporter); 1564} 1565 1566#include "TestClassDef.h" 1567DEFINE_TESTCLASS("Path", PathTestClass, TestPath) 1568