Add Chinese text rendering support
Extend the bitmap font pipeline with the Chinese Joker glyph subset and encode supported UTF-8 text for Tonc TTE. Move the affine map clear of the expanded font, add a compile-time VRAM overlap guard, and cover every custom glyph with host tests.
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""Append the Chinese Joker-description glyph subset to the project's 8x8 ASCII font."""
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
ASCII_FONT_HEIGHT = 48
|
||||
CELL_SIZE = 8
|
||||
GLYPHS_PER_ROW = 16
|
||||
|
||||
|
||||
def read_codepoints(table_path: Path) -> list[int]:
|
||||
table = table_path.read_text(encoding="utf-8")
|
||||
return [int(value, 16) for value in re.findall(r"CHINESE_GLYPH\(0x([0-9A-Fa-f]+)\)", table)]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument("--font", required=True, help="Fusion Pixel Font 8px zh_hans TTF")
|
||||
parser.add_argument("--table", default="include/def_chinese_glyph_table.h")
|
||||
parser.add_argument("--image", default="font/gbalatro_sys8.png")
|
||||
args = parser.parse_args()
|
||||
|
||||
image_path = Path(args.image)
|
||||
base = Image.open(image_path).convert("RGB").crop((0, 0, 128, ASCII_FONT_HEIGHT))
|
||||
codepoints = read_codepoints(Path(args.table))
|
||||
extra_rows = (len(codepoints) + GLYPHS_PER_ROW - 1) // GLYPHS_PER_ROW
|
||||
|
||||
output = Image.new("RGB", (128, ASCII_FONT_HEIGHT + extra_rows * CELL_SIZE), "white")
|
||||
output.paste(base, (0, 0))
|
||||
|
||||
draw = ImageDraw.Draw(output)
|
||||
font = ImageFont.truetype(args.font, CELL_SIZE)
|
||||
for index, codepoint in enumerate(codepoints):
|
||||
x = (index % GLYPHS_PER_ROW) * CELL_SIZE
|
||||
y = ASCII_FONT_HEIGHT + (index // GLYPHS_PER_ROW) * CELL_SIZE
|
||||
draw.text((x, y - 1), chr(codepoint), font=font, fill="black")
|
||||
|
||||
output.save(image_path, optimize=True)
|
||||
print(f"Appended {len(codepoints)} Chinese glyphs to {image_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -5,7 +5,6 @@ import argparse
|
||||
|
||||
CHAR_WIDTH = 8
|
||||
CHAR_HEIGHT = 8
|
||||
NUM_VERT_CHARS = 6
|
||||
NUM_HORZ_CHARS = 16
|
||||
WORD_SIZE = 32
|
||||
|
||||
@@ -48,8 +47,14 @@ img = img.convert("P")
|
||||
img = img.convert('1')
|
||||
img = ImageOps.invert(img)
|
||||
|
||||
if img.width != NUM_HORZ_CHARS * CHAR_WIDTH or img.height % CHAR_HEIGHT != 0:
|
||||
raise ValueError("font image must be 128px wide and a multiple of 8px high")
|
||||
|
||||
num_vert_chars = img.height // CHAR_HEIGHT
|
||||
num_chars = num_vert_chars * NUM_HORZ_CHARS
|
||||
|
||||
glyphs = []
|
||||
for char_y in range(NUM_VERT_CHARS):
|
||||
for char_y in range(num_vert_chars):
|
||||
for char_x in range(NUM_HORZ_CHARS):
|
||||
# Get the 8x8 crop area at our current character in the image
|
||||
pix_x = char_x * CHAR_WIDTH
|
||||
@@ -86,7 +91,7 @@ with open(out_path, "w") as out:
|
||||
.global gbalatro_sys8Font
|
||||
gbalatro_sys8Font:
|
||||
.word gbalatro_sys8Glyphs, 0, 0
|
||||
.hword 32, 96
|
||||
.hword 32, %d
|
||||
.byte 8, 8
|
||||
.byte 8, 8
|
||||
.hword 8
|
||||
@@ -94,9 +99,9 @@ gbalatro_sys8Font:
|
||||
|
||||
.section .rodata
|
||||
.align 2
|
||||
.global gbalatro_sys8Glyphs @ 768 bytes (192 unsigned ints)
|
||||
.global gbalatro_sys8Glyphs
|
||||
gbalatro_sys8Glyphs:
|
||||
""")
|
||||
""" % num_chars)
|
||||
|
||||
for i, glyph in enumerate(glyphs):
|
||||
if first:
|
||||
|
||||
Reference in New Issue
Block a user