npm package: @shopg41/wimp
Description: wimp - Web-based Image Manipulation Program
Latest version: 0.0.2

# wimp - Web-based Image Manipulation Program

I created this project because I couldn't find an open-source and free-to-use image editor library that had solid touch support and was actively maintained. My goal is to provide a powerful and easy-to-use image editor for developers to integrate into their projects.

This project is a professional image editing application built with React, HTML5 Canvas, and Zustand for state management.

## Features

- **Transform Tools:** Rotate, flip, and resize your image with ease.
- **Crop Tools:** Crop your images with various aspect ratio guides or freeform.
- **Drawing Tools:** Draw on your images with a pencil, eraser, and text tool.
- **Adjustments:** Brightness and contrast controls.
- **Filters:** Apply grayscale, sepia, and vintage filters.
- **Secure Blur:** Redact sensitive information with blur tools.
- **File Operations:** Open, save, and export images in multiple formats.
- **Undo/Redo:** Don't worry about making mistakes, you can always undo and redo your actions.
- **Touch Support:** Works on mobile devices with touch support.
- **Zoom & Pan:** Navigate large images with intuitive controls.

## Architecture

This project uses:

- **React** - UI framework
- **Zustand** - State management with a centralized store
- **TypeBox** - Runtime type validation
- **HTML5 Canvas** - Image manipulation

### Store Structure

The Zustand store (`src/store/canvasStore.ts`) manages all application state:

- **Tool State**: Current tool selection
- **Drawing Settings**: Color, brush size, font family
- **Text Objects**: Text overlays on canvas
- **Adjustments**: Brightness, contrast, filters
- **Blur Settings**: Radius and mode
- **Crop State**: Crop mode, selection, aspect ratios
- **Zoom/Pan**: Viewport transformations
- **History**: Undo/redo stack (limited to 20 states)

Components access state directly from the store using `useCanvasStore()`, eliminating prop drilling.

## Getting Started

1.  **Clone the repository:**
    ```bash
    git clone https://github.com/shopg41/wimp.git
    ```
2.  **Install dependencies:**
    ```bash
    bun install
    ```
3.  **Start the development server:**
    ```bash
    bun run dev
    ```

## Installation

```bash
npm install @shopg41/wimp
# or
bun add @shopg41/wimp
# or
yarn add @shopg41/wimp
```

## Using it as a Library

To use this image editor in your own project, import the `Wimp` component and the necessary styles.

```jsx
import { Wimp } from '@shopg41/wimp';
import '@shopg41/wimp/style.css';

function App() {
  return (
    <div style={{ height: '100vh' }}>
      <Wimp />
    </div>
  );
}
```

### Accessing the Store

You can also access the Zustand store directly:

```tsx
import { useCanvasStore } from '@shopg41/wimp';

function MyComponent() {
  const currentTool = useCanvasStore((state) => state.currentTool);
  const setCurrentTool = useCanvasStore((state) => state.setCurrentTool);
  const zoom = useCanvasStore((state) => state.zoom);
  
  // Use selectors for better performance
  const historyIndex = useCanvasStore((s) => s.historyIndex);
  
  return (
    <div>
      <p>Current tool: {currentTool}</p>
      <p>Zoom: {Math.round(zoom * 100)}%</p>
      <p>History: {historyIndex}</p>
    </div>
  );
}
```

## Performance Benchmarks

Performance benchmarks are run using Bun. Results below are from local development environment (M2 MacBook).

### Canvas Scaling Performance

Time to scale images using HTML5 Canvas drawImage with high-quality smoothing.

| Image Size | Scale 0.5x | Scale 1.0x | Scale 1.5x | Scale 2.0x |
|------------|------------|------------|------------|------------|
| Small (640x480) | 1.38ms | 3.81ms | 8.37ms | 14.52ms |
| Medium (1280x720) | 2.70ms | 10.71ms | 24.99ms | 43.86ms |
| Large (1920x1080) | 6.19ms | 24.14ms | 55.05ms | 99.00ms |
| 4K (3840x2160) | 24.55ms | 98.24ms | 227.44ms | 415.45ms |

**Scaling Performance Notes:**
- Operations scale quadratically with image dimensions
- 4K image scaling to 2x can take 400+ ms (notable UI lag)
- Recommendation: Show loading indicator for images >2K resolution

### Blur Processing Performance

Time to apply secure blur with different radii (optimized algorithm).

| Image Size | Radius 10px | Radius 25px | Radius 50px |
|------------|-------------|-------------|--------------|
| Medium (400x300) | 21ms | 88ms | 373ms |
| Large (800x600) | 59ms | 349ms | 1493ms |
| Full HD (1920x1080) | 192ms | 1138ms | 4886ms |

**Blur Performance Notes:**
- Selective blur is 40% faster at 25px radius vs original implementation
- For full-image blur (no mask), uses stack blur algorithm - O(n) complexity
- Stack blur stays ~50ms for 400x300 regardless of radius
- Large images with high radius still block - consider Web Worker for production

### Full Blur Performance (Stack Blur)

When blurring entire image (no selective mask):

| Image Size | Radius 10px | Radius 25px | Radius 50px |
|------------|-------------|-------------|--------------|
| Medium (400x300) | 8ms | 18ms | 52ms |
| Large (800x600) | 28ms | 65ms | 204ms |
| Full HD (1920x1080) | 704ms | 863ms | 875ms |

