1.基本概念

  GDI在Windows中定義為Graphics Device Interface,即圖形設(shè)備接口,是Windows API(Application Programming Interface)的一個重要組成部分。它是Windows圖形顯示程序與實(shí)際物理設(shè)備之間的橋梁,GDI使得用戶無需關(guān)心具體設(shè)備的細(xì)節(jié),而只需在一個虛擬的環(huán)境(即邏輯設(shè)備)中進(jìn)行操作。它的橋梁作用體現(xiàn)在:

  (1)用戶通過調(diào)用GDI函數(shù)將邏輯空間的操作轉(zhuǎn)化為具體針對設(shè)備驅(qū)動程序的調(diào)用。

  為實(shí)現(xiàn)圖形設(shè)備無關(guān)性,Windows 的繪圖操作在一個設(shè)備描述表上進(jìn)行。用戶擁有自己的"邏輯坐標(biāo)"系統(tǒng),它獨(dú)立于實(shí)際的物理設(shè)備,與"設(shè)備坐標(biāo)"相對應(yīng)。開發(fā)Windows應(yīng)用程序時,程序員關(guān)心的是邏輯坐標(biāo),我們在邏輯坐標(biāo)系上繪圖,利用GDI將邏輯窗口映射到物理設(shè)備上。

  (2)GDI能檢測具體設(shè)備的能力,并依據(jù)具體的設(shè)備以最優(yōu)方式驅(qū)動這些設(shè)備,完成真實(shí)的顯示。

  GDI函數(shù)大致可分類為:設(shè)備上下文函數(shù)(如GetDC、CreateDC、DeleteDC)、 畫線函數(shù)(如LineTo、Polyline、Arc)、填充畫圖函數(shù)(如Ellipse、FillRect、Pie)、畫圖屬性函數(shù)(如SetBkColor、SetBkMode、SetTextColor)、文本、字體函數(shù)(如TextOut、GetFontData)、位圖函數(shù)(如SetPixel、BitBlt、StretchBlt)、坐標(biāo)函數(shù)(如DPtoLP、LPtoDP、ScreenToClient、ClientToScreen)、映射函數(shù)(如SetMapMode、SetWindowExtEx、SetViewportExtEx)、元文件函數(shù)(如PlayMetaFile、SetWinMetaFileBits)、區(qū)域函數(shù)(如FillRgn、FrameRgn、InvertRgn)、路徑函數(shù)(如BeginPath、EndPath、StrokeAndFillPath)、裁剪函數(shù)(如SelectClipRgn、SelectClipPath)等。

  GDI雖然使程序員得到了一定程度的解脫,但是其編程方式仍很麻煩。譬如,顯示一張位圖,程序員需要進(jìn)行"裝入位圖―讀取位圖文件頭信息―啟用設(shè)備場景―調(diào)色板變換"等一連串操作。而有了GDI+,這些問題便迎刃而解了。

  顧名思義,GDI+是GDI的增強(qiáng)版。它是微軟在Windows 2000以后操作系統(tǒng)中提供的新接口,其通過一套部署為托管代碼的類來展現(xiàn),這套類被稱為GDI+的"托管類接口"。GDI+主要提供了以下三類服務(wù):

  (1) 二維矢量圖形:GDI+提供了存儲圖形基元自身信息的類(或結(jié)構(gòu)體)、存儲圖形基元繪制方式信息的類以及實(shí)際進(jìn)行繪制的類;

  (2) 圖像處理:大多數(shù)圖片都難以劃定為直線和曲線的集合,無法使用二維矢量圖形方式進(jìn)行處理。因此,GDI+為我們提供了Bitmap、Image等類,它們可用于顯示、操作和保存BMP、JPG、GIF等圖像格式。

  (3) 文字顯示:GDI+支持使用各種字體、字號和樣式來顯示文本。

  GDI接口是基于函數(shù)的,而GDI+是基于C++類的對象化的應(yīng)用程序編程接口,因此使用起來比GDI要方便。

  2.例程簡述

  單擊此處下載本文例程源代碼。

  本文后續(xù)的講解都基于這樣的一個例子工程(例程的開發(fā)環(huán)境為Visual C++6.0,操作系統(tǒng)為Windows XP),它是一個基于對話框的MFC應(yīng)用程序,包括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:基數(shù)樣條

  單擊事件:在窗口演示GDI+的基數(shù)樣條函數(shù)功能

  (ⅲ)ID:IDM_Transformation_Matrix caption:變形和矩陣對象

  單擊事件:在窗口演示GDI+的變形和矩陣對象功能

  (ⅳ)ID:IDM_Scalable_Region caption:可伸縮區(qū)域

  單擊事件:在窗口演示GDI+的可伸縮區(qū)域功能

  (ⅴ)ID:IDM_IMAGE caption:圖像

  單擊事件:在窗口演示GDI+的多種圖像格式支持功能

  (ⅵ)ID:IDM_Alpha_Blend caption:Alpha混合

  單擊事件:在窗口演示GDI+的Alpha混合功能

  (ⅶ)ID:IDM_TEXT caption:文本

  單擊事件:在窗口演示GDI+的強(qiáng)大文本輸出能力

  后續(xù)篇章將集中在對上述菜單單擊事件消息處理函數(shù)的講解,下面的代碼是整個對話框類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_MAP
