Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Java ist auch eine Insel von Christian Ullenboom
Buch: Java ist auch eine Insel (Galileo Computing)
gp Kapitel 14 Grafikprogrammierung mit dem AWT
gp 14.1 Das Abstract-Window-Toolkit
gp 14.1.1 Java Foundation Classes
gp 14.2 Fenster unter grafischen Oberflächen
gp 14.2.1 Fenster öffnen
gp 14.2.2 Größe und Position des Fensters verändern
gp 14.2.3 Fenster- und Dialog-Dekoration
gp 14.3 Das Toolkit
gp 14.3.1 Einen Hinweis beepen
gp 14.4 Grundlegendes zum Zeichnen
gp 14.4.1 Die paint()-Methode
gp 14.4.2 Auffordern zum Neuzeichnen mit repaint()
gp 14.4.3 Fensterinhalte ändern und die ereignisorientierte Programmierung
gp 14.5 Punkte, Linien und Rechtecke aller Art
gp 14.5.1 Linien
gp 14.5.2 Rechtecke
gp 14.6 Alles was rund ist
gp 14.7 Polygone und Polylines
gp 14.7.1 Die Polygon-Klasse
gp 14.7.2 N-Ecke zeichnen
gp 14.7.3 Vollschlanke Linien zeichnen
gp 14.8 Zeichenketten schreiben
gp 14.8.1 Einen neuen Zeichensatz bestimmen
gp 14.8.2 Ableiten eines neuen Fonts aus einem gegebenen Font
gp 14.8.3 Zeichensätze des Systems ermitteln
gp 14.8.4 Die Klasse FontMetrics
gp 14.8.5 True Type Fonts
gp 14.9 Clipping-Operationen
gp 14.10 Farben
gp 14.10.1 Zufällige Farbblöcke zeichnen
gp 14.10.2 Farbanteile zurückgeben
gp 14.10.3 Vordefinierte Farben
gp 14.10.4 Farben aus Hexadezimalzahlen erzeugen
gp 14.10.5 Einen helleren oder dunkleren Farbton wählen
gp 14.10.6 Farbmodelle HSB und RGB
gp 14.10.7 Die Farben des Systems
gp 14.11 Bilder anzeigen und Grafiken verwalten
gp 14.11.1 Eine Grafik zeichnen
gp 14.11.2 Grafiken zentrieren
gp 14.11.3 Laden von Bildern mit dem MediaTracker beobachten
gp 14.11.4 Kein Flackern durch Double-Buffering
gp 14.11.5 Bilder skalieren
gp 14.12 Programm-Icon setzen
gp 14.12.1 VolatileImage
gp 14.13 Grafiken speichern
gp 14.13.1 Bilder im GIF-Format speichern
gp 14.13.2 Gif speichern mit dem ACME-Paket
gp 14.13.3 JPEG-Dateien mit dem Sun-Paket schreiben
gp 14.13.4 Java Image Management Interface (JIMI)
gp 14.14 Von Produzenten, Konsumenten und Beobachtern
gp 14.14.1 Producer und Consumer für Bilder
gp 14.14.2 Beispiel für die Übermittlung von Daten
gp 14.14.3 Bilder selbst erstellen
gp 14.14.4 Die Bildinformationen wieder auslesen
gp 14.15 Filter
gp 14.15.1 Grundlegende Eigenschaft von Filtern
gp 14.15.2 Konkrete Filterklassen
gp 14.15.3 Mit CropImageFilter Teile ausschneiden
gp 14.15.4 Transparenz
gp 14.16 Alles wird bunt mit Farbmodellen
gp 14.16.1 Die abstrakte Klasse ColorModel
gp 14.16.2 Farbwerte im Pixel mit der Klasse DirectColorModel
gp 14.16.3 Die Klasse IndexColorModel
gp 14.17 Drucken
gp 14.17.1 Drucken mit dem einfachen Ansatz
gp 14.17.2 Ein PrintJob
gp 14.17.3 Drucken der Inhalte
gp 14.17.4 Komponenten drucken
gp 14.17.5 Den Drucker am Parallelport ansprechen
gp 14.18 Java 2D-API
gp 14.18.1 Grafische Objekte zeichnen
gp 14.18.2 Geometrische Objekte durch Shape gekennzeichnet
gp 14.18.3 Eigenschaften geometrischer Objekte
gp 14.18.4 Transformationen mit einem AffineTransform-Objekt
gp 14.19 Graphic Layers Framework
gp 14.20 Grafikverarbeitung ohne grafische Oberfläche
gp 14.20.1 Xvfb-Server
gp 14.20.2 Pure Java AWT Toolkit (PJA)


