1

I have to make some thumbnail from specific parts of a large image and save them as png/jpeg images. Here is what I'm doing:

public static void Save(BitmapImage srcBitmap, Int32Rect srcRegion, Rect destRegion)
{
    var cropped = new CroppedBitmap(srcBitmap, srcRegion);

    var drawingVisual = new DrawingVisual();

    using (DrawingContext drawingContext = drawingVisual.RenderOpen())
    {
        drawingContext.DrawImage(cropped, destRegion);
    }

    var bmp = new RenderTargetBitmap(256, 256, 96, 96, PixelFormats.Pbgra32);

    bmp.Render(drawingVisual);

    var bitmapEncoder = new PngBitmapEncoder();

    bitmapEncoder.Frames.Add(BitmapFrame.Create(bmp));

    using (var filestream = new FileStream(path, FileMode.Create))
    {
        bitmapEncoder.Save(filestream);
    }
}

I may call this method using a single large Bitmap with different srcRegion for thousand times, the application increasingly consume more RAM and finally throws System.OutOfMemoryException! Seems there is a memory leak in this function but I don't know where it is. Anyone can help?

EDIT: also I'm not sure whether this is the best way of getting a portion of a large image and resizing that portion into a smaller image (e.g. 256*256) and saving it. Is there any better idea?

enter image description here

9
  • 1
    The function is static which may be the cause of the issue. Try removing static. You may want to put code in a separate class and then dispose the class to insure the object are destroyed to prevent leaking. Commented May 1, 2016 at 18:53
  • Use PerfView and do Memory - Take Heap Snapshot. What do you see? microsoft.com/en-us/download/details.aspx?id=28567. It could be that with .NET 4.6.2 things get better if the leak is related to the too lazy nature of the GC and the strange WPF way of handling unmanaged resources. This might be of interest: geekswithblogs.net/akraus1/archive/2016/04/14/174476.aspx Commented May 1, 2016 at 18:54
  • Is destRegion ever different from (0, 0, 256, 256), and does the size of srcRegion ever differ from (256, 256)? If not, you may probably directly pass the CroppedBitmap to the BitmapEncoder, without using a DrawingVisual and a RenderTargetBtmap. Commented May 1, 2016 at 19:14
  • You don't really need/want/should to use RenderTargetBitmap, DrawingVisual and other wpf things just to make a thumb out of image region, especially if you do this thousands times. It's just not the right tool for the job, even standard Bitmap should do much better. Commented May 1, 2016 at 20:35
  • @Evk there is a problem with System.Drawing.Bitmap it cannot be created for very large images (e.g: 300MB) but I can use BitmapImage to handle such large images. Commented May 2, 2016 at 1:22

2 Answers 2

1

RenderTargetBitmap is not IDisposable, but it should be (since it uses native resources), it's a weird design implementation, but that's how it is. You may try to call bmp.Clear() before exiting, which should release the native resources.

The RenderTargetBitmap does its own check on GC pressure (using the SafeMILHandle) to release the unmanaged resources, but in my experience that didn't work very well (it was a long time ago though, things may be updated these days)

Also, not for production code, but for testing purposes, I'd add a:

GC.Collect();
GC.WaitForPendingFinalizers();

On top of your method (or right after calling it, from the caller), just to make sure the problem is not managed resources while the GC doesn't have memory pressure to release them (you may have more memory available on the system than you can hold native handles, and that will confuse it all).

Sign up to request clarification or add additional context in comments.

1 Comment

You may want to force collection of the GC then (see the note I added). WPF does indeed have a weird way of handling resources, and does its own GC checks (instead of using IDisposable, like it should). The GC often doesn't have enough pressure to release resources and unmanaged handles can fill up easily... if that doesn't work, I can only recommend doing some more serious memory profiling: I don't see anything wrong with your code
0

Ok I improved this code by using TransformedBitmap (Actually I get this idea from @Clemens) and no exception throws anymore.

public static void Save(BitmapImage srcBitmap, Int32Rect srcRegion, Rect destRegion)
{
    var cropped = new CroppedBitmap(srcBitmap, srcRegion);

    var drawingVisual = new DrawingVisual();

    //Here is the changes:
    var scale = 256.0 / srcRegion.Width;
    var transform = new ScaleTransform(scale, scale);
    //

    using (DrawingContext drawingContext = drawingVisual.RenderOpen())
    {
        //Here is the changes:
        drawingContext.DrawImage(new TransformedBitmap(cropped, transform), destRegion);
        //
    }

    var bmp = new RenderTargetBitmap(256, 256, 96, 96, PixelFormats.Pbgra32);

    bmp.Render(drawingVisual);

    var bitmapEncoder = new PngBitmapEncoder();

    bitmapEncoder.Frames.Add(BitmapFrame.Create(bmp));

    using (var filestream = new FileStream(path, FileMode.Create))
    {
        bitmapEncoder.Save(filestream);
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.