




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、第10章 GDI編程3-動(dòng)畫動(dòng)畫是利用人的視覺滯留缺陷 (25ms400ms)和心理認(rèn)可來動(dòng)態(tài)生成系列相關(guān)畫面以產(chǎn)生運(yùn)動(dòng)視覺的技術(shù)。位圖動(dòng)畫是將預(yù)先制作好的一系列表示連續(xù)畫面的位圖,按一定的時(shí)間間隔一幅接一幅地連續(xù)顯示,從而產(chǎn)生動(dòng)畫效果。因?yàn)槔L制動(dòng)畫所需的圖形,以及拍攝和處理圖片,需要美術(shù)、攝影、數(shù)字圖像處理、動(dòng)畫設(shè)計(jì)等知識(shí),我們這里不講。本書只介紹如何顯示已有的位圖(序列)以產(chǎn)生動(dòng)畫效果,以及如何動(dòng)態(tài)繪制不同的簡單圖形以產(chǎn)生二維圖形動(dòng)畫等。用GDI編程實(shí)現(xiàn)動(dòng)畫,一般需要用到計(jì)時(shí)器(Timer)操作,通常在計(jì)時(shí)器響應(yīng)函數(shù)OnTimer中(而不要使用OnDraw)繪圖來實(shí)現(xiàn)動(dòng)畫。10.1 固定位
2、圖動(dòng)畫本節(jié)介紹利用一系列的位圖資源,在同一個(gè)屏幕位置,接連顯示位圖序列,以達(dá)到動(dòng)畫的效果的具體方法。為此,可在交互繪圖程序中添加一個(gè)如圖10-1所示的位圖動(dòng)畫對(duì)話框,并添加對(duì)應(yīng)的對(duì)話框類CDukeDlg。也可以創(chuàng)建一個(gè)基于對(duì)話框的獨(dú)立的MFC應(yīng)用程序。圖10-1 位圖動(dòng)畫對(duì)話框資源當(dāng)然還需添加相應(yīng)的“位圖動(dòng)畫”菜單項(xiàng)(ID_DUKE)和(為視圖類添加)對(duì)應(yīng)的菜單響應(yīng)函數(shù),并在該函數(shù)中創(chuàng)建對(duì)話框類的對(duì)象,打開對(duì)話框來運(yùn)行動(dòng)畫:#include "DukeDlg.h"void CDrawView:OnDuke() CDukeDlg dlg;dlg.DoModal();10.1.
3、1 準(zhǔn)備位圖、加入位圖資源系列公爵(Duke)BMP文件T1.BMP T10.BMP(見圖10-2),來自Java吉祥物的GIF動(dòng)畫文件,可存放在項(xiàng)目的res子目錄的Duke子目錄中(該位圖資源已經(jīng)打包成Duke.rar文件后,放到了系里的網(wǎng)站和我的個(gè)人網(wǎng)頁上)。 圖10-2 Duke位圖文件用VC的資源編輯器依次加入位圖文件:在左邊的項(xiàng)目工作區(qū)中選“資源視圖”頁,展開項(xiàng)目的資源列表,在“Bitmap”表項(xiàng)(若無此項(xiàng),則可直接在項(xiàng)目資源項(xiàng))上單擊鼠標(biāo)右鍵,在彈出的浮動(dòng)菜單中選“添加資源”菜單項(xiàng),在打開的“添加資源”對(duì)話框中,選中左邊“資源類型”欄中的“Bitmap”表項(xiàng),單擊右邊的“導(dǎo)入”按鈕
4、,在彈出的“導(dǎo)入”“打開”鈕,則會(huì)自動(dòng)加入ID為IDB_BITMAPi的位圖資源。為了以后循環(huán)編程的方便,必須保證是從T1.BMP到T10.BMP順序依次加入。為了確認(rèn),可打開頭文件Resource.h查看,若其中的常量IDB_BITMAPi的定義數(shù)值不連續(xù),可作一些手工修改使其連續(xù),例如:#define IDB_BITMAP1 131#define IDB_BITMAP2 132#define IDB_BITMAP3 133#define IDB_BITMAP4 134#define IDB_BITMAP5 135#define IDB_BITMAP6 136#define IDB_BITM
5、AP7 137#define IDB_BITMAP8 138#define IDB_BITMAP9 139#define IDB_BITMAP10 14010.1.2 添加控件、創(chuàng)建對(duì)話框類為對(duì)話框資源添加圖片控件:打開對(duì)話框資源,在控件工具箱中選圖片控件(Picture Control)工具,在對(duì)話框的適當(dāng)位置添加圖片控件,設(shè)置其“ID”屬性值為“IDC_ANI”,修改“Type”屬性為(在其下拉式列表框中選中)“Bitmap”,再在“Image”屬性的下拉式列表框中選中“IDB_BITMAP1”位圖資源,則該位圖繪顯示在圖片控件中。為了控制動(dòng)畫的播放,需要添加一個(gè)即可表示開始動(dòng)畫又可表示停
6、止動(dòng)畫的按鈕(初始標(biāo)題為“開始動(dòng)畫”),可設(shè)置其ID為“IDC_ANI_START_STOP”。為了讓用戶選擇動(dòng)畫的速度,可以添加靜態(tài)文本提示框“每秒幀數(shù):”和文本編輯框(IDC_N),在后面的小節(jié)中還會(huì)添加滑塊控件(Slider Control,IDC_SLIDER_N)。創(chuàng)建該對(duì)話框資源所對(duì)應(yīng)的對(duì)話框類CDukeDlg。10.1.3 添加類變量、裝入與刪除位圖在對(duì)話框類的定義(頭文件)中添加若干類變量:CBitmap *m_pBmp10; / 位圖指針數(shù)組BITMAP m_bs; / 位圖結(jié)構(gòu)變量bool m_bStarted; / 判別動(dòng)畫是否啟動(dòng)(初始化為false)int m_nCu
7、rFrame, / 當(dāng)前幀號(hào)(初值為0) m_nFramesPerSecond; / 每秒幀數(shù)(初值為10)為CDukeDlg類添加(重寫型)消息響應(yīng)函數(shù)OnInitDialog,在該函數(shù)中(也可以在構(gòu)造函數(shù)中)創(chuàng)建位圖對(duì)象并裝入位圖資源,然后獲取位圖結(jié)構(gòu)(其中的位圖寬和高用于BitBlt函數(shù)),并初始化其他類變量,最后設(shè)置編輯控件的初值(粗體部分為新加的):BOOL CDukeDlg:OnInitDialog()CDialog:OnInitDialog();/ TODO: 在此添加額外的初始化for (int i = 0; i < 10; i+) / 裝入位圖資源m_pBmpi = n
8、ew CBitmap;m_pBmpi->LoadBitmap(IDB_BITMAP1 + i);m_pBmp0->GetBitmap(&m_bs); /獲取位圖結(jié)構(gòu)m_bStarted = false; / 設(shè)置已開始動(dòng)畫為假m_nCurFrame = 0; / 設(shè)置初始的當(dāng)前幀為0m_nFramesPerSecond = 10; / 設(shè)置初始動(dòng)畫速度為每秒10幀SetDlgItemInt(IDC_N, m_nFramesPerSecond); / 設(shè)置編輯框初值return TRUE; / return TRUE unless you set the focus to a
9、 control/ 異常: OCX 屬性頁應(yīng)返回 FALSE其中,語句m_pBmpi->LoadBitmap(IDB_BITMAP1 + i);中的IDB_BITMAP1 + i用到了Duke位圖資源ID的連續(xù)性。為了避免內(nèi)存泄漏,還需要為對(duì)話框類添加析構(gòu)函數(shù),并在該函數(shù)中刪除位圖對(duì)象:CDukeDlg:CDukeDlg()for (int i = 0; i < 10; i+) delete m_pBmpi;10.1.4 啟動(dòng)/停止動(dòng)畫(設(shè)置/刪除計(jì)時(shí)器)為按鈕IDC_ANI_START_STOP添加單擊消息響應(yīng)函數(shù):void CDukeDlg:OnBnClickedAniStar
10、tstop() / TODO: 在此添加控件通知處理程序代碼if (m_bStarted) / 已開始動(dòng)畫m_bStarted = false; / 設(shè)置已開始動(dòng)畫為假KillTimer(1); / 取消計(jì)時(shí)器/ 設(shè)置按鈕文本為“開始動(dòng)畫”SetDlgItemText(IDC_ANI_START_STOP, L"開始動(dòng)畫");else / 未開始動(dòng)畫m_bStarted = true; / 設(shè)置已開始動(dòng)畫為真m_nCurFrame = 0; / / 設(shè)置當(dāng)前幀為0/ 獲取編輯框中的幀速值m_nFramesPerSecond = GetDlgItemInt(IDC_N);if
11、 (m_nFramesPerSecond <= 0) / 限制最小幀速值為1m_nFramesPerSecond = 1;else if (m_nFramesPerSecond > 100) / 最大幀速值為100m_nFramesPerSecond = 100;/ 用調(diào)整后幀速值重新設(shè)置編輯框的內(nèi)容SetDlgItemInt(IDC_N, m_nFramesPerSecond);/ 利用幀速值來設(shè)置計(jì)時(shí)器SetTimerm_nFramesPerSecond + 0.5), NULL);/ 設(shè)置按鈕文本為“停止動(dòng)畫”SetDlgItemText(IDC_ANI_START_STOP
12、, L"停止動(dòng)畫");其中:l m_bStarted為布爾型類變量,用于判斷動(dòng)畫是否已經(jīng)開始播放。在構(gòu)造函數(shù)中初始化為假,在用戶按了開始/停止動(dòng)畫按鈕后取反。l m_nCurFrame為整數(shù)型類變量,用于記錄當(dāng)前所要顯示的位圖序號(hào),初始化為0。l m_nFramesPerSecond也為整數(shù)型類變量,用于記錄當(dāng)前的每秒幀數(shù)(幀速值,范圍可設(shè)為1100)。l SetDlgItemText函數(shù),用于動(dòng)態(tài)修改按鈕上的文本。l SetTimer用于設(shè)置計(jì)時(shí)器,它是CWnd的成員函數(shù)(所以也可在其派生的視圖類和對(duì)話框類中使用),其函數(shù)原型為:UINT SetTimer( UINT n
13、IDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD) );n nIDEvent為此計(jì)時(shí)器的編號(hào)。因?yàn)橐粋€(gè)應(yīng)用程序可以設(shè)置多個(gè)計(jì)時(shí)器,為了在響應(yīng)時(shí)區(qū)分它們,必須各有一個(gè)編號(hào)。簡單程序的計(jì)時(shí)器一般只有一個(gè),所以取nIDEvent = 1即可。n nElapse為間隔時(shí)間,單位為毫秒(1/1000秒)??捎帽磉_(dá)式(UINT) / m_nFramesPerSecond + 0.5),從每秒幀數(shù)計(jì)算出間隔時(shí)間的毫秒數(shù)。n lpfnTimer為應(yīng)用程序提供的處理WM_TIMER消息的回調(diào)函數(shù),一般
14、取為NULL,這時(shí)WM_TIMER消息由CWnd派生類的對(duì)應(yīng)消息響應(yīng)函數(shù)來處理。l KillTimer用于刪除計(jì)時(shí)器,它也是CWnd的成員函數(shù),其函數(shù)原型為:BOOL KillTimer( int nIDEvent );在設(shè)置了計(jì)時(shí)器后,系統(tǒng)會(huì)按指定的時(shí)間間隔發(fā)送WM_TIMER消息給應(yīng)用程序窗口,程序員可在計(jì)時(shí)器消息響應(yīng)函數(shù)OnTimer中作需要的處理,在本程序中是顯示當(dāng)前位圖。10.1.5 繪制動(dòng)畫(響應(yīng)計(jì)時(shí)器消息)為CDukeDlg類的WM_TIMER消息添加響應(yīng)函數(shù):void CDukeDlg:OnTimer(UINT nIDEvent) / TODO: 在此添加消息處理程序代碼和/或
15、調(diào)用默認(rèn)值CDC *pDC = GetDlgItem(IDC_ANI)->GetDC(); / 獲取圖片框DCCDC dc; / 定義內(nèi)存DCdc.CreateCompatibleDC(pDC); / 創(chuàng)建兼容DCdc.SelectObject(m_pBmpm_nCurFrame); / 選入當(dāng)前幀位圖pDC->BitBlt(0, 0, m_bs.bmWidth, m_bs.bmHeight, &dc, 0, 0, SRCCOPY); / 顯示當(dāng)前幀位圖m_nCurFrame+; / 當(dāng)前幀加一(設(shè)置下一次要顯示的位圖序號(hào))m_nCurFrame %= 10; / 當(dāng)前幀余
16、10(避免超出位圖數(shù)組,實(shí)現(xiàn)循環(huán))CDialog:OnTimer(nIDEvent);結(jié)果如圖10-3所示。圖10-3 Duke動(dòng)畫10.1.6 滑塊控件的使用為了使用戶能夠利用鼠標(biāo)快速修改每秒幀數(shù)的值,我們?cè)趯?duì)話框中添加了一個(gè)滑塊控件(Slider Control)(可將其ID值設(shè)為“IDC_SLIDER_N”)。對(duì)應(yīng)的MFC類為CSliderCtrl,它是直接從CWnd派生的類:CObjectCCmdTargetCWndCSliderCtrl。可以在對(duì)話框類中定義一個(gè)CSliderCtrl的類指針變量:CSliderCtrl *m_pSlider;然后在對(duì)話框類的初始化函數(shù)中獲得滑塊控件對(duì)
17、象的指針,并設(shè)置其取值范圍為1100,再設(shè)置其滑塊的當(dāng)前位置(注意:粗體代碼必需位于SetDlgItemInt之前):BOOL CDukeDlg:OnInitDialog() CDialog:OnInitDialog();/ TODO: 在此添加額外的初始化m_pSlider = (CSliderCtrl *)GetDlgItem(IDC_SLIDER_N);m_pSlider->SetRange(1, 100);m_pSlider->SetPos(m_nFramesPerSecond);SetDlgItemInt(IDC_N, m_nFramesPerSecond);為了能在用戶
18、移動(dòng)滑塊時(shí),動(dòng)態(tài)修改編輯控件中的值,需要為對(duì)話框類添加水平滾動(dòng)消息(WM_HSCROLL)的響應(yīng)函數(shù)OnHScroll:void CDukeDlg:OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) / TODO: 在此添加消息處理程序代碼和/或調(diào)用默認(rèn)值SetDlgItemInt(IDC_N, m_pSlider->GetPos();CDialog:OnHScroll(nSBCode, nPos, pScrollBar);同樣,為了在用戶修改編輯控件中的值時(shí),動(dòng)態(tài)改變滑塊的位置,還需要為對(duì)話框類添加編輯控件的EN_CHA
19、NGE消息響應(yīng)函數(shù):void CDukeDlg:OnEnChangeN() / TODO: 在此添加控件通知處理程序代碼m_pSlider->SetPos(GetDlgItemInt(IDC_N);注意,如果滑塊初始化的代碼位于SetDlgItemInt之后,則會(huì)造成此語句中的m_pSlider指針無效。 CImageList類上面的位圖動(dòng)畫,也可以使用MFC的圖像列表類CImageList來實(shí)現(xiàn),代碼會(huì)更簡單一些。CImageList類是CObject的直接派生類:CObject CImageList1成員函數(shù)CImageList類的常用成員函數(shù)有:l 構(gòu)造函數(shù):CImageList(
20、 );l 創(chuàng)建函數(shù):BOOL Create( int cx, int cy, UINT nFlags, int nInitial, int nGrow );l 添加函數(shù):int Add( CBitmap* pbmImage, COLORREF crMask );l 繪制函數(shù):BOOL Draw( CDC* pDC, int nImage, POINT pt, UINT nStyle );2例子可在動(dòng)畫對(duì)話框類中添加如下代碼:/ 類變量(頭文件)bool m_bStarted;int m_nCurFrame, m_nFramesPerSecond;CSliderCtrl *m_pSlider;C
21、ImageList imgList;/ 初始化(OnInitDialog函數(shù))m_bStarted = false;m_nCurFrame = 0;m_nFramesPerSecond = 10; / 設(shè)置初始動(dòng)畫速度為每秒10幀m_pSlider = (CSliderCtrl *)GetDlgItem(IDC_SLIDER_N);m_pSlider->SetRange(1, 100);m_pSlider->SetPos(m_nFramesPerSecond);SetDlgItemInt(IDC_N, m_nFramesPerSecond); BITMAP bs;CBitmap b
22、mp;bmp.LoadBitmap(IDB_BITMAP1);bmp.GetBitmap(&bs);imgList.Create(bs.bmWidth, bs.bmHeight, ILC_COLOR8, 10, 0);(&bmp, RGB(0, 0, 0);for (int i = 1; i < 10; i+) bmp.DeleteObject();bmp.LoadBitmap(IDB_BITMAP1 + i);imgList.Add(&bmp, RGB(0, 0, 0);bmp.DeleteObject();/ 繪圖(OnTimer函數(shù))CDC *pDC = G
23、etDlgItem(IDC_ANI)->GetDC();imgList.Draw(pDC, m_nCurFrame, CPoint(0,0), ILD_NORMAL);m_nCurFrame+; m_nCurFrame %= 10;/ OnBnClickedAniStartStop、OnHScroll和OnEnChangeN函數(shù)同前 過程框圖下面分別給出使用位圖數(shù)組和CImageList類來實(shí)現(xiàn)固定位圖動(dòng)畫的主要過程框圖。1使用位圖數(shù)組圖10-4是使用位圖數(shù)組實(shí)現(xiàn)固定位圖動(dòng)畫的主要過程框圖。獲取位圖結(jié)構(gòu)GetBitmap裝入位圖資源LoadBitmap創(chuàng)建位圖對(duì)象CBitmap獲取DCG
24、etDC顯示當(dāng)前位圖BitBlt獲取控件窗口對(duì)象GetDlgItem響應(yīng)計(jì)時(shí)器消息OnTimer添加位圖資源定義位圖結(jié)構(gòu)BITMAP設(shè)置計(jì)時(shí)器SetTimer定義位圖對(duì)象指針數(shù)組CBitmap * 更新當(dāng)前位圖序號(hào)定義內(nèi)存DCCDC創(chuàng)建兼容DCCreateCompatibleDC選入位圖對(duì)象SelectObject圖10-4 使用位圖數(shù)組實(shí)現(xiàn)固定位圖動(dòng)畫的主要步驟2使用圖像列表圖10-5是使用CImageList類實(shí)現(xiàn)固定位圖動(dòng)畫的主要過程框圖。獲取位圖結(jié)構(gòu)GetBitmap裝入位圖資源LoadBitmap定義位圖對(duì)象CBitmap創(chuàng)建圖像列表Create加入位圖對(duì)象Add獲取DCGetDC繪
25、制當(dāng)前位圖Draw獲取控件窗口對(duì)象GetDlgItem響應(yīng)計(jì)時(shí)器消息OnTimer添加位圖資源定義位圖結(jié)構(gòu)BITMAP定義圖像列表對(duì)象CImageList設(shè)置計(jì)時(shí)器SetTimer更新當(dāng)前位圖序號(hào)圖10-5 使用CImageList類實(shí)現(xiàn)固定位圖動(dòng)畫的主要步驟10.2 圖形動(dòng)畫在前面(參見小節(jié))利用鼠標(biāo)進(jìn)行交互繪圖時(shí),我們就已經(jīng)實(shí)現(xiàn)了簡單的圖形動(dòng)畫動(dòng)態(tài)畫直線、矩形或橢圓等,用戶可通過鼠標(biāo)對(duì)繪圖位置坐標(biāo)和圖形大小進(jìn)行交互式選擇。具體做法是,用灰色點(diǎn)線筆在同一個(gè)位置異或畫兩次一樣的圖形第一次畫圖,第二次擦除??焖俨粩嗟卦诓煌牡胤疆嫴粒瓦_(dá)到了動(dòng)畫的效果。即圖形動(dòng)畫的原理就是邊擦邊畫。如果要畫的不
26、再是簡單的線狀圖形,而是復(fù)雜的面狀圖,則異或畫圖方法就不再有效。因?yàn)楫惢驎?huì)大大改變窗口中原有圖形的色彩,這是用戶所不能容忍的。可用的解決方法是,擦除(或保存)要繪圖的區(qū)域,然后再繪制新圖形(并恢復(fù)原區(qū)域的圖形)。具體的實(shí)現(xiàn)方法有兩種直接繪圖和緩沖繪圖。10.2.1 直接繪圖利用直接繪圖方法,來產(chǎn)生動(dòng)畫的原理很簡單,但是會(huì)存在討厭的閃爍現(xiàn)象。1原理通過不斷地擦除(要繪圖的區(qū)域)和繪制(新圖形)動(dòng)態(tài)圖形而產(chǎn)生動(dòng)畫效果??梢允褂肅DC類的畫填充矩形的函數(shù)(使用白色刷):void FillRect(LPCRECT lpRect, CBrush* pBrush);來擦除指定矩形區(qū)域。例如:pDC->
27、;FillRect(&rect, new CBrush(RGB(255, 255, 255);然后,再在該矩形區(qū)域內(nèi)繪制新圖形。例如:pDC->SelectObject(&pen); / 選入畫邊框的筆pDC->SelectObject(&brush); / 選入畫填充色的刷pDC->Ellipse(&rect); / 繪制填充橢圓2例子下面是一個(gè)在白色背景上動(dòng)態(tài)畫伸縮填充橢圓的例子,需要?jiǎng)?chuàng)建一個(gè)傳統(tǒng)單文檔MFC應(yīng)用程序。主要代碼片段如下:1)在視圖類中定義若干類變量:bool shrink; / 用于判斷伸縮int r, w, h, / 當(dāng)前
28、橢圓的短軸半徑和寬高 R, W, H, / 最大橢圓的短軸半徑和寬高 xc, yc; / 橢圓的中心坐標(biāo)CPen pen; / 繪制橢圓邊框的筆(與刷同色)CBrush brush, whiteBrush; / 繪制橢圓內(nèi)部的刷和刪除原橢圓的白刷2)在視圖類的構(gòu)造函數(shù)中,設(shè)置初值、構(gòu)造筆和刷:shrink = true; / 初始為縮小COLORREF greenCol = RGB(0, 150, 0), /定義綠色whiteCol = RGB(255, 255, 255); /定義白色pen.CreatePen(0, 0, greenCol ); / 實(shí)心單像素寬的綠色筆brush.Crea
29、teSolidBrush(greenCol ); / 實(shí)心綠色刷whiteBrush.CreateSolidBrush(whiteCol); / 實(shí)心白色刷3)在某個(gè)菜單項(xiàng)的事件處理函數(shù)中計(jì)算并設(shè)置初值、啟動(dòng)計(jì)時(shí)器:CRect rect; GetClientRect(&rect); / 獲取當(dāng)前客戶區(qū)矩形W = rect.Width(); H = rect.Height();r = R = min(W, H) / 2; / 初始為最大橢圓w = W / 2; h = H / 2;xc = W / 2; yc = H /2;SetTimer(1, 10, NULL); / 可設(shè)置不同的時(shí)
30、間間隔,或者讓用戶來設(shè)置4)在計(jì)時(shí)器的消息響應(yīng)函數(shù)OnTimer中,擦除并繪制橢圓,調(diào)整半徑:CDC *pDC = GetDC();/ 擦除if (shrink) / 對(duì)膨脹不需要擦除CRect rect(xc - w, yc - h, xc + w, yc + h);pDC->FillRect(rect, &whiteBrush);/ 調(diào)整半徑if (shrink) w-; h-; r-; / 縮小1像素if (r = 0) shrink = false; / 轉(zhuǎn)換成放大 else w+; h+; r+; / 放大1像素if (r = R) shrink = true; / 轉(zhuǎn)
31、換成縮小/ 繪制填充橢圓pDC->SelectObject(&pen);pDC->SelectObject(&brush);pDC->Ellipse(xc - w, yc - h, xc + w, yc + h);ReleaseDC(pDC);運(yùn)行結(jié)果如圖10-6所示。 圖10-6 伸縮填充橢圓運(yùn)行該程序后會(huì)發(fā)現(xiàn),存在明顯的閃爍現(xiàn)象,這主要是由收縮時(shí)的擦除操作所造成的。解決辦法是,采用內(nèi)存DC進(jìn)行緩沖繪圖。10.2.2 緩沖繪圖在前面資源位圖動(dòng)畫的繪制過程中,我們已經(jīng)采用了緩沖(buffering)方法來顯示位圖:CDC *pDC = GetDC();CDC
32、dc;dc.CreateCompatibleDC(pDC);dc.SelectObject(m_pBmpm_nCurFrame);pDC->BitBlt(0, 0, bs.bmWidth, bs.bmHeight, &dc, 0, 0, SRCCOPY); 其中,起主要作用的是內(nèi)存DC和CDC類的位塊傳送函數(shù)BitBlt,而且在該位圖動(dòng)畫中并沒有出現(xiàn)閃爍現(xiàn)象。1原理下面我們將這種方法加以擴(kuò)展,不僅使其可用于已有位圖的繪制,還可以用于普通圖形的動(dòng)態(tài)繪制。這需要先創(chuàng)建一個(gè)與當(dāng)前視圖DC兼容的空位圖對(duì)象,并將其作為畫布選入內(nèi)存DC中,然后對(duì)該內(nèi)存DC進(jìn)行各種圖形繪制,最后再用同樣的Bi
33、tBlt函數(shù)將繪圖結(jié)果傳送到屏幕上。具體步驟為:l 獲取當(dāng)前視圖DC采用視圖類CView基類CWnd的成員函數(shù):CDC* GetDC( );l 創(chuàng)建與當(dāng)前視圖DC兼容的位圖對(duì)象分兩步進(jìn)行,先用CBitmap類的缺省構(gòu)造函數(shù):CBitmap( ); 構(gòu)造空位圖對(duì)象,再利用成員函數(shù):BOOL CreateCompatibleBitmap(CDC* pDC, int nWidth, int nHeight);創(chuàng)建指定寬高(一般為當(dāng)前客戶區(qū)大?。┑呐c當(dāng)前DC兼容的位圖對(duì)象。l 創(chuàng)建與當(dāng)前視圖DC兼容的內(nèi)存DC也分兩步進(jìn)行,也是先用CDC類的缺省構(gòu)造函數(shù):CDC( ); 構(gòu)造空DC對(duì)象,再利用成員函數(shù):
34、BOOL CreateCompatibleDC(CDC* pDC);創(chuàng)建與當(dāng)前DC兼容的DC對(duì)象。l 選位圖對(duì)象入內(nèi)存DC利用CDC類的成員函數(shù):CBitmap* SelectObject(CBitmap* pBitmap);將所創(chuàng)建的空白位圖對(duì)象選入內(nèi)存DC中。l 內(nèi)存DC繪圖用該內(nèi)存DC,調(diào)用各種CDC的繪圖成員函數(shù),在內(nèi)存DC中的位圖上進(jìn)行繪圖。包括繪制白色客戶區(qū)矩形,進(jìn)行白色背景設(shè)置(缺省為黑色背景)。l 繪制屏幕利用CDC類的位塊傳送函數(shù):BOOL BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC,int xSrc,
35、 int ySrc, DWORD dwRop );將內(nèi)存DC中,已經(jīng)被繪制好圖形的位圖對(duì)象,傳送到屏幕上。其中,與前面顯示位圖資源的方法最大的不同是,需要先創(chuàng)建一個(gè)與當(dāng)前視圖DC兼容的空位圖對(duì)象,并將其作為畫布選入內(nèi)存DC中,然后才能對(duì)該內(nèi)存DC進(jìn)行各種圖形繪制。注意,在沒有選入位圖對(duì)象的內(nèi)存DC中直接繪圖是無效的。2例子1)簡單的例子:CDC memDC; / 內(nèi)存DCmemDC.CreateCompatibleDC(pDC); / 創(chuàng)建與當(dāng)前視圖DC兼容的DCCRect rect; / 矩形對(duì)象,用于表示客戶區(qū)GetClientRect(&rect); / 獲取客戶區(qū)矩形CBitm
36、ap bmp; / 位圖對(duì)象,作為畫布,用于創(chuàng)建可傳送的兼容DC/ 創(chuàng)建大小與客戶區(qū)一致并且與視圖DC兼容的位圖對(duì)象bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height();/ 將位圖選入內(nèi)存DC,使其可以進(jìn)行各種繪圖操作,并能夠傳遞到屏幕CBitmap *pOldBmp = memDC.SelectObject(&bmp);/ 用白色填充背景(缺省為黑色)CBrush brush(RGB(255, 255, 255);memDC.FillRect(&rect, &brush);/ 繪制各種圖形memDC.R
37、ectangle(10, 10, 200, 150);/ / 將內(nèi)存DC中的圖形傳送到屏幕(在視圖客戶區(qū)繪圖)pDC->BitBlt(0, 0, w, h, &memDC, 0, 0, SRCCOPY); 2)伸縮填充橢圓例現(xiàn)在仍然以伸縮填充橢圓為例,說明緩沖繪圖產(chǎn)生動(dòng)畫的方法。程序的結(jié)構(gòu)同1)中的例,下面主要介紹不同之處(可以在原程序的基礎(chǔ)上進(jìn)行修改)。(1)在視圖類中增加若干類變量定義:CDC memDC; / 空內(nèi)存DC對(duì)象CBitmap bmp, *pOldBmp; / 空位圖對(duì)象和老位圖對(duì)象指針bool created; / 是否創(chuàng)建了內(nèi)存DC和位圖對(duì)象(2)在視圖類的
38、構(gòu)造函數(shù)中,設(shè)置初值:created = false; / 初始為未創(chuàng)建(3)在某個(gè)菜單項(xiàng)的事件處理函數(shù)(也可以在OnSize信息響應(yīng)函數(shù))中,對(duì)內(nèi)存DC和位圖對(duì)象進(jìn)行初始化:CDC *pDC = GetDC(); if (!created) memDC.CreateCompatibleDC(pDC);/ created為真時(shí),需先刪除原位圖對(duì)象,才能創(chuàng)建新位圖對(duì)象if (created) memDC.SelectObject(pOldBmp);bmp.DeleteObject();bmp.CreateCompatibleBitmap(pDC, W, H); / W和H可能已經(jīng)變化pOldBm
39、p = memDC.SelectObject(&bmp);memDC.FillRect(&rect, &whiteBrush);created = true; shrink = true;xc = W / 2; yc = H /2;SetTimer(1, 10, NULL); / 可以設(shè)置不同的時(shí)間間隔(4)在計(jì)時(shí)器的消息響應(yīng)函數(shù)OnTimer中,擦除并繪制橢圓,調(diào)整半徑:void CAniView:OnTimer(UINT_PTR nIDEvent)/ TODO: 在此添加消息處理程序代碼和/或調(diào)用默認(rèn)值CDC *pDC = GetDC();/ 擦除原橢圓(用白色填充
40、客戶區(qū))if (shrink) / 對(duì)膨脹不需要擦除/ 擦除原橢圓(用白色填充客戶區(qū))CRect rect;GetClientRect(&rect); / 獲取當(dāng)前客戶區(qū)矩形CBrush brush(RGB(255, 255, 255);memDC.FillRect(&rect, &brush);/ 調(diào)整半徑/ 繪制新橢圓memDC.SelectObject(&pen);memDC.SelectObject(&brush);memDC.Ellipse(xc - w, yc - h, xc + w, yc + h);/ 將內(nèi)存DC中的圖形傳送到屏幕(在視圖
41、客戶區(qū)繪圖)pDC->BitBlt(0, 0, W, H, &memDC, 0, 0, SRCCOPY);ReleaseDC(pDC);CView:OnTimer(nIDEvent);(5)可以添加一個(gè)停止動(dòng)畫的菜單項(xiàng)(ID_KT)及其事件處理函數(shù):void CAniView:OnKt()/ TODO: 在此添加命令處理程序代碼KillTimer(1); / 刪除計(jì)時(shí)器運(yùn)行結(jié)果類似圖10-6的,但是已經(jīng)沒有閃爍了。3過程框圖圖10-7是緩沖繪制圖形動(dòng)畫的主要過程框圖(粗體為主要步驟):獲取客戶區(qū)矩形GetClientRect創(chuàng)建兼容空位圖CreateCompatibleBitma
42、p定義位圖對(duì)象CBitmap獲取當(dāng)前DCGetDC顯示當(dāng)前圖形BitBlt創(chuàng)建兼容內(nèi)存DCCreateCompatibleDC定義矩形結(jié)構(gòu)RECT或CRect定義內(nèi)存DC對(duì)象CDC修改圖形參數(shù)響應(yīng)計(jì)時(shí)器消息OnTimer設(shè)置計(jì)時(shí)器SetTimer選入位圖對(duì)象SelectObject繪制白色背景FillRect繪制當(dāng)前圖形Ellipse等擦除原圖FillRect釋放DCReleaseDC圖10-7 緩沖繪制圖形動(dòng)畫的主要步驟10.3 移動(dòng)位圖動(dòng)畫前面的固定(資源)位圖動(dòng)畫,是在屏幕的同一位置,連續(xù)顯示多幀不同的圖像,產(chǎn)生動(dòng)畫效果?,F(xiàn)在我們要討論的是,同一圖像在屏幕的不同位置顯示,產(chǎn)生移動(dòng)動(dòng)畫的效
43、果。這里被移動(dòng)的圖像,可以是資源位圖或用戶裝入的圖像、也可以是從屏幕的指定矩形區(qū)域中獲取的截圖、還可以是程序在內(nèi)存DC中繪制圖形所生成的圖像。下面介紹在背景圖上移動(dòng)圖像塊以產(chǎn)生動(dòng)畫效果的具體方法。我們先討論原理,再畫出過程圖,最后給出一個(gè)移動(dòng)足球的具體例子。 原理與圖形動(dòng)畫一樣,移動(dòng)位圖動(dòng)畫原理也是邊擦邊畫。不過,為了不破壞原有的背景,即不能采用異或畫圖,也不能用白色覆蓋來擦除。為了實(shí)現(xiàn)圖像塊在背景圖上移動(dòng),非常重要的一點(diǎn)是,必須預(yù)先保存將會(huì)被移動(dòng)圖像所覆蓋的背景圖上的對(duì)應(yīng)矩形區(qū)域,并在圖像移走后再恢復(fù)該區(qū)域的圖形,參見圖10-8。這一功能可由含圖像的內(nèi)存DC和位塊傳送函數(shù)來完成。另外,為了避
44、免會(huì)產(chǎn)生嚴(yán)重的閃爍現(xiàn)象,必須采用緩沖繪圖方法。保存背景圖塊(供擦除時(shí)用)顯示移動(dòng)圖塊(畫圖)恢復(fù)原背景圖塊(擦除)更新圖塊位置(循環(huán)形成動(dòng)畫)圖10-8 移動(dòng)位圖動(dòng)畫的基本原理還有一點(diǎn)需要考慮的是,被移動(dòng)的圖形一般不是矩形,直接繪制會(huì)產(chǎn)生難看的背景遮擋,例如,在的隨機(jī)繪制足球和單擊定位圖像塊例子中(參見圖9-23和圖9-24),就存在難看的足球四角上的白色遮擋現(xiàn)象。為了將矩形塊中的背景部分去掉,可以將矩形中的背景區(qū),用一種圖形中沒有的顏色來統(tǒng)一著色,并將該色指定為透明色,再利用CDC類或CImage類的透明位塊傳送成員函數(shù)TransparentBlt來進(jìn)行繪制操作。函數(shù)原型為:CDC類:BOO
45、L TransparentBlt(int xDest, int yDest, int nDestWidth, int nDestHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, UINT clrTransparent);CImage類:BOOL TransparentBlt(HDC hDestDC, int xDest, int yDest, int nDestWidth, int nDestHeight, UINT crTransparent = CLR_INVALID) const throw(
46、);透明位塊傳送函數(shù)在傳送的同時(shí),可以進(jìn)行縮放。其最后一個(gè)輸入?yún)?shù)最關(guān)鍵clrTransparent為指定的透明顏色(UINT類型與COLORREF所對(duì)應(yīng)的DWORD類型是等價(jià)的,都是無符號(hào)的4字節(jié)整數(shù))。例如(參見圖10-9):COLORREF transpCol = RGB(255, 0, 0); / 設(shè)置透明色memDC.TransparentBlt(x0, y0, w, h, &dc, 0, 0, w, h, transpCol); 白色背景紅色背景圖10-9 足球圖片10.3.2 過程框圖圖9-10是利用CBitmap類實(shí)現(xiàn)移動(dòng)位圖主要過程的邏輯框圖。繪制傳送恢復(fù)背景傳送繪制
47、圖塊傳送繪制 底圖傳送 保存背景塊傳送 繪制背景選入 畫布與當(dāng)前DC兼容的客戶區(qū)內(nèi)存DC對(duì)象CDC memDC與當(dāng)前DC兼容、大小同客戶區(qū)的位圖對(duì)象裝入了圖片文件的圖像對(duì)象選入 畫布窗口客戶區(qū)與當(dāng)前DC兼容的背景塊內(nèi)存DC對(duì)象CDC memDC與當(dāng)前DC兼容、大小同移動(dòng)圖塊的位圖對(duì)象選入 圖塊與當(dāng)前DC兼容的圖塊內(nèi)存DC對(duì)象CDC memDC裝入了位圖資源的位圖對(duì)象裝足球圖塊對(duì)應(yīng)客戶區(qū)的內(nèi)存DC圖10-10 移動(dòng)位圖的主要步驟10.3.3 例子下面是一個(gè)單擊和拖動(dòng)鼠標(biāo)來在一背景圖上移動(dòng)足球的例子:1創(chuàng)建項(xiàng)目、添加資源創(chuàng)建一個(gè)傳統(tǒng)的單文檔MFC應(yīng)用程序,加入紅色背景的足球資源(IDB_FOOTB
48、ALL_RED)。2添加類變量在視圖類中定義若干類變量:/ #include "atlimage.h"int W, H, w, h, x0, y0; / W 和H為客戶區(qū)的寬高、w和h為圖像塊的 / 寬高、x0和y0為圖像塊上次位置的坐標(biāo)COLORREF transpCol; / 透明色CDC memDC, dc, dc0; / memDC:客戶區(qū)、dc:圖像塊、dc0:背景塊CBitmap memBmp, bmp, bmp0, *pOldBmp; / memBmp用于客戶區(qū)、/ bmp用于圖像塊、bmp0用于背景塊、pOldBmp用于選出位圖CImage imgbk; /
49、 用于底圖3初始化在初始化函數(shù)OnInitialUpdate中裝入圖像、計(jì)算參數(shù)、創(chuàng)建圖像和內(nèi)存DC對(duì)象:/ 裝入圖像-transpCol = RGB(255, 0, 0); / 設(shè)置透明色/ 裝入res目錄中的底圖(在IDE中運(yùn)行時(shí),當(dāng)前目錄為項(xiàng)目所在目錄)imgbk.Load(L"restulips.tif"); / 如果直接在Release或Debug目錄中運(yùn)行,則需要修改文件路徑為:if(imgbk.IsNull() imgbk.Load(L".restulips.tif");/ 如果圖片文件位于可執(zhí)行程序所在目錄,則可修改文件路徑為: if(i
50、mgbk.IsNull() imgbk.Load(L"tulips.tif");bmp.LoadBitmap(IDB_FOOTBALL_RED); / 裝入圖像塊資源(紅色背景足球)/ 創(chuàng)建圖像塊對(duì)應(yīng)的內(nèi)存DC,并選入圖像塊-CDC *pDC = GetDC(); / 獲取視圖DCdc.CreateCompatibleDC(pDC);pOldBmp = dc.SelectObject(&bmp);/ 獲取圖像塊的寬高-BITMAP bm;bmp.GetBitmap(&bm);w = bm.bmWidth;h = bm.bmHeight;/ 創(chuàng)建背景塊位圖和內(nèi)
51、存DC,選入圖像-bmp0.CreateCompatibleBitmap(pDC, w, h);dc0.CreateCompatibleDC(pDC);pOldBmp = dc0.SelectObject(&bmp0);/ 獲取客戶區(qū)大小-CRect rect;GetClientRect(&rect);W = rect.Width(); H = rect.Height();/ 創(chuàng)建與客戶區(qū)對(duì)應(yīng)的圖像和內(nèi)存DC對(duì)象,選入圖像,繪制白色背景-/ 創(chuàng)建與視圖DC兼容并與客戶區(qū)大小一致的位圖對(duì)象memBmp.CreateCompatibleBitmap(pDC, W, H);memDC
52、.CreateCompatibleDC(pDC); / 創(chuàng)建與視圖DC兼容的內(nèi)存DC對(duì)象pOldBmp = memDC.SelectObject(&memBmp); / 選位圖對(duì)象入內(nèi)存DC/ 填充白背景色(缺省為黑色)/memDC.FillRect(&rect, new CBrush(RGB(255, 255, 255);/ 計(jì)算位于客戶區(qū)中央的圖像塊的左上角坐標(biāo)-x0 = W / 2 - w / 2;y0 = H / 2 - h / 2;4繪制底圖和初始圖塊在OnDraw函數(shù)中,先繪制底圖,再在客戶區(qū)中央繪制首個(gè)圖像塊:RECT rect; GetClientRect(&a
53、mp;rect); / 獲取當(dāng)前客戶區(qū)矩形if(!imgbk.IsNull()/ 繪制底圖imgbk.Draw(pDC->m_hDC, rect); / 利用位塊傳送將背景塊復(fù)制到內(nèi)存DCdc0.BitBlt(0, 0, w, h, pDC, x0, y0, SRCCOPY);/ 將客戶區(qū)復(fù)制到與客戶區(qū)對(duì)應(yīng)的內(nèi)存DCmemDC.BitBlt(0, 0, W, H, pDC, 0, 0, SRCCOPY);/ 繪制圖像塊到與客戶區(qū)對(duì)應(yīng)的內(nèi)存DCmemDC.TransparentBlt(x0, y0, w, h, &dc, 0, 0, w, h, transpCol);/ 將與客戶區(qū)對(duì)應(yīng)的內(nèi)存DC中的圖像,傳送到屏幕上pDC->BitBlt(0, 0, W, H, &mem
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 投資合股協(xié)議書
- 房地產(chǎn)行業(yè)風(fēng)險(xiǎn)抵御與應(yīng)急措施
- 八年級(jí)物理下冊(cè)知識(shí)競(jìng)賽策劃
- 二零二五版公司股權(quán)轉(zhuǎn)讓合同
- 食堂勞務(wù)承包合同范例
- 2025浙江省建筑安全員-A證考試題庫附答案
- 2025年-青海省安全員B證考試題庫
- 鋁合金型材在機(jī)械制造中的應(yīng)用流程
- 婦幼保健病人健康監(jiān)測(cè)流程
- 文化產(chǎn)業(yè)課題研究工作計(jì)劃
- 商品過度包裝問題研究及消費(fèi)者感知調(diào)查報(bào)告
- 疾控中心檔案管理工作的現(xiàn)狀與對(duì)策
- GB/T 3452.5-2022液壓氣動(dòng)用O形橡膠密封圈第5部分:彈性體材料規(guī)范
- GB 29837-2013火災(zāi)探測(cè)報(bào)警產(chǎn)品的維修保養(yǎng)與報(bào)廢
- 提高護(hù)理文書書寫質(zhì)量品管圈課件
- DBJ41-T 154-2016 裝配整體式混凝土結(jié)構(gòu)技術(shù)規(guī)程-(高清版)
- 兒童哮喘科普知識(shí)手冊(cè)
- 臨檢基礎(chǔ)小知識(shí)點(diǎn)整理
- 麻醉科臨床路徑
- T∕CATSI 08001-2020 小產(chǎn)區(qū)產(chǎn)品認(rèn)定通則
- R-朗格漢斯細(xì)胞組織細(xì)胞增生癥
評(píng)論
0/150
提交評(píng)論