BMPファイルをDIBSectionとして読み込む

概要:24/8ビットBMPファイルから32/24ビットDIBSectionを作成する

前節では24/8ビットBMPファイルを32ビットDIBSectionとして読み込む関数を作成しました。
今回はより汎用性のある関数を目指しますが、
8ビットDIBSectionは作成できないので、前節の関数を少しだけ拡張して、
32ビットDIBSectionの他に24ビットDIBSectionも作成可能にして、選択できるようにしましょう。

今回の解説を読む前に、前節の内容をきちんと理解しておいて下さい。

■関数の設計

関数名 → CreateDIBSectionFromFile248
引数 → ファイル名 , ビットマップハンドルのポインタ , ピクセル列のポインタのポインタ , BITMAPINFO構造体のポインタ ,
        作成するDIBSectionのビット数を指定するCDF列挙定数
戻り値 → 一行分のバイト数(成功) or 負値(失敗)

int CreateDIBSectionFromFile248(
    char *lpFileName,HBITMAP *lphBmp,LPVOID *lppPixel,BITMAPINFO *lpBmpInfo,CDF type)
{
  ……
}

読み込みに対応するBMPファイルのビット数は24/8ビットです。
作成する DIBSection は32/24ビットです。どちらにするかは type 列挙定数で指定します。

*lphBmp , *lppPixel , *lpBmpInfo には作成した DIBSection の情報が格納されます。
関数はピクセル列のメモリ領域を確保しますが、BITMAPINFO構造体のメモリ領域は確保しません。

CDF列挙型は次のように定義しました。

typedef enum tagCDF{BY=24,DW=32}CDF;
// BY  : *lppPixel は LPBYTE型 である(24bitsDIBSection作成)
// DW  : *lppPixel は LPDWORD型 である(32bitsDIBSection作成)

■BMPファイルの情報を取得するまで

前節と同じ所までのプログラムを以下に示します。

HANDLE fh;
DWORD dwFileSize,dwReadSize;
LPBYTE lpbuf;

LPBITMAPFILEHEADER lpbmpfh;
LPBITMAPINFO lpbmpInfo;

LPBYTE lpbmpPixel;
int bitCount,iWidth,iHeight;

fh=CreateFile(lpFileName,GENERIC_READ,0,NULL,
    OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(fh==INVALID_HANDLE_VALUE){
    MessageBox(NULL,"ファイルが開けません",lpFileName,MB_OK);
    return -1;
}
dwFileSize=GetFileSize(fh,NULL);
lpbuf=(LPBYTE)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,dwFileSize);
ReadFile(fh,lpbuf,dwFileSize,&dwReadSize,NULL);
CloseHandle(fh);

lpbmpfh=(LPBITMAPFILEHEADER)lpbuf;
lpbmpInfo=(LPBITMAPINFO)(lpbuf+sizeof(BITMAPFILEHEADER));
bitCount=lpbmpInfo->bmiHeader.biBitCount;
if(lpbmpfh->bfType!=('M'<<8)+'B' || (bitCount!=24 && bitCount!=8)){
    HeapFree(GetProcessHeap(),0,lpbuf);
    MessageBox(NULL,"24 or 8 ビットBMPファイルしか読み込めません",lpFileName,MB_OK);
    return -2;
}

lpbmpPixel=lpbuf + lpbmpfh->bfOffBits;
iWidth=lpbmpInfo->bmiHeader.biWidth;
iHeight=lpbmpInfo->bmiHeader.biHeight;

■一行分のバイト数を計算する

BMPファイルとDIBSectionはユーザーの指示によってビット数が違う事があるので、
両方の一行分のバイト数を別々に計算します。
これはピクセル列の詰め替えの際に、32/24ビットの処理を統一する仕掛けです。

一行分のバイト数は関数が成功した時の返却値です。

int iLenSrc,iLenDst;

if(iWidth*(bitCount/8)%4) iLenSrc=iWidth*(bitCount/8)+(4-iWidth*(bitCount/8)%4);
else iLenSrc=iWidth*(bitCount/8);
if(iWidth*(type/8)%4) iLenDst=iWidth*(type/8)+(4-iWidth*(type/8)%4);
else iLenDst=iWidth*(type/8);

