getdibits(C++函数GetDIBits怎么用)

本文目录
C++函数GetDIBits怎么用
HBITMAP是常用的GDI对象,而GetDIBits可以从一个HBITMAP对象中获得其对应的位数据。
其原型如下:
int GetDIBits( HDC hdc, // handle to DC
hdc, // handle to DC
HBITMAP hbmp, // handle to bitmap
UINT hbmp, // handle to bitmap
UINT uStartScan, // first scan line to set
UINT cScanLines, // number of scan lines to copy
LPVOID cScanLines, // number of scan lines to copy
LPVOID lpvBits, // array for bitmap bits
LPBITMAPINFO lpbi, // bitmap data buffer
UINT lpbi, // bitmap data buffer
UINT uUsage // RGB or palette index );
标准的GetDIBits调用方式是两次调用:
第一次传入空的 lpvBits,此时的lpbi作为传出参数,从中可以获得lpvBits所需的内存区域大小。
BYTE *lpvBits = NULL;
BITMAPINFO bmpInfo = {0};
bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);
/* 第一次调用GetDIBits获得bmpInfo */
nRet = ::GetDIBits(hDC, hBitmap, 0, pBmpData-》bmHeight, NULL, &bmpInfo, DIB_RGB_COLORS);
if (nRet == 0) {
nRet = GetLastError();
TRACE( _T("GetDIBits for bmpInfo failed %d/n"), nRet);
goto err;
}
以上调用如果成功,就会在bmpInfo.bmiHeader.biSizeImage记录位数据所占的内存区大小,
此时就可以动态分配内存,然后再次调用GetDIBits获得实际的位数据:
lpvBits= new BYTE;
if (NULL == pBitsBuffer) {
nRet = -1;
TRACE( _T("Allocate memory for lpvBits failed/n"));
goto err;
}
/* 第二次调用GetDIBits获得位图数据 */
nRet = ::GetDIBits(hDC, hBitmap, 0, pBmpData-》bmHeight, lpvBits, &bmpInfo, DIB_RGB_COLORS);
if (nRet == 0) {
nRet = GetLastError();
TRACE( _T("GetDIBits for lpvBits failed %d/n"), nRet);
goto err;
}
很多时候,因为位图的长宽和颜色深度都是已知的,因此位数据所占的内存区大小可以由公式
width * heigth * pixelBits / 8 计算获得
那是否可以省略第一步调用呢?
答案是:最好不要
原因是 bmpInfo.bmiHeader.biSizeImage 并不一定等于 width * heigth * pixelBits / 8
经过多次测试发现,对于16位颜色深度的位图,如果其宽度为奇数,则第一次GetDIBits获得
的位数据大小为 (width+1) * heigth * pixelBits / 8
也就是说对于16位颜色深度的位图,HBITMAP对象对其进行存储时,自动将宽度调整为了偶
数,也即将每行数据对齐到4字节(一个DWORD的长度)。
暂时没有查到相关的解释,我的猜想是这是为了在进行像素操作时能直接以DWORD为单位,
这样比以WORD为单位能减少一半的操作次数。
////
16位操作系统下如果使用这种方法获得的将是16位图
biBitCount=16
表示位图最多有2^16种颜色。每个像素用16位(2个字节)表示。这种格式叫作高彩色,或叫增强型16位色,或64K色。它的情况比较复杂,当biCompression成员的值是BI_RGB时,它没有调色板。16位中,最低的5位表示蓝色分量,中间的5位表示绿色分量,高的5位表示红色分量,一共占用了15位,最高的一位保留,设为0。//-》备注1
这种格式也被称作555 16位位图。
内存分布如下
如果biCompression成员的值是BI_BITFIELDS,(const DWORD BI_BITFIELDS = 3;)那么情况就复杂了,首先是原来调色板的位置被三个DWORD变量占据,称为红、绿、蓝掩码。分别用于描述红、绿、蓝分量在16位中所占的位置。在Windows 95(或98)中,系统可接受两种格式的位域:555和565,在555格式下,红、绿、蓝的掩码分别是:0x7C00、0x03E0、0x001F,而在565格式下,它们则分别为:0xF800、0x07E0、0x001F。你在读取一个像素之后,可以分别用掩码“与”上像素值,
从而提取出想要的颜色分量(当然还要再经过适当的左右移操作)。在NT系统中,则没有格式限制,只不过要求掩码之间不能有重叠。(注:这种格式的图像使用起来是比较麻烦的,不过因为它的显示效果接近于真彩,而图像数据又比真彩图像小的多,所以,它更多的被用于游戏软件)。我们只需要读取其中的R或者G的掩码,来判断是那种格式。以红色掩码为例 0111110000000000的时候就是555格式 1111100000000000就是565格式。
555 格式 xrrrrrgggggbbbbb
565 格式 rrrrrggggggbbbbb
解析555格式的代码:
BYTE b= bmpData;
BYTE g=((bmpData》》5);
BYTE r=(bmpData《《1)》》3;
有一点值得提醒的是由于有较多的位操作,所以在处理的时候在前一次操作的上面加上一对括号。
现在我们得到了55RGB各自的分量,但是还有一个新的问题,那就是由于两字节表示了3个颜色 555下每个颜色最多到0x1F。所以我们需要一个转换,这就是掩码的用武之地,将得到的各颜色分量和相应的掩码做与运算,或者乘8就可以了,推荐用与运算。
以下是565格式时的数据分离:
BYTE b= bmpData;
BYTE g=((bmpData》》5);
BYTE r=bmpData》》3;
现在我们得到了565RGB各自的分量,但是仍然还有一个新的问题,565格式下最大的绿色分量也就0x3F。所以我们需要一个转换,这就是掩码的用武之地,将各颜色分量和相应的掩码做与运算,
备注一:
所以这种方法需要通过位移来操作 在写抓屏程序的时候显示不如后边的32位图来的方便, 但是在颜色质量为16位的操作系统下如何抓屏到32位的图像呢
通过上边的两个GetDIBits的方法是不可行的 第一次LPVOID lpvBits, 为空 获得了BITMAPINFO bi信息bi.bmiHeader.biBitCount = 16;
如果想通过修改bi.bmiHeader.biBitCount = 32; 后来第二次调用GetDIBits就会失败 ;
那如何在颜色质量为16位的操作系统下如何获得32位的图像呢?
方法如下:
首先使用 GetObject(HBITMAP bmpScreen) 获得信息
然后通过修改BITMAPINFO 中的biBitCount 为32就可以获得32位结构的LPVOID 了 再进行操作就方便的多了
view plaincopy
BITMAPINFO bi = {0};
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = bmpScreen.bmWidth;
bi.bmiHeader.biHeight = bmpScreen.bmHeight;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 16;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biSizeImage = 0;
bi.bmiHeader.biXPelsPerMeter = 0;
bi.bmiHeader.biYPelsPerMeter = 0;
bi.bmiHeader.biClrUsed = 0;
bi.bmiHeader.biClrImportant = 0;
biBitCount=32
表示位图最多有2^32种颜色。这种位图的结构与16位位图结构非常类似,当biCompression成员的值是BI_RGB时,它也没有调色板,32位中有24位用于存放RGB值,顺序是:最前一字节保留,红8位、绿8位、蓝8位。这种格式也被成为888 32位图。如果biCompression成员的值是BI_BITFIELDS时,原来调色板的位置将被三个DWORD变量占据,成为红、绿、蓝掩码,分别用于描述红、绿、蓝分量在32位中所占的位置。在Windows 95(or98)中,系统只接受888格式,也就是说三个掩码的值将只能是:0xFF0000、0xFF00、0xFF。而在NT系统中,你只要注意使掩码之间不产生重叠就行。
VB用GetDIBits找色
“不知道为什么返回的坐标和实际坐标不一样,有哪位路过的老师能指教一下么?”
----------
单就这个问题来说,DIB位图通常是自下而上的,原点为图形的左下角,这与通常以屏幕或窗口的左上角为原点的习惯有所不同,使用时把y值加个负号就行了

更多文章:
he agreed to help(用need to do sth.和agree to do sth. 造句)
2026年8月31日 01:00
instr函数的返回值(关于vb中InStr函数的一个小问题)
2026年1月26日 22:30
slcp是什么意思(matlab.*和matlab. s有什么区别)
2026年1月3日 12:00
db2数据库update后想恢复(你好,SQL修改错了,修改前没有备份,请问怎么还原到修改前的数据,你知道吗可以给联系方式我请教一下)
2026年9月17日 12:00
assert python用法(python:assert_raises() 怎么用啊,有哪些参数 请说得详细些,先谢谢了!)
2026年7月11日 08:30
this is it歌词好诡异(Turn The Lights On的歌词翻译)
2025年7月26日 02:30
tabletennis安卓游戏(table tennis touch多人游戏时提示game center断开链接怎么解决)
2025年8月8日 17:15
迷你qq头像(2021头像可爱呆萌男生,寻找一些头像小男孩的那种)
2026年2月14日 13:00
















