"""The app icons: the asterisk, white with square ends, on the ground colour. Same geometry as icons/icon.svg. python3 tools/icons.py -> icons/icon-512.png, icons/icon-192.png, icons/apple-touch-icon.png Standard library only: three rotated rectangles, 3x3 supersampled, written as RGB PNG. """ import math import struct import zlib from pathlib import Path ROOT = Path(__file__).resolve().parent.parent GROUND, INK = (11, 11, 14), (237, 240, 240) SS = 3 def arms(size): """The three arms as (centre, unit direction, half-length, half-width) in pixel units.""" c = size / 2 half, hw = 150 / 512 * size, 29 / 512 * size return [((c, c), (math.cos(math.radians(90 + 60 * k)), math.sin(math.radians(90 + 60 * k))), half, hw) for k in range(3)] def inside(x, y, shapes): for (cx, cy), (dx, dy), half, hw in shapes: px, py = x - cx, y - cy along, across = px * dx + py * dy, -px * dy + py * dx if abs(along) <= half and abs(across) <= hw: return True return False def asterisk(size): shapes = arms(size) rows = [] n = SS * SS for y in range(size): row = bytearray([0]) # PNG filter type 0 for x in range(size): hit = 0 for sy in range(SS): for sx in range(SS): if inside(x + (sx + 0.5) / SS, y + (sy + 0.5) / SS, shapes): hit += 1 t = hit / n row.extend(int(round(g + (i - g) * t)) for g, i in zip(GROUND, INK)) rows.append(bytes(row)) return b"".join(rows) def png(size, raw): def chunk(kind, data): return struct.pack(">I", len(data)) + kind + data + struct.pack(">I", zlib.crc32(kind + data) & 0xFFFFFFFF) ihdr = struct.pack(">IIBBBBB", size, size, 8, 2, 0, 0, 0) return b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", ihdr) + chunk(b"IDAT", zlib.compress(raw, 9)) + chunk(b"IEND", b"") if __name__ == "__main__": out = ROOT / "icons" out.mkdir(exist_ok=True) for name, size in (("icon-512.png", 512), ("icon-192.png", 192), ("apple-touch-icon.png", 180)): (out / name).write_bytes(png(size, asterisk(size))) print("built: icons/" + name)