Galileo Computing

14.14 Von Produzenten, Konsumenten und Beobachterndowntop

Bisher kamen die angezeigten Grafiken irgendwie vom Datenträger auf den Schirm. Im Folgenden wollen wir dies etwas präziser betrachten. An den verschiedensten Stellen haben wir bereits von der Eigenschaft der drawImage()-Methode gesprochen, erst bei der ersten Benutzung das Bild zu laden. In Java kommt hinter den Kulissen ein Modell zum Tragen, welches komplex, aber auch sehr leistungsfähig ist. Es ist das Modell vom Erzeuger (engl. producer) und Verbraucher (engl. consumer). Ein Beispiel aus der realen Welt: Katzenpfötchen wird von Katjes produziert und von mir konsumiert. Oder etwas technischer: Ein Objekt, das vom Netzwerk eine Grafik holt, oder auch ein Objekt, das aus einem Array mit Farbinformationen das Bild aufbaut, ist der Produzent. Der Konsument ist die Zeichenfunktion, die das Bild darstellen möchte.


Galileo Computing

14.14.1 Producer und Consumer für Bilderdowntop

Ein besonderer Produzent, der sich um alles kümmert, was das Bilderzeugen angeht, ist der Image Producer. Im Gegensatz dazu sind es die Image Consumer, die etwaige Bilddaten benutzen.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Zu diesen Bildkonsumenten zählen in der Regel Low-level-Zeichenroutinen, die auch die Grafik auf den Schirm bringen. In der Bibliothek von Java ist die Aufgabe der Bildproduzenten und -konsumenten durch die Schnittstelle ImageProducer und ImageConsumer abgebildet. Das Interface ImageProducer beschreibt Methoden, um Pixel eines Bilds bereitzustellen. Klassen, die nun die Schnittstelle implementieren, stellen somit die Bildinformationen einer speziellen Quelle dar. Die Klasse MemoryImageSource ist eine vorgefertigte Klasse, die ImageProducer implementiert. Sie produziert Bildinformationen aus einem Array von Pixeln, die im Speicher gehalten werden.

Im Gegenzug beschreibt die Schnittstelle ImageConsumer Methoden, die einem Objekt den Zugriff auf die Bilddaten des Produzenten erlauben. Objekte, die ImageConsumer implementieren, hängen somit immer an einem Bilderzeuger. Der Produzent liefert die Daten über Methoden zum Konsumenten, indem spezielle, im Interface ImageConsumer vorgeschriebene Methoden aufgerufen werden.


Galileo Computing

14.14.2 Beispiel für die Übermittlung von Datendowntop

Damit für uns das Verfahren deutlich wird, beschreiben wir zunächst das Prinzip der Übermittlung von Daten vom Produzenten zum Konsumenten an einem Beispiel. Wir entwickeln eine Klasse Produzent mit einer Methode beginne() und eine Klasse Konsument, die vom Produzenten Daten haben möchte. Wenn der Produzent etwas für den Konsumenten erzeugen soll, dann ruft der Konsument die erzeugeFür()-Routine mit einem Verweis auf sich auf. Danach ruft der Konsument die Funktion beginne() auf. Über diesen Verweis an erzeugeFür() weiß dann der Produzent, an wen er die Daten schicken muss. Nach dem Aufruf von beginne() sendet der Produzent an alle Konsumenten die Daten, indem er die Methode briefkasten() aller Konsumenten aufruft und somit die Daten abliefert.

