存放在github.com上的源代码链接 Go语言处理Windows系统的图标ICO文件(中) Go语言处理Windows系统的图标ICO文件(下)
我们在编写一个程序后,如果是windows系统中的程序,我们希望它有一个图标,这个时候,我们可以使用一些软件来完成图标,嗯…但是好的Icon制作软件要收费,例如Axialis IconWorkshop,可以制作Windows以及MacOS等系统的图标。网络上基于Web页面的在线转转换工具也是有的,但是我看了下,貌似只是单Image(jpg or png or bmp)转单icon格式,而一个Windows应用程序实际上用到的ico文件,应该是多icon图标的,即一个ico文件内,包含了多个icon图标图像数据。例如下图: 我们可以通过PE Explorer 看到一个Windows的应用程序中,包含图标有多个不同分辨率及不同颜色数量的icon,从Windows Vista开始,windows可以支持的图标分辨率可以达到256x256@32bit的图标资源,既然我们的口号是I am a Gopher!,那么我们无论是从学习角度,还是从Go Code角度,我们完全可以自己实现一个我们自己的Icon工具。
单个icon文件的意思是:我们要分析的ico文件只包含了一个图标,实际上ico文件是一种目录结构的文件,即文件内的数据格式结构是可以存放多文件(多个ico图标),为了将理解流程简化,所以我们先观察只在文件结构中存放了一张图标的ico文件。
我这里首先来分析一个16x16pixel@24bit 的ico文件: 资源管理器中显示的2KB大小是一个大概的值,我们鼠标右键菜单,点击属性查看实际大小: 我们看到是1.12KB,实际大小为1150字节。(1150 / 1024 = 1.123046875 round 1.12KB)
我们再查看文件的HEX(Binary二进制文件,通常我们用16进制查看器来阅读) 我框选的部分有22个字节,这22个字节就是我们这个ico文件的header部分,我们通常称之为文件头(用于描述文件结构的概念) 然后我们通过下面这张图来充分理解,每个字节的含义: 首先根据Windows系统下ico文件格式的标准,存储的数据采用的是little-endian即“小端序”。 什么是小端序,我们可以理解为:数据的低位在前,高位在后,即个十百千-> 1024 -> 4201 在16进制查看器中,例如:
var data uint32 = 255那么小端序存储在内存或磁盘的方式为:
FF 00 00 00 |- 255的16进制 var data uint32 = 256那么一个字节能够表示的最大正整数为255,这里256,超出了1位:
00 01 00 00 |----|- 进位了,256的16进制为100关于小端序的数据转换,这里先说一下代码:
var data = []byte{0x00, 0x01, 0x00, 0x00} Int32Data := binary.LittleEndian.Uint32(data[0:]) t.Logf("%v", Int32Data)输出结果:
=== RUN Test_LittleEndian --- PASS: Test_LittleEndian (0.00s) main_test.go:144: 256 // 这里是结果 PASS ok ICOFormat 0.187s现在我们将概念的理解转换为代码:
type winIcon struct { reservedA uint16 // 保留字段,始终为 '0x0000' fileType uint16 // 图像类型:'0x0100' 为 ico,'0x0200' 为 cur imageCount uint16 // 图像数量:至少为 '0x0100' 即 1个图标 width uint8 // 图像宽度 height uint8 // 图像高度 palette uint8 // 调色板颜色数,不使用调色版为 '0x00' reservedB uint8 // 保留字段,始终为 '0x00' colorPlanes uint16 // 在ico中,指定颜色平面,'0x0000' 或则 '0x0100' bitsPerPixel uint16 // 在ico中,指定每像素的位数,如:'0x2000' 32bit imageDataSize uint16 // 图像数据的大小,单位字节 imageOffset uint32 // 图像数据的偏移量 }等一等,这似乎只是单图标文件的结构描述,我们前面说了,ico文件内部应该是以目录结构描述的。 而且图像的数据可以是bmp(Bitmap位图),也可以是png(Portable Network Graphics便携式网络图形)格式。
所以我们再来看一个多icon图标文件的ico文件结构: 上图中橘黄色的框中为BMP或PNG的数据偏移量,0x960100 -> 小端转换后为:406字节 那么后面的数据段应该是啥样的呢?我们看看从头偏移406字节的内容: 从第一个字节开始偏移406字节的内容选中后,即便我不用不同颜色的框标明一下,都会觉得感觉每一行的结构都好特么的像。再用不同颜色的框一画,聪明的同学肯定感觉一切尽在掌握。哈哈哈。刚好25个… 换句话说,那个BMP或PNG的偏移量表示的就是偏移过后,就是图像数据。而偏移量前的就是我们的ico文件中的头结构。
那么我们可以这样理解ico文件的头结构: 1-6字节:ico文件头,表明ico文件中包含多少个icon图标 7-22字节:ico文件结构头,主要说明图像数据的偏移量 而19至22字节的偏移量则是我们整个单或多ico图标ico文件的头结构。
那么我们来将理解转换代码:
type WinIcon struct { fileHeader *winIconFileHeader // 文件头 icos []winIconStruct // icon 头结构 data []byte // 所有ico文件数据 } type winIconHeader struct { ReservedA uint16 // 保留字段,始终为 '0x0000' FileType uint16 // 图像类型:'0x0100' 为 ico,'0x0200' 为 cur ImageCount uint16 // 图像数量:至少为 '0x0100' 即 1个图标 } type winIconStruct struct { Width uint8 // 图像宽度 Height uint8 // 图像高度 Palette uint8 // 调色板颜色数,不使用调色版为 '0x00' ReservedB uint8 // 保留字段,始终为 '0x00' ColorPlanes uint16 // 在ico中,指定颜色平面,'0x0000' 或则 '0x0100' BitsPerPixel uint16 // 在ico中,指定每像素的位数,如:'0x2000' 32bit ImageDataSize uint16 // 图像数据的大小,单位字节 ImageOffset uint32 // 图像数据的偏移量 }下一节文章会讲到如何读取ico文件