diff --git a/assets/favicon/apple-touch-icon.png b/assets/favicon/apple-touch-icon.png new file mode 100644 index 0000000..184ab6e Binary files /dev/null and b/assets/favicon/apple-touch-icon.png differ diff --git a/assets/favicon/favicon-16.png b/assets/favicon/favicon-16.png new file mode 100644 index 0000000..5905802 Binary files /dev/null and b/assets/favicon/favicon-16.png differ diff --git a/assets/favicon/favicon-32.png b/assets/favicon/favicon-32.png new file mode 100644 index 0000000..31e5f9f Binary files /dev/null and b/assets/favicon/favicon-32.png differ diff --git a/assets/favicon/favicon-48.png b/assets/favicon/favicon-48.png new file mode 100644 index 0000000..acb60ac Binary files /dev/null and b/assets/favicon/favicon-48.png differ diff --git a/assets/favicon/favicon.ico b/assets/favicon/favicon.ico new file mode 100644 index 0000000..dd1fcbf Binary files /dev/null and b/assets/favicon/favicon.ico differ diff --git a/assets/favicon/icon-192.png b/assets/favicon/icon-192.png new file mode 100644 index 0000000..05b6f56 Binary files /dev/null and b/assets/favicon/icon-192.png differ diff --git a/assets/favicon/icon-512.png b/assets/favicon/icon-512.png new file mode 100644 index 0000000..e70f65d Binary files /dev/null and b/assets/favicon/icon-512.png differ diff --git a/index.html b/index.html index 3d93daf..db2b592 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,11 @@ La Leggenda del Regolo + + + + + diff --git a/tools/favicon.py b/tools/favicon.py new file mode 100644 index 0000000..23f9aa9 --- /dev/null +++ b/tools/favicon.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +"""Disegna il Regolino, pixel per pixel, senza immagini sorgenti. + + python3 tools/favicon.py + +Scrive assets/favicon/ e /tmp/regolo-shots/favicon-anteprima.png. +La matrice 16×16 è il disegno originale: ogni simbolo è un pixel. +Tutti gli ingrandimenti sono interi e usano NEAREST, anche nell'ICO. +Colori e carattere riprendono regolo() in src/engine/briscola-render.js. +""" + +from pathlib import Path + +from PIL import Image, ImageDraw, ImageFont + + +ROOT = Path(__file__).resolve().parent.parent +OUTPUT = ROOT / "assets" / "favicon" +PREVIEW = Path("/tmp/regolo-shots/favicon-anteprima.png") +BACKGROUND = "#1b1420" +PALETTE = { + ".": (0, 0, 0, 0), + "#": "#38271f", # contorno del dorso + "G": "#dba32b", # oro + "H": "#ffe396", # luce sulla corona + "R": "#aa4034", # rubino + "L": "#94b365", # testa verde salvia + "S": "#487348", # ombra / spira + "V": "#a3bc72", # luce sulla spira + "B": "#d0cd83", # ventre + "W": "#fff0cd", # occhi crema +} + +# Corona a tre punte, grande testa sorridente, collo e coda arrotolata. +# Nessun tratto viene interpolato: anche gli occhi sono singoli pixel. +PIXELS = ( + "....#..##..#....", + "...#H##HH##H#...", + "...#HGGGGGGH#...", + "....#GGRGGG#....", + "...##########...", + "..#LLLLLLLLLL#..", + ".#LLWWLLWWLLLL#.", + ".#LLW#LL#WLLLS#.", + ".#LLLLLLLLLLLS#.", + "..#L#LLLLL#LS#..", + "...#L#####LS#...", + "....###BS###....", + "..###SSBSLSS##..", + ".#SVV##BS##SSS#.", + ".#SVVVVVVVVSS#..", + "..###########...", +) + + +def sprite(): + """Interpreta la matrice letterale, mantenendo solo alpha 0 o 255.""" + if len(PIXELS) != 16 or any(len(row) != 16 for row in PIXELS): + raise ValueError("Il disegno deve contenere esattamente 16×16 pixel") + result = Image.new("RGBA", (16, 16)) + for y, row in enumerate(PIXELS): + for x, symbol in enumerate(row): + color = PALETTE[symbol] + if isinstance(color, str): + color = tuple(bytes.fromhex(color[1:])) + (255,) + result.putpixel((x, y), color) + return result + + +def enlarge(source, factor): + return source.resize( + (source.width * factor, source.height * factor), Image.Resampling.NEAREST + ) + + +def preview(icons): + """Mostra ogni PNG a 1× e 8×, sia su chiaro sia su scuro. + + Le sei righe includono anche le icone grandi: il foglio è volutamente + ampio, così la scala dichiarata resta esatta per tutti i file. + """ + margin, gap, label_height = 24, 24, 32 + largest = max(icon.width for _, icon in icons) + panel_width = margin * 2 + largest + gap + largest * 8 + height = margin + sum(label_height + icon.height * 8 + gap for _, icon in icons) + sheet = Image.new("RGB", (panel_width * 2, height)) + draw = ImageDraw.Draw(sheet) + font = ImageFont.load_default(size=16) + for panel, (background, foreground) in enumerate( + (("#f5f1e9", "#38271f"), (BACKGROUND, "#fff0cd")) + ): + left = panel * panel_width + draw.rectangle((left, 0, left + panel_width - 1, height - 1), fill=background) + y = margin + for name, icon in icons: + draw.text( + (left + margin, y), + f"{name} | {icon.width} x {icon.height} | 1x e 8x", + fill=foreground, + font=font, + ) + y += label_height + sheet.paste(icon, (left + margin, y), icon) + magnified = enlarge(icon, 8) + sheet.paste(magnified, (left + margin + icon.width + gap, y), magnified) + y += icon.height * 8 + gap + PREVIEW.parent.mkdir(parents=True, exist_ok=True) + sheet.save(PREVIEW, optimize=True) + + +def main(): + OUTPUT.mkdir(parents=True, exist_ok=True) + base = sprite() + icons = [] + for size in (16, 32, 48): + icon = enlarge(base, size // 16) + name = f"favicon-{size}.png" + icon.save(OUTPUT / name, optimize=True) + icons.append((name, icon)) + + # 16×9 = 144: margini di 18 px, centratura esatta sul fondo opaco. + apple = Image.new("RGBA", (180, 180), BACKGROUND) + apple.alpha_composite(enlarge(base, 9), (18, 18)) + apple = apple.convert("RGB") + apple.save(OUTPUT / "apple-touch-icon.png", optimize=True) + icons.append(("apple-touch-icon.png", apple.convert("RGBA"))) + + for size in (192, 512): + icon = enlarge(base, size // 16) + name = f"icon-{size}.png" + icon.save(OUTPUT / name, optimize=True) + icons.append((name, icon)) + + # Fornire tutti i frame evita il ridimensionamento implicito di Pillow. + icons[2][1].save( + OUTPUT / "favicon.ico", + format="ICO", + sizes=[(16, 16), (32, 32), (48, 48)], + append_images=[icons[0][1], icons[1][1]], + ) + preview(icons) + for name, _ in icons: + print(OUTPUT / name) + print(OUTPUT / "favicon.ico") + print(PREVIEW) + + +if __name__ == "__main__": + main()