Implement crop rect for the dilate and erode (morphology) filters. This provoked some cleanup on the GPU side: apply_morphology() now deals with SkBitmaps, rather than GrTextures. There's still a clear opportunity for more refactoring between the two filters.

Note: this adds some test cases to the morphology GM, so it will require a rebaseline.

R=bsalomon@google.com, reed@google.com

Review URL: https://codereview.chromium.org/23892011

git-svn-id: http://skia.googlecode.com/svn/trunk@11313 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
senorblanco@chromium.org 2013-09-17 13:41:43 +00:00
parent ef45a646a7
commit 8fcad98791
3 changed files with 152 additions and 73 deletions

View File

@ -8,8 +8,8 @@
#include "gm.h"
#include "SkMorphologyImageFilter.h"
#define WIDTH 640
#define HEIGHT 480
#define WIDTH 700
#define HEIGHT 560
namespace skiagm {
@ -44,6 +44,16 @@ protected:
virtual SkISize onISize() {
return make_isize(WIDTH, HEIGHT);
}
void drawClippedBitmap(SkCanvas* canvas, const SkPaint& paint, int x, int y) {
canvas->save();
canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
canvas->clipRect(SkRect::MakeWH(
SkIntToScalar(fBitmap.width()), SkIntToScalar(fBitmap.height())));
canvas->drawBitmap(fBitmap, 0, 0, &paint);
canvas->restore();
}
virtual void onDraw(SkCanvas* canvas) {
if (!fOnce) {
make_bitmap();
@ -60,26 +70,25 @@ protected:
{ 24, 24, 25, 25 },
};
SkPaint paint;
for (unsigned j = 0; j < 2; ++j) {
SkIRect cropRect = SkIRect::MakeXYWH(25, 20, 100, 80);
for (unsigned j = 0; j < 4; ++j) {
for (unsigned i = 0; i < SK_ARRAY_COUNT(samples); ++i) {
SkScalar x = SkIntToScalar(i * 140), y = SkIntToScalar(j * 140);
if (j) {
const SkIRect* cr = j & 0x02 ? &cropRect : NULL;
if (j & 0x01) {
paint.setImageFilter(new SkErodeImageFilter(
samples[i].fRadiusX,
samples[i].fRadiusY))->unref();
samples[i].fRadiusY,
NULL,
cr))->unref();
} else {
paint.setImageFilter(new SkDilateImageFilter(
samples[i].fRadiusX,
samples[i].fRadiusY))->unref();
samples[i].fRadiusY,
NULL,
cr))->unref();
}
SkRect bounds = SkRect::MakeXYWH(
x,
y,
SkIntToScalar(samples[i].fWidth),
SkIntToScalar(samples[i].fHeight));
canvas->saveLayer(&bounds, &paint);
canvas->drawBitmap(fBitmap, x, y);
canvas->restore();
drawClippedBitmap(canvas, paint, i * 140, j * 140);
}
}
}

View File

