This patch implements a crop rect for SkImageFilter. It has been implemented for SkColorFilterImageFilter and SkBlurImageFilter as examples.

In order to preserve the immutability of SkImageFilters, the crop rect is passed as a constructor parameter. If NULL (the default), the bounds of the input image are used, as before.

This also tightens up the boundary handling for SkImageBlurFilter on the GPU backend. Where we were previously using clamping semantics, we now respect decal semantics (so we don't oversaturate the edges). This brings the GPU and raster backends into closer alignment, but will require some new baselines for the GPU tests.

At a minimum, the following tests will need new baselines: imageblur, imagefiltersbase, imagefilterscropped, spritebitmap.

R=reed@google.com

Committed: https://code.google.com/p/skia/source/detail?r=10251

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

git-svn-id: http://skia.googlecode.com/svn/trunk@10338 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
senorblanco@chromium.org 2013-07-24 22:19:24 +00:00
parent 91673aafdf
commit 194d775edc
17 changed files with 523 additions and 80 deletions

176
gm/imagefilterscropped.cpp Normal file
View File

@ -0,0 +1,176 @@
/*
* Copyright 2011 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 "SkCanvas.h"
#include "SkColorFilter.h"
#include "SkColorPriv.h"
#include "SkShader.h"
#include "SkBlurImageFilter.h"
#include "SkColorFilterImageFilter.h"
#include "SkTestImageFilters.h"
///////////////////////////////////////////////////////////////////////////////
static void draw_paint(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
SkPaint paint;
paint.setImageFilter(imf);
paint.setColor(SK_ColorBLACK);
canvas->save();
canvas->clipRect(r);
canvas->drawPaint(paint);
canvas->restore();
}
static void draw_path(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
SkPaint paint;
paint.setColor(SK_ColorMAGENTA);
paint.setImageFilter(imf);
paint.setAntiAlias(true);
canvas->save();
canvas->clipRect(r);
canvas->drawCircle(r.centerX(), r.centerY(), r.width()*2/5, paint);
canvas->restore();
}
static void draw_text(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
SkPaint paint;
paint.setImageFilter(imf);
paint.setColor(SK_ColorGREEN);
paint.setAntiAlias(true);
paint.setTextSize(r.height()/2);
paint.setTextAlign(SkPaint::kCenter_Align);
canvas->save();
canvas->clipRect(r);
canvas->drawText("Text", 4, r.centerX(), r.centerY(), paint);
canvas->restore();
}
static void draw_bitmap(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
SkPaint paint;
SkIRect bounds;
r.roundOut(&bounds);
SkBitmap bm;
bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height());
bm.allocPixels();
bm.eraseColor(SK_ColorTRANSPARENT);
SkCanvas c(bm);
draw_path(&c, r, NULL);
paint.setImageFilter(imf);
canvas->save();
canvas->clipRect(r);
canvas->drawBitmap(bm, 0, 0, &paint);
canvas->restore();
}
static void draw_sprite(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
SkPaint paint;
SkIRect bounds;
r.roundOut(&bounds);
SkBitmap bm;
bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height());
bm.allocPixels();
bm.eraseColor(SK_ColorRED);
SkCanvas c(bm);
SkIRect cropRect = SkIRect::MakeXYWH(10, 10, 44, 44);
paint.setColor(SK_ColorGREEN);
c.drawRect(SkRect::Make(cropRect), paint);
paint.setImageFilter(imf);
SkPoint loc = { r.fLeft, r.fTop };
canvas->getTotalMatrix().mapPoints(&loc, 1);
canvas->drawSprite(bm,
SkScalarRoundToInt(loc.fX), SkScalarRoundToInt(loc.fY),
&paint);
}
///////////////////////////////////////////////////////////////////////////////
class ImageFiltersCroppedGM : public skiagm::GM {
public:
ImageFiltersCroppedGM () {}
protected:
virtual SkString onShortName() {
return SkString("imagefilterscropped");
}
virtual SkISize onISize() { return SkISize::Make(700, 460); }
void draw_frame(SkCanvas* canvas, const SkRect& r) {
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorRED);
canvas->drawRect(r, paint);
}
virtual uint32_t onGetFlags() const {
// Because of the use of drawSprite, this test is excluded
// from scaled replay tests because drawSprite ignores the
// reciprocal scale that is applied at record time, which is
// the intended behavior of drawSprite.
return kSkipScaledReplay_Flag;
}
virtual void onDraw(SkCanvas* canvas) {
void (*drawProc[])(SkCanvas*, const SkRect&, SkImageFilter*) = {
draw_sprite, draw_bitmap, draw_path, draw_paint, draw_text
};
SkColorFilter* cf = SkColorFilter::CreateModeFilter(SK_ColorRED,
SkXfermode::kSrcIn_Mode);
SkIRect cropRect = SkIRect::MakeXYWH(10, 10, 44, 44);
SkIRect bogusRect = SkIRect::MakeXYWH(-100, -100, 10, 10);
SkImageFilter* filters[] = {
NULL,
SkColorFilterImageFilter::Create(cf, NULL, &cropRect),
new SkBlurImageFilter(8.0f, 0.0f, NULL, &cropRect),
new SkBlurImageFilter(0.0f, 8.0f, NULL, &cropRect),
new SkBlurImageFilter(8.0f, 8.0f, NULL, &cropRect),
new SkBlurImageFilter(8.0f, 8.0f, NULL, &bogusRect),
SkColorFilterImageFilter::Create(cf, NULL, &bogusRect),
};
cf->unref();
SkRect r = SkRect::MakeWH(SkIntToScalar(64), SkIntToScalar(64));
SkScalar MARGIN = SkIntToScalar(16);
SkScalar DX = r.width() + MARGIN;
SkScalar DY = r.height() + MARGIN;
canvas->translate(MARGIN, MARGIN);
for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
canvas->save();
for (size_t j = 0; j < SK_ARRAY_COUNT(drawProc); ++j) {
drawProc[j](canvas, r, filters[i]);
canvas->translate(0, DY);
}
canvas->restore();
canvas->translate(DX, 0);
}
for(size_t j = 0; j < SK_ARRAY_COUNT(filters); ++j) {
SkSafeUnref(filters[j]);
}
}
private:
typedef GM INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
static skiagm::GM* MyFactory(void*) { return new ImageFiltersCroppedGM; }
static skiagm::GMRegistry reg(MyFactory);

View File

@ -63,6 +63,7 @@
'../gm/lighting.cpp', '../gm/lighting.cpp',
'../gm/image.cpp', '../gm/image.cpp',
'../gm/imagefiltersbase.cpp', '../gm/imagefiltersbase.cpp',
'../gm/imagefilterscropped.cpp',
'../gm/imagefiltersgraph.cpp', '../gm/imagefiltersgraph.cpp',
'../gm/internal_links.cpp', '../gm/internal_links.cpp',
'../gm/lcdtext.cpp', '../gm/lcdtext.cpp',

View File

@ -69,6 +69,7 @@
'../tests/HashCacheTest.cpp', '../tests/HashCacheTest.cpp',
'../tests/ImageCacheTest.cpp', '../tests/ImageCacheTest.cpp',
'../tests/ImageDecodingTest.cpp', '../tests/ImageDecodingTest.cpp',
'../tests/ImageFilterTest.cpp',
'../tests/InfRectTest.cpp', '../tests/InfRectTest.cpp',
'../tests/LListTest.cpp', '../tests/LListTest.cpp',
'../tests/LayerDrawLooperTest.cpp', '../tests/LayerDrawLooperTest.cpp',

View File

@ -9,13 +9,13 @@
#define SkImageFilter_DEFINED #define SkImageFilter_DEFINED
#include "SkFlattenable.h" #include "SkFlattenable.h"
#include "SkRect.h"
class SkBitmap; class SkBitmap;
class SkColorFilter; class SkColorFilter;
class SkDevice; class SkDevice;
class SkMatrix; class SkMatrix;
struct SkIPoint; struct SkIPoint;
struct SkIRect;
class SkShader; class SkShader;
class GrEffectRef; class GrEffectRef;
class GrTexture; class GrTexture;
@ -139,14 +139,25 @@ public:
return fInputs[i]; return fInputs[i];
} }
/**
* Returns the crop rectangle of this filter. This is set at construction
* time, and determines which pixels from the input image will
* be processed. The size of this rectangle should be used as the size
* of the destination image. The origin of this rect should be used to
* offset access to the input images, and should also be added to the
* "offset" parameter in onFilterImage and filterImageGPU(). (The latter
* ensures that the resulting buffer is drawn in the correct location.)
*/
const SkIRect& cropRect() const { return fCropRect; }
protected: protected:
SkImageFilter(int inputCount, SkImageFilter** inputs); SkImageFilter(int inputCount, SkImageFilter** inputs, const SkIRect* cropRect = NULL);
// Convenience constructor for 1-input filters. // Convenience constructor for 1-input filters.
explicit SkImageFilter(SkImageFilter* input); explicit SkImageFilter(SkImageFilter* input, const SkIRect* cropRect = NULL);
// Convenience constructor for 2-input filters. // Convenience constructor for 2-input filters.
SkImageFilter(SkImageFilter* input1, SkImageFilter* input2); SkImageFilter(SkImageFilter* input1, SkImageFilter* input2, const SkIRect* cropRect = NULL);
virtual ~SkImageFilter(); virtual ~SkImageFilter();
@ -160,10 +171,15 @@ protected:
// Default impl copies src into dst and returns true // Default impl copies src into dst and returns true
virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*); virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*);
// Sets rect to the intersection of rect and the crop rect. If there
// is no overlap, returns false and leaves rect unchanged.
bool applyCropRect(SkIRect* rect) const;
private: private:
typedef SkFlattenable INHERITED; typedef SkFlattenable INHERITED;
int fInputCount; int fInputCount;
SkImageFilter** fInputs; SkImageFilter** fInputs;
SkIRect fCropRect;
}; };
#endif #endif

