Files
Xtranslate/scripts/make-icon.js
T
2026-10-02 01:25:19 -04:00

95 lines
2.8 KiB
JavaScript

'use strict';
const fs = require('fs');
const path = require('path');
const zlib = require('zlib');
function crc32(buf) {
let c = ~0;
for (let i = 0; i < buf.length; i += 1) {
c ^= buf[i];
for (let k = 0; k < 8; k += 1) c = (c >>> 1) ^ (0xedb88320 & -(c & 1));
}
return ~c >>> 0;
}
function chunk(type, data) {
const body = Buffer.concat([Buffer.from(type), data]);
const out = Buffer.alloc(8 + body.length);
out.writeUInt32BE(data.length, 0);
body.copy(out, 4);
out.writeUInt32BE(crc32(body), 8 + data.length);
return out;
}
function png(size) {
const raw = Buffer.alloc((size * 4 + 1) * size);
for (let y = 0; y < size; y += 1) {
const row = y * (size * 4 + 1);
raw[row] = 0;
for (let x = 0; x < size; x += 1) {
const nx = x / (size - 1);
const ny = y / (size - 1);
const onX = Math.abs(nx - ny) < 0.16 || Math.abs(nx - (1 - ny)) < 0.16;
const margin = size * 0.18;
const inside = x > margin && y > margin && x < size - margin && y < size - margin;
const i = row + 1 + x * 4;
if (inside && onX) {
raw[i] = 255;
raw[i + 1] = 255;
raw[i + 2] = 255;
raw[i + 3] = 255;
} else if (inside) {
raw[i] = 32;
raw[i + 1] = 122;
raw[i + 2] = 214;
raw[i + 3] = 255;
} else {
raw[i + 3] = 0;
}
}
}
const ihdr = Buffer.alloc(13);
ihdr.writeUInt32BE(size, 0);
ihdr.writeUInt32BE(size, 4);
ihdr[8] = 8;
ihdr[9] = 6;
return Buffer.concat([
Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]),
chunk('IHDR', ihdr),
chunk('IDAT', zlib.deflateSync(raw)),
chunk('IEND', Buffer.alloc(0)),
]);
}
function ico(images) {
const count = images.length;
const header = Buffer.alloc(6 + count * 16);
header.writeUInt16LE(0, 0);
header.writeUInt16LE(1, 2);
header.writeUInt16LE(count, 4);
let offset = 6 + count * 16;
const parts = [header];
images.forEach((image, index) => {
const entry = 6 + index * 16;
header[entry] = image.size === 256 ? 0 : image.size;
header[entry + 1] = image.size === 256 ? 0 : image.size;
header.writeUInt16LE(1, entry + 4);
header.writeUInt16LE(32, entry + 6);
header.writeUInt32LE(image.data.length, entry + 8);
header.writeUInt32LE(offset, entry + 12);
offset += image.data.length;
parts.push(image.data);
});
return Buffer.concat(parts);
}
const root = path.join(__dirname, '..', 'assets');
fs.mkdirSync(root, { recursive: true });
const sizes = [16, 32, 48, 256];
const images = sizes.map((size) => ({ size, data: png(size) }));
fs.writeFileSync(path.join(root, 'tray.png'), images[0].data);
fs.writeFileSync(path.join(root, 'icon.png'), images[3].data);
fs.writeFileSync(path.join(root, 'icon.ico'), ico(images));
console.log('wrote icons', images.map((image) => image.data.length).join(','));