Ein Tool zum Erstellen von Vista-kompatiblen Icons
Oft brauche ich Icons für eigene Programme. Die Grafiken dafür erstelle ich mit einem separaten Bildbearbeitungs-Programm, nun brauchte ich nur noch ein Tool, um die Grafiken zu einem Icon zusammenzustellen.
Wesentlichen Impuls für dieses kleine Programm war für mich der Artikel IconLib - Icons Unfolded (MultiIcon and Windows Vista supported) von CastorTiu auf codeproject.com. In diesem Artikel ist ausführlich beschrieben, wie Icons aufgebaut sind. Das zu diesem Artikel gehörende Codebeispiel bietet mehr Möglichkeiten als das Programm hier, unter anderem kann man Icons auch aus DLLs und anderen Datei-Formaten extrahieren. Für mich war jedoch nur interessant, eigene Icons in den gängigen Formaten zu erstellen oder vorhandene Icons zu bearbeiten und die Struktur einer Icon-Datei in Form übersichtlicher Klassen darzustellen. Auch sollte alles ohne API möglich sein.
Der im Beispiel enthaltene Quelltext enthält alle notwendigen Methoden, um eine vorhandene Icon-Datei zu öffnen, enthaltene Images zu exportieren, neue IconImages aus bestehenden Bitmaps auf dem Computer oder in der Datei vorhandenen Images zu erstellen und die Datei als Icon zu speichern. Für das Datei-Management, das farbige Rendern der Steuerelemente und Undo-Redo habe ich Methoden verwendet, die in anderen Beispielen hier auf der Webseite detaillierter beschrieben sind.
Im folgenden will ich die dem Icon-Format zugrunde liegenden Strukturen (im Beispiel als separat benannte Klassen enthalten) noch etwas näher erläutern.
Ein Icon ist erst einmal eine Art Symbol, das z.B. ein Computerprogramm oder ein Dokument auf dem Bildschirm des Computers bildlich darstellt. So wird zum Beispiel eine Text-Datei durch ein leeres Blatt symbolisiert oder der InternetExplorer durch das bekannte blaue e.
So ein Programm-Bildchen erscheint aber abhängig von der Bildschirmauflösung oder der gewählten Ordner-Ansicht (Liste, Kacheln,…) oder auch an unterschiedlichen Positionen (auf dem Desktop, im Programm-Menü, in der linken oberen Ecke eines Programm-Formulars) oder abhängig von der Grafikkarten-abhängigen Anzahl darstellbarer Farben unterschiedlich.
Dafür enthält ein Icon eine ganze Auswahl an verschiedenen Bildchen mit unterschiedlichen Größen und unterschiedlichen Farbtiefen. Das Betriebssystem liefert abhängig von der nötigen Darstellungsgröße und Farbtiefe eines dieser Einzelbilder und skaliert es, wenn notwendig.
Wie sieht ein Icon im Detail aus?
Vor Vista bestand ein Icon aus mehreren Bitmaps.
Gebraucht wurden Kantenlängen von 48, 32 und 16 Pixeln. Als Farbtiefen eigneten sich 32 Bit ARGB, 8 Bit indizierte Farben und 4 Bit indiziert. Also insgesamt 9 einzelne Bitmaps.
Auch wenn diese Bitmap schon selbst transparente Farben enthalten könnte, enthält das Icon-Format zusätzlich zu den Bitmap-Farbinformationen für jedes Einzelbild noch ein Array mit True/False-Informationen, das für jeden einzelnen Pixel im Bild angibt, ob dieser Pixel transparent ist, oder nicht.
In Windows Vista können Icons auch größer dargestellt werden. Gewünscht sind Darstellungsgrößen bis zu 256*256 Pixeln. Um die Icon-Dateien nicht unverschämt groß werden zu lassen, hat Microsoft sich überlegt, diese größeren Bilder im PNG-Format zu komprimieren (es ist aber auch das bisher verwendete Bitmap-Format für diese großen Bilder erlaubt). Der Vorteil an PNG’s ist, dass sie wenig Speicher brauchen und selbst Transparenz-Informationen enthalten.
So werden für ein Vista-Icon folgende Formate empfohlen:
Kantenlänge (Pixel) | Pixelformat | ImageFormat |
---|---|---|
256 | 32 Bit ARGB | PNG |
256 | 8 Bit indiziert | PNG |
256 | 4 Bit indiziert | PNG |
48 | 32 Bit ARGB | BMP |
48 | 8 Bit indiziert | BMP |
48 | 4 Bit indiziert | BMP |
32 | 32 Bit ARGB | BMP |
32 | 8 Bit indiziert | BMP |
32 | 4 Bit indiziert | BMP |
16 | 32 Bit ARGB | BMP |
16 | 8 Bit indiziert | BMP |
16 | 4 Bit indiziert | BMP |
Wie ist die Icon-Datei (in Bytes) aufgebaut ?
Icon Header | Der IconHeader ist nur 6 Bytes lang und enthält u.a. die Anzahl der enthaltenen Images |
IconDirectory 1 | Ein IconDirectory-Abschnitt ist 16 Bytes lang und enthält Informationen zu Breite, Höhe, Farbtiefe Position und Länge (in Bytes) eines einzelnen IconImage-Abschnitts. |
IconDirectory 2 | |
… | |
IconDirectory n | |
IconImage 1 | Ein IconImage-Abschnitt enthält entweder Daten für ein PNG-Bild oder (für ein Bitmap-Bild) Bitmap-Daten+Transparenz-Daten |
IconImage 2 | |
… | |
IconImage n |
Aufbau des IconHeader-Abschnitts
Bytes | Datentyp | Bezeichnung | Erläuterung |
---|---|---|---|
2 | UShort | Reserved | Reserviert, Wert muss 0 sein |
2 | UShort | Type | Typ – Wert muss bei Icon „1“ sein (bei Cursor-Datei steht hier „0“) |
2 | UShort | ImageCount | Anzahl im Icon enthaltener Einzelbilder |
Gesamtlänge: 6 Bytes |
Aufbau des IconDirectory -Abschnitts
Bytes | Datentyp | Bezeichnung | Erläuterung |
---|---|---|---|
1 | Byte | Width | Breite des Bilds in Pixeln (wenn >256, steht hier „0“) |
1 | Byte | Height | Höhe des Bilds in Pixeln (wenn >256, steht hier „0“) |
1 | Byte | ColorCount | Anzahl Farben der Farbpalette (wenn >256 oder ohne Farbpalette, steht hier „0“) |
1 | Byte | Reserved | Reserviert, ist immer 0 |
2 | UShort | Planes | Anzahl Farbplanes, ist immer 1 |
2 | UShort | BitCount | Bits per Pixel (Farbtiefe) für das Bild. Übliche Werte sind 32, 8, 4 |
4 | UInteger | BytesInRes | Länge des zugehörigen IconImage-Abschnitts in Byte |
4 | UInteger | ImageOffset | Start-Position des zugehörigen IconImage-Abschnitts (Byte) in der Icon-Datei |
Gesamtlänge: 16 Bytes |
Aufbau eines IconImage-Abschnitts
a) PNG-Komprimiert: („PNGImage“) ein komplettes PNG-Bild, mit allen Bytes, die dazu gehören. Abmessungen des Bildes, Farbtiefe… sind in diesem Inhalt bereits festgelegt
b) Bitmap („BitmapImage“)
Aufbau eines BitmapImage-Abschnitts
BitmapInfoHeader | Definiert Breite und Höhe der Bitmap in Pixeln, Farbtiefe, Anzahl Farben in der Farbpalette … |
Palette | Alle in der Farbpalette der Bitmap enthaltenen Farben als RGBQuad |
Farbdaten (XOR) | Ein Array mit Bytes, das abhängig von der Farbtiefe der Bitmap Farbindizes oder Farben für jeden einzelnen Pixel der Bitmap definiert |
Transparenzdaten (AND) | Ein Array, in dem für jeden einzelnen Pixel der Bitmap eine Information enthalten ist, ob dieser transparent erscheinen soll oder nicht |
Aufbau eines BitmapInfoHeader-Abschnitts
Bytes | Datentyp | Bezeichnung | Erläuterung |
---|---|---|---|
4 | UInteger | Size | Größe dieses Headers (40 Byte) |
4 | UInteger | Width | Breite der Bitmap in Pixeln |
4 | UInteger | Height | Höhe der Bitmap in Pixeln, im Icon der doppelten Höhe entsprechend |
2 | UShort | Planes | Anzahl Farb-Planes (immer „1“) |
2 | UShort | BitCount | Bits pro Pixel (Farbtiefe). Typische Werte sind4, 8, 16, 24 und 32. Wenn der Wert für BitCount 4 ist, kann das Bild 16 Farben enthalten, wenn dieser Wert 8 ist, sind 256 Farben verfügbar. BitCount legt auch fest, wie ein Pixel in den Pixeldaten (XOR) kodiert wird. Bei 4 BPP (indizierte Farben) gibt es 16 mögliche Farbindizes für einen Pixel. So teilen sich in den XOR-Daten die Farbinformationen für zwei nebeneinander liegende Bildpixel ein gemeinsames Byte. Bei 8BPP (256 mögliche Farbindizes) ist die Information, welcher Farbindex einem Pixel zugeordnet ist, in einem Byte enthalten. |
4 | UInteger | Compression | verwendete Komprimierungs-Methode – für einfache Bitmaps „0“ – nur dies findet hier Verwendung |
4 | UInteger | SizeImage | Länge des Bitmap-Daten-Abschnitts in Byte |
4 | Integer | XPelsPerMeter | Horizontale Auflösung der Bitmap in Pixel pro Meter |
4 | Integer | YPelsPerMeter | Vertikale Auflösung der Bitmap in Pixel pro Meter |
4 | UInteger | ClrUsed | Anzahl Farben in der Palette oder 0 |
4 | UInteger | ClrImportant | Anzahl wichtiger Farben in der Palette oder 0, wenn alle Farben wichtig sind |
Gesamtlänge: 40 Bytes |
Aufbau eines RGBQuad-Abschnitts
Bytes | Datentyp | Bezeichnung | Erläuterung |
---|---|---|---|
1 | Byte | Blue | Blauanteil der Farbe |
1 | Byte | Green | Grünanteil der Farbe |
1 | Byte | Red | Rotanteil der Farbe |
1 | Byte | Reserved | Reserviert |
Gesamtlänge: 4 Bytes |
Das Speichern der Bitmap-Daten als IconImage im Icon ähnelt sehr dem Speichern einer richtigen Bitmap-Datei. Um das mal komplett darzustellen, habe ich das Erstellen einer Bitmap-Datei aus den einzelnen Bestandteilen im Code demonstriert.
Dazu ist in der Klasse BitmapImage die Methode CreateExtraBitmap (Path As String) zu finden.
Diese Methode speichert die Bitmap-Daten als Bitmap unter einem angegebenen Pfad im Dateisystem des Computers. Auf transparente Farben wird dabei bei Images mit indizierten Farbwerten allerdings verzichtet. Die Transparenz-Werte sind ja eben nicht in den XOR-Daten, sondern nur in den AND-Daten enthalten (und in der realen Bitmap werden eben nur XOR-Daten gespeichert). So entsteht übrigens auch einen weiterer Unterschied zwischen den indizierten Bitmap und PNG-Images im Icon: Die indizierten PNG-Images müssen bei Images mit Transparenz Platz für eine transparente Paletten-Farbe machen. Bei den Bitmap-Images ist die Transparenz-Information in den AND-Daten enthalten, bei den PNG’s gibt’s kein AND. Will man also aus einer Bitmap (mit transparenten Pixeln) ein PNG- und ein Bitmap-Image mit je 4 Bits per Pixel machen, kann das Bitmap-Image 16 Farbabstufungen enthalten, das PNG-Image nur 15 Farben, die sechzehnte Palettenfarbe im PNG-Image wird für die transparente Farbe benötigt. So erklären sich auch die unterschiedlichen Methoden zum Erstellen von IconImages (PNG- bzw. Bitmap-Image) aus einer Bitmap-Vorlage.
Das Feature GifBackColor erfüllt folgenden Zweck: Wenn eine indizierte Bitmap aus einer Bitmap mit Transparenz erstellt wird, entstehen in der indizierten Bitmap an der Grenze zwischen transparenten und nichttransparenten Pixeln manchmal unerwünscht harte Farbübergänge. Zum Beispiel würde aus halbtransparentem Weiss in einer 32BPP-Bitmap in der indizierten Bitmap strahlend helles Weiss. Das ist nicht immer erwünscht. Für diesen Zweck bieten Grafikprogramme zum Erstellen indizierter Gifs aus ARGB-Bildern an, einen Hintergrundfarbton auszuwählen. Diese Hintergrundfarbe könnte z.B. ein leichter Grauton sein, wenn die indizierte Bitmap vor grauen, weißen oder schwarzen Hintergründen erscheinen soll. So erhalten ursprünglich halbtransparente Pixel bei Reduktion auf indizierte Farben einen Mischfarbton aus der gewünschten Gif-Hintergrundfarbe und der Originalfarbe des Pixels. Wenn man auf dieses Verhalten verzichten will, kann man für GifBackColor auch „Transparent“ als Farbe auswählen.
Wichtig war mir in diesem Projekt auch, Undo-Redo-Funktionalität für Änderungen an einer Icon-Datei zu haben. Damit wird dem Benutzer erspart, dass er bei jeder Aktion eine MessageBox mit Fragen wie "Wollen Sie dieses Bild jetzt wirklich löschen?"... beantworten muss. Hat der Benutzer mal einen Fehler gemacht, kann er diese Aktion wieder rückgängig machen. Und weil das Ändern der Icon-Datei in diesem Beispiel auch nur durch Hinzufügen oder Entfernen von Images erfolgt, wurde die Realisierung von Und und Redo auch wirklich einfach. Jedes IconImage-Objekt erhält bereits beim Instanziieren einen eindeutigen temporären Dateinamen. Wird dieses IconImage aus dem Icon entfernt, wird das IconImage unter seinem temporären Pfad (im Temp-Ordner des Dateisystems) im Icon-Format gespeichert. Gleichzeitig wird der temporäre Dateiname einer dafür vorgesehenen String-Liste angefügt. Will man später dieses IconImage-Objekt wiederherstellen, wird die temporäre IconImage-Datei wieder gelesen. Beim Dispose der aktuellen Icon-Datei (z.B. beim Schließen der Anwendung) werden alle in der String-Liste enthaltenen temporären Dateien gelöscht.