关于DirectDraw

本文主要是在网上收集到的一些关于DirectDraw的文章!

一下为相关文章和链接(为什么给了链接还要给文章?因为怕给的链接失效,这样我们就找不到那些好的文章了,在这也谢谢那些提供文章的人!!!)


文章地址:http://dev.gameres.com/Program/Visual/2D/fromfirst.htm

从头学习DirectDraw


 在开始学习DirectDraw编程之前,有一些题外话要说明,以下内容均是个人的心得和体会,如果其中有什么谬误之处,敬请谅解,同时个人不对可能造成的后果负责。。

  以下几点是在编制DirectX应用程序时应该注意的:

  • 尽管使用VB或DELPHI都可以制作DirectX应用程序,但考虑到代码的效率,还是应使用C或C++。其中,C++是面向对象的编程语言,可以使你的程序更易于维护,如果再考虑到代码的兼容性,我推荐使用Microsoft Visual C++ 5.0集成开发环境。当然,使用Borland C++ 5.0也是一个不错的选择。
  • 你应该已经有编制Windows应用程序的经验,以及C++的基础。
  • 熟悉你将要使用的编程工具的运用。例如,如何为集成开发环境设置各种参数,以及编译程序和连接程序的命令行参数。
  • 确认你已下载了微软的DirectX 5.0 SDK中的相关文件,特别是头文件和库文件,这些都是编程所必需的。
  • 确认你已经安装了DirectX 5.0或更高版本的运行时刻库,因为本教程的所有程序均使用了DirectX的新接口(Interface),必须要5.0或更高版本支持。

DirectDraw是什么?

  DirectDraw是DirectX中的关于视频输入输出的基本部分,使用DirectDraw可以方便地编制出高效的视频处理程序,只要用户的硬件支持DirectDraw,就能保证你的代码可以处理它们。
  简单地说,一个DOS程序员可以方便地直接访问视频显存,从而高效地处理视频动画,而在Windows的32位环境中,使用DirectDraw可以做类似的工作(而且做得更好)。例如,在DOS环境中的320x200x256色图形模式中,你可以通过在地址A000:0000开始处的一片内存区进行直接读写的方式来处理视频操作,而在Windows环境中,DirectDraw也提供一片内存区供你直接读写,使你能更好地完成相似的操作。
  当然,DirectDraw提供的远不止这些,但一个游戏程序员可能更关心如何直接访问视频显存,以及如何高效地完成位块拷贝操作等等。


让我们从消息循环开始

  DirectX最初是为游戏开发而推出的,编制游戏的程序员都很贪婪,他们会尽量榨取系统资源,并试图让自己的程序永远具有最高的效率。但Windows是一个多任务的操作系统,当它发现所有的程序都处于空闲时,便会减少给这些程序的资源,其中之一就是开始清理交换文件,为了让自己的程序给Windows以始终繁忙的假象,不妨用一些新的代码来代替常规的方法。

这是常规的消息循环处理

while(GetMessage(&msg,NULL,NULL,NULL)){

    TranslateMessage(&msg);

    DispatchMessage(&msg);

}

return msg.wParam;



//---------------



这是改进的消息循环处理

for(;;){

    if(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE)){

        if(msg.message==WM_QUIT) break;

        TranslateMessage(&msg);

        DispatchMessage(&msg)

    }else{

        if(AppPaused) WaitMessage();

    else{

        // 这里进行任何不基于消息循环的处理

        // 例如动画制作

        }

    }

}

return msg.wParam;

  上例中,新的消息循环处理方式使得Windows认为程序始终繁忙,从而提高了程序的性能。注意,其中的AppPaused变量为真时表示程序未在前台运行,该变量的取值为WM_ACTIVATEAPP消息的wParam参数。


第一个DirectDraw程序

  现在咱们开始编制第一个DirectDraw的应用程序,来示例如何建立一个全屏幕独占方式的应用程序,并利用HDC来实现简单的文本输出。
  这个示例与微软的示例程序不同,它使用了DirectX 5.0中的新接口,在制作最终可执行程序时应确保为LINK程序指定ddraw.lib和dxguid.lib这两个文件。
  示例程序的可执行程序由VC 5.0生成,只在基于Intel奔腾芯片、Windows95环境的机器上才能运行。

  下面重点介绍一下建立和销毁DirectDraw基本对象的方法,以及如何请求新接口。
首先来说明一下以后要用到的术语:

  • DirectDraw基本对象。
    这个对象的实体由DirectX建立,程序只能通过指向该对象实体的指针来访问它,这一点与Delphi十分相似,在Delphi中,一个TLabel类型的变量实际只是一个指针,它指向一个TLabel类的对象。建立DirectDraw基本对象的函数是DirectDrawCreate(),该函数可在内部建立一个DirectDraw对象实体,并将指向该实体的指针返回给应用程序,以后的所有操作都基于这个指针。
  • 主平面。
    可以将主平面简单地理解为“视频显存区”,任何直接对主平面的访问都立刻反映到屏幕上。主平面也是一个对象(程序员只能使用指向该对象的指针),该对象封装了几乎所有的输入输出操作,包括直接访问、利用设备相关(DC)访问以及位块拷贝(Blt)操作等等。主平面可以附带一个或多个“后台缓冲区”。
  • 后台缓冲区。
    后台缓冲区同样也是一个对象,也同样只提供一个指向该对象的指针供程序员使用。后台缓冲区与主平面的操作方式几乎一样,不同的是所有对后台缓冲区的操作不直接反映在屏幕上,例如,你可以在后台缓冲区中放置好图片,然后通过位块拷贝操作(或“弹出”操作)将后台缓冲区中的内容快速映射到主平面(即屏幕),这种操作方式可以避免出现不愉快的闪烁现象。
  • 位块拷贝。
    位块拷贝是将一块矩形区域从一个平面拷贝到另一个平面,可以进行缩放、旋转、镜像、透明等附加动作。DirectDraw提供的位块拷贝方法效率十分高,程序员勿需自行编制代码来完成相似工作。
  • “弹出”操作。
    Flip这个单词有反转、弹出等含义,在DirectDraw中,可以将其理解为迅速互换两个平面,这种互换操作并不是内容的互换,而只是指针的互换,因此其速度比位块拷贝快得多。例如,在全屏幕独占方式下,可以利用“弹出”操作快速地将后台缓冲区与主平面互换(指针的互换),从而获得高速的视频动画效果。只有具有可“弹出”属性的平面才能使用“弹出”操作。
  • 释放。
    在DirectX中,各个对象均有Release方法,这个方法(成员函数)并不见得就是销毁对象,它只是对内部计数器进行递减操作。例如,用DirectDrawCreate()函数建立了DirectDraw基本对象后,该对象的内部计数器就置为值1,如果此时再调用该对象的Release方法,就会使内部计数器减去1,若结果为零则该对象被销毁,但若在调用Release方法之前又为该对象请求新的接口(如IDirectDraw2),则内部计数器又会加一,需要两次Release方法才能完全销毁该对象。

  在示例程序一中,从WinMain函数开始,在登记了窗口类并建立了程序主窗口后,就开始建立DirectDraw的基本对象了:

