Image Decoders

Regardless of the file format (GIF, JPEG, PNG, etc), every Wuffs image decoder has a similar API. This document gives wuffs_gif__decoder__foo_bar examples, but the gif could be replaced by jpeg, png, etc.

Some image formats (GIF) are animated, consisting of an image header and then N frames. The image header gives e.g. the overall image's width and height. Each frame consists of a frame header (e.g. frame rectangle bounds, display duration) and a payload (the pixels). Headers are also known as configurations.

Wuffs image decoders will reject an image dimension (width or height) that is above 0xFF_FFFF = 16_777215 pixels, even if the underlying image file format permits more. This limit simplifies handling potential overflow. For example, width * bytes_per_pixel will not overflow an i32 and width * height * bytes_per_pixel will not overflow an i64. Similarly, converting a pixel width from integers to 26.6 fixed point units will not overflow an i32.

In general, there is one wuffs_base__image_config and then N pairs of (wuffs_base__frame_config, frame payload). Non-animated (still) image formats are treated the same way: that their N is 1 and their single frame's bounds equals the overall image bounds.

To decode everything (without knowing N in advance) sequentially:

wuffs_base__io_buffer src = etc;
wuffs_gif__decoder* dec = etc;

wuffs_base__image_config ic;
wuffs_base__status status = wuffs_gif__decoder__decode_image_config(dec, &ic, &src);
// Error checking (inspecting the status variable) is not shown, for brevity.
// See the example programs, listed below, for how to handle errors.

// Allocate the pixel buffer, based on ic's width and height, etc.
wuffs_base__pixel_buffer pb = etc;

while (true) {
  wuffs_base__frame_config fc;
  status = wuffs_gif__decoder__decode_frame_config(dec, &fc, &src);
  if (status.repr == wuffs_base__note__end_of_data) {
    break;
  }
  // Ditto re error checking.

  status = wuffs_gif__decoder__decode_frame(dec, &pb, &etc);
  // Ditto re error checking.
}

The second argument to each decode_xxx method (&ic, &fc or &pb), is the destination data structure to store the decoded information.

For random (instead of sequential) access to an image‘s frames, call wuffs_gif__decoder__restart_frame(dec, i, io_pos) to prepare to decode the i’th frame. Essentially, it restores the state to be at the top of the while loop above. The wuffs_base__io_buffer‘s reader position will also need to be set to the io_pos position in the source data stream. The position for the i’th frame is calculated by the i'th decode_frame_config call and saved in the frame_config. You can only call restart_frame after decode_image_config is called, explicitly or implicitly (see below), as decoding a single frame might require for-all-frames information like the overall image dimensions and the global palette.

All of those decode_xxx calls are optional. For example, if decode_image_config is not called, then the first decode_frame_config call will implicitly parse and verify the image header, before parsing the first frame‘s header. Similarly, you can call only decode_frame N times, without calling decode_image_config or decode_frame_config, if you already know metadata like N and each frame’s rectangle bounds by some other means (e.g. this is a first party, statically known image). The Image Decoders Call Sequence document has more implementation information.

Specifically, starting with an unknown (but re-windable) animated image, if you want to just find N (i.e. count the number of frames), you can loop calling only the decode_frame_config method and avoid calling the more expensive decode_frame method. For GIF, in terms of the underlying wire format, this will skip over (instead of decompress) the LZW-encoded pixel data, which is faster.

Those decode_xxx methods are also suspendible coroutines. They will return early (with a status code that is_suspendible and therefore isn‘t is_complete) if there isn’t enough source data to complete the operation: an incremental decode. Calling decode_xxx again with additional source data will resume the previous operation instead of starting a new one. Calling decode_yyy whilst decode_xxx is suspended will result in an error.

Once an error is encountered, whether from invalid source data or from a programming error, such as calling decode_yyy while suspended in decode_xxx, all subsequent calls will be no-ops that return an error. To reset the decoder into something that does productive work, re-initialize and then, in order to be able to call restart_frame, call decode_image_config. The io_buffer and its associated stream will also need to be rewound.

Metadata

The Metadata document has more API information, applicable to Wuffs' decoders but not specific to Wuffs' image decoders.

API Listing

In Wuffs syntax, the base.image_decoder methods are:

  • decode_frame?(dst: ptr pixel_buffer, src: io_reader, blend: pixel_blend, workbuf: slice u8, opts: nptr decode_frame_options)
  • decode_frame_config?(dst: nptr frame_config, src: io_reader)
  • decode_image_config?(dst: nptr image_config, src: io_reader)
  • frame_dirty_rect() rect_ie_u32
  • get_quirk(key: u32) u64
  • num_animation_loops() u32
  • num_decoded_frame_configs() u64
  • num_decoded_frames() u64
  • restart_frame!(index: u64, io_position: u64) status
  • set_quirk!(key: u32, value: u64) status
  • set_report_metadata!(fourcc: u32, report: bool)
  • tell_me_more?(dst: io_writer, minfo: nptr more_information, src: io_reader)
  • workbuf_len() range_ii_u64

Implementations

Examples

Examples in other repositories:

  • Skia, a 2-D graphics library used by the Android operating system and the Chromium web browser.

Quirks

Related Documentation

See also the general remarks on Wuffs' standard library.