mozjpeg-oxide
Pure Rust JPEG encoder based on Mozilla's mozjpeg, featuring trellis quantization for optimal compression.
Why mozjpeg-oxide?
| mozjpeg-oxide | C mozjpeg | libjpeg-turbo | |
|---|---|---|---|
| Language | Pure Rust | C | C/asm |
| Memory safety | Compile-time guaranteed | Manual | Manual |
| Trellis quantization | Yes | Yes | No |
| Build complexity | cargo add |
cmake + nasm + C toolchain | cmake + nasm |
Choose mozjpeg-oxide when you want:
- Memory-safe JPEG encoding without C dependencies
- Smaller files than libjpeg-turbo (trellis quantization)
- Simple integration via Cargo
Choose C mozjpeg when you need:
- Smallest possible files at high quality (Q85+)
- Maximum baseline encoding speed (SIMD-optimized entropy coding)
- Established C ABI for FFI
Compression Results vs C mozjpeg
Tested on full Kodak corpus (24 images), trellis + Huffman opt, 4:2:0 subsampling.
Max Compression Mode (Encoder::max_compression())
Progressive mode with optimize_scans=true - each AC scan gets its own optimal Huffman table.
| Quality | Rust vs C | Notes |
|---|---|---|
| Q50 | -0.39% | Rust produces smaller files |
| Q60 | -0.26% | Rust smaller |
| Q70 | -0.38% | Rust smaller |
| Q75 | -0.14% | Rust smaller |
| Q80 | +0.17% | Near-identical |
| Q85 | +0.42% | Near-identical |
| Q90 | +0.97% | Slight gap |
| Q95 | +1.59% | |
| Q97 | +2.14% | |
| Q100 | +1.00% |
All Modes Comparison
| Quality | Baseline | Progressive | Max Compression |
|---|---|---|---|
| Q50 | +0.15% | -1.35% | -0.39% |
| Q60 | +0.47% | -0.81% | -0.26% |
| Q70 | +0.54% | -0.44% | -0.38% |
| Q75 | +0.87% | +0.14% | -0.14% |
| Q80 | +1.34% | +0.84% | +0.17% |
| Q85 | +1.75% | +1.39% | +0.42% |
| Q90 | +2.73% | +2.58% | +0.97% |
| Q95 | +3.87% | +3.61% | +1.59% |
| Q97 | +5.36% | +4.87% | +2.14% |
| Q100 | +3.53% | +2.58% | +1.00% |
Summary:
- Max Compression: Rust matches or beats C at Q50-Q80, within 2.2% at all quality levels
- Progressive: Rust beats C at Q50-Q70, within 5% at all levels
- Baseline: Larger gap due to trellis quantization differences at high quality
Visual quality (SSIMULACRA2, Butteraugli) is virtually identical at all quality levels.
Usage
use ;
// Default: trellis quantization + Huffman optimization
let jpeg = new
.quality
.encode_rgb?;
// Maximum compression: progressive + trellis + deringing
let jpeg = max_compression
.quality
.encode_rgb?;
// Fastest: no optimizations (libjpeg-turbo compatible output)
let jpeg = fastest
.quality
.encode_rgb?;
// Custom configuration
let jpeg = new
.quality
.progressive
.subsampling
.optimize_huffman
.encode_rgb?;
Features
- Trellis quantization - Rate-distortion optimized coefficient selection (AC + DC)
- Progressive JPEG - Multi-scan encoding with spectral selection
- Huffman optimization - 2-pass encoding for optimal entropy coding
- Overshoot deringing - Reduces ringing artifacts at sharp edges
- Chroma subsampling - 4:4:4, 4:2:2, 4:2:0 modes
- Safe Rust -
#![deny(unsafe_code)]with exceptions only for SIMD intrinsics
Performance
Benchmarked on 512x768 image, 20 iterations, release mode:
| Configuration | Rust | C mozjpeg | Ratio |
|---|---|---|---|
| Baseline (huffman opt) | 7.1 ms | 26.8 ms | 3.8x faster |
| Trellis (AC + DC) | 19.7 ms | 25.3 ms | 1.3x faster |
| Progressive + trellis | 20.0 ms | - | - |
Note: C mozjpeg's baseline encoding is typically faster with its hand-optimized SIMD entropy coding. The benchmark numbers above reflect mozjpeg-sys from crates.io which may not have all optimizations enabled.
SIMD Support
mozjpeg-oxide uses multiversion for automatic vectorization by default. Optional hand-written SIMD intrinsics are available:
[]
= { = "0.2", = ["simd-intrinsics"] }
In benchmarks, the difference is minimal (~2%) as multiversion autovectorization works well for DCT and color conversion.
Differences from C mozjpeg
mozjpeg-oxide aims for compatibility with C mozjpeg but has some differences:
| Feature | mozjpeg-oxide | C mozjpeg |
|---|---|---|
| Progressive scan script | Simple 4-scan (or optimize_scans) | 9-scan with successive approximation |
| optimize_scans | Per-scan Huffman tables | Per-scan Huffman tables |
| Trellis EOB optimization | Not implemented | Available (rarely used) |
| Arithmetic coding | Not implemented | Available (rarely used) |
| Grayscale progressive | Not implemented | Available |
Why the file size gap at high quality?
At quality levels above Q85, there's a small gap (1-3%) due to differences in the progressive scan structure:
- C mozjpeg uses a 9-scan successive approximation (SA) script that splits coefficient bits into coarse and fine layers
- mozjpeg-oxide uses a 4-scan script (DC + full AC for each component) with per-scan optimal Huffman tables
With optimize_scans=true (enabled in max_compression()), mozjpeg-oxide matches or beats C mozjpeg at Q50-Q80.
Matching C mozjpeg output exactly
For exact byte-identical output to C mozjpeg, you would need to:
- Use baseline (non-progressive) mode
- Match all encoder settings exactly
- Use the same quantization tables (ImageMagick tables, index 3)
The FFI comparison tests in tests/ffi_comparison.rs verify component-level parity.
Development
Running CI Locally
# Format check
# Clippy lints
# Build
# Unit tests
# Codec comparison tests
# FFI validation tests (requires mozjpeg-sys from crates.io)
Reproduce Benchmarks
# Fetch test corpus (Kodak images, ~15MB)
# Run full corpus comparison
# Run pareto benchmark
Test Coverage
# Install cargo-llvm-cov
# Generate coverage report
# Open report
License
BSD-3-Clause - Same license as the original mozjpeg.
Acknowledgments
Based on Mozilla's mozjpeg, which builds on libjpeg-turbo and the Independent JPEG Group's libjpeg.
AI-Generated Code Notice
This crate was developed with significant assistance from Claude (Anthropic). While the code has been tested against the C mozjpeg reference implementation and passes 248 tests including FFI validation, not all code has been manually reviewed or human-audited.
Before using in production:
- Review critical code paths for your use case
- Run your own validation against expected outputs
- Consider the encoder's test suite coverage for your specific requirements
The FFI comparison tests in tests/ffi_comparison.rs and tests/ffi_validation.rs provide confidence in correctness by comparing outputs against C mozjpeg.