Skip to content

Commit 5e3e784

Browse files
authored
Merge pull request #72 from makermelissa/master
Add Bitfield Compression Support for GIMP
2 parents 4a2f5a6 + 09fece6 commit 5e3e784

File tree

2 files changed

+66
-11
lines changed

2 files changed

+66
-11
lines changed

adafruit_imageload/bmp/__init__.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ def load(
4444
`displayio.Palette`. Will be skipped if None"""
4545
file.seek(10)
4646
data_start = int.from_bytes(file.read(4), "little")
47-
# f.seek(14)
48-
# bmp_header_length = int.from_bytes(file.read(4), 'little')
47+
file.seek(14)
48+
bmp_header_length = int.from_bytes(file.read(4), "little")
4949
# print(bmp_header_length)
5050
file.seek(0x12) # Width of the bitmap in pixels
5151
_width = int.from_bytes(file.read(4), "little")
@@ -61,8 +61,18 @@ def load(
6161
compression = int.from_bytes(file.read(2), "little")
6262
file.seek(0x2E) # Number of colors in the color palette
6363
colors = int.from_bytes(file.read(4), "little")
64+
bitfield_masks = None
65+
if compression == 3 and bmp_header_length >= 56:
66+
bitfield_masks = {}
67+
endianess = "little" if color_depth == 16 else "big"
68+
file.seek(0x36)
69+
bitfield_masks["red"] = int.from_bytes(file.read(4), endianess)
70+
file.seek(0x3A)
71+
bitfield_masks["green"] = int.from_bytes(file.read(4), endianess)
72+
file.seek(0x3E)
73+
bitfield_masks["blue"] = int.from_bytes(file.read(4), endianess)
6474

65-
if compression > 2:
75+
if compression > 3:
6676
raise NotImplementedError("bitmask compression unsupported")
6777

6878
if colors == 0 and color_depth >= 16:
@@ -74,6 +84,7 @@ def load(
7484
_height,
7585
data_start,
7686
color_depth,
87+
bitfield_masks,
7788
bitmap=bitmap,
7889
)
7990
if colors == 0:

adafruit_imageload/bmp/truecolor.py

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,49 @@
1616
import sys
1717

1818
try:
19-
from typing import Tuple, Optional
19+
from typing import Union, Optional, Tuple
2020
from io import BufferedReader
21-
from displayio import Bitmap
2221
from ..displayio_types import BitmapConstructor
2322
except ImportError:
2423
pass
2524

26-
from displayio import ColorConverter, Colorspace
25+
from displayio import ColorConverter, Colorspace, Bitmap
2726

2827
__version__ = "0.0.0+auto.0"
2928
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git"
3029

30+
bitfield_colorspaces = (
31+
{ # 16-bit RGB555
32+
"mask_values": (0x00007C00, 0x000003E0, 0x0000001F),
33+
"color_space": Colorspace.RGB555,
34+
},
35+
{ # 16-bit RGB565
36+
"mask_values": (0x0000F800, 0x000007E0, 0x0000001F),
37+
"color_space": Colorspace.RGB565,
38+
},
39+
{ # 24 or 32-bit RGB888 (Alpha ignored for 32-bit)
40+
"mask_values": (0x0000FF00, 0x00FF0000, 0xFF000000),
41+
"color_space": Colorspace.RGB888,
42+
},
43+
)
44+
45+
46+
def bitfield_format(bitfield_mask):
47+
"""Returns the colorspace for the given bitfield mask"""
48+
mask = (bitfield_mask["red"], bitfield_mask["green"], bitfield_mask["blue"])
49+
for colorspace in bitfield_colorspaces:
50+
if colorspace["mask_values"] == mask:
51+
return colorspace["color_space"]
52+
return None
53+
3154

3255
def load(
3356
file: BufferedReader,
3457
width: int,
3558
height: int,
3659
data_start: int,
3760
color_depth: int,
61+
bitfield_masks: Union[dict, None],
3862
*,
3963
bitmap: Optional[BitmapConstructor] = None,
4064
) -> Tuple[Optional[Bitmap], Optional[ColorConverter]]:
@@ -46,6 +70,7 @@ def load(
4670
:param int height: Image height in pixels
4771
:param int data_start: Byte location where the data starts (after headers)
4872
:param int color_depth: Number of bits used to store a value
73+
:param dict bitfield_masks: The bitfield masks for each color if using bitfield compression
4974
:param BitmapConstructor bitmap: a function that returns a displayio.Bitmap
5075
"""
5176
# pylint: disable=too-many-arguments,too-many-locals,too-many-branches
@@ -55,7 +80,13 @@ def load(
5580
# Set up a ColorConverter object and set appropriate colorspace
5681
# to convert from based on the color depth
5782
input_colorspace = Colorspace.RGB888
58-
if color_depth == 16:
83+
if bitfield_masks is not None:
84+
colorspace = bitfield_format(bitfield_masks)
85+
if colorspace is not None:
86+
input_colorspace = colorspace
87+
else:
88+
raise NotImplementedError("Bitfield mask not supported")
89+
elif color_depth == 16:
5990
input_colorspace = Colorspace.RGB555
6091
converter_obj = ColorConverter(input_colorspace=input_colorspace)
6192
if sys.maxsize > 1073741823:
@@ -64,7 +95,7 @@ def load(
6495

6596
# convert unsigned int to signed int when height is negative
6697
height = negative_height_check(height)
67-
bitmap_obj = bitmap(width, abs(height), 65535)
98+
bitmap_obj = Bitmap(width, abs(height), 65535)
6899
file.seek(data_start)
69100
line_size = width * (color_depth // 8)
70101
# Set the seek direction based on whether the height value is negative or positive
@@ -84,10 +115,23 @@ def load(
84115

85116
for x in range(width):
86117
i = x * bytes_per_pixel
87-
if color_depth == 16:
88-
pixel = chunk[i] | chunk[i + 1] << 8
118+
if bitfield_masks is not None:
119+
color = 0
120+
for byte in range(bytes_per_pixel):
121+
color |= chunk[i + byte] << (8 * byte)
122+
mask = (
123+
bitfield_masks["red"]
124+
| bitfield_masks["green"]
125+
| bitfield_masks["blue"]
126+
)
127+
if color_depth in (24, 32):
128+
mask = mask >> 8
129+
pixel = color & mask
89130
else:
90-
pixel = chunk[i + 2] << 16 | chunk[i + 1] << 8 | chunk[i]
131+
if color_depth == 16:
132+
pixel = chunk[i] | chunk[i + 1] << 8
133+
else:
134+
pixel = chunk[i + 2] << 16 | chunk[i + 1] << 8 | chunk[i]
91135
bitmap_obj[offset + x] = converter_obj.convert(pixel)
92136

93137
return bitmap_obj, ColorConverter(input_colorspace=Colorspace.RGB565)

0 commit comments

Comments
 (0)