WisdomSoft - for your serial experiences.

3.4 イメージ

アプレットから画像ファイルを読み込んで画面上に描画します。

3.4.1 画像を取得する

線を引いたり塗りつぶしたりという作業で図形を描画することができますが、写真のような画像を表示することはできません。これまでの図形は座標点とサイズ、そしてそれらの情報からどのように図を描くのかを表す描画特性で構成されました。しかし、写真の場合は頂点ではなく 1 画素単位の色情報で構成しなければなりません。つまり、ピクセルごとの色情報を含むデータです。このようなデータをビットマップと呼ぶこともあります。

Windows では bmp、ネットワークでは jpeg や png、gif などの形式でイメージデータが交換されていますが、Java プラットフォームではこうしたデータの形式に依存することはできません。そこで Java ではイメージを抽象的に扱えるように、全てのグラフィカルイメージの抽象スーパークラスとなる java.awt.Image クラスを定めています。

 java.awt.Image クラス
java.lang.Object
  |
  +--java.awt.Image
public abstract class Image extends Object

イメージの制御や交換は、この Image 抽象クラスを用いて行われます。実は、AWT や Java 2D のイメージ処理のデザインは極めて煩雑です。具体的な処理ではイメージプロデューサ java.awt.image.ImageProducer インタフェースとイメージコンシューマ java.awt.image.ImageConsumer インタフェースに分割されています。イメージはイメージを生成するイメージプロデューサを提供し、イメージプロデューサはイメージコンシューマにデータを転送します。つまり、イメージを生成するのはイメージプロデューサの役割で、イメージを表示するのはイメージコンシューマの役割となるのです。

これらの作業は全てインタフェース同士で行われるため、完全に作業は抽象化されています。独自のデータ生成方式や、データ表示を行う場合はこれらのインタフェースを実装する必要がありますが、単純にファイルからイメージを表示する場合は、これらの作業自体を隠蔽することができます。

Image クラスは抽象クラスなのでコンストラクタからオブジェクトを生成することはできません。アプレットであれば Applet クラスの getImage() メソッドを使って、ネットワーク上のイメージファイルから Image オブジェクトを生成することができます。

Applet クラス getImage() メソッド
public Image getImage(URL url)
public Image getImage(URL url, String name)

name を指定する場合は url にベースとなる URL を指定し、name に url から相対パスでファイル名を指定します。このメソッドは指定したファイルからイメージを生成して Image オブジェクトを返します。このメソッドがロードできる画像ファイル形式は GIF、JPEG、PNG のいずれかです。

驚くべきことに、イメージファイルの大きさやネットワークの回線速度を問わず getImage() メソッドは一瞬で処理を返します。getImage() メソッドは画像をロードする準備をするだけで、実は画像の読み込みは非同期的に行われるのです。非同期に画像が読み込まれるというのは、ブラウザが HTML ドキュメントの画像を表示するメカニズムに似ています。画像を全て読み込むまで待機し、その後に描画するという場合、ユーザーは画像を読み込んでいる間真っ白な作業領域を見ながらため息をつくことでしょう。しかし、非同期に、読み込まれた部分までの画像を描画すれば、ユーザーは画像が今、どの程度読み込まれているかをリアルタイムで、直観的に認識することができます。実際に画像がメモリに読み込まれるのは、イメージプロデューサにデータの送信を要求した時になります。

イメージを読み込むときは URL クラスでパスを指定することになりますが、多くのケースでは相対的にパスを指定する方法が好ましいと考えられるでしょう。アプレットであれば、アプレットが実行されているサーバーのディレクトリからの相対アドレスで画像ファイルを指定できると便利です。実行中のアプレットが配置されているアドレスは Applet クラスの getCodeBase() メソッドから、アプレットを実行している HTML ドキュメントのアドレスは getDocumentBase() メソッドから取得することができます。

Applet クラス getDocumentBase() メソッド
public URL getDocumentBase()
Applet クラス getCodeBase() メソッド
public URL getCodeBase()

これらの関数が返す URL オブジェクトを 2 つ引数を受け取る getImage() メソッドの url に指定すれば、name 引数に相対パスを指定するだけで、ネットワーク上に配置されたアプレットから相対パスを指定することができます。

3.4.2 イメージの描画