ddrval = DirectDrawCreate(NULL, &_lpDD, NULL);

if (ddrval!=DD_OK){

    // 失败

}

  函数DirectDrawCreate在成功时返回DD_OK(零),若是失败则返回一个32位(HRESULT)的错误代码。不止是这个函数,所有DirectX对象的成员函数(方法)均采用这种方式来返回表示成功或失败的值。
  不必去管该函数的第一个和第三个参数,现在只需要把它们设为空值即可,而第二个参数是一个指针,这个指针指向了一个LPDIRECTDRAW类型的变量,而该变量实际上也是一个指针,如果函数成功则指向IDirectDraw对象实体--------是不是有一点绕来绕去的?
  当基本对象成功建立后,就需要将它“变”成较新的对象以增进性能,这也正是本示例程序为什么需要5.0版本的DirectX来支持的缘故。

ddrval=_lpDD->QueryInterface(IID_IDirectDraw2, (void **)&lpDD);

_lpDD->Release();

if (ddrval!=DD_OK){

    // 失败

}

  下一步是利用新建立的基本对象来建立与该基本对象相关联的主平面,这个主平面实际上就是可视的屏幕,所有对主平面的访问都直接反映在屏幕。在建立主平面之前我们首先将基本对象的属性设为全屏幕独占方式,这样才能改变显示模式,以及建立可“弹出”的平面集。具体的设置方法参见源程序。
  在设置了全屏幕独占方式并设置了适当的显示模式之后,程序建立了一个主平面,这个主平面附带一个后台缓冲区,程序员可以使用“设备相关把柄(HDC)”或直接访问的方式(以后再讲)来对后台缓冲区进行图形操作,所有对后台缓冲区的操作并不能反映在屏幕上,在图形操作完成后,可以利用“弹出”操作来将后台缓冲区“弹”至主平面,从而在屏幕上显示出来。
  本示例程序在进行图形操作示例时,首先获取后台缓冲区的HDC,然后用标准的GDI函数来绘制文本,一旦绘制完毕,就立刻释放刚获取的HDC,因为任何一个平面的HDC被获取后,该平面就被隐式地锁定,无法进行位块拷贝或弹出操作,只有当该HDC被释放后,一个隐式的Unlock才被调用,程序才能对该平面进行前述操作。
  完成后台缓冲区的图形绘制后,程序调用主平面的成员函数(方法)Flip来将后台缓冲区弹至主平面,你将看到屏幕内容已被改变。

  这是我个人搞的一个小封装,可用于非C++的编程语言(如Delphi)。使用此封装库可比较容易地编制DirectDraw应用程序。此封装为静态链接库,提供Visual C++和BorlandC++两种版本,其中包括:
    Visual C++版库文件
    Borland C++版库文件
    相关的头文件
    两个示例源程序
    说明文档(HLP文档格式)






文章地址:http://blog.csdn.net/xqhrs232/article/details/7991481
DirectDraw简单用法

原文地址::http://www.webgou.info/content/programming/363/?jdfwkey=9sycs3

 

 GDI:win32 gdi 与 DirectDraw 操作DIB的,DirectDraw 直接操作内存。

//ce 6下面测试:
IDirectDraw *pDraw;
IDirectDrawSurface *pDFace;
IDirectDrawSurface *pLayer;
HRESULT hr;
DDSURFACEDESC  ddsd;

  RECT rect;
  dlgMain.GetWindowRect(&rect);
  DirectDrawCreate(NULL, &pDraw, NULL);
  pDraw->SetCooperativeLevel(dlgMain,DDSCL_NORMAL);
  ddsd.dwSize = sizeof(ddsd);
  ddsd.dwFlags = DDSD_CAPS;
  ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
  hr = pDraw->CreateSurface(&ddsd, &pDFace,NULL);

  DDCAPS ddcaps;
  memset(&ddcaps, 0, sizeof(ddcaps));
  hr = pDraw->GetCaps(&ddcaps,NULL);
  

  memset(&ddsd, 0, sizeof(ddsd));
  ddsd.dwSize = sizeof(ddsd);
  ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
  ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH |DDSD_PIXELFORMAT;
  ddsd.dwWidth = 800;
  ddsd.dwHeight = 480;
  ddsd.ddpfPixelFormat = ddpfOverlayFormats;

  hr = pDraw->CreateSurface(&ddsd, &pLayer,NULL);

  hr = pLayer->IsLost();
 
  DDOVERLAYFX ofx;
  memset(&ofx, 0, sizeof(DDOVERLAYFX));
  ofx.dwSize = sizeof(DDOVERLAYFX);
  ofx.dckSrcColorkey.dwColorSpaceLowValue = 0x0000F81F;
  ofx.dckSrcColorkey.dwColorSpaceHighValue = 0x0000;
  ofx.dwAlphaConstBitDepth = 8;
  ofx.dwAlphaConst = 0x50;
  RECT rt1= {0,0,400,480};
  hr = pLayer->UpdateOverlay(&rt1, pDFace, &rt1,DDOVER_SHOW|DDOVER_KEYSRCOVERRIDE, &ofx);

  HDC hdc;
  hr = pLayer->GetDC(&hdc);
  FillRect(hdc,&rt1, (HBRUSH)GetStockObject(BLACK_BRUSH));
  SetBkColor(hdc,TRANSPARENT);
  SetTextColor(hdc,0x0000F81F);
  DrawText(hdc,_T("pLayerpLayer pLayervpLayer "), -1,  &rt1, 1);
  hr = pLayer->ReleaseDC(hdc);
   pLayer->Release();
   pDFace->Release();
   pDraw->Release();


