Skip to content
/ zengif Public

Server-side GIF codec with zero-trust design, memory bounds, streaming, and full animation transparency support

License

Unknown, MIT licenses found

Licenses found

Unknown
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

imazen/zengif

Repository files navigation

zengif

CI Crates.io Documentation codecov License

Server-side GIF codec with zero-trust design, memory bounds, streaming, and full animation transparency support.

Why zengif?

zengif combines streaming GIF codec capabilities with server-side production features:

Feature gif + gif-dispose zengif
Streaming decode
Disposal + transparency
Memory limits
Cooperative cancellation
Error tracing (file:line)
Round-trip metadata
High-quality encode

The gif crate provides excellent low-level GIF parsing with memory limits. gif-dispose adds disposal method handling. zengif wraps these and adds: cancellation support, error tracing with file:line info, and a unified API for encode/decode with metadata preservation.

Features

  • Streaming decode/encode - Process GIFs without loading entire file into memory
  • Complete animation support - All disposal methods (Keep, Background, Previous) + transparency working together
  • Memory bounded - Configurable limits, reject oversized inputs before allocating
  • Production ready - Error tracing via whereat, cancellation via enough
  • Zero-trust design - Validate headers, bounds-check frames, limit decompression ratio
  • Round-trip fidelity - Frame timing, loop count, palette, and disposal methods preserved
  • High-quality encoding - Optional imagequant integration for small file sizes

Quick Start

Decoding

use zengif::{Decoder, Limits, Stats};
use enough::Unstoppable;

let limits = Limits::default()
    .max_dimensions(4096, 4096)
    .max_frame_count(1000);

let stats = Stats::new();
let cursor = std::io::Cursor::new(gif_data);

let mut decoder = Decoder::new(cursor, limits, &stats, Unstoppable)?;

while let Some(frame) = decoder.next_frame()? {
    // frame.pixels: Vec<Rgba> - composited RGBA with disposal applied
    // frame.delay: u16 - delay in centiseconds
    // frame.index: usize - frame number
}

println!("Peak buffer usage: {} bytes", stats.peak());

Encoding

use zengif::{Encoder, EncoderConfig, FrameInput, Limits, Repeat, Rgba};
use enough::Unstoppable;

let config = EncoderConfig::new(width, height).repeat(Repeat::Infinite);
let limits = Limits::default();

let mut output = Vec::new();
let mut encoder = Encoder::new(&mut output, config, limits, Unstoppable)?;

for frame in source_frames {
    encoder.add_frame(frame)?;
}

encoder.finish()?;

Round-Trip with Metadata

use zengif::{Decoder, Encoder, Limits, Stats};
use enough::Unstoppable;

let stats = Stats::new();
let mut decoder = Decoder::new(reader, Limits::default(), &stats, Unstoppable)?;

let metadata = decoder.metadata().clone();
let mut encoder = Encoder::from_metadata(writer, &metadata, Limits::default(), Unstoppable)?;

while let Some(frame) = decoder.next_frame()? {
    // Re-encode the composed frame
    let input = FrameInput::new(frame.width, frame.height, frame.delay, frame.pixels);
    encoder.add_frame(input)?;
}

encoder.finish()?;

With Cancellation

use almost_enough::Stopper;
use zengif::{Decoder, Limits, Stats};

let stop = Stopper::new();
let stop_clone = stop.clone();

// In another thread/task:
stop_clone.cancel();

// Decoder will return GifError::Cancelled
let stats = Stats::new();
let decoder = Decoder::new(reader, Limits::default(), &stats, stop)?;

Memory Limits

Protect against malicious inputs:

use zengif::Limits;

let limits = Limits::default()
    .max_dimensions(8192, 8192)           // Max canvas size
    .max_total_pixels(67_108_864)         // Max 64M pixels per frame
    .max_frame_count(10_000)              // Max frames
    .max_file_size(500 * 1024 * 1024)     // 500 MB
    .max_memory(1024 * 1024 * 1024);      // 1 GB peak memory

Error Handling

Errors include location and context for debugging:

Error: InvalidFrameBounds { frame_left: 0, frame_top: 0, frame_width: 5000,
                            frame_height: 5000, canvas_width: 100, canvas_height: 100 }
   at src/decode/frame.rs:142:9
      ╰─ validating frame 3
   at src/decode/mod.rs:89:5
      ╰─ in decode_frame

Feature Flags

Feature Default Description
std Enable std library (disable for no_std+alloc)
simd SIMD acceleration via wide/multiversed
rgb-interop Interop with the rgb crate
imgref-interop Interop with the imgref crate

no_std / WASM Support

zengif supports no_std environments with alloc. Disable the default std feature:

[dependencies]
zengif = { version = "0.3", default-features = false }

With std (default): Full codec - decoder, encoder, quantizers, heuristics.

Without std: Core types only - GifError, Limits, Stats, Rgba, Palette, ComposedFrame, Screen, Disposal. Useful for WASM or embedded where you process frames but use a different codec.

Verified targets: wasm32-unknown-unknown (144KB release build with decoder).

Color Quantization Backends

Choose one or more quantization backends for high-quality GIF encoding:

Feature License Quality Speed Notes
imagequant AGPL-3.0 Best Medium Recommended - best quality AND smallest files (LZW-aware dithering), commercial license
quantizr MIT Good Fast Best MIT-licensed option
color_quant MIT Good Fastest High-throughput servers
exoquant-deprecated MIT Good Slow Deprecated - use quantizr instead

Without any quantization feature, zengif is purely MIT/Apache-2.0 licensed.

Performance

Benchmarks on AMD Ryzen 9 5900X:

Operation Throughput
Decode (composited) ~150 MB/s
Encode (quantized) ~40 MB/s
Encode (pre-indexed) ~200 MB/s

AI-Generated Code Notice

Developed with Claude (Anthropic). Not all code manually reviewed. Review critical paths before production use.

License

Licensed under either of:

at your option.

Dependency Licensing: imagequant (AGPL)

The optional imagequant feature uses libimagequant by Kornel Lesiński, which is licensed under AGPL-3.0. For closed-source projects, please purchase a commercial license — it's reasonably priced and supports continued development of excellent image optimization tools.

Alternative MIT-licensed quantizers: quantizr or color_quant for fully permissive licensing (slightly lower quality).

Without any quantization feature (the default), zengif has no AGPL dependencies and is purely MIT/Apache-2.0 licensed.

About

Server-side GIF codec with zero-trust design, memory bounds, streaming, and full animation transparency support

Resources

License

Unknown, MIT licenses found

Licenses found

Unknown
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

No packages published

Languages