Skip to content

Commit 5f6acbb

Browse files
committed
allow usage of ColorConverter with Bitmap saving
1 parent 53b2b2a commit 5f6acbb

File tree

1 file changed

+91
-6
lines changed

1 file changed

+91
-6
lines changed

adafruit_bitmapsaver.py

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import gc
3232
import struct
3333
import board
34-
from displayio import Bitmap, Palette, Display
34+
from displayio import Bitmap, Palette, Display, ColorConverter
3535

3636
try:
3737
from typing import Tuple, Optional, Union
@@ -81,11 +81,88 @@ def _rgb565_to_bgr_tuple(color: int) -> Tuple[int, int, int]:
8181
return blue, green, red
8282

8383

84+
def rgb565_unpack(packed_rgb: int) -> tuple[int, int, int]:
85+
"""
86+
Convert an int representing a hex rgb565 color into a tuple
87+
of it's r, g, and b values.
88+
:param packed_rgb: rgb565 color value as an int
89+
:return: Tuple with r, g, and b values
90+
"""
91+
r = (packed_rgb >> 11) & 0x1F
92+
g = (packed_rgb >> 5) & 0x3F
93+
b = packed_rgb & 0x1F
94+
return (r, g, b)
95+
96+
97+
def rgb565_pack(r: int, g: int, b: int) -> int:
98+
"""
99+
Convert a tuple with r, g, and b values into an rgb565
100+
color value represented as an integer
101+
:param r: red value
102+
:param g: green value
103+
:param b: blue value
104+
:return int: rgb565 value
105+
"""
106+
return ((r & 0x1F) << 11) | ((g & 0x3F) << 5) | (b & 0x1F)
107+
108+
109+
def rgb888_unpack(packed_rgb: int) -> Tuple[int, int, int]:
110+
"""
111+
Convert an int representing an rgb888 color value into a
112+
tuple containing it's r, g, and b values.
113+
:param packed_rgb: rgb888 integer color value
114+
:return: Tuple containing r, g, and b values
115+
"""
116+
r = (packed_rgb >> 16) & 0xFF
117+
g = (packed_rgb >> 8) & 0xFF
118+
b = packed_rgb & 0xFF
119+
return (r, g, b)
120+
121+
122+
def rgb888_pack(r, g, b):
123+
"""
124+
Convert a tuple with r, g, and b values into an rgb888
125+
color value represented as an integer
126+
:param r: red value
127+
:param g: green value
128+
:param b: blue value
129+
:return int: rgb888 value
130+
"""
131+
return ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF)
132+
133+
134+
def rgb565_to_rgb888(rgb565):
135+
"""
136+
Convert from an integer representing rgb565 color into an integer
137+
representing rgb888 color.
138+
:param rgb565: Color to convert
139+
:return int: rgb888 color value
140+
"""
141+
# Shift the red value to the right by 11 bits.
142+
red5 = rgb565 >> 11
143+
# Shift the green value to the right by 5 bits and extract the lower 6 bits.
144+
green6 = (rgb565 >> 5) & 0b111111
145+
# Extract the lower 5 bits for blue.
146+
blue5 = rgb565 & 0b11111
147+
148+
# Convert 5-bit red to 8-bit red.
149+
red8 = round(red5 / 31 * 255)
150+
# Convert 6-bit green to 8-bit green.
151+
green8 = round(green6 / 63 * 255)
152+
# Convert 5-bit blue to 8-bit blue.
153+
blue8 = round(blue5 / 31 * 255)
154+
155+
# Combine the RGB888 values into a single integer
156+
rgb888_value = (red8 << 16) | (green8 << 8) | blue8
157+
158+
return rgb888_value
159+
160+
84161
# pylint:disable=too-many-locals
85162
def _write_pixels(
86163
output_file: BufferedWriter,
87164
pixel_source: Union[Bitmap, Display],
88-
palette: Optional[Palette],
165+
palette: Optional[Union[Palette, ColorConverter]],
89166
) -> None:
90167
saving_bitmap = isinstance(pixel_source, Bitmap)
91168
width, height = _rotated_height_and_width(pixel_source)
@@ -97,7 +174,13 @@ def _write_pixels(
97174
# pixel_source: Bitmap
98175
for x in range(width):
99176
pixel = pixel_source[x, y - 1]
100-
color = palette[pixel] # handled by save_pixel's guardians
177+
if isinstance(palette, Palette):
178+
color = palette[pixel] # handled by save_pixel's guardians
179+
elif isinstance(palette, ColorConverter):
180+
converted = palette.convert(pixel)
181+
converted_888 = rgb565_to_rgb888(converted)
182+
color = converted_888
183+
101184
for _ in range(3):
102185
row_buffer[buffer_index] = color & 0xFF
103186
color >>= 8
@@ -124,7 +207,7 @@ def _write_pixels(
124207
def save_pixels(
125208
file_or_filename: Union[str, BufferedWriter],
126209
pixel_source: Union[Display, Bitmap] = None,
127-
palette: Optional[Palette] = None,
210+
palette: Optional[Union[Palette, ColorConverter]] = None,
128211
) -> None:
129212
"""Save pixels to a 24 bit per pixel BMP file.
130213
If pixel_source if a displayio.Bitmap, save it's pixels through palette.
@@ -140,8 +223,10 @@ def save_pixels(
140223
pixel_source = board.DISPLAY
141224

142225
if isinstance(pixel_source, Bitmap):
143-
if not isinstance(palette, Palette):
144-
raise ValueError("Third argument must be a Palette for a Bitmap save")
226+
if not isinstance(palette, Palette) and not isinstance(palette, ColorConverter):
227+
raise ValueError(
228+
"Third argument must be a Palette or ColorConverter for a Bitmap save"
229+
)
145230
elif not isinstance(pixel_source, Display):
146231
raise ValueError("Second argument must be a Bitmap or Display")
147232
try:

0 commit comments

Comments
 (0)