- Manifest multiarch i386 (Compat.i386 + GL32 + gamescope) - Wrapper launcher con EULA dialog GTK3 (persistente), audio PipeWire, saves - sdl12-compat build i386, risoluzione 1024x768@16, 60fps - Icona composita (mech + kanji + banner SHOGO, trasparente) 512/256 - icon-build.py (post-processing rembg) - Cartella flathub-submission pronta per la PR - install-shogo-demo.sh per PC nuovi
83 lines
3.0 KiB
Python
83 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Icona Shogo v2: rimozione sfondo con rembg (U2Net) + composizione kanji/mech/banner.
|
|
Uso: PYTHONPATH=~/.local/pylibs-rembg python3 icon-build.py
|
|
"""
|
|
import sys
|
|
sys.path.insert(0, '/home/enne2/.local/pylibs-rembg')
|
|
from PIL import Image, ImageFilter
|
|
from rembg import remove
|
|
|
|
D = '/home/enne2/Dev/shogo-demo-flatpak'
|
|
SRC_MECH = f'{D}/shogo_game_icon_1787156055996.jpg'
|
|
SRC_BANNER = f'{D}/shogo_logo_banner_1787156448853.jpg'
|
|
|
|
# ---------- 1) mech: rimozione sfondo con rembg ----------
|
|
print('rembg: rimozione sfondo mech (può richiedere un paio di minuti su CPU)...')
|
|
with open(SRC_MECH, 'rb') as f:
|
|
inp = f.read()
|
|
out = remove(inp)
|
|
mech = Image.open(__import__('io').BytesIO(out)).convert('RGBA')
|
|
# pulizia: rimuovi eventuali pixel semitrasparenti isolati (opening della maschera alpha)
|
|
a = mech.split()[3].point(lambda v: 0 if v < 40 else 255)
|
|
mech.putalpha(a)
|
|
mech.save(f'{D}/icon-mech-alpha.png')
|
|
print('mech alpha:', mech.size)
|
|
|
|
# ---------- 2) kanji: estrazione dei pixel rosso-scuro (fondale) ----------
|
|
src = Image.open(SRC_MECH).convert('RGBA')
|
|
px = src.load()
|
|
W, H = src.size
|
|
kanji = Image.new('RGBA', (W, H), (0, 0, 0, 0))
|
|
kp = kanji.load()
|
|
for y in range(H):
|
|
for x in range(W):
|
|
r, g, b, a = px[x, y]
|
|
# rosso scuro del kanji: r dominante ma scuro, senza essere arancione acceso del mech
|
|
if r > 55 and r < 175 and r > g + 25 and r > b + 25 and a > 100:
|
|
kp[x, y] = (r, g, b, 255)
|
|
kanji.save(f'{D}/icon-kanji.png')
|
|
print('kanji estratto')
|
|
|
|
# ---------- 3) banner: estrazione e pulizia bordi ----------
|
|
bim = Image.open(SRC_BANNER).convert('RGBA')
|
|
bp = bim.load()
|
|
bw, bh = bim.size
|
|
xs, ys = [], []
|
|
for y in range(bh):
|
|
for x in range(bw):
|
|
r, g, b = bp[x, y][:3]
|
|
if (r + g + b) / 3 > 40:
|
|
xs.append(x); ys.append(y)
|
|
x0, x1, y0, y1 = max(0, min(xs)-14), min(bw-1, max(xs)+14), max(0, min(ys)-14), min(bh-1, max(ys)+14)
|
|
banner = bim.crop((x0, y0, x1+1, y1+1)).copy()
|
|
b2 = banner.load()
|
|
bw2, bh2 = banner.size
|
|
for y in range(bh2):
|
|
for x in range(bw2):
|
|
r, g, b = b2[x, y][:3]
|
|
if (r + g + b) / 3 <= 40:
|
|
b2[x, y] = (0, 0, 0, 0)
|
|
# erosione leggera dell'alpha per togliere l'alone
|
|
alpha = banner.split()[3].filter(ImageFilter.MinFilter(3))
|
|
banner.putalpha(alpha)
|
|
banner.save(f'{D}/icon-banner2.png')
|
|
print('banner:', banner.size)
|
|
|
|
# ---------- 4) composizione ----------
|
|
C = 1024
|
|
canvas = Image.new('RGBA', (C, C), (0, 0, 0, 0))
|
|
# kanji dietro, grande, opacità ~28%
|
|
k2 = kanji.resize((int(C*0.95), int(C*0.95)), Image.LANCZOS)
|
|
k2.putalpha(k2.split()[3].point(lambda v: int(v * 0.28)))
|
|
canvas.alpha_composite(k2, ((C-k2.width)//2, (C-k2.height)//2 - int(C*0.02)))
|
|
# mech sopra
|
|
m = mech.resize((C, C), Image.LANCZOS)
|
|
canvas.alpha_composite(m, (0, 0))
|
|
# banner in basso
|
|
bw3 = int(C * 0.60)
|
|
bh3 = int(banner.height * (bw3 / banner.width))
|
|
b3 = banner.resize((bw3, bh3), Image.LANCZOS)
|
|
canvas.alpha_composite(b3, ((C-bw3)//2, C - bh3 - int(C*0.035)))
|
|
canvas.save(f'{D}/icon-final2.png')
|
|
print('composta: icon-final2.png', canvas.size)
|