#!/usr/bin/env python3
# introimg.py source.png out.bin [preview.png] -- makes the intro picture (fix 12) for the Mega Drive:
# 320 x 224 (a 16:9 picture squeezed, RetroArch stretches it back), 8 grey levels (the hardware has 3 bits per channel)
# with Floyd-Steinberg dithering, palette 0 colours 1-8, 8x8 tiles, identical tiles shared.
# out.bin: palette (16 words) | map: width, height, 40*28 words (tile index from 1, palette 0) | tile count word | tiles.
import sys, struct
from PIL import Image
src, out = sys.argv[1], sys.argv[2]
im = Image.open(src).convert('L').resize((320, 224), Image.LANCZOS)
W, H = im.size
px = [[float(im.getpixel((x, y))) for x in range(W)] for y in range(H)]
lv = [[0] * W for _ in range(H)]
for y in range(H):
    for x in range(W):
        old = px[y][x]
        q = max(0, min(7, int(round(old * 7 / 255))))
        lv[y][x] = q
        err = old - q * 255 / 7
        for dx, dy, f in ((1, 0, 7 / 16), (-1, 1, 3 / 16), (0, 1, 5 / 16), (1, 1, 1 / 16)):
            xx, yy = x + dx, y + dy
            if 0 <= xx < W and 0 <= yy < H: px[yy][xx] += err * f
pal = [0x0000] + [(2 * k) << 8 | (2 * k) << 4 | (2 * k) for k in range(8)] + [0] * 7   # colour k+1 = grey level k
tiles = []; index = {}; mp = []
for ty in range(H // 8):
    for tx in range(W // 8):
        b = bytearray()
        for y in range(8):
            row = [lv[ty * 8 + y][tx * 8 + x] + 1 for x in range(8)]
            for x in range(0, 8, 2): b.append(row[x] << 4 | row[x + 1])
        b = bytes(b)
        if b not in index: index[b] = len(tiles) + 1; tiles.append(b)   # tile 0 stays empty
        mp.append(index[b])
data = struct.pack('>16H', *pal) + struct.pack('>HH', W // 8, H // 8) + struct.pack('>%dH' % len(mp), *mp) \
    + struct.pack('>H', len(tiles)) + b''.join(tiles)
open(out, 'wb').write(data)
print('intro picture:', len(tiles), 'tiles,', len(data), 'bytes')
if len(sys.argv) > 3:
    g = [0, 36, 73, 109, 146, 182, 219, 255]
    pv = Image.new('L', (W, H)); pv.putdata([g[lv[y][x]] for y in range(H) for x in range(W)]); pv.save(sys.argv[3])
