在 VC++6.0 中將 JPG 格式圖片轉換成 BMP 格式

在 VC++6.0 中將 JPG 格式圖片轉換成 BMP 格式

思路:利用 GDI+來完成
難點: (1)配置 GDI 開發環境,添加配置代碼
(2)在當文檔程序中添加轉換代碼
注意:本文檔由 ybdesire 參考網上資料撰寫完成,代碼已經做過測試,可直接複製張貼


實現過程:

一、 配置 GDI 開發環境
(1) 下載 GDI+ SDK for Visual C++ 6.0
http://www.codeguru.com/code/legacy/gdi/GDIPlus.zip
下載的GDIPlus文件夾中有Includes,Lib文件夾和gdiplus.dll文件。
將Includes和Lib中的文件分別拷到VC6安裝目錄中的VC98\include和lib文件夾下。

(2) 新建 MFC 單文檔應用程序 show:

  1. #include <afxdtctl.h>  
  2. #define ULONG_PTR ULONG  
  3. #include <gdiplus.h>  
  4. using namespace Gdiplus;  
  5. #pragma comment(lib, "gdiplus.lib")  

2、如(1)中所說,將gdiplus.dll拷貝到本工程的Debug或Release目錄下

3、在show.h中的class CShowApp : public CWinApp中添加

  1. private:  
  2.     GdiplusStartupInput m_gdiplusStartupInput;  
  3.     ULONG_PTR m_pGdiToken;  

4、在show.cpp中的BOOL CShowApp::InitInstance()中添加
  1. GdiplusStartup(&m_pGdiToken,&m_gdiplusStartupInput,NULL);  

5、 CShowApp添加名字爲ExitInstance的虛函數的,中添加如下退出GDI+的代碼
  1. GdiplusShutdown(m_pGdiToken);  

(3) 測試 GDI+是否配置成功
  1. void CShowView::OnDraw(CDC* pDC)  
  2. {  
  3.     CShowDoc* pDoc = GetDocument();  
  4.     ASSERT_VALID(pDoc);  
  5.     Graphics graphics(pDC->m_hDC);  
  6.     Pen pen(Color(255, 0, 255));  
  7.     graphics.DrawLine(&pen, 0, 0, 200, 100);  
  8. }  

若能成功運行,表明GDI+配置成功


二、 添加轉換代碼,實現將打開的 JPG 格式文件保存成 BMP 格式文件


(1)在 CSshowView 中添加如下成員變量


  1. CString strOpenFileName;  

(2)在 CSshowView 中添加如下成員函數
1、在 CSshowView 中添加 ToWChar 函數

  1. WCHAR* CShowView::ToWChar(char *str)  
  2. {  
  3.     //在 GDI+中,有關字符的參數類型全部都是 WCHAR 類型的  
  4.     //該函數是將傳統字符串進行轉換  
  5.     static WCHAR buffer[1024];  
  6.     wcsset(buffer,0);  
  7.     MultiByteToWideChar(CP_ACP,0,str,strlen(str),buffer,1024);  
  8.     return buffer;  
  9. }  

2、在 CSshowView 中添加 GetImageCLSID 函數

  1. int CShowView::GetImageCLSID(const WCHAR *format, CLSID *pCLSID)  
  2. {  
  3.     UINT num=0;  
  4.     UINT size=0;  
  5.     ImageCodecInfo* pImageCodecInfo=NULL;  
  6.     GetImageEncodersSize(&num,&size);  
  7.     if(size==0)  
  8.         return FALSE; // 編碼信息不可用  
  9.     // 分配內存  
  10.     pImageCodecInfo=(ImageCodecInfo*)(malloc(size));  
  11.     if(pImageCodecInfo==NULL)  
  12.         return FALSE; // 分配失敗  
  13.     // 獲得系統中可用的編碼方式的所有信息  
  14.     GetImageEncoders(num,size,pImageCodecInfo);  
  15.     // 在可用編碼信息中查找 format 格式是否被支持  
  16.   
  17.     for(UINT i=0;i<num;++i)  
  18.     {  
  19.         //MimeType: 編碼方式的具體描述  
  20.         if (wcscmp(pImageCodecInfo[ i] .MimeType,format)==0)  
  21.         {  
  22.             *pCLSID=pImageCodecInfo[i].Clsid;  
  23.             free(pImageCodecInfo);  
  24.             return TRUE;  
  25.         }  
  26.     }  
  27.     free(pImageCodecInfo);  
  28.     return FALSE;  
  29. }  

