#!/usr/bin/env python3 """Generate the 5 missing UI slices to match existing style-board exports.""" from __future__ import annotations from pathlib import Path import numpy as np from PIL import Image, ImageDraw, ImageEnhance, ImageFilter SLICES = Path(r"d:\UD\AI\AIC#Project\Doc\design\UI\slices") def fit_canvas(im: Image.Image, tw: int, th: int) -> Image.Image: im = im.convert("RGBA") bbox = im.getbbox() if bbox: im = im.crop(bbox) out = Image.new("RGBA", (tw, th), (0, 0, 0, 0)) scale = min(tw / max(im.width, 1), th / max(im.height, 1)) nw = max(1, int(im.width * scale)) nh = max(1, int(im.height * scale)) resized = im.resize((nw, nh), Image.Resampling.LANCZOS) out.paste(resized, ((tw - nw) // 2, (th - nh) // 2), resized) return out def make_hover(src_name: str, dst_name: str, size: tuple[int, int], brightness=1.18, color=1.12, contrast=1.05): src = Image.open(SLICES / src_name).convert("RGBA") rgb = src.convert("RGB") rgb = ImageEnhance.Brightness(rgb).enhance(brightness) rgb = ImageEnhance.Color(rgb).enhance(color) rgb = ImageEnhance.Contrast(rgb).enhance(contrast) # stronger top highlight: blend a light gradient on opaque pixels arr = np.array(rgb) alpha = np.array(src.split()[-1]) h, w = alpha.shape # lift luminance slightly more on top half of opaque region ys = np.linspace(1.08, 0.98, h).reshape(h, 1, 1) arr = np.clip(arr.astype(np.float32) * ys, 0, 255).astype(np.uint8) out = Image.fromarray(arr, "RGB").convert("RGBA") out.putalpha(Image.fromarray(alpha, "L")) # soft extra rim glow glow = out.filter(ImageFilter.GaussianBlur(1)) composed = Image.alpha_composite(Image.new("RGBA", out.size, (0, 0, 0, 0)), glow) composed = Image.alpha_composite(composed, out) fit_canvas(composed, *size).save(SLICES / dst_name, "PNG") print("wrote", dst_name) def make_caret(): # 8x64 blue caret matching focus input border blue tw, th = 8, 64 im = Image.new("RGBA", (tw, th), (0, 0, 0, 0)) d = ImageDraw.Draw(im) # soft glow d.rectangle((2, 4, 5, th - 5), fill=(120, 180, 240, 90)) d.rectangle((3, 6, 4, th - 7), fill=(70, 140, 220, 255)) # tiny top/bottom caps d.rectangle((2, 6, 5, 9), fill=(180, 220, 255, 255)) d.rectangle((2, th - 10, 5, th - 7), fill=(50, 110, 190, 255)) im.save(SLICES / "input_caret.png", "PNG") print("wrote input_caret.png") def make_sound_off(): src = Image.open(SLICES / "ico_sound_on.png").convert("RGBA") # work on larger canvas then fit back to 64 big = fit_canvas(src, 128, 128) d = ImageDraw.Draw(big) # red mute slash with dark outline # from top-right to bottom-left across icon pts = [(98, 28), (108, 36), (38, 108), (28, 100)] d.polygon(pts, fill=(160, 40, 40, 255)) pts_inner = [(96, 32), (104, 38), (40, 104), (32, 98)] d.polygon(pts_inner, fill=(230, 70, 70, 255)) # slight highlight on slash d.line([(92, 34), (42, 92)], fill=(255, 160, 160, 200), width=2) fit_canvas(big, 64, 64).save(SLICES / "ico_sound_off.png", "PNG") print("wrote ico_sound_off.png") def make_item_placeholder_then_wait(): """Fallback potion/item icon if AI gen unavailable — stylized potion vial.""" tw, th = 64, 64 im = Image.new("RGBA", (tw, th), (0, 0, 0, 0)) d = ImageDraw.Draw(im) # cork d.rounded_rectangle((26, 6, 38, 14), 2, fill=(180, 120, 60, 255), outline=(100, 60, 30, 255)) # neck d.rectangle((28, 14, 36, 22), fill=(220, 230, 240, 220), outline=(90, 70, 50, 255)) # bottle body d.rounded_rectangle((16, 20, 48, 58), 10, fill=(90, 200, 140, 255), outline=(40, 100, 70, 255), width=2) # liquid highlight d.ellipse((20, 26, 30, 36), fill=(200, 255, 220, 180)) # sparkle d.ellipse((40, 28, 44, 32), fill=(255, 255, 255, 220)) # label band d.rectangle((18, 38, 46, 46), fill=(255, 245, 210, 230)) d.ellipse((28, 39, 36, 45), fill=(230, 90, 90, 255)) im.save(SLICES / "ico_item.png", "PNG") print("wrote ico_item.png (procedural fallback)") def main(): make_hover("btn_primary_normal.png", "btn_primary_hover.png", (256, 96), 1.20, 1.15, 1.06) make_hover("btn_small_normal.png", "btn_small_hover.png", (192, 72), 1.18, 1.14, 1.05) make_caret() make_sound_off() # ico_item is produced by AI gen + postprocess; keep procedural only as fallback if not (SLICES / "ico_item.png").exists(): make_item_placeholder_then_wait() if __name__ == "__main__": main()