思路:利用 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:
- #include <afxdtctl.h>
- #define ULONG_PTR ULONG
- #include <gdiplus.h>
- using namespace Gdiplus;
- #pragma comment(lib, "gdiplus.lib")
2、如(1)中所說,將gdiplus.dll拷貝到本工程的Debug或Release目錄下
3、在show.h中的class CShowApp : public CWinApp中添加
- private:
- GdiplusStartupInput m_gdiplusStartupInput;
- ULONG_PTR m_pGdiToken;
4、在show.cpp中的BOOL CShowApp::InitInstance()中添加
- GdiplusStartup(&m_pGdiToken,&m_gdiplusStartupInput,NULL);
5、 CShowApp添加名字爲ExitInstance的虛函數的,中添加如下退出GDI+的代碼
- GdiplusShutdown(m_pGdiToken);
(3) 測試 GDI+是否配置成功
- void CShowView::OnDraw(CDC* pDC)
- {
- CShowDoc* pDoc = GetDocument();
- ASSERT_VALID(pDoc);
- Graphics graphics(pDC->m_hDC);
- Pen pen(Color(255, 0, 255));
- graphics.DrawLine(&pen, 0, 0, 200, 100);
- }
若能成功運行,表明GDI+配置成功
二、 添加轉換代碼,實現將打開的 JPG 格式文件保存成 BMP 格式文件
(1)在 CSshowView 中添加如下成員變量
- CString strOpenFileName;
(2)在 CSshowView 中添加如下成員函數
1、在 CSshowView 中添加 ToWChar 函數
- WCHAR* CShowView::ToWChar(char *str)
- {
- //在 GDI+中,有關字符的參數類型全部都是 WCHAR 類型的
- //該函數是將傳統字符串進行轉換
- static WCHAR buffer[1024];
- wcsset(buffer,0);
- MultiByteToWideChar(CP_ACP,0,str,strlen(str),buffer,1024);
- return buffer;
- }
2、在 CSshowView 中添加 GetImageCLSID 函數
- int CShowView::GetImageCLSID(const WCHAR *format, CLSID *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;
- }
3、在 MFC ClassWixard 中重載 OnFileOpen()
- void CShowView::OnFileOpen()
- {
- static char szFilter[ ]="常見圖像格式文件(*.*)|*.*|";
- CFileDialog dlgChoseImage(1,NULL,NULL,NULL,szFilter);
- if(dlgChoseImage.DoModal() ==IDOK)
- {
- strOpenFileName=dlgChoseImage.GetPathName();
- // 打開文件後立即在窗口中顯示 (重繪客戶窗口)
- this->Invalidate() ;
- }
- }
4、在 MFC ClassWixard 中重載 OnFileSave()
- void CShowView::OnFileSave()
- {
- 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",strOpenFileName,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() ;
- }
- }
- }
參考資料
[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、查看系統中可用的圖像編碼信息(數量及大小)
- 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);
- MultiByteToWideChar(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.DoModal()==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);
- }