Merge pull request #3 from Optiroc/tools

Python tools to convert png image to/from MiSTer UI font bitmap
This commit is contained in:
sorgelig
2019-03-31 19:31:10 +08:00
committed by GitHub
5 changed files with 2798 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
__pycache__/

6
tools/.pylintrc Normal file
View File

@@ -0,0 +1,6 @@
[FORMAT]
indent-string=' '
max-line-length=180
[MESSAGES CONTROL]
disable=C0103, C0111, C0321, C0326, R1710, W0703

53
tools/pf2png.py Executable file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/env python3
# pf2png
# Convert MiSTer UI font (1bpp bitmap) to 16*n character sheet PNG image.
#
# Program by David Lindecrantz <optiroc@gmail.com>
# Distributed under the terms of the MIT license
import argparse
import os
import sys
import png
def is_file(parser, arg):
if not os.path.isfile(arg): parser.error("file %s does not exist" % arg)
else: return arg
def chunks(seq, size):
return (seq[pos:pos + size] for pos in range(0, len(seq), size))
def main():
try:
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--in-pf", dest="in_pf", metavar="FILE", required=True, type=lambda x: is_file(parser, x), help="input bitmap")
parser.add_argument("-o", "--out-png", dest="out_png", metavar="FILE", help="output png")
args = parser.parse_args()
pf = list(open(args.in_pf, "rb").read())
if args.out_png:
chars_per_line = 16
png_rows = []
for (char_index, char) in enumerate(chunks(pf, 8)):
for (char_line, char_byte) in enumerate(char):
png_line = (char_index // chars_per_line) * 8 + char_line
if len(png_rows) <= png_line:
png_rows.append([])
png_row = png_rows[png_line]
for bit in range(7, -1, -1):
png_row.append(0 if ((char_byte >> bit) & 1) else 255)
with open(args.out_png, "wb") as file:
writer = png.Writer(size=(len(png_rows[0]),len(png_rows)), greyscale=True, compression=9)
writer.write(file, png_rows)
return 0
except Exception as err:
print("error - {}".format(err))
sys.exit(1)
if __name__ == "__main__":
sys.exit(main())

2638
tools/png.py Normal file

File diff suppressed because it is too large Load Diff

100
tools/png2pf.py Executable file
View File

@@ -0,0 +1,100 @@
#!/usr/bin/env python3
# png2pf
# Convert PNG image to MiSTer UI font (1bpp bitmap),
# and optionally a nicely formatted preview image.
#
# Program by David Lindecrantz <optiroc@gmail.com>
# Distributed under the terms of the MIT license
import argparse
import functools
import os
import sys
import png
def is_file(parser, arg):
if not os.path.isfile(arg): parser.error("file %s does not exist" % arg)
else: return arg
def chunks(seq, size):
return (seq[pos:pos + size] for pos in range(0, len(seq), size))
# read png as 2D bitmap array (bool[][])
def read_png_1bpp(path):
(_, _, data, _) = png.Reader(file=open(path, "rb")).asRGBA8()
bitmap = []
for line in data:
l = []
for rgba in chunks(line, 4):
l.append((rgba[3] > 127) and (0.2989 * rgba[0] + 0.5870 * rgba[1] + 0.1140 * rgba[2]) < 128)
bitmap.append(l)
return bitmap
# slice 2D array into dim*dim sized tiles
def make_tiles(pixels, dim=8):
if len(pixels) % dim != 0:
raise Exception("image height not a multiple of {}".format(dim))
if len(pixels[0]) % dim != 0:
raise Exception("image width not a multiple of {}".format(dim))
tiles = []
for y in range(0, len(pixels), dim):
for x in range(0, len(pixels[0]), dim):
tiles.append([pixels[y+yl][x:x+dim] for yl in range(dim)])
return tiles
# write preview png
def write_preview(bitmap, path, dim=8):
scale = 4
width = scale * (1 + len(bitmap[0]) + len(bitmap[0]) // dim)
height = scale * (1 + len(bitmap) + len(bitmap) // dim)
tiles = make_tiles(bitmap, dim)
pixels = [bytearray(width) for i in range(height)]
for ypos in range(scale, height, scale * (dim + 1)):
for xpos in range(scale, width, scale * (dim + 1)):
blit_bitmap(tiles.pop(0), pixels, xpos, ypos, scale)
with open(path, "wb") as file:
writer = png.Writer(size=(width,height), greyscale=True, compression=9)
writer.write(file, pixels)
# blit bitmap (int/bool[][]) to pixel array (bytearray[][])
def blit_bitmap(src_bitmap, dest_pixels, xpos, ypos, scale=1):
for srcy, row in enumerate(src_bitmap):
for srcx, pixel in enumerate(row):
val = int(pixel) if not isinstance(pixel, bool) else (255 if pixel else 0)
dx = xpos + srcx * scale
dy = ypos + srcy * scale
for dx,dy in [(x,y) for x in range(dx, dx + scale) for y in range(dy, dy + scale)]:
dest_pixels[dy][dx] = val
def main():
try:
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--in-png", dest="in_png", metavar="FILE", required=True, type=lambda x: is_file(parser, x), help="input png")
parser.add_argument("-o", "--out-pf", dest="out_pf", metavar="FILE", help="output bitmap")
parser.add_argument("-p", "--out-preview", dest="out_preview", metavar="FILE", help="output preview image")
args = parser.parse_args()
bitmap = read_png_1bpp(args.in_png)
if args.out_pf:
pf = bytearray()
for tile in make_tiles(bitmap):
for line in tile:
pf.append(functools.reduce(lambda a,b: (a << 1) + b, line))
with open(args.out_pf, "wb") as file:
file.write(pf)
if args.out_preview:
write_preview(bitmap, args.out_preview)
return 0
except Exception as err:
print("error - {}".format(err))
sys.exit(1)
if __name__ == "__main__":
sys.exit(main())