END_MESSAGE_MAP()

3.GDI編程

  "GDI"菜單下的"畫線"子菜單單擊事件消息處理函數(shù)的代碼如下:

void CGdiexampleDlg::OnGdiDrawLine()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 //邏輯坐標(biāo)與設(shè)備坐標(biāo)變換
 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);
 //創(chuàng)建繪制正旋曲線的pen并將其選入設(shè)備上下文
 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));
 }
 //創(chuàng)建繪制x軸的pen并將其選入設(shè)備上下文
 CPen penx(PS_SOLID, 1, RGB(0, 0, 255));
 dc.SelectObject(penx.GetSafeHandle());
 //繪制X軸
 dc.MoveTo(0, 0);
 dc.LineTo(rect.right, 0);
 //恢復(fù)原先的pen
 dc.SelectObject(oldObject);
}

  單擊這個按鈕,會出現(xiàn)如圖1所示的效果,我們來對此進(jìn)行解讀。

WindowsGDI和GDI+編程實(shí)例全剖析(2)
圖1 繪制正旋曲線

  前文提到,GDI編程需進(jìn)行設(shè)備坐標(biāo)和邏輯坐標(biāo)的轉(zhuǎn)化。而屏幕上的設(shè)備坐標(biāo)通常會按客戶坐標(biāo)給出,客戶坐標(biāo)依賴于窗口的客戶區(qū)域,其起始位置位于客戶區(qū)域的左上角。為示區(qū)別,圖2給出了設(shè)備坐標(biāo)和用戶邏輯坐標(biāo)的示例。

WindowsGDI和GDI+編程實(shí)例全剖析(2)
圖2 設(shè)備坐標(biāo)與邏輯坐標(biāo)

  設(shè)備坐標(biāo)與邏輯坐標(biāo)的轉(zhuǎn)換關(guān)系如下:

WindowsGDI和GDI+編程實(shí)例全剖析(2)
WindowsGDI和GDI+編程實(shí)例全剖析(2)

  公式中的<Xvorg, Yvorg>是設(shè)備空間中視口的原點(diǎn),而< Xworg, Yworg >是邏輯空間中窗口的原點(diǎn)。 Xwext/Xvext和Ywext/Yvext分別是窗口與視口水平和垂直范圍的比例。

  因此,經(jīng)過程序中的dc.SetWindowOrg (0,0) 和dc.SetViewportOrg (0,rect.bottom/2)語句我們設(shè)置了視口和窗口的原點(diǎn);而經(jīng)過程序中的dc.SetWindowExt (rect.right,rect.bottom) 和dc.SetViewportExt (rect.right,-rect.bottom) 語句我們設(shè)置了視口和窗口的范圍。由于視口和窗口的縱坐標(biāo)方向相反,設(shè)置視口的垂直范圍為負(fù)值。這樣我們得到了一個邏輯坐標(biāo)原點(diǎn)為客戶區(qū)水平方向最左邊和垂直方向居中的坐標(biāo)系,我們在這個坐標(biāo)系上直接繪制正旋曲線,不需要再理睬Windows對話框客戶區(qū)坐標(biāo)了。

  void CGdiexampleDlg::OnGdiDrawLine()函數(shù)中未指定邏輯設(shè)備和物理設(shè)備的映射模式,則為缺省的MM_TEXT。在這種模式下,一個邏輯單位對應(yīng)于一個像素點(diǎn)。映射模式是GDI中的一個重要概念,其它的映射模式還有MM_LOENGLlSH、MM_HIENGLISH、MM_LOMETRIC和MM_HIMETRIC等。我們可以通過如下語句指定映射模式為MM_TEXT:

