gfx/skia/trunk/src/core/SkStroke.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /*
     2  * Copyright 2008 The Android Open Source Project
     3  *
     4  * Use of this source code is governed by a BSD-style license that can be
     5  * found in the LICENSE file.
     6  */
     8 #include "SkStrokerPriv.h"
     9 #include "SkGeometry.h"
    10 #include "SkPath.h"
    12 #define kMaxQuadSubdivide   5
    13 #define kMaxCubicSubdivide  7
    15 static inline bool degenerate_vector(const SkVector& v) {
    16     return !SkPoint::CanNormalize(v.fX, v.fY);
    17 }
    19 static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) {
    20     /*  root2/2 is a 45-degree angle
    21         make this constant bigger for more subdivisions (but not >= 1)
    22     */
    23     static const SkScalar kFlatEnoughNormalDotProd =
    24                                             SK_ScalarSqrt2/2 + SK_Scalar1/10;
    26     SkASSERT(kFlatEnoughNormalDotProd > 0 &&
    27              kFlatEnoughNormalDotProd < SK_Scalar1);
    29     return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd;
    30 }
    32 static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) {
    33     // if the dot-product is -1, then we are definitely too pinchy. We tweak
    34     // that by an epsilon to ensure we have significant bits in our test
    35     static const int kMinSigBitsForDot = 8;
    36     static const SkScalar kDotEpsilon = FLT_EPSILON * (1 << kMinSigBitsForDot);
    37     static const SkScalar kTooPinchyNormalDotProd = kDotEpsilon - 1;
    39     // just some sanity asserts to help document the expected range
    40     SkASSERT(kTooPinchyNormalDotProd >= -1);
    41     SkASSERT(kTooPinchyNormalDotProd < SkDoubleToScalar(-0.999));
    43     SkScalar dot = SkPoint::DotProduct(norm0, norm1);
    44     return dot <= kTooPinchyNormalDotProd;
    45 }
    47 static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after,
    48                                   SkScalar radius,
    49                                   SkVector* normal, SkVector* unitNormal) {
    50     if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) {
    51         return false;
    52     }
    53     unitNormal->rotateCCW();
    54     unitNormal->scale(radius, normal);
    55     return true;
    56 }
    58 static bool set_normal_unitnormal(const SkVector& vec,
    59                                   SkScalar radius,
    60                                   SkVector* normal, SkVector* unitNormal) {
    61     if (!unitNormal->setNormalize(vec.fX, vec.fY)) {
    62         return false;
    63     }
    64     unitNormal->rotateCCW();
    65     unitNormal->scale(radius, normal);
    66     return true;
    67 }
    69 ///////////////////////////////////////////////////////////////////////////////
    71 class SkPathStroker {
    72 public:
    73     SkPathStroker(const SkPath& src,
    74                   SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap,
    75                   SkPaint::Join join);
    77     void moveTo(const SkPoint&);
    78     void lineTo(const SkPoint&);
    79     void quadTo(const SkPoint&, const SkPoint&);
    80     void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&);
    81     void close(bool isLine) { this->finishContour(true, isLine); }
    83     void done(SkPath* dst, bool isLine) {
    84         this->finishContour(false, isLine);
    85         fOuter.addPath(fExtra);
    86         dst->swap(fOuter);
    87     }
    89 private:
    90     SkScalar    fRadius;
    91     SkScalar    fInvMiterLimit;
    93     SkVector    fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal;
    94     SkPoint     fFirstPt, fPrevPt;  // on original path
    95     SkPoint     fFirstOuterPt;
    96     int         fSegmentCount;
    97     bool        fPrevIsLine;
    99     SkStrokerPriv::CapProc  fCapper;
   100     SkStrokerPriv::JoinProc fJoiner;
   102     SkPath  fInner, fOuter; // outer is our working answer, inner is temp
   103     SkPath  fExtra;         // added as extra complete contours
   105     void    finishContour(bool close, bool isLine);
   106     void    preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal,
   107                       bool isLine);
   108     void    postJoinTo(const SkPoint&, const SkVector& normal,
   109                        const SkVector& unitNormal);
   111     void    line_to(const SkPoint& currPt, const SkVector& normal);
   112     void    quad_to(const SkPoint pts[3],
   113                     const SkVector& normalAB, const SkVector& unitNormalAB,
   114                     SkVector* normalBC, SkVector* unitNormalBC,
   115                     int subDivide);
   116     void    cubic_to(const SkPoint pts[4],
   117                     const SkVector& normalAB, const SkVector& unitNormalAB,
   118                     SkVector* normalCD, SkVector* unitNormalCD,
   119                     int subDivide);
   120 };
   122 ///////////////////////////////////////////////////////////////////////////////
   124 void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal,
   125                               SkVector* unitNormal, bool currIsLine) {
   126     SkASSERT(fSegmentCount >= 0);
   128     SkScalar    prevX = fPrevPt.fX;
   129     SkScalar    prevY = fPrevPt.fY;
   131     SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal,
   132                                          unitNormal));
   134     if (fSegmentCount == 0) {
   135         fFirstNormal = *normal;
   136         fFirstUnitNormal = *unitNormal;
   137         fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY);
   139         fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY);
   140         fInner.moveTo(prevX - normal->fX, prevY - normal->fY);
   141     } else {    // we have a previous segment
   142         fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal,
   143                 fRadius, fInvMiterLimit, fPrevIsLine, currIsLine);
   144     }
   145     fPrevIsLine = currIsLine;
   146 }
   148 void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal,
   149                                const SkVector& unitNormal) {
   150     fPrevPt = currPt;
   151     fPrevUnitNormal = unitNormal;
   152     fPrevNormal = normal;
   153     fSegmentCount += 1;
   154 }
   156 void SkPathStroker::finishContour(bool close, bool currIsLine) {
   157     if (fSegmentCount > 0) {
   158         SkPoint pt;
   160         if (close) {
   161             fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt,
   162                     fFirstUnitNormal, fRadius, fInvMiterLimit,
   163                     fPrevIsLine, currIsLine);
   164             fOuter.close();
   165             // now add fInner as its own contour
   166             fInner.getLastPt(&pt);
   167             fOuter.moveTo(pt.fX, pt.fY);
   168             fOuter.reversePathTo(fInner);
   169             fOuter.close();
   170         } else {    // add caps to start and end
   171             // cap the end
   172             fInner.getLastPt(&pt);
   173             fCapper(&fOuter, fPrevPt, fPrevNormal, pt,
   174                     currIsLine ? &fInner : NULL);
   175             fOuter.reversePathTo(fInner);
   176             // cap the start
   177             fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt,
   178                     fPrevIsLine ? &fInner : NULL);
   179             fOuter.close();
   180         }
   181     }
   182     // since we may re-use fInner, we rewind instead of reset, to save on
   183     // reallocating its internal storage.
   184     fInner.rewind();
   185     fSegmentCount = -1;
   186 }
   188 ///////////////////////////////////////////////////////////////////////////////
   190 SkPathStroker::SkPathStroker(const SkPath& src,
   191                              SkScalar radius, SkScalar miterLimit,
   192                              SkPaint::Cap cap, SkPaint::Join join)
   193         : fRadius(radius) {
   195     /*  This is only used when join is miter_join, but we initialize it here
   196         so that it is always defined, to fis valgrind warnings.
   197     */
   198     fInvMiterLimit = 0;
   200     if (join == SkPaint::kMiter_Join) {
   201         if (miterLimit <= SK_Scalar1) {
   202             join = SkPaint::kBevel_Join;
   203         } else {
   204             fInvMiterLimit = SkScalarInvert(miterLimit);
   205         }
   206     }
   207     fCapper = SkStrokerPriv::CapFactory(cap);
   208     fJoiner = SkStrokerPriv::JoinFactory(join);
   209     fSegmentCount = -1;
   210     fPrevIsLine = false;
   212     // Need some estimate of how large our final result (fOuter)
   213     // and our per-contour temp (fInner) will be, so we don't spend
   214     // extra time repeatedly growing these arrays.
   215     //
   216     // 3x for result == inner + outer + join (swag)
   217     // 1x for inner == 'wag' (worst contour length would be better guess)
   218     fOuter.incReserve(src.countPoints() * 3);
   219     fInner.incReserve(src.countPoints());
   220 }
   222 void SkPathStroker::moveTo(const SkPoint& pt) {
   223     if (fSegmentCount > 0) {
   224         this->finishContour(false, false);
   225     }
   226     fSegmentCount = 0;
   227     fFirstPt = fPrevPt = pt;
   228 }
   230 void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) {
   231     fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY);
   232     fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY);
   233 }
   235 void SkPathStroker::lineTo(const SkPoint& currPt) {
   236     if (SkPath::IsLineDegenerate(fPrevPt, currPt)) {
   237         return;
   238     }
   239     SkVector    normal, unitNormal;
   241     this->preJoinTo(currPt, &normal, &unitNormal, true);
   242     this->line_to(currPt, normal);
   243     this->postJoinTo(currPt, normal, unitNormal);
   244 }
   246 void SkPathStroker::quad_to(const SkPoint pts[3],
   247                       const SkVector& normalAB, const SkVector& unitNormalAB,
   248                       SkVector* normalBC, SkVector* unitNormalBC,
   249                       int subDivide) {
   250     if (!set_normal_unitnormal(pts[1], pts[2], fRadius,
   251                                normalBC, unitNormalBC)) {
   252         // pts[1] nearly equals pts[2], so just draw a line to pts[2]
   253         this->line_to(pts[2], normalAB);
   254         *normalBC = normalAB;
   255         *unitNormalBC = unitNormalAB;
   256         return;
   257     }
   259     if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) {
   260         SkPoint     tmp[5];
   261         SkVector    norm, unit;
   263         SkChopQuadAtHalf(pts, tmp);
   264         this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide);
   265         this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide);
   266     } else {
   267         SkVector    normalB;
   269         normalB = pts[2] - pts[0];
   270         normalB.rotateCCW();
   271         SkScalar dot = SkPoint::DotProduct(unitNormalAB, *unitNormalBC);
   272         SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
   273                                      SkScalarSqrt((SK_Scalar1 + dot)/2))));
   275         fOuter.quadTo(  pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
   276                         pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY);
   277         fInner.quadTo(  pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
   278                         pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY);
   279     }
   280 }
   282 void SkPathStroker::cubic_to(const SkPoint pts[4],
   283                       const SkVector& normalAB, const SkVector& unitNormalAB,
   284                       SkVector* normalCD, SkVector* unitNormalCD,
   285                       int subDivide) {
   286     SkVector    ab = pts[1] - pts[0];
   287     SkVector    cd = pts[3] - pts[2];
   288     SkVector    normalBC, unitNormalBC;
   290     bool    degenerateAB = degenerate_vector(ab);
   291     bool    degenerateCD = degenerate_vector(cd);
   293     if (degenerateAB && degenerateCD) {
   294 DRAW_LINE:
   295         this->line_to(pts[3], normalAB);
   296         *normalCD = normalAB;
   297         *unitNormalCD = unitNormalAB;
   298         return;
   299     }
   301     if (degenerateAB) {
   302         ab = pts[2] - pts[0];
   303         degenerateAB = degenerate_vector(ab);
   304     }
   305     if (degenerateCD) {
   306         cd = pts[3] - pts[1];
   307         degenerateCD = degenerate_vector(cd);
   308     }
   309     if (degenerateAB || degenerateCD) {
   310         goto DRAW_LINE;
   311     }
   312     SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD));
   313     bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius,
   314                                                &normalBC, &unitNormalBC);
   315 #ifndef SK_IGNORE_CUBIC_STROKE_FIX
   316     if (--subDivide < 0) {
   317         goto DRAW_LINE;
   318     }
   319 #endif
   320     if (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) ||
   321              normals_too_curvy(unitNormalBC, *unitNormalCD)) {
   322 #ifdef SK_IGNORE_CUBIC_STROKE_FIX
   323         // subdivide if we can
   324         if (--subDivide < 0) {
   325             goto DRAW_LINE;
   326         }
   327 #endif
   328         SkPoint     tmp[7];
   329         SkVector    norm, unit, dummy, unitDummy;
   331         SkChopCubicAtHalf(pts, tmp);
   332         this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit,
   333                        subDivide);
   334         // we use dummys since we already have a valid (and more accurate)
   335         // normals for CD
   336         this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide);
   337     } else {
   338         SkVector    normalB, normalC;
   340         // need normals to inset/outset the off-curve pts B and C
   342         SkVector    unitBC = pts[2] - pts[1];
   343         unitBC.normalize();
   344         unitBC.rotateCCW();
   346         normalB = unitNormalAB + unitBC;
   347         normalC = *unitNormalCD + unitBC;
   349         SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC);
   350         SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
   351                                     SkScalarSqrt((SK_Scalar1 + dot)/2))));
   352         dot = SkPoint::DotProduct(*unitNormalCD, unitBC);
   353         SkAssertResult(normalC.setLength(SkScalarDiv(fRadius,
   354                                     SkScalarSqrt((SK_Scalar1 + dot)/2))));
   356         fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
   357                         pts[2].fX + normalC.fX, pts[2].fY + normalC.fY,
   358                         pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY);
   360         fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
   361                         pts[2].fX - normalC.fX, pts[2].fY - normalC.fY,
   362                         pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY);
   363     }
   364 }
   366 void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
   367     bool    degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
   368     bool    degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
   370     if (degenerateAB | degenerateBC) {
   371         if (degenerateAB ^ degenerateBC) {
   372             this->lineTo(pt2);
   373         }
   374         return;
   375     }
   377     SkVector    normalAB, unitAB, normalBC, unitBC;
   379     this->preJoinTo(pt1, &normalAB, &unitAB, false);
   381     {
   382         SkPoint pts[3], tmp[5];
   383         pts[0] = fPrevPt;
   384         pts[1] = pt1;
   385         pts[2] = pt2;
   387         if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) {
   388             unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY);
   389             unitBC.rotateCCW();
   390             if (normals_too_pinchy(unitAB, unitBC)) {
   391                 normalBC = unitBC;
   392                 normalBC.scale(fRadius);
   394                 fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY);
   395                 fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY);
   396                 fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY);
   398                 fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY);
   399                 fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY);
   400                 fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY);
   402                 fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius,
   403                                  SkPath::kCW_Direction);
   404             } else {
   405                 this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC,
   406                               kMaxQuadSubdivide);
   407                 SkVector n = normalBC;
   408                 SkVector u = unitBC;
   409                 this->quad_to(&tmp[2], n, u, &normalBC, &unitBC,
   410                               kMaxQuadSubdivide);
   411             }
   412         } else {
   413             this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC,
   414                           kMaxQuadSubdivide);
   415         }
   416     }
   418     this->postJoinTo(pt2, normalBC, unitBC);
   419 }
   421 void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2,
   422                             const SkPoint& pt3) {
   423     bool    degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
   424     bool    degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
   425     bool    degenerateCD = SkPath::IsLineDegenerate(pt2, pt3);
   427     if (degenerateAB + degenerateBC + degenerateCD >= 2) {
   428         this->lineTo(pt3);
   429         return;
   430     }
   432     SkVector    normalAB, unitAB, normalCD, unitCD;
   434     // find the first tangent (which might be pt1 or pt2
   435     {
   436         const SkPoint*  nextPt = &pt1;
   437         if (degenerateAB)
   438             nextPt = &pt2;
   439         this->preJoinTo(*nextPt, &normalAB, &unitAB, false);
   440     }
   442     {
   443         SkPoint pts[4], tmp[13];
   444         int         i, count;
   445         SkVector    n, u;
   446         SkScalar    tValues[3];
   448         pts[0] = fPrevPt;
   449         pts[1] = pt1;
   450         pts[2] = pt2;
   451         pts[3] = pt3;
   453         count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
   454         n = normalAB;
   455         u = unitAB;
   456         for (i = 0; i < count; i++) {
   457             this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD,
   458                            kMaxCubicSubdivide);
   459             if (i == count - 1) {
   460                 break;
   461             }
   462             n = normalCD;
   463             u = unitCD;
   465         }
   466     }
   468     this->postJoinTo(pt3, normalCD, unitCD);
   469 }
   471 ///////////////////////////////////////////////////////////////////////////////
   472 ///////////////////////////////////////////////////////////////////////////////
   474 #include "SkPaintDefaults.h"
   476 SkStroke::SkStroke() {
   477     fWidth      = SK_Scalar1;
   478     fMiterLimit = SkPaintDefaults_MiterLimit;
   479     fCap        = SkPaint::kDefault_Cap;
   480     fJoin       = SkPaint::kDefault_Join;
   481     fDoFill     = false;
   482 }
   484 SkStroke::SkStroke(const SkPaint& p) {
   485     fWidth      = p.getStrokeWidth();
   486     fMiterLimit = p.getStrokeMiter();
   487     fCap        = (uint8_t)p.getStrokeCap();
   488     fJoin       = (uint8_t)p.getStrokeJoin();
   489     fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
   490 }
   492 SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
   493     fWidth      = width;
   494     fMiterLimit = p.getStrokeMiter();
   495     fCap        = (uint8_t)p.getStrokeCap();
   496     fJoin       = (uint8_t)p.getStrokeJoin();
   497     fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
   498 }
   500 void SkStroke::setWidth(SkScalar width) {
   501     SkASSERT(width >= 0);
   502     fWidth = width;
   503 }
   505 void SkStroke::setMiterLimit(SkScalar miterLimit) {
   506     SkASSERT(miterLimit >= 0);
   507     fMiterLimit = miterLimit;
   508 }
   510 void SkStroke::setCap(SkPaint::Cap cap) {
   511     SkASSERT((unsigned)cap < SkPaint::kCapCount);
   512     fCap = SkToU8(cap);
   513 }
   515 void SkStroke::setJoin(SkPaint::Join join) {
   516     SkASSERT((unsigned)join < SkPaint::kJoinCount);
   517     fJoin = SkToU8(join);
   518 }
   520 ///////////////////////////////////////////////////////////////////////////////
   522 // If src==dst, then we use a tmp path to record the stroke, and then swap
   523 // its contents with src when we're done.
   524 class AutoTmpPath {
   525 public:
   526     AutoTmpPath(const SkPath& src, SkPath** dst) : fSrc(src) {
   527         if (&src == *dst) {
   528             *dst = &fTmpDst;
   529             fSwapWithSrc = true;
   530         } else {
   531             (*dst)->reset();
   532             fSwapWithSrc = false;
   533         }
   534     }
   536     ~AutoTmpPath() {
   537         if (fSwapWithSrc) {
   538             fTmpDst.swap(*const_cast<SkPath*>(&fSrc));
   539         }
   540     }
   542 private:
   543     SkPath          fTmpDst;
   544     const SkPath&   fSrc;
   545     bool            fSwapWithSrc;
   546 };
   548 void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
   549     SkASSERT(&src != NULL && dst != NULL);
   551     SkScalar radius = SkScalarHalf(fWidth);
   553     AutoTmpPath tmp(src, &dst);
   555     if (radius <= 0) {
   556         return;
   557     }
   559     // If src is really a rect, call our specialty strokeRect() method
   560     {
   561         bool isClosed;
   562         SkPath::Direction dir;
   563         if (src.isRect(&isClosed, &dir) && isClosed) {
   564             this->strokeRect(src.getBounds(), dst, dir);
   565             // our answer should preserve the inverseness of the src
   566             if (src.isInverseFillType()) {
   567                 SkASSERT(!dst->isInverseFillType());
   568                 dst->toggleInverseFillType();
   569             }
   570             return;
   571         }
   572     }
   574     SkAutoConicToQuads converter;
   575     const SkScalar conicTol = SK_Scalar1 / 4;
   577     SkPathStroker   stroker(src, radius, fMiterLimit, this->getCap(),
   578                             this->getJoin());
   579     SkPath::Iter    iter(src, false);
   580     SkPath::Verb    lastSegment = SkPath::kMove_Verb;
   582     for (;;) {
   583         SkPoint  pts[4];
   584         switch (iter.next(pts, false)) {
   585             case SkPath::kMove_Verb:
   586                 stroker.moveTo(pts[0]);
   587                 break;
   588             case SkPath::kLine_Verb:
   589                 stroker.lineTo(pts[1]);
   590                 lastSegment = SkPath::kLine_Verb;
   591                 break;
   592             case SkPath::kQuad_Verb:
   593                 stroker.quadTo(pts[1], pts[2]);
   594                 lastSegment = SkPath::kQuad_Verb;
   595                 break;
   596             case SkPath::kConic_Verb: {
   597                 // todo: if we had maxcurvature for conics, perhaps we should
   598                 // natively extrude the conic instead of converting to quads.
   599                 const SkPoint* quadPts =
   600                     converter.computeQuads(pts, iter.conicWeight(), conicTol);
   601                 for (int i = 0; i < converter.countQuads(); ++i) {
   602                     stroker.quadTo(quadPts[1], quadPts[2]);
   603                     quadPts += 2;
   604                 }
   605                 lastSegment = SkPath::kQuad_Verb;
   606             } break;
   607             case SkPath::kCubic_Verb:
   608                 stroker.cubicTo(pts[1], pts[2], pts[3]);
   609                 lastSegment = SkPath::kCubic_Verb;
   610                 break;
   611             case SkPath::kClose_Verb:
   612                 stroker.close(lastSegment == SkPath::kLine_Verb);
   613                 break;
   614             case SkPath::kDone_Verb:
   615                 goto DONE;
   616         }
   617     }
   618 DONE:
   619     stroker.done(dst, lastSegment == SkPath::kLine_Verb);
   621     if (fDoFill) {
   622         if (src.cheapIsDirection(SkPath::kCCW_Direction)) {
   623             dst->reverseAddPath(src);
   624         } else {
   625             dst->addPath(src);
   626         }
   627     } else {
   628         //  Seems like we can assume that a 2-point src would always result in
   629         //  a convex stroke, but testing has proved otherwise.
   630         //  TODO: fix the stroker to make this assumption true (without making
   631         //  it slower that the work that will be done in computeConvexity())
   632 #if 0
   633         // this test results in a non-convex stroke :(
   634         static void test(SkCanvas* canvas) {
   635             SkPoint pts[] = { 146.333328,  192.333328, 300.333344, 293.333344 };
   636             SkPaint paint;
   637             paint.setStrokeWidth(7);
   638             paint.setStrokeCap(SkPaint::kRound_Cap);
   639             canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
   640         }
   641 #endif
   642 #if 0
   643         if (2 == src.countPoints()) {
   644             dst->setIsConvex(true);
   645         }
   646 #endif
   647     }
   649     // our answer should preserve the inverseness of the src
   650     if (src.isInverseFillType()) {
   651         SkASSERT(!dst->isInverseFillType());
   652         dst->toggleInverseFillType();
   653     }
   654 }
   656 static SkPath::Direction reverse_direction(SkPath::Direction dir) {
   657     SkASSERT(SkPath::kUnknown_Direction != dir);
   658     return SkPath::kCW_Direction == dir ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
   659 }
   661 static void addBevel(SkPath* path, const SkRect& r, const SkRect& outer, SkPath::Direction dir) {
   662     SkPoint pts[8];
   664     if (SkPath::kCW_Direction == dir) {
   665         pts[0].set(r.fLeft, outer.fTop);
   666         pts[1].set(r.fRight, outer.fTop);
   667         pts[2].set(outer.fRight, r.fTop);
   668         pts[3].set(outer.fRight, r.fBottom);
   669         pts[4].set(r.fRight, outer.fBottom);
   670         pts[5].set(r.fLeft, outer.fBottom);
   671         pts[6].set(outer.fLeft, r.fBottom);
   672         pts[7].set(outer.fLeft, r.fTop);
   673     } else {
   674         pts[7].set(r.fLeft, outer.fTop);
   675         pts[6].set(r.fRight, outer.fTop);
   676         pts[5].set(outer.fRight, r.fTop);
   677         pts[4].set(outer.fRight, r.fBottom);
   678         pts[3].set(r.fRight, outer.fBottom);
   679         pts[2].set(r.fLeft, outer.fBottom);
   680         pts[1].set(outer.fLeft, r.fBottom);
   681         pts[0].set(outer.fLeft, r.fTop);
   682     }
   683     path->addPoly(pts, 8, true);
   684 }
   686 void SkStroke::strokeRect(const SkRect& origRect, SkPath* dst,
   687                           SkPath::Direction dir) const {
   688     SkASSERT(dst != NULL);
   689     dst->reset();
   691     SkScalar radius = SkScalarHalf(fWidth);
   692     if (radius <= 0) {
   693         return;
   694     }
   696     SkScalar rw = origRect.width();
   697     SkScalar rh = origRect.height();
   698     if ((rw < 0) ^ (rh < 0)) {
   699         dir = reverse_direction(dir);
   700     }
   701     SkRect rect(origRect);
   702     rect.sort();
   703     // reassign these, now that we know they'll be >= 0
   704     rw = rect.width();
   705     rh = rect.height();
   707     SkRect r(rect);
   708     r.outset(radius, radius);
   710     SkPaint::Join join = (SkPaint::Join)fJoin;
   711     if (SkPaint::kMiter_Join == join && fMiterLimit < SK_ScalarSqrt2) {
   712         join = SkPaint::kBevel_Join;
   713     }
   715     switch (join) {
   716         case SkPaint::kMiter_Join:
   717             dst->addRect(r, dir);
   718             break;
   719         case SkPaint::kBevel_Join:
   720             addBevel(dst, rect, r, dir);
   721             break;
   722         case SkPaint::kRound_Join:
   723             dst->addRoundRect(r, radius, radius, dir);
   724             break;
   725         default:
   726             break;
   727     }
   729     if (fWidth < SkMinScalar(rw, rh) && !fDoFill) {
   730         r = rect;
   731         r.inset(radius, radius);
   732         dst->addRect(r, reverse_direction(dir));
   733     }
   734 }

mercurial