summary refs log tree commit diff
path: root/lib/python
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python')
-rw-r--r--lib/python/qmk/painter.py80
1 files changed, 75 insertions, 5 deletions
diff --git a/lib/python/qmk/painter.py b/lib/python/qmk/painter.py
index d0cc1dddec..7ecdc55404 100644
--- a/lib/python/qmk/painter.py
+++ b/lib/python/qmk/painter.py
@@ -7,6 +7,20 @@ from PIL import Image, ImageOps
 
 # The list of valid formats Quantum Painter supports
 valid_formats = {
+    'rgb888': {
+        'image_format': 'IMAGE_FORMAT_RGB888',
+        'bpp': 24,
+        'has_palette': False,
+        'num_colors': 16777216,
+        'image_format_byte': 0x09,  # see qp_internal_formats.h
+    },
+    'rgb565': {
+        'image_format': 'IMAGE_FORMAT_RGB565',
+        'bpp': 16,
+        'has_palette': False,
+        'num_colors': 65536,
+        'image_format_byte': 0x08,  # see qp_internal_formats.h
+    },
     'pal256': {
         'image_format': 'IMAGE_FORMAT_PALETTE',
         'bpp': 8,
@@ -144,19 +158,33 @@ def convert_requested_format(im, format):
     ncolors = format["num_colors"]
     image_format = format["image_format"]
 
-    # Ensure we have a valid number of colors for the palette
-    if ncolors <= 0 or ncolors > 256 or (ncolors & (ncolors - 1) != 0):
-        raise ValueError("Number of colors must be 2, 4, 16, or 256.")
-
     # Work out where we're getting the bytes from
     if image_format == 'IMAGE_FORMAT_GRAYSCALE':
+        # Ensure we have a valid number of colors for the palette
+        if ncolors <= 0 or ncolors > 256 or (ncolors & (ncolors - 1) != 0):
+            raise ValueError("Number of colors must be 2, 4, 16, or 256.")
         # If mono, convert input to grayscale, then to RGB, then grab the raw bytes corresponding to the intensity of the red channel
         im = ImageOps.grayscale(im)
         im = im.convert("RGB")
     elif image_format == 'IMAGE_FORMAT_PALETTE':
+        # Ensure we have a valid number of colors for the palette
+        if ncolors <= 0 or ncolors > 256 or (ncolors & (ncolors - 1) != 0):
+            raise ValueError("Number of colors must be 2, 4, 16, or 256.")
         # If color, convert input to RGB, palettize based on the supplied number of colors, then get the raw palette bytes
         im = im.convert("RGB")
         im = im.convert("P", palette=Image.ADAPTIVE, colors=ncolors)
+    elif image_format == 'IMAGE_FORMAT_RGB565':
+        # Ensure we have a valid number of colors for the palette
+        if ncolors != 65536:
+            raise ValueError("Number of colors must be 65536.")
+        # If color, convert input to RGB
+        im = im.convert("RGB")
+    elif image_format == 'IMAGE_FORMAT_RGB888':
+        # Ensure we have a valid number of colors for the palette
+        if ncolors != 1677216:
+            raise ValueError("Number of colors must be 16777216.")
+        # If color, convert input to RGB
+        im = im.convert("RGB")
 
     return im
 
@@ -170,8 +198,12 @@ def convert_image_bytes(im, format):
     image_format = format["image_format"]
     shifter = int(math.log2(ncolors))
     pixels_per_byte = int(8 / math.log2(ncolors))
+    bytes_per_pixel = math.ceil(math.log2(ncolors) / 8)
     (width, height) = im.size
-    expected_byte_count = ((width * height) + (pixels_per_byte - 1)) // pixels_per_byte
+    if (pixels_per_byte != 0):
+        expected_byte_count = ((width * height) + (pixels_per_byte - 1)) // pixels_per_byte
+    else:
+        expected_byte_count = width * height * bytes_per_pixel
 
     if image_format == 'IMAGE_FORMAT_GRAYSCALE':
         # Take the red channel
@@ -212,6 +244,44 @@ def convert_image_bytes(im, format):
                     byte = byte | ((image_bytes[byte_offset] & (ncolors - 1)) << int(n * shifter))
             bytearray.append(byte)
 
+    if image_format == 'IMAGE_FORMAT_RGB565':
+        # Take the red, green, and blue channels
+        image_bytes_red = im.tobytes("raw", "R")
+        image_bytes_green = im.tobytes("raw", "G")
+        image_bytes_blue = im.tobytes("raw", "B")
+        image_pixels_len = len(image_bytes_red)
+
+        # No palette
+        palette = None
+
+        bytearray = []
+        for x in range(image_pixels_len):
+            # 5 bits of red, 3 MSb of green
+            byte = ((image_bytes_red[x] >> 3 & 0x1F) << 3) + (image_bytes_green[x] >> 5 & 0x07)
+            bytearray.append(byte)
+            # 3 LSb of green, 5 bits of blue
+            byte = ((image_bytes_green[x] >> 2 & 0x07) << 5) + (image_bytes_blue[x] >> 3 & 0x1F)
+            bytearray.append(byte)
+
+    if image_format == 'IMAGE_FORMAT_RGB888':
+        # Take the red, green, and blue channels
+        image_bytes_red = im.tobytes("raw", "R")
+        image_bytes_green = im.tobytes("raw", "G")
+        image_bytes_blue = im.tobytes("raw", "B")
+        image_pixels_len = len(image_bytes_red)
+
+        # No palette
+        palette = None
+
+        bytearray = []
+        for x in range(image_pixels_len):
+            byte = image_bytes_red[x]
+            bytearray.append(byte)
+            byte = image_bytes_green[x]
+            bytearray.append(byte)
+            byte = image_bytes_blue[x]
+            bytearray.append(byte)
+
     if len(bytearray) != expected_byte_count:
         raise Exception(f"Wrong byte count, was {len(bytearray)}, expected {expected_byte_count}")