Erste Schritte mit Java 2D

Die Java 2D-API ist eine zentrale Java 1.2-Plattform-API (verschiedene Informationen zur API und ihren Implementierungen finden Sie unter Ressourcen). Implementierungen der API sind als Teil der Java Foundation Classes (JFC) in den aktuellen Betaversionen des Sun JDK für Windows NT / 95 und Solaris verfügbar. Mit der Fertigstellung von Java 1.2 sollte Java 2D auf mehr Plattformen verfügbar sein.

Beachten Sie, dass Java 2D zwar etwas unabhängig von den anderen Teilen des JFC entwickelt wurde, aber dennoch ein Kernbestandteil des 1.2 AWT ist. Wir werden die Unterscheidung treffen und auf 2D-spezifische Funktionen zur Diskussion hinweisen, aber Sie sollten bedenken, dass diese Funktionalität für 1.2-Grafiken genauso zentral ist wie die alte 1.0- und 1.1-AWT-Unterstützung.

Java 2D erweitert die bisherigen AWT-Mechanismen zum Zeichnen von 2D-Grafiken, Bearbeiten von Text und Schriftarten, Laden und Verwenden von Bildern sowie Definieren und Behandeln von Farben und Farbräumen. Wir werden diese neuen Mechanismen in dieser und zukünftigen Kolumnen untersuchen.

Ein Hinweis zu Nomenklatur und Konventionen

In dieser Spalte ist meine primäre Entwicklungsplattform ein PC mit Windows 95 oder Windows NT. Ich hoffe, wenn möglich andere plattformspezifische Tipps und Tricks bereitstellen zu können, aber ich werde mich auf Windows konzentrieren, da ich dort die meiste Zeit verbringen werde.

Wenn ich einen Methodennamen schreibe, sollte er immer die Form haben methodname(). Die nachfolgenden Klammern sollen dies als Methode auszeichnen. Die Methode kann Parameter annehmen oder nicht. In der Praxis sollte der Kontext dies immer klar machen.

Quellcodelisten werden mit Zeilennummern angegeben. Ich plane, die Zeilennummern zu verwenden, um den Artikeltext und die Codelisten entsprechend zu verweisen. Dies sollte es Ihnen auch viel einfacher machen, die Spalte mit Anmerkungen zu versehen, falls Sie eine Kopie drucken möchten. Beachten Sie jedoch, dass die aus der Spalte verknüpften Quelldateien reguläre * .java-Dateien (ohne Zeilennummern) sind, damit Sie sie herunterladen und mit ihnen entwickeln können.

Da ich in den kommenden Monaten über viele der Medien- und Kommunikations-APIs schreiben werde, möchte ich sicherstellen, dass der gesamte Beispielcode sowohl als Ganzes als auch in seinen einzelnen Teilen sinnvoll ist. Ich werde versuchen, meine Beispiele konsequent zu benennen und sie in sinnliche Pakete zu packen.

Die Spitze meiner Pakethierarchie wird sein:

com.javaworld.media 

Jede API oder jedes Thema, über das ich schreibe, enthält mindestens ein Unterpaket unter dieser obersten Ebene. Der gesamte Code für diesen Java 2D-Artikel befindet sich beispielsweise in:

com.javaworld.media.j2d 

Um die erste Beispielanwendung auf Java 2D aufzurufen, müssen Sie den Code herunterladen, in Ihren Klassenpfad einfügen und dann Folgendes verwenden:

java com.javaworld.media.j2d.Example01 

(Wenn der Namespace zu lang für Ihren Geschmack ist oder Sie aus einem anderen Grund den Beispielcode verwenden möchten, ohne den vollständig qualifizierten Namen verwenden zu müssen, kommentieren Sie einfach die Paketzeile am Anfang jeder Quellcodedatei aus.)

Ich werde eine Java-Archivdatei (JAR-Datei) für den Beispielcode und die Klassendateien jedes Artikels generieren. Dieses Archiv wird in den Ressourcen jeder Spalte zur Verfügung gestellt, falls Sie es herunterladen und die Beispiele aus dem Archiv ausführen möchten.