class Konsument
{
  irgendwo()
  {
    Produzent nudeln
    nudeln.erzeugeFür( this )
    nudeln.beginne()
  }
  briefkasten( int data )
  {
     ausgabe( "Ich habe ein " + data + " bekommen" )
  }
}
class Produzent
{
  erzeugeFür( Konsument einKonsument )
  {
     // merke sich alle Konsumenten in einer Liste
  }
  beginne()
  {
     data = erzeugeDatum()
     für alle interessierten Konsumeten
       konsument.briefkasten( data )
  }
 }

Wie der ImageProducer dem ImageConsumer die Daten beschreibt

Das Interface ImageProducer benutzt die Methode setPixels() im ImageConsumer, um das Bild dem Konsumenten zu beschreiben. Ein gutes Beispiel für das Modell ist das Laden eines Bilds über ein Netzwerk. So verlangt etwa die Zeichenfunktion drawImage() das Bild. Nehmen wir eine konkrete Klasse an, die ein Bild laden kann. Diese implementiert natürlich dann das Interface ImageProducer. Zunächst beginnt dann die Klasse mit dem Lesevorgang, indem sie eine Netzwerkverbindung aufbaut und einen Kommunikationskanal öffnet. Das Erste, was das Programm dann vom Server liest, ist die Breite und Höhe des Bilds. Seine Informationen über die Dimension gibt es dem Konsumenten mit der Methode setDimensions() weiter. Uns sollte bewusst sein, dass es zu einem Produzenten durchaus mehrere Konsumenten geben kann. Korrekter heißt das: Die Information über die Dimension wird zu allen horchenden Konsumenten gebracht.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Als Nächstes liest der Produzent die Farbinformationen für das Bild. Über die Farbtabelle findet er heraus, welches Farbmodell das Bild benutzt. Dies teilt er über den Aufruf von setColorModel() jedem Konsument mit. Danach lassen sich die Pixel des Bilds übertragen. Die verschiedenen Formate nutzen dabei allerdings unterschiedliche Techniken. Sie heißen Hints. Die Übermittlung der Hints an den Konsumenten erfolgt mit der Methode setHints(). Jeder Konsument kann daraufhin seine Handhabung mit den Bildpunkten optimieren. So könnte etwa ein Konsument, der ein Bild skalieren soll, genau in dem Moment die Bildzeile skalieren und die Werte neu berechnen, in dem der Produzent eine Zeile erzeugt. Mögliche Werte für die Hints sind Folgende:


interface java.awt.image.ImageConsumer

gp ImageConsumer.TOPDOWNLEFTRIGHT
Die Pixellieferung erfolgt von oben nach unten und von links nach rechts.
gp ImageConsumer.COMPLETESCANLINES
Mehrere Zeilen (Scanlines) bauen das Bild auf. Eine Scanline besteht aus mehreren Pixeln, die dann in einem Rutsch anliegen. Es wird also so oft setPixels() aufgerufen, wie es Bildzeilen gibt.
gp ImageConsumer.SINGLEPASS
Die Pixel des gesamten Bilds können wir nach einem Aufruf von setPixels() erwarten. Niemals liefern mehrere Aufrufe dieselben Bildinformationen. Ein progressives JPEG-Bild fällt nicht in diese Kategorie, da es ja erst in mehreren Durchläufen vollständig vorliegt.
gp ImageConsumer.SINGLEFRAME
Das Bild besteht aus genau einem statischen Bild. Ein Programm, welches nicht schrittweise Zeilen zur Verfügung stellt, benutzt dieses Flag. Der Consumer ruft also einmal setPixels() vom Producer auf, und danach steht das Bild bereit. Ein Bild aus einer Videoquelle würde, da es sich immer wieder ändert, niemals SINGLEFRAME sein.
gp ImageConsumer.RANDOMPIXELORDER
Die Bildpunkte kommen in beliebiger Reihenfolge an. Der ImageConsumer kann somit keine Optimierung vornehmen, die von der Reihenfolge der Pixel abhängt. Ohne Bestätigung einer anderen Reihenfolge müssen wir von RANDOMPIXELORDER ausgehen. Erst nach Abschluss durch einen Aufruf von imageComplete() - siehe unten - lässt sich mit dem Bild weiterarbeiten.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Nun kann der Producer anfangen, mittels setPixels() Pixel zu produzieren. Da der Produzent die setPixels()-Methode aufruft, die im Consumer implementiert ist, wird der Konsument dementsprechend all den Programmcode enthalten, der die Bildinformationen benötigt. Entsprechend erinnern wir uns an die Methode briefkasten() aus unserem ersten Beispiel.

