兼容CDC保存整个屏幕程序以及从此引申的思考

转自:http://hi.baidu.com/lysygyy/item/fc26b9121acb62cd38cb3003


今天在QQ上和一个CSDN的专家闲聊,因为他是一所大学的老师,所以,我们还是蛮谈的来的,突然他发来一段代码,让我解释一下原理,我这个郁闷,是不是这位老兄又在故意刁难我(因为之前被他刁难过好几次),万一某一句说的不好,让他抓住把柄把我嘲笑一番就丢人死了,哎,硬着头皮看看吧,谁让人家是老师呢,其实这段程序并不难理解,代码如下,为了方便大家共同学习,我加上了很具体很具体的注释,看看吧,不信你看不懂:

//HWND GetDesktopWindow()返回桌面窗口的句柄
CDC* pdeskDC = GetDesktopWindow()->GetDC();//获取桌面窗口上下文环境的指针(句柄)

CRect rect;
GetDesktopWindow()->GetClientRect(rect);//获取桌面屏幕的客户区域

CDC memDC;//定义一个内存上下文环境
//HDC CreateCompatibleDC(HDC hdc),该函数创建一个与指定设备兼容的内存设备上下文环境
memDC.CreateCompatibleDC(pdeskDC);

//在一个应用程序可以使用内存设备上下文环境进行绘图操作之前,
//它必须选择一个高和宽都正确的位图到设备上下文环境中,
//这可以通过使用CreateCompatibleBitmap函数指定高、宽和色彩组合以满足函数调用的需要。

CBitmap bmp;
//HBITMAP CreateCompatibleBitmap(HDC hdc,int nWidth,int nHeight)
//该函数创建与指定的设备环境相关的设备兼容的位图。

bmp.CreateCompatibleBitmap(pdeskDC,rect.Width(),rect.Height());//创建兼容位图,并指定宽高

memDC.SelectObject(&bmp);//将位图选入内存上下文

BITMAP bitmapinfo;//定义一个BITMAP结构,此结构定义了逻辑位图的高,宽,颜色格式和位值。

bmp.GetBitmap(&bitmapinfo);//本函数用于查看CBitmap对象的信息。返回的信息存放在BITMAP结构中。

int panelsize = 0;//记录调色板大小
if(bitmapinfo.bmBitsPixel<16){//判断是否为真彩色位图
  
   //如果位图使用的颜色数目不是16那么调色板尺寸为bmBitsPixel*sizeof(RGBQUAD)
   panelsize = pow(2,bitmapinfo.bmBitsPixel*sizeof(RGBQUAD));//RGBQUAD结构标识了像素所用到的颜色数据

}
BITMAPINFO *pBInfo = (BITMAPINFO*)LocalAlloc(LPTR,sizeof(BITMAPINFO)+panelsize);
//LocalAlloc函数用来为数据分配局部堆内存
//BITMAPINFO结构中的BITMAPINFOHEADER结构,保存位图信息
//填充位图信息头

//填充位图信息头代码略去

//从源设备上下文拷贝位图到这个当前设备上下文,即从pdeskdc拷贝位图到memDC
memDC.BitBlt(0,0,bitmapinfo.bmWidth,bitmapinfo.bmHeight,pdeskDC,0,0,SRCCOPY);                  

等我加上注释后,准备把我注释后的代码给这位老兄发过去时,他似乎下线了,这家伙,又拿我开涮,白白浪费我宝贵的看电影时间(哈哈,顺便提一句,用PPStream看电影还是蛮不错的),正当我准备继续看电影的时候,这位老兄又出现了。下面是我们接下来的对话。

他说:“我的一个学生提出了一个关于这段代码的问题,把我说晕了。”

我问:“哪句代码有问题?”

他问曰:“屏幕是什么时候保存在位图bmp中的?”

我说:“是通过CreateCompatibleBitmap函数、SelectObject函数和BitBlt函数这三个函数一起作用下保存在内存设备上下文memDC中的。”

他说:“三个函数共同作用?我还有一个问题。”

我说:“那你说说第二个问题吧,一起说出来我一起看看。”

