// High-level pipeline API for image processing import type { Device, ImageEntry, PipelineConfig } from '$lib/types'; import { getExtension } from '$lib/types'; import JSZip from 'jszip'; import { initPhoton } from './photon'; import { workerPool } from './workerPool'; let photonReady = false; export async function ensurePhotonReady(): Promise { if (photonReady) return; await initPhoton(); photonReady = true; } export async function processImage(file: File, device: Device): Promise { const { blob } = await processImageWithPipeline(file, device, { crop: { enabled: true, mode: 'center' }, resize: { enabled: true, mode: 'cover' }, brightness: { enabled: false, value: 0 }, contrast: { enabled: false, value: 0 }, gamma: { enabled: false, value: 1.0 }, autoLevels: { enabled: false, clipPercent: 0.5 }, greyscale: { enabled: true, method: 'luminosity' }, sharpen: { enabled: false, amount: 0 }, quantize: { enabled: true, levels: device.greyLevels }, dither: { enabled: false, algorithm: 'floyd-steinberg' } }); return blob; } export async function processImageWithPreview( file: File, device: Device ): Promise<{ blob: Blob; dataUrl: string }> { return processImageWithPipeline(file, device, { crop: { enabled: true, mode: 'center' }, resize: { enabled: true, mode: 'cover' }, brightness: { enabled: false, value: 0 }, contrast: { enabled: false, value: 0 }, gamma: { enabled: false, value: 1.0 }, autoLevels: { enabled: false, clipPercent: 0.5 }, greyscale: { enabled: true, method: 'luminosity' }, sharpen: { enabled: false, amount: 0 }, quantize: { enabled: true, levels: device.greyLevels }, dither: { enabled: false, algorithm: 'floyd-steinberg' } }); } export async function processImageWithPipeline( file: File, device: Device, config: PipelineConfig ): Promise<{ blob: Blob; dataUrl: string }> { return workerPool.process(file, device, config); } export function getOutputFilename(originalFilename: string, device: Device): string { const lastDot = originalFilename.lastIndexOf('.'); const stem = lastDot > 0 ? originalFilename.substring(0, lastDot) : originalFilename; const ext = getExtension(device.outputFormat); return `${stem}_${device.id}.${ext}`; } export async function createZipArchive(images: ImageEntry[], device: Device): Promise { const zip = new JSZip(); for (const image of images) { if (image.processedBlob) { const filename = getOutputFilename(image.filename, device); zip.file(filename, image.processedBlob); } } return zip.generateAsync({ type: 'blob' }); } export function downloadFile(blob: Blob, filename: string): void { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } export function downloadImage(image: ImageEntry, device: Device): void { if (!image.processedBlob) return; const filename = getOutputFilename(image.filename, device); downloadFile(image.processedBlob, filename); } export async function downloadAllAsZip(images: ImageEntry[], device: Device): Promise { const completedImages = images.filter((img) => img.status === 'complete' && img.processedBlob); if (completedImages.length === 0) return; const zipBlob = await createZipArchive(completedImages, device); downloadFile(zipBlob, '2eink_covers.zip'); }