quantum_painter_qgf.md (9174B)
1 # QMK Graphics Format {#qmk-graphics-format} 2 3 QMK uses a graphics format _("Quantum Graphics Format" - QGF)_ specifically for resource-constrained systems. 4 5 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. 6 7 All integer values are in little-endian format. 8 9 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. 10 11 The general structure of the file is: 12 13 * _Graphics descriptor block_ 14 * _Frame offset block_ 15 * Repeating list of frames: 16 * _Frame descriptor block_ 17 * _Frame palette block_ (optional, depending on frame format) 18 * _Frame delta block_ (optional, depending on delta flag) 19 * _Frame data block_ 20 21 Different frames within the file should be considered "isolated" and may have their own image format and/or palette. 22 23 ## Block Header {#qgf-block-header} 24 25 This block header is present for all blocks, including the graphics descriptor. 26 27 _Block header_ format: 28 29 ```c 30 typedef struct __attribute__((packed)) qgf_block_header_v1_t { 31 uint8_t type_id; // See each respective block type 32 uint8_t neg_type_id; // Negated type ID, used for detecting parsing errors 33 uint24_t length; // 24-bit blob length, allowing for block sizes of a maximum of 16MB 34 } qgf_block_header_v1_t; 35 // STATIC_ASSERT(sizeof(qgf_block_header_v1_t) == 5, "qgf_block_header_v1_t must be 5 bytes in v1 of QGF"); 36 ``` 37 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. 38 39 ## Graphics descriptor block {#qgf-graphics-descriptor} 40 41 * _typeid_ = 0x00 42 * _length_ = 18 43 44 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_. 45 46 _Block_ format: 47 48 ```c 49 typedef struct __attribute__((packed)) qgf_graphics_descriptor_v1_t { 50 qgf_block_header_v1_t header; // = { .type_id = 0x00, .neg_type_id = (~0x00), .length = 18 } 51 uint24_t magic; // constant, equal to 0x464751 ("QGF") 52 uint8_t qgf_version; // constant, equal to 0x01 53 uint32_t total_file_size; // total size of the entire file, starting at offset zero 54 uint32_t neg_total_file_size; // negated value of total_file_size, used for detecting parsing errors 55 uint16_t image_width; // in pixels 56 uint16_t image_height; // in pixels 57 uint16_t frame_count; // minimum of 1 58 } qgf_graphics_descriptor_v1_t; 59 // 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"); 60 ``` 61 62 ## Frame offset block {#qgf-frame-offset-descriptor} 63 64 * _typeid_ = 0x01 65 * _length_ = variable 66 67 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. 68 69 Duplicate frame offsets in this block are allowed, if a certain frame is to be shown multiple times during animation. 70 71 _Block_ format: 72 73 ```c 74 typedef struct __attribute__((packed)) qgf_frame_offsets_v1_t { 75 qgf_block_header_v1_t header; // = { .type_id = 0x01, .neg_type_id = (~0x01), .length = (N * sizeof(uint32_t)) } 76 uint32_t offset[N]; // where 'N' is the number of frames in the file 77 } qgf_frame_offsets_v1_t; 78 ``` 79 80 ## Frame descriptor block {#qgf-frame-descriptor} 81 82 * _typeid_ = 0x02 83 * _length_ = 5 84 85 This block denotes the start of a frame. 86 87 _Block_ format: 88 89 ```c 90 typedef struct __attribute__((packed)) qgf_frame_v1_t { 91 qgf_block_header_v1_t header; // = { .type_id = 0x02, .neg_type_id = (~0x02), .length = 5 } 92 uint8_t format; // Frame format, see below. 93 uint8_t flags; // Frame flags, see below. 94 uint8_t compression_scheme; // Compression scheme, see below. 95 uint8_t transparency_index; // palette index used for transparent pixels (not yet implemented) 96 uint16_t delay; // frame delay time for animations (in units of milliseconds) 97 } qgf_frame_v1_t; 98 // STATIC_ASSERT(sizeof(qgf_frame_v1_t) == (sizeof(qgf_block_header_v1_t) + 6), "qgf_frame_v1_t must be 11 bytes in v1 of QGF"); 99 ``` 100 101 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_. 102 103 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_. 104 105 Frame format possible values: 106 107 * `0x00`: 1bpp grayscale, no palette, `0` = black, `1` = white, LSb first pixel 108 * `0x01`: 2bpp grayscale, no palette, `0` = black, `3` = white, linear interpolation of brightness, LSb first pixel 109 * `0x02`: 4bpp grayscale, no palette, `0` = black, `15` = white, linear interpolation of brightness, LSb first pixel 110 * `0x03`: 8bpp grayscale, no palette, `0` = black, `255` = white, linear interpolation of brightness, LSb first pixel 111 * `0x04`: 1bpp indexed palette, 2 colors, LSb first pixel 112 * `0x05`: 2bpp indexed palette, 4 colors, LSb first pixel 113 * `0x06`: 4bpp indexed palette, 16 colors, LSb first pixel 114 * `0x07`: 8bpp indexed palette, 256 colors, LSb first pixel 115 116 Frame flags is a bitmask with the following format: 117 118 | `bit 7` | `bit 6` | `bit 5` | `bit 4` | `bit 3` | `bit 2` | `bit 1` | `bit 0` | 119 |---------|---------|---------|---------|---------|---------|---------|--------------| 120 | - | - | - | - | - | - | Delta | Transparency | 121 122 * `[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_. 123 * `[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. 124 125 Compression scheme possible values: 126 127 * `0x00`: No compression 128 * `0x01`: [QMK RLE](quantum_painter_rle) 129 130 ## Frame palette block {#qgf-frame-palette-descriptor} 131 132 * _typeid_ = 0x03 133 * _length_ = variable 134 135 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: 136 137 ```c 138 typedef struct __attribute__((packed)) qgf_palette_v1_t { 139 qgf_block_header_v1_t header; // = { .type_id = 0x03, .neg_type_id = (~0x03), .length = (N * 3 * sizeof(uint8_t)) } 140 struct { // container for a single HSV palette entry 141 uint8_t h; // hue component: `[0,360)` degrees is mapped to `[0,255]` uint8_t. 142 uint8_t s; // saturation component: `[0,1]` is mapped to `[0,255]` uint8_t. 143 uint8_t v; // value component: `[0,1]` is mapped to `[0,255]` uint8_t. 144 } hsv[N]; // N * hsv, where N is the number of palette entries depending on the frame format in the descriptor 145 } qgf_palette_v1_t; 146 ``` 147 148 ## Frame delta block {#qgf-frame-delta-descriptor} 149 150 * _typeid_ = 0x04 151 * _length_ = 8 152 153 This block describes where the delta frame should be drawn, with respect to the top left location of the image. 154 155 ```c 156 typedef struct __attribute__((packed)) qgf_delta_v1_t { 157 qgf_block_header_v1_t header; // = { .type_id = 0x04, .neg_type_id = (~0x04), .length = 8 } 158 uint16_t left; // The left pixel location to draw the delta image 159 uint16_t top; // The top pixel location to draw the delta image 160 uint16_t right; // The right pixel location to to draw the delta image 161 uint16_t bottom; // The bottom pixel location to to draw the delta image 162 } qgf_delta_v1_t; 163 // STATIC_ASSERT(sizeof(qgf_delta_v1_t) == 13, "qgf_delta_v1_t must be 13 bytes in v1 of QGF"); 164 ``` 165 166 ## Frame data block {#qgf-frame-data-descriptor} 167 168 * _typeid_ = 0x05 169 * _length_ = variable 170 171 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: 172 173 ```c 174 typedef struct __attribute__((packed)) qgf_data_v1_t { 175 qgf_block_header_v1_t header; // = { .type_id = 0x05, .neg_type_id = (~0x05), .length = N } 176 uint8_t data[N]; // N data octets 177 } qgf_data_v1_t; 178 ```