Nanashi-softプログラマ専用BD-J


◇BD-J-画像表示-

Javaには標準で PNG,Jpeg,GIF画像を読み込む機能を内蔵しています
それも、ファイル名を指定するだけで自動的に認識してくれるので、プログラム上は何も考える必要はありません

BD-J固有の話としては、Imageとして読み込まれた画像は、リソースとは別のメモリ空間に格納されます
リソース12MB内の計算に画像サイズは含めなくて良いです
但し、どのくらいのメモリがあるのかを知る手段が無い為、できる限り最小に留めるのが良いでしょう

○画像表示ができないソース(ぉ

まずは、ダメな例から書いておきます

public void paint(Graphics g){
  Image img= Toolkit.getDefaultToolkit().getImage("gazou.png");
  g.drawImage(img, 0, 0, this);
}

この imgの内容を drawStringして中身を見ると、
com.ibm.oti.pbpui.awt.impl.Imageimpl@(8ケタのコードらしきもの)
が表示される(謎

これがダメな理由は2つあります
1つは、jarファイル内から読み込む場合は、リソースとして起動クラスからの相対パスでファイル名を指定しなければならない点
jarファイルにした時点であらゆるパスが無効になるのです

Image img= Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("gazou.png"));


もう1つは、Toolkitはスレッド実行なので、読み込みが完了するのを待ってから、drawImageする必要がある点
MediaTrackerを使って、終了を待ちます

MediaTracker tracker= new MediaTracker(this);
tracker.addImage(img, 0);
try{
  tracker.waitForAll();
}
catch (Exception e){
}

○画像表示ソース

public void paint(Graphics g){
  Image img= Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("gazou.png"));

  MediaTracker tracker= new MediaTracker(this);
  tracker.addImage(img, 0);
  try{
    tracker.waitForAll();
  }
  catch (Exception e){
  }

  g.drawImage(img, 0, 0, this);

  img.flush();  //機種によってはマズいかも知れません
}

○画像表示ソース2

通常は、読み込んでから paintメソッド内で表示する
プログラム全体で見ると、こんな感じになる

package org.homebrew;

import javax.media.*;
import java.awt.*;
import java.awt.event.*;
import javax.tv.xlet.*;
import org.havi.ui.*;

public class MyXlet extends Component implements Xlet, KeyListener{
  private HScene hs;

  //グローバル変数に画像変数を宣言
  private int readflag= 0;
  private Image img1, img2;

  public void initXlet(XletContext context){
    hs= HSceneFactory.getInstance().getFullScreenScene(HScreen.getDefaultHScreen().getDefaultHGraphicsDevice());
    setBounds(hs.getBounds());
    hs.add(this);
    hs.setVisible(true);
    requestFocus();

    //予め画像を読み込んでおく
    MediaTracker tracker= new MediaTracker(this);
    Image img1= Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("haikei.jpg"));
    Image img2= Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("jinbutu.png"));
    tracker.addImage(img1, 0);
    tracker.addImage(img2, 1);
    try{  tracker.waitForAll();  }catch (Exception e){}
    readflag= 1;
  }

  public void startXlet(){
  }

  public void pauseXlet(){
  }

  public void destroyXlet(boolean unconditional){
  }

  public void paint(Graphics g){
    g.setColor(new Color(0, 0, 0));
    g.fillRect(0, 0, getWidth(), getHeight());

    //プログラム実行と同時にpaintスレッドも開始されるので、読み込み完了を待つ
	if(readflag == 1){
      g.drawImage(img1, 0, 0, this);
      g.drawImage(img2, 0, 0, this);
    }
  }

  public void keyPressed(KeyEvent evt){
  }

  public void keyReleased(KeyEvent e){
  }

  public void keyTyped(KeyEvent e){
  }
}

○メモリーリーク問題

恐らく PS3の BD-J限定の問題だと思いますが、メモリーリークの危険性があります
リソースから読み込んだ画像は、Imageクラスが消滅してもメモリ上に残ります
それを回避する為には、明示的に flushする必要があります

try{
  Image img= Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("gazou.png"));
  MediaTracker tracker= new MediaTracker(this);
  tracker.addImage(img, 0);
  tracker.waitForAll();
  g.drawImage(img, 0, 0, this);
  img.flush();  //明示的にイメージを消去する必要がある
}
catch (Exception e){
}

この flushを記述しなかった場合、同一ファイルであればメモリ上に残ったデータが読み込まれます
異なる画像データを読み込み続けた場合、全てが消えずにメモリ上に残り続けて、やがて画像が読み込まれなくなります
この際、try~catchでも捕らえる事ができず、Imageも NULLでは無い為、プログラムで検知できない状態に陥ります

注意すること
・ローカル変数に確保した Imageクラスは、スコープ内で確実に flushしなければならない
・同一 Imageクラスに別の画像を入れる前に、確実に flushしなければならない

ブログのコメントで、drawImageは非同期メソッドだと言う書き込みがありました。もしそうなら、ここでflushするとマズい事になります
PS3は家電で組み込み系に位置するので必ずしもJava仕様と一致するとは限らないのでこれで良いですが、他に流用はしない方が良いでしょう

○BMPファイル

Java標準ではなぜか対応していないが、PS3 BD-Jでは読み込める
しかし、BDレコーダー(Panasonic製)では読めなかったので、機種依存コードになってしまう
PNGも可逆圧縮なので、PNGファイルを使うようにしよう


TOPプログラマ専用BD-J