Provides various implementations of Android's SkBitmapRegionDecoder.

Implements testing in DM for these implementations.

nanobench testing will follow after this.

BUG=skia:

Review URL: https://codereview.chromium.org/1288963002
diff --git a/dm/DM.cpp b/dm/DM.cpp
index becea6c..154f8aa 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -327,12 +327,116 @@
     }
 }
 
-static bool codec_supported(const char* ext) {
-    // FIXME: Once other versions of SkCodec are available, we can add them to this
-    // list (and eventually we can remove this check once they are all supported).
+static bool brd_color_type_supported(SkBitmapRegionDecoderInterface::Strategy strategy,
+        CodecSrc::DstColorType dstColorType) {
+    switch (strategy) {
+        case SkBitmapRegionDecoderInterface::kCanvas_Strategy:
+            if (CodecSrc::kGetFromCanvas_DstColorType == dstColorType) {
+                return true;
+            }
+            return false;
+        case SkBitmapRegionDecoderInterface::kOriginal_Strategy:
+            switch (dstColorType) {
+                case CodecSrc::kGetFromCanvas_DstColorType:
+                case CodecSrc::kIndex8_Always_DstColorType:
+                case CodecSrc::kGrayscale_Always_DstColorType:
+                    return true;
+                default:
+                    return false;
+            }
+        default:
+            SkASSERT(false);
+            return false;
+    }
+}
+
+static void push_brd_src(Path path, SkBitmapRegionDecoderInterface::Strategy strategy,
+        CodecSrc::DstColorType dstColorType, BRDSrc::Mode mode, uint32_t sampleSize) {
+    SkString folder;
+    switch (strategy) {
+        case SkBitmapRegionDecoderInterface::kCanvas_Strategy:
+            folder.append("brd_canvas");
+            break;
+        case SkBitmapRegionDecoderInterface::kOriginal_Strategy:
+            folder.append("brd_sample");
+            break;
+        default:
+            SkASSERT(false);
+            return;
+    }
+
+    switch (mode) {
+        case BRDSrc::kFullImage_Mode:
+            break;
+        case BRDSrc::kDivisor_Mode:
+            folder.append("_divisor");
+            break;
+        default:
+            SkASSERT(false);
+            return;
+    }
+
+    switch (dstColorType) {
+        case CodecSrc::kGetFromCanvas_DstColorType:
+            break;
+        case CodecSrc::kIndex8_Always_DstColorType:
+            folder.append("_kIndex");
+            break;
+        case CodecSrc::kGrayscale_Always_DstColorType:
+            folder.append("_kGray");
+            break;
+        default:
+            SkASSERT(false);
+            return;
+    }
+
+    if (1 != sampleSize) {
+        folder.appendf("_%.3f", BRDSrc::GetScale(sampleSize));
+    }
+
+    BRDSrc* src = new BRDSrc(path, strategy, mode, dstColorType, sampleSize);
+    push_src("image", folder, src);
+}
+
+static void push_brd_srcs(Path path) {
+
+    const SkBitmapRegionDecoderInterface::Strategy strategies[] = {
+            SkBitmapRegionDecoderInterface::kCanvas_Strategy,
+            SkBitmapRegionDecoderInterface::kOriginal_Strategy
+    };
+
+    // We will only test to one backend (8888), but we will test all of the
+    // color types that we need to decode to on this backend.
+    const CodecSrc::DstColorType dstColorTypes[] = {
+        CodecSrc::kGetFromCanvas_DstColorType,
+        CodecSrc::kIndex8_Always_DstColorType,
+        CodecSrc::kGrayscale_Always_DstColorType,
+    };
+
+    const BRDSrc::Mode modes[] = {
+        BRDSrc::kFullImage_Mode,
+        BRDSrc::kDivisor_Mode
+    };
+
+    const uint32_t sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+    for (SkBitmapRegionDecoderInterface::Strategy strategy : strategies) {
+        for (CodecSrc::DstColorType dstColorType : dstColorTypes) {
+            if (brd_color_type_supported(strategy, dstColorType)) {
+                for (BRDSrc::Mode mode : modes) {
+                    for (uint32_t sampleSize : sampleSizes) {
+                        push_brd_src(path, strategy, dstColorType, mode, sampleSize);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static bool brd_supported(const char* ext) {
     static const char* const exts[] = {
-        "bmp", "gif", "jpg", "jpeg", "png", "ico", "wbmp", "webp",
-        "BMP", "GIF", "JPG", "JPEG", "PNG", "ICO", "WBMP", "WEBP",
+        "jpg", "jpeg", "png", "webp",
+        "JPG", "JPEG", "PNG", "WEBP",
     };
 
     for (uint32_t i = 0; i < SK_ARRAY_COUNT(exts); i++) {
@@ -371,8 +475,9 @@
                     SkString path = SkOSPath::Join(flag, file.c_str());
                     push_src("image", "decode", new ImageSrc(path)); // Decode entire image
                     push_src("image", "subset", new ImageSrc(path, 2)); // Decode into 2x2 subsets
-                    if (codec_supported(exts[j])) {
-                        push_codec_srcs(path);
+                    push_codec_srcs(path);
+                    if (brd_supported(exts[j])) {
+                        push_brd_srcs(path);
                     }
                 }
             }
@@ -381,6 +486,7 @@
             push_src("image", "decode", new ImageSrc(flag)); // Decode entire image.
             push_src("image", "subset", new ImageSrc(flag, 2)); // Decode into 2 x 2 subsets
             push_codec_srcs(flag);
+            push_brd_srcs(flag);
         }
     }
 }
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 2c80fac..e31a249 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -64,6 +64,163 @@
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
+BRDSrc::BRDSrc(Path path, SkBitmapRegionDecoderInterface::Strategy strategy, Mode mode,
+        CodecSrc::DstColorType dstColorType, uint32_t sampleSize)
+    : fPath(path)
+    , fStrategy(strategy)
+    , fMode(mode)
+    , fDstColorType(dstColorType)
+    , fSampleSize(sampleSize)
+{}
+
+bool BRDSrc::veto(SinkFlags flags) const {
+    // No need to test to non-raster or indirect backends.
+    return flags.type != SinkFlags::kRaster
+        || flags.approach != SinkFlags::kDirect;
+}
+
+static SkBitmapRegionDecoderInterface* create_brd(Path path,
+        SkBitmapRegionDecoderInterface::Strategy strategy) {
+    SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
+    if (!encoded) {
+        return NULL;
+    }
+    return SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(new SkMemoryStream(encoded),
+            strategy);
+}
+
+Error BRDSrc::draw(SkCanvas* canvas) const {
+    SkColorType colorType = canvas->imageInfo().colorType();
+    if (kRGB_565_SkColorType == colorType &&
+            CodecSrc::kGetFromCanvas_DstColorType != fDstColorType) {
+        return Error::Nonfatal("Testing non-565 to 565 is uninteresting.");
+    }
+    switch (fDstColorType) {
+        case CodecSrc::kGetFromCanvas_DstColorType:
+            break;
+        case CodecSrc::kIndex8_Always_DstColorType:
+            colorType = kIndex_8_SkColorType;
+            break;
+        case CodecSrc::kGrayscale_Always_DstColorType:
+            colorType = kGray_8_SkColorType;
+            break;
+    }
+
+    SkAutoTDelete<SkBitmapRegionDecoderInterface> brd(create_brd(fPath, fStrategy));
+    if (nullptr == brd.get()) {
+        return Error::Nonfatal(SkStringPrintf("Could not create brd for %s.", fPath.c_str()));
+    }
+
+    const uint32_t width = brd->width();
+    const uint32_t height = brd->height();
+    // Visually inspecting very small output images is not necessary.
+    if ((width / fSampleSize <= 10 || height / fSampleSize <= 10) && 1 != fSampleSize) {
+        return Error::Nonfatal("Scaling very small images is uninteresting.");
+    }
+    switch (fMode) {
+        case kFullImage_Mode: {
+            SkAutoTDelete<SkBitmap> bitmap(brd->decodeRegion(0, 0, width, height, fSampleSize,
+                    colorType));
+            if (nullptr == bitmap.get() || colorType != bitmap->colorType()) {
+                return Error::Nonfatal("Cannot convert to color type.\n");
+            }
+            canvas->drawBitmap(*bitmap, 0, 0);
+            return "";
+        }
+        case kDivisor_Mode: {
+            const uint32_t divisor = 2;
+            if (width < divisor || height < divisor) {
+                return Error::Nonfatal("Divisor is larger than image dimension.\n");
+            }
+
+            // Use a border to test subsets that extend outside the image.
+            // We will not allow the border to be larger than the image dimensions.  Allowing
+            // these large borders causes off by one errors that indicate a problem with the
+            // test suite, not a problem with the implementation.
+            const uint32_t maxBorder = SkTMin(width, height) / (fSampleSize * divisor);
+            const uint32_t scaledBorder = SkTMin(5u, maxBorder);
+            const uint32_t unscaledBorder = scaledBorder * fSampleSize;
+
+            // We may need to clear the canvas to avoid uninitialized memory.
+            // Assume we are scaling a 780x780 image with sampleSize = 8.
+            // The output image should be 97x97.
+            // Each subset will be 390x390.
+            // Each scaled subset be 48x48.
+            // Four scaled subsets will only fill a 96x96 image.
+            // The bottom row and last column will not be touched.
+            // This is an unfortunate result of our rounding rules when scaling.
+            // Maybe we need to consider testing scaled subsets without trying to
+            // combine them to match the full scaled image?  Or maybe this is the
+            // best we can do?
+            canvas->clear(0);
+
+            for (uint32_t x = 0; x < divisor; x++) {
+                for (uint32_t y = 0; y < divisor; y++) {
+                    // Calculate the subset dimensions
+                    uint32_t subsetWidth = width / divisor;
+                    uint32_t subsetHeight = height / divisor;
+                    const int left = x * subsetWidth;
+                    const int top = y * subsetHeight;
+
+                    // Increase the size of the last subset in each row or column, when the
+                    // divisor does not divide evenly into the image dimensions
+                    subsetWidth += (x + 1 == divisor) ? (width % divisor) : 0;
+                    subsetHeight += (y + 1 == divisor) ? (height % divisor) : 0;
+
+                    // Increase the size of the subset in order to have a border on each side
+                    const int decodeLeft = left - unscaledBorder;
+                    const int decodeTop = top - unscaledBorder;
+                    const uint32_t decodeWidth = subsetWidth + unscaledBorder * 2;
+                    const uint32_t decodeHeight = subsetHeight + unscaledBorder * 2;
+                    SkAutoTDelete<SkBitmap> bitmap(brd->decodeRegion(decodeLeft,
+                            decodeTop, decodeWidth, decodeHeight, fSampleSize, colorType));
+                    if (nullptr == bitmap.get() || colorType != bitmap->colorType()) {
+                        return Error::Nonfatal("Cannot convert to color type.\n");
+                    }
+
+                    canvas->drawBitmapRect(*bitmap,
+                            SkRect::MakeXYWH((SkScalar) scaledBorder, (SkScalar) scaledBorder,
+                                    (SkScalar) (subsetWidth / fSampleSize),
+                                    (SkScalar) (subsetHeight / fSampleSize)),
+                            SkRect::MakeXYWH((SkScalar) (left / fSampleSize),
+                                    (SkScalar) (top / fSampleSize),
+                                    (SkScalar) (subsetWidth / fSampleSize),
+                                    (SkScalar) (subsetHeight / fSampleSize)),
+                            nullptr);
+                }
+            }
+            return "";
+        }
+        default:
+            SkASSERT(false);
+            return "Error: Should not be reached.\n";
+    }
+}
+
+SkISize BRDSrc::size() const {
+    SkAutoTDelete<SkBitmapRegionDecoderInterface> brd(create_brd(fPath, fStrategy));
+    if (brd) {
+        return SkISize::Make(SkTMax(1, brd->width() / (int) fSampleSize),
+                SkTMax(1, brd->height() / (int) fSampleSize));
+    }
+    return SkISize::Make(0, 0);
+}
+
+static SkString get_scaled_name(const Path& path, float scale) {
+    return SkStringPrintf("%s_%.3f", SkOSPath::Basename(path.c_str()).c_str(), scale);
+}
+
+Name BRDSrc::name() const {
+    // We will replicate the names used by CodecSrc so that images can
+    // be compared in Gold.
+    if (1 == fSampleSize) {
+        return SkOSPath::Basename(fPath.c_str());
+    }
+    return get_scaled_name(fPath, BRDSrc::GetScale(fSampleSize));
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
 CodecSrc::CodecSrc(Path path, Mode mode, DstColorType dstColorType, float scale)
     : fPath(path)
     , fMode(mode)
@@ -493,9 +650,8 @@
 Name CodecSrc::name() const {
     if (1.0f == fScale) {
         return SkOSPath::Basename(fPath.c_str());
-    } else {
-        return SkStringPrintf("%s_%.3f", SkOSPath::Basename(fPath.c_str()).c_str(), fScale);
     }
+    return get_scaled_name(fPath, fScale);
 }
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h
index efae871..fe6c91d 100644
--- a/dm/DMSrcSink.h
+++ b/dm/DMSrcSink.h
@@ -12,6 +12,7 @@
 #include "SkBBHFactory.h"
 #include "SkBBoxHierarchy.h"
 #include "SkBitmap.h"
+#include "SkBitmapRegionDecoderInterface.h"
 #include "SkCanvas.h"
 #include "SkData.h"
 #include "SkGPipe.h"
@@ -128,6 +129,34 @@
     float                   fScale;
 };
 
+// Allows for testing of various implementations of Android's BitmapRegionDecoder
+class BRDSrc : public Src {
+public:
+    enum Mode {
+        // Decode the entire image as one region.
+        kFullImage_Mode,
+        // Splits the image into multiple regions using a divisor and decodes the regions
+        // separately.  Also, this test adds a border of a few pixels to each of the regions
+        // that it is decoding.  This tests the behavior when a client asks for a region that
+        // does not fully fit in the image.
+        kDivisor_Mode,
+    };
+
+    BRDSrc(Path, SkBitmapRegionDecoderInterface::Strategy, Mode, CodecSrc::DstColorType, uint32_t);
+
+    static float GetScale(uint32_t sampleSize) { return 1.0f / (float) sampleSize; }
+
+    Error draw(SkCanvas*) const override;
+    SkISize size() const override;
+    Name name() const override;
+    bool veto(SinkFlags) const override;
+private:
+    Path                                     fPath;
+    SkBitmapRegionDecoderInterface::Strategy fStrategy;
+    Mode                                     fMode;
+    CodecSrc::DstColorType                   fDstColorType;
+    uint32_t                                 fSampleSize;
+};
 
 class ImageSrc : public Src {
 public:
diff --git a/gyp/utils.gyp b/gyp/utils.gyp
index 9881aa1..2c8c0a2 100644
--- a/gyp/utils.gyp
+++ b/gyp/utils.gyp
@@ -18,6 +18,7 @@
         'utils.gypi',
       ],
       'include_dirs': [
+        '../include/codec',
         '../include/effects',
         '../include/gpu',
         '../include/images',
diff --git a/gyp/utils.gypi b/gyp/utils.gypi
index e2abc03..5f91f8a 100644
--- a/gyp/utils.gypi
+++ b/gyp/utils.gypi
@@ -39,6 +39,9 @@
 
         '<(skia_src_path)/utils/SkBase64.cpp',
         '<(skia_src_path)/utils/SkBase64.h',
+        '<(skia_src_path)/utils/SkBitmapRegionCanvas.cpp',
+        '<(skia_src_path)/utils/SkBitmapRegionDecoderInterface.cpp',
+        '<(skia_src_path)/utils/SkBitmapRegionSampler.cpp',
         '<(skia_src_path)/utils/SkBitmapHasher.cpp',
         '<(skia_src_path)/utils/SkBitmapHasher.h',
         '<(skia_src_path)/utils/SkBitSet.cpp',
diff --git a/src/utils/SkBitmapRegionCanvas.cpp b/src/utils/SkBitmapRegionCanvas.cpp
new file mode 100644
index 0000000..2344d90
--- /dev/null
+++ b/src/utils/SkBitmapRegionCanvas.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmapRegionCanvas.h"
+#include "SkCanvas.h"
+#include "SkScanlineDecoder.h"
+
+SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkScanlineDecoder* decoder)
+    : INHERITED(decoder->getInfo().width(), decoder->getInfo().height())
+    , fDecoder(decoder)
+{}
+
+/*
+ * Chooses the correct image subset offsets and dimensions for the partial decode.
+ */
+static inline void set_subset_region(int inputOffset, int inputDimension,
+        int imageOriginalDimension, int* imageSubsetOffset, int* outOffset,
+        int* imageSubsetDimension) {
+
+    // This must be at least zero, we can't start decoding the image at a negative coordinate.
+    *imageSubsetOffset = SkTMax(0, inputOffset);
+
+    // If inputOffset is less than zero, we decode to an offset location in the output bitmap.
+    *outOffset = *imageSubsetOffset - inputOffset;
+
+    // Use imageSusetOffset to make sure we don't decode pixels past the edge of the image.
+    // Use outOffset to make sure we don't decode pixels past the edge of the region.
+    *imageSubsetDimension = SkTMin(imageOriginalDimension - *imageSubsetOffset,
+            inputDimension - *outOffset);
+}
+
+/*
+ * Returns a scaled dimension based on the original dimension and the sample size.
+ * TODO: Share this implementation with SkScaledCodec.
+ */
+static int get_scaled_dimension(int srcDimension, int sampleSize) {
+    if (sampleSize > srcDimension) {
+        return 1;
+    }
+    return srcDimension / sampleSize;
+}
+
+/*
+ * Three differences from the Android version:
+ *     Returns a Skia bitmap instead of an Android bitmap.
+ *     Android version attempts to reuse a recycled bitmap.
+ *     Removed the options object and used parameters for color type and
+ *     sample size.
+ */
+SkBitmap* SkBitmapRegionCanvas::decodeRegion(int inputX, int inputY,
+                                             int inputWidth, int inputHeight,
+                                             int sampleSize,
+                                             SkColorType dstColorType) {
+    // Reject color types not supported by this method
+    if (kIndex_8_SkColorType == dstColorType || kGray_8_SkColorType == dstColorType) {
+        SkDebugf("Error: Color type not supported.\n");
+        return nullptr;
+    }
+
+    // The client may not necessarily request a region that is fully within
+    // the image.  We may need to do some calculation to determine what part
+    // of the image to decode.
+
+    // The left offset of the portion of the image we want, where zero
+    // indicates the left edge of the image.
+    int imageSubsetX;
+
+    // The size of the output bitmap is determined by the size of the
+    // requested region, not by the size of the intersection of the region
+    // and the image dimensions.  If inputX is negative, we will need to
+    // place decoded pixels into the output bitmap starting at a left offset.
+    // If this is non-zero, imageSubsetX must be zero.
+    int outX;
+
+    // The width of the portion of the image that we will write to the output
+    // bitmap.  If the region is not fully contained within the image, this
+    // will not be the same as inputWidth.
+    int imageSubsetWidth;
+    set_subset_region(inputX, inputWidth, this->width(), &imageSubsetX, &outX, &imageSubsetWidth);
+
+    // The top offset of the portion of the image we want, where zero
+    // indicates the top edge of the image.
+    int imageSubsetY;
+
+    // The size of the output bitmap is determined by the size of the
+    // requested region, not by the size of the intersection of the region
+    // and the image dimensions.  If inputY is negative, we will need to
+    // place decoded pixels into the output bitmap starting at a top offset.
+    // If this is non-zero, imageSubsetY must be zero.
+    int outY;
+
+    // The height of the portion of the image that we will write to the output
+    // bitmap.  If the region is not fully contained within the image, this
+    // will not be the same as inputHeight.
+    int imageSubsetHeight;
+    set_subset_region(inputY, inputHeight, this->height(), &imageSubsetY, &outY,
+            &imageSubsetHeight);
+
+    if (imageSubsetWidth <= 0 || imageSubsetHeight <= 0) {
+        SkDebugf("Error: Region must intersect part of the image.\n");
+        return nullptr;
+    }
+
+    // Create the image info for the decode
+    SkAlphaType dstAlphaType = fDecoder->getInfo().alphaType();
+    if (kUnpremul_SkAlphaType == dstAlphaType) {
+        dstAlphaType = kPremul_SkAlphaType;
+    }
+    SkImageInfo decodeInfo = SkImageInfo::Make(this->width(), this->height(),
+            dstColorType, dstAlphaType);
+    
+    // Start the scanline decoder
+    SkCodec::Result r = fDecoder->start(decodeInfo);
+    if (SkCodec::kSuccess != r) {
+        SkDebugf("Error: Could not start scanline decoder.\n");
+        return nullptr;
+    }
+
+    // Allocate a bitmap for the unscaled decode
+    SkBitmap tmp;
+    SkImageInfo tmpInfo = decodeInfo.makeWH(this->width(), imageSubsetHeight);
+    if (!tmp.tryAllocPixels(tmpInfo)) {
+        SkDebugf("Error: Could not allocate pixels.\n");
+        return nullptr;
+    }
+
+    // Skip the unneeded rows
+    if (SkCodec::kSuccess != fDecoder->skipScanlines(imageSubsetY)) {
+        SkDebugf("Error: Failed to skip scanlines.\n");
+        return nullptr;
+    }
+
+    // Decode the necessary rows
+    SkCodec::Result result = fDecoder->getScanlines(tmp.getAddr(0, 0), imageSubsetHeight,
+            tmp.rowBytes());
+    switch (result) {
+        case SkCodec::kSuccess:
+        case SkCodec::kIncompleteInput:
+            break;
+        default:
+            SkDebugf("Error: Failed to get scanlines.\n");
+            return nullptr;
+    }
+
+    // Calculate the size of the output
+    const int outWidth = get_scaled_dimension(inputWidth, sampleSize);
+    const int outHeight = get_scaled_dimension(inputHeight, sampleSize);
+
+    // Initialize the destination bitmap
+    SkAutoTDelete<SkBitmap> bitmap(new SkBitmap());
+    SkImageInfo dstInfo = decodeInfo.makeWH(outWidth, outHeight);
+    if (!bitmap->tryAllocPixels(dstInfo)) {
+        SkDebugf("Error: Could not allocate pixels.\n");
+        return nullptr;
+    }
+
+    // Zero the bitmap if the region is not completely within the image.
+    // TODO (msarett): Can we make this faster by implementing it to only
+    //                 zero parts of the image that we won't overwrite with
+    //                 pixels?
+    // TODO (msarett): This could be skipped if memory is zero initialized.
+    //                 This would matter if this code is moved to Android and
+    //                 uses Android bitmaps.
+    if (0 != outX || 0 != outY ||
+            inputX + inputWidth > this->width() ||
+            inputY + inputHeight > this->height()) {
+        bitmap->eraseColor(0);
+    }
+
+    // Use a canvas to crop and scale to the destination bitmap
+    SkCanvas canvas(*bitmap);
+    // TODO (msarett): Maybe we can take advantage of the fact that SkRect uses floats?
+    SkRect src = SkRect::MakeXYWH((SkScalar) imageSubsetX, (SkScalar) 0,
+            (SkScalar) imageSubsetWidth, (SkScalar) imageSubsetHeight);
+    SkRect dst = SkRect::MakeXYWH((SkScalar) (outX / sampleSize), (SkScalar) (outY / sampleSize),
+            (SkScalar) get_scaled_dimension(imageSubsetWidth, sampleSize),
+            (SkScalar) get_scaled_dimension(imageSubsetHeight, sampleSize));
+    SkPaint paint;
+    // Overwrite the dst with the src pixels
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    // TODO (msarett): Test multiple filter qualities.  kNone is the default.
+    canvas.drawBitmapRect(tmp, src, dst, &paint);
+
+    return bitmap.detach();
+}
diff --git a/src/utils/SkBitmapRegionCanvas.h b/src/utils/SkBitmapRegionCanvas.h
new file mode 100644
index 0000000..96631d7
--- /dev/null
+++ b/src/utils/SkBitmapRegionCanvas.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkBitmapRegionDecoderInterface.h"
+#include "SkScanlineDecoder.h"
+
+/*
+ * This class implements SkBitmapRegionDecoder using an SkScanlineDecoder and
+ * an SkCanvas.  It uses the scanline decoder to subset the height.  It then
+ * will subset the width and scale by drawing to an SkCanvas.
+ */
+// FIXME (msarett): This implementation does not support WEBP, because WEBP
+// does not have a scanline decoder.
+class SkBitmapRegionCanvas : public SkBitmapRegionDecoderInterface {
+public:
+
+    /*
+     * Takes ownership of pointer to decoder
+     */
+    SkBitmapRegionCanvas(SkScanlineDecoder* decoder);
+
+    /*
+     * Three differences from the Android version:
+     *     Returns a Skia bitmap instead of an Android bitmap.
+     *     Android version attempts to reuse a recycled bitmap.
+     *     Removed the options object and used parameters for color type and
+     *     sample size.
+     */
+    SkBitmap* decodeRegion(int start_x, int start_y, int width, int height,
+                           int sampleSize, SkColorType prefColorType) override;
+
+private:
+
+    SkAutoTDelete<SkScanlineDecoder> fDecoder;
+
+    typedef SkBitmapRegionDecoderInterface INHERITED;
+
+};
diff --git a/src/utils/SkBitmapRegionDecoderInterface.cpp b/src/utils/SkBitmapRegionDecoderInterface.cpp
new file mode 100644
index 0000000..090f042
--- /dev/null
+++ b/src/utils/SkBitmapRegionDecoderInterface.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmapRegionCanvas.h"
+#include "SkBitmapRegionDecoderInterface.h"
+#include "SkBitmapRegionSampler.h"
+#include "SkScanlineDecoder.h"
+#include "SkImageDecoder.h"
+
+SkBitmapRegionDecoderInterface* SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(
+        SkStreamRewindable* stream, Strategy strategy) {
+    switch (strategy) {
+        case kOriginal_Strategy: {
+            SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
+            int width, height;
+            if (nullptr == decoder) {
+                SkDebugf("Error: Could not create image decoder.\n");
+                return nullptr;
+            }
+            if (!decoder->buildTileIndex(stream, &width, &height)) {
+                SkDebugf("Error: Could not build tile index.\n");
+                delete decoder;
+                return nullptr;
+            }
+            return new SkBitmapRegionSampler(decoder, width, height);
+        }
+        case kCanvas_Strategy: {
+            SkScanlineDecoder* decoder = SkScanlineDecoder::NewFromStream(stream);
+            if (nullptr == decoder) {
+                SkDebugf("Error: Failed to create decoder.\n");
+                return nullptr;
+            }
+            switch (decoder->getScanlineOrder()) {
+                case SkScanlineDecoder::kTopDown_SkScanlineOrder:
+                case SkScanlineDecoder::kNone_SkScanlineOrder:
+                    break;
+                default:
+                    SkDebugf("Error: Scanline ordering not supported.\n");
+                    return nullptr;
+            }
+            return new SkBitmapRegionCanvas(decoder);
+        }
+        default:
+            SkASSERT(false);
+            return nullptr;
+    }
+}
diff --git a/src/utils/SkBitmapRegionDecoderInterface.h b/src/utils/SkBitmapRegionDecoderInterface.h
new file mode 100644
index 0000000..bc28c2b
--- /dev/null
+++ b/src/utils/SkBitmapRegionDecoderInterface.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkBitmapRegionDecoder_DEFINED
+#define SkBitmapRegionDecoder_DEFINED
+
+#include "SkBitmap.h"
+#include "SkStream.h"
+
+/*
+ * This class aims to provide an interface to test multiple implementations of
+ * SkBitmapRegionDecoder.
+ */
+class SkBitmapRegionDecoderInterface {
+public:
+
+    enum Strategy {
+        kCanvas_Strategy,   // Draw to the canvas, uses SkCodec
+        kOriginal_Strategy, // Sampling, uses SkImageDecoder
+        // TODO (msarett): Add strategy for SkScaledCodec
+    };
+
+    /*
+     * @param stream   Encoded image stream, takes ownership
+     * @param strategy Strategy used for scaling and subsetting
+     * @return         Tries to create an SkBitmapRegionDecoder, returns NULL
+     *                 on failure
+     */
+    static SkBitmapRegionDecoderInterface* CreateBitmapRegionDecoder(
+            SkStreamRewindable* stream, Strategy strategy);
+
+    /*
+     * Decode a scaled region of the encoded image stream
+     *
+     * @param start_x    X-coordinate of upper-left corner of region.
+     *                   This coordinate is unscaled, relative to the original dimensions.
+     * @param start_y    Y-coordinate of upper-left corner of region.
+     *                   This coordinate is unscaled, relative to the original dimensions.
+     * @param width      Width of the region to decode.
+     *                   This distance is unscaled, relative to the original dimensions.
+     * @param height     Height of the region to decode.
+     *                   This distance is unscaled, relative to the original dimensions.
+     * @param sampleSize An integer downscaling factor for the decode.
+     * @param colorType  Preferred output colorType.
+     *                   New implementations should return NULL if they do not support
+     *                   decoding to this color type.
+     *                   The old kOriginal_Strategy will decode to a default color type
+     *                   if this color type is unsupported.
+     * @return           Pointer to a bitmap of the decoded region on success, NULL on
+     *                   failure.
+     */
+    virtual SkBitmap* decodeRegion(int start_x, int start_y, int width,
+                                   int height, int sampleSize,
+                                   SkColorType colorType) = 0;
+
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+
+    virtual ~SkBitmapRegionDecoderInterface() {}
+
+protected:
+
+    SkBitmapRegionDecoderInterface(int width, int height)
+        : fWidth(width)
+        , fHeight(height)
+    {}
+
+private:
+    const int fWidth;
+    const int fHeight;
+};
+
+#endif
diff --git a/src/utils/SkBitmapRegionSampler.cpp b/src/utils/SkBitmapRegionSampler.cpp
new file mode 100644
index 0000000..98c183d
--- /dev/null
+++ b/src/utils/SkBitmapRegionSampler.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmapRegionSampler.h"
+
+SkBitmapRegionSampler::SkBitmapRegionSampler(SkImageDecoder* decoder, int width, 
+                                             int height)
+    : INHERITED(width, height)
+    , fDecoder(decoder)
+{}
+
+/*
+ * Three differences from the Android version:
+ *     Returns a Skia bitmap instead of an Android bitmap.
+ *     Android version attempts to reuse a recycled bitmap.
+ *     Removed the options object and used parameters for color type and
+ *     sample size.
+ */
+SkBitmap* SkBitmapRegionSampler::decodeRegion(int start_x, int start_y,
+                                              int width, int height,
+                                              int sampleSize,
+                                              SkColorType prefColorType) {
+    // Match Android's default settings
+    fDecoder->setDitherImage(true);
+    fDecoder->setPreferQualityOverSpeed(false);
+    fDecoder->setRequireUnpremultipliedColors(false);
+    fDecoder->setSampleSize(sampleSize);
+
+    // kAlpha8 is the legacy representation of kGray8 used by SkImageDecoder
+    if (kGray_8_SkColorType == prefColorType) {
+        prefColorType = kAlpha_8_SkColorType;
+    }
+
+    SkIRect region;
+    region.fLeft = start_x;
+    region.fTop = start_y;
+    region.fRight = start_x + width;
+    region.fBottom = start_y + height;
+
+    SkAutoTDelete<SkBitmap> bitmap(new SkBitmap());
+    if (!fDecoder->decodeSubset(bitmap.get(), region, prefColorType)) {
+        SkDebugf("Error: decodeRegion failed.\n");
+        return nullptr;
+    }
+    return bitmap.detach();
+}
diff --git a/src/utils/SkBitmapRegionSampler.h b/src/utils/SkBitmapRegionSampler.h
new file mode 100644
index 0000000..d2f738d
--- /dev/null
+++ b/src/utils/SkBitmapRegionSampler.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkBitmapRegionDecoderInterface.h"
+#include "SkImageDecoder.h"
+#include "SkTemplates.h"
+
+/*
+ * This class aims to duplicate the current implementation of
+ * SkBitmapRegionDecoder in Android.
+ */
+class SkBitmapRegionSampler : public SkBitmapRegionDecoderInterface {
+public:
+
+    /*
+     * Takes ownership of pointer to decoder
+     */
+    SkBitmapRegionSampler(SkImageDecoder* decoder, int width, int height);
+
+    /*
+     * Three differences from the Android version:
+     *     Returns a Skia bitmap instead of an Android bitmap.
+     *     Android version attempts to reuse a recycled bitmap.
+     *     Removed the options object and used parameters for color type and
+     *     sample size.
+     */
+    SkBitmap* decodeRegion(int start_x, int start_y, int width, int height,
+                           int sampleSize, SkColorType prefColorType) override;
+
+private:
+
+    SkAutoTDelete<SkImageDecoder> fDecoder;
+
+    typedef SkBitmapRegionDecoderInterface INHERITED;
+
+};