Search code examples
c#.net-coreanimated-gifimagesharp

imagesharp trying to make an animated gif that count from 0 to 9


anyone could tell me why this generate a black only animated gif?

the code also output each in memory generated gif to show that they are different

    public static void Test()
    {
        Image<Rgba32> img = null;
        Image<Rgba32> gif = null;

        TextGraphicsOptions textGraphicsOptions = new TextGraphicsOptions(true);
        SolidBrush<Rgba32> brushYellow = new SolidBrush<Rgba32>(Rgba32.Yellow);

        FontCollection fonts = new FontCollection();
        fonts.Install(fontLocation);
        Font font = fonts.CreateFont("Liberation Mono", PngFontHeight, FontStyle.Regular);


        gif = new Image<Rgba32>(400, 400);
        for (int i = 0; i < 10;++i)
        {
            img = new Image<Rgba32>(400, 400);
            img.Mutate(x => x.Fill(Rgba32.Black));
            img.Mutate(x => x.DrawText(textGraphicsOptions, i.ToString(), font, brushYellow, new PointF(1,1)));

            gif.Frames.AddFrame(img.Frames[0]);


            using (FileStream fs = File.Create(Path.Join(Program.workingDirectory, string.Format("Test-{0}.gif", i))))
            {
                img.SaveAsGif(fs);
            }

            img.Dispose();
        }

        using (FileStream fs = File.Create(Path.Join(Program.workingDirectory, "Test.gif")))
        {
            gif.SaveAsGif(fs);
        }
    }

if I code it to load each individual physical file using this code it make the animated gif as expected.

I want to create the animated gif in memory only.


Solution

  • When you create an Image<>...

    gif = new Image<Rgba32>(400, 400);
    

    ...gif.Frames[0] is a "transparent black" frame (each pixel's RGBA value is #00000000). The additional frames you create in your for loop and add with...

    gif.Frames.AddFrame(img.Frames[0]);
    

    ...become gif.Frames[1] through gif.Frames[10], for a total of 11 frames.

    The GIF encoder uses GifColorTableMode to decide if a color table is generated for each frame or the color table for the first frame is used for all frames. The combination of the default value, GifColorTableMode.Global, plus that first transparent frame results in an 11-frame .gif file with only one color, that same "transparent black". This is why your yellow text doesn't appear and every frame looks the same.

    To solve this, at some point before you save the file you need to remove that initial transparent frame so it doesn't influence color table calculations and because it's not part of your animation, anyways...

    gif.Frames.RemoveFrame(0);
    

    You may also want to change to GifColorTableMode.Local so your .gif file contains color tables reflecting all of the colors rendered...

    gif.MetaData.GetFormatMetaData(GifFormat.Instance).ColorTableMode = GifColorTableMode.Local;
    

    ...although your 10 frames each use almost the same set of colors so if file size is a greater concern than color representation you might leave that property alone. Generating your 400 × 400 animation with GifColorTableMode.Global produces a 9,835-byte file whereas GifColorTableMode.Local produces a 16,703-byte file; 70% bigger but I can't tell the difference between them.

    Related issue on GitHub

    By the way, since I found this along the way, if you wanted to change the duration of the animated frames you would do so using another GetFormatMetaData() method similar to the one shown above...

    GifFrameMetaData frameMetaData = img.MetaData.GetFormatMetaData(GifFormat.Instance);
    
    frameMetaData.FrameDelay = 100;// 1 second