Use demux API in SkWebpCodec

* Necessary to read ICC profiles
* Will be necessary to implement animation
* Requires consolidated data with length

Doesn't affect decode performance (thought I believe
our performance tests only cover SkStreams with memory
bases).

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2311793004

Review-Url: https://codereview.chromium.org/2311793004
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
index c28d077..c12b1df 100644
--- a/src/codec/SkWebpCodec.cpp
+++ b/src/codec/SkWebpCodec.cpp
@@ -7,6 +7,7 @@
 
 #include "SkCodecPriv.h"
 #include "SkWebpCodec.h"
+#include "SkStreamPriv.h"
 #include "SkTemplates.h"
 
 // A WebP decoder on top of (subset of) libwebp
@@ -36,26 +37,55 @@
 SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
     SkAutoTDelete<SkStream> streamDeleter(stream);
 
-    unsigned char buffer[WEBP_VP8_HEADER_SIZE];
-    SkASSERT(WEBP_VP8_HEADER_SIZE <= SkCodec::MinBufferedBytesNeeded());
+    // Webp demux needs a contiguous data buffer.
+    sk_sp<SkData> data = nullptr;
+    if (stream->getMemoryBase()) {
+        // It is safe to make without copy because we'll hold onto the stream.
+        data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength());
+    } else {
+        data = SkCopyStreamToData(stream);
 
-    const size_t bytesPeeked = stream->peek(buffer, WEBP_VP8_HEADER_SIZE);
-    if (bytesPeeked != WEBP_VP8_HEADER_SIZE) {
-        // Use read + rewind as a backup
-        if (stream->read(buffer, WEBP_VP8_HEADER_SIZE) != WEBP_VP8_HEADER_SIZE
-            || !stream->rewind())
+        // If we are forced to copy the stream to a data, we can go ahead and delete the stream.
+        streamDeleter.reset(nullptr);
+    }
+
+    // It's a little strange that the |demux| will outlive |webpData|, though it needs the
+    // pointer in |webpData| to remain valid.  This works because the pointer remains valid
+    // until the SkData is freed.
+    WebPData webpData = { data->bytes(), data->size() };
+    SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> demux(WebPDemuxPartial(&webpData, nullptr));
+    if (nullptr == demux) {
         return nullptr;
     }
 
-    WebPBitstreamFeatures features;
-    VP8StatusCode status = WebPGetFeatures(buffer, WEBP_VP8_HEADER_SIZE, &features);
-    if (VP8_STATUS_OK != status) {
-        return nullptr; // Invalid WebP file.
+    WebPChunkIterator chunkIterator;
+    SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&chunkIterator);
+    sk_sp<SkColorSpace> colorSpace = nullptr;
+    if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) {
+        colorSpace = SkColorSpace::NewICC(chunkIterator.chunk.bytes, chunkIterator.chunk.size);
     }
 
-    // sanity check for image size that's about to be decoded.
+    if (!colorSpace) {
+        colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
+    }
+
+    // Since we do not yet support animation, we get the |width|, |height|, |color|, and |alpha|
+    // from the first frame.  It's the only frame we will decode.
+    //
+    // TODO:
+    // When we support animation, we'll want to report the canvas width and canvas height instead.
+    // We can get these from the |demux| directly.
+    // What |color| and |alpha| will we want to report though?  WebP allows different frames
+    // to be encoded in different ways, making the encoded format difficult to describe.
+    WebPIterator frame;
+    SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
+    if (!WebPDemuxGetFrame(demux, 1, &frame)) {
+        return nullptr;
+    }
+
+    // Sanity check for image size that's about to be decoded.
     {
-        const int64_t size = sk_64_mul(features.width, features.height);
+        const int64_t size = sk_64_mul(frame.width, frame.height);
         if (!sk_64_isS32(size)) {
             return nullptr;
         }
@@ -65,6 +95,16 @@
         }
     }
 
