Hoher RAM-Verbrauch bei Ändern von Deckkraft in PictureBox

  • VB.NET
  • .NET (FX) 3.0–3.5

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von Marcus Gräfe.

    Hoher RAM-Verbrauch bei Ändern von Deckkraft in PictureBox

    Ich habe einen Bildschirmschoner, der ein 768x250 Pixel großes Bild auf zwei verschiedenen Monitoren immer ein- und ausblendet. Für die Änderung der Deckkraft benutze ich folgende Funktion:

    VB.NET-Quellcode

    1. Public Function ChangeOpacity(ByVal img As Image, ByVal opacityvalue As Single) As Bitmap
    2. ' -----------
    3. ' Deckkraft eines Bildes ändern
    4. ' -----------
    5. If opacityvalue > 100 Then
    6. opacityvalue = 100
    7. ElseIf opacityvalue < 0 Then
    8. opacityvalue = 0
    9. End If
    10. Dim bmp As New Bitmap(img.Width, img.Height)
    11. Dim graphics__1 As Graphics = Graphics.FromImage(bmp)
    12. Dim colormatrix As New colormatrix
    13. colormatrix.Matrix33 = opacityvalue
    14. Dim imgAttribute As New ImageAttributes
    15. imgAttribute.SetColorMatrix(colormatrix, ColorMatrixFlag.[Default], ColorAdjustType.Bitmap)
    16. graphics__1.DrawImage(img, New Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imgAttribute)
    17. graphics__1.Dispose()
    18. Return bmp
    19. End Function

    Während dieses Fade-Effekts geht der RAM-verbrauch auf über 1 GB hoch. Kann man das irgendwie optimieren? Die Originalgrafik in der PictureBox ist ein 32Bit-PNG.
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum
    Hi @Marcus Gräfe,

    Marcus Gräfe schrieb:

    ein- und ausblendet
    Wenn du einfach nur den Alpha-Wert deiner Bilder runter und hoch schraubst, brauchst du kein 2test Bild. (Edit: Zurückgezogen...habs grad ausprobiert..funzt leider ned :( )
    Schau dir mal von @ThuCommix XPlan auf Github an. Da ist in der Klasse FadableText ein FadeIn/FadeOut Effekt mit dem Alphakanal implementiert

    Wichtig hierbei ist lediglich die Update-Methode, die FadeInVelocity/FadeOutVelocity,AnimationComplete-Property und das _flag (Zeigt an, ob ausgefaded oder eingefaded wird (k.A. ob das so geschrieben wird ;D))
    Hab daheim n Pong Projekt mit SGL liegen, in dem ich das FadIn/FadeOut abstrakt gemacht hab. Sprich: Per Property kann man festlegen, ob FadeIN ODER FadeOut ergfolgen soll.


    Marcus Gräfe schrieb:

    Änderung der Deckkraft
    Ich geh mal davon aus, dass sich das Ändern des Alpha-Kanals auch auf die Opacity anpassen lässt (Edit: Bin mir zwar sicher, dass des geht, bin aber bisher auch noch auf keine Lösung gestoßen :( )

    Lg Radinator
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Radinator“ ()

    nafets schrieb:

    musst du natürlich die alte disposen

    Dachte ich mir zwar schon, aber leider erhalte ich immer eine Exception, wenn ich das mache. Mir fehlt also die richtige Stelle für das Dispose().

    So rufe ich die Funktion aus Post #1 übrigens auf:

    VB.NET-Quellcode

    1. picBanner.Image = ChangeOpacity(imgOriginal, Convert.ToSingle(iCurrentOpacity) / 100)

    Und im Form_Load speichere ich die Originalgrafik zwischen:

    VB.NET-Quellcode

    1. imgOriginal = picBanner.Image

    Was ich nicht verstehe: Mache ich picBanner.Image.Dispose(), so stößt ChangeOpacity auf einen Fehler. Aber ChangeOpacity greift doch gar nicht auf die PictureBox zu?!

    nafets schrieb:

    Using wäre übrigens auch ne ganz praktische Sache.

    Sagt mir leider gar nichts.
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum
    Durch die Using Anweisung wird am Ende(End Using) automatisch disposed. Ganz praktisch, koennte mann schnell mal vergessen. Using Anweisungen kannst du fuer alles was System.IDisposeAble implementiert nutzen. An deinem Code scheint aber auch etwas fehlerhaft zu sein, der Wert fuer die Opacity muss in einer Range von 0 und 1 sein.

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private original As Image
    3. Private copy As Image
    4. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    5. original = Image.FromFile("D:\1.jpg")
    6. End Sub
    7. Public Function ChangeOpacity(ByVal img As Image, ByVal opacityvalue As Single) As Bitmap
    8. If opacityvalue > 1 Then
    9. opacityvalue = 1
    10. ElseIf opacityvalue < 0 Then
    11. opacityvalue = 0
    12. End If
    13. Using bmp As New Bitmap(img.Width, img.Height)
    14. Using g As Graphics = Graphics.FromImage(bmp)
    15. Dim colormatrix As New ColorMatrix
    16. colormatrix.Matrix33 = opacityvalue
    17. Dim imgAttribute As New ImageAttributes
    18. imgAttribute.SetColorMatrix(colormatrix, ColorMatrixFlag.[Default], ColorAdjustType.Bitmap)
    19. g.DrawImage(img, New Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imgAttribute)
    20. Return DirectCast(bmp.Clone(), Bitmap)
    21. End Using
    22. End Using
    23. End Function
    24. Private Sub TrackBar1_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TrackBar1.Scroll
    25. If Not copy Is Nothing Then
    26. copy.Dispose()
    27. End If
    28. copy = ChangeOpacity(original, CSng(TrackBar1.Value / 100))'Trackbar Maximum = 100
    29. BackgroundImage = Nothing
    30. BackgroundImage = copy
    31. End Sub
    32. End Class

    And i think to myself... what a wonderfuL World!
    Wenn du das Bild mit der aktuellen Opacity immer als Background-Image oä festlegen möchtest, würde ich es folgendermaßen machen:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports System.Drawing.Imaging
    3. Public Class MainForm
    4. Private Sub MainForm_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
    5. Me.DoubleBuffered = True
    6. Me.StartTime = DateTime.Now
    7. Me.RefreshTimer.Start()
    8. End Sub
    9. Private Image As Bitmap = My.Resources.Test
    10. Private StartTime As DateTime
    11. Private Duration As Integer = 1000
    12. Private Sub RefreshTimer_Tick(sender As Object, e As EventArgs) Handles RefreshTimer.Tick
    13. 'Opacity berechnen
    14. Dim progress As Single = (CSng((DateTime.Now - StartTime).TotalMilliseconds) Mod (Duration * 2)) / (Duration)
    15. Dim opacity As Single = If(progress <= 1, progress, (2 - progress))
    16. 'Speicher von der alten Bitmap freigeben, wenn schon eine festgelegt war
    17. If (Me.BackgroundImage IsNot Nothing) Then
    18. Me.BackgroundImage.Dispose()
    19. End If
    20. 'Bitmap mit gewünschter
    21. Me.BackgroundImage = Me.SetOpacity(Me.Image, opacity)
    22. End Sub
    23. Private Function SetOpacity(image As Image, opacity As Single) As Bitmap
    24. Dim bmp As Bitmap = New Bitmap(image.Width, image.Height)
    25. Using g As Graphics = Graphics.FromImage(bmp) 'Using Disposed die Graphics nach dem Gebrauch
    26. 'Matrix und Attribute erstellen
    27. Dim matrix As New ColorMatrix() With {.Matrix33 = opacity}
    28. Dim attributes As New ImageAttributes()
    29. attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
    30. g.DrawImage(image, New Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, attributes)
    31. Return bmp
    32. End Using
    33. End Function
    34. End Class

    Allerdings wäre eine bessere Möglichkeit, einfach das Bild im OnPaint auf die Form zu zeichnen, das würde dann folgendermaßen gehen:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports System.Drawing.Imaging
    3. Public Class MainForm
    4. Private Sub MainForm_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
    5. Me.DoubleBuffered = True 'Doublebuffering entfernt Flackern
    6. Me.StartTime = DateTime.Now
    7. Me.RefreshTimer.Start()
    8. End Sub
    9. Private Image As Bitmap = My.Resources.Test
    10. Private StartTime As DateTime
    11. Private Duration As Integer = 1000
    12. Private ImageOpacity As Single = 0
    13. Private Sub RefreshTimer_Tick(sender As Object, e As EventArgs) Handles RefreshTimer.Tick
    14. 'Opacity berechnen
    15. Dim progress As Single = (CSng((DateTime.Now - StartTime).TotalMilliseconds) Mod (Duration * 2)) / (Duration)
    16. Me.ImageOpacity = If(progress <= 1, progress, (2 - progress))
    17. 'Form neu zeichnen lassen
    18. Me.Invalidate()
    19. End Sub
    20. Protected Overrides Sub OnPaint(e As PaintEventArgs)
    21. 'Matrix und Attribute erstellen
    22. Dim matrix As New ColorMatrix() With {.Matrix33 = Me.ImageOpacity}
    23. Dim attributes As New ImageAttributes()
    24. attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
    25. 'Bild direkt auf die Form zeichnen
    26. e.Graphics.DrawImage(Me.Image, New Rectangle((Me.ClientSize.Width - Me.Image.Width) \ 2, (Me.ClientSize.Height - Me.Image.Height) \ 2, Me.Image.Width, Me.Image.Height), 0, 0, Me.Image.Width, Me.Image.Height, GraphicsUnit.Pixel, attributes)
    27. MyBase.OnPaint(e)
    28. End Sub
    29. End Class

    So sparst du dir das rumhantieren mit extra-Bitmaps und BackgroundImages oder ähnlichem und zeichnest immer direkt auf die Form, was performanter ist.

    Grüße
    Stefan
    OK, mein Beispiel ist ein wenig ungluecklich.

    Aber beim 2. Code finde ich es schoener(Using + bmp.Clone()). Waere das nicht so gut? Kann mir vorstellen dass das kopieren auch auf kosten von Performence geht. Habe da nie tests gemacht.

    VB.NET-Quellcode

    1. Dim bmp As Bitmap = New Bitmap(image.Width, image.Height)
    2. Return bmp

    VB.NET-Quellcode

    1. Using bmp As New Bitmap(img.Width, img.Height)
    2. Return DirectCast(bmp.Clone(), Bitmap)
    3. End Using

    And i think to myself... what a wonderfuL World!
    Es geht natürlich auf die Performance, wenn du ein Bild unnötig kopierst, zudem braucht es kurzzeitig auch mehr Speicher. Die Variante ohne ​Using finde ich daher in diesem Fall sauberer.
    @nafets
    Ich sehe bei deinem 1. Code allerdings keinen Unterscheid zu meinem.

    Zum einen:
    Du machst Me.BackgroundImage.Dispose(). Das führt, wie gesagt, bei mir zu einem Fehler in meiner ChangeOpacity-Funktion. Müsste dann bei dir doch theoretisch auch so sein.

    Zum anderen:
    Ich dispose graphics__1 bereits nach der Verwendung. Mag ja sein, dass das Using in dem Fall sicherer ist, aber es ist doch trotzdem kein echter Unterschied.

    Das zweite Beispiel werde ich mal ausprobieren, aber ich verstehe nicht, was Beispiel #1 am RAM-Verbrauch ändern könnte.
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum
    Ich glaube es liegt daran das du dem imgOriginal picBanner.Image zuweist, wenn du jetzt das picBanner.Image disposed ist auch imgOriginal Nothing. Daher hast du ungueltige Argument bei erstellen von bmp in deiner Funktion.

    In dem Fall macht kopieren wieder Sinn. Mach das in der Load-Methode so, dann muss das klappen da imgOriginal eine Kopie ist und diese nicht freigegeben wird.

    VB.NET-Quellcode

    1. imgOriginal = DirectCast(picBanner.Image.Clone, Image)

    And i think to myself... what a wonderfuL World!

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Eddy“ ()

    Selbst wenn ich das so mache, kommt bei Dim bmp As New Bitmap(img.Width, img.Height) in ChangeOpacity eine Fehlermeldung, weil img Nothing ist.
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum

    Marcus Gräfe schrieb:

    Unterschied
    Ich hab versucht das zu reproduzieren. das Bild dispose ich bei der Zuweisung des nächsten Bildes.
    Im Taskmanager rauscht es ein wenig, aber Deine Werte kann ich nicht reproduzieren.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Drawing.Imaging
    2. Public Class Form1
    3. Private MyBitmap As New Bitmap("C:\Temp\Stein.jpg")
    4. Dim value As Single = 0.0F
    5. Dim Delta As Single = 10.0F
    6. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    7. Me.Timer1.Enabled = Not Me.Timer1.Enabled
    8. End Sub
    9. Public Function ChangeOpacity(ByVal img As Image, ByVal opacityvalue As Single) As Bitmap
    10. ' -----------
    11. ' Deckkraft eines Bildes ändern
    12. ' -----------
    13. If opacityvalue > 100 Then
    14. opacityvalue = 100
    15. Delta = -Delta
    16. ElseIf opacityvalue < 0 Then
    17. opacityvalue = 0
    18. Delta = -Delta
    19. End If
    20. Dim bmp As New Bitmap(img.Width, img.Height)
    21. Using graphics__1 As Graphics = Graphics.FromImage(bmp)
    22. Dim colormatrix As New ColorMatrix
    23. colormatrix.Matrix33 = opacityvalue / 100.0F
    24. Dim imgAttribute As New ImageAttributes
    25. imgAttribute.SetColorMatrix(colormatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
    26. graphics__1.DrawImage(img, New Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imgAttribute)
    27. End Using
    28. Return bmp
    29. End Function
    30. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    31. Dim bmp = Me.ChangeOpacity(Me.MyBitmap, value)
    32. If (Me.PictureBox1.Image IsNot Nothing) Then
    33. Me.PictureBox1.Image.Dispose()
    34. End If
    35. Me.PictureBox1.Image = bmp
    36. value += Delta
    37. End Sub
    38. End Class
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Mokki schrieb:

    wie groß
    Marcus: 768x250 Pixel
    ich: 816x1232 Pixel. :D
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Also das kann ich nicht verstehen, wenn ich das so mache geht es, wenn ich aber im Load imgOriginal = picBanner.Image nutze anstatt Image.Clone() dann wird das mit einer System.ArgumentException quitiert.

    Spoiler anzeigen

    Quellcode

    1. Imports System.Drawing.Imaging
    2. Public Class Form1
    3. Private original As Image
    4. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    5. original = DirectCast(PictureBox1.Image.Clone(), Image)
    6. 'original = PictureBox1.Image
    7. End Sub
    8. Public Function ChangeOpacity(ByVal img As Image, ByVal opacityvalue As Single) As Bitmap
    9. If opacityvalue > 1 Then
    10. opacityvalue = 1
    11. ElseIf opacityvalue < 0 Then
    12. opacityvalue = 0
    13. End If
    14. Dim bmp As New Bitmap(img.Width, img.Height)
    15. Using g As Graphics = Graphics.FromImage(bmp)
    16. Dim colormatrix As New ColorMatrix
    17. colormatrix.Matrix33 = opacityvalue
    18. Dim imgAttribute As New ImageAttributes
    19. imgAttribute.SetColorMatrix(colormatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
    20. g.DrawImage(img, New Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imgAttribute)
    21. Return bmp
    22. End Using
    23. End Function
    24. Private Sub TrackBar1_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TrackBar1.Scroll
    25. PictureBox1.Image.Dispose()
    26. PictureBox1.Image = ChangeOpacity(original, CSng(TrackBar1.Value / 100))
    27. End Sub
    28. End Class

    And i think to myself... what a wonderfuL World!
    Ich habe den Fehler gefunden. Ich hatte an einer anderen Stelle noch ein picBanner.Image = imgOriginal, welches von einem früheren Versuch stammte, die Deckkraft zu ändern.

    Nun geht es und der RAM-Verbrauch ist bei max. 20 MB!

    Vielen Dank euch allen!
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum