118 lines
4.3 KiB
JavaScript
118 lines
4.3 KiB
JavaScript
import sharp from 'sharp';
|
|
import { promises as fs } from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import png2icons from 'png2icons';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const rootDir = path.resolve(__dirname, '..');
|
|
const sourceIcon = path.join(rootDir, 'appicon-square.png');
|
|
|
|
// Electron desktop icons
|
|
const electronIcons = [
|
|
{ name: '16x16.png', size: 16 },
|
|
{ name: '24x24.png', size: 24 },
|
|
{ name: '32x32.png', size: 32 },
|
|
{ name: '48x48.png', size: 48 },
|
|
{ name: '64x64.png', size: 64 },
|
|
{ name: '128x128.png', size: 128 },
|
|
{ name: '256x256.png', size: 256 },
|
|
{ name: '512x512.png', size: 512 },
|
|
{ name: '1024x1024.png', size: 1024 },
|
|
{ name: 'icon.png', size: 512 },
|
|
];
|
|
|
|
async function generateElectronIcons() {
|
|
const iconsDir = path.join(rootDir, 'icons');
|
|
|
|
// Ensure icons directory exists
|
|
await fs.mkdir(iconsDir, { recursive: true });
|
|
|
|
for (const icon of electronIcons) {
|
|
const outputPath = path.join(iconsDir, icon.name);
|
|
await sharp(sourceIcon)
|
|
.resize(icon.size, icon.size)
|
|
.png()
|
|
.toFile(outputPath);
|
|
console.log(`Generated: ${icon.name}`);
|
|
}
|
|
|
|
// Generate ICO file (Windows)
|
|
const sourceBuffer = await fs.readFile(sourceIcon);
|
|
const icoBuffer = png2icons.createICO(sourceBuffer, png2icons.BEZIER, 0, true, true);
|
|
await fs.writeFile(path.join(iconsDir, 'icon.ico'), icoBuffer);
|
|
console.log('Generated: icon.ico');
|
|
|
|
// Generate ICNS file (macOS)
|
|
const icnsBuffer = png2icons.createICNS(sourceBuffer, png2icons.BEZIER, 0);
|
|
await fs.writeFile(path.join(iconsDir, 'icon.icns'), icnsBuffer);
|
|
console.log('Generated: icon.icns');
|
|
}
|
|
|
|
/**
|
|
* Android adaptive icon density configs.
|
|
* Foreground canvas = 108dp equivalent. Safe zone = center 72dp (66.7%).
|
|
* Legacy launcher icon = 48dp equivalent.
|
|
* @type {Array<{density: string, foregroundSize: number, launcherSize: number}>}
|
|
*/
|
|
const androidDensities = [
|
|
{ density: 'mipmap-mdpi', foregroundSize: 108, launcherSize: 48 },
|
|
{ density: 'mipmap-hdpi', foregroundSize: 162, launcherSize: 72 },
|
|
{ density: 'mipmap-xhdpi', foregroundSize: 216, launcherSize: 96 },
|
|
{ density: 'mipmap-xxhdpi', foregroundSize: 324, launcherSize: 144 },
|
|
{ density: 'mipmap-xxxhdpi', foregroundSize: 432, launcherSize: 192 },
|
|
];
|
|
|
|
/**
|
|
* Generates Android mipmap launcher icons from the source icon.
|
|
* The foreground layer centers the content within the adaptive icon safe zone
|
|
* (72/108dp = 66.7% of canvas), leaving transparent padding for the system mask.
|
|
*/
|
|
async function generateAndroidIcons() {
|
|
const resDir = path.join(rootDir, 'android', 'app', 'src', 'main', 'res');
|
|
|
|
for (const { density, foregroundSize, launcherSize } of androidDensities) {
|
|
const dir = path.join(resDir, density);
|
|
|
|
// Foreground layer: content occupies 72/108 of the canvas (safe zone), centered
|
|
const contentSize = Math.round(foregroundSize * (72 / 108));
|
|
const padding = Math.round((foregroundSize - contentSize) / 2);
|
|
|
|
const foregroundBuffer = await sharp(sourceIcon)
|
|
.resize(contentSize, contentSize, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } })
|
|
.extend({ top: padding, bottom: padding, left: padding, right: padding, background: { r: 0, g: 0, b: 0, alpha: 0 } })
|
|
.png()
|
|
.toBuffer();
|
|
|
|
await fs.writeFile(path.join(dir, 'ic_launcher_foreground.png'), foregroundBuffer);
|
|
console.log(`${density}/ic_launcher_foreground.png (${foregroundSize}x${foregroundSize}, content ${contentSize}px)`);
|
|
|
|
// Legacy launcher icon (ic_launcher, ic_launcher_round) — plain square resize
|
|
const legacyBuffer = await sharp(sourceIcon)
|
|
.resize(launcherSize, launcherSize, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } })
|
|
.png()
|
|
.toBuffer();
|
|
|
|
await fs.writeFile(path.join(dir, 'ic_launcher.png'), legacyBuffer);
|
|
await fs.writeFile(path.join(dir, 'ic_launcher_round.png'), legacyBuffer);
|
|
console.log(`${density}/ic_launcher.png + ic_launcher_round.png (${launcherSize}x${launcherSize})`);
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
console.log('Generating icons from:', sourceIcon);
|
|
console.log('');
|
|
|
|
console.log('=== Electron Desktop Icons ===');
|
|
await generateElectronIcons();
|
|
console.log('');
|
|
|
|
console.log('=== Android Mipmap Icons ===');
|
|
await generateAndroidIcons();
|
|
console.log('');
|
|
|
|
console.log('Done!');
|
|
}
|
|
|
|
main().catch(console.error);
|