Die Java 2D API bietet dem Entwickler mehrere Möglichkeiten zur Definition von
Farben an.
Es gibt bereits drei mitgelieferte Klassen, die verschiedene Arten von Farben
repräsentieren:
java.awt.Color
: einfache Farbenjava.awt.GradientPaint
: lineare Farbverläufejava.awt.TexturePaint
: TexturenPaint
-Interface implementieren
(java.awt.Paint
). Dieses Interface gibt eine Reihe von Methoden vor, die
eine konkrete Klasse für eine Farbe implementieren muß.Paint
-Interfaces eigene Farben
definieren kann.
Die Klasse java.awt.Color
liefert monochrome Farben nach dem
RGB-Farbschema. Zusätzlich zu den Red-, Green- und Blue-Komponenten kann man
auf Wunsch auch einen Alpha-Wert bei der Konstruktion eines Color
-Objekts
übergeben, der die Transparenz der Farbe angibt. Standardmäßig sind alle
Farben opak (undurchsichtig).
Die folgende Tabelle zeigt die Konstruktoren der Color
-Klasse:
Konstruktor | Beschreibung |
---|---|
Color( int r, int g, int b ) |
r,g,b stehen für die Ausprägungen der
Red-, Green- und Blue-Komponente und sind
int -Werte mit dem Wertebereich {0,1,...,255}.
255 ist die stärkste Ausprägung einer Farbkomponente.
|
Color( float r, float g, float b ) |
r,g,b sind float -Werte mit dem Wertebereich {0.0,...,1.0}.
1.0 ist die stärkste Ausprägung einer Farbkomponente.
|
Color( int rgb ) |
Die RGB-Werte befinden sich in drei Bytes
des Integers rgb :Bit 0-7: Blue-Komponente Bit 8-15: Green-Komponente Bit 16-23: Red-Komponente Wertebereiche: {0,1,...,255} |
Color( int r, int g, int b, int a ) |
r,g,b stehen für die Ausprägungen der
Red-, Green- und Blue-Komponente, a für den
Alpha-Wert, und sind allesamt
int -Werte mit dem Wertebereich {0,1,...,255}.Ist der Alpha-Wert 0, so ist die Farbe vollkommen transparent, ist er 255, so ist die Farbe opak. |
Color( float r, float g, float b, float a ) |
r,g,b stehen für die Ausprägungen der
Red-, Green- und Blue-Komponente, a für den
Alpha-Wert, und sind allesamt
float -Werte mit dem Wertebereich {0,...,1.0}.Ist der Alpha-Wert 0.0, so ist die Farbe vollkommen transparent, ist er 1.0, so ist die Farbe opak. |
Color( int rgba, boolean hasAlpha ) |
Die RGB-Werte stehen in den Bytes 0 bis 2 von
rgba , der Alpha-Wert im Byte 3.
|
Man kann auch auf die in Color
vordefinierten Farbkonstanten vom Typ Color
zurückgreifen,
um die Definition einer eigenen Farbe zu umgehen. So ist z.B. Color.white
äquivalent zu dem Konstruktoraufruf new Color(255,255,255)
.
Die vordefinierten Farben aus der Color
-Klasse entnehmen Sie bitte der Java
API-Spezifikation.
Nachdem wir nun wissen, wie man einfache Farben erzeugt, betrachten wir nun das Gegenstück zu dieser Operation - die Zerlegung von Farben in ihre einzelnen Bestandteile.
Die folgenden Methoden liefern die Rot-, Grün-, Blau- oder Alpha-Komponente eines
Color
-Objektes. Die Methodenbezeichner sind selbsterklärend.
int getRed()
int getGreen()
int getBlue()
int getAlpha()
Die Rückgabewerte sind wieder Element der Menge {0,1,...,255}.
Die Paint
-Implementation GradientPaint
bietet dem Entwickler folgende Arten von
Farbverläufen:
Ein Verlauf zwischen den Punkten P1(10,10) und P2(20,20) mit den Farben Color.white
und Color.blue
verläuft z.B. "von links oben nach rechts unten".
Eine gedachte Gerade zwischen P1 und P2 bildet mit der X- und der Y-Achse unseres
Koordinatensystems einen 45°-Winkel.
Ein azyklischer Verlauf ist ein einmaliger Verlauf von der Farbe C1 zur Farbe C2 zwischen
den Punkten P1 und P2. Vor P1 haben alle Pixel die Farbe C1, zwischen P1 und P2
findet ein gleichmäßiger Übergang statt und nach P2 haben alle Pixel die Farbe C2.
Bei einem zyklischen Verlauf dagegen beginnt nach P2 ein erneuter Verlauf, und
zwar von C2 zu C1. Im nächsten Abschnitt folgt ein Verlauf von C1 zu C2, dann von C2 zu C1 usw.
Es ergibt sich also theoretisch eine unendliche Wiederholung des Farbverlaufs, welche in
der Realität allerdings durch die Zeichenfläche begrenzt wird.
Für lineare Farbverläufe existieren u.a. folgende Konstruktoren:
Konstruktor | Beschreibung |
---|---|
GradientPaint(float x1, float y1, Color color1, float x2, float y2,
Color color2) |
x1 und y1 stehen für die Koordinaten des Punktes
P1, x2 und y2 für die Koordinaten des Punktes P2,
color1 bzw. color2 für Ausgangsfarbe C1 und Zielfarbe C2.Dieser Konstruktor erzeugt einen azyklischen Verlauf. |
GradientPaint(float x1, float y1, Color color1, float x2, float y2,
Color color2, boolean cyclic) |
Wie vorheriger Konstruktor, allerdings kann hier über das Argument
cyclic eingestellt werden, ob der Verlauf zyklisch
sein soll (wenn cyclic true ist.)
|
Point2D
-Objekten als Parameter,
die ich hier nicht eingefügt habe, da ja lediglich das Konzept der Farbverläufe verdeutlicht
werden soll.
Die folgende paint
-Methode füllt ein Rechteck mit einem azyklischen, linearen
Farbverlauf:
(Zugriff auf die Quelle)
public void paint(Graphics g) {
//Upcast --> mehr Funktionen in Graphics2D
Graphics2D g2d=(Graphics2D)g;
//Rechteck erzeugen:
Rectangle2D rect = new Rectangle2D.Double(100,100,200,200);
//Verlaufsfarbe erzeugen:
GradientPaint gp = new GradientPaint( 110,110, Color.blue, //P1,C1
290,290, Color.white,//P2,C2
false //azyklisch
);
g2d.setPaint( gp ); //Verlaufsfarbe setzen
g2d.fill( rect ); //Rechteck mit Verlauf füllen
}
Das Ergebnis (links mit Beschriftung, rechts ohne Beschriftung)
Die folgende paint
-Methode füllt ein Rechteck mit einem zyklischen, linearen
Farbverlauf
(Zugriff auf die Quelle):
public void paint(Graphics g) {
//Upcast --> mehr Funktionen in Graphics2D
Graphics2D g2d=(Graphics2D)g;
//Rechteck erzeugen:
Rectangle2D rect = new Rectangle2D.Double(100,100,200,200);
//Verlaufsfarbe erzeugen:
GradientPaint gp = new GradientPaint( 110,110, Color.blue, //P1,C1
150,150, Color.white,//P2,C2
true //zyklisch
);
g2d.setPaint( gp ); //Verlaufsfarbe setzen
g2d.fill( rect ); //Rechteck mit Verlauf füllen
}
Das Ergebnis (links mit Beschriftung, rechts ohne Beschriftung):
Die Klasse TexturePaint
aus dem Paket java.awt
ermöglicht das Malen mit Texturen.
Der Konstruktor
TexturePaint(BufferedImage txtr, Rectangle2D anchor)
TexturePaint
-Objekt. Das Bild txtr
wird wie eine Kachel
von der Größe des Rechtecks anchor
(in User-Koordinaten) in ein
Raster gezeichnet. Dieses kann dann zum Zeichnen und Füllen benutzt werden.
Das Bild txtr
sollte
möglichst klein sein, da bei jedem Zeichnen des Bildes in das PaintContext
-Objekt von TexturePaint
die Daten des Bildes kopiert werden.
Das folgende Codebeispiel zeigt den Gebrauch der TexturePaint
-Klasse:
public void paint(Graphics g) {
//Upcast --> mehr Funktionen in Graphics2D
Graphics2D g2d=(Graphics2D)g;
BufferedImage img=loadTheImage(); //ein Bild laden
//Texturfarbe erstellen, Anchor-Rechteck hat genau die Bildmaße
TexturePaint tp = new TexturePaint( img, new Rectangle2D.
Double(0,0,
img.getWidth(),
img.getHeight())
);
//Texturfarbe setzen
g2d.setPaint(tp);
//Rechteck füllen
g2d.fill( new Rectangle2D.Double( 50,50,300,300 ));
}
Das Ergebnis sieht wie folgt aus (mit Beschriftung):
Das Paint-Interface dient dem Entwickler dazu, sich eigene Farben zu definieren.
In diesem Abschnitt möchte ich zunächst allgemein und dann anhand eines Beispiels zeigen,
wie man das Paint
-Interface einsetzt.
Gegeben ist die folgende Vererbungshierarchie von Interfaces, in die das Paint
-Interface
integriert ist:
java.awt.Transparency | +--java.awt.Paint
Man sieht, daß das Paint
-Interface ein Superinterface Transparency
hat.
Es muß also vom Entwickler eine Klasse deklariert werden, die alle geforderten
Methoden aus Paint
und Transparency
definiert.
Paint
schreibt eine Methode (s. Tabelle) public PaintContext
createContext(...)
vor, die ein Objekt einer Klasse liefert, die das PaintContext
-Interface implementiert.
Solch eine Klasse ist also ebenfalls zu definieren.
Um eine eigene Farbe zu definieren, müssen zwei eigene Klassen definiert werden:
MyPaint
,
die das Paint
-Interface (und somit auch das
Superinterface Transparency
implementiert) .PaintContext
-Interface implementiert, als
Bezeichner wäre z.B. MyPaintContext
denkbar.
Ein PaintContext
-Objekt enthält letztendlich ein Raster, also eine Art Bild,
daß das Füllmuster für ein bestimmten Bereich eines Graphics2D
-Objektes
beinhaltet.
Dieser Technik bedienen sich auch die
im JDK enthaltenen Paint
-Implementationen GradientPaint
, TexturePaint
und Color
.
Wenn im Graphics2D
-Objekt etwas gezeichnet wird,
dann wird von einem PaintContext
-Objekt lediglich für einen bestimmten, durch
ein Rechteck definierten Bereich, der das zu zeichnende Objekt eingrenzt, ein
Raster berechnet, daß den bei der Zeichenoperation zu setzenden Pixeln eine
bestimmte Farbe zuordnet. Dieses Raster ist eine Instanz von
Raster
, die von der durch PaintContext
vorgeschriebenen Methode
Raster getRaster(...)
(s. Tabelle unten) geliefert wird.
Als aktuelle Farbe kann man im Graphics2D
-Objekt ein beliebiges Objekt einer
Klasse setzen, die das Paint
-Interface implementiert. Das Setzen einer Farbe erfolgt
durch einen Aufruf von Graphics2D.setPaint( Paint p )
.
Wird z.B. ein Rechteck mit einem linearen Farbverlauf gefüllt, so stammt
dieser aus einem vorher berechneten Raster, welches
bei Aufruf der setPaint()
-Methode des Graphics2D
-Objektes
von der getRaster()
-Methode des jeweiligen PaintContext
-Objektes
erzeugt wurde.
Das Raster für monochrome Farben besteht z.B. immer aus nur einer Farbe (z.B. blau,
wenn die Zeichenfarbe mit setPaint(Color.blue)
gesetzt wurde).
Die folgende Tabelle zeigt, welche Methoden für das Paint
-Interface
definiert werden müssen, um eine
eigene Paint-Implementation zu erhalten.
gefordert von Interface... | Methode | Beschreibung |
---|---|---|
Transparency |
int getTransparency() |
Gibt die Transparenzeigenschaft der eigenen Farbe
zurück (als int codiert). Als Rückgabewert sollte
eine der Klassenkonstanten aus Transparency
verwendet werden:
|
Paint |
public PaintContext createContext(ColorModel cm,
Rectangle deviceBounds,
Rectangle2D userBounds,
AffineTransform xform,
RenderingHints hints)
|
gibt eine PaintContext -Implementation zurück, die ein
Raster für das Rechteck userBounds
(in User-Koordinaten) berechnen
kann.deviceBounds gibt dieses Rechteck
in Gerätekoordinaten an.xform gibt die Transformation an, mit der
user-Koordinaten auf Gerätekoordinaten abgebildet werden.hints gibt die in der Zeichenfläche gesetzten
Rendering-Hints an.
|
Die nächste Tabelle zeigt, welche Methoden das PaintContext
-Interface fordert.
gefordert von Interface... | Methode | Beschreibung |
---|---|---|
PaintContext |
void dispose() |
führt evtl. Aufräumarbeiten durch, z.B. könnte Speicher für ein Raster freigegeben werden, falls dieses als Member-Variable existiert. |
PaintContext |
ColorModel getColorModel() |
gibt das von diesem PaintContext verwendete
Farbmodell zurück (ColorModel.getRGBdefault()
, das Standard RGB-Farbmodell,
sollte für die meisten Fälle ausreichen).
|
PaintContext |
public Raster getRaster(int x,int y,int w,int h) |
Diese Methode gibt das schon beschriebene Raster für das
Rechteck, daß durch x ,y (linke obere Ecke),
w und h (Breite und Höhe) definiert wird, zurück.
Die Parameter sind bereits Gerätekoordinaten umgerechnet.
|
Den Einsatz des Paint
-Interfaces habe ich anhand eines Beispieles dokumentiert.
Zur Ausführung werden folgende drei Quelldateien benötigt:
Paint
-ImplementationPaintContext
-ImplementationImplementiert wurde ein radialer Farbverlauf. Diese Art von Verlauf hat einen Mittelpunkt (im Testprogramm (200,200)), der an den MyPaint-Konstruktor übergeben werden kann. Die Kommentare in den genannten Quellcode-Dateien erläutern die Funktionsweise.
Die Ausführung von TestMyPaint.java
hat folgende Ergebnisgrafik (Ausschnitt):
Das Raster des PaintContext
-Objektes wurde für den Bereich des zu füllenden Rechteckes berechnet,
der Mittelpunkt des Farbverlaufs wurde schon bei der Konstruktion des MyPaint
-Objektes
festgelegt.
Durch das Implementieren des Paint
-Interfaces ist der Entwickler also nicht auf vordefnierte Farbklassen
beschränkt, sondern kann durch Definition einiger weniger Methoden schnell eigene
Farben kreieren.