Est-ce que quelqu'un sait comment créer un gif animé en utilisant c #? Idéalement, j'aurais un contrôle sur la réduction de couleur utilisée.
Utiliser imagemagick (en tant que processus démarré externe) est-il le meilleur choix?
Il existe une classe .NET intégrée qui codera les fichiers 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);
}
Ce code de création d'animation Gif de https://github.com/DataDink/Bumpkit peut définir un délai pour chaque image
Utilise l’encodage GIF standard et ajoute des en-têtes d’animation.
EDIT: Le code est similaire à un éditeur de fichier typique.
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();
}
}
Vous pouvez également envisager d'utiliser la bibliothèque ImageMagick.
Il existe deux wrappers .net pour la bibliothèque répertoriés à http://www.imagemagick.org/script/api.php
Voici un exemple sur la façon de le faire en utilisant le 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");
}
Que le meilleur choix soit d'appeler imagemagick est difficile à comprendre sans connaître les paramètres de qualité importants. Quelques autres options seraient:
ceux-ci ont l'avantage de ne pas dépendre d'une troisième bibliothèque en partie, qui pourrait ou non être disponible sur tous les systèmes exécutant votre code.
Cet article at MS Support explique comment enregistrer un gif avec une table de couleurs personnalisée (cela nécessite une confiance totale). Un gif animé est juste un ensemble de gifs pour chaque image avec quelques informations supplémentaires dans l'en-tête. Donc, la combinaison de ces deux articles devrait vous donner ce dont vous avez besoin.
Pour utiliser l'exemple à partir d'une application Windows Forms, ajoutez des références à ces assemblys:
C:\Program Files\Assemblys de référence\Microsoft\Framework.NETFramework\v4.0\PresentationCore.dll. C:\Programmes\Assemblys de référence\Microsoft\Framework.NETFramework\v4.0\System.Xaml.dll C:\Program Files\Assemblys de référence\Microsoft\Framework.NETFramework\v4.0\WindowsBase.dll
Ensuite
Int32Rect est dans l'espace de noms System.Windows
BitmapSizeOptions est dans l'espace de noms System.Windows.Media.Imaging
BitmapFrame est dans l'espace de noms System.Windows.Media.Imaging
De plus, n'oubliez pas de fermer le flux de fichiers (quelque chose comme ceci):
using(FileStream targetFile = new FileStream(path, FileMode.Create))
{
gEnc.Save(targetFile);
}
J'ai remarqué qu'une autre excellente alternative à ImageMagic et à NGif ne figure pas encore dans les réponses.
FFMpeg peut être utilisé pour créer des GIF animés à partir de:
Vous pouvez démarrer ffmpeg.exe directement à partir de code C # (avec System.Diagnostics.Process) ou utiliser l’un des wrappers .NET ffmpeg existants:
var ffmpeg = new NReco.VideoConverter.FFMpegConverter();
ffmpeg.ConvertMedia("your_clip.mp4", null, "result.gif", null, new ConvertSettings() );
(Cet exemple de code utilise free NReco VideoConverter - Je suis l'auteur de ce composant, n'hésitez pas à poser des questions sur son utilisation).
La taille des images GIF peut être facilement réduite en diminuant la cadence et/ou la taille des images . Il est également possible d’obtenir des GIF animés de haute qualité avec une approche en deux passes qui génère une palette GIF optimale.