Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2012 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 "GrConvolutionEffect.h"
      9 #include "gl/GrGLEffect.h"
     10 #include "gl/GrGLEffectMatrix.h"
     11 #include "gl/GrGLSL.h"
     12 #include "gl/GrGLTexture.h"
     13 #include "GrTBackendEffectFactory.h"
     14 
     15 // For brevity
     16 typedef GrGLUniformManager::UniformHandle UniformHandle;
     17 static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
     18 
     19 class GrGLConvolutionEffect : public GrGLEffect {
     20 public:
     21     GrGLConvolutionEffect(const GrBackendEffectFactory&, const GrEffectRef&);
     22 
     23     virtual void emitCode(GrGLShaderBuilder*,
     24                           const GrEffectStage&,
     25                           EffectKey,
     26                           const char* vertexCoords,
     27                           const char* outputColor,
     28                           const char* inputColor,
     29                           const TextureSamplerArray&) SK_OVERRIDE;
     30 
     31     virtual void setData(const GrGLUniformManager& uman, const GrEffectStage&) SK_OVERRIDE;
     32 
     33     static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
     34 
     35 private:
     36     int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); }
     37 
     38     int                 fRadius;
     39     UniformHandle       fKernelUni;
     40     UniformHandle       fImageIncrementUni;
     41     GrGLEffectMatrix    fEffectMatrix;
     42 
     43     typedef GrGLEffect INHERITED;
     44 };
     45 
     46 GrGLConvolutionEffect::GrGLConvolutionEffect(const GrBackendEffectFactory& factory,
     47                                              const GrEffectRef& effect)
     48     : INHERITED(factory)
     49     , fKernelUni(kInvalidUniformHandle)
     50     , fImageIncrementUni(kInvalidUniformHandle) {
     51     const GrConvolutionEffect& c = CastEffect<GrConvolutionEffect>(effect);
     52     fRadius = c.radius();
     53 }
     54 
     55 void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
     56                                      const GrEffectStage&,
     57                                      EffectKey key,
     58                                      const char* vertexCoords,
     59                                      const char* outputColor,
     60                                      const char* inputColor,
     61                                      const TextureSamplerArray& samplers) {
     62     const char* coords;
     63     fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, vertexCoords, &coords);
     64     fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
     65                                              kVec2f_GrSLType, "ImageIncrement");
     66     fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
     67                                           kFloat_GrSLType, "Kernel", this->width());
     68     SkString* code = &builder->fFSCode;
     69 
     70     code->appendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
     71 
     72     int width = this ->width();
     73     const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni);
     74     const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
     75 
     76     code->appendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords, fRadius, imgInc);
     77 
     78     // Manually unroll loop because some drivers don't; yields 20-30% speedup.
     79     for (int i = 0; i < width; i++) {
     80         SkString index;
     81         SkString kernelIndex;
     82         index.appendS32(i);
     83         kernel.appendArrayAccess(index.c_str(), &kernelIndex);
     84         code->appendf("\t\t%s += ", outputColor);
     85         builder->appendTextureLookup(&builder->fFSCode, samplers[0], "coord");
     86         code->appendf(" * %s;\n", kernelIndex.c_str());
     87         code->appendf("\t\tcoord += %s;\n", imgInc);
     88     }
     89     GrGLSLMulVarBy4f(&builder->fFSCode, 2, outputColor, inputColor);
     90 }
     91 
     92 void GrGLConvolutionEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
     93     const GrConvolutionEffect& conv = GetEffectFromStage<GrConvolutionEffect>(stage);
     94     GrTexture& texture = *conv.texture(0);
     95     // the code we generated was for a specific kernel radius
     96     GrAssert(conv.radius() == fRadius);
     97     float imageIncrement[2] = { 0 };
     98     switch (conv.direction()) {
     99         case Gr1DKernelEffect::kX_Direction:
    100             imageIncrement[0] = 1.0f / texture.width();
    101             break;
    102         case Gr1DKernelEffect::kY_Direction:
    103             imageIncrement[1] = 1.0f / texture.height();
    104             break;
    105         default:
    106             GrCrash("Unknown filter direction.");
    107     }
    108     uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
    109     uman.set1fv(fKernelUni, 0, this->width(), conv.kernel());
    110     fEffectMatrix.setData(uman, conv.getMatrix(), stage.getCoordChangeMatrix(), conv.texture(0));
    111 }
    112 
    113 GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrEffectStage& s, const GrGLCaps&) {
    114     const GrConvolutionEffect& conv = GetEffectFromStage<GrConvolutionEffect>(s);
    115     EffectKey key = conv.radius();
    116     key <<= GrGLEffectMatrix::kKeyBits;
    117     EffectKey matrixKey = GrGLEffectMatrix::GenKey(conv.getMatrix(),
    118                                                    s.getCoordChangeMatrix(),
    119                                                    conv.texture(0));
    120     return key | matrixKey;
    121 }
    122 
    123 ///////////////////////////////////////////////////////////////////////////////
    124 
    125 GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
    126                                          Direction direction,
    127                                          int radius,
    128                                          const float* kernel)
    129     : Gr1DKernelEffect(texture, direction, radius) {
    130     GrAssert(radius <= kMaxKernelRadius);
    131     GrAssert(NULL != kernel);
    132     int width = this->width();
    133     for (int i = 0; i < width; i++) {
    134         fKernel[i] = kernel[i];
    135     }
    136 }
    137 
    138 GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
    139                                          Direction direction,
    140                                          int radius,
    141                                          float gaussianSigma)
    142     : Gr1DKernelEffect(texture, direction, radius) {
    143     GrAssert(radius <= kMaxKernelRadius);
    144     int width = this->width();
    145 
    146     float sum = 0.0f;
    147     float denom = 1.0f / (2.0f * gaussianSigma * gaussianSigma);
    148     for (int i = 0; i < width; ++i) {
    149         float x = static_cast<float>(i - this->radius());
    150         // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
    151         // is dropped here, since we renormalize the kernel below.
    152         fKernel[i] = sk_float_exp(- x * x * denom);
    153         sum += fKernel[i];
    154     }
    155     // Normalize the kernel
    156     float scale = 1.0f / sum;
    157     for (int i = 0; i < width; ++i) {
    158         fKernel[i] *= scale;
    159     }
    160 }
    161 
    162 GrConvolutionEffect::~GrConvolutionEffect() {
    163 }
    164 
    165 const GrBackendEffectFactory& GrConvolutionEffect::getFactory() const {
    166     return GrTBackendEffectFactory<GrConvolutionEffect>::getInstance();
    167 }
    168 
    169 bool GrConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
    170     const GrConvolutionEffect& s = CastEffect<GrConvolutionEffect>(sBase);
    171     return (this->texture(0) == s.texture(0) &&
    172             this->radius() == s.radius() &&
    173             this->direction() == s.direction() &&
    174             0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
    175 }
    176 
    177 ///////////////////////////////////////////////////////////////////////////////
    178 
    179 GR_DEFINE_EFFECT_TEST(GrConvolutionEffect);
    180 
    181 GrEffectRef* GrConvolutionEffect::TestCreate(SkRandom* random,
    182                                              GrContext* context,
    183                                              GrTexture* textures[]) {
    184     int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
    185                                       GrEffectUnitTest::kAlphaTextureIdx;
    186     Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
    187     int radius = random->nextRangeU(1, kMaxKernelRadius);
    188     float kernel[kMaxKernelRadius];
    189     for (int i = 0; i < kMaxKernelRadius; ++i) {
    190         kernel[i] = random->nextSScalar1();
    191     }
    192 
    193     return GrConvolutionEffect::Create(textures[texIdx], dir, radius,kernel);
    194 }
    195