qmk_firmware

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

qgf.c (11598B)


      1 // Copyright 2021 Nick Brassel (@tzarc)
      2 // SPDX-License-Identifier: GPL-2.0-or-later
      3 
      4 // Quantum Graphics File "QGF" File Format.
      5 // See https://docs.qmk.fm/#/quantum_painter_qgf for more information.
      6 
      7 #include "qgf.h"
      8 #include "qp_draw.h"
      9 
     10 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     11 // QGF API
     12 
     13 bool qgf_validate_block_header(qgf_block_header_v1_t *desc, uint8_t expected_typeid, int32_t expected_length) {
     14     if (desc->type_id != expected_typeid || desc->neg_type_id != ((~expected_typeid) & 0xFF)) {
     15         qp_dprintf("Failed to validate header, expected typeid 0x%02X, was 0x%02X, expected negated typeid 0x%02X, was 0x%02X\n", (int)expected_typeid, (int)desc->type_id, (int)((~desc->type_id) & 0xFF), (int)desc->neg_type_id);
     16         return false;
     17     }
     18 
     19     if (expected_length >= 0 && desc->length != expected_length) {
     20         qp_dprintf("Failed to validate header (typeid 0x%02X), expected length %d, was %d\n", (int)desc->type_id, (int)expected_length, (int)desc->length);
     21         return false;
     22     }
     23 
     24     return true;
     25 }
     26 
     27 bool qgf_parse_format(qp_image_format_t format, uint8_t *bpp, bool *has_palette, bool *is_panel_native) {
     28     // clang-format off
     29     static const struct PACKED {
     30         uint8_t bpp;
     31         bool    has_palette;
     32         bool is_panel_native;
     33     } formats[] = {
     34         [GRAYSCALE_1BPP] = {.bpp = 1, .has_palette = false, .is_panel_native = false},
     35         [GRAYSCALE_2BPP] = {.bpp = 2, .has_palette = false, .is_panel_native = false},
     36         [GRAYSCALE_4BPP] = {.bpp = 4, .has_palette = false, .is_panel_native = false},
     37         [GRAYSCALE_8BPP] = {.bpp = 8, .has_palette = false, .is_panel_native = false},
     38         [PALETTE_1BPP] = {.bpp = 1, .has_palette = true, .is_panel_native = false},
     39         [PALETTE_2BPP] = {.bpp = 2, .has_palette = true, .is_panel_native = false},
     40         [PALETTE_4BPP] = {.bpp = 4, .has_palette = true, .is_panel_native = false},
     41         [PALETTE_8BPP] = {.bpp = 8, .has_palette = true, .is_panel_native = false},
     42         [RGB565_16BPP] = {.bpp = 16, .has_palette = false, .is_panel_native = true},
     43         [RGB888_24BPP] = {.bpp = 24, .has_palette = false, .is_panel_native = true},
     44     };
     45     // clang-format on
     46 
     47     // Copy out the required info
     48     if (format > RGB888_24BPP) {
     49         qp_dprintf("Failed to parse frame_descriptor, invalid format 0x%02X\n", (int)format);
     50         return false;
     51     }
     52 
     53     // Copy out the required info
     54     if (bpp) {
     55         *bpp = formats[format].bpp;
     56     }
     57     if (has_palette) {
     58         *has_palette = formats[format].has_palette;
     59     }
     60     if (is_panel_native) {
     61         *is_panel_native = formats[format].is_panel_native;
     62     }
     63 
     64     return true;
     65 }
     66 
     67 bool qgf_parse_frame_descriptor(qgf_frame_v1_t *frame_descriptor, uint8_t *bpp, bool *has_palette, bool *is_panel_native, bool *is_delta, painter_compression_t *compression_scheme, uint16_t *delay) {
     68     // Decode the format
     69     qgf_parse_format(frame_descriptor->format, bpp, has_palette, is_panel_native);
     70 
     71     // Copy out the required info
     72     if (is_delta) {
     73         *is_delta = (frame_descriptor->flags & QGF_FRAME_FLAG_DELTA) == QGF_FRAME_FLAG_DELTA;
     74     }
     75     if (compression_scheme) {
     76         *compression_scheme = frame_descriptor->compression_scheme;
     77     }
     78     if (delay) {
     79         *delay = frame_descriptor->delay;
     80     }
     81 
     82     return true;
     83 }
     84 
     85 bool qgf_read_graphics_descriptor(qp_stream_t *stream, uint16_t *image_width, uint16_t *image_height, uint16_t *frame_count, uint32_t *total_bytes) {
     86     // Seek to the start
     87     qp_stream_setpos(stream, 0);
     88 
     89     // Read and validate the graphics descriptor
     90     qgf_graphics_descriptor_v1_t graphics_descriptor;
     91     if (qp_stream_read(&graphics_descriptor, sizeof(qgf_graphics_descriptor_v1_t), 1, stream) != 1) {
     92         qp_dprintf("Failed to read graphics_descriptor, expected length was not %d\n", (int)sizeof(qgf_graphics_descriptor_v1_t));
     93         return false;
     94     }
     95 
     96     // Make sure this block is valid
     97     if (!qgf_validate_block_header(&graphics_descriptor.header, QGF_GRAPHICS_DESCRIPTOR_TYPEID, (sizeof(qgf_graphics_descriptor_v1_t) - sizeof(qgf_block_header_v1_t)))) {
     98         return false;
     99     }
    100 
    101     // Make sure the magic and version are correct
    102     if (graphics_descriptor.magic != QGF_MAGIC || graphics_descriptor.qgf_version != 0x01) {
    103         qp_dprintf("Failed to validate graphics_descriptor, expected magic 0x%06X was 0x%06X, expected version = 0x%02X was 0x%02X\n", (int)QGF_MAGIC, (int)graphics_descriptor.magic, (int)0x01, (int)graphics_descriptor.qgf_version);
    104         return false;
    105     }
    106 
    107     // Make sure the file length is valid
    108     if (graphics_descriptor.neg_total_file_size != ~graphics_descriptor.total_file_size) {
    109         qp_dprintf("Failed to validate graphics_descriptor, expected negated length 0x%08X was 0x%08X\n", (int)(~graphics_descriptor.total_file_size), (int)graphics_descriptor.neg_total_file_size);
    110         return false;
    111     }
    112 
    113     // Copy out the required info
    114     if (image_width) {
    115         *image_width = graphics_descriptor.image_width;
    116     }
    117     if (image_height) {
    118         *image_height = graphics_descriptor.image_height;
    119     }
    120     if (frame_count) {
    121         *frame_count = graphics_descriptor.frame_count;
    122     }
    123     if (total_bytes) {
    124         *total_bytes = graphics_descriptor.total_file_size;
    125     }
    126 
    127     return true;
    128 }
    129 
    130 static bool qgf_read_frame_offset(qp_stream_t *stream, uint16_t frame_number, uint32_t *frame_offset) {
    131     uint16_t frame_count;
    132     if (!qgf_read_graphics_descriptor(stream, NULL, NULL, &frame_count, NULL)) {
    133         return false;
    134     }
    135 
    136     // Read the frame offsets descriptor
    137     qgf_frame_offsets_v1_t frame_offsets;
    138     if (qp_stream_read(&frame_offsets, sizeof(qgf_frame_offsets_v1_t), 1, stream) != 1) {
    139         qp_dprintf("Failed to read frame_offsets, expected length was not %d\n", (int)sizeof(qgf_frame_offsets_v1_t));
    140         return false;
    141     }
    142 
    143     // Make sure this block is valid
    144     if (!qgf_validate_block_header(&frame_offsets.header, QGF_FRAME_OFFSET_DESCRIPTOR_TYPEID, (frame_count * sizeof(uint32_t)))) {
    145         return false;
    146     }
    147 
    148     if (frame_number >= frame_count) {
    149         qp_dprintf("Invalid frame number, was %d but only %d frames in image\n", (int)frame_number, (int)frame_count);
    150         return false;
    151     }
    152 
    153     // Skip the necessary amount of data to get to the requested frame offset
    154     qp_stream_seek(stream, frame_number * sizeof(uint32_t), SEEK_CUR);
    155 
    156     // Read the frame offset
    157     uint32_t offset = 0;
    158     if (qp_stream_read(&offset, sizeof(uint32_t), 1, stream) != 1) {
    159         qp_dprintf("Failed to read frame offset, expected length was not %d\n", (int)sizeof(uint32_t));
    160         return false;
    161     }
    162 
    163     // Copy out the required info
    164     if (frame_offset) {
    165         *frame_offset = offset;
    166     }
    167 
    168     return true;
    169 }
    170 
    171 void qgf_seek_to_frame_descriptor(qp_stream_t *stream, uint16_t frame_number) {
    172     // Read the offset
    173     uint32_t offset = 0;
    174     qgf_read_frame_offset(stream, frame_number, &offset);
    175 
    176     // Move to the offset
    177     qp_stream_setpos(stream, offset);
    178 }
    179 
    180 bool qgf_validate_frame_descriptor(qp_stream_t *stream, uint16_t frame_number, uint8_t *bpp, bool *has_palette, bool *is_panel_native, bool *is_delta) {
    181     // Seek to the correct location
    182     qgf_seek_to_frame_descriptor(stream, frame_number);
    183 
    184     // Read the raw descriptor
    185     qgf_frame_v1_t frame_descriptor;
    186     if (qp_stream_read(&frame_descriptor, sizeof(qgf_frame_v1_t), 1, stream) != 1) {
    187         qp_dprintf("Failed to read frame_descriptor, expected length was not %d\n", (int)sizeof(qgf_frame_v1_t));
    188         return false;
    189     }
    190 
    191     // Make sure this block is valid
    192     if (!qgf_validate_block_header(&frame_descriptor.header, QGF_FRAME_DESCRIPTOR_TYPEID, (sizeof(qgf_frame_v1_t) - sizeof(qgf_block_header_v1_t)))) {
    193         return false;
    194     }
    195 
    196     return qgf_parse_frame_descriptor(&frame_descriptor, bpp, has_palette, is_panel_native, is_delta, NULL, NULL);
    197 }
    198 
    199 bool qgf_validate_palette_descriptor(qp_stream_t *stream, uint16_t frame_number, uint8_t bpp) {
    200     // Read the palette descriptor
    201     qgf_palette_v1_t palette_descriptor;
    202     if (qp_stream_read(&palette_descriptor, sizeof(qgf_palette_v1_t), 1, stream) != 1) {
    203         qp_dprintf("Failed to read palette_descriptor, expected length was not %d\n", (int)sizeof(qgf_palette_v1_t));
    204         return false;
    205     }
    206 
    207     // Make sure this block is valid
    208     uint32_t expected_length = (1 << bpp) * 3 * sizeof(uint8_t);
    209     if (!qgf_validate_block_header(&palette_descriptor.header, QGF_FRAME_PALETTE_DESCRIPTOR_TYPEID, expected_length)) {
    210         return false;
    211     }
    212 
    213     // Move forward in the stream to the next block
    214     qp_stream_seek(stream, expected_length, SEEK_CUR);
    215     return true;
    216 }
    217 
    218 bool qgf_validate_delta_descriptor(qp_stream_t *stream, uint16_t frame_number) {
    219     // Read the delta descriptor
    220     qgf_delta_v1_t delta_descriptor;
    221     if (qp_stream_read(&delta_descriptor, sizeof(qgf_delta_v1_t), 1, stream) != 1) {
    222         qp_dprintf("Failed to read delta_descriptor, expected length was not %d\n", (int)sizeof(qgf_delta_v1_t));
    223         return false;
    224     }
    225 
    226     // Make sure this block is valid
    227     if (!qgf_validate_block_header(&delta_descriptor.header, QGF_FRAME_DELTA_DESCRIPTOR_TYPEID, (sizeof(qgf_delta_v1_t) - sizeof(qgf_block_header_v1_t)))) {
    228         return false;
    229     }
    230 
    231     return true;
    232 }
    233 
    234 bool qgf_validate_frame_data_descriptor(qp_stream_t *stream, uint16_t frame_number) {
    235     // Read and validate the data block
    236     qgf_data_v1_t data_descriptor;
    237     if (qp_stream_read(&data_descriptor, sizeof(qgf_data_v1_t), 1, stream) != 1) {
    238         qp_dprintf("Failed to read data_descriptor, expected length was not %d\n", (int)sizeof(qgf_data_v1_t));
    239         return false;
    240     }
    241 
    242     if (!qgf_validate_block_header(&data_descriptor.header, QGF_FRAME_DATA_DESCRIPTOR_TYPEID, -1)) {
    243         return false;
    244     }
    245 
    246     return true;
    247 }
    248 
    249 bool qgf_validate_stream(qp_stream_t *stream) {
    250     uint16_t frame_count;
    251     if (!qgf_read_graphics_descriptor(stream, NULL, NULL, &frame_count, NULL)) {
    252         return false;
    253     }
    254 
    255     // Read and validate all the frames (automatically validates the frame offset descriptor in the process)
    256     for (uint16_t i = 0; i < frame_count; ++i) {
    257         // Validate the frame descriptor block
    258         uint8_t bpp             = 0;
    259         bool    has_palette     = false;
    260         bool    is_panel_native = false;
    261         bool    has_delta       = false;
    262         if (!qgf_validate_frame_descriptor(stream, i, &bpp, &has_palette, &is_panel_native, &has_delta)) {
    263             return false;
    264         }
    265 
    266         // If we've got a palette block, check it
    267         if (has_palette && !qgf_validate_palette_descriptor(stream, i, bpp)) {
    268             return false;
    269         }
    270 
    271         // If we've got a delta block, check it
    272         if (has_delta && !qgf_validate_delta_descriptor(stream, i)) {
    273             return false;
    274         }
    275 
    276         // Check the data block
    277         if (!qgf_validate_frame_data_descriptor(stream, i)) {
    278             return false;
    279         }
    280     }
    281 
    282     return true;
    283 }
    284 
    285 // Work out the total size of an image definition, assuming we can read far enough into the file
    286 uint32_t qgf_get_total_size(qp_stream_t *stream) {
    287     // Get the original location
    288     uint32_t oldpos = qp_stream_tell(stream);
    289 
    290     // Read the graphics descriptor, grabbing the size
    291     uint32_t total_size;
    292     if (!qgf_read_graphics_descriptor(stream, NULL, NULL, NULL, &total_size)) {
    293         return false;
    294     }
    295 
    296     // Restore the original location
    297     qp_stream_setpos(stream, oldpos);
    298     return total_size;
    299 }