65.9K
CodeProject 正在变化。 阅读更多。
Home

访问单个图标文件中的多个图标

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (51投票s)

2004 年 2 月 24 日

3分钟阅读

viewsIcon

168055

downloadIcon

6283

一个帮助您访问 ICO 文件中图像的类(VB & C#)。

引言



GDI+ 在很多方面都很出色,但仍缺少一些部分。 其中一个缺失的部分是查看单个 ICO 文件中包含的图标的不同版本的能力。 在本文中,我将向您展示如何访问和利用这些不同的版本。

因为每个人都喜欢这个主题,所以我也用 C# 编写了代码。 我想我应该掸掉我的 C# 技能,并重新编码该应用程序。 这个新的 C# 版本与 VB 版本几乎相同,除了它在图标文件中公开了更多细节; 有关详细信息,请参见源代码。

图标文件

通常,图标文件包含同一图像的多个版本。 通常,这些文件将包含同一图像的小版本和大版本,这样您就不必调整图像大小并冒着丢失宝贵分辨率的风险。

"new Icon" 的局限性

Visual Studio 中的新图标构造函数对多个图像版本的支持不佳。 诚然,您可以指定您请求的图标的高度和宽度,但您无法知道要请求什么(如果您不知道文件中的内容)。 为了解决这个限制,我们必须直接查看 ico 文件并提取我们要使用的位和字节。 我创建了一个图表,它将说明图标文件的内部布局。

由于空间原因,我只在上面的示例中放置了两个图标,但您可以放置任意多个。

图标标头
图标标头保存着访问整个文件的关键。 这个六字节的块告诉您正在访问的文件中有多少个图标,以及您正在访问的文件的类型(0 表示位图,1 表示图标)。

图标条目
在读取图标标头后,我们将知道此图标文件中有多少个图标。 然后,我们可以安全地读取多个图标条目块。 图标条目块不保存图像信息,它们保存图像字节的偏移量和长度。

打开图标文件

最简单的方法是创建一个 FileStream 并将文件转储到 Byte 数组。 然后,您可以将 Byte 数组加载到 Stream 对象中。 以下代码将演示。

    Private Function readIcoFile(ByVal filename As String) As MemoryStream
        ' Open the file
        '
        Dim icoBinaryFile As New FileStream(filename, FileMode.Open, 
                                            FileAccess.Read)


        ' Create the byte array and read it in
        '
        Dim byteArray(icoBinaryFile.Length) As Byte
        icoBinaryFile.Read(byteArray, 0, icoBinaryFile.Length)
        icoBinaryFile.Close()


        ' Load the stream with the bytearray
        '
        icoStream = New MemoryStream(byteArray)
        icoStream.Seek(0, SeekOrigin.Begin)


        ' Debug, these values should be the same!
        '
        Console.WriteLine("Number of bytes: " & byteArray.Length)
        Console.WriteLine("Length of Stream: " & icoStream.Length)
    End Function

读取数据

我发现最好的方法是创建类。 第一个类是图标标头类,它仅包含它读取的元素的定义以及一个将从流中读取信息的新过程。

    Private Class iconHeader
        Public Reserved As Short      ' Always 0
        Public Type As Short          ' 0=Bitmap, 1=Icon
        Public Count As Short         ' Number of icons


        '------------------------------------------
        '     Sub: New
        ' Purpose: Read the six byte header from
        '          the raw ICO file
        '    Note: Short or Int16 are 2 bytes each
        '
        Public Sub New()
            Dim icoFile As New BinaryReader(icoStream)

            Reserved = icoFile.ReadInt16
            Type = icoFile.ReadInt16
            Count = icoFile.ReadInt16
        End Sub
    End Class

下一步几乎相同,但使用的是图标条目数据。

    Private Class iconEntry
        Public Width As Byte          ' Width, in pixels, of the image
        Public Height As Byte         ' Height, in pixels, of the image
        Public ColorCount As Byte     ' Number of colors in image(0 if >=8bpp)
        Public Reserved As Byte       ' Reserved ( must be 0)
        Public Planes As Short        ' Color Planes
        Public BitCount As Short      ' Bits per pixel
        Public BytesInRes As Integer  ' How many bytes in this resource?
        Public ImageOffset As Integer ' Where in the file is this image?

        '------------------------------------------
        '     Sub: New
        ' Purpose: Read the sixteen byte header from
        '          the raw ICO file
        '    Note: Byte is 1 byte
        '          Short or Int16 are 2 bytes
        '          Integer or Int32 are 4 bytes
        '
        Public Sub New(ByVal Index As Integer)
            Dim icoFile As New BinaryReader(icoStream)

            Width = icoFile.ReadByte
            Height = icoFile.ReadByte
            ColorCount = icoFile.ReadByte
            Reserved = icoFile.ReadByte
            Planes = icoFile.ReadInt16
            BitCount = icoFile.ReadInt16
            BytesInRes = icoFile.ReadInt32
            ImageOffset = icoFile.ReadInt32
        End Sub
    End Class

现在,您拥有图像的所有偏移量、大小、颜色信息和长度。

构建图像

好的,我们在这里所做的是使用从标头和条目中提取的信息实际构建一个图标。 我们通过创建另一个流并使用 BinaryWriter 填充数据来做到这一点。 这是一个例子

    Private Function buildIcon(ByVal index As Integer) As Icon
        Dim thisIcon As iconEntry = icons(index)

        ' Allocate the space for the icons byteArray
        Dim icoByteArray(thisIcon.BytesInRes) As Byte

        ' Create the stream
        Dim newIcon As New MemoryStream
        Dim writer As New BinaryWriter(newIcon)

        ' Only one icon in this file
        Dim newCount As Short = 1


        ' Six Bytes + Sixteen Bytes is the new offset
        Dim newOffset As Integer = 22

        Console.WriteLine("Icon Index: " & index & ", 
                          Offset: " & thisIcon.ImageOffset)

        ' Write the file
        With writer
            .Write(icoHeader.Reserved)
            .Write(icoHeader.Type)
            .Write(newCount)
            Console.WriteLine("Header written: " & newIcon.Position)

            .Write(thisIcon.Width)
            .Write(thisIcon.Height)
            .Write(thisIcon.ColorCount)
            .Write(thisIcon.Reserved)
            .Write(thisIcon.Planes)
            .Write(thisIcon.BitCount)
            .Write(thisIcon.BytesInRes)
            .Write(newOffset)
            Console.WriteLine("Image Header written: " & newIcon.Position)

            ' Read the icon from the stream
            icoStream.Seek(thisIcon.ImageOffset, SeekOrigin.Begin)
            icoStream.Read(icoByteArray, 0, thisIcon.BytesInRes)

            ' Write it out
            .Write(icoByteArray)
            .Flush()
            Console.WriteLine("Image written: " & newIcon.Position)
        End With

        ' Move to the start
        newIcon.Seek(0, SeekOrigin.Begin)

        Dim thisImage As Icon = New Icon(newIcon, thisIcon.Width, 
                                         thisIcon.Height)
        writer.Close()

        Return thisImage
    End Function

请注意,我们只是写回我们读取的数据的副本(有一些更改)。 我们所做的更改是

' We only have one icon in this file 
Dim newCount As Short = 1 
' We are moving the image offset so it falls right after the headers 
Dim newOffset As Integer = 22 

包装所有这些的类

我创建了一个名为 MultiIcon 的类,它包含以下属性和方法

  • 计数
    ico 文件中图标的数量
  • 大小
    一个大小数组,包含 ICO 中图标的大小
  • 图像
    返回特定图标的图像
  • findIcon
    返回一个图像,但您可以指定是要最大的图像还是最小的图像

示例

Public thisIcon As multiIcon(filename)

这将创建一个类的新实例。

PictureBox1.Image = thisIcon.image(ComboBox1.SelectedIndex).ToBitmap

这会将图标加载到图片框中。

PictureBox1.Image = thisIcon.findIcon(
                                    multiIcon.iconCriteria.Largest).ToBitmap()
PictureBox1.Image = thisIcon.findIcon(
                                   multiIcon.iconCriteria.Smallest).ToBitmap()

这会将特定版本的图标加载到图片框中。

        Dim size As Size
        For Each size In thisIcon.sizes
            ComboBox1.Items.Add(size.ToString)
        Next

这将列出当前可用的所有图标大小。

© . All rights reserved.