荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Peter (小飞侠), 信区: Program
标  题: DirectX5.0游戏编程指南(三)
发信站: BBS 荔园晨风站 (Thu Jan 21 22:34:55 1999), 转信


二、DirectDraw的体系
    多媒体软件要求高性能的图象。通过DirectDraw,Microsoft使得对图象
敏感性的程序在速度和效率上都比在GDI上有了大大是提高,同时又保持了设
备无关性,DirectDraw提供的工具能完成如下的关键任务:
        .操作多个显示表面
        .直接存取视频内存
        .页翻转
        .后台缓冲
        .调色板的管理
        .剪切
另外,DirectDraw在运行时允许你查询显示硬件的性能然后提供显示设备所能
够支持的最佳效能。
    DirectDraw提供了基于COM的服务接口,使用最多的是IDirectDraw2、
IDirectDrawSurface3、IDirectDrawPalette、IDirectDrawClipper和
IDirectDrawVideoPort。除了这些接口外,DirectDraw继续支持以前版本的接口。
    DirectDraw对象表征了显示适配器,通过IDirectDraw和IDirectDraw2接口揭
示其方法。在大多数情况下,你可以使用DirectDrawCreate函数来创建一个
DirectDraw对象,也可以使用COM函数CoCreateInstance。
    创建了DirectDraw对象后,你可以调用IDirectDraw2::CreateSurface方法为
该对象创建表面。表面表征了显示硬件上的内存,既可以是视频内存,也可以是
系统内存。DirectDraw通过其它的接口扩展了对调色板、剪切及视频端口的支持。

1、DirectDraw的对象类型
    DirectDraw对象既可以是一个单独的对象,也可以是几个对象的组合。最新
版本的DirectDraw有以下类型的对象:

1.1、DirectDraw对象
    DirectDraw对象是DirectDraw应用程序的核心,它是你创建的第一个对象。
创建了DirectDraw对象后,可以在它的基础上创建其它所有相关的对象。创建
DirectDraw对象的函数是DirectDrawCreate。

1.2、DirectDrawSurface对象
    DirectDrawSurface对象表征了一块内存区域,在该区域的数据将作为图象
显示在屏幕上或移动到其它表面上。创建DirectDrawSurface对象的方法是
IDirectDraw2::CreateSurface,其它相关的方法可通过接口 IDirectDrawSurface,
IDirectDrawSurface2和IDirectDrawSurface3得到。

1.3、DirectDrawPalette对象
    DirectDrawPalette对象(有时也指的是“palette”)表征了一个用于表
面的16色或256色的索引调色板,它包含了一系列描述同表面相关的RGB颜色索引值。
创建DirectDrawPalette对象的方法是IDirectDraw2::CreatePalette。可以从接口
IDirectDrawPalette获取其它的方法。

1.4、DirectDrawClipper对象
    DirectDrawClipper对象(有时指的是“clipper”)帮助你禁止向表面的
某一位置或超出表面的位置块写数据,创建DirectDrawClipper的方法是
IDirectDraw2::CreateClipper,其它相关的方法可从接口 IDirectDrawClipper
中获取。

1.5、DirectDrawVideoPort对象
    DirectDrawVideoPort对象表征了一些系统只具有的视频端口硬件,
该硬件允许直接访问帧缓冲区而不需要访问CPU或使用PCI总线。你可以先指定
IDDVideoPortContainer标志,再调用QueryInterface方法来创建
DirectDrawVideoPort对象。其它相关的方法可从接口IDDVideoPortContainer和
IDirectDrawVideoPort中获得。

2、硬件抽象层HAL(Hardware Abstraction Layer)
    DirectDraw通过硬件抽象层HAL提供了设备无关性。HAL是一种特殊的