Ich werde auch eine aktuelle JAR-Datei aufbewahren, die den gesamten Code und die Klassen aus meinen aktuellen und vorherigen Medienprogrammierungsspalten enthält . Diese umfassende JAR-Datei wird auf meiner persönlichen Website verfügbar sein.

Ein letzter Punkt zu den Beispielen: Ich habe mich für jedes Beispiel entschieden, sofern ich nicht ausdrücklich etwas anderes vermerke, eine eigenständige Anwendung oder ein eigenständiges Applet. Dies wird von Zeit zu Zeit zu einer gewissen Wiederholung des Codes führen, aber ich bin der Meinung, dass die Integrität jedes einzelnen Beispiels am besten erhalten bleibt.

Genug von Konventionen. Beginnen wir mit der Programmierung mit Java 2D!

Graphics2D: Eine bessere Grafikklasse

Die zentrale Klasse innerhalb der Java 2D-API ist die java.awt.Graphics2Dabstrakte Klasse, die Unterklassen java.awt.Graphicszur Erweiterung der 2D-Rendering-Funktionalität enthält. Graphics2DFügt eine einheitlichere Unterstützung für Manipulationen einer Vielzahl von Formen hinzu, wodurch Text, Linien und alle Arten anderer zweidimensionaler Formen in ihren Fähigkeiten und ihrem Nutzen vergleichbar werden.

Beginnen wir mit einem einfachen Beispiel, das zeigt, wie Sie eine Graphics2dReferenz erhalten und verwenden .

