xref: /aosp_15_r20/external/skia/src/effects/colorfilters/SkWorkingFormatColorFilter.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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