设备接口,由设备生产厂商提供。DirectDraw利用HAL直接在显示设备上工
作。应用程序并不同HAL交互。设备制造厂商以Windows95下的16位或32位
代码的形式提供HAL,在WindowsNT下,HAL是32位代码。HAL可以是显示驱
动程序的一部分,也可以是通过制造商定义的私有接口同显示驱动程序通
信的动态链接库DLL。DirectDraw HAL由芯片、板卡或OEM(Original
Equipment Manufacturer)提供。HAL只提供了设备相关性代码,执行时没
有仿真。如果一种功能不被硬件支持,HAL将不把该功能作为硬件性能的一
部分处理。另外,HAL不对参数进行证实,DirectDraw会在调用HAL之前
完成参数的证实工作。

3、软件仿真(Software Emulation)
    当硬件HAL不支持某一特性时,DirectDraw就尝试用软件来仿真这一
特性。该仿真功能是由硬件仿真层HEL(Hardware-Emulation Layer)提供
的。显然,软件仿真并不等同于硬件本身具有的特性。你可以用
IDirectDraw2::GetCaps方法来查询硬件所支持的特性。
    有时,硬件固有的特性和软件仿真二者的结合比单独使用软件仿真的
速度还要慢。例如,如果显示设备驱动程序支持DirectDraw但不支持拉伸
块写方式,当从视频内存表面拉伸或位块传输时就会产生性能上的损失。
因为视频内存通常比系统内存的速度要慢,在访问视频内存时CPU必须要
等待。因此,如果你的应用程序使用了硬件未能提供的特性,最好在系统
内存中创建表面,以避免CPU访问视频内存时出现的性能损失情况。

4、系统组成(System Integration)
    下图显示了DirectDraw、图形设备接口GDI、硬件抽象层HAL、硬件仿
真层HEL和硬件之间的关系。

如图中所示,一个DirectDraw对象同GDI是并列关系,两者都通过设备相
关抽象层访问硬件。同GDI不同的是,DirectDraw只要有可能就会充分利
用硬件提供的特有功能。如果硬件不支持某一特性,DirectDraw还会尝
试用是你能够使用GDI函数来处理表面对象。

三、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();

4、表面(Surface)
    一个表面或DirectDrawSurface对象表征了一块线性内存区域。表面通常都
驻留在显示内存中,当然也可以存在于系统内存中。除非明确指定,在创建
DirectDrawSurface对象时,DirectDraw会把DirectDrawSurface对象放置于
能够获得最佳性能的位置。DirectDrawSurface对象还能利用显示卡上的特殊
处理器,不仅可以加快任务的执行速度,还可以同系统CPU并行处理一些任务。
    使用IDirectDraw2::CreateSurface方法,你可以创建单一的表面对象,
复杂的表面翻转链和三维的表面。使用CreateSurface方法创建要
求的表面或翻转链并且获得主表面的IDirectDrawSurface接口的指针。
    IDirectDrawSurface3接口使你能够用位块传输方法直接访问内存,如
IDirectDrawSurface3::BltFast方法等。表面对象能提高一个设备上下文,
它可以让你使用GDI函数。另外,你可以使用IDirectDrawSurface3方法之间
访问显示内存。例如,你可以使用IDirectDrawSurface3::Lock方法锁定显示
内存并获取相关表面的地址。显示内存的地址可能指向可见缓冲区内存(主
表面)或不可见缓冲区(屏外表面或覆盖表面)。不可见缓冲区通常驻留在显
示内存中,但可在系统内存这创建,如果硬件允许或DirectDraw使用了软件仿
真功能的话。另外,IDirectDrawSurface3接口扩展了一些方法。利用这些方
法,能够放置或获取调色板工作于指定类型表面。

4.1、表面接口(Surface Interface)
     DirectDrawSurface对象通过IDirectDrawSurface、 IDirectDrawSurface2,
和IDirectDrawSurface3接口来表现其功能。每一个新版本的接口都提高了同以
前版本同样的功能,并且通过新的方法提供了旧版本中不曾具有的功能。
IDirectDrawSurface接口上该类接口中最老的版本,当你使用
IDirectDraw2::CreateSurface方法创建一个表面时,缺省提供的就是这一接口。
要利用另一版本的接口提供的新的功能,你还必须用QueryInterface方法查询新
版本的接口。下面的代码显示了如何完成这一工作的:

