views:

273

answers:

2

I have a fair few images that I'm loading into a ListBox in my WPF application. Originally I was using GDI to resize the images (the originals take up far too much memory). That was fine, except they were taking about 400ms per image. Not so fine. So in search of another solution I found a method that uses TransformedBitmap (which inherits from BitmapSource). That's great, I thought, I can use that. Except I'm now getting memory leaks somewhere...

I'm loading the images asynchronously using a BackgroundWorker like so:

BitmapSource bs = ImageUtils.ResizeBitmapSource(ImageUtils.GetImageSource(photo.FullName));
                //BitmapSource bs = ImageUtils.GetImageSource(photo.FullName);
                bs.Freeze();

                this.dispatcher.Invoke(new Action(() => { photo.Source = bs; }));

GetImageSource just gets the Bitmap from the path and then converts to BitmapSource.

Here's the code snippet for ResizeBitmapSource:

const int thumbnailSize = 200;
        int width;
        int height;

        if (bs.Width > bs.Height)
        {
            width = thumbnailSize;
            height = (int)(bs.Height * thumbnailSize / bs.Width);
        }
        else
        {
            height = thumbnailSize;
            width = (int)(bs.Width * thumbnailSize / bs.Height);
        }

        BitmapSource tbBitmap = new TransformedBitmap(bs,
                                                           new ScaleTransform(width / bs.Width,
                                                                              height / bs.Height, 0, 0));

        return tbBitmap;

That code is essentially the code from: http://rongchaua.net/blog/c-wpf-fast-image-resize/

Any ideas what could be causing the leak?

edit: Here's the code for GetImageSource, as requested

using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
            {
                using (var bmp = Image.FromStream(stream, false, false))
                {
                    // Use WPF to resize
                    var bitmapSource = ConvertBitmapToBitmapSource(bmp);
                    bitmapSource = ResizeBitmapSource(bitmapSource);
                    return bitmapSource;
                }
            }
+1  A: 

At a guess, from looking at your code you might need to dispose of the bitmap returned by the call to ImageUtils.GetImageSource(photo.FullName).

I have also noted on the blog you pointed out that the author has added an update (11th of March) about inserting a using statement to prevent memory leaks.

Andrew Bienert
Yep, that was based on the comment that I added. It doesn't seem to work though. Not saying that his code is incorrect, but there's something in mine that's causing it to go.. awry...
Harry
+1  A: 

I think you misunderstood how the TransformedBitmap works. It holds onto a reference to the source bitmap, and transforms it in memory. Maybe you could encode the transformed bitmap into a memory stream, and read it right back out. I'm not sure how fast this would be, but you wouldn't then be holding on to the full sized bitmap.

I found this blog post that returned a WriteableBitmap with the TransformedBitmap as the source. The WriteableBitmap will copy the pixel data to a memory buffer in the initializer, so it doesn't actually hold on to a reference to the TransformedBitmap, or the full sized image.

Abe Heidebrecht
OK, I think I understand you. I will try that in the morning, thanks.
Harry