视频播放器制作学习之旅----如饥似渴(DirectDraw DirectShow)

现在我们创建了一个DirectDraw对象,并且知道如何获得高级点的版本,下来让我们再使DirectDraw工作起来的征程中再向前走一步,即设置合作级别。

晕了,什么是合作级别,为什么下来就需要设置合作级别。这个我且放下,我需要了解下DirectDraw程式的基本步骤。这个应该是程序员需要遵守的约定。查了资料,主要步骤有5步:第一步是初始化(定义DD、页面、裁剪板、调色板对象)第二步设置屏幕的显示方式(DirectDraw有自己的设置屏幕的方式,而且它的屏幕模式分为“全屏”(exclusive mode)和“窗口”(normal mode).各有各的设置方法。设置的主要区别在于SetCooperativeLevel的参数。现在已经有了一个屏幕,但是还不能在上面画画,需要第三步建立一个画板)第三步就是建立前后页(两块画板,两块的好处是可以一边在一块上面画,一边给别人看已经画好的另一块。等这块画好了,两块板就对调一下,让别人看新画好的这块。如果画得足够快,换得足够快,看得人就会看到动画了,就像电影的效果一样,我们把这叫做Page Flipping 。不过我们可以根据需要建立三块、四块画板)第四步是给显示区加一个画框(就是创建一个裁剪板,在窗口下,为了防止DirectDraw画到窗口外面去,需要加一个画框,也就是裁剪板,可以使用CreateClipper来创建剪贴板,创建好之后需要调用SetHWnd指定窗口,并调用SetClipper把它套到指定的一个窗口上去)第五步就是在后页画图,前后页互换(DirectDraw用来互换的语句有BltBltFast,如果工作在全屏模式下,前后页互换用函数Flip即可)。

接着刚刚的“设置合作级别”,这里讲的合作,其实是与Windows的合作,主要体现在视频硬件的控制上面,体现在全屏模式还是窗口模式,在全屏模式中,DirectDraw像老式的DOS程序那般行事。就是说,整个荧屏表面都分配给你的游戏,你直接写到视频硬件上。别的程序不会接触到视频硬件。窗口模式有点小不同。在窗口模式中,DirectDraw必须更多的与Windows合作,因为其他的程序可能需要更新他们各自的对用户可见的客户区窗口。因此,在窗口模式中,你对视频硬件的控制和独占是收限制的。

 

创建一个显示表面。

如你所知,在屏幕上显式的图形无非是一系列着了色的像素,而这些像素的颜色又是由内存中的一些格式化数据所描述,要么是调色板,要么是RGB。换句话说,要让任何事情在屏幕上发生,你就需要知道如何在内存中绘画。不过,DirectDraw是设计者决定对显存进行一点抽象,这样不管你系统中(别人系统中)使用什么样的显卡,访问视频表面的方法对于你(程序员观点)而言是不变的。这样,DirectDraw便支持了我们所谓的表面概念。

表面是可以含有位图数据的矩形内存区域,有两种不同的表面:主表面和副表面。主表面直接与显卡上真实的显存对应,并且始终可见。因此,在DirectDraw程序中只能有一个主表面,并且它直接反应屏幕图形而且存储于VRAM(显存)中。当你操作主表面时,你会立即看到结果显示在屏幕上。比如,如果你设置视频模式为640×480×256,则你必需创建一个同样是640×480×256的主表面,并且把它于显示设备,即IDirectDraw7对象,关联起来。

副表面,则相当灵活。它们可以是任何尺寸,可以或保存在VRAM中或保存在系统内存中,而且你爱可以创建任意多个,只要内存够用。大多数情况下,你需要一个到两个副表面(缓冲区)用以平滑动画。和主表面一样,副表面也有色深和绘图的工作要做。你在离屏(offscreen)表面为下一帧动画绘图,然后快速的把离屏表面拷贝或翻页(page flip)到主表面,这样便平滑了动画。这就是所谓的双或三缓存。

副表面的第二个用途是存储游戏中的位图或动画。这是DirectDraw的一个非常重要的特性,因为只有使用DirectDraw表面你才能在位图数据上调用硬件加速功能。如果你你自己去写位块传输程序以绘位图,那你将失去所有的加速功能带来的好处。

现在我有点超前了,所以我得放慢点脚步。我超前的原因只是想让你有机会思考思考。现在,让我们来看看如何创建一个与你的显示模式相同大小的主表面,然后你将学习如何在主表面上写数据以及在屏幕上绘像素

创建主表面两部曲:填充DDSURFACEDESC DirectDraw对象调用CreateSurface创建表面。首先我在头文件中先定义三个表面,一个主表面(primary surface),两个副表面(offscreen surface):

 

//DirectDraw interfaces
 IDirectDraw *m_pDD;
 IDirectDraw3 *m_pDD3;
 IDirectDrawSurface *m_pPrimarySurface;
 IDirectDrawSurface *m_pOffScreenSurface;
 IDirectDrawSurface *m_pOffScreenSurface2;
 IDirectDrawClipper    *m_pDDClipper;

 

CreateSurface(对你想创建的DirectDraw表面的描述即DDSURFACEDESC结构,一个接收接口的指针,表征高级COM属性的pUnkOuter设为NULL),看了MSDN,填写对表面进行描述的DDSURFACEDESC结构是件很烦人的事情。它是一个很复杂的结构。网上查资料说,只需要知道如下几项:

