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