**Full Blur Notes:**
- Stack blur algorithm is O(n) - scales linearly with image size
- Radius has minimal impact on performance (constant time per pixel)
- Recommended for real-time preview when blurring full image

### Canvas Cropping Performance

Time to crop images at various percentages.

| Image Size | Crop 25% | Crop 50% | Crop 75% |
|------------|----------|----------|----------|
| Small (640x480) | 3.63ms | 6.30ms | 11.31ms |
| Medium (1280x720) | 10.41ms | 20.41ms | 33.40ms |
| Large (1920x1080) | 21.64ms | 41.22ms | 76.98ms |
| 4K (3840x2160) | 86.82ms | 167.40ms | 324.76ms |

**Cropping Performance Notes:**
- Cropping involves getImageData, temp canvas creation, and putImageData
- Large crops take longer due to memory operations
- 4K crops can take 80-325ms depending on crop size

### Canvas History Performance

Time to save/restore canvas state for undo/redo functionality.

| Image Size | Save State | Undo Operation |
|------------|------------|----------------|
| Small (640x480) | 16.75ms | 2.73ms |
| Medium (1280x720) | 49.97ms | 7.50ms |
| Large (1920x1080) | 112.69ms | 18.21ms |
| 4K (3840x2160) | 430.22ms | 74.84ms |

**History Performance Notes:**
- Save state is memory-intensive (stores full ImageData)
- 4K images use ~32MB per history state
- History is limited to 20 states to prevent memory issues
- Undo is faster than save (no memory allocation)

### Interaction Performance

| Operation | Average Time |
|-----------|--------------|
| Resize Handle Detection | 0.019ms |
| Coordinate Bounding | 0.002ms |

**Interaction Notes:**
- UI interactions are extremely fast (<0.02ms)
- No performance concerns for real-time interactions

### Throughput Test

Continuous operations per second for 1 second:

| Resolution | Operations/sec | Throughput |
|------------|----------------|------------|
| 720p (1280x720) | 76.5 | 70.5 MP/s |
| 1080p (1920x1080) | 37.8 | 78.5 MP/s |
| 4K (3840x2160) | 9.9 | 81.9 MP/s |

**Throughput Notes:**
- Sustained throughput of ~80 MP/s
- Performance is consistent across resolutions

### File Decoding Performance

Time to decode images from file (FileReader + Image element).

| Image Size | JPEG Read | PNG Read | JPEG Decode | PNG Decode |
|------------|-----------|----------|-------------|------------|
| Small (640x480) | 0.034ms | 0.210ms | <0.001ms | <0.001ms |
| Medium (1280x720) | 0.302ms | 0.841ms | <0.001ms | <0.001ms |
| Large (1920x1080) | 0.047ms | 1.172ms | <0.001ms | <0.001ms |
| 4K (3840x2160) | 0.397ms | 7.044ms | <0.001ms | <0.001ms |

**Decoding Performance Notes:**
- PNG files take longer to read due to larger file size
- JPEG decoding is extremely fast (browser optimized)
- Full load pipeline (File → Decoded): <1ms for all sizes in benchmarks
- Actual browser performance may vary based on image complexity

## Running Benchmarks

```bash
# Run all performance tests
bun run benchmarks/performance-test.ts

# Run decode performance tests
bun run benchmarks/decode-performance.ts

# Run unit tests
bun test
```

## API Reference

### Store Exports

The following types and functions are exported from the store:

```tsx
// Types
import { 
  Tool,           // "pencil" | "eraser" | "blur" | "text" | "crop"
  CropState,      // "inactive" | "creating" | "set" | "moving" | "resizing"
  Filter,        // "none" | "grayscale" | "sepia" | "vintage"
  BlurMode,      // "marked" | "unmarked"
  TextObject,
  Point,
  CropRect,
} from '@shopg41/wimp';

// Store
import { useCanvasStore } from '@shopg41/wimp';
```

### Store Actions

```tsx
const store = useCanvasStore.getState();

// Tool
store.setCurrentTool('pencil');

// Drawing
store.setDrawingColor('#ff0000');
store.setBrushSize(10);
store.setFontFamily('Arial');

// Text
store.addTextObject({ id: '1', text: 'Hello', x: 100, y: 100, fontSize: 24, fontFamily: 'Arial', color: '#000' });
store.updateTextObject('1', { text: 'Updated' });
store.removeTextObject('1');

// Adjustments
store.setBrightness(120);
store.setContrast(110);
store.setCurrentFilter('sepia');

// Blur
store.setBlurRadius(25);
store.setBlurMode('marked');

// Crop
store.setCropMode(true);
store.setCropGuide('16:9');
store.resetCrop();

// Zoom/Pan
store.setZoom(2);
store.setPan({ x: 100, y: 50 });
store.zoomIn();
store.zoomOut();
store.autoFit();

// History
store.saveCanvasState();
store.undo();
store.redo();
store.clearHistory();

// File operations
store.loadImageUrl('https://example.com/image.jpg');
store.exportImage('png', 0.92, 1);
```

### Subscribing to Store Changes

```tsx
// Subscribe to specific state changes
useEffect(() => {
  return useCanvasStore.subscribe(
    (state) => state.currentTool,
    (tool) => console.log('Tool changed:', tool)
  );
}, []);

// Get current state
const state = useCanvasStore.getState();
```

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.