/// <summary> /// Erstellt eine neue Farbtabelle aus derm Bitmap-Puffer (d.h. aus einer binären Bitmap-Tabelle). /// </summary> /// <param name="buffer">Der Bitmap-Puffer mit einem Zeiger am Beginn der Farbtabelle.</param> /// <param name="count">Die Anzahl der in der Tabelle definierten Farben.</param> public ColorTable(ref RAMBuffer buffer, uint count) { // Einträge einzeln einlesen for(int i = 0; i < count; ++i) { // 4 Bytes lesen byte[] b = buffer.ReadByteArray(4); // Farbeintrag erstellen _colors[i] = Color.FromArgb(b[2], b[1], b[0]); } // Fehlende Einträge mit 255er-Weiß initialisieren for(int i = (int)count; i < 256; ++i) { // Weiß einfügen _colors[i] = Color.FromArgb(255, 255, 255); } }
/// <summary> /// Lädt die SLP-Daten. /// </summary> /// <param name="buffer">Ein Puffer mit den SLP-Daten. Die Leseposition wird entsprechend der Datenlänge erhöht.</param> /// <remarks></remarks> private void loadData(RAMBuffer buffer) { // Header #region SLP-Header _headers.Version = buffer.ReadString(4); _headers.FrameCount = buffer.ReadUInteger(); _headers.Comment = buffer.ReadString(24); #endregion SLP-Header // Frame-Informationen: Header #region Frame-Header for(int i = 0; i < _headers.FrameCount; i++) { // Neues Frame-Header-Objekt erstellen FrameInformationHeader aktFIH = new FrameInformationHeader(); // Der Zeichenindex in der SLP-Datei, an dem die Kommandotabelle des Frames beginnt aktFIH.FrameCommandsOffset = buffer.ReadUInteger(); // Der Zeichenindex in der SLP-Datei, an dem die Umrissdaten (RowEdge) des Frames gespeichert sind aktFIH.FrameOutlineOffset = buffer.ReadUInteger(); // Der Zeichenindex in der SLP-Datei, an dem die Farbpalette des Frames definiert ist; Genaueres ist nicht bekannt aktFIH.PaletteOffset = buffer.ReadUInteger(); // Die Frame-Eigenschaften; die Bedeutung dieses Werts ist unbekannt aktFIH.Properties = buffer.ReadUInteger(); // Die Abmessungen des Frames aktFIH.Width = buffer.ReadUInteger(); aktFIH.Height = buffer.ReadUInteger(); // Die Anker (Mittelpunkt) des Frames aktFIH.AnchorX = buffer.ReadInteger(); aktFIH.AnchorY = buffer.ReadInteger(); // Frame-Header in die zentrale Liste schreiben _frameInformationHeaders.Add(aktFIH); } #endregion Frame-Header // Frameinformationen: Daten #region Frame-Daten (RowEdge und Kommandotabelle) // Gefundene Einstellungen merken bool useTransp = false; bool useOutline1 = false; bool useOutline2 = false; bool usePlayerColor = false; bool useShadow = false; // Frames einzeln durchgehen for(int i = 0; i < _headers.FrameCount; i++) { // Spielernummer einer ggf. ausgegebenen Einheit const byte Spielernummer = 0; // Spieler 1 => Blau // Frame-Header-Daten abrufen FrameInformationHeader aktFIH = _frameInformationHeaders[i]; // Neues Frame-Daten-Objekt erstellen FrameInformationData aktFID = new FrameInformationData(); // RowEdge (leere Fläche [=> Transparenz, ohne Kommandos], von den Bildrändern ausgehend) #region RowEdge-Daten // Arrays initialisieren: Das RowEdge-Array und das BinaryRowEdge-Array, um bei unveränderten Frames dieses nicht neu berechnen zu müssen und direkt schreiben zu können aktFID.RowEdge = new ushort[aktFIH.Height, 2]; aktFID.BinaryRowEdge = new BinaryRowedge[aktFIH.Height]; for(int j = 0; j < aktFIH.Height; j++) { // Werte einlesen ushort left = buffer.ReadUShort(); // Links ushort right = buffer.ReadUShort(); // Rechts // Evtl. falsche Werte korrigieren { // Links if(left > aktFIH.Width) { left = (ushort)aktFIH.Width; } // Rechts if(right > aktFIH.Width) { right = (ushort)aktFIH.Width; } } // Werte speichern aktFID.RowEdge[j, 0] = left; aktFID.RowEdge[j, 1] = right; // Binäres RowEdge mitschreiben aktFID.BinaryRowEdge[j] = new BinaryRowedge(left, right); } #endregion RowEdge-Daten // Kommandotabellen-Offsets abrufen #region Kommandotabellen-Offsets aktFID.CommandTableOffsets = new uint[aktFIH.Height]; for(int j = 0; j < aktFIH.Height; j++) { aktFID.CommandTableOffsets[j] = buffer.ReadUInteger(); } #endregion Kommandotabellen-Offsets // Kommandotabelle zeilenweise auslesen und umgewandelte Kommandotabelle erzeugen #region Kommandotabelle // Gleichzeitig wird die binaryCommands-Variable miterzeugt, um unveränderte Frames ggf. ohne Umweg über eine Neuerstellung der Kommandos wieder in die *.slp schreiben zu können aktFID.CommandTable = new int[aktFIH.Height, aktFIH.Width]; aktFID.BinaryCommandTable = new List<BinaryCommand>(); for(int j = 0; j < aktFIH.Height; j++) { // Gibt die aktuelle X-Position innerhalb des generierten Bildes (in Pixeln) an int aktPtr = 0; // Linker RowEdge-Bereich ist leer => transparent, also -1 for(int k = 0; k < aktFID.RowEdge[j, 0]; k++) { aktFID.CommandTable[j, aktPtr + k] = -1; } // Linker RowEdge-Bereich ist komplett in das Bild geschrieben worden aktPtr += aktFID.RowEdge[j, 0]; // Wird false, wenn ein Zeilenumbruch erreicht wurde bool Weiter = true; // Kommando für Kommando lesen while(Weiter) { // Das aktuelle Kommandobyte byte aktKommandoByte = buffer.ReadByte(); // Das aktuelle Kommando (die ersten vier Bits des Kommandobytes) Wird berechnet, um das oft mit der Datenlänge korrelierte Kommandobyte bestimmen zu können (es müssen nur die Werte 0 bis 15 abgefragt werden, statt eigentlich 0 bis 255; weiterhin geben immer die ersten 2 oder 4 Bits das Kommando an, die anderen 4 oder 6 meist nur die jeweilige Länge) byte aktKommando = (byte)(aktKommandoByte & 0x0F); // Das Kommando auswerten switch(aktKommando) { // Farbblock (klein) case 0x00: case 0x04: case 0x08: case 0x0C: { // Länge ermitteln int len = aktKommandoByte >> 2; // Daten einlesen byte[] dat = buffer.ReadByteArray(len); // Daten in die Kommandotabelle schreiben for(int k = 0; k < len; k++) { aktFID.CommandTable[j, aktPtr + k] = dat[k]; } // Es wurden len Pixel eingelesen aktPtr += len; // Binary-Wert erstellen cmdColor(ref aktFID, dat); break; } // Transparenz (klein) case 0x01: case 0x05: case 0x09: case 0x0D: { // Länge ermitteln int len = aktKommandoByte >> 2; // Transparenz in die Kommandotabelle schreiben for(int k = 0; k < len; k++) { aktFID.CommandTable[j, aktPtr + k] = -1; } // Es wurden len Pixel eingelesen aktPtr += len; // Binary-Wert erstellen cmdTransp(ref aktFID, len); // Transparenz wurde gefunden useTransp = true; break; } // Großer Farbblock case 0x02: { // Hilfs-Kommandobyte auslesen byte byte2 = buffer.ReadByte(); // Länge ermitteln int len = ((aktKommandoByte & 0xF0) << 4) + byte2; // Daten einlesen byte[] dat = buffer.ReadByteArray(len); // Daten in die Kommandotabelle schreiben for(int k = 0; k < len; k++) { aktFID.CommandTable[j, aktPtr + k] = dat[k]; } // Es wurden len Pixel eingelesen aktPtr += len; // Binary-Wert erstellen cmdColor(ref aktFID, dat); break; } // Großer Transparenzblock case 0x03: { // Hilfs-Kommandobyte auslesen byte byte2 = buffer.ReadByte(); // Länge ermitteln int len = ((aktKommandoByte & 0xF0) << 4) + byte2; // Transparenz in die Kommandotabelle schreiben for(int k = 0; k < len; k++) { aktFID.CommandTable[j, aktPtr + k] = -1; } // Es wurden len Pixel eingelesen aktPtr += len; // Binary-Wert erstellen cmdTransp(ref aktFID, len); // Transparenz wurde gefunden useTransp = true; break; } // Spielerfarbenblock case 0x06: { // Die oberen 4 Bits bestimmen byte next4Bits = (byte)(aktKommandoByte & 0xF0); // Die Länge der Daten int len = 0; // Die oberen 4 Bits sind 0, wenn die Länge im nächsten Byte angegeben ist; ansonsten werden diese um 4 nach rechts verschoben und sind somit als Längenangabe verwendbar if(next4Bits == 0) { len = buffer.ReadByte(); } else { len = next4Bits >> 4; } // Daten auslesen byte[] dat = buffer.ReadByteArray(len); // Daten durchgehen for(int k = 0; k < len; k++) { // Das aktuelle Byte byte aktVal = dat[k]; // Farbindex berechnen: Die Spielerfarben liegen zwischen 16 und 13. Beim SLP-Schreiben wurde von allen Werten 16 subtrahiert => aktVal liegt zwischen 0 und 7. Zwischen den Spielerfarben auf der Palette liegen immer 16 Einheiten. Angegeben wird die Spielerfarbe durch: 16 + (Spielernummer - 1) * 16, wobei die Spielernummer zwischen 1 und 8 liegt. aktVal = (byte)(aktVal + 16 + 16 * Spielernummer); // Wert in das Bild schreiben aktFID.CommandTable[j, aktPtr + k] = aktVal; // Wert zurück in Array schreiben, um das Schreiben der Binary-Daten zu erleichtern dat[k] += 16; } // Es wurden len Pixel eingelesen aktPtr += len; // Binary-Wert erstellen cmdPlayerColor(ref aktFID, dat); // Spielerfarben wurden gefunden usePlayerColor = true; break; } // Einfarbiger Block case 0x07: { // Die oberen 4 Bits bestimmen byte next4Bits = (byte)(aktKommandoByte & 0xF0); // Die Länge der Daten int len = 0; // Die oberen 4 Bits sind 0, wenn die Länge im nächsten Byte angegeben ist; ansonsten werden diese um 4 nach rechts verschoben und sind somit als Längenangabe verwendbar if(next4Bits == 0) { len = buffer.ReadByte(); } else { len = next4Bits >> 4; } // Das nächste Byte gibt die Farbe an byte farbe = buffer.ReadByte(); // Hilfsvariable für das Binary-Schreiben byte[] dat = new byte[len]; // Betroffene Pixel durchgehen for(int k = 0; k < len; k++) { // Farbe in das Bild schreiben aktFID.CommandTable[j, aktPtr + k] = farbe; // Farbe in das Array übernehmen dat[k] = farbe; } // Es wurden len Pixel eingelesen aktPtr += len; // Binary-Wert erstellen cmdColor(ref aktFID, dat); break; } // Einfarbiger Spielerfarben-Block case 0x0A: { // Die oberen 4 Bits bestimmen byte next4Bits = (byte)(aktKommandoByte & 0xF0); // Die Länge der Daten int len = 0; // Die oberen 4 Bits sind 0, wenn die Länge im nächsten Byte angegeben ist; ansonsten werden diese um 4 nach rechts verschoben und sind somit als Längenangabe verwendbar if(next4Bits == 0) { len = buffer.ReadByte(); } else { len = next4Bits >> 4; } // Das nächste Byte gibt die Grundfarbe an byte farbe = buffer.ReadByte(); // Hilfsvariable für das Binary-Schreiben byte[] dat = new byte[len]; // Daten durchgehen for(int k = 0; k < len; k++) { // Das aktuelle Byte byte aktVal = farbe; // Farbindex berechnen: Die Spielerfarben liegen zwischen 16 und 13. Beim SLP-Schreiben wurde von allen Werten 16 subtrahiert => aktVal liegt zwischen 0 und 7. Zwischen den Spielerfarben auf der Palette liegen immer 16 Einheiten. Angegeben wird die Spielerfarbe durch: 16 + (Spielernummer - 1) * 16, wobei die Spielernummer zwischen 1 und 8 liegt. aktVal = (byte)(aktVal + 16 + 16 * Spielernummer); // Wert in das Bild schreiben aktFID.CommandTable[j, aktPtr + k] = aktVal; // Wert zurück in Array schreiben, um das Schreiben der Binary-Daten zu erleichtern dat[k] = (byte)(farbe + 16); } // Es wurden len Pixel eingelesen aktPtr += len; // Binary-Wert erstellen cmdPlayerColor(ref aktFID, dat); // Spielerfarben wurden gefunden usePlayerColor = true; break; } // Schattenblock case 0x0B: { // Die oberen 4 Bits bestimmen byte next4Bits = (byte)(aktKommandoByte & 0xF0); // Die Länge der Daten int len = 0; // Die oberen 4 Bits sind 0, wenn die Länge im nächsten Byte angegeben ist; ansonsten werden diese um 4 nach rechts verschoben und sind somit als Längenangabe verwendbar if(next4Bits == 0) { len = buffer.ReadByte(); } else { len = next4Bits >> 4; } // Schattenpixel einzeln durchgehen for(int k = 0; k < len; k++) { // Schatten in Bild schreiben aktFID.CommandTable[j, aktPtr + k] = -4; } // Es wurden len Pixel eingelesen aktPtr += len; // Binary-Wert erstellen cmdShadow(ref aktFID, len); // Schatten wurde gefunden useShadow = true; break; } // Outline-Kommandos: Hängen von Kommandobyte ab ("E" am Ende bei allen gemeinsam) case 0x0E: { switch(aktKommandoByte) { // Outline1-Pixel case 0x4E: { // Outline auf Bild schreiben aktFID.CommandTable[j, aktPtr] = -2; // Es wurde ein Pixel eingelesen aktPtr += 1; // Binary-Wert erstellen cmdOutline1(ref aktFID, 1); // Outline1 wurde gefunden useOutline1 = true; break; } // Outline2-Pixel case 0x6E: { // Outline auf Bild schreiben aktFID.CommandTable[j, aktPtr] = -3; // Es wurde ein Pixel eingelesen aktPtr += 1; // Binary-Wert erstellen cmdOutline2(ref aktFID, 1); // Outline2 wurde gefunden useOutline2 = true; break; } // Outline1-Block case 0x5E: { // Blocklänge abrufen int len = buffer.ReadByte(); // Outlinepixel in der angegebenen Anzahl auf das Bild schreiben for(int k = 0; k < len; k++) { aktFID.CommandTable[j, aktPtr + k] = -2; } // Es wurden len Pixel eingelesen aktPtr += len; // Binary-Wert erstellen cmdOutline1(ref aktFID, len); // Outline1 wurde gefunden useOutline1 = true; break; } // Outline2-Block case 0x7E: { // Blocklänge abrufen int len = buffer.ReadByte(); // Outlinepixel in der angegebenen Anzahl auf das Bild schreiben for(int k = 0; k < len; k++) { aktFID.CommandTable[j, aktPtr + k] = -3; } // Es wurden len Pixel eingelesen aktPtr += len; // Binary-Wert erstellen cmdOutline2(ref aktFID, len); // Outline2 wurde gefunden useOutline2 = true; break; } } break; } // Zeilenende case 0x0F: { // Kein weiterer while-Schleifen-Durchlauf => nächste Zeile (for-Schleife) Weiter = false; // Binary-Wert erstellen cmdEOL(ref aktFID); break; } } // Ende switch: Kommando auswerten } // Ende while: Kommando für Kommando // Rechtes RowEdge einfügen (leere Bereiche) Leere Bereiche sind transparent, also erstmal -1 als Palettenindex schreiben for(int k = 0; k < aktFID.RowEdge[j, 1]; k++) { aktFID.CommandTable[j, aktPtr + k] = -1; } aktPtr += aktFID.RowEdge[j, 1]; } // Ende for: Zeile für Zeile #endregion Kommandotabelle // Framedaten zur zentralen Liste hinzufügen _frameInformationData.Add(aktFID); } // Ende for: Frame für Frame // Einstellungen speichern _settings = (useTransp ? Settings.UseTransparency : 0) | (useOutline1 ? Settings.UseOutline1 : 0) | (useOutline2 ? Settings.UseOutline2 : 0) | (usePlayerColor ? Settings.UsePlayerColor : 0) | (useShadow ? Settings.UseShadow : 0); #endregion Frame-Daten (RowEdge und Kommandotabelle) }
/// <summary> /// Erstellt aus der angegebenen Binär-Datei eine neue External-File-Struktur. /// </summary> /// <param name="data">Die auszulesende Binär-Datei.</param> /// <returns></returns> /// <remarks></remarks> public static ExternalFile FromBinary(RAMBuffer data) { // An den Anfang springen data.Position = 0; // Der Rückgabewert ExternalFile ret = new ExternalFile(); // Ausnahmen abfangen für ungültige Dateien try { // DRS-Dateiname uint drsNameLen = data.ReadUInteger(); ret.DRSFile = System.Text.Encoding.ASCII.GetString(data.ReadByteArray((int)drsNameLen)); // Datei-ID ret.FileID = data.ReadUInteger(); // Ressourcen-Typ uint resTypeLen = data.ReadUInteger(); ret.ResourceType = System.Text.Encoding.ASCII.GetString(data.ReadByteArray((int)resTypeLen)); // Data uint dataLen = data.ReadUInteger(); ret.Data = data.ReadByteArray((int)dataLen); } catch { // Fehler MessageBox.Show("Fehler: Das Datenobjekt konnte nicht fehlerfrei in ein ExternalFile-Objekt umgewandelt werden!", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error); } // Fertig return ret; }
/// <summary> /// Fügt eine Ressource hinzu oder ersetzt diese. /// </summary> /// <param name="data">Die hinzuzufügende / zu ersetzende Ressource.</param> /// <param name="resourceID">Die ID der hinzuzufügenden / zu ersetzenden Ressource.</param> /// <param name="resourceType">Der Typ der Ressource, rückwärts geschrieben und drei Zeichen lang.</param> /// <remarks></remarks> public void AddReplaceRessource(RAMBuffer data, ushort resourceID, string resourceType) { // Neue Ressource? if(!_files.ContainsKey(resourceID)) { // Die zu ermittelnden Tabelleninfos TableInfo myTI = new TableInfo(); Table myT = new Table(); // Suchen der Tabelle mit dem passenden Ressourcen-Typen int i; bool found = false; for(i = 0; i < _tableInfos.Count; ++i) { // Passender Ressourcen-Typ? if(_tableInfos[i].ResourceType == resourceType) { // Tabelleninfos merken myTI = _tableInfos[i]; myT = _tables[i]; found = true; break; } } // Neue Tabelle erforderlich? if(!found) { // Tabelleninfo anlegen myTI.Unknown1 = (byte)'a'; myTI.TableOffset = 0; myTI.ResourceType = resourceType; myTI.FileCount = 0; _tableInfos.Add(myTI); // Neue Tabelle ist der einzige Eintrag myT.Entries = new List<TableEntry>(); _tables.Add(myT); } // Eine Datei mehr myTI.FileCount += 1; // Tabelleneintrag für die Datei erstellen TableEntry eintr = new TableEntry(); eintr.FileSize = (uint)data.Length; eintr.FileID = resourceID; myT.Entries.Add(eintr); // Datei speichern data.Position = 0; _files.Add(resourceID, data.ReadByteArray(data.Length)); // Tabelleninfos speichern _tableInfos[i] = myTI; _tables[i] = myT; } else { // Ressource ersetzen data.Position = 0; _files[resourceID] = data.ReadByteArray(data.Length); // Tabellen durchlaufen und passende Datei-ID suchen Table currTable; TableEntry currEntry; for(int i = 0; i < _tables.Count; ++i) { // Aktuelle Tabelle abrufen currTable = _tables[i]; // Eintrag mit passender Datei-ID suchen for(int j = 0; j < currTable.Entries.Count; ++j) { // Eintrg abrufen currEntry = currTable.Entries[j]; // Passende ID? if(currEntry.FileID == resourceID) { // Dateigröße ersetzen currEntry.FileSize = (uint)data.Length; _tables[i].Entries[j] = currEntry; // Fertig return; } } } } }
/// <summary> /// Lädt die angegebene Bitmap-Datei. /// </summary> /// <param name="filename">Der Pfad zur zu ladenden Bitmap-Datei.</param> /// <param name="pal">Optional. Gibt die zu verwendende 256er-Farbtabelle an. Sonst wird die entweder die im Bitmap angegebene oder die 50500er-Farbtabelle verwendet.</param> public BitmapLoader(string filename, JASCPalette pal = null) { // Datei laden _buffer = new RAMBuffer(filename); // Header laden _header = new Header(); _header.type = ReadUShort(); _header.fileSize = ReadUInteger(); _header.reserved = ReadUInteger(); _header.offsetData = ReadUInteger(); _header.imageHeaderSize = ReadUInteger(); _header.width = ReadInteger(); _header.height = ReadInteger(); _header.layerCount = ReadUShort(); _header.bitsPerPixel = ReadUShort(); _header.compression = ReadUInteger(); _header.size = ReadUInteger(); _header.xDPI = ReadInteger(); _header.yDPI = ReadInteger(); _header.colorCount = ReadUInteger(); _header.colorImportantCount = ReadUInteger(); // Farbtabellenanzahl nachjustieren if(_header.colorCount == 0 && _header.bitsPerPixel == 8) _header.colorCount = 256; // Farbtabelle laden bool needAdjustColorTable = false; if(_header.colorCount > 0) { // Bildfarbtabelle laden _colorTable = new ColorTable(ref _buffer, _header.colorCount); // Falls eine Palette übergeben wurde, diese mit der Bildtabelle vergleichen if(pal == null || pal._farben.GetLength(0) != 256) needAdjustColorTable = true; else for(int i = 0; i < 256; ++i) { // Farben vergleichen Color aktF = _colorTable[i]; if(pal._farben[i, 0] != aktF.R || pal._farben[i, 1] != aktF.G || pal._farben[i, 2] != aktF.B) { // Farbtabellen unterscheiden sich needAdjustColorTable = true; break; } } } else { // Bei 24-Bit-Bitmaps wird die Farbtabelle später geladen _colorTable = null; } // Nach Bitzahl unterscheiden if(_header.bitsPerPixel == 8) { // Bilddatenbreite ggf. auf ein Vielfaches von 4 Bytes erhöhen int width = _header.width; // Hilfsvariable zur Performanceerhöhung (immer gleichwertig mit _header.width) int width2 = width; while(width2 % 4 != 0) { width2++; } // Binäre Original-Bilddaten einlesen _imageDataBin = _buffer.ReadByteArray(width2 * Math.Abs(_header.height)); // Neues Bilddaten-Array anlegen (ohne Füllbytes) _imageData = new byte[width * Math.Abs(_header.height)]; // Richtung bestimmen bool dirTopDown = (_header.height < 0); // Der bisher nächste Farbindex byte nearestIndex = 0; // Der Abstand zum bisher nächsten Farbindex double nearestDistance; // Der aktuelle Farbabstand double tempDistance = 0.0; // Bilddaten abrufen int height2 = Math.Abs(_header.height); for(int x = 0; x < width2; x++) { for(int y = 0; y < height2; y++) { // Wenn es sich bei dem aktuellen Pixel um kein Füllbyte handelt, diesen übernehmen if(x < width) { // Pixel abrufen byte aktCol = _imageDataBin[y * width2 + x]; // TODO: 0-Indizes in 255 umwandeln?? // Falls nötig, Farben vergleichen if(needAdjustColorTable) { // Alle Farbwerte abrufen byte aktB = _colorTable[aktCol].B; byte aktG = _colorTable[aktCol].G; byte aktR = _colorTable[aktCol].R; // Die zur Pixelfarbe nächste Palettenfarbe suchen { // Werte zurücksetzen nearestIndex = 0; nearestDistance = 441.673; // Anfangswert: maximaler möglicher Abstand // Alle Einträge durchgehen for(int i = 0; i < 256; i++) { // Aktuelle Paletten-RGB-Werte abrufen byte pR = pal._farben[i, 0]; byte pG = pal._farben[i, 1]; byte pB = pal._farben[i, 2]; // Gleiche Einträge sofort filtern if(aktR == pR && aktB == pB && aktG == pG) { // Paletten-Index überschreiben nearestIndex = (byte)i; // Fertig break; } // Abstand berechnen (Vektorlänge im dreidimensionalen RGB-Farbraum) tempDistance = Math.Sqrt(Math.Pow(aktR - pR, 2) + Math.Pow(aktG - pG, 2) + Math.Pow(aktB - pB, 2)); // Vergleichen if(tempDistance < nearestDistance) { // Index merken nearestDistance = tempDistance; nearestIndex = (byte)i; } } // Paletten-Index überschreiben aktCol = nearestIndex; } } // Ende Adjust-ColorTable // Pixel zum Hauptbildarray hinzufügen und dabei nach Top-Down / Bottom-Up unterscheiden _imageData[(dirTopDown ? y : height2 - y - 1) * width + x] = aktCol; } } } } else if(_header.bitsPerPixel == 24) { // Es handelt sich um ein 24-Bit-Bitmap, somit muss eine Farbtabelle eingeführt werden { // Farbpalettenreader abrufen JASCPalette tempPal; if(pal == null) tempPal = new JASCPalette(new RAMBuffer(BitmapLibrary.Properties.Resources.pal50500)); else tempPal = pal; // Farbpaletteninhalt in eigene Farbtabelle schreiben _colorTable = new ColorTable(); for(int i = 0; i < tempPal._farben.GetLength(0); i++) { // Eintrag in Tabelle einfügen _colorTable[i] = Color.FromArgb(tempPal._farben[i, 0], tempPal._farben[i, 1], tempPal._farben[i, 2]); // Sicherheitshalber bei i = 255 abbrechen (falls Palette zu groß sein sollte) if(i == 255) break; } } // Bilddatenbreite ggf. auf ein Vielfaches von 4 Bytes erhöhen int width = _header.width; // Hilfsvariable zur Performanceerhöhung (immer gleichwertig mit _header.width) int fillBytes = 0; while(((width * 3) + fillBytes) % 4 != 0) { fillBytes++; } // Binäre Original-Bilddaten einlesen _imageDataBin = _buffer.ReadByteArray((3 * width + fillBytes) * Math.Abs(_header.height)); // Neues Bilddaten-Array anlegen (ohne Füllbytes) _imageData = new byte[width * Math.Abs(_header.height)]; // Richtung bestimmen bool dirTopDown = (_header.height < 0); // Der bisher nächste Farbindex byte nearestIndex = 0; // Der Abstand zum bisher nächsten Farbindex double nearestDistance; // Der aktuelle Farbabstand double tempDistance = 0.0; // Bilddaten abrufen int height2 = Math.Abs(_header.height); for(int x = 0; x < width; x++) { for(int y = 0; y < height2; y++) { // Pixel abrufen byte aktB = _imageDataBin[y * (3 * width + fillBytes) + 3 * x]; byte aktG = _imageDataBin[y * (3 * width + fillBytes) + 3 * x + 1]; byte aktR = _imageDataBin[y * (3 * width + fillBytes) + 3 * x + 2]; // Die zur Pixelfarbe nächste Palettenfarbe suchen { // Werte zurücksetzen nearestIndex = 0; nearestDistance = 441.673; // Anfangswert: maximaler möglicher Abstand // Alle Einträge durchgehen for(int i = 0; i < 256; i++) { // Aktuelle Paletten-RGB-Werte abrufen byte pR = _colorTable[i].R; byte pG = _colorTable[i].G; byte pB = _colorTable[i].B; // Gleiche Einträge sofort filtern if(aktR == pR && aktB == pB && aktG == pG) { // Pixel zum Hauptbildarray hinzufügen und dabei nach Top-Down / Bottom-Up unterscheiden _imageData[(dirTopDown ? y : height2 - y - 1) * width + x] = (byte)i; // Fertig break; } // Abstand berechnen (Vektorlänge im dreidimensionalen RGB-Farbraum) tempDistance = Math.Sqrt(Math.Pow(aktR - pR, 2) + Math.Pow(aktG - pG, 2) + Math.Pow(aktB - pB, 2)); // Vergleichen if(tempDistance < nearestDistance) { // Index merken nearestDistance = tempDistance; nearestIndex = (byte)i; } } // Pixel zum Hauptbildarray hinzufügen und dabei nach Top-Down / Bottom-Up unterscheiden _imageData[(dirTopDown ? y : height2 - y - 1) * width + x] = nearestIndex; } } // Ggf. Füllbytes überspringen (bei Dateiende nicht) if(_buffer.Position < _buffer.Length - fillBytes) _buffer.Position = (_buffer.Position + fillBytes); } } }