14.4 GDI 位图对象 (I)

本文深入探讨了Windows图形设备接口(GDI)中位图对象的概念,包括设备相关位图(DDB)、设备无关位图(DIB)的区别,如何创建、使用和加载位图资源,以及单色位图的创建方法。详细介绍了如何通过编程实现位图的显示与交互,适合Windows编程初学者和高级开发者深入理解位图在Windows应用中的应用。
摘要由CSDN通过智能技术生成

摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P522

        在本章前面曾提到 Windows 从 1.0 版就开始支持 GDI 位图对象。由于 Windows 3.0 引入了设备无关位图,因此 GDI 位图对象现在有时也叫做设备相关位图(device-dependent bitmap)或 DDB。我将尽量不用设备相关位图的全称,因为乍一看,这个词和设备无关位图很像。它的缩写 DDB 比较好,因为从视觉上更容易区别于 DIB。

        两种不同位图的存在给初次接触 Windows 3.0 和以后版本的程序员带来了许多迷惑。许多资深 Windows 程序员在准确理解 DIB 和 DDB 在某些方面有联系:DIB 可以被转换成 DDB,反之亦然(尽管有些信息会丢失)。但是,DIB 和 DDB 并不能相互替代;并且要展现同样的可视数据,也不是随便在它们中选一个那么简单。

        如果我们可以假设 DIB 使 DDB 成为了过去式,那问题就变得方便容易多了,但事实并非如此。DDB 在 Windows 上仍然占据着很重要的地位,尤其是在我们很在乎程序性能的时候

14.4.1  创建 DDB

        DDB 是 Windows 图形设备接口定义的若干图形对象(包括画笔、画刷、字体、图元文件和调色板)之一。这些图形对象在 GDI 模块内部存储,应用程序用数值句柄来引用它们。你可以用类型为 HBITMAP(“指向位图的句柄”)的变量存储一个指向 DDB 的句柄。例如,

HBITMAP hBitmap;
然后你 可以通过调用 DDB 创建函数之一(如 CreateBitmap)来取得句柄。这些函数在 GDI 内存中分配并初始化一些内存以存储关于位图和实际位图位的信息。应用程序并不直接访问此内存。此位图独立于任何设备环境。当程序用完位图后,应该删除位图:

DeleteObject (hBitmap);
如果在程序整个运行过程中都在使用 DDB,则可以在程序终止时用这种方法删除位图。

        CreateBitmap 函数的形式如下:

hBitmap = CreateBitmap(cx, cy, cPlanes, cBitsPixel, bits);
前两个参数是位图的宽度和高度,以像素为单位。第 3 个参数是颜色平面的数量,第 4 个参数是每个像素的位数。第 5 个参数指向一个对应于特定颜色格式的位数组。如果不想用像素位初始化 DDB,则可以把最后一个参数设成 NULL。像素位可以以后再设置。

        使用这个函数时,Windows 允许你根据自己的需要创建任何 GDI 的奇怪的类型。比如,假设想要一个位图,它有 7 个像素宽,9 个像素高,有 5 个颜色平面并且每个像素有 3 位,则通过以下函数:

hBitmap = CreateBitmap(7, 9, 5, 3, NULL);
Windows 便会爽快地给你一个有效的位图句柄。

        在这个函数调用期间,Windows 保存你传入函数的参数信息,并且为像素位分配内存。通过粗略计算,这个位图需要 7 乘以 9 乘以 5 再乘以 3,也即 945 位,即需要 118 个字节再加上一些零头。

        然而,在 Windows 给位图分配内存时,每个像素行都有偶数个字节。所以有如下代码:

iWidthBytes = 2 * ((cx * cBitsPixel + 15) / 16);
或者,C 程序员可能以如下这种格式来写:

iWidthBytes = (cx * cBitsPixel + 15) & ~15) >> 3;
所以,分配给 DDB 的内存如下:

iBitmapBytes = cy * cPlanes * iWidthBytes;
在我们的例子中,iWidthBytes 是 4 个字节,因此 iBitmapBytes 是 180 个字节。

        现在, 位图有 5 个颜色平面,每个像素有 3 个颜色位,这意味着什么?这其实并没有很多含义,甚至都没有实用价值。函数调用让 GDI 分配一些内部内存,这个内存有特定的组织方式,但是这并不意味着什么,你也没法用这个位图做任何有意义的工作。

        事实上,调用 CreateBitmap 函数时,你只能按照两种方式设置参数:

  • cPlanes 和 cBitsPixel 值都等于 1(指明一个单色位图)。
  • cPlanes 和 cBitsPixel 值等于某个设备环境中的值,这个值可以通过用 PLANES 和 BITSPIXEL 索引调用 GetDeviceCaps 函数得到。

        在更加现实的情况下,将知识针对第一情况调用 CreateBitmap。对于第二种情况,可以用 CreateCompatibleBitmap 来化繁为简:

