SkContourMeasure.cpp (26542B)
1 /* 2 * Copyright 2018 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 #include "include/core/SkContourMeasure.h" 9 10 #include "include/core/SkMatrix.h" 11 #include "include/core/SkPath.h" 12 #include "include/core/SkPathBuilder.h" 13 #include "include/core/SkPathTypes.h" 14 #include "include/private/base/SkDebug.h" 15 #include "include/private/base/SkFloatingPoint.h" 16 #include "include/private/base/SkTo.h" 17 #include "src/core/SkGeometry.h" 18 #include "src/core/SkPathMeasurePriv.h" 19 #include "src/core/SkPathPriv.h" 20 21 #include <algorithm> 22 #include <array> 23 #include <cstddef> 24 #include <optional> 25 #include <utility> 26 27 #define kMaxTValue 0x3FFFFFFF 28 29 constexpr static inline SkScalar tValue2Scalar(int t) { 30 SkASSERT((unsigned)t <= kMaxTValue); 31 // 1/kMaxTValue can't be represented as a float, but it's close and the limits work fine. 32 const SkScalar kMaxTReciprocal = 1.0f / (SkScalar)kMaxTValue; 33 return t * kMaxTReciprocal; 34 } 35 36 static_assert(0.0f == tValue2Scalar( 0), "Lower limit should be exact."); 37 static_assert(1.0f == tValue2Scalar(kMaxTValue), "Upper limit should be exact."); 38 39 SkScalar SkContourMeasure::Segment::getScalarT() const { 40 return tValue2Scalar(fTValue); 41 } 42 43 void SkContourMeasure_segTo(const SkPoint pts[], unsigned segType, 44 SkScalar startT, SkScalar stopT, SkPathBuilder* dst) { 45 SkASSERT(startT >= 0 && startT <= SK_Scalar1); 46 SkASSERT(stopT >= 0 && stopT <= SK_Scalar1); 47 SkASSERT(startT <= stopT); 48 49 if (startT == stopT) { 50 if (!dst->isEmpty()) { 51 /* if the dash as a zero-length on segment, add a corresponding zero-length line. 52 The stroke code will add end caps to zero length lines as appropriate */ 53 auto lastPtOptional = dst->getLastPt(); 54 SkAssertResult(lastPtOptional.has_value()); 55 dst->lineTo(lastPtOptional.value()); 56 } 57 return; 58 } 59 60 SkPoint tmp0[7], tmp1[7]; 61 62 switch (segType) { 63 case kLine_SegType: 64 if (SK_Scalar1 == stopT) { 65 dst->lineTo(pts[1]); 66 } else { 67 dst->lineTo(SkScalarInterp(pts[0].fX, pts[1].fX, stopT), 68 SkScalarInterp(pts[0].fY, pts[1].fY, stopT)); 69 } 70 break; 71 case kQuad_SegType: 72 if (0 == startT) { 73 if (SK_Scalar1 == stopT) { 74 dst->quadTo(pts[1], pts[2]); 75 } else { 76 SkChopQuadAt(pts, tmp0, stopT); 77 dst->quadTo(tmp0[1], tmp0[2]); 78 } 79 } else { 80 SkChopQuadAt(pts, tmp0, startT); 81 if (SK_Scalar1 == stopT) { 82 dst->quadTo(tmp0[3], tmp0[4]); 83 } else { 84 SkChopQuadAt(&tmp0[2], tmp1, (stopT - startT) / (1 - startT)); 85 dst->quadTo(tmp1[1], tmp1[2]); 86 } 87 } 88 break; 89 case kConic_SegType: { 90 SkConic conic(pts[0], pts[2], pts[3], pts[1].fX); 91 92 if (0 == startT) { 93 if (SK_Scalar1 == stopT) { 94 dst->conicTo(conic.fPts[1], conic.fPts[2], conic.fW); 95 } else { 96 SkConic tmp[2]; 97 if (conic.chopAt(stopT, tmp)) { 98 dst->conicTo(tmp[0].fPts[1], tmp[0].fPts[2], tmp[0].fW); 99 } 100 } 101 } else { 102 if (SK_Scalar1 == stopT) { 103 SkConic tmp[2]; 104 if (conic.chopAt(startT, tmp)) { 105 dst->conicTo(tmp[1].fPts[1], tmp[1].fPts[2], tmp[1].fW); 106 } 107 } else { 108 SkConic tmp; 109 conic.chopAt(startT, stopT, &tmp); 110 dst->conicTo(tmp.fPts[1], tmp.fPts[2], tmp.fW); 111 } 112 } 113 } break; 114 case kCubic_SegType: 115 if (0 == startT) { 116 if (SK_Scalar1 == stopT) { 117 dst->cubicTo(pts[1], pts[2], pts[3]); 118 } else { 119 SkChopCubicAt(pts, tmp0, stopT); 120 dst->cubicTo(tmp0[1], tmp0[2], tmp0[3]); 121 } 122 } else { 123 SkChopCubicAt(pts, tmp0, startT); 124 if (SK_Scalar1 == stopT) { 125 dst->cubicTo(tmp0[4], tmp0[5], tmp0[6]); 126 } else { 127 SkChopCubicAt(&tmp0[3], tmp1, (stopT - startT) / (1 - startT)); 128 dst->cubicTo(tmp1[1], tmp1[2], tmp1[3]); 129 } 130 } 131 break; 132 default: 133 SK_ABORT("unknown segType"); 134 } 135 } 136 137 /////////////////////////////////////////////////////////////////////////////// 138 139 static inline int tspan_big_enough(int tspan) { 140 SkASSERT((unsigned)tspan <= kMaxTValue); 141 return tspan >> 10; 142 } 143 144 // can't use tangents, since we need [0..1..................2] to be seen 145 // as definitely not a line (it is when drawn, but not parametrically) 146 // so we compare midpoints 147 #define CHEAP_DIST_LIMIT (SK_Scalar1/2) // just made this value up 148 149 static bool quad_too_curvy(const SkPoint pts[3], SkScalar tolerance) { 150 // diff = (a/4 + b/2 + c/4) - (a/2 + c/2) 151 // diff = -a/4 + b/2 - c/4 152 SkScalar dx = SkScalarHalf(pts[1].fX) - 153 SkScalarHalf(SkScalarHalf(pts[0].fX + pts[2].fX)); 154 SkScalar dy = SkScalarHalf(pts[1].fY) - 155 SkScalarHalf(SkScalarHalf(pts[0].fY + pts[2].fY)); 156 157 SkScalar dist = std::max(SkScalarAbs(dx), SkScalarAbs(dy)); 158 return dist > tolerance; 159 } 160 161 static bool conic_too_curvy(const SkPoint& firstPt, const SkPoint& midTPt, 162 const SkPoint& lastPt, SkScalar tolerance) { 163 SkPoint midEnds = firstPt + lastPt; 164 midEnds *= 0.5f; 165 SkVector dxy = midTPt - midEnds; 166 SkScalar dist = std::max(SkScalarAbs(dxy.fX), SkScalarAbs(dxy.fY)); 167 return dist > tolerance; 168 } 169 170 static bool cheap_dist_exceeds_limit(const SkPoint& pt, SkScalar x, SkScalar y, 171 SkScalar tolerance) { 172 SkScalar dist = std::max(SkScalarAbs(x - pt.fX), SkScalarAbs(y - pt.fY)); 173 // just made up the 1/2 174 return dist > tolerance; 175 } 176 177 static bool cubic_too_curvy(const SkPoint pts[4], SkScalar tolerance) { 178 return cheap_dist_exceeds_limit(pts[1], 179 SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1/3), 180 SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1/3), tolerance) 181 || 182 cheap_dist_exceeds_limit(pts[2], 183 SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1*2/3), 184 SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1*2/3), tolerance); 185 } 186 187 // puts a cap on the total size of our output, since the client can pass in 188 // arbitrarily large values for resScale. 189 constexpr int kMaxRecursionDepth = 8; 190 191 class SkContourMeasureIter::Impl { 192 public: 193 Impl(const SkPath& path, bool forceClosed, SkScalar resScale) 194 : fPath(path) 195 , fIter(SkPathPriv::Iterate(fPath).begin()) 196 , fTolerance(CHEAP_DIST_LIMIT * sk_ieee_float_divide(1.0f, resScale)) 197 , fForceClosed(forceClosed) {} 198 199 bool hasNextSegments() const { return fIter != SkPathPriv::Iterate(fPath).end(); } 200 SkContourMeasure* buildSegments(); 201 202 private: 203 SkPath fPath; 204 SkPathPriv::RangeIter fIter; 205 SkScalar fTolerance; 206 bool fForceClosed; 207 208 // temporary 209 SkTDArray<SkContourMeasure::Segment> fSegments; 210 SkTDArray<SkPoint> fPts; // Points used to define the segments 211 212 SkDEBUGCODE(void validate() const;) 213 SkScalar compute_line_seg(SkPoint p0, SkPoint p1, SkScalar distance, unsigned ptIndex); 214 SkScalar compute_quad_segs(const SkPoint pts[3], SkScalar distance, 215 int mint, int maxt, unsigned ptIndex, int recursionDepth = 0); 216 SkScalar compute_conic_segs(const SkConic& conic, SkScalar distance, 217 int mint, const SkPoint& minPt, 218 int maxt, const SkPoint& maxPt, 219 unsigned ptIndex, int recursionDepth = 0); 220 SkScalar compute_cubic_segs(const SkPoint pts[4], SkScalar distance, 221 int mint, int maxt, unsigned ptIndex, int recursionDepth = 0); 222 }; 223 224 SkScalar SkContourMeasureIter::Impl::compute_quad_segs(const SkPoint pts[3], SkScalar distance, 225 int mint, int maxt, unsigned ptIndex, 226 int recursionDepth) { 227 if (recursionDepth < kMaxRecursionDepth && 228 tspan_big_enough(maxt - mint) && quad_too_curvy(pts, fTolerance)) { 229 SkPoint tmp[5]; 230 int halft = (mint + maxt) >> 1; 231 232 SkChopQuadAtHalf(pts, tmp); 233 recursionDepth += 1; 234 distance = this->compute_quad_segs(tmp, distance, mint, halft, ptIndex, recursionDepth); 235 distance = this->compute_quad_segs(&tmp[2], distance, halft, maxt, ptIndex, recursionDepth); 236 } else { 237 SkScalar d = SkPoint::Distance(pts[0], pts[2]); 238 SkScalar prevD = distance; 239 distance += d; 240 if (distance > prevD) { 241 SkASSERT(ptIndex < (unsigned)fPts.size()); 242 SkContourMeasure::Segment* seg = fSegments.append(); 243 seg->fDistance = distance; 244 seg->fPtIndex = ptIndex; 245 seg->fType = kQuad_SegType; 246 seg->fTValue = maxt; 247 } 248 } 249 return distance; 250 } 251 252 SkScalar SkContourMeasureIter::Impl::compute_conic_segs(const SkConic& conic, SkScalar distance, 253 int mint, const SkPoint& minPt, 254 int maxt, const SkPoint& maxPt, 255 unsigned ptIndex, int recursionDepth) { 256 int halft = (mint + maxt) >> 1; 257 SkPoint halfPt = conic.evalAt(tValue2Scalar(halft)); 258 if (!halfPt.isFinite()) { 259 return distance; 260 } 261 if (recursionDepth < kMaxRecursionDepth && 262 tspan_big_enough(maxt - mint) && conic_too_curvy(minPt, halfPt, maxPt, fTolerance)) 263 { 264 recursionDepth += 1; 265 distance = this->compute_conic_segs(conic, distance, mint, minPt, halft, halfPt, 266 ptIndex, recursionDepth); 267 distance = this->compute_conic_segs(conic, distance, halft, halfPt, maxt, maxPt, 268 ptIndex, recursionDepth); 269 } else { 270 SkScalar d = SkPoint::Distance(minPt, maxPt); 271 SkScalar prevD = distance; 272 distance += d; 273 if (distance > prevD) { 274 SkASSERT(ptIndex < (unsigned)fPts.size()); 275 SkContourMeasure::Segment* seg = fSegments.append(); 276 seg->fDistance = distance; 277 seg->fPtIndex = ptIndex; 278 seg->fType = kConic_SegType; 279 seg->fTValue = maxt; 280 } 281 } 282 return distance; 283 } 284 285 SkScalar SkContourMeasureIter::Impl::compute_cubic_segs(const SkPoint pts[4], SkScalar distance, 286 int mint, int maxt, unsigned ptIndex, 287 int recursionDepth) { 288 if (recursionDepth < kMaxRecursionDepth && 289 tspan_big_enough(maxt - mint) && cubic_too_curvy(pts, fTolerance)) 290 { 291 SkPoint tmp[7]; 292 int halft = (mint + maxt) >> 1; 293 294 SkChopCubicAtHalf(pts, tmp); 295 recursionDepth += 1; 296 distance = this->compute_cubic_segs(tmp, distance, mint, halft, 297 ptIndex, recursionDepth); 298 distance = this->compute_cubic_segs(&tmp[3], distance, halft, maxt, 299 ptIndex, recursionDepth); 300 } else { 301 SkScalar d = SkPoint::Distance(pts[0], pts[3]); 302 SkScalar prevD = distance; 303 distance += d; 304 if (distance > prevD) { 305 SkASSERT(ptIndex < (unsigned)fPts.size()); 306 SkContourMeasure::Segment* seg = fSegments.append(); 307 seg->fDistance = distance; 308 seg->fPtIndex = ptIndex; 309 seg->fType = kCubic_SegType; 310 seg->fTValue = maxt; 311 } 312 } 313 return distance; 314 } 315 316 SkScalar SkContourMeasureIter::Impl::compute_line_seg(SkPoint p0, SkPoint p1, SkScalar distance, 317 unsigned ptIndex) { 318 SkScalar d = SkPoint::Distance(p0, p1); 319 SkASSERT(d >= 0); 320 SkScalar prevD = distance; 321 distance += d; 322 if (distance > prevD) { 323 SkASSERT((unsigned)ptIndex < (unsigned)fPts.size()); 324 SkContourMeasure::Segment* seg = fSegments.append(); 325 seg->fDistance = distance; 326 seg->fPtIndex = ptIndex; 327 seg->fType = kLine_SegType; 328 seg->fTValue = kMaxTValue; 329 } 330 return distance; 331 } 332 333 #ifdef SK_DEBUG 334 void SkContourMeasureIter::Impl::validate() const { 335 #ifndef SK_DISABLE_SLOW_DEBUG_VALIDATION 336 const SkContourMeasure::Segment* seg = fSegments.begin(); 337 const SkContourMeasure::Segment* stop = fSegments.end(); 338 unsigned ptIndex = 0; 339 SkScalar distance = 0; 340 // limit the loop to a reasonable number; pathological cases can run for minutes 341 int maxChecks = 10000000; // set to INT_MAX to defeat the check 342 while (seg < stop) { 343 SkASSERT(seg->fDistance > distance); 344 SkASSERT(seg->fPtIndex >= ptIndex); 345 SkASSERT(seg->fTValue > 0); 346 347 const SkContourMeasure::Segment* s = seg; 348 while (s < stop - 1 && s[0].fPtIndex == s[1].fPtIndex && --maxChecks > 0) { 349 SkASSERT(s[0].fType == s[1].fType); 350 SkASSERT(s[0].fTValue < s[1].fTValue); 351 s += 1; 352 } 353 354 distance = seg->fDistance; 355 ptIndex = seg->fPtIndex; 356 seg += 1; 357 } 358 #endif 359 } 360 #endif 361 362 SkContourMeasure* SkContourMeasureIter::Impl::buildSegments() { 363 int ptIndex = -1; 364 SkScalar distance = 0; 365 bool haveSeenClose = fForceClosed; 366 bool haveSeenMoveTo = false; 367 368 /* Note: 369 * as we accumulate distance, we have to check that the result of += 370 * actually made it larger, since a very small delta might be > 0, but 371 * still have no effect on distance (if distance >>> delta). 372 * 373 * We do this check below, and in compute_quad_segs and compute_cubic_segs 374 */ 375 376 fSegments.reset(); 377 fPts.reset(); 378 379 auto end = SkPathPriv::Iterate(fPath).end(); 380 for (; fIter != end; ++fIter) { 381 auto [verb, pts, w] = *fIter; 382 if (haveSeenMoveTo && verb == SkPathVerb::kMove) { 383 break; 384 } 385 switch (verb) { 386 case SkPathVerb::kMove: 387 ptIndex += 1; 388 fPts.append(1, pts); 389 SkASSERT(!haveSeenMoveTo); 390 haveSeenMoveTo = true; 391 break; 392 393 case SkPathVerb::kLine: { 394 SkASSERT(haveSeenMoveTo); 395 SkScalar prevD = distance; 396 distance = this->compute_line_seg(pts[0], pts[1], distance, ptIndex); 397 if (distance > prevD) { 398 fPts.append(1, pts + 1); 399 ptIndex++; 400 } 401 } break; 402 403 case SkPathVerb::kQuad: { 404 SkASSERT(haveSeenMoveTo); 405 SkScalar prevD = distance; 406 distance = this->compute_quad_segs(pts, distance, 0, kMaxTValue, ptIndex); 407 if (distance > prevD) { 408 fPts.append(2, pts + 1); 409 ptIndex += 2; 410 } 411 } break; 412 413 case SkPathVerb::kConic: { 414 SkASSERT(haveSeenMoveTo); 415 const SkConic conic(pts, *w); 416 SkScalar prevD = distance; 417 distance = this->compute_conic_segs(conic, distance, 0, conic.fPts[0], 418 kMaxTValue, conic.fPts[2], ptIndex); 419 if (distance > prevD) { 420 // we store the conic weight in our next point, followed by the last 2 pts 421 // thus to reconstitue a conic, you'd need to say 422 // SkConic(pts[0], pts[2], pts[3], weight = pts[1].fX) 423 fPts.append()->set(conic.fW, 0); 424 fPts.append(2, pts + 1); 425 ptIndex += 3; 426 } 427 } break; 428 429 case SkPathVerb::kCubic: { 430 SkASSERT(haveSeenMoveTo); 431 SkScalar prevD = distance; 432 distance = this->compute_cubic_segs(pts, distance, 0, kMaxTValue, ptIndex); 433 if (distance > prevD) { 434 fPts.append(3, pts + 1); 435 ptIndex += 3; 436 } 437 } break; 438 439 case SkPathVerb::kClose: 440 haveSeenClose = true; 441 break; 442 } 443 444 } 445 446 if (!SkIsFinite(distance)) { 447 return nullptr; 448 } 449 if (fSegments.empty()) { 450 return nullptr; 451 } 452 453 if (haveSeenClose) { 454 SkScalar prevD = distance; 455 SkPoint firstPt = fPts[0]; 456 distance = this->compute_line_seg(fPts[ptIndex], firstPt, distance, ptIndex); 457 if (distance > prevD) { 458 *fPts.append() = firstPt; 459 } 460 } 461 462 SkDEBUGCODE(this->validate();) 463 464 return new SkContourMeasure(std::move(fSegments), std::move(fPts), distance, haveSeenClose); 465 } 466 467 static void compute_pos_tan(const SkPoint pts[], unsigned segType, 468 SkScalar t, SkPoint* pos, SkVector* tangent) { 469 switch (segType) { 470 case kLine_SegType: 471 if (pos) { 472 pos->set(SkScalarInterp(pts[0].fX, pts[1].fX, t), 473 SkScalarInterp(pts[0].fY, pts[1].fY, t)); 474 } 475 if (tangent) { 476 tangent->setNormalize(pts[1].fX - pts[0].fX, pts[1].fY - pts[0].fY); 477 } 478 break; 479 case kQuad_SegType: 480 SkEvalQuadAt(pts, t, pos, tangent); 481 if (tangent) { 482 tangent->normalize(); 483 } 484 break; 485 case kConic_SegType: { 486 SkConic(pts[0], pts[2], pts[3], pts[1].fX).evalAt(t, pos, tangent); 487 if (tangent) { 488 tangent->normalize(); 489 } 490 } break; 491 case kCubic_SegType: 492 SkEvalCubicAt(pts, t, pos, tangent, nullptr); 493 if (tangent) { 494 tangent->normalize(); 495 } 496 break; 497 default: 498 SkDEBUGFAIL("unknown segType"); 499 } 500 } 501 502 503 //////////////////////////////////////////////////////////////////////////////// 504 //////////////////////////////////////////////////////////////////////////////// 505 506 SkContourMeasureIter::SkContourMeasureIter() { 507 } 508 509 SkContourMeasureIter::SkContourMeasureIter(const SkPath& path, bool forceClosed, 510 SkScalar resScale) { 511 this->reset(path, forceClosed, resScale); 512 } 513 514 SkContourMeasureIter::~SkContourMeasureIter() {} 515 516 SkContourMeasureIter::SkContourMeasureIter(SkContourMeasureIter&&) = default; 517 SkContourMeasureIter& SkContourMeasureIter::operator=(SkContourMeasureIter&&) = default; 518 519 /** Assign a new path, or null to have none. 520 */ 521 void SkContourMeasureIter::reset(const SkPath& path, bool forceClosed, SkScalar resScale) { 522 if (path.isFinite()) { 523 fImpl = std::make_unique<Impl>(path, forceClosed, resScale); 524 } else { 525 fImpl.reset(); 526 } 527 } 528 529 sk_sp<SkContourMeasure> SkContourMeasureIter::next() { 530 if (!fImpl) { 531 return nullptr; 532 } 533 while (fImpl->hasNextSegments()) { 534 auto cm = fImpl->buildSegments(); 535 if (cm) { 536 return sk_sp<SkContourMeasure>(cm); 537 } 538 } 539 return nullptr; 540 } 541 542 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 543 544 SkContourMeasure::SkContourMeasure(SkTDArray<Segment>&& segs, SkTDArray<SkPoint>&& pts, SkScalar length, bool isClosed) 545 : fSegments(std::move(segs)) 546 , fPts(std::move(pts)) 547 , fLength(length) 548 , fIsClosed(isClosed) 549 {} 550 551 template <typename T, typename K> 552 int SkTKSearch(const T base[], int count, const K& key) { 553 SkASSERT(count >= 0); 554 if (count <= 0) { 555 return ~0; 556 } 557 558 SkASSERT(base != nullptr); // base may be nullptr if count is zero 559 560 unsigned lo = 0; 561 unsigned hi = count - 1; 562 563 while (lo < hi) { 564 unsigned mid = (hi + lo) >> 1; 565 if (base[mid].fDistance < key) { 566 lo = mid + 1; 567 } else { 568 hi = mid; 569 } 570 } 571 572 if (base[hi].fDistance < key) { 573 hi += 1; 574 hi = ~hi; 575 } else if (key < base[hi].fDistance) { 576 hi = ~hi; 577 } 578 return hi; 579 } 580 581 const SkContourMeasure::Segment* SkContourMeasure::distanceToSegment( SkScalar distance, 582 SkScalar* t) const { 583 SkDEBUGCODE(SkScalar length = ) this->length(); 584 SkASSERT(distance >= 0 && distance <= length); 585 586 const Segment* seg = fSegments.begin(); 587 int count = fSegments.size(); 588 589 int index = SkTKSearch<Segment, SkScalar>(seg, count, distance); 590 // don't care if we hit an exact match or not, so we xor index if it is negative 591 index ^= (index >> 31); 592 seg = &seg[index]; 593 594 // now interpolate t-values with the prev segment (if possible) 595 SkScalar startT = 0, startD = 0; 596 // check if the prev segment is legal, and references the same set of points 597 if (index > 0) { 598 startD = seg[-1].fDistance; 599 if (seg[-1].fPtIndex == seg->fPtIndex) { 600 SkASSERT(seg[-1].fType == seg->fType); 601 startT = seg[-1].getScalarT(); 602 } 603 } 604 605 SkASSERT(seg->getScalarT() > startT); 606 SkASSERT(distance >= startD); 607 SkASSERT(seg->fDistance > startD); 608 609 *t = startT + (seg->getScalarT() - startT) * (distance - startD) / (seg->fDistance - startD); 610 return seg; 611 } 612 613 bool SkContourMeasure::getPosTan(SkScalar distance, SkPoint* pos, SkVector* tangent) const { 614 if (SkIsNaN(distance)) { 615 return false; 616 } 617 618 const SkScalar length = this->length(); 619 SkASSERT(length > 0 && !fSegments.empty()); 620 621 // pin the distance to a legal range 622 if (distance < 0) { 623 distance = 0; 624 } else if (distance > length) { 625 distance = length; 626 } 627 628 SkScalar t; 629 const Segment* seg = this->distanceToSegment(distance, &t); 630 if (SkIsNaN(t)) { 631 return false; 632 } 633 634 SkASSERT((unsigned)seg->fPtIndex < (unsigned)fPts.size()); 635 compute_pos_tan(&fPts[seg->fPtIndex], seg->fType, t, pos, tangent); 636 return true; 637 } 638 639 bool SkContourMeasure::getMatrix(SkScalar distance, SkMatrix* matrix, MatrixFlags flags) const { 640 SkPoint position; 641 SkVector tangent; 642 643 if (this->getPosTan(distance, &position, &tangent)) { 644 if (matrix) { 645 if (flags & kGetTangent_MatrixFlag) { 646 matrix->setSinCos(tangent.fY, tangent.fX, 0, 0); 647 } else { 648 matrix->reset(); 649 } 650 if (flags & kGetPosition_MatrixFlag) { 651 matrix->postTranslate(position.fX, position.fY); 652 } 653 } 654 return true; 655 } 656 return false; 657 } 658 659 bool SkContourMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPathBuilder* dst, 660 bool startWithMoveTo) const { 661 SkASSERT(dst); 662 663 SkScalar length = this->length(); // ensure we have built our segments 664 665 if (startD < 0) { 666 startD = 0; 667 } 668 if (stopD > length) { 669 stopD = length; 670 } 671 if (!(startD <= stopD)) { // catch NaN values as well 672 return false; 673 } 674 if (fSegments.empty()) { 675 return false; 676 } 677 678 SkPoint p; 679 SkScalar startT, stopT; 680 const Segment* seg = this->distanceToSegment(startD, &startT); 681 if (!SkIsFinite(startT)) { 682 return false; 683 } 684 const Segment* stopSeg = this->distanceToSegment(stopD, &stopT); 685 if (!SkIsFinite(stopT)) { 686 return false; 687 } 688 SkASSERT(seg <= stopSeg); 689 if (startWithMoveTo) { 690 compute_pos_tan(&fPts[seg->fPtIndex], seg->fType, startT, &p, nullptr); 691 dst->moveTo(p); 692 } 693 694 if (seg->fPtIndex == stopSeg->fPtIndex) { 695 SkContourMeasure_segTo(&fPts[seg->fPtIndex], seg->fType, startT, stopT, dst); 696 } else { 697 do { 698 SkContourMeasure_segTo(&fPts[seg->fPtIndex], seg->fType, startT, SK_Scalar1, dst); 699 seg = SkContourMeasure::Segment::Next(seg); 700 startT = 0; 701 } while (seg->fPtIndex < stopSeg->fPtIndex); 702 SkContourMeasure_segTo(&fPts[seg->fPtIndex], seg->fType, 0, stopT, dst); 703 } 704 705 return true; 706 } 707 708 SkContourMeasure::VerbMeasure SkContourMeasure::ForwardVerbIterator::operator*() const { 709 static constexpr size_t seg_pt_count[] = { 710 2, // kLine (current_pt, 1 line pt) 711 3, // kQuad (current_pt, 2 quad pts) 712 4, // kCubic (current_pt, 3 cubic pts) 713 4, // kConic (current_pt, {weight, 0}, 2 conic pts) 714 }; 715 static constexpr SkPathVerb seg_verb[] = { 716 SkPathVerb::kLine, 717 SkPathVerb::kQuad, 718 SkPathVerb::kCubic, 719 SkPathVerb::kConic, 720 }; 721 static_assert(std::size(seg_pt_count) == std::size(seg_verb)); 722 static_assert(static_cast<size_t>(kLine_SegType) < std::size(seg_pt_count)); 723 static_assert(static_cast<size_t>(kQuad_SegType) < std::size(seg_pt_count)); 724 static_assert(static_cast<size_t>(kCubic_SegType) < std::size(seg_pt_count)); 725 static_assert(static_cast<size_t>(kConic_SegType) < std::size(seg_pt_count)); 726 727 SkASSERT(SkToSizeT(fSegments.front().fType) < std::size(seg_pt_count)); 728 SkASSERT(fSegments.front().fPtIndex + seg_pt_count[fSegments.front().fType] <= fPts.size()); 729 730 return { 731 fSegments.front().fDistance, 732 seg_verb[fSegments.front().fType], 733 SkSpan(fPts.data() + fSegments.front().fPtIndex, seg_pt_count[fSegments.front().fType]), 734 }; 735 } 736 737 #ifdef SK_SUPPORT_MUTABLE_PATHEFFECT 738 bool SkContourMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, 739 bool startWithMoveTo) const { 740 SkPathBuilder builder; 741 if (this->getSegment(startD, stopD, &builder, startWithMoveTo)) { 742 *dst = builder.detach(); 743 return true; 744 } 745 return false; 746 } 747 #endif