ImageSharp
| Language | C# (.NET 8 or later) |
|---|---|
| License | Six Labors Split License: Apache 2.0 for open source, non-profits and companies with under $1M annual revenue; commercial license otherwise |
| Repository | github.com/SixLabors/ImageSharp |
| Documentation | docs.sixlabors.com |
| Depends on | nothing (pure managed code) |
| Read / write | yes / yes; animation, transparency, global or local color tables, comments |
ImageSharp decodes a GIF into an Image whose Frames collection holds one full-canvas frame per animation frame, already composited with the disposal methods applied. Per-frame GIF data (delay, disposal, transparency, local palette) is in frame.Metadata.GetGifMetadata(), and file-level data (loop count, global palette, comments) in image.Metadata.GetGifMetadata(). Processing operations like Resize apply to every frame.
When encoding, ImageSharp quantizes each frame to 256 colors (Wu quantizer by default, with dithering) and can reuse one global palette or write local palettes per frame. License note: the Six Labors Split License isn’t an OSI license for commercial users above the revenue limit.
Installation#
dotnet add package SixLabors.ImageSharp
Basic usage: read an animated GIF and its frame delays#
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Gif;
using Image image = Image.Load("anim.gif");
GifMetadata gif = image.Metadata.GetGifMetadata();
Console.WriteLine($"{image.Width} x {image.Height}, {image.Frames.Count} frames, repeat {gif.RepeatCount} (0 = forever)");
for (int i = 0; i < image.Frames.Count; i++)
{
GifFrameMetadata meta = image.Frames[i].Metadata.GetGifMetadata();
Console.WriteLine($"frame {i}: {meta.FrameDelay * 10} ms, disposal {meta.DisposalMode}");
}
FrameDelay is in 1/100 s, as stored in the file.
How to resize an animated GIF#
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using Image image = Image.Load("anim.gif");
image.Mutate(x => x.Resize(image.Width / 2, 0)); // every frame; 0 = keep aspect ratio
image.SaveAsGif("half.gif"); // keeps delays and loop count
How to create an animated GIF from images#
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.PixelFormats;
using var gif = new Image<Rgba32>(200, 200, new Rgba32(255, 0, 0)); // frame 1: red
gif.Metadata.GetGifMetadata().RepeatCount = 0; // loop forever
gif.Frames.RootFrame.Metadata.GetGifMetadata().FrameDelay = 50; // 0.5 s
Rgba32[] colors = [new Rgba32(0, 255, 0), new Rgba32(0, 0, 255)];
foreach (Rgba32 color in colors)
{
using var frame = new Image<Rgba32>(200, 200, color);
frame.Frames.RootFrame.Metadata.GetGifMetadata().FrameDelay = 50;
gif.Frames.AddFrame(frame.Frames.RootFrame); // copies the frame and its metadata
}
gif.SaveAsGif("anim.gif");
How to extract frames of a GIF as PNG#
using Image image = Image.Load("anim.gif");
for (int i = 0; i < image.Frames.Count; i++)
{
using Image frame = image.Frames.CloneFrame(i);
frame.SaveAsPng($"frame-{i:D3}.png");
}
How to control the palette and dithering#
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
var encoder = new GifEncoder
{
ColorTableMode = FrameColorTableMode.Global, // one palette for all frames: smaller file
Quantizer = new WuQuantizer(new QuantizerOptions { MaxColors = 128, Dither = null }),
};
image.SaveAsGif("out.gif", encoder);
No dithering compresses better with LZW; dithering looks smoother on gradients.
Tips#
- Load only the first frame of big animations with
new DecoderOptions { MaxFrames = 1 }andImage.Load(options, path). - To convert a GIF to animated WebP or PNG (APNG), load it and call
SaveAsWebp/SaveAsPng; frame delays carry over. - Decoding a long GIF allocates every frame at full size: a 480x270 GIF with 300 frames uses about 150 MB.