Laden...

Aufbau einer Bitmap

Letzter Beitrag vor 2 Monaten 38 Posts 968 Views
Aufbau einer Bitmap

Ich habe angefangen mit dem Programmieren angefangen.

Jetzt versuche ich gerade ein Programm zu machen das IFF ILBM 24 Bit anzeigen kann. Diese Bilder haben keinen Maske und keine Komprision. Die werden auch auf einen Amiga kortekt angezeigt.

Ich lese den Body der IFF Datei wird auch korrekt gelesen und übertrage die Daten in eine Bitmap. 
Nur bei meinen Programm bekomme ich nur Datenmüll angezeigt.

Ein Möglichkeit wäre das die Daten nicht richtig in die Bitmap eingetragen werden. Mein Frage daher wie ist die Bitmap genau in C# aufgebaut?

So sieht der Code aus, der den Body ausliest. Vielleicht habe ich ja als Anfänger auch da einen Denkfehler

 private void bitmap_Erstellen(System.Object sender, EventArgs e)
{
    if (intMaske != 0 || kompresion != 0)
    {
        MessageBox.Show("Zur Zeit konnen nur Bilder ohne Maske und Kompresion angezeigt werden!");
        return;
    }
    Bitmap flag = null;
    // Bitmap erstellen
    flag = new Bitmap(bildBreite, bildHoehe, PixelFormat.Format24bppRgb);
    using (FileStream fs = new FileStream(fileIFF, FileMode.Open, FileAccess.Read))
    {
        BinaryReader br = new BinaryReader(fs);
        // Zu Bodydaten springen
        br.BaseStream.Seek(bodyIndex + 8, SeekOrigin.Begin);
        int byteBody = (bildBreite * bildHoehe * 3);
        byte[] bodyData = br.ReadBytes(byteBody);
        int bodyDataSize = bodyData.Length;        
        // Überprüfen, ob bodyData korrekt gelesen wurde
        if (bodyData == null || bodyData.Length == 0)
        {
            MessageBox.Show("Body-Daten konnten nicht gelesen werden.");
            return;
        }
        /*
        // Die ersten 8 Werte anzeigen
        Debug.WriteLine("Erste 8 Werte:");
        for (int i = 0; i < 8 && i < bodyData.Length; i++)
        {
            Debug.WriteLine($"Wert {i + 1}: {bodyData[i]} (Hex: {bodyData[i]:X2})");
        }
        */
        for (indexhoehe = 0; indexhoehe < bildHoehe; indexhoehe++)
        {
            for (indexbreite = 0; indexbreite < bildBreite; indexbreite++)
            {
                pixelIndex = (indexhoehe * bildBreite + indexbreite) * 3;
                byte byteRot = bodyData[pixelIndex];
                byte byteGruen = bodyData[pixelIndex + 1];
                byte byteBlau = bodyData[pixelIndex + 2];
                //byte byteAlpha = bodyData[pixelIndex + 3];
                // Byte-Werte anzeigen
                //Debug.WriteLine($"Pixel ({indexbreite}, {indexhoehe}): Rot = {byteRot} (Hex: {byteRot:X2}), Grün = {byteGruen} (Hex: {byteGruen:X2}), Blau = {byteBlau} (Hex: {byteBlau:X2})");
                //
                // Setze die Pixel in der Bitmap
                Color color = Color.FromArgb(byteRot, byteGruen, byteBlau);
                flag.SetPixel(indexbreite, indexhoehe, color);
                int width = flag.Width;
                int height = flag.Height;
                int pixelSize = 3;
                int flagSize = width * height * pixelSize;
                label1.Text = ("Größe Body " + bodyDataSize.ToString() + " Größe Flag: " + flagSize);
            }
        }
    }
    pictureBox1.Image = flag; // In einer PictureBox anzeigen
}

Hallo und willkommen,

für einen Programmieranfänger ist das aber schon eine herausfordernde Aufgabe!

Woher kommen denn bildBreite und bildHoehe- und welche Werte haben diese? Solltest du diese nicht auch aus der IFF-Datei lesen (s.a. Interchange File Format)? Und selbstverständlich solltest du die Chunks lesen (und nicht mit festen Offsets arbeiten).

Edit:

Ich habe gerade noch mal bei meinem eigenen C-Code für die alten Amiga-Programme nachgeschaut. Das ILBM-Format funktioniert anders als dein bisheriger Schleifencode - die Schleifen sollten so aufgebaut sein:

Height
  Depth
    Width

Es gibt also je Farbkanal (Depth= 3 für RGB) jeweils eigene Bildzeilen (und nicht pro Pixel die 3 RGB-Werte hintereinander), s.a. Interchange File Format: BODY-Chunk!
Bedenke auch, daß du die Width(bzw. bei dir bildBreite) durch 8teilen mußt, um die Anzahl der zu lesenden Bytes zu bestimmen - und dann Bitoperationen benötigst um den Farbkanal-Pixelwert für die einzelnen 8 Pixel je Byte auszulesen → ist also nicht so direkt on-the-fly umwandelbar: erzeuge am besten eine eigene Klasse mit entsprechenden Datentypen und Methoden dafür.

Deinen Schleifencode könnte man außerdem noch aus Performancegründen  verbessern, indem man LockBits und Scan0verwendet, anstatt SetPixel.

Edit2: Vllt. verwendest du (gerade als Programmieranfänger) doch besser eine Library, z.B. IFF-ILBM-Parser.


PS: Und verwende Code-Tags bei deinem Beitrag (solltest du auch noch nachträglich editieren können).

Uff ich sehe meine neuen Beitrag jetzt nicht. Ich hoffe ich habe da nix falsch angeklickt, sonst kann ich ihn noch mal schreiben. Oder muss der zuerst freigeschalten werden. Muss ich halt mal warten, wenn da nichts kommt muss ich es noch mal schreiben.

Aber ich hänge mal ein Bild an wie das dann aussieht.

Ne da habe ich wohl falsch geklickt, man sollte halt keine Beträge schreiben vor dem Ersten Kaffee. Könnte aber auch sein das der Beitrag zu lange ist. 
Ich teile in daher auf mehrere auf.

