EternalWindows
ビットマップ / ビットマップの伸縮

通常、ビットマップを描画する場合はBitBltを呼び出しますが、 拡大や縮小などを施してビットマップを描画したい場合は、StretchBltを呼び出します。

BOOL StretchBlt(
  HDC hdcDest,
  int nXOriginDest,
  int nYOriginDest,
  int nWidthDest,
  int nHeightDest,
  HDC hdcSrc,
  int nXOriginSrc,
  int nYOriginSrc,
  int nWidthSrc,
  int nHeightSrc,
  DWORD dwRop
);

hdcDestは、コピー先のデバイスコンテキストのハンドルを指定します。 nXOriginDestは、コピー先のx座標を指定します。 nYOriginDestは、コピー先のy座標を指定します。 nWidthDestは、コピーする幅を指定します。 nHeightDestは、コピーする高さを指定します。 hdcSrcは、コピー元のデバイスコンテキストのハンドルを指定します。 nXOriginSrcは、コピー元の開始位置を示すx座標を指定します。 nYOriginSrcは、コピー元の開始位置を示すy座標を指定します。 nWidthSrcは、コピー元の幅を指定します。 nWidthSrcよりnWidthDestの方が大きいような場合、 描画されるビットマップの幅はnWidthDestに合わされることになります。 nHeightSrcは、コピー元の幅を指定します。 dwRopは、ラスタオペレーションに関する定数を指定します。

今回のプログラムは、ビットマップを通常、反転、拡大、縮小の順で4回描画します。

