创建翻转表面
在设置了显示模式后,必须创建用于放置应用程序的表面。因为DDex1例子程序已用IDirectDraw7::SetCooperativeLevel方法将模式设置为独占(全屏)模式,所以可以创建在表面之间进行翻转的表面。如果使用SetCooperativeLevel将模式设置为 DDSCL_NORMAL,就可以创建在表面之间进行位块传输的惟一表面。创建翻转表面的步骤如下:
(1)定义表面需求
创建翻转表面的第一步是在DDSURFACEDESC2结构中定义表面需求。下列例子程序显示了创建翻转表面所需的结构定义和标志。
// 创建有一个后台缓冲区的主表面
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
DDSCAPS_FLIP | DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;
在上面的例子中,将dwSize成员设置为DDSURFACEDESC2结构的大小。这样做是为了阻止所使用的任何DirectDraw方法调用返回无效的成员错误(提供dwSize成员是为了便于将来对DDSURFACEDESC2结构进行扩展)。
dwFlags成员用于确定将DDSURFACEDESC2结构中的哪些成员用来填充有效信息。对于DDex1例子程序,dwFlags的值用于指定希望使用DDSCAPS结构(DDSD_CAPS)以及希望创建一个后台缓冲区(DDSD_BACKBUFFERCOUNT)。
例子程序中的dwCaps成员表明将在DDSCAPS结构中使用的标志。在这里该成员指定了一个主表面(DDSCAPS_PRIMARYSURFACE)、一个翻转表面(DDSCAPS_FLIP)和一个复合表面(DDSCAPS_COMPLEX)。
最后,例子程序指定了一个后台缓冲区(后台缓冲区是写入背景和实体的区域),然后将后台缓冲区翻转到主表面上。在DDex1例子程序中,后台缓冲区数设置为1。但是可以创建显示内存所能支持的任意数量的后台缓冲区。
表面内存可以是显示内存或系统内存。如果应用程序用尽了显示内存,DirectDraw就使用系统内存(例如在只有1 MB RAM的显示适配器上指定了不止一个后台缓冲区)。也可以通过将DDSCAPS结构中的dwCaps成员设置为DDSCAPS_SYSTEMMEMORY或DDSCAPS_VIDEOMEMORY,来指定是否只使用系统内存或显示内存(如果指定了DDSCAPS_VIDEOMEMORY,但没有足够的内存用来创建表面,则IDirectDraw7:: CreateSurface返回一个DDERR_OUTOFVIDEOMEMORY错误)。
(2)创建表面
填充DDSURFACEDESC2结构之后,用该结构和lpDD可以调用IDirectDraw7:: CreateSurface方法。lpDD是指向由DirectDrawCreateEx函数创建的DirectDraw对象的指针。调用IDirectDraw7::CreateSurface方法的过程如下:
ddrval = lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);
if(ddrval == DD_OK)
{
// lpDDSPrimary指向新的表面
}
else
{
// 创建表面失败
return FALSE;
}
如果调用成功,则lpDDSPrimary参数将指向由CreateSurface返回的主表面。在得到指向主表面的指针之后,就可以使用IDirectDrawSurface7::GetAttachedSurface方法获取指向后台缓冲区的指针,如下所示:
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
ddrval = lpDDSPrimary->GetAttachedSurface(&ddcaps, &lpDDSBack);
if(ddrval == DD_OK)
{
// lpDDSBack指向后台缓冲区
}
else
{
return FALSE;
}
通过提供表面的主表面地址和用DDSCAPS_BACKBUFFER标志设置能力值,如果IDirectDrawSurface7::GetAttachedSurface调用成功,lpDDSBack参数将指向后台缓冲区。
5. 在表面上绘图
在创建了主表面和后台缓冲区后,DDex1 例子程序通过使用标准Windows GDI函数在主表面和后台缓冲区上输出了一些文本,如下列例子所示:
if (lpDDSPrimary->GetDC(&hdc) == DD_OK)
{
SetBkColor(hdc, RGB(0, 0, 255));
SetTextColor(hdc, RGB(255, 255, 0));
TextOut(hdc, 0, 0, szFrontMsg, lstrlen(szFrontMsg));
lpDDSPrimary->ReleaseDC(hdc);
}
if (lpDDSBack->GetDC(&hdc) == DD_OK)
{
SetBkColor(hdc, RGB(0, 0, 255));
SetTextColor(hdc, RGB(255, 255, 0));
TextOut(hdc, 0, 0, szBackMsg, lstrlen(szBackMsg));
lpDDSBack->ReleaseDC(hdc);
}
上面的例子用IDirectDrawSurface7::GetDC方法来获取设备环境句柄,并在方法的内部锁定了表面。如果不打算使用需要设备环境句柄的Windows函数,可以使用IDirectDrawSurface7::Lock和IDirectDrawSurface7::Unlock方法来锁定和解锁后台缓冲区。
锁定表面内存(整个表面或表面的一部分)可确保应用程序和系统传输器不能同时访问表面内存。这样可以防止在应用程序写入表面内存时发生错误。另外,应用程序在解锁表面内存之前不能进行页面翻转。
在锁定表面后,例子程序用标准Windows GDI函数SetBkColor设置背景颜色,用SetTextColor选择要放在背景上的文本颜色,用TextOut在表面上输出文本和背景颜色。
在将文本写入到缓冲区后,例子程序用IDirectDrawSurface7::ReleaseDC方法来解锁表面并释放句柄。无论在什么时候,只要应用程序完成写入后台缓冲区,就必须调用IDirectDrawSurface7::ReleaseDC或IDirectDrawSurface7::Unlock,这取决于应用程序本身。应用程序在解锁表面以前不能翻转表面。
通常将数据写入到后台缓冲区,然后将后台缓冲区翻转到主表面上使其显示出来。DDex1例子程序在第一次翻转之前有明显的延迟,因此DDex1在初始化函数中写入到主缓冲区,以便防止在显示表面之前产生的延迟。在本例子程序后面的步骤中可以看到,DDex1例子程序在WM_TIMER期间写入后台缓冲区。一般会在初始化函数或标题页中将数据写入主表面。
注意:在用IDirectDrawSurface7::Unlock解锁表面之后,表面内存指针是无效的。必须再次使用IDirectDrawSurface7::Lock,才能获得有效的表面内存指针。
6. 输出到表面
在DDex1中WM_TIMER消息的前面一半的作用是写入后台缓冲区,如下列例子所示:
case WM_TIMER:
// 翻转表面
if(bActive)
{
if (lpDDSBack->GetDC(&hdc) == DD_OK)
{
SetBkColor(hdc, RGB(0, 0, 255));
SetTextColor(hdc, RGB(255, 255, 0));
if(phase)
{
TextOut(hdc, 0, 0, szFrontMsg, lstrlen(szFrontMsg));
phase = 0;
}
else
{
TextOut(hdc, 0, 0, szBackMsg, lstrlen(szBackMsg));
phase = 1;
}
lpDDSBack->ReleaseDC(hdc);
}