Line data Source code
1 : /*
2 : * Copyright 2006 The Android Open Source Project
3 : *
4 : * Use of this source code is governed by a BSD-style license that can be
5 : * found in the LICENSE file.
6 : */
7 :
8 : #include "SkStrokerPriv.h"
9 : #include "SkGeometry.h"
10 : #include "SkPath.h"
11 :
12 26 : static void ButtCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
13 : const SkPoint& stop, SkPath*) {
14 26 : path->lineTo(stop.fX, stop.fY);
15 26 : }
16 :
17 0 : static void RoundCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
18 : const SkPoint& stop, SkPath*) {
19 : SkVector parallel;
20 0 : normal.rotateCW(¶llel);
21 :
22 0 : SkPoint projectedCenter = pivot + parallel;
23 :
24 0 : path->conicTo(projectedCenter + normal, projectedCenter, SK_ScalarRoot2Over2);
25 0 : path->conicTo(projectedCenter - normal, stop, SK_ScalarRoot2Over2);
26 0 : }
27 :
28 0 : static void SquareCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
29 : const SkPoint& stop, SkPath* otherPath) {
30 : SkVector parallel;
31 0 : normal.rotateCW(¶llel);
32 :
33 0 : if (otherPath) {
34 0 : path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
35 0 : path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
36 : } else {
37 0 : path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
38 0 : path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
39 0 : path->lineTo(stop.fX, stop.fY);
40 : }
41 0 : }
42 :
43 : /////////////////////////////////////////////////////////////////////////////
44 :
45 0 : static bool is_clockwise(const SkVector& before, const SkVector& after) {
46 0 : return before.fX * after.fY > before.fY * after.fX;
47 : }
48 :
49 : enum AngleType {
50 : kNearly180_AngleType,
51 : kSharp_AngleType,
52 : kShallow_AngleType,
53 : kNearlyLine_AngleType
54 : };
55 :
56 16 : static AngleType Dot2AngleType(SkScalar dot) {
57 : // need more precise fixed normalization
58 : // SkASSERT(SkScalarAbs(dot) <= SK_Scalar1 + SK_ScalarNearlyZero);
59 :
60 16 : if (dot >= 0) { // shallow or line
61 16 : return SkScalarNearlyZero(SK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType;
62 : } else { // sharp or 180
63 0 : return SkScalarNearlyZero(SK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType;
64 : }
65 : }
66 :
67 0 : static void HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after) {
68 : #if 1
69 : /* In the degenerate case that the stroke radius is larger than our segments
70 : just connecting the two inner segments may "show through" as a funny
71 : diagonal. To pseudo-fix this, we go through the pivot point. This adds
72 : an extra point/edge, but I can't see a cheap way to know when this is
73 : not needed :(
74 : */
75 0 : inner->lineTo(pivot.fX, pivot.fY);
76 : #endif
77 :
78 0 : inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY);
79 0 : }
80 :
81 0 : static void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
82 : const SkPoint& pivot, const SkVector& afterUnitNormal,
83 : SkScalar radius, SkScalar invMiterLimit, bool, bool) {
84 : SkVector after;
85 0 : afterUnitNormal.scale(radius, &after);
86 :
87 0 : if (!is_clockwise(beforeUnitNormal, afterUnitNormal)) {
88 0 : SkTSwap<SkPath*>(outer, inner);
89 0 : after.negate();
90 : }
91 :
92 0 : outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
93 0 : HandleInnerJoin(inner, pivot, after);
94 0 : }
95 :
96 0 : static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
97 : const SkPoint& pivot, const SkVector& afterUnitNormal,
98 : SkScalar radius, SkScalar invMiterLimit, bool, bool) {
99 0 : SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
100 0 : AngleType angleType = Dot2AngleType(dotProd);
101 :
102 0 : if (angleType == kNearlyLine_AngleType)
103 0 : return;
104 :
105 0 : SkVector before = beforeUnitNormal;
106 0 : SkVector after = afterUnitNormal;
107 0 : SkRotationDirection dir = kCW_SkRotationDirection;
108 :
109 0 : if (!is_clockwise(before, after)) {
110 0 : SkTSwap<SkPath*>(outer, inner);
111 0 : before.negate();
112 0 : after.negate();
113 0 : dir = kCCW_SkRotationDirection;
114 : }
115 :
116 : SkMatrix matrix;
117 0 : matrix.setScale(radius, radius);
118 0 : matrix.postTranslate(pivot.fX, pivot.fY);
119 0 : SkConic conics[SkConic::kMaxConicsForArc];
120 0 : int count = SkConic::BuildUnitArc(before, after, dir, &matrix, conics);
121 0 : if (count > 0) {
122 0 : for (int i = 0; i < count; ++i) {
123 0 : outer->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
124 : }
125 0 : after.scale(radius);
126 0 : HandleInnerJoin(inner, pivot, after);
127 : }
128 : }
129 :
130 : #define kOneOverSqrt2 (0.707106781f)
131 :
132 16 : static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
133 : const SkPoint& pivot, const SkVector& afterUnitNormal,
134 : SkScalar radius, SkScalar invMiterLimit,
135 : bool prevIsLine, bool currIsLine) {
136 : // negate the dot since we're using normals instead of tangents
137 16 : SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
138 16 : AngleType angleType = Dot2AngleType(dotProd);
139 16 : SkVector before = beforeUnitNormal;
140 16 : SkVector after = afterUnitNormal;
141 : SkVector mid;
142 : SkScalar sinHalfAngle;
143 : bool ccw;
144 :
145 16 : if (angleType == kNearlyLine_AngleType) {
146 16 : return;
147 : }
148 0 : if (angleType == kNearly180_AngleType) {
149 0 : currIsLine = false;
150 0 : goto DO_BLUNT;
151 : }
152 :
153 0 : ccw = !is_clockwise(before, after);
154 0 : if (ccw) {
155 0 : SkTSwap<SkPath*>(outer, inner);
156 0 : before.negate();
157 0 : after.negate();
158 : }
159 :
160 : /* Before we enter the world of square-roots and divides,
161 : check if we're trying to join an upright right angle
162 : (common case for stroking rectangles). If so, special case
163 : that (for speed an accuracy).
164 : Note: we only need to check one normal if dot==0
165 : */
166 0 : if (0 == dotProd && invMiterLimit <= kOneOverSqrt2) {
167 0 : mid = (before + after) * radius;
168 0 : goto DO_MITER;
169 : }
170 :
171 : /* midLength = radius / sinHalfAngle
172 : if (midLength > miterLimit * radius) abort
173 : if (radius / sinHalf > miterLimit * radius) abort
174 : if (1 / sinHalf > miterLimit) abort
175 : if (1 / miterLimit > sinHalf) abort
176 : My dotProd is opposite sign, since it is built from normals and not tangents
177 : hence 1 + dot instead of 1 - dot in the formula
178 : */
179 0 : sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd));
180 0 : if (sinHalfAngle < invMiterLimit) {
181 0 : currIsLine = false;
182 0 : goto DO_BLUNT;
183 : }
184 :
185 : // choose the most accurate way to form the initial mid-vector
186 0 : if (angleType == kSharp_AngleType) {
187 0 : mid.set(after.fY - before.fY, before.fX - after.fX);
188 0 : if (ccw) {
189 0 : mid.negate();
190 : }
191 : } else {
192 0 : mid.set(before.fX + after.fX, before.fY + after.fY);
193 : }
194 :
195 0 : mid.setLength(radius / sinHalfAngle);
196 : DO_MITER:
197 0 : if (prevIsLine) {
198 0 : outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY);
199 : } else {
200 0 : outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY);
201 : }
202 :
203 : DO_BLUNT:
204 0 : after.scale(radius);
205 0 : if (!currIsLine) {
206 0 : outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
207 : }
208 0 : HandleInnerJoin(inner, pivot, after);
209 : }
210 :
211 : /////////////////////////////////////////////////////////////////////////////
212 :
213 36 : SkStrokerPriv::CapProc SkStrokerPriv::CapFactory(SkPaint::Cap cap) {
214 : const SkStrokerPriv::CapProc gCappers[] = {
215 : ButtCapper, RoundCapper, SquareCapper
216 36 : };
217 :
218 36 : SkASSERT((unsigned)cap < SkPaint::kCapCount);
219 36 : return gCappers[cap];
220 : }
221 :
222 15 : SkStrokerPriv::JoinProc SkStrokerPriv::JoinFactory(SkPaint::Join join) {
223 : const SkStrokerPriv::JoinProc gJoiners[] = {
224 : MiterJoiner, RoundJoiner, BluntJoiner
225 15 : };
226 :
227 15 : SkASSERT((unsigned)join < SkPaint::kJoinCount);
228 15 : return gJoiners[join];
229 : }
|