chore: add topic candidates for 2026-04-25
This commit is contained in:
97
gen-png.cjs
Normal file
97
gen-png.cjs
Normal file
@@ -0,0 +1,97 @@
|
||||
// Pure JS PNG Generator for QR codes
|
||||
// Write a minimal valid PNG with no external dependencies
|
||||
const fs = require('fs');
|
||||
const zlib = require('zlib');
|
||||
|
||||
function crc32(buf) {
|
||||
let crc = 0xFFFFFFFF;
|
||||
const table = new Uint32Array(256);
|
||||
for (let i = 0; i < 256; i++) {
|
||||
let c = i;
|
||||
for (let k = 0; k < 8; k++) c = (c & 1) ? 0xEDB88320 ^ (c >>> 1) : c >>> 1;
|
||||
table[i] = c;
|
||||
}
|
||||
for (let i = 0; i < buf.length; i++) crc = table[(crc ^ buf[i]) & 0xFF] ^ (crc >>> 8);
|
||||
return (crc ^ 0xFFFFFFFF) >>> 0;
|
||||
}
|
||||
|
||||
function makeChunk(type, data) {
|
||||
const typeB = Buffer.from(type);
|
||||
const lenB = Buffer.alloc(4);
|
||||
lenB.writeUInt32BE(data.length);
|
||||
const crcB = Buffer.alloc(4);
|
||||
crcB.writeUInt32BE(crc32(Buffer.concat([typeB, data])));
|
||||
return Buffer.concat([lenB, typeB, data, crcB]);
|
||||
}
|
||||
|
||||
function createPNG(matrix) {
|
||||
const size = matrix.length;
|
||||
const scale = 10;
|
||||
const outSize = size * scale;
|
||||
|
||||
// Raw image data: filter byte (0) + RGB pixels per row
|
||||
const raw = Buffer.alloc((1 + size * 3) * outSize);
|
||||
for (let y = 0; y < outSize; y++) {
|
||||
const rowOffset = y * (1 + outSize * 3);
|
||||
raw[rowOffset] = 0; // filter type: none
|
||||
const moduleY = Math.floor(y / scale);
|
||||
for (let x = 0; x < outSize; x++) {
|
||||
const moduleX = Math.floor(x / scale);
|
||||
const pixel = matrix[moduleY][moduleX] ? 0 : 255;
|
||||
const offset = rowOffset + 1 + x * 3;
|
||||
raw[offset] = pixel; // R
|
||||
raw[offset + 1] = pixel; // G
|
||||
raw[offset + 2] = pixel; // B
|
||||
}
|
||||
}
|
||||
|
||||
const compressed = zlib.deflateSync(raw);
|
||||
|
||||
// PNG signature
|
||||
const signature = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]);
|
||||
|
||||
// IHDR chunk
|
||||
const ihdr = Buffer.alloc(13);
|
||||
ihdr.writeUInt32BE(outSize, 0); // width
|
||||
ihdr.writeUInt32BE(outSize, 4); // height
|
||||
ihdr[8] = 8; // bit depth
|
||||
ihdr[9] = 2; // color type (RGB)
|
||||
ihdr[10] = 0; // compression
|
||||
ihdr[11] = 0; // filter
|
||||
ihdr[12] = 0; // interlace
|
||||
|
||||
const ihdrChunk = makeChunk('IHDR', ihdr);
|
||||
const idatChunk = makeChunk('IDAT', compressed);
|
||||
const iendChunk = makeChunk('IEND', Buffer.alloc(0));
|
||||
|
||||
return Buffer.concat([signature, ihdrChunk, idatChunk, iendChunk]);
|
||||
}
|
||||
|
||||
// Matrix from our QR generator (21x21 for "TEST")
|
||||
const matrix = [
|
||||
[1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1],
|
||||
[1,0,0,0,0,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,1],
|
||||
[1,0,1,1,1,0,1,0,1,1,0,1,0,1,0,1,1,1,0,0,1],
|
||||
[1,0,1,1,1,0,1,0,0,1,1,0,1,0,1,0,1,1,1,0,1],
|
||||
[1,0,1,1,1,0,1,0,1,0,1,0,0,1,0,1,1,1,0,0,1],
|
||||
[1,0,0,0,0,0,1,0,1,1,0,1,1,1,1,1,0,0,0,0,1],
|
||||
[1,1,1,1,1,1,1,0,0,0,0,0,1,0,1,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,0,0,0,0],
|
||||
[1,0,1,0,1,0,1,0,0,0,1,1,0,1,0,1,0,1,1,0,1],
|
||||
[0,1,0,1,0,1,0,1,1,0,0,1,0,1,0,1,1,0,0,1,0],
|
||||
[0,0,1,0,0,1,0,1,0,1,0,1,0,1,0,0,1,0,0,0,0],
|
||||
[1,1,0,1,0,0,1,0,1,0,1,0,1,0,1,1,0,1,1,1,1],
|
||||
[0,0,0,1,1,1,0,1,0,1,0,1,0,1,1,0,1,1,0,0,0],
|
||||
[1,1,1,0,0,0,1,0,0,0,1,0,0,1,0,1,1,0,1,1,0],
|
||||
[1,0,1,1,1,0,1,0,1,1,0,1,0,1,0,0,0,0,0,1,0],
|
||||
[1,0,0,0,0,0,1,0,0,1,1,0,1,0,1,1,1,0,1,0,1],
|
||||
[1,1,1,1,1,1,1,0,1,0,1,0,1,1,0,1,0,1,0,0,1],
|
||||
[0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,1,1,0,1,1,0],
|
||||
[1,1,1,1,1,1,1,0,0,1,0,1,0,0,0,1,1,0,0,0,0],
|
||||
[1,0,0,0,0,0,1,0,1,0,1,0,1,0,1,1,0,1,1,1,1],
|
||||
[1,1,1,1,1,1,1,0,1,0,1,1,0,0,1,0,0,0,0,0,0],
|
||||
];
|
||||
|
||||
const png = createPNG(matrix);
|
||||
fs.writeFileSync('/home/node/.openclaw/workspace/assets/qr-test.png', png);
|
||||
console.log('PNG written:', png.length, 'bytes');
|
||||
Reference in New Issue
Block a user