• 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/base/SkTDArray.h"
13 #include "modules/skcms/skcms.h"
14 #include "src/base/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 defined(SK_GANESH)
27 #include "src/gpu/ganesh/GrColorInfo.h"
28 #include "src/gpu/ganesh/GrColorSpaceXform.h"
29 #include "src/gpu/ganesh/GrFragmentProcessor.h"
30 #endif
31 
32 #if defined(SK_GRAPHITE)
33 #include "src/gpu/graphite/KeyContext.h"
34 #include "src/gpu/graphite/KeyHelpers.h"
35 #include "src/gpu/graphite/PaintParamsKey.h"
36 #endif
37 
asAColorMode(SkColor * color,SkBlendMode * mode) const38 bool SkColorFilter::asAColorMode(SkColor* color, SkBlendMode* mode) const {
39     return as_CFB(this)->onAsAColorMode(color, mode);
40 }
41 
asAColorMatrix(float matrix[20]) const42 bool SkColorFilter::asAColorMatrix(float matrix[20]) const {
43     return as_CFB(this)->onAsAColorMatrix(matrix);
44 }
45 
isAlphaUnchanged() const46 bool SkColorFilter::isAlphaUnchanged() const {
47     return as_CFB(this)->onIsAlphaUnchanged();
48 }
49 
Deserialize(const void * data,size_t size,const SkDeserialProcs * procs)50 sk_sp<SkColorFilter> SkColorFilter::Deserialize(const void* data, size_t size,
51                                                 const SkDeserialProcs* procs) {
52     return sk_sp<SkColorFilter>(static_cast<SkColorFilter*>(
53                                 SkFlattenable::Deserialize(
54                                 kSkColorFilter_Type, data, size, procs).release()));
55 }
56 
57 //////////////////////////////////////////////////////////////////////////////////////////////////
58 
onAsAColorMode(SkColor *,SkBlendMode *) const59 bool SkColorFilterBase::onAsAColorMode(SkColor*, SkBlendMode*) const {
60     return false;
61 }
62 
onAsAColorMatrix(float matrix[20]) const63 bool SkColorFilterBase::onAsAColorMatrix(float matrix[20]) const {
64     return false;
65 }
66 
67 #if defined(SK_GANESH)
asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,GrRecordingContext * context,const GrColorInfo & dstColorInfo,const SkSurfaceProps & props) const68 GrFPResult SkColorFilterBase::asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
69                                                   GrRecordingContext* context,
70                                                   const GrColorInfo& dstColorInfo,
71                                                   const SkSurfaceProps& props) const {
72     // This color filter doesn't implement `asFragmentProcessor`.
73     return GrFPFailure(std::move(inputFP));
74 }
75 #endif
76 
program(skvm::Builder * p,skvm::Color c,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const77 skvm::Color SkColorFilterBase::program(skvm::Builder* p, skvm::Color c,
78                                        const SkColorInfo& dst,
79                                        skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
80     skvm::F32 original = c.a;
81     if ((c = this->onProgram(p,c, dst, uniforms,alloc))) {
82         if (this->isAlphaUnchanged()) {
83             c.a = original;
84         }
85         return c;
86     }
87     //SkDebugf("cannot program %s\n", this->getTypeName());
88     return {};
89 }
90 
filterColor(SkColor c) const91 SkColor SkColorFilter::filterColor(SkColor c) const {
92     // This is mostly meaningless. We should phase-out this call entirely.
93     SkColorSpace* cs = nullptr;
94     return this->filterColor4f(SkColor4f::FromColor(c), cs, cs).toSkColor();
95 }
96 
filterColor4f(const SkColor4f & origSrcColor,SkColorSpace * srcCS,SkColorSpace * dstCS) const97 SkColor4f SkColorFilter::filterColor4f(const SkColor4f& origSrcColor, SkColorSpace* srcCS,
98                                        SkColorSpace* dstCS) const {
99     SkPMColor4f color = { origSrcColor.fR, origSrcColor.fG, origSrcColor.fB, origSrcColor.fA };
100     SkColorSpaceXformSteps(srcCS, kUnpremul_SkAlphaType,
101                            dstCS, kPremul_SkAlphaType).apply(color.vec());
102 
103     return as_CFB(this)->onFilterColor4f(color, dstCS).unpremul();
104 }
105 
onFilterColor4f(const SkPMColor4f & color,SkColorSpace * dstCS) const106 SkPMColor4f SkColorFilterBase::onFilterColor4f(const SkPMColor4f& color,
107                                                SkColorSpace* dstCS) const {
108     constexpr size_t kEnoughForCommonFilters = 512;  // big enough for compose+colormatrix
109     SkSTArenaAlloc<kEnoughForCommonFilters> alloc;
110     SkRasterPipeline    pipeline(&alloc);
111     pipeline.append_constant_color(&alloc, color.vec());
112     SkPaint solidPaint;
113     solidPaint.setColor4f(color.unpremul());
114     SkMatrixProvider matrixProvider(SkMatrix::I());
115     SkSurfaceProps props{}; // default OK; colorFilters don't render text
116     SkStageRec rec = {&pipeline, &alloc, kRGBA_F32_SkColorType, dstCS, solidPaint, props};
117 
118     if (as_CFB(this)->appendStages(rec, color.fA == 1)) {
119         SkPMColor4f dst;
120         SkRasterPipeline_MemoryCtx dstPtr = { &dst, 0 };
121         pipeline.append(SkRasterPipelineOp::store_f32, &dstPtr);
122         pipeline.run(0,0, 1,1);
123         return dst;
124     }
125 
126     // This filter doesn't support SkRasterPipeline... try skvm.
127     skvm::Builder b;
128     skvm::Uniforms uni(b.uniform(), 4);
129     SkColor4f uniColor = {color.fR, color.fG, color.fB, color.fA};
130     SkColorInfo dstInfo = {kRGBA_F32_SkColorType, kPremul_SkAlphaType, sk_ref_sp(dstCS)};
131     if (skvm::Color filtered =
132             as_CFB(this)->program(&b, b.uniformColor(uniColor, &uni), dstInfo, &uni, &alloc)) {
133 
134         b.store({skvm::PixelFormat::FLOAT, 32,32,32,32, 0,32,64,96},
135                 b.varying<SkColor4f>(), filtered);
136 
137         const bool allow_jit = false;  // We're only filtering one color, no point JITing.
138         b.done("filterColor4f", allow_jit).eval(1, uni.buf.data(), &color);
139         return color;
140     }
141 
142     SkASSERT(false);
143     return SkPMColor4f{0,0,0,0};
144 }
145 
146 #if defined(SK_GRAPHITE)
addToKey(const skgpu::graphite::KeyContext & keyContext,skgpu::graphite::PaintParamsKeyBuilder * builder,skgpu::graphite::PipelineDataGatherer * gatherer) const147 void SkColorFilterBase::addToKey(const skgpu::graphite::KeyContext& keyContext,
148                                  skgpu::graphite::PaintParamsKeyBuilder* builder,
149                                  skgpu::graphite::PipelineDataGatherer* gatherer) const {
150     using namespace skgpu::graphite;
151 
152     // Return the input color as-is.
153     PassthroughShaderBlock::BeginBlock(keyContext, builder, gatherer);
154     builder->endBlock();
155 }
156 #endif
157 
158 ///////////////////////////////////////////////////////////////////////////////////////////////////
159 
160 class SkComposeColorFilter final : public SkColorFilterBase {
161 public:
onIsAlphaUnchanged() const162     bool onIsAlphaUnchanged() const override {
163         // Can only claim alphaunchanged support if both our proxys do.
164         return fOuter->isAlphaUnchanged() && fInner->isAlphaUnchanged();
165     }
166 
appendStages(const SkStageRec & rec,bool shaderIsOpaque) const167     bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
168         bool innerIsOpaque = shaderIsOpaque;
169         if (!fInner->isAlphaUnchanged()) {
170             innerIsOpaque = false;
171         }
172         return fInner->appendStages(rec, shaderIsOpaque) &&
173                fOuter->appendStages(rec, innerIsOpaque);
174     }
175 
onProgram(skvm::Builder * p,skvm::Color c,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const176     skvm::Color onProgram(skvm::Builder* p, skvm::Color c,
177                           const SkColorInfo& dst,
178                           skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
179                c = fInner->program(p, c, dst, uniforms, alloc);
180         return c ? fOuter->program(p, c, dst, uniforms, alloc) : skvm::Color{};
181     }
182 
183 #if defined(SK_GANESH)
asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,GrRecordingContext * context,const GrColorInfo & dstColorInfo,const SkSurfaceProps & props) const184     GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
185                                    GrRecordingContext* context,
186                                    const GrColorInfo& dstColorInfo,
187                                    const SkSurfaceProps& props) const override {
188         // Unfortunately, we need to clone the input before we know we need it. This lets us return
189         // the original FP if either internal color filter fails.
190         auto inputClone = inputFP ? inputFP->clone() : nullptr;
191 
192         auto [innerSuccess, innerFP] =
193                 fInner->asFragmentProcessor(std::move(inputFP), context, dstColorInfo, props);
194         if (!innerSuccess) {
195             return GrFPFailure(std::move(inputClone));
196         }
197 
198         auto [outerSuccess, outerFP] =
199                 fOuter->asFragmentProcessor(std::move(innerFP), context, dstColorInfo, props);
200         if (!outerSuccess) {
201             return GrFPFailure(std::move(inputClone));
202         }
203 
204         return GrFPSuccess(std::move(outerFP));
205     }
206 #endif
207 
208 #if defined(SK_GRAPHITE)
addToKey(const skgpu::graphite::KeyContext & keyContext,skgpu::graphite::PaintParamsKeyBuilder * builder,skgpu::graphite::PipelineDataGatherer * gatherer) const209     void addToKey(const skgpu::graphite::KeyContext& keyContext,
210                   skgpu::graphite::PaintParamsKeyBuilder* builder,
211                   skgpu::graphite::PipelineDataGatherer* gatherer) const override {
212         using namespace skgpu::graphite;
213 
214         ComposeColorFilterBlock::BeginBlock(keyContext, builder, gatherer);
215 
216         as_CFB(fInner)->addToKey(keyContext, builder, gatherer);
217         as_CFB(fOuter)->addToKey(keyContext, builder, gatherer);
218 
219         builder->endBlock();
220     }
221 #endif // SK_GRAPHITE
222 
223 protected:
flatten(SkWriteBuffer & buffer) const224     void flatten(SkWriteBuffer& buffer) const override {
225         buffer.writeFlattenable(fOuter.get());
226         buffer.writeFlattenable(fInner.get());
227     }
228 
229 private:
230     friend void ::SkRegisterComposeColorFilterFlattenable();
231     SK_FLATTENABLE_HOOKS(SkComposeColorFilter)
232 
SkComposeColorFilter(sk_sp<SkColorFilter> outer,sk_sp<SkColorFilter> inner)233     SkComposeColorFilter(sk_sp<SkColorFilter> outer, sk_sp<SkColorFilter> inner)
234         : fOuter(as_CFB_sp(std::move(outer)))
235         , fInner(as_CFB_sp(std::move(inner)))
236     {}
237 
238     sk_sp<SkColorFilterBase> fOuter;
239     sk_sp<SkColorFilterBase> fInner;
240 
241     friend class SkColorFilter;
242 
243     using INHERITED = SkColorFilter;
244 };
245 
CreateProc(SkReadBuffer & buffer)246 sk_sp<SkFlattenable> SkComposeColorFilter::CreateProc(SkReadBuffer& buffer) {
247     sk_sp<SkColorFilter> outer(buffer.readColorFilter());
248     sk_sp<SkColorFilter> inner(buffer.readColorFilter());
249     return outer ? outer->makeComposed(std::move(inner)) : inner;
250 }
251 
makeComposed(sk_sp<SkColorFilter> inner) const252 sk_sp<SkColorFilter> SkColorFilter::makeComposed(sk_sp<SkColorFilter> inner) const {
253     if (!inner) {
254         return sk_ref_sp(this);
255     }
256 
257     return sk_sp<SkColorFilter>(new SkComposeColorFilter(sk_ref_sp(this), std::move(inner)));
258 }
259 
260 ///////////////////////////////////////////////////////////////////////////////////////////////////
261 
262 class ColorSpaceXformColorFilter final : public SkColorFilterBase {
263 public:
ColorSpaceXformColorFilter(sk_sp<SkColorSpace> src,sk_sp<SkColorSpace> dst)264     ColorSpaceXformColorFilter(sk_sp<SkColorSpace> src, sk_sp<SkColorSpace> dst)
265             : fSrc(std::move(src))
266             , fDst(std::move(dst))
267             , fSteps(
268                       // We handle premul/unpremul separately, so here just always upm->upm.
269                       fSrc.get(),
270                       kUnpremul_SkAlphaType,
271                       fDst.get(),
272                       kUnpremul_SkAlphaType)
273 
274     {}
275 
276 #if defined(SK_GANESH)
asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,GrRecordingContext * context,const GrColorInfo & dstColorInfo,const SkSurfaceProps & props) const277     GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
278                                    GrRecordingContext* context,
279                                    const GrColorInfo& dstColorInfo,
280                                    const SkSurfaceProps& props) const override {
281         // wish our caller would let us know if our input was opaque...
282         constexpr SkAlphaType alphaType = kPremul_SkAlphaType;
283         return GrFPSuccess(GrColorSpaceXformEffect::Make(
284                 std::move(inputFP), fSrc.get(), alphaType, fDst.get(), alphaType));
285         SkUNREACHABLE;
286     }
287 #endif
288 
289 #if defined(SK_GRAPHITE)
addToKey(const skgpu::graphite::KeyContext & keyContext,skgpu::graphite::PaintParamsKeyBuilder * builder,skgpu::graphite::PipelineDataGatherer * gatherer) const290     void addToKey(const skgpu::graphite::KeyContext& keyContext,
291                   skgpu::graphite::PaintParamsKeyBuilder* builder,
292                   skgpu::graphite::PipelineDataGatherer* gatherer) const override {
293         using namespace skgpu::graphite;
294 
295         constexpr SkAlphaType alphaType = kPremul_SkAlphaType;
296         ColorSpaceTransformBlock::ColorSpaceTransformData data(
297                 fSrc.get(), alphaType, fDst.get(), alphaType);
298         ColorSpaceTransformBlock::BeginBlock(keyContext, builder, gatherer, &data);
299         builder->endBlock();
300     }
301 #endif
302 
appendStages(const SkStageRec & rec,bool shaderIsOpaque) const303     bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
304         if (!shaderIsOpaque) {
305             rec.fPipeline->append(SkRasterPipelineOp::unpremul);
306         }
307 
308         fSteps.apply(rec.fPipeline);
309 
310         if (!shaderIsOpaque) {
311             rec.fPipeline->append(SkRasterPipelineOp::premul);
312         }
313         return true;
314     }
315 
onProgram(skvm::Builder * p,skvm::Color c,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const316     skvm::Color onProgram(skvm::Builder* p, skvm::Color c, const SkColorInfo& dst,
317                           skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
318         return premul(fSteps.program(p, uniforms, unpremul(c)));
319     }
320 
321 protected:
flatten(SkWriteBuffer & buffer) const322     void flatten(SkWriteBuffer& buffer) const override {
323         buffer.writeDataAsByteArray(fSrc->serialize().get());
324         buffer.writeDataAsByteArray(fDst->serialize().get());
325     }
326 
327 private:
328     friend void ::SkRegisterColorSpaceXformColorFilterFlattenable();
329     SK_FLATTENABLE_HOOKS(ColorSpaceXformColorFilter)
330     static sk_sp<SkFlattenable> LegacyGammaOnlyCreateProc(SkReadBuffer& buffer);
331 
332     const sk_sp<SkColorSpace> fSrc;
333     const sk_sp<SkColorSpace> fDst;
334     SkColorSpaceXformSteps fSteps;
335 
336     friend class SkColorFilter;
337     using INHERITED = SkColorFilterBase;
338 };
339 
LegacyGammaOnlyCreateProc(SkReadBuffer & buffer)340 sk_sp<SkFlattenable> ColorSpaceXformColorFilter::LegacyGammaOnlyCreateProc(SkReadBuffer& buffer) {
341     uint32_t dir = buffer.read32();
342     if (!buffer.validate(dir <= 1)) {
343         return nullptr;
344     }
345     if (dir == 0) {
346       return SkColorFilters::LinearToSRGBGamma();
347     }
348 	return SkColorFilters::SRGBToLinearGamma();
349 }
350 
CreateProc(SkReadBuffer & buffer)351 sk_sp<SkFlattenable> ColorSpaceXformColorFilter::CreateProc(SkReadBuffer& buffer) {
352     sk_sp<SkColorSpace> colorSpaces[2];
353     for (int i = 0; i < 2; ++i) {
354         auto data = buffer.readByteArrayAsData();
355         if (!buffer.validate(data != nullptr)) {
356             return nullptr;
357         }
358         colorSpaces[i] = SkColorSpace::Deserialize(data->data(), data->size());
359         if (!buffer.validate(colorSpaces[i] != nullptr)) {
360             return nullptr;
361         }
362     }
363     return sk_sp<SkFlattenable>(
364             new ColorSpaceXformColorFilter(std::move(colorSpaces[0]), std::move(colorSpaces[1])));
365 }
366 
LinearToSRGBGamma()367 sk_sp<SkColorFilter> SkColorFilters::LinearToSRGBGamma() {
368     static SkColorFilter* gSingleton = new ColorSpaceXformColorFilter(
369             SkColorSpace::MakeSRGBLinear(), SkColorSpace::MakeSRGB());
370     return sk_ref_sp(gSingleton);
371 }
372 
SRGBToLinearGamma()373 sk_sp<SkColorFilter> SkColorFilters::SRGBToLinearGamma() {
374     static SkColorFilter* gSingleton = new ColorSpaceXformColorFilter(
375             SkColorSpace::MakeSRGB(), SkColorSpace::MakeSRGBLinear());
376     return sk_ref_sp(gSingleton);
377 }
378 
MakeColorSpaceXform(sk_sp<SkColorSpace> src,sk_sp<SkColorSpace> dst)379 sk_sp<SkColorFilter> SkColorFilterPriv::MakeColorSpaceXform(sk_sp<SkColorSpace> src,
380                                                             sk_sp<SkColorSpace> dst) {
381     return sk_make_sp<ColorSpaceXformColorFilter>(std::move(src), std::move(dst));
382 }
383 
384 class SkWorkingFormatColorFilter final : public SkColorFilterBase {
385 public:
SkWorkingFormatColorFilter(sk_sp<SkColorFilter> child,const skcms_TransferFunction * tf,const skcms_Matrix3x3 * gamut,const SkAlphaType * at)386     SkWorkingFormatColorFilter(sk_sp<SkColorFilter>          child,
387                                const skcms_TransferFunction* tf,
388                                const skcms_Matrix3x3*        gamut,
389                                const SkAlphaType*            at) {
390         fChild = std::move(child);
391         if (tf)    { fTF    = *tf;    fUseDstTF    = false; }
392         if (gamut) { fGamut = *gamut; fUseDstGamut = false; }
393         if (at)    { fAT    = *at;    fUseDstAT    = false; }
394     }
395 
workingFormat(const sk_sp<SkColorSpace> & dstCS,SkAlphaType * at) const396     sk_sp<SkColorSpace> workingFormat(const sk_sp<SkColorSpace>& dstCS, SkAlphaType* at) const {
397         skcms_TransferFunction tf    = fTF;
398         skcms_Matrix3x3        gamut = fGamut;
399 
400         if (fUseDstTF   ) { SkAssertResult(dstCS->isNumericalTransferFn(&tf)); }
401         if (fUseDstGamut) { SkAssertResult(dstCS->toXYZD50             (&gamut)); }
402 
403         *at = fUseDstAT ? kPremul_SkAlphaType : fAT;
404         return SkColorSpace::MakeRGB(tf, gamut);
405     }
406 
407 #if defined(SK_GANESH)
asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,GrRecordingContext * context,const GrColorInfo & dstColorInfo,const SkSurfaceProps & props) const408     GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
409                                    GrRecordingContext* context,
410                                    const GrColorInfo& dstColorInfo,
411                                    const SkSurfaceProps& props) const override {
412         sk_sp<SkColorSpace> dstCS = dstColorInfo.refColorSpace();
413         if (!dstCS) { dstCS = SkColorSpace::MakeSRGB(); }
414 
415         SkAlphaType workingAT;
416         sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
417 
418         GrColorInfo dst = {dstColorInfo.colorType(), dstColorInfo.alphaType(), dstCS},
419                 working = {dstColorInfo.colorType(), workingAT, workingCS};
420 
421         auto [ok, fp] = as_CFB(fChild)->asFragmentProcessor(
422                 GrColorSpaceXformEffect::Make(std::move(inputFP), dst,working), context, working,
423                                               props);
424 
425         return ok ? GrFPSuccess(GrColorSpaceXformEffect::Make(std::move(fp), working,dst))
426                   : GrFPFailure(std::move(fp));
427     }
428 #endif
429 
430 #if defined(SK_GRAPHITE)
addToKey(const skgpu::graphite::KeyContext & keyContext,skgpu::graphite::PaintParamsKeyBuilder * builder,skgpu::graphite::PipelineDataGatherer * gatherer) const431     void addToKey(const skgpu::graphite::KeyContext& keyContext,
432                   skgpu::graphite::PaintParamsKeyBuilder* builder,
433                   skgpu::graphite::PipelineDataGatherer* gatherer) const override {
434         using namespace skgpu::graphite;
435 
436         const SkAlphaType dstAT = keyContext.dstColorInfo().alphaType();
437         sk_sp<SkColorSpace> dstCS = keyContext.dstColorInfo().refColorSpace();
438         if (!dstCS) {
439             dstCS = SkColorSpace::MakeSRGB();
440         }
441 
442         SkAlphaType workingAT;
443         sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
444 
445         ColorSpaceTransformBlock::ColorSpaceTransformData data1(
446                 dstCS.get(), dstAT, workingCS.get(), workingAT);
447         ColorSpaceTransformBlock::BeginBlock(keyContext, builder, gatherer, &data1);
448         builder->endBlock();
449 
450         as_CFB(fChild)->addToKey(keyContext, builder, gatherer);
451 
452         ColorSpaceTransformBlock::ColorSpaceTransformData data2(
453                 workingCS.get(), workingAT, dstCS.get(), dstAT);
454         ColorSpaceTransformBlock::BeginBlock(keyContext, builder, gatherer, &data2);
455         builder->endBlock();
456     }
457 #endif
458 
appendStages(const SkStageRec & rec,bool shaderIsOpaque) const459     bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
460         sk_sp<SkColorSpace> dstCS = sk_ref_sp(rec.fDstCS);
461 
462         if (!dstCS) { dstCS = SkColorSpace::MakeSRGB(); }
463 
464         SkAlphaType workingAT;
465         sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
466 
467         SkColorInfo dst = {rec.fDstColorType, kPremul_SkAlphaType, dstCS},
468                 working = {rec.fDstColorType, workingAT, workingCS};
469 
470         SkStageRec workingRec = {rec.fPipeline,
471                                  rec.fAlloc,
472                                  rec.fDstColorType,
473                                  workingCS.get(),
474                                  rec.fPaint,
475                                  rec.fSurfaceProps};
476 
477         rec.fAlloc->make<SkColorSpaceXformSteps>(dst, working)->apply(rec.fPipeline);
478         if (!as_CFB(fChild)->appendStages(workingRec, shaderIsOpaque)) {
479             return false;
480         }
481         rec.fAlloc->make<SkColorSpaceXformSteps>(working, dst)->apply(rec.fPipeline);
482         return true;
483     }
484 
onProgram(skvm::Builder * p,skvm::Color c,const SkColorInfo & rawDst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const485     skvm::Color onProgram(skvm::Builder* p, skvm::Color c, const SkColorInfo& rawDst,
486                           skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
487         sk_sp<SkColorSpace> dstCS = rawDst.refColorSpace();
488         if (!dstCS) { dstCS = SkColorSpace::MakeSRGB(); }
489 
490         SkAlphaType workingAT;
491         sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
492 
493         SkColorInfo dst = {rawDst.colorType(), kPremul_SkAlphaType, dstCS},
494                 working = {rawDst.colorType(), workingAT, workingCS};
495 
496         c = SkColorSpaceXformSteps{dst,working}.program(p, uniforms, c);
497         c = as_CFB(fChild)->program(p, c, working, uniforms, alloc);
498         return c ? SkColorSpaceXformSteps{working,dst}.program(p, uniforms, c)
499                  : c;
500     }
501 
onFilterColor4f(const SkPMColor4f & origColor,SkColorSpace * rawDstCS) const502     SkPMColor4f onFilterColor4f(const SkPMColor4f& origColor,
503                                 SkColorSpace* rawDstCS) const override {
504         sk_sp<SkColorSpace> dstCS = sk_ref_sp(rawDstCS);
505         if (!dstCS) { dstCS = SkColorSpace::MakeSRGB(); }
506 
507         SkAlphaType workingAT;
508         sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
509 
510         SkColorInfo dst = {kUnknown_SkColorType, kPremul_SkAlphaType, dstCS},
511                 working = {kUnknown_SkColorType, workingAT, workingCS};
512 
513         SkPMColor4f color = origColor;
514         SkColorSpaceXformSteps{dst,working}.apply(color.vec());
515         color = as_CFB(fChild)->onFilterColor4f(color, working.colorSpace());
516         SkColorSpaceXformSteps{working,dst}.apply(color.vec());
517         return color;
518     }
519 
onIsAlphaUnchanged() const520     bool onIsAlphaUnchanged() const override { return fChild->isAlphaUnchanged(); }
521 
522 private:
523     friend void ::SkRegisterWorkingFormatColorFilterFlattenable();
SK_FLATTENABLE_HOOKS(SkWorkingFormatColorFilter)524     SK_FLATTENABLE_HOOKS(SkWorkingFormatColorFilter)
525 
526     void flatten(SkWriteBuffer& buffer) const override {
527         buffer.writeFlattenable(fChild.get());
528         buffer.writeBool(fUseDstTF);
529         buffer.writeBool(fUseDstGamut);
530         buffer.writeBool(fUseDstAT);
531         if (!fUseDstTF)    { buffer.writeScalarArray(&fTF.g, 7); }
532         if (!fUseDstGamut) { buffer.writeScalarArray(&fGamut.vals[0][0], 9); }
533         if (!fUseDstAT)    { buffer.writeInt(fAT); }
534     }
535 
536     sk_sp<SkColorFilter>   fChild;
537     skcms_TransferFunction fTF;     bool fUseDstTF    = true;
538     skcms_Matrix3x3        fGamut;  bool fUseDstGamut = true;
539     SkAlphaType            fAT;     bool fUseDstAT    = true;
540 };
541 
CreateProc(SkReadBuffer & buffer)542 sk_sp<SkFlattenable> SkWorkingFormatColorFilter::CreateProc(SkReadBuffer& buffer) {
543     sk_sp<SkColorFilter> child = buffer.readColorFilter();
544     bool useDstTF    = buffer.readBool(),
545          useDstGamut = buffer.readBool(),
546          useDstAT    = buffer.readBool();
547 
548     skcms_TransferFunction tf;
549     skcms_Matrix3x3        gamut;
550     SkAlphaType            at;
551 
552     if (!useDstTF)    { buffer.readScalarArray(&tf.g, 7); }
553     if (!useDstGamut) { buffer.readScalarArray(&gamut.vals[0][0], 9); }
554     if (!useDstAT)    { at = buffer.read32LE(kLastEnum_SkAlphaType); }
555 
556     return SkColorFilterPriv::WithWorkingFormat(std::move(child),
557                                                 useDstTF    ? nullptr : &tf,
558                                                 useDstGamut ? nullptr : &gamut,
559                                                 useDstAT    ? nullptr : &at);
560 }
561 
WithWorkingFormat(sk_sp<SkColorFilter> child,const skcms_TransferFunction * tf,const skcms_Matrix3x3 * gamut,const SkAlphaType * at)562 sk_sp<SkColorFilter> SkColorFilterPriv::WithWorkingFormat(sk_sp<SkColorFilter> child,
563                                                           const skcms_TransferFunction* tf,
564                                                           const skcms_Matrix3x3* gamut,
565                                                           const SkAlphaType* at) {
566     return sk_make_sp<SkWorkingFormatColorFilter>(std::move(child), tf, gamut, at);
567 }
568 
569 ///////////////////////////////////////////////////////////////////////////////////////////////////
570 
Lerp(float weight,sk_sp<SkColorFilter> cf0,sk_sp<SkColorFilter> cf1)571 sk_sp<SkColorFilter> SkColorFilters::Lerp(float weight, sk_sp<SkColorFilter> cf0,
572                                                         sk_sp<SkColorFilter> cf1) {
573 #ifdef SK_ENABLE_SKSL
574     if (!cf0 && !cf1) {
575         return nullptr;
576     }
577     if (SkScalarIsNaN(weight)) {
578         return nullptr;
579     }
580 
581     if (cf0 == cf1) {
582         return cf0; // or cf1
583     }
584 
585     if (weight <= 0) {
586         return cf0;
587     }
588     if (weight >= 1) {
589         return cf1;
590     }
591 
592     static const SkRuntimeEffect* effect = SkMakeCachedRuntimeEffect(
593         SkRuntimeEffect::MakeForColorFilter,
594         "uniform colorFilter cf0;"
595         "uniform colorFilter cf1;"
596         "uniform half   weight;"
597         "half4 main(half4 color) {"
598             "return mix(cf0.eval(color), cf1.eval(color), weight);"
599         "}"
600     ).release();
601     SkASSERT(effect);
602 
603     sk_sp<SkColorFilter> inputs[] = {cf0,cf1};
604     return effect->makeColorFilter(SkData::MakeWithCopy(&weight, sizeof(weight)),
605                                    inputs, std::size(inputs));
606 #else
607     // TODO(skia:12197)
608     return nullptr;
609 #endif
610 }
611 
612 ///////////////////////////////////////////////////////////////////////////////////////////////////
613 
SkRegisterComposeColorFilterFlattenable()614 void SkRegisterComposeColorFilterFlattenable() {
615     SK_REGISTER_FLATTENABLE(SkComposeColorFilter);
616 }
617 
SkRegisterColorSpaceXformColorFilterFlattenable()618 void SkRegisterColorSpaceXformColorFilterFlattenable() {
619     SK_REGISTER_FLATTENABLE(ColorSpaceXformColorFilter);
620     // TODO(ccameron): Remove after grace period for SKPs to stop using old serialization.
621     SkFlattenable::Register("SkSRGBGammaColorFilter",
622                             ColorSpaceXformColorFilter::LegacyGammaOnlyCreateProc);
623 }
624 
SkRegisterWorkingFormatColorFilterFlattenable()625 void SkRegisterWorkingFormatColorFilterFlattenable() {
626     SK_REGISTER_FLATTENABLE(SkWorkingFormatColorFilter);
627 }
628