イメージリスト

イメージリストは、複数の画像(ビットマップ、アイコン、カーソル)を保存、管理するための機能です。
ツールバーでは、ボタン画像をツールバー自体に登録しましたが、イメージリストに登録してそこから画像を指定することもできます。
その他に画像のマスクという機能を使用することもできます。

イメージリストはコモンコントロールから使用される機能です。
コモンコントロールの使用についてはツールバー#コモンコントロールを参照してください。
(イメージリスト自体はコモンコントロールの初期化を行わなくても動作します)

イメージリストの使用

イメージリストの作成

イメージリストの作成はImageList_Create関数を使用します。

HIMAGELIST ImageList_Create(
 int cx,
 int cy,
 UINT flags,
 int cInitial,
 int cGrow
);
横幅cx、縦幅cyのサイズのイメージリストを作成し、イメージリストハンドルを返す。
失敗した場合はNULLを返す。

イメージリストは全て同じサイズの画像を保持します。
そのサイズは引数cxcyで指定します。

flagsは作成するイメージリストの種類を以下の定数の組み合わせで設定します。
ただし名前に「ILC_COLOR」を含む定数同士は同時に指定できません。

定数 説明
ILC_COLOR デフォルトの設定
通常はILC_COLOR4が使用される
古いディスプレイではILC_COLORDDBが使用される
ILC_COLOR4 4ビットDIB
(16色)
ILC_COLOR8 8ビットDIB
(256色)
ILC_COLOR16 16ビットDIB
(65536色)
ILC_COLOR24 24ビットDIB
(フルカラー)
ILC_COLOR32 32ビットDIB
(トゥルーカラー)
ILC_COLORDDB デバイス依存ビットマップ
(DDB)
ILC_MASK 画像マスクを使用する
マスクを使用する場合、画像は2つのビットマップを登録し、うち一つはもう一方のビットマップを表示する領域を指定するために使用される
ILC_MIRROR プロセスがミラーリングされている場合、アイコンをミラーリング(左右反転)する
ILC_PERITEMMIRROR ILC_MIRRORフラグでミラーリングするとき、画像全体ではなく項目毎にミラーリングする
ILC_ORIGINALSIZE 設定よりも小さなサイズの画像を受け入れ、元のサイズを適用する
Windows Vista以降

cInitialはイメージリストが持つ画像の数の初期値です。

cGrowは、イメージリストが確保している画像の格納可能数が足りなくなったときに、拡張される画像の数を指定します。
0を指定しても必要な数は確保されます。

戻り値はHIMAGELIST型で、これはイメージリストのハンドルです。
失敗した場合はNULLが返されます。

DIBとはビットマップの形式のひとつで「デバイス独立ビットマップ」というものです。
(Device Independent Bitmap)
デバイス(機器のこと)に依存しないビットマップという意味で、色情報をデータ内に持っていてどのデバイスでも適切な色表現が可能です。
これは通常のビットマップファイル(.bmpファイル)で使用される形式です。

これに対してDDBという形式は「デバイス依存ビットマップ」というものです。
(Device Dependent Bitmap)
これはデータ内に色情報を持っておらず、デバイスが持つ色情報を元に描画を行います。
あるデバイス用に作成されたDDBは、他のデバイスでは表示がおかしくなる可能性があります。

イメージの追加

イメージリストに画像を追加するにはImageList_Add関数を使用します。

int ImageList_Add(
 HIMAGELIST himl,
 HBITMAP hbmImage,
 HBITMAP hbmMask
);
イメージリストhimlにビットマップhbmImageを追加する。
マスクを使用する場合はhbmMaskに指定する。
戻り値は追加したイメージの先頭のインデックス。
失敗した場合は-1を返す。

himlはイメージリストハンドルを指定します。

hbmImageはビットマップハンドルを指定します。
ビットマップの数はビットマップの幅から計算されます。
例えばイメージリストの幅(cx)に100を指定して作成した場合、幅が200のビットマップを追加すると画像を2枚追加することになります。

hbmMaskはマスクとして使用するビットマップのハンドルを指定します。
マスクを使用しない場合はNULLを指定します。

イメージリストへの画像の追加はコピー処理が行われます。
元の画像は破棄しても問題ありません。

アイコンの追加

イメージリストはビットマップのほか、アイコンやカーソルなども追加することができます。
アイコン、カーソルの追加はImageList_AddIconマクロを使用します。

void ImageList_AddIcon(
 himl,
 hicon
);
イメージリストhimlにアイコン(またはカーソル)hiconを追加する。