LPDIRECTDRAWSURFACE  lpSurf;
LPDIRECTDRAWSURFACE2 lpSurf2;
         // Create surfaces.
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN |
                DDSCAPS_SYSTEMMEMORY;
ddsd.dwWidth = 10;
ddsd.dwHeight = 10;

ddrval = lpDD2->CreateSurface(&ddsd, &lpSurf,  NULL);
if(ddrval != DD_OK)
        return;

ddrval = lpSurf->QueryInterface(
                IID_IDirectDrawSurface2, (LPVOID *)&lpSurf2);
if(ddrval != DD_OK)
        return;

ddrval = lpSurf2->PageLock(0);
if(ddrval != DD_OK)
                return;

ddrval = lpSurf2->PageUnlock(0);
if(ddrval != DD_OK)
                return;

    上面的例子在调用QueryInterface方法时通过指定IID_IDirectDraw2
标志获得了DirectDrawSurface对象的IDirectDrawSurface2接口。要想得到
IDirectDrawSurface3接口,用IID_IDirectDrawSurface3标志代替
IID_IDirectDraw2即可。

4.2、Color Key
    DirectDraw支持位块传输和覆盖表面方式下的源Color Key和目的
Color Key。通过调用IDirectDrawSurface3::SetColorKey方法来设置
表面的Color Key。当使用位块传输时,源Color Key指明了不被拷贝的
颜色或颜色范围,目的Color Key指明了被替换的颜色或颜色范围。
    一些硬件只支持YUV像素格式的颜色范围。YUV数据通常是视频数据,
透明背景由于转换中的量化误差可能不是一种单一的颜色。
    Color Key用表面的像素格式来指定。如果表面是调色格式的,
Color Key就被指定为索引或索引的范围。如果表面的像素格式由
描述YUV格式的FOURCC代码指定,YUV的Color Key就由三个DDCOLORKEY
结构中的两个成员dwColorSpaceLowValue和dwColorSpaceHighValue三
个字节指定,三个字节分别是V、U、Y数据。
IDirectDrawSurface3::SetColorKey方法中的dwFlags参数指明了
Color Key是否用于覆盖或位块传输操作,是否是一个源Color Key或
目的Color Key。下面的代码是
有效的Color Key的一些实例:
// 8位调色模式
// 调色板实体26是 color key.
dwColorSpaceLowValue = 26;
dwColorSpaceHighValue = 26;

// 24位真彩模式
// 颜色255、128、128是color key.
dwColorSpaceLowValue = RGBQUAD(255,128,128);
dwColorSpaceHighValue = RGBQUAD(255,128,128);

// FourCC YUV模式
// Any YUV color where Y is between 100 and 110
// and U or V is between 50 and 55 is transparent.
dwColorSpaceLowValue = YUVQUAD(100,50,50);
dwColorSpaceHighValue = YUVQUAD(110,55,55);

4.3、像素格式(Pixel Format)
    像素格式指出了任何对表面内存中每个像素数据作出解释。
DirectDraw使用DDPIXELFORMAT结构描述各种像素信息。
DDPIXELFORMAT结构的成员描述了像素格式的下述特性:
        .调色或非调色像素格式
        .如果是非调色格式,像素格式是RGB还是YUV格式
        .位深度
        .像素格式组成部分的位屏蔽
你可以调用IDirectDrawSurface3::GetPixelFormat方法来获取
存在的表面的像素格式。

4.4、创建表面
    DirectDrawSurface对象表征了驻留在显示内存中的一个表面。
如果显示内存用完了或者是显式地创建,该表面也可存在于系统内
存中。你可以使用IDirectDraw2::CreateSurface方法创建一个或多
个表面。调用CreateSurface时,必须指定表面的大小、表面类型
(是单一表面还是复杂表面)、像素格式(如果表面不使用索引的
调色板)。所有的这些特性都包含在DDSURFACEDESC结构中,在调用
时需要将该结构的地址传送过去。如果硬件不支持请求的特性或者
此前已经将那些资源分配给了另一个DirectDrawSurface对象,调用
就会失败。
    创建单一的表面或多表面只需要几行简单的代码。创建表面有
