Files
beefy-player/beefy_player/providers/wordmark.py
T

73 lines
2.9 KiB
Python

"""A half-block wordmark font for the header hero.
Every shipped catalogue draws its brand the same way: four-column capitals,
six pixels tall, emitted as three rows of half blocks (``▀``/``▄``/``█``) — the
same shape as the original SomaFM logo. The header paints every wordmark with
the app's active palette gradient, so catalogues differ only in the word they
spell; the font lives here and each provider's ``hero.py`` is one line of data.
Glyphs are 6x4 bitmaps (``M`` is 6x5). Two pixel rows become one text row, so
a wordmark is always three rows tall and the header keeps a fixed height.
"""
from __future__ import annotations
#: 6-row bitmap per capital; all rows of one glyph are the same width.
GLYPHS: dict[str, tuple[str, ...]] = {
"A": (".##.", "#..#", "####", "#..#", "#..#", "#..#"),
"B": ("###.", "#..#", "###.", "#..#", "#..#", "###."),
"C": (".##.", "#..#", "#...", "#...", "#..#", ".##."),
"D": ("###.", "#..#", "#..#", "#..#", "#..#", "###."),
"E": ("####", "#...", "###.", "#...", "#...", "####"),
"F": ("####", "#...", "###.", "#...", "#...", "#..."),
"G": (".##.", "#..#", "#...", "#.##", "#..#", ".##."),
"H": ("#..#", "#..#", "####", "#..#", "#..#", "#..#"),
"I": ("###", ".#.", ".#.", ".#.", ".#.", "###"),
"K": ("#..#", "#.#.", "##..", "##..", "#.#.", "#..#"),
"L": ("#...", "#...", "#...", "#...", "#...", "####"),
# 5 wide on purpose: the M is the odd one out in the original wordmark.
"M": ("#...#", "##.##", "#.#.#", "#.#.#", "#...#", "#...#"),
"N": ("#..#", "##.#", "##.#", "#.##", "#..#", "#..#"),
"O": (".##.", "#..#", "#..#", "#..#", "#..#", ".##."),
"P": ("###.", "#..#", "###.", "#...", "#...", "#..."),
"R": ("###.", "#..#", "###.", "#.#.", "#..#", "#..#"),
"S": (".###", "#...", "###.", "...#", "...#", "###."),
"T": ("###", ".#.", ".#.", ".#.", ".#.", ".#."),
"U": ("#..#", "#..#", "#..#", "#..#", "#..#", ".##."),
"V": ("#..#", "#..#", "#..#", "#..#", ".##.", ".##."),
"W": ("#...#", "#...#", "#.#.#", "#.#.#", "##.##", "#...#"),
"Y": ("#..#", "#..#", ".##.", "..#.", "..#.", "..#."),
}
ROWS = 3
_GAP = " "
def _cell(top: str, bottom: str) -> str:
if top == "#" and bottom == "#":
return "█"
if top == "#":
return "▀"
if bottom == "#":
return "▄"
return " "
def _glyph_rows(char: str) -> tuple[str, ...]:
bitmap = GLYPHS.get(char)
if bitmap is None:
raise KeyError(f"no wordmark glyph for {char!r}")
return tuple(
"".join(_cell(bitmap[row * 2][col], bitmap[row * 2 + 1][col]) for col in range(len(bitmap[0])))
for row in range(ROWS)
)
def render(text: str) -> tuple[str, ...]:
"""Spell ``text`` as three rows of half blocks."""
glyphs = [_glyph_rows(char.upper()) for char in text]
rows: list[str] = []
for row in range(ROWS):
rows.append(_GAP.join(glyph[row] for glyph in glyphs).rstrip())
return tuple(rows)