mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-28 23:00:21 -04:00
121 lines
5.0 KiB
Python
121 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generates the Android adaptive launcher icon from assets/logo/gen1recomp_cover.png
|
|
and 3D cartridge shortcut assets for every density bucket
|
|
(mdpi, hdpi, xhdpi, xxhdpi, xxxhdpi).
|
|
"""
|
|
|
|
import os
|
|
from PIL import Image, ImageDraw, ImageFilter
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
RES_DIR = os.path.join(ROOT, "mobile", "android", "app", "src", "main", "res")
|
|
COVER = os.path.join(ROOT, "assets", "logo", "gen1recomp_cover.png")
|
|
|
|
DENSITIES = {
|
|
"mdpi": {"shortcut": 48, "adaptive": 108},
|
|
"hdpi": {"shortcut": 72, "adaptive": 162},
|
|
"xhdpi": {"shortcut": 96, "adaptive": 216},
|
|
"xxhdpi": {"shortcut": 144, "adaptive": 324},
|
|
"xxxhdpi": {"shortcut": 192, "adaptive": 432},
|
|
}
|
|
|
|
SHELL_COLORS = {
|
|
"red": {"main": (230, 45, 55), "dark": (175, 25, 35), "light": (255, 90, 100)},
|
|
"blue": {"main": (35, 125, 235), "dark": (20, 85, 175), "light": (80, 165, 255)},
|
|
"yellow": {"main": (255, 205, 10), "dark": (210, 160, 0), "light": (255, 230, 80)},
|
|
"gold": {"main": (225, 170, 40), "dark": (170, 120, 20), "light": (245, 200, 80)},
|
|
"silver": {"main": (185, 190, 200), "dark": (130, 135, 145), "light": (225, 230, 240)},
|
|
}
|
|
|
|
def create_adaptive_foreground(size):
|
|
"""Full-bleed cover. The launcher mask crops the edges; the mark stays centered."""
|
|
cover = Image.open(COVER).convert("RGBA")
|
|
return cover.resize((size, size), Image.Resampling.LANCZOS)
|
|
|
|
def render_3d_cartridge(version, size):
|
|
colors = SHELL_COLORS[version]
|
|
S = size * 4
|
|
|
|
# Transparent canvas so the cartridge sits directly on launcher's white circle plate
|
|
canvas = Image.new("RGBA", (S, S), (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(canvas)
|
|
|
|
# Tight padding to maximize cartridge size
|
|
pad_x = int(S * 0.05)
|
|
pad_y = int(S * 0.03)
|
|
cw = S - pad_x * 2
|
|
ch = S - pad_y * 2
|
|
|
|
# Soft drop shadow
|
|
shadow = Image.new("RGBA", (S, S), (0, 0, 0, 0))
|
|
sdraw = ImageDraw.Draw(shadow)
|
|
sdraw.rounded_rectangle([pad_x + 8, pad_y + 14, pad_x + cw + 8, pad_y + ch + 14],
|
|
radius=int(S * 0.05), fill=(0, 0, 0, 90))
|
|
shadow = shadow.filter(ImageFilter.GaussianBlur(int(S * 0.03)))
|
|
canvas.paste(shadow, (0, 0), shadow)
|
|
|
|
radius = int(S * 0.045)
|
|
depth = int(S * 0.03)
|
|
draw.rounded_rectangle([pad_x, pad_y + depth, pad_x + cw, pad_y + ch + depth],
|
|
radius=radius, fill=colors["dark"])
|
|
draw.rounded_rectangle([pad_x, pad_y, pad_x + cw, pad_y + ch],
|
|
radius=radius, fill=colors["main"])
|
|
|
|
notch_w = int(cw * 0.65)
|
|
notch_h = int(ch * 0.07)
|
|
notch_x = pad_x + (cw - notch_w) // 2
|
|
notch_y = pad_y + int(ch * 0.035)
|
|
draw.rounded_rectangle([notch_x, notch_y, notch_x + notch_w, notch_y + notch_h],
|
|
radius=int(notch_h * 0.4), fill=colors["dark"])
|
|
draw.rounded_rectangle([notch_x, notch_y - 2, notch_x + notch_w, notch_y + notch_h - 2],
|
|
radius=int(notch_h * 0.4), fill=colors["light"])
|
|
|
|
label_margin_x = int(cw * 0.09)
|
|
label_top_y = pad_y + int(ch * 0.20)
|
|
label_w = cw - label_margin_x * 2
|
|
label_h = int(ch * 0.70)
|
|
label_x = pad_x + label_margin_x
|
|
|
|
draw.rounded_rectangle([label_x - 3, label_top_y - 3, label_x + label_w + 3, label_top_y + label_h + 3],
|
|
radius=int(radius * 0.7), fill=colors["dark"])
|
|
|
|
label_path = os.path.join(ROOT, "assets", "labels", f"{version}.png")
|
|
if os.path.exists(label_path):
|
|
label_img = Image.open(label_path).convert("RGBA")
|
|
label_img = label_img.resize((label_w, label_h), Image.Resampling.LANCZOS)
|
|
|
|
mask = Image.new("L", (label_w, label_h), 0)
|
|
mdraw = ImageDraw.Draw(mask)
|
|
mdraw.rounded_rectangle([0, 0, label_w, label_h], radius=int(radius * 0.5), fill=255)
|
|
|
|
canvas.paste(label_img, (label_x, label_top_y), mask)
|
|
else:
|
|
draw.rounded_rectangle([label_x, label_top_y, label_x + label_w, label_top_y + label_h],
|
|
radius=int(radius * 0.5), fill=(240, 240, 240, 255))
|
|
|
|
draw.rounded_rectangle([pad_x, pad_y, pad_x + cw, pad_y + ch],
|
|
radius=radius, outline=colors["light"], width=max(2, int(S * 0.008)))
|
|
|
|
return canvas.resize((size, size), Image.Resampling.LANCZOS)
|
|
|
|
def main():
|
|
os.makedirs(os.path.join(RES_DIR, "values"), exist_ok=True)
|
|
os.makedirs(os.path.join(RES_DIR, "mipmap-anydpi-v26"), exist_ok=True)
|
|
|
|
for density, sizes in DENSITIES.items():
|
|
drawable_dir = os.path.join(RES_DIR, f"drawable-{density}")
|
|
os.makedirs(drawable_dir, exist_ok=True)
|
|
|
|
fg = create_adaptive_foreground(sizes["adaptive"])
|
|
fg.save(os.path.join(drawable_dir, "ic_launcher_foreground.png"), "PNG")
|
|
|
|
for ver in ("red", "blue", "yellow", "gold", "silver"):
|
|
cart = render_3d_cartridge(ver, sizes["shortcut"])
|
|
cart.save(os.path.join(drawable_dir, f"ic_shortcut_{ver}.png"), "PNG")
|
|
|
|
print(f"Generated assets for drawable-{density}")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|