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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/core/SkStrokerPriv.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,260 @@
     1.4 +
     1.5 +/*
     1.6 + * Copyright 2006 The Android Open Source Project
     1.7 + *
     1.8 + * Use of this source code is governed by a BSD-style license that can be
     1.9 + * found in the LICENSE file.
    1.10 + */
    1.11 +
    1.12 +
    1.13 +#include "SkStrokerPriv.h"
    1.14 +#include "SkGeometry.h"
    1.15 +#include "SkPath.h"
    1.16 +
    1.17 +static void ButtCapper(SkPath* path, const SkPoint& pivot,
    1.18 +                       const SkVector& normal, const SkPoint& stop,
    1.19 +                       SkPath*)
    1.20 +{
    1.21 +    path->lineTo(stop.fX, stop.fY);
    1.22 +}
    1.23 +
    1.24 +static void RoundCapper(SkPath* path, const SkPoint& pivot,
    1.25 +                        const SkVector& normal, const SkPoint& stop,
    1.26 +                        SkPath*)
    1.27 +{
    1.28 +    SkScalar    px = pivot.fX;
    1.29 +    SkScalar    py = pivot.fY;
    1.30 +    SkScalar    nx = normal.fX;
    1.31 +    SkScalar    ny = normal.fY;
    1.32 +    SkScalar    sx = SkScalarMul(nx, CUBIC_ARC_FACTOR);
    1.33 +    SkScalar    sy = SkScalarMul(ny, CUBIC_ARC_FACTOR);
    1.34 +
    1.35 +    path->cubicTo(px + nx + CWX(sx, sy), py + ny + CWY(sx, sy),
    1.36 +                  px + CWX(nx, ny) + sx, py + CWY(nx, ny) + sy,
    1.37 +                  px + CWX(nx, ny), py + CWY(nx, ny));
    1.38 +    path->cubicTo(px + CWX(nx, ny) - sx, py + CWY(nx, ny) - sy,
    1.39 +                  px - nx + CWX(sx, sy), py - ny + CWY(sx, sy),
    1.40 +                  stop.fX, stop.fY);
    1.41 +}
    1.42 +
    1.43 +static void SquareCapper(SkPath* path, const SkPoint& pivot,
    1.44 +                         const SkVector& normal, const SkPoint& stop,
    1.45 +                         SkPath* otherPath)
    1.46 +{
    1.47 +    SkVector parallel;
    1.48 +    normal.rotateCW(&parallel);
    1.49 +
    1.50 +    if (otherPath)
    1.51 +    {
    1.52 +        path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
    1.53 +        path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
    1.54 +    }
    1.55 +    else
    1.56 +    {
    1.57 +        path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
    1.58 +        path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
    1.59 +        path->lineTo(stop.fX, stop.fY);
    1.60 +    }
    1.61 +}
    1.62 +
    1.63 +/////////////////////////////////////////////////////////////////////////////
    1.64 +
    1.65 +static bool is_clockwise(const SkVector& before, const SkVector& after)
    1.66 +{
    1.67 +    return SkScalarMul(before.fX, after.fY) - SkScalarMul(before.fY, after.fX) > 0;
    1.68 +}
    1.69 +
    1.70 +enum AngleType {
    1.71 +    kNearly180_AngleType,
    1.72 +    kSharp_AngleType,
    1.73 +    kShallow_AngleType,
    1.74 +    kNearlyLine_AngleType
    1.75 +};
    1.76 +
    1.77 +static AngleType Dot2AngleType(SkScalar dot)
    1.78 +{
    1.79 +// need more precise fixed normalization
    1.80 +//  SkASSERT(SkScalarAbs(dot) <= SK_Scalar1 + SK_ScalarNearlyZero);
    1.81 +
    1.82 +    if (dot >= 0)   // shallow or line
    1.83 +        return SkScalarNearlyZero(SK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType;
    1.84 +    else            // sharp or 180
    1.85 +        return SkScalarNearlyZero(SK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType;
    1.86 +}
    1.87 +
    1.88 +static void HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after)
    1.89 +{
    1.90 +#if 1
    1.91 +    /*  In the degenerate case that the stroke radius is larger than our segments
    1.92 +        just connecting the two inner segments may "show through" as a funny
    1.93 +        diagonal. To pseudo-fix this, we go through the pivot point. This adds
    1.94 +        an extra point/edge, but I can't see a cheap way to know when this is
    1.95 +        not needed :(
    1.96 +    */
    1.97 +    inner->lineTo(pivot.fX, pivot.fY);
    1.98 +#endif
    1.99 +
   1.100 +    inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY);
   1.101 +}
   1.102 +
   1.103 +static void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
   1.104 +                        const SkPoint& pivot, const SkVector& afterUnitNormal,
   1.105 +                        SkScalar radius, SkScalar invMiterLimit, bool, bool)
   1.106 +{
   1.107 +    SkVector    after;
   1.108 +    afterUnitNormal.scale(radius, &after);
   1.109 +
   1.110 +    if (!is_clockwise(beforeUnitNormal, afterUnitNormal))
   1.111 +    {
   1.112 +        SkTSwap<SkPath*>(outer, inner);
   1.113 +        after.negate();
   1.114 +    }
   1.115 +
   1.116 +    outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
   1.117 +    HandleInnerJoin(inner, pivot, after);
   1.118 +}
   1.119 +
   1.120 +static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
   1.121 +                        const SkPoint& pivot, const SkVector& afterUnitNormal,
   1.122 +                        SkScalar radius, SkScalar invMiterLimit, bool, bool)
   1.123 +{
   1.124 +    SkScalar    dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
   1.125 +    AngleType   angleType = Dot2AngleType(dotProd);
   1.126 +
   1.127 +    if (angleType == kNearlyLine_AngleType)
   1.128 +        return;
   1.129 +
   1.130 +    SkVector            before = beforeUnitNormal;
   1.131 +    SkVector            after = afterUnitNormal;
   1.132 +    SkRotationDirection dir = kCW_SkRotationDirection;
   1.133 +
   1.134 +    if (!is_clockwise(before, after))
   1.135 +    {
   1.136 +        SkTSwap<SkPath*>(outer, inner);
   1.137 +        before.negate();
   1.138 +        after.negate();
   1.139 +        dir = kCCW_SkRotationDirection;
   1.140 +    }
   1.141 +
   1.142 +    SkPoint     pts[kSkBuildQuadArcStorage];
   1.143 +    SkMatrix    matrix;
   1.144 +    matrix.setScale(radius, radius);
   1.145 +    matrix.postTranslate(pivot.fX, pivot.fY);
   1.146 +    int count = SkBuildQuadArc(before, after, dir, &matrix, pts);
   1.147 +    SkASSERT((count & 1) == 1);
   1.148 +
   1.149 +    if (count > 1)
   1.150 +    {
   1.151 +        for (int i = 1; i < count; i += 2)
   1.152 +            outer->quadTo(pts[i].fX, pts[i].fY, pts[i+1].fX, pts[i+1].fY);
   1.153 +
   1.154 +        after.scale(radius);
   1.155 +        HandleInnerJoin(inner, pivot, after);
   1.156 +    }
   1.157 +}
   1.158 +
   1.159 +#define kOneOverSqrt2   (0.707106781f)
   1.160 +
   1.161 +static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
   1.162 +                        const SkPoint& pivot, const SkVector& afterUnitNormal,
   1.163 +                        SkScalar radius, SkScalar invMiterLimit,
   1.164 +                        bool prevIsLine, bool currIsLine)
   1.165 +{
   1.166 +    // negate the dot since we're using normals instead of tangents
   1.167 +    SkScalar    dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
   1.168 +    AngleType   angleType = Dot2AngleType(dotProd);
   1.169 +    SkVector    before = beforeUnitNormal;
   1.170 +    SkVector    after = afterUnitNormal;
   1.171 +    SkVector    mid;
   1.172 +    SkScalar    sinHalfAngle;
   1.173 +    bool        ccw;
   1.174 +
   1.175 +    if (angleType == kNearlyLine_AngleType)
   1.176 +        return;
   1.177 +    if (angleType == kNearly180_AngleType)
   1.178 +    {
   1.179 +        currIsLine = false;
   1.180 +        goto DO_BLUNT;
   1.181 +    }
   1.182 +
   1.183 +    ccw = !is_clockwise(before, after);
   1.184 +    if (ccw)
   1.185 +    {
   1.186 +        SkTSwap<SkPath*>(outer, inner);
   1.187 +        before.negate();
   1.188 +        after.negate();
   1.189 +    }
   1.190 +
   1.191 +    /*  Before we enter the world of square-roots and divides,
   1.192 +        check if we're trying to join an upright right angle
   1.193 +        (common case for stroking rectangles). If so, special case
   1.194 +        that (for speed an accuracy).
   1.195 +        Note: we only need to check one normal if dot==0
   1.196 +    */
   1.197 +    if (0 == dotProd && invMiterLimit <= kOneOverSqrt2)
   1.198 +    {
   1.199 +        mid.set(SkScalarMul(before.fX + after.fX, radius),
   1.200 +                SkScalarMul(before.fY + after.fY, radius));
   1.201 +        goto DO_MITER;
   1.202 +    }
   1.203 +
   1.204 +    /*  midLength = radius / sinHalfAngle
   1.205 +        if (midLength > miterLimit * radius) abort
   1.206 +        if (radius / sinHalf > miterLimit * radius) abort
   1.207 +        if (1 / sinHalf > miterLimit) abort
   1.208 +        if (1 / miterLimit > sinHalf) abort
   1.209 +        My dotProd is opposite sign, since it is built from normals and not tangents
   1.210 +        hence 1 + dot instead of 1 - dot in the formula
   1.211 +    */
   1.212 +    sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd));
   1.213 +    if (sinHalfAngle < invMiterLimit)
   1.214 +    {
   1.215 +        currIsLine = false;
   1.216 +        goto DO_BLUNT;
   1.217 +    }
   1.218 +
   1.219 +    // choose the most accurate way to form the initial mid-vector
   1.220 +    if (angleType == kSharp_AngleType)
   1.221 +    {
   1.222 +        mid.set(after.fY - before.fY, before.fX - after.fX);
   1.223 +        if (ccw)
   1.224 +            mid.negate();
   1.225 +    }
   1.226 +    else
   1.227 +        mid.set(before.fX + after.fX, before.fY + after.fY);
   1.228 +
   1.229 +    mid.setLength(SkScalarDiv(radius, sinHalfAngle));
   1.230 +DO_MITER:
   1.231 +    if (prevIsLine)
   1.232 +        outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY);
   1.233 +    else
   1.234 +        outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY);
   1.235 +
   1.236 +DO_BLUNT:
   1.237 +    after.scale(radius);
   1.238 +    if (!currIsLine)
   1.239 +        outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
   1.240 +    HandleInnerJoin(inner, pivot, after);
   1.241 +}
   1.242 +
   1.243 +/////////////////////////////////////////////////////////////////////////////
   1.244 +
   1.245 +SkStrokerPriv::CapProc SkStrokerPriv::CapFactory(SkPaint::Cap cap)
   1.246 +{
   1.247 +    static const SkStrokerPriv::CapProc gCappers[] = {
   1.248 +        ButtCapper, RoundCapper, SquareCapper
   1.249 +    };
   1.250 +
   1.251 +    SkASSERT((unsigned)cap < SkPaint::kCapCount);
   1.252 +    return gCappers[cap];
   1.253 +}
   1.254 +
   1.255 +SkStrokerPriv::JoinProc SkStrokerPriv::JoinFactory(SkPaint::Join join)
   1.256 +{
   1.257 +    static const SkStrokerPriv::JoinProc gJoiners[] = {
   1.258 +        MiterJoiner, RoundJoiner, BluntJoiner
   1.259 +    };
   1.260 +
   1.261 +    SkASSERT((unsigned)join < SkPaint::kJoinCount);
   1.262 +    return gJoiners[join];
   1.263 +}

mercurial