'Create gif animation in wpf
I want to create a GIF animation and save the resulting file. Since the project is created in wpf i tried to use System.Windows.Media.Imaging.GifBitmapEncoder
public static void save(List<BitmapImage> bitmaps)
{
GifBitmapEncoder encoder = new GifBitmapEncoder();
foreach(var bitmap in bitmaps)
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (FileStream fs = new FileStream("new.gif", FileMode.Create))
encoder.Save(fs);
}
But it does not support setting metadata to set the delay between frames and the cycling of the animation itself. Then i found a solution using System.Drawing namespace here
public static void Save(List<Bitmap> bitmaps, string fileName)
{
// Gdi+ constants absent from System.Drawing.
const int PropertyTagFrameDelay = 0x5100;
const int PropertyTagLoopCount = 0x5101;
const short PropertyTagTypeLong = 4;
const short PropertyTagTypeShort = 3;
const int UintBytes = 4;
var gifEncoder = GetEncoder(ImageFormat.Gif);
// Params of the first frame.
var encoderParams1 = new EncoderParameters(1);
encoderParams1.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
// Params of other frames.
var encoderParamsN = new EncoderParameters(1);
encoderParamsN.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.FrameDimensionTime);
// Params for the finalizing call.
var encoderParamsFlush = new EncoderParameters(1);
encoderParamsFlush.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.Flush);
// PropertyItem for the frame delay (apparently, no other way to create a fresh instance).
var frameDelay = (PropertyItem)FormatterServices.GetUninitializedObject(typeof(PropertyItem));
frameDelay.Id = PropertyTagFrameDelay;
frameDelay.Type = PropertyTagTypeLong;
// Length of the value in bytes.
frameDelay.Len = bitmaps.Count * UintBytes;
// The value is an array of 4-byte entries: one per frame.
// Every entry is the frame delay in 1/100-s of a second, in little endian.
frameDelay.Value = new byte[bitmaps.Count * UintBytes];
// E.g., here, we're setting the delay of every frame to 1 second.
var frameDelayBytes = BitConverter.GetBytes((uint)100);
for (int j = 0; j < bitmaps.Count; ++j)
Array.Copy(frameDelayBytes, 0, frameDelay.Value, j * UintBytes, UintBytes);
// PropertyItem for the number of animation loops.
var loopPropertyItem = (PropertyItem)FormatterServices.GetUninitializedObject(typeof(PropertyItem));
loopPropertyItem.Id = PropertyTagLoopCount;
loopPropertyItem.Type = PropertyTagTypeShort;
loopPropertyItem.Len = 1;
// 0 means to animate forever.
loopPropertyItem.Value = BitConverter.GetBytes((ushort)0);
string filePath = $"{fileName}.gif";
using (var stream = new FileStream(filePath, FileMode.Create))
{
bool first = true;
Bitmap firstBitmap = null;
// Bitmaps is a collection of Bitmap instances that'll become gif frames.
foreach (var bitmap in bitmaps)
{
if (first)
{
firstBitmap = bitmap;
firstBitmap.SetPropertyItem(frameDelay);
firstBitmap.SetPropertyItem(loopPropertyItem);
firstBitmap.Save(stream, gifEncoder, encoderParams1);
first = false;
}
else
{
firstBitmap.SaveAdd(bitmap, encoderParamsN);
}
}
firstBitmap.SaveAdd(encoderParamsFlush);
}
}
Both methods create .gif files and in both cases the resulting animation has image distortion similar to noise. What could be the problem? At the same time, if you save each animation frame separately, there are no such distortions.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
