在C#中将图像转换为图标

28

我有一个项目,可以将图像格式文件转换为图标文件。但是,在转换图像后,图像的颜色发生了变化。

以下是我的代码

Bitmap theBitmap = new Bitmap(theImage, new Size(width, height));
IntPtr Hicon = theBitmap.GetHicon();// Get an Hicon for myBitmap.
Icon newIcon = Icon.FromHandle(Hicon);// Create a new icon from the handle.
FileStream fs = new FileStream(@"c:\Icon\" + filename + ".ico", FileMode.OpenOrCreate);//Write Icon to File Stream

进入图像描述

有人知道如何解决这个问题吗?


你需要通过一些更好的处理来运行它。GetHicon() 不是无损转换。 - DonBoitnott
这个答案中有几个链接指向一个非常相似的问题,可能会有所帮助。 这个也可能有用:{这个](http://stackoverflow.com/a/17208258/62576)。 - Ken White
这个问题是由我自己 @KenWhite 提出的。 - r.vengadesh
@r.vengadesh:那你为什么要在这里重复呢? - Ken White
超级晚的评论,但是:他的代码实际上没有将图标保存到磁盘中。它只是创建了一个空的图标文件。要真正保存图标,您需要在其块的末尾添加以下代码: newIcon.Save(fs); - AS7K
显示剩余2条评论
8个回答

17

Bitmap.GetHicon()非常适合创建在任何可以运行.NET代码的Windows版本上都能良好工作的图标,包括旧版的Windows 98和Windows 2000。这些操作系统不支持花哨的图标。

因此,您得到的图标仅使用预先设定的基本颜色调色板中的16种颜色,结果可能会相当令人失望。

Bitmap或Icon类没有选项可以获得更好的结果。通常情况下,您需要使用图标编辑器来创建良好的图标,其中应该包括不同尺寸和颜色深度的多个图像,以便它们能够与任何视频适配器设置和任何操作系统版本兼容。特别是从1600万色缩减到256或16种颜色,这是一个需要多种方法才能实现的非平凡操作,而且没有一种方法是完美的。一个好的图标编辑器具备您需要的工具,让这些操作足够好用。


更新:这已经成为一个非常过时的问题,XP是昨天的产物了。如今,您可以使用这个代码生成外观非常好看的图标。


是的,Bitmap.GetHicon()很好,支持性强,占用空间小,像素处理速度快(因为只使用16种颜色)。为了获得更好的效果,你可以尝试一些像Picasa这样的直方图处理器。你可以选择自己喜欢的颜色和风格。 - user9371102

10
这是我的方法,可以将png转换为图标,包括透明度:
public void ConvertToIco(Image img, string file, int size)
{
    Icon icon;
    using (var msImg = new MemoryStream())
    using (var msIco = new MemoryStream())
    {
        img.Save(msImg, ImageFormat.Png);
        using (var bw = new BinaryWriter(msIco))
        {
            bw.Write((short)0);           //0-1 reserved
            bw.Write((short)1);           //2-3 image type, 1 = icon, 2 = cursor
            bw.Write((short)1);           //4-5 number of images
            bw.Write((byte)size);         //6 image width
            bw.Write((byte)size);         //7 image height
            bw.Write((byte)0);            //8 number of colors
            bw.Write((byte)0);            //9 reserved
            bw.Write((short)0);           //10-11 color planes
            bw.Write((short)32);          //12-13 bits per pixel
            bw.Write((int)msImg.Length);  //14-17 size of image data
            bw.Write(22);                 //18-21 offset of image data
            bw.Write(msImg.ToArray());    // write image data
            bw.Flush();
            bw.Seek(0, SeekOrigin.Begin);
            icon = new Icon(msIco);
        }
    }
    using (var fs = new FileStream(file, FileMode.Create, FileAccess.Write))
    {
        icon.Save(fs);
    }
}

这个不起作用。图片必须是哪种格式(颜色深度为8、24、32)? - Elmue
当我将这段代码插入到已经从文件读取了一个字节数组的代码中(然后还原),它对我很有用。msImg可以简单地是一个32x32像素PNG文件的字节数组,然后这个代码就可以正常工作了。从msIco创建图标是不必要的:你只需要运行File.WriteAllBytes(file, bytes),其中bytes是一个字节数组即可。 - Tim

8
如果您只需要32位图标,可以使用FreeImage:http://freeimage.sourceforge.net
string icoFile = "C:\path\to\file.ico";
FreeImageBitmap fiBitmap = new FreeImageBitmap(theBitmap);
fiBitmap.Rescale(48, 48, FREE_IMAGE_FILTER.FILTER_BICUBIC);
fiBitmap.Save(icoFile);
fiBitmap.Rescale(32, 32, FREE_IMAGE_FILTER.FILTER_BICUBIC);
fiBitmap.SaveAdd(icoFile);
fiBitmap.Rescale(16, 16, FREE_IMAGE_FILTER.FILTER_BICUBIC);
fiBitmap.SaveAdd(icoFile);

如果您需要对32、8、4和1位图标进行完整的支持,则需要创建自己的ico格式编写器。我在开发自己基于C#的png到ico转换器http://iconverticons.com时遇到了这个问题。
实际上,这并不太难; ico文件格式规范可以在这里找到: http://msdn.microsoft.com/en-us/library/ms997538.aspx 您还需要从这里获取位图头规范,因为ico是位图的子集: http://msdn.microsoft.com/en-us/library/dd183376.aspx

2

我已经创建了一个简单的函数,它接受要转换的图像字符串和保存路径。.ico格式可以在Chrome和其他浏览器上使用。

public void ConvertToIco(string pathImageToConvert, string pathToSave)
{
    Bitmap bmp = new Bitmap(pathImageToConvert);
    bmp.Save(pathToSave, System.Drawing.Imaging.ImageFormat.Icon);
}

这里是路径的示例

pathImageToConvert = "yourpath/" + image.extension
pathToSave  = "yourpath/" + image.ico  

1
您可以尝试这个:

Bitmap theBitmap = new Bitmap(theImage, new Size(width, height));

theBitmap.Save(@"C:\Icon\" + filename + ".ico", System.Drawing.Imaging.ImageFormat.Icon);

2
该方法不适用于窗口文件夹图标。 - r.vengadesh
@r.vengadesh 你能解释一下吗? - Andy
1
我想创建窗口图标文件。因此,我只使用上述代码。如果我使用您的代码,则会创建.ico文件。但我们没有将该图像用于窗口文件夹图像。 - r.vengadesh

1

已解决Vista及以上版本的问题

如果您需要动态生成图标(我的应用程序具有不断变化的数量),如果必须使用编辑器,那么您将会很失望。

Ruiwei Bu(darkfall)在github上展示了如何实现。由于我们当然不支持XP和更低版本,这对我们来说已经足够。

使用我放置代码的类的示例:

Dim ico As New Icons With {.state = Iconstates.OK, .UpgradeNum = "123"}
Dim tempfile = "C:\file5.ico"
ico.GetIcon(tempfile)
Dim newicon As New Icon(tempfile)

使用Darkfall代码的类:

Imports System.Drawing.Imaging
Imports System.IO

Public Class Icons
  Property UpgradeNum As String
  Property state As Iconstates

  Function GetIcon(Optional OptionalSave As String = "") As Icon
    Dim bmp As New Bitmap(16, 16)

    Using g = Graphics.FromImage(bmp)
      g.Clear(Color.Transparent)
      g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
      Select Case state
        Case Iconstates.OK
          g.FillEllipse(Brushes.Green, 1, 1, 14, 14)
      End Select
      g.DrawString(UpgradeNum, New Font("Small Fonts", 6), Brushes.Aquamarine, 0, 0)
    End Using

    bmp.Save(OptionalSave & ".png")

    Dim outputStream As New MemoryStream()
    Dim size As Integer = bmp.Size.Width
    If Not ConvertToIcon(bmp, outputStream, size) Then
      Return Nothing
    End If
    If OptionalSave > "" Then

      Using file = New FileStream(OptionalSave, FileMode.Create, System.IO.FileAccess.Write)
        outputStream.WriteTo(file)
        file.Close()
      End Using
    End If

    outputStream.Seek(0, SeekOrigin.Begin)

    Return New Icon(outputStream)
  End Function




  ''' <summary>
  ''' Converts a PNG image to an icon (ico)
  ''' </summary>
  ''' <param name="inputBitmap">The input stream</param>
  ''' <param name="output">The output stream</param>
  ''' <param name="size">Needs to be a factor of 2 (16x16 px by default)</param>
  ''' <param name="preserveAspectRatio">Preserve the aspect ratio</param>
  ''' <returns>Wether or not the icon was succesfully generated</returns>
  Public Shared Function ConvertToIcon(inputBitmap As Bitmap, output As Stream, Optional size As Integer = 16, Optional preserveAspectRatio As Boolean = False) As Boolean

    Dim width As Single = size, height As Single = size


    Dim newBitmap = New Bitmap(inputBitmap, New Size(CInt(width), CInt(height)))
    If newBitmap Is Nothing Then
      Return False
    End If

    ' save the resized png into a memory stream for future use
    Using memoryStream As New MemoryStream()
      newBitmap.Save(memoryStream, ImageFormat.Png)

      Dim iconWriter = New BinaryWriter(output)
      If output Is Nothing OrElse iconWriter Is Nothing Then
        Return False
      End If

      ' 0-1 reserved, 0
      iconWriter.Write(CByte(0))
      iconWriter.Write(CByte(0))

      ' 2-3 image type, 1 = icon, 2 = cursor
      iconWriter.Write(CShort(1))

      ' 4-5 number of images
      iconWriter.Write(CShort(1))

      ' image entry 1
      ' 0 image width
      iconWriter.Write(CByte(width))
      ' 1 image height
      iconWriter.Write(CByte(height))

      ' 2 number of colors
      iconWriter.Write(CByte(0))

      ' 3 reserved
      iconWriter.Write(CByte(0))

      ' 4-5 color planes
      iconWriter.Write(CShort(0))

      ' 6-7 bits per pixel
      iconWriter.Write(CShort(32))

      ' 8-11 size of image data
      iconWriter.Write(CInt(memoryStream.Length))

      ' 12-15 offset of image data
      iconWriter.Write(CInt(6 + 16))

      ' write image data
      ' png data must contain the whole png data file
      iconWriter.Write(memoryStream.ToArray())

      iconWriter.Flush()
    End Using

    Return True
  End Function


End Class
' https://gist.github.com/darkfall/1656050
'=======================================================
'Service provided by Telerik (www.telerik.com)
'Conversion powered by NRefactory.
'Twitter: @telerik
'Facebook: facebook.com/telerik
'=======================================================

0

看起来 .Net Framework 没有任何图标编码器;因此,您无法保存任何真正的图标文件。保存的文件只是一个 PNG 文件。


0

如果您的项目中直接包含BMP图像,那么使用BMP很容易。下面的代码可以正常工作。

  <code>
           try
            {
                Bitmap b = new Bitmap(".\\ConnApp.bmp");
                IntPtr pIcon = b.GetHicon();
                Icon i = Icon.FromHandle(pIcon);
                this.Icon = i;
                logInfo("Icon loaded ?" + i);

                // save ico file. This you can directly assign Icon to your Form (Properties -> Icon -> choose)
                // the exe Icon you can assign from Application -> Properties -> Icon and Manifest -> select icon. 
                FileStream SourceStream = File.Create("\\pos2.ico");
                i.Save(SourceStream);
                SourceStream.Close();
                i.Dispose();
            } catch (Exception ex1) {
                logInfo("Error on iconload " + ex1);
            }
  </code>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接