|
|
#!/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)
|