Alles wird bunt mit Farbmodellen
Ein besonderer Produzent, der sich um alles kümmert, was das Bilderzeugen angeht, ist der Image Producer. Im Gegensatz dazu sind es die Image Consumer, die etwaige Bilddaten benutzen. Bei den Produzenten- und Konsumentenmodell von Image-Objekten stehen die Daten der Pixel immer in einem Byte- oder Integer-Feld zur Verfügung und stehen im Mittelpunkt des Interesses. Oft übersprungen wird das Farbmodell bei MemoryImageSource und einem createImage(). Doch das wollen wir uns nun genauer anschauen.
Die Einträge der Felder sind Pixel, und die Werte standen für Farbinformationen, genauer gesagt für Rot, Grün und Blau. Stillschweigend wird angenommen, dass diese in 24 Bit abgelegt sein müssen. Dies muss jedoch nicht so sein, und die Interpretation der Farbwerte in einem Informationswort bestimmt ein Farbmodell. Für Farbmodelle gibt es in Java die Klasse ColorModel. Mit der Klasse lassen sich dann aus einem Pixel die roten, grünen, blauen und transparenten Anteile bestimmen. Der transparente Teil, auch Alpha-Komponente genannt, bestimmt, in welcher Intensität die Farbinformationen wirken. Alpha-Werte lassen sich nur in Zusammenhang mit Bildern anwenden. Mit der Graphics-Klasse lässt sich ein Alpha-Wert nicht einstellen, der dann alte Zeichenoperationen beeinflusst. Bei den Farbmodellen ist der Anteil der Transparenz genauso lang wie ein Farbwert, nämlich acht Bit. Ein Wert von 255 sagt aus, dass der Farbwert zu 100% sichtbar ist. Ist der Wert 0, so ist die Farbe nicht zu sehen.
Java macht das Programmierleben so plattformunabhängig wie möglich. Bei wenigen oder vielen Farben auf der Zielplattform wird eine optimale Annäherung an unsere Wunschfarben errechnet. So können wir alles in 24 Bit Farbtiefe errechnen, die Dislay-Komponente sucht die wichtigsten Farben heraus und fasst Gruppen ähnlicher Farben zusammen.
Die abstrakte Klasse ColorModel
Die abstrakte Klasse ColorModel beschreibt alle Methoden für konkrete Farbklassen, sodass die Informationen über die Farbwerte und die Transparenz erreichbar sind. Obwohl die Klasse abstrakt ist, besitzt sie zwei Konstruktoren, die von den Unterklassen benutzt werden. Direkte Unterklassen sind ComponentColorModel, IndexColorModel und PackedColorModel.
abstract class java.awt.image.ColorModel
implements Transparency
ColorModel( int pixel\_bits, int bits[], ColorSpace cspace, boolean hasAlpha, boolean isAlphaPremultiplied, int transparency, int transferType )ColorModel(int bits)
Der zweite Konstruktor ist praktisch, da dieser nur die Farbtiefe in Bits erwartet. Diese abstrakte Klasse besitzt jedoch die statische Fabrik-Methode getRGBdefault(), die ein ColorModel-Objekt zurückliefert. Das Standardfarbmodell, auch sRGB genannt, ist ein Farbmodell, welches die Werte als 24-Bit-Tupel mit den Komponenten Alpha, Rot, Grün und Blau enthält. Dieses Farbmodell lässt sich etwa für ein Memory-Image einsetzen. Der erste Konstruktor ist noch leistungsfähiger und seit Java 1.2 dabei. Mit seiner Hilfe muss ein Farbwert nicht zwingend in einem Integer kodiert sein.
Die Methode getPixelSize() liefert die Farbtiefe eines Farbmodells. Das Standardmodell besitzt eine Tiefe von 32 Bit (24 für die Farben und dann noch den Alpha-Kanal). So gibt auch die folgende Zeile als Anwort auf die Frage nach der Anzahl der Farben im Standardmodell 32 Bit aus:
System.out.println( ColorModel.getRGBdefault().getPixelSize() );
Die Hauptaufgabe einer Farbmodell-Klasse ist die Auswertung der Farbinformationen aus einem Speicherwort. Mit drei Methoden lassen sich die verschiedenen Farben auslesen. getRed(int pixel), getGreen(int pixel) und getBlue(int pixel), hinzu kommt noch getAlpha(int pixel). Jede dieser Methoden ist abstrakt und liefert eine Ganzzahl mit dem Farbwert zurück. Wie wir später sehen werden, ist das einfachste Modell genau jenes, das wir bisher immer benutzt haben. Dieses liest nämlich genau von den Stellen 24, 16 und 8 die Farbwerte aus. Da die Methoden abstrakt sind, müssen Unterklassen dieses Verhalten programmieren.
Eine weitere Methode ist getRGB(), welche ein int mit allen Farben im entsprechenden Farbformat zurückliefert. Die Implementierung basiert auf den Anfrage-Methoden.
public int getRGB(int pixel) {
return (getAlpha(pixel) > 8);
}
An getRed() sehen wir ebenso, dass der Pixel auch direkt ein Index für das private Feld rgb ist. Wenn der Index über die Feldgröße läuft, müssen wir den Fehler selbst behandeln.
final public int getRed(int pixel) {
return (rgb[pixel] >> 16) & 0xff;
}
Wenden wir unsere Aufmerksamkeit auf ein Programm, welches ein Bytefeld erzeugt und aus sechs Farben die Pixel in das Feld schreibt. Zum Schluss konvertieren wir das Bytefeld mit einem MemoryImageSource in ein Image-Objekt. Für diese Klasse können wir ein IndexColorModel angeben, das dann folgendes Format hat:
ColorModel cm = IndexColorModel( 8, colorCnt, r, g, b );
Hier handelt es sich um ein Farbmodell mit acht Bits und sechs Farben. Die folgenden Werte zeigen auf die drei Felder mit den Farbwerten. Anschließend erzeugt createImage() mit diesem Farbmodell das Image-Objekt.
Image i = createImage( new MemoryImageSource(w,h,cm,pixels,0,w) );
Zum kompletten Beispiel:
import java.awt.*;
import java.awt.image.*;
public class IndexColorModelDemo extends Frame
{
Image i;
private final static int w = 400, h = 400;
int pixels[] = new int [w*h];
Color colors[] = {
Color.red, Color.orange, Color.yellow, Color.green, Color.blue, Color.magenta
};
IndexColorModelDemo()
{
int colorCnt = colors.length;
byte r[] = new byte[colorCnt],
g[] = new byte[colorCnt],
b[] = new byte[colorCnt];
for ( int i = 0; i < colorCnt; i++ ) {
r[i] = (byte) colors[i].getRed();
g[i] = (byte) colors[i].getGreen();
b[i] = (byte) colors[i].getBlue();
}
int index = 0;
for ( int y = 0; y < h; y++ )
for ( int x = 0; x < w; x++ )
pixels[index++] = (int)(Math.random() * colorCnt);
i = createImage( new MemoryImageSource( w, h, new IndexColorModel(8, colorCnt, r, g, b), pixels, 0, w) );
}
public void paint( Graphics g )
{
if ( i != null )
g.drawImage( i, 0, 0, this );
}
public static void main( String args[] )
{
IndexColorModelDemo d = new IndexColorModelDemo();
d.setSize( w, h );
d.show();
}
}