四个主要的步骤。每一个步骤都需要比前一个步骤更多的准备工作,
不过并不太难,它们是:
        (1).    创建主表面
        (2).    创建一个屏外表面
        (3).    创建复杂表面和翻转链
        (4).    创建宽表面
    在缺省的情况下,DirectDraw在本地视频内存创建一个表面,如
果足够的本地视频内存保存该表面,DirectDraw就尝试利用非本地视
频内存(仅在一些AGP设备系统中)。你可以在调用CreateSurface时
对DDSCAPS结构赋以适当的标志来显式地指明在哪类内存中创建表面。

4.4.1、创建主表面
    主表面是当前在显示器上可见的并且由DDSCAPS_PRIMARYSURFACE
标志指明的表面,每一个DirectDraw对象只能有一个主表面。当你创
建一个主表面时,其大小应该同当前的显示模式匹配。因此,在这种
情况下你不需要指明表面的大小。事实上,如果你指明了表面的大小,
即使同当前的显示模式匹配,也会导致创建过程的失败。
    下面的例子显示了如何设置DDSURFACEDESC结构的相关成员来创建
主表面:
DDSURFACEDESC ddsd;
ddsd.dwSize = sizeof(ddsd);
                // Tell DirectDraw which members are valid.
ddsd.dwFlags = DDSD_CAPS;
    // Request a primary surface.
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

4.4.2、创建屏外表面
    屏外表面通常用于位图的缓存,该位图将被位块传输到主
表面或后台缓冲区中。你必须设定包含DDSC_WIDTH和DDSD_HEIGHT标志
并设定dwWidth和dwHeight成员 为适当的值来说明屏外表面的大小。
另外,还必须在DDSCAPS结构中包含DDSCAPS_OFFSCREENPLAIN标志。
    当没有足够的显示内存使用时,DirectDraw就使用系统内存来创
建表面。你可以在DDSCAPS结构中的dwCaps成员包含
DDSCAPS_SYSTEMMEMORY或DDSCAPS_VIDEOMEMORY标志来显式地指明是在
显示内存还是在系统内存中创建表面。
下面的例子显示了创建一个屏外表面之前的准备工作:
DDSURFACEDESC ddsd;
ddsd.dwSize = sizeof(ddsd);

// Tell DirectDraw which members are valid.
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;

// Request a simple off-screen surface, sized
// 100 by 100 pixels.
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
dwHeight = 100;
dwWidth = 100;
    在DirectX以前的版本中,屏外表面的最大宽度不能超过主表
面的宽度。在DirectX 5中,你可以创建任意宽度的屏外表面,只
要显示设备指出就行。需要注意的是,当声明了屏外表面的宽度时,
如果显示内存容不下请求的表面的宽度,该表面将换在系统内存中
创建。如果显式地声明使用视频内存,而视频内存又不够时,创建
表面的工作就会失败。

4.4.3、创建复杂表面(Complex Surface)和翻转链(Flipping Chain)
    一个复杂表面就是用IDirectDraw2::CreateSurface方法创建的
一组单一表面的组合。若在调用CreateSurface时设定了DDSCAPS_COMPLEX
标志,除了显式指定的表面外,DirectDraw还将隐式地创建一个或多个
表面。你可以象管理单一表面一样管理复杂表面。调用
IDirectDraw::Release方法会释放所有的表面,调用
IDirectDrawSurface3::Restore则会将这些表面恢复。
    最常用的复杂表面是翻转链。通常,一个翻转链由一个主表面和一
个或多个后台缓冲区组成。DDSCAPS_FLIP标志说明了某一表面是一翻转
链一部分。用这种办法创建一个翻转链还需要包含DDSCAPS_COMPLEX标
志。下面的例子创建一个主表面翻转链所需要的准备工作:
DDSURFACEDESC ddsd;
ddsd.dwSize = sizeof(ddsd);

// Tell DirectDraw which members are valid.
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;

