1/* 2 * Copyright 2006 The Android Open Source Project 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkBuffer.h" 9#include "SkCubicClipper.h" 10#include "SkErrorInternals.h" 11#include "SkGeometry.h" 12#include "SkMath.h" 13#include "SkPathPriv.h" 14#include "SkPathRef.h" 15#include "SkRRect.h" 16 17//////////////////////////////////////////////////////////////////////////// 18 19/** 20 * Path.bounds is defined to be the bounds of all the control points. 21 * If we called bounds.join(r) we would skip r if r was empty, which breaks 22 * our promise. Hence we have a custom joiner that doesn't look at emptiness 23 */ 24static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) { 25 dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft); 26 dst->fTop = SkMinScalar(dst->fTop, src.fTop); 27 dst->fRight = SkMaxScalar(dst->fRight, src.fRight); 28 dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom); 29} 30 31static bool is_degenerate(const SkPath& path) { 32 SkPath::Iter iter(path, false); 33 SkPoint pts[4]; 34 return SkPath::kDone_Verb == iter.next(pts); 35} 36 37class SkAutoDisableDirectionCheck { 38public: 39 SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) { 40 fSaved = static_cast<SkPathPriv::FirstDirection>(fPath->fFirstDirection.load()); 41 } 42 43 ~SkAutoDisableDirectionCheck() { 44 fPath->fFirstDirection = fSaved; 45 } 46 47private: 48 SkPath* fPath; 49 SkPathPriv::FirstDirection fSaved; 50}; 51#define SkAutoDisableDirectionCheck(...) SK_REQUIRE_LOCAL_VAR(SkAutoDisableDirectionCheck) 52 53/* This guy's constructor/destructor bracket a path editing operation. It is 54 used when we know the bounds of the amount we are going to add to the path 55 (usually a new contour, but not required). 56 57 It captures some state about the path up front (i.e. if it already has a 58 cached bounds), and then if it can, it updates the cache bounds explicitly, 59 avoiding the need to revisit all of the points in getBounds(). 60 61 It also notes if the path was originally degenerate, and if so, sets 62 isConvex to true. Thus it can only be used if the contour being added is 63 convex. 64 */ 65class SkAutoPathBoundsUpdate { 66public: 67 SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) { 68 this->init(path); 69 } 70 71 SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top, 72 SkScalar right, SkScalar bottom) { 73 fRect.set(left, top, right, bottom); 74 this->init(path); 75 } 76 77 ~SkAutoPathBoundsUpdate() { 78 fPath->setConvexity(fDegenerate ? SkPath::kConvex_Convexity 79 : SkPath::kUnknown_Convexity); 80 if (fEmpty || fHasValidBounds) { 81 fPath->setBounds(fRect); 82 } 83 } 84 85private: 86 SkPath* fPath; 87 SkRect fRect; 88 bool fHasValidBounds; 89 bool fDegenerate; 90 bool fEmpty; 91 92 void init(SkPath* path) { 93 // Cannot use fRect for our bounds unless we know it is sorted 94 fRect.sort(); 95 fPath = path; 96 // Mark the path's bounds as dirty if (1) they are, or (2) the path 97 // is non-finite, and therefore its bounds are not meaningful 98 fHasValidBounds = path->hasComputedBounds() && path->isFinite(); 99 fEmpty = path->isEmpty(); 100 if (fHasValidBounds && !fEmpty) { 101 joinNoEmptyChecks(&fRect, fPath->getBounds()); 102 } 103 fDegenerate = is_degenerate(*path); 104 } 105}; 106#define SkAutoPathBoundsUpdate(...) SK_REQUIRE_LOCAL_VAR(SkAutoPathBoundsUpdate) 107 108//////////////////////////////////////////////////////////////////////////// 109 110/* 111 Stores the verbs and points as they are given to us, with exceptions: 112 - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic 113 - we insert a Move(0,0) if Line | Quad | Cubic is our first command 114 115 The iterator does more cleanup, especially if forceClose == true 116 1. If we encounter degenerate segments, remove them 117 2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt) 118 3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2 119 4. if we encounter Line | Quad | Cubic after Close, cons up a Move 120*/ 121 122//////////////////////////////////////////////////////////////////////////// 123 124// flag to require a moveTo if we begin with something else, like lineTo etc. 125#define INITIAL_LASTMOVETOINDEX_VALUE ~0 126 127SkPath::SkPath() 128 : fPathRef(SkPathRef::CreateEmpty()) { 129 this->resetFields(); 130 fIsVolatile = false; 131} 132 133void SkPath::resetFields() { 134 //fPathRef is assumed to have been emptied by the caller. 135 fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE; 136 fFillType = kWinding_FillType; 137 fConvexity = kUnknown_Convexity; 138 fFirstDirection = SkPathPriv::kUnknown_FirstDirection; 139 140 // We don't touch Android's fSourcePath. It's used to track texture garbage collection, so we 141 // don't want to muck with it if it's been set to something non-nullptr. 142} 143 144SkPath::SkPath(const SkPath& that) 145 : fPathRef(SkRef(that.fPathRef.get())) { 146 this->copyFields(that); 147 SkDEBUGCODE(that.validate();) 148} 149 150SkPath::~SkPath() { 151 SkDEBUGCODE(this->validate();) 152} 153 154SkPath& SkPath::operator=(const SkPath& that) { 155 SkDEBUGCODE(that.validate();) 156 157 if (this != &that) { 158 fPathRef.reset(SkRef(that.fPathRef.get())); 159 this->copyFields(that); 160 } 161 SkDEBUGCODE(this->validate();) 162 return *this; 163} 164 165void SkPath::copyFields(const SkPath& that) { 166 //fPathRef is assumed to have been set by the caller. 167 fLastMoveToIndex = that.fLastMoveToIndex; 168 fFillType = that.fFillType; 169 fConvexity = that.fConvexity; 170 // Simulate fFirstDirection = that.fFirstDirection; 171 fFirstDirection.store(that.fFirstDirection.load()); 172 fIsVolatile = that.fIsVolatile; 173} 174 175bool operator==(const SkPath& a, const SkPath& b) { 176 // note: don't need to look at isConvex or bounds, since just comparing the 177 // raw data is sufficient. 178 return &a == &b || 179 (a.fFillType == b.fFillType && *a.fPathRef.get() == *b.fPathRef.get()); 180} 181 182void SkPath::swap(SkPath& that) { 183 if (this != &that) { 184 fPathRef.swap(that.fPathRef); 185 SkTSwap<int>(fLastMoveToIndex, that.fLastMoveToIndex); 186 SkTSwap<uint8_t>(fFillType, that.fFillType); 187 SkTSwap<uint8_t>(fConvexity, that.fConvexity); 188 // Simulate SkTSwap<uint8_t>(fFirstDirection, that.fFirstDirection); 189 uint8_t temp = fFirstDirection; 190 fFirstDirection.store(that.fFirstDirection.load()); 191 that.fFirstDirection.store(temp); 192 SkTSwap<SkBool8>(fIsVolatile, that.fIsVolatile); 193 } 194} 195 196bool SkPath::isInterpolatable(const SkPath& compare) const { 197 int count = fPathRef->countVerbs(); 198 if (count != compare.fPathRef->countVerbs()) { 199 return false; 200 } 201 if (!count) { 202 return true; 203 } 204 if (memcmp(fPathRef->verbsMemBegin(), compare.fPathRef->verbsMemBegin(), 205 count)) { 206 return false; 207 } 208 return !fPathRef->countWeights() || 209 !SkToBool(memcmp(fPathRef->conicWeights(), compare.fPathRef->conicWeights(), 210 fPathRef->countWeights() * sizeof(*fPathRef->conicWeights()))); 211} 212 213bool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const { 214 int verbCount = fPathRef->countVerbs(); 215 if (verbCount != ending.fPathRef->countVerbs()) { 216 return false; 217 } 218 if (!verbCount) { 219 return true; 220 } 221 out->reset(); 222 out->addPath(*this); 223 fPathRef->interpolate(*ending.fPathRef, weight, out->fPathRef); 224 return true; 225} 226 227static inline bool check_edge_against_rect(const SkPoint& p0, 228 const SkPoint& p1, 229 const SkRect& rect, 230 SkPathPriv::FirstDirection dir) { 231 const SkPoint* edgeBegin; 232 SkVector v; 233 if (SkPathPriv::kCW_FirstDirection == dir) { 234 v = p1 - p0; 235 edgeBegin = &p0; 236 } else { 237 v = p0 - p1; 238 edgeBegin = &p1; 239 } 240 if (v.fX || v.fY) { 241 // check the cross product of v with the vec from edgeBegin to each rect corner 242 SkScalar yL = SkScalarMul(v.fY, rect.fLeft - edgeBegin->fX); 243 SkScalar xT = SkScalarMul(v.fX, rect.fTop - edgeBegin->fY); 244 SkScalar yR = SkScalarMul(v.fY, rect.fRight - edgeBegin->fX); 245 SkScalar xB = SkScalarMul(v.fX, rect.fBottom - edgeBegin->fY); 246 if ((xT < yL) || (xT < yR) || (xB < yL) || (xB < yR)) { 247 return false; 248 } 249 } 250 return true; 251} 252 253bool SkPath::conservativelyContainsRect(const SkRect& rect) const { 254 // This only handles non-degenerate convex paths currently. 255 if (kConvex_Convexity != this->getConvexity()) { 256 return false; 257 } 258 259 SkPathPriv::FirstDirection direction; 260 if (!SkPathPriv::CheapComputeFirstDirection(*this, &direction)) { 261 return false; 262 } 263 264 SkPoint firstPt; 265 SkPoint prevPt; 266 RawIter iter(*this); 267 SkPath::Verb verb; 268 SkPoint pts[4]; 269 SkDEBUGCODE(int moveCnt = 0;) 270 SkDEBUGCODE(int segmentCount = 0;) 271 SkDEBUGCODE(int closeCount = 0;) 272 273 while ((verb = iter.next(pts)) != kDone_Verb) { 274 int nextPt = -1; 275 switch (verb) { 276 case kMove_Verb: 277 SkASSERT(!segmentCount && !closeCount); 278 SkDEBUGCODE(++moveCnt); 279 firstPt = prevPt = pts[0]; 280 break; 281 case kLine_Verb: 282 nextPt = 1; 283 SkASSERT(moveCnt && !closeCount); 284 SkDEBUGCODE(++segmentCount); 285 break; 286 case kQuad_Verb: 287 case kConic_Verb: 288 SkASSERT(moveCnt && !closeCount); 289 SkDEBUGCODE(++segmentCount); 290 nextPt = 2; 291 break; 292 case kCubic_Verb: 293 SkASSERT(moveCnt && !closeCount); 294 SkDEBUGCODE(++segmentCount); 295 nextPt = 3; 296 break; 297 case kClose_Verb: 298 SkDEBUGCODE(++closeCount;) 299 break; 300 default: 301 SkDEBUGFAIL("unknown verb"); 302 } 303 if (-1 != nextPt) { 304 if (SkPath::kConic_Verb == verb) { 305 SkConic orig; 306 orig.set(pts, iter.conicWeight()); 307 SkPoint quadPts[5]; 308 int count = orig.chopIntoQuadsPOW2(quadPts, 1); 309 SkASSERT_RELEASE(2 == count); 310 311 if (!check_edge_against_rect(quadPts[0], quadPts[2], rect, direction)) { 312 return false; 313 } 314 if (!check_edge_against_rect(quadPts[2], quadPts[4], rect, direction)) { 315 return false; 316 } 317 } else { 318 if (!check_edge_against_rect(prevPt, pts[nextPt], rect, direction)) { 319 return false; 320 } 321 } 322 prevPt = pts[nextPt]; 323 } 324 } 325 326 return check_edge_against_rect(prevPt, firstPt, rect, direction); 327} 328 329uint32_t SkPath::getGenerationID() const { 330 uint32_t genID = fPathRef->genID(); 331#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 332 SkASSERT((unsigned)fFillType < (1 << (32 - kPathRefGenIDBitCnt))); 333 genID |= static_cast<uint32_t>(fFillType) << kPathRefGenIDBitCnt; 334#endif 335 return genID; 336} 337 338void SkPath::reset() { 339 SkDEBUGCODE(this->validate();) 340 341 fPathRef.reset(SkPathRef::CreateEmpty()); 342 this->resetFields(); 343} 344 345void SkPath::rewind() { 346 SkDEBUGCODE(this->validate();) 347 348 SkPathRef::Rewind(&fPathRef); 349 this->resetFields(); 350} 351 352bool SkPath::isLastContourClosed() const { 353 int verbCount = fPathRef->countVerbs(); 354 if (0 == verbCount) { 355 return false; 356 } 357 return kClose_Verb == fPathRef->atVerb(verbCount - 1); 358} 359 360bool SkPath::isLine(SkPoint line[2]) const { 361 int verbCount = fPathRef->countVerbs(); 362 363 if (2 == verbCount) { 364 SkASSERT(kMove_Verb == fPathRef->atVerb(0)); 365 if (kLine_Verb == fPathRef->atVerb(1)) { 366 SkASSERT(2 == fPathRef->countPoints()); 367 if (line) { 368 const SkPoint* pts = fPathRef->points(); 369 line[0] = pts[0]; 370 line[1] = pts[1]; 371 } 372 return true; 373 } 374 } 375 return false; 376} 377 378/* 379 Determines if path is a rect by keeping track of changes in direction 380 and looking for a loop either clockwise or counterclockwise. 381 382 The direction is computed such that: 383 0: vertical up 384 1: horizontal left 385 2: vertical down 386 3: horizontal right 387 388A rectangle cycles up/right/down/left or up/left/down/right. 389 390The test fails if: 391 The path is closed, and followed by a line. 392 A second move creates a new endpoint. 393 A diagonal line is parsed. 394 There's more than four changes of direction. 395 There's a discontinuity on the line (e.g., a move in the middle) 396 The line reverses direction. 397 The path contains a quadratic or cubic. 398 The path contains fewer than four points. 399 *The rectangle doesn't complete a cycle. 400 *The final point isn't equal to the first point. 401 402 *These last two conditions we relax if we have a 3-edge path that would 403 form a rectangle if it were closed (as we do when we fill a path) 404 405It's OK if the path has: 406 Several colinear line segments composing a rectangle side. 407 Single points on the rectangle side. 408 409The direction takes advantage of the corners found since opposite sides 410must travel in opposite directions. 411 412FIXME: Allow colinear quads and cubics to be treated like lines. 413FIXME: If the API passes fill-only, return true if the filled stroke 414 is a rectangle, though the caller failed to close the path. 415 416 first,last,next direction state-machine: 417 0x1 is set if the segment is horizontal 418 0x2 is set if the segment is moving to the right or down 419 thus: 420 two directions are opposites iff (dirA ^ dirB) == 0x2 421 two directions are perpendicular iff (dirA ^ dirB) == 0x1 422 423 */ 424static int rect_make_dir(SkScalar dx, SkScalar dy) { 425 return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1); 426} 427bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** ptsPtr, 428 bool* isClosed, Direction* direction) const { 429 int corners = 0; 430 SkPoint first, last; 431 const SkPoint* pts = *ptsPtr; 432 const SkPoint* savePts = nullptr; 433 first.set(0, 0); 434 last.set(0, 0); 435 int firstDirection = 0; 436 int lastDirection = 0; 437 int nextDirection = 0; 438 bool closedOrMoved = false; 439 bool autoClose = false; 440 bool insertClose = false; 441 int verbCnt = fPathRef->countVerbs(); 442 while (*currVerb < verbCnt && (!allowPartial || !autoClose)) { 443 uint8_t verb = insertClose ? (uint8_t) kClose_Verb : fPathRef->atVerb(*currVerb); 444 switch (verb) { 445 case kClose_Verb: 446 savePts = pts; 447 pts = *ptsPtr; 448 autoClose = true; 449 insertClose = false; 450 case kLine_Verb: { 451 SkScalar left = last.fX; 452 SkScalar top = last.fY; 453 SkScalar right = pts->fX; 454 SkScalar bottom = pts->fY; 455 ++pts; 456 if (left != right && top != bottom) { 457 return false; // diagonal 458 } 459 if (left == right && top == bottom) { 460 break; // single point on side OK 461 } 462 nextDirection = rect_make_dir(right - left, bottom - top); 463 if (0 == corners) { 464 firstDirection = nextDirection; 465 first = last; 466 last = pts[-1]; 467 corners = 1; 468 closedOrMoved = false; 469 break; 470 } 471 if (closedOrMoved) { 472 return false; // closed followed by a line 473 } 474 if (autoClose && nextDirection == firstDirection) { 475 break; // colinear with first 476 } 477 closedOrMoved = autoClose; 478 if (lastDirection != nextDirection) { 479 if (++corners > 4) { 480 return false; // too many direction changes 481 } 482 } 483 last = pts[-1]; 484 if (lastDirection == nextDirection) { 485 break; // colinear segment 486 } 487 // Possible values for corners are 2, 3, and 4. 488 // When corners == 3, nextDirection opposes firstDirection. 489 // Otherwise, nextDirection at corner 2 opposes corner 4. 490 int turn = firstDirection ^ (corners - 1); 491 int directionCycle = 3 == corners ? 0 : nextDirection ^ turn; 492 if ((directionCycle ^ turn) != nextDirection) { 493 return false; // direction didn't follow cycle 494 } 495 break; 496 } 497 case kQuad_Verb: 498 case kConic_Verb: 499 case kCubic_Verb: 500 return false; // quadratic, cubic not allowed 501 case kMove_Verb: 502 if (allowPartial && !autoClose && firstDirection) { 503 insertClose = true; 504 *currVerb -= 1; // try move again afterwards 505 goto addMissingClose; 506 } 507 last = *pts++; 508 closedOrMoved = true; 509 break; 510 default: 511 SkDEBUGFAIL("unexpected verb"); 512 break; 513 } 514 *currVerb += 1; 515 lastDirection = nextDirection; 516addMissingClose: 517 ; 518 } 519 // Success if 4 corners and first point equals last 520 bool result = 4 == corners && (first == last || autoClose); 521 if (!result) { 522 // check if we are just an incomplete rectangle, in which case we can 523 // return true, but not claim to be closed. 524 // e.g. 525 // 3 sided rectangle 526 // 4 sided but the last edge is not long enough to reach the start 527 // 528 SkScalar closeX = first.x() - last.x(); 529 SkScalar closeY = first.y() - last.y(); 530 if (closeX && closeY) { 531 return false; // we're diagonal, abort (can we ever reach this?) 532 } 533 int closeDirection = rect_make_dir(closeX, closeY); 534 // make sure the close-segment doesn't double-back on itself 535 if (3 == corners || (4 == corners && closeDirection == lastDirection)) { 536 result = true; 537 autoClose = false; // we are not closed 538 } 539 } 540 if (savePts) { 541 *ptsPtr = savePts; 542 } 543 if (result && isClosed) { 544 *isClosed = autoClose; 545 } 546 if (result && direction) { 547 *direction = firstDirection == ((lastDirection + 1) & 3) ? kCCW_Direction : kCW_Direction; 548 } 549 return result; 550} 551 552bool SkPath::isRect(SkRect* rect, bool* isClosed, Direction* direction) const { 553 SkDEBUGCODE(this->validate();) 554 int currVerb = 0; 555 const SkPoint* pts = fPathRef->points(); 556 const SkPoint* first = pts; 557 if (!this->isRectContour(false, &currVerb, &pts, isClosed, direction)) { 558 return false; 559 } 560 if (rect) { 561 int32_t num = SkToS32(pts - first); 562 if (num) { 563 rect->set(first, num); 564 } else { 565 // 'pts' isn't updated for open rects 566 *rect = this->getBounds(); 567 } 568 } 569 return true; 570} 571 572bool SkPath::isNestedFillRects(SkRect rects[2], Direction dirs[2]) const { 573 SkDEBUGCODE(this->validate();) 574 int currVerb = 0; 575 const SkPoint* pts = fPathRef->points(); 576 const SkPoint* first = pts; 577 Direction testDirs[2]; 578 if (!isRectContour(true, &currVerb, &pts, nullptr, &testDirs[0])) { 579 return false; 580 } 581 const SkPoint* last = pts; 582 SkRect testRects[2]; 583 bool isClosed; 584 if (isRectContour(false, &currVerb, &pts, &isClosed, &testDirs[1])) { 585 testRects[0].set(first, SkToS32(last - first)); 586 if (!isClosed) { 587 pts = fPathRef->points() + fPathRef->countPoints(); 588 } 589 testRects[1].set(last, SkToS32(pts - last)); 590 if (testRects[0].contains(testRects[1])) { 591 if (rects) { 592 rects[0] = testRects[0]; 593 rects[1] = testRects[1]; 594 } 595 if (dirs) { 596 dirs[0] = testDirs[0]; 597 dirs[1] = testDirs[1]; 598 } 599 return true; 600 } 601 if (testRects[1].contains(testRects[0])) { 602 if (rects) { 603 rects[0] = testRects[1]; 604 rects[1] = testRects[0]; 605 } 606 if (dirs) { 607 dirs[0] = testDirs[1]; 608 dirs[1] = testDirs[0]; 609 } 610 return true; 611 } 612 } 613 return false; 614} 615 616int SkPath::countPoints() const { 617 return fPathRef->countPoints(); 618} 619 620int SkPath::getPoints(SkPoint dst[], int max) const { 621 SkDEBUGCODE(this->validate();) 622 623 SkASSERT(max >= 0); 624 SkASSERT(!max || dst); 625 int count = SkMin32(max, fPathRef->countPoints()); 626 sk_careful_memcpy(dst, fPathRef->points(), count * sizeof(SkPoint)); 627 return fPathRef->countPoints(); 628} 629 630SkPoint SkPath::getPoint(int index) const { 631 if ((unsigned)index < (unsigned)fPathRef->countPoints()) { 632 return fPathRef->atPoint(index); 633 } 634 return SkPoint::Make(0, 0); 635} 636 637int SkPath::countVerbs() const { 638 return fPathRef->countVerbs(); 639} 640 641static inline void copy_verbs_reverse(uint8_t* inorderDst, 642 const uint8_t* reversedSrc, 643 int count) { 644 for (int i = 0; i < count; ++i) { 645 inorderDst[i] = reversedSrc[~i]; 646 } 647} 648 649int SkPath::getVerbs(uint8_t dst[], int max) const { 650 SkDEBUGCODE(this->validate();) 651 652 SkASSERT(max >= 0); 653 SkASSERT(!max || dst); 654 int count = SkMin32(max, fPathRef->countVerbs()); 655 copy_verbs_reverse(dst, fPathRef->verbs(), count); 656 return fPathRef->countVerbs(); 657} 658 659bool SkPath::getLastPt(SkPoint* lastPt) const { 660 SkDEBUGCODE(this->validate();) 661 662 int count = fPathRef->countPoints(); 663 if (count > 0) { 664 if (lastPt) { 665 *lastPt = fPathRef->atPoint(count - 1); 666 } 667 return true; 668 } 669 if (lastPt) { 670 lastPt->set(0, 0); 671 } 672 return false; 673} 674 675void SkPath::setPt(int index, SkScalar x, SkScalar y) { 676 SkDEBUGCODE(this->validate();) 677 678 int count = fPathRef->countPoints(); 679 if (count <= index) { 680 return; 681 } else { 682 SkPathRef::Editor ed(&fPathRef); 683 ed.atPoint(index)->set(x, y); 684 } 685} 686 687void SkPath::setLastPt(SkScalar x, SkScalar y) { 688 SkDEBUGCODE(this->validate();) 689 690 int count = fPathRef->countPoints(); 691 if (count == 0) { 692 this->moveTo(x, y); 693 } else { 694 SkPathRef::Editor ed(&fPathRef); 695 ed.atPoint(count-1)->set(x, y); 696 } 697} 698 699void SkPath::setConvexity(Convexity c) { 700 if (fConvexity != c) { 701 fConvexity = c; 702 } 703} 704 705////////////////////////////////////////////////////////////////////////////// 706// Construction methods 707 708#define DIRTY_AFTER_EDIT \ 709 do { \ 710 fConvexity = kUnknown_Convexity; \ 711 fFirstDirection = SkPathPriv::kUnknown_FirstDirection; \ 712 } while (0) 713 714void SkPath::incReserve(U16CPU inc) { 715 SkDEBUGCODE(this->validate();) 716 SkPathRef::Editor(&fPathRef, inc, inc); 717 SkDEBUGCODE(this->validate();) 718} 719 720void SkPath::moveTo(SkScalar x, SkScalar y) { 721 SkDEBUGCODE(this->validate();) 722 723 SkPathRef::Editor ed(&fPathRef); 724 725 // remember our index 726 fLastMoveToIndex = fPathRef->countPoints(); 727 728 ed.growForVerb(kMove_Verb)->set(x, y); 729 730 DIRTY_AFTER_EDIT; 731} 732 733void SkPath::rMoveTo(SkScalar x, SkScalar y) { 734 SkPoint pt; 735 this->getLastPt(&pt); 736 this->moveTo(pt.fX + x, pt.fY + y); 737} 738 739void SkPath::injectMoveToIfNeeded() { 740 if (fLastMoveToIndex < 0) { 741 SkScalar x, y; 742 if (fPathRef->countVerbs() == 0) { 743 x = y = 0; 744 } else { 745 const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex); 746 x = pt.fX; 747 y = pt.fY; 748 } 749 this->moveTo(x, y); 750 } 751} 752 753void SkPath::lineTo(SkScalar x, SkScalar y) { 754 SkDEBUGCODE(this->validate();) 755 756 this->injectMoveToIfNeeded(); 757 758 SkPathRef::Editor ed(&fPathRef); 759 ed.growForVerb(kLine_Verb)->set(x, y); 760 761 DIRTY_AFTER_EDIT; 762} 763 764void SkPath::rLineTo(SkScalar x, SkScalar y) { 765 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). 766 SkPoint pt; 767 this->getLastPt(&pt); 768 this->lineTo(pt.fX + x, pt.fY + y); 769} 770 771void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { 772 SkDEBUGCODE(this->validate();) 773 774 this->injectMoveToIfNeeded(); 775 776 SkPathRef::Editor ed(&fPathRef); 777 SkPoint* pts = ed.growForVerb(kQuad_Verb); 778 pts[0].set(x1, y1); 779 pts[1].set(x2, y2); 780 781 DIRTY_AFTER_EDIT; 782} 783 784void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { 785 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). 786 SkPoint pt; 787 this->getLastPt(&pt); 788 this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2); 789} 790 791void SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 792 SkScalar w) { 793 // check for <= 0 or NaN with this test 794 if (!(w > 0)) { 795 this->lineTo(x2, y2); 796 } else if (!SkScalarIsFinite(w)) { 797 this->lineTo(x1, y1); 798 this->lineTo(x2, y2); 799 } else if (SK_Scalar1 == w) { 800 this->quadTo(x1, y1, x2, y2); 801 } else { 802 SkDEBUGCODE(this->validate();) 803 804 this->injectMoveToIfNeeded(); 805 806 SkPathRef::Editor ed(&fPathRef); 807 SkPoint* pts = ed.growForVerb(kConic_Verb, w); 808 pts[0].set(x1, y1); 809 pts[1].set(x2, y2); 810 811 DIRTY_AFTER_EDIT; 812 } 813} 814 815void SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2, 816 SkScalar w) { 817 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). 818 SkPoint pt; 819 this->getLastPt(&pt); 820 this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w); 821} 822 823void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 824 SkScalar x3, SkScalar y3) { 825 SkDEBUGCODE(this->validate();) 826 827 this->injectMoveToIfNeeded(); 828 829 SkPathRef::Editor ed(&fPathRef); 830 SkPoint* pts = ed.growForVerb(kCubic_Verb); 831 pts[0].set(x1, y1); 832 pts[1].set(x2, y2); 833 pts[2].set(x3, y3); 834 835 DIRTY_AFTER_EDIT; 836} 837 838void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 839 SkScalar x3, SkScalar y3) { 840 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). 841 SkPoint pt; 842 this->getLastPt(&pt); 843 this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2, 844 pt.fX + x3, pt.fY + y3); 845} 846 847void SkPath::close() { 848 SkDEBUGCODE(this->validate();) 849 850 int count = fPathRef->countVerbs(); 851 if (count > 0) { 852 switch (fPathRef->atVerb(count - 1)) { 853 case kLine_Verb: 854 case kQuad_Verb: 855 case kConic_Verb: 856 case kCubic_Verb: 857 case kMove_Verb: { 858 SkPathRef::Editor ed(&fPathRef); 859 ed.growForVerb(kClose_Verb); 860 break; 861 } 862 case kClose_Verb: 863 // don't add a close if it's the first verb or a repeat 864 break; 865 default: 866 SkDEBUGFAIL("unexpected verb"); 867 break; 868 } 869 } 870 871 // signal that we need a moveTo to follow us (unless we're done) 872#if 0 873 if (fLastMoveToIndex >= 0) { 874 fLastMoveToIndex = ~fLastMoveToIndex; 875 } 876#else 877 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1); 878#endif 879} 880 881/////////////////////////////////////////////////////////////////////////////// 882 883namespace { 884 885template <unsigned N> 886class PointIterator { 887public: 888 PointIterator(SkPath::Direction dir, unsigned startIndex) 889 : fCurrent(startIndex % N) 890 , fAdvance(dir == SkPath::kCW_Direction ? 1 : N - 1) { } 891 892 const SkPoint& current() const { 893 SkASSERT(fCurrent < N); 894 return fPts[fCurrent]; 895 } 896 897 const SkPoint& next() { 898 fCurrent = (fCurrent + fAdvance) % N; 899 return this->current(); 900 } 901 902protected: 903 SkPoint fPts[N]; 904 905private: 906 unsigned fCurrent; 907 unsigned fAdvance; 908}; 909 910class RectPointIterator : public PointIterator<4> { 911public: 912 RectPointIterator(const SkRect& rect, SkPath::Direction dir, unsigned startIndex) 913 : PointIterator(dir, startIndex) { 914 915 fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop); 916 fPts[1] = SkPoint::Make(rect.fRight, rect.fTop); 917 fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom); 918 fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom); 919 } 920}; 921 922class OvalPointIterator : public PointIterator<4> { 923public: 924 OvalPointIterator(const SkRect& oval, SkPath::Direction dir, unsigned startIndex) 925 : PointIterator(dir, startIndex) { 926 927 const SkScalar cx = oval.centerX(); 928 const SkScalar cy = oval.centerY(); 929 930 fPts[0] = SkPoint::Make(cx, oval.fTop); 931 fPts[1] = SkPoint::Make(oval.fRight, cy); 932 fPts[2] = SkPoint::Make(cx, oval.fBottom); 933 fPts[3] = SkPoint::Make(oval.fLeft, cy); 934 } 935}; 936 937class RRectPointIterator : public PointIterator<8> { 938public: 939 RRectPointIterator(const SkRRect& rrect, SkPath::Direction dir, unsigned startIndex) 940 : PointIterator(dir, startIndex) { 941 942 const SkRect& bounds = rrect.getBounds(); 943 const SkScalar L = bounds.fLeft; 944 const SkScalar T = bounds.fTop; 945 const SkScalar R = bounds.fRight; 946 const SkScalar B = bounds.fBottom; 947 948 fPts[0] = SkPoint::Make(L + rrect.radii(SkRRect::kUpperLeft_Corner).fX, T); 949 fPts[1] = SkPoint::Make(R - rrect.radii(SkRRect::kUpperRight_Corner).fX, T); 950 fPts[2] = SkPoint::Make(R, T + rrect.radii(SkRRect::kUpperRight_Corner).fY); 951 fPts[3] = SkPoint::Make(R, B - rrect.radii(SkRRect::kLowerRight_Corner).fY); 952 fPts[4] = SkPoint::Make(R - rrect.radii(SkRRect::kLowerRight_Corner).fX, B); 953 fPts[5] = SkPoint::Make(L + rrect.radii(SkRRect::kLowerLeft_Corner).fX, B); 954 fPts[6] = SkPoint::Make(L, B - rrect.radii(SkRRect::kLowerLeft_Corner).fY); 955 fPts[7] = SkPoint::Make(L, T + rrect.radii(SkRRect::kUpperLeft_Corner).fY); 956 } 957}; 958 959} // anonymous namespace 960 961static void assert_known_direction(int dir) { 962 SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir); 963} 964 965void SkPath::addRect(const SkRect& rect, Direction dir) { 966 this->addRect(rect, dir, 0); 967} 968 969void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right, 970 SkScalar bottom, Direction dir) { 971 this->addRect(SkRect::MakeLTRB(left, top, right, bottom), dir, 0); 972} 973 974void SkPath::addRect(const SkRect &rect, Direction dir, unsigned startIndex) { 975 assert_known_direction(dir); 976 fFirstDirection = this->hasOnlyMoveTos() ? 977 (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection; 978 SkAutoDisableDirectionCheck addc(this); 979 SkAutoPathBoundsUpdate apbu(this, rect); 980 981 SkDEBUGCODE(int initialVerbCount = this->countVerbs()); 982 983 const int kVerbs = 5; // moveTo + 3x lineTo + close 984 this->incReserve(kVerbs); 985 986 RectPointIterator iter(rect, dir, startIndex); 987 988 this->moveTo(iter.current()); 989 this->lineTo(iter.next()); 990 this->lineTo(iter.next()); 991 this->lineTo(iter.next()); 992 this->close(); 993 994 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs); 995} 996 997void SkPath::addPoly(const SkPoint pts[], int count, bool close) { 998 SkDEBUGCODE(this->validate();) 999 if (count <= 0) { 1000 return; 1001 } 1002 1003 fLastMoveToIndex = fPathRef->countPoints(); 1004 1005 // +close makes room for the extra kClose_Verb 1006 SkPathRef::Editor ed(&fPathRef, count+close, count); 1007 1008 ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY); 1009 if (count > 1) { 1010 SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1); 1011 memcpy(p, &pts[1], (count-1) * sizeof(SkPoint)); 1012 } 1013 1014 if (close) { 1015 ed.growForVerb(kClose_Verb); 1016 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1); 1017 } 1018 1019 DIRTY_AFTER_EDIT; 1020 SkDEBUGCODE(this->validate();) 1021} 1022 1023#include "SkGeometry.h" 1024 1025static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, 1026 SkPoint* pt) { 1027 if (0 == sweepAngle && (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 pt->set(oval.fRight, oval.centerY()); 1032 return true; 1033 } else if (0 == oval.width() && 0 == oval.height()) { 1034 // Chrome will sometimes create 0 radius round rects. Having degenerate 1035 // quad segments in the path prevents the path from being recognized as 1036 // a rect. 1037 // TODO: optimizing the case where only one of width or height is zero 1038 // should also be considered. This case, however, doesn't seem to be 1039 // as common as the single point case. 1040 pt->set(oval.fRight, oval.fTop); 1041 return true; 1042 } 1043 return false; 1044} 1045 1046// Return the unit vectors pointing at the start/stop points for the given start/sweep angles 1047// 1048static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle, 1049 SkVector* startV, SkVector* stopV, SkRotationDirection* dir) { 1050 startV->fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &startV->fX); 1051 stopV->fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle), &stopV->fX); 1052 1053 /* If the sweep angle is nearly (but less than) 360, then due to precision 1054 loss in radians-conversion and/or sin/cos, we may end up with coincident 1055 vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead 1056 of drawing a nearly complete circle (good). 1057 e.g. canvas.drawArc(0, 359.99, ...) 1058 -vs- canvas.drawArc(0, 359.9, ...) 1059 We try to detect this edge case, and tweak the stop vector 1060 */ 1061 if (*startV == *stopV) { 1062 SkScalar sw = SkScalarAbs(sweepAngle); 1063 if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) { 1064 SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle); 1065 // make a guess at a tiny angle (in radians) to tweak by 1066 SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle); 1067 // not sure how much will be enough, so we use a loop 1068 do { 1069 stopRad -= deltaRad; 1070 stopV->fY = SkScalarSinCos(stopRad, &stopV->fX); 1071 } while (*startV == *stopV); 1072 } 1073 } 1074 *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection; 1075} 1076 1077/** 1078 * If this returns 0, then the caller should just line-to the singlePt, else it should 1079 * ignore singlePt and append the specified number of conics. 1080 */ 1081static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop, 1082 SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc], 1083 SkPoint* singlePt) { 1084 SkMatrix matrix; 1085 1086 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height())); 1087 matrix.postTranslate(oval.centerX(), oval.centerY()); 1088 1089 int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics); 1090 if (0 == count) { 1091 matrix.mapXY(start.x(), start.y(), singlePt); 1092 } 1093 return count; 1094} 1095 1096void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[], 1097 Direction dir) { 1098 SkRRect rrect; 1099 rrect.setRectRadii(rect, (const SkVector*) radii); 1100 this->addRRect(rrect, dir); 1101} 1102 1103void SkPath::addRRect(const SkRRect& rrect, Direction dir) { 1104 // legacy start indices: 6 (CW) and 7(CCW) 1105 this->addRRect(rrect, dir, dir == kCW_Direction ? 6 : 7); 1106} 1107 1108void SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex) { 1109 assert_known_direction(dir); 1110 1111 if (rrect.isEmpty()) { 1112 return; 1113 } 1114 1115 bool isRRect = hasOnlyMoveTos(); 1116 const SkRect& bounds = rrect.getBounds(); 1117 1118 if (rrect.isRect()) { 1119 // degenerate(rect) => radii points are collapsing 1120 this->addRect(bounds, dir, (startIndex + 1) / 2); 1121 } else if (rrect.isOval()) { 1122 // degenerate(oval) => line points are collapsing 1123 this->addOval(bounds, dir, startIndex / 2); 1124 } else { 1125 fFirstDirection = this->hasOnlyMoveTos() ? 1126 (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection; 1127 1128 SkAutoPathBoundsUpdate apbu(this, bounds); 1129 SkAutoDisableDirectionCheck addc(this); 1130 1131 // we start with a conic on odd indices when moving CW vs. even indices when moving CCW 1132 const bool startsWithConic = ((startIndex & 1) == (dir == kCW_Direction)); 1133 const SkScalar weight = SK_ScalarRoot2Over2; 1134 1135 SkDEBUGCODE(int initialVerbCount = this->countVerbs()); 1136 const int kVerbs = startsWithConic 1137 ? 9 // moveTo + 4x conicTo + 3x lineTo + close 1138 : 10; // moveTo + 4x lineTo + 4x conicTo + close 1139 this->incReserve(kVerbs); 1140 1141 RRectPointIterator rrectIter(rrect, dir, startIndex); 1142 // Corner iterator indices follow the collapsed radii model, 1143 // adjusted such that the start pt is "behind" the radii start pt. 1144 const unsigned rectStartIndex = startIndex / 2 + (dir == kCW_Direction ? 0 : 1); 1145 RectPointIterator rectIter(bounds, dir, rectStartIndex); 1146 1147 this->moveTo(rrectIter.current()); 1148 if (startsWithConic) { 1149 for (unsigned i = 0; i < 3; ++i) { 1150 this->conicTo(rectIter.next(), rrectIter.next(), weight); 1151 this->lineTo(rrectIter.next()); 1152 } 1153 this->conicTo(rectIter.next(), rrectIter.next(), weight); 1154 // final lineTo handled by close(). 1155 } else { 1156 for (unsigned i = 0; i < 4; ++i) { 1157 this->lineTo(rrectIter.next()); 1158 this->conicTo(rectIter.next(), rrectIter.next(), weight); 1159 } 1160 } 1161 this->close(); 1162 1163 SkPathRef::Editor ed(&fPathRef); 1164 ed.setIsRRect(isRRect); 1165 1166 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs); 1167 } 1168 1169 SkDEBUGCODE(fPathRef->validate();) 1170} 1171 1172bool SkPath::hasOnlyMoveTos() const { 1173 int count = fPathRef->countVerbs(); 1174 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin(); 1175 for (int i = 0; i < count; ++i) { 1176 if (*verbs == kLine_Verb || 1177 *verbs == kQuad_Verb || 1178 *verbs == kConic_Verb || 1179 *verbs == kCubic_Verb) { 1180 return false; 1181 } 1182 ++verbs; 1183 } 1184 return true; 1185} 1186 1187bool SkPath::isZeroLength() const { 1188 int count = fPathRef->countPoints(); 1189 if (count < 2) { 1190 return true; 1191 } 1192 const SkPoint* pts = fPathRef.get()->points(); 1193 const SkPoint& first = *pts; 1194 for (int index = 1; index < count; ++index) { 1195 if (first != pts[index]) { 1196 return false; 1197 } 1198 } 1199 return true; 1200} 1201 1202void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, 1203 Direction dir) { 1204 assert_known_direction(dir); 1205 1206 if (rx < 0 || ry < 0) { 1207 SkErrorInternals::SetError( kInvalidArgument_SkError, 1208 "I got %f and %f as radii to SkPath::AddRoundRect, " 1209 "but negative radii are not allowed.", 1210 SkScalarToDouble(rx), SkScalarToDouble(ry) ); 1211 return; 1212 } 1213 1214 SkRRect rrect; 1215 rrect.setRectXY(rect, rx, ry); 1216 this->addRRect(rrect, dir); 1217} 1218 1219void SkPath::addOval(const SkRect& oval, Direction dir) { 1220 // legacy start index: 1 1221 this->addOval(oval, dir, 1); 1222} 1223 1224void SkPath::addOval(const SkRect &oval, Direction dir, unsigned startPointIndex) { 1225 assert_known_direction(dir); 1226 1227 /* If addOval() is called after previous moveTo(), 1228 this path is still marked as an oval. This is used to 1229 fit into WebKit's calling sequences. 1230 We can't simply check isEmpty() in this case, as additional 1231 moveTo() would mark the path non empty. 1232 */ 1233 bool isOval = hasOnlyMoveTos(); 1234 if (isOval) { 1235 fFirstDirection = (SkPathPriv::FirstDirection)dir; 1236 } else { 1237 fFirstDirection = SkPathPriv::kUnknown_FirstDirection; 1238 } 1239 1240 SkAutoDisableDirectionCheck addc(this); 1241 SkAutoPathBoundsUpdate apbu(this, oval); 1242 1243 SkDEBUGCODE(int initialVerbCount = this->countVerbs()); 1244 const int kVerbs = 6; // moveTo + 4x conicTo + close 1245 this->incReserve(kVerbs); 1246 1247 OvalPointIterator ovalIter(oval, dir, startPointIndex); 1248 // The corner iterator pts are tracking "behind" the oval/radii pts. 1249 RectPointIterator rectIter(oval, dir, startPointIndex + (dir == kCW_Direction ? 0 : 1)); 1250 const SkScalar weight = SK_ScalarRoot2Over2; 1251 1252 this->moveTo(ovalIter.current()); 1253 for (unsigned i = 0; i < 4; ++i) { 1254 this->conicTo(rectIter.next(), ovalIter.next(), weight); 1255 } 1256 this->close(); 1257 1258 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs); 1259 1260 SkPathRef::Editor ed(&fPathRef); 1261 1262 ed.setIsOval(isOval); 1263} 1264 1265void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) { 1266 if (r > 0) { 1267 this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir); 1268 } 1269} 1270 1271void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, 1272 bool forceMoveTo) { 1273 if (oval.width() < 0 || oval.height() < 0) { 1274 return; 1275 } 1276 1277 if (fPathRef->countVerbs() == 0) { 1278 forceMoveTo = true; 1279 } 1280 1281 SkPoint lonePt; 1282 if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) { 1283 forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt); 1284 return; 1285 } 1286 1287 SkVector startV, stopV; 1288 SkRotationDirection dir; 1289 angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir); 1290 1291 SkPoint singlePt; 1292 SkConic conics[SkConic::kMaxConicsForArc]; 1293 int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt); 1294 if (count) { 1295 this->incReserve(count * 2 + 1); 1296 const SkPoint& pt = conics[0].fPts[0]; 1297 forceMoveTo ? this->moveTo(pt) : this->lineTo(pt); 1298 for (int i = 0; i < count; ++i) { 1299 this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW); 1300 } 1301 } else { 1302 forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt); 1303 } 1304} 1305 1306// This converts the SVG arc to conics. 1307// Partly adapted from Niko's code in kdelibs/kdecore/svgicons. 1308// Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic() 1309// See also SVG implementation notes: 1310// http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter 1311// Note that arcSweep bool value is flipped from the original implementation. 1312void SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arcLarge, 1313 SkPath::Direction arcSweep, SkScalar x, SkScalar y) { 1314 this->injectMoveToIfNeeded(); 1315 SkPoint srcPts[2]; 1316 this->getLastPt(&srcPts[0]); 1317 // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto") 1318 // joining the endpoints. 1319 // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters 1320 if (!rx || !ry) { 1321 this->lineTo(x, y); 1322 return; 1323 } 1324 // If the current point and target point for the arc are identical, it should be treated as a 1325 // zero length path. This ensures continuity in animations. 1326 srcPts[1].set(x, y); 1327 if (srcPts[0] == srcPts[1]) { 1328 this->lineTo(x, y); 1329 return; 1330 } 1331 rx = SkScalarAbs(rx); 1332 ry = SkScalarAbs(ry); 1333 SkVector midPointDistance = srcPts[0] - srcPts[1]; 1334 midPointDistance *= 0.5f; 1335 1336 SkMatrix pointTransform; 1337 pointTransform.setRotate(-angle); 1338 1339 SkPoint transformedMidPoint; 1340 pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1); 1341 SkScalar squareRx = rx * rx; 1342 SkScalar squareRy = ry * ry; 1343 SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX; 1344 SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY; 1345 1346 // Check if the radii are big enough to draw the arc, scale radii if not. 1347 // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii 1348 SkScalar radiiScale = squareX / squareRx + squareY / squareRy; 1349 if (radiiScale > 1) { 1350 radiiScale = SkScalarSqrt(radiiScale); 1351 rx *= radiiScale; 1352 ry *= radiiScale; 1353 } 1354 1355 pointTransform.setScale(1 / rx, 1 / ry); 1356 pointTransform.preRotate(-angle); 1357 1358 SkPoint unitPts[2]; 1359 pointTransform.mapPoints(unitPts, srcPts, (int) SK_ARRAY_COUNT(unitPts)); 1360 SkVector delta = unitPts[1] - unitPts[0]; 1361 1362 SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY; 1363 SkScalar scaleFactorSquared = SkTMax(1 / d - 0.25f, 0.f); 1364 1365 SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared); 1366 if (SkToBool(arcSweep) != SkToBool(arcLarge)) { // flipped from the original implementation 1367 scaleFactor = -scaleFactor; 1368 } 1369 delta.scale(scaleFactor); 1370 SkPoint centerPoint = unitPts[0] + unitPts[1]; 1371 centerPoint *= 0.5f; 1372 centerPoint.offset(-delta.fY, delta.fX); 1373 unitPts[0] -= centerPoint; 1374 unitPts[1] -= centerPoint; 1375 SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX); 1376 SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX); 1377 SkScalar thetaArc = theta2 - theta1; 1378 if (thetaArc < 0 && !arcSweep) { // arcSweep flipped from the original implementation 1379 thetaArc += SK_ScalarPI * 2; 1380 } else if (thetaArc > 0 && arcSweep) { // arcSweep flipped from the original implementation 1381 thetaArc -= SK_ScalarPI * 2; 1382 } 1383 pointTransform.setRotate(angle); 1384 pointTransform.preScale(rx, ry); 1385 1386 int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (SK_ScalarPI / 2))); 1387 SkScalar thetaWidth = thetaArc / segments; 1388 SkScalar t = SkScalarTan(0.5f * thetaWidth); 1389 if (!SkScalarIsFinite(t)) { 1390 return; 1391 } 1392 SkScalar startTheta = theta1; 1393 SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf); 1394 for (int i = 0; i < segments; ++i) { 1395 SkScalar endTheta = startTheta + thetaWidth; 1396 SkScalar cosEndTheta, sinEndTheta = SkScalarSinCos(endTheta, &cosEndTheta); 1397 1398 unitPts[1].set(cosEndTheta, sinEndTheta); 1399 unitPts[1] += centerPoint; 1400 unitPts[0] = unitPts[1]; 1401 unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta); 1402 SkPoint mapped[2]; 1403 pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts)); 1404 this->conicTo(mapped[0], mapped[1], w); 1405 startTheta = endTheta; 1406 } 1407} 1408 1409void SkPath::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, SkPath::ArcSize largeArc, 1410 SkPath::Direction sweep, SkScalar dx, SkScalar dy) { 1411 SkPoint currentPoint; 1412 this->getLastPt(¤tPoint); 1413 this->arcTo(rx, ry, xAxisRotate, largeArc, sweep, currentPoint.fX + dx, currentPoint.fY + dy); 1414} 1415 1416void SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) { 1417 if (oval.isEmpty() || 0 == sweepAngle) { 1418 return; 1419 } 1420 1421 const SkScalar kFullCircleAngle = SkIntToScalar(360); 1422 1423 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) { 1424 this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction); 1425 } else { 1426 this->arcTo(oval, startAngle, sweepAngle, true); 1427 } 1428} 1429 1430/* 1431 Need to handle the case when the angle is sharp, and our computed end-points 1432 for the arc go behind pt1 and/or p2... 1433*/ 1434void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) { 1435 if (radius == 0) { 1436 this->lineTo(x1, y1); 1437 return; 1438 } 1439 1440 SkVector before, after; 1441 1442 // need to know our prev pt so we can construct tangent vectors 1443 { 1444 SkPoint start; 1445 this->getLastPt(&start); 1446 // Handle degenerate cases by adding a line to the first point and 1447 // bailing out. 1448 before.setNormalize(x1 - start.fX, y1 - start.fY); 1449 after.setNormalize(x2 - x1, y2 - y1); 1450 } 1451 1452 SkScalar cosh = SkPoint::DotProduct(before, after); 1453 SkScalar sinh = SkPoint::CrossProduct(before, after); 1454 1455 if (SkScalarNearlyZero(sinh)) { // angle is too tight 1456 this->lineTo(x1, y1); 1457 return; 1458 } 1459 1460 SkScalar dist = SkScalarAbs(SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh)); 1461 1462 SkScalar xx = x1 - SkScalarMul(dist, before.fX); 1463 SkScalar yy = y1 - SkScalarMul(dist, before.fY); 1464 after.setLength(dist); 1465 this->lineTo(xx, yy); 1466 SkScalar weight = SkScalarSqrt(SK_ScalarHalf + cosh * SK_ScalarHalf); 1467 this->conicTo(x1, y1, x1 + after.fX, y1 + after.fY, weight); 1468} 1469 1470/////////////////////////////////////////////////////////////////////////////// 1471 1472void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy, AddPathMode mode) { 1473 SkMatrix matrix; 1474 1475 matrix.setTranslate(dx, dy); 1476 this->addPath(path, matrix, mode); 1477} 1478 1479void SkPath::addPath(const SkPath& path, const SkMatrix& matrix, AddPathMode mode) { 1480 SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints()); 1481 1482 RawIter iter(path); 1483 SkPoint pts[4]; 1484 Verb verb; 1485 1486 SkMatrix::MapPtsProc proc = matrix.getMapPtsProc(); 1487 bool firstVerb = true; 1488 while ((verb = iter.next(pts)) != kDone_Verb) { 1489 switch (verb) { 1490 case kMove_Verb: 1491 proc(matrix, &pts[0], &pts[0], 1); 1492 if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) { 1493 injectMoveToIfNeeded(); // In case last contour is closed 1494 this->lineTo(pts[0]); 1495 } else { 1496 this->moveTo(pts[0]); 1497 } 1498 break; 1499 case kLine_Verb: 1500 proc(matrix, &pts[1], &pts[1], 1); 1501 this->lineTo(pts[1]); 1502 break; 1503 case kQuad_Verb: 1504 proc(matrix, &pts[1], &pts[1], 2); 1505 this->quadTo(pts[1], pts[2]); 1506 break; 1507 case kConic_Verb: 1508 proc(matrix, &pts[1], &pts[1], 2); 1509 this->conicTo(pts[1], pts[2], iter.conicWeight()); 1510 break; 1511 case kCubic_Verb: 1512 proc(matrix, &pts[1], &pts[1], 3); 1513 this->cubicTo(pts[1], pts[2], pts[3]); 1514 break; 1515 case kClose_Verb: 1516 this->close(); 1517 break; 1518 default: 1519 SkDEBUGFAIL("unknown verb"); 1520 } 1521 firstVerb = false; 1522 } 1523} 1524 1525/////////////////////////////////////////////////////////////////////////////// 1526 1527static int pts_in_verb(unsigned verb) { 1528 static const uint8_t gPtsInVerb[] = { 1529 1, // kMove 1530 1, // kLine 1531 2, // kQuad 1532 2, // kConic 1533 3, // kCubic 1534 0, // kClose 1535 0 // kDone 1536 }; 1537 1538 SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb)); 1539 return gPtsInVerb[verb]; 1540} 1541 1542// ignore the last point of the 1st contour 1543void SkPath::reversePathTo(const SkPath& path) { 1544 int i, vcount = path.fPathRef->countVerbs(); 1545 // exit early if the path is empty, or just has a moveTo. 1546 if (vcount < 2) { 1547 return; 1548 } 1549 1550 SkPathRef::Editor(&fPathRef, vcount, path.countPoints()); 1551 1552 const uint8_t* verbs = path.fPathRef->verbs(); 1553 const SkPoint* pts = path.fPathRef->points(); 1554 const SkScalar* conicWeights = path.fPathRef->conicWeights(); 1555 1556 SkASSERT(verbs[~0] == kMove_Verb); 1557 for (i = 1; i < vcount; ++i) { 1558 unsigned v = verbs[~i]; 1559 int n = pts_in_verb(v); 1560 if (n == 0) { 1561 break; 1562 } 1563 pts += n; 1564 conicWeights += (SkPath::kConic_Verb == v); 1565 } 1566 1567 while (--i > 0) { 1568 switch (verbs[~i]) { 1569 case kLine_Verb: 1570 this->lineTo(pts[-1].fX, pts[-1].fY); 1571 break; 1572 case kQuad_Verb: 1573 this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY); 1574 break; 1575 case kConic_Verb: 1576 this->conicTo(pts[-1], pts[-2], *--conicWeights); 1577 break; 1578 case kCubic_Verb: 1579 this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY, 1580 pts[-3].fX, pts[-3].fY); 1581 break; 1582 default: 1583 SkDEBUGFAIL("bad verb"); 1584 break; 1585 } 1586 pts -= pts_in_verb(verbs[~i]); 1587 } 1588} 1589 1590void SkPath::reverseAddPath(const SkPath& src) { 1591 SkPathRef::Editor ed(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->countVerbs()); 1592 1593 const SkPoint* pts = src.fPathRef->pointsEnd(); 1594 // we will iterator through src's verbs backwards 1595 const uint8_t* verbs = src.fPathRef->verbsMemBegin(); // points at the last verb 1596 const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the first verb 1597 const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd(); 1598 1599 bool needMove = true; 1600 bool needClose = false; 1601 while (verbs < verbsEnd) { 1602 uint8_t v = *(verbs++); 1603 int n = pts_in_verb(v); 1604 1605 if (needMove) { 1606 --pts; 1607 this->moveTo(pts->fX, pts->fY); 1608 needMove = false; 1609 } 1610 pts -= n; 1611 switch (v) { 1612 case kMove_Verb: 1613 if (needClose) { 1614 this->close(); 1615 needClose = false; 1616 } 1617 needMove = true; 1618 pts += 1; // so we see the point in "if (needMove)" above 1619 break; 1620 case kLine_Verb: 1621 this->lineTo(pts[0]); 1622 break; 1623 case kQuad_Verb: 1624 this->quadTo(pts[1], pts[0]); 1625 break; 1626 case kConic_Verb: 1627 this->conicTo(pts[1], pts[0], *--conicWeights); 1628 break; 1629 case kCubic_Verb: 1630 this->cubicTo(pts[2], pts[1], pts[0]); 1631 break; 1632 case kClose_Verb: 1633 needClose = true; 1634 break; 1635 default: 1636 SkDEBUGFAIL("unexpected verb"); 1637 } 1638 } 1639} 1640 1641/////////////////////////////////////////////////////////////////////////////// 1642 1643void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const { 1644 SkMatrix matrix; 1645 1646 matrix.setTranslate(dx, dy); 1647 this->transform(matrix, dst); 1648} 1649 1650static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4], 1651 int level = 2) { 1652 if (--level >= 0) { 1653 SkPoint tmp[7]; 1654 1655 SkChopCubicAtHalf(pts, tmp); 1656 subdivide_cubic_to(path, &tmp[0], level); 1657 subdivide_cubic_to(path, &tmp[3], level); 1658 } else { 1659 path->cubicTo(pts[1], pts[2], pts[3]); 1660 } 1661} 1662 1663void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { 1664 SkDEBUGCODE(this->validate();) 1665 if (dst == nullptr) { 1666 dst = (SkPath*)this; 1667 } 1668 1669 if (matrix.hasPerspective()) { 1670 SkPath tmp; 1671 tmp.fFillType = fFillType; 1672 1673 SkPath::Iter iter(*this, false); 1674 SkPoint pts[4]; 1675 SkPath::Verb verb; 1676 1677 while ((verb = iter.next(pts, false)) != kDone_Verb) { 1678 switch (verb) { 1679 case kMove_Verb: 1680 tmp.moveTo(pts[0]); 1681 break; 1682 case kLine_Verb: 1683 tmp.lineTo(pts[1]); 1684 break; 1685 case kQuad_Verb: 1686 // promote the quad to a conic 1687 tmp.conicTo(pts[1], pts[2], 1688 SkConic::TransformW(pts, SK_Scalar1, matrix)); 1689 break; 1690 case kConic_Verb: 1691 tmp.conicTo(pts[1], pts[2], 1692 SkConic::TransformW(pts, iter.conicWeight(), matrix)); 1693 break; 1694 case kCubic_Verb: 1695 subdivide_cubic_to(&tmp, pts); 1696 break; 1697 case kClose_Verb: 1698 tmp.close(); 1699 break; 1700 default: 1701 SkDEBUGFAIL("unknown verb"); 1702 break; 1703 } 1704 } 1705 1706 dst->swap(tmp); 1707 SkPathRef::Editor ed(&dst->fPathRef); 1708 matrix.mapPoints(ed.points(), ed.pathRef()->countPoints()); 1709 dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection; 1710 } else { 1711 SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix); 1712 1713 if (this != dst) { 1714 dst->fFillType = fFillType; 1715 dst->fConvexity = fConvexity; 1716 dst->fIsVolatile = fIsVolatile; 1717 } 1718 1719 if (SkPathPriv::kUnknown_FirstDirection == fFirstDirection) { 1720 dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection; 1721 } else { 1722 SkScalar det2x2 = 1723 SkScalarMul(matrix.get(SkMatrix::kMScaleX), matrix.get(SkMatrix::kMScaleY)) - 1724 SkScalarMul(matrix.get(SkMatrix::kMSkewX), matrix.get(SkMatrix::kMSkewY)); 1725 if (det2x2 < 0) { 1726 dst->fFirstDirection = SkPathPriv::OppositeFirstDirection( 1727 (SkPathPriv::FirstDirection)fFirstDirection.load()); 1728 } else if (det2x2 > 0) { 1729 dst->fFirstDirection = fFirstDirection.load(); 1730 } else { 1731 dst->fConvexity = kUnknown_Convexity; 1732 dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection; 1733 } 1734 } 1735 1736 SkDEBUGCODE(dst->validate();) 1737 } 1738} 1739 1740/////////////////////////////////////////////////////////////////////////////// 1741/////////////////////////////////////////////////////////////////////////////// 1742 1743enum SegmentState { 1744 kEmptyContour_SegmentState, // The current contour is empty. We may be 1745 // starting processing or we may have just 1746 // closed a contour. 1747 kAfterMove_SegmentState, // We have seen a move, but nothing else. 1748 kAfterPrimitive_SegmentState // We have seen a primitive but not yet 1749 // closed the path. Also the initial state. 1750}; 1751 1752SkPath::Iter::Iter() { 1753#ifdef SK_DEBUG 1754 fPts = nullptr; 1755 fConicWeights = nullptr; 1756 fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0; 1757 fForceClose = fCloseLine = false; 1758 fSegmentState = kEmptyContour_SegmentState; 1759#endif 1760 // need to init enough to make next() harmlessly return kDone_Verb 1761 fVerbs = nullptr; 1762 fVerbStop = nullptr; 1763 fNeedClose = false; 1764} 1765 1766SkPath::Iter::Iter(const SkPath& path, bool forceClose) { 1767 this->setPath(path, forceClose); 1768} 1769 1770void SkPath::Iter::setPath(const SkPath& path, bool forceClose) { 1771 fPts = path.fPathRef->points(); 1772 fVerbs = path.fPathRef->verbs(); 1773 fVerbStop = path.fPathRef->verbsMemBegin(); 1774 fConicWeights = path.fPathRef->conicWeights() - 1; // begin one behind 1775 fLastPt.fX = fLastPt.fY = 0; 1776 fMoveTo.fX = fMoveTo.fY = 0; 1777 fForceClose = SkToU8(forceClose); 1778 fNeedClose = false; 1779 fSegmentState = kEmptyContour_SegmentState; 1780} 1781 1782bool SkPath::Iter::isClosedContour() const { 1783 if (fVerbs == nullptr || fVerbs == fVerbStop) { 1784 return false; 1785 } 1786 if (fForceClose) { 1787 return true; 1788 } 1789 1790 const uint8_t* verbs = fVerbs; 1791 const uint8_t* stop = fVerbStop; 1792 1793 if (kMove_Verb == *(verbs - 1)) { 1794 verbs -= 1; // skip the initial moveto 1795 } 1796 1797 while (verbs > stop) { 1798 // verbs points one beyond the current verb, decrement first. 1799 unsigned v = *(--verbs); 1800 if (kMove_Verb == v) { 1801 break; 1802 } 1803 if (kClose_Verb == v) { 1804 return true; 1805 } 1806 } 1807 return false; 1808} 1809 1810SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) { 1811 SkASSERT(pts); 1812 if (fLastPt != fMoveTo) { 1813 // A special case: if both points are NaN, SkPoint::operation== returns 1814 // false, but the iterator expects that they are treated as the same. 1815 // (consider SkPoint is a 2-dimension float point). 1816 if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) || 1817 SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) { 1818 return kClose_Verb; 1819 } 1820 1821 pts[0] = fLastPt; 1822 pts[1] = fMoveTo; 1823 fLastPt = fMoveTo; 1824 fCloseLine = true; 1825 return kLine_Verb; 1826 } else { 1827 pts[0] = fMoveTo; 1828 return kClose_Verb; 1829 } 1830} 1831 1832const SkPoint& SkPath::Iter::cons_moveTo() { 1833 if (fSegmentState == kAfterMove_SegmentState) { 1834 // Set the first return pt to the move pt 1835 fSegmentState = kAfterPrimitive_SegmentState; 1836 return fMoveTo; 1837 } else { 1838 SkASSERT(fSegmentState == kAfterPrimitive_SegmentState); 1839 // Set the first return pt to the last pt of the previous primitive. 1840 return fPts[-1]; 1841 } 1842} 1843 1844void SkPath::Iter::consumeDegenerateSegments(bool exact) { 1845 // We need to step over anything that will not move the current draw point 1846 // forward before the next move is seen 1847 const uint8_t* lastMoveVerb = 0; 1848 const SkPoint* lastMovePt = 0; 1849 const SkScalar* lastMoveWeight = nullptr; 1850 SkPoint lastPt = fLastPt; 1851 while (fVerbs != fVerbStop) { 1852 unsigned verb = *(fVerbs - 1); // fVerbs is one beyond the current verb 1853 switch (verb) { 1854 case kMove_Verb: 1855 // Keep a record of this most recent move 1856 lastMoveVerb = fVerbs; 1857 lastMovePt = fPts; 1858 lastMoveWeight = fConicWeights; 1859 lastPt = fPts[0]; 1860 fVerbs--; 1861 fPts++; 1862 break; 1863 1864 case kClose_Verb: 1865 // A close when we are in a segment is always valid except when it 1866 // follows a move which follows a segment. 1867 if (fSegmentState == kAfterPrimitive_SegmentState && !lastMoveVerb) { 1868 return; 1869 } 1870 // A close at any other time must be ignored 1871 fVerbs--; 1872 break; 1873 1874 case kLine_Verb: 1875 if (!IsLineDegenerate(lastPt, fPts[0], exact)) { 1876 if (lastMoveVerb) { 1877 fVerbs = lastMoveVerb; 1878 fPts = lastMovePt; 1879 fConicWeights = lastMoveWeight; 1880 return; 1881 } 1882 return; 1883 } 1884 // Ignore this line and continue 1885 fVerbs--; 1886 fPts++; 1887 break; 1888 1889 case kConic_Verb: 1890 case kQuad_Verb: 1891 if (!IsQuadDegenerate(lastPt, fPts[0], fPts[1], exact)) { 1892 if (lastMoveVerb) { 1893 fVerbs = lastMoveVerb; 1894 fPts = lastMovePt; 1895 fConicWeights = lastMoveWeight; 1896 return; 1897 } 1898 return; 1899 } 1900 // Ignore this line and continue 1901 fVerbs--; 1902 fPts += 2; 1903 fConicWeights += (kConic_Verb == verb); 1904 break; 1905 1906 case kCubic_Verb: 1907 if (!IsCubicDegenerate(lastPt, fPts[0], fPts[1], fPts[2], exact)) { 1908 if (lastMoveVerb) { 1909 fVerbs = lastMoveVerb; 1910 fPts = lastMovePt; 1911 fConicWeights = lastMoveWeight; 1912 return; 1913 } 1914 return; 1915 } 1916 // Ignore this line and continue 1917 fVerbs--; 1918 fPts += 3; 1919 break; 1920 1921 default: 1922 SkDEBUGFAIL("Should never see kDone_Verb"); 1923 } 1924 } 1925} 1926 1927SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) { 1928 SkASSERT(ptsParam); 1929 1930 if (fVerbs == fVerbStop) { 1931 // Close the curve if requested and if there is some curve to close 1932 if (fNeedClose && fSegmentState == kAfterPrimitive_SegmentState) { 1933 if (kLine_Verb == this->autoClose(ptsParam)) { 1934 return kLine_Verb; 1935 } 1936 fNeedClose = false; 1937 return kClose_Verb; 1938 } 1939 return kDone_Verb; 1940 } 1941 1942 // fVerbs is one beyond the current verb, decrement first 1943 unsigned verb = *(--fVerbs); 1944 const SkPoint* SK_RESTRICT srcPts = fPts; 1945 SkPoint* SK_RESTRICT pts = ptsParam; 1946 1947 switch (verb) { 1948 case kMove_Verb: 1949 if (fNeedClose) { 1950 fVerbs++; // move back one verb 1951 verb = this->autoClose(pts); 1952 if (verb == kClose_Verb) { 1953 fNeedClose = false; 1954 } 1955 return (Verb)verb; 1956 } 1957 if (fVerbs == fVerbStop) { // might be a trailing moveto 1958 return kDone_Verb; 1959 } 1960 fMoveTo = *srcPts; 1961 pts[0] = *srcPts; 1962 srcPts += 1; 1963 fSegmentState = kAfterMove_SegmentState; 1964 fLastPt = fMoveTo; 1965 fNeedClose = fForceClose; 1966 break; 1967 case kLine_Verb: 1968 pts[0] = this->cons_moveTo(); 1969 pts[1] = srcPts[0]; 1970 fLastPt = srcPts[0]; 1971 fCloseLine = false; 1972 srcPts += 1; 1973 break; 1974 case kConic_Verb: 1975 fConicWeights += 1; 1976 // fall-through 1977 case kQuad_Verb: 1978 pts[0] = this->cons_moveTo(); 1979 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint)); 1980 fLastPt = srcPts[1]; 1981 srcPts += 2; 1982 break; 1983 case kCubic_Verb: 1984 pts[0] = this->cons_moveTo(); 1985 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint)); 1986 fLastPt = srcPts[2]; 1987 srcPts += 3; 1988 break; 1989 case kClose_Verb: 1990 verb = this->autoClose(pts); 1991 if (verb == kLine_Verb) { 1992 fVerbs++; // move back one verb 1993 } else { 1994 fNeedClose = false; 1995 fSegmentState = kEmptyContour_SegmentState; 1996 } 1997 fLastPt = fMoveTo; 1998 break; 1999 } 2000 fPts = srcPts; 2001 return (Verb)verb; 2002} 2003 2004/////////////////////////////////////////////////////////////////////////////// 2005 2006/* 2007 Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]] 2008*/ 2009 2010size_t SkPath::writeToMemory(void* storage) const { 2011 SkDEBUGCODE(this->validate();) 2012 2013 if (nullptr == storage) { 2014 const int byteCount = sizeof(int32_t) * 2 + fPathRef->writeSize(); 2015 return SkAlign4(byteCount); 2016 } 2017 2018 SkWBuffer buffer(storage); 2019 2020 int32_t packed = (fConvexity << kConvexity_SerializationShift) | 2021 (fFillType << kFillType_SerializationShift) | 2022 (fFirstDirection << kDirection_SerializationShift) | 2023 (fIsVolatile << kIsVolatile_SerializationShift) | 2024 kCurrent_Version; 2025 2026 buffer.write32(packed); 2027 buffer.write32(fLastMoveToIndex); 2028 2029 fPathRef->writeToBuffer(&buffer); 2030 2031 buffer.padToAlign4(); 2032 return buffer.pos(); 2033} 2034 2035size_t SkPath::readFromMemory(const void* storage, size_t length) { 2036 SkRBufferWithSizeCheck buffer(storage, length); 2037 2038 int32_t packed; 2039 if (!buffer.readS32(&packed)) { 2040 return 0; 2041 } 2042 2043 unsigned version = packed & 0xFF; 2044 if (version >= kPathPrivLastMoveToIndex_Version && !buffer.readS32(&fLastMoveToIndex)) { 2045 return 0; 2046 } 2047 2048 fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF; 2049 fFillType = (packed >> kFillType_SerializationShift) & 0xFF; 2050 uint8_t dir = (packed >> kDirection_SerializationShift) & 0x3; 2051 fIsVolatile = (packed >> kIsVolatile_SerializationShift) & 0x1; 2052 SkPathRef* pathRef = SkPathRef::CreateFromBuffer(&buffer); 2053 if (!pathRef) { 2054 return 0; 2055 } 2056 2057 fPathRef.reset(pathRef); 2058 SkDEBUGCODE(this->validate();) 2059 buffer.skipToAlign4(); 2060 2061 // compatibility check 2062 if (version < kPathPrivFirstDirection_Version) { 2063 switch (dir) { // old values 2064 case 0: 2065 fFirstDirection = SkPathPriv::kUnknown_FirstDirection; 2066 break; 2067 case 1: 2068 fFirstDirection = SkPathPriv::kCW_FirstDirection; 2069 break; 2070 case 2: 2071 fFirstDirection = SkPathPriv::kCCW_FirstDirection; 2072 break; 2073 default: 2074 SkASSERT(false); 2075 } 2076 } else { 2077 fFirstDirection = dir; 2078 } 2079 2080 return buffer.pos(); 2081} 2082 2083/////////////////////////////////////////////////////////////////////////////// 2084 2085#include "SkString.h" 2086#include "SkStringUtils.h" 2087#include "SkStream.h" 2088 2089static void append_params(SkString* str, const char label[], const SkPoint pts[], 2090 int count, SkScalarAsStringType strType, SkScalar conicWeight = -1) { 2091 str->append(label); 2092 str->append("("); 2093 2094 const SkScalar* values = &pts[0].fX; 2095 count *= 2; 2096 2097 for (int i = 0; i < count; ++i) { 2098 SkAppendScalar(str, values[i], strType); 2099 if (i < count - 1) { 2100 str->append(", "); 2101 } 2102 } 2103 if (conicWeight >= 0) { 2104 str->append(", "); 2105 SkAppendScalar(str, conicWeight, strType); 2106 } 2107 str->append(");"); 2108 if (kHex_SkScalarAsStringType == strType) { 2109 str->append(" // "); 2110 for (int i = 0; i < count; ++i) { 2111 SkAppendScalarDec(str, values[i]); 2112 if (i < count - 1) { 2113 str->append(", "); 2114 } 2115 } 2116 if (conicWeight >= 0) { 2117 str->append(", "); 2118 SkAppendScalarDec(str, conicWeight); 2119 } 2120 } 2121 str->append("\n"); 2122} 2123 2124void SkPath::dump(SkWStream* wStream, bool forceClose, bool dumpAsHex) const { 2125 SkScalarAsStringType asType = dumpAsHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType; 2126 Iter iter(*this, forceClose); 2127 SkPoint pts[4]; 2128 Verb verb; 2129 2130 if (!wStream) { 2131 SkDebugf("path: forceClose=%s\n", forceClose ? "true" : "false"); 2132 } 2133 SkString builder; 2134 2135 while ((verb = iter.next(pts, false)) != kDone_Verb) { 2136 switch (verb) { 2137 case kMove_Verb: 2138 append_params(&builder, "path.moveTo", &pts[0], 1, asType); 2139 break; 2140 case kLine_Verb: 2141 append_params(&builder, "path.lineTo", &pts[1], 1, asType); 2142 break; 2143 case kQuad_Verb: 2144 append_params(&builder, "path.quadTo", &pts[1], 2, asType); 2145 break; 2146 case kConic_Verb: 2147 append_params(&builder, "path.conicTo", &pts[1], 2, asType, iter.conicWeight()); 2148 break; 2149 case kCubic_Verb: 2150 append_params(&builder, "path.cubicTo", &pts[1], 3, asType); 2151 break; 2152 case kClose_Verb: 2153 builder.append("path.close();\n"); 2154 break; 2155 default: 2156 SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb); 2157 verb = kDone_Verb; // stop the loop 2158 break; 2159 } 2160 if (!wStream && builder.size()) { 2161 SkDebugf("%s", builder.c_str()); 2162 builder.reset(); 2163 } 2164 } 2165 if (wStream) { 2166 wStream->writeText(builder.c_str()); 2167 } 2168} 2169 2170void SkPath::dump() const { 2171 this->dump(nullptr, false, false); 2172} 2173 2174void SkPath::dumpHex() const { 2175 this->dump(nullptr, false, true); 2176} 2177 2178#ifdef SK_DEBUG 2179void SkPath::validate() const { 2180 SkASSERT((fFillType & ~3) == 0); 2181 2182#ifdef SK_DEBUG_PATH 2183 if (!fBoundsIsDirty) { 2184 SkRect bounds; 2185 2186 bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get()); 2187 SkASSERT(SkToBool(fIsFinite) == isFinite); 2188 2189 if (fPathRef->countPoints() <= 1) { 2190 // if we're empty, fBounds may be empty but translated, so we can't 2191 // necessarily compare to bounds directly 2192 // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will 2193 // be [2, 2, 2, 2] 2194 SkASSERT(bounds.isEmpty()); 2195 SkASSERT(fBounds.isEmpty()); 2196 } else { 2197 if (bounds.isEmpty()) { 2198 SkASSERT(fBounds.isEmpty()); 2199 } else { 2200 if (!fBounds.isEmpty()) { 2201 SkASSERT(fBounds.contains(bounds)); 2202 } 2203 } 2204 } 2205 } 2206#endif // SK_DEBUG_PATH 2207} 2208#endif // SK_DEBUG 2209 2210/////////////////////////////////////////////////////////////////////////////// 2211 2212static int sign(SkScalar x) { return x < 0; } 2213#define kValueNeverReturnedBySign 2 2214 2215enum DirChange { 2216 kLeft_DirChange, 2217 kRight_DirChange, 2218 kStraight_DirChange, 2219 kBackwards_DirChange, 2220 2221 kInvalid_DirChange 2222}; 2223 2224 2225static bool almost_equal(SkScalar compA, SkScalar compB) { 2226 // The error epsilon was empirically derived; worse case round rects 2227 // with a mid point outset by 2x float epsilon in tests had an error 2228 // of 12. 2229 const int epsilon = 16; 2230 if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) { 2231 return false; 2232 } 2233 // no need to check for small numbers because SkPath::Iter has removed degenerate values 2234 int aBits = SkFloatAs2sCompliment(compA); 2235 int bBits = SkFloatAs2sCompliment(compB); 2236 return aBits < bBits + epsilon && bBits < aBits + epsilon; 2237} 2238 2239static bool approximately_zero_when_compared_to(double x, double y) { 2240 return x == 0 || fabs(x) < fabs(y * FLT_EPSILON); 2241} 2242 2243 2244// only valid for a single contour 2245struct Convexicator { 2246 Convexicator() 2247 : fPtCount(0) 2248 , fConvexity(SkPath::kConvex_Convexity) 2249 , fFirstDirection(SkPathPriv::kUnknown_FirstDirection) 2250 , fIsFinite(true) 2251 , fIsCurve(false) { 2252 fExpectedDir = kInvalid_DirChange; 2253 // warnings 2254 fLastPt.set(0, 0); 2255 fCurrPt.set(0, 0); 2256 fLastVec.set(0, 0); 2257 fFirstVec.set(0, 0); 2258 2259 fDx = fDy = 0; 2260 fSx = fSy = kValueNeverReturnedBySign; 2261 } 2262 2263 SkPath::Convexity getConvexity() const { return fConvexity; } 2264 2265 /** The direction returned is only valid if the path is determined convex */ 2266 SkPathPriv::FirstDirection getFirstDirection() const { return fFirstDirection; } 2267 2268 void addPt(const SkPoint& pt) { 2269 if (SkPath::kConcave_Convexity == fConvexity || !fIsFinite) { 2270 return; 2271 } 2272 2273 if (0 == fPtCount) { 2274 fCurrPt = pt; 2275 ++fPtCount; 2276 } else { 2277 SkVector vec = pt - fCurrPt; 2278 SkScalar lengthSqd = vec.lengthSqd(); 2279 if (!SkScalarIsFinite(lengthSqd)) { 2280 fIsFinite = false; 2281 } else if (lengthSqd) { 2282 fPriorPt = fLastPt; 2283 fLastPt = fCurrPt; 2284 fCurrPt = pt; 2285 if (++fPtCount == 2) { 2286 fFirstVec = fLastVec = vec; 2287 } else { 2288 SkASSERT(fPtCount > 2); 2289 this->addVec(vec); 2290 } 2291 2292 int sx = sign(vec.fX); 2293 int sy = sign(vec.fY); 2294 fDx += (sx != fSx); 2295 fDy += (sy != fSy); 2296 fSx = sx; 2297 fSy = sy; 2298 2299 if (fDx > 3 || fDy > 3) { 2300 fConvexity = SkPath::kConcave_Convexity; 2301 } 2302 } 2303 } 2304 } 2305 2306 void close() { 2307 if (fPtCount > 2) { 2308 this->addVec(fFirstVec); 2309 } 2310 } 2311 2312 DirChange directionChange(const SkVector& curVec) { 2313 SkScalar cross = SkPoint::CrossProduct(fLastVec, curVec); 2314 2315 SkScalar smallest = SkTMin(fCurrPt.fX, SkTMin(fCurrPt.fY, SkTMin(fLastPt.fX, fLastPt.fY))); 2316 SkScalar largest = SkTMax(fCurrPt.fX, SkTMax(fCurrPt.fY, SkTMax(fLastPt.fX, fLastPt.fY))); 2317 largest = SkTMax(largest, -smallest); 2318 2319 if (!almost_equal(largest, largest + cross)) { 2320 int sign = SkScalarSignAsInt(cross); 2321 if (sign) { 2322 return (1 == sign) ? kRight_DirChange : kLeft_DirChange; 2323 } 2324 } 2325 2326 if (cross) { 2327 double dLastVecX = SkScalarToDouble(fLastPt.fX) - SkScalarToDouble(fPriorPt.fX); 2328 double dLastVecY = SkScalarToDouble(fLastPt.fY) - SkScalarToDouble(fPriorPt.fY); 2329 double dCurrVecX = SkScalarToDouble(fCurrPt.fX) - SkScalarToDouble(fLastPt.fX); 2330 double dCurrVecY = SkScalarToDouble(fCurrPt.fY) - SkScalarToDouble(fLastPt.fY); 2331 double dCross = dLastVecX * dCurrVecY - dLastVecY * dCurrVecX; 2332 if (!approximately_zero_when_compared_to(dCross, SkScalarToDouble(largest))) { 2333 int sign = SkScalarSignAsInt(SkDoubleToScalar(dCross)); 2334 if (sign) { 2335 return (1 == sign) ? kRight_DirChange : kLeft_DirChange; 2336 } 2337 } 2338 } 2339 2340 if (!SkScalarNearlyZero(fLastVec.lengthSqd(), SK_ScalarNearlyZero*SK_ScalarNearlyZero) && 2341 !SkScalarNearlyZero(curVec.lengthSqd(), SK_ScalarNearlyZero*SK_ScalarNearlyZero) && 2342 fLastVec.dot(curVec) < 0.0f) { 2343 return kBackwards_DirChange; 2344 } 2345 2346 return kStraight_DirChange; 2347 } 2348 2349 2350 bool isFinite() const { 2351 return fIsFinite; 2352 } 2353 2354 void setCurve(bool isCurve) { 2355 fIsCurve = isCurve; 2356 } 2357 2358private: 2359 void addVec(const SkVector& vec) { 2360 SkASSERT(vec.fX || vec.fY); 2361 DirChange dir = this->directionChange(vec); 2362 switch (dir) { 2363 case kLeft_DirChange: // fall through 2364 case kRight_DirChange: 2365 if (kInvalid_DirChange == fExpectedDir) { 2366 fExpectedDir = dir; 2367 fFirstDirection = (kRight_DirChange == dir) ? SkPathPriv::kCW_FirstDirection 2368 : SkPathPriv::kCCW_FirstDirection; 2369 } else if (dir != fExpectedDir) { 2370 fConvexity = SkPath::kConcave_Convexity; 2371 fFirstDirection = SkPathPriv::kUnknown_FirstDirection; 2372 } 2373 fLastVec = vec; 2374 break; 2375 case kStraight_DirChange: 2376 break; 2377 case kBackwards_DirChange: 2378 if (fIsCurve) { 2379 fConvexity = SkPath::kConcave_Convexity; 2380 fFirstDirection = SkPathPriv::kUnknown_FirstDirection; 2381 } 2382 fLastVec = vec; 2383 break; 2384 case kInvalid_DirChange: 2385 SkFAIL("Use of invalid direction change flag"); 2386 break; 2387 } 2388 } 2389 2390 SkPoint fPriorPt; 2391 SkPoint fLastPt; 2392 SkPoint fCurrPt; 2393 // fLastVec does not necessarily start at fLastPt. We only advance it when the cross product 2394 // value with the current vec is deemed to be of a significant value. 2395 SkVector fLastVec, fFirstVec; 2396 int fPtCount; // non-degenerate points 2397 DirChange fExpectedDir; 2398 SkPath::Convexity fConvexity; 2399 SkPathPriv::FirstDirection fFirstDirection; 2400 int fDx, fDy, fSx, fSy; 2401 bool fIsFinite; 2402 bool fIsCurve; 2403}; 2404 2405SkPath::Convexity SkPath::internalGetConvexity() const { 2406 SkASSERT(kUnknown_Convexity == fConvexity); 2407 SkPoint pts[4]; 2408 SkPath::Verb verb; 2409 SkPath::Iter iter(*this, true); 2410 2411 int contourCount = 0; 2412 int count; 2413 Convexicator state; 2414 2415 if (!isFinite()) { 2416 return kUnknown_Convexity; 2417 } 2418 while ((verb = iter.next(pts, true, true)) != SkPath::kDone_Verb) { 2419 switch (verb) { 2420 case kMove_Verb: 2421 if (++contourCount > 1) { 2422 fConvexity = kConcave_Convexity; 2423 return kConcave_Convexity; 2424 } 2425 pts[1] = pts[0]; 2426 // fall through 2427 case kLine_Verb: 2428 count = 1; 2429 state.setCurve(false); 2430 break; 2431 case kQuad_Verb: 2432 // fall through 2433 case kConic_Verb: 2434 // fall through 2435 case kCubic_Verb: 2436 count = 2 + (kCubic_Verb == verb); 2437 // As an additional enhancement, this could set curve true only 2438 // if the curve is nonlinear 2439 state.setCurve(true); 2440 break; 2441 case kClose_Verb: 2442 state.setCurve(false); 2443 state.close(); 2444 count = 0; 2445 break; 2446 default: 2447 SkDEBUGFAIL("bad verb"); 2448 fConvexity = kConcave_Convexity; 2449 return kConcave_Convexity; 2450 } 2451 2452 for (int i = 1; i <= count; i++) { 2453 state.addPt(pts[i]); 2454 } 2455 // early exit 2456 if (!state.isFinite()) { 2457 return kUnknown_Convexity; 2458 } 2459 if (kConcave_Convexity == state.getConvexity()) { 2460 fConvexity = kConcave_Convexity; 2461 return kConcave_Convexity; 2462 } 2463 } 2464 fConvexity = state.getConvexity(); 2465 if (kConvex_Convexity == fConvexity && SkPathPriv::kUnknown_FirstDirection == fFirstDirection) { 2466 fFirstDirection = state.getFirstDirection(); 2467 } 2468 return static_cast<Convexity>(fConvexity); 2469} 2470 2471/////////////////////////////////////////////////////////////////////////////// 2472 2473class ContourIter { 2474public: 2475 ContourIter(const SkPathRef& pathRef); 2476 2477 bool done() const { return fDone; } 2478 // if !done() then these may be called 2479 int count() const { return fCurrPtCount; } 2480 const SkPoint* pts() const { return fCurrPt; } 2481 void next(); 2482 2483private: 2484 int fCurrPtCount; 2485 const SkPoint* fCurrPt; 2486 const uint8_t* fCurrVerb; 2487 const uint8_t* fStopVerbs; 2488 const SkScalar* fCurrConicWeight; 2489 bool fDone; 2490 SkDEBUGCODE(int fContourCounter;) 2491}; 2492 2493ContourIter::ContourIter(const SkPathRef& pathRef) { 2494 fStopVerbs = pathRef.verbsMemBegin(); 2495 fDone = false; 2496 fCurrPt = pathRef.points(); 2497 fCurrVerb = pathRef.verbs(); 2498 fCurrConicWeight = pathRef.conicWeights(); 2499 fCurrPtCount = 0; 2500 SkDEBUGCODE(fContourCounter = 0;) 2501 this->next(); 2502} 2503 2504void ContourIter::next() { 2505 if (fCurrVerb <= fStopVerbs) { 2506 fDone = true; 2507 } 2508 if (fDone) { 2509 return; 2510 } 2511 2512 // skip pts of prev contour 2513 fCurrPt += fCurrPtCount; 2514 2515 SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]); 2516 int ptCount = 1; // moveTo 2517 const uint8_t* verbs = fCurrVerb; 2518 2519 for (--verbs; verbs > fStopVerbs; --verbs) { 2520 switch (verbs[~0]) { 2521 case SkPath::kMove_Verb: 2522 goto CONTOUR_END; 2523 case SkPath::kLine_Verb: 2524 ptCount += 1; 2525 break; 2526 case SkPath::kConic_Verb: 2527 fCurrConicWeight += 1; 2528 // fall-through 2529 case SkPath::kQuad_Verb: 2530 ptCount += 2; 2531 break; 2532 case SkPath::kCubic_Verb: 2533 ptCount += 3; 2534 break; 2535 case SkPath::kClose_Verb: 2536 break; 2537 default: 2538 SkDEBUGFAIL("unexpected verb"); 2539 break; 2540 } 2541 } 2542CONTOUR_END: 2543 fCurrPtCount = ptCount; 2544 fCurrVerb = verbs; 2545 SkDEBUGCODE(++fContourCounter;) 2546} 2547 2548// returns cross product of (p1 - p0) and (p2 - p0) 2549static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) { 2550 SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0); 2551 // We may get 0 when the above subtracts underflow. We expect this to be 2552 // very rare and lazily promote to double. 2553 if (0 == cross) { 2554 double p0x = SkScalarToDouble(p0.fX); 2555 double p0y = SkScalarToDouble(p0.fY); 2556 2557 double p1x = SkScalarToDouble(p1.fX); 2558 double p1y = SkScalarToDouble(p1.fY); 2559 2560 double p2x = SkScalarToDouble(p2.fX); 2561 double p2y = SkScalarToDouble(p2.fY); 2562 2563 cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) - 2564 (p1y - p0y) * (p2x - p0x)); 2565 2566 } 2567 return cross; 2568} 2569 2570// Returns the first pt with the maximum Y coordinate 2571static int find_max_y(const SkPoint pts[], int count) { 2572 SkASSERT(count > 0); 2573 SkScalar max = pts[0].fY; 2574 int firstIndex = 0; 2575 for (int i = 1; i < count; ++i) { 2576 SkScalar y = pts[i].fY; 2577 if (y > max) { 2578 max = y; 2579 firstIndex = i; 2580 } 2581 } 2582 return firstIndex; 2583} 2584 2585static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) { 2586 int i = index; 2587 for (;;) { 2588 i = (i + inc) % n; 2589 if (i == index) { // we wrapped around, so abort 2590 break; 2591 } 2592 if (pts[index] != pts[i]) { // found a different point, success! 2593 break; 2594 } 2595 } 2596 return i; 2597} 2598 2599/** 2600 * Starting at index, and moving forward (incrementing), find the xmin and 2601 * xmax of the contiguous points that have the same Y. 2602 */ 2603static int find_min_max_x_at_y(const SkPoint pts[], int index, int n, 2604 int* maxIndexPtr) { 2605 const SkScalar y = pts[index].fY; 2606 SkScalar min = pts[index].fX; 2607 SkScalar max = min; 2608 int minIndex = index; 2609 int maxIndex = index; 2610 for (int i = index + 1; i < n; ++i) { 2611 if (pts[i].fY != y) { 2612 break; 2613 } 2614 SkScalar x = pts[i].fX; 2615 if (x < min) { 2616 min = x; 2617 minIndex = i; 2618 } else if (x > max) { 2619 max = x; 2620 maxIndex = i; 2621 } 2622 } 2623 *maxIndexPtr = maxIndex; 2624 return minIndex; 2625} 2626 2627static void crossToDir(SkScalar cross, SkPathPriv::FirstDirection* dir) { 2628 *dir = cross > 0 ? SkPathPriv::kCW_FirstDirection : SkPathPriv::kCCW_FirstDirection; 2629} 2630 2631/* 2632 * We loop through all contours, and keep the computed cross-product of the 2633 * contour that contained the global y-max. If we just look at the first 2634 * contour, we may find one that is wound the opposite way (correctly) since 2635 * it is the interior of a hole (e.g. 'o'). Thus we must find the contour 2636 * that is outer most (or at least has the global y-max) before we can consider 2637 * its cross product. 2638 */ 2639bool SkPathPriv::CheapComputeFirstDirection(const SkPath& path, FirstDirection* dir) { 2640 if (kUnknown_FirstDirection != path.fFirstDirection.load()) { 2641 *dir = static_cast<FirstDirection>(path.fFirstDirection.load()); 2642 return true; 2643 } 2644 2645 // don't want to pay the cost for computing this if it 2646 // is unknown, so we don't call isConvex() 2647 if (SkPath::kConvex_Convexity == path.getConvexityOrUnknown()) { 2648 SkASSERT(kUnknown_FirstDirection == path.fFirstDirection); 2649 *dir = static_cast<FirstDirection>(path.fFirstDirection.load()); 2650 return false; 2651 } 2652 2653 ContourIter iter(*path.fPathRef.get()); 2654 2655 // initialize with our logical y-min 2656 SkScalar ymax = path.getBounds().fTop; 2657 SkScalar ymaxCross = 0; 2658 2659 for (; !iter.done(); iter.next()) { 2660 int n = iter.count(); 2661 if (n < 3) { 2662 continue; 2663 } 2664 2665 const SkPoint* pts = iter.pts(); 2666 SkScalar cross = 0; 2667 int index = find_max_y(pts, n); 2668 if (pts[index].fY < ymax) { 2669 continue; 2670 } 2671 2672 // If there is more than 1 distinct point at the y-max, we take the 2673 // x-min and x-max of them and just subtract to compute the dir. 2674 if (pts[(index + 1) % n].fY == pts[index].fY) { 2675 int maxIndex; 2676 int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex); 2677 if (minIndex == maxIndex) { 2678 goto TRY_CROSSPROD; 2679 } 2680 SkASSERT(pts[minIndex].fY == pts[index].fY); 2681 SkASSERT(pts[maxIndex].fY == pts[index].fY); 2682 SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX); 2683 // we just subtract the indices, and let that auto-convert to 2684 // SkScalar, since we just want - or + to signal the direction. 2685 cross = minIndex - maxIndex; 2686 } else { 2687 TRY_CROSSPROD: 2688 // Find a next and prev index to use for the cross-product test, 2689 // but we try to find pts that form non-zero vectors from pts[index] 2690 // 2691 // Its possible that we can't find two non-degenerate vectors, so 2692 // we have to guard our search (e.g. all the pts could be in the 2693 // same place). 2694 2695 // we pass n - 1 instead of -1 so we don't foul up % operator by 2696 // passing it a negative LH argument. 2697 int prev = find_diff_pt(pts, index, n, n - 1); 2698 if (prev == index) { 2699 // completely degenerate, skip to next contour 2700 continue; 2701 } 2702 int next = find_diff_pt(pts, index, n, 1); 2703 SkASSERT(next != index); 2704 cross = cross_prod(pts[prev], pts[index], pts[next]); 2705 // if we get a zero and the points are horizontal, then we look at the spread in 2706 // x-direction. We really should continue to walk away from the degeneracy until 2707 // there is a divergence. 2708 if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) { 2709 // construct the subtract so we get the correct Direction below 2710 cross = pts[index].fX - pts[next].fX; 2711 } 2712 } 2713 2714 if (cross) { 2715 // record our best guess so far 2716 ymax = pts[index].fY; 2717 ymaxCross = cross; 2718 } 2719 } 2720 if (ymaxCross) { 2721 crossToDir(ymaxCross, dir); 2722 path.fFirstDirection = *dir; 2723 return true; 2724 } else { 2725 return false; 2726 } 2727} 2728 2729/////////////////////////////////////////////////////////////////////////////// 2730 2731static bool between(SkScalar a, SkScalar b, SkScalar c) { 2732 SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0) 2733 || (SkScalarNearlyZero(a) && SkScalarNearlyZero(b) && SkScalarNearlyZero(c))); 2734 return (a - b) * (c - b) <= 0; 2735} 2736 2737static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C, 2738 SkScalar D, SkScalar t) { 2739 return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D); 2740} 2741 2742static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3, 2743 SkScalar t) { 2744 SkScalar A = c3 + 3*(c1 - c2) - c0; 2745 SkScalar B = 3*(c2 - c1 - c1 + c0); 2746 SkScalar C = 3*(c1 - c0); 2747 SkScalar D = c0; 2748 return eval_cubic_coeff(A, B, C, D, t); 2749} 2750 2751template <size_t N> static void find_minmax(const SkPoint pts[], 2752 SkScalar* minPtr, SkScalar* maxPtr) { 2753 SkScalar min, max; 2754 min = max = pts[0].fX; 2755 for (size_t i = 1; i < N; ++i) { 2756 min = SkMinScalar(min, pts[i].fX); 2757 max = SkMaxScalar(max, pts[i].fX); 2758 } 2759 *minPtr = min; 2760 *maxPtr = max; 2761} 2762 2763static bool checkOnCurve(SkScalar x, SkScalar y, const SkPoint& start, const SkPoint& end) { 2764 if (start.fY == end.fY) { 2765 return between(start.fX, x, end.fX) && x != end.fX; 2766 } else { 2767 return x == start.fX && y == start.fY; 2768 } 2769} 2770 2771static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2772 SkScalar y0 = pts[0].fY; 2773 SkScalar y3 = pts[3].fY; 2774 2775 int dir = 1; 2776 if (y0 > y3) { 2777 SkTSwap(y0, y3); 2778 dir = -1; 2779 } 2780 if (y < y0 || y > y3) { 2781 return 0; 2782 } 2783 if (checkOnCurve(x, y, pts[0], pts[3])) { 2784 *onCurveCount += 1; 2785 return 0; 2786 } 2787 if (y == y3) { 2788 return 0; 2789 } 2790 2791 // quickreject or quickaccept 2792 SkScalar min, max; 2793 find_minmax<4>(pts, &min, &max); 2794 if (x < min) { 2795 return 0; 2796 } 2797 if (x > max) { 2798 return dir; 2799 } 2800 2801 // compute the actual x(t) value 2802 SkScalar t; 2803 SkAssertResult(SkCubicClipper::ChopMonoAtY(pts, y, &t)); 2804 SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t); 2805 if (SkScalarNearlyEqual(xt, x)) { 2806 if (x != pts[3].fX || y != pts[3].fY) { // don't test end points; they're start points 2807 *onCurveCount += 1; 2808 return 0; 2809 } 2810 } 2811 return xt < x ? dir : 0; 2812} 2813 2814static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2815 SkPoint dst[10]; 2816 int n = SkChopCubicAtYExtrema(pts, dst); 2817 int w = 0; 2818 for (int i = 0; i <= n; ++i) { 2819 w += winding_mono_cubic(&dst[i * 3], x, y, onCurveCount); 2820 } 2821 return w; 2822} 2823 2824static double conic_eval_numerator(const SkScalar src[], SkScalar w, SkScalar t) { 2825 SkASSERT(src); 2826 SkASSERT(t >= 0 && t <= 1); 2827 SkScalar src2w = src[2] * w; 2828 SkScalar C = src[0]; 2829 SkScalar A = src[4] - 2 * src2w + C; 2830 SkScalar B = 2 * (src2w - C); 2831 return (A * t + B) * t + C; 2832} 2833 2834 2835static double conic_eval_denominator(SkScalar w, SkScalar t) { 2836 SkScalar B = 2 * (w - 1); 2837 SkScalar C = 1; 2838 SkScalar A = -B; 2839 return (A * t + B) * t + C; 2840} 2841 2842static int winding_mono_conic(const SkConic& conic, SkScalar x, SkScalar y, int* onCurveCount) { 2843 const SkPoint* pts = conic.fPts; 2844 SkScalar y0 = pts[0].fY; 2845 SkScalar y2 = pts[2].fY; 2846 2847 int dir = 1; 2848 if (y0 > y2) { 2849 SkTSwap(y0, y2); 2850 dir = -1; 2851 } 2852 if (y < y0 || y > y2) { 2853 return 0; 2854 } 2855 if (checkOnCurve(x, y, pts[0], pts[2])) { 2856 *onCurveCount += 1; 2857 return 0; 2858 } 2859 if (y == y2) { 2860 return 0; 2861 } 2862 2863 SkScalar roots[2]; 2864 SkScalar A = pts[2].fY; 2865 SkScalar B = pts[1].fY * conic.fW - y * conic.fW + y; 2866 SkScalar C = pts[0].fY; 2867 A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept) 2868 B -= C; // B = b*w - w * yCept + yCept - a 2869 C -= y; 2870 int n = SkFindUnitQuadRoots(A, 2 * B, C, roots); 2871 SkASSERT(n <= 1); 2872 SkScalar xt; 2873 if (0 == n) { 2874 // zero roots are returned only when y0 == y 2875 // Need [0] if dir == 1 2876 // and [2] if dir == -1 2877 xt = pts[1 - dir].fX; 2878 } else { 2879 SkScalar t = roots[0]; 2880 xt = conic_eval_numerator(&pts[0].fX, conic.fW, t) / conic_eval_denominator(conic.fW, t); 2881 } 2882 if (SkScalarNearlyEqual(xt, x)) { 2883 if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points 2884 *onCurveCount += 1; 2885 return 0; 2886 } 2887 } 2888 return xt < x ? dir : 0; 2889} 2890 2891static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) { 2892 // return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0; 2893 if (y0 == y1) { 2894 return true; 2895 } 2896 if (y0 < y1) { 2897 return y1 <= y2; 2898 } else { 2899 return y1 >= y2; 2900 } 2901} 2902 2903static int winding_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar weight, 2904 int* onCurveCount) { 2905 SkConic conic(pts, weight); 2906 SkConic chopped[2]; 2907 // If the data points are very large, the conic may not be monotonic but may also 2908 // fail to chop. Then, the chopper does not split the original conic in two. 2909 bool isMono = is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY) || !conic.chopAtYExtrema(chopped); 2910 int w = winding_mono_conic(isMono ? conic : chopped[0], x, y, onCurveCount); 2911 if (!isMono) { 2912 w += winding_mono_conic(chopped[1], x, y, onCurveCount); 2913 } 2914 return w; 2915} 2916 2917static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2918 SkScalar y0 = pts[0].fY; 2919 SkScalar y2 = pts[2].fY; 2920 2921 int dir = 1; 2922 if (y0 > y2) { 2923 SkTSwap(y0, y2); 2924 dir = -1; 2925 } 2926 if (y < y0 || y > y2) { 2927 return 0; 2928 } 2929 if (checkOnCurve(x, y, pts[0], pts[2])) { 2930 *onCurveCount += 1; 2931 return 0; 2932 } 2933 if (y == y2) { 2934 return 0; 2935 } 2936 // bounds check on X (not required. is it faster?) 2937#if 0 2938 if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) { 2939 return 0; 2940 } 2941#endif 2942 2943 SkScalar roots[2]; 2944 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, 2945 2 * (pts[1].fY - pts[0].fY), 2946 pts[0].fY - y, 2947 roots); 2948 SkASSERT(n <= 1); 2949 SkScalar xt; 2950 if (0 == n) { 2951 // zero roots are returned only when y0 == y 2952 // Need [0] if dir == 1 2953 // and [2] if dir == -1 2954 xt = pts[1 - dir].fX; 2955 } else { 2956 SkScalar t = roots[0]; 2957 SkScalar C = pts[0].fX; 2958 SkScalar A = pts[2].fX - 2 * pts[1].fX + C; 2959 SkScalar B = 2 * (pts[1].fX - C); 2960 xt = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); 2961 } 2962 if (SkScalarNearlyEqual(xt, x)) { 2963 if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points 2964 *onCurveCount += 1; 2965 return 0; 2966 } 2967 } 2968 return xt < x ? dir : 0; 2969} 2970 2971static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2972 SkPoint dst[5]; 2973 int n = 0; 2974 2975 if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) { 2976 n = SkChopQuadAtYExtrema(pts, dst); 2977 pts = dst; 2978 } 2979 int w = winding_mono_quad(pts, x, y, onCurveCount); 2980 if (n > 0) { 2981 w += winding_mono_quad(&pts[2], x, y, onCurveCount); 2982 } 2983 return w; 2984} 2985 2986static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2987 SkScalar x0 = pts[0].fX; 2988 SkScalar y0 = pts[0].fY; 2989 SkScalar x1 = pts[1].fX; 2990 SkScalar y1 = pts[1].fY; 2991 2992 SkScalar dy = y1 - y0; 2993 2994 int dir = 1; 2995 if (y0 > y1) { 2996 SkTSwap(y0, y1); 2997 dir = -1; 2998 } 2999 if (y < y0 || y > y1) { 3000 return 0; 3001 } 3002 if (checkOnCurve(x, y, pts[0], pts[1])) { 3003 *onCurveCount += 1; 3004 return 0; 3005 } 3006 if (y == y1) { 3007 return 0; 3008 } 3009 SkScalar cross = SkScalarMul(x1 - x0, y - pts[0].fY) - SkScalarMul(dy, x - x0); 3010 3011 if (!cross) { 3012 // zero cross means the point is on the line, and since the case where 3013 // y of the query point is at the end point is handled above, we can be 3014 // sure that we're on the line (excluding the end point) here 3015 if (x != x1 || y != pts[1].fY) { 3016 *onCurveCount += 1; 3017 } 3018 dir = 0; 3019 } else if (SkScalarSignAsInt(cross) == dir) { 3020 dir = 0; 3021 } 3022 return dir; 3023} 3024 3025static void tangent_cubic(const SkPoint pts[], SkScalar x, SkScalar y, 3026 SkTDArray<SkVector>* tangents) { 3027 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY) 3028 && !between(pts[2].fY, y, pts[3].fY)) { 3029 return; 3030 } 3031 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX) 3032 && !between(pts[2].fX, x, pts[3].fX)) { 3033 return; 3034 } 3035 SkPoint dst[10]; 3036 int n = SkChopCubicAtYExtrema(pts, dst); 3037 for (int i = 0; i <= n; ++i) { 3038 SkPoint* c = &dst[i * 3]; 3039 SkScalar t; 3040 SkAssertResult(SkCubicClipper::ChopMonoAtY(c, y, &t)); 3041 SkScalar xt = eval_cubic_pts(c[0].fX, c[1].fX, c[2].fX, c[3].fX, t); 3042 if (!SkScalarNearlyEqual(x, xt)) { 3043 continue; 3044 } 3045 SkVector tangent; 3046 SkEvalCubicAt(c, t, nullptr, &tangent, nullptr); 3047 tangents->push(tangent); 3048 } 3049} 3050 3051static void tangent_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar w, 3052 SkTDArray<SkVector>* tangents) { 3053 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) { 3054 return; 3055 } 3056 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) { 3057 return; 3058 } 3059 SkScalar roots[2]; 3060 SkScalar A = pts[2].fY; 3061 SkScalar B = pts[1].fY * w - y * w + y; 3062 SkScalar C = pts[0].fY; 3063 A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept) 3064 B -= C; // B = b*w - w * yCept + yCept - a 3065 C -= y; 3066 int n = SkFindUnitQuadRoots(A, 2 * B, C, roots); 3067 for (int index = 0; index < n; ++index) { 3068 SkScalar t = roots[index]; 3069 SkScalar xt = conic_eval_numerator(&pts[0].fX, w, t) / conic_eval_denominator(w, t); 3070 if (!SkScalarNearlyEqual(x, xt)) { 3071 continue; 3072 } 3073 SkConic conic(pts, w); 3074 tangents->push(conic.evalTangentAt(t)); 3075 } 3076} 3077 3078static void tangent_quad(const SkPoint pts[], SkScalar x, SkScalar y, 3079 SkTDArray<SkVector>* tangents) { 3080 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) { 3081 return; 3082 } 3083 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) { 3084 return; 3085 } 3086 SkScalar roots[2]; 3087 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, 3088 2 * (pts[1].fY - pts[0].fY), 3089 pts[0].fY - y, 3090 roots); 3091 for (int index = 0; index < n; ++index) { 3092 SkScalar t = roots[index]; 3093 SkScalar C = pts[0].fX; 3094 SkScalar A = pts[2].fX - 2 * pts[1].fX + C; 3095 SkScalar B = 2 * (pts[1].fX - C); 3096 SkScalar xt = (A * t + B) * t + C; 3097 if (!SkScalarNearlyEqual(x, xt)) { 3098 continue; 3099 } 3100 tangents->push(SkEvalQuadTangentAt(pts, t)); 3101 } 3102} 3103 3104static void tangent_line(const SkPoint pts[], SkScalar x, SkScalar y, 3105 SkTDArray<SkVector>* tangents) { 3106 SkScalar y0 = pts[0].fY; 3107 SkScalar y1 = pts[1].fY; 3108 if (!between(y0, y, y1)) { 3109 return; 3110 } 3111 SkScalar x0 = pts[0].fX; 3112 SkScalar x1 = pts[1].fX; 3113 if (!between(x0, x, x1)) { 3114 return; 3115 } 3116 SkScalar dx = x1 - x0; 3117 SkScalar dy = y1 - y0; 3118 if (!SkScalarNearlyEqual((x - x0) * dy, dx * (y - y0))) { 3119 return; 3120 } 3121 SkVector v; 3122 v.set(dx, dy); 3123 tangents->push(v); 3124} 3125 3126static bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) { 3127 return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom; 3128} 3129 3130bool SkPath::contains(SkScalar x, SkScalar y) const { 3131 bool isInverse = this->isInverseFillType(); 3132 if (this->isEmpty()) { 3133 return isInverse; 3134 } 3135 3136 if (!contains_inclusive(this->getBounds(), x, y)) { 3137 return isInverse; 3138 } 3139 3140 SkPath::Iter iter(*this, true); 3141 bool done = false; 3142 int w = 0; 3143 int onCurveCount = 0; 3144 do { 3145 SkPoint pts[4]; 3146 switch (iter.next(pts, false)) { 3147 case SkPath::kMove_Verb: 3148 case SkPath::kClose_Verb: 3149 break; 3150 case SkPath::kLine_Verb: 3151 w += winding_line(pts, x, y, &onCurveCount); 3152 break; 3153 case SkPath::kQuad_Verb: 3154 w += winding_quad(pts, x, y, &onCurveCount); 3155 break; 3156 case SkPath::kConic_Verb: 3157 w += winding_conic(pts, x, y, iter.conicWeight(), &onCurveCount); 3158 break; 3159 case SkPath::kCubic_Verb: 3160 w += winding_cubic(pts, x, y, &onCurveCount); 3161 break; 3162 case SkPath::kDone_Verb: 3163 done = true; 3164 break; 3165 } 3166 } while (!done); 3167 bool evenOddFill = SkPath::kEvenOdd_FillType == this->getFillType() 3168 || SkPath::kInverseEvenOdd_FillType == this->getFillType(); 3169 if (evenOddFill) { 3170 w &= 1; 3171 } 3172 if (w) { 3173 return !isInverse; 3174 } 3175 if (onCurveCount <= 1) { 3176 return SkToBool(onCurveCount) ^ isInverse; 3177 } 3178 if ((onCurveCount & 1) || evenOddFill) { 3179 return SkToBool(onCurveCount & 1) ^ isInverse; 3180 } 3181 // If the point touches an even number of curves, and the fill is winding, check for 3182 // coincidence. Count coincidence as places where the on curve points have identical tangents. 3183 iter.setPath(*this, true); 3184 done = false; 3185 SkTDArray<SkVector> tangents; 3186 do { 3187 SkPoint pts[4]; 3188 int oldCount = tangents.count(); 3189 switch (iter.next(pts, false)) { 3190 case SkPath::kMove_Verb: 3191 case SkPath::kClose_Verb: 3192 break; 3193 case SkPath::kLine_Verb: 3194 tangent_line(pts, x, y, &tangents); 3195 break; 3196 case SkPath::kQuad_Verb: 3197 tangent_quad(pts, x, y, &tangents); 3198 break; 3199 case SkPath::kConic_Verb: 3200 tangent_conic(pts, x, y, iter.conicWeight(), &tangents); 3201 break; 3202 case SkPath::kCubic_Verb: 3203 tangent_cubic(pts, x, y, &tangents); 3204 break; 3205 case SkPath::kDone_Verb: 3206 done = true; 3207 break; 3208 } 3209 if (tangents.count() > oldCount) { 3210 int last = tangents.count() - 1; 3211 const SkVector& tangent = tangents[last]; 3212 if (SkScalarNearlyZero(tangent.lengthSqd())) { 3213 tangents.remove(last); 3214 } else { 3215 for (int index = 0; index < last; ++index) { 3216 const SkVector& test = tangents[index]; 3217 if (SkScalarNearlyZero(test.cross(tangent)) 3218 && SkScalarSignAsInt(tangent.fX * test.fX) <= 0 3219 && SkScalarSignAsInt(tangent.fY * test.fY) <= 0) { 3220 tangents.remove(last); 3221 tangents.removeShuffle(index); 3222 break; 3223 } 3224 } 3225 } 3226 } 3227 } while (!done); 3228 return SkToBool(tangents.count()) ^ isInverse; 3229} 3230 3231int SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, 3232 SkScalar w, SkPoint pts[], int pow2) { 3233 const SkConic conic(p0, p1, p2, w); 3234 return conic.chopIntoQuadsPOW2(pts, pow2); 3235} 3236