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 | |
28 | bool SkColorFilter::onAsAColorMode(SkColor*, SkBlendMode*) const { |
29 | return false; |
30 | } |
31 | |
32 | bool SkColorFilter::onAsAColorMatrix(float matrix[20]) const { |
33 | return false; |
34 | } |
35 | |
36 | #if SK_SUPPORT_GPU |
37 | std::unique_ptr<GrFragmentProcessor> SkColorFilter::asFragmentProcessor(GrRecordingContext*, |
38 | const GrColorInfo&) const { |
39 | return nullptr; |
40 | } |
41 | #endif |
42 | |
43 | bool SkColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const { |
44 | return this->onAppendStages(rec, shaderIsOpaque); |
45 | } |
46 | |
47 | skvm::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 | |
61 | SkColor 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 | |
67 | SkColor4f 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 | |
97 | class SkComposeColorFilter : public SkColorFilter { |
98 | public: |
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 | |
133 | protected: |
134 | void flatten(SkWriteBuffer& buffer) const override { |
135 | buffer.writeFlattenable(fOuter.get()); |
136 | buffer.writeFlattenable(fInner.get()); |
137 | } |
138 | |
139 | private: |
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 | |
154 | sk_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 | |
160 | sk_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 | |
170 | class SkSRGBGammaColorFilter : public SkColorFilter { |
171 | public: |
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 | |
224 | protected: |
225 | void flatten(SkWriteBuffer& buffer) const override { |
226 | buffer.write32(static_cast<uint32_t>(fDir)); |
227 | } |
228 | |
229 | private: |
230 | SK_FLATTENABLE_HOOKS(SkSRGBGammaColorFilter) |
231 | |
232 | const Direction fDir; |
233 | SkColorSpaceXformSteps fSteps; |
234 | |
235 | friend class SkColorFilter; |
236 | typedef SkColorFilter INHERITED; |
237 | }; |
238 | |
239 | sk_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 | |
247 | template <SkSRGBGammaColorFilter::Direction dir> |
248 | sk_sp<SkColorFilter> MakeSRGBGammaCF() { |
249 | static SkColorFilter* gSingleton = new SkSRGBGammaColorFilter(dir); |
250 | return sk_ref_sp(gSingleton); |
251 | } |
252 | |
253 | sk_sp<SkColorFilter> SkColorFilters::LinearToSRGBGamma() { |
254 | return MakeSRGBGammaCF<SkSRGBGammaColorFilter::Direction::kLinearToSRGB>(); |
255 | } |
256 | |
257 | sk_sp<SkColorFilter> SkColorFilters::SRGBToLinearGamma() { |
258 | return MakeSRGBGammaCF<SkSRGBGammaColorFilter::Direction::kSRGBToLinear>(); |
259 | } |
260 | |
261 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
262 | |
263 | class SkMixerColorFilter : public SkColorFilter { |
264 | public: |
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 | |
327 | protected: |
328 | void flatten(SkWriteBuffer& buffer) const override { |
329 | buffer.writeFlattenable(fCF0.get()); |
330 | buffer.writeFlattenable(fCF1.get()); |
331 | buffer.writeScalar(fWeight); |
332 | } |
333 | |
334 | private: |
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 | |
346 | sk_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 | |
353 | sk_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 | |
382 | void SkColorFilter::RegisterFlattenables() { |
383 | SK_REGISTER_FLATTENABLE(SkComposeColorFilter); |
384 | SK_REGISTER_FLATTENABLE(SkModeColorFilter); |
385 | SK_REGISTER_FLATTENABLE(SkSRGBGammaColorFilter); |
386 | SK_REGISTER_FLATTENABLE(SkMixerColorFilter); |
387 | } |
388 | |