SOURCE / PINNED RELEASE
Made of little things.
Powder Tool V600Billion
- Release
- 142767edcab8…
- Author-recorded commit
- 6d92971effd0…
- License
- LICENSE
- Author’s source reference
- nostr://npub1fllw8kw0thjj55wds0uugcnp5kej2nfxd36eruq39d56wwz8r44q5q78wj/wss%3A%2F%2Fgit.napplet.soy%2F/powder-toy
Archive hash verified: ed7d6a8ea7083197…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
/** A small PNG writer: RGB, 8 bit, filter 0, zlib via CompressionStream when the platform has it. */
const CRC_TABLE = (() => {
const table = new Uint32Array(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;
table[n] = c >>> 0;
}
return table;
})();
function crc32(bytes, crc = 0xffffffff) {
for (let i = 0; i < bytes.length; i++) crc = CRC_TABLE[(crc ^ bytes[i]) & 0xff] ^ (crc >>> 8);
return crc;
}
function adler32(bytes) {
let a = 1, b = 0;
for (let i = 0; i < bytes.length; i++) {
a = (a + bytes[i]) % 65521;
b = (b + a) % 65521;
}
return ((b << 16) | a) >>> 0;
}
/** zlib stream with stored (uncompressed) deflate blocks: always available, synchronous. */
function zlibStored(data) {
const blocks = Math.max(1, Math.ceil(data.length / 65535));
const out = new Uint8Array(2 + data.length + blocks * 5 + 4);
out[0] = 0x78; out[1] = 0x01;
let o = 2;
for (let b = 0; b < blocks; b++) {
const start = b * 65535, end = Math.min(data.length, start + 65535), size = end - start;
out[o++] = b === blocks - 1 ? 1 : 0;
out[o++] = size & 0xff; out[o++] = size >>> 8;
out[o++] = ~size & 0xff; out[o++] = (~size >>> 8) & 0xff;
out.set(data.subarray(start, end), o);
o += size;
}
const adler = adler32(data);
out[o++] = adler >>> 24; out[o++] = (adler >>> 16) & 0xff; out[o++] = (adler >>> 8) & 0xff; out[o++] = adler & 0xff;
return out;
}
async function zlib(data) {
if (typeof CompressionStream === 'function') {
try {
const stream = new Blob([data]).stream().pipeThrough(new CompressionStream('deflate'));
return new Uint8Array(await new Response(stream).arrayBuffer());
} catch { /* fall back to stored blocks */ }
}
return zlibStored(data);
}
function chunk(type, data) {
const out = new Uint8Array(12 + data.length);
const view = new DataView(out.buffer);
view.setUint32(0, data.length);
for (let i = 0; i < 4; i++) out[4 + i] = type.charCodeAt(i);
out.set(data, 8);
view.setUint32(8 + data.length, (crc32(out.subarray(4, 8 + data.length)) ^ 0xffffffff) >>> 0);
return out;
}
/**
* @param {number} width
* @param {number} height
* @param {Uint8Array} rgb width * height * 3 bytes
*/
export async function encodePng(width, height, rgb) {
const raw = new Uint8Array(height * (width * 3 + 1));
for (let y = 0; y < height; y++) {
raw[y * (width * 3 + 1)] = 0;
raw.set(rgb.subarray(y * width * 3, (y + 1) * width * 3), y * (width * 3 + 1) + 1);
}
const header = new Uint8Array(13);
const view = new DataView(header.buffer);
view.setUint32(0, width);
view.setUint32(4, height);
header[8] = 8; header[9] = 2; header[10] = 0; header[11] = 0; header[12] = 0;
const parts = [
new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
chunk('IHDR', header),
chunk('IDAT', await zlib(raw)),
chunk('IEND', new Uint8Array(0)),
];
const out = new Uint8Array(parts.reduce((sum, part) => sum + part.length, 0));
let offset = 0;
for (const part of parts) { out.set(part, offset); offset += part.length; }
return out;
}
