In C#,maybe it's a bug for Image.SaveAdd, who can help me to solve it?

后端 未结 4 1414
渐次进展
渐次进展 2020-12-17 22:54

I\'m trying to combine two different gif file into one file.

First, I learned a lot about the gif format. And I know the delay time value is set in Graphics Control

相关标签:
4条回答
  • 2020-12-17 23:31

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

    In order to construct an animated gif, you will need to step through each frame in the animation. A good example of this is shown on VCSKicks or same code here on Stackoverflow.

    0 讨论(0)
  • 2020-12-17 23:35

    Update:

    I did more research and I think these ffmpeg and mplayer recommendations are worth trying:
    Create animated gif from a set of jpeg images

    Update 2:

    This code from Rick van den Bosch is very good too, as it gives you access to the delay times:

    .Net (at least 1.1, they might incorporate it in 2.0) does not give you possibilities to create animated GIFs 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 mentioned its not supported, so this third solution is RenniePet's suggestion to extract the frames from both Gif's and then combine all the frames together.

    Add a reference to System.Drawing.DLL and use this code to get the 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 the Frames like this:

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

    Next we combine all the frames into a single animated gif using NGif

    Download the code, open the Solution and compile the Components project to get the DLL Gif.Components.dll and reference that 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;
    }
    
    0 讨论(0)
  • 2020-12-17 23:44

    This simply isn't supported by any of the .NET image encoders. Neither by GDI+ nor by WIC, the underlying native codecs for the System.Drawing.Bitmap and System.Windows.Media.Imaging.PngBitmapEncoder classes.

    While that sounds like a very strange oversight, the most probably reason was that GIF was encumbered by a software patent. Unisys owned the rights to the LZW compression algorithm and started aggressively pursuing obtaining the license fees for it. Starting at the most obvious targets where most money can be had, Microsoft is forever on the top of the list. They were not modest either, a non-commercial or private web site that used GIFs on their web pages had to cough up five thousand dollars in 1999.

    This killed the image format. Ubiquitous before this, just about everybody stopped using them. Stunningly quickly too, this only took a few months. Happily coinciding with everybody completely having their fill of animated gifs btw, it was grossly overdone before. You may find some web pages from back-then on the wayback machine where everything in the corner of eye was moving. Not the only lucky coincidence, it was the core reason for the open source PNG format being developed. Thank our lucky stars :)

    The patent expired around 2004, depending on where you live, so you won't have to fear a letter from Unisys anymore.

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

    0 讨论(0)
  • 2020-12-17 23:51

    If you are willing to use a third party library you could use Magick.NET. This is a 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:\ffffd.gif");
    }
    
    0 讨论(0)
提交回复
热议问题