DirectX5.0最新游戏编程指南介绍
DirectX5.0最新游戏编程指南介绍
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将自动使用非本地视频内存。
在DMA模型中,对从非本地视频内存中位块传输的纹理贴图的支持是有限的。当显示设备使用了DMA模型时,欲获得硬件的性能,dwCaps成员将被设为DDCAPS2_NONLOCALVIDMEMCAPS标志。同位块传输相关的标志包含在dwNLVBCaps、dwNLVBCaps2、dwNLVBCKeyCaps、dwNLVBFXCaps和dwNLVBRops等成员中。除非显式地指明,DirectDraw将不使用非本地视频内存创建表面。DMA模型的实现在对非本地视频内存表面的支持是不同的。如果驱动程序支持从非本地视频内存表面的纹理贴图,在用IDirect3DDevice2::GetCaps方法获取3D设备的性能时,D3DDEVCAPS_TEXTURENONLOCALVIDMEM标志将被设定。
4.4.11、转换颜色和格式(Converting Color and Format)
非RGB表面格式由四个字符的代码(FOURCC codes)表示。如果应用程序调用了IDirectDrawSurface3::GetPixelFormat方法来要求使用像素格式,并且表面是一个非RGB表面, DDPF_FOURCC标志就会设定,DDPIXELFORMAT结构中的成员dwFourCC将成为有效的。如果FOURCC码表示的是一个YUV格式,DDPF_YUV标志将被设定,dwYUVBitCount、dwYBits、dwUBits、dwVBits和dwYUVAlphaBits
成员将是有效的掩码,能用于从像素中提取信息。
如果当前是RGB格式,DDPF_RGB标志就会被设定,dwRGBBitCount、dwRBits、dwGBits、dwBBits和dwRGBAlphaBits成员将是能够用于从像素中提前信息的有效掩码。
在颜色和格式的转过程中,有两组FOURCC代码用于应用程序中。一组表征了硬件位块传输的能力,另一组表征了硬件覆盖的能力。
4.4.12、覆盖表面(Overlay Surfaces)
覆盖表面是具有特殊硬件支持能力的表面,通常用于显示活动视频、录制视频或静止位图而不需要位块传输到主表面或改变主表面的内容。对覆盖表面的字此完全由硬件提供,DirectDraw支持显示设备驱动程序所支持的特性,DirectDraw不仿真覆盖表面。可以将覆盖表面想象为一片塑料纸,我们可以在这片塑料上画图并可将其放置在显示器前面。塑料纸覆盖在显示器前面时,你可以看到覆盖和主表面,移去塑料纸后,主表面并没有改变。覆盖表面的工作原理同透明塑料纸覆盖的原理很相似。的显示一个覆盖表面时,就是告诉设备驱动程序在哪里怎样使覆盖表面可见。当显示设备扫描线重画到显示器上时,它检查主表面上的每一个像素,看是否被覆盖所代替。如果是,显示设备就从覆盖表面中抽取相关像素的数据替代。
使用这种方法,显示适配卡在显示器上生成主表面和覆盖表面的合成表面, 产生透明和拉伸效果而不需要改变每个表面的内容。合成表面被送入视频数据流直接送到显示器。因为这种处理和像素替代是硬件级的操作,所以不存在明显的性能损失。另外,这种方法还使得能够用不同的像素格式无缝地合成主表面和覆盖表面。
创建覆盖表面需要在DDSCAPS结构中指定 DDSCAPS_OVERLAY标志,然后调用IDirectDraw2::CreateSurface方法覆盖表面只能在支配内存中创建,因此还必须包含DDSCAPS_VIDEOMEMORY标志。同其他类型的表面一样,通过包含合适的标志,可以 创建单一的覆盖表面,也可以创建由多个覆盖组成的翻转链。
你可以调用IDirectDraw2::GetCaps方法来获取有关支持的覆盖特性的信息。该方法用描述所有的特性信息填充一个DDCAPS结构。在报告硬件特性时,设备驱动程序设置dwCaps成员的标志来指明何时强制执行硬件提供的某种类型的约束。获得的驱动程序的能力后,通过检查dwCaps成员的标志可知道提供的约束的信息。DDCAPS结构包含了9个成员,这9个成员描述了覆盖表面的约束信息。下标列出了同覆盖相关的成员既它们的标志。
成员
标志
dwMaxVisibleOverlays
该成员始终有效
dwCurrVisibleOverlays
该成员始终有效
dwAlignBoundarySrc
DDCAPS_ALIGNBOUNDARYSRC
dwAlignSizeSrc
DDCAPS_ALIGNSIZESRC
dwAlignBoundaryDest
DDCAPS_ALIGNBOUNDARYDES
T
dwAlignSizeDest
DDCAPS_ALIGNSIZEDEST
dwMinOverlayStretch
DDCAPS_OVERLAYSTRETCH
dwMaxOverlayStretch
DDCAPS_OVERLAYSTRETCH
dwMaxVisibleOverlays和dwCurrVisibleOverlays成员指明了硬件可以显示的覆盖的最大数目,以及当前有多少个可见。
dwAlignBoundarySrc、dwAlignSizeSrc、dwAlignBoundaryDest、dwAlignSizeDest和dwAlignStrideAlign成员是硬件报告的矩形的位置和大小的约束。这些成员的值指明了在显示覆盖表面时如何确定源矩形和目的矩形的大小和位置。
dwMinOverlayStretch和dwMaxOverlayStretch是有关拉伸因子的信息。
a、源矩形和目的矩形(Source and Destination Rectangles)
要显示一个覆盖表面,需调用IDirectDrawSurface3::UpdateOverlay方法,在dwFlags 参数中指定 DDOVER_SHOW标志。该方法要求你在lpSrcRect和lpDestRect参数中指定一个源矩形和目的矩形。若使用整个表面,将lpSrcRect参数设为NULL即可。目的矩形是在主表面上产生覆盖表面的位置。
源、目的矩形不必大小相同。一般让目的矩形必源矩形大一些或小一些都可以,硬件在显示时会自动地压缩或拉伸。要想成功地显示一个覆盖表面,可能需要调整源、目的矩形的大小和位置,这一过程是否必要依赖于设备驱动程序的限制。
b、边界和大小调整(Boundary and Size Alignment)
由于不同硬件的限制,一些设备驱动程序对用于显示覆盖表面的源矩形和目的矩形的大小和位置做了约束。要找出设备应用的约束,可调用IDirectDraw2::GetCaps方法,然后检查DDCAPS结构中同覆盖相关的dwCaps成员的标志。下标列出了指定边界和大小调整约束的成员和标志。
类别
标志
成员
边界约束
DDCAPS_ALIGNBOUNDARYSRC
dwAlignBoundarySrc
DDCAPS_ALIGNBOUNDARYDEST
dwAlignBoundaryDest
大小约束
DDCAPS_ALIGNSIZESRC
dwAlignSizeSrc
DDCAPS_ALIGNSIZEDEST
dwAlignSizeDest
约束有两种,边界约束和大小约束。两种约束都以像素的方式表示,并且能够用于源矩形和目的矩形。当然,由于覆盖表面和主表面的像素格式的不同,这些约束也可以不一样。
边界约束影响源矩形和目的矩形放置的位置。dwAlignBoundarySrc和dwAlignBoundaryDest成员的值告诉你如何调整相关矩形的左上角。
矩形左上角的X坐标(RECT结构中的left成员)必须是报告出的值的整数倍。
大小约束影响源矩形和目的矩形的有效宽度。dwAlignSizeSrc和dwAlignSizeDest成员的值以像素的格式指出怎样调整相关矩形的宽。如果按照一个最小拉伸因子拉伸矩形,应确保拉伸后的矩形仍然是大小调整过的。拉伸矩形之后,通过向上圆整来调整宽度,就可以保持最小的拉伸因子。
c、最大和最小拉伸因子(Minimum and Maximum Stretch Factors)
由于硬件的局限性,一些设备限制了目的矩形同相关的源矩形宽度的比较。DirectDraw将这些约束作为拉伸因子。一个拉伸因子就是源矩形同目的矩形的宽度之间的比率。若驱动程序提供有关拉伸因子的信息,在调用 IDirectDraw2::GetCaps 方法时后,它将在DDCAPS结构中设置 DDCAPS_OVERLAYSTRETCH标志。注意,拉伸因子都已经乘以1000,所以值为1300的拉伸因子实际上是1.3。
不压缩和拉伸覆盖目的矩形的设备所报告出的最大最小拉伸因子通常是0。
最小拉伸因子指出目的矩形比源矩形宽多少或窄多少。如果最小拉伸因子大于1000,就必须增加目的矩形的宽度。例如,若拉伸因子为1300,则目的矩形的宽度应该至少是源矩形宽度的1.3倍。如果拉伸因子小于1000,目的矩形就比源矩形的宽度要校最大拉伸因子是目的矩形能够拉伸的最大倍数。例如,若最大拉伸因子是2000,则目的矩形的宽度最多可以是源矩形宽度的2倍。若最大拉伸因子小于1000,
目的矩形就需要压缩。经过拉伸之后,目的矩形必须遵守设备要求的任何大小调整约束。因此,最后在大小调整之前拉伸目的矩形。硬件并不要求调整目的矩形的高度。你可以增加矩形的高度来保持方向比率的不变。
d、覆盖Color Key
象其它类型的表面一样,覆盖表面也使用源、目的Color Key来控制表面之间的透明位块传输操作。因为覆盖表面的显示不是通过位块传输完成的,所以在调用IDirectDrawSurface3::UpdateOverlay方法时就需要采取不同的办法来控制覆盖表面显示在主表面上的方式。答案就是覆盖Color Key。同位块传输相关的Color Key相似,覆盖Color Key也有源Color Key和目的Color Key,可通过调用方法
IDirectDrawSurface3::SetColorKey并利用DDCKEY_DESTOVERLAY标志来设置源Color Key和目的Color Key。覆盖表面能够将位块传输和覆盖Color Key结合在一起来控制位块传输操作和覆盖显示操作,两种不同类型的Color Key并不互相冲突。
IDirectDrawSurface3::UpdateOverlay方法用源Color Key检查覆盖表面中哪个像素应该是透明的,允许主表面透过覆盖表面显示。同样,该方法使用目的覆盖Color Key来确定主表面显示时哪部分允许倍覆盖表面所覆盖,其显示效果同位块传输Color Key相同。
e、 定位覆盖表面(Positioning Overlay Surfaces)
在最先调用IDirectDrawSurface3::UpdateOverlay方法显示一个覆盖时,可以用方法 IDirectDrawSurface3::SetOverlayPosition来更新目的矩形。必须确保你指定的目的矩形的位置遵守边界对齐约束, IDirectDraw2::SetOverlayPosition方法并不执行剪切工作,使用了可能引起覆盖越界的坐标会使调用该方法失败,并返回 DDERR_INVALIDPOSITION。
f、创建覆盖表面(Creating Overlay Surfaces)
象所有的表面一样,你可以调用IDirectDraw2::CreateSurface方法来创建一个覆盖表面。创建覆盖表面还需要在相关的结构DDSCAPS 中包含 DDSCAPS_OVERLAY标志。覆盖支持许多显示设备,因此不能判断一个给定的像素格式是否被大多数的驱动程序所支持,必须做好准备使之能工作于多种像素格式。你可以调用 IDirectDraw2::GetFourCCCodes方法来获得驱动程序支持的有关非RGB格式的信息。要创建一个覆盖表面,最好使用最常用的像素格式,若给定的像素格式不被支持,DirectDraw会使用显示设备所支持的其他像素格式。创建覆盖表面翻转链也是允许的。
g、翻转覆盖表面(Flipping Overlay Surfaces)
同其他类型的表面一样,你可以创建覆盖表面翻转链。一旦创建了覆盖的翻转链,就可以调用方法IDirectDrawSurface3::Flip来翻转这些覆盖。软件解压在调用Flip方法显示覆盖表面时可使用DDFLIP_ODD和 DDFLIP_EVEN标志以减少运动赝象。如果驱动程序支持奇──偶翻转,在获得了驱动程序的能力后,DDCAPS2_CANFLIPODDEVEN标志将会在DDCAPS结构中设定。一旦设定了DDCAPS2_CANFLIPODDEVEN,就可在调用IDirectDrawSurface3::UpdateOverlay方法时包含DDOVER_BOB标志以通知驱动程序使用“Bob”算法最小化运动赝象。此后,用DDFLIP_ODD或 DDFLIP_EVEN标志调用Flip时,驱动程序将会自动调整覆盖的源矩形来弥补抖动赝象。
如果获取硬件的能力后没有设置DDCAPS2_CANFLIPODDEVEN标志,但在调用UpdateOverlay时又使用了
DDOVER_BOB标志,那么该调用将会失败。