WinAPIでキレイに描画する -その3 メッセージ管理-

 前回からの続き。

前回まででちらつきのない描画は大体完了したわけだけど、

このままでは、描画の更新のタイミングが上手くいかないんじゃないか?

という話。



 これを解決するには、

自分でWM_PAINTを送信してやればいい。

ただ、何も考えずにSendMessage()で送ればいいかというと、

そうもいかない*1ので、

InvalidateRect()

を使う。




...
#define WINDOW_WIDTH (/*Windowの幅*/)
#define WINDOW_HEIGHT (/*Windowの高さ*/)
...
/*
* Windowプロシージャ
*/

LRESULT CALLBACK WindowProc( HWND hWnd, UINT msg,
WPARAM wp, LPARAM lp )
{
...
static HDC back_hdc;
static HBITMAP back_buf;
...

switch( msg )
{
...
case WM_CREATE:
...
HDC hdc = GetDC( hWnd );

// バックバッファ生成
back_buf = CreateCompatibleBitmap(
hdc,
WINDOW_WIDTH, WINDOW_HEIGHT
);
// バックバッファ描画用ハンドル取得
back_hdc = CreateCompatibleDC( hdc );

// ハンドルにバッファを設定しておく
SelectObject( back_hdc, back_buf );
// 念のためペン初期化
SelectObject( back_hdc, GetStockObject( NULL_PEN ) );

ReleaseDC( hWnd, hdc );
...
break;
...
case WM_DESTROY:
...
// 忘れずにバックバッファ解放
DeleteDC( back_hdc );
DeleteObject( back_buf );
...
break;
...
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint( hWnd, &ps );

// 描画用の処理をまとめた関数
// 引数に描画先のデバイスコンテキスタを渡す

Draw( back_hdc );

// 表示用バッファにバックバッファをそのままコピー
BitBlt( hdc,
0, 0,
WINDOW_WIDTH, WINDOW_HEIGHT,
back_hdc,
0, 0,
SRCCOPY );

EndPaint( hWnd, &ps );
break;
...
// 何かしらの再描画したいメッセージ
// 今回はとりあえず
// 「フォーカス取得時」「クリック時」に再描画させてみる
case WM_SETFOCUS:
case WM_LBUTTONDOWN:
// 第一引数に再描画命令を送信
InvalidateRect( hWnd, NULL, FALSE );
break;

...
}
...
}
...

この色が今回の追加分。

表示を更新したいタイミングで上記の様にInvalidataRect()を呼び出してやるだけでOK。

 それぞれの引数の内容は、関数のリンク先を見てもらえればわかると思うが、

第二引数に任意の範囲を与えてやることも一応可能。(だからどうしたという感じもするが(苦笑))

第三引数にTRUEを与えてやると、ノイズっぽいので初期化した後にWM_PAINTを送信してくれる。

(真っ白・真っ黒ではないので要注意!)

が、基本的に少し重くなるだけなので、FALSEでいいと思う。



 これで、再描画のタイミングもバッチリ決まり、

更新の際もほとんどチラつき無くキレイに描画できるようになる…と思う。



 ただし、InvalidateRect()の呼び出しがあんまりにも頻繁すぎると、

流石にキレイにとはいかなくなるので、

InvalidateRect()の呼び出しはプロシージャ内だけにしておくように心がけておくといい…かも。

(要するに、60fpsのゲームのループで毎回更新するようなのは保証外ですよ。ということ(何))

 しかし、普通のツールとかに使う程度なら、描画の更新回数が最低限で済むようになるので、

それがこの仕様のねらいなんじゃないかな…と思ってみたり。




*1:メッセージの受け取りに少し遅延がある(?)というのもあるけど、
後述のInvalidateRect()で描画許可(?)を出しておかないと上手く描画できないことがあるようなので。