View File

@ -26,6 +26,12 @@ struct SK_API SkIRect {
return r; return r;
} }
static SkIRect SK_WARN_UNUSED_RESULT MakeLargest() {
SkIRect r;
r.setLargest();
return r;
}
static SkIRect SK_WARN_UNUSED_RESULT MakeWH(int32_t w, int32_t h) { static SkIRect SK_WARN_UNUSED_RESULT MakeWH(int32_t w, int32_t h) {
SkIRect r; SkIRect r;
r.set(0, 0, w, h); r.set(0, 0, w, h);
@ -94,6 +100,11 @@ struct SK_API SkIRect {
*/ */
bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
bool isLargest() const { return SK_MinS32 == fLeft &&
SK_MinS32 == fTop &&
SK_MaxS32 == fRight &&
SK_MaxS32 == fBottom; }
friend bool operator==(const SkIRect& a, const SkIRect& b) { friend bool operator==(const SkIRect& a, const SkIRect& b) {
return !memcmp(&a, &b, sizeof(a)); return !memcmp(&a, &b, sizeof(a));
} }

View File

@ -13,7 +13,10 @@
class SK_API SkBlurImageFilter : public SkImageFilter { class SK_API SkBlurImageFilter : public SkImageFilter {
public: public:
SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input = NULL); SkBlurImageFilter(SkScalar sigmaX,
SkScalar sigmaY,
SkImageFilter* input = NULL,
const SkIRect* cropRect = NULL);
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurImageFilter) SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurImageFilter)

View File

@ -14,7 +14,9 @@ class SkColorFilter;
class SK_API SkColorFilterImageFilter : public SkImageFilter { class SK_API SkColorFilterImageFilter : public SkImageFilter {
public: public:
static SkColorFilterImageFilter* Create(SkColorFilter* cf, SkImageFilter* input = NULL); static SkColorFilterImageFilter* Create(SkColorFilter* cf,
SkImageFilter* input = NULL,
const SkIRect* cropRect = NULL);
virtual ~SkColorFilterImageFilter(); virtual ~SkColorFilterImageFilter();
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorFilterImageFilter) SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorFilterImageFilter)
@ -29,7 +31,9 @@ protected:
virtual bool asColorFilter(SkColorFilter**) const SK_OVERRIDE; virtual bool asColorFilter(SkColorFilter**) const SK_OVERRIDE;
private: private:
SkColorFilterImageFilter(SkColorFilter* cf, SkImageFilter* input); SkColorFilterImageFilter(SkColorFilter* cf,
SkImageFilter* input,
const SkIRect* cropRect = NULL);
SkColorFilter* fColorFilter; SkColorFilter* fColorFilter;
typedef SkImageFilter INHERITED; typedef SkImageFilter INHERITED;

View File

@ -18,22 +18,27 @@
SK_DEFINE_INST_COUNT(SkImageFilter) SK_DEFINE_INST_COUNT(SkImageFilter)
SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs) SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const SkIRect* cropRect)
: fInputCount(inputCount), fInputs(new SkImageFilter*[inputCount]) { : fInputCount(inputCount),
fInputs(new SkImageFilter*[inputCount]),
fCropRect(cropRect ? *cropRect : SkIRect::MakeLargest()) {
for (int i = 0; i < inputCount; ++i) { for (int i = 0; i < inputCount; ++i) {
fInputs[i] = inputs[i]; fInputs[i] = inputs[i];
SkSafeRef(fInputs[i]); SkSafeRef(fInputs[i]);
} }
} }
SkImageFilter::SkImageFilter(SkImageFilter* input) SkImageFilter::SkImageFilter(SkImageFilter* input, const SkIRect* cropRect)
: fInputCount(1), fInputs(new SkImageFilter*[1]) { : fInputCount(1),
fInputs(new SkImageFilter*[1]),
fCropRect(cropRect ? *cropRect : SkIRect::MakeLargest()) {
fInputs[0] = input; fInputs[0] = input;
SkSafeRef(fInputs[0]); SkSafeRef(fInputs[0]);
} }
SkImageFilter::SkImageFilter(SkImageFilter* input1, SkImageFilter* input2) SkImageFilter::SkImageFilter(SkImageFilter* input1, SkImageFilter* input2, const SkIRect* cropRect)
: fInputCount(2), fInputs(new SkImageFilter*[2]) { : fInputCount(2), fInputs(new SkImageFilter*[2]),
fCropRect(cropRect ? *cropRect : SkIRect::MakeLargest()) {
fInputs[0] = input1; fInputs[0] = input1;
fInputs[1] = input2; fInputs[1] = input2;
SkSafeRef(fInputs[0]); SkSafeRef(fInputs[0]);
@ -56,6 +61,7 @@ SkImageFilter::SkImageFilter(SkFlattenableReadBuffer& buffer)
fInputs[i] = NULL; fInputs[i] = NULL;
} }
} }
buffer.readIRect(&fCropRect);
} }
void SkImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { void SkImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
@ -67,6 +73,7 @@ void SkImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
buffer.writeFlattenable(input); buffer.writeFlattenable(input);
} }
} }
buffer.writeIRect(fCropRect);
} }
bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src, bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
@ -137,6 +144,10 @@ bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap*
#endif #endif
} }
bool SkImageFilter::applyCropRect(SkIRect* rect) const {
return rect->intersect(fCropRect);
}
bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
SkIRect* dst) { SkIRect* dst) {
*dst = src; *dst = src;

View File

@ -21,8 +21,11 @@ SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer)
fSigma.fHeight = buffer.readScalar(); fSigma.fHeight = buffer.readScalar();
} }
SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input) SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX,
: INHERITED(input), fSigma(SkSize::Make(sigmaX, sigmaY)) { SkScalar sigmaY,
SkImageFilter* input,
const SkIRect* cropRect)
: INHERITED(input, cropRect), fSigma(SkSize::Make(sigmaX, sigmaY)) {
SkASSERT(sigmaX >= 0 && sigmaY >= 0); SkASSERT(sigmaX >= 0 && sigmaY >= 0);
} }
@ -33,13 +36,13 @@ void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
} }
static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize, static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize,
int leftOffset, int rightOffset) int leftOffset, int rightOffset, const SkIRect& bounds)
{ {
int width = src.width(), height = src.height(); int width = bounds.width(), height = bounds.height();
int rightBorder = SkMin32(rightOffset + 1, width); int rightBorder = SkMin32(rightOffset + 1, width);
for (int y = 0; y < height; ++y) { for (int y = 0; y < height; ++y) {
int sumA = 0, sumR = 0, sumG = 0, sumB = 0; int sumA = 0, sumR = 0, sumG = 0, sumB = 0;
SkPMColor* p = src.getAddr32(0, y); SkPMColor* p = src.getAddr32(bounds.fLeft, y + bounds.fTop);
for (int i = 0; i < rightBorder; ++i) { for (int i = 0; i < rightBorder; ++i) {
sumA += SkGetPackedA32(*p); sumA += SkGetPackedA32(*p);
sumR += SkGetPackedR32(*p); sumR += SkGetPackedR32(*p);
@ -48,7 +51,7 @@ static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize,
p++; p++;
} }
const SkColor* sptr = src.getAddr32(0, y); const SkColor* sptr = src.getAddr32(bounds.fLeft, bounds.fTop + y);
SkColor* dptr = dst->getAddr32(0, y); SkColor* dptr = dst->getAddr32(0, y);
for (int x = 0; x < width; ++x) { for (int x = 0; x < width; ++x) {
*dptr = SkPackARGB32(sumA / kernelSize, *dptr = SkPackARGB32(sumA / kernelSize,
@ -76,15 +79,15 @@ static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize,
} }
static void boxBlurY(const SkBitmap& src, SkBitmap* dst, int kernelSize, static void boxBlurY(const SkBitmap& src, SkBitmap* dst, int kernelSize,
int topOffset, int bottomOffset) int topOffset, int bottomOffset, const SkIRect& bounds)
{ {
int width = src.width(), height = src.height(); int width = bounds.width(), height = bounds.height();
int bottomBorder = SkMin32(bottomOffset + 1, height); int bottomBorder = SkMin32(bottomOffset + 1, height);
int srcStride = src.rowBytesAsPixels(); int srcStride = src.rowBytesAsPixels();
int dstStride = dst->rowBytesAsPixels(); int dstStride = dst->rowBytesAsPixels();
for (int x = 0; x < width; ++x) { for (int x = 0; x < width; ++x) {
int sumA = 0, sumR = 0, sumG = 0, sumB = 0; int sumA = 0, sumR = 0, sumG = 0, sumB = 0;
SkColor* p = src.getAddr32(x, 0); SkColor* p = src.getAddr32(bounds.fLeft + x, bounds.fTop);
for (int i = 0; i < bottomBorder; ++i) { for (int i = 0; i < bottomBorder; ++i) {
sumA += SkGetPackedA32(*p); sumA += SkGetPackedA32(*p);
sumR += SkGetPackedR32(*p); sumR += SkGetPackedR32(*p);
@ -93,7 +96,7 @@ static void boxBlurY(const SkBitmap& src, SkBitmap* dst, int kernelSize,
p += srcStride; p += srcStride;
} }
const SkColor* sptr = src.getAddr32(x, 0); const SkColor* sptr = src.getAddr32(bounds.fLeft + x, bounds.fTop);
SkColor* dptr = dst->getAddr32(x, 0); SkColor* dptr = dst->getAddr32(x, 0);
for (int y = 0; y < height; ++y) { for (int y = 0; y < height; ++y) {
*dptr = SkPackARGB32(sumA / kernelSize, *dptr = SkPackARGB32(sumA / kernelSize,
@ -153,7 +156,14 @@ bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
return false; return false;
} }
dst->setConfig(src.config(), src.width(), src.height()); SkIRect srcBounds, dstBounds;
src.getBounds(&srcBounds);
if (!this->applyCropRect(&srcBounds)) {
return false;
}
dst->setConfig(src.config(), srcBounds.width(), srcBounds.height());
dst->getBounds(&dstBounds);
dst->allocPixels(); dst->allocPixels();
int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
@ -176,21 +186,23 @@ bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
} }
if (kernelSizeX > 0 && kernelSizeY > 0) { if (kernelSizeX > 0 && kernelSizeY > 0) {
boxBlurX(src, &temp, kernelSizeX, lowOffsetX, highOffsetX); boxBlurX(src, &temp, kernelSizeX, lowOffsetX, highOffsetX, srcBounds);
boxBlurY(temp, dst, kernelSizeY, lowOffsetY, highOffsetY); boxBlurY(temp, dst, kernelSizeY, lowOffsetY, highOffsetY, dstBounds);
boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX); boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX, dstBounds);
boxBlurY(temp, dst, kernelSizeY, highOffsetY, lowOffsetY); boxBlurY(temp, dst, kernelSizeY, highOffsetY, lowOffsetY, dstBounds);
boxBlurX(*dst, &temp, kernelSizeX3, highOffsetX, highOffsetX); boxBlurX(*dst, &temp, kernelSizeX3, highOffsetX, highOffsetX, dstBounds);
boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY); boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY, dstBounds);
} else if (kernelSizeX > 0) { } else if (kernelSizeX > 0) {
boxBlurX(src, dst, kernelSizeX, lowOffsetX, highOffsetX); boxBlurX(src, dst, kernelSizeX, lowOffsetX, highOffsetX, srcBounds);
boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX); boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX, dstBounds);
boxBlurX(temp, dst, kernelSizeX3, highOffsetX, highOffsetX); boxBlurX(temp, dst, kernelSizeX3, highOffsetX, highOffsetX, dstBounds);
} else if (kernelSizeY > 0) { } else if (kernelSizeY > 0) {
boxBlurY(src, dst, kernelSizeY, lowOffsetY, highOffsetY); boxBlurY(src, dst, kernelSizeY, lowOffsetY, highOffsetY, srcBounds);
boxBlurY(*dst, &temp, kernelSizeY, highOffsetY, lowOffsetY); boxBlurY(*dst, &temp, kernelSizeY, highOffsetY, lowOffsetY, dstBounds);
boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY); boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY, dstBounds);
} }
offset->fX += srcBounds.fLeft;
offset->fY += srcBounds.fTop;
return true; return true;
} }
@ -202,12 +214,21 @@ bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitm
return false; return false;
} }
GrTexture* source = input.getTexture(); GrTexture* source = input.getTexture();
SkRect rect; SkIRect rect;
src.getBounds(&rect); src.getBounds(&rect);
if (!this->applyCropRect(&rect)) {
return false;
}
SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(), SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(),
source, false, rect, source,
fSigma.width(), fSigma.height())); false,
return SkImageFilterUtils::WrapTexture(tex, src.width(), src.height(), result); SkRect::Make(rect),
true,
fSigma.width(),
fSigma.height()));
offset->fX += rect.fLeft;
offset->fY += rect.fTop;
return SkImageFilterUtils::WrapTexture(tex, rect.width(), rect.height(), result);
#else #else
SkDEBUGFAIL("Should not call in GPU-less build"); SkDEBUGFAIL("Should not call in GPU-less build");
return false; return false;