Wir haben oben nur das erhaltene Datum ausgegeben. Ein wirklicher Konsument allerdings sammelt alle Daten, bis das Bild geladen ist, und verwendet es dann weiter, indem er es zum Beispiel anzeigt. In der Regel ist erst nach vielen Aufrufen das Bild aufgebaut, und zwar genau dann, wenn der Consumer jeweils nur eine Zeile des Bilds liefert. Es kann aber auch nur ein Aufruf genügen, nämlich genau dann, wenn das Bild in einem Rutsch geliefert wird (ImageConsumer.SINGLEPASS).

Nachdem das Bild geladen ist, ruft der Producer die imageComplete()-Methode für den Konsumenten auf, um anzuzeigen, dass das Bild geladen ist. Nun sind also keine Aufrufe mehr für setPixels() möglich, um das Bild vollständig zu erhalten. Der Methode imageComplete() wird immer ein Parameter übergeben, und für ein gültiges Bild ist der Parameter ImageConsumer.STATICIMAGEDONE. Auch Multi-Frames-Images (etwa Animated GIF) ist dies gestattet und zeigt an, dass das letzte Bild der Sequenz geladen ist. Besteht das Bild aus mehreren Teilen, es folgen aber noch weitere Frames, ist der Parameter SINGLEFRAMEDONE. Hier zeigt SINGLEFRAMEDONE also nur den Abschluss eines Einzelbilds an. Über setHints() ist dann aber schon ein Multi-Frame angekündigt gewesen.

Mehrere Fehler können beim Produzieren auftreten. IMAGEERROR beziehungsweise IMAGEABORT zeigen an, dass ein schwerer Fehler auftrat und das Bild nicht erzeugt werden konnte. Die Unterscheidung der beiden Fehlerquellen ist nicht eindeutig.


interface java.awt.image.ImageConsumer

gp void imageComplete( int status )
Wird aufgerufen, wenn der ImageProducer alle Daten abgeliefert hat, auch, wenn ein einzelner Rahmen einer Multi-Frame-Animation beendet ist oder ein Fehler auftrat.
gp void setColorModel( ColorModel model )
Das ColorModel bestimmt, wie setPixels() die Pixelinformationen wertet.
gp void setDimensions( int width, int height )
Liefert die Ausmaße der Bildquelle.
gp void setHints( int hintflags )
Liefert die Reihenfolge der Bildinformationen.
gp void setPixels( int x, int y, int w, int h, ColorModel model,
byte pixels[], int off, int scansize)
Die Bildpunkte werden durch einen oder mehrere Aufrufe der Funktion überliefert.
gp void setPixels( int x, int y, int w, int h, ColorModel model,
int pixels[], int off, int scansize )
Die Bildpunkte werden durch einen oder mehrere Aufrufe der Funktion überliefert.
gp void setProperties( Hashtable props )
Setzt eine Liste von Eigenschaften, die mit dem Bild verbunden sind. Dies kann etwa eine Zeichenkette über den Bilderzeuger sein, die Geschwindigkeit eines Bildaufbaus oder die Information, wie viele Konsumenten an einem Produzenten hängen können.

Galileo Computing

14.14.3 Bilder selbst erstellendowntop

