Home | History | Annotate | Download | only in core
      1 
      2 /*
      3  * Copyright 2008 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "SkPathMeasure.h"
     11 #include "SkGeometry.h"
     12 #include "SkPath.h"
     13 #include "SkTSearch.h"
     14 
     15 // these must be 0,1,2 since they are in our 2-bit field
     16 enum {
     17     kLine_SegType,
     18     kQuad_SegType,
     19     kCubic_SegType
     20 };
     21 
     22 #define kMaxTValue  32767
     23 
     24 static inline SkScalar tValue2Scalar(int t) {
     25     SkASSERT((unsigned)t <= kMaxTValue);
     26 
     27 #ifdef SK_SCALAR_IS_FLOAT
     28     return t * 3.05185e-5f; // t / 32767
     29 #else
     30     return (t + (t >> 14)) << 1;
     31 #endif
     32 }
     33 
     34 SkScalar SkPathMeasure::Segment::getScalarT() const {
     35     return tValue2Scalar(fTValue);
     36 }
     37 
     38 const SkPathMeasure::Segment* SkPathMeasure::NextSegment(const Segment* seg) {
     39     unsigned ptIndex = seg->fPtIndex;
     40 
     41     do {
     42         ++seg;
     43     } while (seg->fPtIndex == ptIndex);
     44     return seg;
     45 }
     46 
     47 ///////////////////////////////////////////////////////////////////////////////
     48 
     49 static inline int tspan_big_enough(int tspan) {
     50     SkASSERT((unsigned)tspan <= kMaxTValue);
     51     return tspan >> 10;
     52 }
     53 
     54 #if 0
     55 static inline bool tangents_too_curvy(const SkVector& tan0, SkVector& tan1) {
     56     static const SkScalar kFlatEnoughTangentDotProd = SK_Scalar1 * 99 / 100;
     57 
     58     SkASSERT(kFlatEnoughTangentDotProd > 0 &&
     59              kFlatEnoughTangentDotProd < SK_Scalar1);
     60 
     61     return SkPoint::DotProduct(tan0, tan1) < kFlatEnoughTangentDotProd;
     62 }
     63 #endif
     64 
     65 // can't use tangents, since we need [0..1..................2] to be seen
     66 // as definitely not a line (it is when drawn, but not parametrically)
     67 // so we compare midpoints
     68 #define CHEAP_DIST_LIMIT    (SK_Scalar1/2)  // just made this value up
     69 
     70 static bool quad_too_curvy(const SkPoint pts[3]) {
     71     // diff = (a/4 + b/2 + c/4) - (a/2 + c/2)
     72     // diff = -a/4 + b/2 - c/4
     73     SkScalar dx = SkScalarHalf(pts[1].fX) -
     74                         SkScalarHalf(SkScalarHalf(pts[0].fX + pts[2].fX));
     75     SkScalar dy = SkScalarHalf(pts[1].fY) -
     76                         SkScalarHalf(SkScalarHalf(pts[0].fY + pts[2].fY));
     77 
     78     SkScalar dist = SkMaxScalar(SkScalarAbs(dx), SkScalarAbs(dy));
     79     return dist > CHEAP_DIST_LIMIT;
     80 }
     81 
     82 static bool cheap_dist_exceeds_limit(const SkPoint& pt,
     83                                      SkScalar x, SkScalar y) {
     84     SkScalar dist = SkMaxScalar(SkScalarAbs(x - pt.fX), SkScalarAbs(y - pt.fY));
     85     // just made up the 1/2
     86     return dist > CHEAP_DIST_LIMIT;
     87 }
     88 
     89 static bool cubic_too_curvy(const SkPoint pts[4]) {
     90     return  cheap_dist_exceeds_limit(pts[1],
     91                          SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1/3),
     92                          SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1/3))
     93                          ||
     94             cheap_dist_exceeds_limit(pts[2],
     95                          SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1*2/3),
     96                          SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1*2/3));
     97 }
     98 
     99 SkScalar SkPathMeasure::compute_quad_segs(const SkPoint pts[3],
    100                           SkScalar distance, int mint, int maxt, int ptIndex) {
    101     if (tspan_big_enough(maxt - mint) && quad_too_curvy(pts)) {
    102         SkPoint tmp[5];
    103         int     halft = (mint + maxt) >> 1;
    104 
    105         SkChopQuadAtHalf(pts, tmp);
    106         distance = this->compute_quad_segs(tmp, distance, mint, halft, ptIndex);
    107         distance = this->compute_quad_segs(&tmp[2], distance, halft, maxt, ptIndex);
    108     } else {
    109         SkScalar d = SkPoint::Distance(pts[0], pts[2]);
    110         SkASSERT(d >= 0);
    111         if (!SkScalarNearlyZero(d)) {
    112             distance += d;
    113             Segment* seg = fSegments.append();
    114             seg->fDistance = distance;
    115             seg->fPtIndex = ptIndex;
    116             seg->fType = kQuad_SegType;
    117             seg->fTValue = maxt;
    118         }
    119     }
    120     return distance;
    121 }
    122 
    123 SkScalar SkPathMeasure::compute_cubic_segs(const SkPoint pts[4],
    124                            SkScalar distance, int mint, int maxt, int ptIndex) {
    125     if (tspan_big_enough(maxt - mint) && cubic_too_curvy(pts)) {
    126         SkPoint tmp[7];
    127         int     halft = (mint + maxt) >> 1;
    128 
    129         SkChopCubicAtHalf(pts, tmp);
    130         distance = this->compute_cubic_segs(tmp, distance, mint, halft, ptIndex);
    131         distance = this->compute_cubic_segs(&tmp[3], distance, halft, maxt, ptIndex);
    132     } else {
    133         SkScalar d = SkPoint::Distance(pts[0], pts[3]);
    134         SkASSERT(d >= 0);
    135         if (!SkScalarNearlyZero(d)) {
    136             distance += d;
    137             Segment* seg = fSegments.append();
    138             seg->fDistance = distance;
    139             seg->fPtIndex = ptIndex;
    140             seg->fType = kCubic_SegType;
    141             seg->fTValue = maxt;
    142         }
    143     }
    144     return distance;
    145 }
    146 
    147 void SkPathMeasure::buildSegments() {
    148     SkPoint         pts[4];
    149     int             ptIndex = fFirstPtIndex;
    150     SkScalar        d, distance = 0;
    151     bool            isClosed = fForceClosed;
    152     bool            firstMoveTo = ptIndex < 0;
    153     Segment*        seg;
    154 
    155     fSegments.reset();
    156     bool done = false;
    157     do {
    158         switch (fIter.next(pts)) {
    159             case SkPath::kMove_Verb:
    160                 ptIndex += 1;
    161                 fPts.append(1, pts);
    162                 if (!firstMoveTo) {
    163                     done = true;
    164                     break;
    165                 }
    166                 firstMoveTo = false;
    167                 break;
    168 
    169             case SkPath::kLine_Verb:
    170                 d = SkPoint::Distance(pts[0], pts[1]);
    171                 SkASSERT(d >= 0);
    172                 distance += d;
    173                 seg = fSegments.append();
    174                 seg->fDistance = distance;
    175                 seg->fPtIndex = ptIndex;
    176                 seg->fType = kLine_SegType;
    177                 seg->fTValue = kMaxTValue;
    178                 fPts.append(1, pts + 1);
    179                 ptIndex++;
    180                 break;
    181 
    182             case SkPath::kQuad_Verb:
    183                 distance = this->compute_quad_segs(pts, distance, 0,
    184                                                    kMaxTValue, ptIndex);
    185                 fPts.append(2, pts + 1);
    186                 ptIndex += 2;
    187                 break;
    188 
    189             case SkPath::kCubic_Verb:
    190                 distance = this->compute_cubic_segs(pts, distance, 0,
    191                                                     kMaxTValue, ptIndex);
    192                 fPts.append(3, pts + 1);
    193                 ptIndex += 3;
    194                 break;
    195 
    196             case SkPath::kClose_Verb:
    197                 isClosed = true;
    198                 break;
    199 
    200             case SkPath::kDone_Verb:
    201                 done = true;
    202                 break;
    203         }
    204     } while (!done);
    205 
    206     fLength = distance;
    207     fIsClosed = isClosed;
    208     fFirstPtIndex = ptIndex;
    209 
    210 #ifdef SK_DEBUG
    211     {
    212         const Segment* seg = fSegments.begin();
    213         const Segment* stop = fSegments.end();
    214         unsigned        ptIndex = 0;
    215         SkScalar        distance = 0;
    216 
    217         while (seg < stop) {
    218             SkASSERT(seg->fDistance > distance);
    219             SkASSERT(seg->fPtIndex >= ptIndex);
    220             SkASSERT(seg->fTValue > 0);
    221 
    222             const Segment* s = seg;
    223             while (s < stop - 1 && s[0].fPtIndex == s[1].fPtIndex) {
    224                 SkASSERT(s[0].fType == s[1].fType);
    225                 SkASSERT(s[0].fTValue < s[1].fTValue);
    226                 s += 1;
    227             }
    228 
    229             distance = seg->fDistance;
    230             ptIndex = seg->fPtIndex;
    231             seg += 1;
    232         }
    233     //  SkDebugf("\n");
    234     }
    235 #endif
    236 }
    237 
    238 static void compute_pos_tan(const SkTDArray<SkPoint>& segmentPts, int ptIndex,
    239                     int segType, SkScalar t, SkPoint* pos, SkVector* tangent) {
    240     const SkPoint*  pts = &segmentPts[ptIndex];
    241 
    242     switch (segType) {
    243         case kLine_SegType:
    244             if (pos) {
    245                 pos->set(SkScalarInterp(pts[0].fX, pts[1].fX, t),
    246                         SkScalarInterp(pts[0].fY, pts[1].fY, t));
    247             }
    248             if (tangent) {
    249                 tangent->setNormalize(pts[1].fX - pts[0].fX, pts[1].fY - pts[0].fY);
    250             }
    251             break;
    252         case kQuad_SegType:
    253             SkEvalQuadAt(pts, t, pos, tangent);
    254             if (tangent) {
    255                 tangent->normalize();
    256             }
    257             break;
    258         case kCubic_SegType:
    259             SkEvalCubicAt(pts, t, pos, tangent, NULL);
    260             if (tangent) {
    261                 tangent->normalize();
    262             }
    263             break;
    264         default:
    265             SkDEBUGFAIL("unknown segType");
    266     }
    267 }
    268 
    269 static void seg_to(const SkTDArray<SkPoint>& segmentPts, int ptIndex,
    270                    int segType, SkScalar startT, SkScalar stopT, SkPath* dst) {
    271     SkASSERT(startT >= 0 && startT <= SK_Scalar1);
    272     SkASSERT(stopT >= 0 && stopT <= SK_Scalar1);
    273     SkASSERT(startT <= stopT);
    274 
    275     if (SkScalarNearlyZero(stopT - startT)) {
    276         return;
    277     }
    278 
    279     const SkPoint*  pts = &segmentPts[ptIndex];
    280     SkPoint         tmp0[7], tmp1[7];
    281 
    282     switch (segType) {
    283         case kLine_SegType:
    284             if (stopT == kMaxTValue) {
    285                 dst->lineTo(pts[1]);
    286             } else {
    287                 dst->lineTo(SkScalarInterp(pts[0].fX, pts[1].fX, stopT),
    288                             SkScalarInterp(pts[0].fY, pts[1].fY, stopT));
    289             }
    290             break;
    291         case kQuad_SegType:
    292             if (startT == 0) {
    293                 if (stopT == SK_Scalar1) {
    294                     dst->quadTo(pts[1], pts[2]);
    295                 } else {
    296                     SkChopQuadAt(pts, tmp0, stopT);
    297                     dst->quadTo(tmp0[1], tmp0[2]);
    298                 }
    299             } else {
    300                 SkChopQuadAt(pts, tmp0, startT);
    301                 if (stopT == SK_Scalar1) {
    302                     dst->quadTo(tmp0[3], tmp0[4]);
    303                 } else {
    304                     SkChopQuadAt(&tmp0[2], tmp1, SkScalarDiv(stopT - startT,
    305                                                          SK_Scalar1 - startT));
    306                     dst->quadTo(tmp1[1], tmp1[2]);
    307                 }
    308             }
    309             break;
    310         case kCubic_SegType:
    311             if (startT == 0) {
    312                 if (stopT == SK_Scalar1) {
    313                     dst->cubicTo(pts[1], pts[2], pts[3]);
    314                 } else {
    315                     SkChopCubicAt(pts, tmp0, stopT);
    316                     dst->cubicTo(tmp0[1], tmp0[2], tmp0[3]);
    317                 }
    318             } else {
    319                 SkChopCubicAt(pts, tmp0, startT);
    320                 if (stopT == SK_Scalar1) {
    321                     dst->cubicTo(tmp0[4], tmp0[5], tmp0[6]);
    322                 } else {
    323                     SkChopCubicAt(&tmp0[3], tmp1, SkScalarDiv(stopT - startT,
    324                                                         SK_Scalar1 - startT));
    325                     dst->cubicTo(tmp1[1], tmp1[2], tmp1[3]);
    326                 }
    327             }
    328             break;
    329         default:
    330             SkDEBUGFAIL("unknown segType");
    331             sk_throw();
    332     }
    333 }
    334 
    335 ////////////////////////////////////////////////////////////////////////////////
    336 ////////////////////////////////////////////////////////////////////////////////
    337 
    338 SkPathMeasure::SkPathMeasure() {
    339     fPath = NULL;
    340     fLength = -1;   // signal we need to compute it
    341     fForceClosed = false;
    342     fFirstPtIndex = -1;
    343 }
    344 
    345 SkPathMeasure::SkPathMeasure(const SkPath& path, bool forceClosed) {
    346     fPath = &path;
    347     fLength = -1;   // signal we need to compute it
    348     fForceClosed = forceClosed;
    349     fFirstPtIndex = -1;
    350 
    351     fIter.setPath(path, forceClosed);
    352 }
    353 
    354 SkPathMeasure::~SkPathMeasure() {}
    355 
    356 /** Assign a new path, or null to have none.
    357 */
    358 void SkPathMeasure::setPath(const SkPath* path, bool forceClosed) {
    359     fPath = path;
    360     fLength = -1;   // signal we need to compute it
    361     fForceClosed = forceClosed;
    362     fFirstPtIndex = -1;
    363 
    364     if (path) {
    365         fIter.setPath(*path, forceClosed);
    366     }
    367     fSegments.reset();
    368     fPts.reset();
    369 }
    370 
    371 SkScalar SkPathMeasure::getLength() {
    372     if (fPath == NULL) {
    373         return 0;
    374     }
    375     if (fLength < 0) {
    376         this->buildSegments();
    377     }
    378     SkASSERT(fLength >= 0);
    379     return fLength;
    380 }
    381 
    382 const SkPathMeasure::Segment* SkPathMeasure::distanceToSegment(
    383                                             SkScalar distance, SkScalar* t) {
    384     SkDEBUGCODE(SkScalar length = ) this->getLength();
    385     SkASSERT(distance >= 0 && distance <= length);
    386 
    387     const Segment*  seg = fSegments.begin();
    388     int             count = fSegments.count();
    389 
    390     int index = SkTSearch<SkScalar>(&seg->fDistance, count, distance,
    391                                     sizeof(Segment));
    392     // don't care if we hit an exact match or not, so we xor index if it is negative
    393     index ^= (index >> 31);
    394     seg = &seg[index];
    395 
    396     // now interpolate t-values with the prev segment (if possible)
    397     SkScalar    startT = 0, startD = 0;
    398     // check if the prev segment is legal, and references the same set of points
    399     if (index > 0) {
    400         startD = seg[-1].fDistance;
    401         if (seg[-1].fPtIndex == seg->fPtIndex) {
    402             SkASSERT(seg[-1].fType == seg->fType);
    403             startT = seg[-1].getScalarT();
    404         }
    405     }
    406 
    407     SkASSERT(seg->getScalarT() > startT);
    408     SkASSERT(distance >= startD);
    409     SkASSERT(seg->fDistance > startD);
    410 
    411     *t = startT + SkScalarMulDiv(seg->getScalarT() - startT,
    412                                  distance - startD,
    413                                  seg->fDistance - startD);
    414     return seg;
    415 }
    416 
    417 bool SkPathMeasure::getPosTan(SkScalar distance, SkPoint* pos,
    418                               SkVector* tangent) {
    419     SkASSERT(fPath);
    420     if (fPath == NULL) {
    421         return false;
    422     }
    423 
    424     SkScalar    length = this->getLength(); // call this to force computing it
    425     int         count = fSegments.count();
    426 
    427     if (count == 0 || length == 0) {
    428         return false;
    429     }
    430 
    431     // pin the distance to a legal range
    432     if (distance < 0) {
    433         distance = 0;
    434     } else if (distance > length) {
    435         distance = length;
    436     }
    437 
    438     SkScalar        t;
    439     const Segment*  seg = this->distanceToSegment(distance, &t);
    440 
    441     compute_pos_tan(fPts, seg->fPtIndex, seg->fType, t, pos, tangent);
    442     return true;
    443 }
    444 
    445 bool SkPathMeasure::getMatrix(SkScalar distance, SkMatrix* matrix,
    446                               MatrixFlags flags) {
    447     SkPoint     position;
    448     SkVector    tangent;
    449 
    450     if (this->getPosTan(distance, &position, &tangent)) {
    451         if (matrix) {
    452             if (flags & kGetTangent_MatrixFlag) {
    453                 matrix->setSinCos(tangent.fY, tangent.fX, 0, 0);
    454             } else {
    455                 matrix->reset();
    456             }
    457             if (flags & kGetPosition_MatrixFlag) {
    458                 matrix->postTranslate(position.fX, position.fY);
    459             }
    460         }
    461         return true;
    462     }
    463     return false;
    464 }
    465 
    466 bool SkPathMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst,
    467                                bool startWithMoveTo) {
    468     SkASSERT(dst);
    469 
    470     SkScalar length = this->getLength();    // ensure we have built our segments
    471 
    472     if (startD < 0) {
    473         startD = 0;
    474     }
    475     if (stopD > length) {
    476         stopD = length;
    477     }
    478     if (startD >= stopD) {
    479         return false;
    480     }
    481 
    482     SkPoint  p;
    483     SkScalar startT, stopT;
    484     const Segment* seg = this->distanceToSegment(startD, &startT);
    485     const Segment* stopSeg = this->distanceToSegment(stopD, &stopT);
    486     SkASSERT(seg <= stopSeg);
    487 
    488     if (startWithMoveTo) {
    489         compute_pos_tan(fPts, seg->fPtIndex, seg->fType, startT, &p, NULL);
    490         dst->moveTo(p);
    491     }
    492 
    493     if (seg->fPtIndex == stopSeg->fPtIndex) {
    494         seg_to(fPts, seg->fPtIndex, seg->fType, startT, stopT, dst);
    495     } else {
    496         do {
    497             seg_to(fPts, seg->fPtIndex, seg->fType, startT, SK_Scalar1, dst);
    498             seg = SkPathMeasure::NextSegment(seg);
    499             startT = 0;
    500         } while (seg->fPtIndex < stopSeg->fPtIndex);
    501         seg_to(fPts, seg->fPtIndex, seg->fType, 0, stopT, dst);
    502     }
    503     return true;
    504 }
    505 
    506 bool SkPathMeasure::isClosed() {
    507     (void)this->getLength();
    508     return fIsClosed;
    509 }
    510 
    511 /** Move to the next contour in the path. Return true if one exists, or false if
    512     we're done with the path.
    513 */
    514 bool SkPathMeasure::nextContour() {
    515     fLength = -1;
    516     return this->getLength() > 0;
    517 }
    518 
    519 ///////////////////////////////////////////////////////////////////////////////
    520 ///////////////////////////////////////////////////////////////////////////////
    521 
    522 #ifdef SK_DEBUG
    523 
    524 void SkPathMeasure::dump() {
    525     SkDebugf("pathmeas: length=%g, segs=%d\n", fLength, fSegments.count());
    526 
    527     for (int i = 0; i < fSegments.count(); i++) {
    528         const Segment* seg = &fSegments[i];
    529         SkDebugf("pathmeas: seg[%d] distance=%g, point=%d, t=%g, type=%d\n",
    530                 i, seg->fDistance, seg->fPtIndex, seg->getScalarT(),
    531                  seg->fType);
    532     }
    533 }
    534 
    535 #endif
    536