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
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 80dd25e..b7ada61 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -9,9 +9,11 @@
#include "GrContext.h"
+#include "effects/GrMorphologyEffect.h"
+#include "effects/GrConvolutionEffect.h"
+
#include "GrBufferAllocPool.h"
#include "GrClipIterator.h"
-#include "effects/GrConvolutionEffect.h"
#include "GrGpu.h"
#include "GrIndexBuffer.h"
#include "GrInOrderDrawBuffer.h"
@@ -242,23 +244,6 @@
sb->numSamples(), v);
}
-void build_kernel(float sigma, float* kernel, int kernelWidth) {
- int halfWidth = (kernelWidth - 1) / 2;
- float sum = 0.0f;
- float denom = 1.0f / (2.0f * sigma * sigma);
- for (int i = 0; i < kernelWidth; ++i) {
- float x = static_cast<float>(i - halfWidth);
- // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
- // is dropped here, since we renormalize the kernel below.
- kernel[i] = sk_float_exp(- x * x * denom);
- sum += kernel[i];
- }
- // Normalize the kernel
- float scale = 1.0f / sum;
- for (int i = 0; i < kernelWidth; ++i)
- kernel[i] *= scale;
-}
-
void scale_rect(SkRect* rect, float xScale, float yScale) {
rect->fLeft = SkScalarMul(rect->fLeft, SkFloatToScalar(xScale));
rect->fTop = SkScalarMul(rect->fTop, SkFloatToScalar(yScale));
@@ -266,15 +251,14 @@
rect->fBottom = SkScalarMul(rect->fBottom, SkFloatToScalar(yScale));
}
-float adjust_sigma(float sigma, int *scaleFactor, int *halfWidth,
- int *kernelWidth) {
+float adjust_sigma(float sigma, int *scaleFactor, int *radius) {
*scaleFactor = 1;
while (sigma > MAX_BLUR_SIGMA) {
*scaleFactor *= 2;
sigma *= 0.5f;
}
- *halfWidth = static_cast<int>(ceilf(sigma * 3.0f));
- *kernelWidth = *halfWidth * 2 + 1;
+ *radius = static_cast<int>(ceilf(sigma * 3.0f));
+ GrAssert(*radius <= GrConvolutionEffect::kMaxKernelRadius);
return sigma;
}
@@ -282,10 +266,8 @@
GrTexture* texture,
const SkRect& rect,
int radius,
- GrSamplerState::Filter filter,
- GrSamplerState::FilterDirection direction) {
- GrAssert(filter == GrSamplerState::kErode_Filter ||
- filter == GrSamplerState::kDilate_Filter);
+ GrContext::MorphologyType morphType,
+ Gr1DKernelEffect::Direction direction) {
GrRenderTarget* target = gpu->drawState()->getRenderTarget();
GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
@@ -293,31 +275,31 @@
drawState->setRenderTarget(target);
GrMatrix sampleM;
sampleM.setIDiv(texture->width(), texture->height());
- drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode, filter,
- sampleM);
- drawState->sampler(0)->setMorphologyRadius(radius);
- drawState->sampler(0)->setFilterDirection(direction);
+ drawState->sampler(0)->reset(sampleM);
+ SkAutoTUnref<GrCustomStage> morph(
+ new GrMorphologyEffect(direction, radius, morphType));
+ drawState->sampler(0)->setCustomStage(morph);
drawState->setTexture(0, texture);
gpu->drawSimpleRect(rect, NULL, 1 << 0);
}
-void convolve(GrGpu* gpu,
- GrTexture* texture,
- const SkRect& rect,
- const float* kernel,
- int kernelWidth,
- GrSamplerState::FilterDirection direction) {
+void convolve_gaussian(GrGpu* gpu,
+ GrTexture* texture,
+ const SkRect& rect,
+ float sigma,
+ int radius,
+ Gr1DKernelEffect::Direction direction) {
GrRenderTarget* target = gpu->drawState()->getRenderTarget();
GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
GrDrawState* drawState = gpu->drawState();
drawState->setRenderTarget(target);
GrMatrix sampleM;
sampleM.setIDiv(texture->width(), texture->height());
- drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
- GrSamplerState::kConvolution_Filter,
- sampleM);
- drawState->sampler(0)->setCustomStage(
- new GrConvolutionEffect(direction, kernelWidth, kernel));
+ drawState->sampler(0)->reset(sampleM);
+ SkAutoTUnref<GrConvolutionEffect> conv(new
+ GrConvolutionEffect(direction, radius));
+ conv->setGaussianKernel(sigma);
+ drawState->sampler(0)->setCustomStage(conv);
drawState->setTexture(0, texture);
gpu->drawSimpleRect(rect, NULL, 1 << 0);
}
@@ -2085,10 +2067,10 @@
GrTexture* origTexture = srcTexture;
GrAutoMatrix avm(this, GrMatrix::I());
SkIRect clearRect;
- int scaleFactorX, halfWidthX, kernelWidthX;
- int scaleFactorY, halfWidthY, kernelWidthY;
- sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &halfWidthX, &kernelWidthX);
- sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &halfWidthY, &kernelWidthY);
+ int scaleFactorX, radiusX;
+ int scaleFactorY, radiusY;
+ sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &radiusX);
+ sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &radiusY);
SkRect srcRect(rect);
scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
@@ -2138,21 +2120,17 @@
srcRect.roundOut(&srcIRect);
if (sigmaX > 0.0f) {
- SkAutoTMalloc<float> kernelStorageX(kernelWidthX);
- float* kernelX = kernelStorageX.get();
- build_kernel(sigmaX, kernelX, kernelWidthX);
-
if (scaleFactorX > 1) {
- // Clear out a halfWidth to the right of the srcRect to prevent the
+ // Clear out a radius to the right of the srcRect to prevent the
// X convolution from reading garbage.
clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
- halfWidthX, srcIRect.height());
+ radiusX, srcIRect.height());
this->clear(&clearRect, 0x0);
}
this->setRenderTarget(dstTexture->asRenderTarget());
- convolve(fGpu, srcTexture, srcRect, kernelX, kernelWidthX,
- GrSamplerState::kX_FilterDirection);
+ convolve_gaussian(fGpu, srcTexture, srcRect, sigmaX, radiusX,
+ Gr1DKernelEffect::kX_Direction);
SkTSwap(srcTexture, dstTexture);
if (temp2 && dstTexture == origTexture) {
dstTexture = temp2->texture();
@@ -2160,21 +2138,17 @@
}
if (sigmaY > 0.0f) {
- SkAutoTMalloc<float> kernelStorageY(kernelWidthY);
- float* kernelY = kernelStorageY.get();
- build_kernel(sigmaY, kernelY, kernelWidthY);
-
if (scaleFactorY > 1 || sigmaX > 0.0f) {
- // Clear out a halfWidth below the srcRect to prevent the Y
+ // Clear out a radius below the srcRect to prevent the Y
// convolution from reading garbage.
clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
- srcIRect.width(), halfWidthY);
+ srcIRect.width(), radiusY);
this->clear(&clearRect, 0x0);
}
this->setRenderTarget(dstTexture->asRenderTarget());
- convolve(fGpu, srcTexture, srcRect, kernelY, kernelWidthY,
- GrSamplerState::kY_FilterDirection);
+ convolve_gaussian(fGpu, srcTexture, srcRect, sigmaY, radiusY,
+ Gr1DKernelEffect::kY_Direction);
SkTSwap(srcTexture, dstTexture);
if (temp2 && dstTexture == origTexture) {
dstTexture = temp2->texture();
@@ -2210,7 +2184,7 @@
GrTexture* GrContext::applyMorphology(GrTexture* srcTexture,
const GrRect& rect,
GrTexture* temp1, GrTexture* temp2,
- GrSamplerState::Filter filter,
+ MorphologyType morphType,
SkISize radius) {
ASSERT_OWNED_RESOURCE(srcTexture);
GrRenderTarget* oldRenderTarget = this->getRenderTarget();
@@ -2220,8 +2194,8 @@
SkIntToScalar(srcTexture->height())));
if (radius.fWidth > 0) {
this->setRenderTarget(temp1->asRenderTarget());
- apply_morphology(fGpu, srcTexture, rect, radius.fWidth, filter,
- GrSamplerState::kX_FilterDirection);
+ apply_morphology(fGpu, srcTexture, rect, radius.fWidth, morphType,
+ Gr1DKernelEffect::kX_Direction);
SkIRect clearRect = SkIRect::MakeXYWH(
SkScalarFloorToInt(rect.fLeft),
SkScalarFloorToInt(rect.fBottom),
@@ -2232,8 +2206,8 @@
}
if (radius.fHeight > 0) {
this->setRenderTarget(temp2->asRenderTarget());
- apply_morphology(fGpu, srcTexture, rect, radius.fHeight, filter,
- GrSamplerState::kY_FilterDirection);
+ apply_morphology(fGpu, srcTexture, rect, radius.fHeight, morphType,
+ Gr1DKernelEffect::kY_Direction);
srcTexture = temp2;
}
this->setRenderTarget(oldRenderTarget);
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 411b2c5..871dbb3 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1451,14 +1451,14 @@
GrAutoScratchTexture temp1(context, desc), temp2(context, desc);
texture = context->applyMorphology(texture, rect,
temp1.texture(), temp2.texture(),
- GrSamplerState::kDilate_Filter,
+ GrContext::kDilate_MorphologyType,
radius);
texture->ref();
} else if (filter->asAnErode(&radius)) {
GrAutoScratchTexture temp1(context, desc), temp2(context, desc);
texture = context->applyMorphology(texture, rect,
temp1.texture(), temp2.texture(),
- GrSamplerState::kErode_Filter,
+ GrContext::kErode_MorphologyType,
radius);
texture->ref();
}
diff --git a/src/gpu/effects/Gr1DKernelEffect.h b/src/gpu/effects/Gr1DKernelEffect.h
new file mode 100644
index 0000000..b6e116f
--- /dev/null
+++ b/src/gpu/effects/Gr1DKernelEffect.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef Gr1DKernelEffect_DEFINED
+#define Gr1DKernelEffect_DEFINED
+
+#include "GrCustomStage.h"
+
+/**
+ * Base class for 1D kernel effects. The kernel operates either in X or Y and
+ * has a pixel radius. The kernel is specified in the src texture's space
+ * and the kernel center is pinned to a texel's center. The radius specifies
+ * the number of texels on either side of the center texel in X or Y that are
+ * read. Since the center pixel is also read, the total width is one larger than
+ * two times the radius.
+ */
+class Gr1DKernelEffect : public GrCustomStage {
+
+public:
+ enum Direction {
+ kX_Direction,
+ kY_Direction,
+ };
+
+ Gr1DKernelEffect(Direction direction,
+ int radius)
+ : fDirection(direction)
+ , fRadius(radius) {}
+
+ virtual ~Gr1DKernelEffect() {};
+
+ static int WidthFromRadius(int radius) { return 2 * radius + 1; }
+
+ int radius() const { return fRadius; }
+ int width() const { return WidthFromRadius(fRadius); }
+ Direction direction() const { return fDirection; }
+
+private:
+
+ Direction fDirection;
+ int fRadius;
+
+ typedef GrCustomStage INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrConvolutionEffect.cpp b/src/gpu/effects/GrConvolutionEffect.cpp
index 0dd1606c..9296ecc 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 @@
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 @@
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;
+ }
+}
diff --git a/src/gpu/effects/GrConvolutionEffect.h b/src/gpu/effects/GrConvolutionEffect.h
index df164d5..fd6883b 100644
--- a/src/gpu/effects/GrConvolutionEffect.h
+++ b/src/gpu/effects/GrConvolutionEffect.h
@@ -8,40 +8,58 @@
#ifndef GrConvolutionEffect_DEFINED
#define GrConvolutionEffect_DEFINED
-#include "GrCustomStage.h"
-#include "GrSamplerState.h" // for MAX_KENEL_WIDTH, FilterDirection
+#include "Gr1DKernelEffect.h"
class GrGLConvolutionEffect;
-class GrConvolutionEffect : public GrCustomStage {
+/**
+ * A convolution effect. The kernel is specified as an array of 2 * half-width
+ * + 1 weights. Each texel is multiplied by it's weight and summed to determine
+ * the output color. The output color is modulated by the input color.
+ */
+class GrConvolutionEffect : public Gr1DKernelEffect {
public:
- GrConvolutionEffect(GrSamplerState::FilterDirection direction,
- unsigned int kernelWidth, const float* kernel);
+ GrConvolutionEffect(Direction, int halfWidth, const float* kernel = NULL);
virtual ~GrConvolutionEffect();
- unsigned int width() const { return fKernelWidth; }
+ void setKernel(const float* kernel) {
+ memcpy(fKernel, kernel, this->width());
+ }
+
+ /**
+ * Helper to set the kernel to a Gaussian. Replaces the existing kernel.
+ */
+ void setGaussianKernel(float sigma);
+
const float* kernel() const { return fKernel; }
- GrSamplerState::FilterDirection direction() const { return fDirection; }
static const char* Name() { return "Convolution"; }
typedef GrGLConvolutionEffect GLProgramStage;
virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
- virtual bool isEqual(const GrCustomStage *) const SK_OVERRIDE;
+ virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
+
+ enum {
+ // This was decided based on the min allowed value for the max texture
+ // samples per fragment program run in DX9SM2 (32). A sigma param of 4.0
+ // on a blur filter gives a kernel width of 25 while a sigma of 5.0
+ // would exceed a 32 wide kernel.
+ kMaxKernelRadius = 12,
+ // With a C++11 we could have a constexpr version of WidthFromRadius()
+ // and not have to duplicate this calculation.
+ kMaxKernelWidth = 2 * kMaxKernelRadius + 1,
+ };
protected:
- GrSamplerState::FilterDirection fDirection;
- unsigned int fKernelWidth;
- float fKernel[MAX_KERNEL_WIDTH];
-
+ float fKernel[kMaxKernelWidth];
private:
- typedef GrCustomStage INHERITED;
+ typedef Gr1DKernelEffect INHERITED;
};
#endif
diff --git a/src/gpu/effects/GrMorphologyEffect.cpp b/src/gpu/effects/GrMorphologyEffect.cpp
new file mode 100644
index 0000000..614ef32
--- /dev/null
+++ b/src/gpu/effects/GrMorphologyEffect.cpp
@@ -0,0 +1,169 @@
+/*
+ * 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 "GrMorphologyEffect.h"
+#include "gl/GrGLProgramStage.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLTexture.h"
+#include "GrProgramStageFactory.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLMorphologyEffect : public GrGLProgramStage {
+public:
+ GrGLMorphologyEffect (const GrProgramStageFactory& factory,
+ const GrCustomStage& stage);
+
+ virtual void setupVariables(GrGLShaderBuilder* state,
+ int stage) SK_OVERRIDE;
+ virtual void emitVS(GrGLShaderBuilder* state,
+ const char* vertexCoords) SK_OVERRIDE;
+ virtual void emitFS(GrGLShaderBuilder* state,
+ const char* outputColor,
+ const char* inputColor,
+ const char* samplerName) SK_OVERRIDE;
+
+ static inline StageKey GenKey(const GrCustomStage& s);
+
+ virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE;
+ virtual void setData(const GrGLInterface*,
+ const GrGLTexture&,
+ const GrCustomStage&,
+ int stageNum) SK_OVERRIDE;
+
+private:
+ int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
+
+ int fRadius;
+ GrMorphologyEffect::MorphologyType fType;
+ const GrGLShaderVar* fImageIncrementVar;
+ GrGLint fImageIncrementLocation;
+
+ typedef GrGLProgramStage INHERITED;
+};
+
+GrGLMorphologyEffect ::GrGLMorphologyEffect(const GrProgramStageFactory& factory,
+ const GrCustomStage& stage)
+ : GrGLProgramStage(factory)
+ , fImageIncrementVar(NULL)
+ , fImageIncrementLocation(0) {
+ const GrMorphologyEffect& m = static_cast<const GrMorphologyEffect&>(stage);
+ fRadius = m.radius();
+ fType = m.type();
+}
+
+void GrGLMorphologyEffect::setupVariables(GrGLShaderBuilder* state, int stage) {
+ fImageIncrementVar = &state->addUniform(
+ GrGLShaderBuilder::kBoth_VariableLifetime,
+ kVec2f_GrSLType, "uImageIncrement", stage);
+}
+
+void GrGLMorphologyEffect::emitVS(GrGLShaderBuilder* state,
+ const char* vertexCoords) {
+ GrStringBuilder* code = &state->fVSCode;
+ code->appendf("\t\t%s -= vec2(%d, %d) * %s;\n",
+ vertexCoords, fRadius, fRadius,
+ fImageIncrementVar->getName().c_str());
+}
+
+void GrGLMorphologyEffect::initUniforms(const GrGLInterface* gl,
+ int programID) {
+ GR_GL_CALL_RET(gl, fImageIncrementLocation,
+ GetUniformLocation(programID,
+ fImageIncrementVar->getName().c_str()));
+}
+
+void GrGLMorphologyEffect ::emitFS(GrGLShaderBuilder* state,
+ const char* outputColor,
+ const char* inputColor,
+ const char* samplerName) {
+ GrStringBuilder* code = &state->fFSCode;
+ const char* texFunc = "texture2D";
+ bool complexCoord = false;
+
+ const char* func;
+ switch (fType) {
+ case GrContext::kErode_MorphologyType:
+ state->fFSCode.appendf("\t\tvec4 value = vec4(1, 1, 1, 1);\n");
+ func = "min";
+ break;
+ case GrContext::kDilate_MorphologyType:
+ state->fFSCode.appendf("\t\tvec4 value = vec4(0, 0, 0, 0);\n");
+ func = "max";
+ break;
+ default:
+ GrCrash("Unexpected type");
+ break;
+ }
+
+ code->appendf("\t\tvec2 coord = %s;\n", state->fSampleCoords.c_str());
+ code->appendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width());
+ state->fFSCode.appendf("\t\t\tvalue = %s(value, ", func);
+ state->emitTextureLookup(samplerName, "coord");
+ state->fFSCode.appendf(");\n");
+ code->appendf("\t\t\tcoord += %s;\n",
+ fImageIncrementVar->getName().c_str());
+ code->appendf("\t\t}\n");
+
+ state->fFSCode.appendf("\t\t%s = value%s;\n",
+ outputColor,
+ state->fModulate.c_str());
+}
+
+GrGLProgramStage::StageKey GrGLMorphologyEffect::GenKey(
+ const GrCustomStage& s) {
+ const GrMorphologyEffect& m = static_cast<const GrMorphologyEffect&>(s);
+ StageKey key = static_cast<StageKey>(m.radius());
+ key |= (m.type() << 8);
+ return key;
+}
+
+void GrGLMorphologyEffect ::setData(const GrGLInterface* gl,
+ const GrGLTexture& texture,
+ const GrCustomStage& data,
+ int stageNum) {
+ const Gr1DKernelEffect& kern =
+ static_cast<const Gr1DKernelEffect&>(data);
+ // the code we generated was for a specific kernel radius
+ GrAssert(kern.radius() == fRadius);
+ float imageIncrement[2] = { 0 };
+ switch (kern.direction()) {
+ case Gr1DKernelEffect::kX_Direction:
+ imageIncrement[0] = 1.0f / texture.width();
+ break;
+ case Gr1DKernelEffect::kY_Direction:
+ imageIncrement[1] = 1.0f / texture.height();
+ break;
+ default:
+ GrCrash("Unknown filter direction.");
+ }
+ GR_GL_CALL(gl, Uniform2fv(fImageIncrementLocation, 1, imageIncrement));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrMorphologyEffect::GrMorphologyEffect(Direction direction,
+ int radius,
+ MorphologyType type)
+ : Gr1DKernelEffect(direction, radius)
+ , fType(type) {
+}
+
+GrMorphologyEffect::~GrMorphologyEffect() {
+}
+
+const GrProgramStageFactory& GrMorphologyEffect::getFactory() const {
+ return GrTProgramStageFactory<GrMorphologyEffect>::getInstance();
+}
+
+bool GrMorphologyEffect::isEqual(const GrCustomStage& sBase) const {
+ const GrMorphologyEffect& s =
+ static_cast<const GrMorphologyEffect&>(sBase);
+ return (this->radius() == s.radius() &&
+ this->direction() == s.direction() &&
+ this->type() == s.type());
+}
diff --git a/src/gpu/effects/GrMorphologyEffect.h b/src/gpu/effects/GrMorphologyEffect.h
new file mode 100644
index 0000000..c784460
--- /dev/null
+++ b/src/gpu/effects/GrMorphologyEffect.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrMorphologyEffect_DEFINED
+#define GrMorphologyEffect_DEFINED
+
+#include "GrContext.h"
+#include "Gr1DKernelEffect.h"
+
+class GrGLMorphologyEffect;
+
+/**
+ * Morphology effects. Depending upon the type of morphology, either the
+ * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the
+ * kernel is selected as the new color. The new color is modulated by the input
+ * color.
+ */
+class GrMorphologyEffect : public Gr1DKernelEffect {
+
+public:
+
+ typedef GrContext::MorphologyType MorphologyType;
+
+ GrMorphologyEffect(Direction, int radius, MorphologyType);
+ virtual ~GrMorphologyEffect();
+
+ MorphologyType type() const { return fType; }
+
+ static const char* Name() { return "Morphology"; }
+
+ typedef GrGLMorphologyEffect GLProgramStage;
+
+ virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
+ virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
+
+protected:
+
+ MorphologyType fType;
+
+private:
+
+ typedef Gr1DKernelEffect INHERITED;
+};
+#endif
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 9a78a6e..a49aa61 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -95,18 +95,6 @@
s->appendS32(stage);
}
-inline void convolve_param_names(int stage, GrStringBuilder* k, GrStringBuilder* i) {
- *k = "uKernel";
- k->appendS32(stage);
- *i = "uImageIncrement";
- i->appendS32(stage);
-}
-
-inline void image_increment_param_name(int stage, GrStringBuilder* i) {
- *i = "uImageIncrement";
- i->appendS32(stage);
-}
-
inline void tex_domain_name(int stage, GrStringBuilder* s) {
*s = "uTexDom";
s->appendS32(stage);
@@ -629,7 +617,7 @@
const GrProgramStageFactory& factory =
customStages[s]->getFactory();
programData->fCustomStage[s] =
- factory.createGLInstance(customStages[s]);
+ factory.createGLInstance(*customStages[s]);
}
this->genStageCode(gl,
s,
@@ -752,7 +740,7 @@
const GrProgramStageFactory& factory =
customStages[s]->getFactory();
programData->fCustomStage[s] =
- factory.createGLInstance(customStages[s]);
+ factory.createGLInstance(*customStages[s]);
}
this->genStageCode(gl, s,
fProgramDesc.fStages[s],
@@ -1186,21 +1174,6 @@
GrAssert(kUnusedUniform != locations.fTexDomUni);
}
- GrStringBuilder kernelName, imageIncrementName;
- convolve_param_names(s, &kernelName, &imageIncrementName);
- if (kUseUniform == locations.fKernelUni) {
- GL_CALL_RET(locations.fKernelUni,
- GetUniformLocation(progID, kernelName.c_str()));
- GrAssert(kUnusedUniform != locations.fKernelUni);
- }
-
- if (kUseUniform == locations.fImageIncrementUni) {
- GL_CALL_RET(locations.fImageIncrementUni,
- GetUniformLocation(progID,
- imageIncrementName.c_str()));
- GrAssert(kUnusedUniform != locations.fImageIncrementUni);
- }
-
if (NULL != programData->fCustomStage[s]) {
programData->fCustomStage[s]->
initUniforms(gl.interface(), progID);
@@ -1411,65 +1384,6 @@
}
-void genMorphologyVS(int stageNum,
- const StageDesc& desc,
- GrGLShaderBuilder* segments,
- GrGLProgram::StageUniLocations* locations,
- const char** imageIncrementName,
- const char* varyingVSName) {
-
- GrStringBuilder iiName;
- image_increment_param_name(stageNum, &iiName);
- const GrGLShaderVar* imgInc =
- &segments->addUniform(
- GrGLShaderBuilder::kBoth_VariableLifetime, kVec2f_GrSLType,
- iiName.c_str());
- *imageIncrementName = imgInc->getName().c_str();
-
- locations->fImageIncrementUni = kUseUniform;
- segments->fVSCode.appendf("\t%s -= vec2(%d, %d) * %s;\n",
- varyingVSName, desc.fKernelWidth,
- desc.fKernelWidth, *imageIncrementName);
-}
-
-void genMorphologyFS(int stageNum,
- const StageDesc& desc,
- GrGLShaderBuilder* segments,
- const char* samplerName,
- const char* imageIncrementName,
- const char* fsOutColor,
- GrStringBuilder& texFunc) {
- GrStringBuilder valueVar("value");
- valueVar.appendS32(stageNum);
- GrStringBuilder coordVar("coord");
- coordVar.appendS32(stageNum);
- bool isDilate = StageDesc::kDilate_FetchMode == desc.fFetchMode;
-
- if (isDilate) {
- segments->fFSCode.appendf("\tvec4 %s = vec4(0, 0, 0, 0);\n",
- valueVar.c_str());
- } else {
- segments->fFSCode.appendf("\tvec4 %s = vec4(1, 1, 1, 1);\n",
- valueVar.c_str());
- }
- segments->fFSCode.appendf("\tvec2 %s = %s;\n",
- coordVar.c_str(),
- segments->fSampleCoords.c_str());
- segments->fFSCode.appendf("\tfor (int i = 0; i < %d; i++) {\n",
- desc.fKernelWidth * 2 + 1);
- segments->fFSCode.appendf("\t\t%s = %s(%s, %s(%s, %s)%s);\n",
- valueVar.c_str(), isDilate ? "max" : "min",
- valueVar.c_str(), texFunc.c_str(),
- samplerName, coordVar.c_str(),
- segments->fSwizzle.c_str());
- segments->fFSCode.appendf("\t\t%s += %s;\n",
- coordVar.c_str(),
- imageIncrementName);
- segments->fFSCode.appendf("\t}\n");
- segments->fFSCode.appendf("\t%s = %s%s;\n", fsOutColor,
- valueVar.c_str(), segments->fModulate.c_str());
-}
-
}
void GrGLProgram::genStageCode(const GrGLContextInfo& gl,
@@ -1562,12 +1476,6 @@
GrGLShaderVar* kernel = NULL;
const char* imageIncrementName = NULL;
- if (StageDesc::kDilate_FetchMode == desc.fFetchMode ||
- StageDesc::kErode_FetchMode == desc.fFetchMode) {
- genMorphologyVS(stageNum, desc, segments, locations,
- &imageIncrementName, varyingVSName);
- }
-
if (NULL != customStage) {
segments->fVSCode.appendf("\t{ // stage %d %s\n",
stageNum, customStage->name());
@@ -1677,15 +1585,6 @@
gen2x2FS(stageNum, segments, locations,
samplerName.c_str(), texelSizeName, fsOutColor, texFunc);
break;
- case StageDesc::kConvolution_FetchMode:
- GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask));
- break;
- case StageDesc::kDilate_FetchMode:
- case StageDesc::kErode_FetchMode:
- GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask));
- genMorphologyFS(stageNum, desc, segments,
- samplerName.c_str(), imageIncrementName, fsOutColor, texFunc);
- break;
default:
if (desc.fInConfigFlags & kMulByAlphaMask) {
// only one of the mul by alpha flags should be set
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 189e876..bfb1f1c 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -106,13 +106,10 @@
kCustomTextureDomain_OptFlagBit = 1 << 2,
kIsEnabled_OptFlagBit = 1 << 7
};
- // Convolution is obsolete; left in for testing only
+
enum FetchMode {
kSingle_FetchMode,
k2x2_FetchMode,
- kConvolution_FetchMode,
- kErode_FetchMode,
- kDilate_FetchMode,
kFetchModeCnt,
};
@@ -179,7 +176,6 @@
uint8_t fInConfigFlags; // bitfield of InConfigFlags values
uint8_t fFetchMode; // casts to enum FetchMode
uint8_t fCoordMapping; // casts to enum CoordMapping
- uint8_t fKernelWidth;
/** Non-zero if user-supplied code will write the stage's
contribution to the fragment shader. */
@@ -268,16 +264,12 @@
GrGLint fSamplerUni;
GrGLint fRadial2Uni;
GrGLint fTexDomUni;
- GrGLint fKernelUni;
- GrGLint fImageIncrementUni;
void reset() {
fTextureMatrixUni = kUnusedUniform;
fNormalizedTexelSizeUni = kUnusedUniform;
fSamplerUni = kUnusedUniform;
fRadial2Uni = kUnusedUniform;
fTexDomUni = kUnusedUniform;
- fKernelUni = kUnusedUniform;
- fImageIncrementUni = kUnusedUniform;
}
};
diff --git a/src/gpu/gl/GrGLProgramStage.cpp b/src/gpu/gl/GrGLProgramStage.cpp
index 70d1186..487b468 100644
--- a/src/gpu/gl/GrGLProgramStage.cpp
+++ b/src/gpu/gl/GrGLProgramStage.cpp
@@ -25,11 +25,9 @@
}
-void GrGLProgramStage::setData(const GrGLInterface*,
- const GrGLTexture&,
- GrCustomStage*,
+void GrGLProgramStage::setData(const GrGLInterface*,
+ const GrGLTexture&,
+ const GrCustomStage&,
int stageNum) {
-
-
}
diff --git a/src/gpu/gl/GrGLProgramStage.h b/src/gpu/gl/GrGLProgramStage.h
index 52d7200..e10d13b 100644
--- a/src/gpu/gl/GrGLProgramStage.h
+++ b/src/gpu/gl/GrGLProgramStage.h
@@ -32,7 +32,12 @@
class GrGLProgramStage {
public:
- typedef GrCustomStage::StageKey StageKey ;
+ typedef GrCustomStage::StageKey StageKey;
+ enum {
+ // the number of bits in StageKey available to GenKey
+ kProgramStageKeyBits = GrProgramStageFactory::kProgramStageKeyBits,
+ };
+
// TODO: redundant with GrGLProgram.cpp
enum {
kUnusedUniform = -1,
@@ -79,7 +84,7 @@
created in emit*(). */
virtual void setData(const GrGLInterface* gl,
const GrGLTexture& texture,
- GrCustomStage* stage,
+ const GrCustomStage& stage,
int stageNum);
const char* name() const { return fFactory.name(); }
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index 8b28c9f..29c7900 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -2102,9 +2102,6 @@
case GrSamplerState::k4x4Downsample_Filter:
return GR_GL_LINEAR;
case GrSamplerState::kNearest_Filter:
- case GrSamplerState::kConvolution_Filter:
- case GrSamplerState::kErode_Filter:
- case GrSamplerState::kDilate_Filter:
return GR_GL_NEAREST;
default:
GrAssert(!"Unknown filter type");
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index 2ac26d5..2448fa4 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -7,8 +7,10 @@
#include "GrGpuGL.h"
-#include "GrBinHashKey.h"
#include "effects/GrConvolutionEffect.h"
+#include "effects/GrMorphologyEffect.h"
+
+#include "GrBinHashKey.h"
#include "GrCustomStage.h"
#include "GrGLProgramStage.h"
#include "GrGLSL.h"
@@ -176,6 +178,75 @@
return r->nextF() > .5f;
}
+typedef GrGLProgram::StageDesc StageDesc;
+// TODO: Effects should be able to register themselves for inclusion in the
+// randomly generated shaders. They should be able to configure themselves
+// randomly.
+GrCustomStage* create_random_effect(StageDesc* stageDesc,
+ GrRandom* random) {
+ enum EffectType {
+ kConvolution_EffectType,
+ kErode_EffectType,
+ kDilate_EffectType,
+
+ kEffectCount
+ };
+
+ // TODO: Remove this when generator doesn't apply this non-custom-stage
+ // notion to custom stages automatically.
+ static const uint32_t kMulByAlphaMask =
+ StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag |
+ StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag;
+
+ static const Gr1DKernelEffect::Direction gKernelDirections[] = {
+ Gr1DKernelEffect::kX_Direction,
+ Gr1DKernelEffect::kY_Direction
+ };
+
+ // TODO: When matrices are property of the custom-stage then remove the
+ // no-persp flag code below.
+ int effect = random_int(random, kEffectCount);
+ switch (effect) {
+ case kConvolution_EffectType: {
+ int direction = random_int(random, 2);
+ int kernelRadius = random_int(random, 1, 4);
+ float kernel[GrConvolutionEffect::kMaxKernelWidth];
+ for (int i = 0; i < GrConvolutionEffect::kMaxKernelWidth; i++) {
+ kernel[i] = random->nextF();
+ }
+ // does not work with perspective or mul-by-alpha-mask
+ stageDesc->fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
+ stageDesc->fInConfigFlags &= ~kMulByAlphaMask;
+ return new GrConvolutionEffect(gKernelDirections[direction],
+ kernelRadius,
+ kernel);
+ }
+ case kErode_EffectType: {
+ int direction = random_int(random, 2);
+ int kernelRadius = random_int(random, 1, 4);
+ // does not work with perspective or mul-by-alpha-mask
+ stageDesc->fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
+ stageDesc->fInConfigFlags &= ~kMulByAlphaMask;
+ return new GrMorphologyEffect(gKernelDirections[direction],
+ kernelRadius,
+ GrContext::kErode_MorphologyType);
+ }
+ case kDilate_EffectType: {
+ int direction = random_int(random, 2);
+ int kernelRadius = random_int(random, 1, 4);
+ // does not work with perspective or mul-by-alpha-mask
+ stageDesc->fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
+ stageDesc->fInConfigFlags &= ~kMulByAlphaMask;
+ return new GrMorphologyEffect(gKernelDirections[direction],
+ kernelRadius,
+ GrContext::kDilate_MorphologyType);
+ }
+ default:
+ GrCrash("Unexpected custom effect type");
+ }
+ return NULL;
+}
+
}
bool GrGpuGL::programUnitTest() {
@@ -250,7 +321,7 @@
pdesc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
}
- GrCustomStage* customStages[GrDrawState::kNumStages];
+ SkAutoTUnref<GrCustomStage> customStages[GrDrawState::kNumStages];
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
// enable the stage?
@@ -270,56 +341,34 @@
StageDesc& stage = pdesc.fStages[s];
stage.fCustomStageKey = 0;
- customStages[s] = NULL;
stage.fOptFlags = STAGE_OPTS[random_int(&random, GR_ARRAY_COUNT(STAGE_OPTS))];
stage.fInConfigFlags = IN_CONFIG_FLAGS[random_int(&random, GR_ARRAY_COUNT(IN_CONFIG_FLAGS))];
stage.fCoordMapping = random_int(&random, StageDesc::kCoordMappingCnt);
stage.fFetchMode = random_int(&random, StageDesc::kFetchModeCnt);
- // convolution shaders don't work with persp tex matrix
- if (stage.fFetchMode == StageDesc::kConvolution_FetchMode ||
- stage.fFetchMode == StageDesc::kDilate_FetchMode ||
- stage.fFetchMode == StageDesc::kErode_FetchMode) {
- stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
- }
stage.setEnabled(VertexUsesStage(s, pdesc.fVertexLayout));
static const uint32_t kMulByAlphaMask =
StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag |
StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag;
- switch (stage.fFetchMode) {
- case StageDesc::kSingle_FetchMode:
- stage.fKernelWidth = 0;
- break;
- case StageDesc::kConvolution_FetchMode:
- case StageDesc::kDilate_FetchMode:
- case StageDesc::kErode_FetchMode:
- stage.fKernelWidth = random_int(&random, 2, 8);
- stage.fInConfigFlags &= ~kMulByAlphaMask;
- break;
- case StageDesc::k2x2_FetchMode:
- stage.fKernelWidth = 0;
- stage.fInConfigFlags &= ~kMulByAlphaMask;
- break;
+ if (StageDesc::k2x2_FetchMode == stage.fFetchMode) {
+ stage.fInConfigFlags &= ~kMulByAlphaMask;
}
- // TODO: is there a more elegant way to express this?
- if (stage.fFetchMode == StageDesc::kConvolution_FetchMode) {
- int direction = random_int(&random, 2);
- float kernel[MAX_KERNEL_WIDTH];
- for (int i = 0; i < stage.fKernelWidth; i++) {
- kernel[i] = random.nextF();
+ bool useCustomEffect = random_bool(&random);
+ if (useCustomEffect) {
+ customStages[s].reset(create_random_effect(&stage, &random));
+ if (NULL != customStages[s]) {
+ stage.fCustomStageKey =
+ customStages[s]->getFactory().glStageKey(*customStages[s]);
}
- customStages[s] = new GrConvolutionEffect(
- (GrSamplerState::FilterDirection)direction,
- stage.fKernelWidth, kernel);
- stage.fCustomStageKey =
- customStages[s]->getFactory().glStageKey(customStages[s]);
}
}
CachedData cachedData;
- if (!program.genProgram(this->glContextInfo(), customStages,
- &cachedData)) {
+ GR_STATIC_ASSERT(sizeof(customStages) ==
+ GrDrawState::kNumStages * sizeof(GrCustomStage*));
+ GrCustomStage** stages = reinterpret_cast<GrCustomStage**>(&customStages);
+ if (!program.genProgram(this->glContextInfo(), stages, &cachedData)) {
return false;
}
DeleteProgram(this->glInterface(), &cachedData);
@@ -469,32 +518,6 @@
}
}
-void GrGpuGL::flushConvolution(int s) {
- const GrSamplerState& sampler = this->getDrawState().getSampler(s);
- int kernelUni = fProgramData->fUniLocations.fStages[s].fKernelUni;
- if (GrGLProgram::kUnusedUniform != kernelUni) {
- GL_CALL(Uniform1fv(kernelUni, sampler.getKernelWidth(),
- sampler.getKernel()));
- }
- int imageIncrementUni = fProgramData->fUniLocations.fStages[s].fImageIncrementUni;
- if (GrGLProgram::kUnusedUniform != imageIncrementUni) {
- const GrGLTexture* texture =
- static_cast<const GrGLTexture*>(this->getDrawState().getTexture(s));
- float imageIncrement[2] = { 0 };
- switch (sampler.getFilterDirection()) {
- case GrSamplerState::kX_FilterDirection:
- imageIncrement[0] = 1.0f / texture->width();
- break;
- case GrSamplerState::kY_FilterDirection:
- imageIncrement[1] = 1.0f / texture->height();
- break;
- default:
- GrCrash("Unknown filter direction.");
- }
- GL_CALL(Uniform2fv(imageIncrementUni, 1, imageIncrement));
- }
-}
-
void GrGpuGL::flushTexelSize(int s) {
const int& uni = fProgramData->fUniLocations.fStages[s].fNormalizedTexelSizeUni;
if (GrGLProgram::kUnusedUniform != uni) {
@@ -692,8 +715,6 @@
this->flushRadial2(s);
- this->flushConvolution(s);
-
this->flushTexelSize(s);
this->flushTextureDomain(s);
@@ -706,7 +727,7 @@
this->getDrawState().getTexture(s));
fProgramData->fCustomStage[s]->setData(
this->glInterface(), *texture,
- sampler.getCustomStage(), s);
+ *sampler.getCustomStage(), s);
}
}
}
@@ -875,7 +896,7 @@
GrCustomStage* customStage = sampler.getCustomStage();
if (customStage) {
const GrProgramStageFactory& factory = customStage->getFactory();
- stage->fCustomStageKey = factory.glStageKey(customStage);
+ stage->fCustomStageKey = factory.glStageKey(*customStage);
customStages[index] = customStage;
} else {
stage->fCustomStageKey = 0;
@@ -1027,16 +1048,6 @@
case GrSamplerState::k4x4Downsample_Filter:
stage.fFetchMode = StageDesc::k2x2_FetchMode;
break;
- // performs fKernelWidth texture2D()s
- case GrSamplerState::kConvolution_Filter:
- stage.fFetchMode = StageDesc::kConvolution_FetchMode;
- break;
- case GrSamplerState::kDilate_Filter:
- stage.fFetchMode = StageDesc::kDilate_FetchMode;
- break;
- case GrSamplerState::kErode_Filter:
- stage.fFetchMode = StageDesc::kErode_FetchMode;
- break;
default:
GrCrash("Unexpected filter!");
break;
@@ -1083,13 +1094,6 @@
}
}
- if (sampler.getFilter() == GrSamplerState::kDilate_Filter ||
- sampler.getFilter() == GrSamplerState::kErode_Filter) {
- stage.fKernelWidth = sampler.getKernelWidth();
- } else {
- stage.fKernelWidth = 0;
- }
-
setup_custom_stage(&stage, sampler, customStages,
&fCurrentProgram, s);
@@ -1098,7 +1102,6 @@
stage.fCoordMapping = (StageDesc::CoordMapping) 0;
stage.fInConfigFlags = 0;
stage.fFetchMode = (StageDesc::FetchMode) 0;
- stage.fKernelWidth = 0;
stage.fCustomStageKey = 0;
customStages[s] = NULL;
}