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