Skip to content
Permalink
Browse files

Merge branch 'master' of github.com:adafruit/Adafruit_CircuitPython_I…

…mageLoad into bugfix-fix-the-build-sphinx
  • Loading branch information...
Matt Land
Matt Land committed May 15, 2019
2 parents b7252bc + 4726061 commit 0ee79821e4dea46c42b67101b44ed573a35c801c
@@ -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")
@@ -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')

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)
@@ -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
@@ -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.
@@ -21,14 +21,15 @@ Table of Contents
api

.. toctree::
:caption: Developing
:caption: Tutorials

developing
CircuitPython displayio Setup for TFT FeatherWings <https://learn.adafruit.com/using-circuitpython-displayio-with-a-tft-featherwing>

.. toctree::
:caption: Tutorials
:caption: Development
:maxdepth: 3

CircuitPython displayio Setup for TFT FeatherWings <https://learn.adafruit.com/using-circuitpython-displayio-with-a-tft-featherwing>
developing

.. toctree::
:caption: Related Products
@@ -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
@@ -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 not shown.

0 comments on commit 0ee7982

Please sign in to comment.
You can’t perform that action at this time.