Microsoft Windows GDI+ 不同於GDI體現在2個方面。第一,GDI+通過提供新的功能擴展了GDI的特性,比如漸變畫刷和半透明混合。第二,編程模型的改進使得圖形開發更加簡單和靈活。
相對於GDI的新特性
漸變畫刷(Gradient Brushes)
GDI+通過提供用於填充圖形、路徑、區域的線性漸變和路徑漸變畫刷來擴展了GDI的功能。漸變畫刷同樣能夠被用來繪製線條、曲線、路徑。當您採用線性漸變畫刷填充圖形的時候,顏色將在穿越該圖形時逐漸改變,例如,假設您創建了一個水平漸變畫刷,同時指定左邊緣爲藍色、右邊緣爲綠色。當您採用水平漸變畫刷填充圖形時,顏色將從它的左邊到右邊逐漸由藍色過渡到綠色。同樣地,一個垂直漸變畫刷填充的圖形的顏色將自上而下漸變。下面兩幅插圖顯示了一個水平漸變畫刷填充的橢圓和一個對角漸變畫刷填充的區域。
如果您採用路徑漸變畫刷填充圖形,您可以有許多選擇來指定顏色從圖形的一個區域到另一個區域如何過渡。一種選擇就是設置一箇中心顏色和邊界顏色,這樣圖形由內向外象素顏色將逐漸改變。下圖顯示的是一個由路徑漸變畫刷填充的路徑(由一對貝塞爾樣條所創建)。
基數樣條(Cardinal Splines)
GDI+支持基數樣條,而GDI不支持。基數樣條是一組單個曲線按照一定的順序連接而成的一條較大麴線。樣條由一系列點指定,並通過每一個指定的點。由於基數樣條平滑地穿過組中的每一個點(不出現尖角),因而它比用直線連接創建的路徑更精確。下面是分別使用兩種方法創建的圖形,一個使用基數樣條,一個使用直線。
獨立的路徑對象(Independent Path Objects)
在GDI中,一條路徑是屬於一個設備場景的,在繪製完成後就被銷燬了。在GDI+中,繪圖工作由Graphics對象來完成,您可以創建和保留幾個與Graphics獨立的GraphicsPath對象。繪圖操作時GraphicsPath對象不被破壞,這樣您就可以多次使用同一個GraphicsPath對象畫路徑了。
變換和矩陣對象(Transformations and the Matrix Object)
GDI+提供了矩陣對象,一個非常強大的工具,使得圖形變換(旋轉、平移等)變得簡單靈活。一個矩陣對象總是和一個被變換的圖形對象相聯繫起來。比方說,GraphicsPath對象有一個Transform方法,它的一個參數能夠接受Matrix對象的地址。單個3×3矩陣可以存儲一個變換或者一系列變換。下圖是一個路徑變換前後的例子(先縮放後旋轉)。
可伸縮區域(Scalable Regions)
GDI+在對區域的支持上對GDI進行了很大的改進。在GDI中,區域存儲在設備座標中,唯一可進行的區域變換的操作是平移。而GDI+用世界座標存儲區域,允許對區域進行任何圖形變換(例如縮放),這種變換存儲在一個變換矩陣中。下圖顯示的是經過連續3個變換前後的一個區域:縮放、旋轉、平移
Alpha混合(Alpha Blending)
請注意上圖中,您可以通過變換後的區域(陰影填充)看到變換前的區域(紅色填充)。這種效果可以通過GDI+支持的α混合來實現。利用α混合,您可以指定填充顏色的透明度。透明色將與背景色混合— 填充色越透明,背景色顯示越清晰。下圖所示的四個橢圓被填充了同樣的顏色(紅色),但由於擁有不同的透明度而呈現不同的顯示效果。
多種圖像格式支持(Support for Multiple Image)
GDI+提供了Image、Bitmap和Metafile類,用於載入、保存和處理多種格式的圖形。以下格式均支持:BMP、Graphics Interchange format(GIF)、JPEG、Exif、PNG、TIFF、ICON、WMF、EWF
編程模式的變化
設備場景(Device Context)、句柄(Handles)和Graphics對象
如果您曾經使用過GDI編寫過應用程序,您肯定對設備場景(DC)的概念非常熟悉。設備場景是Windows使用的一個數據結構,用於存儲具體設備性能和與如何在設備上繪製項目的相關信息。而且視頻顯示器的設備場景還與顯示器上的特定窗口有關。首先您必須獲得一個設備場景句柄(HDC),然後在圖形繪製時您把這個句柄作爲一個參數傳遞給GDI函數。當然您也可以把它傳遞給獲得或設置設備場景有關屬性的函數。
您在使用GDI+的時候,您不必像在GDI中那樣關心設備場景句柄。您只需簡單地創建一個Graphics對象,然後以您熟悉的面向對象的方式(比如myGraphicsObject.DrawLine(parameters))調用它的方法即可。Graphics對象是GDI+的核心,正如設備場景是GDI的核心一樣。設備場景(DC)和圖形對象(Graphics)在不同的環境下扮演着同樣的角色,發揮着類似的作用,但是兩者也存在着本質的不同。前者使用基於句柄的編程模型而後者使用面向對象的編程模型。
Graphics對象和設備場景一樣,與屏幕上的特定窗體有關,它包含着圖形繪製的有關屬性信息(比如,平滑模式和隱式文本渲染)。然而,Graphics對象並沒有像GDI那樣與Pen、Path、Image或者Font等攪在一起。比如,在GDI中,您必須先調用SelectObject將一個Pen對象與設備場景關聯,然後您才能使用設備場景繪製一條線。這被稱爲將畫筆選入設備場景。您在設備場景中繪製的所有線條都將採用這個畫筆直到您選擇另一個畫筆爲止。在GDI+中,您只需將一個Pen對象作爲參數傳遞給Graphics類的DrawLine方法即可。您可以在一連串的DrawLine調用中傳入不同的Pen對象,而不必將給定的Pen對象與Graphics對象關聯。
畫線的兩種方法(Two Ways to Draw a Line)
用GDI畫線
HDC hdc;
PAINTSTRUCT ps;
HPEN hPen;
HPEN hPenOld;
hdc = BeginPaint(hWnd, &ps);
hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
hPenOld = (HPEN)SelectObject(hdc, hPen);
MoveToEx(hdc, 20, 10, NULL);
LineTo(hdc, 200, 100);
SelectObject(hdc, hPenOld);
DeleteObject(hdc);
EndPaint(hWnd, &ps);
採用GDI畫線,您需要2個對象:設備場景和畫筆。您通過調用CreatePen獲取一個句柄。緊接着,您調用SelectObject將畫筆選入設備場景。然後調用MoveToEx將畫筆位置設爲(20,10),然後調用繪製一條直線到位置(200,100)。注意MoveToEx和LineTo均需要一個hdc作爲參數。
用GDI+和C++類接口畫線
HDC hdc;
PAINTSTRUCT ps;
Pen* myPen;
Graphics* myGraphics;
hdc = BeginPaint(hWnd, &ps);
myPen = new Pen(Color(255, 255, 0, 0), 3);
myGraphics = new Graphics(hdc);
myGraphics->DrawLine(myPen, 20, 10, 200, 100);
delete myGraphics;
delete myPen;
EndPaint(hWnd, &ps);
通過GDI+和C++類接口畫線,您需要一個Graphics對象和一個Pen對象。注意您不需要向這些對象提供窗體句柄。相反,您只需要構造一個Graphics類(一個Graphics對象)和一個Pen類(一個Pen對象)即可。畫線涉及Graphics類的Graphics::DrawLine方法。Graphics::DrawLine方法的第一個參數是一個Pen對象的指針。較之前面將Pen選入設備場景的GDI例子,這種方案更加更加簡單靈活。
不再有當前位置(No More Current Position)
注意,前面顯示的DrawLine方法同時提供了線段的起點和終點作爲參數。不同的是,在GDI中您必須先設置當前位置然後才能繪製一個起於(x1, y1)終於(x2, y2)的線段。GDI+中已經完全摒棄了當前位置的概念。
繪圖與填充方法分離(Separate Methods for Draw and Fill)
在繪製圖形比如矩形的邊框和填充其內部區域方面,GDI+比GDI更加靈活。GDI有個Rectangle函數可以一步實現繪製矩形邊框和填充其內部區域。邊框採用當前選入的畫筆繪製,內部區域採用當前選入的筆刷填充。
hBrush = CreateHatchBrush(HS_CROSS, RGB(0, 0, 255));
hPen = CreatePen(HS_SOLID, 3, RGB(255, 0, 0));
SelectObject(hdc, hBrush);
SelectObject(hdc, hPen);
Rectangle(hdc, 100, 50, 200, 80);
在GDI+中,繪製矩形邊框和填充其內部區域的方法是獨立開來的。Graphics 類的DrawRectangle方法有一個參數,用於傳入Pen對象的地址。而FillRectangle方法有一個參數,用於傳入Brush對象的地址。
HatchBrush* myHatchBrush = new HatchBrush(
HatchStyleCross,
Color(255, 0, 255, 0);
Color(255, 0, 0, 255));
Pen* myPen = new Pen(Color(255, 255, 0, 0), 3);
myGraphics.FillRectangle(myHatchBrush, 100, 50, 100, 30);
myGraphics.DrawRectangle(myPen, 100, 50, 100, 30);
注意,GDI+中FillRectangle和DrawRectangle方法的參數指定矩形的左、上、寬度和高度。相對地,在GDI的Rectangle函數中,參數指定的是矩形的左、右、上、下。同時還要注意,GDI+中的Color類的構造函數有4個參數。後面的3個參數就是通常的紅、綠、藍的值;第一個參數指定的是alpha半透明值,它表示該顏色與背景色混合的程度。
構造區域(Constructing Regions)
GDI提供了幾個函數可以創建區域:CreateRectRgn、CreateEllpticRgn、CreateRoundRectRgn、CreatePolygonRgn和CreatePolyPolygonRgn。您可能也期望GDI+中的Region類提供類似的構造函數,傳入矩形、橢圓、圓角矩形和多邊形作爲參數,但是情況並非如此。在GDI+中,Region類提供一個傳入Rect對象引用的構造函數,和另一個傳入GraphicsPath對象地址的構造函數。如果您想創建一個基於橢圓、圓角矩形或者多邊形的區域,您可以很容易地通過創建一個GraphicsPath對象(包含橢圓等),然後將GraphicsPath對象的地址傳遞給Region構造函數即可。
在GDI+中可以很簡單地通過圖形和路徑的組合創建一個複雜的區域。Region類有Union和Intersect方法,用於加入一個路徑或者另一個區域到一個已有區域中。GDI+中一個很好的特性是,當一個GraphicsPath對象作爲Region構造函數的參數傳入的時候,它本身並不會被銷燬。而在GDI中,您可以通過PathToRegion函數將一個路徑轉換爲一個區域,但是同時路徑也被銷燬了。同樣地,GraphicsPath對象在其地址作爲Union(合併)和Intersect(相交)方法的參數傳入時本身也不會被銷燬,因此您可以將一個指定的路徑作爲一個組成部分來創建幾個獨立的區域。下面例子中,假設onePath是一個GraphicsPath對象(簡單抑或複雜)的指針並且已經被初始化:
Region region1(rect1);
Region region2(rect2);
Region1.Union(onePath);
Region2.Intersect(onePath);