https://github.com/howbizarre/add-stamp
A Nuxt 4 application that allows users to apply watermarks to bunch of images using WebAssembly (WASM) for high-performance image processing. And use Cloudlfare Worker as BaaS.
https://github.com/howbizarre/add-stamp
baas batch-processing browser cloudflare-workers image-processing images nuxt nuxt4 rust stamp tailwindcss typescript vue vue3 wasn watermark webassembly wrangler
Last synced: about 2 months ago
JSON representation
A Nuxt 4 application that allows users to apply watermarks to bunch of images using WebAssembly (WASM) for high-performance image processing. And use Cloudlfare Worker as BaaS.
- Host: GitHub
- URL: https://github.com/howbizarre/add-stamp
- Owner: howbizarre
- License: other
- Created: 2025-07-22T09:18:04.000Z (10 months ago)
- Default Branch: master
- Last Pushed: 2025-08-14T11:56:13.000Z (10 months ago)
- Last Synced: 2025-08-14T12:30:30.139Z (10 months ago)
- Topics: baas, batch-processing, browser, cloudflare-workers, image-processing, images, nuxt, nuxt4, rust, stamp, tailwindcss, typescript, vue, vue3, wasn, watermark, webassembly, wrangler
- Language: Vue
- Homepage: https://stamp.bizarre.how/
- Size: 1.11 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README-WASM.md
- License: LICENSE.md
Awesome Lists containing this project
README
# WASM Image Processing Module
This document covers the WebAssembly (WASM) module implementation for high-performance image watermarking using Rust. The WASM module provides the core image processing capabilities for the Image Watermarking App.
## Overview
The WASM module is built with Rust and compiled to WebAssembly to provide near-native performance for image processing operations in the browser. It handles:
- **Image Loading**: Support for PNG, JPEG, and WebP formats
- **Watermark Processing**: PNG stamp application with transparency
- **Text Rendering**: Font-based text watermarks with custom fonts
- **Image Scaling**: Smart scaling algorithms with aspect ratio preservation
- **Export**: WebP output with configurable quality settings
## Rust Dependencies
The module uses several high-quality Rust crates for image processing:
```toml
[dependencies]
wasm-bindgen = "0.2" # WebAssembly bindings
image = "0.24" # Core image processing
imageproc = "0.23" # Advanced image operations
rusttype = "0.9" # Font rendering
console_error_panic_hook = "0.1" # Error handling
web-sys = "0.3" # Web API bindings
```
### Key Features
- **`image` crate**: Handles multiple image formats (PNG, JPEG, WebP)
- **`imageproc`**: Provides advanced drawing and text rendering
- **`rusttype`**: TrueType font rendering for text watermarks
- **`wasm-bindgen`**: Seamless Rust-JavaScript interoperability
## Prerequisites for WASM Development
### Required Tools
1. **Rust Toolchain** (stable version recommended)
```bash
# Install Rust using rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
```
2. **wasm-pack** (WebAssembly build tool)
```bash
# Install wasm-pack
cargo install wasm-pack
```
3. **wasm32-unknown-unknown target**
```bash
# Add WebAssembly target
rustup target add wasm32-unknown-unknown
```
### Verifying Installation
```bash
# Check Rust version
rustc --version
# Check Cargo version
cargo --version
# Check wasm-pack version
wasm-pack --version
# Verify WebAssembly target
rustup target list --installed | grep wasm32
```
## Building the WASM Module
### Quick Build Commands
```bash
# Build WASM module for web target
npm run build:wasm
# Clean previous build artifacts
npm run clean:wasm
# Copy built files to public directory
npm run copy:wasm
```
### Manual Build Process
```bash
# Navigate to WASM directory
cd wasm
# Build with wasm-pack (web target)
wasm-pack build --target web --out-dir pkg --out-name image_stamper
# Copy to public directory (Windows)
xcopy /Y /E pkg\* ..\public\wasm\
# Copy to public directory (Unix/Linux/macOS)
cp -r pkg/* ../public/wasm/
```
### Build Configuration
The `Cargo.toml` file is configured for WASM compilation:
```toml
[package]
name = "image-stamper"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"] # Dynamic library for WASM
[dependencies]
# Core WASM bindings
wasm-bindgen = "0.2"
# Image processing with optimized features
image = {
version = "0.24",
default-features = false,
features = ["png", "jpeg", "webp"]
}
# Advanced image operations
imageproc = "0.23"
# Font rendering
rusttype = "0.9"
# Error handling
console_error_panic_hook = "0.1"
# Web APIs
web-sys = { version = "0.3", features = ["console", "File", "FileReader", "Blob"] }
```
## WASM Module Architecture
### Core Components
#### ImageStamper Struct
The main WASM interface provides methods for image processing:
```rust
#[wasm_bindgen]
pub struct ImageStamper {
stamp_data: Vec, // Cached stamp image data
stamp_width: u32, // Stamp dimensions
stamp_height: u32,
}
```
#### Key Methods
1. **`new()`** - Initialize the ImageStamper instance
2. **`set_stamp(stamp_bytes: &[u8])`** - Load and cache watermark image
3. **`process_image(image_bytes: &[u8], opacity: f32)`** - Apply watermark to image
4. **`add_text_watermark(...)`** - Add text-based watermarks
### Processing Pipeline
1. **Input Validation**: Verify image format and dimensions
2. **Image Decoding**: Convert bytes to internal image representation
3. **Stamp Processing**: Scale and position watermark appropriately
4. **Composition**: Blend watermark with original image using opacity
5. **Export**: Encode result as WebP with optimal compression
### Performance Optimizations
- **Memory Pooling**: Reuse allocated buffers for batch processing
- **SIMD Operations**: Leverage vectorized instructions where available
- **Minimal Allocations**: Direct buffer manipulation to reduce GC pressure
- **Streaming**: Process large images in chunks to manage memory usage
## Integration with JavaScript
### Loading the WASM Module
```javascript
import init, { ImageStamper } from './wasm/image_stamper.js';
// Initialize WASM module
await init();
// Create processor instance
const stamper = new ImageStamper();
```
### Processing Images
```javascript
// Set watermark once
await stamper.set_stamp(stampImageBytes);
// Process multiple images
for (const imageFile of images) {
const imageBytes = new Uint8Array(await imageFile.arrayBuffer());
const result = stamper.process_image(imageBytes, 0.5); // 50% opacity
// result is Uint8Array containing WebP data
}
```
## WASM Technical Implementation
### Image Processing Algorithms
#### Watermark Scaling Strategy
The module uses a "contain" scaling approach that:
- Calculates optimal scale to fit watermark within image bounds
- Maintains aspect ratio to prevent distortion
- Applies padding to ensure proper positioning
- Centers the watermark for balanced composition
#### Opacity Blending
```rust
// Alpha blending formula implementation
let alpha = stamp_alpha * opacity;
let inv_alpha = 1.0 - alpha;
result_pixel = (original_pixel * inv_alpha) + (stamp_pixel * alpha);
```
#### Memory Management
- **Stack Allocation**: Use stack memory for small buffers
- **Heap Optimization**: Minimize heap allocations in hot paths
- **Buffer Reuse**: Cache intermediate results for batch operations
- **Zero-Copy**: Direct memory access where possible
### Font System Integration
The module includes embedded Ubuntu font for consistent text rendering:
```rust
// Load embedded font data
const UBUNTU_FONT: &[u8] = include_bytes!("Ubuntu-M.ttf");
// Dynamic font sizing based on image dimensions
let font_size = (image_width.min(image_height) as f32 * 0.05).max(12.0);
```
### Error Handling
Comprehensive error handling for production use:
```rust
#[wasm_bindgen]
pub fn process_image(&self, image_bytes: &[u8], opacity: f32) -> Result, JsValue> {
// Input validation
if image_bytes.is_empty() {
return Err(JsValue::from_str("Empty image data"));
}
if !(0.0..=1.0).contains(&opacity) {
return Err(JsValue::from_str("Opacity must be between 0.0 and 1.0"));
}
// Processing with error propagation
let img = image::load_from_memory(image_bytes)
.map_err(|e| JsValue::from_str(&format!("Image decode error: {}", e)))?;
// ... processing logic
}
```
## WASM Module File Structure
```
wasm/
├── Cargo.toml # Rust package configuration
├── Cargo.lock # Dependency lock file
├── Ubuntu-M.ttf # Embedded font for text watermarks
├── src/
│ └── lib.rs # Main WASM module implementation
├── pkg/ # Generated WASM build output
│ ├── image_stamper.js # JavaScript bindings
│ ├── image_stamper.d.ts # TypeScript definitions
│ ├── image_stamper_bg.wasm # Compiled WebAssembly binary
│ ├── image_stamper_bg.wasm.d.ts # WASM type definitions
│ └── package.json # NPM package metadata
└── target/ # Rust build artifacts
├── wasm32-unknown-unknown/ # WebAssembly target build
└── debug/ # Debug build artifacts
```
### Generated Files
After building, the following files are copied to `public/wasm/`:
- **`image_stamper.js`** - ES module with WASM loader and bindings
- **`image_stamper.d.ts`** - TypeScript type definitions
- **`image_stamper_bg.wasm`** - Compiled WebAssembly binary
- **`image_stamper_bg.wasm.d.ts`** - Additional type definitions
- **`package.json`** - Package metadata for the generated module
## Development Workflow
### 1. Modify Rust Code
Edit files in `wasm/src/` directory:
```bash
# Main implementation
vim wasm/src/lib.rs
```
### 2. Build and Test
```bash
# Build WASM module
npm run build:wasm
# Test in development
npm run dev
```
### 3. Debug WASM Issues
```bash
# Enable debug logging in Rust
console_log!("Debug message: {}", value);
# Check browser console for WASM errors
# Use browser dev tools to inspect WASM module
```
### 4. Performance Profiling
- Use browser dev tools Performance tab
- Monitor WASM memory usage
- Profile JavaScript ↔ WASM boundary crossings
- Optimize hot paths identified in profiling
## Troubleshooting WASM Issues
### Common Build Problems
#### 1. wasm-pack Not Found
```bash
# Error: command not found: wasm-pack
# Solution: Install wasm-pack
cargo install wasm-pack
```
#### 2. Missing WebAssembly Target
```bash
# Error: target 'wasm32-unknown-unknown' not found
# Solution: Add WebAssembly target
rustup target add wasm32-unknown-unknown
```
#### 3. Build Fails with Dependency Errors
```bash
# Clean and rebuild
cargo clean
npm run clean:wasm
npm run build:wasm
```
#### 4. WASM Files Not Found in Public Directory
```bash
# Ensure files are copied correctly
npm run copy:wasm
# Check if files exist
ls public/wasm/
```
### Runtime Issues
#### 1. WASM Module Failed to Load
- Check browser console for detailed error messages
- Verify WASM files are accessible via network tab
- Ensure proper MIME type for `.wasm` files
#### 2. Memory Allocation Errors
```rust
// Add panic hook for better error messages
#[wasm_bindgen]
pub fn init_panic_hook() {
console_error_panic_hook::set_once();
}
```
#### 3. Performance Issues
- Profile using browser dev tools
- Check for excessive memory allocations
- Verify batch processing is working correctly
### Debug Logging
Enable detailed logging in the WASM module:
```rust
// Add debug output
console_log!("Processing image: {}x{}", width, height);
console_log!("Stamp size: {}x{}", stamp_width, stamp_height);
```
### Browser Compatibility
- **Chrome/Edge**: Full support for WASM and File System Access API
- **Firefox**: WASM support, limited File System Access API
- **Safari**: WASM support, no File System Access API
## Performance Benchmarks
Typical processing times on modern hardware:
- **1MP image**: ~50-100ms
- **5MP image**: ~200-400ms
- **10MP image**: ~500-800ms
Memory usage scales linearly with image size:
- **1MP image**: ~4MB peak memory
- **5MP image**: ~20MB peak memory
- **10MP image**: ~40MB peak memory
## Contributing to WASM Module
### Code Style
- Follow Rust standard formatting (`cargo fmt`)
- Use `clippy` for linting (`cargo clippy`)
- Add documentation for public APIs
- Include error handling for all operations
### Testing
```bash
# Run Rust tests
cd wasm
cargo test
# Build and test integration
npm run build:wasm && npm run dev
```
### Adding New Features
1. Implement in `wasm/src/lib.rs`
2. Add appropriate `#[wasm_bindgen]` annotations
3. Update TypeScript definitions if needed
4. Test integration with frontend
5. Update documentation