Spaces:
Running
Running
File size: 2,886 Bytes
b56c709 | 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 69 | // Draws the app icons (a waveform on terracotta) as PNGs with no dependencies.
import { deflateSync } from 'node:zlib';
import { writeFileSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';
const crcTable = Array.from({ length: 256 }, (_, n) => {
let c = n;
for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
return c >>> 0;
});
const crc = (buf) => { let c = ~0; for (const b of buf) c = crcTable[(c ^ b) & 255] ^ (c >>> 8); return ~c >>> 0; };
const chunk = (type, data) => {
const len = Buffer.alloc(4); len.writeUInt32BE(data.length);
const td = Buffer.concat([Buffer.from(type), data]);
const c = Buffer.alloc(4); c.writeUInt32BE(crc(td));
return Buffer.concat([len, td, c]);
};
function draw(size, maskable) {
const ss = 3, S = size * ss; // supersample for smooth edges
const BG = [180, 83, 42], FG = [244, 239, 230];
const heights = [0.18, 0.36, 0.56, 0.36, 0.24, 0.46, 0.2];
const scale = maskable ? 0.62 : 0.8;
const n = heights.length, w = (S * scale) / (n * 1.7);
const total = n * w + (n - 1) * w * 0.7;
const x0 = (S - total) / 2, R = S * 0.22;
const inRound = (x, y, x1, y1, x2, y2, r) => {
if (x < x1 || x > x2 || y < y1 || y > y2) return false;
const cx = Math.min(Math.max(x, x1 + r), x2 - r), cy = Math.min(Math.max(y, y1 + r), y2 - r);
return (x - cx) ** 2 + (y - cy) ** 2 <= r * r;
};
const px = (x, y) => {
if (!maskable && !inRound(x, y, 0, 0, S, S, R)) return null;
for (let i = 0; i < n; i++) {
const bx = x0 + i * w * 1.7, hh = S * heights[i] * scale;
if (inRound(x, y, bx, S / 2 - hh / 2, bx + w, S / 2 + hh / 2, w / 2)) return FG;
}
return BG;
};
const raw = Buffer.alloc(size * (size * 4 + 1));
for (let y = 0; y < size; y++) {
raw[y * (size * 4 + 1)] = 0;
for (let x = 0; x < size; x++) {
let r = 0, g = 0, b = 0, a = 0;
for (let sy = 0; sy < ss; sy++) for (let sx = 0; sx < ss; sx++) {
const c = px(x * ss + sx + 0.5, y * ss + sy + 0.5);
if (c) { r += c[0]; g += c[1]; b += c[2]; a++; }
}
const o = y * (size * 4 + 1) + 1 + x * 4;
if (a) { raw[o] = r / a; raw[o + 1] = g / a; raw[o + 2] = b / a; }
raw[o + 3] = Math.round((a / (ss * ss)) * 255);
}
}
const ihdr = Buffer.alloc(13);
ihdr.writeUInt32BE(size, 0); ihdr.writeUInt32BE(size, 4);
ihdr[8] = 8; ihdr[9] = 6; ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0;
return Buffer.concat([
Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]),
chunk('IHDR', ihdr), chunk('IDAT', deflateSync(raw, { level: 9 })), chunk('IEND', Buffer.alloc(0)),
]);
}
export function writeIcons(dir) {
mkdirSync(dir, { recursive: true });
writeFileSync(join(dir, 'icon-192.png'), draw(192, false));
writeFileSync(join(dir, 'icon-512.png'), draw(512, false));
writeFileSync(join(dir, 'maskable-512.png'), draw(512, true));
}
|