001 Paket com.javaworld.media.j2d; 002 003 import java.awt. *; 004 import java.awt.event. *; 005 006 public class Example01 erweitert Frame {007 / ** 008 * Instanziiert ein Example01-Objekt. 009 ** / 010 public static void main (String args []) {011 new Example01 (); 012} 013 014 / ** 015 * Unser Example01-Konstruktor legt die Größe des Frames fest, fügt die visuellen 016 * -Komponenten hinzu und macht sie dann für den Benutzer sichtbar. 017 * Es wird eine Adapterklasse verwendet, um den Benutzer zu behandeln, der 018 * den Frame schließt. 019 ** / 020 public Example01 () {021 // Titel unseres Rahmens. 022 super ("Java 2D Example01"); 023 024 // Legen Sie die Größe für den Rahmen fest. 025 setSize (400.300); 026 027 // Wir müssen die Sichtbarkeit unseres Frames 028 // aktivieren, indem wir den Parameter Visible auf true setzen. 029 setVisible (true); 030 031 // Nun,Wir möchten sicherstellen, dass die Ressourcen 032 //, die dieser Frame verwendet, wenn das Fenster geschlossen wird, ordnungsgemäß entsorgt werden. Wir verwenden dafür 033 // einen anonymen inneren Klassenadapter. 034 addWindowListener (neuer WindowAdapter () 035 {public void windowClosing (WindowEvent e) 036 {dispose (); System.exit (0);} 037} 038); 039} 040 041 / ** 042 * Die Malmethode liefert die wahre Magie. Hier haben wir 043 * das Graphics-Objekt in Graphics2D umgewandelt, um 044 * zu veranschaulichen, dass wir mit 045 * Graphics2D dieselben alten Grafikfunktionen verwenden können, die wir mit Graphics gewohnt sind. 046 ** / 047 public void paint (Grafik g) {048 // So haben wir ein Quadrat mit einer Breite von 049 // von 200, einer Höhe von 200 und beginnend bei x = 50, y = 50 gezeichnet. 050 g.setColor (Color.red); 051 g.drawRect (50,50,200,200); 052 053 // Let 's Setzen Sie die Farbe auf Blau und zeichnen Sie dann mit dem Graphics2D 054 // -Objekt ein Rechteck, das vom Quadrat versetzt ist. 055 // Bisher haben wir mit Graphics2D nichts gemacht, was 056 // wir auch nicht mit Graphics machen konnten. (Wir verwenden tatsächlich 057 // Graphics2D-Methoden, die von Graphics geerbt wurden.) 058 Graphics2D g2d = (Graphics2D) g; 059 g2d.setColor (Color.blue); 060 g2d.drawRect (75,75,300,200); 061} 062}

Wenn Sie Beispiel01 ausführen, sollten Sie ein rotes Quadrat und ein blaues Rechteck sehen, wie in der folgenden Abbildung gezeigt. Beachten Sie, dass bei der Windows NT / 95-Version von JDK 1.2 Beta 3 (der aktuellsten Version 1.2 ab dieser Spalte) ein Leistungsproblem bekannt ist. Wenn dieses Beispiel auf Ihrem System schmerzhaft langsam ist, müssen Sie möglicherweise den in JavaWorld Java Tip 55 dokumentierten Fehler umgehen (siehe Ressourcen unten für diesen Tipp).

Beachten Sie, dass Sie, so wie Sie ein GraphicsObjekt nicht direkt instanziieren , auch kein Graphics2DObjekt instanziieren . Vielmehr erstellt die Java-Laufzeit ein Rendering-Objekt und übergibt es an paint()(Zeile 047 in der Codeliste von Example01). Auf Java 1.2-Plattformen und darüber hinaus implementiert dieses Objekt auch die Graphics2Dabstrakte Klasse.

Bisher haben wir mit unseren 2D-Grafikfunktionen nichts Besonderes gemacht. paint()Fügen wir am Ende der Methode unseres vorherigen Beispiels Code hinzu und bringen einige neue Funktionen in Java 2D ein (Beispiel 02):

001 / ** 002 * Hier verwenden wir neue Java 2D API-Funktionen wie affine 003 * -Transformationen und Shape-Objekte (in diesem Fall ein generisches 004 *, GeneralPath). 005 ** / 006 public void paint (Grafik g) {007 g.setColor (Color.red); 008 g.drawRect (50,50,200,200); 009 010 Graphics2D g2d = (Graphics2D) g; 011 g2d.setColor (Color.blue); 012 g2d.drawRect (75,75,300,200); 013 014 // Zeichnen wir nun ein weiteres Rechteck, aber dieses Mal verwenden wir 015 // einen GeneralPath, um es segmentweise anzugeben. 016 // Außerdem werden wir dieses 017 // Rechteck relativ zum Geräteraum (und damit zu 018 // den ersten beiden Vierecken) mithilfe einer AffineTransform übersetzen und drehen. 019 // Wir werden auch die Farbe ändern. 020 GeneralPath path = new GeneralPath (GeneralPath.EVEN_ODD); 021 path.moveTo (0.0f, 0.0f); 022 path.lineTo (0.0f, 125.0f); 023 path.lineTo (225.0f, 125.0f);024 path.lineTo (225,0f, 0,0f); 025 path.closePath (); 026 027 AffineTransform at = new AffineTransform (); 028 at.setToRotation (-Math.PI / 8.0); 029 g2d.transform (at); 030 at.setToTranslation (50.0f, 200.0f); 031 g2d.transform (at); 032 033 g2d.setColor (Color.green); 034 g2d.fill (Pfad); 035}

Beachten Sie, dass wir , da GeneralPathes sich im java.awt.geomPaket befindet, auch eine Importzeile hinzufügen müssen:

import java.awt.geom. *; 

Die Ausgabe von Beispiel 02 ist in der folgenden Abbildung dargestellt.

Java 2D ermöglicht die Angabe beliebiger Formen über die java.awt.ShapeSchnittstelle. Eine Vielzahl von Standardformen wie Rechtecke, Polygone, 2D-Linien usw. implementieren diese Schnittstelle. Eine der interessantesten in Bezug auf Flexibilität ist java.awt.geom.GeneralPath.

GeneralPathMit s können Sie einen Pfad mit einer beliebigen Anzahl von Kanten und einer möglicherweise äußerst komplexen Form beschreiben. In Beispiel 02 haben wir ein Rechteck erstellt (Linien 020-025), aber wir hätten genauso gut eine andere Seite oder Seiten hinzufügen können, um ein Fünfeck, ein Siebeneck oder ein anderes mehrseitiges Polygon zu erstellen. Beachten Sie auch, dass GraphicsJava 2D im Gegensatz zu Standardcode die Angabe von Koordinaten mithilfe von Gleitkommazahlen anstelle von Ganzzahlen ermöglicht. CAD-Anbieter der Welt, freuen Sie sich! In der Tat, unterstützt Java 2D integer, doubleund floatingArithmetik in vielen Orten.

Sie haben wahrscheinlich auch bemerkt, dass wir beim Erstellen des Pfads einen Parameter an GeneralPath.EVEN_ODDden Konstruktor übergeben haben (Zeile 020). Dieser Parameter stellt eine Wicklungsregel dar , die dem Renderer mitteilt, wie das Innere der durch unseren Pfad angegebenen Form bestimmt werden soll. Weitere Informationen zu Java 2D-Wicklungsregeln finden Sie in der Java 2D-Javadoc-Dokumentation, auf die in den Ressourcen verwiesen wird.

Die andere wichtige Neuerung in Beispiel 02 dreht sich um die Verwendung von a java.awt.geom.AffineTransforms (Zeilen 027-031). Ich überlasse die Einzelheiten solcher Transformationen dem Leser (siehe Ressourcen für Artikel, in denen dies ausführlicher behandelt wird), aber es genügt zu sagen, AffineTransformdass Sie mit jeder Java 2D-Grafik arbeiten können, um sie zu übersetzen (zu verschieben) und zu drehen , skalieren Sie es, scheren Sie es oder führen Sie Kombinationen dieser Manipulationen durch.

The key to AffineTransform lies in the concept of Device Space and User Space. Device Space is that area into which the graphics will be rendered on the screen. This is analogous to the coordinates that are used when one creates regular AWT-style Graphics-based 2D graphics. User Space, however, is a translatable, rotatable coordinate system that may be operated on by one or more AffineTransforms.

Device Space and User Space coordinate systems initially overlap, with the origin at the upper left of the rendering surface (here, a Frame). The positive x axis moves right from the origin, while the positive y axis moves down.

After the first transformation in Example02 (lines 028 and 029), the User Space coordinate system has been rotated 22.5 degrees counterclockwise relative to the Device Space. Both still share the same origin. (Note that rotations are specified in radians, with -PI/8 radians equaling -22.5 degrees, or 22.5 degrees CCW.) If we were to stop here and draw the rectangle, it would be rotated mostly out of our field of view in the application Frame.

We next apply a second transformation (lines 030 and 031), this one a translation, after the rotation is complete. This moves the User Space coordinate system relative to the Device Space, shifting it down 200.0 (float) units and right 50.0 (float) units.

When we fill in the green rectangle, it is translated and rotated relative to the Device Space.

Of Bezier and higher-ordered curves

Now that we have examined how transforms can be used to manipulate graphical objects, let's reexamine how we build complex and interesting arbitrary shapes.

Curves are used throughout mathematics and computer graphics to approximate complex shapes using a finite, well-defined (and ideally small) number of mathematical points. Whereas the standard AWT did not directly support drawing with arbitrary curves in the past (Java 1.0 or 1.1 platforms), Java 2D adds built-in support for first-, second-, and third-order curves. You can draw curves with two end points and zero, one, or two control points. Java 2D computes first- and second-order curves using linear and quadratic formulas and cubic, or third-order, curves using Bezier curves.

(Bezier-Kurven sind eine Art parametrischer Polynomkurve, die einige sehr wünschenswerte Eigenschaften im Zusammenhang mit der Berechnung geschlossener Kurven und Flächen aufweist. Sie werden in zahlreichen Grafikanwendungen verwendet. Weitere Informationen zur Verwendung parametrischer Polynome und Bezier-Kurven finden Sie in den Ressourcen in Computergrafiken.) Die GeneralPathMethoden, die jede dieser Kurven zeichnen, sind:

  • lineTo() für gerade Segmente (nur Endpunkte angeben)
  • quadTo() für quadratische Kurven (geben Sie einen Kontrollpunkt an)
  • curveTo() für Kurven dritter Ordnung (geben Sie zwei Kontrollpunkte an, die mit der kubischen Bezier-Kurve gezeichnet wurden)