Implement a generic matrix transform image filter.

This will be used in Blink to accommodate matrices that contain
rotation or shearing. This is a generalization of SkResizeImageFilter,
so I've replaced all uses of SkResizeImageFilter in Skia. (It might be
easier to review by diffing it with SkResizeImageFilter, too.)

R=reed@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@13941 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
senorblanco@chromium.org 2014-03-25 17:35:10 +00:00
parent d9ea09e1f2
commit fd0ec2c76a
11 changed files with 304 additions and 11 deletions

View File

@ -16,7 +16,7 @@
#include "SkOffsetImageFilter.h"
#include "SkPerlinNoiseShader.h"
#include "SkRectShaderImageFilter.h"
#include "SkResizeImageFilter.h"
#include "SkMatrixImageFilter.h"
#include "SkScalar.h"
#define RESIZE_FACTOR_X SkIntToScalar(2)
@ -91,6 +91,8 @@ protected:
SkAutoTUnref<SkImageFilter> checkerboard(SkBitmapSource::Create(fCheckerboard));
SkAutoTUnref<SkShader> noise(SkPerlinNoiseShader::CreateFractalNoise(
SkDoubleToScalar(0.1), SkDoubleToScalar(0.05), 1, 0));
SkMatrix resizeMatrix;
resizeMatrix.setScale(RESIZE_FACTOR_X, RESIZE_FACTOR_Y);
SkImageFilter* filters[] = {
SkBlurImageFilter::Create(SkIntToScalar(12), SkIntToScalar(12)),
@ -104,7 +106,7 @@ protected:
SkDilateImageFilter::Create(2, 2, checkerboard.get()),
SkErodeImageFilter::Create(2, 2, checkerboard.get()),
SkOffsetImageFilter::Create(SkIntToScalar(-16), SkIntToScalar(32)),
SkResizeImageFilter::Create(RESIZE_FACTOR_X, RESIZE_FACTOR_Y, SkPaint::kNone_FilterLevel),
SkMatrixImageFilter::Create(resizeMatrix, SkPaint::kNone_FilterLevel),
SkRectShaderImageFilter::Create(noise),
};

View File

