util/font: Add support for dynamic canvas dimensions
Update generate_font.py to support user-defined canvas width and height via command-line arguments. This replaces the hardcoded 16x24 limits. Key changes: - Use argparse for --width and --height parameters. - Ensure glyphs are left-aligned to the MSB (bit 15 or 31) for scalability. TEST=Able to create font table upto 32 pixels wide. ``` python generate_font.py <path_to_ttf> --width 24 --height 32 > font_table.c ``` Change-Id: Ifd02a979abf41a2c2b088ae58bb931f9f6421491 Signed-off-by: Subrata Banik <subratabanik@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/91165 Reviewed-by: Kapil Porwal <kapilporwal@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
2158ebb933
commit
96b29e37db
1 changed files with 64 additions and 36 deletions
|
|
@ -9,31 +9,40 @@ The output is a C source file containing:
|
|||
3. A character width table for proportional spacing.
|
||||
|
||||
Usage:
|
||||
python generate_font.py <path_to_ttf> > font_table.c
|
||||
python generate_font.py <path_to_ttf> --width 24 --height 32 > font_table.c
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from PIL import Image, ImageFont, ImageDraw
|
||||
import sys
|
||||
import os
|
||||
|
||||
# FONT DIMENSIONS
|
||||
FONT_SIZE = 19
|
||||
CANVAS_WIDTH = 16
|
||||
CANVAS_HEIGHT = 24
|
||||
Y_OFFSET = -2
|
||||
|
||||
# PRINTABLE ASCII RANGE
|
||||
START_CHAR = 32
|
||||
END_CHAR = 126
|
||||
NUM_CHARS = END_CHAR - START_CHAR + 1
|
||||
|
||||
def generate_c_table(font_path):
|
||||
def generate_c_table(font_path, canvas_width, canvas_height):
|
||||
if not os.path.exists(font_path):
|
||||
print(f"Error: Font file '{font_path}' not found.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Validation: Width must not exceed 32 bits for uint32_t storage
|
||||
if canvas_width > 32:
|
||||
print(f"Error: Requested width ({canvas_width}) exceeds 32-bit capacity. "
|
||||
"The current renderer supports up to 32 pixels wide.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Validation: Basic sanity check for positive dimensions
|
||||
if canvas_width <= 0 or canvas_height <= 0:
|
||||
print("Error: Width and Height must be positive integers.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Use canvas_width as font size to maintain original calculation style
|
||||
font_size = canvas_width
|
||||
y_offset = 0
|
||||
|
||||
try:
|
||||
font = ImageFont.truetype(font_path, FONT_SIZE)
|
||||
font = ImageFont.truetype(font_path, font_size)
|
||||
except OSError:
|
||||
print(f"Error: Could not open font file '{font_path}'.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
|
@ -43,63 +52,79 @@ def generate_c_table(font_path):
|
|||
print("/*")
|
||||
print(" * THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.")
|
||||
print(f" * Source: {os.path.basename(font_path)}")
|
||||
print(f" * Command: python util/font/{os.path.basename(__file__)} {font_path}")
|
||||
print(f" * Command: python {os.path.basename(__file__)} {font_path} --width {canvas_width} --height {canvas_height}")
|
||||
print(" */\n")
|
||||
print("#include <stdint.h>\n")
|
||||
|
||||
# 1. Output the Macros for C code usage
|
||||
print(f"#define FONT_WIDTH {CANVAS_WIDTH}")
|
||||
print(f"#define FONT_HEIGHT {CANVAS_HEIGHT}")
|
||||
print(f"#define FONT_WIDTH {canvas_width}")
|
||||
print(f"#define FONT_HEIGHT {canvas_height}")
|
||||
print(f"#define FONT_START_CHAR {START_CHAR}")
|
||||
print(f"#define FONT_END_CHAR {END_CHAR}")
|
||||
print(f"#define FONT_NUM_CHARS (FONT_END_CHAR - FONT_START_CHAR + 1)\n")
|
||||
|
||||
widths = []
|
||||
|
||||
# 2. Generate packed font table with block formatting
|
||||
print(f"const uint16_t font_table[FONT_NUM_CHARS][FONT_HEIGHT] = {{")
|
||||
# Choose data type based on width
|
||||
data_type = "uint32_t"
|
||||
hex_format = "08x" if canvas_width > 16 else "04x"
|
||||
bit_depth = 32 if canvas_width > 16 else 16
|
||||
|
||||
# 2. Generate packed font table
|
||||
print(f"const {data_type} font_table[FONT_NUM_CHARS][FONT_HEIGHT] = {{")
|
||||
|
||||
for char_code in range(START_CHAR, END_CHAR + 1):
|
||||
char = chr(char_code)
|
||||
image = Image.new("1", (CANVAS_WIDTH, CANVAS_HEIGHT), 0)
|
||||
image = Image.new("1", (canvas_width, canvas_height), 0)
|
||||
draw = ImageDraw.Draw(image)
|
||||
draw.text((0, Y_OFFSET), char, font=font, fill=1)
|
||||
draw.text((0, y_offset), char, font=font, fill=1)
|
||||
|
||||
pixels = list(image.getdata())
|
||||
rows = []
|
||||
global_mask = 0
|
||||
|
||||
for y in range(CANVAS_HEIGHT):
|
||||
for y in range(canvas_height):
|
||||
row_val = 0
|
||||
base = y * CANVAS_WIDTH
|
||||
for x in range(CANVAS_WIDTH):
|
||||
base = y * canvas_width
|
||||
for x in range(canvas_width):
|
||||
if pixels[base + x]:
|
||||
row_val |= (1 << ((CANVAS_WIDTH - 1) - x))
|
||||
# Map pixels to bits: Leftmost pixel is highest bit
|
||||
row_val |= (1 << ((canvas_width - 1) - x))
|
||||
rows.append(row_val)
|
||||
global_mask |= row_val
|
||||
|
||||
# Dead space removal / Normalization
|
||||
left_shift = 0
|
||||
actual_width = 0
|
||||
if global_mask > 0:
|
||||
temp_mask = global_mask
|
||||
while not (temp_mask & 0x8000):
|
||||
temp_mask <<= 1
|
||||
left_shift += 1
|
||||
|
||||
rightmost_bit = 0
|
||||
for i in range(CANVAS_WIDTH):
|
||||
if (global_mask >> i) & 1:
|
||||
rightmost_bit = i
|
||||
if global_mask > 0:
|
||||
# Find the leftmost pixel column
|
||||
leftmost_col = 0
|
||||
for i in range(canvas_width):
|
||||
if (global_mask >> (canvas_width - 1 - i)) & 1:
|
||||
leftmost_col = i
|
||||
break
|
||||
actual_width = ((CANVAS_WIDTH - 1) - left_shift) - rightmost_bit + 1
|
||||
|
||||
# Find the rightmost pixel column
|
||||
rightmost_col = 0
|
||||
for i in range(canvas_width):
|
||||
if (global_mask >> i) & 1:
|
||||
rightmost_col = (canvas_width - 1) - i
|
||||
break
|
||||
|
||||
# Width is the horizontal span of active pixels
|
||||
actual_width = rightmost_col - leftmost_col + 1
|
||||
|
||||
# left_shift ensures character is left-aligned to MSB of the data type
|
||||
left_shift = (bit_depth - canvas_width) + leftmost_col
|
||||
else:
|
||||
actual_width = 6 # Default space width
|
||||
actual_width = canvas_width // 3 # Default width for space
|
||||
|
||||
widths.append(actual_width)
|
||||
|
||||
# Format the pre-shifted hex rows into blocks of 8
|
||||
hex_values = [f"0x{(row << left_shift) & 0xFFFF:04x}" for row in rows]
|
||||
# Pre-shift values to be MSB-aligned and mask based on bit depth
|
||||
mask_val = 0xFFFFFFFF if bit_depth == 32 else 0xFFFF
|
||||
hex_values = [f"0x{(row << left_shift) & mask_val:{hex_format}}" for row in rows]
|
||||
char_repr = f"'{char}'" if char not in ["'", "\\"] else f"'\\{char}'"
|
||||
|
||||
print(f"\t[0x{char_code:02x} - FONT_START_CHAR] = {{")
|
||||
|
|
@ -118,7 +143,10 @@ def generate_c_table(font_path):
|
|||
print("};")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print(f"Usage: python3 {sys.argv[0]} <path_to_ttf_file>")
|
||||
sys.exit(1)
|
||||
generate_c_table(sys.argv[1])
|
||||
parser = argparse.ArgumentParser(description="Generate bitmapped font table.")
|
||||
parser.add_argument("font_path", help="Path to TTF/OTF font file")
|
||||
parser.add_argument("--width", type=int, default=24, help="Canvas width (default: 24)")
|
||||
parser.add_argument("--height", type=int, default=32, help="Canvas height (default: 32)")
|
||||
|
||||
args = parser.parse_args()
|
||||
generate_c_table(args.font_path, args.width, args.height)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue