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