如果你是一個(gè)使用Visual Basic編程的程序員,,要在程序中顯示JPG或者GIF圖像簡直易如反掌,將圖像控件拖到Form中就可以了,。但是
C++程序員要顯示同樣格式的圖像文件卻沒有那么輕松,,那么是不是要自己編寫JPG解壓縮代碼呢?當(dāng)然不用那么復(fù)雜啦,!本實(shí)例將針對(duì)這個(gè)問題討論如何在MFC中顯示JPG或者GIF格式的圖像文件,。
一、實(shí)現(xiàn)方法 用Visual Basic寫圖像顯示程序之所以如此輕松,,完全是利用了琳瑯滿目的圖像處理控件,,它們處理了顯示圖像文件的所有底層工作,而C++程序員為了實(shí)現(xiàn)相同的功能必須忙乎半天,。其實(shí),,C/C++程序員也能使用那些Visual Basic程序員所用的(或者說幾乎一樣的)圖像控件,。Visual Basic用的圖像控件實(shí)際上都是基于一個(gè)系統(tǒng)級(jí)的COM類--IPicture。下面是有關(guān)Ipicture類的方法描述:
get_Handle():返回圖像對(duì)象的
Windows GDI句柄,;
get_Hpal():返回圖像對(duì)象當(dāng)前使用的調(diào)色板拷貝,;
get_Type():返回當(dāng)前圖像對(duì)象的的圖像類型;
get_Width():返回當(dāng)前圖像對(duì)象的圖像寬度,;
get_Height():返回當(dāng)前圖像對(duì)象的圖像高度;
Render():在指定的位置,、指定的設(shè)備上下文上繪制指定的圖像部分,;
set_Hpal():設(shè)置當(dāng)前圖像的調(diào)色板;
get_CurDC():返回當(dāng)前選中這個(gè)圖像的設(shè)備上下文,;
SelectPicture():將一個(gè)位圖圖像選入給定的設(shè)備上下文,,返回選中圖像的設(shè)備上下文和圖像的GDI句柄 ;
get_KeepOriginalForma():返回圖像對(duì)象KeepOriginalFormat 屬性的當(dāng)前值,;
put_KeepOriginalFormat():設(shè)置圖像對(duì)象的KeepOriginalFormat 屬性,;
PictureChanged():通知圖像對(duì)象它的圖像資源改變了;
SaveAsFile():將圖像數(shù)據(jù)存儲(chǔ)到流中,,格式與存成文件格式相同,;
get_Attributes():返回圖像位屬性當(dāng)前的設(shè)置;
從上面的方法可以看出,,Ipicture類操縱著圖像對(duì)象及其屬性,。圖像對(duì)象提供對(duì)位圖的抽象,而Windows負(fù)責(zé)BMP,、JPG和GIF等格式的標(biāo)準(zhǔn)實(shí)現(xiàn),。程序員要做的只是實(shí)例化Ipicture類對(duì)象,然后調(diào)用其Render()函數(shù),。與通常使用接口的方式不同,,這里實(shí)例的創(chuàng)建我們不用CoCreateInstance()函數(shù),而是用一個(gè)專門的函數(shù)OleLoadPicture(),,具體實(shí)現(xiàn)代碼如下:
IStream* pstm = // 需要一個(gè)流(stream) IPicture* pIPicture; hr = OleLoadPicture(pstm, 0, FALSE, IID_IPicture, (void**)&pIPicture); |
OleLoadPicture()函數(shù)從數(shù)據(jù)流中加載圖像并創(chuàng)建一個(gè)可用來顯示圖像的新IPicture對(duì)象,。 rc = // 顯示圖像的矩形
IPicture 負(fù)責(zé)處理所有瑣事,以便確定圖形之格式,,如 Windows 位圖,、JPEG或者GIF文件--甚至是圖標(biāo)和元文件(metafiles)。當(dāng)然啦,,所有這些的實(shí)現(xiàn)細(xì)節(jié)是需要技巧的,,為此本實(shí)例寫了一個(gè)Demo程序Myimgapp來示范這些IPicture的使用方法。Myimgapp是個(gè)典型的MFC文檔/視圖程序,,在編寫這個(gè)程序之前,,首先對(duì) IPicture COM接口進(jìn)行封裝,,之所以要這么做,主要是考慮到并不是每一個(gè)程序員都能熟練運(yùn)用COM接口進(jìn)行編程,,另外將IPicture的主要功能封裝在C++類中可以使我們的問題更容易解決,,封裝的這個(gè)C++類名字叫做CPicture。它的定義和實(shí)現(xiàn)細(xì)節(jié)請(qǐng)參考本文提供的源代碼,。
本實(shí)例在這個(gè)類中將復(fù)雜而陌生的COM風(fēng)格的參數(shù)映射成MFC程序員更為熟悉的類型,。例如,CPicture可以讓你直接從文件名加載一幅圖像,,CFile或者CArchive,,而不用去處理流,CPicture::Render()替你完成了IPicture中所有令人討厭的但又是必須的HIMETRIC平滑轉(zhuǎn)換工作,。CPicture甚至具備了一個(gè)Load()函數(shù),,它可以從資源數(shù)據(jù)中加載圖像,所以你只要用下面的代碼就可以顯示資源中的圖像:
CPicture pic(ID_MYPIC); // 加載圖像 CRect rc(0,0,0,0); // 使用缺省的rc pic.Render(pDC, rc); // 顯示圖像 |
CPicture::Render提供一個(gè)顯示圖片的矩形,。IPicture 對(duì)圖像進(jìn)行延伸處理,。如果傳遞一個(gè)空矩形,則CPicture用圖像本身的大小--不進(jìn)行延伸處理,。對(duì)于圖像本身而言,,CPicture查找"IMAGE"類型的資源,所以在資源文件中必須要在程序的資源文件中加入如下語句來添加圖像資源:IDR_MYPIC IMAGE MOVEABLE PURE "res\\MyPic.jpg",。
CPicture是個(gè)很棒的傻瓜類,,它具備一個(gè) ATL 智能指針CComQIPtr指向IPicture接口,通過調(diào)用OleLoadPicture來初始化不同的Load函數(shù),。CPicture提供了常用的打包函數(shù)來調(diào)用底層的IPicture,。CPicture只封裝了那些在Demo例子程序中要用到的方法。如果讀者朋友需要調(diào)用IPicture::get_Handle或其它一些很少用到的IPicture方法,,可以自己嘗試編寫相應(yīng)的打包代碼,。
總之,IPicture/CPicture簡化了圖像的顯示,,它甚至可以實(shí)現(xiàn)調(diào)色板的識(shí)別這樣復(fù)雜的處理,,讀者朋友完全可以拋開老式DIB 圖像繪制方法,如加載調(diào)色板,、BitBlts(),、StretchBlts()等操作--這一切IPicture全都可以搞掂。
程序中有一個(gè)消息處理器值得一提:它就是視圖類的OnEraseBkgnd()函數(shù),,當(dāng)要顯示的圖像比客戶區(qū)小的時(shí)候,,這個(gè)函數(shù)必須繪制空白區(qū)域,OnEraseBkgnd()函數(shù)創(chuàng)建一個(gè)與圖像大小相等的切邊(clip)矩形,,然后將客戶區(qū)填成黑色,,之所以要?jiǎng)?chuàng)建切邊矩形,,主要是避免當(dāng)改變窗口大小時(shí)出現(xiàn)的抖動(dòng)--FillRect()不繪制切邊矩形內(nèi)的區(qū)域,此乃Windows圖形處理的常識(shí),。
二,、編程步驟
1、 啟動(dòng)Visual C++6.0,,生成一個(gè)單文檔視圖結(jié)構(gòu)的應(yīng)用程序,,視圖類的基類為CscrollView,同時(shí)將該程序命名為"Myimgapp",;
2,、 在應(yīng)用程序的項(xiàng)目代碼中添加"CPicture"類;工具欄上添加圖像顯示比例的按鈕,,具體參加代碼部分;
3,、 使用資源編輯器向程序中添加Jepg格式的圖像資源,;
4、 添加代碼,,編譯運(yùn)行程序,。
三、程序代碼
/////////////////// Picture object--encapsulates IPicture #pragma once #include <atlbase.h> class CPicture { public: CPicture(); ~CPicture(); // Load frm various sosurces BOOL Load(UINT nIDRes); BOOL Load(LPCTSTR pszPathName); BOOL Load(CFile& file); BOOL Load(CArchive& ar); BOOL Load(IStream* pstm); // render to device context BOOL Render(CDC* pDC, CRect rc=CRect(0,0,0,0), LPCRECT prcMFBounds=NULL) const; CSize GetImageSize(CDC* pDC=NULL) const; operator IPicture*() { return m_spIPicture; } void GetHIMETRICSize(OLE_XSIZE_HIMETRIC& cx, OLE_YSIZE_HIMETRIC& cy) const { cx = cy = 0; const_cast<CPicture*>(this)->m_hr = m_spIPicture->get_Width(&cx); ASSERT(SUCCEEDED(m_hr)); const_cast<CPicture*>(this)->m_hr = m_spIPicture->get_Height(&cy); ASSERT(SUCCEEDED(m_hr)); }
void Free() { if (m_spIPicture) { m_spIPicture.Release(); } }
protected: CComQIPtr<IPicture>m_spIPicture; // ATL smart pointer to IPicture HRESULT m_hr; // last error code };
/////////////////////////////////////////////////////////////// CPicture implementation #include "StdAfx.h" #include "Picture.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif
CPicture::CPicture() {}
CPicture::~CPicture() {}
BOOL CPicture::Load(UINT nIDRes) // Load from resource. Looks for "IMAGE" type. { // find resource in resource file HINSTANCE hInst = AfxGetResourceHandle(); HRSRC hRsrc = ::FindResource(hInst,MAKEINTRESOURCE(nIDRes),"IMAGE"); // type if (!hRsrc) return FALSE; // load resource into memory DWORD len = SizeofResource(hInst, hRsrc); BYTE* lpRsrc = (BYTE*)LoadResource(hInst, hRsrc); if (!lpRsrc) return FALSE; // create memory file and load it CMemFile file(lpRsrc, len); BOOL bRet = Load(file); FreeResource(hRsrc); GlobalFree(lpRsrc); return bRet; }
BOOL CPicture::Load(LPCTSTR pszPathName) // Load from path name. { CFile file; if (!file.Open(pszPathName, CFile::modeRead|CFile::shareDenyWrite)) return FALSE; BOOL bRet = Load(file); file.Close(); return bRet; }
BOOL CPicture::Load(CFile& file) // Load from CFile { CArchive ar(&file, CArchive::load | CArchive::bNoFlushOnDelete); return Load(ar); }
////// Load from archive--create stream and load from stream. BOOL CPicture::Load(CArchive& ar) { CArchiveStream arcstream(&ar); return Load((IStream*)&arcstream); }
////////////////// // Load from stream (IStream). This is the one that really does it: call // OleLoadPicture to do the work. BOOL CPicture::Load(IStream* pstm) { Free(); HRESULT hr = OleLoadPicture(pstm, 0, FALSE,IID_IPicture, (void**)&m_spIPicture); ASSERT(SUCCEEDED(hr) && m_spIPicture); return TRUE; }
////////////////// // Render to device context. Covert to HIMETRIC for IPicture. BOOL CPicture::Render(CDC* pDC, CRect rc, LPCRECT prcMFBounds) const { ASSERT(pDC);
if (rc.IsRectNull()) { CSize sz = GetImageSize(pDC); rc.right = sz.cx; rc.bottom = sz.cy; } long hmWidth,hmHeight; // HIMETRIC units GetHIMETRICSize(hmWidth, hmHeight); m_spIPicture->Render(*pDC, rc.left, rc.top, rc.Width(), rc.Height(),0, hmHeight, hmWidth, -hmHeight, prcMFBounds); return TRUE; }
////////////////// // Get image size in pixels. Converts from HIMETRIC to device coords. CSize CPicture::GetImageSize(CDC* pDC) const { if (!m_spIPicture) return CSize(0,0); LONG hmWidth, hmHeight; // HIMETRIC units m_spIPicture->get_Width(&hmWidth); m_spIPicture->get_Height(&hmHeight); CSize sz(hmWidth,hmHeight); if (pDC==NULL) { CWindowDC dc(NULL); dc.HIMETRICtoDP(&sz); // convert to pixels } else { pDC->HIMETRICtoDP(&sz); } return sz; }
///////////////////////////////////// Picture view is a typical scroll view. #include "Doc.h" class CPictureView : public CScrollView { public: virtual ~CPictureView(); CPictureDoc* GetDocument() { return (CPictureDoc*)m_pDocument; } protected: BOOL m_rcImage; // rect to display image in UINT m_iHowScale; // how to scale image CPictureView(); void GetImageRect(CRect& rc); void SetScrollSizes(); virtual void OnDraw(CDC* pDC); // overridden to draw this view virtual void OnInitialUpdate(); // called first time after construct // command/message handlers afx_msg void OnViewScale(UINT nID); afx_msg void OnUpdateViewScale(CCmdUI* pCmdUI); afx_msg BOOL OnEraseBkgnd(CDC* pDC); afx_msg void OnSize(UINT nType, int cx, int cy); DECLARE_DYNCREATE(CPictureView) DECLARE_MESSAGE_MAP() };
////////////////////////////////////////////////////////////// CPictureView
#include "StdAfx.h" #include "View.h" #include "resource.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif IMPLEMENT_DYNCREATE(CPictureView, CScrollView)
BEGIN_MESSAGE_MAP(CPictureView, CScrollView) ON_WM_ERASEBKGND() ON_WM_SIZE() ON_COMMAND_RANGE(ID_VIEW_TOFIT, ID_VIEW100, OnViewScale) ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_TOFIT, ID_VIEW100, OnUpdateViewScale) END_MESSAGE_MAP()
CPictureView::CPictureView() { m_iHowScale = ID_VIEW_TOFIT; }
CPictureView::~CPictureView() {}
void CPictureView::OnInitialUpdate() { SetScrollSizes(); }
//////////////////// Set scroll sizes based on picture. Page size = client hieight/width; // line size = 1/10 of this. void CPictureView::SetScrollSizes() { CRect rcClient; GetClientRect(&rcClient); CRect rcImage; GetImageRect(rcImage); CSize szTotal = rcImage.Size(); CSize szPage = rcClient.Size(); CSize szLine = szPage; szLine.cx /= 10; szLine.cy /= 10; CScrollView::SetScrollSizes(MM_TEXT, szTotal, szPage, szLine); Invalidate(); }
//////////////////// View was sized: readjust scroll sizes if I'm in "zoom to fit" mode void CPictureView::OnSize(UINT nType, int cx, int cy) { CScrollView::OnSize(nType, cx, cy); if (m_iHowScale==ID_VIEW_TOFIT) { SetScrollSizes(); } }
////////////////// // Erase the background. This is required in case the image is smaller than // the client area, to paint the extra background. Use clipping to avoid flicker. BOOL CPictureView::OnEraseBkgnd(CDC* pDC) { CPictureDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // get client rectangle CRect rcClient; GetClientRect(&rcClient); CRect rc = rcClient; // get image rectangle CRect rcImage; GetImageRect(rcImage); rc = rcImage; CPoint pt = pDC->GetViewportOrg(); CSize sz = GetTotalSize(); // create clipping region CRgn clipRgn; clipRgn.CreateRectRgnIndirect(&rcClient); pDC->SelectClipRgn(&clipRgn); pDC->ExcludeClipRect(&rcImage); CBrush brush(RGB(0,0,0)); // black pDC->FillRect(&rcClient, &brush); pDC->SelectClipRgn(NULL); return TRUE; }
////////////////// // Draw the picture -- call CPicture to do it. void CPictureView::OnDraw(CDC* pDC) { CPictureDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); CPicture* ppic = pDoc->GetPicture(); ASSERT(ppic); if (*ppic) { CRect rc; GetImageRect(rc); ppic->Render(pDC,rc); } }
////////////////// // Get image rectangle, scaled for current zoom factor. void CPictureView::GetImageRect(CRect& rc) { CPictureDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); CPicture* ppic = pDoc->GetPicture(); ASSERT(ppic); if (!ppic || !*ppic) { rc.SetRect(0,0,0,0); } else if (m_iHowScale==ID_VIEW_TOFIT) { GetClientRect(&rc); } else { CSize sz = ppic->GetImageSize(); switch (m_iHowScale) { case ID_VIEW25: sz.cx >>= 2; sz.cy >>= 2; break; case ID_VIEW33: sz.cx /= 3; sz.cy /= 3; break; case ID_VIEW50: sz.cx >>= 1; sz.cy >>= 1; break; case ID_VIEW75: sz.cx = (sz.cx * 3)/4; sz.cy = (sz.cy * 3)/4; break; } rc.SetRect(0,0,sz.cx,sz.cy); } }
////////////////// // Handle zoom command. void CPictureView::OnViewScale(UINT nID) { if (m_iHowScale != nID) { m_iHowScale = nID; ScrollToPosition(CPoint(0,0)); OnInitialUpdate(); } }
//////////// Update zoom menu -- check the whichever zoom factor I'm at now. void CPictureView::OnUpdateViewScale(CCmdUI* pCmdUI) { pCmdUI->SetCheck(pCmdUI->m_nID == m_iHowScale); } |
四,、小結(jié) 本實(shí)例通過Cpicutre類現(xiàn)了JEPG圖像的顯示,,讀者可以將該類直接用在多媒體應(yīng)用程序的開發(fā)中。另外,,MFC提供了一個(gè)現(xiàn)成的類--CPictureHolder,,這個(gè)類的功能幾乎與CPicture完全一樣,讀者朋友可以在afxctl.h文件中找到它的定義,。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1783631