14.7 Clipping-Operationen
 
Alle primitiven Zeichenoperationen wirken sich auf den gesamten Bildschirm aus und sind nicht auf bestimmte Bereiche eingeschränkt. Wenn wir Letzteres erreichen wollen, setzen wir einen so genannten Clipping-Bereich, außerhalb dessen nicht mehr gezeichnet wird. Der Beschnittbereich lässt sich mit zwei Funktionen des aktuellen Graphic-Objekts modifizieren beziehungsweise einschränken:
abstract class java.awt. Graphics
|
|
abstract void setClip( int x, int y, int width, int height )
Setzt den neuen Beschnittbereich, sodass gezeichnete Zeile außerhalb des Bereichs nicht sichtbar sind. |
|
abstract void clipRect( int x, int y, int width, int height )
Verkleinert den Beschrittbereich, in dem der aktuelle Bereich (getClip()) mit dem übergebenen Rechteck geschnitten wird. Wurde vorher kein Beschnittbereich festgelegt – getClip() lieferte null – ist clipRect() mit setClip() identisch. |
Das folgende Programm setzt den ersten Clipping-Bereich mit setClip(100, 100, 100, 200). Das Füllen eines sehr großen Bereichs wird dann lokal nur im Beschnittbereich ausgeführt. Anschließend verkleinert clipRect(0, 200, 150, 50) den Bereich, doch obwohl x bei 0 geginnt, hat schon setClip() ihn bei 100 gesetzt, so dass die Verbindung der beiden Bereiche (100, 100, 100, 200) Ç (0, 200, 150, 50) = (100, 200, 50, 50) ergibt.
Listing 14.13
ClipDemo.java
import java.awt.*;
import javax.swing.*;
public class ClipDemo extends JPanel
{
@Override
protected void paintComponent( Graphics g )
{
super.paintComponent( g );
Graphics gcopy = g.create();
// Clipping erstmalig setzen, auch mit clipRect() möglich
g.setClip( 100, 100, 100, 200 ); // setClip()!!
g.setColor( Color.ORANGE );
g.fillRect( 0, 0, getWidth(), getHeight() );
g.setColor( Color.BLACK );
g.drawOval( 150, 100, 100, 100 );
// Zweiter Clipping-Bereich
g.clipRect( 0, 200, 150, 50 ); // clipRect()!
g.setColor( Color.BLUE );
g.fillRect( 0, 0, 5000, 5000 );
// Arbeite mit der ursprünglichen Größe
gcopy. setColor( Color.GREEN );
gcopy. fillRect( 50, 50, 20, 50 );
gcopy.dispose();
}
public static void main( String[] args )
{
JFrame f = new JFrame();
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.setSize( 400, 400 );
f.add( new ClipDemo() );
f.setVisible( true );
}
}
 Hier klicken, um das Bild zu Vergrößern
Abbildung 14.3
Clipping-Bereiche
Den alten Zustand für Graphics wiederherstellen
Für die Zeichenoperationen im Clipping-Bereich gibt es noch eine alternative Implementierung. Diese verzichtet auf die Kopie des Grafikkontexts mittels create() am Anfang und setzt am Schluss vor die Stelle von gcopy ein getGraphics(), mit dem sich der alte Kontext wiederherstellen lässt. Dann können wir wieder mit g.drawXXX() arbeiten, und gcopy ist überflüssig. Mit dem Original können wir dann das Clipping zurücksetzen und wieder ohne Zuschnitt arbeiten. Wenn wir nach dem Clipping das Original nicht mehr benötigen, können wir selbstverständlich auf die Kopie verzichten.
Alternative Formen
Durch setClip() können alternativ zu den rechteckigen Formen auch beliebige Shape-Objekte die Clipping-Form vorgeben. Nachfolgende paintComponent()-Methode benutzt als Beschnitt ein Dreieck:
Listing 14.14
ClipTri.java, Ausschnitt
@Override
protected void paintComponent( Graphics g )
{
super.paintComponent( g );
Rectangle r = g.getClipBounds();
System.out.println( r );
Polygon p = new Polygon(
new int[] { 200, 100, 300 },
new int[] { 100, 300, 300},
3 );
g.setClip( p );
g.setColor( Color.ORANGE );
g.fillRect( 0, 0, 500, 500 );
}
Bei alten Implementierungen funktioniert das nicht. Auf der Konsole erscheint dann eine Fehlermeldung der folgenden Art:
java.lang.IllegalArgumentException:\ setClip(Shape) only supports Rectangle objects
Verdeckte Bereiche und schnelles Bildschirmerneuern
Clipping-Bereiche sind nicht nur zum Einschränken der Primitiv-Operationen sinnvoll. Bei Bereichsüberdeckungen in Fenstern liefern sie wertvolle Informationen über den neu zu zeichnenden Bereich. Bei einer guten Applikation wird nur der Teil wirklich neu gezeichnet, der auch überdeckt wurde. So lässt sich Rechenzeit sparen.
Beispiel Informationen über Clipping-Bereiche
public void paint( Graphics g )
{
Rectangle r = g.getClipBounds();
System.out.println( r );
}
Das Programm erzeugt etwa
java.awt.Rectangle[x=4,y=23,width=392,height=373]
java.awt.Rectangle[x=104,y=87,width=292,height=309]
java.awt.Rectangle[x=104,y=87,width=286,height=211]
java.awt.Rectangle[x=104,y=87,width=243,height=196]
|
java.awt.Rectangle[x=104,y=87,width=221,height=219]
java.awt.Rectangle[x=101,y=89,width=221,height=219]
...
Aus den Ausgaben lassen sich verschiedene Fensteroperationen ableiten: Ein fremdes Fenster wurde über das Java-Fenster geschoben und dann das fremde Fenster verkleinert.
|
Die Rectangle-Informationen geben Aufschluss über die Größe der neu zu zeichnenden Bereiche. Haben wir schon daran gedacht, die Information in einem Image-Objekt abzulegen, lässt sich wunderbar drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) nutzen. Hier müssen wir die Werte aus dem Rectangle auslesen und in drawImage() übertragen. getClipBounds() liefert ein Rectangle-Objekt, dessen Werte für drawImage() nötig sind. Da jedoch auch beliebige Formen möglich sind, liefert getClip() ein Shape-Objekt. getClipRect() ist die veraltete Methode zu getClipBounds(), sonst aber identisch. Die Methode getClipBounds(Rectangle) – eine der wenigen nicht abstrakten Methoden in Graphics – legt die Informationen im übergebenen Rectangle-Objekt ab, welches auch zurückgeliefert wird. Sie ruft nur getClipBounds() auf und überträgt die vier Attribute in das Rechteck.
Im Übrigen: In der paint()-Methode ist es oft klug, mit clipRect() den Bereich einzuschränken, statt mit setClip() zu setzen, der er könnte schon relativ klein sein; setClip() könnte den Beschnittbereich größer machen und unnötige Zeichenoperationen erzwingen.
|