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