Files
ai-game-2/tools/make_ora.py
2026-03-09 10:54:10 -07:00

154 lines
3.6 KiB
Python

import argparse
import os
import zipfile
import tempfile
import shutil
import numpy as np
from PIL import Image, ImageChops
import xml.etree.ElementTree as ET
NUM_LAYERS = 4
MASKS_PER_LAYER = 3
NOISE_SCALES = [4, 16, 64]
def noise_mask(w, h, scale):
sw = max(1, w // scale)
sh = max(1, h // scale)
noise = np.random.rand(sh, sw)
img = Image.fromarray((noise * 255).astype(np.uint8), "L")
img = img.resize((w, h), Image.BILINEAR)
return img
def build_stack_xml(w, h, groups):
image = ET.Element(
"image",
{"version": "0.0.3", "w": str(w), "h": str(h)}
)
root_stack = ET.SubElement(image, "stack")
for group in groups:
group_el = ET.SubElement(
root_stack,
"stack",
{"name": group["name"]}
)
for layer in group["layers"]:
layer_attrs = {
"name": layer["name"],
"src": layer["src"],
"opacity": "1.0"
}
ET.SubElement(
group_el,
"layer",
layer_attrs
)
return ET.tostring(image, encoding="utf-8", xml_declaration=True)
def build_ora(input_png, output_ora):
base = Image.open(input_png).convert("RGBA")
w, h = base.size
tmp = tempfile.mkdtemp()
try:
data_dir = os.path.join(tmp, "data")
thumb_dir = os.path.join(tmp, "Thumbnails")
os.makedirs(data_dir)
os.makedirs(thumb_dir)
groups = []
for i in range(NUM_LAYERS):
group_layers = []
for j in range(MASKS_PER_LAYER):
mask = noise_mask(w, h, NOISE_SCALES[j])
# Apply mask to alpha channel of base image
masked_image = base.copy()
r, g, b, a = masked_image.split()
# Multiply existing alpha by mask
new_alpha = ImageChops.multiply(a, mask)
masked_image.putalpha(new_alpha)
layer_path = f"data/layer_{i}_{j}.png"
masked_image.save(os.path.join(tmp, layer_path))
group_layers.append({
"name": f"layer {j}",
"src": layer_path
})
groups.append({
"name": f"group {i}",
"layers": group_layers
})
stack_xml = build_stack_xml(w, h, groups)
with open(os.path.join(tmp, "stack.xml"), "wb") as f:
f.write(stack_xml)
with open(os.path.join(tmp, "mimetype"), "w") as f:
f.write("image/openraster")
base.save(os.path.join(tmp, "mergedimage.png"))
thumb = base.copy()
thumb.thumbnail((256, 256))
thumb.save(os.path.join(thumb_dir, "thumbnail.png"))
with zipfile.ZipFile(output_ora, "w") as z:
z.write(
os.path.join(tmp, "mimetype"),
"mimetype",
compress_type=zipfile.ZIP_STORED
)
for root, _, files in os.walk(tmp):
for file in files:
if file == "mimetype":
continue
full = os.path.join(root, file)
rel = os.path.relpath(full, tmp)
z.write(full, rel)
finally:
shutil.rmtree(tmp)
def main():
parser = argparse.ArgumentParser(
description="Generate ORA with grouped layers and mask layers"
)
parser.add_argument("input_png")
parser.add_argument("output_ora")
args = parser.parse_args()
build_ora(args.input_png, args.output_ora)
if __name__ == "__main__":
main()