From 1f2b1dedccdf21b629c45ece80b4ca32f6653296 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 13 Apr 2022 18:00:18 +1000 Subject: 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 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. --- quantum/painter/qp.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 quantum/painter/qp.c (limited to 'quantum/painter/qp.c') diff --git a/quantum/painter/qp.c b/quantum/painter/qp.c new file mode 100644 index 0000000000..e292ff6497 --- /dev/null +++ b/quantum/painter/qp.c @@ -0,0 +1,228 @@ +// Copyright 2021 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "qp_internal.h" +#include "qp_comms.h" +#include "qp_draw.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Internal driver validation + +static bool validate_driver_vtable(struct painter_driver_t *driver) { + return (driver->driver_vtable && driver->driver_vtable->init && driver->driver_vtable->power && driver->driver_vtable->clear && driver->driver_vtable->viewport && driver->driver_vtable->pixdata && driver->driver_vtable->palette_convert && driver->driver_vtable->append_pixels) ? true : false; +} + +static bool validate_comms_vtable(struct painter_driver_t *driver) { + return (driver->comms_vtable && driver->comms_vtable->comms_init && driver->comms_vtable->comms_start && driver->comms_vtable->comms_stop && driver->comms_vtable->comms_send) ? true : false; +} + +static bool validate_driver_integrity(struct painter_driver_t *driver) { + return validate_driver_vtable(driver) && validate_comms_vtable(driver); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantum Painter External API: qp_init + +bool qp_init(painter_device_t device, painter_rotation_t rotation) { + qp_dprintf("qp_init: entry\n"); + struct painter_driver_t *driver = (struct painter_driver_t *)device; + + driver->validate_ok = false; + if (!validate_driver_integrity(driver)) { + qp_dprintf("Failed to validate driver integrity in qp_init\n"); + return false; + } + + driver->validate_ok = true; + + if (!qp_comms_init(device)) { + driver->validate_ok = false; + qp_dprintf("qp_init: fail (could not init comms)\n"); + return false; + } + + if (!qp_comms_start(device)) { + qp_dprintf("qp_init: fail (could not start comms)\n"); + return false; + } + + // Set the rotation before init + driver->rotation = rotation; + + // Invoke init + bool ret = driver->driver_vtable->init(device, rotation); + qp_comms_stop(device); + qp_dprintf("qp_init: %s\n", ret ? "ok" : "fail"); + return ret; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantum Painter External API: qp_power + +bool qp_power(painter_device_t device, bool power_on) { + qp_dprintf("qp_power: entry\n"); + struct painter_driver_t *driver = (struct painter_driver_t *)device; + if (!driver->validate_ok) { + qp_dprintf("qp_power: fail (validation_ok == false)\n"); + return false; + } + + if (!qp_comms_start(device)) { + qp_dprintf("qp_power: fail (could not start comms)\n"); + return false; + } + + bool ret = driver->driver_vtable->power(device, power_on); + qp_comms_stop(device); + qp_dprintf("qp_power: %s\n", ret ? "ok" : "fail"); + return ret; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantum Painter External API: qp_clear + +bool qp_clear(painter_device_t device) { + qp_dprintf("qp_clear: entry\n"); + struct painter_driver_t *driver = (struct painter_driver_t *)device; + if (!driver->validate_ok) { + qp_dprintf("qp_clear: fail (validation_ok == false)\n"); + return false; + } + + if (!qp_comms_start(device)) { + qp_dprintf("qp_clear: fail (could not start comms)\n"); + return false; + } + + bool ret = driver->driver_vtable->clear(device); + qp_comms_stop(device); + qp_dprintf("qp_clear: %s\n", ret ? "ok" : "fail"); + return ret; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantum Painter External API: qp_flush + +bool qp_flush(painter_device_t device) { + qp_dprintf("qp_flush: entry\n"); + struct painter_driver_t *driver = (struct painter_driver_t *)device; + if (!driver->validate_ok) { + qp_dprintf("qp_flush: fail (validation_ok == false)\n"); + return false; + } + + if (!qp_comms_start(device)) { + qp_dprintf("qp_flush: fail (could not start comms)\n"); + return false; + } + + bool ret = driver->driver_vtable->flush(device); + qp_comms_stop(device); + qp_dprintf("qp_flush: %s\n", ret ? "ok" : "fail"); + return ret; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantum Painter External API: qp_get_geometry + +void qp_get_geometry(painter_device_t device, uint16_t *width, uint16_t *height, painter_rotation_t *rotation, uint16_t *offset_x, uint16_t *offset_y) { + qp_dprintf("qp_geometry: entry\n"); + struct painter_driver_t *driver = (struct painter_driver_t *)device; + + switch (driver->rotation) { + default: + case QP_ROTATION_0: + case QP_ROTATION_180: + if (width) { + *width = driver->panel_width; + } + if (height) { + *height = driver->panel_height; + } + break; + case QP_ROTATION_90: + case QP_ROTATION_270: + if (width) { + *width = driver->panel_height; + } + if (height) { + *height = driver->panel_width; + } + break; + } + + if (rotation) { + *rotation = driver->rotation; + } + + if (offset_x) { + *offset_x = driver->offset_x; + } + + if (offset_y) { + *offset_y = driver->offset_y; + } + + qp_dprintf("qp_geometry: ok\n"); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantum Painter External API: qp_set_viewport_offsets + +void qp_set_viewport_offsets(painter_device_t device, uint16_t offset_x, uint16_t offset_y) { + qp_dprintf("qp_set_viewport_offsets: entry\n"); + struct painter_driver_t *driver = (struct painter_driver_t *)device; + + driver->offset_x = offset_x; + driver->offset_y = offset_y; + + qp_dprintf("qp_set_viewport_offsets: ok\n"); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantum Painter External API: qp_viewport + +bool qp_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) { + qp_dprintf("qp_viewport: entry\n"); + struct painter_driver_t *driver = (struct painter_driver_t *)device; + if (!driver->validate_ok) { + qp_dprintf("qp_viewport: fail (validation_ok == false)\n"); + return false; + } + + if (!qp_comms_start(device)) { + qp_dprintf("qp_viewport: fail (could not start comms)\n"); + return false; + } + + // Set the viewport + bool ret = driver->driver_vtable->viewport(device, left, top, right, bottom); + qp_dprintf("qp_viewport: %s\n", ret ? "ok" : "fail"); + qp_comms_stop(device); + return ret; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantum Painter External API: qp_pixdata + +bool qp_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count) { + qp_dprintf("qp_pixdata: entry\n"); + struct painter_driver_t *driver = (struct painter_driver_t *)device; + if (!driver->validate_ok) { + qp_dprintf("qp_pixdata: fail (validation_ok == false)\n"); + return false; + } + + if (!qp_comms_start(device)) { + qp_dprintf("qp_pixdata: fail (could not start comms)\n"); + return false; + } + + bool ret = driver->driver_vtable->pixdata(device, pixel_data, native_pixel_count); + qp_dprintf("qp_pixdata: %s\n", ret ? "ok" : "fail"); + qp_comms_stop(device); + return ret; +} -- cgit v1.2.1