C# 如何在 .net 中创建动画 gif
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1196322/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
how to create an animated gif in .net
提问by
Does anyone know how to create an animated gif using c#? Ideally I would have some control over the color reduction used.
有谁知道如何使用 C# 创建动画 gif?理想情况下,我会对使用的颜色减少进行一些控制。
Is using imagemagick (as an external started process) the best choice?
使用 imagemagick(作为外部启动进程)是最佳选择吗?
回答by olle
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:
在不知道重要的质量参数的情况下,调用 imagemagick 是否是最佳选择有点难以判断。其他一些选择是:
Rick van den Bosch's codearchive.org mirror- NGifarticle on codeplex
Rick van den Bosch 的代码archive.org 镜像- 关于codeplex的NGif文章
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 articleat 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.
MS 支持的这篇文章解释了如何使用自定义颜色表保存 gif(这确实需要完全信任)。动画 gif 只是每个图像的一组 gif,在标题中带有一些附加信息。因此,将这两篇文章结合起来应该可以满足您的需求。
回答by fireydude
There is a built in .NET class which will encode GIF files. GifBitmapEncode MSDN
有一个内置的 .NET 类可以对 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);
}
回答by BalintN
To use the sample from a Windows Forms app, add references to these assemblies:
要使用 Windows 窗体应用程序中的示例,请添加对这些程序集的引用:
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
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
Int32Rect 位于 System.Windows 命名空间中
BitmapSizeOptions 在 System.Windows.Media.Imaging 命名空间中
BitmapFrame 位于 System.Windows.Media.Imaging 命名空间中
Also, don't forget to close the file stream (something like this):
另外,不要忘记关闭文件流(类似这样):
using(FileStream targetFile = new FileStream(path, FileMode.Create))
{
gEnc.Save(targetFile);
}
回答by Bryan Legend
You might also consider using the ImageMagick library.
您也可以考虑使用 ImageMagick 库。
There are two .net wrappers for the library listed at http://www.imagemagick.org/script/api.php
http://www.imagemagick.org/script/api.php 上列出的库有两个 .net 包装器
Here is an example on how to do it using the Magick.net wrapper:
这是一个关于如何使用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");
}
回答by Mathew Sachin
This Gif Animation Creater code from https://github.com/DataDink/Bumpkitcan set Delay foreach Frame:
这个来自https://github.com/DataDink/Bumpkit 的Gif Animation Creater 代码可以为每帧设置延迟:
Uses .Net standard Gif Encoding and adds Animation headers.
使用 .Net 标准 Gif 编码并添加动画标题。
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();
}
}
回答by Vitaliy Fedorchenko
I noticed that one more great alternative to ImageMagic and NGif is not listed in answers yet.
我注意到答案中尚未列出 ImageMagic 和 NGif 的另一种更好的替代方案。
FFMpegcan be used for creating animated GIFs from:
FFMpeg可用于从以下内容创建动画 GIF:
- sequence of images (files)
- existing video clip (say, mp4 or avi)
- from C# bitmap objects by providing input data as "ravvideo" through stdin (without using any temp files)
- 图像序列(文件)
- 现有的视频剪辑(例如,mp4 或 avi)
- 从 C# 位图对象通过 stdin 提供输入数据作为“ravvideo”(不使用任何临时文件)
You can start ffmpeg.exe directly from C# code (with System.Diagnostics.Process) or use one of the existing .NET ffmpeg wrappers:
您可以直接从 C# 代码(使用 System.Diagnostics.Process)或使用现有的 .NET ffmpeg 包装器之一启动 ffmpeg.exe:
var ffmpeg = new NReco.VideoConverter.FFMpegConverter();
ffmpeg.ConvertMedia("your_clip.mp4", null, "result.gif", null, new ConvertSettings() );
(this code example uses free NReco VideoConverter- I'm an author of this component, feel free to ask any questions about its usage).
(此代码示例使用免费的NReco VideoConverter- 我是该组件的作者,请随时提出有关其用法的任何问题)。
GIF size can be easily reduced by decreasing frame rate and/or frame size. Also it is possible to get fine-looking animated GIFs with 2-pass approach that generates optimal GIF palette.
通过降低帧速率和/或帧大小可以轻松减小 GIF 大小。还可以通过生成最佳 GIF 调色板的 2-pass 方法获得精美的动画 GIF。
回答by jaycer
The AnimatedGifpackage can make animated gifs.
该AnimatedGif包可以使GIF动画。
using (var gif = AnimatedGif.AnimatedGif.Create(Path.Combine(outputPath, "output.gif"), 20))
{
for (var i = 0; i < 10; i++)
{
img = Image.FromFile(Path.Combine(outputPath, $"{i.ToString().PadLeft(3, '0')}.png"));
gif.AddFrame(img, delay: -1, quality: GifQuality.Bit8);
}
}