1 /*
2 * Copyright 2023 Google LLC
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 "src/effects/colorfilters/SkWorkingFormatColorFilter.h"
9
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkColorType.h"
13 #include "include/core/SkImageInfo.h"
14 #include "include/core/SkRefCnt.h"
15 #include "include/private/base/SkAssert.h"
16 #include "modules/skcms/skcms.h"
17 #include "src/base/SkArenaAlloc.h"
18 #include "src/core/SkColorFilterPriv.h"
19 #include "src/core/SkColorSpaceXformSteps.h"
20 #include "src/core/SkEffectPriv.h"
21 #include "src/core/SkReadBuffer.h"
22 #include "src/core/SkWriteBuffer.h"
23 #include "src/effects/colorfilters/SkColorFilterBase.h"
24
25 #include <utility>
26
SkWorkingFormatColorFilter(sk_sp<SkColorFilter> child,const skcms_TransferFunction * tf,const skcms_Matrix3x3 * gamut,const SkAlphaType * at)27 SkWorkingFormatColorFilter::SkWorkingFormatColorFilter(sk_sp<SkColorFilter> child,
28 const skcms_TransferFunction* tf,
29 const skcms_Matrix3x3* gamut,
30 const SkAlphaType* at) {
31 SkASSERT(child);
32 fChild = std::move(child);
33 if (tf) {
34 fTF = *tf;
35 fUseDstTF = false;
36 }
37 if (gamut) {
38 fGamut = *gamut;
39 fUseDstGamut = false;
40 }
41 if (at) {
42 fAT = *at;
43 fUseDstAT = false;
44 }
45 }
46
workingFormat(const sk_sp<SkColorSpace> & dstCS,SkAlphaType * at) const47 sk_sp<SkColorSpace> SkWorkingFormatColorFilter::workingFormat(const sk_sp<SkColorSpace>& dstCS,
48 SkAlphaType* at) const {
49 skcms_TransferFunction tf = fTF;
50 skcms_Matrix3x3 gamut = fGamut;
51
52 if (fUseDstTF) {
53 SkAssertResult(dstCS->isNumericalTransferFn(&tf));
54 }
55 if (fUseDstGamut) {
56 SkAssertResult(dstCS->toXYZD50(&gamut));
57 }
58
59 *at = fUseDstAT ? kPremul_SkAlphaType : fAT;
60 return SkColorSpace::MakeRGB(tf, gamut);
61 }
62
appendStages(const SkStageRec & rec,bool shaderIsOpaque) const63 bool SkWorkingFormatColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
64 sk_sp<SkColorSpace> dstCS = sk_ref_sp(rec.fDstCS);
65
66 if (!dstCS) {
67 dstCS = SkColorSpace::MakeSRGB();
68 }
69
70 SkAlphaType workingAT;
71 sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
72
73 SkColorInfo dst = {rec.fDstColorType, kPremul_SkAlphaType, dstCS},
74 working = {rec.fDstColorType, workingAT, workingCS};
75
76 const auto* dstToWorking = rec.fAlloc->make<SkColorSpaceXformSteps>(dst, working);
77 const auto* workingToDst = rec.fAlloc->make<SkColorSpaceXformSteps>(working, dst);
78
79 // The paint color is in the destination color space, so *should* be coverted to working space.
80 // That's not necessary, though:
81 // - Tinting alpha-only image shaders is the only effect that uses paint-color
82 // - Alpha-only image shaders can't be reached from color-filters without SkSL
83 // - SkSL disables paint-color tinting of alpha-only image shaders
84
85 SkStageRec workingRec = {rec.fPipeline,
86 rec.fAlloc,
87 rec.fDstColorType,
88 workingCS.get(),
89 rec.fPaintColor,
90 rec.fSurfaceProps};
91
92 dstToWorking->apply(rec.fPipeline);
93 if (!as_CFB(fChild)->appendStages(workingRec, shaderIsOpaque)) {
94 return false;
95 }
96 workingToDst->apply(rec.fPipeline);
97 return true;
98 }
99
onFilterColor4f(const SkPMColor4f & origColor,SkColorSpace * rawDstCS) const100 SkPMColor4f SkWorkingFormatColorFilter::onFilterColor4f(const SkPMColor4f& origColor,
101 SkColorSpace* rawDstCS) const {
102 sk_sp<SkColorSpace> dstCS = sk_ref_sp(rawDstCS);
103 if (!dstCS) {
104 dstCS = SkColorSpace::MakeSRGB();
105 }
106
107 SkAlphaType workingAT;
108 sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
109
110 SkColorInfo dst = {kUnknown_SkColorType, kPremul_SkAlphaType, dstCS},
111 working = {kUnknown_SkColorType, workingAT, workingCS};
112
113 SkPMColor4f color = origColor;
114 SkColorSpaceXformSteps{dst, working}.apply(color.vec());
115 color = as_CFB(fChild)->onFilterColor4f(color, working.colorSpace());
116 SkColorSpaceXformSteps{working, dst}.apply(color.vec());
117 return color;
118 }
119
onIsAlphaUnchanged() const120 bool SkWorkingFormatColorFilter::onIsAlphaUnchanged() const { return fChild->isAlphaUnchanged(); }
121
onAsAColorMode(SkColor * color,SkBlendMode * mode) const122 bool SkWorkingFormatColorFilter::onAsAColorMode(SkColor* color, SkBlendMode* mode) const {
123 return fChild->asAColorMode(color, mode);
124 }
125
onAsAColorMatrix(float matrix[20]) const126 bool SkWorkingFormatColorFilter::onAsAColorMatrix(float matrix[20]) const {
127 return fChild->asAColorMatrix(matrix);
128 }
129
flatten(SkWriteBuffer & buffer) const130 void SkWorkingFormatColorFilter::flatten(SkWriteBuffer& buffer) const {
131 buffer.writeFlattenable(fChild.get());
132 buffer.writeBool(fUseDstTF);
133 buffer.writeBool(fUseDstGamut);
134 buffer.writeBool(fUseDstAT);
135 if (!fUseDstTF) {
136 buffer.writeScalarArray(&fTF.g, 7);
137 }
138 if (!fUseDstGamut) {
139 buffer.writeScalarArray(&fGamut.vals[0][0], 9);
140 }
141 if (!fUseDstAT) {
142 buffer.writeInt(fAT);
143 }
144 }
145
CreateProc(SkReadBuffer & buffer)146 sk_sp<SkFlattenable> SkWorkingFormatColorFilter::CreateProc(SkReadBuffer& buffer) {
147 sk_sp<SkColorFilter> child = buffer.readColorFilter();
148 bool useDstTF = buffer.readBool(), useDstGamut = buffer.readBool(),
149 useDstAT = buffer.readBool();
150
151 skcms_TransferFunction tf;
152 skcms_Matrix3x3 gamut;
153 SkAlphaType at;
154
155 if (!useDstTF) {
156 buffer.readScalarArray(&tf.g, 7);
157 }
158 if (!useDstGamut) {
159 buffer.readScalarArray(&gamut.vals[0][0], 9);
160 }
161 if (!useDstAT) {
162 at = buffer.read32LE(kLastEnum_SkAlphaType);
163 }
164
165 return SkColorFilterPriv::WithWorkingFormat(std::move(child),
166 useDstTF ? nullptr : &tf,
167 useDstGamut ? nullptr : &gamut,
168 useDstAT ? nullptr : &at);
169 }
170
WithWorkingFormat(sk_sp<SkColorFilter> child,const skcms_TransferFunction * tf,const skcms_Matrix3x3 * gamut,const SkAlphaType * at)171 sk_sp<SkColorFilter> SkColorFilterPriv::WithWorkingFormat(sk_sp<SkColorFilter> child,
172 const skcms_TransferFunction* tf,
173 const skcms_Matrix3x3* gamut,
174 const SkAlphaType* at) {
175 if (!child) {
176 // This color filter applies a conversion from the 'dst' color space to the working format,
177 // invokes the child, and then converts back to 'dst'. If `child` is null, it is the
178 // identity color filter, so the conversion from 'dst' to working format and back to 'dst'
179 // is also the identity.
180 return nullptr;
181 }
182 return sk_make_sp<SkWorkingFormatColorFilter>(std::move(child), tf, gamut, at);
183 }
184
SkRegisterWorkingFormatColorFilterFlattenable()185 void SkRegisterWorkingFormatColorFilterFlattenable() {
186 SK_REGISTER_FLATTENABLE(SkWorkingFormatColorFilter);
187 }
188