commeownder/icons.py

153 lines
5.5 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env python3
"""Display SSD1306 vertical-byte glyph/icon arrays as console text.
Handles two array shapes found in C headers:
Single-page glyphs uint8_t name[][W]
Each row is one glyph: W columns × 8 rows.
Displayed side-by-side in a grid.
Two-page icons uint8_t name[...][2][W] or name[2][W]
Each pair of rows is one icon: W columns × 16 rows.
Outer 1-pixel border trimmed → 14×14 display.
Usage:
python3 icons.py [FILE] show all glyphs and icons (default: main/font.h)
"""
import re
import sys
# ── Glyph display (single page, arbitrary W×8) ───────────────────────────────
def decode_glyph(data):
"""data: list of W ints → 8×W bitmap."""
w = len(data)
return [[(data[col] >> row) & 1 for col in range(w)] for row in range(8)]
def show_glyphs(entries, per_row=10):
"""entries: list of (label, data) — print glyphs side by side."""
if not entries:
return
glyph_w = len(entries[0][1])
col_w = max(glyph_w, max(len(label) for label, _ in entries))
sep = " "
for i in range(0, len(entries), per_row):
chunk = entries[i : i + per_row]
grids = [decode_glyph(data) for _, data in chunk]
labels = [label for label, _ in chunk]
print(sep.join(f"{lbl:^{col_w}}" for lbl in labels))
for row in range(8):
parts = ["".join("x" if grids[g][row][c] else " " for c in range(glyph_w))
for g in range(len(chunk))]
print(sep.join(f"{p:^{col_w}}" for p in parts))
print()
# ── Icon display (two pages, W×16 → trimmed 14×14) ───────────────────────────
def decode_icon(data):
"""data: [[W ints page0], [W ints page1]] → 14×14 list-of-lists."""
w = len(data[0])
rows = [[(data[page][col] >> bit) & 1
for col in range(w)]
for page in range(2)
for bit in range(8)]
return [r[1:-1] for r in rows[1:-1]]
def show_icon(grid, name=""):
if name:
print(f"── {name} ──")
for row in grid:
print("".join("x" if p else " " for p in row))
print()
# ── C file parser ─────────────────────────────────────────────────────────────
def _hex_row(raw):
return [int(x, 16) for x in re.findall(r"0x[0-9A-Fa-f]+", raw)]
def parse_file(path):
"""
Parse all uint8_t byte arrays from a C header.
Returns:
glyphs: dict of array_name → list of (label, [W bytes])
icons: dict of name → [[W page0 bytes], [W page1 bytes]]
"""
text = open(path).read()
glyphs = {}
icons = {}
# ── Two-page icon arrays ──────────────────────────────────────────────────
# Matches: uint8_t name[...][2][W] = { ... };
# Also: uint8_t name[2][W] = { ... };
for m in re.finditer(
r"uint8_t\s+(\w+)\s*(?:\[[^\]]*\])*\[2\]\[([^\]]+)\]\s*=\s*\{(.*?)\}\s*;",
text, re.DOTALL,
):
array_name = m.group(1)
body = m.group(3)
# Pull each { {top}, {bot} } pair, with an optional // comment before it
pairs = re.findall(
r"(?://\s*(.+?)\n)?\s*\{\s*\{([^}]+)\}\s*,\s*\{([^}]+)\}\s*\}",
body,
)
if pairs:
for i, (comment, raw0, raw1) in enumerate(pairs):
label = comment.strip() if comment.strip() else f"{array_name}[{i}]"
icons[label] = [_hex_row(raw0), _hex_row(raw1)]
else:
# Standalone name[2][W] — whole array is one icon
rows = re.findall(r"\{([^}]+)\}", body)
if len(rows) == 2:
icons[array_name] = [_hex_row(rows[0]), _hex_row(rows[1])]
# ── Single-page glyph arrays ──────────────────────────────────────────────
# Matches: uint8_t name[][W] = { {bytes}, // label \n ... };
for m in re.finditer(
r"uint8_t\s+(\w+)\s*\[\]\s*\[([^\]]+)\]\s*=\s*\{(.*?)\}\s*;",
text, re.DOTALL,
):
array_name = m.group(1)
body = m.group(3)
entries = []
for em in re.finditer(r"\{([^}]+)\}\s*(?:,\s*)?(?://\s*(.+))?", body):
data = _hex_row(em.group(1))
comment = (em.group(2) or "").strip().strip("'\"")
label = comment if comment else str(len(entries))
entries.append((label, data))
if entries:
glyphs[array_name] = entries
return glyphs, icons
# ── Entry point ───────────────────────────────────────────────────────────────
if __name__ == "__main__":
path = next((a for a in sys.argv[1:] if not a.startswith("--")), "main/font.h")
glyphs, icons = parse_file(path)
if not glyphs and not icons:
sys.exit(f"no glyph or icon arrays found in {path}")
for array_name, entries in glyphs.items():
print(f"{'' * 60}")
print(f" {array_name} ({len(entries)} glyphs)")
print(f"{'' * 60}\n")
show_glyphs(entries)
for name, data in icons.items():
show_icon(decode_icon(data), name)