ビットマップの色の置き換え

ツールバーボタンの画像背景を透過する

前ページで作成したツールバーは、よく見るとボタン画像の背景色の白色がそのまま表示されています。
(拡大図)
ツールバーのボタンの拡大図

ボタンの表面の色は(特に変更していない場合は)薄い灰色なので、少し不自然な感じになっています。
画像の不要な部分を透明にすれば自然になりますが、本当に透明にするのは面倒なので、画像背景色の白色の部分をボタン表面色と同じ色に置き換えることで透明に見せかけることにします。

ボタン表面の色の取得

ボタン表面の色はGetSysColor関数で取得できます。

DWORD GetSysColor(
 int nIndex
);
nIndexで指定されるシステムカラーを取得する。

nIndexには取得したい箇所の定数を指定します。
ボタン表面の色は定数COLOR_BTNFACEで取得できます。

ビットマップ画像の加工

ビットマップ画像の色を置き換えるにはCreateMappedBitmap関数を使用します。

HBITMAP CreateMappedBitmap(
 HINSTANCE hInstance,
 INT_PTR idBitmap,
 UINT wFlags,
 LPCOLORMAP lpColorMap,
 int iNumMaps
);
モジュールhInstanceに含まれるidBitmapで識別されるビットマップリソースを、lpColorMapの情報を元に色を置き換える。
戻り値は加工されたビットマップのハンドル。

hInstanceidBitmapはビットマップのロードで、LoadBitmap関数と同じです。
ただし識別子の指定にMAKEINTRESOURCEマクロは必要ありません。

wFlagsはビットマップをマスクとして使用する場合にCMB_MASKEDという定数を指定します。
今回は使用しないので0を指定します。

lpColorMapCOLORMAP構造体へのポインタです。
この構造体で指定される色情報を元に画像の色を置き換えます。

typedef struct _COLORMAP {
 COLORREF from;
 COLORREF to;
} COLORMAP, *LPCOLORMAP;
CreateMappedBitmap関数で使用する色情報を格納する構造体。

fromメンバの色をtoメンバの色に置き換えます。
COLORREF型文字色と背景色でも登場しましたが、RBGマクロを使用して「赤・青・緑」の色の強さをそれぞれ0~255の数値で指定します。
「0,0,0」なら黒色、「255,255,255」なら白色になります。


//白色をボタン表面色に置き換え
COLORMAP colorMap;
colorMap.from = RGB(255, 255, 255);
colorMap.to = GetSysColor(COLOR_BTNFACE);

最後の引数iNumMapsは引数lpColorMapが指定するCOLORMAP構造体の数です。
引数lpColorMapにはCOLORMAP構造体の配列を指定することもでき、複数の色を置き換えることができます。
その場合に配列の要素数を指定します。
配列ではなく変数を指定する場合は1を指定します。

CreateMappedBitmap関数を使用して、ビットマップの白色をボタン表面の色に置き換えるコードは以下のようになります。


HWND CreateToolbar(HWND hWnd)
{
	INITCOMMONCONTROLSEX icc;
	icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
	icc.dwICC = ICC_BAR_CLASSES;
	InitCommonControlsEx(&icc);

	HWND hToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hWnd, NULL, hInst, NULL);

	SendMessage(hToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
	SendMessage(hToolbar, TB_SETBITMAPSIZE, 0, MAKELPARAM(16, 15));

	//ビットマップの色の置き換え
	//(白→ボタン表面色)
	COLORMAP colorMap;
	colorMap.from = RGB(255, 255, 255);
	colorMap.to = GetSysColor(COLOR_BTNFACE);
	HBITMAP hbm = CreateMappedBitmap(hInst, IDB_BITMAP1, 0, &colorMap, 1);

	TBADDBITMAP tb;
	tb.hInst = NULL;
	tb.nID = (UINT_PTR)hbm;
	SendMessage(hToolbar, TB_ADDBITMAP, (WPARAM)5, (LPARAM)&tb);

	TBBUTTON tbb[] = {
		{ 0, IDC_A, TBSTATE_ENABLED, BTNS_BUTTON },
		{ 1, IDC_I, TBSTATE_ENABLED, BTNS_BUTTON },
		{ 0, 0, 0, BTNS_SEP },
		{ 2, IDC_U, TBSTATE_ENABLED, BTNS_BUTTON },
		{ 3, IDC_E, TBSTATE_ENABLED, BTNS_BUTTON },
		{ 4, IDC_O, TBSTATE_ENABLED, BTNS_BUTTON }
	};
	SendMessage(hToolbar, TB_ADDBUTTONS, (WPARAM)6, (LPARAM)&tbb);

	return hToolbar;
}

これで画像中の白色ピクセルがボタン表面と同じ色になり、自然な表示になります。
画像背景色を透明にしたツールバーボタン

色の置き換えはビットマップ中のすべてのピクセルに対して行われます。
表示したい画像中(残しておきたい部分)に置き換えられる色(今回は白)が含まれている場合はその箇所も置き換えられてしまうため、表示したい画像中に含まれない色を背景色にしておく必要があります。

また、CreateMappedBitmap関数で使用できるビットマップ画像は256色(8bitカラー)以下に限られます。
それ以上のカラー情報を持つビットマップを指定すると実行時エラーが発生します。

画像が壊れる場合

ツールバーボタンに使用するビットマップ画像は画像編集ソフトで作成できますが、256色カラーで作成したビットマップであってもCreateMappedBitmap関数で加工すると上手くいかないことがあります。
(関数は成功するが画像が壊れる)

その場合はビットマップ画像をVisual Studioのリソースに追加した後、Visual Studioのビットマップエディタで開き、そのまま上書き保存すると上手くいきます。
(「ソリュージョンエクスプローラー」→「リソースファイル」→「Resource.rc(リソースファイル名)」をダブルクリック→リソースビューの「Bitmap」→ビットマップ識別名をダブルクリック)
Visual Studioのビットマップエディタ

この状態で「ファイル」メニューから「(ビットマップ名)の保存」を選択すれば上書きされます。
必要ならばここで編集してもかまいませんし、最初からこれで画像を作成しても良いです。
(あまり使い勝手は良くないですが)