Search code examples
c#encodinggifdecoding

C# GIF encoder/decoder


I’m working on a program that loads a animated GIF file and alter some of the pixels, then spits it back out as a new animated GIF file. The outputted GIF animates just fine in Windows, browsers and Photoshop.

However, if I try to load the GIF with my program, it will not animated. Also, I cannot get the correct palette. The loading code is exactly the same code that I use to load the original GIF with in the first place.

Here’s my saving code:

   public int saveAsGif(string absPath, byte[] mark)
    {
        BitmapPalette palette = new BitmapPalette(getPaletteAsMediaColorList(m_Pal));

        int width = m_CanvasWidth;
        int height = m_CanvasHeight;

        using (FileStream fs = new FileStream(absPath, FileMode.Create))
        {
            GifBitmapEncoder encoder = new GifBitmapEncoder();

            for (int f = 0; f < m_NumberOfFrames; f++)
            {
                FrameData frame = m_Frames[f];
                byte[] pixels = frame.pixels;

                BitmapSource image = BitmapSource.Create(
                    width,
                    height,
                    96,
                    96,
                    System.Windows.Media.PixelFormats.Indexed8,
                    palette,
                    pixels,
                    width);

                encoder.Frames.Add(BitmapFrame.Create(image));

            }
            encoder.Save(fs);

            fs.Close();
        }

        return RESULT_SUCCESFUL;
    }

To make sure it's not my loading code, I created a plain new project with the following code:

    private void Form1_Load(object sender, EventArgs e)
    {
        Bitmap test1 = new Bitmap(@"F:\original.gif");
        pictureBox1.Image = test1;

        Bitmap test2 = new Bitmap(@"F:\exported.gif");
        pictureBox2.Image = test2;
    }

original.gif will load and play perfectly, exported.gif will only show a still frame. However, in Windows/Browser/Photoshop exported.gif will play.


Solution

  • I have found a relative simple solution. It’s not perfect, but it worked for me. It works with GifBitmapEncoder only, no additional libraries are needed.

    All that you need to do;

    1. write the GIF data from encoder to a memory stream.
    2. Binary write the first 13 bytes (GIF89a header and stuff).
    3. Binary write the ‘ApplicationExtention’ block.
    4. Binary write the rest of the GIF data.

    The ApplicationExtention block allows for animations. If no delay times are set, the default time of 100ms is used. If you do want custom delays for each frame, you will need to add a few more bytes in between (see GIF file format doc).

    Code:

    public void saveAsGif(string absPath)
    {
        int width = m_CanvasWidth;
        int height = m_CanvasHeight;
    
        GifBitmapEncoder encoder = new GifBitmapEncoder();
        BitmapPalette palette = new BitmapPalette(getPaletteAsMediaColorList(m_Pal));
    
        for (int f = 0; f < m_NumberOfFrames; f++)
        {
            FrameData frame = m_Frames[f];
            byte[] pixels = frame.pixels;
    
            BitmapSource image = BitmapSource.Create(
                width,
                height,
                96,
                96,
                System.Windows.Media.PixelFormats.Indexed8,
                palette,
                pixels,
                width);
    
            encoder.Frames.Add(BitmapFrame.Create(image));
        }
    
        byte[] GifData;
        using (MemoryStream ms = new MemoryStream())
        {
            encoder.Save(ms);
            GifData = ms.ToArray();
        }
    
        byte[] ApplicationExtention = { 33, 255, 11, 78, 69, 84, 83, 67, 65, 80, 69, 50, 46, 48, 3, 1, 0, 0, 0 };
    
        using (FileStream fs = new FileStream(absPath, FileMode.Create))
        {
            fs.Write(GifData, 0, 13);
            fs.Write(ApplicationExtention, 0, ApplicationExtention.Length);
            fs.Write(GifData, 13, GifData.Length - 13);
        }
    }