# libimagequant-wasm

A TypeScript/JavaScript WebAssembly wrapper for the [libimagequant](https://github.com/ImageOptim/libimagequant) image quantization library. This package provides high-quality image color quantization in the browser with a direct WASM API.

## Features

- **🎨 High-quality image quantization** - Convert 24/32-bit images to 8-bit palette with alpha channel
- **📦 TypeScript support** - Full TypeScript definitions included
- **🔄 Promise-based API** - Modern, easy-to-use async interface
- **🧵 Threading support** - Utilizes libimagequant's multi-threading capabilities
- **🚀 Optimized WASM** - Size and performance optimized WebAssembly build
- **🌐 Browser compatible** - Works in all modern browsers supporting WebAssembly
- **📱 Multiple formats** - Supports ESM and CommonJS
- **⚡ Direct WASM execution** - No Web Worker overhead, run directly or in your own worker

## Installation

```bash
npm install libimagequant-wasm
```

### Requirements

- Node.js >= 16.0.0
- Modern browser with WebAssembly support

## Build from Source

1. Install dependencies:
```bash
npm install
```

2. Install Rust and wasm-pack:
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
rustup target add wasm32-unknown-unknown
```

3. Build the project:
```bash
npm run build
```

This will:
- Build the WASM module with wasm-pack
- Compile TypeScript to ESM and CommonJS
- Generate TypeScript declarations
- Optimize the output

## Quick Start

### Basic Usage

```typescript
import LibImageQuant, { QuantizationOptions } from 'libimagequant-wasm';

// Create quantizer instance
const quantizer = new LibImageQuant();

// Quantize ImageData
const canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

const options: QuantizationOptions = {
    maxColors: 64,
    quality: { min: 70, target: 100 },
    speed: 3
};

const result = await quantizer.quantizeImageData(imageData, options);

console.log(`Quantized to ${result.paletteLength} colors`);
console.log(`Quality: ${Math.round(result.quality * 100)}%`);

// Apply result to canvas
ctx.putImageData(result.imageData, 0, 0);

// Clean up (optional, no worker to terminate)
quantizer.dispose();
```

### Working with PNG Files

```javascript
import LibImageQuant from 'libimagequant-wasm';

const quantizer = new LibImageQuant();

// Quantize PNG from file input
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];

const result = await quantizer.quantizePng(file, { 
    maxColors: 128,
    dithering: 0.8 
});

// result.pngBytes contains the quantized PNG as Uint8Array
// result.imageData contains the result as ImageData for canvas display

// Download the quantized PNG
const blob = new Blob([result.pngBytes], { type: 'image/png' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'quantized.png';
a.click();
```

### Using in Web Workers (Optional)

You can still use this library in Web Workers if you want to avoid blocking the main thread:

```javascript
// worker.js
import LibImageQuant from 'libimagequant-wasm';

const quantizer = new LibImageQuant();

self.onmessage = async function(e) {
    const { imageData, options } = e.data;
    
    try {
        const result = await quantizer.quantizeImageData(imageData, options);
        self.postMessage({ success: true, result });
    } catch (error) {
        self.postMessage({ success: false, error: error.message });
    }
};

// main.js
const worker = new Worker('worker.js', { type: 'module' });

worker.postMessage({
    imageData: /* your ImageData */,
    options: { maxColors: 64 }
});

worker.onmessage = function(e) {
    const { success, result, error } = e.data;
    if (success) {
        // Use result
    } else {
        console.error(error);
    }
};
```

### Next.js Integration

For Next.js applications, you may encounter module resolution issues. Use the pre-loaded WASM module approach:

```typescript
import LibImageQuant from '@fe-daily/libimagequant-wasm';
import wasmModule from '@fe-daily/libimagequant-wasm/wasm/libimagequant_wasm.js';

const quantizer = new LibImageQuant({ wasmModule });
const result = await quantizer.quantizeImageData(imageData, options);
```

For detailed Next.js integration instructions, see [NEXTJS.md](./NEXTJS.md).

## API Reference

### LibImageQuant Class

#### Constructor
```javascript
const quantizer = new LibImageQuant(options)
```

Options:
- `wasmUrl` (string): Custom path to WASM module directory
- `wasmModule` (object): Pre-loaded WASM module (useful for Next.js and other bundlers)

#### Methods

**quantizePng(pngData, options)**
- Quantizes PNG from Uint8Array, ArrayBuffer, or Blob
- Returns: Promise<QuantizationResult>

**quantizeImageData(imageData, options)**
- Quantizes ImageData object
- Returns: Promise<QuantizationResult>

**dispose()**
- Cleans up resources (minimal cleanup needed)

### Quantization Options

```javascript
const options = {
    maxColors: 256,        // Maximum colors in palette (2-256)
    speed: 3,              // Speed vs quality (1-10, lower = better quality)
    quality: {             // Quality settings
        min: 0,            // Minimum quality (0-100)
        target: 100        // Target quality (0-100)
    },
    dithering: 1.0,        // Dithering level (0.0-1.0)
    posterization: 0       // Posterization level (0-4)
};
```

### QuantizationResult

```javascript
{
    palette: [[r, g, b, a], ...],  // Color palette array
    pngBytes: Uint8Array,           // Quantized PNG as bytes
    imageData: ImageData,           // Result as ImageData for canvas
    quality: 0.95,                  // Achieved quality (0-1)
    paletteLength: 64,              // Number of colors in palette
    width: 400,                     // Image width
    height: 300                     // Image height
}
```

## Browser Support

- Chrome 57+
- Firefox 52+
- Safari 11+
- Edge 16+

Requires WebAssembly support. Web Workers are optional and at the discretion of the developer.

## Performance Tips

1. **Reuse quantizer instances** when processing multiple images
2. **Use appropriate speed settings** - higher speed for real-time, lower for final output
3. **Batch process** similar images with the same settings
4. **Consider image size** - quantization time scales with pixel count
5. **Use Web Workers** - Move processing to workers to avoid blocking the main thread

## Examples

See `examples/direct-api-test.html` for a complete working example with the new direct API.

## License

MIT License - See LICENSE file for details.

**Important Note**: This project wraps the libimagequant library, which is dual-licensed under GPL-3.0 and commercial licenses. While this wrapper is MIT licensed, you must ensure you comply with the appropriate libimagequant license for your use case. For commercial use, you may need to obtain a commercial license from the libimagequant authors.

## Contributing

1. Fork the repository
2. Create your feature branch
3. Test your changes with `npm test`
4. Submit a pull request

## Troubleshooting

### WASM Loading Issues
- Verify WebAssembly support: `typeof WebAssembly === 'object'`
- Check browser console for loading errors
- Ensure proper MIME type for .wasm files

### Performance Issues
- Large images may take significant time to quantize
- Consider resizing images before quantization
- Use higher speed settings for real-time applications
- Consider using Web Workers for large images

### Module Loading
- Ensure the WASM files are properly served with correct MIME types
- Check that the relative paths to WASM files are correct

## Changelog

### 0.3.0
- **Breaking Change**: Removed mandatory Web Worker usage
- Direct WASM API - developers can choose to use workers or not
- Simplified API surface
- Better performance for small operations
- Reduced bundle size

### 0.2.0
- PNG direct processing support
- Improved error handling
- Better TypeScript definitions

### 0.1.0
- Initial release
- Basic quantization functionality
- Web Worker support
- Promise-based API