blob: 563d1a405137050cde586df01ff33e5338d58218 [file] [log] [blame]
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrConvolutionEffect.h"
#include "gl/GrGLProgramStage.h"
#include "gl/GrGLSL.h"
#include "gl/GrGLTexture.h"
#include "GrProgramStageFactory.h"
/////////////////////////////////////////////////////////////////////
class GrGLConvolutionEffect : public GrGLProgramStage {
public:
GrGLConvolutionEffect(const GrCustomStage* stage);
virtual const char* name() const SK_OVERRIDE;
virtual void setupVSUnis(VarArray* vsUnis, int stage) SK_OVERRIDE;
virtual void setupFSUnis(VarArray* fsUnis, int stage) SK_OVERRIDE;
virtual void emitVS(GrStringBuilder* code,
const char* vertexCoords) SK_OVERRIDE;
virtual void emitFS(GrStringBuilder* code,
const char* outputColor,
const char* inputColor,
const char* samplerName,
const char* sampleCoords) SK_OVERRIDE;
virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE;
virtual void setData(const GrGLInterface*, const GrCustomStage*,
const GrGLTexture*) SK_OVERRIDE;
static inline StageKey GenKey(const GrCustomStage* s);
protected:
int fKernelWidth;
GrGLShaderVar* fKernelVar;
GrGLShaderVar* fImageIncrementVar;
GrGLint fKernelLocation;
GrGLint fImageIncrementLocation;
private:
typedef GrGLProgramStage INHERITED;
};
GrGLConvolutionEffect::GrGLConvolutionEffect(const GrCustomStage* data)
: fKernelVar(NULL)
, fImageIncrementVar(NULL)
, fKernelLocation(0)
, fImageIncrementLocation(0) {
fKernelWidth = static_cast<const GrConvolutionEffect*>(data)->width();
}
const char* GrGLConvolutionEffect::name() const {
return GrConvolutionEffect::Name();
}
void GrGLConvolutionEffect::setupVSUnis(VarArray* vsUnis,
int stage) {
fImageIncrementVar = &vsUnis->push_back();
fImageIncrementVar->setType(kVec2f_GrSLType);
fImageIncrementVar->setTypeModifier(
GrGLShaderVar::kUniform_TypeModifier);
(*fImageIncrementVar->accessName()) = "uImageIncrement";
fImageIncrementVar->accessName()->appendS32(stage);
fImageIncrementVar->setEmitPrecision(true);
fImageIncrementLocation = kUseUniform;
}
void GrGLConvolutionEffect::setupFSUnis(VarArray* fsUnis,
int stage) {
fKernelVar = &fsUnis->push_back();
fKernelVar->setType(kFloat_GrSLType);
fKernelVar->setTypeModifier(
GrGLShaderVar::kUniform_TypeModifier);
fKernelVar->setArrayCount(fKernelWidth);
(*fKernelVar->accessName()) = "uKernel";
fKernelVar->accessName()->appendS32(stage);
fKernelLocation = kUseUniform;
// Image increment is used in both vertex & fragment shaders.
fsUnis->push_back(*fImageIncrementVar).setEmitPrecision(false);
}
void GrGLConvolutionEffect::emitVS(GrStringBuilder* code,
const char* vertexCoords) {
float scale = (fKernelWidth - 1) * 0.5f;
code->appendf("\t\t%s -= vec2(%g, %g) * %s;\n",
vertexCoords, scale, scale,
fImageIncrementVar->getName().c_str());
}
void GrGLConvolutionEffect::emitFS(GrStringBuilder* code,
const char* outputColor,
const char* inputColor,
const char* samplerName,
const char* sampleCoords) {
const char* texFunc = "texture2D";
bool complexCoord = false;
GrStringBuilder modulate;
if (NULL != inputColor) {
modulate.printf(" * %s", inputColor);
}
// 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", sampleCoords);
code->appendf("\t\tfor (int i = 0; i < %d; i++) {\n",
fKernelWidth);
code->appendf("\t\t\tsum += ");
this->emitTextureLookup(code, samplerName, "coord");
code->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());
}
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()));
}
void GrGLConvolutionEffect::setData(const GrGLInterface* gl,
const GrCustomStage* data,
const GrGLTexture* texture) {
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()));
float imageIncrement[2] = { 0 };
switch (conv->direction()) {
case GrSamplerState::kX_FilterDirection:
imageIncrement[0] = 1.0f / texture->width();
break;
case GrSamplerState::kY_FilterDirection:
imageIncrement[1] = 1.0f / texture->width();
break;
default:
GrCrash("Unknown filter direction.");
}
GR_GL_CALL(gl, Uniform2fv(fImageIncrementLocation, 1, imageIncrement));
}
GrGLProgramStage::StageKey GrGLConvolutionEffect::GenKey(
const GrCustomStage* s) {
return static_cast<const GrConvolutionEffect*>(s)->width();
}
/////////////////////////////////////////////////////////////////////
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() {
}
const char* GrConvolutionEffect::name() const {
return Name();
}
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)));
}