Pause Wuffs' getFrameCount in incremental decode
Bug: skia:8235
Bug: skia:8750
Change-Id: I7b8a6a1aba38ba2f16bd184253fbef2a37000cef
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/223016
Reviewed-by: Leon Scroggins <scroggo@google.com>
Commit-Queue: Leon Scroggins <scroggo@google.com>
diff --git a/src/codec/SkWuffsCodec.cpp b/src/codec/SkWuffsCodec.cpp
index 57704f6..72120a3 100644
--- a/src/codec/SkWuffsCodec.cpp
+++ b/src/codec/SkWuffsCodec.cpp
@@ -316,6 +316,9 @@
void* dst,
size_t rowBytes,
const SkCodec::Options& options) {
+ if (!dst) {
+ return SkCodec::kInvalidParameters;
+ }
if (options.fSubset) {
return SkCodec::kUnimplemented;
}
@@ -481,7 +484,37 @@
}
int SkWuffsCodec::onGetFrameCount() {
- if (!fFramesComplete) {
+ // It is valid, in terms of the SkCodec API, to call SkCodec::getFrameCount
+ // while in an incremental decode (after onStartIncrementalDecode returns
+ // and before onIncrementalDecode returns kSuccess).
+ //
+ // We should not advance the SkWuffsCodec' stream while doing so, even
+ // though other SkCodec implementations can return increasing values from
+ // onGetFrameCount when given more data. If we tried to do so, the
+ // subsequent resume of the incremental decode would continue reading from
+ // a different position in the I/O stream, leading to an incorrect error.
+ //
+ // Other SkCodec implementations can move the stream forward during
+ // onGetFrameCount because they assume that the stream is rewindable /
+ // seekable. For example, an alternative GIF implementation may choose to
+ // store, for each frame walked past when merely counting the number of
+ // frames, the I/O position of each of the frame's GIF data blocks. (A GIF
+ // frame's compressed data can have multiple data blocks, each at most 255
+ // bytes in length). Obviously, this can require O(numberOfFrames) extra
+ // memory to store these I/O positions. The constant factor is small, but
+ // it's still O(N), not O(1).
+ //
+ // Wuffs and SkWuffsCodec tries to minimize relying on the rewindable /
+ // seekable assumption. By design, Wuffs per se aims for O(1) memory use
+ // (after any pixel buffers are allocated) instead of O(N), and its I/O
+ // type, wuffs_base__io_buffer, is not necessarily rewindable or seekable.
+ //
+ // The Wuffs API provides a limited, optional form of seeking, to the start
+ // of an animation frame's data, but does not provide arbitrary save and
+ // load of its internal state whilst in the middle of an animation frame.
+ bool incrementalDecodeIsInProgress = fIncrDecDst != nullptr;
+
+ if (!fFramesComplete && !incrementalDecodeIsInProgress) {
this->readFrames();
this->updateNumFullyReceivedFrames();
}
diff --git a/tests/CodecPartialTest.cpp b/tests/CodecPartialTest.cpp
index dc3508b..0ff5435 100644
--- a/tests/CodecPartialTest.cpp
+++ b/tests/CodecPartialTest.cpp
@@ -101,9 +101,8 @@
while (true) {
// This imitates how Chromium calls getFrameCount before resuming a decode.
- // Without this line, the test passes. With it, it fails when skia_use_wuffs
- // is true.
partialCodec->getFrameCount();
+
const SkCodec::Result result = partialCodec->incrementalDecode();
if (result == SkCodec::kSuccess) {