1 /*
2 * Copyright 2017 Google Inc.
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 "GrNonlinearColorSpaceXformEffect.h"
9
10 #include "GrProcessor.h"
11 #include "glsl/GrGLSLFragmentProcessor.h"
12 #include "glsl/GrGLSLFragmentShaderBuilder.h"
13
14 #include "SkColorSpace_Base.h"
15
16 class GrGLNonlinearColorSpaceXformEffect : public GrGLSLFragmentProcessor {
17 public:
emitCode(EmitArgs & args)18 void emitCode(EmitArgs& args) override {
19 const GrNonlinearColorSpaceXformEffect& csxe =
20 args.fFp.cast<GrNonlinearColorSpaceXformEffect>();
21 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
22 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
23
24 const char* srcCoeffsName = nullptr;
25 if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kSrcTransfer_Op)) {
26 fSrcTransferFnUni = uniformHandler->addUniformArray(
27 kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision,
28 "SrcTransferFn", GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
29 &srcCoeffsName);
30 }
31
32 const char* dstCoeffsName = nullptr;
33 if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kDstTransfer_Op)) {
34 fDstTransferFnUni = uniformHandler->addUniformArray(
35 kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision,
36 "DstTransferFn", GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
37 &dstCoeffsName);
38 }
39
40 const char* gamutXformName = nullptr;
41 if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kGamutXform_Op)) {
42 fGamutXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kMat44f_GrSLType,
43 kDefault_GrSLPrecision, "GamutXform",
44 &gamutXformName);
45 }
46
47 // Helper function to apply a transfer function to a single value
48 SkString tfFuncNameString;
49 static const GrShaderVar gTransferFnFuncArgs[] = {
50 GrShaderVar("x", kFloat_GrSLType),
51 GrShaderVar("coeffs", kFloat_GrSLType,
52 GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs),
53 };
54 SkString transferFnBody;
55 // Temporaries to make evaluation line readable
56 transferFnBody.printf("float A = coeffs[0];");
57 transferFnBody.append("float B = coeffs[1];");
58 transferFnBody.append("float C = coeffs[2];");
59 transferFnBody.append("float D = coeffs[3];");
60 transferFnBody.append("float E = coeffs[4];");
61 transferFnBody.append("float F = coeffs[5];");
62 transferFnBody.append("float G = coeffs[6];");
63 transferFnBody.appendf("return (x < D) ? (C * x) + F : pow(A * x + B, G) + E;");
64 fragBuilder->emitFunction(kFloat_GrSLType, "transfer_fn",
65 SK_ARRAY_COUNT(gTransferFnFuncArgs), gTransferFnFuncArgs,
66 transferFnBody.c_str(), &tfFuncNameString);
67 const char* tfFuncName = tfFuncNameString.c_str();
68
69 if (nullptr == args.fInputColor) {
70 args.fInputColor = "vec4(1)";
71 }
72 fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
73
74 // 1: Un-premultiply the input color (if necessary)
75 fragBuilder->codeAppendf("float nonZeroAlpha = max(color.a, 0.00001);");
76 fragBuilder->codeAppendf("color = vec4(color.rgb / nonZeroAlpha, nonZeroAlpha);");
77
78 // 2: Apply src transfer function (to get to linear RGB)
79 if (srcCoeffsName) {
80 fragBuilder->codeAppendf("color.r = %s(color.r, %s);", tfFuncName, srcCoeffsName);
81 fragBuilder->codeAppendf("color.g = %s(color.g, %s);", tfFuncName, srcCoeffsName);
82 fragBuilder->codeAppendf("color.b = %s(color.b, %s);", tfFuncName, srcCoeffsName);
83 }
84
85 // 3: Apply gamut matrix
86 if (gamutXformName) {
87 // Color is unpremultiplied at this point, so clamp to [0, 1]
88 fragBuilder->codeAppendf(
89 "color.rgb = clamp((%s * vec4(color.rgb, 1.0)).rgb, 0.0, 1.0);", gamutXformName);
90 }
91
92 // 4: Apply dst transfer fn
93 if (dstCoeffsName) {
94 fragBuilder->codeAppendf("color.r = %s(color.r, %s);", tfFuncName, dstCoeffsName);
95 fragBuilder->codeAppendf("color.g = %s(color.g, %s);", tfFuncName, dstCoeffsName);
96 fragBuilder->codeAppendf("color.b = %s(color.b, %s);", tfFuncName, dstCoeffsName);
97 }
98
99 // 5: Premultiply again
100 fragBuilder->codeAppendf("%s = vec4(color.rgb * color.a, color.a);", args.fOutputColor);
101 }
102
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)103 static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&,
104 GrProcessorKeyBuilder* b) {
105 const GrNonlinearColorSpaceXformEffect& csxe =
106 processor.cast<GrNonlinearColorSpaceXformEffect>();
107 b->add32(csxe.ops());
108 }
109
110 protected:
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)111 void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& processor) override {
112 const GrNonlinearColorSpaceXformEffect& csxe =
113 processor.cast<GrNonlinearColorSpaceXformEffect>();
114 if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kSrcTransfer_Op)) {
115 pdman.set1fv(fSrcTransferFnUni, GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
116 csxe.srcTransferFnCoeffs());
117 }
118 if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kDstTransfer_Op)) {
119 pdman.set1fv(fDstTransferFnUni, GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs,
120 csxe.dstTransferFnCoeffs());
121 }
122 if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kGamutXform_Op)) {
123 pdman.setSkMatrix44(fGamutXformUni, csxe.gamutXform());
124 }
125 }
126
127 private:
128 GrGLSLProgramDataManager::UniformHandle fSrcTransferFnUni;
129 GrGLSLProgramDataManager::UniformHandle fDstTransferFnUni;
130 GrGLSLProgramDataManager::UniformHandle fGamutXformUni;
131
132 typedef GrGLSLFragmentProcessor INHERITED;
133 };
134
135 ///////////////////////////////////////////////////////////////////////////////
136
GrNonlinearColorSpaceXformEffect(uint32_t ops,const SkColorSpaceTransferFn & srcTransferFn,const SkColorSpaceTransferFn & dstTransferFn,const SkMatrix44 & gamutXform)137 GrNonlinearColorSpaceXformEffect::GrNonlinearColorSpaceXformEffect(
138 uint32_t ops, const SkColorSpaceTransferFn& srcTransferFn,
139 const SkColorSpaceTransferFn& dstTransferFn, const SkMatrix44& gamutXform)
140 : INHERITED(kPreservesOpaqueInput_OptimizationFlag)
141 , fGamutXform(gamutXform)
142 , fOps(ops) {
143 this->initClassID<GrNonlinearColorSpaceXformEffect>();
144
145 fSrcTransferFnCoeffs[0] = srcTransferFn.fA;
146 fSrcTransferFnCoeffs[1] = srcTransferFn.fB;
147 fSrcTransferFnCoeffs[2] = srcTransferFn.fC;
148 fSrcTransferFnCoeffs[3] = srcTransferFn.fD;
149 fSrcTransferFnCoeffs[4] = srcTransferFn.fE;
150 fSrcTransferFnCoeffs[5] = srcTransferFn.fF;
151 fSrcTransferFnCoeffs[6] = srcTransferFn.fG;
152
153 fDstTransferFnCoeffs[0] = dstTransferFn.fA;
154 fDstTransferFnCoeffs[1] = dstTransferFn.fB;
155 fDstTransferFnCoeffs[2] = dstTransferFn.fC;
156 fDstTransferFnCoeffs[3] = dstTransferFn.fD;
157 fDstTransferFnCoeffs[4] = dstTransferFn.fE;
158 fDstTransferFnCoeffs[5] = dstTransferFn.fF;
159 fDstTransferFnCoeffs[6] = dstTransferFn.fG;
160 }
161
onIsEqual(const GrFragmentProcessor & s) const162 bool GrNonlinearColorSpaceXformEffect::onIsEqual(const GrFragmentProcessor& s) const {
163 const GrNonlinearColorSpaceXformEffect& other = s.cast<GrNonlinearColorSpaceXformEffect>();
164 if (other.fOps != fOps) {
165 return false;
166 }
167 if (SkToBool(fOps & kSrcTransfer_Op) &&
168 memcmp(&other.fSrcTransferFnCoeffs, &fSrcTransferFnCoeffs, sizeof(fSrcTransferFnCoeffs))) {
169 return false;
170 }
171 if (SkToBool(fOps & kDstTransfer_Op) &&
172 memcmp(&other.fDstTransferFnCoeffs, &fDstTransferFnCoeffs, sizeof(fDstTransferFnCoeffs))) {
173 return false;
174 }
175 if (SkToBool(fOps & kGamutXform_Op) && other.fGamutXform != fGamutXform) {
176 return false;
177 }
178 return true;
179 }
180
181 ///////////////////////////////////////////////////////////////////////////////
182
183 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrNonlinearColorSpaceXformEffect);
184
185 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)186 sk_sp<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::TestCreate(GrProcessorTestData* d) {
187 // TODO: Generate a random variety of color spaces for this effect (it can handle wacky
188 // transfer functions, etc...)
189 sk_sp<SkColorSpace> srcSpace = SkColorSpace::MakeSRGBLinear();
190 sk_sp<SkColorSpace> dstSpace = SkColorSpace::MakeSRGB();
191 return GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), dstSpace.get());
192 }
193 #endif
194
195 ///////////////////////////////////////////////////////////////////////////////
196
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const197 void GrNonlinearColorSpaceXformEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
198 GrProcessorKeyBuilder* b) const {
199 GrGLNonlinearColorSpaceXformEffect::GenKey(*this, caps, b);
200 }
201
onCreateGLSLInstance() const202 GrGLSLFragmentProcessor* GrNonlinearColorSpaceXformEffect::onCreateGLSLInstance() const {
203 return new GrGLNonlinearColorSpaceXformEffect();
204 }
205
Make(const SkColorSpace * src,const SkColorSpace * dst)206 sk_sp<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::Make(const SkColorSpace* src,
207 const SkColorSpace* dst) {
208 if (!src || !dst || SkColorSpace::Equals(src, dst)) {
209 // No conversion possible (or necessary)
210 return nullptr;
211 }
212
213 uint32_t ops = 0;
214
215 // We rely on GrColorSpaceXform to build the gamut xform matrix for us (to get caching)
216 auto gamutXform = GrColorSpaceXform::Make(src, dst);
217 SkMatrix44 srcToDstMtx(SkMatrix44::kUninitialized_Constructor);
218 if (gamutXform) {
219 ops |= kGamutXform_Op;
220 srcToDstMtx = gamutXform->srcToDst();
221 }
222
223 SkColorSpaceTransferFn srcTransferFn;
224 if (!src->gammaIsLinear()) {
225 if (src->isNumericalTransferFn(&srcTransferFn)) {
226 ops |= kSrcTransfer_Op;
227 } else {
228 return nullptr;
229 }
230 }
231
232 SkColorSpaceTransferFn dstTransferFn;
233 if (!dst->gammaIsLinear()) {
234 if (dst->isNumericalTransferFn(&dstTransferFn)) {
235 dstTransferFn = dstTransferFn.invert();
236 ops |= kDstTransfer_Op;
237 } else {
238 return nullptr;
239 }
240 }
241
242 return sk_sp<GrFragmentProcessor>(new GrNonlinearColorSpaceXformEffect(
243 ops, srcTransferFn, dstTransferFn, srcToDstMtx));
244 }
245