tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SkContourMeasure.cpp (26542B)


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