Go to Previous Page Go to Contents Go to Java Page Go to Next Page
New Features of Java2 SDK, Standard Edition, v1.4
 
 

VolatileImage

 
 

VolatileImage とは

 
 

Java を使っていて、グラフィックが遅いと思ったことないですか。Java 2 になってからかなり改善はされましたが、C や C++ で書かれたアプリケーションに比べると少し見劣りがしてしまいます。

そこで、登場したのが java.awt.image.VolatileImage クラスです。

その前に、なぜグラフィックが遅いのでしょうか。

簡単にいえば、2 度書きしているからです。

まず、Java の世界の中でグラフィックを描画して、それを Windows や X Window の世界にコピーします。そしてそのイメージが実際に Windows や X Windows で描画されるのです。

そんなことしないで、いきなり Windows や X Window に書き込みにいけばいいじゃないかと思いますよね。それを実現したのが VolatileImage クラスです。

実際には Windows なら直接 VRAM に書き込みして、DirectDraw で描画します。X Window なら pixmap に書き込みます。

だから、速い描画が行えるんですね。特にダブルバッファリングの描画バッファに使用すると、効果大ですよ。

 

 
 

それでは使ってみよう

 
 

それでは、さっそく使ってみましょう。まずは単に VolatileImage を生成して、描画してみましょう。

当然のことですが、J2SDK, SE, v1.4 と Java Plug-in が必要です。

Applet の HTML VolatileImageTest1.html
Applet のソース VolatileImageTest1.java

VolatileImage を使用するには、java.awt.Component クラスの createVolatileImage メソッドを使用します。Swing のコンポーネントは java.awt.Component の派生クラスなので、同様に createVolatileImage メソッドを使用することができます。VolatieImageTest1 クラスでは VolatileImage オブジェクトの生成は initVolatileImage メソッドで行っています。

後は普通の Image クラスと扱いは同じです。

まず、init メソッドで表示するイメージを取得しています。これで、前準備はおしまい。

実際の描画を行う paint メソッドでは、initVolatileImage メソッドをまずコールします。このメソッドで VolatileImage オブジェクトが生成されていなければ、生成します。

そして、VolatileImage オブジェクトから Graphics オブジェクトを取り出し、背景を塗ってから、読み込んだ Image オブジェクト sprite を描画します。

最後にダブルバッファリングを行うために VolatileImage オブジェクトの描画行っています。

import java.awt.image.*;
import java.awt.*;
import javax.swing.*;
 
public class VolatileImageTest1 extends JApplet{
 
    private VolatileImage image;
    private Image sprite;
    private int width;
    private int height;
 
    public void init(){
        sprite = getImage(getDocumentBase(), "javacup0.gif");
    }
 
    private void initVolatileImage(){
        if (image == null || width != getWidth() || height != getHeight()) {
            width = getWidth();
            height = getHeight();
            image = createVolatileImage(width, height);  // イメージの生成
        }
    }
 
    public void paint(Graphics g){
        initVolatileImage();
 
        Graphics gImage = image.getGraphics();
        gImage.setColor(Color.yellow);
        gImage.fillRect(0, 0, width, height);
        if(sprite != null){
            gImage.drawImage(sprite, 0, 0, this);
        }
        gImage.dispose();
 
        g.drawImage(image, 0, 0, this);   // イメージの描画
    }
}

 
 

使うときの注意点

 
 

実をいうと、VolatileImageTest1 クラスには重大な欠陥があります。やっぱり、普通の Image クラスとはちょっと使用法が異なるのです。

その問題というのは、Windows では VolatileImage が書き込む先の VRAM が消えてなくなってしまうことがあるということなのです。

VolatileImage のユーザガイドには、次の時に VRAM が消えてしまうと書いてあります。

  • 他のアプリケーションがフルスクリーンモードで実行しているとき
  • スクリーンセーバが起動したとき
  • WindowsNT/200 のタスクマネージャを使用して割り込みがかかったとき
  • 画面の解像度を変更したとき

筆者はスクリーンセーバで試してみましたが、あたりまえですが、うまく動作しないことがありました。3D 迷路のスクリーンセーバではまったく動作しなくなってしまいました。

それじゃ、どうしましょう。答えは簡単で、VRAM が消えたかどうかをプログラムの中で調べればいいのです。それには validate メソッドと contentsLost メソッドを使用します。

Applet の HTML VolatileImageTest2.html
Applet のソース VolatileImageTest2.java

volatile メソッドを使用したチェックは次のようにします。

        GraphicsConfiguration gc = this.getGraphicsConfiguration();
 
        if (image.validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE) {
            image = createVolatileImage(width, height);
        }