View File

@ -412,7 +412,7 @@ bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src,
// gaussianBlur. Otherwise, we need to save it for later compositing. // gaussianBlur. Otherwise, we need to save it for later compositing.
bool isNormalBlur = (SkBlurMaskFilter::kNormal_BlurStyle == fBlurStyle); bool isNormalBlur = (SkBlurMaskFilter::kNormal_BlurStyle == fBlurStyle);
*result = SkGpuBlurUtils::GaussianBlur(context, src, isNormalBlur && canOverwriteSrc, *result = SkGpuBlurUtils::GaussianBlur(context, src, isNormalBlur && canOverwriteSrc,
clipRect, sigma, sigma); clipRect, false, sigma, sigma);
if (NULL == *result) { if (NULL == *result) {
return false; return false;
} }

View File

@ -57,7 +57,7 @@ bool matrix_needs_clamping(SkScalar matrix[20]) {
}; };
SkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf, SkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf,
SkImageFilter* input) { SkImageFilter* input, const SkIRect* cropRect) {
SkASSERT(cf); SkASSERT(cf);
SkScalar colorMatrix[20], inputMatrix[20]; SkScalar colorMatrix[20], inputMatrix[20];
SkColorFilter* inputColorFilter; SkColorFilter* inputColorFilter;
@ -69,13 +69,15 @@ SkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf,
SkScalar combinedMatrix[20]; SkScalar combinedMatrix[20];
mult_color_matrix(inputMatrix, colorMatrix, combinedMatrix); mult_color_matrix(inputMatrix, colorMatrix, combinedMatrix);
SkAutoTUnref<SkColorFilter> newCF(SkNEW_ARGS(SkColorMatrixFilter, (combinedMatrix))); SkAutoTUnref<SkColorFilter> newCF(SkNEW_ARGS(SkColorMatrixFilter, (combinedMatrix)));
return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0))); return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0), cropRect));
} }
} }
return SkNEW_ARGS(SkColorFilterImageFilter, (cf, input)); return SkNEW_ARGS(SkColorFilterImageFilter, (cf, input, cropRect));
} }
SkColorFilterImageFilter::SkColorFilterImageFilter(SkColorFilter* cf, SkImageFilter* input) : INHERITED(input), fColorFilter(cf) { SkColorFilterImageFilter::SkColorFilterImageFilter(SkColorFilter* cf,
SkImageFilter* input, const SkIRect* cropRect)
: INHERITED(input, cropRect), fColorFilter(cf) {
SkASSERT(cf); SkASSERT(cf);
SkSafeRef(cf); SkSafeRef(cf);
} }
@ -103,22 +105,33 @@ bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& sourc
return false; return false;
} }
SkAutoTUnref<SkDevice> device(proxy->createDevice(src.width(), src.height())); SkIRect bounds;
src.getBounds(&bounds);
if (!this->applyCropRect(&bounds)) {
return false;
}
SkAutoTUnref<SkDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
SkCanvas canvas(device.get()); SkCanvas canvas(device.get());
SkPaint paint; SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode); paint.setXfermodeMode(SkXfermode::kSrc_Mode);
paint.setColorFilter(fColorFilter); paint.setColorFilter(fColorFilter);
canvas.drawSprite(src, 0, 0, &paint); canvas.drawSprite(src, -bounds.fLeft, -bounds.fTop, &paint);
*result = device.get()->accessBitmap(false); *result = device.get()->accessBitmap(false);
loc->fX += bounds.fLeft;
loc->fY += bounds.fTop;
return true; return true;
} }
bool SkColorFilterImageFilter::asColorFilter(SkColorFilter** filter) const { bool SkColorFilterImageFilter::asColorFilter(SkColorFilter** filter) const {
if (filter) { if (cropRect().isLargest()) {
*filter = fColorFilter; if (filter) {
fColorFilter->ref(); *filter = fColorFilter;
fColorFilter->ref();
}
return true;
} }
return true; return false;
} }

