0xAA55 发表于 2015-1-24 03:18:30

【汇编】使用NASM“编译”制作ICO、CUR文件

所谓ICO、CUR指的是Windows的图标和光标文件。这种文件结构十分简单,描述一下就是这样:

ICO、CUR文件结构:
1个文件信息头
N个图像信息头
然后是N个BMP(BITMAPINFOHEADER、调色板、位图)
BITMAPINFOHEADER的高的值是宽的两倍。
如果不是32位的位图,则尾部附带单色位图透明数据

文件信息头的结构:struct ICONFILEHEADER                //文件信息头,一个ICO只有一个
{
        WORD        wReserved;                //保留,值为0
        WORD        wType;                        //类型,1为ICO,2为CUR
        WORD        wCount;                        //整个ICO文件的图像数
};图像信息头的结构:struct ICONINFOHEADER                //图像信息头,有多少图像就有多少个图像信息头
{
        BYTE        bWidth;                        //图像宽度
        BYTE        bHeight;                //图像高度
        BYTE        bColorCount;        //图像颜色数低8位
        BYTE        bReserved;                //保留
        union
        {
                struct
                {
                        INT16        wCursorX;                //光标中心X
                        INT16        wCursorY;                //光标中心Y
                }AsCursor;
                struct
                {
                        WORD        wPlanes;                //位面数
                        WORD        wBitCount;                //颜色位数
                }AsIcon;
        };
        DWORD        dwBytesInRes;        //图像大小
        DWORD        dwImageOffset;        //图像位置
};之后的数据就是ICO、CUR文件包含的位图图像了。这些图像都是以BMP位图的形式存在的——就像去掉了BMP文件头的BMP文件一样,结构如下说明:

[*]一个BITMAPINFOHEADER结构体。
[*]如果是索引颜色,这里有调色板,否则没有。
[*]位图数据。
[*]位图透明通道数据,如果位图不是32位的

其中BITMAPINFOHEADER结构体中的“高度”(即biHeight)的数值是“宽度”(即biWidth)的数值的两倍。如果位图不是32位位图(比如24位、16位、8位、4位、2位、1位等)则在位图数据后面有透明通道数据,这个透明通道数据的内容,相当于一个单色BMP文件的位图部分。

根据如上的资料,我们就能制作出一个ICO、CUR文件的编辑器了。不过我可不想造轮子,造一个功能像画图一样不支持透明通道的不爽,而造一个像PhotoShop一样的则感觉没啥意义。因此我干脆借助NASM汇编的平坦模型原理,编写了一个“脚本”,用NASM编译一下它,就能得到一个ICO、CUR文件,原理是将BMP位图合并到位图里。
想必大家可能失望了……我可不想用汇编语言造一个这样的画图器轮子啊!只是利用了NASM的特性了而已,把它当作组装文件的“脚本解释器”了而已。
我这套工具主要就三个文件:
makeicon.asm
makeicon.inc
makeicon.bat

用法:将位图文件准备好,放到指定位置(比如这个工具的文件夹)

其中文件名以“mask”结尾的BMP文件是黑白位图(所谓“单色位图”),作用是定义位图的透明通道,黑色表示对应像素不透明,白色表示透明。
然后我们来编写makeicon.asm吧。需要的宏已经在makeicon.inc提供了。大家可以看看makeicon.inc的内容:;==============================================================================
;作者:0xAA55
;网站:http://www.0xaa55.com/
;请注明原作者信息,否则视为侵权。
;------------------------------------------------------------------------------
%ifndef        _ICON_CURSOR_FILE_GEN_
%define        _ICON_CURSOR_FILE_GEN_

;==============================================================================
;ICO、CUR文件结构:
;1个文件信息头
;N个图像信息头
;然后是N个BMP(BITMAPINFOHEADER、调色板、位图)
;BITMAPINFOHEADER的高的值是宽的两倍。
;如果不是32位的位图,则尾部附带单色位图透明数据
;------------------------------------------------------------------------------

