花了不少时间,修复了一个SharpIco生成图标的bug
SharpIco bug修好了?真把自己当回事了,高分辨率图片转ico就崩,这逻辑够清奇。原来是位深度闹的,24转32而已,用点真本事解决?
目录5 个章节
前言
上个月我用 dotnet9 AOT 开发了一个 ico 图标生成工具 SharpIco
这个实用小工具一经发布就受到不少朋友的关注
最近还被做成了网站,有图形化界面来一键生成 ico 图标,更方便普通用户的使用
出现问题
不过有网友在 issues 反馈了问题 「高分辨率图片在转换完成后,图片会损坏导致无法打开」

我测试了一下确实会这样
这个就很诡异了
看代码也可以发现,每个尺寸的图片都是通过 Resize 出来的
这样的话,无论输入的图片是多大的,最终都会缩小成几个指定的尺寸
using var original = Image.Load(sourcePng);
foreach (var size in sizes) {
using var clone = original.Clone(ctx => ctx.Resize(size, size));
using var ms = new MemoryStream();
clone.SaveAsPng(ms);
images.Add(ms.ToArray());
}
而且生成的 ico 并不是真正的损坏,在 Photoshop 里还是能打开的,只不过在系统的图片应用打开看不了,而且也无法正确解析图片元数据
排查问题
起初我求助于大模型爷爷(Claude4)
不过给我的几个方案都没啥用
要不就是叫我低于 256 分辨率使用 BMP 保存
然后还得写入 BIP 头什么的乱七八糟的,麻烦得一批
最后还得是我自己仔细观察
写入每张图片的代码是这样的
foreach (var image in images) {
using var ms = new MemoryStream(image);
using var img = Image.Load(ms);
writer.Write((byte)(img.Width == 256 ? 0 : img.Width)); // width
writer.Write((byte)(img.Height == 256 ? 0 : img.Height)); // height
writer.Write((byte)0); // colors in palette
writer.Write((byte)0); // reserved
writer.Write((ushort)1); // color planes
writer.Write((ushort)32); // bits per pixel
writer.Write(image.Length); // size of image data
writer.Write(offset); // offset of image data
offset += image.Length;
}
其中有一行 writer.Write((ushort)32); // bits per pixel 这个代表了图片的颜色位深度
一般来说 ICO 支持的常见位深度有:
- 1 bit(黑白)
- 4 bit(16 色)
- 8 bit(256 色)
- 24 bit(真彩色)
- 32 bit(真彩色 + Alpha)
我们的代码这里直接使用了 32 位色彩
而我发现那些转换出来无法解析的图片,都是 24 位色彩的
那么到这里就破案了
就是位深度不匹配的问题
解决方法
那么如何解决呢?
一开始我尝试在 clone 不同大小的图片时加上透明背景色
using var clone = original.Clone(ctx => {
ctx.Resize(size, size);
ctx.BackgroundColor(Color.Transparent); // 保证有 alpha 通道
});
然后很遗憾的是,没有效果
感觉可能是原始图片没有 alpha,ImageSharp 仍然用 RGB 来保存 PNG,导致保存出的 PNG 是 24-bit
而 ICO 写的 header 是 32-bit,导致无法解析
最终的解决方法是
foreach (var size in sizes) {
using var clone = original.Clone(ctx => ctx.Resize(size, size));
using var ms = new MemoryStream();
// 强制图像为 Rgba32 格式,确保是 32-bit
var rgbaImage = clone.CloneAs<Rgba32>();
rgbaImage.SaveAsPng(ms);
images.Add(ms.ToArray());
}
使用 clone.CloneAs<Rgba32>(); 强制转换图片位 32 位色彩
之后我测试了大部分图片都没问题了~
小结
我以为只要熟悉了ico文件格式的规范就足够了
没想到这只是个开始
图像处理领域还是有很多坑的
难怪大部分工具都选择使用 Magick 这类老牌成熟的图像处理库来实现这个功能
手写的话一不小心就踩坑了~ 还要花大量时间去排查问题
_EOF_
花了不少时间,修复了一个SharpIco生成图标的bug
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
C#学习(三)深入理解委托、匿名方法和-Lambda-表达式
前言 lambda的概念建立在委托的基础上,委托,实现了类型安全的回调方法。在.NET 中回调无处不在,所以委托也无处不在,事件模型建立在委托机制上,Lambda 表达式本质上就是一种匿名委托。本文中将完成一次关于委托的旅行,全面阐述委托及其核心话题,逐一梳理委托、委托链、事件、匿名方法和 Lambda 表达式。 委托的定义 了解委托,从其定义开始,通常一个委托被声明为: public deleg
2021-12-18 · C# · 约 1 分钟
Spark.NET:一个试图把 Django / Rails 式开发体验带回 .NET 世界的全栈 Web 框架。
又一个把Django/Rails那套“约定优于配置”搬进.NET生态的尝试,看着有点意思。预置了认证、队列、定时任务这些破事,省得自己天天重复造轮子。但767个star连个正式release都没有,就敢打着“生产级”旗号?呵,C#全栈爱好者们可以先玩玩,但别指望它能替代ASP.NET Core的体量。
2026-04-29 · C# · 约 1 分钟
上位机开发之假装有设备,使用 C# 模拟串口设备
用C#模拟串口设备?这年头还学这种过时技术?不过行吧,至少比某些只会玩AI的卷王强点。搞搞清楚协议文档,装个破com0com驱动,写点死板代码应付下调试需求。至于什么物联网平台、工业控制?那是别人该干的事,我随便搞搞就够了。
2025-08-26 · C# · 约 1 分钟
C#学习(二)匿名方法和委托的多种使用方式
前言 上次简单介绍了委托的概念和使用,今天继续,并且引入匿名方法的概念,匿名方法在很多语言里面都有,比如Python、js、java8,尤其js,前端小伙伴可能深受js回调地狱之苦,这里面匿名方法功不可没。 js的匿名方法就像这样: 这还没用lambda,因为我想下篇再介绍lambda。 document.querySelector('.btn').onclick = function () {
2021-12-18 · C# · 约 1 分钟
讨论
共 0 条加载评论中…