Windows中負責圖形輸出的是GDI(即Graphic Device Interface,圖形設備接口)。這是Windows與硬件無關的圖形輸出模式的體現。GDI建立在硬件抽象層(HAL)之上,屏蔽了不同輸出設備之間的差異,從而爲用戶提供了一個統一的“標準輸出設備”。但是,與DOS不同,Windows是多任務、進程獨立的,每一個窗口都應該有一個獨立的輸出通道。這樣,GDI又使用了一種簡單的機制來保證在窗口中畫圖的不同程序之間能共享“設備”而又互不干擾。這個機制就是DC(Device Context,設備描述表)。
有人把DC比喻成畫家的畫室,這裏有畫布、畫刷、畫筆等等很多工具。
就畫布而言,畫布形式可以不同,是的,我可以在桌上(desktop)的紙上(window)畫,也可以就畫在桌面上,還可以畫在牆上(管的着嗎!^_^)。
爲此,Windows MFC提供了四種不同的DC環境(封裝爲C++類),以標明不同的繪製權限,即:
CPaintDC, 用於在窗口客戶區畫圖(僅限於在OnPaint處理函數中使用);
CClientDC, 也用於在窗口客戶區畫圖(限於在OnPaint處理函數之外使用);
CWindowDC, 用於在窗口內任意地方畫圖,包括非客戶區;
CMetaFileDC, 用於繪製GDI圖元文件。
這些類都可以直接實例化,如:
CPaintDC dc(this);//this表示此DC所屬窗口爲當前窗口
創建了一個CPaintDC對象dc。
CWindowDC一般不常用,如果想在窗口非客戶區畫圖,可藉助OnNcPaint()處理函數捕獲WM_NCPAINT消息。
剛纔說了,DC中還有畫刷、畫筆等。這些都是DC的屬性,可通過DC自身(調用其成員函數)獲得。
DC屬性包括文本顏色、背景顏色、映射模式、繪圖模式、當前位置、當前畫筆(刷)和當前字體等。
畫筆(Pen)、畫刷(Brush)都是獨立的GDI對象,可通過CDC成員函數SelectObject()選入DC。同樣操作方式的對象還有字體(Font)、位圖(Bitmap)、調色板(Palette)和區域(Region)。
關於SelectObject():在創建好GDI對象之後,SelectObject()接收該對象指針,其返回值爲先前選入DC的相同類型的對象指針(通常用來恢復DC用)。
Windows也預先定義了一些畫筆、畫刷、字體以及其他一些GDI對象。這些對象稱爲備用對象,用CGdiObject::SelectStockObject()選入。CGdiObject是表示GDI對象的CPen、CFont等類的基類。備用對象的屬性說明可以查MSDN。
由CGdiObject派生類創建的畫筆、畫刷和其他對象都要佔用內存資源,因此使用後一定要刪除它們。處理方法與其他window對象類似。在棧中創建的對象,當此CGdiObject超出範圍時會自動析構。在堆中用new創建的CGdiObject對象,可通過調用CGdiObject::DeleteObject顯式刪除(這會引起對GDI對象析構函數的調用)或用相應的delete運算符。如果是備用對象,則沒必要專門刪除,留給windows就可以了。
VC++有一種簡單的方法用來確定是否成功的刪除了GDI對象:只要在調試狀態下運行應用程序的調試版本即可。在應用程序結束時,沒有釋放的資源會顯示在調試窗口中。
一些知識點:
1.繪圖模式:GDI將象素點輸出到邏輯顯示平面上時,並不只是簡單的輸出象素顏色,而是通過一系列布爾運算將輸出象素點的顏色和輸出目標位置上象素點的顏色進行合成再顯示。所使用的邏輯運算關係就由DC的繪圖模式確定。使用CDC:SetROP2()可更改繪圖模式(ROP2,Raster Operation To)。默認繪圖模式是R2_COPYPEN,直接將象素點輸出到顯示平面上。常用的還有R2_NOT,用於實現鼠標繪圖時的Rubber技術。
2.映射模式:這是DC的屬性之一,用於確定從邏輯座標到設備座標的轉換(或者說mapping,映射)方式。設備座標是指窗口中相應的象素點位置。單位只有象素一種。而邏輯座標依映射模式不同而單位各異。如默認的MM_TEXT模式下,一個單位恰恰是一個象素點。MM_LOENGLISH模式下,一個單位則相當於百分之一英寸長。windows支持8種映射模式,其中的MM_ISOTROPIC和MM_ANISOTROPIC是可編程的。這意味着是用戶而不是windows確定從邏輯座標到設備座標的轉換方式。換句話說,就是用戶來自行定製邏輯單位的實際大小。這兩種模式最常用於根據窗口尺寸按比例自動調節畫圖輸出大小的場合。兩者之間的唯一區別在於:前者中X方向和Y方向具有同一縮放比例因子。設置映射模式的函數是CDC::SetMapMode()。
3.座標系常用技術:
1)座標變換:調用CDC::LPtoDP可將邏輯座標值轉換爲設備座標值。反之,調用CDC::DPtoLP可將設備座標值轉換爲邏輯座標值。在相應鼠標單擊區域命中測試時,將使用這兩個函數。因爲鼠標單擊得到的是設備座標,而很多GDI函數使用邏輯座標,必須轉換爲統一座標,操作纔有意義。
2)原點移動:默認方式下,DC的原點位於相應顯示平面的左上角。MFC提供了兩個CDC類成員函數,可改變原點位置。CDC::SetWindowOrg()移動窗口原點,CDC::SetViewportOrg()移動視圖原點(詳見MSDN)。一般只能使用兩者之一。原點移動技術在邊框滾動條設置時有所體現。
3)用戶座標與屏幕座標:前者是設立在窗口客戶區左上角的設備座標值,後者是原點位於屏幕左上角的設備座標值。兩者的相互轉換用函數是CWnd::ClientToScreen()和CWnd::ScreenToClient()。
4.GDI繪圖函數
1)畫線:包括MoveTo、LineTo、Polyline、PolylineTo、Arc、ArcTo、PolyBezier等。其中Polyline和PolylineTo都可以一次畫多條線,兩者的區別是PolylineTo使用DC的當前位置,而Polyline則不需要。ArcTo在win98下未實現。另外,所有GDI畫線函數有一個共同特點,就是從不畫最後一個象素點。
2)畫圖形:畫封閉圖形的GDI函數以外接矩形的座標爲參數。常用函數有Rectangle、Ellipse、RoundRect、Pie、Chord等。
5.畫筆與畫刷:在MFC中,畫筆對象封裝爲CPen類,畫刷對象封裝爲CBrush類。創建兩者都有三種方法。最簡單的就是構造一個CPen對象,直接給出所用參數進行初始化。第二種方法是調用一個未初始化的CPen對象,用CreatePen()初始化。還有一種方法是構造一個未初始化的CPen對象,通過向LOGPEN結構中填充描述畫筆特性的參數,然後調用CPen::CreatePenIndirect()生成畫筆。畫刷基本相同。
1)CPen:windows用當前選入DC的畫筆繪製線條,並給封閉圖形鑲制邊框。畫筆有三個特性,即樣式、寬度和顏色。樣式有PS_SOLID、PS_INSIDEFRAME、PS_NULL等。PS_NULL一般稱其爲“NULL畫筆”,想畫一個沒有邊框的圖形,就用到它了。筆寬以邏輯單位給出,實際意義與當前映射模式相同。顏色是通過RGB宏把三個獨立顏色成分的值合成爲一個可傳遞給GDI的COLORREF值來確定的。
2)CBrush:畫刷有三種基本類型,即單色、帶陰影線和帶圖案。其中圖案畫刷允許用位圖填充圖形內部(這樣,窗口背景也就沒啥新鮮的了)。常用函數有
CDC::SetBkMode()、CDC::SetBkColor()等。
6.文本與字體:CDC有一打文本處理函數,重要的幾個是DrawText、TextOut、GetTextMetrics、SetTextColor和SetTextAlign等。其中GetTextMetrics()傳給TEXTMETRIC結構關於字體性質的相關信息。字體是一組具有特定尺寸(高度)和字樣的字符;字樣指示字體共有屬性,如粗細等。字體封裝在CFont類中,建立字體對象,要在CFont構造之後,再調用CFont的成員函數CreateFont或CreateFontIndirect等來建立GDI字體資源。針對字體,有一個LOGFONT結構,其中定義了字體的所有特性。也可通過填充它來創建字體。