FC2ブログ

ホーム > DIBファイルフォーマット > DIBのビット深度の変更

DIBのビット深度の変更



DIBビット深度の変更は以下のような流れになります。

  1. ビットマップハンドルからDIBSECTION構造体を取得し、幅と高さを算出します。
  2. BITMAPINFO構造体を作成する。
  3. 空のDIBを作成する。
  4. DIBビット深度を変換したピクセルビットを書き込む



ビットフィールドマスクタイプの識別を簡素化するために、独自の定数を定義しました。

//ビットフィールド識別のための定数値
#define BF_555 0x007C3E1F //赤:0x007C00 緑:0x0003E0 青:0x00001F
#define BF_565 0x00F87E1F //赤:0x00F800 緑:0x0007E0 青:0x00001F
#define ALPHA32 0xFFFFFFFF //アルファ:0xFF000000 赤:0x00FF0000 緑:0x0000FF00 青:0x000000FF



※本文で使用するスタンダードコール形式のサブルーチン関数です。


//ビット深度が適正な値かどうか?
BOOL __stdcall _IsDIBitCount(UINT nBitsPerPixel)
{
UINT nBpp[6]={1,4,8,16,24,32};
for (int i=0;i<6;i++)
if (nBpp[i]==nBitsPerPixel) return TRUE;
return FALSE;
}


//DIBのカラーテーブルにRGB(赤/緑/青)カラーの値を設定
UINT __stdcall _SetDIBColorTable(HBITMAP hBitmap,UINT iStart,
UINT cEntries,const RGBQUAD *prgbq)
{
HDC hMemDC=::CreateCompatibleDC(0);
HGDIOBJ hOldObj=::SelectObject(hMemDC,hBitmap);
UINT uResult=::SetDIBColorTable(hMemDC,iStart,cEntries,prgbq);
::SelectObject(hMemDC,hOldObj);
::DeleteDC(hMemDC);
return uResult;
}


//BitFieldsからBI_BITFIELDSのマスクタイプを取得
//(戻り値:BF_555/BF_565/ALPHA32,BI_BITFIELDS以外の時:FALSE)

int __stdcall _GetDIBMaskType(DWORD* lpBitfields)
{
if (!lpBitfields) return FALSE;
DWORD r=lpBitfields[0];
DWORD g=lpBitfields[1];
DWORD b=lpBitfields[2];
if (b==0x001F&&g==0x03E0&&r==0x7C00) return BF_555;
else if (b==0x001F&&g==0x07E0&&r==0xF800) return BF_565;
else if (b==0x0000FF&&g==0x00FF00&&r==0xFF0000) return ALPHA32;
return FALSE;
}


DIBセクションをただ単にコピーするだけなら、API関数CopyImage関数のfuFlagsパラメータにLR_CREATEDIBSECTIONを入れて呼び出せば出来ますが、複写先DIBのビット深度はモノクロDIB以外はすべて32ビットRGBになってしまいます。そこでビット深度も忠実に再現するコピー関数を作成しました。