// Request a primary surface with a single
// back buffer
ddsd.ddsCaps.dwCaps = DDSCAPS_COMPLEX | DDSCAPS_FLIP |
DDSCAPS_PRIMARYSURFACE;
ddsd.dwBackBufferCount = 1;
    该例构建了一个双缓冲翻转环境。调用IDirectDrawSurface3::Flip
方法将交换主表面和后台缓冲区的表面内存。如果DDSURFACEDESC
结构中的成员dwBackBufferCount取值为2,就会创建两个后台缓冲区,
每次调用Flip就会循环地翻转表面,并提供三缓冲翻转环境。

4.4.4、创建宽表面(Wide Surface)
    DirectDraw允许创建比主表面宽的屏外表面,当然这需要显示硬件支持才
行。要确定显示设备是否支持宽表面,需要调用IDirectDraw2::GetCaps方法,
查看第一个DDCAPS结构中的dwCaps2成员是否包含DDCAPS2_WIDESURFACES标志。
如果该标志存在,就表明可以在视频内存中创建比主表面还要宽的屏外表面。
如果该标志不存在,在视频内存中创建宽表面将会失败,并返回一个
DDERR_INVALIDPARAMS错误。系统内存表面、视频端口表面和执行缓冲区都支持
宽表面。

4.4.5、翻转表面(Flipping Surface)
    DirectDraw中的任何表面都可以构建为一个翻转表面。一个翻转表面就是
能够在前台缓冲区(front buffer)和后台缓冲区(back buffer)之间互相
交换的任意一块内存。通常,前台缓冲区就是主表面,但这并不是必需的。当
使用IDirectDrawSurface3::Flip方法执行表面翻转操作时指向主表面的表面
内存的指针和后台缓冲区的表面内存的指针互换。因此,表面的翻转是显示设
备用于指向内存的指针的交换,而不是对表面内存的拷贝。唯一的例外是,当
DirectDraw用软件仿真翻转时只是简单地拷贝表面。如果一个后台缓冲区不能
装入到显示内存中或硬件不支持DirectDraw时,DirectDraw就用软件仿真翻转
操作。当一个翻转链包含了一个主表面和多个后台缓冲区时,指针的转换就采
用了循环的方式周而复始,如下图所示:

    DirectDraw对象的其它附加表面不是翻转链的一部分,不受Flip方法的影
响。切记,DirectDraw翻转表面是通过交换DirectDrawSurface对象表面内存
的指针实现的,而不是交换这些对象。这就意味着,位块传输到后台缓冲区时,
必须一直使用同一个DirectDrawSurface对象,该缓冲区是你创建翻转链的后
台缓冲区。而指向翻转操作一直是通过调用前台缓冲区的Flip方法完成的。

4.4.6、丢失表面(Losing Surface)
    当表征表面内存的DirectDrawSurface对象不必要释放时,同该表面关联
的表面内存却被释放了。这就是失去了表面。当DirectDrawSurface对象事情
了它的表面内存时,许多方法就不执行其它的操作并返回DDERR_SURFACELOST。
因为改变了显示模式或另一个应用获得了对显示设备的独占访问而释放了当前
分配的所有表面内存,表面就可能会丢失。
IDirectDrawSurface3::Restore方法重新创建这些丢失的表面并同
DirectDrawSurface对象重新联结。恢复表面并不装入表面丢失之前存在的位
图。因此,如果丢失的表面,必须完全重新装入此前曾装入的图形。
4.4.7、释放表面(Releasing Surface)
    象所有的COM接口一样,不需要表面时,必须调用Release方法释放表面。
每一个单独创建的表面都必须显式地释放。如果是一次调用
IDirectDraw2::CreateSurface或IDirectDraw::CreateSurface方法显式地
创建的多个表面(如翻转链等),你只需要释放前台缓冲区即可。在这种情
况下,指向后台缓冲区表面的任何指针都会显式地释放,并且不可再用。

4.4.8、更新表面特征(Updating Surface Characteristics)
    你可以使用IDirectDrawSurface3::SetSurfaceDesc方法来更新已有表面
