WPF 的高质量 IconBitmapEncoder






4.72/5 (12投票s)
一个能生成高质量图标的 IconBitmapEncoder,用 VB.NET 和 C# 编写,WPF
更新 2 (2017/11/11)
与我之前所说的相反,您无需在图标中包含 4bpp 和 8bpp 帧。
看起来,对于现代图标(Windows Vista 及更高版本),您只需要包含 32bpp 帧。
我打开了网上和微软 Windows (10) 本身的许多图标,才得出了这个最终结论。
我很抱歉。嗯,人活着就要不断学习。
另外,我想说的是,我对代码进行了一些更改,并已将其移至 GITHUB。在那里,您将获得最新的代码、发布版本等。自 Codeplex 上次更新以来,我已进行了许多更改。
GitHub - HerbertLausmann/HL.IconPro: 一个用于处理 Windows 图标和光标的 .NET C# 库
更新
现在,经过几个月的开发,我完成了更全面的图标编辑工具的第一个发布版本,该工具具有更强大的 IconBitmapEnconder
和全新的 IconBitmapDecoder
。这个工具名为 Icon Pro。源代码和第一个发布版本在此链接处可用:
引言
Windows Presentation Foundation 确实是 Windows 应用程序开发的一场伟大革命,它使我们能够创建具有高端 UI 的应用程序。
然而,并非一切都完美。在编写 WPF 代码时,我意识到 API 中缺少一个图标文件的编码器(因为 WIC 没有图标编码器,而 WPF 的图像编码器(包括解码器)是对原生 WIC API 的封装),这就是 IconBitmapEncoder
。
对此不满意,我决定尝试创建一个图标编码器来弥补这一不足。经过几天的努力,我成功开发了一个能够生成高质量图标文件的 IconBitmapEncoder
。
我所说的“高质量”,是指该编码器支持 32 位图标,以及 PNG 格式的 256x256 尺寸。
背景
基本上,一个图标文件包含以下部分:
但这还不是全部!
一个图标文件必须包含同一图像的多个尺寸(例如:16x16、32x32……),并且每个图像应具有三种不同的像素格式:Indexed 4(16 色 [4 位])、Indexed 8(256 色 [8 位])和 RGB(A – Alpha 通道可选)(真彩色 [32 位])。请看图标的视觉表示。
图标文件中的所有图像必须是 Bitmap (BMP) 格式,不包含文件头(前 14 个字节),但 256x256 的图像应以 PNG 格式(RGBA 32 位)存储。
BMP 图像的高度必须加倍,因为它们没有 **AND 蒙版**。有关 **AND 蒙版** 的更多信息,请参阅 BMP 文件格式(维基百科)。
不要问我为什么,但为了正常工作,图标文件中的帧应该按从小到大的顺序排列。因此,我在 IconBitmapFramesCollection
类中实现了 SortAscending
方法。另一点我需要强调的是,图像必须是正方形的,并且尺寸至少为 16x16 像素,最多为 256x256 像素。
为了开发这个编码器,以下文章非常重要。阅读它们以获得更多背景信息:
Using the Code
下面是用我的 IconBitmapEncoder
类,从单个 256x256 PNG 图像创建高质量图标的示例代码,该代码也包含在下载的解决方案中(也有 C# 版本)。
Public Sub SaveIcon(Source As BitmapSource, _
Stream As IO.Stream, Quality As Integer)
Dim encoder As New IconBitmapEncoder
Dim bmp16 As BitmapSource = IconBitmapEncoder.GetResized(Source, 16)
Dim bmp24 As BitmapSource = IconBitmapEncoder.GetResized(Source, 24)
Dim bmp32 As BitmapSource = IconBitmapEncoder.GetResized(Source, 32)
Dim bmp48 As BitmapSource = IconBitmapEncoder.GetResized(Source, 48)
Dim bmp64 As BitmapSource = IconBitmapEncoder.GetResized(Source, 64)
Dim bmp72 As BitmapSource = IconBitmapEncoder.GetResized(Source, 72)
Dim bmp96 As BitmapSource = IconBitmapEncoder.GetResized(Source, 96)
Dim bmp128 As BitmapSource = IconBitmapEncoder.GetResized(Source, 128)
Dim bmp256 As BitmapSource = IconBitmapEncoder.GetResized(Source, 256)
If Quality >= 90 Then
encoder.Frames.Add(BitmapFrame.Create(bmp256))
End If
If Quality >= 80 Then
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get24plus8BitImage(bmp128)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get4BitImage(bmp128)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get8BitImage(bmp128)))
End If
If Quality >= 70 Then
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get24plus8BitImage(bmp96)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get4BitImage(bmp96)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get8BitImage(bmp96)))
End If
If Quality >= 60 Then
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get24plus8BitImage(bmp72)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get4BitImage(bmp72)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get8BitImage(bmp72)))
End If
If Quality >= 50 Then
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get24plus8BitImage(bmp64)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get4BitImage(bmp64)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get8BitImage(bmp64)))
End If
If Quality >= 40 Then
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get24plus8BitImage(bmp48)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get4BitImage(bmp48)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get8BitImage(bmp48)))
End If
If Quality >= 30 Then
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get24plus8BitImage(bmp32)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get4BitImage(bmp32)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get8BitImage(bmp32)))
End If
If Quality >= 20 Then
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get24plus8BitImage(bmp24)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get4BitImage(bmp24)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get8BitImage(bmp24)))
End If
If Quality >= 0 Then
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get24plus8BitImage(bmp16)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get4BitImage(bmp16)))
encoder.Frames.Add(BitmapFrame.Create_
(IconBitmapEncoder.Get8BitImage(bmp16)))
End If
encoder.Save(Stream)
Stream.Close()
End Sub
Source
是 256x256 的 32 位 PNG 图像;Stream
是 **输出** 流;Quality
是 0-100 范围的值。
因此,我们需要为每个图像尺寸和每个图像像素格式,在编码器的帧集合中添加一个 BitmapFrame
。
关注点
BitmapImage
从 32 位到 8 位和 4 位的像素转换BitmapImage
缩放- 使用
BinaryWriter
来处理**小端字节序**的写入 - 列表项排序,升序和降序
注释
- 该编码器满足了在 Windows XP 及更高版本上创建图标的所有要求。但是我只有机会在 Windows 7 SP1 上进行测试。因此,如果您在使用其他 Windows 版本生成的图标时遇到任何问题,请告诉我,我会尝试修复。
- 演示和 API 在 Visual Studio 2012 SP2、.NET Framework 3.5 SP1 中编写。
历史
- 版本 1.0.0.0 – 2013/11/25
- 版本 1.1.0.0 – 2013/12/21
- 上传了包含源代码(和一个功能齐全的演示)的解决方案
- 现在该代码同时提供 VB.NET 和 C# 版本
- 版本 1.1.0.1 – 2013/12/22
- 添加了一些额外信息
- 从文章页面移除了
IconBitmapEncoder
类的源代码