Ich hab ein eindimesionales Byte-Array voll mit Bilddaten und ich möchte es mit Marshal.Copy in ein Zeiger überführen. Wie kann ich einen neuen IntPtr erzeugen? Hab die SuFu verwendet aber da war immer ein Bitmap und Lockbits voraus... geht das auch anders?
Einige Codefragmente:
public unsafe override void getResult(Frame frame)
{
...
byte[] output = new byte[3*frame.Width*frame.Height];
...
System.Runtime.InteropServices.Marshal.Copy(output, 0, ptr, output.Length);
_bitmap = new Bitmap(frame.Width, frame.Height, 3*frame.Width, PixelFormat.Format24bppRgb, ptr);
}
Wie gesagt Probleme hab ich bei ptr.
Hi digi333,
schau mal bei MemBitmap vorbei.
Du musst aufpassen, wenn bei dir frame.Width kein vielfaches von 4 ist.
Es ist nämlich wichtig, dass jede neue Zeile eines Bitmaps 4-byte-aligned ist. Deshalb gibts bei nem Bitmap am Ende immer noch son Offset.
Und die gesamtBreite in Bytes einer Zeile wird oft als Stride, pitch oder WidthStep bezeichnet
ein byte[]-Array hat in C# wie jedes Objekt eigentlich nie eine feste Position im RAM. kann jederzeit verschoben werden um die Fragmentierung klein zu halten.
Deshalb musst du dein Objekt zuerst "pinnen". Und erst dann kannst du eine feste Speicheradresse holen.
Wichtig dabei ist, am Ende das Pinnen wieder rückgängig zu machen um der GarbageCollection so wieder die Kontrolle zu geben.
Mit Marshal.Copy hat das erstmal nix zu tun. du willst ja nix kopieren, sondern nur die Speicheradresse haben.
Beispiel:
byte[] output;
...
GCHandle dataHandle = GCHandle.Alloc(output, GCHandleType.Pinned);
IntPtr ptr = dataHandle.AddrOfPinnedObject();
wichtig am Ende wie gesagt:
dataHandle.Free();
Sonst bleibt das Array immer weiter gepinned. Auch wenn du das Bitmap schon lange nicht mehr verwendest. Und der speicher wird voller und voller.
//Edit:
natürlich muss das byte[]-Array mindestens so lange existieren wie das Bitmap.
_bitmap scheint ne Klassenvariable zu sein, während output nur lokal ist.
Aber gut, solange du das GCHandle als Klassenvariable hast, kann auch das byte[]-Array nicht am Ende der Funktion von der GC collected werden.
beste Grüße
zommi
IntPtr ptr = new Inptr(); ?
Dann bekomm ich aber bei
System.Runtime.InteropServices.Marshal.Copy(output, 0, ptr, output.Length);
eine ArgumentNullException.
Wie umgehe ich diese? Was darf den dabei nicht NULL sein?
🤔
Wenn du ne neue Instanz von nem Objekt erstellst ist es meistens null. Das sollte deine Frage beantworten
"2 Dinge sind unendlich die Dummheit der Menschen und das Universum, aber beim Universum bin ich mir noch nicht so ganz sicher."
Wenn du ne neue Instanz von nem Objekt erstellst ist es meistens null
Mhh..naja... eigentlich nicht. bevor ich den Konstruktor aufrufe ist die Referenz meistens Null. Danach ja eben nicht mehr.
new Inptr();
erzeugt nen neuen Null-Pointer.
Und weil dieser Points auf nix (null) zeigt, gibs auch konsequenterweise ne ArgumentNullException, wenn du den an den Bitmap-Konstruktor übergibt.
Denn der erwartet einen Pointer auf nen Speicherbereich.
Und bei "null" ist aber kein gültiger Speicherbereich.
//Edit:
Ne aber mal im Ernst: nimm GCHandle und gut ist 🙂
beste Grüße
zommi
Hallo zommi,
was du oben geschrieben hast, klingt eigentlich super... ich möchte mehr hören, aber ich verstehe fast nur Bahnhof.
Wie gesagt habe ich ja ein volles ByteArray für einen Pointer vorbereitet. Aber wie erstelle ich den Pointer so das ich ihn dem Bitmap geben kann? Was ist mit den überzähligen Bits? Die höhe und Breite ändert sich mit der Auflösung. Das ByteArray ist in dem Sinne fast selber gezeichnet, aber hat die gleiche Größe wie frame.
Wie sieht es mit output aus? Soll der erst hinterher beschrieben werden?
@zommi
Naja ich konnte dir jetzt aus dem Gedächtnis 20 Klassen auzählen die nach dem Instanzieren null sind. 😉
"2 Dinge sind unendlich die Dummheit der Menschen und das Universum, aber beim Universum bin ich mir noch nicht so ganz sicher."
Wenn du ne neue Instanz von nem Objekt erstellst ist es meistens null. Das sollte deine Frage beantworten
??? seit wann? wenn du eine instanz erzeugst, dann ist sie da und auch im speicher zu finden. wenn du eine variable anlegst, kannst du sie mit null vorbelegen (wenn es ein nullable typ ist) aber sobald du eine instanz anlegst, ist diese nicht null.
Naja ich konnte dir jetzt aus dem Gedächtnis 20 Klassen auzählen die nach dem Instanzieren null sind.
na jetzt bin ich mal gespannt. das was du vermutlich meinst: manche feldvariablen sind mit null vorinitialisiert. (in diesem fall ist IntPtr.m_value == null)
Hi digi333,
aber ich verstehe fast nur Bahnhof.
Mhh...ich dachte es wär verständlicher X( ( 😉)
Befrag mal Google/Forum/Wiki/MSDN zu folgenden Themen:*"Byte Alignment" (wiki nennts Speicherausrichtung) *"Memory Management c#" bzw. "Garbage Collection c#" *"Large Object Heap" *"Pinning c#" *"device independent bitmap"
Das sollte vorerst reichen. 🙂
Also nochmal:
Wichtig ist: Das Bitmap verweist intern immernoch auf den Speicherbereich.
Du darfst also solange du mit dem Bitmap arbeiten willst, nicht den Speicherbereich loslassen (GCHandle.Free). Aber sobald du das Bitmap nicht mehr brauchst, solltest du das unbedingt tun.
Wenn du nachträglich noch was im Bitmap rumwerkeln willst, dann solltest du dir die Referenz auf das byte[]-Array merken.
Denn du kannst immernoch die Pixel manipulieren und da das Bitmap ja DIREKT darauf verweist, musst du kein neues Bitmap o.Ä. erstellen.
beste Grüße
zommi
Zur Offtopic-null-Diskussion:
new ***();
ruft ja einen Konstruktor auf und wir haben ein reales Objekt (!=null)
(Ich weiß jetzt nich was passiert, wenn im Konstruktor ne Exception geworfen wird...dann ist das Ergebnis evtl. null)
(Ich weiß jetzt nich was passiert, wenn im Konstruktor ne Exception geworfen wird...dann ist das Ergebnis evtl. null)
dann wird zum catchblock gesprungen oder das programm beendet (unhandled exception). die frage ist dann: wo befindet sich der catchblock? wenn er im konstruktor ist, dann kann man die instanziierung weitermachen ansonsten je nach scope nicht mehr. tatsache ist, das bereits vor dem konstruktor, das objekt im speicher liegt (also != null ist), da vor dem konstruktor bereits alle klassenvariablen angelegt werden und der speicher hierfür bereits reserviert sein muss.
Hab momentan noch nicht das Bild so wie ich es haben wollte. Ich habe aber "Angst" vor den Bits am Ende jeder Zeile, da ich davon keine Ahnung hab. du meintest, durch 4 teilbar. Das kann ich nicht immer gewährleisten. Videos haben unterschiedliche Auflösungen. Die Manipulation der Bilder passieren davor im Bytearray.