c8442c5Command-line renderer (Node WebGPU via Dawn) and gallery generatorJeremy Magland 1// Minimal PNG encoder (RGBA, 8-bit) using Node's zlib.
3import { deflateSync } from "node:zlib";
5function crc32(buf: Uint8Array): number {
6 let c = ~0;
7 for (let k = 0; k < buf.length; k++) {
8 c ^= buf[k];
9 for (let b = 0; b < 8; b++) c = (c >>> 1) ^ (0xedb88320 & -(c & 1));
10 }
11 return ~c >>> 0;
12}
14function chunk(type: string, data: Uint8Array): Uint8Array {
15 const out = new Uint8Array(12 + data.length);
16 const v = new DataView(out.buffer);
17 v.setUint32(0, data.length);
18 for (let k = 0; k < 4; k++) out[4 + k] = type.charCodeAt(k);
19 out.set(data, 8);
20 v.setUint32(8 + data.length, crc32(out.subarray(4, 8 + data.length)));
21 return out;
22}
24export function encodePng(rgba: Uint8ClampedArray, width: number, height: number): Uint8Array {
25 const raw = new Uint8Array((width * 4 + 1) * height);
26 for (let y = 0; y < height; y++) {
27 raw[y * (width * 4 + 1)] = 0;
28 raw.set(rgba.subarray(y * width * 4, (y + 1) * width * 4), y * (width * 4 + 1) + 1);
29 }
30 const ihdr = new Uint8Array(13);
31 const v = new DataView(ihdr.buffer);
32 v.setUint32(0, width); v.setUint32(4, height);
33 ihdr[8] = 8; ihdr[9] = 6; ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0;
34 const parts = [new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]), chunk("IHDR", ihdr), chunk("IDAT", new Uint8Array(deflateSync(raw))), chunk("IEND", new Uint8Array(0))];
35 const total = parts.reduce((n, p) => n + p.length, 0);
36 const out = new Uint8Array(total);
37 let o = 0;
38 for (const p of parts) { out.set(p, o); o += p.length; }
39 return out;
40}