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