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 --
然后循环直到全部显示。