文字と図形(とりあえず4種類)からなるアイコンをサクッと作れるツールを作ってみた。
キャプチャ
アイコンの保存は「エクスポート」から。
設定項目は「テンプレート」から保存/読みだしできます。
注意
- フォントには著作権があるので、作成したアイコンファイルを公開したりすることは問題があるかもしれません。
- アイコンのキャッシュのリフレッシュ処理が環境依存な都合で、Windows10向けになっています。
Windows7の場合は、ソースコード内の
ICON_CACHE_CLEAR_EXE_OPTIONの値を"-ClearIconCache"に変更すればOK。
コンパイル方法
csc /unsafe IconMaker.cs IconUtility.cs
ソースコード - IconMaker.cs
IconMaker.cs
usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.Data;usingSystem.Drawing;usingSystem.Drawing.Drawing2D;usingSystem.Drawing.Imaging;usingSystem.IO;usingSystem.Reflection;usingSystem.Runtime.InteropServices;usingSystem.Windows.Forms;usingIconUtility;[Serializable]publicclassSettingsClass{publicbytefg1Red;publicbytefg1Green;publicbytefg1Blue;publicbytefg2Red;publicbytefg2Green;publicbytefg2Blue;publicbytebg1Red;publicbytebg1Green;publicbytebg1Blue;publicbytebg2Red;publicbytebg2Green;publicbytebg2Blue;publicboolgradiation;publicstringfontName;publicstringtextLine1;publicstringtextLine2;publicDecimalfontSizeLine1;publicDecimalfontSizeLine2;publicintyOffsetLine1;publicintyOffsetLine2;publicstringbgFigureTypeName;publicinticonSize;}classIconMaker:Form{PictureBoxpct;//int iconSize = 64;staticreadonlyintDEFAULT_FONT_SIZE=14;staticreadonlyintCANVAS_SIZE=256;staticreadonlystringICON_CACHE_CLEAR_EXE_NAME="ie4uinit.exe";staticreadonlystringICON_CACHE_CLEAR_EXE_OPTION="-show";// windows10//Windows 10では「ie4uinit.exe -show」、//それ以外のバージョンでは「ie4uinit.exe -ClearIconCache」// const int ZOOM = 2;NumericUpDownnudSizeOfIcon;ComboBoxcmbBgFigureType;TextBoxtxtLine1;TextBoxtxtLine2;NumericUpDownnudFontSizeOfLine1;NumericUpDownnudFontSizeOfLine2;NumericUpDownnudOffsetYLine1;NumericUpDownnudOffsetYLine2;TextBoxtxtFontName;ButtonbtnColorForeground1;ButtonbtnColorForeground2;ButtonbtnColorBackground1;ButtonbtnColorBackground2;CheckBoxchkUseGradation;BitmapbmpToBeSaved;FontcurFontSizeIndependent;intcurFontStyle;ColorcolorForeground1;ColorcolorForeground2;ColorcolorBackground1;ColorcolorBackground2;PencurPen;FontcurFont;BrushcurBrush;// RedrawPreview呼びたびに更新PointFcurPoint;GraphicsPathcurPath;GraphicscurG;boolresumeRedraw;IconMaker(){Text="Icon Maker";resumeRedraw=false;colorForeground1=Color.Blue;colorForeground2=Color.Black;colorBackground1=Color.Red;colorBackground2=Color.Yellow;curFontSizeIndependent=newFont("メイリオ",(float)DEFAULT_FONT_SIZE);curFontStyle=(int)FontStyle.Regular;// Bold , Italic , Strikeout , Underline の bitOR を設定可能varmenuStrip1=newMenuStrip();// https://dobon.net/vb/dotnet/control/menustrip.htmlSuspendLayout();menuStrip1.SuspendLayout();vartemplateMenuItem=newToolStripMenuItem(){Text="テンプレート(&T)"};variconMenuItem=newToolStripMenuItem(){Text="エクスポート(&X)"};menuStrip1.Items.Add(templateMenuItem);menuStrip1.Items.Add(iconMenuItem);templateMenuItem.DropDownItems.Add(newToolStripMenuItem("開く(&O)...",null,(s,e)=>{OpenTemplateWithDialog();},Keys.Control|Keys.O));templateMenuItem.DropDownItems.Add(newToolStripMenuItem("保存(&S)...",null,(s,e)=>{SaveTemplateWithDialog();},Keys.Control|Keys.S));iconMenuItem.DropDownItems.Add(newToolStripMenuItem("アイコン(.ico)として保存(&I)...",null,(s,e)=>{SaveImageWithDialog("ico");},Keys.Control|Keys.I));iconMenuItem.DropDownItems.Add(newToolStripMenuItem("画像(.png)として保存(&P)...",null,(s,e)=>{SaveImageWithDialog("png");},Keys.Control|Keys.P));Controls.AddRange(newControl[]{newLabel(){Location=newPoint(10,35),Size=newSize(80,20),Text="アイコンサイズ"},nudSizeOfIcon=newNumericUpDown(){Location=newPoint(100,35),Size=newSize(60,20),Maximum=256,Value=64,Minimum=16,Increment=4}});nudSizeOfIcon.ValueChanged+=(s,e)=>{inticonSize=(int)nudSizeOfIcon.Value;pct.Size=newSize(iconSize,iconSize);RedrawPreview();};GroupBoxgrpText=newGroupBox(){Location=newPoint(10,60),Size=newSize(280,95),Text="テキスト"};Controls.Add(grpText);grpText.Controls.AddRange(newControl[]{newLabel(){Location=newPoint(140,20),Size=newSize(60,20),Text="サイズ"},newLabel(){Location=newPoint(210,20),Size=newSize(65,20),Text="位置調整"},newLabel(){Location=newPoint(10,40),Size=newSize(50,20),Text="1行目"},txtLine1=newTextBox(){Location=newPoint(60,40),Size=newSize(70,20),Text="icon"},nudFontSizeOfLine1=newNumericUpDown(){Location=newPoint(140,40),Size=newSize(60,20),Maximum=100,Value=30,Minimum=1,DecimalPlaces=1},nudOffsetYLine1=newNumericUpDown(){Location=newPoint(210,40),Size=newSize(60,20),Maximum=256,Value=0,Minimum=-256},newLabel(){Location=newPoint(10,65),Size=newSize(50,20),Text="2行目"},txtLine2=newTextBox(){Location=newPoint(60,65),Size=newSize(70,20),Text="maker"},nudFontSizeOfLine2=newNumericUpDown(){Location=newPoint(140,65),Size=newSize(60,20),Maximum=100,Value=15,Minimum=1,DecimalPlaces=1},nudOffsetYLine2=newNumericUpDown(){Location=newPoint(210,65),Size=newSize(60,20),Maximum=256,Value=18,Minimum=-256}});txtLine1.TextChanged+=(s,e)=>{RedrawPreview();};txtLine2.TextChanged+=(s,e)=>{RedrawPreview();};nudFontSizeOfLine1.ValueChanged+=(s,e)=>{RedrawPreview();};nudFontSizeOfLine2.ValueChanged+=(s,e)=>{RedrawPreview();};nudOffsetYLine1.ValueChanged+=(s,e)=>{RedrawPreview();};nudOffsetYLine2.ValueChanged+=(s,e)=>{RedrawPreview();};GroupBoxgrpFont=newGroupBox(){Location=newPoint(10,165),Size=newSize(280,50),Text="フォント"};Controls.Add(grpFont);ButtonbtnSelectFont;grpFont.Controls.AddRange(newControl[]{txtFontName=newTextBox(){Location=newPoint(10,20),Size=newSize(170,25),Text=curFontSizeIndependent.Name,ReadOnly=true},btnSelectFont=newButton(){Location=newPoint(185,15),Size=newSize(70,25),Text="変更..."}});btnSelectFont.Click+=(s,e)=>{SelectAndUpdateFontWithDialog();};GroupBoxgrpColor=newGroupBox(){Location=newPoint(10,225),Size=newSize(280,100),Text="色"};Controls.Add(grpColor);grpColor.Controls.AddRange(newControl[]{newLabel(){Location=newPoint(10,30),Size=newSize(40,20),Text="文字"},btnColorForeground1=newButton(){Location=newPoint(50,20),Size=newSize(30,20),FlatStyle=FlatStyle.Flat},btnColorForeground2=newButton(){Location=newPoint(70,30),Size=newSize(30,20),FlatStyle=FlatStyle.Flat},newLabel(){Location=newPoint(10,70),Size=newSize(40,20),Text="背景"},btnColorBackground1=newButton(){Location=newPoint(50,60),Size=newSize(30,20),FlatStyle=FlatStyle.Flat},btnColorBackground2=newButton(){Location=newPoint(70,70),Size=newSize(30,20),FlatStyle=FlatStyle.Flat},chkUseGradation=newCheckBox(){Location=newPoint(140,40),Size=newSize(100,25),Text="グラデーション",Checked=true}});SetFlatButtonColor(btnColorForeground1,colorForeground1);SetFlatButtonColor(btnColorForeground2,colorForeground2);SetFlatButtonColor(btnColorBackground1,colorBackground1);SetFlatButtonColor(btnColorBackground2,colorBackground2);btnColorForeground1.Click+=(s,e)=>{SelectAndUpdateColorWithDialog((Button)s,refcolorForeground1);};btnColorForeground2.Click+=(s,e)=>{SelectAndUpdateColorWithDialog((Button)s,refcolorForeground2);};btnColorBackground1.Click+=(s,e)=>{SelectAndUpdateColorWithDialog((Button)s,refcolorBackground1);};btnColorBackground2.Click+=(s,e)=>{SelectAndUpdateColorWithDialog((Button)s,refcolorBackground2);};chkUseGradation.Click+=(s,e)=>{ReflectGradationCheckToControls();RedrawPreview();};ReflectGradationCheckToControls();Controls.Add(pct=newPictureBox(){Location=newPoint(350,20),Size=newSize(CANVAS_SIZE,CANVAS_SIZE),Image=newBitmap(CANVAS_SIZE,CANVAS_SIZE),});Controls.Add(cmbBgFigureType=newComboBox(){Location=newPoint(20,350),Size=newSize(80,20),DropDownStyle=ComboBoxStyle.DropDownList});cmbBgFigureType.Items.AddRange(newstring[]{"なし","六角形","角丸","丸","四角"});cmbBgFigureType.SelectedIndex=1;cmbBgFigureType.SelectedIndexChanged+=(s,e)=>{RedrawPreview();};ClientSize=newSize(350+CANVAS_SIZE,400);Load+=(sender,e)=>{RedrawPreview();};curPen=Pens.Black;curBrush=Brushes.White;// とりあえず初期化しておくControls.Add(menuStrip1);MainMenuStrip=menuStrip1;menuStrip1.ResumeLayout(false);menuStrip1.PerformLayout();ResumeLayout(false);PerformLayout();}voidSetFlatButtonColor(Buttonbtn,Colorc){btn.BackColor=c;btn.FlatAppearance.MouseOverBackColor=c;btn.FlatAppearance.MouseDownBackColor=c;}voidSelectAndUpdateFontWithDialog(){FontDialogfd=newFontDialog();fd.Font=curFontSizeIndependent;fd.MaxSize=DEFAULT_FONT_SIZE;fd.MinSize=DEFAULT_FONT_SIZE;fd.FontMustExist=true;fd.AllowVerticalFonts=true;//fd.ShowColor = true;fd.ShowEffects=true;fd.FixedPitchOnly=false;fd.AllowVectorFonts=true;//ダイアログを表示するif(fd.ShowDialog()!=DialogResult.Cancel){curFontSizeIndependent=fd.Font;txtFontName.Text=curFontSizeIndependent.Name;RedrawPreview();}}voidSelectAndUpdateColorWithDialog(Buttonsender,refColorc){ColorDialogcd=newColorDialog();cd.Color=c;cd.FullOpen=true;if(cd.ShowDialog()==DialogResult.OK){c=cd.Color;SetFlatButtonColor(sender,c);RedrawPreview();}}voidReflectGradationCheckToControls(){booltmp=chkUseGradation.Checked;btnColorForeground2.Visible=tmp;btnColorBackground2.Visible=tmp;}voidmoveto(floatx,floaty){curPoint.X=x;curPoint.Y=y;}voidrmoveto(floatrx,floatry){curPoint.X+=rx;curPoint.Y+=ry;}voidlineto(floatx,floaty){curPath.AddLine(curPoint.X,curPoint.Y,x,y);curPoint.X=x;curPoint.Y=y;}voidrlineto(floatrx,floatry){curPath.AddLine(curPoint.X,curPoint.Y,curPoint.X+rx,curPoint.Y+ry);curPoint.X+=rx;curPoint.Y+=ry;}voidcharpath(strings){varsf=newStringFormat();sf.Alignment=StringAlignment.Center;// 横方向の中央sf.LineAlignment=StringAlignment.Center;// 縦方向の中央FontFamilyff=curFont.FontFamily;curPath.AddString(s,ff,curFontStyle,curFont.Size,curPoint,sf);}voidnewpath(){curPath.Reset();curPath.StartFigure();}voidclosepath(){curPath.CloseFigure();}voidstroke(){if(curG!=null){curG.DrawPath(curPen,curPath);}}voidfill(){if(curG!=null){curPath.FillMode=FillMode.Winding;curG.FillPath(curBrush,curPath);}}/*
void eofill()
{
if ( curG != null ) {
curPath.FillMode = FillMode.Alternate;
curG.FillPath(curBrush, curPath);
}
}
*/floatcos(floatdegree){return(float)Math.Cos((degree/180.0)*Math.PI);}floatsin(floatdegree){return(float)Math.Sin((degree/180.0)*Math.PI);}voidRedrawPreview(){if(resumeRedraw){return;}inticonSize=(int)nudSizeOfIcon.Value;bmpToBeSaved=CreateTransparentBitmap(iconSize,iconSize);curPath=newGraphicsPath();curG=Graphics.FromImage(bmpToBeSaved);curG.SmoothingMode=SmoothingMode.AntiAlias;try{if(chkUseGradation.Checked){curBrush=newLinearGradientBrush(newPoint(0,0),newPoint(iconSize,iconSize),colorBackground1,colorBackground2);}else{curBrush=newSolidBrush(colorBackground1);}DrawFigure();if(chkUseGradation.Checked){curBrush=newLinearGradientBrush(newPoint(0,0),newPoint(iconSize,iconSize),colorForeground1,colorForeground2);}else{curBrush=newSolidBrush(colorForeground1);}curFont=newFont(curFontSizeIndependent.Name,(float)nudFontSizeOfLine1.Value);newpath();moveto(iconSize/2,iconSize/2+(int)nudOffsetYLine1.Value);charpath(txtLine1.Text);closepath();fill();if(txtLine2.Text!=""){curFont=newFont(curFontSizeIndependent.Name,(float)nudFontSizeOfLine2.Value);newpath();moveto(iconSize/2,iconSize/2+(int)nudOffsetYLine2.Value);charpath(txtLine2.Text);closepath();fill();}}finally{curG.Dispose();curG=null;}intzoom=1;// 2;Graphicsg=Graphics.FromImage(pct.Image);g.FillRectangle(Brushes.White,0,0,bmpToBeSaved.Width*zoom,bmpToBeSaved.Height*zoom);// pct.Image.Width, pct.Image.Height);g.DrawImage(bmpToBeSaved,0,0,bmpToBeSaved.Width*zoom,bmpToBeSaved.Height*zoom);g.Dispose();pct.Refresh();}voidDrawFigure(){inticonSize=(int)nudSizeOfIcon.Value;stringfigureType=cmbBgFigureType.Text;if(figureType=="六角形"){newpath();for(intdeg=0;deg<360;deg+=60){floatx=iconSize/2+(iconSize/2-1)*cos(deg);floaty=iconSize/2+(iconSize/2-1)*sin(deg);if(deg==0){moveto(x,y);}else{lineto(x,y);}}closepath();fill();}elseif(figureType=="丸"){newpath();curPath.AddArc(0,0,iconSize,iconSize,0,360);closepath();fill();}elseif(figureType=="四角"){newpath();curPath.AddRectangle(newRectangle(0,0,iconSize,iconSize));closepath();fill();}elseif(figureType=="角丸"){intcornerSize=iconSize/2;newpath();curPath.AddArc(0,0,cornerSize,cornerSize,180,90);curPath.AddArc(iconSize-cornerSize,0,cornerSize,cornerSize,270,90);curPath.AddArc(iconSize-cornerSize,iconSize-cornerSize,cornerSize,cornerSize,0,90);curPath.AddArc(0,iconSize-cornerSize,cornerSize,cornerSize,90,90);closepath();fill();}}boolOpenTemplateWithDialog(){OpenFileDialogofd=newOpenFileDialog();ofd.FileName="テンプレート.xml";ofd.Filter="XMLファイル(*.xml)|*.xml";// ofd.Filter = "XMLファイル(*.xml)|*.xml|すべてのファイル(*.*)|*.*";// ofd.FilterIndex = 1;// ofd.Title = "開くファイルを選択してください";ofd.RestoreDirectory=true;// ofd.CheckFileExists = true;// ofd.CheckPathExists = true;if(ofd.ShowDialog()==DialogResult.OK){returnLoadSettingsFromXml(ofd.FileName);}else{returnfalse;}}boolSaveTemplateWithDialog(){SaveFileDialogsfd=newSaveFileDialog();sfd.FileName="新しいIconMakerテンプレート.xml";// sfd.InitialDirectory = @"C:\";sfd.Filter="XMLファイル(*.xml)|*.xml";sfd.Title="保存先のファイルを選択してください";sfd.RestoreDirectory=true;// sfd.OverwritePrompt = true;// sfd.CheckPathExists = true;//ダイアログを表示するif(sfd.ShowDialog()==DialogResult.OK){returnSaveSettingsToXml(sfd.FileName);}else{returnfalse;}}boolSaveImageWithDialog(stringfmt){SaveFileDialogsfd=newSaveFileDialog();// sfd.InitialDirectory = @"C:\";if(fmt=="ico"){sfd.FileName="新しいアイコン.ico";sfd.Filter="アイコンファイル(*.ico)|*.ico";}elseif(fmt=="png"){sfd.FileName="新しい画像.png";sfd.Filter="画像ファイル(*.png)|*.png";}else{returnfalse;}sfd.Title="保存先のファイルを選択してください";sfd.RestoreDirectory=true;// sfd.OverwritePrompt = true;// sfd.CheckPathExists = true;//ダイアログを表示するif(sfd.ShowDialog()==DialogResult.OK){boolsaveResult=false;if(fmt=="ico"){saveResult=SaveAsIcon(sfd.FileName);if(saveResult){RunClearCache();}}elseif(fmt=="png"){saveResult=SaveAsPng(sfd.FileName);}returnsaveResult;}else{returnfalse;}}boolSaveAsIcon(stringdestPath){Iconsicons;icons=newIcons();icons.AddIcon(bmpToBeSaved);try{icons.SaveToFile(destPath);returntrue;}catch(IOExceptione){MessageBox.Show(e.ToString());}returnfalse;}boolSaveAsPng(stringdestPath){try{bmpToBeSaved.Save(destPath,System.Drawing.Imaging.ImageFormat.Png);returntrue;}catch(IOExceptione){MessageBox.Show(e.ToString());}returnfalse;}voidRunClearCache(){try{System.Diagnostics.Process.Start(ICON_CACHE_CLEAR_EXE_NAME,ICON_CACHE_CLEAR_EXE_OPTION);}catch(System.ComponentModel.Win32Exceptione){Console.WriteLine(e);}}boolSaveSettingsToXml(stringdestPath){vart=newSettingsClass(){fg1Red=colorForeground1.R,fg1Green=colorForeground1.G,fg1Blue=colorForeground1.B,fg2Red=colorForeground2.R,fg2Green=colorForeground2.G,fg2Blue=colorForeground2.B,bg1Red=colorBackground1.R,bg1Green=colorBackground1.G,bg1Blue=colorBackground1.B,bg2Red=colorBackground2.R,bg2Green=colorBackground2.G,bg2Blue=colorBackground2.B,gradiation=chkUseGradation.Checked,fontName=curFontSizeIndependent.Name,fontSizeLine1=nudFontSizeOfLine1.Value,fontSizeLine2=nudFontSizeOfLine2.Value,yOffsetLine1=(int)nudOffsetYLine1.Value,yOffsetLine2=(int)nudOffsetYLine2.Value,textLine1=txtLine1.Text,textLine2=txtLine2.Text,iconSize=(int)nudSizeOfIcon.Value,bgFigureTypeName=cmbBgFigureType.Text,};varserializer=newSystem.Xml.Serialization.XmlSerializer(typeof(SettingsClass));try{varsw=newSystem.IO.StreamWriter(destPath,false,newSystem.Text.UTF8Encoding(false));try{serializer.Serialize(sw,t);}finally{sw.Close();}returntrue;}catch(IOExceptione){MessageBox.Show(e.ToString());returnfalse;}}boolLoadSettingsFromXml(stringsrcPath){// https://www.atmarkit.co.jp/ait/articles/1704/19/news021.htmlvarxmlSerializer=newSystem.Xml.Serialization.XmlSerializer(typeof(SettingsClass));SettingsClasst;//var xmlSettings = new System.Xml.XmlReaderSettings();try{using(varstreamReader=newStreamReader(srcPath,System.Text.Encoding.UTF8))using(varxmlReader=System.Xml.XmlReader.Create(streamReader))//, xmlSettings)){t=(SettingsClass)xmlSerializer.Deserialize(xmlReader);}}catch(IOExceptione){MessageBox.Show(e.ToString());returnfalse;}catch(InvalidOperationExceptione){MessageBox.Show(e.ToString());returnfalse;}resumeRedraw=true;try{colorForeground1=Color.FromArgb(t.fg1Red,t.fg1Green,t.fg1Blue);colorForeground2=Color.FromArgb(t.fg2Red,t.fg2Green,t.fg2Blue);colorBackground1=Color.FromArgb(t.bg1Red,t.bg1Green,t.bg1Blue);colorBackground2=Color.FromArgb(t.bg2Red,t.bg2Green,t.bg2Blue);SetFlatButtonColor(btnColorForeground1,colorForeground1);SetFlatButtonColor(btnColorForeground2,colorForeground2);SetFlatButtonColor(btnColorBackground1,colorBackground1);SetFlatButtonColor(btnColorBackground2,colorBackground2);chkUseGradation.Checked=t.gradiation;ReflectGradationCheckToControls();txtLine1.Text=t.textLine1;txtLine2.Text=t.textLine2;nudFontSizeOfLine1.Value=t.fontSizeLine1;nudFontSizeOfLine2.Value=t.fontSizeLine2;nudOffsetYLine1.Value=t.yOffsetLine1;nudOffsetYLine2.Value=t.yOffsetLine2;cmbBgFigureType.Text=t.bgFigureTypeName;nudSizeOfIcon.Value=t.iconSize;curFontSizeIndependent=newFont(t.fontName,(float)DEFAULT_FONT_SIZE);txtFontName.Text=curFontSizeIndependent.Name;}finally{resumeRedraw=false;}RedrawPreview();returntrue;}// 透過色で初期化BitmapCreateTransparentBitmap(intwidth,intheight){Bitmapbmp=newBitmap(width,height,PixelFormat.Format32bppArgb);BitmapDatabd=bmp.LockBits(newRectangle(0,0,width,height),ImageLockMode.WriteOnly,PixelFormat.Format32bppArgb);try{unsafe{// 書き込みbyte*ptr=(byte*)bd.Scan0;for(inty=0;y<height;y++){for(intx=0;x<width;x++){ptr[y*bd.Stride+4*x]=0;// Bptr[y*bd.Stride+4*x+1]=0;// Gptr[y*bd.Stride+4*x+2]=0;// Rptr[y*bd.Stride+4*x+3]=0;// alpha = 0 (透過)}}}}finally{bmp.UnlockBits(bd);}returnbmp;}[STAThread]staticvoidMain(){Application.Run(newIconMaker());}}ソースコード - IconUtility.cs
IconUtility.cs
usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.Data;usingSystem.Drawing;usingSystem.Drawing.Drawing2D;usingSystem.Drawing.Imaging;usingSystem.IO;usingSystem.Reflection;usingSystem.Runtime.InteropServices;namespaceIconUtility{publicclassIcons{classIconEntry{[StructLayout(LayoutKind.Sequential)]publicstructIconDir{publicshorticoReserved;// must be 0publicshorticoResourceType;//must be 1 for iconpublicshorticoResourceCount;publicIconDir(intn){icoReserved=0;icoResourceType=1;icoResourceCount=(short)n;}}[StructLayout(LayoutKind.Sequential)]publicstructIconDirEntry{byte_Width;byte_Height;publicbyteColorCount;publicbytereserved1;publicshortreserved2;publicshortreserved3;publicinticoDIBSize;publicinticoDIBOffset;publicintWidth{get{return(_Width>0)?_Width:256;}}publicintHeight{get{return(_Height>0)?_Height:256;}}publicIconDirEntry(intw,inth){if(w<0||w>256||h<0||h>256){thrownewException("Size parameter error");}_Width=(byte)w;_Height=(byte)h;ColorCount=0;reserved1=0;reserved2=0;reserved3=0;icoDIBSize=0;icoDIBOffset=0;}}[StructLayout(LayoutKind.Sequential)]structBitmapInfoHeader{publicintbiSize;// must be 40publicintbiWidth;publicintbiHeight;publicshortbiPlanes;// must be 1publicshortbiBitCount;// colorpublicintbiCompression;// 0:not compresspublicintbiSizeImage;publicintbiXPixPerMeter;publicintbiYPixPerMeter;publicintbiClrUsed;publicintbiClrImportant;publicBitmapInfoHeader(intw,inth){biSize=40;biWidth=w;biHeight=h*2;// 本体とmaskを含むため2倍とする決まりらしいbiPlanes=1;biBitCount=32;biCompression=0;biSizeImage=0;biXPixPerMeter=0;biYPixPerMeter=0;biClrUsed=0;biClrImportant=0;}}IconDirEntryiconDirEntry;BitmapInfoHeaderbitmapInfoHeader;byte[]bitmapBody;byte[]bitmapMask;publicSystem.Drawing.SizeSize{get{returnnewSystem.Drawing.Size(iconDirEntry.Width,iconDirEntry.Height);}}publicintWidth{get{returniconDirEntry.Width;}}publicintHeight{get{returniconDirEntry.Height;}}intBitPerPixel{get{returnbitmapInfoHeader.biBitCount;}}intCalcDIBSize(){returnMarshal.SizeOf(typeof(BitmapInfoHeader))+bitmapBody.Length+bitmapMask.Length;}publicintUpdateIconDirEntry(inticoDIBOffset){iconDirEntry.icoDIBOffset=icoDIBOffset;iconDirEntry.icoDIBSize=CalcDIBSize();returniconDirEntry.icoDIBSize;}publicvoidWriteIconDirEntryTo(BinaryWriterwriter){Icons.CopyDataToByteArray<IconDirEntry>(writer,iconDirEntry);}publicvoidWriteDataTo(BinaryWriterwriter){Icons.CopyDataToByteArray<BitmapInfoHeader>(writer,bitmapInfoHeader);writer.Write(bitmapBody);writer.Write(bitmapMask);}IconEntry(IconDirEntry_iconDirEntry,BitmapInfoHeader_bitmapInfoHeader,byte[]_bitmapBody,byte[]_bitmapMask){iconDirEntry=_iconDirEntry;bitmapInfoHeader=_bitmapInfoHeader;bitmapBody=_bitmapBody;bitmapMask=_bitmapMask;}// 本体publicIconEntry(Bitmapbmp,Color?alphaColor){intw=bmp.Width;inth=bmp.Height;if(w>256||h>256){thrownewException("size parameter error");}iconDirEntry=newIconDirEntry(w,h);bitmapInfoHeader=newBitmapInfoHeader(w,h);bitmapBody=newbyte[Icons.GetBitmapBodySize(w,h,bitmapInfoHeader.biBitCount)];bitmapMask=newbyte[Icons.GetBitmapMaskSize(w,h)];Draw32bppBitmapToData(bmp,alphaColor);}voidDraw32bppBitmapToData(Bitmapbmp,Color?alphaColor){Array.Clear(bitmapMask,0,bitmapMask.Length);Array.Clear(bitmapBody,0,bitmapBody.Length);BitmapDatabd=bmp.LockBits(newRectangle(0,0,bmp.Width,bmp.Height),ImageLockMode.ReadOnly,PixelFormat.Format32bppArgb);try{IntPtrptr=bd.Scan0;byte[]pixels=newbyte[bd.Stride*bmp.Height];Marshal.Copy(ptr,pixels,0,pixels.Length);intmaskStride=(((Width+7)/8+3)/4)*4;inticoStride=Width*4;for(inty=0;y<bd.Height;y++){for(intx=0;x<bd.Width;x++){intposIco=y*icoStride+4*x;intbytePosMask=y*maskStride+x/8;intbitPosMask=7-(x%8);intpos=(bd.Height-1-y)*bd.Stride+x*4;bitmapBody[posIco]=pixels[pos];//blue;bitmapBody[posIco+1]=pixels[pos+1];//green;bitmapBody[posIco+2]=pixels[pos+2];//red;bitmapBody[posIco+3]=pixels[pos+3];//alphaif(pixels[pos+3]==0||(alphaColor!=null&&pixels[pos]==alphaColor.Value.B&&pixels[pos+1]==alphaColor.Value.G&&pixels[pos+2]==alphaColor.Value.R)){//bitmapMask[bytePosMask] |= (byte)(1<<bitPosMask);// 32bit色のiconだとmaskではなくalpha channelが使用されるっぽいbitmapBody[posIco+3]=0x00;}}}}finally{bmp.UnlockBits(bd);}}}intUpdateIconDirEntries(){iconDir.icoResourceCount=(short)iconEntries.Count;intoffset=Marshal.SizeOf(typeof(IconEntry.IconDir))+iconEntries.Count*Marshal.SizeOf(typeof(IconEntry.IconDirEntry));for(inti=0;i<iconEntries.Count;i++){offset+=iconEntries[i].UpdateIconDirEntry(offset);}returnoffset;}staticintGetBitmapBodySize(intw,inth,intbitCount){return((((w*bitCount+7)/8)+3)/4)*4*h;}staticintGetBitmapMaskSize(intw,inth){return((((w+7)/8)+3)/4)*4*h;}staticTStructCopyDataToStruct<TStruct>(BinaryReaderreader)whereTStruct:struct{varsize=Marshal.SizeOf(typeof(TStruct));varptr=IntPtr.Zero;try{ptr=Marshal.AllocHGlobal(size);Marshal.Copy(reader.ReadBytes(size),0,ptr,size);return(TStruct)Marshal.PtrToStructure(ptr,typeof(TStruct));}finally{if(ptr!=IntPtr.Zero){Marshal.FreeHGlobal(ptr);}}}staticvoidCopyDataToByteArray<TStruct>(BinaryWriterwriter,TStructs)whereTStruct:struct{varsize=Marshal.SizeOf(typeof(TStruct));varbuffer=newbyte[size];varptr=IntPtr.Zero;try{ptr=Marshal.AllocHGlobal(size);Marshal.StructureToPtr(s,ptr,false);Marshal.Copy(ptr,buffer,0,size);}finally{if(ptr!=IntPtr.Zero){Marshal.FreeHGlobal(ptr);}}writer.Write(buffer);}IconEntry.IconDiriconDir;List<IconEntry>iconEntries;// ------------------------------------------------------------------------------// public memberspublicintCount{get{returniconEntries.Count;}}publicIcons(){iconDir=newIconEntry.IconDir(0);iconEntries=newList<IconEntry>();}publicvoidAddIcon(Bitmapbmp,ColoralphaColor){iconEntries.Add(newIconEntry(bmp,alphaColor));UpdateIconDirEntries();}publicvoidAddIcon(Bitmapbmp){iconEntries.Add(newIconEntry(bmp,null));UpdateIconDirEntries();}publicboolSaveToFile(stringpath){//int size = UpdateIconDirEntries();using(varfs=newFileStream(path,FileMode.Create)){using(varwriter=newBinaryWriter(fs)){CopyDataToByteArray<IconEntry.IconDir>(writer,iconDir);foreach(vartiniconEntries){t.WriteIconDirEntryTo(writer);}foreach(vartiniconEntries){t.WriteDataTo(writer);}}}returntrue;}}}