dwSize:设置为sizeof(DDSURFACEDESC)

dwFlags:这个域用来告诉DirectDraw,你给的数据是用来填充DDSURFACEDESC2的哪个域的。或者,如果你在一个查询操作中用这个结构,告诉DirectDraw你要获得DDSURFACEDESC2的哪个域的信息。看看表6.5,其列出了这个标志字可取的值。比如,你如果你要在 dwWidth dwHeight 两个域中填入有效数据则应该像下面这样设置dwFlags域:

ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT;

这样,DirectDraw便知道去查找 dwWidth dwHeight 域,然后放入有效值。就把单位dwFlags看成是引导数据的指示器吧。

dwWidthdwHeight

lPitch这是个有趣的域。基本上它是你所选择模式的水平内存间距。看图6.8lPitch,也被称为步幅或内存宽度,是在给定视频模式下每行的的字节数。基于如下原因,这是个非常重要的数据域:当你要求一个640×480×8的显示模式,你知道每行有640个像素,每个像素占8位内存(即1个字节)。所以,每行有640个字节,于是lPitch似乎便该设为640。对吗?不一定哦。

技巧:lPitch将根据VRAM的不同设计而不同。故而,当你在DirectDraw表面上从一行访问另一行的内存时,你必需使用lPitch来移动到下一行,而不是用宽度乘上每像素的字节数。这一点非常之重要。

大多数新显卡支持我们所谓的线性内存模式而且有硬件寻址功能,这些属性已经是现实了,但并不保证每块显卡都实现之。所以,你不能假设一个640×480×8视频模式在内存中每行占640个字节。而这便是lPitch域存在的理由。你必需在你计算内存地址时应用它以保证计算正确,这样你便可以从一行移动到另一行了。比如,要访问640×480×8256色)视频模式下的任何一个像素,你可以使用下面的代码。假设你已经从DirectDraw得到lPitch值,并且lpSurface已经指向表面内存(我会在下面解释这个参数的)

ddsd.lpSurface[x + y*ddsd.lPitch] = color;

是不是很简单啊?大多数情况下,ddsd.lPitch 设为640以代表640x480x8模式。而对于640×480×16模式,ddsd.lPitch 将设为1280(两字节每像素=640×2)。但是,对于某些显卡,出于显存的布局原因,比如内在缓存的设立或其他什么东西,就使得事情不像上述这样了。所以最正规的方式是:在计算内存时总是使用lPitch,你便总是安全的。

技巧:尽管lPitch值并不总是等于你设置的视频模式的水平值,但使用其来测试水平值可以使你能够调用到其他优化函数。比如,在你完成初始化部分功能的代码中,你可以去获得lPitch值并与你选择的视频模式水平值比较。如果它们相等,则你可以切换到你为优化程序而硬编码每行字节数的函数上。

lpSurface:这个域获得指向你随创建表面的真实内存地址的指针。这些内存可能是VRAM也可能是系统内存,但是你无需为次担心。一旦你获得了指向该内存的指针,你便可以像操作其他内存一样操作它了,比如向它写数据或读数据,等等,这完全取决于你想如何填充像素。呵呵,让这个指针有效多容易啊!但是我们还是要在这里多停留一会。一般,你必需锁定表面内存,并且告诉DirectX你要在该内存上面工作了而其他的进程不许试图在该内存上读或写。进一步的,当你获得了这个指针时,根据不同的色深(8162432)你要经常对其进行类型转换并将其复值给一个工作指针。

dwBackBufferCount:这个域被用来设置和读取后备缓存(或与主表面关联的副离屏翻页缓存)的数目。如果你能回忆起来,通过创建一个或更多的虚主表面(与表面拥有同样的图样和色深的缓存),后备缓存可用来实现动画的平滑化,这也便是离屏缓存。然后你在后备缓存上绘图,这个后备缓存对于用户而言是不可见的。接着快速的翻页或拷贝后备缓存到主表面以供显示。如果你只有一个后备缓存,这个技术便成为双缓存技术。使用两个缓存便叫三缓存技术,当然后者比前者拥有更好的效果但也占用更多的内存。为了是事情简单点,大多数情况下,你将创建一个包括一个主表面和一个后备缓存的翻页链。

ddsCaps:这个域用来使那些被要求的但尚未在表面属性中定义的项有效。事实上,这个域又是另一个数据结构。下面显示了DDSCAPS2
typedef struct _DDSCAPS2
        {
        DWORD    dwCaps;   // Surface capabilities
        DWORD    dwCaps2;  // More surface capabilities
        DWORD    dwCaps3;  // future expansion
        DWORD    dwCaps4;  // future expansion
        } DDSCAPS2, FAR* LPDDSCAPS2;
99.9%的情况下,你只需设置该结构的第一个域。dwCaps.dwCaps2 是为3D准备的,而其他域dwCaps3 dwCaps4是为未来扩展之用,尚未使用。总之,表6.6列出了dwCaps可能设置的标志值。要看完整的列表,到DirectX SDK里去。

比如当要创建一个主表面是,你可以像下面这样设置ddsd.ddsCaps

ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

我知道上面这种表达方式很复杂,在某种程度上确实如此。双重嵌套的控制标志确实有点痛苦。但是,忍受吧……

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值