am 943e1fde: Add support for reusing bitmap in webp format.

* commit '943e1fde4c6ecf0eb8998cd86012caa341a02ccf':
  Add support for reusing bitmap in webp format.
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
index 23dec76..c4eac9c 100644
--- a/src/images/SkImageDecoder.cpp
+++ b/src/images/SkImageDecoder.cpp
@@ -195,19 +195,8 @@
                                     int width, int height, int srcX, int srcY) {
     int w = width / sampleSize;
     int h = height / sampleSize;
-    if (w == src->width() && h == src->height() &&
-          (srcX - destX) / sampleSize == 0 &&
-          (srcY - destY) / sampleSize == 0 &&
-          dest->isNull()
-          ) {
-        // The output rect is the same as the decode result
-        dest->swap(*src);
-        return;
-    }
-
     // if the destination has no pixels then we must allocate them.
     if (dest->isNull()) {
-        // The output rect is smaller than the decode result
         dest->setConfig(src->getConfig(), w, h);
         dest->setIsOpaque(src->isOpaque());
 
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
index b191848..fbb6887 100644
--- a/src/images/SkImageDecoder_libjpeg.cpp
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -77,6 +77,8 @@
     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
 private:
     SkJPEGImageIndex *index;
+    int imageWidth;
+    int imageHeight;
 };
 
 //////////////////////////////////////////////////////////////////////////
@@ -528,7 +530,8 @@
     index->cinfo = cinfo;
     *height = cinfo->output_height;
     *width = cinfo->output_width;
-
+    this->imageWidth = *width;
+    this->imageHeight = *height;
     this->index = index;
     return true;
 }
@@ -537,11 +540,14 @@
     if (index == NULL) {
         return false;
     }
-    int startX = region.fLeft;
-    int startY = region.fTop;
-    int width = region.width();
-    int height = region.height();
     jpeg_decompress_struct *cinfo = index->cinfo;
+
+    SkIRect rect = SkIRect::MakeWH(this->imageWidth, this->imageHeight);
+    if (!rect.intersect(region)) {
+        // If the requested region is entirely outsides the image, just
+        // returns false
+        return false;
+    }
     SkAutoMalloc  srcStorage;
     skjpeg_error_mgr        sk_err;
     cinfo->err = jpeg_std_error(&sk_err);
@@ -579,11 +585,11 @@
         }
     }
 #endif
+    int startX = rect.fLeft;
+    int startY = rect.fTop;
+    int width = rect.width();
+    int height = rect.height();
 
-    int oriStartX = startX;
-    int oriStartY = startY;
-    int oriWidth = width;
-    int oriHeight = height;
     jpeg_init_read_tile_scanline(cinfo, index->index,
                                  &startX, &startY, &width, &height);
     int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