3、在 MFC ClassWixard 中重載 OnFileOpen()

  1. void CShowView::OnFileOpen()  
  2. {  
  3.     static char szFilter[ ]="常見圖像格式文件(*.*)|*.*|";  
  4.     CFileDialog dlgChoseImage(1,NULL,NULL,NULL,szFilter);  
  5.     if(dlgChoseImage.DoModal() ==IDOK)  
  6.     {  
  7.         strOpenFileName=dlgChoseImage.GetPathName();  
  8.         // 打開文件後立即在窗口中顯示 (重繪客戶窗口)  
  9.         this->Invalidate() ;  
  10.     }  
  11. }  

4、在 MFC ClassWixard 中重載 OnFileSave()

  1. void CShowView::OnFileSave()  
  2. {  
  3.     if( strOpenFileName.IsEmpty() )  
  4.     {  
  5.         AfxMessageBox("當前沒有打開圖像文件, 不能進行保存!");  
  6.         return;  
  7.     }  
  8.   
  9.     // 建立圖形對象  
  10.     Graphics graphics(GetDC()->m_hDC);  
  11.   
  12.     // 裝入當前已經打開的圖像文件  
  13.     Image image(ToWChar(strOpenFileName.GetBuffer(strOpenFileName.GetLength())));  
  14.   
  15.     CString strFileSave;  
  16.     // 將其他格式的圖像全部另存爲 BMP 文件  
  17.     static char szFilter[ ]= "位圖(*.BMP)|*.BMP|";  
  18.   
  19.     CFileDialog dlgChoseImage(0,"BMP",strOpenFileName,NULL,szFilter);  
  20.   
  21.     if(dlgChoseImage.DoModal() ==IDOK)  
  22.   
  23.     {  
  24.   
  25.         strFileSave=dlgChoseImage.GetPathName();  
  26.   
  27.         CLSID clsid;  
  28.   
  29.         if(GetImageCLSID(L"image/bmp", &clsid))  
  30.         {  
  31.             image.Save(ToWChar(strFileSave.GetBuffer(strFileSave.GetLength())), &clsid, NULL);  
  32.             // 將保存後的圖像進行顯示  
  33.             strOpenFileName=strFileSave;  
  34.             this->Invalidate() ;  
  35.         }  
  36.     }  
  37. }  

參考資料

[1]http://blog.csdn.net/poonjun/archive/2009/01/04/3701724.aspx
[2]http://www.cnblogs.com/DavidHu/articles/1191635.html
[3]http://www.smth.edu.cn/bbsgcon.php?board=VisualC&num=785
[4]《圖像格式轉換在數字儀表識別系統中的應用》 劉 娜, 汪仁煌, 龐 然

