技术宅的结界

 找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 1829|回复: 2
收起左侧

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

[复制链接]

1038

主题

2307

帖子

5万

积分

用户组: 管理员

一只技术宅

UID
1
精华
216
威望
286 点
宅币
17830 个
贡献
35606 次
宅之契约
0 份
在线时间
1705 小时
注册时间
2014-1-26
发表于 2015-1-24 03:18:30 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有帐号?立即注册→加入我们

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

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

文件信息头的结构:
[C] 纯文本查看 复制代码
struct ICONFILEHEADER		//文件信息头,一个ICO只有一个
{
	WORD	wReserved;		//保留,值为0
	WORD	wType;			//类型,1为ICO,2为CUR
	WORD	wCount;			//整个ICO文件的图像数
};
图像信息头的结构:
[C] 纯文本查看 复制代码
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
20150124022538.png
用法:将位图文件准备好,放到指定位置(比如这个工具的文件夹)
20150124022653.png
其中文件名以“mask”结尾的BMP文件是黑白位图(所谓“单色位图”),作用是定义位图的透明通道,黑色表示对应像素不透明,白色表示透明。
然后我们来编写makeicon.asm吧。需要的宏已经在makeicon.inc提供了。大家可以看看makeicon.inc的内容:
[Asm] 纯文本查看 复制代码
;==============================================================================
;作者: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就行了:
[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之后,它只显示了“请按任意键继续。。。”
那么让我们切克闹。
20150124031318.png
可以看到我们得到了makeicon.ico这个文件。是它是它就是它,我们的朋友小哪吒。
事实证明这个图标文件没有问题,一切正常,它的三个图像都能正常显示。
20150124031448.png 20150124031505.png 20150124031518.png

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

BIN:没有
SRC: makeicon.zip (52.55 KB, 下载次数: 4)

1038

主题

2307

帖子

5万

积分

用户组: 管理员

一只技术宅

UID
1
精华
216
威望
286 点
宅币
17830 个
贡献
35606 次
宅之契约
0 份
在线时间
1705 小时
注册时间
2014-1-26
 楼主| 发表于 2018-2-18 01:37:58 | 显示全部楼层
已更新:修复了索引颜色BMP位图如果不包含完整调色板的话生成的图标显示不正确的问题。

1038

主题

2307

帖子

5万

积分

用户组: 管理员

一只技术宅

UID
1
精华
216
威望
286 点
宅币
17830 个
贡献
35606 次
宅之契约
0 份
在线时间
1705 小时
注册时间
2014-1-26
 楼主| 发表于 2019-3-13 03:37:47 | 显示全部楼层
已更新。现在可以包含PNG文件到图标里了。
附件也更新了,欢迎下载。

本版积分规则

QQ|申请友链||Archiver|手机版|小黑屋|技术宅的结界 ( 滇ICP备16008837号|网站地图

GMT+8, 2019-5-20 13:16 , Processed in 0.119052 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表