FAQ2-1

Q. 透ける部分のあるビットマップを描画するには

 ペイントブラシで,範囲選択したあとにそこをドラッグすると,背景色の部分が透けて移動ができますよね.これっていったいどのようにしてやっているのでしょう?
 似たようなことをしたアプリケーションを作りたいのですが,方法がわかりません.


A. BitBltを利用する

 よくある方法では,ANDマスクのビットマップとXORマスクのビットマップを2枚用意して,透けた描画をするという手段があります.Windowsのアイコンやカーソルもこの方式です.
 しかしこの質問の場合,あらかじめ2枚のビットマップを用意しておけるというわけではなく,元は1枚の絵で,特定の色を透けたように描画する,ということで少し趣旨が違います.

BitBltの意外な利用法

 方法は最終的には同じになるのですが,特定の色を高速に判断する方法として,WindowsのAPIであるBitBltを利用した方法を紹介しましょう.BitBltは,普通,ビットマップの転送のために使うAPIです.
 しかし,これには意外な使用方法があります.通常,転送元のビットマップと転送先のビットマップは,カラーからカラー,またはモノクロからモノクロ,といったように同じ形式で転送を行いますが,これを,カラーからモノクロ,またはモノクロからカラー,というふうに別の形式にすると,BitBltは色の変換を自動的に行ってくれます.
 この色の変換方法が重要で,カラーからモノクロに変換するときは,背景色の部分を白に,そのほかの部分は黒に,といった具合に変換してくれます.(背景色は,SetBkColorで指定する色です.)こうして,特定の色を分別することができるわけです.

透かして描画する方法

 さて,分別できたはいいのですが,次はそれを利用して透かして描画する方法がなくてはいけません.これはまず図2-1を見てください.

[図2-1] :

 図2-1の「AND」や「OR」といった文字は,BitBltの最後のパラメータであるラスターオペレーションを意味しています.「AND」のときはSRCAND,「OR」のときはSRCPAINT,「INVERT」はDSTINVERTです.
 ANDすると,転送元と転送先の一つ一つのピクセルにおいて,どちらかが黒(ビットが0)のピクセルのとき,必ず黒になります.
 ORすると,どちらかが白(ビットが1)のピクセルのとき,必ず白になります.
 この特性は,カラーのビットマップにモノクロを転送しても同じです.こういった特性を駆使して,透けた描画ができるのです.
 リスト2-1にサンプルプログラムを示します.このサンプルは汎用性を持たせるように作ったため,結構ムダがあります.状況に応じて最適化できると思いますので,自分でいろいろ変えてみましょう.

[リスト2-1] :

VOID PASCAL sample_SpriteBlt(
	HDC		hdc,			/*描画先DC	*/
	HBITMAP	hBmp,				/*ビットマップ	*/
	int		xxx,			/*描画する位置(x)	*/
	int		yyy,			/*描画する位置(y)	*/
	int		width,			/*描画する幅	*/
	int		height,			/*描画する高さ	*/
	COLORREF rgbBack			/*透ける色	*/
)
{
	HDC		hdcmemSrc;		/*元のBMP用*/
	HANDLE	holdSrc;

	HDC		hdcmemSrc2;		/*元のBMPの複製用*/
	HANDLE	holdSrc2;
	HBITMAP	hBmpSrc2;

	HDC		hdcmemMono;		/*モノクロ化したBMP用*/
	HANDLE	holdMono;
	HBITMAP	hBmpMono;

	HDC		hdcmemG;		/*描画先DCの複製(作業用)*/
	HANDLE	holdG;
	HBITMAP	hBmpG;
	
	hdcmemSrc   = CreateCompatibleDC(hdc);
	hdcmemSrc2  = CreateCompatibleDC(hdc);
	hdcmemMono  = CreateCompatibleDC(hdc);
	hdcmemG     = CreateCompatibleDC(hdc);
	
	hBmpSrc2  = CreateCompatibleBitmap(hdc, width, height);
	hBmpMono  = CreateBitmap(width, height, 1, 1, NULL);
	hBmpG     = CreateCompatibleBitmap(hdc, width, height);
	
	holdSrc = SelectObject(hdcmemSrc, hBmp);
	holdSrc2= SelectObject(hdcmemSrc2, hBmpSrc2);
	holdMono = SelectObject(hdcmemMono, hBmpMono);
	holdG = SelectObject(hdcmemG, hBmpG);
	
	/* キャプチャ */
	BitBlt(hdcmemG, 0, 0, width, height, hdc, xxx,yyy, SRCCOPY);
	/* モノクロ化 */
	SetBkColor(hdcmemSrc, rgbBack);
	BitBlt(hdcmemMono, 0, 0, width, height, hdcmemSrc, 0,0, SRCCOPY);
	/* AND(繰り抜き) */
	BitBlt(hdcmemG, 0, 0, width, height, hdcmemMono, 0,0, SRCAND);
	/* INVERT(モノクロ反転) */
	BitBlt(hdcmemMono, 0, 0, width, height, NULL, 0,0, DSTINVERT);
	/* AND(背景色消去) */
	BitBlt(hdcmemSrc2, 0, 0, width, height, hdcmemSrc, 0,0, SRCCOPY);
	BitBlt(hdcmemSrc2, 0, 0, width, height, hdcmemMono, 0,0, SRCAND);
	/* OR(重ね) */
	BitBlt(hdcmemG, 0, 0, width, height, hdcmemSrc2, 0,0, SRCPAINT);
	/* 描く */
	BitBlt(hdc, xxx, yyy, width, height, hdcmemG, 0,0, SRCCOPY);
	
	SelectObject(hdcmemSrc, holdSrc);
	SelectObject(hdcmemSrc2, holdSrc2);
	SelectObject(hdcmemMono, holdMono);
	SelectObject(hdcmemG, holdG);
	
	DeleteObject(hBmpSrc2);
	DeleteObject(hBmpMono);
	DeleteObject(hBmpG);
	
	DeleteDC(hdcmemSrc);
	DeleteDC(hdcmemSrc2);
	DeleteDC(hdcmemMono);
	DeleteDC(hdcmemG);
}

Back to FAQ main page