gamut-tiff 0.2.0

TIFF 6.0 (Tagged Image File Format) image encoder and decoder.
Documentation

gamut-tiff

gamut-tiff is a pure-Rust TIFF 6.0 (Tagged Image File Format) image encoder and decoder.

Goals

Part of the gamut workspace, this crate provides TIFF reading and writing that is:

  • Memory-safe on hostile input. #![forbid(unsafe_code)] — TIFF's offset-driven structure is a classic source of parser exploits, so the decoder is built to be robust against malformed IFDs, offset loops, and truncation.
  • Clean-slate from the spec. Implemented directly from the TIFF 6.0 specification (../../references/tiff/tiff6.pdf) rather than wrapping libtiff.
  • Self-contained. TIFF's Image File Directory (IFD) / tag structure is its container, so — unlike gamut-avif/gamut-heic (ISOBMFF) or gamut-webp (RIFF) — this crate needs no separate container crate. It builds only on gamut-color, gamut-dsp, and gamut-bitstream.
  • Permissively licensed, matching the royalty-free TIFF format.

Unlike the video-derived still-image codecs in the workspace, TIFF is natively a still-image format — a good long-term fit for gamut's image-first focus.

Usage

[TiffEncoder] (implementing [gamut_core::EncodeImage] per pixel layout) writes 8-bit grayscale, RGB, palette, and 1-bit bilevel images — uncompressed or PackBits — and [TiffDecoder] (implementing [gamut_core::DecodeImage]) reads them back, both reachable through the umbrella crate's tiff feature:

use gamut_core::Dimensions;
use gamut_tiff::{Compression, TiffEncoder};

let mut tiff = Vec::new();
TiffEncoder::new()
    .with_compression(Compression::PackBits)
    .encode_rgb8(&rgb, Dimensions { width, height }, &mut tiff)
    .expect("encode");

More colour modes and compression schemes are landing incrementally (see Status).

Status

Implemented and conformance-checked against libtiff (issue #107):

  • Structure — byte-order header, IFD/tag read & write, strips and tiles, multi-page documents.
  • Colour modes (8-bit) — grayscale, RGB, RGBA (alpha), palette, CMYK, and 1-bit bilevel.
  • Compression — uncompressed, PackBits, LZW (+ horizontal differencing predictor), and the bilevel CCITT schemes Modified Huffman (Group 3 1-D) and Group 4 (T.6).
  • The decoder is hardened against hostile input (#![forbid(unsafe_code)], a size cap, and a byte-flip fuzz corpus).

Not yet implemented (see STATUS.md): YCbCr (§21), CIE L*a*b* / RGB colorimetry (§20, §23), JPEG-in-TIFF (§22), and smaller items (CCITT Group 3 2-D, planar config, 16-bit/float samples, halftone hints).

Roadmap

The remaining TIFF 6.0 features each land as a follow-up PR that plugs into the same strip/tile pipeline and libtiff oracle: the colour spaces (YCbCr, L*a*b*) need gamut-color conversions matched to libtiff's integer math; JPEG-in-TIFF needs a baseline DCT codec and a libjpeg-enabled oracle build.

Correctness is pinned with a differential oracle against libtiff: gamut-encode → libtiff-decode and libtiff-encode → gamut-decode must agree pixel-for-pixel on every lossless path.

License

Licensed under either of MIT or Apache-2.0 at your option.