//DIBセクションのコピー(忠実コピー)
HBITMAP __stdcall _CopyDIB(HBITMAP hBitmap)
{
//入力されたDIBハンドルが正しくなければ、FALSEを返して終了する
DIBSECTION ds={0};
if (!::GetObject(hBitmap,sizeof(DIBSECTION),&ds)) return FALSE;

//入力されたDIBハンドルがDDBなら、CopyImage関数でDIBのコピーを作成して
//そのハンドルを戻り値として終了する。

if (ds.dsBmih.biSize!=sizeof(BITMAPINFOHEADER))
return (HBITMAP)::CopyImage(hBitmap,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);

//DIBの幅と高さの算出
UINT width =ds.dsBmih.biWidth;
UINT height=abs(ds.dsBmih.biHeight);

//DIBSECTION構造体から転送先のBITMAPINFO構造体のサイズを算出-------------------------
//カラーテーブルのサイズを算出

UINT uColors=(ds.dsBmih.biBitCount>8)?0:ds.dsBmih.biClrUsed;
if ((uColors==0)&&(ds.dsBmih.biBitCount<=8)) uColors=1<<ds.dsBmih.biBitCount;

//転送先のカラーテーブルのサイズの算出
DWORD dwSizeOfColorTable=(uColors)?sizeof(RGBQUAD)*uColors:0;

//マスクビットフィールドのサイズを算出
DWORD dwSizeOfMaskBit= ((ds.dsBmih.biCompression==BI_BITFIELDS)&&
(ds.dsBmih.biBitCount==16)||(ds.dsBmih.biBitCount==32))?
sizeof(DWORD)*3:0;

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

//転送先のBITMAPINFO構造体を作成する
BYTE dib[sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256]={0};
BITMAPINFO* pBmpInfo=(BITMAPINFO*)dib;
//転送元のBITMAPINFOHEADER構造体をコピー
::CopyMemory(&pBmpInfo->bmiHeader,&ds.dsBmih,sizeof(BITMAPINFOHEADER));

//カラーテーブルがある場合
if (dwSizeOfColorTable)
UINT uResult=_GetDIBColorTable(hBitmap,0,uColors,pBmpInfo->bmiColors);

//マスクビット領域がある場合
else if (dwSizeOfMaskBit)::CopyMemory(&pBmpInfo->bmiColors,&ds.dsBitfields,sizeof(DWORD)*3);
//マスクビット領域がある場合
else if (dwSizeOfMaskBit){
::CopyMemory(&pBmpInfo->bmiColors,&ds.dsBitfields,sizeof(DWORD)*3);
if ((ds.dsBmih.biBitCount==32)&&(ds.dsBmih.biCompression==BI_BITFIELDS)){
BITMAPV5HEADER* pBmV5=(BITMAPV5HEADER*)dib;
pBmV5->bV5AlphaMask=0xFF000000;
}
}

//転送先のDIBセクションの作成
LPVOID pvBits;
HBITMAP hbmDst=::CreateDIBSection(0,pBmpInfo,DIB_RGB_COLORS,&pvBits,0,0);
if (!hbmDst) return FALSE;

//ピクセルビットをコピーする。
::CopyMemory(pvBits,ds.dsBm.bmBits,ds.dsBmih.biSizeImage);
return hbmDst;
}


指定されたビット深度マスクビット(16ビットと32ビットDIBのみ有効)に従って、ビット深度を変更したDIBセクションのコピーを作成します。

//指定されたビット深度とマスクビット(16ビットと32ビットDIBのみ有効)に従って、
//ビット深度を変更したDIBセクションのコピーを作成します。