1.链接库

动态装载
typedef HRESULT (* DIRECTDRAWCREATE)( GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter );
m_hModule=::LoadLibrary(_T("ddraw.dll"));
m_pDirectDrawCreate= (DIRECTDRAWCREATE)::GetProcAddress(m_hModule,L"DirectDrawCreate");

静态链接
#pragma comment(lib, "ddraw.lib") 调用DirectDrawCreate

2.GUID的使用

直接定义
const GUID g_IID_IDirectDraw ={0x9c59509a,0x39bd,0x11d1,
0x8c,0x4a,0x00,0xc0,0x4f,0xd9,0x30,0xc5};

链接#pragma comment(lib, "dxguid.lib")
QueryInterface IID_IDirectDraw4

3.创建主页面

// Create the main DirectDraw object
LPDIRECTDRAW g_pDD;
hRet = DirectDrawCreate(NULL, &g_pDD, NULL);
hRet = g_pDD->QueryInterface(g_IID_IDirectDraw, (LPVOID *)&g_pDD);
// Get normal mode
hRet = g_pDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);

memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
// creates a DirectDrawSurface object for this DirectDraw object.
hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);
创建主页面无须指定像素格式和页面大小

4.页面的像素格式

//YUV4:1:1
DDPIXELFORMAT pixel_format=
{sizeof(DDPIXELFORMAT), DDPF_FOURCC, MAKEFOURCC('Y','V','1','2'),0,0,0,0,0};

//RGB565
DDPIXELFORMAT ddpfOverlayFormats =
{sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0xF800, 0x07e0, 0x001F, 0};

5.创建OverLay页面

memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_FLIP | DDSCAPS_VIDEOMEMORY;
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_BACKBUFFERCOUNT | DDSD_PIXELFORMAT;
ddsd.dwWidth = DDRAW_X;
ddsd.dwHeight = DDRAW_Y;
ddsd.dwBackBufferCount = 1;//有个后台页面
ddsd.ddpfPixelFormat = pixel_format;

hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSOverlay, NULL);
创建OverLay页面应该指定页面的大小(可以全屏),像素格式。

6.OverLay页面的显示与隐藏
void ShowOverLay()
{
DDOVERLAYFX ovfx = {0};
ovfx.dwSize = sizeof(DDOVERLAYFX);
ovfx.dckSrcColorkey.dwColorSpaceLowValue = COLOR_KEY;//指定页面的透明色
ovfx.dckSrcColorkey.dwColorSpaceHighValue = COLOR_KEY;
ovfx.dwAlphaConstBitDepth = 8;//指明alpha位宽度
ovfx.dwAlphaConst = 0x50;//alpha透明值 指明页面的透明度.16级.

RECT rs;
rs.left = 0;
rs.top = 0;
rs.right = DDRAW_X;
rs.bottom = DDRAW_Y;

DWORD dwUpdateFlags = DDOVER_KEYSRCOVERRIDE | DDOVER_SHOW;
if(g_pDDSOverlay != NULL && g_pDDSOverlay->IsLost() == DD_OK)
{
HRESULT hret = g_pDDSOverlay->UpdateOverlay(&rs,g_pDDSPrimary,&rs,dwUpdateFlags,&ovfx);
//将主页面和OverLay页面关联起来
if(hret != DD_OK)
{
printf("Update OverLay errorrn");
if(hret == DDERR_SURFACELOST)
{
g_pDDSOverlay->Restore();
}
}
}
}

dwUpdateFlags = DDOVER_HIDE即隐藏页面。
UpdateOverlay这个接口一般是在页面初始化后,显示,隐藏这三种状况下调用,
每次在页面上绘图后不必要调用UpdateOverlay,否则就会造成画面显示卡动,不连续。

7.在页面上绘图

在页面上绘图有两种方法
一是获取页面DC后,以GDI方式绘图
if( g_pDDSOverlay->GetDC(&hDC) == DD_OK)
//进行DC绘图
g_pDDSOverlay->ReleaseDC(hDC);
第二种状况当知道页面的像素格式,Lock页面获取页面指针,直接操作页面存储区。
g_pDDSOverlay->Lock(NULL,&ddsd,0,NULL);
BYTE *pFramePhyPtr = (LPBYTE)ddsd.lpSurface;
int framesize = DDRAW_X * DDRAW_Y;
memcpy(pFramePhyPtr,src[0],framesize);
memcpy(pFramePhyPtr + framesize,src[2],framesize / 4);
memcpy(pFramePhyPtr + framesize + framesize / 4,src[1], framesize / 4);
g_pDDSOverlay->Unlock(NULL);//YUV4:1:1

