c# - 以编程方式创建高质量的 ico 文件

标签 c# .net-4.0

我正在尝试在 C# 中以编程方式从 PNG 文件创建高质量图标(表示:suitable for Win Vista/7/8)以用作快捷方式图标。由于 Bitmap.GetHIcon() 函数不支持这些类型的图标,并且我想避免外部依赖项或库,我目前正在使用我发现的略微修改的 ICO 编写器 here on SO . 我有工作代码,但我在 Windows 显示这些图标的方式中遇到了一些故障。 相关代码为:

// ImageFile contains the path to PNG file
public static String IcoFromImageFile(String ImageFile) {
    //...       
    Image iconfile = Image.FromFile(ImageFile);

    //Returns a correctly resized Bitmap        
    Bitmap bm = ResizeImage(256,256,iconfile);                
    SaveAsIcon(bm, NewIconFile);

    return NewIconFile;

}        

// From: https://stackoverflow.com/a/11448060/368354
public static void SaveAsIcon(Bitmap SourceBitmap, string FilePath) {
    FileStream FS = new FileStream(FilePath, FileMode.Create);
    // ICO header
    FS.WriteByte(0); FS.WriteByte(0);
    FS.WriteByte(1); FS.WriteByte(0);
    FS.WriteByte(1); FS.WriteByte(0);

    // Image size
    // Set to 0 for 256 px width/height
    FS.WriteByte(0);
    FS.WriteByte(0);
    // Palette
    FS.WriteByte(0);
    // Reserved
    FS.WriteByte(0);
    // Number of color planes
    FS.WriteByte(1); FS.WriteByte(0);
    // Bits per pixel
    FS.WriteByte(32); FS.WriteByte(0);

    // Data size, will be written after the data
    FS.WriteByte(0);
    FS.WriteByte(0);
    FS.WriteByte(0);
    FS.WriteByte(0);

    // Offset to image data, fixed at 22
    FS.WriteByte(22);
    FS.WriteByte(0);
    FS.WriteByte(0);
    FS.WriteByte(0);

    // Writing actual data
    SourceBitmap.Save(FS, System.Drawing.Imaging.ImageFormat.Png);

    // Getting data length (file length minus header)
    long Len = FS.Length - 22;

    // Write it in the correct place
    FS.Seek(14, SeekOrigin.Begin);
    FS.WriteByte((byte)Len);
    FS.WriteByte((byte)(Len >> 8));

    FS.Close();
}

这可以编译并工作,但有一个问题。 Windows 无法正确显示快捷方式上的图标。我也以编程方式执行此操作,但即使我手动执行(通过文件属性、更改图标)也会发生这种情况。问题是图标被截断(图像本身显示正确)。这取决于图片,但通常只显示实际图标的 20% 左右。如果我在像 XNView 这样的图像查看器中打开文件,它会显示完整且正确,但 MS Paint 不会。 我制作了这个屏幕截图,以及一个正确显示的图标以供比较

enter image description here

我怀疑错误出在 ICO 保存方法上,但即使在将它们与十六进制编辑器中正常显示的 ICO 进行比较后,标题也正确写入,但 PNG 图像部分本身似乎有所不同。有人有想法吗?我也欢迎更好、更简单的解决方案。

最佳答案

您的 ico 文件设置为仅以 16 位精度保存嵌入位图的长度,但 PNG 文件太大(大于 65535 字节),因此长度记录溢出。

即以下行不完整:

// Write it in the correct place
FS.Seek(14, SeekOrigin.Begin);
FS.WriteByte((byte)Len);
FS.WriteByte((byte)(Len >> 8));

您可以添加这些行:

FS.WriteByte((byte)(Len >> 16));
FS.WriteByte((byte)(Len >> 24));

作为清洁和性能问题,我通常会避免所有这些单独的写入,而只使用带有字节数组参数的写入重载。此外,您可以考虑使用 Save-To-MemoryStream 然后对 header 进行一次写入(现在可以使用 PNG 的字节长度)和一次写入以复制 PNG,而不是使用有点棘手的 Save-To-File 然后查找数据从内存流到文件。

您真正应该解决的另一点是处置IDisposable 资源。即使你还不需要,因为你没有遇到任何问题,总有一天它会咬你一口,如果你有一个相当小的代码库,里面有各种未处理的一次性用品,你将很难找到你的泄漏和/或僵局。一般而言:永远不要调用Close,除非您真的无法避免它 - 而是将您的FileStream 包装在using 中堵塞。类似地,ImageBitmap 是一次性的并分配本地资源,但至少你不会遇到任何锁定问题(AFAIK - 但安全总比后悔好) .

关于c# - 以编程方式创建高质量的 ico 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14157134/

相关文章:

.net - .net 4.5 是否与 .net 4.0 并行工作?

c# - 为什么没有 ConfigurationManager?

c# - 通过 AutoMapper 映射获取相关数据?

c# - 使用 block 与功能 block

c# - 比较两个列表

c# - 在 C# (.NET 4.0) 中填充和播放音频缓冲区

c# - 如何进行 WCF 序列化以在通用字典上保留非默认比较器?

c# - 如何在 C# 中获取网络摄像头图像?

visual-studio-2010 - 忍者.net 4.0

c# - 如何向 DataGrid 添加行和列?