Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ overhead of the decoding code.
Usage Example
=============

.. literalinclude:: examples/imageload_simpletest.py
.. literalinclude:: ../examples/imageload_simpletest.py

Contributing
============
Expand Down
11 changes: 5 additions & 6 deletions adafruit_imageload/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,10 @@ def load(filename, *, bitmap=None, palette=None):
palette is the desired pallete type. The constructor should take the number of colors and
support assignment to indices via [].
"""
with open(filename, "rb") as f:
header = f.read(3)
with open(filename, "rb") as file:
header = file.read(3)
if header.startswith(b"BM"):
from . import bmp
f.seek(0)
return bmp.load(f, bitmap=bitmap, palette=palette)
else:
raise RuntimeError("Unsupported image format")
file.seek(0)
return bmp.load(file, bitmap=bitmap, palette=palette)
raise RuntimeError("Unsupported image format")
36 changes: 22 additions & 14 deletions adafruit_imageload/bmp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,33 @@
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git"

def load(f, *, bitmap=None, palette=None):
f.seek(10)
data_start = int.from_bytes(f.read(4), 'little')
def load(file, *, bitmap=None, palette=None):
"""Loads a bmp image from the open ``file``.
Returns tuple of bitmap object and palette object.
:param object bitmap: Type to store bitmap data. Must have API similar to `displayio.Bitmap`.
Will be skipped if None
:param object palette: Type to store the palette. Must have API similar to
`displayio.Palette`. Will be skipped if None"""
file.seek(10)
data_start = int.from_bytes(file.read(4), 'little')
# f.seek(14)
# bmp_header_length = int.from_bytes(f.read(4), 'little')
# bmp_header_length = int.from_bytes(file.read(4), 'little')
# print(bmp_header_length)
f.seek(18)
width = int.from_bytes(f.read(4), 'little')
height = int.from_bytes(f.read(4), 'little')
f.seek(28)
color_depth = int.from_bytes(f.read(2), 'little')
f.seek(46)
colors = int.from_bytes(f.read(4), 'little')

compute_palette = False
file.seek(18)
width = int.from_bytes(file.read(4), 'little')
height = int.from_bytes(file.read(4), 'little')
file.seek(28)
color_depth = int.from_bytes(file.read(2), 'little')
file.seek(46)
colors = int.from_bytes(file.read(4), 'little')

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These values all look correct to the spec.
Suggestion (no change needed):
It might be easier to maintain and check this section if the seek values are given in hex, so they match up with the documentation.

file.seek(0x12)  # Width of the bitmap in pixels
file.seek(0x1C)  # Number of bits per pixel 

if colors == 0 and color_depth >= 16:
raise NotImplementedError("True color BMP unsupported")
else:
if colors == 0:
colors = 2 ** color_depth
from . import indexed
return indexed.load(f, width, height, data_start, colors, color_depth, bitmap=bitmap, palette=palette)
return indexed.load(file, width, height, data_start, colors, color_depth, bitmap=bitmap,
palette=palette)
55 changes: 24 additions & 31 deletions adafruit_imageload/bmp/indexed.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,52 +32,45 @@
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git"

import math

def load(f, width, height, data_start, colors, color_depth, *, bitmap=None, palette=None):
def load(file, width, height, data_start, colors, color_depth, *, bitmap=None, palette=None):
"""Loads indexed bitmap data into bitmap and palette objects.

:param file file: The open bmp file
:param int width: Image width in pixels
:param int height: Image height in pixels
:param int data_start: Byte location where the data starts (after headers)
:param int colors: Number of distinct colors in the image
:param int color_depth: Number of bits used to store a value"""
# pylint: disable=too-many-arguments,too-many-locals
if palette:
palette = palette(colors)

f.seek(data_start - colors * 4)
for color in range(colors):
c = f.read(4)
palette[color] = c
file.seek(data_start - colors * 4)
for value in range(colors):
palette[value] = file.read(4)

if bitmap:
minimum_color_depth = 1
while colors > 2 ** minimum_color_depth:
minimum_color_depth *= 2

bitmap = bitmap(width, height, colors)
f.seek(data_start)
file.seek(data_start)
line_size = width // (8 // color_depth)
if line_size % 4 != 0:
line_size += (4 - line_size % 4)

packed_pixels = None
if color_depth != minimum_color_depth and minimum_color_depth == 2:
target_line_size = width // 4
if target_line_size % 4 != 0:
target_line_size += (4 - target_line_size % 4)

packed_pixels = bytearray(target_line_size)

for line in range(height-1,-1,-1):
chunk = f.read(line_size)
if packed_pixels:
original_pixels_per_byte = 8 // color_depth
packed_pixels_per_byte = 8 // minimum_color_depth

for i in range(width // packed_pixels_per_byte):
packed_pixels[i] = 0
chunk = bytearray(line_size)
mask = (1 << minimum_color_depth) - 1

for i in range(width):
pi = i // packed_pixels_per_byte
ci = i // original_pixels_per_byte
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))
for y in range(height - 1, -1, -1):
file.readinto(chunk)
pixels_per_byte = 8 // color_depth
offset = y * width

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

return bitmap, palette
6 changes: 0 additions & 6 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,9 @@ Table of Contents
.. toctree::
:caption: Tutorials

.. todo:: Add any Learn guide links here. If there are none, then simply delete this todo and leave
the toctree above for use later.

.. toctree::
:caption: Related Products

.. todo:: Add any product links here. If there are none, then simply delete this todo and leave
the toctree above for use later.

.. toctree::
:caption: Other Links

Expand Down
4 changes: 3 additions & 1 deletion examples/imageload_simpletest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import displayio
import adafruit_imageload

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