12.2 | Einbinden von Bildern |
In Java gibt es die Möglichkeit, Bilder einzubinden. Seit JDK 1.0 werden die Bildformate gif und jpg unterstützt. Seit dem JDK 1.3 kann man zusätzlich png benutzen. Sei dem J2SDK 1.4 ist durch die Verwendung des Image-I/O-API die Einbindung weiterer Formate möglich (siehe Abschnitt 12.10).
Ein Bild wird in einem Java-Programm durch ein Exemplar der Klasse Image repräsentiert. Doch durch Erzeugen eines Exemplars dieser Klasse ist noch kein Bild eingebunden. Zuerst muss eine Verknüpfung zwischen dem Java-Programm und dem Bild hergestellt werden. Wie diese Verknüpfung mit Hilfe der Methode getImage() bzw. createImage() in Applets und Applikationen hergestellt wird, wurde bereits in Abschnitt 7.3 erklärt.
Mit getImage() wird nur ein neues Exemplar der Klasse Image erzeugt, nicht jedoch das Bild geladen. Dies geschieht erst, wenn das Bild zum ersten Mal mit der Methode drawImage() der Klasse Graphics gezeichnet oder der Ladevorgang explizit gestartet wird.
Folgende zwei Varianten dieser Methoden sind für den Benutzer am wichtigsten:Diese Methoden wurden auch schon in anderen Abschnitten benutzt, ohne dass jedoch näher auf sie eingegangen wurde. Sie besitzen alle einen Parameter vom Typ ImageObserver.
- drawImage(Image img, int x, int y, ImageObserver observer)
Das Bild wird in Originalgröße gezeichnet. Die linke obere Ecke des Bildes erscheint an der übergebenen Position.- drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
Das Bild wird innerhalb eines Rechtecks, das durch die übergebenen Werte definiert ist, gezeichnet. Es wird hierbei an die Größe des Rechtecks angepasst. Somit sind Skalierungen und Verzerrungen möglich.
ImageObserver ist ein Interface, das den Aufbereitungsvorgang von Bildern überwacht. Da ImageObserver von der Klasse Component implementiert wird, kann der erforderliche Parameter durch das Schlüsselwort this bereitgestellt werden, wenn der Aufruf von drawImage() innerhalb der Klasse einer von Component abgeleiteten Klasse stattfindet:g.drawImage(myImage, 0, 0, this);Einzelheiten über den ImageObserver folgen am Ende dieses Abschnitts. Die Klasse Image besitzt Methoden, die die Ausmaße des Bildes liefern: Für die Angabe des ImageObservers gilt dasselbe wie bei drawImage(): Er kann auch hier innerhalb von Komponenten durch das Schlüsselwort this bereitgestellt werden. Beide Methoden liefern erst die gewünschte Größe, wenn das mit getImage() angelegte Bild vollständig geladen wurde. Dies liegt an der Arbeitsweise von getImage().
Wie schon erwähnt, lädt getImage() das angegebene Bild nicht sofort beim Aufruf, sondern merkt sich lediglich den Ort, an dem es gespeichert ist. Geladen wird das Bild erst, wenn man es zeichnet oder wenn man den Ladevorgang explizit startet.
Deshalb kann man regelrecht zusehen, wie die einzelnen Pixel eines Bildes angezeigt werden, wenn es zum ersten Mal gezeichnet wird, ohne es vorher zu laden. Das liegt daran, dass die Daten des Bildes erst während des Zeichnens übertragen werden.
Man kann dies umgehen, indem man das Bild in ein nicht sichtbares Offscreen-Image zeichnet, bevor es auf dem Bildschirm angezeigt wird. Durch den Zeichenaufruf wird der Ladevorgang begonnen. Folgt aber der drawImage()-Aufruf direkt auf das drawImage(), welches ein Bild in das Offscreen-Image zeichnet, wurde der Ladevorgang zwar begonnen, aber noch nicht abgeschlossen.
Eine sehr viel bessere Lösung des Problems liefern die Methoden prepareImage() und checkImage() der Klasse Component: Sie bieten dem Programmierer weitere Möglichkeiten, den Aufbereitungsvorgang von Bildern zu beeinflussen:Von beiden Methoden gibt es ebenfalls je eine Variante, die auf skalierte Bilder zugeschnitten ist. Sie besitzen zwei weitere Parameter, die der Breite und Höhe des Bildes entsprechen. Folgende Zeile startet den Aufbereitungsvorgang des Bildes myImage, das die Breite 40 und die Höhe 70 besitzt:
- int checkImage(Image, ImageObserver)
Liefert den Status des Aufbereitungsvorgangs eines Bildes. Dieser ist eine boolesche ODER-Verknüpfung der Flags des ImageObservers.- boolean prepareImage(Image, ImageObserver)
Startet den Aufbereitungsvorgang eines Bildes. Ist das Bild schon für die Anzeige am Bildschirm bereit, liefert die Methode true.prepared = prepareImage(myImage, 40, 70, this);Unter Starten des Aufbereitungsvorgangs versteht man das Bereitstellen von Pixeln. Pixel können durch unterschiedliche Vorgänge bereitgestellt werden:Diese Vorgänge benötigen alle recht viel Zeit. Um dem Benutzer eines Applets lästige Wartezeiten zu ersparen, kann man sie durch Verwendung von prepareImage() frühzeitig starten. Durch rechtzeitigen Aufruf von prepareImage() vor der Darstellung des Bildes kann man es ermöglichen, dass die Aufbereitung eines Bildes abgeschlossen ist, bevor es gezeichnet werden muss. Den Status über den Fortschritt des Aufbereitungsvorgangs liefert checkImage(). Das folgende Beispiel überwacht den Aufbereitungsvorgang eines Bildes:
- Laden eines Bildes
- Berechnen der Bildpunkte bei skalierten Bildern
- Berechnen der Bildpunkte bei gefilterten Bildern
public class CheckImageDemo extends Applet { Image picture; // Zu zeichnendes Bild public void init() { // Verweis auf das Bild anlegen picture = getImage(getCodeBase(), "images/fractal.gif"); // Starten des Ladevorganges prepareImage(picture, this); Thread t = Thread.currentThread(); // Warten, bis das Bild vollständig geladen ist while ((checkImage(picture, this) & ALLBITS) != ALLBITS) { try { // Pause, um dem Ladevorgang keine // Ressourcen zu nehmen t.sleep(50); } catch(InterruptedException e) { e.printStackTrace(); } } } public void paint(Graphics g) { g.drawImage(picture, 0, 0, this); } }Mit der Methode getImage() wird ein Bild angelegt und anschließend durch Aufruf von prepareImage() dessen Aufbereitungsvorgang begonnen. Danach wird in einer Schleife der Status des Bildes abgefragt:while ((checkImage(picture, this) & ALLBITS) != ALLBITS) {Der von checkImage() gelieferte Wert ist eine ODER-Verknüpfung der Flags des ImageObservers. Wenn hier von Flags die Rede ist, sind Konstanten gemeint, die das Interface ImageObserver deklariert. In diesem Fall wird das Flag ALLBITS abgefragt. Es ist gesetzt, wenn alle Pixel eines Bildes zur Verfügung stehen.
In diesem Beispiel wurde in der Schleife, in der das Programm auf die Bilddaten wartet, eine Pause eingebaut:t.sleep(50);In dieser Zeit wird die Ausführung des Threads angehalten, d. h., diese Zeit steht anderen Threads zusätzlich zur Verfügung. Macht man dies nicht, wird unnötig Rechenzeit verbraucht, und die Ausführung anderer Threads kann sich verlangsamen, ebenso wie der Ladevorgang des Bildes. Einen Verweis auf den aktuellen Thread wird von der statischen Methode currentThread() der Klasse Thread geliefert.
Außer ALLBITS besitzt der ImageObserver weitere Flags, die zusätzliche Informationen bereitstellen. Die für den Programmierer wichtigsten Flags sind:
- WIDTH
Ist gesetzt, wenn die Breite des Bildes gültig ist. Ist dies der Fall, liefert ein Aufruf von getWidth() das richtige Ergebnis.- HEIGHT
Ist gesetzt, wenn die Höhe des Bildes gültig ist. Ist dies der Fall, liefert ein Aufruf von getHeight() das richtige Ergebnis.- ALLBITS
Ist gesetzt, wenn alle Pixel eines Bildes verfügbar sind.- ERROR
Ist gesetzt, wenn ein Fehler bei der Aufbereitung aufgetreten ist.
Durch die Verwendung der Klasse MediaTracker können ebenfalls Bilder synchron in Java-Anwendungen geladen werden. Diese Methode wird in der Praxis am häufigsten angewendet. MediaTracker wird ausführlich im Abschnitt erklärt.