 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.
		
			
				
	
	
		
			171 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			171 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // Copyright 2021 Nick Brassel (@tzarc)
 | |
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| 
 | |
| #include "qp_stream.h"
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | |
| // Stream API
 | |
| 
 | |
| uint32_t qp_stream_read_impl(void *output_buf, uint32_t member_size, uint32_t num_members, qp_stream_t *stream) {
 | |
|     uint8_t *output_ptr = (uint8_t *)output_buf;
 | |
| 
 | |
|     uint32_t i;
 | |
|     for (i = 0; i < (num_members * member_size); ++i) {
 | |
|         int16_t c = qp_stream_get(stream);
 | |
|         if (c < 0) {
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         output_ptr[i] = (uint8_t)(c & 0xFF);
 | |
|     }
 | |
| 
 | |
|     return i / member_size;
 | |
| }
 | |
| 
 | |
| uint32_t qp_stream_write_impl(const void *input_buf, uint32_t member_size, uint32_t num_members, qp_stream_t *stream) {
 | |
|     uint8_t *input_ptr = (uint8_t *)input_buf;
 | |
| 
 | |
|     uint32_t i;
 | |
|     for (i = 0; i < (num_members * member_size); ++i) {
 | |
|         if (!qp_stream_put(stream, input_ptr[i])) {
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return i / member_size;
 | |
| }
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | |
| // Memory streams
 | |
| 
 | |
| int16_t mem_get(qp_stream_t *stream) {
 | |
|     qp_memory_stream_t *s = (qp_memory_stream_t *)stream;
 | |
|     if (s->position >= s->length) {
 | |
|         s->is_eof = true;
 | |
|         return STREAM_EOF;
 | |
|     }
 | |
|     return s->buffer[s->position++];
 | |
| }
 | |
| 
 | |
| bool mem_put(qp_stream_t *stream, uint8_t c) {
 | |
|     qp_memory_stream_t *s = (qp_memory_stream_t *)stream;
 | |
|     if (s->position >= s->length) {
 | |
|         s->is_eof = true;
 | |
|         return false;
 | |
|     }
 | |
|     s->buffer[s->position++] = c;
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| int mem_seek(qp_stream_t *stream, int32_t offset, int origin) {
 | |
|     qp_memory_stream_t *s = (qp_memory_stream_t *)stream;
 | |
| 
 | |
|     // Handle as per fseek
 | |
|     int32_t position = s->position;
 | |
|     switch (origin) {
 | |
|         case SEEK_SET:
 | |
|             position = offset;
 | |
|             break;
 | |
|         case SEEK_CUR:
 | |
|             position += offset;
 | |
|             break;
 | |
|         case SEEK_END:
 | |
|             position = s->length + offset;
 | |
|             break;
 | |
|         default:
 | |
|             return -1;
 | |
|     }
 | |
| 
 | |
|     // If we're before the start, ignore it.
 | |
|     if (position < 0) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     // If we're at the end it's okay, we only care if we're after the end for failure purposes -- as per lseek()
 | |
|     if (position > s->length) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     // Update the offset
 | |
|     s->position = position;
 | |
| 
 | |
|     // Successful invocation of fseek() results in clearing of the EOF flag by default, mirror the same functionality
 | |
|     s->is_eof = false;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int32_t mem_tell(qp_stream_t *stream) {
 | |
|     qp_memory_stream_t *s = (qp_memory_stream_t *)stream;
 | |
|     return s->position;
 | |
| }
 | |
| 
 | |
| bool mem_is_eof(qp_stream_t *stream) {
 | |
|     qp_memory_stream_t *s = (qp_memory_stream_t *)stream;
 | |
|     return s->is_eof;
 | |
| }
 | |
| 
 | |
| qp_memory_stream_t qp_make_memory_stream(void *buffer, int32_t length) {
 | |
|     qp_memory_stream_t stream = {
 | |
|         .base =
 | |
|             {
 | |
|                 .get    = mem_get,
 | |
|                 .put    = mem_put,
 | |
|                 .seek   = mem_seek,
 | |
|                 .tell   = mem_tell,
 | |
|                 .is_eof = mem_is_eof,
 | |
|             },
 | |
|         .buffer   = (uint8_t *)buffer,
 | |
|         .length   = length,
 | |
|         .position = 0,
 | |
|     };
 | |
|     return stream;
 | |
| }
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | |
| // FILE streams
 | |
| 
 | |
| #ifdef QP_STREAM_HAS_FILE_IO
 | |
| 
 | |
| int16_t file_get(qp_stream_t *stream) {
 | |
|     qp_file_stream_t *s = (qp_file_stream_t *)stream;
 | |
|     int               c = fgetc(s->file);
 | |
|     if (c < 0 || feof(s->file)) return STREAM_EOF;
 | |
|     return (uint16_t)c;
 | |
| }
 | |
| 
 | |
| bool file_put(qp_stream_t *stream, uint8_t c) {
 | |
|     qp_file_stream_t *s = (qp_file_stream_t *)stream;
 | |
|     return fputc(c, s->file) == c;
 | |
| }
 | |
| 
 | |
| int file_seek(qp_stream_t *stream, int32_t offset, int origin) {
 | |
|     qp_file_stream_t *s = (qp_file_stream_t *)stream;
 | |
|     return fseek(s->file, offset, origin);
 | |
| }
 | |
| 
 | |
| int32_t file_tell(qp_stream_t *stream) {
 | |
|     qp_file_stream_t *s = (qp_file_stream_t *)stream;
 | |
|     return (int32_t)ftell(s->file);
 | |
| }
 | |
| 
 | |
| bool file_is_eof(qp_stream_t *stream) {
 | |
|     qp_file_stream_t *s = (qp_file_stream_t *)stream;
 | |
|     return (bool)feof(s->file);
 | |
| }
 | |
| 
 | |
| qp_file_stream_t qp_make_file_stream(FILE *f) {
 | |
|     qp_file_stream_t stream = {
 | |
|         .base =
 | |
|             {
 | |
|                 .get    = file_get,
 | |
|                 .put    = file_put,
 | |
|                 .seek   = file_seek,
 | |
|                 .tell   = file_tell,
 | |
|                 .is_eof = file_is_eof,
 | |
|             },
 | |
|         .file = f,
 | |
|     };
 | |
|     return stream;
 | |
| }
 | |
| #endif // QP_STREAM_HAS_FILE_IO
 |