2010年08月16日

ビットマップリソースの読み込み



ビットマップリソース (リソースタイプ RT_BITMAP(2))は、デバイス独立ビットマップ(DIB形式)として格納されおり、以下のような構造をしています。

  1. BITMAPINFO構造体
  2. ピクセルデータ





DLL/EXEファイルを開いてモジュールハンドルを取得し、オーバーロード関数を呼び出します。

//DLL/EXEファイルからビットマップリソースを読み込む
HBITMAP __stdcall _LoadDIBFromResource(LPCTSTR lpszExeName,LPCTSTR lpName,LPDWORD lpdwCompression)
{
HMODULE hModule=::LoadLibraryEx(lpszExeName,NULL,
LOAD_LIBRARY_AS_DATAFILE|LOAD_WITH_ALTERED_SEARCH_PATH);
if (!hModule) return FALSE;
HBITMAP hBitmap=_LoadDIBFromResource(hModule,lpName,lpdwCompression);
::FreeLibrary(hModule);
return hBitmap;
}

入力されたリソースモジュールハンドルから、指定したリソース名のビットマップリソース をグローバルメモリーに展開し、_CreateDIBIndirectを呼び出します。

//ピットマップリソースの読み込み
HBITMAP __stdcall _LoadDIBFromResource(HMODULE hModule,LPCTSTR lpName,LPDWORD lpdwCompression)
{
//ビットマップリソース内で、指定したリソース名のリソースハンドルを取得します。
HRSRC hResInfo=::FindResource(hModule,(LPCTSTR)lpName,FreeLibrary);
if (!hResInfo) return FALSE;

//リソースをグローバルメモリにロードします。
HGLOBAL hGlobal=::LoadResource(hModule,hResInfo);
if (!hGlobal) return FALSE;

//メモリ内の指定されたリソースをロックします。
LPVOID pGlobal=::LockResource(hGlobal);
if (!pGlobal) return FALSE;

//指定したリソースのサイズ( バイト数)を取得します。
DWORD dwResSize=::SizeofResource(hModule,hResInfo);
if (!dwResSize) return FALSE;

//パックされたDIBから、DIBセクションを作成します。
HBITMAP hBitmap=_CreateDIBIndirect((BITMAPINFO*)pGlobal,dwResSize);
if (!hBitmap) return FALSE;

//第三引数のlpdwCompressionパラメータが有効な場合は、圧縮方法を保存しておきます。
if (!::IsBadReadPtr(lpdwCompression,sizeof(DWORD)))
*lpdwCompression=((BITMAPINFO*)pGlobal)->bmiHeader.biCompression;

return hBitmap;
}

パックされたDIB から、DIBセクションを作成します。

