C#中使用FreeImage庫加載Bmp、JPG、PNG、PCX、TGA、PSD等25種格式的圖像(源碼)。


  其實我一直都是喜歡自己去做圖像格式的解碼的(目前我自己解碼的圖像格式大概有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是一款免費的、開源的、跨平台(Windows 、Linux 和Mac OS X )的,支持20 多種圖像類型的(如BMP 、JPEG 、GIF 、PNG 、TIFF 等)圖像處理庫。其最大優點就是采用插件驅動型架構,具有快速、靈活、簡單易用的特點,得到了廣泛使用。
      FreeImage 的主要功能有多格式位圖的讀寫;方便的位圖信息獲取;位深度轉換;位圖頁面訪問;基本的幾何變換和點處理;通道合並與混合等。FreeImage 暫時不支持矢量圖形和高級圖像處理,位圖繪制需要由用戶來完成。
      FreeImage 中的所有函數都以FreeImage_ 開頭,如圖像文件的讀寫函數分別為FreeImage_Load 和FreeImage_Save 。FIBITMAP 數據結構保存着位圖信息和像素數據,是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  轉載請保留本行信息*************************

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM