Fix CTM application in SkResizeImagefilter; implement bounds traversals.

SkResizeImageFilter resizes all the pixels from its input (subject to
the input's crop rect), but the offset to be applied was incorrect.
It should take the CTM into account, so that the origin of the resize is
the world space origin, unaffected by whatever clipping is applied.

New GM imageresizetiled exercises the behaviour under
impl-side-painting-like conditions, and existing GMs now have resize
cases added.

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

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

git-svn-id: http://skia.googlecode.com/svn/trunk@13506 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
senorblanco@chromium.org 2014-02-19 22:10:12 +00:00
parent 5a6abaa1c9
commit 897b73f62c
7 changed files with 142 additions and 10 deletions

View File

@ -50,4 +50,9 @@ complexclip2_rrect_aa
rrect_clip_bw
rrect_clip_aa
rrect_bw
rrect_aa
rrect_aa
# Need to rebaseline all platforms due to new test cases added in
# https://codereview.chromium.org/168283006/
imagefiltersclipped
imagefiltersscaled

View File

@ -14,8 +14,12 @@
#include "SkGradientShader.h"
#include "SkMorphologyImageFilter.h"
#include "SkOffsetImageFilter.h"
#include "SkResizeImageFilter.h"
#include "SkScalar.h"
#define RESIZE_FACTOR_X SkIntToScalar(2)
#define RESIZE_FACTOR_Y SkIntToScalar(5)
namespace skiagm {
class ImageFiltersClippedGM : public GM {
@ -96,6 +100,7 @@ protected:
new SkDilateImageFilter(2, 2, checkerboard.get()),
new SkErodeImageFilter(2, 2, checkerboard.get()),
new SkOffsetImageFilter(SkIntToScalar(-16), SkIntToScalar(32)),
new SkResizeImageFilter(RESIZE_FACTOR_X, RESIZE_FACTOR_Y, SkPaint::kNone_FilterLevel),
};
SkRect r = SkRect::MakeWH(SkIntToScalar(64), SkIntToScalar(64));
@ -113,8 +118,11 @@ protected:
paint.setAntiAlias(true);
canvas->save();
canvas->clipRect(bounds);
if (i == 5) {
if (5 == i) {
canvas->translate(SkIntToScalar(16), SkIntToScalar(-32));
} else if (6 == i) {
canvas->scale(SkScalarInvert(RESIZE_FACTOR_X),
SkScalarInvert(RESIZE_FACTOR_Y));
}
canvas->drawCircle(r.centerX(), r.centerY(),
SkScalarDiv(r.width()*2, SkIntToScalar(5)), paint);

View File

@ -14,8 +14,11 @@
#include "SkGradientShader.h"
#include "SkMorphologyImageFilter.h"
#include "SkOffsetImageFilter.h"
#include "SkResizeImageFilter.h"
#include "SkScalar.h"
#define RESIZE_FACTOR SkIntToScalar(4)
namespace skiagm {
class ImageFiltersScaledGM : public GM {
@ -30,7 +33,7 @@ protected:
}
virtual SkISize onISize() {
return make_isize(860, 500);
return make_isize(1020, 500);
}
void make_checkerboard() {
@ -96,6 +99,7 @@ protected:
new SkDilateImageFilter(1, 1, checkerboard.get()),
new SkErodeImageFilter(1, 1, checkerboard.get()),
new SkOffsetImageFilter(SkIntToScalar(32), 0),
new SkResizeImageFilter(RESIZE_FACTOR, RESIZE_FACTOR, SkPaint::kNone_FilterLevel),
};
SkVector scales[] = {
@ -122,6 +126,9 @@ protected:
canvas->scale(scales[j].fX, scales[j].fY);
if (5 == i) {
canvas->translate(SkIntToScalar(-32), 0);
} else if (6 == i) {
canvas->scale(SkScalarInvert(RESIZE_FACTOR),
SkScalarInvert(RESIZE_FACTOR));
}
canvas->drawCircle(r.centerX(), r.centerY(),
SkScalarDiv(r.width()*2, SkIntToScalar(5)), paint);

77
gm/imageresizetiled.cpp Normal file
View File

@ -0,0 +1,77 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm.h"
#include "SkResizeImageFilter.h"
#include "SkRandom.h"
#define WIDTH 640
#define HEIGHT 480
#define RESIZE_FACTOR SkIntToScalar(2)
namespace skiagm {
class ImageResizeTiledGM : public GM {
public:
ImageResizeTiledGM() {
}
protected:
virtual SkString onShortName() SK_OVERRIDE {
return SkString("imageresizetiled");
}
virtual SkISize onISize() SK_OVERRIDE {
return make_isize(WIDTH, HEIGHT);
}
virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
SkPaint paint;
SkAutoTUnref<SkImageFilter> imageFilter(
new SkResizeImageFilter(RESIZE_FACTOR, RESIZE_FACTOR, SkPaint::kNone_FilterLevel));
paint.setImageFilter(imageFilter.get());
const SkScalar tile_size = SkIntToScalar(100);
SkRect bounds;
canvas->getClipBounds(&bounds);
for (SkScalar y = 0; y < HEIGHT; y += tile_size) {
for (SkScalar x = 0; x < WIDTH; x += tile_size) {
canvas->save();
canvas->clipRect(SkRect::MakeXYWH(x, y, tile_size, tile_size));
canvas->scale(SkScalarInvert(RESIZE_FACTOR),
SkScalarInvert(RESIZE_FACTOR));
canvas->saveLayer(NULL, &paint);
const char* str[] = {
"The quick",
"brown fox",
"jumped over",
"the lazy dog.",
};
SkPaint textPaint;
textPaint.setAntiAlias(true);
textPaint.setTextSize(SkIntToScalar(100));
int posY = 0;
for (unsigned i = 0; i < SK_ARRAY_COUNT(str); i++) {
posY += 100;
canvas->drawText(str[i], strlen(str[i]), SkIntToScalar(0),
SkIntToScalar(posY), textPaint);
}
canvas->restore();
canvas->restore();
}
}
}
private:
typedef GM INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
DEF_GM(return new ImageResizeTiledGM(); )
}

View File

@ -91,6 +91,7 @@
'../gm/imageblur.cpp',
'../gm/imageblurtiled.cpp',
'../gm/imagemagnifier.cpp',
'../gm/imageresizetiled.cpp',
'../gm/inversepaths.cpp',
'../gm/lerpmode.cpp',
'../gm/lighting.cpp',

View File

@ -32,6 +32,7 @@ public:
SkResizeImageFilter(SkScalar sx, SkScalar sy, SkPaint::FilterLevel filterLevel,
SkImageFilter* input = NULL);
virtual ~SkResizeImageFilter();
virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkResizeImageFilter)
@ -41,6 +42,8 @@ protected:
virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&,
SkIRect* dst) const SK_OVERRIDE;
private:
SkScalar fSx, fSy;

View File

@ -42,12 +42,12 @@ SkResizeImageFilter::~SkResizeImageFilter() {
bool SkResizeImageFilter::onFilterImage(Proxy* proxy,
const SkBitmap& source,
const SkMatrix& matrix,
const SkMatrix& ctm,
SkBitmap* result,
SkIPoint* offset) const {
SkBitmap src = source;
SkIPoint srcOffset = SkIPoint::Make(0, 0);
if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, &srcOffset)) {
if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, &srcOffset)) {
return false;
}
@ -56,9 +56,13 @@ bool SkResizeImageFilter::onFilterImage(Proxy* proxy,
src.getBounds(&srcBounds);
srcBounds.offset(srcOffset);
SkRect srcRect = SkRect::Make(srcBounds);
SkMatrix dstMatrix;
dstMatrix.setScale(fSx, fSy);
dstMatrix.mapRect(&dstRect, srcRect);
SkMatrix matrix;
if (!ctm.invert(&matrix)) {
return false;
}
matrix.postScale(fSx, fSy);
matrix.postConcat(ctm);
matrix.mapRect(&dstRect, srcRect);
dstRect.roundOut(&dstBounds);
SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(dstBounds.width(), dstBounds.height()));
@ -67,12 +71,11 @@ bool SkResizeImageFilter::onFilterImage(Proxy* proxy,
}
SkCanvas canvas(device.get());
canvas.translate(-SkIntToScalar(dstBounds.fLeft), -SkIntToScalar(dstBounds.fTop));
canvas.scale(fSx, fSy);
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
paint.setFilterLevel(fFilterLevel);
canvas.concat(dstMatrix);
canvas.drawBitmap(src, srcRect.left(), srcRect.top(), &paint);
*result = device.get()->accessBitmap(false);
@ -80,3 +83,31 @@ bool SkResizeImageFilter::onFilterImage(Proxy* proxy,
offset->fY = dstBounds.fTop;
return true;
}
void SkResizeImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
SkRect bounds = src;
if (getInput(0)) {
getInput(0)->computeFastBounds(src, &bounds);
}
dst->setXYWH(bounds.x(), bounds.y(), bounds.width() * fSx, bounds.height() * fSy);
}
bool SkResizeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
SkIRect* dst) const {
SkMatrix matrix;
if (!ctm.invert(&matrix)) {
return false;
}
matrix.postScale(SkScalarInvert(fSx), SkScalarInvert(fSy));
matrix.postConcat(ctm);
SkRect floatBounds;
matrix.mapRect(&floatBounds, SkRect::Make(src));
SkIRect bounds;
floatBounds.roundOut(&bounds);
if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
return false;
}
*dst = bounds;
return true;
}