ゆーたんのつぶやき

株式会社ノークリサーチにてIT関連のシニアアナリストとして活動しています。

Swingでの高速描画



先日のエントリで取り上げたアイティフロンティアが開発した
XMLによるリッチクライアントレンダリング環境「xGUItar」
ですが、現時点ではSwingへのレンダリングのみサポートして
います。


Swingというと登場当初の「重い、遅い」というイメージが
いまだに根強く、現状との間に乖離があるように感じます。
ですが、1.4X以降では普通に使っている限りにおいては
他のGUIフレームワークと比べてそれほど遜色はありません。
ただし、注意しないといけないのはグラフィックコンテキスト
オブジェクトを使って自分で描画をする場合です。この場合は
以下のようにVolatileImageオブジェクトを使ってVRAMに直接
アクセスすることで高速な描画を行なうことができます。

public class VRAMImageBuffer {

  //描画対象となるコンポーネント
  private Component mComponent;
	
  //VRAMバッファ
  private VolatileImage mVolatileImage;

  //VRAMバッファの幅
  private int mWidth;
	 
  //VRAMバッファの高さ
  private int mHeight;

  //コンストラクタ
  public VRAMImageBuffer(Component cComponent){
    mComponent = cComponent;
  }

  //VRAMバッファ用イメージの初期化
  public void initVolatileImage(){

     //VRAMバッファ用イメージが未作成、もしくは現在のサイズと
     //異なっている場合は生成処理を実行する
     if(mVolatileImage == null || 
        mWidth != mComponent.getWidth() || 
        mHeight != mComponent.getHeight()){
          mWidth = mComponent.getWidth();
          mHeight = mComponent.getHeight();
          mVolatileImage = mComponent.createVolatileImage(mWidth, mHeight);	
     }
  }

  //VRAMバッファ用イメージのチェック 
  public void validateVolatileImage() {

     //グラフィックス設定情報を取得する
     GraphicsConfiguration gc = mComponent.getGraphicsConfiguration();

     //VRAMバッファ用領域に変更があった場合は再度生成処理を実行する
     if (mVolatileImage.validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE) {
        mVolatileImage = mComponent.createVolatileImage(mWidth, mHeight);
     }
  }

  //グラフィックスオブジェクトの取得
  public Graphics getGraphics(){
     return mVolatileImage.getGraphics();	
  }

  //描画したコンテンツが失われているかのチェック
  public boolean isContentsLost(){
    return mVolatileImage.contentsLost();
  }

  //VRAMバッファイメージを返す
  public Image getImage(){
    return mVolatileImage;
  }
}


public MyComponent extends JPanel{

  //VRAMバッファ
  private VRAMImageBuffer mImageBuffer;

  //コンストラクタ
  public MyComponent(){
    mImageBuffer = new VRAMImageBuffer(this);
  }

  // 画面の描画処理
  protected void paintComponent(Graphics gComponent) {

     //ダブルバッファ用イメージの初期化
     mImageBuffer.initVolatileImage();

     try{
       do{
           //ダブルバッファ用イメージのチェック
           mImageBuffer.validateVolatileImage(); --- (1)

           //バッファのグラフィックスオブジェクトを取得
           Graphics gBuffer = mImageBuffer.getGraphics();
				
           //親クラスの描画処理を呼びだす
           super.paintComponent(gBuffer);

           //ここに具体的な描画処理を入れる

           //グラフィックスオブジェクトを破棄する
           gBuffer.dispose();

           //バッファのイメージを実際に描画する
           gComponent.drawImage(mImageBuffer.getImage(),0,0,this);

        //バッファ内容が失われたら再描画させる --- (2)
       }while(mImageBuffer.isContentsLost()); 
     }
     catch(NullPointerException ex){
        //VolatileImageのアクセス中に例外が発生することがあるので、
        //ここでキャッチしておく ---(3)
     }
  }

}

上記のコードのVRAMImageBufferクラスがVolatileImageを使った例です。
実際の描画の際にはこのVRAMImageBufferをpaintComponent()メソッドの
中で呼び出します。この時にちょっと面倒なのですが、(1)と(2)のように
イメージバッファの内容を常にチェックし、サイズの変更があったりした
場合には再描画するという処理を自分で入れないといけません。さらに、
これは対処療法的なコードになってしまうのですが、VolatileImageを
参照する際に例外が発生することがあるので、(3)にこれをキャッチする
コードを入れています。(この例外自体は処理を中断するレベルのものでは
ないようです)


Swingについてはプラットフォームに依存せずに共通したルック&フィール
を実現できる点が魅力です。その分、SWT&JFaceと比較してネイティブ環境
とのやりとりなどで制限が多い面もありますが、xGUItarのように普遍的な
GUI生成環境を実現しようという場合には有効な選択肢の一つなのかも知れ
ません。