• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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