DirectDraw的学习笔记(1)

开始看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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值