技术笔记PowerShell PNG 转 ICO
狂犬主子使用 PowerShell 将 PNG 文件转换为 ICO 文件有多种方法,但是都不太方便,且不易理解。鉴于我们现在的操作系统版本大多已经在 Windows Vista 以上,无需考虑以前的 BMP 多分辨率多位数模式,可以直接使用 PNG 加上头信息构造出 ICO 文件。这样就可以非常方便的将 PNG 转换为 ICO,同时可以无损保留 PNG。
结构
这里我们不引入任何依赖,手动合成,因此首先需要了解一下 ICO 文件的结构。
参考:
简言之,ICO 格式由头文件和图片数据组成,其中头文件的十六进制数据结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 00 00 01 00 01 00
00 00 00 00 01 00 20 00 6F 48 00 00 16 00 00 00
|
ICO 格式的字节序为 Little-Endian(如上方的 6F 48 00 00
为从右往左读作 48 6F
),Windows 下默认表现为 LE,通常编写 PowerShell 时无需在意这个问题,自动生成出来就是 LE 的。
由于我们只有一张图,且图片为 PNG 格式,因此我们只需要修改 ICONDIRENTRY
中的图像文件大小即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| function Convert-PngToIco { param ( [string]$PngPath, [string]$IcoPath ) $png = [System.IO.File]::ReadAllBytes($pngPath)
$ico = [System.IO.MemoryStream]::new() $bin = [System.IO.BinaryWriter]::new($ico)
$bin.Write([uint16]0) $bin.Write([uint16]1) $bin.Write([uint16]1)
$bin.Write([sbyte]0) $bin.Write([sbyte]0) $bin.Write([sbyte]0) $bin.Write([sbyte]0) $bin.Write([uint16]1) $bin.Write([uint16]32) $bin.Write([uint32]$png.Length) $bin.Write([uint32]22)
$bin.Write($png)
[System.IO.File]::WriteAllBytes($icoPath, $ico.ToArray())
$bin.Dispose() $ico.Dispose() }
$pngPath = ".\image.png" $icoPath = ".\image.ico" Convert-PngToIco -PngPath $pngPath -IcoPath $icoPath
|
技巧
PNG 图片可以先使用 TinyPNG 压缩,这样制作出来的图标体积更小。