"""Draws MiniABS's PNG icons for ldpi (Galaxy Mini), mdpi and hdpi. The launcher icon (res/drawable-*/ic_launcher.png) comes from the supplied Audiobookshelf icon pack and is not generated here. The notification icon is cut out of that same artwork (art/icon_master_512.png): the white headphones and equalizer become a white silhouette, as Android 2.3's status bar expects. Android 2.x has no vector drawables, so the player icons are rendered at 4x and downsampled for smooth edges. """ import os import sys from PIL import Image, ImageDraw RES = sys.argv[1] ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..") DENS = {"ldpi": 0.75, "mdpi": 1.0, "hdpi": 1.5} LILAC = (188, 138, 252, 255) # the icon's sound-wave colour (#BC8AFC) WHITE = (255, 255, 255, 255) SS = 4 # supersampling def canvas(px): s = px * SS return Image.new("RGBA", (s, s), (0, 0, 0, 0)), s def save(img, px, folder, name): out = img.resize((px, px), Image.LANCZOS) d = os.path.join(RES, "drawable-" + folder) os.makedirs(d, exist_ok=True) out.save(os.path.join(d, name + ".png")) def status_silhouette(): """White mask of the icon's light parts (headphones, bars, waves) on transparency.""" src = Image.open(os.path.join(ROOT, "art", "icon_master_512.png")).convert("RGBA") out = Image.new("RGBA", src.size, (0, 0, 0, 0)) sp, op = src.load(), out.load() for y in range(src.size[1]): for x in range(src.size[0]): r, g, b, a = sp[x, y] # the purple background has a low green channel; the white/lilac artwork a high one level = min(255, max(0, (g - 80) * 255 // 50)) op[x, y] = (255, 255, 255, level * a // 255) box = out.getbbox() out = out.crop(box) side = max(out.size) sq = Image.new("RGBA", (side, side), (0, 0, 0, 0)) sq.paste(out, ((side - out.size[0]) // 2, (side - out.size[1]) // 2)) return sq def status(sil, px, folder): pad = max(1, round(px * 0.06)) inner = sil.resize((px - 2 * pad, px - 2 * pad), Image.LANCZOS) img = Image.new("RGBA", (px, px), (0, 0, 0, 0)) img.paste(inner, (pad, pad)) d = os.path.join(RES, "drawable-" + folder) img.save(os.path.join(d, "ic_stat.png")) def play(px, folder, name, color): img, s = canvas(px) d = ImageDraw.Draw(img) d.polygon([(s * 0.32, s * 0.2), (s * 0.32, s * 0.8), (s * 0.82, s * 0.5)], fill=color) save(img, px, folder, name) def pause(px, folder, name, color): img, s = canvas(px) d = ImageDraw.Draw(img) d.rectangle([s * 0.26, s * 0.2, s * 0.43, s * 0.8], fill=color) d.rectangle([s * 0.57, s * 0.2, s * 0.74, s * 0.8], fill=color) save(img, px, folder, name) def refresh(px, folder): img, s = canvas(px) d = ImageDraw.Draw(img) w = int(s * 0.1) box = [s * 0.2, s * 0.2, s * 0.8, s * 0.8] d.arc(box, 30, 320, fill=WHITE, width=w) # arrow head at the arc's end (top right) d.polygon([(s * 0.86, s * 0.14), (s * 0.88, s * 0.48), (s * 0.56, s * 0.36)], fill=WHITE) save(img, px, folder, "ic_refresh") sil = status_silhouette() for folder, k in DENS.items(): status(sil, round(25 * k), folder) play(round(32 * k), folder, "ic_play", WHITE) # on the purple play button pause(round(32 * k), folder, "ic_pause", WHITE) play(round(38 * k), folder, "ic_play_small", LILAC) # mini player bar, on dark pause(round(38 * k), folder, "ic_pause_small", LILAC) refresh(round(30 * k), folder) print("icons ok")