第十四章 位图和Bitblt(GDI 位图对象1)

GDI 位图对象

我在本章前面已提到过Windows从1.0开始就支持GDI位图对象。因为在Windows 3.0发表了设备无关位图,GDI位图对象有时也称为设备相关位图,或者DDB。我尽量不全部引用device-dependent bitmap的全文,因为它看上去与device-independent bitmap类似。缩写DDB会好一些,因为我们很容易把它与DIB区别开来。

对程序写作者来说,现存的两种不同型态的位图从Windows 3.0开始就更为混乱。许多有经验的Windows程序写作者都不能准确地理解DIB和DDB之间的关系。(恐怕本书的Windows 3.0版本不能澄清这个问题)。诚然,DIB和DDB在许多方面是相关的:DIB与DDB能相互转换(尽管转换程序中会丢失一些信息)。然而DIB和DDB是不可以相互替换的,并且不能简单地选择一种方法来表示同一个可视数据。

如果我们能假设说DIB一定会替代DDB,那以后就会很方便了。但现实并不是如此,DDB还在Windows中扮演着很重要角色,尤其是您在乎程序执行表现好坏时。

建立DDB

DDB是Windows图形设备接口的图形对象之一(其中还包括绘图笔、画刷、字体、metafile和调色盘)。这些图形对象储存在GDI模块内部,由应用程序软件以句柄数字的方式引用。您可以将DDB句柄储存在一个HBITMAP(「handle to a bitmap:位图句柄」)型态的变量中,例如:

HBITMAP hBitmap ;

然后通过呼叫DDB建立的一个函数来获得句柄,例如:CreateBitmap。这些函数配置并初始化GDI内存中的一些内存来储存关于位图的信息,以及实际位图位的信息。应用程序不能直接存取这段内存。位图与设备内容无关。当程序使用完位图以后,就要清除这段内存:

DeleteObject (hBitmap) ;

如果程序执行时您使用了DDB,那么程序终止时,您可以完成上面的操作。

CreateBitmap函数用法如下:

hBitmap = CreateBitmap (cx, cy, cPlanes, cBitsPixel, bits) ;

前两个参数是位图的宽度和高度(以图素为单位),第三个参数是颜色面的数目,第四个参数是每图素的位数,第五个参数是指向一个以特定颜色格式存放的位数组的指针,数组内存放有用来初始化该DDB的图像。如果您不想用一张现有的图像来初始化DDB,可以将最后一个参数设为NULL。以后您还是可以设定该DDB内图素的内容。

使用此函数时,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与设备内容没有其它联系。

CreateDiscardableBitmap函数与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

LPVOIDbmBits ; // pointer to pixel bits

}

BITMAP, * PBITMAP ;

在呼叫CreateBitmapIndirect函数时,您不需要设定bmWidthBytes字段。Windows将为您计算,您也可以将bmBits字段设定为NULL,或者设定为初始化位图时用的图素位地址。

GetObject函数内也使用BITMAP结构,首先定义一个BITMAP型态的结构。

BITMAP bitmap ;

并呼叫函数如下:

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

Windows将用位图信息填充BITMAP结构的字段,不过,bmBits字段等于NULL。

您最后应呼叫DeleteObject来清除程序内建立的所有位图。

位图位

用CreateBitmap或CreateBitmapIndirect来建立设备相关GDI位图对象时,您可以给位图图素位指定一个指针。或者您也可以让位图维持未初始化的状态。在建立位图以后,Windows还提供两个函数来获得并设定图素位。

要设定图素位,请呼叫:

SetBitmapBits (hBitmap, cBytes, &bits) ;

GetBitmapBits函数有相同的语法:

GetBitmapBits (hBitmap, cBytes, &bits) ;

在这两个函数中,cBytes指明要复制的字节数,bits是最少cBytes大小的缓冲区。

DDB中的图素位从顶列开始排列。我在前面说过,每列的字节数都是偶数。除此之外,没什么好说明的了。如果位图是单色的,也就是说它有1个位面并且每个图素占1位,则每个图素不是1就是0。每列最左边的图素是本列第一个字节最高位的位。我们在本章的后面讲完如何显示单色DDB之后,将做一个单色的DDB。

对于非单色位图,应避免出现您需要知道图素位含义的状况。例如,假定在8位颜色的VGA上执行Windows,您可以呼叫CreateCompatibleBitmap。通过GetDeviceCaps,您能够确定您正处理一个有1个颜色位面和每图素8位的设备。一个字节储存一个图素。但是图素值0x37是什么意思呢?很明显是某种颜色,但到底是什么颜色呢?

