與GDI相比,GDI+要強大非常多。對于Windows應用程式來說,用GDI是比較多的,也是比較熟練的,GDI+相對用的較少一點,可是如今GDI+的使用已經非常普遍了。GDI+支援各種類型圖檔的處理,比方常見的bmp、jpg、gif、png等類型,特别是GDI+處理png圖檔時有非常大的優勢。有時我們須要将圖檔檔案載入到記憶體中,然後進行UI的繪制,因為要支援多種類型的圖檔的載入,是以首先想到的是使用GDI+中的圖檔處理類Image或Bitmap。有時我們也須要将記憶體中的位圖資料,儲存成各種類型的圖檔檔案,我們也要用到圖檔處理類Image或Bitmap。GDI+功能強大,但相對GDI而言,要難用非常多,在使用的過程中也有非常多須要注意的地方。以下結合本人在實際開發過程中遇到的問題,進行一些總結,以供參考。
1、GDI+庫的載入與解除安裝
在程式初始化時,加入?載入GDI+的代碼:
在程式退出時,加入?解除安裝GDI+的代碼:
在使用GDI+中相關函數和結構時,盡量加上Gdiplus命名空間名,以防止與其它子產品的代碼由于字段的名稱同樣出現沖突。比方,GDI+庫中定義GDI+函數運作結果的每句類型Status,定義例如以下所看到的。假設我們須要推斷函數是否正确運作,應該将傳回值和Gdiplus::Ok,而不是直接和Ok比較,注意這個加上Gdiplus命名空間名的好習慣。
2、靜态函數FromFile、FromHBitmap和FromStream的使用
FromFile主要是将圖檔檔案載入到GDI+對象中,FromHBitmap和FromStream函數則是将記憶體中的圖檔資料載入到GDI+對象中。我們尋常處理圖檔載入與格式轉換時主要用到兩個類:Bitmap類和Image類。Bitmap類繼承于Image類,這三個函數它都有。Image類則僅僅有FromFile和FromStream函數。在使用這三個函數時,要注意一下幾點。
(1)
對于FromFile、FromHBitmap和FromStream這三個函數,都是靜态函數,MSDN對于傳回值的說明:This method returns a
pointer to the new Bitmap/Image
object(在VS中GO到函數的定義出也是能看出來的,函數傳回是new出來的對象)。這意味着什麼呢?由于傳回的是新建立的類的對象,是須要我們使用者來負責銷毀的,即對象使用完了後須要我們手動将之delete掉。假設不delete掉,不僅會導緻記憶體洩漏,也會導緻GDI句柄洩漏。這點在我們的項目開發中是深有體會的,特别是GDI句柄洩漏使用了專門的工具進行檢測的。
(2)
在使用Image::FromFile時,要注意将指定的檔案載入到Image對象中後,會将磁盤上相應的檔案“鎖住”,其它地方假設要同一時候載入該檔案則可能會出問題,這也是我們在開發過程中遇到的問題。我們的處理辦法是,不使用Image::FromFile函數,使用Image::FromStream。對于Image::FromStream,我們先将檔案讀到記憶體中,然後再将記憶體中資料倒到流中,然後調用Image::FromStream從流中将圖檔資料載入到Image對象中。使用Image::FromStream的流程較複雜,使用時要注意,也有一些陷阱,以下我們會談到。
(3)
對于GDI+提供的函數,對于須要傳入字元串的參數,一般均是WCHAR*寬位元組類型,是以在調用之前要確定傳入字元串是寬位元組的。這點和COM接口相似,一般都要傳入寬位元組的字元串。
3、Image::FromStream的使用
此處主要講怎樣将圖檔檔案載入到Image對象中的,使用Image::FromStream載入的流程大概為:先将圖檔檔案讀到HGLOBAL記憶體中,然後調用CreateStreamOnHGlobal函數在HGLOBAL記憶體資料基礎上建立流,最後調用Image::FromStream将圖檔資料載入到new出來的Image對象中。相關的代碼例如以下所看到的:
如上面的代碼,必需要加上pStream->Release();這句,否則會導緻記憶體洩漏,由于上面GlobalAlloc來的記憶體沒有釋放。可是代碼中使用完後并沒有調用GlobalFree來釋放記憶體,那自己主動釋放記憶體是怎樣做到的呢?那我們就來看看MSDN中,對CreateStreamOnHGlobal函數的說明:
參數fDeleteOnRelease的說明:A
value that indicates whether the underlying handle for this stream object should
be automatically freed when the stream object is released.If set to
FALSE, the caller must free the hGlobal after the final release. If set to TRUE,
the final release will automatically free the hGlobal parameter.
也就是說,當将fDeleteOnRelease參數設定為FALSE時,調用pStream->Release();時就不會自己主動釋放GlobalAlloc來的記憶體,此時必須手動調用GlobalFree來釋放;當将fDeleteOnRelease參數設定為TRUE時,在調用pStream->Release();是會自己主動将GlobalAlloc來的記憶體釋放掉。
4、GDI+的畫圖渲染能力
當我們在用GDI繪制斜線線條(非水準線條、非豎直線條)時,會有明顯的鋸齒,看起來效果不太好。用GDI+繪制則要好非常多,由于GDI+的渲染效果要比GDI好非常多,平滑非常多。