hBitmap = CreateCompatibleBitmap (hdc, cx, cy);
该函数创建一个与设备兼容的位图,该设备的设备环境句柄由第一个参数给出。CreateCompatibleBitmap 使用设备环境句柄获得 GetDeviceCaps 返回的信息,然后把这些信息传给 CreateBitmap。除了和真实的设备环境有相同的内存组织方式外,DDB 在其他方面和设备环境无关。

        CreateDiscardableBitmp 函数有和 CreateCompatibleBitmap 同样的参数,而且在功能上它们是相同的。在 Windows 早期版本中,CreateDiscardableBitmap 创建一个位图,如果内存不多时,Windows 可以从内存中释放此位图。此后程序必须重新创建位图

        第三个位图创建函数是 CreateBitmapIndirect,如下所示:       

hBitmap = CreateBitmapIndirect (&bitmap);
其中 bitmap 是 BITMAP 类型的结构。BITMAP 结构定义如下:

typedef struct _tagBITMAP
{
    LONG      bmType;            // set to 0
    LONG      bmWidth;           // width in pixels
    LONG      bmHeight;          // height in pixels
    LONG      bmWidthBytes;      // width of row in bytes
    WORD      bmPlanes;          // number of color planes
    WORD      bmBitsPixel;       // number of bits per pixel
    LPVOID    bmBits;            // pointer to pixel bits
}
BITMAP, * PBITMAP;

        在调用 CreateBitmapIndirect 函数时,不需要设置 bmWidthBytes 字段。Windows 会计算这个字段值。还可以把 bmBits 字段设为 NULL 或设为像素位的地址来初始化位图。

        BITMAP 结构也用在 GetObject 函数中。首先定义一个 BITMAP 类型的结构:

BITMAP bitmap;
然后如下调用该函数:

GetObject(hBitmap, sizeof(BITMAP), &bitmap);
Windows 会用位图的有关信息来填充 BITMAP 结构的字段。但是,bmBits 字段将会等于 NULL。

        最终应调用 DeleteObject 来销毁在程序中创建的所有位图。

14.4.2  位图的位

        用 CreateBitmap 或 CreateBitmapIndirect 函数创建设备相关 GDI 位图对象时,可以给出指向位图像素位的指针。也可以不初始化位图。Windows 提供了两个能在位图被创建后取得和设置位图像素位的函数。

        要设置像素位,调用以下函数即可:

SetBitmapBits (hBitmap, cBytes, &bits);
GetBitmapBits 函数的语法形式也差不多,如下所示:

GetBitmapBits (hBitmap, cBytes, &bits);
在上面两个函数里,cBytes 代表要复制的字节数,bits 是大小至少为 cBytes 的内存缓冲区。

        DDB 里的像素位从第一行开始排列。如前所述,每行有偶数个字节。除了这一点,我没什么其他要说的。如果位图时单色位图,即位图只有一个颜色平面,每个像素只有一位,那么每个像素非 1 即 0。每行最左边的像素是这行里第一个字节的最高位。等我们搞清楚怎样显示 DDB 后,我们会在本章稍后部分创建一个单色 DDB。

        对于非单色位图,你应该尽量避免需要指定像素位具体值的情况。比如,假设 Windows 在 8 位 VGA 上运行。你调用了  CreateCompatibleBitmap 函数。通过 GetDeviceCaps 函数,你得知你在一个有一个颜色平面、每个像素 8 位的设备上运行程序。每个像素用一个字节存储。但是,值为 0x37 的像素意味着什么呢?很明显,它代表某种颜色,但这是什么颜色呢?

        我们所说的像素其实并代表某种特定的颜色。它只是一个值DDB 并没有颜色表。本质的问题就是:当 DDB 显示在屏幕上时,像素是什么颜色的?像素必须表示某种颜色,但是究竟是什么颜色呢?被显示的像素对应的是视频卡的颜色查找表中索引为 0x37 的 RGB 颜色这对于开发人员来说就是设备相关

        然而,不要因为我们不知道像素值表示什么就认为非单色 DDB 一无是处。很快我们就会看到它们是多么有用。在第 15 章,我们将看到 SetBitmapBits 和 GetBitmapBits 被更加有用的 SetDIBits 和 GetDIBits 函数取代。

        总之,基本的规则如下:对于彩色 DDB,不应该用 CreateBitmap 或 CreateBitmapIndirect 或 SetBitmapBits 来设置像素位。只有对单色 DDB,才可以放心地设置像素位。(如果通过调用 GetBitmapBits 取得了有同样格式的另一个 DDB 的像素位,那么可以作为一种例外。)

        在继续下面的介绍之前,我想提一下 SetBitmapDimensionEx 和 GetBitmapDimensionEx 函数。这些函数允许你设置(并取得)以 0.1 mm 为单位的位图的度量尺寸。这个信息和位图定义一起存储在 GDI 里,但是不用于其他操作。这仅仅是一个标签,让你可以把度量尺寸和 DDB 联系起来。

