Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(758)

Side by Side Diff: src/core/SkStroke.cpp

Issue 558163005: thick stroke Beziers (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: fix bench Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/core/SkStroke.h ('k') | src/core/SkStrokeRec.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « src/core/SkStroke.h ('k') | src/core/SkStrokeRec.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698