qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

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 ```