8.遍历后台页面

EnumAttachedSurfaces
enumerates all the surfaces attached to a given primary surface.
可以先到后台页面上绘制,然后Flip操作.

9.注意要点
不要频繁调用UpdateOverLay。
注意页面的像素格式与图像帧的像素格式的匹配,
为了加快显示速度,最好与显示驱动所支持的像素格式相同。
可以用后台页面和Flip操作,加快页面显示。

具体操作见资源中心的示例。

 

//=====================================

备注:

1.

UpdateOverlay这个接口一般是在页面初始化后,显示,隐藏这三种状况下调用,
每次在页面上绘图后不必要调用UpdateOverlay,否则就会造成画面显示卡动,不连续

2.

不要频繁调用UpdateOverLay。
注意页面的像素格式与图像帧的像素格式的匹配,
为了加快显示速度,最好与显示驱动所支持的像素格式相同。
可以用后台页面和Flip操作,加快页面显示。



文章地址:http://www.cnblogs.com/Jade2009/archive/2009/05/12/1454689.html

DIRECTDRAW 1:创建一个简单的DIRECTDRAW程序


步骤 1: 创建一个 DirectDraw 对象
要创建一个 DirectDraw 对象的实例,你的应用程序要象 DDEx1 例程中的 doInit 函数那样先使用 DirectDrawCreate 函数. DirectDrawCreate 包含三个参数. 第一个参数获得了一个代表显示设备的全局唯一标识符(GUID). 这个 GUID 在大多数情况下被设为 NULL, 表示 DirectDraw 使用系统缺省的显示驱动. 第二个参数包含了 DirectDraw 创建以后的指针的地址. 第三个参数留作将来的扩展之用只能为 NULL.

下面的例子演示了如何创建一个 DirectDraw 对象以及确定是否创建成功:

ddrval = DirectDrawCreate(NULL, &lpDD, NULL);
if(ddrval == DD_OK)
{
    // lpDD 是一个可用的 DirectDraw 对象.
}
else
{
    // DirectDraw 对象不能被创建.
}
步骤 2: 确定应用程序的行为
在你能够改变显示率之前, 你必须至少为 IDirectDraw::SetCooperativeLevel 函数的 dwFlags 参数指定 DDSCL_EXCLUSIVE 和 DDSCL_FULLSCREEN 标志. 这使得你的应用程序能完全控制显示设备,而其它应用程序不能共享之.另外, DDSCL_FULLSCREEN 标志设置应用程序为独占(全屏)模式. 你的应用程序覆盖了整个桌面,而且只有你的应用程序能够写到屏幕上. 然而桌面仍然是可用的.(要看独占模式的应用程序下的桌面,执行 DDEx1 然后按 ALT+ TAB.)

以下的例子演示了如何使用 SetCooperativeLevel 函数:

HRESULT      ddrval;
LPDIRECTDRAW lpDD;     // 已用 DirectDrawCreate 创建
 
ddrval = lpDD->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE |
    DDSCL_FULLSCREEN);
if(ddrval == DD_OK)
{
    // 独占模式成功.
}
else
{
    // 独占模式不成功.
    // 应用程序仍可运行.
}
 
如果 SetCooperativeLevel 没有返回 DD_OK,你仍然可以运行你的程序.然而程序将不能取得独占模式,并且有可能不能实现你所需要的功能.在这种情况下,你也许应该显示信息让用户来决定退出还是继续.

如果你设置全屏,独占的控制等级,你必须把你的应用程序的窗口句柄传递给 SetCooperativeLevel 来允许 Windows 确定你的应用程序是否异常终止.例如,如果发生了一个一般保护(GP)错误而 GDI 换页到 后台缓存(back buffer),使用者将不能返回 Windows 屏幕.要防止这种现象的发生, DirectDraw 提供了一个在后台运行的进程来捕获发向 window 的消息. DirectDraw 使用这些消息来测定何时应用程序终止.然而这一特性强加了一些限制.你不得不指明为你的应用程序获取消息的窗口句柄,这意味着,如果你创建另一个窗口,你必须确定你指明了活动窗口.否则,你将会遇到问题,包括 GDI 的不可预知的行为,或你按了 ALT + TAB 而没有响应.

步骤 3:改变显示模式
在你设置了应用程序的行为之后,你就能使用 IDirectDraw::SetDisplayMode 函数来改变显示分辨率.以下的例子演示了如何把显示模式设为 640×480×8 bpp:

HRESULT      ddrval;
LPDIRECTDRAW lpDD;  // 已创建
 
ddrval = lpDD->SetDisplayMode(640, 480, 8);
if(ddrval == DD_OK)
{
    // 显示模式改变成功.
}
else
{
    // 显示模式不能被改变.
    // 这种模式不被支持或
    // 另一个程序取得了独占模式.
}
 
当你设置显示模式时,你应该确定如果用户的硬件不能支持更高的显示模式,你的应用程序将恢复到被大数显示适配器所支持的模式. 例如,你的可以把应用程序制作为把 640×480×8 作为一个后备显示模式来运行与所有支持它的系统上.

注意  如果显示适配器不能被设置到想要的分辨率 IDirectDraw::SetDisplayMode 将返回一个 DDERR_INVALIDMODE 错误值.应此,在设置显示模式前你应该使用 IDirectDraw::EnumDisplayModes 函数来确定用户的显示适配器的性能.

步骤 4:创建换页页面

在你设置了显示模式之后,你应该创建页面来安放你的应用程序.因为 DDEx1 例子使用了 IDirectDraw::SetCooperativeLevel 函数设置成独占(全屏)模式,你就能创建换页的页面.你如果使用 SetCooperativeLevel 把模式设置为 DDSCL_NORMAL, 你将只能创建位块传送的页面.创建换页页面(flipping surfaces)需要以下步骤:

