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