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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/core/SkStroke.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,734 @@
     1.4 +/*
     1.5 + * Copyright 2008 The Android Open Source Project
     1.6 + *
     1.7 + * Use of this source code is governed by a BSD-style license that can be
     1.8 + * found in the LICENSE file.
     1.9 + */
    1.10 +
    1.11 +#include "SkStrokerPriv.h"
    1.12 +#include "SkGeometry.h"
    1.13 +#include "SkPath.h"
    1.14 +
    1.15 +#define kMaxQuadSubdivide   5
    1.16 +#define kMaxCubicSubdivide  7
    1.17 +
    1.18 +static inline bool degenerate_vector(const SkVector& v) {
    1.19 +    return !SkPoint::CanNormalize(v.fX, v.fY);
    1.20 +}
    1.21 +
    1.22 +static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) {
    1.23 +    /*  root2/2 is a 45-degree angle
    1.24 +        make this constant bigger for more subdivisions (but not >= 1)
    1.25 +    */
    1.26 +    static const SkScalar kFlatEnoughNormalDotProd =
    1.27 +                                            SK_ScalarSqrt2/2 + SK_Scalar1/10;
    1.28 +
    1.29 +    SkASSERT(kFlatEnoughNormalDotProd > 0 &&
    1.30 +             kFlatEnoughNormalDotProd < SK_Scalar1);
    1.31 +
    1.32 +    return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd;
    1.33 +}
    1.34 +
    1.35 +static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) {
    1.36 +    // if the dot-product is -1, then we are definitely too pinchy. We tweak
    1.37 +    // that by an epsilon to ensure we have significant bits in our test
    1.38 +    static const int kMinSigBitsForDot = 8;
    1.39 +    static const SkScalar kDotEpsilon = FLT_EPSILON * (1 << kMinSigBitsForDot);
    1.40 +    static const SkScalar kTooPinchyNormalDotProd = kDotEpsilon - 1;
    1.41 +
    1.42 +    // just some sanity asserts to help document the expected range
    1.43 +    SkASSERT(kTooPinchyNormalDotProd >= -1);
    1.44 +    SkASSERT(kTooPinchyNormalDotProd < SkDoubleToScalar(-0.999));
    1.45 +
    1.46 +    SkScalar dot = SkPoint::DotProduct(norm0, norm1);
    1.47 +    return dot <= kTooPinchyNormalDotProd;
    1.48 +}
    1.49 +
    1.50 +static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after,
    1.51 +                                  SkScalar radius,
    1.52 +                                  SkVector* normal, SkVector* unitNormal) {
    1.53 +    if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) {
    1.54 +        return false;
    1.55 +    }
    1.56 +    unitNormal->rotateCCW();
    1.57 +    unitNormal->scale(radius, normal);
    1.58 +    return true;
    1.59 +}
    1.60 +
    1.61 +static bool set_normal_unitnormal(const SkVector& vec,
    1.62 +                                  SkScalar radius,
    1.63 +                                  SkVector* normal, SkVector* unitNormal) {
    1.64 +    if (!unitNormal->setNormalize(vec.fX, vec.fY)) {
    1.65 +        return false;
    1.66 +    }
    1.67 +    unitNormal->rotateCCW();
    1.68 +    unitNormal->scale(radius, normal);
    1.69 +    return true;
    1.70 +}
    1.71 +
    1.72 +///////////////////////////////////////////////////////////////////////////////
    1.73 +
    1.74 +class SkPathStroker {
    1.75 +public:
    1.76 +    SkPathStroker(const SkPath& src,
    1.77 +                  SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap,
    1.78 +                  SkPaint::Join join);
    1.79 +
    1.80 +    void moveTo(const SkPoint&);
    1.81 +    void lineTo(const SkPoint&);
    1.82 +    void quadTo(const SkPoint&, const SkPoint&);
    1.83 +    void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&);
    1.84 +    void close(bool isLine) { this->finishContour(true, isLine); }
    1.85 +
    1.86 +    void done(SkPath* dst, bool isLine) {
    1.87 +        this->finishContour(false, isLine);
    1.88 +        fOuter.addPath(fExtra);
    1.89 +        dst->swap(fOuter);
    1.90 +    }
    1.91 +
    1.92 +private:
    1.93 +    SkScalar    fRadius;
    1.94 +    SkScalar    fInvMiterLimit;
    1.95 +
    1.96 +    SkVector    fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal;
    1.97 +    SkPoint     fFirstPt, fPrevPt;  // on original path
    1.98 +    SkPoint     fFirstOuterPt;
    1.99 +    int         fSegmentCount;
   1.100 +    bool        fPrevIsLine;
   1.101 +
   1.102 +    SkStrokerPriv::CapProc  fCapper;
   1.103 +    SkStrokerPriv::JoinProc fJoiner;
   1.104 +
   1.105 +    SkPath  fInner, fOuter; // outer is our working answer, inner is temp
   1.106 +    SkPath  fExtra;         // added as extra complete contours
   1.107 +
   1.108 +    void    finishContour(bool close, bool isLine);
   1.109 +    void    preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal,
   1.110 +                      bool isLine);
   1.111 +    void    postJoinTo(const SkPoint&, const SkVector& normal,
   1.112 +                       const SkVector& unitNormal);
   1.113 +
   1.114 +    void    line_to(const SkPoint& currPt, const SkVector& normal);
   1.115 +    void    quad_to(const SkPoint pts[3],
   1.116 +                    const SkVector& normalAB, const SkVector& unitNormalAB,
   1.117 +                    SkVector* normalBC, SkVector* unitNormalBC,
   1.118 +                    int subDivide);
   1.119 +    void    cubic_to(const SkPoint pts[4],
   1.120 +                    const SkVector& normalAB, const SkVector& unitNormalAB,
   1.121 +                    SkVector* normalCD, SkVector* unitNormalCD,
   1.122 +                    int subDivide);
   1.123 +};
   1.124 +
   1.125 +///////////////////////////////////////////////////////////////////////////////
   1.126 +
   1.127 +void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal,
   1.128 +                              SkVector* unitNormal, bool currIsLine) {
   1.129 +    SkASSERT(fSegmentCount >= 0);
   1.130 +
   1.131 +    SkScalar    prevX = fPrevPt.fX;
   1.132 +    SkScalar    prevY = fPrevPt.fY;
   1.133 +
   1.134 +    SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal,
   1.135 +                                         unitNormal));
   1.136 +
   1.137 +    if (fSegmentCount == 0) {
   1.138 +        fFirstNormal = *normal;
   1.139 +        fFirstUnitNormal = *unitNormal;
   1.140 +        fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY);
   1.141 +
   1.142 +        fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY);
   1.143 +        fInner.moveTo(prevX - normal->fX, prevY - normal->fY);
   1.144 +    } else {    // we have a previous segment
   1.145 +        fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal,
   1.146 +                fRadius, fInvMiterLimit, fPrevIsLine, currIsLine);
   1.147 +    }
   1.148 +    fPrevIsLine = currIsLine;
   1.149 +}
   1.150 +
   1.151 +void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal,
   1.152 +                               const SkVector& unitNormal) {
   1.153 +    fPrevPt = currPt;
   1.154 +    fPrevUnitNormal = unitNormal;
   1.155 +    fPrevNormal = normal;
   1.156 +    fSegmentCount += 1;
   1.157 +}
   1.158 +
   1.159 +void SkPathStroker::finishContour(bool close, bool currIsLine) {
   1.160 +    if (fSegmentCount > 0) {
   1.161 +        SkPoint pt;
   1.162 +
   1.163 +        if (close) {
   1.164 +            fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt,
   1.165 +                    fFirstUnitNormal, fRadius, fInvMiterLimit,
   1.166 +                    fPrevIsLine, currIsLine);
   1.167 +            fOuter.close();
   1.168 +            // now add fInner as its own contour
   1.169 +            fInner.getLastPt(&pt);
   1.170 +            fOuter.moveTo(pt.fX, pt.fY);
   1.171 +            fOuter.reversePathTo(fInner);
   1.172 +            fOuter.close();
   1.173 +        } else {    // add caps to start and end
   1.174 +            // cap the end
   1.175 +            fInner.getLastPt(&pt);
   1.176 +            fCapper(&fOuter, fPrevPt, fPrevNormal, pt,
   1.177 +                    currIsLine ? &fInner : NULL);
   1.178 +            fOuter.reversePathTo(fInner);
   1.179 +            // cap the start
   1.180 +            fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt,
   1.181 +                    fPrevIsLine ? &fInner : NULL);
   1.182 +            fOuter.close();
   1.183 +        }
   1.184 +    }
   1.185 +    // since we may re-use fInner, we rewind instead of reset, to save on
   1.186 +    // reallocating its internal storage.
   1.187 +    fInner.rewind();
   1.188 +    fSegmentCount = -1;
   1.189 +}
   1.190 +
   1.191 +///////////////////////////////////////////////////////////////////////////////
   1.192 +
   1.193 +SkPathStroker::SkPathStroker(const SkPath& src,
   1.194 +                             SkScalar radius, SkScalar miterLimit,
   1.195 +                             SkPaint::Cap cap, SkPaint::Join join)
   1.196 +        : fRadius(radius) {
   1.197 +
   1.198 +    /*  This is only used when join is miter_join, but we initialize it here
   1.199 +        so that it is always defined, to fis valgrind warnings.
   1.200 +    */
   1.201 +    fInvMiterLimit = 0;
   1.202 +
   1.203 +    if (join == SkPaint::kMiter_Join) {
   1.204 +        if (miterLimit <= SK_Scalar1) {
   1.205 +            join = SkPaint::kBevel_Join;
   1.206 +        } else {
   1.207 +            fInvMiterLimit = SkScalarInvert(miterLimit);
   1.208 +        }
   1.209 +    }
   1.210 +    fCapper = SkStrokerPriv::CapFactory(cap);
   1.211 +    fJoiner = SkStrokerPriv::JoinFactory(join);
   1.212 +    fSegmentCount = -1;
   1.213 +    fPrevIsLine = false;
   1.214 +
   1.215 +    // Need some estimate of how large our final result (fOuter)
   1.216 +    // and our per-contour temp (fInner) will be, so we don't spend
   1.217 +    // extra time repeatedly growing these arrays.
   1.218 +    //
   1.219 +    // 3x for result == inner + outer + join (swag)
   1.220 +    // 1x for inner == 'wag' (worst contour length would be better guess)
   1.221 +    fOuter.incReserve(src.countPoints() * 3);
   1.222 +    fInner.incReserve(src.countPoints());
   1.223 +}
   1.224 +
   1.225 +void SkPathStroker::moveTo(const SkPoint& pt) {
   1.226 +    if (fSegmentCount > 0) {
   1.227 +        this->finishContour(false, false);
   1.228 +    }
   1.229 +    fSegmentCount = 0;
   1.230 +    fFirstPt = fPrevPt = pt;
   1.231 +}
   1.232 +
   1.233 +void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) {
   1.234 +    fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY);
   1.235 +    fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY);
   1.236 +}
   1.237 +
   1.238 +void SkPathStroker::lineTo(const SkPoint& currPt) {
   1.239 +    if (SkPath::IsLineDegenerate(fPrevPt, currPt)) {
   1.240 +        return;
   1.241 +    }
   1.242 +    SkVector    normal, unitNormal;
   1.243 +
   1.244 +    this->preJoinTo(currPt, &normal, &unitNormal, true);
   1.245 +    this->line_to(currPt, normal);
   1.246 +    this->postJoinTo(currPt, normal, unitNormal);
   1.247 +}
   1.248 +
   1.249 +void SkPathStroker::quad_to(const SkPoint pts[3],
   1.250 +                      const SkVector& normalAB, const SkVector& unitNormalAB,
   1.251 +                      SkVector* normalBC, SkVector* unitNormalBC,
   1.252 +                      int subDivide) {
   1.253 +    if (!set_normal_unitnormal(pts[1], pts[2], fRadius,
   1.254 +                               normalBC, unitNormalBC)) {
   1.255 +        // pts[1] nearly equals pts[2], so just draw a line to pts[2]
   1.256 +        this->line_to(pts[2], normalAB);
   1.257 +        *normalBC = normalAB;
   1.258 +        *unitNormalBC = unitNormalAB;
   1.259 +        return;
   1.260 +    }
   1.261 +
   1.262 +    if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) {
   1.263 +        SkPoint     tmp[5];
   1.264 +        SkVector    norm, unit;
   1.265 +
   1.266 +        SkChopQuadAtHalf(pts, tmp);
   1.267 +        this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide);
   1.268 +        this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide);
   1.269 +    } else {
   1.270 +        SkVector    normalB;
   1.271 +
   1.272 +        normalB = pts[2] - pts[0];
   1.273 +        normalB.rotateCCW();
   1.274 +        SkScalar dot = SkPoint::DotProduct(unitNormalAB, *unitNormalBC);
   1.275 +        SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
   1.276 +                                     SkScalarSqrt((SK_Scalar1 + dot)/2))));
   1.277 +
   1.278 +        fOuter.quadTo(  pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
   1.279 +                        pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY);
   1.280 +        fInner.quadTo(  pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
   1.281 +                        pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY);
   1.282 +    }
   1.283 +}
   1.284 +
   1.285 +void SkPathStroker::cubic_to(const SkPoint pts[4],
   1.286 +                      const SkVector& normalAB, const SkVector& unitNormalAB,
   1.287 +                      SkVector* normalCD, SkVector* unitNormalCD,
   1.288 +                      int subDivide) {
   1.289 +    SkVector    ab = pts[1] - pts[0];
   1.290 +    SkVector    cd = pts[3] - pts[2];
   1.291 +    SkVector    normalBC, unitNormalBC;
   1.292 +
   1.293 +    bool    degenerateAB = degenerate_vector(ab);
   1.294 +    bool    degenerateCD = degenerate_vector(cd);
   1.295 +
   1.296 +    if (degenerateAB && degenerateCD) {
   1.297 +DRAW_LINE:
   1.298 +        this->line_to(pts[3], normalAB);
   1.299 +        *normalCD = normalAB;
   1.300 +        *unitNormalCD = unitNormalAB;
   1.301 +        return;
   1.302 +    }
   1.303 +
   1.304 +    if (degenerateAB) {
   1.305 +        ab = pts[2] - pts[0];
   1.306 +        degenerateAB = degenerate_vector(ab);
   1.307 +    }
   1.308 +    if (degenerateCD) {
   1.309 +        cd = pts[3] - pts[1];
   1.310 +        degenerateCD = degenerate_vector(cd);
   1.311 +    }
   1.312 +    if (degenerateAB || degenerateCD) {
   1.313 +        goto DRAW_LINE;
   1.314 +    }
   1.315 +    SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD));
   1.316 +    bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius,
   1.317 +                                               &normalBC, &unitNormalBC);
   1.318 +#ifndef SK_IGNORE_CUBIC_STROKE_FIX
   1.319 +    if (--subDivide < 0) {
   1.320 +        goto DRAW_LINE;
   1.321 +    }
   1.322 +#endif
   1.323 +    if (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) ||
   1.324 +             normals_too_curvy(unitNormalBC, *unitNormalCD)) {
   1.325 +#ifdef SK_IGNORE_CUBIC_STROKE_FIX
   1.326 +        // subdivide if we can
   1.327 +        if (--subDivide < 0) {
   1.328 +            goto DRAW_LINE;
   1.329 +        }
   1.330 +#endif
   1.331 +        SkPoint     tmp[7];
   1.332 +        SkVector    norm, unit, dummy, unitDummy;
   1.333 +
   1.334 +        SkChopCubicAtHalf(pts, tmp);
   1.335 +        this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit,
   1.336 +                       subDivide);
   1.337 +        // we use dummys since we already have a valid (and more accurate)
   1.338 +        // normals for CD
   1.339 +        this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide);
   1.340 +    } else {
   1.341 +        SkVector    normalB, normalC;
   1.342 +
   1.343 +        // need normals to inset/outset the off-curve pts B and C
   1.344 +
   1.345 +        SkVector    unitBC = pts[2] - pts[1];
   1.346 +        unitBC.normalize();
   1.347 +        unitBC.rotateCCW();
   1.348 +
   1.349 +        normalB = unitNormalAB + unitBC;
   1.350 +        normalC = *unitNormalCD + unitBC;
   1.351 +
   1.352 +        SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC);
   1.353 +        SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
   1.354 +                                    SkScalarSqrt((SK_Scalar1 + dot)/2))));
   1.355 +        dot = SkPoint::DotProduct(*unitNormalCD, unitBC);
   1.356 +        SkAssertResult(normalC.setLength(SkScalarDiv(fRadius,
   1.357 +                                    SkScalarSqrt((SK_Scalar1 + dot)/2))));
   1.358 +
   1.359 +        fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
   1.360 +                        pts[2].fX + normalC.fX, pts[2].fY + normalC.fY,
   1.361 +                        pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY);
   1.362 +
   1.363 +        fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
   1.364 +                        pts[2].fX - normalC.fX, pts[2].fY - normalC.fY,
   1.365 +                        pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY);
   1.366 +    }
   1.367 +}
   1.368 +
   1.369 +void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
   1.370 +    bool    degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
   1.371 +    bool    degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
   1.372 +
   1.373 +    if (degenerateAB | degenerateBC) {
   1.374 +        if (degenerateAB ^ degenerateBC) {
   1.375 +            this->lineTo(pt2);
   1.376 +        }
   1.377 +        return;
   1.378 +    }
   1.379 +
   1.380 +    SkVector    normalAB, unitAB, normalBC, unitBC;
   1.381 +
   1.382 +    this->preJoinTo(pt1, &normalAB, &unitAB, false);
   1.383 +
   1.384 +    {
   1.385 +        SkPoint pts[3], tmp[5];
   1.386 +        pts[0] = fPrevPt;
   1.387 +        pts[1] = pt1;
   1.388 +        pts[2] = pt2;
   1.389 +
   1.390 +        if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) {
   1.391 +            unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY);
   1.392 +            unitBC.rotateCCW();
   1.393 +            if (normals_too_pinchy(unitAB, unitBC)) {
   1.394 +                normalBC = unitBC;
   1.395 +                normalBC.scale(fRadius);
   1.396 +
   1.397 +                fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY);
   1.398 +                fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY);
   1.399 +                fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY);
   1.400 +
   1.401 +                fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY);
   1.402 +                fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY);
   1.403 +                fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY);
   1.404 +
   1.405 +                fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius,
   1.406 +                                 SkPath::kCW_Direction);
   1.407 +            } else {
   1.408 +                this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC,
   1.409 +                              kMaxQuadSubdivide);
   1.410 +                SkVector n = normalBC;
   1.411 +                SkVector u = unitBC;
   1.412 +                this->quad_to(&tmp[2], n, u, &normalBC, &unitBC,
   1.413 +                              kMaxQuadSubdivide);
   1.414 +            }
   1.415 +        } else {
   1.416 +            this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC,
   1.417 +                          kMaxQuadSubdivide);
   1.418 +        }
   1.419 +    }
   1.420 +
   1.421 +    this->postJoinTo(pt2, normalBC, unitBC);
   1.422 +}
   1.423 +
   1.424 +void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2,
   1.425 +                            const SkPoint& pt3) {
   1.426 +    bool    degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
   1.427 +    bool    degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
   1.428 +    bool    degenerateCD = SkPath::IsLineDegenerate(pt2, pt3);
   1.429 +
   1.430 +    if (degenerateAB + degenerateBC + degenerateCD >= 2) {
   1.431 +        this->lineTo(pt3);
   1.432 +        return;
   1.433 +    }
   1.434 +
   1.435 +    SkVector    normalAB, unitAB, normalCD, unitCD;
   1.436 +
   1.437 +    // find the first tangent (which might be pt1 or pt2
   1.438 +    {
   1.439 +        const SkPoint*  nextPt = &pt1;
   1.440 +        if (degenerateAB)
   1.441 +            nextPt = &pt2;
   1.442 +        this->preJoinTo(*nextPt, &normalAB, &unitAB, false);
   1.443 +    }
   1.444 +
   1.445 +    {
   1.446 +        SkPoint pts[4], tmp[13];
   1.447 +        int         i, count;
   1.448 +        SkVector    n, u;
   1.449 +        SkScalar    tValues[3];
   1.450 +
   1.451 +        pts[0] = fPrevPt;
   1.452 +        pts[1] = pt1;
   1.453 +        pts[2] = pt2;
   1.454 +        pts[3] = pt3;
   1.455 +
   1.456 +        count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
   1.457 +        n = normalAB;
   1.458 +        u = unitAB;
   1.459 +        for (i = 0; i < count; i++) {
   1.460 +            this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD,
   1.461 +                           kMaxCubicSubdivide);
   1.462 +            if (i == count - 1) {
   1.463 +                break;
   1.464 +            }
   1.465 +            n = normalCD;
   1.466 +            u = unitCD;
   1.467 +
   1.468 +        }
   1.469 +    }
   1.470 +
   1.471 +    this->postJoinTo(pt3, normalCD, unitCD);
   1.472 +}
   1.473 +
   1.474 +///////////////////////////////////////////////////////////////////////////////
   1.475 +///////////////////////////////////////////////////////////////////////////////
   1.476 +
   1.477 +#include "SkPaintDefaults.h"
   1.478 +
   1.479 +SkStroke::SkStroke() {
   1.480 +    fWidth      = SK_Scalar1;
   1.481 +    fMiterLimit = SkPaintDefaults_MiterLimit;
   1.482 +    fCap        = SkPaint::kDefault_Cap;
   1.483 +    fJoin       = SkPaint::kDefault_Join;
   1.484 +    fDoFill     = false;
   1.485 +}
   1.486 +
   1.487 +SkStroke::SkStroke(const SkPaint& p) {
   1.488 +    fWidth      = p.getStrokeWidth();
   1.489 +    fMiterLimit = p.getStrokeMiter();
   1.490 +    fCap        = (uint8_t)p.getStrokeCap();
   1.491 +    fJoin       = (uint8_t)p.getStrokeJoin();
   1.492 +    fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
   1.493 +}
   1.494 +
   1.495 +SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
   1.496 +    fWidth      = width;
   1.497 +    fMiterLimit = p.getStrokeMiter();
   1.498 +    fCap        = (uint8_t)p.getStrokeCap();
   1.499 +    fJoin       = (uint8_t)p.getStrokeJoin();
   1.500 +    fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
   1.501 +}
   1.502 +
   1.503 +void SkStroke::setWidth(SkScalar width) {
   1.504 +    SkASSERT(width >= 0);
   1.505 +    fWidth = width;
   1.506 +}
   1.507 +
   1.508 +void SkStroke::setMiterLimit(SkScalar miterLimit) {
   1.509 +    SkASSERT(miterLimit >= 0);
   1.510 +    fMiterLimit = miterLimit;
   1.511 +}
   1.512 +
   1.513 +void SkStroke::setCap(SkPaint::Cap cap) {
   1.514 +    SkASSERT((unsigned)cap < SkPaint::kCapCount);
   1.515 +    fCap = SkToU8(cap);
   1.516 +}
   1.517 +
   1.518 +void SkStroke::setJoin(SkPaint::Join join) {
   1.519 +    SkASSERT((unsigned)join < SkPaint::kJoinCount);
   1.520 +    fJoin = SkToU8(join);
   1.521 +}
   1.522 +
   1.523 +///////////////////////////////////////////////////////////////////////////////
   1.524 +
   1.525 +// If src==dst, then we use a tmp path to record the stroke, and then swap
   1.526 +// its contents with src when we're done.
   1.527 +class AutoTmpPath {
   1.528 +public:
   1.529 +    AutoTmpPath(const SkPath& src, SkPath** dst) : fSrc(src) {
   1.530 +        if (&src == *dst) {
   1.531 +            *dst = &fTmpDst;
   1.532 +            fSwapWithSrc = true;
   1.533 +        } else {
   1.534 +            (*dst)->reset();
   1.535 +            fSwapWithSrc = false;
   1.536 +        }
   1.537 +    }
   1.538 +
   1.539 +    ~AutoTmpPath() {
   1.540 +        if (fSwapWithSrc) {
   1.541 +            fTmpDst.swap(*const_cast<SkPath*>(&fSrc));
   1.542 +        }
   1.543 +    }
   1.544 +
   1.545 +private:
   1.546 +    SkPath          fTmpDst;
   1.547 +    const SkPath&   fSrc;
   1.548 +    bool            fSwapWithSrc;
   1.549 +};
   1.550 +
   1.551 +void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
   1.552 +    SkASSERT(&src != NULL && dst != NULL);
   1.553 +
   1.554 +    SkScalar radius = SkScalarHalf(fWidth);
   1.555 +
   1.556 +    AutoTmpPath tmp(src, &dst);
   1.557 +
   1.558 +    if (radius <= 0) {
   1.559 +        return;
   1.560 +    }
   1.561 +
   1.562 +    // If src is really a rect, call our specialty strokeRect() method
   1.563 +    {
   1.564 +        bool isClosed;
   1.565 +        SkPath::Direction dir;
   1.566 +        if (src.isRect(&isClosed, &dir) && isClosed) {
   1.567 +            this->strokeRect(src.getBounds(), dst, dir);
   1.568 +            // our answer should preserve the inverseness of the src
   1.569 +            if (src.isInverseFillType()) {
   1.570 +                SkASSERT(!dst->isInverseFillType());
   1.571 +                dst->toggleInverseFillType();
   1.572 +            }
   1.573 +            return;
   1.574 +        }
   1.575 +    }
   1.576 +
   1.577 +    SkAutoConicToQuads converter;
   1.578 +    const SkScalar conicTol = SK_Scalar1 / 4;
   1.579 +
   1.580 +    SkPathStroker   stroker(src, radius, fMiterLimit, this->getCap(),
   1.581 +                            this->getJoin());
   1.582 +    SkPath::Iter    iter(src, false);
   1.583 +    SkPath::Verb    lastSegment = SkPath::kMove_Verb;
   1.584 +
   1.585 +    for (;;) {
   1.586 +        SkPoint  pts[4];
   1.587 +        switch (iter.next(pts, false)) {
   1.588 +            case SkPath::kMove_Verb:
   1.589 +                stroker.moveTo(pts[0]);
   1.590 +                break;
   1.591 +            case SkPath::kLine_Verb:
   1.592 +                stroker.lineTo(pts[1]);
   1.593 +                lastSegment = SkPath::kLine_Verb;
   1.594 +                break;
   1.595 +            case SkPath::kQuad_Verb:
   1.596 +                stroker.quadTo(pts[1], pts[2]);
   1.597 +                lastSegment = SkPath::kQuad_Verb;
   1.598 +                break;
   1.599 +            case SkPath::kConic_Verb: {
   1.600 +                // todo: if we had maxcurvature for conics, perhaps we should
   1.601 +                // natively extrude the conic instead of converting to quads.
   1.602 +                const SkPoint* quadPts =
   1.603 +                    converter.computeQuads(pts, iter.conicWeight(), conicTol);
   1.604 +                for (int i = 0; i < converter.countQuads(); ++i) {
   1.605 +                    stroker.quadTo(quadPts[1], quadPts[2]);
   1.606 +                    quadPts += 2;
   1.607 +                }
   1.608 +                lastSegment = SkPath::kQuad_Verb;
   1.609 +            } break;
   1.610 +            case SkPath::kCubic_Verb:
   1.611 +                stroker.cubicTo(pts[1], pts[2], pts[3]);
   1.612 +                lastSegment = SkPath::kCubic_Verb;
   1.613 +                break;
   1.614 +            case SkPath::kClose_Verb:
   1.615 +                stroker.close(lastSegment == SkPath::kLine_Verb);
   1.616 +                break;
   1.617 +            case SkPath::kDone_Verb:
   1.618 +                goto DONE;
   1.619 +        }
   1.620 +    }
   1.621 +DONE:
   1.622 +    stroker.done(dst, lastSegment == SkPath::kLine_Verb);
   1.623 +
   1.624 +    if (fDoFill) {
   1.625 +        if (src.cheapIsDirection(SkPath::kCCW_Direction)) {
   1.626 +            dst->reverseAddPath(src);
   1.627 +        } else {
   1.628 +            dst->addPath(src);
   1.629 +        }
   1.630 +    } else {
   1.631 +        //  Seems like we can assume that a 2-point src would always result in
   1.632 +        //  a convex stroke, but testing has proved otherwise.
   1.633 +        //  TODO: fix the stroker to make this assumption true (without making
   1.634 +        //  it slower that the work that will be done in computeConvexity())
   1.635 +#if 0
   1.636 +        // this test results in a non-convex stroke :(
   1.637 +        static void test(SkCanvas* canvas) {
   1.638 +            SkPoint pts[] = { 146.333328,  192.333328, 300.333344, 293.333344 };
   1.639 +            SkPaint paint;
   1.640 +            paint.setStrokeWidth(7);
   1.641 +            paint.setStrokeCap(SkPaint::kRound_Cap);
   1.642 +            canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
   1.643 +        }
   1.644 +#endif
   1.645 +#if 0
   1.646 +        if (2 == src.countPoints()) {
   1.647 +            dst->setIsConvex(true);
   1.648 +        }
   1.649 +#endif
   1.650 +    }
   1.651 +
   1.652 +    // our answer should preserve the inverseness of the src
   1.653 +    if (src.isInverseFillType()) {
   1.654 +        SkASSERT(!dst->isInverseFillType());
   1.655 +        dst->toggleInverseFillType();
   1.656 +    }
   1.657 +}
   1.658 +
   1.659 +static SkPath::Direction reverse_direction(SkPath::Direction dir) {
   1.660 +    SkASSERT(SkPath::kUnknown_Direction != dir);
   1.661 +    return SkPath::kCW_Direction == dir ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
   1.662 +}
   1.663 +
   1.664 +static void addBevel(SkPath* path, const SkRect& r, const SkRect& outer, SkPath::Direction dir) {
   1.665 +    SkPoint pts[8];
   1.666 +
   1.667 +    if (SkPath::kCW_Direction == dir) {
   1.668 +        pts[0].set(r.fLeft, outer.fTop);
   1.669 +        pts[1].set(r.fRight, outer.fTop);
   1.670 +        pts[2].set(outer.fRight, r.fTop);
   1.671 +        pts[3].set(outer.fRight, r.fBottom);
   1.672 +        pts[4].set(r.fRight, outer.fBottom);
   1.673 +        pts[5].set(r.fLeft, outer.fBottom);
   1.674 +        pts[6].set(outer.fLeft, r.fBottom);
   1.675 +        pts[7].set(outer.fLeft, r.fTop);
   1.676 +    } else {
   1.677 +        pts[7].set(r.fLeft, outer.fTop);
   1.678 +        pts[6].set(r.fRight, outer.fTop);
   1.679 +        pts[5].set(outer.fRight, r.fTop);
   1.680 +        pts[4].set(outer.fRight, r.fBottom);
   1.681 +        pts[3].set(r.fRight, outer.fBottom);
   1.682 +        pts[2].set(r.fLeft, outer.fBottom);
   1.683 +        pts[1].set(outer.fLeft, r.fBottom);
   1.684 +        pts[0].set(outer.fLeft, r.fTop);
   1.685 +    }
   1.686 +    path->addPoly(pts, 8, true);
   1.687 +}
   1.688 +
   1.689 +void SkStroke::strokeRect(const SkRect& origRect, SkPath* dst,
   1.690 +                          SkPath::Direction dir) const {
   1.691 +    SkASSERT(dst != NULL);
   1.692 +    dst->reset();
   1.693 +
   1.694 +    SkScalar radius = SkScalarHalf(fWidth);
   1.695 +    if (radius <= 0) {
   1.696 +        return;
   1.697 +    }
   1.698 +
   1.699 +    SkScalar rw = origRect.width();
   1.700 +    SkScalar rh = origRect.height();
   1.701 +    if ((rw < 0) ^ (rh < 0)) {
   1.702 +        dir = reverse_direction(dir);
   1.703 +    }
   1.704 +    SkRect rect(origRect);
   1.705 +    rect.sort();
   1.706 +    // reassign these, now that we know they'll be >= 0
   1.707 +    rw = rect.width();
   1.708 +    rh = rect.height();
   1.709 +
   1.710 +    SkRect r(rect);
   1.711 +    r.outset(radius, radius);
   1.712 +
   1.713 +    SkPaint::Join join = (SkPaint::Join)fJoin;
   1.714 +    if (SkPaint::kMiter_Join == join && fMiterLimit < SK_ScalarSqrt2) {
   1.715 +        join = SkPaint::kBevel_Join;
   1.716 +    }
   1.717 +
   1.718 +    switch (join) {
   1.719 +        case SkPaint::kMiter_Join:
   1.720 +            dst->addRect(r, dir);
   1.721 +            break;
   1.722 +        case SkPaint::kBevel_Join:
   1.723 +            addBevel(dst, rect, r, dir);
   1.724 +            break;
   1.725 +        case SkPaint::kRound_Join:
   1.726 +            dst->addRoundRect(r, radius, radius, dir);
   1.727 +            break;
   1.728 +        default:
   1.729 +            break;
   1.730 +    }
   1.731 +
   1.732 +    if (fWidth < SkMinScalar(rw, rh) && !fDoFill) {
   1.733 +        r = rect;
   1.734 +        r.inset(radius, radius);
   1.735 +        dst->addRect(r, reverse_direction(dir));
   1.736 +    }
   1.737 +}

mercurial