>> WINAPI入門トップに戻る

ビットマップ画像の表示

前回まではブラシやペンを使って描画していましたが、
今回はビットマップ画像を描画する方法をご紹介します。
ビットマップしか描画できないのでご注意下さい。

まずビットマップ画像を用意して下さい。
そしてそのファイルをプロジェクトファイルがあるフォルダと同じ場所に置いてください。
まずファイルを読み込む為にLoadBitmap関数を使います。

HBITMAP LoadBitmap(
HINSTANCE hInstance, // アプリケーションインスタンスのハンドル
LPCTSTR lpBitmapName // ビットマップリソース名
);

第二引数はリソースファイルで指定したリソース名を指定します。
例えば、FILENAMEという名前のリソース名を指定する場合は、
リソースファイルに

FILENAME BITMAP "test.bmp"

と書いてください。
最初のFILENAMEというのがそのリソース名、
BITMAPというのがファイルがビットマップファイルであることを示しています。
次にダブルクオテーションで囲んで、ファイル名を書きます。
それぞれスペースで区切って書いてください。
ここで取り込んだビットマップファイルはコンパイル時に、
実行ファイル(exeファイル)に取り込まれますので、もしソフトを作って配布する場合は、
いちいちビットマップファイルも一緒に配布する必要はありません。
リソースファイルの作成について分からない人は、12章を参照して下さい。
この関数は成功すると、HBITMAP型のハンドルを返します。

次にビットマップファイルの画像の幅と高さを取得します。
あらかじめ分かっていれば取得する必要はありませんが、いちいち調べるのが面倒なので関数で取得します。
関数には、GetObject関数を使います。

int GetObject(
HGDIOBJ hgdiobj, // グラフィックオブジェクトのハンドル
int cbBuffer, // オブジェクト情報を格納するバッファのサイズ
LPVOID lpvObject // オブジェクト情報を格納するバッファ
);

第一引数にはLoadBitmapを実行して得たビットマップハンドルを指定します。
オブジェクト情報を格納するバッファは、BITMAP型の構造体を使います。
サイズはsizeof演算子でBITMAP構造体を指定すれば取得できます。
これを実行すると、指定したBITMAP構造体に画像のサイズなどの情報が格納されます。

ビットマップ構造体は以下のような構成になっています。

typedef struct tagBITMAP { /* bm */
int bmType;
int bmWidth;
int bmHeight;
int bmWidthBytes;
BYTE bmPlanes;
BYTE bmBitsPixel;
LPVOID bmBits;
} BITMAP;

ここで使うのは、bmWidthとbmHeightメンバです。
それぞれ幅と高さが格納されてます。

次にメモリデバイスコンテキストというものを作成します。
今まで取得していたデバイスコンテキストはウィンドウのデバイスコンテキストでしたが、
メモリ上に別のデバイスコンテキストを作成します。
作成するには、CreateCompatibleDC関数を使います。

HDC CreateCompatibleDC(
HDC hdc // デバイスコンテキストのハンドル
);

引数にはウィンドウのデバイスコンテキストを指定するか、NULLを指定します。
NULLを指定すると、ウィンドウのデバイスコンテキストと互換性のあるデバイスコンテキストが作成されます。
成功すると、作成したメモリデバイスコンテキストのハンドルを返します。
作成した直後のメモリデバイスコンテキストは高さ1幅1ピクセルしかないただのモノクロのものです。
ですので、このメモリデバイスコンテキストに先程取得したビットマップを設定して、
そのビットマップの画像とサイズを持つメモリデバイスコンテキストに変化させてやります。
これにはSelectObject関数を使います。

HGDIOBJ SelectObject(
HDC hdc, // デバイスコンテキストのハンドル
HGDIOBJ hgdiobj // オブジェクトのハンドル
);

第一引数には作成したメモリデバイスコンテキストを、
ハンドルには先程取得したビットマップハンドルを指定します。
これでビットマップ画像が描画されているメモリデバイスコンテキストが作成できました。

次にこれをウィンドウに描画するために、このメモリデバイスコンテキストの画像データを
ウィンドウのデバイスコンテキストに描画する必要があります。
これには、BitBlt関数を使います。

