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/SkColorFilterPriv.h"
16 #include "src/core/SkColorSpacePriv.h"
17 #include "src/core/SkColorSpaceXformSteps.h"
18 #include "src/core/SkRasterPipeline.h"
19 #include "src/core/SkReadBuffer.h"
20 #include "src/core/SkWriteBuffer.h"
21
22 #if SK_SUPPORT_GPU
23 #include "src/gpu/GrFragmentProcessor.h"
24 #include "src/gpu/effects/generated/GrMixerEffect.h"
25 #endif
26
onAsAColorMode(SkColor *,SkBlendMode *) const27 bool SkColorFilter::onAsAColorMode(SkColor*, SkBlendMode*) const {
28 return false;
29 }
30
onAsAColorMatrix(float matrix[20]) const31 bool SkColorFilter::onAsAColorMatrix(float matrix[20]) const {
32 return false;
33 }
34
35 #if SK_SUPPORT_GPU
asFragmentProcessor(GrRecordingContext *,const GrColorInfo &) const36 std::unique_ptr<GrFragmentProcessor> SkColorFilter::asFragmentProcessor(GrRecordingContext*,
37 const GrColorInfo&) const {
38 return nullptr;
39 }
40 #endif
41
appendStages(const SkStageRec & rec,bool shaderIsOpaque) const42 bool SkColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
43 return this->onAppendStages(rec, shaderIsOpaque);
44 }
45
filterColor(SkColor c) const46 SkColor SkColorFilter::filterColor(SkColor c) const {
47 // This is mostly meaningless. We should phase-out this call entirely.
48 SkColorSpace* cs = nullptr;
49 return this->filterColor4f(SkColor4f::FromColor(c), cs, cs).toSkColor();
50 }
51
filterColor4f(const SkColor4f & origSrcColor,SkColorSpace * srcCS,SkColorSpace * dstCS) const52 SkColor4f SkColorFilter::filterColor4f(const SkColor4f& origSrcColor, SkColorSpace* srcCS,
53 SkColorSpace* dstCS) const {
54 #ifdef SK_SUPPORT_LEGACY_COLORFILTER_NO_SHADER
55 SkPMColor4f src = origSrcColor.premul();
56 SkColor4f color = *(SkColor4f*)&src;
57 #else
58 SkColor4f color = origSrcColor;
59 SkColorSpaceXformSteps(srcCS, kUnpremul_SkAlphaType,
60 dstCS, kPremul_SkAlphaType).apply(color.vec());
61 #endif
62
63 constexpr size_t kEnoughForCommonFilters = 512; // big enough for compose+colormatrix
64 SkSTArenaAlloc<kEnoughForCommonFilters> alloc;
65 SkRasterPipeline pipeline(&alloc);
66 pipeline.append_constant_color(&alloc, color.vec());
67 SkPaint dummyPaint;
68 SkStageRec rec = {
69 &pipeline, &alloc, kRGBA_F32_SkColorType, dstCS, dummyPaint, nullptr, SkMatrix::I()
70 };
71 this->onAppendStages(rec, color.fA == 1);
72
73 SkPMColor4f dst;
74 SkRasterPipeline_MemoryCtx dstPtr = { &dst, 0 };
75 pipeline.append(SkRasterPipeline::store_f32, &dstPtr);
76 pipeline.run(0,0, 1,1);
77 return dst.unpremul();
78 }
79
80 ///////////////////////////////////////////////////////////////////////////////////////////////////
81
82 /*
83 * Since colorfilters may be used on the GPU backend, and in that case we may string together
84 * many GrFragmentProcessors, we might exceed some internal instruction/resource limit.
85 *
86 * Since we don't yet know *what* those limits might be when we construct the final shader,
87 * we just set an arbitrary limit during construction. If later we find smarter ways to know what
88 * the limnits are, we can change this constant (or remove it).
89 */
90 #define SK_MAX_COMPOSE_COLORFILTER_COUNT 4
91
92 class SkComposeColorFilter : public SkColorFilter {
93 public:
getFlags() const94 uint32_t getFlags() const override {
95 // Can only claim alphaunchanged support if both our proxys do.
96 return fOuter->getFlags() & fInner->getFlags();
97 }
98
onAppendStages(const SkStageRec & rec,bool shaderIsOpaque) const99 bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
100 bool innerIsOpaque = shaderIsOpaque;
101 if (!(fInner->getFlags() & kAlphaUnchanged_Flag)) {
102 innerIsOpaque = false;
103 }
104 return fInner->appendStages(rec, shaderIsOpaque) &&
105 fOuter->appendStages(rec, innerIsOpaque);
106 }
107
108 #if SK_SUPPORT_GPU
asFragmentProcessor(GrRecordingContext * context,const GrColorInfo & dstColorInfo) const109 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
110 GrRecordingContext* context, const GrColorInfo& dstColorInfo) const override {
111 auto innerFP = fInner->asFragmentProcessor(context, dstColorInfo);
112 auto outerFP = fOuter->asFragmentProcessor(context, dstColorInfo);
113 if (!innerFP || !outerFP) {
114 return nullptr;
115 }
116 std::unique_ptr<GrFragmentProcessor> series[] = { std::move(innerFP), std::move(outerFP) };
117 return GrFragmentProcessor::RunInSeries(series, 2);
118 }
119 #endif
120
121 protected:
flatten(SkWriteBuffer & buffer) const122 void flatten(SkWriteBuffer& buffer) const override {
123 buffer.writeFlattenable(fOuter.get());
124 buffer.writeFlattenable(fInner.get());
125 }
126
127 private:
128 SK_FLATTENABLE_HOOKS(SkComposeColorFilter)
129
SkComposeColorFilter(sk_sp<SkColorFilter> outer,sk_sp<SkColorFilter> inner,int composedFilterCount)130 SkComposeColorFilter(sk_sp<SkColorFilter> outer, sk_sp<SkColorFilter> inner,
131 int composedFilterCount)
132 : fOuter(std::move(outer))
133 , fInner(std::move(inner))
134 , fComposedFilterCount(composedFilterCount)
135 {
136 SkASSERT(composedFilterCount >= 2);
137 SkASSERT(composedFilterCount <= SK_MAX_COMPOSE_COLORFILTER_COUNT);
138 }
139
privateComposedFilterCount() const140 int privateComposedFilterCount() const override {
141 return fComposedFilterCount;
142 }
143
144 sk_sp<SkColorFilter> fOuter;
145 sk_sp<SkColorFilter> fInner;
146 const int fComposedFilterCount;
147
148 friend class SkColorFilter;
149
150 typedef SkColorFilter INHERITED;
151 };
152
CreateProc(SkReadBuffer & buffer)153 sk_sp<SkFlattenable> SkComposeColorFilter::CreateProc(SkReadBuffer& buffer) {
154 sk_sp<SkColorFilter> outer(buffer.readColorFilter());
155 sk_sp<SkColorFilter> inner(buffer.readColorFilter());
156 return outer ? outer->makeComposed(std::move(inner)) : inner;
157 }
158
159
makeComposed(sk_sp<SkColorFilter> inner) const160 sk_sp<SkColorFilter> SkColorFilter::makeComposed(sk_sp<SkColorFilter> inner) const {
161 if (!inner) {
162 return sk_ref_sp(this);
163 }
164
165 int count = inner->privateComposedFilterCount() + this->privateComposedFilterCount();
166 if (count > SK_MAX_COMPOSE_COLORFILTER_COUNT) {
167 return nullptr;
168 }
169 return sk_sp<SkColorFilter>(new SkComposeColorFilter(sk_ref_sp(this), std::move(inner), count));
170 }
171
172 ///////////////////////////////////////////////////////////////////////////////////////////////////
173
174 #if SK_SUPPORT_GPU
175 #include "src/gpu/effects/GrSRGBEffect.h"
176 #endif
177
178 class SkSRGBGammaColorFilter : public SkColorFilter {
179 public:
180 enum class Direction {
181 kLinearToSRGB,
182 kSRGBToLinear,
183 };
__anon5cc7d1040102null184 SkSRGBGammaColorFilter(Direction dir) : fDir(dir), fSteps([&]{
185 // We handle premul/unpremul separately, so here just always upm->upm.
186 if (dir == Direction::kLinearToSRGB) {
187 return SkColorSpaceXformSteps{sk_srgb_linear_singleton(), kUnpremul_SkAlphaType,
188 sk_srgb_singleton(), kUnpremul_SkAlphaType};
189 } else {
190 return SkColorSpaceXformSteps{sk_srgb_singleton(), kUnpremul_SkAlphaType,
191 sk_srgb_linear_singleton(), kUnpremul_SkAlphaType};
192 }
193 }()) {}
194
195 #if SK_SUPPORT_GPU
asFragmentProcessor(GrRecordingContext *,const GrColorInfo &) const196 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(GrRecordingContext*,
197 const GrColorInfo&) const override {
198 // wish our caller would let us know if our input was opaque...
199 GrSRGBEffect::Alpha alpha = GrSRGBEffect::Alpha::kPremul;
200 switch (fDir) {
201 case Direction::kLinearToSRGB:
202 return GrSRGBEffect::Make(GrSRGBEffect::Mode::kLinearToSRGB, alpha);
203 case Direction::kSRGBToLinear:
204 return GrSRGBEffect::Make(GrSRGBEffect::Mode::kSRGBToLinear, alpha);
205 }
206 return nullptr;
207 }
208 #endif
209
onAppendStages(const SkStageRec & rec,bool shaderIsOpaque) const210 bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
211 if (!shaderIsOpaque) {
212 rec.fPipeline->append(SkRasterPipeline::unpremul);
213 }
214
215 // TODO: is it valuable to thread this through appendStages()?
216 bool shaderIsNormalized = false;
217 fSteps.apply(rec.fPipeline, shaderIsNormalized);
218
219 if (!shaderIsOpaque) {
220 rec.fPipeline->append(SkRasterPipeline::premul);
221 }
222 return true;
223 }
224
225 protected:
flatten(SkWriteBuffer & buffer) const226 void flatten(SkWriteBuffer& buffer) const override {
227 buffer.write32(static_cast<uint32_t>(fDir));
228 }
229
230 private:
231 SK_FLATTENABLE_HOOKS(SkSRGBGammaColorFilter)
232
233 const Direction fDir;
234 SkColorSpaceXformSteps fSteps;
235
236 friend class SkColorFilter;
237 typedef SkColorFilter INHERITED;
238 };
239
CreateProc(SkReadBuffer & buffer)240 sk_sp<SkFlattenable> SkSRGBGammaColorFilter::CreateProc(SkReadBuffer& buffer) {
241 uint32_t dir = buffer.read32();
242 if (!buffer.validate(dir <= 1)) {
243 return nullptr;
244 }
245 return sk_sp<SkFlattenable>(new SkSRGBGammaColorFilter(static_cast<Direction>(dir)));
246 }
247
248 template <SkSRGBGammaColorFilter::Direction dir>
MakeSRGBGammaCF()249 sk_sp<SkColorFilter> MakeSRGBGammaCF() {
250 static SkColorFilter* gSingleton = new SkSRGBGammaColorFilter(dir);
251 return sk_ref_sp(gSingleton);
252 }
253
LinearToSRGBGamma()254 sk_sp<SkColorFilter> SkColorFilters::LinearToSRGBGamma() {
255 return MakeSRGBGammaCF<SkSRGBGammaColorFilter::Direction::kLinearToSRGB>();
256 }
257
SRGBToLinearGamma()258 sk_sp<SkColorFilter> SkColorFilters::SRGBToLinearGamma() {
259 return MakeSRGBGammaCF<SkSRGBGammaColorFilter::Direction::kSRGBToLinear>();
260 }
261
262 ///////////////////////////////////////////////////////////////////////////////////////////////////
263
264 class SkMixerColorFilter : public SkColorFilter {
265 public:
SkMixerColorFilter(sk_sp<SkColorFilter> cf0,sk_sp<SkColorFilter> cf1,float weight)266 SkMixerColorFilter(sk_sp<SkColorFilter> cf0, sk_sp<SkColorFilter> cf1, float weight)
267 : fCF0(std::move(cf0)), fCF1(std::move(cf1)), fWeight(weight)
268 {
269 SkASSERT(fCF0);
270 SkASSERT(fWeight >= 0 && fWeight <= 1);
271 }
272
getFlags() const273 uint32_t getFlags() const override {
274 uint32_t f0 = fCF0->getFlags();
275 uint32_t f1 = fCF1 ? fCF1->getFlags() : ~0U;
276 return f0 & f1;
277 }
278
onAppendStages(const SkStageRec & rec,bool shaderIsOpaque) const279 bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
280 // want cf0 * (1 - w) + cf1 * w == lerp(w)
281 // which means
282 // dr,dg,db,da <-- cf0
283 // r,g,b,a <-- cf1
284 struct State {
285 float orig_rgba[4 * SkRasterPipeline_kMaxStride];
286 float filtered_rgba[4 * SkRasterPipeline_kMaxStride];
287 };
288 auto state = rec.fAlloc->make<State>();
289 SkRasterPipeline* p = rec.fPipeline;
290
291 p->append(SkRasterPipeline::store_src, state->orig_rgba);
292 if (!fCF1) {
293 fCF0->appendStages(rec, shaderIsOpaque);
294 p->append(SkRasterPipeline::move_src_dst);
295 p->append(SkRasterPipeline::load_src, state->orig_rgba);
296 } else {
297 fCF0->appendStages(rec, shaderIsOpaque);
298 p->append(SkRasterPipeline::store_src, state->filtered_rgba);
299 p->append(SkRasterPipeline::load_src, state->orig_rgba);
300 fCF1->appendStages(rec, shaderIsOpaque);
301 p->append(SkRasterPipeline::load_dst, state->filtered_rgba);
302 }
303 float* storage = rec.fAlloc->make<float>(fWeight);
304 p->append(SkRasterPipeline::lerp_1_float, storage);
305 return true;
306 }
307
308 #if SK_SUPPORT_GPU
asFragmentProcessor(GrRecordingContext * context,const GrColorInfo & dstColorInfo) const309 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
310 GrRecordingContext* context, const GrColorInfo& dstColorInfo) const override {
311 return GrMixerEffect::Make(
312 fCF0->asFragmentProcessor(context, dstColorInfo),
313 fCF1 ? fCF1->asFragmentProcessor(context, dstColorInfo) : nullptr,
314 fWeight);
315 }
316 #endif
317
318 protected:
flatten(SkWriteBuffer & buffer) const319 void flatten(SkWriteBuffer& buffer) const override {
320 buffer.writeFlattenable(fCF0.get());
321 buffer.writeFlattenable(fCF1.get());
322 buffer.writeScalar(fWeight);
323 }
324
325 private:
326 SK_FLATTENABLE_HOOKS(SkMixerColorFilter)
327
328 sk_sp<SkColorFilter> fCF0;
329 sk_sp<SkColorFilter> fCF1;
330 const float fWeight;
331
332 friend class SkColorFilter;
333
334 typedef SkColorFilter INHERITED;
335 };
336
CreateProc(SkReadBuffer & buffer)337 sk_sp<SkFlattenable> SkMixerColorFilter::CreateProc(SkReadBuffer& buffer) {
338 sk_sp<SkColorFilter> cf0(buffer.readColorFilter());
339 sk_sp<SkColorFilter> cf1(buffer.readColorFilter());
340 const float weight = buffer.readScalar();
341 return SkColorFilters::Lerp(weight, std::move(cf0), std::move(cf1));
342 }
343
Lerp(float weight,sk_sp<SkColorFilter> cf0,sk_sp<SkColorFilter> cf1)344 sk_sp<SkColorFilter> SkColorFilters::Lerp(float weight, sk_sp<SkColorFilter> cf0,
345 sk_sp<SkColorFilter> cf1) {
346 if (!cf0 && !cf1) {
347 return nullptr;
348 }
349 if (SkScalarIsNaN(weight)) {
350 return nullptr;
351 }
352
353 if (cf0 == cf1) {
354 return cf0; // or cf1
355 }
356
357 if (weight <= 0) {
358 return cf0;
359 }
360 if (weight >= 1) {
361 return cf1;
362 }
363
364 return sk_sp<SkColorFilter>(cf0
365 ? new SkMixerColorFilter(std::move(cf0), std::move(cf1), weight)
366 : new SkMixerColorFilter(std::move(cf1), nullptr, 1 - weight));
367 }
368
369 ///////////////////////////////////////////////////////////////////////////////////////////////////
370
371 #include "include/private/SkMutex.h"
372
373 #if SK_SUPPORT_GPU
374 #include "include/private/GrRecordingContext.h"
375 #include "src/gpu/effects/GrSkSLFP.h"
376 #include "src/sksl/SkSLByteCode.h"
377
378 class SkRuntimeColorFilter : public SkColorFilter {
379 public:
SkRuntimeColorFilter(int index,SkString sksl,sk_sp<SkData> inputs,void (* cpuFunction)(float[4],const void *))380 SkRuntimeColorFilter(int index, SkString sksl, sk_sp<SkData> inputs,
381 void (*cpuFunction)(float[4], const void*))
382 : fIndex(index)
383 , fSkSL(std::move(sksl))
384 , fInputs(std::move(inputs))
385 , fCpuFunction(cpuFunction) {}
386
387 #if SK_SUPPORT_GPU
asFragmentProcessor(GrRecordingContext * context,const GrColorInfo &) const388 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(GrRecordingContext* context,
389 const GrColorInfo&) const override {
390 return GrSkSLFP::Make(context, fIndex, "Runtime Color Filter", fSkSL,
391 fInputs ? fInputs->data() : nullptr,
392 fInputs ? fInputs->size() : 0);
393 }
394 #endif
395
onAppendStages(const SkStageRec & rec,bool shaderIsOpaque) const396 bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
397 if (fCpuFunction) {
398 struct CpuFuncCtx : public SkRasterPipeline_CallbackCtx {
399 SkRuntimeColorFilterFn cpuFn;
400 const void* inputs;
401 };
402 auto ctx = rec.fAlloc->make<CpuFuncCtx>();
403 ctx->inputs = fInputs->data();
404 ctx->cpuFn = fCpuFunction;
405 ctx->fn = [](SkRasterPipeline_CallbackCtx* arg, int active_pixels) {
406 auto ctx = (CpuFuncCtx*)arg;
407 for (int i = 0; i < active_pixels; i++) {
408 ctx->cpuFn(ctx->rgba + i * 4, ctx->inputs);
409 }
410 };
411 rec.fPipeline->append(SkRasterPipeline::callback, ctx);
412 } else {
413 auto ctx = rec.fAlloc->make<SkRasterPipeline_InterpreterCtx>();
414 // don't need to set ctx->paintColor
415 ctx->inputs = fInputs->data();
416 ctx->ninputs = fInputs->size() / 4;
417 ctx->shaderConvention = false;
418
419 SkAutoMutexExclusive ama(fByteCodeMutex);
420 if (!fByteCode) {
421 SkSL::Compiler c;
422 auto prog = c.convertProgram(SkSL::Program::kPipelineStage_Kind,
423 SkSL::String(fSkSL.c_str()),
424 SkSL::Program::Settings());
425 if (c.errorCount()) {
426 SkDebugf("%s\n", c.errorText().c_str());
427 return false;
428 }
429 fByteCode = c.toByteCode(*prog);
430 }
431 ctx->byteCode = fByteCode.get();
432 ctx->fn = ctx->byteCode->getFunction("main");
433 rec.fPipeline->append(SkRasterPipeline::interpreter, ctx);
434 }
435 return true;
436 }
437
438 protected:
flatten(SkWriteBuffer & buffer) const439 void flatten(SkWriteBuffer& buffer) const override {
440 // the client is responsible for ensuring that the indices match up between flattening and
441 // unflattening; we don't have a reasonable way to enforce that at the moment
442 buffer.writeInt(fIndex);
443 buffer.writeString(fSkSL.c_str());
444 if (fInputs) {
445 buffer.writeDataAsByteArray(fInputs.get());
446 } else {
447 buffer.writeByteArray(nullptr, 0);
448 }
449 }
450
451 private:
452 SK_FLATTENABLE_HOOKS(SkRuntimeColorFilter)
453
454 int fIndex;
455 SkString fSkSL;
456 sk_sp<SkData> fInputs;
457 SkRuntimeColorFilterFn fCpuFunction;
458
459 mutable SkMutex fByteCodeMutex;
460 mutable std::unique_ptr<SkSL::ByteCode> fByteCode;
461
462 friend class SkColorFilter;
463
464 typedef SkColorFilter INHERITED;
465 };
466
CreateProc(SkReadBuffer & buffer)467 sk_sp<SkFlattenable> SkRuntimeColorFilter::CreateProc(SkReadBuffer& buffer) {
468 int index = buffer.readInt();
469 SkString sksl;
470 buffer.readString(&sksl);
471 sk_sp<SkData> inputs = buffer.readByteArrayAsData();
472 return sk_sp<SkFlattenable>(new SkRuntimeColorFilter(index, std::move(sksl), std::move(inputs),
473 nullptr));
474 }
475
SkRuntimeColorFilterFactory(SkString sksl,SkRuntimeColorFilterFn cpuFunc)476 SkRuntimeColorFilterFactory::SkRuntimeColorFilterFactory(SkString sksl,
477 SkRuntimeColorFilterFn cpuFunc)
478 : fIndex(GrSkSLFP::NewIndex())
479 , fSkSL(std::move(sksl))
480 , fCpuFunc(cpuFunc) {}
481
make(sk_sp<SkData> inputs)482 sk_sp<SkColorFilter> SkRuntimeColorFilterFactory::make(sk_sp<SkData> inputs) {
483 return sk_sp<SkColorFilter>(new SkRuntimeColorFilter(fIndex, fSkSL, std::move(inputs),
484 fCpuFunc));
485 }
486
487 #endif // SK_SUPPORT_GPU
488
489 ///////////////////////////////////////////////////////////////////////////////////////////////////
490
491 #include "src/core/SkModeColorFilter.h"
492
RegisterFlattenables()493 void SkColorFilter::RegisterFlattenables() {
494 SK_REGISTER_FLATTENABLE(SkComposeColorFilter);
495 SK_REGISTER_FLATTENABLE(SkModeColorFilter);
496 SK_REGISTER_FLATTENABLE(SkSRGBGammaColorFilter);
497 SK_REGISTER_FLATTENABLE(SkMixerColorFilter);
498 #if SK_SUPPORT_GPU
499 SK_REGISTER_FLATTENABLE(SkRuntimeColorFilter);
500 #endif
501 }
502