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 fChild = std::move(child);
32 if (tf) {
33 fTF = *tf;
34 fUseDstTF = false;
35 }
36 if (gamut) {
37 fGamut = *gamut;
38 fUseDstGamut = false;
39 }
40 if (at) {
41 fAT = *at;
42 fUseDstAT = false;
43 }
44 }
45
workingFormat(const sk_sp<SkColorSpace> & dstCS,SkAlphaType * at) const46 sk_sp<SkColorSpace> SkWorkingFormatColorFilter::workingFormat(const sk_sp<SkColorSpace>& dstCS,
47 SkAlphaType* at) const {
48 skcms_TransferFunction tf = fTF;
49 skcms_Matrix3x3 gamut = fGamut;
50
51 if (fUseDstTF) {
52 SkAssertResult(dstCS->isNumericalTransferFn(&tf));
53 }
54 if (fUseDstGamut) {
55 SkAssertResult(dstCS->toXYZD50(&gamut));
56 }
57
58 *at = fUseDstAT ? kPremul_SkAlphaType : fAT;
59 return SkColorSpace::MakeRGB(tf, gamut);
60 }
61
appendStages(const SkStageRec & rec,bool shaderIsOpaque) const62 bool SkWorkingFormatColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
63 sk_sp<SkColorSpace> dstCS = sk_ref_sp(rec.fDstCS);
64
65 if (!dstCS) {
66 dstCS = SkColorSpace::MakeSRGB();
67 }
68
69 SkAlphaType workingAT;
70 sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
71
72 SkColorInfo dst = {rec.fDstColorType, kPremul_SkAlphaType, dstCS},
73 working = {rec.fDstColorType, workingAT, workingCS};
74
75 const auto* dstToWorking = rec.fAlloc->make<SkColorSpaceXformSteps>(dst, working);
76 const auto* workingToDst = rec.fAlloc->make<SkColorSpaceXformSteps>(working, dst);
77
78 // The paint color is in the destination color space, so *should* be coverted to working space.
79 // That's not necessary, though:
80 // - Tinting alpha-only image shaders is the only effect that uses paint-color
81 // - Alpha-only image shaders can't be reached from color-filters without SkSL
82 // - SkSL disables paint-color tinting of alpha-only image shaders
83
84 SkStageRec workingRec = {rec.fPipeline,
85 rec.fAlloc,
86 rec.fDstColorType,
87 workingCS.get(),
88 rec.fPaintColor,
89 rec.fSurfaceProps};
90
91 dstToWorking->apply(rec.fPipeline);
92 if (!as_CFB(fChild)->appendStages(workingRec, shaderIsOpaque)) {
93 return false;
94 }
95 workingToDst->apply(rec.fPipeline);
96 return true;
97 }
98
onFilterColor4f(const SkPMColor4f & origColor,SkColorSpace * rawDstCS) const99 SkPMColor4f SkWorkingFormatColorFilter::onFilterColor4f(const SkPMColor4f& origColor,
100 SkColorSpace* rawDstCS) const {
101 sk_sp<SkColorSpace> dstCS = sk_ref_sp(rawDstCS);
102 if (!dstCS) {
103 dstCS = SkColorSpace::MakeSRGB();
104 }
105
106 SkAlphaType workingAT;
107 sk_sp<SkColorSpace> workingCS = this->workingFormat(dstCS, &workingAT);
108
109 SkColorInfo dst = {kUnknown_SkColorType, kPremul_SkAlphaType, dstCS},
110 working = {kUnknown_SkColorType, workingAT, workingCS};
111
112 SkPMColor4f color = origColor;
113 SkColorSpaceXformSteps{dst, working}.apply(color.vec());
114 color = as_CFB(fChild)->onFilterColor4f(color, working.colorSpace());
115 SkColorSpaceXformSteps{working, dst}.apply(color.vec());
116 return color;
117 }
118
onIsAlphaUnchanged() const119 bool SkWorkingFormatColorFilter::onIsAlphaUnchanged() const { return fChild->isAlphaUnchanged(); }
120
flatten(SkWriteBuffer & buffer) const121 void SkWorkingFormatColorFilter::flatten(SkWriteBuffer& buffer) const {
122 buffer.writeFlattenable(fChild.get());
123 buffer.writeBool(fUseDstTF);
124 buffer.writeBool(fUseDstGamut);
125 buffer.writeBool(fUseDstAT);
126 if (!fUseDstTF) {
127 buffer.writeScalarArray(&fTF.g, 7);
128 }
129 if (!fUseDstGamut) {
130 buffer.writeScalarArray(&fGamut.vals[0][0], 9);
131 }
132 if (!fUseDstAT) {
133 buffer.writeInt(fAT);
134 }
135 }
136
CreateProc(SkReadBuffer & buffer)137 sk_sp<SkFlattenable> SkWorkingFormatColorFilter::CreateProc(SkReadBuffer& buffer) {
138 sk_sp<SkColorFilter> child = buffer.readColorFilter();
139 bool useDstTF = buffer.readBool(), useDstGamut = buffer.readBool(),
140 useDstAT = buffer.readBool();
141
142 skcms_TransferFunction tf;
143 skcms_Matrix3x3 gamut;
144 SkAlphaType at;
145
146 if (!useDstTF) {
147 buffer.readScalarArray(&tf.g, 7);
148 }
149 if (!useDstGamut) {
150 buffer.readScalarArray(&gamut.vals[0][0], 9);
151 }
152 if (!useDstAT) {
153 at = buffer.read32LE(kLastEnum_SkAlphaType);
154 }
155
156 return SkColorFilterPriv::WithWorkingFormat(std::move(child),
157 useDstTF ? nullptr : &tf,
158 useDstGamut ? nullptr : &gamut,
159 useDstAT ? nullptr : &at);
160 }
161
WithWorkingFormat(sk_sp<SkColorFilter> child,const skcms_TransferFunction * tf,const skcms_Matrix3x3 * gamut,const SkAlphaType * at)162 sk_sp<SkColorFilter> SkColorFilterPriv::WithWorkingFormat(sk_sp<SkColorFilter> child,
163 const skcms_TransferFunction* tf,
164 const skcms_Matrix3x3* gamut,
165 const SkAlphaType* at) {
166 return sk_make_sp<SkWorkingFormatColorFilter>(std::move(child), tf, gamut, at);
167 }
168
SkRegisterWorkingFormatColorFilterFlattenable()169 void SkRegisterWorkingFormatColorFilterFlattenable() {
170 SK_REGISTER_FLATTENABLE(SkWorkingFormatColorFilter);
171 }
172