■CreateDIBSection関数を実行するまで

前節と違うのは、ビット数が type 列挙定数で与えられている事だけです。

CopyMemory(lpBmpInfo,&lpbmpInfo->bmiHeader,sizeof(BITMAPINFOHEADER));
lpBmpInfo->bmiHeader.biBitCount=type;

*lphBmp=CreateDIBSection(NULL,lpBmpInfo,DIB_RGB_COLORS,(void**)lppPixel,NULL,0);

■ピクセル列の詰め替え&関数の終了処理

32ビットと24ビットの違いは、4バイト毎に格納するか、3バイト毎に格納するか、だけです。
iLenDst にはそれぞれのビット数に対応した一行分のバイト数が格納されている事に注意して下さい。
この仕掛けのおかげで、32/24ビットの処理が統一できます。

LPRGBQUAD lpColorTable;
int x,y;

switch(bitCount){
    case 24:
        for(y=0;y<iHeight;y++)
            for(x=0;x<iWidth;x++)
                CopyMemory(*(LPBYTE*)lppPixel+x*(type/8)+y*iLenDst,
                    lpbmpPixel+x*3+y*iLenSrc,3);
        break;
    case 8:
        lpColorTable=lpbmpInfo->bmiColors;
        for(y=0;y<iHeight;y++)
            for(x=0;x<iWidth;x++)
                CopyMemory(*(LPBYTE*)lppPixel+x*(type/8)+y*iLenDst,
                    lpColorTable+lpbmpPixel[x+y*iLenSrc],3);
        break;
}

HeapFree(GetProcessHeap(),0,lpbuf);
return iLenDst;

■DIBSectionを削除する関数

前節と同じです。

void DeleteDIBSection(HBITMAP *lphBmp)
{
    if(*lphBmp!=NULL){
        DeleteObject(*lphBmp);
        *lphBmp=NULL;
    }
}

■関数を呼び出す

それでは、関数を呼び出してみましょう。
描画する時はDIBで、表示する時はDDBで処理してみました。

LRESULT CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    static BITMAPINFO bmpInfo;
    static LPBYTE lpPixel;
    static HBITMAP hBmp;
    static HDC hMemDC;
    static int width,height,length;
    int x,y;

    switch(uMsg) {
        case WM_CREATE:
            length=CreateDIBSectionFromFile248("TOKOPUYO200.bmp",&hBmp,(void**)&lpPixel,&bmpInfo,BY);
            width=bmpInfo.bmiHeader.biWidth;
            height=bmpInfo.bmiHeader.biHeight;
            hMemDC=CreateCompatibleDC(NULL);
            SelectObject(hMemDC,hBmp);
            //描画
            for(y=height/10;y<height/5;y++){
                for(x=width/10;x<width/2;x++){
                    lpPixel[(x*3 )+y*length]=0x80;
                    lpPixel[(x*3+1)+y*length]=0x80;
                    lpPixel[(x*3+2)+y*length]=0x80;
                }
            }
            return 0;
        case WM_DESTROY:
            DeleteDC(hMemDC);
            DeleteDIBSection(&hBmp);
            PostQuitMessage(0);
            return 0;
        case WM_PAINT:
            hdc=BeginPaint(hWnd,&ps);
            //表画面へ転送
            BitBlt(hdc,0,0,width,height,hMemDC,0,0,SRCCOPY);
            EndPaint(hWnd,&ps);
            return 0;
    }
    return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

サンプルは8ビットBMPファイルを24ビットDIBSectionとして読み込んでいます。

前節と殆ど変わりませんでしね。
8ビットとして扱いたい場合はDIBを使って下さい。

■裏技?

CDF列挙型の DW は「*lppPixel が LPDWORD型 である」事を通知する列挙定数ですが、
BYTE型でも、他のどんな型でも問題がないのは関数定義より明らかです。
どんな型であろうと4バイト毎に値を格納するよ、ということを意味しているのですね。

★☆ ダウンロード ☆★


戻る / ホーム