Bestiary portraits in the high-res style: a painted pixel-art bust for every creature

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-08 00:05:41 +01:00
1 parent a24fc51605
commit 8d2362fc11
41 files changed
+308 -1

No files matched your search

@@ -0,0 +1,33 @@
#!/usr/bin/env python3
# Raw portraits (1024x1024) -> 128x128 pixel-art portraits (box downscale, 48 colours, no
# dithering, opaque) and the atlas assets/portraits/portraits.png (8 columns) + order.txt.
import os, sys
from PIL import Image, ImageEnhance
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from portraits import CREATURES
ROOT = "/home/zink/Projects/dungeoncraft"
D = f"{ROOT}/art/generated/portraits"
OUT = f"{ROOT}/assets/portraits"
SIZE, COLORS, COLS = 128, 48, 8
CROP = {} # id -> (l, t, r, b) fractions, default the whole picture
os.makedirs(OUT, exist_ok=True)
rows = (len(CREATURES) + COLS - 1) // COLS
atlas = Image.new("RGB", (COLS * SIZE, rows * SIZE), (0, 0, 0))
order = []
for i, c in enumerate(CREATURES):
im = Image.open(f"{D}/{c['id']}-raw.png").convert("RGB")
l, t, r, b = CROP.get(c["id"], (0, 0, 1, 1))
w, h = im.size
im = im.crop((round(l * w), round(t * h), round(r * w), round(b * h)))
im = im.resize((SIZE, SIZE), Image.BOX)
im = ImageEnhance.Sharpness(im).enhance(1.6)
im = im.quantize(colors=COLORS, method=Image.Quantize.MAXCOVERAGE, kmeans=4, dither=Image.Dither.NONE).convert("RGB")
im.save(f"{D}/{c['id']}-{SIZE}.png", optimize=True)
atlas.paste(im, ((i % COLS) * SIZE, (i // COLS) * SIZE))
order.append(c["kind"])
atlas.save(f"{OUT}/portraits.png", optimize=True)
open(f"{OUT}/order.txt", "w").write("\n".join(order) + "\n")
print(atlas.size, os.path.getsize(f"{OUT}/portraits.png"), "bytes")