- PR -

AWT で再描画すべき領域が L 字型だと paint メソッドが2回呼ばれてしまう

投稿者投稿内容
Kissinger
ぬし
会議室デビュー日: 2002/04/30
投稿数: 428
お住まい・勤務地: 愛知県
投稿日時: 2003-06-30 22:26
提案しておいて覆すようですが、オフスクリーンバッファについては
解決方法の1つでしかありません。
領域が広い場合には、不適切なこともあります。
(unibonさんのような工夫ができないばあい。たとえば、スクロール処理
 の影響がある場合など。)

CAD図面のように非常に広い領域を使う場合には、バッファを使うよりはむ
しろ、いちいち演算したほうが良いこともあります。
ただし全領域を処理しようとするのではなく、各要素を再描画する必要が
あるかチェック(例えば,java.awt.Rectangleクラスの contains()を使用)
することによって処理するかどうか判断します。

contains()を全て実行すると、それ自体が新たな負荷を発生するように
思われますが、大抵の CADや Drawツールでは要素を合成して部品化する
機能を使用できるので、合成された単位で比較すれば判定の回数も減らす
ことができます。
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2003-07-01 18:58
unibon です。こんにちわ。

引用:

Kissingerさんの書き込み (2003-06-30 22:26) より:
提案しておいて覆すようですが、オフスクリーンバッファについては
解決方法の1つでしかありません。
領域が広い場合には、不適切なこともあります。
(unibonさんのような工夫ができないばあい。たとえば、スクロール処理
 の影響がある場合など。)


ありがとうございます。
今回の私の場合は、paint が2回呼ばれることで描画の処理時間が2倍になるのさえ
とりあえず避けられれば良いと考えていたため、
拙いですがつぎのサンプルのように見えている部分(VisibleRect)だけを
オフスクリーンバッファにしました。
バッファと言うよりキャッシュに近いですが。
これだと、すべての領域(10000×10000ピクセル)をバッファリングしていないため、
コンポーネントをスクロールしたりリサイズしたりすると描画(draw)し直しは生じますが、
これは割り切っています。

サンプルは、だだっ広いコンポーネントに斜線を書き、
そのコンポーネントがスクロールペインに入っているものです。
コード:
import java.lang.ref.*;
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;

public class JPainter extends JPanel {

    private Rectangle cacheRect = null;
    private Reference cacheRef = new SoftReference(null);

    public void paint(Graphics panelGraphics) {
        Rectangle visibleRect = getVisibleRect();
        Image cacheImage = (Image) cacheRef.get();
        if (cacheImage == null || !visibleRect.equals(cacheRect)) {
            cacheRect = visibleRect;
            cacheImage = createImage(cacheRect.width, cacheRect.height);
            cacheRef = new SoftReference(cacheImage);
            Graphics cacheGraphics = cacheImage.getGraphics();
            cacheGraphics.translate(-cacheRect.x, -cacheRect.y);
            cacheGraphics.setColor(Color.blue);
            cacheGraphics.drawLine(0, 0, getWidth(), getHeight());
            cacheGraphics.dispose();
        }
        panelGraphics.drawImage(cacheImage, cacheRect.x, cacheRect.y, this);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        JPanel panel = new JPainter();
        panel.setPreferredSize(new Dimension(10000, 10000));
        JScrollPane scrollPane = new JScrollPane(panel);
        Container contentPane = frame.getContentPane();
        contentPane.add(scrollPane);
        frame.setSize(new Dimension(400, 300));
        frame.setVisible(true);
    }
}


なお、このサンプルは AWT ではなく Swing 用ですが、
これはただ単に JComponent クラスの getVisibleRect メソッドに相当するものが、
AWT にはなさそうだったためです
(AWT でも getVisibleRect メソッドと同じことを自前で備えればできるはずです)。

オマケとしてキャッシュの管理に SoftReference を使って、メモリに優しくしました。

なお、いまだに(paint の2回呼び出しを防止するような)切り替えスイッチは
AWT/Swing の API の中に見つけられていません。やっぱりないんでしょうかね。

スキルアップ/キャリアアップ(JOB@IT)