Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Simplify load and use [] on the bitmap #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
May 14, 2019
Merged
Show file tree
Hide file tree
Changes from all 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(0x12) # Width of the bitmap in pixels
width = int.from_bytes(file.read(4), 'little')
height = int.from_bytes(file.read(4), 'little')
file.seek(0x1c) # Number of bits per pixel
color_depth = int.from_bytes(file.read(2), 'little')
file.seek(0x2e) # Number of colors in the color palette
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)
60 changes: 29 additions & 31 deletions adafruit_imageload/bmp/indexed.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,52 +32,50 @@
__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):
c_bytes = file.read(4)
# Need to swap red & blue bytes (bytes 0 and 2)
palette[value] = bytes(b''.join([c_bytes[2:3],
c_bytes[1:2],
c_bytes[0:1],
c_bytes[3:1]]))

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
10 changes: 5 additions & 5 deletions docs/developing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,16 @@ The corresponding Bitmap to the example above appears like this after loading::
4 4 4 5 5 5 12 12 12 5 5 5 7 7 7
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5

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


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



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


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.
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.
10 changes: 5 additions & 5 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ 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: Development
:maxdepth: 3

developing

.. 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
17 changes: 17 additions & 0 deletions examples/imageload_colorwheel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import board
import displayio
import adafruit_imageload

display = board.DISPLAY

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

tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette)

group = displayio.Group()
group.append(tile_grid)
display.show(group)
while True:
pass
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)
Binary file added examples/images/color_wheel.bmp
Binary file not shown.