QMK uses a graphics format _("Quantum Graphics Format" - QGF)_ specifically for resource-constrained systems.
This format is capable of encoding 1-, 2-, 4-, and 8-bit-per-pixel greyscale- and palette-based images. It also includes RLE for pixel data for some basic compression.
All integer values are in little-endian format.
The QGF is defined in terms of _blocks_ -- each _block_ contains a _header_ and an optional _blob_ of data. The _header_ contains the block's _typeid_, and the length of the _blob_ that follows. Each block type is denoted by a different _typeid_ has its own block definition below. All blocks are defined as packed structs, containing zero padding between fields.
The general structure of the file is:
* _Graphics descriptor block_
* _Frame offset block_
* Repeating list of frames:
* _Frame descriptor block_
* _Frame palette block_ (optional, depending on frame format)
* _Frame delta block_ (optional, depending on delta flag)
* _Frame data block_
Different frames within the file should be considered "isolated" and may have their own image format and/or palette.
The _length_ describes the number of octets in the data following the block header -- a block header may specify a _length_ of `0` if no blob is specified.
This block must be located at the start of the file contents, and can exist a maximum of once in an entire QGF file. It is always followed by the _frame offset block_.
// STATIC_ASSERT(sizeof(qgf_graphics_descriptor_v1_t) == (sizeof(qgf_block_header_v1_t) + 18), "qgf_graphics_descriptor_v1_t must be 23 bytes in v1 of QGF");
This block denotes the offsets within the file to each frame's _frame descriptor block_, relative to the start of the file. The _frame offset block_ always immediately follows the _graphics descriptor block_. The contents of this block are an array of U32's, with one entry for each frame.
Duplicate frame offsets in this block are allowed, if a certain frame is to be shown multiple times during animation.
If this frame is grayscale, the _frame descriptor block_ (or _frame delta block_ if flags denote a delta frame) is immediately followed by this frame's corresponding _frame data block_.
If the frame uses an indexed palette, the _frame descriptor block_ (or _frame delta block_ if flags denote a delta frame) is immediately followed by this frame's corresponding _frame palette block_.
Frame format possible values:
*`0x00`: 1bpp grayscale, no palette, `0` = black, `1` = white, LSb first pixel
*`0x01`: 2bpp grayscale, no palette, `0` = black, `3` = white, linear interpolation of brightness, LSb first pixel
*`0x02`: 4bpp grayscale, no palette, `0` = black, `15` = white, linear interpolation of brightness, LSb first pixel
*`0x03`: 8bpp grayscale, no palette, `0` = black, `255` = white, linear interpolation of brightness, LSb first pixel
*`0x04`: 1bpp indexed palette, 2 colors, LSb first pixel
*`0x05`: 2bpp indexed palette, 4 colors, LSb first pixel
*`0x06`: 4bpp indexed palette, 16 colors, LSb first pixel
*`0x07`: 8bpp indexed palette, 256 colors, LSb first pixel
Frame flags is a bitmask with the following format:
*`[1]` -- Delta: Signifies that the current frame is a delta frame, which specifies only a sub-image. The _frame delta block_ follows the _frame palette block_ if the image format specifies a palette, otherwise it directly follows the _frame descriptor block_.
*`[0]` -- Transparency: The transparent palette index in the _blob_ is considered valid and should be used when considering which pixels should be transparent during rendering this frame, if possible.
This block describes the palette used for the frame. The _blob_ contains an array of palette entries -- one palette entry is present for each color used -- each palette entry is in QMK HSV888 format:
This block describes the data associated with the frame. The _blob_ contains an array of bytes containing the data corresponding to the frame's image format: