- PR -

効率の良い描画方法とは?

投稿者投稿内容
ボム
ベテラン
会議室デビュー日: 2003/07/25
投稿数: 61
投稿日時: 2003-08-06 16:42
お世話になります。

・フレームの上にパネルが10枚あり各パネルに絵を張りその上に文字列を書いております。
・キーボードの上下で各パネルの絵&文字列が変わります。

問題は、一回ず上下をすると上手に各パネルが描画されるのですが、例えば上下
長押しだとかをすると描画のスピードがついていけず残像といいますか、前の文字や絵がパネル
上に残ってしまい、新しく描画した絵や文字列と重なってしまうことがあります。

PCの性能が良いとこのような事はおこりませんでした。
そこで、良い描画方法はないものかと思い質問させていただきました。
みなさまのご意見お聞かせください。

下記はpaint()メソッドです。
*******************************************************************************
public void paint(Graphics g) {
  if (offImage == null) { // 起動時
   offImage = this.createImage(sizePanelW, sizePanelH);
   offGraphics = offImage.getGraphics();
   //絵を描画
   main_img = im_target;
   offGraphics.drawImage(main_img, 0, 0, this);
   //オフスクリーン描画
   g.drawImage(offImage, 0, 0, this);
  }
  else { // 復活作業
   //絵を描画
   offGraphics.drawImage(main_img, 0, 0, this);
   //タイトル描画
   offGraphics.setColor(Color.black);
   offGraphics.setFont(TitleFont);
   offGraphics.drawString(Number, posiNumberX, posiNumberY);
   //オフスクリーン描画
   g.drawImage(offImage, 0, 0, this);
  }
 }
****************************************************************************
環境
JBuilder 8
JDK 1.2

さくらば
大ベテラン
会議室デビュー日: 2002/11/12
投稿数: 145
投稿日時: 2003-08-07 13:55
こんにちは、さくらばです。

引用:

Casperさんの書き込み (2003-08-06 16:42) より:

問題は、一回ず上下をすると上手に各パネルが描画されるのですが、例えば上下
長押しだとかをすると描画のスピードがついていけず残像といいますか、前の文字や絵がパネル
上に残ってしまい、新しく描画した絵や文字列と重なってしまうことがあります。

   //絵を描画
   offGraphics.drawImage(main_img, 0, 0, this);
   //タイトル描画
   offGraphics.setColor(Color.black);
   offGraphics.setFont(TitleFont);
   offGraphics.drawString(Number, posiNumberX, posiNumberY);
   //オフスクリーン描画
   g.drawImage(offImage, 0, 0, this);



ソースのこの部分の Number、posiNumberX, posiNumberY は毎回変化するのでしょうか。
もし、変化しないのであれば、事前に offImage を複数枚生成しておけるので、
drawString は行う必要がなくなります。

ところで、このアプリケーションは Swing ですか、AWT ですか?
Swing であれば、デフォルトでダブルバッファリングを行うので自前で
ダブルバッファリングをする必要はありません。

また、J2SE 1.2 をお使いのようですが、新しいバージョンにあげることは
できないのでしょうか。特に J2SE 1.4 以降だとグラフィックの関連のパ
フォーマンスがかなりよくなっています。

J2SE 1.4 以降であれば、ダブルバッファリングには VolatileImage クラス
や BufferStrategy クラスを使用することで高速にダブルバッファリングする
ことが可能です。これらのクラスの使い方は私の Web を参考になさってください。

http://www5.airnet.ne.jp/sakuraba/java/laboratory/JDK1.4/contents.html

# Swing ではデフォルトで VolatileImage を使ってダブルバッファリングしています。

ボム
ベテラン
会議室デビュー日: 2003/07/25
投稿数: 61
投稿日時: 2003-08-07 18:39
ご意見ありがとうございました。
まず説明が不足していた個所を追加します。
・まずSwingかAWTかですが、”AWT”です。
・バージョンを上げることは仕様の為できません。

そこでさくらば様がおっしゃった
>ソースのこの部分の Number、posiNumberX, posiNumberY は毎回変化するのでしょうか
ですが、ハイ変化します。
実際に変化するのは"posiNumberX"の値です、ある条件下で文字列を右から左へとスクロールしているわけです。