擴展知識閱讀

    [3] 色彩鮮豔漂亮的高品質圖像,一個個形象的Windows圖標,高速運動、活靈活現的三維動畫,這些生動的圖形無一不顯示着程序設計者的藝術才華。在程序設計中,圖像處理已經成了每個程序員的必修課,所以,對於每個程序員來說,熟悉“BMP”、“GIF”、“JPEG”圖像格式及具體應用、調色板、圖像文件頭格式、圖像壓縮算法等概念似乎已經成了工作中不可缺少的基礎知識。面對如此多的圖像格式,如果要全部掌握其具體細節,好像這對程序員有些不公。在VC中編程顯示一幅位圖,下列的步驟是不可少的:裝入位圖、獲得位圖的大小信息、啓用設備環境、位傳輸,所需的程序代碼顯得千篇一律的冗長。如果想要裝入的位圖另存爲其他格式的圖像文件……?兩個字:頭疼!而這一 切都是因爲GDI本身的侷限性所造成。

    隨着Windows 2000的推出,上面的情況有了大大的改觀:你可以不必瞭解每種圖像格式的具體含義,照樣可以寫出多格式圖像瀏覽或轉換程序,這一切,全部都依賴於Windows 2000及後繼版中所使用的GDI+技術。首先來看看GDI+的具體技術細節及GDI+編程特點。

    Windows 2000在用戶界面方面包括了幾個重大的改進,可能你已經注意到了有陰影的鼠 標、漸入的工具條快速提示、透明的窗口、平滑地窗口變化等。Windows 2000在界面上之所以有這麼大的改進,完全是因爲Windows2000採用了一種GDI(graphics device interface :圖形設備接口)。GDI+是一種新型的圖形設備接口,它的主要特點在於它能夠創建全新的用戶桌面 體系、能夠輕易地完成二維或三維的圖形處理,爲桌面帶來一種數字化的圖片。 GDI+同時也提供了增強的圖形處理技術,如常見的:alpha blending、 紋理、貼圖、增強的文本及圖片顯示技術。實際上,GDI+主要的特色就在於強調通過硬件加速來達到良好的 視覺感受!

    同傳統的GDI不同,GDI+中引入了對COM(組件對象模型)技術的支持,通過COM技術,GDI+簡化了對圖像文件的訪問(打開、保存)程序:通過調用COM組件來實現的,GDI+扮演的只是指揮者,而非操作員。對於圖像文件,GDI+所關心的不是圖像文件的文件頭信息,不論欲打開的文件格式是什麼類型,GDI+首先要做的是在註冊中查看該圖像格式的編碼(或解碼)信息是否已經註冊(HKEY_CLASSES_ROOT\MIME\Database\Content Type)

    在微軟的其他軟件中已經使用了,如IE。“體驗”過NIMDA病毒的朋友可能對“audio/wav”這段代碼並不陌生,NIMDA就是靠它來僞裝自己的:讓IE認爲附件是WAV文件而自動打開可執行程序。這其實也是IE使用COM技術的一個突出表現。
配合GDI+的推出,微軟也同時發佈了相應的SDK,如果你已經安裝了最新的Microsoft PlatForm SDK或已經開始使用VS .NET,GDI+ SDK已經在你的系統中了。如果沒有的話,可以到http://noner.top263.net/progtool上去下載GDI+的頭文件和庫文件。在使用GDI+之後,, 再有沒有必要去考慮什麼句柄、設備環境這樣的概念了。你只需要簡單地創建一個圖形對象(Graphics object)繪圖即可。圖形對象是GDI+中核心,正如DC之於GDI那樣。圖形對象和DC有許多相似的地方,在使用上遵循着相同的使用規則,但是兩者在本質上已經有很大的區別。一個是基於句柄的GDI,一個是基於組件對象模型的GDI+。使用GDI+的SDK編程,必須得按照下面的規範來進行:使用GDI+的名空間(namespace Gdiplus)進行GDI+的初始化,使用完畢之後也得銷燬GDI+,這種規範在下面所列的程序中有詳細的說明。前面說到了GDI+是通過在註冊中查看編碼信息來訪問圖像文件的,在GDI+的SDK中,編碼信息是儲存在 ImageCodecInfo類中的,在這個類中,有編碼的CLSID(COM組件的GUID標識碼)

    數來實現:

    1、查看系統中可用的圖像編碼信息(數量及大小)

  1. Status GetImageEncodersSize(  
  2.     UINT* numEncoders, //存儲編碼器數量的地址  
  3.     UINT* size //存儲編碼信息所需內存大小  
  4.     );  

2、得到所有的編碼信息
  1. Status GetImageEncoders(  
  2.     UINT numEncoders,//可用編碼器數量  
  3.     UINT size,//儲存編碼器信息所需內存(由ImageCodecInfo類組成的數組的大小)  
  4.     ImageCodecInfo* encoders//編碼器信息指針  
  5.     );  

