バッファリング

Since 2003.05.21  

画面のちらつき

前回作った「Star4.java」は、星の輝きの雰囲気が出ていると思います。そして、よく見ていると「またたき」の感じもあるようです。
しかし、「またたき」感はプログラムしたものではありません。
ためしに、HTMLファイルをこのページの下のテストコードように修正し、ゆっくりと動かしてみます。
時折、画像の更新が引っかかった感じで止まるときがあるはずです。
ご存知のように、ディスプレイ画像は秒あたり数十回の頻度で更新されます。paintメソッドの呼び出しがこの画像更新とぶつかったときに、paintメソッドの実行が中止されることがあるのが「またたき」感の原因です。
今回は、コードの欠点が、プログラムの完成度の高さのように見える逆説的な現象であるに過ぎません。もう少し大きな領域を使ったアプレットの場合は、欠点が「ちらつき」となって表面化します。

バッファリング

ちらつきを生じさせないようにしたり、抑えたりする手法は、「バッファリング」と呼ばれます。色々な実現方法がありますが、大きく次の2種類に分けられます。

  1. ビデオメモリ上に2以上の領域を確保し、表示と描画処理とに切り替えて使用する方法
  2. メインメモリ上に領域を確保し、描画処理はメインメモリに対して実行し、描画処理が終わると、一気にビデオメモリに転送する方法

但し、間にOSが介在しますので、ビデオメモリに書き込むつもりが、OSの用意したメインメモリに書いていたりします。上の図の表示のところが実際のビデオメモリということで、もう一段転送があるということです。
Java1.2 以降で追加されたSwingパッケージ群では、標準でこのバッファリングを使用しますが、ここでは、プログラムの中に記述することにします。
方式は、下側のイメージになります。Star4.java のpaintメソッドの中身をそっくり新しく定義したoffPaintメソッドに移しただけです。

AWTとSwing

Java で GUI をプログラムするときには、AWTパッケージ群とSwingパッケージ群を使います。両者の関係は、次のようなものです。

AWTパッケージ群 基本的なGUI機能の提供
OSのGUIシステムをそのまま使用、そのため、OSによって見え方が異なる
Swingパッケージ群 強力で高度なGUI機能の提供
プラットフォーム独立で、OSが異なっても、「同じ見え方」になる

両方のパッケージ群を使えば、自在なプログラム作成が可能なのですが、世の中に流通しているアプレットは、AWT しか使っていません。
理由は、Windowsの世界で圧倒的に使われているInternetExplorer が実装しているJavaVMが 1.1.4 という古いバージョンで Swing をサポートしていないからです。ようやく一部のコンピュータメーカーが JavaVM1.4 をプレインストールして出荷するようになったという状況で、「使いたいけど使えない」というのが実情です。
最近、アプレットよりサーブレットが注目されるのは、これが原因でしょう。サーブレットは、昔ながらのサーバーサイドコンピューティングの1変種に過ぎないと思うのですが。残念なことです。

またたき、ゆらぎ

改良したコードは、正確に表示します。そうすると表示が平板になった感じがします。その点をさらに工夫したプログラムを、完成形として、「Star.java」という名前で作ってみました。「ゆらぎ」までは入れていませんので、さらに皆さんで改良してみてください。
コードは、ほとんど重複するので表示しません。ダウンロードしてください。java/net/sys5jp/basic/Star.java

IEのJavaVM が古いことに言及したところで、更に付け加えれば、pngファイルは、IE6.0 では、画像としては表示できますが、Java に使用する画像ファイルとしては使えません。JavaVM1.4 をインストールしていないIEでは、Star1.html〜Star5.htmlは表示されません。Java1.1 で扱える画像ファイルは JPEG と GIF の2種類です。
参考のために GIF ファイルを置いておきます。JavaVM1.4 をオフにして試してみてください。
HTMLファイルを右クリックでダウンロード保存してください。クリックしても表示できません。→ java/html/Star.html

Star30.gif Plus30.gif Cross30.gif

JavaVM1.4 のオフは、IEのメニューから、ツール(T)→インターネットオプション(O)

テストコード

java/html/Star4a.html

<html>
<head>
<style>
body {background-color:rgb(0, 0, 0);
    color:rgb(255, 255, 255)}
table {text-align:center;
    margin:10px 10px}
td {padding:5px 10px}
</style>
</head>
<body>
<table>
<tr><td>
<applet code="net/sys5jp/basic/Star3.class"
    codebase="../"
    width=30 height=30>
        <param name="starimage" value="../images/Star30.png">
        <param name="speed" value="1">
</applet>
</td><td>
<applet code="net/sys5jp/basic/Star4.class"
    codebase="../"
    width=30 height=30>
        <param name="starimage" value="../images/Star30.png">
        <param name="plusimage" value="../images/Plus30.png">
        <param name="crossimage" value="../images/Cross30.png">
        <param name="speed" value="1">
