diff options
author | 2016-09-07 11:23:28 -0700 | |
---|---|---|
committer | 2016-09-07 11:23:28 -0700 | |
commit | ff2a6c8a70d5f7e3be99ef41c75412878785edcb (patch) | |
tree | 2e2e288a925931389d40adf2884a05537d20d549 | |
parent | ffac5c4aae18fc706e4077763c190a89c8507fb0 (diff) |
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
-rw-r--r-- | src/codec/SkCodec.cpp | 4 | ||||
-rw-r--r-- | src/codec/SkWebpCodec.cpp | 133 | ||||
-rw-r--r-- | src/codec/SkWebpCodec.h | 13 | ||||
-rw-r--r-- | tests/CodecTest.cpp | 2 |
4 files changed, 87 insertions, 65 deletions
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp index 3192a6f4be..2372f4ce5b 100644 --- a/src/codec/SkCodec.cpp +++ b/src/codec/SkCodec.cpp @@ -142,8 +142,8 @@ SkCodec::~SkCodec() {} bool SkCodec::rewindIfNeeded() { if (!fStream) { - // Some codecs do not have a stream, but they hold others that do. They - // must handle rewinding themselves. + // Some codecs do not have a stream. They may hold onto their own data or another codec. + // They must handle rewinding themselves. return true; } diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp index c28d077bb3..c12b1df5ed 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)) {} diff --git a/src/codec/SkWebpCodec.h b/src/codec/SkWebpCodec.h index 6fda7c2824..b9c493f204 100644 --- a/src/codec/SkWebpCodec.h +++ b/src/codec/SkWebpCodec.h @@ -15,6 +15,10 @@ #include "SkTypes.h" class SkStream; +extern "C" { + struct WebPDemuxer; + void WebPDemuxDelete(WebPDemuxer* dmux); +} static const size_t WEBP_VP8_HEADER_SIZE = 30; @@ -34,7 +38,14 @@ protected: bool onGetValidSubset(SkIRect* /* desiredSubset */) const override; private: - SkWebpCodec(int width, int height, const SkEncodedInfo&, sk_sp<SkColorSpace>, SkStream*); + SkWebpCodec(int width, int height, const SkEncodedInfo&, sk_sp<SkColorSpace>, SkStream*, + WebPDemuxer*, sk_sp<SkData>); + + SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> fDemux; + + // fDemux has a pointer into this data. + // This should not be freed until the decode is completed. + sk_sp<SkData> fData; typedef SkCodec INHERITED; }; diff --git a/tests/CodecTest.cpp b/tests/CodecTest.cpp index fcbfadd068..9c60be4b6b 100644 --- a/tests/CodecTest.cpp +++ b/tests/CodecTest.cpp @@ -879,7 +879,7 @@ public: return fStream.rewind(); } bool isAtEnd() const override { - return false; + return fStream.isAtEnd(); } private: SkMemoryStream fStream; |