Я уверен, что решение этого просто, но я остаюсь царапать голову, поэтому, возможно, кто-то может пролить свет здесь.Утечка памяти при обновлении фона холста WPF
Что я делаю, когда запускается моя программа Загружает входной файл в приватную переменную (_bmpSource) и отображает ее на холсте, создавая ImageBrush и устанавливая фон холста. Нажатием кнопки я изменяю изображение, поворачивая его, а затем отображаю его, снова устанавливая свойство фона холста. Похоже, что я неправильно управляю памятью, потому что операция поворота/отображения работает правильно для нескольких итераций, но в конечном итоге будет бояться жаловаться на недостаточную память.
ПРИМЕЧАНИЕ: Изображение, которое я использую, составляет 5000x5000 пикселей.
Я пробовал все: от перерыва ссылок на вызов коллекции мусора, но кажется, что холст все еще держится за эти данные.
Вот пример кода, с которым я могу воспроизвести проблему.
Частные объявления переменных:
Private _SourceFileName As String = "C:\Users\sean\Desktop\Input.bmp"
Private _bmpSource As BitmapSource
Это вызывается в моем конструкторе для начальной загрузки/отображения изображения:
Me._bmpSource = Me.LoadImage()
Me._Canvas.Background = New System.Windows.Media.ImageBrush(Me._bmpSource)
LoadImage() реализации:
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
И вот реализация RotateImage(), которая запускается за событием нажатия кнопки:
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
Вы можете избавиться от TranslateTransform, потому что вы говорите, что RotateTransform поворачивается по центру изображения. – Nick
Я верю, что если бы я центрировал изображение на холсте, тогда вы были бы правы. Он не показывает это в этом примере кода, но я фактически выравниваю вверху слева, и в этом случае необходим перевод. – user2719109