Implementation of SkBitmapRegionDecoder using SkAndroidCodec
authormsarett <msarett@google.com>
Thu, 22 Oct 2015 14:29:19 +0000 (07:29 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 22 Oct 2015 14:29:19 +0000 (07:29 -0700)
Includes testing in DM and nanobench

BUG=skia:

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

bench/BitmapRegionDecoderBench.cpp
bench/nanobench.cpp
dm/DM.cpp
dm/DMSrcSink.cpp
gyp/tools.gyp
tools/SkBitmapRegionCanvas.cpp
tools/SkBitmapRegionCodec.cpp [new file with mode: 0644]
tools/SkBitmapRegionCodec.h [new file with mode: 0644]
tools/SkBitmapRegionDecoderInterface.cpp
tools/SkBitmapRegionDecoderInterface.h
tools/SkCodecTools.h

index 0398b58..0926936 100644 (file)
@@ -30,6 +30,9 @@ BitmapRegionDecoderBench::BitmapRegionDecoderBench(const char* baseName, SkData*
         case SkBitmapRegionDecoderInterface::kCanvas_Strategy:
             strategyName = "Canvas";
             break;
+        case SkBitmapRegionDecoderInterface::kAndroidCodec_Strategy:
+            strategyName = "AndroidCodec";
+            break;
         default:
             SkASSERT(false);
             strategyName = "";
@@ -54,8 +57,7 @@ bool BitmapRegionDecoderBench::isSuitableFor(Backend backend) {
 }
 
 void BitmapRegionDecoderBench::onDelayedSetup() {
-    SkStreamRewindable* stream = new SkMemoryStream(fData);
-    fBRD.reset(SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(stream, fStrategy));
+    fBRD.reset(SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(fData, fStrategy));
 }
 
 void BitmapRegionDecoderBench::onDraw(int n, SkCanvas* canvas) {
index 1652354..054fbb2 100644 (file)
@@ -596,9 +596,8 @@ static bool valid_subset_bench(const SkString& path, SkColorType colorType, bool
 static bool valid_brd_bench(SkData* encoded, SkBitmapRegionDecoderInterface::Strategy strategy,
         SkColorType colorType, uint32_t sampleSize, uint32_t minOutputSize, int* width,
         int* height) {
-    SkStreamRewindable* stream = new SkMemoryStream(encoded);
     SkAutoTDelete<SkBitmapRegionDecoderInterface> brd(
-            SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(stream, strategy));
+            SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(encoded, strategy));
     if (nullptr == brd.get()) {
         // This is indicates that subset decoding is not supported for a particular image format.
         return false;
@@ -962,8 +961,9 @@ public:
             SkBitmapRegionDecoderInterface::Strategy    fStrategy;
             const char*                                 fName;
         } strategies[] = {
-            { SkBitmapRegionDecoderInterface::kOriginal_Strategy,   "BRD" },
-            { SkBitmapRegionDecoderInterface::kCanvas_Strategy,     "BRD_canvas" },
+            { SkBitmapRegionDecoderInterface::kOriginal_Strategy,    "BRD" },
+            { SkBitmapRegionDecoderInterface::kCanvas_Strategy,      "BRD_canvas" },
+            { SkBitmapRegionDecoderInterface::kAndroidCodec_Strategy, "BRD_android_codec" },
         };
 
         // We intend to create benchmarks that model the use cases in
index c51a6fb..f1f54d3 100644 (file)
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -392,6 +392,15 @@ static bool brd_color_type_supported(SkBitmapRegionDecoderInterface::Strategy st
                 default:
                     return false;
             }
+        case SkBitmapRegionDecoderInterface::kAndroidCodec_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;
@@ -408,6 +417,9 @@ static void push_brd_src(Path path, SkBitmapRegionDecoderInterface::Strategy str
         case SkBitmapRegionDecoderInterface::kOriginal_Strategy:
             folder.append("brd_sample");
             break;
+        case SkBitmapRegionDecoderInterface::kAndroidCodec_Strategy:
+            folder.append("brd_android_codec");
+            break;
         default:
             SkASSERT(false);
             return;
@@ -450,7 +462,8 @@ static void push_brd_srcs(Path path) {
 
     const SkBitmapRegionDecoderInterface::Strategy strategies[] = {
             SkBitmapRegionDecoderInterface::kCanvas_Strategy,
-            SkBitmapRegionDecoderInterface::kOriginal_Strategy
+            SkBitmapRegionDecoderInterface::kOriginal_Strategy,
+            SkBitmapRegionDecoderInterface::kAndroidCodec_Strategy,
     };
 
     const uint32_t sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
@@ -458,14 +471,14 @@ static void push_brd_srcs(Path path) {
     // 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,
+            CodecSrc::kGetFromCanvas_DstColorType,
+            CodecSrc::kIndex8_Always_DstColorType,
+            CodecSrc::kGrayscale_Always_DstColorType,
     };
 
     const BRDSrc::Mode modes[] = {
-        BRDSrc::kFullImage_Mode,
-        BRDSrc::kDivisor_Mode
+            BRDSrc::kFullImage_Mode,
+            BRDSrc::kDivisor_Mode,
     };
 
     for (SkBitmapRegionDecoderInterface::Strategy strategy : strategies) {
index 587949d..9a61b02 100644 (file)
@@ -88,8 +88,7 @@ static SkBitmapRegionDecoderInterface* create_brd(Path path,
     if (!encoded) {
         return NULL;
     }
-    return SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(new SkMemoryStream(encoded),
-            strategy);
+    return SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(encoded, strategy);
 }
 
 Error BRDSrc::draw(SkCanvas* canvas) const {
index e30cb7b..c25543e 100644 (file)
@@ -52,6 +52,7 @@
       'type': 'static_library',
       'sources': [
         '../tools/SkBitmapRegionCanvas.cpp',
+        '../tools/SkBitmapRegionCodec.cpp',
         '../tools/SkBitmapRegionDecoderInterface.cpp',
         '../tools/SkBitmapRegionSampler.cpp',
       ],
index 0892aa5..f9c9573 100644 (file)
@@ -16,30 +16,6 @@ SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkCodec* decoder)
 {}
 
 /*
- * Chooses the correct image subset offsets and dimensions for the partial decode.
- *
- * @return true if the subset is completely contained within the image
- *         false otherwise
- */
-static bool 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);
-
-    return (*outOffset == 0) && (*imageSubsetDimension == inputDimension);
-}
-
-/*
  * Three differences from the Android version:
  *     Returns a Skia bitmap instead of an Android bitmap.
  *     Android version attempts to reuse a recycled bitmap.
@@ -56,48 +32,25 @@ SkBitmap* SkBitmapRegionCanvas::decodeRegion(int inputX, int inputY,
         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;
+    // Fix the input sampleSize if necessary.
+    if (sampleSize < 1) {
+        sampleSize = 1;
+    }
 
     // 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.
+    // requested subset, not by the size of the intersection of the subset
+    // and the image dimensions.
+    // If inputX is negative, we will need to place decoded pixels into the
+    // output bitmap starting at a left offset.  Call this outX.
+    // If outX is non-zero, subsetX must be zero.
+    // If inputY is negative, we will need to place decoded pixels into the
+    // output bitmap starting at a top offset.  Call this outY.
+    // If outY is non-zero, subsetY 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;
-    bool imageContainsEntireSubset = 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;
-    imageContainsEntireSubset &= set_subset_region(inputY, inputHeight, this->height(),
-            &imageSubsetY, &outY, &imageSubsetHeight);
-
-    if (imageSubsetWidth <= 0 || imageSubsetHeight <= 0) {
-        SkCodecPrintf("Error: Region must intersect part of the image.\n");
+    SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight);
+    SubsetType type = adjust_subset_rect(fDecoder->getInfo().dimensions(), &subset, &outX, &outY);
+    if (SubsetType::kOutside_SubsetType == type) {
         return nullptr;
     }
 
@@ -108,7 +61,7 @@ SkBitmap* SkBitmapRegionCanvas::decodeRegion(int inputX, int inputY,
     }
     SkImageInfo decodeInfo = SkImageInfo::Make(this->width(), this->height(),
             dstColorType, dstAlphaType);
-    
+
     // Start the scanline decoder
     SkCodec::Result r = fDecoder->startScanlineDecode(decodeInfo);
     if (SkCodec::kSuccess != r) {
@@ -118,20 +71,20 @@ SkBitmap* SkBitmapRegionCanvas::decodeRegion(int inputX, int inputY,
 
     // Allocate a bitmap for the unscaled decode
     SkBitmap tmp;
-    SkImageInfo tmpInfo = decodeInfo.makeWH(this->width(), imageSubsetHeight);
+    SkImageInfo tmpInfo = decodeInfo.makeWH(this->width(), subset.height());
     if (!tmp.tryAllocPixels(tmpInfo)) {
         SkCodecPrintf("Error: Could not allocate pixels.\n");
         return nullptr;
     }
 
     // Skip the unneeded rows
-    if (!fDecoder->skipScanlines(imageSubsetY)) {
+    if (!fDecoder->skipScanlines(subset.y())) {
         SkCodecPrintf("Error: Failed to skip scanlines.\n");
         return nullptr;
     }
 
     // Decode the necessary rows
-    fDecoder->getScanlines(tmp.getAddr(0, 0), imageSubsetHeight, tmp.rowBytes());
+    fDecoder->getScanlines(tmp.getAddr(0, 0), subset.height(), tmp.rowBytes());
 
     // Calculate the size of the output
     const int outWidth = get_scaled_dimension(inputWidth, sampleSize);
@@ -152,18 +105,18 @@ SkBitmap* SkBitmapRegionCanvas::decodeRegion(int inputX, int inputY,
     // 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 (!imageContainsEntireSubset) {
+    if (SubsetType::kPartiallyInside_SubsetType == type) {
         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 src = SkRect::MakeXYWH((SkScalar) subset.x(), (SkScalar) 0,
+            (SkScalar) subset.width(), (SkScalar) subset.height());
     SkRect dst = SkRect::MakeXYWH((SkScalar) (outX / sampleSize), (SkScalar) (outY / sampleSize),
-            (SkScalar) get_scaled_dimension(imageSubsetWidth, sampleSize),
-            (SkScalar) get_scaled_dimension(imageSubsetHeight, sampleSize));
+            (SkScalar) get_scaled_dimension(subset.width(), sampleSize),
+            (SkScalar) get_scaled_dimension(subset.height(), sampleSize));
     SkPaint paint;
     // Overwrite the dst with the src pixels
     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
diff --git a/tools/SkBitmapRegionCodec.cpp b/tools/SkBitmapRegionCodec.cpp
new file mode 100644 (file)
index 0000000..3f2cd24
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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 "SkBitmapRegionCodec.h"
+#include "SkAndroidCodec.h"
+#include "SkCodecPriv.h"
+#include "SkCodecTools.h"
+
+SkBitmapRegionCodec::SkBitmapRegionCodec(SkAndroidCodec* codec)
+    : INHERITED(codec->getInfo().width(), codec->getInfo().height())
+    , fCodec(codec)
+{}
+
+/*
+ * 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.
+ */
+// FIXME: Should this function should take in SkIRect?
+SkBitmap* SkBitmapRegionCodec::decodeRegion(int inputX, int inputY, int inputWidth, int inputHeight,
+        int sampleSize, SkColorType dstColorType) {
+
+    // Fix the input sampleSize if necessary.
+    if (sampleSize < 1) {
+        sampleSize = 1;
+    }
+
+    // The size of the output bitmap is determined by the size of the
+    // requested subset, not by the size of the intersection of the subset
+    // and the image dimensions.
+    // If inputX is negative, we will need to place decoded pixels into the
+    // output bitmap starting at a left offset.  Call this outX.
+    // If outX is non-zero, subsetX must be zero.
+    // If inputY is negative, we will need to place decoded pixels into the
+    // output bitmap starting at a top offset.  Call this outY.
+    // If outY is non-zero, subsetY must be zero.
+    int outX;
+    int outY;
+    SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight);
+    SubsetType type = adjust_subset_rect(fCodec->getInfo().dimensions(), &subset, &outX, &outY);
+    if (SubsetType::kOutside_SubsetType == type) {
+        return nullptr;
+    }
+
+    // Ask the codec for a scaled subset
+    if (!fCodec->getSupportedSubset(&subset)) {
+        SkCodecPrintf("Error: Could not get subset.\n");
+        return nullptr;
+    }
+    SkISize scaledSize = fCodec->getSampledSubsetDimensions(sampleSize, subset);
+
+    // Create the image info for the decode
+    SkAlphaType dstAlphaType = fCodec->getInfo().alphaType();
+    if (kUnpremul_SkAlphaType == dstAlphaType) {
+        dstAlphaType = kPremul_SkAlphaType;
+    }
+    SkImageInfo decodeInfo = SkImageInfo::Make(scaledSize.width(), scaledSize.height(),
+            dstColorType, dstAlphaType);
+
+    // Construct a color table for the decode if necessary
+    SkAutoTUnref<SkColorTable> colorTable(nullptr);
+    SkPMColor* colorPtr = nullptr;
+    int* colorCountPtr = nullptr;
+    int maxColors = 256;
+    SkPMColor colors[256];
+    if (kIndex_8_SkColorType == dstColorType) {
+        // TODO (msarett): This performs a copy that is unnecessary since
+        //                 we have not yet initialized the color table.
+        //                 And then we need to use a const cast to get
+        //                 a pointer to the color table that we can
+        //                 modify during the decode.  We could alternatively
+        //                 perform the decode before creating the bitmap and
+        //                 the color table.  We still would need to copy the
+        //                 colors into the color table after the decode.
+        colorTable.reset(new SkColorTable(colors, maxColors));
+        colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
+        colorCountPtr = &maxColors;
+    }
+
+    // Initialize the destination bitmap
+    SkAutoTDelete<SkBitmap> bitmap(new SkBitmap());
+    int scaledOutX = 0;
+    int scaledOutY = 0;
+    int scaledOutWidth = scaledSize.width();
+    int scaledOutHeight = scaledSize.height();
+    if (SubsetType::kPartiallyInside_SubsetType == type) {
+        scaledOutX = outX / sampleSize;
+        scaledOutY = outY / sampleSize;
+        // We need to be safe here because getSupportedSubset() may have modified the subset.
+        const int extraX = SkTMax(0, inputWidth - outX - subset.width());
+        const int extraY = SkTMax(0, inputHeight - outY - subset.height());
+        const int scaledExtraX = extraX / sampleSize;
+        const int scaledExtraY = extraY / sampleSize;
+        scaledOutWidth += scaledOutX + scaledExtraX;
+        scaledOutHeight += scaledOutY + scaledExtraY;
+    }
+    SkImageInfo outInfo = decodeInfo.makeWH(scaledOutWidth, scaledOutHeight);
+    if (!bitmap->tryAllocPixels(outInfo, nullptr, colorTable.get())) {
+        SkCodecPrintf("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 (SubsetType::kPartiallyInside_SubsetType == type) {
+        void* pixels = bitmap->getPixels();
+        size_t bytes = outInfo.getSafeSize(bitmap->rowBytes());
+        memset(pixels, 0, bytes);
+    }
+
+    // Decode into the destination bitmap
+    SkAndroidCodec::AndroidOptions options;
+    options.fSampleSize = sampleSize;
+    options.fSubset = &subset;
+    options.fColorPtr = colorPtr;
+    options.fColorCount = colorCountPtr;
+    void* dst = bitmap->getAddr(scaledOutX, scaledOutY);
+    size_t rowBytes = bitmap->rowBytes();
+    SkCodec::Result result = fCodec->getAndroidPixels(decodeInfo, dst, rowBytes, &options);
+    if (SkCodec::kSuccess != result && SkCodec::kIncompleteInput != result) {
+        SkCodecPrintf("Error: Could not get pixels.\n");
+        return nullptr;
+    }
+
+    return bitmap.detach();
+}
+
+bool SkBitmapRegionCodec::conversionSupported(SkColorType colorType) {
+    // FIXME: Call virtual function when it lands.
+    SkImageInfo info = SkImageInfo::Make(0, 0, colorType, fCodec->getInfo().alphaType(),
+            fCodec->getInfo().profileType());
+    return conversion_possible(info, fCodec->getInfo());
+}
diff --git a/tools/SkBitmapRegionCodec.h b/tools/SkBitmapRegionCodec.h
new file mode 100644 (file)
index 0000000..14d024e
--- /dev/null
@@ -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 "SkAndroidCodec.h"
+
+/*
+ * This class implements SkBitmapRegionDecoder using an SkAndroidCodec.
+ */
+class SkBitmapRegionCodec : public SkBitmapRegionDecoderInterface {
+public:
+
+    /*
+     * Takes ownership of pointer to codec
+     */
+    SkBitmapRegionCodec(SkAndroidCodec* codec);
+
+    /*
+     * 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;
+
+    bool conversionSupported(SkColorType colorType) override;
+
+private:
+
+    SkAutoTDelete<SkAndroidCodec> fCodec;
+
+    typedef SkBitmapRegionDecoderInterface INHERITED;
+
+};
index 835ed9a..ec6327e 100644 (file)
@@ -6,24 +6,26 @@
  */
 
 #include "SkBitmapRegionCanvas.h"
+#include "SkBitmapRegionCodec.h"
 #include "SkBitmapRegionDecoderInterface.h"
 #include "SkBitmapRegionSampler.h"
+#include "SkAndroidCodec.h"
 #include "SkCodec.h"
 #include "SkCodecPriv.h"
 #include "SkImageDecoder.h"
 
 SkBitmapRegionDecoderInterface* SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(
-        SkStreamRewindable* stream, Strategy strategy) {
-    SkAutoTDelete<SkStreamRewindable> streamDeleter(stream);
+        SkData* data, Strategy strategy) {
     switch (strategy) {
         case kOriginal_Strategy: {
+            SkAutoTDelete<SkStreamRewindable> stream(new SkMemoryStream(data));
             SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
             int width, height;
             if (nullptr == decoder) {
                 SkCodecPrintf("Error: Could not create image decoder.\n");
                 return nullptr;
             }
-            if (!decoder->buildTileIndex(streamDeleter.detach(), &width, &height)) {
+            if (!decoder->buildTileIndex(stream.detach(), &width, &height)) {
                 SkCodecPrintf("Error: Could not build tile index.\n");
                 delete decoder;
                 return nullptr;
@@ -31,7 +33,7 @@ SkBitmapRegionDecoderInterface* SkBitmapRegionDecoderInterface::CreateBitmapRegi
             return new SkBitmapRegionSampler(decoder, width, height);
         }
         case kCanvas_Strategy: {
-            SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(streamDeleter.detach()));
+            SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(data));
             if (nullptr == codec) {
                 SkCodecPrintf("Error: Failed to create decoder.\n");
                 return nullptr;
@@ -46,6 +48,14 @@ SkBitmapRegionDecoderInterface* SkBitmapRegionDecoderInterface::CreateBitmapRegi
             }
             return new SkBitmapRegionCanvas(codec.detach());
         }
+        case kAndroidCodec_Strategy: {
+            SkAutoTDelete<SkAndroidCodec> codec = SkAndroidCodec::NewFromData(data);
+            if (NULL == codec) {
+                SkCodecPrintf("Error: Failed to create codec.\n");
+                return NULL;
+            }
+            return new SkBitmapRegionCodec(codec.detach());
+        }
         default:
             SkASSERT(false);
             return nullptr;
index 047f023..e1f79bc 100644 (file)
@@ -19,19 +19,18 @@ class SkBitmapRegionDecoderInterface {
 public:
 
     enum Strategy {
-        kCanvas_Strategy,   // Draw to the canvas, uses SkCodec
-        kOriginal_Strategy, // Sampling, uses SkImageDecoder
-        // TODO (msarett): Add strategy for SkScaledCodec
+        kCanvas_Strategy,       // Draw to the canvas, uses SkCodec
+        kOriginal_Strategy,     // Sampling, uses SkImageDecoder
+        kAndroidCodec_Strategy, // Uses SkAndroidCodec for scaling and subsetting
     };
 
     /*
-     * @param stream   Encoded image stream, takes ownership
+     * @param data     Refs the data while this object exists, unrefs on destruction
      * @param strategy Strategy used for scaling and subsetting
-     * @return         Tries to create an SkBitmapRegionDecoder, returns NULL
-     *                 on failure
+     * @return         Tries to create an SkBitmapRegionDecoder, returns NULL on failure
      */
     static SkBitmapRegionDecoderInterface* CreateBitmapRegionDecoder(
-            SkStreamRewindable* stream, Strategy strategy);
+            SkData* data, Strategy strategy);
 
     /*
      * Decode a scaled region of the encoded image stream
index 097915b..285d3a2 100644 (file)
@@ -12,4 +12,52 @@ inline float get_scale_from_sample_size(uint32_t sampleSize) {
     return 1.0f / (float) sampleSize;
 }
 
+enum SubsetType {
+    kFullyInside_SubsetType,
+    kPartiallyInside_SubsetType,
+    kOutside_SubsetType,
+};
+
+/*
+ * Corrects image subset offsets and dimensions in order to perform a valid decode.
+ * Also indicates if the image subset should be placed at an offset within the
+ * output bitmap.
+ *
+ * Values of output variables are undefined if the SubsetType is kInvalid.
+ *
+ * @param imageDims Original image dimensions.
+ * @param subset    As input, the subset that the client requested.
+ *                  As output, the image subset that we will decode.
+ * @param outX      The left offset of the image subset within the output bitmap.
+ * @param outY      The top offset of the image subset within the output bitmap.
+ *
+ * @return An indication of how the subset is contained in the image.
+ *         If the return value is kInvalid, values of output variables are undefined.
+ */
+inline SubsetType adjust_subset_rect(const SkISize& imageDims, SkIRect* subset, int* outX,
+        int* outY) {
+    // These must be at least zero, we can't start decoding the image at a negative coordinate.
+    int left = SkTMax(0, subset->fLeft);
+    int top = SkTMax(0, subset->fTop);
+
+    // If input offsets are less than zero, we decode to an offset location in the output bitmap.
+    *outX = left - subset->fLeft;
+    *outY = top - subset->fTop;
+
+    // Make sure we don't decode pixels past the edge of the image or past the edge of the subset.
+    int width = SkTMin(imageDims.width() - left, subset->width() - *outX);
+    int height = SkTMin(imageDims.height() - top, subset->height() - *outY);
+    if (width <= 0 || height <= 0) {
+        return SubsetType::kOutside_SubsetType;
+    }
+
+    subset->setXYWH(left, top, width, height);
+    if ((*outX != 0) || (*outY != 0) || (width != subset->width()) ||
+            (height != subset->height())) {
+        return SubsetType::kPartiallyInside_SubsetType;
+    }
+
+    return SubsetType::kFullyInside_SubsetType;
+}
+
 #endif // SkCodecTools_DEFINED