@ -17,7 +17,7 @@
#include "SkOffsetImageFilter.h"
#include "SkPerlinNoiseShader.h"
#include "SkRectShaderImageFilter.h"
#include "SkResizeImageFilter.h"
#include "SkMatrixImageFilter.h"
#include "SkScalar.h"
#define RESIZE_FACTOR SkIntToScalar(4)
@ -100,6 +100,8 @@ protected:
SkScalar kd = SkIntToScalar(2);
SkScalar surfaceScale = SkIntToScalar(1);
SkColor white(0xFFFFFFFF);
SkMatrix resizeMatrix;
resizeMatrix.setScale(RESIZE_FACTOR, RESIZE_FACTOR);
SkImageFilter* filters[] = {
SkBlurImageFilter::Create(SkIntToScalar(4), SkIntToScalar(4)),
@ -113,7 +115,7 @@ protected:
SkDilateImageFilter::Create(1, 1, checkerboard.get()),
SkErodeImageFilter::Create(1, 1, checkerboard.get()),
SkOffsetImageFilter::Create(SkIntToScalar(32), 0),
SkResizeImageFilter::Create(RESIZE_FACTOR, RESIZE_FACTOR, SkPaint::kNone_FilterLevel),
SkMatrixImageFilter::Create(resizeMatrix, SkPaint::kNone_FilterLevel),
SkRectShaderImageFilter::Create(noise),
SkLightingImageFilter::CreatePointLitDiffuse(pointLocation, white, surfaceScale, kd),
SkLightingImageFilter::CreateSpotLitDiffuse(spotLocation, spotTarget, spotExponent,

View File

@ -6,7 +6,7 @@
*/
#include "gm.h"
#include "SkResizeImageFilter.h"
#include "SkMatrixImageFilter.h"
#include "SkRandom.h"
#define WIDTH 640
@ -36,8 +36,10 @@ protected:
virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
SkPaint paint;
SkMatrix matrix;
matrix.setScale(RESIZE_FACTOR, RESIZE_FACTOR);
SkAutoTUnref<SkImageFilter> imageFilter(
SkResizeImageFilter::Create(RESIZE_FACTOR, RESIZE_FACTOR, SkPaint::kNone_FilterLevel));
SkMatrixImageFilter::Create(matrix, SkPaint::kNone_FilterLevel));
paint.setImageFilter(imageFilter.get());
const SkScalar tile_size = SkIntToScalar(100);
SkRect bounds;

97
gm/matriximagefilter.cpp Normal file
View File

@ -0,0 +1,97 @@
/*
* 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 "SkColor.h"
#include "SkMatrixImageFilter.h"
namespace skiagm {
class MatrixImageFilterGM : public GM {
public:
MatrixImageFilterGM() {
this->setBGColor(0x00000000);
}
protected:
virtual SkString onShortName() {
return SkString("matriximagefilter");
}
void draw(SkCanvas* canvas, const SkRect& rect, const SkBitmap& bitmap,
const SkMatrix& matrix, SkPaint::FilterLevel filterLevel) {
SkAutoTUnref<SkImageFilter> imageFilter(
SkMatrixImageFilter::Create(matrix, filterLevel));
SkPaint paint;
paint.setImageFilter(imageFilter.get());
canvas->saveLayer(&rect, &paint);
canvas->drawBitmap(bitmap, 0, 0);
canvas->restore();
}
virtual SkISize onISize() {
return make_isize(420, 100);
}
void make_checkerboard(SkBitmap* bitmap) {
bitmap->allocN32Pixels(64, 64);
SkCanvas canvas(*bitmap);
SkPaint darkPaint;
darkPaint.setColor(0xFF404040);
SkPaint lightPaint;
lightPaint.setColor(0xFFA0A0A0);
for (int y = 0; y < 64; y += 32) {
for (int x = 0; x < 64; x += 32) {
canvas.save();
canvas.translate(SkIntToScalar(x), SkIntToScalar(y));
canvas.drawRect(SkRect::MakeXYWH(0, 0, 16, 16), darkPaint);
canvas.drawRect(SkRect::MakeXYWH(16, 0, 16, 16), lightPaint);
canvas.drawRect(SkRect::MakeXYWH(0, 16, 16, 16), lightPaint);
canvas.drawRect(SkRect::MakeXYWH(16, 16, 16, 16), darkPaint);
canvas.restore();
}
}
}
virtual void onDraw(SkCanvas* canvas) {
canvas->clear(0x00000000);
SkPaint paint;
SkMatrix matrix;
SkScalar margin = SkIntToScalar(10);
matrix.setSkew(SkDoubleToScalar(0.5), SkDoubleToScalar(0.2));
SkRect srcBounds;
SkBitmap checkerboard;
make_checkerboard(&checkerboard);
checkerboard.getBounds(&srcBounds);
// Outer paint does no filtering; leave it all for the filter
paint.setFilterLevel(SkPaint::kNone_FilterLevel);
SkRect srcRect = SkRect::MakeWH(96, 96);
canvas->translate(margin, margin);
draw(canvas, srcRect, checkerboard, matrix, SkPaint::kNone_FilterLevel);
canvas->translate(srcRect.width() + margin, 0);
draw(canvas, srcRect, checkerboard, matrix, SkPaint::kLow_FilterLevel);
canvas->translate(srcRect.width() + margin, 0);
draw(canvas, srcRect, checkerboard, matrix, SkPaint::kMedium_FilterLevel);
canvas->translate(srcRect.width() + margin, 0);
draw(canvas, srcRect, checkerboard, matrix, SkPaint::kHigh_FilterLevel);
}
private:
typedef GM INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static GM* MyFactory(void*) { return new MatrixImageFilterGM; }
static GMRegistry reg(MyFactory);
}

View File

@ -9,7 +9,7 @@
#include "SkBitmapDevice.h"
#include "SkBitmapSource.h"
#include "SkColor.h"
#include "SkResizeImageFilter.h"
#include "SkMatrixImageFilter.h"
#include "SkRefCnt.h"
namespace skiagm {
@ -38,11 +38,11 @@ protected:
canvas->translate(rect.x(), rect.y());
canvas->scale(deviceScaleX, deviceScaleY);
canvas->translate(-rect.x(), -rect.y());
SkMatrix matrix;
matrix.setScale(SkScalarInvert(deviceScaleX),
SkScalarInvert(deviceScaleY));
SkAutoTUnref<SkImageFilter> imageFilter(
SkResizeImageFilter::Create(SkScalarInvert(deviceScaleX),
SkScalarInvert(deviceScaleY),
filterLevel,
input));
SkMatrixImageFilter::Create(matrix, filterLevel, input));
SkPaint filteredPaint;
filteredPaint.setImageFilter(imageFilter.get());
canvas->saveLayer(&rect, &filteredPaint);

View File

@ -58,6 +58,7 @@
'<(skia_src_path)/effects/SkTableMaskFilter.cpp',
'<(skia_src_path)/effects/SkTestImageFilters.cpp',
'<(skia_src_path)/effects/SkTileImageFilter.cpp',
'<(skia_src_path)/effects/SkMatrixImageFilter.cpp',
'<(skia_src_path)/effects/SkTransparentShader.cpp',
'<(skia_src_path)/effects/SkXfermodeImageFilter.cpp',

View File

@ -107,6 +107,7 @@
'../gm/lcdtext.cpp',
'../gm/linepaths.cpp',
'../gm/matrixconvolution.cpp',
'../gm/matriximagefilter.cpp',
'../gm/megalooper.cpp',
'../gm/mixedxfermodes.cpp',
'../gm/modecolorfilters.cpp',

View File

@ -0,0 +1,58 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkMatrixImageFilter_DEFINED
#define SkMatrixImageFilter_DEFINED
#include "SkImageFilter.h"
#include "SkScalar.h"
#include "SkSize.h"
#include "SkPoint.h"
#include "SkPaint.h"
/*! \class SkMatrixImageFilter
Matrix transformation image filter. This filter draws its source
input transformed by the given matrix.
*/
class SK_API SkMatrixImageFilter : public SkImageFilter {
public:
/** Construct a 2D transformation image filter.
* @param transform The matrix to apply when drawing the src bitmap
* @param filterLevel The quality of filtering to apply when scaling.
* @param input The input image filter. If NULL, the src bitmap
* passed to filterImage() is used instead.
*/
static SkMatrixImageFilter* Create(const SkMatrix& transform,
SkPaint::FilterLevel,
SkImageFilter* input = NULL);
virtual ~SkMatrixImageFilter();
virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMatrixImageFilter)
protected:
SkMatrixImageFilter(const SkMatrix& transform,
SkPaint::FilterLevel,
SkImageFilter* input = NULL);
SkMatrixImageFilter(SkReadBuffer& buffer);
virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&,
SkIRect* dst) const SK_OVERRIDE;
private:
SkMatrix fTransform;
SkPaint::FilterLevel fFilterLevel;
typedef SkImageFilter INHERITED;
};
#endif

