hermes-hub/scripts/generate_branding_assets.py
2026-08-20 10:53:49 +07:00

111 lines
4.5 KiB
Python

"""Generate all brand assets for Hermes Hub:
- logo PNGs at multiple resolutions (1024, 512, 256, 128, 64, 32)
- app icons (including multi-layer HermesHub.ico with 16, 24, 32, 48, 64, 128, 256)
- splash screen assets
- installer assets
"""
import os
import sys
from pathlib import Path
from PIL import Image, ImageDraw, ImageFilter, ImageOps
import numpy as np
REPO_ROOT = Path(__file__).resolve().parent.parent
SOURCE_DIR = REPO_ROOT / "assets" / "branding" / "source"
LOGO_DIR = REPO_ROOT / "assets" / "branding" / "logo"
APP_DIR = REPO_ROOT / "assets" / "branding" / "app"
SPLASH_DIR = REPO_ROOT / "assets" / "branding" / "splash"
INSTALLER_DIR = REPO_ROOT / "assets" / "branding" / "installer"
ICONS_DIR = REPO_ROOT / "assets" / "branding" / "icons"
for d in [LOGO_DIR, APP_DIR, SPLASH_DIR, INSTALLER_DIR, ICONS_DIR]:
d.mkdir(parents=True, exist_ok=True)
raw_logo_path = SOURCE_DIR / "Hermes Hub.png"
if not raw_logo_path.exists():
print("Error: Source logo not found at", raw_logo_path)
sys.exit(1)
raw_logo = Image.open(raw_logo_path).convert("RGBA")
width, height = raw_logo.size
# The logo is a circular emblem. We want to extract the circular emblem cleanly.
# Find circular emblem boundary:
# Center is (width // 2, height // 2). Radius is approximately width * 0.46
cx, cy = width / 2.0, height / 2.0
# The outer gold ring extends to about r = width * 0.47
r = min(width, height) * 0.475
# Create a circular mask with anti-aliasing (supersampled 2x)
mask_hi = Image.new("L", (width * 2, height * 2), 0)
draw_hi = ImageDraw.Draw(mask_hi)
draw_hi.ellipse(
[(cx - r) * 2, (cy - r) * 2, (cx + r) * 2, (cy + r) * 2],
fill=255
)
mask = mask_hi.resize((width, height), Image.Resampling.LANCZOS)
# Create clean transparent circular logo
circular_logo = raw_logo.copy()
# Crop to square bounding box of circle
bbox = (int(cx - r), int(cy - r), int(cx + r), int(cy + r))
circular_logo = circular_logo.crop(bbox)
mask_crop = mask.crop(bbox)
circular_logo.putalpha(mask_crop)
# Master square icon on transparent background
master_size = 1024
master_logo = circular_logo.resize((master_size, master_size), Image.Resampling.LANCZOS)
# Save Master Logo PNGs
master_logo.save(LOGO_DIR / "logo_master.png", "PNG")
for sz in [1024, 512, 256, 128, 64, 32]:
resized = circular_logo.resize((sz, sz), Image.Resampling.LANCZOS)
resized.save(LOGO_DIR / f"logo_{sz}.png", "PNG")
# App Icons (PNGs)
for sz in [1024, 512, 256, 128, 64, 48, 32, 24, 16]:
resized = circular_logo.resize((sz, sz), Image.Resampling.LANCZOS)
resized.save(APP_DIR / f"app_icon_{sz}.png", "PNG")
# Windows Multi-Resolution ICO
ico_sizes = [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)]
ico_images = [circular_logo.resize(s, Image.Resampling.LANCZOS) for s in ico_sizes]
ico_images[0].save(
APP_DIR / "HermesHub.ico",
format="ICO",
sizes=ico_sizes,
append_images=ico_images[1:]
)
# Also copy to root and launcher dir for executable embedding
ico_images[0].save(REPO_ROOT / "launcher" / "HermesHub.ico", format="ICO", sizes=ico_sizes, append_images=ico_images[1:])
# Splash Screen Background & Logo Assets
# Dark-first background #0F1510
splash_w, splash_h = 600, 360
splash_bg = Image.new("RGBA", (splash_w, splash_h), (15, 21, 16, 255))
# Subtle dark green radial gradient or corner border
draw_s = ImageDraw.Draw(splash_bg)
draw_s.rectangle([(0, 0), (splash_w - 1, splash_h - 1)], outline=(47, 74, 54, 255), width=2)
# Add gold accent line at bottom
draw_s.line([(0, splash_h - 4), (splash_w, splash_h - 4)], fill=(205, 170, 100, 255), width=3)
splash_bg.save(SPLASH_DIR / "splash_bg.png", "PNG")
splash_logo = circular_logo.resize((160, 160), Image.Resampling.LANCZOS)
splash_logo.save(SPLASH_DIR / "splash_logo.png", "PNG")
# Installer Banner & Assets
inst_w, inst_h = 500, 120
inst_banner = Image.new("RGBA", (inst_w, inst_h), (26, 42, 31, 255))
draw_ib = ImageDraw.Draw(inst_banner)
draw_ib.rectangle([(0, 0), (inst_w - 1, inst_h - 1)], outline=(47, 74, 54, 255), width=1)
draw_ib.line([(0, inst_h - 3), (inst_w, inst_h - 3)], fill=(205, 170, 100, 255), width=2)
# Paste small logo on banner
banner_logo = circular_logo.resize((80, 80), Image.Resampling.LANCZOS)
inst_banner.paste(banner_logo, (20, 20), banner_logo)
inst_banner.save(INSTALLER_DIR / "installer_banner.png", "PNG")
ico_images[0].save(INSTALLER_DIR / "installer_icon.ico", format="ICO", sizes=ico_sizes, append_images=ico_images[1:])
print("Successfully generated all branding assets!")
print("App ICO created at:", APP_DIR / "HermesHub.ico")