LCOV - app.info - gfx/skia/src/core/SkStroke.cpp

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-07-07 | 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