1 /*
2 * Copyright 2006 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 "include/core/SkPaint.h"
9
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkBlendMode.h"
12 #include "include/core/SkBlender.h"
13 #include "include/core/SkColorFilter.h"
14 #include "include/core/SkImageFilter.h"
15 #include "include/core/SkMaskFilter.h"
16 #include "include/core/SkPathEffect.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkScalar.h"
19 #include "include/core/SkShader.h"
20 #include "include/core/SkStrokeRec.h"
21 #include "include/private/base/SkTPin.h"
22 #include "include/private/base/SkTo.h"
23 #include "src/core/SkBlenderBase.h"
24 #include "src/core/SkColorFilterBase.h"
25 #include "src/core/SkColorSpacePriv.h"
26 #include "src/core/SkColorSpaceXformSteps.h"
27 #include "src/core/SkMaskFilterBase.h"
28 #include "src/core/SkPaintDefaults.h"
29 #include "src/core/SkPathEffectBase.h"
30
31 #include <utility>
32
33 // define this to get a printf for out-of-range parameter in setters
34 // e.g. setTextSize(-1)
35 //#define SK_REPORT_API_RANGE_CHECK
36
37
SkPaint()38 SkPaint::SkPaint()
39 : fColor4f{0, 0, 0, 1} // opaque black
40 , fWidth{0}
41 , fMiterLimit{SkPaintDefaults_MiterLimit}
42 , fBitfields{(unsigned)false, // fAntiAlias
43 (unsigned)false, // fDither
44 (unsigned)SkPaint::kDefault_Cap, // fCapType
45 (unsigned)SkPaint::kDefault_Join, // fJoinType
46 (unsigned)SkPaint::kFill_Style, // fStyle
47 0} // fPadding
48 {
49 static_assert(sizeof(fBitfields) == sizeof(fBitfieldsUInt), "");
50 }
51
SkPaint(const SkColor4f & color,SkColorSpace * colorSpace)52 SkPaint::SkPaint(const SkColor4f& color, SkColorSpace* colorSpace) : SkPaint() {
53 this->setColor(color, colorSpace);
54 }
55
56 SkPaint::SkPaint(const SkPaint& src) = default;
57
58 SkPaint::SkPaint(SkPaint&& src) = default;
59
60 SkPaint::~SkPaint() = default;
61
62 SkPaint& SkPaint::operator=(const SkPaint& src) = default;
63
64 SkPaint& SkPaint::operator=(SkPaint&& src) = default;
65
operator ==(const SkPaint & a,const SkPaint & b)66 bool operator==(const SkPaint& a, const SkPaint& b) {
67 #define EQUAL(field) (a.field == b.field)
68 return EQUAL(fPathEffect)
69 && EQUAL(fShader)
70 && EQUAL(fMaskFilter)
71 && EQUAL(fColorFilter)
72 && EQUAL(fBlender)
73 && EQUAL(fImageFilter)
74 && EQUAL(fColor4f)
75 && EQUAL(fWidth)
76 && EQUAL(fMiterLimit)
77 && EQUAL(fBitfieldsUInt)
78 ;
79 #undef EQUAL
80 }
81
82 #define DEFINE_FIELD_REF(type) \
83 sk_sp<Sk##type> SkPaint::ref##type() const { return f##type; }
84 DEFINE_FIELD_REF(ColorFilter)
DEFINE_FIELD_REF(Blender)85 DEFINE_FIELD_REF(Blender)
86 DEFINE_FIELD_REF(ImageFilter)
87 DEFINE_FIELD_REF(MaskFilter)
88 DEFINE_FIELD_REF(PathEffect)
89 DEFINE_FIELD_REF(Shader)
90 #undef DEFINE_FIELD_REF
91
92 #define DEFINE_FIELD_SET(Field) \
93 void SkPaint::set##Field(sk_sp<Sk##Field> f) { f##Field = std::move(f); }
94 DEFINE_FIELD_SET(ColorFilter)
95 DEFINE_FIELD_SET(ImageFilter)
96 DEFINE_FIELD_SET(MaskFilter)
97 DEFINE_FIELD_SET(PathEffect)
98 DEFINE_FIELD_SET(Shader)
99 #undef DEFINE_FIELD_SET
100
101 ///////////////////////////////////////////////////////////////////////////////
102
103 void SkPaint::reset() { *this = SkPaint(); }
104
setStyle(Style style)105 void SkPaint::setStyle(Style style) {
106 if ((unsigned)style < kStyleCount) {
107 fBitfields.fStyle = style;
108 } else {
109 #ifdef SK_REPORT_API_RANGE_CHECK
110 SkDebugf("SkPaint::setStyle(%d) out of range\n", style);
111 #endif
112 }
113 }
114
setStroke(bool isStroke)115 void SkPaint::setStroke(bool isStroke) {
116 fBitfields.fStyle = isStroke ? kStroke_Style : kFill_Style;
117 }
118
setColor(SkColor color)119 void SkPaint::setColor(SkColor color) {
120 fColor4f = SkColor4f::FromColor(color);
121 }
122
setColor(const SkColor4f & color,SkColorSpace * colorSpace)123 void SkPaint::setColor(const SkColor4f& color, SkColorSpace* colorSpace) {
124 SkColorSpaceXformSteps steps{colorSpace, kUnpremul_SkAlphaType,
125 sk_srgb_singleton(), kUnpremul_SkAlphaType};
126 fColor4f = {color.fR, color.fG, color.fB, SkTPin(color.fA, 0.0f, 1.0f)};
127 steps.apply(fColor4f.vec());
128 }
129
setAlphaf(float a)130 void SkPaint::setAlphaf(float a) {
131 fColor4f.fA = SkTPin(a, 0.0f, 1.0f);
132 }
133
setARGB(U8CPU a,U8CPU r,U8CPU g,U8CPU b)134 void SkPaint::setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
135 this->setColor(SkColorSetARGB(a, r, g, b));
136 }
137
asBlendMode() const138 std::optional<SkBlendMode> SkPaint::asBlendMode() const {
139 return fBlender ? as_BB(fBlender)->asBlendMode()
140 : SkBlendMode::kSrcOver;
141 }
142
getBlendMode_or(SkBlendMode defaultMode) const143 SkBlendMode SkPaint::getBlendMode_or(SkBlendMode defaultMode) const {
144 return this->asBlendMode().value_or(defaultMode);
145 }
146
isSrcOver() const147 bool SkPaint::isSrcOver() const {
148 return !fBlender || as_BB(fBlender)->asBlendMode() == SkBlendMode::kSrcOver;
149 }
150
setBlendMode(SkBlendMode mode)151 void SkPaint::setBlendMode(SkBlendMode mode) {
152 this->setBlender(mode == SkBlendMode::kSrcOver ? nullptr : SkBlender::Mode(mode));
153 }
154
setBlender(sk_sp<SkBlender> blender)155 void SkPaint::setBlender(sk_sp<SkBlender> blender) {
156 fBlender = std::move(blender);
157 }
158
setStrokeWidth(SkScalar width)159 void SkPaint::setStrokeWidth(SkScalar width) {
160 if (width >= 0) {
161 fWidth = width;
162 } else {
163 #ifdef SK_REPORT_API_RANGE_CHECK
164 SkDebugf("SkPaint::setStrokeWidth() called with negative value\n");
165 #endif
166 }
167 }
168
setStrokeMiter(SkScalar limit)169 void SkPaint::setStrokeMiter(SkScalar limit) {
170 if (limit >= 0) {
171 fMiterLimit = limit;
172 } else {
173 #ifdef SK_REPORT_API_RANGE_CHECK
174 SkDebugf("SkPaint::setStrokeMiter() called with negative value\n");
175 #endif
176 }
177 }
178
setStrokeCap(Cap ct)179 void SkPaint::setStrokeCap(Cap ct) {
180 if ((unsigned)ct < kCapCount) {
181 fBitfields.fCapType = SkToU8(ct);
182 } else {
183 #ifdef SK_REPORT_API_RANGE_CHECK
184 SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct);
185 #endif
186 }
187 }
188
setStrokeJoin(Join jt)189 void SkPaint::setStrokeJoin(Join jt) {
190 if ((unsigned)jt < kJoinCount) {
191 fBitfields.fJoinType = SkToU8(jt);
192 } else {
193 #ifdef SK_REPORT_API_RANGE_CHECK
194 SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt);
195 #endif
196 }
197 }
198
199 ///////////////////////////////////////////////////////////////////////////////
200
canComputeFastBounds() const201 bool SkPaint::canComputeFastBounds() const {
202 if (this->getImageFilter() && !this->getImageFilter()->canComputeFastBounds()) {
203 return false;
204 }
205 // Pass nullptr for the bounds to determine if they can be computed
206 if (this->getPathEffect() &&
207 !as_PEB(this->getPathEffect())->computeFastBounds(nullptr)) {
208 return false;
209 }
210 return true;
211 }
212
computeFastBounds(const SkRect & orig,SkRect * storage) const213 const SkRect& SkPaint::computeFastBounds(const SkRect& orig, SkRect* storage) const {
214 // Things like stroking, etc... will do math on the bounds rect, assuming that it's sorted.
215 SkASSERT(orig.isSorted());
216 SkPaint::Style style = this->getStyle();
217 // ultra fast-case: filling with no effects that affect geometry
218 if (kFill_Style == style) {
219 uintptr_t effects = 0;
220 effects |= reinterpret_cast<uintptr_t>(this->getMaskFilter());
221 effects |= reinterpret_cast<uintptr_t>(this->getPathEffect());
222 effects |= reinterpret_cast<uintptr_t>(this->getImageFilter());
223 if (!effects) {
224 return orig;
225 }
226 }
227
228 return this->doComputeFastBounds(orig, storage, style);
229 }
230
doComputeFastBounds(const SkRect & origSrc,SkRect * storage,Style style) const231 const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
232 SkRect* storage,
233 Style style) const {
234 SkASSERT(storage);
235
236 const SkRect* src = &origSrc;
237
238 SkRect tmpSrc;
239 if (this->getPathEffect()) {
240 tmpSrc = origSrc;
241 SkAssertResult(as_PEB(this->getPathEffect())->computeFastBounds(&tmpSrc));
242 src = &tmpSrc;
243 }
244
245 SkScalar radius = SkStrokeRec::GetInflationRadius(*this, style);
246 *storage = src->makeOutset(radius, radius);
247
248 if (this->getMaskFilter()) {
249 as_MFB(this->getMaskFilter())->computeFastBounds(*storage, storage);
250 }
251
252 if (this->getImageFilter()) {
253 *storage = this->getImageFilter()->computeFastBounds(*storage);
254 }
255
256 return *storage;
257 }
258
259 ///////////////////////////////////////////////////////////////////////////////
260
261 // return true if the filter exists, and may affect alpha
affects_alpha(const SkColorFilter * cf)262 static bool affects_alpha(const SkColorFilter* cf) {
263 return cf && !as_CFB(cf)->isAlphaUnchanged();
264 }
265
266 // return true if the filter exists, and may affect alpha
affects_alpha(const SkImageFilter * imf)267 static bool affects_alpha(const SkImageFilter* imf) {
268 // TODO: check if we should allow imagefilters to broadcast that they don't affect alpha
269 // ala colorfilters
270 return imf != nullptr;
271 }
272
nothingToDraw() const273 bool SkPaint::nothingToDraw() const {
274 auto bm = this->asBlendMode();
275 if (!bm) {
276 return false;
277 }
278 switch (bm.value()) {
279 case SkBlendMode::kSrcOver:
280 case SkBlendMode::kSrcATop:
281 case SkBlendMode::kDstOut:
282 case SkBlendMode::kDstOver:
283 case SkBlendMode::kPlus:
284 if (0 == this->getAlpha()) {
285 return !affects_alpha(fColorFilter.get()) && !affects_alpha(fImageFilter.get());
286 }
287 break;
288 case SkBlendMode::kDst:
289 return true;
290 default:
291 break;
292 }
293 return false;
294 }
295