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