@@ -604,22 +610,25 @@
     {
         bitmap->setConfig(config, cinfo->output_width, height);
         bitmap->setIsOpaque(true);
-        // Check ahead of time if the swap(dest, src) is possible in crop or
-        // not. If yes, then we will stick to AllocPixelRef since it's cheaper
+
+        // Check ahead of time if the swap(dest, src) is possible or not.
+        // If yes, then we will stick to AllocPixelRef since it's cheaper
         // with the swap happening. If no, then we will use alloc to allocate
         // pixels to prevent garbage collection.
-        int w = oriWidth / actualSampleSize;
-        int h = oriHeight / actualSampleSize;
-        if (w == bitmap->width() && h == bitmap->height() &&
-            (startX - oriStartX) / actualSampleSize == 0 &&
-            (startY - oriStartY) / actualSampleSize == 0 && bm->isNull()) {
-            // Not using a recycled-bitmap and the output rect is same as the
-            // decoded region.
+        //
+        // Not using a recycled-bitmap and the output rect is same as the
+        // decoded region.
+        int w = rect.width() / actualSampleSize;
+        int h = rect.height() / actualSampleSize;
+        bool swapOnly = (rect == region) && bm->isNull() &&
+                        (w == bitmap->width()) && (h == bitmap->height()) &&
+                        ((startX - rect.x()) / actualSampleSize == 0) &&
+                        ((startY - rect.y()) / actualSampleSize == 0);
+        if (swapOnly) {
             if (!this->allocPixelRef(bitmap, NULL)) {
                 return return_false(*cinfo, *bitmap, "allocPixelRef");
             }
-        }
-        else {
+        } else {
             if (!bitmap->allocPixels()) {
                 return return_false(*cinfo, *bitmap, "allocPixels");
             }
@@ -644,8 +653,13 @@
             row_total_count += row_count;
             rowptr += bpr;
         }
-        cropBitmap(bm, bitmap, actualSampleSize, oriStartX, oriStartY,
-                   oriWidth, oriHeight, startX, startY);
+
+        if (swapOnly) {
+            bm->swap(*bitmap);
+        } else {
+            cropBitmap(bm, bitmap, actualSampleSize, region.x(), region.y(),
+                       region.width(), region.height(), startX, startY);
+        }
         return true;
     }
 #endif
@@ -671,20 +685,21 @@
     bitmap->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
     bitmap->setIsOpaque(true);
 
-    // Check ahead of time if the swap(dest, src) is possible in crop or not.
+    // Check ahead of time if the swap(dest, src) is possible or not.
     // If yes, then we will stick to AllocPixelRef since it's cheaper with the
     // swap happening. If no, then we will use alloc to allocate pixels to
     // prevent garbage collection.
-    int w = oriWidth / actualSampleSize;
-    int h = oriHeight / actualSampleSize;
-    if (w == bitmap->width() && h == bitmap->height() &&
-        (startX - oriStartX) / actualSampleSize == 0 &&
-        (startY - oriStartY) / actualSampleSize == 0 && bm->isNull()) {
+    int w = rect.width() / actualSampleSize;
+    int h = rect.height() / actualSampleSize;
+    bool swapOnly = (rect == region) && bm->isNull() &&
+                    (w == bitmap->width()) && (h == bitmap->height()) &&
+                    ((startX - rect.x()) / actualSampleSize == 0) &&
+                    ((startY - rect.y()) / actualSampleSize == 0);
+    if (swapOnly) {
         if (!this->allocPixelRef(bitmap, NULL)) {
             return return_false(*cinfo, *bitmap, "allocPixelRef");
         }
-    }
-    else {
+    } else {
         if (!bitmap->allocPixels()) {
             return return_false(*cinfo, *bitmap, "allocPixels");
         }
@@ -724,8 +739,12 @@
             return return_false(*cinfo, *bitmap, "skip rows");
         }
     }
-    cropBitmap(bm, bitmap, actualSampleSize, oriStartX, oriStartY,
-               oriWidth, oriHeight, startX, startY);
+    if (swapOnly) {
+        bm->swap(*bitmap);
+    } else {
+        cropBitmap(bm, bitmap, actualSampleSize, region.x(), region.y(),
+                   region.width(), region.height(), startX, startY);
+    }
     return true;
 }
 
diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp
index bb31806..fa35239 100644
--- a/src/images/SkImageDecoder_libpng.cpp
+++ b/src/images/SkImageDecoder_libpng.cpp
@@ -58,7 +58,7 @@
 protected:
     virtual bool onBuildTileIndex(SkStream *stream,
              int *width, int *height);
-    virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect);
+    virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect region);
     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
 
 private:
@@ -616,7 +616,7 @@
     return true;
 }
 
-bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect rect) {
+bool SkPNGImageDecoder::onDecodeRegion(SkBitmap* bm, SkIRect region) {
     int i;
     png_structp png_ptr = this->index->png_ptr;
     png_infop info_ptr = this->index->info_ptr;
@@ -624,14 +624,19 @@
         return false;
     }
 
-    int requestedHeight = rect.fBottom - rect.fTop;
-    int requestedWidth = rect.fRight - rect.fLeft;
-
     png_uint_32 origWidth, origHeight;
     int bit_depth, color_type, interlace_type;
     png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bit_depth,
             &color_type, &interlace_type, int_p_NULL, int_p_NULL);
 
+    SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
+
+    if (!rect.intersect(region)) {
+        // If the requested region is entirely outsides the image, just
+        // returns false
+        return false;
+    }
+
     SkBitmap::Config    config;
     bool                hasAlpha = false;
     bool                doDither = this->getDitherImage();
@@ -643,7 +648,7 @@
     }
 
     const int sampleSize = this->getSampleSize();
-    SkScaledBitmapSampler sampler(origWidth, requestedHeight, sampleSize);
+    SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize);
 
     SkBitmap *decodedBitmap = new SkBitmap;
     SkAutoTDelete<SkBitmap> adb(decodedBitmap);
@@ -669,17 +674,17 @@
     // Check ahead of time if the swap(dest, src) is possible in crop or not.
     // If yes, then we will stick to AllocPixelRef since it's cheaper with the swap happening.
     // If no, then we will use alloc to allocate pixels to prevent garbage collection.
