Laden...

Bildinhalt verschwindet wenn Fenster verdeckt ist! Wie Inhalt schlagartig wiederherstellen?

Erstellt von sharp vor 16 Jahren Letzter Beitrag vor 16 Jahren 4.838 Views
S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren
Bildinhalt verschwindet wenn Fenster verdeckt ist! Wie Inhalt schlagartig wiederherstellen?

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();
        }
5.742 Beiträge seit 2007
vor 16 Jahren
S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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?

49.485 Beiträge seit 2005
vor 16 Jahren

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

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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?

49.485 Beiträge seit 2005
vor 16 Jahren

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

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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();
        }
49.485 Beiträge seit 2005
vor 16 Jahren

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

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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...

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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).

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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

49.485 Beiträge seit 2005
vor 16 Jahren

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

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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! 🙁

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo sharp,

in meinem Tutorial steht aber Code mit dem es geht. Vermutlich fehl bei dir das Invalidate.

herbivore

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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));
49.485 Beiträge seit 2005
vor 16 Jahren

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

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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?

49.485 Beiträge seit 2005
vor 16 Jahren

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

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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?

49.485 Beiträge seit 2005
vor 16 Jahren

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

K
13 Beiträge seit 2008
vor 16 Jahren

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

5.742 Beiträge seit 2007
vor 16 Jahren

Hallo sharp,

außerdem bringt es nichts, im Paint-Handler einer Picturebox das Bild, das diese darstellen soll, neu zu zeichnen.

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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?

49.485 Beiträge seit 2005
vor 16 Jahren

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

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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?

49.485 Beiträge seit 2005
vor 16 Jahren

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

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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.

K
13 Beiträge seit 2008
vor 16 Jahren

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

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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)

K
13 Beiträge seit 2008
vor 16 Jahren

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

S
sharp Themenstarter:in
91 Beiträge seit 2008
vor 16 Jahren

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?

49.485 Beiträge seit 2005
vor 16 Jahren

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

K
13 Beiträge seit 2008
vor 16 Jahren

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