Files
aetherbound-guild/tools/build_asset_contact_sheet.py
T

64 lines
2.2 KiB
Python

#!/usr/bin/env python3
"""Build a labeled checkerboard contact sheet from transparent PNG assets."""
from __future__ import annotations
import argparse
from pathlib import Path
from PIL import Image, ImageDraw
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--input-dir", type=Path, required=True)
parser.add_argument("--pattern", default="*.png")
parser.add_argument("--cols", type=int, default=4)
parser.add_argument("--output", type=Path, required=True)
return parser.parse_args()
def checker(size: tuple[int, int], step: int = 16) -> Image.Image:
image = Image.new("RGB", size, (238, 238, 238))
draw = ImageDraw.Draw(image)
for y in range(0, size[1], step):
for x in range(0, size[0], step):
if (x // step + y // step) % 2:
draw.rectangle((x, y, x + step - 1, y + step - 1), fill=(210, 210, 210))
return image
def main() -> None:
args = parse_args()
files = sorted(args.input_dir.glob(args.pattern))
if not files:
raise SystemExit(f"no assets match {args.input_dir / args.pattern}")
if args.cols <= 0:
raise SystemExit("--cols must be positive")
cell_width = 300
cell_height = 330
art_size = 256
rows = (len(files) + args.cols - 1) // args.cols
sheet = Image.new("RGB", (args.cols * cell_width, rows * cell_height), (24, 29, 38))
draw = ImageDraw.Draw(sheet)
for index, path in enumerate(files):
x = (index % args.cols) * cell_width
y = (index // args.cols) * cell_height
panel = checker((art_size, art_size))
asset = Image.open(path).convert("RGBA")
asset.thumbnail((art_size, art_size), Image.Resampling.LANCZOS)
offset = ((art_size - asset.width) // 2, (art_size - asset.height) // 2)
panel.paste(asset, offset, asset)
sheet.paste(panel, (x + 22, y + 18))
draw.text((x + 22, y + 286), path.stem, fill=(244, 240, 224))
args.output.parent.mkdir(parents=True, exist_ok=True)
sheet.save(args.output, optimize=True)
print(f"CONTACT_SHEET_OK assets={len(files)} output={args.output}")
if __name__ == "__main__":
main()