VDB: dump captured palettes (VDB_PALDUMP) + swatch renderer
VDB_PALDUMP=<prefix> writes each completed 768-byte palette load to <prefix>N.rgb (256 RGB entries per VDB display group). render_vdb_palettes.py renders each as a 16x16 swatch grid + linear ramp. First capture: pal1 (aux1) is a structured red/green/olive/black color map (2-bit R x 2-bit G) -- likely the color display's status coloring; pal0 (secondary, dynamic ~29 reloads) and pal2 (aux2) are grayscale, sampled black mid-animation. Palettes are the per-display color maps the VDB applies when splitting the framebuffer to the six monitors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Render captured VDB palettes (VDB_PALDUMP <prefix>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:
|
||||
<prefix>N.png -- a 16x16 grid of the 256 colors + a 256-wide linear ramp.
|
||||
|
||||
Usage: render_vdb_palettes.py <prefix> (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()
|
||||
Reference in New Issue
Block a user