Win32APIのバッファリングについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
keito
記事: 20
登録日時: 2年前

Win32APIのバッファリングについて

#1

投稿記事 by keito » 1ヶ月前

時々お世話になっています。
C/C++/Win32APIはまだまだ初心者です。

こちらのサイトのソースコードを確認・変更しながらダブルバッファリングの処理を勉強しているのですが、
以下の2点がどうしてもうまくいきません。
  • 子ウィンドウ(ボタンなど)も一緒にダブルバッファリングの処理する
  • BitBlt関数でコピー先長方形の領域を可変にする
以下のソースコードは画面全体を灰色の四角で塗りつぶし、1つボタンを配置するソースコードですが、四角は画面全体まで描画されてない場合があり(最大化した際も画面全体が塗りつぶされない)、またボタンはちらついてしまいます。
どうしたらいいでしょうか……?

コード:

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpCmdLine, int nCmdShow) {

	WNDCLASSEX wcx;
	MSG msg;
	HWND hWnd, hButton;
	LPCSTR szTitle = TEXT("Sample");
	LPCSTR szWindowClass = TEXT("SAMPLE");

	ZeroMemory(&wcx, sizeof(wcx));
	wcx.cbSize = sizeof(WNDCLASSEX);
	wcx.style = 0;
	wcx.lpfnWndProc = WndProc;
	wcx.cbClsExtra = 0;
	wcx.cbWndExtra = 0;
	wcx.hInstance = hInstance;
	wcx.hIcon = 0;
	wcx.hIconSm = 0;
	wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wcx.lpszMenuName = NULL;
	wcx.lpszClassName = szWindowClass;

	if (!RegisterClassEx(&wcx)) { return 0; }

	hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL);

	hButton = CreateWindow(TEXT("BUTTON"), TEXT("BUTTON"), WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
		100, 100, 100, 25, hWnd, NULL, hInstance, NULL);

	if (!hWnd) { return 0; }

	while (GetMessage(&msg, NULL, 0, 0) != 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);

	}

	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam) {

	static BITMAPINFO   bmpInfo;    // ビットマップ情報
	static LPDWORD      lpPixel;    // ピクセル配列
	static HBITMAP      hBitmap;    // ビットマップ
	static HDC          hMemDC;     // オフスクリーン
	WINDOWINFO winInfo;
	GetWindowInfo(hWnd, &winInfo);

	switch (uMessage) {
	case WM_CREATE:
	{
		HDC hDC;

		// DIB構造体の初期化
		bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bmpInfo.bmiHeader.biWidth = winInfo.rcClient.right - winInfo.rcClient.left;
		bmpInfo.bmiHeader.biHeight = winInfo.rcClient.bottom - winInfo.rcClient.top;
		bmpInfo.bmiHeader.biPlanes = 1;
		bmpInfo.bmiHeader.biBitCount = 32;
		bmpInfo.bmiHeader.biCompression = BI_RGB;

		// DIBセクションの作成
		hDC = GetDC(hWnd);
		hMemDC = CreateCompatibleDC(hDC);
		hBitmap = CreateDIBSection(NULL, &bmpInfo, DIB_RGB_COLORS, (LPVOID*)&lpPixel, NULL, 0);
		SelectObject(hMemDC, hBitmap);
		SelectObject(hMemDC, GetStockObject(DC_PEN));
		SelectObject(hMemDC, GetStockObject(DC_BRUSH));
		ReleaseDC(hWnd, hDC);
	}
	break;
	case WM_SHOWWINDOW:
		SetTimer(hWnd, 1, 10, NULL);
		break;
	case WM_PAINT:
	{
		PAINTSTRUCT     ps;
		HDC             hDC;

		// DIBセクションの描画
		hDC = BeginPaint(hWnd, &ps);
		SelectObject(hMemDC, GetStockObject(GRAY_BRUSH));
		Rectangle(hMemDC, 0, 0, bmpInfo.bmiHeader.biWidth, bmpInfo.bmiHeader.biHeight);
		BitBlt(hDC, 0, 0, bmpInfo.bmiHeader.biWidth, bmpInfo.bmiHeader.biHeight, hMemDC, 0, 0, SRCCOPY);
		EndPaint(hWnd, &ps);
	}
	break;
	case WM_ERASEBKGND:
		// 何も処理しない⇒塗り潰した
		return 1;
	case WM_SIZING:
	case WM_MDIMAXIMIZE:
		bmpInfo.bmiHeader.biWidth = winInfo.rcClient.right - winInfo.rcClient.left;
		bmpInfo.bmiHeader.biHeight = winInfo.rcClient.bottom - winInfo.rcClient.top;
		hBitmap = CreateDIBSection(NULL, &bmpInfo, DIB_RGB_COLORS, (LPVOID*)&lpPixel, NULL, 0);
		InvalidateRect(hWnd, NULL, TRUE);
		break;
	case WM_TIMER:
		//ダブルバッファリングの動作を確認する為のタイマー
		InvalidateRect(hWnd, NULL, TRUE);
		break;
	case WM_CLOSE:
		// DIBセクションの破棄
		DeleteDC(hMemDC);
		DeleteObject(hBitmap);
		DestroyWindow(hWnd);
		break;
	case WM_DESTROY:
		KillTimer(hWnd, 1);
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, uMessage, wParam, lParam);
	}
	return 0;
}