Image クラスはシステム上の実体イメージを完全に抽象化しているため、イメージのピクセルに直接アクセスするようなメソッドは提供していません。イメージのピクセルを生成するのはイメージプロデューサの役割です。Image クラスが提供するメソッドの中で特に重要なのは、イメージの幅と高さを返すメソッドです。イメージの幅を得るには getWidth() メソッド、高さを得るには getHeight() メソッドを使います。

Image クラス getWidth() メソッド
public abstract int getWidth(ImageObserver observer)
Image クラス getHeight() メソッド
public abstract int getHeight(ImageObserver observer)

ここで気になるのは、これらのメソッドが引数として受け取る ImageObserver 型の存在です。observer パラメータには java.awt.image.ImageObserver インタフェースを実装するオブジェクトを渡します。ImageObserver は イメージプロデューサ、イメージコンシューマに連なるイメージの実体を隠蔽するイメージオブザーバと呼ばれるインタフェースです。

イメージオブザーバの役割は、イメージプロデューサの作業を監視することです。イメージプロデューサがイメージの生成を開始すると、イメージオブザーバがイメージの状態などをチェックし、イメージの生成作業を続けるかどうかなどをイメージプロデューサに指示します。

イメージオブザーバを独自に実装する必要は、基本的にありません。全てのコンポーネントのスーパークラスである Component クラスがイメージオブザーバを実装しているからです。イメージを描画するほとんどのアプリケーションは Component オブジェクトを保有していることでしょう。非同期によるイメージ制御では、頻繁にイメージオブザーバが使われるので、イメージオブザーバの標準的な実装が Component クラスだと覚えてください。

無事に Image オブジェクトを取得することができれば、後は描画するだけです。イメージを描画するには Graphics クラスの drawImage() メソッドをつかいます。

Graphics クラス drawImage() メソッド
public abstract boolean drawImage(Image img, int x, int y, ImageObserver observer)
public abstract boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)

img パラメータに描画する Image オブジェクトを指定し、x パラメータと y パラメータにイメージを描画する座標を指定します。observer パラメータには、イメージを描画する際、イメージプロデューサのデータ生成を監視するイメージオブザーバを指定します。

描画するイメージに対して幅 width パラメータと高さ height パラメータを指定する drawImage() メソッドの場合は、Image オブジェクトの本来のサイズではなく、指定した幅と高さに伸縮します。

コード1
import java.applet.Applet;
import java.awt.*;

//<applet code="Test.class" width="400" height= "400"></applet>

public class Test extends Applet {
	private Image image;
	public void init() {
		image = getImage(getDocumentBase() , "test.jpg");
	}

	public void paint(Graphics g) {
		g.drawImage(image , 0 , 0 , this);
	}
}
実行結果
コード1 実行結果

コード1はアプレットが実行されている HTML ドキュメントと同じディレクトリに配置されている test.jpg というファイルを描画するアプレットです。実行図を見てのとおり、アプレットに読み込んだイメージが正しく描画されています。

Image オブジェクトの一部分だけを描画したければ、次のメソッドをつかいます。

Graphics クラス drawImage() メソッド
public abstract boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer)

dx1 パラメータと dy1 パラメータにはイメージを描画する最初の点を、dx2 パラメータと dy2 パラメータにはイメージの矩形の2番目の点を指定します。これはサイズではなく座標店であることに注意してください。2 番目の点が最初の点よりも左上の座標に位置する場合は画像が伸縮回転します。そうでなければ通常の伸縮と同じです。

sx1 パラメータと sy1 パラメータには元の Image オブジェクトの描画する最初の点、dx2 パラメータと dy2 パラメータには Image オブジェクトの 2 番目の点を指定します。これらの引数で元の Image オブジェクトのイメージの特定部分を抽出することができます。

コード2
import java.applet.Applet;
import java.awt.*;

//<applet code="Test.class" width="400" height= "400"></applet>

public class Test extends Applet {
	private Image image;
	public void init() {
		image = getImage(getDocumentBase() , "test.jpg");
	}

	public void paint(Graphics g) {
		g.drawImage(image , 0 , 0 , 300 , 200 , 30 , 10 , 200 , 120 , this);
	}
}
実行結果
コード2 実行結果

コード2は先程と同じ test.jpg ファイルの画像の一部分だけを切り抜いて、アプレット全体に伸縮して表示しています。このように、画像の源泉である Image オブジェクトを操作しなくても、drawImage() メソッドで画像の一部分だけを表示したり、Image オブジェクトを伸縮して表示することができます。