mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-05-09 10:45:44 +02:00
229 lines
6.7 KiB
JavaScript
229 lines
6.7 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const zlib = require('node:zlib');
|
|
|
|
const root = path.resolve(__dirname, '..');
|
|
const iconsDir = path.join(root, 'src-tauri', 'icons');
|
|
|
|
const pngOutputs = {
|
|
'32x32.png': 32,
|
|
'128x128.png': 128,
|
|
'128x128@2x.png': 256,
|
|
'icon.png': 512,
|
|
'Square30x30Logo.png': 30,
|
|
'Square44x44Logo.png': 44,
|
|
'Square71x71Logo.png': 71,
|
|
'Square89x89Logo.png': 89,
|
|
'Square107x107Logo.png': 107,
|
|
'Square142x142Logo.png': 142,
|
|
'Square150x150Logo.png': 150,
|
|
'Square284x284Logo.png': 284,
|
|
'Square310x310Logo.png': 310,
|
|
'StoreLogo.png': 50,
|
|
};
|
|
|
|
const internalPngSizes = [16, 32, 64, 128, 256, 512, 1024];
|
|
|
|
function clamp(value, lower = 0, upper = 1) {
|
|
return Math.max(lower, Math.min(upper, value));
|
|
}
|
|
|
|
function smoothstep(edge0, edge1, value) {
|
|
if (edge0 === edge1) return 0;
|
|
const t = clamp((value - edge0) / (edge1 - edge0));
|
|
return t * t * (3 - 2 * t);
|
|
}
|
|
|
|
function blend(dst, srcRgb, srcAlpha) {
|
|
if (srcAlpha <= 0) return dst;
|
|
const alpha = clamp(srcAlpha);
|
|
const inv = 1 - alpha;
|
|
return [
|
|
Math.round(dst[0] * inv + srcRgb[0] * alpha),
|
|
Math.round(dst[1] * inv + srcRgb[1] * alpha),
|
|
Math.round(dst[2] * inv + srcRgb[2] * alpha),
|
|
255,
|
|
];
|
|
}
|
|
|
|
function roundedRectAlpha(nx, ny, half, radius, feather) {
|
|
const qx = Math.abs(nx) - (half - radius);
|
|
const qy = Math.abs(ny) - (half - radius);
|
|
const outside = Math.hypot(Math.max(qx, 0), Math.max(qy, 0));
|
|
const inside = Math.min(Math.max(qx, qy), 0);
|
|
const signedDistance = outside + inside - radius;
|
|
return smoothstep(feather, -feather, signedDistance);
|
|
}
|
|
|
|
function drawIcon(size) {
|
|
const pixels = Buffer.alloc(size * size * 4);
|
|
const feather = 2.4 / Math.max(size, 1);
|
|
for (let y = 0; y < size; y += 1) {
|
|
for (let x = 0; x < size; x += 1) {
|
|
const nx = ((x + 0.5) / size) * 2 - 1;
|
|
const ny = ((y + 0.5) / size) * 2 - 1;
|
|
|
|
const bgAlpha = roundedRectAlpha(nx, ny, 0.93, 0.28, feather);
|
|
if (bgAlpha <= 0) continue;
|
|
|
|
const gradientMix = clamp((nx - ny + 2) / 4);
|
|
const bg = [
|
|
Math.round(7 + 8 * gradientMix),
|
|
Math.round(20 + 26 * (1 - gradientMix)),
|
|
Math.round(28 + 42 * gradientMix),
|
|
];
|
|
let rgba = [bg[0], bg[1], bg[2], Math.round(255 * bgAlpha)];
|
|
const r = Math.hypot(nx, ny);
|
|
|
|
const ringDistance = Math.abs(r - 0.53) - 0.085;
|
|
const ringAlpha = smoothstep(feather * 2.2, -feather * 2.2, ringDistance) * bgAlpha;
|
|
rgba = blend(rgba, [27, 196, 157], ringAlpha);
|
|
|
|
const glowAlpha = smoothstep(0.74, 0.16, r) * 0.18 * bgAlpha;
|
|
rgba = blend(rgba, [32, 228, 190], glowAlpha);
|
|
|
|
const diamondDistance = Math.abs(nx) + Math.abs(ny) - 0.34;
|
|
const diamondAlpha = smoothstep(feather * 2.6, -feather * 2.6, diamondDistance) * bgAlpha;
|
|
rgba = blend(rgba, [13, 41, 45], diamondAlpha);
|
|
|
|
const barVDistance = Math.max(Math.abs(nx) - 0.055, Math.abs(ny) - 0.44);
|
|
const barHDistance = Math.max(Math.abs(ny) - 0.055, Math.abs(nx) - 0.44);
|
|
const barAlpha = Math.max(
|
|
smoothstep(feather * 2.6, -feather * 2.6, barVDistance),
|
|
smoothstep(feather * 2.6, -feather * 2.6, barHDistance),
|
|
) * 0.92 * bgAlpha;
|
|
rgba = blend(rgba, [183, 251, 239], barAlpha);
|
|
|
|
const coreDistance = r - 0.108;
|
|
const coreAlpha = smoothstep(feather * 2.4, -feather * 2.4, coreDistance) * bgAlpha;
|
|
rgba = blend(rgba, [244, 255, 253], coreAlpha);
|
|
|
|
const index = (y * size + x) * 4;
|
|
pixels[index] = rgba[0];
|
|
pixels[index + 1] = rgba[1];
|
|
pixels[index + 2] = rgba[2];
|
|
pixels[index + 3] = rgba[3];
|
|
}
|
|
}
|
|
return pixels;
|
|
}
|
|
|
|
function crc32(buffer) {
|
|
let crc = ~0;
|
|
for (let i = 0; i < buffer.length; i += 1) {
|
|
crc ^= buffer[i];
|
|
for (let j = 0; j < 8; j += 1) {
|
|
crc = (crc >>> 1) ^ (0xEDB88320 & -(crc & 1));
|
|
}
|
|
}
|
|
return (~crc) >>> 0;
|
|
}
|
|
|
|
function chunk(type, data) {
|
|
const out = Buffer.alloc(8 + data.length + 4);
|
|
out.writeUInt32BE(data.length, 0);
|
|
out.write(type, 4, 4, 'ascii');
|
|
data.copy(out, 8);
|
|
out.writeUInt32BE(crc32(Buffer.concat([Buffer.from(type, 'ascii'), data])), out.length - 4);
|
|
return out;
|
|
}
|
|
|
|
function encodePng(size, rgba) {
|
|
const stride = size * 4;
|
|
const rows = [];
|
|
for (let y = 0; y < size; y += 1) {
|
|
rows.push(Buffer.from([0]));
|
|
rows.push(rgba.subarray(y * stride, (y + 1) * stride));
|
|
}
|
|
const raw = Buffer.concat(rows);
|
|
return Buffer.concat([
|
|
Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]),
|
|
chunk('IHDR', Buffer.from([
|
|
(size >>> 24) & 0xff,
|
|
(size >>> 16) & 0xff,
|
|
(size >>> 8) & 0xff,
|
|
size & 0xff,
|
|
(size >>> 24) & 0xff,
|
|
(size >>> 16) & 0xff,
|
|
(size >>> 8) & 0xff,
|
|
size & 0xff,
|
|
8,
|
|
6,
|
|
0,
|
|
0,
|
|
0,
|
|
])),
|
|
chunk('IDAT', zlib.deflateSync(raw, { level: 9 })),
|
|
chunk('IEND', Buffer.alloc(0)),
|
|
]);
|
|
}
|
|
|
|
function writeIco(targetPath, pngData) {
|
|
const header = Buffer.alloc(6);
|
|
header.writeUInt16LE(0, 0);
|
|
header.writeUInt16LE(1, 2);
|
|
header.writeUInt16LE(1, 4);
|
|
|
|
const entry = Buffer.alloc(16);
|
|
entry.writeUInt8(0, 0);
|
|
entry.writeUInt8(0, 1);
|
|
entry.writeUInt8(0, 2);
|
|
entry.writeUInt8(0, 3);
|
|
entry.writeUInt16LE(1, 4);
|
|
entry.writeUInt16LE(32, 6);
|
|
entry.writeUInt32LE(pngData.length, 8);
|
|
entry.writeUInt32LE(22, 12);
|
|
|
|
fs.writeFileSync(targetPath, Buffer.concat([header, entry, pngData]));
|
|
}
|
|
|
|
function writeIcns(targetPath, pngBySize) {
|
|
const typeMap = new Map([
|
|
[16, 'icp4'],
|
|
[32, 'icp5'],
|
|
[64, 'icp6'],
|
|
[128, 'ic07'],
|
|
[256, 'ic08'],
|
|
[512, 'ic09'],
|
|
[1024, 'ic10'],
|
|
]);
|
|
|
|
const blocks = [];
|
|
for (const [size, type] of typeMap) {
|
|
const png = pngBySize.get(size);
|
|
if (!png) continue;
|
|
const header = Buffer.alloc(8);
|
|
header.write(type, 0, 4, 'ascii');
|
|
header.writeUInt32BE(png.length + 8, 4);
|
|
blocks.push(header, png);
|
|
}
|
|
|
|
const payload = Buffer.concat(blocks);
|
|
const icnsHeader = Buffer.alloc(8);
|
|
icnsHeader.write('icns', 0, 4, 'ascii');
|
|
icnsHeader.writeUInt32BE(payload.length + 8, 4);
|
|
fs.writeFileSync(targetPath, Buffer.concat([icnsHeader, payload]));
|
|
}
|
|
|
|
fs.mkdirSync(iconsDir, { recursive: true });
|
|
|
|
const pngBySize = new Map();
|
|
for (const size of internalPngSizes) {
|
|
pngBySize.set(size, encodePng(size, drawIcon(size)));
|
|
}
|
|
|
|
for (const [filename, size] of Object.entries(pngOutputs)) {
|
|
fs.writeFileSync(path.join(iconsDir, filename), pngBySize.get(size) ?? encodePng(size, drawIcon(size)));
|
|
}
|
|
|
|
writeIco(path.join(iconsDir, 'icon.ico'), pngBySize.get(256));
|
|
writeIcns(path.join(iconsDir, 'icon.icns'), pngBySize);
|
|
|
|
const created = fs.readdirSync(iconsDir).sort();
|
|
console.log(`Generated ${created.length} desktop icons in ${iconsDir}`);
|
|
for (const name of created) {
|
|
console.log(` - ${name}`);
|
|
}
|