Bisher haben wir über unsere bekannten Zeichenfunktionen wie drawLine() und so weiter auf die Oberfläche gezeichnet. Die paint()-Methode gab uns den Grafikkontext in die Hand, mit dem wir die Operation durchführen konnten. Nun wäre es aber von Vorteil, wenn wir direkt in eine Zeichenfläche malen könnten und nicht immer über die Elementarfunktionen gehen müssten. Es ist intuitiv klar, dass dieser Weg bei bestimmten Grafikoperationen schneller ist. So können wir nicht existierende Grafikfunktionen - beispielsweise eine weiche Linie - durch Punktoperationen direkt auf dem Raster durchführen, ohne immer die Funktion drawLine() und setColor() für einen Punkt zu bemühen. Wesentlich schneller sind wir wieder mit Bildern im Hintergrund, so wie wir im letzten Abschnitt flackerfreie Bilder produziert haben.

Was wir dafür benötigen, ist eine Klasse aus dem awt.image-Paket, nämlich MemoryImageSource.

Listing 14.19 MemImage.java

import java.awt.*;
import java.awt.image.*;
class MemImage extends Frame
{
  final static int a = Color.white.getRGB();
  final static int b = Color.black.getRGB();
  final static int c = Color.yellow.getRGB();
  int imageData[] = {
    a,a,a,a,a,a,a,a,a,a,b,b,b,b,b,b,b,b,b,b,b,a,a,a,a,a,a,a,a,a,a,a,
    a,a,a,a,a,a,a,b,b,b,b,b,c,c,c,c,c,c,c,b,b,b,b,b,a,a,a,a,a,a,a,a,
    a,a,a,a,a,b,b,b,c,c,c,c,c,b,c,c,c,b,c,c,c,c,c,b,b,b,a,a,a,a,a,a,
    a,a,a,b,b,b,c,c,b,b,c,c,c,b,b,b,b,b,c,c,c,b,b,c,c,b,b,b,a,a,a,a,
    a,a,b,b,c,c,c,b,b,c,c,c,c,b,c,b,c,b,c,c,c,c,b,b,c,c,c,b,b,a,a,a,
    a,b,b,c,c,b,b,b,b,c,c,c,c,b,b,b,b,b,c,c,c,c,b,b,b,b,c,c,b,b,a,a,
    a,b,c,b,b,b,b,b,b,c,c,c,c,b,b,b,b,b,c,c,c,c,b,b,b,b,b,b,c,b,a,a,
    b,b,c,b,b,b,b,b,b,b,c,c,b,b,b,b,b,b,b,c,c,b,b,b,b,b,b,b,c,b,b,a,
    b,c,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,c,b,a,
    b,c,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,c,b,a,
    b,c,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,c,b,a,
    b,c,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,c,b,a,
    b,c,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,c,b,a,
    b,b,c,b,b,b,b,c,c,b,b,b,b,b,b,b,b,b,b,b,b,b,c,c,b,b,b,b,c,b,b,a,
    a,b,c,b,b,b,c,c,c,c,b,c,c,b,b,b,b,b,c,c,b,c,c,c,c,b,b,b,c,b,a,a,
    a,b,b,c,c,b,c,c,c,c,b,c,c,c,b,b,b,c,c,c,b,c,c,c,c,b,c,c,b,b,a,a,
    a,a,b,b,c,c,b,c,c,c,c,c,c,c,c,b,c,c,c,c,c,c,c,c,b,c,c,b,b,a,a,a,
    a,a,a,b,b,b,c,c,c,c,c,c,c,c,c,b,c,c,c,c,c,c,c,c,c,b,b,b,a,a,a,a,
    a,a,a,a,a,b,b,b,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,b,b,b,a,a,a,a,a,a,
    a,a,a,a,a,a,a,b,b,b,b,b,c,c,c,c,c,c,c,b,b,b,b,b,a,a,a,a,a,a,a,a,
    a,a,a,a,a,a,a,a,a,a,b,b,b,b,b,b,b,b,b,b,b,a,a,a,a,a,a,a,a,a,a,a
  };
  Image icon;
  MemImage()
  {
    super( "Mit freundlicher Unterstuetzung von..." );
    icon = createImage(new MemoryImageSource( 32, 21, imageData, 0, 32) );
    setBackground( Color.white );
    setSize( 300, 300 );
    show();
  }
  public void paint( Graphics g )
  {
    g.drawImage( icon, 100, 100, 64, 64, this );
  }
  public static void main( String args[] )
  {
    MemImage mi = new MemImage();
  }
}

