问题
显示位图似乎相当简单。在应用程序的资源中添加一张漂亮的位图,使用函数 LoadBitmap 将位图装 入内存,然后将位图选入设备描述表,再使用函数 BitBlt 便可显示出位图。但是这样显示的位图和目标环境或许有所不同。比如在256色的环境里,即使你载入的是256色位图,也会按屏幕颜色(一般为24位色)画出,那应该怎么按位图原来的颜色画出呢?
方法
要显示位图,首先必须了解 Windows 中位图的概念。位图有两种基本格式:设备相关位图(DDB) 和设备无关位图(DIB)。在设备相关位图中,只有大小信息和位图数据,没有位图的设备分辨率信息,也 没有位图的颜色信息。
在 Windows 3.x 中引入了一种新的位图格式:设备无关位图。这种格式的位图包含位图的分辨率和 颜色信息,资源编辑器创建的 .BMP 文件便是此种位图格式。
在编译应用程序时,开发环境将资源数据编联在应用程序末,位图以设备无关位图的格式编联,包含所 有颜色的调色板信息。但 Windows API 中的位图句柄(HBITMAP)所指的位图为设备相关位图,设备相关 位图只有在选入设备描述表后才能操作,设备描
述表为位图提供颜色、大小和分辨率信息。
当调用 API 函数 LoadBitmap 时,Windows 将设备无关位图装入内存,并自动转换成 4 位(16 色)的设备相关位图,所以会有颜色信息的丢失。在本节的例子中将调用更底层的函数,自己将位图资源 装入内存,并建立指针以便应用程序可以存取,接着调
用 GDI 函数将设备无关的位图转换成设备相关的 位图,以便于显示。使用此方法,便
可保留所有需要的颜色信息。
本文作者特别提示:
在高版本的 Visual C++ 中,如果你的屏幕颜色设置大于 256 色,则生成的应用程序使用函数 LoadBitmap 可以显示大于 16 色以上的位图。
在本节的例子完成以后,如果例子程序中两个菜单项的显示没有区别,你可以试着将你的屏幕的颜色 设为 256 色试一试。
步骤
按照下列步骤实现一个例子程序。运行此例子程序,将显示出一个有菜单的简单窗口,从菜单 File 中 选择菜单项 LoadBitmap,例子程序便调用 API 函数 LoadBitmap 装入位图资源,接着将其选入设备描 述表并显示在窗口中。
从菜单 File 中选择菜单项 LoadResource,例子程序将调用更底层的 API 函数来装入位图资源并 将其转换成设备相关位图。你可以比较一下这两种位图显示的效果。注意,如果例子程序是运行在单色显示 器的系统上,或显示器的驱动程序只支持 16 色,那么这两个菜单项的显示将没有任何区别。
实现例子程序的具体步骤如下:
1.在 Visual C++ 中,创建一个新的工程,选择工程类型为 Win32 Application。
2.进入 App Studio,为刚才新建的工程插入资源文件。
3.在资源中添加一个主菜单。在主菜单中添加一个下拉菜单,命名菜单为 &File。在 File 菜单中添 加三个菜单项,分别命名三个菜单项为 Use &LoadBitmap、Use &DIB Functions 和 E&xit,菜单项 ID 对应分别为 ID_USELOADBITMAP、ID_USEDIBFUNCS 以及 ID_EXIT。在菜单项 Exit 与 Use DIB Functions 之间添加一条分隔线。
4.在资源中插入一幅 256 色的位图文件,要确保位图不是用 RLE(run length encoding) 压缩算 法保存的,因为有的显示驱动程序不支持此种压缩算法。
5.在工程中插入 C++ 源文件,命名文件为 256Color.cpp。
6.在文件 256Color.cpp 中键入下列代码。代码包含了本例子程序所需要的 Windows 头文件以及 包含资源标识符定义的文件 resource.h,另外定义了预处理符号 STRICT,用来表示对 Windows 应用 程序中使用的窗口句柄、菜单句柄和位图句柄进行严格的类型检查。
7.在同一源文件中添加下列定义。MainWindowClassName 定义了此例子程序使用的窗口类的名字, 其它的句柄和指针是全局变量,用在整个例子程序中,初始化为零或 NULL 是为了避免以后出现错误。
8.在源文件中添加下列函数。函数 CreateDIBPalette 用来从设备无关位图中取出颜色信息,创建 逻辑调色板,创建和显示设备相关位图时使用此调色板。传送给函数的是 BITMAPINFOHEADER 结构的指 针,指向内存中的设备无关位图。此函数返回值是创建
的逻辑调色板的句柄,如果此函数失败,则句柄为 NULL。
9.在源文件中添加下列代码。函数 ConvertDIBToDDB 用来把设备无关位图转换成设备相关位图,而 设备相关位图可以被快速地显示在屏幕上。此函数的实现方法为:首先建立一个内存设备描述表,同显示位 图的屏幕相兼容,接着在设备描述表中创建设备相关位图,大小要同设备无关位图的大小一样,在设备描述 表中调入前面刚创建的逻辑调色板,并实现调色板,最后,使用函数 etDIBits 拷贝位图,按要求转换颜 色。DIB_RGB_COLORS 表示设备无关位图中的颜色表的颜色是 RGB 值,而不是系统调色板的索引。
10.在同一源文件中添加函数 LoadBitmapResource。此函数联合使用函数 FindResource、LoadResource 和 LockResource 来将需要的位图资源装入内存,返回一个 BITMAPINFOHEADER 结构的指针,指向内存中的设备无关位图的开始处。
使用这些函数应该注意两件事情:第一件事情是资源是只读的。尽管函数 LoadResource 返回一个 指向资源的指针,但任何修改资源的企图都会引起保护错误,要修改位图需要创建一个拷贝,并且只能修 改拷贝。
应该注意的第二件事情是 Windows 9x 和 Windows NT 跟踪应用程序使用的资源,当应用程序结 束后,Windows 负责进行释放,所以应用程序可以不考虑资源的释放。
11.在源文件中添加函数 Paint。每当 Windows 操作系统发送消息 WM_PAINT 给例子程序窗口 时,此函数将被调用,这时,位图已经转换成为内存中的设备相关位图和调色板,所以可以相当迅速地将 位图显示在幕上,将调色板选入当前屏幕的设备描述表并实现调色板,以便显示正确的图象颜色,接着 将位图选入内存设备描述表,调用 API 函数 BitBlt 将位图显示在屏幕上。
12.现在已经键入了此例子程序的大部分代码,在下面几步中所添加的函数构成了本例子程序的框架。 下面的函数 MainWndProc 是一个回调函数,用来处理所有发送给例子程序窗口的消息。在本例子程序 中,需要响应三个菜单项:ID_USELOADBITMAP 对应菜单项 LoadBitmap,ID_USEDIBFUNCS 对应菜 单项 LoadResource,ID_EXIT 对应菜单项 Exit。当用户选择了菜单项 LoadResource 后,此函数 将调用前面刚创建的函数,装入位图,创建调色板,并将设备无关位图转换成设备相关位图,接着调用 InvalidateRect 刷新窗口,从而显示出新装入的位图。
当用户从菜单中选择了菜单项 LoadBitmap 后,例子程序将丢弃前面已装入的位图,调用 API 函 数 LoadBitmap 从资源中装入位图,Windows 将对位图中的颜色进行转换,生成 16 色设备相关位 图。
13.在源文件中添加函数 InitApplication。例子程序第一个实例运行时调用此函数,接着只有在窗口 类不再存在时才调用此函数,此函数为例子程序的主窗口初始化并登记窗口类。
14.添加函数 initInstance。例子程序的每个实例运行都将调用此函数,用来创建并显示此例子程序 的主窗口。
15.此例子程序的最后一段代码是函数 WinMain。此函数是 Windows 操作系统上的应用程序的入口 点,完成窗口类和窗口的初始化,接着循环处理消息直到例子程序结束。
16.编译并运行此例子程序。
注释
在 Windows 中显示 256 色的位图决不是一件简单的任务。本节的例子程序只适合显示静态的位 图,显示动画则效果不是很好,因为大量时间花费在逻辑调色板向物理调色板的转换上,以及将设备无 关位图中 RGB 值转换到设备相关位图中的调色板中,所以使处理速度非常缓慢。其他处理调色板的方 法将在以后的章节中讨论。