Iniziare con Java 2D

L'API Java 2D è un'API principale della piattaforma Java 1.2 (vedere Risorse per una varietà di informazioni sull'API e sulle sue implementazioni). Le implementazioni dell'API sono disponibili come parte delle Java Foundation Classes (JFC) nelle attuali versioni beta di Sun JDK per Windows NT / 95 e Solaris. Con la finalizzazione di Java 1.2, Java 2D dovrebbe essere disponibile su più piattaforme.

Si noti che sebbene Java 2D sia stato sviluppato in qualche modo indipendentemente dalle altre parti di JFC, è comunque una parte centrale di 1.2 AWT. Faremo la distinzione e indicheremo le caratteristiche specifiche del 2D per la discussione, ma dovresti ricordare che questa funzionalità è altrettanto centrale per la grafica 1.2 quanto il vecchio supporto 1.0 e 1.1 AWT.

Java 2D estende i precedenti meccanismi AWT per disegnare grafica 2D, manipolare testo e caratteri, caricare e usare immagini e definire e trattare colori e spazi colore. Esploreremo questi nuovi meccanismi in questo e in quelli futuri.

Una nota sulla nomenclatura e le convenzioni

Per questa colonna, la mia piattaforma di sviluppo principale sarà un PC con Windows 95 o Windows NT. Spero di fornire altri suggerimenti e trucchi specifici per piattaforma ove possibile, ma mi concentrerò su Windows poiché è lì che trascorrerò la maggior parte del mio tempo.

Quando scrivo un nome di metodo, dovrebbe sempre essere della forma methodname(). Le parentesi finali hanno lo scopo di differenziarlo come metodo. Il metodo può o non può assumere parametri. In pratica, il contesto dovrebbe sempre renderlo chiaro.

Gli elenchi del codice sorgente verranno forniti con i numeri di riga inclusi. Ho intenzione di utilizzare i numeri di riga per fare un riferimento incrociato al testo dell'articolo e agli elenchi di codici, come appropriato. Questo dovrebbe anche rendere molto più facile annotare la colonna, se scegli di stampare una copia. Si noti, tuttavia, che i file sorgente collegati dalla colonna saranno normali file * .java (senza i numeri di riga) in modo da poterli scaricare e sviluppare con essi.

Poiché nei prossimi mesi scriverò di molte delle API per i media e le comunicazioni, voglio assicurarmi che tutto il codice di esempio abbia senso nel suo insieme e nelle sue singole parti. Cercherò di dare un nome coerente ai miei esempi e di inserirli in pacchetti sensati.

La parte superiore della gerarchia dei miei pacchetti sarà:

com.javaworld.media 

Ogni API o argomento di cui scrivo avrà almeno un sottopacchetto sotto questo livello superiore. Ad esempio, tutto il codice per questo articolo Java 2D sarà in:

com.javaworld.media.j2d 

Quindi, per richiamare la prima applicazione di esempio su Java 2D, dovresti scaricare il codice, inserirlo nel tuo classpath, quindi utilizzare:

java com.javaworld.media.j2d.Example01 

(Se lo spazio dei nomi è troppo lungo per i tuoi gusti o per qualche altro motivo desideri utilizzare il codice di esempio senza dover utilizzare il nome completo, è sufficiente commentare la riga del pacchetto all'inizio di ogni file di codice sorgente.)

Genererò un file Java Archive (jar) per il codice di esempio di ogni articolo e i file di classe. Questo archivio sarà reso disponibile nelle Risorse di ogni colonna, se desideri scaricarlo ed eseguire gli esempi dall'interno dell'archivio.

Manterrò anche un file jar aggiornato contenente tutto il codice e le classi dalle mie colonne di programmazione multimediale corrente e precedente . Questo file jar onnicomprensivo sarà disponibile sul mio sito Web personale.

Un ultimo punto sugli esempi: ho scelto di fare ogni esempio, a meno che non specifichi diversamente, un'applicazione o un'applet autonoma. Questo porterà di tanto in tanto a ripetizioni del codice, ma ritengo che preservi al meglio l'integrità di ogni singolo esempio.

Basta con le convenzioni. Cominciamo a programmare con Java 2D!

Graphics2D: una migliore classe grafica

La classe centrale all'interno dell'API Java 2D è la java.awt.Graphics2Dclasse astratta, che è una sottoclasse java.awt.Graphicsper estendere la funzionalità di rendering 2D. Graphics2Daggiunge un supporto più uniforme per la manipolazione di una varietà di forme, rendendo in effetti testo, linee e tutti i tipi di altre forme bidimensionali comparabili nelle loro capacità e utilità.

Cominciamo con un semplice esempio che mostra come ottenere e utilizzare un Graphics2driferimento.

001 pacchetto com.javaworld.media.j2d; 002003 import java.awt. *; 004 import java.awt.event. *; 005 006 public class Example01 extends Frame {007 / ** 008 * Istanzia un oggetto Example01. 009 ** / 010 public static void main (String args []) {011 new Example01 (); 012} 013 014 / ** 015 * Il nostro costruttore Example01 imposta le dimensioni del frame, aggiunge i componenti visivi 016 * e quindi li rende visibili all'utente. 017 * Utilizza una classe adapter per gestire la chiusura 018 * del frame da parte dell'utente. 019 ** / 020 public Example01 () {021 // Assegna un titolo al frame. 022 super ("Java 2D Example01"); 023024 // Imposta le dimensioni del frame. 025 setSize (400,300); 026 027 // Dobbiamo attivare la visibilità del nostro frame 028 // impostando il parametro Visible su true. 029 setVisible (true); 030031 // Ora,vogliamo essere sicuri di disporre correttamente delle risorse 032 // che questo frame sta usando quando la finestra è chiusa. Usiamo 033 // un adattatore di classe interna anonimo per questo. 034 addWindowListener (new WindowAdapter () 035 {public void windowClosing (WindowEvent e) 036 {dispose (); System.exit (0);} 037} 038); 039} 040 041 / ** 042 * Il metodo di pittura fornisce la vera magia. Qui 043 * castiamo l'oggetto Graphics su Graphics2D per illustrare 044 * che possiamo usare le stesse vecchie capacità grafiche con 045 * Graphics2D che siamo abituati a usare con Graphics. 046 ** / 047 public void paint (Graphics g) {048 // Ecco come disegnavamo un quadrato con larghezza 049 // di 200, altezza di 200 e che inizia con x = 50, y = 50. 050 g.setColor (Color.red); 051 g.drawRect (50,50,200,200); 052 053 // Let 's imposta il Colore su blu e poi usa l'oggetto Graphics2D 054 // per disegnare un rettangolo, sfalsato dal quadrato. 055 // Finora, non abbiamo fatto nulla con Graphics2D che 056 // non avremmo potuto fare anche usando Graphics. (In realtà 057 // stiamo utilizzando metodi Graphics2D ereditati da Graphics.) 058 Graphics2D g2d = (Graphics2D) g; 059 g2d.setColor (Color.blue); 060 g2d.drawRect (75,75,300,200); 061} 062}

Quando esegui Example01, dovresti vedere un quadrato rosso e un rettangolo blu, come mostrato nella figura sotto. Notare che esiste un problema di prestazioni noto con la versione Windows NT / 95 di JDK 1.2 Beta 3 (la versione 1.2 più recente di questa colonna). Se questo esempio è estremamente lento sul tuo sistema, potresti dover aggirare il bug come documentato in JavaWorld Java Tip 55 (vedi Risorse sotto per questo suggerimento).

Notare che proprio come non si istanzia direttamente un Graphicsoggetto, non si istanzia neppure un Graphics2Doggetto. Piuttosto, il runtime Java costruisce un oggetto di rendering e lo passa a paint()(riga 047 nel listato di codice Example01), e sulle piattaforme Java 1.2 e oltre, questo oggetto implementa anche la Graphics2Dclasse astratta.

Finora non abbiamo fatto nulla di particolarmente speciale con le nostre capacità grafiche 2D. Aggiungiamo del codice alla fine del paint()metodo del nostro esempio precedente e introduciamo diverse funzionalità nuove in Java 2D (Esempio02):

001 / ** 002 * Qui utilizziamo le nuove funzionalità dell'API 2D Java come le trasformazioni 003 * affine e gli oggetti Shape (in questo caso uno 004 * generico, GeneralPath). 005 ** / 006 public void paint (Graphics g) {007 g.setColor (Color.red); 008 g.drawRect (50,50,200,200); 009010 Graphics2D g2d = (Graphics2D) g; 011 g2d.setColor (Color.blue); 012 g2d.drawRect (75,75,300,200); 013 014 // Ora, disegniamo un altro rettangolo, ma questa volta, 015 // usiamo un GeneralPath per specificarlo segmento per segmento. 016 // Inoltre, trasleremo e ruoteremo questo 017 // rettangolo relativo allo spazio del dispositivo (e quindi, a 018 // i primi due quadrilateri) utilizzando un AffineTransform. 019 // Ne cambieremo anche il colore. 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); 032033 g2d.setColor (Color.green); 034 g2d.fill (percorso); 035}

Nota che poiché GeneralPathsi trova nel java.awt.geompacchetto, dobbiamo essere sicuri di aggiungere anche una riga di importazione:

import java.awt.geom. *; 

L'output di Example02 è mostrato nella figura seguente.

Java 2D consente la specifica di forme arbitrarie utilizzando l' java.awt.Shapeinterfaccia. Una varietà di forme predefinite come rettangoli, poligoni, linee 2D, ecc. Implementano questa interfaccia. Uno dei più interessanti in termini di flessibilità è java.awt.geom.GeneralPath.

GeneralPaths consentono di descrivere un percorso con un numero arbitrario di bordi e una forma potenzialmente estremamente complessa. Nell'Esempio02, abbiamo creato un rettangolo (linee 020-025), ma altrettanto facilmente avremmo potuto aggiungere un altro lato o lati per creare un pentagono, o ettagono, o qualche altro poligono a più lati. Si noti inoltre che, a differenza del Graphicscodice standard , Java 2D ci consente di specificare le coordinate utilizzando numeri in virgola mobile anziché interi. Fornitori CAD di tutto il mondo, rallegratevi! In realtà, Java 2D supporta integer, doublee floatingl'aritmetica in molti luoghi.

Probabilmente hai anche notato che quando abbiamo creato il percorso abbiamo passato un parametro,, GeneralPath.EVEN_ODDnel costruttore (riga 020). Questo parametro rappresenta una regola tortuosa che dice al renderizzatore come determinare l'interno della forma specificata dal nostro percorso. Fare riferimento alla documentazione Java 2D javadoc a cui si fa riferimento nelle risorse per ulteriori informazioni sulle regole di avvolgimento Java 2D.

L'altra grande innovazione nell'Esempio02 ruota attorno all'uso di una java.awt.geom.AffineTransforms (righe 027-031). Lascio al lettore le specifiche di tali trasformazioni (vedi Risorse per articoli che ne discutono in maggior dettaglio), ma è sufficiente dire che AffineTransformti permettono di operare su qualsiasi grafica Java 2D per tradurla (spostarla), ruotarla , ridimensionalo, taglialo o esegui combinazioni di queste manipolazioni.

La chiave AffineTransformsta nel concetto di spazio dispositivo e spazio utente . Lo spazio del dispositivo è l'area in cui la grafica verrà renderizzata sullo schermo. Questo è analogo alle coordinate che vengono utilizzate quando si crea una Graphicsgrafica 2D basata sullo stile AWT regolare . Lo spazio utente, tuttavia, è un sistema di coordinate traslabile e ruotabile che può essere utilizzato da uno o più AffineTransforms.

I sistemi di coordinate Spazio dispositivo e Spazio utente si sovrappongono inizialmente, con l'origine in alto a sinistra della superficie di rendering (qui, un Frame). L'asse x positivo si sposta a destra dall'origine, mentre l'asse y positivo si sposta verso il basso.

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.

(Le curve di Bézier sono un tipo di curva polinomiale parametrica che ha alcune proprietà molto desiderabili relative al calcolo di curve e superfici chiuse. Sono utilizzate in numerose applicazioni grafiche. Fare riferimento alle Risorse per ulteriori informazioni sull'uso dei polinomi parametrici e delle curve di Bezier in computer grafica.) I GeneralPathmetodi che disegnano ciascuna di queste curve sono:

  • lineTo() per segmenti retti (specificare solo i punti finali)
  • quadTo() per curve quadratiche (specificare un punto di controllo)
  • curveTo() per curve di terzo ordine (specificare due punti di controllo, disegnati usando la curva di Bézier cubica)