如何在.NET中创建一个动画GIF(how to create an animated gif in

2019-06-17 18:05发布

有谁知道如何创建一个使用C#GIF动画? 理想情况下我会在使用的色彩还原一些控制。

使用ImageMagick的(作为外部启动的进程)的最佳选择?

Answer 1:

有一个内置在其中将编码GIF文件.NET类。 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);
}


Answer 2:

从这个gif动画创世代码https://github.com/DataDink/Bumpkit可设置延时的foreach框架:

软件使用.NET标准的Gif编码,并增加了动画标题。

编辑 :制造类似于典型的文件写入的代码。

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


Answer 3:

你也可以考虑使用ImageMagick库。

有在列出的库中有两个.NET包装http://www.imagemagick.org/script/api.php

下面是有关如何使用做一个例子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");
}


Answer 4:

无论调用的ImageMagick是最好的选择是一种很难awnser不知道质量参数是非常重要的。 其他一些选项是:

  • 里克·范登博世代码 archive.org镜
  • NGif CodePlex上的文章

这些有你没有的第三部分库,它可能会或可能不会提供上执行代码的所有系统的依赖的优势。

该文章在MS支持解释了如何保存自定义颜色表的GIF(这并不需要完全信任)。 甲动画GIF只是一组的GIF用于与在报头中的一些附加信息的每个图像的。 因此,结合这两个条款应该得到你所需要的。



Answer 5:

从Windows窗体应用程序中使用的样品,添加对这些组件:

C:\ Program Files文件\参考大会\微软\ Framework.NETFramework \ V4.0 \ PresentationCore.dll中C:\ Program Files文件\参考大会\微软\ Framework.NETFramework \ V4.0 \ System.Xaml.dll C:\ PROGRAM文件\参考大会\微软\ Framework.NETFramework \ V4.0 \ WindowsBase.dll中

然后

  • Int32Rect是在System.Windows命名空间

  • BitmapSizeOptions在System.Windows.Media.Imaging命名空间

  • BitmapFrame在System.Windows.Media.Imaging命名空间

另外,不要忘记关闭文件流(像这样):

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


Answer 6:

我注意到,对ImageMagic和NGif一个更伟大的选择不回答尚未上市。

FFmpeg的可用于从创建动画GIF:

  • 图像的序列(文件)的
  • 现有的视频剪辑(比如,MP4或AVI)
  • 从通过标准输入提供输入数据为“ravvideo” C#的位图对象(不使用任何临时文件)

您可以直接从C#代码(用的System.Diagnostics.Process)开始ffmpeg.exe或使用现有的.NET ffmpeg的包装之一:

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

(此代码示例使用免费NReco VideoConverter -我这个组件的作者,随时询问其使用的任何问题)。

可以通过减小帧速率和/或帧大小容易地减小尺寸GIF。 此外,也可以得到美貌的GIF动画与生成最佳GIF调色板2回合方法。



文章来源: how to create an animated gif in .net