Index: src/effects/SkMorphologyImageFilter.cpp |
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp |
index e895cacf13832605bf3558cc3e9a44a519987bc6..5e204183aba449b9491b14caae49d43cd1783798 100644 |
--- a/src/effects/SkMorphologyImageFilter.cpp |
+++ b/src/effects/SkMorphologyImageFilter.cpp |
@@ -303,9 +303,16 @@ public: |
return SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type)); |
} |
+ static GrFragmentProcessor* Create(GrTexture* tex, Direction dir, int radius, |
+ MorphologyType type, 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 +323,8 @@ public: |
protected: |
MorphologyType fType; |
+ bool fUseRange; |
+ float fRange[2]; |
private: |
bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE; |
@@ -323,6 +332,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 +360,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 +372,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 +383,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 +409,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 + %f * %s);", |
+ range, dir, float(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 +455,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 +499,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 +529,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 +560,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 +595,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, |