biYPelsPerMeter
说明垂直分辨率,用象素/米表示 biClrUsed
说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)
biClrImportant
说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要。 3. 彩色表
彩色表包含的元素与位图所具有的颜色数相同,象素的颜色用RGBQUAD结构来定义。对于24-位真彩色图像就不使用彩色表(同样也包括16位、和32位位图),因为位图中的RGB值就代表了每个象素的颜色。彩色表中的颜色按颜色的重要性排序,这可以辅助显示驱动程序为不能显示足够多颜色数的显示设备显示彩色图像。RGBQUAD结构描述由R、G、B相对强度组成的颜色,定义如下:
typedef struct tagRGBQUAD { /* rgbq */ BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD; 其中:
rgbBlue指定蓝色强度 rgbGreen 指定绿色强度 rgbRed 指定红色强度 rgbReserved 保留,设置为0 4. 位图数据
紧跟在彩色表之后的是图像数据字节阵列。图像的每一扫描行由表示图像象素的连续的字节组成,每一行的字节数取决于图像的颜色数目和用象素表示的图像宽度。扫描行是由底向上存储的,这就是说,阵列中的第一个字节表示位图左下角的象素,而最后一个字节表示位图右上角的象素。(只针对与倒向DIB,如果是正向DIB,则扫描行是由顶向下存储的),倒向DIB的原点在图像的左下角,而正向DIB的原点在图像的左上角。
34
同时,每一扫描行的字节数必需是4的整倍数,也就是DWORD对齐的。如果你想确保图像的扫描行DWORD对齐,可使用这个代码:
(((width*biBitCount)+31)>>5)<<2
35
3. 开发工具及开发平台简介
3.1. 理解VC工程
Visual C++作为一种程序设计语言,它同时也是一个集成开发工具,提供了软件代码自动生成和可视化的资源编辑功能。在使用Visual C++开发应用程序的过程中,系统为我们生成了大量的各种类型的文件,在本节中将要详细介绍Visual C++中这些不同类型的文件分别起什么样的作用,在此基础上对Visual C++如何管理应用程序所用到的各种文件有一个全面的认识。
首先要介绍的是扩展名为.dsw的文件类型,这种类型的文件在VC中是级别最高的,称为Workspace文件。在VC中,应用程序是以Project的形式存在的,Project文件以.dsp扩展名,在Workspace文件中可以包含多个Project,由Workspace文件对它们进行统一的协调和管理。
与.dsw类型的Workspace文件相配合的一个重要的文件类型是以.opt为扩展名的文件,这个文件中包含的是在Workspace文件中要用到的本地计算机的有关配置信息,所以这个文件不能在不同的计算机上共享,当我们打开一个Workspace文件时,如果系统找不到需要的.opt类型文件,就会自动地创建一个与之配合的包含本地计算机信息的.opt文件。
上面提到的Project文件的扩展名是.dsp,这个文件中存放的是一个特定的工程,也就是特定的应用程序的有关信息,每个工程都对应有一个.dsp类型的文件。
以.clw为扩展名的文件是用来存放应用程序中用到的类和资源的信息的,这些信息是VC中的ClassWizard工具管理和使用类的信息来源。
对应每个应用程序有一个readme.txt文件,这个文件中列出了应用程序中用到的所有的文件的信息,打开并查看其中的内容就可以对应用程序的文件结构有一个基本的认识。
在应用程序中大量应用的是以.h和.cpp为扩展名的文件,以h为扩展名的文件称为头文件。以.cpp为扩展名的文件称为实现文件,一般说来.h为扩展名的文件与.cpp为扩展名的文件是一一对应配合使用的,在.h为扩展名的文件中包含的主要是类的定义,而在.cpp为扩展名的文件中包含的主要是类成员函数的实现代码。
在应用程序中经常要使用一些位图、菜单之类的资源,VC中以.rc为扩展名的文件称为资源文件,其中包含了应用程序中用到的所有的Windows资源,要指出的一点
36
是.rc文件可以直接在VC集成环境中以可视化的方法进行编辑和修改。
最后要介绍的是以.rc2为扩展名的文件,它也是资源文件,但这个文件中的资源不能在VC的集成环境下直接进行编辑和修改,而是由我们自己根据需要手工地编辑这个文件。
对于以.ico,.bmp等为扩展名的文件是具体的资源,产生这种资源的途径很多。使用.rc资源文件的目的就是为了对程序中用到的大量的资源进行统一的管理。 3.2. DLL的介绍
3.2.1. 静态链接和动态链接
当前连接的目标代码(.obj)如果引用了一个函数却没有定义它,链接程序可能通过两种途径来解决这种从外部对该函数的引用:
静态链接
链接程序搜索一个或者多个库文件(标准库.lib),直到在某个库中找到了含有所引用函数的对像模块,然后链接程序把这个对像模块拷贝到结果可执行文件(.exe)中。链接程序维护对该函数的所有引用,使它们指向该程序中现在含有该函数拷贝的地方。
动态链接
链接程序也是搜索一个或者多个库文件(输入库.lib),当在某个库中找到了所引用函数的输入记录时,便把输入记录拷贝到结果可执行文件中,产生一次对该函数的动态链接。这里,输入记录不包含函数的代码或者数据,而是指定一个包含该函数代码以及该函数的顺序号或函数名的动态链接库。
当程序运行时,Windows装入程序,并寻找文件中出现的任意动态链接。对于每个动态连接,Windows装入指定的DLL并且把它映射到调用进程的虚拟地址空间(如果没有映射的话)。因此,调用和目标函数之间的实际连接不是在链接应用程序时一次完成的(静态),相反,是运行该程序时由Windows完成的(动态)。这种动态连接称为加载时动态链接。还有一种动态连接方式下面会谈到。 3.2.2. 调用约定
调用约定(Calling Convention)决定一下内容:函数参数的压栈顺序,由调用者还是被调用者把参数弹出栈,以及产生函数修饰名的方法。MFC支持一下调用约定:
1._cdecl
按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于“C”函数或者变量,修饰名是在函数名前面加下划线。对于“C++”函数,有所不同。如函数void test(void)
37
的修饰名是_test;对于不属于一个类的“C++”全局函数,修饰名是?test@@ZAXXZ。
这是MFC缺省调用约定。由于是调用者负责把参数弹出栈,所以可以给函数定义个数不定的参数,如printf函数。
2._stdcall
按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。对于“C”函数或者变量,修饰名以下划线为前缀,再是函数名,然后是符号“@”及参数的字节数,如函数int func(int a,double b)的修饰名是_func@12。对于“C++”函数,则有所不同。所有的Win32 API函数都遵循该约定。
_fastcall
头两个DWORD类型或者占更少字节的参数被放入ECX和EDX寄存器,其他剩下的参数按从右至左的顺序压入栈。由被调用者把参数弹出栈,对于“C”函数或者变量,修饰名以“@”为前缀,然后是函数名,接着是符号“@”及参数的字节数,如函数int func(int a,double b)的修饰名是@func@12。对于“C++”函数,有所不同。未来的编译器可能使用不同的寄存器来存放参数。 3.2.3. DLL的封装方法
步骤如下:
创建一个新的VC工程,类型为MFC APPWizard(dll),并选择MFC静态链接方式。 为项目设置静态链接库,将所需SDK的.cpp,.h,.lib文件加载到Visual C++项目中。 修改.def文件,在“EXPORTS”行下方列举所需要封装的API函数。
编写API函数。在项目的主cpp文件中编写各API函数的代码。这里要注意在文件头声明必要的.h文件,API函数名前要加关键字,否则调用会出错。
编译,调试直至获得无误的.dll文件。
38