淺談位圖的淡入淡出和漸隱
發表于:2007-07-14來源:作者:點擊數:
標簽:
成都電子科技大學朱寧 在許多 游戲 和屏幕保護程序中,我們都可以發現位圖的淡入淡出和漸隱(一幅圖象漸漸的消失于另一幅圖象中)的應用。如何實現這些效果呢? 在 windows (GDI)環境下,實現位圖的淡入淡出和漸隱的方法有三種:1.調色板動畫;2.模式畫刷;3
成都電子科技大學 朱寧
在許多
游戲和屏幕保護程序中,我們都可以發現位圖的淡入淡出和漸隱(一幅圖象漸漸的消失于另一幅圖象中)的應用。如何實現這些效果呢?
在
windows(GDI)環境下,實現位圖的淡入淡出和漸隱的方法有三種:1.調色板動畫;2.模式畫刷;3.動畫法。其中,第一種方法速度很快,但只能用于256色的圖形,而且不易實現漸隱效果。第二種方法實現比較簡單,但是主觀效果不及其余兩種。第三種方法的效果很好,但速度要稍慢一些。由于現在已經很難得到
質量較高的256色圖片,加之目前幾乎所有的顯卡均支持高彩和真彩模式,所以不推薦采用第一種方法。下面介紹后兩種方法在Visual C++編程環境下的實現。
一:模式畫刷法:
CDC類的BitBlt(...),MaskBlt(...),以及WIN32API ::StretchDIBits(...)函數均支持三元ROP(Raster Operation)操作,即由源,模式畫刷(pattern brush)和目的區域原有的圖形經一定的邏輯運算而形成最終的輸出圖形。所以,通過改變模式畫刷的圖案,輔以一定的ROP操作,就可以形成一些特殊的效果。
首先,要準備若干個8*8的單色位圖,作為模式畫刷的模板。單色位圖中應只含有黑白兩種顏色的像素,每一個位圖中兩種像素的比例和形狀將決定顯示的效果,通常我們由一個全黑的位圖開始,逐漸增加白色像素的比例,最后一幅位圖全部由白色象素組成。
這些位圖制作好以后,將它們Import入工程,命名為IDB_PATTERN1、IDB_PATTERN2 ... ... 調用CBitmap::LoadBitmap(...)函數將其選入對應的CBitmap對象,然后調用CBrush::CreatePatternBrush(...)制作模式畫刷。
有了合適的模式畫刷以后,還需要設定我們所需的ROP碼,對于淡入操作,要求將源位圖與模式畫刷的反依次相與。對于淡出操作,要求將當前顯示區域的位圖與模式畫刷依次相與。對于漸隱,我們需要把原位圖與模式畫刷相與后,把這個結果和當前顯示區域的位圖和畫刷的鏡象(原畫刷的非)相與的結果相或。依次改變畫刷,就可以得到漸隱的效果。這些操作的ROP碼,MFC中并沒有對應的預定義宏,但我們可以通過計算得到它,在Visual C++ 5.0 的在線文檔“Ternary Raster Operations”中,詳細介紹了計算方法。最終我們得到淡入、淡出操作的ROP碼分別為000C0324、0x00A000C9。漸隱操作的ROP碼是0x00AC0744。為了形成完整的動畫效果,我們需要設置一個定時器來自動的執行這一系列的操作。
下面用一個簡單的例子說明模式畫刷法的實現:
1:建立一個基于對話框的項目,命名為PatternDemo.
2:刪除對話框上的“Todo:...”注釋,并增加一個按紐,命名為“DEMO”
3:為DEMO按紐加入對應的事件句柄OnDEMO(...).
4:在CPatternDemo中加入私有成員變量如下:
CDC *pdc;
CDC memDC;
CBitmap bmp;
CBrush brush[8];
UINT counter;
UINT mode;
UINT onrun;
5:用VC自帶的位圖編輯器,按上文要求編輯8個8*8像素的單色位圖,命名為IDB_PATTERN1...IDB_PATTERN8。
6:Import兩個100*100像素的真彩bmp圖片,命名為IDB_BMPSOURCE1和IDB_BMPSOURCE2。
7:使用ClassWizard為CPatternDemoDlg加入WM_CREATE的消息響應函數OnCreate(...),并在其中添加如下代碼:
...
for(int i=0;i<8;i++)
{
bmp.LoadBitmap(IDB_PATTERN1+i);
brush[i].CreatePatternBrush(&bmp);
bmp.DeleteObject();
}
...
8:在CPatternDemoDlg::OnDEMO(...)函數中添加如下代碼:
...
if(!onrun)
{
pdc=GetDC();
pdc->SetBkColor(RGB(0,0,0));
pdc->SetTextColor(RGB(255,255,255));
pdc->FillSolidRect(0,0,100,100,RGB(0,0,0));
memDC.CreateCompatibleDC(pdc);
bmp.LoadBitmap(IDB_BMPSOURCE1);
memDC.SelectObject(&bmp);
bmp.DeleteObject();
mode=1;
counter=0;
SetTimer(1,200,NULL);
onrun=1;
}
...
9:使用ClassWizard為CPatternDemoDlg加入WM_TIMER的消息響應函數OnTimer(...),并在其中添加如下代碼:
...
if(mode==1)
{
if(counter>7)
{
mode=2;
counter=0;
return;
}
pdc->SelectObject(&brush[counter]);
pdc->BitBlt(0,0,100,100,&memDC,0,0,0x000C0324);
counter++;
}
if(mode==2)
{
if(counter>7)
{
mode=3;
counter=0;
return;
}
if(counter==0)
{
bmp.LoadBitmap(IDB_BMPSOURCE2);
memDC.SelectObject(&bmp);
bmp.DeleteObject();
}
pdc->SelectObject(&brush[counter]);
pdc->BitBlt(0,0,100,100,&memDC,0,0,0x00AC0744);
counter++;
}
if(mode==3)
{
if(counter>7)
{
memDC.DeleteDC();
bmp.DeleteObject();
KillTimer(1);
onrun=0;
return;
}
pdc->SelectObject(&brush[counter]);
pdc->BitBlt(0,0,100,100,NULL,0,0,0x00A000C9);
counter++;
}
...
9:在CPatternDemoDlg::CpatternDemoDlg()中加入:
...
onrun=0;
...
10: 使用ClassWizard為CPatternDemoDlg加入WM_DESTORY的消息響應函數OnDestory(...),并在其中添加如下代碼:
...
memDC.DeleteDC();
KillTimer(1);
...
編譯運行該項目,可以看到第一幅圖象從背景中漸漸的浮現出來,隨后,又漸漸地隱入第二幅圖象之中,接著,第二幅圖象又慢慢地消失于背景中。
動畫法:
這種方法是利用直接操作位圖的數據來實現的,可以實現像素顏色的平滑變化,視覺效果可以做的很好,因此,這種方法在屏保中的應用非常多。
首先,我們必須了解bmp圖形的結構。一個bmp圖形由兩個部分組成,即文件頭和數據區,文件頭存放bmp圖形的大小、格式等信息,數據區存放bmp圖形各個像素的顏色信息。對于24位真彩色的bmp來說,文件頭的大小為54個字節,前14個字節對應VC中定義的BITMAPFILEINFO結構,后40個字節對應BITMAPINFOHEADER結構。我們把bmp數據區的數據讀出,經過一定的運算,再利用WIN32API::StretchDIBits(...)函數直接輸出到顯示DC上,就可以實現一些特技效果。
下面讓我們分步去實現一個全屏幕的演示程序:
1:生成一個基于對話框的項目,命名為F1:
2:刪除F1Dlg.h、F1Dlg.h和F1.cpp中與其相關的所有代碼。
3:在項目中添加一個基類為generic Cwnd的新類,命名為CW.
4:為CW類添加如下私有成員變量:
//////////////////////
UINT y_offset;
UINT x_offset;
UINT stage;
BYTE* p3;
BYTE * p2;
BYTE * p1;
BITMAPINFOHEADER header;
HGLOBAL hlb1;
HGLOBAL hlb2;
HGLOBAL hlb3;
UINT start;
UINT counter;
////////////////////////
并在W.h的頂部加入宏定義 #define BMP_SIZE 192000
5:為CW添加Create(...)虛函數,WM_CREATE,WM_TIMER,WM_PAINT,WM_DESTORY,WM_LBUTTONDOWN的消息句柄,接受缺省的函數名稱。
6:刪除CW::Create(...)中的原有代碼,用以下代碼替換:
/////////////////////////
LPCTSTR m_lpszCN;
m_lpszCN = AfxRegisterWndClass(CS_BYTEALIGNCLIENT,
::LoadCursor(AfxGetResourceHandle(),
MAKEINTRESOURCE(IDC_NULLCORSOR)));
return CWnd::CreateEx(WS_EX_TOPMOST,m_lpszCN, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext);
//////////////////////////
7: 刪除CF1App:: InitInstance()中#endif以后的所有代碼,用以下代碼代替:
///////////////////////////
int cx=GetSystemMetrics(SM_CXSCREEN);
int cy=GetSystemMetrics(SM_CYSCREEN);
CRect rectDefault(0,0,cx,cy);
m_pMainWnd=new CW();
m_pMainWnd->Create(NULL, _T("Hello World!"), WS_VISIBLE|WS_POPUP, rectDefault,NULL,NULL);
return TRUE;
///////////////////////////
8:在CW::OnCreate(...)函數中加入如下代碼:
/////////////////////////////
counter=0;
int cx=GetSystemMetrics(SM_CXSCREEN);
int cy=GetSystemMetrics(SM_CYSCREEN);
x_offset=(cx-640)/2;
y_offset=(cy-400)/2;
/////////////////////////////
9: 在CW::OnDestroy()函數中加入如下代碼:
////////////////////////////////
KillTimer(1);
GlobalFree(hlb1);
GlobalFree(hlb2);
GlobalFree(hlb3);
/////////////////////////////
10: 在CW:: OnLButtonDown(...)函數中加入如下代碼:
//////////////////////////////
SendMessage(WM_CLOSE);
//////////////////////////////
11: 在CW:: OnLButtonDown(...)函數中加入如下代碼:
/////////////////////////////
CPaintDC dc(this);
dc.FillSolidRect(0,0,800,600,RGB(0,0,0));
dc.SetTextColor(RGB(200,0,0));
if(!start) return;
CFile f1,f2;
f1.Open("bmp1.bmp",CFile::modeRead);
f2.Open("bmp2.bmp",CFile::modeRead);
f1.Seek(14,CFile::begin);
f1.Read(&header,40);
f2.Seek(54,CFile::begin);
hlb1=GlobalAlloc(GMEM_MOVEABLE,BMP_SIZE);
p1=(BYTE*)GlobalLock(hlb1);
p1=(BYTE*)malloc(BMP_SIZE);
f1.ReadHuge(p1,BMP_SIZE);
GlobalUnlock(hlb1);
hlb2=GlobalAlloc(GMEM_MOVEABLE,BMP_SIZE);
p2=(BYTE*)GlobalLock(hlb2);
f2.ReadHuge(p2,BMP_SIZE);
GlobalUnlock(hlb2);
hlb3=GlobalAlloc(GMEM_MOVEABLE,BMP_SIZE);
p3=(BYTE*)GlobalLock(hlb3);
GlobalUnlock(hlb3);
f1.Close();
f2.Close();
stage=1;
SetTimer(1,100,NULL);
start=0;
///////////////////////////
12: 在CW:: OnTimer(...)函數中加入如下代碼:
///////////////////////////
if(stage==1)
{
if(counter++>63)
{
stage=2;
counter=0;
return;
}
for(int i=0;i<BMP_SIZE;i++)
p3[i]=counter*p1[i]/64;
::StretchDIBits(GetDC()->m_hDC,x_offset,y_offset,640,400,0,0,320,200,p3,
((BITMAPINFO*)(&header)),NULL,SR
CCOPY);
}
if(stage==2)
{
if(counter++>63)
{
stage=3;
counter=0;
return;
}
for(int i=0;i<BMP_SIZE;i++)
p3[i]=(64-counter)*p1[i]/64+counter*p2[i]/64;
::StretchDIBits(GetDC()->m_hDC,x_offset,y_offset,640,400,0,0,320,200,p3,
((BITMAPINFO*)(&header)),NULL,SRCCOPY);
}
if(stage==3)
{
if(counter++>63)
{
KillTimer(1);
SendMessage(WM_CLOSE);
return;
}
for(int i=0;i<BMP_SIZE;i++)
p3[i]=(64-counter)*p2[i]/64;
::StretchDIBits(GetDC()->m_hDC,x_offset,y_offset,640,400,0,0,320,200,p3,
((BITMAPINFO*)(&header)),NULL,SRCCOPY);
}
//////////////////////////////////
13:最后,將兩個分辨率為320*200的24bitBMP拷入工程所在的目錄中,分別命名為1.bmp和2.bmp. 編譯運行程序,可以看到在黑色的背景中,第一幅圖象(1.bmp)由暗漸漸變亮,當它完全出現后,第二幅圖象(2.bmp)從第一幅圖象中慢慢的浮現出來。當第二幅圖象完全取代第一幅后,它的亮度又逐漸減小,最終消失在黑色的背景中。與模式畫刷法不同,這些過渡非常平滑。
原文轉自:http://www.anti-gravitydesign.com