分享
 
 
 

DirectX5.0最新游戏编程指南 DirectDraw教程篇 四、使用覆盖表面

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

四、使用覆盖表面

本例将使用DirectX SDK包含的Mosquito范例程序一步一步地说明怎样在程序中使用DirectDraw和硬件支持的覆盖表面。Mosquito使用覆盖表面的翻转链而没有位块传输到主表面将运动位图显示在桌面上。Mosquito程序调整覆盖表面的特征以适应硬件的限制。

1、创建一个主表面

要使用覆盖表面,必须先要初始化一个主表面,覆盖表面将显示在该主表面上。Mosquito用如下代码创建了一个主表面:

// Zero-out the structure and set the dwSize member.

ZeroMemory(&ddsd, sizeof(ddsd));

ddsd.dwSize = sizeof(ddsd);

// Set flags and create a primary surface.

ddsd.dwFlags = DDSD_CAPS;

ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

ddrval = g_lpdd->CreateSurface(&ddsd, &g_lpddsPrimary, NULL );

程序先初始化将要使用的DDSURFACEDESC结构,然后设定适当的标志调用IDirectDraw2::CreateSurface方法创建主表面。在对该方法的调用中,第一个参数是描述将要创建的表面的DDSURFACEDESC结构的指针;第二个参数是一个变量的指针,如果调用成功,该变量将接收IDirectDrawSurface接口的指针;第三个参数设为NULL表明没有COM集合。

2、检测硬件对覆盖的支持

初始化DirectDraw后,需要检测设备是否支持覆盖表面。因为DirectDraw不能仿真覆盖,所以如果硬件不支持覆盖,就不能继续下面的工作。你可以用IDirectDraw2::GetCaps方法获取硬件设备驱动程序的能力检测覆盖支持。在调用该方法之后,查看DDCAPS结构中的dwFlags成员是否包含有DDCAPS_OVERLAY标志。若有就表明支持覆盖,否则就不支持。

下面的代码是Mosquito程序中的一部分,它表明了怎样检测硬件的覆盖支持能力:

BOOL AreOverlaysSupported()

{

DDCAPS capsDrv;

HRESULT ddrval;

// Get driver capabilities to determine Overlay support.

ZeroMemory(&capsDrv, sizeof(capsDrv));

capsDrv.dwSize = sizeof(capsDrv);

ddrval = g_lpdd->GetCaps(&capsDrv, NULL);

if (FAILED(ddrval))

return FALSE;

// Does the driver support overlays in the current mode?

// (Currently the DirectDraw emulation layer does not support overlays.

// Overlay related APIs will fail without hardware support).

if (!(capsDrv.dwCaps & DDCAPS_OVERLAY))

return FALSE;

return TRUE;

}

程序首先调用IDirectDraw2::GetCaps方法获取设备驱动程序的能力。第一个参数是DDCAPS结构的地址指针;因为程序不需要关仿真的信息,所以第二个参数就设为NULL。获取驱动程序的能力后,程序使用了逻辑“与”来检查dwFlags成员是否包含有 DDCAPS_OVERLAY标志。若否,程序返回FALSE表明失败。若是,就返回TRUE表明显示设备支持覆盖表面。

3、创建一个覆盖表面

如果知道显示设备支持覆盖表面,就可以创建一个。因为没有指明设备怎样支持覆盖表面的标准,所以不能够期望创建任意大小的像素格式的表面。另外,也不要期望第一次创建覆盖表面就会成功。因此,必须作好准备进行多次创建的尝试,直到有一个能够工作为止。

Mosquito程序在创建表面时遵循“best case to worst case”的原则,首先尝试创建一个三缓冲页翻转复杂覆盖表面。如果尝试失败,程序就改变方法尝试用其它通用的迅速格式来配置。下面的代码就是这一思路的表现:

ZeroMemory(&ddsdOverlay, sizeof(ddsdOverlay));

