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