Chromium Code Reviews| Index: src/effects/SkMorphologyImageFilter.cpp |
| diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp |
| index e895cacf13832605bf3558cc3e9a44a519987bc6..b2dc5b0f050b14d4a542f50e7c2a15acb2a82063 100644 |
| --- a/src/effects/SkMorphologyImageFilter.cpp |
| +++ b/src/effects/SkMorphologyImageFilter.cpp |
| @@ -298,14 +298,20 @@ public: |
| kDilate_MorphologyType, |
| }; |
| - static GrFragmentProcessor* Create(GrTexture* tex, Direction dir, int radius, |
| - MorphologyType type) { |
| + static GrFragmentProcessor* Create(GrTexture* tex, Direction dir, int radius, MorphologyType type) { |
| return SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type)); |
| } |
| + static GrFragmentProcessor* Create(GrTexture* tex, Direction dir, int radius, MorphologyType type, |
|
reed1
2015/01/20 13:53:05
nit: 100col max
|
| + float bounds[2]) { |
| + return SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type, bounds)); |
| + } |
| + |
| virtual ~GrMorphologyEffect(); |
| MorphologyType type() const { return fType; } |
| + bool useRange() const { return fUseRange; } |
| + const float* range() const { return fRange; } |
| const char* name() const SK_OVERRIDE { return "Morphology"; } |
| @@ -316,6 +322,8 @@ public: |
| protected: |
| MorphologyType fType; |
| + bool fUseRange; |
| + float fRange[2]; |
| private: |
| bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE; |
| @@ -323,6 +331,7 @@ private: |
| void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE; |
| GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType); |
| + GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType, float bounds[2]); |
| GR_DECLARE_FRAGMENT_PROCESSOR_TEST; |
| @@ -350,8 +359,11 @@ private: |
| int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); } |
| int fRadius; |
| + Gr1DKernelEffect::Direction fDirection; |
| + bool fUseRange; |
| GrMorphologyEffect::MorphologyType fType; |
| - GrGLProgramDataManager::UniformHandle fImageIncrementUni; |
| + GrGLProgramDataManager::UniformHandle fPixelSizeUni; |
| + GrGLProgramDataManager::UniformHandle fRangeUni; |
| typedef GrGLFragmentProcessor INHERITED; |
| }; |
| @@ -359,6 +371,8 @@ private: |
| GrGLMorphologyEffect::GrGLMorphologyEffect(const GrProcessor& proc) { |
| const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>(); |
| fRadius = m.radius(); |
| + fDirection = m.direction(); |
| + fUseRange = m.useRange(); |
| fType = m.type(); |
| } |
| @@ -368,9 +382,14 @@ void GrGLMorphologyEffect::emitCode(GrGLFPBuilder* builder, |
| const char* inputColor, |
| const TransformedCoordsArray& coords, |
| const TextureSamplerArray& samplers) { |
| - fImageIncrementUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, |
| - kVec2f_GrSLType, kDefault_GrSLPrecision, |
| - "ImageIncrement"); |
| + fPixelSizeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, |
| + kFloat_GrSLType, kDefault_GrSLPrecision, |
| + "PixelSize"); |
| + const char* pixelSizeInc = builder->getUniformCStr(fPixelSizeUni); |
| + fRangeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, |
| + kVec2f_GrSLType, kDefault_GrSLPrecision, |
| + "Range"); |
| + const char* range = builder->getUniformCStr(fRangeUni); |
| GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); |
| SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0); |
| @@ -389,14 +408,41 @@ void GrGLMorphologyEffect::emitCode(GrGLFPBuilder* builder, |
| func = ""; // suppress warning |
| break; |
| } |
| - const char* imgInc = builder->getUniformCStr(fImageIncrementUni); |
| - fsBuilder->codeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords2D.c_str(), fRadius, imgInc); |
| - fsBuilder->codeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width()); |
| + const char* dir; |
| + switch (fDirection) { |
| + case Gr1DKernelEffect::kX_Direction: |
| + dir = "x"; |
| + break; |
| + case Gr1DKernelEffect::kY_Direction: |
| + dir = "y"; |
| + break; |
| + default: |
| + SkFAIL("Unknown filter direction."); |
| + dir = ""; // suppress warning |
| + } |
| + |
| + // vec2 coord = coord2D; |
| + fsBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str()); |
| + // coord.x -= radius * pixelSize; |
| + fsBuilder->codeAppendf("\t\tcoord.%s -= %d.0 * %s; \n", dir, fRadius, pixelSizeInc); |
| + if (fUseRange) { |
| + // highBound = min(highBound, coord.x + (width-1) * pixelSize); |
| + fsBuilder->codeAppendf("\t\tfloat highBound = min(%s.y, coord.%s + %d * %s);", |
| + range, dir, width() - 1, pixelSizeInc); |
| + // coord.x = max(lowBound, coord.x); |
| + fsBuilder->codeAppendf("\t\tcoord.%s = max(%s.x, coord.%s);", dir, range, dir); |
| + } |
| + fsBuilder->codeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", width()); |
| fsBuilder->codeAppendf("\t\t\t%s = %s(%s, ", outputColor, func, outputColor); |
| fsBuilder->appendTextureLookup(samplers[0], "coord"); |
| fsBuilder->codeAppend(");\n"); |
| - fsBuilder->codeAppendf("\t\t\tcoord += %s;\n", imgInc); |
| + // coord.x += pixelSize; |
| + fsBuilder->codeAppendf("\t\t\tcoord.%s += %s;\n", dir, pixelSizeInc); |
| + if (fUseRange) { |
| + // coord.x = min(highBound, coord.x); |
| + fsBuilder->codeAppendf("\t\t\tcoord.%s = min(highBound, coord.%s);", dir, dir); |
| + } |
| fsBuilder->codeAppend("\t\t}\n"); |
| SkString modulate; |
| GrGLSLMulVarBy4f(&modulate, outputColor, inputColor); |
| @@ -408,27 +454,41 @@ void GrGLMorphologyEffect::GenKey(const GrProcessor& proc, |
| const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>(); |
| uint32_t key = static_cast<uint32_t>(m.radius()); |
| key |= (m.type() << 8); |
| + key |= (m.direction() << 9); |
| + if (m.useRange()) key |= 1 << 10; |
| b->add32(key); |
| } |
| void GrGLMorphologyEffect::setData(const GrGLProgramDataManager& pdman, |
| const GrProcessor& proc) { |
| - const Gr1DKernelEffect& kern = proc.cast<Gr1DKernelEffect>(); |
| - GrTexture& texture = *kern.texture(0); |
| - // the code we generated was for a specific kernel radius |
| - SkASSERT(kern.radius() == fRadius); |
| - float imageIncrement[2] = { 0 }; |
| - switch (kern.direction()) { |
| + const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>(); |
| + GrTexture& texture = *m.texture(0); |
| + // the code we generated was for a specific kernel radius, direction and bound usage |
| + SkASSERT(m.radius() == fRadius); |
| + SkASSERT(m.direction() == fDirection); |
| + SkASSERT(m.useRange() == fUseRange); |
| + |
| + float pixelSize = 0.0f; |
| + switch (fDirection) { |
| case Gr1DKernelEffect::kX_Direction: |
| - imageIncrement[0] = 1.0f / texture.width(); |
| + pixelSize = 1.0f / texture.width(); |
| break; |
| case Gr1DKernelEffect::kY_Direction: |
| - imageIncrement[1] = 1.0f / texture.height(); |
| + pixelSize = 1.0f / texture.height(); |
| break; |
| default: |
| SkFAIL("Unknown filter direction."); |
| } |
| - pdman.set2fv(fImageIncrementUni, 1, imageIncrement); |
| + pdman.set1f(fPixelSizeUni, pixelSize); |
| + |
| + if (fUseRange) { |
| + const float* range = m.range(); |
| + if (fDirection && texture.origin() == kBottomLeft_GrSurfaceOrigin) { |
| + pdman.set2f(fRangeUni, 1.0f - range[1], 1.0f - range[0]); |
| + } else { |
| + pdman.set2f(fRangeUni, range[0], range[1]); |
| + } |
| + } |
| } |
| /////////////////////////////////////////////////////////////////////////////// |
| @@ -438,10 +498,22 @@ GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture, |
| int radius, |
| MorphologyType type) |
| : Gr1DKernelEffect(texture, direction, radius) |
| - , fType(type) { |
| + , fType(type), fUseRange(false) { |
| this->initClassID<GrMorphologyEffect>(); |
| } |
| +GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture, |
| + Direction direction, |
| + int radius, |
| + MorphologyType type, |
| + float range[2]) |
| + : Gr1DKernelEffect(texture, direction, radius) |
| + , fType(type), fUseRange(true) { |
| + this->initClassID<GrMorphologyEffect>(); |
| + fRange[0] = range[0]; |
| + fRange[1] = range[1]; |
| +} |
| + |
| GrMorphologyEffect::~GrMorphologyEffect() { |
| } |
| @@ -456,6 +528,7 @@ bool GrMorphologyEffect::onIsEqual(const GrFragmentProcessor& sBase) const { |
| const GrMorphologyEffect& s = sBase.cast<GrMorphologyEffect>(); |
| return (this->radius() == s.radius() && |
| this->direction() == s.direction() && |
| + this->useRange() == s.useRange() && |
| this->type() == s.type()); |
| } |
| @@ -486,7 +559,26 @@ GrFragmentProcessor* GrMorphologyEffect::TestCreate(SkRandom* random, |
| namespace { |
| -void apply_morphology_pass(GrContext* context, |
| + |
| +void apply_morphology_rect(GrContext* context, |
| + GrTexture* texture, |
| + const SkIRect& srcRect, |
| + const SkIRect& dstRect, |
| + int radius, |
| + GrMorphologyEffect::MorphologyType morphType, |
| + float bounds[2], |
| + Gr1DKernelEffect::Direction direction) { |
| + GrPaint paint; |
| + paint.addColorProcessor(GrMorphologyEffect::Create(texture, |
| + direction, |
| + radius, |
| + morphType, |
| + bounds))->unref(); |
| + context->drawNonAARectToRect(paint, SkMatrix::I(), SkRect::Make(dstRect), |
| + SkRect::Make(srcRect)); |
| +} |
| + |
| +void apply_morphology_rect_no_bounds(GrContext* context, |
| GrTexture* texture, |
| const SkIRect& srcRect, |
| const SkIRect& dstRect, |
| @@ -502,6 +594,51 @@ void apply_morphology_pass(GrContext* context, |
| SkRect::Make(srcRect)); |
| } |
| +void apply_morphology_pass(GrContext* context, |
| + GrTexture* texture, |
| + const SkIRect& srcRect, |
| + const SkIRect& dstRect, |
| + int radius, |
| + GrMorphologyEffect::MorphologyType morphType, |
| + Gr1DKernelEffect::Direction direction) { |
| + float bounds[2] = { 0.0f, 1.0f }; |
| + SkIRect lowerSrcRect = srcRect, lowerDstRect = dstRect; |
| + SkIRect middleSrcRect = srcRect, middleDstRect = dstRect; |
| + SkIRect upperSrcRect = srcRect, upperDstRect = dstRect; |
| + if (direction == Gr1DKernelEffect::kX_Direction) { |
| + bounds[0] = (SkIntToScalar(srcRect.left()) + 0.5f) / texture->width(); |
| + bounds[1] = (SkIntToScalar(srcRect.right()) - 0.5f) / texture->width(); |
| + lowerSrcRect.fRight = srcRect.left() + radius; |
| + lowerDstRect.fRight = dstRect.left() + radius; |
| + upperSrcRect.fLeft = srcRect.right() - radius; |
| + upperDstRect.fLeft = dstRect.right() - radius; |
| + middleSrcRect.inset(radius, 0); |
| + middleDstRect.inset(radius, 0); |
| + } else { |
| + bounds[0] = (SkIntToScalar(srcRect.top()) + 0.5f) / texture->height(); |
| + bounds[1] = (SkIntToScalar(srcRect.bottom()) - 0.5f) / texture->height(); |
| + lowerSrcRect.fBottom = srcRect.top() + radius; |
| + lowerDstRect.fBottom = dstRect.top() + radius; |
| + upperSrcRect.fTop = srcRect.bottom() - radius; |
| + upperDstRect.fTop = dstRect.bottom() - radius; |
| + middleSrcRect.inset(0, radius); |
| + middleDstRect.inset(0, radius); |
| + } |
| + if (middleSrcRect.fLeft - middleSrcRect.fRight >= 0) { |
| + // radius covers srcRect; use bounds over entire draw |
| + apply_morphology_rect(context, texture, srcRect, dstRect, radius, |
| + morphType, bounds, direction); |
| + } else { |
| + // Draw upper and lower margins with bounds; middle without. |
| + apply_morphology_rect(context, texture, lowerSrcRect, lowerDstRect, radius, |
| + morphType, bounds, direction); |
| + apply_morphology_rect(context, texture, upperSrcRect, upperDstRect, radius, |
| + morphType, bounds, direction); |
| + apply_morphology_rect_no_bounds(context, texture, middleSrcRect, middleDstRect, radius, |
| + morphType, direction); |
| + } |
| +} |
| + |
| bool apply_morphology(const SkBitmap& input, |
| const SkIRect& rect, |
| GrMorphologyEffect::MorphologyType morphType, |