2010年08月30日

透過DIB画像の表示



透過DIB画像の表示には以下の2種類の方法があります。

  1. マスク画像を用いる方法
    まず画像の四隅の色から透過色を求めてマスク画像を作成し、このマスク画像で背景画像を理論積(AND)で切り取ります。次にマスク画像を反転してから前景画像上でマスクする部分を理論積(AND)で切り取り、最後にマスク部分を切り取った前景画像理論和(OR)背景画像に貼り付けます。

  2. アルファブレンド32ビットDIBを用いる方法
    まず背景画像から表示する画像の部分のコピーと表示する画像のコピーを作成します。次に背景画像前景画像アルファブレンド演算ピクセルビット毎に合成データを算出し、前景画像に戻します。最後に合成した前景画像背景画像に貼り付けます。



ビットマップの指定位置のピクセル色を取り出す関数です。

//ビットマップの指定位置のピクセル色を取り出します。
COLORREF __stdcall _GetPixel(HBITMAP hBitmap,int X,int Y)
{
//BITMAP構造体の取得に失敗したら-1を返して終了します。
BITMAP bm={0};
if (!::GetObject(hBitmap,sizeof(BITMAP),&bm)) return -1;

//指定位置のピクセル色を取り出します。
HDC hMemDC=::CreateCompatibleDC(NULL);
HGDIOBJ hOldObj=::SelectObject(hMemDC,hBitmap);
COLORREF color=::GetPixel(hMemDC,X,Y);
::SelectObject(hMemDC,hOldObj);
::DeleteDC(hMemDC);

return color;
}


上記_GetPixel関数にて取得したビットマップの左上の隅のピクセル色を透過色として、その透過色と元のビットマップからマスクビットマップを作成する関数です。

//指定した透過色からマスクビットマップを作成します。
HBITMAP __stdcall _CreateMaskBitmap(HBITMAP hBitmap,COLORREF colorMask)
{
//透過色が正しくない場合は、エラー終了します。
if (colorMask&0xFF000000) return FALSE;

//入力されたビットマップの32ビットDIBのコピーを作成します。
HBITMAP hbmMask=_CopyDIB(hBitmap,32,0);

//32ビットDIBのBITMAP構造体を取得します。
BITMAP bmm;
if (!::GetObject(hbmMask,sizeof(BITMAP),&bmm)) return FALSE;

//ピクセルデータの総数を算出します。
UINT nPixels=(bmm.bmWidthBytes*bmm.bmHeight)>>2;

//転送元/転送先ポインタを32ビットDIBのピクセルビットの先頭アドレスにします。
DWORD* src=(DWORD*)bmm.bmBits;
DWORD* dst=src;

//ピクセルビットと比較するために、透過色のRGBをBGRに並べ替えたDWORD変数を用意します。
DWORD bgr=((colorMask&0x00FF)<<16)|(colorMask&0x00FF00)|((colorMask&0xFF0000)>>16);

//ピクセルの数だけループします。
for(UINT i=0;i<nPixels;i++)
//ピクセルビットの色が透過色に一致したら白色、
//それ以外は黒色に色を変更します。

*dst++=(*src++==bgr)? 0xFFFFFF:0x000000;

//出来たマスクビットマップをモノクロDDBに変換します。
HBITMAP hbmMono=(HBITMAP)::CopyImage(hbmMask,IMAGE_BITMAP,0,0,LR_MONOCHROME);
//作業用の32ビットマスクDIBは、もういらないので削除します。
::DeleteObject(hbmMask);

return hbmMono;
}


カラービットマップマスクビットマップからアルファチャネル付き32ビットDIBを作成する関数です。

