Windows+GDI和GDI+編程實例剖析

34
1.基基基基 GDI 在 Windows 在在在在 Graphics Device Interface 在在 ,, Windows API(Application Programming Interface)在 在在在在在在在在在 一。 Windows 在在在在在在在在在在在在在 GDI在 在 在 體,一 在在在在在在(在在在在在)在在在在在在在在在在在在 。體: (1)在在在在在在在 GDI 在 在 體。 Windows 在在在在在在 在在在在在在 一。 "在在在在"在 在 ,, 在"在在在在"在 在 Windows 在 在 在在在在在在在在在在在 ,, GDI 在在在在在在在在在在在在在 (2)GDI 在 在 在 在 在 體,體,。 GDI 在在在在在在 在在在 (在 GetDCCreateDCDeleteDC)在在在在(在 LineToPolylineArc) (在 EllipseFillRectPie) (在 SetBkColorSetBkModeSetTextColor) 在在在 、、體(在 TextOutGetFontData) (在 SetPixelBitBltStretchBlt) (在 DPtoLPLPtoDPScreenToClientClientToScreen) (在 SetMapModeSetWindowExtExSetViewportExtEx) (在 PlayMetaFileSetWinMetaFileBits) (在 FillRgnFrameRgnInvertRgn) 在(在 BeginPathEndPathStrokeAndFillPath) (在 SelectClipRgn
  • date post

    15-Oct-2014
  • Category

    Documents

  • view

    233
  • download

    3

Transcript of Windows+GDI和GDI+編程實例剖析

Page 1: 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)、座

Page 2: Windows+GDI和GDI+編程實例剖析

標函數(如 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+支援使用各種字體、字型大小和樣式來顯示文本。

Page 3: Windows+GDI和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:新增功能,其下又包括下列子功能表:

Page 4: Windows+GDI和GDI+編程實例剖析

(ⅰ)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+的強大文本輸出能力

Page 5: Windows+GDI和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);

Page 6: Windows+GDI和GDI+編程實例剖析

//創建繪製正旋曲線的 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所示的效果,我們來對此進行解讀。

Page 7: Windows+GDI和GDI+編程實例剖析

圖 1 繪製正旋曲線

前文提到,GDI 程式設計需進行設備座標和邏輯座標的轉化。而螢幕上的設備

座標通常會按客戶座標給出,客戶座標依賴于視窗的用戶端區域,其起始位置

位於用戶端區域的左上角。為示區別,圖 2給出了設備座標和使用者邏輯座標

的示例。

圖 2 設備座標與邏輯座標

設備座標與邏輯座標的轉換關係如下:

Page 8: Windows+GDI和GDI+編程實例剖析

公式中的<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 程式設計中,幾乎所有的操作都

Page 9: Windows+GDI和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);

Page 10: Windows+GDI和GDI+編程實例剖析

//創建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

Page 11: Windows+GDI和GDI+編程實例剖析

少了(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 的環境

Page 12: Windows+GDI和GDI+編程實例剖析

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+ 繪圖面,而且此類

無法被繼承,它的所有成員函數都不是虛函數。

Page 13: Windows+GDI和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);

//填充

Page 14: Windows+GDI和GDI+編程實例剖析

graphics.FillRectangle(&lgb, 0, 0, rect.right, rect.bottom);}

本程式使用線性漸變畫刷,當畫刷從客戶區左上角移向客戶區右下角的過程中,

顏色逐漸由藍色轉變為綠色。

圖 3 GDI+漸變畫刷

基數樣條函數

GDI+支持基數樣條,基數樣條指的是一連串單獨的曲線,這些曲線連接起來

形成一條較大的曲線。樣條由點(Point 結構體)的陣列指定,並通過該陣列中的

每一個點。基數樣條平滑地穿過陣列中的每一個點(不出現尖角),因此比用直線

連接創建的路徑精確。

void CGdiexampleDlg::OnCardinalSpline(){// TODO: Add your command handler code here

Page 15: Windows+GDI和GDI+編程實例剖析

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)沒有尖角。這個工作我們在中學的數學課上把離散的點連接成曲線時做

過。

Page 16: Windows+GDI和GDI+編程實例剖析

圖 4 GDI+基數樣條

持久的路徑物件

在 GDI 中,路徑隸屬於一個設備上下文,一旦設備環境指標超過它的生存期,

路徑也會被刪除。利用 GDI+,可以創建並維護與 Graphics 物件分開的

GraphicsPath 物件,它不依賴於 Graphics 物件的生存期。

變形和矩陣對象

GDI+提供了 Matrix 物件,它是一種可以使變形(旋轉、平移、縮放等) 簡易靈

活的強大工具,Matrix 物件需與要被變形的物件聯合使用。對於

GraphicsPath 類,我們可以使用其成員函數 Transform接收Matrix參數用

於變形。

Page 17: Windows+GDI和GDI+編程實例剖析

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 度之後的圖形,藍色的為經過拉伸為平行四邊形後的圖形。

Page 18: Windows+GDI和GDI+編程實例剖析

圖 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);

Page 19: Windows+GDI和GDI+編程實例剖析

//創建Region

Region region(&path);

//填充區域

graphics.FillRegion(&SolidBrush(Color::Blue), &region);

//區域變形

Matrix matrix;

matrix.Rotate(10.0f); //旋轉順時針 20 度

matrix.Scale(1.0f, 0.3f); //拉伸

region.Transform(&matrix); //應用變形

//填充變形後的區域

graphics.FillRegion(&SolidBrush(Color::Green), &region);}

上述程式中以藍色填充一個三角形區域,接著將此區域旋轉和拉伸,再次顯示,

其效果如圖 6。

Page 20: Windows+GDI和GDI+編程實例剖析

圖 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] =

Page 21: Windows+GDI和GDI+編程實例剖析

{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 點陣圖程式設計全攻略》一文中所介紹的

Page 22: Windows+GDI和GDI+編程實例剖析

用 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)

Page 23: Windows+GDI和GDI+編程實例剖析

};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。

Page 24: Windows+GDI和GDI+編程實例剖析

圖 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 的效果很 像破舊彩色電視機,紅色電子

Page 25: Windows+GDI和GDI+編程實例剖析

槍"嗝"了。剛大學畢業時,俺那個叫窮啊,就買了這麼個電視機,還看得很爽,

真是往事不堪回首!

圖 9 GDI+中的 ColorMatrix

強大的文字輸出

GDI+擁有極其強大的文字輸出處理能力,輸出文字的顏色、字體、填充方式都

可以直接作為 Graphics 類 DrawString 成員函數的參數進行設置,其功能遠

勝過 GDI 設備上下文的 TextOut 函數。

void CGdiexampleDlg::OnText(){// TODO: Add your command handler code hereCClientDC dc(this);

//創建Graphics 對象

Graphics graphics(dc);

Page 26: Windows+GDI和GDI+編程實例剖析

//創建 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 "華文中宋"); // 定義"楷體"字樣

Page 27: Windows+GDI和GDI+編程實例剖析

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 物件,就可以用物件導向的方式調用其成員函數進行圖

Page 28: Windows+GDI和GDI+編程實例剖析

形操作,程式設計方式是基於物件的。

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);

Page 29: Windows+GDI和GDI+編程實例剖析

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 的整理和封裝。作為

一種良好的"生產工具",它必將大大地促進開發時的"生產力"。