@Th69:

Eine IFF Datei, egal ob sie Text, Sound, Amin oder Grafik (ILBM) enthält teilt sich immer in meiner Blöcke auf. Bild Breite und Höhe stehen dann im BMHD Block.
Der ist so aufgebaut.

BMHD-Blockstruktur laut Dokumentation: 
Blocktyp (BMHD)                              bmhdIndex Byte 0-4
Unbenutzt (2 Bytes)                         bmhdIndex Byte 5-7
Bildbreite (2 Bytes)                           bmhdIndex Byte 8-9
Bildhöhe (2 Bytes)                             bmhdIndex Byte 10-11
X-Versatz (2 Bytes)                           bmhdIndex Byte 12-13
Y-Versatz (2 Bytes)                           bmhdIndex Byte 14-15
Anzahl der Bitplanes (1 Byte)           bmhdIndex Byte 16
Maskierungstechnik (1 Byte)            bmhdIndex Byte 17
Komprimierungstechnik (1 Byte)      bmhdIndex Byte 18
Padbyte (1 Byte, ungenutzt              bmhdIndex Byte 19
Transparentfarbe (2 Bytes)              bmhdIndex Byte 20-21
X-Aspektverhältnis (1 Byte)              bmhdIndex Byte 22
Y-Aspektverhältnis (1 Byte)              bmhdIndex Byte 23
Bildschirmbreite (2 Bytes)                bmhdIndex Byte 24-25
Bildschirmhöhe (2 Bytes)                  bmhdIndex Byte 26-27

Der wird von meinen Code auch korrekt ausgelesen.  Hier muss man nur aufpassen wegen höherwertigen und niederwertigen Byte. Da macht der Amiga anders als ein PC.

Die Grafikdaten stehen dann im Block Body. Wobei die ersten 8 Byte übersprungen werden müssen. In den ersten 4 Byte steht, BODY und in den nächsten 4 Byte ist die Größe des Body gespeichert. Aber nur die Größe es Body nicht die gesamt Größe der Datei, die ist an einer anderen Stelle gespeichert.

Ich muss leider Sagen das mein Ansatz den Body auszulesen falsch war.

Die Dokumentation dazu ist leider auch nicht die beste. Ich habe das zuerst so verstanden das im Body die Daten als RGB gespeichert sind. Also so wie auf einen PC. Das ist mir zwar auch komisch vorgekommen, weil ein Amiga Grafik nicht so speichert, aber man kann es halt so verstehen wie es beschreiben ist. 
Dazu muss man halt sagen das man eine IFF 24 Bit halt nicht so einfach vergleichen kann. Den in der gibt es keine Farbkarte mehr wie das sonst der Fall ist. 
Die Farbkarte würde man im CAMP Block finden. Der ist aber bei einer 24 Bit Grafik leer, weil er ganz einfach zu groß wäre.

Daher stehen die Farben direkt im BODY Block. Allerdings sind sie dann auch bei einer 24 BIT IFF gedreht wie ein Amiga das so macht, ( Das hat auch Apple mal so gemacht ).  Wenn man sich die Ebenen der Grafik drei Dimensional vorstellt ist die dann um 90 Grad gedreht.

Sie stehen also so im Speicher. 
Byte 0           Byte 1           Byte 2    ...

R0  01234567   01234567   01234567

R1  01234567   01234567   01234567

R2  01234567   01234567   01234567

R3  01234567   01234567   01234567

und so weiter.

Das heißt in der ersten Ebene stehen für alle Pixel der Wert von R0. Also das erste Bit für Rot. In der zweiten Ebene das zweite Bit für Rot und so weiter. Dann kommen die Werte für Grün und dann für Blau.

Wen meine Gedankengänge stimmen würde das heißen.

Bei einer Grafik von 800 x 600 Pixel, wäre die Grafik Daten im Body 1 440 000 Byte groß. ( Nur die Grafikdaten, die ganze Datei hätte dann 1 440 040 Byte)

Das müsste heißen die Blöcke für Rot, Grün und Blau sind immer 480 000 Byte groß. Jede Ebene wäre dann 60 000 Byte groß. 
Für das erste Pixel müsste dann das erste Bit im Byte 0 stehen. Das zweit Bit dann im Byte an der Stelle 60 000, das dritte Bit für Rot an der stelle 120 000 und so weiter. 
Die Werte für Grün sollten dann beim Byte an der Stelle 480 000 beginnen und für Blau bei 960 000.

Das heißt ich muss für die Farbe für ein Pixel 24 Byte auslesen. gebraucht wird aber für ein Pixel immer nur ein 1 Bit aus den 24 Byte. Weil man damit aber schon die Farbwerte von 8 Pixel liest. Habe ich das so gemacht das die einzelne Bit gleich 8 Pixel zu gewissen werden.

Nur leider bekomme ich halt nur Datenmüll. Komischerweise hat dieser Datenmüll viele  Grün. Die Grafik besteht aber mehr aus Rot und Orange. 
Also könnte es sein das meine Zuordnung der Bit falsch ist, oder meine Gedankengänge.

Das ist jetzt der Code mit dem ich das Auslese.

Die Debug.WriteLine Befehle habe ich nur dazu eingefügt damit ich sehe, ob die Schleifen richtig laufen und richtig gezählt wird.

  // ********** IFF ILBM in Bitmap erstellen **********
 private void bitmap_Erstellen(System.Object sender, EventArgs e)
 {
     if (intMaske != 0 || kompresion != 0)
     {
         MessageBox.Show("Zur Zeit konnen nur Bilder ohne Maske und Kompresion angezeigt werden!");
         return;
     }
     // Bitmap erstellen
     Bitmap flag = new Bitmap(bildBreite, bildHoehe, PixelFormat.Format24bppRgb);
     using (FileStream fs = new FileStream(fileIFF, FileMode.Open, FileAccess.Read))
     {
         BinaryReader br = new BinaryReader(fs);
         // Zu Bodydaten springen
         br.BaseStream.Seek(bodyIndex + 8, SeekOrigin.Begin);
         int byteBody = (bildBreite * bildHoehe * 3);
         byte[] bodyData = br.ReadBytes(byteBody);
         // Überprüfen, ob bodyData korrekt gelesen wurde
         if (bodyData == null || bodyData.Length == 0)
         {
             MessageBox.Show("Body-Daten konnten nicht gelesen werden.");
             return;
         }
         // Sprung Adresse errechnen.
         hauptzaehler = ((bildBreite * bildHoehe * 3) / 24);   // Hauptzähler berechnen
         bytezaehler = (bildBreite * bildHoehe) / 8;         // Einsprung berechnen für Farbwerte
         rotByteArray = 0;                                   // Einsprung Adresse die Farbe Rot in IFF Body
         gruenByteArray = bildBreite * bildHoehe;            // Einsprung Adresse die Farbe Grüne in IFF Body
         blauByteArray = (bildBreite * bildHoehe) * 2;       // Einsprung Adresse die Farbe Blau in IFF Body
         // Zähler zum zuweisen der Farben in Bitmap auf Null setzen
         bitmapZaehlerBreite = 0;
         bitmapZaehlerHoehe = 0;
         hz = 0;         // Zähler für hauptzähler auf Null setzen
         byte[] RotByte = new byte[8];
         byte[] GruenByte = new byte[8];
         byte[] BlauByte = new byte[8];
         Debug.WriteLine($"Breite: {bildBreite}");
         Debug.WriteLine($"Höhe: {bildHoehe}");
         Debug.WriteLine($"Hauptzähler: {hauptzaehler}");
         Debug.WriteLine($"\nrotByteIndex: {rotByteIndex}");
         Debug.WriteLine($"gruenByteIndex: {gruenByteIndex}");
         Debug.WriteLine($"blauByteIndex: {blauByteIndex}");
         Debug.WriteLine($"bytezaehler: {bytezaehler}\n");
         while (hz < hauptzaehler)
         {
             rotByteIndex = rotByteArray;
             gruenByteIndex = gruenByteArray;
             blauByteIndex = blauByteArray;
             rotByteIndex += hz;
             gruenByteIndex += hz;
             blauByteIndex += hz;        
                 // Arrays zurücksetzen
                 Array.Clear(RotByte, 0, RotByte.Length);
             Array.Clear(GruenByte, 0, GruenByte.Length);
             Array.Clear(BlauByte, 0, BlauByte.Length);
             // Rot auslesen für 8 Pixel
             for (int pixelPosRot = 0; pixelPosRot < 8; pixelPosRot++)
             {
                 Debug.WriteLine($"pixelPosRot: {pixelPosRot}, rotByteIndex: {rotByteIndex}");
                 pixelRGB[pixelPosRot] = bodyData[rotByteIndex];
                 rotByteIndex += bytezaehler;
             }
             Debug.WriteLine($"\n");
             // Grün auslesen für 8 Pixel
             for (int pixelPosGruen = 8; pixelPosGruen < 16; pixelPosGruen++)
             {
                 Debug.WriteLine($"pixelPosGruen: {pixelPosGruen}, grünByteIndex: {gruenByteIndex}");
                 pixelRGB[pixelPosGruen] = bodyData[gruenByteIndex];
                 gruenByteIndex += bytezaehler;
                
             }
             Debug.WriteLine($"\n");
             // Blau auslesn für 8 Pixel
             for (int pixelPosBlau = 16; pixelPosBlau < 24; pixelPosBlau++)
             {
                 Debug.WriteLine($"pixelPosBlau: {pixelPosBlau}, blauByteIndex: {blauByteIndex}");
                 pixelRGB[pixelPosBlau] = bodyData[blauByteIndex];
                 blauByteIndex += bytezaehler;
             }
             Debug.WriteLine($"\n");
             // pixelRGB den Byte in der Bitmap zu ornden. 
             for (int i = 0; i < 8; i++)
              {
                  for (int bit = 0; bit < 8; bit++)
                  {
                      // RotByte
                      RotByte[bit] |= (byte)((pixelRGB[i] & (1 << (7 - bit))) != 0 ? (1 << (7 - i)) : 0);
                      // GruenByte
                      GruenByte[bit] |= (byte)((pixelRGB[i + 8] & (1 << (7 - bit))) != 0 ? (1 << (7 - i)) : 0);
                      // BlauByte
                      BlauByte[bit] |= (byte)((pixelRGB[i + 16] & (1 << (7 - bit))) != 0 ? (1 << (7 - i)) : 0);
                  }
              }
             hz += 1;
             Debug.WriteLine($"\nHz: {hz}");
             // Farben der Bitmap zuweisen
             for (int z = 0; z < 8; z++)
              {
                  Debug.WriteLine($"RotByte:  {RotByte[z]} GrünByte: {GruenByte[z]} BlauByte: {BlauByte[z]}");
                  flag.SetPixel(bitmapZaehlerBreite, bitmapZaehlerHoehe, Color.FromArgb(RotByte[z], GruenByte[z], BlauByte[z]));
                  Debug.WriteLine($"\nBreite: {bitmapZaehlerBreite}");
                  Debug.WriteLine($"Hohe: {bitmapZaehlerHoehe}");
                  if (bitmapZaehlerBreite < bildBreite -1)
                  {
                      bitmapZaehlerBreite += 1;
                  }
                  else
                  {
                      bitmapZaehlerBreite = 0;
                      bitmapZaehlerHoehe++;
                  }
              }
         }

Zitat von Gerwald

Ne da habe ich wohl falsch geklickt, man sollte halt keine Beträge schreiben vor dem Ersten Kaffee. Könnte aber auch sein das der Beitrag zu lange ist. 
Ich teile in daher auf mehrere auf.

@Th69:

Eine IFF Datei, egal ob sie Text, Sound, Amin oder Grafik (ILBM) enthält teilt sich immer in mehre Blöcke auf. Bild Breite und Höhe stehen dann im BMHD Block.
Der ist so aufgebaut.

BMHD-Blockstruktur laut Dokumentation: 
Blocktyp (BMHD)                              bmhdIndex Byte 0-4
Unbenutzt (2 Bytes)                         bmhdIndex Byte 5-7
Bildbreite (2 Bytes)                           bmhdIndex Byte 8-9
Bildhöhe (2 Bytes)                             bmhdIndex Byte 10-11
X-Versatz (2 Bytes)                           bmhdIndex Byte 12-13
Y-Versatz (2 Bytes)                           bmhdIndex Byte 14-15
Anzahl der Bitplanes (1 Byte)           bmhdIndex Byte 16
Maskierungstechnik (1 Byte)            bmhdIndex Byte 17
Komprimierungstechnik (1 Byte)      bmhdIndex Byte 18
Padbyte (1 Byte, ungenutzt              bmhdIndex Byte 19
Transparentfarbe (2 Bytes)              bmhdIndex Byte 20-21
X-Aspektverhältnis (1 Byte)              bmhdIndex Byte 22
Y-Aspektverhältnis (1 Byte)              bmhdIndex Byte 23
Bildschirmbreite (2 Bytes)                bmhdIndex Byte 24-25
Bildschirmhöhe (2 Bytes)                  bmhdIndex Byte 26-27

Der wird von meinen Code auch korrekt ausgelesen.  Hier muss man nur aufpassen wegen höherwertigen und niederwertigen Byte. Da macht der Amiga anders als ein PC.

Die Grafikdaten stehen dann im Block Body. Wobei die ersten 8 Byte übersprungen werden müssen. In den ersten 4 Byte steht, BODY und in den nächsten 4 Byte ist die Größe des Body gespeichert. Aber nur die Größe es Body nicht die gesamt Größe der Datei, die ist an einer anderen Stelle gespeichert.

Hallo, dachte schon, du würdest dich nicht mehr melden.

Dein Gedankengang ist aber teilweise falsch - hast du dir nicht den Wikipedia-Artikel angeschaut?

Der BODY-Block ist, wie ich schon geschrieben habe sowie es im Wiki-Artikel steht, zeilenweise aufgebaut, d.h. erst kommt die erste Zeile für R(ot), dann die erste Zeile für G(rün), dann die erste Zeile für B(lau) und erst danach dann die zweite Zeile R(ot), zweite Zeile G(rün), zweite Zeile B(lau), ...

Und bei einer 24-Bit Grafik besteht jeder Pixel aus genau 3 Bytes, d.h. du benötigst keine Bitoperationen (diese werden nur für die Palettenindizes beim CMAP-Block benötigt). Ein Byte aus dem BODY-Block entspricht also jeweils dem R, G oder B-Wert eines Pixels. Du mußt diese dann nur nach jeder Zeile pixelweise jeweils zu einem Color- Wert zusammensetzen.

Sorry, ich hatte bei deinem Eingangsbeitrag überlesen, daß du eine 24Bit IFF-Grafik einlesen willst. Aber wie kann der Amiga diese denn darstellen, denn er kann doch nur max 32 (bzw. 64 im Extra-Halfbrite-Modus) gleichzeitig anzeigen?

Sorry, hat ein bisschen gedauert, weil ich mir ja eingebildet habe ich muss noch schnell einen Video Konverter machen der auf ffmpeg passiert. Der kann zwar noch  nichts anderes als Videos von einen Format ins andere zu bringen. Aber er lauft super und ist auch schon in WPF gemacht. Und einen Bilder Konverter habe ich mir auch noch eingebildet. Der ist aber noch nicht fertig. Der Dient auch mehr dazu um später mal einen IFF Konverter zu machen. Zuerst mal mit IFF 24Bit, dann auch mal die andern IFF ILBM. Dann halt auch mal HAM6 und HAM8. Zweck des ganzen ist ein Verständnis für die Dateien Formate zu bekommen. Um vielleicht mal in der Lage zu sein auf Windows einen CDXL Konverter zumachen. Bis jetzt gibt es da nur einen guten auf Linux.

Wenn du CDXL nicht kennst kannst du dir das auf meinen Kanal ansehen. Das ist ein CDXL HAM8 auf meinen Amiga 4000, am Amiga 2000 geht das dann nur mit HAM6. Das können alle Amiga ab Werk.  Dazu muss ich aber sagen ich kann Höher FPS und höher Einstellungen in Audio fahren.  Bei Amiga haben einen MC 68060@50. 
Amiga 4000 HAM 8 Video (youtube.com)

Klar kann ein Amiga 24 Bit Grafiken und auch 32 Bit darstellen. (Siehe Bild). So gar als Hintergrundbild. Das kann mein Amiga 2000 und auch Amiga 4000. Setzt aber eine Grafikkarte voraus. Meine Amiga sind auch  Ausgerüstet. Die können auch in Internet. Wobei da kann man halt nur Amiga typisch Seiten anzeigen. Hat aber seine Vorteil wenn sie das können.

Ganz richtig ist das nicht. 64 Farben im Extra Halbrite Modus trifft auf die ersten Amiga zu. Also Amiga 1000, 500, 600, 2000 und 3000. Die Amiga 1200 und Amiga 4000 könnten in dem Modus dann 256 Farben. Wo bei die erste noch dem HAM6 Modus haben mit dem man 4096 Farben anzeigen kann. Der Amiga 1200 und Amiga 4000 können in HAM8 mit dem AGA Chip dann noch mehr anzeigen. 
Tja, und den AAA Chip den hat Commodore leider mit ins Grab genommen. Der hätte schon im Amiga 4000 verbaut sein sollen. Das weiß man weil der Jumper dafür da ist. ( Nur Commodore hat auf Grund des PC Geschafftes zu wenig Geld für die Entwicklung ). Der hätte 16,8 Millionen Farben anzeigen könnten oder bei einer Auflösung von 1260 x 1024 256 Farben. Zumindest wird das immer so gesagt. Der hätte auch sehr schnell sein müssen, weil Commodore damals bekannt gab das man mit den Chip ein Fenster in dem ein Video läuft verschieben kann und das Video dabei weiter läuft.

Erzeugt habe ich übrigens die IFF 24 Bit Grafiken am Amiga mit dem Picture Manager 5.5. Am könnte sie zwar auch mit XnView auf dem PC erzeugen. Nur baut das immer dann eine Maske mit ein. Was das ganze noch schwerer macht.

Auf diesen Gedankengang bin ich durch diesen Betrag von mir in Amiga Englisch Board gekommen.

IFF 24 Bit richtig gelesen? - Englisches Amiga-Board (abime.net)

So wie ich das Verstehe sind die Daten dort so gespeichert wie ich das Beschrieben habe.

Aber ich kann das ja mal versuchen das so auszulesen wie du das beschreibst. 
Ganz kapiere ich es aber noch nicht. Also da heißt bei einer Grafik von 800 x 600 würden zuerst 800 Byte für Rot kommen, dann 800 Byte für Grün und dann 800 für Blau.

PS: Egal wie viele versuche daneben gehen, ich lerne so immer was neues über C# dabei.

OK, mit einer externen Grafikkarte geht das sicherlich, aber ich meinte einen Original Amiga 500 (bzw. bis 4000). Mit dem Hold-And-Modify-Modus (HAM) kann man zwar mehr Farben gleichzeitig anzeigen, jedoch ist das Format dafür auch um einiges komplexer (gibt dafür auch extra bei IFF-Bildern einen eigenen Typ), so daß dieses nicht so einfach konvertiert werden kann.

Also da heißt bei einer Grafik von 800 x 600 würden zuerst 800 Byte für Rot kommen, dann 800 Byte für Grün und dann 800 für Blau.

Genau!

Ja, HAM6 und HAM8 ist ein eigener Typ, wird auch wieder anderes gespeichert.

So wohl HAM6 und HAM8 kann man auf der Workbench nicht nutzen. Bilder die in HAM6 oder HAM8 gespeichert sind öffnen immer den passenden Screen dazu. Am Amiga kann so viel Screen offenen als in den Speicher passt. Heute würden wir vielleicht Desktop sagen. Dabei kann jeder Screen eine eigene Auflösung haben und Farbtiefe und Farbpalette. Mit dem AAA Chip hätte dann so gar jedes Fenster eine eigne Farbpalette haben können.

CDXL Videos sind vereinfacht gesagt nichts andres als aufeinander folgende HAM Bilder. Wobei ein CDXL Video nur eine gewissen Anzahl von Bildern haben kann.

Das IFF Format selbst ist ein Container Format, das aus verschieden Blocken besteht. So lassen sich in IFF ILBM auch noch Blöcke hinzufügen. Das haben zum Beispiel manche Grafikprogramme gemacht. So könnte man in einer Grafik auch einen Block mit Kommentaren oder einen Block mit dem Autor einfügen. Diese Blöcke würden dann, zum Beispiel vom MulitView ( universal Anzeige Programm des Amigas ) überlesen und die Grafik dennoch korrekt angezeigt.

Man muss bei einer IFF Datei immer zuerst die ersten Block auslesen. Bei jeder IFF Datei hat man zuerst FORM stehen. Das sind die ersten 4 Byte. In den nächsten 4 Byte steht die gesamt Größe der Datei. Bei einer Grafik steht dann ILBM. Damit wird angegeben das es sich um eine ILBM Grafik handelt. RGB8 würde heißen das es sich im eine 24 Bit Grafik von Turbo Silber handelt. 
Dann kommt BMHD, gefolgt von den Werten der Grafik. Dann Farbkarte und dann der Body. 
Der Witz ist es, es kann auch zuerst die Farbkarte kommen und dann erst BMHD. Die müssen nicht in einer gewissen Reihenfolge stehen. Wichtig ist nur das der Body als letzter steht. Am kann also nicht davon ausgehen das die Block an einer gewissen Adresse stehen. Das heißt man muss die schon suchen in der Datei wo sie sind.  
Aufpassen muss man da auch, weil es immer auch leere Byte gibt. Auch muss man beachten wo genau die Daten eines Blocks stehen. Wenn du meinen Code ansiehst wird du auch sehen das ich die ersten 8 Byte im Body überspringe. Weil ersten 4 Byte BODY stehe. Damit man weiß wo sich der befinden und dann kommen 4 Byte die die Größe des Bodys angeben.

In  Hex Editor sieht das dann so aus. 
FORM�ù(ILBMBMHD��� X������€��,, XBODY�ù�¤nàÃ9�?æ�8|Ľ¡� <¼Ö—ÿçæiÐ

Wie du siehst gibt es hier keine Farbkarte (CMAP Block). Der wird auch bei einer IFF 24 Bit nicht gebraucht. Es gibt aber auch Programme die den Block der Farbkarte bei einer 24 Bit Grafik mit anlegen. Der ist dann leer oder mit ein paar 0 Byte gefühlt.

Ein IFF Format das sicher jeder kennt ist AIFF. TIFF ist vereinfacht gesagt ein aufgeblasenes IFF Format. Wobei Microsoft das so verändert hat das niemand mit Lizenzen kommt.-)

Ich habe den Code jetzt angepasst. Aber so ganz passt es wohl noch nicht.

Ich habe mal die ersten Werte von RotByte, GruenByte und BlauByte  im Hex Editor verglichen. Die stimmen.
Nur das Bild wird noch nicht richtig angezeigt. Vielleicht habe ich als Anfänger da ja eine Fehler der mir nicht auffällt. 
Ich habe mal ein Bild angehängt.

// Bitmap erstellen
Bitmap flag = new Bitmap(bildBreite, bildHoehe, PixelFormat.Format24bppRgb);

using (FileStream fs = new FileStream(fileIFF, FileMode.Open, FileAccess.Read))
{
BinaryReader br = new BinaryReader(fs);

// Zu Bodydaten springen
br.BaseStream.Seek(bodyIndex + 8, SeekOrigin.Begin);

int byteBody = (bildBreite * bildHoehe * 3);
byte[] bodyData = br.ReadBytes(byteBody);

// Überprüfen, ob bodyData korrekt gelesen wurde
if (bodyData == null || bodyData.Length == 0)
{
MessageBox.Show("Body-Daten konnten nicht gelesen werden.");
return;
}

// Sprung Adresse errechnen.
hauptzaehler = bildBreite * bildHoehe;   // Hauptzähler berechnen
spaltenzaehler = 0;                      // Ist spaltenzaehler = bildBreite. ByteArray muss neu berechnet werden

rotByteArray = 0;
gruenByteArray = bildBreite;
blauByteArray = bildBreite * 2;
rotByteIndex = rotByteArray;
gruenByteIndex = gruenByteArray;
blauByteIndex = blauByteArray;

// Zähler zum zuweisen der Farben in Bitmap auf Null setzen
bitmapZaehlerBreite = 0;
bitmapZaehlerHoehe = 0;

hz = 0;         // Zähler für hauptzähler auf Null setzen

Debug.WriteLine($"Breite: {bildBreite}");
Debug.WriteLine($"Höhe: {bildHoehe}");
Debug.WriteLine($"Hauptzähler: {hauptzaehler}");

Debug.WriteLine($"\nrotByteIndex: {rotByteIndex}");
Debug.WriteLine($"gruenByteIndex: {gruenByteIndex}");
Debug.WriteLine($"blauByteIndex: {blauByteIndex}");

while (hz < hauptzaehler)
{

Debug.WriteLine($"\nHauptZähler: {hz}  Spaltenzähler: {spaltenzaehler}");

// Rot auslesen
RotByte = bodyData[rotByteIndex];

// Grün auslesen 
GruenByte = bodyData[gruenByteIndex];

// Blau auslesn für 8 Pixel
BlauByte = bodyData[blauByteIndex];

// Zuweisung der von xxxByteIndex
if (spaltenzaehler < bildBreite - 1)
{
rotByteIndex++;
gruenByteIndex++;
blauByteIndex++;
spaltenzaehler++;
}
else
{
rotByteArray = rotByteArray + (bildBreite * 3);
gruenByteArray = gruenByteArray + (bildBreite * 3);
blauByteArray = blauByteArray + (bildBreite * 3);

rotByteIndex = rotByteArray;
gruenByteIndex = gruenByteArray;
blauByteIndex = blauByteArray;

spaltenzaehler = 0;
}
hz += 1;

Debug.WriteLine($"\nBitmapzähler Breite: {bitmapZaehlerBreite}");
Debug.WriteLine($"Bitmapzähler Höhe: {bitmapZaehlerHoehe}\n");

Debug.WriteLine($"RotByte: {RotByte:X2}, GruenByte: {GruenByte:X2}, BlauByte: {BlauByte:X2}, Index: {rotByteIndex:X}\n");
Debug.WriteLine("---------------------------\n");

flag.SetPixel(bitmapZaehlerBreite, bitmapZaehlerHoehe, Color.FromArgb(RotByte, GruenByte, BlauByte));

if (bitmapZaehlerBreite < bildBreite -1)
{                       
bitmapZaehlerBreite++;
}
else
{
bitmapZaehlerBreite = 0;
bitmapZaehlerHoehe++;
}

}
}

pictureBox1.Image = flag;
}

Es wäre schön, wenn du den Code in C#-Code Tags packen würdest.

Auf die Schnelle sehe ich bei dir keinen inhaltlichen Fehler.

Hast du überprüft, ob rotByteIndexbzw. rotByteArray(btw.: Warum hast du zwei verschiedene Variablen hierfür?) nach der Schleife auch den Wert von byteBody hat?

Kannst du nicht mal mit einem kleineren Bild (z.B. 16x16) testen? Ich selber habe dafür IrfanView (mit installiertem IFF Plugin) benutzt. Ich habe es aber nicht mehr hier installiert - weiß also nicht, ob es auch 24 Bit unterstützt oder nur Bilder mit einer Farbpalette.

PS: Mir hättest du nicht im Detail erklären müssen, wie das IFF/ILBM-Format aufgebaut ist.

Ich habe mir jetzt mal die Einsprung Adressen angesehen, die scheinen zu passen.

HauptZähler: 798  Spaltenzähler: 798

rotByteIndex: 798
gruenByteIndex: 1598
blauByteIndex: 2398

Bitmapzähler Breite: 798
Bitmapzähler Höhe: 0

RotByte: 00, GruenByte: 00, BlauByte: 00, Index: 31F

---------------------------

HauptZähler: 799  Spaltenzähler: 799

rotByteIndex: 799
gruenByteIndex: 1599
blauByteIndex: 2399

Bitmapzähler Breite: 799
Bitmapzähler Höhe: 0

RotByte: 00, GruenByte: 00, BlauByte: 00, Index: 960

---------------------------

HauptZähler: 800  Spaltenzähler: 0

rotByteIndex: 2400
gruenByteIndex: 3200
blauByteIndex: 4000

Bitmapzähler Breite: 0
Bitmapzähler Höhe: 1

RotByte: 00, GruenByte: 00, BlauByte: 00, Index: 961

Zwei Variablen.

xxxxByteIndex zählt immer eine Zeile. Mit xxxByteArray errechne ich die neue Adresse wenn eine Zeile durchgelaufen ist.

Beispiel Bild mit 800 x 600 Pixel.

Startwert für: rotByteIndex = 0
gruenByteIndex = 800
blauByteIndex = 1600

rotByteIndex zählt  von 0 bis 799
gruenByteIndex zählt von 800 bis 1599
blauByteIndex zählt von 1600 bis 2399.

Ist die Zeile durch wird ByteArray um die Bild Breite mal 3 erhöht und auf ByteIndex übergeben.

Die Werte für die zweite Zeile sind dann

rotByteIndex = 2400
gruneByteIndex = 3200
blauByteIndex = 4000

Ja, ein Profi würde es vielleicht anderes machen.

Klar kann ich das auch mit einen kleiner Bild machen. Muss ich am PC eines in der Größe anpassen und dann am Amiga in ein IFF konvertieren.

Ich habe jetzt das gleiche Bild auf 100 x 100 Pixel geändert. 16 x 16 war mir doch zu kleine. 
Gibt aber auch nur Datenmüll. 
Am Amiga und am PC von xnView wird es richtig angezeigt.

Evtl. hattest du doch recht mit deinem vorherigen Code, denn laut ILBM IFF Interleaved Bitmap: 24-bit ILBMs liegen R, G, B direkt hintereinander im Speicher (und die Bitreihenfolge ist vertauscht?).

Ich mache mir gerade was zu essen. Dann werde ich mal die Bit drehen. Versuchen kann man es ja.

Ich glaube so falsch liegst du da gar nicht. Die KI hatte da eine ganz gute Idee und meint nimm zum Beispiel ein Bild mit Vierecken. 
In den Bild gibt es ein grünes Viereck an der oben rechten Ecke. Wenn man sich aber das Bild da angezeigt wird ansieht. Passt es zwar in der Höhe, aber nicht in der Breite. Da wiederholt das Bild. 
Ich mach einen zweiten Beitrag wo ich das Originalbild anhänge damit man den Vergleich besser sieht.

Das ist das Ausgangsbild.

Hast du dafür den zuletzt geposteten Code verwendet? Was kommt denn raus, wenn du deinen Originalcode darauf anwendest?

Und kannst du mal das IFF Bild hier anhängen (als ZIP)?

PS: In der GUI und im Code ist "Kompression" falsch geschrieben.

Das ist der letzte Code. Zeilenweise gelesen.

Sorry das habe ich jetzt überlesen.

Wenn ich den Code nehme wo man davon ausgeht das die Grafik im Speicher gedreht ist. Also so wie ich das am Anfang gesagt habe. Bekomme ich Streifen.

Daß du bei keinem deiner Versuche grau für die ersten Zeilen herausbekommst, ist schon eigenartig, denn das bedeutet, daß die Daten irgendwie anders codiert sind.

Trotzdem scheint ja die vorherige Version (das Bild mit den Streifen) schon in die richtige Richtung zu gehen. Eigenartig ist jedoch, daß dort rechts im Bild plötzlich der Hintergrund von weiß auf schwarz wechselt.

Du hast ja etwas von 90° gedreht geschrieben - versuche doch mal die Breite und Höhe zu tauschen (also nur 600 pro Zeile zu lesen).

Edit:

Ich habe unter IFFshow/iff24.py ein Python-Script zum Erzeugen von 24bit IFF-Bildern gefunden.

Anscheinend ist es wirklich so, daß die Farbbits je Ebene (plane) aufgeteilt sind, d.h. es gibt insg. 24 (3 * 8) Ebenen je Zeile (und jede Zeile besteht dann aus Width / 8Bytes).

Sorry für das Mißverständnis: ich hatte immer gedacht die z.B. R0 - R7 beziehen sich auf die Bits eines gelesenen Bytes, aber sie sind verteilt auf die verschiedenen Ebenen. Mein Eingangsbeitrag stimmte also (fast), nur daß die Bildtiefe dann 24 (und nicht nur 3) ist.

Also unterscheidet sich 24 Bit von x-Bit Paletten-Bildern nur dadrin, daß einfach mehr Ebenen (planes) abgespeichert sind (und anstatt eines Index in die Farbpalette direkt der 24 Bit Wert die RGB Farbe ergibt).

Edit2:

Die Original IFF-Formatbeschreibung habe ich nun auch unter ILBM IFF Interleaved Bitmap gefunden (dort sieht man auch die richtige Formatierung unter "24-bit ILBMs" - fast ganz unten).

Eine weitere gute Beschreibung habe ich unter IFF gefunden (auch dort sind 24 Bit IFF-Bilder genauer erklärt).

Und ich habe sogar jetzt Original Amiga C-Code online gefunden: IFFSource Code: apps/24bitDemo/24bitDemo.c

Ich muss gestehen für mich als Anfänger wird das immer verwirrender. Daher ist es für mich jetzt auch schwer den C Code zu verstehen. 
Vielleicht sollte ich das mal ruhen lassen und meine Kenntnisse zuerst mal vertiefen.

Ist ja voll interessant. Man sagt immer, am Amiga braucht man bei einer IFF 24 Bit Grafik 24 Befehle ( Assembler ) um die Farbe eines Pixel zu ändern. Das würde das unterstreichen wie ich am Anfang gesagt habe das die Daten liegen. Ist aber nicht so. 
Es wird aber noch besser. Wenn ich von dem Masken lesen, hört sich das für mich so an als wenn dann mehr Daten gespeichert sind. Weil ja der Maskenwert noch dazu kommt. 
Aber jetzt pass mal auf. Bei einer Grafik von 800 x 600 24 Bit mit dem Maskenwert 1. Also eine IFF Grafik wie sie zum Beispiel auch XnView erzeugt. Da ist der Body 1 440 000 Byte Groß. Bei der selben Grafik ohne Maske so wie ich das verstehe müsste der kleiner sein. Er ist aber auch 1 440 000 Byte groß. Was ja auch passt 800  * 600 * 3 = 1 440 000.

Wobei ich bei den Beschreibungen auch so meine Zweifel habe. Denn die BMHD wird zwar immer richtig beschrieben. Was aber so gut wie keiner dazu schreibt ist das die ersten zwei Byte leer sind.

Wie auch immer, ich habe jetzt noch wenn gefragt der so einen Code, wenn auch für den Amiga, schon mal geschrieben hat. Vielleicht habe ich Glück und er gibt mir Antwort.

Ich hatte dir doch auch den Link zu dem IFF-Parser gegeben. Schau dir einfach die Methode IFF.cs: ReadBody an. Bei 24 Bit Bildern ist dann BMHD.NPlanes dann eben 24 - und nur die Methode ColorIndexToColor ist dabei dann zu viel, da iColor schon den Farbwert (als uint) angibt.

PS:

Du hast Recht, bei einem Masking-Wert von 1sollte es jeweils eine Ebene (Bitplane) mehr pro Zeile geben (da hat dann wohl XnView einen Fehler).

Und wegen den zwei Nullen nach dem Chunk-Namen: nach jedem Chunk-Namen (z.B. BMHD) kommen noch mal 4 Bytes als Chunk-Länge (in Big Endian Format), so daß bei Daten kleiner gleich 0xFFFFeben zwei Nullen am Anfang sind (so daß dann z.B. dort für die fixe BMHD-Größe von 20 (= 0x14)00 00 00 14 steht).

Sorry, bei der BMHD hast du Recht, das hatte ich falsch im Kopf.

Es ist noch nicht perfekt. Aber der Weg stimmt. Da hat er mir den richtigen Tipp gegeben.

@Th69
Wie liegen jetzt die Daten. Vereinfacht gesagt hatten wir beide zu einen Teil recht. Es stimmt auch so wie ich es gepostet habe. Nur wenn immer gesagt wird im Amiga sind die Daten 90 Grad gedreht sieht man sie halt auch mal schnell mit dem falsch Blickwinkel an.

Sie liegen Zeilenweiße. 
Beispiel Grafik 800 x600

800 Bit mit R0, 800 Bit mit R1 .... 800 Bit mit R7, 800 Bit mir G0, 800 Bit mit G1 .... 800 Bit mit G7, 800 Bit mit B0, 800 Bit mit B1 .... 800 Bit mit B7

Ganz passte es aber noch nicht. Da muss ich mir meinen Code noch genauer ansehen. Denke da läuft ein Zähler nicht ganz richtig.

Huhu jetzt klappt  es

Gratulation!

Was war denn noch der Fehler? Und kannst du den Code hier als Anhang hinzufügen?

Ich habe mir dann eine andres Testbild gemacht, was auch Dreiecke und Kreis hat. Da konnte ich dann sehen was noch schief läuft.

Das Problem ist, das ein MC 680x0 die Byte anderes speichert als ein x86. Da ist die Bit Reihenfolge eine anderes. Aber und da bin ich auch darüber gefallen. Man muss Wissen wenn die Bit Reihenfolge im Byte umdrehen muss und wann nicht. Weil man das aber so nicht unbedingt drehen muss, bin ich nicht auf die Idee gekommen das im Body zu machen.

Klar kann ich den Code hier veröffentlichen. Ein Ziel des ganzen ist ja auch eine Doku zu schaffen die es mal genau beschreibt. 
Aber bitte bedenkt das ist der Code eines Anfängers und viele Teile sind jetzt etwas schlecht, weil da viele Versuche dahinter stehen und der Code oft verändert wurde. 
Ich habe in dir mal Angehängt.

Vielen dank noch mal für deine Hilfe. Auch wenn es jetzt nicht ganz die Lösung war, war dein Ansatz schon wichtig um zu verstehen wie die Daten da lieben.

Es gibt da noch einen Fehler im Code.

bitmapZaehlerBreite  und bitmapZaehlerHoehe müssen auf 0 zurück gesetzt werden bevor die Daten eines neuen Body geladen werden.

Wenn du das Projekt weiterführen möchtest, dann wäre jetzt der richtige Zeitpunkt, so wie ich schon im ersten Beitrag geschrieben habe, die Funktionalität in eine eigene Klasse (und einer eigenen Datei) - anstatt in der Forms-Klasse - auszulagern. Dazu am besten sogar ein eigenes Bibliotheks/Library-Projekt erzeugen und als Referenz in dem WinForms-Projekt hinzufügen.

Wenn du deinen Code mit dem aus dem IFF-ILBM-Parser vergleichst, dann siehst du, daß dein Code um einiges komplizierter (und inperformanter) ist - gerade was auch die Bitoperationen betrifft. Auch die von mir erwähnte Schleifenverschachtelung (Height / Depth / Width) wird ja von diesem Projekt verwendet.

Aber es ist ja gut, wenn du dich selber intensiv mit dieser Thematik beschäftigst und dann selbst auf Lösungen kommst. 
Kannst ja erstmal deinen Code ein bißchen aufräumen und dokumentieren.

Wenn du weitere Tipps benötigst, so kannst du dich ja jederzeit melden.

Danke für dein Angebot, ich komme da sicher gerne mal zurück.

Das kann jetzt aber einige Zeit dauern.

Was ich noch nicht verstehe ist wie der x und y Aspect errechnet wird.

Bei den beiden Werten geht es ja um die Skalierung, d.h. sie geben das Verhältnis von X zu Y (d.h. Breite zu Höhe) an. Da hierfür nur ein Byte im BMHD-Header zur Verfügung steht, können hier auch kleinere Zahlen drin stehen (z.B. 4 : 3, 16: 10, etc.), es geht ja nur um das Verhältnis X/Y.

Und in PageWidth und PageHeight sollte die ursprüngliche Screen-Auflösung drinstehen, mit der des Bild dargestellt wurde (vor dem Abspeichern).

Wenn jetzt das IFF-Bild geladen wird, kann dann auf dem System entweder die Auflösung anhand der PageWidth und PageHeigth angepaßt werden (beim Amiga z.B. einen neuen Screen in dieser Größe erzeugen) oder alternativ anhand des Aspekt-Ratio berechnet werden (damit die Anzeige nicht verzerrt aussieht).

Was für Werte hast du denn bei deinen Bildern drin stehen (und welche Originalauflösung hast du beim Abspeichern der Bilder auf dem Amiga verwendet)?

Edit:

Diesbzgl. ist sogar ein Fehler im Amiga-Code von modules/2Filbmw.c , da dort direkt die Werte von DI.Resolution.x und DI.Resolution.y zugewiesen werden. Da diese jedoch als WORD, d.h. ushort, gespeichert sind, wird nur das Low-Byte übernommen...

Bei mir steht

X 44

Y 44.

Für was die gehören verstehe ich schon. Nur wie werden diese Werte beim erstellen der Grafik errechnet?

Sorry habe das mit der Auflösung ünersehen.

Erzeugt habe ich die Datei mit dem Picture Manager 5.5 am Amiga bei einer Auflösung von 1024 x 768 bei 24 Bit

Ich hatte oben noch mal ein "Edit" hinzugefügt.

In diesem Code tauchen auch die Werte 44 sowie 52 auf. Nach kurzer Internetsuche habe ich dazu Finding the aspect ratio gefunden (dieses scheinen also festdefinierte Werte zu sein für die auf dem Amiga gebräuchlichen Auflösungen bzw. Darstellungen am Monitor).

Dort steht es auch. Für Pal 44:44