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