イメージリストを使ってみる

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

イメージリスト

イメージリスト(image list)とは、コモンコントロールライブラリ(COMCTL32.DLL)によって提供されている、同じサイズのイメージ(アイコンやビットマップなど)を効率的に管理するための機能のことです。

イメージリスト自体は、同じサイズのイメージの集合を持つオブジェクトで、個々のイメージリストオブジェクトはハンドルによって識別されます。1つのイメージリストが持っているそれぞれのイメージは、 0 から始まるインデックスによって参照されます。

イメージリストにはマスクを用いて、特定の領域を透明化して描画するという機能があります。すなわち、右図のように、アイコンを表示させたようなイメージを使うことができるようになります。

イメージリストの作成手順

イメージリストを作成する手順は、以下のようになります。

  1. InitCommonControls 関数を呼び出す。
  2. ImageList_Create 関数を呼び出しイメージリストを作成する。
  3. 表示するイメージのビットマップオブジェクトを作成する。
  4. ImageList_Add 関数または ImageList_AddMasked 関数を呼び出して、作成したビットマップオブジェクトのイメージをイメージリストに追加する。
  5. ビットマップオブジェクトを削除する。
  6. イメージリストが不要になったら、 ImageList_Destroy 関数を呼び出してイメージリストを破棄する。

コモンコントロールライブラリの初期化

上でも述べた通りイメージリストはコモンコントロールライブラリから提供されるオブジェクトであり、イメージリストを使うには、コモンコントロールを使用しない場合でも 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