DirectDraw简介
1. DirectDraw是DirectX的基石,DirectX的其他组件,如Direct3D,都是建立在它的基础上的,用DirectDraw可以访问显卡上的内存,另外,DirectDraw还可以作为位图混合器,用它可以生成、移动和转换图像数据或对数据进行合成用于显示
2. 显示技术:图形协处理器、显卡内存。DirectDraw允许我们指定在哪块内存区域中创建DirectDraw对象,以优化对内存的使用,在一般情况下,都必须把经常被CPU访问的内存缓冲区而且经常被图形协处理器操作的内存缓冲区放入显示内存
3. DirectDraw对象:
(1) DirectDraw:提供了改变系统显示模式、查询显示能力、控制内存使用、管理子对象等功能
(2) DirectDrawSurface:它提供了生成平滑动画、操作覆盖图和访问GDI功能所需要的一切
(3) DirectDrawPalette:这个对象提供了调色板化显示模式的颜色控制
(4) DirectDrawClipper:这个对象允许用户创建运行于用户桌面窗口中的DirectDraw应用程序
4. 访问显示卡的软件层次结构之间的交互关系(GDI不支持直接访问显示内存之类的底层操作,另外对于诸如覆盖图这些游戏和多媒体开发人员感兴趣的高层功能它也不提供)
5. 当应用程序调用DirectDraw时,它将根据各组件的能力自动确定要调用与硬件相关的HAL功能还是调用一般性的HEL功能,如果没有提定要用HAL功能还是HEL功能,DirectDraw通常会使用HAL,因为HAL能够更好地利用硬件特性,如果指定的HAL功能没有找到,则使用相应的HEL功能代替,HEL有时会调用GDI的一些功能,而有时则直接访问硬件以完成一些简单的并与具体硬件无关的任务,偶尔HAL和HEL也会调用一个被称为是VFLATD的驱动程序层以访问显示卡上的内存,VFALTD把一些显示卡的段内存结构变成线性结构
6. DirectX Properties对话框中的DirectDraw标签页面可用于调整DirectDraw的行为,以方便调试和在不同的计算机系统上模拟执行
7. 总体上,每个DirectDraw应用程序都是由以下几步组成的:
(1) 创建DirectDraw对象
(2) 设置协作级别
(3) 选择显示模式
8. 在一个进程中可创建多个DirectDraw对象,每个对象是互相独立的。接口和其他子对象在DirectDraw对象之间是不能共享的,其中唯一例外的是剪裁器,它可以共享
9. DirectDraw设备:创建DirectDraw对象时,所生成的对象总是附属于宿主系统中与HAL相一致的DirectDraw设备的
为了让用户能够自由选择系统所支持的能力,必须使用DirectDrawEnumerate功能:这个功能将枚举所有已安装的设备(枚举函数通过为每个元素调用回调函数来获取信息列表,当调用枚举函数时,必须提供一下指向回调函数的指针)
10.创建DirectDraw对象:
(1) DirectDrawCreate函数:这个函数将创建一个带有IDirectDraw接口的DirectDraw对象
(2)可以代替DirectDrawCreate和QueryInterface功能的函数是COM的CoCreateInstance,这个函数根据一个接口的唯一标识返回指向接口的指针(一般不用)
这个函数需要一些附加的代码才能正常运行,在使用前必须调用CoInitialize函数,使用后则调用CoUninitialize函数,另外,为了使对象称为一个可用对象,必须调用DirectDraw对象的Initialize方法
11. 可获取的资源
(1) DirectDraw的版本
(2) 能力标志(DirectDraw对象的GetCaps方法可以返回性能指标结构,其中描述了哪些能力是得到了硬件的支持而哪些则是被模拟的)
(3) 显示内存(GetCaps方法返回的DDCAPS结构中,有两个成员变量分别存放了所有显示内存和剩余的显示内存)
12. 协作级别
(1) 协作级别可用于控制应用程序和系统及其它应用程序之间的交互程度。在调用DirectDraw其他方法之前必须设置协作级别(SetCooperativeLevel函数)
(2) 一般,DirectDraw应用程序可分为两类:全屏模式和窗体化模式,全屏模式使用的是DDSCL_EXCLUSIVE和DDSCL_FULLSCREEN标志,而对于窗体化应用程序,则必须使用DDSCL_NORMAL标志
13. 显示模式
(1) DirectDraw的显示模式内容包括宽度、高度、格式和刷新频率等
(2) 显示模式的高度和宽度总称为分辨率
(3) 显示模式的格式主要用来描述像素的类型,格式有时又被称为位深度或颜色深度
(4) 刷新频率:每秒钟屏幕被刷新的次数(高的刷新频率会使动画更加平滑)
(5) 低分辨率模式:Mode X
(6) 模式支持:应支持尽可能多的显示模式并让用户有最终选择的权利。
14. 管理显示模式
(1) DirectDraw提供了一套完整的用于管理显示模式的方法,这些方法可以直接使用
(2) 枚举可支持的显示模式(EnumDisplayModes方法)
(3) 设置显示模式(SetDisplayMode方法)
(4) 恢复显示模式(一般DirectDraw能够自动做这些事)
(5) 获取显示模式(GetDisplayMode方法)
图面
15. 图面:DirectDraw的内容主要是图面,DirectDraw从本质上来说是一个内存管理器,它允许开发人员创建和管理数据缓冲区,并以一定的形式显示缓冲区中的数据,这些缓冲区用DirectDraw的术语来称呼是图面,图面是DirectDraw的主要成分,它也是DirectDraw的基本对象,而且基本上所有的DirectDraw操作在某种程度上都包含它。
(1)图面就是一幅以矩形方式来管理的内存缓冲区—它是一张虚拟的纸,应用程序可以拷贝、组合和重新安排其中的内容
(2)图面的类型:主图面是显示给用户的图面,最基本的图面是off_screen图面,它是由DirectDraw管理的内存片段,它在存储应用程序的背景色等内容时显得特别有用
1. 组合图面实际上是由DirectDraw创建和维护的图面集合
2. 简单图面是由DirectDraw创建的并且它是组合图面结构的组成部分
3. 纹理是一种用于Direct3D的off_screen图面
4. 覆盖图面则是一种通过显示硬件可以自动与主图面组合的图面
5. 切换图面是一种复杂的图面,它可用于生成平滑的动画
(3)图面描述:有关图面的所有信息都保存在一个称为图面描述的结构中DDSURFACEDESC,这个结构将作为参数传递给许多图面方法
(4)用GetSurfaceDesc方法可以获取图面描述信息
(5)图面格式:图面格式也称为像素格式像素格式结构:DDPIXELFOEMAT,这些也是图面描述的一部分,但是可以用函数GetPixelFormat直接获取像素格式结构信息
(6)最常用的格式RGB(其他:调色板化的格式、YUV格式(压缩视频数据)、FOURCC(其他所有可能的图面格式都用四个字符来标识))GetCaps方法返回的结构中保存了HAL支持的FOURCC格式的数目;GetFourCCCodes方法可以获取相应的代码标识
(7)调用——分配——调用 (看大小——分配空间——获取需要的内容)
(8)创建图面:所有的DirectDraw图面都是由CreateSurface开始它们的生命周期的(要正确填写图面描述结构并不容易(该函数的参数))(DirectDraw Test应用程序)
(9)主图面:主图面就是用户能够看到的当前界面,每个DirectDraw对象只有一个主图面,在DirectDraw启动之前,主图面已经存在了—主图面就是GDI用于绘制Windows用户界面的图面。在创建指向它的DirectDraw图面对象之前,我们是没有办法通过DirectDraw来访问主图面的(主图面的尺寸和格式由显示模式决定,不能改变)
(10)off_screen图面:它是不能直接被看见的,它主要是作为存储可视部件的存储缓冲区,这些缓冲区可用于组成主图面或切换图面,创建off_screen图面的过程和创建主图面的过程基本是一样的,只是要提供更多细节
(11)图面的尺寸:主图面的尺寸是固定的,而off_screen图面的尺寸是可调的
(12)定位:DirectDraw允许直接操作位于常规系统内存外的图面,当创建一个图面时,可以把它的内存缓冲区定位于DirectDraw管理范围内的任何地方,可以是系统内存也可以是显示内存(通过图面描述中的ddsCaps成员变量,可以指定图面的位置)GetAvailable VidMem方法可以获取当前内存的更详细的信息
(13)对于经常要被图形协处理器访问的图面可最先创建以确保它被放在显示内存中,然后再根据图面的重要性依次创建他们直到显示内存用完为止
(14)调入图面:LoadImage函数和BitBlt函数(如果所保存的图像的大小和像素格式与目标图面不一致,要进行转换的放缩)
(15)丢失的图面:程序间切换,显示内存中的图面都会丢失(位于系统内存中的图面不会丢失)。由于DirectDraw仍然保留了关于图面的信息,可以调用Restore方法恢复图面,但是图面中的内容不能恢复,所以,在恢复之后,应用程序必须重新构造图面,即重新调入off_screen图面中的内容、恢复主图面图像等(恢复图面的逻辑并装入内容)
渲染
16. DirectDrawSurface的渲染方法
(1) Lock 获取图面内存缓冲区的访问权
(2) Unlock 释放内存缓冲区
(3) GetDC 获取图面的GDI兼容的设备上下文
(4) ReleaseDC 释放设备上下文
访问控制:DirectDrawSurface提供了Lock和Unlock方法来裁决图面内存的访问
跨度;线性存储的矩形图面;内存对齐;分段切换(通过VFLATD驱动程序访问分段切换卡上的显示内存(现在已不用这种显示卡))
17. DirectDraw和GDI一起使用
步骤:(1)获取一个设备上下文(DirectDraw的GetDC方法可以在任何时候调用GDI)
(2)使用GDI
(3)释放设备上下文
位转换操作
18. 基本的位转换操作
(1) 位转换操作就是一个拷贝操作—把内存中的数据从一个地方移到另一个地方,在DirectDraw中,拷贝的源对象和目标对象都是DirectDreaSurface对象的整体或部分(一般情况下,只有位于显示内存中的位转换操作才能得到硬件的加速,AGP结构将改变这一点)
(2) 多个off_screen图面保存了作为位转换操作源的图像,位转换操作的目的是把这些off_screen图面组合成主图面,或者把它们写入到切换图面的后端缓冲区,在一个游戏中,这些off_screen图面可以用于保存精灵动画的帧、滚动背景片段或其他的图像数据,游戏的生动程度直接取决于位转换器的速度。
(3) DirectDrawSurface对象提供了两个执行位转换操作的方法
1. Blt方法:通过所提供的标志位,Blt方法可以控制操作的很多方面(许多标志与a(透明度)位转换操作和Z—缓冲区(深度)位转换操作有关)我们可以在两个不同的图面之间进行位转换操作,也可以使所指定的目标矩形和源矩形在同一个图面中,甚至可以两个矩形重叠
2. BltFast方法:与Blt相比,BltFast功能有限,不支持镜像、拉伸、剪切等操作,但是对于硬件不支持而需要HEL模拟的操作,BltFast要比Blt快10%
(4) 位转换操作坐标:Blt和BltFast都是用NULL来表示整个图面而以RECT结构来表示某个矩形区域
(5) 位转换器定时:显示卡上的图形协处理器有自己的内存和访问内存的总线,这种独立性使它一旦接受到指令后就可以自己执行位转换操作—异步操作
在等待位转换操作器建立时,Blt和BltFast将在位转换器建立后返回,这样应用程序在位转换器正在执行位转换操作时能够继续执行其他操作。
调用GetBltStatus方法可以获取转换器的当前状态
(6) 特殊效果:除了基本的位转换操作之外,DirectDraw通过控制标志和DDBLTFX结构支持多种特殊效果,除了颜色填充之外,要生成其他的特殊效果必须使用Blt方法而不能使用BltFast方法
1. 颜色填充:单色填充整个图面或某个矩形区域
2. 透明:颜色值有时也称为色调值,它主要用于组合有透明区域的图像。DirectDraw支持两种类型的颜色值:源颜色值和目标颜色值
源颜色值操作等价于DirectDraw中的蓝屏,源颜色值是最常用的颜色值,它经常用于处理有透明区域的off_screen位图,如精灵动画等
目标颜色值不像源颜色值那样易于理解,而且它也不常用,目标颜色值经常使用的地方——生成一个窗口的效果,这样源图像只出现在需要的地方
DirectDraw颜色值:由DirectDraw中的DDCOLORKEY结构表示,定义某个范围的颜色值这个功能最常用的的场合是用于处理视频数据,所以它经常与YUV图面格式一起使用(SetColorKey方法可用来设置一个已有图面的颜色值)
3. 放缩:DirectDraw的HEL可以通过必要的收缩或拉伸来放缩一个位转换操作,当目标和源的矩形大小不一样时,放缩功能自动被执行
4. 镜像:HEL提供了位转换矩形的X轴和Y轴上的镜像操作
5. 旋转:HEL不支持旋转,如果硬件不支持,那么它将不可用
6. 在检验位转换操作的效果时,必须记住DirectDraw不能混合使用HEL和HAL来支持它,而且不同的HAL也可能不能一起工作
(7) 所要提供的填充颜色的颜色值格式必须与目标图面的格式一致
页面切换
19. 没有任何技术能像页面切换功能那样大幅度地提高应用程序的性能—特别是对于使用动画的那些程序。页面切换其实是通过使用多个图面以保证在当前视图中不会出现绘制操作,新的图像都是绘制到不可见的图面中(即后端缓冲区),然后再把它们快速地切换到视图中。
(1) 撕裂现象:闪屏等(双缓冲技术可防止撕裂现象)
DirectX将处理创建前端缓冲区和后端缓冲区的所有细节,而且它还能自动同步缓冲区切换操作和垂直空白间隔的时间
(2) DirectDraw切换:DirectDraw的切换图面是组合图面的一种,组合图面是由一次调用CreateSurface方法创建的一组功能性的图面,组合图面的行为和单个图面很相似,它们都是一次调用创建,一次调用释放和一次调用恢复
一次调用CreateSurface函数就能创建一个切换结构:前端缓冲区、后端缓冲区、(过渡缓冲区)
(3) 切换操作的阶段:
1. 显示硬件读取了主显示缓冲区的位置并且把这些像素转换到屏幕上
2. 当调用主图面的Flip方法后,下面的指针将被修改:
指向主显示内存的寄存器的值被改成指向原来的后端缓冲区
指向后端缓冲区图面内存的指针被改成指向原来的前端缓冲区的内存
指向前端缓冲区图面内存的指针被改成指向原来的后端缓冲区的内存
(4) 对切换操作的支持
要执行一个真正的切换操作必须有硬件的支持,另外还要在全屏模式下工作,这就意味着在窗体化的桌面上就不能执行主图面的切换操作
(5) 帧速率:动画被刷新的快慢程度(帧速率不仅仅只是性能的度量标准,必须知道帧速率,才能调整游戏的速度,使它能在不同系统上良好的运行)
20. 使用切换图面
(1) 一般情况下,DirectDraw的双缓冲区技术包含如下步骤:
1. 创建切换结构
2. 获取指向后端缓冲区的指针
3. 往后端缓冲区中写入内容
4. 触发切换操作
(2) 切换图面是应用程序中最重要的内容之一,所以必须确保在精灵动画和其他小一点的图形对象创建之前创建它
(3) 切换结构是不能在显示内存和系统内存中分开并存的
(4) 一次调用CreateSurface函数可创建两个缓冲区:前端缓冲区和后端缓冲区,指向前端缓冲区的指针是由函数返回的,要得到指向后点缓冲区的指针,要使用GetAttatchedSurface方法
(5) DirectDraw切换结构中的后端缓冲区将总是后端缓冲区,当切换操作发生时,只有内存缓冲区中的画面交换了,所以总是向原来就是后端缓冲区的DirectDraw图面中绘制所需要的内容
(6) 用Flip方法建立切换操作(我们总是对前端缓冲区调用Flip方法),该方法的缺省行为是把与后端缓冲区图面相关的内存映射到前端缓冲区,而把前端缓冲区图面相关的内存映射到后端缓冲区
(7) DDFLIP_WAIT标志:这个标志把等待循环加入到DirectDraw的内部
(8) 其他切换方法:GetFlipStatus方法和FlipToGDISurface方法
(9) 多个缓冲区
(10) 页面切换和双缓冲技术不仅可以防止撕裂现象而且还能提高应用程序的帧速率,在切换操作中,实际上并没有发生内存数据的移动,只是图面结构中内存的指针发生了变换,若循环过程中有废弃时间,使用多缓冲区可以提高动画帧速率
调色板
21. DirectDraw通过DirectDrawPalette对象提供对显示卡的硬件调色板的直接访问
(1) 调色板是用于节省时间和显示内存的机制
(2) DirectDraw调色板:
1. 类型:不同尺寸的调色板、索引调色板(调色板的调色板)
2. 管理和操作调色板的方法:CreatePalette(创建调色板)等(3)
3. 调色板动画(使用调色板动画,可以在显示内存中进行改变或移动动画,而不需要真正地移动显示内存数据)
(3) 创建调色板:DirectDraw中,调色板是一个独立的对象,可以独立于画面存在
调色板使用DirectDraw的CreatePalette方法创建的(该方法需要一个颜色表,用来初始化调色板的颜色矩阵,颜色列表是一个PALETTEENTRY结构的矩阵)
使调色板与图面关联:使用DirectDrawSurface的SetPalette方法可以使调色板与图面关联或断开关联(创建多个调色板,用setPalette方法切换可以实现动画)
操作调色板的条目:DirectDraw提供了GetEntries方法和SetEntries方法来操作调色板条目(改变显示,实现调色板动画)
了解调色板:DirectDrawSurface的GetPalette方法和DirectDrawPalette的GetCaps方法
(4) 撕裂:页面切换与垂直空白间隔能够同步,但是调色板却不考虑扫描线的位置而随时改变,其实在实际编程中可能做的调色板改变要少得多,且只会影响屏幕较小的区域,撕裂效果不明显
(5) 垂直空白同步:今后的DirectDraw版本将会支持用硬件实现的调色板切换同步
(6) 其他调色板行为:
l 如果已经把调色板与某图面相关联,然后再使用GetDC方法来获取GDI绘图函数所需要的设备上下文,那么GDI函数会使用该图面的调色板,如果没有调色板与这个图面关联,GDI函数就使用系统调色板
l 使用DirectDraw从一个图面拷贝到另一图面时,不发生颜色装换,也就是说,所有复制的像素会用与目标图面相关联的调色板解释,
l 图面丢失使,如果它有调色板,那么调色板仍与这一图面相联,恢复丢失的图面时不需要把调色板再关联一次
l 图面切换时,调色板不会移动,于是,不能使用不同调色板与一个切换图面的前端和后端缓冲分别关联的方法来实现调色板动画
22. DirectDraw调色板目前只在硬件模拟层(HEL)得以支持
覆盖图
23. 覆盖图是一种特殊的图面,它可以覆盖在主图面上(覆盖图很难操作,它没有协调实现,也没有均衡支持)。在位转换操作中,源像素数据替换了目标像素数据,而用覆盖图,两种图面的结合不是破坏性的,覆盖图似乎是浮在主图面上的,而不是永久性地改变了显示图形,当移开或关闭覆盖图使,下面的主图面部分又可恢复出来
24. 覆盖图的一个主要应用是用来创建在桌面上的窗口中的切换图面,当不能创建经典的DirectDraw切换图面时,可以创建一个带一个或多个后端缓存的覆盖图,并把它放在Windows桌面上,使它浮在应用窗口的框架内,覆盖图的另一个用途是覆盖主图面的控制或状态信息,
25. 实现覆盖图:由于显卡的不同,覆盖图的实现也大不相同,而且,限制几乎总是存在的——尺寸、形状、位置和格式等各方面都有限制。DirectDraw对覆盖图的支持是有限的。
创建覆盖图:也是用CreateSurface方法来创建覆盖图
覆盖图支持:GetCaps方法传回的DDCAPS_OVERLAY功能标志可以指示覆盖图的功能
定义像素格式:创建一个与主图面格式不同的图面是很少见的操作,而创建覆盖图则正是这些操作之一,实际上,为覆盖图提供的格式范围很有限。
显示覆盖图:覆盖图要受很多限制,包括尺寸及位置的缩放比例及对齐请求等,首先要考虑的限制是可能的覆盖图和目的矩形的界限及尺寸的对齐请求(位置对齐、尺寸对齐),缩放等
刷新覆盖图:显示、隐藏、移动覆盖图都伴随着UpdateOverlay方法的使用,该函数通过提供控制标志和DDOVERLAYFX结构中的值来允许对操作进行许多控制
移动覆盖图:SetOverlayPosition方法,只有覆盖图处于可见状态时,该函数才起作用
26. 覆盖图可用于在桌面窗口中提供页面切换——这也是实现此功能的唯一途径。
基于窗口的DirectDraw
27. 窗口应用程序与全屏应用程序
功能:应用程序窗口 全屏:WS_POPUP 窗口:与用户界面相适应
功能:协作级别 全屏:DDSCL_FULLSCREEN 窗口:DDSCL_NORMAL
DDSCL_EXCLUSIVE
功能:主界面 全屏:推荐页面切换 窗口:只能是覆盖图
功能:显示模式 全屏:自选 窗口:在当前桌面模式下运行
功能:裁剪 全屏:随意 窗口:与其他窗口互相作用
功能:调色板 全屏:直接控制硬件调色板
窗口:调色板管理器调节,其他程序可以改变调色板
功能:绘图 全屏:开发者定义
窗口:用户可以移动、改变尺寸、也可以用其他窗口覆盖它
28. 初始化:大部分全屏和窗口模式的不同点都可以在初始化使涉及到
(1) 应用程序窗口
创建窗口:CreateWindowsEx函数
设置窗口大小:AdjustWindowsRectEx函数
MoveWindow函数
协作级别:DDSCL_NORMAL
主图面:不支持切换图面
显示模式:窗口化应用程序应该准备好工作于桌面当前选用的显示模式
剪裁:DirectDrawClipper,一旦一个DirectDrawClipper对象与一个窗口关联上,剪裁列表就会自动地被操作系统刷新以反映桌面上窗口排列的变化,接着DirectDrawBlt和UpdateOverlay方法就利用剪裁列表来调整输出
1. 创建一个DirectDrawClipper对象:运用CreateClipper方法(与父DirectDraw对象关联)或者是DirectDrawCreateClipper方法(独立的)
2. 将剪裁器和窗口相关联:SetHWnd方法
3. 将剪裁器和图面相关联:SetClipper方法(BltFast方法不支持剪裁)
调色板:调色板管理器、颜色转换、屏幕暂停、调色板消息(改变)
渲染:
客户矩形:用户可以自由移动和改变应用程序的窗口,程序要处理客户矩形的变化:GetClientRect函数和ClientToScreen函数
重访丢失的画面:(IsLost方法检测主图面的状态)
应用DirectDraw
29. 帧速率会因为其他程序占用资源或Windows响应后台任务而动态改变,要考虑这种变化
30. 尽量使DirectDraw初始化与应用程序初始化分离,可以使在必要时彻底重初始化DirectDraw变得简单一些