117 lines
4.8 KiB
Python
117 lines
4.8 KiB
Python
import os, random, sys
|
|
from PIL import Image, ImageChops, ImageDraw, ImageFilter, ImageFont
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
ROOT = os.path.abspath(os.path.join(HERE, "..", ".."))
|
|
NAVY = (0x1B, 0x2A, 0x49); DEEP = (0x10, 0x1B, 0x33); GOLD = (0xD4, 0xA6, 0x4A)
|
|
FONT = os.path.join(ROOT, "assets", "fonts", "Inter-Bold.otf")
|
|
COIN = Image.open(os.path.join(HERE, "logo_coin_master.png")).convert("RGBA")
|
|
|
|
|
|
def gradient(size, c1, c2):
|
|
w, h = size
|
|
g = Image.new("RGB", (w, h))
|
|
px = g.load()
|
|
for y in range(h):
|
|
for x in range(w):
|
|
t = (x / w + y / h) / 2
|
|
px[x, y] = tuple(round(c1[i] + (c2[i] - c1[i]) * t) for i in range(3))
|
|
return g
|
|
|
|
|
|
def background(S, style):
|
|
bg = gradient((S, S), NAVY, DEEP).convert("RGBA")
|
|
ov = Image.new("RGBA", (S, S), (0, 0, 0, 0))
|
|
d = ImageDraw.Draw(ov)
|
|
if style == "circles":
|
|
# Large soft discs and one thin gold ring, partly off the tile.
|
|
d.ellipse([S * 0.46, -S * 0.42, S * 1.34, S * 0.46], fill=(255, 255, 255, 20))
|
|
d.ellipse([-S * 0.42, S * 0.52, S * 0.42, S * 1.36], fill=(255, 255, 255, 14))
|
|
d.ellipse([S * 0.58, S * 0.60, S * 1.18, S * 1.20], fill=(212, 166, 74, 22))
|
|
d.ellipse([S * 0.04, S * 0.04, S * 0.96, S * 0.96],
|
|
outline=(212, 166, 74, 46), width=max(1, round(S * 0.006)))
|
|
elif style == "endings":
|
|
rnd = random.Random(74)
|
|
f = ImageFont.truetype(FONT, round(S * 0.115))
|
|
cw, ch = S * 0.36, S * 0.19
|
|
gold = {(1, 1), (3, 3), (0, 4), (2, 5)}
|
|
layer = Image.new("RGBA", (S * 2, S * 2), (0, 0, 0, 0))
|
|
ld = ImageDraw.Draw(layer)
|
|
for r in range(int(S * 2 / ch) + 1):
|
|
for c in range(int(S * 2 / cw) + 1):
|
|
t = "%04d" % rnd.randint(0, 9999)
|
|
x = c * cw + (cw / 2 if r % 2 else 0) - S * 0.2
|
|
y = r * ch - S * 0.1
|
|
fill = GOLD + (150,) if (c, r) in gold else (255, 255, 255, 26)
|
|
ld.text((x, y), t, font=f, fill=fill)
|
|
layer = layer.rotate(-12, resample=Image.BICUBIC)
|
|
o = S // 2
|
|
layer = layer.crop((o, o, o + S, o + S))
|
|
ov = layer
|
|
return Image.alpha_composite(bg, ov)
|
|
|
|
|
|
def icon(px, style, ratio, shape, ss=4):
|
|
S = px * ss
|
|
bg = background(S, style)
|
|
cx = cy = S / 2
|
|
R = S * ratio / 2
|
|
if style == "endings":
|
|
# Calm the middle so the coin reads cleanly.
|
|
m = Image.new("L", (S, S), 255)
|
|
ImageDraw.Draw(m).ellipse([cx - R * 1.55, cy - R * 1.55, cx + R * 1.55, cy + R * 1.55], fill=70)
|
|
m = m.filter(ImageFilter.GaussianBlur(S * 0.07))
|
|
veil = Image.new("RGBA", (S, S), NAVY + (255,))
|
|
veil.putalpha(Image.eval(m, lambda v: 255 - v))
|
|
bg = Image.alpha_composite(bg, veil)
|
|
glow = Image.new("RGBA", (S, S), (0, 0, 0, 0))
|
|
ImageDraw.Draw(glow).ellipse([cx - R, cy - R + S * 0.02, cx + R, cy + R + S * 0.02], fill=GOLD + (130,))
|
|
glow = glow.filter(ImageFilter.GaussianBlur(S * 0.035))
|
|
bg = Image.alpha_composite(bg, glow)
|
|
coin = COIN.resize((round(R * 2), round(R * 2)), Image.LANCZOS)
|
|
bg.alpha_composite(coin, (round(cx - R), round(cy - R)))
|
|
if shape == "rounded":
|
|
mask = Image.new("L", (S, S), 0)
|
|
ImageDraw.Draw(mask).rounded_rectangle([0, 0, S - 1, S - 1], radius=S * 0.22, fill=255)
|
|
bg.putalpha(mask)
|
|
return bg.resize((px, px), Image.LANCZOS)
|
|
|
|
|
|
def write_icons(style):
|
|
web = os.path.join(ROOT, "web")
|
|
jobs = [
|
|
("icons/Icon-512.png", 512, 0.62, "rounded"),
|
|
("icons/Icon-192.png", 192, 0.62, "rounded"),
|
|
("icons/Icon-maskable-512.png", 512, 0.52, "full"),
|
|
("icons/Icon-maskable-192.png", 192, 0.52, "full"),
|
|
("icons/apple-touch-icon.png", 180, 0.62, "full"),
|
|
("favicon.png", 64, 0.78, "rounded"),
|
|
]
|
|
for name, px, ratio, shape in jobs:
|
|
icon(px, style, ratio, shape).save(os.path.join(web, name), optimize=True)
|
|
print(name, px)
|
|
|
|
|
|
def write_preview():
|
|
out = os.path.join(HERE, "preview")
|
|
os.makedirs(out, exist_ok=True)
|
|
for style in ("endings", "circles"):
|
|
sheet = Image.new("RGB", (512 + 40 + 192 + 40 + 64 + 40 + 32, 512 + 40), (244, 242, 236))
|
|
big = icon(512, style, 0.62, "rounded")
|
|
sheet.paste(big, (20, 20), big)
|
|
mid = icon(192, style, 0.62, "rounded")
|
|
sheet.paste(mid, (512 + 40, 20), mid)
|
|
small = icon(64, style, 0.78, "rounded")
|
|
sheet.paste(small, (512 + 40 + 192 + 20, 20), small)
|
|
tiny = icon(32, style, 0.78, "rounded")
|
|
sheet.paste(tiny, (512 + 40 + 192 + 20 + 64 + 20, 20), tiny)
|
|
sheet.save(os.path.join(out, f"icon_{style}.png"))
|
|
print("preview", style)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) > 1 and sys.argv[1] == "preview":
|
|
write_preview()
|
|
else:
|
|
write_icons(sys.argv[1] if len(sys.argv) > 1 else "circles")
|