• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/SkRefCnt.h"
9 #include "include/core/SkString.h"
10 #include "include/core/SkUnPreMultiply.h"
11 #include "include/effects/SkRuntimeEffect.h"
12 #include "include/private/SkNx.h"
13 #include "include/private/SkTDArray.h"
14 #include "src/core/SkArenaAlloc.h"
15 #include "src/core/SkColorFilterBase.h"
16 #include "src/core/SkColorFilterPriv.h"
17 #include "src/core/SkColorSpacePriv.h"
18 #include "src/core/SkColorSpaceXformSteps.h"
19 #include "src/core/SkMatrixProvider.h"
20 #include "src/core/SkRasterPipeline.h"
21 #include "src/core/SkReadBuffer.h"
22 #include "src/core/SkRuntimeEffectPriv.h"
23 #include "src/core/SkVM.h"
24 #include "src/core/SkWriteBuffer.h"
25 
26 #if SK_SUPPORT_GPU
27 #include "src/gpu/GrColorInfo.h"
28 #include "src/gpu/GrColorSpaceXform.h"
29 #include "src/gpu/GrFragmentProcessor.h"
30 #endif
31 
asAColorMode(SkColor * color,SkBlendMode * mode) const32 bool SkColorFilter::asAColorMode(SkColor* color, SkBlendMode* mode) const {
33     return as_CFB(this)->onAsAColorMode(color, mode);
34 }
35 
asAColorMatrix(float matrix[20]) const36 bool SkColorFilter::asAColorMatrix(float matrix[20]) const {
37     return as_CFB(this)->onAsAColorMatrix(matrix);
38 }
39 
isAlphaUnchanged() const40 bool SkColorFilter::isAlphaUnchanged() const {
41     return as_CFB(this)->onIsAlphaUnchanged();
42 }
43 
Deserialize(const void * data,size_t size,const SkDeserialProcs * procs)44 sk_sp<SkColorFilter> SkColorFilter::Deserialize(const void* data, size_t size,
45                                                 const SkDeserialProcs* procs) {
46     return sk_sp<SkColorFilter>(static_cast<SkColorFilter*>(
47                                 SkFlattenable::Deserialize(
48                                 kSkColorFilter_Type, data, size, procs).release()));
49 }
50 
51 //////////////////////////////////////////////////////////////////////////////////////////////////
52 
onAsAColorMode(SkColor *,SkBlendMode *) const53 bool SkColorFilterBase::onAsAColorMode(SkColor*, SkBlendMode*) const {
54     return false;
55 }
56 
onAsAColorMatrix(float matrix[20]) const57 bool SkColorFilterBase::onAsAColorMatrix(float matrix[20]) const {
58     return false;
59 }
60 
61 #if SK_SUPPORT_GPU
asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,GrRecordingContext * context,const GrColorInfo & dstColorInfo) const62 GrFPResult SkColorFilterBase::asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
63                                                   GrRecordingContext* context,
64                                                   const GrColorInfo& dstColorInfo) const {
65     // This color filter doesn't implement `asFragmentProcessor`.
66     return GrFPFailure(std::move(inputFP));
67 }
68 #endif
69 
appendStages(const SkStageRec & rec,bool shaderIsOpaque) const70 bool SkColorFilterBase::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
71     return this->onAppendStages(rec, shaderIsOpaque);
72 }
73 
program(skvm::Builder * p,skvm::Color c,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const74 skvm::Color SkColorFilterBase::program(skvm::Builder* p, skvm::Color c,
75                                        const SkColorInfo& dst,
76                                        skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
77     skvm::F32 original = c.a;
78     if ((c = this->onProgram(p,c, dst, uniforms,alloc))) {
79         if (this->isAlphaUnchanged()) {
80             c.a = original;
81         }
82         return c;
83     }
84     //SkDebugf("cannot onProgram %s\n", this->getTypeName());
85     return {};
86 }
87 
filterColor(SkColor c) const88 SkColor SkColorFilter::filterColor(SkColor c) const {
89     // This is mostly meaningless. We should phase-out this call entirely.
90     SkColorSpace* cs = nullptr;
91     return this->filterColor4f(SkColor4f::FromColor(c), cs, cs).toSkColor();
92 }
93 
filterColor4f(const SkColor4f & origSrcColor,SkColorSpace * srcCS,SkColorSpace * dstCS) const94 SkColor4f SkColorFilter::filterColor4f(const SkColor4f& origSrcColor, SkColorSpace* srcCS,
95                                        SkColorSpace* dstCS) const {
96     SkPMColor4f color = { origSrcColor.fR, origSrcColor.fG, origSrcColor.fB, origSrcColor.fA };
97     SkColorSpaceXformSteps(srcCS, kUnpremul_SkAlphaType,
98                            dstCS, kPremul_SkAlphaType).apply(color.vec());
99 
100     return as_CFB(this)->onFilterColor4f(color, dstCS).unpremul();
101 }
102 
onFilterColor4f(const SkPMColor4f & color,SkColorSpace * dstCS) const103 SkPMColor4f SkColorFilterBase::onFilterColor4f(const SkPMColor4f& color,
104                                                SkColorSpace* dstCS) const {
105     constexpr size_t kEnoughForCommonFilters = 512;  // big enough for compose+colormatrix
106     SkSTArenaAlloc<kEnoughForCommonFilters> alloc;
107     SkRasterPipeline    pipeline(&alloc);
108     pipeline.append_constant_color(&alloc, color.vec());
109     SkPaint blankPaint;
110     SkSimpleMatrixProvider matrixProvider(SkMatrix::I());
111     SkStageRec rec = {
112         &pipeline, &alloc, kRGBA_F32_SkColorType, dstCS, blankPaint, nullptr, matrixProvider
113     };
114 
115     if (as_CFB(this)->onAppendStages(rec, color.fA == 1)) {
116         SkPMColor4f dst;
117         SkRasterPipeline_MemoryCtx dstPtr = { &dst, 0 };
118         pipeline.append(SkRasterPipeline::store_f32, &dstPtr);
119         pipeline.run(0,0, 1,1);
120         return dst;
121     }
122 
123     // This filter doesn't support SkRasterPipeline... try skvm.
124     skvm::Builder b;
125     skvm::Uniforms uni(b.uniform(), 4);
126     SkColor4f uniColor = {color.fR, color.fG, color.fB, color.fA};
127     SkColorInfo dstInfo = {kRGBA_F32_SkColorType, kPremul_SkAlphaType, sk_ref_sp(dstCS)};
128     if (skvm::Color filtered =
129             as_CFB(this)->program(&b, b.uniformColor(uniColor, &uni), dstInfo, &uni, &alloc)) {
130 
131         b.store({skvm::PixelFormat::FLOAT, 32,32,32,32, 0,32,64,96},
132                 b.varying<SkColor4f>(), filtered);
133 
134         const bool allow_jit = false;  // We're only filtering one color, no point JITing.
135         b.done("filterColor4f", allow_jit).eval(1, uni.buf.data(), &color);
136         return color;
137     }
138 
139     SkASSERT(false);
140     return SkPMColor4f{0,0,0,0};
141 }
142 
143 ///////////////////////////////////////////////////////////////////////////////////////////////////
144 
145 class SkComposeColorFilter : public SkColorFilterBase {
146 public:
onIsAlphaUnchanged() const147     bool onIsAlphaUnchanged() const override {
148         // Can only claim alphaunchanged support if both our proxys do.
149         return fOuter->isAlphaUnchanged() && fInner->isAlphaUnchanged();
150     }
151 
onAppendStages(const SkStageRec & rec,bool shaderIsOpaque) const152     bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
153         bool innerIsOpaque = shaderIsOpaque;
154         if (!fInner->isAlphaUnchanged()) {
155             innerIsOpaque = false;
156         }
157         return fInner->appendStages(rec, shaderIsOpaque) &&
158                fOuter->appendStages(rec, innerIsOpaque);
159     }
160 
onProgram(skvm::Builder * p,skvm::Color c,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const161     skvm::Color onProgram(skvm::Builder* p, skvm::Color c,
162                           const SkColorInfo& dst,
163                           skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
164                c = fInner->program(p, c, dst, uniforms, alloc);
165         return c ? fOuter->program(p, c, dst, uniforms, alloc) : skvm::Color{};
166     }
167 
168 #if SK_SUPPORT_GPU
asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,GrRecordingContext * context,const GrColorInfo & dstColorInfo) const169     GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
170                                    GrRecordingContext* context,
171                                    const GrColorInfo& dstColorInfo) const override {
172         GrFragmentProcessor* originalInputFP = inputFP.get();
173 
174         auto [innerSuccess, innerFP] =
175                 fInner->asFragmentProcessor(std::move(inputFP), context, dstColorInfo);
176         if (!innerSuccess) {
177             return GrFPFailure(std::move(innerFP));
178         }
179 
180         auto [outerSuccess, outerFP] =
181                 fOuter->asFragmentProcessor(std::move(innerFP), context, dstColorInfo);
182         if (!outerSuccess) {
183             // In the rare event that the outer FP cannot be built, we have no good way of
184             // separating the inputFP from the innerFP, so we need to return a cloned inputFP.
185             // This could hypothetically be expensive, but failure here should be extremely rare.
186             return GrFPFailure(originalInputFP->clone());
187         }
188 
189         return GrFPSuccess(std::move(outerFP));
190     }
191 #endif
192 
193     SK_FLATTENABLE_HOOKS(SkComposeColorFilter)
194 
195 protected:
flatten(SkWriteBuffer & buffer) const196     void flatten(SkWriteBuffer& buffer) const override {
197         buffer.writeFlattenable(fOuter.get());
198         buffer.writeFlattenable(fInner.get());
199     }
200 
201 private:
SkComposeColorFilter(sk_sp<SkColorFilter> outer,sk_sp<SkColorFilter> inner)202     SkComposeColorFilter(sk_sp<SkColorFilter> outer, sk_sp<SkColorFilter> inner)
203         : fOuter(as_CFB_sp(std::move(outer)))
204         , fInner(as_CFB_sp(std::move(inner)))
205     {}
206 
207     sk_sp<SkColorFilterBase> fOuter;
208     sk_sp<SkColorFilterBase> fInner;
209 
210     friend class SkColorFilter;
211 
212     using INHERITED = SkColorFilter;
213 };
214 
CreateProc(SkReadBuffer & buffer)215 sk_sp<SkFlattenable> SkComposeColorFilter::CreateProc(SkReadBuffer& buffer) {
216     sk_sp<SkColorFilter> outer(buffer.readColorFilter());
217     sk_sp<SkColorFilter> inner(buffer.readColorFilter());
218     return outer ? outer->makeComposed(std::move(inner)) : inner;
219 }
220 
makeComposed(sk_sp<SkColorFilter> inner) const221 sk_sp<SkColorFilter> SkColorFilter::makeComposed(sk_sp<SkColorFilter> inner) const {
222     if (!inner) {
223         return sk_ref_sp(this);
224     }
225 
226     return sk_sp<SkColorFilter>(new SkComposeColorFilter(sk_ref_sp(this), std::move(inner)));
227 }
228 
229 ///////////////////////////////////////////////////////////////////////////////////////////////////
230 
231 class SkSRGBGammaColorFilter : public SkColorFilterBase {
232 public:
233     enum class Direction {
234         kLinearToSRGB,
235         kSRGBToLinear,
236     };
__anonb988d4290102null237     SkSRGBGammaColorFilter(Direction dir) : fDir(dir), fSteps([&]{
238         // We handle premul/unpremul separately, so here just always upm->upm.
239         if (dir == Direction::kLinearToSRGB) {
240             return SkColorSpaceXformSteps{sk_srgb_linear_singleton(), kUnpremul_SkAlphaType,
241                                           sk_srgb_singleton(),        kUnpremul_SkAlphaType};
242         } else {
243             return SkColorSpaceXformSteps{sk_srgb_singleton(),        kUnpremul_SkAlphaType,
244                                           sk_srgb_linear_singleton(), kUnpremul_SkAlphaType};
245         }
246     }()) {}
247 
248 #if SK_SUPPORT_GPU
asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,GrRecordingContext * context,const GrColorInfo & dstColorInfo) const249     GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
250                                    GrRecordingContext* context,
251                                    const GrColorInfo& dstColorInfo) const override {
252         // wish our caller would let us know if our input was opaque...
253         constexpr SkAlphaType alphaType = kPremul_SkAlphaType;
254         switch (fDir) {
255             case Direction::kLinearToSRGB:
256                 return GrFPSuccess(GrColorSpaceXformEffect::Make(
257                                        std::move(inputFP),
258                                        sk_srgb_linear_singleton(), alphaType,
259                                        sk_srgb_singleton(),        alphaType));
260             case Direction::kSRGBToLinear:
261                 return GrFPSuccess(GrColorSpaceXformEffect::Make(
262                                        std::move(inputFP),
263                                        sk_srgb_singleton(),        alphaType,
264                                        sk_srgb_linear_singleton(), alphaType));
265         }
266         SkUNREACHABLE;
267     }
268 #endif
269 
onAppendStages(const SkStageRec & rec,bool shaderIsOpaque) const270     bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
271         if (!shaderIsOpaque) {
272             rec.fPipeline->append(SkRasterPipeline::unpremul);
273         }
274 
275         fSteps.apply(rec.fPipeline);
276 
277         if (!shaderIsOpaque) {
278             rec.fPipeline->append(SkRasterPipeline::premul);
279         }
280         return true;
281     }
282 
onProgram(skvm::Builder * p,skvm::Color c,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const283     skvm::Color onProgram(skvm::Builder* p, skvm::Color c, const SkColorInfo& dst,
284                           skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
285         return premul(fSteps.program(p, uniforms, unpremul(c)));
286     }
287 
288     SK_FLATTENABLE_HOOKS(SkSRGBGammaColorFilter)
289 
290 protected:
flatten(SkWriteBuffer & buffer) const291     void flatten(SkWriteBuffer& buffer) const override {
292         buffer.write32(static_cast<uint32_t>(fDir));
293     }
294 
295 private:
296     const Direction fDir;
297     SkColorSpaceXformSteps fSteps;
298 
299     friend class SkColorFilter;
300     using INHERITED = SkColorFilterBase;
301 };
302 
CreateProc(SkReadBuffer & buffer)303 sk_sp<SkFlattenable> SkSRGBGammaColorFilter::CreateProc(SkReadBuffer& buffer) {
304     uint32_t dir = buffer.read32();
305     if (!buffer.validate(dir <= 1)) {
306         return nullptr;
307     }
308     return sk_sp<SkFlattenable>(new SkSRGBGammaColorFilter(static_cast<Direction>(dir)));
309 }
310 
311 template <SkSRGBGammaColorFilter::Direction dir>
MakeSRGBGammaCF()312 sk_sp<SkColorFilter> MakeSRGBGammaCF() {
313     static SkColorFilter* gSingleton = new SkSRGBGammaColorFilter(dir);
314     return sk_ref_sp(gSingleton);
315 }
316 
LinearToSRGBGamma()317 sk_sp<SkColorFilter> SkColorFilters::LinearToSRGBGamma() {
318     return MakeSRGBGammaCF<SkSRGBGammaColorFilter::Direction::kLinearToSRGB>();
319 }
320 
SRGBToLinearGamma()321 sk_sp<SkColorFilter> SkColorFilters::SRGBToLinearGamma() {
322     return MakeSRGBGammaCF<SkSRGBGammaColorFilter::Direction::kSRGBToLinear>();
323 }
324 
325 struct SkWorkingFormatColorFilter : public SkColorFilterBase {
326     sk_sp<SkColorFilter>   fChild;
327     skcms_TransferFunction fTF;     bool fUseDstTF    = true;
328     skcms_Matrix3x3        fGamut;  bool fUseDstGamut = true;
329     SkAlphaType            fAT;     bool fUseDstAT    = true;
330 
SkWorkingFormatColorFilterSkWorkingFormatColorFilter331     SkWorkingFormatColorFilter(sk_sp<SkColorFilter>          child,
332                                const skcms_TransferFunction* tf,
333                                const skcms_Matrix3x3*        gamut,
334                                const SkAlphaType*            at) {
335         fChild = std::move(child);
336         if (tf)    { fTF    = *tf;    fUseDstTF    = false; }
337         if (gamut) { fGamut = *gamut; fUseDstGamut = false; }
338         if (at)    { fAT    = *at;    fUseDstAT    = false; }
339     }
340 
341 
workingFormatSkWorkingFormatColorFilter342     sk_sp<SkColorSpace> workingFormat(const sk_sp<SkColorSpace>& dstCS, SkAlphaType* at) const {
343         skcms_TransferFunction tf    = fTF;
344         skcms_Matrix3x3        gamut = fGamut;
345 
346         if (fUseDstTF   ) { SkAssertResult(dstCS->isNumericalTransferFn(&tf)); }
347         if (fUseDstGamut) { SkAssertResult(dstCS->toXYZD50             (&gamut)); }
348 
349         *at = fUseDstAT ? kPremul_SkAlphaType : fAT;
350         return SkColorSpace::MakeRGB(tf, gamut);
351     }
352 
353 #if SK_SUPPORT_GPU
asFragmentProcessorSkWorkingFormatColorFilter354     GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
355                                    GrRecordingContext* context,
356                                    const GrColorInfo& dstColorInfo) const override {
357         sk_sp<SkColorSpace> dstCS = dstColorInfo.refColorSpace();
358         if (!dstCS) { dstCS = SkColorSpace::MakeSRGB(); }
359 
360         SkAlphaType workingAT;
361         sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
362 
363         GrColorInfo dst = {dstColorInfo.colorType(), dstColorInfo.alphaType(), dstCS},
364                 working = {dstColorInfo.colorType(), workingAT, workingCS};
365 
366         auto [ok, fp] = as_CFB(fChild)->asFragmentProcessor(
367                 GrColorSpaceXformEffect::Make(std::move(inputFP), dst,working), context, working);
368 
369         return ok ? GrFPSuccess(GrColorSpaceXformEffect::Make(std::move(fp), working,dst))
370                   : GrFPFailure(std::move(fp));
371     }
372 #endif
373 
onAppendStagesSkWorkingFormatColorFilter374     bool onAppendStages(const SkStageRec&, bool) const override { return false; }
375 
onProgramSkWorkingFormatColorFilter376     skvm::Color onProgram(skvm::Builder* p, skvm::Color c, const SkColorInfo& rawDst,
377                           skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
378         sk_sp<SkColorSpace> dstCS = rawDst.refColorSpace();
379         if (!dstCS) { dstCS = SkColorSpace::MakeSRGB(); }
380 
381         SkAlphaType workingAT;
382         sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
383 
384         SkColorInfo dst = {rawDst.colorType(), kPremul_SkAlphaType, dstCS},
385                 working = {rawDst.colorType(), workingAT, workingCS};
386 
387         c = SkColorSpaceXformSteps{dst,working}.program(p, uniforms, c);
388         c = as_CFB(fChild)->program(p, c, working, uniforms, alloc);
389         return c ? SkColorSpaceXformSteps{working,dst}.program(p, uniforms, c)
390                  : c;
391     }
392 
onFilterColor4fSkWorkingFormatColorFilter393     SkPMColor4f onFilterColor4f(const SkPMColor4f& origColor,
394                                 SkColorSpace* rawDstCS) const override {
395         sk_sp<SkColorSpace> dstCS = sk_ref_sp(rawDstCS);
396         if (!dstCS) { dstCS = SkColorSpace::MakeSRGB(); }
397 
398         SkAlphaType workingAT;
399         sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
400 
401         SkColorInfo dst = {kUnknown_SkColorType, kPremul_SkAlphaType, dstCS},
402                 working = {kUnknown_SkColorType, workingAT, workingCS};
403 
404         SkPMColor4f color = origColor;
405         SkColorSpaceXformSteps{dst,working}.apply(color.vec());
406         color = as_CFB(fChild)->onFilterColor4f(color, working.colorSpace());
407         SkColorSpaceXformSteps{working,dst}.apply(color.vec());
408         return color;
409     }
410 
onIsAlphaUnchangedSkWorkingFormatColorFilter411     bool onIsAlphaUnchanged() const override { return fChild->isAlphaUnchanged(); }
412 
SK_FLATTENABLE_HOOKSSkWorkingFormatColorFilter413     SK_FLATTENABLE_HOOKS(SkWorkingFormatColorFilter)
414     void flatten(SkWriteBuffer& buffer) const override {
415         buffer.writeFlattenable(fChild.get());
416         buffer.writeBool(fUseDstTF);
417         buffer.writeBool(fUseDstGamut);
418         buffer.writeBool(fUseDstAT);
419         if (!fUseDstTF)    { buffer.writeScalarArray(&fTF.g, 7); }
420         if (!fUseDstGamut) { buffer.writeScalarArray(&fGamut.vals[0][0], 9); }
421         if (!fUseDstAT)    { buffer.writeInt(fAT); }
422     }
423 };
424 
CreateProc(SkReadBuffer & buffer)425 sk_sp<SkFlattenable> SkWorkingFormatColorFilter::CreateProc(SkReadBuffer& buffer) {
426     sk_sp<SkColorFilter> child = buffer.readColorFilter();
427     bool useDstTF    = buffer.readBool(),
428          useDstGamut = buffer.readBool(),
429          useDstAT    = buffer.readBool();
430 
431     skcms_TransferFunction tf;
432     skcms_Matrix3x3        gamut;
433     SkAlphaType            at;
434 
435     if (!useDstTF)    { buffer.readScalarArray(&tf.g, 7); }
436     if (!useDstGamut) { buffer.readScalarArray(&gamut.vals[0][0], 9); }
437     if (!useDstAT)    { at = buffer.read32LE(kLastEnum_SkAlphaType); }
438 
439     return SkColorFilterPriv::WithWorkingFormat(std::move(child),
440                                                 useDstTF    ? nullptr : &tf,
441                                                 useDstGamut ? nullptr : &gamut,
442                                                 useDstAT    ? nullptr : &at);
443 }
444 
WithWorkingFormat(sk_sp<SkColorFilter> child,const skcms_TransferFunction * tf,const skcms_Matrix3x3 * gamut,const SkAlphaType * at)445 sk_sp<SkColorFilter> SkColorFilterPriv::WithWorkingFormat(sk_sp<SkColorFilter> child,
446                                                           const skcms_TransferFunction* tf,
447                                                           const skcms_Matrix3x3* gamut,
448                                                           const SkAlphaType* at) {
449     return sk_make_sp<SkWorkingFormatColorFilter>(std::move(child), tf, gamut, at);
450 }
451 
452 ///////////////////////////////////////////////////////////////////////////////////////////////////
453 
Lerp(float weight,sk_sp<SkColorFilter> cf0,sk_sp<SkColorFilter> cf1)454 sk_sp<SkColorFilter> SkColorFilters::Lerp(float weight, sk_sp<SkColorFilter> cf0,
455                                                         sk_sp<SkColorFilter> cf1) {
456 #ifdef SK_ENABLE_SKSL
457     if (!cf0 && !cf1) {
458         return nullptr;
459     }
460     if (SkScalarIsNaN(weight)) {
461         return nullptr;
462     }
463 
464     if (cf0 == cf1) {
465         return cf0; // or cf1
466     }
467 
468     if (weight <= 0) {
469         return cf0;
470     }
471     if (weight >= 1) {
472         return cf1;
473     }
474 
475     sk_sp<SkRuntimeEffect> effect = SkMakeCachedRuntimeEffect(
476         SkRuntimeEffect::MakeForColorFilter,
477         "uniform colorFilter cf0;"
478         "uniform colorFilter cf1;"
479         "uniform half   weight;"
480         "half4 main(half4 color) {"
481             "return mix(cf0.eval(color), cf1.eval(color), weight);"
482         "}"
483     );
484     SkASSERT(effect);
485 
486     sk_sp<SkColorFilter> inputs[] = {cf0,cf1};
487     return effect->makeColorFilter(SkData::MakeWithCopy(&weight, sizeof(weight)),
488                                    inputs, SK_ARRAY_COUNT(inputs));
489 #else
490     // TODO(skia:12197)
491     return nullptr;
492 #endif
493 }
494 
495 ///////////////////////////////////////////////////////////////////////////////////////////////////
496 
497 #include "src/core/SkModeColorFilter.h"
498 
RegisterFlattenables()499 void SkColorFilterBase::RegisterFlattenables() {
500     SK_REGISTER_FLATTENABLE(SkComposeColorFilter);
501     SK_REGISTER_FLATTENABLE(SkModeColorFilter);
502     SK_REGISTER_FLATTENABLE(SkSRGBGammaColorFilter);
503     SK_REGISTER_FLATTENABLE(SkWorkingFormatColorFilter);
504 }
505