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(¶llel); 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 +}