Files
heicode-mananger/scripts/rebuild-icons.py

96 lines
3.8 KiB
Python

"""Regenerate Tauri/desktop icons from heicode-logo.jpg."""
from pathlib import Path
from PIL import Image
ROOT = Path(__file__).resolve().parent.parent
SRC = ROOT / "heicode-logo.jpg"
TAURI_ICONS = ROOT / "cc-haha" / "desktop" / "src-tauri" / "icons"
PUBLIC = ROOT / "cc-haha" / "desktop" / "public"
def load_trimmed_square():
img = Image.open(SRC).convert("RGB")
# Find bbox of non-white pixels (treat >=250 RGB as white).
gray = img.convert("L")
mask = gray.point(lambda v: 0 if v >= 250 else 255)
bbox = mask.getbbox()
if bbox:
img = img.crop(bbox)
# Pad to square on white.
w, h = img.size
side = max(w, h)
canvas = Image.new("RGB", (side, side), (255, 255, 255))
canvas.paste(img, ((side - w) // 2, (side - h) // 2))
return canvas
def to_rgba_with_transparent_white(img: Image.Image, threshold: int = 248) -> Image.Image:
rgba = img.convert("RGBA")
pixels = rgba.load()
w, h = rgba.size
for y in range(h):
for x in range(w):
r, g, b, _ = pixels[x, y]
if r >= threshold and g >= threshold and b >= threshold:
pixels[x, y] = (r, g, b, 0)
return rgba
def main():
base = load_trimmed_square()
print(f"Trimmed source: {base.size}")
# PNG outputs (RGB on white → looks correct on any background, matches glass edges).
sizes = {
TAURI_ICONS / "32x32.png": 32,
TAURI_ICONS / "64x64.png": 64,
TAURI_ICONS / "128x128.png": 128,
TAURI_ICONS / "128x128@2x.png": 256,
TAURI_ICONS / "icon.png": 1024,
PUBLIC / "app-icon.png": 1024,
}
for path, size in sizes.items():
path.parent.mkdir(parents=True, exist_ok=True)
# Tauri's macOS bundler rejects non-RGBA PNGs ("icon ... is not RGBA").
# Force RGBA on every PNG output so the same files work for both
# Windows .ico embedding and macOS .icns embedding without per-platform
# branches. Alpha channel is fully opaque (255) since we paint on
# solid white in load_trimmed_square — the rounded glass edges of the
# logo retain their painted look on any background.
base.resize((size, size), Image.LANCZOS).convert("RGBA").save(path, "PNG")
print(f"wrote {path} ({size})")
# Windows Square*Logo / StoreLogo
win_sizes = {
"Square30x30Logo.png": 30,
"Square44x44Logo.png": 44,
"Square71x71Logo.png": 71,
"Square89x89Logo.png": 89,
"Square107x107Logo.png": 107,
"Square142x142Logo.png": 142,
"Square150x150Logo.png": 150,
"Square284x284Logo.png": 284,
"Square310x310Logo.png": 310,
"StoreLogo.png": 50,
}
for name, size in win_sizes.items():
path = TAURI_ICONS / name
# Tauri's macOS bundler rejects non-RGBA PNGs ("icon ... is not RGBA").
# Force RGBA on every PNG output so the same files work for both
# Windows .ico embedding and macOS .icns embedding without per-platform
# branches. Alpha channel is fully opaque (255) since we paint on
# solid white in load_trimmed_square — the rounded glass edges of the
# logo retain their painted look on any background.
base.resize((size, size), Image.LANCZOS).convert("RGBA").save(path, "PNG")
print(f"wrote {path} ({size})")
# ICO with multi-resolution.
ico_path = TAURI_ICONS / "icon.ico"
base.save(ico_path, format="ICO", sizes=[(16,16),(24,24),(32,32),(48,48),(64,64),(128,128),(256,256)])
print(f"wrote {ico_path}")
# ICNS (PIL supports it). Required sizes: 16,32,64,128,256,512,1024.
icns_path = TAURI_ICONS / "icon.icns"
base.save(icns_path, format="ICNS", sizes=[(16,16),(32,32),(64,64),(128,128),(256,256),(512,512),(1024,1024)])
print(f"wrote {icns_path}")
if __name__ == "__main__":
main()