📄 14. 位图和bitblt.txt
字号:
现在情况是这样: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程序利用此技术直接建
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -