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