1、基本原理
在Visual C++ 6.0中,顯示位元影像的方法及過程如下:
1.1 顯示程式資源中的位元影像(位元影像的所有資料均存在於可執行檔中)
1)從資源中裝入位元影像
·定義位元影像對象資料成員CBitmap m_Bitmap;
·調用CBitmap成員函數LoadBitmap(),例如:m_Bitmap.LoadBitmap(IDB_BITMAP1);
·傳入LoadBitmap的參數是位元影像在圖形編輯器中產生或從位元影像檔案中引入時賦予的識別符。
2)產生與位元影像相聯絡的記憶體裝置情境對象
CDC MemDC;
MemDC.CreateCompatibleDC(NULL);
MemDC.SelectObject(&m_Bitmap);
3)顯示位元影像
CClientDC ClientDC(this);
BITMAP BM;
m_Bitmap.GetObject(sizeof(BM), &BM);
ClientDC.BitBlt(
X,Y, // 目標裝置邏輯橫、縱座標
BM.bmWidth, BM.bmHeight, // 顯示位元影像的像素寬、高度
&MemDC, // 待顯示位元影像資料的裝置情境對象
0,0, // 來源資料中的橫、縱座標
SRCCOPY // 位操作方式
); 這種方法顯示位元影像速度快,但不是很靈活,而且會使可執行檔增大。
1.2 顯示獨立檔案方式的位元影像(位元影像的所有資料獨立於可執行檔)
HBITMAP *hBitmap; // 定義位元影像物件控點
BITMAP BM;
CDC MemDC;
CClientDC ClientDC(this);
MemDC.CreateCompatibleDC(&ClientDC);
hBitmap=(HBITMAP*)::LoadImage(
AfxGetInstanceHandle(), // 取得應用程式控制代碼
"demo1.bmp", // 位元影像檔案名稱
IMAGE_BITMAP, // 類型為Windows位元影像
0,0,
LR_LOADFROMFILE
);
// 從檔案中取位元影像資料
MemDC.SelectObject(hBitmap);
::GetObject(hBitmap,sizeof(BM), &BM);
// 使用格式與方法一同
ClientDC.BitBlt(... ...)
這種方法顯示位元影像速度較之前一種慢了一點,但其靈活性較大,可以任意變換位元影像檔案,而無需重新編譯來源程式,也減小了可執行檔的大小。
2、實現方法
下面介紹各種圖形顯示技巧的具體實現原理及方法。以下所有程式演算法的實現均可放在視類(CView,也可視自己的需要放在其他類)中處理,且有必要進行如下的相關操作:
增加如下類成員變數
BITMAP m_Bm; // 儲存位元影像的寬、高度等資料
HBITMAP *m_hBitmap; // 儲存位元影像資料控制代碼
CDC m_MemDC; // 記憶體裝置情境對象
在類建構函式中加入如下代碼
m_MemDC.CreateCompatibleDC(NULL); // 產生記憶體裝置情境對象
m_hBitmap=(HBITMAP *)::LoadImage(
// 從檔案中裝入位元影像資料
AfxGetInstanceHandle(),
"demo1.bmp",
IMAGE_BITMAP,
0,0,
LR_LOADFROMFILE
);
m_MemDC.SelectObject(m_hBitmap); // 將位元影像選入記憶體裝置情境對象
::GetObject(m_hBitmap, sizeof(m_Bm),&m_Bm);
2.1 水平交錯效果
原理:將記憶體裝置情境對象(例如:MemDC)中的位元影像資料拆分成奇、偶掃描線兩部分,其中奇數條掃描線由上往下移動,偶數條掃描線則由下往上移動,且兩者同時進行。螢幕上的效果為分別由上下兩端出現的較淡柵欄圖形,逐漸相互靠近,直至整個位元影像完全清楚。垂直交錯效果的實現原理與之類似。
程式演算法:
int i, j;
for ( i=0; i<=m_Bm.bmHeight; i+=2 )
{
j = i;
while ( j>0 )
{
ClientDC.StretchBlt(
// 奇數,由上至下
0,j-1, // 目標裝置邏輯橫、縱座標
m_Bm.bmWidth, 1, // 顯示位元影像的像素寬、高度
&m_MemDC, // 源位元影像裝置情境對象
0,m_Bm.bmHeight-(i-j-1), // 源位元影像的起始橫、縱座標
m_Bm.bmWidth,1, // 源位元影像的像素寬、高度
SRCCOPY
);
ClientDC.StretchBlt(
// 偶數,由下至上
0, m_Bm.bmHeight-j, // 目標裝置邏輯橫、縱座標
m_Bm.bmWidth,1, // 顯示位元影像的像素寬、高度
&m_MemDC, // 源位元影像裝置情境對象
0,i-j, // 源位元影像的起始橫、縱座標
m_Bm.bmWidth,1, // 源位元影像的像素寬、高度
SRCCOPY
);
j-=2;
}
// while ( j>0 )
Sleep(10);
}
// for ( i=0; i<=m_Bm.bmHeight; i+ =2 )
2.2 雨滴效果
原理:將記憶體裝置情境對象(例如:MemDC)中位元影像資料的最後一條掃描線,順序地從目標裝置(例如:ClientDC)中待顯示位元影像的第一條掃描線所在位置移動至最後一條處,並保留此條掃描線在螢幕上移動時留下的軌跡。接著再把MemDC中位元影像資料的倒數第二條掃描線,順序地從目標裝置(例如:ClientDC)中待顯示位元影像的第一條掃描線所在位置移動至倒數第二條處。其餘的掃描線依此類推。
程式演算法:
int i,j;
for ( i=0; i<=m_Bm.bmHeight; i++ )
{
for ( j=0; j<=m_Bm.bmHeight-i; j++ )
ClientDC.StretchBlt(
0,j, // 目標裝置邏輯橫、縱座標
m_Bm.bmWidth, 1, // 顯示位元影像的像素寬、高度
&m_MemDC, // 源位元影像裝置情境對象
0,m_Bm.bmHeight-i,// 源位元影像的起始橫、縱座標
m_Bm.bmWidth,1, // 源位元影像的像素寬、高度
SRCCOPY
);
Sleep(20);
}
// for ( i=0; i<=m_Bm.bmHeight; i++ )
2.3 百葉窗效果
原理:將記憶體裝置情境對象(例如:MemDC)中的位元影像資料分成若干組,然後分別從第一組到最後一組進行搬移,第一次搬移每組中第一條掃描線到目標裝置(例如:ClientDC)中待顯示位元影像的相應位置,第二次搬移每組中第二條掃描線,接著第三條、第四條掃描線。
程式演算法:
int i,stepi,j;
stepi=m_Bm.bmHeight/10;
for ( i=0; i<=stepi; i++ )
{
for ( j=0; j<10; j++ )
ClientDC.StretchBlt(
0,j*stepi+i, // 目標裝置邏輯橫、縱座標
m_Bm.bmWidth,1, // 顯示位元影像的像素寬、高度
&m_MemDC, // 源位元影像裝置情境對象
0,j*stepi+i, // 源位元影像的起始橫、縱座標
m_Bm.bmWidth,1, // 源位元影像的像素寬、高度
SRCCOPY
);
Sleep(20);
}
// for ( i=0; i<=stepi; i++ )
2.4 隨機積木效果
原理:將記憶體裝置情境對象(例如:MemDC)中的位元影像資料分成縱橫十等份共一百組資料,然後隨機地取出這一百組資料中的某一組顯示到目標裝置(例如:ClientDC)中待顯示位元影像的相應位置,如此反覆直到所有一百組資料均顯示完畢為止。
程式演算法:
int i,j,stepx,stepy,dispnum,x,y;
int pxy[10][10]; // 使用本數組記錄已顯示過的資料群組
for ( i=0; i<10; i++ )
for ( j=0; j<10; j++ )
pxy[i][j]=0;
stepx=m_Bm.bmWidth/10;
stepy=m_Bm.bmHeight/10;
srand( (unsigned)time( NULL ) );
dispnum=0;
// 記錄已顯示過的資料群組的個數
while(1)
{
x=rand() % 10;
y=rand() % 10;
if ( pxy[x][y] ) // 本組x,y所代表的資料群組是否已顯示過?
continue;
pxy[x][y]=1; // 表明本組x,y所代表的資料群組已顯示過
ClientDC.StretchBlt(
x*stepx,y*stepy, // 目標裝置邏輯橫、縱座標
stepx,stepy, // 顯示位元影像的像素寬、高度
&m_MemDC, // 源位元影像裝置情境對象
x*stepx, y*stepy, // 源位元影像的起始橫、縱座標
stepx,stepy, // 源位元影像的像素寬、高度
SRCCOPY
);
dispnum++;
if ( dispnum >=100 ) break;
Sleep(30);
}
// while(1)
結 語
以上程式碼均在Visual C++ 6.0中調試通過,所有片斷均可編寫成獨立的函數,靈活使用。如果對以上幾種顯示效果進行變換,我們還可以實現多種其它特技效果。
crazybit :關於“隨機積木”演算法的隨機選取,我認為周長發先生在他的《精通Visual C++映像編程》中介紹的方法更好。
方法為 :
待顯示圖象小塊:pBlock[nBlockNum]
取隨機數(範圍 : 0..nBlockNum),得random,顯示pBlock[random],然後把pBlock[random]和pBlock[nBlockNum]交換
nBlockNum --
然後迴圈直到全部顯示。