ddsdOverlay.dwSize = sizeof(ddsdOverlay);

ddsdOverlay.dwFlags= DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH |

DDSD_BACKBUFFERCOUNT| DDSD_PIXELFORMAT;

ddsdOverlay.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_FLIP |

DDSCAPS_COMPLEX | DDSCAPS_VIDEOMEMORY;

ddsdOverlay.dwWidth =320;

ddsdOverlay.dwHeight =240;

ddsdOverlay.dwBackBufferCount=2;

// Try to create an overlay surface using one of the pixel formats in our

// global list.

i=0;

do{

ddsdOverlay.ddpfPixelFormat=g_ddpfOverlayFormats[i];

// Try to create the overlay surface

ddrval = g_lpdd->CreateSurface(&ddsdOverlay, &g_lpddsOverlay, NULL);

} while( FAILED(ddrval) && (++i < NUM_OVERLAY_FORMATS) );

程序设置DDSURFACEDESC结构中的标志和值以反映三缓冲页翻转复杂覆盖表面,然后执行循环。在循环中,程序尝试用各种常用的像素格式创建要求的表面。如果尝试成功,循环就终止。如果尝试失败,说明很有可能是显示硬件没有足够的显示内存支持三缓冲的方案或者硬件根本就不支持翻转覆盖表面。在这种情况下,在最小要求的配置下使用一个单一的非翻转覆盖表面,代码如下:

// If we failed to create a triple buffered complex overlay surface, try

// again with a single non-flippable buffer.

if(FAILED(ddrval))

{

ddsdOverlay.dwBackBufferCount=0;

ddsdOverlay.ddsCaps.dwCaps=DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;

ddsdOverlay.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT;

// Try to create the overlay surface

ddrval = g_lpdd->CreateSurface(&ddsdOverlay, &g_lpddsOverlay, NULL);

i=0;

do{

ddsdOverlay.ddpfPixelFormat=g_ddpfOverlayFormats[i];

ddrval = g_lpdd->CreateSurface(&ddsdOverlay, &g_lpddsOverlay, NULL);

} while( FAILED(ddrval) && (++i < NUM_OVERLAY_FORMATS) );

// We couldn't create an overlay surface. Exit, returning failure.

if (FAILED(ddrval))

return FALSE;

}

上面的代码对DDSURFACEDESC结构中的标志和值复原来反映一个单一的非翻转覆盖表面,然后通过像素格式的循环尝试创建表面。如果创建表面成功,循环就停止。如果不成功,程序返回FALSE表明创建表面失败。在成功地创建覆盖表面之后,就可将位图装入其中以供显示。

4、显示覆盖表面

创建了覆盖表面之后就可以显示它了。通常,硬件在用于显示覆盖的矩形的位置和像素格式上加上对齐约束。另外,还需要经常通过调整目的矩形的宽度来说明最小要求的拉伸因子以成功地显示覆盖表面。Mosquito程序按照以下的步骤准备和显示覆盖表面。

4.1、检测显示的最小要求

大部分的显示硬件在显示覆盖时都会加上约束。你必须很仔细地调整覆盖使之满足这些约束。可以通过调用IDirectDraw2::GetCaps方法获得有关这些约束的信息。该方法填充的结构DDCAPS包含了有关覆盖能力和使用约束的信息。不同硬件的约束是不同的,因此必须始终要查看包含在dwFlags成员的标志以确定附加的是哪一种约束。

Mosquito程序开始时先获取硬件的能力,然后采用基于最小拉伸因子的方法,如下所示:

// Get driver capabilities

ddrval = g_lpdd->GetCaps(&capsDrv, NULL);

if (FAILED(ddrval))

return FALSE;

// Check the minimum stretch and set the local variable accordingly.

if(capsDrv.dwCaps & DDCAPS_OVERLAYSTRETCH)

uStretchFactor1000 = (capsDrv.dwMinOverlayStretch>1000) ? capsDrv.dwMinOverlayStretch : 1000;