;==============================================================================
;BMP文件头
;------------------------------------------------------------------------------
struc BMFH;BITMAPFILEHEADER
.bfType                resw 1
.bfSize                resd 1
.bfReserved1        resw 1
.bfReserved2        resw 1
.bfOffBits        resd 1
.Size:
endstruc

;==============================================================================
;BMP信息头
;------------------------------------------------------------------------------
struc BMIF;BITMAPINFOHEADER
        .biSize                                resd 1
        .biWidth                        resd 1
        .biHeight                        resd 1
        .biPlanes                        resw 1
        .biBitCount                        resw 1
        .biCompression                resd 1
        .biSizeImage                resd 1
        .biXPelsPerMeter        resd 1
        .biYPelsPerMeter        resd 1
        .biClrUsed                        resd 1
        .biClrImportant                resd 1
        .Size:
endstruc

;==============================================================================
;调色板项
;------------------------------------------------------------------------------
struc RGBQ
        .R        resb 1
        .G        resb 1
        .B        resb 1
        .X        resb 1
        .Size:
endstruc

;==============================================================================
;宏:FileHeader
;用法:用在汇编文件最开始的地方,定义ICO或CUR文件的文件头。
;参数:FT_Ico或FT_Cur,指明文件格式。
;------------------------------------------------------------------------------
%define        FT_Ico        1
%define FT_Cur        2

%macro FileHeader 1
        segment .data align=1
        segment .text align=1
        %define FT_FileType %1
        %assign NumImages 0
        dw 0
        dw %1
        dw NbImages
%endmacro

;宏:_bitmap_pitch
;用于计算位图每行字节数
%define _bitmap_pitch(w,b) ((((w) * (b) - 1) / 32 + 1) * 4)

;==============================================================================
;宏:IncludeBMP
;参数:
;如果是图标:
;    尺寸,颜色数,位面数,颜色位数,BMP文件路径
;
;如果是光标:
;    尺寸,颜色数,位面数,颜色位数,焦点X,焦点Y,BMP文件路径
;
;如果BMP文件是索引颜色,则有第六个参数:作为透明通道的BMP文件路径,必须为单色位
;图。
;------------------------------------------------------------------------------
%macro IncludeBMP 5-6
        segment .text
        db (%1)&0xFF;宽度
        db (%1)&0xFF;高度
        db (1 << (%4))&0xFF;颜色数
        db 0                ;保留
       
        ;如果是图标,下面两个参数分别是位面数和颜色位数,如果是光标,则是焦点坐标
        %if FT_FileType == FT_Ico
                dw %3, %4
                %define Color_File %5
                %define Alpha_File %6
        %else
                dw %5, %6
                %define Color_File %7
                %define Alpha_File %8
        %endif
       
       
        dd %%ImageDataLen
        dd %%ImageData
       
        segment .data
        %%ImageData:
        dd BMIF.Size
        dd (%1)
        dd (%1) * 2 ;高的值是宽的两倍
        dw %3, %4
        dd 0 ;不能是压缩格式,也不能有位域
        dd (%1) * _bitmap_pitch(%1, %4) ; 实际大小
        ;从文件取打印尺寸
        incbin Color_File, BMFH.Size + BMIF.biXPelsPerMeter, 4 * 2
        %if (%4)<=8 ;调色板颜色
                dd 1 << (%4) ;使用的颜色数
                dd 1 << (%4) ;重要的颜色数
                incbin Color_File, BMFH.Size + BMIF.Size, 4 * (%2) ;插入调色板
                times (1 << (%4)) - (%2) dd 0 ;补全调色板颜色
                incbin Color_File, BMFH.Size + BMIF.Size + 4 * (%2), (%1) * _bitmap_pitch(%1, %4) ;按照实际大小插入位图
                incbin Alpha_File,BMFH.Size + BMIF.Size + 4 * 2 ;然后插入透明通道
        %else
                dd 0 ;无调色板数据
                dd 0
                incbin Color_File, BMFH.Size + BMIF.Size ;插入整张位图
        %endif
        %%ImageDataLen equ $-%%ImageData
       
        %assign NumImages NumImages+1
%endmacro

