C++ GDI 位图绘制完整示例:从加载到窗口显示

云舒
云舒
发布时间:2026-10-13 | 4人浏览过
原创

介绍在 C++ Windows 编程中使用 GDI 库进行位图绘制的具体方法,包含从加载图片、创建兼容 DC 到最终贴图显示的完整操作步骤,并提供经过修正的完整可运行代码。

核心操作流程

使用 GDI 绘制位图主要依赖以下四个关键步骤:

  1. 加载位图资源:使用 LoadImage API 从文件系统中读取 BMP 图像。
  2. 创建兼容 DC:调用 CreateCompatibleDC 创建一个内存设备上下文,用于离屏渲染。
  3. 选择位图对象:通过 SelectObject 将加载的位图选入内存 DC,使其成为当前绘图目标。
  4. 执行贴图操作:在主窗口的 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 进行高效的像素块传输。通过上述示例,您可以实现基础的图像显示功能。

以上就是C++ GDI 位图绘制的详细内容,更多关于 C++ Windows 编程的资料请关注本站其它相关文章!
相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表