# rusty_jpeg
[](https://github.com/remade-with-rust)
[](https://www.mata.network)
[](#licence)
[](https://crates.io/crates/rusty_jpeg)
[](https://docs.rs/rusty_jpeg)
Pure-Rust JPEG / MJPEG **decoder + encoder**. No C, no FFI. Baseline and
progressive DCT, planar YUV in and out, with real quality and
chroma-subsampling control.
This crate is a vendored merge of two upstream pure-Rust projects, carried
forward in-tree as one codec:
| `decode` | [`jpeg-decoder`](https://github.com/image-rs/jpeg-decoder) 0.3.2 | MIT OR Apache-2.0 |
| `encode` | [`jpeg-encoder`](https://github.com/vstroebel/jpeg-encoder) 0.7.0 | (MIT OR Apache-2.0) AND IJG |
See [`NOTICE.md`](NOTICE.md) for attribution and the IJG obligations, and
[`CHANGES.md`](CHANGES.md) for everything changed since vendoring.
> This software is based in part on the work of the Independent JPEG Group.
## Performance vs FFmpeg
Both halves are measured against system **FFmpeg 8.1.2** on the same machine,
one pinned core each, **CPU time** (not wall), best-of-N with the arms
alternated. Content is a 1920×1080 4:2:0 photographic fixture (1/f fractal
noise, q90, 11.8× compression) — synthetic clips misprice JPEG stage shares
badly, so this one is calibrated to real photographic coefficient density.
| **Encode** | **1.19× faster (16%)** | at matched output size |
| **Decode** | **parity** | paired ABBA, N=21: 13/21, z = 1.09 → inside noise |
Both numbers are deliberately unflattering to us where the methodology allows a
choice:
- **Encode** is compared at **matched output size**, not matched `-q:v`. At
equal `-q:v` our files are 41% larger, which is a different operating point
and would price our extra bits as speed. Ours at `-q:v 5` is 27.17 MB against
FFmpeg's 28.04 MB at `-q:v 3` — we are *slightly smaller* and still faster.
Both run fixed Huffman tables in one pass, because our CLI defaults to
optimized tables (two passes) and FFmpeg's does not; comparing defaults would
price our better compression as a speed loss.
- **Decode** is compared on a **byte-identical bitstream** — the reference clip
is stream-copied from the exact fixture and `cmp`-gated before timing, after
an earlier harness was caught feeding FFmpeg a 2.67× smaller file. It is a
paired win-rate with a z-score rather than a ratio of medians, because on this
box the medians and the minima of the *same* arms disagreed by six points.
The honest read is parity; we do not claim the +2.8% the median suggests.
Where the speed comes from, all gated on byte-identical output:
- an **AVX2 forward-DCT + quantize** kernel in the encoder;
- an **AVX2 IDCT that transforms two 8×8 blocks per instruction stream**
(block A in the low 128-bit lane, block B in the high one — every op involved
is lane-independent, so it is byte-identical to running the SSSE3 kernel
twice, which a 64-round oracle test asserts). Worth 7.8% of whole decode;
- a **whole-block DC-only shortcut** ahead of the SIMD dispatch (~31% of blocks
on photographic content have no AC energy);
- **buffered entropy reads with a bulk 8-byte refill**, and **recycled** output
planes and MCU-row coefficient buffers;
- **no rayon.** Upstream `jpeg-decoder` enables it by default; measured here it
is a net loss at every image size — turning it off took 1080p decode from
6.94 to **3.82 ms/frame**.
`Decoder::set_single_threaded(true)` selects the synchronous worker, which uses
**~38% less CPU** than the threaded one; the threaded default is still the
faster choice in wall-clock on a multi-core box (6.97 vs 7.96 ms/frame).
## Why merge them
Holding the two halves in one crate buys three things the separate crates
could not:
1. **The encoder is gated against the decoder as a round-trip oracle** — the
standing correctness gate for every change, running in CI on real content
rather than as a one-off bring-up check.
2. **Shared primitives** — quantization tables, zig-zag order, and the
feature-gated stage profiler are defined once.
3. **The encoder's SIMD is on by default.** Upstream put its AVX2 FDCT and
RGB→YCbCr kernels behind a non-default `simd` feature; consumers who took
the default (as this workspace did) silently ran the scalar path.
## Decode
```rust
use std::io::Cursor;
fn main() -> Result<(), rusty_jpeg::decode::Error> {
let bytes = std::fs::read("in.jpg").expect("read input");
let mut decoder = rusty_jpeg::Decoder::new(Cursor::new(bytes));
let pixels = decoder.decode()?;
let info = decoder.info().expect("populated by decode()");
println!("{}x{}, {:?}", info.width, info.height, info.pixel_format);
println!("{} bytes of pixel data", pixels.len());
Ok(())
}
```
## Encode
```rust
use rusty_jpeg::{ColorType, Encoder};
fn main() -> Result<(), rusty_jpeg::EncodingError> {
// A 2x2 RGB image: red, green, blue, white.
let pixels = [255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255];
let mut jpeg = Vec::new();
let encoder = Encoder::new(&mut jpeg, 90); // quality 0-100
encoder.encode(&pixels, 2, 2, ColorType::Rgb)?;
std::fs::write("out.jpg", jpeg).expect("write output");
Ok(())
}
```
Chroma subsampling (`SamplingFactor`) and quantization table selection
(`QuantizationTableType`) are set on the `Encoder` before `encode`.
## Features
| `std` | yes | Standard library. |
| `simd` | yes | Encoder AVX2 FDCT + quantize + colour conversion. |
| `rayon` | **no** | Decoder work-stealing threading. Off because it measured *slower* at every image size (see above); kept so the measurement is cheap to repeat. |
| `platform_independent` | no | Decoder: drop arch-specific code, `forbid(unsafe_code)`. |
| `benchmark` | no | Expose internal kernels for A/B oracle tests. |
| `profile` | no | Feature-gated stage profiler; zero-cost when off. |
| `counters` | no | Deterministic event counters (symbols, refills, LUT hits). Separate from `profile` on purpose: enabling both at once perturbed cycle counts 3×. |
## Part of Remade With Rust
This crate is the standalone JPEG engine of
**[remade_ffmpeg_rs](https://github.com/Remade-With-Rust/remade_ffmpeg_rs)** — a
ground-up, permissively-licensed Rust rebuild of FFmpeg: a drop-in
`ffmpeg`/`ffprobe` CLI on pure-Rust codecs, with no copyleft. Also check out our
sister project **[FFAI](https://github.com/Remade-With-Rust/FFAI)** — media for
an AI-first world — and the rest of
**[github.com/remade-with-rust](https://github.com/remade-with-rust)**, including
the sibling codec crates
[`rusty_h264`](https://crates.io/crates/rusty_h264),
[`rusty_vp9`](https://crates.io/crates/rusty_vp9),
[`rusty_mp3`](https://crates.io/crates/rusty_mp3),
[`rusty_aac`](https://crates.io/crates/rusty_aac),
[`rusty-opus`](https://crates.io/crates/rusty-opus),
[`rusty_vorbis`](https://crates.io/crates/rusty_vorbis), and the
[rusty-av1-toolkit](https://github.com/Remade-With-Rust/rusty-av1-toolkit) forks.
## About Mata Network
[Mata Network](https://www.mata.network) builds sovereign, self-hostable
infrastructure. **Remade With Rust** is our open-source home for the
permissively-licensed building blocks that work depends on.
## Licence
`(MIT OR Apache-2.0) AND IJG` — the IJG clause attaches to the forward-DCT
files inherited from the encoder. See [`NOTICE.md`](NOTICE.md).