OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2008 The Android Open Source Project | 2 * Copyright 2008 The Android Open Source Project |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkStrokerPriv.h" | 8 #include "SkStrokerPriv.h" |
9 #include "SkGeometry.h" | 9 #include "SkGeometry.h" |
10 #include "SkPath.h" | 10 #include "SkPath.h" |
11 | 11 |
12 #if QUAD_STROKE_APPROXIMATION | 12 #ifdef SK_QUAD_STROKE_APPROXIMATION |
13 | 13 |
14 enum { | 14 enum { |
15 kTangent_RecursiveLimit, | 15 kTangent_RecursiveLimit, |
16 kCubic_RecursiveLimit, | 16 kCubic_RecursiveLimit, |
| 17 kConic_RecursiveLimit, |
17 kQuad_RecursiveLimit | 18 kQuad_RecursiveLimit |
18 }; | 19 }; |
19 | 20 |
20 // quads with extreme widths (e.g. (0,1) (1,6) (0,3) width=5e7) recurse to p
oint of failure | 21 // quads with extreme widths (e.g. (0,1) (1,6) (0,3) width=5e7) recurse to p
oint of failure |
21 // largest seen for normal cubics : 5, 26 | 22 // largest seen for normal cubics : 5, 26 |
22 // largest seen for normal quads : 11 | 23 // largest seen for normal quads : 11 |
23 static const int kRecursiveLimits[] = { 5*3, 26*3, 11*3 }; // 3x limits see
n in practical tests | 24 static const int kRecursiveLimits[] = { 5*3, 26*3, 11*3, 11*3 }; // 3x limit
s seen in practice |
24 | 25 |
| 26 SK_COMPILE_ASSERT(0 == kTangent_RecursiveLimit, cubic_stroke_relies_on_tange
nt_equalling_zero); |
| 27 SK_COMPILE_ASSERT(1 == kCubic_RecursiveLimit, cubic_stroke_relies_on_cubic_e
qualling_one); |
25 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kRecursiveLimits) == kQuad_RecursiveLimit +
1, | 28 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kRecursiveLimits) == kQuad_RecursiveLimit +
1, |
26 recursive_limits_mismatch); | 29 recursive_limits_mismatch); |
27 | 30 |
28 #ifdef SK_DEBUG // enables tweaking these values at runtime from SampleApp | 31 #ifdef SK_DEBUG |
29 bool gDebugStrokerErrorSet = false; | |
30 SkScalar gDebugStrokerError; | |
31 | |
32 int gMaxRecursion[SK_ARRAY_COUNT(kRecursiveLimits)] = { 0 }; | 32 int gMaxRecursion[SK_ARRAY_COUNT(kRecursiveLimits)] = { 0 }; |
33 #endif | 33 #endif |
34 #ifndef DEBUG_QUAD_STROKER | 34 #ifndef DEBUG_QUAD_STROKER |
35 #define DEBUG_QUAD_STROKER 0 | 35 #define DEBUG_QUAD_STROKER 0 |
36 #endif | 36 #endif |
37 | 37 |
38 #if DEBUG_QUAD_STROKER | 38 #if DEBUG_QUAD_STROKER |
39 /* Enable to show the decisions made in subdividing the curve -- helpful
when the resulting | 39 /* Enable to show the decisions made in subdividing the curve -- helpful
when the resulting |
40 stroke has more than the optimal number of quadratics and lines */ | 40 stroke has more than the optimal number of quadratics and lines */ |
41 #define STROKER_RESULT(resultType, depth, quadPts, format, ...) \ | 41 #define STROKER_RESULT(resultType, depth, quadPts, format, ...) \ |
42 SkDebugf("[%d] %s " format "\n", depth, __FUNCTION__, __VA_ARGS_
_), \ | 42 SkDebugf("[%d] %s " format "\n", depth, __FUNCTION__, __VA_ARGS_
_), \ |
43 SkDebugf(" " #resultType " t=(%g,%g)\n", quadPts->fStartT, quad
Pts->fEndT), \ | 43 SkDebugf(" " #resultType " t=(%g,%g)\n", quadPts->fStartT, quad
Pts->fEndT), \ |
44 resultType | 44 resultType |
45 #define STROKER_DEBUG_PARAMS(...) , __VA_ARGS__ | 45 #define STROKER_DEBUG_PARAMS(...) , __VA_ARGS__ |
46 #else | 46 #else |
47 #define STROKER_RESULT(resultType, depth, quadPts, format, ...) \ | 47 #define STROKER_RESULT(resultType, depth, quadPts, format, ...) \ |
48 resultType | 48 resultType |
49 #define STROKER_DEBUG_PARAMS(...) | 49 #define STROKER_DEBUG_PARAMS(...) |
50 #endif | 50 #endif |
51 | 51 |
52 #endif | 52 #endif |
53 | 53 |
| 54 #ifndef SK_QUAD_STROKE_APPROXIMATION |
54 #define kMaxQuadSubdivide 5 | 55 #define kMaxQuadSubdivide 5 |
55 #define kMaxCubicSubdivide 7 | 56 #define kMaxCubicSubdivide 7 |
| 57 #endif |
56 | 58 |
57 static inline bool degenerate_vector(const SkVector& v) { | 59 static inline bool degenerate_vector(const SkVector& v) { |
58 return !SkPoint::CanNormalize(v.fX, v.fY); | 60 return !SkPoint::CanNormalize(v.fX, v.fY); |
59 } | 61 } |
60 | 62 |
61 #if !QUAD_STROKE_APPROXIMATION | 63 #ifndef SK_QUAD_STROKE_APPROXIMATION |
62 static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) { | 64 static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) { |
63 /* root2/2 is a 45-degree angle | 65 /* root2/2 is a 45-degree angle |
64 make this constant bigger for more subdivisions (but not >= 1) | 66 make this constant bigger for more subdivisions (but not >= 1) |
65 */ | 67 */ |
66 static const SkScalar kFlatEnoughNormalDotProd = | 68 static const SkScalar kFlatEnoughNormalDotProd = |
67 SK_ScalarSqrt2/2 + SK_Scalar1/10; | 69 SK_ScalarSqrt2/2 + SK_Scalar1/10; |
68 | 70 |
69 SkASSERT(kFlatEnoughNormalDotProd > 0 && | 71 SkASSERT(kFlatEnoughNormalDotProd > 0 && |
70 kFlatEnoughNormalDotProd < SK_Scalar1); | 72 kFlatEnoughNormalDotProd < SK_Scalar1); |
71 | 73 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
104 SkVector* normal, SkVector* unitNormal) { | 106 SkVector* normal, SkVector* unitNormal) { |
105 if (!unitNormal->setNormalize(vec.fX, vec.fY)) { | 107 if (!unitNormal->setNormalize(vec.fX, vec.fY)) { |
106 return false; | 108 return false; |
107 } | 109 } |
108 unitNormal->rotateCCW(); | 110 unitNormal->rotateCCW(); |
109 unitNormal->scale(radius, normal); | 111 unitNormal->scale(radius, normal); |
110 return true; | 112 return true; |
111 } | 113 } |
112 | 114 |
113 /////////////////////////////////////////////////////////////////////////////// | 115 /////////////////////////////////////////////////////////////////////////////// |
114 #if QUAD_STROKE_APPROXIMATION | 116 #ifdef SK_QUAD_STROKE_APPROXIMATION |
115 | 117 |
116 struct SkQuadConstruct { // The state of the quad stroke under construction. | 118 struct SkQuadConstruct { // The state of the quad stroke under construction. |
117 SkPoint fQuad[3]; // the stroked quad parallel to the original curve | 119 SkPoint fQuad[3]; // the stroked quad parallel to the original curve |
118 SkPoint fTangentStart; // a point tangent to fQuad[0] | 120 SkPoint fTangentStart; // a point tangent to fQuad[0] |
119 SkPoint fTangentEnd; // a point tangent to fQuad[2] | 121 SkPoint fTangentEnd; // a point tangent to fQuad[2] |
120 SkScalar fStartT; // a segment of the original curve | 122 SkScalar fStartT; // a segment of the original curve |
121 SkScalar fMidT; // " | 123 SkScalar fMidT; // " |
122 SkScalar fEndT; // " | 124 SkScalar fEndT; // " |
123 bool fStartSet; // state to share common points across structs | 125 bool fStartSet; // state to share common points across structs |
124 bool fEndSet; // " | 126 bool fEndSet; // " |
(...skipping 24 matching lines...) Expand all Loading... |
149 fQuad[2] = parent->fQuad[2]; | 151 fQuad[2] = parent->fQuad[2]; |
150 fTangentEnd = parent->fTangentEnd; | 152 fTangentEnd = parent->fTangentEnd; |
151 fEndSet = true; | 153 fEndSet = true; |
152 return true; | 154 return true; |
153 } | 155 } |
154 }; | 156 }; |
155 #endif | 157 #endif |
156 | 158 |
157 class SkPathStroker { | 159 class SkPathStroker { |
158 public: | 160 public: |
159 #if QUAD_STROKE_APPROXIMATION | |
160 SkPathStroker(const SkPath& src, | |
161 SkScalar radius, SkScalar miterLimit, SkScalar error, SkPaint:
:Cap, | |
162 SkPaint::Join, SkScalar resScale); | |
163 #else | |
164 SkPathStroker(const SkPath& src, | 161 SkPathStroker(const SkPath& src, |
165 SkScalar radius, SkScalar miterLimit, SkPaint::Cap, | 162 SkScalar radius, SkScalar miterLimit, SkPaint::Cap, |
166 SkPaint::Join, SkScalar resScale); | 163 SkPaint::Join, SkScalar resScale); |
167 #endif | |
168 | 164 |
169 void moveTo(const SkPoint&); | 165 void moveTo(const SkPoint&); |
170 void lineTo(const SkPoint&); | 166 void lineTo(const SkPoint&); |
171 void quadTo(const SkPoint&, const SkPoint&); | 167 void quadTo(const SkPoint&, const SkPoint&); |
| 168 #ifdef SK_QUAD_STROKE_APPROXIMATION |
| 169 void conicTo(const SkPoint&, const SkPoint&, SkScalar weight); |
| 170 #endif |
172 void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&); | 171 void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&); |
173 void close(bool isLine) { this->finishContour(true, isLine); } | 172 void close(bool isLine) { this->finishContour(true, isLine); } |
174 | 173 |
175 void done(SkPath* dst, bool isLine) { | 174 void done(SkPath* dst, bool isLine) { |
176 this->finishContour(false, isLine); | 175 this->finishContour(false, isLine); |
177 fOuter.addPath(fExtra); | 176 fOuter.addPath(fExtra); |
178 dst->swap(fOuter); | 177 dst->swap(fOuter); |
179 } | 178 } |
180 | 179 |
181 SkScalar getResScale() const { return fResScale; } | 180 SkScalar getResScale() const { return fResScale; } |
182 | 181 |
183 private: | 182 private: |
184 #if QUAD_STROKE_APPROXIMATION | |
185 SkScalar fError; | |
186 #endif | |
187 SkScalar fRadius; | 183 SkScalar fRadius; |
188 SkScalar fInvMiterLimit; | 184 SkScalar fInvMiterLimit; |
189 SkScalar fResScale; | 185 SkScalar fResScale; |
| 186 #ifdef SK_QUAD_STROKE_APPROXIMATION |
| 187 SkScalar fInvResScale; |
| 188 SkScalar fInvResScaleSquared; |
| 189 #endif |
190 | 190 |
191 SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal; | 191 SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal; |
192 SkPoint fFirstPt, fPrevPt; // on original path | 192 SkPoint fFirstPt, fPrevPt; // on original path |
193 SkPoint fFirstOuterPt; | 193 SkPoint fFirstOuterPt; |
194 int fSegmentCount; | 194 int fSegmentCount; |
195 bool fPrevIsLine; | 195 bool fPrevIsLine; |
196 | 196 |
197 SkStrokerPriv::CapProc fCapper; | 197 SkStrokerPriv::CapProc fCapper; |
198 SkStrokerPriv::JoinProc fJoiner; | 198 SkStrokerPriv::JoinProc fJoiner; |
199 | 199 |
200 SkPath fInner, fOuter; // outer is our working answer, inner is temp | 200 SkPath fInner, fOuter; // outer is our working answer, inner is temp |
201 SkPath fExtra; // added as extra complete contours | 201 SkPath fExtra; // added as extra complete contours |
202 | 202 |
203 #if QUAD_STROKE_APPROXIMATION | 203 #ifdef SK_QUAD_STROKE_APPROXIMATION |
204 enum StrokeType { | 204 enum StrokeType { |
205 kOuter_StrokeType = 1, // use sign-opposite values later to flip pe
rpendicular axis | 205 kOuter_StrokeType = 1, // use sign-opposite values later to flip pe
rpendicular axis |
206 kInner_StrokeType = -1 | 206 kInner_StrokeType = -1 |
207 } fStrokeType; | 207 } fStrokeType; |
208 | 208 |
209 enum ResultType { | 209 enum ResultType { |
210 kSplit_ResultType, // the caller should split the quad stroke i
n two | 210 kSplit_ResultType, // the caller should split the quad stroke i
n two |
211 kDegenerate_ResultType, // the caller should add a line | 211 kDegenerate_ResultType, // the caller should add a line |
212 kQuad_ResultType, // the caller should (continue to try to) ad
d a quad stroke | 212 kQuad_ResultType, // the caller should (continue to try to) ad
d a quad stroke |
213 kNormalError_ResultType, // the cubic's normal couldn't be computed -
- abort | 213 kNormalError_ResultType, // the cubic's normal couldn't be computed -
- abort |
(...skipping 10 matching lines...) Expand all Loading... |
224 | 224 |
225 enum IntersectRayType { | 225 enum IntersectRayType { |
226 kCtrlPt_RayType, | 226 kCtrlPt_RayType, |
227 kResultType_RayType, | 227 kResultType_RayType, |
228 }; | 228 }; |
229 | 229 |
230 int fRecursionDepth; // track stack depth to abort if numerics ru
n amok | 230 int fRecursionDepth; // track stack depth to abort if numerics ru
n amok |
231 bool fFoundTangents; // do less work until tangents meet (cubic) | 231 bool fFoundTangents; // do less work until tangents meet (cubic) |
232 | 232 |
233 void addDegenerateLine(const SkQuadConstruct* ); | 233 void addDegenerateLine(const SkQuadConstruct* ); |
| 234 ReductionType CheckConicLinear(const SkConic& , SkPoint* reduction); |
234 ReductionType CheckCubicLinear(const SkPoint cubic[4], SkPoint reduction[3], | 235 ReductionType CheckCubicLinear(const SkPoint cubic[4], SkPoint reduction[3], |
235 const SkPoint** tanPtPtr); | 236 const SkPoint** tanPtPtr); |
236 ReductionType CheckQuadLinear(const SkPoint quad[3], SkPoint* reduction); | 237 ReductionType CheckQuadLinear(const SkPoint quad[3], SkPoint* reduction); |
| 238 ResultType compareQuadConic(const SkConic& , SkQuadConstruct* ); |
237 ResultType compareQuadCubic(const SkPoint cubic[4], SkQuadConstruct* ); | 239 ResultType compareQuadCubic(const SkPoint cubic[4], SkQuadConstruct* ); |
238 ResultType compareQuadQuad(const SkPoint quad[3], SkQuadConstruct* ); | 240 ResultType compareQuadQuad(const SkPoint quad[3], SkQuadConstruct* ); |
| 241 bool conicPerpRay(const SkConic& , SkScalar t, SkPoint* tPt, SkPoint* onPt, |
| 242 SkPoint* tangent) const; |
| 243 bool conicQuadEnds(const SkConic& , SkQuadConstruct* ); |
| 244 bool conicStroke(const SkConic& , SkQuadConstruct* ); |
239 bool cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct* ) const; | 245 bool cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct* ) const; |
240 bool cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tPt, SkPoint*
onPt, | 246 bool cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tPt, SkPoint*
onPt, |
241 SkPoint* tangent) const; | 247 SkPoint* tangent) const; |
242 bool cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* ); | 248 bool cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* ); |
243 bool cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct* , SkPoint*
mid) const; | 249 bool cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct* , SkPoint*
mid) const; |
244 bool cubicStroke(const SkPoint cubic[4], SkQuadConstruct* ); | 250 bool cubicStroke(const SkPoint cubic[4], SkQuadConstruct* ); |
245 void init(StrokeType strokeType, SkQuadConstruct* , SkScalar tStart, SkScala
r tEnd); | 251 void init(StrokeType strokeType, SkQuadConstruct* , SkScalar tStart, SkScala
r tEnd); |
246 ResultType intersectRay(SkQuadConstruct* , IntersectRayType STROKER_DEBUG_P
ARAMS(int) ) const; | 252 ResultType intersectRay(SkQuadConstruct* , IntersectRayType STROKER_DEBUG_P
ARAMS(int) ) const; |
247 bool ptInQuadBounds(const SkPoint quad[3], const SkPoint& pt) const; | 253 bool ptInQuadBounds(const SkPoint quad[3], const SkPoint& pt) const; |
248 void quadPerpRay(const SkPoint quad[3], SkScalar t, SkPoint* tPt, SkPoint* o
nPt, | 254 void quadPerpRay(const SkPoint quad[3], SkScalar t, SkPoint* tPt, SkPoint* o
nPt, |
249 SkPoint* tangent) const; | 255 SkPoint* tangent) const; |
250 bool quadStroke(const SkPoint quad[3], SkQuadConstruct* ); | 256 bool quadStroke(const SkPoint quad[3], SkQuadConstruct* ); |
| 257 void setConicEndNormal(const SkConic& , |
| 258 const SkVector& normalAB, const SkVector& unitNormalA
B, |
| 259 SkVector* normalBC, SkVector* unitNormalBC); |
251 void setCubicEndNormal(const SkPoint cubic[4], | 260 void setCubicEndNormal(const SkPoint cubic[4], |
252 const SkVector& normalAB, const SkVector& unitNormalA
B, | 261 const SkVector& normalAB, const SkVector& unitNormalA
B, |
253 SkVector* normalCD, SkVector* unitNormalCD); | 262 SkVector* normalCD, SkVector* unitNormalCD); |
254 void setQuadEndNormal(const SkPoint quad[3], | 263 void setQuadEndNormal(const SkPoint quad[3], |
255 const SkVector& normalAB, const SkVector& unitNormalAB
, | 264 const SkVector& normalAB, const SkVector& unitNormalAB
, |
256 SkVector* normalBC, SkVector* unitNormalBC); | 265 SkVector* normalBC, SkVector* unitNormalBC); |
257 void setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt, SkPoint* ta
ngent) const; | 266 void setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt, SkPoint* ta
ngent) const; |
258 static bool SlightAngle(SkQuadConstruct* ); | 267 static bool SlightAngle(SkQuadConstruct* ); |
259 ResultType strokeCloseEnough(const SkPoint stroke[3], const SkPoint ray[2], | 268 ResultType strokeCloseEnough(const SkPoint stroke[3], const SkPoint ray[2], |
260 SkQuadConstruct* STROKER_DEBUG_PARAMS(int dept
h) ) const; | 269 SkQuadConstruct* STROKER_DEBUG_PARAMS(int dept
h) ) const; |
261 ResultType tangentsMeet(const SkPoint cubic[4], SkQuadConstruct* ); | 270 ResultType tangentsMeet(const SkPoint cubic[4], SkQuadConstruct* ); |
262 #endif | 271 #endif |
263 | 272 |
264 void finishContour(bool close, bool isLine); | 273 void finishContour(bool close, bool isLine); |
265 void preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal, | 274 void preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal, |
266 bool isLine); | 275 bool isLine); |
267 void postJoinTo(const SkPoint&, const SkVector& normal, | 276 void postJoinTo(const SkPoint&, const SkVector& normal, |
268 const SkVector& unitNormal); | 277 const SkVector& unitNormal); |
269 | 278 |
270 void line_to(const SkPoint& currPt, const SkVector& normal); | 279 void line_to(const SkPoint& currPt, const SkVector& normal); |
271 #if !QUAD_STROKE_APPROXIMATION | 280 #ifndef SK_QUAD_STROKE_APPROXIMATION |
272 void quad_to(const SkPoint pts[3], | 281 void quad_to(const SkPoint pts[3], |
273 const SkVector& normalAB, const SkVector& unitNormalAB, | 282 const SkVector& normalAB, const SkVector& unitNormalAB, |
274 SkVector* normalBC, SkVector* unitNormalBC, | 283 SkVector* normalBC, SkVector* unitNormalBC, |
275 int subDivide); | 284 int subDivide); |
276 void cubic_to(const SkPoint pts[4], | 285 void cubic_to(const SkPoint pts[4], |
277 const SkVector& normalAB, const SkVector& unitNormalAB, | 286 const SkVector& normalAB, const SkVector& unitNormalAB, |
278 SkVector* normalCD, SkVector* unitNormalCD, | 287 SkVector* normalCD, SkVector* unitNormalCD, |
279 int subDivide); | 288 int subDivide); |
280 #endif | 289 #endif |
281 }; | 290 }; |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
341 } | 350 } |
342 } | 351 } |
343 // since we may re-use fInner, we rewind instead of reset, to save on | 352 // since we may re-use fInner, we rewind instead of reset, to save on |
344 // reallocating its internal storage. | 353 // reallocating its internal storage. |
345 fInner.rewind(); | 354 fInner.rewind(); |
346 fSegmentCount = -1; | 355 fSegmentCount = -1; |
347 } | 356 } |
348 | 357 |
349 /////////////////////////////////////////////////////////////////////////////// | 358 /////////////////////////////////////////////////////////////////////////////// |
350 | 359 |
351 #if QUAD_STROKE_APPROXIMATION | |
352 SkPathStroker::SkPathStroker(const SkPath& src, | |
353 SkScalar radius, SkScalar miterLimit, SkScalar erro
r, | |
354 SkPaint::Cap cap, SkPaint::Join join, SkScalar resS
cale) | |
355 #else | |
356 SkPathStroker::SkPathStroker(const SkPath& src, | 360 SkPathStroker::SkPathStroker(const SkPath& src, |
357 SkScalar radius, SkScalar miterLimit, | 361 SkScalar radius, SkScalar miterLimit, |
358 SkPaint::Cap cap, SkPaint::Join join, SkScalar resS
cale) | 362 SkPaint::Cap cap, SkPaint::Join join, SkScalar resS
cale) |
359 #endif | 363 : fRadius(radius) |
360 : fRadius(radius), fResScale(resScale) { | 364 , fResScale(resScale) { |
361 | 365 |
362 /* This is only used when join is miter_join, but we initialize it here | 366 /* This is only used when join is miter_join, but we initialize it here |
363 so that it is always defined, to fis valgrind warnings. | 367 so that it is always defined, to fis valgrind warnings. |
364 */ | 368 */ |
365 fInvMiterLimit = 0; | 369 fInvMiterLimit = 0; |
366 | 370 |
367 if (join == SkPaint::kMiter_Join) { | 371 if (join == SkPaint::kMiter_Join) { |
368 if (miterLimit <= SK_Scalar1) { | 372 if (miterLimit <= SK_Scalar1) { |
369 join = SkPaint::kBevel_Join; | 373 join = SkPaint::kBevel_Join; |
370 } else { | 374 } else { |
371 fInvMiterLimit = SkScalarInvert(miterLimit); | 375 fInvMiterLimit = SkScalarInvert(miterLimit); |
372 } | 376 } |
373 } | 377 } |
374 fCapper = SkStrokerPriv::CapFactory(cap); | 378 fCapper = SkStrokerPriv::CapFactory(cap); |
375 fJoiner = SkStrokerPriv::JoinFactory(join); | 379 fJoiner = SkStrokerPriv::JoinFactory(join); |
376 fSegmentCount = -1; | 380 fSegmentCount = -1; |
377 fPrevIsLine = false; | 381 fPrevIsLine = false; |
378 | 382 |
379 // Need some estimate of how large our final result (fOuter) | 383 // Need some estimate of how large our final result (fOuter) |
380 // and our per-contour temp (fInner) will be, so we don't spend | 384 // and our per-contour temp (fInner) will be, so we don't spend |
381 // extra time repeatedly growing these arrays. | 385 // extra time repeatedly growing these arrays. |
382 // | 386 // |
383 // 3x for result == inner + outer + join (swag) | 387 // 3x for result == inner + outer + join (swag) |
384 // 1x for inner == 'wag' (worst contour length would be better guess) | 388 // 1x for inner == 'wag' (worst contour length would be better guess) |
385 fOuter.incReserve(src.countPoints() * 3); | 389 fOuter.incReserve(src.countPoints() * 3); |
386 fOuter.setIsVolatile(true); | 390 fOuter.setIsVolatile(true); |
387 fInner.incReserve(src.countPoints()); | 391 fInner.incReserve(src.countPoints()); |
388 fInner.setIsVolatile(true); | 392 fInner.setIsVolatile(true); |
389 #if QUAD_STROKE_APPROXIMATION | 393 #ifdef SK_QUAD_STROKE_APPROXIMATION |
390 #ifdef SK_DEBUG | 394 // TODO : write a common error function used by stroking and filling |
391 if (!gDebugStrokerErrorSet) { | 395 // The '4' below matches the fill scan converter's error term |
392 gDebugStrokerError = error; | 396 fInvResScale = SkScalarInvert(resScale * 4); |
393 } | 397 fInvResScaleSquared = fInvResScale * fInvResScale; |
394 fError = gDebugStrokerError; | |
395 #else | |
396 fError = error; | |
397 #endif | |
398 fRecursionDepth = 0; | 398 fRecursionDepth = 0; |
399 #endif | 399 #endif |
400 } | 400 } |
401 | 401 |
402 void SkPathStroker::moveTo(const SkPoint& pt) { | 402 void SkPathStroker::moveTo(const SkPoint& pt) { |
403 if (fSegmentCount > 0) { | 403 if (fSegmentCount > 0) { |
404 this->finishContour(false, false); | 404 this->finishContour(false, false); |
405 } | 405 } |
406 fSegmentCount = 0; | 406 fSegmentCount = 0; |
407 fFirstPt = fPrevPt = pt; | 407 fFirstPt = fPrevPt = pt; |
408 } | 408 } |
409 | 409 |
410 void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { | 410 void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { |
411 fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY); | 411 fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY); |
412 fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY); | 412 fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY); |
413 } | 413 } |
414 | 414 |
415 void SkPathStroker::lineTo(const SkPoint& currPt) { | 415 void SkPathStroker::lineTo(const SkPoint& currPt) { |
416 if (SkPath::IsLineDegenerate(fPrevPt, currPt)) { | 416 if (SkPath::IsLineDegenerate(fPrevPt, currPt)) { |
417 return; | 417 return; |
418 } | 418 } |
419 SkVector normal, unitNormal; | 419 SkVector normal, unitNormal; |
420 | 420 |
421 this->preJoinTo(currPt, &normal, &unitNormal, true); | 421 this->preJoinTo(currPt, &normal, &unitNormal, true); |
422 this->line_to(currPt, normal); | 422 this->line_to(currPt, normal); |
423 this->postJoinTo(currPt, normal, unitNormal); | 423 this->postJoinTo(currPt, normal, unitNormal); |
424 } | 424 } |
425 | 425 |
426 #if !QUAD_STROKE_APPROXIMATION | 426 #ifndef SK_QUAD_STROKE_APPROXIMATION |
427 void SkPathStroker::quad_to(const SkPoint pts[3], | 427 void SkPathStroker::quad_to(const SkPoint pts[3], |
428 const SkVector& normalAB, const SkVector& unitNormalAB, | 428 const SkVector& normalAB, const SkVector& unitNormalAB, |
429 SkVector* normalBC, SkVector* unitNormalBC, | 429 SkVector* normalBC, SkVector* unitNormalBC, |
430 int subDivide) { | 430 int subDivide) { |
431 if (!set_normal_unitnormal(pts[1], pts[2], fRadius, | 431 if (!set_normal_unitnormal(pts[1], pts[2], fRadius, |
432 normalBC, unitNormalBC)) { | 432 normalBC, unitNormalBC)) { |
433 // pts[1] nearly equals pts[2], so just draw a line to pts[2] | 433 // pts[1] nearly equals pts[2], so just draw a line to pts[2] |
434 this->line_to(pts[2], normalAB); | 434 this->line_to(pts[2], normalAB); |
435 *normalBC = normalAB; | 435 *normalBC = normalAB; |
436 *unitNormalBC = unitNormalAB; | 436 *unitNormalBC = unitNormalAB; |
(...skipping 17 matching lines...) Expand all Loading... |
454 SkScalarSqrt((SK_Scalar1 + dot)/2)))); | 454 SkScalarSqrt((SK_Scalar1 + dot)/2)))); |
455 | 455 |
456 fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, | 456 fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, |
457 pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY); | 457 pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY); |
458 fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, | 458 fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, |
459 pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY); | 459 pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY); |
460 } | 460 } |
461 } | 461 } |
462 #endif | 462 #endif |
463 | 463 |
464 #if QUAD_STROKE_APPROXIMATION | 464 #ifdef SK_QUAD_STROKE_APPROXIMATION |
465 void SkPathStroker::setQuadEndNormal(const SkPoint quad[3], const SkVector& norm
alAB, | 465 void SkPathStroker::setQuadEndNormal(const SkPoint quad[3], const SkVector& norm
alAB, |
466 const SkVector& unitNormalAB, SkVector* normalBC, SkVector* unitNormalBC
) { | 466 const SkVector& unitNormalAB, SkVector* normalBC, SkVector* unitNormalBC
) { |
467 if (!set_normal_unitnormal(quad[1], quad[2], fRadius, normalBC, unitNormalBC
)) { | 467 if (!set_normal_unitnormal(quad[1], quad[2], fRadius, normalBC, unitNormalBC
)) { |
468 *normalBC = normalAB; | 468 *normalBC = normalAB; |
469 *unitNormalBC = unitNormalAB; | 469 *unitNormalBC = unitNormalAB; |
470 } | 470 } |
471 } | 471 } |
472 | 472 |
| 473 void SkPathStroker::setConicEndNormal(const SkConic& conic, const SkVector& norm
alAB, |
| 474 const SkVector& unitNormalAB, SkVector* normalBC, SkVector* unitNormalBC
) { |
| 475 setQuadEndNormal(conic.fPts, normalAB, unitNormalAB, normalBC, unitNormalBC)
; |
| 476 } |
| 477 |
473 void SkPathStroker::setCubicEndNormal(const SkPoint cubic[4], const SkVector& no
rmalAB, | 478 void SkPathStroker::setCubicEndNormal(const SkPoint cubic[4], const SkVector& no
rmalAB, |
474 const SkVector& unitNormalAB, SkVector* normalCD, SkVector* unitNormalCD
) { | 479 const SkVector& unitNormalAB, SkVector* normalCD, SkVector* unitNormalCD
) { |
475 SkVector ab = cubic[1] - cubic[0]; | 480 SkVector ab = cubic[1] - cubic[0]; |
476 SkVector cd = cubic[3] - cubic[2]; | 481 SkVector cd = cubic[3] - cubic[2]; |
477 | 482 |
478 bool degenerateAB = degenerate_vector(ab); | 483 bool degenerateAB = degenerate_vector(ab); |
479 bool degenerateCD = degenerate_vector(cd); | 484 bool degenerateCD = degenerate_vector(cd); |
480 | 485 |
481 if (degenerateAB && degenerateCD) { | 486 if (degenerateAB && degenerateCD) { |
482 goto DEGENERATE_NORMAL; | 487 goto DEGENERATE_NORMAL; |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
540 If outer_1 == 0 and outer_2 == 1, the smaller of the remaining indices (2 an
d 3) is 2. | 545 If outer_1 == 0 and outer_2 == 1, the smaller of the remaining indices (2 an
d 3) is 2. |
541 | 546 |
542 This table can be collapsed to: (1 + (2 >> outer_2)) >> outer_1 | 547 This table can be collapsed to: (1 + (2 >> outer_2)) >> outer_1 |
543 | 548 |
544 Given three indices (outer_1 outer_2 mid_1) from 0..3, the remaining index i
s: | 549 Given three indices (outer_1 outer_2 mid_1) from 0..3, the remaining index i
s: |
545 | 550 |
546 mid_2 == (outer_1 ^ outer_2 ^ mid_1) | 551 mid_2 == (outer_1 ^ outer_2 ^ mid_1) |
547 */ | 552 */ |
548 static bool cubic_in_line(const SkPoint cubic[4]) { | 553 static bool cubic_in_line(const SkPoint cubic[4]) { |
549 SkScalar ptMax = -1; | 554 SkScalar ptMax = -1; |
550 int outer1, outer2; | 555 int outer1 SK_INIT_TO_AVOID_WARNING; |
| 556 int outer2 SK_INIT_TO_AVOID_WARNING; |
551 for (int index = 0; index < 3; ++index) { | 557 for (int index = 0; index < 3; ++index) { |
552 for (int inner = index + 1; inner < 4; ++inner) { | 558 for (int inner = index + 1; inner < 4; ++inner) { |
553 SkVector testDiff = cubic[inner] - cubic[index]; | 559 SkVector testDiff = cubic[inner] - cubic[index]; |
554 SkScalar testMax = SkTMax(SkScalarAbs(testDiff.fX), SkScalarAbs(test
Diff.fY)); | 560 SkScalar testMax = SkTMax(SkScalarAbs(testDiff.fX), SkScalarAbs(test
Diff.fY)); |
555 if (ptMax < testMax) { | 561 if (ptMax < testMax) { |
556 outer1 = index; | 562 outer1 = index; |
557 outer2 = inner; | 563 outer2 = inner; |
558 ptMax = testMax; | 564 ptMax = testMax; |
559 } | 565 } |
560 } | 566 } |
(...skipping 15 matching lines...) Expand all Loading... |
576 | 582 |
577 /* Given quad, see if all there points are in a line. | 583 /* Given quad, see if all there points are in a line. |
578 Return true if the inside point is close to a line connecting the outermost p
oints. | 584 Return true if the inside point is close to a line connecting the outermost p
oints. |
579 | 585 |
580 Find the outermost point by looking for the largest difference in X or Y. | 586 Find the outermost point by looking for the largest difference in X or Y. |
581 Since the XOR of the indices is 3 (0 ^ 1 ^ 2) | 587 Since the XOR of the indices is 3 (0 ^ 1 ^ 2) |
582 the missing index equals: outer_1 ^ outer_2 ^ 3 | 588 the missing index equals: outer_1 ^ outer_2 ^ 3 |
583 */ | 589 */ |
584 static bool quad_in_line(const SkPoint quad[3]) { | 590 static bool quad_in_line(const SkPoint quad[3]) { |
585 SkScalar ptMax = -1; | 591 SkScalar ptMax = -1; |
586 int outer1, outer2; | 592 int outer1 SK_INIT_TO_AVOID_WARNING; |
| 593 int outer2 SK_INIT_TO_AVOID_WARNING; |
587 for (int index = 0; index < 2; ++index) { | 594 for (int index = 0; index < 2; ++index) { |
588 for (int inner = index + 1; inner < 3; ++inner) { | 595 for (int inner = index + 1; inner < 3; ++inner) { |
589 SkVector testDiff = quad[inner] - quad[index]; | 596 SkVector testDiff = quad[inner] - quad[index]; |
590 SkScalar testMax = SkTMax(SkScalarAbs(testDiff.fX), SkScalarAbs(test
Diff.fY)); | 597 SkScalar testMax = SkTMax(SkScalarAbs(testDiff.fX), SkScalarAbs(test
Diff.fY)); |
591 if (ptMax < testMax) { | 598 if (ptMax < testMax) { |
592 outer1 = index; | 599 outer1 = index; |
593 outer2 = inner; | 600 outer2 = inner; |
594 ptMax = testMax; | 601 ptMax = testMax; |
595 } | 602 } |
596 } | 603 } |
597 } | 604 } |
598 SkASSERT(outer1 >= 0 && outer1 <= 1); | 605 SkASSERT(outer1 >= 0 && outer1 <= 1); |
599 SkASSERT(outer2 >= 1 && outer2 <= 2); | 606 SkASSERT(outer2 >= 1 && outer2 <= 2); |
600 SkASSERT(outer1 < outer2); | 607 SkASSERT(outer1 < outer2); |
601 int mid = outer1 ^ outer2 ^ 3; | 608 int mid = outer1 ^ outer2 ^ 3; |
602 SkScalar lineSlop = ptMax * ptMax * 0.00001f; // this multiplier is pulled
out of the air | 609 SkScalar lineSlop = ptMax * ptMax * 0.00001f; // this multiplier is pulled
out of the air |
603 return pt_to_line(quad[mid], quad[outer1], quad[outer2]) <= lineSlop; | 610 return pt_to_line(quad[mid], quad[outer1], quad[outer2]) <= lineSlop; |
604 } | 611 } |
605 | 612 |
| 613 static bool conic_in_line(const SkConic& conic) { |
| 614 return quad_in_line(conic.fPts); |
| 615 } |
| 616 |
606 SkPathStroker::ReductionType SkPathStroker::CheckCubicLinear(const SkPoint cubic
[4], | 617 SkPathStroker::ReductionType SkPathStroker::CheckCubicLinear(const SkPoint cubic
[4], |
607 SkPoint reduction[3], const SkPoint** tangentPtPtr) { | 618 SkPoint reduction[3], const SkPoint** tangentPtPtr) { |
608 bool degenerateAB = degenerate_vector(cubic[1] - cubic[0]); | 619 bool degenerateAB = degenerate_vector(cubic[1] - cubic[0]); |
609 bool degenerateBC = degenerate_vector(cubic[2] - cubic[1]); | 620 bool degenerateBC = degenerate_vector(cubic[2] - cubic[1]); |
610 bool degenerateCD = degenerate_vector(cubic[3] - cubic[2]); | 621 bool degenerateCD = degenerate_vector(cubic[3] - cubic[2]); |
611 if (degenerateAB & degenerateBC & degenerateCD) { | 622 if (degenerateAB & degenerateBC & degenerateCD) { |
612 return kPoint_ReductionType; | 623 return kPoint_ReductionType; |
613 } | 624 } |
614 if (degenerateAB + degenerateBC + degenerateCD == 2) { | 625 if (degenerateAB + degenerateBC + degenerateCD == 2) { |
615 return kLine_ReductionType; | 626 return kLine_ReductionType; |
(...skipping 11 matching lines...) Expand all Loading... |
627 SkScalar t = tValues[index]; | 638 SkScalar t = tValues[index]; |
628 SkEvalCubicAt(cubic, t, &reduction[index], NULL, NULL); | 639 SkEvalCubicAt(cubic, t, &reduction[index], NULL, NULL); |
629 } | 640 } |
630 SK_COMPILE_ASSERT(kQuad_ReductionType + 1 == kDegenerate_ReductionType, enum
_out_of_whack); | 641 SK_COMPILE_ASSERT(kQuad_ReductionType + 1 == kDegenerate_ReductionType, enum
_out_of_whack); |
631 SK_COMPILE_ASSERT(kQuad_ReductionType + 2 == kDegenerate2_ReductionType, enu
m_out_of_whack); | 642 SK_COMPILE_ASSERT(kQuad_ReductionType + 2 == kDegenerate2_ReductionType, enu
m_out_of_whack); |
632 SK_COMPILE_ASSERT(kQuad_ReductionType + 3 == kDegenerate3_ReductionType, enu
m_out_of_whack); | 643 SK_COMPILE_ASSERT(kQuad_ReductionType + 3 == kDegenerate3_ReductionType, enu
m_out_of_whack); |
633 | 644 |
634 return (ReductionType) (kQuad_ReductionType + count); | 645 return (ReductionType) (kQuad_ReductionType + count); |
635 } | 646 } |
636 | 647 |
| 648 SkPathStroker::ReductionType SkPathStroker::CheckConicLinear(const SkConic& coni
c, |
| 649 SkPoint* reduction) { |
| 650 bool degenerateAB = degenerate_vector(conic.fPts[1] - conic.fPts[0]); |
| 651 bool degenerateBC = degenerate_vector(conic.fPts[2] - conic.fPts[1]); |
| 652 if (degenerateAB & degenerateBC) { |
| 653 return kPoint_ReductionType; |
| 654 } |
| 655 if (degenerateAB | degenerateBC) { |
| 656 return kLine_ReductionType; |
| 657 } |
| 658 if (!conic_in_line(conic)) { |
| 659 return kQuad_ReductionType; |
| 660 } |
| 661 SkScalar t; |
| 662 if (!conic.findMaxCurvature(&t) || 0 == t) { |
| 663 return kLine_ReductionType; |
| 664 } |
| 665 conic.evalAt(t, reduction, NULL); |
| 666 return kDegenerate_ReductionType; |
| 667 } |
| 668 |
637 SkPathStroker::ReductionType SkPathStroker::CheckQuadLinear(const SkPoint quad[3
], | 669 SkPathStroker::ReductionType SkPathStroker::CheckQuadLinear(const SkPoint quad[3
], |
638 SkPoint* reduction) { | 670 SkPoint* reduction) { |
639 bool degenerateAB = degenerate_vector(quad[1] - quad[0]); | 671 bool degenerateAB = degenerate_vector(quad[1] - quad[0]); |
640 bool degenerateBC = degenerate_vector(quad[2] - quad[1]); | 672 bool degenerateBC = degenerate_vector(quad[2] - quad[1]); |
641 if (degenerateAB & degenerateBC) { | 673 if (degenerateAB & degenerateBC) { |
642 return kPoint_ReductionType; | 674 return kPoint_ReductionType; |
643 } | 675 } |
644 if (degenerateAB | degenerateBC) { | 676 if (degenerateAB | degenerateBC) { |
645 return kLine_ReductionType; | 677 return kLine_ReductionType; |
646 } | 678 } |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
735 pts[2].fX + normalC.fX, pts[2].fY + normalC.fY, | 767 pts[2].fX + normalC.fX, pts[2].fY + normalC.fY, |
736 pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY); | 768 pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY); |
737 | 769 |
738 fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, | 770 fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, |
739 pts[2].fX - normalC.fX, pts[2].fY - normalC.fY, | 771 pts[2].fX - normalC.fX, pts[2].fY - normalC.fY, |
740 pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY); | 772 pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY); |
741 } | 773 } |
742 } | 774 } |
743 #endif | 775 #endif |
744 | 776 |
| 777 #ifdef SK_QUAD_STROKE_APPROXIMATION |
| 778 void SkPathStroker::conicTo(const SkPoint& pt1, const SkPoint& pt2, SkScalar wei
ght) { |
| 779 const SkConic conic(fPrevPt, pt1, pt2, weight); |
| 780 SkPoint reduction; |
| 781 ReductionType reductionType = CheckConicLinear(conic, &reduction); |
| 782 if (kPoint_ReductionType == reductionType) { |
| 783 return; |
| 784 } |
| 785 if (kLine_ReductionType == reductionType) { |
| 786 this->lineTo(pt2); |
| 787 return; |
| 788 } |
| 789 if (kDegenerate_ReductionType == reductionType) { |
| 790 this->lineTo(reduction); |
| 791 SkStrokerPriv::JoinProc saveJoiner = fJoiner; |
| 792 fJoiner = SkStrokerPriv::JoinFactory(SkPaint::kRound_Join); |
| 793 this->lineTo(pt2); |
| 794 fJoiner = saveJoiner; |
| 795 return; |
| 796 } |
| 797 SkASSERT(kQuad_ReductionType == reductionType); |
| 798 SkVector normalAB, unitAB, normalBC, unitBC; |
| 799 this->preJoinTo(pt1, &normalAB, &unitAB, false); |
| 800 SkQuadConstruct quadPts; |
| 801 this->init(kOuter_StrokeType, &quadPts, 0, 1); |
| 802 if (!this->conicStroke(conic, &quadPts)) { |
| 803 return; |
| 804 } |
| 805 this->init(kInner_StrokeType, &quadPts, 0, 1); |
| 806 if (!this->conicStroke(conic, &quadPts)) { |
| 807 return; |
| 808 } |
| 809 this->setConicEndNormal(conic, normalAB, unitAB, &normalBC, &unitBC); |
| 810 this->postJoinTo(pt2, normalBC, unitBC); |
| 811 } |
| 812 #endif |
| 813 |
745 void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) { | 814 void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) { |
746 #if QUAD_STROKE_APPROXIMATION | 815 #ifdef SK_QUAD_STROKE_APPROXIMATION |
747 const SkPoint quad[3] = { fPrevPt, pt1, pt2 }; | 816 const SkPoint quad[3] = { fPrevPt, pt1, pt2 }; |
748 SkPoint reduction; | 817 SkPoint reduction; |
749 ReductionType reductionType = CheckQuadLinear(quad, &reduction); | 818 ReductionType reductionType = CheckQuadLinear(quad, &reduction); |
750 if (kPoint_ReductionType == reductionType) { | 819 if (kPoint_ReductionType == reductionType) { |
751 return; | 820 return; |
752 } | 821 } |
753 if (kLine_ReductionType == reductionType) { | 822 if (kLine_ReductionType == reductionType) { |
754 this->lineTo(pt2); | 823 this->lineTo(pt2); |
755 return; | 824 return; |
756 } | 825 } |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
824 } else { | 893 } else { |
825 this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC, | 894 this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC, |
826 kMaxQuadSubdivide); | 895 kMaxQuadSubdivide); |
827 } | 896 } |
828 } | 897 } |
829 #endif | 898 #endif |
830 | 899 |
831 this->postJoinTo(pt2, normalBC, unitBC); | 900 this->postJoinTo(pt2, normalBC, unitBC); |
832 } | 901 } |
833 | 902 |
834 #if QUAD_STROKE_APPROXIMATION | 903 #ifdef SK_QUAD_STROKE_APPROXIMATION |
835 // Given a point on the curve and its derivative, scale the derivative by the ra
dius, and | 904 // Given a point on the curve and its derivative, scale the derivative by the ra
dius, and |
836 // compute the perpendicular point and its tangent. | 905 // compute the perpendicular point and its tangent. |
837 void SkPathStroker::setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt, | 906 void SkPathStroker::setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt, |
838 SkPoint* tangent) const { | 907 SkPoint* tangent) const { |
839 if (!dxy->setLength(fRadius)) { // consider moving double logic into SkPoin
t::setLength | 908 if (!dxy->setLength(fRadius)) { // consider moving double logic into SkPoin
t::setLength |
840 double xx = dxy->fX; | 909 double xx = dxy->fX; |
841 double yy = dxy->fY; | 910 double yy = dxy->fY; |
842 double dscale = fRadius / sqrt(xx * xx + yy * yy); | 911 double dscale = fRadius / sqrt(xx * xx + yy * yy); |
843 dxy->fX = SkDoubleToScalar(xx * dscale); | 912 dxy->fX = SkDoubleToScalar(xx * dscale); |
844 dxy->fY = SkDoubleToScalar(yy * dscale); | 913 dxy->fY = SkDoubleToScalar(yy * dscale); |
845 } | 914 } |
846 SkScalar axisFlip = SkIntToScalar(fStrokeType); // go opposite ways for out
er, inner | 915 SkScalar axisFlip = SkIntToScalar(fStrokeType); // go opposite ways for out
er, inner |
847 onPt->fX = tPt.fX + axisFlip * dxy->fY; | 916 onPt->fX = tPt.fX + axisFlip * dxy->fY; |
848 onPt->fY = tPt.fY - axisFlip * dxy->fX; | 917 onPt->fY = tPt.fY - axisFlip * dxy->fX; |
849 if (tangent) { | 918 if (tangent) { |
850 tangent->fX = onPt->fX + dxy->fX; | 919 tangent->fX = onPt->fX + dxy->fX; |
851 tangent->fY = onPt->fY + dxy->fY; | 920 tangent->fY = onPt->fY + dxy->fY; |
852 } | 921 } |
853 } | 922 } |
854 | 923 |
| 924 // Given a conic and t, return the point on curve, its perpendicular, and the pe
rpendicular tangent. |
| 925 // Returns false if the perpendicular could not be computed (because the derivat
ive collapsed to 0) |
| 926 bool SkPathStroker::conicPerpRay(const SkConic& conic, SkScalar t, SkPoint* tPt,
SkPoint* onPt, |
| 927 SkPoint* tangent) const { |
| 928 SkVector dxy; |
| 929 conic.evalAt(t, tPt, &dxy); |
| 930 if (dxy.fX == 0 && dxy.fY == 0) { |
| 931 dxy = conic.fPts[2] - conic.fPts[0]; |
| 932 } |
| 933 setRayPts(*tPt, &dxy, onPt, tangent); |
| 934 return true; |
| 935 } |
| 936 |
| 937 // Given a conic and a t range, find the start and end if they haven't been foun
d already. |
| 938 bool SkPathStroker::conicQuadEnds(const SkConic& conic, SkQuadConstruct* quadPts
) { |
| 939 if (!quadPts->fStartSet) { |
| 940 SkPoint conicStartPt; |
| 941 if (!this->conicPerpRay(conic, quadPts->fStartT, &conicStartPt, &quadPts
->fQuad[0], |
| 942 &quadPts->fTangentStart)) { |
| 943 return false; |
| 944 } |
| 945 quadPts->fStartSet = true; |
| 946 } |
| 947 if (!quadPts->fEndSet) { |
| 948 SkPoint conicEndPt; |
| 949 if (!this->conicPerpRay(conic, quadPts->fEndT, &conicEndPt, &quadPts->fQ
uad[2], |
| 950 &quadPts->fTangentEnd)) { |
| 951 return false; |
| 952 } |
| 953 quadPts->fEndSet = true; |
| 954 } |
| 955 return true; |
| 956 } |
| 957 |
| 958 |
855 // Given a cubic and t, return the point on curve, its perpendicular, and the pe
rpendicular tangent. | 959 // Given a cubic and t, return the point on curve, its perpendicular, and the pe
rpendicular tangent. |
856 // Returns false if the perpendicular could not be computed (because the derivat
ive collapsed to 0) | 960 // Returns false if the perpendicular could not be computed (because the derivat
ive collapsed to 0) |
857 bool SkPathStroker::cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tP
t, SkPoint* onPt, | 961 bool SkPathStroker::cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tP
t, SkPoint* onPt, |
858 SkPoint* tangent) const { | 962 SkPoint* tangent) const { |
859 SkVector dxy; | 963 SkVector dxy; |
860 SkEvalCubicAt(cubic, t, tPt, &dxy, NULL); | 964 SkEvalCubicAt(cubic, t, tPt, &dxy, NULL); |
861 if (dxy.fX == 0 && dxy.fY == 0) { | 965 if (dxy.fX == 0 && dxy.fY == 0) { |
862 if (SkScalarNearlyZero(t)) { | 966 if (SkScalarNearlyZero(t)) { |
863 dxy = cubic[2] - cubic[0]; | 967 dxy = cubic[2] - cubic[0]; |
864 } else if (SkScalarNearlyZero(1 - t)) { | 968 } else if (SkScalarNearlyZero(1 - t)) { |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
923 SkVector bLen = quadPts->fTangentEnd - end; | 1027 SkVector bLen = quadPts->fTangentEnd - end; |
924 SkScalar denom = aLen.cross(bLen); | 1028 SkScalar denom = aLen.cross(bLen); |
925 SkVector ab0 = start - end; | 1029 SkVector ab0 = start - end; |
926 SkScalar numerA = bLen.cross(ab0); | 1030 SkScalar numerA = bLen.cross(ab0); |
927 SkScalar numerB = aLen.cross(ab0); | 1031 SkScalar numerB = aLen.cross(ab0); |
928 if (!SkScalarNearlyZero(denom)) { | 1032 if (!SkScalarNearlyZero(denom)) { |
929 // if the perpendicular distances from the quad points to the opposite t
angent line | 1033 // if the perpendicular distances from the quad points to the opposite t
angent line |
930 // are small, a straight line is good enough | 1034 // are small, a straight line is good enough |
931 SkScalar dist1 = pt_to_line(start, end, quadPts->fTangentEnd); | 1035 SkScalar dist1 = pt_to_line(start, end, quadPts->fTangentEnd); |
932 SkScalar dist2 = pt_to_line(end, start, quadPts->fTangentStart); | 1036 SkScalar dist2 = pt_to_line(end, start, quadPts->fTangentStart); |
933 if (SkTMax(dist1, dist2) <= fError * fError) { | |
934 return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, | |
935 "SkTMax(dist1=%g, dist2=%g) <= fError * fError", dist1, dist
2); | |
936 } | |
937 if ((numerA >= 0) != (numerB >= 0)) { | 1037 if ((numerA >= 0) != (numerB >= 0)) { |
938 if (kCtrlPt_RayType == intersectRayType) { | 1038 if (kCtrlPt_RayType == intersectRayType) { |
939 numerA /= denom; | 1039 numerA /= denom; |
940 SkPoint* ctrlPt = &quadPts->fQuad[1]; | 1040 SkPoint* ctrlPt = &quadPts->fQuad[1]; |
941 ctrlPt->fX = start.fX * (1 - numerA) + quadPts->fTangentStart.fX
* numerA; | 1041 ctrlPt->fX = start.fX * (1 - numerA) + quadPts->fTangentStart.fX
* numerA; |
942 ctrlPt->fY = start.fY * (1 - numerA) + quadPts->fTangentStart.fY
* numerA; | 1042 ctrlPt->fY = start.fY * (1 - numerA) + quadPts->fTangentStart.fY
* numerA; |
943 } | 1043 } |
944 return STROKER_RESULT(kQuad_ResultType, depth, quadPts, | 1044 return STROKER_RESULT(kQuad_ResultType, depth, quadPts, |
945 "(numerA=%g >= 0) != (numerB=%g >= 0)", numerA, numerB); | 1045 "(numerA=%g >= 0) != (numerB=%g >= 0)", numerA, numerB); |
946 } | 1046 } |
| 1047 if (SkTMax(dist1, dist2) <= fInvResScaleSquared) { |
| 1048 return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, |
| 1049 "SkTMax(dist1=%g, dist2=%g) <= fInvResScaleSquared", dist1,
dist2); |
| 1050 } |
947 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, | 1051 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, |
948 "(numerA=%g >= 0) == (numerB=%g >= 0)", numerA, numerB); | 1052 "(numerA=%g >= 0) == (numerB=%g >= 0)", numerA, numerB); |
949 } else { // if the lines are parallel, straight line is good enough | 1053 } else { // if the lines are parallel, straight line is good enough |
950 return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, | 1054 return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, |
951 "SkScalarNearlyZero(denom=%g)", denom); | 1055 "SkScalarNearlyZero(denom=%g)", denom); |
952 } | 1056 } |
953 } | 1057 } |
954 | 1058 |
955 // Given a cubic and a t-range, determine if the stroke can be described by a qu
adratic. | 1059 // Given a cubic and a t-range, determine if the stroke can be described by a qu
adratic. |
956 SkPathStroker::ResultType SkPathStroker::tangentsMeet(const SkPoint cubic[4], | 1060 SkPathStroker::ResultType SkPathStroker::tangentsMeet(const SkPoint cubic[4], |
(...skipping 15 matching lines...) Expand all Loading... |
972 SkScalar B = r[1]; | 1076 SkScalar B = r[1]; |
973 SkScalar C = r[0]; | 1077 SkScalar C = r[0]; |
974 A += C - 2 * B; // A = a - 2*b + c | 1078 A += C - 2 * B; // A = a - 2*b + c |
975 B -= C; // B = -(b - c) | 1079 B -= C; // B = -(b - c) |
976 return SkFindUnitQuadRoots(A, 2 * B, C, roots); | 1080 return SkFindUnitQuadRoots(A, 2 * B, C, roots); |
977 } | 1081 } |
978 | 1082 |
979 // Return true if the point is close to the bounds of the quad. This is used as
a quick reject. | 1083 // Return true if the point is close to the bounds of the quad. This is used as
a quick reject. |
980 bool SkPathStroker::ptInQuadBounds(const SkPoint quad[3], const SkPoint& pt) con
st { | 1084 bool SkPathStroker::ptInQuadBounds(const SkPoint quad[3], const SkPoint& pt) con
st { |
981 SkScalar xMin = SkTMin(SkTMin(quad[0].fX, quad[1].fX), quad[2].fX); | 1085 SkScalar xMin = SkTMin(SkTMin(quad[0].fX, quad[1].fX), quad[2].fX); |
982 if (pt.fX + fError < xMin) { | 1086 if (pt.fX + fInvResScale < xMin) { |
983 return false; | 1087 return false; |
984 } | 1088 } |
985 SkScalar xMax = SkTMax(SkTMax(quad[0].fX, quad[1].fX), quad[2].fX); | 1089 SkScalar xMax = SkTMax(SkTMax(quad[0].fX, quad[1].fX), quad[2].fX); |
986 if (pt.fX - fError > xMax) { | 1090 if (pt.fX - fInvResScale > xMax) { |
987 return false; | 1091 return false; |
988 } | 1092 } |
989 SkScalar yMin = SkTMin(SkTMin(quad[0].fY, quad[1].fY), quad[2].fY); | 1093 SkScalar yMin = SkTMin(SkTMin(quad[0].fY, quad[1].fY), quad[2].fY); |
990 if (pt.fY + fError < yMin) { | 1094 if (pt.fY + fInvResScale < yMin) { |
991 return false; | 1095 return false; |
992 } | 1096 } |
993 SkScalar yMax = SkTMax(SkTMax(quad[0].fY, quad[1].fY), quad[2].fY); | 1097 SkScalar yMax = SkTMax(SkTMax(quad[0].fY, quad[1].fY), quad[2].fY); |
994 if (pt.fY - fError > yMax) { | 1098 if (pt.fY - fInvResScale > yMax) { |
995 return false; | 1099 return false; |
996 } | 1100 } |
997 return true; | 1101 return true; |
998 } | 1102 } |
999 | 1103 |
1000 static bool points_within_dist(const SkPoint& nearPt, const SkPoint& farPt, SkSc
alar limit) { | 1104 static bool points_within_dist(const SkPoint& nearPt, const SkPoint& farPt, SkSc
alar limit) { |
1001 return nearPt.distanceToSqd(farPt) <= limit * limit; | 1105 return nearPt.distanceToSqd(farPt) <= limit * limit; |
1002 } | 1106 } |
1003 | 1107 |
1004 static bool sharp_angle(const SkPoint quad[3]) { | 1108 static bool sharp_angle(const SkPoint quad[3]) { |
(...skipping 10 matching lines...) Expand all Loading... |
1015 } | 1119 } |
1016 SkScalar dot = smaller.dot(larger); | 1120 SkScalar dot = smaller.dot(larger); |
1017 return dot > 0; | 1121 return dot > 0; |
1018 } | 1122 } |
1019 | 1123 |
1020 SkPathStroker::ResultType SkPathStroker::strokeCloseEnough(const SkPoint stroke[
3], | 1124 SkPathStroker::ResultType SkPathStroker::strokeCloseEnough(const SkPoint stroke[
3], |
1021 const SkPoint ray[2], SkQuadConstruct* quadPts STROKER_DEBUG_PARAMS(int
depth)) const { | 1125 const SkPoint ray[2], SkQuadConstruct* quadPts STROKER_DEBUG_PARAMS(int
depth)) const { |
1022 SkPoint strokeMid; | 1126 SkPoint strokeMid; |
1023 SkEvalQuadAt(stroke, SK_ScalarHalf, &strokeMid); | 1127 SkEvalQuadAt(stroke, SK_ScalarHalf, &strokeMid); |
1024 // measure the distance from the curve to the quad-stroke midpoint, compare
to radius | 1128 // measure the distance from the curve to the quad-stroke midpoint, compare
to radius |
1025 if (points_within_dist(ray[0], strokeMid, fError)) { // if the difference i
s small | 1129 if (points_within_dist(ray[0], strokeMid, fInvResScaleSquared)) { // if the
difference is small |
1026 if (sharp_angle(quadPts->fQuad)) { | 1130 if (sharp_angle(quadPts->fQuad)) { |
1027 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, | 1131 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, |
1028 "sharp_angle (1) =%g,%g, %g,%g, %g,%g", | 1132 "sharp_angle (1) =%g,%g, %g,%g, %g,%g", |
1029 quadPts->fQuad[0].fX, quadPts->fQuad[0].fY, | 1133 quadPts->fQuad[0].fX, quadPts->fQuad[0].fY, |
1030 quadPts->fQuad[1].fX, quadPts->fQuad[1].fY, | 1134 quadPts->fQuad[1].fX, quadPts->fQuad[1].fY, |
1031 quadPts->fQuad[2].fX, quadPts->fQuad[2].fY); | 1135 quadPts->fQuad[2].fX, quadPts->fQuad[2].fY); |
1032 } | 1136 } |
1033 return STROKER_RESULT(kQuad_ResultType, depth, quadPts, | 1137 return STROKER_RESULT(kQuad_ResultType, depth, quadPts, |
1034 "points_within_dist(ray[0]=%g,%g, strokeMid=%g,%g, fError)", | 1138 "points_within_dist(ray[0]=%g,%g, strokeMid=%g,%g, fInvResScaleS
quared=%g)", |
1035 ray[0].fX, ray[0].fY, strokeMid.fX, strokeMid.fY); | 1139 ray[0].fX, ray[0].fY, strokeMid.fX, strokeMid.fY, fInvResScaleSq
uared); |
1036 } | 1140 } |
1037 // measure the distance to quad's bounds (quick reject) | 1141 // measure the distance to quad's bounds (quick reject) |
1038 // an alternative : look for point in triangle | 1142 // an alternative : look for point in triangle |
1039 if (!ptInQuadBounds(stroke, ray[0])) { // if far, subdivide | 1143 if (!ptInQuadBounds(stroke, ray[0])) { // if far, subdivide |
1040 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, | 1144 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, |
1041 "!pt_in_quad_bounds(stroke=(%g,%g %g,%g %g,%g), ray[0]=%g,%g)", | 1145 "!pt_in_quad_bounds(stroke=(%g,%g %g,%g %g,%g), ray[0]=%g,%g)", |
1042 stroke[0].fX, stroke[0].fY, stroke[1].fX, stroke[1].fY, stroke[2
].fX, stroke[2].fY, | 1146 stroke[0].fX, stroke[0].fY, stroke[1].fX, stroke[1].fY, stroke[2
].fX, stroke[2].fY, |
1043 ray[0].fX, ray[0].fY); | 1147 ray[0].fX, ray[0].fY); |
1044 } | 1148 } |
1045 // measure the curve ray distance to the quad-stroke | 1149 // measure the curve ray distance to the quad-stroke |
1046 SkScalar roots[2]; | 1150 SkScalar roots[2]; |
1047 int rootCount = intersect_quad_ray(ray, stroke, roots); | 1151 int rootCount = intersect_quad_ray(ray, stroke, roots); |
1048 if (rootCount != 1) { | 1152 if (rootCount != 1) { |
1049 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, | 1153 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, |
1050 "rootCount=%d != 1", rootCount); | 1154 "rootCount=%d != 1", rootCount); |
1051 } | 1155 } |
1052 SkPoint quadPt; | 1156 SkPoint quadPt; |
1053 SkEvalQuadAt(stroke, roots[0], &quadPt); | 1157 SkEvalQuadAt(stroke, roots[0], &quadPt); |
1054 SkScalar error = fError * (SK_Scalar1 - SkScalarAbs(roots[0] - 0.5f) * 2); | 1158 SkScalar error = fInvResScale * (SK_Scalar1 - SkScalarAbs(roots[0] - 0.5f) *
2); |
1055 if (points_within_dist(ray[0], quadPt, error)) { // if the difference is sm
all, we're done | 1159 if (points_within_dist(ray[0], quadPt, error)) { // if the difference is sm
all, we're done |
1056 if (sharp_angle(quadPts->fQuad)) { | 1160 if (sharp_angle(quadPts->fQuad)) { |
1057 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, | 1161 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, |
1058 "sharp_angle (2) =%g,%g, %g,%g, %g,%g", | 1162 "sharp_angle (2) =%g,%g, %g,%g, %g,%g", |
1059 quadPts->fQuad[0].fX, quadPts->fQuad[0].fY, | 1163 quadPts->fQuad[0].fX, quadPts->fQuad[0].fY, |
1060 quadPts->fQuad[1].fX, quadPts->fQuad[1].fY, | 1164 quadPts->fQuad[1].fX, quadPts->fQuad[1].fY, |
1061 quadPts->fQuad[2].fX, quadPts->fQuad[2].fY); | 1165 quadPts->fQuad[2].fX, quadPts->fQuad[2].fY); |
1062 } | 1166 } |
1063 return STROKER_RESULT(kQuad_ResultType, depth, quadPts, | 1167 return STROKER_RESULT(kQuad_ResultType, depth, quadPts, |
1064 "points_within_dist(ray[0]=%g,%g, quadPt=%g,%g, error=%g)", | 1168 "points_within_dist(ray[0]=%g,%g, quadPt=%g,%g, error=%g)", |
1065 ray[0].fX, ray[0].fY, quadPt.fX, quadPt.fY, error); | 1169 ray[0].fX, ray[0].fY, quadPt.fX, quadPt.fY, error); |
1066 } | 1170 } |
1067 // otherwise, subdivide | 1171 // otherwise, subdivide |
1068 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, "%s", "fall through
"); | 1172 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, "%s", "fall through
"); |
1069 } | 1173 } |
1070 | 1174 |
1071 SkPathStroker::ResultType SkPathStroker::compareQuadCubic(const SkPoint cubic[4]
, | 1175 SkPathStroker::ResultType SkPathStroker::compareQuadCubic(const SkPoint cubic[4]
, |
1072 SkQuadConstruct* quadPts) { | 1176 SkQuadConstruct* quadPts) { |
1073 // get the quadratic approximation of the stroke | 1177 // get the quadratic approximation of the stroke |
1074 if (!this->cubicQuadEnds(cubic, quadPts)) { | 1178 if (!this->cubicQuadEnds(cubic, quadPts)) { |
1075 return kNormalError_ResultType; | 1179 return kNormalError_ResultType; |
1076 } | 1180 } |
1077 ResultType resultType = intersectRay(quadPts, kCtrlPt_RayType | 1181 ResultType resultType = intersectRay(quadPts, kCtrlPt_RayType |
1078 STROKER_DEBUG_PARAMS(fRecursionDepth) ); | 1182 STROKER_DEBUG_PARAMS(fRecursionDepth) ); |
1079 if (resultType != kQuad_ResultType) { | 1183 if (resultType != kQuad_ResultType) { |
1080 return resultType; | 1184 return resultType; |
1081 } | 1185 } |
1082 // project a ray from the curve to the stroke | 1186 // project a ray from the curve to the stroke |
1083 SkPoint ray[2]; // point near midpoint on quad, midpoint on cubic | 1187 SkPoint ray[2]; // points near midpoint on quad, midpoint on cubic |
1084 if (!this->cubicPerpRay(cubic, quadPts->fMidT, &ray[1], &ray[0], NULL)) { | 1188 if (!this->cubicPerpRay(cubic, quadPts->fMidT, &ray[1], &ray[0], NULL)) { |
1085 return kNormalError_ResultType; | 1189 return kNormalError_ResultType; |
1086 } | 1190 } |
1087 return strokeCloseEnough(quadPts->fQuad, ray, quadPts STROKER_DEBUG_PARAMS(
fRecursionDepth)); | 1191 return strokeCloseEnough(quadPts->fQuad, ray, quadPts STROKER_DEBUG_PARAMS(
fRecursionDepth)); |
1088 } | 1192 } |
1089 | 1193 |
1090 // if false is returned, caller splits quadratic approximation | 1194 SkPathStroker::ResultType SkPathStroker::compareQuadConic(const SkConic& conic, |
| 1195 SkQuadConstruct* quadPts) { |
| 1196 // get the quadratic approximation of the stroke |
| 1197 if (!this->conicQuadEnds(conic, quadPts)) { |
| 1198 return kNormalError_ResultType; |
| 1199 } |
| 1200 ResultType resultType = intersectRay(quadPts, kCtrlPt_RayType |
| 1201 STROKER_DEBUG_PARAMS(fRecursionDepth) ); |
| 1202 if (resultType != kQuad_ResultType) { |
| 1203 return resultType; |
| 1204 } |
| 1205 // project a ray from the curve to the stroke |
| 1206 SkPoint ray[2]; // points near midpoint on quad, midpoint on conic |
| 1207 if (!this->conicPerpRay(conic, quadPts->fMidT, &ray[1], &ray[0], NULL)) { |
| 1208 return kNormalError_ResultType; |
| 1209 } |
| 1210 return strokeCloseEnough(quadPts->fQuad, ray, quadPts STROKER_DEBUG_PARAMS(
fRecursionDepth)); |
| 1211 } |
| 1212 |
1091 SkPathStroker::ResultType SkPathStroker::compareQuadQuad(const SkPoint quad[3], | 1213 SkPathStroker::ResultType SkPathStroker::compareQuadQuad(const SkPoint quad[3], |
1092 SkQuadConstruct* quadPts) { | 1214 SkQuadConstruct* quadPts) { |
1093 // get the quadratic approximation of the stroke | 1215 // get the quadratic approximation of the stroke |
1094 if (!quadPts->fStartSet) { | 1216 if (!quadPts->fStartSet) { |
1095 SkPoint quadStartPt; | 1217 SkPoint quadStartPt; |
1096 this->quadPerpRay(quad, quadPts->fStartT, &quadStartPt, &quadPts->fQuad[
0], | 1218 this->quadPerpRay(quad, quadPts->fStartT, &quadStartPt, &quadPts->fQuad[
0], |
1097 &quadPts->fTangentStart); | 1219 &quadPts->fTangentStart); |
1098 quadPts->fStartSet = true; | 1220 quadPts->fStartSet = true; |
1099 } | 1221 } |
1100 if (!quadPts->fEndSet) { | 1222 if (!quadPts->fEndSet) { |
(...skipping 18 matching lines...) Expand all Loading... |
1119 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; | 1241 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; |
1120 path->lineTo(quad[2].fX, quad[2].fY); | 1242 path->lineTo(quad[2].fX, quad[2].fY); |
1121 } | 1243 } |
1122 | 1244 |
1123 bool SkPathStroker::cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct
* quadPts) const { | 1245 bool SkPathStroker::cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct
* quadPts) const { |
1124 SkPoint strokeMid; | 1246 SkPoint strokeMid; |
1125 if (!cubicQuadMid(cubic, quadPts, &strokeMid)) { | 1247 if (!cubicQuadMid(cubic, quadPts, &strokeMid)) { |
1126 return false; | 1248 return false; |
1127 } | 1249 } |
1128 SkScalar dist = pt_to_line(strokeMid, quadPts->fQuad[0], quadPts->fQuad[2]); | 1250 SkScalar dist = pt_to_line(strokeMid, quadPts->fQuad[0], quadPts->fQuad[2]); |
1129 return dist < fError * fError; | 1251 return dist < fInvResScaleSquared; |
1130 } | 1252 } |
1131 | 1253 |
1132 bool SkPathStroker::cubicStroke(const SkPoint cubic[4], SkQuadConstruct* quadPts
) { | 1254 bool SkPathStroker::cubicStroke(const SkPoint cubic[4], SkQuadConstruct* quadPts
) { |
1133 if (!fFoundTangents) { | 1255 if (!fFoundTangents) { |
1134 ResultType resultType = this->tangentsMeet(cubic, quadPts); | 1256 ResultType resultType = this->tangentsMeet(cubic, quadPts); |
1135 if (kQuad_ResultType != resultType) { | 1257 if (kQuad_ResultType != resultType) { |
1136 if (kNormalError_ResultType == resultType) { | 1258 if (kNormalError_ResultType == resultType) { |
1137 return false; | 1259 return false; |
1138 } | 1260 } |
1139 if ((kDegenerate_ResultType == resultType | 1261 if ((kDegenerate_ResultType == resultType |
1140 || points_within_dist(quadPts->fQuad[0], quadPts->fQuad[2],
fError)) | 1262 || points_within_dist(quadPts->fQuad[0], quadPts->fQuad[2], |
1141 && cubicMidOnLine(cubic, quadPts)) { | 1263 fInvResScaleSquared)) && cubicMidOnLine(cubic, quadPts)) { |
1142 addDegenerateLine(quadPts); | 1264 addDegenerateLine(quadPts); |
1143 return true; | 1265 return true; |
1144 } | 1266 } |
1145 } else { | 1267 } else { |
1146 fFoundTangents = true; | 1268 fFoundTangents = true; |
1147 } | 1269 } |
1148 } | 1270 } |
1149 if (fFoundTangents) { | 1271 if (fFoundTangents) { |
1150 ResultType resultType = this->compareQuadCubic(cubic, quadPts); | 1272 ResultType resultType = this->compareQuadCubic(cubic, quadPts); |
1151 if (kQuad_ResultType == resultType) { | 1273 if (kQuad_ResultType == resultType) { |
(...skipping 30 matching lines...) Expand all Loading... |
1182 addDegenerateLine(quadPts); | 1304 addDegenerateLine(quadPts); |
1183 return true; | 1305 return true; |
1184 } | 1306 } |
1185 if (!this->cubicStroke(cubic, &half)) { | 1307 if (!this->cubicStroke(cubic, &half)) { |
1186 return false; | 1308 return false; |
1187 } | 1309 } |
1188 --fRecursionDepth; | 1310 --fRecursionDepth; |
1189 return true; | 1311 return true; |
1190 } | 1312 } |
1191 | 1313 |
| 1314 bool SkPathStroker::conicStroke(const SkConic& conic, SkQuadConstruct* quadPts)
{ |
| 1315 ResultType resultType = this->compareQuadConic(conic, quadPts); |
| 1316 if (kQuad_ResultType == resultType) { |
| 1317 const SkPoint* stroke = quadPts->fQuad; |
| 1318 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; |
| 1319 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY); |
| 1320 return true; |
| 1321 } |
| 1322 if (kDegenerate_ResultType == resultType) { |
| 1323 addDegenerateLine(quadPts); |
| 1324 return true; |
| 1325 } |
| 1326 SkDEBUGCODE(gMaxRecursion[kConic_RecursiveLimit] = SkTMax(gMaxRecursion[kCon
ic_RecursiveLimit], |
| 1327 fRecursionDepth + 1)); |
| 1328 if (++fRecursionDepth > kRecursiveLimits[kConic_RecursiveLimit]) { |
| 1329 return false; // just abort if projected quad isn't representable |
| 1330 } |
| 1331 SkQuadConstruct half; |
| 1332 (void) half.initWithStart(quadPts); |
| 1333 if (!this->conicStroke(conic, &half)) { |
| 1334 return false; |
| 1335 } |
| 1336 (void) half.initWithEnd(quadPts); |
| 1337 if (!this->conicStroke(conic, &half)) { |
| 1338 return false; |
| 1339 } |
| 1340 --fRecursionDepth; |
| 1341 return true; |
| 1342 } |
| 1343 |
1192 bool SkPathStroker::quadStroke(const SkPoint quad[3], SkQuadConstruct* quadPts)
{ | 1344 bool SkPathStroker::quadStroke(const SkPoint quad[3], SkQuadConstruct* quadPts)
{ |
1193 ResultType resultType = this->compareQuadQuad(quad, quadPts); | 1345 ResultType resultType = this->compareQuadQuad(quad, quadPts); |
1194 if (kQuad_ResultType == resultType) { | 1346 if (kQuad_ResultType == resultType) { |
1195 const SkPoint* stroke = quadPts->fQuad; | 1347 const SkPoint* stroke = quadPts->fQuad; |
1196 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; | 1348 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; |
1197 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY); | 1349 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY); |
1198 return true; | 1350 return true; |
1199 } | 1351 } |
1200 if (kDegenerate_ResultType == resultType) { | 1352 if (kDegenerate_ResultType == resultType) { |
1201 addDegenerateLine(quadPts); | 1353 addDegenerateLine(quadPts); |
(...skipping 14 matching lines...) Expand all Loading... |
1216 return false; | 1368 return false; |
1217 } | 1369 } |
1218 --fRecursionDepth; | 1370 --fRecursionDepth; |
1219 return true; | 1371 return true; |
1220 } | 1372 } |
1221 | 1373 |
1222 #endif | 1374 #endif |
1223 | 1375 |
1224 void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2, | 1376 void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2, |
1225 const SkPoint& pt3) { | 1377 const SkPoint& pt3) { |
1226 #if QUAD_STROKE_APPROXIMATION | 1378 #ifdef SK_QUAD_STROKE_APPROXIMATION |
1227 const SkPoint cubic[4] = { fPrevPt, pt1, pt2, pt3 }; | 1379 const SkPoint cubic[4] = { fPrevPt, pt1, pt2, pt3 }; |
1228 SkPoint reduction[3]; | 1380 SkPoint reduction[3]; |
1229 const SkPoint* tangentPt; | 1381 const SkPoint* tangentPt; |
1230 ReductionType reductionType = CheckCubicLinear(cubic, reduction, &tangentPt)
; | 1382 ReductionType reductionType = CheckCubicLinear(cubic, reduction, &tangentPt)
; |
1231 if (kPoint_ReductionType == reductionType) { | 1383 if (kPoint_ReductionType == reductionType) { |
1232 return; | 1384 return; |
1233 } | 1385 } |
1234 if (kLine_ReductionType == reductionType) { | 1386 if (kLine_ReductionType == reductionType) { |
1235 this->lineTo(pt3); | 1387 this->lineTo(pt3); |
1236 return; | 1388 return; |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1342 } | 1494 } |
1343 | 1495 |
1344 SkStroke::SkStroke(const SkPaint& p, SkScalar width) { | 1496 SkStroke::SkStroke(const SkPaint& p, SkScalar width) { |
1345 fWidth = width; | 1497 fWidth = width; |
1346 fMiterLimit = p.getStrokeMiter(); | 1498 fMiterLimit = p.getStrokeMiter(); |
1347 fCap = (uint8_t)p.getStrokeCap(); | 1499 fCap = (uint8_t)p.getStrokeCap(); |
1348 fJoin = (uint8_t)p.getStrokeJoin(); | 1500 fJoin = (uint8_t)p.getStrokeJoin(); |
1349 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); | 1501 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); |
1350 } | 1502 } |
1351 | 1503 |
1352 #if QUAD_STROKE_APPROXIMATION | |
1353 void SkStroke::setError(SkScalar error) { | |
1354 SkASSERT(error > 0); | |
1355 fError = error; | |
1356 } | |
1357 #endif | |
1358 | |
1359 void SkStroke::setWidth(SkScalar width) { | 1504 void SkStroke::setWidth(SkScalar width) { |
1360 SkASSERT(width >= 0); | 1505 SkASSERT(width >= 0); |
1361 fWidth = width; | 1506 fWidth = width; |
1362 } | 1507 } |
1363 | 1508 |
1364 void SkStroke::setMiterLimit(SkScalar miterLimit) { | 1509 void SkStroke::setMiterLimit(SkScalar miterLimit) { |
1365 SkASSERT(miterLimit >= 0); | 1510 SkASSERT(miterLimit >= 0); |
1366 fMiterLimit = miterLimit; | 1511 fMiterLimit = miterLimit; |
1367 } | 1512 } |
1368 | 1513 |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1425 // our answer should preserve the inverseness of the src | 1570 // our answer should preserve the inverseness of the src |
1426 if (src.isInverseFillType()) { | 1571 if (src.isInverseFillType()) { |
1427 SkASSERT(!dst->isInverseFillType()); | 1572 SkASSERT(!dst->isInverseFillType()); |
1428 dst->toggleInverseFillType(); | 1573 dst->toggleInverseFillType(); |
1429 } | 1574 } |
1430 return; | 1575 return; |
1431 } | 1576 } |
1432 } | 1577 } |
1433 | 1578 |
1434 SkAutoConicToQuads converter; | 1579 SkAutoConicToQuads converter; |
| 1580 #ifndef SK_QUAD_STROKE_APPROXIMATION |
1435 const SkScalar conicTol = SK_Scalar1 / 4 / fResScale; | 1581 const SkScalar conicTol = SK_Scalar1 / 4 / fResScale; |
1436 | 1582 #endif |
1437 #if QUAD_STROKE_APPROXIMATION | |
1438 SkPathStroker stroker(src, radius, fMiterLimit, fError, this->getCap(), | |
1439 this->getJoin(), fResScale); | |
1440 #else | |
1441 SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(), this->getJ
oin(), fResScale); | 1583 SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(), this->getJ
oin(), fResScale); |
1442 #endif | |
1443 SkPath::Iter iter(src, false); | 1584 SkPath::Iter iter(src, false); |
1444 SkPath::Verb lastSegment = SkPath::kMove_Verb; | 1585 SkPath::Verb lastSegment = SkPath::kMove_Verb; |
1445 | 1586 |
1446 for (;;) { | 1587 for (;;) { |
1447 SkPoint pts[4]; | 1588 SkPoint pts[4]; |
1448 switch (iter.next(pts, false)) { | 1589 switch (iter.next(pts, false)) { |
1449 case SkPath::kMove_Verb: | 1590 case SkPath::kMove_Verb: |
1450 stroker.moveTo(pts[0]); | 1591 stroker.moveTo(pts[0]); |
1451 break; | 1592 break; |
1452 case SkPath::kLine_Verb: | 1593 case SkPath::kLine_Verb: |
1453 stroker.lineTo(pts[1]); | 1594 stroker.lineTo(pts[1]); |
1454 lastSegment = SkPath::kLine_Verb; | 1595 lastSegment = SkPath::kLine_Verb; |
1455 break; | 1596 break; |
1456 case SkPath::kQuad_Verb: | 1597 case SkPath::kQuad_Verb: |
1457 stroker.quadTo(pts[1], pts[2]); | 1598 stroker.quadTo(pts[1], pts[2]); |
1458 lastSegment = SkPath::kQuad_Verb; | 1599 lastSegment = SkPath::kQuad_Verb; |
1459 break; | 1600 break; |
1460 case SkPath::kConic_Verb: { | 1601 case SkPath::kConic_Verb: { |
| 1602 #ifdef SK_QUAD_STROKE_APPROXIMATION |
| 1603 stroker.conicTo(pts[1], pts[2], iter.conicWeight()); |
| 1604 lastSegment = SkPath::kConic_Verb; |
| 1605 break; |
| 1606 #else |
1461 // todo: if we had maxcurvature for conics, perhaps we should | 1607 // todo: if we had maxcurvature for conics, perhaps we should |
1462 // natively extrude the conic instead of converting to quads. | 1608 // natively extrude the conic instead of converting to quads. |
1463 const SkPoint* quadPts = | 1609 const SkPoint* quadPts = |
1464 converter.computeQuads(pts, iter.conicWeight(), conicTol); | 1610 converter.computeQuads(pts, iter.conicWeight(), conicTol); |
1465 for (int i = 0; i < converter.countQuads(); ++i) { | 1611 for (int i = 0; i < converter.countQuads(); ++i) { |
1466 stroker.quadTo(quadPts[1], quadPts[2]); | 1612 stroker.quadTo(quadPts[1], quadPts[2]); |
1467 quadPts += 2; | 1613 quadPts += 2; |
1468 } | 1614 } |
1469 lastSegment = SkPath::kQuad_Verb; | 1615 lastSegment = SkPath::kQuad_Verb; |
| 1616 #endif |
1470 } break; | 1617 } break; |
1471 case SkPath::kCubic_Verb: | 1618 case SkPath::kCubic_Verb: |
1472 stroker.cubicTo(pts[1], pts[2], pts[3]); | 1619 stroker.cubicTo(pts[1], pts[2], pts[3]); |
1473 lastSegment = SkPath::kCubic_Verb; | 1620 lastSegment = SkPath::kCubic_Verb; |
1474 break; | 1621 break; |
1475 case SkPath::kClose_Verb: | 1622 case SkPath::kClose_Verb: |
1476 stroker.close(lastSegment == SkPath::kLine_Verb); | 1623 stroker.close(lastSegment == SkPath::kLine_Verb); |
1477 break; | 1624 break; |
1478 case SkPath::kDone_Verb: | 1625 case SkPath::kDone_Verb: |
1479 goto DONE; | 1626 goto DONE; |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1589 default: | 1736 default: |
1590 break; | 1737 break; |
1591 } | 1738 } |
1592 | 1739 |
1593 if (fWidth < SkMinScalar(rw, rh) && !fDoFill) { | 1740 if (fWidth < SkMinScalar(rw, rh) && !fDoFill) { |
1594 r = rect; | 1741 r = rect; |
1595 r.inset(radius, radius); | 1742 r.inset(radius, radius); |
1596 dst->addRect(r, reverse_direction(dir)); | 1743 dst->addRect(r, reverse_direction(dir)); |
1597 } | 1744 } |
1598 } | 1745 } |
OLD | NEW |