14.4.3  内存设备环境

        我们要探讨的下一个概念是 内存设备环境(memory device context)。 使用 GDI 位图对象时,需要用到内存设备环境

        通常,设备环境对应于特定的图形输出设备(比如视频显示或打印机)和设备驱动。内存设备环境只存在于内存。它不是一个真实的图形输出设备,但是用大家的话说,它和特定的真实设备“兼容”。

        要创建一个内存设备环境,必须有一个对应于真实设备的设备句柄。假设句柄是 hdc,则可以像下面这样创建一个内存设备环境:

hdcMem = CreateCompatibleDC (hdc);
通常这个函数调用比这条语句还要简单。如果把参数设成 NULL,Windows 将创建于视频显示兼容的内存设备环境。应用程序创建任何内存设备环境后,最终镀硬铬调用 DeleteDC 销毁它。

        跟真实的点阵设备一样,内存设备也有一个显示表面。然而,这个显示表面最开始很小——它是单色的,有 1 个像素宽,1 个像素高。显示表面仅仅是 1 位。

        当然,对于这个 1 位的显示表面,你做不了太多事。所以,有实际意义的下一步就是增大显示表面。为此,可以把 GDI 位图对象选入内存设备环境:

SelectObject (hdcMem, hBitmap);
向设备环境中引入画笔、画刷、字体、区域和调色板也是使用同样的函数。但是, 在各类设备环境中,只有内存设备环境才可以选入位图。(如果有需要,也可以把其他 GDI 对象选进内存设备环境。)

        在把位图选进内存设备环境时,SelectObject 仅仅在以下两种情况才有效:位图是单色位图,或者此位图与和内存设备环境兼容的设备有同样的颜色组织。这就是为什么创建一个奇怪的 DDB(比如,位图有 5 个颜色平面,每个像素有 3 位)没有什么用的原因。

        通过前面的描述,我们知道,在调用 SelectObject 函数之后,DDB 成为内存设备环境的显示表面。有了这个内存设备环境,你可以做几乎任何你可以在真实设备环境上执行的操作。例如,如果用 GDI 绘图函数在内存设备环境上绘图,图像就会画在位图上。这点很有用。也可以调用 BitBlt,把内存设备环境作为源环境,视频设备环境作为目标环境。可以这样在显示器上画位图。另外,还可以把视频设备环境作为源环境,把内存设备环境作为目标环境,调用 BitBlt,把图像从屏幕显示到位图上。我们将探讨所有这些可能性。

14.4.4  加载位图资源

        除了各种各样的位图创建函数,取得 GDI 位图对象句柄的另一个方法是使用 LoadBitmap 函数有了这个函数,便无需担心位图格式。就像创建图标或鼠标指针一样,只需要简单创建一个位图,并把它作为程序的一项资源即可。LoadBitmap 函数和 LoadIcon 及 LoadCursor 函数语法相同:

hBitmap = LoadBitmap(hInstance, szBitmapName);
如果想加载系统位图,第一个参数可以设成 NULL。系统位图是 Windows 可视界面的小部件,比如关闭框和选中标记,它们的标识符以字母 OBM 开头。如果位图和某整数标识符而不是名称相关联,则第二个参数可以使用 MAKEINTRESOURCE 宏。所有用 LoadBitmap 加载的位图最终应该用 DeleteObject 删除。

        如果位图资源是单色位图,从 LoadBitmap 返回的句柄将代表单色位图对象。如果位图资源非单色,LoadBitmap 返回的句柄引用的则是 GDI 位图对象,且此对象和程序正在其上运行的视频显示有相同的颜色组织。因此,该位图总是与视频显示兼容,并且可以被选进和视频显示兼容的内存设备环境。现在不必为 LoadBitmap 调用期间背后的颜色转换而烦恼。我们会在第 15 章解释颜色是怎样转换的。

        BRICKS1 程序展示了怎样加载一个小的单色位图资源。这个位图本身看上去不完全像砖块,但是如果横向和纵向重复绘制这个位图,看起来就好像一面砖墙。