HBITMAP __stdcall _CopyDIB(HBITMAP hBitmap,UINT nBitsPerPixel,UINT nMaskType)
{
//入力されたDIBハンドルが正しくなければ、FALSEを返して終了する
DIBSECTION ds={0};
if (!::GetObject(hBitmap,sizeof(DIBSECTION),&ds)) return FALSE;
HBITMAP hbmDIB=hBitmap;
//指定したビット深度が適正値ではないか、
//入力したビットマップがDIBではない時は新たにDIBを作成する

if ((!_IsDIBitCount(nBitsPerPixel))||
(ds.dsBmih.biSize!=sizeof(BITMAPINFOHEADER)))
{
hbmDIB=(HBITMAP)::CopyImage(hBitmap,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);
//新たに作成したDIBのDIBSECTION構造体を取得する
if (!::GetObject(hbmDIB,sizeof(DIBSECTION),&ds)) return FALSE;
//指定したビット深度が適正値ではない時は、新たに作成したDIBを返して終了する。
if (!_IsDIBitCount(nBitsPerPixel)) return hbmDIB;
}
//元のDIBのビット深度と変更するビット深度が同じ場合で
//ビット深度が16ビットでも32ビットでもなく、
//マスクタイプが変更するマスクタイプと同じなら
//新たに作成したDIBを返して終了する。

if ((ds.dsBmih.biBitCount==nBitsPerPixel)&&
(((nBitsPerPixel!=16)&&(nBitsPerPixel!=32))||
(nMaskType==_GetDIBMaskType(ds.dsBitfields))))
{
//DIBを新たに作成していなかったなら、DIBの忠実コピー関数で作成する
if (hbmDIB==hBitmap) hbmDIB=_CopyDIB(hBitmap);
return hbmDIB;
}

//DIBの幅と高さの算出
UINT width =ds.dsBmih.biWidth;
UINT height=abs(ds.dsBmih.biHeight);

//DIBSECTION構造体から転送先のBITMAPINFO構造体のサイズを算出-------------------------
//転送先のカラーテーブルのエントリー数の算出
UINT uColors=(nBitsPerPixel>8)?0:1<<nBitsPerPixel;

//転送先のカラーテーブルのサイズの算出
DWORD dwSizeOfColorTable=(uColors)?sizeof(RGBQUAD)*uColors:0;

//マスクビットフィールドのサイズを算出
DWORD dwSizeOfMaskBit=(nBitsPerPixel==16)? sizeof(DWORD)*3:
((nBitsPerPixel==32)&&(nMaskType==ALPHA32))?
sizeof(DWORD)*4:0;

//BITMAPINFO構造体のサイズを算出する
DWORD dwSizeOfBitmapInfo=((nBitsPerPixel==32)&&(nMaskType==ALPHA32))?
sizeof(BITMAPV5HEADER):
sizeof(BITMAPINFOHEADER)+dwSizeOfColorTable+dwSizeOfMaskBit;

//転送先のBITMAPINFO構造体を作成する
BITMAPINFO* pBmpInfo=(BITMAPINFO*)(new BYTE[dwSizeOfBitmapInfo]); //メモリー領域の確保
::ZeroMemory(pBmpInfo,dwSizeOfBitmapInfo);

//転送元のBITMAPINFOHEADER構造体をコピー
::CopyMemory(&pBmpInfo->bmiHeader,&ds.dsBmih,sizeof
(BITMAPINFOHEADER));pBmpInfo->bmiHeader.biCompression=(dwSizeOfMaskBit)?BI_BITFIELDS:BI_RGB; //圧縮形式
pBmpInfo->bmiHeader.biBitCount =nBitsPerPixel; //ビット深度
DWORD widthBytes=WIDTHBYTES(nBitsPerPixel*width); //ビットマップバイト幅の算出
DWORD dwSizeImage=widthBytes*height; //ピクセルデータのバイト数
pBmpInfo->bmiHeader.biSizeImage=widthBytes*height; //ピクセルデータの算出
pBmpInfo->bmiHeader.biClrUsed=(uColors>=256)? 0:uColors; //使用色数
//マスクビット領域がある場合
if (pBmpInfo->bmiHeader.biCompression==BI_BITFIELDS)
{ DWORD*pMask=(DWORD*)&pBmpInfo->bmiColors;
if (nBitsPerPixel==16){
if (nMaskType==BF_565){
pMask[0]=0xF800; //BF_565
pMask[1]=0x07E0;
pMask[2]=0x001F;
}
else{
pMask[0]=0x7C00; //BF_555
pMask[1]=0x03E0;
pMask[2]=0x001F;
}
}
else if (nBitsPerPixel==32){
pMask[0]=0x00FF0000; //ALPHA32
pMask[1]=0x0000FF00;
pMask[2]=0x000000FF;
pMask[3]=0xFF000000;

}
}

//DIBセクションの作成
LPVOID pvBits;
HBITMAP hbmDst=::CreateDIBSection(0,pBmpInfo,DIB_RGB_COLORS,&pvBits,0,0);
if (!hbmDst) {
//新たに転送元DIBを作成していたら、削除する。
if (hbmDIB!=hBitmap) ::DeleteObject(hbmDIB);
delete[]pBmpInfo;
return FALSE;
}

//マスクビット領域があってビット深度が16ビットの場合
if((pBmpInfo->bmiHeader.biCompression==BI_BITFIELDS)&&(nBitsPerPixel==16))
{
//デバイスコンキスト作成
HDC hDstDC=::CreateCompatibleDC(0);
HDC hSrcDC=::CreateCompatibleDC(0);

HGDIOBJ hDstObj=::SelectObject(hDstDC, hbmDst);
HGDIOBJ hSrcObj=::SelectObject(hSrcDC, hbmDIB);

//RGB565へ転送して変換する
::BitBlt(hDstDC,0,0,width,height,hSrcDC,0,0,SRCCOPY);

::SelectObject(hDstDC, hDstObj);
::SelectObject(hSrcDC, hSrcObj);

::DeleteDC(hDstDC);
::DeleteDC(hSrcDC);
::GetObject(hbmDst,sizeof(DIBSECTION),&ds);

}
else{
HDC hDC=::GetDC(0);
//hbmDIBのピクセルビットをpBmpInfoで指定された形式に変換して、pvBitsにコピーする
int nLine=::GetDIBits(hDC,hbmDIB,0,height,pvBits,pBmpInfo,DIB_RGB_COLORS);
::ReleaseDC(0,hDC);

//カラーテーブルがある時は、カラーテーブルをDIBに設定する
if (nBitsPerPixel<=8) _SetDIBColorTable(hbmDst,0,uColors,pBmpInfo->bmiColors);
}
//ほかの形式からアルファビット付32ビットDIBへ変換する場合で、
//アルファビットがすべて0x00の時は、アルファビットを0xFFにする

if ((nBitsPerPixel==32)&&(pBmpInfo->bmiHeader.biCompression==BI_BITFIELDS))
{
UINT uNumOfBits=dwSizeImage>>2;
BOOL bAlphaBit=FALSE;
DWORD* chk=(DWORD*)pvBits;
//アルファビットがすべて0x00かどうか、スキャンする
for(UINT i=0;i<uNumOfBits;i++){
if ((*chk++)&0xFF000000){
bAlphaBit=TRUE;
break;
}
}
DWORD* src=(DWORD*)pvBits;
DWORD* dst=src;
//すべて0x00ならアルファビットを0xFFにする
if (!bAlphaBit){
for(UINT i=0;i<uNumOfBits;i++)
*dst++=((*src++)|0xFF000000);
}
}

//確保した領域を開放する。
delete[]pBmpInfo;
//新たに転送元DIBを作成していたら、削除する。
if (hbmDIB!=hBitmap) ::DeleteObject(hbmDIB);
//作成した転送先DIBを返して終了する。
return hbmDst;
}


