circuitpython/tools/gen_display_resources.py
Jeff Epler a93475707e gen_display_resources: speed it up
It was intended that the `f.load_glyphs` line was fast and did most of
the work.  However, it actually didn't, because it's necessary to pass
in a code point by number, not by string.

Additionally, a little light layer violation is needed to make the check
for missing characters fast.  This used to be less important, as no
fonts had missing characters.  However, it would take an appreciable
length of time on the Korean translation when failing to find hundreds
of different code points.

Testing performed: built
build-circuitplayground_express_displayio/autogen_display_resources.c with ko
translation before and after change.  verified the file content was identical.
Time went from about 7s on my machine to way under 1 second.
2019-12-19 16:26:01 -06:00

200 lines
5.5 KiB
Python

import argparse
import os
import struct
import sys
sys.path.append("bitmap_font")
sys.path.append("../../tools/bitmap_font")
from adafruit_bitmap_font import bitmap_font
parser = argparse.ArgumentParser(description='Generate USB descriptors.')
parser.add_argument('--font', type=str,
help='Font path', required=True)
parser.add_argument('--extra_characters', type=str,
help='Unicode string of extra characters')
parser.add_argument('--sample_file', type=argparse.FileType('r'),
help='Text file that includes strings to support.')
parser.add_argument('--output_c_file', type=argparse.FileType('w'), required=True)
args = parser.parse_args()
class BitmapStub:
def __init__(self, width, height, color_depth):
self.width = width
self.rows = [b''] * height
def _load_row(self, y, row):
self.rows[y] = bytes(row)
f = bitmap_font.load_font(args.font, BitmapStub)
# Load extra characters from the sample file.
sample_characters = set()
if args.sample_file:
for line in args.sample_file:
# Skip comments because we add additional characters in our huffman comments.
if line.startswith("//"):
continue
for c in line.strip():
sample_characters.add(c)
# Merge visible ascii, sample characters and extra characters.
visible_ascii = bytes(range(0x20, 0x7f)).decode("utf-8")
all_characters = visible_ascii
for c in sample_characters:
if c not in all_characters:
all_characters += c
if args.extra_characters:
all_characters.extend(args.extra_characters)
all_characters = "".join(sorted(set(all_characters)))
filtered_characters = all_characters
# Try to pre-load all of the glyphs. Misses will still be slow later.
f.load_glyphs(set(ord(c) for c in all_characters))
# Get each glyph.
for c in set(all_characters):
if ord(c) not in f._glyphs:
print("Font missing character:", c, ord(c))
filtered_characters = filtered_characters.replace(c, "")
continue
g = f.get_glyph(ord(c))
if g["shift"][1] != 0:
raise RuntimeError("y shift")
x, y, dx, dy = f.get_bounding_box()
tile_x, tile_y = x - dx, y - dy
total_bits = tile_x * len(all_characters)
total_bits += 32 - total_bits % 32
bytes_per_row = total_bits // 8
b = bytearray(bytes_per_row * tile_y)
for x, c in enumerate(filtered_characters):
g = f.get_glyph(ord(c))
start_bit = x * tile_x + g["bounds"][2]
start_y = (tile_y - 2) - (g["bounds"][1] + g["bounds"][3])
for y, row in enumerate(g["bitmap"].rows):
for i in range(g["bounds"][0]):
byte = i // 8
bit = i % 8
if row[byte] & (1 << (7-bit)) != 0:
overall_bit = start_bit + (start_y + y) * bytes_per_row * 8 + i
b[overall_bit // 8] |= 1 << (7 - (overall_bit % 8))
extra_characters = ""
for c in filtered_characters:
if c not in visible_ascii:
extra_characters += c
c_file = args.output_c_file
c_file.write("""\
#include "shared-bindings/displayio/Palette.h"
#include "supervisor/shared/display.h"
""")
c_file.write("""\
_displayio_color_t terminal_colors[2] = {
{
.rgb888 = 0x000000,
.rgb565 = 0x0000,
.luma = 0x00,
.chroma = 0
},
{
.rgb888 = 0xffffff,
.rgb565 = 0xffff,
.luma = 0xff,
.chroma = 0
},
};
displayio_palette_t supervisor_terminal_color = {
.base = {.type = &displayio_palette_type },
.colors = terminal_colors,
.color_count = 2,
.needs_refresh = false
};
""")
c_file.write("""\
displayio_tilegrid_t supervisor_terminal_text_grid = {{
.base = {{ .type = &displayio_tilegrid_type }},
.bitmap = (displayio_bitmap_t*) &supervisor_terminal_font_bitmap,
.pixel_shader = &supervisor_terminal_color,
.x = 16,
.y = 0,
.pixel_width = {1},
.pixel_height = {2},
.bitmap_width_in_tiles = {0},
.tiles_in_bitmap = {0},
.width_in_tiles = 1,
.height_in_tiles = 1,
.tile_width = {1},
.tile_height = {2},
.tiles = NULL,
.partial_change = false,
.full_change = false,
.hidden = false,
.hidden_by_parent = false,
.moved = false,
.inline_tiles = false,
.in_group = true
}};
""".format(len(all_characters), tile_x, tile_y))
c_file.write("""\
const uint32_t font_bitmap_data[{}] = {{
""".format(bytes_per_row * tile_y // 4))
for i, word in enumerate(struct.iter_unpack(">I", b)):
c_file.write("0x{:08x}, ".format(word[0]))
if (i + 1) % (bytes_per_row // 4) == 0:
c_file.write("\n")
c_file.write("""\
};
""")
c_file.write("""\
displayio_bitmap_t supervisor_terminal_font_bitmap = {{
.base = {{.type = &displayio_bitmap_type }},
.width = {},
.height = {},
.data = (size_t*) font_bitmap_data,
.stride = {},
.bits_per_value = 1,
.x_shift = 5,
.x_mask = 0x1f,
.bitmask = 0x1,
.read_only = true
}};
""".format(len(all_characters) * tile_x, tile_y, bytes_per_row / 4))
c_file.write("""\
const fontio_builtinfont_t supervisor_terminal_font = {{
.base = {{.type = &fontio_builtinfont_type }},
.bitmap = &supervisor_terminal_font_bitmap,
.width = {},
.height = {},
.unicode_characters = (const uint8_t*) "{}",
.unicode_characters_len = {}
}};
""".format(tile_x, tile_y, extra_characters, len(extra_characters.encode("utf-8"))))
c_file.write("""\
terminalio_terminal_obj_t supervisor_terminal = {
.base = { .type = &terminalio_terminal_type },
.font = &supervisor_terminal_font,
.cursor_x = 0,
.cursor_y = 0,
.tilegrid = &supervisor_terminal_text_grid
};
""")