かずま

Re: Win32APIのバッファリングについて

#2

投稿記事 by かずま » 1ヶ月前

ボタンがちらつくのは、タイマーで 10ミリ秒に 1回という
高頻度でウィンドウの再描画をさせているからなので、
SetTimer() の引数を 100 ぐらいにしてみてはいかがでしょう?

最大化した際に発生するウィンドウメッセージは、WM_SIZING でも
WM_MDIMAXIMIZE でもないので、代わりに WM_SIZE にしましょう。

そこで CreateDIBSection していますが、hMemDC のビットマップを
新たに作成されたものに置き換えるために CreateDIBSection(); の
後に、次のコードが必要だと思います。

コード:

        {
		    HGDIOBJ obj = SelectObject(hMemDC, hBitmap);
            DeleteObject(obj);
		}
case WM_PAINT: では、BitBlt により mMemDC の内容のコピー
だけにして、Rectangle などによる mMemDC の内容変更は、
必要な時に 1回だけにしたほうがよいと思うので、WM_CREATE と
WM_SIZE でRectangle を実行しましょう。

keito
記事: 20
登録日時: 2年前

Re: Win32APIのバッファリングについて

#3

投稿記事 by keito » 1ヶ月前

ありがとうございます、10ms毎に再描画は少し無理があったんですね……

コード:

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam) {

	static BITMAPINFO   bmpInfo;    // ビットマップ情報
	static LPDWORD      lpPixel;    // ピクセル配列
	static HBITMAP      hBitmap;    // ビットマップ
	static HDC          hMemDC;     // オフスクリーン
	static WINDOWINFO winInfo;
	static HGDIOBJ obj;
	GetWindowInfo(hWnd, &winInfo);

	switch (uMessage) {
	case WM_CREATE:
	{
		HDC hDC;

		// DIB構造体の初期化
		bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bmpInfo.bmiHeader.biWidth = winInfo.rcClient.right - winInfo.rcClient.left;
		bmpInfo.bmiHeader.biHeight = winInfo.rcClient.bottom - winInfo.rcClient.top;
		bmpInfo.bmiHeader.biPlanes = 1;
		bmpInfo.bmiHeader.biBitCount = 32;
		bmpInfo.bmiHeader.biCompression = BI_RGB;

		// DIBセクションの作成
		hDC = GetDC(hWnd);
		hMemDC = CreateCompatibleDC(hDC);
		hBitmap = CreateDIBSection(NULL, &bmpInfo, DIB_RGB_COLORS, (LPVOID*)&lpPixel, NULL, 0);
		SelectObject(hMemDC, hBitmap);
		SelectObject(hMemDC, GetStockObject(DC_PEN));
		SelectObject(hMemDC, GetStockObject(DC_BRUSH));
		ReleaseDC(hWnd, hDC);
	}
	break;
	case WM_SHOWWINDOW:
		SetTimer(hWnd, 1, 33, NULL);
		break;
	case WM_PAINT:
	{
		PAINTSTRUCT     ps;
		HDC             hDC;

		// DIBセクションの描画
		hDC = BeginPaint(hWnd, &ps);
		SelectObject(hMemDC, GetStockObject(GRAY_BRUSH));
		Rectangle(hMemDC, 0, 0, bmpInfo.bmiHeader.biWidth, bmpInfo.bmiHeader.biHeight);
		BitBlt(hDC, 0, 0, bmpInfo.bmiHeader.biWidth, bmpInfo.bmiHeader.biHeight, hMemDC, 0, 0, SRCCOPY);
		EndPaint(hWnd, &ps);
	}
	break;
	case WM_ERASEBKGND:
		// 何も処理しない⇒塗り潰した
		return 1;
	case WM_SIZE:
	case WM_SIZING:
		bmpInfo.bmiHeader.biWidth = winInfo.rcClient.right - winInfo.rcClient.left;
		bmpInfo.bmiHeader.biHeight = winInfo.rcClient.bottom - winInfo.rcClient.top;
		hBitmap = CreateDIBSection(NULL, &bmpInfo, DIB_RGB_COLORS, (LPVOID*)&lpPixel, NULL, 0);
		obj = SelectObject(hMemDC, hBitmap);
		DeleteObject(obj);
		InvalidateRect(hWnd, NULL, TRUE);
		break;
	case WM_TIMER:
		//ダブルバッファリングの動作を確認する為のタイマー
		InvalidateRect(hWnd, NULL, TRUE);
		break;
	case WM_CLOSE:
		// DIBセクションの破棄
		DeleteDC(hMemDC);
		DeleteObject(hBitmap);
		DestroyWindow(hWnd);
		break;
	case WM_DESTROY:
		KillTimer(hWnd, 1);
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, uMessage, wParam, lParam);
	}
	return 0;
}

ISLe
記事: 2610
登録日時: 7年前
連絡を取る:

Re: Win32APIのバッファリングについて

#4

投稿記事 by ISLe » 1ヶ月前

ボタンのちらつき対策にはウィンドウスタイルにWS_CLIPCHILDRENを加えてください。
ウィンドウズが勝手にボタンを避けて描画してくれるので高速描画でもへっちゃらです。

返信

“C言語何でも質問掲示板” へ戻る