定义页面参数
创建页面
定义页面参数
创建换页页面(flipping surfaces)的第一步是在一个 DDSURFACEDESC 结构中定义页面参数.以下的例子演示了结构的定义以及创建换页页面所需的标志.

// 创建带有一个后台缓存的主页面.
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
    DDSCAPS_FLIP | DDSCAPS_COMPLEX;
 
ddsd.dwBackBufferCount = 1;
 
在这个例子中, dwSize 成员被设为 DDSURFACEDESC 结构的大小.者可以预防任何 DirectDraw 函数返回不可用成员错误. (dwSize 成员是为 DDSURFACEDESC 结构将来的扩展提供的.)

dwFlags 成员确定了 DDSURFACEDESC 结构中的哪一个成员将被填充有效信息.对于 DDEx1 例子, dwFlags 被设置为指明你要使用 DDSCAPS 结构(DDSD_CAPS)以及你要创建一个后台缓存(back buffer)(DDSD_BACKBUFFERCOUNT).

在这个例子中 dwCaps 成员指出这些标志将在 DDSCAPS 结构中使用.在这种情况下,它指明了一个主页面(primary surface)(DDSCAPS_PRIMARYSURFACE),一个换页页面(flipping surface)(DDSCAPS_FLIP),和一个复杂页面(complex surface)(DDSCAPS_COMPLEX).

最后,这个例子指明了一个后台缓存.后台缓存是背景和精灵将被实际写入的地方.然后后台缓存被换页到主页面.在 DDEx1 例子中,后台缓存的个数被设为 1.然而,你可以创建显存所允许的个数的后台缓存.要得到关于创建一个或更多后台缓存的信息,请参阅三缓冲(Triple Buffering).

页面内存可以是显示内存或系统内存.如果显存不够 DirectDraw 使用系统内存(例如,如果你在只有 1M 显存的显示适配器上指明超过一个后台缓存(back buffer)).你也可以通过设置 DDSCAPS 结构的 dwCaps 成员为 DDSCAPS_SYSTEMMEMORY 或 DDSCAPS_VIDEOMEMORY 指明只用系统内存或显示内存.(如果你指明 DDSCAPS_VIDEOMEMORY,而创建页面的有用显存不够, IDirectDraw::CreateSurface 将返回一个 DDERR_OUTOFVIDEOMEMORY 错误.)

创建页面
在 DDSURFACEDESC 结构被填充后,你可以使用它和由 DirectDrawCreate 函数创建的指向 DirectDraw 对象的指针 lpDD,来调用 IDirectDraw::CreateSurface 函数,如下所示:

ddrval = lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);
if(ddrval == DD_OK)
{
    // lpDDSPrimary 指向新页面.
}
else
{
    // 页面未创建.
    return FALSE;
}
 
lpDDSPrimary 参数将指向如果 CreateSurface 调用成功返回的主页面(primary surface).

在指向主页面的指针可用后,你可以使用 IDirectDrawSurface3::GetAttachedSurface 函数来提取一个指向后台缓存(back buffer)的指针,如下所示:

ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
ddrval = lpDDSPrimary->GetAttachedSurface(&ddcaps, &lpDDSBack);
if(ddrval == DD_OK)
{
    // lpDDSBack 指向后台缓存.
}
else
{
    return FALSE;
}
 
通过提供主页面(primary surface)的地址以及使用 DDSCAPS_BACKBUFFER 标志设置特性值, 如果 IDirectDrawSurface3::GetAttachedSurface 调用成功 lpDDSBack 参数将指向后台缓存(back buffer).

步骤 5:着色到页面
在一个主页面(primary surface)和一个后台缓存(back buffer)被创建之后, 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);
}
 
这个例子使用了 IDirectDrawSurface3::GetDC 函数来获得设备描述句柄,同时它内在地琐定了页面.如果你不使用需要设备描述句柄的 Windows 函数, 你可以使用 IDirectDrawSurface3::Lock 和 IDirectDrawSurface3::Unlock 函数来锁定和解锁后台缓存(back buffer).

锁定页面内存(不论整个页面或一部分页面)使得你的应用程序和系统位块传送器不能同时获得对主页面的访问.这预防了由于你的应用程序正在写向页面内存而引发的错误.另外,在主页面被解锁前你的应用程序不能换页.

在页面解锁后,这个例子使用标准的 Windows GDI 函数: SetBkColor 来设置背景颜色, SetTextColor 来选择放在背景上的文本的颜色, 以及 TextOut 来把文本和背景颜色打印在页面上.

在文本被写到缓存后,这个例子使用 IDirectDrawSurface3::ReleaseDC 函数来解锁页面和释放句柄.一旦你的应用程序结束写向后台缓存(back buffer),你必须调用 IDirectDrawSurface3::ReleaseDC 或 IDirectDrawSurface3::Unlock,视你的程序而定.在页面被解锁前你的应用程序不能换页.

典型的,你写向后台缓存,然后换页到主页面(primary surface)以被显示.对于 DDEx1,在第一次换页前没有明显的延迟,所以 DDEx1 在初始函数中写向主缓存来预防显示页面前的延迟. 就如你在这个DIRECTDRAW 后面的步骤中将要看到的那样, DDEx1 只在 WM_TIMER 期间写向后台缓存.通常你只在初始函数或标题页中写向主页面.

注意  在通过使用 IDirectDrawSurface3::Unlock 解锁页面后,页面内存的指针是不可用的. 你必须重新使用 IDirectDrawSurface3::Lock 来获得一个可用的页面内存的指针.

步骤 6:写向页面
DDEx1 中 WM_TIMER 消息的前一半专门写向后台缓存(back buffer),如下所示:

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);
        }
 
