问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

C++函数GetDIBits怎么用???

发布网友 发布时间:2022-04-30 09:06

我来回答

1个回答

热心网友 时间:2022-06-20 08:02

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[bmpInfo.bmiHeader.biSizeImage];
    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[i*storeWidth+j];

BYTE g=((bmpData[i*storeWidth+j +1]<<6)>>3)+(buffer[i*storeWidth+j]>>5);

BYTE r=(bmpData[i*storeWidth+j +1]<<1)>>3;

有一点值得提醒的是由于有较多的位操作,所以在处理的时候在前一次操作的上面加上一对括号。

现在我们得到了55RGB各自的分量,但是还有一个新的问题,那就是由于两字节表示了3个颜色  555下每个颜色最多到0x1F。所以我们需要一个转换,这就是掩码的用武之地,将得到的各颜色分量和相应的掩码做与运算,或者乘8就可以了,推荐用与运算。

 

以下是565格式时的数据分离:

BYTE b= bmpData[i*storeWidth+j];

BYTE g=((bmpData[i*storeWidth+j+1]<<5)>>2)+(buffer[i*storeWidth+j]>>5);

BYTE r=bmpData[i*storeWidth+j +1]>>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 了 再进行操作就方便的多了


[cpp] 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系统中,你只要注意使掩码之间不产生重叠就行。


追问请写出具体代码,不要复制了..............

追答抱歉啊,我的专业不是程序,而是硬件,对程序的了解也就仅限于此了。

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
七彩虹G808手机显示无命令怎么办 七彩虹手机显示无命令提示,关机也关不了,是怎么回事啊? 七彩虹平板开不了机 提示无命令图标怎么办 脑筋急转弯:冶金学---打一本书(小说)名 【收藏】B站上免费值得看的『影视剧资源』整理 宜搜小说可以看四大名著吗 哪些品牌的乳液质地好? 乳液买什么牌子的好? 哪些品牌的乳液口碑比较好? 什么国产品牌的乳液比较好用? 来电显示无号码怎么回事 来电显示无号码 手机来电显示未知号码如何解决:所有来电都是:未知,没号 为什么打过来的电话显示无号码? WII原装手柄和组装手柄具体有什么区别 求,wii手柄内置加速器与外置的区别大吗 WII PRO手柄跟普通的游戏手柄有什么不同 第一套百元人民币是那一年发行 wii的手柄内置加速器什么意思 面额100元人民币最早是那一年出来 wii的手柄,内置加速器不如外带加速器灵敏吗 wii 没有加速手柄,只有普通的手柄 能玩那些体感游戏??? 第一套100元人民币是哪一年发行的? WII手柄的加速器是怎么个用法 请教如何鉴定Wii的原装加密加速手柄 wii普通原装手柄(不是加速手柄),能玩运动度假胜地吗? wII有加速器和没有加速器的手柄玩游戏有什么区别 国产wii手柄和原装的区别 wii手柄加速和不加速有什么区别吗作用在哪 怎样从外观上区别上WII手柄有内置加速器和没有内置的? 长安之星5面包车通病 长安之星52020版钥匙带遥控吗 长安之星5导航安装步骤 长安之星5新车即将要停车怠速有时发抖、高速有时断火是什么问题_百度问一问 vivoz3i怎么样?值得购买吗? 长安之星5,2座几年一审 如何限制oracle数据库远程连接 长安之星5远近光灯的型号 长安之星5亮灯可以开多久 vivo z3i这款手机好不好,处理器怎么样 LS4aab2d5fa586955车架号是长安那款车 vivoZ3i是否还在生产? 长安之星河北厂生产的那款车? 5.2米的面包车有哪些 vivoZ3i有变形器吗? 长安之星4500后开门的能拉货吗 vivo Z3i 支持指纹解锁吗? 长安跨越王x5大架号在哪 08年长安小货车发动机是什么型号 全城9 新年是1.5还是2.0