ITPub博客

首页 > IT职业 > IT生活 > DirectX学习手记(二) (转)

DirectX学习手记(二) (转)

原创 IT生活 作者:amyz 时间:2007-10-09 09:38:53 0 删除 编辑
DirectX学习手记(二) (转)[@more@]

DirectX学习手记(二) happyfire 2002/8/11 此回说说怎样用DirectDraw向表面上贴图,包括创建离屏表面,设置调色板,载入位图到表面,透明色,页面丢失等。 二. 用DirectDraw贴图 先让我们回忆一下上一回的内容。为了初始化DirectDraw我们首先创建了一个DirectDraw对象,然后设置了协作模式(全屏+独占),设置显示模式,然后创建主表面,提取后台缓冲表面指针。至此可以在后台表面上进行操作,然后flip到前台显示出来。最后程序结束前释放所有的directdraw对象。好了,现在说说怎样向后台表面贴图,即让屏幕显示图片。 第一步:创建离屏表面 离屏表面是你永远看不到的表面(所谓离屏),它通常被用来存放位图。通常的做法是把离屏表面上的位图用Blt的方法贴到后台表面,后台表面再flip为前台表面。看看它的创建方法: //声明用来存放图像的离屏表面(声明为全局变量) LPDIRECTDRAWSURFACE lpDDSPic ; //在InitDDraw()中创建保存图像的离屏表面 ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH ; ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN ; ddsd.dwHeight = 480 ; ddsd.dwWidth = 640 ; if ( lpDD->CreateSurface(&ddsd,&lpDDSPic,NULL)!=DD_OK ) return FALSE ; 首先要声明一个表面,然后填充DDSURFACEDESC表面描述结构体。dwFlags填DDSD_CAPS表示ddsCaps域有效, DDSD_HEIGHT和DDSD_WIDTH表示要指定页面的大小。ddsCaps.dwCaps填DDSCAPS_OFFSCREENPLAIN表示创建的是离屏表面。dwHeight和dwWidth填页面的大小,单位是像素。然后就可以用DirectDraw对象的CreateSurface 方法创建lpDDSPic了。注意CreateSurface的第三个参数必须填NULL,该参数将允许与今后的 com 集合特性相兼容。整个创建过程和创建主表面非常相似,只是ddsd的填充不同。这里稍微谈一下离屏表面的大小问题。在 DirectX 5.0 以前的版本中,离屏表面的宽度的最大值不能超出于主表面的宽度.而从 DirectX 5.0 版开始,你可以创建任何宽度的离屏页面,只要你的显存和内存足够大.缺省情况下,离屏表面是被创建在显存中的,但如果显存不够用,它就会被置于内存中,当然你可以指明只在显存中创建表面(可在 DDSCAPS 结构的 dwCaps 成员中包含进 DDSCAPS_SYSTEMMEMORY 或 DDSCAPS_VideOMEMORY 标志符,以明确的表明你希望将表面置于何处),如果这样的话,当表面太大而超出显存的容纳范围时,就会创建失败。另外,以上面的方法创建的离屏表面的像素格式(可以简单理解为表示几位的颜色)是和主表面相同的,当然也可以创建一个像素格式与主表面不同的离屏表面.然而,在这种情况下,该离屏表面将被限制于系统内存中。具体的做法有点烦,各位感兴趣的可以查一下手册,:) 。 第二步:读入调色板并设置上我们的主表面被设置为基于调色板(8位色深度)。一个基于调色板的表面是一些数字的集合, 其中的每一个数字代表一个像素.每一个数字的值都对应于一个色彩表(color table)中的项,这个表告诉 DirectDraw 对这个像素使用什么样的颜色. 这个表就是调色板(Palette)。使用调色板是为了尽量降低对显存的需求,它用一个颜色索引(Color Index)来代表各个像素点的颜色,而不是直接用红,绿,蓝三基色的亮度值来确定每个像素点的颜色。调色板包含了若干颜色索引和该索引所对应的真实颜色值。调色板的颜色索引主要采用4或8两种位深度。要在表面上正常显示图像就必须将表面的调色板设置为该图像的调色板。如下面的代码片断: //先声明调色板对象(其实是指针类型,就这么说吧,上面提到的各个对象都是这样的) LPDIRECTDRAWPALETTE lpDDPal ; //从位图设置调色板 lpDDPal = DDLoadPalette(lpDD, szBitmap); if (lpDDPal) lpDDSPrimary->SetPalette(lpDDPal); else return FALSE ; DDLoadPalette是DDutil.cpp里面的函数,happyfire也不大懂,作为初学者拿来用就是了。 第三步:将位图文件读入已创建好的离屏表面中 这一步我们用DirectDraw apis里面的一个函数DDLoadbitmap来实现,具体用法是: lpDDSPic = DDLoadBitmap ( lpDD, szBitmap, 0, 0 ) ; //szBitmap是位图文件名,例如".picbackground.bmp" 使用了DDLoadBitmap这一步变得非常简单,但是要把Ddutil.cpp和Ddutil.h加到工程中才行。你可以看看 Ddutil.cpp中该函数的实现,它调用windows的API LoadImage,并且用作为参数传进来的directdraw对象创建了一个表面并返回了它的指针。 第四步:使用blit的方法,将离屏表面上的图片传送到后台缓冲 何谓blit?Bit block transfer(位块传送),即将内存中的数据块从一处传送到另一处。DirectDraw 提供了两个方法,Blt和BltFast。Blt功能强大,BltFast速度较快。这里我们先不讨论Blt,只用BltFast。看如何实现: RECT rcRect ; rcRect.left= 0; rcRect.top= 0 ; rcRect.right= 640; rcRect.bottom=480; lpddsBack->BltFast( 0, 0, lpddsPic, &rcRect, DDBLTFAST_SRCCOLORKEY|DDBLTFAST_WAIT); RECT是windows的矩形结构(所以要包含windows.h),rcRect这个矩形用来确定源表面(这里是离屏表面)上的哪一块区域要被传送到目标表面(这里是后台缓冲表面)上去。BltFast的前两个参数是两个Dword值,表示目标表面上的一个点的坐标x和y,它决定了传送过去的图形在目标表面上所处的位置。你可以改变x,y和rcRect看看效果。最后一个参数是传送类型: DDBLTFAST_DESTCOLORKEY 指定进行一次带透明的位块传送,使用目标表面的关键色(color key)。 DDBLTFAST_NOCOLORKEY 指定进行一次普通的复制,不带透明成分。 DDBLTFAST_SRCCOLORKEY 指定进行一次带透明的位块传送,使用源表面的关键色。 DDBLTFAST_WAIT 如果位块传送器正忙,延迟 DDERR_WASSTILLDRAWING 消息的发送,直到位块传送器准备好或发生其它错误时才返回。 看看这个color key,这可是很有用的!所谓关键色,即我们说的透明色,如果在源表面上指定了一个颜色为关键色,那么在blit操作中,将视具有这种颜色的区域为透明,不会被传送到目标页面上。这样的话,虽然传送过去的是一个矩形,但矩形上是关键色的部分是不会传送的,从而看上去是有轮廓的图形,比如一个精灵。 当然必须先为一个表面设置关键色才行。在InitDDraw中我们用DDSetColorKey ( lpDDSPic, RGB(255,0,255) ) ;为创建好的离屏表面lpDDSPic设置了紫红色的关键色。你可以在图片中画一个精灵,然后把所有非精灵的部分用紫红色填充。然后将BltFast的最后一个参数设为DDBLTFAST_SRCCOLORKEY.这样传送过去的就是一个精灵的样子了。 用DDBLTFAST_DESTCOLORKEY可以为目标表面设置关键色,有所不同的是,目标表面上颜色只有为关键色才能被覆盖,即染色。 最后在说说目标表面和原表面的区别。其实它们都是相对的。调用BltFast的为目标表面,作为参数的为源表面。一般的用法,后台表面调用BltFast,参数为离屏表面。 贴图的问题就说这么多吧,其实这里面的内容还是挺多的。还有非调色板模式的16位,24位,32位RGB格式,16位RGB对应于不同的显卡还有555,565两种模式。但我们是初学者嘛,这些东西...:) 有待研究。(如果你真的立志于作游戏,呵呵,准备学汇编吧,以后还要和MMX处理器的指令什么的打交道啊) 最后要说的是第四步的操作要放在MainLoop中,这样让它不停的Blt再Flip,如果你在程序中设计按了某个键贴不同的图并改变贴图的位置,就可以做出精灵动画了,基本的原理就是这样的。 三.浅谈表面丢失的处理 如果你的程序从全屏模式下用Alt+Tab切出去,再切回来,你可能发现图片不见了。因为当代表页面内存的 DirectDrawSurface对象被不得已的释放时,与该对象相关联的页面内存也会被释放。当一个DirectDrawSurface对象丢失其页面内存的时候,它的许多函数将返回DDERR_SURFACELOST,并且不进行任何其它操作。先看一下这个片断: while (1){ hRst = lpDDSPrimary->Flip(NULL,0) ; if ( hRst==DD_OK ) break ; if ( hRst==DDERR_SURFACELOST ){ if ( RestoreAllDDS()!=DD_OK ) break ; } if (hRst != DDERR_WASSTILLDRAWING){ break; } } 上回flip中没提到flip有失败的可能,上面的片断是解决的方法。如果flip返回DDERR_WASSTILLDRAWING,那么是由于上一次flip 操作尚未完成(flip指令已发出,但directX还没有完成操作),我们可以做的只能是让while循环下去,直到 hRst != DDERR_WASSTILLDRAWING。如果flip返回DD_OK,那表示flip操作完成,可以break出去了。如果flip返回 DDERR_SURFACELOST,表示发生了表面失效,这时我们需要自己处理它。看看我的RestoreAlDDS()函数是怎么工作的: HRESULT RestoreAllDDS( void ) { HRESULT hRst ; hRst = lpDDSPrimary->Restore(); if (hRst) hRst = lpDDSBack->Restore () ; if (hRst) hRst = lpDDSPic->Restore() ; DDReLoadBitmap ( lpDDSPic,szBitmap ) ; return hRst ; } 在这个函数里面,我们先后调用了主表面,后台表面,离屏表面的Restore方法。Restore方法可以为这些丢失了内存的页面重新分配内存,并且将这些内存与DirectDrawSurface对象联系上。但重建内存并不会使以前存在于该页面上的图象重新显现出来,因此在调用Restore函数重建之后,必须亲手重新绘制所有的图象。在这里我用了DDReLoadBitmap函数,这个函数也是 Ddutil.cpp里面的。需要说明的是,如果用DDLoadBitmap代替DDReLoadBitmap是不行的!你可以看看这两个函数的实现,比较一下。


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10752019/viewspace-975392/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
  • 博文量
    3984
  • 访问量
    7335174