1 unstable release
Uses new Rust 2024
| 0.1.0 | Feb 8, 2026 |
|---|
#1530 in Images
69KB
1.5K
SLoC
zenpnm
PNM/PAM/PFM image format decoder and encoder, with optional basic BMP support.
no_std compatible (with alloc), forbid(unsafe_code), panic-free. All arithmetic is checked. Suitable for server and embedded use.
Formats
PNM family (always available):
- P5 (PGM binary) — grayscale, 8-bit and 16-bit
- P6 (PPM binary) — RGB, 8-bit and 16-bit
- P7 (PAM) — grayscale, RGB, RGBA, 8-bit and 16-bit
- PFM — floating-point grayscale and RGB (32-bit per channel)
Basic BMP (basic-bmp feature, opt-in):
- Uncompressed 24-bit RGB and 32-bit RGBA only
- Not auto-detected — you call
decode_bmp/encode_bmpexplicitly - No RLE, indexed color, or advanced headers
Zero-copy decoding
PNM files with maxval=255 (the common case) decode to a borrowed slice into your input buffer. No allocation, no copy. Formats requiring transformation (16-bit, non-255 maxval, PFM, BMP) allocate.
use zenpnm::*;
use enough::Unstoppable;
let pixels = vec![255u8, 0, 0, 0, 255, 0]; // 2 RGB pixels
let encoded = encode_ppm(&pixels, 2, 1, PixelLayout::Rgb8, Unstoppable)?;
let decoded = decode(&encoded, Unstoppable)?;
assert!(decoded.is_borrowed()); // zero-copy
assert_eq!(decoded.pixels(), &pixels[..]);
# Ok::<(), PnmError>(())
Cooperative cancellation
Every function takes a stop parameter implementing enough::Stop. Pass Unstoppable when you don't need cancellation. For server use, pass a token that checks a shutdown flag — decode/encode will bail out promptly via PnmError::Cancelled.
Resource limits
use zenpnm::*;
use enough::Unstoppable;
let limits = Limits {
max_width: Some(4096),
max_height: Some(4096),
max_pixels: Some(16_000_000),
max_memory_bytes: Some(64 * 1024 * 1024),
..Default::default()
};
# let data = encode_ppm(&[0u8; 3], 1, 1, PixelLayout::Rgb8, Unstoppable).unwrap();
let decoded = decode_with_limits(&data, &limits, Unstoppable)?;
# Ok::<(), PnmError>(())
API
All public functions are flat, one-shot calls at crate root.
Decode:
decode(data, stop)— auto-detect PNM format from magic bytesdecode_with_limits(data, limits, stop)— same, with resource limitsdecode_bmp(data, stop)— explicit BMP decode (requiresbasic-bmpfeature)decode_bmp_with_limits(data, limits, stop)
Encode:
encode_ppm(pixels, w, h, layout, stop)— P6 binary RGBencode_pgm(pixels, w, h, layout, stop)— P5 binary grayscaleencode_pam(pixels, w, h, layout, stop)— P7, any supported layoutencode_pfm(pixels, w, h, layout, stop)— PFM floating-pointencode_bmp(pixels, w, h, layout, stop)— 24-bit BMP (requiresbasic-bmp)encode_bmp_rgba(pixels, w, h, layout, stop)— 32-bit BMP with alpha
Types:
DecodeOutput<'a>— decoded image with.pixels(),.width,.height,.layout,.is_borrowed(),.into_owned()PixelLayout— pixel format enum (Gray8, Gray16, Rgb8, Rgba8, Bgr8, Bgra8, GrayF32, RgbF32)Limits— resource limits (max width/height/pixels/memory)PnmError— error type,#[non_exhaustive]
Features
[dependencies]
zenpnm = "0.1" # PNM (always included)
zenpnm = { version = "0.1", features = ["basic-bmp"] } # + BMP
zenpnm = { version = "0.1", features = ["rgb"] } # + typed pixel API
zenpnm = { version = "0.1", features = ["imgref"] } # + ImgVec/ImgRef (implies rgb)
zenpnm = { version = "0.1", features = ["all"] } # everything
Credits
PNM implementation draws from zune-ppm by Caleb Etemesi (MIT/Apache-2.0/Zlib licensed).
AI-Generated Code Notice
Developed with Claude (Anthropic). Not all code manually reviewed. Review critical paths before production use.
License
MIT OR Apache-2.0
Dependencies
~165–630KB
~14K SLoC