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