GDI+ Alternativen für das Zeichnen einfacher 2D Objekten

  • C#

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von simpelSoft.

    GDI+ Alternativen für das Zeichnen einfacher 2D Objekten

    Hallo zusammen,

    bislang habe ich viele Controls auf kleiner Zeichenfläche entwickelt und alle mit GDI+ im Paint-Event gezeichnet.
    Alles unter WinForms und C#.
    So etwas z.B.:



    Nun muss ich auf einer Fläche von 1480x320 Pixeln zeichnen und da stoße ich bei schnellen Bildwechseln auf die natürlichen Grenzen von GDI+.
    Leider habe ich nach über einer Woche der Suche nach Alternativen den Faden verloren und finde nicht zum Ziel.
    Ausflüge in die Welt von Direct2D mittels geeigneter Wrapper, OpenGL (OpenTK) und BufferedGraphics werfen mehr Fragen auf als vorher.
    Wobei ich nicht mal weiß, ob BufferedGraphics überhaupt noch jemand verwendet...
    Viele Direct2D Projekte und erst recht OpenGl sind mir sehr unverständlich und für mich zu kompliziert.

    Ich habe mir auch .NET MAUI angeschaut (soll wohl Bestandteil von .NET6 werden!?), für mich leider völlig unverständlich und erschlagend.

    Ich bitte um Hilfe zur Entscheidungsfindung.
    Optimal wäre es, wenn ich ähnlich wie mit GDI+ zeichnen könnte, nur eben schneller.

    Vorgaben:
    • hardwarebeschleunigtes Zeichnen von 2D Objekten (bitte kein WPF vorschlagen, ich müsste sonst alle Projekte komplett umkrempeln und die Zeit habe ich leider nicht);
    • nur WinForms, entweder mit dem .NET-Framework oder .NET5;
    • einfache 2D Objekte (Linien, Kreise, Kreissegmente, Rechtecke) mit Kantenglättung;
    • optimal wäre ein ähnlicher Workflow wie bei GDI+;
    Ich würde mich sehr über Anregungen freuen!

    simpelSoft schrieb:

    und alle mit GDI+ im Paint-Event gezeichnet.
    Verstehe ich Dich richtig:
    Wenn auf Deiner GUI diese 4 Objekte vorkommen, werden die im Paint-Event der MainForm dargestellt
    oder
    Jedes dieser 4 Objekte ist ein eigenes UserControl, das separat dargestellt wird?
    ====
    Ich würde zunächst die zweite Variante präferieren.
    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!

    simpelSoft schrieb:

    auf einer Fläche von 1480x320 Pixeln
    Wie viele Deiner Controls sind denn da drauf?
    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!

    RodFromGermany schrieb:

    Wie viele Deiner Controls sind denn da drauf?


    Ein einzelnes Control auf der gesamten Fläche (Spektrumanalyse mit 512 Bins).

    PS: bitte nicht falsch verstehen, ich suche keine Lösung mit GDI+, sondern eine hardwarebeschleunigte Lösung, da der PC, auf dem das dann läuft, ein recht schwachbrüstiges Exemplar ist - dafür aber winzig ;o)

    Bluespide schrieb:

    SkiaSharp


    Danke!
    Habe ich mir auch schon angeschaut und verstehe es einfach nicht.
    Mein Haputproblem ist bei diesen ganzen Lösungen, dass alle Beispiele so verd** kompliziert sind und mehr in die Richtung 3D gehen (davon verstehe ich nichts).
    Wenn es denn in diesen fetten Paketen ein einfaches Beispiel für Winforms zum Zeichnen von einfachen 2D Objekten geben würde ...

    Gibt tatsächlich einen Wrapper


    Danke, das schaue ich mir an :thumbsup:

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

    Takafusa schrieb:



    Habe den Wrapper als NuGet-Paket im Testprojekt, das wars auch schon 8| .
    Keine Beispiele, keine Tuts zum Einbinden von SDL selbst in C#/Winforms und VS2019.
    Ich komm einfach nicht zurecht damit.
    Gibts denn nichts zum "sofort benutzen"?

    Hier muss es doch User geben, die Alternativen zu GDI+ benutzen und ein Miniprojekt zum anschauen haben?
    Sozusagen ein paar Linien, Kreise und Rechtecke, mehr brauch ich nicht.
    Träum... :)
    Wie @Bluespide schon sagte: im Visual Studio eigenen Nuget-Paket-Manager SkiaSharp herunterladen. Damit kannst du direkt in die Bytes der Bitmaps schreiben. Mit nem guten PC läuft das ganze fast in Echtzeit.
    Hier ein Beispiel aus einem meiner Projekte:

    In Form1.vb (Auszug)

    VB.NET-Quellcode

    1. Private Async Sub FormMain_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
    2. Select Case e.KeyCode
    3. Case Keys.W
    4. If hemisphere.Theta > 0US Then
    5. hemisphere.Theta -= 1US
    6. find_Vector()
    7. Await hemisphere.calculate_Async()
    8. PictureBox1.Image = ClassHemisphere.displayedBitmap
    9. End If


    In der Klasse diese Funktion haben:

    VB.NET-Quellcode

    1. Public Async Function calculate_Async() As Task(Of Boolean)
    2. Return Await Task.Run(Function() Verarbeitung())
    3. End Function

    welche diese aufruft:

    VB.NET-Quellcode

    1. Private Function Verarbeitung() As Boolean
    2. displayedBitmap = Nothing
    3. Dim imageInfo As New SKImageInfo(FormMain.PictureBox1.Size.Width, FormMain.PictureBox1.Size.Height)
    4. Using surface As SKSurface = SKSurface.Create(imageInfo)
    5. Using canvas As SKCanvas = surface.Canvas
    6. canvas.Translate(300.0F, 300.0F)
    7. Using DarkBlue As New SKPaint With {
    8. .TextSize = 64.0F,
    9. .IsAntialias = True,
    10. .Color = New SKColor(0, 0, 122),
    11. .Style = SKPaintStyle.Fill
    12. }
    13. Using BrightYellow As New SKPaint With {
    14. .TextSize = 64.0F,
    15. .IsAntialias = True,
    16. .Color = New SKColor(255, 255, 77),
    17. .Style = SKPaintStyle.Fill
    18. }
    19. Using Magenta As New SKPaint With {
    20. .TextSize = 64.0F,
    21. .IsAntialias = True,
    22. .Color = New SKColor(255, 0, 115),
    23. .Style = SKPaintStyle.Fill
    24. }
    25. 'hier dein Zeichnen-Code
    26. 'zum Beispiel
    27. Dim SKPoints0 As New SKPoint(projected_Point.X + 2.0F, projected_Point.Y + 2.0F)
    28. Dim SKPoints1 As New SKPoint(projected_Point.X + 1.0F, projected_Point.Y + 1.0F)
    29. Dim SKPoints2 As New SKPoint(projected_Point.X, projected_Point.Y)
    30. Dim SKPoints3 As New SKPoint(projected_Point.X - 1.0F, projected_Point.Y - 1.0F)
    31. Dim SKPoints4 As New SKPoint(projected_Point.X - 2.0F, projected_Point.Y - 2.0F)
    32. canvas.DrawPoints(SKPointMode.Lines, {SKPoints0, SKPoints1, SKPoints2, SKPoints3, SKPoints4}, BrightYellow)
    33. '–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    34. ' get the data into displayedBitmap because the PictureBox is only accepting an usual System.Drawing.Bitmap.
    35. '–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    36. Using image As SKImage = surface.Snapshot()
    37. Using data As SKData = image.Encode(SKEncodedImageFormat.Png, 100)
    38. Using mStream As New IO.MemoryStream(data.ToArray())
    39. displayedBitmap = New Bitmap(mStream, False)
    40. End Using
    41. End Using
    42. End Using
    43. End Using
    44. Return True
    45. End Function


    displayedBitmap ist ein klassenweites System.Drawing.Bitmap. Sobald dieses beschrieben wurde, kannst du es außerhalb dieser Prozedur der PictureBox zuweisen. Die Funktion kann asynchron laufen.

    VB.NET-Quellcode

    1. Public Shared displayedBitmap As System.Drawing.Bitmap


    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Bartosz“ ()

    simpelSoft schrieb:

    Hier muss es doch User geben, die Alternativen zu GDI+ benutzen und ein Miniprojekt zum anschauen haben?

    Ich würde sagen in der Regel reicht GDI+ und wenn es dann größer werden soll, dann aber richtig.
    Vielleicht reicht GDI+ bei dir ja auch, wenn du den Code etwas optimierst. Du musst ja nicht immer alles neu Zeichnen. Du könntest selber einen Buffer verwenden und nur die Sachen neu Zeichnen, die sich auch verändern. Aber das hängt dann jetzt davon ab, wie genau dein Control aussieht. ​1480x320 Pixeln klingt für mich in GDI+ noch machbar.

    simpelSoft schrieb:

    das wars auch schon .


    Hihi, viel mehr ist auch nicht zu machen, ist fast gleich wie in C++, mit SetParent(WinAPI) kannst du ein Borderless SDLWindow deinen Forms/Controls hinzufügen. Du musst die x86 und oder die x64 SDL2 dll runterladen, ds nugetpacket SDL2-CS installieren, dann auf x64 oder x86 umstellen, evtl. einmal das Projekt starten damit Debug/release Ordner erstellt werden, die SDL2.DLL dorthin kopieren, dann ein wenig Code, siehe Anhang.

    Du kannst dir C++ Code ansehen, da gibt es ohne ende Beispiele. Alles was du brauchst ist im Wrapper vorhanden, ausser SetParent(siehe dazu Pinvoke.net).
    Bilder
    • Unbenannt.jpg

      116,93 kB, 1.123×556, 85 mal angesehen

    Bartosz schrieb:

    Damit kannst du direkt in die Bytes der Bitmaps schreiben.


    Wunderbar!
    Danke für die Erklärung, das hat mir geholfen, ich kann schon mal zeichnen:



    Nun guck ich mir noch SDL genauer an und dann mach ich mal ein paar Speedtests.

    Nachtrag: für mich ist es nun Skia geworden und ich bin vom Workflow begeistert. DANKE an alle Mitdenker, auch wenn ich mich nun für Skia entschieden habe ;) !

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

    Hi

    Freut mich das Du ein entsprechendes Paket gefunden hast mit dem Du arbeiten kannst. Ich war schon drauf und dran ein einfaches Direct2D Sample, ohne zusätzliche Verweise und irgendwas herunterladen laden zu müssen, zu basteln. Vllt ein anderes mal.
    Mfg -Franky-
    Ich glaube, nun ist das Ziel erreicht :thumbsup: .
    Die Kombination aus Skia und OpenTK ist ja grandios schnell.
    Wenn man statt in eine PictureBox in das dafür vorgesehene und hardwarebeschleunigte OpenGL-Control rendert, flitzt die Kiste so richtig.
    1000 dicke Linien mit Kantenglättung und Alphakanal in 4,3ms ist mehr als ich erwartet habe - bin damit sehr glücklich :) .
    Die 4,3ms beziehen sich auf eine Anzeigegröße von 1480x320px.

    Adios GDI+ (hast mich lange genug begleitet, danke dafür)!



    PS: Wenn das Jemand mal nachvollziehen möchte, könnte ich die Erstellung des Projekts und den Code hier beschreiben. Als Dateianhang ist das zu groß, denn die NuGet-Pakete sprengen den Rahmen der erlaubten Größe. Einfach Bescheid sagen...

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

    simpelSoft schrieb:

    Wenn das Jemand mal nachvollziehen möchte, könnte ich die Erstellung des Projekts und den Code hier beschreiben. Als Dateianhang ist das zu groß, denn die NuGet-Pakete sprengen...

    Du könntest inne Tuts oder im SourceCode-Austausch ein SampleProjekt uploaden.
    Nuget-Pakete gehören da nicht hinein - die eingebundene package.config genügt.
    Dann kann man via Nuget die Pakete selber ziehen.

    Aber selber ausprobieren, obs wirklich klappt.
    Sample-Projects, die garnet lauffähig sind, sind total ärgerlich, weil man verschwendet Stunden über Stunden, um rauszukriegen, wie's geht, was garnet geht.

    ErfinderDesRades schrieb:


    Du könntest inne Tuts…


    Ich habe einen anderen Vorschlag, der Kollege Scott W Harden kann das viel besser als ich!
    Durch seine Beispielprojekte habe ich in kurzer Zeit viel gelernt.
    Man muss sich nur die Beispiele aus dem gesamten Repo zu Winforms/Skia/OpenTK heraussuchen und kann hervorragend daraus lernen (oder eben die Technologie heraussuchen, mit der man arbeitet).
    Und dann meistens auch noch ein paar NuGet Paket aktualisieren, dann gehts prima.
    Interessant sind auch die Vergleiche von Skia zu GDI+ und WPF.

    Und eine Website hat er auch, da stehen sehr viele Infos zu diesem Repo: LINK

    Für den Beginn reicht auch dieses Tut: LINK
    Und hier als letzter Beitrag in diesem StackOverflow-Thread gibt es noch den totalen Knaller: LINK
    Unbedingt nachbauen (ist ganz einfach, steht alles im Text), das lohnt sich!
    Da wird asynchron auf mehrere Layer gezeichnet, da habe ich für mein derzeitiges Projekt viele Inspirationen her.

    Beste Erfolge!