else

uStretchFactor1000 = 1000;

上面的代码调用IDirectDraw2::GetCaps方法获取硬件的能力。在本例中,第一个参数是DDCAPS结构的指针;第二个参数是NULL,表明了不需要获取有关仿真的信息。程序在一个临时变量中保留了最小拉伸因子以备以后之用。如果驱动程序报告出的拉伸因子大于1000,就表明驱动程序要求所有的目的矩形沿X轴的方向拉伸。例如,若拉伸因子是1.3,源矩形宽320个像素,目的矩形就必须至少要有416(320X1.3=416)个像素的宽。如果驱动程序报告出的拉伸因子小于1000,就表明驱动程序能够显示比源矩形小的覆盖,但不能伸展覆盖。

下面的代码是测定描述驱动程序的大小对齐约束的值:

// Grab any alignment restrictions and set the local variables acordingly.

uSrcSizeAlign = (capsDrv.dwCaps & DDCAPS_ALIGNSIZESRC)?capsDrv.dwAlignSizeSrc:0;

uDestSizeAlign= (capsDrv.dwCaps & DDCAPS_ALIGNSIZESRC)?capsDrv.dwAlignSizeDest:0;

例中使用了更多的临时变量来保存从dwAlignSizeSrc和dwAlignSizeDest成员中获得的大小对齐约束。这些值提供了有关像素宽度对齐约束的信息,并且在以后设定源矩形和目的矩形的大小时需要用到。源矩形和目的矩形必须是这些值的倍数。

最后,程序测定描述目的矩形边界对齐约束的值:

// Set the "destination position alignment" global so we won't have to

// keep calling GetCaps() every time we move the overlay surface.

if (capsDrv.dwCaps & DDCAPS_ALIGNBOUNDARYDEST)

g_dwOverlayXPositionAlignment = capsDrv.dwAlignBoundaryDest;

else

g_dwOverlayXPositionAlignment = 0;

上面的代码使用了一个全局变量来保存目的矩形边界约束的值,该值是从dwAlignBoundaryDest成员中的得来的,在以后程序重新放置覆盖时将会用到。你必须设定目的矩形左上角的X坐标在像素格式上同该值对齐。也就是说,如果该值是4,就只能指定左上角的X坐标为0,4,8,12等像素宽的目的矩形。Mosquito程序首先在0,0处显示覆盖,于是在第一次显示覆盖之前就不需要获取约束信息。但是因为不同应用程序的实现过程可能不同 ,所以你可能需要在显示覆盖之前检查这些信息以调整目的矩形。

2、设置源矩形和目的矩形

在获得了驱动程序的覆盖约束之后,就应该设定有关源矩形和目的矩形的值,确保能够正确显示覆盖。下面的代码就设定了源矩形的特征:

// Set initial values in the source RECT.

rs.left=0; rs.top=0;

rs.right = 320;

rs.bottom = 240;

// Apply size alignment restrictions, if necessary.

if (capsDrv.dwCaps & DDCAPS_ALIGNSIZESRC && uSrcSizeAlign)

rs.right -= rs.right % uSrcSizeAlign;

上面的代码设置了包含整个表面大小的初始值。如果设备驱动程序要求大小对齐,程序就调整源矩形来保证。程序调整了源矩形的宽度使之比初始值要小,这是因为如果不是完全重新创建表面,就不能够扩展宽度。

在设定了源矩形的大小后,需要设置和调整目的矩形的大小。这一过程需要稍微多一点的工作,因为目的矩形可能需要先拉伸再调整以符合大小对齐约束。下面的代码根据最小拉伸因子来设置和调整目的矩形的大小:

// Set up the destination RECT, starting with the source RECT values.

// We use the source RECT dimensions instead of the surface dimensions in

// case they differ.

rd.left=0; rd.top=0;

rd.right = (rs.right*uStretchFactor1000+999)/1000; // (Adding 999 avoids integer truncation problems.)

