All formats

zune-jpeg

LanguageRust (works in no_std with alloc)
LicenseMIT, Apache-2.0 or Zlib
Repositorygithub.com/etemesi254/zune-image
Documentationdocs.rs/zune-jpeg
Depends onzune-core
Read / writeyes / no; baseline and progressive, grayscale, YCbCr, CMYK and YCCK; raw Exif, XMP, IPTC, ICC

zune-jpeg is a JPEG decoder written in Rust with SSE/AVX2 and NEON code paths. Its speed is close to libjpeg-turbo, and it’s the JPEG decoder of the image crate since version 0.25. It decodes to RGB, RGBA, BGR, BGRA, grayscale (Luma) or raw YCbCr/CMYK, and gives access to Exif, XMP, IPTC and ICC data as raw bytes.

It only decodes. It doesn’t apply the Exif orientation and doesn’t resize. Version 0.5 changed the input API: the decoder takes any reader that implements ZByteReaderTrait (a BufReader<File>, std::io::Cursor, or ZCursor over a byte slice) instead of a &[u8].

Installation#

Shell
cargo add zune-jpeg zune-core

Basic usage: decode a JPEG to RGB pixels#

Rust
use std::fs::File;
use std::io::BufReader;
use zune_jpeg::JpegDecoder;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let file = BufReader::new(File::open("in.jpg")?);
    let mut decoder = JpegDecoder::new(file);
    let pixels: Vec<u8> = decoder.decode()?; // RGB by default, 3 bytes per pixel

    let (width, height) = decoder.dimensions().unwrap();
    println!("{width} x {height}, {} bytes", pixels.len());
    Ok(())
}

How to decode to RGBA or grayscale#

Rust
use zune_core::bytestream::ZCursor;
use zune_core::colorspace::ColorSpace;
use zune_core::options::DecoderOptions;
use zune_jpeg::JpegDecoder;

let data = std::fs::read("in.jpg")?;
let options = DecoderOptions::default().jpeg_set_out_colorspace(ColorSpace::RGBA); // or ColorSpace::Luma
let mut decoder = JpegDecoder::new_with_options(ZCursor::new(&data), options);
let rgba = decoder.decode()?;

How to get JPEG width and height without decoding#

decode_headers parses markers up to the first scan:

Rust
use zune_core::bytestream::ZCursor;
use zune_jpeg::JpegDecoder;

let data = std::fs::read("in.jpg")?;
let mut decoder = JpegDecoder::new(ZCursor::new(&data));
decoder.decode_headers()?;
let info = decoder.info().unwrap();
println!("{} x {}, {} components, {:?}", info.width, info.height, info.components, info.sof);

info.sof tells baseline from progressive.

How to read the raw Exif and ICC profile#

Rust
decoder.decode_headers()?;
if let Some(exif) = decoder.exif() {
    println!("Exif: {} bytes", exif.len());  // parse with the kamadak-exif crate
}
if let Some(icc) = decoder.icc_profile() {
    println!("ICC profile: {} bytes", icc.len());
}

How to limit image size for untrusted files#

Rust
let options = DecoderOptions::default()
    .set_max_width(10_000)
    .set_max_height(10_000)
    .jpeg_set_max_scans(100)      // reject progressive files with absurd scan counts
    .set_strict_mode(true);       // fail on corrupt data instead of best-effort output
let mut decoder = JpegDecoder::new_with_options(ZCursor::new(&data), options);

Tips#