View File

@ -11,6 +11,7 @@
#if SK_SUPPORT_GPU #if SK_SUPPORT_GPU
#include "effects/GrConvolutionEffect.h" #include "effects/GrConvolutionEffect.h"
#include "effects/GrTextureDomainEffect.h"
#include "GrContext.h" #include "GrContext.h"
#endif #endif
@ -40,24 +41,40 @@ static float adjust_sigma(float sigma, int *scaleFactor, int *radius) {
static void convolve_gaussian(GrContext* context, static void convolve_gaussian(GrContext* context,
GrTexture* texture, GrTexture* texture,
const SkRect& rect, const SkRect& srcRect,
const SkRect& dstRect,
bool cropToSrcRect,
float sigma, float sigma,
int radius, int radius,
Gr1DKernelEffect::Direction direction) { Gr1DKernelEffect::Direction direction) {
GrPaint paint; GrPaint paint;
paint.reset();
float cropRect[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
if (cropToSrcRect) {
if (direction == Gr1DKernelEffect::kX_Direction) {
cropRect[0] = SkScalarToFloat(srcRect.left()) / texture->width();
cropRect[1] = SkScalarToFloat(srcRect.right()) / texture->width();
} else {
cropRect[2] = SkScalarToFloat(srcRect.top()) / texture->height();
cropRect[3] = SkScalarToFloat(srcRect.bottom()) / texture->height();
}
}
SkAutoTUnref<GrEffectRef> conv(GrConvolutionEffect::CreateGaussian(texture, SkAutoTUnref<GrEffectRef> conv(GrConvolutionEffect::CreateGaussian(texture,
direction, direction,
radius, radius,
sigma)); sigma,
cropToSrcRect,
cropRect));
paint.addColorEffect(conv); paint.addColorEffect(conv);
context->drawRect(paint, rect); context->drawRectToRect(paint, dstRect, srcRect);
} }
GrTexture* GaussianBlur(GrContext* context, GrTexture* GaussianBlur(GrContext* context,
GrTexture* srcTexture, GrTexture* srcTexture,
bool canClobberSrc, bool canClobberSrc,
const SkRect& rect, const SkRect& rect,
bool cropToRect,
float sigmaX, float sigmaX,
float sigmaY) { float sigmaY) {
GrAssert(NULL != context); GrAssert(NULL != context);
@ -79,7 +96,7 @@ GrTexture* GaussianBlur(GrContext* context,
scale_rect(&srcRect, static_cast<float>(scaleFactorX), scale_rect(&srcRect, static_cast<float>(scaleFactorX),
static_cast<float>(scaleFactorY)); static_cast<float>(scaleFactorY));
GrContext::AutoClip acs(context, srcRect); GrContext::AutoClip acs(context, SkRect::MakeWH(srcRect.width(), srcRect.height()));
GrAssert(kBGRA_8888_GrPixelConfig == srcTexture->config() || GrAssert(kBGRA_8888_GrPixelConfig == srcTexture->config() ||
kRGBA_8888_GrPixelConfig == srcTexture->config() || kRGBA_8888_GrPixelConfig == srcTexture->config() ||
@ -104,10 +121,25 @@ GrTexture* GaussianBlur(GrContext* context,
matrix.setIDiv(srcTexture->width(), srcTexture->height()); matrix.setIDiv(srcTexture->width(), srcTexture->height());
context->setRenderTarget(dstTexture->asRenderTarget()); context->setRenderTarget(dstTexture->asRenderTarget());
SkRect dstRect(srcRect); SkRect dstRect(srcRect);
if (cropToRect && i == 1) {
dstRect.offset(-dstRect.fLeft, -dstRect.fTop);
SkRect domain;
matrix.mapRect(&domain, rect);
domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f,
i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f);
SkAutoTUnref<GrEffectRef> effect(GrTextureDomainEffect::Create(
srcTexture,
matrix,
domain,
GrTextureDomainEffect::kDecal_WrapMode,
true));
paint.addColorEffect(effect);
} else {
GrTextureParams params(SkShader::kClamp_TileMode, true);
paint.addColorTextureEffect(srcTexture, matrix, params);
}
scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f, scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f,
i < scaleFactorY ? 0.5f : 1.0f); i < scaleFactorY ? 0.5f : 1.0f);
GrTextureParams params(SkShader::kClamp_TileMode, true);
paint.addColorTextureEffect(srcTexture, matrix, params);
context->drawRectToRect(paint, dstRect, srcRect); context->drawRectToRect(paint, dstRect, srcRect);
srcRect = dstRect; srcRect = dstRect;
srcTexture = dstTexture; srcTexture = dstTexture;
@ -126,9 +158,11 @@ GrTexture* GaussianBlur(GrContext* context,
context->clear(&clearRect, 0x0); context->clear(&clearRect, 0x0);
} }
context->setRenderTarget(dstTexture->asRenderTarget()); context->setRenderTarget(dstTexture->asRenderTarget());
convolve_gaussian(context, srcTexture, srcRect, sigmaX, radiusX, SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
Gr1DKernelEffect::kX_Direction); convolve_gaussian(context, srcTexture, srcRect, dstRect, cropToRect,
sigmaX, radiusX, Gr1DKernelEffect::kX_Direction);
srcTexture = dstTexture; srcTexture = dstTexture;
srcRect = dstRect;
SkTSwap(dstTexture, tempTexture); SkTSwap(dstTexture, tempTexture);
} }
@ -142,9 +176,11 @@ GrTexture* GaussianBlur(GrContext* context,
} }
context->setRenderTarget(dstTexture->asRenderTarget()); context->setRenderTarget(dstTexture->asRenderTarget());
convolve_gaussian(context, srcTexture, srcRect, sigmaY, radiusY, SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
Gr1DKernelEffect::kY_Direction); convolve_gaussian(context, srcTexture, srcRect, dstRect, cropToRect,
sigmaY, radiusY, Gr1DKernelEffect::kY_Direction);
srcTexture = dstTexture; srcTexture = dstTexture;
srcRect = dstRect;
SkTSwap(dstTexture, tempTexture); SkTSwap(dstTexture, tempTexture);
} }