#include <windows.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HDC     hdcMem = NULL;
	static HBITMAP hbmpMem = NULL;
	static HBITMAP hbmpMemPrev = NULL;
	
	switch (uMsg) {

	case WM_CREATE: {
		HDC hdc;

		hdc = GetDC(hwnd);
		
		hdcMem = CreateCompatibleDC(hdc);
		hbmpMem = (HBITMAP)LoadImage(NULL, TEXT("sample.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
		if (hbmpMem == NULL) {
			ReleaseDC(hwnd, hdc);
			return -1;
		}

		hbmpMemPrev = (HBITMAP)SelectObject(hdcMem, hbmpMem);

		ReleaseDC(hwnd, hdc);

		return 0;
	}

	case WM_PAINT: {
		int         nWidth;
		int         nHeight;
		HDC         hdc;
		BITMAP      bm;
		PAINTSTRUCT ps;
		
		GetObject(hbmpMem, sizeof(BITMAP), &bm);
		nWidth = bm.bmWidth;
		nHeight = bm.bmHeight;

		hdc = BeginPaint(hwnd, &ps);
		
		StretchBlt(hdc, 0, 0, nWidth, nHeight, hdcMem, 0, 0, nWidth, nHeight, SRCCOPY);

		StretchBlt(hdc, 192, 96, -nWidth, nHeight, hdcMem, 0, 0, nWidth, nHeight, SRCCOPY);

		StretchBlt(hdc, 288, 96, nWidth / 2, nHeight / 2, hdcMem, 0, 0, nWidth, nHeight, SRCCOPY);

		StretchBlt(hdc, 288, 192, nWidth * 2, nHeight * 2, hdcMem, 0, 0, nWidth, nHeight, SRCCOPY);

		EndPaint(hwnd, &ps);

		return 0;
	}

	case WM_DESTROY:
		if (hdcMem != NULL) {
			if (hbmpMem != NULL) {
				SelectObject(hdcMem, hbmpMemPrev);
				DeleteObject(hbmpMem);
			}
			DeleteDC(hdcMem);
		}
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

ビットマップを通常の大きさで描画しているのは次のコードです。

StretchBlt(hdc, 0, 0, nWidth, nHeight, hdcMem, 0, 0, nWidth, nHeight, SRCCOPY);

通常の大きさで描画する場合は、幅や高さに関する引数を特に変更する必要はありません。 続いて、反転して描画するコードを示します。

StretchBlt(hdc, 192, 96, -nWidth, nHeight, hdcMem, 0, 0, nWidth, nHeight, SRCCOPY);

キャラクターを反転して描画します。 反転を表しているのは、第3引数の-nWidthです。 第4引数を-nWidthとすると、縦にも反転するようになります。 続いて、縮小して描画するコードを示します。

StretchBlt(hdc, 288, 96, nWidth / 2, nHeight / 2, hdcMem, 0, 0, nWidth, nHeight, SRCCOPY);

キャラクターを縮小して描画します。 第4引数と第5引数の / 2 が 本来の大きさの半分で描画する意味を持ちます。 最後に拡大して描画するコードを示します。

StretchBlt(hdc, 288, 192, nWidth * 2, nHeight * 2, hdcMem, 0, 0, nWidth, nHeight, SRCCOPY);

キャラクターを拡大して描画します。 第4引数と第5引数の * 2 が 本来の大きさの2倍で描画する意味を持ちます。

伸縮モードについて

StretchBltは、デバイスコンテキストの伸縮モードに基づいて、拡大や縮小を行います。 伸縮モードは、ビットマップの行または列を、 描画先のピクセルとどのように組み合わせるかを定義しています。 伸縮モードを変更するSetStretchBltModeには、次に示す定数を指定することができます。

伸縮モード 意味
HALFTONE コピー元のピクセルをコピー先のピクセルに関連付ける。 コピー先の平均的な色は、コピー元のピクセルの色に近い色になる。 HALFTONEを設定したプログラムは、SetBrushOrgExを呼び出さなくてはならない。 なお、HALFTONEは Windows 95/98/Me ではサポートされていない。
COLORONCOLOR ピクセルを削除する。取り除く点の情報を何らかの形で維持せようとはせず、 単純にそれらの点を削除する。 COLORONCOLORは、デバイスコンテキストのデフォルトの伸縮モードである。
BLACKONWHITE 論理 AND 演算子を使って、残す点の色と取り除く点の色を組み合わせる。
WHITEONBLACK 論理 OR 演算子を使って、残す点の色と取り除く点の色を組み合わせる。

次に、伸縮モードをHALFTONEした場合におけるStretchBltの呼び出し例を示します。

#include <windows.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HDC     hdcMem = NULL;
	static HBITMAP hbmpMem = NULL;
	static HBITMAP hbmpMemPrev = NULL;
	
	switch (uMsg) {

	case WM_CREATE: {
		HDC hdc;

		hdc = GetDC(hwnd);
		
		hdcMem = CreateCompatibleDC(hdc);
		hbmpMem = (HBITMAP)LoadImage(NULL, TEXT("sample.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
		if (hbmpMem == NULL) {
			ReleaseDC(hwnd, hdc);
			return -1;
		}

		hbmpMemPrev = (HBITMAP)SelectObject(hdcMem, hbmpMem);

		ReleaseDC(hwnd, hdc);

		return 0;
	}

	case WM_PAINT: {
		int         nWidth;
		int         nHeight;
		HDC         hdc;
		BITMAP      bm;
		PAINTSTRUCT ps;
		
		GetObject(hbmpMem, sizeof(BITMAP), &bm);
		nWidth = bm.bmWidth;
		nHeight = bm.bmHeight;

		hdc = BeginPaint(hwnd, &ps);
		
		StretchBlt(hdc, 0, 0, nWidth / 2, nHeight / 2, hdcMem, 0, 0, nWidth, nHeight, SRCCOPY);
	
		SetStretchBltMode(hdc, HALFTONE);
		SetBrushOrgEx(hdc, 0, 0, NULL);
		
		StretchBlt(hdc, 0, nHeight, nWidth / 2, nHeight / 2, hdcMem, 0, 0, nWidth, nHeight, SRCCOPY);

		EndPaint(hwnd, &ps);

		return 0;
	}

	case WM_DESTROY:
		if (hdcMem != NULL) {
			if (hbmpMem != NULL) {
				SelectObject(hdcMem, hbmpMemPrev);
				DeleteObject(hbmpMem);
			}
			DeleteDC(hdcMem);
		}
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

1回目のStretchBltは、伸縮モードを変更せずに呼び出しいていますが、 2回目のStretchBltでは伸縮モードをHALFTONEに指定して呼び出しています。 この結果、2回目のStretchBltで表示されたビットマップの方が、 1回目よりも見栄え良く描画されていると思われます。 HALFTONEを指定してSetStretchBltModeを呼び出した後は、 SetBrushOrgExを呼び出すようにします。



戻る