Implement srcRect and dstRect functionality in SkBitmapSource. This is required for the "preserveAspectRatio" options of SVG's feImage. Covered by new GM "bitmapsource".

Note: I initially implemented this as a fully-generic SkResizeImageFilter, but then I realized that the dstRect should always be transformed by the filter matrix, but that the srcRect should not (since it's specified relative to the dimensions of the original bitmap). Since this would be confusing for someone attempting to use this as a generic resizing filter, I decided to build the functionality into SkBitmapSource instead.

BUG=
R=reed@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@12522 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/bitmapsource.cpp b/gm/bitmapsource.cpp
new file mode 100644
index 0000000..f3507eb
--- /dev/null
+++ b/gm/bitmapsource.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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 "gm.h"
+
+#include "SkBitmapSource.h"
+
+class BitmapSourceGM : public skiagm::GM {
+public:
+    BitmapSourceGM() : fInitialized(false) {
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("bitmapsource");
+    }
+
+    void make_bitmap() {
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+        fBitmap.allocPixels();
+        SkBitmapDevice device(fBitmap);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xFFFFFFFF);
+        paint.setTextSize(SkIntToScalar(96));
+        const char* str = "e";
+        canvas.drawText(str, strlen(str), SkIntToScalar(20), SkIntToScalar(70), paint);
+    }
+
+    virtual SkISize onISize() { return SkISize::Make(500, 150); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            this->make_bitmap();
+            fInitialized = true;
+        }
+        canvas->clear(0x00000000);
+        {
+            SkRect srcRect = SkRect::MakeXYWH(20, 20, 30, 30);
+            SkRect dstRect = SkRect::MakeXYWH(0, 10, 60, 60);
+            SkRect clipRect = SkRect::MakeXYWH(0, 0, 100, 100);
+            SkRect bounds;
+            fBitmap.getBounds(&bounds);
+            SkAutoTUnref<SkImageFilter> bitmapSource(new SkBitmapSource(fBitmap));
+            SkAutoTUnref<SkImageFilter> bitmapSourceSrcRect(new SkBitmapSource(fBitmap, srcRect, srcRect));
+            SkAutoTUnref<SkImageFilter> bitmapSourceSrcRectDstRect(new SkBitmapSource(fBitmap, srcRect, dstRect));
+            SkAutoTUnref<SkImageFilter> bitmapSourceDstRectOnly(new SkBitmapSource(fBitmap, bounds, dstRect));
+
+            SkPaint paint;
+            paint.setImageFilter(bitmapSource);
+            canvas->save();
+            canvas->clipRect(clipRect);
+            canvas->drawPaint(paint);
+            canvas->restore();
+            canvas->translate(SkIntToScalar(100), 0);
+
+            paint.setImageFilter(bitmapSourceSrcRect);
+            canvas->save();
+            canvas->clipRect(clipRect);
+            canvas->drawPaint(paint);
+            canvas->restore();
+            canvas->translate(SkIntToScalar(100), 0);
+
+            paint.setImageFilter(bitmapSourceSrcRectDstRect);
+            canvas->save();
+            canvas->clipRect(clipRect);
+            canvas->drawPaint(paint);
+            canvas->restore();
+            canvas->translate(SkIntToScalar(100), 0);
+
+            paint.setImageFilter(bitmapSourceDstRectOnly);
+            canvas->save();
+            canvas->clipRect(clipRect);
+            canvas->drawPaint(paint);
+            canvas->restore();
+            canvas->translate(SkIntToScalar(100), 0);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+    SkBitmap fBitmap;
+    bool fInitialized;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new BitmapSourceGM; }
+static skiagm::GMRegistry reg(MyFactory);
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index f8f38e9..4dd03ce 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -20,6 +20,7 @@
     '../gm/bitmaprect.cpp',
     '../gm/bitmaprecttest.cpp',
     '../gm/bitmapscroll.cpp',
+    '../gm/bitmapsource.cpp',
     '../gm/bleed.cpp',
     '../gm/blurs.cpp',
     '../gm/blurquickreject.cpp',
diff --git a/include/effects/SkBitmapSource.h b/include/effects/SkBitmapSource.h
index 138987e..699186e 100644
--- a/include/effects/SkBitmapSource.h
+++ b/include/effects/SkBitmapSource.h
@@ -14,6 +14,7 @@
 class SK_API SkBitmapSource : public SkImageFilter {
 public:
     explicit SkBitmapSource(const SkBitmap& bitmap);
+    SkBitmapSource(const SkBitmap& bitmap, const SkRect& srcRect, const SkRect& dstRect);
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBitmapSource)
 
@@ -25,6 +26,7 @@
 
 private:
     SkBitmap fBitmap;
+    SkRect   fSrcRect, fDstRect;
     typedef SkImageFilter INHERITED;
 };
 
