1.基本概念
GDI在Windows中定義為Graphics Device Interface,即圖形設備接口,是Windows API(Application Programming Interface)的一個(gè)重要組成部分。它是Windows圖形顯示程序與實(shí)際物理設備之間的橋梁,GDI使得用戶(hù)無(wú)需關(guān)心具體設備的細節,而只需在一 個(gè)虛擬的環(huán)境(即邏輯設備)中進(jìn)行操作。它的橋梁作用體現在:
(1)用戶(hù)通過(guò)調用GDI函數將邏輯空間的操作轉化為具體針對設備驅動(dòng)程序的調用。
為實(shí)現圖形設備無(wú)關(guān)性,Windows的繪圖操作在一個(gè)設備描述表上進(jìn)行。用戶(hù)擁有自己的"邏輯坐標"系統,它獨立于實(shí)際的物理設備,與"設備坐標"相對應。開(kāi)發(fā)Windows應用程序時(shí),程序員關(guān)心的是邏輯坐標,我們在邏輯坐標系上繪圖,利用GDI將邏輯窗口映射到物理設備上。
(2)GDI能檢測具體設備的能力,并依據具體的設備以最優(yōu)方式驅動(dòng)這些設備,完成真實(shí)的顯示。
GDI函數大致可分類(lèi)為:設備上下文函數(如GetDC、CreateDC、DeleteDC)、 畫(huà)線(xiàn)函數(如LineTo、Polyline、Arc)、填充畫(huà)圖函數(如Ellipse、FillRect、Pie)、畫(huà)圖屬性函數(如 SetBkColor、SetBkMode、SetTextColor)、文本、字體函數(如TextOut、GetFontData)、位圖函數(如 SetPixel、BitBlt、StretchBlt)、坐標函數(如DPtoLP、LPtoDP、ScreenToClient、 ClientToScreen)、映射函數(如SetMapMode、SetWindowExtEx、SetViewportExtEx)、元文件函數 (如PlayMetaFile、SetWinMetaFileBits)、區域函數(如FillRgn、FrameRgn、InvertRgn)、路徑函 數(如BeginPath、EndPath、StrokeAndFillPath)、裁剪函數(如SelectClipRgn、 SelectClipPath)等。
GDI雖然使程序員得到了一定程度的解脫,但是其編程方式仍很麻煩。譬如,顯示一張位圖,程序員需要進(jìn)行"裝入位圖―讀取位圖文件頭信息―啟用設備場(chǎng)景―調色板變換"等一連串操作。而有了GDI+,這些問(wèn)題便迎刃而解了。
顧名思義,GDI+是GDI的增強版。它是微軟在Windows 2000以后操作系統中提供的新接口,其通過(guò)一套部署為托管代碼的類(lèi)來(lái)展現,這套類(lèi)被稱(chēng)為GDI+的"托管類(lèi)接口"。GDI+主要提供了以下三類(lèi)服務(wù):
(1) 二維矢量圖形:GDI+提供了存儲圖形基元自身信息的類(lèi)(或結構體)、存儲圖形基元繪制方式信息的類(lèi)以及實(shí)際進(jìn)行繪制的類(lèi);
(2) 圖像處理:大多數圖片都難以劃定為直線(xiàn)和曲線(xiàn)的集合,無(wú)法使用二維矢量圖形方式進(jìn)行處理。因此,GDI+為我們提供了Bitmap、Image等類(lèi),它們可用于顯示、操作和保存BMP、JPG、GIF等圖像格式。
(3) 文字顯示:GDI+支持使用各種字體、字號和樣式來(lái)顯示文本。
GDI接口是基于函數的,而GDI+是基于C++類(lèi)的對象化的應用程序編程接口,因此使用起來(lái)比GDI要方便。
2.例程簡(jiǎn)述
單擊此處下載本文例程源代碼。
本文后續的講解都基于這樣的一個(gè)例子工程(例程的開(kāi)發(fā)環(huán)境為Visual C++6.0,操作系統為Windows XP),它是一個(gè)基于對話(huà)框的MFC應用程序,包括2個(gè)父菜單:
(1) GDI
GDI父菜單下包括一個(gè)子菜單:
ID:IDM_GDI_DRAW_LINE caption:畫(huà)線(xiàn)
單擊事件:在窗口繪制正旋曲線(xiàn)
(2) GDI+
DIB位圖父菜單下包括兩個(gè)子菜單:
a. ID:IDM_GDIP_DRAW_LINE caption:畫(huà)線(xiàn)
單擊事件:在窗口繪制正旋曲線(xiàn)
b. caption:新增功能,其下又包括下列子菜單:
(ⅰ)ID:IDM_Gradient_Brush caption:漸變畫(huà)刷
單擊事件:在窗口演示GDI+的漸變畫(huà)刷功能
(ⅱ)ID:IDM_Cardinal_Spline caption:基數樣條
單擊事件:在窗口演示GDI+的基數樣條函數功能
(ⅲ)ID:IDM_Transformation_Matrix caption:變形和矩陣對象
單擊事件:在窗口演示GDI+的變形和矩陣對象功能
(ⅳ)ID:IDM_Scalable_Region caption:可伸縮區域
單擊事件:在窗口演示GDI+的可伸縮區域功能
(ⅴ)ID:IDM_IMAGE caption:圖像
單擊事件:在窗口演示GDI+的多種圖像格式支持功能
(ⅵ)ID:IDM_Alpha_Blend caption:Alpha混合
單擊事件:在窗口演示GDI+的Alpha混合功能
(ⅶ)ID:IDM_TEXT caption:文本
單擊事件:在窗口演示GDI+的強大文本輸出能力
后續篇章將集中在對上述菜單單擊事件消息處理函數的講解,下面的代碼是整個(gè)對話(huà)框類(lèi)CGdiexampleDlg的消息映射:
| BEGIN_MESSAGE_MAP(CGdiexampleDlg, CDialog) |
3.GDI編程
頭文件:#include "math.h "
#define PI 3.1415926
"GDI"菜單下的"畫(huà)線(xiàn)"子菜單單擊事件消息處理函數的代碼如下:
| void CGdiexampleDlg::OnGdiDrawLine() |
單擊這個(gè)按鈕,會(huì )出現如圖1所示的效果,我們來(lái)對此進(jìn)行解讀。
|
|
前文提到,GDI編程需進(jìn)行設備坐標和邏輯坐標的轉化。而屏幕上的設備坐標通常會(huì )按客戶(hù)坐標給出,客戶(hù)坐標依賴(lài)于窗口的客戶(hù)區域,其起始位置位于客戶(hù)區域的左上角。為示區別,圖2給出了設備坐標和用戶(hù)邏輯坐標的示例。
|
|
設備坐標與邏輯坐標的轉換關(guān)系如下:
公式中的<Xvorg, Yvorg>是設備空間中視口的原點(diǎn),而< Xworg, Yworg >是邏輯空間中窗口的原點(diǎn)。 Xwext/Xvext和Ywext/Yvext分別是窗口與視口水平和垂直范圍的比例。
因此,經(jīng)過(guò)程序中的dc.SetWindowOrg (0,0) 和dc.SetViewportOrg (0,rect.bottom/2)語(yǔ)句我們設置了視口和窗口的原點(diǎn);而經(jīng)過(guò)程序中的dc.SetWindowExt (rect.right,rect.bottom) 和dc.SetViewportExt (rect.right,-rect.bottom) 語(yǔ)句我們設置了視口和窗口的范圍。由于視口和窗口的縱坐標方向相反,設置視口的垂直范圍為負值。這樣我們得到了一個(gè)邏輯坐標原點(diǎn)為客戶(hù)區水平方向最左邊和垂直方向居中的坐標系,我們在這個(gè)坐標系上直接繪制正旋曲線(xiàn),不需要再理睬Windows對話(huà)框客戶(hù)區坐標了。
void CGdiexampleDlg::OnGdiDrawLine()函數中未指定邏輯設備和物理設備的映射模式,則為缺省的MM_TEXT。在這種模式下,一個(gè)邏輯單位對應于一個(gè)像素點(diǎn)。映射模式是GDI中的一個(gè)重要概念,其它的映射模式還有MM_LOENGLlSH、MM_HIENGLISH、 MM_LOMETRIC和MM_HIMETRIC等。我們可以通過(guò)如下語(yǔ)句指定映射模式為MM_TEXT:
| dc.SetMapMode(MM_TEXT); |
值得一提的是,從上述代碼可以看出:在GDI編程中,幾乎所有的操作都圍繞設備上下文dc展開(kāi)。的確,這正是GDI編程的特點(diǎn)!設備上下文是Windows 使用的一種結構,所有GDI操作前都需取得特定設備的上下文,函數中的CClientDC dc (this) 語(yǔ)句完成這一功能。
歸納可得,利用GDI進(jìn)行圖形、圖像處理的一般操作步驟為:
1. 取得指定窗口的DC;
2. 確定使用的坐標系及映射方式;
3. 進(jìn)行圖形、圖像或文字處理;
4. 釋放所使用的DC。
5. GDI+編程
"GDI+"菜單下的"畫(huà)線(xiàn)"子菜單單擊事件消息處理函數的代碼如下:
| void CGdiexampleDlg::OnGdipDrawLine() |
由于我們使用的是Visual C++6.0而非VS.Net,我們需要下載微軟的GDIPLUS支持包。在微軟官方網(wǎng)站下載時(shí)需認證Windows為正版,我們可從這個(gè)地址下載: http://www.codeguru.com/code/legacy/gdi/GDIPlus.zip。一個(gè)完整的GDI+支持包至少包括如下文 件:
(1)頭文件:gdiplus.h
(2)動(dòng)態(tài)鏈接庫的.lib文件:gdiplus.lib
(3)動(dòng)態(tài)鏈接庫的.dll文件:gdiplus.dll
少了(1)、(2)程序不能編譯,少了(3)程序能以共享DLL的方式編譯但是不能運行,運行時(shí)找不到.dll文件。
為使得Visual C++6.0支持GDI+,我們需要在使用GDI+對象的文件的開(kāi)頭添加如下代碼:
| #define UNICODE |
在Visual C++中使用GDI+必須先進(jìn)行GDI+的初始化,我們在CWinApp派生類(lèi)的InitInstance函數中進(jìn)行此項工作是最好的:
| ///////////////////////////////////////////////////////////////////////////// |
單擊"GDI+"菜單下的"畫(huà)線(xiàn)"子菜單,也會(huì )出現如圖1所示的效果。觀(guān)察void CGdiexampleDlg::OnGdipDrawLine() 函數,我們發(fā)現用GDI+進(jìn)行圖形、圖像操作的步驟為:
(1)創(chuàng )建 Graphics 對象:Graphics 對象表示GDI+繪圖表面,是用于創(chuàng )建圖形圖像的對象;
(2)使用 Graphics 對象繪制線(xiàn)條和形狀、呈現文本或顯示與操作圖像。
Graphics 對象是GDI+的核心,GDI中設備上下文dc和Graphics 對象的作用相似,但在GDI中使用的是基于句柄的編程模式,而GDI+中使用的則是基于對象的編程模式。Graphics封裝了GDI+ 繪圖面,而且此類(lèi)無(wú)法被繼承,它的所有成員函數都不是虛函數。
下面,我們來(lái)逐個(gè)用實(shí)際代碼實(shí)現GDI+的新增功能,這些新增功能包括:漸變的畫(huà)刷(Gradient Brushes)、基數樣條函數(Cardinal Splines)、持久的路徑對象(Persistent Path Objects)、變形和矩陣對象(Transformations &Matrix Object)、可伸縮區域(Scalable Regions)、Alpha混合(Alpha Blending)和豐富的圖像格式支持等。
漸變的畫(huà)刷
GDI+提供了用于填充圖形、路徑和區域的線(xiàn)性漸變畫(huà)刷和路徑漸變畫(huà)刷。
線(xiàn)性漸變畫(huà)刷使用漸變顏色來(lái)填充圖形。
當用路徑漸變畫(huà)刷填充圖形時(shí),可指定從圖形的一部分移至另一部分時(shí)畫(huà)刷顏色的變化方式。例如,我們可以只指定圖形的中心顏色和邊緣顏色,當畫(huà)刷從圖形中間向外邊緣移動(dòng)時(shí),畫(huà)刷會(huì )逐漸從中心顏色變化到邊緣顏色。
| void CGdiexampleDlg::OnGradientBrush() |
本程序使用線(xiàn)性漸變畫(huà)刷,當畫(huà)刷從客戶(hù)區左上角移向客戶(hù)區右下角的過(guò)程中,顏色逐漸由藍色轉變?yōu)榫G色。
|
|
基數樣條函數
GDI+支持基數樣條,基數樣條指的是一連串單獨的曲線(xiàn),這些曲線(xiàn)連接起來(lái)形成一條較大的曲線(xiàn)。樣條由點(diǎn)(Point結構體)的數組指定,并通過(guò)該數組中的每一個(gè)點(diǎn)?;鶖禈訔l平滑地穿過(guò)數組中的每一個(gè)點(diǎn)(不出現尖角),因此比用直線(xiàn)連接創(chuàng )建的路徑精確。
| void CGdiexampleDlg::OnCardinalSpline() { |
圖4演示了直接連線(xiàn)和經(jīng)過(guò)基數樣條平滑擬合后的線(xiàn)條的對比,后者的曲線(xiàn)(Curve)沒(méi)有尖角。這個(gè)工作我們在中學(xué)的數學(xué)課上把離散的點(diǎn)連接成曲線(xiàn)時(shí)做過(guò)。

圖4 GDI+基數樣條
持久的路徑對象
在GDI中,路徑隸屬于一個(gè)設備上下文,一旦設備環(huán)境指針超過(guò)它的生存期,路徑也會(huì )被刪除。利用GDI+,可以創(chuàng )建并維護與Graphics對象分開(kāi)的GraphicsPath 對象,它不依賴(lài)于Graphics對象的生存期。
變形和矩陣對象
GDI+提供了Matrix對象,它是一種可以使變形(旋轉、平移、縮放等) 簡(jiǎn)易靈活的強大工具,Matrix對象需與要被變形的對象聯(lián)合使用。對于GraphicsPath類(lèi),我們可以使用其成員函數Transform接收Matrix參數用于變形。
| void CGdiexampleDlg::OnTransformationMatrix() |
圖5演示了正方形經(jīng)過(guò)旋轉和拉伸之后的效果:黑色的為原始圖形,紅色的為旋轉45度之后的圖形,藍色的為經(jīng)過(guò)拉伸為平行四邊形后的圖形。
|
|
可伸縮區域
GDI+通過(guò)對區域(Region)的支持極大地擴展了GDI。在GDI 中,區域存儲在設備坐標中,可應用于區域的唯一變形是平移。但是在GDI +中,區域存儲在全局坐標(世界坐標)中,可對區域利用變形矩陣進(jìn)行變形(旋轉、平移、縮放等)。
| void CGdiexampleDlg::OnScalableRegion() |
上述程序中以藍色填充一個(gè)三角形區域,接著(zhù)將此區域旋轉和拉伸,再次顯示,其效果如圖6。

圖6 GDI+區域變形
豐富的圖像格式支持
GDI +提供了Image、Bitmap 和Metafile 類(lèi),方便用戶(hù)進(jìn)行圖像格式的加載、操作和保存。GDI+支持的圖像格式有BMP、GIF、JPEG、EXIF、PNG、TIFF、ICON、WMF、EMF等,幾乎涵蓋了所有的常用圖像格式。
| void CGdiexampleDlg::OnImage() |
上述程序將D盤(pán)根目錄下文件名為"1.jpg"的jpg圖像以矩陣和平行四邊形兩種方式顯示,效果如圖7。
|
|
由此我們可以看出,GDI+在圖像顯示和操作方面的確比GDI簡(jiǎn)單許多?;貞浳覀冊凇?span style="COLOR: #0033ff" lang="EN-US">Visual C++中DDB與DIB位圖編程全攻略》一文中所介紹的用GDI顯示位圖的方式,其與GDI+圖像處理的難易程度真是有天壤之別。
Alpha混合
Alpha允許將兩個(gè)物體混合起來(lái)顯示,在3D氣氛和場(chǎng)景渲染等方面有廣泛應用。它能"霧化"圖像,使得一個(gè)圖像著(zhù)色在另一個(gè)半透明的圖像上,呈現一種朦朧美。我們知道,一個(gè)像素可用 R,G,B三個(gè)維度來(lái)表示,我們可以再加上第4個(gè)即:Alpha維度(channel),表征透明程度。
| void CGdiexampleDlg::OnAlphaBlend() |
上述程序中將D盤(pán)根目錄下文件名為"1.jpg"的圖像以矩陣和平行四邊形兩種方式顯示,然后將文件名為為"2.jpg"的圖像與之進(jìn)行混合,其效果如圖8。
|
|
為了能進(jìn)行Alpha混合,我們需要使用ImageAttributes類(lèi)和ColorMatrix矩陣,ImageAttributes可以進(jìn)行顏色、灰度等調整從而達到控制圖像著(zhù)色方式的目的。ColorMatrix是ImageAttributes類(lèi)大多數函數的參數,它包含了Alpha、 Red、Green、Blue維度的值,以及另一維w,順序為RGBaw。
CGdiexampleDlg:: OnAlphaBlend()函數中ColorMatrix的實(shí)例ClrMatrix中元素(4,4)的值為0.5,表示Alpha度的值為0.5(即半 透明)。在ColorMatrix中,元素(5,5)的值恒定為1.0。我們把ClrMatrix的元素(0,0)修改為0.0,就是使得圖像2.jpg的 紅色維度全不顯示,再看效果,為圖9。列位讀者,我們以前在豪杰超級解霸中調整R,G,B值從而控制圖像輸出顏色的時(shí)候,調的就是這個(gè)東東!圖9的效果很 像破舊彩色電視機,紅色電子槍"嗝"了。剛大學(xué)畢業(yè)時(shí),俺那個(gè)叫窮啊,就買(mǎi)了這么個(gè)電視機,還看得很爽,真是往事不堪回首!

圖9 GDI+中的ColorMatrix
強大的文字輸出
GDI+擁有極其強大的文字輸出處理能力,輸出文字的顏色、字體、填充方式都可以直接作為Graphics類(lèi)DrawString成員函數的參數進(jìn)行設置,其功能遠勝過(guò)GDI設備上下文的TextOut函數。
| void CGdiexampleDlg::OnText() |
上述代碼的執行效果如圖10所示,字體、顏色和填充都很豐富!

圖10 GDI+文本輸出
5.GDI與GDI+的比較
GDI+相對GDI而言主要在編程方式上發(fā)生了巨大的改變。
GDI的核心是設備上下文,GDI函數都依賴(lài)于設備上下文句柄,其編程方式是基于句柄的;GDI+無(wú)需時(shí)刻依賴(lài)于句柄或設備上下文,用戶(hù)只需創(chuàng )建一個(gè)Graphics 對象,就可以用面向對象的方式調用其成員函數進(jìn)行圖形操作,編程方式是基于對象的。
GDI在使用設備上下文繪制線(xiàn)條之前,必須先調用SelectObject 以使鋼筆對象和設備上下文關(guān)聯(lián)。其后,在設備上下文中繪制的所有線(xiàn)條均使用該鋼筆,直到選擇另一支不同的鋼筆為止。CGdiexampleDlg:: OnGdiDrawLine函數中的下列語(yǔ)句完成的就是這個(gè)功能:
| //創(chuàng )建繪制正旋曲線(xiàn)的pen并將其選入設備上下文 |
但是,在GDI+中,只需將Pen對象直接作為參數傳遞給Graphics類(lèi)的DrawLine等方法即可,而不必使Pen對象與Graphics對象關(guān)聯(lián),例如CGdiexampleDlg::OnGdipDrawLine函數中的下列語(yǔ)句:
| Pen myPen(Color::Red); |
GDI中有當前位置的概念,所以在使用GDI繪制線(xiàn)條前應該先使用MoveTo移動(dòng)當前位置,再使用LineTo畫(huà)線(xiàn),例如:
| //繪制正旋曲線(xiàn) |
而GDI+中則沒(méi)有當前位置的概念,畫(huà)線(xiàn)函數中可以直接指定起點(diǎn)和終點(diǎn),例如:
| graphics.DrawLine(&myPen,0,0,rect.right,0); |
6.結論
鑒于GDI+良好的易用性和其具有的強大功能,我們建議盡快拋棄GDI編程方式,因為我們沒(méi)有必要將時(shí)間浪費在無(wú)意義的重復代碼的設計上。GDI+對 GDI的增強,某種意義上類(lèi)似于MFC對Windows API的整理和封裝。作為一種良好的"生產(chǎn)工具",它必將大大地促進(jìn)開(kāi)發(fā)時(shí)的"生產(chǎn)力"。
聯(lián)系客服