DIB操作関数を作る

DIBを扱う時には、ビットマップのバッファとBITMAPINFO 構造体へのポインタが必要でした。描く時にも、いくつもの引数を付けてStretchDIBitsを呼び出すなどけっこう大変です。毎回、このような方法でDIBを管理するのも面倒なので、DIBを操作する関数を作ってみましょう。まず今回はフルカラーDIBを作る関数、画面(デバイスコンテキスト)に表示する関数、最後に「後片付け」をする関数、の3つの関数を作ってみました。

・DIBの作成

DIBの実体はポインタの集まりです。
まず、そのポインタやDIBに関する情報をまとめた構造体を定義しましょう。DIBの操作では、この構造体を作ってその構造体を操作するようにするのです。構造体の名前はHIBMP にしました。

typedef struct dibstruct { // DIB構造体

	int width,height; // 大きさ

	BYTE* lpBMP; // ビットマップバッファへのポインタ
	BITMAPINFO* lpInfo; // BITMAPINFO 構造体へのポインタ
	BITMAPINFOHEADER* lpInfoh; //BITMAPINFOHEADER 構造体へのポインタ

} HIBMP;

このうち、最後のBITMAPINFOHEADERへのポインタは操作上は必要ないのですが、DIB(正確にはBITMAPINFO)の「内部」に作成するものなので、とりあえず構造体の中に入れておきました。どこにあるかわからない構造体を使うのは、ちょっと気味が悪いですから。

さて、DIBを作る時には、まずDIBの実体であるlpBMPlpInfolpInfoh にメモリを割り当てます。

 hb.lpBMP=GlobalAlloc(GMEM_FIXED,width*height*3);
 hb.lpInfoh=GlobalAlloc(GMEM_FIXED,sizeof(BITMAPINFOHEADER));
 hb.lpInfo=GlobalAlloc(GMEM_FIXED,sizeof(BITMAPINFO));

構造体のポインタには構造体のサイズ分だけ割り当てておけば良いでしょう。次にlpInfohのフィールドを初期化してlpInfoに入れるのですが、今回はフルカラーなので問題になるのは大きさだけです。大きさを横widthheightHIBMP の名前をhbとするなら、

 hb.lpInfoh->biSize=sizeof(BITMAPINFOHEADER); // BITMAPINFOHEADER構造体
 hb.lpInfoh->biWidth=width;
 hb.lpInfoh->biHeight=height;
 hb.lpInfoh->biPlanes=1;
 hb.lpInfoh->biBitCount=24;
 hb.lpInfoh->biCompression=BI_RGB;
 hb.lpInfoh->biSizeImage=0;
 hb.lpInfoh->biXPelsPerMeter=0;
 hb.lpInfoh->biYPelsPerMeter=0;
 hb.lpInfoh->biClrUsed=0;
 hb.lpInfoh->biClrImportant=0;

 hb.lpInfo->bmiHeader=*hb.lpInfoh;

という感じになります。ついでに、lpBMPZeroMemory(hb.lpBMP,width*height*3); として初期化しておきましょう。

ただし、この関数ではバッファの調整(横方向の大きさは4の倍数でなければならない)をしていないので、とりあえず今回はwidthに4の倍数を指定してください。

・横width、縦height のフルカラーDIB作成関数

  HIBMP CreateIBMP(int width,int height) { // DIB作成

	 HIBMP hb;

	 hb.width=width; // 大きさ設定
	 hb.height=height;

	 hb.lpBMP=GlobalAlloc(GMEM_FIXED,width*height*3); // メモリ割り当て
	 hb.lpInfoh=GlobalAlloc(GMEM_FIXED,sizeof(BITMAPINFOHEADER));
	 hb.lpInfo=GlobalAlloc(GMEM_FIXED,sizeof(BITMAPINFO));

	 ZeroMemory(hb.lpBMP,width*height*3); // ビットマップをクリア

	 hb.lpInfoh->biSize=sizeof(BITMAPINFOHEADER); // BITMAPINFOHEADER 構造体
	 hb.lpInfoh->biWidth=64;
	 hb.lpInfoh->biHeight=64;
	 hb.lpInfoh->biPlanes=1;
	 hb.lpInfoh->biBitCount=24;
	 hb.lpInfoh->biCompression=BI_RGB;
	 hb.lpInfoh->biSizeImage=0;
	 hb.lpInfoh->biXPelsPerMeter=0;
	 hb.lpInfoh->biYPelsPerMeter=0;
	 hb.lpInfoh->biClrUsed=0;
	 hb.lpInfoh->biClrImportant=0;

	 hb.lpInfo->bmiHeader=*hb.lpInfoh;

	 return hb; // 作成したDIB構造体を返す

 }

・描画と後始末

作成したDIBを描く時にはStretchDIBitsを使います。このAPI を使って指定した位置にDIBを描く関数を作りましょう。引数として、DIB構造体(HIBMP)、描画対象となるデバイスコンテキストのハンドル、そして描く位置をとるようにしました。

 void drawIBMPtoDC(HIBMP hb,HDC hdc,int x,int y) { // DIB表示

	 StretchDIBits(hdc,x,y,hb.width,hb.height,0,0,hb.width,hb.height,
		hb.lpBMP,hb.lpInfo,DIB_RGB_COLORS,SRCCOPY);

 }

これで、構造体hb に格納されたDIBをhdc(x,y)に描く事が出来ます。さらに、「後始末」は確保したメモリを解放するためのもので、

 void FreeIBMP(HIBMP hb) { // 後始末

	 GlobalFree(hb.lpBMP); // メモリを解放
	 GlobalFree(hb.lpInfo);
	 GlobalFree(hb.lpInfoh);

 }

とメモリを解放するようにします。

さあ、これでDIBの基本的な操作が出来るようになりました。ソースをダウンロードしたら、lpBMPをいじって「絵」を描いたり、描く位置を変えるなどして遊んでみてください。操作がHIBMP型構造体と直感的な引数をとる関数に整理されたので、 DIBの扱いがだいぶ楽になりましたね。

プログラムソース表示
戻る