dc.SetMapMode(MM_TEXT);

  值得一提的是,從上述代碼可以看出:在GDI編程中,幾乎所有的操作都圍繞設(shè)備上下文dc展開。的確,這正是GDI編程的特點(diǎn)!設(shè)備上下文是Windows 使用的一種結(jié)構(gòu),所有GDI操作前都需取得特定設(shè)備的上下文,函數(shù)中的CClientDC dc (this) 語句完成這一功能。

  歸納可得,利用GDI進(jìn)行圖形、圖像處理的一般操作步驟為:

  1. 取得指定窗口的DC;

  2. 確定使用的坐標(biāo)系及映射方式;

  3. 進(jìn)行圖形、圖像或文字處理;

  4. 釋放所使用的DC。

  4.GDI+編程

  "GDI+"菜單下的"畫線"子菜單單擊事件消息處理函數(shù)的代碼如下:

void CGdiexampleDlg::OnGdipDrawLine()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 //邏輯坐標(biāo)與設(shè)備坐標(biāo)變換
 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);

 //創(chuàng)建Graphics對象
 Graphics graphics(dc);
 //創(chuàng)建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支持包。在微軟官方網(wǎng)站下載時需認(rèn)證Windows為正版,我們可從這個地址下載:http://www.codeguru.com/code/legacy/gdi/GDIPlus.zip。一個完整的GDI+支持包至少包括如下文件:

  (1)頭文件:gdiplus.h

  (2)動態(tài)鏈接庫的.lib文件:gdiplus.lib

  (3)動態(tài)鏈接庫的.dll文件:gdiplus.dll

  少了(1)、(2)程序不能編譯,少了(3)程序能以共享DLL的方式編譯但是不能運(yùn)行,運(yùn)行時找不到.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+必須先進(jìn)行GDI+的初始化,我們在CWinApp派生類的InitInstance函數(shù)中進(jìn)行此項(xiàng)工作是最好的:

///////////////////////////////////////
CGdiexampleApp initialization

BOOL CGdiexampleApp::InitInstance()
{
 AfxEnableControlContainer();

 // Standard initialization

 #ifdef _AFXDLL
  Enable3dControls(); // Call this when using MFC in a shared DLL
 #else
  Enable3dControlsStatic(); //

Call this when linking to MFC statically
 #endif

 //初始化gdiplus的環(huán)境
 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){}

 //關(guān)閉gdiplus的環(huán)境
 GdiplusShutdown(gdiplusToken);

 return FALSE;
}


  單擊"GDI+"菜單下的"畫線"子菜單,也會出現(xiàn)如圖1所示的效果。觀察void CGdiexampleDlg::OnGdipDrawLine() 函數(shù),我們發(fā)現(xiàn)用GDI+進(jìn)行圖形、圖像操作的步驟為:

  (1)創(chuàng)建 Graphics 對象:Graphics 對象表示GDI+繪圖表面,是用于創(chuàng)建圖形圖像的對象;

  (2)使用 Graphics 對象繪制線條和形狀、呈現(xiàn)文本或顯示與操作圖像。

  Graphics 對象是GDI+的核心,GDI中設(shè)備上下文dc和Graphics 對象的作用相似,但在GDI中使用的是基于句柄的編程模式,而GDI+中使用的則是基于對象的編程模式。Graphics封裝了GDI+ 繪圖面,而且此類無法被繼承,它的所有成員函數(shù)都不是虛函數(shù)。

  下面,我們來逐個用實(shí)際代碼實(shí)現(xiàn)GDI+的新增功能,這些新增功能包括:漸變的畫刷(Gradient Brushes)、基數(shù)樣條函數(shù)(Cardinal Splines)、持久的路徑對象(Persistent Path Objects)、變形和矩陣對象(Transformations &Matrix Object)、可伸縮區(qū)域(Scalable Regions)、Alpha混合(Alpha Blending)和豐富的圖像格式支持等。

