Home | History | Annotate | Download | only in effects
      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(GrGLProgramBuilder*,
     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(GrGLProgramBuilder* 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     GrGLFragmentShaderBuilder* 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 GrProcessor& sBase) const {
    166     const GrBicubicEffect& s = sBase.cast<GrBicubicEffect>();
    167     return this->textureAccess(0) == s.textureAccess(0) &&
    168            !memcmp(fCoefficients, s.coefficients(), 16) &&
    169            fDomain == s.fDomain;
    170 }
    171 
    172 void GrBicubicEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
    173     // FIXME: Perhaps we can do better.
    174     *validFlags = 0;
    175     return;
    176 }
    177 
    178 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrBicubicEffect);
    179 
    180 GrFragmentProcessor* GrBicubicEffect::TestCreate(SkRandom* random,
    181                                                  GrContext* context,
    182                                                  const GrDrawTargetCaps&,
    183                                                  GrTexture* textures[]) {
    184     int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
    185                                       GrProcessorUnitTest::kAlphaTextureIdx;
    186     SkScalar coefficients[16];
    187     for (int i = 0; i < 16; i++) {
    188         coefficients[i] = random->nextSScalar1();
    189     }
    190     return GrBicubicEffect::Create(textures[texIdx], coefficients);
    191 }
    192 
    193 //////////////////////////////////////////////////////////////////////////////
    194 
    195 bool GrBicubicEffect::ShouldUseBicubic(const SkMatrix& matrix,
    196                                        GrTextureParams::FilterMode* filterMode) {
    197     if (matrix.isIdentity()) {
    198         *filterMode = GrTextureParams::kNone_FilterMode;
    199         return false;
    200     }
    201 
    202     SkScalar scales[2];
    203     if (!matrix.getMinMaxScales(scales) || scales[0] < SK_Scalar1) {
    204         // Bicubic doesn't handle arbitrary minimization well, as src texels can be skipped
    205         // entirely,
    206         *filterMode = GrTextureParams::kMipMap_FilterMode;
    207         return false;
    208     }
    209     // At this point if scales[1] == SK_Scalar1 then the matrix doesn't do any scaling.
    210     if (scales[1] == SK_Scalar1) {
    211         if (matrix.rectStaysRect() && SkScalarIsInt(matrix.getTranslateX()) &&
    212             SkScalarIsInt(matrix.getTranslateY())) {
    213             *filterMode = GrTextureParams::kNone_FilterMode;
    214         } else {
    215             // Use bilerp to handle rotation or fractional translation.
    216             *filterMode = GrTextureParams::kBilerp_FilterMode;
    217         }
    218         return false;
    219     }
    220     // When we use the bicubic filtering effect each sample is read from the texture using
    221     // nearest neighbor sampling.
    222     *filterMode = GrTextureParams::kNone_FilterMode;
    223     return true;
    224 }
    225