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