VC中實現多格式影像的靈活轉換 (轉)

worldblog發表於2007-12-12
VC中實現多格式影像的靈活轉換 (轉)[@more@]

四川省新都縣國家稅務局 周鳴揚

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

隨著的推出,上面的情況有了大大的改觀:你可以不必瞭解每種影像格式的具體含義,照樣可以寫出多格式影像瀏覽或轉換程式,這一切,全部都依賴於Windows 2000及後繼版中所使用的GDI+技術。首先來看看GDI+的具體技術細節及GDI+程式設計特點。 Windows 2000在介面方面包括了幾個重大的改進,可能你已經注意到了有陰影的滑鼠、漸入的工具條提示、透明的視窗、平滑地視窗變化等。Windows 2000在介面上之所以有這麼大的改進,完全是因為採用了一種GDI(graphics device interface :圖形裝置介面)。這種GDI,以前叫GDI2k,現在有了一個更好聽的名字:GDI+。GDI+是一種新型的圖形裝置介面,它的主要特點在於它能夠建立全新的使用者桌面體系、能夠輕易地完成二維或三維的圖形處理,為桌面帶來一種數字化的圖片。 GDI+ 同時也提供了增強的圖形處理技術,如常見的:alpha blending、 紋理、貼圖、增強的文字及圖片顯示技術。實際上,GDI+主要的特色就在於強調透過加速來達到良好的視覺感受! 同傳統的GDI不同,GDI+中引入了對COM(模型)技術的支援,透過COM技術,GDI+簡化了對影像檔案的訪問(開啟、儲存)程式:透過COM元件來實現的,GDI+扮演的只是指揮者,而非操作員。對於影像檔案,GDI+所關心的不是影像檔案的檔案頭資訊,不論欲開啟的檔案格式是什麼型別,GDI+首先要做的是在註冊中檢視該影像格式的編碼(或解碼)資訊是否已經註冊(HKEY_CLASSES_MIMEDatabaseContent Type),如果已經註冊,就透過該編碼資訊呼叫COM元件,就這麼簡單。這種技術其實早就在的其他中已經使用了,如IE。“體驗”過NIMDA的朋友可能對“audio/wav”這段程式碼並不陌生,NIMDA就是靠它來偽裝自己的:讓IE認為附件是WAV檔案而自動開啟可程式。這其實也是IE使用COM技術的一個突出表現。 配合GDI+的推出,微軟也同時釋出了相應的SDK,如果你已經了最新的 PlatFoSDK或已經開始使用VS,GDI+ SDK已經在你的中了。如果沒有的話,可以到上去GDI+的標頭檔案和庫檔案。在使用GDI+之後,, 再有沒有必要去考慮什麼控制程式碼、裝置環境這樣的概念了。你只需要簡單地建立一個圖形物件(Graphics ),然後直接呼叫該物件的方法(methods)進行繪圖即可。圖形物件是GDI+中核心,正如DC之於GDI那樣。圖形物件和DC有許多相似的地方,在使用上遵循著相同的使用規則,但是兩者在本質上已經有很大的區別。一個是基於控制程式碼的GDI,一個是基於元件物件模型的GDI+。使用GDI+的SDK程式設計,必須得按照下面的規範來進行:使用GDI+的名空間(namespace Gdiplus)、在使用GDI+時必須進行GDI+的初始化,使用完畢之後也得銷燬GDI+,這種規範在下面所列的程式中有詳細的說明。 前面說到了GDI+是透過在註冊中檢視編碼資訊來訪問影像檔案的,在GDI+的SDK中,編碼資訊是儲存在 ImageCodecInfo類中的,在這個類中,有編碼的CLSID(COM元件的GUID標識碼)、編碼方式描述等。在GDI中,在登錄檔中訪問編碼資訊通常使用以下兩個函式來實現: 1、檢視系統中可用的影像編碼資訊(數量及大小) Status GetImageEncodersSize( UINT* numEncoders, //編碼器數量的地址 UINT* size //儲存編碼資訊所需大小 ); 2、得到所有的編碼資訊 Status GetImageEncoders( UINT numEncoders,//可用編碼器數量 UINT size,//儲存編碼器資訊所需記憶體(由ImageCodecInfo類組成的陣列的大小) ImageCodecInfo* encoders//編碼器資訊指標 ); 在GetImageEncoders函式中,引數numEncoders和size都是由GetImageEncodersSize所返回的。下面的程式碼就能夠在登錄檔中查詢具體格式影像的編碼方式: int GetImageCLSID(const WCHAR* format, CLSID* pCLSID) {//得到格式為format的影像檔案的編碼值,訪問該格式影像的COM元件的 //GUID值儲存在pCLSID中 UINT num = 0; UINT size = 0; ImageCodecInfo* pImageCodecInfo = NULL; GetImageEncodersSize(&num, &size); if(size == 0) return FALSE; // 編碼資訊不可用 //分配記憶體 pImageCodecInfo = (ImageCodecInfo*)(malloc(size)); if(pImageCodecInfo == NULL) return FALSE; // 分配失敗 //獲得系統中可用的編碼方式的所有資訊 GetImageEncoders(num, size, pImageCodecInfo); //在可用編碼資訊中查詢format格式是否被支援 for(UINT i = 0; i < num; ++i) { //MimeType:編碼方式的具體描述 if( wcscmp(pImageCodecInfo[i].MimeType, format) == 0 ) { *pCLSID = pImageCodecInfo[i].Clsid; free(pImageCodecInfo); return TRUE; } } free(pImageCodecInfo); return FALSE; } 有了這種認識,實現多格式的影像的瀏覽與轉換就並不是什麼難事了。為了講述的方便,首先在VC中建立一個SDI專案ImageShow,首先對使用GDI+申明和初始化及銷燬進行程式碼編制,具體程式碼如下: #include "Gdiplus.h" using namespace Gdiplus; CImageShowView::CImageShowView() { //初始化GDI+ GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); } CImageShowView::~CImageShowView() { //銷燬GDI+ ULONG_PTR gdiplusToken; GdiplusShutdown(gdiplusToken); } 接著透過類嚮導(Class Wizard),過載“檔案”選單中的“開啟”和“另存為”兩項,為了程式設計的簡單,本程式只將當前開啟的影像檔案直接存為BMP檔案(實際上儲存成其他格式的檔案也很簡單,只不過是對檔名進行分析而已)。另外,為了在開啟和儲存檔案進行檔名的傳遞,首先應在CImageShowView類中加入一全域性變數“CString strOpenFileName”。“開啟”和“另存為”兩項的響應程式碼如下,大家透過程式碼中的註釋部份理解程式設計思路,應該不會有什麼問題: WCHAR* ToWChar(char * str) { //在GDI+中,有關字元的引數型別全部都是WCHAR型別的 //該函式是將傳統字串進行轉換 static WCHAR buffer[1024]; wcsset(buffer,0); MultiByteToWChar(CP_ACP,0,str,strlen(str),buffer,1024); return buffer; } void CImageShowView::OnFileOpen() { //本程式能夠開啟各類常見格式的影像檔案 static char szFilter[]="常見格式圖形檔案(*.*)|*.*|"; CFileDialog dlgChoseImage(1,NULL,NULL,NULL,szFilter); if(dlgChoseImage.odal()==IDOK) { strOpenFileName=dlgChoseImage.GetPathName(); //開啟檔案後立即在視窗中顯示(重繪客戶視窗) this->Invalidate(); } } void CImageShowView::OnFileSaveAs() { if(strOpenFileName.IsEmpty()) { AfxMessageBox("當前沒有開啟影像檔案,不能進行儲存!"); return; } //建立圖形對像 Graphics graphics(GetDC()->m_hDC); //裝入當前已經開啟的圖形檔案 Image image(ToWChar(strOpenFileName.GetBuffer(strOpenFileName.GetLength()))); CString strFileSave; //當其他格式的影像全部另存為BMP檔案 static char szFilter[]="點陣圖(*.BMP)|*.BMP|"; CFileDialog dlgChoseImage(0,"BMP",NULL,NULL,szFilter); if(dlgChoseImage.DoModal()==IDOK) { strFileSave=dlgChoseImage.GetPathName(); CLSID clsid; if(GetImageCLSID(L"image/bmp", &clsid)) { image.Save(ToWChar(strFileSave.GetBuffer(strFileSave.GetLength())), &clsid, NULL); //將儲存後的影像進行顯示 strOpenFileName=strFileSave; this->Invalidate(); } } } 最後,為了顯示瀏覽影像轉換前後的效果,還應該在視窗中分另繪製轉換前後的影像,這很容易,只需要在OnDraw函式中新增繪製程式碼,如下所述: void CImageShowView::OnDraw(CDC* pDC) { CImageShowDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); //如果沒有選擇顯示圖形檔案,則不用重繪 if(strOpenFileName.IsEmpty()) return; //顯示當前開啟的影像檔案的全名 this->GetParent()->SetWindowText(strOpenFileName); //建立圖形物件 Graphics graphics(pDC->m_hDC); //裝入圖形檔案 Image image(ToWChar(strOpenFileName.GetBuffer(strOpenFileName.GetLength()))); Point destPoints[3] = { Point(0, 0), Point(image.GetWidth(), 0), Point(0, image.GetHeight()) }; Point* pdestPoints = destPoints; //在指定區域pdestPoints顯示影像 graphics.DrawImage(&image, pdestPoints, 3); } 在編譯上面的程式之前,應該將Gdiplus.lib檔案連編到專案中去,否則將會出現“LINK 2001”編譯錯誤。該程式在 6.0、Windows2000/XP下透過,它能夠顯示或轉換的影像格式有BMP、GIF、JPEG 、Exif 、PNG 、TIFF 、ICON、WMF 、EMF等等。需要說明的是,本文只就GDI+程式設計的基本原理進行闡述,其實,GDI+的應用遠不止於此。在GDI+的背後,有你意想不到的驚奇! 瞧,這程式執行起來是不是有些象ACDSee之類的影像瀏覽程式?如果對本程式進行些改進,你也以做出功能更加強勁的影像處理程式。本文中所提到的程式,在我的主頁“國稅之家”()的“個人世界”中可以下下載到。有關GDI+的程式設計序幫助資訊,大家可以到微軟的MSDN網站去查閱。如果你有Visual Studio .NET,這就最好,因為所附的MSDN for Visual Studio.NET 7.0中有GDI+程式設計所需的全部資訊。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-991963/,如需轉載,請註明出處,否則將追究法律責任。

相關文章