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