"""Render the Dashboard Studio app icon: a dashboard with one lamp-lit tile.""" import sys from PIL import Image, ImageDraw, ImageFilter S = 1024 # draw large, downscale for anti-aliasing def vgradient(w, h, top, bottom): g = Image.new("RGBA", (w, h)) px = g.load() for y in range(h): t = y / (h - 1) c = tuple(int(top[i] + (bottom[i] - top[i]) * t) for i in range(3)) + (255,) for x in range(w): px[x, y] = c return g def rounded_mask(size, box, r): m = Image.new("L", size, 0) ImageDraw.Draw(m).rounded_rectangle(box, r, fill=255) return m img = Image.new("RGBA", (S, S), (0, 0, 0, 0)) # background squircle bg = vgradient(S, S, (34, 44, 78), (14, 17, 30)) img.paste(bg, (0, 0), rounded_mask((S, S), (0, 0, S - 1, S - 1), 230)) pad, gap = 150, 56 left = (pad, pad, 492, S - pad) # tall lit tile rt = (492 + gap, pad, S - pad, 492 - gap // 2) # graph tile rb = (492 + gap, 492 + gap // 2, S - pad, S - pad) # switch tile # warm glow behind the lit tile glow = Image.new("RGBA", (S, S), (0, 0, 0, 0)) ImageDraw.Draw(glow).rounded_rectangle(left, 64, fill=(255, 196, 92, 170)) img = Image.alpha_composite(img, glow.filter(ImageFilter.GaussianBlur(60))) # lit tile with a warm gradient lit = vgradient(left[2] - left[0], left[3] - left[1], (255, 236, 190), (255, 196, 96)) img.paste(lit, left[:2], rounded_mask(lit.size, (0, 0, lit.size[0] - 1, lit.size[1] - 1), 64)) # translucent shapes go on a layer that is blended in, not pasted over layer = Image.new("RGBA", (S, S), (0, 0, 0, 0)) ld = ImageDraw.Draw(layer) for box in (rt, rb): ld.rounded_rectangle(box, 64, fill=(255, 255, 255, 26), outline=(255, 255, 255, 40), width=4) cx = left[0] + 100 ld.ellipse((cx - 52, left[1] + 48, cx + 52, left[1] + 152), fill=(255, 255, 255, 140)) bar = (left[0] + 48, left[3] - 128, left[2] - 48, left[3] - 64) ld.rounded_rectangle(bar, 32, fill=(122, 82, 16, 50)) img = Image.alpha_composite(img, layer) d = ImageDraw.Draw(img) # lamp in the badge and the filled part of the brightness bar d.ellipse((cx - 20, left[1] + 80, cx + 20, left[1] + 120), fill=(122, 82, 16, 255)) d.rounded_rectangle((bar[0], bar[1], bar[0] + int((bar[2] - bar[0]) * 0.68), bar[3]), 32, fill=(140, 94, 20, 255)) # graph tile: a line in the accent blue pts = [(rt[0] + 44, rt[3] - 70), (rt[0] + 110, rt[3] - 120), (rt[0] + 170, rt[3] - 96), (rt[0] + 240, rt[3] - 170), (rt[2] - 44, rt[1] + 70)] d.line(pts, fill=(90, 170, 255, 255), width=22, joint="curve") for p in (pts[0], pts[-1]): d.ellipse((p[0] - 11, p[1] - 11, p[0] + 11, p[1] + 11), fill=(90, 170, 255, 255)) # switch tile: an "on" switch sw = (rb[0] + 50, (rb[1] + rb[3]) // 2 - 46, rb[2] - 50, (rb[1] + rb[3]) // 2 + 46) d.rounded_rectangle(sw, 46, fill=(48, 209, 88, 255)) d.ellipse((sw[2] - 86, sw[1] + 6, sw[2] - 6, sw[3] - 6), fill=(255, 255, 255, 255)) out = sys.argv[1] img.resize((256, 256), Image.LANCZOS).save(out) img.resize((128, 128), Image.LANCZOS).save(out.replace(".png", "-128.png")) print("saved", out)