使用IDirectDrawSurface::Lock( )就能让我们随心所欲,因为此函数可以允许我们直接修改页面。
Lock( )函数的用法如下:
HRESULT Lock(
LPRECT lpDestRect,
LPDDSURFACEDESC lpDDSurfaceDesc,
DWORD dwFlags,
HANDLE hEvent
);
第一个参数为一个指向某个RECT的指针,它指定将被锁定的页面区域。如果该参数为 NULL,整个页面将被锁定。
第二个参数为一个 DDSURFACEDESC结构的地址,将被填充页面的相关信息。
第三个参数,即dwFlags,还是象以前一样给它DDLOCK_WAIT。
第四个参数规定要为NULL。
现在举一个例子来说明怎样使用Lock( ),我们的目标是使lpDDSBack半透明地浮现在lpDDSBuffer上。先看看完整的锁屏部分(注意,这一节只讨论24和32位色下如何操作):
DDSURFACEDESC ddsd, ddsd2; //DirectDraw页面描述
ZeroMemory(&ddsd, sizeof(ddsd)); //ddsd用前要清空
ddsd.dwSize = sizeof(ddsd); //DirectDraw中的对象都要这样
ZeroMemory(&ddsd2, sizeof(ddsd2));
ddsd2.dwSize = sizeof(ddsd2);
lpDDSBuffer->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); //Lock!
lpDDSBack->Lock(NULL, &ddsd2, DDLOCK_WAIT, NULL);
BYTE *Bitmap = (BYTE*)ddsd.lpSurface; //Lock后页面的信息被存在这里,请注意
//这个指针可能每次Lock( )后都不同!
BYTE *Bitmap2 = (BYTE*)ddsd2.lpSurface;
锁完页面后,Bitmap数组的存储格式是这样的:如为32位色则页面上坐标为(x,y)的点的R/G/B值分别存在Bitmap的 y*ddsd.lPitch+x*4,y*ddsd.lPitch+x*4+1,y*ddsd.lPitch+x*4+2处;如为24位色则页面上坐标为 (x,y)的点的R/G/B值分别存在Bitmap的 y*ddsd.lPitch+x*3,y*ddsd.lPitch+x*3+1,y*ddsd.lPitch+x*3+2处。所以,现在我们就可以发挥想 象,做出想要的一切效果了,比如说动态光照(将一目标页面按光照表改变亮度即可)!下面是接下来的代码(32位色时):
int pos;
for (int y=0;y<480; y++)
{
pos=y*ddsd.lPitch;
for (int x=0; x<640; x++)
{
Bitmap[pos] =(Bitmap[pos]+Bitmap2[pos])>>1; //改R
pos++;
Bitmap[pos] =(Bitmap[pos]+Bitmap2[pos])>>1; //改G
pos++;
Bitmap[pos] =(Bitmap[pos]+Bitmap2[pos])>>1; //改B
pos+=2;//到下一个R处
}
}
lpDDSBack->Unlock(&ddsd2); //Unlock!
lpDDSBuffer->Unlock(&ddsd);
由于使用Lock后DirectDraw要锁定页面,在没有使用Unlock( )前我们是无法用其他办法如Blt来修改页面的。所以用完Lock( )要赶快象上面的程序那样Unlock( )。Unlock的方法很简单,lpDDSXXX->Unlock(LPDDSURFACEDESC lpDDSurfaceDesc)即可。
7.2 程序的提速
上面的程序看起来好象很简单,但运行速度很可能会很慢,即使你直接用汇编重写也不会快多少。原因是读显存非常慢,写显存的速度也比写内存慢。解决这个问题的方法是:
(1) 把除了主页面外的所有页面放在内存中。(初始化页面时将ddsd.ddsCaps.dwCaps中的 DDSCAPS_OFFSCREENPLAIN后再或( | )一项"DDSCAPS_SYSTEMMEMORY")这样做的另一个好处是你Lock( )一次后就永远得到了页面指针,而且然后一Unlock( )就又可以使用Blt了,你就拥有了两种改变页面的手段。
(2)将后台缓冲改为一个普通的离屏页面。
(3)将Flip( )改用BltFast( )函数实现。当然你也可以直接用memcpy( )拷贝,这样做有时候会快一些。注意要一行一行地拷贝,比如说640x480x24位色全屏幕拷贝是这样的:
BYTE *pSrc=(BYTE *)ddsd_src.lpSurface;
BYTE *pDest=(BYTE *)ddsd_dest.lpSurface;
for (int y=0;y<480;y++)
{
memcpy(pDest, pSrc, 1920); //若为32位色则为2560
pSrc+=ddsd_src.lPitch;
pDest+=ddsd_dest.lPitch;
}
看起来好像要重写很多东西,其实改动的部分并不多。这样改了之后整个程序的速度就会快很多,但还不能很好地满足全屏幕特效的要求,因为全屏幕特效实在很耗时间,只有用MMX指令重写速度才能比较快。所以在下面一章中,我们将介绍内嵌汇编和MMX指令。
众所周知,从内存到显示内存的数据传送速度比从内存到内存的速度慢,也比从显示内存到显示内存的速度慢。我们如果把BackSurface开在系统 内存里面,这样要显示的时候,我们只要把BackSurface的数据送到PrimarySurface里面,也就是说,我们只要从内存传送一屏的的数据 到显示内存里面就可以了,而上面所说的组织数据的问题,则是在速度较快的内存到内存之间的操作。而假如我们把BackSurface开在显示内存里面,则 每次显示的时候,组织数据是通过速度较慢的显示内存到内存之间的操作,则2-3屏的数据足以使整体速度下降很多。 这样,如果要提高游戏的显示速度,应该尽可能的把BakcSurface开在系统内存里面。