gfx/skia/trunk/src/gpu/effects/GrConvolutionEffect.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/gpu/effects/GrConvolutionEffect.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,238 @@
     1.4 +/*
     1.5 + * Copyright 2012 Google Inc.
     1.6 + *
     1.7 + * Use of this source code is governed by a BSD-style license that can be
     1.8 + * found in the LICENSE file.
     1.9 + */
    1.10 +
    1.11 +#include "GrConvolutionEffect.h"
    1.12 +#include "gl/GrGLEffect.h"
    1.13 +#include "gl/GrGLSL.h"
    1.14 +#include "gl/GrGLTexture.h"
    1.15 +#include "GrTBackendEffectFactory.h"
    1.16 +
    1.17 +// For brevity
    1.18 +typedef GrGLUniformManager::UniformHandle UniformHandle;
    1.19 +
    1.20 +class GrGLConvolutionEffect : public GrGLEffect {
    1.21 +public:
    1.22 +    GrGLConvolutionEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
    1.23 +
    1.24 +    virtual void emitCode(GrGLShaderBuilder*,
    1.25 +                          const GrDrawEffect&,
    1.26 +                          EffectKey,
    1.27 +                          const char* outputColor,
    1.28 +                          const char* inputColor,
    1.29 +                          const TransformedCoordsArray&,
    1.30 +                          const TextureSamplerArray&) SK_OVERRIDE;
    1.31 +
    1.32 +    virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE;
    1.33 +
    1.34 +    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
    1.35 +
    1.36 +private:
    1.37 +    int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); }
    1.38 +    bool useBounds() const { return fUseBounds; }
    1.39 +    Gr1DKernelEffect::Direction direction() const { return fDirection; }
    1.40 +
    1.41 +    int                 fRadius;
    1.42 +    bool                fUseBounds;
    1.43 +    Gr1DKernelEffect::Direction    fDirection;
    1.44 +    UniformHandle       fKernelUni;
    1.45 +    UniformHandle       fImageIncrementUni;
    1.46 +    UniformHandle       fBoundsUni;
    1.47 +
    1.48 +    typedef GrGLEffect INHERITED;
    1.49 +};
    1.50 +
    1.51 +GrGLConvolutionEffect::GrGLConvolutionEffect(const GrBackendEffectFactory& factory,
    1.52 +                                             const GrDrawEffect& drawEffect)
    1.53 +    : INHERITED(factory) {
    1.54 +    const GrConvolutionEffect& c = drawEffect.castEffect<GrConvolutionEffect>();
    1.55 +    fRadius = c.radius();
    1.56 +    fUseBounds = c.useBounds();
    1.57 +    fDirection = c.direction();
    1.58 +}
    1.59 +
    1.60 +void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
    1.61 +                                     const GrDrawEffect&,
    1.62 +                                     EffectKey key,
    1.63 +                                     const char* outputColor,
    1.64 +                                     const char* inputColor,
    1.65 +                                     const TransformedCoordsArray& coords,
    1.66 +                                     const TextureSamplerArray& samplers) {
    1.67 +    SkString coords2D = builder->ensureFSCoords2D(coords, 0);
    1.68 +    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    1.69 +                                             kVec2f_GrSLType, "ImageIncrement");
    1.70 +    if (this->useBounds()) {
    1.71 +        fBoundsUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    1.72 +                                         kVec2f_GrSLType, "Bounds");
    1.73 +    }
    1.74 +    fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
    1.75 +                                          kFloat_GrSLType, "Kernel", this->width());
    1.76 +
    1.77 +    builder->fsCodeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
    1.78 +
    1.79 +    int width = this->width();
    1.80 +    const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni);
    1.81 +    const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
    1.82 +
    1.83 +    builder->fsCodeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords2D.c_str(), fRadius, imgInc);
    1.84 +
    1.85 +    // Manually unroll loop because some drivers don't; yields 20-30% speedup.
    1.86 +    for (int i = 0; i < width; i++) {
    1.87 +        SkString index;
    1.88 +        SkString kernelIndex;
    1.89 +        index.appendS32(i);
    1.90 +        kernel.appendArrayAccess(index.c_str(), &kernelIndex);
    1.91 +        builder->fsCodeAppendf("\t\t%s += ", outputColor);
    1.92 +        builder->fsAppendTextureLookup(samplers[0], "coord");
    1.93 +        if (this->useBounds()) {
    1.94 +            const char* bounds = builder->getUniformCStr(fBoundsUni);
    1.95 +            const char* component = this->direction() == Gr1DKernelEffect::kY_Direction ? "y" : "x";
    1.96 +            builder->fsCodeAppendf(" * float(coord.%s >= %s.x && coord.%s <= %s.y)",
    1.97 +                component, bounds, component, bounds);
    1.98 +        }
    1.99 +        builder->fsCodeAppendf(" * %s;\n", kernelIndex.c_str());
   1.100 +        builder->fsCodeAppendf("\t\tcoord += %s;\n", imgInc);
   1.101 +    }
   1.102 +
   1.103 +    SkString modulate;
   1.104 +    GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
   1.105 +    builder->fsCodeAppend(modulate.c_str());
   1.106 +}
   1.107 +
   1.108 +void GrGLConvolutionEffect::setData(const GrGLUniformManager& uman,
   1.109 +                                    const GrDrawEffect& drawEffect) {
   1.110 +    const GrConvolutionEffect& conv = drawEffect.castEffect<GrConvolutionEffect>();
   1.111 +    GrTexture& texture = *conv.texture(0);
   1.112 +    // the code we generated was for a specific kernel radius
   1.113 +    SkASSERT(conv.radius() == fRadius);
   1.114 +    float imageIncrement[2] = { 0 };
   1.115 +    float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
   1.116 +    switch (conv.direction()) {
   1.117 +        case Gr1DKernelEffect::kX_Direction:
   1.118 +            imageIncrement[0] = 1.0f / texture.width();
   1.119 +            break;
   1.120 +        case Gr1DKernelEffect::kY_Direction:
   1.121 +            imageIncrement[1] = ySign / texture.height();
   1.122 +            break;
   1.123 +        default:
   1.124 +            GrCrash("Unknown filter direction.");
   1.125 +    }
   1.126 +    uman.set2fv(fImageIncrementUni, 1, imageIncrement);
   1.127 +    if (conv.useBounds()) {
   1.128 +        const float* bounds = conv.bounds();
   1.129 +        if (Gr1DKernelEffect::kY_Direction == conv.direction() &&
   1.130 +            texture.origin() != kTopLeft_GrSurfaceOrigin) {
   1.131 +            uman.set2f(fBoundsUni, 1.0f - bounds[1], 1.0f - bounds[0]);
   1.132 +        } else {
   1.133 +            uman.set2f(fBoundsUni, bounds[0], bounds[1]);
   1.134 +        }
   1.135 +    }
   1.136 +    uman.set1fv(fKernelUni, this->width(), conv.kernel());
   1.137 +}
   1.138 +
   1.139 +GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrDrawEffect& drawEffect,
   1.140 +                                                    const GrGLCaps&) {
   1.141 +    const GrConvolutionEffect& conv = drawEffect.castEffect<GrConvolutionEffect>();
   1.142 +    EffectKey key = conv.radius();
   1.143 +    key <<= 2;
   1.144 +    if (conv.useBounds()) {
   1.145 +        key |= 0x2;
   1.146 +        key |= GrConvolutionEffect::kY_Direction == conv.direction() ? 0x1 : 0x0;
   1.147 +    }
   1.148 +    return key;
   1.149 +}
   1.150 +
   1.151 +///////////////////////////////////////////////////////////////////////////////
   1.152 +
   1.153 +GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
   1.154 +                                         Direction direction,
   1.155 +                                         int radius,
   1.156 +                                         const float* kernel,
   1.157 +                                         bool useBounds,
   1.158 +                                         float bounds[2])
   1.159 +    : Gr1DKernelEffect(texture, direction, radius), fUseBounds(useBounds) {
   1.160 +    SkASSERT(radius <= kMaxKernelRadius);
   1.161 +    SkASSERT(NULL != kernel);
   1.162 +    int width = this->width();
   1.163 +    for (int i = 0; i < width; i++) {
   1.164 +        fKernel[i] = kernel[i];
   1.165 +    }
   1.166 +    memcpy(fBounds, bounds, sizeof(fBounds));
   1.167 +}
   1.168 +
   1.169 +GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
   1.170 +                                         Direction direction,
   1.171 +                                         int radius,
   1.172 +                                         float gaussianSigma,
   1.173 +                                         bool useBounds,
   1.174 +                                         float bounds[2])
   1.175 +    : Gr1DKernelEffect(texture, direction, radius), fUseBounds(useBounds) {
   1.176 +    SkASSERT(radius <= kMaxKernelRadius);
   1.177 +    int width = this->width();
   1.178 +
   1.179 +    float sum = 0.0f;
   1.180 +    float denom = 1.0f / (2.0f * gaussianSigma * gaussianSigma);
   1.181 +    for (int i = 0; i < width; ++i) {
   1.182 +        float x = static_cast<float>(i - this->radius());
   1.183 +        // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
   1.184 +        // is dropped here, since we renormalize the kernel below.
   1.185 +        fKernel[i] = sk_float_exp(- x * x * denom);
   1.186 +        sum += fKernel[i];
   1.187 +    }
   1.188 +    // Normalize the kernel
   1.189 +    float scale = 1.0f / sum;
   1.190 +    for (int i = 0; i < width; ++i) {
   1.191 +        fKernel[i] *= scale;
   1.192 +    }
   1.193 +    memcpy(fBounds, bounds, sizeof(fBounds));
   1.194 +}
   1.195 +
   1.196 +GrConvolutionEffect::~GrConvolutionEffect() {
   1.197 +}
   1.198 +
   1.199 +const GrBackendEffectFactory& GrConvolutionEffect::getFactory() const {
   1.200 +    return GrTBackendEffectFactory<GrConvolutionEffect>::getInstance();
   1.201 +}
   1.202 +
   1.203 +bool GrConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
   1.204 +    const GrConvolutionEffect& s = CastEffect<GrConvolutionEffect>(sBase);
   1.205 +    return (this->texture(0) == s.texture(0) &&
   1.206 +            this->radius() == s.radius() &&
   1.207 +            this->direction() == s.direction() &&
   1.208 +            this->useBounds() == s.useBounds() &&
   1.209 +            0 == memcmp(fBounds, s.fBounds, sizeof(fBounds)) &&
   1.210 +            0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
   1.211 +}
   1.212 +
   1.213 +///////////////////////////////////////////////////////////////////////////////
   1.214 +
   1.215 +GR_DEFINE_EFFECT_TEST(GrConvolutionEffect);
   1.216 +
   1.217 +GrEffectRef* GrConvolutionEffect::TestCreate(SkRandom* random,
   1.218 +                                             GrContext*,
   1.219 +                                             const GrDrawTargetCaps&,
   1.220 +                                             GrTexture* textures[]) {
   1.221 +    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
   1.222 +                                      GrEffectUnitTest::kAlphaTextureIdx;
   1.223 +    Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
   1.224 +    int radius = random->nextRangeU(1, kMaxKernelRadius);
   1.225 +    float kernel[kMaxKernelWidth];
   1.226 +    for (size_t i = 0; i < SK_ARRAY_COUNT(kernel); ++i) {
   1.227 +        kernel[i] = random->nextSScalar1();
   1.228 +    }
   1.229 +    float bounds[2];
   1.230 +    for (size_t i = 0; i < SK_ARRAY_COUNT(bounds); ++i) {
   1.231 +        bounds[i] = random->nextF();
   1.232 +    }
   1.233 +
   1.234 +    bool useBounds = random->nextBool();
   1.235 +    return GrConvolutionEffect::Create(textures[texIdx],
   1.236 +                                       dir,
   1.237 +                                       radius,
   1.238 +                                       kernel,
   1.239 +                                       useBounds,
   1.240 +                                       bounds);
   1.241 +}

mercurial