其實我一直都是喜歡自己去做圖像格式的解碼的(目前我自己解碼的圖像格式大概有15種),但是寫本文主要原因是基於CSDN的這個帖子的:
http://bbs.csdn.net/topics/390510431 用pictureBox顯示一個黑白8bit圖像,如何消除顆粒感
用於測試的原始的JPG圖像: http://files.cnblogs.com/Imageshop/img01.rar
這個帖子中,作者的需要加載一副灰度的8位的PG格式圖像,但是利用.net的Bitmap類加載的圖像會出現明顯顆粒感,由於.net中的Bitmap類是基於GDI+操作的,因此我也是試着用我的Imageshop打開這幅圖像(Imageshop內部也是用GDI+的API實現的),同樣有顆粒感。因此,我們需要從其他的手段來解決這個問題。

.net下加載的效果 Photoshop打開的效果
首先,我用了VS6.0中的Stdpicture對象來加載這幅圖像,能得到正確的結果。然后用PS打開它,也能得到較好的效果,最后用微軟的圖片查看器,也是可以正確顯示的。最后用mspaint(畫圖)工具打開,則出現了和在.net中一樣的效果。
因此,我們的第一理想方案是使用com里的Stdpicture來解決這個問題,其實在VB6.0下,一個LoadPicture函數就可以解決它,但是在C#下要使用它,需要很多API函數來處理,我自己試着搞了下,覺得過於繁瑣,因此放棄了。
因此,我把希望投向了比較有名的圖像解碼的軟件FreeImage中,經過試驗,發現FreeImage的解碼是和PS一致的。
我們先來看看百度對FreeImage的介紹:
由上述可見,FreeImage的側重點偏向於圖像的解碼和編碼,顯示圖像則需要用戶自己負責,而這正是我們所需要的。
為了能在.NET中使用FreeImage,我知道的有兩種方式,一種是直接使用FreeImage 的Flat API,而這需要對使用的API函數進行聲明。另外一種方式就是使用FreeImage 提供的FreeImageNET.dll中提供的類庫(其實就是對FreeImage.dll中函數的封裝)。 我這里把兩種方式的實現都簡單的描述下:
public static Bitmap LoadImageFormFreeImage(string FileName) { Bitmap Bmp = null; FREE_IMAGE_FORMAT fif = FREE_IMAGE_FORMAT.FIF_UNKNOWN; ; fif = FreeImage_GetFileType(FileName, 0); if (fif == FREE_IMAGE_FORMAT.FIF_UNKNOWN) { fif = FreeImage_GetFIFFromFilename(FileName); } if ((fif != FREE_IMAGE_FORMAT.FIF_UNKNOWN) && (FreeImage_FIFSupportsReading(fif) != 0)) { IntPtr Dib = FreeImage_Load(fif, FileName, 0); int Bpp = FreeImage_GetBPP(Dib); PixelFormat PF; int Width, Height, Stride; switch (Bpp) { case 1: PF = PixelFormat.Format1bppIndexed; break; case 4: PF = PixelFormat.Format4bppIndexed; break; case 8: PF = PixelFormat.Format8bppIndexed; break; case 16: PF = PixelFormat.Format16bppRgb555; break; case 24: PF = PixelFormat.Format24bppRgb; break; case 32: PF = PixelFormat.Format32bppArgb; break; default: FreeImage_Free(Dib); return null; } Width = FreeImage_GetWidth(Dib); // 圖像寬度 Height = FreeImage_GetHeight(Dib); // 圖像高度 Stride = FreeImage_GetPitch(Dib); // 圖像掃描行的大小,必然是4的整數倍 /** 方案1:存在內存泄露 * FreeImage_FlipVertical(Dib); // 因為FreeImage的認為的圖像的起點在左下角,不進行翻轉則圖像的倒過來的 * IntPtr Bits = FreeImage_GetBits(Dib); // 得到圖像數據在內存中的地址 * Bmp = new Bitmap(Width, Height, Stride, PF, Bits); // 實際上調用的GdipCreateBitmapFromScan0函數從內存創建位圖 **/ //方案2: IntPtr Bits = FreeImage_GetBits(Dib); Bmp = new Bitmap(Width, Height, Stride, PF, Bits); Bmp.RotateFlip(RotateFlipType.RotateNoneFlipY); // 調用GDI+自己的旋轉函數 if (Bpp <= 8) { ColorPalette Pal = Bmp.Palette; // 設置調色板 RGBQUAD* GdiPal = FreeImage_GetPalette(Dib); int ClrUsed = FreeImage_GetColorsUsed(Dib); for (int I = 0; I < ClrUsed; I++) { Pal.Entries[I] = Color.FromArgb(255, GdiPal[I].Red, GdiPal[I].Green, GdiPal[I].Blue); } Bmp.Palette = Pal; } FreeImage_Free(Dib); // 使用方案2則可以立馬釋放 return Bmp; } return null; } }
上述代碼中,我們對方案1為什么存在內存泄露做一定的說明。
方案1中,Bmp = new Bitmap(Width, Height, Stride, PF, Bits)這條語句實際上調用了GDI+的函數GdipCreateBitmapFromScan0從內存創建位圖,通過此種方式創建的位圖並沒有新分配一塊內存給創建的位圖,而是和Bits對應的內存綁定的。您可以用如下的代碼驗證這一點:
BitmapData BmpData = Bmp.LockBits(new Rectangle(0, 0, Bmp.Width, Bmp.Height), ImageLockMode.ReadWrite, Bmp.PixelFormat);
if (BmpData.Scan0 == Bits )
MessageBox.Show ("通過GDI+創建的圖像和FreeImage的DIB對象公用同一內存.")
Bmp.UnlockBits(BmpData);
正是由於這個原因的存在,如果采用方案1,我們不能在創建GDI+的位圖后立馬釋放FreeImage的創建的DIB對象,即不能調用FreeImage_Free(Dib);如果調用了,對應的 Bmp對象實際上是個空對象了。
這樣的話也許可能沒有關系,我們只要在適當的地方調用Bmp.Dispose,不就可以了嗎,你可以做個試驗,使用這段代碼,然后不斷的打開新圖像,你會發現程序占用的內存會不斷的增加,而沒有釋放。因此,我們看看MSDN對GdipCreateBitmapFromScan0這個函數是怎么解釋的,特別是最后一個參數:
- scan0 [in]
-
Type: BYTE*
Pointer to an array of bytes that contains the pixel data. The caller is responsible for allocating and freeing the block of memory pointed to by this parameter.
上述文字表示用戶需要對分配的內存進行釋放,也就是說Dispose方法無法釋放該部分內存。
有了上述的問題,我們轉而使用方案2,方案2使用了一句Bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);這個語句會創建一副新的位圖,也就是說進行旋轉后的圖像已經不再同FreeImage使用同一塊內存了。那么此時就可以放心的釋放掉FreeImage的DIB對象了。
本以為RotateFlip函數會降低效率,測試表面微軟對這個函數的執行效率還是很高的,其實這個函數的函數完全可以借助於CopyMemory函數來高速實現。
當圖像的位深小於8時,需要獲取調色板的數據。但是我對認為上述獲取調色板的FreeImage_GetPalette函數存在內存泄露,無法釋放這些RGBQUAD*分配的內存的。FreeImage應該考慮使用類似於GDI+中獲取調色板數據那種方式。
使用FreeImageNET.dll中提供的類庫,則編寫代碼更為方便,推薦使用第二種方式,朋友們可以參考附件。
實際上FreeImage還有很多強大的功能,比如色深轉換、充分利用它洗看圖軟件,格式批處理那是很快捷方便的。
附件中的拖動圖像的方式我認為也是值得作為大家學習的。
http://files.cnblogs.com/Imageshop/FreeImage.rar
http://files.cnblogs.com/Imageshop/FreeImageApi.rar
***************************作者: laviewpbt 時間: 2013.7.7 聯系QQ: 33184777 轉載請保留本行信息*************************
