release nouri 0.6.0 polish backup and pwa
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
BRAND_DIR = ROOT / "nouri" / "static" / "brand"
|
||||
|
||||
BG_TOP = "#F6C394"
|
||||
BG_BOTTOM = "#DE9862"
|
||||
BOWL = "#FFF7EF"
|
||||
STROKE = "#8C533B"
|
||||
|
||||
|
||||
def rounded_gradient_icon(size: int, *, maskable: bool = False) -> Image.Image:
|
||||
image = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(image)
|
||||
radius = int(size * (0.28 if maskable else 0.24))
|
||||
|
||||
for y in range(size):
|
||||
blend = y / max(size - 1, 1)
|
||||
r1, g1, b1 = ImageColorTuple(BG_TOP)
|
||||
r2, g2, b2 = ImageColorTuple(BG_BOTTOM)
|
||||
color = (
|
||||
int(r1 + (r2 - r1) * blend),
|
||||
int(g1 + (g2 - g1) * blend),
|
||||
int(b1 + (b2 - b1) * blend),
|
||||
255,
|
||||
)
|
||||
draw.line((0, y, size, y), fill=color)
|
||||
|
||||
mask = Image.new("L", (size, size), 0)
|
||||
ImageDraw.Draw(mask).rounded_rectangle((0, 0, size - 1, size - 1), radius=radius, fill=255)
|
||||
image.putalpha(mask)
|
||||
|
||||
inset = int(size * (0.11 if maskable else 0.13))
|
||||
inner = [inset, inset, size - inset, size - inset]
|
||||
draw.rounded_rectangle(inner, radius=int(radius * 0.84), outline=(255, 255, 255, 54), width=max(2, size // 80))
|
||||
|
||||
bowl_top = int(size * 0.24)
|
||||
bowl_left = int(size * 0.27)
|
||||
bowl_right = int(size * 0.73)
|
||||
bowl_bottom = int(size * 0.68)
|
||||
draw.rounded_rectangle(
|
||||
(bowl_left, bowl_top, bowl_right, bowl_bottom),
|
||||
radius=int(size * 0.16),
|
||||
fill=BOWL,
|
||||
)
|
||||
draw.rounded_rectangle(
|
||||
(int(size * 0.31), int(size * 0.31), int(size * 0.69), int(size * 0.60)),
|
||||
radius=int(size * 0.12),
|
||||
fill=(239, 195, 159, 64),
|
||||
)
|
||||
|
||||
bowl_curve_top = int(size * 0.56)
|
||||
draw.pieslice(
|
||||
(int(size * 0.24), bowl_curve_top, int(size * 0.76), int(size * 0.84)),
|
||||
start=0,
|
||||
end=180,
|
||||
fill=(247, 179, 125, 255),
|
||||
)
|
||||
|
||||
line_width = max(4, size // 26)
|
||||
steam = [
|
||||
(0.50, 0.31),
|
||||
(0.56, 0.31),
|
||||
(0.56, 0.40),
|
||||
(0.45, 0.50),
|
||||
(0.45, 0.58),
|
||||
]
|
||||
draw.line([(int(size * x), int(size * y)) for x, y in steam], fill=STROKE, width=line_width, joint="curve")
|
||||
draw.line(
|
||||
[(int(size * 0.45), int(size * 0.58)), (int(size * 0.56), int(size * 0.58))],
|
||||
fill=STROKE,
|
||||
width=line_width,
|
||||
)
|
||||
return image
|
||||
|
||||
|
||||
def ImageColorTuple(hex_color: str) -> tuple[int, int, int]:
|
||||
hex_color = hex_color.lstrip("#")
|
||||
return tuple(int(hex_color[index:index + 2], 16) for index in (0, 2, 4))
|
||||
|
||||
|
||||
def badge_icon(size: int) -> Image.Image:
|
||||
image = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(image)
|
||||
draw.rounded_rectangle((0, 0, size - 1, size - 1), radius=size // 3, fill=BG_BOTTOM)
|
||||
draw.ellipse((size * 0.18, size * 0.18, size * 0.82, size * 0.82), fill=BOWL)
|
||||
draw.rectangle((size * 0.34, size * 0.52, size * 0.66, size * 0.63), fill=STROKE)
|
||||
return image
|
||||
|
||||
|
||||
def save_assets() -> None:
|
||||
BRAND_DIR.mkdir(parents=True, exist_ok=True)
|
||||
rounded_gradient_icon(180).save(BRAND_DIR / "pwa-180.png")
|
||||
rounded_gradient_icon(192).save(BRAND_DIR / "pwa-192.png")
|
||||
rounded_gradient_icon(512).save(BRAND_DIR / "pwa-512.png")
|
||||
rounded_gradient_icon(512, maskable=True).save(BRAND_DIR / "pwa-maskable-512.png")
|
||||
badge_icon(96).save(BRAND_DIR / "pwa-badge.png")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
save_assets()
|
||||
Reference in New Issue
Block a user