1 /* 2 * Copyright 2015 Google Inc. 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 #ifndef SkPathPriv_DEFINED 9 #define SkPathPriv_DEFINED 10 11 #include "include/core/SkArc.h" 12 #include "include/core/SkPath.h" 13 #include "include/core/SkPathBuilder.h" 14 #include "include/core/SkPathTypes.h" 15 #include "include/core/SkPoint.h" 16 #include "include/core/SkRect.h" 17 #include "include/core/SkRefCnt.h" 18 #include "include/core/SkScalar.h" 19 #include "include/core/SkTypes.h" 20 #include "include/private/SkIDChangeListener.h" 21 #include "include/private/SkPathRef.h" 22 #include "include/private/base/SkDebug.h" 23 #include "src/core/SkPathEnums.h" 24 25 #include <cstdint> 26 #include <iterator> 27 #include <utility> 28 29 class SkMatrix; 30 class SkRRect; 31 32 static_assert(0 == static_cast<int>(SkPathFillType::kWinding), "fill_type_mismatch"); 33 static_assert(1 == static_cast<int>(SkPathFillType::kEvenOdd), "fill_type_mismatch"); 34 static_assert(2 == static_cast<int>(SkPathFillType::kInverseWinding), "fill_type_mismatch"); 35 static_assert(3 == static_cast<int>(SkPathFillType::kInverseEvenOdd), "fill_type_mismatch"); 36 37 // These are computed from a stream of verbs 38 struct SkPathVerbAnalysis { 39 int points, weights; 40 unsigned segmentMask; 41 bool valid; 42 }; 43 44 class SkPathPriv { 45 public: 46 static SkPathVerbAnalysis AnalyzeVerbs(const uint8_t verbs[], int count); 47 48 // skbug.com/9906: Not a perfect solution for W plane clipping, but 1/16384 is a 49 // reasonable limit (roughly 5e-5) 50 inline static constexpr SkScalar kW0PlaneDistance = 1.f / (1 << 14); 51 AsFirstDirection(SkPathDirection dir)52 static SkPathFirstDirection AsFirstDirection(SkPathDirection dir) { 53 // since we agree numerically for the values in Direction, we can just cast. 54 return (SkPathFirstDirection)dir; 55 } 56 57 /** 58 * Return the opposite of the specified direction. kUnknown is its own 59 * opposite. 60 */ OppositeFirstDirection(SkPathFirstDirection dir)61 static SkPathFirstDirection OppositeFirstDirection(SkPathFirstDirection dir) { 62 static const SkPathFirstDirection gOppositeDir[] = { 63 SkPathFirstDirection::kCCW, SkPathFirstDirection::kCW, SkPathFirstDirection::kUnknown, 64 }; 65 return gOppositeDir[(unsigned)dir]; 66 } 67 68 /** 69 * Tries to compute the direction of the outer-most non-degenerate 70 * contour. If it can be computed, return that direction. If it cannot be determined, 71 * or the contour is known to be convex, return kUnknown. If the direction was determined, 72 * it is cached to make subsequent calls return quickly. 73 */ 74 static SkPathFirstDirection ComputeFirstDirection(const SkPath&); 75 IsClosedSingleContour(const SkPath & path)76 static bool IsClosedSingleContour(const SkPath& path) { 77 int verbCount = path.countVerbs(); 78 if (verbCount == 0) 79 return false; 80 int moveCount = 0; 81 auto verbs = path.fPathRef->verbsBegin(); 82 for (int i = 0; i < verbCount; i++) { 83 switch (verbs[i]) { 84 case SkPath::Verb::kMove_Verb: 85 moveCount += 1; 86 if (moveCount > 1) { 87 return false; 88 } 89 break; 90 case SkPath::Verb::kClose_Verb: 91 if (i == verbCount - 1) { 92 return true; 93 } 94 return false; 95 default: break; 96 } 97 } 98 return false; 99 } 100 101 // In some scenarios (e.g. fill or convexity checking all but the last leading move to are 102 // irrelevant to behavior). SkPath::injectMoveToIfNeeded should ensure that this is always at 103 // least 1. LeadingMoveToCount(const SkPath & path)104 static int LeadingMoveToCount(const SkPath& path) { 105 int verbCount = path.countVerbs(); 106 auto verbs = path.fPathRef->verbsBegin(); 107 for (int i = 0; i < verbCount; i++) { 108 if (verbs[i] != SkPath::Verb::kMove_Verb) { 109 return i; 110 } 111 } 112 return verbCount; // path is all move verbs 113 } 114 AddGenIDChangeListener(const SkPath & path,sk_sp<SkIDChangeListener> listener)115 static void AddGenIDChangeListener(const SkPath& path, sk_sp<SkIDChangeListener> listener) { 116 path.fPathRef->addGenIDChangeListener(std::move(listener)); 117 } 118 119 /** 120 * This returns true for a rect that has a move followed by 3 or 4 lines and a close. If 121 * 'isSimpleFill' is true, an uncloseed rect will also be accepted as long as it starts and 122 * ends at the same corner. This does not permit degenerate line or point rectangles. 123 */ 124 static bool IsSimpleRect(const SkPath& path, bool isSimpleFill, SkRect* rect, 125 SkPathDirection* direction, unsigned* start); 126 127 /** 128 * Creates a path from arc params using the semantics of SkCanvas::drawArc. This function 129 * assumes empty ovals and zero sweeps have already been filtered out. 130 */ 131 static void CreateDrawArcPath(SkPath* path, const SkArc& arc, bool isFillNoPathEffect); 132 133 /** 134 * Determines whether an arc produced by CreateDrawArcPath will be convex. Assumes a non-empty 135 * oval. 136 */ 137 static bool DrawArcIsConvex(SkScalar sweepAngle, SkArc::Type arcType, bool isFillNoPathEffect); 138 ShrinkToFit(SkPath * path)139 static void ShrinkToFit(SkPath* path) { 140 path->shrinkToFit(); 141 } 142 143 /** 144 * Returns a C++11-iterable object that traverses a path's verbs in order. e.g: 145 * 146 * for (SkPath::Verb verb : SkPathPriv::Verbs(path)) { 147 * ... 148 * } 149 */ 150 struct Verbs { 151 public: VerbsVerbs152 Verbs(const SkPath& path) : fPathRef(path.fPathRef.get()) {} 153 struct Iter { 154 void operator++() { fVerb++; } 155 bool operator!=(const Iter& b) { return fVerb != b.fVerb; } 156 SkPath::Verb operator*() { return static_cast<SkPath::Verb>(*fVerb); } 157 const uint8_t* fVerb; 158 }; beginVerbs159 Iter begin() { return Iter{fPathRef->verbsBegin()}; } endVerbs160 Iter end() { return Iter{fPathRef->verbsEnd()}; } 161 private: 162 Verbs(const Verbs&) = delete; 163 Verbs& operator=(const Verbs&) = delete; 164 SkPathRef* fPathRef; 165 }; 166 167 /** 168 * Iterates through a raw range of path verbs, points, and conics. All values are returned 169 * unaltered. 170 * 171 * NOTE: This class's definition will be moved into SkPathPriv once RangeIter is removed. 172 */ 173 using RangeIter = SkPath::RangeIter; 174 175 /** 176 * Iterable object for traversing verbs, points, and conic weights in a path: 177 * 178 * for (auto [verb, pts, weights] : SkPathPriv::Iterate(skPath)) { 179 * ... 180 * } 181 */ 182 struct Iterate { 183 public: IterateIterate184 Iterate(const SkPath& path) 185 : Iterate(path.fPathRef->verbsBegin(), 186 // Don't allow iteration through non-finite points. 187 (!path.isFinite()) ? path.fPathRef->verbsBegin() 188 : path.fPathRef->verbsEnd(), 189 path.fPathRef->points(), path.fPathRef->conicWeights()) { 190 } IterateIterate191 Iterate(const uint8_t* verbsBegin, const uint8_t* verbsEnd, const SkPoint* points, 192 const SkScalar* weights) 193 : fVerbsBegin(verbsBegin), fVerbsEnd(verbsEnd), fPoints(points), fWeights(weights) { 194 } beginIterate195 SkPath::RangeIter begin() { return {fVerbsBegin, fPoints, fWeights}; } endIterate196 SkPath::RangeIter end() { return {fVerbsEnd, nullptr, nullptr}; } 197 private: 198 const uint8_t* fVerbsBegin; 199 const uint8_t* fVerbsEnd; 200 const SkPoint* fPoints; 201 const SkScalar* fWeights; 202 }; 203 204 /** 205 * Returns a pointer to the verb data. 206 */ VerbData(const SkPath & path)207 static const uint8_t* VerbData(const SkPath& path) { 208 return path.fPathRef->verbsBegin(); 209 } 210 211 /** Returns a raw pointer to the path points */ PointData(const SkPath & path)212 static const SkPoint* PointData(const SkPath& path) { 213 return path.fPathRef->points(); 214 } 215 216 /** Returns the number of conic weights in the path */ ConicWeightCnt(const SkPath & path)217 static int ConicWeightCnt(const SkPath& path) { 218 return path.fPathRef->countWeights(); 219 } 220 221 /** Returns a raw pointer to the path conic weights. */ ConicWeightData(const SkPath & path)222 static const SkScalar* ConicWeightData(const SkPath& path) { 223 return path.fPathRef->conicWeights(); 224 } 225 226 /** Returns true if the underlying SkPathRef has one single owner. */ TestingOnly_unique(const SkPath & path)227 static bool TestingOnly_unique(const SkPath& path) { 228 return path.fPathRef->unique(); 229 } 230 231 // Won't be needed once we can make path's immutable (with their bounds always computed) HasComputedBounds(const SkPath & path)232 static bool HasComputedBounds(const SkPath& path) { 233 return path.hasComputedBounds(); 234 } 235 236 /** Returns true if constructed by addCircle(), addOval(); and in some cases, 237 addRoundRect(), addRRect(). SkPath constructed with conicTo() or rConicTo() will not 238 return true though SkPath draws oval. 239 240 rect receives bounds of oval. 241 dir receives SkPathDirection of oval: kCW_Direction if clockwise, kCCW_Direction if 242 counterclockwise. 243 start receives start of oval: 0 for top, 1 for right, 2 for bottom, 3 for left. 244 245 rect, dir, and start are unmodified if oval is not found. 246 247 Triggers performance optimizations on some GPU surface implementations. 248 249 @param rect storage for bounding SkRect of oval; may be nullptr 250 @param dir storage for SkPathDirection; may be nullptr 251 @param start storage for start of oval; may be nullptr 252 @return true if SkPath was constructed by method that reduces to oval 253 */ IsOval(const SkPath & path,SkRect * rect,SkPathDirection * dir,unsigned * start)254 static bool IsOval(const SkPath& path, SkRect* rect, SkPathDirection* dir, unsigned* start) { 255 bool isCCW = false; 256 bool result = path.fPathRef->isOval(rect, &isCCW, start); 257 if (dir && result) { 258 *dir = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW; 259 } 260 return result; 261 } 262 263 /** Returns true if constructed by addRoundRect(), addRRect(); and if construction 264 is not empty, not SkRect, and not oval. SkPath constructed with other calls 265 will not return true though SkPath draws SkRRect. 266 267 rrect receives bounds of SkRRect. 268 dir receives SkPathDirection of oval: kCW_Direction if clockwise, kCCW_Direction if 269 counterclockwise. 270 start receives start of SkRRect: 0 for top, 1 for right, 2 for bottom, 3 for left. 271 272 rrect, dir, and start are unmodified if SkRRect is not found. 273 274 Triggers performance optimizations on some GPU surface implementations. 275 276 @param rrect storage for bounding SkRect of SkRRect; may be nullptr 277 @param dir storage for SkPathDirection; may be nullptr 278 @param start storage for start of SkRRect; may be nullptr 279 @return true if SkPath contains only SkRRect 280 */ IsRRect(const SkPath & path,SkRRect * rrect,SkPathDirection * dir,unsigned * start)281 static bool IsRRect(const SkPath& path, SkRRect* rrect, SkPathDirection* dir, 282 unsigned* start) { 283 bool isCCW = false; 284 bool result = path.fPathRef->isRRect(rrect, &isCCW, start); 285 if (dir && result) { 286 *dir = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW; 287 } 288 return result; 289 } 290 291 /** 292 * Sometimes in the drawing pipeline, we have to perform math on path coordinates, even after 293 * the path is in device-coordinates. Tessellation and clipping are two examples. Usually this 294 * is pretty modest, but it can involve subtracting/adding coordinates, or multiplying by 295 * small constants (e.g. 2,3,4). To try to preflight issues where these optionations could turn 296 * finite path values into infinities (or NaNs), we allow the upper drawing code to reject 297 * the path if its bounds (in device coordinates) is too close to max float. 298 */ TooBigForMath(const SkRect & bounds)299 static bool TooBigForMath(const SkRect& bounds) { 300 // This value is just a guess. smaller is safer, but we don't want to reject largish paths 301 // that we don't have to. 302 constexpr SkScalar scale_down_to_allow_for_small_multiplies = 0.25f; 303 constexpr SkScalar max = SK_ScalarMax * scale_down_to_allow_for_small_multiplies; 304 305 // use ! expression so we return true if bounds contains NaN 306 return !(bounds.fLeft >= -max && bounds.fTop >= -max && 307 bounds.fRight <= max && bounds.fBottom <= max); 308 } TooBigForMath(const SkPath & path)309 static bool TooBigForMath(const SkPath& path) { 310 return TooBigForMath(path.getBounds()); 311 } 312 313 // Returns number of valid points for each SkPath::Iter verb PtsInIter(unsigned verb)314 static int PtsInIter(unsigned verb) { 315 static const uint8_t gPtsInVerb[] = { 316 1, // kMove pts[0] 317 2, // kLine pts[0..1] 318 3, // kQuad pts[0..2] 319 3, // kConic pts[0..2] 320 4, // kCubic pts[0..3] 321 0, // kClose 322 0 // kDone 323 }; 324 325 SkASSERT(verb < std::size(gPtsInVerb)); 326 return gPtsInVerb[verb]; 327 } 328 329 // Returns number of valid points for each verb, not including the "starter" 330 // point that the Iterator adds for line/quad/conic/cubic PtsInVerb(unsigned verb)331 static int PtsInVerb(unsigned verb) { 332 static const uint8_t gPtsInVerb[] = { 333 1, // kMove pts[0] 334 1, // kLine pts[0..1] 335 2, // kQuad pts[0..2] 336 2, // kConic pts[0..2] 337 3, // kCubic pts[0..3] 338 0, // kClose 339 0 // kDone 340 }; 341 342 SkASSERT(verb < std::size(gPtsInVerb)); 343 return gPtsInVerb[verb]; 344 } 345 346 static bool IsAxisAligned(const SkPath& path); 347 AllPointsEq(const SkPoint pts[],int count)348 static bool AllPointsEq(const SkPoint pts[], int count) { 349 for (int i = 1; i < count; ++i) { 350 if (pts[0] != pts[i]) { 351 return false; 352 } 353 } 354 return true; 355 } 356 LastMoveToIndex(const SkPath & path)357 static int LastMoveToIndex(const SkPath& path) { return path.fLastMoveToIndex; } 358 359 static bool IsRectContour(const SkPath&, bool allowPartial, int* currVerb, 360 const SkPoint** ptsPtr, bool* isClosed, SkPathDirection* direction, 361 SkRect* rect); 362 363 /** Returns true if SkPath is equivalent to nested SkRect pair when filled. 364 If false, rect and dirs are unchanged. 365 If true, rect and dirs are written to if not nullptr: 366 setting rect[0] to outer SkRect, and rect[1] to inner SkRect; 367 setting dirs[0] to SkPathDirection of outer SkRect, and dirs[1] to SkPathDirection of 368 inner SkRect. 369 370 @param rect storage for SkRect pair; may be nullptr 371 @param dirs storage for SkPathDirection pair; may be nullptr 372 @return true if SkPath contains nested SkRect pair 373 */ 374 static bool IsNestedFillRects(const SkPath&, SkRect rect[2], 375 SkPathDirection dirs[2] = nullptr); 376 IsInverseFillType(SkPathFillType fill)377 static bool IsInverseFillType(SkPathFillType fill) { 378 return (static_cast<int>(fill) & 2) != 0; 379 } 380 381 /** Returns equivalent SkPath::FillType representing SkPath fill inside its bounds. 382 . 383 384 @param fill one of: kWinding_FillType, kEvenOdd_FillType, 385 kInverseWinding_FillType, kInverseEvenOdd_FillType 386 @return fill, or kWinding_FillType or kEvenOdd_FillType if fill is inverted 387 */ ConvertToNonInverseFillType(SkPathFillType fill)388 static SkPathFillType ConvertToNonInverseFillType(SkPathFillType fill) { 389 return (SkPathFillType)(static_cast<int>(fill) & 1); 390 } 391 392 /** 393 * If needed (to not blow-up under a perspective matrix), clip the path, returning the 394 * answer in "result", and return true. 395 * 396 * Note result might be empty (if the path was completely clipped out). 397 * 398 * If no clipping is needed, returns false and "result" is left unchanged. 399 */ 400 static bool PerspectiveClip(const SkPath& src, const SkMatrix&, SkPath* result); 401 402 /** 403 * Gets the number of GenIDChangeListeners. If another thread has access to this path then 404 * this may be stale before return and only indicates that the count was the return value 405 * at some point during the execution of the function. 406 */ 407 static int GenIDChangeListenersCount(const SkPath&); 408 UpdatePathPoint(SkPath * path,int index,const SkPoint & pt)409 static void UpdatePathPoint(SkPath* path, int index, const SkPoint& pt) { 410 SkASSERT(index < path->countPoints()); 411 SkPathRef::Editor ed(&path->fPathRef); 412 ed.writablePoints()[index] = pt; 413 path->dirtyAfterEdit(); 414 } 415 GetConvexity(const SkPath & path)416 static SkPathConvexity GetConvexity(const SkPath& path) { 417 return path.getConvexity(); 418 } GetConvexityOrUnknown(const SkPath & path)419 static SkPathConvexity GetConvexityOrUnknown(const SkPath& path) { 420 return path.getConvexityOrUnknown(); 421 } SetConvexity(const SkPath & path,SkPathConvexity c)422 static void SetConvexity(const SkPath& path, SkPathConvexity c) { 423 path.setConvexity(c); 424 } ForceComputeConvexity(const SkPath & path)425 static void ForceComputeConvexity(const SkPath& path) { 426 path.setConvexity(SkPathConvexity::kUnknown); 427 (void)path.isConvex(); 428 } 429 ReverseAddPath(SkPathBuilder * builder,const SkPath & reverseMe)430 static void ReverseAddPath(SkPathBuilder* builder, const SkPath& reverseMe) { 431 builder->privateReverseAddPath(reverseMe); 432 } 433 MakePath(const SkPathVerbAnalysis & analysis,const SkPoint points[],const uint8_t verbs[],int verbCount,const SkScalar conics[],SkPathFillType fillType,bool isVolatile)434 static SkPath MakePath(const SkPathVerbAnalysis& analysis, 435 const SkPoint points[], 436 const uint8_t verbs[], 437 int verbCount, 438 const SkScalar conics[], 439 SkPathFillType fillType, 440 bool isVolatile) { 441 return SkPath::MakeInternal(analysis, points, verbs, verbCount, conics, fillType, 442 isVolatile); 443 } 444 }; 445 446 // Lightweight variant of SkPath::Iter that only returns segments (e.g. lines/conics). 447 // Does not return kMove or kClose. 448 // Always "auto-closes" each contour. 449 // Roughly the same as SkPath::Iter(path, true), but does not return moves or closes 450 // 451 class SkPathEdgeIter { 452 const uint8_t* fVerbs; 453 const uint8_t* fVerbsStop; 454 const SkPoint* fPts; 455 const SkPoint* fMoveToPtr; 456 const SkScalar* fConicWeights; 457 SkPoint fScratch[2]; // for auto-close lines 458 bool fNeedsCloseLine; 459 bool fNextIsNewContour; 460 SkDEBUGCODE(bool fIsConic;) 461 462 enum { 463 kIllegalEdgeValue = 99 464 }; 465 466 public: 467 SkPathEdgeIter(const SkPath& path); 468 conicWeight()469 SkScalar conicWeight() const { 470 SkASSERT(fIsConic); 471 return *fConicWeights; 472 } 473 474 enum class Edge { 475 kLine = SkPath::kLine_Verb, 476 kQuad = SkPath::kQuad_Verb, 477 kConic = SkPath::kConic_Verb, 478 kCubic = SkPath::kCubic_Verb, 479 }; 480 EdgeToVerb(Edge e)481 static SkPath::Verb EdgeToVerb(Edge e) { 482 return SkPath::Verb(e); 483 } 484 485 struct Result { 486 const SkPoint* fPts; // points for the segment, or null if done 487 Edge fEdge; 488 bool fIsNewContour; 489 490 // Returns true when it holds an Edge, false when the path is done. 491 explicit operator bool() { return fPts != nullptr; } 492 }; 493 next()494 Result next() { 495 auto closeline = [&]() { 496 fScratch[0] = fPts[-1]; 497 fScratch[1] = *fMoveToPtr; 498 fNeedsCloseLine = false; 499 fNextIsNewContour = true; 500 return Result{ fScratch, Edge::kLine, false }; 501 }; 502 503 for (;;) { 504 SkASSERT(fVerbs <= fVerbsStop); 505 if (fVerbs == fVerbsStop) { 506 return fNeedsCloseLine 507 ? closeline() 508 : Result{ nullptr, Edge(kIllegalEdgeValue), false }; 509 } 510 511 SkDEBUGCODE(fIsConic = false;) 512 513 const auto v = *fVerbs++; 514 switch (v) { 515 case SkPath::kMove_Verb: { 516 if (fNeedsCloseLine) { 517 auto res = closeline(); 518 fMoveToPtr = fPts++; 519 return res; 520 } 521 fMoveToPtr = fPts++; 522 fNextIsNewContour = true; 523 } break; 524 case SkPath::kClose_Verb: 525 if (fNeedsCloseLine) return closeline(); 526 break; 527 default: { 528 // Actual edge. 529 const int pts_count = (v+2) / 2, 530 cws_count = (v & (v-1)) / 2; 531 SkASSERT(pts_count == SkPathPriv::PtsInIter(v) - 1); 532 533 fNeedsCloseLine = true; 534 fPts += pts_count; 535 fConicWeights += cws_count; 536 537 SkDEBUGCODE(fIsConic = (v == SkPath::kConic_Verb);) 538 SkASSERT(fIsConic == (cws_count > 0)); 539 540 bool isNewContour = fNextIsNewContour; 541 fNextIsNewContour = false; 542 return { &fPts[-(pts_count + 1)], Edge(v), isNewContour }; 543 } 544 } 545 } 546 } 547 }; 548 549 #endif 550