//カラービットマップとマスクビットマップから、アルファチャネル付き32ビットDIBを作成します。
HBITMAP __stdcall _CreateAlphaBlendDIB(HBITMAP hbmColor,HBITMAP hbmMask)
{
//カラービットマップのアルファチャネル付き32ビットDIBのコピーを作成します。
//作成に失敗したら、FALSEを返して終了します。

HBITMAP hbmColor32=_CopyDIB(hbmColor,32,RGBA8888);
if (!hbmColor32) return FALSE;

//マスク画像の32ビットDIBのコピーを作成します。
//作成に失敗した場合は、前に作成したアルファチャネル付き32ビットDIBを削除し、エラー終了します。

HBITMAP hbmMask32=_CopyDIB(hbmMask,32,0);
if (!hbmMask32){
::DeleteObject(hbmColor32);
return FALSE;
}

//カラー画像のBITMAP構造体を取得します。
BITMAP bmc; ::GetObject(hbmColor32,sizeof(BITMAP),&bmc);
//マスク画像のBITMAP構造体を取得します。
BITMAP bmm; ::GetObject(hbmMask32, sizeof(BITMAP),&bmm);

//カラー画像のポインタピクセルビットへのポインタを取得します。
DWORD* src=(DWORD*)bmc.bmBits;
DWORD* dst=src;
//マスク画像のポインタピクセルビットへのポインタを取得します。
DWORD* msk=(DWORD*)bmm.bmBits;

//ピクセルの総数を算出します。
UINT nPixels=(bmc.bmWidthBytes*abs(bmc.bmHeight))>>2;

//すべてのピクセルに対してカラー/マスクビットを合成します。
for(UINT i=0;i<nPixels;i++){
//背景透過部分はカラー画像のアルファビットを0にし、
//それ以外はカラー画像のアルファビットを255にします。

if ((*msk++)&0x00FFFFFF) (*src++)&=0x00FFFFFF;
else (*src++)|=0xFF000000;
}

//作業用の32ビットマスク画像は、もういらないので削除します。
::DeleteObject(hbmMask32);

return hbmColor32;
}


アルファブレンド演算マクロです。

//アルファブレンド演算マクロ
#ifndef ALPHA_BLEND
#define ALPHA_BLEND(composite,fg,alpha,bg){\
WORD temp=((WORD)(fg)*(WORD)(alpha)+(WORD)(bg)*(WORD)(255-(WORD)(alpha))+(WORD)128);\
(composite)=(BYTE)((temp+(temp>>8))>>8);\
}
#endif//ALPHA_BLEND



指定したデバイスコンテキストに、入力されたビットマップ画像を描画する関数です。

  1. 入力されたビットマップ画像が32ビットDIBの場合は、指定したデバイスコンテキスト背景画像32ビットカラーDIBアルファブレンドしたものを描画します。
  2. 上記以外でマスクビットマップがある場合は、マスクDDBで入力されたDC背景画像のマスクされる部分を切り取り、マスクDDBの反転DDBを作成してこれでカラーDDB/DIBの背景部分を切り取った後、入力されたDCORで書き込みます。
  3. それ以外の場合は、カラーDDB/DIBを入力されたDCにそのまま描画します。


