2 * Copyright 2006 The Android Open Source Project
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "include/core/SkPath.h"
9 #include "src/core/SkGeometry.h"
10 #include "src/core/SkPointPriv.h"
11 #include "src/core/SkStrokerPriv.h"
15 static void ButtCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
16 const SkPoint& stop, SkPath*) {
17 path->lineTo(stop.fX, stop.fY);
20 static void RoundCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
21 const SkPoint& stop, SkPath*) {
23 SkPointPriv::RotateCW(normal, ¶llel);
25 SkPoint projectedCenter = pivot + parallel;
27 path->conicTo(projectedCenter + normal, projectedCenter, SK_ScalarRoot2Over2);
28 path->conicTo(projectedCenter - normal, stop, SK_ScalarRoot2Over2);
31 static void SquareCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
32 const SkPoint& stop, SkPath* otherPath) {
34 SkPointPriv::RotateCW(normal, ¶llel);
37 path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
38 path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
40 path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
41 path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
42 path->lineTo(stop.fX, stop.fY);
46 /////////////////////////////////////////////////////////////////////////////
48 static bool is_clockwise(const SkVector& before, const SkVector& after) {
49 return before.fX * after.fY > before.fY * after.fX;
59 static AngleType Dot2AngleType(SkScalar dot) {
60 // need more precise fixed normalization
61 // SkASSERT(SkScalarAbs(dot) <= SK_Scalar1 + SK_ScalarNearlyZero);
63 if (dot >= 0) { // shallow or line
64 return SkScalarNearlyZero(SK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType;
65 } else { // sharp or 180
66 return SkScalarNearlyZero(SK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType;
70 static void HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after) {
72 /* In the degenerate case that the stroke radius is larger than our segments
73 just connecting the two inner segments may "show through" as a funny
74 diagonal. To pseudo-fix this, we go through the pivot point. This adds
75 an extra point/edge, but I can't see a cheap way to know when this is
78 inner->lineTo(pivot.fX, pivot.fY);
81 inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY);
84 static void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
85 const SkPoint& pivot, const SkVector& afterUnitNormal,
86 SkScalar radius, SkScalar invMiterLimit, bool, bool) {
88 afterUnitNormal.scale(radius, &after);
90 if (!is_clockwise(beforeUnitNormal, afterUnitNormal)) {
96 outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
97 HandleInnerJoin(inner, pivot, after);
100 static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
101 const SkPoint& pivot, const SkVector& afterUnitNormal,
102 SkScalar radius, SkScalar invMiterLimit, bool, bool) {
103 SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
104 AngleType angleType = Dot2AngleType(dotProd);
106 if (angleType == kNearlyLine_AngleType)
109 SkVector before = beforeUnitNormal;
110 SkVector after = afterUnitNormal;
111 SkRotationDirection dir = kCW_SkRotationDirection;
113 if (!is_clockwise(before, after)) {
118 dir = kCCW_SkRotationDirection;
122 matrix.setScale(radius, radius);
123 matrix.postTranslate(pivot.fX, pivot.fY);
124 SkConic conics[SkConic::kMaxConicsForArc];
125 int count = SkConic::BuildUnitArc(before, after, dir, &matrix, conics);
127 for (int i = 0; i < count; ++i) {
128 outer->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
131 HandleInnerJoin(inner, pivot, after);
135 #define kOneOverSqrt2 (0.707106781f)
137 static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
138 const SkPoint& pivot, const SkVector& afterUnitNormal,
139 SkScalar radius, SkScalar invMiterLimit,
140 bool prevIsLine, bool currIsLine) {
141 // negate the dot since we're using normals instead of tangents
142 SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
143 AngleType angleType = Dot2AngleType(dotProd);
144 SkVector before = beforeUnitNormal;
145 SkVector after = afterUnitNormal;
147 SkScalar sinHalfAngle;
150 if (angleType == kNearlyLine_AngleType) {
153 if (angleType == kNearly180_AngleType) {
158 ccw = !is_clockwise(before, after);
166 /* Before we enter the world of square-roots and divides,
167 check if we're trying to join an upright right angle
168 (common case for stroking rectangles). If so, special case
169 that (for speed an accuracy).
170 Note: we only need to check one normal if dot==0
172 if (0 == dotProd && invMiterLimit <= kOneOverSqrt2) {
173 mid = (before + after) * radius;
177 /* midLength = radius / sinHalfAngle
178 if (midLength > miterLimit * radius) abort
179 if (radius / sinHalf > miterLimit * radius) abort
180 if (1 / sinHalf > miterLimit) abort
181 if (1 / miterLimit > sinHalf) abort
182 My dotProd is opposite sign, since it is built from normals and not tangents
183 hence 1 + dot instead of 1 - dot in the formula
185 sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd));
186 if (sinHalfAngle < invMiterLimit) {
191 // choose the most accurate way to form the initial mid-vector
192 if (angleType == kSharp_AngleType) {
193 mid.set(after.fY - before.fY, before.fX - after.fX);
198 mid.set(before.fX + after.fX, before.fY + after.fY);
201 mid.setLength(radius / sinHalfAngle);
204 outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY);
206 outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY);
212 outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
214 HandleInnerJoin(inner, pivot, after);
217 /////////////////////////////////////////////////////////////////////////////
219 SkStrokerPriv::CapProc SkStrokerPriv::CapFactory(SkPaint::Cap cap) {
220 const SkStrokerPriv::CapProc gCappers[] = {
221 ButtCapper, RoundCapper, SquareCapper
224 SkASSERT((unsigned)cap < SkPaint::kCapCount);
225 return gCappers[cap];
228 SkStrokerPriv::JoinProc SkStrokerPriv::JoinFactory(SkPaint::Join join) {
229 const SkStrokerPriv::JoinProc gJoiners[] = {
230 MiterJoiner, RoundJoiner, BluntJoiner
233 SkASSERT((unsigned)join < SkPaint::kJoinCount);
234 return gJoiners[join];