88 lines
2.9 KiB
JavaScript
88 lines
2.9 KiB
JavaScript
import { deflateSync } from "node:zlib";
|
|
import { writeFileSync, mkdirSync } from "node:fs";
|
|
|
|
// Civetest: minimal solid-color PNG encoder (non-interlaced, 8-bit RGB)
|
|
function crc32(buf) {
|
|
let c = ~0;
|
|
for (let n = 0; n < buf.length; n++) {
|
|
c ^= buf[n];
|
|
for (let k = 0; k < 8; k++) c = (c >>> 1) ^ (0xedb88320 & -(c & 1));
|
|
}
|
|
return ~c >>> 0;
|
|
}
|
|
|
|
function chunk(type, data) {
|
|
const len = Buffer.alloc(4);
|
|
len.writeUInt32BE(data.length, 0);
|
|
const typeBuf = Buffer.from(type, "ascii");
|
|
const crc = Buffer.alloc(4);
|
|
crc.writeUInt32BE(crc32(Buffer.concat([typeBuf, data])), 0);
|
|
return Buffer.concat([len, typeBuf, data, crc]);
|
|
}
|
|
|
|
// Simple canvas: draw background + centered white square "page" motif
|
|
function draw(size) {
|
|
const bg = [37, 99, 235]; // accent #2563eb
|
|
const white = [255, 255, 255];
|
|
const rows = [];
|
|
for (let y = 0; y < size; y++) {
|
|
const row = Buffer.alloc(1 + size * 3);
|
|
let p = 1;
|
|
for (let x = 0; x < size; x++) {
|
|
let color = bg;
|
|
// white rounded "page / document" block centered
|
|
const margin = Math.round(size * 0.22);
|
|
const inner = size - margin * 2;
|
|
const xm = x - margin;
|
|
const ym = y - margin;
|
|
const radius = Math.round(size * 0.08);
|
|
if (
|
|
xm >= 0 &&
|
|
ym >= 0 &&
|
|
xm < inner &&
|
|
ym < inner &&
|
|
(xm <= radius || xm >= inner - radius || ym <= radius || ym >= inner - radius || Math.hypot(xm - radius, ym - radius) <= radius || Math.hypot(xm - (inner - radius), ym - radius) <= radius || Math.hypot(xm - radius, ym - (inner - radius)) <= radius || Math.hypot(xm - (inner - radius), ym - (inner - radius)) <= radius)
|
|
) {
|
|
color = white;
|
|
}
|
|
// blue horizontal bars to suggest "CV" lines
|
|
const barTop = Math.round(size * 0.45);
|
|
const barHeight = Math.round(size * 0.06);
|
|
if (ym >= barTop && ym < barTop + barHeight && xm > Math.round(size * 0.32) && xm < inner - Math.round(size * 0.25)) {
|
|
color = bg;
|
|
}
|
|
if (ym >= barTop + barHeight * 2 && ym < barTop + barHeight * 3 && xm > Math.round(size * 0.38) && xm < inner - Math.round(size * 0.1)) {
|
|
color = bg;
|
|
}
|
|
row[p++] = color[0];
|
|
row[p++] = color[1];
|
|
row[p++] = color[2];
|
|
}
|
|
rows.push(row);
|
|
}
|
|
return Buffer.concat(rows);
|
|
}
|
|
|
|
function png(size, file) {
|
|
const raw = draw(size);
|
|
const ihdr = Buffer.alloc(13);
|
|
ihdr.writeUInt32BE(size, 0);
|
|
ihdr.writeUInt32BE(size, 4);
|
|
ihdr[8] = 8; // bit depth
|
|
ihdr[9] = 2; // color type RGB
|
|
ihdr[10] = 0;
|
|
ihdr[11] = 0;
|
|
ihdr[12] = 0;
|
|
const png = Buffer.concat([
|
|
Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]),
|
|
chunk("IHDR", ihdr),
|
|
chunk("IDAT", deflateSync(raw, { level: 9 })),
|
|
chunk("IEND", Buffer.alloc(0)),
|
|
]);
|
|
writeFileSync(file, png);
|
|
console.log(`generated ${file} (${size}x${size})`);
|
|
}
|
|
|
|
mkdirSync("./public/icons", { recursive: true });
|
|
png(192, "./public/icons/icon-192.png");
|
|
png(512, "./public/icons/icon-512.png"); |