As discussed in the Image Decoders document, here's the canonical sequence of API calls to decode an animated image (with N frames):
decode_frame_config) #N returning an
"@end of data"status value.
The final DFC call returns
"@end of data" even if the animation loops.
Decoding a still (non-animated) image is equivalent to N being 1.
These three methods (DIC, DFC, DF) are all coroutines and may return I/O related suspension status values. Those coroutines should be resumed by calling the same method again, before moving to the next one in the sequence.
Encountering an error (such as the input not being valid in that image file format) is sticky (any further calls will return that same error).
These three methods (DIC, DFC, DF) are all also optional in some sense. Referring to the ordered list at the top of this page, any
decode_etc call jumps forward to the next matching method in that canonical sequence, implicitly calling any jumped over method with the same
io_reader but null values for other arguments.
For example, starting with a freshly initialized image decoder, if it's a first party image and you already know its width, height and pixel format (so you can prepare a perfectly sized pixel buffer without having to call DIC or DFC), it is valid to call DF, going straight to the DF #0 stage. This implicitly calls DIC and DFC #0.
Even if you didn't know the width, height and pixel format, it is still valid to call DF with a smaller-dimensioned pixel buffer (which just means the decoded image will be clipped) or with a different pixel format (the decoder will convert).
After that, it is valid to call DFC, going to the DFC #1 stage (or get “@end of data” for still images), which would not make any implicit calls.
After that, it is valid to call DFC, going to the DFC #2 stage, implicitly calling DF #1.
And so on.
For example, starting at the DF #N-1 stage, it is valid to call DF again, which will return “@end of data” because it first implicitly calls DF #N, which returns “@end of data”.
Implicitly calling DF (skipping over the pixel data) is typically much faster than explicitly calling DF (decompressing the pixel data). For example, starting with a freshly initialized image decoder, if you just want to count the number of animation frames, without needing each frame's decoded pixels, just call DFC (and not DF) in a loop until “#end of data”.
It‘s not part of Wuffs’ public API, but Wuffs' standard library's image decoder implementations all have a private
call_sequence field to track this state machine. Broadly speaking, an image decoder starts in an initial state (labeled
0x00). The DIC call moves to state
0x20. Subsequent DFC and DF calls bounce between states
0x20 until the last DFC moves the final state
0x60. These are the bold-labeled states in the left hand column of the diagram below:
The original Google Docs document for that diagram is here.
The italic-labeled states (whose numeric labels all have the
0x10 bit set) in the right hand column are side-track states when metadata is encountered, and are discussed below. If the caller never opts in to seeing metadata (opting in by calling
set_report_metadata) then the decoder never enters these
(base_value | 0x10) states.
0x08 bit is used when calling
restart_frame, discussed below. The low three bits are reserved but currently unused.
The primary purposes of the
call_sequence field is track when we've reached “@end of data” and to reject method calls that occur out of sequence. Such method calls result in the
"#bad call sequence" error status value. For example, a DIC call is illegal after a DIC, DFC or DF call. Similarly, a TMM (
tell_me_more) call is illegal unless the decoder is in a right hand column state.
Image decoding typically runs sequentially: the first frame (#0) is decoded, then the second (#1), then the third (#2), etc. However, decoding can sometimes happen in random access instead of sequential order. Playing a looping animation may require rewinding back to the first frame. Replaying an animation may be able to re-use previousy cached frames at times, so a decoder may be asked to jump ahead. While Wuffs is designed for sequential decoding (as it does not assume that its input byte stream is rewindable or seekable), using Wuffs to implement other graphics APIs can involve random access to animation frames.
restart_frame method supports these usage patterns, setting up the decoder so that it‘s just about to decode the I’th frame configuration (when the
io_reader is also placed at
io_position previously returned by the I'th DFC).
The RF method can only be called when the image configuration has already been decoded. Equivalently, only when the
call_sequence is at least