/*------------------------------------------
	BRICKS1.C -- LoadBitmap Demonstration
				(c) Charles Petzold, 1998
------------------------------------------*/

#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance,
	PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT("Bricks1");
	HWND		 hwnd;
	MSG			 msg;
	WNDCLASS	 wndclass;

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_INFORMATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"),
			szAppName, MB_ICONERROR);

		return 0;
	}

	hwnd = CreateWindow(szAppName, TEXT("LoadBitmap Demo"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HBITMAP	hBitmap;
	static int		cxClient, cyClient, cxSource, cySource;
	BITMAP			bitmap;
	HDC				hdc, hdcMem;
	HINSTANCE		hInstance;
	int				x, y;
	PAINTSTRUCT		ps;

	switch (message)
	{
	case WM_CREATE:
		hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

		hBitmap = LoadBitmap(hInstance, TEXT("Bricks"));

		GetObject(hBitmap, sizeof(BITMAP), &bitmap);

		cxSource = bitmap.bmWidth;
		cySource = bitmap.bmHeight;
		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		return 0;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		hdcMem = CreateCompatibleDC(hdc);
		SelectObject(hdcMem, hBitmap);

		for (y = 0; y < cyClient; y += cySource)
			for (x = 0; x < cxClient; x += cxSource)
			{
				BitBlt(hdc, x, y, cxSource, cySource, hdcMem, 0, 0, SRCCOPY);
			}

		ReleaseDC(hwnd, hdcMem);
		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		DeleteObject(hBitmap);
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}
BRICKS1.RC (excerpts)

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

/
//
// Bitmap
//

BRICKS                  BITMAP                  "Bricks.bmp"
BRICKS.BMP


        在 Visual C++ Developer Studio 中创建位图时,指定位图的高度和宽度为 8 个像素,且为单色位图,并给位图取名“Bricks”。BRICKS1 程序在 WM_CREATE 消息处理期间加载位图,并且用 GetObject 确定它的像素规模(这样即使位图不是 8 个像素的正方形,程序仍然可以运行)。BRICKS1 程序稍后在 WM_DESTROY 消息处理期间删除了位图句柄。

        处理 WM_PAINT 消息时,BRICKS1 创建一个与显示器兼容的内存设备环境,并且把位图选进该环境。接下来就是从内存设备环境到客户区设备环境的一系列 BitBlt 调用。之后程序删除内存设备环境。图 14-6 下式了程序的运行结果。

图 14-6  BRICKS1 的显示

        顺便提一下,Developer Studio 创建的 BRICKS.BMP 文件是一个设备无关位图。你也可以在 Developer Studio 中试着创建一个彩色的 BRICKS.BMP 文件(采用你选择的任何一种颜色)并且确保一切顺序。

        我们已经知道 DIB 可以被转换成与视频显示兼容的 GDI 位图对象。第 15 章我们将介绍怎样转换。

14.4.5  单色位图格式

        如果要处理小的单色图像,那么并不一定要把它们创建成资源。与彩色位图对象不同,单色位图对象格式相对比较简单,而且几乎可以直接从你想要创建的图像导出。例如,假设你想创建一个如下所示的位图:

        你可以写下一系列直接对应于网格的位(0 代表黑色,1 代表白色)。在从左到右读这些位时,可以给每 8 位赋一个 16 进制字节。如果位图的宽度不是 16 的倍数,在右边添零以得到偶数个字节

        这个位图宽度为 20 个像素,高度为 5 个扫描行,即宽度为 4 个字节。可以用以下语句给这个位图创建 BITMAP 结构:

static BITMAP bitmap = { 0, 20, 5, 4, 1, 1 };
并用一个 BYTE 数组存储这些位:

static BYTE bits [] = { 0x51, 0x77, 0x10, 0x00,
                        0x57, 0x77, 0x50, 0x00,
                        0x13, 0x77, 0x50, 0x00,                       
                        0x57, 0x77, 0x50, 0x00,
                        0x51, 0x11, 0x10, 0x00 };
用 CreateBitmapIndirect 函数创建这个位图需要两条语句:

bitmap.bmBits = (PSTR) bits;
hBitmap = CreateBitmapIndirect (&bitmap);
另一个方法是:

hBitmap = CreateBitmapIndirect (&bitmap);
SetBitmapBits (hBitmap, sizeof(bits), bits);
也可以用一条语句创建这个位图:

hBitmap = CreateBitmap(20, 5, 1, 1, bits);

        BRICKS2 程序用上述技术直接创建了砖块位图,而没有用到资源文件。

/*------------------------------------------
	BRICKS2.C -- CreateBitmap Demonstration
		    (c) Charles Petzold, 1998
------------------------------------------*/

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance,
	PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT("Bricks2");
	HWND		 hwnd;
	MSG			 msg;
	WNDCLASS	 wndclass;

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_INFORMATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"),
			szAppName, MB_ICONERROR);

		return 0;
	}

	hwnd = CreateWindow(szAppName, TEXT("CreateBitmap Demo"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static BITMAP	bitmap = { 0, 8, 8, 2, 1, 1 };
	static BYTE		bits[8][2] = { 0xFF, 0, 0x0C, 0, 0x0C, 0, 0x0C, 0,
								   0xFF, 0, 0xC0, 0, 0xC0, 0, 0xC0, 0 };

	static HBITMAP	hBitmap;
	static int		cxClient, cyClient, cxSource, cySource;
	HDC				hdc, hdcMem;
	int				x, y;
	PAINTSTRUCT		ps;

	switch (message)
	{
	case WM_CREATE:
		bitmap.bmBits = bits;
		hBitmap = CreateBitmapIndirect(&bitmap);
		cxSource = bitmap.bmWidth;
		cySource = bitmap.bmHeight;
		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		return 0;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		hdcMem = CreateCompatibleDC(hdc);
		SelectObject(hdcMem, hBitmap);

		for (y = 0; y < cyClient; y += cySource)
			for (x = 0; x < cxClient; x += cxSource)
			{
				BitBlt(hdc, x, y, cxSource, cySource, hdcMem, 0, 0, SRCCOPY);
			}

		DeleteDC(hdcMem);
		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		DeleteObject(hBitmap);
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}

        你或许想尝试用类似方法创建彩色位图。比如,如果你的视频显示在 256 色模式下运行,你可以用本章前面的色彩定义表格对每个彩色砖块的像素进行定义。但是,这段程序在其他任何视频模式下都将无法正确运行。以设备无关的方式处理彩色位图需要用到第 15 章讨论的 DIB。

14.4.6  位图画刷

        BRICKS 系列的最后一个程序是 BRICKS3。乍一看这个程序,你可能会有这样的反应:“和位图有关的代码在哪里?”

/*---------------------------------------------------
	BRICKS3.C -- CreatePatternBrush Demonstration
		    (c) Charles Petzold, 1998
---------------------------------------------------*/

#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance,
	PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT("Bricks3");
	HBITMAP		 hBitmap;
	HBRUSH		 hBrush;
	HWND		 hwnd;
	MSG			 msg;
	WNDCLASS	 wndclass;

	hBitmap = LoadBitmap(hInstance, TEXT("Bricks"));
	hBrush = CreatePatternBrush(hBitmap);
	DeleteObject(hBitmap);

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_INFORMATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = hBrush;
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"),
			szAppName, MB_ICONERROR);

		return 0;
	}

	hwnd = CreateWindow(szAppName, TEXT("CreatePatternBrush Demo"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}
BRICKS3.RC (excerpts)

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

/
//
// Bitmap
//

BRICKS                  BITMAP                  "Bricks.bmp"

        这段程序和 BRICKS1 使用了相同的 BRICKS.BMP,而且窗口看起来也一样。

        可以看出,窗口过程没做什么。BRICKS3 实际上把砖块图案作为窗口类的背景画刷来使用,背景画刷在 WNDCLASS 结构的 hbrBackground 字段定义。

        正如你现在猜想的一样,GDI 画刷是很小的位图,通常为 8 * 8 像素的正方形。要把一个位图变成画刷,可以调用 CreatePatternBrush,或者调用 CreateBrushIndirect 并把 LOGBRUSH 结构的 lbStyle 字段设置成 BS_PATTERN。位图至少要是 8 像素宽,8 像素高。如果更大,Windows 98 只取图的左上角作为画刷。而 Windows NT 则没有这个限制,它可以把整个位图都变成画刷。

        记住,画刷和位图都是 GDI 对象,所以应该在程序终止前删除任何创建的画刷和位图。基于位图创建画刷时,Windows 会复制一份位图的位信息,供画刷绘图时使用。在调用 CreatePatternBrush(或者 CreateBrushIndirect)之后,可以立即删除位图,这不会影响画刷。依次类推,还可以删除画刷,而不影响最初用来创建画刷的位图。请注意,BRICKS3 在创建画刷后删除了位图,而且在程序终止前删除了画刷。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值