//ビットマップ画像の描画
BOOL __stdcall _DrawBitmap(HDC hDC,int xDst,int yDst,int cxDst,int cyDst,HBITMAP hbmColor,
HBITMAP hbmMask,int xSrc,int ySrc,int cxSrc,int cySrc)
{
//入力されたカラーDIBのDIBSECTION構造体を取得します。
DIBSECTION ds={0};
//DIBSECTION構造体が取得できない場合は、FALSEを返して終了します。
if (!::GetObject(hbmColor,sizeof(DIBSECTION),&ds)) return FALSE;

//画像サイズを取得します。
UINT width =ds.dsBm.bmWidth; //画像幅
UINT height=ds.dsBm.bmHeight; //画像高さ

//メモリーDCを作成します。
HDC hMemDC=::CreateCompatibleDC(hDC);
::SetStretchBltMode(hMemDC,COLORONCOLOR); //きれいに縮小するため。
::SetStretchBltMode(hDC,COLORONCOLOR); //きれいに縮小するため。

//カラービットマップが32ビットDIBの場合
if ((ds.dsBmih.biSize==sizeof(BITMAPINFOHEADER))&&(ds.dsBmih.biBitCount==32))
{
//背景画像用にもう一つメモリーDCを作成します。
HDC hBackDC=::CreateCompatibleDC(hDC);
::SetStretchBltMode(hBackDC,COLORONCOLOR); //きれいに縮小するため。

//背景画像用のDIBを作成します。
BITMAPINFOHEADER bh={0};
bh.biSize=sizeof(BITMAPINFOHEADER);
bh.biBitCount=24; //ビット深度は24ビット
bh.biWidth=cxDst; //画像幅/高さとも実際に表示するサイズにします。
bh.biHeight=cyDst;
bh.biPlanes=1;
DWORD widthBytes24=WIDTHBYTES(24*cxDst);
bh.biSizeImage=widthBytes24*cyDst;

//DIBセクションを作成します。
LPVOID pBitsBack=0;
HBITMAP hbmBack=::CreateDIBSection(hDC,(BITMAPINFO*)&bh,DIB_RGB_COLORS,&pBitsBack,0,0);
//作成に失敗した場合は、作成したメモリーDCを削除してから、
//FALSEを返して終了します。

if (!hbmBack){
::DeleteDC(hBackDC);
::DeleteDC(hMemDC);
return FALSE;
}

//背景画像用のメモリーDCに背景画像用のDIBを選択します。
HGDIOBJ hOldObj=::SelectObject(hBackDC,hbmBack);

//入力されたDCの画像を背景画像用のメモリーDCにコピーします。
::BitBlt(hBackDC,0,0,cxDst,cyDst,hDC,xDst,yDst,SRCCOPY);

//背景と前景を合成するDIBの作成
bh.biBitCount=32; //ビット深度は32ビット
DWORD widthBytes32=WIDTHBYTES(32*cxDst);
bh.biSizeImage=widthBytes32*cyDst;

//DIBセクションを作成します。
LPVOID pBitsFore;
HBITMAP hbmAlpha=::CreateDIBSection(hDC,(BITMAPINFO*)&bh,DIB_RGB_COLORS,&pBitsFore,0,0);
//作成に失敗した場合は、背景画像用のDIBと
//作成したメモリーDCを削除してから、FALSEを返して終了します。

if (!hbmAlpha){
::DeleteObject(hbmBack);
::DeleteDC(hBackDC);
::DeleteDC(hMemDC);
return FALSE;
}

//カラー画像を背景と前景を合成するDIBにコピーします。
::SelectObject(hBackDC,hbmAlpha);//hbmBackが開放されます。
HGDIOBJ hOldMem=::SelectObject(hMemDC,hbmColor);
::StretchBlt(hBackDC,0,0,cxDst,cyDst,hMemDC,xSrc,ySrc,cxSrc,cySrc,SRCCOPY);

::SelectObject(hBackDC,hOldObj);//hbmAlphaが開放されます。
//背景画像用のメモリーDCはもう必要ないので削除します。
::DeleteDC(hBackDC);

//背景と前景をアルファブレンド合成します。
BYTE r1,g1,b1,r2,g2,b2,a;
PBYTE fore,back,dest;
for(int i=0;i<cyDst;i++){
//ピクセルデータへのポインタの算出
fore=(PBYTE)pBitsFore+i*widthBytes32;//前景画像
back=(PBYTE)pBitsBack+i*widthBytes24;//背景画像
dest=fore;//結果を入れる前景画像へのポインタ
for(int j=0;j<cxDst;j++){
b1=*fore++; //前景画像のピクセルデータの読み出し
g1=*fore++;
r1=*fore++;
a =*fore++;
b2=*back++; //背景画像のピクセルデータの読み出し
g2=*back++;
r2=*back++;
//アルファブレンドマクロで演算処理
ALPHA_BLEND(*dest++,b1,a,b2);
ALPHA_BLEND(*dest++,g1,a,g2);
ALPHA_BLEND(*dest++,r1,a,r2);
dest++;
}
}

//入力されたDCにコピーする
::SelectObject(hMemDC,hbmAlpha);
::BitBlt(hDC,xDst,yDst,cxDst,cyDst,hMemDC,0,0,SRCCOPY);

//この関数内で作成したDIBは、必要ないので削除します。
::DeleteObject(hbmAlpha);
::DeleteObject(hbmBack);
}

//通常のビットマップ画像の場合。
else{
BITMAP bm;
//マスクビットマップがない場合。
if (!::GetObject(hbmMask,sizeof(BITMAP),&bm)){
HGDIOBJ hOldObj=::SelectObject(hMemDC,hbmColor);
//カラービットマップのみを入力されたDCにコピーします。
::StretchBlt(hDC,xDst,yDst,cxDst,cyDst,hMemDC,xSrc,ySrc,cxSrc,cySrc,SRCCOPY);
::SelectObject(hMemDC,hOldObj);
}
//マスクビットマップがある場合。
else {
//マスクビットマップで入力されたDCのマスクの部分を切り取ります。
HGDIOBJ hOldObj=::SelectObject(hMemDC,hbmMask);
::StretchBlt(hDC,xDst,yDst,cxDst,cyDst,hMemDC,xSrc,ySrc,cxSrc,cySrc,SRCAND);

//マスク用にもうひとつメモリーDCを作成します。
HDC hMaskDC=::CreateCompatibleDC(hDC);
//マスクを反転した画像を格納するモノクロビットマップを作成します。
HBITMAP hbmNotMask=::CreateBitmap(width,height,1,1,NULL);
HGDIOBJ hMaskObj=::SelectObject(hMaskDC,hbmNotMask);
//マスクビットマップを反転して作成したビットマップにコピーします。
::BitBlt(hMaskDC,0,0,width,height,hMemDC,0,0,NOTSRCCOPY);

//カラービットマップのコピーを作成します。
HBITMAP hbmColorCut=(HBITMAP)::CopyImage(hbmColor,IMAGE_BITMAP,0,0,0);

//マスクビットマップの反転画像でカラービットマップのマスク部分を切り取ります。
::SelectObject(hMemDC,hbmColorCut);
::BitBlt(hMemDC,0,0,width,height,hMaskDC,0,0,SRCAND);
::SelectObject(hMaskDC,hMaskObj);

//マスクの反転ビットマップとメモリーDCはもう必要ないので削除します。
::DeleteObject(hbmNotMask);
::DeleteDC(hMaskDC);

//カラー画像を入力されたDCにコピーする
::StretchBlt(hDC,xDst,yDst,cxDst,cyDst,hMemDC,xSrc,ySrc,cxSrc,cySrc,SRCPAINT);
::SelectObject(hMemDC,hOldObj);

//カラービットマップのコピーはもう必要ないので削除します。
::DeleteObject(hbmColorCut);
}
}
//作成したメモリーDCを削除します。
::DeleteDC(hMemDC);

return TRUE;
}



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

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



