1 /*
2 * Copyright 2008 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkStrokerPriv.h"
9 #include "SkGeometry.h"
10 #include "SkPath.h"
11
12 #ifndef SK_LEGACY_STROKE_CURVES
13
14 enum {
15 kTangent_RecursiveLimit,
16 kCubic_RecursiveLimit,
17 kConic_RecursiveLimit,
18 kQuad_RecursiveLimit
19 };
20
21 // quads with extreme widths (e.g. (0,1) (1,6) (0,3) width=5e7) recurse to point of failure
22 // largest seen for normal cubics : 5, 26
23 // largest seen for normal quads : 11
24 static const int kRecursiveLimits[] = { 5*3, 26*3, 11*3, 11*3 }; // 3x limits seen in practice
25
26 SK_COMPILE_ASSERT(0 == kTangent_RecursiveLimit, cubic_stroke_relies_on_tangent_equalling_zero);
27 SK_COMPILE_ASSERT(1 == kCubic_RecursiveLimit, cubic_stroke_relies_on_cubic_equalling_one);
28 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kRecursiveLimits) == kQuad_RecursiveLimit + 1,
29 recursive_limits_mismatch);
30
31 #ifdef SK_DEBUG
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, quadPts->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
54 #ifdef SK_LEGACY_STROKE_CURVES
55 #define kMaxQuadSubdivide 5
56 #define kMaxCubicSubdivide 7
57 #endif
58
degenerate_vector(const SkVector & v)59 static inline bool degenerate_vector(const SkVector& v) {
60 return !SkPoint::CanNormalize(v.fX, v.fY);
61 }
62
63 #ifdef SK_LEGACY_STROKE_CURVES
normals_too_curvy(const SkVector & norm0,SkVector & norm1)64 static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) {
65 /* root2/2 is a 45-degree angle
66 make this constant bigger for more subdivisions (but not >= 1)
67 */
68 static const SkScalar kFlatEnoughNormalDotProd =
69 SK_ScalarSqrt2/2 + SK_Scalar1/10;
70
71 SkASSERT(kFlatEnoughNormalDotProd > 0 &&
72 kFlatEnoughNormalDotProd < SK_Scalar1);
73
74 return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd;
75 }
76
normals_too_pinchy(const SkVector & norm0,SkVector & norm1)77 static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) {
78 // if the dot-product is -1, then we are definitely too pinchy. We tweak
79 // that by an epsilon to ensure we have significant bits in our test
80 static const int kMinSigBitsForDot = 8;
81 static const SkScalar kDotEpsilon = FLT_EPSILON * (1 << kMinSigBitsForDot);
82 static const SkScalar kTooPinchyNormalDotProd = kDotEpsilon - 1;
83
84 // just some sanity asserts to help document the expected range
85 SkASSERT(kTooPinchyNormalDotProd >= -1);
86 SkASSERT(kTooPinchyNormalDotProd < SkDoubleToScalar(-0.999));
87
88 SkScalar dot = SkPoint::DotProduct(norm0, norm1);
89 return dot <= kTooPinchyNormalDotProd;
90 }
91 #endif
92
set_normal_unitnormal(const SkPoint & before,const SkPoint & after,SkScalar radius,SkVector * normal,SkVector * unitNormal)93 static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after,
94 SkScalar radius,
95 SkVector* normal, SkVector* unitNormal) {
96 if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) {
97 return false;
98 }
99 unitNormal->rotateCCW();
100 unitNormal->scale(radius, normal);
101 return true;
102 }
103
set_normal_unitnormal(const SkVector & vec,SkScalar radius,SkVector * normal,SkVector * unitNormal)104 static bool set_normal_unitnormal(const SkVector& vec,
105 SkScalar radius,
106 SkVector* normal, SkVector* unitNormal) {
107 if (!unitNormal->setNormalize(vec.fX, vec.fY)) {
108 return false;
109 }
110 unitNormal->rotateCCW();
111 unitNormal->scale(radius, normal);
112 return true;
113 }
114
115 ///////////////////////////////////////////////////////////////////////////////
116 #ifndef SK_LEGACY_STROKE_CURVES
117
118 struct SkQuadConstruct { // The state of the quad stroke under construction.
119 SkPoint fQuad[3]; // the stroked quad parallel to the original curve
120 SkPoint fTangentStart; // a point tangent to fQuad[0]
121 SkPoint fTangentEnd; // a point tangent to fQuad[2]
122 SkScalar fStartT; // a segment of the original curve
123 SkScalar fMidT; // "
124 SkScalar fEndT; // "
125 bool fStartSet; // state to share common points across structs
126 bool fEndSet; // "
127
128 // return false if start and end are too close to have a unique middle
initSkQuadConstruct129 bool init(SkScalar start, SkScalar end) {
130 fStartT = start;
131 fMidT = (start + end) * SK_ScalarHalf;
132 fEndT = end;
133 fStartSet = fEndSet = false;
134 return fStartT < fMidT && fMidT < fEndT;
135 }
136
initWithStartSkQuadConstruct137 bool initWithStart(SkQuadConstruct* parent) {
138 if (!init(parent->fStartT, parent->fMidT)) {
139 return false;
140 }
141 fQuad[0] = parent->fQuad[0];
142 fTangentStart = parent->fTangentStart;
143 fStartSet = true;
144 return true;
145 }
146
initWithEndSkQuadConstruct147 bool initWithEnd(SkQuadConstruct* parent) {
148 if (!init(parent->fMidT, parent->fEndT)) {
149 return false;
150 }
151 fQuad[2] = parent->fQuad[2];
152 fTangentEnd = parent->fTangentEnd;
153 fEndSet = true;
154 return true;
155 }
156 };
157 #endif
158
159 class SkPathStroker {
160 public:
161 SkPathStroker(const SkPath& src,
162 SkScalar radius, SkScalar miterLimit, SkPaint::Cap,
163 SkPaint::Join, SkScalar resScale);
164
165 void moveTo(const SkPoint&);
166 void lineTo(const SkPoint&);
167 void quadTo(const SkPoint&, const SkPoint&);
168 #ifndef SK_LEGACY_STROKE_CURVES
169 void conicTo(const SkPoint&, const SkPoint&, SkScalar weight);
170 #endif
171 void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&);
close(bool isLine)172 void close(bool isLine) { this->finishContour(true, isLine); }
173
done(SkPath * dst,bool isLine)174 void done(SkPath* dst, bool isLine) {
175 this->finishContour(false, isLine);
176 fOuter.addPath(fExtra);
177 dst->swap(fOuter);
178 }
179
getResScale() const180 SkScalar getResScale() const { return fResScale; }
181
182 private:
183 SkScalar fRadius;
184 SkScalar fInvMiterLimit;
185 SkScalar fResScale;
186 #ifndef SK_LEGACY_STROKE_CURVES
187 SkScalar fInvResScale;
188 SkScalar fInvResScaleSquared;
189 #endif
190
191 SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal;
192 SkPoint fFirstPt, fPrevPt; // on original path
193 SkPoint fFirstOuterPt;
194 int fSegmentCount;
195 bool fPrevIsLine;
196
197 SkStrokerPriv::CapProc fCapper;
198 SkStrokerPriv::JoinProc fJoiner;
199
200 SkPath fInner, fOuter; // outer is our working answer, inner is temp
201 SkPath fExtra; // added as extra complete contours
202
203 #ifndef SK_LEGACY_STROKE_CURVES
204 enum StrokeType {
205 kOuter_StrokeType = 1, // use sign-opposite values later to flip perpendicular axis
206 kInner_StrokeType = -1
207 } fStrokeType;
208
209 enum ResultType {
210 kSplit_ResultType, // the caller should split the quad stroke in two
211 kDegenerate_ResultType, // the caller should add a line
212 kQuad_ResultType, // the caller should (continue to try to) add a quad stroke
213 kNormalError_ResultType, // the cubic's normal couldn't be computed -- abort
214 };
215
216 enum ReductionType {
217 kPoint_ReductionType, // all curve points are practically identical
218 kLine_ReductionType, // the control point is on the line between the ends
219 kQuad_ReductionType, // the control point is outside the line between the ends
220 kDegenerate_ReductionType, // the control point is on the line but outside the ends
221 kDegenerate2_ReductionType, // two control points are on the line but outside ends (cubic)
222 kDegenerate3_ReductionType, // three areas of max curvature found (for cubic)
223 };
224
225 enum IntersectRayType {
226 kCtrlPt_RayType,
227 kResultType_RayType,
228 };
229
230 int fRecursionDepth; // track stack depth to abort if numerics run amok
231 bool fFoundTangents; // do less work until tangents meet (cubic)
232
233 void addDegenerateLine(const SkQuadConstruct* );
234 ReductionType CheckConicLinear(const SkConic& , SkPoint* reduction);
235 ReductionType CheckCubicLinear(const SkPoint cubic[4], SkPoint reduction[3],
236 const SkPoint** tanPtPtr);
237 ReductionType CheckQuadLinear(const SkPoint quad[3], SkPoint* reduction);
238 ResultType compareQuadConic(const SkConic& , SkQuadConstruct* );
239 ResultType compareQuadCubic(const SkPoint cubic[4], SkQuadConstruct* );
240 ResultType compareQuadQuad(const SkPoint quad[3], SkQuadConstruct* );
241 bool conicPerpRay(const SkConic& , SkScalar t, SkPoint* tPt, SkPoint* onPt,
242 SkPoint* tangent) const;
243 bool conicQuadEnds(const SkConic& , SkQuadConstruct* );
244 bool conicStroke(const SkConic& , SkQuadConstruct* );
245 bool cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct* ) const;
246 bool cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tPt, SkPoint* onPt,
247 SkPoint* tangent) const;
248 bool cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* );
249 bool cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct* , SkPoint* mid) const;
250 bool cubicStroke(const SkPoint cubic[4], SkQuadConstruct* );
251 void init(StrokeType strokeType, SkQuadConstruct* , SkScalar tStart, SkScalar tEnd);
252 ResultType intersectRay(SkQuadConstruct* , IntersectRayType STROKER_DEBUG_PARAMS(int) ) const;
253 bool ptInQuadBounds(const SkPoint quad[3], const SkPoint& pt) const;
254 void quadPerpRay(const SkPoint quad[3], SkScalar t, SkPoint* tPt, SkPoint* onPt,
255 SkPoint* tangent) const;
256 bool quadStroke(const SkPoint quad[3], SkQuadConstruct* );
257 void setConicEndNormal(const SkConic& ,
258 const SkVector& normalAB, const SkVector& unitNormalAB,
259 SkVector* normalBC, SkVector* unitNormalBC);
260 void setCubicEndNormal(const SkPoint cubic[4],
261 const SkVector& normalAB, const SkVector& unitNormalAB,
262 SkVector* normalCD, SkVector* unitNormalCD);
263 void setQuadEndNormal(const SkPoint quad[3],
264 const SkVector& normalAB, const SkVector& unitNormalAB,
265 SkVector* normalBC, SkVector* unitNormalBC);
266 void setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt, SkPoint* tangent) const;
267 static bool SlightAngle(SkQuadConstruct* );
268 ResultType strokeCloseEnough(const SkPoint stroke[3], const SkPoint ray[2],
269 SkQuadConstruct* STROKER_DEBUG_PARAMS(int depth) ) const;
270 ResultType tangentsMeet(const SkPoint cubic[4], SkQuadConstruct* );
271 #endif
272
273 void finishContour(bool close, bool isLine);
274 bool preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal,
275 bool isLine);
276 void postJoinTo(const SkPoint&, const SkVector& normal,
277 const SkVector& unitNormal);
278
279 void line_to(const SkPoint& currPt, const SkVector& normal);
280 #ifdef SK_LEGACY_STROKE_CURVES
281 void quad_to(const SkPoint pts[3],
282 const SkVector& normalAB, const SkVector& unitNormalAB,
283 SkVector* normalBC, SkVector* unitNormalBC,
284 int subDivide);
285 void cubic_to(const SkPoint pts[4],
286 const SkVector& normalAB, const SkVector& unitNormalAB,
287 SkVector* normalCD, SkVector* unitNormalCD,
288 int subDivide);
289 #endif
290 };
291
292 ///////////////////////////////////////////////////////////////////////////////
293
preJoinTo(const SkPoint & currPt,SkVector * normal,SkVector * unitNormal,bool currIsLine)294 bool SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal,
295 SkVector* unitNormal, bool currIsLine) {
296 SkASSERT(fSegmentCount >= 0);
297
298 SkScalar prevX = fPrevPt.fX;
299 SkScalar prevY = fPrevPt.fY;
300
301 if (!set_normal_unitnormal(fPrevPt, currPt, fRadius, normal, unitNormal)) {
302 return false;
303 }
304
305 if (fSegmentCount == 0) {
306 fFirstNormal = *normal;
307 fFirstUnitNormal = *unitNormal;
308 fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY);
309
310 fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY);
311 fInner.moveTo(prevX - normal->fX, prevY - normal->fY);
312 } else { // we have a previous segment
313 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal,
314 fRadius, fInvMiterLimit, fPrevIsLine, currIsLine);
315 }
316 fPrevIsLine = currIsLine;
317 return true;
318 }
319
postJoinTo(const SkPoint & currPt,const SkVector & normal,const SkVector & unitNormal)320 void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal,
321 const SkVector& unitNormal) {
322 fPrevPt = currPt;
323 fPrevUnitNormal = unitNormal;
324 fPrevNormal = normal;
325 fSegmentCount += 1;
326 }
327
finishContour(bool close,bool currIsLine)328 void SkPathStroker::finishContour(bool close, bool currIsLine) {
329 if (fSegmentCount > 0) {
330 SkPoint pt;
331
332 if (close) {
333 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt,
334 fFirstUnitNormal, fRadius, fInvMiterLimit,
335 fPrevIsLine, currIsLine);
336 fOuter.close();
337 // now add fInner as its own contour
338 fInner.getLastPt(&pt);
339 fOuter.moveTo(pt.fX, pt.fY);
340 fOuter.reversePathTo(fInner);
341 fOuter.close();
342 } else { // add caps to start and end
343 // cap the end
344 fInner.getLastPt(&pt);
345 fCapper(&fOuter, fPrevPt, fPrevNormal, pt,
346 currIsLine ? &fInner : NULL);
347 fOuter.reversePathTo(fInner);
348 // cap the start
349 fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt,
350 fPrevIsLine ? &fInner : NULL);
351 fOuter.close();
352 }
353 }
354 // since we may re-use fInner, we rewind instead of reset, to save on
355 // reallocating its internal storage.
356 fInner.rewind();
357 fSegmentCount = -1;
358 }
359
360 ///////////////////////////////////////////////////////////////////////////////
361
SkPathStroker(const SkPath & src,SkScalar radius,SkScalar miterLimit,SkPaint::Cap cap,SkPaint::Join join,SkScalar resScale)362 SkPathStroker::SkPathStroker(const SkPath& src,
363 SkScalar radius, SkScalar miterLimit,
364 SkPaint::Cap cap, SkPaint::Join join, SkScalar resScale)
365 : fRadius(radius)
366 , fResScale(resScale) {
367
368 /* This is only used when join is miter_join, but we initialize it here
369 so that it is always defined, to fis valgrind warnings.
370 */
371 fInvMiterLimit = 0;
372
373 if (join == SkPaint::kMiter_Join) {
374 if (miterLimit <= SK_Scalar1) {
375 join = SkPaint::kBevel_Join;
376 } else {
377 fInvMiterLimit = SkScalarInvert(miterLimit);
378 }
379 }
380 fCapper = SkStrokerPriv::CapFactory(cap);
381 fJoiner = SkStrokerPriv::JoinFactory(join);
382 fSegmentCount = -1;
383 fPrevIsLine = false;
384
385 // Need some estimate of how large our final result (fOuter)
386 // and our per-contour temp (fInner) will be, so we don't spend
387 // extra time repeatedly growing these arrays.
388 //
389 // 3x for result == inner + outer + join (swag)
390 // 1x for inner == 'wag' (worst contour length would be better guess)
391 fOuter.incReserve(src.countPoints() * 3);
392 fOuter.setIsVolatile(true);
393 fInner.incReserve(src.countPoints());
394 fInner.setIsVolatile(true);
395 #ifndef SK_LEGACY_STROKE_CURVES
396 // TODO : write a common error function used by stroking and filling
397 // The '4' below matches the fill scan converter's error term
398 fInvResScale = SkScalarInvert(resScale * 4);
399 fInvResScaleSquared = fInvResScale * fInvResScale;
400 fRecursionDepth = 0;
401 #endif
402 }
403
moveTo(const SkPoint & pt)404 void SkPathStroker::moveTo(const SkPoint& pt) {
405 if (fSegmentCount > 0) {
406 this->finishContour(false, false);
407 }
408 fSegmentCount = 0;
409 fFirstPt = fPrevPt = pt;
410 }
411
line_to(const SkPoint & currPt,const SkVector & normal)412 void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) {
413 fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY);
414 fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY);
415 }
416
lineTo(const SkPoint & currPt)417 void SkPathStroker::lineTo(const SkPoint& currPt) {
418 if (SkPath::IsLineDegenerate(fPrevPt, currPt)) {
419 return;
420 }
421 SkVector normal, unitNormal;
422
423 if (!this->preJoinTo(currPt, &normal, &unitNormal, true)) {
424 return;
425 }
426 this->line_to(currPt, normal);
427 this->postJoinTo(currPt, normal, unitNormal);
428 }
429
430 #ifdef SK_LEGACY_STROKE_CURVES
quad_to(const SkPoint pts[3],const SkVector & normalAB,const SkVector & unitNormalAB,SkVector * normalBC,SkVector * unitNormalBC,int subDivide)431 void SkPathStroker::quad_to(const SkPoint pts[3],
432 const SkVector& normalAB, const SkVector& unitNormalAB,
433 SkVector* normalBC, SkVector* unitNormalBC,
434 int subDivide) {
435 if (!set_normal_unitnormal(pts[1], pts[2], fRadius,
436 normalBC, unitNormalBC)) {
437 // pts[1] nearly equals pts[2], so just draw a line to pts[2]
438 this->line_to(pts[2], normalAB);
439 *normalBC = normalAB;
440 *unitNormalBC = unitNormalAB;
441 return;
442 }
443
444 if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) {
445 SkPoint tmp[5];
446 SkVector norm, unit;
447
448 SkChopQuadAtHalf(pts, tmp);
449 this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide);
450 this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide);
451 } else {
452 SkVector normalB;
453
454 normalB = pts[2] - pts[0];
455 normalB.rotateCCW();
456 SkScalar dot = SkPoint::DotProduct(unitNormalAB, *unitNormalBC);
457 SkAssertResult(normalB.setLength(fRadius / SkScalarSqrt((SK_Scalar1 + dot)/2)));
458
459 fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
460 pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY);
461 fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
462 pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY);
463 }
464 }
465 #endif
466
467 #ifndef SK_LEGACY_STROKE_CURVES
setQuadEndNormal(const SkPoint quad[3],const SkVector & normalAB,const SkVector & unitNormalAB,SkVector * normalBC,SkVector * unitNormalBC)468 void SkPathStroker::setQuadEndNormal(const SkPoint quad[3], const SkVector& normalAB,
469 const SkVector& unitNormalAB, SkVector* normalBC, SkVector* unitNormalBC) {
470 if (!set_normal_unitnormal(quad[1], quad[2], fRadius, normalBC, unitNormalBC)) {
471 *normalBC = normalAB;
472 *unitNormalBC = unitNormalAB;
473 }
474 }
475
setConicEndNormal(const SkConic & conic,const SkVector & normalAB,const SkVector & unitNormalAB,SkVector * normalBC,SkVector * unitNormalBC)476 void SkPathStroker::setConicEndNormal(const SkConic& conic, const SkVector& normalAB,
477 const SkVector& unitNormalAB, SkVector* normalBC, SkVector* unitNormalBC) {
478 setQuadEndNormal(conic.fPts, normalAB, unitNormalAB, normalBC, unitNormalBC);
479 }
480
setCubicEndNormal(const SkPoint cubic[4],const SkVector & normalAB,const SkVector & unitNormalAB,SkVector * normalCD,SkVector * unitNormalCD)481 void SkPathStroker::setCubicEndNormal(const SkPoint cubic[4], const SkVector& normalAB,
482 const SkVector& unitNormalAB, SkVector* normalCD, SkVector* unitNormalCD) {
483 SkVector ab = cubic[1] - cubic[0];
484 SkVector cd = cubic[3] - cubic[2];
485
486 bool degenerateAB = degenerate_vector(ab);
487 bool degenerateCD = degenerate_vector(cd);
488
489 if (degenerateAB && degenerateCD) {
490 goto DEGENERATE_NORMAL;
491 }
492
493 if (degenerateAB) {
494 ab = cubic[2] - cubic[0];
495 degenerateAB = degenerate_vector(ab);
496 }
497 if (degenerateCD) {
498 cd = cubic[3] - cubic[1];
499 degenerateCD = degenerate_vector(cd);
500 }
501 if (degenerateAB || degenerateCD) {
502 DEGENERATE_NORMAL:
503 *normalCD = normalAB;
504 *unitNormalCD = unitNormalAB;
505 return;
506 }
507 SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD));
508 }
509
init(StrokeType strokeType,SkQuadConstruct * quadPts,SkScalar tStart,SkScalar tEnd)510 void SkPathStroker::init(StrokeType strokeType, SkQuadConstruct* quadPts, SkScalar tStart,
511 SkScalar tEnd) {
512 fStrokeType = strokeType;
513 fFoundTangents = false;
514 quadPts->init(tStart, tEnd);
515 }
516
517 // returns the distance squared from the point to the line
pt_to_line(const SkPoint & pt,const SkPoint & lineStart,const SkPoint & lineEnd)518 static SkScalar pt_to_line(const SkPoint& pt, const SkPoint& lineStart, const SkPoint& lineEnd) {
519 SkVector dxy = lineEnd - lineStart;
520 if (degenerate_vector(dxy)) {
521 return pt.distanceToSqd(lineStart);
522 }
523 SkVector ab0 = pt - lineStart;
524 SkScalar numer = dxy.dot(ab0);
525 SkScalar denom = dxy.dot(dxy);
526 SkScalar t = numer / denom;
527 SkPoint hit;
528 hit.fX = lineStart.fX * (1 - t) + lineEnd.fX * t;
529 hit.fY = lineStart.fY * (1 - t) + lineEnd.fY * t;
530 return hit.distanceToSqd(pt);
531 }
532
533 /* Given a cubic, determine if all four points are in a line.
534 Return true if the inner points is close to a line connecting the outermost points.
535
536 Find the outermost point by looking for the largest difference in X or Y.
537 Given the indices of the outermost points, and that outer_1 is greater than outer_2,
538 this table shows the index of the smaller of the remaining points:
539
540 outer_2
541 0 1 2 3
542 outer_1 ----------------
543 0 | - 2 1 1
544 1 | - - 0 0
545 2 | - - - 0
546 3 | - - - -
547
548 If outer_1 == 0 and outer_2 == 1, the smaller of the remaining indices (2 and 3) is 2.
549
550 This table can be collapsed to: (1 + (2 >> outer_2)) >> outer_1
551
552 Given three indices (outer_1 outer_2 mid_1) from 0..3, the remaining index is:
553
554 mid_2 == (outer_1 ^ outer_2 ^ mid_1)
555 */
cubic_in_line(const SkPoint cubic[4])556 static bool cubic_in_line(const SkPoint cubic[4]) {
557 SkScalar ptMax = -1;
558 int outer1 SK_INIT_TO_AVOID_WARNING;
559 int outer2 SK_INIT_TO_AVOID_WARNING;
560 for (int index = 0; index < 3; ++index) {
561 for (int inner = index + 1; inner < 4; ++inner) {
562 SkVector testDiff = cubic[inner] - cubic[index];
563 SkScalar testMax = SkTMax(SkScalarAbs(testDiff.fX), SkScalarAbs(testDiff.fY));
564 if (ptMax < testMax) {
565 outer1 = index;
566 outer2 = inner;
567 ptMax = testMax;
568 }
569 }
570 }
571 SkASSERT(outer1 >= 0 && outer1 <= 2);
572 SkASSERT(outer2 >= 1 && outer2 <= 3);
573 SkASSERT(outer1 < outer2);
574 int mid1 = (1 + (2 >> outer2)) >> outer1;
575 SkASSERT(mid1 >= 0 && mid1 <= 2);
576 SkASSERT(outer1 != mid1 && outer2 != mid1);
577 int mid2 = outer1 ^ outer2 ^ mid1;
578 SkASSERT(mid2 >= 1 && mid2 <= 3);
579 SkASSERT(mid2 != outer1 && mid2 != outer2 && mid2 != mid1);
580 SkASSERT(((1 << outer1) | (1 << outer2) | (1 << mid1) | (1 << mid2)) == 0x0f);
581 SkScalar lineSlop = ptMax * ptMax * 0.00001f; // this multiplier is pulled out of the air
582 return pt_to_line(cubic[mid1], cubic[outer1], cubic[outer2]) <= lineSlop
583 && pt_to_line(cubic[mid2], cubic[outer1], cubic[outer2]) <= lineSlop;
584 }
585
586 /* Given quad, see if all there points are in a line.
587 Return true if the inside point is close to a line connecting the outermost points.
588
589 Find the outermost point by looking for the largest difference in X or Y.
590 Since the XOR of the indices is 3 (0 ^ 1 ^ 2)
591 the missing index equals: outer_1 ^ outer_2 ^ 3
592 */
quad_in_line(const SkPoint quad[3])593 static bool quad_in_line(const SkPoint quad[3]) {
594 SkScalar ptMax = -1;
595 int outer1 SK_INIT_TO_AVOID_WARNING;
596 int outer2 SK_INIT_TO_AVOID_WARNING;
597 for (int index = 0; index < 2; ++index) {
598 for (int inner = index + 1; inner < 3; ++inner) {
599 SkVector testDiff = quad[inner] - quad[index];
600 SkScalar testMax = SkTMax(SkScalarAbs(testDiff.fX), SkScalarAbs(testDiff.fY));
601 if (ptMax < testMax) {
602 outer1 = index;
603 outer2 = inner;
604 ptMax = testMax;
605 }
606 }
607 }
608 SkASSERT(outer1 >= 0 && outer1 <= 1);
609 SkASSERT(outer2 >= 1 && outer2 <= 2);
610 SkASSERT(outer1 < outer2);
611 int mid = outer1 ^ outer2 ^ 3;
612 SkScalar lineSlop = ptMax * ptMax * 0.00001f; // this multiplier is pulled out of the air
613 return pt_to_line(quad[mid], quad[outer1], quad[outer2]) <= lineSlop;
614 }
615
conic_in_line(const SkConic & conic)616 static bool conic_in_line(const SkConic& conic) {
617 return quad_in_line(conic.fPts);
618 }
619
CheckCubicLinear(const SkPoint cubic[4],SkPoint reduction[3],const SkPoint ** tangentPtPtr)620 SkPathStroker::ReductionType SkPathStroker::CheckCubicLinear(const SkPoint cubic[4],
621 SkPoint reduction[3], const SkPoint** tangentPtPtr) {
622 bool degenerateAB = degenerate_vector(cubic[1] - cubic[0]);
623 bool degenerateBC = degenerate_vector(cubic[2] - cubic[1]);
624 bool degenerateCD = degenerate_vector(cubic[3] - cubic[2]);
625 if (degenerateAB & degenerateBC & degenerateCD) {
626 return kPoint_ReductionType;
627 }
628 if (degenerateAB + degenerateBC + degenerateCD == 2) {
629 return kLine_ReductionType;
630 }
631 if (!cubic_in_line(cubic)) {
632 *tangentPtPtr = degenerateAB ? &cubic[2] : &cubic[1];
633 return kQuad_ReductionType;
634 }
635 SkScalar tValues[3];
636 int count = SkFindCubicMaxCurvature(cubic, tValues);
637 if (count == 0) {
638 return kLine_ReductionType;
639 }
640 for (int index = 0; index < count; ++index) {
641 SkScalar t = tValues[index];
642 SkEvalCubicAt(cubic, t, &reduction[index], NULL, NULL);
643 }
644 SK_COMPILE_ASSERT(kQuad_ReductionType + 1 == kDegenerate_ReductionType, enum_out_of_whack);
645 SK_COMPILE_ASSERT(kQuad_ReductionType + 2 == kDegenerate2_ReductionType, enum_out_of_whack);
646 SK_COMPILE_ASSERT(kQuad_ReductionType + 3 == kDegenerate3_ReductionType, enum_out_of_whack);
647
648 return (ReductionType) (kQuad_ReductionType + count);
649 }
650
CheckConicLinear(const SkConic & conic,SkPoint * reduction)651 SkPathStroker::ReductionType SkPathStroker::CheckConicLinear(const SkConic& conic,
652 SkPoint* reduction) {
653 bool degenerateAB = degenerate_vector(conic.fPts[1] - conic.fPts[0]);
654 bool degenerateBC = degenerate_vector(conic.fPts[2] - conic.fPts[1]);
655 if (degenerateAB & degenerateBC) {
656 return kPoint_ReductionType;
657 }
658 if (degenerateAB | degenerateBC) {
659 return kLine_ReductionType;
660 }
661 if (!conic_in_line(conic)) {
662 return kQuad_ReductionType;
663 }
664 SkScalar t;
665 if (!conic.findMaxCurvature(&t) || 0 == t) {
666 return kLine_ReductionType;
667 }
668 conic.evalAt(t, reduction, NULL);
669 return kDegenerate_ReductionType;
670 }
671
CheckQuadLinear(const SkPoint quad[3],SkPoint * reduction)672 SkPathStroker::ReductionType SkPathStroker::CheckQuadLinear(const SkPoint quad[3],
673 SkPoint* reduction) {
674 bool degenerateAB = degenerate_vector(quad[1] - quad[0]);
675 bool degenerateBC = degenerate_vector(quad[2] - quad[1]);
676 if (degenerateAB & degenerateBC) {
677 return kPoint_ReductionType;
678 }
679 if (degenerateAB | degenerateBC) {
680 return kLine_ReductionType;
681 }
682 if (!quad_in_line(quad)) {
683 return kQuad_ReductionType;
684 }
685 SkScalar t = SkFindQuadMaxCurvature(quad);
686 if (0 == t) {
687 return kLine_ReductionType;
688 }
689 SkEvalQuadAt(quad, t, reduction, NULL);
690 return kDegenerate_ReductionType;
691 }
692
693 #else
694
cubic_to(const SkPoint pts[4],const SkVector & normalAB,const SkVector & unitNormalAB,SkVector * normalCD,SkVector * unitNormalCD,int subDivide)695 void SkPathStroker::cubic_to(const SkPoint pts[4],
696 const SkVector& normalAB, const SkVector& unitNormalAB,
697 SkVector* normalCD, SkVector* unitNormalCD,
698 int subDivide) {
699 SkVector ab = pts[1] - pts[0];
700 SkVector cd = pts[3] - pts[2];
701 SkVector normalBC, unitNormalBC;
702
703 bool degenerateAB = degenerate_vector(ab);
704 bool degenerateCD = degenerate_vector(cd);
705
706 if (degenerateAB && degenerateCD) {
707 DRAW_LINE:
708 this->line_to(pts[3], normalAB);
709 *normalCD = normalAB;
710 *unitNormalCD = unitNormalAB;
711 return;
712 }
713
714 if (degenerateAB) {
715 ab = pts[2] - pts[0];
716 degenerateAB = degenerate_vector(ab);
717 }
718 if (degenerateCD) {
719 cd = pts[3] - pts[1];
720 degenerateCD = degenerate_vector(cd);
721 }
722 if (degenerateAB || degenerateCD) {
723 goto DRAW_LINE;
724 }
725 SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD));
726 bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius,
727 &normalBC, &unitNormalBC);
728 #ifndef SK_IGNORE_CUBIC_STROKE_FIX
729 if (--subDivide < 0) {
730 goto DRAW_LINE;
731 }
732 #endif
733 if (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) ||
734 normals_too_curvy(unitNormalBC, *unitNormalCD)) {
735 #ifdef SK_IGNORE_CUBIC_STROKE_FIX
736 // subdivide if we can
737 if (--subDivide < 0) {
738 goto DRAW_LINE;
739 }
740 #endif
741 SkPoint tmp[7];
742 SkVector norm, unit, dummy, unitDummy;
743
744 SkChopCubicAtHalf(pts, tmp);
745 this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit,
746 subDivide);
747 // we use dummys since we already have a valid (and more accurate)
748 // normals for CD
749 this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide);
750 } else {
751 SkVector normalB, normalC;
752
753 // need normals to inset/outset the off-curve pts B and C
754
755 SkVector unitBC = pts[2] - pts[1];
756 unitBC.normalize();
757 unitBC.rotateCCW();
758
759 normalB = unitNormalAB + unitBC;
760 normalC = *unitNormalCD + unitBC;
761
762 SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC);
763 SkAssertResult(normalB.setLength(fRadius / SkScalarSqrt((SK_Scalar1 + dot)/2)));
764 dot = SkPoint::DotProduct(*unitNormalCD, unitBC);
765 SkAssertResult(normalC.setLength(fRadius / SkScalarSqrt((SK_Scalar1 + dot)/2)));
766
767 fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
768 pts[2].fX + normalC.fX, pts[2].fY + normalC.fY,
769 pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY);
770
771 fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
772 pts[2].fX - normalC.fX, pts[2].fY - normalC.fY,
773 pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY);
774 }
775 }
776 #endif
777
778 #ifndef SK_LEGACY_STROKE_CURVES
conicTo(const SkPoint & pt1,const SkPoint & pt2,SkScalar weight)779 void SkPathStroker::conicTo(const SkPoint& pt1, const SkPoint& pt2, SkScalar weight) {
780 const SkConic conic(fPrevPt, pt1, pt2, weight);
781 SkPoint reduction;
782 ReductionType reductionType = CheckConicLinear(conic, &reduction);
783 if (kPoint_ReductionType == reductionType) {
784 return;
785 }
786 if (kLine_ReductionType == reductionType) {
787 this->lineTo(pt2);
788 return;
789 }
790 if (kDegenerate_ReductionType == reductionType) {
791 this->lineTo(reduction);
792 SkStrokerPriv::JoinProc saveJoiner = fJoiner;
793 fJoiner = SkStrokerPriv::JoinFactory(SkPaint::kRound_Join);
794 this->lineTo(pt2);
795 fJoiner = saveJoiner;
796 return;
797 }
798 SkASSERT(kQuad_ReductionType == reductionType);
799 SkVector normalAB, unitAB, normalBC, unitBC;
800 if (!this->preJoinTo(pt1, &normalAB, &unitAB, false)) {
801 this->lineTo(pt2);
802 return;
803 }
804 SkQuadConstruct quadPts;
805 this->init(kOuter_StrokeType, &quadPts, 0, 1);
806 (void) this->conicStroke(conic, &quadPts);
807 this->init(kInner_StrokeType, &quadPts, 0, 1);
808 (void) this->conicStroke(conic, &quadPts);
809 this->setConicEndNormal(conic, normalAB, unitAB, &normalBC, &unitBC);
810 this->postJoinTo(pt2, normalBC, unitBC);
811 }
812 #endif
813
quadTo(const SkPoint & pt1,const SkPoint & pt2)814 void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
815 #ifndef SK_LEGACY_STROKE_CURVES
816 const SkPoint quad[3] = { fPrevPt, pt1, pt2 };
817 SkPoint reduction;
818 ReductionType reductionType = CheckQuadLinear(quad, &reduction);
819 if (kPoint_ReductionType == reductionType) {
820 return;
821 }
822 if (kLine_ReductionType == reductionType) {
823 this->lineTo(pt2);
824 return;
825 }
826 if (kDegenerate_ReductionType == reductionType) {
827 this->lineTo(reduction);
828 SkStrokerPriv::JoinProc saveJoiner = fJoiner;
829 fJoiner = SkStrokerPriv::JoinFactory(SkPaint::kRound_Join);
830 this->lineTo(pt2);
831 fJoiner = saveJoiner;
832 return;
833 }
834 SkASSERT(kQuad_ReductionType == reductionType);
835 SkVector normalAB, unitAB, normalBC, unitBC;
836 if (!this->preJoinTo(pt1, &normalAB, &unitAB, false)) {
837 this->lineTo(pt2);
838 return;
839 }
840 SkQuadConstruct quadPts;
841 this->init(kOuter_StrokeType, &quadPts, 0, 1);
842 (void) this->quadStroke(quad, &quadPts);
843 this->init(kInner_StrokeType, &quadPts, 0, 1);
844 (void) this->quadStroke(quad, &quadPts);
845 this->setQuadEndNormal(quad, normalAB, unitAB, &normalBC, &unitBC);
846 #else
847 bool degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
848 bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
849
850 if (degenerateAB | degenerateBC) {
851 if (degenerateAB ^ degenerateBC) {
852 this->lineTo(pt2);
853 }
854 return;
855 }
856
857 SkVector normalAB, unitAB, normalBC, unitBC;
858
859 this->preJoinTo(pt1, &normalAB, &unitAB, false);
860
861 {
862 SkPoint pts[3], tmp[5];
863 pts[0] = fPrevPt;
864 pts[1] = pt1;
865 pts[2] = pt2;
866
867 if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) {
868 unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY);
869 unitBC.rotateCCW();
870 if (normals_too_pinchy(unitAB, unitBC)) {
871 normalBC = unitBC;
872 normalBC.scale(fRadius);
873
874 fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY);
875 fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY);
876 fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY);
877
878 fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY);
879 fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY);
880 fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY);
881
882 fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius,
883 SkPath::kCW_Direction);
884 } else {
885 this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC,
886 kMaxQuadSubdivide);
887 SkVector n = normalBC;
888 SkVector u = unitBC;
889 this->quad_to(&tmp[2], n, u, &normalBC, &unitBC,
890 kMaxQuadSubdivide);
891 }
892 } else {
893 this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC,
894 kMaxQuadSubdivide);
895 }
896 }
897 #endif
898
899 this->postJoinTo(pt2, normalBC, unitBC);
900 }
901
902 #ifndef SK_LEGACY_STROKE_CURVES
903 // Given a point on the curve and its derivative, scale the derivative by the radius, and
904 // compute the perpendicular point and its tangent.
setRayPts(const SkPoint & tPt,SkVector * dxy,SkPoint * onPt,SkPoint * tangent) const905 void SkPathStroker::setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt,
906 SkPoint* tangent) const {
907 SkPoint oldDxy = *dxy;
908 if (!dxy->setLength(fRadius)) { // consider moving double logic into SkPoint::setLength
909 double xx = oldDxy.fX;
910 double yy = oldDxy.fY;
911 double dscale = fRadius / sqrt(xx * xx + yy * yy);
912 dxy->fX = SkDoubleToScalar(xx * dscale);
913 dxy->fY = SkDoubleToScalar(yy * dscale);
914 }
915 SkScalar axisFlip = SkIntToScalar(fStrokeType); // go opposite ways for outer, inner
916 onPt->fX = tPt.fX + axisFlip * dxy->fY;
917 onPt->fY = tPt.fY - axisFlip * dxy->fX;
918 if (tangent) {
919 tangent->fX = onPt->fX + dxy->fX;
920 tangent->fY = onPt->fY + dxy->fY;
921 }
922 }
923
924 // Given a conic and t, return the point on curve, its perpendicular, and the perpendicular tangent.
925 // Returns false if the perpendicular could not be computed (because the derivative collapsed to 0)
conicPerpRay(const SkConic & conic,SkScalar t,SkPoint * tPt,SkPoint * onPt,SkPoint * tangent) const926 bool SkPathStroker::conicPerpRay(const SkConic& conic, SkScalar t, SkPoint* tPt, SkPoint* onPt,
927 SkPoint* tangent) const {
928 SkVector dxy;
929 conic.evalAt(t, tPt, &dxy);
930 if (dxy.fX == 0 && dxy.fY == 0) {
931 dxy = conic.fPts[2] - conic.fPts[0];
932 }
933 setRayPts(*tPt, &dxy, onPt, tangent);
934 return true;
935 }
936
937 // Given a conic and a t range, find the start and end if they haven't been found already.
conicQuadEnds(const SkConic & conic,SkQuadConstruct * quadPts)938 bool SkPathStroker::conicQuadEnds(const SkConic& conic, SkQuadConstruct* quadPts) {
939 if (!quadPts->fStartSet) {
940 SkPoint conicStartPt;
941 if (!this->conicPerpRay(conic, quadPts->fStartT, &conicStartPt, &quadPts->fQuad[0],
942 &quadPts->fTangentStart)) {
943 return false;
944 }
945 quadPts->fStartSet = true;
946 }
947 if (!quadPts->fEndSet) {
948 SkPoint conicEndPt;
949 if (!this->conicPerpRay(conic, quadPts->fEndT, &conicEndPt, &quadPts->fQuad[2],
950 &quadPts->fTangentEnd)) {
951 return false;
952 }
953 quadPts->fEndSet = true;
954 }
955 return true;
956 }
957
958
959 // Given a cubic and t, return the point on curve, its perpendicular, and the perpendicular tangent.
960 // Returns false if the perpendicular could not be computed (because the derivative collapsed to 0)
cubicPerpRay(const SkPoint cubic[4],SkScalar t,SkPoint * tPt,SkPoint * onPt,SkPoint * tangent) const961 bool SkPathStroker::cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tPt, SkPoint* onPt,
962 SkPoint* tangent) const {
963 SkVector dxy;
964 SkEvalCubicAt(cubic, t, tPt, &dxy, NULL);
965 if (dxy.fX == 0 && dxy.fY == 0) {
966 if (SkScalarNearlyZero(t)) {
967 dxy = cubic[2] - cubic[0];
968 } else if (SkScalarNearlyZero(1 - t)) {
969 dxy = cubic[3] - cubic[1];
970 } else {
971 return false;
972 }
973 if (dxy.fX == 0 && dxy.fY == 0) {
974 dxy = cubic[3] - cubic[0];
975 }
976 }
977 setRayPts(*tPt, &dxy, onPt, tangent);
978 return true;
979 }
980
981 // Given a cubic and a t range, find the start and end if they haven't been found already.
cubicQuadEnds(const SkPoint cubic[4],SkQuadConstruct * quadPts)982 bool SkPathStroker::cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* quadPts) {
983 if (!quadPts->fStartSet) {
984 SkPoint cubicStartPt;
985 if (!this->cubicPerpRay(cubic, quadPts->fStartT, &cubicStartPt, &quadPts->fQuad[0],
986 &quadPts->fTangentStart)) {
987 return false;
988 }
989 quadPts->fStartSet = true;
990 }
991 if (!quadPts->fEndSet) {
992 SkPoint cubicEndPt;
993 if (!this->cubicPerpRay(cubic, quadPts->fEndT, &cubicEndPt, &quadPts->fQuad[2],
994 &quadPts->fTangentEnd)) {
995 return false;
996 }
997 quadPts->fEndSet = true;
998 }
999 return true;
1000 }
1001
cubicQuadMid(const SkPoint cubic[4],const SkQuadConstruct * quadPts,SkPoint * mid) const1002 bool SkPathStroker::cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct* quadPts,
1003 SkPoint* mid) const {
1004 SkPoint cubicMidPt;
1005 return this->cubicPerpRay(cubic, quadPts->fMidT, &cubicMidPt, mid, NULL);
1006 }
1007
1008 // Given a quad and t, return the point on curve, its perpendicular, and the perpendicular tangent.
quadPerpRay(const SkPoint quad[3],SkScalar t,SkPoint * tPt,SkPoint * onPt,SkPoint * tangent) const1009 void SkPathStroker::quadPerpRay(const SkPoint quad[3], SkScalar t, SkPoint* tPt, SkPoint* onPt,
1010 SkPoint* tangent) const {
1011 SkVector dxy;
1012 SkEvalQuadAt(quad, t, tPt, &dxy);
1013 if (dxy.fX == 0 && dxy.fY == 0) {
1014 dxy = quad[2] - quad[0];
1015 }
1016 setRayPts(*tPt, &dxy, onPt, tangent);
1017 }
1018
1019 // Find the intersection of the stroke tangents to construct a stroke quad.
1020 // Return whether the stroke is a degenerate (a line), a quad, or must be split.
1021 // Optionally compute the quad's control point.
intersectRay(SkQuadConstruct * quadPts,IntersectRayType intersectRayType STROKER_DEBUG_PARAMS (int depth)) const1022 SkPathStroker::ResultType SkPathStroker::intersectRay(SkQuadConstruct* quadPts,
1023 IntersectRayType intersectRayType STROKER_DEBUG_PARAMS(int depth)) const {
1024 const SkPoint& start = quadPts->fQuad[0];
1025 const SkPoint& end = quadPts->fQuad[2];
1026 SkVector aLen = quadPts->fTangentStart - start;
1027 SkVector bLen = quadPts->fTangentEnd - end;
1028 SkScalar denom = aLen.cross(bLen);
1029 SkVector ab0 = start - end;
1030 SkScalar numerA = bLen.cross(ab0);
1031 SkScalar numerB = aLen.cross(ab0);
1032 if (!SkScalarNearlyZero(denom)) {
1033 // if the perpendicular distances from the quad points to the opposite tangent line
1034 // are small, a straight line is good enough
1035 SkScalar dist1 = pt_to_line(start, end, quadPts->fTangentEnd);
1036 SkScalar dist2 = pt_to_line(end, start, quadPts->fTangentStart);
1037 if ((numerA >= 0) != (numerB >= 0)) {
1038 if (kCtrlPt_RayType == intersectRayType) {
1039 numerA /= denom;
1040 SkPoint* ctrlPt = &quadPts->fQuad[1];
1041 ctrlPt->fX = start.fX * (1 - numerA) + quadPts->fTangentStart.fX * numerA;
1042 ctrlPt->fY = start.fY * (1 - numerA) + quadPts->fTangentStart.fY * numerA;
1043 }
1044 return STROKER_RESULT(kQuad_ResultType, depth, quadPts,
1045 "(numerA=%g >= 0) != (numerB=%g >= 0)", numerA, numerB);
1046 }
1047 if (SkTMax(dist1, dist2) <= fInvResScaleSquared) {
1048 return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts,
1049 "SkTMax(dist1=%g, dist2=%g) <= fInvResScaleSquared", dist1, dist2);
1050 }
1051 return STROKER_RESULT(kSplit_ResultType, depth, quadPts,
1052 "(numerA=%g >= 0) == (numerB=%g >= 0)", numerA, numerB);
1053 } else { // if the lines are parallel, straight line is good enough
1054 return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts,
1055 "SkScalarNearlyZero(denom=%g)", denom);
1056 }
1057 }
1058
1059 // Given a cubic and a t-range, determine if the stroke can be described by a quadratic.
tangentsMeet(const SkPoint cubic[4],SkQuadConstruct * quadPts)1060 SkPathStroker::ResultType SkPathStroker::tangentsMeet(const SkPoint cubic[4],
1061 SkQuadConstruct* quadPts) {
1062 if (!this->cubicQuadEnds(cubic, quadPts)) {
1063 return kNormalError_ResultType;
1064 }
1065 return intersectRay(quadPts, kResultType_RayType STROKER_DEBUG_PARAMS(fRecursionDepth));
1066 }
1067
1068 // Intersect the line with the quad and return the t values on the quad where the line crosses.
intersect_quad_ray(const SkPoint line[2],const SkPoint quad[3],SkScalar roots[2])1069 static int intersect_quad_ray(const SkPoint line[2], const SkPoint quad[3], SkScalar roots[2]) {
1070 SkVector vec = line[1] - line[0];
1071 SkScalar r[3];
1072 for (int n = 0; n < 3; ++n) {
1073 r[n] = (quad[n].fY - line[0].fY) * vec.fX - (quad[n].fX - line[0].fX) * vec.fY;
1074 }
1075 SkScalar A = r[2];
1076 SkScalar B = r[1];
1077 SkScalar C = r[0];
1078 A += C - 2 * B; // A = a - 2*b + c
1079 B -= C; // B = -(b - c)
1080 return SkFindUnitQuadRoots(A, 2 * B, C, roots);
1081 }
1082
1083 // Return true if the point is close to the bounds of the quad. This is used as a quick reject.
ptInQuadBounds(const SkPoint quad[3],const SkPoint & pt) const1084 bool SkPathStroker::ptInQuadBounds(const SkPoint quad[3], const SkPoint& pt) const {
1085 SkScalar xMin = SkTMin(SkTMin(quad[0].fX, quad[1].fX), quad[2].fX);
1086 if (pt.fX + fInvResScale < xMin) {
1087 return false;
1088 }
1089 SkScalar xMax = SkTMax(SkTMax(quad[0].fX, quad[1].fX), quad[2].fX);
1090 if (pt.fX - fInvResScale > xMax) {
1091 return false;
1092 }
1093 SkScalar yMin = SkTMin(SkTMin(quad[0].fY, quad[1].fY), quad[2].fY);
1094 if (pt.fY + fInvResScale < yMin) {
1095 return false;
1096 }
1097 SkScalar yMax = SkTMax(SkTMax(quad[0].fY, quad[1].fY), quad[2].fY);
1098 if (pt.fY - fInvResScale > yMax) {
1099 return false;
1100 }
1101 return true;
1102 }
1103
points_within_dist(const SkPoint & nearPt,const SkPoint & farPt,SkScalar limit)1104 static bool points_within_dist(const SkPoint& nearPt, const SkPoint& farPt, SkScalar limit) {
1105 return nearPt.distanceToSqd(farPt) <= limit * limit;
1106 }
1107
sharp_angle(const SkPoint quad[3])1108 static bool sharp_angle(const SkPoint quad[3]) {
1109 SkVector smaller = quad[1] - quad[0];
1110 SkVector larger = quad[1] - quad[2];
1111 SkScalar smallerLen = smaller.lengthSqd();
1112 SkScalar largerLen = larger.lengthSqd();
1113 if (smallerLen > largerLen) {
1114 SkTSwap(smaller, larger);
1115 largerLen = smallerLen;
1116 }
1117 if (!smaller.setLength(largerLen)) {
1118 return false;
1119 }
1120 SkScalar dot = smaller.dot(larger);
1121 return dot > 0;
1122 }
1123
strokeCloseEnough(const SkPoint stroke[3],const SkPoint ray[2],SkQuadConstruct * quadPts STROKER_DEBUG_PARAMS (int depth)) const1124 SkPathStroker::ResultType SkPathStroker::strokeCloseEnough(const SkPoint stroke[3],
1125 const SkPoint ray[2], SkQuadConstruct* quadPts STROKER_DEBUG_PARAMS(int depth)) const {
1126 SkPoint strokeMid;
1127 SkEvalQuadAt(stroke, SK_ScalarHalf, &strokeMid);
1128 // measure the distance from the curve to the quad-stroke midpoint, compare to radius
1129 if (points_within_dist(ray[0], strokeMid, fInvResScale)) { // if the difference is small
1130 if (sharp_angle(quadPts->fQuad)) {
1131 return STROKER_RESULT(kSplit_ResultType, depth, quadPts,
1132 "sharp_angle (1) =%g,%g, %g,%g, %g,%g",
1133 quadPts->fQuad[0].fX, quadPts->fQuad[0].fY,
1134 quadPts->fQuad[1].fX, quadPts->fQuad[1].fY,
1135 quadPts->fQuad[2].fX, quadPts->fQuad[2].fY);
1136 }
1137 return STROKER_RESULT(kQuad_ResultType, depth, quadPts,
1138 "points_within_dist(ray[0]=%g,%g, strokeMid=%g,%g, fInvResScale=%g)",
1139 ray[0].fX, ray[0].fY, strokeMid.fX, strokeMid.fY, fInvResScale);
1140 }
1141 // measure the distance to quad's bounds (quick reject)
1142 // an alternative : look for point in triangle
1143 if (!ptInQuadBounds(stroke, ray[0])) { // if far, subdivide
1144 return STROKER_RESULT(kSplit_ResultType, depth, quadPts,
1145 "!pt_in_quad_bounds(stroke=(%g,%g %g,%g %g,%g), ray[0]=%g,%g)",
1146 stroke[0].fX, stroke[0].fY, stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY,
1147 ray[0].fX, ray[0].fY);
1148 }
1149 // measure the curve ray distance to the quad-stroke
1150 SkScalar roots[2];
1151 int rootCount = intersect_quad_ray(ray, stroke, roots);
1152 if (rootCount != 1) {
1153 return STROKER_RESULT(kSplit_ResultType, depth, quadPts,
1154 "rootCount=%d != 1", rootCount);
1155 }
1156 SkPoint quadPt;
1157 SkEvalQuadAt(stroke, roots[0], &quadPt);
1158 SkScalar error = fInvResScale * (SK_Scalar1 - SkScalarAbs(roots[0] - 0.5f) * 2);
1159 if (points_within_dist(ray[0], quadPt, error)) { // if the difference is small, we're done
1160 if (sharp_angle(quadPts->fQuad)) {
1161 return STROKER_RESULT(kSplit_ResultType, depth, quadPts,
1162 "sharp_angle (2) =%g,%g, %g,%g, %g,%g",
1163 quadPts->fQuad[0].fX, quadPts->fQuad[0].fY,
1164 quadPts->fQuad[1].fX, quadPts->fQuad[1].fY,
1165 quadPts->fQuad[2].fX, quadPts->fQuad[2].fY);
1166 }
1167 return STROKER_RESULT(kQuad_ResultType, depth, quadPts,
1168 "points_within_dist(ray[0]=%g,%g, quadPt=%g,%g, error=%g)",
1169 ray[0].fX, ray[0].fY, quadPt.fX, quadPt.fY, error);
1170 }
1171 // otherwise, subdivide
1172 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, "%s", "fall through");
1173 }
1174
compareQuadCubic(const SkPoint cubic[4],SkQuadConstruct * quadPts)1175 SkPathStroker::ResultType SkPathStroker::compareQuadCubic(const SkPoint cubic[4],
1176 SkQuadConstruct* quadPts) {
1177 // get the quadratic approximation of the stroke
1178 if (!this->cubicQuadEnds(cubic, quadPts)) {
1179 return kNormalError_ResultType;
1180 }
1181 ResultType resultType = intersectRay(quadPts, kCtrlPt_RayType
1182 STROKER_DEBUG_PARAMS(fRecursionDepth) );
1183 if (resultType != kQuad_ResultType) {
1184 return resultType;
1185 }
1186 // project a ray from the curve to the stroke
1187 SkPoint ray[2]; // points near midpoint on quad, midpoint on cubic
1188 if (!this->cubicPerpRay(cubic, quadPts->fMidT, &ray[1], &ray[0], NULL)) {
1189 return kNormalError_ResultType;
1190 }
1191 return strokeCloseEnough(quadPts->fQuad, ray, quadPts STROKER_DEBUG_PARAMS(fRecursionDepth));
1192 }
1193
compareQuadConic(const SkConic & conic,SkQuadConstruct * quadPts)1194 SkPathStroker::ResultType SkPathStroker::compareQuadConic(const SkConic& conic,
1195 SkQuadConstruct* quadPts) {
1196 // get the quadratic approximation of the stroke
1197 if (!this->conicQuadEnds(conic, quadPts)) {
1198 return kNormalError_ResultType;
1199 }
1200 ResultType resultType = intersectRay(quadPts, kCtrlPt_RayType
1201 STROKER_DEBUG_PARAMS(fRecursionDepth) );
1202 if (resultType != kQuad_ResultType) {
1203 return resultType;
1204 }
1205 // project a ray from the curve to the stroke
1206 SkPoint ray[2]; // points near midpoint on quad, midpoint on conic
1207 if (!this->conicPerpRay(conic, quadPts->fMidT, &ray[1], &ray[0], NULL)) {
1208 return kNormalError_ResultType;
1209 }
1210 return strokeCloseEnough(quadPts->fQuad, ray, quadPts STROKER_DEBUG_PARAMS(fRecursionDepth));
1211 }
1212
compareQuadQuad(const SkPoint quad[3],SkQuadConstruct * quadPts)1213 SkPathStroker::ResultType SkPathStroker::compareQuadQuad(const SkPoint quad[3],
1214 SkQuadConstruct* quadPts) {
1215 // get the quadratic approximation of the stroke
1216 if (!quadPts->fStartSet) {
1217 SkPoint quadStartPt;
1218 this->quadPerpRay(quad, quadPts->fStartT, &quadStartPt, &quadPts->fQuad[0],
1219 &quadPts->fTangentStart);
1220 quadPts->fStartSet = true;
1221 }
1222 if (!quadPts->fEndSet) {
1223 SkPoint quadEndPt;
1224 this->quadPerpRay(quad, quadPts->fEndT, &quadEndPt, &quadPts->fQuad[2],
1225 &quadPts->fTangentEnd);
1226 quadPts->fEndSet = true;
1227 }
1228 ResultType resultType = intersectRay(quadPts, kCtrlPt_RayType
1229 STROKER_DEBUG_PARAMS(fRecursionDepth));
1230 if (resultType != kQuad_ResultType) {
1231 return resultType;
1232 }
1233 // project a ray from the curve to the stroke
1234 SkPoint ray[2];
1235 this->quadPerpRay(quad, quadPts->fMidT, &ray[1], &ray[0], NULL);
1236 return strokeCloseEnough(quadPts->fQuad, ray, quadPts STROKER_DEBUG_PARAMS(fRecursionDepth));
1237 }
1238
addDegenerateLine(const SkQuadConstruct * quadPts)1239 void SkPathStroker::addDegenerateLine(const SkQuadConstruct* quadPts) {
1240 const SkPoint* quad = quadPts->fQuad;
1241 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner;
1242 path->lineTo(quad[2].fX, quad[2].fY);
1243 }
1244
cubicMidOnLine(const SkPoint cubic[4],const SkQuadConstruct * quadPts) const1245 bool SkPathStroker::cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct* quadPts) const {
1246 SkPoint strokeMid;
1247 if (!cubicQuadMid(cubic, quadPts, &strokeMid)) {
1248 return false;
1249 }
1250 SkScalar dist = pt_to_line(strokeMid, quadPts->fQuad[0], quadPts->fQuad[2]);
1251 return dist < fInvResScaleSquared;
1252 }
1253
cubicStroke(const SkPoint cubic[4],SkQuadConstruct * quadPts)1254 bool SkPathStroker::cubicStroke(const SkPoint cubic[4], SkQuadConstruct* quadPts) {
1255 if (!fFoundTangents) {
1256 ResultType resultType = this->tangentsMeet(cubic, quadPts);
1257 if (kQuad_ResultType != resultType) {
1258 if (kNormalError_ResultType == resultType) {
1259 return false;
1260 }
1261 if ((kDegenerate_ResultType == resultType
1262 || points_within_dist(quadPts->fQuad[0], quadPts->fQuad[2],
1263 fInvResScale)) && cubicMidOnLine(cubic, quadPts)) {
1264 addDegenerateLine(quadPts);
1265 return true;
1266 }
1267 } else {
1268 fFoundTangents = true;
1269 }
1270 }
1271 if (fFoundTangents) {
1272 ResultType resultType = this->compareQuadCubic(cubic, quadPts);
1273 if (kQuad_ResultType == resultType) {
1274 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner;
1275 const SkPoint* stroke = quadPts->fQuad;
1276 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY);
1277 return true;
1278 }
1279 if (kDegenerate_ResultType == resultType) {
1280 addDegenerateLine(quadPts);
1281 return true;
1282 }
1283 if (kNormalError_ResultType == resultType) {
1284 return false;
1285 }
1286 }
1287 if (!SkScalarIsFinite(quadPts->fQuad[2].fX) || !SkScalarIsFinite(quadPts->fQuad[2].fY)) {
1288 return false; // just abort if projected quad isn't representable
1289 }
1290 SkDEBUGCODE(gMaxRecursion[fFoundTangents] = SkTMax(gMaxRecursion[fFoundTangents],
1291 fRecursionDepth + 1));
1292 if (++fRecursionDepth > kRecursiveLimits[fFoundTangents]) {
1293 return false; // just abort if projected quad isn't representable
1294 }
1295 SkQuadConstruct half;
1296 if (!half.initWithStart(quadPts)) {
1297 addDegenerateLine(quadPts);
1298 return true;
1299 }
1300 if (!this->cubicStroke(cubic, &half)) {
1301 return false;
1302 }
1303 if (!half.initWithEnd(quadPts)) {
1304 addDegenerateLine(quadPts);
1305 return true;
1306 }
1307 if (!this->cubicStroke(cubic, &half)) {
1308 return false;
1309 }
1310 --fRecursionDepth;
1311 return true;
1312 }
1313
conicStroke(const SkConic & conic,SkQuadConstruct * quadPts)1314 bool SkPathStroker::conicStroke(const SkConic& conic, SkQuadConstruct* quadPts) {
1315 ResultType resultType = this->compareQuadConic(conic, quadPts);
1316 if (kQuad_ResultType == resultType) {
1317 const SkPoint* stroke = quadPts->fQuad;
1318 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner;
1319 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY);
1320 return true;
1321 }
1322 if (kDegenerate_ResultType == resultType) {
1323 addDegenerateLine(quadPts);
1324 return true;
1325 }
1326 SkDEBUGCODE(gMaxRecursion[kConic_RecursiveLimit] = SkTMax(gMaxRecursion[kConic_RecursiveLimit],
1327 fRecursionDepth + 1));
1328 if (++fRecursionDepth > kRecursiveLimits[kConic_RecursiveLimit]) {
1329 return false; // just abort if projected quad isn't representable
1330 }
1331 SkQuadConstruct half;
1332 (void) half.initWithStart(quadPts);
1333 if (!this->conicStroke(conic, &half)) {
1334 return false;
1335 }
1336 (void) half.initWithEnd(quadPts);
1337 if (!this->conicStroke(conic, &half)) {
1338 return false;
1339 }
1340 --fRecursionDepth;
1341 return true;
1342 }
1343
quadStroke(const SkPoint quad[3],SkQuadConstruct * quadPts)1344 bool SkPathStroker::quadStroke(const SkPoint quad[3], SkQuadConstruct* quadPts) {
1345 ResultType resultType = this->compareQuadQuad(quad, quadPts);
1346 if (kQuad_ResultType == resultType) {
1347 const SkPoint* stroke = quadPts->fQuad;
1348 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner;
1349 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY);
1350 return true;
1351 }
1352 if (kDegenerate_ResultType == resultType) {
1353 addDegenerateLine(quadPts);
1354 return true;
1355 }
1356 SkDEBUGCODE(gMaxRecursion[kQuad_RecursiveLimit] = SkTMax(gMaxRecursion[kQuad_RecursiveLimit],
1357 fRecursionDepth + 1));
1358 if (++fRecursionDepth > kRecursiveLimits[kQuad_RecursiveLimit]) {
1359 return false; // just abort if projected quad isn't representable
1360 }
1361 SkQuadConstruct half;
1362 (void) half.initWithStart(quadPts);
1363 if (!this->quadStroke(quad, &half)) {
1364 return false;
1365 }
1366 (void) half.initWithEnd(quadPts);
1367 if (!this->quadStroke(quad, &half)) {
1368 return false;
1369 }
1370 --fRecursionDepth;
1371 return true;
1372 }
1373
1374 #endif
1375
cubicTo(const SkPoint & pt1,const SkPoint & pt2,const SkPoint & pt3)1376 void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2,
1377 const SkPoint& pt3) {
1378 #ifndef SK_LEGACY_STROKE_CURVES
1379 const SkPoint cubic[4] = { fPrevPt, pt1, pt2, pt3 };
1380 SkPoint reduction[3];
1381 const SkPoint* tangentPt;
1382 ReductionType reductionType = CheckCubicLinear(cubic, reduction, &tangentPt);
1383 if (kPoint_ReductionType == reductionType) {
1384 return;
1385 }
1386 if (kLine_ReductionType == reductionType) {
1387 this->lineTo(pt3);
1388 return;
1389 }
1390 if (kDegenerate_ReductionType <= reductionType && kDegenerate3_ReductionType >= reductionType) {
1391 this->lineTo(reduction[0]);
1392 SkStrokerPriv::JoinProc saveJoiner = fJoiner;
1393 fJoiner = SkStrokerPriv::JoinFactory(SkPaint::kRound_Join);
1394 if (kDegenerate2_ReductionType <= reductionType) {
1395 this->lineTo(reduction[1]);
1396 }
1397 if (kDegenerate3_ReductionType == reductionType) {
1398 this->lineTo(reduction[2]);
1399 }
1400 this->lineTo(pt3);
1401 fJoiner = saveJoiner;
1402 return;
1403 }
1404 SkASSERT(kQuad_ReductionType == reductionType);
1405 SkVector normalAB, unitAB, normalCD, unitCD;
1406 if (!this->preJoinTo(*tangentPt, &normalAB, &unitAB, false)) {
1407 this->lineTo(pt3);
1408 return;
1409 }
1410 SkScalar tValues[2];
1411 int count = SkFindCubicInflections(cubic, tValues);
1412 SkScalar lastT = 0;
1413 for (int index = 0; index <= count; ++index) {
1414 SkScalar nextT = index < count ? tValues[index] : 1;
1415 SkQuadConstruct quadPts;
1416 this->init(kOuter_StrokeType, &quadPts, lastT, nextT);
1417 (void) this->cubicStroke(cubic, &quadPts);
1418 this->init(kInner_StrokeType, &quadPts, lastT, nextT);
1419 (void) this->cubicStroke(cubic, &quadPts);
1420 lastT = nextT;
1421 }
1422 // emit the join even if one stroke succeeded but the last one failed
1423 // this avoids reversing an inner stroke with a partial path followed by another moveto
1424 this->setCubicEndNormal(cubic, normalAB, unitAB, &normalCD, &unitCD);
1425 #else
1426 bool degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
1427 bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
1428 bool degenerateCD = SkPath::IsLineDegenerate(pt2, pt3);
1429
1430 if (degenerateAB + degenerateBC + degenerateCD >= 2
1431 || (degenerateAB && SkPath::IsLineDegenerate(fPrevPt, pt2))) {
1432 this->lineTo(pt3);
1433 return;
1434 }
1435
1436 SkVector normalAB, unitAB, normalCD, unitCD;
1437
1438 // find the first tangent (which might be pt1 or pt2
1439 {
1440 const SkPoint* nextPt = &pt1;
1441 if (degenerateAB)
1442 nextPt = &pt2;
1443 this->preJoinTo(*nextPt, &normalAB, &unitAB, false);
1444 }
1445
1446 {
1447 SkPoint pts[4], tmp[13];
1448 int i, count;
1449 SkVector n, u;
1450 SkScalar tValues[3];
1451
1452 pts[0] = fPrevPt;
1453 pts[1] = pt1;
1454 pts[2] = pt2;
1455 pts[3] = pt3;
1456
1457 count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
1458 n = normalAB;
1459 u = unitAB;
1460 for (i = 0; i < count; i++) {
1461 this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD,
1462 kMaxCubicSubdivide);
1463 if (i == count - 1) {
1464 break;
1465 }
1466 n = normalCD;
1467 u = unitCD;
1468
1469 }
1470 }
1471 #endif
1472
1473 this->postJoinTo(pt3, normalCD, unitCD);
1474 }
1475
1476 ///////////////////////////////////////////////////////////////////////////////
1477 ///////////////////////////////////////////////////////////////////////////////
1478
1479 #include "SkPaintDefaults.h"
1480
SkStroke()1481 SkStroke::SkStroke() {
1482 fWidth = SK_Scalar1;
1483 fMiterLimit = SkPaintDefaults_MiterLimit;
1484 fCap = SkPaint::kDefault_Cap;
1485 fJoin = SkPaint::kDefault_Join;
1486 fDoFill = false;
1487 }
1488
SkStroke(const SkPaint & p)1489 SkStroke::SkStroke(const SkPaint& p) {
1490 fWidth = p.getStrokeWidth();
1491 fMiterLimit = p.getStrokeMiter();
1492 fCap = (uint8_t)p.getStrokeCap();
1493 fJoin = (uint8_t)p.getStrokeJoin();
1494 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
1495 }
1496
SkStroke(const SkPaint & p,SkScalar width)1497 SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
1498 fWidth = width;
1499 fMiterLimit = p.getStrokeMiter();
1500 fCap = (uint8_t)p.getStrokeCap();
1501 fJoin = (uint8_t)p.getStrokeJoin();
1502 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
1503 }
1504
setWidth(SkScalar width)1505 void SkStroke::setWidth(SkScalar width) {
1506 SkASSERT(width >= 0);
1507 fWidth = width;
1508 }
1509
setMiterLimit(SkScalar miterLimit)1510 void SkStroke::setMiterLimit(SkScalar miterLimit) {
1511 SkASSERT(miterLimit >= 0);
1512 fMiterLimit = miterLimit;
1513 }
1514
setCap(SkPaint::Cap cap)1515 void SkStroke::setCap(SkPaint::Cap cap) {
1516 SkASSERT((unsigned)cap < SkPaint::kCapCount);
1517 fCap = SkToU8(cap);
1518 }
1519
setJoin(SkPaint::Join join)1520 void SkStroke::setJoin(SkPaint::Join join) {
1521 SkASSERT((unsigned)join < SkPaint::kJoinCount);
1522 fJoin = SkToU8(join);
1523 }
1524
1525 ///////////////////////////////////////////////////////////////////////////////
1526
1527 // If src==dst, then we use a tmp path to record the stroke, and then swap
1528 // its contents with src when we're done.
1529 class AutoTmpPath {
1530 public:
AutoTmpPath(const SkPath & src,SkPath ** dst)1531 AutoTmpPath(const SkPath& src, SkPath** dst) : fSrc(src) {
1532 if (&src == *dst) {
1533 *dst = &fTmpDst;
1534 fSwapWithSrc = true;
1535 } else {
1536 (*dst)->reset();
1537 fSwapWithSrc = false;
1538 }
1539 }
1540
~AutoTmpPath()1541 ~AutoTmpPath() {
1542 if (fSwapWithSrc) {
1543 fTmpDst.swap(*const_cast<SkPath*>(&fSrc));
1544 }
1545 }
1546
1547 private:
1548 SkPath fTmpDst;
1549 const SkPath& fSrc;
1550 bool fSwapWithSrc;
1551 };
1552
strokePath(const SkPath & src,SkPath * dst) const1553 void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
1554 SkASSERT(dst);
1555
1556 SkScalar radius = SkScalarHalf(fWidth);
1557
1558 AutoTmpPath tmp(src, &dst);
1559
1560 if (radius <= 0) {
1561 return;
1562 }
1563
1564 // If src is really a rect, call our specialty strokeRect() method
1565 {
1566 SkRect rect;
1567 bool isClosed;
1568 SkPath::Direction dir;
1569 if (src.isRect(&rect, &isClosed, &dir) && isClosed) {
1570 this->strokeRect(rect, dst, dir);
1571 // our answer should preserve the inverseness of the src
1572 if (src.isInverseFillType()) {
1573 SkASSERT(!dst->isInverseFillType());
1574 dst->toggleInverseFillType();
1575 }
1576 return;
1577 }
1578 }
1579
1580 SkAutoConicToQuads converter;
1581 #ifdef SK_LEGACY_STROKE_CURVES
1582 const SkScalar conicTol = SK_Scalar1 / 4 / fResScale;
1583 #endif
1584 SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(), this->getJoin(), fResScale);
1585 SkPath::Iter iter(src, false);
1586 SkPath::Verb lastSegment = SkPath::kMove_Verb;
1587
1588 for (;;) {
1589 SkPoint pts[4];
1590 switch (iter.next(pts, false)) {
1591 case SkPath::kMove_Verb:
1592 stroker.moveTo(pts[0]);
1593 break;
1594 case SkPath::kLine_Verb:
1595 stroker.lineTo(pts[1]);
1596 lastSegment = SkPath::kLine_Verb;
1597 break;
1598 case SkPath::kQuad_Verb:
1599 stroker.quadTo(pts[1], pts[2]);
1600 lastSegment = SkPath::kQuad_Verb;
1601 break;
1602 case SkPath::kConic_Verb: {
1603 #ifndef SK_LEGACY_STROKE_CURVES
1604 stroker.conicTo(pts[1], pts[2], iter.conicWeight());
1605 lastSegment = SkPath::kConic_Verb;
1606 break;
1607 #else
1608 // todo: if we had maxcurvature for conics, perhaps we should
1609 // natively extrude the conic instead of converting to quads.
1610 const SkPoint* quadPts =
1611 converter.computeQuads(pts, iter.conicWeight(), conicTol);
1612 for (int i = 0; i < converter.countQuads(); ++i) {
1613 stroker.quadTo(quadPts[1], quadPts[2]);
1614 quadPts += 2;
1615 }
1616 lastSegment = SkPath::kQuad_Verb;
1617 #endif
1618 } break;
1619 case SkPath::kCubic_Verb:
1620 stroker.cubicTo(pts[1], pts[2], pts[3]);
1621 lastSegment = SkPath::kCubic_Verb;
1622 break;
1623 case SkPath::kClose_Verb:
1624 stroker.close(lastSegment == SkPath::kLine_Verb);
1625 break;
1626 case SkPath::kDone_Verb:
1627 goto DONE;
1628 }
1629 }
1630 DONE:
1631 stroker.done(dst, lastSegment == SkPath::kLine_Verb);
1632
1633 if (fDoFill) {
1634 if (src.cheapIsDirection(SkPath::kCCW_Direction)) {
1635 dst->reverseAddPath(src);
1636 } else {
1637 dst->addPath(src);
1638 }
1639 } else {
1640 // Seems like we can assume that a 2-point src would always result in
1641 // a convex stroke, but testing has proved otherwise.
1642 // TODO: fix the stroker to make this assumption true (without making
1643 // it slower that the work that will be done in computeConvexity())
1644 #if 0
1645 // this test results in a non-convex stroke :(
1646 static void test(SkCanvas* canvas) {
1647 SkPoint pts[] = { 146.333328, 192.333328, 300.333344, 293.333344 };
1648 SkPaint paint;
1649 paint.setStrokeWidth(7);
1650 paint.setStrokeCap(SkPaint::kRound_Cap);
1651 canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
1652 }
1653 #endif
1654 #if 0
1655 if (2 == src.countPoints()) {
1656 dst->setIsConvex(true);
1657 }
1658 #endif
1659 }
1660
1661 // our answer should preserve the inverseness of the src
1662 if (src.isInverseFillType()) {
1663 SkASSERT(!dst->isInverseFillType());
1664 dst->toggleInverseFillType();
1665 }
1666 }
1667
reverse_direction(SkPath::Direction dir)1668 static SkPath::Direction reverse_direction(SkPath::Direction dir) {
1669 SkASSERT(SkPath::kUnknown_Direction != dir);
1670 return SkPath::kCW_Direction == dir ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1671 }
1672
addBevel(SkPath * path,const SkRect & r,const SkRect & outer,SkPath::Direction dir)1673 static void addBevel(SkPath* path, const SkRect& r, const SkRect& outer, SkPath::Direction dir) {
1674 SkPoint pts[8];
1675
1676 if (SkPath::kCW_Direction == dir) {
1677 pts[0].set(r.fLeft, outer.fTop);
1678 pts[1].set(r.fRight, outer.fTop);
1679 pts[2].set(outer.fRight, r.fTop);
1680 pts[3].set(outer.fRight, r.fBottom);
1681 pts[4].set(r.fRight, outer.fBottom);
1682 pts[5].set(r.fLeft, outer.fBottom);
1683 pts[6].set(outer.fLeft, r.fBottom);
1684 pts[7].set(outer.fLeft, r.fTop);
1685 } else {
1686 pts[7].set(r.fLeft, outer.fTop);
1687 pts[6].set(r.fRight, outer.fTop);
1688 pts[5].set(outer.fRight, r.fTop);
1689 pts[4].set(outer.fRight, r.fBottom);
1690 pts[3].set(r.fRight, outer.fBottom);
1691 pts[2].set(r.fLeft, outer.fBottom);
1692 pts[1].set(outer.fLeft, r.fBottom);
1693 pts[0].set(outer.fLeft, r.fTop);
1694 }
1695 path->addPoly(pts, 8, true);
1696 }
1697
strokeRect(const SkRect & origRect,SkPath * dst,SkPath::Direction dir) const1698 void SkStroke::strokeRect(const SkRect& origRect, SkPath* dst,
1699 SkPath::Direction dir) const {
1700 SkASSERT(dst != NULL);
1701 dst->reset();
1702
1703 SkScalar radius = SkScalarHalf(fWidth);
1704 if (radius <= 0) {
1705 return;
1706 }
1707
1708 SkScalar rw = origRect.width();
1709 SkScalar rh = origRect.height();
1710 if ((rw < 0) ^ (rh < 0)) {
1711 dir = reverse_direction(dir);
1712 }
1713 SkRect rect(origRect);
1714 rect.sort();
1715 // reassign these, now that we know they'll be >= 0
1716 rw = rect.width();
1717 rh = rect.height();
1718
1719 SkRect r(rect);
1720 r.outset(radius, radius);
1721
1722 SkPaint::Join join = (SkPaint::Join)fJoin;
1723 if (SkPaint::kMiter_Join == join && fMiterLimit < SK_ScalarSqrt2) {
1724 join = SkPaint::kBevel_Join;
1725 }
1726
1727 switch (join) {
1728 case SkPaint::kMiter_Join:
1729 dst->addRect(r, dir);
1730 break;
1731 case SkPaint::kBevel_Join:
1732 addBevel(dst, rect, r, dir);
1733 break;
1734 case SkPaint::kRound_Join:
1735 dst->addRoundRect(r, radius, radius, dir);
1736 break;
1737 default:
1738 break;
1739 }
1740
1741 if (fWidth < SkMinScalar(rw, rh) && !fDoFill) {
1742 r = rect;
1743 r.inset(radius, radius);
1744 dst->addRect(r, reverse_direction(dir));
1745 }
1746 }
1747