core: refactor png2toi/toi2png tools into one toif_convert usable as a library

unify filename extensions
pull/249/head
Pavol Rusnak 5 years ago
parent b7d1404803
commit 9cb9653c27
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D

@ -17,8 +17,8 @@ All multibyte integer values are little endian!
TOI currently supports 2 variants:
* `f`: full-color, file extension `.toif`
* `g`: gray-scale, file extension `.toig`
* `f`: full-color
* `g`: gray-scale
### Full-color
@ -55,5 +55,4 @@ zdata = zdata[2:-4] # strip header and checksum
## Tools
* [png2toi](../tools/png2toi) - tool for converting PNG into TOI format
* [toi2png](../tools/toi2png) - tool for converting TOI into PNG
* [toif_convert](../tools/toif_convert) - tool for converting PNGs into TOI format and back

@ -47,22 +47,22 @@ BG = BLACK
FG = WHITE
# icons
ICON_RESET = "trezor/res/header_icons/reset.toig"
ICON_WIPE = "trezor/res/header_icons/wipe.toig"
ICON_RECOVERY = "trezor/res/header_icons/recovery.toig"
ICON_NOCOPY = "trezor/res/header_icons/nocopy.toig"
ICON_WRONG = "trezor/res/header_icons/wrong.toig"
ICON_CONFIG = "trezor/res/header_icons/cog.toig"
ICON_RECEIVE = "trezor/res/header_icons/receive.toig"
ICON_SEND = "trezor/res/header_icons/send.toig"
ICON_RESET = "trezor/res/header_icons/reset.toif"
ICON_WIPE = "trezor/res/header_icons/wipe.toif"
ICON_RECOVERY = "trezor/res/header_icons/recovery.toif"
ICON_NOCOPY = "trezor/res/header_icons/nocopy.toif"
ICON_WRONG = "trezor/res/header_icons/wrong.toif"
ICON_CONFIG = "trezor/res/header_icons/cog.toif"
ICON_RECEIVE = "trezor/res/header_icons/receive.toif"
ICON_SEND = "trezor/res/header_icons/send.toif"
ICON_DEFAULT = ICON_CONFIG
ICON_CANCEL = "trezor/res/cancel.toig"
ICON_CONFIRM = "trezor/res/confirm.toig"
ICON_LOCK = "trezor/res/lock.toig"
ICON_CLICK = "trezor/res/click.toig"
ICON_BACK = "trezor/res/left.toig"
ICON_SWIPE = "trezor/res/swipe.toig"
ICON_CHECK = "trezor/res/check.toig"
ICON_SPACE = "trezor/res/space.toig"
ICON_CANCEL = "trezor/res/cancel.toif"
ICON_CONFIRM = "trezor/res/confirm.toif"
ICON_LOCK = "trezor/res/lock.toif"
ICON_CLICK = "trezor/res/click.toif"
ICON_BACK = "trezor/res/left.toif"
ICON_SWIPE = "trezor/res/swipe.toif"
ICON_CHECK = "trezor/res/check.toif"
ICON_SPACE = "trezor/res/space.toif"

