Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
badgie/convert.py
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
136 lines (103 sloc)
4.02 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Converts images into a format suitable for display on Badger 2040. | |
Optionally resizes images to 296x128 to fit the display. | |
Crunches images down to dithered, 1bit colour depth. | |
Outputs either in raw binary format or as a .py file for embedding into MicroPython. | |
Output to py functionality is borrwed from data_to_py.py, Copyright (c) 2016 Peter Hinch | |
""" | |
import io | |
import argparse | |
from PIL import Image, ImageEnhance | |
from pathlib import Path | |
PY_HEADER = """# Code generated by convert.py. | |
""" | |
PY_FOOTER = """_mvdata = memoryview(_data) | |
def data(): | |
return _mvdata | |
""" | |
parser = argparse.ArgumentParser(description='Converts images into the format used by Badger2040.') | |
parser.add_argument('file', nargs="+", help='input files to convert') | |
parser.add_argument('--out_dir', type=Path, default=None, help='output directory') | |
parser.add_argument('--binary', action="store_true", help='output binary file for MicroPython') | |
parser.add_argument('--py', action="store_true", help='output .py file for MicroPython embedding') | |
parser.add_argument('--resize', action="store_true", help='force images to 296x128 pixels') | |
options = parser.parse_args() | |
class ByteWriter(object): | |
bytes_per_line = 16 | |
def __init__(self, stream, varname): | |
self.stream = stream | |
self.stream.write('{} =\\\n'.format(varname)) | |
self.bytecount = 0 # For line breaks | |
def _eol(self): | |
self.stream.write("'\\\n") | |
def _eot(self): | |
self.stream.write("'\n") | |
def _bol(self): | |
self.stream.write("b'") | |
# Output a single byte | |
def obyte(self, data): | |
if not self.bytecount: | |
self._bol() | |
self.stream.write('\\x{:02x}'.format(data)) | |
self.bytecount += 1 | |
self.bytecount %= self.bytes_per_line | |
if not self.bytecount: | |
self._eol() | |
# Output from a sequence | |
def odata(self, bytelist): | |
for byt in bytelist: | |
self.obyte(byt) | |
# ensure a correct final line | |
def eot(self): # User force EOL if one hasn't occurred | |
if self.bytecount: | |
self._eot() | |
self.stream.write('\n') | |
def convert_image(img): | |
if options.resize: | |
img = img.resize((296, 128)) # resize | |
try: | |
enhancer = ImageEnhance.Contrast(img) | |
img = enhancer.enhance(2.0) | |
except ValueError: | |
pass | |
img = img.convert("1") # convert to black and white | |
return img | |
def write_stream(header, footer, ip_stream, op_stream): | |
op_stream.write(header) | |
op_stream.write('\n') | |
data = ip_stream.read() | |
bw_data = ByteWriter(op_stream, '_data') | |
bw_data.odata(data) | |
bw_data.eot() | |
op_stream.write(footer) | |
# create map of images based on input filenames | |
for input_filename in options.file: | |
with Image.open(input_filename) as img: | |
img = convert_image(img) | |
image_name = Path(input_filename).stem | |
w, h = img.size | |
output_data = [~b & 0xff for b in list(img.tobytes())] | |
if options.binary: | |
if options.out_dir is not None: | |
output_filename = (options.out_dir / image_name).with_suffix(".bin") | |
else: | |
output_filename = Path(input_filename).with_suffix(".bin") | |
print(f"Saving to {output_filename}, {w}x{h}") | |
with open(output_filename, "wb") as out: | |
out.write(bytearray(output_data)) | |
elif options.py: | |
if options.out_dir is not None: | |
output_filename = (options.out_dir / image_name).with_suffix(".py") | |
else: | |
output_filename = Path(input_filename).with_suffix(".py") | |
print(f"Saving to {output_filename}, {w}x{h}") | |
with open(output_filename, "w") as out: | |
write_stream(PY_HEADER, PY_FOOTER, io.BytesIO(bytes(output_data)), out) | |
else: | |
image_code = '''\ | |
static const uint8_t {image_name}[{count}] = {{ | |
{byte_data} | |
}}; | |
'''.format(image_name=image_name, count=len(output_data), byte_data=", ".join(str(b) for b in output_data)) | |
print(image_code) |