• ベストアンサー

ダブルバッファの作り方

画面に描画する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;

質問者が選んだベストアンサー

  • ベストアンサー
  • mixmarion
  • ベストアンサー率100% (2/2)
回答No.3

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); 以下は参考意見なんですが(横やりを入れて申し訳ないのですが)、ちらつき防止のためだけなら、オフスクリーンバッファをあらかじめその都度作成&破棄するという方針でも良いと思いますよー あかかじめ作っておく場合の利点は、高速化が期待できるところかと。 (その分リソースを確保しっ放しになるので、場合によって使い分ければ良いと思います)

usatan2
質問者

お礼

回答ありがとうございます。 お教えいただいたソースを、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); で画面に描く

その他の回答 (6)

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.7

★回答者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つより状況により選択して下さい。

usatan2
質問者

お礼

再度、回答ありがとうございます。 No3さんのお礼に書いた私の対策はOh-Orangeさんの分類(3)に相当するわけですね。 わかりやすい説明と分類、ありがとうございました。

回答No.6

>># バックバッファはあらかじめ作っておくべきでは。 > はじめに作っておいて、ウインドサイズが変更されても、困ることは無いのですか? の件です。はじめにデスクトップサイズのバッファーを作ってみてはどうでしょうか。

usatan2
質問者

お礼

回答ありがとうございます。 はじめにデスクトップサイズのバッファーを作るというのも一案ですね。 ありがとうございます。

  • MrBan
  • ベストアンサー率53% (331/615)
回答No.5

ANo1です。 CreateCompatibleDCの直後は、 DC自体はデバイス互換でもSelectされているビットマップは前述のものになります。 なので、「CreateCompatibleDCのビットマップ」 *ではなく*、 「画面と互換のあるビットマップ」に対してCreateCompatibleBitmapが必要です。 例えばBeginPatintのDCに対してCreateCompatibleBitmapする等。 > 問題がある場合、どのように対処すればよいのでしょうか? WM_SIZE等のタイミングで作り直す。 > また特にエラーもなかったので 提示コードではやっていませんが、APIの戻り値は確認していますか?

usatan2
質問者

お礼

再度、回答ありがとうございます。 >WM_SIZE等のタイミングで作り直す。 ご教示のとおりにプログラムしたら、スムーズな描画になりました。 具体的なプログラムは、No3さんのお礼に書いたとおりです。 >提示コードではやっていませんが、APIの戻り値は確認していますか? 確認してません。やはり確認したほうがいいのですね。アドバイスありがとうございます。 一緒のお礼になってしまいますが、MrBanさん、Oh-Orangeさん、mixmarionさん、適切なアドバイス、本当にありがとうございました。

  • mixmarion
  • ベストアンサー率100% (2/2)
回答No.4

No.3です。 サンプルコードの最後、 誤:EndPaint(GetSafeHwnd(), &ps); 正:EndPaint(hWnd, &ps); でした。 ごめんなさい。

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.2

★アドバイス >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つの過去質問が参考になると思います。

参考URL:
http://oshiete1.goo.ne.jp/qa3052870.html
usatan2
質問者

お礼

回答ありがとうございます。 > つい昨日似たよな質問があり回答しました。 > 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)
回答No.1

> hBitmap = CreateCompatibleBitmap(hBuffer, w, h); とりあえず、CreateCompatibleBitmapの引数hBufferで意図しないbitmapを作りだしていませんか。 <MSDN引用:CreateCompatibleDC> メモリデバイスコンテキストを作成した時点では、 その表示サーフェスはモノクロームであり、 高さと幅は 1 ピクセル×1 ピクセルです。 </MSDN引用> 何もでないように見えるのはこのあたりが原因では。 # バックバッファはあらかじめ作っておくべきでは。

usatan2
質問者

お礼

回答ありがとうございます。 >とりあえず、CreateCompatibleBitmapの引数hBufferで意図しないbitmapを作りだしていませんか。 直前の hBuffer = CreateCompatibleDC(NULL); で、NULL を指定しているので、現在の画面と互換性のある、つまりフルカラーのメモリデバイスコンテキストが作成されていると思うのですが。 でも真っ黒な画面なので、モノクロームのようですね。どのように指定したらよいのでしょう? <MSDN引用:CreateCompatibleDC> 既存のデバイスコンテキストのハンドルを指定します。NULL を指定すると、アプリケーションの現在の画面と互換性のあるメモリデバイスコンテキストが作成されます。 </MSDN引用> ># バックバッファはあらかじめ作っておくべきでは。 これなんですが、途中でウインドウサイズを変更することがあるので、初めに作っておくと、困るのではないのかと思って、毎回作ることにしたのです。はじめに作っておいて、ウインドサイズが変更されても、困ることは無いのですか?

