# -*- coding: utf-8 -*- """Generate MiSans-Semibold 0-9/% with shared baseline + matched cap height.""" from pathlib import Path from PIL import Image, ImageDraw, ImageFont ROOT = Path(r"C:\Users\qjyu\Documents\SoundWalker\一诺国际吉他") FONT = next((ROOT / "Doc" / "UI" / "MiSans").rglob("MiSans-Semibold.ttf")) OUT_C = ROOT / "Code" / "YNGJ-GT1-M - AT32F403ARCT7" / "device" / "LCD_ILI9341" / "Drv_ILI9341_Lcd_Image_ChargePct.c" OUT_H = ROOT / "Code" / "YNGJ-GT1-M - AT32F403ARCT7" / "device" / "LCD_ILI9341" / "Drv_ILI9341_Lcd_Image_ChargePct.h" OUT_PREV = ROOT / "Code" / "YNGJ-GT1-M - AT32F403ARCT7" / "tools" / "out" / "_charge_pct_glyphs.png" H = 20 FONT_PX = 18 WHITE = 0xFFFF BLACK = 0x0000 TOP_PAD = 1 def render_on_baseline(ch: str, font: ImageFont.FreeTypeFont) -> Image.Image: """Render char into HxW with shared baseline = H - 2.""" ascent, descent = font.getmetrics() baseline = H - 2 # wide scratch scratch = Image.new("L", (48, H + 8), 0) dr = ImageDraw.Draw(scratch) # Pillow text y is top of em-box approx; use anchor if available try: dr.text((4, baseline), ch, font=font, fill=255, anchor="ls") except TypeError: dr.text((4, baseline - ascent), ch, font=font, fill=255) bbox = scratch.getbbox() if bbox is None: return Image.new("L", (8, H), 0) l, t, r, b = bbox # keep full height H strip from y=0..H-1 of scratch (aligned) strip = scratch.crop((l, 0, r, H)) # 1-bit bw = strip.point(lambda v: 255 if v >= 140 else 0) # add 1px side pad out = Image.new("L", (bw.size[0] + 2, H), 0) out.paste(bw, (1, 0)) return out def to_rgb565_be(im: Image.Image) -> bytes: w, h = im.size out = bytearray(w * h * 2) i = 0 for y in range(h): for x in range(w): c = WHITE if im.getpixel((x, y)) else BLACK out[i] = (c >> 8) & 0xFF out[i + 1] = c & 0xFF i += 2 return bytes(out) font = ImageFont.truetype(str(FONT), FONT_PX) chars = list("0123456789%") glyphs = {ch: render_on_baseline(ch, font) for ch in chars} # Verify ink y-ranges share baseline for ch in "50%": im = glyphs[ch] ys = [y for y in range(im.size[1]) for x in range(im.size[0]) if im.getpixel((x, y))] print(ch, "size", im.size, "ink y", min(ys), max(ys)) total_w = sum(glyphs[c].size[0] for c in "50%") + 4 prev = Image.new("L", (total_w, H + 4), 0) x = 2 for ch in "50%": prev.paste(glyphs[ch], (x, 2)) x += glyphs[ch].size[0] OUT_PREV.parent.mkdir(parents=True, exist_ok=True) prev.resize((prev.size[0] * 5, prev.size[1] * 5), Image.NEAREST).save(OUT_PREV) prev.resize((prev.size[0] * 5, prev.size[1] * 5), Image.NEAREST).save( OUT_PREV.with_name("_charge_pct_prev_ascii.png") ) print("preview", OUT_PREV) h_lines = [ "#ifndef __DRV_ILI9341_LCD_IMAGE_CHARGE_PCT_H", "#define __DRV_ILI9341_LCD_IMAGE_CHARGE_PCT_H", "", '#include "stdint.h"', "", f"#define CHARGE_PCT_GLYPH_H {H}", "", ] c_lines = [ '#include "Drv_ILI9341_Lcd_Image_ChargePct.h"', "", "/* Auto-generated by tools/gen_charge_pct_glyphs.py — MiSans Semibold, shared baseline */", "", ] for ch in chars: im = glyphs[ch] w, h = im.size data = to_rgb565_be(im) name = "Pct" if ch == "%" else f"Digit{ch}" arr = f"gImage_Charge_{name}_{w}x{h}" h_lines.append(f"#define CHARGE_{name.upper()}_W {w}") h_lines.append(f"#define CHARGE_{name.upper()}_H {h}") h_lines.append(f"extern const unsigned char {arr}[{len(data)}];") h_lines.append("") c_lines.append(f"const unsigned char {arr}[{len(data)}] = {{ /* {w}x{h} RGB565 BE */") hexes = [f"0x{b:02X}" for b in data] for i in range(0, len(hexes), 16): c_lines.append(" " + ",".join(hexes[i : i + 16]) + ",") c_lines.append("};") c_lines.append("") h_lines += [ "typedef struct {", " const unsigned char *data;", " uint8_t w;", " uint8_t h;", "} ChargePctGlyph;", "", "extern const ChargePctGlyph g_ChargePctDigits[10];", "extern const ChargePctGlyph g_ChargePctPercent;", "", "#endif", "", ] c_lines.append("const ChargePctGlyph g_ChargePctDigits[10] = {") for d in range(10): im = glyphs[str(d)] w, h = im.size c_lines.append(f" {{ gImage_Charge_Digit{d}_{w}x{h}, {w}, {h} }},") c_lines.append("};") c_lines.append("") im = glyphs["%"] w, h = im.size c_lines.append( f"const ChargePctGlyph g_ChargePctPercent = {{ gImage_Charge_Pct_{w}x{h}, {w}, {h} }};" ) c_lines.append("") OUT_H.write_text("\n".join(h_lines), encoding="utf-8", newline="\n") OUT_C.write_text("\n".join(c_lines), encoding="utf-8", newline="\n") print("wrote", OUT_C.stat().st_size, "bytes")