ICO 介绍

ico是Icon file的缩写,是Windows的图标文件格式的一种,可以存储单个图案、多尺寸、多色板的图标文件。图标是具有明确指代含义的计算机图形。其中桌面图标是软件标识,界面中的图标是功能标识。图标有一套标准的大小和属性格式,且通常是小尺寸的。每个图标都含有多张相同显示内容的图片,每一张图片具有不同的尺寸和发色数。一个图标就是一套相似的图片,每一张图片有不同的格式。从这一点上说图标是三维的。图标还有另一个特性:它含有透明区域,在透明区域内可以透出图标下的桌面背景。在结构上图标其实和麦当劳的巨无霸汉堡差不多。一个图标实际上是多张不同格式的图片的集合体,并且还包含了一定的透明区域。因为计算机操作系统和显示设备的多样性,导致了图标的大小需要有多种格式。操作系统在显示一个图标时,会按照一定的标准选择图标中最适合当前显示环境和状态的图像。如果你用的是Windows98操作系统,显示环境是 800x600分辨率,32位色深,你在桌面上看到的每个图标的图像格式就是256色32x32象素大小。如果在相同的显示环境下,Windows XP操作系统中,这些图标的图像格式就是:真彩色(32位色深)、32x32象素大小。

下面就是Windows各个操作系统中的标准图标格式:(单位:大小象素—颜色)。Windows 98 SE/ME/200048 x 48 - 256 32 x 32 - 256 16 x 16 - 25648 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 参考

   win 32 注册窗口时,用LoadIcon()加载ICON图标时,VS2008打开ICON图标发现,一个ICON图标里有多个BMP图片数据。不同SIZE.仔细baidu.google,发现了一点点知识。贴上;
windows ICON 有其固定的几种size ,对于xp NT,标准是48*48(256),还有其他大小,32*32(256色彩),16*16(256色彩),windows 支持的色彩model,有16色彩,256色彩,还有真彩RGB.一个标准的ICON图标里还应该含有RGB/A色彩model的sizeBMP.所以一个完善的ICON图标还有48*48(RGB),32*32(RGB),16*16(RGB),当然为了支持后续的系统版本,还有超大图标256*256(VISTA系统中使用,但是在win7 中也用到了)。MAC系统中也有使用。不过MAC不适用ICON.用图片软件打开只能显示最后一个图标;
一个ICON图标里包含多个size,使得System能够使用一个ICON,便能用不同zixe显示图标,如在桌面上程序图标48*48,EXE窗口上16*16,当用户选择文件浏览里,便有大图标,小图标,超大图标显示功能。至于window是怎么知道什么时候用大图标,什么时候用小图标,怎么实现的?暂时不清楚。 

ICON ,图标结构:
一个ICON图标里的BMP位图数据中其实包含了两个位图,一个是AND(逻辑与)位图,一个是xor(异或)位图。AND位图相当于是个蒙版图片。假设要显示的图标是红桃心,那么这个AND位图,用0来形成心的形状(像素显示黑色),其他则是1,白色。先将AND图与图标所在位置背景区域里进行AND运算,那么,该区域里,与黑色心形AND的区域都成为0,都是黑色了,但其他部分则保留原来背景区域色彩。然后和XOR位图进行XOR运算。XOR位图里红色的心形存储真实色彩数据,心形外的则为黑色用0表示。进行XOR运算,与0运算的数据即为原来数据,所以,形成图案显示,背景透明和原来一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 //icon文件数据格式:
//文件头(6)字节
WORD idresserved //双字节大小,保留字必须是0;
WORD idtype //资源类型,1为ICON,2为光标
WORD Idcount //文件里有几个图像

//图像索引信息块(1)16字节 有多少个图像就有多少个信息索引快
byte bwidth; // width, in pixels, of the image
byte bheight; // height, in pixels, of the image
byte bcolorcount; // number of colors in image (0 if >=8bpp)
byte breserved; // reserved ( must be 0)
word wplanes; // color planes
word wbitcount; // bits per pixel
dword dwbytesinres; // how many bytes in this resource?
dword dwimageoffset; // where in the file is this image?
1
2
3
4
5
6
7
8
//BMP图像信息头(40字节)有多少个图像就有多少个信息头  C中定义:
typdef struct
{
bitmapinfoheader icheader; // dib header
rgbquad iccolors[1]; // color table
byte icxor[1]; // dib bits for xor mask
byte icand[1]; // dib bits for and mask
} iconimage, *lpiconimage;

接下来就是xor调色板,(这个还是有点含糊)

接下来是xor位图,以调色板里的值存放;
然后是AND位图。

所看到这里,也大概知道ICON结构原理。 用软件打开ICON,发现ICON里存放也是有规律的,先RGB模式4848,3232,1616,然后是超大模式256256.然后是低色彩模式4848 ,3232 16*16.
猜测Window 回根据窗口句柄去遍历ICON的SIZE,根据context表中维护的全局宏来对应load哪一个大小的ICON.如果没有该size的,则匹配最适合的。这也就是有的程序图标当我们放大时,还是那么小,丫的,人家最大就那么点点。

知道了图标格式构成,能不能readfile and writefile ,自己用程序写一个ICON呢??有时间尝试下。如果在ICON 加入一些SHELL code?

参考:

Icon资源详解一

本文分享&备忘最近了解到的icon资源在windows平台下相关的一部分知识,所有测试代码都尽可能的依赖win32 API实现。更全面的知识,参考文末列出的”参考资料“。

 关键字:Icon资源存储结构、windows shell下显示哪个图标、如何获取EXE各种长宽的显示图标。
一个icon资源(可以是*.ico文件,也可以是windows资源节区里的icon group),可以包含多张图片,这些图片有着各自的size或者颜色深度,这些图片可以是bmp格式或者png格式(vista之后支持,一般256*256时使用)。

Icon文件的组成

Icon文件结构由两部分组成:icon文件头和多张图片数据,图片可以是bmp、png:
  1. Icon文件头

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
        //Icon文件头

    typedef struct
    {
    WORD idReserved; // Reserved (must be 0)
    WORD idType; // Resource Type (1 for icons)
    WORD idCount; // How many images?
    ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em)
    } ICONDIR, *LPICONDIR;

    //其中idReserved必须是0;idType对于ICON文件来说必须是1;idCount指明icon文件有多少张图片,也就指明了接下来有多少个ICONDIRENTRY结构体数据。

    typedef struct
    {
    BYTE bWidth; // Width, in pixels, of the image
    BYTE bHeight; // Height, in pixels, of the image
    BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
    BYTE bReserved; // Reserved ( must be 0)
    WORD wPlanes; // Color Planes
    WORD wBitCount; // Bits per pixel
    DWORD dwBytesInRes; // How many bytes in this resource?
    DWORD dwImageOffset; // Where in the file is this image?
    } ICONDIRENTRY, *LPICONDIRENTRY;

    //其中dwBytesInRes指明本图片占用多少字节,dwImageOffset指明本图片数据开始位置相对文件头的偏移。颜色深度:bColorCount = 1 << (wBitCount * wPlanes),如果大于等于8,则填0。

    ````

    2. 图片数据

    ```c++
    //如果是位图格式图片:
    typdef struct
    {
    BITMAPINFOHEADER icHeader; // DIB header
    RGBQUAD icColors[1]; // Color table
    BYTE icXOR[1]; // DIB bits for XOR mask
    BYTE icAND[1]; // DIB bits for AND mask
    } ICONIMAGE, *LPICONIMAGE;

    //icHeader中只有部分信息是icon文件需要的。icXOR也称为“image“,icAND也称为“mask”。

一个有两张位图图片的ico文件,文件头内容(前0x15字节是icon文件头 + ICONDIRENTRY 的内容):

如果是PNG格式图片,则Icon文件头后边接着的是png数据格式,譬如:
icon中含有一张png图片,文件头内容(前0x15字节是icon文件头 + ICONDIRENTRY 的内容):

png格式API相关:虽然LoadImage在vista版本下可以加载含有png格式图片的ico文件,但是windows没有直接API支持应用程序对png、jpeg格式文件的解压使用。png、jpeg格式数据时,有StretchDIBits、SetDIBitsToDevice两API可以使用,但必须先检查硬件是否支持,因为这属于硬件级别的解码支持。想在GetDIBitsBI_PNG,获取PNG格式的数据是行不通的,BITMAPINFOHEADER结构体中的biCompression值BI_PNG,仅仅在上述两API中生效。
为什么在icon文件中有png格式存储:.ico文件中含有png格式,这是vista以及之后的版本才做的支持,目的是为了减小256256 ico 文件的size,一般的ico编辑会默认把ico文件中256256 size的图片保存为png格式,这是微软推荐的保存格式(参考资料5)。xp以及之前的系统用不到256256格式的ico,所以没有支持。

Icon在windows中的显示

这里的icon特指两种情形:(1)EXE中的icon group资源;(2).ico文件。
EXE文件在windows shell下显示哪个icon,按照MSDN文文件档(参考资料1)的介绍,是这样子:
桌面图标显示哪个ICON图片,有两个步骤,1、选择序号最小的RT_GROUP_ICON,优先字母序号,再找数字序号;2、在RT_GROUP_ICON里面选择合适的RC_ICON:(1)size跟要求的最接近;(2)如果size符合要求的有多个,则选择颜色深度跟显示系统颜色深度匹配的;(3)如果颜色深度没有匹配的,则选择在显示系统支持范围内颜色深度值大的;(4)如果所有的颜色深度都大于显示系统支持范围,则选择颜色深度最低的;(5)系统把8位以及大于8位的颜色深度当作相同深度,这时,系统选择最先遍历到的RC_ICON;(6)如果显示系统的颜色深度是8位,则系统会在16色(颜色深度4位) 和256色(颜色深度8位)之间选择16色的,并且使用系统默认调色板。(这一条规律似乎跟(3)矛盾,有人说这是在安全模式下才成立的)。
在RT_GROUP_ICON选定的情况下,按照我自己的实践测试:
自己绘制一个
.ico文件并对各个size做标记,可以发现windows7下exe在选择显示哪个图标的时候,只用到:16 * 16(文件夹内小图标),32 * 32(属性、桌面小图标), 48 * 48(中等图标), 256 * 256(大图标)这四种size,xp下则使用前3种。如果ico中含有png格式,则需要使用VS2008以及以上的IDE,否则rc无法对其进行编译。对于windows显示何种图标参考资料4、5、6有更详细的介绍。

从PE文件中提取icon资源

对于EXE文件,最简单的是使用SHGetFileInfo API,但只能获取16* 16,32 * 32 size的图标。该api如果只指定SHGFI_ICON,则表明使用默认的3232,如果指定SHGFI_LARGEICON,则表明是3232,如果指定SHGFI_SMALLICON,则表明是16*16。SHGetFileInfo 还可以根据文件后缀名获取某类文件的图标。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//1、某文件的32 * 32 pixels icon
//获取exe的32 * 32 icon
SHFILEINFO sfi;
ZeroMemory(&sfi, sizeof(SHFILEINFO));
::SHGetFileInfo(L"D:\\a.exe", FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(SHFILEINFO), SHGFI_ICON | SHGFI_LARGEICON);
HICON icon_handle = sfi.hIcon;

//2、某类文件的32 * 32 pixels icon
//获取*.rmvb文件类型的32 * 32 icon
SHFILEINFO sfi;
ZeroMemory(&sfi, sizeof(SHFILEINFO));
::SHGetFileInfo(L"*.rmvb", FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(SHFILEINFO), SHGFI_ICON | SHGFI_USEFILEATTRIBUTES);
HICON icon_handle = sfi.hIcon;

//3、 提取其它size的图标,譬如: 48 * 48、256 * 256,则需要使用另外的方法
//获取48 * 48 或者 256 * 256 pixels icon
SHFILEINFO sfi;
ZeroMemory(&sfi, sizeof(SHFILEINFO));
::SHGetFileInfo(L"D:\\b.exe", FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX);
HIMAGELIST* imageList = NULL;
//SHIL_EXTRALARGE获取48 * 48的图标, SHIL_JUMBO 获取256 * 256的图标。
HRESULT hResult = ::SHGetImageList(SHIL_EXTRALARGE , IID_IImageList, (void**)&imageList);
HICON icon_handle = NULL;
if (hResult == S_OK)
{
hResult = ((IImageList*)imageList)->GetIcon(sfi.iIcon, ILD_NORMAL, &icon_handle);
}

获取到HICON之后,可以使用DrawIcon API执行绘制,也可以使用AlphaBlend“重造轮子”执行绘制。如果要保存为文件,则构造一个ICON文件头,再把HICON转换为DIB数据拼在ICON文件头后边,注意:hbmColor和hbmMask这两个句柄的DIB数据都要有。我试过直接拿bmp位图数据修改:前头加上icon文件头,BITMAPINFOHEADER的biHeight = 2,保存后各种size的图片都变残缺了(好像是被挖去了一个二维码),只有256256的图片少部分像素丢失,显示稍微正常点,可能是因为256256的图片mask部分用处不大的缘故,另外,还发现这样子的256256 icon图片是留有透明通道的,这也说明,*.bmp位图文件中是可以有透明通道的,只是显示图片的工具把它忽略了。(2014.3.22 部分内容补充)
上边所说到的ico资源,都是在windows shell下能显示的图片,如果要提取没有显示的图片,则要利用资源枚举系列函数:EnumResourceNames、FindResoucLoadResource、LockResource、CreateIconFromResourceEx。

参考资料

  1. ICON结构,很全面的知识:

  2. ICON发展历史系列,涉及一些MSDN没有谈到的细节:
    ICO文件格式的演化(一):单色图标
    ICO文件格式的演化(二):彩色图标
    ICO文件格式的演化(三):带透明通道的图标
    ICO文件格式的演化(四):PNG格式

  3. HICON保存为*.ico文件,对于256格式的icon图片,保存时没有保存为png,其中一个答案的代码:“WriteFile(hFile, &padding, 4 - bmp.bmWidthBytes, &nWritten, 0);”有错误,应该是:“ WriteFile(hFile, &padding, 4 - (bmp.bmWidthBytes & 3), &nWritten, 0); ”,该答案对于低于8颜色深度的图片没有做处理,另一个使用GetDiBits实现的答案更好。
    http://stackoverflow.com/questions/2289894/how-can-i-save-hicon-to-an-ico-file

  4. windows系统对ICON显示的各种选择:http://stackoverflow.com/questions/3236115/which-icon-sizes-should-my-windows-applications-icon-include

  5. ICO 文件格式以及发展历史:http://en.wikipedia.org/wiki/ICO_(file_format)

  6. vista中的icons图标:http://msdn.microsoft.com/en-us/library/aa511280.aspx

  7. 不支持在应用程序中使用jpeg、png格式的解压,仅仅StretchDIBits、SetDIBitsToDevice两API支持直接向输出设备输出jpeg、png压缩格式数据,这是硬件支持的,使用时需要先判断硬件是否支持: http://msdn.microsoft.com/en-us/library/dd145023(VS.85).aspx

  8. 获取exe *.ico文件中所有size的图片,批评者的观点很全面:http://stackoverflow.com/questions/16330403/get-hbitmaps-for-all-sizes-and-depths-of-a-file-type-icon-c

  9. 总结了IOS、Windows上的各种icon size对比:http://www.visualpharm.com/articles/icon_sizes.html

  10. icon resources analyze tool

Icon资源详解二

本文分享&备忘最近了解到的icon资源在windows平台下相关的一部分知识。所有测试代码都尽可能的依赖win32 API实现。通过源码可以了解其结构,同时它们也是可复用的代码积累。
内容摘要:如何保存exe的图标为*.ico文件、如何遍历PE文件的icon资源并保存为*.ico文件、如何合并多个*.ico文件、如何分割含有多张图片的*.ico文件.

保存exe的图标为*.ico文件

exe在windows shell下的可显示图标有3或者4种,使用win32 API获取它们的ICON handle的代码也很简单,前一篇文章已经说过。这里主要难点是如何根据ico文件的存储格式,把HICON保存为*.ico文件。
文件格式:icon文件头 + directory entrys + bmp header info and color table + image data + mask data。
思路:从HICON拿到两个BITMAP HANDLE,从Color HANDLE拿到ICON image data,从MaskHandle拿到mask data,从两HANDLE拿到bmp header 的数据。所有数据写入文件,循环处理多个HICON。
256256 icon的说明:这是windows vista+版本才有的。虽然可以加载保存为png格式的.ico文件,但是GDI并没有提供直接操作png图片的API,我写的工具保存256256 icon图片为BMP格式,会比较大。注意到在directory entry中各只有一个字节保存icon的的长、宽,所以256*256的图片该位置的值是0。
HICON保存为*.ico文件的详细代码见:

抽取PE文件的icon资源

使用EnumResourceNames枚举到资源,通过CreateIconFromResourceEx从资源获取到HICON,之后就是HICON到*.ico的过程。也可以尝试加个icon文件头后直接把资源写入到文件(我未做测试验证)。
枚举PE文件所有*.ico资源的HICON的详细代码:

合并*.ico文件

这里不需要涉及资源API,纯粹是文件数据操作。
思路:把所有*.ico文件的数据读取出来,再把数据按照一个*.ico文件的格式找位置写到一个文件里。我的做法是先读取每个源文件的前6个字节,拿到了所有图片的算出directory entry的占用的size,这样就可以定位到目标文件图片实际数据的初始位置,后面再循环处理一个个ico文件,写入图片数据和entry。小工具不暂考虑文件被读两次的性能问题~
合并*.ico文件的详细代码:

分割含有多张图片的*.ico文件

这里也不需要涉及资源API,纯粹是文件数据操作。
思路:读取*.ico文件头的前6个字节,拿到图片个数,循环读取对应的directory entry和图片数据,把新的*ico文件头、新的directory entry、图片数据写入到新立目标文件。
分割*.ico文件的详细代码:

工具release

  1. 找出EXE的所有windows shell可显示icon,并保存到一个*.ico文件中;

  2. 找出PE文件的所有icon,并按组保存;

  3. 合并*.icon文件;

  4. 将*icon中的图片一个个分割保存;

工具所在
代码project

常用工具

首先很多大型制图软件都支持或者插件支持ico格式图标,比如: Photoshop、gimp、IconWorkshop等;

以下为一些小型的工具:

参考:

key in Github

windows ico tool