談談Windows的
繪圖裝置介面(Graphics Device Interface, GDI),當我們要呈現影像在Windows中,一定會撰寫GDI的程式。
根據MSDN上的解釋:
http://msdn.microsoft.com/en-us/library/ms536380%28v=VS.85%29.aspx
「As its name suggests,
GDI+ is the successor to Windows Graphics Device Interface (GDI), the graphics device interface included with earlier versions of Windows. Windows XP or Windows Server 2003 supports GDI for compatibility with existing applications, but programmers of new applications should use GDI+ for all their graphics needs because
GDI+ optimizes many of the capabilities of GDI and also provides additional features.
」
就目前而言,Windows作業系統都是使用XP以後的版本,因此現在多使用也建議以GDI+的API開發,而在.NET Framework中相關GDI+元件的命名空間有:
我們在撰寫GDI的程式上有一個觀念很重要:
Windows作業系統不會記憶任何視窗上面顯示的內容。換句話說,內容必須由我們的應用程式負責處理,這通常是
OnPaint()事件處理常式在做的事情。
利用
Graphics類別,我們可以操作GDI+繪圖介面去做我們想要做的事情,也就是在視窗上繪圖,這裡要特別注意,
使用Graphics必須呼叫Dispose()釋放所使用的所有資源,不然就是使用
using 陳述式。一般來說,在OnPaint()事件處理常式中使用Graphics物件對視窗繪圖,這樣視窗每次收到Paint事件時,就會保持視窗內容的樣子,而不會是一片空白(因為Windows作業系統不會幫你繪圖)。
取得Graphics物件的方式有兩個方法:
- 一個是前述的OnPaint()事件處理常式中,利用PaintEventArgs參數取得所需要繪製的Graphics物件,也可以使用ClipRectangle屬性取得要繪製的矩形Rectangle結構。
- 呼叫視窗(控制項)的CreateGraphics()方法取得。
另外,常用的影像物件是
Bitmap物件(點陣圖),Bitmap可以讀取和存檔的類型有:BMP、GIF、EXIG、JPG、PNG 及 TIFF(對於一般程式撰寫已經夠用了),你可以使用
GetPixel和
SetPixel方法為影像重新上色,這裡不建議使用這種方式,
GetPixel和 SetPixel方法的效能不好,建議使用BitmapData的操作方式,效能上可以加速一個數量級左右。
重點在於:使用.NET Framework的影像處理,你要用Array運算,處理之後將Array的資料轉換至BitmapData,
影像處理用Array,而影像呈現還是用Bitmap,如此就能解決效能瓶頸的問題。
使用
BitmapData的方式如下,注意到要使用Bitmap類別的
LockBits和
UnlockBits方法。參考
http://msdn.microsoft.com/zh-tw/library/5ey6h79d%28v=VS.90%29.aspx
// Create a new bitmap.
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Set every third value to 255. A 24bpp bitmap will look red.
for (int counter = 2; counter < rgbValues.Length; counter += 3)
rgbValues[counter] = 255;
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
另外,Bitmap的PixelFormat建議使用
Format32bppArgb的格式,也就是每像素 32 位元,各有 8位元用於 Alpha、Red、Green和Blue,雖然大多數的影像都24位元的
Format24bppRgb,但是在使用BitmapData複製記憶體時,最好每個
Stride(掃描寬度)是4bytes的倍數,避免記憶體對映到Bitmap發生影像的移位錯誤。