1 /*
2  * Copyright 2012 Google Inc.
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 "SkStrokeRec.h"
9 #include "SkPaintDefaults.h"
10 
11 // must be < 0, since ==0 means hairline, and >0 means normal stroke
12 #define kStrokeRec_FillStyleWidth     (-SK_Scalar1)
13 
SkStrokeRec(InitStyle s)14 SkStrokeRec::SkStrokeRec(InitStyle s) {
15     fResScale       = 1;
16     fWidth          = (kFill_InitStyle == s) ? kStrokeRec_FillStyleWidth : 0;
17     fMiterLimit     = SkPaintDefaults_MiterLimit;
18     fCap            = SkPaint::kDefault_Cap;
19     fJoin           = SkPaint::kDefault_Join;
20     fStrokeAndFill  = false;
21 }
22 
SkStrokeRec(const SkPaint & paint,SkScalar resScale)23 SkStrokeRec::SkStrokeRec(const SkPaint& paint, SkScalar resScale) {
24     this->init(paint, paint.getStyle(), resScale);
25 }
26 
SkStrokeRec(const SkPaint & paint,SkPaint::Style styleOverride,SkScalar resScale)27 SkStrokeRec::SkStrokeRec(const SkPaint& paint, SkPaint::Style styleOverride, SkScalar resScale) {
28     this->init(paint, styleOverride, resScale);
29 }
30 
init(const SkPaint & paint,SkPaint::Style style,SkScalar resScale)31 void SkStrokeRec::init(const SkPaint& paint, SkPaint::Style style, SkScalar resScale) {
32     fResScale = resScale;
33 
34     switch (style) {
35         case SkPaint::kFill_Style:
36             fWidth = kStrokeRec_FillStyleWidth;
37             fStrokeAndFill = false;
38             break;
39         case SkPaint::kStroke_Style:
40             fWidth = paint.getStrokeWidth();
41             fStrokeAndFill = false;
42             break;
43         case SkPaint::kStrokeAndFill_Style:
44             if (0 == paint.getStrokeWidth()) {
45                 // hairline+fill == fill
46                 fWidth = kStrokeRec_FillStyleWidth;
47                 fStrokeAndFill = false;
48             } else {
49                 fWidth = paint.getStrokeWidth();
50                 fStrokeAndFill = true;
51             }
52             break;
53         default:
54             SkDEBUGFAIL("unknown paint style");
55             // fall back on just fill
56             fWidth = kStrokeRec_FillStyleWidth;
57             fStrokeAndFill = false;
58             break;
59     }
60 
61     // copy these from the paint, regardless of our "style"
62     fMiterLimit = paint.getStrokeMiter();
63     fCap        = paint.getStrokeCap();
64     fJoin       = paint.getStrokeJoin();
65 }
66 
getStyle() const67 SkStrokeRec::Style SkStrokeRec::getStyle() const {
68     if (fWidth < 0) {
69         return kFill_Style;
70     } else if (0 == fWidth) {
71         return kHairline_Style;
72     } else {
73         return fStrokeAndFill ? kStrokeAndFill_Style : kStroke_Style;
74     }
75 }
76 
setFillStyle()77 void SkStrokeRec::setFillStyle() {
78     fWidth = kStrokeRec_FillStyleWidth;
79     fStrokeAndFill = false;
80 }
81 
setHairlineStyle()82 void SkStrokeRec::setHairlineStyle() {
83     fWidth = 0;
84     fStrokeAndFill = false;
85 }
86 
setStrokeStyle(SkScalar width,bool strokeAndFill)87 void SkStrokeRec::setStrokeStyle(SkScalar width, bool strokeAndFill) {
88     if (strokeAndFill && (0 == width)) {
89         // hairline+fill == fill
90         this->setFillStyle();
91     } else {
92         fWidth = width;
93         fStrokeAndFill = strokeAndFill;
94     }
95 }
96 
97 #include "SkStroke.h"
98 
99 #ifdef SK_DEBUG
100     // enables tweaking these values at runtime from Viewer
101     bool gDebugStrokerErrorSet = false;
102     SkScalar gDebugStrokerError;
103 #endif
104 
applyToPath(SkPath * dst,const SkPath & src) const105 bool SkStrokeRec::applyToPath(SkPath* dst, const SkPath& src) const {
106     if (fWidth <= 0) {  // hairline or fill
107         return false;
108     }
109 
110     SkStroke stroker;
111     stroker.setCap((SkPaint::Cap)fCap);
112     stroker.setJoin((SkPaint::Join)fJoin);
113     stroker.setMiterLimit(fMiterLimit);
114     stroker.setWidth(fWidth);
115     stroker.setDoFill(fStrokeAndFill);
116 #ifdef SK_DEBUG
117     stroker.setResScale(gDebugStrokerErrorSet ? gDebugStrokerError : fResScale);
118 #else
119     stroker.setResScale(fResScale);
120 #endif
121     stroker.strokePath(src, dst);
122     return true;
123 }
124 
applyToPaint(SkPaint * paint) const125 void SkStrokeRec::applyToPaint(SkPaint* paint) const {
126     if (fWidth < 0) {  // fill
127         paint->setStyle(SkPaint::kFill_Style);
128         return;
129     }
130 
131     paint->setStyle(fStrokeAndFill ? SkPaint::kStrokeAndFill_Style : SkPaint::kStroke_Style);
132     paint->setStrokeWidth(fWidth);
133     paint->setStrokeMiter(fMiterLimit);
134     paint->setStrokeCap((SkPaint::Cap)fCap);
135     paint->setStrokeJoin((SkPaint::Join)fJoin);
136 }
137 
getInflationRadius() const138 SkScalar SkStrokeRec::getInflationRadius() const {
139     return GetInflationRadius((SkPaint::Join)fJoin, fMiterLimit, (SkPaint::Cap)fCap, fWidth);
140 }
141 
GetInflationRadius(const SkPaint & paint,SkPaint::Style style)142 SkScalar SkStrokeRec::GetInflationRadius(const SkPaint& paint, SkPaint::Style style) {
143     SkScalar width = SkPaint::kFill_Style == style ? -SK_Scalar1 : paint.getStrokeWidth();
144     return GetInflationRadius(paint.getStrokeJoin(), paint.getStrokeMiter(), paint.getStrokeCap(),
145                               width);
146 
147 }
148 
GetInflationRadius(SkPaint::Join join,SkScalar miterLimit,SkPaint::Cap cap,SkScalar strokeWidth)149 SkScalar SkStrokeRec::GetInflationRadius(SkPaint::Join join, SkScalar miterLimit, SkPaint::Cap cap,
150                                          SkScalar strokeWidth) {
151     if (strokeWidth < 0) {  // fill
152         return 0;
153     } else if (0 == strokeWidth) {
154         // FIXME: We need a "matrixScale" parameter here in order to properly handle hairlines.
155         // Their with is determined in device space, unlike other strokes.
156         // http://skbug.com/8157
157         return SK_Scalar1;
158     }
159 
160     // since we're stroked, outset the rect by the radius (and join type, caps)
161     SkScalar multiplier = SK_Scalar1;
162     if (SkPaint::kMiter_Join == join) {
163         multiplier = SkTMax(multiplier, miterLimit);
164     }
165     if (SkPaint::kSquare_Cap == cap) {
166         multiplier = SkTMax(multiplier, SK_ScalarSqrt2);
167     }
168     return strokeWidth/2 * multiplier;
169 }
170 
171