aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/imagefilterscropped.cpp176
-rw-r--r--gyp/gmslides.gypi1
-rw-r--r--include/core/SkImageFilter.h24
-rw-r--r--include/core/SkRect.h11
-rw-r--r--include/effects/SkBicubicImageFilter.h3
-rw-r--r--include/effects/SkBlurImageFilter.h5
-rwxr-xr-xinclude/effects/SkColorFilterImageFilter.h8
-rw-r--r--src/core/SkImageFilter.cpp23
-rw-r--r--src/effects/SkBlurImageFilter.cpp71
-rwxr-xr-xsrc/effects/SkColorFilterImageFilter.cpp27
-rw-r--r--src/effects/SkGpuBlurUtils.cpp47
-rw-r--r--src/gpu/SkGpuDevice.cpp14
-rw-r--r--src/gpu/effects/GrConvolutionEffect.cpp35
-rw-r--r--src/gpu/effects/GrConvolutionEffect.h23
14 files changed, 395 insertions, 73 deletions
diff --git a/gm/imagefilterscropped.cpp b/gm/imagefilterscropped.cpp
new file mode 100644
index 0000000000..f1ede2fefd
--- /dev/null
+++ b/gm/imagefilterscropped.cpp
@@ -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);
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 9d335496eb..46b1bd5bfc 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -63,6 +63,7 @@
'../gm/lighting.cpp',
'../gm/image.cpp',
'../gm/imagefiltersbase.cpp',
+ '../gm/imagefilterscropped.cpp',
'../gm/imagefiltersgraph.cpp',
'../gm/internal_links.cpp',
'../gm/lcdtext.cpp',
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index fe383ae9e3..e467761a1f 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -9,13 +9,13 @@
#define SkImageFilter_DEFINED
#include "SkFlattenable.h"
+#include "SkRect.h"
class SkBitmap;
class SkColorFilter;
class SkDevice;
class SkMatrix;
struct SkIPoint;
-struct SkIRect;
class SkShader;
class GrEffectRef;
class GrTexture;
@@ -139,14 +139,25 @@ public:
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:
- SkImageFilter(int inputCount, SkImageFilter** inputs);
+ SkImageFilter(int inputCount, SkImageFilter** inputs, const SkIRect* cropRect = NULL);
// Convenience constructor for 1-input filters.
- explicit SkImageFilter(SkImageFilter* input);
+ explicit SkImageFilter(SkImageFilter* input, const SkIRect* cropRect = NULL);
// Convenience constructor for 2-input filters.
- SkImageFilter(SkImageFilter* input1, SkImageFilter* input2);
+ SkImageFilter(SkImageFilter* input1, SkImageFilter* input2, const SkIRect* cropRect = NULL);
virtual ~SkImageFilter();
@@ -160,10 +171,15 @@ protected:
// Default impl copies src into dst and returns true
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:
typedef SkFlattenable INHERITED;
int fInputCount;
SkImageFilter** fInputs;
+ SkIRect fCropRect;
};
#endif
diff --git a/include/core/SkRect.h b/include/core/SkRect.h
index b2f515129d..9f3b59a38f 100644
--- a/include/core/SkRect.h
+++ b/include/core/SkRect.h
@@ -26,6 +26,12 @@ struct SK_API SkIRect {
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) {
SkIRect r;
r.set(0, 0, w, h);
@@ -94,6 +100,11 @@ struct SK_API SkIRect {
*/
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) {
return !memcmp(&a, &b, sizeof(a));
}
diff --git a/include/effects/SkBicubicImageFilter.h b/include/effects/SkBicubicImageFilter.h
index 75cd27df74..6696365433 100644
--- a/include/effects/SkBicubicImageFilter.h
+++ b/include/effects/SkBicubicImageFilter.h
@@ -27,7 +27,8 @@ public:
passed to filterImage() is used instead.
*/
- SkBicubicImageFilter(const SkSize& scale, const SkScalar coefficients[16],
+ SkBicubicImageFilter(const SkSize& scale,
+ const SkScalar coefficients[16],
SkImageFilter* input = NULL);
static SkBicubicImageFilter* CreateMitchell(const SkSize& scale, SkImageFilter* input = NULL);
virtual ~SkBicubicImageFilter();
diff --git a/include/effects/SkBlurImageFilter.h b/include/effects/SkBlurImageFilter.h
index 56b1f3543e..f2677353e0 100644
--- a/include/effects/SkBlurImageFilter.h
+++ b/include/effects/SkBlurImageFilter.h
@@ -13,7 +13,10 @@
class SK_API SkBlurImageFilter : public SkImageFilter {
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)
diff --git a/include/effects/SkColorFilterImageFilter.h b/include/effects/SkColorFilterImageFilter.h
index 2e5e59c3c0..314ab070b6 100755
--- a/include/effects/SkColorFilterImageFilter.h
+++ b/include/effects/SkColorFilterImageFilter.h
@@ -14,7 +14,9 @@ class SkColorFilter;
class SK_API SkColorFilterImageFilter : public SkImageFilter {
public:
- static SkColorFilterImageFilter* Create(SkColorFilter* cf, SkImageFilter* input = NULL);
+ static SkColorFilterImageFilter* Create(SkColorFilter* cf,
+ SkImageFilter* input = NULL,
+ const SkIRect* cropRect = NULL);
virtual ~SkColorFilterImageFilter();
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorFilterImageFilter)
@@ -29,7 +31,9 @@ protected:
virtual bool asColorFilter(SkColorFilter**) const SK_OVERRIDE;
private:
- SkColorFilterImageFilter(SkColorFilter* cf, SkImageFilter* input);
+ SkColorFilterImageFilter(SkColorFilter* cf,
+ SkImageFilter* input,
+ const SkIRect* cropRect = NULL);
SkColorFilter* fColorFilter;
typedef SkImageFilter INHERITED;
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
index ddd6449490..ff060a1a52 100644
--- a/src/core/SkImageFilter.cpp
+++ b/src/core/SkImageFilter.cpp
@@ -18,22 +18,27 @@
SK_DEFINE_INST_COUNT(SkImageFilter)
-SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs)
- : fInputCount(inputCount), fInputs(new SkImageFilter*[inputCount]) {
+SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const SkIRect* cropRect)
+ : fInputCount(inputCount),
+ fInputs(new SkImageFilter*[inputCount]),
+ fCropRect(cropRect ? *cropRect : SkIRect::MakeLargest()) {
for (int i = 0; i < inputCount; ++i) {
fInputs[i] = inputs[i];
SkSafeRef(fInputs[i]);
}
}
-SkImageFilter::SkImageFilter(SkImageFilter* input)
- : fInputCount(1), fInputs(new SkImageFilter*[1]) {
+SkImageFilter::SkImageFilter(SkImageFilter* input, const SkIRect* cropRect)
+ : fInputCount(1),
+ fInputs(new SkImageFilter*[1]),
+ fCropRect(cropRect ? *cropRect : SkIRect::MakeLargest()) {
fInputs[0] = input;
SkSafeRef(fInputs[0]);
}
-SkImageFilter::SkImageFilter(SkImageFilter* input1, SkImageFilter* input2)
- : fInputCount(2), fInputs(new SkImageFilter*[2]) {
+SkImageFilter::SkImageFilter(SkImageFilter* input1, SkImageFilter* input2, const SkIRect* cropRect)
+ : fInputCount(2), fInputs(new SkImageFilter*[2]),
+ fCropRect(cropRect ? *cropRect : SkIRect::MakeLargest()) {
fInputs[0] = input1;
fInputs[1] = input2;
SkSafeRef(fInputs[0]);
@@ -56,6 +61,7 @@ SkImageFilter::SkImageFilter(SkFlattenableReadBuffer& buffer)
fInputs[i] = NULL;
}
}
+ buffer.readIRect(&fCropRect);
}
void SkImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
@@ -67,6 +73,7 @@ void SkImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
buffer.writeFlattenable(input);
}
}
+ buffer.writeIRect(fCropRect);
}
bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
@@ -137,6 +144,10 @@ bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap*
#endif
}
+bool SkImageFilter::applyCropRect(SkIRect* rect) const {
+ return rect->intersect(fCropRect);
+}
+
bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
SkIRect* dst) {
*dst = src;
diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp
index 157a0c3335..0f0c809e55 100644
--- a/src/effects/SkBlurImageFilter.cpp
+++ b/src/effects/SkBlurImageFilter.cpp
@@ -21,8 +21,11 @@ SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer)
fSigma.fHeight = buffer.readScalar();
}
-SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input)
- : INHERITED(input), fSigma(SkSize::Make(sigmaX, sigmaY)) {
+SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX,
+ SkScalar sigmaY,
+ SkImageFilter* input,
+ const SkIRect* cropRect)
+ : INHERITED(input, cropRect), fSigma(SkSize::Make(sigmaX, sigmaY)) {
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,
- 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);
for (int y = 0; y < height; ++y) {
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) {
sumA += SkGetPackedA32(*p);
sumR += SkGetPackedR32(*p);
@@ -48,7 +51,7 @@ static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize,
p++;
}
- const SkColor* sptr = src.getAddr32(0, y);
+ const SkColor* sptr = src.getAddr32(bounds.fLeft, bounds.fTop + y);
SkColor* dptr = dst->getAddr32(0, y);
for (int x = 0; x < width; ++x) {
*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,
- 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 srcStride = src.rowBytesAsPixels();
int dstStride = dst->rowBytesAsPixels();
for (int x = 0; x < width; ++x) {
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) {
sumA += SkGetPackedA32(*p);
sumR += SkGetPackedR32(*p);
@@ -93,7 +96,7 @@ static void boxBlurY(const SkBitmap& src, SkBitmap* dst, int kernelSize,
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);
for (int y = 0; y < height; ++y) {
*dptr = SkPackARGB32(sumA / kernelSize,
@@ -153,7 +156,14 @@ bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
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();
int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
@@ -176,21 +186,23 @@ bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
}
if (kernelSizeX > 0 && kernelSizeY > 0) {
- boxBlurX(src, &temp, kernelSizeX, lowOffsetX, highOffsetX);
- boxBlurY(temp, dst, kernelSizeY, lowOffsetY, highOffsetY);
- boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX);
- boxBlurY(temp, dst, kernelSizeY, highOffsetY, lowOffsetY);
- boxBlurX(*dst, &temp, kernelSizeX3, highOffsetX, highOffsetX);
- boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY);
+ boxBlurX(src, &temp, kernelSizeX, lowOffsetX, highOffsetX, srcBounds);
+ boxBlurY(temp, dst, kernelSizeY, lowOffsetY, highOffsetY, dstBounds);
+ boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX, dstBounds);
+ boxBlurY(temp, dst, kernelSizeY, highOffsetY, lowOffsetY, dstBounds);
+ boxBlurX(*dst, &temp, kernelSizeX3, highOffsetX, highOffsetX, dstBounds);
+ boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY, dstBounds);
} else if (kernelSizeX > 0) {
- boxBlurX(src, dst, kernelSizeX, lowOffsetX, highOffsetX);
- boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX);
- boxBlurX(temp, dst, kernelSizeX3, highOffsetX, highOffsetX);
+ boxBlurX(src, dst, kernelSizeX, lowOffsetX, highOffsetX, srcBounds);
+ boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX, dstBounds);
+ boxBlurX(temp, dst, kernelSizeX3, highOffsetX, highOffsetX, dstBounds);
} else if (kernelSizeY > 0) {
- boxBlurY(src, dst, kernelSizeY, lowOffsetY, highOffsetY);
- boxBlurY(*dst, &temp, kernelSizeY, highOffsetY, lowOffsetY);
- boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY);
+ boxBlurY(src, dst, kernelSizeY, lowOffsetY, highOffsetY, srcBounds);
+ boxBlurY(*dst, &temp, kernelSizeY, highOffsetY, lowOffsetY, dstBounds);
+ boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY, dstBounds);
}
+ offset->fX += srcBounds.fLeft;
+ offset->fY += srcBounds.fTop;
return true;
}
@@ -202,12 +214,17 @@ bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitm
return false;
}
GrTexture* source = input.getTexture();
- SkRect rect;
+ SkIRect rect;
src.getBounds(&rect);
- SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(),
- source, false, rect,
+ if (!this->applyCropRect(&rect)) {
+ return false;
+ }
+ SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(),
+ source, false, SkRect::Make(rect),
fSigma.width(), fSigma.height()));
- return SkImageFilterUtils::WrapTexture(tex, src.width(), src.height(), result);
+ offset->fX += rect.fLeft;
+ offset->fY += rect.fTop;
+ return SkImageFilterUtils::WrapTexture(tex, rect.width(), rect.height(), result);
#else
SkDEBUGFAIL("Should not call in GPU-less build");
return false;
diff --git a/src/effects/SkColorFilterImageFilter.cpp b/src/effects/SkColorFilterImageFilter.cpp
index 1d3cfee1c3..a24934e0e2 100755
--- a/src/effects/SkColorFilterImageFilter.cpp
+++ b/src/effects/SkColorFilterImageFilter.cpp
@@ -57,7 +57,7 @@ bool matrix_needs_clamping(SkScalar matrix[20]) {
};
SkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf,
- SkImageFilter* input) {
+ SkImageFilter* input, const SkIRect* cropRect) {
SkASSERT(cf);
SkScalar colorMatrix[20], inputMatrix[20];
SkColorFilter* inputColorFilter;
@@ -69,13 +69,15 @@ SkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf,
SkScalar combinedMatrix[20];
mult_color_matrix(inputMatrix, colorMatrix, 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);
SkSafeRef(cf);
}
@@ -103,22 +105,31 @@ bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& sourc
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());
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
paint.setColorFilter(fColorFilter);
- canvas.drawSprite(src, 0, 0, &paint);
+ canvas.drawSprite(src, -bounds.fLeft, -bounds.fTop, &paint);
*result = device.get()->accessBitmap(false);
+ loc->fX += bounds.fLeft;
+ loc->fY += bounds.fTop;
return true;
}
bool SkColorFilterImageFilter::asColorFilter(SkColorFilter** filter) const {
- if (filter) {
+ if (filter && cropRect().isLargest()) {
*filter = fColorFilter;
fColorFilter->ref();
+ return true;
}
- return true;
+ return false;
}
diff --git a/src/effects/SkGpuBlurUtils.cpp b/src/effects/SkGpuBlurUtils.cpp
index 479648850c..5b52ad3cea 100644
--- a/src/effects/SkGpuBlurUtils.cpp
+++ b/src/effects/SkGpuBlurUtils.cpp
@@ -11,6 +11,7 @@
#if SK_SUPPORT_GPU
#include "effects/GrConvolutionEffect.h"
+#include "effects/GrTextureDomainEffect.h"
#include "GrContext.h"
#endif
@@ -40,18 +41,29 @@ static float adjust_sigma(float sigma, int *scaleFactor, int *radius) {
static void convolve_gaussian(GrContext* context,
GrTexture* texture,
- const SkRect& rect,
+ const SkRect& srcRect,
+ const SkRect& dstRect,
float sigma,
int radius,
Gr1DKernelEffect::Direction direction) {
GrPaint paint;
+ paint.reset();
+ float cropRect[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
+ 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,
direction,
radius,
- sigma));
+ sigma,
+ cropRect));
paint.addColorEffect(conv);
- context->drawRect(paint, rect);
+ context->drawRectToRect(paint, dstRect, srcRect);
}
GrTexture* GaussianBlur(GrContext* context,
@@ -79,7 +91,7 @@ GrTexture* GaussianBlur(GrContext* context,
scale_rect(&srcRect, static_cast<float>(scaleFactorX),
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() ||
kRGBA_8888_GrPixelConfig == srcTexture->config() ||
@@ -104,10 +116,25 @@ GrTexture* GaussianBlur(GrContext* context,
matrix.setIDiv(srcTexture->width(), srcTexture->height());
context->setRenderTarget(dstTexture->asRenderTarget());
SkRect dstRect(srcRect);
+ if (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,
i < scaleFactorY ? 0.5f : 1.0f);
- GrTextureParams params(SkShader::kClamp_TileMode, true);
- paint.addColorTextureEffect(srcTexture, matrix, params);
context->drawRectToRect(paint, dstRect, srcRect);
srcRect = dstRect;
srcTexture = dstTexture;
@@ -126,9 +153,11 @@ GrTexture* GaussianBlur(GrContext* context,
context->clear(&clearRect, 0x0);
}
context->setRenderTarget(dstTexture->asRenderTarget());
- convolve_gaussian(context, srcTexture, srcRect, sigmaX, radiusX,
+ SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
+ convolve_gaussian(context, srcTexture, srcRect, dstRect, sigmaX, radiusX,
Gr1DKernelEffect::kX_Direction);
srcTexture = dstTexture;
+ srcRect = dstRect;
SkTSwap(dstTexture, tempTexture);
}
@@ -142,9 +171,11 @@ GrTexture* GaussianBlur(GrContext* context,
}
context->setRenderTarget(dstTexture->asRenderTarget());
- convolve_gaussian(context, srcTexture, srcRect, sigmaY, radiusY,
+ SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
+ convolve_gaussian(context, srcTexture, srcRect, dstRect, sigmaY, radiusY,
Gr1DKernelEffect::kY_Direction);
srcTexture = dstTexture;
+ srcRect = dstRect;
SkTSwap(dstTexture, tempTexture);
}
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index eaac5ab12c..4456f513c4 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1387,7 +1387,7 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
SkAutoCachedTexture act(this, bitmap, NULL, &texture);
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.
SkBitmap filteredBitmap;
@@ -1396,6 +1396,8 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
texture = (GrTexture*) filteredBitmap.getTexture();
w = filteredBitmap.width();
h = filteredBitmap.height();
+ } else {
+ return;
}
}
@@ -1407,12 +1409,12 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
}
fContext->drawRectToRect(grPaint,
- SkRect::MakeXYWH(SkIntToScalar(left),
- SkIntToScalar(top),
- SkIntToScalar(w),
- SkIntToScalar(h)),
SkRect::MakeXYWH(SkIntToScalar(offset.fX),
SkIntToScalar(offset.fY),
+ SkIntToScalar(w),
+ SkIntToScalar(h)),
+ SkRect::MakeXYWH(0,
+ 0,
SK_Scalar1 * w / texture->width(),
SK_Scalar1 * h / texture->height()));
}
@@ -1481,6 +1483,8 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* device,
h = filteredBitmap.height();
x += offset.fX;
y += offset.fY;
+ } else {
+ return;
}
}
diff --git a/src/gpu/effects/GrConvolutionEffect.cpp b/src/gpu/effects/GrConvolutionEffect.cpp
index 380581fae3..5682b9c65c 100644
--- a/src/gpu/effects/GrConvolutionEffect.cpp
+++ b/src/gpu/effects/GrConvolutionEffect.cpp
@@ -37,6 +37,7 @@ private:
int fRadius;
UniformHandle fKernelUni;
UniformHandle fImageIncrementUni;
+ UniformHandle fCropRectUni;
GrGLEffectMatrix fEffectMatrix;
typedef GrGLEffect INHERITED;
@@ -47,6 +48,7 @@ GrGLConvolutionEffect::GrGLConvolutionEffect(const GrBackendEffectFactory& facto
: INHERITED(factory)
, fKernelUni(kInvalidUniformHandle)
, fImageIncrementUni(kInvalidUniformHandle)
+ , fCropRectUni(kInvalidUniformHandle)
, fEffectMatrix(drawEffect.castEffect<GrConvolutionEffect>().coordsType()) {
const GrConvolutionEffect& c = drawEffect.castEffect<GrConvolutionEffect>();
fRadius = c.radius();
@@ -62,6 +64,8 @@ void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, &coords);
fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
kVec2f_GrSLType, "ImageIncrement");
+ fCropRectUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+ kVec4f_GrSLType, "CropRect");
fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
kFloat_GrSLType, "Kernel", this->width());
@@ -70,6 +74,7 @@ void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
int width = this ->width();
const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni);
const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
+ const char* cropRect = builder->getUniformCStr(fCropRectUni);
builder->fsCodeAppendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords, fRadius, imgInc);
@@ -81,9 +86,11 @@ void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
kernel.appendArrayAccess(index.c_str(), &kernelIndex);
builder->fsCodeAppendf("\t\t%s += ", outputColor);
builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType, samplers[0], "coord");
- builder->fsCodeAppendf(" * %s;\n", kernelIndex.c_str());
+ builder->fsCodeAppendf(" * float(coord.x >= %s.x && coord.x <= %s.y && coord.y >= %s.z && coord.y <= %s.w) * %s;\n",
+ cropRect, cropRect, cropRect, cropRect, kernelIndex.c_str());
builder->fsCodeAppendf("\t\tcoord += %s;\n", imgInc);
}
+
SkString modulate;
GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
builder->fsCodeAppend(modulate.c_str());
@@ -96,17 +103,26 @@ void GrGLConvolutionEffect::setData(const GrGLUniformManager& uman,
// the code we generated was for a specific kernel radius
GrAssert(conv.radius() == fRadius);
float imageIncrement[2] = { 0 };
+ float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
switch (conv.direction()) {
case Gr1DKernelEffect::kX_Direction:
imageIncrement[0] = 1.0f / texture.width();
break;
case Gr1DKernelEffect::kY_Direction:
- imageIncrement[1] = 1.0f / texture.height();
+ imageIncrement[1] = ySign / texture.height();
break;
default:
GrCrash("Unknown filter direction.");
}
uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
+ 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());
fEffectMatrix.setData(uman, conv.getMatrix(), drawEffect, conv.texture(0));
}
@@ -128,7 +144,8 @@ GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrDrawEffect& drawEffe
GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
Direction direction,
int radius,
- const float* kernel)
+ const float* kernel,
+ float cropRect[4])
: Gr1DKernelEffect(texture, direction, radius) {
GrAssert(radius <= kMaxKernelRadius);
GrAssert(NULL != kernel);
@@ -136,12 +153,14 @@ GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
for (int i = 0; i < width; i++) {
fKernel[i] = kernel[i];
}
+ memcpy(fCropRect, cropRect, sizeof(fCropRect));
}
GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
Direction direction,
int radius,
- float gaussianSigma)
+ float gaussianSigma,
+ float cropRect[4])
: Gr1DKernelEffect(texture, direction, radius) {
GrAssert(radius <= kMaxKernelRadius);
int width = this->width();
@@ -160,6 +179,7 @@ GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
for (int i = 0; i < width; ++i) {
fKernel[i] *= scale;
}
+ memcpy(fCropRect, cropRect, sizeof(fCropRect));
}
GrConvolutionEffect::~GrConvolutionEffect() {
@@ -174,6 +194,7 @@ bool GrConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
return (this->texture(0) == s.texture(0) &&
this->radius() == s.radius() &&
this->direction() == s.direction() &&
+ 0 == memcmp(fCropRect, s.fCropRect, sizeof(fCropRect)) &&
0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
}
@@ -190,9 +211,13 @@ GrEffectRef* GrConvolutionEffect::TestCreate(SkMWCRandom* random,
Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
int radius = random->nextRangeU(1, kMaxKernelRadius);
float kernel[kMaxKernelRadius];
+ float cropRect[4];
for (int i = 0; i < kMaxKernelRadius; ++i) {
kernel[i] = random->nextSScalar1();
}
+ for (int i = 0; i < 4; ++i) {
+ cropRect[i] = random->nextF();
+ }
- return GrConvolutionEffect::Create(textures[texIdx], dir, radius,kernel);
+ return GrConvolutionEffect::Create(textures[texIdx], dir, radius, kernel, cropRect);
}
diff --git a/src/gpu/effects/GrConvolutionEffect.h b/src/gpu/effects/GrConvolutionEffect.h
index e4faa945b8..f75cfe9155 100644
--- a/src/gpu/effects/GrConvolutionEffect.h
+++ b/src/gpu/effects/GrConvolutionEffect.h
@@ -22,11 +22,16 @@ class GrConvolutionEffect : public Gr1DKernelEffect {
public:
/// 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,
+ float cropRect[4]) {
AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex,
dir,
halfWidth,
- kernel)));
+ kernel,
+ cropRect)));
return CreateEffectRef(effect);
}
@@ -34,11 +39,13 @@ public:
static GrEffectRef* CreateGaussian(GrTexture* tex,
Direction dir,
int halfWidth,
- float gaussianSigma) {
+ float gaussianSigma,
+ float cropRect[4]) {
AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex,
dir,
halfWidth,
- gaussianSigma)));
+ gaussianSigma,
+ cropRect)));
return CreateEffectRef(effect);
}
@@ -46,6 +53,8 @@ public:
const float* kernel() const { return fKernel; }
+ const float* cropRect() const { return fCropRect; }
+
static const char* Name() { return "Convolution"; }
typedef GrGLConvolutionEffect GLEffect;
@@ -72,15 +81,17 @@ public:
protected:
float fKernel[kMaxKernelWidth];
+ float fCropRect[4];
private:
GrConvolutionEffect(GrTexture*, Direction,
- int halfWidth, const float* kernel);
+ int halfWidth, const float* kernel, float cropRect[4]);
/// Convolve with a Gaussian kernel
GrConvolutionEffect(GrTexture*, Direction,
int halfWidth,
- float gaussianSigma);
+ float gaussianSigma,
+ float cropRect[4]);
virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;