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