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