如何用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
相关产品推荐
相关产品推荐

