GDI中如何从位图获取原始缓冲区?相关异常问题咨询
问题背景
我对GDI的理解是:GetDIBits函数可在Windows系统中从捕获的位图中提取原始像素数据。
引用:GetDIBits函数会检索指定兼容位图的位数据,并按指定格式将其复制到缓冲区中作为DIB。
我写了以下代码尝试获取像素数据:
HDC srcd , memdc; HBITMAP hbmp; BITMAP bmp; DWORD dwBytesWritten = 0; DWORD dwSizeofDIB = 0; HANDLE hFile = NULL; char* lpbitmap = NULL; HANDLE hDIB = NULL; DWORD dwBmpSize = 0; BITMAPFILEHEADER bmfHeader; BITMAPINFOHEADER bi; int Height = GetSystemMetrics(SM_CYSCREEN); int Width = GetSystemMetrics(SM_CXSCREEN); srcd = GetDC(NULL); if (!srcd) printf("failed to obtain screen"); memdc = CreateCompatibleDC(srcd); // creates memory device context hbmp = CreateCompatibleBitmap(srcd, Width, Height); SelectObject(memdc, hbmp); BitBlt(memdc, 0, 0, Width, Height, srcd, 0, 0, SRCCOPY); BITMAP bmpScreen; GetObject(hbmp, sizeof(bmpScreen), (LPVOID)&bmpScreen); bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = bmpScreen.bmWidth; bi.biHeight = bmpScreen.bmHeight; bi.biPlanes = 1; bi.biBitCount = 32; bi.biCompression = BI_RGB; bi.biSizeImage = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrUsed = 0; bi.biClrImportant = 0; dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight; hDIB = GlobalAlloc(GHND, dwBmpSize); lpbitmap = (char*)GlobalLock(hDIB); GetDIBits(memdc, hbmp, 0, (UINT)bmpScreen.bmHeight, lpbitmap, (BITMAPINFO*)&bi, DIB_RGB_COLORS); printf("dwBmpSize: %ld\n", dwBmpSize); printf("Strlen(lpbitmap): %lld\n", strlen(lpbitmap));
我的设备是1920×1080分辨率的单屏,每个像素32位,理论上应获取约66兆位的数据,但输出不符合预期:
dwBmpSize: 8294400 Strlen(lpbitmap): 103614
我的疑问:
- 为何
strlen返回如此小的数值? - 为何
lpbitmap缓冲区包含大量类似“A”的垃圾值?
问题解答
1. 关于strlen返回值过小的问题
strlen是用来统计以'\0'(空字符)结尾的字符串长度的函数,但lpbitmap里存储的是二进制像素数据,不是字符串。像素数据中很容易出现值为0的字节(比如黑色像素的Alpha通道或蓝色通道),strlen遇到第一个'\0'就会停止计数,所以返回的长度远小于实际的像素数据大小。
要确认缓冲区的真实数据量,直接用你计算好的dwBmpSize即可,完全不应该用strlen来检测二进制数据。
2. 关于缓冲区出现类似“A”的“垃圾值”问题
首先,32位像素的格式通常是BGRA(蓝、绿、红、Alpha),每个通道占1字节。你看到的“A”对应的ASCII码是0x41,这其实是某个通道的正常数值,不是垃圾值——比如某个像素的Alpha通道是0x41,或者红/绿/蓝通道是0x41,就会被文本编辑器解析成字符“A”。
另外,GlobalAlloc分配的内存默认不会初始化,但你已经通过GetDIBits把像素数据写入了缓冲区,所以里面的内容都是真实的屏幕像素数据,只是用文本方式查看二进制数据时,会把部分字节解析成可打印字符,看起来像“垃圾值”而已。如果要验证数据正确性,可以尝试把这些像素数据保存为BMP文件,或者直接读取特定位置的像素值来检查。
内容的提问来源于stack exchange,提问作者loaded_dypper

