1 #include "GrBicubicEffect.h" 2 3 #define DS(x) SkDoubleToScalar(x) 4 5 const SkScalar GrBicubicEffect::gMitchellCoefficients[16] = { 6 DS( 1.0 / 18.0), DS(-9.0 / 18.0), DS( 15.0 / 18.0), DS( -7.0 / 18.0), 7 DS(16.0 / 18.0), DS( 0.0 / 18.0), DS(-36.0 / 18.0), DS( 21.0 / 18.0), 8 DS( 1.0 / 18.0), DS( 9.0 / 18.0), DS( 27.0 / 18.0), DS(-21.0 / 18.0), 9 DS( 0.0 / 18.0), DS( 0.0 / 18.0), DS( -6.0 / 18.0), DS( 7.0 / 18.0), 10 }; 11 12 13 class GrGLBicubicEffect : public GrGLEffect { 14 public: 15 GrGLBicubicEffect(const GrBackendEffectFactory& factory, 16 const GrDrawEffect&); 17 18 virtual void emitCode(GrGLShaderBuilder*, 19 const GrDrawEffect&, 20 EffectKey, 21 const char* outputColor, 22 const char* inputColor, 23 const TransformedCoordsArray&, 24 const TextureSamplerArray&) SK_OVERRIDE; 25 26 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 27 28 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 29 const GrTextureDomain& domain = drawEffect.castEffect<GrBicubicEffect>().domain(); 30 return GrTextureDomain::GLDomain::DomainKey(domain); 31 } 32 33 private: 34 typedef GrGLUniformManager::UniformHandle UniformHandle; 35 36 UniformHandle fCoefficientsUni; 37 UniformHandle fImageIncrementUni; 38 GrTextureDomain::GLDomain fDomain; 39 40 typedef GrGLEffect INHERITED; 41 }; 42 43 GrGLBicubicEffect::GrGLBicubicEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) 44 : INHERITED(factory) { 45 } 46 47 void GrGLBicubicEffect::emitCode(GrGLShaderBuilder* builder, 48 const GrDrawEffect& drawEffect, 49 EffectKey key, 50 const char* outputColor, 51 const char* inputColor, 52 const TransformedCoordsArray& coords, 53 const TextureSamplerArray& samplers) { 54 const GrTextureDomain& domain = drawEffect.castEffect<GrBicubicEffect>().domain(); 55 56 SkString coords2D = builder->ensureFSCoords2D(coords, 0); 57 fCoefficientsUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, 58 kMat44f_GrSLType, "Coefficients"); 59 fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, 60 kVec2f_GrSLType, "ImageIncrement"); 61 62 const char* imgInc = builder->getUniformCStr(fImageIncrementUni); 63 const char* coeff = builder->getUniformCStr(fCoefficientsUni); 64 65 SkString cubicBlendName; 66 67 static const GrGLShaderVar gCubicBlendArgs[] = { 68 GrGLShaderVar("coefficients", kMat44f_GrSLType), 69 GrGLShaderVar("t", kFloat_GrSLType), 70 GrGLShaderVar("c0", kVec4f_GrSLType), 71 GrGLShaderVar("c1", kVec4f_GrSLType), 72 GrGLShaderVar("c2", kVec4f_GrSLType), 73 GrGLShaderVar("c3", kVec4f_GrSLType), 74 }; 75 builder->fsEmitFunction(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 builder->fsCodeAppendf("\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 builder->fsCodeAppendf("\tcoord /= %s;\n", imgInc); 89 builder->fsCodeAppend("\tvec2 f = fract(coord);\n"); 90 builder->fsCodeAppendf("\tcoord = (coord - f + vec2(0.5)) * %s;\n", imgInc); 91 builder->fsCodeAppend("\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(builder, domain, sampleVar.c_str(), coord, samplers[0]); 99 } 100 builder->fsCodeAppendf("\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 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, (GrGLSLExpr4(bicubicColor.c_str()) * GrGLSLExpr4(inputColor)).c_str()); 105 } 106 107 void GrGLBicubicEffect::setData(const GrGLUniformManager& uman, 108 const GrDrawEffect& drawEffect) { 109 const GrBicubicEffect& effect = drawEffect.castEffect<GrBicubicEffect>(); 110 const GrTexture& texture = *effect.texture(0); 111 float imageIncrement[2]; 112 imageIncrement[0] = 1.0f / texture.width(); 113 imageIncrement[1] = 1.0f / texture.height(); 114 uman.set2fv(fImageIncrementUni, 1, imageIncrement); 115 uman.setMatrix4f(fCoefficientsUni, effect.coefficients()); 116 fDomain.setData(uman, effect.domain(), texture.origin()); 117 } 118 119 static inline void convert_row_major_scalar_coeffs_to_column_major_floats(float dst[16], 120 const SkScalar src[16]) { 121 for (int y = 0; y < 4; y++) { 122 for (int x = 0; x < 4; x++) { 123 dst[x * 4 + y] = SkScalarToFloat(src[y * 4 + x]); 124 } 125 } 126 } 127 128 GrBicubicEffect::GrBicubicEffect(GrTexture* texture, 129 const SkScalar coefficients[16], 130 const SkMatrix &matrix, 131 const SkShader::TileMode tileModes[2]) 132 : INHERITED(texture, matrix, GrTextureParams(tileModes, GrTextureParams::kNone_FilterMode)) 133 , fDomain(GrTextureDomain::IgnoredDomain()) { 134 convert_row_major_scalar_coeffs_to_column_major_floats(fCoefficients, coefficients); 135 } 136 137 GrBicubicEffect::GrBicubicEffect(GrTexture* texture, 138 const SkScalar coefficients[16], 139 const SkMatrix &matrix, 140 const SkRect& domain) 141 : INHERITED(texture, matrix, GrTextureParams(SkShader::kClamp_TileMode, 142 GrTextureParams::kNone_FilterMode)) 143 , fDomain(domain, GrTextureDomain::kClamp_Mode) { 144 convert_row_major_scalar_coeffs_to_column_major_floats(fCoefficients, coefficients); 145 } 146 147 GrBicubicEffect::~GrBicubicEffect() { 148 } 149 150 const GrBackendEffectFactory& GrBicubicEffect::getFactory() const { 151 return GrTBackendEffectFactory<GrBicubicEffect>::getInstance(); 152 } 153 154 bool GrBicubicEffect::onIsEqual(const GrEffect& sBase) const { 155 const GrBicubicEffect& s = CastEffect<GrBicubicEffect>(sBase); 156 return this->textureAccess(0) == s.textureAccess(0) && 157 !memcmp(fCoefficients, s.coefficients(), 16); 158 } 159 160 void GrBicubicEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { 161 // FIXME: Perhaps we can do better. 162 *validFlags = 0; 163 return; 164 } 165 166 GR_DEFINE_EFFECT_TEST(GrBicubicEffect); 167 168 GrEffectRef* GrBicubicEffect::TestCreate(SkRandom* random, 169 GrContext* context, 170 const GrDrawTargetCaps&, 171 GrTexture* textures[]) { 172 int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx : 173 GrEffectUnitTest::kAlphaTextureIdx; 174 SkScalar coefficients[16]; 175 for (int i = 0; i < 16; i++) { 176 coefficients[i] = random->nextSScalar1(); 177 } 178 return GrBicubicEffect::Create(textures[texIdx], coefficients); 179 } 180 181 ////////////////////////////////////////////////////////////////////////////// 182 183 bool GrBicubicEffect::ShouldUseBicubic(const SkMatrix& matrix, 184 GrTextureParams::FilterMode* filterMode) { 185 if (matrix.isIdentity()) { 186 *filterMode = GrTextureParams::kNone_FilterMode; 187 return false; 188 } 189 190 SkScalar scales[2]; 191 if (!matrix.getMinMaxScales(scales) || scales[0] < SK_Scalar1) { 192 // Bicubic doesn't handle arbitrary minimization well, as src texels can be skipped 193 // entirely, 194 *filterMode = GrTextureParams::kMipMap_FilterMode; 195 return false; 196 } 197 // At this point if scales[1] == SK_Scalar1 then the matrix doesn't do any scaling. 198 if (scales[1] == SK_Scalar1) { 199 if (matrix.rectStaysRect() && SkScalarIsInt(matrix.getTranslateX()) && 200 SkScalarIsInt(matrix.getTranslateY())) { 201 *filterMode = GrTextureParams::kNone_FilterMode; 202 } else { 203 // Use bilerp to handle rotation or fractional translation. 204 *filterMode = GrTextureParams::kBilerp_FilterMode; 205 } 206 return false; 207 } 208 // When we use the bicubic filtering effect each sample is read from the texture using 209 // nearest neighbor sampling. 210 *filterMode = GrTextureParams::kNone_FilterMode; 211 return true; 212 } 213