Windows+GDI和GDI+編程實例剖析
-
date post
15-Oct-2014 -
Category
Documents
-
view
233 -
download
3
Transcript of Windows+GDI和GDI+編程實例剖析
1.基本概念
GDI 在 Windows 中定義為 Graphics Device Interface,即圖形裝置介面,
是 Windows API(Application Programming Interface)的一個重要組成
部分。它是 Windows 圖形顯示程式與實際物理設備之間的橋樑,GDI 使得使
用者無需關心具體設備的細節,而只需在一 個虛擬的環境(即邏輯裝置)中進行
操作。它的橋樑作用體現在:
(1)使用者通過調用 GDI 函數將邏輯空間的操作轉化為具體針對設備驅動程式
的調用。
為實現圖形設備無關性,Windows 的繪圖操作在一個設備描述表上進行。使用
者擁有自己的"邏輯座標"系統,它獨立於實際的物理設備,與"設備座標"相對
應。開發 Windows 應用程式時,程 序員關心的是邏輯座標,我們在邏輯坐標
系上繪圖,利用 GDI 將邏輯視窗映射到物理設備上。
(2)GDI 能檢測具體設備的能力,並依據具體的設備以最優方式驅動這些設備,
完成真實的顯示。
GDI 函數大致可分類為:設備上下文函數(如
GetDC、CreateDC、DeleteDC)、 畫線函數(如 LineTo、Polyline、Arc)、填
充畫圖函數(如 Ellipse、FillRect、Pie)、畫圖屬性函數(如
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 雖然使程式師得到了一定程度的解脫,但是其程式設計方式仍很麻煩。譬
如,顯示一張點陣圖,程式師需要進行"裝入點陣圖―讀取點陣圖檔頭資訊―啟
用設備場景―調色板變換"等一連串操作。而有了 GDI+,這些問題便迎刃而解
了。
顧名思義,GDI+是 GDI 的增強版。它是微軟在 Windows 2000 以後作業系
統中提供的新介面,其通過一套部署為託管代碼的類來展現,這套類被稱為
GDI+的"託管類介面"。GDI+主要提供了以下三類服務:
(1) 二維向量圖形:GDI+提供了存儲圖形基元自身資訊的類(或結構體)、存儲
圖形基元繪製方式資訊的類以及實際進行繪製的類;
(2) 影像處理:大多數圖片都難以劃定為直線和曲線的集合,無法使用二維向
量圖形方式進行處理。因此,GDI+為我們提供了 Bitmap、Image 等類,它們可
用於顯示、操作和保存 BMP、JPG、GIF 等圖像格式。
(3) 文字顯示:GDI+支援使用各種字體、字型大小和樣式來顯示文本。
GDI 介面是基於函數的,而 GDI+是基於 C++類的物件化的應用程式設計發
展介面,因此使用起來比GDI 要方便。
2.常式簡述
按一下此處下載本文常式原始程式碼。
本文後續的講解都基於這樣的一個例子工程(常式的開發環境為 Visual C+
+6.0,作業系統為 Windows XP),它是一個基於對話方塊的 MFC 應用程式,
包括 2 個父功能表:
(1) GDI
GDI父功能表下包括一個子功能表:
ID:IDM_GDI_DRAW_LINE caption:畫線
按一下事件:在視窗繪製正旋曲線
(2) GDI+
DIB 點陣圖父功能表下包括兩個子功能表:
a. ID:IDM_GDIP_DRAW_LINE caption:畫線
按一下事件:在視窗繪製正旋曲線
b. caption:新增功能,其下又包括下列子功能表:
(ⅰ)ID:IDM_Gradient_Brush caption:漸變畫刷
按一下事件:在視窗演示 GDI+的漸變畫刷功能
(ⅱ)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+的強大文本輸出能力
後續篇章將集中在對上述功能表按一下事件消息處理函數的講解,下面的代碼
是整個對話方塊類 CGdiexampleDlg 的消息映射:
BEGIN_MESSAGE_MAP(CGdiexampleDlg, CDialog)//{{AFX_MSG_MAP(CGdiexampleDlg)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_COMMAND(IDM_GDI_DRAW_LINE, OnGdiDrawLine)ON_COMMAND(IDM_GDIP_DRAW_LINE, OnGdipDrawLine)ON_COMMAND(IDM_Gradient_Brush, OnGradientBrush)ON_COMMAND(IDM_Cardinal_Spline, OnCardinalSpline)ON_COMMAND(IDM_Transformation_Matrix, OnTransformationMatrix)ON_COMMAND(IDM_Scalable_Region, OnScalableRegion)ON_COMMAND(IDM_IMAGE, OnImage)ON_COMMAND(IDM_Alpha_Blend, OnAlphaBlend)ON_COMMAND(IDM_TEXT, OnText)//}}AFX_MSG_MAPEND_MESSAGE_MAP()
3.GDI程式設計
"GDI"功能表下的"畫線"子功能表按一下事件消息處理函數的代碼如下:
void CGdiexampleDlg::OnGdiDrawLine(){// TODO: Add your command handler code hereCClientDC dc(this);
//邏輯座標與設備座標變換
CRect rect;GetClientRect(&rect);dc.SetMapMode(MM_ANISOTROPIC);dc.SetWindowOrg(0, 0);dc.SetWindowExt(rect.right, rect.bottom);dc.SetViewportOrg(0, rect.bottom / 2);dc.SetViewportExt(rect.right, - rect.bottom);
//創建繪製正旋曲線的 pen 並將其選入設備上下文
CPen pen(PS_SOLID, 1, RGB(255, 0, 0));HGDIOBJ oldObject = dc.SelectObject(pen.GetSafeHandle());
//繪製正旋曲線
dc.MoveTo(0, 0);for (int i = 0; i < rect.right; i++){dc.LineTo(i, 100 *sin(2 *(i / (rect.right / 5.0)) *PI));}
//創建繪製 x軸的 pen 並將其選入設備上下文
CPen penx(PS_SOLID, 1, RGB(0, 0, 255));dc.SelectObject(penx.GetSafeHandle());
//繪製 X軸
dc.MoveTo(0, 0);dc.LineTo(rect.right, 0);
//恢復原先的 pen
dc.SelectObject(oldObject);}
按一下這個按鈕,會出現如圖 1所示的效果,我們來對此進行解讀。
圖 1 繪製正旋曲線
前文提到,GDI 程式設計需進行設備座標和邏輯座標的轉化。而螢幕上的設備
座標通常會按客戶座標給出,客戶座標依賴于視窗的用戶端區域,其起始位置
位於用戶端區域的左上角。為示區別,圖 2給出了設備座標和使用者邏輯座標
的示例。
圖 2 設備座標與邏輯座標
設備座標與邏輯座標的轉換關係如下:
公式中的<Xvorg, Yvorg>是設備空間中視口的原點,而< Xworg, Yworg
>是邏輯空間中視窗的原點。 Xwext/Xvext 和 Ywext/Yvext 分別是視窗與視
口水平和垂直範圍的比例。
因此,經過程式中的 dc.SetWindowOrg (0,0) 和 dc.SetViewportOrg
(0,rect.bottom/2)語句我們設置了視口和視窗的原點;而經過程式中的
dc.SetWindowExt (rect.right,rect.bottom) 和 dc.SetViewportExt
(rect.right,-rect.bottom) 語句我們設置了視口和視窗的範圍。由於視口和
視窗的縱坐標方向相反,設置視口的垂直範圍為負值。這樣我們得到了一個邏輯
座標原點為客戶區水準方向最左邊和垂直方向居中的坐標系,我們在這個坐標
系上直接繪製正旋曲線,不需要再理睬Windows 對話方塊客戶區座標了。
void CGdiexampleDlg::OnGdiDrawLine()函數中未指定邏輯裝置和物理
設備的映射模式,則為缺省的 MM_TEXT。在這種模式下, 一個邏輯單位對應
於一個圖元點。映射模式是 GDI 中的一個重要概念,其它的映射模式還有
MM_LOENGLlSH、MM_HIENGLISH、 MM_LOMETRIC 和
MM_HIMETRIC 等。我們可以通過如下語句指定映射模式為 MM_TEXT:
dc.SetMapMode(MM_TEXT);
值得一提的是,從上述代碼可以看出:在 GDI 程式設計中,幾乎所有的操作都
圍繞設備上下文 dc 展開。的確,這正是 GDI 程式設計的特點!設備上下文是
Windows 使用的一種結構,所有 GDI 操作前都需取得特定設備的上下文,函
數中的 CClientDC dc (this) 語句完成這一功能。
歸納可得,利用 GDI 進行圖形、影像處理的一般操作步驟為:
1. 取得指定視窗的 DC;
2. 確定使用的坐標系及映射方式;
3. 進行圖形、圖像或文字處理;
4. 釋放所使用的 DC。
4.GDI+程式設計
"GDI+"功能表下的"畫線"子功能表按一下事件消息處理函數的代碼如下:
void CGdiexampleDlg::OnGdipDrawLine(){// TODO: Add your command handler code here CClientDC dc(this);
//邏輯座標與設備座標變換
CRect rect;GetClientRect(&rect);dc.SetMapMode(MM_ANISOTROPIC);dc.SetWindowOrg(0, 0);dc.SetWindowExt(rect.right, rect.bottom);dc.SetViewportOrg(0, rect.bottom / 2);dc.SetViewportExt(rect.right, - rect.bottom);
//創建Graphics 對象
Graphics graphics(dc);
//創建 pen
Pen myPen(Color::Red);myPen.SetWidth(1);
//畫正旋曲線
for (int i = 0; i < rect.right; i++){graphics.DrawLine(&myPen, i, 100 *sin(2 *(i / (rect.right / 5.0)) *PI), i +1, 100 *sin(2 *((i + 1) / (rect.right / 5.0)) *PI));}
//畫 X軸
myPen.SetColor(Color::Blue);graphics.DrawLine(&myPen, 0, 0, rect.right, 0);}
由於我們使用的是 Visual C++6.0 而非VS.Net,我們需要下載微軟的
GDIPLUS 支持包。在微軟官方網站下載時需認證Windows 為正版,我們可
從這個地址下載:
http://www.codeguru.com/code/legacy/gdi/GDIPlus.zip。一個完整的
GDI+支持包至少包括如下文 件:
(1)標頭檔:gdiplus.h
(2)動態連結程式庫的.lib 文件:gdiplus.lib
(3)動態連結程式庫的.dll 文件:gdiplus.dll
少了(1)、(2)程式不能編譯,少了(3)程式能以共用 DLL 的方式編譯但是不能運
行,運行時找不到.dll 文件。
為使得 Visual C++6.0 支援 GDI+,我們需要在使用 GDI+物件的檔的開頭
添加如下代碼:
#define UNICODE#ifndef ULONG_PTR#define ULONG_PTR unsigned long*#endif#include "c:\gdiplus\includes\gdiplus.h"using namespace Gdiplus;#pragma comment(lib, "c:\\gdiplus\\lib\\gdiplus.lib")
在 Visual C++中使用 GDI+必須先進行 GDI+的初始化,我們在 CWinApp
派生類的 InitInstance 函數中進行此項工作是最好的:
/////////////////////////////////////////////////////////////////////////////// CGdiexampleApp initialization
BOOL CGdiexampleApp::InitInstance(){AfxEnableControlContainer();
// Standard initialization
#ifdef _AFXDLLEnable3dControls(); // Call this when using MFC in a shared DLL#elseEnable3dControlsStatic(); // Call this when linking to MFC statically#endif
//初始化 gdiplus 的環境
GdiplusStartupInput gdiplusStartupInput;ULONG_PTR gdiplusToken;
// 初始化 GDI+.
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
CGdiexampleDlg dlg;m_pMainWnd = &dlg;int nResponse = dlg.DoModal();if (nResponse == IDOK){}else if (nResponse == IDCANCEL){}
//關閉 gdiplus 的環境
GdiplusShutdown(gdiplusToken);
return FALSE;}
按一下"GDI+"菜單下的"畫線"子功能表,也會出現如圖 1所示的效果。觀察
void CGdiexampleDlg::OnGdipDrawLine() 函數,我們發現用 GDI+進行
圖形、圖像操作的步驟為:
(1)創建 Graphics 對象:Graphics 物件表示 GDI+繪圖表面,是用於創建圖
形圖像的物件;
(2)使用 Graphics 物件繪製線條和形狀、呈現文本或顯示與操作圖像。
Graphics 物件是 GDI+的核心,GDI 中設備上下文 dc 和 Graphics 物件的
作用相似,但在 GDI 中使用的是基於控制碼的程式設計模式,而 GDI+中使用
的則是基於物件的程式設計模式。Graphics封裝了 GDI+ 繪圖面,而且此類
無法被繼承,它的所有成員函數都不是虛函數。
下面,我們來逐個用實際代碼實現 GDI+的新增功能,這些新增功能包括:漸
變的畫刷(Gradient Brushes)、基數樣條函數(Cardinal Splines)、持久的路
徑物件(Persistent Path Objects)、變形和矩陣物件(Transformations
&Matrix Object)、可伸縮區域(Scalable Regions)、Alpha混合(Alpha
Blending)和豐富的圖像格式支援等。
漸變的畫刷
GDI+提供了用於填充圖形、路徑和區域的線性漸變畫刷和路徑漸變畫刷。
線性漸變畫刷使用漸變顏色來填充圖形。
當用路徑漸變畫刷填充圖形時,可指定從圖形的一部分移至另一部分時畫刷顏
色的變化方式。例如,我們可以只指定圖形的中心顏色和邊緣顏色,當畫刷從圖
形中間向外邊緣移動時,畫刷會逐漸從中心顏色變化到邊緣顏色。
void CGdiexampleDlg::OnGradientBrush(){// TODO: Add your command handler code hereCClientDC dc(this);CRect rect;GetClientRect(&rect);
//創建Graphics 對象
Graphics graphics(dc);
//創建漸變畫刷
LinearGradientBrush lgb(Point(0, 0), Point(rect.right, rect.bottom), Color::Blue, Color::Green);
//填充
graphics.FillRectangle(&lgb, 0, 0, rect.right, rect.bottom);}
本程式使用線性漸變畫刷,當畫刷從客戶區左上角移向客戶區右下角的過程中,
顏色逐漸由藍色轉變為綠色。
圖 3 GDI+漸變畫刷
基數樣條函數
GDI+支持基數樣條,基數樣條指的是一連串單獨的曲線,這些曲線連接起來
形成一條較大的曲線。樣條由點(Point 結構體)的陣列指定,並通過該陣列中的
每一個點。基數樣條平滑地穿過陣列中的每一個點(不出現尖角),因此比用直線
連接創建的路徑精確。
void CGdiexampleDlg::OnCardinalSpline(){// TODO: Add your command handler code here
CClientDC dc(this);
//創建Graphics 對象
Graphics graphics(dc);Point points[] ={Point(0, 0), Point(100, 200), Point(200, 0), Point(300, 200), Point(400, 00)};
//直接畫線
for (int i = 0; i < 4; i++){graphics.DrawLine(&Pen(Color::Blue, 3), points[i], points[i + 1]);}
//利用基數樣條畫線
graphics.DrawCurve(&Pen(Color::Red, 3), points, 5);}
圖 4演示了直接連線和經過基數樣條平滑擬合後的線條的對比,後者的曲線
(Curve)沒有尖角。這個工作我們在中學的數學課上把離散的點連接成曲線時做
過。
圖 4 GDI+基數樣條
持久的路徑物件
在 GDI 中,路徑隸屬於一個設備上下文,一旦設備環境指標超過它的生存期,
路徑也會被刪除。利用 GDI+,可以創建並維護與 Graphics 物件分開的
GraphicsPath 物件,它不依賴於 Graphics 物件的生存期。
變形和矩陣對象
GDI+提供了 Matrix 物件,它是一種可以使變形(旋轉、平移、縮放等) 簡易靈
活的強大工具,Matrix 物件需與要被變形的物件聯合使用。對於
GraphicsPath 類,我們可以使用其成員函數 Transform接收Matrix參數用
於變形。
void CGdiexampleDlg::OnTransformationMatrix(){// TODO: Add your command handler code hereCClientDC dc(this);
//創建Graphics 對象
Graphics graphics(dc);GraphicsPath path;path.AddRectangle(Rect(250, 20, 70, 70));
graphics.DrawPath(&Pen(Color::Black, 1), &path); // 在應用
變形矩陣之前繪製矩形
// 路徑變形
Matrix matrix1, matrix2;
matrix1.Rotate(45.0f); //旋轉順時針 45 度
path.Transform(&matrix1); //應用變形
graphics.DrawPath(&Pen(Color::Red, 3), &path);
matrix2.Scale(1.0f, 0.5f); //轉化成為平行四邊形法則
path.Transform(&matrix2); //應用變形
graphics.DrawPath(&Pen(Color::Blue, 3), &path);}
圖 5演示了正方形經過旋轉和拉伸之後的效果:黑色的為原始圖形,紅色的為
旋轉 45 度之後的圖形,藍色的為經過拉伸為平行四邊形後的圖形。
圖 5 GDI+變形和矩陣對象
可伸縮區域
GDI+通過對區域(Region)的支援極大地擴展了 GDI。在 GDI 中,區域存儲在
設備座標中,可應用於區域的唯一變形是平移。但是在 GDI +中,區域存儲在
全域座標(世界座標)中,可對區域利用變形矩陣進行變形(旋轉、平移、縮放等)。
void CGdiexampleDlg::OnScalableRegion(){// TODO: Add your command handler code hereCClientDC dc(this);
//創建Graphics 對象
Graphics graphics(dc);
//創建GraphicsPath
GraphicsPath path;path.AddLine(100, 100, 150, 150);path.AddLine(50, 150, 150, 150);path.AddLine(50, 150, 100, 100);
//創建Region
Region region(&path);
//填充區域
graphics.FillRegion(&SolidBrush(Color::Blue), ®ion);
//區域變形
Matrix matrix;
matrix.Rotate(10.0f); //旋轉順時針 20 度
matrix.Scale(1.0f, 0.3f); //拉伸
region.Transform(&matrix); //應用變形
//填充變形後的區域
graphics.FillRegion(&SolidBrush(Color::Green), ®ion);}
上述程式中以藍色填充一個三角形區域,接著將此區域旋轉和拉伸,再次顯示,
其效果如圖 6。
圖 6 GDI+區域變形
豐富的圖像格式支援
GDI +提供了 Image、Bitmap 和 Metafile 類,方便使用者進行圖像格式的
載入、操作和保存。GDI+支援的圖像格式有
BMP、GIF、JPEG、EXIF、PNG、TIFF、ICON、WMF、EMF 等,幾乎涵蓋了
所有的常用圖像格式。
void CGdiexampleDlg::OnImage(){// TODO: Add your command handler code hereCClientDC dc(this);
//創建Graphics 對象
Graphics graphics(dc);Image image(L "d:\\1.jpg");
//在矩形區域內顯示 jpg 圖像
Point destPoints1[3] =
{Point(10, 10), Point(220, 10), Point(10, 290)};graphics.DrawImage(&image, destPoints1, 3);
//在平行四邊形區域內顯示 jpg 圖像
Point destPoints2[3] ={Point(230, 10), Point(440, 10), Point(270, 290)};graphics.DrawImage(&image, destPoints2, 3);}
上述程式將 D盤根目錄下檔案名為"1.jpg"的 jpg 圖像以矩陣和平行四邊形兩
種方式顯示,效果如圖 7。
圖 7 GDI+多種圖像格式支援
由此我們可以看出,GDI+在圖像顯示和操作方面的確比GDI簡單許多。回憶
我們在《Visual C++中 DDB 與 DIB 點陣圖程式設計全攻略》一文中所介紹的
用 GDI 顯示點陣圖的方式,其與 GDI+影像處理的難易程度真是有天壤之別。
Alpha混合
Alpha允許將兩個物體混合起 來顯示,在 3D氣氛和場景渲染等方面有廣泛應
用。它能"霧化"圖像,使得一個圖像著色在另一個半透明的圖像上,呈現一種朦
朧美。我們知道,一個圖元可用 R,G,B 三個維度來表示,我們可以再加上第
4 個即:Alpha 維度(channel),表徵透明程度。
void CGdiexampleDlg::OnAlphaBlend(){// TODO: Add your command handler code hereCClientDC dc(this);
//創建Graphics 對象
Graphics graphics(dc);
//創建ColorMatrix
ColorMatrix ClrMatrix ={1.0f, 0.0f, 0.0f, 0.0f, 0.0f,0.0f, 1.0f, 0.0f, 0.0f, 0.0f,0.0f, 0.0f, 1.0f, 0.0f, 0.0f,0.0f, 0.0f, 0.0f, 0.5f, 0.0f,0.0f, 0.0f, 0.0f, 0.0f, 1.0f};
//將 ColorMatrix賦給 ImageAttributes
ImageAttributes ImgAttr;ImgAttr.SetColorMatrix(&ClrMatrix, ColorMatrixFlagsDefault,ColorAdjustTypeBitmap);
//在矩形區域內顯示 jpg 圖像
Image img1(L "d:\\1.jpg");Point destPoints1[3] ={Point(10, 10), Point(220, 10), Point(10, 290)
};graphics.DrawImage(&img1, destPoints1, 3);
//Alpha混合
Image img2(L "d:\\2.jpg");int width, height;width = img2.GetWidth();height = img2.GetHeight();graphics.DrawImage(&img2, RectF(10, 10, 210, 280), 0, 0, width, height,UnitPixel, &ImgAttr);
//在平行四邊形區域內顯示 jpg 圖像
Point destPoints2[3] ={Point(230, 10), Point(440, 10), Point(270, 290)};graphics.DrawImage(&img1, destPoints2, 3);
//Alpha混合
graphics.DrawImage(&img2, destPoints2, 3, 0, 0, width, height, UnitPixel,&ImgAttr);}
上述程式中將 D盤根目錄下檔案名為"1.jpg"的圖像以矩陣和平行四邊形兩種
方式顯示,然後將檔案名為為"2.jpg"的圖像與之進行混合,其效果如圖 8。
圖 8 GDI+ Alpha混合
為了能進行 Alpha混合,我們需要使用 ImageAttributes 類和 ColorMatrix
矩陣,ImageAttributes 可以進行顏 色、灰度等調整從而達到控制圖像著色方
式的目的。ColorMatrix 是 ImageAttributes 類大多數函數的參數,它包含
了 Alpha、 Red、Green、Blue 維度的值,以及另一維 w,順序為 RGBaw。
CGdiexampleDlg:: OnAlphaBlend()函數中 ColorMatrix 的實例
ClrMatrix 中元素(4,4)的值為 0.5,表示 Alpha 度的值為 0.5(即半 透明)。在
ColorMatrix 中,元素(5,5)的值恒定為 1.0。我們把ClrMatrix 的元素(0,0)
修改為 0.0,即使得圖像 2.jpg 的 紅色維度全不顯示,再看效果,為圖 9。列位
讀者,我們以前在豪傑超級解霸中調整R,G,B值從而控制圖像輸出顏色的
時候,調的就是這個東東!圖 9 的效果很 像破舊彩色電視機,紅色電子
槍"嗝"了。剛大學畢業時,俺那個叫窮啊,就買了這麼個電視機,還看得很爽,
真是往事不堪回首!
圖 9 GDI+中的 ColorMatrix
強大的文字輸出
GDI+擁有極其強大的文字輸出處理能力,輸出文字的顏色、字體、填充方式都
可以直接作為 Graphics 類 DrawString 成員函數的參數進行設置,其功能遠
勝過 GDI 設備上下文的 TextOut 函數。
void CGdiexampleDlg::OnText(){// TODO: Add your command handler code hereCClientDC dc(this);
//創建Graphics 對象
Graphics graphics(dc);
//創建 20號"楷體"字體
FontFamily fontFamily1(L "楷體_GB2312"); // 定義"楷體"字樣
Font font1(&fontFamily1, 20, FontStyleRegular, UnitPoint);
//定義輸出UNICODE 字串
WCHAR string[256];
wcscpy(string, L "天極網的讀者朋友,您好!");
//以藍色畫刷和 20號"楷體"顯示字串
graphics.DrawString(string, (INT)wcslen(string), &font1, PointF(30, 10),&SolidBrush(Color::Blue));
//定義字串顯示畫刷
LinearGradientBrush linGrBrush(Point(30, 50), Point(100, 50), Color(255, 255,0, 0), Color(255, 0, 0, 255));
//以線性漸變畫刷和創建的 20號"楷體"顯示字串
graphics.DrawString(string, (INT)wcslen(string), &font1, PointF(30, 50),&linGrBrush);
//創建 20號"華文行楷"字體
FontFamily fontFamily2(L "華文行楷"); // 定義"楷體"字樣
Font font2(&fontFamily2, 20, FontStyleRegular, UnitPoint);
//以線性漸變畫刷和 20號"華文行楷"顯示字串
graphics.DrawString(string, (INT)wcslen(string), &font2, PointF(30, 90),&linGrBrush);
//以圖像創建畫刷
Image image(L "d:\\3.jpg");TextureBrush tBrush(&image);
//以圖像畫刷和 20號"華文行楷"顯示字串
graphics.DrawString(string, (INT)wcslen(string), &font2, PointF(30, 130),&tBrush);
//創建 25號"華文中宋"字體
FontFamily fontFamily3(L "華文中宋"); // 定義"楷體"字樣
Font font3(&fontFamily2, 25, FontStyleRegular, UnitPoint);
//以圖像畫刷和 20號"華文行楷"顯示字串
graphics.DrawString(string, (INT)wcslen(string), &font3, PointF(30, 170),&tBrush);}
上述代碼的執行效果如圖 10所示,字體、顏色和填充都很豐富!
圖 10 GDI+文本輸出
5.GDI與GDI+的比較
GDI+相對 GDI 而言主要在程式設計方式上發生了巨大的改變。
GDI 的核心是設備上下文,GDI 函數都依賴於設備上下文控制碼,其程式設計
方式是基於控制碼的;GDI+無需時刻依賴於控制碼或設備上下文,使用者只
需創建一個 Graphics 物件,就可以用物件導向的方式調用其成員函數進行圖
形操作,程式設計方式是基於物件的。
GDI 在使用設備上下文繪製線條之前,必須先調用 SelectObject 以使鋼筆物
件和設備上下文關聯。其後,在設備上下文中繪製的所有線條均使用該鋼筆,直
到選擇另一支不同的鋼筆為止。CGdiexampleDlg:: OnGdiDrawLine 函數中
的下列語句完成的就是這個功能:
//創建繪製正旋曲線的 pen 並將其選入設備上下文
CPen pen(PS_SOLID,1,RGB(255,0,0));HGDIOBJ oldObject = dc.SelectObject(pen.GetSafeHandle()); …
//創建繪製 x軸的 pen 並將其選入設備上下文
CPen penx(PS_SOLID,1,RGB(0,0,255));dc.SelectObject(penx.GetSafeHandle());…
//恢復原先的 pen
dc.SelectObject(oldObject);
但是,在 GDI+中,只需將 Pen 物件直接作為參數傳遞給Graphics 類的
DrawLine 等方法即可,而不必使 Pen 物件與 Graphics 物件關聯,例如
CGdiexampleDlg::OnGdipDrawLine 函數中的下列語句:
Pen myPen(Color::Red);myPen.SetWidth(1);…graphics.DrawLine(&myPen,i,100*sin(2*(i/(rect.right/5.0))*PI),i+1,100*sin(2*((i+1)/(rect.right/5.0))*PI));…graphics.DrawLine(&myPen,0,0,rect.right,0);
GDI 中有當前位置的概念,所以在使用 GDI 繪製線條前應該先使用 MoveTo
移動當前位置,再使用 LineTo 畫線,例如:
//繪製正旋曲線
dc.MoveTo(0,0) ;for(int i=0;i<rect.right;i++){dc.LineTo(i,100*sin(2*(i/(rect.right/5.0))*PI)); }
而 GDI+中則沒有當前位置的概念,畫線函數中可以直接指定起點和終點,例
如:
graphics.DrawLine(&myPen,0,0,rect.right,0);
6.結論
鑒於 GDI+良好的易用性和其具有的強大功能,我們建議儘快拋棄GDI 程式設
計方式,因為我們沒有必要將時間浪費在無意義的重複代碼的設計上。GDI+對
GDI 的增強,某種意義上類似於 MFC 對 Windows API 的整理和封裝。作為
一種良好的"生產工具",它必將大大地促進開發時的"生產力"。