circuitpython/tools/gen_display_resources.py
Radomir Dopieralski 2c7b11b1af displayio.Bitmap is now byte-aligned for depth < 8
The traditional layout of pixels in bitmaps of depth less than eight
is with the order of values in a byte reversed, but with bytes in
the same order as the pixels on the line.

Before now, displayio.Bitmap did reverse the values, but did it on a
word (four bytes) basis, not byte, which resulted in groups of four
bytes being in the order opposite to the screen order.

This patch fixes this, by making processing of pixels in bitmaps of
depth less than 8 bits based on bytes, not words. Since the internal
details are changing, any code that accessed bitmaps through the
memoryview buffer, or that created bitmaps directly from raw data,
and that used depth of less than 8 bits will be affected.

Therefore, the gen_display_resources.py script also had to be modified
to account for the changes.
2024-08-05 23:14:55 +02:00

390 lines
9.1 KiB
Python

# SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors)
#
# SPDX-License-Identifier: MIT
import argparse
import os
import struct
import sys
sys.path.insert(0, "bitmap_font")
sys.path.insert(0, "../../tools/bitmap_font")
from adafruit_bitmap_font import bitmap_font
parser = argparse.ArgumentParser(description="Generate displayio resources.")
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", encoding="utf-8"),
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 = list(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))
missing = 0
# Get each glyph.
for c in set(all_characters):
if ord(c) not in f._glyphs:
missing += 1
filtered_characters = filtered_characters.replace(c, "")
continue
g = f.get_glyph(ord(c))
if g["shift"][1] != 0:
raise RuntimeError("y shift")
if missing > 0:
print("Font missing", missing, "characters", file=sys.stderr)
tile_x, tile_y, dx, dy = f.get_bounding_box()
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/Bitmap.h"
#include "shared-bindings/displayio/Palette.h"
#include "supervisor/shared/display.h"
"""
)
c_file.write(
"""\
#if CIRCUITPY_REPL_LOGO
"""
)
if tile_y == 16:
blinka_size = 16
c_file.write(
"""\
const uint32_t blinka_bitmap_data[32] = {
0x11000000, 0x00000011,
0x11010000, 0x00001053,
0x11010000, 0x00001156,
0x11010000, 0x00001411,
0x11010000, 0x00200020,
0x11000000, 0x00000013,
0x01000000, 0x00002011,
0x00000000, 0x00003311,
0x00000000, 0x00201201,
0x11110000, 0x00301344,
0x23230300, 0x00221124,
0x14111100, 0x00331144,
0x32323200, 0x00221134,
0x44111111, 0x00334444,
0x11111111, 0x01441411,
0x23232323, 0x10111121
};
"""
)
else:
blinka_size = 12
c_file.write(
"""\
const uint32_t blinka_bitmap_data[28] = {
0x11010000, 0x00000000,
0x53110000, 0x00000010,
0x56110000, 0x00000011,
0x11110000, 0x00000014,
0x12010000, 0x00002000,
0x11000000, 0x00000030,
0x11000000, 0x00000020,
0x44110100, 0x00000013,
0x24232300, 0x00000012,
0x44141101, 0x00000013,
0x34323232, 0x00000112,
0x44111111, 0x00001044
};
"""
)
c_file.write(
"""\
const displayio_bitmap_t blinka_bitmap = {{
.base = {{.type = &displayio_bitmap_type }},
.width = {0},
.height = {0},
.data = (uint32_t*) blinka_bitmap_data,
.stride = 2,
.bits_per_value = 4,
.x_shift = 1,
.x_mask = 0x01,
.bitmask = 0x0f,
.read_only = true
}};
_displayio_color_t blinka_colors[7] = {{
{{
.rgb888 = 0x000000,
.transparent = true
}},
{{ // Purple
.rgb888 = 0x8428bc
}},
{{ // Pink
.rgb888 = 0xff89bc
}},
{{ // Light blue
.rgb888 = 0x7beffe
}},
{{ // Dark purple
.rgb888 = 0x51395f
}},
{{ // White
.rgb888 = 0xffffff
}},
{{ // Dark Blue
.rgb888 = 0x0736a0
}},
}};
displayio_palette_t blinka_palette = {{
.base = {{.type = &displayio_palette_type }},
.colors = blinka_colors,
.color_count = 7,
.needs_refresh = false
}};
displayio_tilegrid_t supervisor_blinka_sprite = {{
.base = {{.type = &displayio_tilegrid_type }},
.bitmap = (displayio_bitmap_t*) &blinka_bitmap,
.pixel_shader = &blinka_palette,
.x = 0,
.y = 0,
.pixel_width = {0},
.pixel_height = {0},
.bitmap_width_in_tiles = 1,
.width_in_tiles = 1,
.height_in_tiles = 1,
.tile_width = {0},
.tile_height = {0},
.top_left_x = {0},
.top_left_y = {0},
.tiles = 0,
.partial_change = false,
.full_change = false,
.hidden = false,
.hidden_by_parent = false,
.moved = false,
.inline_tiles = true,
.in_group = true
}};
#endif
""".format(
blinka_size
)
)
c_file.write(
"""\
#if CIRCUITPY_TERMINALIO
_displayio_color_t terminal_colors[2] = {
{
.rgb888 = 0x000000
},
{
.rgb888 = 0xffffff
},
};
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_scroll_area_text_grid = {{
.base = {{ .type = &displayio_tilegrid_type }},
.bitmap = (displayio_bitmap_t*) &supervisor_terminal_font_bitmap,
.pixel_shader = &supervisor_terminal_color,
.x = 0,
.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(
"""\
displayio_tilegrid_t supervisor_terminal_status_bar_text_grid = {{
.base = {{ .type = &displayio_tilegrid_type }},
.bitmap = (displayio_bitmap_t*) &supervisor_terminal_font_bitmap,
.pixel_shader = &supervisor_terminal_color,
.x = 0,
.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 = (uint32_t*) font_bitmap_data,
.stride = {},
.bits_per_value = 1,
.x_shift = 3,
.x_mask = 0x07,
.bitmask = 0x01,
.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,
.scroll_area = NULL,
.status_bar = NULL
};
#endif
"""
)