From 1f2b1dedccdf21b629c45ece80b4ca32f6653296 Mon Sep 17 00:00:00 2001
From: Nick Brassel <nick@tzarc.org>
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 <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.
---
 drivers/painter/tft_panel/qp_tft_panel.c | 130 +++++++++++++++++++++++++++++++
 1 file changed, 130 insertions(+)
 create mode 100644 drivers/painter/tft_panel/qp_tft_panel.c

(limited to 'drivers/painter/tft_panel/qp_tft_panel.c')

diff --git a/drivers/painter/tft_panel/qp_tft_panel.c b/drivers/painter/tft_panel/qp_tft_panel.c
new file mode 100644
index 0000000000..4d636c9509
--- /dev/null
+++ b/drivers/painter/tft_panel/qp_tft_panel.c
@@ -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;
+}
-- 
cgit v1.2.3

[cgit] Unable to lock slot /tmp/cgit/4b100000.lock: Permission denied (13)