windows编程之位图绘制
双缓冲技术
当屏幕刷新的时候闪烁,原因是绘图与显示器刷新不同步,有时间差,为解决这一问题,这就需要用到双缓冲技术来绘图了。双缓冲技术是相对单缓冲而言的,单缓冲就是直接在设备DC上绘图;而双缓冲就是先在一个与设备DC相兼容的内存缓冲区里进行绘图,然后再一次性复制到设备DC上。一次性在屏幕上显示就不会出现闪烁的现象。
- 通俗的话说就是
- 普通绘图就是直接在我们看得到的黑板上绘图
- 双缓冲就是先在一个虚拟的黑板上画完,等用到的时候在把虚拟黑板上的图画复制到我们看得到的黑板上去;
相关函数
- LoadImage (加载图片)
- CreateCompatibleDC (创建内存DC)
- BitBlt (执行位块传输)
- StretchBlt (复制位图并拉伸或压缩位图)
- TransparentBlt (位图透明化)
- DeleteDC (删除内存DC)
LoadImage
加载图标、游标、动画游标或位图。
HANDLE LoadImageA(
[in, optional] HINSTANCE hInst,
[in] LPCSTR name,
[in] UINT type,
[in] int cx,
[in] int cy,
[in] UINT fuLoad
);
参数解析
参数名 | 含义 |
---|---|
hInst | 【1】DLL 或可执行文件 (.exe 模块的句柄) ,其中包含要加载的图像。 有关详细信息,请参阅 GetModuleHandle。 请注意,从 32 位 Windows 开始,实例句柄 (HINSTANCE) ,例如由 WinMain 系统函数调用公开的应用程序实例句柄,而模块句柄 (HMODULE) 也是如此。【2】若要加载 OEM 映像,请将此参数设置为 NULL。【3】若要加载独立资源 (图标、游标或位图文件) (例如,c:\myimage.bmp),请将此参数设置为 NULL。 |
name | 要加载的图像。 如果 hinst 参数为非 NULL 且 fuLoad 参数省略 LR_LOADFROMFILE, 则 lpszName 在 hinst 模块中指定映像资源。 如果要从模块按名称加载映像资源, lpszName 参数是指向包含映像资源名称的以 null 结尾的字符串的指针。 如果要从模块中按序号加载图像资源,请使用 MAKEINTRESOURCE 宏将图像序号转换为可传递给 LoadImage 函数的窗体。 |
type | 要加载的图像的类型. 见下表 |
cx | 图标或光标的宽度(以像素为单位)。 如果此参数为零且 fuLoad 参数 LR_DEFAULTSIZE,则函数使用 SM_CXICON 或 SM_CXCURSOR 系统指标值来设置宽度。 如果此参数为零且未使用 LR_DEFAULTSIZE ,则函数使用实际资源宽度。 |
cy | 图标或光标的高度(以像素为单位)。 如果此参数为零且 fuLoad 参数 LR_DEFAULTSIZE,则函数使用 SM_CYICON 或 SM_CYCURSOR 系统指标值来设置高度。 如果此参数为零且未使用 LR_DEFAULTSIZE ,则函数使用实际资源高度。 |
fuLoad | 见下表 |
返回值
类型: HANDLE
如果函数成功,则返回值是新加载的图像的句柄。
如果函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 GetLastError。
备注
如果 IS_INTRESOURCE (lpszName) 为 TRUE, 则 lpszName 指定给定资源的整数标识符。 否则,它是指向以 null 结尾的字符串的指针。 如果字符串的第一个字符是磅号 (#) ,则其余字符表示指定资源的整数标识符的十进制数。 例如,字符串“#258”表示标识符 258。
使用完加载的位图、游标或图标而不指定 LR_SHARED 标志时,可以通过调用下表中的某个函数来释放其关联的内存。
资源 | 发布函数 |
---|---|
Bitmap | DeleteObject |
游标 | DestroyCursor |
图标 | DestroyIcon |
**hInst: **
如果 hinst 参数为 NULL 且 fuLoad 参数省略 LR_LOADFROMFILE 值, 则 lpszName 指定要加载的 OEM 映像。 OEM 映像标识符在 Winuser.h 中定义,并具有以下前缀。
前缀 | 含义 |
---|---|
OBM_ | OEM 位图 |
OIC_ | OEM 图标 |
Ocr_ | OEM 游标 |
type:
值 | 含义 |
---|---|
IMAGE_BITMAP | 加载位图。 |
IMAGE_CURSOR | 加载游标。 |
IMAGE_ICON | 含义加载图标。 |
fuLoad:
此参数可使用以下一个或多个值。
Value | 含义 |
---|---|
LR_CREATEDIBSECTION | 当 uType 参数指定IMAGE_BITMAP时,会导致函数返回 DIB 节位图而不是兼容的位图。 此标志可用于加载位图而不将其映射到显示设备的颜色。 |
LR_DEFAULTCOLOR | 默认标志;它不执行任何作用。 这意味着“不 LR_MONOCHROME”。 |
LR_DEFAULTSIZE | 如果 cxDesired 或 cyDesired 值设置为零,则使用由光标或图标的系统指标值指定的宽度或高度。 如果未指定此标志, 并且 cxDesired 和 cyDesired 设置为零,则函数将使用实际资源大小。 如果资源包含多个图像,则函数使用第一个图像的大小。 |
LR_LOADFROMFILE | 从 lpszName 指定的文件中加载独立图像, (图标、游标或位图文件) 。 |
LR_LOADMAP3DCOLORS | 在颜色表中搜索图像,并将以下灰色阴影替换为相应的三维颜色。Dk Gray,RGB (128,128,128) 与 COLOR_3DSHADOW灰色,RGB (192,192,192) 与 COLOR_3DFACELt Gray,RGB (223,223,223 ) ,COLOR_3DLIGHT如果要加载颜色深度大于 8bpp 的位图,请不要使用此选项。 |
LR_LOADTRANSPARENT | 检索图像中第一个像素的颜色值,并将颜色表中的相应条目替换为默认窗口颜色 (COLOR_WINDOW) 。 使用该条目的图像中的所有像素都将成为默认窗口颜色。 此值仅适用于具有相应颜色表的图像。如果要加载颜色深度大于 8bpp 的位图,请不要使用此选项。如果 fuLoad 同时包括 LR_LOADTRANSPARENT 和 LR_LOADMAP3DCOLORS 值, LR_LOADTRANSPARENT 优先。 但是,颜色表项替换为 COLOR_3DFACE 而不是 COLOR_WINDOW。 |
LR_MONOCHROME | 加载黑白图像。 |
LR_SHARED | 如果多次加载映像,则共享映像句柄。 如果未设置 LR_SHARED ,则对同一资源的 LoadImage 的第二次调用将再次加载映像并返回不同的句柄。使用此标志时,系统会在不再需要资源时销毁资源。不要对具有非标准大小的图像使用 LR_SHARED ,这些图像在加载后可能会更改,或者从文件加载。加载系统图标或游标时,必须使用 LR_SHARED ,否则函数将无法加载资源。无论请求的大小如何,此函数都会找到缓存中请求的资源名称的第一个映像。 |
LR_VGACOLOR | 使用真正的 VGA 颜色。 |
CreateCompatibleDC
CreateCompatibleDC 函数 (DC) 创建与指定设备兼容的内存设备上下文。
HDC CreateCompatibleDC(
[in] HDC hdc
);
参数名 | 含义 |
---|---|
hdc | 现有 DC 的句柄。 如果此句柄为 NULL,则该函数将创建与应用程序的当前屏幕兼容的内存 DC。 |
返回值
如果函数成功,则返回值是内存 DC 的句柄。
如果函数失败,则返回值为 NULL。
备注
- 内存 DC 仅存在于内存中。 创建内存 DC 时,其显示图面正好是一个单色像素宽和一个单色像素高。 在应用程序可以使用内存 DC 进行绘图操作之前,它必须选择正确宽度和高度到 DC 的位图。 若要选择 DC 中的位图,请使用 CreateCompatibleBitmap 函数,指定所需的高度、宽度和颜色组织。
- 创建内存 DC 后,所有属性都设置为普通默认值。 内存 DC 可用作普通 DC。 可以设置属性;获取其属性的当前设置;并选择笔、画笔和区域。
- CreateCompatibleDC 函数只能与支持光栅操作的设备一起使用。 应用程序可以通过调用 GetDeviceCaps 函数来确定设备是否支持这些操作。
- 不再需要内存 DC 时,请调用 DeleteDC 函数。 建议调用 DeleteDC 以删除 DC。 但是,还可以使用 HDC 调用 DeleteObject 以删除 DC。
- 如果 hdc 为 NULL,则调用 CreateCompatibleDC 的线程拥有创建的 HDC。 销毁此线程后,HDC 将不再有效。 因此,如果创建 HDC 并将其传递给另一个线程,则退出第一个线程,第二个线程将无法使用 HDC。
- Icm: 如果为图像颜色管理 (ICM) 启用了传递给此函数的 DC,则由该函数创建的 DC 已启用 ICM。 源和目标颜色空间在 DC 中指定。
BitBlt
BitBlt 函数执行与从指定源设备上下文到目标设备上下文中的像素矩形对应的颜色数据的位块传输。
BOOL BitBlt(
[in] HDC hdc,
[in] int x,
[in] int y,
[in] int cx,
[in] int cy,
[in] HDC hdcSrc,
[in] int x1,
[in] int y1,
[in] DWORD rop
);
参数解析:
参数名 | 含义 |
---|---|
hdc | 目标设备上下文的句柄。 |
x | 目标矩形左上角的 x 坐标(以逻辑单位为单位)。 |
y | 目标矩形左上角的 y 坐标(以逻辑单位为单位)。 |
cx | 源矩形和目标矩形的宽度(以逻辑单位为单位)。 |
cy | 源和目标矩形的高度(以逻辑单位为单位)。 |
hdcSrc | 源设备上下文的句柄。 |
x1 | 源矩形左上角的 x 坐标(以逻辑单位为单位)。 |
y1 | 源矩形左上角的 y 坐标(以逻辑单位为单位)。 |
rop | 光栅操作代码。 这些代码定义源矩形的颜色数据如何与目标矩形的颜色数据组合,以实现最终颜色。 |
**光栅操作代码: **
Value | 含义 |
---|---|
黑暗 | 使用与物理调色板中的索引 0 关联的颜色填充目标矩形。 (对于默认的物理调色板,该颜色为黑色。) |
CAPTUREBLT | 包括在生成的图像中窗口顶部分层的任何窗口。 默认情况下,图像仅包含窗口。 请注意,这通常无法用于打印设备上下文。 |
DSTINVERT | 反转目标矩形。 |
MERGECOPY | 使用布尔 AND 运算符将源矩形的颜色与当前在 hdcDest 中选择的画笔合并。 |
MERGEPAINT | 使用布尔 OR 运算符将倒置源矩形的颜色与目标矩形的颜色合并。 |
NOMIRRORBITMAP | 防止位图镜像。 |
NOTSRCCOPY | 将倒置源矩形复制到目标。 |
NOTSRCERASE | 使用布尔 OR 运算符合并源矩形和目标矩形的颜色,然后反转生成的颜色。 |
PATCOPY | 将当前在 hdcDest 中选择的画笔复制到目标位图中。 |
PATINVERT | 使用布尔 XOR 运算符将当前在 hdcDest 中选择的画笔的颜色与目标矩形的颜色组合在一起。 |
PATPAINT | 使用布尔 OR 运算符将当前在 hdcDest 中选择的画笔的颜色与倒置源矩形的颜色组合在一起。 此操作的结果与目标矩形的颜色结合使用,方法是使用布尔 OR 运算符。 |
SRCAND | 使用布尔 AND 运算符合并源矩形和目标矩形的颜色。 |
SRCCOPY | 将源矩形直接复制到目标矩形。 |
SRCERASE | 使用布尔 AND 运算符将目标矩形的反转颜色与源矩形的颜色组合在一起。 |
SRCINVERT | 使用布尔 XOR 运算符合并源矩形和目标矩形的颜色。 |
SRCPAINT | 使用布尔 OR 运算符合并源矩形和目标矩形的颜色。 |
白 | 使用与物理调色板中的索引 1 关联的颜色填充目标矩形。 (对于默认的物理调色板,该颜色为白色。) |
返回值
如果该函数成功,则返回值为非零值。
如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。
备注
- BitBlt 仅对目标 DC 执行剪辑。
- 如果旋转或剪切转换在源设备上下文中有效, BitBlt 将返回错误。 如果源设备上下文中存在其他转换 (并且匹配转换在目标设备上下文中无效) ,则目标设备上下文中的矩形会根据需要拉伸、压缩或旋转。
- 如果源和目标设备上下文的颜色格式不匹配, BitBlt 函数将源颜色格式转换为匹配目标格式。
- 记录增强型图元文件时,如果源设备上下文标识增强型图元文件设备上下文,则会发生错误。
- 并非所有设备都支持 BitBlt 函数。 有关详细信息,请参阅 GetDeviceCaps 函数中的RC_BITBLT光栅功能条目以及以下函数: MaskBlt、 PlgBlt 和 StretchBlt。
- 如果源和目标设备上下文表示不同的设备,则 BitBlt 将返回错误。 若要在不同设备的 DC 之间传输数据,请通过调用 GetDIBit 将内存位图转换为 DIB。 若要向第二台设备显示 DIB,请调用 SetDIBits 或 StretchDIBits。
- Icm: 当出现 blit 时,不会执行颜色管理。
代码示例
if (!BitBlt(hdcMemDC,
0, 0,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
hdcWindow,
0, 0,
SRCCOPY))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
goto done;
}
StretchBlt
StretchBlt 函数将一个位图从源矩形复制到目标矩形中,并拉伸或压缩位图以适应目标矩形的尺寸(如有必要)。 系统根据当前在目标设备上下文中设置的拉伸模式拉伸或压缩位图。
BOOL StretchBlt(
[in] HDC hdcDest,
[in] int xDest,
[in] int yDest,
[in] int wDest,
[in] int hDest,
[in] HDC hdcSrc,
[in] int xSrc,
[in] int ySrc,
[in] int wSrc,
[in] int hSrc,
[in] DWORD rop
);
参数解析:
参数名 | 含义 |
---|---|
hdcDest | 目标设备上下文的句柄。 |
xDest | 目标矩形左上角的 x 坐标(以逻辑单位为单位)。 |
yDest | 目标矩形左上角的 y 坐标(以逻辑单位为单位)。 |
wDest | 目标矩形的宽度(以逻辑单元表示)。 |
hDest | 目标矩形的高度(以逻辑单元表示)。 |
hdcSrc | 源设备上下文的句柄。 |
xSrc | 源矩形左上角的 x 坐标(以逻辑单位为单位)。 |
ySrc | 源矩形左上角的 y 坐标(以逻辑单位为单位)。 |
wSrc | 源矩形的宽度(以逻辑单元表示)。 |
hSrc | 源矩形的高度(以逻辑单元表示)。 |
rop | 要执行的光栅操作。 光栅操作代码定义系统如何在涉及画笔、源位图和目标位图的输出操作中组合颜色。参考上面的表 |
返回值
如果该函数成功,则返回值为非零值。
如果函数失败,则返回值为零。
备注
- StretchBlt 在内存中拉伸或压缩源位图,然后将结果复制到目标矩形。 此位图可以是兼容位图 (DDB) ,也可以是 CreateDIBSection 的输出。 模式或目标像素的颜色数据在拉伸或压缩后合并。
- 记录增强的图元文件时, (发生错误,如果源设备上下文标识增强型图元文件设备上下文,函数将返回 FALSE) 。
- 如果指定的光栅操作需要画笔,系统将使用当前选择的画笔进入目标设备上下文。
- 使用当前为目标设备上下文指定的转换来转换目标坐标;源坐标通过使用当前为源设备上下文指定的转换进行转换。
- 如果源转换具有旋转或剪切,则会发生错误。
- 如果目标位图、源位图和图案位图的颜色格式不相同, StretchBlt 会将源位图和模式位图转换为与目标位图匹配。
- 如果 StretchBlt 必须将单色位图转换为颜色位图,则会将白色位 (1) 设置为背景色,将黑色位 (0) 设置为前景色。 若要将颜色位图转换为单色位图,它将将背景色与白色匹配的像素设置为白色 (1) ,并将所有其他像素设置为黑色 (0) 。 在转换中将使用彩色设备上下文的前景色和背景色。
- 如果 nWidthSrc 和 nWidthDest 参数的符号或 nHeightSrc 和 nHeightDest 参数不同,StretchBlt 将创建位图的镜像图像。 如果 nWidthSrc 和 nWidthDest 具有不同的符号,则函数会在 x 轴上创建位图的镜像图像。 如果 nHeightSrc 和 nHeightDest 具有不同的符号,则函数会在 y 轴上创建位图的镜像图像。
- 并非所有设备都支持 StretchBlt 函数。 有关详细信息,请参阅 GetDeviceCaps。
- Icm: 当发生 blit 操作时,不会执行颜色管理。
- 在多个监视器系统中使用时, hdcSrc 和 hdcDest 都必须引用同一设备,否则该函数将失败。 若要在不同设备的 DC 之间传输数据,请通过调用 GetDIBit 将内存位图转换为 DIB。 若要向第二台设备显示 DIB,请调用 SetDIBits 或 StretchDIBits。
TransparentBlt
TransparentBlt 函数执行与从指定源设备上下文到目标设备上下文中的像素矩形对应的颜色数据的位块传输。
BOOL TransparentBlt(
[in] HDC hdcDest,
[in] int xoriginDest,
[in] int yoriginDest,
[in] int wDest,
[in] int hDest,
[in] HDC hdcSrc,
[in] int xoriginSrc,
[in] int yoriginSrc,
[in] int wSrc,
[in] int hSrc,
[in] UINT crTransparent
);
参数解析:
参数名 | 含义 |
---|---|
hdcDest | 目标设备上下文的句柄。 |
xoriginDest | 目标矩形左上角的 x 坐标(以逻辑单位为单位)。 |
yoriginDest | 目标矩形左上角的 y 坐标(以逻辑单位为单位)。 |
wDest | 目标矩形的宽度(以逻辑单元表示)。 |
hDest | 目标矩形的高度(以逻辑单元表示)。 |
hdcSrc | 源设备上下文的句柄。 |
xoriginSrc | 源矩形的 x 坐标(以逻辑单位为单位)。 |
yoriginSrc | 源矩形的 y 坐标(以逻辑单位为单位)。 |
wSrc | 源矩形的宽度(以逻辑单元表示)。 |
hSrc | 源矩形的高度(以逻辑单元表示)。 |
crTransparent | 要视为透明的源位图中的 RGB 颜色。 |
返回值:
如果函数成功,则返回值为 TRUE。
如果函数失败,则返回值为 FALSE。
备注:
- TransparentBlt 函数适用于兼容位图 (DDB) 。
- TransparentBlt 函数支持源位图的所有格式。 但是,对于 32 bpp 位图,它只复制 alpha 值。 使用 AlphaBlend 指定透明度为 32 位/像素位图。
- 如果源矩形和目标矩形的大小不相同,则会拉伸源位图以匹配目标矩形。 使用 SetStretchBltMode 函数时,BLACKONWHITE 和 WHITEONBLACK 的 iStretchMode 模式将转换为 TransparentBlt 函数的 COLORONCOLOR。
- 目标设备上下文指定目标坐标的转换类型。 源设备上下文指定源坐标的转换类型。
- 如果源或目标的宽度或高度为负,则 TransparentBlt 不会镜像位图。
- 在多个监视器系统中使用时, hdcSrc 和 hdcDest 都必须引用同一设备,否则该函数将失败。 若要在不同设备的 DC 之间传输数据,请通过调用 GetDIBit 将内存位图转换为 DIB。 若要向第二台设备显示 DIB,请调用 SetDIBits 或 StretchDIBits。
DeleteDC
DeleteDC 函数 (DC) 删除指定的设备上下文。
BOOL DeleteDC(
[in] HDC hdc // 设备上下文的句柄。
);
返回值:
如果该函数成功,则返回值为非零值。
如果函数失败,则返回值为零。
备注:
应用程序不得删除通过调用 GetDC 函数获取其句柄的 DC。 相反,它必须调用 ReleaseDC 函数来释放 DC。
综合应用代码示例
#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("NueXini");
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("Error"), szAppName, MB_ICONERROR | MB_OK);
return 0;
}
hWnd = CreateWindow(
szAppName,
szAppName,
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)
{
HDC hdc, hdcMem;
PAINTSTRUCT ps;
HBITMAP hBitMap;
BITMAP bm;
switch (message)
{
case WM_CREATE:
return 0;
case WM_SIZE:
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
hdcMem = CreateCompatibleDC(hdc);
hBitMap = (HBITMAP)LoadImage(NULL, TEXT("../monster.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
SelectObject(hdcMem, hBitMap);
GetObject(hBitMap, sizeof(BITMAP), &bm);
// 原样输出位图
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
// 复制上面的位图
StretchBlt(hdc, 0, bm.bmHeight, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
// 获取一个颜色像素,输出透明位图
TransparentBlt(hdc, bm.bmWidth + 10, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, GetPixel(hdcMem, 0, 0));
// 复制上面的位图, 缩小2倍
StretchBlt(hdc, bm.bmWidth + 10, bm.bmHeight, bm.bmWidth / 2, bm.bmHeight / 2, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
// 获取一个颜色像素,输出缩小2倍的透明位图
TransparentBlt(hdc, bm.bmWidth * 2, 0, bm.bmWidth / 2, bm.bmHeight / 2, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, GetPixel(hdcMem, 0, 0));
// 释放位图
DeleteObject(hBitMap);
// 释放内存DC
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
enjoy it ~