</applet>
</td></tr>
<tr><td>
Star3.class
</td><td>
Star4.class
</td></tr>
</table>
</body>
</html>

ソースコード

java/net/sys5jp/basic/Star5.java

1 package net.sys5jp.basic;
2
3 import java.applet.*;
4 import java.awt.*;
5
6 public class Star5 extends Applet implements Runnable {
7     boolean runnable = true;
8     boolean running = false;
9     Thread timer;
10     int maxstage = 3;
11     int stage = 0;
12     long sleep = 50;
13     Color backColor = new Color(0, 0, 0);
14     Image offImage;
15     Image starImage;
16     Image plusImage;
17     Image crossImage;
18     Image backImage;
19     Dimension dim;
20
21     private Color getColorParameter(String param) {
22         String value = getParameter(param);
23         try { return new Color(Integer.parseInt(value, 16)); }
24         catch(Exception e) { return null; }
25     }
26
27     private int getIntParameter(String param) {
28         String value = getParameter(param);
29         try { return Integer.parseInt(value, 10); }
30         catch(Exception e) { return 0; }
31     }
32
33     public void offPaint() {
34         Graphics g = offImage.getGraphics();
35         g.drawImage(backImage, 0, 0, this);
36         switch (stage) {
37             case 0:
38                 break;
39             case 1:
40                 g.drawImage(crossImage, 0, 0, this);
41                 break;
42             case 2:
43                 g.drawImage(plusImage, 0, 0, this);
44                 break;
45             default:
46                 g.drawImage(starImage, 0, 0, this);
47                 break;
48         }
49     }
50
51     public void paint(Graphics g) {
52         if(offImage != null) {
53             g.drawImage(offImage, 0, 0, this);
54         }
55     }
56
57     public void init() {
58         Color color;
59         if((color = getColorParameter("backcolor")) != null) {
60             backColor = color;
61         }
62         try { starImage
63                 = getImage(getDocumentBase(), getParameter("starimage")); }
64         catch(Exception e) { runnable = false; }
65         try { plusImage
66                 = getImage(getDocumentBase(), getParameter("plusimage")); }
67         catch(Exception e) { runnable = false; }
68         try { crossImage
69                 = getImage(getDocumentBase(), getParameter("crossimage")); }
70         catch(Exception e) { runnable = false; }
71         int cc;
72         if((cc = getIntParameter("speed")) > 0) {
73             sleep = 1000 / cc;
74         }
75         setBackground(backColor);
76         dim = getSize();
77         backImage = createImage(dim.width, dim.height);
78         Graphics bg = backImage.getGraphics();
79         bg.setColor(backColor);
80         bg.fillRect(0, 0, dim.width, dim.height);
81         offImage = createImage(dim.width, dim.height);
82     }
83
84     public void run() {
85         while(running) {
86             offPaint();
87             repaint();
88             if (stage >= maxstage) { stage = 0; }
89             else { stage++; }
90             try { Thread.sleep(sleep); }
91             catch (InterruptedException e) {}
92         }
93         timer = null;
94     }
95
96     public void start() {
97         if (runnable) {
98             running = true;
99             if (timer == null) {
100                 timer = new Thread(this);
101                 timer.start();
102             }
103         }
104     }
105
106     public void stop() { running = false; }
107
108 }

java/html/Star5.html

<html>
<head>
<style>
body {background-color:rgb(0, 0, 0);
    color:rgb(255, 255, 255)}
table {text-align:center;
    margin:10px 10px}
td {padding:5px 10px}
</style>
</head>
<body>
<table>
<tr><td>
<applet code="net/sys5jp/basic/Star3.class"
    codebase="../"
    width=30 height=30>
        <param name="starimage" value="../images/Star30.png">
        <param name="speed" value="5">
</applet>
</td><td>
<applet code="net/sys5jp/basic/Star4.class"
    codebase="../"
    width=30 height=30>
        <param name="starimage" value="../images/Star30.png">
        <param name="plusimage" value="../images/Plus30.png">
        <param name="crossimage" value="../images/Cross30.png">
        <param name="speed" value="5">
</applet>
</td><td>
<applet code="net/sys5jp/basic/Star5.class"
    codebase="../"
    width=30 height=30>
        <param name="starimage" value="../images/Star30.png">
        <param name="plusimage" value="../images/Plus30.png">
        <param name="crossimage" value="../images/Cross30.png">
        <param name="speed" value="5">
</applet>
</td></tr>
<tr><td>
Star3.class
</td><td>
Star4.class
</td><td>
Star5.class
</td></tr>
</table>
</body>
</html>

トップヘ 目次ヘ 前ヘ