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 "glsl/GrGLSLFragmentProcessor.h" 12 #include "glsl/GrGLSLFragmentShaderBuilder.h" 13 14 class GrGLNonlinearColorSpaceXformEffect : public GrGLSLFragmentProcessor { 15 public: 16 void emitCode(EmitArgs& args) override { 17 const GrNonlinearColorSpaceXformEffect& csxe = 18 args.fFp.cast<GrNonlinearColorSpaceXformEffect>(); 19 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 20 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 21 22 const char* srcCoeffsName = nullptr; 23 if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kSrcTransfer_Op)) { 24 fSrcTransferFnUni = uniformHandler->addUniformArray( 25 kFragment_GrShaderFlag, kHalf_GrSLType, "SrcTransferFn", 26 GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs, &srcCoeffsName); 27 } 28 29 const char* dstCoeffsName = nullptr; 30 if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kDstTransfer_Op)) { 31 fDstTransferFnUni = uniformHandler->addUniformArray( 32 kFragment_GrShaderFlag, kHalf_GrSLType, "DstTransferFn", 33 GrNonlinearColorSpaceXformEffect::kNumTransferFnCoeffs, &dstCoeffsName); 34 } 35 36 const char* gamutXformName = nullptr; 37 if (SkToBool(csxe.ops() & GrNonlinearColorSpaceXformEffect::kGamutXform_Op)) { 38 fGamutXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4x4_GrSLType, 39 "GamutXform", &gamutXformName); 40 } 41 42 // Helper function to apply the src or dst transfer function to a single value 43 SkString tfFuncNames[2]; 44 for (size_t i = 0; i < 2; ++i) { 45 const char* coeffsName = i ? dstCoeffsName : srcCoeffsName; 46 if (!coeffsName) { 47 continue; 48 } 49 const char* fnName = i ? "dst_transfer_fn" : "src_transfer_fn"; 50 static const GrShaderVar gTransferFnFuncArgs[] = { 51 GrShaderVar("x", kHalf_GrSLType), 52 }; 53 SkString transferFnBody; 54 // Temporaries to make evaluation line readable 55 transferFnBody.printf("half A = %s[0];", coeffsName); 56 transferFnBody.appendf("half B = %s[1];", coeffsName); 57 transferFnBody.appendf("half C = %s[2];", coeffsName); 58 transferFnBody.appendf("half D = %s[3];", coeffsName); 59 transferFnBody.appendf("half E = %s[4];", coeffsName); 60 transferFnBody.appendf("half F = %s[5];", coeffsName); 61 transferFnBody.appendf("half G = %s[6];", coeffsName); 62 transferFnBody.append("half s = sign(x);"); 63 transferFnBody.append("x = abs(x);"); 64 transferFnBody.appendf("return s * ((x < D) ? (C * x) + F : pow(A * x + B, G) + E);"); 65 fragBuilder->emitFunction(kHalf_GrSLType, fnName, SK_ARRAY_COUNT(gTransferFnFuncArgs), 66 gTransferFnFuncArgs, transferFnBody.c_str(), &tfFuncNames[i]); 67 } 68 69 if (nullptr == args.fInputColor) { 70 args.fInputColor = "half4(1)"; 71 } 72 fragBuilder->codeAppendf("half4 color = %s;", args.fInputColor); 73 74 // 1: Un-premultiply the input color (if necessary) 75 fragBuilder->codeAppendf("half nonZeroAlpha = max(color.a, 0.00001);"); 76 fragBuilder->codeAppendf("color = half4(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);", tfFuncNames[0].c_str()); 81 fragBuilder->codeAppendf("color.g = %s(color.g);", tfFuncNames[0].c_str()); 82 fragBuilder->codeAppendf("color.b = %s(color.b);", tfFuncNames[0].c_str()); 83 } 84 85 // 3: Apply gamut matrix 86 if (gamutXformName) { 87 fragBuilder->codeAppendf( 88 "color.rgb = (%s * half4(color.rgb, 1.0)).rgb;", gamutXformName); 89 } 90 91 // 4: Apply dst transfer fn 92 if (dstCoeffsName) { 93 fragBuilder->codeAppendf("color.r = %s(color.r);", tfFuncNames[1].c_str()); 94 fragBuilder->codeAppendf("color.g = %s(color.g);", tfFuncNames[1].c_str()); 95 fragBuilder->codeAppendf("color.b = %s(color.b);", tfFuncNames[1].c_str()); 96 } 97 98 // 5: Premultiply again 99 fragBuilder->codeAppendf("%s = half4(color.rgb * color.a, color.a);", args.fOutputColor); 100 } 101 102 static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&, 103 GrProcessorKeyBuilder* b) { 104 const GrNonlinearColorSpaceXformEffect& csxe = 105 processor.cast<GrNonlinearColorSpaceXformEffect>(); 106 b->add32(csxe.ops()); 107 } 108 109 protected: 110 void onSetData(const GrGLSLProgramDataManager& pdman, 111 const GrFragmentProcessor& 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 137 GrNonlinearColorSpaceXformEffect::GrNonlinearColorSpaceXformEffect( 138 uint32_t ops, const SkColorSpaceTransferFn& srcTransferFn, 139 const SkColorSpaceTransferFn& dstTransferFn, const SkMatrix44& gamutXform) 140 : INHERITED(kGrNonlinearColorSpaceXformEffect_ClassID, 141 kPreservesOpaqueInput_OptimizationFlag) 142 , fGamutXform(gamutXform) 143 , fOps(ops) { 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 162 GrNonlinearColorSpaceXformEffect::GrNonlinearColorSpaceXformEffect( 163 const GrNonlinearColorSpaceXformEffect& that) 164 : INHERITED(kGrNonlinearColorSpaceXformEffect_ClassID, 165 kPreservesOpaqueInput_OptimizationFlag) 166 , fGamutXform(that.fGamutXform) 167 , fOps(that.fOps) { 168 memcpy(fSrcTransferFnCoeffs, that.fSrcTransferFnCoeffs, sizeof(fSrcTransferFnCoeffs)); 169 memcpy(fDstTransferFnCoeffs, that.fDstTransferFnCoeffs, sizeof(fDstTransferFnCoeffs)); 170 } 171 172 std::unique_ptr<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::clone() const { 173 return std::unique_ptr<GrFragmentProcessor>(new GrNonlinearColorSpaceXformEffect(*this)); 174 } 175 176 bool GrNonlinearColorSpaceXformEffect::onIsEqual(const GrFragmentProcessor& s) const { 177 const GrNonlinearColorSpaceXformEffect& other = s.cast<GrNonlinearColorSpaceXformEffect>(); 178 if (other.fOps != fOps) { 179 return false; 180 } 181 if (SkToBool(fOps & kSrcTransfer_Op) && 182 memcmp(&other.fSrcTransferFnCoeffs, &fSrcTransferFnCoeffs, sizeof(fSrcTransferFnCoeffs))) { 183 return false; 184 } 185 if (SkToBool(fOps & kDstTransfer_Op) && 186 memcmp(&other.fDstTransferFnCoeffs, &fDstTransferFnCoeffs, sizeof(fDstTransferFnCoeffs))) { 187 return false; 188 } 189 if (SkToBool(fOps & kGamutXform_Op) && other.fGamutXform != fGamutXform) { 190 return false; 191 } 192 return true; 193 } 194 195 /////////////////////////////////////////////////////////////////////////////// 196 197 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrNonlinearColorSpaceXformEffect); 198 199 #if GR_TEST_UTILS 200 std::unique_ptr<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::TestCreate( 201 GrProcessorTestData* d) { 202 // TODO: Generate a random variety of color spaces for this effect (it can handle wacky 203 // transfer functions, etc...) 204 sk_sp<SkColorSpace> srcSpace = SkColorSpace::MakeSRGBLinear(); 205 sk_sp<SkColorSpace> dstSpace = SkColorSpace::MakeSRGB(); 206 return GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), dstSpace.get()); 207 } 208 #endif 209 210 /////////////////////////////////////////////////////////////////////////////// 211 212 void GrNonlinearColorSpaceXformEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, 213 GrProcessorKeyBuilder* b) const { 214 GrGLNonlinearColorSpaceXformEffect::GenKey(*this, caps, b); 215 } 216 217 GrGLSLFragmentProcessor* GrNonlinearColorSpaceXformEffect::onCreateGLSLInstance() const { 218 return new GrGLNonlinearColorSpaceXformEffect(); 219 } 220 221 std::unique_ptr<GrFragmentProcessor> GrNonlinearColorSpaceXformEffect::Make( 222 const SkColorSpace* src, const SkColorSpace* dst) { 223 if (!src || !dst || SkColorSpace::Equals(src, dst)) { 224 // No conversion possible (or necessary) 225 return nullptr; 226 } 227 228 uint32_t ops = 0; 229 230 // We rely on GrColorSpaceXform to build the gamut xform matrix for us (to get caching) 231 auto gamutXform = GrColorSpaceXform::MakeGamutXform(src, dst); 232 SkMatrix44 srcToDstMtx(SkMatrix44::kUninitialized_Constructor); 233 if (gamutXform) { 234 ops |= kGamutXform_Op; 235 srcToDstMtx = gamutXform->gamutXform(); 236 } 237 238 SkColorSpaceTransferFn srcTransferFn; 239 if (!src->gammaIsLinear()) { 240 if (src->isNumericalTransferFn(&srcTransferFn)) { 241 ops |= kSrcTransfer_Op; 242 } else { 243 return nullptr; 244 } 245 } 246 247 SkColorSpaceTransferFn dstTransferFn; 248 if (!dst->gammaIsLinear()) { 249 if (dst->isNumericalTransferFn(&dstTransferFn)) { 250 dstTransferFn = dstTransferFn.invert(); 251 ops |= kDstTransfer_Op; 252 } else { 253 return nullptr; 254 } 255 } 256 257 return std::unique_ptr<GrFragmentProcessor>( 258 new GrNonlinearColorSpaceXformEffect(ops, srcTransferFn, dstTransferFn, srcToDstMtx)); 259 } 260