イメージリストにマスク(ILC_MASKフラグ)を設定している場合はマスクも同時にコピーされます。

イメージリストの描画

イメージリストに追加した画像はImageList_Drawで描画することができます。

BOOL ImageList_Draw(
 HIMAGELIST himl,
 int i,
 HDC hdcDst,
 int x,
 int y,
 UINT fStyle
);
イメージリストhimlの画像インデックスiを、デバイスコンテキストhdcDstの座標x,yに、描画フラグfStyleを適用して描画する。
成功した場合は0以外を、失敗した場合は0を返す。

himlはイメージリストハンドルです。
iはイメージリストが保存する画像のインデックスです。
(先頭は0)

hdcDstは描画先となるデバイスコンテキストハンドルです。
xyは画像を描画する座標です。

fStyleは描画スタイルを以下の定数で指定します。

定数 説明
ILD_BLEND
ILD_SELECTED
ILD_BLEND50と同じ。
ILD_FOCUS ILD_BLEND25と同じ。
ILD_BLEND25 システムハイライトカラーと25%ブレンドして描画する。
画像マスクを使用しない場合は効果がない。
ILD_BLEND50 システムハイライトカラーと50%ブレンドして描画する。
画像マスクを使用しない場合は効果がない。
ILD_IMAGE オーバーレイの描画にマスクを必要としない場合、このフラグを設定する。
ImageList_DrawEx関数はマスクを無視して画像を描画する。
ILD_MASK マスクを描画する。
ILD_NORMAL イメージリストの背景色を使用して画像を描画する。
背景色がCLR_NONEの場合はマスクを使用して透過して描画する。
ILD_TRANSPARENT 背景色に関係なくマスクを使用して画像を透過して描画する。
イメージリストにマスクが含まれない場合は効果がない。

ImageList_DrawEx関数

画像の描画はImageList_DrawEx関数を使用することもできます。

BOOL ImageList_DrawEx(
 HIMAGELIST himl,
 int i,
 HDC hdcDst,
 int x,
 int y,
 int dx,
 int dy,
 COLORREF rgbBk,
 COLORREF rgbFg,
 UINT fStyle
);
イメージリストhimlの画像インデックスiを、デバイスコンテキストhdcDstに、座標x,y、サイズdx,dy、背景色rgbBk、前景色rgbBk、描画フラグfStyleを適用して描画する。
成功した場合は0以外を、失敗した場合は0を返す。

座標の指定までの引数、およびfStyleImageList_Draw関数と同じです。

dxは画像の横幅、dyは縦幅の指定です。
拡大/縮小されるわけではなく、画像上の描画する範囲の指定です。
0を指定すると画像の横幅、縦幅がそのまま使用されます。

rgbBkは画像の背景色の指定です。
マスクを使用する時、透過される領域を指定の色で塗りつぶします。
(マスクを使用しない場合は効果はありません)
RGBマクロで生成したCOLORREF型の値を指定できるほか、以下の定数も指定できます。

定数 説明
CLR_NONE 背景色を使用しない
(透明)
CLR_DEFAULT イメージリストに設定されている背景色を使用する

rgbFgは画像の前景色です。
ブレンド(ILD_BLEND25ILD_BLEND50)を使用する時の色を指定します。
RGBマクロで生成したCOLORREF型の値を指定できるほか、以下の定数も指定できます。

定数 説明
CLR_NONE デバイスコンテキストの背景色とブレンドする
CLR_DEFAULT システムのハイライト色とブレンドする

イメージリストの破棄

イメージリストは不要になったらImageList_Destroy関数で破棄します。

BOOL ImageList_Destroy(
 HIMAGELIST himl
);
イメージリストhimlを破棄する。
成功した場合は0以外を、失敗した場合は0を返す。

この関数は難しいことはないでしょう。

サンプルコード

ここまでのサンプルコードです。
画像は以下のビットマップを「IDB_BITMAP1」というIDでリソースに追加しています。
(8bitカラー、300 × 100)
サンプル画像


#pragma comment(lib, "Comctl32.lib")

#include <windows.h>
#include <commctrl.h>
#include "resource.h"