View File

@ -25,6 +25,8 @@ namespace SkGpuBlurUtils {
* @param canClobberSrc If true, srcTexture may be overwritten, and * @param canClobberSrc If true, srcTexture may be overwritten, and
* may be returned as the result. * may be returned as the result.
* @param rect The destination rectangle. * @param rect The destination rectangle.
* @param cropToRect If true, do not sample any pixels outside the
* source rect.
* @param sigmaX The blur's standard deviation in X. * @param sigmaX The blur's standard deviation in X.
* @param sigmaY The blur's standard deviation in Y. * @param sigmaY The blur's standard deviation in Y.
* @return the blurred texture, which may be srcTexture reffed, or a * @return the blurred texture, which may be srcTexture reffed, or a
@ -34,6 +36,7 @@ namespace SkGpuBlurUtils {
GrTexture* srcTexture, GrTexture* srcTexture,
bool canClobberSrc, bool canClobberSrc,
const SkRect& rect, const SkRect& rect,
bool cropToRect,
float sigmaX, float sigmaX,
float sigmaY); float sigmaY);
#endif #endif

View File

@ -1387,7 +1387,7 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
SkAutoCachedTexture act(this, bitmap, NULL, &texture); SkAutoCachedTexture act(this, bitmap, NULL, &texture);
SkImageFilter* filter = paint.getImageFilter(); SkImageFilter* filter = paint.getImageFilter();
SkIPoint offset = SkIPoint::Make(0, 0); SkIPoint offset = SkIPoint::Make(left, top);
// This bitmap will own the filtered result as a texture. // This bitmap will own the filtered result as a texture.
SkBitmap filteredBitmap; SkBitmap filteredBitmap;
@ -1396,6 +1396,8 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
texture = (GrTexture*) filteredBitmap.getTexture(); texture = (GrTexture*) filteredBitmap.getTexture();
w = filteredBitmap.width(); w = filteredBitmap.width();
h = filteredBitmap.height(); h = filteredBitmap.height();
} else {
return;
} }
} }
@ -1407,12 +1409,12 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
} }
fContext->drawRectToRect(grPaint, fContext->drawRectToRect(grPaint,
SkRect::MakeXYWH(SkIntToScalar(left),
SkIntToScalar(top),
SkIntToScalar(w),
SkIntToScalar(h)),
SkRect::MakeXYWH(SkIntToScalar(offset.fX), SkRect::MakeXYWH(SkIntToScalar(offset.fX),
SkIntToScalar(offset.fY), SkIntToScalar(offset.fY),
SkIntToScalar(w),
SkIntToScalar(h)),
SkRect::MakeXYWH(0,
0,
SK_Scalar1 * w / texture->width(), SK_Scalar1 * w / texture->width(),
SK_Scalar1 * h / texture->height())); SK_Scalar1 * h / texture->height()));
} }
@ -1481,6 +1483,8 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* device,
h = filteredBitmap.height(); h = filteredBitmap.height();
x += offset.fX; x += offset.fX;
y += offset.fY; y += offset.fY;
} else {
return;
} }
} }

