
在VB中,我可以通過創(chuàng)建一個(gè)圖像控件來顯示一個(gè)JPG或GIF文件,但是我如何在我的MFC應(yīng)用程序中顯示一個(gè)JGP文件呢?
許多讀者
好問題!有時(shí)使用VB的程序員覺得這個(gè)很容易。只要往你的表中拖入一個(gè)圖像控件,然后你就可以往下做了……然而C++程序員就不得不感到煩惱和頭疼。那我
們要做些什么呢,編寫我們自己的JPG解壓函數(shù)嗎?
當(dāng)然不是這樣的!事實(shí)上,C/C++程序員能夠使用與VB程序員所使用的非常類似(可以說是差不多)的圖像控件。我并沒有開玩笑。VB圖像控件
是基于一個(gè)叫"IPicture"的系統(tǒng)COM類(如
Figure 1 所示)。IPicture管理一個(gè)圖像對(duì)象和它的特性。圖像對(duì)象為位圖提供一個(gè)抽象化的東西。Windows提供
了一個(gè)知道如何處理BMP,JPG和GIF位圖的標(biāo)準(zhǔn)操作。你所要做的只是使IPicture實(shí)例化,并調(diào)用Render。你可以調(diào)用一個(gè)叫
做"OleLoadPicture"的特殊函數(shù),來替代通常所要調(diào)用的"CoCreateInstance"。
IStream* pstm = // 需要一個(gè)信息流
IPicture* pIPicture;
hr = OleLoadPicture(pstm, 0, FALSE,
IID_IPicture, (void**)&pIPicture);
OleLoadPicture從信息流里加載圖像,并創(chuàng)建一個(gè)你能夠用來顯示圖像的新的IPicture對(duì)象。
rc = // 要在其中顯示的矩形
// 轉(zhuǎn)換rc為HIMETRIC
spIPicture->Render(pDC, rc);
IPicture包攬了所有的令人厭煩的用來推算圖像是否是Windows位圖,JPEG,或GIF文件的事,
它甚至還可以推算圖像是否是圖標(biāo)和圖元文件!自然,其中的細(xì)節(jié)是需要些技巧,所以我就寫了一個(gè)將它們都包含其中的叫"ImgView"( 如
Figure 2 所示)的演示程序。

Figure 2 ImgView
ImgView是一個(gè)典型的MFC文檔/視圖結(jié)構(gòu)的應(yīng)用程序,它使用了一個(gè)我以前寫的叫"CPicture"的類(如
Figure 3 所示)來封裝IPicture。CPicture將一些麻煩的COM類型的參數(shù)映射為那些更容易被MFC程序員接
受的類型。例如,CPicture可以讓你直接從一個(gè)文件名加載圖像,如CFile或CArchive,而不是處理信息流;而且
CPicture::Render完成了所有的令人厭煩的而又是IPicture所需要的HIMETRIC坐標(biāo)轉(zhuǎn)換,這樣,你就沒必要去做這些了。
CPicture甚至還有一個(gè)可以從你的資源數(shù)據(jù)中加載圖像的加載函數(shù),所以要顯示一個(gè)資源圖像,你所要做的就是像下面那樣寫:
CPicture pic(ID_MYPIC); // 加載pic
CRect rc(0,0,0,0); // 使用默認(rèn) rc
pic.Render(pDC, rc); // 顯示它
什么能夠使工作變得更加容易呢?CPicture::Render能獲得一個(gè)你想在其中顯示圖像的矩形。IPicture可以適當(dāng)?shù)乩靾D像。
如果你傳遞了一個(gè)空的矩形,CPicture就使用圖像本來的尺寸,并不對(duì)其進(jìn)行拉伸。對(duì)于圖像本身,CPicture要尋找一個(gè)名為"IMAGE"的資
源類型,所以你必須對(duì)你的RC文件進(jìn)行如下的編寫:
IDR_MYPIC IMAGE MOVEABLE PURE "res\MyPic.jpg"
總的來說,CPicture相當(dāng)沒頭腦。它有一個(gè)ATL CComQIPtr巧妙的指向
IPicture界面的指針,其中不同的加載函數(shù)通過調(diào)用OleLoadPicture來初始化該界面。CPicture提供一般的封裝函數(shù)來調(diào)用里面的
IPicture。CPicture僅封裝了我編寫ImgView所需要的IPicture成員函數(shù);這么做是因?yàn)槲沂沁@樣的一個(gè)懶惰的程序員。如果你還
需要調(diào)用IPicture::get_Handle或一些其它的較少用的IPicture成員函數(shù),很抱歉,你就只好自己為其添加封裝了。至少,這代碼是
件瑣碎的事情。
順便說一下,在我寫完CPicture后,我就覺得有件事要提一下,那就是我發(fā)現(xiàn)一個(gè)鮮為人知的叫
做"CPictureHolder"的MFC類也做了絕大部分的類似的事情。你可以在afxctl.h中找到它。
正如我先前提到的,ImgView是一個(gè)典型的MFC文檔/視圖結(jié)構(gòu)的應(yīng)用程序,其中
CPictureDoc和CPictureView類分別對(duì)應(yīng)于文檔和視圖結(jié)構(gòu)。如
Figure 4 所示 中顯示了該視圖。CPictureDoc有些瑣碎;它使用CPicture來保存圖像--
class CPictureDoc : public CDocument {
protected:
CPicture m_pict; // 圖像
};
,并且CPictureDoc::Serialize調(diào)用CPicture: oad從MFC所建立的存檔中讀取圖像。
void CPictureDoc::Serialize(CArchive& ar)
{
if (ar.IsLoading()) {
m_pict.Load(ar);
}
}
僅僅是為了有趣,CPictureDoc::OnNewDocument從程序的資源數(shù)據(jù)中加載了一張漂亮的NASA圖像。為了顯示這圖
像,CPictureView::OnDraw調(diào)用了CPicture::Render。
void CPictureView::OnDraw(CDC* pDC)
{
CPictureDoc* pDoc = GetDocument();
CPicture* ppic = pDoc->GetPicture();
CRect rc;
GetImageRect(rc);
ppic->Render(pDC,rc);
}
GetImageRect是CPictureView的一個(gè)函數(shù),它依靠當(dāng)前ImgView縮放比例而返回一個(gè)適當(dāng)?shù)膱D像矩形。(ImgView可以
通過25%,33%,50%,75%,100%或"自適應(yīng)比例"這六種比例形式來顯示圖像)。GetImageRect調(diào)用
CPicture::GetImageSize獲得真實(shí)的圖像尺寸,隨后依據(jù)比例適當(dāng)?shù)乜s放。
現(xiàn)在,在CPictureView中剩下的就是典型的CScrollView部分,其中有用于視的初始化和
滾動(dòng)條尺寸與句柄命令的設(shè)置之類的代碼。對(duì)于IPicture唯一有意思的是,正如之前我所提到的,IPicture::Render希望它的坐標(biāo)是
HIMETRIC單位的,然而一般的MFC應(yīng)用程序使用的是默認(rèn)的MM_TEXT映射模式。不要擔(dān)心,CPicture::Render和
CPicture::GetImageSize對(duì)其做了魔術(shù)般的轉(zhuǎn)換,所以你就沒必要為這樣世俗的和令人厭煩的瑣事而操過多的心了。
CPictureView有一個(gè)消息處理函數(shù)值得注意,它就是OnEraseBkgnd。它被要求在圖像比
視的客戶區(qū)小的情況時(shí)對(duì)空白的區(qū)域進(jìn)行填充(如圖5所示)。OnEraseBkgnd創(chuàng)建一個(gè)與圖像大小一樣的剪切的矩形,然后將客戶矩形填充為黑色。當(dāng)
你變化窗口尺寸的時(shí)候,這樣的剪切就避免了閃爍,其中的FillRect并沒有往被剪切的矩形中填充。這是標(biāo)準(zhǔn)的Windows圖形101。

