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, ¶llel); 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, ¶llel); 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