#!/usr/bin/env python3 """Porta uno sfondo generato alla risoluzione del gioco (640x360, o il doppio in larghezza per le panoramiche). python3 tools/prepare_background.py assets/raw/fortezza-esterno.png assets/scenes/fortezza-esterno.png \ --clone 625,332,12,12,-14,0 --crop x,y,w,h ritaglio nel file originale prima di ridurre (per immagini non in 16:9) --clone x,y,w,h,dx,dy copia sopra il riquadro x,y,w,h i pixel spostati di dx,dy (coordinate finali): serve a coprire filigrane o difetti con un pezzo di sfondo vicino; si può ripetere --colors N riduce la tavolozza a N colori senza retinatura (0 = lascia i colori come sono) """ import argparse from PIL import Image def box(text, n): values = [int(v) for v in text.split(',')] if len(values) != n: raise argparse.ArgumentTypeError(f'servono {n} numeri separati da virgola') return values def main(): p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) p.add_argument('src') p.add_argument('dst') p.add_argument('--size', default='640x360') p.add_argument('--crop', type=lambda t: box(t, 4)) p.add_argument('--clone', type=lambda t: box(t, 6), action='append', default=[]) p.add_argument('--colors', type=int, default=0) a = p.parse_args() w, h = (int(v) for v in a.size.split('x')) im = Image.open(a.src).convert('RGB') if a.crop: x, y, cw, ch = a.crop im = im.crop((x, y, x + cw, y + ch)) if abs(im.width / im.height - w / h) > 0.02: raise SystemExit(f'proporzioni {im.width}x{im.height} diverse da {w}x{h}: usa --crop') # BOX fa la media dei pixel di ogni blocco: niente aloni e niente sfocatura in più. im = im.resize((w, h), Image.BOX) for x, y, cw, ch, dx, dy in a.clone: im.paste(im.crop((x + dx, y + dy, x + dx + cw, y + dy + ch)), (x, y)) if a.colors: im = im.quantize(a.colors, dither=Image.Dither.NONE).convert('RGB') im.save(a.dst, optimize=True) print(f'{a.dst}: {w}x{h}') if __name__ == '__main__': main()