C++ GDI 位图绘制完整示例:从加载到窗口显示
介绍在 C++ Windows 编程中使用 GDI 库进行位图绘制的具体方法,包含从加载图片、创建兼容 DC 到最终贴图显示的完整操作步骤,并提供经过修正的完整可运行代码。
核心操作流程
使用 GDI 绘制位图主要依赖以下四个关键步骤:
- 加载位图资源:使用
LoadImageAPI 从文件系统中读取 BMP 图像。 - 创建兼容 DC:调用
CreateCompatibleDC创建一个内存设备上下文,用于离屏渲染。 - 选择位图对象:通过
SelectObject将加载的位图选入内存 DC,使其成为当前绘图目标。 - 执行贴图操作:在主窗口的 DC 上使用
BitBlt将内存中的位图复制到屏幕上。
完整代码实现
以下代码演示了一个完整的 Windows 窗口程序,包含窗口类注册、消息循环以及正确的 GDI 位图加载与绘制逻辑。代码已针对原稿中存在的函数调用错误和语法问题进行了修正。
第1步:初始化与资源加载
在 Game_Init 函数中完成 HDC 获取、位图加载及内存 DC 创建。注意需确保 BMP 文件路径正确,且在清理时释放资源。
#include <windows.h>
#include <tchar.h>
const int TAR_HIGH = 800;
const int TAR_WEIGHT = 600;
const wchar_t* TAR_TITLE = L"C++ GDI 位图绘制示例";
HDC g_hdc = NULL;
HDC g_mdc = NULL;
HBITMAP g_hbitmap = NULL;
// 绘制函数:将内存位图拷贝到屏幕
void Game_Paint(HWND hwnd) {
if (g_hdc && g_mdc && g_hbitmap) {
SelectObject(g_mdc, g_hbitmap);
BitBlt(g_hdc, 0, 0, TAR_HIGH, TAR_WEIGHT, g_mdc, 0, 0, SRCCOPY);
}
}
// 初始化函数
bool Game_Init(HWND hwnd) {
g_hdc = GetDC(hwnd);
// 加载位图,需确保 content.bmp 存在且路径相对正确
g_hbitmap = (HBITMAP)LoadImage(NULL, L"content.bmp", IMAGE_BITMAP, TAR_HIGH, TAR_WEIGHT, LR_LOADFROMFILE);
if (!g_hbitmap) {
MessageBox(hwnd, L"无法加载位图文件", L"Error", MB_OK | MB_ICONERROR);
ReleaseDC(hwnd, g_hdc);
return false;
}
// 建立内存设备上下文 (Memory DC)
g_mdc = CreateCompatibleDC(g_hdc);
// 首次绘制
Game_Paint(hwnd);
// 注意:此处不应立即 ReleaseDC,应在 WM_PAINT 或窗口关闭时管理 HDC 生命周期
// 为简化示例,这里保持句柄开放供后续使用,实际项目建议精细管理
return true;
}
// 清理函数
bool Game_Clear(HWND hwnd) {
if (g_hbitmap) DeleteObject(g_hbitmap);
if (g_mdc) DeleteDC(g_mdc);
if (g_hdc) ReleaseDC(hwnd, g_hdc); // 恢复标准释放方式
g_hbitmap = NULL;
g_mdc = NULL;
g_hdc = NULL;
return true;
}
第2步:窗口过程与消息处理
在窗口过程 SdjzuProc 中,重点修正了以下逻辑错误:
- WM_PAINT:必须在
BeginPaint之后获取 HDC,并在EndPaint之前完成绘制和验证矩形,避免重复绘制和资源泄漏。 - WM_KEYDOWN:修复了赋值运算符
=误用为比较运算符==的逻辑错误。 - WM_DESTROY:确保在窗口销毁前释放 GDI 资源。
LRESULT CALLBACK SdjzuProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT paintstruct;
switch (message) {
case WM_PAINT:
g_hdc = BeginPaint(hwnd, &paintstruct);
if (!g_hbitmap || !g_mdc) {
// 如果初始化失败或资源未加载,尝试重新初始化
Game_Init(hwnd);
} else {
Game_Paint(hwnd);
}
EndPaint(hwnd, &paintstruct);
ValidateRect(hwnd, NULL);
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE) { // 修正:使用 == 进行比较
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
Game_Clear(hwnd); // 清理资源
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
第3步:主入口与窗口创建
配置窗口类属性,注册窗口,并在 WinMain 中启动消息循环。确保窗口样式包含标题栏和最小化按钮,以便测试 ESC 退出功能。
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
WNDCLASSEX wndclass = { 0 };
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = SdjzuProc;
wndclass.hInstance = hInstance;
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszClassName = TEXT("sdjzuhero");
if (!RegisterClassEx(&wndclass))
return -1;
HWND hwnd = CreateWindow(
TEXT("sdjzuhero"), TAR_TITLE,
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, TAR_HIGH, TAR_WEIGHT,
NULL, NULL, hInstance, NULL
);
if (!hwnd) return -1;
MoveWindow(hwnd, 250, 80, TAR_HIGH, TAR_WEIGHT, TRUE);
ShowWindow(hwnd, nShowCmd);
UpdateWindow(hwnd);
MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnregisterClass(TEXT("sdjzuhero"), hInstance);
return (int)msg.wParam;
}
常见问题排错
- 位图不显示:检查
LoadImage返回值是否为 NULL,确认BMP文件格式是否正确且未被其他程序占用。 - 闪烁或重绘异常:确保在
WM_PAINT中使用BeginPaint和EndPaint配对,不要在全局 HDC 上直接绘图而不进行验证(ValidateRect)。 - 内存泄漏:每次成功
CreateCompatibleDC后,必须在程序退出或窗口销毁时调用DeleteDC;选入 DC 的 GDI 对象(如位图)在删除 DC 前也应确保被正确注销或删除。
总结
C++ GDI 位图绘制是理解 Windows 图形子系统的基础。关键在于正确管理设备上下文(DC)的生命周期,并使用 BitBlt 进行高效的像素块传输。通过上述示例,您可以实现基础的图像显示功能。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









