これまでアイコンファイルを保存するときは以下のようにしていました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
private void buttonSave_Click(object sender, EventArgs e) { Bitmap bmp = new Bitmap(32, 32); for(int row = 0; row < 32; row++) { for(int column = 0; column < 32; column++) { bmp.SetPixel(column, row, pictureBox[column, row].BackColor); } } SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "アイコン| *.ico"; if(dlg.ShowDialog() != DialogResult.OK) return; //ビットマップをアイコンへ変換 Icon icon = Icon.FromHandle(bmp.GetHicon()); Stream outStream = new FileStream(dlg.FileName, FileMode.Create, FileAccess.Write); icon.Save(outStream); outStream.Dispose(); bmp.Dispose(); icon.Dispose(); } |
この方法ではアイコンが16色になってしまいます。
以下の方法はどうでしょうか。この方法はダメです。この方法ではたしかにicoファイルが保存されるのですが、Icon形式ではなく、PNG形式で保存されます。そこで実際にIconを使おうとすると例外が発生していまいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
private void buttonSave_Click(object sender, EventArgs e) { Bitmap bmp = new Bitmap(32, 32); for(int row = 0; row < 32; row++) { for(int column = 0; column < 32; column++) { bmp.SetPixel(column, row, pictureBox[column, row].BackColor); } } SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "アイコン| *.ico"; if(dlg.ShowDialog() != DialogResult.OK) return; bmp.Save(dlg.FileName, System.Drawing.Imaging.ImageFormat.Icon); bmp.Dispose(); } |
上の方法で保存したファイルをつかって以下の処理をしようとすると例外が発生します。
1 |
Icon icon = new Icon("ファイルのパスを指定"); |
下の場合は例外は発生しません。ビットマップファイルでもアイコン化することができます。
1 2 |
Bitmap bitmap = new Bitmap("ファイルのパスを指定"); Icon temp = Icon.FromHandle(bitmap.GetHicon()); |
これだったらアイコンをファイルとして保存せずに、BMPやPNGファイルからIconを作ったほうが早そうです。
アイコンファイルの保存をするのであれば、自前でやるしかなさそうです。
アイコンフォーマットは以下のようになっています。
参考にしたサイト
これらによるとアイコンファイルのフォーマットは以下のようになっていることがわかります。
アイコンヘッダ……ICOファイルに関する包括的情報を格納している。
ディレクトリ[1..n]……個々の画像に関する包括的情報を格納している。
アイコン画像データ[1..n]……AND/XORビットマップ形式か、PNG形式
それからビットマップファイルフォーマットは以下のようになっています。
参考にしたサイト
実際に保存されているアイコンファイルがどのようになっているのか実際に開いて調べてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
//アイコンヘッダー 0 ヘッダ予約済み。常に0(2バイト) 0 1 タイプ。アイコン (.ico) は 1、カーソル (.cur) は 2(2バイト) 0 1 このファイルにある画像の数(2バイト) 0 //ディレクトリ 32 幅。256ピクセルなら0になる 32 高さ。256ピクセルなら0になる。 16 色数。256色以上なら0 0 予約済み 0 カラープレーン数(2バイト) 0 0 ICOフォーマットではピクセル毎のビット数(2バイト) 0 232 対応するビットマップデータのバイト数 (4バイト)※ 2 0 0 // 232, 2, 0 ,0 ということは 256 * 2 + 232 = 744バイト 22 対応するビットマップデータまでのファイル内オフセット(4バイト)※ 0 0 0 // この場合、ビットマップデータは22バイト目から始まる //ここから画像のデータ 40 ビットマップのヘッダーのサイズ(4バイト) 0 0 0 32 ビットマップの幅(4バイト) 0 0 0 64 ビットマップの高さ(4バイト)(AND/XOR操作のため高さは2倍になっている) 0 0 0 1 planes(2バイト) 0 4 ビットカウント(この場合は4ビット)(2バイト) 0 0 compression(4バイト) 0 0 0 128 imageSize(4バイト) 2 0 0 0 xPixelsPerM(4バイト) 0 0 0 0 yPixelsPerM(4バイト) 0 0 0 0 colorUsed(4バイト) 0 0 0 0 colorsImportant(4バイト) 0 0 0 // ここからカラーマップ(ビットカウントは4なので16個存在する) // カラーマップ1(4バイト) 0 0 0 0 // カラーマップ2(4バイト) 0 0 128 0 // カラーマップ3(4バイト) 0 128 0 0 // カラーマップ4(4バイト) 0 128 128 0 // カラーマップ5(4バイト) 128 0 0 0 // カラーマップ6(4バイト) 128 0 128 0 // カラーマップ7(4バイト) 128 128 0 0 // カラーマップ8(4バイト) 128 128 128 0 // カラーマップ8(4バイト) 192 192 192 0 // カラーマップ10(4バイト) 0 0 255 0 // カラーマップ11(4バイト) 0 255 0 0 // カラーマップ12(4バイト) 0 255 255 0 // カラーマップ13(4バイト) 255 0 0 0 // カラーマップ14(4バイト) 255 0 255 0 // カラーマップ15(4バイト) 255 255 0 0 // カラーマップ16(4バイト) 255 255 255 0 //以下、ビットマップデータが続く・・・ |
そこでフルカラーで保存するにはこのなかの値を適切に書き換える必要があります。
ディレクトリ内の対応するビットマップデータのバイト数を変更する。
画像データ内のビットカウント 「32」に変更する。
カラーマップは必要ない。
このようなメソッドをつくってみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
// バイトを格納するリスト(最後に配列に変換する) List<Byte> bytes = new List<byte>(); //ヘッダ //(2バイト)予約済み(常に0) bytes.Add(0); bytes.Add(0); //(2バイト)アイコンなら1 bytes.Add(1); bytes.Add(0); //(2バイト)このファイルにある画像の数 bytes.Add(1); bytes.Add(0); // ディレクトリ //(1バイト)幅 bytes.Add(32); //(1バイト)高さ bytes.Add(32); //(1バイト)色数。256色以上なら0 bytes.Add(0); //(1バイト)予約済み bytes.Add(0); //(2バイト)カラープレーン数 0でよい bytes.Add(0); bytes.Add(0); //(2バイト)ICOフォーマットではピクセル毎のビット数 0でよい bytes.Add(0); bytes.Add(0); //(4バイト)対応するビットマップデータのバイト数 // 32×32でXORビットマップは各ピクセル4バイト、ANDビットマップは各ピクセル1ビットなので // ビットマップのヘッダー40バイト + 32×32×4 + 32×32÷8 // 40 + 4096 + 128 = 4264 バイト // リトルエンディアンなので 168, 16, 0, 0 bytes.Add(168); bytes.Add(16); bytes.Add(0); bytes.Add(0); //(4バイト)対応するビットマップデータまでのファイル内オフセット // アイコンヘッダーが6バイトでディレクトリがひとつあたり16バイトなので 6+16=22となる bytes.Add(22); bytes.Add(0); bytes.Add(0); bytes.Add(0); // 1番目の画像のデータ //(4バイト)ビットマップのヘッダーのサイズ(40で固定) bytes.Add(40); bytes.Add(0); bytes.Add(0); bytes.Add(0); //(4バイト)ビットマップの幅 bytes.Add(32); bytes.Add(0); bytes.Add(0); bytes.Add(0); //(4バイト)ビットマップの高さ(AND/XOR操作のため倍の値にする) bytes.Add(64); bytes.Add(0); bytes.Add(0); bytes.Add(0); //(2バイト)planes bytes.Add(1); bytes.Add(0); //(2バイト)bitCount フルカラーなら32にする bytes.Add(32); bytes.Add(0); //(4バイト)圧縮 compression 0でよい bytes.Add(0); bytes.Add(0); bytes.Add(0); bytes.Add(0); //(4バイト)imageSize // 32×32でXORビットマップは各ピクセル4バイト、ANDビットマップは各ピクセル1ビットなので // 32×32×4 + 32×32÷8 = 4224 なのだが、ここは0でよいらしい bytes.Add(0); bytes.Add(0); bytes.Add(0); bytes.Add(0); //(4バイト)xPixelsPerM bytes.Add(0); bytes.Add(0); bytes.Add(0); bytes.Add(0); //(4バイト)yPixelsPerM bytes.Add(0); bytes.Add(0); bytes.Add(0); bytes.Add(0); //(4バイト)colorUsed bytes.Add(0); bytes.Add(0); bytes.Add(0); bytes.Add(0); //(4バイト)colorsImportant bytes.Add(0); bytes.Add(0); bytes.Add(0); bytes.Add(0); // 32ピクセル×32ピクセル×4バイト for(int row = 31; row >= 0; row--) { for(int colum = 0; colum < 32; colum++) { Color color = panels[colum, row].BackColor; bytes.Add(color.B); bytes.Add(color.G); bytes.Add(color.R); bytes.Add(0); } } // 32ピクセル×32ピクセル×1ビット // 32ピクセル×32ピクセル÷8 for(int i = 0; i< 128; i++) { bytes.Add(0); } byte[] Bytes = bytes.ToArray(); // これをファイルに保存する |
一応、これでアイコンを保存することができます。