# jpegli-rs
[](https://crates.io/crates/jpegli-rs)
[](https://docs.rs/jpegli-rs)
[](https://github.com/imazen/jpegli-rs/actions/workflows/ci.yml)
[](https://codecov.io/gh/imazen/jpegli-rs)
[](LICENSE)
Pure Rust implementation of **jpegli** - Google's improved JPEG encoder/decoder from the JPEG XL project.
## Features
- **Pure Rust** - No C/C++ dependencies required
- **Perceptual optimization** - Uses adaptive quantization for better visual quality
- **Backward compatible** - Produces standard JPEG files readable by any decoder
- **SIMD accelerated** - Uses `wide` crate for portable SIMD
- **Color management** - Optional ICC profile support via lcms2 or moxcms
## What is jpegli?
jpegli is Google's improved JPEG encoder that produces smaller files at the same visual quality,
or better quality at the same file size. It achieves this through:
- **Adaptive quantization** - Content-aware bit allocation
- **Improved quantization tables** - Better than standard IJG libjpeg tables
- **XYB color space** (optional) - Perceptually optimized color representation
- **Smart zero-biasing** - Intelligent coefficient rounding
## Usage
```rust
use jpegli::{Encoder, Quality, PixelFormat};
// Encode RGB image data to JPEG
let jpeg_data = Encoder::new()
.width(800)
.height(600)
.pixel_format(PixelFormat::Rgb)
.quality(Quality::default()) // Q90
.encode(&rgb_pixels)?;
// Decode JPEG to RGB
let decoded = jpegli::Decoder::new().decode(&jpeg_data)?;
println!("{}x{}", decoded.width, decoded.height);
let rgb_pixels: &[u8] = &decoded.data;
```
## Feature Flags
- `simd` (default) - Enable SIMD acceleration
- `cms-lcms2` - Use lcms2 for color management (C dependency)
- `cms-moxcms` - Use moxcms for color management (pure Rust)
## Encoder Status
The encoder is feature-complete and production-ready:
| Baseline JPEG | ✅ Working |
| Progressive JPEG | ✅ Working (levels 0-2) |
| Adaptive quantization | ✅ Matches C++ jpegli |
| Huffman optimization | ✅ Working |
| 4:4:4 / 4:2:0 / 4:2:2 / 4:4:0 subsampling | ✅ Working |
| XYB color space | ✅ Working (with ICC) |
| Grayscale | ✅ Working |
**Encoder Performance**: 30-55 MP/s (varies by image complexity and quality setting)
## Decoder Status
The decoder is functional with 12-bit internal precision (matching C++ jpegli):
| Baseline JPEG | ✅ Working |
| Progressive JPEG | ✅ Working |
| All subsampling modes | ✅ Working |
| ICC profile extraction | ✅ Working |
| XYB decoding (with CMS) | ✅ Working |
| f32 output format | ✅ Working |
**Decoder Performance** (1024x768 image):
| zune-jpeg | 392 MP/s | Integer IDCT, AVX2/NEON |
| jpeg-decoder | 120 MP/s | Integer IDCT |
| **jpegli-rs** | **47 MP/s** | f32 IDCT (12-bit precision) |
The decoder is slower than alternatives because it uses a float pipeline for 12-bit precision,
matching C++ jpegli's design. See [Future Goals](#future-goals) for planned optimizations.
## Encoder Parity with C++ jpegli
Tested on CLIC2025 + Kodak datasets (56 images × 20 quality levels = 1,120 encodings per encoder):
### Overall Results
| Avg file size | 247,434 bytes | 247,388 bytes | **+0.02%** |
| Avg DSSIM | 0.00234 | 0.00234 | identical |
| Avg SSIMULACRA2 | 73.44 | 73.44 | identical |
| Avg encode time | 37.1 ms | 42.9 ms | **1.15x faster** |
### Performance by Corpus
| Kodak | 24 | 0.4 MP | jpegli-rs | 74,791 | 8.7 ms | **45.2 MP/s** |
| Kodak | 24 | 0.4 MP | cjpegli | 74,783 | 13.2 ms | 29.8 MP/s |
| CLIC2025 | 32 | 2.8 MP | jpegli-rs | 376,916 | 58.5 ms | **47.4 MP/s** |
| CLIC2025 | 32 | 2.8 MP | cjpegli | 376,842 | 65.2 ms | 42.5 MP/s |
jpegli-rs produces **byte-for-byte nearly identical** output to C++ jpegli with **15-50% faster** encoding.
### Quality by Quality Level
| Q30 | 0.0055 | 56.2 | 0.91 |
| Q50 | 0.0039 | 63.7 | 1.11 |
| Q70 | 0.0023 | 72.3 | 1.51 |
| Q80 | 0.0015 | 77.3 | 1.91 |
| Q90 | 0.0007 | 84.3 | 2.94 |
| Q95 | 0.0003 | 88.7 | 4.37 |
Lower DSSIM is better. Higher SSIMULACRA2 is better.
## Development
### Running FFI Comparison Tests
To verify the Rust implementation matches the C++ original:
```bash
# Linux/macOS
./internal/setup-ffi-tests.sh
# Windows
.\internal\setup-ffi-tests.ps1
```
This requires CMake, a C++ compiler, and ~10 minutes for the initial C++ build.
See [internal/README.md](../internal/README.md) for details.
### Running Benchmarks
```bash
# Decoder performance comparison
cargo run --release --example decode_benchmark
# Encoder benchmark vs C++ cjpegli (requires cjpegli in PATH)
cargo run --release --example encode_benchmark
# Encoder quality comparison (jpegli vs mozjpeg)
cargo run --release --example pareto_comparison
```
## Future Goals
### Decoder Optimization (Target: 100+ MP/s)
The current decoder uses f32 arithmetic for 12-bit precision. To reach competitive speeds:
- [ ] Optional integer IDCT path for u8 output
- [ ] Platform-specific SIMD (AVX2, NEON) for hot paths
- [ ] Optimized bit reader with bulk byte loading
- [ ] Multi-threaded decoding for large images
### Encoder Improvements
- [ ] Parallel block processing
- [ ] Memory-efficient streaming API
- [ ] Further entropy coding optimizations
## License
**AGPL-3.0-or-later**
A commercial license is available from https://imageresizing.net/pricing
The original jpegli from libjxl is BSD-3-Clause licensed.
This Rust implementation is an independent port, not a derivative work.
## Acknowledgments
This is a Rust port of [jpegli](https://github.com/libjxl/libjxl/tree/main/lib/jpegli)
from the JPEG XL project by Google.
## AI-Generated Code Notice
This crate was developed with significant assistance from Claude (Anthropic).
While extensively tested against the C++ reference implementation with 170+ tests,
not all code paths have been manually reviewed.
Before production use in critical applications:
- Review code paths relevant to your use case
- Run your own validation tests
- Report any issues at https://github.com/imazen/jpegli-rs/issues