漸變的畫刷

  GDI+提供了用于填充圖形、路徑和區(qū)域的線性漸變畫刷和路徑漸變畫刷。

  線性漸變畫刷使用漸變顏色來填充圖形。

  當(dāng)用路徑漸變畫刷填充圖形時,可指定從圖形的一部分移至另一部分時畫刷顏色的變化方式。例如,我們可以只指定圖形的中心顏色和邊緣顏色,當(dāng)畫刷從圖形中間向外邊緣移動時,畫刷會逐漸從中心顏色變化到邊緣顏色。

void CGdiexampleDlg::OnGradientBrush()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 CRect rect;
 GetClientRect(&rect);
 //創(chuàng)建Graphics對象
 Graphics graphics(dc);
 //創(chuàng)建漸變畫刷
 LinearGradientBrush lgb(Point(0, 0), Point(rect.right, rect.bottom), Color::Blue, Color::Green);
 //填充
 graphics.FillRectangle(&lgb, 0, 0, rect.right, rect.bottom);
}

  本程序使用線性漸變畫刷,當(dāng)畫刷從客戶區(qū)左上角移向客戶區(qū)右下角的過程中,顏色逐漸由藍(lán)色轉(zhuǎn)變?yōu)榫G色。

WindowsGDI和GDI+編程實(shí)例全剖析(3)
圖3 GDI+漸變畫刷

  基數(shù)樣條函數(shù)

  GDI+支持基數(shù)樣條,基數(shù)樣條指的是一連串單獨(dú)的曲線,這些曲線連接起來形成一條較大的曲線。樣條由點(diǎn)(Point結(jié)構(gòu)體)的數(shù)組指定,并通過該數(shù)組中的每一個點(diǎn)?;鶖?shù)樣條平滑地穿過數(shù)組中的每一個點(diǎn)(不出現(xiàn)尖角),因此比用直線連接創(chuàng)建的路徑精確。

void CGdiexampleDlg::OnCardinalSpline()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 //創(chuàng)建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]);
 }
 //利用基數(shù)樣條畫線
 graphics.DrawCurve(&Pen(Color::Red, 3), points, 5);
}

  圖4演示了直接連線和經(jīng)過基數(shù)樣條平滑擬合后的線條的對比,后者的曲線(Curve)沒有尖角。這個工作我們在中學(xué)的數(shù)學(xué)課上把離散的點(diǎn)連接成曲線時做過。

WindowsGDI和GDI+編程實(shí)例全剖析(3)
圖4 GDI+基數(shù)樣條

?

持久的路徑對象

  在GDI中,路徑隸屬于一個設(shè)備上下文,一旦設(shè)備環(huán)境指針超過它的生存期,路徑也會被刪除。利用GDI+,可以創(chuàng)建并維護(hù)與Graphics對象分開的GraphicsPath 對象,它不依賴于Graphics對象的生存期。

  變形和矩陣對象

  GDI+提供了Matrix對象,它是一種可以使變形(旋轉(zhuǎn)、平移、縮放等) 簡易靈活的強(qiáng)大工具,Matrix對象需與要被變形的對象聯(lián)合使用。對于GraphicsPath類,我們可以使用其成員函數(shù)Transform接收Matrix參數(shù)用于變形。

void CGdiexampleDlg::OnTransformationMatrix()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 //創(chuàng)建Graphics對象
 Graphics graphics(dc);
 GraphicsPath path;
 path.AddRectangle(Rect(250, 20, 70, 70));
 graphics.DrawPath(&Pen(Color::Black, 1), &path); // 在應(yīng)用變形矩陣之前繪制矩形
 // 路徑變形
 Matrix matrix1, matrix2;

 matrix1.Rotate(45.0f); //旋轉(zhuǎn)順時針45度
 path.Transform(&matrix1); //應(yīng)用變形
 graphics.DrawPath(&Pen(Color::Red, 3), &path);

 matrix2.Scale(1.0f, 0.5f); //轉(zhuǎn)化成為平行四邊形法則
 path.Transform(&matrix2); //應(yīng)用變形
 graphics.DrawPath(&Pen(Color::Blue, 3), &path);
}

  圖5演示了正方形經(jīng)過旋轉(zhuǎn)和拉伸之后的效果:黑色的為原始圖形,紅色的為旋轉(zhuǎn)45度之后的圖形,藍(lán)色的為經(jīng)過拉伸為平行四邊形后的圖形。