View File

@ -0,0 +1,126 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkMatrixImageFilter.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkDevice.h"
#include "SkColorPriv.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
#include "SkMatrix.h"
#include "SkRect.h"
SkMatrixImageFilter::SkMatrixImageFilter(const SkMatrix& transform,
SkPaint::FilterLevel filterLevel,
SkImageFilter* input)
: INHERITED(input),
fTransform(transform),
fFilterLevel(filterLevel) {
}
SkMatrixImageFilter* SkMatrixImageFilter::Create(const SkMatrix& transform,
SkPaint::FilterLevel filterLevel,
SkImageFilter* input) {
return SkNEW_ARGS(SkMatrixImageFilter, (transform, filterLevel, input));
}
SkMatrixImageFilter::SkMatrixImageFilter(SkReadBuffer& buffer)
: INHERITED(1, buffer) {
buffer.readMatrix(&fTransform);
fFilterLevel = static_cast<SkPaint::FilterLevel>(buffer.readInt());
}
void SkMatrixImageFilter::flatten(SkWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
buffer.writeMatrix(fTransform);
buffer.writeInt(fFilterLevel);
}
SkMatrixImageFilter::~SkMatrixImageFilter() {
}
bool SkMatrixImageFilter::onFilterImage(Proxy* proxy,
const SkBitmap& source,
const Context& ctx,
SkBitmap* result,
SkIPoint* offset) const {
SkBitmap src = source;
SkIPoint srcOffset = SkIPoint::Make(0, 0);
if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
return false;
}
SkRect dstRect;
SkIRect srcBounds, dstBounds;
src.getBounds(&srcBounds);
srcBounds.offset(srcOffset);
SkRect srcRect = SkRect::Make(srcBounds);
SkMatrix matrix;
if (!ctx.ctm().invert(&matrix)) {
return false;
}
matrix.postConcat(fTransform);
matrix.postConcat(ctx.ctm());
matrix.mapRect(&dstRect, srcRect);
dstRect.roundOut(&dstBounds);
SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(dstBounds.width(), dstBounds.height()));
if (NULL == device.get()) {
return false;
}
SkCanvas canvas(device.get());
canvas.translate(-SkIntToScalar(dstBounds.x()), -SkIntToScalar(dstBounds.y()));
canvas.concat(matrix);
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
paint.setFilterLevel(fFilterLevel);
canvas.drawBitmap(src, srcRect.x(), srcRect.y(), &paint);
*result = device.get()->accessBitmap(false);
offset->fX = dstBounds.fLeft;
offset->fY = dstBounds.fTop;
return true;
}
void SkMatrixImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
SkRect bounds = src;
if (getInput(0)) {
getInput(0)->computeFastBounds(src, &bounds);
}
SkMatrix matrix;
matrix.setTranslate(-bounds.x(), -bounds.y());
matrix.postConcat(fTransform);
matrix.postTranslate(bounds.x(), bounds.y());
matrix.mapRect(dst, bounds);
}
bool SkMatrixImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
SkIRect* dst) const {
SkMatrix transformInverse;
if (!fTransform.invert(&transformInverse)) {
return false;
}
SkMatrix matrix;
if (!ctm.invert(&matrix)) {
return false;
}
matrix.postConcat(transformInverse);
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;
}

View File

@ -58,6 +58,7 @@
#include "SkTableColorFilter.h"
#include "SkTestImageFilters.h"
#include "SkTileImageFilter.h"
#include "SkMatrixImageFilter.h"
#include "SkXfermodeImageFilter.h"
static void InitializeFlattenables(int*) {
@ -96,6 +97,7 @@ static void InitializeFlattenables(int*) {
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkStippleMaskFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSumPathEffect)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTileImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMatrixImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkXfermodeImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMagnifierImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMatrixConvolutionImageFilter)

View File

@ -58,6 +58,7 @@
#include "SkTableColorFilter.h"
#include "SkTestImageFilters.h"
#include "SkTileImageFilter.h"
#include "SkMatrixImageFilter.h"
#include "SkXfermodeImageFilter.h"
static void InitializeFlattenables(int*) {
@ -96,6 +97,7 @@ static void InitializeFlattenables(int*) {
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkStippleMaskFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSumPathEffect)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTileImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMatrixImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkXfermodeImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMagnifierImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMatrixConvolutionImageFilter)