Fix Morphology effects sourcing outside of the crop rect.

BUG=skia:1766

Committed: https://skia.googlesource.com/skia/+/f6be925b5615f07039ce95c3433039694a8d1679

Review URL: https://codereview.chromium.org/781153002
This commit is contained in:
cwallez 2015-01-26 12:20:14 -08:00 committed by Commit bot
parent 66e09a87d4
commit 80a61df691
2 changed files with 195 additions and 20 deletions

View File

@ -12,7 +12,9 @@
#include "SkShader.h" #include "SkShader.h"
#include "SkBlurImageFilter.h" #include "SkBlurImageFilter.h"
#include "SkMorphologyImageFilter.h"
#include "SkColorFilterImageFilter.h" #include "SkColorFilterImageFilter.h"
#include "SkBitmapSource.h"
#include "SkMergeImageFilter.h" #include "SkMergeImageFilter.h"
#include "SkOffsetImageFilter.h" #include "SkOffsetImageFilter.h"
#include "SkTestImageFilters.h" #include "SkTestImageFilters.h"
@ -98,7 +100,28 @@ protected:
return SkString("imagefilterscropped"); return SkString("imagefilterscropped");
} }
virtual SkISize onISize() { return SkISize::Make(400, 640); } virtual SkISize onISize() { return SkISize::Make(400, 880); }
void make_checkerboard() {
fCheckerboard.allocN32Pixels(80, 80);
SkCanvas canvas(fCheckerboard);
canvas.clear(SK_ColorTRANSPARENT);
SkPaint darkPaint;
darkPaint.setColor(0xFF404040);
SkPaint lightPaint;
lightPaint.setColor(0xFFA0A0A0);
for (int y = 0; y < 80; y += 16) {
for (int x = 0; x < 80; x += 16) {
canvas.save();
canvas.translate(SkIntToScalar(x), SkIntToScalar(y));
canvas.drawRect(SkRect::MakeXYWH(0, 0, 8, 8), darkPaint);
canvas.drawRect(SkRect::MakeXYWH(8, 0, 8, 8), lightPaint);
canvas.drawRect(SkRect::MakeXYWH(0, 8, 8, 8), lightPaint);
canvas.drawRect(SkRect::MakeXYWH(8, 8, 8, 8), darkPaint);
canvas.restore();
}
}
}
void draw_frame(SkCanvas* canvas, const SkRect& r) { void draw_frame(SkCanvas* canvas, const SkRect& r) {
SkPaint paint; SkPaint paint;
@ -107,6 +130,10 @@ protected:
canvas->drawRect(r, paint); canvas->drawRect(r, paint);
} }
virtual void onOnceBeforeDraw() SK_OVERRIDE{
make_checkerboard();
}
virtual void onDraw(SkCanvas* canvas) { virtual void onDraw(SkCanvas* canvas) {
void (*drawProc[])(SkCanvas*, const SkRect&, SkImageFilter*) = { void (*drawProc[])(SkCanvas*, const SkRect&, SkImageFilter*) = {
draw_sprite, draw_bitmap, draw_path, draw_paint, draw_text draw_sprite, draw_bitmap, draw_path, draw_paint, draw_text
@ -122,6 +149,9 @@ protected:
SkAutoTUnref<SkImageFilter> cfOffset(SkColorFilterImageFilter::Create(cf.get(), offset.get())); SkAutoTUnref<SkImageFilter> cfOffset(SkColorFilterImageFilter::Create(cf.get(), offset.get()));
SkAutoTUnref<SkImageFilter> erodeX(SkErodeImageFilter::Create(8, 0, NULL, &cropRect));
SkAutoTUnref<SkImageFilter> erodeY(SkErodeImageFilter::Create(0, 8, NULL, &cropRect));
SkImageFilter* filters[] = { SkImageFilter* filters[] = {
NULL, NULL,
SkColorFilterImageFilter::Create(cf.get(), NULL, &cropRect), SkColorFilterImageFilter::Create(cf.get(), NULL, &cropRect),
@ -129,6 +159,10 @@ protected:
SkBlurImageFilter::Create(8.0f, 0.0f, NULL, &cropRect), SkBlurImageFilter::Create(8.0f, 0.0f, NULL, &cropRect),
SkBlurImageFilter::Create(0.0f, 8.0f, NULL, &cropRect), SkBlurImageFilter::Create(0.0f, 8.0f, NULL, &cropRect),
SkBlurImageFilter::Create(8.0f, 8.0f, NULL, &cropRect), SkBlurImageFilter::Create(8.0f, 8.0f, NULL, &cropRect),
SkErodeImageFilter::Create(1, 1, NULL, &cropRect),
SkErodeImageFilter::Create(8, 0, erodeY, &cropRect),
SkErodeImageFilter::Create(0, 8, erodeX, &cropRect),
SkErodeImageFilter::Create(8, 8, NULL, &cropRect),
SkMergeImageFilter::Create(NULL, cfOffset.get(), SkXfermode::kSrcOver_Mode, &cropRect), SkMergeImageFilter::Create(NULL, cfOffset.get(), SkXfermode::kSrcOver_Mode, &cropRect),
SkBlurImageFilter::Create(8.0f, 8.0f, NULL, &bogusRect), SkBlurImageFilter::Create(8.0f, 8.0f, NULL, &bogusRect),
SkColorFilterImageFilter::Create(cf.get(), NULL, &bogusRect), SkColorFilterImageFilter::Create(cf.get(), NULL, &bogusRect),
@ -143,6 +177,8 @@ protected:
for (size_t j = 0; j < SK_ARRAY_COUNT(drawProc); ++j) { for (size_t j = 0; j < SK_ARRAY_COUNT(drawProc); ++j) {
canvas->save(); canvas->save();
for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) { for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
SkPaint paint;
canvas->drawBitmap(fCheckerboard, 0, 0);
drawProc[j](canvas, r, filters[i]); drawProc[j](canvas, r, filters[i]);
canvas->translate(0, DY); canvas->translate(0, DY);
} }
@ -156,6 +192,7 @@ protected:
} }
private: private:
SkBitmap fCheckerboard;
typedef GM INHERITED; typedef GM INHERITED;
}; };

View File

@ -303,9 +303,16 @@ public:
return SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type)); 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(); virtual ~GrMorphologyEffect();
MorphologyType type() const { return fType; } MorphologyType type() const { return fType; }
bool useRange() const { return fUseRange; }
const float* range() const { return fRange; }
const char* name() const SK_OVERRIDE { return "Morphology"; } const char* name() const SK_OVERRIDE { return "Morphology"; }
@ -316,6 +323,8 @@ public:
protected: protected:
MorphologyType fType; MorphologyType fType;
bool fUseRange;
float fRange[2];
private: private:
bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE; bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
@ -323,6 +332,7 @@ private:
void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE; void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType); GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType, float bounds[2]);
GR_DECLARE_FRAGMENT_PROCESSOR_TEST; GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
@ -350,8 +360,11 @@ private:
int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); } int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
int fRadius; int fRadius;
Gr1DKernelEffect::Direction fDirection;
bool fUseRange;
GrMorphologyEffect::MorphologyType fType; GrMorphologyEffect::MorphologyType fType;
GrGLProgramDataManager::UniformHandle fImageIncrementUni; GrGLProgramDataManager::UniformHandle fPixelSizeUni;
GrGLProgramDataManager::UniformHandle fRangeUni;
typedef GrGLFragmentProcessor INHERITED; typedef GrGLFragmentProcessor INHERITED;
}; };
@ -359,6 +372,8 @@ private:
GrGLMorphologyEffect::GrGLMorphologyEffect(const GrProcessor& proc) { GrGLMorphologyEffect::GrGLMorphologyEffect(const GrProcessor& proc) {
const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>(); const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
fRadius = m.radius(); fRadius = m.radius();
fDirection = m.direction();
fUseRange = m.useRange();
fType = m.type(); fType = m.type();
} }
@ -368,9 +383,14 @@ void GrGLMorphologyEffect::emitCode(GrGLFPBuilder* builder,
const char* inputColor, const char* inputColor,
const TransformedCoordsArray& coords, const TransformedCoordsArray& coords,
const TextureSamplerArray& samplers) { const TextureSamplerArray& samplers) {
fImageIncrementUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, fPixelSizeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
kVec2f_GrSLType, kDefault_GrSLPrecision, kFloat_GrSLType, kDefault_GrSLPrecision,
"ImageIncrement"); "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(); GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0); SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
@ -389,14 +409,41 @@ void GrGLMorphologyEffect::emitCode(GrGLFPBuilder* builder,
func = ""; // suppress warning func = ""; // suppress warning
break; break;
} }
const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
fsBuilder->codeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords2D.c_str(), fRadius, imgInc); const char* dir;
fsBuilder->codeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width()); 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->codeAppendf("\t\t\t%s = %s(%s, ", outputColor, func, outputColor);
fsBuilder->appendTextureLookup(samplers[0], "coord"); fsBuilder->appendTextureLookup(samplers[0], "coord");
fsBuilder->codeAppend(");\n"); 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"); fsBuilder->codeAppend("\t\t}\n");
SkString modulate; SkString modulate;
GrGLSLMulVarBy4f(&modulate, outputColor, inputColor); GrGLSLMulVarBy4f(&modulate, outputColor, inputColor);
@ -408,27 +455,41 @@ void GrGLMorphologyEffect::GenKey(const GrProcessor& proc,
const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>(); const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
uint32_t key = static_cast<uint32_t>(m.radius()); uint32_t key = static_cast<uint32_t>(m.radius());
key |= (m.type() << 8); key |= (m.type() << 8);
key |= (m.direction() << 9);
if (m.useRange()) key |= 1 << 10;
b->add32(key); b->add32(key);
} }
void GrGLMorphologyEffect::setData(const GrGLProgramDataManager& pdman, void GrGLMorphologyEffect::setData(const GrGLProgramDataManager& pdman,
const GrProcessor& proc) { const GrProcessor& proc) {
const Gr1DKernelEffect& kern = proc.cast<Gr1DKernelEffect>(); const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
GrTexture& texture = *kern.texture(0); GrTexture& texture = *m.texture(0);
// the code we generated was for a specific kernel radius // the code we generated was for a specific kernel radius, direction and bound usage
SkASSERT(kern.radius() == fRadius); SkASSERT(m.radius() == fRadius);
float imageIncrement[2] = { 0 }; SkASSERT(m.direction() == fDirection);
switch (kern.direction()) { SkASSERT(m.useRange() == fUseRange);
float pixelSize = 0.0f;
switch (fDirection) {
case Gr1DKernelEffect::kX_Direction: case Gr1DKernelEffect::kX_Direction:
imageIncrement[0] = 1.0f / texture.width(); pixelSize = 1.0f / texture.width();
break; break;
case Gr1DKernelEffect::kY_Direction: case Gr1DKernelEffect::kY_Direction:
imageIncrement[1] = 1.0f / texture.height(); pixelSize = 1.0f / texture.height();
break; break;
default: default:
SkFAIL("Unknown filter direction."); 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, int radius,
MorphologyType type) MorphologyType type)
: Gr1DKernelEffect(texture, direction, radius) : Gr1DKernelEffect(texture, direction, radius)
, fType(type) { , fType(type), fUseRange(false) {
this->initClassID<GrMorphologyEffect>(); 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() { GrMorphologyEffect::~GrMorphologyEffect() {
} }
@ -456,6 +529,7 @@ bool GrMorphologyEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
const GrMorphologyEffect& s = sBase.cast<GrMorphologyEffect>(); const GrMorphologyEffect& s = sBase.cast<GrMorphologyEffect>();
return (this->radius() == s.radius() && return (this->radius() == s.radius() &&
this->direction() == s.direction() && this->direction() == s.direction() &&
this->useRange() == s.useRange() &&
this->type() == s.type()); this->type() == s.type());
} }
@ -486,7 +560,26 @@ GrFragmentProcessor* GrMorphologyEffect::TestCreate(SkRandom* random,
namespace { 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, GrTexture* texture,
const SkIRect& srcRect, const SkIRect& srcRect,
const SkIRect& dstRect, const SkIRect& dstRect,
@ -502,6 +595,51 @@ void apply_morphology_pass(GrContext* context,
SkRect::Make(srcRect)); 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, bool apply_morphology(const SkBitmap& input,
const SkIRect& rect, const SkIRect& rect,
GrMorphologyEffect::MorphologyType morphType, GrMorphologyEffect::MorphologyType morphType,