12<!-- Launcher icon foreground (?px=size): the W of the desktop icon (build/appicon.png),3 lifted off its white tile onto a transparent 108dp adaptive-icon layer; the tile4 is the layer below (ic_launcher.xml). The W stays inside the 66dp safe zone.5 It's the monochrome (themed) layer too. -->6<meta charset="utf-8">7<style>8 html, body { margin: 0; background: transparent; }9 canvas { display: block; }10</style>11<canvas id="art"></canvas>12<script type="module">13 const px = Number(new URLSearchParams(location.search).get("px") || 432);14 const img = new Image();15 img.src = "../../build/appicon.png";16 await img.decode();1718 // The glyph: dark ink on the white tile. Its alpha is how dark each pixel is.19 const src = document.createElement("canvas");20 src.width = img.width; src.height = img.height;21 const sctx = src.getContext("2d");22 sctx.drawImage(img, 0, 0);23 const d = sctx.getImageData(0, 0, src.width, src.height);24 const INK = 0x21;25 let x0 = src.width, y0 = src.height, x1 = 0, y1 = 0;26 for (let y = 0; y < src.height; y++) {27 for (let x = 0; x < src.width; x++) {28 const i = (y * src.width + x) * 4;29 const lum = Math.min(d.data[i], d.data[i + 1], d.data[i + 2]);30 // Only the tile's opaque inside: its edge and drop shadow are grey too.31 const a = d.data[i + 3] === 255 ? Math.max(0, Math.min(1, (255 - lum) / (255 - INK))) : 0;32 d.data[i] = d.data[i + 1] = d.data[i + 2] = INK;33 d.data[i + 3] = Math.round(a * 255);34 if (a > 0.5) { x0 = Math.min(x0, x); x1 = Math.max(x1, x); y0 = Math.min(y0, y); y1 = Math.max(y1, y); }35 }36 }37 sctx.putImageData(d, 0, 0);3839 // Shadows and the tile's rounded edge sit far from the glyph: keep the glyph's box only.40 const pad = 4;41 const gw = x1 - x0 + 1 + pad * 2, gh = y1 - y0 + 1 + pad * 2;42 const h = px * 0.40, w = h * gw / gh;43 const c = document.getElementById("art"), ctx = c.getContext("2d");44 c.width = c.height = px;45 ctx.imageSmoothingQuality = "high";46 ctx.drawImage(src, x0 - pad, y0 - pad, gw, gh, (px - w) / 2, (px - h) / 2, w, h);47 document.body.dataset.ready = "1";48</script>