- ベストアンサー
ダブルバッファの作り方
画面に描画するBCC5.5 のCプログラムがあります。画面がちらつくので、ダブルバッファにしたいのですが、具体的に、どの関数を呼んで実装したらよいのかわかりません。WEB検索をしますと結構情報がヒットしますが、解決に至ってませんので、よろしくお願いします。 具体的にやったことは、現在動いているプログラムの case WM_PAINT: hdc=BeginPaint(hWnd,&ps); paint(hdc); // 自作の描画プログラム本体 ReleaseDC(hWnd,hdc); EndPaint(hWnd,&ps); break; の部分を、「画面サイズのビットマップイメージhBitmapをつくり、そこにpaint関数で描き込み、最終画面を一気に出力する」というつもりで以下のプログラムに書き換えたのですが、表示すらしなくなってしまいました。何が悪いのかお教えください。 case WM_PAINT: GetClientRect(hWnd,&rt); h = (int)rt.bottom; w = (int)rt.right; hBuffer = CreateCompatibleDC(NULL); hBitmap = CreateCompatibleBitmap(hBuffer, w, h); SelectObject(hBuffer, hBitmap); paint(hBuffer); hdc=BeginPaint(hWnd,&ps); BitBlt(hdc, 0,0,w,h, hBuffer,0,0, SRCCOPY); ReleaseDC(hWnd,hdc); EndPaint(hWnd,&ps); DeleteDC(hBuffer); DeleteObject(hBitmap); break;
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
CreateCompatibleBitmapでメモリデバイスコンテキストを指定した場合、モノクロビットマップが作成されてしまいます。 この場合、CreateCompatibleBitmapにはスクリーンのデバイスコンテキスト(つまりBeginPaintで得られるDC)を渡す必要があると思います。 HDC hdcScreen = BeginPaint(hWnd, &ps); HDC hdcMem = CreateCompatibleDC(hdcScreen); HBITMAP hbmpMem = CreateCompatibleBitmap(hdcScreen, 幅, 高さ); HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcMem, hBitmap); paint(hdcMem); BitBlt(hdcScreen, rt.left, rt.top, 幅, 高さ, hdcMem, 0, 0, SRCCOPY); SelectObject(hdcMem, hbmpOld); DeleteObject(hbmpMem); DeleteObject(hdcMem); EndPaint(GetSafeHwnd(), &ps); 以下は参考意見なんですが(横やりを入れて申し訳ないのですが)、ちらつき防止のためだけなら、オフスクリーンバッファをあらかじめその都度作成&破棄するという方針でも良いと思いますよー あかかじめ作っておく場合の利点は、高速化が期待できるところかと。 (その分リソースを確保しっ放しになるので、場合によって使い分ければ良いと思います)
その他の回答 (6)
- Oh-Orange
- ベストアンサー率63% (854/1345)
★回答者No.2です。 >途中でウインドウサイズを変更することがあるので… ↑ ウインドウ・サイズが変更されたときの対策 ・すでに回答者No.5(MrBan)さんが『WM_SIZE等のタイミングで作り直す。』と 回答している通りです。 でも次の3タイプがあります。 (1)毎回メモリDCを作成/破棄 (2)起動時にデスクトップ画面と同じ大きさのメモリDCを作成 (3)WM_SIZEやWM_DISPLAYCHANGEメッセージでメモリDCを作り直す ・(1)のメリットはメモリ量(リソース量)を描画時しか消費しない (1)のデメリットは毎回メモリDCを作成/破棄するので低速 ・(2)のメリットは毎回メモリDCを作成/破棄しないので高速 (2)のデメリットはメモリ量(リソース量)に無駄が出る場合あり ・(3)のメリットはメモリ量を必要最低限に作成/破棄も最低回数に抑えられる (3)のデメリットはウインドウ・サイズや画面サイズの変更に手を加える手間がかかる 上記の3つより状況により選択して下さい。
お礼
再度、回答ありがとうございます。 No3さんのお礼に書いた私の対策はOh-Orangeさんの分類(3)に相当するわけですね。 わかりやすい説明と分類、ありがとうございました。
- shippo_ppk
- ベストアンサー率51% (28/54)
>># バックバッファはあらかじめ作っておくべきでは。 > はじめに作っておいて、ウインドサイズが変更されても、困ることは無いのですか? の件です。はじめにデスクトップサイズのバッファーを作ってみてはどうでしょうか。
お礼
回答ありがとうございます。 はじめにデスクトップサイズのバッファーを作るというのも一案ですね。 ありがとうございます。
- MrBan
- ベストアンサー率53% (331/615)
ANo1です。 CreateCompatibleDCの直後は、 DC自体はデバイス互換でもSelectされているビットマップは前述のものになります。 なので、「CreateCompatibleDCのビットマップ」 *ではなく*、 「画面と互換のあるビットマップ」に対してCreateCompatibleBitmapが必要です。 例えばBeginPatintのDCに対してCreateCompatibleBitmapする等。 > 問題がある場合、どのように対処すればよいのでしょうか? WM_SIZE等のタイミングで作り直す。 > また特にエラーもなかったので 提示コードではやっていませんが、APIの戻り値は確認していますか?
お礼
再度、回答ありがとうございます。 >WM_SIZE等のタイミングで作り直す。 ご教示のとおりにプログラムしたら、スムーズな描画になりました。 具体的なプログラムは、No3さんのお礼に書いたとおりです。 >提示コードではやっていませんが、APIの戻り値は確認していますか? 確認してません。やはり確認したほうがいいのですね。アドバイスありがとうございます。 一緒のお礼になってしまいますが、MrBanさん、Oh-Orangeさん、mixmarionさん、適切なアドバイス、本当にありがとうございました。
- mixmarion
- ベストアンサー率100% (2/2)
No.3です。 サンプルコードの最後、 誤:EndPaint(GetSafeHwnd(), &ps); 正:EndPaint(hWnd, &ps); でした。 ごめんなさい。
- Oh-Orange
- ベストアンサー率63% (854/1345)
★アドバイス >WEB検索をしますと結構情報がヒットしますが、 >解決に至ってませんので、よろしくお願いします。 ↑ ここのQ&AのC/C++カテゴリでも検索しましたか? つい昨日似たよな質問があり回答しました。 http://oshiete1.goo.ne.jp/qa4093552.html→『WM_PAINTとBitBlt』 または http://oshiete1.goo.ne.jp/qa3052870.html→『画面がチカチカする』 が参考になると思います。 ・あとWM_PAINTメッセージ内でなぜ >ReleaseDC(hWnd,hdc); を行っているのでしょうか? >EndPaint(hWnd,&ps); これをしているので必要ないと思います。 ・WM_PAINTメッセージ内でデバイスコンテキストは (1)BeginPaint()関数で取得 (2)EndPaint()関数で解放 これが基本。 だからWM_PAINTメッセージ内でGetDC()、ReleaseDC()関数を混ぜると おかしくなりませんか?混乱しますよ。 ・とにかく上記の2つの過去質問が参考になると思います。
お礼
回答ありがとうございます。 > つい昨日似たよな質問があり回答しました。 > http://oshiete1.goo.ne.jp/qa4093552.html→『WM_PAINTとBitBlt』 > または > http://oshiete1.goo.ne.jp/qa3052870.html→『画面がチカチカする』 > が参考になると思います。 確かに、読んで見ると関係ありますね。「ダブルバッファ C言語」や「ダブルバッファ BCC」などで検索したので、上位に見つからなかったようです。 http://oshiete1.goo.ne.jp/qa3052870.htmlのNo3の回答、参考にしています。ありがとうございます。 早速ですが、No1さんのお礼にも描いたのですが、ひとつ質問があります。 CASE WM_CREATE: で hMemBMPとhMemDCを1回作って、それをStatic 変数として、毎回使用していますが、途中でウインドサイズが変更したとき問題ないのでしょうか? 問題がある場合、どのように対処すればよいのでしょうか? >・あとWM_PAINTメッセージ内でなぜ >>ReleaseDC(hWnd,hdc); > を行っているのでしょうか? >>EndPaint(hWnd,&ps); > これをしているので必要ないと思います。 そうなんですか。EndPaint(hWnd,&ps);だけでは、hdc を開放するところがないのReleaseDC(hWnd,hdc);が必要かと思い入れてました。また特にエラーもなかったので、知らずにいました。ありがとうございます。
- MrBan
- ベストアンサー率53% (331/615)
> hBitmap = CreateCompatibleBitmap(hBuffer, w, h); とりあえず、CreateCompatibleBitmapの引数hBufferで意図しないbitmapを作りだしていませんか。 <MSDN引用:CreateCompatibleDC> メモリデバイスコンテキストを作成した時点では、 その表示サーフェスはモノクロームであり、 高さと幅は 1 ピクセル×1 ピクセルです。 </MSDN引用> 何もでないように見えるのはこのあたりが原因では。 # バックバッファはあらかじめ作っておくべきでは。
お礼
回答ありがとうございます。 >とりあえず、CreateCompatibleBitmapの引数hBufferで意図しないbitmapを作りだしていませんか。 直前の hBuffer = CreateCompatibleDC(NULL); で、NULL を指定しているので、現在の画面と互換性のある、つまりフルカラーのメモリデバイスコンテキストが作成されていると思うのですが。 でも真っ黒な画面なので、モノクロームのようですね。どのように指定したらよいのでしょう? <MSDN引用:CreateCompatibleDC> 既存のデバイスコンテキストのハンドルを指定します。NULL を指定すると、アプリケーションの現在の画面と互換性のあるメモリデバイスコンテキストが作成されます。 </MSDN引用> ># バックバッファはあらかじめ作っておくべきでは。 これなんですが、途中でウインドウサイズを変更することがあるので、初めに作っておくと、困るのではないのかと思って、毎回作ることにしたのです。はじめに作っておいて、ウインドサイズが変更されても、困ることは無いのですか?
お礼
回答ありがとうございます。 お教えいただいたソースを、No2さんの回答を参考に加工して以下の変更を加えたところ、スムーズな描画になりました。 ありがとうございました。 case WM_CREATE: に、 GetClientRect(hWnd,&rt); WindowsHeight = (int)rt.bottom; WindowsWidth = (int)rt.right; hDC = GetDC(hWnd); hMemBMP = CreateCompatibleBitmap( hDC, WindowsWidth, WindowsHeight ); hMemDC = CreateCompatibleDC( hDC ); SelectObject(hMemDC, hMemBMP); ReleaseDC(hWnd, hDC); を追加し、 case WM_PAINT: GetClientRect(hWnd,&rt); h= (int)rt.bottom; w= (int)rt.right; if(h!=WindowsHeight || w!=WindowsWidth) { DeleteObject(hMemBMP); DeleteObject(hMemDC); 再度、オフスクリーンバッファを作成 } で、ウインドサイズ変更の場合の処理をして、 SelectObject( hMemDC, GetStockObject(NULL_PEN) ); SelectObject( hMemDC, CreateSolidBrush(RGB(0xFF,0xFF,0xFF)) ); Rectangle( hMemDC, 0, 0, WindowsWidth, WindowsHeight ); DeleteObject( SelectObject(hMemDC,GetStockObject(WHITE_BRUSH)) ); ReleaseDC(hWnd, hDC); で、白で初期化して paint (hMemDC); で描画して、 hDC=BeginPaint(hWnd,&ps); BitBlt(hDC, 0,0,w,h, hMemDC,0,0, SRCCOPY); EndPaint(hWnd,&ps); で画面に描く