圖 5 OnEraseBkgnd填充被剪切的圖像
IPicture/CPicture真正使得顯示圖像變得容易了。它甚至可以完成調(diào)色板的實(shí)現(xiàn)和所有令人厭煩的事情。你可以丟掉原先所有的用來加載調(diào)
色板,BitBlts和StretchBlts等的DIB的繪圖代碼了,IPicture是個(gè)很好的辦法。如果你還沒有使用IPicture來顯示圖像,
那么現(xiàn)在就開始用它吧!
所有的事情都是這么的簡(jiǎn)單,這讓我想寫另一個(gè)類來試試。當(dāng)你想寫一個(gè)圖像瀏覽器
時(shí),CPictureView是很好用的,但要是你想將一個(gè)圖像加到對(duì)話框或其它的一些窗口上,那該怎么辦呢?為了實(shí)現(xiàn)這,我寫了另一個(gè)
類,CPictureCtrl(如
Figure 6 所示)。CPictureCtrl可以讓你將一個(gè)圖像作為一個(gè)子控件放在任何的對(duì)話框或窗口上。例如:
class CAboutDialog : public CDialog {
protected:
CPictureCtrl m_wndPict;
virtual BOOL OnInitDialog();
};
BOOL CAboutDialog::OnInitDialog()
{
m_wndPict.SubclassDlgItem(IDC_MYIMAGE,this);
return CDialog::OnInitDialog();
}
這里假設(shè)在你的對(duì)話框中有一個(gè)靜態(tài)控件,它的ID是IDC_IMAGE,同時(shí)還有一個(gè)具有相同ID的IMAGE資源。我從我那很常用的
CStaticLink中派生了CPictureCtrl,這樣,如果你想的話就可以聲明一個(gè)URL超鏈接了(或僅僅創(chuàng)建一個(gè)與控件和圖像具有相同ID的
字符串資源)。如果你聲明了一個(gè)URL,在這圖像上點(diǎn)擊鼠標(biāo)將啟動(dòng)你的瀏覽器并實(shí)現(xiàn)這個(gè)鏈接。令人驚奇的是,CPicture保存了一個(gè)CPicture
對(duì)象,并通過重載WM_PAINT來調(diào)用CPicture::Render,而不是通過一般的靜態(tài)控件。要想了解更多的細(xì)節(jié),可從本文開始處的鏈接下載源
文件,使用它吧,我祝福你!
使用 cppqa@microsoft.com
發(fā)送你的問題和評(píng)論給 Paul
|