Web: PWA-Icons neu — Umschlag-Logo auf blauem Verlauf
Ersetzt die dunkle Kachel durch den weißen Umschlag auf blauem Diagonal-Verlauf (icon-192/512.png). Reproduzierbar via web/_gen_icons.py (Pillow, Supersampling), Logo mit Safe-Zone-Padding -> taugt auch als maskable Icon. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
59
web/_gen_icons.py
Normal file
59
web/_gen_icons.py
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generiert die PWA-Icons (icon-192/512.png): weißes Umschlag-Logo auf blauem
|
||||
Verlauf. Reproduzierbar — bei Logo-Änderungen einfach neu laufen lassen.
|
||||
|
||||
python3 web/_gen_icons.py
|
||||
"""
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
SS = 4 # Supersampling-Faktor für weiche Kanten
|
||||
GRAD_TL = (91, 155, 255) # #5b9bff oben links
|
||||
GRAD_BR = (26, 63, 176) # #1a3fb0 unten rechts
|
||||
INK = (255, 255, 255, 255) # Umschlag in Weiß
|
||||
|
||||
# Logo im 48er-Koordinatensystem (identisch zu favicon.svg / Homepage-SVG)
|
||||
VB = 48.0
|
||||
LOGO_CX, LOGO_CY = 24.0, 24.75 # Mittelpunkt der Logo-Bounding-Box
|
||||
LOGO_W = 34.0 # Breite der Bounding-Box
|
||||
FILL_FRAC = 0.60 # Logo füllt 60 % der Icon-Breite (Maskable-safe)
|
||||
|
||||
|
||||
def diagonal_gradient(size):
|
||||
g = Image.new("RGB", (64, 64))
|
||||
px = g.load()
|
||||
for y in range(64):
|
||||
for x in range(64):
|
||||
t = (x + y) / 126.0
|
||||
px[x, y] = tuple(round(a + (b - a) * t) for a, b in zip(GRAD_TL, GRAD_BR))
|
||||
return g.resize((size, size), Image.BILINEAR)
|
||||
|
||||
|
||||
def render(size):
|
||||
W = size * SS
|
||||
img = diagonal_gradient(W).convert("RGBA")
|
||||
d = ImageDraw.Draw(img)
|
||||
|
||||
scale = (FILL_FRAC * W) / LOGO_W # px pro viewBox-Einheit
|
||||
cx = cy = W / 2.0
|
||||
|
||||
def T(vx, vy): # viewBox -> Pixel (zentriert)
|
||||
return (cx + (vx - LOGO_CX) * scale, cy + (vy - LOGO_CY) * scale)
|
||||
|
||||
sw = max(1, round(3.0 * scale)) # Strichstärke
|
||||
r = 4.5 * scale # Eckenradius Umschlag
|
||||
|
||||
# Umschlag-Körper (abgerundetes Rechteck)
|
||||
x0, y0 = T(7, 16); x1, y1 = T(41, 40)
|
||||
d.rounded_rectangle([x0, y0, x1, y1], radius=r, outline=INK, width=sw)
|
||||
# Klappe (V)
|
||||
d.line([T(8, 18.5), T(24, 31), T(40, 18.5)], fill=INK, width=sw, joint="curve")
|
||||
# Kapuze (oberer Ellipsenbogen)
|
||||
ax0, ay0 = T(17, 9.5); ax1, ay1 = T(31, 22.5)
|
||||
d.arc([ax0, ay0, ax1, ay1], start=180, end=360, fill=INK, width=sw)
|
||||
|
||||
return img.resize((size, size), Image.LANCZOS)
|
||||
|
||||
|
||||
for s in (192, 512):
|
||||
render(s).save(f"web/icon-{s}.png")
|
||||
print(f"web/icon-{s}.png geschrieben")
|
||||
BIN
web/icon-192.png
BIN
web/icon-192.png
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 8.5 KiB |
BIN
web/icon-512.png
BIN
web/icon-512.png
Binary file not shown.
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 21 KiB |
Reference in New Issue
Block a user