//パックされたDIB(ランレングス圧縮を含む)からDIBセクションを作成します。
HBITMAP __stdcall _CreateDIBIndirect(BITMAPINFO* pPackedDIB,DWORD dwDIBSize)
{
//入力されたBITMAPINFO構造体が有効ではない、またはDIBでない場合は、エラー終了します。
ASSERT(!::IsBadReadPtr(pPackedDIB,dwDIBSize));
if ((dwDIBSize<sizeof(BITMAPINFO))||
(::IsBadReadPtr(pPackedDIB,dwDIBSize))||
(pPackedDIB->bmiHeader.biSize<sizeof(BITMAPINFOHEADER)))
return FALSE;

//ランレングス圧縮されていない場合は、オーバーロード関数を呼び出します。
DWORD dwCompression=pPackedDIB->bmiHeader.biCompression;
if ((dwCompression!=BI_RLE4)&&(dwCompression!=BI_RLE8))
return _CreateDIBIndirect(pPackedDIB);

//DIBの幅
UINT width =pPackedDIB->bmiHeader.biWidth;
//DIBの高さ(絶対値)
UINT height=abs(pPackedDIB->bmiHeader.biHeight);
//DIBのビットカウント
UINT nBpp=pPackedDIB->bmiHeader.biBitCount;

//ビットマップバイト幅を算出します。
DWORD widthBytes=WIDTHBYTES(nBpp*width);
//ピクセルデータのバイト数を算出します。
DWORD dwSizeImage=widthBytes*height;

//カラーテーブルのサイズを算出します。
DWORD dwClrUsed=pPackedDIB->bmiHeader.biClrUsed;
UINT nColors=0;
//カラーテーブルがあって使用色数が0でないなら、エントリー数は使用色数を、
//それ以外は2のnBpp乗を代入します。

ASSERT(nBpp<=8);
nColors=(dwClrUsed)?dwClrUsed:1<<nBpp;
DWORD dwSizeOfColorTable=sizeof(RGBQUAD)*nColors;

//BITMAPINFO構造体のサイズを算出します。
DWORD dwSizeOfBitmapInfo=sizeof(BITMAPINFOHEADER)+dwSizeOfColorTable;

//算出したサイズがメモリーサイズより大きい場合は、エラー終了します。
if (dwSizeOfBitmapInfo>dwDIBSize) return FALSE;

//BITMAPINFO構造体を格納するバッファを確保します。
CByteArray arrayBmi;
arrayBmi.SetSize(dwSizeOfBitmapInfo);
BITMAPINFO* pBmi=(BITMAPINFO*)arrayBmi.GetData();

//パックされたDIBからBITMAPINFO構造体をコピーします。
::CopyMemory(pBmi,pPackedDIB,dwSizeOfBitmapInfo);

//biCompressionの値をBI_RGBにしないと、DIBセクション作成に失敗します。
ASSERT((dwCompression==BI_RLE4)||(dwCompression==BI_RLE8));
pBmi->bmiHeader.biCompression=BI_RGB;

//DIBセクションを作成します。
LPVOID pvBits=NULL;
HBITMAP hBitmap=::CreateDIBSection(NULL,pBmi,DIB_RGB_COLORS,&pvBits,NULL,NULL);
//作成に失敗したら、エラー終了します。
if (!hBitmap) return FALSE;

//パックされたDIB内のピクセルデータへのポインタを算出します。
BYTE* pBitsImage=(BYTE*)pPackedDIB+dwSizeOfBitmapInfo;
//パックされたDIB内のピクセルデータのサイズを取得します。
DWORD dwBytesInRes=min(dwSizeImage,dwDIBSize-dwSizeOfBitmapInfo);

BOOL bResult=FALSE;
//それぞれの圧縮形式に合わせて、イメージピクセルを読み込みます。
CMemFile infile(pBitsImage,dwBytesInRes);
switch(dwCompression){
case BI_RLE4:
//4ビットランレングス圧縮の場合は、_DecodeRle4関数を呼び出して解凍します。
bResult=_DecodeRle4(pvBits,dwSizeImage,width,height,&infile);
break;
case BI_RLE8:
//8ビットランレングス圧縮の場合は、_DecodeRle8関数を呼び出して解凍します。
bResult=_DecodeRle8(pvBits,dwSizeImage,width,height,&infile);
break;
default:
break;
}
infile.Close();

//読み込みエラーがある場合は、作成したDIBを削除してからエラー終了します。
if (!bResult){
::DeleteObject(hBitmap);
return FALSE;
}
return hBitmap;
}

