| Index: src/codec/SkWebpCodec.cpp
|
| diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
|
| index c28d077bb329826750ebca4bb92ac2f005e2ac02..c12b1df5edf955d547aad34892d441544528f485 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 @@ bool SkWebpCodec::IsWebp(const void* buf, size_t bytesRead) {
|
| 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);
|
| + }
|
| +
|
| + 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.
|
| + // 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 @@ SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
|
| }
|
| }
|
|
|
| + // 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 @@ SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
|
| 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 @@ bool SkWebpCodec::onDimensionsSupported(const SkISize& dim) {
|
| && 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 @@ static WEBP_CSP_MODE webp_decode_mode(SkColorType ct, bool premultiply) {
|
| }
|
| }
|
|
|
| -// 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 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
|
| 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))
|
| {}
|
|
|