WindowsGDI和GDI+編程實(shí)例全剖析(4)
圖5 GDI+變形和矩陣對象

  可伸縮區(qū)域

  GDI+通過對區(qū)域(Region)的支持極大地擴(kuò)展了GDI。在GDI 中,區(qū)域存儲在設(shè)備坐標(biāo)中,可應(yīng)用于區(qū)域的唯一變形是平移。但是在GDI +中,區(qū)域存儲在全局坐標(biāo)(世界坐標(biāo))中,可對區(qū)域利用變形矩陣進(jìn)行變形(旋轉(zhuǎn)、平移、縮放等)。

void CGdiexampleDlg::OnScalableRegion()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 //創(chuàng)建Graphics對象
 Graphics graphics(dc);
 //創(chuàng)建GraphicsPath
 GraphicsPath path;
 path.AddLine(100, 100, 150, 150);
 path.AddLine(50, 150, 150, 150);
 path.AddLine(50, 150, 100, 100);
 //創(chuàng)建Region
 Region region(&path);
 //填充區(qū)域
 graphics.FillRegion(&SolidBrush(Color::Blue), &region);
 //區(qū)域變形
 Matrix matrix;
 matrix.Rotate(10.0f); //旋轉(zhuǎn)順時針20度
 matrix.Scale(1.0f, 0.3f); //拉伸
 region.Transform(&matrix); //應(yīng)用變形
 //填充變形后的區(qū)域
 graphics.FillRegion(&SolidBrush(Color::Green), &region);
}

  上述程序中以藍(lán)色填充一個三角形區(qū)域,接著將此區(qū)域旋轉(zhuǎn)和拉伸,再次顯示,其效果如圖6。

WindowsGDI和GDI+編程實(shí)例全剖析(4)
圖6 GDI+區(qū)域變形

豐富的圖像格式支持

  GDI +提供了Image、Bitmap 和Metafile 類,方便用戶進(jìn)行圖像格式的加載、操作和保存。GDI+支持的圖像格式有BMP、GIF、JPEG、EXIF、PNG、TIFF、ICON、WMF、EMF等,幾乎涵蓋了所有的常用圖像格式。

void CGdiexampleDlg::OnImage()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 //創(chuàng)建Graphics對象
 Graphics graphics(dc);
 Image image(L "d:\1.jpg");
 //在矩形區(qū)域內(nèi)顯示jpg圖像
 Point destPoints1[3] =
 {
  Point(10, 10), Point(220, 10), Point(10, 290)
 };
 graphics.DrawImage(&image, destPoints1, 3);
 //在平行四邊形區(qū)域內(nèi)顯示jpg圖像
 Point destPoints2[3] =
 {
  Point(230, 10), Point(440, 10), Point(270, 290)
 };
 graphics.DrawImage(&image, destPoints2, 3);
}

  上述程序?qū)盤根目錄下文件名為"1.jpg"的jpg圖像以矩陣和平行四邊形兩種方式顯示,效果如圖7。

WindowsGDI和GDI+編程實(shí)例全剖析(5)
圖7 GDI+多種圖像格式支持

  由此我們可以看出,GDI+在圖像顯示和操作方面的確比GDI簡單許多?;貞浳覀冊凇禫isual C++中DDB與DIB位圖編程全攻略》一文中所介紹的用GDI顯示位圖的方式,其與GDI+圖像處理的難易程度真是有天壤之別。

Alpha混合

  Alpha允許將兩個物體混合起來顯示,在3D氣氛和場景渲染等方面有廣泛應(yīng)用。它能"霧化"圖像,使得一個圖像著色在另一個半透明的圖像上,呈現(xiàn)一種朦朧美。我們知道,一個像素可用R,G,B三個維度來表示,我們可以再加上第4個即:Alpha維度(channel),表征透明程度。