;==============================================================================
;宏:IncludePNG
;参数:
;如果是图标:
;    尺寸,PNG文件路径
;
;如果是光标:
;    尺寸,焦点X,焦点Y,PNG文件路径
;
;------------------------------------------------------------------------------
%macro IncludePNG 2-4
        segment .text
        db (%1)&0xFF;宽度
        db (%1)&0xFF;高度
        db 0                ;颜色数
        db 0                ;保留
       
        ;如果是图标,下面两个参数分别是位面数和颜色位数,如果是光标,则是焦点坐标
        %if FT_FileType == FT_Ico
                dw 1, 32
                %define Color_File %2
        %else
                dw %2, %3
                %define Color_File %4
        %endif
       
        dd %%ImageDataLen
        dd %%ImageData
       
        segment .data
        %%ImageData:
        incbin Color_File
        %%ImageDataLen equ $-%%ImageData
       
        %assign NumImages NumImages+1
%endmacro

;==============================================================================
;宏:FileEnd
;用法:用在文件结尾。
;------------------------------------------------------------------------------
%macro FileEnd 0
        NbImages equ NumImages
%endmacro

%endif可以从源码上看出这些宏的用法:
FileHeader:定义文件头的内容。
IncludeBMP:将BMP位图文件加入到ICO或CUR中,参数已经在源码中说明了。
IncludePNG:将PNG文件加入到ICO或CUR中。
FileEnd:做一些结尾工作。
为了缩小文件尺寸,我已经把icon16.bmp、icon32.bmp、icon48.bmp处理为256色(8位色)位图了。
因此当我们需要把刚才那些位图组合成一个ICO文件,我们只需要这样编写makeicon.asm就行了:%include"makeicon.inc"

FileHeader FT_Ico

;IncludeBMP 尺寸,颜色数,位面数,颜色位数,位图文件名[,透明通道文件名]
;如果不是图标,是光标,那么参数则应该是:
;IncludeBMP 尺寸,颜色数,位面数,颜色位数,焦点X,焦点Y,位图文件名[,透明通道文件名]
;如果不是32位色(或者0x100000000色)那么就必须要有透明通道文件,这里叫“掩码”。
;前两张位图实际颜色数是22,这是用PS看到的
IncludeBMP 16,22,1,8,"icon16x16.bmp","mask16x16.bmp"
IncludeBMP 32,22,1,8,"icon32x32.bmp","mask32x32.bmp"
;后面这张48x48的实际颜色数是256
IncludeBMP 48,256,1,8,"icon48x48.bmp","mask48x48.bmp"
;PNG格式的图标用不着啰嗦
IncludePNG 128,"icon128x128.png"

FileEnd边写好了以后,保存,然后双击makeicon.bat。哦?对了,我们必须要有nasm.exe这个编译器在系统里,它是一个著名的汇编器,非常屌。
NASM汇编器点此下载
之后把它放到这个源码文件夹,或者干脆放到%PATH%下使其随时都能使用(我就是这么做的)。

双击了makeicon.bat之后,它只显示了“请按任意键继续。。。”
那么让我们切克闹。

可以看到我们得到了makeicon.ico这个文件。是它是它就是它,我们的朋友小哪吒。
事实证明这个图标文件没有问题,一切正常,它的三个图像都能正常显示。


从某种程度上来说,我这个东西基本只依赖NASM这个汇编器(makeicon.bat这个文件也只是调用了nasm,给了一个很简单的命令行参数而已。)而nasm是开源的跨平台汇编器。因此我的这个BMP转图标的“脚本”也是跨平台的哦。要在安卓手机上可以通过安装DOSBox运行DOS版的NASM编译makeicon.asm文件得到图标。

BIN:没有
SRC:

0xAA55 发表于 2019-3-13 03:37:47

已更新。现在可以包含PNG文件到图标里了。
附件也更新了,欢迎下载。

0xAA55 发表于 2018-2-18 01:37:58

已更新:修复了索引颜色BMP位图如果不包含完整调色板的话生成的图标显示不正确的问题。
页: [1]
查看完整版本: 【汇编】使用NASM“编译”制作ICO、CUR文件