+    // TODO:
+    // The only reason we actually need to call WebPGetFeatures() is to get the |features.format|.
+    // This call actually re-reads the frame header.  Should we suggest that libwebp expose
+    // the format on the |frame|?
+    WebPBitstreamFeatures features;
+    VP8StatusCode status = WebPGetFeatures(frame.fragment.bytes, frame.fragment.size, &features);
+    if (VP8_STATUS_OK != status) {
+        return nullptr;
+    }
+
     SkEncodedInfo::Color color;
     SkEncodedInfo::Alpha alpha;
     switch (features.format) {
@@ -98,30 +138,9 @@
             return nullptr;
     }
 
-    // FIXME (msarett):
-    // Temporary strategy for getting ICC profiles from webps.  Once the incremental decoding
-    // API lands, we will use the WebPDemuxer to manage the entire decode.
-    sk_sp<SkColorSpace> colorSpace = nullptr;
-    const void* memory = stream->getMemoryBase();
-    if (memory) {
-        WebPData data = { (const uint8_t*) memory, stream->getLength() };
-        WebPDemuxState state;
-        SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> demux(WebPDemuxPartial(&data, &state));
-
-        WebPChunkIterator chunkIterator;
-        SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&chunkIterator);
-        if (demux && WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) {
-            colorSpace = SkColorSpace::NewICC(chunkIterator.chunk.bytes, chunkIterator.chunk.size);
-        }
-    }
-
-    if (!colorSpace) {
-        colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
-    }
-
     SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8);
-    return new SkWebpCodec(features.width, features.height, info, colorSpace,
-                           streamDeleter.release());
+    return new SkWebpCodec(features.width, features.height, info, std::move(colorSpace),
+                           streamDeleter.release(), demux.release(), std::move(data));
 }
 
 // This version is slightly different from SkCodecPriv's version of conversion_possible. It
@@ -158,7 +177,6 @@
             && dim.height() >= 1 && dim.height() <= info.height();
 }
 
-
 static WEBP_CSP_MODE webp_decode_mode(SkColorType ct, bool premultiply) {
     switch (ct) {
         case kBGRA_8888_SkColorType:
@@ -172,11 +190,6 @@
     }
 }
 
-// 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;
-
 bool SkWebpCodec::onGetValidSubset(SkIRect* desiredSubset) const {
     if (!desiredSubset) {
         return false;
@@ -263,33 +276,31 @@
     config.output.u.RGBA.size = dstInfo.getSafeSize(rowBytes);
     config.output.is_external_memory = 1;
 
+    WebPIterator frame;
+    SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
+    // If this succeeded in NewFromStream(), it should succeed again here.
+    SkAssertResult(WebPDemuxGetFrame(fDemux, 1, &frame));
+
     SkAutoTCallVProc<WebPIDecoder, WebPIDelete> idec(WebPIDecode(nullptr, 0, &config));
     if (!idec) {
         return kInvalidInput;
     }
 
-    SkAutoTMalloc<uint8_t> storage(BUFFER_SIZE);
-    uint8_t* buffer = storage.get();
-    while (true) {
-        const size_t bytesRead = stream()->read(buffer, BUFFER_SIZE);
-        if (0 == bytesRead) {
-            WebPIDecGetRGB(idec, rowsDecoded, NULL, NULL, NULL);
+    switch (WebPIUpdate(idec, frame.fragment.bytes, frame.fragment.size)) {
+        case VP8_STATUS_OK:
+            return kSuccess;
+        case VP8_STATUS_SUSPENDED:
+            WebPIDecGetRGB(idec, rowsDecoded, nullptr, nullptr, nullptr);
             return kIncompleteInput;
-        }
-
-        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;
-        }
+        default:
+            return kInvalidInput;
     }
 }
 
 SkWebpCodec::SkWebpCodec(int width, int height, const SkEncodedInfo& info,
-                         sk_sp<SkColorSpace> colorSpace, SkStream* stream)
-    : INHERITED(width, height, info, stream, colorSpace)
+                         sk_sp<SkColorSpace> colorSpace, SkStream* stream, WebPDemuxer* demux,
+                         sk_sp<SkData> data)
+    : INHERITED(width, height, info, stream, std::move(colorSpace))
+    , fDemux(demux)
+    , fData(std::move(data))
 {}