View File

@ -33,10 +33,13 @@ public:
private: private:
int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); } int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); }
bool useCropRect() const { return fUseCropRect; }
int fRadius; int fRadius;
bool fUseCropRect;
UniformHandle fKernelUni; UniformHandle fKernelUni;
UniformHandle fImageIncrementUni; UniformHandle fImageIncrementUni;
UniformHandle fCropRectUni;
GrGLEffectMatrix fEffectMatrix; GrGLEffectMatrix fEffectMatrix;
typedef GrGLEffect INHERITED; typedef GrGLEffect INHERITED;
@ -47,9 +50,11 @@ GrGLConvolutionEffect::GrGLConvolutionEffect(const GrBackendEffectFactory& facto
: INHERITED(factory) : INHERITED(factory)
, fKernelUni(kInvalidUniformHandle) , fKernelUni(kInvalidUniformHandle)
, fImageIncrementUni(kInvalidUniformHandle) , fImageIncrementUni(kInvalidUniformHandle)
, fCropRectUni(kInvalidUniformHandle)
, fEffectMatrix(drawEffect.castEffect<GrConvolutionEffect>().coordsType()) { , fEffectMatrix(drawEffect.castEffect<GrConvolutionEffect>().coordsType()) {
const GrConvolutionEffect& c = drawEffect.castEffect<GrConvolutionEffect>(); const GrConvolutionEffect& c = drawEffect.castEffect<GrConvolutionEffect>();
fRadius = c.radius(); fRadius = c.radius();
fUseCropRect = c.useCropRect();
} }
void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder, void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
@ -62,12 +67,16 @@ void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &coords); fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &coords);
fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
kVec2f_GrSLType, "ImageIncrement"); kVec2f_GrSLType, "ImageIncrement");
if (this->useCropRect()) {
fCropRectUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
kVec4f_GrSLType, "CropRect");
}
fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType, fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
kFloat_GrSLType, "Kernel", this->width()); kFloat_GrSLType, "Kernel", this->width());
builder->fsCodeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor); builder->fsCodeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
int width = this ->width(); int width = this->width();
const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni); const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni);
const char* imgInc = builder->getUniformCStr(fImageIncrementUni); const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
@ -81,9 +90,15 @@ void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
kernel.appendArrayAccess(index.c_str(), &kernelIndex); kernel.appendArrayAccess(index.c_str(), &kernelIndex);
builder->fsCodeAppendf("\t\t%s += ", outputColor); builder->fsCodeAppendf("\t\t%s += ", outputColor);
builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType, samplers[0], "coord"); builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType, samplers[0], "coord");
if (this->useCropRect()) {
const char* cropRect = builder->getUniformCStr(fCropRectUni);
builder->fsCodeAppendf(" * float(coord.x >= %s.x && coord.x <= %s.y && coord.y >= %s.z && coord.y <= %s.w)",
cropRect, cropRect, cropRect, cropRect);
}
builder->fsCodeAppendf(" * %s;\n", kernelIndex.c_str()); builder->fsCodeAppendf(" * %s;\n", kernelIndex.c_str());
builder->fsCodeAppendf("\t\tcoord += %s;\n", imgInc); builder->fsCodeAppendf("\t\tcoord += %s;\n", imgInc);
} }
SkString modulate; SkString modulate;
GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor); GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
builder->fsCodeAppend(modulate.c_str()); builder->fsCodeAppend(modulate.c_str());
@ -96,17 +111,28 @@ void GrGLConvolutionEffect::setData(const GrGLUniformManager& uman,
// the code we generated was for a specific kernel radius // the code we generated was for a specific kernel radius
GrAssert(conv.radius() == fRadius); GrAssert(conv.radius() == fRadius);
float imageIncrement[2] = { 0 }; float imageIncrement[2] = { 0 };
float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
switch (conv.direction()) { switch (conv.direction()) {
case Gr1DKernelEffect::kX_Direction: case Gr1DKernelEffect::kX_Direction:
imageIncrement[0] = 1.0f / texture.width(); imageIncrement[0] = 1.0f / texture.width();
break; break;
case Gr1DKernelEffect::kY_Direction: case Gr1DKernelEffect::kY_Direction:
imageIncrement[1] = 1.0f / texture.height(); imageIncrement[1] = ySign / texture.height();
break; break;
default: default:
GrCrash("Unknown filter direction."); GrCrash("Unknown filter direction.");
} }
uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement); uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
if (conv.useCropRect()) {
float c[4];
memcpy(c, conv.cropRect(), sizeof(c));
if (texture.origin() != kTopLeft_GrSurfaceOrigin) {
float tmp = 1.0f - c[2];
c[2] = 1.0f - c[3];
c[3] = tmp;
}
uman.set4fv(fCropRectUni, 0, 1, c);
}
uman.set1fv(fKernelUni, 0, this->width(), conv.kernel()); uman.set1fv(fKernelUni, 0, this->width(), conv.kernel());
fEffectMatrix.setData(uman, conv.getMatrix(), drawEffect, conv.texture(0)); fEffectMatrix.setData(uman, conv.getMatrix(), drawEffect, conv.texture(0));
} }
@ -114,7 +140,8 @@ void GrGLConvolutionEffect::setData(const GrGLUniformManager& uman,
GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrDrawEffect& drawEffect, GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrDrawEffect& drawEffect,
const GrGLCaps&) { const GrGLCaps&) {
const GrConvolutionEffect& conv = drawEffect.castEffect<GrConvolutionEffect>(); const GrConvolutionEffect& conv = drawEffect.castEffect<GrConvolutionEffect>();
EffectKey key = conv.radius(); EffectKey key = conv.radius() << 1;
key |= conv.useCropRect() ? 0x1 : 0x0;
key <<= GrGLEffectMatrix::kKeyBits; key <<= GrGLEffectMatrix::kKeyBits;
EffectKey matrixKey = GrGLEffectMatrix::GenKey(conv.getMatrix(), EffectKey matrixKey = GrGLEffectMatrix::GenKey(conv.getMatrix(),
drawEffect, drawEffect,
@ -128,21 +155,26 @@ GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrDrawEffect& drawEffe
GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture, GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
Direction direction, Direction direction,
int radius, int radius,
const float* kernel) const float* kernel,
: Gr1DKernelEffect(texture, direction, radius) { bool useCropRect,
float cropRect[4])
: Gr1DKernelEffect(texture, direction, radius), fUseCropRect(useCropRect) {
GrAssert(radius <= kMaxKernelRadius); GrAssert(radius <= kMaxKernelRadius);
GrAssert(NULL != kernel); GrAssert(NULL != kernel);
int width = this->width(); int width = this->width();
for (int i = 0; i < width; i++) { for (int i = 0; i < width; i++) {
fKernel[i] = kernel[i]; fKernel[i] = kernel[i];
} }
memcpy(fCropRect, cropRect, sizeof(fCropRect));
} }
GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture, GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
Direction direction, Direction direction,
int radius, int radius,
float gaussianSigma) float gaussianSigma,
: Gr1DKernelEffect(texture, direction, radius) { bool useCropRect,
float cropRect[4])
: Gr1DKernelEffect(texture, direction, radius), fUseCropRect(useCropRect) {
GrAssert(radius <= kMaxKernelRadius); GrAssert(radius <= kMaxKernelRadius);
int width = this->width(); int width = this->width();
@ -160,6 +192,7 @@ GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
for (int i = 0; i < width; ++i) { for (int i = 0; i < width; ++i) {
fKernel[i] *= scale; fKernel[i] *= scale;
} }
memcpy(fCropRect, cropRect, sizeof(fCropRect));
} }
GrConvolutionEffect::~GrConvolutionEffect() { GrConvolutionEffect::~GrConvolutionEffect() {
@ -174,6 +207,7 @@ bool GrConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
return (this->texture(0) == s.texture(0) && return (this->texture(0) == s.texture(0) &&
this->radius() == s.radius() && this->radius() == s.radius() &&
this->direction() == s.direction() && this->direction() == s.direction() &&
0 == memcmp(fCropRect, s.fCropRect, sizeof(fCropRect)) &&
0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float))); 0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
} }
@ -190,9 +224,19 @@ GrEffectRef* GrConvolutionEffect::TestCreate(SkMWCRandom* random,
Direction dir = random->nextBool() ? kX_Direction : kY_Direction; Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
int radius = random->nextRangeU(1, kMaxKernelRadius); int radius = random->nextRangeU(1, kMaxKernelRadius);
float kernel[kMaxKernelRadius]; float kernel[kMaxKernelRadius];
float cropRect[4];
for (int i = 0; i < kMaxKernelRadius; ++i) { for (int i = 0; i < kMaxKernelRadius; ++i) {
kernel[i] = random->nextSScalar1(); kernel[i] = random->nextSScalar1();
} }
for (int i = 0; i < 4; ++i) {
cropRect[i] = random->nextF();
}
return GrConvolutionEffect::Create(textures[texIdx], dir, radius,kernel); bool useCropRect = random->nextBool();
return GrConvolutionEffect::Create(textures[texIdx],
dir,
radius,
kernel,
useCropRect,
cropRect);
} }