BOOL BitBlt(
HDC hdcDest, // コピー先デバイスコンテキストのハンドル
int nXDest, // コピー先長方形の左上隅の x 座標
int nYDest, // コピー先長方形の左上隅の y 座標
int nWidth, // コピー先長方形の幅
int nHeight, // コピー先長方形の高さ
HDC hdcSrc, // コピー元デバイスコンテキストのハンドル
int nXSrc, // コピー元長方形の左上隅の x 座標
int nYSrc, // コピー元長方形の左上隅の y 座標
DWORD dwRop // ラスタオペレーションコード
);

第一引数はコピー先のデバイスコンテキストハンドルなので、ウィンドウのデバイスコンテキストハンドルを指定します。
第二引数はコピー先でバイスコンテキストのどの部分へ描画するかのX座標を指定します。
第三引数はそのy座標。
第四引数はコピーする際の幅。
第五引数はコピーする際の高さ。
第六引数はコピー元のデバイスコンテキストハンドルなので、メモリデバイスコンテキストハンドルを指定します。
第七引数はコピー元のどの部分から描画するかのX座標を指定します。
第八引数はそのY座標。
第九引数はコピーする際のオプションを指定します。
オプションはかなりの数があります。

BLACKNESS	物理パレットのインデックス 0 に対応する色(既定の物理パレットでは黒)で、コピー先の長方形を塗りつぶします。
CAPTUREBLT	Windows 98 と Windows 2000:アプリケーションのウィンドウより上位にレイヤー化されているすべてのウィンドウを、最終的なイメージに含めます。既定では、アプリケーションのウィンドウだけがイメージに含まれます。
DSTINVERT	コピー先長方形の色を反転します。
MERGECOPY	論理 AND 演算子を使って、コピー元の色とコピー先の色を組み合わせます。
MERGEPAINT	論理 OR 演算子を使って、コピー元の色を反転した色と、コピー先の色を組み合わせます。
NOMIRRORBITMAP	Windows 98 と Windows 2000:ビットマップのミラーリング(ミラーイメージを作成すること)を防止します。
NOTSRCCOPY	コピー元の色を反転して、コピー先へコピーします。
NOTSRCERASE	論理 OR 演算子を使って、コピー元の色とコピー先の色を組み合わせ、さらに反転します。
PATCOPY	指定したパターンをコピー先へコピーします。
PATINVERT	論理 XOR 演算子を使って、指定したパターンの色と、コピー先の色を組み合わせます。
PATPAINT	論理 OR 演算子を使って、指定したパターンの色と、コピー元の色を反転した色を組み合わせます。さらに論理 OR 演算子を使って、その結果と、コピー先の色を組み合わせます。
SRCAND	論理 AND 演算子を使って、コピー元の色とコピー先の色を組み合わせます。
SRCCOPY	コピー元長方形をコピー先長方形へそのままコピーします。
SRCERASE	論理 AND 演算子を使って、コピー先の色を反転した色と、コピー元の色を組み合わせます。
SRCINVERT	論理 XOR 演算子を使って、コピー元の色とコピー先の色を組み合わせます。
SRCPAINT	論理 OR 演算子を使って、コピー元の色とコピー先の色を組み合わせます。
WHITENESS	物理パレットのインデックス 1 に対応する色(既定の物理パレットでは白)で、コピー先の長方形を塗りつぶします。


ですが基本的に使うのはそのままコピーするSRCCOPYしか使わないので、
他は興味があったら使ってみるぐらいでいいです。

これで描画は出来ました。
最後に、作成したメモリデバイスコンテキストを、DeleteDC関数で破棄、
読み込んだビットマップをDeleteObject関数で破棄する必要があります。

BOOL DeleteDC(
HDC hdc // デバイスコンテキストのハンドル
);
BOOL DeleteObject(
HGDIOBJ hObject // グラフィックオブジェクトのハンドル
);

それぞれメモリデバイスコンテキストのハンドル、ビットマップハンドルを指定します。

画像の描画の流れは以上になります。
これをコードで表したものが以下になります。
●リソーススクリプト

FILENAME BITMAP "test.bmp"


●ソースコード

#include <windows.h>
#include <stdio.h>

#define MSG(m) {\
	MessageBoxA(NULL,m,NULL,MB_OK);}

