Make GrGammaEffect have explicit sRGB modes, plus exponential mode.
authorbrianosman <brianosman@google.com>
Wed, 11 May 2016 13:49:32 +0000 (06:49 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 11 May 2016 13:49:32 +0000 (06:49 -0700)
Convert it to a "standard" FP that just transforms the input color.

For now, we still infer the sRGB transfer curves from the exponent,
but I'm hoping that eventually SkGammas will provide us with the
exact curve we're supposed to be applying. In any case, this adds
support for doing the inverse transformation, as well, which will
be needed in an upcoming Vulkan YUV change, among other things.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1954863002

Committed: https://skia.googlesource.com/skia/+/2139303e4c0a9cbcfac695977a80eb026a9296ab

Review-Url: https://codereview.chromium.org/1954863002

src/gpu/GrContext.cpp
src/gpu/effects/GrGammaEffect.cpp
src/gpu/effects/GrGammaEffect.h

index 443494073af596a9ac0f9fee05d49be897d7478c..4235ae85524ab36d9ffc4426d0e00a3d4548aa33 100644 (file)
@@ -550,12 +550,9 @@ bool GrContext::applyGamma(GrRenderTarget* dst, GrTexture* src, SkScalar gamma){
     }
 
     GrPaint paint;
-    if (SkScalarNearlyEqual(gamma, 1.0f)) {
-        paint.addColorTextureProcessor(src, GrCoordTransform::MakeDivByTextureWHMatrix(src));
-    } else {
-        SkAutoTUnref<const GrFragmentProcessor> fp;
-        fp.reset(GrGammaEffect::Create(src, gamma));
-        paint.addColorFragmentProcessor(fp);
+    paint.addColorTextureProcessor(src, GrCoordTransform::MakeDivByTextureWHMatrix(src));
+    if (!SkScalarNearlyEqual(gamma, 1.0f)) {
+        paint.addColorFragmentProcessor(GrGammaEffect::Create(gamma))->unref();
     }
     paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
     paint.setGammaCorrect(true);
index b2aa48aad2dcee5c772d419c99863886a2c3ebdf..71d8c914238fa972906e6acd4aeb16c490bf54f6 100644 (file)
@@ -23,54 +23,59 @@ public:
         GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
 
         const char* gammaUniName = nullptr;
-        if (!ge.gammaIsSRGB()) {
+        if (GrGammaEffect::Mode::kExponential == ge.mode()) {
             fGammaUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
                                                    kDefault_GrSLPrecision, "Gamma", &gammaUniName);
         }
 
-        GrGLSLShaderVar tmpVar("tmpColor", kVec4f_GrSLType, 0, kHigh_GrSLPrecision);
-        SkString tmpDecl;
-        tmpVar.appendDecl(args.fGLSLCaps, &tmpDecl);
-
         SkString srgbFuncName;
-        if (ge.gammaIsSRGB()) {
-            static const GrGLSLShaderVar gSrgbArgs[] = {
-                GrGLSLShaderVar("x", kFloat_GrSLType),
-            };
-
-            fragBuilder->emitFunction(kFloat_GrSLType,
-                                        "linear_to_srgb",
-                                        SK_ARRAY_COUNT(gSrgbArgs),
-                                        gSrgbArgs,
-                                        "return (x <= 0.0031308) ? (x * 12.92) "
-                                        ": (1.055 * pow(x, 0.416666667) - 0.055);",
-                                        &srgbFuncName);
+        static const GrGLSLShaderVar gSrgbArgs[] = {
+            GrGLSLShaderVar("x", kFloat_GrSLType),
+        };
+        switch (ge.mode()) {
+            case GrGammaEffect::Mode::kLinearToSRGB:
+                fragBuilder->emitFunction(kFloat_GrSLType,
+                                          "linear_to_srgb",
+                                          SK_ARRAY_COUNT(gSrgbArgs),
+                                          gSrgbArgs,
+                                          "return (x <= 0.0031308) ? (x * 12.92) "
+                                          ": (1.055 * pow(x, 0.416666667) - 0.055);",
+                                          &srgbFuncName);
+                break;
+            case GrGammaEffect::Mode::kSRGBToLinear:
+                fragBuilder->emitFunction(kFloat_GrSLType,
+                                          "srgb_to_linear",
+                                          SK_ARRAY_COUNT(gSrgbArgs),
+                                          gSrgbArgs,
+                                          "return (x <= 0.04045) ? (x / 12.92) "
+                                          ": pow((x + 0.055) / 1.055, 2.4);",
+                                          &srgbFuncName);
+            default:
+                // No helper function needed
+                break;
         }
 
-        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 (nullptr == args.fInputColor) {
+            args.fInputColor = "vec4(1)";
+        }
 
-        if (ge.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 {
+        if (GrGammaEffect::Mode::kExponential == ge.mode()) {
             fragBuilder->codeAppendf("%s = vec4(pow(%s.rgb, vec3(%s)), %s.a);",
-                                        args.fOutputColor, tmpVar.c_str(), gammaUniName,
-                                        tmpVar.c_str());
+                                     args.fOutputColor, args.fInputColor, gammaUniName,
+                                     args.fInputColor);
+        } else {
+            fragBuilder->codeAppendf("%s = vec4(%s(%s.r), %s(%s.g), %s(%s.b), %s.a);",
+                                     args.fOutputColor,
+                                     srgbFuncName.c_str(), args.fInputColor,
+                                     srgbFuncName.c_str(), args.fInputColor,
+                                     srgbFuncName.c_str(), args.fInputColor,
+                                     args.fInputColor);
         }
     }
 
     void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
         const GrGammaEffect& ge = proc.cast<GrGammaEffect>();
-        if (!ge.gammaIsSRGB()) {
+        if (GrGammaEffect::Mode::kExponential == ge.mode()) {
             pdman.set1f(fGammaUni, ge.gamma());
         }
     }
@@ -78,7 +83,7 @@ public:
     static inline void GenKey(const GrProcessor& processor, const GrGLSLCaps&,
                               GrProcessorKeyBuilder* b) {
         const GrGammaEffect& ge = processor.cast<GrGammaEffect>();
-        uint32_t key = ge.gammaIsSRGB() ? 0x1 : 0x0;
+        uint32_t key = static_cast<uint32_t>(ge.mode());
         b->add32(key);
     }
 
@@ -90,19 +95,17 @@ private:
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrGammaEffect::GrGammaEffect(GrTexture* texture, SkScalar gamma)
-    : INHERITED(texture, GrCoordTransform::MakeDivByTextureWHMatrix(texture)) {
+GrGammaEffect::GrGammaEffect(Mode mode, SkScalar gamma)
+    : fMode(mode)
+    , fGamma(gamma) {
     this->initClassID<GrGammaEffect>();
-
-    fGamma = gamma;
-    fGammaIsSRGB = SkScalarNearlyEqual(gamma, 1.0f / 2.2f);
 }
 
 bool GrGammaEffect::onIsEqual(const GrFragmentProcessor& s) const {
     const GrGammaEffect& other = s.cast<GrGammaEffect>();
     return
-        other.fGammaIsSRGB == fGammaIsSRGB &&
-        other.fGamma == fGamma;
+        other.fMode == fMode &&
+        (fMode != Mode::kExponential || other.fGamma == fGamma);
 }
 
 void GrGammaEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
@@ -115,9 +118,9 @@ GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrGammaEffect);
 
 const GrFragmentProcessor* GrGammaEffect::TestCreate(GrProcessorTestData* d) {
     // We want to be sure and test sRGB sometimes
-    bool srgb = d->fRandom->nextBool();
-    return new GrGammaEffect(d->fTextures[GrProcessorUnitTest::kSkiaPMTextureIdx],
-                             srgb ? 1.0f / 2.2f : d->fRandom->nextRangeScalar(0.5f, 2.0f));
+    Mode testMode = static_cast<Mode>(d->fRandom->nextRangeU(0, 2));
+    SkScalar gamma = d->fRandom->nextRangeScalar(0.5f, 2.0f);
+    return new GrGammaEffect(testMode, gamma);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -131,6 +134,15 @@ GrGLSLFragmentProcessor* GrGammaEffect::onCreateGLSLInstance() const {
     return new GrGLGammaEffect();
 }
 
-const GrFragmentProcessor* GrGammaEffect::Create(GrTexture* texture, SkScalar gamma) {
-    return new GrGammaEffect(texture, gamma);
+const GrFragmentProcessor* GrGammaEffect::Create(SkScalar gamma) {
+    // TODO: Once our public-facing API for specifying gamma curves settles down, expose this,
+    // and allow clients to explicitly request sRGB, rather than inferring from the exponent.
+    // Note that AdobeRGB (for example) is speficied as x^2.2, not the Rec.709 curves.
+    if (SkScalarNearlyEqual(gamma, 2.2f)) {
+        return new GrGammaEffect(Mode::kSRGBToLinear, 2.2f);
+    } else if (SkScalarNearlyEqual(gamma, 1.0f / 2.2f)) {
+        return new GrGammaEffect(Mode::kLinearToSRGB, 1.0f / 2.2f);
+    } else {
+        return new GrGammaEffect(Mode::kExponential, gamma);
+    }
 }
index 44d6d6707cb1f14a862764b59845b00b416e7200..2e53b09278613799deea19ef7cf09c10f8d4b775 100644 (file)
@@ -8,35 +8,40 @@
 #ifndef GrGammaEffect_DEFINED
 #define GrGammaEffect_DEFINED
 
-#include "GrSingleTextureEffect.h"
+#include "GrFragmentProcessor.h"
 
-class GrGammaEffect : public GrSingleTextureEffect {
+class GrGammaEffect : public GrFragmentProcessor {
 public:
+    enum class Mode {
+        kLinearToSRGB,
+        kSRGBToLinear,
+        kExponential,
+    };
+
     /**
-     * Creates an effect that applies a gamma curve. The source texture is always
-     * sampled unfiltered and with clamping.
-     */
-    static const GrFragmentProcessor* Create(GrTexture*, SkScalar gamma);
+    * Creates an effect that applies a gamma curve.
+    */
+    static const GrFragmentProcessor* Create(SkScalar gamma);
 
     const char* name() const override { return "Gamma"; }
 
-    bool gammaIsSRGB() const { return fGammaIsSRGB; }
+    Mode mode() const { return fMode; }
     SkScalar gamma() const { return fGamma; }
 
 private:
-    GrGammaEffect(GrTexture*, SkScalar gamma);
+    GrGammaEffect(Mode mode, SkScalar gamma);
 
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
     void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
     bool onIsEqual(const GrFragmentProcessor&) const override;
     void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
-    bool fGammaIsSRGB;
+    Mode fMode;
     SkScalar fGamma;
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
-    typedef GrSingleTextureEffect INHERITED;
+    typedef GrFragmentProcessor INHERITED;
 };
 
 #endif