• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libs/graphics/sgl/SkStrokerPriv.cpp
2 **
3 ** Copyright 2006, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 **     http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17 
18 #include "SkStrokerPriv.h"
19 #include "SkGeometry.h"
20 #include "SkPath.h"
21 
ButtCapper(SkPath * path,const SkPoint & pivot,const SkVector & normal,const SkPoint & stop,SkPath *)22 static void ButtCapper(SkPath* path, const SkPoint& pivot,
23                        const SkVector& normal, const SkPoint& stop,
24                        SkPath*)
25 {
26     path->lineTo(stop.fX, stop.fY);
27 }
28 
RoundCapper(SkPath * path,const SkPoint & pivot,const SkVector & normal,const SkPoint & stop,SkPath *)29 static void RoundCapper(SkPath* path, const SkPoint& pivot,
30                         const SkVector& normal, const SkPoint& stop,
31                         SkPath*)
32 {
33     SkScalar    px = pivot.fX;
34     SkScalar    py = pivot.fY;
35     SkScalar    nx = normal.fX;
36     SkScalar    ny = normal.fY;
37     SkScalar    sx = SkScalarMul(nx, CUBIC_ARC_FACTOR);
38     SkScalar    sy = SkScalarMul(ny, CUBIC_ARC_FACTOR);
39 
40     path->cubicTo(px + nx + CWX(sx, sy), py + ny + CWY(sx, sy),
41                   px + CWX(nx, ny) + sx, py + CWY(nx, ny) + sy,
42                   px + CWX(nx, ny), py + CWY(nx, ny));
43     path->cubicTo(px + CWX(nx, ny) - sx, py + CWY(nx, ny) - sy,
44                   px - nx + CWX(sx, sy), py - ny + CWY(sx, sy),
45                   stop.fX, stop.fY);
46 }
47 
SquareCapper(SkPath * path,const SkPoint & pivot,const SkVector & normal,const SkPoint & stop,SkPath * otherPath)48 static void SquareCapper(SkPath* path, const SkPoint& pivot,
49                          const SkVector& normal, const SkPoint& stop,
50                          SkPath* otherPath)
51 {
52     SkVector parallel;
53     normal.rotateCW(&parallel);
54 
55     if (otherPath)
56     {
57         path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
58         path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
59     }
60     else
61     {
62         path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
63         path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
64         path->lineTo(stop.fX, stop.fY);
65     }
66 }
67 
68 /////////////////////////////////////////////////////////////////////////////
69 
is_clockwise(const SkVector & before,const SkVector & after)70 static bool is_clockwise(const SkVector& before, const SkVector& after)
71 {
72     return SkScalarMul(before.fX, after.fY) - SkScalarMul(before.fY, after.fX) > 0;
73 }
74 
75 enum AngleType {
76     kNearly180_AngleType,
77     kSharp_AngleType,
78     kShallow_AngleType,
79     kNearlyLine_AngleType
80 };
81 
Dot2AngleType(SkScalar dot)82 static AngleType Dot2AngleType(SkScalar dot)
83 {
84 // need more precise fixed normalization
85 //  SkASSERT(SkScalarAbs(dot) <= SK_Scalar1 + SK_ScalarNearlyZero);
86 
87     if (dot >= 0)   // shallow or line
88         return SkScalarNearlyZero(SK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType;
89     else            // sharp or 180
90         return SkScalarNearlyZero(SK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType;
91 }
92 
HandleInnerJoin(SkPath * inner,const SkPoint & pivot,const SkVector & after)93 static void HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after)
94 {
95 #if 1
96     /*  In the degenerate case that the stroke radius is larger than our segments
97         just connecting the two inner segments may "show through" as a funny
98         diagonal. To pseudo-fix this, we go through the pivot point. This adds
99         an extra point/edge, but I can't see a cheap way to know when this is
100         not needed :(
101     */
102     inner->lineTo(pivot.fX, pivot.fY);
103 #endif
104 
105     inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY);
106 }
107 
BluntJoiner(SkPath * outer,SkPath * inner,const SkVector & beforeUnitNormal,const SkPoint & pivot,const SkVector & afterUnitNormal,SkScalar radius,SkScalar invMiterLimit,bool,bool)108 static void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
109                         const SkPoint& pivot, const SkVector& afterUnitNormal,
110                         SkScalar radius, SkScalar invMiterLimit, bool, bool)
111 {
112     SkVector    after;
113     afterUnitNormal.scale(radius, &after);
114 
115     if (!is_clockwise(beforeUnitNormal, afterUnitNormal))
116     {
117         SkTSwap<SkPath*>(outer, inner);
118         after.negate();
119     }
120 
121     outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
122     HandleInnerJoin(inner, pivot, after);
123 }
124 
RoundJoiner(SkPath * outer,SkPath * inner,const SkVector & beforeUnitNormal,const SkPoint & pivot,const SkVector & afterUnitNormal,SkScalar radius,SkScalar invMiterLimit,bool,bool)125 static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
126                         const SkPoint& pivot, const SkVector& afterUnitNormal,
127                         SkScalar radius, SkScalar invMiterLimit, bool, bool)
128 {
129     SkScalar    dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
130     AngleType   angleType = Dot2AngleType(dotProd);
131 
132     if (angleType == kNearlyLine_AngleType)
133         return;
134 
135     SkVector            before = beforeUnitNormal;
136     SkVector            after = afterUnitNormal;
137     SkRotationDirection dir = kCW_SkRotationDirection;
138 
139     if (!is_clockwise(before, after))
140     {
141         SkTSwap<SkPath*>(outer, inner);
142         before.negate();
143         after.negate();
144         dir = kCCW_SkRotationDirection;
145     }
146 
147     SkPoint     pts[kSkBuildQuadArcStorage];
148     SkMatrix    matrix;
149     matrix.setScale(radius, radius);
150     matrix.postTranslate(pivot.fX, pivot.fY);
151     int count = SkBuildQuadArc(before, after, dir, &matrix, pts);
152     SkASSERT((count & 1) == 1);
153 
154     if (count > 1)
155     {
156         for (int i = 1; i < count; i += 2)
157             outer->quadTo(pts[i].fX, pts[i].fY, pts[i+1].fX, pts[i+1].fY);
158 
159         after.scale(radius);
160         HandleInnerJoin(inner, pivot, after);
161     }
162 }
163 
164 #ifdef SK_SCALAR_IS_FLOAT
165     #define kOneOverSqrt2   (0.707106781f)
166 #else
167     #define kOneOverSqrt2   (46341)
168 #endif
169 
MiterJoiner(SkPath * outer,SkPath * inner,const SkVector & beforeUnitNormal,const SkPoint & pivot,const SkVector & afterUnitNormal,SkScalar radius,SkScalar invMiterLimit,bool prevIsLine,bool currIsLine)170 static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
171                         const SkPoint& pivot, const SkVector& afterUnitNormal,
172                         SkScalar radius, SkScalar invMiterLimit,
173                         bool prevIsLine, bool currIsLine)
174 {
175     // negate the dot since we're using normals instead of tangents
176     SkScalar    dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
177     AngleType   angleType = Dot2AngleType(dotProd);
178     SkVector    before = beforeUnitNormal;
179     SkVector    after = afterUnitNormal;
180     SkVector    mid;
181     SkScalar    sinHalfAngle;
182     bool        ccw;
183 
184     if (angleType == kNearlyLine_AngleType)
185         return;
186     if (angleType == kNearly180_AngleType)
187     {
188         currIsLine = false;
189         goto DO_BLUNT;
190     }
191 
192     ccw = !is_clockwise(before, after);
193     if (ccw)
194     {
195         SkTSwap<SkPath*>(outer, inner);
196         before.negate();
197         after.negate();
198     }
199 
200     /*  Before we enter the world of square-roots and divides,
201         check if we're trying to join an upright right angle
202         (common case for stroking rectangles). If so, special case
203         that (for speed an accuracy).
204         Note: we only need to check one normal if dot==0
205     */
206     if (0 == dotProd && invMiterLimit <= kOneOverSqrt2)
207     {
208         mid.set(SkScalarMul(before.fX + after.fX, radius),
209                 SkScalarMul(before.fY + after.fY, radius));
210         goto DO_MITER;
211     }
212 
213     /*  midLength = radius / sinHalfAngle
214         if (midLength > miterLimit * radius) abort
215         if (radius / sinHalf > miterLimit * radius) abort
216         if (1 / sinHalf > miterLimit) abort
217         if (1 / miterLimit > sinHalf) abort
218         My dotProd is opposite sign, since it is built from normals and not tangents
219         hence 1 + dot instead of 1 - dot in the formula
220     */
221     sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd));
222     if (sinHalfAngle < invMiterLimit)
223     {
224         currIsLine = false;
225         goto DO_BLUNT;
226     }
227 
228     // choose the most accurate way to form the initial mid-vector
229     if (angleType == kSharp_AngleType)
230     {
231         mid.set(after.fY - before.fY, before.fX - after.fX);
232         if (ccw)
233             mid.negate();
234     }
235     else
236         mid.set(before.fX + after.fX, before.fY + after.fY);
237 
238     mid.setLength(SkScalarDiv(radius, sinHalfAngle));
239 DO_MITER:
240     if (prevIsLine)
241         outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY);
242     else
243         outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY);
244 
245 DO_BLUNT:
246     after.scale(radius);
247     if (!currIsLine)
248         outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
249     HandleInnerJoin(inner, pivot, after);
250 }
251 
252 /////////////////////////////////////////////////////////////////////////////
253 
CapFactory(SkPaint::Cap cap)254 SkStrokerPriv::CapProc SkStrokerPriv::CapFactory(SkPaint::Cap cap)
255 {
256     static const SkStrokerPriv::CapProc gCappers[] = {
257         ButtCapper, RoundCapper, SquareCapper
258     };
259 
260     SkASSERT((unsigned)cap < SkPaint::kCapCount);
261     return gCappers[cap];
262 }
263 
JoinFactory(SkPaint::Join join)264 SkStrokerPriv::JoinProc SkStrokerPriv::JoinFactory(SkPaint::Join join)
265 {
266     static const SkStrokerPriv::JoinProc gJoiners[] = {
267         MiterJoiner, RoundJoiner, BluntJoiner
268     };
269 
270     SkASSERT((unsigned)join < SkPaint::kJoinCount);
271     return gJoiners[join];
272 }
273 
274 
275 
276