diff options
author | 2012-05-31 18:40:36 +0000 | |
---|---|---|
committer | 2012-05-31 18:40:36 +0000 | |
commit | b505a128efae9debcaa9642bade90bab5525d477 (patch) | |
tree | 8ba6050a03c34a2f1165915c44f361e1512406b9 /src/gpu/effects/GrConvolutionEffect.cpp | |
parent | 0d10280190c411b18feb569a2248552047d1aa93 (diff) |
Implement morphology as a custom effect
Review URL: http://codereview.appspot.com/6250073/
git-svn-id: http://skia.googlecode.com/svn/trunk@4102 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/gpu/effects/GrConvolutionEffect.cpp')
-rw-r--r-- | src/gpu/effects/GrConvolutionEffect.cpp | 167 |
1 files changed, 86 insertions, 81 deletions
diff --git a/src/gpu/effects/GrConvolutionEffect.cpp b/src/gpu/effects/GrConvolutionEffect.cpp index 0dd1606c81..9296ecc93e 100644 --- a/src/gpu/effects/GrConvolutionEffect.cpp +++ b/src/gpu/effects/GrConvolutionEffect.cpp @@ -11,14 +11,11 @@ #include "gl/GrGLTexture.h" #include "GrProgramStageFactory.h" -///////////////////////////////////////////////////////////////////// - class GrGLConvolutionEffect : public GrGLProgramStage { - public: - GrGLConvolutionEffect(const GrProgramStageFactory& factory, - const GrCustomStage* stage); + const GrCustomStage& stage); + virtual void setupVariables(GrGLShaderBuilder* state, int stage) SK_OVERRIDE; virtual void emitVS(GrGLShaderBuilder* state, @@ -27,38 +24,38 @@ public: const char* outputColor, const char* inputColor, const char* samplerName) SK_OVERRIDE; + virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE; - virtual void setData(const GrGLInterface*, + virtual void setData(const GrGLInterface*, const GrGLTexture&, - GrCustomStage*, + const GrCustomStage&, int stageNum) SK_OVERRIDE; - static inline StageKey GenKey(const GrCustomStage* s); - -protected: - - unsigned int fKernelWidth; - const GrGLShaderVar* fKernelVar; - const GrGLShaderVar* fImageIncrementVar; - - GrGLint fKernelLocation; - GrGLint fImageIncrementLocation; + static inline StageKey GenKey(const GrCustomStage&); private: + int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); } + + int fRadius; + const GrGLShaderVar* fKernelVar; + GrGLint fKernelLocation; + const GrGLShaderVar* fImageIncrementVar; + GrGLint fImageIncrementLocation; typedef GrGLProgramStage INHERITED; }; -GrGLConvolutionEffect::GrGLConvolutionEffect( - const GrProgramStageFactory& factory, - const GrCustomStage* data) +GrGLConvolutionEffect::GrGLConvolutionEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage) : GrGLProgramStage(factory) , fKernelVar(NULL) - , fImageIncrementVar(NULL) , fKernelLocation(0) + , fImageIncrementVar(NULL) , fImageIncrementLocation(0) { - fKernelWidth = static_cast<const GrConvolutionEffect*>(data)->width(); + const GrConvolutionEffect& c = + static_cast<const GrConvolutionEffect&>(stage); + fRadius = c.radius(); } void GrGLConvolutionEffect::setupVariables(GrGLShaderBuilder* state, @@ -68,124 +65,132 @@ void GrGLConvolutionEffect::setupVariables(GrGLShaderBuilder* state, kVec2f_GrSLType, "uImageIncrement", stage); fKernelVar = &state->addUniform( GrGLShaderBuilder::kFragment_VariableLifetime, - kFloat_GrSLType, "uKernel", stage, fKernelWidth); + kFloat_GrSLType, "uKernel", stage, this->width()); fImageIncrementLocation = kUseUniform; fKernelLocation = kUseUniform; } void GrGLConvolutionEffect::emitVS(GrGLShaderBuilder* state, - const char* vertexCoords) { + const char* vertexCoords) { GrStringBuilder* code = &state->fVSCode; - float scale = (fKernelWidth - 1) * 0.5f; - code->appendf("\t\t%s -= vec2(%g, %g) * %s;\n", - vertexCoords, scale, scale, + code->appendf("\t\t%s -= vec2(%d, %d) * %s;\n", + vertexCoords, fRadius, fRadius, fImageIncrementVar->getName().c_str()); - } void GrGLConvolutionEffect::emitFS(GrGLShaderBuilder* state, - const char* outputColor, - const char* inputColor, - const char* samplerName) { + const char* outputColor, + const char* inputColor, + const char* samplerName) { GrStringBuilder* code = &state->fFSCode; const char* texFunc = "texture2D"; bool complexCoord = false; - GrStringBuilder modulate; - if (NULL != inputColor) { - modulate.printf(" * %s", inputColor); - } + state->fFSCode.appendf("\t\tvec4 sum = vec4(0, 0, 0, 0);\n"); + + code->appendf("\t\tvec2 coord = %s;\n", state->fSampleCoords.c_str()); + code->appendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width()); // Creates the string "kernel[i]" with workarounds for // possible driver bugs GrStringBuilder kernelIndex; fKernelVar->appendArrayAccess("i", &kernelIndex); - - code->appendf("\t\tvec4 sum = vec4(0, 0, 0, 0);\n"); - code->appendf("\t\tvec2 coord = %s;\n", state->fSampleCoords.c_str()); - code->appendf("\t\tfor (int i = 0; i < %d; i++) {\n", - fKernelWidth); - - code->appendf("\t\t\tsum += "); + state->fFSCode.appendf("\t\t\tsum += "); state->emitTextureLookup(samplerName, "coord"); - code->appendf(" * %s;\n", kernelIndex.c_str()); + state->fFSCode.appendf(" * %s;\n", kernelIndex.c_str()); code->appendf("\t\t\tcoord += %s;\n", fImageIncrementVar->getName().c_str()); code->appendf("\t\t}\n"); - code->appendf("\t\t%s = sum%s;\n", outputColor, modulate.c_str()); + + state->fFSCode.appendf("\t\t%s = sum%s;\n", + outputColor, + state->fModulate.c_str()); } void GrGLConvolutionEffect::initUniforms(const GrGLInterface* gl, int programID) { - GR_GL_CALL_RET(gl, fKernelLocation, - GetUniformLocation(programID, fKernelVar->getName().c_str())); GR_GL_CALL_RET(gl, fImageIncrementLocation, GetUniformLocation(programID, fImageIncrementVar->getName().c_str())); + GR_GL_CALL_RET(gl, fKernelLocation, + GetUniformLocation(programID, fKernelVar->getName().c_str())); } void GrGLConvolutionEffect::setData(const GrGLInterface* gl, const GrGLTexture& texture, - GrCustomStage* data, + const GrCustomStage& data, int stageNum) { - const GrConvolutionEffect* conv = - static_cast<const GrConvolutionEffect*>(data); - // the code we generated was for a specific kernel width - GrAssert(conv->width() == fKernelWidth); - GR_GL_CALL(gl, Uniform1fv(fKernelLocation, - fKernelWidth, - conv->kernel())); + const GrConvolutionEffect& conv = + static_cast<const GrConvolutionEffect&>(data); + // the code we generated was for a specific kernel radius + GrAssert(conv.radius() == fRadius); float imageIncrement[2] = { 0 }; - switch (conv->direction()) { - case GrSamplerState::kX_FilterDirection: + switch (conv.direction()) { + case Gr1DKernelEffect::kX_Direction: imageIncrement[0] = 1.0f / texture.width(); break; - case GrSamplerState::kY_FilterDirection: - imageIncrement[1] = 1.0f / texture.width(); + case Gr1DKernelEffect::kY_Direction: + imageIncrement[1] = 1.0f / texture.height(); break; default: GrCrash("Unknown filter direction."); } GR_GL_CALL(gl, Uniform2fv(fImageIncrementLocation, 1, imageIncrement)); + + GR_GL_CALL(gl, Uniform1fv(fKernelLocation, this->width(), conv.kernel())); } GrGLProgramStage::StageKey GrGLConvolutionEffect::GenKey( - const GrCustomStage* s) { - return static_cast<const GrConvolutionEffect*>(s)->width(); + const GrCustomStage& s) { + return static_cast<const GrConvolutionEffect&>(s).radius(); } -///////////////////////////////////////////////////////////////////// - -GrConvolutionEffect::GrConvolutionEffect( - GrSamplerState::FilterDirection direction, - unsigned int kernelWidth, - const float* kernel) - : fDirection (direction) - , fKernelWidth (kernelWidth) { - GrAssert(kernelWidth <= MAX_KERNEL_WIDTH); - for (unsigned int i = 0; i < kernelWidth; i++) { - fKernel[i] = kernel[i]; +/////////////////////////////////////////////////////////////////////////////// + +GrConvolutionEffect::GrConvolutionEffect(Direction direction, + int radius, + const float* kernel) + : Gr1DKernelEffect(direction, radius) { + GrAssert(radius <= kMaxKernelRadius); + int width = this->width(); + if (NULL != kernel) { + for (int i = 0; i < width; i++) { + fKernel[i] = kernel[i]; + } } } GrConvolutionEffect::~GrConvolutionEffect() { - } const GrProgramStageFactory& GrConvolutionEffect::getFactory() const { return GrTProgramStageFactory<GrConvolutionEffect>::getInstance(); } -bool GrConvolutionEffect::isEqual(const GrCustomStage * sBase) const { - const GrConvolutionEffect* s = - static_cast<const GrConvolutionEffect*>(sBase); - - return (fKernelWidth == s->fKernelWidth && - fDirection == s->fDirection && - 0 == memcmp(fKernel, s->fKernel, fKernelWidth * sizeof(float))); +bool GrConvolutionEffect::isEqual(const GrCustomStage& sBase) const { + const GrConvolutionEffect& s = + static_cast<const GrConvolutionEffect&>(sBase); + return (this->radius() == s.radius() && + this->direction() == s.direction() && + 0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float))); } - - +void GrConvolutionEffect::setGaussianKernel(float sigma) { + int width = this->width(); + float sum = 0.0f; + float denom = 1.0f / (2.0f * sigma * sigma); + for (int i = 0; i < width; ++i) { + float x = static_cast<float>(i - this->radius()); + // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian + // is dropped here, since we renormalize the kernel below. + fKernel[i] = sk_float_exp(- x * x * denom); + sum += fKernel[i]; + } + // Normalize the kernel + float scale = 1.0f / sum; + for (int i = 0; i < width; ++i) { + fKernel[i] *= scale; + } +} |