Index: src/gpu/effects/GrColorSpaceEffect.cpp |
diff --git a/src/gpu/effects/GrColorSpaceEffect.cpp b/src/gpu/effects/GrColorSpaceEffect.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c1f39b03048949fafdff85f2b31140d558d9497e |
--- /dev/null |
+++ b/src/gpu/effects/GrColorSpaceEffect.cpp |
@@ -0,0 +1,174 @@ |
+/* |
+ * Copyright 2016 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include "GrColorSpaceEffect.h" |
+ |
+#include "GrContext.h" |
+#include "GrCoordTransform.h" |
+#include "GrFragmentProcessor.h" |
+#include "GrInvariantOutput.h" |
+#include "GrProcessor.h" |
+#include "SkColorSpace.h" |
+#include "glsl/GrGLSLFragmentProcessor.h" |
+#include "glsl/GrGLSLFragmentShaderBuilder.h" |
+ |
+class GrGLColorSpaceEffect : public GrGLSLFragmentProcessor { |
+public: |
+ void emitCode(EmitArgs& args) override { |
+ const GrColorSpaceEffect& cse = args.fFp.cast<GrColorSpaceEffect>(); |
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
+ GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
+ |
+ const char* gammaUniName = nullptr; |
+ if (cse.manualDstGamma() && !cse.gammaIsSRGB()) { |
+ fGammaUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec3f_GrSLType, |
+ kDefault_GrSLPrecision, "Gamma", &gammaUniName); |
+ } |
+ |
+ GrGLSLShaderVar tmpVar("tmpColor", kVec4f_GrSLType, 0, kHigh_GrSLPrecision); |
+ SkString tmpDecl; |
+ tmpVar.appendDecl(args.fGLSLCaps, &tmpDecl); |
+ |
+ if (cse.manualDstGamma()) { |
+ SkString srgbFuncName; |
+ if (cse.gammaIsSRGB()) { |
+ static const GrGLSLShaderVar gSrgbArgs[] = { |
+ GrGLSLShaderVar("x", kFloat_GrSLType), |
+ }; |
+ |
+ fragBuilder->emitFunction(kFloat_GrSLType, |
+ "linear_to_srgb", |
+ SK_ARRAY_COUNT(gSrgbArgs), |
+ gSrgbArgs, |
+ "return (x <= 0.0031308f) ? (x * 12.92f) " |
+ ": (1.055f * pow(x, 0.416666667f) - 0.055f);", |
+ &srgbFuncName); |
+ } |
+ |
+ fragBuilder->codeAppendf("%s;", tmpDecl.c_str()); |
+ |
+ fragBuilder->codeAppendf("%s = ", tmpVar.c_str()); |
+ fragBuilder->appendTextureLookup(args.fTexSamplers[0], args.fCoords[0].c_str(), |
+ args.fCoords[0].getType()); |
+ fragBuilder->codeAppend(";"); |
+ |
+ if (cse.gammaIsSRGB()) { |
+ fragBuilder->codeAppendf("%s = vec4(%s(%s.r), %s(%s.g), %s(%s.b), %s.a);", |
+ args.fOutputColor, |
+ srgbFuncName.c_str(), tmpVar.c_str(), |
+ srgbFuncName.c_str(), tmpVar.c_str(), |
+ srgbFuncName.c_str(), tmpVar.c_str(), |
+ tmpVar.c_str()); |
+ } else { |
+ fragBuilder->codeAppendf("%s = vec4(pow(%s.rgb, %s.rgb), %s.a);", |
+ args.fOutputColor, tmpVar.c_str(), gammaUniName, |
+ tmpVar.c_str()); |
+ } |
+ } else { |
+ // Pass-through case: |
+ fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, tmpVar.c_str()); |
+ } |
+ } |
+ |
+ void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) { |
+ const GrColorSpaceEffect& cse = proc.cast<GrColorSpaceEffect>(); |
+ if (cse.manualDstGamma() && !cse.gammaIsSRGB()) { |
+ SkFloat3 gamma = cse.gamma(); |
+ pdman.set3fv(fGammaUni, 1, &gamma.fVec[0]); |
+ } |
+ } |
+ |
+ static inline void GenKey(const GrProcessor& processor, const GrGLSLCaps&, |
+ GrProcessorKeyBuilder* b) { |
+ const GrColorSpaceEffect& cse = processor.cast<GrColorSpaceEffect>(); |
+ uint32_t key = cse.manualDstGamma() ? 0x1 : 0x0; |
+ key |= cse.gammaIsSRGB() << 1; |
+ b->add32(key); |
+ } |
+ |
+private: |
+ GrGLSLProgramDataManager::UniformHandle fGammaUni; |
+ |
+ typedef GrGLSLFragmentProcessor INHERITED; |
+}; |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+static inline float invert_gamma(float x) { |
+ // TODO: What's the deal with kDevice_Named? (It has gamma == 0). |
+ return SkScalarNearlyZero(x) ? 1.0f : (1.0f / x); |
+} |
+ |
+GrColorSpaceEffect::GrColorSpaceEffect(GrTexture* texture, |
+ const SkColorSpace* srcColorSpace, |
+ const SkColorSpace* dstColorSpace, |
+ uint32_t colorSpaceOpsFlags) |
+ : INHERITED(texture, GrCoordTransform::MakeDivByTextureWHMatrix(texture)) |
+ , fManualDstGamma(SkToBool(colorSpaceOpsFlags & GrContext::kManualDstGamma_ColorSpaceOpsFlag)) { |
+ this->initClassID<GrColorSpaceEffect>(); |
+ |
+ fGamma = dstColorSpace->gamma(); |
+ |
+ // TODO: Add an isSRGB (or isSRGBGamma) on SkColorSpace |
+ fGammaIsSRGB = |
+ SkScalarNearlyEqual(fGamma.fVec[0], 2.2f) && |
+ SkScalarNearlyEqual(fGamma.fVec[1], 2.2f) && |
+ SkScalarNearlyEqual(fGamma.fVec[2], 2.2f); |
+ |
+ // Store inverse destination gamma, for use in shader |
+ fGamma.fVec[0] = invert_gamma(fGamma.fVec[0]); |
+ fGamma.fVec[1] = invert_gamma(fGamma.fVec[1]); |
+ fGamma.fVec[2] = invert_gamma(fGamma.fVec[2]); |
+ |
+ // TODO: Actually read out the color matrices, do the concat, inject that in the shader |
+ SkASSERT(srcColorSpace == dstColorSpace); |
+} |
+ |
+bool GrColorSpaceEffect::onIsEqual(const GrFragmentProcessor& s) const { |
+ const GrColorSpaceEffect& other = s.cast<GrColorSpaceEffect>(); |
+ // Conservative implemenation. Gamma doesn't matter if manual-gamma flag isn't set. |
+ return |
+ other.fManualDstGamma == fManualDstGamma && |
+ other.fGammaIsSRGB == fGammaIsSRGB && |
+ other.fGamma.fVec[0] == fGamma.fVec[0] && |
+ other.fGamma.fVec[1] == fGamma.fVec[1] && |
+ other.fGamma.fVec[2] == fGamma.fVec[2]; |
+} |
+ |
+void GrColorSpaceEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const { |
+ inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrColorSpaceEffect); |
+ |
+const GrFragmentProcessor* GrColorSpaceEffect::TestCreate(GrProcessorTestData* d) { |
+ sk_sp<SkColorSpace> srgb = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); |
+ bool doManualGamma = d->fRandom->nextBool(); |
+ uint32_t opsFlags = doManualGamma ? GrContext::kManualDstGamma_ColorSpaceOpsFlag : 0; |
+ return new GrColorSpaceEffect(d->fTextures[GrProcessorUnitTest::kSkiaPMTextureIdx], |
+ srgb.get(), srgb.get(), opsFlags); |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+void GrColorSpaceEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps, |
+ GrProcessorKeyBuilder* b) const { |
+ GrGLColorSpaceEffect::GenKey(*this, caps, b); |
+} |
+ |
+GrGLSLFragmentProcessor* GrColorSpaceEffect::onCreateGLSLInstance() const { |
+ return new GrGLColorSpaceEffect(); |
+} |
+ |
+const GrFragmentProcessor* GrColorSpaceEffect::Create(GrTexture* texture, |
+ const SkColorSpace* srcColorSpace, |
+ const SkColorSpace* dstColorSpace, |
+ uint32_t colorSpaceOpsFlags) { |
+ return new GrColorSpaceEffect(texture, srcColorSpace, dstColorSpace, colorSpaceOpsFlags); |
+} |