调用 IDirectDrawSurface3::GetDC 函数的那行代码锁定后台缓存(back buffer)来预备写操作. SetBkColor 和 SetTextColor 函数设置背景和文本的颜色.

接着, phase 变量决定了应该写主缓存消息还是后台缓存消息. 如果 phase 等于 1,将写主页面(primary surface)消息,同时 phase 被设为 0.如果 phase 等于 0,将写后台缓存消息,同时 phase 被设为 1.注意,其实两种情况下消息都是写到后台缓存里的.

在消息被写到后台缓存后,后台缓存被使用 IDirectDrawSurface3::ReleaseDC 函数解锁.

步骤 7:换页
在页面内存解锁后,你就可以使用 IDirectDrawSurface3::Flip 函数来换页后台缓存(back buffer)到主页面(primary surface)了,如下所示:

while(1)
{
    HRESULT ddrval;
    ddrval = lpDDSPrimary->Flip(NULL, 0);
    if(ddrval == DD_OK)
    {
        break;
    }
    if(ddrval == DDERR_SURFACELOST)
    {
        ddrval = lpDDSPrimary->Restore();
        if(ddrval != DD_OK)
        {
            break;
        }
    }
    if(ddrval != DDERR_WASSTILLDRAWING)
    {
        break;
    }
}
 
在这个例子里, lpDDSPrimary 参数指明了主页面(primary surface) 和与之关联的后台缓存(back buffer). 当调用 IDirectDrawSurface3::Flip 后,前后页面被交换(只改变了页面的指针,数据并未实际移动). 如果换页成功返回 DD_OK,程序从 while 循环跳出.

如果换页返回 DDERR_SURFACELOST,可以尝试使用 IDirectDrawSurface3::Restore 函数来恢复页面.如果恢复成功,程序跳回 IDirectDrawSurface3::Flip 调用并再次尝试.如果恢复不成功,程序从循环跳出,并返回一个错误.

注意  当你调用 IDirectDrawSurface3::Flip, 换页并不立即完成,而是在系统发生下一次垂直空扫时.如果前一次换页还未发生, IDirectDrawSurface3::Flip 返回 DDERR_WASSTILLDRAWING.在这个例子里, IDirectDrawSurface3::Flip 持续调用直到返回 DD_OK.

步骤 8: 释放 DirectDraw 对象
当你按 F12 时, DDEx1 程序在退出前执行 WM_DESTROY 消息.这个消息调用 finiObjects 函数,它包含了所有的 IUnknown::Release 调用,如下所示:

static void finiObjects(void)
{
    if(lpDD != NULL)
    {
        if(lpDDSPrimary != NULL)
        {
            lpDDSPrimary->Release();
            lpDDSPrimary = NULL;
        }
        lpDD->Release();
        lpDD = NULL;
    }
} // finiObjects
 
程序检查 DirectDraw 对象(lpDD)和 DirectDrawSurface 对象(lpDDSPrimary)的指针是否不等于 NULL. 然后 DDEx1 调用 IDirectDrawSurface3::Release 函数把 DirectDrawSurface 对象的引用计数(reference count)减 1.因为这使得引用计数变为 0, DirectDrawSurface 对象被释放. DirectDrawSurface 指针通过值被设为 NULL 而被释放.然后,程序调用 IDirectDraw::Release 把 DirectDraw 对象的引用计数减为 0,释放 DirectDraw 对象.它的指针同样通过值被设为 NULL 而被释放.



文章地址:http://www.cppblog.com/tim/archive/2007/07/22/28574.aspx

DirectDraw编程方法与技巧
添加时间:2006-10-18   出处:www.3DGameDev.org  作者:songtao 
  
  
1 概 述

  DirectX是Microsoft为软件开发人员提供的一套精心设计的接口,用于开发高性能、实时的应用程序。它以COM(component object modal)为基本结构[1],位于硬件和软件之间,像gdi(graphics device interface)一样提供了硬件无关的API(application programming interface)接口;它和GDI有一重要不同点:DirectX是一套底层的API接口,它提供了直接访问硬件的能力,使得DirectX应用程序能充分发挥硬件的威力。
  DirectDraw是DirectX中提供直接操纵显存、执行硬件映射、硬件覆盖及切换显示页等功能的组件。它不但兼容已有的Windows应用程序和驱动程序,而且还兼容许多显示卡:从简单的SVGA到支持图像剪裁、拉伸和非RGB格式图像的高档显示卡。DirectDrawHAL(hardware-abstraction layer)抽象了显示卡的硬件功能,以设备无关的方式提供了一些以前是设备相关的功能,如多显示页技术、访问并控制显示卡映射寄存器、支持3DZ-Buffer、支持Z-order硬件覆盖、访问图像拉伸硬件,以及同时访问标准显存和控制显存。正因为DirectDraw有这些优点,现在许多基于Windows95/98/NT的游戏程序都使用了DirectDraw;而它的设备无关性使开发者摆脱了繁重的显示卡接口工作,集中精力实现程序的主要功能。DirectX中的DDraw.dll实现了DirectDraw所需要的函数、对象及其接口。下面先介绍DirectDraw中主要的函数、对象和接口,然后再说明DirectDraw的使用方法。

