In C #, maybe this is a bug for Image.SaveAdd, who can help me solve it?

I am trying to merge two different gif files into one file.

Firstly, I learned a lot about gif format. And I know that the delay time value is set in the Graphics Control Extension, which is a gif file block.

I saved the first gif and set the value to FrameDelay, as shown below:

ImageCodecInfo codeInfo = GetEncoder(ImageFormat.Gif); System.Drawing.Imaging.Encoder saveEncoder = System.Drawing.Imaging.Encoder.SaveFlag; EncoderParameters parameters = new EncoderParameters(1); parameters.Param[0] = new EncoderParameter(saveEncoder, (long)EncoderValue.MultiFrame); PropertyItem PropertyTagFrameDelay = img1.GetPropertyItem(0x5100); PropertyTagFrameDelay.Value = new byte[] { 0x96, 0x00 };// this is the delay value 0x0096, means 1.5 second img1.SetPropertyItem(PropertyTagFrameDelay); PropertyItem LoopCount = img1.GetPropertyItem(0x5101); LoopCount.Value = new byte[] { 0x00, 0x00 };// this means the gif loops endlessly img1.SetPropertyItem(LoopCount); img1.Save(@"c:\ddd.gif", codeInfo, parameters); 

Then I tried to add another image as a second frame.

  parameters = new EncoderParameters(1); parameters.Param[0] = new EncoderParameter(saveEncoder, (long)EncoderValue.FrameDimensionTime); PropertyTagFrameDelay = img2.GetPropertyItem(0x5100); PropertyTagFrameDelay.Value = new byte[] { 0x96, 0x00 };// this is the delay value 0x0096, means 1.5 second img2.SetPropertyItem(PropertyTagFrameDelay); 

Finally, I have to interrupt this image.

 parameters = new EncoderParameters(1); parameters.Param[0] = new EncoderParameter(saveEncoder, (long)EncoderValue.Flush); img1.SaveAdd(parameters); 

And I found that the delay time of the second frame is always 0.

I tried many methods, but I do not know how to do this as 0x96.

And what's wrong with that?

+7
c # image gif
source share
4 answers

It is simply not supported by any of the .NET code images. Neither GDI + nor WIC are the base codecs for the System.Drawing.Bitmap and System.Windows.Media.Imaging.PngBitmapEncoder classes.

Although this sounds very strange, the most likely reason is that GIF was saddled with a software patent. Unisys owns the rights to the LZW compression algorithm and has begun aggressively pursuing licensing fees for it. Starting with the most obvious goals for which there can be the most money, Microsoft is always at the top of the list. They were also not modest: a non-profit or private website that used GIF files on their web pages in 1999 was supposed to deflate five thousand dollars.

This killed the image format. The ubiquitous before that, almost everyone stopped using them. Amazingly fast, it took only a few months. Fortunately, the coincidence with all the fully filling animated gifs was greatly exaggerated before. You can find some web pages on the back, and then on the machine, where everything in the corner of the eye moved. Not the only happy coincidence, it was the main reason for developing the open source PNG format. Thanks to our lucky stars :)

The patent expired around 2004, depending on where you live, so you no longer have to be afraid of writing from Unisys.

In short, you'll have to shop around another library to add this feature to your program. It is well covered by this existing SO question ; there is no need to repeat it here.

+15
source share

From the Microsoft Developer Center : you cannot use SaveAdd to add frames to an animated gif file.

To build an animated gif, you need to go through each frame in the animation. A good example of this is shown on VCSKicks or https://stackoverflow.com/a/166268/ .

+1
source share

Update:

I have done more research and I think these ffmpeg and mplayer recommendations are worth a try:
Create an animated gif from a set of jpeg images

Update 2:

This code from Rick van den Bosch is also very good, as it gives you access to delays:

.Net (at least 1.1, they can include it in 2.0) does not give you can create animated GIF files through GDI +.

 //Variable declaration StringCollection stringCollection; MemoryStream memoryStream; BinaryWriter binaryWriter; Image image; Byte[] buf1; Byte[] buf2; Byte[] buf3; //Variable declaration stringCollection = a_StringCollection_containing_images; Response.ContentType = "Image/gif"; memoryStream = new MemoryStream(); buf2 = new Byte[19]; buf3 = new Byte[8]; buf2[0] = 33; //extension introducer buf2[1] = 255; //application extension buf2[2] = 11; //size of block buf2[3] = 78; //N buf2[4] = 69; //E buf2[5] = 84; //T buf2[6] = 83; //S buf2[7] = 67; //C buf2[8] = 65; //A buf2[9] = 80; //P buf2[10] = 69; //E buf2[11] = 50; //2 buf2[12] = 46; //. buf2[13] = 48; //0 buf2[14] = 3; //Size of block buf2[15] = 1; // buf2[16] = 0; // buf2[17] = 0; // buf2[18] = 0; //Block terminator buf3[0] = 33; //Extension introducer buf3[1] = 249; //Graphic control extension buf3[2] = 4; //Size of block buf3[3] = 9; //Flags: reserved, disposal method, user input, transparent color buf3[4] = 10; //Delay time low byte buf3[5] = 3; //Delay time high byte buf3[6] = 255; //Transparent color index buf3[7] = 0; //Block terminator binaryWriter = new BinaryWriter(Response.OutputStream); for (int picCount = 0; picCount < stringCollection.Count; picCount++) { image = Bitmap.FromFile(stringCollection[picCount]); image.Save(memoryStream, ImageFormat.Gif); buf1 = memoryStream.ToArray(); if (picCount == 0) { //only write these the first time.... binaryWriter.Write(buf1, 0, 781); //Header & global color table binaryWriter.Write(buf2, 0, 19); //Application extension } binaryWriter.Write(buf3, 0, 8); //Graphic extension binaryWriter.Write(buf1, 789, buf1.Length - 790); //Image data if (picCount == stringCollection.Count - 1) { //only write this one the last time.... binaryWriter.Write(";"); //Image terminator } memoryStream.SetLength(0); } binaryWriter.Close(); Response.End(); 

