ダブルバッファリンクで、CPU使用率のようなグラフ作成するには? – プログラミング – Home

ダブルバッファリンクで、CPU使用率の...
 
通知
すべてクリア

[解決済] ダブルバッファリンクで、CPU使用率のようなグラフ作成するには?


ロケットサラダ
 ロケットサラダ
(@ロケットサラダ)
ゲスト
結合: 18年前
投稿: 34
Topic starter  

VC++6.0 MFC ダイアログベースです。

今、計測器機と通信を行う、データロギングソフトを作成しています。
受信したデータを、タスクマネージャーのCPU使用率で使われているようなグラフ
にしたいと思っているのですが、どのようにしたら良いのか解らなく困っています。

①下記コードのようにして、ダブルバッファでグラフを書くことは出来ました。
 ですが、OnPaint()呼び出し毎に Draw() で、グラフを全て書き直しています。
 『一定時間毎に左側にずれていく、右端の部分だけグラフを追加して行く』
 と言う場所が解りません。
 
②どのように解らないか。
 1. OnPaint() 内の Draw() 関数でグラフを書いているのですが、
  CDC MemDC は最後デバイスコンテキストを元に戻してしまいますが、
  次にOnPaint()が呼び出された時に、さっき書いたグラフを
  そのまま表示するにはどうしたら良いですか?
  (左にずらして表示するのは次のステップのつもりです。)

 2.1.でそのまま表示出来たとして、一定時間毎に表示をずらしていった場合、
  (例えば横幅500ピクセルで、一定時間毎に 1 ピクセル左にずれるとします)
  dc.BitBlt()でコピーするBMPは毎回1回で500ピクセル全画面コピーするんでしょうか?
  (その場合1ピクセルずらした元グラフ499ピクセルと、新しく書かれた1ピクセルはどうやって
  結合するのでしょうか?)
  横幅499ピクセル転送してから、残りの新しく書かれた1ピクセルを右端の隙間に転送する
  のでしょうか?
(その場合、横幅1ピクセル毎にグラフを書いて、dc.BitBlt()を横幅ピクセル回
  ループさせて書くのでしょうか?)

実際どのようにしたら良いのか全く解っていないので、
②の解らない内容が検討外れで、
言っていることが意味不明のような可能性がありますがよろしくお願い致します。

void CLoggingGraph::OnPaint()
{

CPaintDC dc(this); // 描画用のデバイス コンテキスト

CRect rcClient;

CDC MemDC;
CBitmap Bitmap;

CBitmap* pOldBitmap;

// ウィンドウのクライアント領域の座標を取得。
GetClientRect(&rcClient);

int nWidth = rcClient.Width();
int nHeight = rcClient.Height();

// pDC により指定されるデバイスと互換性のある メモリ デバイス コンテキスト を作成。
MemDC.CreateCompatibleDC(&dc);

// pDC で指定されたデバイスと互換性のあるBMPを初期化します。
Bitmap.CreateCompatibleBitmap( &dc , nWidth , nHeight );

// 使用するデバイスコンテキストを選択
pOldBitmap = MemDC.SelectObject(&Bitmap);

// 裏画面にグラフを描画
Draw(&MemDC, nWidth, nHeight);

// コピー元の デバイス コンテキスト から現在の デバイス コンテキスト にBMPをコピー
dc.BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);

//デバイスコンテキストを元に戻す
MemDC.SelectObject(pOldBitmap);

}


引用未解決
トピックタグ
wclrp ( 'o')
 wclrp ( 'o')
(@wclrp ( 'o'))
ゲスト
結合: 18年前
投稿: 287
 

長い話になりそうだ。

ダブルバッファといいながらバッファは一個とは?

<余談>
ビットマップなしで直接ウィンドウのDCへ描画する場合
ScrollDC
無駄な描画を減らせるので早くなるかもしれないが
動画も余裕で再生できる今のコンピュータの性能なら気にしなくていいかも
確か表示内容をスクロールし
新たに現れた部分が更新領域になる。
そのあとWM_MAINTで更新領域だけ描画すればいい。
自動的にクリッピングされているので
WM_MAINTで再普通に描画すれば更新領域だけの描画になる。
</余談>

ビットマップの意味ないと思う。
ウィンドウサイズが変わるとか画面の色数が変わったときに作り直すようにして
それ以外はビットマップを作り置きしていいんじゃないかな。

面倒だから一旦ここで送信


返信引用
wclrp ( 'o')
 wclrp ( 'o')
(@wclrp ( 'o'))
ゲスト
結合: 18年前
投稿: 287
 