他说:“.SelectObject函数已经将位图bmp放入内存设备上下文memDC当中了,为什么还要用BitBlt函数再将位图bmp从pdeskDC拷贝到内存设备上下文menDC中呢?这次不是多余了么?”

这下我终于明白他的问题原因了,是因为这位老兄没有真正搞清楚位图文件是如何一步步形成的。解释了一下午终于让他清醒了过来。其实这两个问题应该算作一个问题,还是值得去讨论下的,现在我来给各位解释一下这个问题。

首先先说一下位图文件,位图文件是由四部分构成的:

1、位图文件头(可用BITMAPFILEHEADER结构来描述)。

2、位图信息头(可用BITMAPINFOHEADER结构来描述)。

3、彩色表(可用RGBQUAD结构来描述)

4、位图数据块(紧跟在彩色表之后的图像数据字节阵列)

我再来讲一下CreateCompatibleBitmap函数、SelectObject函数和BitBlt函数的功能。

BOOL CreateCompatibleBitmap( CDC* pDC, int nWidth, int nHeight ):初始化一个于指定设备上下文兼容的位图,这个位图与指定的设备上下文有相同的颜色位面数或相同的每个像素的位数。

CBitmap* SelectObject( CBitmap* pBitmap ):将对象选入设备上下文中。

BOOL BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop ):从源设备上下文拷贝位图到这个当前设备上下文。

看完这三个函数的功能,下面就好解释位图文件的形成过程了,首先,CreateCompatibleBitmap函数被调用时返回的位图对象bmp中只包含相应的设备描述表中的位图信息头,不包含颜色表和位图数据块。然后调用SelectObject将位图选入内存设备上下文memDC之后,还要调用BitBlt函数将颜色表和位图数据块从源设备拷贝至内存设备,到此为止,有了位图的几个部分根据位图的文件结构,就可以将其存储为位图文件或者组合成为一个位图数据流。

如果不太理解也没有关系,因为这个属于比较底层的东西,在做利用兼容CDC保存图形、屏幕的时候就请记住以下这样一个顺序。

第一步:用GetDesktopWindow()->GetDC()函数创建一个显示设备上下文pdeskDC,它对应了整个显示屏幕.

第二步:调用CreateCompatibleBitmap创建一个与显示设备上下文兼容的位图bmp

第三步:调用CreateCompatibleBitmap创建一个与显示设备上下文兼容的内存设备上下文memDC

第四步:用SelectObject函数将创建的位图选入内存设备上下文

第五步:调用BitBlt函数将显示设备上下文指定的区域(即指定的屏幕区域)拷贝到内存设备上下文中.

第六步:从内存设备上下文中获得位图句手柄

最后补充一个概念:真彩色位图,以及调色板。通常我们所说的多少位的位图,这个“位”代表位图中每像素所占的位数。在我们的程序当中有这样一部分代码:

int panelsize = 0;//记录调色板大小
if(bitmapinfo.bmBitsPixel<16){//判断是否为真彩色位图
  
   //如果位图使用的颜色数目不是16那么调色板尺寸为bmBitsPixel*sizeof(RGBQUAD)
   panelsize = pow(2,bitmapinfo.bmBitsPixel*sizeof(RGBQUAD));//RGBQUAD结构标识了像素所用到的颜色数据

}

我来解释一下,通常我们把16位和高于16位的位图成为真彩色位图,在真彩色位图里面是不需要调色板的。而小于16的位图需要调色板。下面我来解释一下原因,在16位的位图,每像素所占的位置有555和565两种方式:

555 方式:0rrrrrggggghhhhh

565方式:rrrrrgggggghhhhh

而16位以上的位图比如24位,已经占用了三个字节,完全可以表示RGB了,没有必要使用调色板了。32位占了4个字节,也完全可以表示RGB了,那么64同样如此。

好了,由这个问题我们还引申了好多值得讨论的东西,最后说明一点,VC++6.0关于图形这一块这个难点,因为一些概念都是很虚抽象的,要注意。

发布了20 篇原创文章 · 获赞 11 · 访问量 15万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章