三、DirectDraw的要素
DirectDraw中最基本的要素有协作等级、显示模式、DirectDraw对象、表面、调色板、剪切板等,下面对各个要素逐一进行介绍。
1、协作等级(Cooperative Level)
协作等级描述了DirectDraw如何同显示交互及对那些可能影响显示的事件的反应。你可以使用IDirectDraw2::SetCooperativeLevel方法来设置协作等级。在大多数的情况下,你可以使用协作等级来确定应用程序是否运行在独占全屏模式或窗口模式。DirectDraw协作等级还具有以下作用:
.使DirectDraw能够使用X模式(Mode X)分辨率。
.如果用户按了ctrl + alt + del,禁止DirectDraw释放对显示的独占控制和重新启动(仅在独占模式下)。
.使DirectDraw能够最大化和最小化以响应激活的事件。
标准的协作等级指明了DirectDraw程序是一个窗口应用程序。在这种情况下,你不能改变主表面的调色板和执行页翻转。另外,你还不
能调用那些对显示或视频内存影响程度大的方法,如 IDirectDraw2::Compact等。
在全屏独占协作等级下,你可以使用硬件的所有性能,设置惯用调色板和动态调色板,改变显示分辨率,紧凑内存和实现页翻转等。全屏独占模式不禁止其它的应用分配表面,也不禁止它们使用DirectDraw和GDI,但禁止其它发应用改变显示分辨率和调色板。
因为应用程序可以在多窗口使用DirectDraw,所以当应用要求工作在DDSCL_NORMAL模式时,IDirectDraw2:: SetCooperativeLevel方法并不需要一个指定的窗口句柄。将NULL传递给窗口句柄,所有的窗口都能同时工作在标准窗口模式下。
2、显示模式(Display Modes)
显示模式是对显示硬件从主表面传送给显示器的图象的大小和位深度(bit-depth)的描述。显示模式可以刻画为:宽、高和位深度。例如,大多数的显示适配器都能显示宽640个像素,高480个像素,每个像素的颜色值为8位的数据图象,该显示模式就几为640x480x256。
要想得到更大的分辨率或位深度,就需要更多的显示内存。
显示模式有两种类型:调色模式和非调色模式。对于调色显示模式,每一个像素都是一个指向相关调色板的索引值。显示模式的位深度决定了调色板中能够具有的颜色的数目。例如,对于8位调色显示模式,每一个像素都是一个从0到255的值。在这种显示模式下,调色板可以包含256中颜色。非调色显示模式则不使用调色板,该模式下的位深度指明了用于描述一个像素的总的位数。
主表面和主翻转链中的任何表面都应该和显示模式的大小、位深度和像素格式匹配。
2.1、检测所支持的显示模式
因为显示硬件的不同,并不是所有的设备都支持所有的显示模式。要检测系统所支持的显示模式,需要调用IDirectDraw2::EnumDisplayModes方法。通过设定适当的值和标志, IDirectDraw2::EnumDisplayModes方法可以列出所有支持的显示模式,也能判断是否支持某一指定的显示模式。
该方法的第一个参数dwFlags控制方法其它的选项,通常可以将dwFlags设为0来忽略其它的选项;第二个参数lpDDSurfaceDesc是用以描述所给显示模式的结构 DDSURFACEDESC的地址,一般将其设为NULL可以列出所有的模式。第三个参数lpContext是一个指针,DirectDraw需要将该指针传递给回调函数,如果不需要在回调函数中有额外的数据,可将其设为NULL。最后一个参数是lpEnumModesCallback,它是DirectDraw对于每一种支持的显示模式调用的回调函数的地址。
在调用IDirectDraw2::EnumDisplayModes方法时提供的回调函数必须同EnumModesCallback函数的原型相匹配。对于硬件所支持的每一种显示模式,DirectDraw调用你的回调函数来传递两个参数,第一个是DDSURFACEDESC结构的地址,该结构描述了一种支持的显示模式;
第二个参数是调用IDirectDraw2::EnumDisplayModes时指定的应用定义的数据的地址。
通过检查DDSURFACEDESC结构的值来得到它所描述的显示模式,其中关键的成员是dwWidth、dwHeight、和ddpfPixelFormat。dwWidth和dwHeight描述了显示模式的大小;ddpfPixelFormat是一个DDPIXELFORMAT结构,它包含了有关位深度的信息。
DDPIXELFORMAT结构包含了描述显示模式位深度的信息,并且说明该显示模式是否使用调色板。如果dwFlags成员包含了 DDPF_PALETTEINDEXED1、DDPF_PALETTEINDEXED2、DDPF_PALETTEINDEXED4或DDPF_PALETTEINDEXED8标志,该显示模式的位深度就是1、2、4或8位,并且每个像素都是一个相公调色板的索引。如果dwFlags包含了DDPF_RGB标志,该显示模式就是非调色显示模式,并且其位深度由DDPIXELFORMAT结构中的dwRGBBitCount成员提供。
2.2、设置显示模式
你可以调用IDirectDraw2::SetDisplayMode方法来设置显示模式。该方法接受四个参数来设置显示模式的分辨率的大小、位深度和刷新率。它使用第五个参数来指定给定模式的特殊选项,目前仅用于13模式和X模式320x200x8中。
你可以指定期望得到的显示模式的位深度,但不能指定显示硬件用于该位深度的像素格式。要检测显示硬件用于当前位深度的RGB位屏蔽,可以在设置显示模式后调用 IDirectDraw2::GetDisplayMode方法。如果当前显示模式是非调色模式,你可以通过检查dwRBitMask、dwGBitMask和dwBBitMask中的值来获得教正的红、绿、蓝颜色位。
你还可以通过多个应用来改变显示模式,只要它们都共享同一个显示卡。也可以改变位深度,只要应用程序能独占式地访问DirectDraw对象,所有的DirectDrawSurface对象在显示模式改变后都会释放表面内存,所以在更改了显示模式之后,必须使用 IDirectDrawSurface3::Restore方法重新为对象分配表面内存。
2.3、恢复显示模式
如果显示模式是通过调用IDirectDraw2::SetDisplayMode方法(而不是 IDirectDraw::SetDisplayMode方法)完成的,你可以调用IDirectDraw2::RestoreDisplayMode方法来恢复到原来的显示模式。如果应用程序是独占式的协作等级,当你将应用程序的协作等级设回标准时,显示模式就会自动恢复为原来的模式。如果使用了DirectDraw接口,你必须显式地恢复显示模式。
2.4、X模式和13模式
DirectDraw支持X模式和13模式两类显示模式。13模式是320x200,每个像素是8位的调色模式,其16进制BIOS模式号为13。模式X是从标准的VGA 13模式衍生出来的混合模式,该模式允许使用256KB的显示内存(13模式仅允许实用64KB的显示内存)。
在Windows95下,DirectDraw对所有的显示卡提供两种X模式(320x200x8和320x240x8)。一些显示卡也支持线性低分辨率模式,在线性低分辨率默默上下,主表面能够被锁定也能直接访问,这在X模式下是不允许的。
只要应用程序调用IDirectDraw2::SetCooperativeLevel方法时使用了 DDSCL_ALLOWMODEX,、DDSCL_FULLSCREEN和DDSCL_EXCLUSIVE标志,X模式就可用。如果没有指定 DDSCL_ALLOWMODEX标志, IDirectDraw2::EnumDisplayModes方法就不能列出X模式,当请求X模式时,对IDirectDraw2::SetDisplayMode方法的调用就会失败。
当应用程序在X模式时不能使用IDirectDrawSurface3::Lock或IDirectDrawSurface3::Blt方法锁定主表面或向主表面块写数据,也不能在主表面或屏幕DC的GDI使用 IDirectDrawSurface3::GetDC方法。X模式由DDSCAPS肩胛骨中的DDSCAPS_MODEX标志来指定,DDSCAPS结构是调用IDirectDrawSurface3::GetCaps和IDirectDraw2::EnumDisplayModes方法返回的DDSURFACEDESC结构中的一部分。
2.5、高分辨率和真彩色
DirectDraw支持由显示设备驱动程序支持的所有的屏幕分辨率和位深度。DirectDraw允许应用程序改变显示模式到任一种计算机显示驱动程序支持的显示模式,包括24位和32位(真彩色)模式。
DirectDraw也支持真彩色表面的HEL位块传输。如果显示设备驱动程序在这些分辨率下支持位块传输,显示内存──显示内存的位块传输将使用硬件块写方式,否则将使用HEL来提高位块传输的性能。
Windows95和NT允许你指定正在使用的显示器的类型。DirectDraw检查已知显示模式的列表,如果DirectDraw检测到要求的模式同该显示器不兼容,对 IDirectDraw2::SetDisplayMode方法的调用将会失败。当你调用方法IDirectDraw2::EnumDisplayModes时,只有显示器支持的模式才会被列出来。
3、DirectDraw对象
DirectDraw对象是所有DirectDraw应用程序的核心,也是Direct3D应用程序的一个有机组成部分。DirectDraw对象是你创建的第一个对象,通过该对象再创建其它相关对象。一般通过调用DirectDrawCreate函数来创建一个DirectDraw对象,该函数返回一个IDirectDraw接口。
DirectDraw对象表征了显示设备。如果该显示设备支持硬件加速功能,DirectDraw对象还能利用硬件的加速功能。每一个DirectDraw对象都能处理显示设备和创建依赖于该DirectDraw对象的表面对象、调色板对象和剪切板对象。例如,创建一个表面需要调用IDirectDraw2::CreateSurface方法,若要将一个调色板对象附在表面上,需要调用 IDirectDraw2::CreatePalette方法。另外, IDirectDraw2接口还包含了类似的创建剪切板对象的方法。
你可以同时创建一个DirectDraw对象的多个实例。最简单的例子是在Windows95系统中使用双显示器。尽管Windows95目前不支持多显示器,但为每一个显示设备写DirectDraw硬件抽象层是可能的。Windows95和GDI能够识别的显示设备在创建缺省的DirectDraw对象的实例时将会被用到。Windows95和GDI不能识别的显示设备能被另一个独立的DirectDraw对象所表征,该对象必须用第二个显示设备的全局统一标识符GUID(Globally Unique Identifier)来创建,此GUID可以通过函数DirectDrawEnumerate获得。
DirectDraw对象管理它所创建的其它所有对象。它控制缺省调色板、Color Key和硬件显示模式,它还对已经分配的资源和保留的资源做上标记。
新版本的DirectDraw在以前的版本中增加了新的内容。IDirectDraw2接口通过增加了IDirectDraw2::GetAvailableVidMem方法而对IDirectDraw接口进行了扩展。该方法使你能够查询显示设备总的可用视频内存及其中有多少内存供一个指定的表面使用。
IDirectDraw2::SetCooperativeLevel方法同IDirectDraw2::SetDisplayMode之间的交互作用同IDirectDraw接口中的这些方法有所不同。如果应用程序使用IDirectDraw接口设置全屏独占方式的协作等级并且改变显示模式,当返回标准协作等级时,显示模式不会自动恢复,你必须调用
IDirectDraw::RestoreDisplayMode方法来显式地恢复。如果使用了IDirectDraw2接口,对RestoreDisplayMode的调用就不是必须的。然而IDirectDraw2::RestoreDisplayMode方法不支持显式地恢复原先的显示模式。
作为DirectX的基础的COM指明了一个对象可以通过增加新的接口来提供新的功能而不影响其向后兼容性。因此IDirectDraw2接口就可以代替IDirectDraw接口。新的接口可以通过IDirectDraw::QueryInterface方法获得,如下面的C++代码所示:
// Create an IDirectDraw2 interface.
LPDIRECTDRAW lpDD;
LPDIRECTDRAW2 lpDD2;
ddrval = DirectDrawCreate(NULL, &lpDD, NULL);
if(ddrval != DD_OK)
return;
ddrval = lpDD->SetCooperativeLevel(hwnd,
DDSCL_NORMAL);
if(ddrval != DD_OK)
return;
ddrval = lpDD->QueryInterface(IID_IDirectDraw2,
(LPVOID *)&lpDD2);
if(ddrval != DD_OK)
return;
例中创建了一个DirectDraw对象,然后调用IDirectDraw接口的 IUnknown::QueryInterface方法创建一个IDirectDraw2接口。
获取了一个IDirectDraw2接口后,就可以调用它的方法以利用其新特性的优点。因为一些方法可能会在新版本的接口中有所变化,因此混合使用不同版本的接口(例如IDirectDraw和IDirectDraw2)可能会导致不可预测的错误。
3.1、每一进程中的多个DirectDraw对象
DirectDraw允许在一个进程中多次调用DirectDrawCreate函数。每次调用之后对每个唯一独立DirectDraw对象都返回一个唯一的独立的接口。每个DirectDraw对象都可以用于所需,对象之间没有依赖关系。每个对象就象是单独在进程中创建的一样。
每一个DirectDraw对象创建的DirectDrawSurface对象、DirectDrawClipper 对象和DirectDrawPalette对象都不能被其它的DirectDraw对象使用,因为这些对象在其父对象DirectDraw对象撤消时都会自动撤消。
使用DirectDrawCreateClipper函数创建的DirectDrawClipper对象是一例外,这种情况下的DirectDrawClipper对象独立于其它任意的DirectDraw对象并能用于一个或多个DirectDraw对象。
3.2、用CoCreateInstance创建DirectDraw对象
你也可以不使用DirectDrawCreate函数而使用CoCreateInstance函数和 IDirectDraw2::Initialize方法来创建DirectDraw对象。下面是用CoCreateInstance函数创建DirectDraw对象的步骤:
i. 在应用程序开始时调用CoInitialize初始化COM:
if (FAILED(CoInitialize(NULL)))
return FALSE;
ii. 使用CoCreateInstance函数和IDirectDraw2::Initialize方法创建DirectDraw对象:
ddrval = CoCreateInstance(&CLSID_DirectDraw,
NULL, CLSCTX_ALL, &IID_IDirectDraw2, &lpdd);
if(!FAILED(ddrval))
ddrval = IDirectDraw2_Initialize(lpdd, NULL);
在对CoCreateInstance的调用中,第一个参数CLSID_DirectDraw是DirectDraw驱动对象类的类标志符, IID_IDirectDraw2参数指明了要创建的DirectDraw接口,lpdd参数指向获取的DirectDraw对象。如果调用成功,函数将返回一贯未初始化的DirectDraw对象。
在使用DirectDraw对象之前,还必须调用IDirectDraw2::Initialize方法,该方法不使用GUID参数(DirectDrawCreate函数则需要只要)。
DirectDraw对象初始化后就可以使用也可以释放该对象,就好象是该对象是由DirectDrawCreate函数创建的一样。若在调用IDirectDraw2::Initialize方法之前使用同DirectDraw对象相关的方法将会导致一个DDERR_NOTINITIALIZED错误。
在关闭应用程序前,应该使用CoUninitialize函数关闭COM,形式如下:
CoUnitialize();