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 GrDrawEffect&);
     22 
     23     virtual void emitCode(GrGLShaderBuilder*,
     24                           const GrDrawEffect&,
     25                           EffectKey,
     26                           const char* outputColor,
     27                           const char* inputColor,
     28                           const TextureSamplerArray&) SK_OVERRIDE;
     29 
     30     virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE;
     31 
     32     static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
     33 
     34 private:
     35     int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); }
     36     bool useBounds() const { return fUseBounds; }
     37     Gr1DKernelEffect::Direction direction() const { return fDirection; }
     38 
     39     int                 fRadius;
     40     bool                fUseBounds;
     41     Gr1DKernelEffect::Direction    fDirection;
     42     UniformHandle       fKernelUni;
     43     UniformHandle       fImageIncrementUni;
     44     UniformHandle       fBoundsUni;
     45     GrGLEffectMatrix    fEffectMatrix;
     46 
     47     typedef GrGLEffect INHERITED;
     48 };
     49 
     50 GrGLConvolutionEffect::GrGLConvolutionEffect(const GrBackendEffectFactory& factory,
     51                                              const GrDrawEffect& drawEffect)
     52     : INHERITED(factory)
     53     , fKernelUni(kInvalidUniformHandle)
     54     , fImageIncrementUni(kInvalidUniformHandle)
     55     , fBoundsUni(kInvalidUniformHandle)
     56     , fEffectMatrix(drawEffect.castEffect<GrConvolutionEffect>().coordsType()) {
     57     const GrConvolutionEffect& c = drawEffect.castEffect<GrConvolutionEffect>();
     58     fRadius = c.radius();
     59     fUseBounds = c.useBounds();
     60     fDirection = c.direction();
     61 }
     62 
     63 void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
     64                                      const GrDrawEffect&,
     65                                      EffectKey key,
     66                                      const char* outputColor,
     67                                      const char* inputColor,
     68                                      const TextureSamplerArray& samplers) {
     69     const char* coords;
     70     fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &coords);
     71     fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
     72                                              kVec2f_GrSLType, "ImageIncrement");
     73     if (this->useBounds()) {
     74         fBoundsUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
     75                                          kVec2f_GrSLType, "Bounds");
     76     }
     77     fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
     78                                           kFloat_GrSLType, "Kernel", this->width());
     79 
     80     builder->fsCodeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
     81 
     82     int width = this->width();
     83     const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni);
     84     const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
     85 
     86     builder->fsCodeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords, fRadius, imgInc);
     87 
     88     // Manually unroll loop because some drivers don't; yields 20-30% speedup.
     89     for (int i = 0; i < width; i++) {
     90         SkString index;
     91         SkString kernelIndex;
     92         index.appendS32(i);
     93         kernel.appendArrayAccess(index.c_str(), &kernelIndex);
     94         builder->fsCodeAppendf("\t\t%s += ", outputColor);
     95         builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType, samplers[0], "coord");
     96         if (this->useBounds()) {
     97             const char* bounds = builder->getUniformCStr(fBoundsUni);
     98             const char* component = this->direction() == Gr1DKernelEffect::kY_Direction ? "y" : "x";
     99             builder->fsCodeAppendf(" * float(coord.%s >= %s.x && coord.%s <= %s.y)",
    100                 component, bounds, component, bounds);
    101         }
    102         builder->fsCodeAppendf(" * %s;\n", kernelIndex.c_str());
    103         builder->fsCodeAppendf("\t\tcoord += %s;\n", imgInc);
    104     }
    105 
    106     SkString modulate;
    107     GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
    108     builder->fsCodeAppend(modulate.c_str());
    109 }
    110 
    111 void GrGLConvolutionEffect::setData(const GrGLUniformManager& uman,
    112                                     const GrDrawEffect& drawEffect) {
    113     const GrConvolutionEffect& conv = drawEffect.castEffect<GrConvolutionEffect>();
    114     GrTexture& texture = *conv.texture(0);
    115     // the code we generated was for a specific kernel radius
    116     GrAssert(conv.radius() == fRadius);
    117     float imageIncrement[2] = { 0 };
    118     float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
    119     switch (conv.direction()) {
    120         case Gr1DKernelEffect::kX_Direction:
    121             imageIncrement[0] = 1.0f / texture.width();
    122             break;
    123         case Gr1DKernelEffect::kY_Direction:
    124             imageIncrement[1] = ySign / texture.height();
    125             break;
    126         default:
    127             GrCrash("Unknown filter direction.");
    128     }
    129     uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
    130     if (conv.useBounds()) {
    131         const float* bounds = conv.bounds();
    132         if (Gr1DKernelEffect::kY_Direction == conv.direction() &&
    133             texture.origin() != kTopLeft_GrSurfaceOrigin) {
    134             uman.set2f(fBoundsUni, 1.0f - bounds[1], 1.0f - bounds[0]);
    135         } else {
    136             uman.set2f(fBoundsUni, bounds[0], bounds[1]);
    137         }
    138     }
    139     uman.set1fv(fKernelUni, 0, this->width(), conv.kernel());
    140     fEffectMatrix.setData(uman, conv.getMatrix(), drawEffect, conv.texture(0));
    141 }
    142 
    143 GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrDrawEffect& drawEffect,
    144                                                     const GrGLCaps&) {
    145     const GrConvolutionEffect& conv = drawEffect.castEffect<GrConvolutionEffect>();
    146     EffectKey key = conv.radius();
    147     key <<= 2;
    148     if (conv.useBounds()) {
    149         key |= 0x2;
    150         key |= GrConvolutionEffect::kY_Direction == conv.direction() ? 0x1 : 0x0;
    151     }
    152     key <<= GrGLEffectMatrix::kKeyBits;
    153     EffectKey matrixKey = GrGLEffectMatrix::GenKey(conv.getMatrix(),
    154                                                    drawEffect,
    155                                                    conv.coordsType(),
    156                                                    conv.texture(0));
    157     return key | matrixKey;
    158 }
    159 
    160 ///////////////////////////////////////////////////////////////////////////////
    161 
    162 GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
    163                                          Direction direction,
    164                                          int radius,
    165                                          const float* kernel,
    166                                          bool useBounds,
    167                                          float bounds[2])
    168     : Gr1DKernelEffect(texture, direction, radius), fUseBounds(useBounds) {
    169     GrAssert(radius <= kMaxKernelRadius);
    170     GrAssert(NULL != kernel);
    171     int width = this->width();
    172     for (int i = 0; i < width; i++) {
    173         fKernel[i] = kernel[i];
    174     }
    175     memcpy(fBounds, bounds, sizeof(fBounds));
    176 }
    177 
    178 GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
    179                                          Direction direction,
    180                                          int radius,
    181                                          float gaussianSigma,
    182                                          bool useBounds,
    183                                          float bounds[2])
    184     : Gr1DKernelEffect(texture, direction, radius), fUseBounds(useBounds) {
    185     GrAssert(radius <= kMaxKernelRadius);
    186     int width = this->width();
    187 
    188     float sum = 0.0f;
    189     float denom = 1.0f / (2.0f * gaussianSigma * gaussianSigma);
    190     for (int i = 0; i < width; ++i) {
    191         float x = static_cast<float>(i - this->radius());
    192         // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
    193         // is dropped here, since we renormalize the kernel below.
    194         fKernel[i] = sk_float_exp(- x * x * denom);
    195         sum += fKernel[i];
    196     }
    197     // Normalize the kernel
    198     float scale = 1.0f / sum;
    199     for (int i = 0; i < width; ++i) {
    200         fKernel[i] *= scale;
    201     }
    202     memcpy(fBounds, bounds, sizeof(fBounds));
    203 }
    204 
    205 GrConvolutionEffect::~GrConvolutionEffect() {
    206 }
    207 
    208 const GrBackendEffectFactory& GrConvolutionEffect::getFactory() const {
    209     return GrTBackendEffectFactory<GrConvolutionEffect>::getInstance();
    210 }
    211 
    212 bool GrConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
    213     const GrConvolutionEffect& s = CastEffect<GrConvolutionEffect>(sBase);
    214     return (this->texture(0) == s.texture(0) &&
    215             this->radius() == s.radius() &&
    216             this->direction() == s.direction() &&
    217             this->useBounds() == s.useBounds() &&
    218             0 == memcmp(fBounds, s.fBounds, sizeof(fBounds)) &&
    219             0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
    220 }
    221 
    222 ///////////////////////////////////////////////////////////////////////////////
    223 
    224 GR_DEFINE_EFFECT_TEST(GrConvolutionEffect);
    225 
    226 GrEffectRef* GrConvolutionEffect::TestCreate(SkMWCRandom* random,
    227                                              GrContext*,
    228                                              const GrDrawTargetCaps&,
    229                                              GrTexture* textures[]) {
    230     int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
    231                                       GrEffectUnitTest::kAlphaTextureIdx;
    232     Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
    233     int radius = random->nextRangeU(1, kMaxKernelRadius);
    234     float kernel[kMaxKernelRadius];
    235     for (int i = 0; i < kMaxKernelRadius; ++i) {
    236         kernel[i] = random->nextSScalar1();
    237     }
    238     float bounds[2];
    239     for (int i = 0; i < 2; ++i) {
    240         bounds[i] = random->nextF();
    241     }
    242 
    243     bool useBounds = random->nextBool();
    244     return GrConvolutionEffect::Create(textures[texIdx],
    245                                        dir,
    246                                        radius,
    247                                        kernel,
    248                                        useBounds,
    249                                        bounds);
    250 }
    251