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