summary refs log tree commit diff
path: root/quantum
diff options
context:
space:
mode:
authorDavid Hoelscher <infinityis@users.noreply.github.com>2023-01-14 04:24:54 -0600
committerGitHub <noreply@github.com>2023-01-14 21:24:54 +1100
commit45851a10f66119ceff3baadde27f68e287eff481 (patch)
treefe482cc85723cbf6a626ddb2f346d916f60ce29b /quantum
parent5873fbe5690a008548c258840b273d0712495ed4 (diff)
Add RGB565 and RGB888 color support to Quantum Painter (#19382)
Diffstat (limited to 'quantum')
-rw-r--r--quantum/painter/qgf.c4
-rw-r--r--quantum/painter/qp.c2
-rw-r--r--quantum/painter/qp.h8
-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.c37
-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
9 files changed, 93 insertions, 18 deletions
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 e5f595d71d..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
 
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 943cbfef5b..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");
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;