// (This isn't required by DDraw, but we'll stretch the

// height, too, to maintain aspect ratio).

rd.bottom = rs.bottom*uStretchFactor1000/1000;

前面的代码先设置目的矩形的左上角位置,再根据最小拉伸因子设定目的矩形的宽度。根据拉伸因子调整矩形时,注意程序在宽度和拉伸因子的乘积上又加了999,这是为了避免出现整数截断,整数截断会导致矩形同最小拉伸因子的要求不一致。程序在拉伸宽度后也拉伸了矩形的高度。不过,对高度的拉伸并不是必须的,这里只是为了保持位图的长宽比率避免出现失真的现象。

拉伸目的矩形后,程序对其进行调整以保持和大小对齐约束一致,下面是相应的代码:

// Adjust the destination RECT's width to comply with any imposed

// alignment restrictions.

if (capsDrv.dwCaps & DDCAPS_ALIGNSIZEDEST && uDestSizeAlign)

rd.right = (int)((rd.right+uDestSizeAlign-1)/uDestSizeAlign)*uDestSizeAlign;

程序检测硬件能力的标志,查看驱动程序是否加了矩形大小对齐约束。如果是,就增加目的矩形的宽度使之满足大小对齐约束。这里对矩形的调整是扩展其宽度而不能减少其宽度,因为减少宽度可能会导致目的矩形比最小拉伸因子要求的还要小,从而引起显示覆盖表面失败。

3、显示覆盖表面

在设置了源矩形和目的矩形后,就可以显示覆盖了。如果显示覆盖之前的准备工作正确,显示覆盖会很简单。Mosquito程序用如下的代码来显示覆盖:

// Set the flags we'll send to UpdateOverlay

dwUpdateFlags = DDOVER_SHOW | DDOVER_DDFX;

// Does the overlay hardware support source color keying?

// If so, we can hide the black background around the image.

// This probably won't work with YUV formats

if (capsDrv.dwCKeyCaps & DDCKEYCAPS_SRCOVERLAY)

dwUpdateFlags |= DDOVER_KEYSRCOVERRIDE;

// Create an overlay FX structure so we can specify a source color key.

// This information is ignored if the DDOVER_SRCKEYOVERRIDE flag isn't set.

ZeroMemory(&ovfx, sizeof(ovfx));

ovfx.dwSize = sizeof(ovfx);

ovfx.dckSrcColorkey.dwColorSpaceLowValue=0; // Specify black as the color key

ovfx.dckSrcColorkey.dwColorSpaceHighValue=0;

// Call UpdateOverlay() to displays the overlay on the screen.

ddrval = g_lpddsOverlay->UpdateOverlay(&rs, g_lpddsPrimary, &rd, dwUpdateFlags, &ovfx);

if(FAILED(ddrval))

return FALSE;

程序开始在临时变量dwUpdateFlags中设定了DDOVER_SHOW和DDOVER_DDFX标志,指明该覆盖是第一次显示,硬件应该使用包含在DDOVERLAYFX结构中的效果信息完成这一工作。然后,程序检查DDCAPS结构确定覆盖是否支持源Color Key。如果是,DDOVER_KEYSRCOVERRIDE标志就包含在dwUpdateFlags变量中利用源Color Key,程序也据此设置Color Key。

准备工作完成之后,程序调用IDirectDrawSurface3::UpdateOverlay方法来显示覆盖。在对该方法的调用中,第一个参数和第三个参数是已调整的源矩形和目的矩形的地址。第二个参数是覆盖显示在其上的主表面的地址。第四个参数是 包括了放置于此前准备的dwUpdateFlags变量中的标志。第五个参数是DDOVERLAYFX结构的地址,该结构中的成员将设定同那些标志相匹配。

