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 +}