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 "include/core/SkPath.h" 9 10#include "include/core/SkData.h" 11#include "include/core/SkMath.h" 12#include "include/core/SkPathBuilder.h" 13#include "include/core/SkRRect.h" 14#include "include/private/SkMacros.h" 15#include "include/private/SkPathRef.h" 16#include "include/private/SkTo.h" 17#include "src/core/SkBuffer.h" 18#include "src/core/SkCubicClipper.h" 19#include "src/core/SkGeometry.h" 20#include "src/core/SkMatrixPriv.h" 21#include "src/core/SkPathMakers.h" 22#include "src/core/SkPathPriv.h" 23#include "src/core/SkPointPriv.h" 24#include "src/core/SkSafeMath.h" 25#include "src/core/SkTLazy.h" 26// need SkDVector 27#include "src/pathops/SkPathOpsPoint.h" 28 29#include <cmath> 30#include <utility> 31 32struct SkPath_Storage_Equivalent { 33 void* fPtr; 34 int32_t fIndex; 35 uint32_t fFlags; 36}; 37 38static_assert(sizeof(SkPath) == sizeof(SkPath_Storage_Equivalent), 39 "Please keep an eye on SkPath packing."); 40 41static float poly_eval(float A, float B, float C, float t) { 42 return (A * t + B) * t + C; 43} 44 45static float poly_eval(float A, float B, float C, float D, float t) { 46 return ((A * t + B) * t + C) * t + D; 47} 48 49//////////////////////////////////////////////////////////////////////////// 50 51/** 52 * Path.bounds is defined to be the bounds of all the control points. 53 * If we called bounds.join(r) we would skip r if r was empty, which breaks 54 * our promise. Hence we have a custom joiner that doesn't look at emptiness 55 */ 56static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) { 57 dst->fLeft = std::min(dst->fLeft, src.fLeft); 58 dst->fTop = std::min(dst->fTop, src.fTop); 59 dst->fRight = std::max(dst->fRight, src.fRight); 60 dst->fBottom = std::max(dst->fBottom, src.fBottom); 61} 62 63static bool is_degenerate(const SkPath& path) { 64 return (path.countVerbs() - SkPathPriv::LeadingMoveToCount(path)) == 0; 65} 66 67class SkAutoDisableDirectionCheck { 68public: 69 SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) { 70 fSaved = static_cast<SkPathFirstDirection>(fPath->getFirstDirection()); 71 } 72 73 ~SkAutoDisableDirectionCheck() { 74 fPath->setFirstDirection(fSaved); 75 } 76 77private: 78 SkPath* fPath; 79 SkPathFirstDirection fSaved; 80}; 81 82/* This class's constructor/destructor bracket a path editing operation. It is 83 used when we know the bounds of the amount we are going to add to the path 84 (usually a new contour, but not required). 85 86 It captures some state about the path up front (i.e. if it already has a 87 cached bounds), and then if it can, it updates the cache bounds explicitly, 88 avoiding the need to revisit all of the points in getBounds(). 89 90 It also notes if the path was originally degenerate, and if so, sets 91 isConvex to true. Thus it can only be used if the contour being added is 92 convex. 93 */ 94class SkAutoPathBoundsUpdate { 95public: 96 SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fPath(path), fRect(r) { 97 // Cannot use fRect for our bounds unless we know it is sorted 98 fRect.sort(); 99 // Mark the path's bounds as dirty if (1) they are, or (2) the path 100 // is non-finite, and therefore its bounds are not meaningful 101 fHasValidBounds = path->hasComputedBounds() && path->isFinite(); 102 fEmpty = path->isEmpty(); 103 if (fHasValidBounds && !fEmpty) { 104 joinNoEmptyChecks(&fRect, fPath->getBounds()); 105 } 106 fDegenerate = is_degenerate(*path); 107 } 108 109 ~SkAutoPathBoundsUpdate() { 110 fPath->setConvexity(fDegenerate ? SkPathConvexity::kConvex 111 : SkPathConvexity::kUnknown); 112 if ((fEmpty || fHasValidBounds) && fRect.isFinite()) { 113 fPath->setBounds(fRect); 114 } 115 } 116 117private: 118 SkPath* fPath; 119 SkRect fRect; 120 bool fHasValidBounds; 121 bool fDegenerate; 122 bool fEmpty; 123}; 124 125//////////////////////////////////////////////////////////////////////////// 126 127/* 128 Stores the verbs and points as they are given to us, with exceptions: 129 - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic 130 - we insert a Move(0,0) if Line | Quad | Cubic is our first command 131 132 The iterator does more cleanup, especially if forceClose == true 133 1. If we encounter degenerate segments, remove them 134 2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt) 135 3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2 136 4. if we encounter Line | Quad | Cubic after Close, cons up a Move 137*/ 138 139//////////////////////////////////////////////////////////////////////////// 140 141// flag to require a moveTo if we begin with something else, like lineTo etc. 142// This will also be the value of lastMoveToIndex for a single contour 143// ending with close, so countVerbs needs to be checked against 0. 144#define INITIAL_LASTMOVETOINDEX_VALUE ~0 145 146SkPath::SkPath() 147 : fPathRef(SkPathRef::CreateEmpty()) { 148 this->resetFields(); 149 fIsVolatile = false; 150} 151 152SkPath::SkPath(sk_sp<SkPathRef> pr, SkPathFillType ft, bool isVolatile, SkPathConvexity ct, 153 SkPathFirstDirection firstDirection) 154 : fPathRef(std::move(pr)) 155 , fLastMoveToIndex(INITIAL_LASTMOVETOINDEX_VALUE) 156 , fConvexity((uint8_t)ct) 157 , fFirstDirection((uint8_t)firstDirection) 158 , fFillType((unsigned)ft) 159 , fIsVolatile(isVolatile) 160{} 161 162void SkPath::resetFields() { 163 //fPathRef is assumed to have been emptied by the caller. 164 fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE; 165 fFillType = SkToU8(SkPathFillType::kWinding); 166 this->setConvexity(SkPathConvexity::kUnknown); 167 this->setFirstDirection(SkPathFirstDirection::kUnknown); 168 169 // We don't touch Android's fSourcePath. It's used to track texture garbage collection, so we 170 // don't want to muck with it if it's been set to something non-nullptr. 171} 172 173SkPath::SkPath(const SkPath& that) 174 : fPathRef(SkRef(that.fPathRef.get())) { 175 this->copyFields(that); 176 SkDEBUGCODE(that.validate();) 177} 178 179SkPath::~SkPath() { 180 SkDEBUGCODE(this->validate();) 181} 182 183SkPath& SkPath::operator=(const SkPath& that) { 184 SkDEBUGCODE(that.validate();) 185 186 if (this != &that) { 187 fPathRef.reset(SkRef(that.fPathRef.get())); 188 this->copyFields(that); 189 } 190 SkDEBUGCODE(this->validate();) 191 return *this; 192} 193 194void SkPath::copyFields(const SkPath& that) { 195 //fPathRef is assumed to have been set by the caller. 196 fLastMoveToIndex = that.fLastMoveToIndex; 197 fFillType = that.fFillType; 198 fIsVolatile = that.fIsVolatile; 199 200 // Non-atomic assignment of atomic values. 201 this->setConvexity(that.getConvexityOrUnknown()); 202 this->setFirstDirection(that.getFirstDirection()); 203} 204 205bool operator==(const SkPath& a, const SkPath& b) { 206 // note: don't need to look at isConvex or bounds, since just comparing the 207 // raw data is sufficient. 208 return &a == &b || 209 (a.fFillType == b.fFillType && *a.fPathRef == *b.fPathRef); 210} 211 212void SkPath::swap(SkPath& that) { 213 if (this != &that) { 214 fPathRef.swap(that.fPathRef); 215 std::swap(fLastMoveToIndex, that.fLastMoveToIndex); 216 217 const auto ft = fFillType; 218 fFillType = that.fFillType; 219 that.fFillType = ft; 220 221 const auto iv = fIsVolatile; 222 fIsVolatile = that.fIsVolatile; 223 that.fIsVolatile = iv; 224 225 // Non-atomic swaps of atomic values. 226 SkPathConvexity c = this->getConvexityOrUnknown(); 227 this->setConvexity(that.getConvexityOrUnknown()); 228 that.setConvexity(c); 229 230 SkPathFirstDirection fd = this->getFirstDirection(); 231 this->setFirstDirection(that.getFirstDirection()); 232 that.setFirstDirection(fd); 233 } 234} 235 236bool SkPath::isInterpolatable(const SkPath& compare) const { 237 // need the same structure (verbs, conicweights) and same point-count 238 return fPathRef->fPoints.count() == compare.fPathRef->fPoints.count() && 239 fPathRef->fVerbs == compare.fPathRef->fVerbs && 240 fPathRef->fConicWeights == compare.fPathRef->fConicWeights; 241} 242 243bool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const { 244 int pointCount = fPathRef->countPoints(); 245 if (pointCount != ending.fPathRef->countPoints()) { 246 return false; 247 } 248 if (!pointCount) { 249 return true; 250 } 251 out->reset(); 252 out->addPath(*this); 253 fPathRef->interpolate(*ending.fPathRef, weight, out->fPathRef.get()); 254 return true; 255} 256 257static inline bool check_edge_against_rect(const SkPoint& p0, 258 const SkPoint& p1, 259 const SkRect& rect, 260 SkPathFirstDirection dir) { 261 const SkPoint* edgeBegin; 262 SkVector v; 263 if (SkPathFirstDirection::kCW == dir) { 264 v = p1 - p0; 265 edgeBegin = &p0; 266 } else { 267 v = p0 - p1; 268 edgeBegin = &p1; 269 } 270 if (v.fX || v.fY) { 271 // check the cross product of v with the vec from edgeBegin to each rect corner 272 SkScalar yL = v.fY * (rect.fLeft - edgeBegin->fX); 273 SkScalar xT = v.fX * (rect.fTop - edgeBegin->fY); 274 SkScalar yR = v.fY * (rect.fRight - edgeBegin->fX); 275 SkScalar xB = v.fX * (rect.fBottom - edgeBegin->fY); 276 if ((xT < yL) || (xT < yR) || (xB < yL) || (xB < yR)) { 277 return false; 278 } 279 } 280 return true; 281} 282 283bool SkPath::conservativelyContainsRect(const SkRect& rect) const { 284 // This only handles non-degenerate convex paths currently. 285 if (!this->isConvex()) { 286 return false; 287 } 288 289 SkPathFirstDirection direction = SkPathPriv::ComputeFirstDirection(*this); 290 if (direction == SkPathFirstDirection::kUnknown) { 291 return false; 292 } 293 294 SkPoint firstPt; 295 SkPoint prevPt; 296 int segmentCount = 0; 297 SkDEBUGCODE(int moveCnt = 0;) 298 299 for (auto [verb, pts, weight] : SkPathPriv::Iterate(*this)) { 300 if (verb == SkPathVerb::kClose || (segmentCount > 0 && verb == SkPathVerb::kMove)) { 301 // Closing the current contour; but since convexity is a precondition, it's the only 302 // contour that matters. 303 SkASSERT(moveCnt); 304 segmentCount++; 305 break; 306 } else if (verb == SkPathVerb::kMove) { 307 // A move at the start of the contour (or multiple leading moves, in which case we 308 // keep the last one before a non-move verb). 309 SkASSERT(!segmentCount); 310 SkDEBUGCODE(++moveCnt); 311 firstPt = prevPt = pts[0]; 312 } else { 313 int pointCount = SkPathPriv::PtsInVerb((unsigned) verb); 314 SkASSERT(pointCount > 0); 315 316 if (!SkPathPriv::AllPointsEq(pts, pointCount + 1)) { 317 SkASSERT(moveCnt); 318 int nextPt = pointCount; 319 segmentCount++; 320 321 if (SkPathVerb::kConic == verb) { 322 SkConic orig; 323 orig.set(pts, *weight); 324 SkPoint quadPts[5]; 325 int count = orig.chopIntoQuadsPOW2(quadPts, 1); 326 SkASSERT_RELEASE(2 == count); 327 328 if (!check_edge_against_rect(quadPts[0], quadPts[2], rect, direction)) { 329 return false; 330 } 331 if (!check_edge_against_rect(quadPts[2], quadPts[4], rect, direction)) { 332 return false; 333 } 334 } else { 335 if (!check_edge_against_rect(prevPt, pts[nextPt], rect, direction)) { 336 return false; 337 } 338 } 339 prevPt = pts[nextPt]; 340 } 341 } 342 } 343 344 if (segmentCount) { 345 return check_edge_against_rect(prevPt, firstPt, rect, direction); 346 } 347 return false; 348} 349 350uint32_t SkPath::getGenerationID() const { 351 uint32_t genID = fPathRef->genID(); 352#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 353 SkASSERT((unsigned)fFillType < (1 << (32 - SkPathPriv::kPathRefGenIDBitCnt))); 354 genID |= static_cast<uint32_t>(fFillType) << SkPathPriv::kPathRefGenIDBitCnt; 355#endif 356 return genID; 357} 358 359SkPath& SkPath::reset() { 360 SkDEBUGCODE(this->validate();) 361 362 fPathRef.reset(SkPathRef::CreateEmpty()); 363 this->resetFields(); 364 return *this; 365} 366 367SkPath& SkPath::rewind() { 368 SkDEBUGCODE(this->validate();) 369 370 SkPathRef::Rewind(&fPathRef); 371 this->resetFields(); 372 return *this; 373} 374 375bool SkPath::isLastContourClosed() const { 376 int verbCount = fPathRef->countVerbs(); 377 if (0 == verbCount) { 378 return false; 379 } 380 return kClose_Verb == fPathRef->atVerb(verbCount - 1); 381} 382 383bool SkPath::isLine(SkPoint line[2]) const { 384 int verbCount = fPathRef->countVerbs(); 385 386 if (2 == verbCount) { 387 SkASSERT(kMove_Verb == fPathRef->atVerb(0)); 388 if (kLine_Verb == fPathRef->atVerb(1)) { 389 SkASSERT(2 == fPathRef->countPoints()); 390 if (line) { 391 const SkPoint* pts = fPathRef->points(); 392 line[0] = pts[0]; 393 line[1] = pts[1]; 394 } 395 return true; 396 } 397 } 398 return false; 399} 400 401/* 402 Determines if path is a rect by keeping track of changes in direction 403 and looking for a loop either clockwise or counterclockwise. 404 405 The direction is computed such that: 406 0: vertical up 407 1: horizontal left 408 2: vertical down 409 3: horizontal right 410 411A rectangle cycles up/right/down/left or up/left/down/right. 412 413The test fails if: 414 The path is closed, and followed by a line. 415 A second move creates a new endpoint. 416 A diagonal line is parsed. 417 There's more than four changes of direction. 418 There's a discontinuity on the line (e.g., a move in the middle) 419 The line reverses direction. 420 The path contains a quadratic or cubic. 421 The path contains fewer than four points. 422 *The rectangle doesn't complete a cycle. 423 *The final point isn't equal to the first point. 424 425 *These last two conditions we relax if we have a 3-edge path that would 426 form a rectangle if it were closed (as we do when we fill a path) 427 428It's OK if the path has: 429 Several colinear line segments composing a rectangle side. 430 Single points on the rectangle side. 431 432The direction takes advantage of the corners found since opposite sides 433must travel in opposite directions. 434 435FIXME: Allow colinear quads and cubics to be treated like lines. 436FIXME: If the API passes fill-only, return true if the filled stroke 437 is a rectangle, though the caller failed to close the path. 438 439 directions values: 440 0x1 is set if the segment is horizontal 441 0x2 is set if the segment is moving to the right or down 442 thus: 443 two directions are opposites iff (dirA ^ dirB) == 0x2 444 two directions are perpendicular iff (dirA ^ dirB) == 0x1 445 446 */ 447static int rect_make_dir(SkScalar dx, SkScalar dy) { 448 return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1); 449} 450 451bool SkPath::isRect(SkRect* rect, bool* isClosed, SkPathDirection* direction) const { 452 SkDEBUGCODE(this->validate();) 453 int currVerb = 0; 454 const SkPoint* pts = fPathRef->points(); 455 return SkPathPriv::IsRectContour(*this, false, &currVerb, &pts, isClosed, direction, rect); 456} 457 458bool SkPath::isOval(SkRect* bounds) const { 459 return SkPathPriv::IsOval(*this, bounds, nullptr, nullptr); 460} 461 462bool SkPath::isRRect(SkRRect* rrect) const { 463 return SkPathPriv::IsRRect(*this, rrect, nullptr, nullptr); 464} 465 466int SkPath::countPoints() const { 467 return fPathRef->countPoints(); 468} 469 470int SkPath::getPoints(SkPoint dst[], int max) const { 471 SkDEBUGCODE(this->validate();) 472 473 SkASSERT(max >= 0); 474 SkASSERT(!max || dst); 475 int count = std::min(max, fPathRef->countPoints()); 476 sk_careful_memcpy(dst, fPathRef->points(), count * sizeof(SkPoint)); 477 return fPathRef->countPoints(); 478} 479 480SkPoint SkPath::getPoint(int index) const { 481 if ((unsigned)index < (unsigned)fPathRef->countPoints()) { 482 return fPathRef->atPoint(index); 483 } 484 return SkPoint::Make(0, 0); 485} 486 487int SkPath::countVerbs() const { 488 return fPathRef->countVerbs(); 489} 490 491int SkPath::getVerbs(uint8_t dst[], int max) const { 492 SkDEBUGCODE(this->validate();) 493 494 SkASSERT(max >= 0); 495 SkASSERT(!max || dst); 496 int count = std::min(max, fPathRef->countVerbs()); 497 if (count) { 498 memcpy(dst, fPathRef->verbsBegin(), count); 499 } 500 return fPathRef->countVerbs(); 501} 502 503size_t SkPath::approximateBytesUsed() const { 504 size_t size = sizeof (SkPath); 505 if (fPathRef != nullptr) { 506 size += fPathRef->approximateBytesUsed(); 507 } 508 return size; 509} 510 511bool SkPath::getLastPt(SkPoint* lastPt) const { 512 SkDEBUGCODE(this->validate();) 513 514 int count = fPathRef->countPoints(); 515 if (count > 0) { 516 if (lastPt) { 517 *lastPt = fPathRef->atPoint(count - 1); 518 } 519 return true; 520 } 521 if (lastPt) { 522 lastPt->set(0, 0); 523 } 524 return false; 525} 526 527void SkPath::setPt(int index, SkScalar x, SkScalar y) { 528 SkDEBUGCODE(this->validate();) 529 530 int count = fPathRef->countPoints(); 531 if (count <= index) { 532 return; 533 } else { 534 SkPathRef::Editor ed(&fPathRef); 535 ed.atPoint(index)->set(x, y); 536 } 537} 538 539void SkPath::setLastPt(SkScalar x, SkScalar y) { 540 SkDEBUGCODE(this->validate();) 541 542 int count = fPathRef->countPoints(); 543 if (count == 0) { 544 this->moveTo(x, y); 545 } else { 546 SkPathRef::Editor ed(&fPathRef); 547 ed.atPoint(count-1)->set(x, y); 548 } 549} 550 551// This is the public-facing non-const setConvexity(). 552void SkPath::setConvexity(SkPathConvexity c) { 553 fConvexity.store((uint8_t)c, std::memory_order_relaxed); 554} 555 556// Const hooks for working with fConvexity and fFirstDirection from const methods. 557void SkPath::setConvexity(SkPathConvexity c) const { 558 fConvexity.store((uint8_t)c, std::memory_order_relaxed); 559} 560void SkPath::setFirstDirection(SkPathFirstDirection d) const { 561 fFirstDirection.store((uint8_t)d, std::memory_order_relaxed); 562} 563SkPathFirstDirection SkPath::getFirstDirection() const { 564 return (SkPathFirstDirection)fFirstDirection.load(std::memory_order_relaxed); 565} 566 567bool SkPath::isConvexityAccurate() const { 568 SkPathConvexity convexity = this->getConvexityOrUnknown(); 569 if (convexity != SkPathConvexity::kUnknown) { 570 auto conv = this->computeConvexity(); 571 if (conv != convexity) { 572 SkASSERT(false); 573 return false; 574 } 575 } 576 return true; 577} 578 579SkPathConvexity SkPath::getConvexity() const { 580// Enable once we fix all the bugs 581// SkDEBUGCODE(this->isConvexityAccurate()); 582 SkPathConvexity convexity = this->getConvexityOrUnknown(); 583 if (convexity == SkPathConvexity::kUnknown) { 584 convexity = this->computeConvexity(); 585 } 586 SkASSERT(convexity != SkPathConvexity::kUnknown); 587 return convexity; 588} 589 590////////////////////////////////////////////////////////////////////////////// 591// Construction methods 592 593SkPath& SkPath::dirtyAfterEdit() { 594 this->setConvexity(SkPathConvexity::kUnknown); 595 this->setFirstDirection(SkPathFirstDirection::kUnknown); 596 597#ifdef SK_DEBUG 598 // enable this as needed for testing, but it slows down some chrome tests so much 599 // that they don't complete, so we don't enable it by default 600 // e.g. TEST(IdentifiabilityPaintOpDigestTest, MassiveOpSkipped) 601 if (this->countVerbs() < 16) { 602 SkASSERT(fPathRef->dataMatchesVerbs()); 603 } 604#endif 605 606 return *this; 607} 608 609void SkPath::incReserve(int inc) { 610 SkDEBUGCODE(this->validate();) 611 if (inc > 0) { 612 SkPathRef::Editor(&fPathRef, inc, inc); 613 } 614 SkDEBUGCODE(this->validate();) 615} 616 617SkPath& SkPath::moveTo(SkScalar x, SkScalar y) { 618 SkDEBUGCODE(this->validate();) 619 620 SkPathRef::Editor ed(&fPathRef); 621 622 // remember our index 623 fLastMoveToIndex = fPathRef->countPoints(); 624 625 ed.growForVerb(kMove_Verb)->set(x, y); 626 627 return this->dirtyAfterEdit(); 628} 629 630SkPath& SkPath::rMoveTo(SkScalar x, SkScalar y) { 631 SkPoint pt = {0,0}; 632 int count = fPathRef->countPoints(); 633 if (count > 0) { 634 if (fLastMoveToIndex >= 0) { 635 pt = fPathRef->atPoint(count - 1); 636 } else { 637 pt = fPathRef->atPoint(~fLastMoveToIndex); 638 } 639 } 640 return this->moveTo(pt.fX + x, pt.fY + y); 641} 642 643void SkPath::injectMoveToIfNeeded() { 644 if (fLastMoveToIndex < 0) { 645 SkScalar x, y; 646 if (fPathRef->countVerbs() == 0) { 647 x = y = 0; 648 } else { 649 const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex); 650 x = pt.fX; 651 y = pt.fY; 652 } 653 this->moveTo(x, y); 654 } 655} 656 657SkPath& SkPath::lineTo(SkScalar x, SkScalar y) { 658 SkDEBUGCODE(this->validate();) 659 660 this->injectMoveToIfNeeded(); 661 662 SkPathRef::Editor ed(&fPathRef); 663 ed.growForVerb(kLine_Verb)->set(x, y); 664 665 return this->dirtyAfterEdit(); 666} 667 668SkPath& SkPath::rLineTo(SkScalar x, SkScalar y) { 669 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). 670 SkPoint pt; 671 this->getLastPt(&pt); 672 return this->lineTo(pt.fX + x, pt.fY + y); 673} 674 675SkPath& SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { 676 SkDEBUGCODE(this->validate();) 677 678 this->injectMoveToIfNeeded(); 679 680 SkPathRef::Editor ed(&fPathRef); 681 SkPoint* pts = ed.growForVerb(kQuad_Verb); 682 pts[0].set(x1, y1); 683 pts[1].set(x2, y2); 684 685 return this->dirtyAfterEdit(); 686} 687 688SkPath& SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { 689 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). 690 SkPoint pt; 691 this->getLastPt(&pt); 692 return this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2); 693} 694 695SkPath& SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 696 SkScalar w) { 697 // check for <= 0 or NaN with this test 698 if (!(w > 0)) { 699 this->lineTo(x2, y2); 700 } else if (!SkScalarIsFinite(w)) { 701 this->lineTo(x1, y1); 702 this->lineTo(x2, y2); 703 } else if (SK_Scalar1 == w) { 704 this->quadTo(x1, y1, x2, y2); 705 } else { 706 SkDEBUGCODE(this->validate();) 707 708 this->injectMoveToIfNeeded(); 709 710 SkPathRef::Editor ed(&fPathRef); 711 SkPoint* pts = ed.growForVerb(kConic_Verb, w); 712 pts[0].set(x1, y1); 713 pts[1].set(x2, y2); 714 715 (void)this->dirtyAfterEdit(); 716 } 717 return *this; 718} 719 720SkPath& SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2, 721 SkScalar w) { 722 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). 723 SkPoint pt; 724 this->getLastPt(&pt); 725 return this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w); 726} 727 728SkPath& SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 729 SkScalar x3, SkScalar y3) { 730 SkDEBUGCODE(this->validate();) 731 732 this->injectMoveToIfNeeded(); 733 734 SkPathRef::Editor ed(&fPathRef); 735 SkPoint* pts = ed.growForVerb(kCubic_Verb); 736 pts[0].set(x1, y1); 737 pts[1].set(x2, y2); 738 pts[2].set(x3, y3); 739 740 return this->dirtyAfterEdit(); 741} 742 743SkPath& SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 744 SkScalar x3, SkScalar y3) { 745 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). 746 SkPoint pt; 747 this->getLastPt(&pt); 748 return this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2, 749 pt.fX + x3, pt.fY + y3); 750} 751 752SkPath& SkPath::close() { 753 SkDEBUGCODE(this->validate();) 754 755 int count = fPathRef->countVerbs(); 756 if (count > 0) { 757 switch (fPathRef->atVerb(count - 1)) { 758 case kLine_Verb: 759 case kQuad_Verb: 760 case kConic_Verb: 761 case kCubic_Verb: 762 case kMove_Verb: { 763 SkPathRef::Editor ed(&fPathRef); 764 ed.growForVerb(kClose_Verb); 765 break; 766 } 767 case kClose_Verb: 768 // don't add a close if it's the first verb or a repeat 769 break; 770 default: 771 SkDEBUGFAIL("unexpected verb"); 772 break; 773 } 774 } 775 776 // signal that we need a moveTo to follow us (unless we're done) 777#if 0 778 if (fLastMoveToIndex >= 0) { 779 fLastMoveToIndex = ~fLastMoveToIndex; 780 } 781#else 782 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1); 783#endif 784 return *this; 785} 786 787/////////////////////////////////////////////////////////////////////////////// 788 789static void assert_known_direction(SkPathDirection dir) { 790 SkASSERT(SkPathDirection::kCW == dir || SkPathDirection::kCCW == dir); 791} 792 793SkPath& SkPath::addRect(const SkRect &rect, SkPathDirection dir, unsigned startIndex) { 794 assert_known_direction(dir); 795 this->setFirstDirection(this->hasOnlyMoveTos() ? (SkPathFirstDirection)dir 796 : SkPathFirstDirection::kUnknown); 797 SkAutoDisableDirectionCheck addc(this); 798 SkAutoPathBoundsUpdate apbu(this, rect); 799 800 SkDEBUGCODE(int initialVerbCount = this->countVerbs()); 801 802 const int kVerbs = 5; // moveTo + 3x lineTo + close 803 this->incReserve(kVerbs); 804 805 SkPath_RectPointIterator iter(rect, dir, startIndex); 806 807 this->moveTo(iter.current()); 808 this->lineTo(iter.next()); 809 this->lineTo(iter.next()); 810 this->lineTo(iter.next()); 811 this->close(); 812 813 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs); 814 return *this; 815} 816 817SkPath& SkPath::addPoly(const SkPoint pts[], int count, bool close) { 818 SkDEBUGCODE(this->validate();) 819 if (count <= 0) { 820 return *this; 821 } 822 823 fLastMoveToIndex = fPathRef->countPoints(); 824 825 // +close makes room for the extra kClose_Verb 826 SkPathRef::Editor ed(&fPathRef, count+close, count); 827 828 ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY); 829 if (count > 1) { 830 SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1); 831 memcpy(p, &pts[1], (count-1) * sizeof(SkPoint)); 832 } 833 834 if (close) { 835 ed.growForVerb(kClose_Verb); 836 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1); 837 } 838 839 (void)this->dirtyAfterEdit(); 840 SkDEBUGCODE(this->validate();) 841 return *this; 842} 843 844#include "src/core/SkGeometry.h" 845 846static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, 847 SkPoint* pt) { 848 if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) { 849 // Chrome uses this path to move into and out of ovals. If not 850 // treated as a special case the moves can distort the oval's 851 // bounding box (and break the circle special case). 852 pt->set(oval.fRight, oval.centerY()); 853 return true; 854 } else if (0 == oval.width() && 0 == oval.height()) { 855 // Chrome will sometimes create 0 radius round rects. Having degenerate 856 // quad segments in the path prevents the path from being recognized as 857 // a rect. 858 // TODO: optimizing the case where only one of width or height is zero 859 // should also be considered. This case, however, doesn't seem to be 860 // as common as the single point case. 861 pt->set(oval.fRight, oval.fTop); 862 return true; 863 } 864 return false; 865} 866 867// Return the unit vectors pointing at the start/stop points for the given start/sweep angles 868// 869static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle, 870 SkVector* startV, SkVector* stopV, SkRotationDirection* dir) { 871 SkScalar startRad = SkDegreesToRadians(startAngle), 872 stopRad = SkDegreesToRadians(startAngle + sweepAngle); 873 874 startV->fY = SkScalarSinSnapToZero(startRad); 875 startV->fX = SkScalarCosSnapToZero(startRad); 876 stopV->fY = SkScalarSinSnapToZero(stopRad); 877 stopV->fX = SkScalarCosSnapToZero(stopRad); 878 879 /* If the sweep angle is nearly (but less than) 360, then due to precision 880 loss in radians-conversion and/or sin/cos, we may end up with coincident 881 vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead 882 of drawing a nearly complete circle (good). 883 e.g. canvas.drawArc(0, 359.99, ...) 884 -vs- canvas.drawArc(0, 359.9, ...) 885 We try to detect this edge case, and tweak the stop vector 886 */ 887 if (*startV == *stopV) { 888 SkScalar sw = SkScalarAbs(sweepAngle); 889 if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) { 890 // make a guess at a tiny angle (in radians) to tweak by 891 SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle); 892 // not sure how much will be enough, so we use a loop 893 do { 894 stopRad -= deltaRad; 895 stopV->fY = SkScalarSinSnapToZero(stopRad); 896 stopV->fX = SkScalarCosSnapToZero(stopRad); 897 } while (*startV == *stopV); 898 } 899 } 900 *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection; 901} 902 903/** 904 * If this returns 0, then the caller should just line-to the singlePt, else it should 905 * ignore singlePt and append the specified number of conics. 906 */ 907static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop, 908 SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc], 909 SkPoint* singlePt) { 910 SkMatrix matrix; 911 912 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height())); 913 matrix.postTranslate(oval.centerX(), oval.centerY()); 914 915 int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics); 916 if (0 == count) { 917 matrix.mapXY(stop.x(), stop.y(), singlePt); 918 } 919 return count; 920} 921 922SkPath& SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[], 923 SkPathDirection dir) { 924 SkRRect rrect; 925 rrect.setRectRadii(rect, (const SkVector*) radii); 926 return this->addRRect(rrect, dir); 927} 928 929SkPath& SkPath::addRRect(const SkRRect& rrect, SkPathDirection dir) { 930 // legacy start indices: 6 (CW) and 7(CCW) 931 return this->addRRect(rrect, dir, dir == SkPathDirection::kCW ? 6 : 7); 932} 933 934SkPath& SkPath::addRRect(const SkRRect &rrect, SkPathDirection dir, unsigned startIndex) { 935 assert_known_direction(dir); 936 937 bool isRRect = hasOnlyMoveTos(); 938 const SkRect& bounds = rrect.getBounds(); 939 940 if (rrect.isRect() || rrect.isEmpty()) { 941 // degenerate(rect) => radii points are collapsing 942 this->addRect(bounds, dir, (startIndex + 1) / 2); 943 } else if (rrect.isOval()) { 944 // degenerate(oval) => line points are collapsing 945 this->addOval(bounds, dir, startIndex / 2); 946 } else { 947 this->setFirstDirection(this->hasOnlyMoveTos() ? (SkPathFirstDirection)dir 948 : SkPathFirstDirection::kUnknown); 949 950 SkAutoPathBoundsUpdate apbu(this, bounds); 951 SkAutoDisableDirectionCheck addc(this); 952 953 // we start with a conic on odd indices when moving CW vs. even indices when moving CCW 954 const bool startsWithConic = ((startIndex & 1) == (dir == SkPathDirection::kCW)); 955 const SkScalar weight = SK_ScalarRoot2Over2; 956 957 SkDEBUGCODE(int initialVerbCount = this->countVerbs()); 958 const int kVerbs = startsWithConic 959 ? 9 // moveTo + 4x conicTo + 3x lineTo + close 960 : 10; // moveTo + 4x lineTo + 4x conicTo + close 961 this->incReserve(kVerbs); 962 963 SkPath_RRectPointIterator rrectIter(rrect, dir, startIndex); 964 // Corner iterator indices follow the collapsed radii model, 965 // adjusted such that the start pt is "behind" the radii start pt. 966 const unsigned rectStartIndex = startIndex / 2 + (dir == SkPathDirection::kCW ? 0 : 1); 967 SkPath_RectPointIterator rectIter(bounds, dir, rectStartIndex); 968 969 this->moveTo(rrectIter.current()); 970 if (startsWithConic) { 971 for (unsigned i = 0; i < 3; ++i) { 972 this->conicTo(rectIter.next(), rrectIter.next(), weight); 973 this->lineTo(rrectIter.next()); 974 } 975 this->conicTo(rectIter.next(), rrectIter.next(), weight); 976 // final lineTo handled by close(). 977 } else { 978 for (unsigned i = 0; i < 4; ++i) { 979 this->lineTo(rrectIter.next()); 980 this->conicTo(rectIter.next(), rrectIter.next(), weight); 981 } 982 } 983 this->close(); 984 985 SkPathRef::Editor ed(&fPathRef); 986 ed.setIsRRect(isRRect, dir == SkPathDirection::kCCW, startIndex % 8); 987 988 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs); 989 } 990 991 SkDEBUGCODE(fPathRef->validate();) 992 return *this; 993} 994 995bool SkPath::hasOnlyMoveTos() const { 996 int count = fPathRef->countVerbs(); 997 const uint8_t* verbs = fPathRef->verbsBegin(); 998 for (int i = 0; i < count; ++i) { 999 if (*verbs == kLine_Verb || 1000 *verbs == kQuad_Verb || 1001 *verbs == kConic_Verb || 1002 *verbs == kCubic_Verb) { 1003 return false; 1004 } 1005 ++verbs; 1006 } 1007 return true; 1008} 1009 1010bool SkPath::isZeroLengthSincePoint(int startPtIndex) const { 1011 int count = fPathRef->countPoints() - startPtIndex; 1012 if (count < 2) { 1013 return true; 1014 } 1015 const SkPoint* pts = fPathRef->points() + startPtIndex; 1016 const SkPoint& first = *pts; 1017 for (int index = 1; index < count; ++index) { 1018 if (first != pts[index]) { 1019 return false; 1020 } 1021 } 1022 return true; 1023} 1024 1025SkPath& SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, 1026 SkPathDirection dir) { 1027 assert_known_direction(dir); 1028 1029 if (rx < 0 || ry < 0) { 1030 return *this; 1031 } 1032 1033 SkRRect rrect; 1034 rrect.setRectXY(rect, rx, ry); 1035 return this->addRRect(rrect, dir); 1036} 1037 1038SkPath& SkPath::addOval(const SkRect& oval, SkPathDirection dir) { 1039 // legacy start index: 1 1040 return this->addOval(oval, dir, 1); 1041} 1042 1043SkPath& SkPath::addOval(const SkRect &oval, SkPathDirection dir, unsigned startPointIndex) { 1044 assert_known_direction(dir); 1045 1046 /* If addOval() is called after previous moveTo(), 1047 this path is still marked as an oval. This is used to 1048 fit into WebKit's calling sequences. 1049 We can't simply check isEmpty() in this case, as additional 1050 moveTo() would mark the path non empty. 1051 */ 1052 bool isOval = hasOnlyMoveTos(); 1053 if (isOval) { 1054 this->setFirstDirection((SkPathFirstDirection)dir); 1055 } else { 1056 this->setFirstDirection(SkPathFirstDirection::kUnknown); 1057 } 1058 1059 SkAutoDisableDirectionCheck addc(this); 1060 SkAutoPathBoundsUpdate apbu(this, oval); 1061 1062 SkDEBUGCODE(int initialVerbCount = this->countVerbs()); 1063 const int kVerbs = 6; // moveTo + 4x conicTo + close 1064 this->incReserve(kVerbs); 1065 1066 SkPath_OvalPointIterator ovalIter(oval, dir, startPointIndex); 1067 // The corner iterator pts are tracking "behind" the oval/radii pts. 1068 SkPath_RectPointIterator rectIter(oval, dir, startPointIndex + (dir == SkPathDirection::kCW ? 0 : 1)); 1069 const SkScalar weight = SK_ScalarRoot2Over2; 1070 1071 this->moveTo(ovalIter.current()); 1072 for (unsigned i = 0; i < 4; ++i) { 1073 this->conicTo(rectIter.next(), ovalIter.next(), weight); 1074 } 1075 this->close(); 1076 1077 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs); 1078 1079 SkPathRef::Editor ed(&fPathRef); 1080 1081 ed.setIsOval(isOval, SkPathDirection::kCCW == dir, startPointIndex % 4); 1082 return *this; 1083} 1084 1085SkPath& SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, SkPathDirection dir) { 1086 if (r > 0) { 1087 this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir); 1088 } 1089 return *this; 1090} 1091 1092SkPath& SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, 1093 bool forceMoveTo) { 1094 if (oval.width() < 0 || oval.height() < 0) { 1095 return *this; 1096 } 1097 1098 if (fPathRef->countVerbs() == 0) { 1099 forceMoveTo = true; 1100 } 1101 1102 SkPoint lonePt; 1103 if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) { 1104 return forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt); 1105 } 1106 1107 SkVector startV, stopV; 1108 SkRotationDirection dir; 1109 angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir); 1110 1111 SkPoint singlePt; 1112 1113 // Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently 1114 // close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous 1115 // arcs from the same oval. 1116 auto addPt = [&forceMoveTo, this](const SkPoint& pt) { 1117 SkPoint lastPt; 1118 if (forceMoveTo) { 1119 this->moveTo(pt); 1120 } else if (!this->getLastPt(&lastPt) || 1121 !SkScalarNearlyEqual(lastPt.fX, pt.fX) || 1122 !SkScalarNearlyEqual(lastPt.fY, pt.fY)) { 1123 this->lineTo(pt); 1124 } 1125 }; 1126 1127 // At this point, we know that the arc is not a lone point, but startV == stopV 1128 // indicates that the sweepAngle is too small such that angles_to_unit_vectors 1129 // cannot handle it. 1130 if (startV == stopV) { 1131 SkScalar endAngle = SkDegreesToRadians(startAngle + sweepAngle); 1132 SkScalar radiusX = oval.width() / 2; 1133 SkScalar radiusY = oval.height() / 2; 1134 // We do not use SkScalar[Sin|Cos]SnapToZero here. When sin(startAngle) is 0 and sweepAngle 1135 // is very small and radius is huge, the expected behavior here is to draw a line. But 1136 // calling SkScalarSinSnapToZero will make sin(endAngle) be 0 which will then draw a dot. 1137 singlePt.set(oval.centerX() + radiusX * SkScalarCos(endAngle), 1138 oval.centerY() + radiusY * SkScalarSin(endAngle)); 1139 addPt(singlePt); 1140 return *this; 1141 } 1142 1143 SkConic conics[SkConic::kMaxConicsForArc]; 1144 int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt); 1145 if (count) { 1146 this->incReserve(count * 2 + 1); 1147 const SkPoint& pt = conics[0].fPts[0]; 1148 addPt(pt); 1149 for (int i = 0; i < count; ++i) { 1150 this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW); 1151 } 1152 } else { 1153 addPt(singlePt); 1154 } 1155 return *this; 1156} 1157 1158// This converts the SVG arc to conics. 1159// Partly adapted from Niko's code in kdelibs/kdecore/svgicons. 1160// Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic() 1161// See also SVG implementation notes: 1162// http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter 1163// Note that arcSweep bool value is flipped from the original implementation. 1164SkPath& SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arcLarge, 1165 SkPathDirection arcSweep, SkScalar x, SkScalar y) { 1166 this->injectMoveToIfNeeded(); 1167 SkPoint srcPts[2]; 1168 this->getLastPt(&srcPts[0]); 1169 // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto") 1170 // joining the endpoints. 1171 // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters 1172 if (!rx || !ry) { 1173 return this->lineTo(x, y); 1174 } 1175 // If the current point and target point for the arc are identical, it should be treated as a 1176 // zero length path. This ensures continuity in animations. 1177 srcPts[1].set(x, y); 1178 if (srcPts[0] == srcPts[1]) { 1179 return this->lineTo(x, y); 1180 } 1181 rx = SkScalarAbs(rx); 1182 ry = SkScalarAbs(ry); 1183 SkVector midPointDistance = srcPts[0] - srcPts[1]; 1184 midPointDistance *= 0.5f; 1185 1186 SkMatrix pointTransform; 1187 pointTransform.setRotate(-angle); 1188 1189 SkPoint transformedMidPoint; 1190 pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1); 1191 SkScalar squareRx = rx * rx; 1192 SkScalar squareRy = ry * ry; 1193 SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX; 1194 SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY; 1195 1196 // Check if the radii are big enough to draw the arc, scale radii if not. 1197 // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii 1198 SkScalar radiiScale = squareX / squareRx + squareY / squareRy; 1199 if (radiiScale > 1) { 1200 radiiScale = SkScalarSqrt(radiiScale); 1201 rx *= radiiScale; 1202 ry *= radiiScale; 1203 } 1204 1205 pointTransform.setScale(1 / rx, 1 / ry); 1206 pointTransform.preRotate(-angle); 1207 1208 SkPoint unitPts[2]; 1209 pointTransform.mapPoints(unitPts, srcPts, (int) SK_ARRAY_COUNT(unitPts)); 1210 SkVector delta = unitPts[1] - unitPts[0]; 1211 1212 SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY; 1213 SkScalar scaleFactorSquared = std::max(1 / d - 0.25f, 0.f); 1214 1215 SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared); 1216 if ((arcSweep == SkPathDirection::kCCW) != SkToBool(arcLarge)) { // flipped from the original implementation 1217 scaleFactor = -scaleFactor; 1218 } 1219 delta.scale(scaleFactor); 1220 SkPoint centerPoint = unitPts[0] + unitPts[1]; 1221 centerPoint *= 0.5f; 1222 centerPoint.offset(-delta.fY, delta.fX); 1223 unitPts[0] -= centerPoint; 1224 unitPts[1] -= centerPoint; 1225 SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX); 1226 SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX); 1227 SkScalar thetaArc = theta2 - theta1; 1228 if (thetaArc < 0 && (arcSweep == SkPathDirection::kCW)) { // arcSweep flipped from the original implementation 1229 thetaArc += SK_ScalarPI * 2; 1230 } else if (thetaArc > 0 && (arcSweep != SkPathDirection::kCW)) { // arcSweep flipped from the original implementation 1231 thetaArc -= SK_ScalarPI * 2; 1232 } 1233 1234 // Very tiny angles cause our subsequent math to go wonky (skbug.com/9272) 1235 // so we do a quick check here. The precise tolerance amount is just made up. 1236 // PI/million happens to fix the bug in 9272, but a larger value is probably 1237 // ok too. 1238 if (SkScalarAbs(thetaArc) < (SK_ScalarPI / (1000 * 1000))) { 1239 return this->lineTo(x, y); 1240 } 1241 1242 pointTransform.setRotate(angle); 1243 pointTransform.preScale(rx, ry); 1244 1245 // the arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd 1246 int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (2 * SK_ScalarPI / 3))); 1247 SkScalar thetaWidth = thetaArc / segments; 1248 SkScalar t = SkScalarTan(0.5f * thetaWidth); 1249 if (!SkScalarIsFinite(t)) { 1250 return *this; 1251 } 1252 SkScalar startTheta = theta1; 1253 SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf); 1254 auto scalar_is_integer = [](SkScalar scalar) -> bool { 1255 return scalar == SkScalarFloorToScalar(scalar); 1256 }; 1257 bool expectIntegers = SkScalarNearlyZero(SK_ScalarPI/2 - SkScalarAbs(thetaWidth)) && 1258 scalar_is_integer(rx) && scalar_is_integer(ry) && 1259 scalar_is_integer(x) && scalar_is_integer(y); 1260 1261 for (int i = 0; i < segments; ++i) { 1262 SkScalar endTheta = startTheta + thetaWidth, 1263 sinEndTheta = SkScalarSinSnapToZero(endTheta), 1264 cosEndTheta = SkScalarCosSnapToZero(endTheta); 1265 1266 unitPts[1].set(cosEndTheta, sinEndTheta); 1267 unitPts[1] += centerPoint; 1268 unitPts[0] = unitPts[1]; 1269 unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta); 1270 SkPoint mapped[2]; 1271 pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts)); 1272 /* 1273 Computing the arc width introduces rounding errors that cause arcs to start 1274 outside their marks. A round rect may lose convexity as a result. If the input 1275 values are on integers, place the conic on integers as well. 1276 */ 1277 if (expectIntegers) { 1278 for (SkPoint& point : mapped) { 1279 point.fX = SkScalarRoundToScalar(point.fX); 1280 point.fY = SkScalarRoundToScalar(point.fY); 1281 } 1282 } 1283 this->conicTo(mapped[0], mapped[1], w); 1284 startTheta = endTheta; 1285 } 1286 1287#ifndef SK_LEGACY_PATH_ARCTO_ENDPOINT 1288 // The final point should match the input point (by definition); replace it to 1289 // ensure that rounding errors in the above math don't cause any problems. 1290 this->setLastPt(x, y); 1291#endif 1292 return *this; 1293} 1294 1295SkPath& SkPath::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, SkPath::ArcSize largeArc, 1296 SkPathDirection sweep, SkScalar dx, SkScalar dy) { 1297 SkPoint currentPoint; 1298 this->getLastPt(¤tPoint); 1299 return this->arcTo(rx, ry, xAxisRotate, largeArc, sweep, 1300 currentPoint.fX + dx, currentPoint.fY + dy); 1301} 1302 1303SkPath& SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) { 1304 if (oval.isEmpty() || 0 == sweepAngle) { 1305 return *this; 1306 } 1307 1308 const SkScalar kFullCircleAngle = SkIntToScalar(360); 1309 1310 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) { 1311 // We can treat the arc as an oval if it begins at one of our legal starting positions. 1312 // See SkPath::addOval() docs. 1313 SkScalar startOver90 = startAngle / 90.f; 1314 SkScalar startOver90I = SkScalarRoundToScalar(startOver90); 1315 SkScalar error = startOver90 - startOver90I; 1316 if (SkScalarNearlyEqual(error, 0)) { 1317 // Index 1 is at startAngle == 0. 1318 SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f); 1319 startIndex = startIndex < 0 ? startIndex + 4.f : startIndex; 1320 return this->addOval(oval, sweepAngle > 0 ? SkPathDirection::kCW : SkPathDirection::kCCW, 1321 (unsigned) startIndex); 1322 } 1323 } 1324 return this->arcTo(oval, startAngle, sweepAngle, true); 1325} 1326 1327/* 1328 Need to handle the case when the angle is sharp, and our computed end-points 1329 for the arc go behind pt1 and/or p2... 1330*/ 1331SkPath& SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) { 1332 this->injectMoveToIfNeeded(); 1333 1334 if (radius == 0) { 1335 return this->lineTo(x1, y1); 1336 } 1337 1338 // need to know our prev pt so we can construct tangent vectors 1339 SkPoint start; 1340 this->getLastPt(&start); 1341 1342 // need double precision for these calcs. 1343 SkDVector befored, afterd; 1344 befored.set({x1 - start.fX, y1 - start.fY}).normalize(); 1345 afterd.set({x2 - x1, y2 - y1}).normalize(); 1346 double cosh = befored.dot(afterd); 1347 double sinh = befored.cross(afterd); 1348 1349 if (!befored.isFinite() || !afterd.isFinite() || SkScalarNearlyZero(SkDoubleToScalar(sinh))) { 1350 return this->lineTo(x1, y1); 1351 } 1352 1353 // safe to convert back to floats now 1354 SkVector before = befored.asSkVector(); 1355 SkVector after = afterd.asSkVector(); 1356 SkScalar dist = SkScalarAbs(SkDoubleToScalar(radius * (1 - cosh) / sinh)); 1357 SkScalar xx = x1 - dist * before.fX; 1358 SkScalar yy = y1 - dist * before.fY; 1359 after.setLength(dist); 1360 this->lineTo(xx, yy); 1361 SkScalar weight = SkScalarSqrt(SkDoubleToScalar(SK_ScalarHalf + cosh * 0.5)); 1362 return this->conicTo(x1, y1, x1 + after.fX, y1 + after.fY, weight); 1363} 1364 1365/////////////////////////////////////////////////////////////////////////////// 1366 1367SkPath& SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy, AddPathMode mode) { 1368 SkMatrix matrix; 1369 1370 matrix.setTranslate(dx, dy); 1371 return this->addPath(path, matrix, mode); 1372} 1373 1374SkPath& SkPath::addPath(const SkPath& srcPath, const SkMatrix& matrix, AddPathMode mode) { 1375 if (srcPath.isEmpty()) { 1376 return *this; 1377 } 1378 1379 // Detect if we're trying to add ourself 1380 const SkPath* src = &srcPath; 1381 SkTLazy<SkPath> tmp; 1382 if (this == src) { 1383 src = tmp.set(srcPath); 1384 } 1385 1386 if (kAppend_AddPathMode == mode && !matrix.hasPerspective()) { 1387 fLastMoveToIndex = this->countPoints() + src->fLastMoveToIndex; 1388 1389 SkPathRef::Editor ed(&fPathRef); 1390 auto [newPts, newWeights] = ed.growForVerbsInPath(*src->fPathRef); 1391 matrix.mapPoints(newPts, src->fPathRef->points(), src->countPoints()); 1392 if (int numWeights = src->fPathRef->countWeights()) { 1393 memcpy(newWeights, src->fPathRef->conicWeights(), numWeights * sizeof(newWeights[0])); 1394 } 1395 // fiddle with fLastMoveToIndex, as we do in SkPath::close() 1396 if ((SkPathVerb)fPathRef->verbsEnd()[-1] == SkPathVerb::kClose) { 1397 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1); 1398 } 1399 return this->dirtyAfterEdit(); 1400 } 1401 1402 SkMatrixPriv::MapPtsProc mapPtsProc = SkMatrixPriv::GetMapPtsProc(matrix); 1403 bool firstVerb = true; 1404 for (auto [verb, pts, w] : SkPathPriv::Iterate(*src)) { 1405 switch (verb) { 1406 SkPoint mappedPts[3]; 1407 case SkPathVerb::kMove: 1408 mapPtsProc(matrix, mappedPts, &pts[0], 1); 1409 if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) { 1410 injectMoveToIfNeeded(); // In case last contour is closed 1411 SkPoint lastPt; 1412 // don't add lineTo if it is degenerate 1413 if (fLastMoveToIndex < 0 || !this->getLastPt(&lastPt) || 1414 lastPt != mappedPts[0]) { 1415 this->lineTo(mappedPts[0]); 1416 } 1417 } else { 1418 this->moveTo(mappedPts[0]); 1419 } 1420 break; 1421 case SkPathVerb::kLine: 1422 mapPtsProc(matrix, mappedPts, &pts[1], 1); 1423 this->lineTo(mappedPts[0]); 1424 break; 1425 case SkPathVerb::kQuad: 1426 mapPtsProc(matrix, mappedPts, &pts[1], 2); 1427 this->quadTo(mappedPts[0], mappedPts[1]); 1428 break; 1429 case SkPathVerb::kConic: 1430 mapPtsProc(matrix, mappedPts, &pts[1], 2); 1431 this->conicTo(mappedPts[0], mappedPts[1], *w); 1432 break; 1433 case SkPathVerb::kCubic: 1434 mapPtsProc(matrix, mappedPts, &pts[1], 3); 1435 this->cubicTo(mappedPts[0], mappedPts[1], mappedPts[2]); 1436 break; 1437 case SkPathVerb::kClose: 1438 this->close(); 1439 break; 1440 } 1441 firstVerb = false; 1442 } 1443 return *this; 1444} 1445 1446/////////////////////////////////////////////////////////////////////////////// 1447 1448// ignore the last point of the 1st contour 1449SkPath& SkPath::reversePathTo(const SkPath& path) { 1450 if (path.fPathRef->fVerbs.count() == 0) { 1451 return *this; 1452 } 1453 1454 const uint8_t* verbs = path.fPathRef->verbsEnd(); 1455 const uint8_t* verbsBegin = path.fPathRef->verbsBegin(); 1456 SkASSERT(verbsBegin[0] == kMove_Verb); 1457 const SkPoint* pts = path.fPathRef->pointsEnd() - 1; 1458 const SkScalar* conicWeights = path.fPathRef->conicWeightsEnd(); 1459 1460 while (verbs > verbsBegin) { 1461 uint8_t v = *--verbs; 1462 pts -= SkPathPriv::PtsInVerb(v); 1463 switch (v) { 1464 case kMove_Verb: 1465 // if the path has multiple contours, stop after reversing the last 1466 return *this; 1467 case kLine_Verb: 1468 this->lineTo(pts[0]); 1469 break; 1470 case kQuad_Verb: 1471 this->quadTo(pts[1], pts[0]); 1472 break; 1473 case kConic_Verb: 1474 this->conicTo(pts[1], pts[0], *--conicWeights); 1475 break; 1476 case kCubic_Verb: 1477 this->cubicTo(pts[2], pts[1], pts[0]); 1478 break; 1479 case kClose_Verb: 1480 break; 1481 default: 1482 SkDEBUGFAIL("bad verb"); 1483 break; 1484 } 1485 } 1486 return *this; 1487} 1488 1489SkPath& SkPath::reverseAddPath(const SkPath& srcPath) { 1490 // Detect if we're trying to add ourself 1491 const SkPath* src = &srcPath; 1492 SkTLazy<SkPath> tmp; 1493 if (this == src) { 1494 src = tmp.set(srcPath); 1495 } 1496 1497 const uint8_t* verbsBegin = src->fPathRef->verbsBegin(); 1498 const uint8_t* verbs = src->fPathRef->verbsEnd(); 1499 const SkPoint* pts = src->fPathRef->pointsEnd(); 1500 const SkScalar* conicWeights = src->fPathRef->conicWeightsEnd(); 1501 1502 bool needMove = true; 1503 bool needClose = false; 1504 while (verbs > verbsBegin) { 1505 uint8_t v = *--verbs; 1506 int n = SkPathPriv::PtsInVerb(v); 1507 1508 if (needMove) { 1509 --pts; 1510 this->moveTo(pts->fX, pts->fY); 1511 needMove = false; 1512 } 1513 pts -= n; 1514 switch (v) { 1515 case kMove_Verb: 1516 if (needClose) { 1517 this->close(); 1518 needClose = false; 1519 } 1520 needMove = true; 1521 pts += 1; // so we see the point in "if (needMove)" above 1522 break; 1523 case kLine_Verb: 1524 this->lineTo(pts[0]); 1525 break; 1526 case kQuad_Verb: 1527 this->quadTo(pts[1], pts[0]); 1528 break; 1529 case kConic_Verb: 1530 this->conicTo(pts[1], pts[0], *--conicWeights); 1531 break; 1532 case kCubic_Verb: 1533 this->cubicTo(pts[2], pts[1], pts[0]); 1534 break; 1535 case kClose_Verb: 1536 needClose = true; 1537 break; 1538 default: 1539 SkDEBUGFAIL("unexpected verb"); 1540 } 1541 } 1542 return *this; 1543} 1544 1545/////////////////////////////////////////////////////////////////////////////// 1546 1547void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const { 1548 SkMatrix matrix; 1549 1550 matrix.setTranslate(dx, dy); 1551 this->transform(matrix, dst); 1552} 1553 1554static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4], 1555 int level = 2) { 1556 if (--level >= 0) { 1557 SkPoint tmp[7]; 1558 1559 SkChopCubicAtHalf(pts, tmp); 1560 subdivide_cubic_to(path, &tmp[0], level); 1561 subdivide_cubic_to(path, &tmp[3], level); 1562 } else { 1563 path->cubicTo(pts[1], pts[2], pts[3]); 1564 } 1565} 1566 1567void SkPath::transform(const SkMatrix& matrix, SkPath* dst, SkApplyPerspectiveClip pc) const { 1568 if (matrix.isIdentity()) { 1569 if (dst != nullptr && dst != this) { 1570 *dst = *this; 1571 } 1572 return; 1573 } 1574 1575 SkDEBUGCODE(this->validate();) 1576 if (dst == nullptr) { 1577 dst = (SkPath*)this; 1578 } 1579 1580 if (matrix.hasPerspective()) { 1581 SkPath tmp; 1582 tmp.fFillType = fFillType; 1583 1584 SkPath clipped; 1585 const SkPath* src = this; 1586 if (pc == SkApplyPerspectiveClip::kYes && 1587 SkPathPriv::PerspectiveClip(*this, matrix, &clipped)) 1588 { 1589 src = &clipped; 1590 } 1591 1592 SkPath::Iter iter(*src, false); 1593 SkPoint pts[4]; 1594 SkPath::Verb verb; 1595 1596 while ((verb = iter.next(pts)) != kDone_Verb) { 1597 switch (verb) { 1598 case kMove_Verb: 1599 tmp.moveTo(pts[0]); 1600 break; 1601 case kLine_Verb: 1602 tmp.lineTo(pts[1]); 1603 break; 1604 case kQuad_Verb: 1605 // promote the quad to a conic 1606 tmp.conicTo(pts[1], pts[2], 1607 SkConic::TransformW(pts, SK_Scalar1, matrix)); 1608 break; 1609 case kConic_Verb: 1610 tmp.conicTo(pts[1], pts[2], 1611 SkConic::TransformW(pts, iter.conicWeight(), matrix)); 1612 break; 1613 case kCubic_Verb: 1614 subdivide_cubic_to(&tmp, pts); 1615 break; 1616 case kClose_Verb: 1617 tmp.close(); 1618 break; 1619 default: 1620 SkDEBUGFAIL("unknown verb"); 1621 break; 1622 } 1623 } 1624 1625 dst->swap(tmp); 1626 SkPathRef::Editor ed(&dst->fPathRef); 1627 matrix.mapPoints(ed.writablePoints(), ed.pathRef()->countPoints()); 1628 dst->setFirstDirection(SkPathFirstDirection::kUnknown); 1629 } else { 1630 SkPathConvexity convexity = this->getConvexityOrUnknown(); 1631 1632 SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef, matrix); 1633 1634 if (this != dst) { 1635 dst->fLastMoveToIndex = fLastMoveToIndex; 1636 dst->fFillType = fFillType; 1637 dst->fIsVolatile = fIsVolatile; 1638 } 1639 1640 // Due to finite/fragile float numerics, we can't assume that a convex path remains 1641 // convex after a transformation, so mark it as unknown here. 1642 // However, some transformations are thought to be safe: 1643 // axis-aligned values under scale/translate. 1644 // 1645 if (convexity == SkPathConvexity::kConvex && 1646 (!matrix.isScaleTranslate() || !SkPathPriv::IsAxisAligned(*this))) { 1647 // Not safe to still assume we're convex... 1648 convexity = SkPathConvexity::kUnknown; 1649 } 1650 dst->setConvexity(convexity); 1651 1652 if (this->getFirstDirection() == SkPathFirstDirection::kUnknown) { 1653 dst->setFirstDirection(SkPathFirstDirection::kUnknown); 1654 } else { 1655 SkScalar det2x2 = 1656 matrix.get(SkMatrix::kMScaleX) * matrix.get(SkMatrix::kMScaleY) - 1657 matrix.get(SkMatrix::kMSkewX) * matrix.get(SkMatrix::kMSkewY); 1658 if (det2x2 < 0) { 1659 dst->setFirstDirection( 1660 SkPathPriv::OppositeFirstDirection( 1661 (SkPathFirstDirection)this->getFirstDirection())); 1662 } else if (det2x2 > 0) { 1663 dst->setFirstDirection(this->getFirstDirection()); 1664 } else { 1665 dst->setFirstDirection(SkPathFirstDirection::kUnknown); 1666 } 1667 } 1668 1669 SkDEBUGCODE(dst->validate();) 1670 } 1671} 1672 1673/////////////////////////////////////////////////////////////////////////////// 1674/////////////////////////////////////////////////////////////////////////////// 1675 1676SkPath::Iter::Iter() { 1677#ifdef SK_DEBUG 1678 fPts = nullptr; 1679 fConicWeights = nullptr; 1680 fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0; 1681 fForceClose = fCloseLine = false; 1682#endif 1683 // need to init enough to make next() harmlessly return kDone_Verb 1684 fVerbs = nullptr; 1685 fVerbStop = nullptr; 1686 fNeedClose = false; 1687} 1688 1689SkPath::Iter::Iter(const SkPath& path, bool forceClose) { 1690 this->setPath(path, forceClose); 1691} 1692 1693void SkPath::Iter::setPath(const SkPath& path, bool forceClose) { 1694 fPts = path.fPathRef->points(); 1695 fVerbs = path.fPathRef->verbsBegin(); 1696 fVerbStop = path.fPathRef->verbsEnd(); 1697 fConicWeights = path.fPathRef->conicWeights(); 1698 if (fConicWeights) { 1699 fConicWeights -= 1; // begin one behind 1700 } 1701 fLastPt.fX = fLastPt.fY = 0; 1702 fMoveTo.fX = fMoveTo.fY = 0; 1703 fForceClose = SkToU8(forceClose); 1704 fNeedClose = false; 1705} 1706 1707bool SkPath::Iter::isClosedContour() const { 1708 if (fVerbs == nullptr || fVerbs == fVerbStop) { 1709 return false; 1710 } 1711 if (fForceClose) { 1712 return true; 1713 } 1714 1715 const uint8_t* verbs = fVerbs; 1716 const uint8_t* stop = fVerbStop; 1717 1718 if (kMove_Verb == *verbs) { 1719 verbs += 1; // skip the initial moveto 1720 } 1721 1722 while (verbs < stop) { 1723 // verbs points one beyond the current verb, decrement first. 1724 unsigned v = *verbs++; 1725 if (kMove_Verb == v) { 1726 break; 1727 } 1728 if (kClose_Verb == v) { 1729 return true; 1730 } 1731 } 1732 return false; 1733} 1734 1735SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) { 1736 SkASSERT(pts); 1737 if (fLastPt != fMoveTo) { 1738 // A special case: if both points are NaN, SkPoint::operation== returns 1739 // false, but the iterator expects that they are treated as the same. 1740 // (consider SkPoint is a 2-dimension float point). 1741 if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) || 1742 SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) { 1743 return kClose_Verb; 1744 } 1745 1746 pts[0] = fLastPt; 1747 pts[1] = fMoveTo; 1748 fLastPt = fMoveTo; 1749 fCloseLine = true; 1750 return kLine_Verb; 1751 } else { 1752 pts[0] = fMoveTo; 1753 return kClose_Verb; 1754 } 1755} 1756 1757SkPath::Verb SkPath::Iter::next(SkPoint ptsParam[4]) { 1758 SkASSERT(ptsParam); 1759 1760 if (fVerbs == fVerbStop) { 1761 // Close the curve if requested and if there is some curve to close 1762 if (fNeedClose) { 1763 if (kLine_Verb == this->autoClose(ptsParam)) { 1764 return kLine_Verb; 1765 } 1766 fNeedClose = false; 1767 return kClose_Verb; 1768 } 1769 return kDone_Verb; 1770 } 1771 1772 unsigned verb = *fVerbs++; 1773 const SkPoint* SK_RESTRICT srcPts = fPts; 1774 SkPoint* SK_RESTRICT pts = ptsParam; 1775 1776 switch (verb) { 1777 case kMove_Verb: 1778 if (fNeedClose) { 1779 fVerbs--; // move back one verb 1780 verb = this->autoClose(pts); 1781 if (verb == kClose_Verb) { 1782 fNeedClose = false; 1783 } 1784 return (Verb)verb; 1785 } 1786 if (fVerbs == fVerbStop) { // might be a trailing moveto 1787 return kDone_Verb; 1788 } 1789 fMoveTo = *srcPts; 1790 pts[0] = *srcPts; 1791 srcPts += 1; 1792 fLastPt = fMoveTo; 1793 fNeedClose = fForceClose; 1794 break; 1795 case kLine_Verb: 1796 pts[0] = fLastPt; 1797 pts[1] = srcPts[0]; 1798 fLastPt = srcPts[0]; 1799 fCloseLine = false; 1800 srcPts += 1; 1801 break; 1802 case kConic_Verb: 1803 fConicWeights += 1; 1804 [[fallthrough]]; 1805 case kQuad_Verb: 1806 pts[0] = fLastPt; 1807 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint)); 1808 fLastPt = srcPts[1]; 1809 srcPts += 2; 1810 break; 1811 case kCubic_Verb: 1812 pts[0] = fLastPt; 1813 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint)); 1814 fLastPt = srcPts[2]; 1815 srcPts += 3; 1816 break; 1817 case kClose_Verb: 1818 verb = this->autoClose(pts); 1819 if (verb == kLine_Verb) { 1820 fVerbs--; // move back one verb 1821 } else { 1822 fNeedClose = false; 1823 } 1824 fLastPt = fMoveTo; 1825 break; 1826 } 1827 fPts = srcPts; 1828 return (Verb)verb; 1829} 1830 1831void SkPath::RawIter::setPath(const SkPath& path) { 1832 SkPathPriv::Iterate iterate(path); 1833 fIter = iterate.begin(); 1834 fEnd = iterate.end(); 1835} 1836 1837SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) { 1838 if (!(fIter != fEnd)) { 1839 return kDone_Verb; 1840 } 1841 auto [verb, iterPts, weights] = *fIter; 1842 int numPts; 1843 switch (verb) { 1844 case SkPathVerb::kMove: numPts = 1; break; 1845 case SkPathVerb::kLine: numPts = 2; break; 1846 case SkPathVerb::kQuad: numPts = 3; break; 1847 case SkPathVerb::kConic: 1848 numPts = 3; 1849 fConicWeight = *weights; 1850 break; 1851 case SkPathVerb::kCubic: numPts = 4; break; 1852 case SkPathVerb::kClose: numPts = 0; break; 1853 } 1854 memcpy(pts, iterPts, sizeof(SkPoint) * numPts); 1855 ++fIter; 1856 return (Verb) verb; 1857} 1858 1859/////////////////////////////////////////////////////////////////////////////// 1860 1861#include "include/core/SkStream.h" 1862#include "include/core/SkString.h" 1863#include "src/core/SkStringUtils.h" 1864 1865static void append_params(SkString* str, const char label[], const SkPoint pts[], 1866 int count, SkScalarAsStringType strType, SkScalar conicWeight = -12345) { 1867 str->append(label); 1868 str->append("("); 1869 1870 const SkScalar* values = &pts[0].fX; 1871 count *= 2; 1872 1873 for (int i = 0; i < count; ++i) { 1874 SkAppendScalar(str, values[i], strType); 1875 if (i < count - 1) { 1876 str->append(", "); 1877 } 1878 } 1879 if (conicWeight != -12345) { 1880 str->append(", "); 1881 SkAppendScalar(str, conicWeight, strType); 1882 } 1883 str->append(");"); 1884 if (kHex_SkScalarAsStringType == strType) { 1885 str->append(" // "); 1886 for (int i = 0; i < count; ++i) { 1887 SkAppendScalarDec(str, values[i]); 1888 if (i < count - 1) { 1889 str->append(", "); 1890 } 1891 } 1892 if (conicWeight >= 0) { 1893 str->append(", "); 1894 SkAppendScalarDec(str, conicWeight); 1895 } 1896 } 1897 str->append("\n"); 1898} 1899 1900void SkPath::dump(SkWStream* wStream, bool dumpAsHex) const { 1901 SkScalarAsStringType asType = dumpAsHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType; 1902 Iter iter(*this, false); 1903 SkPoint pts[4]; 1904 Verb verb; 1905 1906 SkString builder; 1907 char const * const gFillTypeStrs[] = { 1908 "Winding", 1909 "EvenOdd", 1910 "InverseWinding", 1911 "InverseEvenOdd", 1912 }; 1913 builder.printf("path.setFillType(SkPathFillType::k%s);\n", 1914 gFillTypeStrs[(int) this->getFillType()]); 1915 while ((verb = iter.next(pts)) != kDone_Verb) { 1916 switch (verb) { 1917 case kMove_Verb: 1918 append_params(&builder, "path.moveTo", &pts[0], 1, asType); 1919 break; 1920 case kLine_Verb: 1921 append_params(&builder, "path.lineTo", &pts[1], 1, asType); 1922 break; 1923 case kQuad_Verb: 1924 append_params(&builder, "path.quadTo", &pts[1], 2, asType); 1925 break; 1926 case kConic_Verb: 1927 append_params(&builder, "path.conicTo", &pts[1], 2, asType, iter.conicWeight()); 1928 break; 1929 case kCubic_Verb: 1930 append_params(&builder, "path.cubicTo", &pts[1], 3, asType); 1931 break; 1932 case kClose_Verb: 1933 builder.append("path.close();\n"); 1934 break; 1935 default: 1936 SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb); 1937 verb = kDone_Verb; // stop the loop 1938 break; 1939 } 1940 if (!wStream && builder.size()) { 1941 SkDebugf("%s", builder.c_str()); 1942 builder.reset(); 1943 } 1944 } 1945 if (wStream) { 1946 wStream->writeText(builder.c_str()); 1947 } 1948} 1949 1950void SkPath::dump(std::string& desc, int depth) const { 1951 std::string split(depth, '\t'); 1952 desc += split + "\n SkPath:{ \n"; 1953 Iter iter(*this, false); 1954 SkPoint points[4]; 1955 Verb verb; 1956 1957 SkString descSk; 1958 char const * const gFillTypeStrs[] = { 1959 "Winding", 1960 "EvenOdd", 1961 "InverseWinding", 1962 "InverseEvenOdd", 1963 }; 1964 descSk.printf("path.setFillType(SkPath::k%s_FillType);\n", gFillTypeStrs[(int) this->getFillType()]); 1965 while ((verb = iter.next(points)) != kDone_Verb) { 1966 switch (verb) { 1967 case kMove_Verb: 1968 append_params(&descSk, "path.moveTo", &points[0], 1, kDec_SkScalarAsStringType); 1969 break; 1970 case kLine_Verb: 1971 append_params(&descSk, "path.lineTo", &points[1], 1, kDec_SkScalarAsStringType); 1972 break; 1973 case kQuad_Verb: 1974 append_params(&descSk, "path.quadTo", &points[1], 2, kDec_SkScalarAsStringType); 1975 break; 1976 case kConic_Verb: 1977 append_params(&descSk, "path.conicTo", &points[1], 2, kDec_SkScalarAsStringType, iter.conicWeight()); 1978 break; 1979 case kCubic_Verb: 1980 append_params(&descSk, "path.cubicTo", &points[1], 3, kDec_SkScalarAsStringType); 1981 break; 1982 case kClose_Verb: 1983 descSk.append("path.close();\n"); 1984 break; 1985 default: 1986 break; 1987 } 1988 if (descSk.size()) { 1989 desc += split + std::string(descSk.c_str()); 1990 descSk.reset(); 1991 } 1992 } 1993 desc += split + "}\n"; 1994} 1995 1996void SkPath::dumpArrays(SkWStream* wStream, bool dumpAsHex) const { 1997 SkString builder; 1998 1999 auto bool_str = [](bool v) { return v ? "true" : "false"; }; 2000 2001 builder.appendf("// fBoundsIsDirty = %s\n", bool_str(fPathRef->fBoundsIsDirty)); 2002 builder.appendf("// fGenerationID = %d\n", fPathRef->fGenerationID); 2003 builder.appendf("// fSegmentMask = %d\n", fPathRef->fSegmentMask); 2004 builder.appendf("// fIsOval = %s\n", bool_str(fPathRef->fIsOval)); 2005 builder.appendf("// fIsRRect = %s\n", bool_str(fPathRef->fIsRRect)); 2006 2007 auto append_scalar = [&](SkScalar v) { 2008 if (dumpAsHex) { 2009 builder.appendf("SkBits2Float(0x%08X) /* %g */", SkFloat2Bits(v), v); 2010 } else { 2011 builder.appendf("%g", v); 2012 } 2013 }; 2014 2015 builder.append("const SkPoint path_points[] = {\n"); 2016 for (int i = 0; i < this->countPoints(); ++i) { 2017 SkPoint p = this->getPoint(i); 2018 builder.append(" { "); 2019 append_scalar(p.fX); 2020 builder.append(", "); 2021 append_scalar(p.fY); 2022 builder.append(" },\n"); 2023 } 2024 builder.append("};\n"); 2025 2026 const char* gVerbStrs[] = { 2027 "Move", "Line", "Quad", "Conic", "Cubic", "Close" 2028 }; 2029 builder.append("const uint8_t path_verbs[] = {\n "); 2030 for (auto v = fPathRef->verbsBegin(); v != fPathRef->verbsEnd(); ++v) { 2031 builder.appendf("(uint8_t)SkPathVerb::k%s, ", gVerbStrs[*v]); 2032 } 2033 builder.append("\n};\n"); 2034 2035 const int nConics = fPathRef->conicWeightsEnd() - fPathRef->conicWeights(); 2036 if (nConics) { 2037 builder.append("const SkScalar path_conics[] = {\n "); 2038 for (auto c = fPathRef->conicWeights(); c != fPathRef->conicWeightsEnd(); ++c) { 2039 append_scalar(*c); 2040 builder.append(", "); 2041 } 2042 builder.append("\n};\n"); 2043 } 2044 2045 char const * const gFillTypeStrs[] = { 2046 "Winding", 2047 "EvenOdd", 2048 "InverseWinding", 2049 "InverseEvenOdd", 2050 }; 2051 2052 builder.appendf("SkPath path = SkPath::Make(path_points, %d, path_verbs, %d, %s, %d,\n", 2053 this->countPoints(), this->countVerbs(), 2054 nConics ? "path_conics" : "nullptr", nConics); 2055 builder.appendf(" SkPathFillType::k%s, %s);\n", 2056 gFillTypeStrs[(int)this->getFillType()], 2057 bool_str(fIsVolatile)); 2058 2059 if (wStream) { 2060 wStream->writeText(builder.c_str()); 2061 } else { 2062 SkDebugf("%s\n", builder.c_str()); 2063 } 2064} 2065 2066bool SkPath::isValidImpl() const { 2067 if ((fFillType & ~3) != 0) { 2068 return false; 2069 } 2070 2071#ifdef SK_DEBUG_PATH 2072 if (!fBoundsIsDirty) { 2073 SkRect bounds; 2074 2075 bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get()); 2076 if (SkToBool(fIsFinite) != isFinite) { 2077 return false; 2078 } 2079 2080 if (fPathRef->countPoints() <= 1) { 2081 // if we're empty, fBounds may be empty but translated, so we can't 2082 // necessarily compare to bounds directly 2083 // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will 2084 // be [2, 2, 2, 2] 2085 if (!bounds.isEmpty() || !fBounds.isEmpty()) { 2086 return false; 2087 } 2088 } else { 2089 if (bounds.isEmpty()) { 2090 if (!fBounds.isEmpty()) { 2091 return false; 2092 } 2093 } else { 2094 if (!fBounds.isEmpty()) { 2095 if (!fBounds.contains(bounds)) { 2096 return false; 2097 } 2098 } 2099 } 2100 } 2101 } 2102#endif // SK_DEBUG_PATH 2103 return true; 2104} 2105 2106/////////////////////////////////////////////////////////////////////////////// 2107 2108static int sign(SkScalar x) { return x < 0; } 2109#define kValueNeverReturnedBySign 2 2110 2111enum DirChange { 2112 kUnknown_DirChange, 2113 kLeft_DirChange, 2114 kRight_DirChange, 2115 kStraight_DirChange, 2116 kBackwards_DirChange, // if double back, allow simple lines to be convex 2117 kInvalid_DirChange 2118}; 2119 2120// only valid for a single contour 2121struct Convexicator { 2122 2123 /** The direction returned is only valid if the path is determined convex */ 2124 SkPathFirstDirection getFirstDirection() const { return fFirstDirection; } 2125 2126 void setMovePt(const SkPoint& pt) { 2127 fFirstPt = fLastPt = pt; 2128 fExpectedDir = kInvalid_DirChange; 2129 } 2130 2131 bool addPt(const SkPoint& pt) { 2132 if (fLastPt == pt) { 2133 return true; 2134 } 2135 // should only be true for first non-zero vector after setMovePt was called. 2136 if (fFirstPt == fLastPt && fExpectedDir == kInvalid_DirChange) { 2137 fLastVec = pt - fLastPt; 2138 fFirstVec = fLastVec; 2139 } else if (!this->addVec(pt - fLastPt)) { 2140 return false; 2141 } 2142 fLastPt = pt; 2143 return true; 2144 } 2145 2146 static SkPathConvexity BySign(const SkPoint points[], int count) { 2147 if (count <= 3) { 2148 // point, line, or triangle are always convex 2149 return SkPathConvexity::kConvex; 2150 } 2151 2152 const SkPoint* last = points + count; 2153 SkPoint currPt = *points++; 2154 SkPoint firstPt = currPt; 2155 int dxes = 0; 2156 int dyes = 0; 2157 int lastSx = kValueNeverReturnedBySign; 2158 int lastSy = kValueNeverReturnedBySign; 2159 for (int outerLoop = 0; outerLoop < 2; ++outerLoop ) { 2160 while (points != last) { 2161 SkVector vec = *points - currPt; 2162 if (!vec.isZero()) { 2163 // give up if vector construction failed 2164 if (!vec.isFinite()) { 2165 return SkPathConvexity::kUnknown; 2166 } 2167 int sx = sign(vec.fX); 2168 int sy = sign(vec.fY); 2169 dxes += (sx != lastSx); 2170 dyes += (sy != lastSy); 2171 if (dxes > 3 || dyes > 3) { 2172 return SkPathConvexity::kConcave; 2173 } 2174 lastSx = sx; 2175 lastSy = sy; 2176 } 2177 currPt = *points++; 2178 if (outerLoop) { 2179 break; 2180 } 2181 } 2182 points = &firstPt; 2183 } 2184 return SkPathConvexity::kConvex; // that is, it may be convex, don't know yet 2185 } 2186 2187 bool close() { 2188 // If this was an explicit close, there was already a lineTo to fFirstPoint, so this 2189 // addPt() is a no-op. Otherwise, the addPt implicitly closes the contour. In either case, 2190 // we have to check the direction change along the first vector in case it is concave. 2191 return this->addPt(fFirstPt) && this->addVec(fFirstVec); 2192 } 2193 2194 bool isFinite() const { 2195 return fIsFinite; 2196 } 2197 2198 int reversals() const { 2199 return fReversals; 2200 } 2201 2202private: 2203 DirChange directionChange(const SkVector& curVec) { 2204 SkScalar cross = SkPoint::CrossProduct(fLastVec, curVec); 2205 if (!SkScalarIsFinite(cross)) { 2206 return kUnknown_DirChange; 2207 } 2208 if (cross == 0) { 2209 return fLastVec.dot(curVec) < 0 ? kBackwards_DirChange : kStraight_DirChange; 2210 } 2211 return 1 == SkScalarSignAsInt(cross) ? kRight_DirChange : kLeft_DirChange; 2212 } 2213 2214 bool addVec(const SkVector& curVec) { 2215 DirChange dir = this->directionChange(curVec); 2216 switch (dir) { 2217 case kLeft_DirChange: // fall through 2218 case kRight_DirChange: 2219 if (kInvalid_DirChange == fExpectedDir) { 2220 fExpectedDir = dir; 2221 fFirstDirection = (kRight_DirChange == dir) ? SkPathFirstDirection::kCW 2222 : SkPathFirstDirection::kCCW; 2223 } else if (dir != fExpectedDir) { 2224 fFirstDirection = SkPathFirstDirection::kUnknown; 2225 return false; 2226 } 2227 fLastVec = curVec; 2228 break; 2229 case kStraight_DirChange: 2230 break; 2231 case kBackwards_DirChange: 2232 // allow path to reverse direction twice 2233 // Given path.moveTo(0, 0); path.lineTo(1, 1); 2234 // - 1st reversal: direction change formed by line (0,0 1,1), line (1,1 0,0) 2235 // - 2nd reversal: direction change formed by line (1,1 0,0), line (0,0 1,1) 2236 fLastVec = curVec; 2237 return ++fReversals < 3; 2238 case kUnknown_DirChange: 2239 return (fIsFinite = false); 2240 case kInvalid_DirChange: 2241 SK_ABORT("Use of invalid direction change flag"); 2242 break; 2243 } 2244 return true; 2245 } 2246 2247 SkPoint fFirstPt {0, 0}; // The first point of the contour, e.g. moveTo(x,y) 2248 SkVector fFirstVec {0, 0}; // The direction leaving fFirstPt to the next vertex 2249 2250 SkPoint fLastPt {0, 0}; // The last point passed to addPt() 2251 SkVector fLastVec {0, 0}; // The direction that brought the path to fLastPt 2252 2253 DirChange fExpectedDir { kInvalid_DirChange }; 2254 SkPathFirstDirection fFirstDirection { SkPathFirstDirection::kUnknown }; 2255 int fReversals { 0 }; 2256 bool fIsFinite { true }; 2257}; 2258 2259SkPathConvexity SkPath::computeConvexity() const { 2260 auto setComputedConvexity = [=](SkPathConvexity convexity){ 2261 SkASSERT(SkPathConvexity::kUnknown != convexity); 2262 this->setConvexity(convexity); 2263 return convexity; 2264 }; 2265 2266 auto setFail = [=](){ 2267 return setComputedConvexity(SkPathConvexity::kConcave); 2268 }; 2269 2270 if (!this->isFinite()) { 2271 return setFail(); 2272 } 2273 2274 // pointCount potentially includes a block of leading moveTos and trailing moveTos. Convexity 2275 // only cares about the last of the initial moveTos and the verbs before the final moveTos. 2276 int pointCount = this->countPoints(); 2277 int skipCount = SkPathPriv::LeadingMoveToCount(*this) - 1; 2278 2279 if (fLastMoveToIndex >= 0) { 2280 if (fLastMoveToIndex == pointCount - 1) { 2281 // Find the last real verb that affects convexity 2282 auto verbs = fPathRef->verbsEnd() - 1; 2283 while(verbs > fPathRef->verbsBegin() && *verbs == Verb::kMove_Verb) { 2284 verbs--; 2285 pointCount--; 2286 } 2287 } else if (fLastMoveToIndex != skipCount) { 2288 // There's an additional moveTo between two blocks of other verbs, so the path must have 2289 // more than one contour and cannot be convex. 2290 return setComputedConvexity(SkPathConvexity::kConcave); 2291 } // else no trailing or intermediate moveTos to worry about 2292 } 2293 const SkPoint* points = fPathRef->points(); 2294 if (skipCount > 0) { 2295 points += skipCount; 2296 pointCount -= skipCount; 2297 } 2298 2299 // Check to see if path changes direction more than three times as quick concave test 2300 SkPathConvexity convexity = Convexicator::BySign(points, pointCount); 2301 if (SkPathConvexity::kConvex != convexity) { 2302 return setComputedConvexity(SkPathConvexity::kConcave); 2303 } 2304 2305 int contourCount = 0; 2306 bool needsClose = false; 2307 Convexicator state; 2308 2309 for (auto [verb, pts, wt] : SkPathPriv::Iterate(*this)) { 2310 // Looking for the last moveTo before non-move verbs start 2311 if (contourCount == 0) { 2312 if (verb == SkPathVerb::kMove) { 2313 state.setMovePt(pts[0]); 2314 } else { 2315 // Starting the actual contour, fall through to c=1 to add the points 2316 contourCount++; 2317 needsClose = true; 2318 } 2319 } 2320 // Accumulating points into the Convexicator until we hit a close or another move 2321 if (contourCount == 1) { 2322 if (verb == SkPathVerb::kClose || verb == SkPathVerb::kMove) { 2323 if (!state.close()) { 2324 return setFail(); 2325 } 2326 needsClose = false; 2327 contourCount++; 2328 } else { 2329 // lines add 1 point, cubics add 3, conics and quads add 2 2330 int count = SkPathPriv::PtsInVerb((unsigned) verb); 2331 SkASSERT(count > 0); 2332 for (int i = 1; i <= count; ++i) { 2333 if (!state.addPt(pts[i])) { 2334 return setFail(); 2335 } 2336 } 2337 } 2338 } else { 2339 // The first contour has closed and anything other than spurious trailing moves means 2340 // there's multiple contours and the path can't be convex 2341 if (verb != SkPathVerb::kMove) { 2342 return setFail(); 2343 } 2344 } 2345 } 2346 2347 // If the path isn't explicitly closed do so implicitly 2348 if (needsClose && !state.close()) { 2349 return setFail(); 2350 } 2351 2352 if (this->getFirstDirection() == SkPathFirstDirection::kUnknown) { 2353 if (state.getFirstDirection() == SkPathFirstDirection::kUnknown 2354 && !this->getBounds().isEmpty()) { 2355 return setComputedConvexity(state.reversals() < 3 ? 2356 SkPathConvexity::kConvex : SkPathConvexity::kConcave); 2357 } 2358 this->setFirstDirection(state.getFirstDirection()); 2359 } 2360 return setComputedConvexity(SkPathConvexity::kConvex); 2361} 2362 2363/////////////////////////////////////////////////////////////////////////////// 2364 2365class ContourIter { 2366public: 2367 ContourIter(const SkPathRef& pathRef); 2368 2369 bool done() const { return fDone; } 2370 // if !done() then these may be called 2371 int count() const { return fCurrPtCount; } 2372 const SkPoint* pts() const { return fCurrPt; } 2373 void next(); 2374 2375private: 2376 int fCurrPtCount; 2377 const SkPoint* fCurrPt; 2378 const uint8_t* fCurrVerb; 2379 const uint8_t* fStopVerbs; 2380 const SkScalar* fCurrConicWeight; 2381 bool fDone; 2382 SkDEBUGCODE(int fContourCounter;) 2383}; 2384 2385ContourIter::ContourIter(const SkPathRef& pathRef) { 2386 fStopVerbs = pathRef.verbsEnd(); 2387 fDone = false; 2388 fCurrPt = pathRef.points(); 2389 fCurrVerb = pathRef.verbsBegin(); 2390 fCurrConicWeight = pathRef.conicWeights(); 2391 fCurrPtCount = 0; 2392 SkDEBUGCODE(fContourCounter = 0;) 2393 this->next(); 2394} 2395 2396void ContourIter::next() { 2397 if (fCurrVerb >= fStopVerbs) { 2398 fDone = true; 2399 } 2400 if (fDone) { 2401 return; 2402 } 2403 2404 // skip pts of prev contour 2405 fCurrPt += fCurrPtCount; 2406 2407 SkASSERT(SkPath::kMove_Verb == fCurrVerb[0]); 2408 int ptCount = 1; // moveTo 2409 const uint8_t* verbs = fCurrVerb; 2410 2411 for (verbs++; verbs < fStopVerbs; verbs++) { 2412 switch (*verbs) { 2413 case SkPath::kMove_Verb: 2414 goto CONTOUR_END; 2415 case SkPath::kLine_Verb: 2416 ptCount += 1; 2417 break; 2418 case SkPath::kConic_Verb: 2419 fCurrConicWeight += 1; 2420 [[fallthrough]]; 2421 case SkPath::kQuad_Verb: 2422 ptCount += 2; 2423 break; 2424 case SkPath::kCubic_Verb: 2425 ptCount += 3; 2426 break; 2427 case SkPath::kClose_Verb: 2428 break; 2429 default: 2430 SkDEBUGFAIL("unexpected verb"); 2431 break; 2432 } 2433 } 2434CONTOUR_END: 2435 fCurrPtCount = ptCount; 2436 fCurrVerb = verbs; 2437 SkDEBUGCODE(++fContourCounter;) 2438} 2439 2440// returns cross product of (p1 - p0) and (p2 - p0) 2441static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) { 2442 SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0); 2443 // We may get 0 when the above subtracts underflow. We expect this to be 2444 // very rare and lazily promote to double. 2445 if (0 == cross) { 2446 double p0x = SkScalarToDouble(p0.fX); 2447 double p0y = SkScalarToDouble(p0.fY); 2448 2449 double p1x = SkScalarToDouble(p1.fX); 2450 double p1y = SkScalarToDouble(p1.fY); 2451 2452 double p2x = SkScalarToDouble(p2.fX); 2453 double p2y = SkScalarToDouble(p2.fY); 2454 2455 cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) - 2456 (p1y - p0y) * (p2x - p0x)); 2457 2458 } 2459 return cross; 2460} 2461 2462// Returns the first pt with the maximum Y coordinate 2463static int find_max_y(const SkPoint pts[], int count) { 2464 SkASSERT(count > 0); 2465 SkScalar max = pts[0].fY; 2466 int firstIndex = 0; 2467 for (int i = 1; i < count; ++i) { 2468 SkScalar y = pts[i].fY; 2469 if (y > max) { 2470 max = y; 2471 firstIndex = i; 2472 } 2473 } 2474 return firstIndex; 2475} 2476 2477static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) { 2478 int i = index; 2479 for (;;) { 2480 i = (i + inc) % n; 2481 if (i == index) { // we wrapped around, so abort 2482 break; 2483 } 2484 if (pts[index] != pts[i]) { // found a different point, success! 2485 break; 2486 } 2487 } 2488 return i; 2489} 2490 2491/** 2492 * Starting at index, and moving forward (incrementing), find the xmin and 2493 * xmax of the contiguous points that have the same Y. 2494 */ 2495static int find_min_max_x_at_y(const SkPoint pts[], int index, int n, 2496 int* maxIndexPtr) { 2497 const SkScalar y = pts[index].fY; 2498 SkScalar min = pts[index].fX; 2499 SkScalar max = min; 2500 int minIndex = index; 2501 int maxIndex = index; 2502 for (int i = index + 1; i < n; ++i) { 2503 if (pts[i].fY != y) { 2504 break; 2505 } 2506 SkScalar x = pts[i].fX; 2507 if (x < min) { 2508 min = x; 2509 minIndex = i; 2510 } else if (x > max) { 2511 max = x; 2512 maxIndex = i; 2513 } 2514 } 2515 *maxIndexPtr = maxIndex; 2516 return minIndex; 2517} 2518 2519static SkPathFirstDirection crossToDir(SkScalar cross) { 2520 return cross > 0 ? SkPathFirstDirection::kCW : SkPathFirstDirection::kCCW; 2521} 2522 2523/* 2524 * We loop through all contours, and keep the computed cross-product of the 2525 * contour that contained the global y-max. If we just look at the first 2526 * contour, we may find one that is wound the opposite way (correctly) since 2527 * it is the interior of a hole (e.g. 'o'). Thus we must find the contour 2528 * that is outer most (or at least has the global y-max) before we can consider 2529 * its cross product. 2530 */ 2531SkPathFirstDirection SkPathPriv::ComputeFirstDirection(const SkPath& path) { 2532 auto d = path.getFirstDirection(); 2533 if (d != SkPathFirstDirection::kUnknown) { 2534 return d; 2535 } 2536 2537 // We don't want to pay the cost for computing convexity if it is unknown, 2538 // so we call getConvexityOrUnknown() instead of isConvex(). 2539 if (path.getConvexityOrUnknown() == SkPathConvexity::kConvex) { 2540 SkASSERT(d == SkPathFirstDirection::kUnknown); 2541 return d; 2542 } 2543 2544 ContourIter iter(*path.fPathRef); 2545 2546 // initialize with our logical y-min 2547 SkScalar ymax = path.getBounds().fTop; 2548 SkScalar ymaxCross = 0; 2549 2550 for (; !iter.done(); iter.next()) { 2551 int n = iter.count(); 2552 if (n < 3) { 2553 continue; 2554 } 2555 2556 const SkPoint* pts = iter.pts(); 2557 SkScalar cross = 0; 2558 int index = find_max_y(pts, n); 2559 if (pts[index].fY < ymax) { 2560 continue; 2561 } 2562 2563 // If there is more than 1 distinct point at the y-max, we take the 2564 // x-min and x-max of them and just subtract to compute the dir. 2565 if (pts[(index + 1) % n].fY == pts[index].fY) { 2566 int maxIndex; 2567 int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex); 2568 if (minIndex == maxIndex) { 2569 goto TRY_CROSSPROD; 2570 } 2571 SkASSERT(pts[minIndex].fY == pts[index].fY); 2572 SkASSERT(pts[maxIndex].fY == pts[index].fY); 2573 SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX); 2574 // we just subtract the indices, and let that auto-convert to 2575 // SkScalar, since we just want - or + to signal the direction. 2576 cross = minIndex - maxIndex; 2577 } else { 2578 TRY_CROSSPROD: 2579 // Find a next and prev index to use for the cross-product test, 2580 // but we try to find pts that form non-zero vectors from pts[index] 2581 // 2582 // Its possible that we can't find two non-degenerate vectors, so 2583 // we have to guard our search (e.g. all the pts could be in the 2584 // same place). 2585 2586 // we pass n - 1 instead of -1 so we don't foul up % operator by 2587 // passing it a negative LH argument. 2588 int prev = find_diff_pt(pts, index, n, n - 1); 2589 if (prev == index) { 2590 // completely degenerate, skip to next contour 2591 continue; 2592 } 2593 int next = find_diff_pt(pts, index, n, 1); 2594 SkASSERT(next != index); 2595 cross = cross_prod(pts[prev], pts[index], pts[next]); 2596 // if we get a zero and the points are horizontal, then we look at the spread in 2597 // x-direction. We really should continue to walk away from the degeneracy until 2598 // there is a divergence. 2599 if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) { 2600 // construct the subtract so we get the correct Direction below 2601 cross = pts[index].fX - pts[next].fX; 2602 } 2603 } 2604 2605 if (cross) { 2606 // record our best guess so far 2607 ymax = pts[index].fY; 2608 ymaxCross = cross; 2609 } 2610 } 2611 if (ymaxCross) { 2612 d = crossToDir(ymaxCross); 2613 path.setFirstDirection(d); 2614 } 2615 return d; // may still be kUnknown 2616} 2617 2618/////////////////////////////////////////////////////////////////////////////// 2619 2620static bool between(SkScalar a, SkScalar b, SkScalar c) { 2621 SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0) 2622 || (SkScalarNearlyZero(a) && SkScalarNearlyZero(b) && SkScalarNearlyZero(c))); 2623 return (a - b) * (c - b) <= 0; 2624} 2625 2626static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3, 2627 SkScalar t) { 2628 SkScalar A = c3 + 3*(c1 - c2) - c0; 2629 SkScalar B = 3*(c2 - c1 - c1 + c0); 2630 SkScalar C = 3*(c1 - c0); 2631 SkScalar D = c0; 2632 return poly_eval(A, B, C, D, t); 2633} 2634 2635template <size_t N> static void find_minmax(const SkPoint pts[], 2636 SkScalar* minPtr, SkScalar* maxPtr) { 2637 SkScalar min, max; 2638 min = max = pts[0].fX; 2639 for (size_t i = 1; i < N; ++i) { 2640 min = std::min(min, pts[i].fX); 2641 max = std::max(max, pts[i].fX); 2642 } 2643 *minPtr = min; 2644 *maxPtr = max; 2645} 2646 2647static bool checkOnCurve(SkScalar x, SkScalar y, const SkPoint& start, const SkPoint& end) { 2648 if (start.fY == end.fY) { 2649 return between(start.fX, x, end.fX) && x != end.fX; 2650 } else { 2651 return x == start.fX && y == start.fY; 2652 } 2653} 2654 2655static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2656 SkScalar y0 = pts[0].fY; 2657 SkScalar y3 = pts[3].fY; 2658 2659 int dir = 1; 2660 if (y0 > y3) { 2661 using std::swap; 2662 swap(y0, y3); 2663 dir = -1; 2664 } 2665 if (y < y0 || y > y3) { 2666 return 0; 2667 } 2668 if (checkOnCurve(x, y, pts[0], pts[3])) { 2669 *onCurveCount += 1; 2670 return 0; 2671 } 2672 if (y == y3) { 2673 return 0; 2674 } 2675 2676 // quickreject or quickaccept 2677 SkScalar min, max; 2678 find_minmax<4>(pts, &min, &max); 2679 if (x < min) { 2680 return 0; 2681 } 2682 if (x > max) { 2683 return dir; 2684 } 2685 2686 // compute the actual x(t) value 2687 SkScalar t; 2688 if (!SkCubicClipper::ChopMonoAtY(pts, y, &t)) { 2689 return 0; 2690 } 2691 SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t); 2692 if (SkScalarNearlyEqual(xt, x)) { 2693 if (x != pts[3].fX || y != pts[3].fY) { // don't test end points; they're start points 2694 *onCurveCount += 1; 2695 return 0; 2696 } 2697 } 2698 return xt < x ? dir : 0; 2699} 2700 2701static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2702 SkPoint dst[10]; 2703 int n = SkChopCubicAtYExtrema(pts, dst); 2704 int w = 0; 2705 for (int i = 0; i <= n; ++i) { 2706 w += winding_mono_cubic(&dst[i * 3], x, y, onCurveCount); 2707 } 2708 return w; 2709} 2710 2711static double conic_eval_numerator(const SkScalar src[], SkScalar w, SkScalar t) { 2712 SkASSERT(src); 2713 SkASSERT(t >= 0 && t <= 1); 2714 SkScalar src2w = src[2] * w; 2715 SkScalar C = src[0]; 2716 SkScalar A = src[4] - 2 * src2w + C; 2717 SkScalar B = 2 * (src2w - C); 2718 return poly_eval(A, B, C, t); 2719} 2720 2721 2722static double conic_eval_denominator(SkScalar w, SkScalar t) { 2723 SkScalar B = 2 * (w - 1); 2724 SkScalar C = 1; 2725 SkScalar A = -B; 2726 return poly_eval(A, B, C, t); 2727} 2728 2729static int winding_mono_conic(const SkConic& conic, SkScalar x, SkScalar y, int* onCurveCount) { 2730 const SkPoint* pts = conic.fPts; 2731 SkScalar y0 = pts[0].fY; 2732 SkScalar y2 = pts[2].fY; 2733 2734 int dir = 1; 2735 if (y0 > y2) { 2736 using std::swap; 2737 swap(y0, y2); 2738 dir = -1; 2739 } 2740 if (y < y0 || y > y2) { 2741 return 0; 2742 } 2743 if (checkOnCurve(x, y, pts[0], pts[2])) { 2744 *onCurveCount += 1; 2745 return 0; 2746 } 2747 if (y == y2) { 2748 return 0; 2749 } 2750 2751 SkScalar roots[2]; 2752 SkScalar A = pts[2].fY; 2753 SkScalar B = pts[1].fY * conic.fW - y * conic.fW + y; 2754 SkScalar C = pts[0].fY; 2755 A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept) 2756 B -= C; // B = b*w - w * yCept + yCept - a 2757 C -= y; 2758 int n = SkFindUnitQuadRoots(A, 2 * B, C, roots); 2759 SkASSERT(n <= 1); 2760 SkScalar xt; 2761 if (0 == n) { 2762 // zero roots are returned only when y0 == y 2763 // Need [0] if dir == 1 2764 // and [2] if dir == -1 2765 xt = pts[1 - dir].fX; 2766 } else { 2767 SkScalar t = roots[0]; 2768 xt = conic_eval_numerator(&pts[0].fX, conic.fW, t) / conic_eval_denominator(conic.fW, t); 2769 } 2770 if (SkScalarNearlyEqual(xt, x)) { 2771 if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points 2772 *onCurveCount += 1; 2773 return 0; 2774 } 2775 } 2776 return xt < x ? dir : 0; 2777} 2778 2779static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) { 2780 // return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0; 2781 if (y0 == y1) { 2782 return true; 2783 } 2784 if (y0 < y1) { 2785 return y1 <= y2; 2786 } else { 2787 return y1 >= y2; 2788 } 2789} 2790 2791static int winding_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar weight, 2792 int* onCurveCount) { 2793 SkConic conic(pts, weight); 2794 SkConic chopped[2]; 2795 // If the data points are very large, the conic may not be monotonic but may also 2796 // fail to chop. Then, the chopper does not split the original conic in two. 2797 bool isMono = is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY) || !conic.chopAtYExtrema(chopped); 2798 int w = winding_mono_conic(isMono ? conic : chopped[0], x, y, onCurveCount); 2799 if (!isMono) { 2800 w += winding_mono_conic(chopped[1], x, y, onCurveCount); 2801 } 2802 return w; 2803} 2804 2805static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2806 SkScalar y0 = pts[0].fY; 2807 SkScalar y2 = pts[2].fY; 2808 2809 int dir = 1; 2810 if (y0 > y2) { 2811 using std::swap; 2812 swap(y0, y2); 2813 dir = -1; 2814 } 2815 if (y < y0 || y > y2) { 2816 return 0; 2817 } 2818 if (checkOnCurve(x, y, pts[0], pts[2])) { 2819 *onCurveCount += 1; 2820 return 0; 2821 } 2822 if (y == y2) { 2823 return 0; 2824 } 2825 // bounds check on X (not required. is it faster?) 2826#if 0 2827 if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) { 2828 return 0; 2829 } 2830#endif 2831 2832 SkScalar roots[2]; 2833 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, 2834 2 * (pts[1].fY - pts[0].fY), 2835 pts[0].fY - y, 2836 roots); 2837 SkASSERT(n <= 1); 2838 SkScalar xt; 2839 if (0 == n) { 2840 // zero roots are returned only when y0 == y 2841 // Need [0] if dir == 1 2842 // and [2] if dir == -1 2843 xt = pts[1 - dir].fX; 2844 } else { 2845 SkScalar t = roots[0]; 2846 SkScalar C = pts[0].fX; 2847 SkScalar A = pts[2].fX - 2 * pts[1].fX + C; 2848 SkScalar B = 2 * (pts[1].fX - C); 2849 xt = poly_eval(A, B, C, t); 2850 } 2851 if (SkScalarNearlyEqual(xt, x)) { 2852 if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points 2853 *onCurveCount += 1; 2854 return 0; 2855 } 2856 } 2857 return xt < x ? dir : 0; 2858} 2859 2860static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2861 SkPoint dst[5]; 2862 int n = 0; 2863 2864 if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) { 2865 n = SkChopQuadAtYExtrema(pts, dst); 2866 pts = dst; 2867 } 2868 int w = winding_mono_quad(pts, x, y, onCurveCount); 2869 if (n > 0) { 2870 w += winding_mono_quad(&pts[2], x, y, onCurveCount); 2871 } 2872 return w; 2873} 2874 2875static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2876 SkScalar x0 = pts[0].fX; 2877 SkScalar y0 = pts[0].fY; 2878 SkScalar x1 = pts[1].fX; 2879 SkScalar y1 = pts[1].fY; 2880 2881 SkScalar dy = y1 - y0; 2882 2883 int dir = 1; 2884 if (y0 > y1) { 2885 using std::swap; 2886 swap(y0, y1); 2887 dir = -1; 2888 } 2889 if (y < y0 || y > y1) { 2890 return 0; 2891 } 2892 if (checkOnCurve(x, y, pts[0], pts[1])) { 2893 *onCurveCount += 1; 2894 return 0; 2895 } 2896 if (y == y1) { 2897 return 0; 2898 } 2899 SkScalar cross = (x1 - x0) * (y - pts[0].fY) - dy * (x - x0); 2900 2901 if (!cross) { 2902 // zero cross means the point is on the line, and since the case where 2903 // y of the query point is at the end point is handled above, we can be 2904 // sure that we're on the line (excluding the end point) here 2905 if (x != x1 || y != pts[1].fY) { 2906 *onCurveCount += 1; 2907 } 2908 dir = 0; 2909 } else if (SkScalarSignAsInt(cross) == dir) { 2910 dir = 0; 2911 } 2912 return dir; 2913} 2914 2915static void tangent_cubic(const SkPoint pts[], SkScalar x, SkScalar y, 2916 SkTDArray<SkVector>* tangents) { 2917 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY) 2918 && !between(pts[2].fY, y, pts[3].fY)) { 2919 return; 2920 } 2921 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX) 2922 && !between(pts[2].fX, x, pts[3].fX)) { 2923 return; 2924 } 2925 SkPoint dst[10]; 2926 int n = SkChopCubicAtYExtrema(pts, dst); 2927 for (int i = 0; i <= n; ++i) { 2928 SkPoint* c = &dst[i * 3]; 2929 SkScalar t; 2930 if (!SkCubicClipper::ChopMonoAtY(c, y, &t)) { 2931 continue; 2932 } 2933 SkScalar xt = eval_cubic_pts(c[0].fX, c[1].fX, c[2].fX, c[3].fX, t); 2934 if (!SkScalarNearlyEqual(x, xt)) { 2935 continue; 2936 } 2937 SkVector tangent; 2938 SkEvalCubicAt(c, t, nullptr, &tangent, nullptr); 2939 tangents->push_back(tangent); 2940 } 2941} 2942 2943static void tangent_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar w, 2944 SkTDArray<SkVector>* tangents) { 2945 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) { 2946 return; 2947 } 2948 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) { 2949 return; 2950 } 2951 SkScalar roots[2]; 2952 SkScalar A = pts[2].fY; 2953 SkScalar B = pts[1].fY * w - y * w + y; 2954 SkScalar C = pts[0].fY; 2955 A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept) 2956 B -= C; // B = b*w - w * yCept + yCept - a 2957 C -= y; 2958 int n = SkFindUnitQuadRoots(A, 2 * B, C, roots); 2959 for (int index = 0; index < n; ++index) { 2960 SkScalar t = roots[index]; 2961 SkScalar xt = conic_eval_numerator(&pts[0].fX, w, t) / conic_eval_denominator(w, t); 2962 if (!SkScalarNearlyEqual(x, xt)) { 2963 continue; 2964 } 2965 SkConic conic(pts, w); 2966 tangents->push_back(conic.evalTangentAt(t)); 2967 } 2968} 2969 2970static void tangent_quad(const SkPoint pts[], SkScalar x, SkScalar y, 2971 SkTDArray<SkVector>* tangents) { 2972 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) { 2973 return; 2974 } 2975 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) { 2976 return; 2977 } 2978 SkScalar roots[2]; 2979 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, 2980 2 * (pts[1].fY - pts[0].fY), 2981 pts[0].fY - y, 2982 roots); 2983 for (int index = 0; index < n; ++index) { 2984 SkScalar t = roots[index]; 2985 SkScalar C = pts[0].fX; 2986 SkScalar A = pts[2].fX - 2 * pts[1].fX + C; 2987 SkScalar B = 2 * (pts[1].fX - C); 2988 SkScalar xt = poly_eval(A, B, C, t); 2989 if (!SkScalarNearlyEqual(x, xt)) { 2990 continue; 2991 } 2992 tangents->push_back(SkEvalQuadTangentAt(pts, t)); 2993 } 2994} 2995 2996static void tangent_line(const SkPoint pts[], SkScalar x, SkScalar y, 2997 SkTDArray<SkVector>* tangents) { 2998 SkScalar y0 = pts[0].fY; 2999 SkScalar y1 = pts[1].fY; 3000 if (!between(y0, y, y1)) { 3001 return; 3002 } 3003 SkScalar x0 = pts[0].fX; 3004 SkScalar x1 = pts[1].fX; 3005 if (!between(x0, x, x1)) { 3006 return; 3007 } 3008 SkScalar dx = x1 - x0; 3009 SkScalar dy = y1 - y0; 3010 if (!SkScalarNearlyEqual((x - x0) * dy, dx * (y - y0))) { 3011 return; 3012 } 3013 SkVector v; 3014 v.set(dx, dy); 3015 tangents->push_back(v); 3016} 3017 3018static bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) { 3019 return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom; 3020} 3021 3022bool SkPath::contains(SkScalar x, SkScalar y) const { 3023 bool isInverse = this->isInverseFillType(); 3024 if (this->isEmpty()) { 3025 return isInverse; 3026 } 3027 3028 if (!contains_inclusive(this->getBounds(), x, y)) { 3029 return isInverse; 3030 } 3031 3032 SkPath::Iter iter(*this, true); 3033 bool done = false; 3034 int w = 0; 3035 int onCurveCount = 0; 3036 do { 3037 SkPoint pts[4]; 3038 switch (iter.next(pts)) { 3039 case SkPath::kMove_Verb: 3040 case SkPath::kClose_Verb: 3041 break; 3042 case SkPath::kLine_Verb: 3043 w += winding_line(pts, x, y, &onCurveCount); 3044 break; 3045 case SkPath::kQuad_Verb: 3046 w += winding_quad(pts, x, y, &onCurveCount); 3047 break; 3048 case SkPath::kConic_Verb: 3049 w += winding_conic(pts, x, y, iter.conicWeight(), &onCurveCount); 3050 break; 3051 case SkPath::kCubic_Verb: 3052 w += winding_cubic(pts, x, y, &onCurveCount); 3053 break; 3054 case SkPath::kDone_Verb: 3055 done = true; 3056 break; 3057 } 3058 } while (!done); 3059 bool evenOddFill = SkPathFillType::kEvenOdd == this->getFillType() 3060 || SkPathFillType::kInverseEvenOdd == this->getFillType(); 3061 if (evenOddFill) { 3062 w &= 1; 3063 } 3064 if (w) { 3065 return !isInverse; 3066 } 3067 if (onCurveCount <= 1) { 3068 return SkToBool(onCurveCount) ^ isInverse; 3069 } 3070 if ((onCurveCount & 1) || evenOddFill) { 3071 return SkToBool(onCurveCount & 1) ^ isInverse; 3072 } 3073 // If the point touches an even number of curves, and the fill is winding, check for 3074 // coincidence. Count coincidence as places where the on curve points have identical tangents. 3075 iter.setPath(*this, true); 3076 done = false; 3077 SkTDArray<SkVector> tangents; 3078 do { 3079 SkPoint pts[4]; 3080 int oldCount = tangents.count(); 3081 switch (iter.next(pts)) { 3082 case SkPath::kMove_Verb: 3083 case SkPath::kClose_Verb: 3084 break; 3085 case SkPath::kLine_Verb: 3086 tangent_line(pts, x, y, &tangents); 3087 break; 3088 case SkPath::kQuad_Verb: 3089 tangent_quad(pts, x, y, &tangents); 3090 break; 3091 case SkPath::kConic_Verb: 3092 tangent_conic(pts, x, y, iter.conicWeight(), &tangents); 3093 break; 3094 case SkPath::kCubic_Verb: 3095 tangent_cubic(pts, x, y, &tangents); 3096 break; 3097 case SkPath::kDone_Verb: 3098 done = true; 3099 break; 3100 } 3101 if (tangents.count() > oldCount) { 3102 int last = tangents.count() - 1; 3103 const SkVector& tangent = tangents[last]; 3104 if (SkScalarNearlyZero(SkPointPriv::LengthSqd(tangent))) { 3105 tangents.remove(last); 3106 } else { 3107 for (int index = 0; index < last; ++index) { 3108 const SkVector& test = tangents[index]; 3109 if (SkScalarNearlyZero(test.cross(tangent)) 3110 && SkScalarSignAsInt(tangent.fX * test.fX) <= 0 3111 && SkScalarSignAsInt(tangent.fY * test.fY) <= 0) { 3112 tangents.remove(last); 3113 tangents.removeShuffle(index); 3114 break; 3115 } 3116 } 3117 } 3118 } 3119 } while (!done); 3120 return SkToBool(tangents.count()) ^ isInverse; 3121} 3122 3123int SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, 3124 SkScalar w, SkPoint pts[], int pow2) { 3125 const SkConic conic(p0, p1, p2, w); 3126 return conic.chopIntoQuadsPOW2(pts, pow2); 3127} 3128 3129bool SkPathPriv::IsSimpleRect(const SkPath& path, bool isSimpleFill, SkRect* rect, 3130 SkPathDirection* direction, unsigned* start) { 3131 if (path.getSegmentMasks() != SkPath::kLine_SegmentMask) { 3132 return false; 3133 } 3134 SkPoint rectPts[5]; 3135 int rectPtCnt = 0; 3136 bool needsClose = !isSimpleFill; 3137 for (auto [v, verbPts, w] : SkPathPriv::Iterate(path)) { 3138 switch (v) { 3139 case SkPathVerb::kMove: 3140 if (0 != rectPtCnt) { 3141 return false; 3142 } 3143 rectPts[0] = verbPts[0]; 3144 ++rectPtCnt; 3145 break; 3146 case SkPathVerb::kLine: 3147 if (5 == rectPtCnt) { 3148 return false; 3149 } 3150 rectPts[rectPtCnt] = verbPts[1]; 3151 ++rectPtCnt; 3152 break; 3153 case SkPathVerb::kClose: 3154 if (4 == rectPtCnt) { 3155 rectPts[4] = rectPts[0]; 3156 rectPtCnt = 5; 3157 } 3158 needsClose = false; 3159 break; 3160 case SkPathVerb::kQuad: 3161 case SkPathVerb::kConic: 3162 case SkPathVerb::kCubic: 3163 return false; 3164 } 3165 } 3166 if (needsClose) { 3167 return false; 3168 } 3169 if (rectPtCnt < 5) { 3170 return false; 3171 } 3172 if (rectPts[0] != rectPts[4]) { 3173 return false; 3174 } 3175 // Check for two cases of rectangles: pts 0 and 3 form a vertical edge or a horizontal edge ( 3176 // and pts 1 and 2 the opposite vertical or horizontal edge). 3177 bool vec03IsVertical; 3178 if (rectPts[0].fX == rectPts[3].fX && rectPts[1].fX == rectPts[2].fX && 3179 rectPts[0].fY == rectPts[1].fY && rectPts[3].fY == rectPts[2].fY) { 3180 // Make sure it has non-zero width and height 3181 if (rectPts[0].fX == rectPts[1].fX || rectPts[0].fY == rectPts[3].fY) { 3182 return false; 3183 } 3184 vec03IsVertical = true; 3185 } else if (rectPts[0].fY == rectPts[3].fY && rectPts[1].fY == rectPts[2].fY && 3186 rectPts[0].fX == rectPts[1].fX && rectPts[3].fX == rectPts[2].fX) { 3187 // Make sure it has non-zero width and height 3188 if (rectPts[0].fY == rectPts[1].fY || rectPts[0].fX == rectPts[3].fX) { 3189 return false; 3190 } 3191 vec03IsVertical = false; 3192 } else { 3193 return false; 3194 } 3195 // Set sortFlags so that it has the low bit set if pt index 0 is on right edge and second bit 3196 // set if it is on the bottom edge. 3197 unsigned sortFlags = 3198 ((rectPts[0].fX < rectPts[2].fX) ? 0b00 : 0b01) | 3199 ((rectPts[0].fY < rectPts[2].fY) ? 0b00 : 0b10); 3200 switch (sortFlags) { 3201 case 0b00: 3202 rect->setLTRB(rectPts[0].fX, rectPts[0].fY, rectPts[2].fX, rectPts[2].fY); 3203 *direction = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW; 3204 *start = 0; 3205 break; 3206 case 0b01: 3207 rect->setLTRB(rectPts[2].fX, rectPts[0].fY, rectPts[0].fX, rectPts[2].fY); 3208 *direction = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW; 3209 *start = 1; 3210 break; 3211 case 0b10: 3212 rect->setLTRB(rectPts[0].fX, rectPts[2].fY, rectPts[2].fX, rectPts[0].fY); 3213 *direction = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW; 3214 *start = 3; 3215 break; 3216 case 0b11: 3217 rect->setLTRB(rectPts[2].fX, rectPts[2].fY, rectPts[0].fX, rectPts[0].fY); 3218 *direction = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW; 3219 *start = 2; 3220 break; 3221 } 3222 return true; 3223} 3224 3225bool SkPathPriv::DrawArcIsConvex(SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) { 3226 if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) { 3227 // This gets converted to an oval. 3228 return true; 3229 } 3230 if (useCenter) { 3231 // This is a pie wedge. It's convex if the angle is <= 180. 3232 return SkScalarAbs(sweepAngle) <= 180.f; 3233 } 3234 // When the angle exceeds 360 this wraps back on top of itself. Otherwise it is a circle clipped 3235 // to a secant, i.e. convex. 3236 return SkScalarAbs(sweepAngle) <= 360.f; 3237} 3238 3239void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle, 3240 SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) { 3241 SkASSERT(!oval.isEmpty()); 3242 SkASSERT(sweepAngle); 3243#if defined(SK_BUILD_FOR_FUZZER) 3244 if (sweepAngle > 3600.0f || sweepAngle < -3600.0f) { 3245 return; 3246 } 3247#endif 3248 path->reset(); 3249 path->setIsVolatile(true); 3250 path->setFillType(SkPathFillType::kWinding); 3251 if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) { 3252 path->addOval(oval); 3253 SkASSERT(path->isConvex() && DrawArcIsConvex(sweepAngle, false, isFillNoPathEffect)); 3254 return; 3255 } 3256 if (useCenter) { 3257 path->moveTo(oval.centerX(), oval.centerY()); 3258 } 3259 auto firstDir = 3260 sweepAngle > 0 ? SkPathFirstDirection::kCW : SkPathFirstDirection::kCCW; 3261 bool convex = DrawArcIsConvex(sweepAngle, useCenter, isFillNoPathEffect); 3262 // Arc to mods at 360 and drawArc is not supposed to. 3263 bool forceMoveTo = !useCenter; 3264 while (sweepAngle <= -360.f) { 3265 path->arcTo(oval, startAngle, -180.f, forceMoveTo); 3266 startAngle -= 180.f; 3267 path->arcTo(oval, startAngle, -180.f, false); 3268 startAngle -= 180.f; 3269 forceMoveTo = false; 3270 sweepAngle += 360.f; 3271 } 3272 while (sweepAngle >= 360.f) { 3273 path->arcTo(oval, startAngle, 180.f, forceMoveTo); 3274 startAngle += 180.f; 3275 path->arcTo(oval, startAngle, 180.f, false); 3276 startAngle += 180.f; 3277 forceMoveTo = false; 3278 sweepAngle -= 360.f; 3279 } 3280 path->arcTo(oval, startAngle, sweepAngle, forceMoveTo); 3281 if (useCenter) { 3282 path->close(); 3283 } 3284 path->setConvexity(convex ? SkPathConvexity::kConvex : SkPathConvexity::kConcave); 3285 path->setFirstDirection(firstDir); 3286} 3287 3288/////////////////////////////////////////////////////////////////////////////////////////////////// 3289#include "include/private/SkNx.h" 3290 3291static int compute_quad_extremas(const SkPoint src[3], SkPoint extremas[3]) { 3292 SkScalar ts[2]; 3293 int n = SkFindQuadExtrema(src[0].fX, src[1].fX, src[2].fX, ts); 3294 n += SkFindQuadExtrema(src[0].fY, src[1].fY, src[2].fY, &ts[n]); 3295 SkASSERT(n >= 0 && n <= 2); 3296 for (int i = 0; i < n; ++i) { 3297 extremas[i] = SkEvalQuadAt(src, ts[i]); 3298 } 3299 extremas[n] = src[2]; 3300 return n + 1; 3301} 3302 3303static int compute_conic_extremas(const SkPoint src[3], SkScalar w, SkPoint extremas[3]) { 3304 SkConic conic(src[0], src[1], src[2], w); 3305 SkScalar ts[2]; 3306 int n = conic.findXExtrema(ts); 3307 n += conic.findYExtrema(&ts[n]); 3308 SkASSERT(n >= 0 && n <= 2); 3309 for (int i = 0; i < n; ++i) { 3310 extremas[i] = conic.evalAt(ts[i]); 3311 } 3312 extremas[n] = src[2]; 3313 return n + 1; 3314} 3315 3316static int compute_cubic_extremas(const SkPoint src[4], SkPoint extremas[5]) { 3317 SkScalar ts[4]; 3318 int n = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX, src[3].fX, ts); 3319 n += SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY, src[3].fY, &ts[n]); 3320 SkASSERT(n >= 0 && n <= 4); 3321 for (int i = 0; i < n; ++i) { 3322 SkEvalCubicAt(src, ts[i], &extremas[i], nullptr, nullptr); 3323 } 3324 extremas[n] = src[3]; 3325 return n + 1; 3326} 3327 3328SkRect SkPath::computeTightBounds() const { 3329 if (0 == this->countVerbs()) { 3330 return SkRect::MakeEmpty(); 3331 } 3332 3333 if (this->getSegmentMasks() == SkPath::kLine_SegmentMask) { 3334 return this->getBounds(); 3335 } 3336 3337 SkPoint extremas[5]; // big enough to hold worst-case curve type (cubic) extremas + 1 3338 3339 // initial with the first MoveTo, so we don't have to check inside the switch 3340 Sk2s min, max; 3341 min = max = from_point(this->getPoint(0)); 3342 for (auto [verb, pts, w] : SkPathPriv::Iterate(*this)) { 3343 int count = 0; 3344 switch (verb) { 3345 case SkPathVerb::kMove: 3346 extremas[0] = pts[0]; 3347 count = 1; 3348 break; 3349 case SkPathVerb::kLine: 3350 extremas[0] = pts[1]; 3351 count = 1; 3352 break; 3353 case SkPathVerb::kQuad: 3354 count = compute_quad_extremas(pts, extremas); 3355 break; 3356 case SkPathVerb::kConic: 3357 count = compute_conic_extremas(pts, *w, extremas); 3358 break; 3359 case SkPathVerb::kCubic: 3360 count = compute_cubic_extremas(pts, extremas); 3361 break; 3362 case SkPathVerb::kClose: 3363 break; 3364 } 3365 for (int i = 0; i < count; ++i) { 3366 Sk2s tmp = from_point(extremas[i]); 3367 min = Sk2s::Min(min, tmp); 3368 max = Sk2s::Max(max, tmp); 3369 } 3370 } 3371 SkRect bounds; 3372 min.store((SkPoint*)&bounds.fLeft); 3373 max.store((SkPoint*)&bounds.fRight); 3374 return bounds; 3375} 3376 3377bool SkPath::IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact) { 3378 return exact ? p1 == p2 : SkPointPriv::EqualsWithinTolerance(p1, p2); 3379} 3380 3381bool SkPath::IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2, 3382 const SkPoint& p3, bool exact) { 3383 return exact ? p1 == p2 && p2 == p3 : SkPointPriv::EqualsWithinTolerance(p1, p2) && 3384 SkPointPriv::EqualsWithinTolerance(p2, p3); 3385} 3386 3387bool SkPath::IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2, 3388 const SkPoint& p3, const SkPoint& p4, bool exact) { 3389 return exact ? p1 == p2 && p2 == p3 && p3 == p4 : 3390 SkPointPriv::EqualsWithinTolerance(p1, p2) && 3391 SkPointPriv::EqualsWithinTolerance(p2, p3) && 3392 SkPointPriv::EqualsWithinTolerance(p3, p4); 3393} 3394 3395////////////////////////////////////////////////////////////////////////////////////////////////// 3396 3397SkPathVerbAnalysis sk_path_analyze_verbs(const uint8_t vbs[], int verbCount) { 3398 SkPathVerbAnalysis info = {false, 0, 0, 0}; 3399 3400 bool needMove = true; 3401 bool invalid = false; 3402 for (int i = 0; i < verbCount; ++i) { 3403 switch ((SkPathVerb)vbs[i]) { 3404 case SkPathVerb::kMove: 3405 needMove = false; 3406 info.points += 1; 3407 break; 3408 case SkPathVerb::kLine: 3409 invalid |= needMove; 3410 info.segmentMask |= kLine_SkPathSegmentMask; 3411 info.points += 1; 3412 break; 3413 case SkPathVerb::kQuad: 3414 invalid |= needMove; 3415 info.segmentMask |= kQuad_SkPathSegmentMask; 3416 info.points += 2; 3417 break; 3418 case SkPathVerb::kConic: 3419 invalid |= needMove; 3420 info.segmentMask |= kConic_SkPathSegmentMask; 3421 info.points += 2; 3422 info.weights += 1; 3423 break; 3424 case SkPathVerb::kCubic: 3425 invalid |= needMove; 3426 info.segmentMask |= kCubic_SkPathSegmentMask; 3427 info.points += 3; 3428 break; 3429 case SkPathVerb::kClose: 3430 invalid |= needMove; 3431 needMove = true; 3432 break; 3433 default: 3434 invalid = true; 3435 break; 3436 } 3437 } 3438 info.valid = !invalid; 3439 return info; 3440} 3441 3442SkPath SkPath::Make(const SkPoint pts[], int pointCount, 3443 const uint8_t vbs[], int verbCount, 3444 const SkScalar ws[], int wCount, 3445 SkPathFillType ft, bool isVolatile) { 3446 if (verbCount <= 0) { 3447 return SkPath(); 3448 } 3449 3450 const auto info = sk_path_analyze_verbs(vbs, verbCount); 3451 if (!info.valid || info.points > pointCount || info.weights > wCount) { 3452 SkDEBUGFAIL("invalid verbs and number of points/weights"); 3453 return SkPath(); 3454 } 3455 3456 return SkPath(sk_sp<SkPathRef>(new SkPathRef(SkTDArray<SkPoint>(pts, info.points), 3457 SkTDArray<uint8_t>(vbs, verbCount), 3458 SkTDArray<SkScalar>(ws, info.weights), 3459 info.segmentMask)), 3460 ft, isVolatile, SkPathConvexity::kUnknown, SkPathFirstDirection::kUnknown); 3461} 3462 3463SkPath SkPath::Rect(const SkRect& r, SkPathDirection dir, unsigned startIndex) { 3464 return SkPathBuilder().addRect(r, dir, startIndex).detach(); 3465} 3466 3467SkPath SkPath::Oval(const SkRect& r, SkPathDirection dir) { 3468 return SkPathBuilder().addOval(r, dir).detach(); 3469} 3470 3471SkPath SkPath::Oval(const SkRect& r, SkPathDirection dir, unsigned startIndex) { 3472 return SkPathBuilder().addOval(r, dir, startIndex).detach(); 3473} 3474 3475SkPath SkPath::Circle(SkScalar x, SkScalar y, SkScalar r, SkPathDirection dir) { 3476 return SkPathBuilder().addCircle(x, y, r, dir).detach(); 3477} 3478 3479SkPath SkPath::RRect(const SkRRect& rr, SkPathDirection dir) { 3480 return SkPathBuilder().addRRect(rr, dir).detach(); 3481} 3482 3483SkPath SkPath::RRect(const SkRRect& rr, SkPathDirection dir, unsigned startIndex) { 3484 return SkPathBuilder().addRRect(rr, dir, startIndex).detach(); 3485} 3486 3487SkPath SkPath::RRect(const SkRect& r, SkScalar rx, SkScalar ry, SkPathDirection dir) { 3488 return SkPathBuilder().addRRect(SkRRect::MakeRectXY(r, rx, ry), dir).detach(); 3489} 3490 3491SkPath SkPath::Polygon(const SkPoint pts[], int count, bool isClosed, 3492 SkPathFillType ft, bool isVolatile) { 3493 return SkPathBuilder().addPolygon(pts, count, isClosed) 3494 .setFillType(ft) 3495 .setIsVolatile(isVolatile) 3496 .detach(); 3497} 3498 3499////////////////////////////////////////////////////////////////////////////////////////////////// 3500 3501bool SkPathPriv::IsRectContour(const SkPath& path, bool allowPartial, int* currVerb, 3502 const SkPoint** ptsPtr, bool* isClosed, SkPathDirection* direction, 3503 SkRect* rect) { 3504 int corners = 0; 3505 SkPoint closeXY; // used to determine if final line falls on a diagonal 3506 SkPoint lineStart; // used to construct line from previous point 3507 const SkPoint* firstPt = nullptr; // first point in the rect (last of first moves) 3508 const SkPoint* lastPt = nullptr; // last point in the rect (last of lines or first if closed) 3509 SkPoint firstCorner; 3510 SkPoint thirdCorner; 3511 const SkPoint* pts = *ptsPtr; 3512 const SkPoint* savePts = nullptr; // used to allow caller to iterate through a pair of rects 3513 lineStart.set(0, 0); 3514 signed char directions[] = {-1, -1, -1, -1, -1}; // -1 to 3; -1 is uninitialized 3515 bool closedOrMoved = false; 3516 bool autoClose = false; 3517 bool insertClose = false; 3518 int verbCnt = path.fPathRef->countVerbs(); 3519 while (*currVerb < verbCnt && (!allowPartial || !autoClose)) { 3520 uint8_t verb = insertClose ? (uint8_t) SkPath::kClose_Verb : path.fPathRef->atVerb(*currVerb); 3521 switch (verb) { 3522 case SkPath::kClose_Verb: 3523 savePts = pts; 3524 autoClose = true; 3525 insertClose = false; 3526 [[fallthrough]]; 3527 case SkPath::kLine_Verb: { 3528 if (SkPath::kClose_Verb != verb) { 3529 lastPt = pts; 3530 } 3531 SkPoint lineEnd = SkPath::kClose_Verb == verb ? *firstPt : *pts++; 3532 SkVector lineDelta = lineEnd - lineStart; 3533 if (lineDelta.fX && lineDelta.fY) { 3534 return false; // diagonal 3535 } 3536 if (!lineDelta.isFinite()) { 3537 return false; // path contains infinity or NaN 3538 } 3539 if (lineStart == lineEnd) { 3540 break; // single point on side OK 3541 } 3542 int nextDirection = rect_make_dir(lineDelta.fX, lineDelta.fY); // 0 to 3 3543 if (0 == corners) { 3544 directions[0] = nextDirection; 3545 corners = 1; 3546 closedOrMoved = false; 3547 lineStart = lineEnd; 3548 break; 3549 } 3550 if (closedOrMoved) { 3551 return false; // closed followed by a line 3552 } 3553 if (autoClose && nextDirection == directions[0]) { 3554 break; // colinear with first 3555 } 3556 closedOrMoved = autoClose; 3557 if (directions[corners - 1] == nextDirection) { 3558 if (3 == corners && SkPath::kLine_Verb == verb) { 3559 thirdCorner = lineEnd; 3560 } 3561 lineStart = lineEnd; 3562 break; // colinear segment 3563 } 3564 directions[corners++] = nextDirection; 3565 // opposite lines must point in opposite directions; xoring them should equal 2 3566 switch (corners) { 3567 case 2: 3568 firstCorner = lineStart; 3569 break; 3570 case 3: 3571 if ((directions[0] ^ directions[2]) != 2) { 3572 return false; 3573 } 3574 thirdCorner = lineEnd; 3575 break; 3576 case 4: 3577 if ((directions[1] ^ directions[3]) != 2) { 3578 return false; 3579 } 3580 break; 3581 default: 3582 return false; // too many direction changes 3583 } 3584 lineStart = lineEnd; 3585 break; 3586 } 3587 case SkPath::kQuad_Verb: 3588 case SkPath::kConic_Verb: 3589 case SkPath::kCubic_Verb: 3590 return false; // quadratic, cubic not allowed 3591 case SkPath::kMove_Verb: 3592 if (allowPartial && !autoClose && directions[0] >= 0) { 3593 insertClose = true; 3594 *currVerb -= 1; // try move again afterwards 3595 goto addMissingClose; 3596 } 3597 if (!corners) { 3598 firstPt = pts; 3599 } else { 3600 closeXY = *firstPt - *lastPt; 3601 if (closeXY.fX && closeXY.fY) { 3602 return false; // we're diagonal, abort 3603 } 3604 } 3605 lineStart = *pts++; 3606 closedOrMoved = true; 3607 break; 3608 default: 3609 SkDEBUGFAIL("unexpected verb"); 3610 break; 3611 } 3612 *currVerb += 1; 3613 addMissingClose: 3614 ; 3615 } 3616 // Success if 4 corners and first point equals last 3617 if (corners < 3 || corners > 4) { 3618 return false; 3619 } 3620 if (savePts) { 3621 *ptsPtr = savePts; 3622 } 3623 // check if close generates diagonal 3624 closeXY = *firstPt - *lastPt; 3625 if (closeXY.fX && closeXY.fY) { 3626 return false; 3627 } 3628 if (rect) { 3629 rect->set(firstCorner, thirdCorner); 3630 } 3631 if (isClosed) { 3632 *isClosed = autoClose; 3633 } 3634 if (direction) { 3635 *direction = directions[0] == ((directions[1] + 1) & 3) ? 3636 SkPathDirection::kCW : SkPathDirection::kCCW; 3637 } 3638 return true; 3639} 3640 3641 3642bool SkPathPriv::IsNestedFillRects(const SkPath& path, SkRect rects[2], SkPathDirection dirs[2]) { 3643 SkDEBUGCODE(path.validate();) 3644 int currVerb = 0; 3645 const SkPoint* pts = path.fPathRef->points(); 3646 SkPathDirection testDirs[2]; 3647 SkRect testRects[2]; 3648 if (!IsRectContour(path, true, &currVerb, &pts, nullptr, &testDirs[0], &testRects[0])) { 3649 return false; 3650 } 3651 if (IsRectContour(path, false, &currVerb, &pts, nullptr, &testDirs[1], &testRects[1])) { 3652 if (testRects[0].contains(testRects[1])) { 3653 if (rects) { 3654 rects[0] = testRects[0]; 3655 rects[1] = testRects[1]; 3656 } 3657 if (dirs) { 3658 dirs[0] = testDirs[0]; 3659 dirs[1] = testDirs[1]; 3660 } 3661 return true; 3662 } 3663 if (testRects[1].contains(testRects[0])) { 3664 if (rects) { 3665 rects[0] = testRects[1]; 3666 rects[1] = testRects[0]; 3667 } 3668 if (dirs) { 3669 dirs[0] = testDirs[1]; 3670 dirs[1] = testDirs[0]; 3671 } 3672 return true; 3673 } 3674 } 3675 return false; 3676} 3677 3678/////////////////////////////////////////////////////////////////////////////////////////////////// 3679 3680#include "src/core/SkEdgeClipper.h" 3681 3682struct SkHalfPlane { 3683 SkScalar fA, fB, fC; 3684 3685 SkScalar eval(SkScalar x, SkScalar y) const { 3686 return fA * x + fB * y + fC; 3687 } 3688 SkScalar operator()(SkScalar x, SkScalar y) const { return this->eval(x, y); } 3689 3690 bool normalize() { 3691 double a = fA; 3692 double b = fB; 3693 double c = fC; 3694 double dmag = sqrt(a * a + b * b); 3695 // length of initial plane normal is zero 3696 if (dmag == 0) { 3697 fA = fB = 0; 3698 fC = SK_Scalar1; 3699 return true; 3700 } 3701 double dscale = sk_ieee_double_divide(1.0, dmag); 3702 a *= dscale; 3703 b *= dscale; 3704 c *= dscale; 3705 // check if we're not finite, or normal is zero-length 3706 if (!sk_float_isfinite(a) || !sk_float_isfinite(b) || !sk_float_isfinite(c) || 3707 (a == 0 && b == 0)) { 3708 fA = fB = 0; 3709 fC = SK_Scalar1; 3710 return false; 3711 } 3712 fA = a; 3713 fB = b; 3714 fC = c; 3715 return true; 3716 } 3717 3718 enum Result { 3719 kAllNegative, 3720 kAllPositive, 3721 kMixed 3722 }; 3723 Result test(const SkRect& bounds) const { 3724 // check whether the diagonal aligned with the normal crosses the plane 3725 SkPoint diagMin, diagMax; 3726 if (fA >= 0) { 3727 diagMin.fX = bounds.fLeft; 3728 diagMax.fX = bounds.fRight; 3729 } else { 3730 diagMin.fX = bounds.fRight; 3731 diagMax.fX = bounds.fLeft; 3732 } 3733 if (fB >= 0) { 3734 diagMin.fY = bounds.fTop; 3735 diagMax.fY = bounds.fBottom; 3736 } else { 3737 diagMin.fY = bounds.fBottom; 3738 diagMax.fY = bounds.fTop; 3739 } 3740 SkScalar test = this->eval(diagMin.fX, diagMin.fY); 3741 SkScalar sign = test*this->eval(diagMax.fX, diagMax.fY); 3742 if (sign > 0) { 3743 // the path is either all on one side of the half-plane or the other 3744 if (test < 0) { 3745 return kAllNegative; 3746 } else { 3747 return kAllPositive; 3748 } 3749 } 3750 return kMixed; 3751 } 3752}; 3753 3754// assumes plane is pre-normalized 3755// If we fail in our calculations, we return the empty path 3756static SkPath clip(const SkPath& path, const SkHalfPlane& plane) { 3757 SkMatrix mx, inv; 3758 SkPoint p0 = { -plane.fA*plane.fC, -plane.fB*plane.fC }; 3759 mx.setAll( plane.fB, plane.fA, p0.fX, 3760 -plane.fA, plane.fB, p0.fY, 3761 0, 0, 1); 3762 if (!mx.invert(&inv)) { 3763 return SkPath(); 3764 } 3765 3766 SkPath rotated; 3767 path.transform(inv, &rotated); 3768 if (!rotated.isFinite()) { 3769 return SkPath(); 3770 } 3771 3772 SkScalar big = SK_ScalarMax; 3773 SkRect clip = {-big, 0, big, big }; 3774 3775 struct Rec { 3776 SkPathBuilder fResult; 3777 SkPoint fPrev = {0,0}; 3778 } rec; 3779 3780 SkEdgeClipper::ClipPath(rotated, clip, false, 3781 [](SkEdgeClipper* clipper, bool newCtr, void* ctx) { 3782 Rec* rec = (Rec*)ctx; 3783 3784 bool addLineTo = false; 3785 SkPoint pts[4]; 3786 SkPath::Verb verb; 3787 while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) { 3788 if (newCtr) { 3789 rec->fResult.moveTo(pts[0]); 3790 rec->fPrev = pts[0]; 3791 newCtr = false; 3792 } 3793 3794 if (addLineTo || pts[0] != rec->fPrev) { 3795 rec->fResult.lineTo(pts[0]); 3796 } 3797 3798 switch (verb) { 3799 case SkPath::kLine_Verb: 3800 rec->fResult.lineTo(pts[1]); 3801 rec->fPrev = pts[1]; 3802 break; 3803 case SkPath::kQuad_Verb: 3804 rec->fResult.quadTo(pts[1], pts[2]); 3805 rec->fPrev = pts[2]; 3806 break; 3807 case SkPath::kCubic_Verb: 3808 rec->fResult.cubicTo(pts[1], pts[2], pts[3]); 3809 rec->fPrev = pts[3]; 3810 break; 3811 default: break; 3812 } 3813 addLineTo = true; 3814 } 3815 }, &rec); 3816 3817 rec.fResult.setFillType(path.getFillType()); 3818 SkPath result = rec.fResult.detach().makeTransform(mx); 3819 if (!result.isFinite()) { 3820 result = SkPath(); 3821 } 3822 return result; 3823} 3824 3825// true means we have written to clippedPath 3826bool SkPathPriv::PerspectiveClip(const SkPath& path, const SkMatrix& matrix, SkPath* clippedPath) { 3827 if (!matrix.hasPerspective()) { 3828 return false; 3829 } 3830 3831 SkHalfPlane plane { 3832 matrix[SkMatrix::kMPersp0], 3833 matrix[SkMatrix::kMPersp1], 3834 matrix[SkMatrix::kMPersp2] - kW0PlaneDistance 3835 }; 3836 if (plane.normalize()) { 3837 switch (plane.test(path.getBounds())) { 3838 case SkHalfPlane::kAllPositive: 3839 return false; 3840 case SkHalfPlane::kMixed: { 3841 *clippedPath = clip(path, plane); 3842 return true; 3843 } break; 3844 default: break; // handled outside of the switch 3845 } 3846 } 3847 // clipped out (or failed) 3848 *clippedPath = SkPath(); 3849 return true; 3850} 3851 3852int SkPathPriv::GenIDChangeListenersCount(const SkPath& path) { 3853 return path.fPathRef->genIDChangeListenerCount(); 3854} 3855 3856bool SkPathPriv::IsAxisAligned(const SkPath& path) { 3857 // Conservative (quick) test to see if all segments are axis-aligned. 3858 // Multiple contours might give a false-negative, but for speed, we ignore that 3859 // and just look at the raw points. 3860 3861 const SkPoint* pts = path.fPathRef->points(); 3862 const int count = path.fPathRef->countPoints(); 3863 3864 for (int i = 1; i < count; ++i) { 3865 if (pts[i-1].fX != pts[i].fX && pts[i-1].fY != pts[i].fY) { 3866 return false; 3867 } 3868 } 3869 return true; 3870} 3871