aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/effects/SkMorphologyImageFilter.cpp
diff options
context:
space:
mode:
authorGravatar senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-08-22 20:51:19 +0000
committerGravatar senorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-08-22 20:51:19 +0000
commit84207c42789e67ef377befb0c9057b9b73fbd6e3 (patch)
tree2de3ef8b70b68b36fcc9a02a28f05e8c62b596c4 /src/effects/SkMorphologyImageFilter.cpp
parentab2246fa08a9fbea134818a057f40d4f73e71f9c (diff)
Move the code for the GPU implementation of morphology effects from GrContext
and GrMorphologyEffect.* into SkMorphologyImageFilter.cpp. Review URL: https://codereview.appspot.com/6458065/ git-svn-id: http://skia.googlecode.com/svn/trunk@5241 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/effects/SkMorphologyImageFilter.cpp')
-rw-r--r--src/effects/SkMorphologyImageFilter.cpp280
1 files changed, 264 insertions, 16 deletions
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index 34b3c09d62..cbfa5cf0e4 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -13,6 +13,9 @@
#if SK_SUPPORT_GPU
#include "GrContext.h"
#include "GrTexture.h"
+#include "GrGpu.h"
+#include "gl/GrGLProgramStage.h"
+#include "effects/Gr1DKernelEffect.h"
#endif
SkMorphologyImageFilter::SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer)
@@ -218,29 +221,274 @@ bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
return true;
}
-GrTexture* SkDilateImageFilter::onFilterImageGPU(GrTexture* src, const SkRect& rect) {
#if SK_SUPPORT_GPU
+
+///////////////////////////////////////////////////////////////////////////////
+
+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:
+
+ enum MorphologyType {
+ kErode_MorphologyType,
+ kDilate_MorphologyType,
+ };
+
+ GrMorphologyEffect(GrTexture*, 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:
+ GR_DECLARE_CUSTOM_STAGE_TEST;
+
+ typedef Gr1DKernelEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLMorphologyEffect : public GrGLProgramStage {
+public:
+ GrGLMorphologyEffect (const GrProgramStageFactory& factory,
+ const GrCustomStage& stage);
+
+ virtual void setupVariables(GrGLShaderBuilder* builder) 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, const GrGLCaps& caps);
+
+ virtual void setData(const GrGLUniformManager&,
+ const GrCustomStage&,
+ const GrRenderTarget*,
+ int stageNum) SK_OVERRIDE;
+
+private:
+ int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
+
+ int fRadius;
+ GrMorphologyEffect::MorphologyType fType;
+ GrGLUniformManager::UniformHandle fImageIncrementUni;
+
+ typedef GrGLProgramStage INHERITED;
+};
+
+GrGLMorphologyEffect::GrGLMorphologyEffect(const GrProgramStageFactory& factory,
+ const GrCustomStage& stage)
+ : GrGLProgramStage(factory)
+ , fImageIncrementUni(GrGLUniformManager::kInvalidUniformHandle) {
+ const GrMorphologyEffect& m = static_cast<const GrMorphologyEffect&>(stage);
+ fRadius = m.radius();
+ fType = m.type();
+}
+
+void GrGLMorphologyEffect::setupVariables(GrGLShaderBuilder* builder) {
+ fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+ kVec2f_GrSLType, "ImageIncrement");
+}
+
+void GrGLMorphologyEffect::emitFS(GrGLShaderBuilder* builder,
+ const char* outputColor,
+ const char* inputColor,
+ const char* samplerName) {
+ SkString* code = &builder->fFSCode;
+
+ const char* func;
+ switch (fType) {
+ case GrMorphologyEffect::kErode_MorphologyType:
+ code->appendf("\t\tvec4 value = vec4(1, 1, 1, 1);\n");
+ func = "min";
+ break;
+ case GrMorphologyEffect::kDilate_MorphologyType:
+ code->appendf("\t\tvec4 value = vec4(0, 0, 0, 0);\n");
+ func = "max";
+ break;
+ default:
+ GrCrash("Unexpected type");
+ func = ""; // suppress warning
+ break;
+ }
+ const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
+
+ code->appendf("\t\tvec2 coord = %s - %d.0 * %s;\n",
+ builder->fSampleCoords.c_str(), fRadius, imgInc);
+ code->appendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width());
+ code->appendf("\t\t\tvalue = %s(value, ", func);
+ builder->emitTextureLookup(samplerName, "coord");
+ code->appendf(");\n");
+ code->appendf("\t\t\tcoord += %s;\n", imgInc);
+ code->appendf("\t\t}\n");
+ code->appendf("\t\t%s = value%s;\n", outputColor, builder->fModulate.c_str());
+}
+
+GrGLProgramStage::StageKey GrGLMorphologyEffect::GenKey(const GrCustomStage& s,
+ const GrGLCaps& caps) {
+ 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 GrGLUniformManager& uman,
+ const GrCustomStage& data,
+ const GrRenderTarget*,
+ int stageNum) {
+ const Gr1DKernelEffect& kern =
+ static_cast<const Gr1DKernelEffect&>(data);
+ GrGLTexture& texture =
+ *static_cast<GrGLTexture*>(data.texture(0));
+ // 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.");
+ }
+ uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture,
+ Direction direction,
+ int radius,
+ MorphologyType type)
+ : Gr1DKernelEffect(texture, 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 (INHERITED::isEqual(sBase) &&
+ this->radius() == s.radius() &&
+ this->direction() == s.direction() &&
+ this->type() == s.type());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrMorphologyEffect);
+
+GrCustomStage* GrMorphologyEffect::TestCreate(SkRandom* random,
+ GrContext* context,
+ GrTexture* textures[]) {
+ int texIdx = random->nextBool() ? GrCustomStageUnitTest::kSkiaPMTextureIdx :
+ GrCustomStageUnitTest::kAlphaTextureIdx;
+ Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
+ static const int kMaxRadius = 10;
+ int radius = random->nextRangeU(1, kMaxRadius);
+ MorphologyType type = random->nextBool() ? GrMorphologyEffect::kErode_MorphologyType :
+ GrMorphologyEffect::kDilate_MorphologyType;
+
+ return SkNEW_ARGS(GrMorphologyEffect, (textures[texIdx], dir, radius, type));
+}
+
+namespace {
+
+void apply_morphology_pass(GrContext* context,
+ GrTexture* texture,
+ const SkRect& rect,
+ int radius,
+ GrMorphologyEffect::MorphologyType morphType,
+ Gr1DKernelEffect::Direction direction) {
+ GrMatrix sampleM;
+ sampleM.setIDiv(texture->width(), texture->height());
+ GrPaint paint;
+ paint.reset();
+ paint.textureSampler(0)->reset(sampleM);
+ paint.textureSampler(0)->setCustomStage(SkNEW_ARGS(GrMorphologyEffect, (texture, direction, radius, morphType)))->unref();
+ context->drawRect(paint, rect);
+}
+
+GrTexture* apply_morphology(GrTexture* srcTexture,
+ const GrRect& rect,
+ GrMorphologyEffect::MorphologyType morphType,
+ SkISize radius) {
+ GrContext* context = srcTexture->getContext();
+ srcTexture->ref();
+ GrContext::AutoMatrix avm(context, GrMatrix::I());
+ GrContext::AutoClip acs(context, GrRect::MakeWH(SkIntToScalar(srcTexture->width()),
+ SkIntToScalar(srcTexture->height())));
+ GrTextureDesc desc;
+ desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+ desc.fWidth = SkScalarCeilToInt(rect.width());
+ desc.fHeight = SkScalarCeilToInt(rect.height());
+ desc.fConfig = kRGBA_8888_GrPixelConfig;
+ if (radius.fWidth > 0) {
+ GrAutoScratchTexture ast(context, desc);
+ GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
+ apply_morphology_pass(context, srcTexture, rect, radius.fWidth,
+ morphType, Gr1DKernelEffect::kX_Direction);
+ SkIRect clearRect = SkIRect::MakeXYWH(
+ SkScalarFloorToInt(rect.fLeft),
+ SkScalarFloorToInt(rect.fBottom),
+ SkScalarFloorToInt(rect.width()),
+ radius.fHeight);
+ context->clear(&clearRect, 0x0);
+ srcTexture->unref();
+ srcTexture = ast.detach();
+ }
+ if (radius.fHeight > 0) {
+ GrAutoScratchTexture ast(context, desc);
+ GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
+ apply_morphology_pass(context, srcTexture, rect, radius.fHeight,
+ morphType, Gr1DKernelEffect::kY_Direction);
+ srcTexture->unref();
+ srcTexture = ast.detach();
+ }
+ return srcTexture;
+}
+
+};
+
+GrTexture* SkDilateImageFilter::onFilterImageGPU(GrTexture* src, const SkRect& rect) {
SkAutoTUnref<GrTexture> input(this->getInputResultAsTexture(src, rect));
- return src->getContext()->applyMorphology(input.get(), rect,
- GrContext::kDilate_MorphologyType,
- radius());
-#else
- SkDEBUGFAIL("Should not call in GPU-less build");
- return NULL;
-#endif
+ return apply_morphology(src, rect, GrMorphologyEffect::kDilate_MorphologyType, radius());
}
GrTexture* SkErodeImageFilter::onFilterImageGPU(GrTexture* src, const SkRect& rect) {
-#if SK_SUPPORT_GPU
SkAutoTUnref<GrTexture> input(this->getInputResultAsTexture(src, rect));
- return src->getContext()->applyMorphology(input.get(), rect,
- GrContext::kErode_MorphologyType,
- radius());
-#else
- SkDEBUGFAIL("Should not call in GPU-less build");
- return NULL;
-#endif
+ return apply_morphology(src, rect, GrMorphologyEffect::kErode_MorphologyType, radius());
}
+#endif
+
SK_DEFINE_FLATTENABLE_REGISTRAR(SkDilateImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR(SkErodeImageFilter)