-
Notifications
You must be signed in to change notification settings - Fork 143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Need public API to seek to another APNG frame #510
Comments
Let me also be transparent that I am not yet 100% sure about the motivation here. The immediate motivation is that the
I am working with the Skia team to better understand the motivation for supporting seeking to an earlier frame. One hypothesis is that this is needed when decoding the current frame depends on first restoring one of previous frames (e.g. because one or more earlier frames use |
Seeking back to the first frame makes sense to me, but I'd be curious what use seeking to a different frame would be? I'd expect that you wouldn't be able to reconstruct it without potentially needing to know all previous frames. Another question I have looking at the current API is that we don't seem to distinguish between the static image and the first frame of the animation, which may or may not be the same. Might be worth separating them like image-webp does. As far as API changes, it might be worth investigating whether to unconditionally require |
The comparison to As an aside: with regards to |
For now I've implemented seeking to an earlier frames by 1) rewinding the input stream to the beginning and recreating
Yes. FWIW this is not blocking - the client can recognize the situation (
Right - see how:
(Note that the code above is codec-independent.) But, I am still not sure in what scenario a previous/required animation frame would be unavailable. If required frames may be discarded (out-of-viewport? low-on-memory?) then maybe it should be okay to restart the animation from the first frame? I dunno... |
This commit tweaks `StreamingDecoder::update2` so that the `image_data_callback` is optional, and then avoids talking to `self.inflater` when it is `None`. This results in the following performance gains: ``` next_frame_info/tests/animated/basic_f20.png: 18 skips time: [46.612 µs 46.715 µs 46.894 µs] change: [-93.059% -93.045% -93.031%] (p = 0.00 < 0.05) Performance has improved. ``` This commit is mainly motivated by https://crbug.com/371060427 and image-rs#510.
I think implementing seek-to-frame-N as rewind-and-skip-N-1-frames can be made a bit faster if we won't try to decompress the image data as we skip over the frames. FWW I've prototyped this at a268b03...anforowicz:image-png:image-data-sink-fn. |
@anforowicz I've implemented a similar compression skipping for GIF. Maybe use similar approach in PNG if possible? |
This commit adds an internal `DecodeOptions::skip_frame_decoding` flag and sets it to true from `ReadDecoder::finish_decoding_image_data`. This results in the following performance gains when using the `next_frame_info` public API to skip frames: `change: [-94.186% -94.170% -94.157%] (p = 0.00 < 0.05)` This commit is mainly motivated by https://crbug.com/371060427 and image-rs#510. Hat tip to @kornelski for suggesting this approach in image-rs#510 (comment)
Thanks for the pointer @kornelski. It's interesting how |
This commit adds an internal `DecodeOptions::skip_frame_decoding` flag and sets it to true from `ReadDecoder::finish_decoding_image_data`. This results in the following performance gains when using the `next_frame_info` public API to skip frames: `change: [-94.186% -94.170% -94.157%] (p = 0.00 < 0.05)` This commit is mainly motivated by https://crbug.com/371060427 and image-rs#510. Hat tip to @kornelski for suggesting this approach in image-rs#510 (comment)
This commit adds an internal `DecodeOptions::skip_frame_decoding` flag and sets it to true from `ReadDecoder::finish_decoding_image_data`. This results in the following performance gains when using the `next_frame_info` public API to skip frames: `change: [-94.186% -94.170% -94.157%] (p = 0.00 < 0.05)` This commit is mainly motivated by https://crbug.com/371060427 and image-rs#510. Hat tip to @kornelski for suggesting this approach in image-rs#510 (comment)
This commit adds an internal `DecodeOptions::skip_frame_decoding` flag and sets it to true from `ReadDecoder::finish_decoding_image_data`. This results in the following performance gains when using the `next_frame_info` public API to skip frames: `change: [-94.186% -94.170% -94.157%] (p = 0.00 < 0.05)` This commit is mainly motivated by https://crbug.com/371060427 and image-rs#510. Hat tip to @kornelski for suggesting this approach in image-rs#510 (comment)
Disclaimer
This issue is based on my current, evolving understanding of the requirements that Chromium and Skia architectures impose upon an underlying codec implementation. I am opening this issue early to facilitate a transparent discussion, but before accepting the new APIs we should definitely require that I first finish prototyping the APNG-focused part of the Chromium/Blink/Skia integration (as a proof-of-concept that the new APIs actually work and solve my use case).
Problem 1a: No way to get to next image data
When
Reader.next_interlaced_row
reaches the end of a frame, then it gets "stuck" - subsequent calls will continue returningNone
(which correctly indicates that no more rows in the current frame) and there is no way to proceed to the next frame.Problem 1b: No way to read the next
fcTL
chunkTo successfully decode a frame, one needs to have its
FrameControl
data, butInfo.frame_control
won't be updated until thefcTL
chunk is encountered and parsed. There is no way to askReader
to get the nextFrameControl
metadata.Problem 2: No way to go back and decode an earlier frame
Skia and Blink abstractions allow asking a codec to (again) decode an earlier frame:
Options.fFrameIndex
parameter ofSkCodec::onStartIncrementalDecode
(andonGetPixels
) which can specify an earlier frame.SkWuffsCodec
hereSkWuffsCodec
populates itsfFrames
field incrementally - see here.index
parameter ofblink::ImageDecoder::DecodeFrameBufferAtIndex
andDecode
libpng
-backedPNGImageReader
hereEarly discussion
Initially I tried solving problem 1 by publicly exposing
Reader.read_until_image_data
.One solution to problem 2 would be to (brainstorming quality, please bear with me):
Reader.seek_to_frame(&mut self, frame_index: usize) -> Result<&FrameControl, DecodingError>
method that is hidden behindwhere R: Seek
IDAT
is not part of the animation, then there is no way to seek back toIDAT
.IDAT
orfdAT
chunk and would need to restore the related state inStreamingDecoder
.State::ImageData(...)
orState::ApngSequenceNumber
(this requires remembering and restoringcurrent_chunk
-type
,crc
,remaining
State::U32 { kind: U32ValueKind::Length }
(no need to remember chunk contents; requires seeking to a slightly earlier offset)Reader.read_until_image_data
to store frame offsets if possibleoffset = None
whenR: !Seek
(maybe by introducing a private trait that is implemented forRead + !Seek
and forRead + Seek
)FrameControl
fdAT
/fcTL
sequence number (can add a crate-internal accessor toStreamingDecoder
? or maybe weDecoded::ChunkBegin
should be replaced withDecoded::DataChunkBegin(opaque_state_to_restore)
?)I think that if we have
seek_to_frame
API, then we would still need to makeread_until_image_data
public (to support moving to the next fcTL when the input stream doesn't implementSeek
).I am not sure if there are other, better ways to solve problem 2.
libpng
andStreamingDecoder
use a push model (slices with additional data is pushed into their APIs) andReader
uses a pull model (callingReader
APIs triggersRead.read
from the underlying data stream). In a push model the client is responsible for managing seeking/offsets. But a push model may still have a state that needs to be restored (I am not sure how exactly that works inlibpng
).The text was updated successfully, but these errors were encountered: