aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/effects/GrGammaEffect.cpp
blob: 765612052c6043fc9ada3c4f2dfe4b5d58761b8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 * 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 "GrGammaEffect.h"

#include "GrContext.h"
#include "GrCoordTransform.h"
#include "GrFragmentProcessor.h"
#include "GrInvariantOutput.h"
#include "GrProcessor.h"
#include "glsl/GrGLSLFragmentProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"

class GrGLGammaEffect : public GrGLSLFragmentProcessor {
public:
    void emitCode(EmitArgs& args) override {
        const GrGammaEffect& ge = args.fFp.cast<GrGammaEffect>();
        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
        GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;

        const char* gammaUniName = nullptr;
        if (GrGammaEffect::Mode::kExponential == ge.mode()) {
            fGammaUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
                                                   kDefault_GrSLPrecision, "Gamma", &gammaUniName);
        }

        SkString srgbFuncName;
        static const GrShaderVar gSrgbArgs[] = {
            GrShaderVar("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;
        }

        if (nullptr == args.fInputColor) {
            args.fInputColor = "vec4(1)";
        }

        if (GrGammaEffect::Mode::kExponential == ge.mode()) {
            fragBuilder->codeAppendf("%s = vec4(pow(%s.rgb, vec3(%s)), %s.a);",
                                     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 (GrGammaEffect::Mode::kExponential == ge.mode()) {
            pdman.set1f(fGammaUni, ge.gamma());
        }
    }

    static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&,
                              GrProcessorKeyBuilder* b) {
        const GrGammaEffect& ge = processor.cast<GrGammaEffect>();
        uint32_t key = static_cast<uint32_t>(ge.mode());
        b->add32(key);
    }

private:
    GrGLSLProgramDataManager::UniformHandle fGammaUni;

    typedef GrGLSLFragmentProcessor INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

GrGammaEffect::GrGammaEffect(Mode mode, SkScalar gamma)
    : fMode(mode)
    , fGamma(gamma) {
    this->initClassID<GrGammaEffect>();
}

bool GrGammaEffect::onIsEqual(const GrFragmentProcessor& s) const {
    const GrGammaEffect& other = s.cast<GrGammaEffect>();
    return
        other.fMode == fMode &&
        (fMode != Mode::kExponential || other.fGamma == fGamma);
}

void GrGammaEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
    inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
}

///////////////////////////////////////////////////////////////////////////////

GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrGammaEffect);

sk_sp<GrFragmentProcessor> GrGammaEffect::TestCreate(GrProcessorTestData* d) {
    // We want to be sure and test sRGB sometimes
    Mode testMode = static_cast<Mode>(d->fRandom->nextRangeU(0, 2));
    SkScalar gamma = d->fRandom->nextRangeScalar(0.5f, 2.0f);
    return sk_sp<GrFragmentProcessor>(new GrGammaEffect(testMode, gamma));
}

///////////////////////////////////////////////////////////////////////////////

void GrGammaEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
                                          GrProcessorKeyBuilder* b) const {
    GrGLGammaEffect::GenKey(*this, caps, b);
}

GrGLSLFragmentProcessor* GrGammaEffect::onCreateGLSLInstance() const {
    return new GrGLGammaEffect();
}

sk_sp<GrFragmentProcessor> GrGammaEffect::Make(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 sk_sp<GrFragmentProcessor>(new GrGammaEffect(Mode::kSRGBToLinear, 2.2f));
    } else if (SkScalarNearlyEqual(gamma, 1.0f / 2.2f)) {
        return sk_sp<GrFragmentProcessor>(new GrGammaEffect(Mode::kLinearToSRGB, 1.0f / 2.2f));
    } else {
        return sk_sp<GrFragmentProcessor>(new GrGammaEffect(Mode::kExponential, gamma));
    }
}