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/SkColorFilter.h"
9#include "include/core/SkRefCnt.h"
10#include "include/core/SkString.h"
11#include "include/core/SkUnPreMultiply.h"
12#include "include/private/SkNx.h"
13#include "include/private/SkTDArray.h"
14#include "src/core/SkArenaAlloc.h"
15#include "src/core/SkColorSpacePriv.h"
16#include "src/core/SkColorSpaceXformSteps.h"
17#include "src/core/SkRasterPipeline.h"
18#include "src/core/SkReadBuffer.h"
19#include "src/core/SkVM.h"
20#include "src/core/SkWriteBuffer.h"
21
22#if SK_SUPPORT_GPU
23#include "src/gpu/GrColorSpaceXform.h"
24#include "src/gpu/GrFragmentProcessor.h"
25#include "src/gpu/effects/generated/GrMixerEffect.h"
26#endif
27
28bool SkColorFilter::onAsAColorMode(SkColor*, SkBlendMode*) const {
29 return false;
30}
31
32bool SkColorFilter::onAsAColorMatrix(float matrix[20]) const {
33 return false;
34}
35
36#if SK_SUPPORT_GPU
37std::unique_ptr<GrFragmentProcessor> SkColorFilter::asFragmentProcessor(GrRecordingContext*,
38 const GrColorInfo&) const {
39 return nullptr;
40}
41#endif
42
43bool SkColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
44 return this->onAppendStages(rec, shaderIsOpaque);
45}
46
47skvm::Color SkColorFilter::program(skvm::Builder* p, skvm::Color c,
48 SkColorSpace* dstCS,
49 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
50 skvm::F32 original = c.a;
51 if ((c = this->onProgram(p,c, dstCS, uniforms,alloc))) {
52 if (this->getFlags() & kAlphaUnchanged_Flag) {
53 c.a = original;
54 }
55 return c;
56 }
57 //SkDebugf("cannot onProgram %s\n", this->getTypeName());
58 return {};
59}
60
61SkColor SkColorFilter::filterColor(SkColor c) const {
62 // This is mostly meaningless. We should phase-out this call entirely.
63 SkColorSpace* cs = nullptr;
64 return this->filterColor4f(SkColor4f::FromColor(c), cs, cs).toSkColor();
65}
66
67SkColor4f SkColorFilter::filterColor4f(const SkColor4f& origSrcColor, SkColorSpace* srcCS,
68 SkColorSpace* dstCS) const {
69#ifdef SK_SUPPORT_LEGACY_COLORFILTER_NO_SHADER
70 SkPMColor4f src = origSrcColor.premul();
71 SkColor4f color = *(SkColor4f*)&src;
72#else
73 SkColor4f color = origSrcColor;
74 SkColorSpaceXformSteps(srcCS, kUnpremul_SkAlphaType,
75 dstCS, kPremul_SkAlphaType).apply(color.vec());
76#endif
77
78 constexpr size_t kEnoughForCommonFilters = 512; // big enough for compose+colormatrix
79 SkSTArenaAlloc<kEnoughForCommonFilters> alloc;
80 SkRasterPipeline pipeline(&alloc);
81 pipeline.append_constant_color(&alloc, color.vec());
82 SkPaint dummyPaint;
83 SkStageRec rec = {
84 &pipeline, &alloc, kRGBA_F32_SkColorType, dstCS, dummyPaint, nullptr, SkMatrix::I()
85 };
86 this->onAppendStages(rec, color.fA == 1);
87
88 SkPMColor4f dst;
89 SkRasterPipeline_MemoryCtx dstPtr = { &dst, 0 };
90 pipeline.append(SkRasterPipeline::store_f32, &dstPtr);
91 pipeline.run(0,0, 1,1);
92 return dst.unpremul();
93}
94
95///////////////////////////////////////////////////////////////////////////////////////////////////
96
97class SkComposeColorFilter : public SkColorFilter {
98public:
99 uint32_t getFlags() const override {
100 // Can only claim alphaunchanged support if both our proxys do.
101 return fOuter->getFlags() & fInner->getFlags();
102 }
103
104 bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
105 bool innerIsOpaque = shaderIsOpaque;
106 if (!(fInner->getFlags() & kAlphaUnchanged_Flag)) {
107 innerIsOpaque = false;
108 }
109 return fInner->appendStages(rec, shaderIsOpaque) &&
110 fOuter->appendStages(rec, innerIsOpaque);
111 }
112
113 skvm::Color onProgram(skvm::Builder* p, skvm::Color c,
114 SkColorSpace* dstCS,
115 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
116 c = fInner->program(p, c, dstCS, uniforms, alloc);
117 return c ? fOuter->program(p, c, dstCS, uniforms, alloc) : skvm::Color{};
118 }
119
120#if SK_SUPPORT_GPU
121 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
122 GrRecordingContext* context, const GrColorInfo& dstColorInfo) const override {
123 auto innerFP = fInner->asFragmentProcessor(context, dstColorInfo);
124 auto outerFP = fOuter->asFragmentProcessor(context, dstColorInfo);
125 if (!innerFP || !outerFP) {
126 return nullptr;
127 }
128 std::unique_ptr<GrFragmentProcessor> series[] = { std::move(innerFP), std::move(outerFP) };
129 return GrFragmentProcessor::RunInSeries(series, 2);
130 }
131#endif
132
133protected:
134 void flatten(SkWriteBuffer& buffer) const override {
135 buffer.writeFlattenable(fOuter.get());
136 buffer.writeFlattenable(fInner.get());
137 }
138
139private:
140 SK_FLATTENABLE_HOOKS(SkComposeColorFilter)
141
142 SkComposeColorFilter(sk_sp<SkColorFilter> outer, sk_sp<SkColorFilter> inner)
143 : fOuter(std::move(outer))
144 , fInner(std::move(inner)) {}
145
146 sk_sp<SkColorFilter> fOuter;
147 sk_sp<SkColorFilter> fInner;
148
149 friend class SkColorFilter;
150
151 typedef SkColorFilter INHERITED;
152};
153
154sk_sp<SkFlattenable> SkComposeColorFilter::CreateProc(SkReadBuffer& buffer) {
155 sk_sp<SkColorFilter> outer(buffer.readColorFilter());
156 sk_sp<SkColorFilter> inner(buffer.readColorFilter());
157 return outer ? outer->makeComposed(std::move(inner)) : inner;
158}
159
160sk_sp<SkColorFilter> SkColorFilter::makeComposed(sk_sp<SkColorFilter> inner) const {
161 if (!inner) {
162 return sk_ref_sp(this);
163 }
164
165 return sk_sp<SkColorFilter>(new SkComposeColorFilter(sk_ref_sp(this), std::move(inner)));
166}
167
168///////////////////////////////////////////////////////////////////////////////////////////////////
169
170class SkSRGBGammaColorFilter : public SkColorFilter {
171public:
172 enum class Direction {
173 kLinearToSRGB,
174 kSRGBToLinear,
175 };
176 SkSRGBGammaColorFilter(Direction dir) : fDir(dir), fSteps([&]{
177 // We handle premul/unpremul separately, so here just always upm->upm.
178 if (dir == Direction::kLinearToSRGB) {
179 return SkColorSpaceXformSteps{sk_srgb_linear_singleton(), kUnpremul_SkAlphaType,
180 sk_srgb_singleton(), kUnpremul_SkAlphaType};
181 } else {
182 return SkColorSpaceXformSteps{sk_srgb_singleton(), kUnpremul_SkAlphaType,
183 sk_srgb_linear_singleton(), kUnpremul_SkAlphaType};
184 }
185 }()) {}
186
187#if SK_SUPPORT_GPU
188 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(GrRecordingContext*,
189 const GrColorInfo&) const override {
190 // wish our caller would let us know if our input was opaque...
191 SkAlphaType at = kPremul_SkAlphaType;
192 switch (fDir) {
193 case Direction::kLinearToSRGB:
194 return GrColorSpaceXformEffect::Make(sk_srgb_linear_singleton(), at,
195 sk_srgb_singleton(), at);
196 case Direction::kSRGBToLinear:
197 return GrColorSpaceXformEffect::Make(sk_srgb_singleton(), at,
198 sk_srgb_linear_singleton(), at);
199 }
200 return nullptr;
201 }
202#endif
203
204 bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
205 if (!shaderIsOpaque) {
206 rec.fPipeline->append(SkRasterPipeline::unpremul);
207 }
208
209 // TODO: is it valuable to thread this through appendStages()?
210 bool shaderIsNormalized = false;
211 fSteps.apply(rec.fPipeline, shaderIsNormalized);
212
213 if (!shaderIsOpaque) {
214 rec.fPipeline->append(SkRasterPipeline::premul);
215 }
216 return true;
217 }
218
219 skvm::Color onProgram(skvm::Builder* p, skvm::Color c, SkColorSpace* dstCS,
220 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
221 return premul(fSteps.program(p, uniforms, unpremul(c)));
222 }
223
224protected:
225 void flatten(SkWriteBuffer& buffer) const override {
226 buffer.write32(static_cast<uint32_t>(fDir));
227 }
228
229private:
230 SK_FLATTENABLE_HOOKS(SkSRGBGammaColorFilter)
231
232 const Direction fDir;
233 SkColorSpaceXformSteps fSteps;
234
235 friend class SkColorFilter;
236 typedef SkColorFilter INHERITED;
237};
238
239sk_sp<SkFlattenable> SkSRGBGammaColorFilter::CreateProc(SkReadBuffer& buffer) {
240 uint32_t dir = buffer.read32();
241 if (!buffer.validate(dir <= 1)) {
242 return nullptr;
243 }
244 return sk_sp<SkFlattenable>(new SkSRGBGammaColorFilter(static_cast<Direction>(dir)));
245}
246
247template <SkSRGBGammaColorFilter::Direction dir>
248sk_sp<SkColorFilter> MakeSRGBGammaCF() {
249 static SkColorFilter* gSingleton = new SkSRGBGammaColorFilter(dir);
250 return sk_ref_sp(gSingleton);
251}
252
253sk_sp<SkColorFilter> SkColorFilters::LinearToSRGBGamma() {
254 return MakeSRGBGammaCF<SkSRGBGammaColorFilter::Direction::kLinearToSRGB>();
255}
256
257sk_sp<SkColorFilter> SkColorFilters::SRGBToLinearGamma() {
258 return MakeSRGBGammaCF<SkSRGBGammaColorFilter::Direction::kSRGBToLinear>();
259}
260
261///////////////////////////////////////////////////////////////////////////////////////////////////
262
263class SkMixerColorFilter : public SkColorFilter {
264public:
265 SkMixerColorFilter(sk_sp<SkColorFilter> cf0, sk_sp<SkColorFilter> cf1, float weight)
266 : fCF0(std::move(cf0)), fCF1(std::move(cf1)), fWeight(weight)
267 {
268 SkASSERT(fCF0);
269 SkASSERT(fWeight >= 0 && fWeight <= 1);
270 }
271
272 uint32_t getFlags() const override {
273 uint32_t f0 = fCF0->getFlags();
274 uint32_t f1 = fCF1 ? fCF1->getFlags() : ~0U;
275 return f0 & f1;
276 }
277
278 bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
279 // want cf0 * (1 - w) + cf1 * w == lerp(w)
280 // which means
281 // dr,dg,db,da <-- cf0
282 // r,g,b,a <-- cf1
283 struct State {
284 float orig_rgba[4 * SkRasterPipeline_kMaxStride];
285 float filtered_rgba[4 * SkRasterPipeline_kMaxStride];
286 };
287 auto state = rec.fAlloc->make<State>();
288 SkRasterPipeline* p = rec.fPipeline;
289
290 p->append(SkRasterPipeline::store_src, state->orig_rgba);
291 if (!fCF1) {
292 fCF0->appendStages(rec, shaderIsOpaque);
293 p->append(SkRasterPipeline::move_src_dst);
294 p->append(SkRasterPipeline::load_src, state->orig_rgba);
295 } else {
296 fCF0->appendStages(rec, shaderIsOpaque);
297 p->append(SkRasterPipeline::store_src, state->filtered_rgba);
298 p->append(SkRasterPipeline::load_src, state->orig_rgba);
299 fCF1->appendStages(rec, shaderIsOpaque);
300 p->append(SkRasterPipeline::load_dst, state->filtered_rgba);
301 }
302 float* storage = rec.fAlloc->make<float>(fWeight);
303 p->append(SkRasterPipeline::lerp_1_float, storage);
304 return true;
305 }
306
307 skvm::Color onProgram(skvm::Builder* p, skvm::Color c,
308 SkColorSpace* dstCS,
309 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
310 skvm::Color c0 = fCF0->program(p, c, dstCS, uniforms, alloc);
311 skvm::Color c1 = fCF1 ? fCF1->program(p, c, dstCS, uniforms, alloc) : c;
312 return (c0 && c1)
313 ? lerp(c0, c1, p->uniformF(uniforms->pushF(fWeight)))
314 : skvm::Color{};
315 }
316
317#if SK_SUPPORT_GPU
318 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
319 GrRecordingContext* context, const GrColorInfo& dstColorInfo) const override {
320 return GrMixerEffect::Make(
321 fCF0->asFragmentProcessor(context, dstColorInfo),
322 fCF1 ? fCF1->asFragmentProcessor(context, dstColorInfo) : nullptr,
323 fWeight);
324 }
325#endif
326
327protected:
328 void flatten(SkWriteBuffer& buffer) const override {
329 buffer.writeFlattenable(fCF0.get());
330 buffer.writeFlattenable(fCF1.get());
331 buffer.writeScalar(fWeight);
332 }
333
334private:
335 SK_FLATTENABLE_HOOKS(SkMixerColorFilter)
336
337 sk_sp<SkColorFilter> fCF0;
338 sk_sp<SkColorFilter> fCF1;
339 const float fWeight;
340
341 friend class SkColorFilter;
342
343 typedef SkColorFilter INHERITED;
344};
345
346sk_sp<SkFlattenable> SkMixerColorFilter::CreateProc(SkReadBuffer& buffer) {
347 sk_sp<SkColorFilter> cf0(buffer.readColorFilter());
348 sk_sp<SkColorFilter> cf1(buffer.readColorFilter());
349 const float weight = buffer.readScalar();
350 return SkColorFilters::Lerp(weight, std::move(cf0), std::move(cf1));
351}
352
353sk_sp<SkColorFilter> SkColorFilters::Lerp(float weight, sk_sp<SkColorFilter> cf0,
354 sk_sp<SkColorFilter> cf1) {
355 if (!cf0 && !cf1) {
356 return nullptr;
357 }
358 if (SkScalarIsNaN(weight)) {
359 return nullptr;
360 }
361
362 if (cf0 == cf1) {
363 return cf0; // or cf1
364 }
365
366 if (weight <= 0) {
367 return cf0;
368 }
369 if (weight >= 1) {
370 return cf1;
371 }
372
373 return sk_sp<SkColorFilter>(cf0
374 ? new SkMixerColorFilter(std::move(cf0), std::move(cf1), weight)
375 : new SkMixerColorFilter(std::move(cf1), nullptr, 1 - weight));
376}
377
378///////////////////////////////////////////////////////////////////////////////////////////////////
379
380#include "src/core/SkModeColorFilter.h"
381
382void SkColorFilter::RegisterFlattenables() {
383 SK_REGISTER_FLATTENABLE(SkComposeColorFilter);
384 SK_REGISTER_FLATTENABLE(SkModeColorFilter);
385 SK_REGISTER_FLATTENABLE(SkSRGBGammaColorFilter);
386 SK_REGISTER_FLATTENABLE(SkMixerColorFilter);
387}
388