2 DirectDraw中的函数、对象和接口简介

  DirectDraw包括一个代表显示卡的主对象DirectDraw,由函数DirectDrawCreate创建,实现接口IDirectDraw和IDirectDraw2。如果机器上装有多个显示卡,可以为每个显示卡创建一个DirectDraw对象。也可以创建多个DirectDraw对象,它们各自独立并代表同一物理对象。IDirectDraw是为了和以前版本DirectX兼容而保留的接口,基于DirectX3以上版本的程序应该使用IDirectDraw2。IDirectDraw2和IDirectDraw接口所包含的方法定义基本相同,但具体实现不同。DirectDraw对象主要用于创建其它3个对象:DirectDrawSurface、DirectDrawPalette、DirectDrawClipper。
  DirectDrawSurface对象由函数IDirectDraw2::CreateSurface创建,代表显存中一块线性区域,实现接口IDirectDrawSurface和IDirectDrawSurface2。IDirectDrawSurface是为了和以前版本DirectX兼容而保留的接口,基于DirectX3以上版本的程序应该使用IDirectDrawSurface2。二者的接口定义也基本相同。可以为一个DirectDraw对象创建多个IDirectDrawSurface对象,代表物理屏幕或逻辑屏幕,通过IDirectDrawSurface2::Flip、IDirectDrawSurface2::BltFast等方法切换显示页或映射部分屏幕内容。
  DirectDrawPalette对象由函数IDirectDraw2::CreatePalette创建,实现接口IDirectDrawPalette。它代表显示卡的物理调色板,可以是16色或256色。每个DirectDrawPalette必须附着(attach)在一个DirectDrawSurface上,不同的DirectDrawSurface对象可以有不同的DirectDrawPalette。
  DirectDrawClipper对象由函数IDirectDraw2::CreateClipper或DirectDrawCreateClipp
er创建,实现接口IDirectDrawClipper。DirectDraw用它来处理屏幕的剪贴。它常用于在Window模式(与全屏模式相对应)下运行的DirectDraw程序,在使用前也必须被附着在一个DirectDrawSurface上。

3 DirectDraw程序一般工作过程

  和一般的Windows程序[2]一样,DirectDraw程序要先创建一个主窗口,然后进行DirectDraw的初始化:创建所需要的对象,设置程序的工作模式,建立必要的数据结构。在初始化工作完成后,就可以在主窗口的消息循环中根据用户的输入调用相应对象的方法。

3.1 初始化DirectDraw
3.1.1 创建DirectDraw对象
  首先调用DirectDrawCreate创建代表某个显示卡的DirectDraw对象:
  ddres=DirectDrawCreate(NULL,&lpDD,NULL);
  DirectDrawCreate的第一项参数是代表显示卡驱动程序的GUID(globally unique identifier),若为NULL则表示采用系统默认的驱动程序。第二项参数是IDirectDraw接口类型指针的地址,用于接收指向由DirectDrawCreate所创建对象的指针。这个指针不需应用程序预先分配内存。DirectDrawCreate成功时会调用AddRef将对象的引用计数加1。如果要知道系统中所有驱动程序的GUID,可以调用DirectDrawEnumerate,它接收两个参数:回调函数的地址和传给回调函数的自定义数据的地址,其工作方式和Win32 API Enum Windows类似。
3.1.2 设置DirectDraw的工作模式
  创建DirectDraw对象后应该马上设置DirectDraw对象的工作模式:
  ddres=lpDD→SetCooperativeLevel(hWnd,DDSCL_EXCLUSIVE|DDSCLFUL_LSCREEN);
  SetCooperativeLevel的第一个参数是和DirectDraw对象关联的窗口句柄,一般是程序主窗口的句柄;第二个参数指明了DirectDraw对象的工作模式。DirectDraw对象有两种工作模式:普通模式(Windowedmode,参数DDSCL_NORMAL)和独占模式(Full_Screen mode,参数DDSCL_EXCLUSIVE)。普通模式下DirectDraw和普通Windows程序的区别不大,主要是DirectDraw程序可以随心所欲地读取整个屏幕的内容或在屏幕的任意位置输出,而其它的Windows程序毫无察觉。独占模式就是游戏“红色警报”和“赤壁”所采用的方式,并必须和全屏模式(参数DDSCL_FULLSCREEN)联用,此时程序的主窗口被扩展为整个屏幕。其它应用程序都成为后台程序,使用Alt+Tab键可以在程序间切换。
3.1.3 得到IDirectDraw2类型的接口
  接下来利用COM的重要方法QueryInterface,通过IDirectDraw接口得到一个IDirectDraw2类型的接口。QueryInterface成功时会将对象的引用计数加1,而我们也不再需要IDirectDraw接口,因此这里调用IDirectDraw::Release将对象的引用计数减1,也即释放先前得到的IDirectDraw接口。
3.1.4 根据需要切换屏幕显示模式
  如果DirectDraw的工作模式设定为全屏独占模式,则可以根据需要切换屏幕显示模式:
  ddres=lpDD2→SetDisplayMode(800,600,16);
  SetDisplayMode的前两个参数是屏幕的横、纵分辨率,最后一个是每个像素点的颜色位数。上例将屏幕设为800×600,16位色。
