SkStroke.cpp revision 2e0d727942c26339425bc5ba26189af070081213
1
2/*
3 * Copyright 2008 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkStrokerPriv.h"
11#include "SkGeometry.h"
12#include "SkPath.h"
13
14#define kMaxQuadSubdivide   5
15#define kMaxCubicSubdivide  4
16
17static inline bool degenerate_vector(const SkVector& v) {
18    return !SkPoint::CanNormalize(v.fX, v.fY);
19}
20
21static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) {
22    /*  root2/2 is a 45-degree angle
23        make this constant bigger for more subdivisions (but not >= 1)
24    */
25    static const SkScalar kFlatEnoughNormalDotProd =
26                                            SK_ScalarSqrt2/2 + SK_Scalar1/10;
27
28    SkASSERT(kFlatEnoughNormalDotProd > 0 &&
29             kFlatEnoughNormalDotProd < SK_Scalar1);
30
31    return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd;
32}
33
34static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) {
35    static const SkScalar kTooPinchyNormalDotProd = -SK_Scalar1 * 999 / 1000;
36
37    return SkPoint::DotProduct(norm0, norm1) <= kTooPinchyNormalDotProd;
38}
39
40static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after,
41                                  SkScalar radius,
42                                  SkVector* normal, SkVector* unitNormal) {
43    if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) {
44        return false;
45    }
46    unitNormal->rotateCCW();
47    unitNormal->scale(radius, normal);
48    return true;
49}
50
51static bool set_normal_unitnormal(const SkVector& vec,
52                                  SkScalar radius,
53                                  SkVector* normal, SkVector* unitNormal) {
54    if (!unitNormal->setNormalize(vec.fX, vec.fY)) {
55        return false;
56    }
57    unitNormal->rotateCCW();
58    unitNormal->scale(radius, normal);
59    return true;
60}
61
62///////////////////////////////////////////////////////////////////////////////
63
64class SkPathStroker {
65public:
66    SkPathStroker(const SkPath& src,
67                  SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap,
68                  SkPaint::Join join);
69
70    void moveTo(const SkPoint&);
71    void lineTo(const SkPoint&);
72    void quadTo(const SkPoint&, const SkPoint&);
73    void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&);
74    void close(bool isLine) { this->finishContour(true, isLine); }
75
76    void done(SkPath* dst, bool isLine) {
77        this->finishContour(false, isLine);
78        fOuter.addPath(fExtra);
79        dst->swap(fOuter);
80    }
81
82private:
83    SkScalar    fRadius;
84    SkScalar    fInvMiterLimit;
85
86    SkVector    fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal;
87    SkPoint     fFirstPt, fPrevPt;  // on original path
88    SkPoint     fFirstOuterPt;
89    int         fSegmentCount;
90    bool        fPrevIsLine;
91
92    SkStrokerPriv::CapProc  fCapper;
93    SkStrokerPriv::JoinProc fJoiner;
94
95    SkPath  fInner, fOuter; // outer is our working answer, inner is temp
96    SkPath  fExtra;         // added as extra complete contours
97
98    void    finishContour(bool close, bool isLine);
99    void    preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal,
100                      bool isLine);
101    void    postJoinTo(const SkPoint&, const SkVector& normal,
102                       const SkVector& unitNormal);
103
104    void    line_to(const SkPoint& currPt, const SkVector& normal);
105    void    quad_to(const SkPoint pts[3],
106                    const SkVector& normalAB, const SkVector& unitNormalAB,
107                    SkVector* normalBC, SkVector* unitNormalBC,
108                    int subDivide);
109    void    cubic_to(const SkPoint pts[4],
110                    const SkVector& normalAB, const SkVector& unitNormalAB,
111                    SkVector* normalCD, SkVector* unitNormalCD,
112                    int subDivide);
113};
114
115///////////////////////////////////////////////////////////////////////////////
116
117void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal,
118                              SkVector* unitNormal, bool currIsLine) {
119    SkASSERT(fSegmentCount >= 0);
120
121    SkScalar    prevX = fPrevPt.fX;
122    SkScalar    prevY = fPrevPt.fY;
123
124    SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal,
125                                         unitNormal));
126
127    if (fSegmentCount == 0) {
128        fFirstNormal = *normal;
129        fFirstUnitNormal = *unitNormal;
130        fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY);
131
132        fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY);
133        fInner.moveTo(prevX - normal->fX, prevY - normal->fY);
134    } else {    // we have a previous segment
135        fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal,
136                fRadius, fInvMiterLimit, fPrevIsLine, currIsLine);
137    }
138    fPrevIsLine = currIsLine;
139}
140
141void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal,
142                               const SkVector& unitNormal) {
143    fPrevPt = currPt;
144    fPrevUnitNormal = unitNormal;
145    fPrevNormal = normal;
146    fSegmentCount += 1;
147}
148
149void SkPathStroker::finishContour(bool close, bool currIsLine) {
150    if (fSegmentCount > 0) {
151        SkPoint pt;
152
153        if (close) {
154            fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt,
155                    fFirstUnitNormal, fRadius, fInvMiterLimit,
156                    fPrevIsLine, currIsLine);
157            fOuter.close();
158            // now add fInner as its own contour
159            fInner.getLastPt(&pt);
160            fOuter.moveTo(pt.fX, pt.fY);
161            fOuter.reversePathTo(fInner);
162            fOuter.close();
163        } else {    // add caps to start and end
164            // cap the end
165            fInner.getLastPt(&pt);
166            fCapper(&fOuter, fPrevPt, fPrevNormal, pt,
167                    currIsLine ? &fInner : NULL);
168            fOuter.reversePathTo(fInner);
169            // cap the start
170            fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt,
171                    fPrevIsLine ? &fInner : NULL);
172            fOuter.close();
173        }
174    }
175    // since we may re-use fInner, we rewind instead of reset, to save on
176    // reallocating its internal storage.
177    fInner.rewind();
178    fSegmentCount = -1;
179}
180
181///////////////////////////////////////////////////////////////////////////////
182
183SkPathStroker::SkPathStroker(const SkPath& src,
184                             SkScalar radius, SkScalar miterLimit,
185                             SkPaint::Cap cap, SkPaint::Join join)
186        : fRadius(radius) {
187
188    /*  This is only used when join is miter_join, but we initialize it here
189        so that it is always defined, to fis valgrind warnings.
190    */
191    fInvMiterLimit = 0;
192
193    if (join == SkPaint::kMiter_Join) {
194        if (miterLimit <= SK_Scalar1) {
195            join = SkPaint::kBevel_Join;
196        } else {
197            fInvMiterLimit = SkScalarInvert(miterLimit);
198        }
199    }
200    fCapper = SkStrokerPriv::CapFactory(cap);
201    fJoiner = SkStrokerPriv::JoinFactory(join);
202    fSegmentCount = -1;
203    fPrevIsLine = false;
204
205    // Need some estimate of how large our final result (fOuter)
206    // and our per-contour temp (fInner) will be, so we don't spend
207    // extra time repeatedly growing these arrays.
208    //
209    // 3x for result == inner + outer + join (swag)
210    // 1x for inner == 'wag' (worst contour length would be better guess)
211    fOuter.incReserve(src.countPoints() * 3);
212    fInner.incReserve(src.countPoints());
213}
214
215void SkPathStroker::moveTo(const SkPoint& pt) {
216    if (fSegmentCount > 0) {
217        this->finishContour(false, false);
218    }
219    fSegmentCount = 0;
220    fFirstPt = fPrevPt = pt;
221}
222
223void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) {
224    fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY);
225    fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY);
226}
227
228void SkPathStroker::lineTo(const SkPoint& currPt) {
229    if (SkPath::IsLineDegenerate(fPrevPt, currPt)) {
230        return;
231    }
232    SkVector    normal, unitNormal;
233
234    this->preJoinTo(currPt, &normal, &unitNormal, true);
235    this->line_to(currPt, normal);
236    this->postJoinTo(currPt, normal, unitNormal);
237}
238
239void SkPathStroker::quad_to(const SkPoint pts[3],
240                      const SkVector& normalAB, const SkVector& unitNormalAB,
241                      SkVector* normalBC, SkVector* unitNormalBC,
242                      int subDivide) {
243    if (!set_normal_unitnormal(pts[1], pts[2], fRadius,
244                               normalBC, unitNormalBC)) {
245        // pts[1] nearly equals pts[2], so just draw a line to pts[2]
246        this->line_to(pts[2], normalAB);
247        *normalBC = normalAB;
248        *unitNormalBC = unitNormalAB;
249        return;
250    }
251
252    if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) {
253        SkPoint     tmp[5];
254        SkVector    norm, unit;
255
256        SkChopQuadAtHalf(pts, tmp);
257        this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide);
258        this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide);
259    } else {
260        SkVector    normalB, unitB;
261        SkAssertResult(set_normal_unitnormal(pts[0], pts[2], fRadius,
262                                             &normalB, &unitB));
263
264        fOuter.quadTo(  pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
265                        pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY);
266        fInner.quadTo(  pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
267                        pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY);
268    }
269}
270
271void SkPathStroker::cubic_to(const SkPoint pts[4],
272                      const SkVector& normalAB, const SkVector& unitNormalAB,
273                      SkVector* normalCD, SkVector* unitNormalCD,
274                      int subDivide) {
275    SkVector    ab = pts[1] - pts[0];
276    SkVector    cd = pts[3] - pts[2];
277    SkVector    normalBC, unitNormalBC;
278
279    bool    degenerateAB = degenerate_vector(ab);
280    bool    degenerateCD = degenerate_vector(cd);
281
282    if (degenerateAB && degenerateCD) {
283DRAW_LINE:
284        this->line_to(pts[3], normalAB);
285        *normalCD = normalAB;
286        *unitNormalCD = unitNormalAB;
287        return;
288    }
289
290    if (degenerateAB) {
291        ab = pts[2] - pts[0];
292        degenerateAB = degenerate_vector(ab);
293    }
294    if (degenerateCD) {
295        cd = pts[3] - pts[1];
296        degenerateCD = degenerate_vector(cd);
297    }
298    if (degenerateAB || degenerateCD) {
299        goto DRAW_LINE;
300    }
301    SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD));
302    bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius,
303                                               &normalBC, &unitNormalBC);
304
305    if (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) ||
306             normals_too_curvy(unitNormalBC, *unitNormalCD)) {
307        // subdivide if we can
308        if (--subDivide < 0) {
309            goto DRAW_LINE;
310        }
311        SkPoint     tmp[7];
312        SkVector    norm, unit, dummy, unitDummy;
313
314        SkChopCubicAtHalf(pts, tmp);
315        this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit,
316                       subDivide);
317        // we use dummys since we already have a valid (and more accurate)
318        // normals for CD
319        this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide);
320    } else {
321        SkVector    normalB, normalC;
322
323        // need normals to inset/outset the off-curve pts B and C
324
325        if (0) {    // this is normal to the line between our adjacent pts
326            normalB = pts[2] - pts[0];
327            normalB.rotateCCW();
328            SkAssertResult(normalB.setLength(fRadius));
329
330            normalC = pts[3] - pts[1];
331            normalC.rotateCCW();
332            SkAssertResult(normalC.setLength(fRadius));
333        } else {    // miter-join
334            SkVector    unitBC = pts[2] - pts[1];
335            unitBC.normalize();
336            unitBC.rotateCCW();
337
338            normalB = unitNormalAB + unitBC;
339            normalC = *unitNormalCD + unitBC;
340
341            SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC);
342            SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
343                                        SkScalarSqrt((SK_Scalar1 + dot)/2))));
344            dot = SkPoint::DotProduct(*unitNormalCD, unitBC);
345            SkAssertResult(normalC.setLength(SkScalarDiv(fRadius,
346                                        SkScalarSqrt((SK_Scalar1 + dot)/2))));
347        }
348
349        fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
350                        pts[2].fX + normalC.fX, pts[2].fY + normalC.fY,
351                        pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY);
352
353        fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
354                        pts[2].fX - normalC.fX, pts[2].fY - normalC.fY,
355                        pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY);
356    }
357}
358
359void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
360    bool    degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
361    bool    degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
362
363    if (degenerateAB | degenerateBC) {
364        if (degenerateAB ^ degenerateBC) {
365            this->lineTo(pt2);
366        }
367        return;
368    }
369
370    SkVector    normalAB, unitAB, normalBC, unitBC;
371
372    this->preJoinTo(pt1, &normalAB, &unitAB, false);
373
374    {
375        SkPoint pts[3], tmp[5];
376        pts[0] = fPrevPt;
377        pts[1] = pt1;
378        pts[2] = pt2;
379
380        if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) {
381            unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY);
382            unitBC.rotateCCW();
383            if (normals_too_pinchy(unitAB, unitBC)) {
384                normalBC = unitBC;
385                normalBC.scale(fRadius);
386
387                fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY);
388                fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY);
389                fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY);
390
391                fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY);
392                fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY);
393                fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY);
394
395                fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius,
396                                 SkPath::kCW_Direction);
397            } else {
398                this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC,
399                              kMaxQuadSubdivide);
400                SkVector n = normalBC;
401                SkVector u = unitBC;
402                this->quad_to(&tmp[2], n, u, &normalBC, &unitBC,
403                              kMaxQuadSubdivide);
404            }
405        } else {
406            this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC,
407                          kMaxQuadSubdivide);
408        }
409    }
410
411    this->postJoinTo(pt2, normalBC, unitBC);
412}
413
414void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2,
415                            const SkPoint& pt3) {
416    bool    degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
417    bool    degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
418    bool    degenerateCD = SkPath::IsLineDegenerate(pt2, pt3);
419
420    if (degenerateAB + degenerateBC + degenerateCD >= 2) {
421        this->lineTo(pt3);
422        return;
423    }
424
425    SkVector    normalAB, unitAB, normalCD, unitCD;
426
427    // find the first tangent (which might be pt1 or pt2
428    {
429        const SkPoint*  nextPt = &pt1;
430        if (degenerateAB)
431            nextPt = &pt2;
432        this->preJoinTo(*nextPt, &normalAB, &unitAB, false);
433    }
434
435    {
436        SkPoint pts[4], tmp[13];
437        int         i, count;
438        SkVector    n, u;
439        SkScalar    tValues[3];
440
441        pts[0] = fPrevPt;
442        pts[1] = pt1;
443        pts[2] = pt2;
444        pts[3] = pt3;
445
446#if 1
447        count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
448#else
449        count = 1;
450        memcpy(tmp, pts, 4 * sizeof(SkPoint));
451#endif
452        n = normalAB;
453        u = unitAB;
454        for (i = 0; i < count; i++) {
455            this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD,
456                           kMaxCubicSubdivide);
457            if (i == count - 1) {
458                break;
459            }
460            n = normalCD;
461            u = unitCD;
462
463        }
464
465        // check for too pinchy
466        for (i = 1; i < count; i++) {
467            SkPoint p;
468            SkVector    v, c;
469
470            SkEvalCubicAt(pts, tValues[i - 1], &p, &v, &c);
471
472            SkScalar    dot = SkPoint::DotProduct(c, c);
473            v.scale(SkScalarInvert(dot));
474
475            if (SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY)) {
476                fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction);
477            }
478        }
479
480    }
481
482    this->postJoinTo(pt3, normalCD, unitCD);
483}
484
485///////////////////////////////////////////////////////////////////////////////
486///////////////////////////////////////////////////////////////////////////////
487
488#include "SkPaintDefaults.h"
489
490SkStroke::SkStroke() {
491    fWidth      = SK_Scalar1;
492    fMiterLimit = SkPaintDefaults_MiterLimit;
493    fCap        = SkPaint::kDefault_Cap;
494    fJoin       = SkPaint::kDefault_Join;
495    fDoFill     = false;
496}
497
498SkStroke::SkStroke(const SkPaint& p) {
499    fWidth      = p.getStrokeWidth();
500    fMiterLimit = p.getStrokeMiter();
501    fCap        = (uint8_t)p.getStrokeCap();
502    fJoin       = (uint8_t)p.getStrokeJoin();
503    fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
504}
505
506SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
507    fWidth      = width;
508    fMiterLimit = p.getStrokeMiter();
509    fCap        = (uint8_t)p.getStrokeCap();
510    fJoin       = (uint8_t)p.getStrokeJoin();
511    fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
512}
513
514void SkStroke::setWidth(SkScalar width) {
515    SkASSERT(width >= 0);
516    fWidth = width;
517}
518
519void SkStroke::setMiterLimit(SkScalar miterLimit) {
520    SkASSERT(miterLimit >= 0);
521    fMiterLimit = miterLimit;
522}
523
524void SkStroke::setCap(SkPaint::Cap cap) {
525    SkASSERT((unsigned)cap < SkPaint::kCapCount);
526    fCap = SkToU8(cap);
527}
528
529void SkStroke::setJoin(SkPaint::Join join) {
530    SkASSERT((unsigned)join < SkPaint::kJoinCount);
531    fJoin = SkToU8(join);
532}
533
534///////////////////////////////////////////////////////////////////////////////
535
536#ifdef SK_SCALAR_IS_FIXED
537    /*  return non-zero if the path is too big, and should be shrunk to avoid
538        overflows during intermediate calculations. Note that we compute the
539        bounds for this. If we had a custom callback/walker for paths, we could
540        perhaps go faster by using that, and just perform the abs | in that
541        routine
542    */
543    static int needs_to_shrink(const SkPath& path) {
544        const SkRect& r = path.getBounds();
545        SkFixed mask = SkAbs32(r.fLeft);
546        mask |= SkAbs32(r.fTop);
547        mask |= SkAbs32(r.fRight);
548        mask |= SkAbs32(r.fBottom);
549        // we need the top 3 bits clear (after abs) to avoid overflow
550        return mask >> 29;
551    }
552
553    static void identity_proc(SkPoint pts[], int count) {}
554    static void shift_down_2_proc(SkPoint pts[], int count) {
555        for (int i = 0; i < count; i++) {
556            pts->fX >>= 2;
557            pts->fY >>= 2;
558            pts += 1;
559        }
560    }
561    #define APPLY_PROC(proc, pts, count)    proc(pts, count)
562#else   // float does need any of this
563    #define APPLY_PROC(proc, pts, count)
564#endif
565
566void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
567    SkASSERT(&src != NULL && dst != NULL);
568
569    SkScalar radius = SkScalarHalf(fWidth);
570
571    dst->reset();
572    if (radius <= 0) {
573        return;
574    }
575
576#ifdef SK_SCALAR_IS_FIXED
577    void (*proc)(SkPoint pts[], int count) = identity_proc;
578    if (needs_to_shrink(src)) {
579        proc = shift_down_2_proc;
580        radius >>= 2;
581        if (radius == 0) {
582            return;
583        }
584    }
585#endif
586
587    SkPathStroker   stroker(src, radius, fMiterLimit, this->getCap(),
588                            this->getJoin());
589
590    SkPath::Iter    iter(src, false);
591    SkPoint         pts[4];
592    SkPath::Verb    verb, lastSegment = SkPath::kMove_Verb;
593
594    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
595        switch (verb) {
596            case SkPath::kMove_Verb:
597                APPLY_PROC(proc, &pts[0], 1);
598                stroker.moveTo(pts[0]);
599                break;
600            case SkPath::kLine_Verb:
601                APPLY_PROC(proc, &pts[1], 1);
602                stroker.lineTo(pts[1]);
603                lastSegment = verb;
604                break;
605            case SkPath::kQuad_Verb:
606                APPLY_PROC(proc, &pts[1], 2);
607                stroker.quadTo(pts[1], pts[2]);
608                lastSegment = verb;
609                break;
610            case SkPath::kCubic_Verb:
611                APPLY_PROC(proc, &pts[1], 3);
612                stroker.cubicTo(pts[1], pts[2], pts[3]);
613                lastSegment = verb;
614                break;
615            case SkPath::kClose_Verb:
616                stroker.close(lastSegment == SkPath::kLine_Verb);
617                break;
618            default:
619                break;
620        }
621    }
622    stroker.done(dst, lastSegment == SkPath::kLine_Verb);
623
624#ifdef SK_SCALAR_IS_FIXED
625    // undo our previous down_shift
626    if (shift_down_2_proc == proc) {
627        // need a real shift methid on path. antialias paths could use this too
628        SkMatrix matrix;
629        matrix.setScale(SkIntToScalar(4), SkIntToScalar(4));
630        dst->transform(matrix);
631    }
632#endif
633
634    if (fDoFill) {
635        if (src.cheapIsDirection(SkPath::kCCW_Direction)) {
636            dst->reverseAddPath(src);
637        } else {
638            dst->addPath(src);
639        }
640    } else {
641        //  Seems like we can assume that a 2-point src would always result in
642        //  a convex stroke, but testing has proved otherwise.
643        //  TODO: fix the stroker to make this assumption true (without making
644        //  it slower that the work that will be done in computeConvexity())
645#if 0
646        // this test results in a non-convex stroke :(
647        static void test(SkCanvas* canvas) {
648            SkPoint pts[] = { 146.333328,  192.333328, 300.333344, 293.333344 };
649            SkPaint paint;
650            paint.setStrokeWidth(7);
651            paint.setStrokeCap(SkPaint::kRound_Cap);
652            canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
653        }
654#endif
655#if 0
656        if (2 == src.countPoints()) {
657            dst->setIsConvex(true);
658        }
659#endif
660    }
661
662    // our answer should preserve the inverseness of the src
663    if (src.isInverseFillType()) {
664        SkASSERT(!dst->isInverseFillType());
665        dst->toggleInverseFillType();
666    }
667}
668
669