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