(2) 2 について

>(例えば横幅500ピクセルで、一定時間毎に 1 ピクセル左にずれるとします)

> dc.BitBlt()でコピーするBMPは毎回1回で500ピクセル全画面コピーするんでしょうか?

そうです。

一定時間毎の処理で
ScrollDCを使いスクリーンからスクリーンへのBitBltでスクロールさせます。
上に別のウィンドウが重なっていてもちゃんと
新しく現れる領域が更新領域になります。

こんなことしなくて全部描画しちゃえよ。
今のPCなら余裕だよ。

そしてOnPaint()で
ビットマップ全体をビットマップからスクリーンへBitBltします。
更新領域が狭ければ自動的にその部分のBitBltしかしないから気にするな。

ScrollDCを使用していなければ全部BitBltすることになるが
今のPCなら余裕だよ。気にするな。

>横幅499ピクセル転送してから、
>残りの新しく書かれた1ピクセルを右端の隙間に転送するのでしょうか?

前に書いてあるようにビットマップは作り置きしておく場合は
ビットマップからビットマップへBitBltすればスクロールできる。
というか、こういう使い方しないとビットマップを使うことでかえって
プログラマの作業は増えるがてメモリは食うし処理は遅くなるし。

で、ビットマップの右端に新しいグラフを描く。
これはグラフ全部描くより右端だけしか描かない方が早いから。

単純なグラフだったら全部グラフ書き直してもいいんじゃない。
この場合のビットマップを使う理由は画面のちらつき防止。
処理速度的にはビットマップを使う分遅くなると思う。
画面のちらつき防止ならビットマップの作り置きは要らないな。

ということで今までの話が覆ってしまったアハハ


返信引用
ロケットサラダ
 ロケットサラダ
(@ロケットサラダ)
ゲスト
結合: 18年前
投稿: 34
Topic starter  

wclrp ( 'o')さんありがとうございます。

>長い話になりそうだ。
ほんとうにありがとうございます。

>ダブルバッファといいながらバッファは一個とは?
グラフィックを画面に直接描画せずに、メモリ上にいったんすべての描画を行い、
それを画面に転送すればダブルバッファだと思って、その言葉を使ってしまいました。

>動画も余裕で再生できる今のコンピュータの性能なら気にしなくていいかも
// 裏画面にグラフを描画
Draw(&MemDC, nWidth, nHeight);
ここでグラフの線を書いているのですが、とても時間のかかる計算を繰り返しているため、
とても重くなってしまっています。(こっちの計算を直せよ!と突っ込まないで下さい。)
毎回全て計算しなくて済めば軽くなるのでは?と思い、試してみることにしました。

使っているPCは、WinXP UPU 1.6GHz メモリ 512M です。 計算する所だけ
CPU使用率が80%を越えます。

>一定時間毎の処理で
>ScrollDCを使いスクリーンからスクリーンへのBitBltでスクロールさせます。
>そしてOnPaint()で
>ビットマップ全体をビットマップからスクリーンへBitBltします。

一定時間毎に呼び出される関数を作成してそこで、
ScrollDCで画像をスクロールさせ、右端に新しいグラフを追加する。

OnPaint()では一定時間毎に呼び出される関数で作られるBMPを、メイン画面に
転送を行うと言う事でしょうか?

>前に書いてあるようにビットマップは作り置きしておく場合は
>ビットマップからビットマップへBitBltすればスクロールできる。
リアルタイムにデータを受信して、その結果から計算してグラフを作るのですが、
データが解らないのですが作り起きってできますか?

>処理速度的にはビットマップを使う分遅くなると思う。
もしかしたら効果が薄いのかもしれませんが、やってみようと思っています。


返信引用
wclrp ( 'o')
 wclrp ( 'o')
(@wclrp ( 'o'))
ゲスト
結合: 18年前
投稿: 287
 

> OnPaint()では
> 一定時間毎に呼び出される関数で作られるBMPを
> メイン画面に転送を行うと言う事でしょうか?

そういうことですね。

たとえばアイコン化したウィンドウを復元するとか
上に重なっていた邪魔なウィンドウがなくなるとかでも
OnPaintが呼ばれるよね。

そのたびに時間のかかるグラフの全体を作成しなおしていたら
スゲー遅くなってしまう。

> リアルタイムにデータを受信して、その結果から計算してグラフを作るのですが、
> データが解らないのですが作り起きってできますか?

意味がわからない。