hbmDstの内容が、ビット深度を変更して作成したDIBのハンドルになります。

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

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



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



24ビットフルカラー画像が表示されます。



画像が表示されたところで「編集」/「ビット深度の変更」で「ビット深度の変更」ダイヤログを表示させ、「ビット深度」コンボボックスで8ビットを選択します。



256色パレットDIBに減色した画像が表示されます。



次に「Ctrl+Z」で24ビットフルカラー画像に戻しておいてから、再び「編集」/ビット深度の変更」で「ビット深度の変更」ダイヤログを表示させ、「ビット深度」コンボボックスで今度は4ビットを選択します。



16色パレットDIBに減色した画像が表示されます。



最後に「Ctrl+Z」で24ビットフルカラー画像に戻しておいてから、再び「編集」/ビット深度の変更」で「ビット深度の変更」ダイヤログを表示させ、「ビット深度」コンボボックスで今度は1ビットを選択します。



モノクロDIBに減色した画像が表示されます。



尚16/24/32ビットに変更する場合、画像の変化は肉眼では判別できないので、「ビット深度の変更」ダイヤログの初期設定値で確かめてください。


スポンサーサイト



コメント: 0

この記事へのコメント
ブログ著者にのみ知らせます。

Trackback+Pingback: 0

TrackBack URL for this entry
http://hiroshi0945.blog75.fc2.com/tb.php/16-5804a21e
Listed below are links to weblogs that reference
DIBのビット深度の変更 from マルチメディアファイルフォーマット

Home > DIBファイルフォーマット > DIBのビット深度の変更

タグクラウド
ブロとも申請フォーム

この人とブロともになる

ブロとも一覧

このページの先頭へ戻る