void CGdiexampleDlg::OnAlphaBlend()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 //創(chuàng)建Graphics對象
 Graphics graphics(dc);
 //創(chuàng)建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);
 //在矩形區(qū)域內(nèi)顯示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);
 //在平行四邊形區(qū)域內(nèi)顯示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"的圖像與之進(jìn)行混合,其效果如圖8。

WindowsGDI和GDI+編程實(shí)例全剖析(6)
圖8 GDI+ Alpha混合

  為了能進(jìn)行Alpha混合,我們需要使用ImageAttributes類和ColorMatrix矩陣,ImageAttributes可以進(jìn)行顏色、灰度等調(diào)整從而達(dá)到控制圖像著色方式的目的。ColorMatrix是ImageAttributes類大多數(shù)函數(shù)的參數(shù),它包含了Alpha、Red、Green、Blue維度的值,以及另一維w,順序?yàn)镽GBaw。

  CGdiexampleDlg::OnAlphaBlend()函數(shù)中ColorMatrix的實(shí)例ClrMatrix中元素(4,4)的值為0.5,表示Alpha度的值為0.5(即半透明)。在ColorMatrix中,元素(5,5)的值恒定為1.0。我們把ClrMatrix的元素(0,0)修改為0.0,即使得圖像2.jpg的紅色維度全不顯示,再看效果,為圖9。列位讀者,我們以前在豪杰超級解霸中調(diào)整R,G,B值從而控制圖像輸出顏色的時候,調(diào)的就是這個東東!圖9的效果很像破舊彩色電視機(jī),紅色電子槍"嗝"了。剛大學(xué)畢業(yè)時,俺那個叫窮啊,就買了這么個電視機(jī),還看得很爽,真是往事不堪回首!

WindowsGDI和GDI+編程實(shí)例全剖析(6)
圖9 GDI+中的ColorMatrix

強(qiáng)大的文字輸出

  GDI+擁有極其強(qiáng)大的文字輸出處理能力,輸出文字的顏色、字體、填充方式都可以直接作為Graphics類DrawString成員函數(shù)的參數(shù)進(jìn)行設(shè)置,其功能遠(yuǎn)勝過GDI設(shè)備上下文的TextOut函數(shù)。

void CGdiexampleDlg::OnText()
{
 // TODO: Add your command handler code here
 CClientDC dc(this);
 //創(chuàng)建Graphics對象
 Graphics graphics(dc);
 //創(chuàng)建20號"楷體"字體
 FontFamily fontFamily1(L "楷體_GB2312"); // 定義"楷體"字樣
 Font font1(&fontFamily1, 20, FontStyleRegular, UnitPoint);
 //定義輸出UNICODE字符串
 WCHAR string[256];
 wcscpy(string, L "天極網(wǎng)的讀者朋友,您好!");
 //以藍(lán)色畫刷和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));
 //以線性漸變畫刷和創(chuàng)建的20號"楷體"顯示字符串
 graphics.DrawString(string, (INT)wcslen(string), &font1, PointF(30, 50),&linGrBrush);
 //創(chuàng)建20號"華文行楷"字體
 FontFamily fontFamily2(L "華文行楷"); // 定義"楷體"字樣
 Font font2(&fontFamily2, 20, FontStyleRegular, UnitPoint);
 //以線性漸變畫刷和20號"華文行楷"顯示字符串
 graphics.DrawString(string, (INT)wcslen(string), &font2, PointF(30, 90),&linGrBrush);
 //以圖像創(chuàng)建畫刷
 Image image(L "d:\3.jpg");
 TextureBrush tBrush(&image);
 //以圖像畫刷和20號"華文行楷"顯示字符串
 graphics.DrawString(string, (INT)wcslen(string), &font2, PointF(30, 130),&tBrush);
 //創(chuàng)建25號"華文中宋"字體
 FontFamily fontFamily3(L "華文中宋"); // 定義"楷體"字樣
 Font font3(&fontFamily2, 25, FontStyleRegular, UnitPoint);
 //以圖像畫刷和20號"華文行楷"顯示字符串
 graphics.DrawString(string, (INT)wcslen(string), &font3, PointF(30, 170),&tBrush);
}

  上述代碼的執(zhí)行效果如圖10所示,字體、顏色和填充都很豐富!