原本我程序里用的的图标都是网上下载或者在线用png转成ico,但是之前那个公司不能上外网,而ps和fw都不能直接生成ico格式(ps可以找转ico的插件)。后来,网上找了个C#图片转ico的例子,借助的Icon类,但是生成的图效果不太好,透明图部分会有黑线(预览)。于是乎就折腾了半天ico格式的协议,主要参考的是https://www.cnblogs.com/cswuyg/p/3603707.html以及他的相关参考,还有http://lqzit.iteye.com/blog/1033407。除了掩码没搞明白,其他基本都清楚了,然后根据在线转换网站生成的格式来作为参照,完成了生成ico的功能。(其实可以直接参照开源软件ImageMagick的代码,在源码包的coders文件夹里有各种图的格式,我也是后来才知道,ImageMagick中文网:http://www.imagemagick.com.cn/download.html)
如果要了解icon相关的协议,可以百度或者看我最上面那个链接,这里我只写下我的实现。生成的是32色RGBA的ico图,在xp/win7/win10下显示正常,预览没有发现有黑线。
首先,ico结构=Icon格式头+位图格式头+位图数据(数据行是反过来的,第一行在最后)+位图掩码部分。
(代码中Byte一个字节,Word两个字节,Long和DWord四个字节,小端模式--即低位在前)
第一部分:Icon格式头(22Byte)
struct IconHeader
{
WORD idReserved; // 保留位必须为0
WORD idType; // 类型 (1表示icon),也就是必须为1
WORD idCount; // 包含多少张图(icon可以含多张图,分别在不同尺寸下展示)
ICONDIRENTRY idEntries[]; // 对应idCount的结构体,多少张图就有多少个该结构
};
struct ICONDIRENTRY
{
BYTE bWidth; // 图片的像素宽度
BYTE bHeight; // 图片的像素高度
BYTE bColorCount; // 颜色深度:=1<<(wBitCount×wPlanes),如果大于等于8就为0;但是可以不管,填0就行
BYTE bReserved; // 保留位必须为零
WORD wPlanes; // Color Planes,我填的1
WORD wBitCount; // Bits per pixel,我填的32
DWORD dwBytesInRes; //该图字节数(位图头+image数据+掩码的字节总数)
DWORD dwImageOffset; //相对于文件开始处的偏移量,单个图填22,毕竟icon头加起来22字节
};
通过BeyondCompare软件查看得到的icon头hex数据是这样的(64*64像素)
第二部分:位图格式
struct BitmapHeader
{
BITMAPINFOHEADER icHeader; // DIB header
RGBQUAD icColors[]; // 颜色表,24/32色不用颜色表
BYTE icXOR[]; // DIB bits for XOR mask
BYTE icAND[]; // DIB bits for AND mask 这两个单色的时候位置相反
};
struct bitmapHeader
{
DWORD biSize;//结构长度,40Byte
LONG biWidth; //图宽度
LONG biHeight;//图高度(xor+and的)也就是两倍高度
WORD biPlanes;//我填的1
WORD biBitCount;//我填的32
DWORD biCompression;//压缩方式,填的0
DWORD biSizeImage;//图大小,仅rgba部分
LONG biXPelsPerMeter;//(后面四个貌似不用管,是属于bmp的格式),X方向分辨率
LONG biYPelsPerMeter;//Y方向分辨率
DWORD biClrUsed;//颜色数,可以填0默认
DWORD biClrImportant;//重要颜色数,填0表示全是重要
};
通过BeyondCompare软件查看得到的位图头hex数据是这样的
虽然后两个字节数组xor和and没搞懂协议怎么算的,但是我根据网站生成的格式倒推出,icHeader位图格式头为40Byte;icColors我用的32色所以没有该部分;icXor填图片原始数据,并且行数是倒过来的,第一行是原图的最后一行,以此类推,且每个像素先后填入颜色B.G.R.A部分值,共width×height×4 Byte;icAnd部分,对应不透明度,如果是透明就为1,半透明和不透明都是0(相当于1个bit对应于一个像素点,行数同height,宽度若原为16/32则为掩码占的宽度为4Byte,若原为48/64则为8Byte,若原为128则为16Byte,并且16和48时有效位填不满,空出来的是每行后部分字节填0(自行脑补二维数组,行数相等,arr[0][0]有效arr[0][n-1]为0)),掩码部分占了width×掩码宽度 Byte。(之前16/48无效部分我填的零,但是后来看网站生成的好像填的1)
这样,假如是64*64的ico图标,应该占16958字节,22+40+16384+512.
知道了格式就容易把图片转为ico图标了,获取rgab值然后填充字节数组就行了。