まず、Component クラスの getGraphicsConfiguration メソッドを使用し、それを validate メソッドの引数にします。GraphicsConfiguration クラスは表示を行うデバイスの特性を表します。例えば、解像度とか色数などによって GraphicsConfiguration オブジェクトは異なるわけです。

validate メソッドは VolatileImage を作ったときと、現在の状態で表示を行うデバイスの状態が変わったかどうかを調べます。戻り値は次の 3 種類です。

  • VolatileImage.IMAGE_INCOMPATIBLE
  • VolatileImage.IMAGE_OK
  • VolatileImage.IMAGE_RESTORED

この中で、IMAGE_INCOMPATIBLE であった場合は、VolatileImage オブジェクトを生成しなおさなければなりません。

contensLost メソッドは文字通り VolatileImage オブジェクトが描画する VRAM が消えてしまったかどうかを調べます。使い方は次の通りです。

    public void paint(Graphics g){
        do { 
            GraphicsConfiguration gc = this.getGraphicsConfiguration();
 
            if (image.validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE) {
                image = createVolatileImage(width, height);
            }
    
            // 描画処理
 
            g.drawImage(image, 0, 0, this);
        } while (image.contentsLost());
    }

validate メソッドを使用してチェックをした後、何らかの描画処理を行います。そして、drawImage メソッドで描画します。

この後にもし VRAM が消失していたら、もう一度描画のやり直しをします。

もう一点注意しなくてはいけないのが、VolatileImage オブジェクトに対して getGraphics メソッドを使用すると NullPointerException が発生することがあることです。

結局、VolatileImageTest2.java の paint メソッドは次のようになりました。validate メソッドの部分はメソッドにしています。

    private void validateVolatileImage() {
        GraphicsConfiguration gc = this.getGraphicsConfiguration();
 
        if (image.validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE) {
            image = createVolatileImage(width, height);
        }
    }
 
    public void paint(Graphics g){
        initVolatileImage();     // VolatileImage が null ならば生成
 
        try{
            do {
                validateVolatileImage();
                Graphics gImage = image.getGraphics();
                gImage.setColor(Color.yellow);
                gImage.fillRect(0, 0, width, height);
                if(sprite != null){
                    gImage.drawImage(sprite, 0, 0, this);
                }
                gImage.dispose();
                g.drawImage(image, 0, 0, this);
            } while (image.contentsLost());
        }catch(NullPointerException ex){}
    }

 

 
 

実験、実験

 
 

さて、VolatileImage クラスを使うとどのくらい描画が早くなるのでしょうか。VolatileImage クラスと単なる Image クラスを使用して比較してみました。

Applet の HTML VolatileImageTest3.html
Applet のソース VolatileImageTest3.java

普通、アニメーションを行うときは、ある程度スリープを入れるのですが、ここでは入れていません。時々止まったりするように見えるのはこのせいです。本当はコーヒーカップは回転しているのですけど、ほとんど見えないですね。

描画を 500 回するごとに、1 秒間に何回描画を行えたかをアプレットの下部に表示させて見ました。

筆者の環境は

  • AMD Athron 800MHz
  • Memory 512 MB
  • Windows 200 SP2
  • Netscape 6.1 PR1

です。これでだいたい

使用したクラス 描画数 / 秒
VolatileImage 270 から 350
Image 70 から 100

となりました。3 倍から 4 倍程度速くなっています。

もうひとつ、テストプログラムを作ってみました。

ソース VolatileImageTest4.java

これはアプレットではなくアプリケーションなので、ダウンロードして実行してみてください。

こちらは同じプログラムの中で VolatileImage と Image を使って、イメージの描画を 500 回行ったときの時間を計測します。

筆者の環境で試してみた結果は次の通りになりました。

使用したクラス 時間 [ms]
VolatileImage 1172
Image 5367

約 5 倍も違いました。

簡単に使えて、効果の大きい VolatileImage をぜひぜひお試しください。特にダブルバッファリングには効果が大きいですよ。

 
 

おまけ

 
 

VolatileImage クラスを明示的に使わなくても Java2 SE v1.4 にしてみるとグラフィックのパフォーマンスがずいぶん向上しているのが分かります。特に Swing は顕著です。

Swing はデフォルトでダブルバッファリングを行うのですが、そのバッファに v1.4 では VolatileImage が使われています。だから、なにもしなくても早くなるんです。

それ以外にもパイプライン処理などいろいろなところに手を加えられているようです。そのため、既存のアプリケーションも v1.4 で動作させるともれなくパフォーマンスが向上しますよ。

今回使用したサンプルはここからダウンロードできます。画像ファイルもここに含まれています。

参考 URL

(Jun. 2001)

 
 
Go to Previous Page Go to Contents Go to Java Page Go to Next Page