Search code examples
c#asp.netimagesharp

Imagesharp - Still image to fill with an animated gif


I've been continuing to experiment with ImageSharp and looking to do something a bit more clever. I have a still png image with a transparent background, and an animated gif image that's bigger or equal to the still image. The code below is part of an interface I've created which calls a method, ProcessImage and returns as a memory stream to be outputted as an image of any type later on.

All is good and it's recognized the frames of the gif image. However, the frames seem to be lapping with eachother as it progresses instead of the individual frames referencing the initial frame of the still image.

I might be overlooking an obvious error in my code, but how can I prevent each frame from lapping with eachother and having each frame take a copy of the clean slate of the still image and fill the whitespace with each frame of the Animated gif Image?

Below are the references. I did have to reduce the size of the gif as the uploads are limited to 2mb

Circle - Still Image

Thunder - Animated Image

The Result of the Thunder and Circle Combined

Code:

    public MemoryStream ProcessImage()
    {
        MemoryStream ms = new MemoryStream();
        const int min = 128; // Grey midpoint
        using (var img = Image.Load(ImagePath))
        using (var texture = Image.Load(Texture))
        {
            if (img.Width > texture.Width || img.Height > texture.Height)
            {
                throw new InvalidOperationException("Image dimensions must be less than or equal to texture dimensions!");
            }

            Image<Rgba32> animated = new Image<Rgba32>(img.Width, img.Height);
            ImageFrame<Rgba32> imageFrame = img.Frames[0];
            foreach (var Frame in texture.Frames)
            {
                ImageFrame<Rgba32> currentFrame = imageFrame;
                for (int y = 0; y < currentFrame.Height; y++)
                {
                    for (int x = 0; x < currentFrame.Width; x++)
                    {
                        var pixel = currentFrame[x, y];
                        if (pixel.R >= min && pixel.G >= min && pixel.B >= min && pixel.A >= min)
                        {
                            currentFrame[x, y] = Frame[x, y];
                        }
                    }
                }
                animated.Frames.AddFrame(currentFrame);
            }
            animated.SaveAsGif(ms);
            return ms;
        }
    }

Solution

  • Found the solution to the problem. I needed to clone the source image (the circle) and dispose of it after each frame of the gif. I can achieve this via the using statement, which is a convenient way to ensure that objects like imageFrame disposes correctly. After this solution was implemented, the frames no longer lap together.

            MemoryStream ms = new MemoryStream();
            const int min = 128; // Grey midpoint
            using (var img = Image.Load(ImagePath))
            using (var texture = Image.Load(Texture))
            {
                if (img.Width > texture.Width || img.Height > texture.Height)
                {
                    throw new InvalidOperationException("Image dimensions must be less than or equal to texture dimensions!");
                }
    
                Image<Rgba32> animated = new Image<Rgba32>(img.Width, img.Height);
                foreach (var Frame in texture.Frames)
                {
                    using (var imageFrame = img.Frames.CloneFrame(0))
                    {
                        Image<Rgba32> currentFrame = imageFrame;
                        for (int y = 0; y < currentFrame.Height; y++)
                        {
                            for (int x = 0; x < currentFrame.Width; x++)
                            {
                                var pixel = currentFrame[x, y];
                                if (pixel.R >= min && pixel.G >= min && pixel.B >= min && pixel.A >= min)
                                {
                                    currentFrame[x, y] = Frame[x, y];
                                }
                            }
                        }
                        animated.Frames.AddFrame(currentFrame.Frames.First());
                    }
                }
                GifEncoder GifEncode = new GifEncoder();
                animated.SaveAsGif(ms);
                return ms;