第七篇:圖形設備接口(GDI)
我想大家和我一樣通過前幾天的學習,對VC++MFC應用程序框架的神奇功能有了一些了解,但是還是感覺不能駕御這個強的開發工具,不過別擔心,我170多斤體重不是一口吃出來的,是經過了30年不懈的努力才吃成了如此“魁梧”的體形,呵呵開個玩笑。所以學習也一樣。學VC尤其如此。還有我發現很多好的技術類書籍有一個共同的特點,就是在前幾章學到了一些東西在你正在疑惑或者苦苦領會的時候,接下來的章節便給你解除疑惑。雷神經驗:遇到實在想不明白的地方先放下,繼續向下讀,也許讀著讀著前面的問題就明白了。『VC++技術內幕』當然屬於好的技術書籍一類,所以在本書第五章開始仔細的給我們講解設備環境類和圖形設備接口(GDI),使得我們能守得雲開見月明。
設備環境類CDC:
CDC是設備環境類的基類直接由CObject派生。是GDI的關鍵元素,它代表了物理設備。每一個C++設備環境對象都有相對應Windows設備環境,並通過一個32位類型的HDC句柄來標識。CDC類的虛擬性使我們可以很容易的做到編寫同時適用於多種設備的代碼。例如OnDraw函數的pDC->TextOut(0,0,"Hello");既可以適用於顯示器、還可以適用於打印預覽和打印,只需要在CView::OnDraw函數的pDC參數指向不同的對象類。
CClientDC和CWindowDC是顯示設備環境類,都是由CDC派生而來,區別在於CClientDC是窗口的客戶區不包括邊框、標題欄和菜單欄,(0,0)指客戶區域的左上角。CWindowDC的(0,0)指整個屏幕的左上角,這意味著我們可以在顯示器的任意地方繪圖,包括窗口邊框、標題欄和菜單欄等等。CWindowDC一般應用在框架窗口,而不是視圖窗口。
CDC對象被創建後一定要在合適的時候將它刪除掉,如果忘記了刪除設備環境對象則會造成內存丟失。如何做才能避免出現這個問題呢,我們應該在堆棧中構造對象。
看例子7-1
//***************************
// 例子7-1
void CMyView::OnLButtonDown(UINT nFlags,CPoint point)
{
CRect rect;
CClientDC dc(this); //在堆棧中構造設備環境對象,用一個窗口指針this作參數。
dc.GetClipBox(rect); //GetClipBox函數是一個虛函數,作用是可以獲得選定區域的尺寸
}
//析構函數在函數返回時自動調用,也就完成對設備環境對象的刪除。
書上還給出了另一種寫法:
void CMyView::OnLButtonDown(UINT nFlags,CPoint point)
{
CRect rect;
CDC * pDC=GetDC(); //通過調用CWnd的GetDC()函數獲得設備環境指針
pDC->GetClipBox(rect); //可以獲得選定區域的尺寸
ReleaseDC(pDC); //一定不能忘記,釋放設備環境。(書上又寫錯了)
}
創建的設備環境對象具有一些默認的特性,通過CDC類的成員函數可以設定這些特性。例如前一篇筆記用到的刷子、映射模式等等。我們還可以通過重載SelectObject函數將GDI對象選進設備環境中。
GDI對象是通過CGdiObject派生類的C++對象來表示的。讀著怎麼這麼別扭?舉例說一下。
CBrush是一個GDI的派生類,它在MFC中的層次結構是這樣的:CObject派生CGdiObject派生CBrush,明白了吧。CGdiObject是所有GDI對象的抽象基類。下面列出的是GDI派生類的列表:
CBitmap:位圖是一種位矩陣,每一個顯示象素都對應於其中的一個或多個位,可以用來表示圖象,也可以用來創建刷子
CBrush:刷子定義了一種位圖形式的象素,可以用來對區域內部填充顏色。
CFont:字體是一種具有某種風格和尺寸的所有字符的完整集合,常常被作為資源,其中一些依賴某種設備。
CPalette:調色板是一種顏色映射接口,它允許應用程序在不影響其他應用程序的前提下,可以充分利用輸出設備的顏色描繪能力。
CPen:筆是一種用來畫線及繪制有形邊框的工具,可以指定它的顏色及寬度,並可以指定畫虛線、點線還是實線。
CRgn:區域是由多邊形、橢圓二者組合形成的一種范圍,可以用來進行填充、裁剪、鼠標點中測試等等。
以上很容易理解,可以用WINDOWS的畫圖幫助我們理解。
CGdiObject類很眼生,看過很多代碼就沒有看到過它,原因是由於CGdiObject類是所有GDI對象類的虛擬基類,所以我們不必創建CGdiObject類的對象,可以直接構造它的派生類的對象,例如這樣
CPen newPen(PS_DASHDOTDOT,2,(COLORREF) 0); //黑色的筆寬度為2
但需要注意的是CFont和CRgn的對象建立需要先調用默認的構造函數來構造C++對象,然後再調用相應的創建函數如:
CreateFont或CreatePolygonRgn等。
CGdiObject類有一個虛擬的析構函數,它派生類的析構函數需要將與C++對象相關聯的GDI對象刪除掉,一定要在退出程序之前把構造的CGdiObject派生類對象干掉。因為一個沒有釋放的GDI對象會占用很多的內存。
讓我們用一個例子跟蹤一下GDI對象
//*************************************
// 例子7-2
void CMy10View::OnDraw(CDC* pDC)
{
pDC->MoveTo (10,10);
pDC->LineTo (110,10);
CPen newPen(PS_DASHDOTDOT,10,(COLORREF) 192); //紅色的筆寬度為10
CPen * pOldPen=pDC->SelectObject (&newPen);
//在將新對象選進設備環境的同時返回指向前一次被選對象的指針。作用保存原來的對象,以便完成任務時恢復它。
pDC->MoveTo (10,20);
pDC->LineTo (110,20);
pDC->SelectObject (pOldPen);//把原來的對象恢復
pDC->MoveTo (10,30);
pDC->LineTo (110,30);
}
屏幕上應該顯示三條線,第一條和第三條一樣顏色和粗細因為他們都是用的設備環境默認的CPen對象,第二條是一條用我們自己設定的CPen對象。我們可以看出在將新對象選進設備環境的同時返回指向前一次被選對象的指針。作用保存原來的對象,以便完成任務時恢復它。
Windows還包含有一些可以利用的庫存對象,它們不會被刪除,因為Windows對企圖刪除它們的動作不予理睬。我們可以用SelectStockObject函數將它們選進設備環境。下面列出的是所有的有關刷子、筆、字體和調色板的庫存對象。
//***************************************************************************
// MSDN中的內容
BLACK_BRUSH //Black brush.
DKGRAY_BRUSH //Dark gray brush.
GRAY_BRUSH //Gray brush.
HOLLOW_BRUSH //Hollow brush.
LTGRAY_BRUSH //Light gray brush.
NULL_BRUSH //Null brush.
WHITE_BRUSH //White brush.
BLACK_PEN //Black pen.
NULL_PEN //Null pen.
WHITE_PEN //White pen.
ANSI_FIXED_FONT //ANSI fixed system font.
ANSI_VAR_FONT // ANSI variable system font.
DEVICE_DEFAULT_FONT //Device-dependent font.
OEM_FIXED_FONT //OEM-dependent fixed font.
SYSTEM_FONT //The system font. By default, Windows uses the system font
to draw menus, dialog-box controls, and other text. In Windows versions
3.0 and later, the system font is proportional width; earlier versions
of Windows use a fixed-width system font.
SYSTEM_FIXED_FONT //The fixed-width system font used in Windows prior
to version 3.0. This object is available for compatibility with earlier
versions of Windows.
DEFAULT_PALETTE //Default color palette. This palette consists of the
20 static colors in the system palette.
//*******************************************************************************
一個問題:
由於SelectObject函數返回的GDI C++對象指針具有臨時性,當程序的空閒處理階段或者控制函數返回時應用程序框架會將臨時的C++對象刪除,我們不能簡單的把這一指針保存在類的數據成員中,而應該借助GetSafeHdc函數將它轉化為Windows的句柄,以便持久的保存GDI的標識。
我們將例子7-2做些改動。
//**************************************************
// 例子7-3
void CMy10View::OnDraw(CDC* pDC)
{
HPEN m_hPen; //一個指向CPen對象的指針
pDC->MoveTo (10,10);
pDC->LineTo (110,10);
CPen newPen(PS_DASHDOTDOT,10,(COLORREF) 192); //紅色的筆寬度為10
CPen * pOldPen=pDC->SelectObject (&newPen); //在將新對象選進設備環境的同時返回指向前一次被選對象的指針。作用保存原來的對象,以便完成任務時恢復它。
m_hPen=(HPEN)pOldPen->GetSafeHandle ();//獲得並保存原來對象的句柄
pDC->MoveTo (10,20);
pDC->LineTo (110,20);
pDC->SelectObject (CPen::FromHandle (m_hPen));//把原來的對象恢復,和例子7-2不同的是通過句柄
pDC->MoveTo (10,30);
pDC->LineTo (110,30);
}
補充一下IGDI派生類的Windows handle type列表
CPen HPEN
CBrush HBRUSH
CFont HFONT
CBitmap HBITMAP
CPalette HPALETTE
CRgn HRGN
好了我們已經對GDI有了一些了解,下一篇我們將說說顏色和字體,雷神希望大家對我的學習筆記提些建議,因為很多東西也許不必說的這麼羅嗦,可我知道初學VC的痛苦,太多的東西看不明白,所以盡量寫的詳細。文章記錄了我學習時的心得,很多東西是從MSDN查來的,不知道合不合大家(初學者)的口味。
未完待續