You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Win32 API实现直接位图?修改数组为何不更新HBITMAP?

问题原因

CreateBitmap 创建的是设备相关位图(DDB),调用时它会把你传入的像素数组数据完整复制到GDI内部的设备专属内存里,之后你的原始数组和HBITMAP就没有任何关联了——修改数组元素自然不会影响到已经创建好的HBITMAP。

解决方案:使用CreateDIBSection

要实现和GDI+直接位图一样的“修改像素数组自动同步到位图”效果,必须用 CreateDIBSection 函数。它会创建设备无关位图(DIB),并返回一个指向像素缓冲区的指针,这个缓冲区和HBITMAP直接绑定,修改缓冲区的像素会立刻反映到HBITMAP上。

完整示例代码

#include <windows.h>
#include <algorithm>

HBITMAP hBitmap;
uint32_t* pPixels = nullptr;
const int BITMAP_WIDTH = 240;
const int BITMAP_HEIGHT = 240;

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_CREATE:
        {
            // 初始化BITMAPINFO结构,指定32位RGB格式
            BITMAPINFO bmi = {0};
            bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
            bmi.bmiHeader.biWidth = BITMAP_WIDTH;
            // 负高度表示像素数据按从上到下的顺序存储(和GDI+逻辑一致)
            bmi.bmiHeader.biHeight = -BITMAP_HEIGHT;
            bmi.bmiHeader.biPlanes = 1;
            bmi.bmiHeader.biBitCount = 32;
            bmi.bmiHeader.biCompression = BI_RGB;
            bmi.bmiHeader.biSizeImage = BITMAP_WIDTH * BITMAP_HEIGHT * sizeof(uint32_t);

            // 创建DIBSection,获取共享像素缓冲区指针
            hBitmap = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, (void**)&pPixels, nullptr, 0);
            
            // 初始化所有像素为红色
            std::fill(pPixels, pPixels + BITMAP_WIDTH * BITMAP_HEIGHT, RGB(255, 0, 0));
            return 0;
        }

        case WM_LBUTTONDOWN:
        {
            // 修改左上角10x10区域为蓝色,演示同步效果
            for (int y = 0; y < 10; y++) {
                for (int x = 0; x < 10; x++) {
                    pPixels[y * BITMAP_WIDTH + x] = RGB(0, 0, 255);
                }
            }
            // 触发窗口重绘,让修改后的位图显示出来
            InvalidateRect(hWnd, nullptr, TRUE);
            return 0;
        }

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            
            // 创建兼容DC,绘制HBITMAP到窗口
            HDC hdcMem = CreateCompatibleDC(hdc);
            HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMem, hBitmap);
            BitBlt(hdc, 0, 0, BITMAP_WIDTH, BITMAP_HEIGHT, hdcMem, 0, 0, SRCCOPY);
            
            // 清理GDI资源
            SelectObject(hdcMem, hOldBitmap);
            DeleteDC(hdcMem);
            
            EndPaint(hWnd, &ps);
            return 0;
        }

        case WM_DESTROY:
        {
            // 释放位图资源
            DeleteObject(hBitmap);
            PostQuitMessage(0);
            return 0;
        }

        default:
            return DefWindowProc(hWnd, msg, wParam, lParam);
    }
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    const wchar_t CLASS_NAME[] = L"DIBSectionDemoWindow";

    WNDCLASS wc = {0};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

    RegisterClass(&wc);

    HWND hWnd = CreateWindowEx(
        0,
        CLASS_NAME,
        L"DIBSection 直接像素修改演示",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 300, 300,
        nullptr, nullptr, hInstance, nullptr
    );

    if (hWnd == nullptr) {
        return 0;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

关键细节说明

  • CreateDIBSection 的第四个参数会返回指向像素缓冲区的指针,这个缓冲区是GDI分配的,应用程序可以直接读写。
  • 设置 biHeight 为负数是为了让像素数据的顺序和GDI+一致(从上到下),如果设为正数,像素数据是从下到上存储的,修改时需要注意坐标转换。
  • 修改像素后需要调用 InvalidateRect 触发窗口重绘,才能让修改后的位图显示在界面上。
  • 所有GDI资源(HBITMAP、HDC等)使用后必须正确释放,避免内存泄漏。

内容的提问来源于stack exchange,提问作者EpsilonDeltaCriterion

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 14:54:13