LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkPathMeasure.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 393 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 29 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13