Java 2D
Bei der Java 2D API handelt es sich um eine Menge von Klassen für die zweidimensionale
Grafikverarbeitung, welche wiederum Teilmenge der Java-Klassenbibliothek der Java Foundation
Classes des Java Development Kit ist. Mit Hilfe dieser vordefinierten Objekte lassen sich
mit relativ geringem Aufwand anspruchsvolle 2D-Grafiken erzeugen. Nachfolgend befindet sich
eine Auflistung der Pakete, deren Klassen die Java 2D API bilden.
Beteiligte Klasse
Die Java 2D-API besteht aus folgenden Paketen:
- java.awt
- java.awt.geom
- java.awt.font
- java.awt.color
- java.awt.image
- java.awt.image.renderable
- java.awt.print
Grundlagen
Was kann man damit machen?
Die folgende Liste zeigt, welche Elemente die Java 2D-API dem Programmierer zur Verfügung stellt:
- geometrische Formen
- Farbkomposition- und Zerlegung
- verschiedene Pinselstriche
- Füllen von Umrissen mit Farben, Mustern oder Farbverläufen
- (affine) geometrische Transformationen von Umrissen
- Alpha-Werte
- Clipping
- formatierter Text
- Rendering-Optionen, z.B. Antialiasing
- Bildverarbeitung
Das Koordinatensystem
Es existieren in Java 2D zwei von einander unterschiedliche Koordinatensysteme.
-
User Space
Ist das dem Programmierer zu Verfügung gestellte Koordinatensystem. Sein Ursprung befindet sich in der linken oberen Ecke, die x-Koordinaten erhöhen sich nach rechts und die y-Koordinaten nach unten.
-
Device Space
Der Device Space ist das vom jeweiligen Gerät (Drucker, Bildschirm, usw.) abhängige Koordinatensystem. Die Informationen darüber sind in den Klassen GraphicsEnvironment, GraphicsDevice und GraphicsConfiguration verkapselt.
Die Koordinatensysteme brauchen nicht übereinzustimmen, die nötigen Umrechungen übernimmt das System und brauchen nicht vom Benutzer vorgenommen werden.
Zeichenfläche (Fenster)
Bevor wir mit der Java 2D-API zweidimensionale Grafiken zeichnen können, müssen wir als erstes
ein Ausgabefenster (Frame) erzeugen. Darum müssen wir uns als erstes mit Fenstern beschäftigen,
bevor wir auf die Java 2D-API eingehen können. Aus einfachhalberkeit werden wir uns aber nur mit
Swing-Applikationen beschäftigen und nicht mit AWT-Applikationen oder mit Applets.
Swing-Fenster darstellen
Um unter Swing ein Fenster öffnen zu können, müssen wir zunächst das Paket javax.swing.*
einbinden. Dann können wir eine Klasse JFrame und deren Methoden nutzen.
import javax.swing.JFrame;
public class Java2D
{
public static void main(String args[])
{
// Neues Fenster erzeugen
JFrame f = new JFrame("Ein Fenster");
// Fenstergroesse angeben
f.setSize(300, 200);
// Damit das Fenster geschlossen werden kann
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Fenster anzeigen
f.setVisible(true);
}
}
Von JFrame ableiten
Wir können unsere neue Klasse auch direkt von JFrame ableiten.
Dann ist es uns gestattet, die Funktionen der Klassen direkt aufzurufen, zum Beispiel
setSize(). Im Hauptprogramm erzeugen wir über den eigenen
Konstruktor dann das Fenster, denn der eigene Konstruktor ruft ja über die
super()-Funktion automatisch den Konstruktor der Oberklasse,
also von JFrame auf.
import javax.swing.JFrame;
public class Java2D extends JFrame
{
// Konstruktor
public Java2D(String p_Titel)
{
// Konstruktor der Oberklasse aufrufen
super(p_Titel);
// Fenstergroesse angeben
this.setSize(300, 200);
// Damit das Fenster geschlossen werden kann
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Fenster anzeigen
this.setVisible(true);
}
public static void main(String args[])
{
// Objekt der abgeleiteten Klasse erzeugen
Java2D f = new Java2D("Ein Fenster");
}
}
Die paint()-Methode
Um zweidimensionale Grafiken zeichnen zu können, müssen wir die Funktion paint()
der Klasse Frame, JFrame, JApplet oder Applet
überschreiben und dort alles einsetzen, was gezeichnet werden soll, etwa Linien, Texte oder gefüllte Polygone.
Der gewünschte Inhalt wird immer dann gezeichnet, wenn das Fenster neu aufgebaut oder wenn von außen
repaint() aufgerufen wird. In genau diesem Fall wird das Grafiksystem
paint() aufrufen und das Zeichnen anstoßen.
import javax.swing.JFrame;
public class Java2D extends JFrame
{
// Konstruktor
public Java2D(String p_Titel)
{
// Konstruktor der Oberklasse aufrufen
super(p_Titel);
// Fenstergroesse angeben
this.setSize(300, 200);
// Damit das Fenster geschlossen werden kann
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Fenster anzeigen
this.setVisible(true);
}
// Überschreiben der Methode paint()
public void paint(Graphics g)
{
// Zeichnen von zweidimensonalen Grafiken
}
public static void main(String args[])
{
// Objekt der abgeleiteten Klasse erzeugen
Java2D f = new Java2D("Ein Fenster");
}
}
Zeichnen mit JFrame (Die paintComponent()-Methode)
Grundsätzlich ließe sich auch von JFrame einen Unterklasse bilden
und paint() überschreiben, aber das ist nicht der übliche Weg.
Stattdessen wählen wir einen anderen Ansatz: Wir bilden eine eigene Komponente, eine Unterklasse
von JPanel und setzten diese auf das Fenster. Wenn nun das Fenster
neu gezeichnet wird, wird auch die Komponente JPanel neu gezeichnet
und ruft die paint()-Funktion auf. Allerdings überschreiben Unterklassen
von Swing im Regelfall paint() nicht, sondern eine andere Funktion:
paintComponent(). Das liegt daran, dass Swing in paint()
zum Beispiel noch Rahmen zeichnet und sich um eine Pufferung des Bildschirminhaltes zur Optimierung kümmert.
So ruft paint() die drei Funktionen paintComponent(),
paintBorder() und paintChildren() auf und bei
Wiederdarstellung kümmert sich ein RepaintManager um eine zügige Darstellung mit Hilfe der gepufferten
Inhalte, was bei normalen Swing-Interaktionskomponenten wie Schaltflächen wichtig ist.
paintComponent() besitzt ist in der Oberklasse die Sichtbarkeit
protected, was wir beibehalten sollten. Die Funktion wird nicht
von außen aufgerufen und daher muss eine Unterklasse die Sichtbarkeit nicht zu public
erweitern. Der Aufruf von super.paintComponent() ist immer dann angebracht,
wenn die Oberklasse ihre Inhalte zeichnen soll. Bei vollständig eigenem Inhalt ist das nicht nötig.
class Java2D extends JPanel
{
// Überschreiben der Methode paintComponent()
protected void paintComponent(Graphics g)
{
// Konstruktor der Oberklasse aufrufen
super.paintComponent(g);
// Zeichnen von zweidimensonalen Grafiken
}
public static void main(String args[])
{
// Neues Fenster erzeugen
JFrame f = new JFrame("Ein Fenster");
// Fenstergroesse angeben
f.setSize(300, 200);
// Damit das Fenster geschlossen werden kann
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Neue Komponente zum Fenster hinzufuegen
f.add(new Java2D());
// Fenster anzeigen
f.setVisible(true);
}
}
Die Klasse Graphics
Ein spezieller Parameter wird in der paint()-Methode übergeben – der Grafikkontext,
ein Objekt vom Typ Graphics. Die Klasse Graphics, besitzt
verschiedene Methoden zum Zeichnen, etwa von Linien, Kreisen, Ovalen, Rechtecken, Zeichenfolgen oder Bildern.
Das grafische System übergibt unserem Programm über paint() ein gültiges Graphics-Objekt,
und wir können auf diese Weise auf der grafischen Oberfläche zeichnen. Dies funktioniert auch dann, wenn die Zeichenfläche
nicht direkt sichtbar ist, wie bei Hintergrundgrafiken. Wir können nur in der paint()-Methode
auf das Graphics-Objekt zugreifen. Diese wiederum wird immer dann aufgerufen, wenn die Komponente neu gezeichnet werden muss.
Die Klasse Graphics2D
Wenn nun die grafische Oberfläche Objekte zeichnet, wird für alle Komponenten die paint()-Methode mit dem
passenden Grafikkontext aufgerufen. Unter Java 2D wurde dies um einen neuen Grafikkontext mit der Klasse
Graphics2D erweitert. Um die erweiterte Funktionalität zu nutzen, muss das bekannte Graphics-Objekt in
ein Graphics2D-Objekt gecastet werden. Da Graphics2D eine Unterklasse
von Graphics ist, lassen sich natürlich noch alle AWT-Operationen weiterverwenden.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
...
}
Geometrische Formen
Linien
-
drawLine(int x1, int y1, int x2, int y2)
Linie zwischen den Koordinaten (x1,y1) und (x2,y2) in der Vordergrundfarbe zeichnen.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.drawLine(100, 100, 500, 400);
}
Rechtecke
-
void drawRect( int x, int y, int width, int height )
Ein Rechteck in der Vordergrundfarbe zeichnen. Das Rechteck ist
width + 1 Pixel breit und height + 1 Pixel hoch.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.drawRect(100, 100, 500, 400);
}
-
void abstract fillRect( int x, int y, int width, int height )
Zeichnet ein gefülltes Rechteck in der Vordergrundfarbe. Das Rechteck
ist width + 1 Pixel breit und height + 1 Pixel hoch.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.fillRect(100, 100, 500, 400);
}
-
void abstract drawRoundRect( int x, y, int width, height, int arcWidth, arcHeight )
Zeichnet ein abgerundetes Rechteck in der Vordergrundfarbe. Das Rechteck ist width + 1 Pixel breit und height + 1 Pixel hoch.
arcWidth gibt den horizontalen und arcHeight den vertikalen Durchmesser der Kreisbögen der Ränder an.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.drawRoundRect(100, 100, 500, 400, 10, 10);
}
-
void abstract fillRoundRect( int x, y, int width, height, int arcWidth, arcHeight )
Ist wie drawRoundRect(), nur gefüllt in der Vordergrundfarbe.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.fillRoundRect(100, 100, 500, 400, 10, 10);
}
-
void draw3DRect( int x, int y, int width, int height, boolean raised )
Zeichnet ein dreidimensional angedeutetes Rechteck in der Vordergrundfarbe. Der Parameter raised
gibt an, ob das Rechteck erhöht oder vertieft wirken soll. Die Farben für den Effekt werden aus den Vordergrundfarben gewonnen.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.draw3DRect(100, 100, 500, 400, true);
}
-
void fill3DRect( int x, int y, int width, int height, boolean raised )
Ist wie draw3Drect(), nur gefüllt in der Vordergrundfarbe.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.fill3DRect(100, 100, 500, 400, true);
}
Ovale und Kreisbögen
-
abstract drawOval( int x, int y, intwidth, int height )
Zeichnet ein Oval in der Vordergrundfarbe, welches die Ausmaße eines Rechtecks hat.
Das Oval hat eine Größe von width + 1 Pixel in der Breite und height + 1 Pixel in der Höhe.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.drawOval(100, 100, 500, 400);
}
-
Die Funktion abstract fillOval( int x, int y, int width, int height )
Ist wie drawOval(), nur gefüllt mit der Vordergrundfarbe.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.fillOval(100, 100, 500, 400);
}
-
abstract void drawArc( int x, int y, int width, int height, int startAngle, int arcAngle )
Zeichnet einen Kreisbogen. Null Grad liegt in der 3-Uhr-Position. Bei einem Aufruf mit den Winkelargumenten 0, 270 wird ein
Kreisbogen gezeichnet, bei dem 90 Grad im unteren rechten Bereich nicht gezeichnet sind.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.drawArc(100, 100, 500, 400, 100, 50);
}
-
abstract void fillArc( int x, int y, int width, int height, int startAngle, int arcAngle )
Ist wie drawArc(), nur gefüllt in der Vordergrundfarbe.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.fillArc(100, 100, 500, 400, 100, 50);
}
Polygone und Polylines
-
abstract void drawPolyline( int xPoints[], int yPoints[], int nPoints )
Zeichnet einen Linienzug durch die gegebenen Koordinaten in der Vordergrundfarbe. Die Figur ist nicht automatisch
geschlossen, wenn nicht die Start- und Endkoordinaten gleich sind. Mit nPoint kontrollieren wir die Anzahl der
gezeichneten Linien.
public void paint(Graphics g)
{
int xPoints = { 210, 320, 230, 340 };
int yPoints = { 210, 320, 430, 210 };
Graphics2D g2 = (Graphics2D) g;
g2.drawPolyline(xPoints, yPoints, 4);
}
-
abstract void drawPolygon( int xPoints[], int yPoints[], int nPoints )
Zeichnet wie drawPolyline() einen Linienzug, schließt diesen aber immer gleich, indem die erste Koordinate
mit der Koordinate nPoints verbunden wird.
public void paint(Graphics g)
{
int xPoints = { 210, 320, 230, 340 };
int yPoints = { 210, 320, 430, 210 };
Graphics2D g2 = (Graphics2D) g;
g2.drawPolygon(xPoints, yPoints, 4);
}
-
abstract void fillPolygon( int xPoints[], int yPoints[], int nPoints )
Ist wie drawPolygon(), nur gefüllt in der Vordergrundfarbe. Da eine Polyline offen ist,
kann sie nicht gefüllt werden. Somit gibt es die Funktion fillPolyline() nicht.
public void paint(Graphics g)
{
int xPoints = { 210, 320, 230, 340 };
int yPoints = { 210, 320, 430, 210 };
Graphics2D g2 = (Graphics2D) g;
g2.fillPolygon(xPoints, yPoints, 4);
}
Die Polygon-Klasse
Neben der Möglichkeit, die Linienzüge durch Koordinatenfelder zu beschreiben, gibt es in Java die
Polygon-Klasse Polygon. Sie ist eine Erweiterung des Interfaces Shape. Sie ist aber minimal, lediglich
die Methode getBounds() wird implementiert. Ein Polygon-Objekt verwaltet seine Koordinaten eigenständig,
und von außen können wir Elemente hinzunehmen. Mit der mächtigen Methode contains() können wir
herausfinden, ob ein Punkt in der von dem Polygon ausgezeichneten Fläche liegt. Doch zunächst müssen wir
ein Polygon-Objekt erzeugen. Dazu dienen zwei Konstruktoren:
-
Polygon()
Erzeugt ein Polygon-Objekt ohne Koordinaten.
-
Polygon( int xpoints[], int ypoints[], int npoints )
Erzeugt ein Polygon mit den angegebenen Koordinaten.
Nun können wir Punkte hinzufügen und Anfragen an das Polygon-Objekt stellen:
-
Rectangle getBounds()
Gibt die Bounding-Box der Figur zurück. Diese beschreibt ein Rechteck, das das Objekt gerade umschließt.
Ein Rectangle-Objekt besitzt die Variablen height (Höhe des Rechtecks), width (Breite des Rechtecks),
x (x-Koordinate) und y (y-Koordinate des Rechtecks). Mit verschiedenen Funktionen lassen sich Rechtecke
zusammenfassen und schneiden.
-
void addPoint( int x, int y )
Koordinate (x,y) hinzufügen. Die Grenzen (engl. boundings) werden automatisch aktualisiert.
-
boolean contains( int x, int y )
Liefert true, wenn der Punkt (x,y) im Polygon liegt. Es wird ein Gerade-/Ungerade-Algorithmus verwendet,
um dies herauszufinden.
-
boolean contains( Point p )
Liefert true, wenn der Punkt p im Polygon liegt. Ein Point-Objekt besitzt die Attribute x und y für die
Koordinaten.
Das erzeugte Polygon können wir mit speziellen Methoden, natürlich aus Graphics, zeichnen.
-
void drawPolygon( Polygon p )
Zeichnet das Polygon in der Vordergrundfarbe.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Polygon pol = new Polygon();
pol.addPoint(410,10);
pol.addPoint(520,120);
pol.addPoint(430,230);
pol.addPoint(540,10);
g2.drawPolygon(pol);
}
-
void fillPolygon( Polygon p )
Zeichnet ein gefülltes Polygon in der Vordergrundfarbe.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Polygon fillPol = new Polygon();
fillPol.addPoint(610,10);
fillPol.addPoint(720,120);
fillPol.addPoint(630,230);
fillPol.addPoint(740,10);
g2.fillPolygon(fillPol);
}
Text
Für die Textausgabe gibt es in der Klasse Graphics
verschiedene Methoden.
-
void drawString(String s, int x, int y)
void drawString(String s, float x, float y)
Schreibt einen String in der aktuellen Farbe und mit dem aktuellen Zeichensatz.
Die x- und y-Werte bestimmen die Startpunkte der Grundline.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.drawString("Hallo Java", 10, 10);
}
-
void drawString(AttributedCharacterIterator iterator, int x, int y)
void drawString(AttributedCharacterIterator iterator, int x, int y)
Den zu zeichnenden String durch einen Attribut-Iterator angeben.
-
void drawChars(char data[], int offset, int length, int x, int y)
Zeichnet eine Zeichenkette (aus einem char-Array) in der aktuellen Farbe und mit dem aktuellen Zeichensatz.
Dabei kann mit offset und mit length angegeben werden, von welchem Zeichen bis zu welchen Zeichen die Zeichenkette ausgegeben werden soll.
public void paint(Graphics g)
{
char data[] = {'N','o','c','h',' ', 'e','i','n',' ','t','e','x','t'};
Graphics2D g2 = (Graphics2D) g;
g2.drawChars(data, 0, data.length, 10, 10);
}
-
void drawBytes(byte data[], int offset, int length, int x, int y)
Ist wie drawChars, es wird nur ein byte-Array verwendet.
public void paint(Graphics g)
{
byte data[] = {'<','S','e','r','v','u','s','>'};
Graphics2D g2 = (Graphics2D) g;
g2.drawBytes(data, 0, data.length, 10, 10);
}
Zeichensatz
Die Methoden drawString, drwawChars und drawBytes
verwenden immer den aktuellen Zeichensatz.
Um den Zeichensatz umändern zu können, müssen wir zuerst ein Font-Objekt von der
Klasse Font erzeugen.
Font f = new Font(name, style, size);
Danach können wir mit der Methode void setFont(Font f)
von der Klasse Graphics unseren neuen erzeugten Font setzen.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Font f = new Font("Serif",Font.BOLD, 20);
g2.setFont(f);
g2.drawString("Hallo Java", 10, 10);
}
Um den aktuell verwendeten Font abzurufen, können wir die Methode
Font getFont() verwenden.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
System.out.println(g2.getFont());
}
Die Font-Klasse
-
deriveFont
Aus einem bestehenden Font einen neuen Font ableiten.
-
Font deriveFont(float size)
Von einem bestehenden Font ableiten und die Größe ändern.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Font f = new Font("Serif",Font.BOLD, 20);
g2.setFont(f);
g2.drawString("Hallo Java", 10, 10);
Font fNeu = f.deriveFont(10f);
g2.setFont(fNeu);
g2.drawString("Hallo Java", 10, 10);
}
-
Font deriveFont(int style)
Von einem bestehenden Font ableiten und den Style ändern.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Font f = new Font("Serif",Font.BOLD, 20);
g2.setFont(f);
g2.drawString("Hallo Java", 10, 10);
Font fNeu = f.deriveFont(Font.ITALIC);
g2.setFont(fNeu);
g2.drawString("Hallo Java", 10, 10);
}
-
Font deriveFont(int style, float size)
Von einem bestehenden Font ableiten und den Style und die Größe ändern.
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Font f = new Font("Serif",Font.BOLD, 20);
g2.setFont(f);
g2.drawString("Hallo Java", 10, 10);
Font fNeu = f.deriveFont(Font.ITALIC, 10f);
g2.setFont(fNeu);
g2.drawString("Hallo Java", 10, 10);
}
Die FontMetrics-Klasse
Bilder
Farben
Filter
Drucken