Eine Biene als Wappen, an allen vier Stellen
Aus dem Quadrat links in beiden Leisten wird eine Pixelbiene von der Seite: braune Streifen, schwarze Beine, Fluegel mit Kerbe. Sie steht in genau einem Raster (tools/biene.py, 16x16 Zeichen); daraus entstehen SVG, PNG und eine Fassung aus Schriftzeichen. SVG statt PNG in den Leisten: Die HoneyBar laeuft in fremden Apps, und ob ein <img> dort laedt, entscheidet deren CSP. Markup kann niemand blockieren. Im System-Fenster und ueber dem Startlauf steht die Biene aus Schriftzeichen — beides sind Textflaechen, ein Bild darin waere ein Fremdkoerper. Das Wappen liegt an drei Stellen (Datei, Modul, HoneyBar-Zeile), weil die Leiste nichts nachladen kann. TestWappenIstUeberallDasselbe haelt sie gleich.
This commit is contained in:
1 parent
aec042a8c9
commit
3f8b98b869
14 files changed
+301
-10
No files matched your search
+151
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Erzeugt das HoneyOS-Wappen aus einem Textraster.
|
||||
|
||||
Die Biene steht **hier** und nirgends sonst: als 16 × 16 Zeichen, ein Zeichen
|
||||
je Pixel. Daraus entstehen alle Fassungen, die HoneyOS braucht — SVG für die
|
||||
beiden Leisten, PNG für Dokumentation und Vorschau, und eine Fassung aus
|
||||
Schriftzeichen fürs System-Fenster und den Startlauf.
|
||||
|
||||
python3 tools/biene.py
|
||||
|
||||
Kein Pillow, keine Abhängigkeit: Ein PNG ist ein Kopfteil, ein zlib-Strom aus
|
||||
Bildzeilen (jede mit einem Filterbyte davor) und eine Prüfsumme — das kann die
|
||||
Standardbibliothek. Der Vorteil gegenüber einem Bildprogramm ist nicht die
|
||||
Bequemlichkeit, sondern dass die **Quelle Text bleibt**: Ein Pixel ändern heißt
|
||||
einen Buchstaben ändern, danach stimmen wieder alle Fassungen überein.
|
||||
"""
|
||||
import json
|
||||
import pathlib
|
||||
import struct
|
||||
import zlib
|
||||
|
||||
WURZEL = pathlib.Path(__file__).resolve().parent.parent
|
||||
|
||||
# Die Farben nach dem Vorbild: Die Streifen sind **braun**. Schwarz sind nur
|
||||
# Augen, Beine und Fühler — das ist der Unterschied zwischen warm und hart.
|
||||
FARBEN = {
|
||||
"Y": (242, 179, 61), # Honiggelb, der Körper
|
||||
"L": (255, 208, 96), # Oberkante, eine Spur heller
|
||||
"y": (210, 149, 42), # Unterseite im Schatten
|
||||
"S": (92, 58, 28), # Streifen
|
||||
"s": (70, 43, 20), # Streifen an der Unterkante
|
||||
"E": (24, 20, 16), # Augenfeld und Beine
|
||||
"B": (99, 199, 192), # der Lichtpunkt im Auge
|
||||
"D": (59, 36, 18), # Fühler
|
||||
"W": (245, 242, 231), # Flügel
|
||||
"w": (214, 209, 190), # Flügel, untere Lage
|
||||
}
|
||||
|
||||
# Von der Seite: Stachel hinten, Kopf in Laufrichtung. Diese Ansicht ist es
|
||||
# geworden, weil ihre Streifen **senkrecht** laufen — deshalb bleibt sie auch
|
||||
# als Text lesbar, wo die Vorderansicht in Streifen zerfällt.
|
||||
BIENE = [
|
||||
"....WW...WW.....",
|
||||
"..WWWWW.WWWWW...",
|
||||
"..WWWWWWWWWWW.D.",
|
||||
"...WWWWWWWWW.DD.",
|
||||
"...LLLLLLLLLLL..",
|
||||
"..YYSSYYSSYYYYY.",
|
||||
".YYYSSYYSSYYEBY.",
|
||||
"SYYYSSYYSSYYEEY.",
|
||||
".YYYSSYYSSYYYYY.",
|
||||
"..yssyyssyyyyyy.",
|
||||
"...EE..EE..EE...",
|
||||
"...EE..EE..EE...",
|
||||
"................",
|
||||
"................",
|
||||
"................",
|
||||
"................",
|
||||
]
|
||||
|
||||
|
||||
def svg(raster):
|
||||
"""Waagerechte Läufe gleicher Farbe werden ein Rechteck — dieselbe Idee wie
|
||||
bei den Pixelsymbolen in web/js/glyphs.js, nur mit Farbe statt currentColor.
|
||||
|
||||
Warum SVG und nicht PNG für die Leisten: Die HoneyBar läuft in *fremden*
|
||||
Apps. Ein <img> holt dort eine Datei von HoneyOS, und ob das erlaubt ist,
|
||||
entscheidet die CSP der fremden App. Markup kann niemand blockieren."""
|
||||
teile = []
|
||||
for y, zeile in enumerate(raster):
|
||||
x = 0
|
||||
while x < len(zeile):
|
||||
z = zeile[x]
|
||||
if z not in FARBEN:
|
||||
x += 1
|
||||
continue
|
||||
lauf = 1
|
||||
while x + lauf < len(zeile) and zeile[x + lauf] == z:
|
||||
lauf += 1
|
||||
r, g, b = FARBEN[z]
|
||||
teile.append(f'<rect x="{x}" y="{y}" width="{lauf}" height="1" fill="#{r:02x}{g:02x}{b:02x}"/>')
|
||||
x += lauf
|
||||
return ('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" '
|
||||
'shape-rendering="crispEdges" aria-hidden="true">' + "".join(teile) + "</svg>")
|
||||
|
||||
|
||||
def png(raster, faktor, ziel):
|
||||
"""Vergrößert wird um ganze Faktoren, damit jedes Pixel ein Quadrat bleibt."""
|
||||
n = len(raster)
|
||||
roh = bytearray()
|
||||
for zeile in raster:
|
||||
pixel = bytearray()
|
||||
for zeichen in zeile:
|
||||
r, g, b = FARBEN.get(zeichen, (0, 0, 0))
|
||||
pixel += bytes((r, g, b, 255 if zeichen in FARBEN else 0)) * faktor
|
||||
for _ in range(faktor):
|
||||
roh += b"\x00" + pixel # Filterbyte 0: keine Vorhersage
|
||||
|
||||
def block(art, daten):
|
||||
return (struct.pack(">I", len(daten)) + art + daten
|
||||
+ struct.pack(">I", zlib.crc32(art + daten) & 0xFFFFFFFF))
|
||||
|
||||
kopf = struct.pack(">IIBBBBB", n * faktor, n * faktor, 8, 6, 0, 0, 0)
|
||||
ziel.write_bytes(b"\x89PNG\r\n\x1a\n" + block(b"IHDR", kopf)
|
||||
+ block(b"IDAT", zlib.compress(bytes(roh), 9)) + block(b"IEND", b""))
|
||||
|
||||
|
||||
# Ein Zeichen ist etwa halb so breit wie hoch — **ein Pixel sind zwei Zeichen**,
|
||||
# sonst steht die Biene hochkant. Blockzeichen (█ ▓ ▒) wären naheliegend,
|
||||
# kacheln aber nicht: Zwischen den Zellen bleiben Fugen, und der Körper wird zur
|
||||
# Waffel. Gewöhnliche Schriftzeichen behaupten gar nicht erst, eine Fläche zu
|
||||
# sein — deshalb sehen sie besser aus.
|
||||
ZEICHEN = {"Y": "#", "L": "#", "y": "#", "S": ":", "s": ":", "E": ":", "D": ":", "W": "-", "B": "o"}
|
||||
|
||||
|
||||
def text(raster):
|
||||
zeilen = ["".join(ZEICHEN.get(c, " ") * 2 for c in z).rstrip() for z in raster]
|
||||
while zeilen and not zeilen[0]:
|
||||
zeilen.pop(0)
|
||||
while zeilen and not zeilen[-1]:
|
||||
zeilen.pop()
|
||||
return "\n".join(zeilen)
|
||||
|
||||
|
||||
def main():
|
||||
bild = svg(BIENE)
|
||||
zeichen = text(BIENE)
|
||||
|
||||
(WURZEL / "web/img/biene.svg").write_text(bild + "\n")
|
||||
png(BIENE, 16, WURZEL / "web/img/biene-256.png")
|
||||
png(BIENE, 2, WURZEL / "web/img/biene-32.png")
|
||||
|
||||
# Für die Oberfläche als Modul: Das Wappen im Markup (die Leiste holt kein
|
||||
# Bild nach) und die Zeichenfassung fürs System-Fenster.
|
||||
(WURZEL / "web/js/biene.js").write_text(
|
||||
"/* Erzeugt von tools/biene.py — nicht von Hand ändern.\n"
|
||||
" *\n"
|
||||
" * Das Wappen als Markup statt als Bilddatei: So kommt es ohne zweite\n"
|
||||
" * Anfrage aus und lässt sich mit dem Text daneben ausrichten.\n"
|
||||
" */\n"
|
||||
# Einfache Anführungszeichen: So steht das Markup wörtlich da und ist
|
||||
# mit web/img/biene.svg vergleichbar — ein Test hält beides gleich.
|
||||
f"export const BIENE_SVG = '{bild}';\n\n"
|
||||
f"export const BIENE_ASCII = {json.dumps(zeichen)};\n"
|
||||
)
|
||||
print(text(BIENE))
|
||||
print(f"\nweb/img/biene.svg ({len(bild)} Zeichen), biene-256.png, biene-32.png, web/js/biene.js")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user