如果硬件只支持一个覆盖表面而且该表面正在使用,UpdateOverlay方法就会失败,并返回DDERR_OUTOFCAPS。另外,有可能硬件报告出的最小拉伸因子过小,在UpdateOverlay方法失败后,你就需要尝试减少目的矩形的宽度来应付这种可能性。不过,这种情况很少发生,在Mosquito中也只是简单地返回一个错误信息。

5、更新覆盖的显示位置

显示覆盖表面之后,有时可能就不需要对覆盖左其它的操作了。但有些软件还需要重新放置覆盖,改变覆盖的显示位置。Mosquito程序就使用IDirectDrawSurface3::SetOverlayPosition方法重新放置覆盖,代码如下:

// Set X- and Y-coordinates

.

.

.

// We need to check for any alignment restrictions on the X position

// and align it if necessary.

if (g_dwOverlayXPositionAlignment)

dwXAligned = g_nOverlayXPos - g_nOverlayXPos % g_dwOverlayXPositionAlignment;

else

dwXAligned = g_nOverlayXPos;

// Set the overlay to its new position.

ddrval = g_lpddsOverlay->SetOverlayPosition(dwXAligned, g_nOverlayYPos);

if (ddrval == DDERR_SURFACELOST)

{

if (!RestoreAllSurfaces())

return;

}

程序开始对齐矩形以满足可能存在的任何目的矩形边界对齐约束。当程序此前调用IDirectDraw2::GetCaps方法时,全局变量 g_dwOverlayXPositionAlignment已经设定为同DDCAPS结构中dwAlignBoundaryDest成员所报告出的值相等。如果存在目的矩形约束,程序就据此调整新的X坐标为像素对齐的。若不满足要求,覆盖表面就不能显示。

在完成了对X坐标的调整之后,程序调用IDirectDrawSurface3::SetOverlayPosition方法重新放置覆盖。在调用中第一个参数是对齐的新的X坐标,第二个参数的新的Y坐标。这些值表明了覆盖左上角新的位置。这里并不需要取得宽度和高度信息,因为DirectDraw在开始用

IDirectDrawSurface3::UpdateOverlay方法显示覆盖时就已经获得了表面大小的信息。如果因为一个或多个表面丢失而引起的重新放置覆盖表面的失败,Mosquito程序就调用一个应用定义的函数来恢复这些表面并重新装入它们的位图。

注意,不要使用太靠近目标表面的右、下边界的坐标。因为IDirectDraw2::SetOverlayPosition方法并不执行剪切功能,所以使用那些可能导致覆盖超出目标表面边界的坐标会引起调用的失败,并返回DDERR_INVALIDPOSITION。

6、隐藏覆盖表面

如果不再需要一个覆盖表面或只想不让覆盖可见,就可以设定适当的标志调用IDirectDrawSurface3::UpdateOverlay方法来隐藏该覆盖表面。Mosquito用以下代码隐藏覆盖表面并准备关闭应用程序:

void DestroyOverlay()

{

if (g_lpddsOverlay){

// Use UpdateOverlay() with the DDOVER_HIDE flag to remove an overlay

// from the display.

g_lpddsOverlay->UpdateOverlay(NULL, g_lpddsPrimary, NULL, DDOVER_HIDE, NULL);

g_lpddsOverlay->Release();

g_lpddsOverlay=NULL;

}

}

在调用IDirectDrawSurface3::UpdateOverlay时,对源矩形和目的矩形指定了NULL,因为在隐藏覆盖的过程中不需要源矩形和目的矩形。

同理,第五个参数也被指定为NULL是因为不使用覆盖效果。第二个参数是目标表面的指针。最后,程序在第四个参数使用 DDOVER_HIDE标志表明该覆盖将从视口中取消。

程序在隐藏覆盖之后,释放了它的IDirectDrawSurface3接口,并且将全局变量设为NULL使之变得无效。对于Mosquito程序来说,覆盖就不再需要了。如果在应用程序中还需要使用该覆盖,就只需简单地隐藏覆盖,而不要释放它,然后在需要的时候再重新显示。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有