LCOV - code coverage report
Current view: directory - gfx/skia/src/core - SkStroke.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 285 0 0.0 %
Date: 2012-06-02 Functions: 28 0 0.0 %

       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 "SkStrokerPriv.h"
      11                 : #include "SkGeometry.h"
      12                 : #include "SkPath.h"
      13                 : 
      14                 : #define kMaxQuadSubdivide   5
      15                 : #define kMaxCubicSubdivide  4
      16                 : 
      17               0 : static inline bool degenerate_vector(const SkVector& v) {
      18               0 :     return !SkPoint::CanNormalize(v.fX, v.fY);
      19                 : }
      20                 : 
      21               0 : static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) {
      22                 :     /*  root2/2 is a 45-degree angle
      23                 :         make this constant bigger for more subdivisions (but not >= 1)
      24                 :     */
      25                 :     static const SkScalar kFlatEnoughNormalDotProd =
      26                 :                                             SK_ScalarSqrt2/2 + SK_Scalar1/10;
      27                 : 
      28                 :     SkASSERT(kFlatEnoughNormalDotProd > 0 &&
      29                 :              kFlatEnoughNormalDotProd < SK_Scalar1);
      30                 : 
      31               0 :     return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd;
      32                 : }
      33                 : 
      34               0 : static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) {
      35                 :     static const SkScalar kTooPinchyNormalDotProd = -SK_Scalar1 * 999 / 1000;
      36                 : 
      37               0 :     return SkPoint::DotProduct(norm0, norm1) <= kTooPinchyNormalDotProd;
      38                 : }
      39                 : 
      40               0 : static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after,
      41                 :                                   SkScalar radius,
      42                 :                                   SkVector* normal, SkVector* unitNormal) {
      43               0 :     if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) {
      44               0 :         return false;
      45                 :     }
      46               0 :     unitNormal->rotateCCW();
      47               0 :     unitNormal->scale(radius, normal);
      48               0 :     return true;
      49                 : }
      50                 : 
      51               0 : static bool set_normal_unitnormal(const SkVector& vec,
      52                 :                                   SkScalar radius,
      53                 :                                   SkVector* normal, SkVector* unitNormal) {
      54               0 :     if (!unitNormal->setNormalize(vec.fX, vec.fY)) {
      55               0 :         return false;
      56                 :     }
      57               0 :     unitNormal->rotateCCW();
      58               0 :     unitNormal->scale(radius, normal);
      59               0 :     return true;
      60                 : }
      61                 : 
      62                 : ///////////////////////////////////////////////////////////////////////////////
      63                 : 
      64               0 : class SkPathStroker {
      65                 : public:
      66                 :     SkPathStroker(SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap,
      67                 :                   SkPaint::Join join);
      68                 : 
      69                 :     void moveTo(const SkPoint&);
      70                 :     void lineTo(const SkPoint&);
      71                 :     void quadTo(const SkPoint&, const SkPoint&);
      72                 :     void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&);
      73               0 :     void close(bool isLine) { this->finishContour(true, isLine); }
      74                 : 
      75               0 :     void done(SkPath* dst, bool isLine) {
      76               0 :         this->finishContour(false, isLine);
      77               0 :         fOuter.addPath(fExtra);
      78               0 :         dst->swap(fOuter);
      79               0 :     }
      80                 : 
      81                 : private:
      82                 :     SkScalar    fRadius;
      83                 :     SkScalar    fInvMiterLimit;
      84                 : 
      85                 :     SkVector    fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal;
      86                 :     SkPoint     fFirstPt, fPrevPt;  // on original path
      87                 :     SkPoint     fFirstOuterPt;
      88                 :     int         fSegmentCount;
      89                 :     bool        fPrevIsLine;
      90                 : 
      91                 :     SkStrokerPriv::CapProc  fCapper;
      92                 :     SkStrokerPriv::JoinProc fJoiner;
      93                 : 
      94                 :     SkPath  fInner, fOuter; // outer is our working answer, inner is temp
      95                 :     SkPath  fExtra;         // added as extra complete contours
      96                 : 
      97                 :     void    finishContour(bool close, bool isLine);
      98                 :     void    preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal,
      99                 :                       bool isLine);
     100                 :     void    postJoinTo(const SkPoint&, const SkVector& normal,
     101                 :                        const SkVector& unitNormal);
     102                 : 
     103                 :     void    line_to(const SkPoint& currPt, const SkVector& normal);
     104                 :     void    quad_to(const SkPoint pts[3],
     105                 :                     const SkVector& normalAB, const SkVector& unitNormalAB,
     106                 :                     SkVector* normalBC, SkVector* unitNormalBC,
     107                 :                     int subDivide);
     108                 :     void    cubic_to(const SkPoint pts[4],
     109                 :                     const SkVector& normalAB, const SkVector& unitNormalAB,
     110                 :                     SkVector* normalCD, SkVector* unitNormalCD,
     111                 :                     int subDivide);
     112                 : };
     113                 : 
     114                 : ///////////////////////////////////////////////////////////////////////////////
     115                 : 
     116               0 : void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal,
     117                 :                               SkVector* unitNormal, bool currIsLine) {
     118               0 :     SkASSERT(fSegmentCount >= 0);
     119                 : 
     120               0 :     SkScalar    prevX = fPrevPt.fX;
     121               0 :     SkScalar    prevY = fPrevPt.fY;
     122                 : 
     123               0 :     SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal,
     124                 :                                          unitNormal));
     125                 : 
     126               0 :     if (fSegmentCount == 0) {
     127               0 :         fFirstNormal = *normal;
     128               0 :         fFirstUnitNormal = *unitNormal;
     129               0 :         fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY);
     130                 : 
     131               0 :         fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY);
     132               0 :         fInner.moveTo(prevX - normal->fX, prevY - normal->fY);
     133                 :     } else {    // we have a previous segment
     134                 :         fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal,
     135               0 :                 fRadius, fInvMiterLimit, fPrevIsLine, currIsLine);
     136                 :     }
     137               0 :     fPrevIsLine = currIsLine;
     138               0 : }
     139                 : 
     140               0 : void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal,
     141                 :                                const SkVector& unitNormal) {
     142               0 :     fPrevPt = currPt;
     143               0 :     fPrevUnitNormal = unitNormal;
     144               0 :     fPrevNormal = normal;
     145               0 :     fSegmentCount += 1;
     146               0 : }
     147                 : 
     148               0 : void SkPathStroker::finishContour(bool close, bool currIsLine) {
     149               0 :     if (fSegmentCount > 0) {
     150                 :         SkPoint pt;
     151                 : 
     152               0 :         if (close) {
     153                 :             fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt,
     154                 :                     fFirstUnitNormal, fRadius, fInvMiterLimit,
     155               0 :                     fPrevIsLine, currIsLine);
     156               0 :             fOuter.close();
     157                 :             // now add fInner as its own contour
     158               0 :             fInner.getLastPt(&pt);
     159               0 :             fOuter.moveTo(pt.fX, pt.fY);
     160               0 :             fOuter.reversePathTo(fInner);
     161               0 :             fOuter.close();
     162                 :         } else {    // add caps to start and end
     163                 :             // cap the end
     164               0 :             fInner.getLastPt(&pt);
     165                 :             fCapper(&fOuter, fPrevPt, fPrevNormal, pt,
     166               0 :                     currIsLine ? &fInner : NULL);
     167               0 :             fOuter.reversePathTo(fInner);
     168                 :             // cap the start
     169               0 :             fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt,
     170               0 :                     fPrevIsLine ? &fInner : NULL);
     171               0 :             fOuter.close();
     172                 :         }
     173                 :     }
     174               0 :     fInner.reset();
     175               0 :     fSegmentCount = -1;
     176               0 : }
     177                 : 
     178                 : ///////////////////////////////////////////////////////////////////////////////
     179                 : 
     180               0 : SkPathStroker::SkPathStroker(SkScalar radius, SkScalar miterLimit,
     181                 :                              SkPaint::Cap cap, SkPaint::Join join)
     182               0 :         : fRadius(radius) {
     183                 : 
     184                 :     /*  This is only used when join is miter_join, but we initialize it here
     185                 :         so that it is always defined, to fis valgrind warnings.
     186                 :     */
     187               0 :     fInvMiterLimit = 0;
     188                 : 
     189               0 :     if (join == SkPaint::kMiter_Join) {
     190               0 :         if (miterLimit <= SK_Scalar1) {
     191               0 :             join = SkPaint::kBevel_Join;
     192                 :         } else {
     193               0 :             fInvMiterLimit = SkScalarInvert(miterLimit);
     194                 :         }
     195                 :     }
     196               0 :     fCapper = SkStrokerPriv::CapFactory(cap);
     197               0 :     fJoiner = SkStrokerPriv::JoinFactory(join);
     198               0 :     fSegmentCount = -1;
     199               0 :     fPrevIsLine = false;
     200               0 : }
     201                 : 
     202               0 : void SkPathStroker::moveTo(const SkPoint& pt) {
     203               0 :     if (fSegmentCount > 0) {
     204               0 :         this->finishContour(false, false);
     205                 :     }
     206               0 :     fSegmentCount = 0;
     207               0 :     fFirstPt = fPrevPt = pt;
     208               0 : }
     209                 : 
     210               0 : void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) {
     211               0 :     fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY);
     212               0 :     fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY);
     213               0 : }
     214                 : 
     215               0 : void SkPathStroker::lineTo(const SkPoint& currPt) {
     216               0 :     if (SkPath::IsLineDegenerate(fPrevPt, currPt)) {
     217               0 :         return;
     218                 :     }
     219                 :     SkVector    normal, unitNormal;
     220                 : 
     221               0 :     this->preJoinTo(currPt, &normal, &unitNormal, true);
     222               0 :     this->line_to(currPt, normal);
     223               0 :     this->postJoinTo(currPt, normal, unitNormal);
     224                 : }
     225                 : 
     226               0 : void SkPathStroker::quad_to(const SkPoint pts[3],
     227                 :                       const SkVector& normalAB, const SkVector& unitNormalAB,
     228                 :                       SkVector* normalBC, SkVector* unitNormalBC,
     229                 :                       int subDivide) {
     230               0 :     if (!set_normal_unitnormal(pts[1], pts[2], fRadius,
     231               0 :                                normalBC, unitNormalBC)) {
     232                 :         // pts[1] nearly equals pts[2], so just draw a line to pts[2]
     233               0 :         this->line_to(pts[2], normalAB);
     234               0 :         *normalBC = normalAB;
     235               0 :         *unitNormalBC = unitNormalAB;
     236               0 :         return;
     237                 :     }
     238                 : 
     239               0 :     if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) {
     240                 :         SkPoint     tmp[5];
     241                 :         SkVector    norm, unit;
     242                 : 
     243               0 :         SkChopQuadAtHalf(pts, tmp);
     244               0 :         this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide);
     245               0 :         this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide);
     246                 :     } else {
     247                 :         SkVector    normalB, unitB;
     248               0 :         SkAssertResult(set_normal_unitnormal(pts[0], pts[2], fRadius,
     249                 :                                              &normalB, &unitB));
     250                 : 
     251               0 :         fOuter.quadTo(  pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
     252               0 :                         pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY);
     253               0 :         fInner.quadTo(  pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
     254               0 :                         pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY);
     255                 :     }
     256                 : }
     257                 : 
     258               0 : void SkPathStroker::cubic_to(const SkPoint pts[4],
     259                 :                       const SkVector& normalAB, const SkVector& unitNormalAB,
     260                 :                       SkVector* normalCD, SkVector* unitNormalCD,
     261                 :                       int subDivide) {
     262               0 :     SkVector    ab = pts[1] - pts[0];
     263               0 :     SkVector    cd = pts[3] - pts[2];
     264                 :     SkVector    normalBC, unitNormalBC;
     265                 : 
     266               0 :     bool    degenerateAB = degenerate_vector(ab);
     267               0 :     bool    degenerateCD = degenerate_vector(cd);
     268                 : 
     269               0 :     if (degenerateAB && degenerateCD) {
     270                 : DRAW_LINE:
     271               0 :         this->line_to(pts[3], normalAB);
     272               0 :         *normalCD = normalAB;
     273               0 :         *unitNormalCD = unitNormalAB;
     274               0 :         return;
     275                 :     }
     276                 : 
     277               0 :     if (degenerateAB) {
     278               0 :         ab = pts[2] - pts[0];
     279               0 :         degenerateAB = degenerate_vector(ab);
     280                 :     }
     281               0 :     if (degenerateCD) {
     282               0 :         cd = pts[3] - pts[1];
     283               0 :         degenerateCD = degenerate_vector(cd);
     284                 :     }
     285               0 :     if (degenerateAB || degenerateCD) {
     286                 :         goto DRAW_LINE;
     287                 :     }
     288               0 :     SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD));
     289               0 :     bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius,
     290               0 :                                                &normalBC, &unitNormalBC);
     291                 : 
     292               0 :     if (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) ||
     293               0 :              normals_too_curvy(unitNormalBC, *unitNormalCD)) {
     294                 :         // subdivide if we can
     295               0 :         if (--subDivide < 0) {
     296               0 :             goto DRAW_LINE;
     297                 :         }
     298                 :         SkPoint     tmp[7];
     299                 :         SkVector    norm, unit, dummy, unitDummy;
     300                 : 
     301               0 :         SkChopCubicAtHalf(pts, tmp);
     302                 :         this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit,
     303               0 :                        subDivide);
     304                 :         // we use dummys since we already have a valid (and more accurate)
     305                 :         // normals for CD
     306               0 :         this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide);
     307                 :     } else {
     308                 :         SkVector    normalB, normalC;
     309                 :         
     310                 :         // need normals to inset/outset the off-curve pts B and C
     311                 : 
     312                 :         if (0) {    // this is normal to the line between our adjacent pts
     313                 :             normalB = pts[2] - pts[0];
     314                 :             normalB.rotateCCW();
     315                 :             SkAssertResult(normalB.setLength(fRadius));
     316                 : 
     317                 :             normalC = pts[3] - pts[1];
     318                 :             normalC.rotateCCW();
     319                 :             SkAssertResult(normalC.setLength(fRadius));
     320                 :         } else {    // miter-join
     321               0 :             SkVector    unitBC = pts[2] - pts[1];
     322               0 :             unitBC.normalize();
     323               0 :             unitBC.rotateCCW();
     324                 : 
     325               0 :             normalB = unitNormalAB + unitBC;
     326               0 :             normalC = *unitNormalCD + unitBC;
     327                 : 
     328               0 :             SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC);
     329               0 :             SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
     330                 :                                         SkScalarSqrt((SK_Scalar1 + dot)/2))));
     331               0 :             dot = SkPoint::DotProduct(*unitNormalCD, unitBC);
     332               0 :             SkAssertResult(normalC.setLength(SkScalarDiv(fRadius,
     333                 :                                         SkScalarSqrt((SK_Scalar1 + dot)/2))));
     334                 :         }
     335                 : 
     336               0 :         fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
     337               0 :                         pts[2].fX + normalC.fX, pts[2].fY + normalC.fY,
     338               0 :                         pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY);
     339                 : 
     340               0 :         fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
     341               0 :                         pts[2].fX - normalC.fX, pts[2].fY - normalC.fY,
     342               0 :                         pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY);
     343                 :     }
     344                 : }
     345                 : 
     346               0 : void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
     347               0 :     bool    degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
     348               0 :     bool    degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
     349                 : 
     350               0 :     if (degenerateAB | degenerateBC) {
     351               0 :         if (degenerateAB ^ degenerateBC) {
     352               0 :             this->lineTo(pt2);
     353                 :         }
     354               0 :         return;
     355                 :     }
     356                 : 
     357                 :     SkVector    normalAB, unitAB, normalBC, unitBC;
     358                 : 
     359               0 :     this->preJoinTo(pt1, &normalAB, &unitAB, false);
     360                 : 
     361                 :     {
     362                 :         SkPoint pts[3], tmp[5];
     363               0 :         pts[0] = fPrevPt;
     364               0 :         pts[1] = pt1;
     365               0 :         pts[2] = pt2;
     366                 : 
     367               0 :         if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) {
     368               0 :             unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY);
     369               0 :             unitBC.rotateCCW();
     370               0 :             if (normals_too_pinchy(unitAB, unitBC)) {
     371               0 :                 normalBC = unitBC;
     372               0 :                 normalBC.scale(fRadius);
     373                 : 
     374               0 :                 fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY);
     375               0 :                 fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY);
     376               0 :                 fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY);
     377                 : 
     378               0 :                 fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY);
     379               0 :                 fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY);
     380               0 :                 fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY);
     381                 : 
     382                 :                 fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius,
     383               0 :                                  SkPath::kCW_Direction);
     384                 :             } else {
     385                 :                 this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC,
     386               0 :                               kMaxQuadSubdivide);
     387               0 :                 SkVector n = normalBC;
     388               0 :                 SkVector u = unitBC;
     389                 :                 this->quad_to(&tmp[2], n, u, &normalBC, &unitBC,
     390               0 :                               kMaxQuadSubdivide);
     391                 :             }
     392                 :         } else {
     393                 :             this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC,
     394               0 :                           kMaxQuadSubdivide);
     395                 :         }
     396                 :     }
     397                 : 
     398               0 :     this->postJoinTo(pt2, normalBC, unitBC);
     399                 : }
     400                 : 
     401               0 : void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2,
     402                 :                             const SkPoint& pt3) {
     403               0 :     bool    degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
     404               0 :     bool    degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
     405               0 :     bool    degenerateCD = SkPath::IsLineDegenerate(pt2, pt3);
     406                 : 
     407               0 :     if (degenerateAB + degenerateBC + degenerateCD >= 2) {
     408               0 :         this->lineTo(pt3);
     409               0 :         return;
     410                 :     }
     411                 : 
     412                 :     SkVector    normalAB, unitAB, normalCD, unitCD;
     413                 : 
     414                 :     // find the first tangent (which might be pt1 or pt2
     415                 :     {
     416               0 :         const SkPoint*  nextPt = &pt1;
     417               0 :         if (degenerateAB)
     418               0 :             nextPt = &pt2;
     419               0 :         this->preJoinTo(*nextPt, &normalAB, &unitAB, false);
     420                 :     }
     421                 : 
     422                 :     {
     423                 :         SkPoint pts[4], tmp[13];
     424                 :         int         i, count;
     425                 :         SkVector    n, u;
     426                 :         SkScalar    tValues[3];
     427                 : 
     428               0 :         pts[0] = fPrevPt;
     429               0 :         pts[1] = pt1;
     430               0 :         pts[2] = pt2;
     431               0 :         pts[3] = pt3;
     432                 : 
     433                 : #if 1
     434               0 :         count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
     435                 : #else
     436                 :         count = 1;
     437                 :         memcpy(tmp, pts, 4 * sizeof(SkPoint));
     438                 : #endif
     439               0 :         n = normalAB;
     440               0 :         u = unitAB;
     441               0 :         for (i = 0; i < count; i++) {
     442               0 :             this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD,
     443               0 :                            kMaxCubicSubdivide);
     444               0 :             if (i == count - 1) {
     445               0 :                 break;
     446                 :             }
     447               0 :             n = normalCD;
     448               0 :             u = unitCD;
     449                 : 
     450                 :         }
     451                 : 
     452                 :         // check for too pinchy
     453               0 :         for (i = 1; i < count; i++) {
     454                 :             SkPoint p;
     455                 :             SkVector    v, c;
     456                 : 
     457               0 :             SkEvalCubicAt(pts, tValues[i - 1], &p, &v, &c);
     458                 : 
     459               0 :             SkScalar    dot = SkPoint::DotProduct(c, c);
     460               0 :             v.scale(SkScalarInvert(dot));
     461                 : 
     462               0 :             if (SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY)) {
     463               0 :                 fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction);
     464                 :             }
     465                 :         }
     466                 : 
     467                 :     }
     468                 : 
     469               0 :     this->postJoinTo(pt3, normalCD, unitCD);
     470                 : }
     471                 : 
     472                 : ///////////////////////////////////////////////////////////////////////////////
     473                 : ///////////////////////////////////////////////////////////////////////////////
     474                 : 
     475                 : #include "SkPaint.h"
     476                 : 
     477               0 : SkStroke::SkStroke() {
     478               0 :     fWidth      = SK_DefaultStrokeWidth;
     479               0 :     fMiterLimit = SK_DefaultMiterLimit;
     480               0 :     fCap        = SkPaint::kDefault_Cap;
     481               0 :     fJoin       = SkPaint::kDefault_Join;
     482               0 :     fDoFill     = false;
     483               0 : }
     484                 : 
     485               0 : SkStroke::SkStroke(const SkPaint& p) {
     486               0 :     fWidth      = p.getStrokeWidth();
     487               0 :     fMiterLimit = p.getStrokeMiter();
     488               0 :     fCap        = (uint8_t)p.getStrokeCap();
     489               0 :     fJoin       = (uint8_t)p.getStrokeJoin();
     490               0 :     fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
     491               0 : }
     492                 : 
     493               0 : SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
     494               0 :     fWidth      = width;
     495               0 :     fMiterLimit = p.getStrokeMiter();
     496               0 :     fCap        = (uint8_t)p.getStrokeCap();
     497               0 :     fJoin       = (uint8_t)p.getStrokeJoin();
     498               0 :     fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
     499               0 : }
     500                 : 
     501               0 : void SkStroke::setWidth(SkScalar width) {
     502               0 :     SkASSERT(width >= 0);
     503               0 :     fWidth = width;
     504               0 : }
     505                 : 
     506               0 : void SkStroke::setMiterLimit(SkScalar miterLimit) {
     507               0 :     SkASSERT(miterLimit >= 0);
     508               0 :     fMiterLimit = miterLimit;
     509               0 : }
     510                 : 
     511               0 : void SkStroke::setCap(SkPaint::Cap cap) {
     512               0 :     SkASSERT((unsigned)cap < SkPaint::kCapCount);
     513               0 :     fCap = SkToU8(cap);
     514               0 : }
     515                 : 
     516               0 : void SkStroke::setJoin(SkPaint::Join join) {
     517               0 :     SkASSERT((unsigned)join < SkPaint::kJoinCount);
     518               0 :     fJoin = SkToU8(join);
     519               0 : }
     520                 : 
     521                 : ///////////////////////////////////////////////////////////////////////////////
     522                 : 
     523                 : #ifdef SK_SCALAR_IS_FIXED
     524                 :     /*  return non-zero if the path is too big, and should be shrunk to avoid
     525                 :         overflows during intermediate calculations. Note that we compute the
     526                 :         bounds for this. If we had a custom callback/walker for paths, we could
     527                 :         perhaps go faster by using that, and just perform the abs | in that
     528                 :         routine
     529                 :     */
     530                 :     static int needs_to_shrink(const SkPath& path) {
     531                 :         const SkRect& r = path.getBounds();
     532                 :         SkFixed mask = SkAbs32(r.fLeft);
     533                 :         mask |= SkAbs32(r.fTop);
     534                 :         mask |= SkAbs32(r.fRight);
     535                 :         mask |= SkAbs32(r.fBottom);
     536                 :         // we need the top 3 bits clear (after abs) to avoid overflow
     537                 :         return mask >> 29;
     538                 :     }
     539                 : 
     540                 :     static void identity_proc(SkPoint pts[], int count) {}
     541                 :     static void shift_down_2_proc(SkPoint pts[], int count) {
     542                 :         for (int i = 0; i < count; i++) {
     543                 :             pts->fX >>= 2;
     544                 :             pts->fY >>= 2;
     545                 :             pts += 1;
     546                 :         }
     547                 :     }
     548                 :     #define APPLY_PROC(proc, pts, count)    proc(pts, count)
     549                 : #else   // float does need any of this
     550                 :     #define APPLY_PROC(proc, pts, count)
     551                 : #endif
     552                 : 
     553               0 : void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
     554               0 :     SkASSERT(&src != NULL && dst != NULL);
     555                 : 
     556               0 :     SkScalar radius = SkScalarHalf(fWidth);
     557                 : 
     558               0 :     dst->reset();
     559               0 :     if (radius <= 0) {
     560               0 :         return;
     561                 :     }
     562                 :     
     563                 : #ifdef SK_SCALAR_IS_FIXED
     564                 :     void (*proc)(SkPoint pts[], int count) = identity_proc;
     565                 :     if (needs_to_shrink(src)) {
     566                 :         proc = shift_down_2_proc;
     567                 :         radius >>= 2;
     568                 :         if (radius == 0) {
     569                 :             return;
     570                 :         }
     571                 :     }
     572                 : #endif
     573                 : 
     574                 :     SkPathStroker   stroker(radius, fMiterLimit, this->getCap(),
     575               0 :                             this->getJoin());
     576                 : 
     577               0 :     SkPath::Iter    iter(src, false);
     578                 :     SkPoint         pts[4];
     579               0 :     SkPath::Verb    verb, lastSegment = SkPath::kMove_Verb;
     580                 : 
     581               0 :     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
     582               0 :         switch (verb) {
     583                 :             case SkPath::kMove_Verb:
     584                 :                 APPLY_PROC(proc, &pts[0], 1);
     585               0 :                 stroker.moveTo(pts[0]);
     586               0 :                 break;
     587                 :             case SkPath::kLine_Verb:
     588                 :                 APPLY_PROC(proc, &pts[1], 1);
     589               0 :                 stroker.lineTo(pts[1]);
     590               0 :                 lastSegment = verb;
     591               0 :                 break;
     592                 :             case SkPath::kQuad_Verb:
     593                 :                 APPLY_PROC(proc, &pts[1], 2);
     594               0 :                 stroker.quadTo(pts[1], pts[2]);
     595               0 :                 lastSegment = verb;
     596               0 :                 break;
     597                 :             case SkPath::kCubic_Verb:
     598                 :                 APPLY_PROC(proc, &pts[1], 3);
     599               0 :                 stroker.cubicTo(pts[1], pts[2], pts[3]);
     600               0 :                 lastSegment = verb;
     601               0 :                 break;
     602                 :             case SkPath::kClose_Verb:
     603               0 :                 stroker.close(lastSegment == SkPath::kLine_Verb);
     604               0 :                 break;
     605                 :             default:
     606               0 :                 break;
     607                 :         }
     608                 :     }
     609               0 :     stroker.done(dst, lastSegment == SkPath::kLine_Verb);
     610                 : 
     611                 : #ifdef SK_SCALAR_IS_FIXED
     612                 :     // undo our previous down_shift
     613                 :     if (shift_down_2_proc == proc) {
     614                 :         // need a real shift methid on path. antialias paths could use this too
     615                 :         SkMatrix matrix;
     616                 :         matrix.setScale(SkIntToScalar(4), SkIntToScalar(4));
     617                 :         dst->transform(matrix);
     618                 :     }
     619                 : #endif
     620                 : 
     621               0 :     if (fDoFill) {
     622               0 :         dst->addPath(src);
     623                 :     } else {
     624                 :         //  Seems like we can assume that a 2-point src would always result in
     625                 :         //  a convex stroke, but testing has proved otherwise.
     626                 :         //  TODO: fix the stroker to make this assumption true (without making
     627                 :         //  it slower that the work that will be done in computeConvexity())
     628                 : #if 0
     629                 :         // this test results in a non-convex stroke :(
     630                 :         static void test(SkCanvas* canvas) {
     631                 :             SkPoint pts[] = { 146.333328,  192.333328, 300.333344, 293.333344 };
     632                 :             SkPaint paint;
     633                 :             paint.setStrokeWidth(7);
     634                 :             paint.setStrokeCap(SkPaint::kRound_Cap);
     635                 :             canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
     636                 :         }        
     637                 : #endif
     638                 : #if 0
     639                 :         if (2 == src.countPoints()) {
     640                 :             dst->setIsConvex(true);
     641                 :         }
     642                 : #endif
     643                 :     }
     644                 : 
     645                 :     // our answer should preserve the inverseness of the src
     646               0 :     if (src.isInverseFillType()) {
     647               0 :         SkASSERT(!dst->isInverseFillType());
     648               0 :         dst->toggleInverseFillType();
     649                 :     }
     650                 : }
     651                 : 
     652               0 : void SkStroke::strokeLine(const SkPoint& p0, const SkPoint& p1,
     653                 :                           SkPath* dst) const {
     654               0 :     SkPath  tmp;
     655                 : 
     656               0 :     tmp.moveTo(p0);
     657               0 :     tmp.lineTo(p1);
     658               0 :     this->strokePath(tmp, dst);
     659               0 : }
     660                 : 

Generated by: LCOV version 1.7