-
2021-07-26 02:06:55
ICO
(Windows的图标文件格式)
语音
编辑
锁定
讨论
上传视频
ICO是Windows的图标文件格式,图标文件可以存储单个图案、多尺寸、多色板的图标文件。一个图标实际上是多张不同格式的图片的集合体,并且还包含了一定的透明区域。
中文名
图标外文名
ICOn
外国语缩写
ICO
ICO格式详解
编辑
语音
它是Windows的图标文件格式的一种,可以存储单个图案、多尺寸、多色板的图标文件。
图标是具有明确指代含义的计算机图形。其中桌面图标是软件标识,界面中的图标是功能标识。
图标有一套标准的大小和属性格式,且通常是小尺寸的。每个图标都含有多张相同显示内容的图片,每一张图片具有不同的尺寸和发色数。一个图标就是一套相似的图片,每一张图片有不同的格式,从这一点上说图标是三维的。图标还有另一个特性:它含有透明区域,在透明区域内可以透出图标下的桌面背景。在结构上图标其实和麦当劳的巨无霸汉堡差不多。
一个图标实际上是多张不同格式的图片的集合体,并且还包含了一定的透明区域。因为计算机操作系统和显示设备的多样性,导致了图标的大小需要有多种格式。
操作系统在显示一个图标时,会按照一定的标准选择图标中最适合当前显示环境和状态的图像。如果你用的是Windows98操作系统,显示环境是800x600分辨率,32位色深,你在桌面上看到的每个图标的图像格式就是256色32x32像素大小。如果在相同的显示环境下,Windows XP操作系统中,这些图标的图像格式就是:真彩色(32位色深)、32x32像素大小。
下面就是Windows各个操作系统中的标准图标格式:(单位:大小—颜色)
Windows 98 SE/ME/2000
48 x 48 - 256 32 x 32 - 256 16 x 16 - 256
48 x 48 - 16 32 x 32 - 16 16 x 16 - 16
Windows XP
48 x 48 - 32bit 32 x 32 - 32bit 24 x 24 - 32bit * 16 x 16 - 32bit
(32位真彩色支持多通道透明。)
48 x 48 - 256 32 x 32 - 256 24 x 24 - 256 * 16 x 16 - 256
48 x 48 - 16 32 x 32 - 16 24 x 24 - 16 * 16 x 16 - 16
* 这种格式在XP图标中并不是必须的。
在Vista系统下最大可以支持256 x 256
同时,非标准的ico格式文件也支持不规则尺寸的存储。
注意:Windows98/2000对24 x 24格式的图标不兼容。你可以在相关应用软件中打开含有这种图像格式的图标,但操作系统却认为是无效的。你必须确保你所设计的图标中至少含有以上所列的图像格式来获得良好的显示效果。如果操作系统在图标中找不到特定的图象格式,它总是采用最接近的图象格式来显示,比如把大小为48 x 48的图标缩小为24 x 24像素大小。当然,效果就差些了。
图标的文件格式
在Windows操作系统中,单个图标的文件名后缀是.ICO。这种格式的图标可以在Windows操作系统中直接浏览;后缀名是.ICL的代表图标库,它是多个图标的集合,一般操作系统不直接支持这种格式的文件,需要借助第三方软件才能浏览。
在图形用户界面中,系统中的所有资源分别由三种类型的图标所表示:应用程序图标(指向具体完成某一功能的可执行程序)、文件夹图标(指向用于存放其他应用程序、文档或子文件夹的“容器”)和文档图标(指向由某个应用程序所创建的信息)。
在Windows系统中,左下角带有弧形箭头的图标代表快捷方式。快捷方式是一种特殊的文件类型,它提供了对系统中一些资源对象的快速简便访问,快捷方式图标是原对象的“替身”图标。
快捷方式图标十分有用,它是定制桌面,进行快速访问经常使用的应用程序和文档的最主要的方法。
ICO应用途径
编辑
语音
ico可以作为浏览器首段图标显示,还可以在收藏夹内收藏内容的前段显示小图标
ico还可以应用于windows软件
ico也可以应用于个性化磁盘驱动器图标
词条图册
更多图册
更多相关内容 -
win10超多图标.ico文件
2020-10-15 17:05:37win10系统的几百个自定义.ico文件,包括网上找的和几个自己做的,可以替换系统自定义图标,基本上都是纯白色的。直接用.ico文件替换原本系统的图标便可以更改,方便好用颜值高 -
windows安装包 默认图标 打包 软件 图标 安装包图标.ico格式文件
2019-01-06 13:41:19windows安装包 默认图标 打包 软件 图标 安装包图标.ico格式文件 -
Windows+ICO图标大全
2013-11-26 14:36:59Windows+ICO图标大全,很全的Windows ICO图标,要做开发和写软件的拿去用吧 -
Go语言处理Windows系统的图标ICO文件(中)
2019-05-28 07:35:56Go语言处理Windows系统的图标ICO文件(上) Go语言处理Windows系统的图标ICO文件(下)… 编写中 提取ICO文件中的所有图标图像 在上一篇文章中,我们了解了ico文件的结构,在这一篇文章中,我们首先来看看如何将多icon...ICO文件格式
存放在github.com上的源代码链接
Go语言处理Windows系统的图标ICO文件(上)
Go语言处理Windows系统的图标ICO文件(下)
提取ICO文件中的所有图标图像
在上一篇文章中,我们了解了ico文件的结构,在这一篇文章中,我们首先来看看如何将多icon资源的ico文件中的图标图像提取出来。
从我选中的部分,我们已经知道了,该ico文件有25个ico图像(png和bmp),从开头的22个字节后,每16字节为一个ico文件的header,直到第一个header中的偏移量为止(19-22字节所描述的偏移量)。那么将我们的理解转换为代码如下:
1、通过22个字节的header
中,最后4个字节,我们获取icon图标头结构偏移量0x960100
,即:‘406bytes offset’。
2、我们获取的0-406的内容就是我们第一个ico文件中所有icon图标的structure header
。
3、根据icon数量来循环处理22个字节后的其他icon文件的header
声明与定义一个读取ico文件的函数:func LoadIconFile(rd io.Reader) (icon *WinIcon, err error)
如果我们完成了读取,则返回一个
*WinIcon
对象的指针,如果失败则返回错误对象。
那么我们的错误应该有哪些呢?
1、非法的文件(非法的ico文件)
a、文件的长度连22个字节都没(说明根本就不是一个ico文件,只是扩展名是.ico)
b、文件的长度的确超过了22字节,但是这22字节的内容却和ico的header结构不匹配
c、可能传入的读取像不是一个*os.File
,亦或则不是一个文件呢…可能需要做些基本判断
d、在读取数据中,会不会出现越界的情况呢?
先暂时想到这里。
这里我定义了一些错误信息对象:// 定义变量 var ( // 错误信息 ErrIcoInvalid = errors.New("ico: Invalid icon file") // 无效的ico文件 ErrIcoReaders = errors.New("ico: Reader type is not os.File pointer") // LoadIconFile的io.Reader参数不是文件指针 ErrIcoFileType = errors.New("ico: Reader is directory, not file") // io.Reader的文件指针是目录,不是文件 ErrIconsIndex = errors.New("ico: Slice out of bounds") // 读取ico文件时,可能出现的切片越界错误 )
定义两个常量:
// 定义常量 const ( fileHeaderSize = 6 // 文件头的大小 headerSize = 16 // icon图标的头结构大小 )
所以我们还需要一个函数来判断是否是ico文件:
func getIconHeader(b []byte) (wih *winIconHeader, err error)
在这个函数中,我们判断文件合法性,如果非法,则返回
err
,如果合法,则提取icon的结构。
如果是单icon图标的ico文件,那么结构中的文件数量则为1,如果是多icon图标的ico文件,则文件数量为n,我们可以根据这个n来循环。或则为1则直接读取icon图标数据。文件头只有6个字节,只是做一个简单判断,即3个部分,保留字段、是否是ico的marker,icon图标的数量
getIconHeader
的代码实现:if len(b) != fileHeaderSize { return nil, ErrIcoInvalid } reserved := binary.LittleEndian.Uint16(b[0:2]) filetype := binary.LittleEndian.Uint16(b[2:4]) imagecount := binary.LittleEndian.Uint16(b[4:6]) if reserved != 0 || filetype != 1 || imagecount == 0 { return nil, ErrIcoInvalid } header := &winIconFileHeader{ ReservedA: reserved, FileType: filetype, ImageCount: imagecount, } return header, nil
我们获取到了文件的头结构后,也就获得了icon图标的数量,根据数量我们可以控制循环的次数。
这里是部分LoadIconFile
函数的代码:// 创建一个 winIconStruct 数组切片 icos := make([]winIconStruct, int(icoHeader.ImageCount)) // 根据文件头中表示的icon图标文件的数量进行循环 structOffset := fileHeaderSize // 这里的icoHeader就是文件头,ImageCount就是我们获取的ico图标数量 for i := 0; i < int(icoHeader.ImageCount); i++ { // data 是怎个文件的[]byte数据 wis := getIconStruct(data, structOffset, headerSize) structOffset += headerSize icos[i] = *wis } // 这个循环的意义在于,我们将ico图标的头结构全部拿到,拿到这个就可以 // 根据ico图标的头结构信息来获取偏移量,从而获取图标图像的数据 // 创建 WinIcon 对象 ico = &WinIcon{ fileHeader: icoHeader, icos: icos, data: data, } return ico, nil
获取所有数据:
func getFileAll(rd *bufio.Reader, size int64) (fb []byte, err error)
实现:
data := make([]byte, size) // 丢弃1-6字节内容(文件头) // if _, err := rd.Discard(fileHeaderSize); err != nil { // return nil, err // } // size = size - fileHeaderSize for i := int64(0); i < size; i++ { b, err := rd.ReadByte() if err != nil { return nil, err } data[i] = b } return data, nil
当我们有了文件的所有数据,以及icon图标的所有头结构后,我们就可以获取图标数据了:
func (wi *WinIcon) GetImageData(index int) (d []byte, err error) func (wi *WinIcon) getImageData(data []byte, offset, datasize int) []byte
getImageData
是包内的private:
成员函数,GetImageData
是包外public:
成员函数从参数上看
GetImageData
仅需要传递索引即可,getImageData
需要传递的内容是所有数据data,以及ico文件在所有数据中的偏移量,以及数据大小(length长度),最后返回ico的图像数据:[]byte类型。getImageData
代码实现:var d = make([]byte, datasize) // 不建议 data[offset:length] 采用复制数据会相对安全些 for i, j := offset, 0; i < datasize+offset; i++ { d[j] = data[i] j++ } return d
GetImageData
代码实现:if index >= wi.getIconsHeaderCount() || index < 0 { return nil, ErrIconsIndex } wis := wi.icos[index] db := wi.getImageData(wi.data, int(wis.ImageOffset), int(wis.ImageDataSize)) return db, nil
wi.getIconsHeaderCount
获取我们已经的得到的icon图标头结构的数量。基本上而言,以上代码就是读取数据的主要内容。完整代码请见页面中的github.com的链接。
好了,当我们实现了ico文件的读取/解析功能后,我们就可以实现提取数据的功能了。
函数签名:func (wis winIconStruct) generateFileNameFormat(prefix string, width, height, bit int) string func (wis winIconStruct) IconToFile(path string, data []byte) error func (wi *WinIcon) ExtractIconToFile(filePrefix, filePath string) error func (wi *WinIcon) IconToFile(filePath string, index int) error
这里顺便提一下,关于
WinIcon
、winIconStruct
,在之前的结构体定义的时候,WinIcon
是首字母大写的,意思就是它是包级的,包外可以访问,而winIconStruct
是包内的,包外是隐藏,我们包内实现,包内使用。
所以generateFileNameFormat
、IconToFile
并不是提供给调用ico包的角色使用的。这里就贴一下
(wi *WinIcon) ExtractIconToFile
,我们可以使用该函数将ico文件中所有的icon图标全部提取出来,并保存到磁盘,而(wis winIconStruct) generateFileNameFormat
就是自动生成文件名前缀的函数。生成后效果如下:
favicon_all.ico 这个文件就是多icon图标结构的ico文件,使用了30天试用版的某IconWorkshop软件生成(我不是美术,所以花钱买,貌似辱没了Gopher呀,而且买了,我就没必要写这个教程了?,我的win10三套都是正版,office正版,Xmind正版…,基本上我很少用盗版,嗯…不好意思,跑题了??),2-4个ico文件,分辨率为0x0,是因为图标的width和height数据段使用1byte,前面说了,1byte的最大正整数为255,而256这个数字则会被储存为0x00,高位丢失。所以在ico文件中如果icon头结构中的w,h字段值为0,那么如果没有出错的话,分辨率则为256x256pixel。func (wi *WinIcon) ExtractIconToFile(filePrefix, filePath string) error
ExtractIconToFile
的实现:for _, v := range wi.icos { fileName := v.generateFileNameFormat(filePrefix, int(v.Width), int(v.Height), int(v.BitsPerPixel)) fp := filepath.Join(filePath, fileName) d := wi.getImageData(wi.data, int(v.ImageOffset), int(v.ImageDataSize)) if err := v.IconToFile(fp, d); err != nil { return err } } return nil
这里我贴出package的测试ico_test.go
package ico import ( "log" "os" "path/filepath" "testing" ) func TestLoadIconFile(t *testing.T) { var fs *os.File defer func() { if fs != nil { fs.Close() } if err := recover(); err != nil { log.SetFlags(log.Ldate | log.Ltime | log.Llongfile) log.Printf("LoadIconFile() = Error:%v\r\n", err) os.Exit(1) } }() path := "../testico/" file := "favicon_all.ico" filePath := filepath.Join(path, file) fs, err := os.Open(filePath) if err != nil { panic(err) } wi, err := LoadIconFile(fs) if err != nil { panic(err) } // if err := wi.ExtractIconToFile("test", "../testdata/", 0); err != nil { // panic(err) // } // for _, v := range wi.icos { // fmt.Printf("image width:%v, height:%v\r\n", v.Width, v.Height) // fmt.Printf("image offset:%v, datesize:%v\r\n", v.ImageOffset, v.ImageDataSize) // d := wi.getImageData(wi.data, int(v.ImageOffset), int(v.ImageDataSize)) // fn := fmt.Sprintf("../testico/%vx%v@%v.ico", v.Width, v.Height, v.BitsPerPixel) // err := ioutil.WriteFile(fn, d, 0) // if err != nil { // fmt.Println(err) // } // } if err := wi.ExtractIconToFile("test", "../testico/"); err != nil { panic(err) } }
我们使用go语言封装的时候,没必要写main package和main函数来跑代码,golang给我们自带了UT,善加利用,即可提高效率和质量。
提取后的png文件头部:
提取后的dib(bitmap)文件头部:
我们需要实现对png或bmp的数据检测函数。
以下是具有像素格式RGB24的2×2像素,24位位图(Windows DIB标题BITMAPINFOHEADER)的示例:
以下是具有像素格式ARGB32的alpha通道(Windows DIB标题BITMAPV4HEADER)中具有不透明度值的4×2像素,32位位图的示例:
PNG文件头结构:
关于提取的*.ico
文件数据,实际上还需要进一步处理,bitmap需要写一个bmp头,因为ico文件中的bitmap icon图标是没有BMP header
,只有DIB header
的。而png没有这个问题,我们需要识别一下后,bmp的保存为*.bmp
,png的保存为*.png
。有了提取,我们还需要实现根据命令行参数将多个bmp或png文件打包为ico文件结构并输出ico文件的功能。而这些内容,我们将在下一集教程中讲解。下面是读取ico文件,实现了全部提取及单独提取及单独提取并保存为单icon图标的ico文件的功能:
打包功能还没有写,下一章教程实现。/* _____ __ __ _ __ ╱ ____| | ╲/ | | |/ / | | __ ___ | ╲ / | __ _ _ __| ' / | | |_ |/ _ ╲| |╲ /| |/ _` | '__| < | |__| | __/| | | ( _| | | | . ╲ ╲_____|╲___ |_| |_|╲__,_ |_| |_|╲_╲ 可爱飞行猪❤: golang83@outlook.com ??? Author Name: GeMarK.VK.Chow奥迪哥 ??? Creaet Time: 2019/05/25 - 07:51:34 ProgramFile: ico.go Description: Windows系统的ico文件工具包 */ package ico import ( "bufio" "bytes" "encoding/binary" "errors" "fmt" "io" "io/ioutil" "os" "path/filepath" "runtime" ) // 定义常量 const ( typeUKN = iota // unknow type typeBMP // bmp ico typePNG // png ico fileHeaderSize = 6 // 文件头的大小 headerSize = 16 // icon图标的头结构大小 bitmapHeaderSize = 14 // 位图文件头 dibHeaderSize = 40 // dib结构头 ) // 定义变量 var ( // 错误信息 ErrIcoInvalid = errors.New("ico: Invalid icon file") // 无效的ico文件 ErrIcoReaders = errors.New("ico: Reader type is not os.File pointer") // LoadIconFile的io.Reader参数不是文件指针 ErrIcoFileType = errors.New("ico: Reader is directory, not file") // io.Reader的文件指针是目录,不是文件 ErrIconsIndex = errors.New("ico: Slice out of bounds") // 读取ico文件时,可能出现的切片越界错误 ) // 类型定义 // 定义icon图标数据的存放 type ( ICONTYPE int WinIconData [][]byte WinIconStruct []winIconStruct ) // 定义 Windows 系统的 Ico 文件结构 type WinIcon struct { fileHeader *winIconFileHeader // 文件头 icos WinIconStruct // icon 头结构 icod WinIconData // 单独的icon图标的数据 data []byte // 所有ico文件数据 } type winIconFileHeader 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 uint32 // 图像数据的大小,单位字节 ImageOffset uint32 // 图像数据的偏移量 } type dibHeader struct { dibSize uint32 // 4bytes bitmapWidth uint32 // 4bytes bitmapHeight uint32 // 4bytes colorPlanes uint16 // 2bytes BitsPerPixel uint16 // 2bytes markerBI_RGB uint32 // 4bytes originalSize uint32 // 4bytes printResH uint32 // 4bytes printResV uint32 // 4bytes Palette uint32 // 4bytes importantColor uint32 // 4bytes } type bitmapHeader struct { bitmapID uint16 fileSize uint32 unusedA uint16 unusedB uint16 bitmapDataOffset uint32 } // createBitmapHeader 创建位图文件头结构 func createBitmapHeader(datasize int) *bitmapHeader { return &bitmapHeader{ bitmapID: binary.LittleEndian.Uint16([]byte{0x42, 0x4d}), fileSize: uint32(datasize + bitmapHeaderSize), unusedA: 0, unusedB: 0, bitmapDataOffset: uint32(bitmapHeaderSize + dibHeaderSize), } } // GetIconType 获取icon的数据类型 func GetIconType(d []byte) ICONTYPE { if checkDIBHeader(d) { return typeBMP } if checkPNGHeader(d) { return typePNG } else { return typeUKN } } // checkPNGHeader 检测是否是png ico数据 func checkPNGHeader(d []byte) bool { if len(d) < 8 { return false } if bytes.Compare(d[0:8], []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}) != 0 { return false } return true } // headerToBytes 将bitmapHeader位图头结构转换为字节切片 func (bmh *bitmapHeader) headerToBytes() []byte { d := make([]byte, bitmapHeaderSize) binary.LittleEndian.PutUint16(d[0:2], bmh.bitmapID) binary.LittleEndian.PutUint32(d[2:6], bmh.fileSize) binary.LittleEndian.PutUint16(d[6:8], bmh.unusedA) binary.LittleEndian.PutUint16(d[8:10], bmh.unusedB) binary.LittleEndian.PutUint32(d[10:14], bmh.bitmapDataOffset) return d } // JoinHeader 将bitmapfileheader链接到含有dib头的位图数据前 func (bmh *bitmapHeader) JoinHeader(d []byte) []byte { h := bmh.headerToBytes() j := [][]byte{h, d} return bytes.Join(j, nil) } // checkDIBHeader 检测是否是bmp ico数据 func checkDIBHeader(d []byte) bool { if len(d) < 40 { return false } a := d[0:4] b := []byte{0x28, 0, 0, 0} if bytes.Compare(a, b) != 0 { return false } return true } // 将ico文件的数据载入到内存 func LoadIconFile(rd io.Reader) (icon *WinIcon, err error) { // 类型断言 v, t := rd.(*os.File) if !t { return nil, ErrIcoReaders } // 声明与定义变量 var ( fileSize int64 ico *WinIcon ) // 获取文件信息及判断是否是文件,而不是目录 fi, err := v.Stat() if err != nil { return nil, err } if fi.IsDir() { return nil, ErrIcoFileType } fileSize = fi.Size() // 创建缓冲IO的Reader对象窥视6个字节的文件头 reader := bufio.NewReader(rd) p, err := reader.Peek(fileHeaderSize) if err != nil { return nil, err } // 检测文件头及获取头结构 icoHeader, err := getIconFileHeader(p) if err != nil { return nil, err } // 获取ico文件的所有数据 data, err := getFileAll(reader, fileSize) if err != nil { return nil, err } // 创建一个 winIconStruct 数组切片 icos := make(WinIconStruct, int(icoHeader.ImageCount)) icod := make(WinIconData, int(icoHeader.ImageCount)) // 根据文件头中表示的icon图标文件的数量进行循环 structOffset := fileHeaderSize for i := 0; i < int(icoHeader.ImageCount); i++ { wis := getIconStruct(data, structOffset, headerSize) icodata := wis.getImageData(data, wis.getIconOffset(), wis.getIconLength()) structOffset += headerSize icos[i] = *wis icod[i] = icodata } // 创建 WinIcon 对象 ico = &WinIcon{ fileHeader: icoHeader, icos: icos, icod: icod, data: data, } return ico, nil } // getFileAll 获取ico文件所有数据(不包括文件头的6个字节) // rd *bufio.Reader: 对象 // size int64: 文件大小(我们需要读取的总数量) // fb []byte: 文件的所有数据,如果成功读取的话 // err error: 如果读取出现错误,返回错误 func getFileAll(rd *bufio.Reader, size int64) (fb []byte, err error) { data := make([]byte, size) for i := int64(0); i < size; i++ { b, err := rd.ReadByte() if err != nil { return nil, err } data[i] = b } return data, nil } // getIconFileHeader 获取文件头结构 // b []byte: 读取的数据来自这个字节切片 // wih *winIconFileHeader: 如果获取成功返回 winIconFileHeader对象指针 // err error: 如果读取发生错误,则返回错误信息 func getIconFileHeader(b []byte) (wih *winIconFileHeader, err error) { if len(b) != fileHeaderSize { return nil, ErrIcoInvalid } reserved := binary.LittleEndian.Uint16(b[0:2]) filetype := binary.LittleEndian.Uint16(b[2:4]) imagecount := binary.LittleEndian.Uint16(b[4:6]) if reserved != 0 || filetype != 1 || imagecount == 0 { return nil, ErrIcoInvalid } header := &winIconFileHeader{ ReservedA: reserved, FileType: filetype, ImageCount: imagecount, } return header, nil } // getIconStruct 根据 offset, length 来获取icon图标结构 // b []byte: 文件数据的字节切片 // offset int: 偏移量 // length int: 数据长度 func getIconStruct(b []byte, offset, length int) (wis *winIconStruct) { var std []byte //std = b[offset:length] std = make([]byte, headerSize) j := 0 for i := offset; i < length+offset; i++ { std[j] = b[i] j++ } is := &winIconStruct{ Width: std[0], Height: std[1], Palette: std[2], ReservedB: std[3], ColorPlanes: binary.LittleEndian.Uint16(std[4:6]), BitsPerPixel: binary.LittleEndian.Uint16(std[6:8]), ImageDataSize: binary.LittleEndian.Uint32(std[8:12]), ImageOffset: binary.LittleEndian.Uint32(std[12:]), } return is } // getImageData 根据 offset, length 参数获取图标图像数据 // data []byte: 图像数据的字节切片 // offset int: 图像数据的偏移量 // length int: 图像数据的长度 // return []byte: 返回获取的数据字节切片 func (wi *WinIcon) getImageData(data []byte, offset, datasize int) []byte { var d = make([]byte, datasize) //data[offset:length] for i, j := offset, 0; i < datasize+offset; i++ { d[j] = data[i] j++ } return d } func (wis winIconStruct) getImageData(data []byte, offset, ds int) []byte { var d = make([]byte, ds) for i, j := offset, 0; i < ds+offset; i++ { d[j] = data[i] j++ } return d } // ExtractIconToFile 提取 ico 数据到文件 // filePrefix string: 为前缀,如果传如空字符串,则没有前缀,使用数字和分辨率作为文件名 // filePath string: 提取的数据写入的路径,空字符串则将文件保存到当前目录 // 舍弃:--count int: 提取文件的数量,0: 为所有,> 0 则根据已保存的map对象来提取对应数量内容,指定数量超出实际数量则全部提取-- // 该函数不检测路径的有效性,使用者自己把控,如果路径有问题,会返回error对象 func (wi *WinIcon) ExtractIconToFile(filePrefix, filePath string) error { var ext string for i, v := range wi.icos { w := v.getIconWidth() h := v.getIconHeight() b := v.getIconBitsPerPixel() d, _ := wi.GetImageData(i) if GetIconType(d) == typeBMP { ext = "bmp" } else { ext = "png" if w == 0 && h == 0 { w = 256 h = 256 } } fn := v.generateFileNameFormat(filePrefix, ext, w, h, b) if err := wi.IconToFile(filePath, fn, i); err != nil { return err } } return nil } // GetImageData 获取ico图标的图像数据 // index int: 下标索引,0序 // 如果越界或读取数据错误,返回 error 对象 func (wi *WinIcon) GetImageData(index int) (d []byte, err error) { if index >= wi.getIconsHeaderCount() || index < 0 { return nil, ErrIconsIndex } wis := wi.icos[index] offset := wis.getIconOffset() datasize := wis.getIconLength() data := wi.getImageData(wi.data, offset, datasize) return data, nil } // IconToFile 将图标写入文件 // path string: 文件写入的路径 // name string: 文件名 // error 如果写入发生错误,则返回错误信息 // IconToFile 并不会检测路径是否有效 func (wi *WinIcon) IconToFile(path, name string, index int) error { if index >= wi.getIconsHeaderCount() || index < 0 { return ErrIconsIndex } wis := wi.icos[index] p := filepath.Join(path, name) d, e := wi.GetImageData(index) if e != nil { return e } // 处理bitmap头结构 if GetIconType(d) == typeBMP { w := wis.getIconWidth() h := wis.getIconHeight() b := wis.getIconBitsPerPixel() s := len(d) - dibHeaderSize dib := createDIBHeader(w, h, b, s, 0, 0) err := dib.EditDIBHeader(d) if err != nil { return err } bmh := createBitmapHeader(len(d)) d = bmh.JoinHeader(d) } if e := wis.IconToFile(p, d); e != nil { return e } else { return nil } } // IconToIcoFile 将ico文件中的指定icon图标数据写入ico文件 // path string: 路径(不检查合法性) // index int: icon图标的索引 // error: 如果发生错误返回error对象 func (wi *WinIcon) IconToIcoFile(path string, index int) error { if index < 0 || index >= len(wi.icos) { return ErrIconsIndex } d := wi.icod[index] wis := wi.icos[index] wis.ImageOffset = fileHeaderSize + headerSize wis.ImageDataSize = uint32(len(d)) d = wis.joinHeader(d) if e := ioutil.WriteFile(path, d, getPerm()); e != nil { return e } return nil } // getIconsHeaderCount 获取 icons 图标的结构数量-可能和头结构的ico数量不一致,只是可能 // 返回值为数量,类型 int func (wi *WinIcon) getIconsHeaderCount() int { return len(wi.icos) } // generateFileNameFormat 产生文件名 func (wis winIconStruct) generateFileNameFormat(prefix, ext string, width, height, bit int) string { return fmt.Sprintf("%s_icon%dx%d@%dbit.%s", prefix, width, height, bit, ext) } // iconToFile 将ico图像数据写入磁盘文件 func (wis winIconStruct) IconToFile(path string, data []byte) error { if err := ioutil.WriteFile(path, data, getPerm()); err != nil { return err } return nil } func (wis winIconStruct) headerToBytes() []byte { d := make([]byte, fileHeaderSize+headerSize) binary.LittleEndian.PutUint16(d[0:2], 0) binary.LittleEndian.PutUint16(d[2:4], 1) binary.LittleEndian.PutUint16(d[4:6], 1) d[6] = uint8(wis.getIconWidth()) d[7] = uint8(wis.getIconHeight()) d[8] = wis.Palette d[9] = wis.ReservedB binary.LittleEndian.PutUint16(d[10:12], wis.ColorPlanes) binary.LittleEndian.PutUint16(d[12:14], wis.BitsPerPixel) binary.LittleEndian.PutUint32(d[14:18], wis.ImageDataSize) binary.LittleEndian.PutUint32(d[18:22], wis.ImageOffset) return d } func (wis winIconStruct) joinHeader(d []byte) []byte { h := wis.headerToBytes() j := [][]byte{h, d} return bytes.Join(j, nil) } // getIconOffset 获取icon图像数据的偏移量 // 返回偏移量数据 func (wis winIconStruct) getIconOffset() int { return int(wis.ImageOffset) } // getIconLength 获取icon图像数据的长度 // 返回长度数据 func (wis winIconStruct) getIconLength() int { return int(wis.ImageDataSize) } // getIconWidth 获取icon图像数据的宽度 func (wis winIconStruct) getIconWidth() int { return int(wis.Width) } // getIconHeight 获取icon图像数据的高度 func (wis winIconStruct) getIconHeight() int { return int(wis.Height) } // getIconBitsPerPixel 获取icon图像数据的颜色位数 func (wis winIconStruct) getIconBitsPerPixel() int { return int(wis.BitsPerPixel) } // createDIBHeader创建DIB头结构 func createDIBHeader(width, height, bit, size, p, i int) *dibHeader { return &dibHeader{ dibSize: 40, // uint32 bitmapWidth: uint32(width), // uint32 bitmapHeight: uint32(height), // uint32 colorPlanes: 1, // uint16 BitsPerPixel: uint16(bit), // uint16 markerBI_RGB: 0, // uint32 originalSize: uint32(size), // uint32 printResH: 2835, // uint32 printResV: 2835, // uint32 Palette: uint32(p), // uint32 importantColor: uint32(i), // uint32 } } // HeaderToBytes 将DIB的头结构转换为字节切片 func (dh *dibHeader) HeaderToBytes() []byte { d := make([]byte, dibHeaderSize) binary.LittleEndian.PutUint32(d[0:4], dh.dibSize) binary.LittleEndian.PutUint32(d[4:8], dh.bitmapWidth) binary.LittleEndian.PutUint32(d[8:12], dh.bitmapHeight) binary.LittleEndian.PutUint16(d[12:14], dh.colorPlanes) binary.LittleEndian.PutUint16(d[14:16], dh.BitsPerPixel) binary.LittleEndian.PutUint32(d[16:20], dh.markerBI_RGB) binary.LittleEndian.PutUint32(d[20:24], dh.originalSize) binary.LittleEndian.PutUint32(d[24:28], dh.printResH) binary.LittleEndian.PutUint32(d[28:32], dh.printResV) binary.LittleEndian.PutUint32(d[32:36], dh.Palette) binary.LittleEndian.PutUint32(d[36:40], dh.importantColor) return d } // EditDIBHeader 修改DIB头 func (dh *dibHeader) EditDIBHeader(b []byte) error { if len(b) <= 40 { return ErrIconsIndex } h := dh.HeaderToBytes() for i := 0; i < 40; i++ { b[i] = h[i] } return nil } func CreateWinIcon(b [][]byte) (*WinIcon, error) { return nil, nil } func getPerm() os.FileMode { if runtime.GOOS == "windows" { return 0 } else { return 0666 } }
-
windows图标大全
2021-02-14 10:08:59windows图标大全 -
非常精美应用程序图标 ICO 合集
2018-09-17 11:57:27多年收集的精美的图标,通用性强,非常适合作为应用程序的图标和按钮图标。 -
Windows 空白图标资源 blank.ico
2019-06-10 10:58:54Windows 空白图标资源,blank.ico,windows系统资源,通过修改注册表,去除windows图标箭头 -
常用windows文件图标
2008-06-21 21:36:12常用的windows文件图标,ico格式,方便网页设计 -
Win10系统图标ico文件全套
2021-08-02 14:13:47Win10系统图标ico文件全套 -
win10 ico图标,完整原版
2020-09-10 10:17:00win10原版ico、dll 含win10资源管理器原版图标,可替换q-dir图标 及系统全部默认图标dll供大家使用 希望对大家有帮助,推荐一款文件资源管理器——q-dir -
Mico and Sico:将图像文件合并为一个 Windows 图标 (.ico) 文件。-开源
2021-07-12 04:07:00Mico 是一个命令行工具,用于从多个图像创建 Windows 图标。 Sico 是一个命令行工具,用于将图标拆分回单独的图像。 -
最新最全Windows10系统图标
2019-01-11 15:24:10最新最全Windows10系统图标,包含各种硬件图标、磁盘管理所用图标、音频设备图标、网络和共享中心图标和各种传感器图标。 -
Go语言处理Windows系统的图标ICO文件(上)
2019-05-25 23:29:30Go语言处理Windows系统的图标ICO文件(中)… 编写中 Go语言处理Windows系统的图标ICO文件(下)… 编写中 概述 我们在编写一个程序后,如果是windows系统中的程序,我们希望它有一个图标,这个时候,我们可以使用一些...ICO 文件格式
存放在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文件分析
单个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便携式网络图形)格式。
多个ico图标结构分析
所以我们再来看一个多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文件
-
windows98 xp的图标资源.rar
2021-01-20 19:21:11自己提取的Windows98和XP下的经典图标,做一些APP的时候如果需要复古风可以采用,里面还附加了ico图标裁剪工具,可以根据需要裁剪图标大小。 -
怎样将jpg、png制作成windows桌面ico图标文件
2021-12-08 15:47:19之前在前端设计实现了一个生成ico图标的功能,然而后来经过用户的反馈才发现,之前生成的ico图标的编码格式,其实只能在网站等地方使用,如果在windows桌面上使用,就会提示找不到ico图标文件,错误提示如下所示。...之前在前端设计实现了一个生成ico图标的功能,然而后来经过用户的反馈才发现,之前生成的ico图标的编码格式,其实只能在网站等地方使用,如果在windows桌面上使用,就会提示找不到ico图标文件,错误提示如下所示。
这个错误确实是百思不得其解,我以为只要图片的后缀是ico的,windows系统就能够认可,然而事实是,windows的桌面ico对它内部的编码格式是有很严格要求的。也就是说,windows桌面图标使用的ico文件,不是从png或者jpg直接演化而来的。
因此从网上找到了一个思路,也就是先将任何图像变成bmp格式的,因为bmp格式保存的就是实际的像素值和每个像素的内容。然后再将bmp格式后缀改成ico就能够被windows系统所识别了。所以我们可以将普通图像显示在canvas中,然后getimagedata获取到图像的每个像素内容,然后通过代码,以bmp格式的形式,来将每个像素放入到规定的编码格式中,并且附加上数据头等等一些指示符,就可以自己拼接出一个完整的bmp编码文件,然后变成ico后缀就搞定了。大概的思路就是这样,因为实际上自己拼接出图像文件的编码并不是太简单,当然也不难,开源的资源也很多。
在线ICO图标制作生成,图片转换ICO图标Pro版
https://www.butterpig.top/icopro
所以这个工具现在可以选择是将图片转换成普通网站使用的ico图标,还是windows专用的标准ico图标格式。并且因为windows桌面图标是通过bmp演化而来,所以实际上这个ico的文件大小,其实居然是和他的宽高尺寸完全相关的,大家可以自己去这个工具体验一下。
-
visual c++用代码 提取EXE或DLL中的图标ico
2021-01-28 10:36:33vc提取EXE或DLL中的资源,比如 图标 -
苹果桌面图标ico全集
2012-04-09 13:55:40涵盖苹果电脑系统所有ico图标 windows系统可以直接更换 -
win11 图标 icon
2022-04-18 07:50:20windows11 中的所有图标,icon -
Mico and Sico:将图像文件合并到单个Windows图标(.ico)文件中。-开源
2021-05-12 10:50:25Mico是用于从多个图像创建Windows图标的命令行工具。 Sico是用于将图标拆分回单独图像的命令行工具。 -
git-icon:Windows 10 的存储库图标
2021-05-31 19:05:40适用于 Windows 10 的 Git 图标 Windows 10 文件夹图标风格的 Git 存储库图标。 我最终还可能包含 Windows 7 和 8 以及 Mac OS X 的图标。 安装 下载 git.ico 将 git.ico 放在不会被删除的位置。 我推荐在 C:/... -
windows图标文件打包
2021-08-24 17:59:23.ico 酷炫图标文件打包带走 -
给你400个.ico图标文件.zip
2021-01-11 11:54:26给你400个.ico图标文件.zip 图标各类有 windows 互联网 交通 其它 体育 办公 动物 卡通人物 太空 娱乐 建筑物 杂项 电脑 电子产品 水果 生活用品 游戏人物 绘图 水果 电视节目 音乐 食品 饮料.. -
IconMod:从Windows可执行文件添加和删除图标组(.ico文件)-开源
2021-04-13 06:27:03IconMod从Windows可执行文件添加和删除图标组(.ico文件)。 -
ico图标是什么格式的文件
2021-07-26 02:06:42ICO是Windows的图标文件格式,图标文件可以存储单个图案、多尺寸、多色板的图标文件。一个图标实际上是多张不同格式的图片的集合体,并且还包含了一定的透明区域。下面来看看具体的介绍吧。ico图标格式它是Windows的... -
Go语言处理Windows系统的图标ICO文件(下)
2019-06-04 15:12:55Go语言处理Windows系统的图标ICO文件(上) Go语言处理Windows系统的图标ICO文件(中) 将PNG或BMP转换为ICO文件 在前面的两章中,我们初探了ico文件的结构,并且完成了将ico文件中的bmp和png数据提取出来的功能,在本... -
分享win10常用图标ico_png_html素材
2021-09-27 11:06:50最近搞前端web开发,需要展示常用相关文件的图标以及点击下载,自己扣了一些win10常用图标,可用于html素材, 分享一下...图标样本请看 https://blog.csdn.net/tianbbs2008/article/details/120314073 -
WIndows 下所有ICon图标集合
2016-03-10 11:27:45WIndows 下所有ICon图标集合,包含48*48 32*32 16*16 -
【gcc 给exe文件添加ico图标(win10)】
2021-11-23 14:24:10准备一个ico图标文件放在工程目录A,命名demo.ico 在目录A下新建一个文件,demo.rc,内容如下,注意前面加id,否则会报语法错误 id ICON "demo.ico" 用cmd或powershell进入目录A,执行: windres -i "demo.rc" -o...