#!/usr/bin/env python3 """Render captured VDB palettes (VDB_PALDUMP N.rgb) as swatch images. Each .rgb is 256 RGB triplets (768 bytes) -- one of the VDB's three display palette groups (secondary / aux1 / aux2). For each, emit: N.png -- a 16x16 grid of the 256 colors + a 256-wide linear ramp. Usage: render_vdb_palettes.py (e.g. .../scratchpad/vdbpal) """ import sys from PIL import Image, ImageDraw, ImageFont NAMES = {0: "secondary (dynamic)", 1: "aux 1", 2: "aux 2"} def render_one(path, out, title): data = open(path, "rb").read() if len(data) < 768: data = data + b"\x00" * (768 - len(data)) cols = [(data[i * 3], data[i * 3 + 1], data[i * 3 + 2]) for i in range(256)] cell, grid = 28, 16 gw = cell * grid ramp_h = 40 pad, top = 12, 34 W = gw + pad * 2 H = top + gw + 10 + ramp_h + pad img = Image.new("RGB", (W, H), (24, 24, 28)) d = ImageDraw.Draw(img) try: font = ImageFont.truetype("consola.ttf", 16) except Exception: font = ImageFont.load_default() d.text((pad, 8), title, fill=(230, 230, 235), font=font) # 16x16 swatch grid (index 0 top-left, row-major) for i, c in enumerate(cols): x = pad + (i % grid) * cell y = top + (i // grid) * cell d.rectangle([x, y, x + cell - 1, y + cell - 1], fill=c) # linear ramp strip below ry = top + gw + 10 for i, c in enumerate(cols): x0 = pad + i * (gw / 256.0) x1 = pad + (i + 1) * (gw / 256.0) d.rectangle([x0, ry, x1, ry + ramp_h], fill=c) d.rectangle([pad, ry, pad + gw, ry + ramp_h], outline=(80, 80, 88)) img.save(out) # quick stats grays = sum(1 for r, g, b in cols if abs(r - g) < 6 and abs(g - b) < 6) print(f"{out}: {grays}/256 grayscale-ish entries") def main(): prefix = sys.argv[1] for g in (0, 1, 2): p = f"{prefix}{g}.rgb" try: render_one(p, f"{prefix}{g}.png", f"VDB palette {g} - {NAMES.get(g, '')}") except FileNotFoundError: print(f"(no {p})") if __name__ == "__main__": main()