データロギングソフトだから今まで受信してきたデータを記録しているんでしょ。
記録していないならなおさらグラフをビットマップに作り置きしておかないと
OnPaintが呼ばれたとき何を表示するのさ。

CPU使用率のようなグラフだから
数秒前のグラフが左へスクロールしていくようなものじゃないの?
左へスクロールする部分は位置が違うだけで絵の内容は同じだから
ビットマップに残しておけば少しは作業が減るかと思うんだが?
といっても横幅500ドットぐらいの線グラフか棒グラフか知らないが
そんなに時間かかるとも思えないけど。


返信引用
ISLe
 ISLe
(@ISLe)
ゲスト
結合: 18年前
投稿: 38
 

ダブルバッファじゃなくてオフスクリーンだね。


返信引用
ISLe
 ISLe
(@ISLe)
ゲスト
結合: 18年前
投稿: 38
 

ゲームで円筒スクロールするテクがそのまま使えそうだけど。

オフスクリーンビットマップは原点をずらしながら描く。
クライアントDCに転送するときは、原点の右側全部をクライアントのx座標0の位置に、
原点より左側をそれに続く位置に2回に分けて転送する。
#原点のx座標が0のときは1回で転送できる。


返信引用
ロケットサラダ
 ロケットサラダ
(@ロケットサラダ)
ゲスト
結合: 18年前
投稿: 34
Topic starter  

wclrp ( 'o')さんありがとうございます。
>意味がわからない。

>データロギングソフトだから今まで受信してきたデータを記録しているんでしょ。
>記録していないならなおさらグラフをビットマップに作り置きしておかないと
>OnPaintが呼ばれたとき何を表示するのさ。
勘違いしていました。今後“表示する予定のデータ”をビットマップに作り置きして
おいて・・・。と思ってしまっていました。

>CPU使用率のようなグラフだから
>数秒前のグラフが左へスクロールしていくようなものじゃないの?
>左へスクロールする部分は位置が違うだけで絵の内容は同じだから
>ビットマップに残しておけば少しは作業が減るかと思うんだが?
そうです。
私もそれを考えてやろうと思っています。

>といっても横幅500ドットぐらいの線グラフか棒グラフか知らないが
>そんなに時間かかるとも思えないけど。
横幅500ピクセルは仮の値で、実際はもう少し大きいのですが、
グラフに描く処理方法も今後改善していこうと考えています。

ISLeさんありがとうございます。
>ダブルバッファじゃなくてオフスクリーンだね。
知らないままダブルバッファと言い続ける所でした。

>ゲームで円筒スクロールするテクがそのまま使えそうだけど。
>オフスクリーンビットマップは原点をずらしながら描く。
ISLeさん、ここで言う原点と言うのは 例えば横幅500ピクセルで、
1ピクセル 左横にずらしたいのなら原点の座標は(1,y)のようにして、
x座標 1~500に書かれていたビットマップ を一定時間後に0~499 の位置に
転送して・・
>原点より左側をそれに続く位置に2回に分けて転送する。
空白になる x座標500の位置だけグラフを新たに書いて、そこは
分けて転送を行うと言うことでよろしいでしょうか?


返信引用
FUKU
 FUKU
(@FUKU)
ゲスト
結合: 17年前
投稿: 73
 

大昔に同じようなコードを書いたことがあるので手順だけ。

1. ScrollDC()でオフスクリーンビットマップ(以下、裏ビット)を2ピクセル左に
2. 裏ビットの右2ピクセル幅の矩形に、グラフの縦線と横線を引く
3. 1つのデータを裏ビットに描画
(前回位置から今回位置までをMoveTo~LineTo)
4. ScrollWindowEx()でクライアント領域を2ピクセル左に
5. 裏ビットに描いた2ピクセル幅の矩形をBitBlt()でクライアントDCに転送

1.~5.を単位時間毎に行うようにします。
100ms間隔で更新してましたが、CPU負荷は殆ど無かったです

あと、WM_PAINTがきたらクリップ領域を裏ビットから描画。
WM_SIZEがきたら裏ビットのサイズを変更し、保持している値を使って全更新します。


返信引用
ロケットサラダ
 ロケットサラダ
(@ロケットサラダ)
ゲスト
結合: 18年前
投稿: 34
Topic starter  

FUKUさんありがとうございます。

手順を理解することが出来ました。
後はこれを実装することが出来るかどうか・・・です。

今回の質問は方法(手順)についてお伺いしました。
手順が理解できたのでここで解決とさせて頂きます。

みなさん丁寧に教えて頂きありがとうございました。


返信引用

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました