mirror of https://github.com/VERT-sh/VERT.git
112 lines
3.1 KiB
TypeScript
112 lines
3.1 KiB
TypeScript
/**
|
|
* ICNS (Apple Icon Image) file format encoder
|
|
*
|
|
* Creates .icns files from PNG images at various sizes.
|
|
*
|
|
* File structure:
|
|
* - 8 byte header: 'icns' magic + 4 byte file length (big-endian)
|
|
* - Icon elements: OSType (4 bytes) + length (4 bytes) + data
|
|
*
|
|
* References:
|
|
* - https://en.wikipedia.org/wiki/Apple_Icon_Image_format
|
|
* - https://github.com/fiahfy/icns
|
|
*/
|
|
|
|
/**
|
|
* Icon type definitions for ICNS format
|
|
* Maps pixel dimensions to OSType codes
|
|
*
|
|
* Only modern PNG-based types (OS X 10.5+) are included.
|
|
* Legacy types (is32, il32, ih32, etc.) require raw RGB + mask encoding
|
|
* and are not supported by this encoder.
|
|
*/
|
|
export const ICNS_TYPES = {
|
|
// Modern PNG-based formats (OS X 10.5+)
|
|
'ic07': { size: 128, scale: 1, format: 'png' }, // 128x128
|
|
'ic08': { size: 256, scale: 1, format: 'png' }, // 256x256
|
|
'ic09': { size: 512, scale: 1, format: 'png' }, // 512x512
|
|
'ic10': { size: 1024, scale: 2, format: 'png' }, // 1024x1024 (512x512@2x retina)
|
|
'ic11': { size: 32, scale: 2, format: 'png' }, // 32x32 (16x16@2x retina)
|
|
'ic12': { size: 64, scale: 2, format: 'png' }, // 64x64 (32x32@2x retina)
|
|
'ic13': { size: 256, scale: 2, format: 'png' }, // 256x256 (128x128@2x retina)
|
|
'ic14': { size: 512, scale: 2, format: 'png' }, // 512x512 (256x256@2x retina)
|
|
} as const;
|
|
|
|
export type IconType = keyof typeof ICNS_TYPES;
|
|
|
|
export interface IconEntry {
|
|
type: IconType;
|
|
data: Uint8Array;
|
|
}
|
|
|
|
/**
|
|
* Encodes icon entries into a complete ICNS file
|
|
*/
|
|
export function encodeIcns(entries: IconEntry[]): Uint8Array {
|
|
if (entries.length === 0) {
|
|
throw new Error('At least one icon entry is required');
|
|
}
|
|
|
|
// Calculate total file size
|
|
let totalSize = 8; // Header size
|
|
for (const entry of entries) {
|
|
totalSize += 8 + entry.data.length; // type (4) + length (4) + data
|
|
}
|
|
|
|
// Create output buffer
|
|
const buffer = new ArrayBuffer(totalSize);
|
|
const view = new DataView(buffer);
|
|
const uint8 = new Uint8Array(buffer);
|
|
|
|
let offset = 0;
|
|
|
|
// Write file header
|
|
// Magic number: 'icns'
|
|
uint8[offset++] = 0x69; // 'i'
|
|
uint8[offset++] = 0x63; // 'c'
|
|
uint8[offset++] = 0x6E; // 'n'
|
|
uint8[offset++] = 0x73; // 's'
|
|
|
|
// File length (big-endian)
|
|
view.setUint32(offset, totalSize, false);
|
|
offset += 4;
|
|
|
|
const textEncoder = new TextEncoder();
|
|
|
|
// Write icon elements
|
|
for (const entry of entries) {
|
|
// Write OSType (4 bytes)
|
|
const typeBytes = textEncoder.encode(entry.type);
|
|
uint8.set(typeBytes, offset);
|
|
offset += 4;
|
|
|
|
// Write data length (big-endian): 8 (header) + data length
|
|
const elementSize = 8 + entry.data.length;
|
|
view.setUint32(offset, elementSize, false);
|
|
offset += 4;
|
|
|
|
// Write image data
|
|
uint8.set(entry.data, offset);
|
|
offset += entry.data.length;
|
|
}
|
|
|
|
return uint8;
|
|
}
|
|
|
|
/**
|
|
* Get the recommended icon sizes for a complete ICNS file
|
|
* Returns sizes in descending order (largest first)
|
|
*/
|
|
export function getRecommendedSizes(): IconType[] {
|
|
return [
|
|
'ic10', // 1024x1024 (512@2x)
|
|
'ic14', // 512x512 (256@2x)
|
|
'ic09', // 512x512
|
|
'ic13', // 256x256 (128@2x)
|
|
'ic08', // 256x256
|
|
'ic12', // 64x64 (32@2x)
|
|
'ic07', // 128x128
|
|
'ic11', // 32x32 (16@2x)
|
|
];
|
|
}
|