1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
#!/usr/bin/env python3
"""Draw the app's launcher icon: a white padlock with three "code" dots on a
teal disc, on a transparent background.
The icon is drawn at 8x the target size and scaled down, which gives smooth
edges at the small sizes watches use (35 to 70 px).
Usage: make_icon.py --size 60 --out resources/drawables/launcher_icon.png
"""
from __future__ import annotations
import argparse
from pathlib import Path
from PIL import Image, ImageDraw
TEAL = (14, 124, 134, 255)
WHITE = (255, 255, 255, 255)
SUPERSAMPLE = 8
def draw_icon(size: int) -> Image.Image:
"""Return the icon as an RGBA image of size x size pixels."""
s = size * SUPERSAMPLE
img = Image.new("RGBA", (s, s), (0, 0, 0, 0))
d = ImageDraw.Draw(img)
margin = s * 0.02
d.ellipse((margin, margin, s - margin, s - margin), fill=TEAL)
# Lock body: a rounded rectangle in the lower middle.
body_w, body_h = s * 0.50, s * 0.36
body_x0, body_y0 = (s - body_w) / 2, s * 0.44
d.rounded_rectangle((body_x0, body_y0, body_x0 + body_w, body_y0 + body_h), radius=s * 0.06, fill=WHITE)
# Shackle: the top half of an ellipse plus two short legs into the body.
stroke = int(s * 0.07)
sx0, sx1, sy0, sy1 = s * 0.34, s * 0.66, s * 0.20, s * 0.56
d.arc((sx0, sy0, sx1, sy1), start=180, end=360, fill=WHITE, width=stroke)
leg_top, leg_bottom = (sy0 + sy1) / 2, body_y0 + s * 0.02
d.rectangle((sx0, leg_top, sx0 + stroke, leg_bottom), fill=WHITE)
d.rectangle((sx1 - stroke, leg_top, sx1, leg_bottom), fill=WHITE)
# Three dots on the body stand for a one-time code.
radius = s * 0.035
center_y = body_y0 + body_h * 0.5
for i in range(3):
center_x = s * (0.38 + 0.12 * i)
d.ellipse((center_x - radius, center_y - radius, center_x + radius, center_y + radius), fill=TEAL)
return img.resize((size, size), Image.Resampling.LANCZOS)
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("--size", type=int, default=60, help="icon width and height in pixels")
parser.add_argument("--out", type=Path, required=True, help="PNG file to write")
args = parser.parse_args()
if not 16 <= args.size <= 256:
parser.error("--size must be between 16 and 256")
args.out.parent.mkdir(parents=True, exist_ok=True)
draw_icon(args.size).save(args.out, format="PNG", optimize=True)
print(f"wrote {args.out} ({args.size}x{args.size})")
if __name__ == "__main__":
main()
|