いかがでしょうか?
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2003-08-07 20:27
unibon です。こんにちわ。

引用:

Casperさんの書き込み (2003-08-06 16:42) より:
問題は、一回ず上下をすると上手に各パネルが描画されるのですが、例えば上下
長押しだとかをすると描画のスピードがついていけず残像といいますか、前の文字や絵がパネル
上に残ってしまい、新しく描画した絵や文字列と重なってしまうことがあります。


引用:

Casperさんの書き込み (2003-08-06 16:42) より:
public void paint(Graphics g) {
  if (offImage == null) { // 起動時
   offImage = this.createImage(sizePanelW, sizePanelH);
   offGraphics = offImage.getGraphics();
   //絵を描画
   main_img = im_target;
   offGraphics.drawImage(main_img, 0, 0, this);
   //オフスクリーン描画
   g.drawImage(offImage, 0, 0, this);
  }
  else { // 復活作業
   //絵を描画
   offGraphics.drawImage(main_img, 0, 0, this);
   //タイトル描画
   offGraphics.setColor(Color.black);
   offGraphics.setFont(TitleFont);
   offGraphics.drawString(Number, posiNumberX, posiNumberY);
   //オフスクリーン描画
   g.drawImage(offImage, 0, 0, this);
  }
 }


#以下、勘で書きますが。
コードの断片を拝見していても、
上記の「起動時」と「復活作業」の意味合いが良く分からないこともありますが、
offImage に描かれたものをクリアするコードが見当たらないように感じます
(たしかに「起動時」には offImage が作り直されますが)。
したがって、重ねがきされてしまうのもこのあたりに原因があるような感じがします。

まずは、
(1) offImage(offGraphics) への描画がうまくいっていないのか、
(2) paint メソッドがうまく呼ばれていないのか、
(3) paint メソッドの引数の g に対する描画(offImage からのビットマップのコピー)がうまくいかないのか、
の3つほどの中のどれにあたるのかを切り分けられてはどうでしょうか。
漠然とですが、フィールドがたくさん出てきているので、
そのあたりの使い分けがマズくて、上記(1)のような状況になっているような気がします。
上記(2)や(3)ならば、(一時的にボタンを押したら repaint するようなどにして)
repaint すれば再描画されて表示が直りますが、
上記(1)だと repaint しても表示が直らないので、切り分けできるかもしれません。

また、環境によって挙動が異なるようであれば、
Windows のビデオドライバのアクセラレータをオフにしてみてはどうでしょうか。
画面のプロパティ→設定→詳細設定→トラブルシューティング→ハードウェア アクセラレータ
です。

あと、細かい点についてふれますと、
offImage.getGraphics() した Graphics は、
使い終わったら dispose しなければならないですが、
その点はなされているでしょうか。
しなくても、ガーベッジコレクションに連動して dispose されますが、
タイムラグがありメモリが逼迫するので
アプリケーションのバグが表面化しやすくなります。
#これは利点なのかも。
未記入
大ベテラン
会議室デビュー日: 2003/06/28
投稿数: 219
投稿日時: 2003-08-07 21:04
こんにちは。
ちょっと思いつきな意見かもしれませんが、描画に関するイベントは、えっと例えば
run()メソッドによるものと、キー入力によるものが存在していると思われます。
これらの処理は同期していますでしょうか?イベントがそのまま反映されていた場合、
衝突して描画が崩れる可能性があるかもしれないです。
かずくん
ぬし
会議室デビュー日: 2003/01/08
投稿数: 759
お住まい・勤務地: 太陽系第三惑星
投稿日時: 2003-08-07 22:29
どれくらいのタイミングで再描画しているのかわからないですが、
javax.swing.Timerで描画タイミングを遅くするという方法ではダメでしょうか。
javax.swing.TimerはSDK 1.2で使えたように思えます。

swingパッケージに属していますが、swing componentとは関係ないので、AWT componentでも使用できると思います。

運用環境がSDK 1.2以降となるのであれば、このような方法はいかがでしょう。


訂正

引用:

swingパッケージに属していますが、swing componentとは関係ないので、AWT componentでも使用できると思います。



と書きましたが、内部で呼ぶSwingUtilitiesのメソッドのいくつかはswing componentに依存しているので、SwingUtilitiesがロードされたときに、依存するswing componentがロードされてしまいます(Timer自身は、swing componentに依存していないけど)。


