Paint Event beschleunigen?

  • VB.NET

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von Fibonacci.

    Paint Event beschleunigen?

    Hey,

    ist es möglich, das Paint Event zu beschleunigen? Momentan zeichne ich viel mit GDI auf meiner Form herum und wenn ich die Größe verändere dauert es eine gefühlte halbe Sekunde, bis der Krempel neu gezeichnet ist.

    Mit freundlichem Gruß,
    Fib
    Der Übersetzer funktioniert grad nicht Richtig, aber hoffentlich hilft dir das trotzdem...

    VB.NET-Quellcode

    1. public class DrawOptimizedPanel : Panel
    2. {
    3. public DrawOptimizedPanel()
    4. {
    5. SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
    6. }
    7. }


    Ich glaube schon, ja.
    Jo; läuft. Danke.


    Edit: Hier einmal in VB.NET übersetzt (scheint der oben genannten Eigenschaft zu gleichen):

    Quellcode

    1. SetStyle((ControlStyles.AllPaintingInWmPaint Or (ControlStyles.OptimizedDoubleBuffer Or ControlStyles.UserPaint)), True)
    Aber übrigens die Geschwindigkeit der OnPaint-Routine hängt viel häufiger mit anderen Sachen als dem eigentlichen Zeichnen zusammen. Wie z.B mit komplexen Berechnungen innerhalb der Routine, Invalidieren an ungeeigneten Orten etc.
    DoubleBuffered unterbindet das zwar meistens, aber bei komplexeren Zeichnungen hast du dann halt immer mehr Perfomance Verlust.
    /nicht getestet
    @ErfinderDesRades:
    Ja; wenn auch nur gering (aber besser als vorher).


    @rotherford:
    Sind die Geschwindigkeitsverluste bei Rechtecken und Linien schon gravierend? Ich glaube (und hoffe) eher nicht. Würde ein Farbverlauf großen Einfluss nehmen?
    ein farbverlauf, wenn es immer derselbe ist, oder fonts, oder bitmaps, bei denen sich nichts ändert einmal laden und einfach immer wieder verwenden, aber du könntest uns auch mal deine methode zeigen...
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Ich denke, du solltest wirklich deine Paint-Methode mal zeigen.
    Ich zb. male ein bildschirmgroßes Form mit vlt. 50 oder 100 Figuren voll, und das flackert vlt. beim resizen, aber bestimmt keine gefühlte halbe Sekunde.

    Mein Prinzip dabei ist, die Figuren in GraphicsPathes vorzubereiten, und die sind dann komplett mit einem Befehl aufs Form gebatscht:

    VB.NET-Quellcode

    1. e.Graphics.DrawPath(myPen, myGraphicsPath)
    2. 'oder
    3. e.Graphics.FillPath(myBrush, myGraphicsPath)
    jeder GraphicsPath kann allerdings nur mit einem Pen oder einem Brush gebatscht werden, also pro Farbe oder Textur oder Farbverlauf braucht man einen eigenen GraphisPath.
    Jdfs. meine ZeichenRoutinen tun wirklich nur zeichnen, einen GraphicsPath nach dem anneren, und das wars auch schon.

    Alternativ kann man das neuzeichnen auch aussetzen bis zum Abschluß des Resizing-Vorgangs. Dann gehen die Figuren zwar nicht kontinuirlich mit, aber's klappert nicht so epilepsie-auslösend ;)
    Ok; hier bitte sehr:

    VB.NET-Quellcode

    1. Private Sub Draw_Shape(ByVal Image As PictureBox, ByVal Shape As Shape, ByVal Size As Size, ByVal Color As Color)
    2. Dim bmp As New Bitmap(Size.Width, Size.Height)
    3. Dim g As Graphics = Graphics.FromImage(bmp)
    4. g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
    5. g.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit
    6. Dim rect As New Rectangle(0, 0, Size.Width, Size.Height)
    7. Dim lgb As New LinearGradientBrush(rect, Color.FromArgb(100, 100, 100), Color.FromArgb(95, 95, 95), LinearGradientMode.Vertical)
    8. g.FillRectangle(lgb, rect)
    9. g.DrawString("blub", New Font("Segoe WP", 32, FontStyle.Regular), Brushes.SeaGreen, New Point(200, 175))
    10. g.SmoothingMode = Drawing2D.SmoothingMode.None
    11. g.DrawRectangle(New Pen(New SolidBrush(Color.FromArgb(1000, 100, 100))), New Rectangle(0, 0, Me.Width, Me.Height ))
    12. If IsMaximized = False Then
    13. g.SmoothingMode = Drawing2D.SmoothingMode.None
    14. g.FillRectangle(New SolidBrush(Color), New Rectangle(0, 0, 1, 1))
    15. g.FillRectangle(New SolidBrush(Color), New Rectangle(0, 1, 1, 1))
    16. g.FillRectangle(New SolidBrush(Color), New Rectangle(0, 10, 1, 1))
    17. g.FillRectangle(New SolidBrush(Color), New Rectangle(0, 11, 1, 1))
    18. g.FillRectangle(New SolidBrush(Color), New Rectangle(0, 100, 1, 1))
    19. g.FillRectangle(New SolidBrush(Color), New Rectangle(0, 110, 1, 1))
    20. End If
    21. Image.BackgroundImage = bmp
    22. End Sub

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

    jo - is doch horror.
    da wird in jedem Paint eine Riesen-Bitmap erzeugt, davon ein Graphics gebildet, ins Graphics gemalt, und dann das BackgroundImage der Picbox ausgetauscht.
    Statt gleich in die Graphics zu malen, die die PaintEventargs eines PaintEvents dir liefern.

    Ausserdem werden 6 SolidBrushes gebildet, mit denen 6 Rechtecke verfüllt werden.
    Die könnten alle bereits in einem GraphicsPath angelegt sein, die Rechtecke, und der wäre dann mit einem einzigen Brush und einem einzigen Aufruf hingebatscht.

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

    ich hab das mal getestet mit dem massenhaften Erzeugen von Bitmaps, die nicht mehr freigegeben werden:
    Sone Anwendung bläst sich gewaltig auf (innerhalb von Sekunden), bis irgendwann nicht mehr weitergeht, und der GarbageCollector quasi einen Befreiungsschlag ausführt.
    Merkt man inne Useability erstaunlicherweise kaum, aber sauber ist natürlich was anneres.

    @TE: Das müssteste sogar direkt nachgucken können: Starte das Teil mal, und lass dabei den Taskmanager laufen.
    @ErfinderDesRades:
    Werde dann mal alles umbasteln. Bin schon auf das Ergebnis gespannt. Danke für die Hilfe! (Und alles Gute zum 1.000sten hilfreichen Post!)


    @xtts02:
    Whoops; gut, dass du es sagst. Gerade mal angetestet: Im Ruhestand: 20.000 - 25.000 K; nach ein bisschen Laufzeit (mit unzähligen Re-Paints): 170.000 K; Zwischendurch beim Zeichnen: 380.000 K.
    Das sollte mir zu denken geben... ;)
    Du kannst auch einen anderen Weg gehen. Dort, wo das Update-Event aufgerufen wird, rufst Du eine Routine zum Erstellen eines neuen Hintergrundbildes auf, die muss nichts mit Paint zu tun haben. So kannst Du mit Paint noch diverse Farbtupfen auf dieses Bild setzen.
    Und:
    Du musst nicht permanent New SolidBrush(), den kannst Du separat bereitstellen.
    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!
    @xtts02:
    Reicht es eigentlich, folgendes zu disposen? Oder noch etwas?

    VB.NET-Quellcode

    1. Dim bmp As New Bitmap(Size.Width, Size.Height)
    2. Dim g As Graphics = Graphics.FromImage(bmp)
    3. g.Dispose()


    @RodFromGermany:
    Ok; habe jetzt erstmal alle SolidBrushes auf eine Variable reduziert. Meinst du, ich solle den Rest in meiner Prozedur so beibehalten? Oder ggf. Teile davon in das Paint Event auslagern?
    Das alles kannsdt Du unabhängig von Paint machen, rufe doch den Code einfach in einer Button_Click auf.
    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!
    ständig neue Bitmaps zu erzeugen ist keine gute Option - wenn die disposed werden, ist das zwar netter zu den System-Resourcen, aber Performance bringt das nicht.

    Fibonacci schrieb:

    Werde dann mal alles umbasteln. Bin schon auf das Ergebnis gespannt.

    mach erstmal ein Gesamt-Backup der Source.
    Weil der Umbau ist erheblich.


    und schnell mal basteln ist vmtl. auch nicht, weil du musst deine ganze Denke umstellen. Weil bei OwnerDrawing gibts kein Bild mehr, sondern nurnoch Objekte mit Zeichnungs-Informationen.
    Und diese Objekte musste selbst programmieren - wenns sauber ablaufen soll, müssen die folgendes können:
    • jederzeit die aktuellen Abmaße als Rectangle ausgeben. Also nur die Location, wo gemalt wird, reicht nicht
      zB eine Methode .GetBounds() As Rectangle
    • sich jederzeit neu malen können - zB eine Methode DrawObject.Draw(g As Graphics)
      Diese Methode kann dann vom PaintEvent für alle DrawObjekte aufgerufen werden.
    • jederzeit ihren Status ändern
      Status kann beinhalten: Position, Drehung, Streckung, und natürlich beliebige Daten, die anzuzeigen sind.
    Von diesen Objekten machste dir eine List(Of DrawObject), und im Paint werden einfach alle DrawObjecte aufgefordert, sich zu zeichnen.

    Also um den Status zu ändern machstedann 3 Schritte
    1. me.Invalidate(drawObject.GetBounds()) 'Paint an der alten Position auslösen, dass dort gelöscht wird
    2. Änderungen am Status
    3. me.Invalidate(drawObject.GetBounds()) 'Paint an der neuen Position auslösen, dass dort neu gezeichnet wird