image (Rust)
| Language | Rust |
|---|---|
| License | MIT or Apache-2.0 |
| Repository | github.com/image-rs/image |
| Documentation | docs.rs/image, codecs::gif |
| Depends on | the gif crate, pure Rust |
| Read / write | yes / 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#
- Use
into_frames()as an iterator instead ofcollect_frames()to process long animations frame by frame. Delay::numer_denom_msreturns a fraction: GIF delays are multiples of 10 ms, sonum / denis exact.- Build with
--release; GIF encoding in debug builds is very slow.