SkPath.cpp revision 7cc7f494796afc681355b15e88c57ef2709a9220
1 2/* 3 * Copyright 2006 The Android Open Source Project 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 9 10#include "SkPath.h" 11#include "SkBuffer.h" 12#include "SkMath.h" 13#include "SkPathRef.h" 14#include "SkThread.h" 15 16 17//////////////////////////////////////////////////////////////////////////// 18 19#if SK_DEBUG_PATH_REF 20 21SkPath::PathRefDebugRef::PathRefDebugRef(SkPath* owner) : fOwner(owner) {} 22 23SkPath::PathRefDebugRef::PathRefDebugRef(SkPathRef* pr, SkPath* owner) 24: fPathRef(pr) 25, fOwner(owner) { 26 pr->addOwner(owner); 27} 28 29SkPath::PathRefDebugRef::~PathRefDebugRef() { 30 fPathRef->removeOwner(fOwner); 31} 32 33void SkPath::PathRefDebugRef::reset(SkPathRef* ref) { 34 bool diff = (ref != fPathRef.get()); 35 if (diff && NULL != fPathRef.get()) { 36 fPathRef.get()->removeOwner(fOwner); 37 } 38 fPathRef.reset(ref); 39 if (diff && NULL != fPathRef.get()) { 40 fPathRef.get()->addOwner(fOwner); 41 } 42} 43 44void SkPath::PathRefDebugRef::swap(SkPath::PathRefDebugRef* other) { 45 if (other->fPathRef.get() != fPathRef.get()) { 46 other->fPathRef->removeOwner(other->fOwner); 47 other->fPathRef->addOwner(fOwner); 48 49 fPathRef->removeOwner(fOwner); 50 fPathRef->addOwner(other->fOwner); 51 } 52 53 fPathRef.swap(&other->fPathRef); 54} 55 56SkPathRef* SkPath::PathRefDebugRef::get() const { return fPathRef.get(); } 57 58SkAutoTUnref<SkPathRef>::BlockRefType *SkPath::PathRefDebugRef::operator->() const { 59 return fPathRef.operator->(); 60} 61 62SkPath::PathRefDebugRef::operator SkPathRef*() { 63 return fPathRef.operator SkPathRef *(); 64} 65 66#endif 67 68//////////////////////////////////////////////////////////////////////////// 69 70 71SK_DEFINE_INST_COUNT(SkPath); 72 73// This value is just made-up for now. When count is 4, calling memset was much 74// slower than just writing the loop. This seems odd, and hopefully in the 75// future this we appear to have been a fluke... 76#define MIN_COUNT_FOR_MEMSET_TO_BE_FAST 16 77 78//////////////////////////////////////////////////////////////////////////// 79 80/** 81 * Path.bounds is defined to be the bounds of all the control points. 82 * If we called bounds.join(r) we would skip r if r was empty, which breaks 83 * our promise. Hence we have a custom joiner that doesn't look at emptiness 84 */ 85static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) { 86 dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft); 87 dst->fTop = SkMinScalar(dst->fTop, src.fTop); 88 dst->fRight = SkMaxScalar(dst->fRight, src.fRight); 89 dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom); 90} 91 92static bool is_degenerate(const SkPath& path) { 93 SkPath::Iter iter(path, false); 94 SkPoint pts[4]; 95 return SkPath::kDone_Verb == iter.next(pts); 96} 97 98class SkAutoDisableOvalCheck { 99public: 100 SkAutoDisableOvalCheck(SkPath* path) : fPath(path) { 101 fSaved = fPath->fIsOval; 102 } 103 104 ~SkAutoDisableOvalCheck() { 105 fPath->fIsOval = fSaved; 106 } 107 108private: 109 SkPath* fPath; 110 bool fSaved; 111}; 112 113/* This guy's constructor/destructor bracket a path editing operation. It is 114 used when we know the bounds of the amount we are going to add to the path 115 (usually a new contour, but not required). 116 117 It captures some state about the path up front (i.e. if it already has a 118 cached bounds), and the if it can, it updates the cache bounds explicitly, 119 avoiding the need to revisit all of the points in getBounds(). 120 121 It also notes if the path was originally degenerate, and if so, sets 122 isConvex to true. Thus it can only be used if the contour being added is 123 convex. 124 */ 125class SkAutoPathBoundsUpdate { 126public: 127 SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) { 128 this->init(path); 129 } 130 131 SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top, 132 SkScalar right, SkScalar bottom) { 133 fRect.set(left, top, right, bottom); 134 this->init(path); 135 } 136 137 ~SkAutoPathBoundsUpdate() { 138 fPath->setIsConvex(fDegenerate); 139 if (fEmpty) { 140 fPath->fBounds = fRect; 141 fPath->fBoundsIsDirty = false; 142 fPath->fIsFinite = fPath->fBounds.isFinite(); 143 } else if (!fDirty) { 144 joinNoEmptyChecks(&fPath->fBounds, fRect); 145 fPath->fBoundsIsDirty = false; 146 fPath->fIsFinite = fPath->fBounds.isFinite(); 147 } 148 } 149 150private: 151 SkPath* fPath; 152 SkRect fRect; 153 bool fDirty; 154 bool fDegenerate; 155 bool fEmpty; 156 157 // returns true if we should proceed 158 void init(SkPath* path) { 159 fPath = path; 160 fDirty = SkToBool(path->fBoundsIsDirty); 161 fDegenerate = is_degenerate(*path); 162 fEmpty = path->isEmpty(); 163 // Cannot use fRect for our bounds unless we know it is sorted 164 fRect.sort(); 165 } 166}; 167 168// Return true if the computed bounds are finite. 169static bool compute_pt_bounds(SkRect* bounds, const SkPathRef& ref) { 170 int count = ref.countPoints(); 171 if (count <= 1) { // we ignore just 1 point (moveto) 172 bounds->setEmpty(); 173 return count ? ref.points()->isFinite() : true; 174 } else { 175 return bounds->setBoundsCheck(ref.points(), count); 176 } 177} 178 179//////////////////////////////////////////////////////////////////////////// 180 181/* 182 Stores the verbs and points as they are given to us, with exceptions: 183 - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic 184 - we insert a Move(0,0) if Line | Quad | Cubic is our first command 185 186 The iterator does more cleanup, especially if forceClose == true 187 1. If we encounter degenerate segments, remove them 188 2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt) 189 3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2 190 4. if we encounter Line | Quad | Cubic after Close, cons up a Move 191*/ 192 193//////////////////////////////////////////////////////////////////////////// 194 195// flag to require a moveTo if we begin with something else, like lineTo etc. 196#define INITIAL_LASTMOVETOINDEX_VALUE ~0 197 198SkPath::SkPath() 199#if SK_DEBUG_PATH_REF 200 : fPathRef(SkPathRef::CreateEmpty(), this) 201#else 202 : fPathRef(SkPathRef::CreateEmpty()) 203#endif 204 , fFillType(kWinding_FillType) 205 , fBoundsIsDirty(true) { 206 fConvexity = kUnknown_Convexity; 207 fSegmentMask = 0; 208 fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE; 209 fIsOval = false; 210 fIsFinite = false; // gets computed when we know our bounds 211#ifdef SK_BUILD_FOR_ANDROID 212 fGenerationID = 0; 213 fSourcePath = NULL; 214#endif 215} 216 217SkPath::SkPath(const SkPath& src) 218#if SK_DEBUG_PATH_REF 219 : fPathRef(this) 220#endif 221{ 222 SkDEBUGCODE(src.validate();) 223 src.fPathRef.get()->ref(); 224 fPathRef.reset(src.fPathRef.get()); 225 fBounds = src.fBounds; 226 fFillType = src.fFillType; 227 fBoundsIsDirty = src.fBoundsIsDirty; 228 fConvexity = src.fConvexity; 229 fIsFinite = src.fIsFinite; 230 fSegmentMask = src.fSegmentMask; 231 fLastMoveToIndex = src.fLastMoveToIndex; 232 fIsOval = src.fIsOval; 233#ifdef SK_BUILD_FOR_ANDROID 234 fGenerationID = src.fGenerationID; 235 fSourcePath = NULL; 236#endif 237} 238 239SkPath::~SkPath() { 240 SkDEBUGCODE(this->validate();) 241} 242 243SkPath& SkPath::operator=(const SkPath& src) { 244 SkDEBUGCODE(src.validate();) 245 246 if (this != &src) { 247 src.fPathRef.get()->ref(); 248 fPathRef.reset(src.fPathRef.get()); 249 fBounds = src.fBounds; 250 fFillType = src.fFillType; 251 fBoundsIsDirty = src.fBoundsIsDirty; 252 fConvexity = src.fConvexity; 253 fIsFinite = src.fIsFinite; 254 fSegmentMask = src.fSegmentMask; 255 fLastMoveToIndex = src.fLastMoveToIndex; 256 fIsOval = src.fIsOval; 257 GEN_ID_INC; 258 } 259 SkDEBUGCODE(this->validate();) 260 return *this; 261} 262 263SK_API bool operator==(const SkPath& a, const SkPath& b) { 264 // note: don't need to look at isConvex or bounds, since just comparing the 265 // raw data is sufficient. 266 267 // We explicitly check fSegmentMask as a quick-reject. We could skip it, 268 // since it is only a cache of info in the fVerbs, but its a fast way to 269 // notice a difference 270 271 return &a == &b || 272 (a.fFillType == b.fFillType && a.fSegmentMask == b.fSegmentMask && 273 *a.fPathRef.get() == *b.fPathRef.get()); 274} 275 276void SkPath::swap(SkPath& other) { 277 SkASSERT(&other != NULL); 278 279 if (this != &other) { 280 SkTSwap<SkRect>(fBounds, other.fBounds); 281 fPathRef.swap(&other.fPathRef); 282 SkTSwap<uint8_t>(fFillType, other.fFillType); 283 SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty); 284 SkTSwap<uint8_t>(fConvexity, other.fConvexity); 285 SkTSwap<uint8_t>(fSegmentMask, other.fSegmentMask); 286 SkTSwap<int>(fLastMoveToIndex, other.fLastMoveToIndex); 287 SkTSwap<SkBool8>(fIsOval, other.fIsOval); 288 SkTSwap<SkBool8>(fIsFinite, other.fIsFinite); 289 GEN_ID_INC; 290 } 291} 292 293#ifdef SK_BUILD_FOR_ANDROID 294uint32_t SkPath::getGenerationID() const { 295 return fGenerationID; 296} 297 298const SkPath* SkPath::getSourcePath() const { 299 return fSourcePath; 300} 301 302void SkPath::setSourcePath(const SkPath* path) { 303 fSourcePath = path; 304} 305#endif 306 307void SkPath::reset() { 308 SkDEBUGCODE(this->validate();) 309 310 fPathRef.reset(SkPathRef::CreateEmpty()); 311 GEN_ID_INC; 312 fBoundsIsDirty = true; 313 fConvexity = kUnknown_Convexity; 314 fSegmentMask = 0; 315 fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE; 316 fIsOval = false; 317} 318 319void SkPath::rewind() { 320 SkDEBUGCODE(this->validate();) 321 322 SkPathRef::Rewind(&fPathRef); 323 GEN_ID_INC; 324 fConvexity = kUnknown_Convexity; 325 fBoundsIsDirty = true; 326 fSegmentMask = 0; 327 fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE; 328 fIsOval = false; 329} 330 331bool SkPath::isEmpty() const { 332 SkDEBUGCODE(this->validate();) 333 return 0 == fPathRef->countVerbs(); 334} 335 336bool SkPath::isLine(SkPoint line[2]) const { 337 int verbCount = fPathRef->countVerbs(); 338 int ptCount = fPathRef->countVerbs(); 339 340 if (2 == verbCount && 2 == ptCount) { 341 if (kMove_Verb == fPathRef->atVerb(0) && 342 kLine_Verb == fPathRef->atVerb(1)) { 343 if (line) { 344 const SkPoint* pts = fPathRef->points(); 345 line[0] = pts[0]; 346 line[1] = pts[1]; 347 } 348 return true; 349 } 350 } 351 return false; 352} 353 354/* 355 Determines if path is a rect by keeping track of changes in direction 356 and looking for a loop either clockwise or counterclockwise. 357 358 The direction is computed such that: 359 0: vertical up 360 1: horizontal right 361 2: vertical down 362 3: horizontal left 363 364A rectangle cycles up/right/down/left or up/left/down/right. 365 366The test fails if: 367 The path is closed, and followed by a line. 368 A second move creates a new endpoint. 369 A diagonal line is parsed. 370 There's more than four changes of direction. 371 There's a discontinuity on the line (e.g., a move in the middle) 372 The line reverses direction. 373 The rectangle doesn't complete a cycle. 374 The path contains a quadratic or cubic. 375 The path contains fewer than four points. 376 The final point isn't equal to the first point. 377 378It's OK if the path has: 379 Several colinear line segments composing a rectangle side. 380 Single points on the rectangle side. 381 382The direction takes advantage of the corners found since opposite sides 383must travel in opposite directions. 384 385FIXME: Allow colinear quads and cubics to be treated like lines. 386FIXME: If the API passes fill-only, return true if the filled stroke 387 is a rectangle, though the caller failed to close the path. 388 */ 389bool SkPath::isRect(SkRect* rect) const { 390 SkDEBUGCODE(this->validate();) 391 392 int corners = 0; 393 SkPoint first, last; 394 first.set(0, 0); 395 last.set(0, 0); 396 int firstDirection = 0; 397 int lastDirection = 0; 398 int nextDirection = 0; 399 bool closedOrMoved = false; 400 bool autoClose = false; 401 const SkPoint* pts = fPathRef->points(); 402 int verbCnt = fPathRef->countVerbs(); 403 int currVerb = 0; 404 while (currVerb < verbCnt) { 405 switch (fPathRef->atVerb(currVerb++)) { 406 case kClose_Verb: 407 pts = fPathRef->points(); 408 autoClose = true; 409 case kLine_Verb: { 410 SkScalar left = last.fX; 411 SkScalar top = last.fY; 412 SkScalar right = pts->fX; 413 SkScalar bottom = pts->fY; 414 ++pts; 415 if (left != right && top != bottom) { 416 return false; // diagonal 417 } 418 if (left == right && top == bottom) { 419 break; // single point on side OK 420 } 421 nextDirection = (left != right) << 0 | 422 (left < right || top < bottom) << 1; 423 if (0 == corners) { 424 firstDirection = nextDirection; 425 first = last; 426 last = pts[-1]; 427 corners = 1; 428 closedOrMoved = false; 429 break; 430 } 431 if (closedOrMoved) { 432 return false; // closed followed by a line 433 } 434 closedOrMoved = autoClose; 435 if (lastDirection != nextDirection) { 436 if (++corners > 4) { 437 return false; // too many direction changes 438 } 439 } 440 last = pts[-1]; 441 if (lastDirection == nextDirection) { 442 break; // colinear segment 443 } 444 // Possible values for corners are 2, 3, and 4. 445 // When corners == 3, nextDirection opposes firstDirection. 446 // Otherwise, nextDirection at corner 2 opposes corner 4. 447 int turn = firstDirection ^ (corners - 1); 448 int directionCycle = 3 == corners ? 0 : nextDirection ^ turn; 449 if ((directionCycle ^ turn) != nextDirection) { 450 return false; // direction didn't follow cycle 451 } 452 break; 453 } 454 case kQuad_Verb: 455 case kCubic_Verb: 456 return false; // quadratic, cubic not allowed 457 case kMove_Verb: 458 last = *pts++; 459 closedOrMoved = true; 460 break; 461 } 462 lastDirection = nextDirection; 463 } 464 // Success if 4 corners and first point equals last 465 bool result = 4 == corners && first == last; 466 if (result && rect) { 467 *rect = getBounds(); 468 } 469 return result; 470} 471 472int SkPath::countPoints() const { 473 return fPathRef->countPoints(); 474} 475 476int SkPath::getPoints(SkPoint dst[], int max) const { 477 SkDEBUGCODE(this->validate();) 478 479 SkASSERT(max >= 0); 480 SkASSERT(!max || dst); 481 int count = SkMin32(max, fPathRef->countPoints()); 482 memcpy(dst, fPathRef->points(), count * sizeof(SkPoint)); 483 return fPathRef->countPoints(); 484} 485 486SkPoint SkPath::getPoint(int index) const { 487 if ((unsigned)index < (unsigned)fPathRef->countPoints()) { 488 return fPathRef->atPoint(index); 489 } 490 return SkPoint::Make(0, 0); 491} 492 493int SkPath::countVerbs() const { 494 return fPathRef->countVerbs(); 495} 496 497static inline void copy_verbs_reverse(uint8_t* inorderDst, 498 const uint8_t* reversedSrc, 499 int count) { 500 for (int i = 0; i < count; ++i) { 501 inorderDst[i] = reversedSrc[~i]; 502 } 503} 504 505int SkPath::getVerbs(uint8_t dst[], int max) const { 506 SkDEBUGCODE(this->validate();) 507 508 SkASSERT(max >= 0); 509 SkASSERT(!max || dst); 510 int count = SkMin32(max, fPathRef->countVerbs()); 511 copy_verbs_reverse(dst, fPathRef->verbs(), count); 512 return fPathRef->countVerbs(); 513} 514 515bool SkPath::getLastPt(SkPoint* lastPt) const { 516 SkDEBUGCODE(this->validate();) 517 518 int count = fPathRef->countPoints(); 519 if (count > 0) { 520 if (lastPt) { 521 *lastPt = fPathRef->atPoint(count - 1); 522 } 523 return true; 524 } 525 if (lastPt) { 526 lastPt->set(0, 0); 527 } 528 return false; 529} 530 531void SkPath::setLastPt(SkScalar x, SkScalar y) { 532 SkDEBUGCODE(this->validate();) 533 534 int count = fPathRef->countPoints(); 535 if (count == 0) { 536 this->moveTo(x, y); 537 } else { 538 fIsOval = false; 539 SkPathRef::Editor ed(&fPathRef); 540 ed.atPoint(count-1)->set(x, y); 541 GEN_ID_INC; 542 } 543} 544 545void SkPath::computeBounds() const { 546 SkDEBUGCODE(this->validate();) 547 SkASSERT(fBoundsIsDirty); 548 549 fIsFinite = compute_pt_bounds(&fBounds, *fPathRef.get()); 550 fBoundsIsDirty = false; 551} 552 553void SkPath::setConvexity(Convexity c) { 554 if (fConvexity != c) { 555 fConvexity = c; 556 GEN_ID_INC; 557 } 558} 559 560////////////////////////////////////////////////////////////////////////////// 561// Construction methods 562 563#define DIRTY_AFTER_EDIT \ 564 do { \ 565 fBoundsIsDirty = true; \ 566 fConvexity = kUnknown_Convexity; \ 567 fIsOval = false; \ 568 } while (0) 569 570#define DIRTY_AFTER_EDIT_NO_CONVEXITY_CHANGE \ 571 do { \ 572 fBoundsIsDirty = true; \ 573 } while (0) 574 575void SkPath::incReserve(U16CPU inc) { 576 SkDEBUGCODE(this->validate();) 577 SkPathRef::Editor(&fPathRef, inc, inc); 578 SkDEBUGCODE(this->validate();) 579} 580 581void SkPath::moveTo(SkScalar x, SkScalar y) { 582 SkDEBUGCODE(this->validate();) 583 584 SkPathRef::Editor ed(&fPathRef); 585 586 // remember our index 587 fLastMoveToIndex = ed.pathRef()->countPoints(); 588 589 ed.growForVerb(kMove_Verb)->set(x, y); 590 591 GEN_ID_INC; 592 DIRTY_AFTER_EDIT_NO_CONVEXITY_CHANGE; 593} 594 595void SkPath::rMoveTo(SkScalar x, SkScalar y) { 596 SkPoint pt; 597 this->getLastPt(&pt); 598 this->moveTo(pt.fX + x, pt.fY + y); 599} 600 601void SkPath::injectMoveToIfNeeded() { 602 if (fLastMoveToIndex < 0) { 603 SkScalar x, y; 604 if (fPathRef->countVerbs() == 0) { 605 x = y = 0; 606 } else { 607 const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex); 608 x = pt.fX; 609 y = pt.fY; 610 } 611 this->moveTo(x, y); 612 } 613} 614 615void SkPath::lineTo(SkScalar x, SkScalar y) { 616 SkDEBUGCODE(this->validate();) 617 618 this->injectMoveToIfNeeded(); 619 620 SkPathRef::Editor ed(&fPathRef); 621 ed.growForVerb(kLine_Verb)->set(x, y); 622 fSegmentMask |= kLine_SegmentMask; 623 624 GEN_ID_INC; 625 DIRTY_AFTER_EDIT; 626} 627 628void SkPath::rLineTo(SkScalar x, SkScalar y) { 629 SkPoint pt; 630 this->getLastPt(&pt); 631 this->lineTo(pt.fX + x, pt.fY + y); 632} 633 634void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { 635 SkDEBUGCODE(this->validate();) 636 637 this->injectMoveToIfNeeded(); 638 639 SkPathRef::Editor ed(&fPathRef); 640 SkPoint* pts = ed.growForVerb(kQuad_Verb); 641 pts[0].set(x1, y1); 642 pts[1].set(x2, y2); 643 fSegmentMask |= kQuad_SegmentMask; 644 645 GEN_ID_INC; 646 DIRTY_AFTER_EDIT; 647} 648 649void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { 650 SkPoint pt; 651 this->getLastPt(&pt); 652 this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2); 653} 654 655void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 656 SkScalar x3, SkScalar y3) { 657 SkDEBUGCODE(this->validate();) 658 659 this->injectMoveToIfNeeded(); 660 661 SkPathRef::Editor ed(&fPathRef); 662 SkPoint* pts = ed.growForVerb(kCubic_Verb); 663 pts[0].set(x1, y1); 664 pts[1].set(x2, y2); 665 pts[2].set(x3, y3); 666 fSegmentMask |= kCubic_SegmentMask; 667 668 GEN_ID_INC; 669 DIRTY_AFTER_EDIT; 670} 671 672void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 673 SkScalar x3, SkScalar y3) { 674 SkPoint pt; 675 this->getLastPt(&pt); 676 this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2, 677 pt.fX + x3, pt.fY + y3); 678} 679 680void SkPath::close() { 681 SkDEBUGCODE(this->validate();) 682 683 int count = fPathRef->countVerbs(); 684 if (count > 0) { 685 switch (fPathRef->atVerb(count - 1)) { 686 case kLine_Verb: 687 case kQuad_Verb: 688 case kCubic_Verb: 689 case kMove_Verb: { 690 SkPathRef::Editor ed(&fPathRef); 691 ed.growForVerb(kClose_Verb); 692 GEN_ID_INC; 693 break; 694 } 695 default: 696 // don't add a close if it's the first verb or a repeat 697 break; 698 } 699 } 700 701 // signal that we need a moveTo to follow us (unless we're done) 702#if 0 703 if (fLastMoveToIndex >= 0) { 704 fLastMoveToIndex = ~fLastMoveToIndex; 705 } 706#else 707 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1); 708#endif 709} 710 711/////////////////////////////////////////////////////////////////////////////// 712 713void SkPath::addRect(const SkRect& rect, Direction dir) { 714 this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir); 715} 716 717void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right, 718 SkScalar bottom, Direction dir) { 719 SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom); 720 721 this->incReserve(5); 722 723 this->moveTo(left, top); 724 if (dir == kCCW_Direction) { 725 this->lineTo(left, bottom); 726 this->lineTo(right, bottom); 727 this->lineTo(right, top); 728 } else { 729 this->lineTo(right, top); 730 this->lineTo(right, bottom); 731 this->lineTo(left, bottom); 732 } 733 this->close(); 734} 735 736void SkPath::addPoly(const SkPoint pts[], int count, bool close) { 737 SkDEBUGCODE(this->validate();) 738 if (count <= 0) { 739 return; 740 } 741 742 SkPathRef::Editor ed(&fPathRef); 743 fLastMoveToIndex = ed.pathRef()->countPoints(); 744 uint8_t* vb; 745 SkPoint* p; 746 // +close makes room for the extra kClose_Verb 747 ed.grow(count + close, count, &vb, &p); 748 749 memcpy(p, pts, count * sizeof(SkPoint)); 750 vb[~0] = kMove_Verb; 751 if (count > 1) { 752 // cast to unsigned, so if MIN_COUNT_FOR_MEMSET_TO_BE_FAST is defined to 753 // be 0, the compiler will remove the test/branch entirely. 754 if ((unsigned)count >= MIN_COUNT_FOR_MEMSET_TO_BE_FAST) { 755 memset(vb - count, kLine_Verb, count - 1); 756 } else { 757 for (int i = 1; i < count; ++i) { 758 vb[~i] = kLine_Verb; 759 } 760 } 761 fSegmentMask |= kLine_SegmentMask; 762 } 763 if (close) { 764 vb[~count] = kClose_Verb; 765 } 766 767 GEN_ID_INC; 768 DIRTY_AFTER_EDIT; 769 SkDEBUGCODE(this->validate();) 770} 771 772#define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3) 773 774void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, 775 Direction dir) { 776 SkScalar w = rect.width(); 777 SkScalar halfW = SkScalarHalf(w); 778 SkScalar h = rect.height(); 779 SkScalar halfH = SkScalarHalf(h); 780 781 if (halfW <= 0 || halfH <= 0) { 782 return; 783 } 784 785 bool skip_hori = rx >= halfW; 786 bool skip_vert = ry >= halfH; 787 788 if (skip_hori && skip_vert) { 789 this->addOval(rect, dir); 790 return; 791 } 792 793 SkAutoPathBoundsUpdate apbu(this, rect); 794 795 if (skip_hori) { 796 rx = halfW; 797 } else if (skip_vert) { 798 ry = halfH; 799 } 800 801 SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); 802 SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); 803 804 this->incReserve(17); 805 this->moveTo(rect.fRight - rx, rect.fTop); 806 if (dir == kCCW_Direction) { 807 if (!skip_hori) { 808 this->lineTo(rect.fLeft + rx, rect.fTop); // top 809 } 810 this->cubicTo(rect.fLeft + rx - sx, rect.fTop, 811 rect.fLeft, rect.fTop + ry - sy, 812 rect.fLeft, rect.fTop + ry); // top-left 813 if (!skip_vert) { 814 this->lineTo(rect.fLeft, rect.fBottom - ry); // left 815 } 816 this->cubicTo(rect.fLeft, rect.fBottom - ry + sy, 817 rect.fLeft + rx - sx, rect.fBottom, 818 rect.fLeft + rx, rect.fBottom); // bot-left 819 if (!skip_hori) { 820 this->lineTo(rect.fRight - rx, rect.fBottom); // bottom 821 } 822 this->cubicTo(rect.fRight - rx + sx, rect.fBottom, 823 rect.fRight, rect.fBottom - ry + sy, 824 rect.fRight, rect.fBottom - ry); // bot-right 825 if (!skip_vert) { 826 this->lineTo(rect.fRight, rect.fTop + ry); 827 } 828 this->cubicTo(rect.fRight, rect.fTop + ry - sy, 829 rect.fRight - rx + sx, rect.fTop, 830 rect.fRight - rx, rect.fTop); // top-right 831 } else { 832 this->cubicTo(rect.fRight - rx + sx, rect.fTop, 833 rect.fRight, rect.fTop + ry - sy, 834 rect.fRight, rect.fTop + ry); // top-right 835 if (!skip_vert) { 836 this->lineTo(rect.fRight, rect.fBottom - ry); 837 } 838 this->cubicTo(rect.fRight, rect.fBottom - ry + sy, 839 rect.fRight - rx + sx, rect.fBottom, 840 rect.fRight - rx, rect.fBottom); // bot-right 841 if (!skip_hori) { 842 this->lineTo(rect.fLeft + rx, rect.fBottom); // bottom 843 } 844 this->cubicTo(rect.fLeft + rx - sx, rect.fBottom, 845 rect.fLeft, rect.fBottom - ry + sy, 846 rect.fLeft, rect.fBottom - ry); // bot-left 847 if (!skip_vert) { 848 this->lineTo(rect.fLeft, rect.fTop + ry); // left 849 } 850 this->cubicTo(rect.fLeft, rect.fTop + ry - sy, 851 rect.fLeft + rx - sx, rect.fTop, 852 rect.fLeft + rx, rect.fTop); // top-left 853 if (!skip_hori) { 854 this->lineTo(rect.fRight - rx, rect.fTop); // top 855 } 856 } 857 this->close(); 858} 859 860static void add_corner_arc(SkPath* path, const SkRect& rect, 861 SkScalar rx, SkScalar ry, int startAngle, 862 SkPath::Direction dir, bool forceMoveTo) { 863 rx = SkMinScalar(SkScalarHalf(rect.width()), rx); 864 ry = SkMinScalar(SkScalarHalf(rect.height()), ry); 865 866 SkRect r; 867 r.set(-rx, -ry, rx, ry); 868 869 switch (startAngle) { 870 case 0: 871 r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom); 872 break; 873 case 90: 874 r.offset(rect.fLeft - r.fLeft, rect.fBottom - r.fBottom); 875 break; 876 case 180: r.offset(rect.fLeft - r.fLeft, rect.fTop - r.fTop); break; 877 case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break; 878 default: SkDEBUGFAIL("unexpected startAngle in add_corner_arc"); 879 } 880 881 SkScalar start = SkIntToScalar(startAngle); 882 SkScalar sweep = SkIntToScalar(90); 883 if (SkPath::kCCW_Direction == dir) { 884 start += sweep; 885 sweep = -sweep; 886 } 887 888 path->arcTo(r, start, sweep, forceMoveTo); 889} 890 891void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[], 892 Direction dir) { 893 // abort before we invoke SkAutoPathBoundsUpdate() 894 if (rect.isEmpty()) { 895 return; 896 } 897 898 SkAutoPathBoundsUpdate apbu(this, rect); 899 900 if (kCW_Direction == dir) { 901 add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true); 902 add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false); 903 add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false); 904 add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false); 905 } else { 906 add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true); 907 add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false); 908 add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false); 909 add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false); 910 } 911 this->close(); 912} 913 914bool SkPath::hasOnlyMoveTos() const { 915 int count = fPathRef->countVerbs(); 916 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin(); 917 for (int i = 0; i < count; ++i) { 918 if (*verbs == kLine_Verb || 919 *verbs == kQuad_Verb || 920 *verbs == kCubic_Verb) { 921 return false; 922 } 923 ++verbs; 924 } 925 return true; 926} 927 928void SkPath::addOval(const SkRect& oval, Direction dir) { 929 /* If addOval() is called after previous moveTo(), 930 this path is still marked as an oval. This is used to 931 fit into WebKit's calling sequences. 932 We can't simply check isEmpty() in this case, as additional 933 moveTo() would mark the path non empty. 934 */ 935 fIsOval = hasOnlyMoveTos(); 936 937 SkAutoDisableOvalCheck adoc(this); 938 939 SkAutoPathBoundsUpdate apbu(this, oval); 940 941 SkScalar cx = oval.centerX(); 942 SkScalar cy = oval.centerY(); 943 SkScalar rx = SkScalarHalf(oval.width()); 944 SkScalar ry = SkScalarHalf(oval.height()); 945#if 0 // these seem faster than using quads (1/2 the number of edges) 946 SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); 947 SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); 948 949 this->incReserve(13); 950 this->moveTo(cx + rx, cy); 951 if (dir == kCCW_Direction) { 952 this->cubicTo(cx + rx, cy - sy, cx + sx, cy - ry, cx, cy - ry); 953 this->cubicTo(cx - sx, cy - ry, cx - rx, cy - sy, cx - rx, cy); 954 this->cubicTo(cx - rx, cy + sy, cx - sx, cy + ry, cx, cy + ry); 955 this->cubicTo(cx + sx, cy + ry, cx + rx, cy + sy, cx + rx, cy); 956 } else { 957 this->cubicTo(cx + rx, cy + sy, cx + sx, cy + ry, cx, cy + ry); 958 this->cubicTo(cx - sx, cy + ry, cx - rx, cy + sy, cx - rx, cy); 959 this->cubicTo(cx - rx, cy - sy, cx - sx, cy - ry, cx, cy - ry); 960 this->cubicTo(cx + sx, cy - ry, cx + rx, cy - sy, cx + rx, cy); 961 } 962#else 963 SkScalar sx = SkScalarMul(rx, SK_ScalarTanPIOver8); 964 SkScalar sy = SkScalarMul(ry, SK_ScalarTanPIOver8); 965 SkScalar mx = SkScalarMul(rx, SK_ScalarRoot2Over2); 966 SkScalar my = SkScalarMul(ry, SK_ScalarRoot2Over2); 967 968 /* 969 To handle imprecision in computing the center and radii, we revert to 970 the provided bounds when we can (i.e. use oval.fLeft instead of cx-rx) 971 to ensure that we don't exceed the oval's bounds *ever*, since we want 972 to use oval for our fast-bounds, rather than have to recompute it. 973 */ 974 const SkScalar L = oval.fLeft; // cx - rx 975 const SkScalar T = oval.fTop; // cy - ry 976 const SkScalar R = oval.fRight; // cx + rx 977 const SkScalar B = oval.fBottom; // cy + ry 978 979 this->incReserve(17); // 8 quads + close 980 this->moveTo(R, cy); 981 if (dir == kCCW_Direction) { 982 this->quadTo( R, cy - sy, cx + mx, cy - my); 983 this->quadTo(cx + sx, T, cx , T); 984 this->quadTo(cx - sx, T, cx - mx, cy - my); 985 this->quadTo( L, cy - sy, L, cy ); 986 this->quadTo( L, cy + sy, cx - mx, cy + my); 987 this->quadTo(cx - sx, B, cx , B); 988 this->quadTo(cx + sx, B, cx + mx, cy + my); 989 this->quadTo( R, cy + sy, R, cy ); 990 } else { 991 this->quadTo( R, cy + sy, cx + mx, cy + my); 992 this->quadTo(cx + sx, B, cx , B); 993 this->quadTo(cx - sx, B, cx - mx, cy + my); 994 this->quadTo( L, cy + sy, L, cy ); 995 this->quadTo( L, cy - sy, cx - mx, cy - my); 996 this->quadTo(cx - sx, T, cx , T); 997 this->quadTo(cx + sx, T, cx + mx, cy - my); 998 this->quadTo( R, cy - sy, R, cy ); 999 } 1000#endif 1001 this->close(); 1002} 1003 1004bool SkPath::isOval(SkRect* rect) const { 1005 if (fIsOval && rect) { 1006 *rect = getBounds(); 1007 } 1008 1009 return fIsOval; 1010} 1011 1012void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) { 1013 if (r > 0) { 1014 SkRect rect; 1015 rect.set(x - r, y - r, x + r, y + r); 1016 this->addOval(rect, dir); 1017 } 1018} 1019 1020#include "SkGeometry.h" 1021 1022static int build_arc_points(const SkRect& oval, SkScalar startAngle, 1023 SkScalar sweepAngle, 1024 SkPoint pts[kSkBuildQuadArcStorage]) { 1025 1026 if (0 == sweepAngle && 1027 (0 == startAngle || SkIntToScalar(360) == startAngle)) { 1028 // Chrome uses this path to move into and out of ovals. If not 1029 // treated as a special case the moves can distort the oval's 1030 // bounding box (and break the circle special case). 1031 pts[0].set(oval.fRight, oval.centerY()); 1032 return 1; 1033 } 1034 1035 SkVector start, stop; 1036 1037 start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX); 1038 stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle), 1039 &stop.fX); 1040 1041 /* If the sweep angle is nearly (but less than) 360, then due to precision 1042 loss in radians-conversion and/or sin/cos, we may end up with coincident 1043 vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead 1044 of drawing a nearly complete circle (good). 1045 e.g. canvas.drawArc(0, 359.99, ...) 1046 -vs- canvas.drawArc(0, 359.9, ...) 1047 We try to detect this edge case, and tweak the stop vector 1048 */ 1049 if (start == stop) { 1050 SkScalar sw = SkScalarAbs(sweepAngle); 1051 if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) { 1052 SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle); 1053 // make a guess at a tiny angle (in radians) to tweak by 1054 SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle); 1055 // not sure how much will be enough, so we use a loop 1056 do { 1057 stopRad -= deltaRad; 1058 stop.fY = SkScalarSinCos(stopRad, &stop.fX); 1059 } while (start == stop); 1060 } 1061 } 1062 1063 SkMatrix matrix; 1064 1065 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height())); 1066 matrix.postTranslate(oval.centerX(), oval.centerY()); 1067 1068 return SkBuildQuadArc(start, stop, 1069 sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection, 1070 &matrix, pts); 1071} 1072 1073void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, 1074 bool forceMoveTo) { 1075 if (oval.width() < 0 || oval.height() < 0) { 1076 return; 1077 } 1078 1079 SkPoint pts[kSkBuildQuadArcStorage]; 1080 int count = build_arc_points(oval, startAngle, sweepAngle, pts); 1081 SkASSERT((count & 1) == 1); 1082 1083 if (fPathRef->countVerbs() == 0) { 1084 forceMoveTo = true; 1085 } 1086 this->incReserve(count); 1087 forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]); 1088 for (int i = 1; i < count; i += 2) { 1089 this->quadTo(pts[i], pts[i+1]); 1090 } 1091} 1092 1093void SkPath::addArc(const SkRect& oval, SkScalar startAngle, 1094 SkScalar sweepAngle) { 1095 if (oval.isEmpty() || 0 == sweepAngle) { 1096 return; 1097 } 1098 1099 const SkScalar kFullCircleAngle = SkIntToScalar(360); 1100 1101 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) { 1102 this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction); 1103 return; 1104 } 1105 1106 SkPoint pts[kSkBuildQuadArcStorage]; 1107 int count = build_arc_points(oval, startAngle, sweepAngle, pts); 1108 1109 this->incReserve(count); 1110 this->moveTo(pts[0]); 1111 for (int i = 1; i < count; i += 2) { 1112 this->quadTo(pts[i], pts[i+1]); 1113 } 1114} 1115 1116/* 1117 Need to handle the case when the angle is sharp, and our computed end-points 1118 for the arc go behind pt1 and/or p2... 1119*/ 1120void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 1121 SkScalar radius) { 1122 SkVector before, after; 1123 1124 // need to know our prev pt so we can construct tangent vectors 1125 { 1126 SkPoint start; 1127 this->getLastPt(&start); 1128 // Handle degenerate cases by adding a line to the first point and 1129 // bailing out. 1130 if ((x1 == start.fX && y1 == start.fY) || 1131 (x1 == x2 && y1 == y2) || 1132 radius == 0) { 1133 this->lineTo(x1, y1); 1134 return; 1135 } 1136 before.setNormalize(x1 - start.fX, y1 - start.fY); 1137 after.setNormalize(x2 - x1, y2 - y1); 1138 } 1139 1140 SkScalar cosh = SkPoint::DotProduct(before, after); 1141 SkScalar sinh = SkPoint::CrossProduct(before, after); 1142 1143 if (SkScalarNearlyZero(sinh)) { // angle is too tight 1144 this->lineTo(x1, y1); 1145 return; 1146 } 1147 1148 SkScalar dist = SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh); 1149 if (dist < 0) { 1150 dist = -dist; 1151 } 1152 1153 SkScalar xx = x1 - SkScalarMul(dist, before.fX); 1154 SkScalar yy = y1 - SkScalarMul(dist, before.fY); 1155 SkRotationDirection arcDir; 1156 1157 // now turn before/after into normals 1158 if (sinh > 0) { 1159 before.rotateCCW(); 1160 after.rotateCCW(); 1161 arcDir = kCW_SkRotationDirection; 1162 } else { 1163 before.rotateCW(); 1164 after.rotateCW(); 1165 arcDir = kCCW_SkRotationDirection; 1166 } 1167 1168 SkMatrix matrix; 1169 SkPoint pts[kSkBuildQuadArcStorage]; 1170 1171 matrix.setScale(radius, radius); 1172 matrix.postTranslate(xx - SkScalarMul(radius, before.fX), 1173 yy - SkScalarMul(radius, before.fY)); 1174 1175 int count = SkBuildQuadArc(before, after, arcDir, &matrix, pts); 1176 1177 this->incReserve(count); 1178 // [xx,yy] == pts[0] 1179 this->lineTo(xx, yy); 1180 for (int i = 1; i < count; i += 2) { 1181 this->quadTo(pts[i], pts[i+1]); 1182 } 1183} 1184 1185/////////////////////////////////////////////////////////////////////////////// 1186 1187void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) { 1188 SkMatrix matrix; 1189 1190 matrix.setTranslate(dx, dy); 1191 this->addPath(path, matrix); 1192} 1193 1194void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) { 1195 SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints()); 1196 1197 fIsOval = false; 1198 1199 RawIter iter(path); 1200 SkPoint pts[4]; 1201 Verb verb; 1202 1203 SkMatrix::MapPtsProc proc = matrix.getMapPtsProc(); 1204 1205 while ((verb = iter.next(pts)) != kDone_Verb) { 1206 switch (verb) { 1207 case kMove_Verb: 1208 proc(matrix, &pts[0], &pts[0], 1); 1209 this->moveTo(pts[0]); 1210 break; 1211 case kLine_Verb: 1212 proc(matrix, &pts[1], &pts[1], 1); 1213 this->lineTo(pts[1]); 1214 break; 1215 case kQuad_Verb: 1216 proc(matrix, &pts[1], &pts[1], 2); 1217 this->quadTo(pts[1], pts[2]); 1218 break; 1219 case kCubic_Verb: 1220 proc(matrix, &pts[1], &pts[1], 3); 1221 this->cubicTo(pts[1], pts[2], pts[3]); 1222 break; 1223 case kClose_Verb: 1224 this->close(); 1225 break; 1226 default: 1227 SkDEBUGFAIL("unknown verb"); 1228 } 1229 } 1230} 1231 1232/////////////////////////////////////////////////////////////////////////////// 1233 1234static const uint8_t gPtsInVerb[] = { 1235 1, // kMove 1236 1, // kLine 1237 2, // kQuad 1238 3, // kCubic 1239 0, // kClose 1240 0 // kDone 1241}; 1242 1243// ignore the initial moveto, and stop when the 1st contour ends 1244void SkPath::pathTo(const SkPath& path) { 1245 int i, vcount = path.fPathRef->countVerbs(); 1246 // exit early if the path is empty, or just has a moveTo. 1247 if (vcount < 2) { 1248 return; 1249 } 1250 1251 SkPathRef::Editor(&fPathRef, vcount, path.countPoints()); 1252 1253 fIsOval = false; 1254 1255 const uint8_t* verbs = path.fPathRef->verbs(); 1256 // skip the initial moveTo 1257 const SkPoint* pts = path.fPathRef->points() + 1; 1258 1259 SkASSERT(verbs[~0] == kMove_Verb); 1260 for (i = 1; i < vcount; i++) { 1261 switch (verbs[~i]) { 1262 case kLine_Verb: 1263 this->lineTo(pts[0].fX, pts[0].fY); 1264 break; 1265 case kQuad_Verb: 1266 this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY); 1267 break; 1268 case kCubic_Verb: 1269 this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY); 1270 break; 1271 case kClose_Verb: 1272 return; 1273 } 1274 pts += gPtsInVerb[verbs[~i]]; 1275 } 1276} 1277 1278// ignore the last point of the 1st contour 1279void SkPath::reversePathTo(const SkPath& path) { 1280 int i, vcount = path.fPathRef->countVerbs(); 1281 // exit early if the path is empty, or just has a moveTo. 1282 if (vcount < 2) { 1283 return; 1284 } 1285 1286 SkPathRef::Editor(&fPathRef, vcount, path.countPoints()); 1287 1288 fIsOval = false; 1289 1290 const uint8_t* verbs = path.fPathRef->verbs(); 1291 const SkPoint* pts = path.fPathRef->points(); 1292 1293 SkASSERT(verbs[~0] == kMove_Verb); 1294 for (i = 1; i < vcount; ++i) { 1295 int n = gPtsInVerb[verbs[~i]]; 1296 if (n == 0) { 1297 break; 1298 } 1299 pts += n; 1300 } 1301 1302 while (--i > 0) { 1303 switch (verbs[~i]) { 1304 case kLine_Verb: 1305 this->lineTo(pts[-1].fX, pts[-1].fY); 1306 break; 1307 case kQuad_Verb: 1308 this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY); 1309 break; 1310 case kCubic_Verb: 1311 this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY, 1312 pts[-3].fX, pts[-3].fY); 1313 break; 1314 default: 1315 SkDEBUGFAIL("bad verb"); 1316 break; 1317 } 1318 pts -= gPtsInVerb[verbs[~i]]; 1319 } 1320} 1321 1322void SkPath::reverseAddPath(const SkPath& src) { 1323 SkPathRef::Editor ed(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->countVerbs()); 1324 1325 const SkPoint* pts = src.fPathRef->pointsEnd(); 1326 // we will iterator through src's verbs backwards 1327 const uint8_t* verbs = src.fPathRef->verbsMemBegin(); // points at the last verb 1328 const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the first verb 1329 1330 fIsOval = false; 1331 1332 bool needMove = true; 1333 bool needClose = false; 1334 while (verbs < verbsEnd) { 1335 uint8_t v = *(verbs++); 1336 int n = gPtsInVerb[v]; 1337 1338 if (needMove) { 1339 --pts; 1340 this->moveTo(pts->fX, pts->fY); 1341 needMove = false; 1342 } 1343 pts -= n; 1344 switch (v) { 1345 case kMove_Verb: 1346 if (needClose) { 1347 this->close(); 1348 needClose = false; 1349 } 1350 needMove = true; 1351 pts += 1; // so we see the point in "if (needMove)" above 1352 break; 1353 case kLine_Verb: 1354 this->lineTo(pts[0]); 1355 break; 1356 case kQuad_Verb: 1357 this->quadTo(pts[1], pts[0]); 1358 break; 1359 case kCubic_Verb: 1360 this->cubicTo(pts[2], pts[1], pts[0]); 1361 break; 1362 case kClose_Verb: 1363 needClose = true; 1364 break; 1365 default: 1366 SkASSERT(!"unexpected verb"); 1367 } 1368 } 1369} 1370 1371/////////////////////////////////////////////////////////////////////////////// 1372 1373void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const { 1374 SkMatrix matrix; 1375 1376 matrix.setTranslate(dx, dy); 1377 this->transform(matrix, dst); 1378} 1379 1380#include "SkGeometry.h" 1381 1382static void subdivide_quad_to(SkPath* path, const SkPoint pts[3], 1383 int level = 2) { 1384 if (--level >= 0) { 1385 SkPoint tmp[5]; 1386 1387 SkChopQuadAtHalf(pts, tmp); 1388 subdivide_quad_to(path, &tmp[0], level); 1389 subdivide_quad_to(path, &tmp[2], level); 1390 } else { 1391 path->quadTo(pts[1], pts[2]); 1392 } 1393} 1394 1395static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4], 1396 int level = 2) { 1397 if (--level >= 0) { 1398 SkPoint tmp[7]; 1399 1400 SkChopCubicAtHalf(pts, tmp); 1401 subdivide_cubic_to(path, &tmp[0], level); 1402 subdivide_cubic_to(path, &tmp[3], level); 1403 } else { 1404 path->cubicTo(pts[1], pts[2], pts[3]); 1405 } 1406} 1407 1408void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { 1409 SkDEBUGCODE(this->validate();) 1410 if (dst == NULL) { 1411 dst = (SkPath*)this; 1412 } 1413 1414 if (matrix.hasPerspective()) { 1415 SkPath tmp; 1416 tmp.fFillType = fFillType; 1417 1418 SkPath::Iter iter(*this, false); 1419 SkPoint pts[4]; 1420 SkPath::Verb verb; 1421 1422 while ((verb = iter.next(pts, false)) != kDone_Verb) { 1423 switch (verb) { 1424 case kMove_Verb: 1425 tmp.moveTo(pts[0]); 1426 break; 1427 case kLine_Verb: 1428 tmp.lineTo(pts[1]); 1429 break; 1430 case kQuad_Verb: 1431 subdivide_quad_to(&tmp, pts); 1432 break; 1433 case kCubic_Verb: 1434 subdivide_cubic_to(&tmp, pts); 1435 break; 1436 case kClose_Verb: 1437 tmp.close(); 1438 break; 1439 default: 1440 SkDEBUGFAIL("unknown verb"); 1441 break; 1442 } 1443 } 1444 1445 dst->swap(tmp); 1446 SkPathRef::Editor ed(&dst->fPathRef); 1447 matrix.mapPoints(ed.points(), ed.pathRef()->countPoints()); 1448 } else { 1449 /* 1450 * If we're not in perspective, we can transform all of the points at 1451 * once. 1452 * 1453 * Here we also want to optimize bounds, by noting if the bounds are 1454 * already known, and if so, we just transform those as well and mark 1455 * them as "known", rather than force the transformed path to have to 1456 * recompute them. 1457 * 1458 * Special gotchas if the path is effectively empty (<= 1 point) or 1459 * if it is non-finite. In those cases bounds need to stay empty, 1460 * regardless of the matrix. 1461 */ 1462 if (!fBoundsIsDirty && matrix.rectStaysRect() && fPathRef->countPoints() > 1) { 1463 dst->fBoundsIsDirty = false; 1464 if (fIsFinite) { 1465 matrix.mapRect(&dst->fBounds, fBounds); 1466 if (!(dst->fIsFinite = dst->fBounds.isFinite())) { 1467 dst->fBounds.setEmpty(); 1468 } 1469 } else { 1470 dst->fIsFinite = false; 1471 dst->fBounds.setEmpty(); 1472 } 1473 } else { 1474 GEN_ID_PTR_INC(dst); 1475 dst->fBoundsIsDirty = true; 1476 } 1477 1478 SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix); 1479 1480 if (this != dst) { 1481 dst->fFillType = fFillType; 1482 dst->fSegmentMask = fSegmentMask; 1483 dst->fConvexity = fConvexity; 1484 } 1485 1486 // It's an oval only if it stays a rect. 1487 dst->fIsOval = fIsOval && matrix.rectStaysRect(); 1488 1489 SkDEBUGCODE(dst->validate();) 1490 } 1491} 1492 1493/////////////////////////////////////////////////////////////////////////////// 1494/////////////////////////////////////////////////////////////////////////////// 1495 1496enum SegmentState { 1497 kEmptyContour_SegmentState, // The current contour is empty. We may be 1498 // starting processing or we may have just 1499 // closed a contour. 1500 kAfterMove_SegmentState, // We have seen a move, but nothing else. 1501 kAfterPrimitive_SegmentState // We have seen a primitive but not yet 1502 // closed the path. Also the initial state. 1503}; 1504 1505SkPath::Iter::Iter() { 1506#ifdef SK_DEBUG 1507 fPts = NULL; 1508 fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0; 1509 fForceClose = fCloseLine = false; 1510 fSegmentState = kEmptyContour_SegmentState; 1511#endif 1512 // need to init enough to make next() harmlessly return kDone_Verb 1513 fVerbs = NULL; 1514 fVerbStop = NULL; 1515 fNeedClose = false; 1516} 1517 1518SkPath::Iter::Iter(const SkPath& path, bool forceClose) { 1519 this->setPath(path, forceClose); 1520} 1521 1522void SkPath::Iter::setPath(const SkPath& path, bool forceClose) { 1523 fPts = path.fPathRef->points(); 1524 fVerbs = path.fPathRef->verbs(); 1525 fVerbStop = path.fPathRef->verbsMemBegin(); 1526 fLastPt.fX = fLastPt.fY = 0; 1527 fMoveTo.fX = fMoveTo.fY = 0; 1528 fForceClose = SkToU8(forceClose); 1529 fNeedClose = false; 1530 fSegmentState = kEmptyContour_SegmentState; 1531} 1532 1533bool SkPath::Iter::isClosedContour() const { 1534 if (fVerbs == NULL || fVerbs == fVerbStop) { 1535 return false; 1536 } 1537 if (fForceClose) { 1538 return true; 1539 } 1540 1541 const uint8_t* verbs = fVerbs; 1542 const uint8_t* stop = fVerbStop; 1543 1544 if (kMove_Verb == *(verbs - 1)) { 1545 verbs -= 1; // skip the initial moveto 1546 } 1547 1548 while (verbs > stop) { 1549 // verbs points one beyond the current verb, decrement first. 1550 unsigned v = *(--verbs); 1551 if (kMove_Verb == v) { 1552 break; 1553 } 1554 if (kClose_Verb == v) { 1555 return true; 1556 } 1557 } 1558 return false; 1559} 1560 1561SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) { 1562 SkASSERT(pts); 1563 if (fLastPt != fMoveTo) { 1564 // A special case: if both points are NaN, SkPoint::operation== returns 1565 // false, but the iterator expects that they are treated as the same. 1566 // (consider SkPoint is a 2-dimension float point). 1567 if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) || 1568 SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) { 1569 return kClose_Verb; 1570 } 1571 1572 pts[0] = fLastPt; 1573 pts[1] = fMoveTo; 1574 fLastPt = fMoveTo; 1575 fCloseLine = true; 1576 return kLine_Verb; 1577 } else { 1578 pts[0] = fMoveTo; 1579 return kClose_Verb; 1580 } 1581} 1582 1583const SkPoint& SkPath::Iter::cons_moveTo() { 1584 if (fSegmentState == kAfterMove_SegmentState) { 1585 // Set the first return pt to the move pt 1586 fSegmentState = kAfterPrimitive_SegmentState; 1587 return fMoveTo; 1588 } else { 1589 SkASSERT(fSegmentState == kAfterPrimitive_SegmentState); 1590 // Set the first return pt to the last pt of the previous primitive. 1591 return fPts[-1]; 1592 } 1593} 1594 1595void SkPath::Iter::consumeDegenerateSegments() { 1596 // We need to step over anything that will not move the current draw point 1597 // forward before the next move is seen 1598 const uint8_t* lastMoveVerb = 0; 1599 const SkPoint* lastMovePt = 0; 1600 SkPoint lastPt = fLastPt; 1601 while (fVerbs != fVerbStop) { 1602 unsigned verb = *(fVerbs - 1); // fVerbs is one beyond the current verb 1603 switch (verb) { 1604 case kMove_Verb: 1605 // Keep a record of this most recent move 1606 lastMoveVerb = fVerbs; 1607 lastMovePt = fPts; 1608 lastPt = fPts[0]; 1609 fVerbs--; 1610 fPts++; 1611 break; 1612 1613 case kClose_Verb: 1614 // A close when we are in a segment is always valid except when it 1615 // follows a move which follows a segment. 1616 if (fSegmentState == kAfterPrimitive_SegmentState && !lastMoveVerb) { 1617 return; 1618 } 1619 // A close at any other time must be ignored 1620 fVerbs--; 1621 break; 1622 1623 case kLine_Verb: 1624 if (!IsLineDegenerate(lastPt, fPts[0])) { 1625 if (lastMoveVerb) { 1626 fVerbs = lastMoveVerb; 1627 fPts = lastMovePt; 1628 return; 1629 } 1630 return; 1631 } 1632 // Ignore this line and continue 1633 fVerbs--; 1634 fPts++; 1635 break; 1636 1637 case kQuad_Verb: 1638 if (!IsQuadDegenerate(lastPt, fPts[0], fPts[1])) { 1639 if (lastMoveVerb) { 1640 fVerbs = lastMoveVerb; 1641 fPts = lastMovePt; 1642 return; 1643 } 1644 return; 1645 } 1646 // Ignore this line and continue 1647 fVerbs--; 1648 fPts += 2; 1649 break; 1650 1651 case kCubic_Verb: 1652 if (!IsCubicDegenerate(lastPt, fPts[0], fPts[1], fPts[2])) { 1653 if (lastMoveVerb) { 1654 fVerbs = lastMoveVerb; 1655 fPts = lastMovePt; 1656 return; 1657 } 1658 return; 1659 } 1660 // Ignore this line and continue 1661 fVerbs--; 1662 fPts += 3; 1663 break; 1664 1665 default: 1666 SkDEBUGFAIL("Should never see kDone_Verb"); 1667 } 1668 } 1669} 1670 1671SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) { 1672 SkASSERT(ptsParam); 1673 1674 if (fVerbs == fVerbStop) { 1675 // Close the curve if requested and if there is some curve to close 1676 if (fNeedClose && fSegmentState == kAfterPrimitive_SegmentState) { 1677 if (kLine_Verb == this->autoClose(ptsParam)) { 1678 return kLine_Verb; 1679 } 1680 fNeedClose = false; 1681 return kClose_Verb; 1682 } 1683 return kDone_Verb; 1684 } 1685 1686 // fVerbs is one beyond the current verb, decrement first 1687 unsigned verb = *(--fVerbs); 1688 const SkPoint* SK_RESTRICT srcPts = fPts; 1689 SkPoint* SK_RESTRICT pts = ptsParam; 1690 1691 switch (verb) { 1692 case kMove_Verb: 1693 if (fNeedClose) { 1694 fVerbs++; // move back one verb 1695 verb = this->autoClose(pts); 1696 if (verb == kClose_Verb) { 1697 fNeedClose = false; 1698 } 1699 return (Verb)verb; 1700 } 1701 if (fVerbs == fVerbStop) { // might be a trailing moveto 1702 return kDone_Verb; 1703 } 1704 fMoveTo = *srcPts; 1705 pts[0] = *srcPts; 1706 srcPts += 1; 1707 fSegmentState = kAfterMove_SegmentState; 1708 fLastPt = fMoveTo; 1709 fNeedClose = fForceClose; 1710 break; 1711 case kLine_Verb: 1712 pts[0] = this->cons_moveTo(); 1713 pts[1] = srcPts[0]; 1714 fLastPt = srcPts[0]; 1715 fCloseLine = false; 1716 srcPts += 1; 1717 break; 1718 case kQuad_Verb: 1719 pts[0] = this->cons_moveTo(); 1720 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint)); 1721 fLastPt = srcPts[1]; 1722 srcPts += 2; 1723 break; 1724 case kCubic_Verb: 1725 pts[0] = this->cons_moveTo(); 1726 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint)); 1727 fLastPt = srcPts[2]; 1728 srcPts += 3; 1729 break; 1730 case kClose_Verb: 1731 verb = this->autoClose(pts); 1732 if (verb == kLine_Verb) { 1733 fVerbs++; // move back one verb 1734 } else { 1735 fNeedClose = false; 1736 fSegmentState = kEmptyContour_SegmentState; 1737 } 1738 fLastPt = fMoveTo; 1739 break; 1740 } 1741 fPts = srcPts; 1742 return (Verb)verb; 1743} 1744 1745/////////////////////////////////////////////////////////////////////////////// 1746 1747SkPath::RawIter::RawIter() { 1748#ifdef SK_DEBUG 1749 fPts = NULL; 1750 fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0; 1751#endif 1752 // need to init enough to make next() harmlessly return kDone_Verb 1753 fVerbs = NULL; 1754 fVerbStop = NULL; 1755} 1756 1757SkPath::RawIter::RawIter(const SkPath& path) { 1758 this->setPath(path); 1759} 1760 1761void SkPath::RawIter::setPath(const SkPath& path) { 1762 fPts = path.fPathRef->points(); 1763 fVerbs = path.fPathRef->verbs(); 1764 fVerbStop = path.fPathRef->verbsMemBegin(); 1765 fMoveTo.fX = fMoveTo.fY = 0; 1766 fLastPt.fX = fLastPt.fY = 0; 1767} 1768 1769SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) { 1770 SkASSERT(NULL != pts); 1771 if (fVerbs == fVerbStop) { 1772 return kDone_Verb; 1773 } 1774 1775 // fVerbs points one beyond next verb so decrement first. 1776 unsigned verb = *(--fVerbs); 1777 const SkPoint* srcPts = fPts; 1778 1779 switch (verb) { 1780 case kMove_Verb: 1781 pts[0] = *srcPts; 1782 fMoveTo = srcPts[0]; 1783 fLastPt = fMoveTo; 1784 srcPts += 1; 1785 break; 1786 case kLine_Verb: 1787 pts[0] = fLastPt; 1788 pts[1] = srcPts[0]; 1789 fLastPt = srcPts[0]; 1790 srcPts += 1; 1791 break; 1792 case kQuad_Verb: 1793 pts[0] = fLastPt; 1794 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint)); 1795 fLastPt = srcPts[1]; 1796 srcPts += 2; 1797 break; 1798 case kCubic_Verb: 1799 pts[0] = fLastPt; 1800 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint)); 1801 fLastPt = srcPts[2]; 1802 srcPts += 3; 1803 break; 1804 case kClose_Verb: 1805 fLastPt = fMoveTo; 1806 pts[0] = fMoveTo; 1807 break; 1808 } 1809 fPts = srcPts; 1810 return (Verb)verb; 1811} 1812 1813/////////////////////////////////////////////////////////////////////////////// 1814 1815/* 1816 Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]] 1817*/ 1818 1819uint32_t SkPath::writeToMemory(void* storage) const { 1820 SkDEBUGCODE(this->validate();) 1821 1822 if (NULL == storage) { 1823 const int byteCount = sizeof(int32_t) 1824#if NEW_PICTURE_FORMAT 1825 + fPathRef->writeSize() 1826#else 1827 + 2 * sizeof(int32_t) 1828 + sizeof(SkPoint) * fPathRef->countPoints() 1829 + sizeof(uint8_t) * fPathRef->countVerbs() 1830#endif 1831 + sizeof(SkRect); 1832 return SkAlign4(byteCount); 1833 } 1834 1835 SkWBuffer buffer(storage); 1836#if !NEW_PICTURE_FORMAT 1837 buffer.write32(fPathRef->countPoints()); 1838 buffer.write32(fPathRef->countVerbs()); 1839#endif 1840 1841 // Call getBounds() to ensure (as a side-effect) that fBounds 1842 // and fIsFinite are computed. 1843 const SkRect& bounds = this->getBounds(); 1844 SkASSERT(!fBoundsIsDirty); 1845 1846 int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift) | 1847 ((fIsOval & 1) << kIsOval_SerializationShift) | 1848 (fConvexity << kConvexity_SerializationShift) | 1849 (fFillType << kFillType_SerializationShift) | 1850 (fSegmentMask << kSegmentMask_SerializationShift); 1851 1852 buffer.write32(packed); 1853 1854 fPathRef->writeToBuffer(&buffer); 1855 1856 buffer.write(&bounds, sizeof(bounds)); 1857 1858 buffer.padToAlign4(); 1859 return buffer.pos(); 1860} 1861 1862uint32_t SkPath::readFromMemory(const void* storage) { 1863 SkRBuffer buffer(storage); 1864#if !NEW_PICTURE_FORMAT 1865 int32_t pcount = buffer.readS32(); 1866 int32_t vcount = buffer.readS32(); 1867#endif 1868 1869 uint32_t packed = buffer.readS32(); 1870 fIsFinite = (packed >> kIsFinite_SerializationShift) & 1; 1871 fIsOval = (packed >> kIsOval_SerializationShift) & 1; 1872 fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF; 1873 fFillType = (packed >> kFillType_SerializationShift) & 0xFF; 1874 fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0xFF; 1875 1876#if NEW_PICTURE_FORMAT 1877 fPathRef.reset(SkPathRef::CreateFromBuffer(&buffer)); 1878#else 1879 fPathRef.reset(SkPathRef::CreateFromBuffer(vcount, pcount, &buffer)); 1880#endif 1881 1882 buffer.read(&fBounds, sizeof(fBounds)); 1883 fBoundsIsDirty = false; 1884 1885 buffer.skipToAlign4(); 1886 1887 GEN_ID_INC; 1888 1889 SkDEBUGCODE(this->validate();) 1890 return buffer.pos(); 1891} 1892 1893/////////////////////////////////////////////////////////////////////////////// 1894 1895void SkPath::dump(bool forceClose, const char title[]) const { 1896 Iter iter(*this, forceClose); 1897 SkPoint pts[4]; 1898 Verb verb; 1899 1900 SkDebugf("path: forceClose=%s %s\n", forceClose ? "true" : "false", 1901 title ? title : ""); 1902 1903 while ((verb = iter.next(pts, false)) != kDone_Verb) { 1904 switch (verb) { 1905 case kMove_Verb: 1906 SkDebugf(" path: moveTo [%g %g]\n", 1907 SkScalarToFloat(pts[0].fX), SkScalarToFloat(pts[0].fY)); 1908 break; 1909 case kLine_Verb: 1910 SkDebugf(" path: lineTo [%g %g]\n", 1911 SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY)); 1912 break; 1913 case kQuad_Verb: 1914 SkDebugf(" path: quadTo [%g %g] [%g %g]\n", 1915 SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY), 1916 SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY)); 1917 break; 1918 case kCubic_Verb: 1919 SkDebugf(" path: cubeTo [%g %g] [%g %g] [%g %g]\n", 1920 SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY), 1921 SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY), 1922 SkScalarToFloat(pts[3].fX), SkScalarToFloat(pts[3].fY)); 1923 break; 1924 case kClose_Verb: 1925 SkDebugf(" path: close\n"); 1926 break; 1927 default: 1928 SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb); 1929 verb = kDone_Verb; // stop the loop 1930 break; 1931 } 1932 } 1933 SkDebugf("path: done %s\n", title ? title : ""); 1934} 1935 1936void SkPath::dump() const { 1937 this->dump(false); 1938} 1939 1940#ifdef SK_DEBUG 1941void SkPath::validate() const { 1942 SkASSERT(this != NULL); 1943 SkASSERT((fFillType & ~3) == 0); 1944 1945 if (!fBoundsIsDirty) { 1946 SkRect bounds; 1947 1948 bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get()); 1949 SkASSERT(SkToBool(fIsFinite) == isFinite); 1950 1951 if (fPathRef->countPoints() <= 1) { 1952 // if we're empty, fBounds may be empty but translated, so we can't 1953 // necessarily compare to bounds directly 1954 // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will 1955 // be [2, 2, 2, 2] 1956 SkASSERT(bounds.isEmpty()); 1957 SkASSERT(fBounds.isEmpty()); 1958 } else { 1959 if (bounds.isEmpty()) { 1960 SkASSERT(fBounds.isEmpty()); 1961 } else { 1962 if (!fBounds.isEmpty()) { 1963 SkASSERT(fBounds.contains(bounds)); 1964 } 1965 } 1966 } 1967 } 1968 1969 uint32_t mask = 0; 1970 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbs(); 1971 for (int i = 0; i < fPathRef->countVerbs(); i++) { 1972 switch (verbs[~i]) { 1973 case kLine_Verb: 1974 mask |= kLine_SegmentMask; 1975 break; 1976 case kQuad_Verb: 1977 mask |= kQuad_SegmentMask; 1978 break; 1979 case kCubic_Verb: 1980 mask |= kCubic_SegmentMask; 1981 case kMove_Verb: // these verbs aren't included in the segment mask. 1982 case kClose_Verb: 1983 break; 1984 case kDone_Verb: 1985 SkDEBUGFAIL("Done verb shouldn't be recorded."); 1986 break; 1987 default: 1988 SkDEBUGFAIL("Unknown Verb"); 1989 break; 1990 } 1991 } 1992 SkASSERT(mask == fSegmentMask); 1993} 1994#endif 1995 1996/////////////////////////////////////////////////////////////////////////////// 1997 1998static int sign(SkScalar x) { return x < 0; } 1999#define kValueNeverReturnedBySign 2 2000 2001static int CrossProductSign(const SkVector& a, const SkVector& b) { 2002 return SkScalarSignAsInt(SkPoint::CrossProduct(a, b)); 2003} 2004 2005// only valid for a single contour 2006struct Convexicator { 2007 Convexicator() : fPtCount(0), fConvexity(SkPath::kConvex_Convexity) { 2008 fSign = 0; 2009 // warnings 2010 fCurrPt.set(0, 0); 2011 fVec0.set(0, 0); 2012 fVec1.set(0, 0); 2013 fFirstVec.set(0, 0); 2014 2015 fDx = fDy = 0; 2016 fSx = fSy = kValueNeverReturnedBySign; 2017 } 2018 2019 SkPath::Convexity getConvexity() const { return fConvexity; } 2020 2021 void addPt(const SkPoint& pt) { 2022 if (SkPath::kConcave_Convexity == fConvexity) { 2023 return; 2024 } 2025 2026 if (0 == fPtCount) { 2027 fCurrPt = pt; 2028 ++fPtCount; 2029 } else { 2030 SkVector vec = pt - fCurrPt; 2031 if (vec.fX || vec.fY) { 2032 fCurrPt = pt; 2033 if (++fPtCount == 2) { 2034 fFirstVec = fVec1 = vec; 2035 } else { 2036 SkASSERT(fPtCount > 2); 2037 this->addVec(vec); 2038 } 2039 2040 int sx = sign(vec.fX); 2041 int sy = sign(vec.fY); 2042 fDx += (sx != fSx); 2043 fDy += (sy != fSy); 2044 fSx = sx; 2045 fSy = sy; 2046 2047 if (fDx > 3 || fDy > 3) { 2048 fConvexity = SkPath::kConcave_Convexity; 2049 } 2050 } 2051 } 2052 } 2053 2054 void close() { 2055 if (fPtCount > 2) { 2056 this->addVec(fFirstVec); 2057 } 2058 } 2059 2060private: 2061 void addVec(const SkVector& vec) { 2062 SkASSERT(vec.fX || vec.fY); 2063 fVec0 = fVec1; 2064 fVec1 = vec; 2065 int sign = CrossProductSign(fVec0, fVec1); 2066 if (0 == fSign) { 2067 fSign = sign; 2068 } else if (sign) { 2069 if (fSign != sign) { 2070 fConvexity = SkPath::kConcave_Convexity; 2071 } 2072 } 2073 } 2074 2075 SkPoint fCurrPt; 2076 SkVector fVec0, fVec1, fFirstVec; 2077 int fPtCount; // non-degenerate points 2078 int fSign; 2079 SkPath::Convexity fConvexity; 2080 int fDx, fDy, fSx, fSy; 2081}; 2082 2083SkPath::Convexity SkPath::ComputeConvexity(const SkPath& path) { 2084 SkPoint pts[4]; 2085 SkPath::Verb verb; 2086 SkPath::Iter iter(path, true); 2087 2088 int contourCount = 0; 2089 int count; 2090 Convexicator state; 2091 2092 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 2093 switch (verb) { 2094 case kMove_Verb: 2095 if (++contourCount > 1) { 2096 return kConcave_Convexity; 2097 } 2098 pts[1] = pts[0]; 2099 count = 1; 2100 break; 2101 case kLine_Verb: count = 1; break; 2102 case kQuad_Verb: count = 2; break; 2103 case kCubic_Verb: count = 3; break; 2104 case kClose_Verb: 2105 state.close(); 2106 count = 0; 2107 break; 2108 default: 2109 SkDEBUGFAIL("bad verb"); 2110 return kConcave_Convexity; 2111 } 2112 2113 for (int i = 1; i <= count; i++) { 2114 state.addPt(pts[i]); 2115 } 2116 // early exit 2117 if (kConcave_Convexity == state.getConvexity()) { 2118 return kConcave_Convexity; 2119 } 2120 } 2121 return state.getConvexity(); 2122} 2123 2124/////////////////////////////////////////////////////////////////////////////// 2125 2126class ContourIter { 2127public: 2128 ContourIter(const SkPathRef& pathRef); 2129 2130 bool done() const { return fDone; } 2131 // if !done() then these may be called 2132 int count() const { return fCurrPtCount; } 2133 const SkPoint* pts() const { return fCurrPt; } 2134 void next(); 2135 2136private: 2137 int fCurrPtCount; 2138 const SkPoint* fCurrPt; 2139 const uint8_t* fCurrVerb; 2140 const uint8_t* fStopVerbs; 2141 bool fDone; 2142 SkDEBUGCODE(int fContourCounter;) 2143}; 2144 2145ContourIter::ContourIter(const SkPathRef& pathRef) { 2146 fStopVerbs = pathRef.verbsMemBegin(); 2147 fDone = false; 2148 fCurrPt = pathRef.points(); 2149 fCurrVerb = pathRef.verbs(); 2150 fCurrPtCount = 0; 2151 SkDEBUGCODE(fContourCounter = 0;) 2152 this->next(); 2153} 2154 2155void ContourIter::next() { 2156 if (fCurrVerb <= fStopVerbs) { 2157 fDone = true; 2158 } 2159 if (fDone) { 2160 return; 2161 } 2162 2163 // skip pts of prev contour 2164 fCurrPt += fCurrPtCount; 2165 2166 SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]); 2167 int ptCount = 1; // moveTo 2168 const uint8_t* verbs = fCurrVerb; 2169 2170 for (--verbs; verbs > fStopVerbs; --verbs) { 2171 switch (verbs[~0]) { 2172 case SkPath::kMove_Verb: 2173 goto CONTOUR_END; 2174 case SkPath::kLine_Verb: 2175 ptCount += 1; 2176 break; 2177 case SkPath::kQuad_Verb: 2178 ptCount += 2; 2179 break; 2180 case SkPath::kCubic_Verb: 2181 ptCount += 3; 2182 break; 2183 default: // kClose_Verb, just keep going 2184 break; 2185 } 2186 } 2187CONTOUR_END: 2188 fCurrPtCount = ptCount; 2189 fCurrVerb = verbs; 2190 SkDEBUGCODE(++fContourCounter;) 2191} 2192 2193// returns cross product of (p1 - p0) and (p2 - p0) 2194static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) { 2195 SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0); 2196 // We may get 0 when the above subtracts underflow. We expect this to be 2197 // very rare and lazily promote to double. 2198 if (0 == cross) { 2199 double p0x = SkScalarToDouble(p0.fX); 2200 double p0y = SkScalarToDouble(p0.fY); 2201 2202 double p1x = SkScalarToDouble(p1.fX); 2203 double p1y = SkScalarToDouble(p1.fY); 2204 2205 double p2x = SkScalarToDouble(p2.fX); 2206 double p2y = SkScalarToDouble(p2.fY); 2207 2208 cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) - 2209 (p1y - p0y) * (p2x - p0x)); 2210 2211 } 2212 return cross; 2213} 2214 2215// Returns the first pt with the maximum Y coordinate 2216static int find_max_y(const SkPoint pts[], int count) { 2217 SkASSERT(count > 0); 2218 SkScalar max = pts[0].fY; 2219 int firstIndex = 0; 2220 for (int i = 1; i < count; ++i) { 2221 SkScalar y = pts[i].fY; 2222 if (y > max) { 2223 max = y; 2224 firstIndex = i; 2225 } 2226 } 2227 return firstIndex; 2228} 2229 2230static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) { 2231 int i = index; 2232 for (;;) { 2233 i = (i + inc) % n; 2234 if (i == index) { // we wrapped around, so abort 2235 break; 2236 } 2237 if (pts[index] != pts[i]) { // found a different point, success! 2238 break; 2239 } 2240 } 2241 return i; 2242} 2243 2244/** 2245 * Starting at index, and moving forward (incrementing), find the xmin and 2246 * xmax of the contiguous points that have the same Y. 2247 */ 2248static int find_min_max_x_at_y(const SkPoint pts[], int index, int n, 2249 int* maxIndexPtr) { 2250 const SkScalar y = pts[index].fY; 2251 SkScalar min = pts[index].fX; 2252 SkScalar max = min; 2253 int minIndex = index; 2254 int maxIndex = index; 2255 for (int i = index + 1; i < n; ++i) { 2256 if (pts[i].fY != y) { 2257 break; 2258 } 2259 SkScalar x = pts[i].fX; 2260 if (x < min) { 2261 min = x; 2262 minIndex = i; 2263 } else if (x > max) { 2264 max = x; 2265 maxIndex = i; 2266 } 2267 } 2268 *maxIndexPtr = maxIndex; 2269 return minIndex; 2270} 2271 2272static bool crossToDir(SkScalar cross, SkPath::Direction* dir) { 2273 if (dir) { 2274 *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction; 2275 } 2276 return true; 2277} 2278 2279#if 0 2280#include "SkString.h" 2281#include "../utils/SkParsePath.h" 2282static void dumpPath(const SkPath& path) { 2283 SkString str; 2284 SkParsePath::ToSVGString(path, &str); 2285 SkDebugf("%s\n", str.c_str()); 2286} 2287#endif 2288 2289namespace { 2290// for use with convex_dir_test 2291double mul(double a, double b) { return a * b; } 2292SkScalar mul(SkScalar a, SkScalar b) { return SkScalarMul(a, b); } 2293double toDouble(SkScalar a) { return SkScalarToDouble(a); } 2294SkScalar toScalar(SkScalar a) { return a; } 2295 2296// determines the winding direction of a convex polygon with the precision 2297// of T. CAST_SCALAR casts an SkScalar to T. 2298template <typename T, T (CAST_SCALAR)(SkScalar)> 2299bool convex_dir_test(int n, const SkPoint pts[], SkPath::Direction* dir) { 2300 // we find the first three points that form a non-degenerate 2301 // triangle. If there are no such points then the path is 2302 // degenerate. The first is always point 0. Now we find the second 2303 // point. 2304 int i = 0; 2305 enum { kX = 0, kY = 1 }; 2306 T v0[2]; 2307 while (1) { 2308 v0[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX); 2309 v0[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY); 2310 if (v0[kX] || v0[kY]) { 2311 break; 2312 } 2313 if (++i == n - 1) { 2314 return false; 2315 } 2316 } 2317 // now find a third point that is not colinear with the first two 2318 // points and check the orientation of the triangle (which will be 2319 // the same as the orientation of the path). 2320 for (++i; i < n; ++i) { 2321 T v1[2]; 2322 v1[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX); 2323 v1[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY); 2324 T cross = mul(v0[kX], v1[kY]) - mul(v0[kY], v1[kX]); 2325 if (0 != cross) { 2326 *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction; 2327 return true; 2328 } 2329 } 2330 return false; 2331} 2332} 2333 2334/* 2335 * We loop through all contours, and keep the computed cross-product of the 2336 * contour that contained the global y-max. If we just look at the first 2337 * contour, we may find one that is wound the opposite way (correctly) since 2338 * it is the interior of a hole (e.g. 'o'). Thus we must find the contour 2339 * that is outer most (or at least has the global y-max) before we can consider 2340 * its cross product. 2341 */ 2342bool SkPath::cheapComputeDirection(Direction* dir) const { 2343// dumpPath(*this); 2344 // don't want to pay the cost for computing this if it 2345 // is unknown, so we don't call isConvex() 2346 const Convexity conv = this->getConvexityOrUnknown(); 2347 2348 ContourIter iter(*fPathRef.get()); 2349 2350 // initialize with our logical y-min 2351 SkScalar ymax = this->getBounds().fTop; 2352 SkScalar ymaxCross = 0; 2353 2354 for (; !iter.done(); iter.next()) { 2355 int n = iter.count(); 2356 if (n < 3) { 2357 continue; 2358 } 2359 2360 const SkPoint* pts = iter.pts(); 2361 SkScalar cross = 0; 2362 if (kConvex_Convexity == conv) { 2363 // We try first at scalar precision, and then again at double 2364 // precision. This is because the vectors computed between distant 2365 // points may lose too much precision. 2366 if (convex_dir_test<SkScalar, toScalar>(n, pts, dir)) { 2367 return true; 2368 } 2369 if (convex_dir_test<double, toDouble>(n, pts, dir)) { 2370 return true; 2371 } else { 2372 return false; 2373 } 2374 } else { 2375 int index = find_max_y(pts, n); 2376 if (pts[index].fY < ymax) { 2377 continue; 2378 } 2379 2380 // If there is more than 1 distinct point at the y-max, we take the 2381 // x-min and x-max of them and just subtract to compute the dir. 2382 if (pts[(index + 1) % n].fY == pts[index].fY) { 2383 int maxIndex; 2384 int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex); 2385 if (minIndex == maxIndex) { 2386 goto TRY_CROSSPROD; 2387 } 2388 SkASSERT(pts[minIndex].fY == pts[index].fY); 2389 SkASSERT(pts[maxIndex].fY == pts[index].fY); 2390 SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX); 2391 // we just subtract the indices, and let that auto-convert to 2392 // SkScalar, since we just want - or + to signal the direction. 2393 cross = minIndex - maxIndex; 2394 } else { 2395 TRY_CROSSPROD: 2396 // Find a next and prev index to use for the cross-product test, 2397 // but we try to find pts that form non-zero vectors from pts[index] 2398 // 2399 // Its possible that we can't find two non-degenerate vectors, so 2400 // we have to guard our search (e.g. all the pts could be in the 2401 // same place). 2402 2403 // we pass n - 1 instead of -1 so we don't foul up % operator by 2404 // passing it a negative LH argument. 2405 int prev = find_diff_pt(pts, index, n, n - 1); 2406 if (prev == index) { 2407 // completely degenerate, skip to next contour 2408 continue; 2409 } 2410 int next = find_diff_pt(pts, index, n, 1); 2411 SkASSERT(next != index); 2412 cross = cross_prod(pts[prev], pts[index], pts[next]); 2413 // if we get a zero, but the pts aren't on top of each other, then 2414 // we can just look at the direction 2415 if (0 == cross) { 2416 // construct the subtract so we get the correct Direction below 2417 cross = pts[index].fX - pts[next].fX; 2418 } 2419 } 2420 2421 if (cross) { 2422 // record our best guess so far 2423 ymax = pts[index].fY; 2424 ymaxCross = cross; 2425 } 2426 } 2427 } 2428 2429 return ymaxCross ? crossToDir(ymaxCross, dir) : false; 2430} 2431 2432/////////////////////////////////////////////////////////////////////////////// 2433 2434static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C, 2435 SkScalar D, SkScalar t) { 2436 return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D); 2437} 2438 2439static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3, 2440 SkScalar t) { 2441 SkScalar A = c3 + 3*(c1 - c2) - c0; 2442 SkScalar B = 3*(c2 - c1 - c1 + c0); 2443 SkScalar C = 3*(c1 - c0); 2444 SkScalar D = c0; 2445 return eval_cubic_coeff(A, B, C, D, t); 2446} 2447 2448/* Given 4 cubic points (either Xs or Ys), and a target X or Y, compute the 2449 t value such that cubic(t) = target 2450 */ 2451static bool chopMonoCubicAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3, 2452 SkScalar target, SkScalar* t) { 2453 // SkASSERT(c0 <= c1 && c1 <= c2 && c2 <= c3); 2454 SkASSERT(c0 < target && target < c3); 2455 2456 SkScalar D = c0 - target; 2457 SkScalar A = c3 + 3*(c1 - c2) - c0; 2458 SkScalar B = 3*(c2 - c1 - c1 + c0); 2459 SkScalar C = 3*(c1 - c0); 2460 2461 const SkScalar TOLERANCE = SK_Scalar1 / 4096; 2462 SkScalar minT = 0; 2463 SkScalar maxT = SK_Scalar1; 2464 SkScalar mid; 2465 int i; 2466 for (i = 0; i < 16; i++) { 2467 mid = SkScalarAve(minT, maxT); 2468 SkScalar delta = eval_cubic_coeff(A, B, C, D, mid); 2469 if (delta < 0) { 2470 minT = mid; 2471 delta = -delta; 2472 } else { 2473 maxT = mid; 2474 } 2475 if (delta < TOLERANCE) { 2476 break; 2477 } 2478 } 2479 *t = mid; 2480 return true; 2481} 2482 2483template <size_t N> static void find_minmax(const SkPoint pts[], 2484 SkScalar* minPtr, SkScalar* maxPtr) { 2485 SkScalar min, max; 2486 min = max = pts[0].fX; 2487 for (size_t i = 1; i < N; ++i) { 2488 min = SkMinScalar(min, pts[i].fX); 2489 max = SkMaxScalar(max, pts[i].fX); 2490 } 2491 *minPtr = min; 2492 *maxPtr = max; 2493} 2494 2495static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y) { 2496 SkPoint storage[4]; 2497 2498 int dir = 1; 2499 if (pts[0].fY > pts[3].fY) { 2500 storage[0] = pts[3]; 2501 storage[1] = pts[2]; 2502 storage[2] = pts[1]; 2503 storage[3] = pts[0]; 2504 pts = storage; 2505 dir = -1; 2506 } 2507 if (y < pts[0].fY || y >= pts[3].fY) { 2508 return 0; 2509 } 2510 2511 // quickreject or quickaccept 2512 SkScalar min, max; 2513 find_minmax<4>(pts, &min, &max); 2514 if (x < min) { 2515 return 0; 2516 } 2517 if (x > max) { 2518 return dir; 2519 } 2520 2521 // compute the actual x(t) value 2522 SkScalar t, xt; 2523 if (chopMonoCubicAt(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY, y, &t)) { 2524 xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t); 2525 } else { 2526 SkScalar mid = SkScalarAve(pts[0].fY, pts[3].fY); 2527 xt = y < mid ? pts[0].fX : pts[3].fX; 2528 } 2529 return xt < x ? dir : 0; 2530} 2531 2532static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y) { 2533 SkPoint dst[10]; 2534 int n = SkChopCubicAtYExtrema(pts, dst); 2535 int w = 0; 2536 for (int i = 0; i <= n; ++i) { 2537 w += winding_mono_cubic(&dst[i * 3], x, y); 2538 } 2539 return w; 2540} 2541 2542static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y) { 2543 SkScalar y0 = pts[0].fY; 2544 SkScalar y2 = pts[2].fY; 2545 2546 int dir = 1; 2547 if (y0 > y2) { 2548 SkTSwap(y0, y2); 2549 dir = -1; 2550 } 2551 if (y < y0 || y >= y2) { 2552 return 0; 2553 } 2554 2555 // bounds check on X (not required. is it faster?) 2556#if 0 2557 if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) { 2558 return 0; 2559 } 2560#endif 2561 2562 SkScalar roots[2]; 2563 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, 2564 2 * (pts[1].fY - pts[0].fY), 2565 pts[0].fY - y, 2566 roots); 2567 SkASSERT(n <= 1); 2568 SkScalar xt; 2569 if (0 == n) { 2570 SkScalar mid = SkScalarAve(y0, y2); 2571 // Need [0] and [2] if dir == 1 2572 // and [2] and [0] if dir == -1 2573 xt = y < mid ? pts[1 - dir].fX : pts[dir - 1].fX; 2574 } else { 2575 SkScalar t = roots[0]; 2576 SkScalar C = pts[0].fX; 2577 SkScalar A = pts[2].fX - 2 * pts[1].fX + C; 2578 SkScalar B = 2 * (pts[1].fX - C); 2579 xt = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); 2580 } 2581 return xt < x ? dir : 0; 2582} 2583 2584static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) { 2585 // return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0; 2586 if (y0 == y1) { 2587 return true; 2588 } 2589 if (y0 < y1) { 2590 return y1 <= y2; 2591 } else { 2592 return y1 >= y2; 2593 } 2594} 2595 2596static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y) { 2597 SkPoint dst[5]; 2598 int n = 0; 2599 2600 if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) { 2601 n = SkChopQuadAtYExtrema(pts, dst); 2602 pts = dst; 2603 } 2604 int w = winding_mono_quad(pts, x, y); 2605 if (n > 0) { 2606 w += winding_mono_quad(&pts[2], x, y); 2607 } 2608 return w; 2609} 2610 2611static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y) { 2612 SkScalar x0 = pts[0].fX; 2613 SkScalar y0 = pts[0].fY; 2614 SkScalar x1 = pts[1].fX; 2615 SkScalar y1 = pts[1].fY; 2616 2617 SkScalar dy = y1 - y0; 2618 2619 int dir = 1; 2620 if (y0 > y1) { 2621 SkTSwap(y0, y1); 2622 dir = -1; 2623 } 2624 if (y < y0 || y >= y1) { 2625 return 0; 2626 } 2627 2628 SkScalar cross = SkScalarMul(x1 - x0, y - pts[0].fY) - 2629 SkScalarMul(dy, x - pts[0].fX); 2630 2631 if (SkScalarSignAsInt(cross) == dir) { 2632 dir = 0; 2633 } 2634 return dir; 2635} 2636 2637bool SkPath::contains(SkScalar x, SkScalar y) const { 2638 bool isInverse = this->isInverseFillType(); 2639 if (this->isEmpty()) { 2640 return isInverse; 2641 } 2642 2643 const SkRect& bounds = this->getBounds(); 2644 if (!bounds.contains(x, y)) { 2645 return isInverse; 2646 } 2647 2648 SkPath::Iter iter(*this, true); 2649 bool done = false; 2650 int w = 0; 2651 do { 2652 SkPoint pts[4]; 2653 switch (iter.next(pts, false)) { 2654 case SkPath::kMove_Verb: 2655 case SkPath::kClose_Verb: 2656 break; 2657 case SkPath::kLine_Verb: 2658 w += winding_line(pts, x, y); 2659 break; 2660 case SkPath::kQuad_Verb: 2661 w += winding_quad(pts, x, y); 2662 break; 2663 case SkPath::kCubic_Verb: 2664 w += winding_cubic(pts, x, y); 2665 break; 2666 case SkPath::kDone_Verb: 2667 done = true; 2668 break; 2669 } 2670 } while (!done); 2671 2672 switch (this->getFillType()) { 2673 case SkPath::kEvenOdd_FillType: 2674 case SkPath::kInverseEvenOdd_FillType: 2675 w &= 1; 2676 break; 2677 default: 2678 break; 2679 } 2680 return SkToBool(w); 2681} 2682 2683