//ウィンドウハンドル
HWND hwnd;
//インスタンスハンドル
HINSTANCE hinst;

//ウィンドウ横幅
#define WIDTH 500
#define HEIGHT 300



LRESULT CALLBACK WinProc(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp)
{
	HDC hdc;

	//ビットマップハンドル
	static HBITMAP hb;

	PAINTSTRUCT ps;
	
	//ビットマップ構造体
	static BITMAP bp;

	//縦、横
	static int width,height;

	//メモリデバイスコンテキスト
	static HDC mhdc;

	switch(msg){
		case WM_DESTROY:
			//ビットマップハンドルを削除
			DeleteObject(hb);
			//メモリデバイスコンテキストを破棄
			DeleteDC(mhdc);

			PostQuitMessage(0);
			return 0;

		case WM_CREATE:
			
			//ファイル読み込み
			hb=LoadBitmap(hinst,"FILENAME");


			//画像サイズ取得
			GetObject(hb,sizeof(BITMAP),&bp);
			width=bp.bmWidth;
			height=bp.bmHeight;

			//メモリデバイスコンテキストを作成
			mhdc = CreateCompatibleDC(NULL);

			
			//メモリデバイスコンテキストにビットマップを設定
			SelectObject(mhdc,hb);


		case WM_PAINT:

			hdc = BeginPaint(hwnd,&ps);

			//描画
			BitBlt(hdc,0,0,width,height,mhdc,0,0,SRCCOPY);

			EndPaint(hwnd,&ps);



			return 0;

		
	}
	return DefWindowProc(hwnd,msg,wp,lp);
}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
{
	MSG msg;
	WNDCLASS wc;

	wc.style=CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc=WinProc;
	wc.cbClsExtra=wc.cbWndExtra=0;
	wc.hInstance=hInstance;
	wc.hCursor=wc.hIcon=NULL;
	wc.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszClassName="test";
	wc.lpszMenuName=NULL;
	
	if(!RegisterClass(&wc)){
		MSG("クラスの登録失敗");
		return -1;
	}

	
	//インスタンスハンドル
	hinst=hInstance;

	hwnd=CreateWindowA("test","テストウィンドウ",WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
		0,0,400,400,NULL,NULL,hInstance,NULL);


	if(hwnd==NULL){
		MSG("ウィンドウ作成失敗");
		return -1;
	}


	//エラーチェック用変数
	int check;

	while(check=GetMessage(&msg,NULL,0,0)){
		if(check==-1){
			break;
		}
		DispatchMessage(&msg);
	}

	//クラス解放
	UnregisterClass("test",hinst);

	return 0;

}

今回の画像は、

の画像を使用しました。
これをプロジェクトファイルがあるフォルダと同じ場所に置いて実行すると、以下のような画面が現われます。


WM_CREATEの部分で、ビットマップの取得と画像サイズの取得、
メモリでバイスコンテキストの作成と、ビットマップ画像のメモリデバイスコンテキストへの設定、
を行ってます。
WM_PAINT部分でBitBlt関数を使って、画像そのままの大きさでそのままコピーしています。
WM_DESTROYの部分で、作成したメモリでバイスコンテキストとビットマップハンドルを破棄しています。

以上が、ビットマップ画像を描画する流れです。
理解できましたでしょうか?流れを掴めば慣れてくると思いますので頑張って下さい。

次回はデバイス情報を取得する方法を説明します。


