GrBicubicEffect.cpp revision 0d3f061262a53b775f0a92b0abf8a4a846290d65
1/*
2 * Copyright 2014 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 "GrBicubicEffect.h"
9#include "GrInvariantOutput.h"
10#include "gl/builders/GrGLProgramBuilder.h"
11
12#define DS(x) SkDoubleToScalar(x)
13
14const SkScalar GrBicubicEffect::gMitchellCoefficients[16] = {
15    DS( 1.0 / 18.0), DS(-9.0 / 18.0), DS( 15.0 / 18.0), DS( -7.0 / 18.0),
16    DS(16.0 / 18.0), DS( 0.0 / 18.0), DS(-36.0 / 18.0), DS( 21.0 / 18.0),
17    DS( 1.0 / 18.0), DS( 9.0 / 18.0), DS( 27.0 / 18.0), DS(-21.0 / 18.0),
18    DS( 0.0 / 18.0), DS( 0.0 / 18.0), DS( -6.0 / 18.0), DS(  7.0 / 18.0),
19};
20
21
22class GrGLBicubicEffect : public GrGLFragmentProcessor {
23public:
24    GrGLBicubicEffect(const GrProcessor&);
25
26    virtual void emitCode(EmitArgs&) override;
27
28    static inline void GenKey(const GrProcessor& effect, const GrGLSLCaps&,
29                              GrProcessorKeyBuilder* b) {
30        const GrTextureDomain& domain = effect.cast<GrBicubicEffect>().domain();
31        b->add32(GrTextureDomain::GLDomain::DomainKey(domain));
32    }
33
34protected:
35    void onSetData(const GrGLProgramDataManager&, const GrProcessor&) override;
36
37private:
38    typedef GrGLProgramDataManager::UniformHandle UniformHandle;
39
40    UniformHandle               fCoefficientsUni;
41    UniformHandle               fImageIncrementUni;
42    GrTextureDomain::GLDomain   fDomain;
43
44    typedef GrGLFragmentProcessor INHERITED;
45};
46
47GrGLBicubicEffect::GrGLBicubicEffect(const GrProcessor&) {
48}
49
50void GrGLBicubicEffect::emitCode(EmitArgs& args) {
51    const GrTextureDomain& domain = args.fFp.cast<GrBicubicEffect>().domain();
52
53    fCoefficientsUni = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
54                                           kMat44f_GrSLType, kDefault_GrSLPrecision,
55                                           "Coefficients");
56    fImageIncrementUni = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
57                                             kVec2f_GrSLType, kDefault_GrSLPrecision,
58                                             "ImageIncrement");
59
60    const char* imgInc = args.fBuilder->getUniformCStr(fImageIncrementUni);
61    const char* coeff = args.fBuilder->getUniformCStr(fCoefficientsUni);
62
63    SkString cubicBlendName;
64
65    static const GrGLSLShaderVar gCubicBlendArgs[] = {
66        GrGLSLShaderVar("coefficients",  kMat44f_GrSLType),
67        GrGLSLShaderVar("t",             kFloat_GrSLType),
68        GrGLSLShaderVar("c0",            kVec4f_GrSLType),
69        GrGLSLShaderVar("c1",            kVec4f_GrSLType),
70        GrGLSLShaderVar("c2",            kVec4f_GrSLType),
71        GrGLSLShaderVar("c3",            kVec4f_GrSLType),
72    };
73    GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
74    SkString coords2D = fsBuilder->ensureFSCoords2D(args.fCoords, 0);
75    fsBuilder->emitFunction(kVec4f_GrSLType,
76                            "cubicBlend",
77                            SK_ARRAY_COUNT(gCubicBlendArgs),
78                            gCubicBlendArgs,
79                            "\tvec4 ts = vec4(1.0, t, t * t, t * t * t);\n"
80                            "\tvec4 c = coefficients * ts;\n"
81                            "\treturn c.x * c0 + c.y * c1 + c.z * c2 + c.w * c3;\n",
82                            &cubicBlendName);
83    fsBuilder->codeAppendf("\tvec2 coord = %s - %s * vec2(0.5);\n", coords2D.c_str(), imgInc);
84    // We unnormalize the coord in order to determine our fractional offset (f) within the texel
85    // We then snap coord to a texel center and renormalize. The snap prevents cases where the
86    // starting coords are near a texel boundary and accumulations of imgInc would cause us to skip/
87    // double hit a texel.
88    fsBuilder->codeAppendf("\tcoord /= %s;\n", imgInc);
89    fsBuilder->codeAppend("\tvec2 f = fract(coord);\n");
90    fsBuilder->codeAppendf("\tcoord = (coord - f + vec2(0.5)) * %s;\n", imgInc);
91    fsBuilder->codeAppend("\tvec4 rowColors[4];\n");
92    for (int y = 0; y < 4; ++y) {
93        for (int x = 0; x < 4; ++x) {
94            SkString coord;
95            coord.printf("coord + %s * vec2(%d, %d)", imgInc, x - 1, y - 1);
96            SkString sampleVar;
97            sampleVar.printf("rowColors[%d]", x);
98            fDomain.sampleTexture(fsBuilder, domain, sampleVar.c_str(), coord, args.fSamplers[0]);
99        }
100        fsBuilder->codeAppendf("\tvec4 s%d = %s(%s, f.x, rowColors[0], rowColors[1], rowColors[2], rowColors[3]);\n", y, cubicBlendName.c_str(), coeff);
101    }
102    SkString bicubicColor;
103    bicubicColor.printf("%s(%s, f.y, s0, s1, s2, s3)", cubicBlendName.c_str(), coeff);
104    fsBuilder->codeAppendf("\t%s = %s;\n", args.fOutputColor,(GrGLSLExpr4(bicubicColor.c_str()) *
105                           GrGLSLExpr4(args.fInputColor)).c_str());
106}
107
108void GrGLBicubicEffect::onSetData(const GrGLProgramDataManager& pdman,
109                                const GrProcessor& processor) {
110    const GrBicubicEffect& bicubicEffect = processor.cast<GrBicubicEffect>();
111    const GrTexture& texture = *processor.texture(0);
112    float imageIncrement[2];
113    imageIncrement[0] = 1.0f / texture.width();
114    imageIncrement[1] = 1.0f / texture.height();
115    pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
116    pdman.setMatrix4f(fCoefficientsUni, bicubicEffect.coefficients());
117    fDomain.setData(pdman, bicubicEffect.domain(), texture.origin());
118}
119
120static inline void convert_row_major_scalar_coeffs_to_column_major_floats(float dst[16],
121                                                                          const SkScalar src[16]) {
122    for (int y = 0; y < 4; y++) {
123        for (int x = 0; x < 4; x++) {
124            dst[x * 4 + y] = SkScalarToFloat(src[y * 4 + x]);
125        }
126    }
127}
128
129GrBicubicEffect::GrBicubicEffect(GrTexture* texture,
130                                 const SkScalar coefficients[16],
131                                 const SkMatrix &matrix,
132                                 const SkShader::TileMode tileModes[2])
133  : INHERITED(texture, matrix, GrTextureParams(tileModes, GrTextureParams::kNone_FilterMode))
134  , fDomain(GrTextureDomain::IgnoredDomain()) {
135    this->initClassID<GrBicubicEffect>();
136    convert_row_major_scalar_coeffs_to_column_major_floats(fCoefficients, coefficients);
137}
138
139GrBicubicEffect::GrBicubicEffect(GrTexture* texture,
140                                 const SkScalar coefficients[16],
141                                 const SkMatrix &matrix,
142                                 const SkRect& domain)
143  : INHERITED(texture, matrix,
144              GrTextureParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode))
145  , fDomain(domain, GrTextureDomain::kClamp_Mode) {
146    this->initClassID<GrBicubicEffect>();
147    convert_row_major_scalar_coeffs_to_column_major_floats(fCoefficients, coefficients);
148}
149
150GrBicubicEffect::~GrBicubicEffect() {
151}
152
153void GrBicubicEffect::onGetGLProcessorKey(const GrGLSLCaps& caps,
154                                        GrProcessorKeyBuilder* b) const {
155    GrGLBicubicEffect::GenKey(*this, caps, b);
156}
157
158GrGLFragmentProcessor* GrBicubicEffect::onCreateGLInstance() const  {
159    return new GrGLBicubicEffect(*this);
160}
161
162bool GrBicubicEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
163    const GrBicubicEffect& s = sBase.cast<GrBicubicEffect>();
164    return !memcmp(fCoefficients, s.coefficients(), 16) &&
165           fDomain == s.fDomain;
166}
167
168void GrBicubicEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
169    // FIXME: Perhaps we can do better.
170    inout->mulByUnknownSingleComponent();
171}
172
173GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrBicubicEffect);
174
175const GrFragmentProcessor* GrBicubicEffect::TestCreate(GrProcessorTestData* d) {
176    int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
177                                          GrProcessorUnitTest::kAlphaTextureIdx;
178    SkScalar coefficients[16];
179    for (int i = 0; i < 16; i++) {
180        coefficients[i] = d->fRandom->nextSScalar1();
181    }
182    return GrBicubicEffect::Create(d->fTextures[texIdx], coefficients);
183}
184
185//////////////////////////////////////////////////////////////////////////////
186
187bool GrBicubicEffect::ShouldUseBicubic(const SkMatrix& matrix,
188                                       GrTextureParams::FilterMode* filterMode) {
189    if (matrix.isIdentity()) {
190        *filterMode = GrTextureParams::kNone_FilterMode;
191        return false;
192    }
193
194    SkScalar scales[2];
195    if (!matrix.getMinMaxScales(scales) || scales[0] < SK_Scalar1) {
196        // Bicubic doesn't handle arbitrary minimization well, as src texels can be skipped
197        // entirely,
198        *filterMode = GrTextureParams::kMipMap_FilterMode;
199        return false;
200    }
201    // At this point if scales[1] == SK_Scalar1 then the matrix doesn't do any scaling.
202    if (scales[1] == SK_Scalar1) {
203        if (matrix.rectStaysRect() && SkScalarIsInt(matrix.getTranslateX()) &&
204            SkScalarIsInt(matrix.getTranslateY())) {
205            *filterMode = GrTextureParams::kNone_FilterMode;
206        } else {
207            // Use bilerp to handle rotation or fractional translation.
208            *filterMode = GrTextureParams::kBilerp_FilterMode;
209        }
210        return false;
211    }
212    // When we use the bicubic filtering effect each sample is read from the texture using
213    // nearest neighbor sampling.
214    *filterMode = GrTextureParams::kNone_FilterMode;
215    return true;
216}
217