Provides various implementations of Android's SkBitmapRegionDecoder.

Implements testing in DM for these implementations.

nanobench testing will follow after this.

TBR=scroggo
BUG=skia:

Committed: https://skia.googlesource.com/skia/+/76f755e6d54a32f9887ad254ce59a3a62f28bde4

Review URL: https://codereview.chromium.org/1288963002
diff --git a/tools/SkBitmapRegionCanvas.cpp b/tools/SkBitmapRegionCanvas.cpp
new file mode 100644
index 0000000..2344d90
--- /dev/null
+++ b/tools/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();
+}