View File

@ -22,11 +22,18 @@ class GrConvolutionEffect : public Gr1DKernelEffect {
public: public:
/// Convolve with an arbitrary user-specified kernel /// Convolve with an arbitrary user-specified kernel
static GrEffectRef* Create(GrTexture* tex, Direction dir, int halfWidth, const float* kernel) { static GrEffectRef* Create(GrTexture* tex,
Direction dir,
int halfWidth,
const float* kernel,
bool useCropRect,
float cropRect[4]) {
AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex, AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex,
dir, dir,
halfWidth, halfWidth,
kernel))); kernel,
useCropRect,
cropRect)));
return CreateEffectRef(effect); return CreateEffectRef(effect);
} }
@ -34,11 +41,15 @@ public:
static GrEffectRef* CreateGaussian(GrTexture* tex, static GrEffectRef* CreateGaussian(GrTexture* tex,
Direction dir, Direction dir,
int halfWidth, int halfWidth,
float gaussianSigma) { float gaussianSigma,
bool useCropRect,
float cropRect[4]) {
AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex, AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex,
dir, dir,
halfWidth, halfWidth,
gaussianSigma))); gaussianSigma,
useCropRect,
cropRect)));
return CreateEffectRef(effect); return CreateEffectRef(effect);
} }
@ -46,6 +57,9 @@ public:
const float* kernel() const { return fKernel; } const float* kernel() const { return fKernel; }
const float* cropRect() const { return fCropRect; }
bool useCropRect() const { return fUseCropRect; }
static const char* Name() { return "Convolution"; } static const char* Name() { return "Convolution"; }
typedef GrGLConvolutionEffect GLEffect; typedef GrGLConvolutionEffect GLEffect;
@ -72,15 +86,22 @@ public:
protected: protected:
float fKernel[kMaxKernelWidth]; float fKernel[kMaxKernelWidth];
bool fUseCropRect;
float fCropRect[4];
private: private:
GrConvolutionEffect(GrTexture*, Direction, GrConvolutionEffect(GrTexture*, Direction,
int halfWidth, const float* kernel); int halfWidth,
const float* kernel,
bool useCropRect,
float cropRect[4]);
/// Convolve with a Gaussian kernel /// Convolve with a Gaussian kernel
GrConvolutionEffect(GrTexture*, Direction, GrConvolutionEffect(GrTexture*, Direction,
int halfWidth, int halfWidth,
float gaussianSigma); float gaussianSigma,
bool useCropRect,
float cropRect[4]);
virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;

