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