Weitere aktuelle Java-Titel finden Sie bei dpunkt.
 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index


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.

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:
  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:
  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:

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.


 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index

Copyright © 2002 dpunkt.Verlag, Heidelberg. Alle Rechte vorbehalten.