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