78
tests/ImageFilterTest.cpp Normal file
View File

@ -0,0 +1,78 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Test.h"
#include "SkColorMatrixFilter.h"
#include "SkColorFilterImageFilter.h"
#include "SkRect.h"
class ImageFilterTest {
public:
static SkImageFilter* make_scale(float amount, SkImageFilter* input = NULL) {
SkScalar s = SkFloatToScalar(amount);
SkScalar matrix[20] = { s, 0, 0, 0, 0,
0, s, 0, 0, 0,
0, 0, s, 0, 0,
0, 0, 0, s, 0 };
SkAutoTUnref<SkColorFilter> filter(new SkColorMatrixFilter(matrix));
return SkColorFilterImageFilter::Create(filter, input);
}
static SkImageFilter* make_grayscale(SkImageFilter* input = NULL, const SkIRect* cropRect = NULL) {
SkScalar matrix[20];
memset(matrix, 0, 20 * sizeof(SkScalar));
matrix[0] = matrix[5] = matrix[10] = SkFloatToScalar(0.2126f);
matrix[1] = matrix[6] = matrix[11] = SkFloatToScalar(0.7152f);
matrix[2] = matrix[7] = matrix[12] = SkFloatToScalar(0.0722f);
matrix[18] = SkFloatToScalar(1.0f);
SkAutoTUnref<SkColorFilter> filter(new SkColorMatrixFilter(matrix));
return SkColorFilterImageFilter::Create(filter, input, cropRect);
}
static SkImageFilter* make_mode_blue(SkImageFilter* input = NULL) {
SkAutoTUnref<SkColorFilter> filter(
SkColorFilter::CreateModeFilter(SK_ColorBLUE, SkXfermode::kSrcIn_Mode));
return SkColorFilterImageFilter::Create(filter, input);
}
static void Test(skiatest::Reporter* reporter) {
{
// Check that two non-clipping color matrices concatenate into a single filter.
SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f));
SkAutoTUnref<SkImageFilter> quarterBrightness(make_scale(0.5f, halfBrightness));
REPORTER_ASSERT(reporter, NULL == quarterBrightness->getInput(0));
}
{
// Check that a clipping color matrix followed by a grayscale does not concatenate into a single filter.
SkAutoTUnref<SkImageFilter> doubleBrightness(make_scale(2.0f));
SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f, doubleBrightness));
REPORTER_ASSERT(reporter, NULL != halfBrightness->getInput(0));
}
{
// Check that a color filter image filter without a crop rect can be
// expressed as a color filter.
SkAutoTUnref<SkImageFilter> gray(make_grayscale());
REPORTER_ASSERT(reporter, true == gray->asColorFilter(NULL));
}
{
// Check that a color filter image filter with a crop rect cannot
// be expressed as a color filter.
SkIRect cropRect = SkIRect::MakeXYWH(0, 0, 100, 100);
SkAutoTUnref<SkImageFilter> grayWithCrop(make_grayscale(NULL, &cropRect));
REPORTER_ASSERT(reporter, false == grayWithCrop->asColorFilter(NULL));
}
}
};
#include "TestClassDef.h"
DEFINE_TESTCLASS("ImageFilterTest", ImageFilterTestClass, ImageFilterTest::Test)