俺#

新潟市でIT業を営むおっさんのブログ。

JavaのToolkit.createImage()はbyte[]がソースでも非同期処理である

すげー久しぶりにGUIのあるJavaアプリを作っている。まぁ、他の作業を補助するためのちょっとしたツールなんだけども。正確にはケータイアプリならDoJaだのMIDPだの飽きるほどやったし、Androidアプリは現在進行形だけど、純なJava SEでGUIなんて10年以上やってないんじゃなかろうか。

Swingなんて最近の技術(ぉい)は知らんのでとりあえずAWTで。

しかし、byteの配列に格納されているJPEGファイルのイメージをImageとして読み込み画面に表示する、というググればすぐ出てきそうな簡単な部分で引っかかってしまった。情けないというかなんというか。やっぱC++Builderでやれば良かったーとか思ったけど後の祭り。

物凄く単純化するとコード的にはこんな感じ。

class ImageCanvas extends Canvas
{
 private Image image;

 private int getNextImageSize()
 {
   // どっかからjpegファイルのサイズをもってくる
 }
 
 private boolean getNextImageBytes(byte buffer)
 {
   // どっかからjpegファイルの中身をもってくる
 }

 public void showNextImage()
 {
  byte buffer = new byte[getNextImageSize()];
  if(getNextImageBytes(buffer))
  {
   image = Toolkit.getDefaultToolkit().createImage(buffer);
   repaint();
  }
 }

 public void paint(Graphics g)
 {
  g.drawImage(image, 0, 0, this);
 }
}

showNextImage()を連続で呼ぶ機構が別にあって、これでパラパラアニメが表示されるはず...だったのだが、どうも挙動が変。具体的には画面がちらつく。「ああ、ダブルバッファリングしなきゃダメか。」と思ってやってみたけどダメ。なんでよ?

答え:Toolkit.createImage()は例えbyteの配列がソースでも非同期処理だから

Appletがネットワーク越しに画像を持ってくるのに便利な仕様で「そういえばあったわ〜」って感じだが、完全に失念してた(苦笑。たまたまpaint()までに読み込みが間に合うと描画され、間に合わないと描画されない。結果画面がちらついていたワケです。

教科書通りにMediaTrackerで読込完了を待ち合わせてやっても良いのだが、今回の場合は同期処理してくれればそれで問題ないので以下のように変更して解決となった。

修正前:

   image = Toolkit.getDefaultToolkit().createImage(buffer);
   repaint();

       
修正後:

   image = ImageIO.read(new ByteArrayInputStream(buffer));
   repaint();

後者は比較的新しい機構で、同期処理でございます。んー、byte列を直接食べてくれるから、という理由でちゃんと仕様を確認せずにうろ覚えの知識で前者を使ったのが間違いだね。