イメージオブザーバ
描画のタイミング指定
前回は、画像を出力するための表面的な動作を覚えました
しかし、イメージオブザーバの謎を残したままです
イメージプロデューサやイメージコンシューマについては、追々話していきます
ですが、イメージオブザーバの理解は必要なので、ここでしっかりと基盤を身につけましょう
java.awt.ComponentクラスがImageObserverインタフェイスを実装しているのは話しました
ImageObserverは、複数の定数とそれを操作するためのimageUpdate()メソッドが宣言されています
public abstract boolean imageUpdate( Image img, int infoflags, int x, int y, int width, int height )
このメソッドの実装はComponentクラスで行われてます
imgは監視するイメージ、infoflagsがインタフェイスで定義されている定数
残りは座標とサイズです
ImageobserverインタフェイスのimageUpdate()は、更新情報が必要ならばtrueを、そうでない場合はfalseを返します
drawImage()メソッドはイメージプロデューサーに指定のイメージオブザーバを登録します
ここではじめて、画像の参照を読み込み始めます
すると、イメージオブザーバは画像のロード毎にimageUpdate()を呼び出します
imageUpdate()メソッドはロードされた画像のビットを受け取りrepaint()を呼び出します
repaint()はupdate()を呼び出し、結果としてpaint()メソッドが再描画され
現在ダウンロードされた、可能な限りの画像を表示します
drawImage()とimageUpdate()の過程をプロンプトを使って押さえてみましょう
その流れがわかるはずです
import java.applet.Applet;
import java.awt.*;
/* <applet code="test.class" width="300"height="300">
<param name="img" , value="img/test.jpg">
</applet>
*/
public class test extends Applet {
Image img;
public void init() {
img = getImage(getDocumentBase() , getParameter("img"));
}
public void paint(Graphics g) {
System.out.println(g.drawImage(img , 0 , 0 , this));
}
}
このプログラムをアプレットビューワで実行してください
大体、次のような結果が標準出力に出力されるはずです
false
false
false
false
false
false
false
false
true
正確な結果は状況によって異なりますが
おおよそこれに類似した結果が得られます
画面の読み込み途中に、なんどもpaint()メソッドが呼び出され再描画されているのが明確にわかります
さらに、アプレットビューワを別のウィンドウで隠すなどして、再び画面に出すとpaint()が呼び出されましたね
falseが出力されるということは、つまり読み込み途中でもできるところまでは出力しているということです
これが、非同期的なロードというわけです
イメージプロデューサが画像の次のビットを読み込み、イメージオブザーバのimageUpdate()を呼び出し
imageUpdate()がrepaint()をよびだして、結果としてコンポーネントが更新される
この過程を、イメージプロデューサが読み込みを終了するまで続けられるのです
imageUpdate()を操作する
ComponentクラスのimageUpdate()がrepaint()を呼び出していることがわかりました
ということは、imageUpdate()をオーバーライドすることで制御することができます
imageUpdate()メソッドは、現状を知るための定数を受け取ります
この定数はImageObsarverインタフェイスで定義されています
- ABORT : 非同期で読み込んでいた画像の完成前に生成処理が中止された
- ALLBITS : 追跡していた画像のイメージが完全に生成された
- ERROR : 追跡中の画像にエラーが発生し、これ以上読み込めない。同時にABORTをセット
- FRAMEBITS : 追跡していたマルチフレーム イメージの別の完了フレームを再描画できる
- HEIGHT : imageUpdate()のheightから値を取得できる
- PROPERTIES : イメージのプロパティが使用できる
- SOMEBITS : スケーリングに必要な追加ピクセルを使用できる
- WIDTH : imageUpdate()のwidthから値を取得できる
とりあえず、この場ではALLBITSだけを覚えておけば良いと思います
imageUpdate()をオーバーライドし、repaint()のタイミングを制御することで
プログラマの任意のタイミングで描画することができます
次のプログラムは代表的なイメージオブザーバの制御ですが
imageUpdate()をオーバーライドし、画像を全て読み込んでから描画します
imageUpdate()は、さらに画像を読み込む必要がある時はtrue、画像の生成が終了した場合はfalseを返します
この判定は、imageUpdate()が受け取るImageobserverインタフェイスの定数で判断します
import java.applet.Applet;
import java.awt.*;
/* <applet code="test.class" width="300"height="300">
<param name="img" , value="img/test.jpg">
</applet>
*/
public class test extends Applet {
Image img;
public void init() {
img = getImage(getDocumentBase() , getParameter("img"));
}
public void paint(Graphics g) {
System.out.println(g.drawImage(img , 0 , 0 , this));
}
public boolean imageUpdate(Image img , int flg , int x , int y , int w , int h) {
if (flg == ALLBITS) {
repaint();
return false;
}
else return true;
}
}
このプログラムを実行すると、プロンプトにはfalseが一度しか出てこないはずです
最初のfalseは、アプレットが実行時にpaint()を呼び出した時のdrawImage()です
それ以降は、imageUpdate()が呼び出されるたびに渡された定数を評価します
渡された定数flgがALLBITSと等しければ、つまりイメージ全体の生成が終了すればrepaint()を呼び出し
そうでなければ何もせずにさらに読み込みを続けます
imageUpdate()の定数の比較は、次のように論理演算子を用いてもかまいません
通常、定数は複数の定数と比較したりすることもあるため論理演算子を使ったほうが良いです
if ((flg & ALLBITS) == 0) return true;
else {
repaint();
return false;
}
さらにimageUpdate()の仕様を詳しく見てみましょう
イメージプロデューサが画像を読み込んでimageUpdate()を呼び出すことはわかりましたが
実際にJavaはどのように画像を読み込んでいるのでしょうか
これは、imageUpdate()が呼び出された時に渡されるイメージのサイズを調べればわかります
つまり、パラメータのx,y,width,heightの部分です
imageUpdate()が呼び出されるごとに、標準出力にイメージの内容を出力しましょう
そうすれば、画像のどの部分が渡されているのかわかるはずです
import java.applet.Applet;
import java.awt.*;
/* <applet code="test.class" width="300"height="300">
<param name="img" , value="img/test.jpg">
</applet>
*/
public class test extends Applet {
Image img;
public void init() {
img = getImage(getDocumentBase() , getParameter("img"));
}
public void paint(Graphics g) {
g.drawImage(img , 0 , 0 , this);
}
public boolean imageUpdate(Image img , int flg , int x , int y , int w , int h) {
System.out.println("x = " + x + " : y = " + y + " : w = " + w + " : h = " + h);
if ((flg & ALLBITS) == 0) return true;
else {
repaint();
return false;
}
}
}
このプログラムを実行すると、標準出力にずら〜〜〜〜っと出力されます
ちょっと長いですけど、興味深い結果だと思います
結果を見ると、imageUpdate()は走査線ごとに呼び出されているのがわかります
画像の始点から縦に1ずつ横の最大サイズまでを読み取っていますね
これをyが画像の最大縦サイズになるまで繰り返し
全ての生成作業が終わると全体を表示させることができるようになります