I'm sure the solution to this is simple but I'm left scratching my head so maybe someone can shed some light here.
What I am doing is when my program starts I load an input file into a private variable (_bmpSource) and display it in a Canvas by creating an ImageBrush and setting the canvas's background. On a button click I modify the image by rotating it, and then display it by again setting the canvas's background property. It appears that I am mismanaging memory because the rotate/display operation works correctly for a few iterations but will eventually bomb complaining about insufficient memory.
NOTE: The image I'm using is a 5000x5000 pixel bmp.
I've tried everything from breaking references to calling garbage collection but it appears the canvas is still holding onto this data somehow.
Here is some sample code that I can reproduce the issue with.
Private variable declarations:
Private _SourceFileName As String = "C:\Users\sean\Desktop\Input.bmp"
Private _bmpSource As BitmapSource
This gets called in my constructor for the initial load/display of the image:
Me._bmpSource = Me.LoadImage()
Me._Canvas.Background = New System.Windows.Media.ImageBrush(Me._bmpSource)
LoadImage() implementation:
Private Function LoadImage() As BitmapImage
Dim bmpImage As New BitmapImage
Dim imageSourceStream As New FileStream(Me._SourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read)
Dim decoder As New BmpBitmapDecoder(imageSourceStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad)
Dim encoder As New JpegBitmapEncoder
encoder.Frames.Add(BitmapFrame.Create(decoder.Frames(0)))
Dim streamSourcePath As String = My.Computer.FileSystem.GetTempFileName
Dim Stream As New System.IO.FileStream(streamSourcePath, FileMode.OpenOrCreate, FileAccess.Write)
encoder.Save(Stream)
Stream.Close()
Stream = Nothing
bmpImage.BeginInit()
bmpImage.CreateOptions = BitmapCreateOptions.IgnoreImageCache
bmpImage.CacheOption = BitmapCacheOption.OnLoad
bmpImage.UriSource = New Uri(streamSourcePath)
bmpImage.EndInit()
Return bmpImage
End Function
And here is the RotateImage() implementation which runs behind a button click event:
Public Sub RotateImage()
' Store our source rect for use in applied transforms
Dim ImageRect As New System.Drawing.RectangleF(System.Drawing.PointF.Empty, New System.Drawing.SizeF(Me._bmpSource.Width, Me._bmpSource.Height))
' Store our target rect for use in applied transforms
' NOTE: For this demo we're always rotating 90 degrees so just flip height/width
Dim CanvasRect As System.Drawing.RectangleF = New System.Drawing.RectangleF(System.Drawing.Point.Empty, New System.Drawing.SizeF(Me._bmpSource.Height, Me._bmpSource.Width))
' Figure out the point in which we will need to draw the image
' to fit it within the bounds of our canvas rectangle
Dim WidthOffset As Decimal = (CanvasRect.Width - ImageRect.Width) / 2
Dim HeightOffset As Decimal = (CanvasRect.Height - ImageRect.Height) / 2
' Build transform needed for rotate
Dim transformActions As New System.Windows.Media.TransformGroup
transformActions.Children.Add(New System.Windows.Media.RotateTransform(90, Me._bmpSource.Width / 2, Me._bmpSource.Height / 2))
transformActions.Children.Add(New System.Windows.Media.TranslateTransform(WidthOffset, HeightOffset))
' Apply the transformation to the image
Dim transformedBitmap As New System.Windows.Media.Imaging.TransformedBitmap(Me._bmpSource, transformActions)
' Trying to break references here so things will get disposed but it isn't working
If TypeOf Me._bmpSource Is BitmapImage Then
CType(Me._bmpSource, BitmapImage).StreamSource = Nothing
ElseIf TypeOf Me._bmpSource Is TransformedBitmap Then
CType(Me._bmpSource, TransformedBitmap).Source = Nothing
End If
' Breaking main ref also doesn't help
Me._bmpSource = Nothing
' Garbage collection does nother either
GC.WaitForPendingFinalizers()
GC.Collect()
Me._bmpSource = transformedBitmap
Me._Canvas.Background = New System.Windows.Media.ImageBrush(Me._bmpSource)
Me._Canvas.UpdateLayout()
End Sub
GC somehow didn't have enough time to release memory. Add this code at the end of your method or in each loop.