Home | History | Annotate | Download | only in effects
      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