[ メッセージ編集済み 編集者: かずくん 編集日時 2003-08-07 22:51 ]
ボム
ベテラン
会議室デビュー日: 2003/07/25
投稿数: 61
投稿日時: 2003-08-08 10:05
unibon様、Ken-Lab様、かずくん様、貴重なご意見ありがとうございました。

unibon様のご意見
>offImage に描かれたものをクリアするコードが見当たらないように感じます
私が記述した"paint()"の"if"文の"else"の下に"offImage.flush"を追加しましたが
変わりありませんでした。
ただ、気になるのは"offImage"をクリアしない為に上手く描画できないのであれば、
PCの性能で描画できたり、できなかったりという現象は起こらないのではないでしょうか?

>offImage.getGraphics() した Graphics は、使い終わったら dispose
>しなければならないですが
この"paint()"の書かれているクラスはアプリケーションが終了するまで使用されているのですが、どのようなタイミングで"dispose"すれば良いでしょうか?(別の質問をしてしまいすみません)

Ken-Lab様のご意見
>run()メソッドによるものと、キー入力によるものが存在していると思われます。
>これらの処理は同期していますでしょうか?イベントがそのまま反映されていた場合、
>衝突して描画が崩れる可能性があるかもしれないです。
はい、このペイントがされるのはスレッドによる文字の移動時と、キー入力イベント時です。
衝突して描画が崩れるとはどのような事でしょうか?

かずくん様のご意見
>javax.swing.Timerで描画タイミングを遅くするという方法ではダメでしょうか。
swingパッケージが使えない?為か駄目でした。


さくらば
大ベテラン
会議室デビュー日: 2002/11/12
投稿数: 145
投稿日時: 2003-08-08 11:45
こんにちは、さくらばです。

結局、パフォーマンスチューニングの問題ですね。
どこにボトルネックがあるのか分からないのですが、スレッドで行っている処理と
イベント処理が怪しそうです。

できれば、この部分のソースが見てみたいのですが、いかがでしょうか。


引用:

Casperさんの書き込み (2003-08-08 10:05) より:
unibon様、Ken-Lab様、かずくん様、貴重なご意見ありがとうございました。

unibon様のご意見
>offImage に描かれたものをクリアするコードが見当たらないように感じます
私が記述した"paint()"の"if"文の"else"の下に"offImage.flush"を追加しましたが
変わりありませんでした。
ただ、気になるのは"offImage"をクリアしない為に上手く描画できないのであれば、
PCの性能で描画できたり、できなかったりという現象は起こらないのではないでしょうか?



flush は必要ないです。多分、unibon さんがいいたいのは、Graphics#clearRect
とかでイメージを消去することだったと思います。

予想でしかないのですが、OffImage と main_img の大きさは同じではないですか。
多分、同じなのでイメージを消去するというコードが結果的に必要なく、正しく動作
していたのだと思います。

引用:

>offImage.getGraphics() した Graphics は、使い終わったら dispose
>しなければならないですが
この"paint()"の書かれているクラスはアプリケーションが終了するまで使用されているのですが、どのようなタイミングで"dispose"すれば良いでしょうか?(別の質問をしてしまいすみません)



Image#getGraphics した Graphics オブジェクトがプロパティになっている
のであれば dispose しなくてもかまわないと思います。

よく、paint メソッドのなかで Image#getGraphcis メソッドを使用して
Graphics オブジェクトを取得することがあるのですが、こういう場合は
paint メソッドの最後で Graphics#dispose メソッドをコールするようにします。

引用:

Ken-Lab様のご意見
>run()メソッドによるものと、キー入力によるものが存在していると思われます。
>これらの処理は同期していますでしょうか?イベントがそのまま反映されていた場合、
>衝突して描画が崩れる可能性があるかもしれないです。
はい、このペイントがされるのはスレッドによる文字の移動時と、キー入力イベント時です。
衝突して描画が崩れるとはどのような事でしょうか?



run メソッドでの処理と、イベント処理をどのようにしているかによると思います。
例えば、run メソッドの中では posiNumberX などを変更して、repaint している
だけならほぼ問題ありません。

# posiNumberX が同期されていないのは問題になるかもしれませんが...

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