import sharp from "sharp"; import { writeFileSync, readFileSync } from "node:fs"; import { resolve } from "node:path"; const ROOT = resolve(import.meta.dirname, ".."); const SOURCE = resolve(ROOT, "public/image/Studio/Logo Flat.png"); const OUT_ICON_PNG = resolve(ROOT, "app/icon.png"); const OUT_FAVICON_ICO = resolve(ROOT, "app/favicon.ico"); const OUT_PUBLIC_PNG = resolve(ROOT, "public/image/Studio/favicon.png"); const SIZE = 512; const MOUNTAIN_FILL = 0.78; async function makeFavicon(size) { const circle = Buffer.from( ` ` ); const mountainTargetW = Math.round(size * MOUNTAIN_FILL); const mountain = await sharp(SOURCE) .resize({ width: mountainTargetW }) .toBuffer(); const mountainMeta = await sharp(mountain).metadata(); return sharp(circle) .composite([ { input: mountain, top: Math.round((size - mountainMeta.height) / 2) + Math.round(size * 0.03), left: Math.round((size - mountainMeta.width) / 2), }, ]) .png() .toBuffer(); } function buildIco(pngBuffers) { const count = pngBuffers.length; const header = Buffer.alloc(6); header.writeUInt16LE(0, 0); header.writeUInt16LE(1, 2); header.writeUInt16LE(count, 4); const dirEntries = []; let offset = 6 + count * 16; for (const { buffer, size } of pngBuffers) { const entry = Buffer.alloc(16); entry.writeUInt8(size === 256 ? 0 : size, 0); entry.writeUInt8(size === 256 ? 0 : size, 1); entry.writeUInt8(0, 2); entry.writeUInt8(0, 3); entry.writeUInt16LE(1, 4); entry.writeUInt16LE(32, 6); entry.writeUInt32LE(buffer.length, 8); entry.writeUInt32LE(offset, 12); dirEntries.push(entry); offset += buffer.length; } return Buffer.concat([header, ...dirEntries, ...pngBuffers.map((p) => p.buffer)]); } const big = await makeFavicon(SIZE); writeFileSync(OUT_ICON_PNG, big); writeFileSync(OUT_PUBLIC_PNG, big); const icoSizes = [16, 32, 48]; const icoPngs = await Promise.all( icoSizes.map(async (s) => ({ size: s, buffer: await sharp(big).resize(s, s).png({ compressionLevel: 9 }).toBuffer(), })) ); writeFileSync(OUT_FAVICON_ICO, buildIco(icoPngs)); console.log("icon.png:", big.length, "bytes"); console.log("favicon.ico:", readFileSync(OUT_FAVICON_ICO).length, "bytes");