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