在GetImageEncoders函數中,參數numEncoders和size都是由GetImageEncodersSize所返回的。下面的代碼就能夠在註冊表中查找具體格式圖像的編碼方式:

  1. int GetImageCLSID(const WCHAR* format, CLSID* pCLSID)  
  2. {//得到格式爲format的圖像文件的編碼值,訪問該格式圖像的COM組件的  
  3.     //GUID值保存在pCLSID中  
  4.     UINT num = 0;  
  5.     UINT size = 0;  
  6.     ImageCodecInfo* pImageCodecInfo = NULL;  
  7.     GetImageEncodersSize(&num, &size);  
  8.   
  9.     if(size == 0)  
  10.         return FALSE; // 編碼信息不可用  
  11.   
  12.     //分配內存  
  13.     pImageCodecInfo = (ImageCodecInfo*)(malloc(size));  
  14.     if(pImageCodecInfo == NULL)  
  15.         return FALSE; // 分配失敗  
  16.   
  17.     //獲得系統中可用的編碼方式的所有信息  
  18.     GetImageEncoders(num, size, pImageCodecInfo);  
  19.   
  20.     //在可用編碼信息中查找format格式是否被支持  
  21.     for(UINT i = 0; i < num; ++i)  
  22.     { //MimeType:編碼方式的具體描述  
  23.         if( wcscmp(pImageCodecInfo[i].MimeType, format) == 0 )  
  24.         {  
  25.             *pCLSID = pImageCodecInfo[i].Clsid;  
  26.             free(pImageCodecInfo);  
  27.             return TRUE;  
  28.         }  
  29.     }  
  30.     free(pImageCodecInfo);  
  31.     return FALSE;  
  32. }  

有了這種認識,實現多格式的圖像的瀏覽與轉換就並不是什麼難事了。爲了講述的方便,首先在VC中建立一個SDI項目ImageShow,首先對使用GDI+申明和初始化及銷燬進行代碼編制,具體代碼如下:

  1. #include "Gdiplus.h"  
  2. using namespace Gdiplus;  
  3.   
  4. CImageShowView::CImageShowView()  
  5. {  
  6.     //初始化GDI+  
  7.     GdiplusStartupInput gdiplusStartupInput;  
  8.     ULONG_PTR gdiplusToken;  
  9.     GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);  
  10. }  
  11.   
  12.   
  13. CImageShowView::~CImageShowView()  
  14. {  
  15.     //銷燬GDI+  
  16.     ULONG_PTR gdiplusToken;  
  17.     GdiplusShutdown(gdiplusToken);  
  18. }  

接着通過類嚮導(Class Wizard),重載“文件”菜單中的“打開”和“另存爲”兩項,爲了編程的簡單,本程序只將當前打開的圖像文件直接存爲BMP文件(實際上保存成其他格式的文件也很簡單,只不過是對文件名進行分析而已)進行文件名的傳遞,首先應在CImageShowView類中加入一全局變量“CString strOpenFileName”。“打開”和“另存爲”兩項的響應代碼如下,大家通過代碼中的註釋部份理解編程思路,應該不會有什麼問題:
  1. WCHAR* ToWChar(char * str)  
  2. {  
  3.     //在GDI+中,有關字符的參數類型全部都是WCHAR類型的  
  4.     //該函數是將傳統字符串進行轉換  
  5.     static WCHAR buffer[1024];  
  6.     wcsset(buffer,0);  
  7.     MultiByteToWideChar(CP_ACP,0,str,strlen(str),buffer,1024);  
  8.     return buffer;  
  9. }  

  1. void CImageShowView::OnFileOpen()  
  2. {  
  3.     //本程序能夠打開各類常見格式的圖像文件  
  4.     static char szFilter[]="常見格式圖形文件(*.*)|*.*|";  
  5.     CFileDialog dlgChoseImage(1,NULL,NULL,NULL,szFilter);  
  6.   
  7.     if(dlgChoseImage.DoModal()==IDOK)  
  8.     {  
  9.         strOpenFileName=dlgChoseImage.GetPathName();  
  10.         //打開文件後立即在窗口中顯示(重繪客戶窗口)  
  11.         this->Invalidate();  
  12.     }  
  13. }  
  14.   
  15.   
  16. void CImageShowView::OnFileSaveAs()  
  17. {  
  18.     if(strOpenFileName.IsEmpty())  
  19.     {  
  20.         AfxMessageBox("當前沒有打開圖像文件,不能進行保存!");  
  21.         return;  
  22.     }  
  23.   
  24.   
  25.     //建立圖形對像  
  26.     Graphics graphics(GetDC()->m_hDC);  
  27.   
  28.     //裝入當前已經打開的圖形文件  
  29.     Image image(ToWChar(strOpenFileName.GetBuffer(strOpenFileName.GetLength())));  
  30.     CString strFileSave;  
  31.   
  32.     //當其他格式的圖像全部另存爲BMP文件  
  33.     static char szFilter[]="位圖(*.BMP)|*.BMP|";  
  34.     CFileDialog dlgChoseImage(0,"BMP",NULL,NULL,szFilter);  
  35.     if(dlgChoseImage.DoModal()==IDOK)  
  36.     {  
  37.         strFileSave=dlgChoseImage.GetPathName();  
  38.         CLSID clsid;  
  39.   
  40.         if(GetImageCLSID(L"image/bmp", &clsid))  
  41.         {  
  42.             image.Save(ToWChar(strFileSave.GetBuffer(strFileSave.GetLength())), &clsid,NULL);  
  43.   
  44.             //將保存後的圖像進行顯示  
  45.             strOpenFileName=strFileSave;  
  46.             this->Invalidate();  
  47.         }  
  48.     }  
  49. }  