>> 【デバイス情報の取得】に進む
>> WINAPI入門トップに戻る
●更新履歴
2016/08/16 Java入門ページにページを幾つか追加
2016/04/08 Java入門ページ作成
2016/03/09 メニューレイアウト変更。ブラウザキャッシュのクリアをお願い致します。
2016/03/09 PDOトランザクション、自動コミットモードをオフ追加
2016/03/09 PDO 例外処理 try catch追加
2016/03/09 PDO update文実行追加
2016/03/09 PDO delete文実行追加
2016/03/09 PDO insert文実行追加
2016/03/09 PDO selectでデータを取得、fetchAll、queryメソッド追加
2016/03/09 PDO bindValueとbindParamの違い追加
2016/03/09 PDO prepare プリペアドステートメントの使い方追加
2016/03/04 ソースコードをクリップボードにコピーする機能を追加
2016/03/04 C言語、C++のページのソースコードを一部修正
2014/01/31 C言語関数一覧ページに11ページほど追加
2014/01/31 C言語関数一覧ページに30ページほど追加
2014/01/30 C言語関数一覧ページ作成中
2013/07/01 レイアウト変更に伴いブラウザキャッシュのクリアをお願いします。
2013/07/01 MySQL入門ページ作成
2013/07/01 PHP入門ページにSQLite学習項目追加
2013/06/25 ドメイン変更、レイアウトを一部変更
2013/03/14 レイアウトを一部変更
2012/08/13 C言語よくある課題・宿題ページ開設!
2012/08/13 シューティングゲーム作成第33章追加!
2012/08/11 ドメイン変更&サーバ移設完了
2012/04/21 シューティングゲームプログラミング第2,3章の内容を修正
2012/04/19 シューティングゲームプログラミング第2章の内容を修正
2012/04/03 Googleカスタム検索を設置!
2012/04/03 シューティングゲームプログラミング第32章追加!
2012/04/03 シューティングゲームプログラミング第31章追加!
2012/03/31 サイトをリニューアルしました!
2012/03/25 シューティングゲームプログラミング第30章追加!
2012/03/19 シューティングゲームプログラミング第29章追加!
2012/03/16 シューティングゲームプログラミング第28章追加!
2012/02/27 シューティングゲームプログラミング第27章追加!
2012/02/03 シューティングゲームプログラミング第26章追加!
2012/01/31 シューティングゲームプログラミング第25章追加!
2012/01/20 シューティングゲームプログラミング第23,24章追加!
2012/01/11 シューティングゲームプログラミング第22章追加!
2012/01/05 トップページ、ゲームプログラミング関連のトップページのデザインを変更
2012/01/04 シューティングゲームプログラミング第21章追加!
2012/01/01 シューティングゲームプログラミング第20章追加!
2011/12/25 シューティングゲームプログラミング第19章追加!
2011/12/22 シューティングゲームプログラミング第18章追加!
2011/12/18 シューティングゲームプログラミング第17章追加!
2011/12/17 シューティングゲームプログラミングページOPEN!
2011/11/21 ゲームプログラミングページOPEN!
2011/11/21 サイトデザインを大幅に変更
2011/11/17 TOPページのデザインを変更。相互リンクページに、複数サイト追加。
2011/11/06 WINAPI学習ページ(33~36章)追加
2011/11/05 WINAPI学習ページ(20~32章)追加
2011/10/27 WINAPI学習ページ(14~19章)追加
2011/10/21 WINAPI学習ページ(13章)追加
2011/10/21 サイトマップ、連絡ページ追加
2011/10/17 WINAPI学習ページ(6~11章)追加
2011/10/16 WINAPI学習ページ(1~5章)追加
2011/10/13 全体のレイアウト変更
2011/10/07 PHP学習ページ(8~11章)追加
2011/10/06 PHP学習ページ(1~7章)作成
2011/10/06 JavaScriptリファレンスページ作成
2011/10/05 C言語学習ページ発展編(10~14章)追加
2011/10/04 C言語学習ページ発展編(1~9章)追加。
2011/10/03 HTML/CSSリファレンスのページ追加。(個々の詳細ページは作成中)
2011/09/30 HTML学習ページ(8章)追加
2011/09/29 JavaScript学習ページ(12~17章)追加
2011/09/28 JavaScript学習ページ(1~11章)追加
2011/09/27 HTML学習ページ(4~7章)追加
2011/09/26 C言語学習ページ(27章)追加、C++学習ページ(17章)、HTML学習ページ(1~3章)追加
2011/09/25 C言語学習ページ(23~26章)を追加
2011/09/24 C++学習ページ(9~16章)追加
2011/09/23 C++学習ページ(3~8章)追加
2011/09/22 C言語の学習ページ(22章)とC++学習ページ(1~2章)追加
2011/09/21 C言語の学習ページ(15章~21章)を追加
2011/09/20 C言語の学習ページ(10章~14章)を追加
2011/09/19 サイト作成(随時更新予定)