関連するQ&A

  • TextOut( ) が動かない

    LRESULT CALLBACK WndProc( HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam){ PAINTSTRUCT ps; HDC hdc; switch(msg){ case WM_KEYDOWN:  if( wParam == VK_ESCAPE ){   hdc = BeginPaint(hWnd, &ps);   TextOut(hdc,0,0,str,strlen(str));   EndPaint(hWnd, &ps);  }  break; case WM_PAINT:  break; エスケープキーで文字表示をやりたいけど TextOut( ) が動作していないみたいでした。 switch(msg){ case WM_KEYDOWN:  if( wParam == VK_ESCAPE ){   hdc = BeginPaint(hWnd, &ps);   TextOut(hdc,0,0,str,strlen(str));   EndPaint(hWnd, &ps);  }  break; case WM_PAINT:  hdc = BeginPaint(hWnd, &ps);  TextOut(hdc,0,0,str,strlen(str));  EndPaint(hWnd, &ps);  break; とすると、常に文字が表示されたから、やっぱり case WM_KEYDOWN: の中の TextOut( ) が 動作していないんだと思いました。 TextOut( ) は case WM_PAINT: からのつながりが ある場合でないと実行されないんですか? ソースのおかしいところがあったら教えてください。

  • TextOut( ) を BeginPaint( ) と GetDC( )

    LRESULT CALLBACK の case WM_PAINT: で、 hdc = BeginPaint(hWnd, &ps); TextOut(hdc,0,0,str,strlen(str)); EndPaint(hWnd, &ps); ↑問題無し。↓文字がちらつく。 hdc = GetDC(hWnd); TextOut(hdc,0,0,str,strlen(str)); ReleaseDC(hWnd,hdc); ちらつきの原因は、高速で TextOut( ) が繰り返されるから だと思いました。 どうして TextOut( ) が繰り返されるんですか? 上の方法の場合は、 ReleaseDC(hWnd,hdc); は書かなくてもいいですか?

  • c++ ダブルバッファリング、以下のコードでできず

    こんばんわ。visual studio express 2013でC++を勉強している者です。 ダブルバッファリングをしたいのですが、チラつきが改善しません。 どう書けばダブルバッファリングを実現できるのか、ご教授ください。 以下は私が書いたコードの一部です。よろしくお願いします。 (要は、hdc_mem1のビットマップを、hdc_mem0(白紙のビットマップ)に読み込み、hdc_mem0のみを画面に表示しようとしています。 いずれは用意したすべての種類のビットマップをhdc_mem0に読み込み、hdc_mem0のみ画面に表示させることによってチラつきを抑えようと考えています) case WM_CREATE:  hdc = GetDC(hWnd);  hBmp0 = LoadBitmap(hInst, TEXT("MYBMP0"));  GetObject(hBmp0, (int)sizeof(BITMAP), &bmp_info0);  hdc_mem0 = CreateCompatibleDC(hdc);  SelectObject(hdc_mem0, hBmp0);  hBmp1 = LoadBitmap(hInst, TEXT("MYBMP1"));  GetObject(hBmp1, (int)sizeof(BITMAP), &bmp_info1);  hdc_mem1 = CreateCompatibleDC(hdc);  SelectObject(hdc_mem1, hBmp1);  DeleteObject(hBmp0);  DeleteObject(hBmp1);  ReleaseDC(hWnd, hdc);  break; case WM_PAINT:  hdc = BeginPaint(hWnd, &ps);  BitBlt(hdc_mem0, 0, 0, bmp_info1.bmWidth, bmp_info1.bmHeight, hdc_mem1, 0, 0, SRCCOPY);  BitBlt(hdc, 0, 0, bmp_info0.bmWidth, bmp_info0.bmHeight, hdc_mem0, 0, 0, SRCCOPY);  EndPaint(hWnd, &ps);  break; case WM_DESTROY:  DeleteDC(hdc_mem0);  DeleteDC(hdc_mem1);  PostQuitMessage(0);  break;

  • WM_PAINT

    WM_PAINT について教えてほしいのですが、 WM_PAINTがシステムから発行されるタイミングとして、 クライアント領域に無効領域がある時 UpdateWindow()を呼び出した時 InvalidateRect()を呼び出した時があると思いますが 例えば LRESULT CALLBACK WndProc(HWND , UINT , WPARAM , LPARAM) { LPCTSTR  Str = TEXT("Kitty"); switch (msg) { case WM_CREATE:    hdc = GetDC(hwnd);    SetTextColor(hdc , RGB(255, 0 , 0));    ReleaseDC(hwnd , hdc);    return 0; case WM_PAINT:    hdc = BeginPaint(hwnd , &ps);    TextOut(hdc, 10 , 10 , Str, lstrlen(Str));    EndPaint(hwnd , &ps);    return 0; } return DefWindowProc(hwnd , msg , wp , lp); } の場合, WM_PAINTはどのタイミングでシステムから呼び出されるんですか? WinMain()でUpdateWindow()もInvalidateRect()もつかっていないのですが。 いつシステムから送られるかご教授をお願いします。

  • TextOutについて

    ウィンドウの雛形に、 case WM_PAINT:{ static int t; PAINTSTRUCT ps; char cbuf[100]; HDC hdc = BeginPaint( hWnd, &ps ); wsprintf( cbuf, _T("on WM_PAINT%d"),t); TextOut(hdc,0,100,cbuf,sizeof(cbuf)); SetWindowText( hWnd, cbuf ); t++; EndPaint( hWnd, &ps ); break; } として実行したところ、ウィンドウを任意のところに重ねて、移動すると、SetWindowはタイトルバーにちゃんとの値が表示されるのですが、TextOutのtは再描画されないのですが、通っているのに描画されないのはなぜでしょうか?

  • メモリデバイスコンテキストを用いた描画処理

    お世話になります。 Borland C++ 5.5.1 for Win32で簡易な画像ビュアーを開発しています。 基本的な動作は完成したのですが、ある条件下で発生する問題を回避するために、メモリデバイスコンテキストを用いようと考えています。 ところが、後述のように変更したところ、画像がまったく表示されなくなりました。(ウィンドウ背景色で塗りつぶされたままです。一瞬たりとも表示されません。) Windowsプログラムに慣れていないため解決に難儀しています。 どなたか原因と対策をご教示くださいますようよろしくお願いします。 ////////////////////////// //変更前 画像表示される// ////////////////////////// case WM_PAINT: hdc = BeginPaint(hWnd, &ps); /* ~省略(画像処理)~ */ StretchDIBits(hdc,~,SRCCOPY);//処理した画像をウィンドウへ EndPaint(hWnd, &ps); break; //////////////////////////// //変更後 画像表示されない// //////////////////////////// case WM_PAINT: hdc = BeginPaint(hWnd, &ps); hmemdc = CreateCompatibleDC(hdc); /* ~省略(画像処理)~ */ StretchDIBits(hdc,~,SRCCOPY);//処理した画像をMemoryDCへ BitBlt(hdc, 0, 0, rc.right, rc.bottom, hmemdc, 0, 0, SRCCOPY);//MemoryDCからウィンドウへ(rcはクライアント領域) DeleteDC(hmemdc); EndPaint(hWnd, &ps); break;

  • WinAPIでRECT構造体の宣言について

    PAINTSTRUCT ps; HDC hdc; RECT rct={200,200,200,200}; char *szStr = "あけまして\n\tおめでとう"; switch (msg) { case WM_PAINT: GetClientRect(hWnd, &rct); hdc = BeginPaint(hWnd, &ps); DrawText(hdc, (LPCTSTR)szStr, -1, &rct, DT_CENTER | DT_WORDBREAK); EndPaint(hWnd, &ps); break; のようにしているのですが文字列が表示される場所が中央上部です。 何故なのでしょう? 自分としてはど真ん中辺りから表示されるようにしているつもりなのですが

  • 画像処理 ビットマップ ダブルバッファリング

    「猫でもわかるWindowsプログラミング」を参考に、ビットマップを表示するプログラムを作っています。 下記ソース(WM_PAINT部分のみ抜粋)のような感じで、 読み込んだビットマップをダブルバッファリングを用いて表示させたいのですが、 何も表示させることができず、困っております。 LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) {   HBITMAP hBmp;   BITMAP bmp_info;   HDC hDC, hDC_mem;   PAINTSTRUCT ps;   int w=0, h=0;   switch (msg) { case WM_PAINT:   hDC = BeginPaint(hwnd,&ps);   hDC_mem = CreateCompatibleDC(hDC);   hBmp=LoadBitmap(hInst, TEXT("MYBMP"));   hBmp = (HBITMAP)LoadImage(hInst, TEXT("MYBMP"), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR); GetObject(hBmp, (int)sizeof(BITMAP), &bmp_info); w = bmp_info.bmWidth; h = bmp_info.bmHeight; SelectObject(hDC_mem, hBmp); BitBlt(hDC, 0, 0, w, h, hDC_mem, 0, 0, SRCCOPY); DeleteDC(hDC_mem); DeleteObject( hBmp ); EndPaint( hwnd, &ps ); break; } } リソースですが、下記のように bitmap1.bmpを指定しており、画像はもちろんフォルダ内に入れ、 読み込める状態にはしています。   //   // Bitmap   //   MYBMP BITMAP "bitmap1.bmp"   #endif // 日本語 resources しかし、hBmpの戻り値を調べると NULL の値が入っています。当然画像は表示されません。 当方、Microsoft visual studio 2005を使っておりますが、このプロジェクトは、 Win32コンソールアプリケーション⇒Windowsアプリケーション で作成はせず、 Win32コンソールアプリケーション⇒コンソールアプリケーション で作成していますので、 ここが問題になっているのでしょうか? できれば、コンソールアプリケーションで作成したいですが、LoadImage関数は使えないのでしょうか? それとも他に問題点があるのでしょうか? 以前は動画処理をしており、SetDIBitsToDevice関数を使って配列の中身を指定し、画像をウィンドウ上に表示していました。 しかし、画像のちらつきが気になったため、ダブルバッファリングに改良しようとし、 調べてみたところ、ビットマップを用いるやり方が一般的なようでした。 そこで、まずはビットマップの表示をさせるプログラムをつくってみようと思ったのですが、つまずいている状態です。 環境は、 Microsoft Windows XP Home Edition Version2002 Service Pack 3 Pentium(R) 4 CPU 2.80GHz 1.0GB RAM Microsoft visual studio 2005(有料) です。 何かアドバイスをよろしくお願いします。

  • CTreeCtrlのCreate関数でエラーになります。

    Visual C++ .NET Win32 プロジェクト で、アプリケーションを作成しようと思っています。 #include<afxwin.h> #include<afxcmn.h> // マルチスレッド CTreeCtrl *m_TreeCtrl; LRESULT CALLBACK int WindowProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { switch(message) { case WM_LBUTTONDOWN: m_TreeCtrl=new CTreeCtrl; m_TreeCtrl->Create(WS_VISIBLE | WS_TABSTOP | WS_CHILD | WS_BORDER | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES | TVS_DISABLEDRAGDROP, CRect(10, 10, 300, 100), CWnd::FromHandle(hWnd), 10000); break; case WM_CREATE: break; case WM_DESTROY: PostQuitMessage(0); break; case WM_PAINT: HDC hDC; PAINTSTRUCT Paint; hDC=BeginPaint(hWnd,&Paint); EndPaint(hWnd,&Paint); } return DefWindowProc(hWnd,message,wParam,lParam); } ・・・ と記述すると、エラーになります。 解決方法を教えてください。

  • ExtTextOut

    WIN32,VISUAL C++2005にて開発してます。 下記のプログラムを実行すると文字化けします。 原因と解決策を教えてください。 ウィンドウの背景色と同じ色にしようとすると文字化けします。 WIN32初心者なので出来るだけ分かりやすくお願いします。 case WM_PAINT: HDC hdc; PAINTSTRUCT paint; wchar_t *str = L"Ver***byKKK"; COLORREF sCOLOR = SetBkColor(hdc,GetBkColor(hdc)); hdc = BeginPaint(hWnd, &paint); ExtTextOut(hdc,120,225,sCOLOR,NULL,(LPCTSTR)str, wcslen(str),NULL); EndPaint(hWnd, &paint); break;

専門家に質問してみよう