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.
This commit is contained in:
		
							parent
							
								
									1dbbd2b6b0
								
							
						
					
					
						commit
						1f2b1dedcc
					
				
					 62 changed files with 7561 additions and 35 deletions
				
			
		
							
								
								
									
										130
									
								
								drivers/painter/tft_panel/qp_tft_panel.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								drivers/painter/tft_panel/qp_tft_panel.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,130 @@
 | 
			
		|||
// Copyright 2021 Nick Brassel (@tzarc)
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
#include "color.h"
 | 
			
		||||
#include "qp_internal.h"
 | 
			
		||||
#include "qp_comms.h"
 | 
			
		||||
#include "qp_draw.h"
 | 
			
		||||
#include "qp_tft_panel.h"
 | 
			
		||||
 | 
			
		||||
#define BYTE_SWAP(x) (((((uint16_t)(x)) >> 8) & 0x00FF) | ((((uint16_t)(x)) << 8) & 0xFF00))
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Native pixel format conversion
 | 
			
		||||
 | 
			
		||||
uint16_t qp_rgb888_to_rgb565(uint8_t r, uint8_t g, uint8_t b) {
 | 
			
		||||
    uint16_t rgb565 = (((uint16_t)r) >> 3) << 11 | (((uint16_t)g) >> 2) << 5 | (((uint16_t)b) >> 3);
 | 
			
		||||
    return rgb565;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t qp_rgb888_to_rgb565_swapped(uint8_t r, uint8_t g, uint8_t b) {
 | 
			
		||||
    uint16_t rgb565 = (((uint16_t)r) >> 3) << 11 | (((uint16_t)g) >> 2) << 5 | (((uint16_t)b) >> 3);
 | 
			
		||||
    return BYTE_SWAP(rgb565);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t qp_rgb888_to_bgr565(uint8_t r, uint8_t g, uint8_t b) {
 | 
			
		||||
    uint16_t bgr565 = (((uint16_t)b) >> 3) << 11 | (((uint16_t)g) >> 2) << 5 | (((uint16_t)r) >> 3);
 | 
			
		||||
    return bgr565;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t qp_rgb888_to_bgr565_swapped(uint8_t r, uint8_t g, uint8_t b) {
 | 
			
		||||
    uint16_t bgr565 = (((uint16_t)b) >> 3) << 11 | (((uint16_t)g) >> 2) << 5 | (((uint16_t)r) >> 3);
 | 
			
		||||
    return BYTE_SWAP(bgr565);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Quantum Painter API implementations
 | 
			
		||||
 | 
			
		||||
// Power control
 | 
			
		||||
bool qp_tft_panel_power(painter_device_t device, bool power_on) {
 | 
			
		||||
    struct painter_driver_t *                          driver = (struct painter_driver_t *)device;
 | 
			
		||||
    struct tft_panel_dc_reset_painter_driver_vtable_t *vtable = (struct tft_panel_dc_reset_painter_driver_vtable_t *)driver->driver_vtable;
 | 
			
		||||
    qp_comms_command(device, power_on ? vtable->opcodes.display_on : vtable->opcodes.display_off);
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Screen clear
 | 
			
		||||
bool qp_tft_panel_clear(painter_device_t device) {
 | 
			
		||||
    struct painter_driver_t *driver = (struct painter_driver_t *)device;
 | 
			
		||||
    driver->driver_vtable->init(device, driver->rotation); // Re-init the LCD
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Screen flush
 | 
			
		||||
bool qp_tft_panel_flush(painter_device_t device) {
 | 
			
		||||
    // No-op, as there's no framebuffer in RAM for this device.
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Viewport to draw to
 | 
			
		||||
bool qp_tft_panel_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {
 | 
			
		||||
    struct painter_driver_t *                          driver = (struct painter_driver_t *)device;
 | 
			
		||||
    struct tft_panel_dc_reset_painter_driver_vtable_t *vtable = (struct tft_panel_dc_reset_painter_driver_vtable_t *)driver->driver_vtable;
 | 
			
		||||
 | 
			
		||||
    // Fix up the drawing location if required
 | 
			
		||||
    left += driver->offset_x;
 | 
			
		||||
    right += driver->offset_x;
 | 
			
		||||
    top += driver->offset_y;
 | 
			
		||||
    bottom += driver->offset_y;
 | 
			
		||||
 | 
			
		||||
    // Check if we need to manually swap the window coordinates based on whether or not we're in a sideways rotation
 | 
			
		||||
    if (vtable->swap_window_coords && (driver->rotation == QP_ROTATION_90 || driver->rotation == QP_ROTATION_270)) {
 | 
			
		||||
        uint16_t temp;
 | 
			
		||||
 | 
			
		||||
        temp = left;
 | 
			
		||||
        left = top;
 | 
			
		||||
        top  = temp;
 | 
			
		||||
 | 
			
		||||
        temp   = right;
 | 
			
		||||
        right  = bottom;
 | 
			
		||||
        bottom = temp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (vtable->num_window_bytes == 1) {
 | 
			
		||||
        // Set up the x-window
 | 
			
		||||
        uint8_t xbuf[2] = {left & 0xFF, right & 0xFF};
 | 
			
		||||
        qp_comms_command_databuf(device, vtable->opcodes.set_column_address, xbuf, sizeof(xbuf));
 | 
			
		||||
 | 
			
		||||
        // Set up the y-window
 | 
			
		||||
        uint8_t ybuf[2] = {top & 0xFF, bottom & 0xFF};
 | 
			
		||||
        qp_comms_command_databuf(device, vtable->opcodes.set_row_address, ybuf, sizeof(ybuf));
 | 
			
		||||
    } else if (vtable->num_window_bytes == 2) {
 | 
			
		||||
        // Set up the x-window
 | 
			
		||||
        uint8_t xbuf[4] = {left >> 8, left & 0xFF, right >> 8, right & 0xFF};
 | 
			
		||||
        qp_comms_command_databuf(device, vtable->opcodes.set_column_address, xbuf, sizeof(xbuf));
 | 
			
		||||
 | 
			
		||||
        // Set up the y-window
 | 
			
		||||
        uint8_t ybuf[4] = {top >> 8, top & 0xFF, bottom >> 8, bottom & 0xFF};
 | 
			
		||||
        qp_comms_command_databuf(device, vtable->opcodes.set_row_address, ybuf, sizeof(ybuf));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Lock in the window
 | 
			
		||||
    qp_comms_command(device, vtable->opcodes.enable_writes);
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stream pixel data to the current write position in GRAM
 | 
			
		||||
bool qp_tft_panel_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count) {
 | 
			
		||||
    qp_comms_send(device, pixel_data, native_pixel_count * sizeof(uint16_t));
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convert supplied palette entries into their native equivalents
 | 
			
		||||
bool qp_tft_panel_palette_convert(painter_device_t device, int16_t palette_size, qp_pixel_t *palette) {
 | 
			
		||||
    struct painter_driver_t *                          driver = (struct painter_driver_t *)device;
 | 
			
		||||
    struct tft_panel_dc_reset_painter_driver_vtable_t *vtable = (struct tft_panel_dc_reset_painter_driver_vtable_t *)driver->driver_vtable;
 | 
			
		||||
    for (int16_t i = 0; i < palette_size; ++i) {
 | 
			
		||||
        RGB rgb           = hsv_to_rgb_nocie((HSV){palette[i].hsv888.h, palette[i].hsv888.s, palette[i].hsv888.v});
 | 
			
		||||
        palette[i].rgb565 = vtable->rgb888_to_native16bit(rgb.r, rgb.g, rgb.b);
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Append pixels to the target location, keyed by the pixel index
 | 
			
		||||
bool qp_tft_panel_append_pixels(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices) {
 | 
			
		||||
    uint16_t *buf = (uint16_t *)target_buffer;
 | 
			
		||||
    for (uint32_t i = 0; i < pixel_count; ++i) {
 | 
			
		||||
        buf[pixel_offset + i] = palette[palette_indices[i]].rgb565;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								drivers/painter/tft_panel/qp_tft_panel.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								drivers/painter/tft_panel/qp_tft_panel.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,67 @@
 | 
			
		|||
// Copyright 2021 Nick Brassel (@tzarc)
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
#include "color.h"
 | 
			
		||||
#include "qp_internal.h"
 | 
			
		||||
 | 
			
		||||
#ifdef QUANTUM_PAINTER_SPI_ENABLE
 | 
			
		||||
#    include "qp_comms_spi.h"
 | 
			
		||||
#endif // QUANTUM_PAINTER_SPI_ENABLE
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Common TFT panel implementation using D/C, and RST pins.
 | 
			
		||||
 | 
			
		||||
typedef uint16_t (*rgb888_to_native_uint16_t)(uint8_t r, uint8_t g, uint8_t b);
 | 
			
		||||
 | 
			
		||||
// Driver vtable with extras
 | 
			
		||||
struct tft_panel_dc_reset_painter_driver_vtable_t {
 | 
			
		||||
    struct painter_driver_vtable_t base; // must be first, so it can be cast to/from the painter_driver_vtable_t* type
 | 
			
		||||
 | 
			
		||||
    // Conversion function for palette entries
 | 
			
		||||
    rgb888_to_native_uint16_t rgb888_to_native16bit;
 | 
			
		||||
 | 
			
		||||
    // Number of bytes for transmitting x/y coordinates
 | 
			
		||||
    uint8_t num_window_bytes;
 | 
			
		||||
 | 
			
		||||
    // Whether or not the x/y coords should be swapped on 90/270 rotation
 | 
			
		||||
    bool swap_window_coords;
 | 
			
		||||
 | 
			
		||||
    // Opcodes for normal display operation
 | 
			
		||||
    struct {
 | 
			
		||||
        uint8_t display_on;
 | 
			
		||||
        uint8_t display_off;
 | 
			
		||||
        uint8_t set_column_address;
 | 
			
		||||
        uint8_t set_row_address;
 | 
			
		||||
        uint8_t enable_writes;
 | 
			
		||||
    } opcodes;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Device definition
 | 
			
		||||
typedef struct tft_panel_dc_reset_painter_device_t {
 | 
			
		||||
    struct painter_driver_t base; // must be first, so it can be cast to/from the painter_device_t* type
 | 
			
		||||
 | 
			
		||||
    union {
 | 
			
		||||
#ifdef QUANTUM_PAINTER_SPI_ENABLE
 | 
			
		||||
        // SPI-based configurables
 | 
			
		||||
        struct qp_comms_spi_dc_reset_config_t spi_dc_reset_config;
 | 
			
		||||
#endif // QUANTUM_PAINTER_SPI_ENABLE
 | 
			
		||||
 | 
			
		||||
        // TODO: I2C/parallel etc.
 | 
			
		||||
    };
 | 
			
		||||
} tft_panel_dc_reset_painter_device_t;
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Forward declarations for injecting into concrete driver vtables
 | 
			
		||||
 | 
			
		||||
bool qp_tft_panel_power(painter_device_t device, bool power_on);
 | 
			
		||||
bool qp_tft_panel_clear(painter_device_t device);
 | 
			
		||||
bool qp_tft_panel_flush(painter_device_t device);
 | 
			
		||||
bool qp_tft_panel_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom);
 | 
			
		||||
bool qp_tft_panel_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count);
 | 
			
		||||
bool qp_tft_panel_palette_convert(painter_device_t device, int16_t palette_size, qp_pixel_t *palette);
 | 
			
		||||
bool qp_tft_panel_append_pixels(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices);
 | 
			
		||||
 | 
			
		||||
uint16_t qp_rgb888_to_rgb565(uint8_t r, uint8_t g, uint8_t b);
 | 
			
		||||
uint16_t qp_rgb888_to_rgb565_swapped(uint8_t r, uint8_t g, uint8_t b);
 | 
			
		||||
uint16_t qp_rgb888_to_bgr565(uint8_t r, uint8_t g, uint8_t b);
 | 
			
		||||
uint16_t qp_rgb888_to_bgr565_swapped(uint8_t r, uint8_t g, uint8_t b);
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue