Skip to content

Commit 4726061

Browse files
authored
Merge pull request #7 from tannewt/use_bitmap_subscr
Simplify load and use [] on the bitmap
2 parents f4c78f7 + 8d93f80 commit 4726061

File tree

9 files changed

+87
-63
lines changed

9 files changed

+87
-63
lines changed

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ overhead of the decoding code.
2020
Usage Example
2121
=============
2222

23-
.. literalinclude:: examples/imageload_simpletest.py
23+
.. literalinclude:: ../examples/imageload_simpletest.py
2424

2525
Contributing
2626
============

adafruit_imageload/__init__.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,10 @@ def load(filename, *, bitmap=None, palette=None):
4141
palette is the desired pallete type. The constructor should take the number of colors and
4242
support assignment to indices via [].
4343
"""
44-
with open(filename, "rb") as f:
45-
header = f.read(3)
44+
with open(filename, "rb") as file:
45+
header = file.read(3)
4646
if header.startswith(b"BM"):
4747
from . import bmp
48-
f.seek(0)
49-
return bmp.load(f, bitmap=bitmap, palette=palette)
50-
else:
51-
raise RuntimeError("Unsupported image format")
48+
file.seek(0)
49+
return bmp.load(file, bitmap=bitmap, palette=palette)
50+
raise RuntimeError("Unsupported image format")

adafruit_imageload/bmp/__init__.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,33 @@
3232
__version__ = "0.0.0-auto.0"
3333
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git"
3434

35-
def load(f, *, bitmap=None, palette=None):
36-
f.seek(10)
37-
data_start = int.from_bytes(f.read(4), 'little')
35+
def load(file, *, bitmap=None, palette=None):
36+
"""Loads a bmp image from the open ``file``.
37+
38+
Returns tuple of bitmap object and palette object.
39+
40+
:param object bitmap: Type to store bitmap data. Must have API similar to `displayio.Bitmap`.
41+
Will be skipped if None
42+
:param object palette: Type to store the palette. Must have API similar to
43+
`displayio.Palette`. Will be skipped if None"""
44+
file.seek(10)
45+
data_start = int.from_bytes(file.read(4), 'little')
3846
# f.seek(14)
39-
# bmp_header_length = int.from_bytes(f.read(4), 'little')
47+
# bmp_header_length = int.from_bytes(file.read(4), 'little')
4048
# print(bmp_header_length)
41-
f.seek(18)
42-
width = int.from_bytes(f.read(4), 'little')
43-
height = int.from_bytes(f.read(4), 'little')
44-
f.seek(28)
45-
color_depth = int.from_bytes(f.read(2), 'little')
46-
f.seek(46)
47-
colors = int.from_bytes(f.read(4), 'little')
48-
49-
compute_palette = False
49+
file.seek(0x12) # Width of the bitmap in pixels
50+
width = int.from_bytes(file.read(4), 'little')
51+
height = int.from_bytes(file.read(4), 'little')
52+
file.seek(0x1c) # Number of bits per pixel
53+
color_depth = int.from_bytes(file.read(2), 'little')
54+
file.seek(0x2e) # Number of colors in the color palette
55+
colors = int.from_bytes(file.read(4), 'little')
56+
5057
if colors == 0 and color_depth >= 16:
5158
raise NotImplementedError("True color BMP unsupported")
5259
else:
5360
if colors == 0:
5461
colors = 2 ** color_depth
5562
from . import indexed
56-
return indexed.load(f, width, height, data_start, colors, color_depth, bitmap=bitmap, palette=palette)
63+
return indexed.load(file, width, height, data_start, colors, color_depth, bitmap=bitmap,
64+
palette=palette)

adafruit_imageload/bmp/indexed.py

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -32,52 +32,50 @@
3232
__version__ = "0.0.0-auto.0"
3333
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git"
3434

35-
import math
36-
37-
def load(f, width, height, data_start, colors, color_depth, *, bitmap=None, palette=None):
35+
def load(file, width, height, data_start, colors, color_depth, *, bitmap=None, palette=None):
36+
"""Loads indexed bitmap data into bitmap and palette objects.
37+
38+
:param file file: The open bmp file
39+
:param int width: Image width in pixels
40+
:param int height: Image height in pixels
41+
:param int data_start: Byte location where the data starts (after headers)
42+
:param int colors: Number of distinct colors in the image
43+
:param int color_depth: Number of bits used to store a value"""
44+
# pylint: disable=too-many-arguments,too-many-locals
3845
if palette:
3946
palette = palette(colors)
4047

41-
f.seek(data_start - colors * 4)
42-
for color in range(colors):
43-
c = f.read(4)
44-
palette[color] = c
48+
file.seek(data_start - colors * 4)
49+
for value in range(colors):
50+
c_bytes = file.read(4)
51+
# Need to swap red & blue bytes (bytes 0 and 2)
52+
palette[value] = bytes(b''.join([c_bytes[2:3],
53+
c_bytes[1:2],
54+
c_bytes[0:1],
55+
c_bytes[3:1]]))
4556

4657
if bitmap:
4758
minimum_color_depth = 1
4859
while colors > 2 ** minimum_color_depth:
4960
minimum_color_depth *= 2
5061

5162
bitmap = bitmap(width, height, colors)
52-
f.seek(data_start)
63+
file.seek(data_start)
5364
line_size = width // (8 // color_depth)
5465
if line_size % 4 != 0:
5566
line_size += (4 - line_size % 4)
5667

57-
packed_pixels = None
58-
if color_depth != minimum_color_depth and minimum_color_depth == 2:
59-
target_line_size = width // 4
60-
if target_line_size % 4 != 0:
61-
target_line_size += (4 - target_line_size % 4)
62-
63-
packed_pixels = bytearray(target_line_size)
64-
65-
for line in range(height-1,-1,-1):
66-
chunk = f.read(line_size)
67-
if packed_pixels:
68-
original_pixels_per_byte = 8 // color_depth
69-
packed_pixels_per_byte = 8 // minimum_color_depth
70-
71-
for i in range(width // packed_pixels_per_byte):
72-
packed_pixels[i] = 0
68+
chunk = bytearray(line_size)
69+
mask = (1 << minimum_color_depth) - 1
7370

74-
for i in range(width):
75-
pi = i // packed_pixels_per_byte
76-
ci = i // original_pixels_per_byte
77-
packed_pixels[pi] |= ((chunk[ci] >> (8 - color_depth*(i % original_pixels_per_byte + 1))) & 0x3) << (8 - minimum_color_depth*(i % packed_pixels_per_byte + 1))
71+
for y in range(height - 1, -1, -1):
72+
file.readinto(chunk)
73+
pixels_per_byte = 8 // color_depth
74+
offset = y * width
7875

79-
bitmap._load_row(line, packed_pixels)
80-
else:
81-
bitmap._load_row(line, chunk)
76+
for x in range(width):
77+
i = x // pixels_per_byte
78+
pixel = (chunk[i] >> (8 - color_depth*(x % pixels_per_byte + 1))) & mask
79+
bitmap[offset + x] = pixel
8280

8381
return bitmap, palette

docs/developing.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,16 @@ The corresponding Bitmap to the example above appears like this after loading::
102102
4 4 4 5 5 5 12 12 12 5 5 5 7 7 7
103103
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
104104

105-
This grid represents the example image (`15 pixels wide` and `17 pixels tall`).
106-
The coordinates are arranged in a zero indexed grid, starting in the top left at `[0,0]`,
107-
and continuing down and to the right to a final coordinate of `[14,16]`.
105+
This grid represents the example image (``15 pixels wide`` and ``17 pixels tall``).
106+
The coordinates are arranged in a zero indexed grid, starting in the top left at ``[0,0]``,
107+
and continuing down and to the right to a final coordinate of ``[14,16]``.
108108

109109

110110
The value at each position is an integer, representing an entry in the palette object.
111111

112112

113113

114-
For example, the Bitmap coordinate `[0,0]` has the value (integer) `5`.
114+
For example, the Bitmap coordinate ``[0,0]`` has the value (integer) ``5``.
115115

116116

117-
This corresponds to the the Palette object's, `[5]` which is `b'\x00\x00\xff\x00'`. This is a byte string that represents a color.
117+
This corresponds to the the Palette object's, ``[5]`` which is ``b'\x00\x00\xff\x00'``. This is a byte string that represents a color.

docs/index.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ Table of Contents
2323
.. toctree::
2424
:caption: Tutorials
2525

26-
.. todo:: Add any Learn guide links here. If there are none, then simply delete this todo and leave
27-
the toctree above for use later.
26+
.. toctree::
27+
:caption: Development
28+
:maxdepth: 3
29+
30+
developing
2831

2932
.. toctree::
3033
:caption: Related Products
3134

32-
.. todo:: Add any product links here. If there are none, then simply delete this todo and leave
33-
the toctree above for use later.
34-
3535
.. toctree::
3636
:caption: Other Links
3737

examples/imageload_colorwheel.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import board
2+
import displayio
3+
import adafruit_imageload
4+
5+
display = board.DISPLAY
6+
7+
bitmap, palette = adafruit_imageload.load("images/color_wheel.bmp",
8+
bitmap=displayio.Bitmap,
9+
palette=displayio.Palette)
10+
11+
tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette)
12+
13+
group = displayio.Group()
14+
group.append(tile_grid)
15+
display.show(group)
16+
while True:
17+
pass

examples/imageload_simpletest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import displayio
22
import adafruit_imageload
33

4-
image, palette = adafruit_imageload.load("images/4bit.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette)
4+
image, palette = adafruit_imageload.load("images/4bit.bmp",
5+
bitmap=displayio.Bitmap,
6+
palette=displayio.Palette)

examples/images/color_wheel.bmp

76.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)