Tips of VC++ >
ビットマップ >
ビットマップファイルを読み込む
★ 前へ戻る ★ 次へ進む ★
この文章を読む前にこちらの
「ビットマップファイルの構造」をお読みになると、
より一層理解がしやすくなると思います。
ファイル操作については、CreateFile系のAPIを使用しますが、
特に説明はしません。今後、書く予定はあります。
ここではビットマップファイルを読みこむだけにとどまりたいと思います。
表示するまでを書くべきだとは思いますが、
紙面の都合上…。面倒なだけという理由では決してありませんよ。(^^;
まず、ファイルの構造に目を通してください。 これを順番に読み取っていきます。またも、この表。
ファイルの情報 | BITMAPFILEHEADER構造体 |
ビットマップの情報 | BITMAPINFOHEADER構造体 |
カラーテーブル | RGBQUAD構造体の配列 |
画像ビット | 数字がちゃらちゃら |
BITMAPFILEHEADER構造体には使う情報がないので
飛ばしても言いのですが、一応読み取っておきます。
そんな時間かかんないでしょ。
次のBITMAPINFOHEADERとカラーテーブルは
BITMAPINFOとして読み取りたくなりますが、
配列の要素数が分からないので、先にBITMAPINFOHEADERを
読み取ってカラーテーブルのエントリ数を確かめます。
さらに、この構造体から画像ビットのサイズも算出できます。
その分だけバッファに確保します。
BITMAPINFOHEADER::biCompressionの値で、BI_RGB以外のものは
除外します。圧縮に関してはよく分からないものでして。(汗
BOOL LoadDIB(LPCTSTR in_Path) { // ファイルを開く HANDLE hFile=::CreateFile(in_Path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL); if ( hFile==NULL ) return FALSE; // BITMAPFILEHEADERを読み取る BITMAPFILEHEADER fh; DWORD bytesread; ::ReadFile(hFile,&fh, sizeof(BITMAPFILEHEADER),&bytesread,NULL); if ( bytesread!=sizeof(BITMAPFILEHEADER) || fh.bfType!=0x4D42 ) { ::CloseHandle(hFile); return FALSE; } // BITMAPINFOHEADERを読み取る BITMAPINFOHEADER ih; ::ReadFile(hFile,&ih,sizeof(BITMAPINFOHEADER), &bytesread,NULL); if ( bytesread!=sizeof(BITMAPINFOHEADER) || ih.biSize!=sizeof(BITMAPINFOHEADER) || ih.biCompression!=BI_RGB ) { ::CloseHandle(hFile); return FALSE; } // カラーテーブルのエントリを求める if ( ih.biClrUsed==0 ) if ( ih.biBitCount<16 ) ih.biClrUsed=1<// 1ラインあたりのバイト数とビットマップサイズを計算 DWORD linesize=ih.biWidth*ih.biBitCount/8; if ( linesize%4!=0 ) linesize=((linesize/4)+1)*4; ih.biSizeImage=linesize*::abs(ih.biHeight); // BITMAPINFO構造体を作る PBITMAPINFO pInfo= (PBITMAPINFO)(new BYTE[sizeof(BITMAPINFOHEADER)+ sizeof(RGBQUAD)*ih.biClrUsed]); ::memcpy(pInfo,&ih,sizeof(BITMAPINFOHEADER)); // カラーテーブルの読み取り ::ReadFile(hFile,pInfo->bmiColors, ih.biClrUsed*sizeof(RGBQUAD),&bytesread,NULL); if ( bytesread!=ih.biClrUsed*sizeof(RGBQUAD) ) { ::CloseHandle(hFile); delete [] pInfo; return FALSE; } // 画像ビットの読み取り PBYTE pBits=new BYTE[ih.biSizeImage]; ::ReadFile(in_hFile,pBits, ih.biSizeImage,&bytesread, NULL); // 後片付け ::CloseHandle(hFile); delete [] pInfo; delete [] pBits; if ( bytesread!=ih.biSizeImage ) return FALSE; else return TRUE; }
BITMAPINFOHEADERを読み取るまでは簡単です。
次にカラーテーブルを読み取るためにエントリ数を求めます。
BIMAPINFOHEADER::biClrUsedに値が入っていればそれを使用します。
0の場合もあるので、そのときはbiBitCountから求めることができます。
このときのエントリ数は、2^(ビット数)なので、
シフト演算子で求めると楽です。
for文を使って2を掛けていく方法もありますね。(誰もやらんよ
switch文で全てのパターンを列挙する方法も。(もう何も言うまい
カラーテーブルを読み取る前にビットマップサイズも計算しておきます。
幅×高さで簡単に求まりそうな気がしますが、そう簡単にはいきません。
なぜなら、「ビットマップファイルの構造」でも
書きましたが、画像ビットの幅は4で割りきれるようにして保存されており、
足りない部分は0で埋められるのです。
よって、幅×高さよりも大きくなる場合があります。
さらに、高さは負の値を取ることがあるのにも注意してください。
これは絶対値を使えばいいです。
したがって以上のような記述をせざるを得ません。
幅を4で割って、余りがなければ素直に幅×高さをすればOK。
しかしながら、余りが出た場合、一度4で割った整数の商に
再度4を掛け直せば正しい値が出ます。それに高さの絶対値を掛けます。
そんなに難しくないでしょう。残りはもう慣性で書けます。(ほんとかよ、おい
May 10, 2002