今回は、コモンコントロールでアイコンなどのイメージを使う場合に必要となるイメージリストについてやってみましょう。

イメージリスト(image list)とは、コモンコントロールライブラリ(COMCTL32.DLL)によって提供されている、同じサイズのイメージ(アイコンやビットマップなど)を効率的に管理するための機能のことです。
イメージリスト自体は、同じサイズのイメージの集合を持つオブジェクトで、個々のイメージリストオブジェクトはハンドルによって識別されます。1つのイメージリストが持っているそれぞれのイメージは、 0 から始まるインデックスによって参照されます。
イメージリストにはマスクを用いて、特定の領域を透明化して描画するという機能があります。すなわち、右図のように、アイコンを表示させたようなイメージを使うことができるようになります。
イメージリストを作成する手順は、以下のようになります。
上でも述べた通りイメージリストはコモンコントロールライブラリから提供されるオブジェクトであり、イメージリストを使うには、コモンコントロールを使用しない場合でも InitCommonControls 関数または InitCommonControlsEx 関数を呼び出してライブラリの初期化を行なわなければならないことになっています。(実際には呼び出さなくても動作はするのですが。)
イメージリストオブジェクトを作成するには、 ImageList_Create 関数を呼び出します。
HIMAGELIST ImageList_Create(
int cx, // イメージの幅
int cy, // イメージの高さ
UINT flags, // イメージリストのタイプ
int cInitial, // イメージ数の初期値
int cGrow // 拡張イメージ数
);
cx パラメータおよび cy パラメータはイメージのサイズをピクセル単位で指定します。
flags パラメータにはイメージリストのタイプを指定します。例えば、マスクを使用するフルカラー(24ビット色)のイメージリストを作成する場合には $19 (ILC_COLOR24 | ILC_MASK) を指定します。
cInitial パラメータおよび cGrow パラメータは、それぞれイメージ数の初期値とイメージリストリサイズ時のイメージ数です。
最初に述べた通り、イメージリストはWindowsオブジェクトとして、ハンドルによって管理されます。 ImageList_Create 関数は、戻り値として作成したイメージリストのハンドルを返します。
イメージリストを作成できたら、 ImageList_Add 関数または ImageList_AddMasked 関数を呼び出してイメージを追加していきます。
int ImageList_Add(
HIMAGELIST himl, // イメージリストのハンドル
HBITBAP hbmImage, // イメージのビットマップハンドル
HBITMAP hbmMask // マスクのビットマップハンドル
);
ImageList_Add 関数は、マスクなしイメージを追加する場合や、マスクイメージのビットマップからマスク付きイメージを作成する場合に使用します。マスクにはモノクロのビットマップを指定します。
int ImageList_AddMasked(
HIMAGELIST himl, // イメージリストのハンドル
HBITBAP hbmImage, // イメージのビットマップハンドル
COLORREF crMask // マスク生成に使用する色
);
ImageList_AddMasked 関数は、マスク付きイメージを作成する際に、マスクイメージを指定した色から生成する場合に使用します。このとき、 crMask パラメータには 0x00BBGGRR の形で色を指定します。0xRR, 0xGG, 0xBB はそれぞれ R,G,B の輝度を 16 進数で表したものです。
ImageList_AddMasked 関数は、ビットマップイメージの中に crMask パラメータで指定された色があると、その部分の色情報をすべて黒に変換していしまいます。たとえ、指定されたイメージリストがマスクなしで作成されたものであっても、イメージ追加時にこの関数を使った場合には、マスク色として指定された部分は黒に変換されます。そこで、マスクなしで作成されたイメージリストにこの関数でイメージを追加する場合には、 crMask パラメータに 0 (黒)を指定するようにしてください。
これら2つの関数は、戻り値として新しく追加されたイメージのインデックスを返します。イメージの追加に失敗した場合は -1 を返します。
どちらの関数も、イメージをビットマップのハンドルで指定するようになっています。したがって、あらかじめビットマップオブジェクトを作成しておく必要があります。ここでは、HSPウィンドウのイメージからDIBセクションオブジェクトを作成して、それを指定することにします。
ビットマップオブジェクトの作成については、『ビットマップオブジェクトを作成する』の項を参照してください。
マスクを指定するビットマップイメージとして、モノクロのビットマップが使われます。モノクロビットマップの作成については説明していませんが、 CreateCompatibleBitmap 関数の代わりに、 CreateBitmap 関数を使うことによって、モノクロのビットマップを作成できます。ただし、マスクを使用する場合は、マスク部分を他で使われていない色で塗りつぶして ImageList_AddMasked 関数に渡した方が簡単なので、モノクロビットマップについてはここでは説明しないことにします。
通常、追加するイメージのサイズは、イメージリスト作成時に指定した大きさでなければなりません。ただし、ビットマップイメージの幅をイメージリスト作成時に指定した幅の整数倍にすると、一度に複数のイメージをイメージリストに追加することができます。このとき、イメージの数はビットマップの幅から自動的に決定されるようになっています。また、関数の戻り値として、追加されたイメージのうち最初のイメージのインデックスが返ります。
イメージリストにイメージを追加したら、作成したビットマップはもう不要なので、 DeleteObject 関数を呼び出して削除しておきましょう。
イメージリストが不要になった場合には ImageList_Destroy 関数を呼び出してイメージリストを破棄しなければなりません。
BOOL ImageList_Destroy(
HIMAGELIST himl // イメージリストのハンドル
);
この関数は、指定されたイメージリストを破棄して、イメージリストの情報の保持のために使われていたメモリを解放します。
破棄した後のイメージリストのハンドルは無効なものになってしまうので、使用することはできません。
イメージリスト中のイメージを描画させたい場合は、 ImageList_Draw 関数を使います。
BOOL ImageList_Draw(
HIMAGELIST himl, // イメージリストのハンドル
int index, // イメージのインデックス
HDC hdcDest, // 描画先デバイスコンテキスト
int x, // 描画先x座標
int y, // 描画先y座標
UINT fStyle // 描画スタイル
);
hdcDest パラメータには描画先デバイスコンテキストのハンドルを指定します。現在描画中のHSPウィンドウに描画するには、ウィンドウの BMSCR 構造体に含まれているメモリデバイスコンテキストのハンドルを指定することになります。それには
mref bmscr, 67
hdc = bmscr.4 ; 描画中ウィンドウのデバイスコンテキスト
として、この値を指定します。また、この場合実際にディスプレイに表示されているウィンドウに反映させるには redraw を実行する必要があります。
fStyle パラメータには、描画スタイルを指定します。イメージリストがマスクつきで作成されている場合に、マスク部分を透明化して描画するには 0x0001 (ILD_TRANSPARENT) を指定します。
さて、実際にスクリプトを書いてみます。今回は以下の画像を使用します。この画像のサイズは横 (32×4) ピクセル、縦 32 ピクセルで、背景に使われている色の各輝度は、赤 166 (0xA6)、緑 202(0xCA)、青240(0xF0) ですので、背景色を透過させるようにするには、 ImageList_AddMasked 関数のマスク色を指定する引数に 0x00F0CAA6 を指定すれば OK です。
(iconlist.bmp)
次のスクリプトを実行させると、マスクを用いない場合のイメージと用いた場合のイメージがそれぞれ描画されます。ここでは、マスクによる透明化を確認するために cls 命令を用いていったん画面を塗りつぶしています。モジュール定義命令 DrawImageList では、メモリ上のデバイスコンテキストに描画をしているため、実際に画面に表示されているウィンドウに反映させるには redraw を実行させる必要があります。
#include "llmod.as"
#module ;###### ビットマップオブジェクト作成モジュール #########
;===============================================================
; CreateDIB DIBセクションオブジェクト作成
;===============================================================
#deffunc CreateDIB int, int, int, int
mref px, 0 ; HSPウィンドウx座標
mref py, 1 ; HSPウィンドウy座標
mref sx, 2 ; xサイズ
mref sy, 3 ; yサイズ
mref stt, 64 ; stat
mref bmscr, 67 ; 描画中ウィンドウのBMSCR構造体
; DIBセクションオブジェクト作成
pm = bmscr.4, sx, sy
dllproc "CreateCompatibleBitmap", pm, 3, D_GDI@
hbitmap = stat ; ビットマップオブジェクトのハンドル
; メモリデバイスコンテキスト作成
pm = bmscr.4
dllproc "CreateCompatibleDC", pm, 1, D_GDI@
hdcMemory = stat ; メモリデバイスコンテキストのハンドル
; ビットマップをデバイスコンテキストに選択
pm = hdcMemory, hbitmap
dllproc "SelectObject", pm, 2, D_GDI@
; HSPウィンドウからビットマップにイメージをコピー
pm = hdcMemory, 0, 0, sx, sy, bmscr.4, px, py, $CC0020
dllproc "BitBlt", pm, 9, D_GDI@
; デバイスコンテキストを削除
dllproc "DeleteDC", hdcMemory, 1, D_GDI@
stt = hbitmap ; ビットマップオブジェクトのハンドル
return
#global ;############# モジュール終わり ########################
#module ;######### イメージリスト操作モジュール ################
;===============================================================
; CreateImageList イメージリストの作成
;===============================================================
#deffunc CreateImageList int, int, int, int
mref sx, 0 ; イメージの幅
mref sy, 1 ; イメージの高さ
mref flags, 2 ; イメージリストのタイプ
mref num, 3 ; イメージの数
; イメージリストの作成
pm.0 = sx
pm.1 = sy
pm.2 = flags
pm.3 = num
pm.4 = 0
dllproc "ImageList_Create", pm, 5, D_COMCTL@
return
;===============================================================
; AddImageList イメージリストに描画中ウィンドウのイメージ追加
;===============================================================
#deffunc AddImageList int, int, int, int, int, int
mref himl, 0 ; イメージリストのハンドル
mref cx, 1 ; x座標
mref cy, 2 ; y座標
mref sx, 3 ; xサイズ
mref sy, 4 ; yサイズ
mref crmask, 5 ; マスク生成に用いる色
mref stt, 64 ; stat
; DIBオブジェクトの作成
CreateDIB cx, cy, sx, sy
hbitmap = stat ; ビットマップハンドル
; ビットマップをイメージリストに追加
pm.0 = himl
pm.1 = hbitmap
pm.2 = crmask
dllproc "ImageList_AddMasked", pm, 3, D_COMCTL@
index = stat ; 最初のイメージのインデックス
; ビットマップオブジェクトを削除
dllproc "DeleteObject", hbitmap, 1, D_GDI@
stt = index ; イメージのインデックスをstatに格納
return
;===============================================================
; DestroyImageList イメージリストの破棄
;===============================================================
#deffunc DestroyImageList int
mref himl, 0 ; イメージリストのハンドル
; イメージリストを破棄
pm.0 = himl
dllproc "ImageList_Destroy", pm, 1, D_COMCTL@
return
;===============================================================
; DrawImageList イメージリストのイメージ描画
;===============================================================
#deffunc DrawImageList int, int, int, int
mref himl, 0 ; イメージリストのハンドル
mref index, 1 ; イメージのインデックス
mref cx, 2 ; x座標
mref cy, 3 ; y座標
mref bmscr, 67 ; 描画中ウィンドウのBMSCR構造体
; イメージを描画
pm.0 = himl
pm.1 = index
pm.2 = bmscr.4 ; デバイスコンテキストのハンドル
pm.3 = cx
pm.4 = cy
pm.5 = 0x0001 ; ILD_TRANSPARENT
dllproc "ImageList_Draw", pm, 6, D_COMCTL@
return
#global ;############# モジュール終わり ########################
; ビットマップファイルの読み込み
buffer 2,,, 0 ; フルカラーモード
picload "iconlist.bmp"
; 24bit DIB イメージリスト作成(マスクなし)
CreateImageList 32, 32, 0x0018, 4
himg1 = stat ; イメージリストのハンドル
AddImageList himg1, 0, 0, 32*4, 32
idx1 = stat ; イメージのインデックス(ここでは0になる)
; 24bit DIB イメージリスト作成(マスクあり)
CreateImageList 32, 32, 0x0018 | 0x0001, 4
himg2 = stat ; イメージリストのハンドル
AddImageList himg2, 0, 0, 32*4, 32, 0x00F0CAA6
idx2 = stat ; イメージのインデックス(ここでは0になる)
gsel 0
cls 1 ; いったん塗りつぶし
pos 20, 10 : mes "マスクなし"
repeat 4
DrawImageList himg1, idx1+cnt, 40*cnt+30, 30
loop
pos 20, 80 : mes "マスクあり"
repeat 4
DrawImageList himg2, idx2+cnt, 40*cnt+30, 100
loop
redraw ; redraw は必須
onexit *lb_on_quit
stop
*lb_on_quit
; 終了時の処理(イメージリスト破棄)
DestroyImageList himg1
DestroyImageList himg2
end