@ -1,119 +0,0 @@
#!/usr/bin/env python3
from PIL import Image
import sys
import struct
import zlib
from os.path import basename
def process_rgb(w, h, pix):
data = bytes()
for j in range(h):
for i in range(w):
r, g, b = pix[i, j]
c = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3)
data += struct.pack(">H", c)
return data
def process_grayscale(w, h, pix):
data = bytes()
for j in range(h):
for i in range(w // 2):
l1, l2 = pix[i * 2, j], pix[i * 2 + 1, j]
c = (l1 & 0xF0) | (l2 >> 4)
data += struct.pack(">B", c)
return data
def process_image(ifn, savefiles=False):
im = Image.open(ifn)
w, h = im.size
print("Opened %s ... %d x %d @ %s" % (ifn, w, h, im.mode))
if im.mode == "RGB":
print("Detected RGB mode")
elif im.mode == "L":
if w % 2 > 0:
print("PNG file must have width divisible by 2")
return 3
print("Detected GRAYSCALE mode")
else:
print("Unknown mode:", im.mode)
return 4
pix = im.load()
bname = basename(ifn[:-4])
ofn_h = "%s.h" % bname
ofn_py = "%s.py" % bname
if im.mode == "RGB":
ofn = "%s.toif" % bname
pixeldata = process_rgb(w, h, pix)
else:
ofn = "%s.toig" % bname
pixeldata = process_grayscale(w, h, pix)
z = zlib.compressobj(level=9, wbits=10)
zdata = z.compress(pixeldata) + z.flush()
zdata = zdata[2:-4] # strip header and checksum
data = b""
if im.mode == "RGB":
data += b"TOIf"
else:
data += b"TOIg"
data += struct.pack("<HH", w, h)
data += struct.pack("<I", len(zdata))
data += zdata
if savefiles:
with open(ofn, "wb") as f:
f.write(data)
print("Written %s ... %d bytes" % (ofn, len(data)))
with open(ofn_py, "wb") as f:
f.write(("%s = %s\n" % (bname, data)).encode())
print("Written %s ... %d bytes" % (ofn_py, len(data)))
with open(ofn_h, "wt") as f:
f.write("// clang-format off\n")
f.write("static const uint8_t toi_%s[] = {\n" % bname)
f.write(" // magic\n")
if im.mode == "RGB":
f.write(" 'T', 'O', 'I', 'f',\n")
else:
f.write(" 'T', 'O', 'I', 'g',\n")
f.write(" // width (16-bit), height (16-bit)\n")
f.write(
" 0x%02x, 0x%02x, 0x%02x, 0x%02x,\n"
% (w & 0xFF, w >> 8, h & 0xFF, h >> 8)
)
l = len(zdata)
f.write(" // compressed data length (32-bit)\n")
f.write(
" 0x%02x, 0x%02x, 0x%02x, 0x%02x,\n"
% (l & 0xFF, (l >> 8) & 0xFF, (l >> 16) & 0xFF, l >> 24)
)
f.write(" // compressed data\n")
f.write(" ")
for b in zdata:
f.write(" 0x%02x," % b)
f.write("\n};\n")
print("Written %s ... done" % ofn_py)
return data
def main():
if len(sys.argv) < 2:
print("Usage png2toi image.png")
return 1
ifn = sys.argv[1]
if not ifn.endswith(".png"):
print("Must provide PNG file")
return 2
process_image(ifn, savefiles=True)
main()

@ -1,92 +0,0 @@
#!/usr/bin/env python3
from PIL import Image
import sys
import struct
import zlib
def process_rgb(w, h, data):
pix = bytearray(w * h * 3)
for i in range(w * h):
c = (data[i * 2] << 8) + data[i * 2 + 1]
pix[i * 3 + 0] = (c & 0xF800) >> 8
pix[i * 3 + 1] = (c & 0x07C0) >> 3
pix[i * 3 + 2] = (c & 0x001F) << 3
return bytes(pix)
def process_grayscale(w, h, data):
pix = bytearray(w * h)
for i in range(w * h // 2):
pix[i * 2 + 0] = data[i] & 0xF0
pix[i * 2 + 1] = (data[i] & 0x0F) << 4
return bytes(pix)
def process_image(ifn, ofn):
data = open(ifn, "rb").read()
if ifn.endswith(".toif"):
if data[:4] != b"TOIf":
print("Unknown TOIF header")
return 1
elif ifn.endswith(".toig"):
if data[:4] != b"TOIg":
print("Unknown TOIG header")
return 2
else:
print("Unsupported format")
return 3
if ofn is None:
ofn = "%s.png" % ifn[:-5]
w, h = struct.unpack("<HH", data[4:8])
print("Opened %s ... %d x %d" % (ifn, w, h))
l = struct.unpack("<I", data[8:12])[0]
data = data[12:]
if len(data) != l:
print("Compressed data length mismatch (%d vs %d)" % (len(data), l))
return 4
data = zlib.decompress(data, -10)
if ifn.endswith(".toif"):
if len(data) != w * h * 2:
print(
"Uncompressed data length mismatch (%d vs %d)" % (len(data), w * h * 2)
)
return 5
pix = process_rgb(w, h, data)
img = Image.frombuffer("RGB", (w, h), pix, "raw", "RGB", 0, 1)
img.save(ofn)
print("Written %s ..." % ofn)
if ifn.endswith(".toig"):
if len(data) != w * h // 2:
print(
"Uncompressed data length mismatch (%d vs %d)" % (len(data), w * h // 2)
)
return 6
pix = process_grayscale(w, h, data)
img = Image.frombuffer("L", (w, h), pix, "raw", "L", 0, 1)
img.save(ofn)
print("Written %s ..." % ofn)
def main():
if len(sys.argv) < 2:
print("Usage: toi2png image.toi[fg] [output]")
return 1
ifn = sys.argv[1]
if not ifn.endswith(".toif") and not ifn.endswith(".toig"):
print("Must provide TOIF/TOIG file")
return 2
ofn = sys.argv[2] if len(sys.argv) > 2 else None
process_image(ifn, ofn)
main()

@ -0,0 +1,152 @@
#!/usr/bin/env python3
from PIL import Image
import io
import sys
import struct
import zlib
from os.path import basename
def png2toif(input_data):
def process_rgb(w, h, pix):
data = bytes()
for j in range(h):
for i in range(w):
r, g, b = pix[i, j]
c = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3)
data += struct.pack(">H", c)
return data
def process_grayscale(w, h, pix):
data = bytes()
for j in range(h):
for i in range(w // 2):
l1, l2 = pix[i * 2, j], pix[i * 2 + 1, j]
c = (l1 & 0xF0) | (l2 >> 4)
data += struct.pack(">B", c)
return data
im = Image.open(io.BytesIO(input_data))
w, h = im.size
if im.mode not in ["RGB", "L"]:
raise ValueError("Unknown mode:", im.mode)
if im.mode == "L":
if w % 2 > 0:
raise ValueError("PNG file must have width divisible by 2")
pix = im.load()
if im.mode == "RGB":
pixeldata = process_rgb(w, h, pix)
else:
pixeldata = process_grayscale(w, h, pix)
z = zlib.compressobj(level=9, wbits=10)
zdata = z.compress(pixeldata) + z.flush()
zdata = zdata[2:-4] # strip header and checksum
data = b""
if im.mode == "RGB":
data += b"TOIf"
else:
data += b"TOIg"
data += struct.pack("<HH", w, h)
data += struct.pack("<I", len(zdata))
data += zdata
return data
"""
with open(ofn_py, "wb") as f:
f.write(("%s = %s\n" % (bname, data)).encode())
with open(ofn_h, "wt") as f:
f.write("// clang-format off\n")
f.write("static const uint8_t toi_%s[] = {\n" % bname)
f.write(" // magic\n")
if im.mode == "RGB":
f.write(" 'T', 'O', 'I', 'f',\n")
else:
f.write(" 'T', 'O', 'I', 'g',\n")
f.write(" // width (16-bit), height (16-bit)\n")
f.write(
" 0x%02x, 0x%02x, 0x%02x, 0x%02x,\n"
% (w & 0xFF, w >> 8, h & 0xFF, h >> 8)
)
l = len(zdata)
f.write(" // compressed data length (32-bit)\n")
f.write(
" 0x%02x, 0x%02x, 0x%02x, 0x%02x,\n"
% (l & 0xFF, (l >> 8) & 0xFF, (l >> 16) & 0xFF, l >> 24)
)
f.write(" // compressed data\n")
f.write(" ")
for b in zdata:
f.write(" 0x%02x," % b)
f.write("\n};\n")
"""
def toif2png(data):
def process_rgb(w, h, data):
pix = bytearray(w * h * 3)
for i in range(w * h):
c = (data[i * 2] << 8) + data[i * 2 + 1]
pix[i * 3 + 0] = (c & 0xF800) >> 8
pix[i * 3 + 1] = (c & 0x07C0) >> 3
pix[i * 3 + 2] = (c & 0x001F) << 3
return bytes(pix)
def process_grayscale(w, h, data):
pix = bytearray(w * h)
for i in range(w * h // 2):
pix[i * 2 + 0] = data[i] & 0xF0
pix[i * 2 + 1] = (data[i] & 0x0F) << 4
return bytes(pix)
if data[:4] != b"TOIf" and data[:4] != b"TOIg":
raise ValueError("Unknown TOIF header")
format = data[:4].decode().lower()
w, h = struct.unpack("<HH", data[4:8])
l = struct.unpack("<I", data[8:12])[0]
data = data[12:]
if len(data) != l:
raise ValueError("Compressed data length mismatch (%d vs %d)" % (len(data), l))
data = zlib.decompress(data, -10)
if format == "toif":
if len(data) != w * h * 2:
raise ValueError("Uncompressed data length mismatch (%d vs %d)" % (len(data), w * h * 2))
pix = process_rgb(w, h, data)
img = Image.frombuffer("RGB", (w, h), pix, "raw", "RGB", 0, 1)
ret = io.BytesIO()
img.save(ret, format="PNG")
return ret.getvalue()
if format == "toig":
if len(data) != w * h // 2:
raise ValueError("Uncompressed data length mismatch (%d vs %d)" % (len(data), w * h // 2))
pix = process_grayscale(w, h, data)
img = Image.frombuffer("L", (w, h), pix, "raw", "L", 0, 1)
ret = io.BytesIO()
img.save(ret, format="PNG")
return ret.getvalue()
def main():
if len(sys.argv) < 2:
print("Usage: toi_convert [input.png|input.toif] [output.toif|output.png]")
return 1
ifn, ofn = sys.argv[1], sys.argv[2]
if ifn.endswith(".png") and ofn.endswith(".toif"):
open(ofn, "wb").write(png2toif(open(ifn, "rb").read()))
elif ifn.endswith(".toif") and ofn.endswith(".png"):
open(ofn, "wb").write(toif2png(open(ifn, "rb").read()))
else:
print("Must provide a valid combination of PNG/TOIF or TOIF/PNG files")
return 2
if __name__ == "__main__":
main()
Loading…
Cancel
Save