开始看Wince下面的DDraw编程了,在wince的对应目录下面有几个例子:
在使用 DirextDraw时,需要首先创建一个对象DirectDraw 的实体,该对象实体代表了微机显示适配器。然后,使用接口所提供的方法来操作该对象实体,使之完成有关命令和任务。接着,你还需要创建一个或多个 DirectDraw-surface对象的实体,以便能在图形表面(Surface)上展示你的游戏画面。
下面,在例程 DDEX1 中展示如何使用Directx 3 SDK来 DirectDraw对象实体,如何创建一个带有后台缓冲区的基本表面(Surface),以及如何弹出表面(Surface)。
首先是创建DirectDraw对象并且获得缓存surface的句柄:
/*
* Create the main DirectDraw object.
*/
ddrval = DirectDrawCreate(NULL,&lpDD,NULL);
if(ddrval==DD_OK)
{
//Get exclusive mode.
Ddrval=lpdd->SetCooperativeLevel(hwnd,
DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN);
if(ddrval==DD_OK)
{
//Create the primary surface with 1 back buffer.
Ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS / DSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
DDSCAPS_FLIP |
DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;
ddrval = lpDD->CreateSurfae(&ddsd, &lpDDSPrimary,
NULL);
if(ddrval == DD_OK)
{
//Cet a pointer to the back buffer.
Ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
ddrval = lpDDSPrimary->GetAttachedSurface(&ddscaps,
&lpDDSBack);
这里创建了主表面并且由主表面获得了离屏表面,下面就是画图在这些表面上了:
if( ddrval == DD_OK)
{ // Draw some text.
//对当前显示的缓存直接操作,写一点东西在上面
If(lpDDSPrimary->GetDC(&hdc) == DD_OK)
{
SetBkColor(hdc, RGB(0,0,255));
SetTextColor( hdc,RGB(255,255,0 ) );
TextOut( hdc, 0, 0, sxFrontMsg, lstrlen(szFrontMsg ));
lpDDSPrimary->ReleaseDC(hdc);
}
//对缓存进行操作
if(lpDDSBack->GetDC(&hdc) == DD_OK)
{
SetBkColor( hdc, RGB(0, 0, 255 ) );
SetTextColor( hdc, RGB( 255,255, 0 ) ):
TexOut( hdc, 0, 0, szBackMsg, lstrlen( szBackMsg ) );
lpDDSBack->ReleaseDC(hdc);
}
// Create a timer to flop the pages.
If(SetTimer( hwnd, TIMER_ID, TIMER_RATE, NULL))
{
return TRUE;
}
}
}
}
}
}
这样初始化工作基本完成。这里有几个要简要地说明一下:
首先是设置显示模式,如果设置为DDSCL_EXCLUSIVE 和 DDSCL_FULLSCREEN,那么你的程序就得到了整个屏幕的控制权,也就是全屏显示,但是这并不是说桌面就没有了,还是可以切换出来的。
然后,你可以改变一下你的显示模式,这个是和你的底层驱动相关的,因为我们的显示屏支持的格式很多,支持的分辨率也有很多,因而要选择一个显示模式,当然你可以使用默认值,但是你也可以改变:
IDirectDraw::SetdisplayMode
hRet = g_pDD->SetDisplayMode(640, 480, 8, 0, 0);
然后就是创建主表面和交换页了
// Create the primary surface with one back buffer.
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP |
DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;
//这里我们设置的是只有一个缓存页面
这个参数是什么意思呢?首先设置dwsize,这个成员主要是为了以后的扩展。然后就是一个dwFlag,就是说这两个方面的变量我们是需要改变的,一个是CAPS另一个是BACKBUFFCOUNT,然后下面是具体的值,这里我们可以仔细的看一下DDSCAPS_PRIMARYSURFACE 表示一个主表面;DDSCAPS_FLIP表示一个交换页;DDSCAPS_COMPLEX表示一个合成页。
最后,例子指定了一个后台缓冲.后台缓冲就是实际的绘图操作先在那里完成,然后,再快速的翻动(flip)到主平面(primary surface)上.在DDEx1中,后台缓冲的数目是1.其实,你要你的显存允许,你想建几个就建几个.你想知道更多的关于创建大于1块缓冲的信息,可以去看 "triple buffering".
创建的"平面"占用的存储空间,可以是系统内存也可以是显存.如果应用程序使用的空间超出了 显存,DirectDraw就会使用系统内存.(例如你指定多块缓存在一个仅有1MB显存的是配器上).你也可以这样设置DDSCAPS结构的 dwCaps成员,设成DDSCAPS_VIDEOMEMEORY或DDCAPS_SYSTEMMEMORY以达到只用显存或只用内存.(如指定用显存, 而显存不够,IDirectDraw7::CreateSurface返回一个DDERR_OUTOFVIDEOMEMORY错误)
在填充完DDSURFACEDESC2结构后,你就可以用他和g_pDD指针(DirectDrawCreateEx函数创建的DirectDraw对象的指针)调用IDirectDraw7::CreateSurface方法,就像下面 :
hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);
if (hRet != DD_OK)
{
// g_pDDSPrimary points to the new surface.
}
else
{
// The surface was not created.
return FALSE;
}
g_pDDSPrimary参数将指向由CreateSurface函数返回的主平面(primary surface)的地址,如果调用成功的话.
指向主平面(primary surface)的指针有效后,就可以调用IDirectDrawSurface7::GetAttachedSurface方法去得到一个指向缓冲的指针.如下:
ZeroMemory(&ddscaps, sizeof(ddscaps));
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
hRet = g_pDDSPrimary->GetAttachedSurface(&ddscaps, &g_pDDSBack);
if (hRet != DD_OK)
{
// g_pDDSBack points to the back buffer.
}
else
{
return FALSE;
}
如果IDirectDrawSurface7::GetAttachedSurf ace调用成功,g_pDDSBack参数将指向缓存区.
在主平面(primary surface)和后台缓冲(back buffer)创建完成后,例子DDEx1用windows标准GDI函数输出了一些文本在缓冲上.如下:
(注意使用GDI函数不一定就是GDI程序)
if (g_pDDSBack->GetDC(&hdc) == DD_OK)
{
SetBkColor(hdc, RGB(0, 0, 255));
SetTextColor(hdc, RGB(255, 255, 0));
if (phase)
{
GetClientRect(hWnd, &rc);
GetTextExtentPoint(hdc, szMsg, lstrlen(szMsg), &size);
TextOut(hdc, (rc.right - size.cx) / 2, (rc.bottom - size.cy) / 2,
szMsg, sizeof(szMsg) - 1);
TextOut(hdc, 0, 0, szFrontMsg, lstrlen(szFrontMsg));
phase = 0;
}
else
{
TextOut(hdc, 0, 0, szBackMsg, lstrlen(szBackMsg));
phase = 1;
}
g_pDDSBack->ReleaseDC(hdc);
}
这个例子使用the IDirectDrawSurface7::GetDC方法获得设备上下文的句柄,并且,为了准备写入,将缓冲上锁.如果你不准备用需要设备上下文句柄的windows函数,你可以使用IDirectDrawSurface7::Lock IDirectDrawSurface7::Unlock方法加解锁.
接下来,phase变量决定了应该输出到主缓存消息(primary buffer message)还是后台缓存消息(back buffer message).如果phase=1,输出
主缓存消息,且设phase=0.如果phase=0,输出后台缓存消息,且设phase=1.
当消息输出到缓存区后,用IDirectDrawSurface7::ReleaseDC 方法将缓存区解锁.
对创建平面的内存上锁,是保证你的程序和系统不能同时对此内存进行存取.这防止你写入"平面"内存事发生错误.另外,不对"平面"内存解锁,你的程序将无法翻转平面.
平面被加锁之后,例子中用了标准windowsGDI函数:SetBkColor设置背景色,SetTextColor设置在背景上显示的字的颜色,用TextOut在"表面"上输出文字和背景色.
当文字被输出到缓存之后,例子应用IDirectDrawSurface7::ReleaseDC方法解锁"表面"并且释放句柄.不论在什么时候你的程序完成了对缓存的写入,一定要调用IDirectDrawSurface7::ReleaseDC 或IDirectDrawSurface7::Unlock之一,具体用哪个,由你的程序而定.不解锁,你的程序将不能翻转平面.
注意:用IDirectDrawSurface7::Unlock对"平面"解锁后,指向"平面"的指针将会无效.你必须再用IDirectDrawSurface7::lock去获得一个有效的指针.
翻转平面
在DDEx1中,WM_TIMER消息引发从缓存到主平面的翻转.当"平面"内存解锁后,你就可以用IDirectDrawSurface7::Flip方法实现从缓存到主平面的翻转.如下:
case WM_TIMER:
// Update and flip surfaces
if (g_bActive && TIMER_ID == wParam)
{
UpdateFrame(hWnd);
while (TRUE)
{
hRet = g_pDDSPrimary->Flip(NULL, 0);
if (hRet == DD_OK)
break;
if (hRet == DDERR_SURFACELOST)
{
hRet = g_pDDSPrimary->Restore();
if (hRet != DD_OK)
break;
}
if (hRet != DDERR_WASSTILLDRAWING)
break;
}
}
break;
在例子中,g_pDDSPrimary参数指示主平面和与他连接的缓存.当IDirectDrawSurface7::Flip被调用,前后平面将会交换(只是交换指针,没有实际的数据交换).如果翻转成功,返回DD_OK,跳出循环.
如果翻转返回的是DDERR_SURFACELOST值,就试图用IDirectDrawSurface7::Restore 方法保存平面.如果保存成功,程序循环到调用IDirectDrawSurface7::Flip,再试一次.如果保存不成功,程序跳出循环,返回一个错误.
注意:当你调用IDirectDrawSurface7::Flip 时,翻转动作并不能马上完成.如果,前一个翻转动作还没有完成,IDirectDrawSurface7::Flip 会返回DDERR_WASSTILLDRAWING.在这个例子中,IDirectDrawSurface7::Flip会一直调用直到返回DD_OK.
所以说这个while循环很必要,因为这个Flip是一个非阻塞的函数,调用就会返回,一直调用到成功为止。
当你按下F12键时,例子DDEx1将在退出程序前处理WM_DESTROY消息.这个消息将调用ReleaseAllObjects函数,这个函数包括了多个Release调用.像下面一样:
static void
ReleaseAllObjects(void)
{
if (g_pDD != NULL)
{
if (g_pDDSPrimary != NULL)
{
g_pDDSPrimary->Release();
g_pDDSPrimary = NULL;
}
g_pDD->Release();
g_pDD = NULL;
}
}
这个程序检测DirectDraw对象的指针g_pDD和DirectDraw平面对象的指针g_pDDSPrimary是否为NULL.然后,调用 IDirectDrawSurface7::Release方法令DirectDraw平面对象的reference count(可以认为是创建的对象的数目)数减1,这使得reference count减为0,DirectDraw平面对象就释放了.然后,还需将他的指针值设为NULL.接下来,程序调用 IDirectDraw7::Release,同样是令DirectDraw的reference count减1,然后然后....全销毁.
http://www.vczx.com/article/show.php?id=502