Add SkWebpCodec, for decoding .webp images.

Based on SkImageDecoder_libwebp.

TODO:
Support YUV? (Longer term - may influence our API for SkImageGenerator)

BUG=skia:3257

Review URL: https://codereview.chromium.org/1044433002
diff --git a/dm/DM.cpp b/dm/DM.cpp
index 33ce0a4..60929d5 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -259,8 +259,8 @@
     // 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 const char* const exts[] = {
-        "bmp", "gif", "jpg", "jpeg", "png", "ico", "wbmp",
-        "BMP", "GIF", "JPG", "JPEG", "PNG", "ICO", "WBMP"
+        "bmp", "gif", "jpg", "jpeg", "png", "ico", "wbmp", "webp",
+        "BMP", "GIF", "JPG", "JPEG", "PNG", "ICO", "WBMP", "WEBP",
     };
 
     for (uint32_t i = 0; i < SK_ARRAY_COUNT(exts); i++) {
diff --git a/gyp/codec.gyp b/gyp/codec.gyp
index 7c22a5d..0d54184 100644
--- a/gyp/codec.gyp
+++ b/gyp/codec.gyp
@@ -19,6 +19,7 @@
         'core.gyp:*',
         'giflib.gyp:giflib',
         'libjpeg.gyp:libjpeg',
+        'libwebp.gyp:libwebp',
       ],
       'cflags':[
         # FIXME: This gets around a longjmp warning. See
@@ -44,6 +45,7 @@
         '../src/codec/SkMaskSwizzler.cpp',
         '../src/codec/SkMasks.cpp',
         '../src/codec/SkSwizzler.cpp',
+        '../src/codec/SkWebpCodec.cpp',
       ],
       'direct_dependent_settings': {
         'include_dirs': [
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index c4adf72..93db0e5 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -15,6 +15,7 @@
 #include "SkCodecPriv.h"
 #include "SkJpegCodec.h"
 #include "SkStream.h"
+#include "SkWebpCodec.h"
 
 struct DecoderProc {
     bool (*IsFormat)(SkStream*);
@@ -24,6 +25,7 @@
 static const DecoderProc gDecoderProcs[] = {
     { SkPngCodec::IsPng, SkPngCodec::NewFromStream },
     { SkJpegCodec::IsJpeg, SkJpegCodec::NewFromStream },
+    { SkWebpCodec::IsWebp, SkWebpCodec::NewFromStream },
     { SkGifCodec::IsGif, SkGifCodec::NewFromStream },
     { SkIcoCodec::IsIco, SkIcoCodec::NewFromStream },
     { SkBmpCodec::IsBmp, SkBmpCodec::NewFromStream },
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
new file mode 100644
index 0000000..b02015c
--- /dev/null
+++ b/src/codec/SkWebpCodec.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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 "SkWebpCodec.h"
+#include "SkImageGenerator.h"
+#include "SkTemplates.h"
+
+// A WebP decoder on top of (subset of) libwebp
+// For more information on WebP image format, and libwebp library, see:
+//   https://code.google.com/speed/webp/
+//   http://www.webmproject.org/code/#libwebp-webp-image-library
+//   https://chromium.googlesource.com/webm/libwebp
+
+// 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/encode.h"
+
+bool SkWebpCodec::IsWebp(SkStream* stream) {
+    // WEBP starts with the following:
+    // RIFFXXXXWEBPVP
+    // Where XXXX is unspecified.
+    const char LENGTH = 14;
+    char bytes[LENGTH];
+    if (stream->read(&bytes, LENGTH) != LENGTH) {
+        return false;
+    }
+    return !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "WEBPVP", 6);
+}
+
+static const size_t WEBP_VP8_HEADER_SIZE = 30;
+
+// Parse headers of RIFF container, and check for valid Webp (VP8) content.
+// NOTE: This calls peek instead of read, since onGetPixels will need these
+// bytes again.
+static bool webp_parse_header(SkStream* stream, SkImageInfo* info) {
+    unsigned char buffer[WEBP_VP8_HEADER_SIZE];
+    if (!stream->peek(buffer, WEBP_VP8_HEADER_SIZE)) {
+        return false;
+    }
+
+    WebPBitstreamFeatures features;
+    VP8StatusCode status = WebPGetFeatures(buffer, WEBP_VP8_HEADER_SIZE, &features);
+    if (VP8_STATUS_OK != status) {
+        return false; // Invalid WebP file.
+    }
+
+    // sanity check for image size that's about to be decoded.
+    {
+        const int64_t size = sk_64_mul(features.width, features.height);
+        if (!sk_64_isS32(size)) {
+            return false;
+        }
+        // now check that if we are 4-bytes per pixel, we also don't overflow
+        if (sk_64_asS32(size) > (0x7FFFFFFF >> 2)) {
+            return false;
+        }
+    }
+
+    if (info) {
+        // FIXME: Is N32 the right type?
+        // Is unpremul the right type? Clients of SkImageGenerator may assume it's the
+        // best type, when Skia currently cannot draw unpremul (and raster is faster
+        // with premul).
+        *info = SkImageInfo::Make(features.width, features.height, kN32_SkColorType,
+                                  SkToBool(features.has_alpha) ? kUnpremul_SkAlphaType
+                                                              : kOpaque_SkAlphaType);
+    }
+    return true;
+}
+
+SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
+    SkAutoTDelete<SkStream> streamDeleter(stream);
+    SkImageInfo info;
+    if (webp_parse_header(stream, &info)) {
+        return SkNEW_ARGS(SkWebpCodec, (info, streamDeleter.detach()));
+    }
+    return NULL;
+}
+
+static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
+    switch (dst.colorType()) {
+        // Both byte orders are supported.
+        case kBGRA_8888_SkColorType:
+        case kRGBA_8888_SkColorType:
+            break;
+        default:
+            return false;
+    }
+    if (dst.profileType() != src.profileType()) {
+        return false;
+    }
+    if (dst.alphaType() == src.alphaType()) {
+        return true;
+    }
+    return kPremul_SkAlphaType == dst.alphaType() &&
+            kUnpremul_SkAlphaType == src.alphaType();
+}
+
+SkISize SkWebpCodec::onGetScaledDimensions(float desiredScale) const {
+    SkISize dim = this->getInfo().dimensions();
+    dim.fWidth = SkScalarRoundToInt(desiredScale * dim.fWidth);
+    dim.fHeight = SkScalarRoundToInt(desiredScale * dim.fHeight);
+    return dim;
+}
+
+static WEBP_CSP_MODE webp_decode_mode(SkColorType ct, bool premultiply) {
+    switch (ct) {
+        case kBGRA_8888_SkColorType:
+            return premultiply ? MODE_bgrA : MODE_BGRA;
+        case kRGBA_8888_SkColorType:
+            return premultiply ? MODE_rgbA : MODE_RGBA;
+        default:
+            return MODE_LAST;
+    }
+}
+
+// The WebP decoding API allows us to incrementally pass chunks of bytes as we receive them to the
+// decoder with WebPIAppend. In order to do so, we need to read chunks from the SkStream. This size
+// is arbitrary.
+static const size_t BUFFER_SIZE = 4096;
+
+SkImageGenerator::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
+                                                  size_t rowBytes, const Options&, SkPMColor*,
+                                                  int*) {
+    switch (this->rewindIfNeeded()) {
+        case kCouldNotRewind_RewindState:
+            return kCouldNotRewind;
+        case kRewound_RewindState:
+            // Rewound to the beginning. Since creation only does a peek, the stream is at the
+            // correct position.
+            break;
+        case kNoRewindNecessary_RewindState:
+            // Already at the right spot for decoding.
+            break;
+    }
+
+    if (!conversion_possible(dstInfo, this->getInfo())) {
+        return kInvalidConversion;
+    }
+
+    WebPDecoderConfig config;
+    if (0 == WebPInitDecoderConfig(&config)) {
+        // ABI mismatch.
+        // FIXME: New enum for this?
+        return kInvalidInput;
+    }
+
+    // Free any memory associated with the buffer. Must be called last, so we declare it first.
+    SkAutoTCallVProc<WebPDecBuffer, WebPFreeDecBuffer> autoFree(&(config.output));
+
+    SkISize dimensions = dstInfo.dimensions();
+    if (this->getInfo().dimensions() != dimensions) {
+        // Caller is requesting scaling.
+        config.options.use_scaling = 1;
+        config.options.scaled_width = dimensions.width();
+        config.options.scaled_height = dimensions.height();
+    }
+
+    config.output.colorspace = webp_decode_mode(dstInfo.colorType(),
+            dstInfo.alphaType() == kPremul_SkAlphaType);
+    config.output.u.RGBA.rgba = (uint8_t*) dst;
+    config.output.u.RGBA.stride = (int) rowBytes;
+    config.output.u.RGBA.size = dstInfo.getSafeSize(rowBytes);
+    config.output.is_external_memory = 1;
+
+    SkAutoTCallVProc<WebPIDecoder, WebPIDelete> idec(WebPIDecode(NULL, 0, &config));
+    if (!idec) {
+        return kInvalidInput;
+    }
+
+    SkAutoMalloc storage(BUFFER_SIZE);
+    uint8_t* buffer = static_cast<uint8_t*>(storage.get());
+    while (true) {
+        const size_t bytesRead = stream()->read(buffer, BUFFER_SIZE);
+        if (0 == bytesRead) {
+            // FIXME: Maybe this is an incomplete image? How to decide? Based
+            // on the number of rows decoded? We can know the number of rows
+            // decoded using WebPIDecGetRGB.
+            return kInvalidInput;
+        }
+
+        switch (WebPIAppend(idec, buffer, bytesRead)) {
+            case VP8_STATUS_OK:
+                return kSuccess;
+            case VP8_STATUS_SUSPENDED:
+                // Break out of the switch statement. Continue the loop.
+                break;
+            default:
+                return kInvalidInput;
+        }
+    }
+}
+
+SkWebpCodec::SkWebpCodec(const SkImageInfo& info, SkStream* stream)
+    : INHERITED(info, stream) {}
diff --git a/src/codec/SkWebpCodec.h b/src/codec/SkWebpCodec.h
new file mode 100644
index 0000000..9ea6a94
--- /dev/null
+++ b/src/codec/SkWebpCodec.h
@@ -0,0 +1,38 @@
+/*
+ * 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 SkWebpCodec_DEFINED
+#define SkWebpCodec_DEFINED
+
+#include "SkCodec.h"
+#include "SkEncodedFormat.h"
+#include "SkImageInfo.h"
+#include "SkTypes.h"
+
+class SkStream;
+
+class SkWebpCodec final : public SkCodec {
+public:
+    // Assumes IsWebp was called and returned true.
+    static SkCodec* NewFromStream(SkStream*);
+    static bool IsWebp(SkStream*);
+protected:
+    Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*)
+            override;
+    SkEncodedFormat onGetEncodedFormat() const override { return kWEBP_SkEncodedFormat; }
+
+    bool onReallyHasAlpha() const override {
+        return this->getInfo().alphaType() != kOpaque_SkAlphaType;
+    }
+
+    SkISize onGetScaledDimensions(float desiredScale) const override;
+private:
+    SkWebpCodec(const SkImageInfo&, SkStream*);
+
+    typedef SkCodec INHERITED;
+};
+#endif // SkWebpCodec_DEFINED
diff --git a/tests/CodexTest.cpp b/tests/CodexTest.cpp
index 080e6e5..5c96990 100644
--- a/tests/CodexTest.cpp
+++ b/tests/CodexTest.cpp
@@ -89,6 +89,11 @@
     // WBMP
     check(r, "mandrill.wbmp", SkISize::Make(512, 512), false);
 
+    // WEBP
+    check(r, "baby_tux.webp", SkISize::Make(386, 395), false);
+    check(r, "color_wheel.webp", SkISize::Make(128, 128), false);
+    check(r, "yellow_rose.webp", SkISize::Make(400, 301), false);
+
     // BMP
     check(r, "randPixels.bmp", SkISize::Make(8, 8), false);