/** * Minimaler PNG-Encoder – nur für den Platzhalter-Anbieter des Job-Dispatchers. * * Bewusst ohne Fremdbibliothek: das Repo soll für einen Testbildgenerator keine * Abhängigkeit mitschleppen. `zlib` bringt Node mit, mehr braucht ein PNG nicht. */ import { deflateSync } from 'node:zlib'; const CRC = (() => { const t = new Int32Array(256); for (let n = 0; n < 256; n++) { let c = n; for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; t[n] = c; } return t; })(); function crc32(buf) { let c = -1; for (let i = 0; i < buf.length; i++) c = CRC[(c ^ buf[i]) & 0xff] ^ (c >>> 8); return (c ^ -1) >>> 0; } function chunk(typ, daten) { const len = Buffer.alloc(4); len.writeUInt32BE(daten.length); const körper = Buffer.concat([Buffer.from(typ, 'ascii'), daten]); const crc = Buffer.alloc(4); crc.writeUInt32BE(crc32(körper)); return Buffer.concat([len, körper, crc]); } /** * @param {number} breite * @param {number} hoehe * @param {(x:number,y:number)=>[number,number,number]} farbe RGB je Pixel */ export function pngErzeugen(breite, hoehe, farbe) { const roh = Buffer.alloc(hoehe * (breite * 3 + 1)); let p = 0; for (let y = 0; y < hoehe; y++) { roh[p++] = 0; // Filter: none for (let x = 0; x < breite; x++) { const [r, g, b] = farbe(x, y); roh[p++] = r; roh[p++] = g; roh[p++] = b; } } const ihdr = Buffer.alloc(13); ihdr.writeUInt32BE(breite, 0); ihdr.writeUInt32BE(hoehe, 4); ihdr[8] = 8; // bit depth ihdr[9] = 2; // colour type: truecolour ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0; return Buffer.concat([ Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), chunk('IHDR', ihdr), chunk('IDAT', deflateSync(roh, { level: 9 })), chunk('IEND', Buffer.alloc(0)), ]); }