1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/*
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2006 The Android Open Source Project
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com *
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file.
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com */
7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
88a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkStrokerPriv.h"
98a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkGeometry.h"
108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkPath.h"
11df429f3beac1c191289ba1e3bd918bf84df57bf5Cary Clark#include "SkPointPriv.h"
128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
13a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reedstatic void ButtCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
14a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed                       const SkPoint& stop, SkPath*) {
158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    path->lineTo(stop.fX, stop.fY);
168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
18a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reedstatic void RoundCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
19a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed                        const SkPoint& stop, SkPath*) {
2088f0a99fd48cfb30ca5596182f8932d76cd76f17reed    SkVector parallel;
21df429f3beac1c191289ba1e3bd918bf84df57bf5Cary Clark    SkPointPriv::RotateCW(normal, &parallel);
2288f0a99fd48cfb30ca5596182f8932d76cd76f17reed
2388f0a99fd48cfb30ca5596182f8932d76cd76f17reed    SkPoint projectedCenter = pivot + parallel;
2488f0a99fd48cfb30ca5596182f8932d76cd76f17reed
2588f0a99fd48cfb30ca5596182f8932d76cd76f17reed    path->conicTo(projectedCenter + normal, projectedCenter, SK_ScalarRoot2Over2);
2688f0a99fd48cfb30ca5596182f8932d76cd76f17reed    path->conicTo(projectedCenter - normal, stop, SK_ScalarRoot2Over2);
278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
29a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reedstatic void SquareCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
30a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed                         const SkPoint& stop, SkPath* otherPath) {
318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkVector parallel;
32df429f3beac1c191289ba1e3bd918bf84df57bf5Cary Clark    SkPointPriv::RotateCW(normal, &parallel);
338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
34a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    if (otherPath) {
358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
37a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    } else {
388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        path->lineTo(stop.fX, stop.fY);
418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com/////////////////////////////////////////////////////////////////////////////
458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
46a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reedstatic bool is_clockwise(const SkVector& before, const SkVector& after) {
47a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    return before.fX * after.fY > before.fY * after.fX;
488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comenum AngleType {
518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    kNearly180_AngleType,
528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    kSharp_AngleType,
538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    kShallow_AngleType,
548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    kNearlyLine_AngleType
558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com};
568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
57a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reedstatic AngleType Dot2AngleType(SkScalar dot) {
588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com// need more precise fixed normalization
598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com//  SkASSERT(SkScalarAbs(dot) <= SK_Scalar1 + SK_ScalarNearlyZero);
608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
61a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    if (dot >= 0) { // shallow or line
628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return SkScalarNearlyZero(SK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType;
63a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    } else {           // sharp or 180
648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return SkScalarNearlyZero(SK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType;
65a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    }
668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
68a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reedstatic void HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after) {
698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#if 1
708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    /*  In the degenerate case that the stroke radius is larger than our segments
718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        just connecting the two inner segments may "show through" as a funny
728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        diagonal. To pseudo-fix this, we go through the pivot point. This adds
738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        an extra point/edge, but I can't see a cheap way to know when this is
748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        not needed :(
758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    */
768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    inner->lineTo(pivot.fX, pivot.fY);
778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#endif
788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY);
808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comstatic void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                        const SkPoint& pivot, const SkVector& afterUnitNormal,
84a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed                        SkScalar radius, SkScalar invMiterLimit, bool, bool) {
858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkVector    after;
868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    afterUnitNormal.scale(radius, &after);
878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
88a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    if (!is_clockwise(beforeUnitNormal, afterUnitNormal)) {
898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkTSwap<SkPath*>(outer, inner);
908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        after.negate();
918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    HandleInnerJoin(inner, pivot, after);
958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comstatic void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                        const SkPoint& pivot, const SkVector& afterUnitNormal,
99a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed                        SkScalar radius, SkScalar invMiterLimit, bool, bool) {
1008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkScalar    dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
1018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    AngleType   angleType = Dot2AngleType(dotProd);
1028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (angleType == kNearlyLine_AngleType)
1048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return;
1058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkVector            before = beforeUnitNormal;
1078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkVector            after = afterUnitNormal;
1088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkRotationDirection dir = kCW_SkRotationDirection;
1098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
110a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    if (!is_clockwise(before, after)) {
1118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkTSwap<SkPath*>(outer, inner);
1128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        before.negate();
1138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        after.negate();
1148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        dir = kCCW_SkRotationDirection;
1158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkMatrix    matrix;
1188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    matrix.setScale(radius, radius);
1198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    matrix.postTranslate(pivot.fX, pivot.fY);
12088f0a99fd48cfb30ca5596182f8932d76cd76f17reed    SkConic conics[SkConic::kMaxConicsForArc];
12188f0a99fd48cfb30ca5596182f8932d76cd76f17reed    int count = SkConic::BuildUnitArc(before, after, dir, &matrix, conics);
12288f0a99fd48cfb30ca5596182f8932d76cd76f17reed    if (count > 0) {
12388f0a99fd48cfb30ca5596182f8932d76cd76f17reed        for (int i = 0; i < count; ++i) {
12488f0a99fd48cfb30ca5596182f8932d76cd76f17reed            outer->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
12588f0a99fd48cfb30ca5596182f8932d76cd76f17reed        }
12688f0a99fd48cfb30ca5596182f8932d76cd76f17reed        after.scale(radius);
12788f0a99fd48cfb30ca5596182f8932d76cd76f17reed        HandleInnerJoin(inner, pivot, after);
12888f0a99fd48cfb30ca5596182f8932d76cd76f17reed    }
1298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1318f4d2306fa866a26f9448048ff63f692b2ba43aareed@google.com#define kOneOverSqrt2   (0.707106781f)
1328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comstatic void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
1348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                        const SkPoint& pivot, const SkVector& afterUnitNormal,
1358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                        SkScalar radius, SkScalar invMiterLimit,
136a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed                        bool prevIsLine, bool currIsLine) {
1378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // negate the dot since we're using normals instead of tangents
1388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkScalar    dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
1398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    AngleType   angleType = Dot2AngleType(dotProd);
1408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkVector    before = beforeUnitNormal;
1418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkVector    after = afterUnitNormal;
1428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkVector    mid;
1438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkScalar    sinHalfAngle;
1448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    bool        ccw;
1458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
146a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    if (angleType == kNearlyLine_AngleType) {
1478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return;
148a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    }
149a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    if (angleType == kNearly180_AngleType) {
1508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        currIsLine = false;
1518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        goto DO_BLUNT;
1528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
153fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
1548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    ccw = !is_clockwise(before, after);
155a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    if (ccw) {
1568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkTSwap<SkPath*>(outer, inner);
1578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        before.negate();
1588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        after.negate();
1598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
160fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
1618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    /*  Before we enter the world of square-roots and divides,
1628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        check if we're trying to join an upright right angle
1638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        (common case for stroking rectangles). If so, special case
1648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        that (for speed an accuracy).
1658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        Note: we only need to check one normal if dot==0
1668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    */
167a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    if (0 == dotProd && invMiterLimit <= kOneOverSqrt2) {
168a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed        mid = (before + after) * radius;
1698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        goto DO_MITER;
1708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    /*  midLength = radius / sinHalfAngle
1738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (midLength > miterLimit * radius) abort
1748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (radius / sinHalf > miterLimit * radius) abort
1758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (1 / sinHalf > miterLimit) abort
1768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (1 / miterLimit > sinHalf) abort
1778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        My dotProd is opposite sign, since it is built from normals and not tangents
1788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        hence 1 + dot instead of 1 - dot in the formula
1798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    */
1808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd));
181a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    if (sinHalfAngle < invMiterLimit) {
1828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        currIsLine = false;
1838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        goto DO_BLUNT;
1848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // choose the most accurate way to form the initial mid-vector
187a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    if (angleType == kSharp_AngleType) {
1888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        mid.set(after.fY - before.fY, before.fX - after.fX);
189a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed        if (ccw) {
1908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            mid.negate();
191a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed        }
192a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    } else {
1938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        mid.set(before.fX + after.fX, before.fY + after.fY);
194a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    }
1958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
19680ea19ca4bdd68c1493666a5fe7e4ce9d43ded8breed    mid.setLength(radius / sinHalfAngle);
1978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comDO_MITER:
198a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    if (prevIsLine) {
1998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY);
200a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    } else {
2018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY);
202a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    }
2038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comDO_BLUNT:
2058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    after.scale(radius);
206a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    if (!currIsLine) {
2078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
208a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    }
2098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    HandleInnerJoin(inner, pivot, after);
2108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
2118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com/////////////////////////////////////////////////////////////////////////////
2138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
214a99b6ceff92183b424634f2e7276b9ea1d59e69dMike ReedSkStrokerPriv::CapProc SkStrokerPriv::CapFactory(SkPaint::Cap cap) {
215a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    const SkStrokerPriv::CapProc gCappers[] = {
2168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        ButtCapper, RoundCapper, SquareCapper
2178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    };
2188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkASSERT((unsigned)cap < SkPaint::kCapCount);
2208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return gCappers[cap];
2218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
2228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
223a99b6ceff92183b424634f2e7276b9ea1d59e69dMike ReedSkStrokerPriv::JoinProc SkStrokerPriv::JoinFactory(SkPaint::Join join) {
224a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    const SkStrokerPriv::JoinProc gJoiners[] = {
2258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        MiterJoiner, RoundJoiner, BluntJoiner
2268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    };
2278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkASSERT((unsigned)join < SkPaint::kJoinCount);
2298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return gJoiners[join];
2308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
231