All formats

image (Rust)

LanguageRust
LicenseMIT or Apache-2.0
Repositorygithub.com/image-rs/image
Documentationdocs.rs/image, codecs::gif
Depends onthe gif crate, pure Rust
Read / writeyes / yes; animation (composited frames), loop count, transparency

image reads GIFs with the gif crate. image::open("x.gif") returns the first frame; GifDecoder with the AnimationDecoder trait returns all frames as image::Frame values: full-canvas RGBA buffers with the disposal methods applied, plus each frame’s Delay. GifEncoder writes frames back, quantizing RGBA with NeuQuant, and sets the loop count with set_repeat.

This is the easy way to process animations in Rust: every frame is a complete RgbaImage you can resize or edit with imageops. It uses more memory than the streaming gif crate because whole frames are kept.

Installation#

Shell
cargo add image

Basic usage: read all frames and delays#

Rust
use image::codecs::gif::GifDecoder;
use image::AnimationDecoder;
use std::fs::File;
use std::io::BufReader;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let decoder = GifDecoder::new(BufReader::new(File::open("anim.gif")?))?;
    let frames = decoder.into_frames().collect_frames()?;
    for (i, frame) in frames.iter().enumerate() {
        let (num, den) = frame.delay().numer_denom_ms();
        let buf = frame.buffer();
        println!("frame {i}: {}x{}, {} ms", buf.width(), buf.height(), num / den);
    }
    Ok(())
}

How to resize an animated GIF#

Rust
use image::codecs::gif::{GifDecoder, GifEncoder, Repeat};
use image::imageops::{self, FilterType};
use image::{AnimationDecoder, Frame};
use std::fs::File;
use std::io::BufReader;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let decoder = GifDecoder::new(BufReader::new(File::open("anim.gif")?))?;
    let frames = decoder.into_frames().collect_frames()?;

    let resized: Vec<Frame> = frames
        .iter()
        .map(|f| {
            let buf = f.buffer();
            let small = imageops::resize(buf, buf.width() / 2, buf.height() / 2, FilterType::Lanczos3);
            Frame::from_parts(small, 0, 0, f.delay())
        })
        .collect();

    let mut encoder = GifEncoder::new(File::create("half.gif")?);
    encoder.set_repeat(Repeat::Infinite)?;
    encoder.encode_frames(resized)?;
    Ok(())
}

How to create an animated GIF#

Rust
use image::codecs::gif::{GifEncoder, Repeat};
use image::{Delay, Frame, Rgba, RgbaImage};
use std::fs::File;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut encoder = GifEncoder::new_with_speed(File::create("anim.gif")?, 10);
    encoder.set_repeat(Repeat::Infinite)?;
    for i in 0..20u32 {
        let mut img = RgbaImage::from_pixel(100, 100, Rgba([255, 255, 255, 255]));
        for x in 0..100 {
            img.put_pixel(x, i * 5, Rgba([255, 0, 0, 255]));
        }
        encoder.encode_frame(Frame::from_parts(img, 0, 0, Delay::from_numer_denom_ms(50, 1)))?;
    }
    Ok(())
}

new_with_speed takes 1 (best quality, slowest) to 30 (fastest) for the quantizer.

How to convert the first frame of a GIF to PNG#

Rust
let img = image::open("anim.gif")?;
img.save("first.png")?;

Tips#