アイコンの保存方法

最終更新 2003 10/03

「アイコンエディタ使えばいいじゃん?」と言われると何も言い返せないのですが
アイコン( .ico )のデータフォーマットはビットマップよりも複雑です。

アイコンフォーマットについての資料がほとんどなくて(あっても適当だった)
バイナリエディタ使ってかなり時間かけて調べました。
ほとんどデータ解析に近かったです。

画像コンバータとかアイコンエディタなんか作りたい方は参考にどうぞ。


アイコンファイルのフォーマット
ファイルヘッダ6 byte
情報ヘッダ16 byte
BITMAPINFOHEADER40 byte
パレットデータモノクロ 2 * 4
16色  16 * 4
256色 256 * 4
ピクセルデータモノクロ 横幅 * 高さ / 8
16色  横幅 * 高さ / 2
256色 横幅 * 高さ
マスクデータ128 byte ( 固定 )
アイコンファイルは1つのファイルに複数のアイコンを登録することができます。 1つのアイコンファイルに10種類のアイコンデータを持たせる事もできます。 アイコンファイルに3つのアイコンを登録する場合は、このようなフォーマットに なります。
ファイルヘッダ6 byte
情報ヘッダ116 byte
情報ヘッダ216 byte
情報ヘッダ316 byte
BITMAPINFOHEADER140 byte
パレットデータ1モノクロ 2 * 4
16色  16 * 4
256色 256 * 4
ピクセルデータ1モノクロ 横幅 * 高さ / 8
16色  横幅 * 高さ / 2
256色 横幅 * 高さ
マスクデータ1128 byte ( 固定 )
BITMAPINFOHEADER240 byte
パレットデータ2モノクロ 2 * 4
16色  16 * 4
256色 256 * 4
ピクセルデータ2モノクロ 横幅 * 高さ / 8
16色  横幅 * 高さ / 2
256色 横幅 * 高さ
マスクデータ2128 byte ( 固定 )
BITMAPINFOHEADER340 byte
パレットデータ3モノクロ 2 * 4
16色  16 * 4
256色 256 * 4
ピクセルデータ3モノクロ 横幅 * 高さ / 8
16色  横幅 * 高さ / 2
256色 横幅 * 高さ
マスクデータ3128 byte ( 固定 )
アイコンファイルのフォーマットは、大まかに分けるとこういう構造に なっています。 以下でファイルヘッダや情報ヘッダの詳しい説明をします。
ファイルヘッダ
icoReserved2 byte
icoResourceType2 byte
icoResourceCount2 byte
icoReserved
常に0を指定します。
icoResourceType
アイコンの場合は1を指定します。
icoResourceCount
登録するアイコンの数。 1以上の数を指定します。
情報ヘッダ
icoWidth1 byte
icoHeight1 byte
icoColorCount1 byte
icoReserved11 byte
icoReserved22 byte
icoReserved32 byte
icoDIBSize4 byte
icoDIBOffset4 byte
icoWidth
アイコンの横幅を指定します。 16か32のどちらかでなければいけません。
icoHeight
アイコンの高さを指定します。 16か32のどちらかでなければいけません。
icoColorCount
色数を指定します。 モノクロなら2、16色なら16、256色なら0を指定します。
icoReserved1
常に0を指定します。
icoReserved2
アイコンの横幅が32の時は31が指定されている場合がありますが 0、あるいは指定しないでも問題ありません。
icoReserved3
アイコンの高さが32の時は31が指定されている場合がありますが 0、あるいは指定しないでも問題ありません。
icoDIBSize
sizeof( BITMAPINFOHEADER ) + 色数×4 + イメージサイズ + マスクサイズ + 32 を指定します。 最後の + 32 は何の意味があるのか分かりませんが、色数に関係なく + 32 しないとうまくいきません。
icoDIBOffset
1つ目のアイコンデータの BITMAPINFOHEADER までのバイト数。 登録するアイコンが1つの場合、ファイルヘッダ + 情報ヘッダ 登録するアイコンが2つの場合、 ファイルヘッダ + 情報ヘッダ1 + 情報ヘッダ2 登録するアイコンが3つの場合、 ファイルヘッダ + 情報ヘッダ1 + 情報ヘッダ2 + 情報ヘッダ3 4つ以上もこんな感じで指定します。
BITMAPINFOHEADER
ビットマップファイルのヘッダにあるものと同じですが、アイコンの 場合は一部のメンバ変数の扱いが違うので注意が必要です。
biSize4 byte
biWidth4 byte
biHeight4 byte
biPlanes2 byte
biBitCount2 byte
biCompression4 byte
biSizeImage4 byte
biXPelsPerMeter4 byte
biYPelsPerMeter4 byte
biClrUsed4 byte
biClrImportant4 byte
biSize
sizeof( BITMAPINFOHEADER ) を指定します。
biWidth
アイコンの横幅を指定します。 16か32のどちらかでなければいけません。
biHeight
アイコンの高さ×2を指定します。 16×2か32×2のどちらかでなければいけません。
biPlanes
1を指定します。
biBitCount
モノクロなら2、16色なら4、256色なら8を指定します。
biCompression
0を指定します。
biSizeImage
ピクセルデータのサイズにマスクデータのサイズを足した値を 指定します。 マスクデータのサイズは128バイト固定です。 アイコンの色数や大きさに関係なく128です。 計算式は 横幅 * 高さ + 128 です。
biXPelsPerMeter
0を指定します。
biYPelsPerMeter
0を指定します。
biClrUsed
モノクロなら2、16色なら16、256色なら256を指定しますが 未指定、あるいは0でも問題ありません。
biClrImportant
0を指定します。
パレットデータ
パレットデータはビットマップのパレットと扱いは同じです。 ここに指定するデータもビットマップと同じなので、ビットマップの パレットの扱い方を知っている方は以下の説明は読み飛ばして下さい。 アイコンやビットマップで使っているパレットは、絵を描くときに使う パレットとイメージは同じです。 アイコンファイルはインデックスカラーしか使えないようです。 ですから、16ビット(65536色)以上の色数でアイコンを 描くことはできません。(多分) インデックスカラーというのは、2色、16色あるいは256色使える パレットを用意して、そのパレットに用意している色に番号を振ります。 赤は0、青は1、黒は50・・・という具合です。 2色(モノクロ)の時は0〜1、16色の時は0〜15、256色の 時は0〜255まで番号を振ることができます。 アイコンの絵のデータにはこの色につけた番号を使います。 絵の色が、赤・赤・青・黒・赤・・・という順番で並んでいる時は 絵は、0・0・1・50・0・・・というデータになります。 こうすることで、2色の場合は1ビット、16色の場合は4ビット 256色の場合は1バイトで絵のデータを表現できるので、色数が 少ないほどファイルサイズを減らす事ができます。 逆に、24ビットで絵を表現するとしたら、1色につき3バイトの 容量が必要になります。 256色なら1色につき1バイトで済みます。 ファイルサイズにどれだけ差がつくかは一目瞭然です。 アイコンは最大でも32×32ピクセルしかないので、16ビットや 24ビットなんて色数は必要ないです。 多分、そんな理由でアイコンは2色、16色、256色のインデックス カラーしか使えないようになっているんだと思います。 パレットの基礎知識を説明した所で本題に戻ります。 アイコンファイルのパレットデータの部分には、色につけた番号の順に 実際の色データを並べます。 色データは1色を表現するのに4バイト使います。 詳しい事は 文字の背景色を変える をご覧下さい。 プログラム中では RGBQUAD という構造体を使うのがいいと思います。 この構造体に色データを格納しておきます。
RGBQUAD palette[256]; //256色のパレットを作成 //パレットの0番に赤を割り振る //rgbReserved は使わないので常に0 palette[0].rgbRed = 255; palette[0].rgbGreen = 0; palette[0].rgbBlue = 0; palette[0].rgbReserved = 0;
ファイルに保存するときは、この構造体の値をそのまま指定して あげるだけなので楽です。
// hFile は CreateFile() であらかじめ値を取得しとく DWORD bc; WriteFile( hFile, palette, sizeof(palette), &bc, NULL );
ピクセルデータ
ピクセルデータはアイコンの絵のデータのことです。 ファイルに保存する時は左右はそのままで、上下だけ逆に反転して 書き込まないといけません。 なんでそんな面倒なことになっているのかは知りません。 こういう絵があったら こんな風に上下を反転して書き込む 32*32のサイズのデータを上下反転して書き込むサンプルを 書いておきます。
// image_data にあらかじめ絵のデータを入れておく
// image_data はこんな風に宣言しておく
// BYTE image_data[ 32 * 32 ];
// new でメモリを確保してもいいし、GlobalAlloc() を
// 使ってもOK

