轉載連接:http://blog.csdn.net/byxdaz/article/details/5972759html
GDI+(Graphics Device Interface Plus圖形設備接口加)是Windows XP和Windows Server 2003操做系統的子系統,也是.NET框架的重要組成部分,負責在屏幕和打印機上繪製圖形圖像和顯示信息。git
GDI+不但在功能上比GDI 要強大不少,並且在代碼編寫方面也更簡單,所以會很快成爲Windows圖形圖像程序開發的首選。程序員
1、 GDI+的特色和新增功能算法
GDI+與GDI同樣,都具備設備無關性。應用程序的程序員可利用GDI+這樣的圖形設備接口在屏幕或打印機上顯示信息,而不須要考慮特定顯示設備的具體狀況。應用程序的程序員調用GDI+類提供的方法,而這些方法又反過來相應地調用特定的設備驅動程序。GDI+將應用程序與圖形硬件隔離,而正是這種隔離容許開發人員建立設備無關的應用程序。編程
|
|
|
|
|
一、 GDI+的功能數組
GDI+主要提供瞭如下三種功能:緩存
1) 二維矢量圖形安全
矢量圖形包括座標系統中的系列點指定的繪圖基元(如直線、曲線和圖形)。例如,直線可經過它的兩個端點來指定,而矩形可經過肯定其左上角位置的點並給出其寬度和高度的一對數字來指定。簡單路徑可由經過直線鏈接的點的數組來指定。貝塞爾樣條是由四個控制點指定的複雜曲線。網絡
GDI+提供了存儲基元自身相關信息的類(結構)、存儲基元繪製方式相關信息的類,以及實際進行繪製的類。例如,Rectangle結構存儲矩形的位置和尺寸;Pen類存儲有關線條顏色、線條粗細和線型的信息;而Graphics類具備用於繪製直線、矩形、路徑和其它圖形的方法(相似於GDI中的CDC類)。還有幾種Brush類,它們存儲有關如何使用顏色或圖案來填充封閉圖形和路徑的信息。框架
用戶能夠在圖元文件中記錄矢量圖像(圖形命令的序列)。GDI+提供了Metafile類,可用於記錄、顯示和保存圖元文件。MetafileHeader和MetaHeader類容許您檢查圖元文件頭中存儲的數據。
2) 圖像處理
某些種類的圖片很難或者根本沒法用矢量圖形技術來顯示。例如,工具欄按鈕上的圖片和顯示爲圖標的圖片就難以指定爲直線和曲線的集合。擁擠的棒球運動場的高分辨率數字照片會更難以使用矢量技術來製做。這種類型的圖像可存儲爲位圖,即表明屏幕上單個點顏色的數字數組。
GDI+提供了Image、Bitmap和Metafile類,可用於顯示、操做和保存位圖。它們支持衆多的圖像文件格式,還能夠進行多種圖像處理的操做。
3) 文字顯示版式
就是使用各類字體、字號和樣式來顯示文本。GDI +爲這種複雜任務提供了大量的支持。GDI+中的新功能之一是子像素消除鋸齒,它可使文本在LCD 屏幕上呈現時顯得比較平滑。
二、 GDI+新增特性
1)、漸變畫刷
漸變畫刷(gradient brush梯度刷)經過提供用於填充圖形、路徑和區域的線性漸變畫筆和路徑漸變畫筆,GDI+擴展了GDI 的功能。漸變畫筆還可用於繪製直線、曲線和路徑。線性漸變畫筆可用於使用顏色來填充圖形,畫筆在圖形中移動時,顏色會逐漸改變。例如,假定經過指定圖形左邊爲藍色、右邊爲綠色,建立了一個水平漸變畫筆。當用水平漸變畫筆填充該圖形時,隨着畫筆從圖形的左邊移至右邊,顏色就會由藍色逐漸變爲綠色。用相似方法定義的垂直漸變畫筆填充的圖形,顏色從上到下變化。圖顯示了用水平漸變畫筆填充的橢圓和用斜式漸變畫筆填充的區域。
圖 水平和斜式漸變畫筆
用路徑漸變畫筆填充圖形時,可選擇不一樣的方法來指定當從圖形的一部分至另外一部分移動畫筆時顏色的變化方式。一種選擇是指定中心顏色和邊緣顏色,在從圖形中間向外邊緣移動畫筆時,像素逐漸從一種顏色變化到另外一種顏色。圖顯示了用路徑漸變畫筆填充的路徑(該路徑是用一對貝塞爾樣條建立的)。
圖 路徑漸變畫筆
2)、基數樣條函數
GDI+支持在GDI 中不支持的基數樣條(cardinal spines)。基數樣條是一連串單獨的曲線,這些曲線鏈接起來造成一條較長的光滑曲線。樣條由點的數組指定,並經過該數組中的每個點。基數樣條平滑地(沒有銳角)經過數組中的每個點,所以,比經過鏈接直線建立的路徑更光滑精準。圖顯示了兩個路徑:一個以基數樣條的形式建立;另外一個經過鏈接直線建立。
圖 基數樣條路徑和折線路徑
3)、持久路徑對象
在GDI 中,路徑屬於設備上下文,而且會在繪製時被毀壞。利用GDI +,繪圖由Graphics對象執行,能夠建立並維護幾個與Graphics對象分開的持久的路徑對象(persistent path object)—— GraphicsPath對象。繪圖操做不會破壞GraphicsPath 對象,所以能夠屢次使用同一個GraphicsPath 對象來繪製路徑。
4)、變換和矩陣對象
GDI+提供了Matrix(矩陣) 對象,它是一種可使(縮放、旋轉和平移等)變換(transformation)簡易靈活的強大工具。矩陣對象通常與變換對象聯合使用。例如,GraphicsPath 對象具備Transform 方法,此方法接收Matrix 對象做爲參數。單一的3×3矩陣可存儲一種變換或一個變換序列。圖顯示了一個路徑在執行兩種變換先後的狀況。
圖 路徑的變換
5)、可伸縮區域
GDI+ 經過對可伸縮區域(Scalable Regions)的支持極大地擴展了GDI。在GDI 中,區域被存儲在設備座標中,並且,可應用於區域的唯一變換是平移。而GDI+在全局座標中存儲區域,而且容許區域發生任何可存儲在變換矩陣中的變換(如縮放和旋轉)。圖顯示一個區域在執行三種變換(縮放、旋轉和平移)先後的狀況。
圖 區域的三種變換(縮放、旋轉和平移)
6)、α混色
在下圖中,能夠在變換區域(用藍色陰影畫筆填充)中看到未變換區域(用紅色填充)。這是由GDI+支持的α混色(Alpha Blending,透明混合)實現的。使用α混色,能夠指定填充顏色的透明度。透明色與背景色相混合———填充色越透明,透出的背景色就越多。圖顯示四個用相同顏色(紅色)填充、但透明層次不一樣的橢圓。
圖 不一樣透明度
7)、豐富的圖像格式支持
GDI+提供Image、Bitmap 和Metafile 類,能夠用不一樣的格式加載、保存和操做圖像。GDI+支持BMP、GIF、JPEG、EXIF、PNG、TIFF、ICON、WMF、EMF共9種常見的圖像格式。
8)、GDI+的不足
雖然,相對於GDI來講,GDI+ 確實增長了許多新特性,並且功能更強大,使用也更方便。可是,這並不等於GDI+ 就可以徹底代替GDI。
由於GDI+其實是GDI的封裝和擴展,GDI+的執行效率通常要低於GDI的。另外,GDI+不支持圖的位運算,那麼就不能進行異或繪圖等操做。並且在VC中,GDI+ 還不支持雙緩存機制(如內存DC和顯示DC),這將大大影響GDI+ 在高速圖形、圖像、動畫和視頻等方面的應用。
三、 GDI+的使用
1) GDI+開發包
若採用的是Visual C++ 2008,則已經包含了開發GDI+應用程序所需的全部東西。若是使用的是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文件。
若是你使用的操做系統是Windows XP或Windows Server 2003,則GDI+所對應的動態連接庫,已經被包含在其中。gdiplus.dll通常位於操做系統的WinSxS(Windows side-by-side assembly,視窗並行程序集)目錄中,例如:
C:/WINDOWS/WinSxS/x86_Microsoft.Windows.GdiPlus_6595b64144ccf1df_1.0.0.0_x-ww_8d353f13/gdiplus.dll(1661KB,2002.10.8)
而GDI的動態連接庫gdi32.dll,卻通常在操做系統的32位系統目錄中:
F:/WINDOWS/system32/gdi32.dll(272KB,2004.8.4)
2) VC使用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")
//在CWinApp派生類的InitInstance函數中加入:
//初始化gdiplus的環境
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
// 初始化GDI+.
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
//在CWinApp派生類的ExitInstance函數中加入:
//關閉gdiplus的環境
GdiplusShutdown(gdiplusToken);
四、GDI+的組成
GDI+ API包含54個類、12個函數、6類(226個)圖像常量、55種枚舉和19種結構。
1)、類
GDI+ API中共有54個類,核心類是Graphics,它是實際繪製直線、曲線、圖形、圖像和文本的類。許多其它GDI+類是與Graphics類一塊兒使用的。例如,DrawLine方法接收Pen對象,該對象中存有所要繪製的線條的屬性(顏色、寬度、虛線線型等)。FillRectangle方法能夠接收指向LinearGradientBrush對象的指針,該對象與Graphics對象配合工做來用一種漸變色填充矩形。Font和StringFormat對象影響Graphics對象繪製文本的方式。Matrix對象存儲並操做Graphics對象的仿射變換——旋轉、縮放和翻轉圖像。
GDI+還提供了用於組織圖形數據的幾種結構類(例如 Rect、Point和Size)。並且,某些類的主要做用是結構化數據類型。例如,BitmapData類是Bitmap類的幫助器,PathData類是GraphicsPath類的幫助器。
下面是全部GDI+的API類的列表:
GDI+的API類(54個)
名稱 |
類 |
功能 |
調整箭頭帽 |
AdjustableArrowCap |
建立自定義箭頭線帽 |
位圖 |
Bitmap |
提供裝入和保存矢量和光柵圖像的方法,並能夠建立和操做光柵圖像 |
位圖數據 |
BitmapData |
保存位圖的屬性 |
模糊 |
Blur |
將高斯模糊效果做用到圖像 |
亮度對比度 |
BrightnessContrast |
改變圖像的亮度和對比度 |
刷 |
Brush |
定義刷對象 |
緩存圖像 |
CachedBitmap |
用爲特色設備顯示而優化過的格式存儲位圖 |
字符範圍 |
CharacterRange |
指定串內字符位置的範圍 |
顏色 |
Color |
保存表示顏色的32位值 |
色平衡 |
ColorBalance |
改變位圖的顏色平衡 |
顏色曲線 |
ColorCurve |
可調整位圖的曝光度、密度、對比度、加亮、陰影、色調、白飽和和黑飽和。 |
顏色查找表 |
ColorLUT |
用於定製位圖的顏色調整 |
顏色矩陣效果 |
ColorMatrixEffect |
對位圖進行仿射變換 |
定製線帽 |
CustomLineCap |
封裝了自定義線帽 |
效果 |
Effect |
做用於圖像的效果和調整類的基類 |
編碼器參數 |
EncoderParameter |
保存圖像編碼器的參數 |
編碼器參數組 |
EncoderParameters |
圖像編碼器參數的數組 |
字體 |
Font |
封裝了字體的族系、高度、大小和風格等特性 |
字體集 |
FontCollection |
包含枚舉字體集中的字體族系的方法 |
字體族 |
FontFamily |
封裝了構成一個字體族的字體集合 |
GDI+基類 |
GdiplusBase |
提供對GDI+對象的存儲分配與釋放,是其它GDI+類的基類 |
圖形 |
Graphics |
提供繪製圖形、圖像和文本的方法,存儲顯示設備和被畫項目的屬性 |
圖形路徑 |
GraphicsPath |
保存一個供繪圖用的直線、曲線和形狀序列 |
圖形路徑迭代器 |
GraphicsPathIterator |
提供從保存在GraphicsPath對象中的路徑裏選擇孤立子集的方法 |
影線刷 |
HatchBrush |
定義具備影線風格和前景色/背景色的矩形刷 |
色調飽和度亮度 |
HueSaturationLightness |
改變位圖的色調H、飽和度S和亮度L |
圖像 |
Image |
提供裝入和保存矢量和光柵圖像的方法 |
圖像屬性 |
ImageAttributes |
含渲染時如何操做位圖和圖元文件顏色的信息 |
圖像編解碼信息 |
ImageCodecInfo |
存儲與圖像編解碼有關的信息 |
圖像項數據 |
ImageItemData |
用於存儲和獲取自定義圖像的元數據 |
已裝入字體集 |
InstalledFontCollection |
定義表示已裝入系統中的字體集 |
級別 |
Levels |
可調整位圖的加亮、陰影和色調 |
線形梯度刷 |
LinearGradientBrush |
定義線性漸變刷 |
矩陣 |
Matrix |
表示3×3的仿射變換矩陣 |
圖元文件 |
Metafile |
定義包含描述一系列圖形API調用記錄的圖形元文件,可被記錄(構造)和回放(顯示) |
圖元文件頭 |
MetafileHeader |
保存關聯圖元文件的性質 |
路徑數據 |
PathData |
GraphicsPath和GraphicsPathIterator類的助手類,用於獲取和設置路徑中的數據點及其類型 |
路徑梯度刷 |
PathGradientBrush |
保存顏色的梯度屬性,用於漸變色填充路徑內部 |
筆 |
Pen |
用於繪製直線和曲線的筆對象 |
點 |
Point |
封裝2D整數座標系統中的點 |
浮點點 |
PointF |
封裝2D浮點座標系統中的點 |
專用字體集 |
PrivateFontCollection |
保存用於特定應用程序的字體集,可含未裝入系統中的字體 |
特性項 |
PropertyItem |
Image和Bitmap類的助手類,保存一塊(piece)圖像元數據 |
矩形 |
Rect |
保存矩形的左上角、寬度和高度之對象(整數) |
浮點矩形 |
RectF |
保存矩形的左上角、寬度和高度之對象(浮點數) |
紅眼校訂 |
RedEyeCorrection |
校訂有時在閃光照片中出現的紅眼 |
區域 |
Region |
描述顯示錶面的範圍,能夠是任意形狀 |
銳化 |
Sharpen |
調整位圖的清晰度 |
大小 |
Size |
封裝2D整數座標系統中的寬和高 |
浮點大小 |
SizeF |
封裝2D浮點數座標系統中的寬和高 |
實心刷 |
SolidBrush |
定義實心顏色的刷子對象 |
串格式 |
StringFormat |
封裝文本的格式(layout)信息和顯示操做 |
紋理刷 |
TextureBrush |
用於填充的包含圖像對象的刷子 |
濃淡 |
Tint |
改變位圖的色彩濃淡 |
下面是GDI+ API類的層次結構圖:
GdiplusBase |
Graphics |
GraphicsPath |
GraphicsPathIterator |
Pen |
Brush |
SolidBrush |
HatchBrush |
TextureBrush
|
LinearGradientBrush |
PathGradientBrush |
Image |
Bitmap |
Metafile |
CustomLineCap |
AdjustableArrowCap |
CachedBitmap |
ImageAttributes |
FontCollection |
InstalledFontCollection |
PrivateFontCollection |
StringFormat |
Region |
Font |
FontFamily |
Matrix |
Point |
PointF |
Size |
SizeF |
Rect |
RectF |
Color |
Effect |
Blur |
BrightnessContrast |
ColorBalance |
ColorCurve |
ColorLUT |
ColorMatrixEffect |
HueSaturationLightness |
Levels |
RedEyeCorrection |
Sharpen |
Tint |
ImageItemData |
BitmapData |
MetafileHeader |
PropertyItem |
EncoderParameter |
EncoderParameters |
ImageCodecInfo |
PathData |
CharacterRange |
獨立類 |
繪圖類 |
效果類 |
GDI+類的層次結構圖
2)、函數
GDI+命名空間中的函數(12個)
名稱 |
函數 |
功能 |
關閉GDI+ |
GdiplusShutdown |
清除GDI+所使用的資源 |
啓動GDI+ |
GdiplusStartup |
初始化GDI+ |
獲取圖像解碼器 |
GetImageDecoders |
獲取含有可用圖像解碼器信息的ImageCodecInfo對象數組 |
獲取圖像解碼器的大小 |
GetImageDecodersSize |
獲取含有可用圖像解碼器的數目 |
獲取圖像編碼器 |
GetImageEncoders |
獲取含有可用圖像編碼器信息的ImageCodecInfo對象數組 |
獲取圖像編碼器的大小 |
GetImageEncodersSize |
獲取含有可用圖像編碼器的數目 |
獲取像素格式大小 |
GetPixelFormatSize |
返回指定像素格式的每像素二進制位數 |
是否爲α像素格式 |
IsAlphaPixelFormat |
肯定指定像素格式是否有α份量 |
是否爲規範像素格式 |
IsCanonicalPixelFormat |
肯定指定像素格式是否爲規範格式之一 |
是否爲擴展像素格式 |
IsExtendedPixelFormat |
肯定指定像素格式是否使用16位色 |
是否爲索引像素格式 |
IsIndexedPixelFormat |
肯定指定像素格式是不是索引格式 |
對象類型是否有效 |
ObjectTypeIsValid |
肯定ObjectType枚舉元素是否表示一個有效對象類型 |
3)、常量
GDI+中定義了以下6類圖像常量(226個):(GdiplusImaging.h)
類型 |
常量 |
說明 |
圖像 文件 格式 ImageFormat* (11個) |
ImageFormatBMP |
BMP(BitMaP位圖) |
ImageFormatEMF |
EMF(Enhanced MetaFile加強圖元文件) |
|
ImageFormatEXIF |
Exif(Exchangeable Image File可交換圖像文件) |
|
ImageFormatGIF |
GIF(Graphics Interchange Format圖形交換格式) |
|
ImageFormatIcon |
Icon(圖標) |
|
ImageFormatJPEG |
JPEG(Joint Photographic Experts Group聯合圖象專家組) |
|
ImageFormatMemoryBMP |
從內存位圖構造的圖像 |
|
ImageFormatPNG |
PNG(Portable Network Graphics可移植網絡圖形) |
|
ImageFormatTIFF |
TIFF(Tagged Image File Format標籤圖像文件格式) |
|
ImageFormatUndefined |
不能肯定格式 |
|
ImageFormatWMF |
WMF(Windows Metafile Format視窗圖元文件格式) |
|
圖像 幀維 |
FrameDimensionPage |
多幀TIFF圖像 |
FrameDimensionTime |
多幀GIF圖像 |
|
圖像 編碼器 (13個) |
EncoderChrominanceTable |
色度表 |
EncoderColorDepth |
顏色深度 |
|
EncoderColorSpace |
顏色空間 |
|
EncoderCompression |
壓縮 |
|
EncoderLuminanceTable |
亮度表 |
|
EncoderQuality |
質量 |
|
EncoderRenderMethod |
渲染方法 |
|
EncoderSaveFlag |
保存標誌 |
|
EncoderScanMethod |
掃描方法 |
|
EncoderTransformation |
變換 |
|
EncoderVersion |
版本 |
|
EncoderImageItems |
圖像項 |
|
EncoderSaveAsCMYK |
保存爲CMYK(Cyan青、Magenta品紅、Yellow黃、blacK黑,用於印刷的四分色) |
|
圖像 像素 格式 (14個) |
PixelFormat1bppIndexed |
每像素1位,索引色 |
PixelFormat4bppIndexed |
每像素4位,索引色 |
|
PixelFormat8bppIndexed |
每像素8位,索引色 |
|
PixelFormat16bppARGB1555 |
每像素16位,α份量1位、RGB份量各5位 |
|
PixelFormat16bppGrayScale |
每像素16位,灰度 |
|
PixelFormat16bppRGB555 |
每像素16位,RGB份量各5位,另1位未用 |
|
PixelFormat16bppRGB565 |
每像素16位,RB份量各5位、G份量6位 |
|
PixelFormat24bppRGB |
每像素24位,RGB份量各8位 |
|
PixelFormat32bppARGB |
每像素32位,αRGB份量各8位 |
|
PixelFormat32bppPARGB |
每像素32位,αRGB份量各8位,RGB份量預乘α份量 |
|
PixelFormat32bppRGB |
每像素24位,RGB份量各8位,另8位未用 |
|
PixelFormat48bppRGB |
每像素48位,RGB份量各16位 |
|
PixelFormat64bppARGB |
每像素64位,αRGB份量各16位 |
|
PixelFormat64bppPARGB |
每像素64位,αRGB份量各16位,RGB份量預乘α份量 |
|
圖像 特性 標誌 類型 (9個) |
PixelFormat4bppIndexed |
格式爲每像素4位,索引色 |
PropertyTagTypeASCII |
值數據成員爲以null結尾的ASCII字符串 |
|
PropertyTagTypeByte |
值數據成員爲字節數組 |
|
PropertyTagTypeLong |
值數據成員爲32位無符號長整數的數組 |
|
PropertyTagTypeRational |
值數據成員爲32位無符號長整數對的數組,每對數中的第一個整數爲分子,第二個整數爲分母 |
|
PropertyTagTypeShort |
值數據成員爲16位無符號短整數的數組 |
|
PropertyTagTypeSLONG |
值數據成員爲32位有符號長整數的數組 |
|
PropertyTagTypeSRational |
值數據成員爲32位有符號長整數對的數組,每對數中的第一個整數爲分子,第二個整數爲分母 |
|
PropertyTagTypeUndefined |
值數據成員爲字節數組,可保存任何數據類型的值 |
|
圖像 特性 標誌 (217個) |
PropertyTagGpsVer ~ |
GPS(Global Positioning Systems全球定位系統)版本 |
PropertyTagGpsDestDist |
(0x0000)~ 到目標點的距離(0x001A)(27個) |
|
PropertyTagNewSubfileType ~ |
子文件數據類型(0x00FE)~ |
|
PropertyTagPageNumber |
被掃描圖像的頁數(0x0129)(44個) |
|
PropertyTagTransferFunction |
圖像傳送函數表(0x012D) |
|
PropertyTagSoftwareUsed |
指定用於生成圖像的設備之軟件或固件的名稱和版本的以null結尾的字符串(0x0131) |
|
PropertyTagDateTime |
圖像建立的日期和時間(0x0132) |
|
PropertyTagArtist ~ |
指定圖像建立者姓名的以null結尾的字符串(0x013B) |
|
PropertyTagTileByteCounts |
~ 標題的字節數(0x0145)(11個) |
|
PropertyTagInkSet ~ |
在分開圖像中使用的墨水集(0x014C) |
|
PropertyTagNumberOfInks |
~ 墨水數目(0x014D)(3個) |
|
PropertyTagDotRange ~ |
對應於0%點和100%點的顏色份量值(0x0150)~ |
|
PropertyTagTransferRange |
擴充傳送函數範圍的值表(0x0156)(7個) |
|
PropertyTagJPEGProc ~ |
JPEG壓縮過程(0x0200)~ |
|
PropertyTagImageTitle |
圖像標題的以null結尾的字符串(0x0320)(17個) |
|
PropertyTagResolutionXUnit ~ |
顯示水平分辨率的單位(0x5001)~(27個) |
|
PropertyTagThumbnailData |
RGB或JPEG中的原始縮略圖中的位數據(0x501B) |
|
PropertyTagThumbnailImage Width ~ |
略圖像的每行像素數(0x5020)~(28個) |
|
PropertyTagThumbnailCopy Right |
含縮略圖像版權信息的以null結尾的字符串(0x503B) |
|
PropertyTagLuminanceTable |
亮度表(0x5090) |
|
PropertyTagFrameDelay ~ |
GIF動畫中兩幀之間的延時,單位爲10毫秒(0x5100) |
|
PropertyTagPaletteHistogram |
~ 調色板直方圖(0x5113)(9個) |
|
PropertyTagCopyright ~ |
含版權信息的以null結尾的字符串(0x8298B)~ |
|
PropertyTagExifCfaPattern |
顏色濾波器數組(0xA302)(48個) |
4)、枚舉
GDI+定義了55種枚舉,它們都是相關常數的集合。例如,LineJoin枚舉包含元素Bevel、Miter和Round,它們指定可用於鏈接兩個線條的線型。下面是全部枚舉類型的列表:
GDI+枚舉類型(55種)
枚舉類型 |
名稱 |
|
枚舉類型 |
名稱 |
BrushType |
刷類型 |
ImageType |
圖像類型 |
|
ColorAdjustType |
顏色調整類型 |
InterpolationMode |
插值類型 |
|
ColorChannelFlags |
顏色通道標誌 |
ItemDataPosition |
項數據位置 |
|
ColorMatrixFlags |
顏色矩陣標誌 |
LinearGradientMode |
線性梯度模式 |
|
CombineMode |
組合模式 |
LineCap |
線帽 |
|
CompositingMode |
合成模式 |
LineJoin |
線鏈接 |
|
CompositingQuality |
合成質量 |
MatrixOrder |
矩陣序(左右乘) |
|
CoordinateSpace |
座標空間 |
MetafileFrameUnit |
圖元文件幀單位 |
|
CurveAdjustments |
曲線調整 |
MetafileType |
圖元文件類型 |
|
CurveChannel |
曲線通道 |
ObjectType |
對象類型 |
|
DashCap |
虛線帽 |
PaletteFlags |
調色板標誌 |
|
DashStyle |
虛線風格 |
PaletteType |
調色板類型 |
|
DitherType |
抖動類型 |
PathPointType |
路徑點類型 |
|
DriverStringOptions |
驅動器串選項 |
PenAlignment |
筆對齊 |
|
EmfPlusRecordType |
EMF+等圖元文件記錄類型 |
PenType |
筆類型 |
|
EmfToWmfBitsFlags |
EMF轉WMF的標誌位 |
PixelOffsetMode |
像素偏移模式 |
|
EmfType |
EMF類型 |
RotateFlipType |
旋轉翻轉類型 |
|
EncoderParameterValueType |
編碼器參數值類型 |
SmoothingMode |
平滑模式 |
|
EncoderValue |
編碼器值 |
Status |
狀態 |
|
FillMode |
填充模式 |
StringAlignment |
串對齊 |
|
FlushIntention |
刷新意圖 |
StringDigitSubstitute |
串數字替換 |
|
FontStyle |
字體風格 |
StringFormatFlags |
串格式標誌 |
|
HatchStyle |
影線風格 |
StringTrimming |
串修整 |
|
HistogramFormat |
直方圖格式 |
TextRenderingHint |
文本渲染提示 |
|
HotkeyPrefix |
熱鍵前綴 |
Unit |
單位 |
|
ImageCodecFlags |
圖像編解碼標誌 |
WarpMode |
彎曲模式 |
|
ImageFlags |
圖像標誌 |
WrapMode |
覆蓋模式 |
|
ImageLockMode |
圖像加鎖模式 |
|
5)、結構
GDI+ API中還定義了19種結構,用於GDI+的各類函數調用中。下面是全部GDI+ API結構的列表:
GDI+ API中的結構(19種)
結構 |
名稱 |
BlurParams |
模糊參數 |
BrightnessContrastParams |
亮度對比度參數 |
ColorBalanceParams |
顏色平衡參數 |
ColorCurveParams |
顏色曲線參數 |
ColorLUTParams |
顏色查找表參數 |
ColorMap |
顏色映射 |
ColorMatrix |
顏色矩陣 |
ColorPalette |
顏色調色板 |
ENHMETAHEADER3 |
加強圖元文件頭 |
GdiplusAbort |
GDI+異常中斷 |
GdiplusStartupInput |
GDI+啓動輸入 |
GdiplusStartupOutput |
GDI+啓動輸出 |
HueSaturationLightnessParams |
色調飽和度亮度參數 |
LevelsParams |
級別參數 |
PWMFRect16 |
可定位WMF矩形(INT16整數值) |
RedEyeCorrectionParams |
紅眼校訂參數 |
SharpenParams |
銳化參數 |
TintParams |
濃淡參數 |
WmfPlaceableFileHeader |
可定位WMF文件頭 |
6)、GDI+平面API
GDI+暴露出(exposes)一個平面(flat)API,它包含大約600個函數,被實如今Gdiplus.dll中,聲明在Gdiplusflat.h內。這些函數被包裝到了前面討論過的GDI+ API的54個C++類的集合之中。不要直接調用這些函數,而推薦用調用類成員方法來替代。由於微軟產品支持服務(Microsoft Product Support Services),不會爲直接調用平面API的代碼提供支持。
做爲C++封裝的替代方案,微軟網絡框架(Microsoft .NET Framework),提供了GDI+的一個託管代碼封裝類集,包含大約60個類、50個枚舉和8個結構。它們屬於下列命名空間: // 在C#中使用之
System.Drawing
System.Drawing.Drawing2D
System.Drawing.Imaging
System.Drawing.Text
System.Drawing.Printing
DllExports |
System.Drawing[.dll] |
Gdiplus |
Gdiplus.h |
afxwin.h |
C++封裝 (MFC) |
C++封裝 |
託管代碼封裝 |
設備驅動程序 |
計算機硬件(顯示器、打印機等圖形設備) |
GDI API |
GDI+平面API |
C++ |
C#、VB、J# |
GDI+ API |
GDI+託管類接口 |
GDI類與結構 |
Gdi32.dll |
WinGDI.h |
Gdiplus.dll |
GdiplusFlat.h |
GDI+的封裝與使用
這兩種包裝(C++和託管代碼)都採用了面向對象方法,因此兩者在將參數傳遞給封裝的方法和將參數傳遞給平面API函數的方式上存在差異。
2、GDI+編程
本部分簡單介紹GDI+編程中的一些概念與技巧,具體的編程細節請參考《精通GDI+編程》、陳寶楷《GDI+編程》等書籍。
一、Point、浮點數點類PointF;Size、浮點數大小類SizeF;Rect、浮點數矩形類RectF等。
浮點數版的幾何對象和繪圖函數,是GDI+新增的功能,這些在各類工程技術領域都很是有用。由於通常的實際圖形設計,都是基於實數座標的。包括機械(機牀/汽車/輪船/飛機等)、建築(房屋/橋樑/道路/堤壩等)和圖形動畫設計(形狀/物體/人物/背景/軌跡等)等設計,都必須使用浮點參數和座標系。
二、Color:在GDI+中,色彩是經過Color(色彩)類來描述的。Color類的構造函數分別爲:
Color();
Color(BYTE a,BYTE r,BYTE g,BYTE b);
Color(ARGB argb);
Color(BYTE r,BYTE g,BYTE b);
參數:
a:色彩的透明度(0~255)
r、g、b:紅、綠、藍3種色彩份量值(0~255)
不一樣於GDI,GDI+在對色彩支持方面主要體如今對色彩的透明度支持。從本質上講,透明度是像素之間的一種合成運算。它的計算公式是:
輸出色彩=前景色*Alpha/255 + 背景色*(255-Alpha)/255
三、Graphics(圖形)
圖形類Graphics是GDI+的核心,它提供繪製圖形、圖像和文本的各類方法(操做/函數)(似GDI中的CDC類),還能夠存儲顯示設備和被畫項目的屬性(到圖元文件)。Graphics類及其成員函數都被定義在頭文件Gdiplusgraphics.h中。
Graphics類的構造函數有以下4種:
Graphics(Image* image); // 用於繪製圖像
Graphics(HDC hdc); // 用於在當前窗口中繪圖
Graphics(HDC hdc, HANDLE hdevice); // 用於在指定設備上繪製圖形
Graphics(HWND hwnd, BOOL icm = FALSE); // 用於在指定窗口中繪圖能夠進行顏色調整
其中,最經常使用的是第二種——在當前視圖窗口中繪圖的圖形類構造函數。
注意,該構造函數的輸入參數,是設備上下文的句柄,而不是CDC類對象的指針。通常能夠由CDC對象獲得(CDC類含有公用數據成員HDC m_hDC;)
6種繪製直線和折線的函數:(前三個爲整數版,後三個爲對應的浮點數版) // 畫折線DrawLines
Status DrawLine(const Pen* pen, INT x1, INT y1, INT x2, INT y2);
Status DrawLine(const Pen* pen, const Point& pt1, const Point& pt2);
Status DrawLines(const Pen* pen, const Point* points, INT count); // 畫折線
Status DrawLine(const Pen* pen, REAL x1, REAL y1, REAL x2, REAL y2);
Status DrawLine(const Pen* pen, const PointF& pt1, const PointF& pt2);
Status DrawLines(const Pen* pen, const PointF* points, INT count);
6種繪製矩形和矩形組的函數:(也是前三個爲整數版,後三個爲對應的浮點數版) // Rectangle = rect angle
Status DrawRectangle(const Pen* pen, const Rect& rect);
Status DrawRectangle(const Pen* pen, INT x, INT y, INT width, INT height);
Status DrawRectangles(const Pen* pen, const Rect* rects, INT count);
Status DrawRectangle(const Pen* pen, const RectF& rect);
Status DrawRectangle(const Pen* pen, REAL x, REAL y, REAL width, REAL height);
Status DrawRectangles(const Pen* pen, const RectF* rects, INT count);
繪製橢圓的函數,若是輸入參數所肯定的外接矩形的寬高相等,則畫圓。(也是前兩個爲整數版,後兩個爲對應的浮點數版)
Status DrawEllipse(const Pen* pen, const Rect& rect);
Status DrawEllipse(const Pen* pen, INT x, INT y, INT width, INT height)
Status DrawEllipse(const Pen* pen, const RectF& rect);
Status DrawEllipse(const Pen* pen, REAL x, REAL y, REAL width, REAL height);
繪製橢圓弧的函數,若是輸入參數所肯定的外接矩形的寬高相等,則畫圓弧。(也是前兩個爲整數版,後兩個爲對應的浮點數版)
Status DrawArc(const Pen* pen, INT x, INT y, INT width, INT height, REAL startAngle, REAL sweepAngle); // sweep 掠過
Status DrawArc(const Pen* pen, const Rect& rect, REAL startAngle, REAL sweepAngle);
Status DrawArc(const Pen* pen, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle);
Status DrawArc(const Pen* pen, const RectF& rect, REAL startAngle, REAL sweepAngle);
畫弧函數的輸入參數 // 注意:順時鐘方向
該函數的功能與GDI的Arc相同:
BOOL Arc( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 );
BOOL Arc( LPCRECT lpRect, POINT ptStart, POINT ptEnd );
可是也有區別,主要是,最後的參數再也不是弧的終角,而是弧段所對應的掃描角。這卻是與另外一個GDI畫圓弧函數相似(其中(x, y)爲圓心、nRadius爲半徑、fStartAngle爲起始角、fSweepAngle也爲弧段跨角):
BOOL AngleArc(int x, int y, int nRadius, float fStartAngle, float fSweepAngle);
固然,GDI+肯定矩形的後兩個參數也再也不是右下角座標,而改爲寬高了,這已是老問題了。
另外要注意的是,角度的單位是度(不是弧度,C++的三角函數採用的是弧度單位),並且都必須是實數。零度角爲x軸方向,順時針方向爲正(這與數學上反時針方向爲正恰好相反)。 // 如上圖所示。
繪製多邊形的函數,第一個爲整數版,後一個爲對應的浮點數版:
Status DrawPolygon(const Pen* pen, const Point* points, INT count);
Status DrawPolygon(const Pen* pen, const PointF* points, INT count);
其中,各參數的含義同畫折線函數DrawLines的,只是DrawPolygon函數會將點數組中的起點和終點鏈接起來,造成一個封閉的多邊形區域。
該函數的功能與GDI的Polygon相同:
BOOL Polygon( LPPOINT lpPoints, int nCount );
注意:GDI+中沒有提供,與GDI函數RoundRect(圓角矩形)和Chord(弓弦),具備相似功能的繪圖函數。能夠利用矩形+橢圓和弧+直線等函數本身來實現。
在GDI+中畫填充圖,不需像GDI那樣得先將刷子選入DC,而是與GDI+畫線狀圖的函數相似,將刷子做爲畫填充圖函數的第一個輸入參數。
l 畫填充矩形[組] FillRectangle[s]
Status FillRectangle(const Brush* brush, const Rect& rect);
Status FillRectangle(const Brush* brush, INT x, INT y, INT width, INT height);
Status FillRectangles(const Brush* brush, const Rect* rects, INT count);
Status FillRectangle(const Brush* brush, const RectF& rect);
Status FillRectangle(const Brush* brush, REAL x, REAL y, REAL width, REAL height);
Status FillRectangles(const Brush* brush, const RectF* rects, INT count);
用指定刷子Brush,填充rect的內部區域,無邊線,填充區域包括矩形的左邊界和上邊界,但不包括矩形的右邊界和下邊界。功能與GDI的FillRect相似:
void FillRect( LPCRECT lpRect, CBrush* pBrush );
可是,GDI中沒有同時填充一個矩形數組的函數。不過GDI卻有GDI+沒有的畫填充圓角矩形的函數FillSolidRect。
l 畫填充[橢]圓FillEllipse
Status FillEllipse(const Brush* brush, const Rect& rect);
Status FillEllipse(const Brush* brush, INT x, INT y, INT width, INT height);
Status FillEllipse(const Brush* brush, const RectF& rect);
Status FillEllipse(const Brush* brush, REAL x, REAL y, REAL width, REAL height);
GDI中沒有相似函數,但能夠用(採用當前刷填充的)Ellipse來代替。
l 畫餅圖DrawPie // pie餡餅 DrawPie與FillPie
Status DrawPie(const Pen* pen, const Rect& rect, REAL startAngle, REAL sweepAngle);
Status DrawPie(const Pen* pen, INT x, INT y, INT width, INT height, REAL startAngle, REAL sweepAngle);
Status DrawPie(const Pen* pen, const RectF& rect, REAL startAngle, REAL sweepAngle);
Status DrawPie(const Pen* pen, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle);
與GDI的下列函數相似,可是部分輸入參數的含義有所不一樣:
BOOL Pie( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 );
BOOL Pie( LPCRECT lpRect, POINT ptStart, POINT ptEnd );
l 畫填充多邊形FillPolygon
Status FillPolygon(const Brush* brush, const Point* points, INT count);
Status FillPolygon(const Brush* brush, const Point* points, INT count, FillMode fillMode);
Status FillPolygon(const Brush* brush, const PointF* points, INT count);
Status FillPolygon(const Brush* brush, const PointF* points, INT count, FillMode fillMode);
前面講的各類畫線狀圖或填充圖的GDI+函數,雖然在形式上與GDI的有所不一樣(函數名前加了Draw或Fill、將筆或刷做爲第一個輸入參數、部分位置輸入參數改爲了大小參數、並增長了浮點數版),可是在功能上倒是相同的。
如今要講的曲線繪製,則是GDI+新增長的內容。曲線在機械設計、工程建築和圖形動畫等領域,都有十分普遍應用。
經常使用的曲線有Bezier(貝塞爾)曲線和樣條(spline)曲線。貝塞爾曲線比較簡單,適合於畫控制點少的曲線。當控制點太多時,要不曲線的次數(比點數少1)過高,要不拼接比較困難,並且沒有局部性(即修改一點影響全局),性能不太好。而樣條曲線則能夠畫任意多個控制點的曲線,曲線的次數也能夠指定(通常爲二次或三次,如TrueType字體採用的是二次B樣條曲線),而且具備局部性。貝塞爾曲線特別是樣條曲線有不少變種。常見的貝塞爾曲線有普通貝塞爾曲線和有理貝塞爾曲線。經常使用的樣條曲線有:B樣條、β樣條、Hermite(厄密)樣條、基數樣條、Kochanek-Bartels樣條和Catmull-Rom樣條等。
GDI+中所實現的是普通貝塞爾曲線(不過控制點,位於控制多邊形的凸包以內)和基數樣條曲線(過控制點)。
l 基數樣條曲線(cardinal spline curve) // DrawCurve與DrawClosedCurve
Status DrawCurve(const Pen* pen, const Point* points, INT count); // tension = 0.5f
Status DrawCurve(const Pen* pen, const Point* points, INT count, REAL tension);
Status DrawCurve(const Pen* pen, const Point* points, INT count, INT offset, INT numberOfSegments, REAL tension = 0.5f); // 只畫部分點
Status DrawCurve(const Pen* pen, const PointF* points, INT count);
Status DrawCurve(const Pen* pen, const PointF* points, INT count, REAL tension);
Status DrawCurve(const Pen* pen, const PointF* points, INT count, INT offset, INT numberOfSegments, REAL tension = 0.5f);
Status DrawClosedCurve(const Pen* pen, const Point* points, INT count);
Status DrawClosedCurve(const Pen *pen, const Point* points, INT count, REAL tension);
Status DrawClosedCurve(const Pen* pen, const PointF* points, INT count);
Status DrawClosedCurve(const Pen *pen, const PointF* points, INT count, REAL tension);
其中:
參數tension(張力)指定曲線的彎曲程度,tension = 0.0(直線)~ 1.0(最彎曲)
無張力版的函數的 tension = 0.5(缺省值)
第3/6個DrawCurve,只畫從points[offset]開始的numberOfSegments個點組成的部分曲線段
DrawClosedCurve函數(鏈接首尾點)畫封閉的基數樣條曲線
l 貝塞爾曲線(Bezier curve) DrawBezier
Status DrawBezier(const Pen* pen, INT x1, INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4);
Status DrawBezier(const Pen* pen, const Point& pt1, const Point& pt2, const Point& pt3, const Point& pt4);
Status DrawBeziers(const Pen* pen, const Point* points, INT count);
Status DrawBezier(const Pen* pen, REAL x1, REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4);
Status DrawBezier(const Pen* pen, const PointF& pt1, const PointF& pt2, const PointF& pt3, const PointF& pt4);
Status DrawBeziers(const Pen* pen, const PointF* points, INT count);
l 填充封閉基數樣條曲線 FillClosedCurve
Status FillClosedCurve(const Brush* brush, const Point* points, INT count);
Status FillClosedCurve(const Brush* brush, const Point* points, INT count, FillMode fillMode, REAL tension = 0.5f);
Status FillClosedCurve(const Brush* brush, const PointF* points, INT count);
Status FillClosedCurve(const Brush* brush, const PointF* points, INT count, FillMode fillMode, REAL tension = 0.5f);
GDI中沒有用於清屏的專門函數,得本身用背景色畫窗口大小的填充矩形,或者調用窗口類的Invalidate和UpdateWindow函數。如今,GDI+有了清屏函數Clear:
Status Clear(const Color &color);
其中的輸入參數color,爲用戶指定的填充背景色。例如:
Graphics graph(GetDC()->m_hDC);
graph.Clear(Color::White); // 使用Graphics類之對象調用
四、 Pen
與GDI中的同樣,GDI+中的筆(pen鋼筆/畫筆)也是畫線狀圖的工具,可是功能更增強大。例如:透明筆、圖案筆、自定義虛線風格、線帽、筆的縮放和旋轉、筆的鏈接點屬性等。
GDI+中的筆對應於Pen類,被定義在GdiplusPen.h頭文件中。
筆的構造函數主要有兩個:
Pen(const Color &color, REAL width = 1.0); // 單色筆
Pen(const Brush *brush, REAL width = 1.0); // 紋理圖案筆
其中,最經常使用的是第一個,它構造一個顏色爲color,寬度爲width(缺省爲1)的單色筆。若是顏色的α值<255,則所建立的筆就是帶透明度的筆。
五、 Brush
與GDI中的同樣,GDI+中的刷(brush畫刷/畫筆)也是畫填充圖的工具,GDI+中也有與GDI相對應的實心刷(單色刷)、條紋刷(影線刷)和紋理刷(圖像刷)。不過,GDI+又新增長了功能強大的線性漸變刷和路徑漸變刷,並且還爲全部這些刷各自創建了對應的類,基類是Brush(功能少)。
下面是GDI+中各類刷類的層次結構圖:
GdiplusBase |
Brush |
SolidBrush |
HatchBrush |
TextureBrush
|
LinearGradientBrush |
PathGradientBrush |
GDI+刷類的層次結構
// (1) SolidBrush實心刷
// (2) HatchBrush 條紋刷
// (3) TextureBrush 紋理刷
// (4) LinearGradientBrush 線性漸變刷 // gradient傾斜的,梯度
// (5) PathGradientBrush 路徑漸變刷
全部刷類都被定義在頭文件GdiplusBrush.h中。
六、 文字
GDI+的文本排版和字體處理的功能比GDI的更增強大。特別是Windows XP提供了對LCD(液晶)顯示器的特殊優化功能,GDI+也提供了對應的ClearType(清晰活字)文字處理技術,以加強字體的清晰度。另外,GDI+還提供了構造專用字體集的功能,能夠包含私有的臨時字體(不需預先安裝到系統中)。
Windows中使用的字體,通常是TrueType(真實活字)字體(TTF = TrueType Font),它是1991年Apple 和Microsoft 聯合開發的一種字體技術,採用二次B樣條曲線來描述字符的輪廓。
在GDI+中,與文字相關的類有:字體族類FontFamily、字體類Font和字體集類FontCollection及其兩個派生類InstalledFontCollection(已安裝字體集)和PrivateFontCollection(專用字體集)。(在GDI中只有CFont一個字體類)
這些類的層次結構爲:
FontCollection |
InstalledFontCollection |
PrivateFontCollection |
Font |
FontFamily |
GdiplusBase |
在GDI中,咱們用CDC類的成員函數TextOut、DrawText和ExtTextOut等來輸出文本串。在GDI+中,咱們則是利用Graphics類的重載成員函數DrawString來繪製文本。
七、 路徑
路徑(path)是一系列相互鏈接的直線和曲線,由許多不一樣類型的點所構成,用於表示複雜的不規則圖形,也叫作圖形路徑(graphics path)。路徑能夠被畫輪廓和填充,也能夠用於建立區域和路徑漸變刷等。
在GDI中也有路徑(咱們沒有講),可是它只是做爲DC的一種狀態才能存在。獨立的路徑對象,則是GDI+的新特色。
八、 區域
區域(region)由若干幾何形狀所構成的一種封閉圖形,主要用於複雜圖形的繪製、圖形輸出的剪裁和鼠標擊中的測試。最簡單也是最經常使用的區域是矩形,其次是橢圓和多邊形以及它們的組合。這些也正是GDI所支持的區域類型。
GDI+中的區域是一種顯示錶面的範圍(an area of the display surface),能夠是任意形狀(的圖形的組合),邊界通常爲路徑。除了上面所講的矩形、橢圓、多邊形以外,其邊界還能夠含直線、折線、弧、貝塞爾曲線和樣條曲線等開圖形,其內容還能夠包含餅、閉曲線等閉圖形。
在GDI+中,區域所對應的類是Region,它是一個獨立的類(沒有基類,也沒有派生類)。可是它又若干相關的類,如各類圖形類和圖形路徑類等。
Region類有6個構造函數:
Region(VOID); // 建立一個空區域
Region(const Rect &rect); // 建立一個整數型矩形區域
Region(const RectF &rect); // 建立一個浮點數型矩形區域
Region(const GraphicsPath *path); // 由圖形路徑來建立區域
Region(const BYTE *regionData, INT size);// 由(另外一)區域的數據構造區域
Region(HRGN hRgn); // 由GDI的區域句柄構造區域
其中,建立矩形區域最簡單,由路徑建立區域最經常使用。
九、 變換
變換(transform)是GDI+新增長的強大功能,包括圖形對象的簡單變換和基於矩陣的座標變換、圖形變換、圖像變換、色彩變換、路徑變換和區域變換等。
GDI+的核心——圖形類Graphics,提供了3個成員函數,能夠對其所繪製的圖形進行平移(translate)、旋轉(rotate)和伸縮(scale比例尺/縮放)等基本的圖形變換:(與紋理刷類中的對應函數的原型是同樣的)
Status TranslateTransform(REAL dx, REAL dy, MatrixOrder order = MatrixOrderPrepend);
Status RotateTransform(REAL angle, MatrixOrder order = MatrixOrderPrepend);
Status ScaleTransform(REAL sx, REAL sy, MatrixOrder order = MatrixOrderPrepend);
其中的最後一個輸入參數爲矩陣相乘的順序,取值爲矩陣順序枚舉類型MatrixOrder中的符號常量,缺省值都爲MatrixOrderAppend(左乘):
typedef enum {
MatrixOrderPrepend = 0, // 矩陣左乘(預先序,前置)
MatrixOrderAppend = 1 // 矩陣右乘(追加序,後綴)
} MatrixOrder;
由於這些變換均可以用矩陣表示,並且與圖形對象已經設置的現有變換矩陣要進行合成(至關於兩個變換矩陣進行乘法運算)。
在圖形對象的這三種基本變換中,最經常使用的是第一種——平移變換。咱們在前面曾屢次使用,避免了重複定義(有座標平移的)繪圖區域的麻煩。
十、 圖像
GDI+支持以下9種用於Windows的常見圖像格式:
l BMP——BitMaP(位圖),擴展名爲.BMP,由Microsoft與IBM於1980年代中期爲Windows和PS/2制訂的圖像格式,通常不壓縮。支持黑白、僞彩圖、灰度圖和真彩圖,每像素位數可爲一、四、八、1六、2四、3二、64等,經常使用的是24位位圖。
l GIF——Graphics Interchange Format(可交換圖形格式),擴展名爲.GIF,由CompuServe公司1987年制定,採用無損的變長LZW壓縮算法。只支持僞彩圖(最多256索引色),寬高用雙字節無符號整數表示(最多64K*64K像素)。可存儲多幅圖片,經常使用於簡單的網絡動畫。壓縮比較高,使用普遍。
l JPEG——Joint Photographic Experts Group(聯合圖象專家組),擴展名爲.JPG,是國際標準化組織ISO和IEC於1991年聯合制定的靜態圖像壓縮標準,採用以DCT爲主的有損壓縮方法。支持灰度圖和真彩圖,可是不支持僞彩圖。壓縮比高,使用普遍。
l Exif——EXchangeable Image File Format(可交換圖像文件格式),擴展名爲.Exit?,由JEIDA(Japan Electronic Industry Development Association日本電子工業發展協會/日本電子情報技術產業協會)於1996年10月制定。用於數碼相機,內含JPEG圖像,另包含拍攝日期、快門速度、曝光時間、照相機制造廠商和型號等相關信息。
l PNG——Portable Network Graphic Format(可移植網絡圖形格式,讀成「ping」),擴展名爲.png,由W3C(World Wide Web Consortium萬維網協會)於1996年10月推出的一種標準圖像格式,2003年成爲ISO國際標準。PNG採用與GIF同樣的無損壓縮方法,可是除了僞彩圖外,PNG還支持多達16位深度的灰度圖像和48位深度的彩色圖像,而且還可支持多達16位的α通道數據。
l TIFF——Tag Image File Format(標籤圖像文件格式),擴展名爲.tif,由Aldus於1986年秋聯合多家掃描儀制造商和軟件公司共同開發,支持黑白、索引色、灰度、真彩圖,可校訂顏色和調色溫,支持多種壓縮編碼(如Huffman、LZW、RLE),可存儲多幅圖片。經常使用於對質量要求高的專業圖像的存儲。
l ICON——icon(圖標),擴展名爲.ico,由Microsoft與IBM於1980年代中期爲Windows和PS/2制訂的圖標圖像格式。圖像大小爲16*1六、32*32或54*64。
l WMF——Windows MetaFile(視窗元文件),擴展名爲.WMF,由Microsoft與IBM於1980年代中期爲Windows和PS/2制訂的圖形文件格式,用於保存GDI的繪圖指令記錄。
l EMF——Enhanced Windows MetaFile(加強型視窗元文件),擴展名爲.EMF,是微軟公司於1993年隨32位操做系統Windws NT推出的一種改進的WMF格式,用於Win32。GDI+使用的是擴展EMF格式——EMF+。
GDI+的圖像及其處理的功能十分強大,能夠用不一樣的格式加載、保存和操做圖像。但因爲篇幅所限,本小節只介紹最基本的內容。
GDI+中有三個圖像類,其中的Image(圖像)爲基類,其餘兩個爲它的派生類——Bitmap(位圖)和Metafile([圖]元文件/矢量圖形)。它們的類層次結構以下圖所示:
GdiplusBase |
Image |
Bitmap |
Metafile |
圖像類的層次結構
除此以外,還有大量與圖像處理有關的GDI+類,如Effect類及其11個派生類以及與圖像數據和信息有關的7個獨立類。因爲時間關係,咱們只准備介紹上面這三個主要的圖像類及其基本操做。
十一、 圖元文件
從一開始GDI就支持(圖)元文件(metafile),早期(1985年)的版本爲WMF(Windows MetaFile視窗元文件),主要針對Win16(Win3.x),後來(1990年)也支持Win32(Win95/ 98/Me)。之後(1993年)隨Windows NT推出了改進的元文件版本——EMF(Enhanced Windows MetaFile加強型視窗元文件),只支持Win32(Win95/98/Me/NT/2000/XP)。如今(2001年)又隨GDI+推出了增強型EMF——EMF+,能夠同時支持GDI和GDI+。
元文件所支持的GDI類型
元文件類型 |
Win16 GDI |
Win32 GDI |
Win32/64 GDI+ |
WMF |
√ |
√ |
× |
EMF |
× |
√ |
× |
EMF+ |
× |
√ |
√ |
雖然在GDI+中,將圖元文件所對應的類Metafile做爲Image的派生類,但這只是爲了圖元文件能夠同時處理圖形和圖像。其實圖元文件中所包含的就是一系列繪圖(包括繪製圖像)指令及參數,屬於矢量圖形文件。它所佔空間小、能夠任意縮放(不會產生馬賽克效應),可是繪製圖形須要必定的時間。
在GDI+中,圖元文件對應的類爲Metafile,它是Image類的派生類。GDI+的Metafile類支持三種類型的圖元文件:僅EMF類型、僅EMF+類型、EMF及EMF+雙重類型(缺省值)。它們對應於枚舉類型:
typedef enum {
EmfTypeEmfOnly = MetafileTypeEmf, // 僅EMF類型
EmfTypeEmfPlusOnly = MetafileTypeEmfPlusOnly, // 僅EMF+類型
EmfTypeEmfPlusDual = MetafileTypeEmfPlusDual // EMF及EMF+雙重類型
} EmfType; // enhance meta file
Metafile類有13個構造函數:
// 文件型
Metafile(const WCHAR *filename);
Metafile(const WCHAR *fileName, HDC referenceHdc, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);
Metafile(const WCHAR *fileName, HDC referenceHdc, const Rect &frameRect, MetaFileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);
Metafile(const WCHAR *fileName, HDC referenceHdc, const RectF &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);
// 流型
Metafile(IStream *stream);
Metafile(IStream *stream, HDC referenceHdc, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);
Metafile(IStream *stream, HDC referenceHdc, const Rect &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);
Metafile(IStream *stream, HDC referenceHdc, const RectF &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);
// DC句柄型
Metafile(HDC referenceHdc, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);
Metafile(HDC referenceHdc, const Rect &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);
Metafile(HDC referenceHdc, const RectF &frameRect, MetaFileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);
// WMF/EMF句柄型
Metafile(HENHMETAFILE hEmf, BOOL deleteEmf = FALSE);
Metafile(HMETAFILE hWmf, const WmfPlaceableFileHeader *wmfPlaceableFileHeader, BOOL deleteWmf = FALSE);
其中用到的枚舉類型有:
typedef enum {
MetafileFrameUnitPixel = UnitPixel, // 象素
MetafileFrameUnitPoint = UnitPoint, // 點
MetafileFrameUnitInch = UnitInch, // 英寸
MetafileFrameUnitDocument = UnitDocument, // 文擋
MetafileFrameUnitMillimeter = UnitDocument + 1, // 毫米
MetafileFrameUnitGdi = UnitDocument + 2 // GDI+單位數目
} MetafileFrameUnit;
typedef struct {
UINT32 Key; // 鍵
INT16 Hmf; //
PWMFRect16 BoundingBox; // 邊界盒
INT16 Inch; // 英寸
UINT32 Reserved; // 保留
INT16 Checksum; // 檢測和
} WmfPlaceableFileHeader;
其中,最簡單經常使用的構造函數是:// 不帶DC參數,只能用於打開已經存在的元文件
Metafile(const WCHAR *filename);
它由文件名來構造元文件對象。例如:
Metafile mf(L"yyy.emf");
經常使用且完整的構造函數是:// 帶DC參數,只用於建立新圖元文件
Metafile(const WCHAR *fileName, HDC referenceHdc, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);
它能夠指定元文件類型,並加上描述串。例如:
Metafile mf(L"yyy.emf", GetDC()->m_hDC, MetafileTypeEmf, L"陰陽魚");
另外一個較爲經常使用的構造函數是:
Metafile(HDC referenceHdc, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);
它用於構造內存元文件。這些內存元文件構造函數還有對應的流構造函數版本。
Metafile類的其餘成員函數有:
// 顯示元文件記錄,須要與Graphics類的EnumerateMetafile
Status PlayRecord(EmfPlusRecordType recordType, UINT flags, UINT dataSize, const BYTE *data);
// 函數及用戶自定義的回調函數配套使用(似GDI的)
static UINT EmfToWmfBits(HENHMETAFILE hemf, UINT cbData16, LPBYTE pData16, INT iMapMode, EmfToWmfBitsFlags eFlags); // 用於EMF到WMF的轉換
HENHMETAFILE GetHENHMETAFILE(VOID); // 可用於EMF的SDK函數
// 獲取和設置底層光柵限制,用於減小刷空間大小 // 陳寶楷???
UINT GetDownLevelRasterizationLimit(VOID);
Status SetDownLevelRasterizationLimit(UINT metafileRasterizationLimitDpi);
// 獲取元文件頭
Status GetMetafileHeader(MetafileHeader *header) const;
static Status GetMetafileHeader(const WCHAR *filename, MetafileHeader *header);
static Status GetMetafileHeader(IStream *stream, MetafileHeader *header);
static Status GetMetafileHeader(HENHMETAFILE *hEmf, MetafileHeader *header);
static Status GetMetafileHeader(HMETAFILE hWmf, const WmfPlaceableFileHeader *wmfPlaceableFileHeader, MetafileHeader *header);
爲了將繪圖記錄保存到圖元文件中,須要先建立元文件對象,而後用該圖元文件對象再來建立圖形對象,最後調用圖形類的各類繪圖函數來向圖元文件中添加繪圖記錄。
具體方法以下:
能夠先使用Metafile類的用於建立新圖元文件的構造函數(帶DC參數的),如
Metafile(const WCHAR *fileName, HDC referenceHdc, EmfType type =
EmfTypeEmfPlusDual, const WCHAR *description = NULL);
來建立元文件對象。
而後使用Graphics類的構造函數(注意,Metafile是Image的派生類)
Graphics(Image* image);
來建立圖形對象。
最後調用各類圖形類的圖形設置、操做和繪製函數成員函數來向圖元文件添加繪圖記錄。
例如:
Metafile *myMetafile = new Metafile(L"MyDiskFile.emf", GetDC()->m_hDC);
Graphics *myGraphics = new Graphics(myMetafile);
// SmoothingMode::tiAlias不能在VC中使用,可在C#中使用。
myGraphics->SetSmoothingMode(SmoothingModeAntiAlias);
myGraphics->RotateTransform(30);
// Create an elliptical clipping region.
GraphicsPath myPath;
myPath.AddEllipse(0, 0, 200, 100);
Region myRegion(&myPath);
myGraphics->SetClip(&myRegion);
Pen myPen(Color(255, 0, 0, 255));
myGraphics->DrawPath(&myPen, &myPath);
for(int j=0; j<=300; j+=10) myGraphics->DrawLine(&myPen,0,0,300-j,j);
delete myGraphics;
delete myMetafile;
能夠先使用Metafile類的用於打開已有圖元文件的構造函數(不帶DC參數的),如
Metafile(const WCHAR *filename);
來建立元文件對象。
而後再調用Graphics類的各類DrawImage成員函數,如:
Status DrawImage(Image *image, INT x, INT y);
來重畫圖元文件中的全部繪圖記錄。
另外,爲了獲取當前圖元文件的邊界矩形,能夠先調用Metafile類的成員函數:
Status GetMetafileHeader(MetafileHeader *header) const;
來獲取MetafileHeader對象,而後再用MetafileHeader類的成員函數:
void GetBounds(Rect *rect);
獲得邊界矩形。可用於Graphics類的DrawImage成員函數:
DrawImage(Image *image, const Rect &rect);
注意,若是用帶DC參數的構造函數來建立Metafile對象,則會清空原圖元文件(以便從新開始添加記錄),不能用於圖元文件的播放。
能夠利用Metafile類的成員函數
Status PlayRecord(EmfPlusRecordType recordType, UINT flags,
UINT dataSize, const BYTE *data);
來重畫圖元文件中指定記錄。與EMF中討論的相似,該函數須要與Graphics類的枚舉元文件成員函數(共有12個同名的重載函數),如:
Status EnumerateMetafile(const Metafile *metafile, const PointF &destPoint, EnumerateMetafileProccallback, VOID *callbackData = NULL, ImageAttributes *imageAttributes = NULL);
配套使用,該函數遍歷圖元文件的每一個記錄,並調用用戶自定義的回調函數(該函數能夠本身命名)
BOOL CALLBACK metaCallback(EmfPlusRecordType recordType, unsigned int flags, unsigned int dataSize, const unsigned char* pStr, void* callbackData);
對記錄進行各類處理,包括使用元文件的成員函數PlayRecord來繪製(播放)記錄。
在GDI+中,想實現交互繪圖時的窗口動態重畫,很是困難。
雖然Metafile類有一個成員函數
HENHMETAFILE GetHENHMETAFILE(VOID);
能夠用於獲取圖元文件的句柄,但通過個人實驗發現,它只對使用不帶DC輸入參數的構造函數所建立的不能用於添加繪圖記錄的Metafile對象有效。
另外,雖然說能夠建立內存Metafile對象,可是GDI+卻沒有提供任何辦法(沒有複製、保存、克隆等函數,父類Image的對應函數對寫入型Metafile對象都是無效的),可將其保存到圖元文件中。由於沒法得到用於添加記錄的圖元文件的句柄,因此各類SDK函數也派不上用場。
由於除了幫助文檔,幾乎無資料可看,惟一的途徑就是編碼作試驗。下面是我通過很長時間,好不容易纔摸索出來的,一種可行的解決辦法(可是很臭。大家能夠尋找其餘辦法,若是有了更好的方法,請與你們共享):
(說明:爲了防止重畫圖元文件時,圖形的位置有偏移或其大小發生變化,能夠採用以下的構造函數:
Metafile(const WCHAR *fileName, HDC referenceHdc, const Rect &frameRect, MetaFileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, const WCHAR *description = NULL);
來建立Metafile對象。其中的邊框矩形,能夠設置爲屏幕大小,並使用像素單位。該邊框同時還用於進行圖元文件重畫的Graphics類的DrawImage函數。)
在視圖類中定義以下幾個類變量:Metafile對象及其對應的Graphics對象的指針、邊框矩形、兩個圖元文件名的寬字符串數組(以便繞開GDI+的文件鎖定功能)、以及在這兩個文件名中切換的整數。如:
Metafile *mf;
Graphics *mfGraph;
Rect rect0;
wchar_t *fns[2]; // wchar_t
int fni;
在視圖類的構造函數中,初始化部分類變量:
mf = NULL;
mfGraph = NULL;
fns[0] = L"draw.emf";
fns[1] = L"draw0.emf";
fni = 0;
在視圖類的初始化函數OnInitialUpdate中,計算邊框矩形、建立Metafile對象:
HDC hdcRef = GetDC()->m_hDC;
rect0.X = 0;
rect0.Y = 0;
rect0.Width = GetDeviceCaps(hdcRef, HORZRES);
rect0.Height = GetDeviceCaps(hdcRef, VERTRES);
mf = new Metafile(fns[fni], hdcRef, rect0, MetafileFrameUnitPixel);
mfGraph = new Graphics(mf);
在視圖類的OnLButtonUp等函數中,利用圖元文件所對應的圖形對象,向圖元文件添加各類繪圖記錄。如:
mfGraph->DrawLine(&Pen(Color::Green), p0.x, p0.y, point.x , point.y);
……
在視圖類的OnDraw函數中,刪除當前元文件對象(系統纔會將元文件的內容寫入磁盤)和對應的圖形對象,打開該磁盤元文件並播放。而後,切換文件名,建立新的元文件對象和對應的圖形對象,並將老元文件中現有的記錄,經過新元文件所對應的圖形對象的圖像繪製,加入到新元文件中,最後刪除老元文件的句柄。如:
delete mfGraph;
delete mf;
Metafile *mf0 = new Metafile(fns[fni]);
Graphics graph(pDC->m_hDC);
graph.DrawImage(mf0, rect0);
fni = !fni; // 至關於if(fni) fni = 0; else fni = 1;
mf = new Metafile(fns[fni], pDC->m_hDC, rect0, MetafileFrameUnitPixel);
mfGraph = new Graphics(mf);
mfGraph->DrawImage(mf0, rect0);
delete mf0;
最後,在視圖類的析構函數中,刪除當前元文件對象(系統會將元文件的內容寫入磁盤)和對應的圖形對象。例如:
delete mfGraph;
delete mf;
3、GDI+使用過程當中出現的問題:
1)、在VC調用過程當中,重繪問題。
GDI+程序每每在窗口被建立時,不能自動重畫(沒有自動調用OnDraw函數)。解決辦法是,在建立圖形對象後,本身調用視圖類(基類CWnd)的成員函數RedrawWindow:
BOOL RedrawWindow(LPCRECT lpRectUpdate = NULL, CRgn* prgnUpdate = NULL,
UINT flags = RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
其中,lpRectUpdate爲窗口客戶區中須要重畫的矩形(NULL表示整個客戶區矩形重畫)、prgnUpdate表示須要重畫的區域(NULL表示整個客戶區矩形區域重畫)、flags爲特徵標誌(RDW_INVALIDATE指定範圍無效、RDW_UPDATENOW當即更新、RDW_ERASE擦除背景)。
例如:
Graphics graph(pDC->m_hDC);
RedrawWindow(); // 通常輸入參數取缺省值便可
// 至關於Invalidate(); UpdateWindow();的綜合效果 // this->Invalidate();
注意:不能在OnDraw和OnPaint函數中調用RedrawWindow,那樣會形成反覆調用,產生死循環。
其實,只要GDI+的兩個初始化語句放置的位置對(必須放在CWinApp:: InitInstance ();語句以前),就不會出現該問題。
2)、new 問題。
不能使用new來動態建立GDI+對象。解決辦法是:打開(缺省)位於C:/Program Files/Microsoft Visual Studio 8/VC/PlatformSDK/Include目錄中的GdiplusBase.h頭文件,並註釋掉裏面GdiplusBase類的內容(該類其實只含new、new[]、delete和delete[]這四個運算符的重載),使其成爲一個空類(不要刪除整個類)。
對實驗室中的寫保護機器,不能修改安裝目錄中的GdiplusBase.h頭文件,解決辦法是:
l 將該頭文件複製到你的項目目錄中;
l 註釋掉該頭文件裏面GdiplusBase類的內容(保留類定義);
l 在你項目中全部的#include <gdiplus.h>語句以前,包含"GdiplusBase.h"頭文件,形如:
#include "gdiplusBase.h"
#include <gdiplus.h>
l 則編譯系統會優先包含項目目錄中的gdiplusBase.h頭文件,從而屏蔽掉原來位於平臺SDK的Include目錄中的同名頭文件。 // 技巧
你也能夠在有些使用new的地方改用&,例如你能夠將代碼
Pen *pPen = new Pen(Color::Red); // 在C#可運行
改成
Pen *pPen = &Pen(Color::Red);
又例如,你也能夠將代碼:
graphics.DrawPolygon(new Pen(Color::Green), points, n);
改成
Pen pen(Color::Green);
graphics.DrawPolygon(&pen, points, n);
或直接改成
graphics.DrawPolygon(&Pen(Color::Green), points, n);
3)、調試問題
如今版本的VC05存在許多Bug,特別是GDI+程序在調試時的問題就更多。解決辦法是:
l 在編譯運行時,不使用Debug配置,而改用Release配置;
l 運行時不使用調試運行(F5),而改用不調試直接運行(Ctrl +F5);
l 最好是用靜態連接的MFC庫,而不用DLL動態庫。
經常使用的調試方法有:
l 使用MessageBox信息框:
n 在視圖類中的經常使用格式爲
MessageBox(L"提示信息");
n 在應用程序類和文檔類中的經常使用格式爲
MessageBox(NULL, L"提示信息", L"標題", MB_OK); // Win32 API
l 設置斷點,而後逐步運行(F10)或F11。
l 運行當前位置,而後逐步運行(F10)
l 利用調試界面中的「局部變量」和「監視1」等窗口,來查看變量當前的值
4)、用MFC開發GDI+程序
建立一個名爲GDIPlusDemo的MFC單文檔應用程序項目。
首先要進行GDI+系統的初始化,這須要在應用程序類CGDIPlusDemoApp中聲明一個成員變量:
ULONG_PTR m_gdiplusToken; // ULONG PTR 爲int64 類型
並在該類的初始化函數CGDIPlusDemoApp::InitInstance() 中加入如下代碼來對GDI+進行初始化:
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
注意:這兩個語句必須加在應用程序類的InitInstance函數中的
CWinApp:: InitInstance ();
語句以前,否則之後會形成視圖窗口不能自動重畫、程序中不能使用字體等等一系列問題。
還要在CGDIPlusDemoApp::ExitInstance() 函數中加入如下代碼來關閉GDI +:
GdiplusShutdown(m_gdiplusToken);
上面的InitInstance和ExitInstance都是應用程序類的重寫型成員函數。並且,缺省時無ExitInstance,須要本身利用屬性窗口來添加(不要手工添加)。
接下來就能夠利用GDI+進行繪圖了。
在OnDraw函數中畫圖:
CGDIPlusDemoView::OnDraw (CDC* pDC) {
Graphics graph(pDC->m_hDC); // 建立圖形對象
Pen bluePen(Color(255, 0, 0, 255)); // 建立藍色筆
Pen redPen(Color(255, 255, 0, 0)); // 建立紅色筆
int y = 255; // y的初值
for (int x = 0; x < 256; x += 5) { // 繪製紅藍網線
graph.DrawLine(&bluePen, 0, y, x, 0);
graph.DrawLine(&redPen, 255, x, y, 255);
y -= 5;
}
for (y = 0; y < 256; y++) { // 畫綠色透明度水平漸變的線(填滿正方形)
Pen pen(Color(y, 0, 255, 0)); // A green pen with shifting alpha
graph.DrawLine(&pen, 0, y, 255, y);
}
for (int x = 0; x < 256; x++) { // 畫品紅色透明度垂直漸變的線(填滿扁矩形)
Pen pen(Color (x, 255, 0, 255)); // A magenta pen with shifting alpha
graph.DrawLine(&pen, x, 100, x, 200);
}
}
運行的結果如圖所示。(其中,第一個圖爲第一個循環所繪製的結果、第二個圖爲前兩個循環所繪製的結果、第三個圖爲所有循環所繪製的結果)
圖 透明度的連續變化
5)GDI+幫助文檔
GDI+的英文幫助內容,位於VS2008的「目錄/Win32和COM開發/Graphics and Multimedia/ GDI+」,主要的參考資料位於其子目錄「GDI+ Reference」中。
GDI+的中文幫助內容位於VS2008的「目錄/開發工具和語言/Visual Studio文檔/基於Windows的應用程序、組件和服務/建立基於 Windows 的應用程序/Windows窗體(Windows Forms)/加強Windows窗體應用程序/Windows窗體中的圖形和繪製」,其中包括「圖形概述(Windows 窗體)」、「關於 GDI+ 託管代碼」和「使用託管圖形類」三個子目錄。
6)Graphics與GraphicsPath中心點
Graphics中心點是左上點,而GraphicsPath中心點是真正的中心點。如圖:
GraphicsPath類的GetBounds等函數獲得的點,是轉換到Graphics平面上的點,對GraphicsPath進行矩陣操做時,須要就將點座標平移到GraphicsPath中心。
例如:(大小漸變文字)
GraphicsPath path; // 定義路徑對象
path.AddString(L"大小漸變文字測試", -1, // 將文本串加入路徑
&FontFamily(L"隸書"), FontStyleRegular, 100, Point(0, 0), NULL);
RectF boundRect;
path.GetBounds(&boundRect); // 獲取路徑的界限矩形
Matrix M; // 定義矩陣對象(單位陣)
M.Translate(-(boundRect.X + boundRect.Width / 2),
-(boundRect.Y + boundRect.Height / 2)); // 平移原點到文本路徑的中心
path.Transform(&M); // 更改路徑的中心點
INT n = path.GetPointCount(); // 獲取路徑中的點數
PointF *points = new PointF[n]; // 動態建立點數組
path.GetPathPoints(points, n); // 獲取路徑的點數組
BYTE *types = new BYTE[n]; // 動態建立類型數組
path.GetPathTypes(types, n); // 獲取路徑類型數組(用於路徑重構)
for (int i= 0; i < n; i++) // 根據路徑點到中心的距離,按比例修改點的y值
points[i].Y *= 2*(boundRect.Width-abs(points[i].X))/boundRect.Width;
GraphicsPath newPath(points, types, n); // 用新的路徑點構造新路徑
CRect crect;
GetClientRect(&crect); // CRect
Graphics graph(pDC->m_hDC); // 將座標原點移到窗口中心:
graph.TranslateTransform(REAL(crect.Width()/2),REAL(crect.Height()/2));
graph.FillPath(&SolidBrush(Color::Green), &newPath);//填充路徑(繪製文本串)
delete points;
delete types;
7)在保存圖象的時候會發生這樣的錯誤:「GDI+發生通常性錯誤」,通常解決方法參考下面。
「GDI+發生通常性錯誤」,這樣的錯誤通常能夠這樣重現:
Image image = new Bitmap(openFileDialog1 .FileName );
image.Save(openFileDialog1 .FileName ,System .Drawing .Imaging .ImageFormat .Jpeg );
發生這個錯誤的緣由是:
從一個文件構造的Bitmap 對象或一個 Image 對象, 在該對象的生存期內該文件處於鎖定狀態。 所以, 在沒有釋放這個Image或Bitmap對象前,沒法更改圖像並將其保存回原文件。
解決方法:
構造一個新的Image對象,而後把原來的Image對象中的圖象經過Graphics的DrawImage()方法,拷貝到新Image對象中,最後經過Dispose()方法釋放原來的Image對象:
Image image = new Bitmap ( openFileDialog1 . FileName );
//新建第二個Image類型的變量newImage,這裏是根據程序須要設置本身設置。
Image newImage = new Bitmap ( 800 , 600 );
//將第一個bmp拷貝到bmp2中
Graphics draw = Graphics . FromImage ( newImage);
draw . DrawImage ( image , 0 , 0 );
//釋放第一個Image對象
image.Dispose();
//保存圖象
newImage.Save(openFileDialog1.FileName);
8) GDI+ 縮放圖片的方法
方法一 : 最簡單的 , 使用 GetThumbnailImage, 這個方法的侷限性對支持內嵌縮略圖的圖片文件無效 . 由於 MSDN 中提到 : 若是圖片文件有內嵌的縮略圖 , 那麼就提取這個縮略圖返回 , 不然就縮放原圖片 , 不過我想對位圖仍是安全的 :
Bitmap * image = new Bitmap(L"MagicLinux.bmp");
Image* pScaledImage = NULL;
UINT nWidth = image->GetWidth()/2;
UINT nHeight= image->GetHeight()/2;
pScaledImage = image->GetThumbnailImage(nWidth, nHeight, NULL, NULL);
delete pScaledImage;
delete image;
方法二 :使用 Graphics::DrawImage, 這樣還能夠控制 InterpolationMode( 插值模式 , 在縮放和旋轉時候使用 ), 便可以控制縮放質量高低 . 也能夠經過 Graphics 對象作其餘的控制 .
Bitmap * ScaleBitmap(Bitmap * pBitmap,UINT nWidth,UINT nHeight)
{
Bitmap * pTemp = new Bitmap(nWidth,nHeight,pBitmap->GetPixelFormat());
if( pTemp )
{
Graphics * g = Graphics::FromImage(pTemp);
if( g )
{
// use the best interpolation mode
g->SetInterpolationMode(InterpolationModeHighQualityBicubic);
g->DrawImage(pBitmap,0,0,nWidth,nHeight);
delete g;
}
}
return pTemp;
}
pScaledImage = ScaleBitmap(image,nWidth,nHeight);
保存圖片的代碼 :
bool SaveAsImageFile(Image * pImage,LPCWSTR lpszFileName,LPCWSTR lpszImageType)
{
UINT num = 0;
// number of image encoders
UINT size = 0;
// size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
if(GetImageEncodersSize(&num, &size)!= Ok || size == 0 )
return false; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return false; // Failure
if(GetImageEncoders(num, size, pImageCodecInfo)!= Ok )
return false;
bool bOk = false;
for(UINT j = 0; j < num; ++j)
{
if( wcscmp(pImageCodecInfo[j].MimeType, lpszImageType) == 0 )
{
pImage->Save(lpszFileName,&(pImageCodecInfo[j].Clsid));
bOk = true;
break;
}
}
free(pImageCodecInfo);
return bOk;
}
調用象這樣就能夠保存成各類圖片 ( 固然要求有對應的 encoder 才行 ):
SaveAsImageFile (pScaledImage,L"1.bmp",L"image/bmp");
SaveAsImageFile (pScaledImage,L"1.jpg",L"image/jpeg");
SaveAsImageFile (pScaledImage,L"1.png",L"image/png");
9)GetPixel/SetPixel速度慢問題
GDI+的Bitmap類提供了兩個罪惡的函數GetPixel, SetPixel,用來獲取某個像素點的顏色值。這個2個函數若是隻調用一次兩次也就罷了,若是調用屢次速度就很慢了,可使用LockBits方法,就是把圖像的內存區域根據格式鎖定,拿到那塊內存的首地址,經過直接操做內存。好比將圖象灰度化:
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if(size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for(UINT j = 0; j < num; ++j)
{
if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
//圖象灰度處理
void CGDIPlusTestView::OnImageGray()
{ Graphics g(this->m_hWnd);
Bitmap imLoad(_T("c://0250001207A.bmp"));
long nImageWidth = imLoad.GetWidth();
long nImageHeight = imLoad.GetHeight();
Rect rectDest(0,0,400,400);
g.DrawImage(&imLoad,rectDest,0,0,nImageWidth,nImageHeight,UnitPixel,NULL,NULL,NULL);
Bitmap *pGrayImg = new Bitmap(nImageWidth, nImageHeight,PixelFormat8bppIndexed);
//生成調色版
ColorPalette* pal = (ColorPalette*)malloc(sizeof(ColorPalette) + 256*sizeof(ARGB));
pal->Count = 256;
pal->Flags = 0;
for(int m=0;m<256;m++)
{
pal->Entries[m] = Color::MakeARGB(255,m,m,m);
}
pGrayImg->SetPalette(pal);
BitmapData bmGrayData;
pGrayImg->LockBits(newRect(0,0,nImageWidth,nImageHeight),ImageLockModeWrite,PixelFormat8bppIndexed,&bmGrayData);
BitmapData bmData;
Status iSucess = imLoad.LockBits(newRect(0,0,nImageWidth,nImageHeight),ImageLockModeRead,PixelFormat24bppRGB,&bmData);
BYTE * p = (BYTE*)bmData.Scan0;
BYTE * q = (BYTE*)bmGrayData.Scan0;
BYTE * pt = p, *qt = q;
int i,j;
BYTE val;
for (j = 0; j < nImageHeight; j++)
{
// Stride是指圖像每一行須要佔用的字節數。根據BMP格式的標準,Stride必定要是4的倍數。
pt = p + j * bmData.Stride;
qt = q + j * bmGrayData.Stride;
for (i = 0; i < nImageWidth; i++)
{
val = (*pt)*0.114 + (*(pt+1))*0.587 + (*(pt+2))*0.299;
if(val>255)
{
val = 255;
}
if(val<0)
{
val = 0;
}
*qt = val;
pt += 3;
qt += 1;
}
}
imLoad.UnlockBits(&bmData);
pGrayImg->UnlockBits(&bmGrayData);
CLSID cidImage;
GetEncoderClsid(L"image/tiff", &cidImage);
//保存圖像
pGrayImg->Save(L"C://Gray.tif",&cidImage,NULL);
if(pal!=NULL)
{
free(pal);
pal = NULL;
}
//顯示圖像
rectDest.X = 401;
rectDest.Y = 0;
g.DrawImage(pGrayImg,rectDest,0,0,nImageWidth,nImageHeight,UnitPixel,NULL,NULL,NULL);
if(pGrayImg!=NULL)
{
delete []pGrayImg;
pGrayImg = NULL;
}
}
10)透明,半透明和不透明
在WinForm/WPF裏面咱們常常會看到一些關於透明的屬性,好比Backcolor裏面能夠選擇Transparant, Form裏面有一個叫Opacity的屬性。都是和透明以及透明度相關的。在實際上是在GDI+應用層上的一些東西,在這裏我就不講了。主要從更基本的地方講起,其中還包括兩塊徹底不一樣的內容。在LockBits的時候把PixelFormat設定成爲Format24bppRgb。可是若是你仔細研究,會發現其實裏面有各類各樣的圖片格式,其中有一種叫作Format32bppArgb。這個意思是說除了RGB,在圖像中還存在一個通道,叫作A。這個A就是用來描述當前像素是透明,半透明,仍是全透明的份量。這個通道是2個叫Catmull和Smith在上世紀70年代初發明的。經過這個份量,咱們能夠進行alpha混合的一些計算。從而使表面的圖像和背景圖像混合,從而形成透明半透明的效果。在這種格式下A做爲一個byte,取值能夠從0到255,那麼0表示圖像徹底透明,則徹底不可見,255則表示圖像徹底不透明。每一個像素均可以實現這種透明或者半透明的效果。更詳細解釋能夠參考http://en.wikipedia.org/wiki/Alpha_compositing,或者去買本數字圖像處理的書回來看。讓咱們來看看下面這段代碼,這個函數能夠把圖像變成半透明的。
public unsafe Bitmap GenerateBitmap(byte alpha)
{
FileStream fs = new FileStream(image, FileMode.Open, FileAccess.Read);
Image img = Image.FromStream(fs, false, false);
Bitmap bmp = new Bitmap(img);
img.Dispose();
fs.Close();
int width = bmp.Width;
int height = bmp.Height;
BitmapData bmData = bmp.LockBits(
new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
byte* p = (byte*)bmData.Scan0;
int offset = bmData.Stride - width * 4;
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++)
{
p[3] = alpha;
p += 4;
}
p += offset;
}
bmp.UnlockBits(bmData);
return bmp;
}
你們能夠注意一下第17,22和23行,因爲圖像格式不對了,因此咱們計算offset和遞加的操做都該了,此外因爲用小數端存儲方式,Alpha通道在p[3]的位置。
順便提一句,還有一種格式叫作Format32bppPArgb,這叫作premultiplied alpha,就是說在RGB份量裏面,alpha份量的數據已經被預先乘進去了。好比說,一個半透明的紅色點,在ARGB下,矢量是(255,0,0,128),而在PARGB下就變成了(128,0,0,128)。這是爲了避免要每次都作乘法。
還有要注意的是,若是你想把這個Bitmap保存成爲一個文件,那麼必須用png格式,纔可以保存alpha通道的信息。若是你存爲JPG/BMP/GIF,那麼alpha通道的信息將會被丟失。若是存爲BMP,那麼文件格式將變成Format32bppRgb,其中1個字節再也不使用;若是保存爲JPEG,那麼是Format24bppRgb;存爲GIF,格式將變成Format8bppIndexed。根據標準,BMP/JPG原本就不支持透明通道,因此沒有可能保留透明信息。GIF卻是支持透明,可是GIF中顏色的信息都是索引,因此Alpha的解釋對GIF徹底沒有效果,接下去咱們來分析怎麼樣使用GIF的透明。
11) 一個不錯的 GDI+ FAQ site : http://www.bobpowell.net/faqmain.htm