14.10 Farben
Der Einsatz von Farben in Java-Programmen ist Dank der Color-Klasse und der Farbräume einfach. Die Klasse stellt eine Vielzahl von Routinen zur Verfügung, mit denen Color-Objekte erzeugt und manipuliert werden können.
class java.awt.Color
implements Paint, Serializable
|
|
Color( float r, float g, float b )
Erzeugt ein Color-Objekt mit den Grundfarben Rot, Grün und Blau. Die Werte müssen im Bereich 0.0 bis 1.0 liegen. |
|
Color( int r, int g, int b )
Erzeugt ein Color-Objekt mit den Grundfarben Rot, Grün und Blau. Die Werte müssen im Bereich 0 bis 255 liegen. |
|
Color( int rgb )
Erzeugt ein Color-Objekt aus dem rgb-Wert, der die Farben Rot, Grün und Blau kodiert. Der Rotanteil befindet sich unter den Bits 16 bis 23, der Grünanteil in 8 bis 15 und der Blauanteil in 0 bis 7. Da ein Integer immer 32 Bit breit ist, ist jede Farbe durch ein Byte (8 Bit) repräsentiert. |
Eine private Funktion testColorValueRange() der Color-Klasse überprüft, ob die Werte tatsächlich zwischen 0.0 und 1.0 (erster Fall) oder zwischen 0 und 255 (zweiter Fall) liegen. Wenn nicht, wird eine IllegalArgumentException ausgelöst. Im dritten Fall werden von der Ganzzahl nur die Farbinformationen aus den 24 Bit genommen. Sonstige Werte werden einfach nicht betrachtet und mit einem Alpha-Wert gleich 255 überschrieben. So zeigt es auch der Einzeiler aus dem Quelltext an:
public Color( int rgb ) {
value = 0xff000000 | rgb;
}
abstract class java.awt.Graphics
|
|
abstract void setColor( Color c )
Setzt die aktuelle Farbe, die dann von den Zeichenfunktionen umgesetzt wird. |
|
abstract Color getColor()
Liefert die aktuelle Farbe. |
|
abstract void setXORMode( Color c )
Setzt die Pixel-Operation auf XOR. Abwechselnde Punkte werden in der aktuellen Farbe und der mit dieser Funktion gesetzten XOR-Farbe gesetzt. |
Hinweis Die menschliche Farbwahrnehmung: Menschen unterscheiden Farben nach drei Eigenschaften: Farbton, Helligkeit und Sättigung. Der Mensch kann etwa zweihundert Farbtöne unterscheiden. Diese werden durch die Wellenlänge des Lichts bestimmt. Die Lichtintensität und Empfindlichkeit unserer Rezeptoren lässt uns etwa fünfhundert Helligkeitsstufen unterscheiden. Bei der Sättigung handelt es sich um eine Mischung mit weißem Licht. Hier erkennen wir etwa zwanzig Stufen. Damit kann unser visuelles System etwa zwei Millionen (200 x 500 x 20) Farbnuancen unterscheiden.
|
14.10.1 Zufällige Farbblöcke zeichnen
Um die Möglichkeiten der Farbgestaltung einmal zu beobachten, betrachten wir die Ausgabe eines Programms, welches Rechtecke mit wahllosen Farben anzeigt.
Listing 14.12 ColorBox.java
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class ColorBox extends JFrame
{
public void paint( Graphics g )
{
g.setColor( Color.white );
g.fillRect( 0, 0, getWidth(), getHeight() );
Random r = new Random();
for ( int y = 45; y < getHeight() - 25; y += 30 )
for ( int x = 20; x < getWidth() - 25; x += 30 )
{
g.setColor( new Color(r.nextInt(256),r.nextInt(256),r.nextInt(256)) );
g.fillRect( x, y, 25, 25);
g.setColor( Color.black );
g.drawRect( x-1, y-1, 25, 25 );
}
}
public static void main( String args[] )
{
JFrame cb = new ColorBox();
cb.setTitle( "Neoplastizismus" );
cb.setSize( 300, 300 );
cb.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
cb.show();
}
}
Hier klicken, um das Bild zu Vergrößern
Abbildung 14.4 Programmierter Neoplastizismus
Das Fenster der Applikation hat eine gewisse Größe, die wir mit size() in der Höhe und Breite abfragen. Anschließend erzeugen wir Blöcke, die mit einer zufälligen Farbe gefüllt sind. fillRect() übernimmt diese Aufgabe. Da die gefüllten Rechtecke immer in der Vordergrundfarbe gezeichnet werden, setzen wir den Zeichenstift durch die Funktion setColor(), die natürlich Objektmethode von Graphics ist. Entsprechend gibt es eine korrespondierende Funktion getColor(), die die aktuelle Vordergrundfarbe als Color-Objekt zurückgibt. Diese Funktion darf nicht mit den Funktionen getColor(String) beziehungsweise getColor(String, Color) aus der Color-Klasse verwechselt werden.
14.10.2 Farbanteile zurückgeben
Mitunter müssen wir den umgekehrten Weg gehen und von einem gegebenen Color-Objekt wieder die Rot-, Grün- oder Blau-Anteile extrahieren. Dies ist einfach, und die Funktionsbibliothek bietet Entsprechendes an:
class java.awt.Color
implements Paint, Serializable
|
|
int getRed(), int getGreen(), int getBlue()
Liefert Rot-, Grün- und Blau-Anteile des Farb-Objekts im Bereich von 0 bis 255. |
|
int getAlpha()
Gibt den Alpha-Anteil als Ganzzahl kodiert zurück. |
|
int getRGB()
Gibt die RGB-Farbe als Ganzzahl kodiert zurück. Die Bits 24-31 sind für den Alpha-Wert, 16-23 für Rot, 8-15 für Grün und 0-7 für Blau. |
14.10.3 Vordefinierte Farben
Wenn wir Farben benutzen wollen, dann sind schon viele Werte vordefiniert (wie im vorausgehenden Beispiel die Farbe Rot). Weitere sind: black, blue, cyan, darkGray, gray, green, lightGray, magenta, orange, pink, white und yellow. In der Klasse jawa.awt.Color sind dazu viele Zeilen der Form
/**
* The color white.
*/
public final static Color white = new Color(255, 255, 255);
platziert. Nachfolgend zeigt die Tabelle die Wertbelegung für die Farbtupel:
Farbname
|
Rot
|
Grün
|
Blau
|
white
|
255
|
255
|
255
|
black
|
0
|
0
|
0
|
lightGray
|
192
|
192
|
192
|
darkGray
|
128
|
128
|
128
|
red
|
255
|
0
|
0
|
green
|
0
|
255
|
0
|
blue
|
0
|
0
|
255
|
yellow
|
255
|
255
|
0
|
purple
|
255
|
0
|
255
|
Tabelle 14.2 Farbanteile für die vordefinierten Standardfarben
14.10.4 Farben aus Hexadezimalzahlen erzeugen
Um eine Farbbeschreibung im hexadezimalen Format in einzelne Farbkomponenten der Color-Klasse zu zerlegen, also zum Beispiel von FFFFFF nach (255,255,255), gibt es zwei einfache und elegante Wege. Der erste führt über die Wrapper-Klasse Integer. Die folgende Zeile erzeugt aus dem String colorHexString ein Color-Objekt:
Color color = new Color( Integer.parseInt(colorHexString, 16) );
Ein anderer Weg ist noch eleganter, da die Color-Klasse eine einfache Routine bereitstellt:
Color color = Color.decode( "#" + colorHexString );
decode(String) verlangt eine 24-Bit-Integer-Zahl als String kodiert. Durch das Hash-Symbol und das Plus erzeugen wir ein String-Objekt, welches als Hexadezimalzahl bewertet wird.
class java.awt.Color
implements Paint, Serializable
|
|
Color decode( String nm ) throws NumberFormatException
Liefert die Farbe vom übergebenen String. Die Zeichenkette ist als 24-Bit-Integer kodiert. |
Nun wertet decode() den String aus, indem wiederum die decode()-Funktion der Integer-Klasse aufgerufen wird. Aus diesem Rückgabewert wird dann wiederum das Color-Objekt aufgebaut.
public static Color decode( String nm ) throws NumberFormatException
{
Integer intval = Integer.decode( nm );
int i = intval.intValue();
return new Color( (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF );
}
Wir sehen, dass bei falschen Werten eine NumberFormatException ausgelöst wird. Diese Exception kommt von der decode()-Funktion der Integer-Klasse. Die Implementierung verrät uns die Arbeitsweise und zeigt uns auf, dass wir auch aus Oktalziffern ein Color-Objekt erzeugen könnten oder aber aus einem String, der nicht mit dem Hash-Zeichen, sondern mit dem gewohnten Präfix 0x beginnt.
public static Integer decode( String nm ) throws NumberFormatException
{
if ( nm.startsWith("0x") ) {
return Integer.valueOf(nm.substring(2), 16);
}
if ( nm.startsWith("#") ) {
return Integer.valueOf(nm.substring(1), 16);
}
if ( nm.startsWith("0") && nm.length() > 1 ) {
return Integer.valueOf( nm.substring(1), 8 );
}
return Integer.valueOf( nm );
}
14.10.5 Einen helleren oder dunkleren Farbton wählen
Zwei besondere Funktionen sind brighter() und darker(). Sie liefern ein Farb-Objekt zurück, das jeweils eine Farbnuance heller beziehungsweise dunkler ist.
Beispiel Die Implementierung von draw3DRect() zeigt den Einsatz der Funktionen.
public void draw3DRect(int x, int y, int width, int height, boolean raised)
{
Color c = getColor();
Color brighter = c.brighter();
Color darker = c.darker();
setColor(raised ? brighter : darker);
drawLine(x, y, x, y + height);
drawLine(x + 1, y, x + width - 1, y);
setColor(raised ? darker : brighter);
drawLine(x + 1, y + height, x + width, y + height);
drawLine(x + width, y, x + width, y + height - 1);
setColor(c);
}
|
|
Wie viele andere Funktionen aus der Color-Klasse sind auch die Routinen sichtbar implementiert, also nicht nativ:
public Color brighter() {
return new Color(Math.min((int)(getRed() *(1/FACTOR)), 255),
Math.min((int)(getGreen()*(1/FACTOR)), 255),
Math.min((int)(getBlue() *(1/FACTOR)), 255));
}
public Color darker() {
return new Color(Math.max((int)(getRed() *FACTOR), 0),
Math.max((int)(getGreen()*FACTOR), 0),
Math.max((int)(getBlue() *FACTOR), 0));
}
FACTOR ist eine finale Konstante, die durch 0,7 vordefiniert ist. Sie lässt sich also nicht ändern.
class java.awt.Color
implements Paint, Serializable
|
|
Color brighter()
Gibt einen helleren Farbton zurück. |
|
Color darker()
Gibt einen dunkleren Farbton zurück. |
Farbveränderung mit Nullanteilen
Bei den Farbwerten müssen wir nun die Zusammensetzung aus Rot, Grün und Blau bedenken. Ein voller Wert ist mit 255 belegt. Die Berechnung kann diesen Wert noch modifizieren. Doch ist ein Eintrag mit 0 belegt, so erkennen wir aus der Berechnung, dass der Wert bei null bleiben wird. Daher sollten wir bedenken, was bei reinen Farben wie etwa Rot durch ein brighter() passiert. Ein reiner Rotton kann sich zwar in der Helligkeit ändern, aber ein Color.red.brighter() liefert immer noch Color.red.
System.out.println( Color.red.brighter() ); // java.awt.Color[r=255,g=0,b=0]
System.out.println( Color.red.darker() ); // java.awt.Color[r=178,g=0,b=0]
Es ist also nicht so, dass bei brighter() die Farben näher an Weiß herankommen und bei darker() an Schwarz.
Um also echte Helligkeitsveränderungen zu bekommen, müssen wir die Farben vorher umrechnen. Hierzu bieten sich andere Farbräume an, wie beispielsweise der HSB-Raum, in dem wir Komponenten für die Helligkeit haben. RGBtoHSB() gibt ein Feld mit den Werten für Hue, Saturation und Brightness für ein Tripel von Rot-, Grün- und Blau-Werten zurück. Nach einer Veränderung der Helligkeit können wir diesen Farbraum wieder mit HSBtoRGB() zurückkonvertieren.
14.10.6 Farbmodelle HSB und RGB
Zwei Farbmodelle sind in der Computergrafik geläufig. Das RGB-Modell, wo die Farben durch einen Rot-, Grün- und Blau-Anteil definiert werden, oder das HSB-Modell, das die Farben durch Grundton (Hue), Farbsättigung (Saturation) und Helligkeit (Brightness) definiert. Die Farbmodelle können die gleichen Farben beschreiben und umgerechnet werden.
class java.awt.Color
implements Paint, Serializable
|
|
static int HSBtoRGB( float hue, float saturation, float brightness )
Aus einem HSB-kodierten Farbwert wird ein RBG-Farbwert gemacht. |
|
static float[] RGBtoHSB( int r, int g, int b, float hsbvals[] )
Verlangt ein Array hsbvals zur Aufnahme von HSB, in dem die Werte gespeichert werden sollen. Das Array kann null sein und wird somit angelegt. Das Feld wird zurückgegeben. |
|
static Color getHSBColor( float h, float s, float b )
Die Funktion kann genutzt werden, um Color-Objekte aus einem HSB-Modell zu erzeugen. |
Die Implementierung von getHSBColor() ist ein Witz:
public static Color getHSBColor(float h, float s, float b) {
return new Color(HSBtoRGB(h, s, b));
}
14.10.7 Die Farben des Systems
Bei eigenen Java-Programmen ist es wichtig, dass diese sich so perfekt wie möglich in die Reihe der anderen Host-Programme einreihen, ohne großartig aufzufallen. Dafür muss ein Fenster die globalen Einstellungen wie den Zeichensatz und die Farben kennen. Für die Systemfarben gibt es die Klasse SystemColor, welche alle Farben einer grafischen Oberfläche auf symbolische Konstanten abbildet. Besonders praktisch ist dies bei Änderungen von Farben während der Laufzeit. Über diese Klasse können immer die aktuellen Werte eingeholt werden, denn ändert sich beispielsweise die Hintergrundfarbe der Laufleisten, so ändert sich damit auch der RGB-Wert. Die Systemfarben sind Konstanten von SystemColor und werden mit der Funktion getRGB() in eine Ganzzahl umgewandelt.
Die Klasse definiert folgende statischen finalen Variablen:
class java.awt.SystemColor
implements Serializable
|
SystemColor
|
Welche Farbe anspricht
|
desktop
|
Farbe des Desktop-Hintergrunds
|
activeCaption
|
Hintergrundfarben für Text im Fensterrahmen
|
activeCaptionText
|
Farbe für Text im Fensterrahmen
|
activeCaptionBorder
|
Rahmenfarbe für Text im Fensterrahmen
|
inactiveCaption
|
Hintergrundfarbe für inaktiven Text im Fensterrahmen
|
inactiveCaptionText
|
Farbe für inaktiven Text im Fensterrahmen
|
inactiveCaptionBorder
|
Rahmenfarbe für inaktiven Text im Fensterrahmen
|
window
|
Hintergrundfarbe der Fenster
|
windowBorder
|
Rahmenfarbe der Fenster
|
windowText
|
Textfarbe für Fenster
|
menu
|
Hintergrundfarbe für Menüs
|
menuText
|
Textfarbe für Menüs
|
text
|
Hintergrundfarbe für Textkomponenten
|
textText
|
Textfarbe für Textkomponenten
|
textHighlight
|
Hintergrundfarbe für hervorgehobenen Text
|
textHighlightText
|
Farbe des Texts, wenn dieser hervorgehoben ist
|
textInactiveText
|
Farbe für inaktiven Text
|
control
|
Hintergrundfarbe für Kontroll-Objekte
|
controlText
|
Textfarbe für Kontroll-Objekte
|
controlHighlight
|
Normale Farbe, mit der Kontroll-Objekte hervorgehoben werden
|
controlLtHighlight
|
Hellere Farbe, mit der Kontroll-Objekte hervorgehoben werden
|
controlShadow
|
Normale Hintergrundfarbe für Kontroll-Objekte
|
controlDkShadow
|
Dunklerer Schatten für Kontroll-Objekte
|
scrollbar
|
Hintergrundfarbe der Schieberegler
|
info
|
Hintergrundfarbe der Hilfe
|
infoText
|
Textfarbe der Hilfe
|
Tabelle 14.3 Konstanten der Systemfarben
Um die Systemfarbe in eine brauchbare Variable zu konvertieren, gibt es die getRGB()-Funktion. So erzeugen wir mit
new Color( (SystemColor.window).getRGB() )
einfach ein Color-Objekt in der Farbe des Fensters.
final class java.awt.SystemColor
implements Serializable
|
|
int getRGB()
Liefert den RGB-Wert der Systemfarbe als Ganzzahl kodiert. |
Um zu sehen, welche Farben auf dem laufenden System aktiv sind, formulieren wir ein Programm, das eine kleine Textzeile in der jeweiligen Farbe angibt. Da wir auf die internen Daten nicht zugreifen können, müssen wir ein Farbfeld mit SystemColor-Objekten aufbauen.
Hier klicken, um das Bild zu Vergrößern
Abbildung 14.5 Die Systemfarben unter einer Windows-Konfiguration
Listing 14.13 SystemColors.java
import java.awt.*;
import java.awt.event.*;
class SystemColors extends Frame
{
private String systemColorString[] = {
"desktop","activeCaption","activeCaptionText",
"activeCaptionBorder", "inactiveCaption",
"inactiveCaptionText", "inactiveCaptionBorder",
"window", "windowText", "menu", "menuText",
"text", "textText", "textHighlight",
"textHighlightText","textInactiveText",
"control", "controlText", "controlHighlight",
"controlLtHighlight", "controlShadow",
"controlDkShadow", "scrollbar",
"info","infoText"
};
private SystemColor systemColor[] = {
SystemColor.desktop,
SystemColor.activeCaption,
SystemColor.activeCaptionText,
SystemColor.activeCaptionBorder,
SystemColor.inactiveCaption,
SystemColor.inactiveCaptionText,
SystemColor.inactiveCaptionBorder,
SystemColor.window,
SystemColor.windowText,
SystemColor.menu,
SystemColor.menuText,
SystemColor.text,
SystemColor.textText,
SystemColor.textHighlight,
SystemColor.textHighlightText,
SystemColor.textInactiveText,
SystemColor.control,
SystemColor.controlText,
SystemColor.controlHighlight,
SystemColor.controlLtHighlight,
SystemColor.controlShadow,
SystemColor.controlDkShadow,
SystemColor.scrollbar,
SystemColor.info,
SystemColor.infoText
};
public SystemColors() {
setSize( 200, 400 );
addWindowListener(new WindowAdapter() {
public void windowClosing( WindowEvent e ) {
System.exit(0);
}
});
}
public void paint( Graphics g )
{
g.setFont( new Font( "Dialog", Font.BOLD, 12 ) );
for ( int i=0; i < systemColorString.length; i++ ) {
g.setColor( new Color( systemColor[i].getRGB() ) );
g.drawString( systemColorString[i], 20, 40+(i*13) );
}
}
public static void main( String args[] ) {
SystemColors c = new SystemColors();
c.show();
}
}
|