BYTE new_data[ 32 * 32 ];   //ファイルに書き込むデータ

BYTE i, j;
for( i = 0; i < 32; i++ )
{
    for( j = 0; j < 32; j++ )
    {
        DWORD pos = ( 31 - i ) * 32 + j;
        BYTE  data = image_data[ pos ];
        new_data[ i * 32 + j ] = data;
    }
}

// hFile の値は CreateFile() でゲットしておく

DWORD bc;
WriteData( hFile, new_data, sizeof( new_data ), &bc, NULL );
マスクデータ
マスクデータは、絵のデータのどこを表示して、どこを透明に するかを指定するためのデータです。 絵を表示する部分に0、透明にする部分に1を指定します。 0と1しかないので、絵のデータ1ピクセルを1ビットで表現します。 ようするに、横8ピクセルで1バイトになります。 色データの扱い方は、2色(モノクロ)ビットマップと同じです。 モノクロビットマップと違うのは、マスクデータのデータサイズが 128バイト固定だということです。 なので、16×16のモノクロビットマップでマスクデータを 作った場合、16×16のピクセルデータのサイズは64バイト ですから、64バイト分サイズが足りません。 「128バイトメモリを確保して、そこに64バイト突っ込めば  いいのでは?」 そう考えるのが普通です。 しかし、アイコンファイルのマスクデータは32×32を基準にした データ構造になっています。 以下に例を書きます。 FF 55 82 FF 8C 11 4A BB .... これは32×32のアイコンのマスクデータの一部だと思って下さい。 16進数で書いてあります。 FF とか 82 とが1バイト(横8ピクセル)分のデータに相当します。 このデータを16×16のフォーマットで書くとこうなります。 FF 00 55 00 82 00 FF 00 8C 00 11 00 4A 00 BB 00 .... ようするに、16×16の場合は、偶数バイトにマスクデータを 書いて、奇数バイトは0で埋めるという作業が必要になります。 ファイルに書き込む時はピクセルデータと同様、上下を反転してから 書き込みます。 長くなってしまったので、次で実際にビットマップで作った絵を アイコンに変換するプログラムサンプルを紹介します。
アイコンを保存する関数を作る

| next | home |