ビルドが終わると、直ちにプログラムが自動起動してウィンドウが表示されます。ウィンドウ内にあるトラッカーの範囲内で、右クリックしてポップアップメニューで「開く」を選択して、「ファイルを開く」ダイヤログを表示させます。



resフォルダー内の「1729_01.bmp」を背景画像として選択し、「開く」ボタンを押して読み込みます。画像が表示されたところで、画像のない所を左クリックしてトラッカーのフォーカスを移動させます。



新しいトラッカーの範囲内で再び右クリックしてポップアップメニューで「開く」を選択し、「ファイルを開く」ダイヤログを表示させます。



「purikick.bmp」を選択し、「開く」ボタンを押して読み込みます。画像が表示されたところで、トラッカーの範囲内で右クリックしてポップアップメニューで「画像のプロパティ」を選択し、「画像のプロパティ」ダイヤログを表示させます。



「透過色を使ってマスク透過する」にチェックを入れて「適用」ボタンを押します。画像のマスク部分が透過されて背景が見えるようになったかを確認してください。



再び画像のない所を左クリックしてトラッカーのフォーカスを移動させ、そのトラッカーの範囲内で右クリックしてポップアップメニューで「開く」を選択し、「ファイルを開く」ダイヤログを表示させます。



「tp_sphere_3.bmp」を選択し、「開く」ボタンを押して読み込みます。



画像が表示されたところで、その画像を移動させて、画像の下に背景が透けて見えるかを確認してください。




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

メールアドレス:

ホームページアドレス:

コメント:

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


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

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