Bildpunkte ansprechen

Zunächst wird das Bild im Speicher, das heißt, in einem Integer-Feld gezeichnet. So bereiten die drei Zeilen

int breite   = 100;
int höhe     = 100;
int pixels[] = new int[ breite * höhe ];

ein Ganzzahl-Array mit 100 x 100 Bildpunkten vor.

Die Farben sind durch die Grundfarben Rot, Grün und Blau in den Abstufungen 0 bis 255 gegeben. Sie setzen sich zu einer 24-Bit-Zahl zusammen, die den Farbwert repräsentiert. Jede Farbe hat eine eigene Position.

pixels[ x*width + y ] = (r << 24) | (g << 16) | b;

Wir dürfen allerdings nicht vergessen, die Transparenz auf 0xFF zu setzen.

Die Methode, die aus den Rot-, Grün- und Blau-Werten das Integer berechnet, welches in das Array positioniert wird, lässt sich aus oberer Zeile ableiten.

static public int rgbToInt( byte r, byte g, byte b ) {
  return (int)( 0xFF000000 + r << 16 + g << 8 + b );
}

Nachdem das Feld mit Inhalt gefüllt ist, machen wir daraus einen ImageProducer. Dies ist ein Interface für Objekte, die aus Daten ein Image erzeugen können. Ein ImageProducer kann ein Bild immer neu konstruieren, wenn es benötigt wird, beispielsweise, wenn sich die Breite oder Höhe ändert, da das Bild umskaliert wurde. Nachdem wir einen ImageProducer für unser handberechnetes Bild haben, müssen wir es natürlich noch in ein Image umwandeln, damit wir es zeichnen können. Die Umwandlung wird mit einer Funktion createImage() vorgenommen; sie erzeugt aus dem ImageProducer ein wirkliches Image. Die Zeile verdeutlicht dies:

Image i = createImage( new MemoryImageSource(breite, höhe, pixels, 0, breite) );

Dieses Image-Objekt kann nun wieder mit drawImage() gezeichnet werden.


class java.awt.image.MemoryImageSource
implements ImageProducer

gp MemoryImageSource( int w, int h, int pix[], int off, int scan )
Erzeugt ein ImageProducer-Objekt, das aus einem Integer-Feld ein Image-Objekt erstellt. Die Elemente des Arrays repräsentieren die RGB-Farben des aktuellen ColorModel. scansize ist der Abstand von einer Pixelzeile zur nächsten im Feld.

class java.awt.Component
implements ImageObserver, MenuContainer, Serializable

gp Image createImage( ImageProducer )
Erzeugt ein Image vom angegebenen ImageProducer.

Abbildung
Hier klicken, um das Bild zu Vergrößern


Galileo Computing

14.14.4 Die Bildinformationen wieder auslesentoptop

Das letzte Beispiel zeigt den Weg in die eine Richtung; das Bild wird konstruiert und dann in ein Image konvertiert. Doch auch der umgekehrte Weg ist notwendig und muss beispielsweise beim Speichern eines Bilds verwendet werden. Auch Grafikfilter müssen Farben von existierenden Bildpunkten mit in die Berechnung hineinnehmen.

Das Gegenteil vom MemoryImageSource ist ein PixelGrabber. Dieser wird auf ein Image-Objekt angesetzt und füllt ein Ganzzahl-Feld mit den Farbwerten, die die Anteile der Farben Rot, Grün und Blau enthalten.


