cómo crear un gif animado en .net

¿Alguien sabe cómo crear un gif animado usando c #? Idealmente, tendría cierto control sobre la reducción de color utilizada.

¿Usar imagemgick (como proceso externo iniciado) es la mejor opción?

Hay una clase .NET incorporada que codificará los archivos GIF. 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); } 

Este código de Gif Animation Creater de https://github.com/DataDink/Bumpkit puede establecer Delay foreach Frame:

Utiliza el estándar .Net Gif Encoding y agrega encabezados de animación.

EDITAR : hizo que el código sea similar a un escritor de archivos típico.

 using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Threading.Tasks; ///  /// Creates a GIF using .Net GIF encoding and additional animation headers. ///  public class GifWriter : IDisposable { #region Fields const long SourceGlobalColorInfoPosition = 10, SourceImageBlockPosition = 789; readonly BinaryWriter _writer; bool _firstFrame = true; readonly object _syncLock = new object(); #endregion ///  /// Creates a new instance of GifWriter. ///  /// The  to output the Gif to. /// Default Delay between consecutive frames... FrameRate = 1000 / DefaultFrameDelay. /// No of times the Gif should repeat... -1 to repeat indefinitely. 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; } ///  /// Creates a new instance of GifWriter. ///  /// The path to the file to output the Gif to. /// Default Delay between consecutive frames... FrameRate = 1000 / DefaultFrameDelay. /// No of times the Gif should repeat... -1 to repeat indefinitely. public GifWriter(string FileName, int DefaultFrameDelay = 500, int Repeat = -1) : this(new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read), DefaultFrameDelay, Repeat) { } #region Properties ///  /// Gets or Sets the Default Width of a Frame. Used when unspecified. ///  public int DefaultWidth { get; set; } ///  /// Gets or Sets the Default Height of a Frame. Used when unspecified. ///  public int DefaultHeight { get; set; } ///  /// Gets or Sets the Default Delay in Milliseconds. ///  public int DefaultFrameDelay { get; set; } ///  /// The Number of Times the Animation must repeat. /// -1 indicates no repeat. 0 indicates repeat indefinitely ///  public int Repeat { get; } #endregion ///  /// Adds a frame to this animation. ///  /// The image to add /// Delay in Milliseconds between this and last frame... 0 =  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 ///  /// Frees all resources used by this object. ///  public void Dispose() { // Complete File _writer.Write((byte)0x3b); // File Trailer _writer.BaseStream.Dispose(); _writer.Dispose(); } } 

También podría considerar usar la biblioteca ImageMagick.

Hay dos contenedores .net para la biblioteca listados en http://www.imagemgick.org/script/api.php

Aquí hay un ejemplo de cómo hacerlo usando el contenedor Magick.net :

 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"); } 

Si llamar a imagemgick es la mejor opción es difícil de entender sin conocer los parámetros de calidad que son importantes. Algunas otras opciones serían:

  • El código de Rick van den Bosch archive.org mirror
  • Artículo de NGif sobre codeplex

estos tienen la ventaja de que no tiene una dependencia de una biblioteca parcialmente en parte, que podría o no estar disponible en todos los sistemas que ejecutan su código.

Este artículo de MS Support explica cómo guardar un gif con una tabla de colores personalizada (esto requiere una confianza total). Un gif animado es solo un conjunto de gifs para cada imagen con información adicional en el encabezado. Por lo tanto, la combinación de estos dos artículos debería brindarle lo que necesita.

Para usar el ejemplo de una aplicación de Windows Forms, agregue referencias a estos ensamblajes:

C: \ Archivos de progtwig \ Conjuntos de referencia \ Microsoft \ Framework.NETFramework \ v4.0 \ PresentationCore.dll C: \ Archivos de progtwig \ Conjuntos de referencia \ Microsoft \ Framework.NETFramework \ v4.0 \ System.Xaml.dll C: \ Program Archivos \ Assemblies de referencia \ Microsoft \ Framework.NETFramework \ v4.0 \ WindowsBase.dll

Entonces

  • Int32Rect está en el espacio de nombres System.Windows

  • BitmapSizeOptions está en el espacio de nombres System.Windows.Media.Imaging

  • BitmapFrame está en el espacio de nombres System.Windows.Media.Imaging

Además, no olvide cerrar la secuencia de archivos (algo como esto):

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

Noté que una gran alternativa más a ImageMagic y NGif aún no figura en las respuestas.

FFMpeg se puede usar para crear GIF animados desde:

  • secuencia de imágenes (archivos)
  • video clip existente (digamos, mp4 o avi)
  • desde objetos de bitmap C # al proporcionar datos de entrada como “ravvideo” a través de stdin (sin usar ningún archivo temporal)

Puede iniciar ffmpeg.exe directamente desde el código C # (con System.Diagnostics.Process) o usar uno de los contenedores .ffgg de .NET existentes:

 var ffmpeg = new NReco.VideoConverter.FFMpegConverter(); ffmpeg.ConvertMedia("your_clip.mp4", null, "result.gif", null, new ConvertSettings() ); 

(este ejemplo de código usa NReco VideoConverter gratuito – Soy autor de este componente, no dude en hacer preguntas sobre su uso).

El tamaño GIF puede reducirse fácilmente disminuyendo la velocidad de cuadros y / o el tamaño del cuadro. También es posible obtener GIF animados de aspecto fino con un enfoque de 2 pasos que genera una paleta GIF óptima.