 1f2b1dedcc
			
		
	
	
		1f2b1dedcc
		
			
		
	
	
	
	
		
			
			* Install dependencies before executing unit tests. * Split out UTF-8 decoder. * Fixup python formatting rules. * Add documentation for QGF/QFF and the RLE format used. * Add CLI commands for converting images and fonts. * Add stub rules.mk for QP. * Add stream type. * Add base driver and comms interfaces. * Add support for SPI, SPI+D/C comms drivers. * Include <qp.h> when enabled. * Add base support for SPI+D/C+RST panels, as well as concrete implementation of ST7789. * Add support for GC9A01. * Add support for ILI9341. * Add support for ILI9163. * Add support for SSD1351. * Implement qp_setpixel, including pixdata buffer management. * Implement qp_line. * Implement qp_rect. * Implement qp_circle. * Implement qp_ellipse. * Implement palette interpolation. * Allow for streams to work with either flash or RAM. * Image loading. * Font loading. * QGF palette loading. * Progressive decoder of pixel data supporting Raw+RLE, 1-,2-,4-,8-bpp monochrome and palette-based images. * Image drawing. * Animations. * Font rendering. * Check against 256 colours, dump out the loaded palette if debugging enabled. * Fix build. * AVR is not the intended audience. * `qmk format-c` * Generation fix. * First batch of docs. * More docs and examples. * Review comments. * Public API documentation.
		
			
				
	
	
		
			136 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // Copyright 2021 Nick Brassel (@tzarc)
 | |
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| // Quantum Graphics File "QGF" File Format.
 | |
| // See https://docs.qmk.fm/#/quantum_painter_qgf for more information.
 | |
| 
 | |
| #include <stdint.h>
 | |
| #include <stdbool.h>
 | |
| 
 | |
| #include "qp_stream.h"
 | |
| #include "qp_internal.h"
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | |
| // QGF structures
 | |
| 
 | |
| /////////////////////////////////////////
 | |
| // Common block header
 | |
| 
 | |
| typedef struct QP_PACKED qgf_block_header_v1_t {
 | |
|     uint8_t  type_id;     // See each respective block type below.
 | |
|     uint8_t  neg_type_id; // Negated type ID, used for detecting parsing errors.
 | |
|     uint32_t length : 24; // 24-bit blob length, allowing for block sizes of a maximum of 16MB.
 | |
| } qgf_block_header_v1_t;
 | |
| 
 | |
| _Static_assert(sizeof(qgf_block_header_v1_t) == 5, "qgf_block_header_v1_t must be 5 bytes in v1 of QGF");
 | |
| 
 | |
| /////////////////////////////////////////
 | |
| // Graphics descriptor
 | |
| 
 | |
| #define QGF_GRAPHICS_DESCRIPTOR_TYPEID 0x00
 | |
| 
 | |
| typedef struct QP_PACKED qgf_graphics_descriptor_v1_t {
 | |
|     qgf_block_header_v1_t header;              // = { .type_id = 0x00, .neg_type_id = (~0x00), .length = 18 }
 | |
|     uint32_t              magic : 24;          // constant, equal to 0x464751 ("QGF")
 | |
|     uint8_t               qgf_version;         // constant, equal to 0x01
 | |
|     uint32_t              total_file_size;     // total size of the entire file, starting at offset zero
 | |
|     uint32_t              neg_total_file_size; // negated value of total_file_size
 | |
|     uint16_t              image_width;         // in pixels
 | |
|     uint16_t              image_height;        // in pixels
 | |
|     uint16_t              frame_count;         // minimum of 1
 | |
| } qgf_graphics_descriptor_v1_t;
 | |
| 
 | |
| _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");
 | |
| 
 | |
| #define QGF_MAGIC 0x464751
 | |
| 
 | |
| /////////////////////////////////////////
 | |
| // Frame offset descriptor
 | |
| 
 | |
| #define QGF_FRAME_OFFSET_DESCRIPTOR_TYPEID 0x01
 | |
| 
 | |
| typedef struct QP_PACKED qgf_frame_offsets_v1_t {
 | |
|     qgf_block_header_v1_t header;    // = { .type_id = 0x01, .neg_type_id = (~0x01), .length = (N * sizeof(uint32_t)) }
 | |
|     uint32_t              offset[0]; // '0' signifies that this struct is immediately followed by the frame offsets
 | |
| } qgf_frame_offsets_v1_t;
 | |
| 
 | |
