在C#中,也许这是Image.SaveAdd的一个错误,谁可以帮我解决它?

我正在尝试将两个不同的gif文件合并到一个文件中。

首先,我学到了很多关于gif格式的知识。 我知道延迟时间值是在图形控件扩展中设置的,这是一个gif文件块。

我保存了第一个gif并设置了FrameDelay值,代码如下:

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

然后我尝试添加另一个图像作为第二帧。

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

最后,我应该终止这个图像。

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

我发现第二帧的延迟时间总是为0。

我尝试了很多方法,但我不知道它是否为0x96。

那有什么不对呢?

任何 .NET图像编码器都不支持这一点。 无论是GDI +还是WIC, System.Drawing.BitmapSystem.Windows.Media.Imaging.PngBitmapEncoder类的底层本机编解码器。

虽然这听起来像是一个非常奇怪的疏忽,但最可能的原因是GIF受到软件专利的阻碍。 Unisys拥有LZW压缩算法的权利,并开始积极寻求获得许可证费用。 从可以获得大部分资金的最明显的目标开始,微软永远位居榜首。 他们也不谦虚,在他们的网页上使用GIF的非商业或私人网站在1999年不得不咳嗽五千美元。

这杀死了图像格式。 在此之前无处不在,几乎每个人都停止使用它们。 也很快,这只花了几个月 。 令人高兴的是,每个人都完全拥有GIF动画,这在以前是非常过分的。 您可能会在后卫机器上找到一些网页,其中眼角的一切都在移动。 这不是唯一的幸运巧合,而是开源PNG格式的核心原因。 谢谢我们的幸运星:)

该专利于2004年左右到期,取决于您居住的地方,因此您不必再担心Unisys的来信了。

简而言之,您必须到处寻找另一个图书馆才能将此function添加到您的计划中。 这个现有的SO问题很好地涵盖了它,不需要在这里重复。

从Microsoft开发人员中心 :您不能使用SaveAdd将帧添加到动画gif文件。

为了构造动画gif,您需要逐步浏览动画中的每个帧。 VCSKicks或Stackoverflow上的 相同代码就是一个很好的例子。

更新:

我做了更多研究,我认为这些ffmpeg和mplayer建议值得尝试:
从一组jpeg图像创建动画gif

更新2:

来自Rick van den Bosch的代码非常好,因为它可以让您访问延迟时间:

.Net(至少1.1,它们可能将它合并到2.0中)并没有为你提供通过GDI +创建动画GIF的可能性。

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

正如汉斯提到它不受支持,所以第三个解决方案是RenniePet建议从两个Gif中提取帧然后将所有帧组合在一起。

添加对System.Drawing.DLL的引用并使用此代码获取帧:

 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 } } 

我们可以像这样提取所有帧:

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

接下来,我们使用NGif将所有帧组合成单个动画gif

下载代码,打开解决方案并编译Components项目以获取DLL Gif.Components.dll并在解决方案中引用该DLL。

 private static string CombineFramesIntoGif(int startFrameCount, int endFrameCount) { List imageFilePaths = new List(); 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; } 

如果您愿意使用第三方库,则可以使用Magick.NET 。 这是ImageMagick的C#包装器。

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