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