-    int w = requestedWidth / sampleSize;
-    int h = requestedHeight / sampleSize;
-    if (w == decodedBitmap->width() && h == decodedBitmap->height() &&
-        (0 - rect.fLeft / sampleSize) == 0 && (rect.fTop - rect.fTop) / sampleSize == 0 &&
-        bm->isNull()) {
+    int w = rect.width() / sampleSize;
+    int h = rect.height() / sampleSize;
+    bool swapOnly = (rect == region) && (w == decodedBitmap->width()) &&
+                    (h == decodedBitmap->height()) &&
+                    ((0 - rect.x()) / sampleSize == 0) && bm->isNull();
+    if (swapOnly) {
         if (!this->allocPixelRef(decodedBitmap,
                 SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
             return false;
         }
-    }
-    else {
+    } else {
         if (!decodedBitmap->allocPixels(
             NULL, SkBitmap::kIndex8_Config == config ? colorTable : NULL)) {
             return false;
@@ -706,8 +711,6 @@
     png_ptr->pass = 0;
     png_read_update_info(png_ptr, info_ptr);
 
-    // SkDebugf("Request size %d %d\n", requestedWidth, requestedHeight);
-
     int actualTop = rect.fTop;
 
     if (SkBitmap::kIndex8_Config == config && 1 == sampleSize) {
@@ -757,7 +760,7 @@
                     png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
                 }
                 uint8_t* row = base;
-                for (png_uint_32 y = 0; y < requestedHeight; y++) {
+                for (png_uint_32 y = 0; y < rect.height(); y++) {
                     uint8_t* bmRow = row;
                     png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
                     row += rb;
@@ -790,8 +793,12 @@
             }
         }
     }
-    cropBitmap(bm, decodedBitmap, sampleSize, rect.fLeft, rect.fTop,
-                requestedWidth, requestedHeight, 0, rect.fTop);
+    if (swapOnly) {
+        bm->swap(*decodedBitmap);
+    } else {
+        cropBitmap(bm, decodedBitmap, sampleSize, region.x(), region.y(),
+                   region.width(), region.height(), 0, rect.y());
+    }
 
     if (0 != theTranspColor) {
         reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);
diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp
index c2fce45..3e416cc 100644
--- a/src/images/SkImageDecoder_libwebp.cpp
+++ b/src/images/SkImageDecoder_libwebp.cpp
@@ -21,6 +21,7 @@
 #include "SkStream.h"
 #include "SkTemplates.h"
 #include "SkUtils.h"
+#include "SkTScopedPtr.h"
 
 // A WebP decoder only, on top of (subset of) libwebp
 // For more information on WebP image format, and libwebp library, see:
@@ -151,7 +152,7 @@
 // Incremental WebP image decoding. Reads input buffer of 64K size iteratively
 // and decodes this block to appropriate color-space as per config object.
 static bool webp_idecode(SkStream* stream, WebPDecoderConfig& config) {
-    WebPIDecoder* idec = WebPIDecode(NULL, NULL, &config);
+    WebPIDecoder* idec = WebPIDecode(NULL, 0, &config);
     if (idec == NULL) {
         WebPFreeDecBuffer(&config.output);
         return false;
@@ -309,33 +310,82 @@
     return true;
 }
 
+static bool isConfigCompatiable(SkBitmap* bitmap) {
+    SkBitmap::Config config = bitmap->config();
+    return config == SkBitmap::kARGB_4444_Config ||
+           config == SkBitmap::kRGB_565_Config ||
+           config == SkBitmap::kARGB_8888_Config;
+}
+
 bool SkWEBPImageDecoder::onDecodeRegion(SkBitmap* decodedBitmap,
                                         SkIRect region) {
-    const int width = region.width();
-    const int height = region.height();
+    SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
 
-    const int sampleSize = this->getSampleSize();
-    SkScaledBitmapSampler sampler(width, height, sampleSize);
-
-    if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
-                         sampler.scaledHeight())) {
+    if (!rect.intersect(region)) {
+        // If the requested region is entirely outsides the image, just
+        // returns false
         return false;
     }
 
-    if (!this->allocPixelRef(decodedBitmap, NULL)) {
-        return return_false(*decodedBitmap, "allocPixelRef");
+    const int sampleSize = this->getSampleSize();
+    SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize);
+    const int width = sampler.scaledWidth();
+    const int height = sampler.scaledHeight();
+
+    // The image can be decoded directly to decodedBitmap if
+    //   1. the region is within the image range
+    //   2. bitmap's config is compatible
+    //   3. bitmap's size is same as the required region (after sampled)
+    bool directDecode = (rect == region) &&
+                        (decodedBitmap->isNull() ||
+                         isConfigCompatiable(decodedBitmap) &&
+                         (decodedBitmap->width() == width) &&
+                         (decodedBitmap->height() == height));
+    SkTScopedPtr<SkBitmap> adb;
+    SkBitmap *bitmap = decodedBitmap;
+
+    if (!directDecode) {
+        // allocates a temp bitmap
+        bitmap = new SkBitmap;
+        adb.reset(bitmap);
     }
 
-    SkAutoLockPixels alp(*decodedBitmap);
+    if (bitmap->isNull()) {
+        if (!setDecodeConfig(bitmap, width, height)) {
+            return false;
+        }
+        // alloc from native heap if it is a temp bitmap. (prevent GC)
+        bool allocResult = (bitmap == decodedBitmap)
+                               ? allocPixelRef(bitmap, NULL)
+                               : bitmap->allocPixels();
+        if (!allocResult) {
+            return return_false(*decodedBitmap, "allocPixelRef");
+        }
+    } else {
+        // This is also called in setDecodeConfig in above block.
+        // i.e., when bitmap->isNull() is true.
+        if (!chooseFromOneChoice(bitmap->config(), width, height)) {
+            return false;
+        }
+    }
 
+    SkAutoLockPixels alp(*bitmap);
     WebPDecoderConfig config;
-    if (!webp_get_config_resize_crop(config, decodedBitmap, region)) {
+    if (!webp_get_config_resize_crop(config, bitmap, rect)) {
         return false;
     }
 
     // Decode the WebP image data stream using WebP incremental decoding for
     // the specified cropped image-region.
-    return webp_idecode(this->inputStream, config);
+    if (!webp_idecode(this->inputStream, config)) {
+        return false;
+    }
+
+    if (!directDecode) {
+        cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(),
+                  region.width(), region.height(), rect.x(), rect.y());
+    }
+    return true;
 }
 
 bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,