diff --git a/src/effects/SkBitmapSource.cpp b/src/effects/SkBitmapSource.cpp
index 72f51f8..5e19744 100644
--- a/src/effects/SkBitmapSource.cpp
+++ b/src/effects/SkBitmapSource.cpp
@@ -6,24 +6,71 @@
  */
 
 #include "SkBitmapSource.h"
+#include "SkDevice.h"
+#include "SkCanvas.h"
+#include "SkFlattenableBuffers.h"
+#include "SkValidationUtils.h"
 
 SkBitmapSource::SkBitmapSource(const SkBitmap& bitmap)
   : INHERITED(0, 0),
-    fBitmap(bitmap) {
+    fBitmap(bitmap),
+    fSrcRect(SkRect::MakeWH(SkIntToScalar(bitmap.width()),
+                            SkIntToScalar(bitmap.height()))),
+    fDstRect(fSrcRect) {
+}
+
+SkBitmapSource::SkBitmapSource(const SkBitmap& bitmap, const SkRect& srcRect, const SkRect& dstRect)
+  : INHERITED(0, 0),
+    fBitmap(bitmap),
+    fSrcRect(srcRect),
+    fDstRect(dstRect) {
 }
 
 SkBitmapSource::SkBitmapSource(SkFlattenableReadBuffer& buffer)
   : INHERITED(0, buffer) {
     fBitmap.unflatten(buffer);
+    buffer.readRect(&fSrcRect);
+    buffer.readRect(&fDstRect);
+    buffer.validate(SkIsValidRect(fSrcRect) && SkIsValidRect(fDstRect));
 }
 
 void SkBitmapSource::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
     fBitmap.flatten(buffer);
+    buffer.writeRect(fSrcRect);
+    buffer.writeRect(fDstRect);
 }
 
-bool SkBitmapSource::onFilterImage(Proxy*, const SkBitmap&, const SkMatrix&,
+bool SkBitmapSource::onFilterImage(Proxy* proxy, const SkBitmap&, const SkMatrix& matrix,
                                    SkBitmap* result, SkIPoint* offset) {
-    *result = fBitmap;
+    SkRect bounds, dstRect;
+    fBitmap.getBounds(&bounds);
+    matrix.mapRect(&dstRect, fDstRect);
+    if (fSrcRect == bounds && dstRect == bounds) {
+        *result = fBitmap;
+        return true;
+    }
+    SkIRect dstIRect;
+    dstRect.roundOut(&dstIRect);
+
+    SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(dstIRect.width(), dstIRect.height()));
+    if (NULL == device.get()) {
+        return false;
+    }
+
+    SkCanvas canvas(device.get());
+    SkPaint paint;
+
+    // Subtract off the integer component of the translation (will be applied in loc, below).
+    dstRect.offset(-SkIntToScalar(dstIRect.fLeft), -SkIntToScalar(dstIRect.fTop));
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    // FIXME: this probably shouldn't be necessary, but drawBitmapRectToRect asserts
+    // None filtering when it's translate-only
+    paint.setFilterLevel(fSrcRect.width() == dstRect.width() && fSrcRect.height() == dstRect.height() ? SkPaint::kNone_FilterLevel : SkPaint::kMedium_FilterLevel);
+    canvas.drawBitmapRectToRect(fBitmap, &fSrcRect, dstRect, &paint);
+
+    *result = device.get()->accessBitmap(false);
+    offset->fX += dstIRect.fLeft;
+    offset->fY += dstIRect.fTop;
     return true;
 }