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 |
| 13 |
| 14 enum { |
| 15 kTangent_RecursiveLimit, |
| 16 kCubic_RecursiveLimit, |
| 17 kQuad_RecursiveLimit |
| 18 }; |
| 19 |
| 20 // 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 quads : 11 |
| 23 static const int kRecursiveLimits[] = { 5*3, 26*3, 11*3 }; // 3x limits see
n in practical tests |
| 24 |
| 25 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kRecursiveLimits) == kQuad_RecursiveLimit +
1, |
| 26 recursive_limits_mismatch); |
| 27 |
| 28 #ifdef SK_DEBUG // enables tweaking these values at runtime from SampleApp |
| 29 bool gDebugStrokerErrorSet = false; |
| 30 SkScalar gDebugStrokerError; |
| 31 |
| 32 int gMaxRecursion[SK_ARRAY_COUNT(kRecursiveLimits)] = { 0 }; |
| 33 #endif |
| 34 #ifndef DEBUG_QUAD_STROKER |
| 35 #define DEBUG_QUAD_STROKER 0 |
| 36 #endif |
| 37 |
| 38 #if DEBUG_QUAD_STROKER |
| 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 */ |
| 41 #define STROKER_RESULT(resultType, depth, quadPts, format, ...) \ |
| 42 SkDebugf("[%d] %s " format "\n", depth, __FUNCTION__, __VA_ARGS_
_), \ |
| 43 SkDebugf(" " #resultType " t=(%g,%g)\n", quadPts->fStartT, quad
Pts->fEndT), \ |
| 44 resultType |
| 45 #define STROKER_DEBUG_PARAMS(...) , __VA_ARGS__ |
| 46 #else |
| 47 #define STROKER_RESULT(resultType, depth, quadPts, format, ...) \ |
| 48 resultType |
| 49 #define STROKER_DEBUG_PARAMS(...) |
| 50 #endif |
| 51 |
| 52 #endif |
| 53 |
12 #define kMaxQuadSubdivide 5 | 54 #define kMaxQuadSubdivide 5 |
13 #define kMaxCubicSubdivide 7 | 55 #define kMaxCubicSubdivide 7 |
14 | 56 |
15 static inline bool degenerate_vector(const SkVector& v) { | 57 static inline bool degenerate_vector(const SkVector& v) { |
16 return !SkPoint::CanNormalize(v.fX, v.fY); | 58 return !SkPoint::CanNormalize(v.fX, v.fY); |
17 } | 59 } |
18 | 60 |
| 61 #if !QUAD_STROKE_APPROXIMATION |
19 static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) { | 62 static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) { |
20 /* root2/2 is a 45-degree angle | 63 /* root2/2 is a 45-degree angle |
21 make this constant bigger for more subdivisions (but not >= 1) | 64 make this constant bigger for more subdivisions (but not >= 1) |
22 */ | 65 */ |
23 static const SkScalar kFlatEnoughNormalDotProd = | 66 static const SkScalar kFlatEnoughNormalDotProd = |
24 SK_ScalarSqrt2/2 + SK_Scalar1/10; | 67 SK_ScalarSqrt2/2 + SK_Scalar1/10; |
25 | 68 |
26 SkASSERT(kFlatEnoughNormalDotProd > 0 && | 69 SkASSERT(kFlatEnoughNormalDotProd > 0 && |
27 kFlatEnoughNormalDotProd < SK_Scalar1); | 70 kFlatEnoughNormalDotProd < SK_Scalar1); |
28 | 71 |
29 return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd; | 72 return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd; |
30 } | 73 } |
31 | 74 |
32 static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) { | 75 static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) { |
33 // if the dot-product is -1, then we are definitely too pinchy. We tweak | 76 // if the dot-product is -1, then we are definitely too pinchy. We tweak |
34 // that by an epsilon to ensure we have significant bits in our test | 77 // that by an epsilon to ensure we have significant bits in our test |
35 static const int kMinSigBitsForDot = 8; | 78 static const int kMinSigBitsForDot = 8; |
36 static const SkScalar kDotEpsilon = FLT_EPSILON * (1 << kMinSigBitsForDot); | 79 static const SkScalar kDotEpsilon = FLT_EPSILON * (1 << kMinSigBitsForDot); |
37 static const SkScalar kTooPinchyNormalDotProd = kDotEpsilon - 1; | 80 static const SkScalar kTooPinchyNormalDotProd = kDotEpsilon - 1; |
38 | 81 |
39 // just some sanity asserts to help document the expected range | 82 // just some sanity asserts to help document the expected range |
40 SkASSERT(kTooPinchyNormalDotProd >= -1); | 83 SkASSERT(kTooPinchyNormalDotProd >= -1); |
41 SkASSERT(kTooPinchyNormalDotProd < SkDoubleToScalar(-0.999)); | 84 SkASSERT(kTooPinchyNormalDotProd < SkDoubleToScalar(-0.999)); |
42 | 85 |
43 SkScalar dot = SkPoint::DotProduct(norm0, norm1); | 86 SkScalar dot = SkPoint::DotProduct(norm0, norm1); |
44 return dot <= kTooPinchyNormalDotProd; | 87 return dot <= kTooPinchyNormalDotProd; |
45 } | 88 } |
| 89 #endif |
46 | 90 |
47 static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after, | 91 static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after, |
48 SkScalar radius, | 92 SkScalar radius, |
49 SkVector* normal, SkVector* unitNormal) { | 93 SkVector* normal, SkVector* unitNormal) { |
50 if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) { | 94 if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) { |
51 return false; | 95 return false; |
52 } | 96 } |
53 unitNormal->rotateCCW(); | 97 unitNormal->rotateCCW(); |
54 unitNormal->scale(radius, normal); | 98 unitNormal->scale(radius, normal); |
55 return true; | 99 return true; |
56 } | 100 } |
57 | 101 |
58 static bool set_normal_unitnormal(const SkVector& vec, | 102 static bool set_normal_unitnormal(const SkVector& vec, |
59 SkScalar radius, | 103 SkScalar radius, |
60 SkVector* normal, SkVector* unitNormal) { | 104 SkVector* normal, SkVector* unitNormal) { |
61 if (!unitNormal->setNormalize(vec.fX, vec.fY)) { | 105 if (!unitNormal->setNormalize(vec.fX, vec.fY)) { |
62 return false; | 106 return false; |
63 } | 107 } |
64 unitNormal->rotateCCW(); | 108 unitNormal->rotateCCW(); |
65 unitNormal->scale(radius, normal); | 109 unitNormal->scale(radius, normal); |
66 return true; | 110 return true; |
67 } | 111 } |
68 | 112 |
69 /////////////////////////////////////////////////////////////////////////////// | 113 /////////////////////////////////////////////////////////////////////////////// |
| 114 #if QUAD_STROKE_APPROXIMATION |
| 115 |
| 116 struct SkQuadConstruct { // The state of the quad stroke under construction. |
| 117 SkPoint fQuad[3]; // the stroked quad parallel to the original curve |
| 118 SkPoint fTangentStart; // a point tangent to fQuad[0] |
| 119 SkPoint fTangentEnd; // a point tangent to fQuad[2] |
| 120 SkScalar fStartT; // a segment of the original curve |
| 121 SkScalar fMidT; // " |
| 122 SkScalar fEndT; // " |
| 123 bool fStartSet; // state to share common points across structs |
| 124 bool fEndSet; // " |
| 125 |
| 126 // return false if start and end are too close to have a unique middle |
| 127 bool init(SkScalar start, SkScalar end) { |
| 128 fStartT = start; |
| 129 fMidT = (start + end) * SK_ScalarHalf; |
| 130 fEndT = end; |
| 131 fStartSet = fEndSet = false; |
| 132 return fStartT < fMidT && fMidT < fEndT; |
| 133 } |
| 134 |
| 135 bool initWithStart(SkQuadConstruct* parent) { |
| 136 if (!init(parent->fStartT, parent->fMidT)) { |
| 137 return false; |
| 138 } |
| 139 fQuad[0] = parent->fQuad[0]; |
| 140 fTangentStart = parent->fTangentStart; |
| 141 fStartSet = true; |
| 142 return true; |
| 143 } |
| 144 |
| 145 bool initWithEnd(SkQuadConstruct* parent) { |
| 146 if (!init(parent->fMidT, parent->fEndT)) { |
| 147 return false; |
| 148 } |
| 149 fQuad[2] = parent->fQuad[2]; |
| 150 fTangentEnd = parent->fTangentEnd; |
| 151 fEndSet = true; |
| 152 return true; |
| 153 } |
| 154 }; |
| 155 #endif |
70 | 156 |
71 class SkPathStroker { | 157 class SkPathStroker { |
72 public: | 158 public: |
| 159 #if QUAD_STROKE_APPROXIMATION |
| 160 SkPathStroker(const SkPath& src, |
| 161 SkScalar radius, SkScalar miterLimit, SkScalar error, SkPaint:
:Cap cap, |
| 162 SkPaint::Join join); |
| 163 #else |
73 SkPathStroker(const SkPath& src, | 164 SkPathStroker(const SkPath& src, |
74 SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap, | 165 SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap, |
75 SkPaint::Join join); | 166 SkPaint::Join join); |
| 167 #endif |
76 | 168 |
77 void moveTo(const SkPoint&); | 169 void moveTo(const SkPoint&); |
78 void lineTo(const SkPoint&); | 170 void lineTo(const SkPoint&); |
79 void quadTo(const SkPoint&, const SkPoint&); | 171 void quadTo(const SkPoint&, const SkPoint&); |
80 void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&); | 172 void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&); |
81 void close(bool isLine) { this->finishContour(true, isLine); } | 173 void close(bool isLine) { this->finishContour(true, isLine); } |
82 | 174 |
83 void done(SkPath* dst, bool isLine) { | 175 void done(SkPath* dst, bool isLine) { |
84 this->finishContour(false, isLine); | 176 this->finishContour(false, isLine); |
85 fOuter.addPath(fExtra); | 177 fOuter.addPath(fExtra); |
86 dst->swap(fOuter); | 178 dst->swap(fOuter); |
87 } | 179 } |
88 | 180 |
89 private: | 181 private: |
| 182 #if QUAD_STROKE_APPROXIMATION |
| 183 SkScalar fError; |
| 184 #endif |
90 SkScalar fRadius; | 185 SkScalar fRadius; |
91 SkScalar fInvMiterLimit; | 186 SkScalar fInvMiterLimit; |
92 | 187 |
93 SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal; | 188 SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal; |
94 SkPoint fFirstPt, fPrevPt; // on original path | 189 SkPoint fFirstPt, fPrevPt; // on original path |
95 SkPoint fFirstOuterPt; | 190 SkPoint fFirstOuterPt; |
96 int fSegmentCount; | 191 int fSegmentCount; |
97 bool fPrevIsLine; | 192 bool fPrevIsLine; |
98 | 193 |
99 SkStrokerPriv::CapProc fCapper; | 194 SkStrokerPriv::CapProc fCapper; |
100 SkStrokerPriv::JoinProc fJoiner; | 195 SkStrokerPriv::JoinProc fJoiner; |
101 | 196 |
102 SkPath fInner, fOuter; // outer is our working answer, inner is temp | 197 SkPath fInner, fOuter; // outer is our working answer, inner is temp |
103 SkPath fExtra; // added as extra complete contours | 198 SkPath fExtra; // added as extra complete contours |
104 | 199 |
| 200 #if QUAD_STROKE_APPROXIMATION |
| 201 enum StrokeType { |
| 202 kOuter_StrokeType = 1, // use sign-opposite values later to flip pe
rpendicular axis |
| 203 kInner_StrokeType = -1 |
| 204 } fStrokeType; |
| 205 |
| 206 enum ResultType { |
| 207 kSplit_ResultType, // the caller should split the quad stroke i
n two |
| 208 kDegenerate_ResultType, // the caller should add a line |
| 209 kQuad_ResultType, // the caller should (continue to try to) ad
d a quad stroke |
| 210 kNormalError_ResultType, // the cubic's normal couldn't be computed -
- abort |
| 211 }; |
| 212 |
| 213 enum ReductionType { |
| 214 kPoint_ReductionType, // all curve points are practically identica
l |
| 215 kLine_ReductionType, // the control point is on the line between
the ends |
| 216 kQuad_ReductionType, // the control point is outside the line bet
ween the ends |
| 217 kDegenerate_ReductionType, // the control point is on the line but outs
ide the ends |
| 218 kDegenerate2_ReductionType, // two control points are on the line but ou
tside ends (cubic) |
| 219 kDegenerate3_ReductionType, // three areas of max curvature found (for c
ubic) |
| 220 }; |
| 221 |
| 222 enum IntersectRayType { |
| 223 kCtrlPt_RayType, |
| 224 kResultType_RayType, |
| 225 }; |
| 226 |
| 227 int fRecursionDepth; // track stack depth to abort if numerics ru
n amok |
| 228 bool fFoundTangents; // do less work until tangents meet (cubic) |
| 229 |
| 230 void addDegenerateLine(const SkQuadConstruct* ); |
| 231 ReductionType CheckCubicLinear(const SkPoint cubic[4], SkPoint reduction[3], |
| 232 const SkPoint** tanPtPtr); |
| 233 ReductionType CheckQuadLinear(const SkPoint quad[3], SkPoint* reduction); |
| 234 ResultType compareQuadCubic(const SkPoint cubic[4], SkQuadConstruct* ); |
| 235 ResultType compareQuadQuad(const SkPoint quad[3], SkQuadConstruct* ); |
| 236 bool cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct* ) const; |
| 237 bool cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tPt, SkPoint*
onPt, |
| 238 SkPoint* tangent) const; |
| 239 bool cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* ); |
| 240 bool cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct* , SkPoint*
mid) const; |
| 241 bool cubicStroke(const SkPoint cubic[4], SkQuadConstruct* ); |
| 242 void init(StrokeType strokeType, SkQuadConstruct* , SkScalar tStart, SkScala
r tEnd); |
| 243 ResultType intersectRay(SkQuadConstruct* , IntersectRayType STROKER_DEBUG_P
ARAMS(int) ) const; |
| 244 bool ptInQuadBounds(const SkPoint quad[3], const SkPoint& pt) const; |
| 245 void quadPerpRay(const SkPoint quad[3], SkScalar t, SkPoint* tPt, SkPoint* o
nPt, |
| 246 SkPoint* tangent) const; |
| 247 bool quadStroke(const SkPoint quad[3], SkQuadConstruct* ); |
| 248 void setCubicEndNormal(const SkPoint cubic[4], |
| 249 const SkVector& normalAB, const SkVector& unitNormalA
B, |
| 250 SkVector* normalCD, SkVector* unitNormalCD); |
| 251 void setQuadEndNormal(const SkPoint quad[3], |
| 252 const SkVector& normalAB, const SkVector& unitNormalAB
, |
| 253 SkVector* normalBC, SkVector* unitNormalBC); |
| 254 void setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt, SkPoint* ta
ngent) const; |
| 255 static bool SlightAngle(SkQuadConstruct* ); |
| 256 ResultType strokeCloseEnough(const SkPoint stroke[3], const SkPoint ray[2], |
| 257 SkQuadConstruct* STROKER_DEBUG_PARAMS(int dept
h) ) const; |
| 258 ResultType tangentsMeet(const SkPoint cubic[4], SkQuadConstruct* ); |
| 259 #endif |
| 260 |
105 void finishContour(bool close, bool isLine); | 261 void finishContour(bool close, bool isLine); |
106 void preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal, | 262 void preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal, |
107 bool isLine); | 263 bool isLine); |
108 void postJoinTo(const SkPoint&, const SkVector& normal, | 264 void postJoinTo(const SkPoint&, const SkVector& normal, |
109 const SkVector& unitNormal); | 265 const SkVector& unitNormal); |
110 | 266 |
111 void line_to(const SkPoint& currPt, const SkVector& normal); | 267 void line_to(const SkPoint& currPt, const SkVector& normal); |
| 268 #if !QUAD_STROKE_APPROXIMATION |
112 void quad_to(const SkPoint pts[3], | 269 void quad_to(const SkPoint pts[3], |
113 const SkVector& normalAB, const SkVector& unitNormalAB, | 270 const SkVector& normalAB, const SkVector& unitNormalAB, |
114 SkVector* normalBC, SkVector* unitNormalBC, | 271 SkVector* normalBC, SkVector* unitNormalBC, |
115 int subDivide); | 272 int subDivide); |
116 void cubic_to(const SkPoint pts[4], | 273 void cubic_to(const SkPoint pts[4], |
117 const SkVector& normalAB, const SkVector& unitNormalAB, | 274 const SkVector& normalAB, const SkVector& unitNormalAB, |
118 SkVector* normalCD, SkVector* unitNormalCD, | 275 SkVector* normalCD, SkVector* unitNormalCD, |
119 int subDivide); | 276 int subDivide); |
| 277 #endif |
120 }; | 278 }; |
121 | 279 |
122 /////////////////////////////////////////////////////////////////////////////// | 280 /////////////////////////////////////////////////////////////////////////////// |
123 | 281 |
124 void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal, | 282 void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal, |
125 SkVector* unitNormal, bool currIsLine) { | 283 SkVector* unitNormal, bool currIsLine) { |
126 SkASSERT(fSegmentCount >= 0); | 284 SkASSERT(fSegmentCount >= 0); |
127 | 285 |
128 SkScalar prevX = fPrevPt.fX; | 286 SkScalar prevX = fPrevPt.fX; |
129 SkScalar prevY = fPrevPt.fY; | 287 SkScalar prevY = fPrevPt.fY; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
180 } | 338 } |
181 } | 339 } |
182 // since we may re-use fInner, we rewind instead of reset, to save on | 340 // since we may re-use fInner, we rewind instead of reset, to save on |
183 // reallocating its internal storage. | 341 // reallocating its internal storage. |
184 fInner.rewind(); | 342 fInner.rewind(); |
185 fSegmentCount = -1; | 343 fSegmentCount = -1; |
186 } | 344 } |
187 | 345 |
188 /////////////////////////////////////////////////////////////////////////////// | 346 /////////////////////////////////////////////////////////////////////////////// |
189 | 347 |
| 348 #if QUAD_STROKE_APPROXIMATION |
| 349 SkPathStroker::SkPathStroker(const SkPath& src, |
| 350 SkScalar radius, SkScalar miterLimit, SkScalar erro
r, |
| 351 SkPaint::Cap cap, SkPaint::Join join) |
| 352 #else |
190 SkPathStroker::SkPathStroker(const SkPath& src, | 353 SkPathStroker::SkPathStroker(const SkPath& src, |
191 SkScalar radius, SkScalar miterLimit, | 354 SkScalar radius, SkScalar miterLimit, |
192 SkPaint::Cap cap, SkPaint::Join join) | 355 SkPaint::Cap cap, SkPaint::Join join) |
| 356 #endif |
193 : fRadius(radius) { | 357 : fRadius(radius) { |
194 | 358 |
195 /* This is only used when join is miter_join, but we initialize it here | 359 /* This is only used when join is miter_join, but we initialize it here |
196 so that it is always defined, to fis valgrind warnings. | 360 so that it is always defined, to fis valgrind warnings. |
197 */ | 361 */ |
198 fInvMiterLimit = 0; | 362 fInvMiterLimit = 0; |
199 | 363 |
200 if (join == SkPaint::kMiter_Join) { | 364 if (join == SkPaint::kMiter_Join) { |
201 if (miterLimit <= SK_Scalar1) { | 365 if (miterLimit <= SK_Scalar1) { |
202 join = SkPaint::kBevel_Join; | 366 join = SkPaint::kBevel_Join; |
203 } else { | 367 } else { |
204 fInvMiterLimit = SkScalarInvert(miterLimit); | 368 fInvMiterLimit = SkScalarInvert(miterLimit); |
205 } | 369 } |
206 } | 370 } |
207 fCapper = SkStrokerPriv::CapFactory(cap); | 371 fCapper = SkStrokerPriv::CapFactory(cap); |
208 fJoiner = SkStrokerPriv::JoinFactory(join); | 372 fJoiner = SkStrokerPriv::JoinFactory(join); |
209 fSegmentCount = -1; | 373 fSegmentCount = -1; |
210 fPrevIsLine = false; | 374 fPrevIsLine = false; |
211 | 375 |
212 // Need some estimate of how large our final result (fOuter) | 376 // Need some estimate of how large our final result (fOuter) |
213 // and our per-contour temp (fInner) will be, so we don't spend | 377 // and our per-contour temp (fInner) will be, so we don't spend |
214 // extra time repeatedly growing these arrays. | 378 // extra time repeatedly growing these arrays. |
215 // | 379 // |
216 // 3x for result == inner + outer + join (swag) | 380 // 3x for result == inner + outer + join (swag) |
217 // 1x for inner == 'wag' (worst contour length would be better guess) | 381 // 1x for inner == 'wag' (worst contour length would be better guess) |
218 fOuter.incReserve(src.countPoints() * 3); | 382 fOuter.incReserve(src.countPoints() * 3); |
219 fInner.incReserve(src.countPoints()); | 383 fInner.incReserve(src.countPoints()); |
| 384 #if QUAD_STROKE_APPROXIMATION |
| 385 #ifdef SK_DEBUG |
| 386 if (!gDebugStrokerErrorSet) { |
| 387 gDebugStrokerError = error; |
| 388 } |
| 389 fError = gDebugStrokerError; |
| 390 #else |
| 391 fError = error; |
| 392 #endif |
| 393 fRecursionDepth = 0; |
| 394 #endif |
220 } | 395 } |
221 | 396 |
222 void SkPathStroker::moveTo(const SkPoint& pt) { | 397 void SkPathStroker::moveTo(const SkPoint& pt) { |
223 if (fSegmentCount > 0) { | 398 if (fSegmentCount > 0) { |
224 this->finishContour(false, false); | 399 this->finishContour(false, false); |
225 } | 400 } |
226 fSegmentCount = 0; | 401 fSegmentCount = 0; |
227 fFirstPt = fPrevPt = pt; | 402 fFirstPt = fPrevPt = pt; |
228 } | 403 } |
229 | 404 |
230 void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { | 405 void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { |
231 fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY); | 406 fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY); |
232 fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY); | 407 fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY); |
233 } | 408 } |
234 | 409 |
235 void SkPathStroker::lineTo(const SkPoint& currPt) { | 410 void SkPathStroker::lineTo(const SkPoint& currPt) { |
236 if (SkPath::IsLineDegenerate(fPrevPt, currPt)) { | 411 if (SkPath::IsLineDegenerate(fPrevPt, currPt)) { |
237 return; | 412 return; |
238 } | 413 } |
239 SkVector normal, unitNormal; | 414 SkVector normal, unitNormal; |
240 | 415 |
241 this->preJoinTo(currPt, &normal, &unitNormal, true); | 416 this->preJoinTo(currPt, &normal, &unitNormal, true); |
242 this->line_to(currPt, normal); | 417 this->line_to(currPt, normal); |
243 this->postJoinTo(currPt, normal, unitNormal); | 418 this->postJoinTo(currPt, normal, unitNormal); |
244 } | 419 } |
245 | 420 |
| 421 #if !QUAD_STROKE_APPROXIMATION |
246 void SkPathStroker::quad_to(const SkPoint pts[3], | 422 void SkPathStroker::quad_to(const SkPoint pts[3], |
247 const SkVector& normalAB, const SkVector& unitNormalAB, | 423 const SkVector& normalAB, const SkVector& unitNormalAB, |
248 SkVector* normalBC, SkVector* unitNormalBC, | 424 SkVector* normalBC, SkVector* unitNormalBC, |
249 int subDivide) { | 425 int subDivide) { |
250 if (!set_normal_unitnormal(pts[1], pts[2], fRadius, | 426 if (!set_normal_unitnormal(pts[1], pts[2], fRadius, |
251 normalBC, unitNormalBC)) { | 427 normalBC, unitNormalBC)) { |
252 // pts[1] nearly equals pts[2], so just draw a line to pts[2] | 428 // pts[1] nearly equals pts[2], so just draw a line to pts[2] |
253 this->line_to(pts[2], normalAB); | 429 this->line_to(pts[2], normalAB); |
254 *normalBC = normalAB; | 430 *normalBC = normalAB; |
255 *unitNormalBC = unitNormalAB; | 431 *unitNormalBC = unitNormalAB; |
(...skipping 15 matching lines...) Expand all Loading... |
271 SkScalar dot = SkPoint::DotProduct(unitNormalAB, *unitNormalBC); | 447 SkScalar dot = SkPoint::DotProduct(unitNormalAB, *unitNormalBC); |
272 SkAssertResult(normalB.setLength(SkScalarDiv(fRadius, | 448 SkAssertResult(normalB.setLength(SkScalarDiv(fRadius, |
273 SkScalarSqrt((SK_Scalar1 + dot)/2)))); | 449 SkScalarSqrt((SK_Scalar1 + dot)/2)))); |
274 | 450 |
275 fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, | 451 fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, |
276 pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY); | 452 pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY); |
277 fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, | 453 fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, |
278 pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY); | 454 pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY); |
279 } | 455 } |
280 } | 456 } |
| 457 #endif |
| 458 |
| 459 #if QUAD_STROKE_APPROXIMATION |
| 460 void SkPathStroker::setQuadEndNormal(const SkPoint quad[3], const SkVector& norm
alAB, |
| 461 const SkVector& unitNormalAB, SkVector* normalBC, SkVector* unitNormalBC
) { |
| 462 if (!set_normal_unitnormal(quad[1], quad[2], fRadius, normalBC, unitNormalBC
)) { |
| 463 *normalBC = normalAB; |
| 464 *unitNormalBC = unitNormalAB; |
| 465 } |
| 466 } |
| 467 |
| 468 void SkPathStroker::setCubicEndNormal(const SkPoint cubic[4], const SkVector& no
rmalAB, |
| 469 const SkVector& unitNormalAB, SkVector* normalCD, SkVector* unitNormalCD
) { |
| 470 SkVector ab = cubic[1] - cubic[0]; |
| 471 SkVector cd = cubic[3] - cubic[2]; |
| 472 |
| 473 bool degenerateAB = degenerate_vector(ab); |
| 474 bool degenerateCD = degenerate_vector(cd); |
| 475 |
| 476 if (degenerateAB && degenerateCD) { |
| 477 goto DEGENERATE_NORMAL; |
| 478 } |
| 479 |
| 480 if (degenerateAB) { |
| 481 ab = cubic[2] - cubic[0]; |
| 482 degenerateAB = degenerate_vector(ab); |
| 483 } |
| 484 if (degenerateCD) { |
| 485 cd = cubic[3] - cubic[1]; |
| 486 degenerateCD = degenerate_vector(cd); |
| 487 } |
| 488 if (degenerateAB || degenerateCD) { |
| 489 DEGENERATE_NORMAL: |
| 490 *normalCD = normalAB; |
| 491 *unitNormalCD = unitNormalAB; |
| 492 return; |
| 493 } |
| 494 SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD)); |
| 495 } |
| 496 |
| 497 void SkPathStroker::init(StrokeType strokeType, SkQuadConstruct* quadPts, SkScal
ar tStart, |
| 498 SkScalar tEnd) { |
| 499 fStrokeType = strokeType; |
| 500 fFoundTangents = false; |
| 501 quadPts->init(tStart, tEnd); |
| 502 } |
| 503 |
| 504 // returns the distance squared from the point to the line |
| 505 static SkScalar pt_to_line(const SkPoint& pt, const SkPoint& lineStart, const Sk
Point& lineEnd) { |
| 506 SkVector dxy = lineEnd - lineStart; |
| 507 if (degenerate_vector(dxy)) { |
| 508 return pt.distanceToSqd(lineStart); |
| 509 } |
| 510 SkVector ab0 = pt - lineStart; |
| 511 SkScalar numer = dxy.dot(ab0); |
| 512 SkScalar denom = dxy.dot(dxy); |
| 513 SkScalar t = numer / denom; |
| 514 SkPoint hit; |
| 515 hit.fX = lineStart.fX * (1 - t) + lineEnd.fX * t; |
| 516 hit.fY = lineStart.fY * (1 - t) + lineEnd.fY * t; |
| 517 return hit.distanceToSqd(pt); |
| 518 } |
| 519 |
| 520 /* Given a cubic, determine if all four points are in a line. |
| 521 Return true if the inner points is close to a line connecting the outermost
points. |
| 522 |
| 523 Find the outermost point by looking for the largest difference in X or Y. |
| 524 Given the indices of the outermost points, and that outer_1 is greater than
outer_2, |
| 525 this table shows the index of the smaller of the remaining points: |
| 526 |
| 527 outer_2 |
| 528 0 1 2 3 |
| 529 outer_1 ---------------- |
| 530 0 | - 2 1 1 |
| 531 1 | - - 0 0 |
| 532 2 | - - - 0 |
| 533 3 | - - - - |
| 534 |
| 535 If outer_1 == 0 and outer_2 == 1, the smaller of the remaining indices (2 an
d 3) is 2. |
| 536 |
| 537 This table can be collapsed to: (1 + (2 >> outer_2)) >> outer_1 |
| 538 |
| 539 Given three indices (outer_1 outer_2 mid_1) from 0..3, the remaining index i
s: |
| 540 |
| 541 mid_2 == (outer_1 ^ outer_2 ^ mid_1) |
| 542 */ |
| 543 static bool cubic_in_line(const SkPoint cubic[4]) { |
| 544 SkScalar ptMax = -1; |
| 545 int outer1, outer2; |
| 546 for (int index = 0; index < 3; ++index) { |
| 547 for (int inner = index + 1; inner < 4; ++inner) { |
| 548 SkVector testDiff = cubic[inner] - cubic[index]; |
| 549 SkScalar testMax = SkTMax(SkScalarAbs(testDiff.fX), SkScalarAbs(test
Diff.fY)); |
| 550 if (ptMax < testMax) { |
| 551 outer1 = index; |
| 552 outer2 = inner; |
| 553 ptMax = testMax; |
| 554 } |
| 555 } |
| 556 } |
| 557 SkASSERT(outer1 >= 0 && outer1 <= 2); |
| 558 SkASSERT(outer2 >= 1 && outer2 <= 3); |
| 559 SkASSERT(outer1 < outer2); |
| 560 int mid1 = (1 + (2 >> outer2)) >> outer1; |
| 561 SkASSERT(mid1 >= 0 && mid1 <= 2); |
| 562 SkASSERT(outer1 != mid1 && outer2 != mid1); |
| 563 int mid2 = outer1 ^ outer2 ^ mid1; |
| 564 SkASSERT(mid2 >= 1 && mid2 <= 3); |
| 565 SkASSERT(mid2 != outer1 && mid2 != outer2 && mid2 != mid1); |
| 566 SkASSERT(((1 << outer1) | (1 << outer2) | (1 << mid1) | (1 << mid2)) == 0x0f
); |
| 567 SkScalar lineSlop = ptMax * ptMax * 0.00001f; // this multiplier is pulled
out of the air |
| 568 return pt_to_line(cubic[mid1], cubic[outer1], cubic[outer2]) <= lineSlop |
| 569 && pt_to_line(cubic[mid2], cubic[outer1], cubic[outer2]) <= lineSlop
; |
| 570 } |
| 571 |
| 572 /* Given quad, see if all there points are in a line. |
| 573 Return true if the inside point is close to a line connecting the outermost p
oints. |
| 574 |
| 575 Find the outermost point by looking for the largest difference in X or Y. |
| 576 Since the XOR of the indices is 3 (0 ^ 1 ^ 2) |
| 577 the missing index equals: outer_1 ^ outer_2 ^ 3 |
| 578 */ |
| 579 static bool quad_in_line(const SkPoint quad[3]) { |
| 580 SkScalar ptMax = -1; |
| 581 int outer1, outer2; |
| 582 for (int index = 0; index < 2; ++index) { |
| 583 for (int inner = index + 1; inner < 3; ++inner) { |
| 584 SkVector testDiff = quad[inner] - quad[index]; |
| 585 SkScalar testMax = SkTMax(SkScalarAbs(testDiff.fX), SkScalarAbs(test
Diff.fY)); |
| 586 if (ptMax < testMax) { |
| 587 outer1 = index; |
| 588 outer2 = inner; |
| 589 ptMax = testMax; |
| 590 } |
| 591 } |
| 592 } |
| 593 SkASSERT(outer1 >= 0 && outer1 <= 1); |
| 594 SkASSERT(outer2 >= 1 && outer2 <= 2); |
| 595 SkASSERT(outer1 < outer2); |
| 596 int mid = outer1 ^ outer2 ^ 3; |
| 597 SkScalar lineSlop = ptMax * ptMax * 0.00001f; // this multiplier is pulled
out of the air |
| 598 return pt_to_line(quad[mid], quad[outer1], quad[outer2]) <= lineSlop; |
| 599 } |
| 600 |
| 601 SkPathStroker::ReductionType SkPathStroker::CheckCubicLinear(const SkPoint cubic
[4], |
| 602 SkPoint reduction[3], const SkPoint** tangentPtPtr) { |
| 603 bool degenerateAB = degenerate_vector(cubic[1] - cubic[0]); |
| 604 bool degenerateBC = degenerate_vector(cubic[2] - cubic[1]); |
| 605 bool degenerateCD = degenerate_vector(cubic[3] - cubic[2]); |
| 606 if (degenerateAB & degenerateBC & degenerateCD) { |
| 607 return kPoint_ReductionType; |
| 608 } |
| 609 if (degenerateAB + degenerateBC + degenerateCD == 2) { |
| 610 return kLine_ReductionType; |
| 611 } |
| 612 if (!cubic_in_line(cubic)) { |
| 613 *tangentPtPtr = degenerateAB ? &cubic[2] : &cubic[1]; |
| 614 return kQuad_ReductionType; |
| 615 } |
| 616 SkScalar tValues[3]; |
| 617 int count = SkFindCubicMaxCurvature(cubic, tValues); |
| 618 if (count == 0) { |
| 619 return kLine_ReductionType; |
| 620 } |
| 621 for (int index = 0; index < count; ++index) { |
| 622 SkScalar t = tValues[index]; |
| 623 SkEvalCubicAt(cubic, t, &reduction[index], NULL, NULL); |
| 624 } |
| 625 SK_COMPILE_ASSERT(kQuad_ReductionType + 1 == kDegenerate_ReductionType, enum
_out_of_whack); |
| 626 SK_COMPILE_ASSERT(kQuad_ReductionType + 2 == kDegenerate2_ReductionType, enu
m_out_of_whack); |
| 627 SK_COMPILE_ASSERT(kQuad_ReductionType + 3 == kDegenerate3_ReductionType, enu
m_out_of_whack); |
| 628 |
| 629 return (ReductionType) (kQuad_ReductionType + count); |
| 630 } |
| 631 |
| 632 SkPathStroker::ReductionType SkPathStroker::CheckQuadLinear(const SkPoint quad[3
], |
| 633 SkPoint* reduction) { |
| 634 bool degenerateAB = degenerate_vector(quad[1] - quad[0]); |
| 635 bool degenerateBC = degenerate_vector(quad[2] - quad[1]); |
| 636 if (degenerateAB & degenerateBC) { |
| 637 return kPoint_ReductionType; |
| 638 } |
| 639 if (degenerateAB | degenerateBC) { |
| 640 return kLine_ReductionType; |
| 641 } |
| 642 if (!quad_in_line(quad)) { |
| 643 return kQuad_ReductionType; |
| 644 } |
| 645 SkScalar t = SkFindQuadMaxCurvature(quad); |
| 646 if (0 == t) { |
| 647 return kLine_ReductionType; |
| 648 } |
| 649 SkEvalQuadAt(quad, t, reduction, NULL); |
| 650 return kDegenerate_ReductionType; |
| 651 } |
| 652 |
| 653 #else |
281 | 654 |
282 void SkPathStroker::cubic_to(const SkPoint pts[4], | 655 void SkPathStroker::cubic_to(const SkPoint pts[4], |
283 const SkVector& normalAB, const SkVector& unitNormalAB, | 656 const SkVector& normalAB, const SkVector& unitNormalAB, |
284 SkVector* normalCD, SkVector* unitNormalCD, | 657 SkVector* normalCD, SkVector* unitNormalCD, |
285 int subDivide) { | 658 int subDivide) { |
286 SkVector ab = pts[1] - pts[0]; | 659 SkVector ab = pts[1] - pts[0]; |
287 SkVector cd = pts[3] - pts[2]; | 660 SkVector cd = pts[3] - pts[2]; |
288 SkVector normalBC, unitNormalBC; | 661 SkVector normalBC, unitNormalBC; |
289 | 662 |
290 bool degenerateAB = degenerate_vector(ab); | 663 bool degenerateAB = degenerate_vector(ab); |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
355 | 728 |
356 fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, | 729 fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, |
357 pts[2].fX + normalC.fX, pts[2].fY + normalC.fY, | 730 pts[2].fX + normalC.fX, pts[2].fY + normalC.fY, |
358 pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY); | 731 pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY); |
359 | 732 |
360 fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, | 733 fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, |
361 pts[2].fX - normalC.fX, pts[2].fY - normalC.fY, | 734 pts[2].fX - normalC.fX, pts[2].fY - normalC.fY, |
362 pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY); | 735 pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY); |
363 } | 736 } |
364 } | 737 } |
| 738 #endif |
365 | 739 |
366 void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) { | 740 void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) { |
| 741 #if QUAD_STROKE_APPROXIMATION |
| 742 const SkPoint quad[3] = { fPrevPt, pt1, pt2 }; |
| 743 SkPoint reduction; |
| 744 ReductionType reductionType = CheckQuadLinear(quad, &reduction); |
| 745 if (kPoint_ReductionType == reductionType) { |
| 746 return; |
| 747 } |
| 748 if (kLine_ReductionType == reductionType) { |
| 749 this->lineTo(pt2); |
| 750 return; |
| 751 } |
| 752 if (kDegenerate_ReductionType == reductionType) { |
| 753 this->lineTo(reduction); |
| 754 SkStrokerPriv::JoinProc saveJoiner = fJoiner; |
| 755 fJoiner = SkStrokerPriv::JoinFactory(SkPaint::kRound_Join); |
| 756 this->lineTo(pt2); |
| 757 fJoiner = saveJoiner; |
| 758 return; |
| 759 } |
| 760 SkASSERT(kQuad_ReductionType == reductionType); |
| 761 SkVector normalAB, unitAB, normalBC, unitBC; |
| 762 this->preJoinTo(pt1, &normalAB, &unitAB, false); |
| 763 SkQuadConstruct quadPts; |
| 764 this->init(kOuter_StrokeType, &quadPts, 0, 1); |
| 765 if (!this->quadStroke(quad, &quadPts)) { |
| 766 return; |
| 767 } |
| 768 this->init(kInner_StrokeType, &quadPts, 0, 1); |
| 769 if (!this->quadStroke(quad, &quadPts)) { |
| 770 return; |
| 771 } |
| 772 this->setQuadEndNormal(quad, normalAB, unitAB, &normalBC, &unitBC); |
| 773 #else |
367 bool degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1); | 774 bool degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1); |
368 bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2); | 775 bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2); |
369 | 776 |
370 if (degenerateAB | degenerateBC) { | 777 if (degenerateAB | degenerateBC) { |
371 if (degenerateAB ^ degenerateBC) { | 778 if (degenerateAB ^ degenerateBC) { |
372 this->lineTo(pt2); | 779 this->lineTo(pt2); |
373 } | 780 } |
374 return; | 781 return; |
375 } | 782 } |
376 | 783 |
(...skipping 30 matching lines...) Expand all Loading... |
407 SkVector n = normalBC; | 814 SkVector n = normalBC; |
408 SkVector u = unitBC; | 815 SkVector u = unitBC; |
409 this->quad_to(&tmp[2], n, u, &normalBC, &unitBC, | 816 this->quad_to(&tmp[2], n, u, &normalBC, &unitBC, |
410 kMaxQuadSubdivide); | 817 kMaxQuadSubdivide); |
411 } | 818 } |
412 } else { | 819 } else { |
413 this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC, | 820 this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC, |
414 kMaxQuadSubdivide); | 821 kMaxQuadSubdivide); |
415 } | 822 } |
416 } | 823 } |
| 824 #endif |
417 | 825 |
418 this->postJoinTo(pt2, normalBC, unitBC); | 826 this->postJoinTo(pt2, normalBC, unitBC); |
419 } | 827 } |
420 | 828 |
| 829 #if QUAD_STROKE_APPROXIMATION |
| 830 // Given a point on the curve and its derivative, scale the derivative by the ra
dius, and |
| 831 // compute the perpendicular point and its tangent. |
| 832 void SkPathStroker::setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt, |
| 833 SkPoint* tangent) const { |
| 834 if (!dxy->setLength(fRadius)) { // consider moving double logic into SkPoin
t::setLength |
| 835 double xx = dxy->fX; |
| 836 double yy = dxy->fY; |
| 837 double dscale = fRadius / sqrt(xx * xx + yy * yy); |
| 838 dxy->fX = SkDoubleToScalar(xx * dscale); |
| 839 dxy->fY = SkDoubleToScalar(yy * dscale); |
| 840 } |
| 841 SkScalar axisFlip = SkIntToScalar(fStrokeType); // go opposite ways for out
er, inner |
| 842 onPt->fX = tPt.fX + axisFlip * dxy->fY; |
| 843 onPt->fY = tPt.fY - axisFlip * dxy->fX; |
| 844 if (tangent) { |
| 845 tangent->fX = onPt->fX + dxy->fX; |
| 846 tangent->fY = onPt->fY + dxy->fY; |
| 847 } |
| 848 } |
| 849 |
| 850 // Given a cubic and t, return the point on curve, its perpendicular, and the pe
rpendicular tangent. |
| 851 // Returns false if the perpendicular could not be computed (because the derivat
ive collapsed to 0) |
| 852 bool SkPathStroker::cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tP
t, SkPoint* onPt, |
| 853 SkPoint* tangent) const { |
| 854 SkVector dxy; |
| 855 SkEvalCubicAt(cubic, t, tPt, &dxy, NULL); |
| 856 if (dxy.fX == 0 && dxy.fY == 0) { |
| 857 if (SkScalarNearlyZero(t)) { |
| 858 dxy = cubic[2] - cubic[0]; |
| 859 } else if (SkScalarNearlyZero(1 - t)) { |
| 860 dxy = cubic[3] - cubic[1]; |
| 861 } else { |
| 862 return false; |
| 863 } |
| 864 if (dxy.fX == 0 && dxy.fY == 0) { |
| 865 dxy = cubic[3] - cubic[0]; |
| 866 } |
| 867 } |
| 868 setRayPts(*tPt, &dxy, onPt, tangent); |
| 869 return true; |
| 870 } |
| 871 |
| 872 // Given a cubic and a t range, find the start and end if they haven't been foun
d already. |
| 873 bool SkPathStroker::cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* quadP
ts) { |
| 874 if (!quadPts->fStartSet) { |
| 875 SkPoint cubicStartPt; |
| 876 if (!this->cubicPerpRay(cubic, quadPts->fStartT, &cubicStartPt, &quadPts
->fQuad[0], |
| 877 &quadPts->fTangentStart)) { |
| 878 return false; |
| 879 } |
| 880 quadPts->fStartSet = true; |
| 881 } |
| 882 if (!quadPts->fEndSet) { |
| 883 SkPoint cubicEndPt; |
| 884 if (!this->cubicPerpRay(cubic, quadPts->fEndT, &cubicEndPt, &quadPts->fQ
uad[2], |
| 885 &quadPts->fTangentEnd)) { |
| 886 return false; |
| 887 } |
| 888 quadPts->fEndSet = true; |
| 889 } |
| 890 return true; |
| 891 } |
| 892 |
| 893 bool SkPathStroker::cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct*
quadPts, |
| 894 SkPoint* mid) const { |
| 895 SkPoint cubicMidPt; |
| 896 return this->cubicPerpRay(cubic, quadPts->fMidT, &cubicMidPt, mid, NULL); |
| 897 } |
| 898 |
| 899 // Given a quad and t, return the point on curve, its perpendicular, and the per
pendicular tangent. |
| 900 void SkPathStroker::quadPerpRay(const SkPoint quad[3], SkScalar t, SkPoint* tPt,
SkPoint* onPt, |
| 901 SkPoint* tangent) const { |
| 902 SkVector dxy; |
| 903 SkEvalQuadAt(quad, t, tPt, &dxy); |
| 904 if (dxy.fX == 0 && dxy.fY == 0) { |
| 905 dxy = quad[2] - quad[0]; |
| 906 } |
| 907 setRayPts(*tPt, &dxy, onPt, tangent); |
| 908 } |
| 909 |
| 910 // Find the intersection of the stroke tangents to construct a stroke quad. |
| 911 // Return whether the stroke is a degenerate (a line), a quad, or must be split. |
| 912 // Optionally compute the quad's control point. |
| 913 SkPathStroker::ResultType SkPathStroker::intersectRay(SkQuadConstruct* quadPts, |
| 914 IntersectRayType intersectRayType STROKER_DEBUG_PARAMS(int depth)) cons
t { |
| 915 const SkPoint& start = quadPts->fQuad[0]; |
| 916 const SkPoint& end = quadPts->fQuad[2]; |
| 917 SkVector aLen = quadPts->fTangentStart - start; |
| 918 SkVector bLen = quadPts->fTangentEnd - end; |
| 919 SkScalar denom = aLen.cross(bLen); |
| 920 SkVector ab0 = start - end; |
| 921 SkScalar numerA = bLen.cross(ab0); |
| 922 SkScalar numerB = aLen.cross(ab0); |
| 923 if (!SkScalarNearlyZero(denom)) { |
| 924 // if the perpendicular distances from the quad points to the opposite t
angent line |
| 925 // are small, a straight line is good enough |
| 926 SkScalar dist1 = pt_to_line(start, end, quadPts->fTangentEnd); |
| 927 SkScalar dist2 = pt_to_line(end, start, quadPts->fTangentStart); |
| 928 if (SkTMax(dist1, dist2) <= fError * fError) { |
| 929 return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, |
| 930 "SkTMax(dist1=%g, dist2=%g) <= fError * fError", dist1, dist
2); |
| 931 } |
| 932 if ((numerA >= 0) != (numerB >= 0)) { |
| 933 if (kCtrlPt_RayType == intersectRayType) { |
| 934 numerA /= denom; |
| 935 SkPoint* ctrlPt = &quadPts->fQuad[1]; |
| 936 ctrlPt->fX = start.fX * (1 - numerA) + quadPts->fTangentStart.fX
* numerA; |
| 937 ctrlPt->fY = start.fY * (1 - numerA) + quadPts->fTangentStart.fY
* numerA; |
| 938 } |
| 939 return STROKER_RESULT(kQuad_ResultType, depth, quadPts, |
| 940 "(numerA=%g >= 0) != (numerB=%g >= 0)", numerA, numerB); |
| 941 } |
| 942 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, |
| 943 "(numerA=%g >= 0) == (numerB=%g >= 0)", numerA, numerB); |
| 944 } else { // if the lines are parallel, straight line is good enough |
| 945 return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, |
| 946 "SkScalarNearlyZero(denom=%g)", denom); |
| 947 } |
| 948 } |
| 949 |
| 950 // Given a cubic and a t-range, determine if the stroke can be described by a qu
adratic. |
| 951 SkPathStroker::ResultType SkPathStroker::tangentsMeet(const SkPoint cubic[4], |
| 952 SkQuadConstruct* quadPts) { |
| 953 if (!this->cubicQuadEnds(cubic, quadPts)) { |
| 954 return kNormalError_ResultType; |
| 955 } |
| 956 return intersectRay(quadPts, kResultType_RayType STROKER_DEBUG_PARAMS(fRecu
rsionDepth)); |
| 957 } |
| 958 |
| 959 // Intersect the line with the quad and return the t values on the quad where th
e line crosses. |
| 960 static int intersect_quad_ray(const SkPoint line[2], const SkPoint quad[3], SkSc
alar roots[2]) { |
| 961 SkVector vec = line[1] - line[0]; |
| 962 SkScalar r[3]; |
| 963 for (int n = 0; n < 3; ++n) { |
| 964 r[n] = (quad[n].fY - line[0].fY) * vec.fX - (quad[n].fX - line[0].fX) *
vec.fY; |
| 965 } |
| 966 SkScalar A = r[2]; |
| 967 SkScalar B = r[1]; |
| 968 SkScalar C = r[0]; |
| 969 A += C - 2 * B; // A = a - 2*b + c |
| 970 B -= C; // B = -(b - c) |
| 971 return SkFindUnitQuadRoots(A, 2 * B, C, roots); |
| 972 } |
| 973 |
| 974 // Return true if the point is close to the bounds of the quad. This is used as
a quick reject. |
| 975 bool SkPathStroker::ptInQuadBounds(const SkPoint quad[3], const SkPoint& pt) con
st { |
| 976 SkScalar xMin = SkTMin(SkTMin(quad[0].fX, quad[1].fX), quad[2].fX); |
| 977 if (pt.fX + fError < xMin) { |
| 978 return false; |
| 979 } |
| 980 SkScalar xMax = SkTMax(SkTMax(quad[0].fX, quad[1].fX), quad[2].fX); |
| 981 if (pt.fX - fError > xMax) { |
| 982 return false; |
| 983 } |
| 984 SkScalar yMin = SkTMin(SkTMin(quad[0].fY, quad[1].fY), quad[2].fY); |
| 985 if (pt.fY + fError < yMin) { |
| 986 return false; |
| 987 } |
| 988 SkScalar yMax = SkTMax(SkTMax(quad[0].fY, quad[1].fY), quad[2].fY); |
| 989 if (pt.fY - fError > yMax) { |
| 990 return false; |
| 991 } |
| 992 return true; |
| 993 } |
| 994 |
| 995 static bool points_within_dist(const SkPoint& nearPt, const SkPoint& farPt, SkSc
alar limit) { |
| 996 return nearPt.distanceToSqd(farPt) <= limit * limit; |
| 997 } |
| 998 |
| 999 static bool sharp_angle(const SkPoint quad[3]) { |
| 1000 SkVector smaller = quad[1] - quad[0]; |
| 1001 SkVector larger = quad[1] - quad[2]; |
| 1002 SkScalar smallerLen = smaller.lengthSqd(); |
| 1003 SkScalar largerLen = larger.lengthSqd(); |
| 1004 if (smallerLen > largerLen) { |
| 1005 SkTSwap(smaller, larger); |
| 1006 largerLen = smallerLen; |
| 1007 } |
| 1008 if (!smaller.setLength(largerLen)) { |
| 1009 return false; |
| 1010 } |
| 1011 SkScalar dot = smaller.dot(larger); |
| 1012 return dot > 0; |
| 1013 } |
| 1014 |
| 1015 SkPathStroker::ResultType SkPathStroker::strokeCloseEnough(const SkPoint stroke[
3], |
| 1016 const SkPoint ray[2], SkQuadConstruct* quadPts STROKER_DEBUG_PARAMS(int
depth)) const { |
| 1017 SkPoint strokeMid; |
| 1018 SkEvalQuadAt(stroke, SK_ScalarHalf, &strokeMid); |
| 1019 // measure the distance from the curve to the quad-stroke midpoint, compare
to radius |
| 1020 if (points_within_dist(ray[0], strokeMid, fError)) { // if the difference i
s small |
| 1021 if (sharp_angle(quadPts->fQuad)) { |
| 1022 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, |
| 1023 "sharp_angle (1) =%g,%g, %g,%g, %g,%g", |
| 1024 quadPts->fQuad[0].fX, quadPts->fQuad[0].fY, |
| 1025 quadPts->fQuad[1].fX, quadPts->fQuad[1].fY, |
| 1026 quadPts->fQuad[2].fX, quadPts->fQuad[2].fY); |
| 1027 } |
| 1028 return STROKER_RESULT(kQuad_ResultType, depth, quadPts, |
| 1029 "points_within_dist(ray[0]=%g,%g, strokeMid=%g,%g, fError)", |
| 1030 ray[0].fX, ray[0].fY, strokeMid.fX, strokeMid.fY); |
| 1031 } |
| 1032 // measure the distance to quad's bounds (quick reject) |
| 1033 // an alternative : look for point in triangle |
| 1034 if (!ptInQuadBounds(stroke, ray[0])) { // if far, subdivide |
| 1035 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, |
| 1036 "!pt_in_quad_bounds(stroke=(%g,%g %g,%g %g,%g), ray[0]=%g,%g)", |
| 1037 stroke[0].fX, stroke[0].fY, stroke[1].fX, stroke[1].fY, stroke[2
].fX, stroke[2].fY, |
| 1038 ray[0].fX, ray[0].fY); |
| 1039 } |
| 1040 // measure the curve ray distance to the quad-stroke |
| 1041 SkScalar roots[2]; |
| 1042 int rootCount = intersect_quad_ray(ray, stroke, roots); |
| 1043 if (rootCount != 1) { |
| 1044 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, |
| 1045 "rootCount=%d != 1", rootCount); |
| 1046 } |
| 1047 SkPoint quadPt; |
| 1048 SkEvalQuadAt(stroke, roots[0], &quadPt); |
| 1049 SkScalar error = fError * (SK_Scalar1 - SkScalarAbs(roots[0] - 0.5f) * 2); |
| 1050 if (points_within_dist(ray[0], quadPt, error)) { // if the difference is sm
all, we're done |
| 1051 if (sharp_angle(quadPts->fQuad)) { |
| 1052 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, |
| 1053 "sharp_angle (2) =%g,%g, %g,%g, %g,%g", |
| 1054 quadPts->fQuad[0].fX, quadPts->fQuad[0].fY, |
| 1055 quadPts->fQuad[1].fX, quadPts->fQuad[1].fY, |
| 1056 quadPts->fQuad[2].fX, quadPts->fQuad[2].fY); |
| 1057 } |
| 1058 return STROKER_RESULT(kQuad_ResultType, depth, quadPts, |
| 1059 "points_within_dist(ray[0]=%g,%g, quadPt=%g,%g, error=%g)", |
| 1060 ray[0].fX, ray[0].fY, quadPt.fX, quadPt.fY, error); |
| 1061 } |
| 1062 // otherwise, subdivide |
| 1063 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, "%s", "fall through
"); |
| 1064 } |
| 1065 |
| 1066 SkPathStroker::ResultType SkPathStroker::compareQuadCubic(const SkPoint cubic[4]
, |
| 1067 SkQuadConstruct* quadPts) { |
| 1068 // get the quadratic approximation of the stroke |
| 1069 if (!this->cubicQuadEnds(cubic, quadPts)) { |
| 1070 return kNormalError_ResultType; |
| 1071 } |
| 1072 ResultType resultType = intersectRay(quadPts, kCtrlPt_RayType |
| 1073 STROKER_DEBUG_PARAMS(fRecursionDepth) ); |
| 1074 if (resultType != kQuad_ResultType) { |
| 1075 return resultType; |
| 1076 } |
| 1077 // project a ray from the curve to the stroke |
| 1078 SkPoint ray[2]; // point near midpoint on quad, midpoint on cubic |
| 1079 if (!this->cubicPerpRay(cubic, quadPts->fMidT, &ray[1], &ray[0], NULL)) { |
| 1080 return kNormalError_ResultType; |
| 1081 } |
| 1082 return strokeCloseEnough(quadPts->fQuad, ray, quadPts STROKER_DEBUG_PARAMS(
fRecursionDepth)); |
| 1083 } |
| 1084 |
| 1085 // if false is returned, caller splits quadratic approximation |
| 1086 SkPathStroker::ResultType SkPathStroker::compareQuadQuad(const SkPoint quad[3], |
| 1087 SkQuadConstruct* quadPts) { |
| 1088 // get the quadratic approximation of the stroke |
| 1089 if (!quadPts->fStartSet) { |
| 1090 SkPoint quadStartPt; |
| 1091 this->quadPerpRay(quad, quadPts->fStartT, &quadStartPt, &quadPts->fQuad[
0], |
| 1092 &quadPts->fTangentStart); |
| 1093 quadPts->fStartSet = true; |
| 1094 } |
| 1095 if (!quadPts->fEndSet) { |
| 1096 SkPoint quadEndPt; |
| 1097 this->quadPerpRay(quad, quadPts->fEndT, &quadEndPt, &quadPts->fQuad[2], |
| 1098 &quadPts->fTangentEnd); |
| 1099 quadPts->fEndSet = true; |
| 1100 } |
| 1101 ResultType resultType = intersectRay(quadPts, kCtrlPt_RayType |
| 1102 STROKER_DEBUG_PARAMS(fRecursionDepth)); |
| 1103 if (resultType != kQuad_ResultType) { |
| 1104 return resultType; |
| 1105 } |
| 1106 // project a ray from the curve to the stroke |
| 1107 SkPoint ray[2]; |
| 1108 this->quadPerpRay(quad, quadPts->fMidT, &ray[1], &ray[0], NULL); |
| 1109 return strokeCloseEnough(quadPts->fQuad, ray, quadPts STROKER_DEBUG_PARAMS(
fRecursionDepth)); |
| 1110 } |
| 1111 |
| 1112 void SkPathStroker::addDegenerateLine(const SkQuadConstruct* quadPts) { |
| 1113 const SkPoint* quad = quadPts->fQuad; |
| 1114 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; |
| 1115 path->lineTo(quad[2].fX, quad[2].fY); |
| 1116 } |
| 1117 |
| 1118 bool SkPathStroker::cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct
* quadPts) const { |
| 1119 SkPoint strokeMid; |
| 1120 if (!cubicQuadMid(cubic, quadPts, &strokeMid)) { |
| 1121 return false; |
| 1122 } |
| 1123 SkScalar dist = pt_to_line(strokeMid, quadPts->fQuad[0], quadPts->fQuad[2]); |
| 1124 return dist < fError * fError; |
| 1125 } |
| 1126 |
| 1127 bool SkPathStroker::cubicStroke(const SkPoint cubic[4], SkQuadConstruct* quadPts
) { |
| 1128 if (!fFoundTangents) { |
| 1129 ResultType resultType = this->tangentsMeet(cubic, quadPts); |
| 1130 if (kQuad_ResultType != resultType) { |
| 1131 if (kNormalError_ResultType == resultType) { |
| 1132 return false; |
| 1133 } |
| 1134 if ((kDegenerate_ResultType == resultType |
| 1135 || points_within_dist(quadPts->fQuad[0], quadPts->fQuad[2],
fError)) |
| 1136 && cubicMidOnLine(cubic, quadPts)) { |
| 1137 addDegenerateLine(quadPts); |
| 1138 return true; |
| 1139 } |
| 1140 } else { |
| 1141 fFoundTangents = true; |
| 1142 } |
| 1143 } |
| 1144 if (fFoundTangents) { |
| 1145 ResultType resultType = this->compareQuadCubic(cubic, quadPts); |
| 1146 if (kQuad_ResultType == resultType) { |
| 1147 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; |
| 1148 const SkPoint* stroke = quadPts->fQuad; |
| 1149 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY)
; |
| 1150 return true; |
| 1151 } |
| 1152 if (kDegenerate_ResultType == resultType) { |
| 1153 addDegenerateLine(quadPts); |
| 1154 return true; |
| 1155 } |
| 1156 if (kNormalError_ResultType == resultType) { |
| 1157 return false; |
| 1158 } |
| 1159 } |
| 1160 if (!SkScalarIsFinite(quadPts->fQuad[2].fX) || !SkScalarIsFinite(quadPts->fQ
uad[2].fY)) { |
| 1161 return false; // just abort if projected quad isn't representable |
| 1162 } |
| 1163 SkDEBUGCODE(gMaxRecursion[fFoundTangents] = SkTMax(gMaxRecursion[fFoundTange
nts], |
| 1164 fRecursionDepth + 1)); |
| 1165 if (++fRecursionDepth > kRecursiveLimits[fFoundTangents]) { |
| 1166 return false; // just abort if projected quad isn't representable |
| 1167 } |
| 1168 SkQuadConstruct half; |
| 1169 if (!half.initWithStart(quadPts)) { |
| 1170 addDegenerateLine(quadPts); |
| 1171 return true; |
| 1172 } |
| 1173 if (!this->cubicStroke(cubic, &half)) { |
| 1174 return false; |
| 1175 } |
| 1176 if (!half.initWithEnd(quadPts)) { |
| 1177 addDegenerateLine(quadPts); |
| 1178 return true; |
| 1179 } |
| 1180 if (!this->cubicStroke(cubic, &half)) { |
| 1181 return false; |
| 1182 } |
| 1183 --fRecursionDepth; |
| 1184 return true; |
| 1185 } |
| 1186 |
| 1187 bool SkPathStroker::quadStroke(const SkPoint quad[3], SkQuadConstruct* quadPts)
{ |
| 1188 ResultType resultType = this->compareQuadQuad(quad, quadPts); |
| 1189 if (kQuad_ResultType == resultType) { |
| 1190 const SkPoint* stroke = quadPts->fQuad; |
| 1191 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; |
| 1192 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY); |
| 1193 return true; |
| 1194 } |
| 1195 if (kDegenerate_ResultType == resultType) { |
| 1196 addDegenerateLine(quadPts); |
| 1197 return true; |
| 1198 } |
| 1199 SkDEBUGCODE(gMaxRecursion[kQuad_RecursiveLimit] = SkTMax(gMaxRecursion[kQuad
_RecursiveLimit], |
| 1200 fRecursionDepth + 1)); |
| 1201 if (++fRecursionDepth > kRecursiveLimits[kQuad_RecursiveLimit]) { |
| 1202 return false; // just abort if projected quad isn't representable |
| 1203 } |
| 1204 SkQuadConstruct half; |
| 1205 (void) half.initWithStart(quadPts); |
| 1206 if (!this->quadStroke(quad, &half)) { |
| 1207 return false; |
| 1208 } |
| 1209 (void) half.initWithEnd(quadPts); |
| 1210 if (!this->quadStroke(quad, &half)) { |
| 1211 return false; |
| 1212 } |
| 1213 --fRecursionDepth; |
| 1214 return true; |
| 1215 } |
| 1216 |
| 1217 #endif |
| 1218 |
421 void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2, | 1219 void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2, |
422 const SkPoint& pt3) { | 1220 const SkPoint& pt3) { |
| 1221 #if QUAD_STROKE_APPROXIMATION |
| 1222 const SkPoint cubic[4] = { fPrevPt, pt1, pt2, pt3 }; |
| 1223 SkPoint reduction[3]; |
| 1224 const SkPoint* tangentPt; |
| 1225 ReductionType reductionType = CheckCubicLinear(cubic, reduction, &tangentPt)
; |
| 1226 if (kPoint_ReductionType == reductionType) { |
| 1227 return; |
| 1228 } |
| 1229 if (kLine_ReductionType == reductionType) { |
| 1230 this->lineTo(pt3); |
| 1231 return; |
| 1232 } |
| 1233 if (kDegenerate_ReductionType <= reductionType && kDegenerate3_ReductionType
>= reductionType) { |
| 1234 this->lineTo(reduction[0]); |
| 1235 SkStrokerPriv::JoinProc saveJoiner = fJoiner; |
| 1236 fJoiner = SkStrokerPriv::JoinFactory(SkPaint::kRound_Join); |
| 1237 if (kDegenerate2_ReductionType <= reductionType) { |
| 1238 this->lineTo(reduction[1]); |
| 1239 } |
| 1240 if (kDegenerate3_ReductionType == reductionType) { |
| 1241 this->lineTo(reduction[2]); |
| 1242 } |
| 1243 this->lineTo(pt3); |
| 1244 fJoiner = saveJoiner; |
| 1245 return; |
| 1246 } |
| 1247 SkASSERT(kQuad_ReductionType == reductionType); |
| 1248 SkVector normalAB, unitAB, normalCD, unitCD; |
| 1249 this->preJoinTo(*tangentPt, &normalAB, &unitAB, false); |
| 1250 SkScalar tValues[2]; |
| 1251 int count = SkFindCubicInflections(cubic, tValues); |
| 1252 SkScalar lastT = 0; |
| 1253 for (int index = 0; index <= count; ++index) { |
| 1254 SkScalar nextT = index < count ? tValues[index] : 1; |
| 1255 SkQuadConstruct quadPts; |
| 1256 this->init(kOuter_StrokeType, &quadPts, lastT, nextT); |
| 1257 if (!this->cubicStroke(cubic, &quadPts)) { |
| 1258 return; |
| 1259 } |
| 1260 this->init(kInner_StrokeType, &quadPts, lastT, nextT); |
| 1261 if (!this->cubicStroke(cubic, &quadPts)) { |
| 1262 return; |
| 1263 } |
| 1264 lastT = nextT; |
| 1265 } |
| 1266 this->setCubicEndNormal(cubic, normalAB, unitAB, &normalCD, &unitCD); |
| 1267 #else |
423 bool degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1); | 1268 bool degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1); |
424 bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2); | 1269 bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2); |
425 bool degenerateCD = SkPath::IsLineDegenerate(pt2, pt3); | 1270 bool degenerateCD = SkPath::IsLineDegenerate(pt2, pt3); |
426 | 1271 |
427 if (degenerateAB + degenerateBC + degenerateCD >= 2 | 1272 if (degenerateAB + degenerateBC + degenerateCD >= 2 |
428 || (degenerateAB && SkPath::IsLineDegenerate(fPrevPt, pt2))) { | 1273 || (degenerateAB && SkPath::IsLineDegenerate(fPrevPt, pt2))) { |
429 this->lineTo(pt3); | 1274 this->lineTo(pt3); |
430 return; | 1275 return; |
431 } | 1276 } |
432 | 1277 |
(...skipping 25 matching lines...) Expand all Loading... |
458 this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD, | 1303 this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD, |
459 kMaxCubicSubdivide); | 1304 kMaxCubicSubdivide); |
460 if (i == count - 1) { | 1305 if (i == count - 1) { |
461 break; | 1306 break; |
462 } | 1307 } |
463 n = normalCD; | 1308 n = normalCD; |
464 u = unitCD; | 1309 u = unitCD; |
465 | 1310 |
466 } | 1311 } |
467 } | 1312 } |
| 1313 #endif |
468 | 1314 |
469 this->postJoinTo(pt3, normalCD, unitCD); | 1315 this->postJoinTo(pt3, normalCD, unitCD); |
470 } | 1316 } |
471 | 1317 |
472 /////////////////////////////////////////////////////////////////////////////// | 1318 /////////////////////////////////////////////////////////////////////////////// |
473 /////////////////////////////////////////////////////////////////////////////// | 1319 /////////////////////////////////////////////////////////////////////////////// |
474 | 1320 |
475 #include "SkPaintDefaults.h" | 1321 #include "SkPaintDefaults.h" |
476 | 1322 |
477 SkStroke::SkStroke() { | 1323 SkStroke::SkStroke() { |
(...skipping 13 matching lines...) Expand all Loading... |
491 } | 1337 } |
492 | 1338 |
493 SkStroke::SkStroke(const SkPaint& p, SkScalar width) { | 1339 SkStroke::SkStroke(const SkPaint& p, SkScalar width) { |
494 fWidth = width; | 1340 fWidth = width; |
495 fMiterLimit = p.getStrokeMiter(); | 1341 fMiterLimit = p.getStrokeMiter(); |
496 fCap = (uint8_t)p.getStrokeCap(); | 1342 fCap = (uint8_t)p.getStrokeCap(); |
497 fJoin = (uint8_t)p.getStrokeJoin(); | 1343 fJoin = (uint8_t)p.getStrokeJoin(); |
498 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); | 1344 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); |
499 } | 1345 } |
500 | 1346 |
| 1347 #if QUAD_STROKE_APPROXIMATION |
| 1348 void SkStroke::setError(SkScalar error) { |
| 1349 SkASSERT(error > 0); |
| 1350 fError = error; |
| 1351 } |
| 1352 #endif |
| 1353 |
501 void SkStroke::setWidth(SkScalar width) { | 1354 void SkStroke::setWidth(SkScalar width) { |
502 SkASSERT(width >= 0); | 1355 SkASSERT(width >= 0); |
503 fWidth = width; | 1356 fWidth = width; |
504 } | 1357 } |
505 | 1358 |
506 void SkStroke::setMiterLimit(SkScalar miterLimit) { | 1359 void SkStroke::setMiterLimit(SkScalar miterLimit) { |
507 SkASSERT(miterLimit >= 0); | 1360 SkASSERT(miterLimit >= 0); |
508 fMiterLimit = miterLimit; | 1361 fMiterLimit = miterLimit; |
509 } | 1362 } |
510 | 1363 |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
568 SkASSERT(!dst->isInverseFillType()); | 1421 SkASSERT(!dst->isInverseFillType()); |
569 dst->toggleInverseFillType(); | 1422 dst->toggleInverseFillType(); |
570 } | 1423 } |
571 return; | 1424 return; |
572 } | 1425 } |
573 } | 1426 } |
574 | 1427 |
575 SkAutoConicToQuads converter; | 1428 SkAutoConicToQuads converter; |
576 const SkScalar conicTol = SK_Scalar1 / 4; | 1429 const SkScalar conicTol = SK_Scalar1 / 4; |
577 | 1430 |
| 1431 #if QUAD_STROKE_APPROXIMATION |
| 1432 SkPathStroker stroker(src, radius, fMiterLimit, fError, this->getCap(), |
| 1433 this->getJoin()); |
| 1434 #else |
578 SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(), | 1435 SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(), |
579 this->getJoin()); | 1436 this->getJoin()); |
| 1437 #endif |
580 SkPath::Iter iter(src, false); | 1438 SkPath::Iter iter(src, false); |
581 SkPath::Verb lastSegment = SkPath::kMove_Verb; | 1439 SkPath::Verb lastSegment = SkPath::kMove_Verb; |
582 | 1440 |
583 for (;;) { | 1441 for (;;) { |
584 SkPoint pts[4]; | 1442 SkPoint pts[4]; |
585 switch (iter.next(pts, false)) { | 1443 switch (iter.next(pts, false)) { |
586 case SkPath::kMove_Verb: | 1444 case SkPath::kMove_Verb: |
587 stroker.moveTo(pts[0]); | 1445 stroker.moveTo(pts[0]); |
588 break; | 1446 break; |
589 case SkPath::kLine_Verb: | 1447 case SkPath::kLine_Verb: |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
726 default: | 1584 default: |
727 break; | 1585 break; |
728 } | 1586 } |
729 | 1587 |
730 if (fWidth < SkMinScalar(rw, rh) && !fDoFill) { | 1588 if (fWidth < SkMinScalar(rw, rh) && !fDoFill) { |
731 r = rect; | 1589 r = rect; |
732 r.inset(radius, radius); | 1590 r.inset(radius, radius); |
733 dst->addRect(r, reverse_direction(dir)); | 1591 dst->addRect(r, reverse_direction(dir)); |
734 } | 1592 } |
735 } | 1593 } |
OLD | NEW |