/// <summary> /// Erstellt eine neue Farbtabelle aus einen angegebenen JASCPalette-Objekt. <param name="pal">Die zu ladende JASC-Palette.</param> /// </summary> public ColorTable(JASCPalette pal) { // Anzahl der Farben in pal verwenden, aber maximal 256 int count = Math.Min(pal._farben.GetLength(0), 256); // Alle Farben der Palette durchlaufen for(int i = 0; i < count; i++) { // Farbeintrag erstellen _colors[i] = Color.FromArgb(pal._farben[i, 0], pal._farben[i, 1], pal._farben[i, 2]); } }
/// <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); } } }