65 lines
2.8 KiB
Python
65 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")
|