As Hans called it unsupported, this is the third solution - a RenniePet proposal to extract frames from both Gifs and then combine all the frames together.

Add a link to System.Drawing.DLL and use this code to get frames:

 using System.Drawing; using System.Drawing.Imaging; public class GifImage { private Image gifImage; private FrameDimension dimension; private int frameCount; private int currentFrame = -1; private bool reverse; private int step = 1; public GifImage(string path) { gifImage = Image.FromFile(path); //initialize dimension = new FrameDimension(gifImage.FrameDimensionsList[0]); //gets the GUID //total frames in the animation frameCount = gifImage.GetFrameCount(dimension); } public int GetFrameCount() { return frameCount; } public bool ReverseAtEnd { //whether the gif should play backwards when it reaches the end get { return reverse; } set { reverse = value; } } public Image GetNextFrame() { currentFrame += step; //if the animation reaches a boundary... if (currentFrame >= frameCount || currentFrame < 1) { if (reverse) { step *= -1; //...reverse the count //apply it currentFrame += step; } else { currentFrame = 0; //...or start over } } return GetFrame(currentFrame); } public Image GetFrame(int index) { gifImage.SelectActiveFrame(dimension, index); //find the frame return (Image)gifImage.Clone(); //return a copy of it } } 

We can extract all frames as follows:

 private static readonly string tempFolder = @"C:\temp\"; static void Main(string[] args) { CombineGifs(@"c:\temp\a.gif", @"c:\temp\b.gif"); } public static void CombineGifs(string firstImageFilePath, string secondImageFilePath) { int frameCounter = ExtractGifFramesAndGetCount(firstImageFilePath, 0); int secondframeCounter = ExtractGifFramesAndGetCount(secondImageFilePath, frameCounter); string filePathOfCombinedGif = CombineFramesIntoGif(0, secondframeCounter); } private static int ExtractGifFramesAndGetCount(string filePath, int imageNameStartNumber) { ////NGif had an error when I tried it //GifDecoder gifDecoder = new GifDecoder(); //gifDecoder.Read(filePath); //int frameCounter = imageNameStartNumber + gifDecoder.GetFrameCount(); //for (int i = imageNameStartNumber; i < frameCounter; i++) //{ // Image frame = gifDecoder.GetFrame(i); // frame i // frame.Save(tempFolder + i.ToString() + ".png", ImageFormat.Png); //} //So we'll use the Gifimage implementation GifImage gifImage = new GifImage(filePath); gifImage.ReverseAtEnd = false; int frameCounter = imageNameStartNumber + gifImage.GetFrameCount(); for (int i = imageNameStartNumber; i < frameCounter; i++) { Image img = gifImage.GetNextFrame(); img.Save(tempFolder + i.ToString() + ".png"); } return frameCounter; } 

Then we combine all the frames into one animated gif using NGif

Download the code, open the Solution, and compile the Components project to get the Gif.Components.dll DLL and Gif.Components.dll this DLL in your solution.

 private static string CombineFramesIntoGif(int startFrameCount, int endFrameCount) { List<string> imageFilePaths = new List<string>(); for (int i = startFrameCount; i < endFrameCount; i++) { imageFilePaths.Add(tempFolder + i.ToString() + ".png"); } string outputFilePath = tempFolder + "test.gif"; AnimatedGifEncoder e = new AnimatedGifEncoder(); e.Start(outputFilePath); e.SetDelay(500); //-1:no repeat,0:always repeat e.SetRepeat(0); for (int i = 0; i < imageFilePaths.Count; i++) { e.AddFrame(Image.FromFile(imageFilePaths[i])); } e.Finish(); return outputFilePath; } 
+1
source share

If you want to use a third-party library, you can use Magick.NET . This is the C # wrapper for ImageMagick .

 using (MagickImageCollection images = new MagickImageCollection()) { MagickImage firstFrame = new MagickImage("first.gif"); firstFrame.AnimationDelay = 1500; images.Add(firstFrame); MagickImage secondFrame = new MagickImage("second.gif"); secondFrame.AnimationDelay = 200; images.Add(secondFrame); // This method will try to make your output image smaller. images.OptimizePlus(); images.Write(@"c:\ddd.gif"); } 
+1
source share

All Articles