的特征。用该方法可以改变像素格式,也可以改变DirectDrawSurface
对象的表面内存在系统内存中的位置,该对象是应用程序显式地分配的。它
允许一个表面直接使用先前分配的缓冲区中的数据而不需要拷贝,这一新的
表面内存由客户应用来分配,也由该客户应用来释放。
    调用 IDirectDrawSurface3::SetSurfaceDesc方法时,lpddsd参数必须
是描述新的表面内存的结构DDSURFACEDESC的地址,也就是指向该内存地址
的指针。在DDSURFACEDESC结构中,可以只设定dwFlags成员来反映表面内存
的位置、大小和像素格式。因此dwFlags只能包含DDSD_WIDTH、DDSD_HEIGHT、
DDSD_PITCH、DDSD_LPSURFACE和DDSD_PIXELFORMAT等标志的组合,用这些组
合来设定有效的结构成员。
    在设置结构的值前,必须分配内存来装入表面。分配的内存的大小非常
重要 。你不仅需要分配足够的内存来容纳表面的宽和高,还需要为表面间距
(pitch)留下足够的内存空间,表面间距是一个QWORD(8个字节),间距是用
高度而不是用像素度量的。
    设置结构中表面的值时,lpSurface成员是你分配的内存的指针,
dwHeight和dwWidth成员以像素单位描述了表面的大小。如果指定了表面的大
小,也需要填充Ipitch成员来反映表面间距。ddpfPixelFormat成员描述了
表面的像素格式。除了lpSurface成员外,若你不为这些成员设定值,
IDirectDrawSurface3::SetSurfaceDesc方法就从当前的表面中使用缺省的值。
在调用IDirectDrawSurface3::SetSurfaceDesc方法时需要注意一些问题。
例如,lpSurface必须是系统内存的一个有效指针(该方法目前不支持视频内
存指针);dwWidth和dwHeight必须是非0的值;不能在主翻转链中重新分配
主表面或其它任意表面。
    你可以为多个DirectDrawSurface对象分配同一内存,但必须注意当内存
分配给任意表面对象后,就不能取消对该内存的分配。对SetSurfaceDesc方法
不正确的使用会导致不可预料的反应。因此,当不再需要表面内存时,你必
须记住释放它。不过,在调用该方法时,DirectDraw将释放创建表面时显式分
配的表面内存。

4.4.9、直接访问帧缓冲区(Accessing the Frame-Buffer Directly)
    你可以使用IDirectDrawSurface3::Lock方法直接访问帧缓冲区或系统
内存中的表面内存。当调用该方法时,lpDestRect参数是一个RECT结构的指针。
该结构描述了要直接访问的表面上的矩形。若想锁定整个表面,可将
lpDestRect设为NULL。也可以指定一个只包含了表面的一部分的RECT。如果没
有两个矩形重叠,两个进程可以同时锁定一个表面中的多个矩形。
    Lock方法用需要适当地访问表面内存的所有信息填充DDSURFACEDESC结构。
该结构包含了有关表面间距和像素格式的信息(在同主表面的像素格式不同时)。
在完成了对表面的访问后,调用IDirectDrawSurface3::Unlock方法对其进行
解锁。

4.4.10、使用非本地视频内存表面(Using Non-local Video Memory Surfaces)
    DirectDraw支持高级图形端口AGP(Advanced Graphics Port)特性,
能够在非本地视频内存中创建表面。在AGP系统中,如果本地视频内存已
用完或应用程序显式地要求使用非本地内存,DirectDraw将会使用非本
地视频内存创建表面。
    目前,有两种AGP体系。一种是“执行模型”(Execute Model),一种
是“DMA模型”(Direct Memory Access Model)。在执行模型中,显示设
备支持非本地视频内存和本地视频内存同样的特性。因此,当使用
IDirectDraw2::GetCaps方法获取硬件的性能时,同位块传输相关
的标志dwNLVBCaps、dwNLVBCaps2、dwNLVBCKeyCaps、dwNLVBFXCaps和
dwNLVBRops将指明用于本地视频内存。在执行模式下,如果本地视频内
存被用完了,DirectDraw将自动使用非本地视频内存。

--
※ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: 192.168.1.3]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店