如何无需复制直接读取HBITMAP的原始像素字节?
问题1:无需复制数据访问设备相关位图像素的方法
你通过以下代码创建了设备相关的HBITMAP句柄:
// copy screen to bitmap HDC hScreen = GetDC(NULL);//don't use hwnd, it doesn't work for non native windows HDC hDC = CreateCompatibleDC(hScreen); HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, w, h); HGDIOBJ old_obj = SelectObject(hDC, hBitmap); BOOL bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY);
你希望只读访问该位图的RGB像素值,完成计算后丢弃,但发现通过GetObject获取的BITMAP结构中bmBits为空——因为该位图不是CreateDIBSection创建的,根据官方文档,只有DIBSection创建的位图才能通过GetObject拿到像素指针,其他方式创建的只能获取宽高、格式等元数据,必须用GetDIBits或GetBitmapBits(但这两个会复制数据)。
解答
不存在无需复制数据就能直接访问你当前创建的这种设备相关位图像素的方法,原因如下:
- 设备相关位图(DDB)的像素数据存储在显示设备的显存中,属于硬件相关内存区域,用户态代码无法直接访问,必须通过GDI函数将数据复制到用户态内存才能读取。
- 只有设备无关位图(DIB),尤其是通过
CreateDIBSection创建的DIBSection,才会把像素数据放在用户态可直接访问的内存中,这也是GetObject能返回有效bmBits指针的原因。
如果想避免额外的数据复制,建议直接修改创建位图的方式,用CreateDIBSection替代CreateCompatibleBitmap,直接创建可直接访问像素的DIBSection,示例代码如下:
// 获取屏幕的像素格式 BITMAPINFO bmi = {0}; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = w; bmi.bmiHeader.biHeight = -h; // 负高度表示像素数据按从上到下的顺序存储 bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = GetDeviceCaps(hScreen, BITSPIXEL); // 获取屏幕位深度(24或32) bmi.bmiHeader.biCompression = BI_RGB; void* pBits = nullptr; HBITMAP hBitmap = CreateDIBSection(hScreen, &bmi, DIB_RGB_COLORS, &pBits, nullptr, 0); // 将屏幕内容BitBlt到DIBSection的DC中 HDC hDC = CreateCompatibleDC(hScreen); HGDIOBJ old_obj = SelectObject(hDC, hBitmap); BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY); SelectObject(hDC, old_obj); DeleteDC(hDC); // pBits直接指向像素数据,可直接读取RGB值,无需复制 // 使用完毕后释放位图 DeleteObject(hBitmap);
这样创建的位图,既可以通过pBits直接访问像素,也可以通过GetObject获取DIBSECTION结构拿到bmBits指针,完全不需要额外复制数据。
问题2:MSDN保存HBITMAP代码的疑惑解析
你提到的MSDN代码片段翻译及解释如下:
// 为BITMAPINFO结构分配内存。(该结构包含一个BITMAPINFOHEADER结构和一个RGBQUAD数组) if (cClrBits < 24) pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << cClrBits)); // 对于24位或32位像素格式,不需要RGBQUAD数组 else pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
代码解释
BITMAPINFO结构由两部分组成:BITMAPINFOHEADER(位图基本信息头) + 可选的RGBQUAD颜色表(调色板)。- 当位图位深度(
cClrBits)小于24位时(如8位、4位、1位),属于索引色位图,需要用RGBQUAD数组定义每个索引对应的实际RGB颜色,因此要为颜色表分配额外内存——1 << cClrBits是调色板的颜色总数(比如8位对应256种颜色)。 - 当位深度为24位或32位时,属于真彩色位图,每个像素直接存储RGB(或RGBA)值,不需要调色板,因此只需分配
BITMAPINFOHEADER的大小即可,无需额外的RGBQUAD数组空间。
内容的提问来源于stack exchange,提问作者Dartz
相关产品推荐
相关产品推荐

