C#

アイコンの作成

アイコン・ファイルのフォーマットがわかったので、アイコンを作るところもやってみた。思いつく限りをトライして手探りで調べた。忘れないように記録しておく。以下の手順は、アイコン・ファイルを読み込んでICONIMAGE構造体を作成するところまではできているという前提で説明する。

方針

  • APIのCreateIconIndirect()でアイコンのハンドルを作成し、Icon.FromHandle()でIconクラスのインスタンスを作成する。
  • ICONINFO.hbmMaskには、APIのCreateBitmap()で作成した1ビットカラーのDDB (Device Dependent Bitmap) のハンドルを指定する。
  • ICONINFO.hbmColorには、APIのCreateCompatibleBitmap()で作成したDDBのハンドルを指定する。
  • ビットマップの内容は、一旦DIB (Device Independent Bitmap) を作ってから、APIのBilBltでDDBに転送する。

手順

  • 準備
    1. マスク・ビットマップ用のカラーテーブル (#1) を用意する。2色でインデックス0は黒、インデックス1は白にする。
  • マスク・ビットマップの作成
    1. CreateDIBSection()で1ビットカラーのDIB (#2) を作成する[*1]
    2. ICONIMAGE.icANDの内容を、DIB (#2) のビットマップ内容にコピーする。icANDはDIBなので、そのままコピーすれば良い。
    3. SetDIBColorTable()でマスク・ビットマップ用のカラーテーブル (#1) を設定する。
    4. CreateBitmap()で1ビットカラーのDDB (#3) を作成する[*1]
    5. BitBltでDIB (#2) からDDB (#3) へコピーする。
  • カラー・ビットマップの作成(マスク・ビットマップの作成とだいたい同じ)
    1. ICONIMAGE.icHeaderの情報を元に、CreateDIBSection()でDIB (#4) を作成する[*1]
    2. ICONIMAGE.icXORの内容を、DIB (#4) のビットマップ内容にコピーする。icXORはDIBなので、そのままコピーすれば良い。
    3. 作成したDIB (#4) がインデックス・カラーの場合は、SetDIBColorTable()でICONIMAGE.icColorsを設定する。
    4. CreateCompatibleBitmap()でDDB (#5) を作成する[*1]。ディスプレイ・デバイスと互換性のあるビットマップ。多くの場合32ビットカラーになると思う。
    5. BitBltでDIB (#4) からDDB (#5) へコピーする。
  • アイコンの作成
    1. ICONINFO.hbmMaskにマスク・ビットマップ (#3) のハンドル設定する。
    2. ICONINFO.hbmColorにカラー・ビットマップ (#5) のハンドルを設定する。
    3. CreateIconIndirect()でアイコンのハンドルを作成する。
    4. Icon.FromHandle()でIconクラスのインスタンスを作成する。
[*1]
ICONIMAGE.icHeader.biHeightは、2倍の高さになっている。ビットマップを作成するときは元の高さにする必要がある。

失敗

  • BitmapクラスのGetHbitmap()メソッドは、CreateCompatibleBitmap()のように、ディスプレイと互換性のあるビットマップを作成する。1ビットカラーのビットマップを作ってそのハンドルを引き取っても無理。32ビットカラーのビットマップが返ってきてしまう。Bitmapクラスでは、マスク・ビットマップのハンドルを作成できない。
  • ICONINFO構造体のhbmMaskやhbmColorに、CreateDIBSection()で作成したビットマップのハンドルを指定すると、CreateIconIndirect()で失敗する。
  • DIBを作ってDDBに転送する代わりに、CreateDIBitmap()でDDBを作ることはできる。デバイス・コンテキスト次第で1ビットカラーのビットマップも作成できる。BitBltを使うよりずっと簡単だが、残念なことにカラーテーブルが反映されない。惜しい。もう一歩という気がする。何かやり方があるのだろうと思うが、探せなかった。CreateCompatibleBitmap()とSetDIBits()の組み合わせでもうまくいかなかった。CreateDIBitmap()と同じ結果になった。

その他

  • 手順には概要を記したが、実際のコーディングではデバイス・コンテキストを作成したり、ビットマップを選択したり、後始末があったりなどして、少々面倒くさい。
  • CreateDIBSection()では、BITMAPINFO構造体のbmiColorsを見ていないようだ。
  • DIBのビットマップ内容はDWORD(4バイト)境界だが、DDBのビットマップ内容はWORD(2バイト)境界になっているようだ。
  • CreateIconIndirect()で使用したマスク・ビットマップとカラー・ビットマップを削除するタイミングはよくわからないが、これらのビットマップを管理し、不要になったら削除しなければならないそうだ。
  • IconクラスのDispose()でアイコンのリソースも解放してくれそうだが、Icon.FromHandle()で作成したIconはDispose()してもリソースを解放してくれないので、APIのDestroyIcon()で削除する必要がある。ちなみにCursorクラスでも似たような状況。
  • BitmapクラスのインスタンスからIconクラスのインスタンスを作るには、Bitmap.GetHicon()メソッドを使うのが簡単。32ビットARGBのビットマップなら、ちゃんと透過のアイコンを作ってくれる。
  • アイコン・ファイルからIconクラスのインスタンスを作るには、Iconクラスのコンストラクタを使う。
  • 要するに、あまり必要ない知識か。

(2016/05/25 初稿)