Add image cropping support in Skia-WebP decoder.

Implement virtual methods 'onBuildTileIndex' & 'onDecodeRegion' for
supporting image cropping for Skia-WebP image Decoder.
Utilize the native rescaling+Cropping capability of WebP-Decoder to
implement image cropping.
- Incorporated code comments from Pascal.
- Found one bug w.r.t setting SampleSize. Rectifying that in this patch.

Change-Id: Ib7b04129a65ed17f845d0600f3553b9b24846838
diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp
index 7b4a39f..984331c 100644
--- a/src/images/SkImageDecoder_libwebp.cpp
+++ b/src/images/SkImageDecoder_libwebp.cpp
@@ -30,8 +30,8 @@
 
 #include <stdio.h>
 extern "C" {
-// If moving libwebp out of skia source tree, path for webp headers must be updated accordingly.
-// Here, we enforce using local copy in webp sub-directory.
+// If moving libwebp out of skia source tree, path for webp headers must be
+// updated accordingly. Here, we enforce using local copy in webp sub-directory.
 #include "webp/decode.h"
 #include "webp/decode_vp8.h"
 #include "webp/encode.h"
@@ -92,10 +92,15 @@
     }
 
 protected:
+    virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height);
+    virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect);
     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
 
 private:
     bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height);
+    SkStream *inputStream;
+    int origWidth;
+    int origHeight;
 };
 
 //////////////////////////////////////////////////////////////////////////
@@ -130,15 +135,7 @@
     return false; // must always return false
 }
 
-// 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, SkBitmap* decodedBitmap,
-                         int origWidth, int origHeight) {
-    SkAutoLockPixels alp(*decodedBitmap);
-
-    stream->rewind();
-    const uint32_t contentSize = stream->getLength();
-
+static WEBP_CSP_MODE webp_decode_mode(SkBitmap* decodedBitmap) {
     WEBP_CSP_MODE mode = MODE_LAST;
     SkBitmap::Config config = decodedBitmap->config();
     if (config == SkBitmap::kARGB_8888_Config) {
@@ -147,33 +144,21 @@
       mode = MODE_RGBA_4444;
     } else if (config == SkBitmap::kRGB_565_Config) {
       mode = MODE_RGB_565;
-    } else {
-      return false;
     }
+    return mode;
+}
 
-    WebPDecoderConfig decode_config;
-    if (WebPInitDecoderConfig(&decode_config) == 0) {
-        return false;
-    }
-
-    decode_config.output.colorspace = mode;
-    decode_config.output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
-    decode_config.output.u.RGBA.stride = decodedBitmap->rowBytes();
-    decode_config.output.u.RGBA.size = decodedBitmap->getSize();
-    decode_config.output.is_external_memory = 1;
-    if (origWidth != decodedBitmap->width() ||
-        origHeight != decodedBitmap->height()) {
-        decode_config.options.use_scaling = 1;
-        decode_config.options.scaled_width = decodedBitmap->width();
-        decode_config.options.scaled_height = decodedBitmap->height();
-    }
-
-    WebPIDecoder* idec = WebPIDecode(NULL, NULL, &decode_config);
+// 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);
     if (idec == NULL) {
-        WebPFreeDecBuffer(&decode_config.output);
+        WebPFreeDecBuffer(&config.output);
         return false;
     }
 
+    stream->rewind();
+    const uint32_t contentSize = stream->getLength();
     uint32_t read_buffer_size = contentSize;
     if (read_buffer_size > WEBP_IDECODE_BUFFER_SZ) {
         read_buffer_size = WEBP_IDECODE_BUFFER_SZ;
@@ -182,7 +167,7 @@
     unsigned char* input = (uint8_t*)srcStorage.get();
     if (input == NULL) {
         WebPIDelete(idec);
-        WebPFreeDecBuffer(&decode_config.output);
+        WebPFreeDecBuffer(&config.output);
         return false;
     }
 
@@ -206,7 +191,7 @@
     }
     srcStorage.free();
     WebPIDelete(idec);
-    WebPFreeDecBuffer(&decode_config.output);
+    WebPFreeDecBuffer(&config.output);
 
     if (bytes_remaining > 0) {
         return false;
@@ -215,13 +200,74 @@
     }
 }
 