| _Static_assert(sizeof(qgf_frame_offsets_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_frame_offsets_v1_t must only contain qgf_block_header_v1_t in v1 of QGF");
 | |
| 
 | |
| /////////////////////////////////////////
 | |
| // Frame descriptor
 | |
| 
 | |
| #define QGF_FRAME_DESCRIPTOR_TYPEID 0x02
 | |
| 
 | |
| typedef struct QP_PACKED qgf_frame_v1_t {
 | |
|     qgf_block_header_v1_t header;                 // = { .type_id = 0x02, .neg_type_id = (~0x02), .length = 6 }
 | |
|     qp_image_format_t     format : 8;             // Frame format, see qp.h.
 | |
|     uint8_t               flags;                  // Frame flags, see below.
 | |
|     painter_compression_t compression_scheme : 8; // Compression scheme, see qp.h.
 | |
|     uint8_t               transparency_index;     // palette index used for transparent pixels (not yet implemented)
 | |
|     uint16_t              delay;                  // frame delay time for animations (in units of milliseconds)
 | |
| } qgf_frame_v1_t;
 | |
| 
 | |
| _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");
 | |
| 
 | |
| #define QGF_FRAME_FLAG_DELTA 0x02
 | |
| #define QGF_FRAME_FLAG_TRANSPARENT 0x01
 | |
| 
 | |
| /////////////////////////////////////////
 | |
| // Frame palette descriptor
 | |
| 
 | |
| #define QGF_FRAME_PALETTE_DESCRIPTOR_TYPEID 0x03
 | |
| 
 | |
| typedef struct QP_PACKED qgf_palette_entry_v1_t {
 | |
|     uint8_t h; // hue component: `[0,360)` degrees is mapped to `[0,255]` uint8_t.
 | |
|     uint8_t s; // saturation component: `[0,1]` is mapped to `[0,255]` uint8_t.
 | |
|     uint8_t v; // value component: `[0,1]` is mapped to `[0,255]` uint8_t.
 | |
| } qgf_palette_entry_v1_t;
 | |
| 
 | |
| _Static_assert(sizeof(qgf_palette_entry_v1_t) == 3, "Palette entry is not 3 bytes in size");
 | |
| 
 | |
| typedef struct QP_PACKED qgf_palette_v1_t {
 | |
|     qgf_block_header_v1_t  header; // = { .type_id = 0x03, .neg_type_id = (~0x03), .length = (N * 3 * sizeof(uint8_t)) }
 | |
|     qgf_palette_entry_v1_t hsv[0]; // N * hsv, where N is the number of palette entries depending on the frame format in the descriptor
 | |
| } qgf_palette_v1_t;
 | |
| 
 | |
| _Static_assert(sizeof(qgf_palette_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_palette_v1_t must only contain qgf_block_header_v1_t in v1 of QGF");
 | |
| 
 | |
| /////////////////////////////////////////
 | |
| // Frame delta descriptor
 | |
| 
 | |
| #define QGF_FRAME_DELTA_DESCRIPTOR_TYPEID 0x04
 | |
| 
 | |
| typedef struct QP_PACKED qgf_delta_v1_t {
 | |
|     qgf_block_header_v1_t header; // = { .type_id = 0x04, .neg_type_id = (~0x04), .length = 8 }
 | |
|     uint16_t              left;   // The left pixel location to draw the delta image
 | |
|     uint16_t              top;    // The top pixel location to draw the delta image
 | |
|     uint16_t              right;  // The right pixel location to to draw the delta image
 | |
|     uint16_t              bottom; // The bottom pixel location to to draw the delta image
 | |
| } qgf_delta_v1_t;
 | |
| 
 | |
| _Static_assert(sizeof(qgf_delta_v1_t) == (sizeof(qgf_block_header_v1_t) + 8), "qgf_delta_v1_t must be 13 bytes in v1 of QGF");
 | |
| 
 | |
| /////////////////////////////////////////
 | |
| // Frame data descriptor
 | |
| 
 | |
| #define QGF_FRAME_DATA_DESCRIPTOR_TYPEID 0x05
 | |
| 
 | |
| typedef struct QP_PACKED qgf_data_v1_t {
 | |
|     qgf_block_header_v1_t header;  // = { .type_id = 0x05, .neg_type_id = (~0x05), .length = N }
 | |
|     uint8_t               data[0]; // 0 signifies that this struct is immediately followed by the length of data specified in the header
 | |
| } qgf_data_v1_t;
 | |
| 
 | |
| _Static_assert(sizeof(qgf_data_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_data_v1_t must only contain qgf_block_header_v1_t in v1 of QGF");
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | |
| // QGF API
 | |
| 
 | |
| uint32_t qgf_get_total_size(qp_stream_t *stream);
 | |
| bool     qgf_validate_stream(qp_stream_t *stream);
 | |
| bool     qgf_validate_block_header(qgf_block_header_v1_t *desc, uint8_t expected_typeid, int32_t expected_length);
 | |
| 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);
 | |
| bool     qgf_parse_format(qp_image_format_t format, uint8_t *bpp, bool *has_palette);
 | |
| void     qgf_seek_to_frame_descriptor(qp_stream_t *stream, uint16_t frame_number);
 | |
| bool     qgf_parse_frame_descriptor(qgf_frame_v1_t *frame_descriptor, uint8_t *bpp, bool *has_palette, bool *is_delta, painter_compression_t *compression_scheme, uint16_t *delay);
 |