/// <summary> /// Exportiert den angegebenen Frame in eine Bitmap-Datei (50500er-Palette). /// </summary> /// <param name="frameID">Die ID des zu exportierenden Frame.</param> /// <param name="Pal">Die Farbtabelle.</param> /// <param name="filename">Die Bitmap-Datei, in die die Daten geschrieben werden sollen.</param> /// <param name="mask">Die zu exportierende Maske (oder reine Grafik) als Element der Masks-Enumeration.</param> public void exportFrame(int frameID, string filename, ColorTable Pal, Masks mask = Masks.Graphic) { // Framedaten abrufen FrameInformationHeader FIH = _frameInformationHeaders[(int)frameID]; // Rückgabebild erstellen BitmapLoader bmp = new BitmapLoader((int)FIH.Width, (int)FIH.Height, Pal); // Welche Maske ist gewollt? if(mask == Masks.Graphic) // Es handelt sich um die reine Frame-Grafik { // Bild pixelweise durchgehen for(int i = 0; i < FIH.Width; i++) { for(int j = 0; j < FIH.Height; j++) { // Palettenindex abrufen int farbID = _frameInformationData[(int)frameID].CommandTable[j, i]; // Sonderindizes in die jeweiligen Farben umsetzen; meist Rein-Weiß switch(farbID) { case -1: farbID = 255; break; case -2: farbID = 255; break; case -3: farbID = 255; break; case -4: farbID = _schatten; break; } // Pixel in das Bild schreiben bmp[i, j] = (byte)farbID; } } } else if(mask != Masks.PlayerColor) // Es handelt sich um eine Maske (außer der Spielerfarbe) { // Den Index und die Zielfarbe angeben int maskIndex = 0; int maskColor = 0; if(mask == Masks.Transparency) { maskIndex = -1; maskColor = 0; } else if(mask == Masks.Outline1) { maskIndex = -2; maskColor = 8; } else if(mask == Masks.Outline2) { maskIndex = -3; maskColor = 124; } else if(mask == Masks.Shadow) { maskIndex = -4; maskColor = 131; } // Bild pixelweise durchgehen for(int i = 0; i < FIH.Width; i++) { for(int j = 0; j < FIH.Height; j++) { // Palettenindex abrufen int farbID = _frameInformationData[(int)frameID].CommandTable[j, i]; // Je nach Masken Farben setzen if(farbID == maskIndex) { // Masken-Farbe übernehmen farbID = maskColor; } else { // Keine Maske, also weiß farbID = 255; } // Pixel übernehmen bmp[i, j] = (byte)farbID; } } } else // Spielerfarbe { // Bild pixelweise durchgehen for(int i = 0; i < FIH.Width; i++) { for(int j = 0; j < FIH.Height; j++) { // Palettenindex abrufen int farbID = _frameInformationData[(int)frameID].CommandTable[j, i]; // Liegt keine Spielerfarbe vor? if(farbID < 16 || farbID > 23) { // Pixel weiß einfärben farbID = 255; } // Pixel übernehmen bmp[i, j] = (byte)farbID; } } } // Fertig, Bild speichern bmp.saveToFile(filename); }
/// <summary> /// Ersetzt einen vorhandenen Frame oder fügt einen neuen am Ende hinzu. /// </summary> /// <param name="frameID">Die ID des Frames (bei Ersetzung) oder -1 für einen neuen Frame.</param> /// <param name="frameBitmap">Die Bilddaten, die in Kommando-Daten umgewandelt werden sollen (mit 50500er-Palette versehen).</param> /// <param name="pal">Die zu verwendende Farbpalette.</param> /// <param name="ankerX">Die X-Koordinate des Mittelpunkts der Grafik.</param> /// <param name="ankerY">Die Y-Koordinate des Mittelpunkts der Grafik.</param> /// <param name="settings">Die Einstellungen als Wert der Settings-Enumeration.</param> public void addReplaceFrame(int frameID, BitmapLoader frameBitmap, ColorTable pal, int ankerX, int ankerY, Settings settings) { #region Grundlegenes und Initialisierungen // Größen ermitteln int height = frameBitmap.Height; int width = frameBitmap.Width; // Framedaten FrameInformationData aktFID; FrameInformationHeader aktFIH; // Neuer Frame? if(frameID < 0 || frameID >= _frameInformationData.Count) { // Neue Framedaten erstellen aktFID = new FrameInformationData(); _frameInformationData.Add(aktFID); aktFIH = new FrameInformationHeader(); _frameInformationHeaders.Add(aktFIH); // Neue Frame-ID ermitteln frameID = _frameInformationData.Count - 1; } else { // Frame laden aktFID = _frameInformationData[frameID]; aktFIH = _frameInformationHeaders[frameID]; } // Anker speichern aktFIH.AnchorX = ankerX; aktFIH.AnchorY = ankerY; // Speichern der Abmessungen aktFIH.Width = (uint)width; aktFIH.Height = (uint)height; // RowEdge initialisieren aktFID.BinaryRowEdge = new BinaryRowedge[height]; for(int i = 0; i < height; i++) { aktFID.BinaryRowEdge[i] = new BinaryRowedge(); } // Kommando-Offset-Array initalisieren aktFID.CommandTableOffsets = new uint[height]; // Kommando-Array initialisieren aktFID.BinaryCommandTable = new List<BinaryCommand>(); #endregion Grundlegenes und Initialisierungen // Bild in umgesetzte Kommandodaten umwandeln #region Umwandlung in umgesetzte Kommandodaten (Masken + Farbenindizes) int[,] xCommandTable; // [Y, X] => Hilfsvariable für effizienteren Zugriff, enthält alle Palettenverweise des Bilds bzw. die negativen Masken-Pixel aktFID.CommandTable = new int[height, width]; { // Bildindizes (Daten) in die umgesetzte Kommandotabelle schreiben for(int i = 0; i < height; i++) { for(int j = 0; j < width; j++) { aktFID.CommandTable[i, j] = frameBitmap[j, i]; } } // Aus Effizienzgründen die umgewandelte Kommandotabelle in ein nahezu Objekt-freies int-Array kopieren (Direktzugriff ohne Umwege über unnötige OOP) xCommandTable = (int[,])aktFID.CommandTable.Clone(); // Berechnen der Masken #region Maskenberechnung { // Unsichtbar wird immer 255er-Weiß int _transMaskIndex = 255; // Transparenz if((settings & Settings.UseTransparency) == Settings.UseTransparency) { // Jedem transparenten 255er-Pixel den Index -1 verpassen for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { if(xCommandTable[y, x] == _transMaskIndex) xCommandTable[y, x] = -1; } } } // Sämtliche Transparenz hat nun den Wert -1 _transMaskIndex = -1; // Umriss 1 (Outline 1): Der Umriss des Objekts in der Grafik (wenn das Objekt hinter einem anderen steht, werden diese Umrisse in der Spielerfarbe dargestellt) Es müssen alle vier Richtungen durchgerechnet werden if((settings & Settings.UseOutline1) == Settings.UseOutline1) { // Waagerecht for(int y = 0; y < height; y++) { // Nach rechts for(int x = 0; x < width - 1; x++) { // Befindet sich beim nächsten Pixel keine Transparenz und auch kein Umriss 1? => Umriss if(xCommandTable[y, x] == _transMaskIndex && xCommandTable[y, x + 1] != _transMaskIndex && xCommandTable[y, x + 1] != -2) { xCommandTable[y, x] = -2; } } // Nach links for(int x = width - 1; x > 0; x--) { // Befindet sich beim nächsten Pixel keine Transparenz und auch kein Umriss 1? => Umriss if(xCommandTable[y, x] == _transMaskIndex && xCommandTable[y, x - 1] != _transMaskIndex && xCommandTable[y, x - 1] != -2) { xCommandTable[y, x] = -2; } } } // Senkrecht for(int x = 0; x < width; x++) { // Nach unten for(int y = 0; y < height - 1; y++) { // Befindet sich beim nächsten Pixel keine Transparenz und auch kein Umriss 1? => Umriss if(xCommandTable[y, x] == _transMaskIndex && xCommandTable[y + 1, x] != _transMaskIndex && xCommandTable[y + 1, x] != -2) { xCommandTable[y, x] = -2; } } // Nach oben for(int y = height - 1; y > 0; y--) { // Befindet sich beim nächsten Pixel keine Transparenz und auch kein Umriss 1? => Umriss if(xCommandTable[y, x] == _transMaskIndex && xCommandTable[y - 1, x] != _transMaskIndex && xCommandTable[y - 1, x] != -2) { xCommandTable[y, x] = -2; } } } } // Umriss 2 (Outline 2): Zweiter Umriss, umschließt Umriss 1 if((settings & Settings.UseOutline2) == Settings.UseOutline2) { // Zuerst Outline 1 für das Outline 2-Array neu berechnen (falls dies noch nicht passiert ist) if((settings & Settings.UseOutline1) != Settings.UseOutline1) { // Waagerecht for(int y = 0; y < height; y++) { // Nach rechts for(int x = 0; x < width - 1; x++) { // Befindet sich beim nächsten Pixel keine Transparenz und auch kein Umriss 1? => Umriss if(xCommandTable[y, x] == _transMaskIndex && xCommandTable[y, x + 1] != _transMaskIndex && xCommandTable[y, x + 1] != -2) { xCommandTable[y, x] = -2; } } // Nach links for(int x = width - 1; x > 0; x--) { // Befindet sich beim nächsten Pixel keine Transparenz und auch kein Umriss 1? => Umriss if(xCommandTable[y, x] == _transMaskIndex && xCommandTable[y, x - 1] != _transMaskIndex && xCommandTable[y, x - 1] != -2) { xCommandTable[y, x] = -2; } } } // Senkrecht for(int x = 0; x < width; x++) { // Nach unten for(int y = 0; y < height - 1; y++) { // Befindet sich beim nächsten Pixel keine Transparenz und auch kein Umriss 1? => Umriss if(xCommandTable[y, x] == _transMaskIndex && xCommandTable[y + 1, x] != _transMaskIndex && xCommandTable[y + 1, x] != -2) { xCommandTable[y, x] = -2; } } // Nach oben for(int y = height - 1; y > 0; y--) { // Befindet sich beim nächsten Pixel keine Transparenz und auch kein Umriss 1? => Umriss if(xCommandTable[y, x] == _transMaskIndex && xCommandTable[y - 1, x] != _transMaskIndex && xCommandTable[y - 1, x] != -2) { xCommandTable[y, x] = -2; } } } } // Outline 2 auf Outline 1 basierend erstellen (Umriss 1 wird wie eine gewöhnliche Farbe behandelt) { // Waagerecht for(int y = 0; y < height; y++) { // Nach rechts for(int x = 0; x < width - 1; x++) { // Befindet sich beim nächsten Pixel keine Transparenz? => Umriss if(xCommandTable[y, x] == _transMaskIndex && xCommandTable[y, x + 1] != _transMaskIndex && (x + 1 != width - 1)) { xCommandTable[y, x] = -3; } } // Nach links for(int x = width - 1; x > 0; x--) { // Befindet sich beim nächsten Pixel keine Transparenz? => Umriss if(xCommandTable[y, x] == _transMaskIndex && xCommandTable[y, x - 1] != _transMaskIndex && (x + 1 != width - 1)) { xCommandTable[y, x] = -3; } } } // Senkrecht for(int x = 0; x < width; x++) { // Nach unten for(int y = 0; y < height - 1; y++) { // Befindet sich beim nächsten Pixel keine Transparenz? => Umriss if(xCommandTable[y, x] == _transMaskIndex && xCommandTable[y + 1, x] != _transMaskIndex) { xCommandTable[y, x] = -3; } } // Nach oben for(int y = height - 1; y > 0; y--) { // Befindet sich beim nächsten Pixel keine Transparenz? => Umriss if(xCommandTable[y, x] == _transMaskIndex && xCommandTable[y - 1, x] != _transMaskIndex) { xCommandTable[y, x] = -3; } } } } } // Schatten (Farbindex 131) if((settings & Settings.UseShadow) == Settings.UseShadow) { // Jeden Schattenpixel mit -4 markieren for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { if(xCommandTable[y, x] == 131) xCommandTable[y, x] = -4; } } } } // Ende Maskenberechnung #endregion Maskenberechnung // Kommandotabelle zwischenspeichern aktFID.CommandTable = (int[,])xCommandTable.Clone(); } // Ende Umwandlung in umgesetzte Kommandodaten #endregion Umwandlung in umgesetzte Kommandodaten (Masken + Farbenindizes) // Generieren der RowEdge-Daten (Außenränder, Bestimmung waagerecht) #region Generierung der RowEdge-Daten for(int y = 0; y < height; y++) { // Von links nach rechts int left = 0; for(int x = 0; x < width; x++) { if(xCommandTable[y, x] != -1) break; else left++; } // Von rechts nach links int right = 0; for(int x = width - 1; x >= 0; x--) { if(xCommandTable[y, x] != -1) break; else right++; } // Liegt eine leere Zeile vor? if(left == width) { // Leere Zeile right = 0; } // Werte speichern aktFID.BinaryRowEdge[y] = new BinaryRowedge(left, right); } #endregion Generierung der RowEdge-Daten // Ziel-Kommando-Tabelle erstellen CreateBinaryCommandTable(aktFID, width, height, settings); // Frame-Daten-Variablen speichern _frameInformationHeaders[frameID] = aktFIH; _frameInformationData[frameID] = aktFID; // Sicherheitshalber Frame-Anzahl im Header aktualisieren _headers.FrameCount = (uint)_frameInformationHeaders.Count; // Fertig: RowEdge-Daten und Kommandotabelle vollständig erstellt. }