兼容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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章