// Minimal PNG encoder (RGBA, 8-bit) using Node's zlib. import { deflateSync } from "node:zlib"; function crc32(buf: Uint8Array): number { let c = ~0; for (let k = 0; k < buf.length; k++) { c ^= buf[k]; for (let b = 0; b < 8; b++) c = (c >>> 1) ^ (0xedb88320 & -(c & 1)); } return ~c >>> 0; } function chunk(type: string, data: Uint8Array): Uint8Array { const out = new Uint8Array(12 + data.length); const v = new DataView(out.buffer); v.setUint32(0, data.length); for (let k = 0; k < 4; k++) out[4 + k] = type.charCodeAt(k); out.set(data, 8); v.setUint32(8 + data.length, crc32(out.subarray(4, 8 + data.length))); return out; } export function encodePng(rgba: Uint8ClampedArray, width: number, height: number): Uint8Array { const raw = new Uint8Array((width * 4 + 1) * height); for (let y = 0; y < height; y++) { raw[y * (width * 4 + 1)] = 0; raw.set(rgba.subarray(y * width * 4, (y + 1) * width * 4), y * (width * 4 + 1) + 1); } const ihdr = new Uint8Array(13); const v = new DataView(ihdr.buffer); v.setUint32(0, width); v.setUint32(4, height); ihdr[8] = 8; ihdr[9] = 6; ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0; 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))]; const total = parts.reduce((n, p) => n + p.length, 0); const out = new Uint8Array(total); let o = 0; for (const p of parts) { out.set(p, o); o += p.length; } return out; }