Diskussionsthread Zeichnen außerhalb des Paint-EventHandlers

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von Bluespide.

    Diskussionsthread Zeichnen außerhalb des Paint-EventHandlers

    Hattest Du schonmal einen Aufrufcounter für den Paint-EventHandler und Deine Methode gemacht, um zu sehen, ob es an der Häufigkeit des Aufrufs liegt? Deine Methode wird ja explizit aufgerufen, der Paint-EH implizit.

    @dive26, @Bluespide, @RodFromGermany und alle anderen:
    der Einfachheit her ausgelagert aus Zeichnen im .Paint Event wesentlich beschleunigen ~VaporiZed
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „VaporiZed“ ()

    @VaporiZed
    In meiner "alten" App habe ich den selben Zeichencode direkt im Paint-Event hinterlegt, ja.
    Aber da dauerte so eine Anzeige, je nach Rechnerleistung, schon manchmal ein paar Sekunden und man sah, wie sich die Linien aufbauten.

    Im neuen Code habe ich im Paint-Event nichts mehr drin, sondern befülle es über einen gezielten Aufruf einer Sub.
    Da ist die Grafik in einer zehntel Sekunde fertig auf dem Schirm.
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    Ok, dann anders rum: Was ist, wenn Du im Paint-EH nur Deine Sub aufrufst? Gibt das dann auch diesen Performanceschub oder ist das dann wie direkt im Paint-EH gezeichnet?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    dive26 schrieb:

    und man sah, wie sich die Linien aufbauten

    Das kann im Paint-Event eigentlich gar nicht sein. Es wird erst was angezeigt, wenn das Paint-Event fertig ist. Da musst du etwas anderes gemacht haben.

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

    @VaporiZednullEs macht keinen unterschied, ob ich direkt in der Paint-Routine die Befehle ausführe oder in der Paint-Routine eine Unterprozedur aufrufe (und dieser natürlich das graphics-Objetkt übergebe).nullHabe beides in meiner alten App versucht - kein Unterschied. Man kann zuschauen, wie sich die Grafik von links nach rechts aufbaut (weil ich diese auch so aus 1440 Einzelwerten erstelle).

    @Bluespide
    Ich habe jetzt genauer nachgesehen. Du hast vollkommen Recht.
    Ich rufe zwar (in meiner alten App) aus dem Paint-Event eine Funktion auf, aber die nimmt nicht das Graphics-Objekt aus der Paint-Routine, sondern erstellt ein neues.
    Using g As Graphics = PanelControl.CreateGraphics
    Ich habe das jetzt mal so umgebaut, dass ich das Graphics-Objekt des Paint-Events verwende - machte aber keinen Unterschied von der Geschwindigkeit.

    Im Paint-Event ist es dennoch langsamer als intern auf eine Bitmap zu zeichnen und diese dann auf einmal als BackgroundImage einzufügen.
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „dive26“ ()

    @dive26 Wenn Du die Bitmap ZeichenFlaeche vorhältst und nur noch die finalen Änderungen vornimmst, ist das Malen ggf. noch schneller.
    Kein permanentes Disposen und neues Erstellen, sondern nur noch ein Copy Image.
    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!

    dive26 schrieb:

    Es macht keinen unterschied, ob ich direkt in der Paint-Routine die Befehle ausführe oder in der Paint-Routine eine Unterprozedur aufrufe (und dieser natürlich das graphics-Objetkt übergebe).
    Das ist klar, aber wenn Du nur Deine neue Methode aufrufst, ohne den e-Parameter zu übergeben und ihn zu nitzen, dann ist eben die Frage, was passiert. Weil: Wenn es relevante Geschwindigkeitsunterschiede gibt, ist das relevant. Wenn nicht, scheint die Beschleunigung nur an der Tatsache zu liegen, dass es eben nicht im Paint-EH liegt, der aber eben implizit vom System aufgerufen wird. Dein bisheriger Weg ist das explizite Aufrufen, was ggf. zu Problemen bei GUI-Aktualisierungsnotwenigkeit mit sich bringt, z.B. Fensterverschiebung.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    @RodFromGermany
    Es ändert sich pro Report zu viel an der Grafik. Da bringt es nichts eine "vorgefertigte" Grafik vorzuhalten.
    Es sind ja keine Live-Reports, sondern Archiv-Reports. Wenn man die aufruft, dann werden die Grafiken erstellt.

    Ja korrekt. Statt das Image bei jeder Anzeige zu disposen, sollte man es einmalig in der gewünschten Größe erstellen und dann nur noch "übermalen".
    Das mache ich bei mir eh - passte aber im Beispielcode oben nicht - zu viele Infos ;)
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    Also die Bilder im Startpost des Threads sehen mir irgendwie wie echtzeit-Darstellungen aus. Sprcih permanenter Refresh ist nötig. Und je nachdem wie man den gestalltet, bekommt man die CPU auch zu schwitzen. Und die GPU würd ich jetzt auch nicht ganz ignorieren wollen.

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

    @Coldfire
    Nein, sind nur "Berichte".
    Also werden einmal aufgerufen und die Anzeige bleibt so lange bis man einen anderen aufruft.

    Hier gibt es ein kurzes Video davon: facebook.com/100002099452229/videos/355815800109238/
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    dive26 schrieb:

    Hier gibt es ein kurzes Video davon
    allerdings nur unter der Voraussetzung, dass man dort angemeldet ist. ;(
    Bin ich nicht und werde es auch nicht sein.
    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!
    Naja, da kommt man auch ohne Anmeldung ran.
    Hab mal selber was getestet, keine Ahnung, ob's sinnvoller Prüfcode ist.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Friend Class FrmMain
    2. Private Canvas As Bitmap
    3. Private ReadOnly Random As New Random
    4. Private ReadOnly Lines As New List(Of Point)
    5. Public Sub DrawIn(Panel As Panel)
    6. Panel.BackgroundImage = Nothing
    7. If Canvas IsNot Nothing Then Canvas.Dispose()
    8. Canvas = New Bitmap(Panel.Width, Panel.Height)
    9. Using g As Graphics = Graphics.FromImage(Canvas)
    10. g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
    11. g.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
    12. g.Clear(Color.Black)
    13. g.DrawLines(Pens.Green, Lines.ToArray)
    14. End Using
    15. Panel.BackgroundImage = Canvas
    16. End Sub
    17. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    18. Dim StartTime = Date.Now
    19. DrawIn(Panel1)
    20. Console.WriteLine("explizit: " & (Date.Now - StartTime).TotalMilliseconds)
    21. End Sub
    22. Private Sub FrmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    23. CreateLines()
    24. End Sub
    25. Private Sub CreateLines()
    26. For x = 0 To 1000
    27. Lines.Add(New Point(x, Random.Next(300) + 100))
    28. Next
    29. End Sub
    30. Private Sub Panel1_Paint(sender As Object, e As PaintEventArgs) Handles Panel1.Paint
    31. Dim StartTime = Date.Now
    32. Dim g = e.Graphics
    33. g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
    34. g.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
    35. g.Clear(Color.Black)
    36. g.DrawLines(Pens.Green, Lines.ToArray)
    37. Console.WriteLine("implizit: " & (Date.Now - StartTime).TotalMilliseconds)
    38. End Sub
    39. End Class

    Das Button-Klicken führt sowohl das neu vorgestellte als auch das klassische Zeichnen per Paint-EventHandler aus. Ergebnis: Das neue ist ca. doppelt so schnell. Bei mir und meinem alten PC heißt das: 25 ms statt 50 ms, bis das Bild fertig ist. Ist für mich also erstmal praxisirrelevant.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Coldfire schrieb:

    Mittelwerte fürdas Zeichnen von 1000,10000 und 100000 Linien
    Wenn mehrere gleichjfarbige Linien auf ein Pixel | eine Pixelreihe fallen. lässt sich da das Malen noch wesentlich beschleunigen.
    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!

    VaporiZed schrieb:

    Das Button-Klicken führt sowohl das neu vorgestellte als auch das klassische Zeichnen per Paint-EventHandler aus. Ergebnis: Das neue ist ca. doppelt so schnell.
    hmm.
    Also das Zeichnen in die Graphics einer Bitmap ist doppelt so schnell wie das Zeichnen in die Graphics eines Controls.
    Zu bedenken ist, dass die Bitmap dann ja nochmal zu zeichnen ist - nämlich in die Graphics des Controls. Das passiert mit kleiner Verzögerung, nachdem die Bitmap dem Control.BackgroundImage zugewiesen wird.
    Guter Einwand. Habe das Testprojekt nochmal verändert, hoffentlich zum Besseren. Da zeigt sich, dass der Unterschied im statistischen Rauschen untergeht.
    Dateien
    • DrawComparison.zip

      (18,88 kB, 293 mal heruntergeladen, zuletzt: )
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    So Leute. Ich muss meine Aussage nochmals bestätigen.

    Habe nämlich mal meine "alte" App überarbeitet.
    Da stand vorher direkt im Paint Event ein Verweis auf eine Sub die alle Zeichenoperationen vornimmt.
    In dieser Sub hatte ich alle Zeichenoperationen in diesen Using-Block geschrieben: ​Using g As Graphics = PanelControl.CreateGraphics.
    Ich habe im Paint-Event auch sichergestellt, dass diese Zeichen-Unterroutine nicht aufgerufen wird, solange die noch läuft.

    Nun habe ich als ersten Schritt die Zeichen-Sub aus dem Paint-Event des Panels entfernt (Paint-Event steht nun kein Code mehr) und über einen Button gestartet.
    Das Ergebnis war identisch langsam.

    Nun habe ich das ganze so umgebaut, dass zuerst alles in eine Bitmap gezeichnet wird und dann die Bitmap in das Panel als Hintergrund.
    ​Using g As Graphics = Graphics.FromImage(ZeichenFlaeche) (Zeichenfläche ist die Bitmap).
    Egal ob ich nun die Zeichenroutine über einen Button oder direkt im Paint-Event aufrufe. Das Ergebnis ist in beiden Fällen sofort angezeigt.

    Ich bleibe also dabei, dass das Zeichnen in eine Bitmap schneller ist als in ein Graphics-Objekt.

    Der gesamte Code ist zu komplex um ihn hier zu posten. Aber ganz oben im ersten Beitrag sieht man ja worum es geht.
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at
    Also meine Vermutung ist gerade eher, dass du dir sehr viel Zeit ersparst, dadurch dass du mit der anderen Methode weniger oft Zeichnest. Für mich ist das Beispiel auch nicht so ganz klar.

    Ich muss aber sagen, dass ich gerade bei einem Test auch festgestellt habe, dass das DrawLine auf einem Bitmap schneller ist, aber nur ca. 13%?

    C#-Quellcode

    1. ​public partial class Form1 : Form {
    2. public Form1() => InitializeComponent();
    3. private void button1_Click(object sender, EventArgs e) {
    4. this.pictureBox1.Invalidate();
    5. }
    6. private void pictureBox1_Paint(object sender, PaintEventArgs e) {
    7. double ms = MyOwnPaintEvent(e.Graphics);
    8. this.button1.Text = ms.ToString();
    9. }
    10. private void button2_Click(object sender, EventArgs e) {
    11. using (Bitmap bitmapBuffer = new Bitmap(this.pictureBox1.Width, this.pictureBox1.Height)) {
    12. using Graphics g = Graphics.FromImage(bitmapBuffer);
    13. double ms = MyOwnPaintEvent(g);
    14. this.button2.Text = ms.ToString();
    15. }
    16. }
    17. private double MyOwnPaintEvent(Graphics e) {
    18. Stopwatch sw = Stopwatch.StartNew();
    19. Random rnd = new Random(12345);
    20. for (int i = 0; i < 500_000; i++) {
    21. e.DrawLine(Pens.Black, rnd.Next(0, 100), rnd.Next(0, 100), 400, 400);
    22. }
    23. return sw.Elapsed.TotalMilliseconds;
    24. }
    25. }