diff options
author | Nick Brassel <nick@tzarc.org> | 2022-04-13 18:00:18 +1000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-13 18:00:18 +1000 |
commit | 1f2b1dedccdf21b629c45ece80b4ca32f6653296 (patch) | |
tree | a4283b928fe11c6662be10067314531f12774152 /quantum/painter/qff.c | |
parent | 1dbbd2b6b068b9f921ebc0341c890df16a491007 (diff) |
Quantum Painter (#10174)
* 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.
Diffstat (limited to 'quantum/painter/qff.c')
-rw-r--r-- | quantum/painter/qff.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/quantum/painter/qff.c b/quantum/painter/qff.c new file mode 100644 index 0000000000..cd6af788f9 --- /dev/null +++ b/quantum/painter/qff.c @@ -0,0 +1,137 @@ +// Copyright 2021 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later + +// Quantum Font File "QFF" File Format. +// See https://docs.qmk.fm/#/quantum_painter_qff for more information. + +#include "qff.h" +#include "qp_draw.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// QFF API + +bool qff_read_font_descriptor(qp_stream_t *stream, uint8_t *line_height, bool *has_ascii_table, uint16_t *num_unicode_glyphs, uint8_t *bpp, bool *has_palette, painter_compression_t *compression_scheme, uint32_t *total_bytes) { + // Seek to the start + qp_stream_setpos(stream, 0); + + // Read and validate the font descriptor + qff_font_descriptor_v1_t font_descriptor; + if (qp_stream_read(&font_descriptor, sizeof(qff_font_descriptor_v1_t), 1, stream) != 1) { + qp_dprintf("Failed to read font_descriptor, expected length was not %d\n", (int)sizeof(qff_font_descriptor_v1_t)); + return false; + } + + // Make sure this block is valid + if (!qgf_validate_block_header(&font_descriptor.header, QFF_FONT_DESCRIPTOR_TYPEID, (sizeof(qff_font_descriptor_v1_t) - sizeof(qgf_block_header_v1_t)))) { + return false; + } + + // Make sure the magic and version are correct + if (font_descriptor.magic != QFF_MAGIC || font_descriptor.qff_version != 0x01) { + qp_dprintf("Failed to validate font_descriptor, expected magic 0x%06X was 0x%06X, expected version = 0x%02X was 0x%02X\n", (int)QFF_MAGIC, (int)font_descriptor.magic, (int)0x01, (int)font_descriptor.qff_version); + return false; + } + + // Make sure the file length is valid + if (font_descriptor.neg_total_file_size != ~font_descriptor.total_file_size) { + qp_dprintf("Failed to validate font_descriptor, expected negated length 0x%08X was 0x%08X\n", (int)(~font_descriptor.total_file_size), (int)font_descriptor.neg_total_file_size); + return false; + } + + // Copy out the required info + if (line_height) { + *line_height = font_descriptor.line_height; + } + if (has_ascii_table) { + *has_ascii_table = font_descriptor.has_ascii_table; + } + if (num_unicode_glyphs) { + *num_unicode_glyphs = font_descriptor.num_unicode_glyphs; + } + if (bpp || has_palette) { + if (!qgf_parse_format(font_descriptor.format, bpp, has_palette)) { + return false; + } + } + if (compression_scheme) { + *compression_scheme = font_descriptor.compression_scheme; + } + if (total_bytes) { + *total_bytes = font_descriptor.total_file_size; + } + + return true; +} + +static bool qff_validate_ascii_descriptor(qp_stream_t *stream) { + // Read the raw descriptor + qff_ascii_glyph_table_v1_t ascii_descriptor; + if (qp_stream_read(&ascii_descriptor, sizeof(qff_ascii_glyph_table_v1_t), 1, stream) != 1) { + qp_dprintf("Failed to read ascii_descriptor, expected length was not %d\n", (int)sizeof(qff_ascii_glyph_table_v1_t)); + return false; + } + + // Make sure this block is valid + if (!qgf_validate_block_header(&ascii_descriptor.header, QFF_ASCII_GLYPH_DESCRIPTOR_TYPEID, (sizeof(qff_ascii_glyph_table_v1_t) - sizeof(qgf_block_header_v1_t)))) { + return false; + } + + return true; +} + +static bool qff_validate_unicode_descriptor(qp_stream_t *stream, uint16_t num_unicode_glyphs) { + // Read the raw descriptor + qff_unicode_glyph_table_v1_t unicode_descriptor; + if (qp_stream_read(&unicode_descriptor, sizeof(qff_unicode_glyph_table_v1_t), 1, stream) != 1) { + qp_dprintf("Failed to read unicode_descriptor, expected length was not %d\n", (int)sizeof(qff_unicode_glyph_table_v1_t)); + return false; + } + + // Make sure this block is valid + if (!qgf_validate_block_header(&unicode_descriptor.header, QFF_UNICODE_GLYPH_DESCRIPTOR_TYPEID, num_unicode_glyphs * 6)) { + return false; + } + + // Skip the necessary amount of data to get to the next block + qp_stream_seek(stream, num_unicode_glyphs * sizeof(qff_unicode_glyph_v1_t), SEEK_CUR); + + return true; +} + +bool qff_validate_stream(qp_stream_t *stream) { + bool has_ascii_table; + uint16_t num_unicode_glyphs; + + if (!qff_read_font_descriptor(stream, NULL, &has_ascii_table, &num_unicode_glyphs, NULL, NULL, NULL, NULL)) { + return false; + } + + if (has_ascii_table) { + if (!qff_validate_ascii_descriptor(stream)) { + return false; + } + } + + if (num_unicode_glyphs > 0) { + if (!qff_validate_unicode_descriptor(stream, num_unicode_glyphs)) { + return false; + } + } + + return true; +} + +uint32_t qff_get_total_size(qp_stream_t *stream) { + // Get the original location + uint32_t oldpos = qp_stream_tell(stream); + + // Read the font descriptor, grabbing the size + uint32_t total_size; + if (!qff_read_font_descriptor(stream, NULL, NULL, NULL, NULL, NULL, NULL, &total_size)) { + return false; + } + + // Restore the original location + qp_stream_setpos(stream, oldpos); + return total_size; +} |