3.1.5 创建DirectDrawSurface对象并得到IDirectDrawSurface2接口
  创建DirectDraw对象后,下一步调用IDirectDraw2::CreateSurface创建代表物理屏幕或逻辑屏幕的DirectDrawSurface对象。IDirectDraw2::CreateSurface的第一个参数是DDSURFACEDESC结构的地址,第二个参数是一个IDirectDrawSurface接口类型的指针地址,第三个参数必须是NULL。数据结构DDSURFACEDESC包含了创建DirectDrawSurface所需信息。在得到一个IDirectDrawSurface类型接口后,仍使用QueryInterface得到一个IDirectDrawSurface2类型的接口,并释放先前得到的IDirectDrawSurface接口:
  //Get the IDirectDrawSurface2interface.
  LPDIRECTDRAWSURFACE2lpDDSPrimary=NULL;
  ddres=lpDDSPrimaryTemp>QueryInterface(IID_IDirectDrawSurface2,(LPVOID)&lpDDSPrimary;
  lpDDSPrimaryTemp→Release();
  上面得到的lpDDSPrimary指向代表物理屏幕的对象。还需调用IDirectDrawSurface2::GetAttachedSurface得到指向创建时附带的代表相关逻辑屏幕的对象指针。至此,DirectDraw初始化工作完成。

3.2 使用DirectDraw
3.2.1 使用GDI函数向物理屏幕或逻辑屏幕输出
  调用IDirectDrawSurface2::GetDC可得代表某个屏幕的设备描述表的句柄,使用GDI函数输出,最后调用IDirectDrawSurface2::ReleaseDC释放句柄。为防止在对屏幕作图期间其它应用程序争夺显存,IDirectDrawSurface2::GetDC调用IDirectDrawSurface2::Lock得到Winl6 Lock。这意味着其它程序在该程序释放Win16Lock前都不能访问GDI和USER资源。IDirectDrawSurface2::ReleaseDC调用IDirectDrawSurface2::Unlock释放Win16 Lock。于是,在调用IDirectDrawSurface2::GetDC和IDirectDrawSurface2::ReleaseDC期间,Windows将被挂起。因此,应用程序应尽量缩短这一对函数调用之间的间隔时间,而且调试程序也无法跟踪这段时间内执行的操作。
3.2.2 交替切换物理屏幕和逻辑屏幕或执行屏幕内容的映射
  准备好内存中的逻辑屏幕后,可以调用IDirectDrawSurface2::Flip方法切换物理屏幕和逻辑屏幕,也可调用IDirectDrawSurface2::BltFast、IDirectDrawSurface2::Blt等方法执行部分屏幕内容的映射。一般情况下程序采用异步方式,在显示卡硬件执行切换动作的同时准备下一页屏幕,使CPU和显示卡硬件并行,提高整体执行速度。
  使用Flip切换物理屏幕和逻辑屏幕后,原指向物理屏幕的指针仍然指向物理屏幕,原指向逻辑屏幕的指针仍然指向逻辑屏幕,即指针所指内容也被交换了,便于程序操纵各个屏幕而不至于混淆。
3.2.3 释放DirectDraw对象
  程序结束之前要释放所创建的DirectDraw对象。这只要在相应接口上调用Release方法即可。

4 使用DirectDraw的技巧和注意事项

4.1 检查方法的返回值
  正确执行DirectDraw方法时都返回DDOK。且其值是零。返回其它值表明发生了某种错误。一般地,程序应检查这些返回值以决定是否出错。

4.2 检查Flip和Blt的状态
  如果在Flip或Blt操作的返回值是DDERR_WASSTILLDRAWING情况,为提高效率,DirectDraw提供了IDirectDrawSurface2::GetFlipStatus和IDirectDrawSurface2::GetBltStatus方法。它们能立即返回当前的Flip和Blt状态,于是应用程序可以在上一操作完成之前执行某些其他的任务。

4.3 在位图映射中使用ColorKey
  ColorKey是一种或几种颜色的集合,用于在位图映射操作中区分前景色和背景色。ColorKey包括两种:Source color key和Destination color key。前者是指源位图中代表透明色的颜色,在执行映射操作时将不被映射到目标位图上;后者是指目标位图中将被源位图中相应位置颜色取代的颜色,如果目标位图指定了Destination color key,则只有这些指定的颜色被替换。可以在创建DirectDrawSurface对象时指定Color Key;也可使用方法IDirectDrawSurface2::GetColorKey和IDirectDrawSurface2::SetColorKey以获取和设置已有的DirectDrawSurface对象的ColorKey。

4.4 GDI重定向
  由于GDI在Windows系统启动时先于DirectDraw被装入,而DirectDraw工作时又绕过了GDI,因此GDI不知道DirectDraw对物理屏幕所进行的操作。即使DirectDraw调用了Flip方法将先前的物理屏幕切换为逻辑屏幕,如不采取措施,GDI将仍然向切换后的逻辑屏幕上输出。如果DirectDraw程序拥有菜单、滚动条等由GDI负责绘制的元素,那么在Flip完成后这些元素就会成为不可见,而在对应的逻辑屏幕被切换成物理屏幕时又会显示出来。为避免屏幕闪烁,DirectDraw提供了IDirectDraw2::GetGDISurface,用于确定当前被GDI认为是物理屏幕的DirectDrawSurface对象;以及IDirectDraw2::FlipToGDISurface,用于将GDI的输出重新定向到当前的物理屏幕上。如果需要,可以在每次Flip操作后调用它,以保证屏幕正常。

4.5 在显存中存放位图
  由于从显存到显存的映射比从系统内存到显存的映射快,所以经常将需要映射的位图存放在显存中以提高速度。大多数显示卡在存放了物理屏幕和相关逻辑屏幕之外还有足够的内存可以用来存放位图。可以调用IDirectDraw2::GetCaps检查显存;在创建DirectDrawSurface对象时可以通过结构DDSURFACEDESC中的DDSCAPS域指定该对象存在于显存或系统内存中。如果指定在显存中创建对象,而显存又没有地方容纳该对象,IDirectDraw2::CreateSurface会返回错误信息DDERR_OUTOFVIDEOMEMORY。如果没有指定创建的位置,DirectDraw总是尽量利用空闲的显存。

4.6 检查硬件的性能
  虽然DirectDraw通过HAL和HEL屏蔽了硬件的具体特性,但应用程序也需根据硬件的不同性能来改变自身的执行方式。利用IDirectDraw2::GetCaps方法可以得到有关硬件性能的详细信息。

4.7 保持主窗口的消息循环畅通
  在调用SetCooperativeLevel设置DirectDraw的工作模式时,应用程序为DirectDraw指定了主窗口。由于DirectDraw直接操纵硬件可能导致死机,因此DirectDraw在后台监视主窗口的消息循环,当消息循环长时间没有反应时,DirectDraw就释放所有的资源,结束应用程序的执行。所以DirectDraw程s序应该注意避免长时间封锁消息循环。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值