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