Einfarbige PNG-Dateien erzeugen mit jSingleColorPNG

Manchmal benötigt man für Tests oder Projekte einfarbige Bild-Dateien, die man dann entweder alle manuell mit einem Bildbearbeitungsprogramm erzeugt. Oder man integriert in das Projekt eine Möglichkeit, diese Dateien programatisch zu erzeugen, etwa wenn zu verwendende Farbe erst später feststeht.

In Java ist es recht einfach, eine solche Datei zu erzeugen: Man erstellt erst ein BufferedImage, holt sich davon ein Graphics2D, auf dem man dann die Vordergrundfarbe setzt und über die gesamte Größe ein gefülltes Rechteck zeichnet. Das Ergebnis lässt man sich schließlich per ImageIO ins gewünschte Format serialisieren.

Wie man dies auch anders machen kann - wenn man nur die fertige PNG-Datei benötigt - zeigt jSingleColorPNG

Das Projekt

inhaltl. Voraussetzungen

Für das Verständnis hilfreich: grundlegenge Kenntnisse über Bildformate und Farben.

techn. Voraussetzungen

Java 7 oder neuer.

Voraussetzungen

Die Idee hinter jSingleColorPNG

Statt jedes Mal den Weg über Zeichenfläche und Speichern zu gehen, habe ich mir mal das PNG-Dateiformat angesehen. Dieses Format kann lizenzfrei implementiert werden und die Spezifikation wird vom W3C verwaltet.

Kleiner Exkurs: das PNG-Dateiformat

PNG vereint die Vorzeile von JPEG und GIF: es werden bis zu 16,7 Millionen Farben (vgl. JPEG) sowie Transparenz (vgl. GIF) unterstützt. Die Daten werden dabei verlustfrei gespeichert, und trotzdem können die Dateien recht klein gehalten werden. Für mehr Details empfehle ich das Studium der entsprechenden W3C-Spezifikation.

In einer PNG-Datei können sich u.a. ein Header, eine Farbpalette, die Deflate-komprimierten Daten sowie ein Abschlussbereich befinden.

Die Idee: Erzeugen der Binärdaten von PNG statt Nutzung einer Zeichenfläche

Zunächst wird der Header geschrieben, in dem Größenangabe, Farbtiefe und der Verweis auf die Nutzung einer Farbpalette stehen. Die Farbtiefe wird auf 1 Bit gesetzt, denn unser Bild wird nur aus zwei Farben bestehen: dem Hintergrund sowie der Vordergrundfarbe.

In der darauf folgenden Palette wird diese Farbe als RGB-Wert hinterlegt.

Schliesslich folgen Deflate-komprimiert die Bilddaten. Dabei werden nur Nullen geschrieben, denn in der Palette steht im Index 0 die Farbe, die wir verwenden möchten. Die Pixel "laufen" dabei zeilenweise von links nach rechts, aufgrund der 1-Bit-Farbtiefe 8 Pixel pro Byte. Pro Zeile wird noch ein Byte für einen Filter eingefügt, der aber auch 0 ist, da er nicht verwendet wird.

Das Ende der Datei bildet ein fester Abschlussblock.

Jeder dieser vier Blöcke hat enthält neben dem jeweiligen Typ, der Länge und den Nutzdaten auch noch eine CRC16-Prüfsumme.

Das Ergebnis

Aus dem Aufruf von MiniPngCreator.createSingleColoredImage(r, g, b, width, height, increaseCompat) bekommt man dann das byte[] mit den PNG-Daten heraus.

Was hat es mit dem Parameter increaseCompat auf sich?

Bei den Tests der erzeugten Daten ist mir aufgefallen, dass nicht jede Software mit den 1-Bit-Farbtiefe palettenbasierten Bildern zurecht zu kommen scheint. Deshalb kann der Parameter increaseCompat auf true gesetzt werden, und es wird dann eine 8-Bit-Farbtiefe-Datei erzeugt. Diese ist natürlich ein bisschen länger, denn in den Nutzdaten wird nun ein Byte pro Pixel geschrieben (entgegen 1 Byte pro 8 Pixel), was zwar durch die Komprimierung zum Teil kompensiert wird, aber eben nicht vollständig.

Trotzdem sind diese Dateien auch sehr klein, und die Erzeugung der Daten ziemlich schnell.

Idee und Umsetzung

Disclaimer

Sowohl der Quellcode als auch die Informationen in diesem Text sind sorgfältig zusammengestellt und getestet worden. Trotzdem sind Fehler nicht auszuschliessen. In diesem Fall bitte gerne einen Issue auf GitHub zum Projekt erstellen. Danke.

Zusatzinfos
Navigation