前言

上个月我用 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 这类老牌成熟的图像处理库来实现这个功能

手写的话一不小心就踩坑了~ 还要花大量时间去排查问题

花了不少时间,修复了一个SharpIco生成图标的bug的更多相关文章

  1. 一个highcharts混合图Demo

    公司要我做一个highcharts的mockup,其实半个小时就能做完了,我做了将近两个小时,唉!不过还好,总算把东西学会了.勤能补拙! 把代码贴上来 布局很简单,一个div里套两个div,给好id, ...

  2. java中从1000万个随机数中查找出相同的10万个随机数花的最少时间

    偶然在群里看到有人问到大数据查询,自己也就想了小艾改如何解决,从从1000万个随机数中查找出相同的10万个随机数花的最少时间, 谈到效率,自然是hashmap莫属. import java.util. ...

  3. 所有城市list每次从页面花1段时间抽取后写入到数组,

    所有城市list每次从页面花1段时间抽取后写入到数组,

  4. 前段时间,接手一个项目使用的是原始的jdbc作为数据库的访问,发布到服务器上在运行了一段时间之后总是会出现无法访问的情况,登录到服务器,查看tomcat日志发现总是报如下的错误。    Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data source rejected est

    前段时间,接手一个项目使用的是原始的jdbc作为数据库的访问,发布到服务器上在运行了一段时间之后总是会出现无法访问的情况,登录到服务器,查看tomcat日志发现总是报如下的错误. Caused by: ...

  5. 业余时间正在开发一个REACT小视频站点

    业余时间正在开发一个REACT小视频站点,数据接口来源于吐槽弹幕网(C站),供C站用户移动端的观看.因现存在移动网络屏蔽C站的关系,最好使用非移动网络进行观看.否则会比较卡,影响用户体验. 站点测试入 ...

  6. 如何用Qt写一个同一时间只能运行一个实例的应用程序

    http://blog.sina.com.cn/s/blog_6343941a0100nk2x.html 可以达到的目的: 1.应用只启动一个实例,依赖于QtNetwork模块 2.启动时向另一个实例 ...

  7. tensorboard以时间命名每一个文件夹

    tensorboard 有一个良好的命名习惯以时间命名每一个文件夹,例如**20190523_081232** ''' from datetiome import datetime dir = os. ...

  8. 一个历时五天的 Bug

    一个程序员在没有成长成为架构师之前,几乎都要跟 Bug为伴,程序员有很多时间都是花在了查找各种 Bug上. 我印象深刻的一个Bug, 是一个服务器网络框架无锁队列的 Bug .那个 Bug 连续查找了 ...

  9. android一个下拉放大库bug的解决过程及思考

    android一个下拉放大库bug的解决过程及思考 起因 项目中要做一个下拉缩放图片的效果,搜索了下github上面,找到了两个方案. https://github.com/Frank-Zhu/Pul ...

  10. Delphi 提示在Delphi的IDE中,按Ctrl+Shift+G键可以为一个接口生成一个新的GUID。

    对于Object Pascal语言来说,最近一段时间最有意义的改进就是从Delphi3开始支持接口(interface),接口定义了能够与一个对象进行交互操作的一组过程和函数.对一个接口进行定义包含两 ...

随机推荐

  1. 3D Gaussian 三维视觉重建

    论文资料 论文 https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/3d_gaussian_splatting_low.pdf 资料网站 ...

  2. Oracle impdp 导入报错 ORA-39083 + ORA-00439

    Oracle 11G R2 impdp导入的时候 一直报错: ORA-39083: 对象类型 TABLE:"xxx"."xxx" 创建失败, 出现错误: ORA ...

  3. 使用Python可视化莫比乌斯带

    引言 莫比乌斯带,这个名字或许大家都听过,但你知道它是什么吗?它是一种非常神奇的几何物体,只有一个面和一个边,乍一看,似乎是个不可思议的存在.今天,我们就来用 Python 轻松地可视化莫比乌斯带,一 ...

  4. nohup启动jar包

    1. 后台启动jar包,并追加日志到日志文件run.log nohup java -jar wash-1.0-SNAPSHOT.jar >> run.log 2>&1 &am ...

  5. windows 环境下vs code配置go mod 包管理进行开发,终于解决go mod 模式下可以编译运行,但引入包"github.com/gin-gonic/gin"的飘红黄波浪警告

    最近在积极的转入go后端开发,学习gin的时候,能够编译运行,但是在improt github.com/gin-gonic/gin 波浪警告 当时忘记截图了,类似于这样的波浪警告 , 内容大概是&qu ...

  6. JVM 的组成

    JVM 的组成 JVM(Java Virtual Machine)是 Java 的核心组件,负责执行 Java 字节码程序.以下是 JVM 的主要组成部分: 1. 类加载子系统(Class Loade ...

  7. 有的时候,会遇到DataGrid里面嵌套DataGrid(重叠嵌套),然后里面的鼠标滚轮无法响应外面的滚动,为此记录下解决方案

    有的时候,会遇到DataGrid里面嵌套DataGrid(重叠嵌套),然后里面的鼠标滚轮无法响应外面的滚动,为此记录下解决方案 本实例是在DataGrid的详情行里再嵌入一个DataGrid,模拟重叠 ...

  8. 在AI大爆发的背景下,企业管理软件有什么冲击

    今天与同行开会提到在AI大爆发的背景下,未来企业管理软件究竟有什么冲击? 我和同事对此问题进行了探讨,一些拙见,与大家分享.先直接说观点:在未来的5到10年,制造业的管理软件市场将几乎消失.下面我来聊 ...

  9. CTF实验吧加了料的报错注入

    实验吧地址 http://ctf5.shiyanbar.com/web/baocuo/index.php F12审查元素发现源码中的提示是这样一整句的查询 基本确定此题为一个SQL注入 /# = un ...

  10. FastAPI与Tortoise-ORM开发的神奇之旅

    title: FastAPI与Tortoise-ORM开发的神奇之旅 date: 2025/05/05 00:15:48 updated: 2025/05/05 00:15:48 author: cm ...