Beispiel Bilddaten aus einer Grafik auslesen und in ein Feld legen
PixelGrabber grab = new PixelGrabber( image,0,0,breite,höhe,pixels,0,breite );

Das Auslesen der Farbwerte wird durch die Funktion grabPixels() initiiert. So füllt

grab.grabPixels();

unser Feld image mit den Farbwerten. Der PixelGrabber implementiert die Klasse Image-Consumer, die allgemein Bilder »verbraucht«. Die Farben werden ausgelesen, indem wir das Feld mit RGB-Farben wieder entschlüsseln.

int alpha = (pixel >> 24) & 0xff;
int red   = (pixel >> 16) & 0xff;
int green = (pixel >> 8)  & 0xff;
int blue  = (pixel)       & 0xff;

class java.awt.image.PixelGrabber
implements ImageConsumer

gp PixelGrabber( Image, int x, int y, int w, int h,
int pix[], int off, int scan )
Erzeugt ein PixelGrabber-Objekt, welches ein Rechteck von RGB-Farben aus dem Feld holt. Das Rechteck ist durch die Ausmaße x, y, w, h beschrieben. Die Farben für einen Punkt (i,j) sind im Feld an der Position (j - y) * scan + (i - x) + off. Mit der Umwandlung wird noch nicht begonnen. Sie muss mit der Funktion grabPixles angeregt werden.
gp boolean grabPixels() throws InterruptedException
Die Werte werden von einem Image oder ImageProducer geholt. Da das Kodieren einige Zeit in Anspruch nimmt, kann die Funktion von außen unterbrochen werden. Daher ist eine try-Anweisung notwendig, die InterruptedException abfängt. Geht alles gut, wird true zurückgegeben.
gp int getHeight()
Liefert die Höhe des Pixelfelds. Ist die Höhe nicht verfügbar, ist das Ergebnis -1.
gp int getWidth()
Liefert die Breite eines Pixelfelds - ist diese nicht verfügbar, ist das Ergebnis -1.

Ein Grabber-Beispiel

Das nachfolgende Programm lädt ein Bild und gibt die Farbinformationen - also die Anteile Rot, Grün, Blau - auf der Konsole aus. Dabei müssen wir nur die Maus im Bild bewegen. Die Ereignisbehandlung wird über einen MouseMotionListener übernommen. Im nächsten Kapitel werden wir die Ereignisbehandlung genauer kennen lernen. Um das Bild zu laden, nutzen wir eine Methode aus Swing, die später noch genauer erklärt wird.

Listing 14.20 DuAlterGrabber.java

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
class DuAlterGrabber extends JFrame
{
  Image image = new ImageIcon(
    ClassLoader.getSystemResource("gatesInAlbuquerque.jpg" )).getImage();
  
  DuAlterGrabber()
  {
    final int width = image.getWidth( this ), height = image.getHeight( this );
    final int pixels[] = new int[width * height];
    
    PixelGrabber grabber =  new PixelGrabber(
      image, 0, 0, width, height, pixels, 0, width );
    
    try
    {
      grabber.grabPixels();
    }
    catch ( InterruptedException e ) {
      System.err.println( "Error getting pixels" );
    }
    
    addMouseMotionListener( new MouseMotionAdapter() {
          public void mouseMoved( MouseEvent e )
          {
            int pixel = pixels[e.getY() * width + e.getX()];
            int red   = (pixel >> 16) & 0xff,
                green = (pixel >> 8) & 0xff,
                blue  = (pixel)  &  0xff;
            System.out.println( "R=" +red+ " G=" +green+ " B=" +blue );
          }
        } );
    setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    setSize( width, height );
  }
  
  public void paint( Graphics g )
  {
    if ( image != null )
      g.drawImage( image, 0, 0, this );
  }
  
  public static void main( String args[] )
  {
    new DuAlterGrabber().show();
  }
}

Abbildung
Hier klicken, um das Bild zu Vergrößern





Copyright (c) Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de