66 lines
2.8 KiB
Python
66 lines
2.8 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
"""Génère src/mask-data.ts à partir de assets/mask.png (nécessite Pillow : pip install pillow).
|
|||
|
|
|
|||
|
|
Usage : python3 scripts/make-mask.py [taille] (32 par défaut ; l'image affichée fait taille × taille pixels,
|
|||
|
|
soit taille colonnes × taille/2 lignes de terminal)
|
|||
|
|
|
|||
|
|
Le fond uni (couleur des coins) est rendu transparent, l'image est recadrée au carré puis réduite
|
|||
|
|
en moyennant les couleurs : aucune pixelisation par blocs, aucune réduction de palette.
|
|||
|
|
"""
|
|||
|
|
import sys
|
|||
|
|
from collections import deque
|
|||
|
|
from pathlib import Path
|
|||
|
|
from PIL import Image
|
|||
|
|
|
|||
|
|
N = int(sys.argv[1]) if len(sys.argv) > 1 else 32
|
|||
|
|
root = Path(__file__).resolve().parent.parent
|
|||
|
|
im = Image.open(root / "assets" / "mask.png").convert("RGB")
|
|||
|
|
W, H = im.size
|
|||
|
|
px = im.load()
|
|||
|
|
bg = px[0, 0]
|
|||
|
|
near = lambda c: all(abs(c[i] - bg[i]) <= 12 for i in range(3))
|
|||
|
|
|
|||
|
|
# Fond = zone connectée aux coins et proche de la couleur du fond (le visage crème est isolé par son contour noir).
|
|||
|
|
transparent = [[False] * W for _ in range(H)]
|
|||
|
|
queue = deque([(0, 0), (W - 1, 0), (0, H - 1), (W - 1, H - 1)])
|
|||
|
|
while queue:
|
|||
|
|
x, y = queue.popleft()
|
|||
|
|
if x < 0 or y < 0 or x >= W or y >= H or transparent[y][x] or not near(px[x, y]):
|
|||
|
|
continue
|
|||
|
|
transparent[y][x] = True
|
|||
|
|
queue.extend([(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)])
|
|||
|
|
|
|||
|
|
xs = [x for y in range(H) for x in range(W) if not transparent[y][x]]
|
|||
|
|
ys = [y for y in range(H) for x in range(W) if not transparent[y][x]]
|
|||
|
|
x0, x1, y0, y1 = min(xs), max(xs) + 1, min(ys), max(ys) + 1
|
|||
|
|
side = max(x1 - x0, y1 - y0)
|
|||
|
|
ox = x0 - (side - (x1 - x0)) // 2
|
|||
|
|
oy = y0 - (side - (y1 - y0)) // 2
|
|||
|
|
|
|||
|
|
rows = []
|
|||
|
|
for gy in range(N):
|
|||
|
|
row = ""
|
|||
|
|
for gx in range(N):
|
|||
|
|
total = opaque = 0
|
|||
|
|
acc = [0, 0, 0]
|
|||
|
|
for y in range(int(oy + gy * side / N), int(oy + (gy + 1) * side / N) + 1):
|
|||
|
|
for x in range(int(ox + gx * side / N), int(ox + (gx + 1) * side / N) + 1):
|
|||
|
|
total += 1
|
|||
|
|
if 0 <= x < W and 0 <= y < H and not transparent[y][x]:
|
|||
|
|
opaque += 1
|
|||
|
|
for i in range(3):
|
|||
|
|
acc[i] += px[x, y][i]
|
|||
|
|
# moyenne des pixels opaques uniquement (évite le liseré clair du fond) ; transparent si moins de la moitié est opaque
|
|||
|
|
row += "......" if opaque * 2 < total or opaque == 0 else "".join("%02x" % round(a / opaque) for a in acc)
|
|||
|
|
rows.append(row)
|
|||
|
|
|
|||
|
|
lines = "\n".join(f' "{r}",' for r in rows)
|
|||
|
|
(root / "src" / "mask-data.ts").write_text(
|
|||
|
|
"// Fichier généré par scripts/make-mask.py à partir de assets/mask.png : ne pas modifier à la main.\n"
|
|||
|
|
"// Une chaîne par ligne de pixels ; chaque pixel = 6 chiffres hexadécimaux rrggbb, \"......\" = transparent.\n"
|
|||
|
|
f"export const MASK_SIZE = {N};\n\n"
|
|||
|
|
f"export const MASK_DATA: string[] = [\n{lines}\n];\n",
|
|||
|
|
encoding="utf-8",
|
|||
|
|
)
|
|||
|
|
print(f"{N}x{N} écrit dans src/mask-data.ts")
|