BMPファイルを32ビットDIBSectionとして読み込む

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

DIB と DIBSection との違いはメモリ領域を確保するのが
HeapAlloc 関数であるか、CreateDIBSection 関数であるか、くらいのものです。

今回の解説を読む前に、「BMPファイルを32ビットDIBとして読み込む」の内容をきちんと理解しておいて下さい。

■関数の設計

関数名 → CreateDIBSection32FromFile248
引数 → ファイル名 , ビットマップハンドルのポインタ , ピクセル列のポインタのポインタ , BITMAPINFO構造体のポインタ
戻り値 → 0 (成功) or 負値(失敗)

int CreateDIBSection32FromFile248(
    char *lpFileName,HBITMAP *lphBmp,LPDWORD *lppPixel,BITMAPINFO *lpBmpInfo)
{
  ……
}

読み込みに対応するBMPファイルのビット数は24/8ビットです。
作成する DIBSection は32ビットです。

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

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

ここまでは32ビットDIBの時と同じです。

HANDLE fh;
DWORD dwFileSize,dwReadSize;
LPBYTE lpbuf;

LPBITMAPFILEHEADER lpbmpfh;
LPBITMAPINFO lpbmpInfo;

LPBYTE lpbmpPixel;
int bitCount,iWidth,iHeight,iLength;

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;

if(iWidth*(bitCount/8)%4) iLength=iWidth*(bitCount/8)+(4-iWidth*(bitCount/8)%4);
else iLength=iWidth*(bitCount/8);

■CreateDIBSection関数を実行するまで

CreateDIBSection関数は、BITMAPINFO構造体の情報を基に、
ピクセル列のメモリ領域確保、ビットマップ作成を行います。

従って、CreateDIBSection関数を実行する前にBITMAPINFO構造体を設定しておかなければなりません。
32ビットDIBSectionの場合は biWidth , biHeight , biBitCount だけを参照していると思われます。

カラーテーブルは無いかもしれないので、
コピーするのはBITMAPINFO構造体ではなく、BITMAPINFOHEADER構造体の領域までです。

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

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

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

ここも32ビットDIBの時と同じです。

LPRGBQUAD lpColorTable;
int x,y;

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

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

■DIBSectionを削除する関数

DIBSectionはビットマップを削除する事で、ピクセル列のメモリ領域も自動的に解放されます。
自らピクセル列のメモリ領域を解放してはいけません。

従って、削除関数に渡すのはピクセル列のポインタではなく、ビットマップハンドルのポインタです。

void DeleteDIBSection32(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 LPDWORD lpPixel;
    static HBITMAP hBmp;
    static HDC hMemDC;
    static int width,height;
    int x,y;

    switch(uMsg) {
        case WM_CREATE:
            CreateDIBSection32FromFile248("BARBARA.bmp",&hBmp,&lpPixel,&bmpInfo);
            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+y*width]=0x0000ff00;
                }
            }
            return 0;
        case WM_DESTROY:
            DeleteDC(hMemDC);
            DeleteDIBSection32(&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ファイルを読み込んでいます。

★☆ ダウンロード ☆★


戻る / ホーム