《DirectX 50游戏编程指南之DirectDraw篇.doc》由会员分享,可在线阅读,更多相关《DirectX 50游戏编程指南之DirectDraw篇.doc(24页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、我们的精彩,全因您的参与 中国游戏开发者.CNDirectX 5.0游戏编程指南之DirectDraw篇1998-08-25 吴佳鲜、朱春喜 目 录1 配置DirectX SDK2 DirectDraw实例3 创建动画4 使用覆盖表面5 其它的DirectDraw范例-DirectX是为Visual C+的用户准备的,因此要编制DirectDraw游戏程序,最好对VC+要有一定的了解。不愿意使用 VC的用户也可以利用Arakelian Soft公司开发的专门针对Visual Basic5.0用户的ActiveX控件Direc tStudio98或Tegosoft公司的TegoSoft Acti
2、veX for Visual Basic。不过,如果想充分发挥DirectX的性能,并且希望保持程序的兼容,那么最好还是使用Visual C+。为了叙述方便,假定已经安装了DirectX5.0 SDK 和Visual C+ 5.0。其目录分别是C:DX5SDK和C:ProgramFilesDevStudio。如果你使用了另一种编译器或安装到了其它目录下,必须将下面的例子做适当的修改才能运行。有人安装了DirectX SDK后却不知怎样使用,因为它是基于Visual C+的,却没有一个界面友好的集成开发环境,因此必须对Visual C+进行适当的配制。1、配置DirectX SDK1.1 配置M
3、icrosoft Developer Studio为了编译DirectX SDK提供的例子,需要打开一个新的Project Workspace,插入适当的文件,设置环境变量使得编译器能够找到需要的链接库和包含文件,下面描述了设置的全部过程。启动Microsoft Developer Studio,安装下述步骤创建工程 在File菜单,选择New 在New对话框中选择Project中的Win32 Application 在Project Name输入DDEX1 在Location文本框输入放置工程文件的位置,点OK按钮 一个新的DDEX1 Classes文件夹就出现在workspace窗口的左边
4、了 创建了工程后,需要使用如下步骤向工程插入适当的文件 在Project菜单选择Add to Project | Files 浏览到C:DX5SDKSDKSAMPLESDDEX1目录,选择所有的文件 选择OK,该目录下的DDEX1.CPP、DDEX1.RC、RESOURCE.H就加入到工程了 然后设置包含文件的路径 在Tools菜单,选择Options,就弹出Options对话框 选择Directories,在Show Directories For列表框选择Include files 在Directories列表框双击列表底部的空白行,输入C:DX5SDKSDKINC 同样再加入另一个路径C
5、:DX5SDKSDKSAMPLESMISC 选择OK按钮 设置链接库目录 在Show Directories For列表框选择Library files 在Directories:列表框双击底部空白行,输入C:DX5SDKSDKLIB 单击OK按钮 最后设置建立应用程序时链接的模块 在Project菜单单击Settings 选择Link 在Category下拉框选择General 在Object/Library模块列表框加入Ddraw.lib和Winmm.lib 单击OK 1.2 配制NMAKE路径有时候命令行的方式比集成环境更加方便,所以许多有经验的程序员更愿意用命令行的方式来建立应用程序。
6、下面是包含文件和链接库模块的路径:echo offset PATH=C:Program FilesDevStudioSharedIDEBin;C:Program FilesDevStudioVcBin;%PATH%set INCLUDE=C:Program FilesDevStudioVcinclude;C:Program FilesDevStudioVcMfcinclude;C:DX5SDKSDKINC;%INCLUDE%set LIB= C:Program FilesDevStudioSharedIDEVclib;C:Program FilesDevStudioVcMfclib; C:DX
7、5SDKSDKLIB;%LIB%set INIT= C:Program FilesDevStudio;%INIT%将上述内容加入Autoexec.bat。在例子的目录下输入 NMAKE 将会在当前目录下创建一个DEBUG目录,并将生成的可执行文件放在该目录下。为了在学习的过程中熟悉DirectX SDK,我们将按照DirectX SDK提供的范例程序的顺序由浅入深,循序渐进。1.3 为Borland C+5.0配置DirectX SDK尽管DirectX 5 SDK是主要为Visual C+用户准备的,但Microsoft并未忘记众多的Borland C+用户,所以在DirectX SDK中也
8、提供了DirectX的Borland C+库。不过,可能出于竞争的缘故,安装后的DirectX SDK中并没有Borland C+库。这就需要用户自己来处理这一恼人的问题了。我们知道,DirectX 5 SDK是以一个IDX5SDK.EXE发布的, 运行IDX5SDK后,它先将压缩的文件全部解压到某个目录下,然后再运行该目录下的SETUP.EXE安装DirectX SDK。实际上,在解压后的目录中包含了一个D:DX5SDKSDKLIBBORLANDC目录, 该目录下就是Borland C+的链接库文件。但在SETUP安装时,安装程序并没有把该目录复制到安装目录中。解决方法很简单,即SETUP安
9、装完成后,再建立一个C:DX5SDKSDKLIBBorland,将目录D:DX5SDKSDKLIBBorland下的所有文件都复制到C:DX5SDKSDKLIBBorland目录下。然后在Borland C+ 5.0的集成环境中如同配置Visual C+5.0那样配置工程文件。2、DirectDraw实例要使用DirectDraw,首先必须创建DirectDraw对象的一个实例来表征计算机上的显示适配卡,然后使用接口方法来处理对象。另外还需要创建一个或多个DirectDrawSurface对象的实例来显示游戏。DDEX1首先创建一个DirectDraw对象,再创建一个主表面(primary s
10、urface)和一个后台缓冲区(back buffer),然后在表面之间转换。DDEXx例子都是用C+写成的,如果你使用的是C编译器,必须将代码做适当改动, 至少要加入虚表和指向接口方法的指针。2.1 初始化DirectDraw对象DDEX1 程序在doInit函数包含了DirectDraw 的初始化代码:/ 创建主 DirectDraw 对象ddrval = DirectDrawCreate( NULL, &lpDD, NULL );if( ddrval = DD_OK )/ 获取独占模式ddrval = lpDD-SetCooperativeLevel( hwnd, DDSCL_EXCLU
11、SIVE| DDSCL_FULLSCREEN );if(ddrval = DD_OK )ddrval = lpDD-SetDisplayMode(640, 480, 8 );if( ddrval = DD_OK )/创建带有一个后台缓冲区的主表面ddsd.dwSize = sizeof( ddsd );ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |DDSCAPS_FLIP|DDSCAPS_COMPLEX;ddsd.dwBackBufferCount =
12、1;ddrval = lpDD-CreateSurface( &ddsd, &lpDDSPrimary, NULL );if( ddrval = DD_OK )/获取后台缓冲区的指针ddscaps.dwCaps = DDSCAPS_BACKBUFFER;ddrval=lpDDSPrimary-GetAttachedSurface(&ddscaps,&lpDDSBack);if( ddrval = DD_OK )/ 画出一些文本if(lpDDSPrimary-GetDC(&hdc) = DD_OK)SetBkColor( hdc, RGB( 0, 0, 255 ) );SetTextColor(
13、 hdc, RGB( 255, 255, 0 ) );TextOut( hdc, 0, 0, szFrontMsg, lstrlen(szFrontMsg) );lpDDSPrimary-ReleaseDC(hdc); if(lpDDSBack-GetDC(&hdc) = DD_OK) SetBkColor( hdc, RGB( 0, 0, 255 ) );SetTextColor( hdc, RGB( 255, 255, 0 ) );TextOut( hdc, 0, 0, szBackMsg, lstrlen(szBackMsg) );lpDDSBack-ReleaseDC(hdc);/ 创
14、建翻转页面的计时器if(SetTimer( hwnd, TIMER_ID, TIMER_RATE, NULL ) ) return TRUE;wsprintf(buf, Direct Draw Init Failed (%08lx)n, ddrval );.下面详细说明初始化DirectDraw对象的创建和准备表面的每一步骤。2.2 创建DirectDraw对象创建DirectDraw对象的一个实例,应该用DirectDrawCreate API函数, 也可以用COM中的CoCreateInstance函数。DirectDrawCreate用一个全局统一标志符GUID(Globally Uni
15、que IDentifier)来表征显示设备,在大多数情况下GUID为NULL(使用系统的缺省显示设备,既“空设备”);指针指向DirectDraw对象的地址;第三个参数总是NULL(供将来扩展使用)。下述代码表明了如何创建一个DirectDraw对象,并且检验是否成功。ddrval = DirectDrawCreate( NULL, &lpDD, NULL );if( ddrval = DD_OK )/ lpDD 是合法的DirectDraw对象else/ DirectDraw对象不能被创建2.3 设置显示模式设置DirectDraw应用程序的显示模式需要两步:首先调用IDirectDraw
16、:SetCooperativeLevel方法来设定该模式下的要求,一旦确定了要求,再用IDirectDraw:SetDisplayMode方法来选择显示辨率。在改变显示分辨率之前,还必须通过IDirectDraw:SetCooperativeLevel方法来指定DDSCL_EXCLUSIVE和DDSCL_FULLSCREEN标志。这样能使游戏程序完全控制显示设备,其它的应用程序不能同时共享显示设备。DDSCL_FULLSCREEN标志表示将程序设为全屏模式。下面的代码显示了如何使用IDirectDraw:SetCooperativeLevel方法:HRESULT ddrval;LPDIRECT
17、DRAW lpDD; / already created by DirectDrawCreateddrval = lpDD-SetCooperativeLevel( hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);if( ddrval = DD_OK )/ 全屏独占方式设置成功else/ 调用不成功,但程序仍然能继续运行如果IDirectDraw:SetCooperativeLevel不返回DD_OK,你仍然可以运行该程序,但不是全屏模式,有时可能产生一些无法预料的错误。因此你应该显示一条错误信息,让用户知道发生了什么事,由用户来决定是否继续运行游戏。使用I
18、DirectDraw:SetCooperativeLevel时,必须向窗口传送一个句柄(HWND),让窗口决定何时非正常地终止应用程序。例如,若发生了GP错误或GDI被翻转到了后台缓冲区,用户就无法访问当前屏幕。为了避免这种情况,DirectDraw有一个后台等待进程,它俘获所有发往该窗口的消息,用这些消息来确定应用程序何时终止。如果创建了新的窗口,必须确定该窗口为活动的,否则,就会有一系列的事件无法继续工作。2.4 改变显示模式一旦选择了应用程序的工作模式,就可以使用IDirectDraw:SetDisplayMode方法来改变显示模式,下面的代码将显示模式设置成640x480x256:HR
19、ESULT ddrval;LPDIRECTDRAW lpDD; / already createdddrval = lpDD-SetDisplayMode( 640, 480, 8 );if( ddrval = DD_OK )/ 改变模式成功else/ 显示模式不能改变/ 系统可能不支持该模式当设定显示模式时,应该确保如果用户的设备不支持更高的分辨率,应用程序应该返回系统支持的标准模式。如果显示示配卡不支持设计的分辨率,IDirectDraw:SetDisplayMode返回一个DDERR_INVALIDMODE错误值。因此,在设置分辨率时,应该先用IDirectDraw:EnumDispla
20、yMode方法检测用户的显示设备的性能。2.5 创建可翻转表面(Flippable Surface)设定了显示模式后,必须创建放置应用程序的表面。在DDEX1例中,我们使用IDirectDraw:SetCooperativeLevel方法将程序设成独占全屏模式,然后可以创建翻转表面。如果使用IDirectDraw:SetCooperativeLevel设成DDSCL_NORMAL模式,就只能创建块写方式的表面了。2.6 定义表面要求创建可翻转表面的的第一步是在DDSURFACEDESC结构中定义表面的要求。下面的代码描述了结构的定义及创建可翻转表面所需要的标志:/ 创建带有一个后台缓冲区的主表
21、面ddsd.dwSize = sizeof( ddsd );ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP |DDSCAPS_COMPLEX;ddsd.dwBackBufferCount = 1;例中,成员变量dwSize被设为DDSURFACEDESC结构的大小。dwFlags标志指定DDSURFACEDESC结构中哪些域可存放有效信息。在DDEX1例中,dwFlags指出了需要使用DDSCAPS结构(DDSD_CAPS)并创
22、建一个后台缓冲区(DDSD_BACKBUFFERCOUNT)。dwCaps指出DDSCAPS结构会用到的标志,本例中它指定了一个主表面(DDSCAPS_PRIMARYSURFACE),一个翻转表面(DDSCAPS_FLIP)和一个复杂表面(DDSCAPS_COMPLEX)。最后,程序指定了一个后台缓冲区。后台缓冲区是背景图像和人物将写入的位置,它可以转化为主表面。本例中,后台缓冲区的数目为1,事实上,只要有足够的显示内存,可以创建任意多个后台缓冲区,一般每1M的显示内存只能用来创建一个后台缓冲区。表面的内存既可以是显示内存,也可以是系统内存。DirectDraw在使用完了显示内存时(例如在仅有
23、1M的显示内存创建了2个后台缓冲区)会自动使用系统内存。你可以通过将DDSCAPS结构中的dwCaps设定为DDSCAPS_SYSTEMMEMORY或DDSCAPS_VIDEOMEMORY来指定只使用系统内存或只使用显示内存。如果指定了DDSCAPS_VIDEOMEMORY又没有足够的显示内存来创建表面,IDirectDraw:CreateSurface将返回一个DDERR_OUTOFVIDEOMEMORY错误。2.7 创建表面填完了DDSURFACEDESC结构,就可以使用该结构和lpDD了,lpDD是用DirectDrawCreate 方法创建的DirectDraw对象的指针,下面的代码显
24、示了这一过程:ddrval = lpDD-CreateSurface( &ddsd, &lpDDSPrimary, NULL);if( ddrval = DD_OK )/ lpDDSPrimary points to new surfaceelse/ surface was not createdreturn FALSE;如果调用成功,IDirectDraw:CreateSurface函数就返回指向主表面的指针lpDDSPrimary。若主表面指针可用,就可以调用IDirectDrawSurface:GetAttachedSurface 方法取得后台缓冲区的指针,如下所示:ddscaps.dw
25、Caps = DDSCAPS_BACKBUFFER;ddrval = lpDDSPrimary-GetAttachedSurface( &ddcaps, &lpDDSBack);if( ddrval = DD_OK )/ lpDDSBack points to the back bufferelsereturn FALSE;如果IDirectDrawSurface:GetAttachedSurface调用成功,通过提供主表面的地址和设置DDSCAPS_BACKBUFFER标志,lpDDSBack参数就指向后台缓冲区。2.8 着色表面创建了主表面和后台缓冲区后,DDEX1使用标准的Windows
26、 GDI函数将一些文本提交到主表面和后台缓冲区,代码如下:if (lpDDSPrimary-GetDC(&hdc) = DD_OK)SetBkColor( hdc, RGB( 0, 0, 255 ) );SetTextColor( hdc, RGB( 255, 255, 0 ) );TextOut( hdc, 0, 0, szFrontMsg, lstrlen(szFrontMsg);lpDDSPrimary-ReleaseDC(hdc);if (lpDDSBack-GetDC(&hdc) = DD_OK)SetBkColor( hdc, RGB( 0, 0, 255 ) );SetTextC
27、olor( hdc, RGB( 255, 255, 0 ) );TextOut( hdc, 0, 0, szBackMsg, lstrlen(szBackMsg);lpDDSBack-ReleaseDC(hdc);例中使用了IDirectDrawSurface:GetDC方法来设备上下文的句柄且锁定该表面。如果不想使用要求句柄的Windows函数,还可以使用IDirectDrawSurface:Lock和IDirectDrawSurface:Unlock方法来锁定和解锁后台缓冲区。锁定表面的内存(不管是整个表面还是其中的一部分)能确保你的应用程序和系统不会同时访问这块内存。另外,除非给内存解锁
28、,程序不能翻转表面。本例在锁定表面后使用Windows GDI函数SetBkColor来设置背景颜色,使用SetTextColor来设置文本颜色,然后使用TextOut将文本输出到表面。当文本写入缓冲区后,例中使用了IDirectDrawSurface:ReleaseDC方法来解锁表面并释放句柄。良好的习惯是,向后台缓冲区写数据完成后,马上调用IDirectDrawSurface:ReleaseDC或IDirectDrawSurface:Unlock。一般来讲,当向表面写数据时,该表面就是后台缓冲区,然后将缓冲区翻转成主表面显示出来。在DDEX1中,第一次翻转表面之前有一个重要的延迟。于是DD
29、EX1就将数据写入主缓冲区,避免开始显示时有太长的时间间隔。后面将会讲到,DDEX1只在WM_TIMER期间向后台写数据。初始化函数或标题头可能会写入主缓冲区。应该注意的是,一旦使用IDirectDrawSurface:Unlock对表面解锁,指向表面的指针就变成无效,必须再次使用IDirectDrawSurface:Lock方法才能获取该表面内存的有效指针。2.9 写表面及翻转表面完成了初始化后,DDEX1开始处理消息循环。在消息循环的过程中,完成锁定后台缓冲区 写入新的文本 解锁后台缓冲区 翻转表面的过程。WM_TIMER包含了写数据和翻转表面的大部分代码。WM_TIMER消息的前半部分用
30、于向后台缓冲区写数据,“phase”变量决定是写主缓冲区消息还是写后台缓冲区消息。如果phase为1,表示写主缓冲区的消息,然后将phase改变为0;若为0,表示写后台缓冲区的消息,然后将phase改变为1。注意:两种情况中的消息都是写向后台缓冲区。后台缓冲区写入了消息后,使用IDirectDrawSurface:ReleaseDC方法解锁。下面的代码实现了这一点:case WM_TIMER:/ Flip surfacesif( bActive )if (lpDDSBack-GetDC(&hdc)= DD_OK)SetBkColor( hdc, RGB( 0, 0, 255 ) );SetTe
31、xtColor( hdc, RGB( 255, 255, 0 ) );if( phase )TextOut( hdc, 0, 0, szFrontMsg, lstrlen(szFrontMsg) );phase = 0;elseTextOut( hdc, 0, 0, szBackMsg, lstrlen(szBackMsg) );phase = 1;lpDDSBack-ReleaseDC(hdc);表面内存解锁后,使用IDirectDrawSurface:Flip方法将后台缓冲区翻转成主表面,代码如下:while( 1 )HRESULT ddrval;ddrval = lpDDSPrimary
32、-Flip( NULL, 0 );if( ddrval = DD_OK )break;if( ddrval = DDERR_SURFACELOST )if(ddrval = lpDDSPrimary-&gval != DD_OK )break;if( ddrval != DDERR_WASSTILLDRAWING )break; 例中,lpDDSPrimary指明了主表面及其后台缓冲区。调用IDirectDrawSurface:Flip方法后,主表面和后表面交换。调用成功后,返回DD_OK,程序终止While循环;如果返回DDERR_SURFACELOST, 表明可能是表面丢失,需要用IDir
33、ectDrawSurface:Restore方法恢复该表面,若恢复成功,就再一次调用IDirectDrawSurface:Flip方法;如果失败,程序终止While循环并返回一个错误值。另外,如前所述,即使调用IDirectDrawSurface:Flip成功,交换也不是立即完成,它将等到系统中在此之前的表面交换都完成后才进行。例如,前一次的表面翻转还未发生时,IDirectDrawSurface:Flip就返回DERR_WASSTILLDRAWING。本例中,IDirectDrawSurface:Flip继续循环直到返回DD_OK。2.10 释放DirectDraw对象当按了F12后,DDE
34、X1程序在退出之前先处理WM_DESTROY消息,该消息调用了finiObjects函数,而finiObjects函数包含了所有的Iunknown Release的调用,代码如下:static void finiObjects( void )if( lpDD != NULL )if( lpDDSPrimary != NULL)lpDDSPrimary-Release();lpDDSPrimary = NULL;lpDD-Release();lpDD = NULL; /* finiObjects */程序检测DirectDraw对象的指针(lpDD)和DirectDrawSurface对象的指针
35、(lpDDSPrimary)是否等于NULL,本例中显然不为NULL。然后DDEX1调用IDirectDrawSurface:Release方法将DirectDrawSurface对象的参考值减1,这将会使得其参考值变为0,DirectDrawSurface 对象就被释放了,DirectDrawSurface的指针被设为NULL值,然后撤消。程序又调用IDirectDraw:Release就DirectDraw对象的参考值减1变为0,释放DirectDraw对象及其指针。上述的DDEX1是DirectDraw最基本的应用,它首先创建DirectDraw对象和DirectDrawSurface对
36、象,创建一个主表面及其后台缓冲区,将文本输出到后台缓冲区,然后转化表面。第二个例子DDEX2扩展了DDEX1的功能,它可以将一个位图文件调入后台缓冲区。第三个例子DDEX3则更进一步,除了一个主表面及后台缓冲区外, 还创建了两个屏外表面,将位图调入每一个屏外表面, 然后使用IDirectDrawSurface:BltFast方法将一个屏外表面的内容位块传输到后台缓冲区,然后翻转表面并将另一个屏外表面的内容位块传输到后台缓冲区。下面将详细讨论这些功能。2.11 将位图调入表面如DDEX1中一样,doInit函数是DDEX2的初始化函数,两者的实质一样,一直到下面的代码:lpDDPal = DDL
37、oadPalette(lpDD, szBackground);if(lpDDPal = NULL)goto error;ddrval = lpDDSPrimary-SetPalette(lpDDPal);if( ddrval != DD_OK )goto error; / load a bitmap into the back buffer.ddrval = DDReLoadBitmap(lpDDSBack, szBackground);if( ddrval != DD_OK )goto error;代码的第一行从函数DDLoadPalette返回一个值,该函数在C:DX5SDKSDKSAMP
38、LESMISC中的Ddutil.cpp文件中,因此编译DDEX2时需要将Ddutil.cpp和Ddutil.h加入过程。大部分的DirectDraw程序都需要该文件。在DDEX2中, DDLoadPalette函数从Back.bmp文件中创建一个DirectDrawPalette对象。DDLoadPalette函数首先检查用于创建调色板的文件或资源十分存在,如果不存在,就创建一个缺省调色板。在DDEX2中, 它从位图文件中抽取调色板信息并存储在由ape指向的结构,然后如下创建DirectDrawPalette对象:lpdd-CreatePalette(DDPCAPS_8BIT, ape, &d
39、dpal, NULL);return ddpal;当IDirectDraw:CreatePalette方法返回时,ddpal就指向该DirectDrawPalette对象。ape是指向一个结构的指针, 该结构能包含2/4/16/256个线性的实体,实体的数目由IDirectDraw:CreatePalette调用的dwFlags参数决定。本例中,dwFlags设定为DDPCAPS_8BIT,这表示该结构中有256个实体,每个实体有四个字节(红色、绿色、蓝色和标志字节)。2.12 设置调色板,将位图调入后台缓冲区创建了调色板之后,可以通过调用IDirectDrawSurface:SetPalet
40、te方法将DirectDrawPalette 对象的指针ddpal传送给主表面,代码如下:ddrval = lpDDSPrimary-SetPalette(lpDDPal);if( ddrval != DD_OK )/ SetPalette failed调用了IDirectDrawSurface:SetPalette方法之后,DirectDrawPalette对象就和DirectDrawSurface 对象挂( hook)在一起了,需要改变调色板时,只需要创建一个新的调色板对其进行设置就可以了。DirectDrawPalette对象同DirectDrawSurface对象挂在一起后,DDEX2
41、使用以下代码将Back.bmp文件装入后台缓冲区:/ load a bitmap into the back buffer.ddrval = DDReLoadBitmap(lpDDSBack, szBackground);if( ddrval != DD_OK )/ Load failedDDReLoadBitmap是Ddutil.cpp中的另一个函数,它将位图从文件或资源中调入已经存在的DirectDraw表面。在本例中,它将szBackground指向的Back.bmp装入lpDDSBack指向的后台缓冲区。DDReLoadBitmap调DDCopyBitmap函数将文件拷贝到后台缓冲区并
42、延展为适当的尺寸。DDCopyBitmap函数将位图拷入内存,使用GetObject函数获取位图的大小,然后用下述代码获取将要放置位图的后台缓冲区的大小:/ get size of surface.ddsd.dwSize = sizeof(ddsd);ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;lpdds-GetSurfaceDesc(&ddsd);ddsd是指向DDSURFACEDESC结构的指针,该结构储存了DirectDraw表面的当前描述。DDSURFACEDESC结构的成员描述了由DDSD_HEIGHT 和DDSD_WIDTH指定了表面的高和宽。IDirectDrawSurface:GetSurfaceDesc方法使用合适的值调入结构,例中高为480,宽为640。DDCopyBitmap函数锁定表面并将位图拷贝到后台缓冲区,