最後,爲了顯示瀏覽圖像轉換前後的效果,還應該在窗口中分另繪製轉換前後的圖像,這很容易,只需要在OnDraw函數中添加繪製代碼,如下所述:
  1. void CImageShowView::OnDraw(CDC* pDC)  
  2. {  
  3.     CImageShowDoc* pDoc = GetDocument();  
  4.     ASSERT_VALID(pDoc);  
  5.     //如果沒有選擇顯示圖形文件,則不用重繪  
  6.     if(strOpenFileName.IsEmpty())  
  7.         return;  
  8.     //顯示當前打開的圖像文件的全名  
  9.     this->GetParent()->SetWindowText(strOpenFileName);  
  10.   
  11.     //建立圖形對象  
  12.     Graphics graphics(pDC->m_hDC);  
  13.   
  14.     //裝入圖形文件  
  15.     Image image(ToWChar(strOpenFileName.GetBuffer(strOpenFileName.GetLength())));  
  16.     Point destPoints[3] =  
  17.     {  
  18.         Point(0, 0),  
  19.         Point(image.GetWidth(), 0),  
  20.         Point(0, image.GetHeight())  
  21.     };  
  22.     Point* pdestPoints = destPoints;  
  23.     //在指定區域pdestPoints顯示圖像  
  24.     graphics.DrawImage(&image, pdestPoints, 3);  
  25. }  
在編譯上面的程序之前,應該將Gdiplus.lib文件連編到項目中去,否則將會出現“LINK 2001”編譯錯誤。該程序在Visual Studio 6.0、Windows2000/XP下調試通過,它能夠顯示或轉換的圖像格式有BMP、GIF、JPEG 、Exif 、PNG 、TIFF 、ICON、WMF 、EMF等等。需要說明的是,本文只就GDI+編程的基本原理進行闡述,其實,GDI+的應用遠不止於此。在GDI+的背後,有你意想不到的驚奇!瞧,這程序運行起來是不是有些象ACDSee之類的圖像瀏覽程序?如果對本程序進行些改進,你也以做出功能更加強勁的圖像處理程序。本文中所提到的程序,在我的主頁“國稅之家”(http://nationaltax.home.chinaren.com)的“個人世界”中可以下下載到。有關GDI+的編程序幫助信息,大家可以到微軟的MSDN網站去查閱。如果你有VisualStudio .NET,這就最好,因爲所附的MSDN for Visual Studio.NET 7.0中有GDI+編程所需的全部信息
發佈了82 篇原創文章 · 獲贊 22 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章