{"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["/**\n * libimagequant WASM - Promise-based API for image quantization in browsers\n * \n * This module provides a high-level, promise-based interface for the libimagequant\n * image quantization library running in a Web Worker with WASM.\n */\n\nexport interface QuantizationOptions {\n  /** Speed vs quality trade-off (1-10, lower = better quality) */\n  speed?: number;\n  /** Quality settings */\n  quality?: {\n    min: number;\n    target: number;\n  };\n  /** Maximum colors in palette (2-256) */\n  maxColors?: number;\n  /** Dithering level (0.0-1.0) */\n  dithering?: number;\n  /** Posterization level (0-4) */\n  posterization?: number;\n  /** Whether to return RGBA image data */\n  returnRgba?: boolean;\n}\n\nexport interface QuantizationResult {\n  /** Color palette array */\n  palette: number[][];\n  /** Palette indices for each pixel (if returnRgba is false) */\n  indexedData: number[] | null;\n  /** RGBA image data (if returnRgba is true) */\n  imageData: number[] | null;\n  /** Achieved quality (0-1) */\n  quality: number;\n  /** Number of colors in palette */\n  paletteLength: number;\n  /** Image width */\n  width: number;\n  /** Image height */\n  height: number;\n}\n\nexport interface LibImageQuantOptions {\n  /** Custom path to worker.js file */\n  workerUrl?: string;\n  /** Custom path to WASM module directory (should contain libimagequant_wasm.js) */\n  wasmUrl?: string;\n}\n\nexport class LibImageQuant {\n    private worker: Worker | null = null;\n    private isReady: boolean = false;\n    private pendingOperations = new Map<number, { resolve: (value: any) => void; reject: (reason?: any) => void }>();\n    private operationCounter: number = 0;\n    private workerUrl: string;\n    private wasmUrl?: string;\n    private initPromise: Promise<void>;\n\n    constructor(options: LibImageQuantOptions = {}) {\n        this.workerUrl = options.workerUrl || './worker.js';\n        this.wasmUrl = options.wasmUrl;\n        this.initPromise = this.initialize();\n    }\n\n    /**\n     * Initialize the worker and WASM module\n     */\n    private async initialize(): Promise<void> {\n        if (this.isReady) return;\n\n        return new Promise((resolve, reject) => {\n            try {\n                this.worker = new Worker(this.workerUrl, { type: 'module' });\n                \n                this.worker.onmessage = (e) => {\n                    const { type, id, success, result, error } = e.data;\n                    \n                    if (type === 'ready') {\n                        // Send WASM URL configuration to worker if provided\n                        if (this.wasmUrl) {\n                            this.worker?.postMessage({\n                                type: 'configure',\n                                wasmUrl: this.wasmUrl\n                            });\n                        }\n                        this.isReady = true;\n                        resolve();\n                        return;\n                    }\n                    \n                    const operation = this.pendingOperations.get(id);\n                    if (operation) {\n                        this.pendingOperations.delete(id);\n                        \n                        if (success) {\n                            operation.resolve(result);\n                        } else {\n                            operation.reject(new Error(error));\n                        }\n                    }\n                };\n                \n                this.worker.onerror = (error) => {\n                    reject(new Error(`Worker error: ${error.message}`));\n                };\n                \n                // Set a timeout for initialization\n                setTimeout(() => {\n                    if (!this.isReady) {\n                        reject(new Error('Worker initialization timeout'));\n                    }\n                }, 10000);\n                \n            } catch (error) {\n                reject(error);\n            }\n        });\n    }\n\n    /**\n     * Send a message to the worker and return a promise\n     */\n    private async sendMessage(action: string, data: any): Promise<any> {\n        await this.initPromise;\n        \n        return new Promise((resolve, reject) => {\n            const id = ++this.operationCounter;\n            \n            this.pendingOperations.set(id, { resolve, reject });\n            \n            this.worker?.postMessage({\n                id,\n                action,\n                data\n            });\n            \n            // Set a timeout for the operation\n            setTimeout(() => {\n                if (this.pendingOperations.has(id)) {\n                    this.pendingOperations.delete(id);\n                    reject(new Error('Operation timeout'));\n                }\n            }, 30000); // 30 second timeout\n        });\n    }\n\n    /**\n     * Quantize a single image\n     */\n    async quantize(\n        imageData: ImageData | Uint8ClampedArray, \n        width: number, \n        height: number, \n        options: QuantizationOptions = {}\n    ): Promise<QuantizationResult> {\n        // Convert ImageData to array if necessary\n        const data = imageData instanceof ImageData \n            ? imageData.data \n            : imageData;\n\n        return this.sendMessage('quantize', {\n            imageData: Array.from(data),\n            width,\n            height,\n            options\n        });\n    }\n\n    /**\n     * Quantize using ImageData object directly\n     */\n    async quantizeImageData(imageData: ImageData, options: QuantizationOptions = {}): Promise<QuantizationResult> {\n        return this.quantize(imageData.data, imageData.width, imageData.height, options);\n    }\n\n    /**\n     * Quantize an image from a canvas element\n     */\n    async quantizeCanvas(canvas: HTMLCanvasElement, options: QuantizationOptions = {}): Promise<QuantizationResult> {\n        const ctx = canvas.getContext('2d');\n        if (!ctx) throw new Error('Failed to get 2D context');\n        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n        return this.quantizeImageData(imageData, options);\n    }\n\n    /**\n     * Quantize an image from an Image or HTMLImageElement\n     */\n    async quantizeImage(image: HTMLImageElement, options: QuantizationOptions = {}): Promise<QuantizationResult> {\n        return new Promise((resolve, reject) => {\n            const canvas = document.createElement('canvas');\n            const ctx = canvas.getContext('2d');\n            if (!ctx) throw new Error('Failed to get 2D context');\n            \n            canvas.width = image.naturalWidth || image.width;\n            canvas.height = image.naturalHeight || image.height;\n            \n            ctx.drawImage(image, 0, 0);\n            \n            this.quantizeCanvas(canvas, options)\n                .then(resolve)\n                .catch(reject);\n        });\n    }\n\n    /**\n     * Create a histogram from multiple images (for batch quantization)\n     */\n    async createHistogram(\n        images: Array<{ imageData: ImageData | Uint8ClampedArray; width: number; height: number }>, \n        options: QuantizationOptions = {}\n    ): Promise<{ histogramId: string; imageCount: number }> {\n        const processedImages = images.map(img => ({\n            imageData: Array.from(img.imageData instanceof ImageData ? img.imageData.data : img.imageData),\n            width: img.width,\n            height: img.height\n        }));\n\n        return this.sendMessage('create_histogram', {\n            images: processedImages,\n            options\n        });\n    }\n\n    /**\n     * Apply quantization result to a canvas\n     */\n    applyToCanvas(canvas: HTMLCanvasElement, result: QuantizationResult): void {\n        if (!result.imageData) {\n            throw new Error('Result does not contain image data');\n        }\n\n        const ctx = canvas.getContext('2d');\n        if (!ctx) throw new Error('Failed to get 2D context');\n        canvas.width = result.width;\n        canvas.height = result.height;\n\n        const imageData = new ImageData(\n            new Uint8ClampedArray(result.imageData),\n            result.width,\n            result.height\n        );\n\n        ctx.putImageData(imageData, 0, 0);\n    }\n\n    /**\n     * Create an ImageData object from quantization result\n     */\n    toImageData(result: QuantizationResult): ImageData {\n        if (!result.imageData) {\n            throw new Error('Result does not contain image data');\n        }\n\n        return new ImageData(\n            new Uint8ClampedArray(result.imageData),\n            result.width,\n            result.height\n        );\n    }\n\n    /**\n     * Terminate the worker and clean up resources\n     */\n    dispose() {\n        if (this.worker) {\n            this.worker.terminate();\n            this.worker = null;\n        }\n        \n        this.isReady = false;\n        this.pendingOperations.clear();\n    }\n}\n\n// Convenience functions\nexport function createQuantizer(options?: LibImageQuantOptions): LibImageQuant {\n    return new LibImageQuant(options);\n}\n\nexport async function quantizeImageData(imageData: ImageData, options: QuantizationOptions = {}): Promise<QuantizationResult> {\n    const quantizer = new LibImageQuant();\n    try {\n        return await quantizer.quantizeImageData(imageData, options);\n    } finally {\n        quantizer.dispose();\n    }\n}\n\nexport async function quantizeCanvas(canvas: HTMLCanvasElement, options: QuantizationOptions = {}): Promise<QuantizationResult> {\n    const quantizer = new LibImageQuant();\n    try {\n        return await quantizer.quantizeCanvas(canvas, options);\n    } finally {\n        quantizer.dispose();\n    }\n}\n\nexport async function quantizeImage(image: HTMLImageElement, options: QuantizationOptions = {}): Promise<QuantizationResult> {\n    const quantizer = new LibImageQuant();\n    try {\n        return await quantizer.quantizeImage(image, options);\n    } finally {\n        quantizer.dispose();\n    }\n}\n\n// Default export\nexport default LibImageQuant;"],"names":["LibImageQuant","constructor","options","this","worker","isReady","pendingOperations","Map","operationCounter","workerUrl","wasmUrl","initPromise","initialize","Promise","resolve","reject","Worker","type","onmessage","e","id","success","result","error","data","postMessage","operation","get","delete","Error","onerror","message","setTimeout","sendMessage","action","set","has","quantize","imageData","width","height","ImageData","Array","from","quantizeImageData","quantizeCanvas","canvas","ctx","getContext","getImageData","quantizeImage","image","document","createElement","naturalWidth","naturalHeight","drawImage","then","catch","createHistogram","images","processedImages","map","img","applyToCanvas","Uint8ClampedArray","putImageData","toImageData","dispose","terminate","clear","async","quantizer"],"mappings":"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"}