summaryrefslogtreecommitdiff
path: root/quantum/painter
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/painter')
-rw-r--r--quantum/painter/lvgl/qp_lvgl.c150
-rw-r--r--quantum/painter/lvgl/qp_lvgl.h25
-rw-r--r--quantum/painter/lvgl/rules.mk24
-rw-r--r--quantum/painter/qgf.c4
-rw-r--r--quantum/painter/qp.c2
-rw-r--r--quantum/painter/qp.h15
-rw-r--r--quantum/painter/qp_draw.h10
-rw-r--r--quantum/painter/qp_draw_codec.c44
-rw-r--r--quantum/painter/qp_draw_image.c49
-rw-r--r--quantum/painter/qp_draw_text.c2
-rw-r--r--quantum/painter/qp_internal_driver.h2
-rw-r--r--quantum/painter/qp_internal_formats.h2
-rw-r--r--quantum/painter/rules.mk6
13 files changed, 317 insertions, 18 deletions
diff --git a/quantum/painter/lvgl/qp_lvgl.c b/quantum/painter/lvgl/qp_lvgl.c
new file mode 100644
index 0000000000..c6dd08ef97
--- /dev/null
+++ b/quantum/painter/lvgl/qp_lvgl.c
@@ -0,0 +1,150 @@
+// Copyright 2022 Jose Pablo Ramirez (@jpe230)
+// Copyright 2022 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "qp_lvgl.h"
+#include "timer.h"
+#include "deferred_exec.h"
+#include "lvgl.h"
+
+typedef struct lvgl_state_t {
+ uint8_t fnc_id; // Ideally this should be the pointer of the function to run
+ uint16_t delay_ms;
+ deferred_token defer_token;
+} lvgl_state_t;
+
+static deferred_executor_t lvgl_executors[2] = {0}; // For lv_tick_inc and lv_task_handler
+static lvgl_state_t lvgl_states[2] = {0}; // For lv_tick_inc and lv_task_handler
+
+painter_device_t selected_display = NULL;
+void * color_buffer = NULL;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Quantum Painter LVGL Integration Internal: qp_lvgl_flush
+
+void qp_lvgl_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
+ if (selected_display) {
+ uint32_t number_pixels = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1);
+ qp_viewport(selected_display, area->x1, area->y1, area->x2, area->y2);
+ qp_pixdata(selected_display, (void *)color_p, number_pixels);
+ qp_flush(selected_display);
+ lv_disp_flush_ready(disp);
+ }
+}
+
+static uint32_t tick_task_callback(uint32_t trigger_time, void *cb_arg) {
+ lvgl_state_t * state = (lvgl_state_t *)cb_arg;
+ static uint32_t last_tick = 0;
+ switch (state->fnc_id) {
+ case 0: {
+ uint32_t now = timer_read32();
+ lv_tick_inc(TIMER_DIFF_32(now, last_tick));
+ last_tick = now;
+ } break;
+ case 1:
+ lv_task_handler();
+ break;
+
+ default:
+ break;
+ }
+
+ // The tasks should run indefinitely
+ return state->delay_ms;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Quantum Painter LVGL Integration API: qp_lvgl_attach
+
+bool qp_lvgl_attach(painter_device_t device) {
+ qp_dprintf("qp_lvgl_start: entry\n");
+ qp_lvgl_detach();
+
+ struct painter_driver_t *driver = (struct painter_driver_t *)device;
+ if (!driver->validate_ok) {
+ qp_dprintf("qp_lvgl_attach: fail (validation_ok == false)\n");
+ qp_lvgl_detach();
+ return false;
+ }
+
+ // Setting up the tasks
+ lvgl_state_t *lv_tick_inc_state = &lvgl_states[0];
+ lv_tick_inc_state->fnc_id = 0;
+ lv_tick_inc_state->delay_ms = 1;
+ lv_tick_inc_state->defer_token = defer_exec_advanced(lvgl_executors, 2, 1, tick_task_callback, lv_tick_inc_state);
+
+ if (lv_tick_inc_state->defer_token == INVALID_DEFERRED_TOKEN) {
+ qp_dprintf("qp_lvgl_attach: fail (could not set up qp_lvgl executor)\n");
+ qp_lvgl_detach();
+ return false;
+ }
+
+ lvgl_state_t *lv_task_handler_state = &lvgl_states[1];
+ lv_task_handler_state->fnc_id = 1;
+ lv_task_handler_state->delay_ms = 5;
+ lv_task_handler_state->defer_token = defer_exec_advanced(lvgl_executors, 2, 5, tick_task_callback, lv_task_handler_state);
+
+ if (lv_task_handler_state->defer_token == INVALID_DEFERRED_TOKEN) {
+ qp_dprintf("qp_lvgl_attach: fail (could not set up qp_lvgl executor)\n");
+ qp_lvgl_detach();
+ return false;
+ }
+
+ // Init LVGL
+ lv_init();
+
+ // Set up lvgl display buffer
+ static lv_disp_draw_buf_t draw_buf;
+ // Allocate a buffer for 1/10 screen size
+ const size_t count_required = driver->panel_width * driver->panel_height / 10;
+ color_buffer = color_buffer ? realloc(color_buffer, sizeof(lv_color_t) * count_required) : malloc(sizeof(lv_color_t) * count_required);
+ if (!color_buffer) {
+ qp_dprintf("qp_lvgl_attach: fail (could not set up memory buffer)\n");
+ qp_lvgl_detach();
+ return false;
+ }
+ memset(color_buffer, 0, sizeof(lv_color_t) * count_required);
+ // Initialize the display buffer.
+ lv_disp_draw_buf_init(&draw_buf, color_buffer, NULL, count_required);
+
+ selected_display = device;
+
+ uint16_t panel_width, panel_height, offset_x, offset_y;
+ qp_get_geometry(selected_display, &panel_width, &panel_height, NULL, &offset_x, &offset_y);
+
+ panel_width -= offset_x;
+ panel_height -= offset_y;
+
+ // Setting up display driver
+ static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
+ lv_disp_drv_init(&disp_drv); /*Basic initialization*/
+ disp_drv.flush_cb = qp_lvgl_flush; /*Set your driver function*/
+ disp_drv.draw_buf = &draw_buf; /*Assign the buffer to the display*/
+ disp_drv.hor_res = panel_width; /*Set the horizontal resolution of the display*/
+ disp_drv.ver_res = panel_height; /*Set the vertical resolution of the display*/
+ lv_disp_drv_register(&disp_drv); /*Finally register the driver*/
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Quantum Painter LVGL Integration API: qp_lvgl_detach
+
+void qp_lvgl_detach(void) {
+ for (int i = 0; i < 2; ++i) {
+ cancel_deferred_exec_advanced(lvgl_executors, 2, lvgl_states[i].defer_token);
+ }
+ if (color_buffer) {
+ free(color_buffer);
+ color_buffer = NULL;
+ }
+ selected_display = NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Quantum Painter LVGL Integration Internal: qp_lvgl_internal_tick
+
+void qp_lvgl_internal_tick(void) {
+ static uint32_t last_lvgl_exec = 0;
+ deferred_exec_advanced_task(lvgl_executors, 2, &last_lvgl_exec);
+}
diff --git a/quantum/painter/lvgl/qp_lvgl.h b/quantum/painter/lvgl/qp_lvgl.h
new file mode 100644
index 0000000000..d9ad5e8df1
--- /dev/null
+++ b/quantum/painter/lvgl/qp_lvgl.h
@@ -0,0 +1,25 @@
+// Copyright 2022 Jose Pablo Ramirez (@jpe230)
+// Copyright 2022 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "qp.h"
+#include "lvgl.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Quantum Painter - LVGL External API
+
+/**
+ * Sets up LVGL with the supplied display.
+ *
+ * @param device[in] the handle of the device to control
+ * @return true if init. of LVGL succeeded
+ * @return false if init. of LVGL failed
+ */
+bool qp_lvgl_attach(painter_device_t device);
+
+/**
+ * Disconnects LVGL from any attached display
+ */
+void qp_lvgl_detach(void);
diff --git a/quantum/painter/lvgl/rules.mk b/quantum/painter/lvgl/rules.mk
new file mode 100644
index 0000000000..50226941b3
--- /dev/null
+++ b/quantum/painter/lvgl/rules.mk
@@ -0,0 +1,24 @@
+# LVGL Integration
+
+OPT_DEFS += -DQUANTUM_PAINTER_LVGL_INTEGRATION_ENABLE -DLV_CONF_INCLUDE_SIMPLE
+DEFERRED_EXEC_ENABLE := yes
+
+LVGL_DIR_NAME = lvgl
+LVGL_DIR = $(LIB_DIR)
+LVGL_PATH = $(LVGL_DIR)/$(LVGL_DIR_NAME)
+
+COMMON_VPATH += $(PLATFORM_PATH) \
+ $(QUANTUM_DIR)/painter/$(LVGL_DIR_NAME) \
+ $(LVGL_PATH)
+
+include $(LVGL_PATH)/src/extra/extra.mk
+include $(LVGL_PATH)/src/core/lv_core.mk
+include $(LVGL_PATH)/src/draw/lv_draw.mk
+include $(LVGL_PATH)/src/draw/sw/lv_draw_sw.mk
+include $(LVGL_PATH)/src/font/lv_font.mk
+include $(LVGL_PATH)/src/hal/lv_hal.mk
+include $(LVGL_PATH)/src/misc/lv_misc.mk
+include $(LVGL_PATH)/src/widgets/lv_widgets.mk
+
+SRC += qp_lvgl.c \
+ $(CSRCS)
diff --git a/quantum/painter/qgf.c b/quantum/painter/qgf.c
index 834837105b..6a4af07001 100644
--- a/quantum/painter/qgf.c
+++ b/quantum/painter/qgf.c
@@ -38,11 +38,13 @@ bool qgf_parse_format(qp_image_format_t format, uint8_t *bpp, bool *has_palette)
[PALETTE_2BPP] = {.bpp = 2, .has_palette = true},
[PALETTE_4BPP] = {.bpp = 4, .has_palette = true},
[PALETTE_8BPP] = {.bpp = 8, .has_palette = true},
+ [RGB565_16BPP] = {.bpp = 16, .has_palette = false},
+ [RGB888_24BPP] = {.bpp = 24, .has_palette = false},
};
// clang-format on
// Copy out the required info
- if (format > PALETTE_8BPP) {
+ if (format > RGB888_24BPP) {
qp_dprintf("Failed to parse frame_descriptor, invalid format 0x%02X\n", (int)format);
return false;
}
diff --git a/quantum/painter/qp.c b/quantum/painter/qp.c
index e292ff6497..de36dee2c1 100644
--- a/quantum/painter/qp.c
+++ b/quantum/painter/qp.c
@@ -12,7 +12,7 @@
// 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;
+ 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 && driver->driver_vtable->append_pixdata) ? true : false;
}
static bool validate_comms_vtable(struct painter_driver_t *driver) {
diff --git a/quantum/painter/qp.h b/quantum/painter/qp.h
index 69bc435961..00f5d7931a 100644
--- a/quantum/painter/qp.h
+++ b/quantum/painter/qp.h
@@ -64,6 +64,14 @@
# define QUANTUM_PAINTER_SUPPORTS_256_PALETTE FALSE
#endif
+#ifndef QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS
+/**
+ * @def This controls whether the native color range is supported. This avoids the use of palettes but each image
+ * requires more storage space.
+ */
+# define QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS FALSE
+#endif
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter types
@@ -463,3 +471,10 @@ int16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, pai
#ifdef QUANTUM_PAINTER_SSD1351_ENABLE
# include "qp_ssd1351.h"
#endif // QUANTUM_PAINTER_SSD1351_ENABLE
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Quantum Painter Extras
+
+#ifdef QUANTUM_PAINTER_LVGL_INTEGRATION_ENABLE
+# include "qp_lvgl.h"
+#endif // QUANTUM_PAINTER_LVGL_INTEGRATION_ENABLE
diff --git a/quantum/painter/qp_draw.h b/quantum/painter/qp_draw.h
index 7094d80eaa..84b1946ca7 100644
--- a/quantum/painter/qp_draw.h
+++ b/quantum/painter/qp_draw.h
@@ -30,9 +30,11 @@ bool qp_internal_fillrect_helper_impl(painter_device_t device, uint16_t l, uint1
// Convert from input pixel data + palette to equivalent pixels
typedef int16_t (*qp_internal_byte_input_callback)(void* cb_arg);
typedef bool (*qp_internal_pixel_output_callback)(qp_pixel_t* palette, uint8_t index, void* cb_arg);
+typedef bool (*qp_internal_byte_output_callback)(uint8_t byte, void* cb_arg);
bool qp_internal_decode_palette(painter_device_t device, uint32_t pixel_count, uint8_t bits_per_pixel, qp_internal_byte_input_callback input_callback, void* input_arg, qp_pixel_t* palette, qp_internal_pixel_output_callback output_callback, void* output_arg);
bool qp_internal_decode_grayscale(painter_device_t device, uint32_t pixel_count, uint8_t bits_per_pixel, qp_internal_byte_input_callback input_callback, void* input_arg, qp_internal_pixel_output_callback output_callback, void* output_arg);
bool qp_internal_decode_recolor(painter_device_t device, uint32_t pixel_count, uint8_t bits_per_pixel, qp_internal_byte_input_callback input_callback, void* input_arg, qp_pixel_t fg_hsv888, qp_pixel_t bg_hsv888, qp_internal_pixel_output_callback output_callback, void* output_arg);
+bool qp_internal_send_bytes(painter_device_t device, uint32_t byte_count, qp_internal_byte_input_callback input_callback, void* input_arg, qp_internal_byte_output_callback output_callback, void* output_arg);
// Global variable used for interpolated pixel lookup table.
#if QUANTUM_PAINTER_SUPPORTS_256_PALETTE
@@ -82,4 +84,12 @@ struct qp_internal_pixel_output_state {
bool qp_internal_pixel_appender(qp_pixel_t* palette, uint8_t index, void* cb_arg);
+struct qp_internal_byte_output_state {
+ painter_device_t device;
+ uint32_t byte_write_pos;
+ uint32_t max_bytes;
+};
+
+bool qp_internal_byte_appender(uint8_t byteval, void* cb_arg);
+
qp_internal_byte_input_callback qp_internal_prepare_input_state(struct qp_internal_byte_input_state* input_state, painter_compression_t compression);
diff --git a/quantum/painter/qp_draw_codec.c b/quantum/painter/qp_draw_codec.c
index 438dce3994..5d1cf7c52e 100644
--- a/quantum/painter/qp_draw_codec.c
+++ b/quantum/painter/qp_draw_codec.c
@@ -12,18 +12,19 @@ static const qp_pixel_t qp_pixel_white = {.hsv888 = {.h = 0, .s = 0, .v = 255}};
static const qp_pixel_t qp_pixel_black = {.hsv888 = {.h = 0, .s = 0, .v = 0}};
bool qp_internal_bpp_capable(uint8_t bits_per_pixel) {
-#if !(QUANTUM_PAINTER_SUPPORTS_256_PALETTE)
+#if !(QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS)
+# if !(QUANTUM_PAINTER_SUPPORTS_256_PALETTE)
if (bits_per_pixel > 4) {
qp_dprintf("qp_internal_decode_palette: image bpp greater than 4\n");
return false;
}
-#endif
+# endif
if (bits_per_pixel > 8) {
qp_dprintf("qp_internal_decode_palette: image bpp greater than 8\n");
return false;
}
-
+#endif
return true;
}
@@ -32,7 +33,7 @@ bool qp_internal_decode_palette(painter_device_t device, uint32_t pixel_count, u
const uint8_t pixels_per_byte = 8 / bits_per_pixel;
uint32_t remaining_pixels = pixel_count; // don't try to derive from byte_count, we may not use an entire byte
while (remaining_pixels > 0) {
- uint8_t byteval = input_callback(input_arg);
+ int16_t byteval = input_callback(input_arg);
if (byteval < 0) {
return false;
}
@@ -64,6 +65,21 @@ bool qp_internal_decode_recolor(painter_device_t device, uint32_t pixel_count, u
return qp_internal_decode_palette(device, pixel_count, bits_per_pixel, input_callback, input_arg, qp_internal_global_pixel_lookup_table, output_callback, output_arg);
}
+bool qp_internal_send_bytes(painter_device_t device, uint32_t byte_count, qp_internal_byte_input_callback input_callback, void* input_arg, qp_internal_byte_output_callback output_callback, void* output_arg) {
+ uint32_t remaining_bytes = byte_count;
+ while (remaining_bytes > 0) {
+ int16_t byteval = input_callback(input_arg);
+ if (byteval < 0) {
+ return false;
+ }
+ if (!output_callback(byteval, output_arg)) {
+ return false;
+ }
+ remaining_bytes -= 1;
+ }
+ return true;
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Progressive pull of bytes, push of pixels
@@ -128,6 +144,26 @@ bool qp_internal_pixel_appender(qp_pixel_t* palette, uint8_t index, void* cb_arg
return true;
}
+bool qp_internal_byte_appender(uint8_t byteval, void* cb_arg) {
+ struct qp_internal_byte_output_state* state = (struct qp_internal_byte_output_state*)cb_arg;
+ struct painter_driver_t* driver = (struct painter_driver_t*)state->device;
+
+ if (!driver->driver_vtable->append_pixdata(state->device, qp_internal_global_pixdata_buffer, state->byte_write_pos++, byteval)) {
+ return false;
+ }
+
+ // If we've hit the transmit limit, send out the entire buffer and reset the write position
+ if (state->byte_write_pos == state->max_bytes) {
+ struct painter_driver_t* driver = (struct painter_driver_t*)state->device;
+ if (!driver->driver_vtable->pixdata(state->device, qp_internal_global_pixdata_buffer, state->byte_write_pos * 8 / driver->native_bits_per_pixel)) {
+ return false;
+ }
+ state->byte_write_pos = 0;
+ }
+
+ return true;
+}
+
qp_internal_byte_input_callback qp_internal_prepare_input_state(struct qp_internal_byte_input_state* input_state, painter_compression_t compression) {
switch (compression) {
case IMAGE_UNCOMPRESSED:
diff --git a/quantum/painter/qp_draw_image.c b/quantum/painter/qp_draw_image.c
index e9b975f23a..fa80617242 100644
--- a/quantum/painter/qp_draw_image.c
+++ b/quantum/painter/qp_draw_image.c
@@ -151,7 +151,7 @@ static bool qp_drawimage_prepare_frame_for_stream_read(painter_device_t device,
qp_internal_invalidate_palette();
if (!qp_internal_bpp_capable(info->bpp)) {
- qp_dprintf("qp_drawimage_recolor: fail (image bpp too high (%d), check QUANTUM_PAINTER_SUPPORTS_256_PALETTE)\n", (int)info->bpp);
+ qp_dprintf("qp_drawimage_recolor: fail (image bpp too high (%d), check QUANTUM_PAINTER_SUPPORTS_256_PALETTE or QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS)\n", (int)info->bpp);
qp_comms_stop(device);
return false;
}
@@ -167,8 +167,10 @@ static bool qp_drawimage_prepare_frame_for_stream_read(painter_device_t device,
needs_pixconvert = true;
} else {
- // Interpolate from fg/bg
- needs_pixconvert = qp_internal_interpolate_palette(fg_hsv888, bg_hsv888, palette_entries);
+ if (info->bpp <= 8) {
+ // Interpolate from fg/bg
+ needs_pixconvert = qp_internal_interpolate_palette(fg_hsv888, bg_hsv888, palette_entries);
+ }
}
if (needs_pixconvert) {
@@ -260,15 +262,28 @@ static bool qp_drawimage_recolor_impl(painter_device_t device, uint16_t x, uint1
return false;
}
- // Set up the output state
- struct qp_internal_pixel_output_state output_state = {.device = device, .pixel_write_pos = 0, .max_pixels = qp_internal_num_pixels_in_buffer(device)};
-
- // Decode the pixel data and stream to the display
- bool ret = qp_internal_decode_palette(device, pixel_count, frame_info->bpp, input_callback, &input_state, qp_internal_global_pixel_lookup_table, qp_internal_pixel_appender, &output_state);
+ bool ret = false;
+ if (frame_info->bpp <= 8) {
+ // Set up the output state
+ struct qp_internal_pixel_output_state output_state = {.device = device, .pixel_write_pos = 0, .max_pixels = qp_internal_num_pixels_in_buffer(device)};
- // Any leftovers need transmission as well.
- if (ret && output_state.pixel_write_pos > 0) {
- ret &= driver->driver_vtable->pixdata(device, qp_internal_global_pixdata_buffer, output_state.pixel_write_pos);
+ // Decode the pixel data and stream to the display
+ ret = qp_internal_decode_palette(device, pixel_count, frame_info->bpp, input_callback, &input_state, qp_internal_global_pixel_lookup_table, qp_internal_pixel_appender, &output_state);
+ // Any leftovers need transmission as well.
+ if (ret && output_state.pixel_write_pos > 0) {
+ ret &= driver->driver_vtable->pixdata(device, qp_internal_global_pixdata_buffer, output_state.pixel_write_pos);
+ }
+ } else {
+ // Set up the output state
+ struct qp_internal_byte_output_state output_state = {.device = device, .byte_write_pos = 0, .max_bytes = qp_internal_num_pixels_in_buffer(device) * driver->native_bits_per_pixel / 8};
+
+ // Stream the raw pixel data to the display
+ uint32_t byte_count = pixel_count * frame_info->bpp / 8;
+ ret = qp_internal_send_bytes(device, byte_count, input_callback, &input_state, qp_internal_byte_appender, &output_state);
+ // Any leftovers need transmission as well.
+ if (ret && output_state.byte_write_pos > 0) {
+ ret &= driver->driver_vtable->pixdata(device, qp_internal_global_pixdata_buffer, output_state.byte_write_pos * 8 / driver->native_bits_per_pixel);
+ }
}
qp_dprintf("qp_drawimage_recolor: %s\n", ret ? "ok" : "fail");
@@ -399,3 +414,15 @@ void qp_internal_animation_tick(void) {
static uint32_t last_anim_exec = 0;
deferred_exec_advanced_task(animation_executors, QUANTUM_PAINTER_CONCURRENT_ANIMATIONS, &last_anim_exec);
}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Quantum Painter Core API: qp_internal_task
+
+void qp_internal_task(void) {
+ qp_internal_animation_tick();
+#ifdef QUANTUM_PAINTER_LVGL_INTEGRATION_ENABLE
+ // Run LVGL ticks
+ void qp_lvgl_internal_tick(void);
+ qp_lvgl_internal_tick();
+#endif
+}
diff --git a/quantum/painter/qp_draw_text.c b/quantum/painter/qp_draw_text.c
index 0f5473abd0..f9fb2bf08f 100644
--- a/quantum/painter/qp_draw_text.c
+++ b/quantum/painter/qp_draw_text.c
@@ -100,7 +100,7 @@ static painter_font_handle_t qp_load_font_internal(bool (*stream_factory)(qff_fo
qff_read_font_descriptor(&font->stream, &font->base.line_height, &font->has_ascii_table, &font->num_unicode_glyphs, &font->bpp, &font->has_palette, &font->compression_scheme, NULL);
if (!qp_internal_bpp_capable(font->bpp)) {
- qp_dprintf("qp_load_font: fail (image bpp too high (%d), check QUANTUM_PAINTER_SUPPORTS_256_PALETTE)\n", (int)font->bpp);
+ qp_dprintf("qp_load_font: fail (image bpp too high (%d), check QUANTUM_PAINTER_SUPPORTS_256_PALETTE or QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS)\n", (int)font->bpp);
qp_close_font((painter_font_handle_t)font);
return NULL;
}
diff --git a/quantum/painter/qp_internal_driver.h b/quantum/painter/qp_internal_driver.h
index 9e9d6bc848..82a0178a73 100644
--- a/quantum/painter/qp_internal_driver.h
+++ b/quantum/painter/qp_internal_driver.h
@@ -16,6 +16,7 @@ typedef bool (*painter_driver_viewport_func)(painter_device_t device, uint16_t l
typedef bool (*painter_driver_pixdata_func)(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count);
typedef bool (*painter_driver_convert_palette_func)(painter_device_t device, int16_t palette_size, qp_pixel_t *palette);
typedef bool (*painter_driver_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);
+typedef bool (*painter_driver_append_pixdata)(painter_device_t device, uint8_t *target_buffer, uint32_t pixdata_offset, uint8_t pixdata_byte);
// Driver vtable definition
struct painter_driver_vtable_t {
@@ -27,6 +28,7 @@ struct painter_driver_vtable_t {
painter_driver_pixdata_func pixdata;
painter_driver_convert_palette_func palette_convert;
painter_driver_append_pixels append_pixels;
+ painter_driver_append_pixdata append_pixdata;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/quantum/painter/qp_internal_formats.h b/quantum/painter/qp_internal_formats.h
index a4a86f0345..194f82b31a 100644
--- a/quantum/painter/qp_internal_formats.h
+++ b/quantum/painter/qp_internal_formats.h
@@ -44,6 +44,8 @@ typedef enum qp_image_format_t {
PALETTE_2BPP = 0x05,
PALETTE_4BPP = 0x06,
PALETTE_8BPP = 0x07,
+ RGB565_16BPP = 0x08,
+ RGB888_24BPP = 0x09,
} qp_image_format_t;
typedef enum painter_compression_t { IMAGE_UNCOMPRESSED, IMAGE_COMPRESSED_RLE } painter_compression_t;
diff --git a/quantum/painter/rules.mk b/quantum/painter/rules.mk
index 5ac374a96e..199e406dd6 100644
--- a/quantum/painter/rules.mk
+++ b/quantum/painter/rules.mk
@@ -2,6 +2,8 @@
QUANTUM_PAINTER_DRIVERS ?=
QUANTUM_PAINTER_ANIMATIONS_ENABLE ?= yes
+QUANTUM_PAINTER_LVGL_INTEGRATION ?= no
+
# The list of permissible drivers that can be listed in QUANTUM_PAINTER_DRIVERS
VALID_QUANTUM_PAINTER_DRIVERS := \
rgb565_surface \
@@ -152,3 +154,7 @@ ifeq ($(strip $(QUANTUM_PAINTER_NEEDS_COMMS_SPI)), yes)
endif
endif
+# Check if LVGL needs to be enabled
+ifeq ($(strip $(QUANTUM_PAINTER_LVGL_INTEGRATION)), yes)
+ include $(QUANTUM_DIR)/painter/lvgl/rules.mk
+endif