图素实际上并不涉及任何固定的颜色,它只是一个值。DDB没有颜色表。问题的关键在于:当DDB显示在屏幕上时,图素的颜色是什么。它肯定是某种颜色,但具体是什么颜色呢?显示的图素将与在显示卡上的调色盘查看表里的0x37索引值代表的RGB颜色有关。这就是您现在碰到的设备依赖性。

不过,不要只因为我们不知道图素值的含义,就假定非单色DDB没用。我们将简要看一下它们的用途。 下一章,我们将看到SetBitmapBits和GetBitmapBits函数是如何被更有用的SetDIBits和GetDIBits函数所取代的。

因此,基本的规则是这样的:不要用CreateBitmap、CreateBitmapIndirect或SetBitmapBits来设定彩色DDB的位,您只能安全地使用这些函数来设定单色DDB的位。(如果您在呼叫GetBitmapBits期间,从其它相同格式的DDB中获得位,那么这些规则例外。)

在继续之前,让我再讨论一下SetBitmapDimensionEx和GetBitmapDimensionEx函数。这些函数让您设定(和获得)位图的测量尺寸(以0.1毫米为单位)。这些信息与位图分辨率一起储存在GDI中,但不用于任何操作。它只是您与DDB联系的一个测量尺寸标识。

内存设备内容

我们必须解决的下一个概念是内存设备内容。您需要用内存设备内容来处理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可将屏幕上的一些内容复制给位图。我们将看到这些都是可能的。

载入位图资源

除了各种各样的位图建立函数以外,获得GDI位图对象句柄的另一个方法就是呼叫LoadBitmap函数。使用此函数,您不必担心位图格式。在程序中,您只需简单地按资源来建立位图,这与建立图标或者鼠标光标的方法类似。LoadBitmap函数的语法与LoadIcon和LoadCursor相同:

hBitmap = LoadBitmap (hInstance, szBitmapName) ;

如果想加载系统位图,那么将第一个参数设为NULL。这些不同的位图是Windows视觉接口(例如关闭方块和勾选标记)的一小部分,它们的标识符以字母OBM开始。如果位图与整数标识符而不是与名称有联系,那么第二个参数就可以使用MAKEINTRESOURCE宏。由LoadBitmap加载的所有位图最终应用DeleteObject清除。

如果位图资源是单色的,那么从LoadBitmap传回的句柄将指向一个单色的位图对象。如果位图资源不是单色,那么从LoadBitmap传回的句柄将指向一个GDI位图对象,该对象与执行程序的视讯显示器有相同的色彩组织。因此,位图始终与视讯显示器兼容,并且总是选进与视讯显示器兼容的内存设备内容中。采用LoadBitmap呼叫后,就不用担心任何色彩转换的问题了。在下一章中,我们就知道LoadBitmap的具体运作方式了。

程序14-3所示的BRICKS1程序示范了加载一小张单色位图资源的方法。此位图本身不像砖块,但当它水平和垂直重复时,就与砖墙相似了。

程序14-3  BRICKS1

BRICKS1.C