//ウィンドウの生成等は省略

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HIMAGELIST hImageList;

	HBITMAP hBmp;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_CREATE:
		//イメージリストの作成
		hImageList = ImageList_Create(
			100, 100,		//サイズ
			ILC_COLOR8,		//8ビットカラー
			3,				//画像3枚分の保存領域の確保
			0);

		//画像の追加
		hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
		ImageList_Add(
			hImageList,
			hBmp,
			NULL);

		//ロードした画像の破棄
		DeleteObject(hBmp);
		break;

	case WM_PAINT: //描画
		hdc = BeginPaint(hWnd, &ps);

		//イメージリストから描画
		ImageList_Draw(hImageList, 0, hdc, 0, 0, ILD_NORMAL);
		ImageList_Draw(hImageList, 1, hdc, 100, 50, ILD_NORMAL);
		ImageList_Draw(hImageList, 2, hdc, 200, 100, ILD_NORMAL);

		EndPaint(hWnd, &ps);
		break;

	case WM_DESTROY: //ウィンドウの破棄
		//イメージリストの破棄
		ImageList_Destroy(hImageList);
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

実行結果です。
クライアント領域の背景色はグレー(COLOR_APPWORKSPACE)にしています。
イメージリストの使用サンプル

追加した画像は一枚だけですが、イメージリスト作成時に指定した画像一枚当たりの横幅は「100」で、追加した画像の横幅は「300」です。
そのため、リスト上では三枚の画像が存在することになります。
サンプルコードではそれぞれ別の画像として描画できることを示すために描画位置を適当にズラしています。

マスクの使用

マスクを使用すると、画像の不要な部分を透過することができます。
マスクを使用するにはイメージリストの作成時に、引数flagsILC_MASKフラグを追加します。


HIMAGELIST hImageList = ImageList_Create(
			100, 100,
			ILC_COLOR24 | ILC_MASK, //マスクフラグの追加
			3,
			0);

マスクを使用する場合、描画するビットマップとは別に、描画したい領域を黒色で塗りつぶしたビットマップをもう一枚用意します。
ここでは先ほどの画像に加えて以下の画像を「IDB_BITMAP2」というリソースIDで読み込みます。
マスクのサンプル画像

このビットマップハンドルをImageList_Add関数の第三引数に指定します。


#pragma comment(lib, "Comctl32.lib")

#include <windows.h>
#include <commctrl.h>
#include "resource.h"

//ウィンドウの生成等は省略

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HIMAGELIST hImageList;

	HBITMAP hBmp1, hBmp2;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_CREATE:
		//イメージリストの作成
		hImageList = ImageList_Create(
			100, 100,
			ILC_COLOR8 | ILC_MASK,
			3,
			0);

		//画像の追加
		hBmp1 = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
		hBmp2 = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP2));
		ImageList_Add(
			hImageList,
			hBmp1,
			hBmp2);

		//ロードした画像の破棄
		DeleteObject(hBmp1);
		DeleteObject(hBmp2);
		break;

	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);

		//イメージリストから描画
		ImageList_Draw(hImageList, 0, hdc, 0, 0, ILD_NORMAL);
		ImageList_Draw(hImageList, 1, hdc, 100, 50, ILD_NORMAL);
		ImageList_Draw(hImageList, 2, hdc, 200, 100, ILD_NORMAL);

		EndPaint(hWnd, &ps);
		break;

	case WM_DESTROY: //ウィンドウの破棄
		//イメージリストの破棄
		ImageList_Destroy(hImageList);
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

実行結果です。
マスク画像中の塗りつぶされた領域が描画され、それ以外の領域は透過されます。
マスクの使用サンプル

ImageList_AddMasked関数

マスク用の画像を用意する方法は複雑な色を含む画像でも形状通りに切り抜くことが出来ます。
しかし今回のように単純な塗りの画像の場合はImageList_AddMasked関数を使用すると便利です。

int ImageList_AddMasked(
 HIMAGELIST himl,
 HBITMAP hbmImage,
 COLORREF crMask
);
イメージリストhimlにビットマップhbmImageを追加する。
マスクとして使用する色はカラー情報crMaskで指定する。

これはImageList_Add関数と同じくイメージリストに画像を追加しますが、第三引数がビットマップハンドルではなくCOLORREF型になっています。
(→文字色と背景色を参照)
ここに指定した色が透過されます。


hImageList = ImageList_Create(
	100, 100,
	ILC_COLOR8 | ILC_MASK,
	3,
	0);

//画像の追加
hBmp1 = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
//hBmp2 = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP2));
//ImageList_Add(
//	hImageList,
//	hBmp1,
//	hBmp2);
ImageList_AddMasked(
	hImageList,
	hBmp1,
	RGB(255, 255, 255));

//ロードした画像の破棄
DeleteObject(hBmp1);
//DeleteObject(hBmp2);

ただしこの関数は8bitカラー以下のビットマップに有効で、16bitカラー以上のビットマップには対応していません。
(と、Microsoftは説明しています)