@ -14,7 +14,7 @@
class SK_API SkMorphologyImageFilter : public SkImageFilter {
public:
SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input);
SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input, const SkIRect* cropRect);
protected:
SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer);
@ -32,8 +32,10 @@ private:
class SK_API SkDilateImageFilter : public SkMorphologyImageFilter {
public:
SkDilateImageFilter(int radiusX, int radiusY, SkImageFilter* input = NULL)
: INHERITED(radiusX, radiusY, input) {}
SkDilateImageFilter(int radiusX, int radiusY,
SkImageFilter* input = NULL,
const SkIRect* cropRect = NULL)
: INHERITED(radiusX, radiusY, input, cropRect) {}
virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
@ -53,8 +55,10 @@ private:
class SK_API SkErodeImageFilter : public SkMorphologyImageFilter {
public:
SkErodeImageFilter(int radiusX, int radiusY, SkImageFilter* input = NULL)
: INHERITED(radiusX, radiusY, input) {}
SkErodeImageFilter(int radiusX, int radiusY,
SkImageFilter* input = NULL,
const SkIRect* cropRect = NULL)
: INHERITED(radiusX, radiusY, input, cropRect) {}
virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;

View File

@ -26,8 +26,8 @@ SkMorphologyImageFilter::SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer
fRadius.fHeight = buffer.readInt();
}
SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input)
: INHERITED(input), fRadius(SkISize::Make(radiusX, radiusY)) {
SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input, const SkIRect* cropRect)
: INHERITED(input, cropRect), fRadius(SkISize::Make(radiusX, radiusY)) {
}
@ -71,17 +71,17 @@ static void erode(const SkPMColor* src, SkPMColor* dst,
}
}
static void erodeX(const SkBitmap& src, SkBitmap* dst, int radiusX)
static void erodeX(const SkBitmap& src, SkBitmap* dst, int radiusX, const SkIRect& bounds)
{
erode(src.getAddr32(0, 0), dst->getAddr32(0, 0),
radiusX, src.width(), src.height(),
erode(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
radiusX, bounds.width(), bounds.height(),
1, src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels());
}
static void erodeY(const SkBitmap& src, SkBitmap* dst, int radiusY)
static void erodeY(const SkBitmap& src, SkBitmap* dst, int radiusY, const SkIRect& bounds)
{
erode(src.getAddr32(0, 0), dst->getAddr32(0, 0),
radiusY, src.height(), src.width(),
erode(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
radiusY, bounds.height(), bounds.width(),
src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels(), 1);
}
@ -119,17 +119,17 @@ static void dilate(const SkPMColor* src, SkPMColor* dst,
}
}
static void dilateX(const SkBitmap& src, SkBitmap* dst, int radiusX)
static void dilateX(const SkBitmap& src, SkBitmap* dst, int radiusX, const SkIRect& bounds)
{
dilate(src.getAddr32(0, 0), dst->getAddr32(0, 0),
radiusX, src.width(), src.height(),
dilate(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
radiusX, bounds.width(), bounds.height(),
1, src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels());
}
static void dilateY(const SkBitmap& src, SkBitmap* dst, int radiusY)
static void dilateY(const SkBitmap& src, SkBitmap* dst, int radiusY, const SkIRect& bounds)
{
dilate(src.getAddr32(0, 0), dst->getAddr32(0, 0),
radiusY, src.height(), src.width(),
dilate(src.getAddr32(bounds.left(), bounds.top()), dst->getAddr32(0, 0),
radiusY, bounds.height(), bounds.width(),
src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels(), 1);
}
@ -145,12 +145,18 @@ bool SkErodeImageFilter::onFilterImage(Proxy* proxy,
return false;
}
SkIRect bounds;
src.getBounds(&bounds);
if (!this->applyCropRect(&bounds, ctm)) {
return false;
}
SkAutoLockPixels alp(src);
if (!src.getPixels()) {
return false;
}
dst->setConfig(src.config(), src.width(), src.height());
dst->setConfig(src.config(), bounds.width(), bounds.height());
dst->allocPixels();
int width = radius().width();
@ -161,7 +167,9 @@ bool SkErodeImageFilter::onFilterImage(Proxy* proxy,
}
if (width == 0 && height == 0) {
src.copyTo(dst, dst->config());
src.extractSubset(dst, bounds);
offset->fX += bounds.left();
offset->fY += bounds.top();
return true;
}
@ -172,13 +180,16 @@ bool SkErodeImageFilter::onFilterImage(Proxy* proxy,
}
if (width > 0 && height > 0) {
erodeX(src, &temp, width);
erodeY(temp, dst, height);
erodeX(src, &temp, width, bounds);
SkIRect tmpBounds = SkIRect::MakeWH(bounds.width(), bounds.height());
erodeY(temp, dst, height, tmpBounds);
} else if (width > 0) {
erodeX(src, dst, width);
erodeX(src, dst, width, bounds);
} else if (height > 0) {
erodeY(src, dst, height);
erodeY(src, dst, height, bounds);
}
offset->fX += bounds.left();
offset->fY += bounds.top();
return true;
}
@ -193,12 +204,18 @@ bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
return false;
}
SkIRect bounds;
src.getBounds(&bounds);
if (!this->applyCropRect(&bounds, ctm)) {
return false;
}
SkAutoLockPixels alp(src);
if (!src.getPixels()) {
return false;
}
dst->setConfig(src.config(), src.width(), src.height());
dst->setConfig(src.config(), bounds.width(), bounds.height());
dst->allocPixels();
int width = radius().width();
@ -209,7 +226,9 @@ bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
}
if (width == 0 && height == 0) {
src.copyTo(dst, dst->config());
src.extractSubset(dst, bounds);
offset->fX += bounds.left();
offset->fY += bounds.top();
return true;
}
@ -220,13 +239,16 @@ bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
}
if (width > 0 && height > 0) {
dilateX(src, &temp, width);
dilateY(temp, dst, height);
dilateX(src, &temp, width, bounds);
SkIRect tmpBounds = SkIRect::MakeWH(bounds.width(), bounds.height());
dilateY(temp, dst, height, tmpBounds);
} else if (width > 0) {
dilateX(src, dst, width);
dilateX(src, dst, width, bounds);
} else if (height > 0) {
dilateY(src, dst, height);
dilateY(src, dst, height, bounds);
}
offset->fX += bounds.left();
offset->fY += bounds.top();
return true;
}
@ -446,7 +468,8 @@ namespace {
void apply_morphology_pass(GrContext* context,
GrTexture* texture,
const SkIRect& rect,
const SkIRect& srcRect,
const SkIRect& dstRect,
int radius,
GrMorphologyEffect::MorphologyType morphType,
Gr1DKernelEffect::Direction direction) {
@ -455,15 +478,19 @@ void apply_morphology_pass(GrContext* context,
direction,
radius,
morphType))->unref();
context->drawRect(paint, SkRect::MakeFromIRect(rect));
context->drawRectToRect(paint, SkRect::MakeFromIRect(dstRect), SkRect::MakeFromIRect(srcRect));
}
GrTexture* apply_morphology(GrTexture* srcTexture,
const SkIRect& rect,
GrMorphologyEffect::MorphologyType morphType,
SkISize radius) {
bool apply_morphology(const SkBitmap& input,
const SkIRect& rect,
GrMorphologyEffect::MorphologyType morphType,
SkISize radius,
SkBitmap* dst) {
GrTexture* srcTexture = input.getTexture();
SkASSERT(NULL != srcTexture);
GrContext* context = srcTexture->getContext();
srcTexture->ref();
SkAutoTUnref<GrTexture> src(srcTexture);
GrContext::AutoMatrix am;
am.setIdentity(context);
@ -471,62 +498,101 @@ GrTexture* apply_morphology(GrTexture* srcTexture,
GrContext::AutoClip acs(context, SkRect::MakeWH(SkIntToScalar(srcTexture->width()),
SkIntToScalar(srcTexture->height())));
SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height());
GrTextureDesc desc;
desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
desc.fWidth = rect.width();
desc.fHeight = rect.height();
desc.fConfig = kSkia8888_GrPixelConfig;
SkIRect srcRect = rect;
if (radius.fWidth > 0) {
GrAutoScratchTexture ast(context, desc);
GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
apply_morphology_pass(context, srcTexture, rect, radius.fWidth,
apply_morphology_pass(context, src, srcRect, dstRect, radius.fWidth,
morphType, Gr1DKernelEffect::kX_Direction);
SkIRect clearRect = SkIRect::MakeXYWH(rect.fLeft, rect.fBottom,
rect.width(), radius.fHeight);
SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom,
dstRect.width(), radius.fHeight);
context->clear(&clearRect, 0x0);
srcTexture->unref();
srcTexture = ast.detach();
src.reset(ast.detach());
srcRect = dstRect;
}
if (radius.fHeight > 0) {
GrAutoScratchTexture ast(context, desc);
GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
apply_morphology_pass(context, srcTexture, rect, radius.fHeight,
apply_morphology_pass(context, src, srcRect, dstRect, radius.fHeight,
morphType, Gr1DKernelEffect::kY_Direction);
srcTexture->unref();
srcTexture = ast.detach();
src.reset(ast.detach());
}
return srcTexture;
return SkImageFilterUtils::WrapTexture(src, rect.width(), rect.height(), dst);
}
};
bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
SkBitmap* result, SkIPoint* offset) {
SkBitmap inputBM;
if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &inputBM, offset)) {
SkBitmap input;
if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, offset)) {
return false;
}
GrTexture* input = inputBM.getTexture();
SkIRect bounds;
src.getBounds(&bounds);
SkAutoTUnref<GrTexture> resultTex(apply_morphology(input, bounds,
GrMorphologyEffect::kDilate_MorphologyType, radius()));
return SkImageFilterUtils::WrapTexture(resultTex, src.width(), src.height(), result);
if (!this->applyCropRect(&bounds, ctm)) {
return false;
}
int width = radius().width();
int height = radius().height();
if (width < 0 || height < 0) {
return false;
}
if (width == 0 && height == 0) {
src.extractSubset(result, bounds);
offset->fX += bounds.left();
offset->fY += bounds.top();
return true;
}
if (!apply_morphology(input, bounds, GrMorphologyEffect::kDilate_MorphologyType, radius(), result)) {
return false;
}
offset->fX += bounds.left();
offset->fY += bounds.top();
return true;
}
bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
SkBitmap* result, SkIPoint* offset) {
SkBitmap inputBM;
if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &inputBM, offset)) {
SkBitmap input;
if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, offset)) {
return false;
}
GrTexture* input = inputBM.getTexture();
SkIRect bounds;
src.getBounds(&bounds);
SkAutoTUnref<GrTexture> resultTex(apply_morphology(input, bounds,
GrMorphologyEffect::kErode_MorphologyType, radius()));
return SkImageFilterUtils::WrapTexture(resultTex, src.width(), src.height(), result);
if (!this->applyCropRect(&bounds, ctm)) {
return false;
}
int width = radius().width();
int height = radius().height();
if (width < 0 || height < 0) {
return false;
}
if (width == 0 && height == 0) {
src.extractSubset(result, bounds);
offset->fX += bounds.left();
offset->fY += bounds.top();
return true;
}
if (!apply_morphology(input, bounds, GrMorphologyEffect::kErode_MorphologyType, radius(), result)) {
return false;
}
offset->fX += bounds.left();
offset->fY += bounds.top();
return true;
}
#endif