/*--------------------------------------------------------------------------

BRICKS1.C -- LoadBitmap 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 ("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_APPLICATION) ;

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) ;

}


DeleteDC (hdcMem) ;

EndPaint (hwnd, &ps) ;

return 0 ;



case WM_DESTROY:

DeleteObject (hBitmap) ;

PostQuitMessage (0) ;

return 0 ;

}

return DefWindowProc (hwnd, message, wParam, lParam) ;

}
BRICKS1.RC (摘录)

//Microsoft Developer Studio generated resource script.

#include "resource.h"

#include "afxres.h"

/

// Bitmap

BRICKS BITMAP DISCARDABLE "Bricks.bmp"

BRICKS.BMP

 

 

在Visual C++ Developer Studio中建立位图时,应指明位图的高度和宽度都是8个图素,是单色,名称是「Bricks」。BRICKS1程序在WM_CREATE消息处理期间加载了位图并用GetObject来确定位图的图素尺寸(以便当位图不是8图素见方时程序仍能继续工作)。以后,BRICKS1将在WM_DESTROY消息中删除此位图。

在WM_PAINT消息处理期间,BRICKS1建立了一个与显示器兼容的内存设备内容,并且选进了位图。然后是从内存设备内容到显示区域设备内容一系列的BitBlt函数呼叫,再删除内存设备内容。图14-3显示了程序的执行结果。

顺便说一下,Developer Studio建立的BRICKS.BMP文件是一个设备无关位图。您可能想在Developer Studio内建立一个彩色的BRICKS.BMP文件(您可自己选定颜色),并且保证一切工作正常。

我们看到DIB能转换成与视讯显示器兼容的GDI位图对象。我们将在下一章看到这是如何操作的。

 

 

图14-3 BRICKS1的屏幕显示

单色位图格式

如果您在处理小块单色图像,那么您不必把它们当成资源来建立。与彩色位图对象不同,单色位的格式相对简单一些,而且几乎能全部从您要建立的图像中分离出来。例如,假定您要建立下图所示的位图:

 

 

您能写下一系列的位(0代表黑色,1代表白色),这些位直接对应于网格。从左到右读这些位,您能给每8字节配置一个十六进制元的字节值。如果位图的宽度不是16的倍数,在字节的右边用零填充,以得到偶数个字节:

0 1 0 1 0 0 0 1 0 1 1 1 0 1 1 1 0 0 0 1 = 51 77 10 00

0 1 0 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 0 1 = 57 77 50 00

0 0 0 1 0 0 1 1 0 1 1 1 0 1 1 1 0 1 0 1 = 13 77 50 00

0 1 0 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 0 1 = 57 77 50 00

0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 = 51 11 10 00

图素宽为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) ;

在程序14-4显示的BRICKS2程序利用此技术直接建立了砖块位图,而没有使用资源。

程序14-4 BRICKS2
        
BRICKS2.C

/*--------------------------------------------------------------------------

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_APPLICATION) ;

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 BITMA Pbitmap = { 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色模式下,那么您可以根据表14-2来定义彩色砖的每个图素。不过,当程序执行在其它显示模式下时,此程序代码不起作用。以设备无关方式处理彩色位图需要使用 下章讨论的DIB。

位图中的画刷

BRICKS系列的最后一个项目是BRICKS3,如程序14-5所示。乍看此程序,您可能会有这种感觉:程序代码哪里去了呢?

程序14-5 BRICKS3
        
BRICKS3.C

/*-------------------------------------------------------------------------

BRICKS3.C -- CreatePatternBrush 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 ("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_APPLICATION) ;

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) ;

}


DeleteObject (hBrush) ;

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 (摘录)

//Microsoft Developer Studio generated resource script.

#include "resource.h"

#include "afxres.h"

/

// Bitmap

BRICKS BITMAP DISCARDABLE "Bricks.bmp"

此程序与BRICKS1使用同一个BRICKS.BMP文件,而且窗口看上去也相同。

正如您看到的一样,窗口消息处理程序没有更多的内容。BRICKS3实际上使用砖块图案作为窗口类别背景画刷,它在WNDCLASS结构的hbrBackground字段中定义。

您现在可能猜想GDI画刷是很小的位图,通常是8个图素见方。如果将LOGBRUSH结构的lbStyle字段设定为BS_PATTERN,然后呼叫CreatePatternBrush或CreateBrushIndirect,您就可以在位图外面来建立画刷了。此位图至少是宽高各8个图素。如果再大,Windows 98将只使用位图的左上角作为画刷。而Windows NT不受此限制,它会使用整个位图。

请记住,画刷和位图都是GDI对象,而且您应该在程序终止前删除您在程序中建立画刷和位图。如果您依据位图建立画刷,那么在用画刷画图时,Windows将复制位图位到画刷所绘制的区域内。呼叫CreatePatternBrush(或者CreateBrushIndirect)之后,您可以立即删除位图而不会影响到画笔。类似地,您也可以删除画刷而不会影响到您选进的原始位图。注意,BRICKS3在建立画刷后删除了位图,并在程序终止前删除了画刷。

绘制位图

在窗口中绘图时,我们已经将位图当成绘图来源使用过了。这要求先将位图选进内存设备内容,并呼叫BitBlt或者StretchBlt。您也可以用内存设备内容句柄作为所有实际呼叫的GDI函数中的第一参数。内存设备内容的动作与实际的设备内容相同,除非显示平面是位图。

程序14-6所示的HELLOBIT程序展示了此项技术。程序在一个小位图上显示了字符串「Hello, world!」,然后从位图到程序显示区域执行BitBlt或StretchBlt(依照选择的菜单选项而定)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值