+static bool webp_get_config_resize_crop(WebPDecoderConfig& config,
+                                        SkBitmap* decodedBitmap,
+                                        SkIRect region) {
+    WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap);
+    if (mode == MODE_LAST) {
+        return false;
+    }
+
+    if (WebPInitDecoderConfig(&config) == 0) {
+        return false;
+    }
+
+    config.output.colorspace = mode;
+    config.output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
+    config.output.u.RGBA.stride = decodedBitmap->rowBytes();
+    config.output.u.RGBA.size = decodedBitmap->getSize();
+    config.output.is_external_memory = 1;
+
+    config.options.use_cropping = 1;
+    config.options.crop_left = region.fLeft;
+    config.options.crop_top = region.fTop;
+    config.options.crop_width = region.width();
+    config.options.crop_height = region.height();
+
+    if (region.width() != decodedBitmap->width() ||
+        region.height() != decodedBitmap->height()) {
+        config.options.use_scaling = 1;
+        config.options.scaled_width = decodedBitmap->width();
+        config.options.scaled_height = decodedBitmap->height();
+    }
+
+    return true;
+}
+
+static bool webp_get_config_resize(WebPDecoderConfig& config,
+                                   SkBitmap* decodedBitmap, int origWidth,
+                                   int origHeight) {
+    WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap);
+    if (mode == MODE_LAST) {
+        return false;
+    }
+
+    if (WebPInitDecoderConfig(&config) == 0) {
+        return false;
+    }
+
+    config.output.colorspace = mode;
+    config.output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
+    config.output.u.RGBA.stride = decodedBitmap->rowBytes();
+    config.output.u.RGBA.size = decodedBitmap->getSize();
+    config.output.is_external_memory = 1;
+
+    if (origWidth != decodedBitmap->width() ||
+        origHeight != decodedBitmap->height()) {
+        config.options.use_scaling = 1;
+        config.options.scaled_width = decodedBitmap->width();
+        config.options.scaled_height = decodedBitmap->height();
+    }
+
+    return true;
+}
+
 bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap,
                                          int width, int height) {
     bool hasAlpha = false;
     SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha);
 
-    // only accept prefConfig if it makes sense for us. YUV converter
-    // supports output in RGB565, RGBA4444 and RGBA8888 formats.
+    // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats.
     if (hasAlpha) {
         if (config != SkBitmap::kARGB_4444_Config) {
             config = SkBitmap::kARGB_8888_Config;
@@ -245,6 +291,53 @@
     return true;
 }
 
+bool SkWEBPImageDecoder::onBuildTileIndex(SkStream* stream,
+                                          int *width, int *height) {
+    int origWidth, origHeight;
+    if (!webp_parse_header(stream, &origWidth, &origHeight)) {
+        return false;
+    }
+
+    stream->rewind();
+    *width = origWidth;
+    *height = origHeight;
+
+    this->inputStream = stream;
+    this->origWidth = origWidth;
+    this->origHeight = origHeight;
+
+    return true;
+}
+
+bool SkWEBPImageDecoder::onDecodeRegion(SkBitmap* decodedBitmap,
+                                        SkIRect region) {
+    const int width = region.width();
+    const int height = region.height();
+
+    const int sampleSize = this->getSampleSize();
+    SkScaledBitmapSampler sampler(width, height, sampleSize);
+
+    if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
+                         sampler.scaledHeight())) {
+        return false;
+    }
+
+    if (!this->allocPixelRef(decodedBitmap, NULL)) {
+        return return_false(*decodedBitmap, "allocPixelRef");
+    }
+
+    SkAutoLockPixels alp(*decodedBitmap);
+
+    WebPDecoderConfig config;
+    if (!webp_get_config_resize_crop(config, decodedBitmap, region)) {
+        return false;
+    }
+
+    // Decode the WebP image data stream using WebP incremental decoding for
+    // the specified cropped image-region.
+    return webp_idecode(this->inputStream, config);
+}
+
 bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
                                   Mode mode) {
 #ifdef TIME_DECODE
@@ -273,12 +366,15 @@
         return return_false(*decodedBitmap, "allocPixelRef");
     }
 
-    // Decode the WebP image data stream using WebP incremental decoding.
-    if (!webp_idecode(stream, decodedBitmap, origWidth, origHeight)) {
+    SkAutoLockPixels alp(*decodedBitmap);
+
+    WebPDecoderConfig config;
+    if (!webp_get_config_resize(config, decodedBitmap, origWidth, origHeight)) {
         return false;
     }
 
-    return true;
+    // Decode the WebP image data stream using WebP incremental decoding.
+    return webp_idecode(stream, config);
 }
 
 ///////////////////////////////////////////////////////////////////////////////