//パックされたDIBからDIBセクションを作成します。
HBITMAP __stdcall _CreateDIBIndirect(BITMAPINFO* pPackedDIB)
{
ASSERT(!::IsBadReadPtr(pPackedDIB,sizeof(BITMAPINFO)));
//入力されたBITMAPINFO構造体が有効ではない場合は、エラー終了します。
if ((::IsBadReadPtr(pPackedDIB,sizeof(BITMAPINFO)))||
(pPackedDIB->bmiHeader.biSize<sizeof(BITMAPINFOHEADER)))
return FALSE;

//DIBの幅
UINT width =pPackedDIB->bmiHeader.biWidth;
//DIBの高さ(絶対値)
UINT height=abs(pPackedDIB->bmiHeader.biHeight);
//DIBのビットカウント
UINT nBpp=pPackedDIB->bmiHeader.biBitCount;

//ビットマップバイト幅を算出します。
DWORD widthBytes=WIDTHBYTES(nBpp*width);
//ピクセルデータのバイト数を算出します。
DWORD dwSizeImage=widthBytes*height;

//カラーテーブルのサイズを算出します。
DWORD dwClrUsed=pPackedDIB->bmiHeader.biClrUsed;
UINT nColors=0;
//カラーテーブルがあって使用色数が0でないなら、エントリー数は使用色数を、
//それ以外は2のnBpp乗を代入します。

if (nBpp<=8) nColors=(dwClrUsed)?dwClrUsed:1<<nBpp;
DWORD dwSizeOfColorTable=sizeof(RGBQUAD)*nColors;

//マスクビットフィールドがあれば、そのサイズを算出します。
DWORD dwSizeOfMaskBit=0;
if (pPackedDIB->bmiHeader.biCompression==BI_BITFIELDS){
if (nBpp==16) dwSizeOfMaskBit=sizeof(DWORD)*3;
else if (nBpp==32) dwSizeOfMaskBit=sizeof(DWORD)*(
(pPackedDIB->bmiHeader.biSize>=(sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*4))?4:3);
}

//BITMAPINFO構造体のサイズを算出します。
DWORD dwSizeOfBitmapInfo=sizeof(BITMAPINFOHEADER)+dwSizeOfColorTable+dwSizeOfMaskBit;

//算出したサイズがメモリーサイズより大きい場合は、エラー終了します。
if (::IsBadReadPtr(pPackedDIB,dwSizeOfBitmapInfo)) return FALSE;

//BITMAPINFO構造体を格納するバッファを確保します。
CByteArray arrayBmi;
arrayBmi.SetSize(dwSizeOfBitmapInfo);
BITMAPINFO* pBmi=(BITMAPINFO*)arrayBmi.GetData();
::CopyMemory(pBmi,pPackedDIB,dwSizeOfBitmapInfo);

//各チャンネル毎のカラーマスク値を、一時保管する変数を用意します。
DWORD dwRedMask=0;
DWORD dwGreenMask=0;
DWORD dwBlueMask=0;
DWORD dwAlphaMask=0;

//ランレングス圧縮されたDIBの場合は、エラー終了します。
DWORD dwCompression=pBmi->bmiHeader.biCompression;
if ((dwCompression==BI_RLE4)||(dwCompression==BI_RLE8)) return FALSE;

//マスクビットフィールドが有効な場合。
else if (dwCompression==BI_BITFIELDS){
//BITMAPINFOの長さがBITMAPINFOHEADER+BITFIELDS[3]以上の場合は、
//RGBカラーマスクを取得します。

if (dwSizeOfBitmapInfo>=sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*3){
dwRedMask =((BITMAPV4HEADER*)pBmi)->bV4RedMask;
dwGreenMask=((BITMAPV4HEADER*)pBmi)->bV4GreenMask;
dwBlueMask =((BITMAPV4HEADER*)pBmi)->bV4BlueMask;
}

//32ビットDIBで、BITMAPINFOの長さがBITMAPINFOHEADER+BITFIELDS[4]以上の場合は、
//アルファチャンネルを取得します。

if ((nBpp==32)&&(dwSizeOfBitmapInfo>=sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*4))
dwAlphaMask=((BITMAPV4HEADER*)pBmi)->bV4AlphaMask;

//32ビットDIB若しくは、16ビットDIBでRGB565ではない場合は、
//biCompressionをBI_RGBにして、BITMAPINFOHEADERのサイズを正規サイズに戻します。

if ((nBpp==32)||((nBpp==16)&&
((dwRedMask!=0x0000F800)||
(dwGreenMask!=0x000007E0)||
(dwBlueMask==0x0000001F))))
{
pBmi->bmiHeader.biCompression=BI_RGB;
pBmi->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
}
}

//パックされたDIB内のピクセルデータへのポインタを算出します。
BYTE* pBitsImage=(BYTE*)pPackedDIB+dwSizeOfBitmapInfo;

//32ビットDIBの場合。
DWORD widthBytesSafe=widthBytes;
if (nBpp==32){
int nPixels=dwSizeImage>>2; //ピクセル数
//アルファチャンネルが使われているかどうか調べます。
DWORD* src=(DWORD*)pBitsImage;
DWORD* dst=src;
BOOL bUsed=FALSE;
for (int i=0;i<nPixels;i++){
DWORD dw=*src++;
if (dw&0xFF000000){
bUsed=TRUE;
break;
}
}

//アルファチャンネルが使われていない場合は、24ビットDIBに変更します。
if (!bUsed){
nBpp=pBmi->bmiHeader.biBitCount=24;
//ビットマップバイト幅の算出
widthBytes=WIDTHBYTES(24*width);
//ピクセルデータのバイト数
dwSizeImage=widthBytes*height;
pBmi->bmiHeader.biSizeImage=dwSizeImage;
}
}

//DIBセクションを作成します。
LPVOID pvBits=NULL;
HBITMAP hBitmap=::CreateDIBSection(NULL,pBmi,DIB_RGB_COLORS,&pvBits,NULL,NULL);
//作成に失敗したら、エラー終了します。
if (!hBitmap) return

//32ビットDIBから24ビットDIBに変更していた場合。
if ((pPackedDIB->bmiHeader.biBitCount==32)&&(nBpp==24)){
//32ビットDIBのピクセルビットを、24ビットDIBにコピーします。
for(UINT i=0;i<height;i++){
PBYTE src=(PBYTE)pvBitsImage+i*widthBytesSafe;
PBYTE dst=(PBYTE)pvBits+i*widthBytes;
for(UINT j=0;j<width;j++){
*dst++=*src++;
*dst++=*src++;
*dst++=*src++;
src++;
}
}
}
else
//リソースのピクセルデータを、DIBのピクセルビットにコピーします。
::CopyMemory(pvBits,pBitsImage,dwSizeImage);

//32ビットDIBの場合。
if (nBpp==32){
int nPixels=dwSizeImage>>2; //ピクセル数

//マスクビットフィールドが有効で、RGBA8888ではない場合は、
//ピクセルビットをRGBA8888に合わせます。

if ((dwCompression==BI_BITFIELDS)&&
((dwRedMask!=0x00FF0000)||(dwGreenMask!=0x0000FF00)||
(dwBlueMask!=0x000000FF)||(dwAlphaMask!=0xFF000000)))
{
//各チャンネルのカラーマスク値が正しい場合。
DWORD dwMask[4]={dwRedMask,dwGreenMask,dwBlueMask,dwAlphaMask};
if (_IsColorMasks(dwMask,4)){
//各チャンネルのビットシフト量を計算します。
int nShiftsBlue=0;
if (dwBlueMask)
while(((dwBlueMask>>nShiftsBlue)&1)==0) nShiftsBlue++;
int nShiftsGreen=0;
if (dwGreenMask)
while(((dwGreenMask>>nShiftsGreen)&1)==0) nShiftsGreen++;
int nShiftsRed=0;
if (dwRedMask)
while(((dwRedMask>>nShiftsRed)&1)==0) nShiftsRed++;
int nShiftsAlpha=0;
if (dwAlphaMask)
while(((dwAlphaMask>>nShiftsAlpha)&1)==0) nShiftsAlpha++;

//各チャンネルの最大値を算出します。
int maxRed=dwRedMask>>nShiftsRed;
int maxGreen=dwGreenMask>>nShiftsGreen;
int maxBlue=dwBlueMask>>nShiftsBlue;
int maxAlpha=dwAlphaMask>>nShiftsAlpha;

//ピクセルビットを各チャンネルに分解してから、改めて並べなおします。
DWORD* src=(DWORD*)pvBits;
DWORD* dst=src;
for (int i=0;i<nPixels;i++){
DWORD dw=*src++;
DWORD r=(maxRed)? ((dw&dwRedMask)>>nShiftsRed)*0xFF/maxRed :0;
DWORD g=(maxGreen)? ((dw&dwGreenMask)>>nShiftsGreen)*0xFF/maxGreen :0;
DWORD b=(maxBlue)? ((dw&dwBlueMask)>>nShiftsBlue)*0xFF/maxBlue :0;
DWORD a=(maxAlpha)? ((dw&dwAlphaMask)>>nShiftsAlpha)*0xFF/maxAlpha :0;
*dst++=((b&0x000000FF)|
((g<<8)&0x0000FF00)|
((r<<16)&0x00FF0000)|
((a<<24)&0xFF000000));
}
}
}
}
return hBitmap;
}



hBitmapの内容が、読み込んだDIBファイルから作成したDIBのハンドルになります。

サンプルプログラム(VisualC++net2003ソリューション)DIBFileTest02.zip

上記のzipファイルをダウンロードしてからWinRAR等で解凍し、DIBFileTest02フォルダー内にある「DIBFileTest.sln」を開きます。「F5」キーを押すと、ビルド確認のダイヤログが表示されるので「Yes」を選択してソリューションをビルドします。



ビルドが終わると直ちにプログラムが自動起動してウィンドウが表示されます。ウィンドウのメニューで「ファイル」/「開く」を選択して、「ファイルを開く」ダイヤログを表示させます。



Debugフォルダー内の「DIBFileTest.exe」を選択し、「開く」ボタンを押すと、「リソース名を選択してください。」ダイヤログが表示されるので、コンボボックスで130番を選択します。



最後に下のような画像が表示されるか確認します。







posted by ひろし at 02:23| Comment(0) | DIBファイルフォーマット | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。