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