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