Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to create an animated gif in .net

People also ask

Can you make animated GIF in paint net?

Creating complex animations requires the use of professional illustration programs such as Adobe Flash or Toon Boom, but GIF animations can quickly and easily be made with a simple graphics program such as Paint.NET.

How do you make an animated GIF in HTML?

Animated images in HTML are an image on a web page that moves. It is in GIF format i.e. Graphics Interchange Format file. To add an animated image in HTML is quite easy. You need to use the <image> tag with the src attribute.

Does paint net support GIF?

Paint.net can already open normal GIFs, so we can't use the standard GIF extension as paint.


This Gif Animation Creater code from https://github.com/DataDink/Bumpkit can set Delay foreach Frame:

Uses .Net standard Gif Encoding and adds Animation headers.

EDIT: Made the code similar to a typical file writer.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading.Tasks;

/// <summary>
/// Creates a GIF using .Net GIF encoding and additional animation headers.
/// </summary>
public class GifWriter : IDisposable
{
    #region Fields
    const long SourceGlobalColorInfoPosition = 10,
        SourceImageBlockPosition = 789;

    readonly BinaryWriter _writer;
    bool _firstFrame = true;
    readonly object _syncLock = new object();
    #endregion

    /// <summary>
    /// Creates a new instance of GifWriter.
    /// </summary>
    /// <param name="OutStream">The <see cref="Stream"/> to output the Gif to.</param>
    /// <param name="DefaultFrameDelay">Default Delay between consecutive frames... FrameRate = 1000 / DefaultFrameDelay.</param>
    /// <param name="Repeat">No of times the Gif should repeat... -1 not to repeat, 0 to repeat indefinitely.</param>
    public GifWriter(Stream OutStream, int DefaultFrameDelay = 500, int Repeat = -1)
    {
        if (OutStream == null)
            throw new ArgumentNullException(nameof(OutStream));

        if (DefaultFrameDelay <= 0)
            throw new ArgumentOutOfRangeException(nameof(DefaultFrameDelay));

        if (Repeat < -1)
            throw new ArgumentOutOfRangeException(nameof(Repeat));

        _writer = new BinaryWriter(OutStream);
        this.DefaultFrameDelay = DefaultFrameDelay;
        this.Repeat = Repeat;
    }

    /// <summary>
    /// Creates a new instance of GifWriter.
    /// </summary>
    /// <param name="FileName">The path to the file to output the Gif to.</param>
    /// <param name="DefaultFrameDelay">Default Delay between consecutive frames... FrameRate = 1000 / DefaultFrameDelay.</param>
    /// <param name="Repeat">No of times the Gif should repeat... -1 not to repeat, 0 to repeat indefinitely.</param>
    public GifWriter(string FileName, int DefaultFrameDelay = 500, int Repeat = -1)
        : this(new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read), DefaultFrameDelay, Repeat) { }

    #region Properties
    /// <summary>
    /// Gets or Sets the Default Width of a Frame. Used when unspecified.
    /// </summary>
    public int DefaultWidth { get; set; }

    /// <summary>
    /// Gets or Sets the Default Height of a Frame. Used when unspecified.
    /// </summary>
    public int DefaultHeight { get; set; }

    /// <summary>
    /// Gets or Sets the Default Delay in Milliseconds.
    /// </summary>
    public int DefaultFrameDelay { get; set; }

    /// <summary>
    /// The Number of Times the Animation must repeat.
    /// -1 indicates no repeat. 0 indicates repeat indefinitely
    /// </summary>
    public int Repeat { get; }
    #endregion

    /// <summary>
    /// Adds a frame to this animation.
    /// </summary>
    /// <param name="Image">The image to add</param>
    /// <param name="Delay">Delay in Milliseconds between this and last frame... 0 = <see cref="DefaultFrameDelay"/></param>
    public void WriteFrame(Image Image, int Delay = 0)
    {
        lock (_syncLock)
            using (var gifStream = new MemoryStream())
            {
                Image.Save(gifStream, ImageFormat.Gif);

                // Steal the global color table info
                if (_firstFrame)
                    InitHeader(gifStream, _writer, Image.Width, Image.Height);

                WriteGraphicControlBlock(gifStream, _writer, Delay == 0 ? DefaultFrameDelay : Delay);
                WriteImageBlock(gifStream, _writer, !_firstFrame, 0, 0, Image.Width, Image.Height);
            }

        if (_firstFrame)
            _firstFrame = false;
    }

    #region Write
    void InitHeader(Stream SourceGif, BinaryWriter Writer, int Width, int Height)
    {
        // File Header
        Writer.Write("GIF".ToCharArray()); // File type
        Writer.Write("89a".ToCharArray()); // File Version

        Writer.Write((short)(DefaultWidth == 0 ? Width : DefaultWidth)); // Initial Logical Width
        Writer.Write((short)(DefaultHeight == 0 ? Height : DefaultHeight)); // Initial Logical Height

        SourceGif.Position = SourceGlobalColorInfoPosition;
        Writer.Write((byte)SourceGif.ReadByte()); // Global Color Table Info
        Writer.Write((byte)0); // Background Color Index
        Writer.Write((byte)0); // Pixel aspect ratio
        WriteColorTable(SourceGif, Writer);

        // App Extension Header for Repeating
        if (Repeat == -1)
            return;

        Writer.Write(unchecked((short)0xff21)); // Application Extension Block Identifier
        Writer.Write((byte)0x0b); // Application Block Size
        Writer.Write("NETSCAPE2.0".ToCharArray()); // Application Identifier
        Writer.Write((byte)3); // Application block length
        Writer.Write((byte)1);
        Writer.Write((short)Repeat); // Repeat count for images.
        Writer.Write((byte)0); // terminator
    }

    static void WriteColorTable(Stream SourceGif, BinaryWriter Writer)
    {
        SourceGif.Position = 13; // Locating the image color table
        var colorTable = new byte[768];
        SourceGif.Read(colorTable, 0, colorTable.Length);
        Writer.Write(colorTable, 0, colorTable.Length);
    }

    static void WriteGraphicControlBlock(Stream SourceGif, BinaryWriter Writer, int FrameDelay)
    {
        SourceGif.Position = 781; // Locating the source GCE
        var blockhead = new byte[8];
        SourceGif.Read(blockhead, 0, blockhead.Length); // Reading source GCE

        Writer.Write(unchecked((short)0xf921)); // Identifier
        Writer.Write((byte)0x04); // Block Size
        Writer.Write((byte)(blockhead[3] & 0xf7 | 0x08)); // Setting disposal flag
        Writer.Write((short)(FrameDelay / 10)); // Setting frame delay
        Writer.Write(blockhead[6]); // Transparent color index
        Writer.Write((byte)0); // Terminator
    }

    static void WriteImageBlock(Stream SourceGif, BinaryWriter Writer, bool IncludeColorTable, int X, int Y, int Width, int Height)
    {
        SourceGif.Position = SourceImageBlockPosition; // Locating the image block
        var header = new byte[11];
        SourceGif.Read(header, 0, header.Length);
        Writer.Write(header[0]); // Separator
        Writer.Write((short)X); // Position X
        Writer.Write((short)Y); // Position Y
        Writer.Write((short)Width); // Width
        Writer.Write((short)Height); // Height

        if (IncludeColorTable) // If first frame, use global color table - else use local
        {
            SourceGif.Position = SourceGlobalColorInfoPosition;
            Writer.Write((byte)(SourceGif.ReadByte() & 0x3f | 0x80)); // Enabling local color table
            WriteColorTable(SourceGif, Writer);
        }
        else Writer.Write((byte)(header[9] & 0x07 | 0x07)); // Disabling local color table

        Writer.Write(header[10]); // LZW Min Code Size

        // Read/Write image data
        SourceGif.Position = SourceImageBlockPosition + header.Length;

        var dataLength = SourceGif.ReadByte();
        while (dataLength > 0)
        {
            var imgData = new byte[dataLength];
            SourceGif.Read(imgData, 0, dataLength);

            Writer.Write((byte)dataLength);
            Writer.Write(imgData, 0, dataLength);
            dataLength = SourceGif.ReadByte();
        }

        Writer.Write((byte)0); // Terminator
    }
    #endregion

    /// <summary>
    /// Frees all resources used by this object.
    /// </summary>
    public void Dispose()
    {
        // Complete File
        _writer.Write((byte)0x3b); // File Trailer

        _writer.BaseStream.Dispose();
        _writer.Dispose();
    }
}

