3

I am using the following piece of code to load images as thumbnails to a FlowLayoutPanel control. Unfortunately i get an OutOfMemory exception.

As you already guess the memory leak is found at line

 Pedit.Image = System.Drawing.Image.FromStream(fs)

So how could i optimize the following code?

 Private Sub LoadImagesCommon(ByVal FlowPanel As FlowLayoutPanel, ByVal fi As FileInfo)
        Pedit = New DevExpress.XtraEditors.PictureEdit
        Pedit.Width = txtIconsWidth.EditValue
        Pedit.Height = Pedit.Width / (4 / 3)
        Dim fs As System.IO.FileStream
        fs = New System.IO.FileStream(fi.FullName, IO.FileMode.Open, IO.FileAccess.Read)
        Pedit.Image = System.Drawing.Image.FromStream(fs)
        fs.Close()
        fs.Dispose()
        Pedit.Properties.SizeMode = DevExpress.XtraEditors.Controls.PictureSizeMode.Zoom

        If FlowPanel Is flowR Then
            AddHandler Pedit.MouseClick, AddressOf Pedit_MouseClick
            AddHandler Pedit.MouseEnter, AddressOf Pedit_MouseEnter
            AddHandler Pedit.MouseLeave, AddressOf Pedit_MouseLeave
        End If

        FlowPanel.Controls.Add(Pedit)
    End Sub

Update: The problem occurs while loading a number of images (3264x2448px at 300dpi - each image is about 3Mb's)

OrElse
  • 9,709
  • 39
  • 140
  • 253
  • Have you tried isolating the problem by removing all superfluous code and attempting only to load the image? Have you tried assigning the loaded image (if it gets that far) into a 'normal' image display control? – Grant Thomas Jan 31 '11 at 16:58
  • 1
    Do you run out of memory when loading just one image, or after many images? Is it just one particular image that gives you trouble, or does it fail when you try to load any image? Are the images exceptionally large? Please, more detail or the best we can do is guess. – Jim Mischel Jan 31 '11 at 17:05
  • @Chocol8, does this happen for any group of images or is there 1 specific image that is causing this? Unfortunately the OOM exception tends to be a generic error that gets thrown often when dealing with images. (Ok, not a generic error, but a low-level error that doesn't get wrapped in a more specific error.) I've run across this error before with corrupt images. Sometimes its images that get uncompressed into memory as much larger images. http://stackoverflow.com/questions/1108607/out-of-memory-exception-on-system-drawing-image-fromfile – Chris Haas Jan 31 '11 at 17:20
  • @Chris. This error happens on a group of images. – OrElse Jan 31 '11 at 17:24
  • 3
    An image of 3264x2448 is 7,990,272 pixels. If it's 24 bit color, that's 3 byte per pixel or about 23 megabytes per image. They might be only 3 MB when compressed on disk, but in memory it's going to use the full uncompressed size. You can't load many of those if you have a system with little memory. – Jim Mischel Jan 31 '11 at 17:27

4 Answers4

5

Documentation for Image.FromFile (which is related to your FromStream) says that it will throw OutOfMemoryException if the file is not a valid image format or if GDI+ doesn't support the pixel format. Is it possible you're trying to load an unsupported image type?

Also, documentation for Image.FromStream says that you have to keep the stream open for the lifetime of the image, so even if your code loaded the image you'd probably get an error because you're closing the file while the image is still active. See http://msdn.microsoft.com/en-us/library/93z9ee4x.aspx.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
4

Couple of thoughts:

First off, as Jim has stated, when using Image.FromStream the stream should remain open for the lifetime of the Image as remarked on the MSDN page. As such, I would suggest to copy the contents of the file to a MemoryStream, and use the latter to create the Image instance. So you can release the file handle asap.

Secondly, the images you're using are rather big (uncompressed, as they would exist in memory, Width x Height x BytesPerPixel). Assuming the context you use them in might allow for them to be smaller, consider resizing them, and potentially caching the resized versions somewhere for later use.

Lastly, don't forget to Dispose the image and the Stream when they are no longer needed.

Yannick Motton
  • 34,761
  • 4
  • 39
  • 55
3

You can solve this in a few steps:

  • to get free from the File-dependency, you have to copy the images. By really drawing it to a new Bitmap, you can't just copy it.
  • since you want thumbnails, and your source-bitmaps are rather large, combine this with shrinking the images.
H H
  • 263,252
  • 30
  • 330
  • 514
  • ...and i was ready for posting the third related question... How does an "ACDSee" type of program manages this? – OrElse Feb 01 '11 at 07:37
  • @chocol: I imagine ACDsee and the like create thumbnails and use load-on-demand for the rest. – H H Feb 01 '11 at 09:57
0

I had the same problem. Jim Mischel answer led me to discover loading an innocent .txt file was the culprit. Here's my method in case anyone is interested.

Here's my method:

/// <summary>
/// Loads every image from the folder specified as param.
/// </summary>
/// <param name="pDirectory">Path to the directory from which you want to load images.  
/// NOTE: this method will throws exceptions if the argument causes 
/// <code>Directory.GetFiles(path)</code> to throw an exception.</param>
/// <returns>An ImageList, if no files are found, it'll be empty (not null).</returns>
public static ImageList InitImageListFromDirectory(string pDirectory)
{
    ImageList imageList = new ImageList();

    foreach (string f in System.IO.Directory.GetFiles(pDirectory))
    {
        try
        {
            Image img = Image.FromFile(f);
            imageList.Images.Add(img);
        }
        catch
        {
            // Out of Memory Exceptions are thrown in Image.FromFile if you pass in a non-image file.
        }
    }

    return imageList;
}
blak3r
  • 16,066
  • 16
  • 78
  • 98