Beim Klick auf mein Panel wird Pixel für Pixel ein Farbwert gezeichnet.
Ich finde C# in dieser Hinsicht recht lahm, 300x200 Pixel dauern mehrere Sekunden auf einen flotten Rechner!! 8o (Java ist da sogar viel viel schneller). Wenn ich Punkt für Punkt mittels fillRectangle zeichne, dann gibt es leider auch kaum Geschwindigkeitsunterschiede! Gibt es eine schnellere Methode Punkt für Punkt zu zeichnen?
Vor allem wird dummerweise der Bildinhalt verloren, wenn das Fenster auserhalb des Monitors verschoben wird! 🙁Kann man das gezeichnete auf einen Schlag wiederherstellen? Wie?
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
Graphics graph = panel1.CreateGraphics();
Bitmap bmp = new Bitmap(301, 201);
for (int i = 0; i <= 300; i = i + 1)
{
for (int j = 0; j <= 200; j = j + 1)
{
//in Wirklichkeit wird hier Pixel für Pixel ein anderer Code gezeichnet, der Code-Einfachheit halber aber:
bmp.SetPixel(i, j, Color.FromArgb(255, 250, 0));
}
}
graph.DrawImage(bmp, 0, 0);
graph.Dispose();
}
Hallo sharp,
siehe hierzu das Tutorial [Tutorial] Zeichnen in Windows-Programmen (Paint/OnPaint, PictureBox) sowie GetPixel und SetPixel um Längen geschlagen. 800 mal schneller
Cool, danke. (Ja wie, da hat ein Privatmann einfach so selbst einen Code geschrieben der um Größenordnung schneller als MIcrosofts Code ist? Wieso macht Microsoft es nicht gleich richtig?)
Dort steht: "GDI32 GetPixel und SetPixel sind ebenfalls so schnell"
Ich kenn aber nur GDI+. Habe Visual Studio 2005.
Wie krieg ich GDI32? Ist es viel Aufwand?
Hallo sharp,
Ich kenn aber nur GDI+.
GDI oder GDI+: hier ist sowieso das gleiche damit gemeint.
Wie krieg ich GDI32? Ist es viel Aufwand?
Du kannst mit DllImport auf als Win32-Funktionen durchgreifen. Siehe www.pinvoke.net
Viel Aufwand nicht, aber eher unschön/unnötig.
herbivore
Danke für die Antwort.
Habe mr jetzt [Tutorial] Zeichnen in Windows-Programmen (Paint/OnPaint, PictureBox) angeschaut. Ich sehe dort aber leider nicht eine Beschreibung wie der Bildinhalt restauriert werden kann? 🙁 (Ich will ja übrigens nicht irgendwelche Kreise und so Sachen restaurieren, sondern Millionen von Pixeln!!)
In welcher Post ist dort die Lösung beschrieben?
Hallo sharp,
In welcher Post ist dort die Lösung beschrieben?
in allen. In allen Beispielen in sichergestellt, dass der Bildinhalt "restauriert" wird. Es ist außerdem genau beschrieben, wie das funktioniert. Du kannst dir also eine Vorgehensweise aussuchen.
herbivore
Mit picturebox hat es nach viel probiererei geklappt 🙂
Jetzt hab ich nur noch das Problem wie ich GDI32 importiere?
Egal an welcher Stelle ich es im Code einfüge, ich krieg immer wegen der [DllImport]-Zeile Compilerfehler. 🙁
public Form1()
{
[DllImport("gdi32.dll")]
static extern uint SetPixel(IntPtr hdc, int X, int Y, uint crColor);
// [DllImport("gdi32.dll")]
// public static extern int PtInRegion(int hRgn, int x, int y); //so hats auch net gefuntzt
InitializeComponent();
}
Hallo sharp,
ich krieg immer wegen der [DllImport]-Zeile Compilerfehler.
welchen? Siehe [Hinweis] Wie poste ich richtig? Punkt 5. Siehe außerdem [Hinweis] Syntaxfehler selbst lösen (Compilerfehlermeldungen).
herbivore
Puh, 10 Stellen werden in dieser Zeile syntaktisch rot markiert!!
[DllImport("gdi32.dll")] static extern uint SetPixel(IntPtr hdc, int X, int Y, uint crColor);
[DllImport => "; expected"
] => "invalid expression
static extern uint => jeweils "invalid expression term"
( => "expected ; or ="
hdc => ") expected"
und es geht so weiter an den anderen Stellen...
Ich hab die Zeile jetzt oberhalb von
public Form1()
geschoben, und krieg endlich keine Compilerfehler! (Merkwürdig, dachte, dass ich diese Stelle eigentlich durchprobiert hatte).
Dummerweise zeichnet er den Pixel nicht in meine PictureBox, sondern ins Panel!
Graphics vGraphics = Graphics.FromHwnd(Handle);
SetPixel(vGraphics.GetHdc(), 10, 10, (uint)ColorToRGB(Color.Green));
Habe mir erhofft, dass so das grüne Beispielpixel in meine Picturebox gezeichnet wird, aber damit zeichnet er scheinbar überhaupt nichts! X( Hier der Code:
Graphics vGraphics = Graphics.FromImage(pictureBox1.Image);
SetPixel(vGraphics.GetHdc(), 10, 10, (uint)ColorToRGB(Color.Green));
Und das find ich noch merkwürdiger, wieso stützt er ab! Aber ich will ja viele Pixel zeichnen!! X(
Graphics vGraphics = Graphics.FromHwnd(Handle);
SetPixel(vGraphics.GetHdc(), 10, 10, (uint)ColorToRGB(Color.Green));
SetPixel(vGraphics.GetHdc(), 10, 10, (uint)ColorToRGB(Color.Green)); //aufgrund dieser doppelt vorkommenden Zeile stützt Programm!
Ich möchte also viele Pixel zeichnen, und diese in meine Picturebox zeichnen, aber wie?
Das war mein urspünglicher Code, nur das Zeichnen war halt sehr langsam...:
Graphics graph = Graphics.FromImage(pictureBox1.Image);
graph.FillRectangle(...,...,...,1,1); //Punkt zeichnen
Hier nochmals der direkte Gdi32 Link mit der Dll: http://www.pinvoke.net/default.aspx/gdi32.SetPixel
Hallo sharp,
ich muss nochmal auf [Tutorial] Zeichnen in Windows-Programmen (Paint/OnPaint, PictureBox) verweisen. Zeichne entweder mit e.Graphics im OnPaint oder mit Graphics.FromImage direkt in das Bild der PictureBox. Alles andere ist Mist.
herbivore
oder mit Graphics.FromImage direkt in das Bild der PictureBox.
Das hab ich ja auch wie oben beschrieben versucht:
Graphics vGraphics = Graphics.FromImage(pictureBox1.Image);
nur so zeichnet er garnichts, wie oben beschrieben! 🙁
Hallo sharp,
in meinem Tutorial steht aber Code mit dem es geht. Vermutlich fehl bei dir das Invalidate.
herbivore
Ich habs ohne Invalidate gemacht (brauch ich doch auch nicht?), hab meinen Zeichnen-Code hier eingefügt:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
}
In diesen Code hab ich ja auch net Invalidate verwendet, und der Bildinhalt ist schlagartig da, wenn das Fenster wieder zurückgeschoben wird:
funtzt, Bildinhalt wird tatsächlich schlagartig restauriert:
Graphics graph = Graphics.FromImage(pictureBox1.Image);
graph.FillRectangle(brush, ... , ... , 1, 1); //Nachteil: ewig langsames zeichnen...
funtzt, aber mit deinem beschriebenen Nachteil: Bildinhalt wird nicht restauriert...
Graphics vGraphics = pictureBox1.CreateGraphics();
IntPtr intPtr = vGraphics.GetHdc();
SetPixel(intPtr, 10, 10, (uint)ColorToRGB(Color.Green));
SetPixel(intPtr, 10, 10, (uint)ColorToRGB(Color.Green)); //Programm stürtzt jetzt übrigens nicht mehr wie vorher ab, da ich intPtr verwende!
Übrigens, wie man sieht, eine Sache konnte ich hier ausnahmsweise sogar selber lösen: ich verwende nicht mehr "vGraphics.GetHdc()" als Argument, sondern intPtr, damit kann ich beliebig viele Pixel zeichnen.
Ich versteh's zwar überhaupt nicht, aber es läuft.
Wieso funtzt das net: 🙁
(Denn wenn so klappen würde, hätte ich ja die schlagartige Bildrestaurierungsfunktion gepaart mit dem schnellen setpixel-zeichnen.
Graphics vGraphics = Graphics.FromImage(pictureBox1.Image);
IntPtr intPtr = vGraphics.GetHdc();
SetPixel(intPtr, 10, 10, (uint)ColorToRGB(Color.Green));
Hallo sharp,
ich kann dich nur nochmal auf das Tutorial verweisen. Hast du das überhaupt richtig gelesen? Ich denke, du hast die Zeichenlogik in Windows noch nicht verstanden. Es reicht eben nicht, im MouseDown zu zeichnen.
Außerdem ist CreateGraphics Mist. Steht alles im Tutorial.
herbivore
Ok, werd ich mir mal genauer durchlesen.
Aber trotzdem, die Sache find ich irgendwie voll unlogisch, denn das funktioniert ja prima mit der sofortigen Bildinhalt-restaurierung:
Graphics graph = Graphics.FromImage(pictureBox1.Image);
graph.FillRectangle(brush, ... , ... , 1, 1); //Nachteil: ewig langsames zeichnen...
Und wenn es funktioniert, dann ist es doch egal ob ichs fälschlicherweise in MouseDown zeichne, oder?
Hallo sharp,
nein, es ist nicht egal, wo du zeichnest. Und CreateGraphics ist Mist. Aber wir drehen uns im Kreis. Es steht alles im Tutorial.
herbivore
Ok, dort steht man soll im Paint oder OnPaint zeichnen, habe also Paint genommen:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics graph = Graphics.FromImage(pictureBox1.Image);
SolidBrush brush = new SolidBrush(Color.Red);
for (int i = 0; i <= 300; i = i + 1)
{
for (int j = 0; j <= 200; j = j + 1)
{
graph.FillRectangle(brush,i,j,1,1);
}
}
}
Aber ich versteh die Philosophie nicht: Ich muss nun sekundenlang bei jeder Restaurierung warten (weil er ja den Paint Code automatisch aufruft)! Ich bin nun also durch sekundenlange Warterei bestraft weil ich nun Paint verwende? ?(
Oder hab ich schon wieder was falsch gemacht?
Hallo sharp,
Aber ich versteh die Philosophie nicht: Ich muss nun sekundenlang bei jeder Restaurierung warten
das liegt ja nur an deiner ineffizienten Wiese zu zeichnen, nicht an der Philosophie.
Pixelweise zu zeichnen ist nicht das Ideale. Warum zeichnest du nicht ein große Rechteck statt vieler kleiner. Du zeichnest sie ja eh alle in rot.
Ansonsten gibt es ja in [Tutorial] Zeichnen in Windows-Programmen (Paint/OnPaint, PictureBox) noch die Alternative mit der PictureBox.
herbivore
Ich bin nun also durch sekundenlange Warterei bestraft weil ich nun Paint verwende? ?(
Oder hab ich schon wieder was falsch gemacht?
nein, das ist nunmal so in .net.
Du kannst dir das leben aber auch leicht machen: Nimm einfach die Api-Funktion CreateDIBSection (Gdi32) und erzeug dir eine DIB in benötigter Größe. Die Funktion liefert dir eine Speicheradresse, an der sich die Bilddaten befinden. Diese Daten kannst du direkt (ohne Setpixel oder DC) setzen. Wenn es 24-Bit-Grafik sein soll, mußt du einzelne Bytes setzen, wenns 32-Bit sind, kannst du 4-Byte-Integer für die Farben verwenden (beachte, dass du Rot und Blau vertauschen mußt).
Du kannst dem Speicherbereich auch eine Array überstülpen (Zeiger auf die Daten in eine Safearry-Stuktur eintragen). Wie das prinzipiell geht, ist z.B. hier beschrieben: http://www.activevb.de/tutorials/tut_safearray/safearray.html
Wenn deine Bitmap befüllt ist, brauchst du sie nur noch irgendwie in ein Fenster oder eine Picturebox zu übertragen. Du könntest die DIB z.B. in einen DC laden und dann per Bitblt übertragen. Wahrscheinlich gibt es auch eine zulässige C#-Methode.
Dies Möglichkeit ist vermutllich von allen die schnellste, da keine Grafikmethoden benötigt werden, um Bits zu setzen und vor allem hast du eine persistente Grafik, die du nicht ständig neu zeichenen mußt. Obendrein kannst du die DIB auch mal schnell auf die HD dumpen.
Gerade in diesen Dingen sind die althergebrachten Methoden doch sehr viel einfacher und vor allem geradeaus.
Gruß,
Klaus
Hallo sharp,
außerdem bringt es nichts, im Paint-Handler einer Picturebox das Bild, das diese darstellen soll, neu zu zeichnen.
Ich zeichne in Wirklichkeit jeden Pixel in einer anderen Farbe! (zigtausende oder sogar hundertausende Pixel!) (Habe den Code absichtlich sehr vereinfacht, ansonsten wärs sehr lang) Edit: Ich zeichne nicht Kreise oder Rechtecke, sondern muss auf tieferer Ebene, nämlich Pixel zeichnen.
(Und selbst wenn ichs später nochmal mit der schnellen GDI32 PIxel-Zeichnerei versuch, bei großen Bildern wärs damit zwar viel besser, aber würde bestimmt sowiso noch paar Sekunden dauern(zu lange!!), weil mein Code der den Farbwert berechnet zeitaufwendig ist).
außerdem bringt es nichts, im Paint-Handler einer Picturebox das Bild, das diese darstellen soll, neu zu zeichnen.
Hmm. Aber laut dem Link sollte ich doch Paint verwenden? (Ich raffs wohl wieder net). Bzw. wie hätte ichs machen sollen?
Interessante Idee Klaus, danke. (Hört sich aber zu kompliziert an 😉 )
Ansonsten gibt es ja in [Tutorial] Zeichnen in Windows-Programmen (Paint/OnPaint, PictureBox) noch die Alternative mit der PictureBox.
Gilt das in meinen Fall noch immer, also das letzte Beispiel des Links hat damit zu tun?
Oder muss ich nun in meinen "speziellen" Fall ganz anders vorgehen als in deinen Link?
Hallo sharp,
Hmm. Aber laut dem Link sollte ich doch Paint verwenden? (Ich raffs wohl wieder net). Bzw. wie hätte ichs machen sollen?
wie wäre es, wenn du das Tutorial mal im ganzen, in Ruhe und im Zweifel mehrmals liest. Alle Fragen die du gestellt hast, werden darin beantwortet. Das Tutorial behauptet keineswegs, dass man nur in Paint zeichnen kann (auch wenn die Verwendung von Paint schon im Vordergrund steht), sondern zeigt eben genau eine Alternative auf.
herbivore
Oh menno, das sagt sich so einfach, das versuch ich doch, nur meine Versuche klappen ja nicht, wie man sieht.
Aber bitte wenigsten paar kleine Tipps, nicht dass ich komplett auf das falsche Pferd setze:
Pixel für Pixel in eine Liste zu speichern um mit dieser Liste den Bildinhalt zu restaurieren könnte selbst mit GDI32 bei großen Bildern (1000x1000) die Bildinhalt-restaurierung vielleicht paar Sekunden dauern? Diese Methode fällt somit aus (da zu langsam), oder?
Bleibt nur noch die PictureBox Lösung vom Link um den Inhalt schlagartig zu restaurieren?
Hallo sharp,
ich verstehe dich wirklich nicht. In dem Tutorial gibt es genau drei Beispiele. Um Animation geht es nicht und OnPaint ist für deinen Fall zu langsam, bleibt also genau eins übrig. Was gibt es also noch zu überlegen, wie es richtig sein könnte?
herbivore
Also das letzte Beispiele dort, die PictureBox Methodik ist in meinen Fall die Lösung. (Und mein anderer Fehler war, dass ich PictureBox nicht in der Paint-Methode zeichnen sollte).
Falls ich was falsches von mir gegeben habe, dann bitte sagen.
Ok, ich versuch jetzt das Beispiel in ruhe zu analysieren.
Hört sich aber zu kompliziert an 😉
Ist es aber nicht. Es ist so einfach, wie ein 2D-Array vollzuschreiben. Mir ist gerade aufgefallen, dass es noch einfacher ist, als oben beschrieben, wenn du keine dynamische Veränderung brauchst. Du schreibst einfach ein Byte- oder Integer-Array voll und konvertiest das zu einer DIB. Letzteres ist im Grunde ein Befehl (in der API GetDiBits).
Was genau willst du den eigentlich machen? Geht es um ein Intensitätsdiagramm, also z.B. eine Temperaturverteilung?
Gruß,
Klaus
Es ist die bekannte Mandelbrot Menge, je nach Benutzerinteraktion mit der GUI wird jedesmal ein neue Mandelbrotmenge Pixel für Pixel reingezeichnet.
noch einfacher ist, als oben beschrieben, wenn du keine dynamische Veränderung brauchst
Also kommt diese Lösung bei mir nicht in Frage? (Bitte schreiben, wenn ich mich irre)
Es ist die bekannte Mandelbrot Menge, je nach Benutzerinteraktion mit der GUI wird jedesmal ein neue Mandelbrotmenge Pixel für Pixel reingezeichnet.
noch einfacher ist, als oben beschrieben, wenn du keine dynamische Veränderung brauchst
Also kommt diese Lösung bei mir nicht in Frage?
Doch durchaus. Unter dynamisch verstehe ich dass die Bitmap z.B. per Maus rotiert werden soll, also dass eine Bitmap bei einer Aktion der Maus, oder was auch immer, so schnell folgen kann, dass die Zeit für die Darstellung nicht ins Gewicht fällt. Bei nem Mandlebrotbaum ist das natürlich nicht kritisch. Da reicht es, wenn die Bitmap innerhalb 10-100ms fertig ist, dh. der Mandelbrot-Algotrithmus braucht ja eh länger als die Darstellung.
Gruß,
Klaus
8o so viele Paramter!
[DllImport("gdi32.dll")] static extern int GetDIBits(IntPtr hdc, IntPtr hbmp, uint uStartScan, uint cScanLines, [Out] byte[] lpvBits, ref BITMAPINFO lpbmi, uint uUsage);
Compiler Error: The type or namespace name 'BITMAPINFO' could not be found (are you missing a using directive or an assembly reference)? Wieso?
Hier steht C# signature, wieso geht der import net out of the box: http://www.pinvoke.net/default.aspx/gdi32.GetDIBits
Und wenns bei mir mit GetDIBits klappen sollte, was muss man dann machen?
Hallo sharp,
BITMAPINFO ist ein Win32-Typ. Der ist in .NET nicht bekannt. Den musst du in .NET selbst nachbauen. Schau mal auf www.pinvoke.net , ob es da vielleicht schon was gibt, was du übernehmen kannst.
Ich würde wie gesagt, Win32 hier eher nicht verwenden.
herbivore
8o so viele Paramter!
[DllImport("gdi32.dll")] static extern int GetDIBits(IntPtr hdc, IntPtr hbmp, uint uStartScan, uint cScanLines, [Out] byte[] lpvBits, ref BITMAPINFO lpbmi, uint uUsage);
Compiler Error: The type or namespace name 'BITMAPINFO' could not be found (are you missing a using directive or an assembly reference)? Wieso?
Hier steht C# signature, wieso geht der import net out of the box:
>Und wenns bei mir mit GetDIBits klappen sollte, was muss man dann machen?
Sorry, hab mich vertippt. Du mußt natürlich SetDiBits nehmen. Mit GetDIBits kannst du den Inhalt einer Bitmap auslesen.
BITMAPINFO bzw. BITMAPINFOHEADER mußt du selbst definieren.
typedef struct tagBITMAPINFO { // bmi
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;
typedef struct tagBITMAPINFOHEADER{ // bmih
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
das ist aber nicht weiter wild.
Gruß,
Klaus