There is a built in .NET class which will encode GIF files. GifBitmapEncode MSDN

System.Windows.Media.Imaging.GifBitmapEncoder gEnc = new GifBitmapEncoder();

foreach (System.Drawing.Bitmap bmpImage in images)
{
    var bmp = bmpImage.GetHbitmap();
    var src = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
        bmp,
        IntPtr.Zero,
        Int32Rect.Empty,
        BitmapSizeOptions.FromEmptyOptions());
    gEnc.Frames.Add(BitmapFrame.Create(src));
    DeleteObject(bmp); // recommended, handle memory leak
}
using(FileStream fs = new FileStream(path, FileMode.Create))
{
    gEnc.Save(fs);
}

You might also consider using the ImageMagick library.

There are two .net wrappers for the library listed at http://www.imagemagick.org/script/api.php

Here is an example on how to do it using the Magick.net wrapper:

using (MagickImageCollection collection = new MagickImageCollection())
{
  // Add first image and set the animation delay to 100ms
  collection.Add("Snakeware.png");
  collection[0].AnimationDelay = 100;

  // Add second image, set the animation delay to 100ms and flip the image
  collection.Add("Snakeware.png");
  collection[1].AnimationDelay = 100;
  collection[1].Flip();

  // Optionally reduce colors
  QuantizeSettings settings = new QuantizeSettings();
  settings.Colors = 256;
  collection.Quantize(settings);

  // Optionally optimize the images (images should have the same size).
  collection.Optimize();

  // Save gif
  collection.Write("Snakeware.Animated.gif");
}

Whether or not calling imagemagick is the best choice is kind of hard to awnser without knowing the quality parameters that are important. Some other options would be:

  • Rick van den Bosch's code archive.org mirror
  • NGif article on codeplex

these have the advantage that you don't have a dependency on a third partly library which might or might not be available on all systems executing your code.

This article at MS Support explains how to save a gif with a custom color table (this does require full trust). A animated gif is just a set of gifs for each image with some additional information in the header. So combining these two articles should get you what you need.


To use the sample from a Windows Forms app, add references to these assemblies:

C:\Program Files\Reference Assemblies\Microsoft\Framework.NETFramework\v4.0\PresentationCore.dll C:\Program Files\Reference Assemblies\Microsoft\Framework.NETFramework\v4.0\System.Xaml.dll C:\Program Files\Reference Assemblies\Microsoft\Framework.NETFramework\v4.0\WindowsBase.dll

Then

  • Int32Rect is in the System.Windows namespace

  • BitmapSizeOptions is in the System.Windows.Media.Imaging namespace

  • BitmapFrame is in the System.Windows.Media.Imaging namespace

Also, don't forget to close the file stream (something like this):

using(FileStream targetFile = new FileStream(path, FileMode.Create))
{
   gEnc.Save(targetFile);
}