This patch implements a crop rect for SkImageFilter. It has been implemented for...
authorsenorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 22 Jul 2013 20:03:22 +0000 (20:03 +0000)
committersenorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 22 Jul 2013 20:03:22 +0000 (20:03 +0000)
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

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

git-svn-id: http://skia.googlecode.com/svn/trunk@10251 2bbb7eff-a529-9590-31e7-b0007b416f81

14 files changed:
gm/imagefilterscropped.cpp [new file with mode: 0644]
gyp/gmslides.gypi
include/core/SkImageFilter.h
include/core/SkRect.h
include/effects/SkBicubicImageFilter.h
include/effects/SkBlurImageFilter.h
include/effects/SkColorFilterImageFilter.h
src/core/SkImageFilter.cpp
src/effects/SkBlurImageFilter.cpp
src/effects/SkColorFilterImageFilter.cpp
src/effects/SkGpuBlurUtils.cpp
src/gpu/SkGpuDevice.cpp
src/gpu/effects/GrConvolutionEffect.cpp
src/gpu/effects/GrConvolutionEffect.h

diff --git a/gm/imagefilterscropped.cpp b/gm/imagefilterscropped.cpp
new file mode 100644 (file)
index 0000000..f1ede2f
--- /dev/null
@@ -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);
index 9d335496ebee74d7af288365999c811b74ccad83..46b1bd5bfc82bb7124efbdc8a665ce8ab4e9a19d 100644 (file)
@@ -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',
index fe383ae9e316d632ce04eb15efdd0b40efeeb46c..e467761a1f0607e2ce244003be132c295c4f9874 100644 (file)
@@ -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
index b2f515129d05bf9a0cca392f880f0d6c75d22e25..9f3b59a38f7a29936de4dc0c9cc4564ae3472278 100644 (file)
@@ -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));
     }
index 75cd27df7464715136d8ad133ac5e54b83b40b7a..669636543395c3cbe63a890253bf5bb95a57388b 100644 (file)
@@ -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();
index 56b1f3543e2c137ee743840afe91cebeb749a4ef..f2677353e071d18127c28cec007776b83d13fc43 100644 (file)
 
 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)
 
index 2e5e59c3c0969c47c3de63b1dbeec2a8f8eb92a9..314ab070b65e827870b33d3c118c62e1a9383db6 100755 (executable)
@@ -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;
index ddd6449490ba0c0734cfaa925a610f24f704485b..ff060a1a52cb8e764905959f62d06af9617e53ee 100644 (file)
 
 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;
index 157a0c333573cc44253343f2ec3ceabd1955b8ea..0f0c809e55b030c62d28996cbb453cbf71af0503 100644 (file)
@@ -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;
index 1d3cfee1c398fd413f9399f805ee92a82162330f..a24934e0e235940dd784e119ed39a712f7293a80 100755 (executable)
@@ -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;
 }
index 479648850c67b8e219e8bf5d06e05cde715fa27c..5b52ad3cea9748bb1b4a14127aa98cef63c2a100 100644 (file)
@@ -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);
     }
 
index eaac5ab12c0a50264b65689c2f7d6701f1d5b662..4456f513c4806258a8219dcd9f5c279afaa6e323 100644 (file)
@@ -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;
         }
     }
 
index 380581fae360795c6447725e701f00fcbb7bb173..5682b9c65cb5a671c2eb6ade2a4648e94655b2f2 100644 (file)
@@ -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);
 }
index e4faa945b833f763502e5be8cc572a061e9c43da..f75cfe91556c3e01b2c69ace7c8e31aa074882b7 100644 (file)
@@ -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;