Magick.NET
| Language | C# (.NET 8+, .NET Standard 2.0, .NET Framework 4.6.2+) |
|---|---|
| License | Apache 2.0 (ImageMagick: ImageMagick License, permissive) |
| Repository | github.com/dlemstra/Magick.NET |
| Documentation | Magick.NET docs and samples |
| Depends on | native ImageMagick, bundled in the NuGet package |
| Read / write | yes / yes; animation, transparency, frame optimization, quantization with dithering |
Magick.NET reads an animated GIF into a MagickImageCollection, one MagickImage per frame. Frames come as stored in the file: each may be a smaller rectangle placed at Page.X/Page.Y on the canvas. Coalesce() turns them into full-canvas frames you can edit; Optimize() and OptimizeTransparency() turn them back into small difference frames before saving. ImageMagick’s quantizer with Floyd-Steinberg or Riemersma dithering produces good palettes from RGB frames.
The native library makes the package large. Use the Q8 package for GIF: it needs half the memory of Q16.
Installation#
Shell
dotnet add package Magick.NET-Q8-AnyCPU
Basic usage: read the frames of an animated GIF#
C#
using ImageMagick;
using var frames = new MagickImageCollection("anim.gif");
Console.WriteLine($"{frames.Count} frames, loop {frames[0].AnimationIterations} (0 = forever)");
foreach (var frame in frames)
{
Console.WriteLine($"{frame.Width}x{frame.Height} at ({frame.Page.X},{frame.Page.Y}), " +
$"delay {frame.AnimationDelay}/100 s, dispose {frame.GifDisposeMethod}");
}
How to resize an animated GIF#
Coalesce first, or partial frames are scaled and placed wrongly:
C#
using var frames = new MagickImageCollection("anim.gif");
frames.Coalesce(); // full-canvas frames
foreach (var frame in frames)
frame.Resize(new MagickGeometry("320x")); // width 320, keep aspect ratio
frames.Optimize(); // back to small difference frames
frames.Write("small.gif");
How to create an animated GIF from images#
C#
using ImageMagick;
using var collection = new MagickImageCollection();
foreach (var file in new[] { "f1.png", "f2.png", "f3.png" })
{
var image = new MagickImage(file);
image.AnimationDelay = 20; // 1/100 s units: 0.2 s
image.GifDisposeMethod = GifDisposeMethod.Background;
collection.Add(image);
}
collection[0].AnimationIterations = 0; // loop forever
collection.Quantize(new QuantizeSettings { Colors = 256, DitherMethod = DitherMethod.FloydSteinberg });
collection.Optimize();
collection.Write("anim.gif");
How to make a GIF file smaller#
C#
using var frames = new MagickImageCollection("big.gif");
frames.Coalesce();
frames.Quantize(new QuantizeSettings { Colors = 64, DitherMethod = DitherMethod.No });
frames.Optimize(); // crop frames to changed areas
frames.OptimizeTransparency(); // make unchanged pixels transparent
frames.Write("smaller.gif");
How to extract the first frame or all frames#
C#
using var frames = new MagickImageCollection("anim.gif");
frames.Coalesce();
frames[0].Write("first.png");
for (int i = 0; i < frames.Count; i++)
frames[i].Write($"frame-{i:D3}.png");
Tips#
- Reading only the first frame is faster:
new MagickImage("anim.gif")loads frame 0. AnimationDelayusesAnimationTicksPerSecondunits, 100 by default for GIF.- Set
ResourceLimits.MemoryandResourceLimits.Areawhen processing uploaded GIFs; a small file can expand to thousands of large frames.