Simon Tribus
Simon Tribus

Reputation: 456

C# Graphics.DrawImage() throws out of memory exception when trying to combine multiple images

I am trying to combine multiple images to a single one. this is my code right now:

    public static Bitmap Merge(List<Image> imgs)
    {
        int outputImageWidth = 0;
        int outputImageHeight = 0;

        foreach (var img in imgs)
        {
            if(outputImageWidth < img.Width)
            {
                outputImageWidth = img.Width;
            }
            outputImageHeight += img.Height;
        }

        outputImageHeight += 1;

        using (Bitmap outputImage = new Bitmap(outputImageWidth, outputImageHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
        {
            foreach (var img in imgs)
            {
                Graphics graphics = Graphics.FromImage(outputImage);
                graphics.DrawImage(img, new Rectangle(new Point(0, outputImage.Height + 1), img.Size), new Rectangle(new Point(), img.Size), GraphicsUnit.Pixel);
                graphics.Dispose();
                graphics = null;
                img.Dispose();
            }
            foreach (var img in imgs)
            {
                img.Dispose();
            }
            GC.Collect();

            return (Bitmap)outputImage.Clone();
        }
    }

While debugging, i figgured out, then whenever I call graphics.DrawImage(...) about 100-300mb of memory were allocated. I expected it to get liberated whenever i dispose the object, but with every iteration, the process allocates more memory, until I get an out of memory exceptio (approximately after 30 pages on a 32 Bit process). Any ideas?

Upvotes: 1

Views: 557

Answers (1)

Kit
Kit

Reputation: 21709

Try this instead

public static Bitmap Merge(List<Image> imgs)
{
    ...

    var outputImage = new Bitmap(outputImageWidth, outputImageHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb));
    using (var graphics = Graphics.FromImage(outputImage))
    {
        foreach (var img in imgs)
        {
            using (img)
                graphics.DrawImage(img, new Rectangle(new Point(0, outputImage.Height + 1), img.Size), new Rectangle(new Point(), img.Size), GraphicsUnit.Pixel);                 
        }

        return outputImage;
    }
}

This

  • features using blocks that allow for Dispose to always be called, even if there are exceptions thrown part of the way through
  • uses a single Graphics throughout the process
  • doesn't allocate the (huge) bitmap twice

You will, of course, have to dispose of the resulting bitmap outside of this method once you're done with it.

Upvotes: 3

Related Questions